From 970972d242e26d0cdf635d3af646df1a519dc677 Mon Sep 17 00:00:00 2001 From: ThisIsPIRI <34787507+ThisIsPIRI@users.noreply.github.com> Date: Mon, 6 Aug 2018 14:41:37 +0900 Subject: [PATCH 001/622] Clarify what happens when a new value is input to some methods --- tensorflow/contrib/training/python/training/hparam.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tensorflow/contrib/training/python/training/hparam.py b/tensorflow/contrib/training/python/training/hparam.py index 3beb7bfe30..9f5059b4b1 100644 --- a/tensorflow/contrib/training/python/training/hparam.py +++ b/tensorflow/contrib/training/python/training/hparam.py @@ -494,6 +494,7 @@ class HParams(object): value: New value of the hyperparameter. Raises: + KeyError: If the hyperparameter doesn't exist. ValueError: If there is a type mismatch. """ param_type, is_list = self._hparam_types[name] @@ -510,7 +511,7 @@ class HParams(object): setattr(self, name, _cast_to_type_if_compatible(name, param_type, value)) def del_hparam(self, name): - """Removes the hyperparameter with key 'name'. + """Removes the hyperparameter with key 'name'. Does nothing if it isn't present. Args: name: Name of the hyperparameter. @@ -532,7 +533,7 @@ class HParams(object): The `HParams` instance. Raises: - ValueError: If `values` cannot be parsed. + ValueError: If `values` cannot be parsed or a hyperparameter in `values` doesn't exist. """ type_map = dict() for name, t in self._hparam_types.items(): @@ -543,7 +544,7 @@ class HParams(object): return self.override_from_dict(values_map) def override_from_dict(self, values_dict): - """Override hyperparameter values, parsing new values from a dictionary. + """Override existing hyperparameter values, parsing new values from a dictionary. Args: values_dict: Dictionary of name:value pairs. @@ -552,6 +553,7 @@ class HParams(object): The `HParams` instance. Raises: + KeyError: If a hyperparameter in `values_dict` doesn't exist. ValueError: If `values_dict` cannot be parsed. """ for name, value in values_dict.items(): @@ -591,7 +593,7 @@ class HParams(object): sort_keys=sort_keys) def parse_json(self, values_json): - """Override hyperparameter values, parsing new values from a json object. + """Override existing hyperparameter values, parsing new values from a json object. Args: values_json: String containing a json object of name:value pairs. @@ -600,6 +602,7 @@ class HParams(object): The `HParams` instance. Raises: + KeyError: If a hyperparameter in `values_json` doesn't exist. ValueError: If `values_json` cannot be parsed. """ values_map = json.loads(values_json) -- GitLab From 26368188c018cdcc1bbb80ce8205fb04305816c2 Mon Sep 17 00:00:00 2001 From: ThisIsPIRI <34787507+ThisIsPIRI@users.noreply.github.com> Date: Mon, 6 Aug 2018 14:56:38 +0900 Subject: [PATCH 002/622] Add 'existing' to parse's docstring --- tensorflow/contrib/training/python/training/hparam.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/contrib/training/python/training/hparam.py b/tensorflow/contrib/training/python/training/hparam.py index 9f5059b4b1..372630df81 100644 --- a/tensorflow/contrib/training/python/training/hparam.py +++ b/tensorflow/contrib/training/python/training/hparam.py @@ -521,7 +521,7 @@ class HParams(object): del self._hparam_types[name] def parse(self, values): - """Override hyperparameter values, parsing new values from a string. + """Override existing hyperparameter values, parsing new values from a string. See parse_values for more detail on the allowed format for values. -- GitLab From bd6c11f878e1820417d1ceff1b02222178f60c16 Mon Sep 17 00:00:00 2001 From: Guozhong Zhuang Date: Fri, 12 Oct 2018 10:10:21 -0700 Subject: [PATCH 003/622] Clean out MKL_ML code from batchnorm ops --- .../core/kernels/mkl_fused_batch_norm_op.cc | 658 +----------------- 1 file changed, 2 insertions(+), 656 deletions(-) diff --git a/tensorflow/core/kernels/mkl_fused_batch_norm_op.cc b/tensorflow/core/kernels/mkl_fused_batch_norm_op.cc index 2ec6c8fa89..4b8c066902 100644 --- a/tensorflow/core/kernels/mkl_fused_batch_norm_op.cc +++ b/tensorflow/core/kernels/mkl_fused_batch_norm_op.cc @@ -20,671 +20,19 @@ limitations under the License. #include "tensorflow/core/framework/tensor.h" #include "tensorflow/core/framework/tensor_types.h" #include "tensorflow/core/util/tensor_format.h" - -#ifndef INTEL_MKL_ML_ONLY +#include "tensorflow/core/util/mkl_util.h" #include "mkldnn.hpp" + using mkldnn::batch_normalization_backward; using mkldnn::batch_normalization_forward; using mkldnn::prop_kind; using mkldnn::stream; using mkldnn::use_global_stats; using mkldnn::use_scale_shift; -#else -#include "mkl_dnn.h" -#include "mkl_dnn_types.h" -#endif - -#include "tensorflow/core/util/mkl_util.h" -// TODO(inteltf) Address comments from PR 8968. namespace tensorflow { using CPUDevice = Eigen::ThreadPoolDevice; -#ifdef INTEL_MKL_ML_ONLY - -template -class MklFusedBatchNormOp : public OpKernel { - public: - explicit MklFusedBatchNormOp(OpKernelConstruction* context) - : OpKernel(context) { - float epsilon; - OP_REQUIRES_OK(context, context->GetAttr("epsilon", &epsilon)); - epsilon_ = T(epsilon); - string tensor_format; - OP_REQUIRES_OK(context, context->GetAttr("data_format", &tensor_format)); - OP_REQUIRES(context, FormatFromString(tensor_format, &tensor_format_), - errors::InvalidArgument("Invalid data format")); - OP_REQUIRES_OK(context, context->GetAttr("is_training", &is_training_)); - } - - void Compute(OpKernelContext* context) override { - MklFusedBatchNormOpContext mkl_context; - const Tensor& input = MklGetInput(context, 0); - const Tensor& scale = MklGetInput(context, 1); - const Tensor& shift = MklGetInput(context, 2); - const Tensor& est_mean = MklGetInput(context, 3); - const Tensor& est_variance = MklGetInput(context, 4); - - GetMklShape(context, 0, &(mkl_context.mkl_shape_input_shape)); - bool input_in_mkl_format = mkl_context.mkl_shape_input_shape.IsMklTensor(); - - if (!input_in_mkl_format) { - OP_REQUIRES(context, input.dims() == 4, - errors::InvalidArgument("input must be 4-dimensional", - input.shape().DebugString())); - } - OP_REQUIRES(context, scale.dims() == 1, - errors::InvalidArgument("scale must be 1-dimensional", - scale.shape().DebugString())); - OP_REQUIRES(context, shift.dims() == 1, - errors::InvalidArgument("offset must be 1-dimensional", - shift.shape().DebugString())); - OP_REQUIRES(context, est_mean.dims() == 1, - errors::InvalidArgument("estimated_mean must be 1-dimensional", - est_mean.shape().DebugString())); - - OP_REQUIRES( - context, est_variance.dims() == 1, - errors::InvalidArgument("estimated_variance must be 1-dimensional", - est_variance.shape().DebugString())); - - if (is_training_) { - OP_REQUIRES(context, est_mean.dim_size(0) == 0, - errors::InvalidArgument("estimated_mean empty for training", - est_mean.shape().DebugString())); - OP_REQUIRES(context, est_variance.dim_size(0) == 0, - errors::InvalidArgument( - "estimated_variance must be empty for training", - est_variance.shape().DebugString())); - } - - unsigned int flag_batch_norm = - is_training_ ? dnnUseScaleShift - : (dnnUseInputMeanVariance | dnnUseScaleShift); - - mkl_context.MklExtractParams(context, tensor_format_); - - // Create layout only for input data as it is used in Op primitive. - mkl_context.MklCreateInputLayout(context); - - // Create Op primitive. - CHECK_EQ(dnnBatchNormalizationCreateForward_v2_F32( - &(mkl_context.mkl_prim_batchnorm), nullptr, - mkl_context.mkl_lt_input, static_cast(epsilon_), - flag_batch_norm), - E_SUCCESS); - - // Temporary tensors with buffers for the context inputs, if - // conversion to MKL-Op specific layouts are required. It is assumed here - // that TF's 1D tensors (scale, shift, est_mean, and est_variance) won't - // require any conversion. - // Since scale-shift is combined in MKL, a buffer is required. - Tensor mkl_tmp_input_buf_tensor, mkl_tmp_scale_shift_buf_tensor; - mkl_context.MklPrepareContextInputs(context, &mkl_tmp_input_buf_tensor, - &mkl_tmp_scale_shift_buf_tensor); - - // Output data in MKL layout - Tensor* output = nullptr; - TensorShape tf_shape_output; - MklShape mkl_shape_output; - mkl_shape_output.SetMklTensor(true); - mkl_shape_output.SetMklLayout(mkl_context.mkl_prim_batchnorm, - dnnResourceDst); - mkl_shape_output.SetTfLayout(mkl_context.mkl_params.in_dim, - mkl_context.mkl_params.in_sizes, - mkl_context.mkl_params.in_strides); - mkl_shape_output.SetTfDimOrder(mkl_context.mkl_params.in_dim, - tensor_format_); - tf_shape_output.AddDim(dnnLayoutGetMemorySize_F32(static_cast( - mkl_shape_output.GetMklLayout())) / - sizeof(T)); - AllocateOutputSetMklShape(context, 0, &output, tf_shape_output, - mkl_shape_output); - mkl_context.mkl_res_batchnorm[dnnResourceDst] = - static_cast(output->flat().data()); - - // Batch mean in TF layout - Tensor* batch_mean = nullptr; - MklShape mkl_shape_batch_mean; - mkl_shape_batch_mean.SetMklTensor(false); - AllocateOutputSetMklShape(context, 1, &batch_mean, scale.shape(), - mkl_shape_batch_mean); - // Batch variance in TF layout - Tensor* batch_variance = nullptr; - MklShape mkl_shape_batch_variance; - mkl_shape_batch_variance.SetMklTensor(false); - AllocateOutputSetMklShape(context, 2, &batch_variance, scale.shape(), - mkl_shape_batch_variance); - // If training mode, set dnnResourceMean and dnnResourceVariance to - // output tensors for batch mean and variance. - // Otherwise, set dnnResourceMean and dnnResourceVariance to - // estimated mean and variance. - if (is_training_) - mkl_context.MklSetMeanVariance(*batch_mean, *batch_variance); - else - mkl_context.MklSetMeanVariance(est_mean, est_variance); - - // Now that all resources are set, it is ready for dnnExecute - CHECK_EQ(dnnExecute_F32(mkl_context.mkl_prim_batchnorm, - mkl_context.mkl_res_batchnorm), - E_SUCCESS); - - // Mean and variance (without Bessel's correction) saved for backward - // computation to serve as pre-computed mean and variance. - Tensor* saved_mean = nullptr; - MklShape mkl_shape_saved_mean; - mkl_shape_saved_mean.SetMklTensor(false); - AllocateOutputSetMklShape(context, 3, &saved_mean, scale.shape(), - mkl_shape_saved_mean); - std::memcpy( - reinterpret_cast(saved_mean->flat().data()), - reinterpret_cast(mkl_context.mkl_res_batchnorm[dnnResourceMean]), - scale.NumElements() * sizeof(float)); - Tensor* saved_variance = nullptr; - MklShape mkl_shape_saved_variance; - mkl_shape_saved_variance.SetMklTensor(false); - AllocateOutputSetMklShape(context, 4, &saved_variance, scale.shape(), - mkl_shape_saved_variance); - std::memcpy(reinterpret_cast(saved_variance->flat().data()), - reinterpret_cast( - mkl_context.mkl_res_batchnorm[dnnResourceVariance]), - scale.NumElements() * sizeof(float)); - - // Bessel's correction on variance, if training mode is on - if (is_training_) { - float* p_var = static_cast(batch_variance->flat().data()); - auto depth = mkl_context.mkl_params.depth; - size_t orig_size = mkl_context.mkl_params.in_sizes[0] * - mkl_context.mkl_params.in_sizes[1] * - mkl_context.mkl_params.in_sizes[3]; - size_t adjust_size = orig_size - 1; - float adjust_factor = (static_cast(orig_size)) / adjust_size; - for (int i = 0; i < depth; i++) p_var[i] = adjust_factor * p_var[i]; - } - - mkl_context.MklCleanup(); - } - - private: - T epsilon_; - TensorFormat tensor_format_; - bool is_training_; - - // Structure containing all info for MklOp - typedef struct { - // Parameters used for input and output layouts - struct MklBatchNormParams { - // BatchNormOp src and - size_t in_dim; - size_t in_sizes[4]; - size_t in_strides[4]; - size_t depth; // Batch normalization is done for per channel. - } mkl_params; - - MklShape mkl_shape_input_shape; - - // MKL primitive and resources for BatchNormOp - dnnPrimitive_t mkl_prim_batchnorm = nullptr; - void* mkl_res_batchnorm[dnnResourceNumber]; - - // MKL layouts for inputs in the context - dnnLayout_t mkl_lt_input = nullptr; - - void MklCleanup() { - bool input_in_mkl_format = mkl_shape_input_shape.IsMklTensor(); - if (!input_in_mkl_format) dnnLayoutDelete_F32(mkl_lt_input); - if (mkl_prim_batchnorm != nullptr) dnnDelete_F32(mkl_prim_batchnorm); - } - - void MklExtractParams(OpKernelContext* context, - const TensorFormat& tensor_format) { - const Tensor& input = MklGetInput(context, 0); - bool input_in_mkl_format = mkl_shape_input_shape.IsMklTensor(); - mkl_params.in_dim = input_in_mkl_format - ? mkl_shape_input_shape.GetDimension() - : input.dims(); - mkl_params.in_sizes[0] = static_cast( - input_in_mkl_format ? mkl_shape_input_shape.GetSizes()[0] - : GetTensorDim(input, tensor_format, 'W')); - mkl_params.in_sizes[1] = static_cast( - input_in_mkl_format ? mkl_shape_input_shape.GetSizes()[1] - : GetTensorDim(input, tensor_format, 'H')); - mkl_params.in_sizes[2] = static_cast( - input_in_mkl_format ? mkl_shape_input_shape.GetSizes()[2] - : GetTensorDim(input, tensor_format, 'C')); - mkl_params.in_sizes[3] = static_cast( - input_in_mkl_format ? mkl_shape_input_shape.GetSizes()[3] - : GetTensorDim(input, tensor_format, 'N')); - mkl_params.depth = mkl_params.in_sizes[2]; - GetStridesFromSizes(tensor_format, mkl_params.in_strides, - mkl_params.in_sizes); - } - - void MklCreateInputLayout(OpKernelContext* context) { - const Tensor& input = MklGetInput(context, 0); - bool input_in_mkl_format = mkl_shape_input_shape.IsMklTensor(); - if (input_in_mkl_format) { - mkl_lt_input = - static_cast(mkl_shape_input_shape.GetCurLayout()); - } else { - CHECK_EQ( - dnnLayoutCreate_F32(&mkl_lt_input, mkl_params.in_dim, - mkl_params.in_sizes, mkl_params.in_strides), - E_SUCCESS); - } - } - void MklPrepareContextInputs(OpKernelContext* context, - Tensor* mkl_tmp_input_buf_tensor, - Tensor* mkl_tmp_scale_shift_buf_tensor) { - bool mkl_convert_input; - dnnPrimitive_t mkl_prim_convert_input = nullptr; - dnnLayout_t mkl_lt_internal_input = nullptr; - void* mkl_buf_converted_input = nullptr; - // Compare with internal layouts and convert if needed - const Tensor& input = MklGetInput(context, 0); - void* mkl_buf_input = - const_cast(static_cast(input.flat().data())); - CHECK_EQ(dnnLayoutCreateFromPrimitive_F32( - &mkl_lt_internal_input, mkl_prim_batchnorm, dnnResourceSrc), - E_SUCCESS); - mkl_convert_input = - !dnnLayoutCompare_F32(mkl_lt_internal_input, mkl_lt_input); - if (mkl_convert_input) { - CHECK_EQ(dnnConversionCreate_F32(&mkl_prim_convert_input, mkl_lt_input, - mkl_lt_internal_input), - E_SUCCESS); - AllocTmpBuffer(context, mkl_tmp_input_buf_tensor, mkl_lt_internal_input, - &mkl_buf_converted_input); - CHECK_EQ(dnnConversionExecute_F32(mkl_prim_convert_input, mkl_buf_input, - mkl_buf_converted_input), - E_SUCCESS); - dnnDelete_F32(mkl_prim_convert_input); - } - dnnLayoutDelete_F32(mkl_lt_internal_input); - mkl_res_batchnorm[dnnResourceSrc] = - (mkl_convert_input) ? mkl_buf_converted_input : mkl_buf_input; - - // scale-shift layout is created from primitive. So no conversion - // is needed, however, a buffer has to be allocated. - dnnLayout_t mkl_lt_scale_shift = nullptr; - void* mkl_buf_scale_shift = nullptr; - CHECK_EQ( - dnnLayoutCreateFromPrimitive_F32( - &mkl_lt_scale_shift, mkl_prim_batchnorm, dnnResourceScaleShift), - E_SUCCESS); - AllocTmpBuffer(context, mkl_tmp_scale_shift_buf_tensor, - mkl_lt_scale_shift, &mkl_buf_scale_shift); - // Fill the scale-shift buffer with data, presumably buffer is 2D array - const Tensor& scale = MklGetInput(context, 1); - const Tensor& shift = MklGetInput(context, 2); - float* buf_scale_shift = static_cast(mkl_buf_scale_shift); - float* buf_scale = const_cast( - static_cast(scale.flat().data())); - float* buf_shift = const_cast( - static_cast(shift.flat().data())); - auto depth = mkl_params.depth; - for (int i = 0; i < depth; i++) { - buf_scale_shift[i] = buf_scale[i]; - buf_scale_shift[i + depth] = buf_shift[i]; - } - mkl_res_batchnorm[dnnResourceScaleShift] = mkl_buf_scale_shift; - } - - inline void MklSetMeanVariance(const Tensor& mean, const Tensor& variance) { - mkl_res_batchnorm[dnnResourceMean] = const_cast( - static_cast(mean.flat().data())); - mkl_res_batchnorm[dnnResourceVariance] = const_cast( - static_cast(variance.flat().data())); - } - } MklFusedBatchNormOpContext; -}; - -template -class MklFusedBatchNormGradOp : public OpKernel { - public: - explicit MklFusedBatchNormGradOp(OpKernelConstruction* context) - : OpKernel(context) { - float epsilon; - OP_REQUIRES_OK(context, context->GetAttr("epsilon", &epsilon)); - epsilon_ = T(epsilon); - string tensor_format; - OP_REQUIRES_OK(context, context->GetAttr("data_format", &tensor_format)); - OP_REQUIRES(context, FormatFromString(tensor_format, &tensor_format_), - errors::InvalidArgument("Invalid data format")); - } - - void Compute(OpKernelContext* context) override { - MklFusedBatchNormGradOpContext mkl_context; - - const Tensor& out_backprop = MklGetInput(context, 0); - const Tensor& input = MklGetInput(context, 1); - const Tensor& scale = MklGetInput(context, 2); - const Tensor& saved_mean = MklGetInput(context, 3); - const Tensor& saved_var = MklGetInput(context, 4); - - // Here scale, mean, and variance are 1D and considered - // those having same layout in MKL and TF - GetMklShape(context, 0, &(mkl_context.mkl_shape_out_backprop)); - GetMklShape(context, 1, &(mkl_context.mkl_shape_input_shape)); - - bool input_in_mkl_format = mkl_context.mkl_shape_input_shape.IsMklTensor(); - bool out_backprop_in_mkl_format = - mkl_context.mkl_shape_out_backprop.IsMklTensor(); - if (!out_backprop_in_mkl_format) { - OP_REQUIRES(context, out_backprop.dims() == 4, - errors::InvalidArgument("input must be 4-dimensional", - out_backprop.shape().DebugString())); - } - if (!input_in_mkl_format) { - OP_REQUIRES(context, input.dims() == 4, - errors::InvalidArgument("input must be 4-dimensional", - input.shape().DebugString())); - } - OP_REQUIRES(context, scale.dims() == 1, - errors::InvalidArgument("scale must be 1-dimensional", - scale.shape().DebugString())); - OP_REQUIRES(context, saved_mean.dims() == 1, - errors::InvalidArgument("saved mean must be 1-dimensional", - saved_mean.shape().DebugString())); - OP_REQUIRES(context, saved_var.dims() == 1, - errors::InvalidArgument("saved variance must be 1-dimensional", - saved_var.shape().DebugString())); - - mkl_context.MklExtractParams(context, tensor_format_); - - mkl_context.MklCreateInputLayout(context); - - unsigned int flag_batch_norm_grad = dnnUseScaleShift; - - // Create Backward Op primitive. - CHECK_EQ(dnnBatchNormalizationCreateBackward_v2_F32( - &(mkl_context.mkl_prim_batchnorm_bwd), nullptr, - mkl_context.mkl_lt_input, static_cast(epsilon_), - flag_batch_norm_grad), - E_SUCCESS); - - // Temporary tensors and their buffers if conversion is required - Tensor mkl_tmp_input_buf_tensor, mkl_tmp_outbackprop_buf_tensor, - mkl_tmp_scaleshift_buf_tensor; - mkl_context.MklPrepareContextInputs(context, &mkl_tmp_input_buf_tensor, - &mkl_tmp_outbackprop_buf_tensor, - &mkl_tmp_scaleshift_buf_tensor); - - // Allocate tensor for grad w.r.t. input(x) - Tensor* in_backprop = nullptr; - TensorShape tf_shape_in_backprop; - MklShape mkl_shape_in_backprop; - mkl_shape_in_backprop.SetMklTensor(true); - mkl_shape_in_backprop.SetMklLayout(mkl_context.mkl_prim_batchnorm_bwd, - dnnResourceDiffSrc); - mkl_shape_in_backprop.SetTfLayout(mkl_context.mkl_params.in_dims, - mkl_context.mkl_params.in_sizes, - mkl_context.mkl_params.in_strides); - mkl_shape_in_backprop.SetTfDimOrder(mkl_context.mkl_params.in_dims, - tensor_format_); - tf_shape_in_backprop.AddDim( - dnnLayoutGetMemorySize_F32( - static_cast(mkl_shape_in_backprop.GetMklLayout())) / - sizeof(T)); - AllocateOutputSetMklShape(context, 0, &in_backprop, tf_shape_in_backprop, - mkl_shape_in_backprop); - mkl_context.mkl_res_batchnorm_bwd[dnnResourceDiffSrc] = - static_cast(in_backprop->flat().data()); - - // grad_scale and grad_shift are combined together in MKL - // So create a single temporary buffer for those. - // Also set dnnResourceDiffScaleShift to the temporary buffer - Tensor mkl_tmp_grad_scale_shift_buf_tensor; - mkl_context.MklPrepareGradScaleShift(context, - &mkl_tmp_grad_scale_shift_buf_tensor); - - // All dnn resources are set now, ready to execute - CHECK_EQ(dnnExecute_F32(mkl_context.mkl_prim_batchnorm_bwd, - mkl_context.mkl_res_batchnorm_bwd), - E_SUCCESS); - - // Now separate out scale and shift grad and copy to individual tensors - const TensorShape& tf_shape_scale_shift = scale.shape(); - // Allocate tensor for grad w.r.t. scale (beta) - Tensor* scale_backprop = nullptr; - MklShape mkl_shape_scale_backprop; - AllocateOutputSetMklShape(context, 1, &scale_backprop, tf_shape_scale_shift, - mkl_shape_scale_backprop); - - // Allocate tensor for grad w.r.t. shift(gamma) - Tensor* shift_backprop = nullptr; - MklShape mkl_shape_shift_backprop; - AllocateOutputSetMklShape(context, 2, &shift_backprop, tf_shape_scale_shift, - mkl_shape_shift_backprop); - - // copy scale and shift grads to tensors - float* mkl_buf_scale_shift = const_cast(static_cast( - mkl_tmp_grad_scale_shift_buf_tensor.flat().data())); - float* tf_buf_scale = const_cast( - static_cast(scale_backprop->flat().data())); - float* tf_buf_shift = const_cast( - static_cast(shift_backprop->flat().data())); - auto depth = mkl_context.mkl_params.depth; - for (int i = 0; i < depth; i++) { - tf_buf_scale[i] = mkl_buf_scale_shift[i]; - tf_buf_shift[i] = mkl_buf_scale_shift[i + depth]; - } - - // Two placeholders for estimated_mean and estimated_variance, which are - // used for inference and thus not needed here for gradient computation. - Tensor* placeholder_1 = nullptr; - MklShape mkl_shape_placeholder_1; - AllocateOutputSetMklShape(context, 3, &placeholder_1, TensorShape({}), - mkl_shape_placeholder_1); - Tensor* placeholder_2 = nullptr; - MklShape mkl_shape_placeholder_2; - AllocateOutputSetMklShape(context, 4, &placeholder_2, TensorShape({}), - mkl_shape_placeholder_2); - - mkl_context.MklCleanup(); - } - - private: - T epsilon_; - TensorFormat tensor_format_; - - // Structure containing all info for MklOp - typedef struct { - // Parameters used for input and output layouts - struct MklBatchNormParams { - // BatchNormOp src and - size_t in_dims; - size_t in_sizes[4]; - size_t in_strides[4]; - size_t depth; // Batch normalization is done for per channel. - } mkl_params; - - MklShape mkl_shape_out_backprop; - MklShape mkl_shape_input_shape; - - // MKL primitive and resources for BatchNormOp - dnnPrimitive_t mkl_prim_batchnorm_bwd = nullptr; - void* mkl_res_batchnorm_bwd[dnnResourceNumber]; - - // MKL layouts for inputs in the context - dnnLayout_t mkl_lt_out_backprop = nullptr; - dnnLayout_t mkl_lt_input = nullptr; - - void MklCleanup() { - bool input_in_mkl_format = mkl_shape_input_shape.IsMklTensor(); - bool out_backprop_in_mkl_format = mkl_shape_out_backprop.IsMklTensor(); - if (!input_in_mkl_format) dnnLayoutDelete_F32(mkl_lt_input); - if (!out_backprop_in_mkl_format) dnnLayoutDelete_F32(mkl_lt_out_backprop); - - dnnDelete_F32(mkl_prim_batchnorm_bwd); - } - - void MklExtractParams(OpKernelContext* context, - const TensorFormat& tensor_format) { - const Tensor& input = MklGetInput(context, 1); - bool input_in_mkl_format = mkl_shape_input_shape.IsMklTensor(); - mkl_params.in_dims = input_in_mkl_format - ? mkl_shape_input_shape.GetDimension() - : input.dims(); - mkl_params.in_sizes[0] = static_cast( - input_in_mkl_format ? mkl_shape_input_shape.GetSizes()[0] - : GetTensorDim(input, tensor_format, 'W')); - mkl_params.in_sizes[1] = static_cast( - input_in_mkl_format ? mkl_shape_input_shape.GetSizes()[1] - : GetTensorDim(input, tensor_format, 'H')); - mkl_params.in_sizes[2] = static_cast( - input_in_mkl_format ? mkl_shape_input_shape.GetSizes()[2] - : GetTensorDim(input, tensor_format, 'C')); - mkl_params.in_sizes[3] = static_cast( - input_in_mkl_format ? mkl_shape_input_shape.GetSizes()[3] - : GetTensorDim(input, tensor_format, 'N')); - mkl_params.depth = mkl_params.in_sizes[2]; - GetStridesFromSizes(tensor_format, mkl_params.in_strides, - mkl_params.in_sizes); - } - - void MklCreateInputLayout(OpKernelContext* context) { - const Tensor& input = MklGetInput(context, 0); - bool input_in_mkl_format = mkl_shape_input_shape.IsMklTensor(); - if (input_in_mkl_format) { - mkl_lt_input = - static_cast(mkl_shape_input_shape.GetCurLayout()); - } else { - CHECK_EQ( - dnnLayoutCreate_F32(&mkl_lt_input, mkl_params.in_dims, - mkl_params.in_sizes, mkl_params.in_strides), - E_SUCCESS); - } - - bool out_backprop_in_mkl_format = mkl_shape_out_backprop.IsMklTensor(); - if (out_backprop_in_mkl_format) { - mkl_lt_out_backprop = - static_cast(mkl_shape_out_backprop.GetCurLayout()); - } else { - CHECK_EQ( - dnnLayoutCreate_F32(&mkl_lt_out_backprop, mkl_params.in_dims, - mkl_params.in_sizes, mkl_params.in_strides), - E_SUCCESS); - } - } - - void MklPrepareContextInputs(OpKernelContext* context, - Tensor* mkl_tmp_input_buf_tensor, - Tensor* mkl_tmp_outbackprop_buf_tensor, - Tensor* mkl_tmp_scaleshift_buf_tensor) { - bool mkl_convert_input; - dnnPrimitive_t mkl_prim_convert_input = nullptr; - dnnLayout_t mkl_lt_internal_input = nullptr; - void* mkl_buf_converted_input = nullptr; - // Compare with internal layouts and convert if needed - const Tensor& input = MklGetInput(context, 1); - void* mkl_buf_input = - const_cast(static_cast(input.flat().data())); - CHECK_EQ( - dnnLayoutCreateFromPrimitive_F32( - &mkl_lt_internal_input, mkl_prim_batchnorm_bwd, dnnResourceSrc), - E_SUCCESS); - mkl_convert_input = - !dnnLayoutCompare_F32(mkl_lt_internal_input, mkl_lt_input); - if (mkl_convert_input) { - CHECK_EQ(dnnConversionCreate_F32(&mkl_prim_convert_input, mkl_lt_input, - mkl_lt_internal_input), - E_SUCCESS); - AllocTmpBuffer(context, mkl_tmp_input_buf_tensor, mkl_lt_internal_input, - &mkl_buf_converted_input); - CHECK_EQ(dnnConversionExecute_F32(mkl_prim_convert_input, mkl_buf_input, - mkl_buf_converted_input), - E_SUCCESS); - dnnDelete_F32(mkl_prim_convert_input); - } - dnnLayoutDelete_F32(mkl_lt_internal_input); - mkl_res_batchnorm_bwd[dnnResourceSrc] = - (mkl_convert_input) ? mkl_buf_converted_input : mkl_buf_input; - - bool mkl_convert_out_backprop; - dnnPrimitive_t mkl_prim_convert_out_backprop = nullptr; - dnnLayout_t mkl_lt_internal_out_backprop = nullptr; - void* mkl_buf_converted_out_backprop = nullptr; - // Compare with internal layouts and convert if needed - const Tensor& out_backprop = MklGetInput(context, 0); - void* mkl_buf_out_backprop = const_cast( - static_cast(out_backprop.flat().data())); - CHECK_EQ(dnnLayoutCreateFromPrimitive_F32(&mkl_lt_internal_out_backprop, - mkl_prim_batchnorm_bwd, - dnnResourceDiffDst), - E_SUCCESS); - mkl_convert_out_backprop = !dnnLayoutCompare_F32( - mkl_lt_internal_out_backprop, mkl_lt_out_backprop); - if (mkl_convert_out_backprop) { - CHECK_EQ(dnnConversionCreate_F32(&mkl_prim_convert_out_backprop, - mkl_lt_out_backprop, - mkl_lt_internal_out_backprop), - E_SUCCESS); - AllocTmpBuffer(context, mkl_tmp_outbackprop_buf_tensor, - mkl_lt_internal_out_backprop, - &mkl_buf_converted_out_backprop); - CHECK_EQ(dnnConversionExecute_F32(mkl_prim_convert_out_backprop, - mkl_buf_out_backprop, - mkl_buf_converted_out_backprop), - E_SUCCESS); - dnnDelete_F32(mkl_prim_convert_out_backprop); - } - dnnLayoutDelete_F32(mkl_lt_internal_out_backprop); - mkl_res_batchnorm_bwd[dnnResourceDiffDst] = - (mkl_convert_out_backprop) ? mkl_buf_converted_out_backprop - : mkl_buf_out_backprop; - - // Set dnnResourceMean and dnnResourceVariance - const Tensor& saved_mean = MklGetInput(context, 3); - const Tensor& saved_var = MklGetInput(context, 4); - void* mkl_buf_saved_mean = const_cast( - static_cast(saved_mean.flat().data())); - void* mkl_buf_saved_var = const_cast( - static_cast(saved_var.flat().data())); - mkl_res_batchnorm_bwd[dnnResourceMean] = mkl_buf_saved_mean; - mkl_res_batchnorm_bwd[dnnResourceVariance] = mkl_buf_saved_var; - - // Set dnnResourceScaleShift - // Note backward Op needs only current values of scale parameters, - // shift parameters could be garbage and won't be used - const Tensor& scale = MklGetInput(context, 2); - dnnLayout_t mkl_lt_scale_shift = nullptr; - void* mkl_buf_scale_shift = nullptr; - CHECK_EQ(dnnLayoutCreateFromPrimitive_F32(&mkl_lt_scale_shift, - mkl_prim_batchnorm_bwd, - dnnResourceScaleShift), - E_SUCCESS); - AllocTmpBuffer(context, mkl_tmp_scaleshift_buf_tensor, mkl_lt_scale_shift, - &mkl_buf_scale_shift); - float* pscale = - const_cast(static_cast(scale.flat().data())); - float* pscale_shift = static_cast(mkl_buf_scale_shift); - auto depth = mkl_params.depth; - for (int i = 0; i < depth; i++) pscale_shift[i] = pscale[i]; - mkl_res_batchnorm_bwd[dnnResourceScaleShift] = mkl_buf_scale_shift; - dnnLayoutDelete_F32(mkl_lt_scale_shift); - } - - void MklPrepareGradScaleShift(OpKernelContext* context, - Tensor* mkl_tmp_grad_scale_shift_buf_tensor) { - dnnLayout_t mkl_lt_grad_scaleshift = nullptr; - void* mkl_buf_grad_scaleshift = nullptr; - CHECK_EQ(dnnLayoutCreateFromPrimitive_F32(&mkl_lt_grad_scaleshift, - mkl_prim_batchnorm_bwd, - dnnResourceDiffScaleShift), - E_SUCCESS); - AllocTmpBuffer(context, mkl_tmp_grad_scale_shift_buf_tensor, - mkl_lt_grad_scaleshift, &mkl_buf_grad_scaleshift); - mkl_res_batchnorm_bwd[dnnResourceDiffScaleShift] = - mkl_buf_grad_scaleshift; - dnnLayoutDelete_F32(mkl_lt_grad_scaleshift); - } - } MklFusedBatchNormGradOpContext; -}; -#endif - -#ifndef INTEL_MKL_ML_ONLY - struct MklBatchNormFwdParams { memory::dims src_dims; int depth; @@ -1765,8 +1113,6 @@ class MklFusedBatchNormGradOp : public OpKernel { memory::dims GetMeanVarianceDims() { return memory::dims({1, depth_}); } }; -#endif - #define REGISTER_MKL_CPU(T) \ REGISTER_KERNEL_BUILDER(Name("_MklFusedBatchNorm") \ .Device(DEVICE_CPU) \ -- GitLab From cb9dccae23f34edb15cdbe58ad6fceab702138fb Mon Sep 17 00:00:00 2001 From: Guozhong Zhuang Date: Fri, 12 Oct 2018 10:41:32 -0700 Subject: [PATCH 004/622] adjust headers inclusion order per review suggestion of another PR --- tensorflow/core/kernels/mkl_fused_batch_norm_op.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/core/kernels/mkl_fused_batch_norm_op.cc b/tensorflow/core/kernels/mkl_fused_batch_norm_op.cc index 4b8c066902..ff46e75a36 100644 --- a/tensorflow/core/kernels/mkl_fused_batch_norm_op.cc +++ b/tensorflow/core/kernels/mkl_fused_batch_norm_op.cc @@ -19,8 +19,8 @@ limitations under the License. #include "tensorflow/core/framework/register_types.h" #include "tensorflow/core/framework/tensor.h" #include "tensorflow/core/framework/tensor_types.h" -#include "tensorflow/core/util/tensor_format.h" #include "tensorflow/core/util/mkl_util.h" +#include "tensorflow/core/util/tensor_format.h" #include "mkldnn.hpp" using mkldnn::batch_normalization_backward; -- GitLab From bae74d26f93872374b48c60a73d189df148a6f99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yan=20Facai=20=28=E9=A2=9C=E5=8F=91=E6=89=8D=29?= Date: Tue, 16 Oct 2018 09:45:45 +0800 Subject: [PATCH 005/622] CLN: remove reshape in conv3d, becasue bias_add has supported 5-dim --- .../python/keras/layers/convolutional.py | 33 ++----------------- .../python/keras/layers/convolutional_test.py | 16 +++++++++ 2 files changed, 19 insertions(+), 30 deletions(-) diff --git a/tensorflow/python/keras/layers/convolutional.py b/tensorflow/python/keras/layers/convolutional.py index 58024677ee..f8afa0d430 100644 --- a/tensorflow/python/keras/layers/convolutional.py +++ b/tensorflow/python/keras/layers/convolutional.py @@ -199,21 +199,8 @@ class Conv(Layer): # nn.bias_add does not accept a 1D input tensor. bias = array_ops.reshape(self.bias, (1, self.filters, 1)) outputs += bias - if self.rank == 2: + else: outputs = nn.bias_add(outputs, self.bias, data_format='NCHW') - if self.rank == 3: - # As of Mar 2017, direct addition is significantly slower than - # bias_add when computing gradients. To use bias_add, we collapse Z - # and Y into a single dimension to obtain a 4D input tensor. - outputs_shape = outputs.shape.as_list() - if outputs_shape[0] is None: - outputs_shape[0] = -1 - outputs_4d = array_ops.reshape(outputs, - [outputs_shape[0], outputs_shape[1], - outputs_shape[2] * outputs_shape[3], - outputs_shape[4]]) - outputs_4d = nn.bias_add(outputs_4d, self.bias, data_format='NCHW') - outputs = array_ops.reshape(outputs_4d, outputs_shape) else: outputs = nn.bias_add(outputs, self.bias, data_format='NHWC') @@ -1127,24 +1114,10 @@ class Conv3DTranspose(Conv3D): outputs.set_shape(out_shape) if self.use_bias: - outputs_shape = outputs.shape.as_list() - if outputs_shape[0] is None: - outputs_shape[0] = -1 - if self.data_format == 'channels_first': - outputs_4d = array_ops.reshape(outputs, [ - outputs_shape[0], outputs_shape[1], - outputs_shape[2] * outputs_shape[3], outputs_shape[4] - ]) - else: - outputs_4d = array_ops.reshape(outputs, [ - outputs_shape[0], outputs_shape[1] * outputs_shape[2], - outputs_shape[3], outputs_shape[4] - ]) - outputs_4d = nn.bias_add( - outputs_4d, + outputs = nn.bias_add( + outputs, self.bias, data_format=conv_utils.convert_data_format(self.data_format, ndim=4)) - outputs = array_ops.reshape(outputs_4d, outputs_shape) if self.activation is not None: return self.activation(outputs) diff --git a/tensorflow/python/keras/layers/convolutional_test.py b/tensorflow/python/keras/layers/convolutional_test.py index 4afddbc8cc..63fb60ebaf 100644 --- a/tensorflow/python/keras/layers/convolutional_test.py +++ b/tensorflow/python/keras/layers/convolutional_test.py @@ -336,6 +336,14 @@ class Conv3DTransposeTest(test.TestCase): self.assertEqual(layer.kernel.constraint, k_constraint) self.assertEqual(layer.bias.constraint, b_constraint) + def test_conv3dtranspose_dynamic_shape(self): + with self.session(use_gpu=True): + # Won't raise error here. + layer = keras.layers.Conv3DTranspose(3, 3, data_format='channels_last') + layer.build((None, None, None, None, 1)) + layer1 = keras.layers.Conv3DTranspose(3, 3, data_format='channels_first') + layer1.build((None, 1, None, None, None)) + class SeparableConv1DTest(test.TestCase): @@ -557,6 +565,14 @@ class Conv3DTest(test.TestCase): self.assertEqual(layer.kernel.constraint, k_constraint) self.assertEqual(layer.bias.constraint, b_constraint) + def test_conv3d_dynamic_shape(self): + with self.session(use_gpu=True): + # Won't raise error here. + layer = keras.layers.Conv3D(3, 3, data_format='channels_last') + layer.build((None, None, None, None, 1)) + layer1 = keras.layers.Conv3D(3, 3, data_format='channels_first') + layer1.build((None, 1, None, None, None)) + class ZeroPaddingTest(test.TestCase): -- GitLab From 65344d0a7ebd21c71b3f3ed7cb091541504b659a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yan=20Facai=20=28=E9=A2=9C=E5=8F=91=E6=89=8D=29?= Date: Thu, 1 Nov 2018 09:36:09 +0800 Subject: [PATCH 006/622] TST: use testing_utils.layer_test instead --- .../python/keras/layers/convolutional_test.py | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/tensorflow/python/keras/layers/convolutional_test.py b/tensorflow/python/keras/layers/convolutional_test.py index 63fb60ebaf..7d8051e596 100644 --- a/tensorflow/python/keras/layers/convolutional_test.py +++ b/tensorflow/python/keras/layers/convolutional_test.py @@ -339,10 +339,16 @@ class Conv3DTransposeTest(test.TestCase): def test_conv3dtranspose_dynamic_shape(self): with self.session(use_gpu=True): # Won't raise error here. - layer = keras.layers.Conv3DTranspose(3, 3, data_format='channels_last') - layer.build((None, None, None, None, 1)) - layer1 = keras.layers.Conv3DTranspose(3, 3, data_format='channels_first') - layer1.build((None, 1, None, None, None)) + testing_utils.layer_test( + keras.layers.Conv3DTranspose, + kwargs={'data_format': 'channels_first', + 'filters': 3, 'kernel_size': 3}, + input_shape=(None, 1, None, None, None)) + testing_utils.layer_test( + keras.layers.Conv3DTranspose, + kwargs={'data_format': 'channels_last', + 'filters': 3, 'kernel_size': 3}, + input_shape=(None, None, None, None, 1)) class SeparableConv1DTest(test.TestCase): @@ -568,10 +574,16 @@ class Conv3DTest(test.TestCase): def test_conv3d_dynamic_shape(self): with self.session(use_gpu=True): # Won't raise error here. - layer = keras.layers.Conv3D(3, 3, data_format='channels_last') - layer.build((None, None, None, None, 1)) - layer1 = keras.layers.Conv3D(3, 3, data_format='channels_first') - layer1.build((None, 1, None, None, None)) + testing_utils.layer_test( + keras.layers.Conv3D, + kwargs={'data_format': 'channels_first', + 'filters': 3, 'kernel_size': 3}, + input_shape=(None, 1, None, None, None)) + testing_utils.layer_test( + keras.layers.Conv3D, + kwargs={'data_format': 'channels_last', + 'filters': 3, 'kernel_size': 3}, + input_shape=(None, None, None, None, 1)) class ZeroPaddingTest(test.TestCase): -- GitLab From 35f20ca9e794b455777a52bf70b5f7d79ae60455 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yan=20Facai=20=28=E9=A2=9C=E5=8F=91=E6=89=8D=29?= Date: Thu, 1 Nov 2018 10:11:14 +0800 Subject: [PATCH 007/622] TST: add input_data --- .../python/keras/layers/convolutional_test.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tensorflow/python/keras/layers/convolutional_test.py b/tensorflow/python/keras/layers/convolutional_test.py index 7d8051e596..737e12a2bd 100644 --- a/tensorflow/python/keras/layers/convolutional_test.py +++ b/tensorflow/python/keras/layers/convolutional_test.py @@ -337,18 +337,21 @@ class Conv3DTransposeTest(test.TestCase): self.assertEqual(layer.bias.constraint, b_constraint) def test_conv3dtranspose_dynamic_shape(self): + input_data = np.random.random((1, 3, 3, 3, 3)) with self.session(use_gpu=True): # Won't raise error here. testing_utils.layer_test( keras.layers.Conv3DTranspose, kwargs={'data_format': 'channels_first', 'filters': 3, 'kernel_size': 3}, - input_shape=(None, 1, None, None, None)) + input_shape=(None, 3, None, None, None), + input_data=input_data) testing_utils.layer_test( keras.layers.Conv3DTranspose, kwargs={'data_format': 'channels_last', 'filters': 3, 'kernel_size': 3}, - input_shape=(None, None, None, None, 1)) + input_shape=(None, None, None, None, 3), + input_data=input_data) class SeparableConv1DTest(test.TestCase): @@ -572,18 +575,21 @@ class Conv3DTest(test.TestCase): self.assertEqual(layer.bias.constraint, b_constraint) def test_conv3d_dynamic_shape(self): + input_data = np.random.random((1, 3, 3, 3, 3)) with self.session(use_gpu=True): # Won't raise error here. testing_utils.layer_test( keras.layers.Conv3D, kwargs={'data_format': 'channels_first', 'filters': 3, 'kernel_size': 3}, - input_shape=(None, 1, None, None, None)) + input_shape=(None, 3, None, None, None), + input_data=input_data) testing_utils.layer_test( keras.layers.Conv3D, kwargs={'data_format': 'channels_last', 'filters': 3, 'kernel_size': 3}, - input_shape=(None, None, None, None, 1)) + input_shape=(None, None, None, None, 3), + input_data=input_data) class ZeroPaddingTest(test.TestCase): -- GitLab From 8de3a6ee6c7b498e32e8c22c6631a3c0a7a4af86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yan=20Facai=20=28=E9=A2=9C=E5=8F=91=E6=89=8D=29?= Date: Thu, 1 Nov 2018 10:35:48 +0800 Subject: [PATCH 008/622] TST: data_format=first only run for gpu --- .../python/keras/layers/convolutional_test.py | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/tensorflow/python/keras/layers/convolutional_test.py b/tensorflow/python/keras/layers/convolutional_test.py index 737e12a2bd..ecee080937 100644 --- a/tensorflow/python/keras/layers/convolutional_test.py +++ b/tensorflow/python/keras/layers/convolutional_test.py @@ -337,21 +337,22 @@ class Conv3DTransposeTest(test.TestCase): self.assertEqual(layer.bias.constraint, b_constraint) def test_conv3dtranspose_dynamic_shape(self): - input_data = np.random.random((1, 3, 3, 3, 3)) + input_data = np.random.random((1, 3, 3, 3, 3)).astype(np.float32) with self.session(use_gpu=True): # Won't raise error here. - testing_utils.layer_test( - keras.layers.Conv3DTranspose, - kwargs={'data_format': 'channels_first', - 'filters': 3, 'kernel_size': 3}, - input_shape=(None, 3, None, None, None), - input_data=input_data) testing_utils.layer_test( keras.layers.Conv3DTranspose, kwargs={'data_format': 'channels_last', 'filters': 3, 'kernel_size': 3}, input_shape=(None, None, None, None, 3), input_data=input_data) + if test.is_gpu_available(cuda_only=True): + testing_utils.layer_test( + keras.layers.Conv3DTranspose, + kwargs={'data_format': 'channels_first', + 'filters': 3, 'kernel_size': 3}, + input_shape=(None, 3, None, None, None), + input_data=input_data) class SeparableConv1DTest(test.TestCase): @@ -575,21 +576,22 @@ class Conv3DTest(test.TestCase): self.assertEqual(layer.bias.constraint, b_constraint) def test_conv3d_dynamic_shape(self): - input_data = np.random.random((1, 3, 3, 3, 3)) + input_data = np.random.random((1, 3, 3, 3, 3)).astype(np.float32) with self.session(use_gpu=True): # Won't raise error here. - testing_utils.layer_test( - keras.layers.Conv3D, - kwargs={'data_format': 'channels_first', - 'filters': 3, 'kernel_size': 3}, - input_shape=(None, 3, None, None, None), - input_data=input_data) testing_utils.layer_test( keras.layers.Conv3D, kwargs={'data_format': 'channels_last', 'filters': 3, 'kernel_size': 3}, input_shape=(None, None, None, None, 3), input_data=input_data) + if test.is_gpu_available(cuda_only=True): + testing_utils.layer_test( + keras.layers.Conv3D, + kwargs={'data_format': 'channels_first', + 'filters': 3, 'kernel_size': 3}, + input_shape=(None, 3, None, None, None), + input_data=input_data) class ZeroPaddingTest(test.TestCase): -- GitLab From 470d0b4bac05151a5761fd3351deba3bcad10994 Mon Sep 17 00:00:00 2001 From: Steve Nesae Date: Sun, 4 Nov 2018 10:58:02 -0600 Subject: [PATCH 009/622] Added CMSIS preprocessor files --- .../micro/examples/micro_speech/CMSIS/hann.h | 1 + .../micro_speech/CMSIS/no_30ms_sample_data.cc | 66 ++++++++++++++++ .../micro_speech/CMSIS/no_30ms_sample_data.h | 32 ++++++++ .../CMSIS/no_power_spectrum_data.cc | 23 ++++++ .../CMSIS/no_power_spectrum_data.h | 29 +++++++ .../micro_speech/CMSIS/preprocessor.cc | 76 +++++++++++++++++++ .../micro_speech/CMSIS/preprocessor.h | 26 +++++++ .../micro_speech/CMSIS/preprocessor_test.cc | 63 +++++++++++++++ .../CMSIS/yes_30ms_sample_data.cc | 70 +++++++++++++++++ .../micro_speech/CMSIS/yes_30ms_sample_data.h | 32 ++++++++ .../CMSIS/yes_power_spectrum_data.cc | 23 ++++++ .../CMSIS/yes_power_spectrum_data.h | 29 +++++++ 12 files changed, 470 insertions(+) create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hann.h create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.cc create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.h create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.cc create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.h create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.cc create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.h create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor_test.cc create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.cc create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.h create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.cc create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.h diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hann.h b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hann.h new file mode 100644 index 0000000000..b610f79190 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hann.h @@ -0,0 +1 @@ +q15_t hann[480] = {0, 1, 5, 12, 22, 35, 50, 69, 90, 114, 140, 170, 202, 237, 275, 316, 359, 405, 454, 506, 560, 617, 677, 739, 805, 873, 943, 1016, 1092, 1171, 1252, 1335, 1422, 1511, 1602, 1696, 1793, 1892, 1993, 2097, 2203, 2312, 2424, 2537, 2653, 2772, 2893, 3016, 3141, 3269, 3399, 3531, 3665, 3802, 3941, 4081, 4224, 4370, 4517, 4666, 4817, 4970, 5125, 5283, 5442, 5603, 5765, 5930, 6096, 6264, 6434, 6606, 6779, 6954, 7131, 7309, 7488, 7670, 7852, 8037, 8222, 8409, 8598, 8788, 8979, 9171, 9364, 9559, 9755, 9952, 10151, 10350, 10550, 10751, 10954, 11157, 11361, 11566, 11772, 11979, 12186, 12394, 12603, 12812, 13022, 13233, 13444, 13656, 13868, 14080, 14293, 14507, 14720, 14934, 15148, 15363, 15577, 15792, 16007, 16222, 16437, 16652, 16866, 17081, 17296, 17510, 17725, 17939, 18153, 18366, 18579, 18792, 19004, 19216, 19428, 19639, 19849, 20059, 20268, 20476, 20684, 20891, 21097, 21302, 21507, 21711, 21913, 22115, 22316, 22516, 22715, 22912, 23109, 23304, 23498, 23691, 23883, 24074, 24263, 24450, 24637, 24822, 25005, 25187, 25368, 25547, 25724, 25900, 26074, 26246, 26417, 26586, 26753, 26919, 27082, 27244, 27404, 27562, 27718, 27873, 28025, 28175, 28323, 28469, 28613, 28755, 28895, 29033, 29168, 29301, 29433, 29561, 29688, 29812, 29934, 30054, 30171, 30286, 30398, 30508, 30616, 30721, 30824, 30924, 31022, 31117, 31210, 31300, 31388, 31473, 31555, 31635, 31712, 31787, 31858, 31928, 31994, 32058, 32119, 32178, 32233, 32286, 32337, 32384, 32429, 32471, 32510, 32547, 32580, 32611, 32639, 32665, 32687, 32707, 32724, 32738, 32749, 32758, 32763, 32766, 32766, 32763, 32758, 32749, 32738, 32724, 32707, 32687, 32665, 32639, 32611, 32580, 32547, 32510, 32471, 32429, 32384, 32337, 32286, 32233, 32178, 32119, 32058, 31994, 31928, 31858, 31787, 31712, 31635, 31555, 31473, 31388, 31300, 31210, 31117, 31022, 30924, 30824, 30721, 30616, 30508, 30398, 30286, 30171, 30054, 29934, 29812, 29688, 29561, 29433, 29301, 29168, 29033, 28895, 28755, 28613, 28469, 28323, 28175, 28025, 27873, 27718, 27562, 27404, 27244, 27082, 26919, 26753, 26586, 26417, 26246, 26074, 25900, 25724, 25547, 25368, 25187, 25005, 24822, 24637, 24450, 24263, 24074, 23883, 23691, 23498, 23304, 23109, 22912, 22715, 22516, 22316, 22115, 21913, 21711, 21507, 21302, 21097, 20891, 20684, 20476, 20268, 20059, 19849, 19639, 19428, 19216, 19004, 18792, 18579, 18366, 18153, 17939, 17725, 17510, 17296, 17081, 16866, 16652, 16437, 16222, 16007, 15792, 15577, 15363, 15148, 14934, 14720, 14507, 14293, 14080, 13868, 13656, 13444, 13233, 13022, 12812, 12603, 12394, 12186, 11979, 11772, 11566, 11361, 11157, 10954, 10751, 10550, 10350, 10151, 9952, 9755, 9559, 9364, 9171, 8979, 8788, 8598, 8409, 8222, 8037, 7852, 7670, 7488, 7309, 7131, 6954, 6779, 6606, 6434, 6264, 6096, 5930, 5765, 5603, 5442, 5283, 5125, 4970, 4817, 4666, 4517, 4370, 4224, 4081, 3941, 3802, 3665, 3531, 3399, 3269, 3141, 3016, 2893, 2772, 2653, 2537, 2424, 2312, 2203, 2097, 1993, 1892, 1793, 1696, 1602, 1511, 1422, 1335, 1252, 1171, 1092, 1016, 943, 873, 805, 739, 677, 617, 560, 506, 454, 405, 359, 316, 275, 237, 202, 170, 140, 114, 90, 69, 50, 35, 22, 12, 5, 1, 0}; diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.cc new file mode 100644 index 0000000000..924f16f285 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.cc @@ -0,0 +1,66 @@ +/* Copyright 2018 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. +==============================================================================*/ + +// See the header for documentation on the meaning of this data. + +#include "tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.h" + +const int g_no_30ms_sample_data_size = 480; +const int16_t g_no_30ms_sample_data[480] = { + 5713, 5735, 5735, 5737, 5701, 5691, 5656, 5633, 5611, 5552, 5475, + 5394, 5293, 5177, 5064, 4924, 4737, 4599, 4420, 4237, 4048, 3828, + 3623, 3413, 3183, 2915, 2622, 2308, 1980, 1657, 1261, 901, 549, + 205, -85, -383, -688, -969, -1246, -1530, -1850, -2206, -2561, -2915, + -3224, -3482, -3713, -3921, -4107, -4287, -4470, -4660, -4850, -5057, -5239, + -5395, -5540, -5619, -5697, -5724, -5697, -5675, -5633, -5590, -5579, -5530, + -5486, -5442, -5426, -5391, -5348, -5276, -5197, -5124, -5039, -4925, -4808, + -4677, -4581, -4479, -4343, -4218, -4087, -3970, -3858, -3729, -3570, -3384, + -3206, -3020, -2839, -2636, -2453, -2287, -2185, -2154, -1926, -1562, -1223, + -758, -473, -64, 395, 599, 880, 814, 938, 1172, 1498, 1928, + 2127, 2422, 2608, 2841, 2937, 2886, 2815, 2985, 3324, 3757, 4152, + 4481, 4652, 4917, 4965, 4766, 4583, 4328, 4503, 4815, 5118, 5408, + 5682, 5956, 6082, 6055, 5744, 5426, 5341, 5427, 5606, 5882, 6065, + 6226, 6428, 6477, 6385, 6009, 5728, 5552, 5439, 5339, 5200, 5008, + 4947, 4835, 4614, 4330, 3887, 3521, 3111, 2460, 1983, 1297, 650, + 279, -353, -720, -1044, -1518, -1668, -2117, -2496, -2743, -3266, -3607, + -3790, -4149, -4075, -4042, -4096, -3981, -4138, -4226, -4214, -4503, -4455, + -4577, -4642, -4346, -4351, -4270, -4263, -4522, -4521, -4673, -4814, -4731, + -4950, -5011, -5004, -5288, -5341, -5566, -5833, -5783, -5929, -5847, -5765, + -5828, -5644, -5613, -5615, -5428, -5291, -5014, -4554, -4277, -3964, -3854, + -3829, -3612, -3603, -3438, -3137, -2831, -2164, -1438, -939, -330, -156, + 46, 242, 73, 242, 220, 239, 542, 565, 739, 872, 801, + 857, 676, 543, 586, 567, 828, 1142, 1490, 1985, 2508, 2982, + 3438, 3699, 3939, 4069, 4178, 4420, 4622, 4917, 5338, 5801, 6285, + 6658, 6963, 7213, 7233, 7328, 7176, 7038, 7031, 6860, 6957, 6767, + 6599, 6523, 6212, 6147, 6063, 5860, 6020, 6015, 6033, 6184, 5722, + 5607, 5016, 4337, 4063, 3229, 3080, 3006, 2804, 3035, 2541, 2136, + 1879, 1012, 401, -575, -1584, -1930, -2278, -2485, -2477, -2712, -2747, + -2766, -3320, -3592, -4188, -4669, -4672, -4939, -4789, -4426, -4203, -3674, + -3563, -3656, -3759, -4067, -4257, -4522, -4970, -5204, -5237, -5139, -4907, + -4911, -4917, -4921, -5007, -5230, -5654, -6122, -6464, -6733, -6948, -7067, + -6972, -6800, -6520, -6132, -5830, -5382, -5091, -4797, -4546, -4472, -4362, + -4350, -4235, -3851, -3454, -3144, -2735, -2341, -1845, -1262, -958, -549, + -166, 66, 382, 366, 352, 341, 85, -13, -176, -303, -235, + -341, -309, -227, -249, -50, 143, 384, 874, 1149, 1552, 2155, + 2767, 3499, 3994, 4460, 4920, 5288, 5569, 5704, 5881, 6094, 6461, + 6653, 6803, 7115, 7311, 7521, 7612, 7443, 7380, 7124, 6742, 6495, + 5964, 5656, 5415, 5167, 5656, 5813, 6027, 6401, 6351, 6787, 7019, + 6581, 6512, 5965, 5308, 5140, 4336, 4147, 3899, 3398, 3360, 2830, + 2624, 1968, 1026, 395, -699, -1424, -2327, -3006, -3192, -3435, -3337, + -3686, -3513, -3350, -3502, -3261, -3878, -4005, -4063, -4187, -3767, -3598, + -3384, -3300, -3094, -2857, -3023, -3274, -3851, -4352, -4523, -4943, -5477, + -5612, -5682, -5733, -5714, -5965, -6110, -5950, -6158, -6548, -6897, -7165, + -7281, -7352, -7258, -7185, -6659, -5946, -5470, +}; diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.h b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.h new file mode 100644 index 0000000000..5299930659 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.h @@ -0,0 +1,32 @@ +/* Copyright 2018 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. +==============================================================================*/ + +// This data was created from the PCM data in a WAV file held in v2 of the +// Speech Commands test dataset, at the path: +// speech_commands_test_set_v0.02/no/f9643d42_nohash_4.wav +// The data was extracted starting at an offset of 8,960, which corresponds to +// the 29th spectrogram slice. It's designed to be used to test the +// preprocessing pipeline, to ensure that the expected spectrogram slice is +// produced given this input. + +#ifndef TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_NO_30MS_SAMPLE_DATA_H_ +#define TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_NO_30MS_SAMPLE_DATA_H_ + +#include + +extern const int g_no_30ms_sample_data_size; +extern const int16_t g_no_30ms_sample_data[]; + +#endif // TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_NO_30MS_SAMPLE_DATA_H_ diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.cc new file mode 100644 index 0000000000..064363e584 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.cc @@ -0,0 +1,23 @@ +/* Copyright 2018 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. +==============================================================================*/ + +// See the header for documentation on the meaning of this data. + +#include "tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.h" + +const uint8_t g_no_power_spectrum_data[g_no_power_spectrum_data_size] = { + 255, 7, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.h b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.h new file mode 100644 index 0000000000..c19566bfb6 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.h @@ -0,0 +1,29 @@ +/* Copyright 2018 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. +==============================================================================*/ + +// This data was extracted from the larger feature data held in +// no_features_data.cc and consists of the 29th spectrogram slice of 43 values. +// This is the expected result of running the sample data in +// no_30ms_sample_data.cc through through the preprocessing pipeline. + +#ifndef TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_NO_POWER_SPECTRUM_DATA_H_ +#define TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_NO_POWER_SPECTRUM_DATA_H_ + +#include + +constexpr int g_no_power_spectrum_data_size = 43; +extern const uint8_t g_no_power_spectrum_data[]; + +#endif // TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_NO_POWER_SPECTRUM_DATA_H_ diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.cc new file mode 100644 index 0000000000..7932258a5d --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.cc @@ -0,0 +1,76 @@ +/* Copyright 2018 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. +==============================================================================*/ + +extern "C" { + #define ARM_MATH_CM4 + #define IFFT_FLAG_R 0 + #define BIT_REVERSE_FLAG 1 + #define FFT_SIZE 512 + #include + #include "tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/hann.h" +} + + #include "tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.h" + +q15_t bufA[FFT_SIZE]; +q15_t bufB[FFT_SIZE]; +arm_rfft_instance_q15 S_arm_fft; +arm_status arm_math_status; + +namespace { +// These constants allow us to allocate fixed-sized arrays on the stack for our +// working memory. +constexpr int kInputSize = 512; +constexpr int kAverageWindowSize = 6; +constexpr int kOutputSize = + ((kInputSize / 2) + (kAverageWindowSize - 1)) / kAverageWindowSize; +} //namespace + +TfLiteStatus Preprocess(tflite::ErrorReporter* error_reporter, + const int16_t* input, int input_size, int output_size, + uint8_t* output) { + if (input_size > kInputSize) { + error_reporter->Report("Input size %d larger than %d", input_size, + kInputSize); + return kTfLiteError; + } + if (output_size != kOutputSize) { + error_reporter->Report("Requested output size %d doesn't match %d", + output_size, kOutputSize); + return kTfLiteError; + } + + arm_mult_q15((q15_t *) input, hann, bufB, 512); + + // Should move init code outside of Preprocess() function + arm_math_status = arm_rfft_init_q15(&S_arm_fft, FFT_SIZE, IFFT_FLAG_R, BIT_REVERSE_FLAG); + arm_rfft_q15(&S_arm_fft, bufB, bufA); + arm_shift_q15(bufA, 5, bufB, FFT_SIZE); + + arm_cmplx_mag_squared_q15(bufB, bufA, 256); + arm_shift_q15(bufA, 1, bufB, 256); + + int i; + for (i=0; i<42; i++) { + arm_mean_q15(bufB+6*i, 6, bufA+i); + } + arm_mean_q15(bufB+252, 4, bufA+42); + + for (i=0; i<43; i++) { + output[i] = (uint8_t) (bufA[i] >> 5); + } + + return kTfLiteOk; +} diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.h b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.h new file mode 100644 index 0000000000..571eb5921b --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.h @@ -0,0 +1,26 @@ +/* Copyright 2018 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_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_PREPROCESSOR_H_ +#define TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_PREPROCESSOR_H_ + +#include "tensorflow/contrib/lite/c/c_api_internal.h" +#include "tensorflow/contrib/lite/experimental/micro/micro_error_reporter.h" + +TfLiteStatus Preprocess(tflite::ErrorReporter* error_reporter, + const int16_t* input, int input_size, int output_size, + uint8_t* output); + +#endif // TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_PREPROCESSOR_H_ diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor_test.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor_test.cc new file mode 100644 index 0000000000..ef82707b12 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor_test.cc @@ -0,0 +1,63 @@ +/* Copyright 2018 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/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.h" +#include "tensorflow/contrib/lite/c/c_api_internal.h" +#include "tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.h" +#include "tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.h" +#include "tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.h" +#include "tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.h" +#include "tensorflow/contrib/lite/experimental/micro/micro_error_reporter.h" +#include "tensorflow/contrib/lite/experimental/micro/testing/micro_test.h" + +TF_LITE_MICRO_TESTS_BEGIN + +TF_LITE_MICRO_TEST(TestPreprocessor) { + tflite::MicroErrorReporter micro_error_reporter; + tflite::ErrorReporter* error_reporter = µ_error_reporter; + + uint8_t yes_calculated_data[g_yes_power_spectrum_data_size]; + TfLiteStatus yes_status = Preprocess( + error_reporter, g_yes_30ms_sample_data, g_yes_30ms_sample_data_size, + g_yes_power_spectrum_data_size, yes_calculated_data); + TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, yes_status); + + for (int i = 0; i < g_yes_power_spectrum_data_size; ++i) { + TF_LITE_MICRO_EXPECT_EQ(g_yes_power_spectrum_data[i], + yes_calculated_data[i]); + if (g_yes_power_spectrum_data[i] != yes_calculated_data[i]) { + error_reporter->Report("Expected value %d but found %d", + g_yes_power_spectrum_data[i], + yes_calculated_data[i]); + } + } + + uint8_t no_calculated_data[g_yes_power_spectrum_data_size]; + TfLiteStatus no_status = Preprocess( + error_reporter, g_no_30ms_sample_data, g_no_30ms_sample_data_size, + g_no_power_spectrum_data_size, no_calculated_data); + TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, no_status); + + for (int i = 0; i < g_no_power_spectrum_data_size; ++i) { + TF_LITE_MICRO_EXPECT_EQ(g_no_power_spectrum_data[i], no_calculated_data[i]); + if (g_no_power_spectrum_data[i] != no_calculated_data[i]) { + error_reporter->Report("Expected value %d but found %d", + g_no_power_spectrum_data[i], + no_calculated_data[i]); + } + } +} + +TF_LITE_MICRO_TESTS_END diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.cc new file mode 100644 index 0000000000..22a44e756d --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.cc @@ -0,0 +1,70 @@ +/* Copyright 2018 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. +==============================================================================*/ + +// See the header for documentation on the meaning of this data. + +#include "tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.h" + +const int g_yes_30ms_sample_data_size = 480; +const int16_t g_yes_30ms_sample_data[480] = { + -876, -470, 510, 803, 170, -787, -1568, -1893, -1598, -1027, + -992, -1803, -2610, -2484, -1905, -2113, -3113, -3399, -2267, -1261, + -2007, -3637, -3909, -2340, -893, -1158, -2272, -2486, -1639, -915, + -777, -596, -91, 196, 85, 210, 875, 1373, 1247, 1219, + 1958, 2718, 2328, 1196, 1008, 2350, 3677, 3269, 1503, 366, + 922, 2264, 2810, 1996, 608, -168, 75, 680, 811, 395, + -56, -318, -607, -966, -1108, -925, -613, -368, -369, -919, + -1926, -2460, -1685, -300, 155, -611, -1524, -2204, -3227, -3859, + -2037, 1622, 2382, -2583, -8448, -7544, -84, 4814, 915, -6423, + -7558, -1746, 2515, -59, -4587, -3858, 1260, 3625, 187, -4148, + -3500, 1542, 5467, 4780, 1256, -1127, -403, 2481, 5332, 6346, + 5014, 2536, 1216, 2467, 5039, 6238, 5070, 3381, 3269, 4173, + 3905, 2248, 1586, 3299, 5240, 4362, 1004, -1382, -489, 2113, + 3168, 1620, -742, -1824, -1435, -897, -1058, -1500, -1545, -1398, + -1965, -3266, -4136, -3756, -2609, -1804, -1986, -3087, -4599, -5296, + -4051, -1731, -781, -2228, -4092, -3977, -2325, -1353, -1568, -1490, + -428, 178, -672, -1650, -1058, 749, 2039, 2079, 1540, 897, + 310, 572, 2266, 4265, 4265, 1869, -231, 559, 3332, 4752, + 3229, 768, 101, 1364, 2463, 1984, 819, 411, 723, 675, + -162, -923, -743, -32, 185, -516, -1653, -2359, -2103, -986, + 42, -205, -1702, -2870, -2337, -809, -221, -982, -1544, -946, + -598, -2117, -4291, -4100, -857, 1948, 338, -4799, -7972, -5403, + 173, 2371, -1063, -5533, -5578, -1777, 605, -985, -3249, -2213, + 1184, 2691, 560, -2356, -2288, 1233, 5244, 6441, 4004, 370, + -663, 2555, 7404, 9282, 6573, 2612, 1836, 4662, 7467, 7393, + 5421, 4262, 4741, 5362, 4705, 3163, 2397, 3337, 4887, 4810, + 2254, -749, -1316, 772, 2706, 2016, -573, -2552, -2746, -2012, + -1647, -1978, -2579, -3105, -3473, -3911, -4484, -4891, -4795, -4163, + -3543, -3538, -4275, -5356, -5743, -4637, -2614, -1301, -1825, -3341, + -4011, -2937, -751, 1007, 1245, 235, -639, -61, 1626, 2864, + 2967, 2734, 3013, 3329, 2914, 2312, 2666, 3839, 4308, 3162, + 1453, 768, 1255, 1887, 2006, 1715, 1031, -297, -1660, -1690, + -277, 813, -30, -2137, -3370, -2854, -1553, -593, -413, -1146, + -2567, -3440, -2369, -205, 379, -1258, -2315, -812, 262, -3205, + -8576, -7894, 738, 7492, 1951, -11595, -17098, -6934, 7139, 8065, + -4575, -14199, -8946, 3606, 7504, -547, -8242, -5113, 4406, 8113, + 2134, -5040, -4089, 4157, 10934, 10158, 4167, -565, -192, 4428, + 9765, 12201, 9861, 4512, 1225, 3451, 8483, 10133, 6497, 2574, + 3333, 6806, 6986, 2487, -1214, 623, 5416, 6647, 2204, -3289, + -4556, -1565, 1544, 1525, -1236, -4293, -5695, -5174, -3995, -3403, + -3449, -3750, -4505, -6014, -7296, -6523, -3849, -2096, -3288, -5722, + -6004, -3581, -1497, -1960, -3330, -2800, -434, 964, -111, -1739, + -1136, 1736, 4151, 3736, 1274, -451, 469, 3386, 5833, 5898, + 3646, 1085, 272, 1743, 4061, 5108, 3837, 1490, 246, 967, + 1866, 859, -1069, -974, 1542, 2835, 47, -4285, -5068, -1567, + 1781, 1223, -1997, -4227, -3747, -1720, 41, 245, -1228, -2972, + -2673, 22, 1980, -930, -7721, -11271, -5725, 4974, 8484, -2007, + -16979, -19255, -4670, 11057, 9690, -6417, -17537, -10841, 4262, 9292, +}; diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.h b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.h new file mode 100644 index 0000000000..cb757e84ba --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.h @@ -0,0 +1,32 @@ +/* Copyright 2018 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. +==============================================================================*/ + +// This data was created from the PCM data in a WAV file held in v2 of the +// Speech Commands test dataset, at the path: +// speech_commands_test_set_v0.02/yes/f2e59fea_nohash_1.wav +// The data was extracted starting at an offset of 8,000, which corresponds to +// the 26th spectrogram slice. It's designed to be used to test the +// preprocessing pipeline, to ensure that the expected spectrogram slice is +// produced given this input. + +#ifndef TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_YES_30MS_SAMPLE_DATA_H_ +#define TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_YES_30MS_SAMPLE_DATA_H_ + +#include + +extern const int g_yes_30ms_sample_data_size; +extern const int16_t g_yes_30ms_sample_data[]; + +#endif // TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_YES_30MS_SAMPLE_DATA_H_ diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.cc new file mode 100644 index 0000000000..96a84e5eaa --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.cc @@ -0,0 +1,23 @@ +/* Copyright 2018 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. +==============================================================================*/ + +// See the header for documentation on the meaning of this data. + +#include "tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.h" + +const uint8_t g_yes_power_spectrum_data[g_yes_power_spectrum_data_size] = { + 8, 89, 8, 0, 0, 0, 0, 0, 0, 0, 0, 4, 13, 1, 6, 23, 20, 6, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.h b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.h new file mode 100644 index 0000000000..b02853f2ea --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.h @@ -0,0 +1,29 @@ +/* Copyright 2018 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. +==============================================================================*/ + +// This data was extracted from the larger feature data held in +// no_features_data.cc and consists of the 26th spectrogram slice of 43 values. +// This is the expected result of running the sample data in +// yes_30ms_sample_data.cc through through the preprocessing pipeline. + +#ifndef TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_YES_POWER_SPECTRUM_DATA_H_ +#define TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_YES_POWER_SPECTRUM_DATA_H_ + +#include + +constexpr int g_yes_power_spectrum_data_size = 43; +extern const uint8_t g_yes_power_spectrum_data[]; + +#endif // TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_YES_POWER_SPECTRUM_DATA_H_ -- GitLab From dd64bc646851fd9d2479e596274b2bea854c7af0 Mon Sep 17 00:00:00 2001 From: Steve Nesae Date: Sun, 4 Nov 2018 11:14:45 -0600 Subject: [PATCH 010/622] Initial add of cmsis-dsp makefile and modification of apollo3evb_makefile; Added *.bin rule to Mkaefile --- .../experimental/micro/tools/make/Makefile | 5 ++++ .../make/targets/apollo3evb_makefile.inc | 25 +++++++++++-------- .../tools/make/targets/cmsis-dsp_makefile.inc | 9 +++++++ 3 files changed, 29 insertions(+), 10 deletions(-) create mode 100644 tensorflow/lite/experimental/micro/tools/make/targets/cmsis-dsp_makefile.inc diff --git a/tensorflow/lite/experimental/micro/tools/make/Makefile b/tensorflow/lite/experimental/micro/tools/make/Makefile index 5492003e5a..b80ace859a 100644 --- a/tensorflow/lite/experimental/micro/tools/make/Makefile +++ b/tensorflow/lite/experimental/micro/tools/make/Makefile @@ -180,6 +180,11 @@ $(BINDIR)%_test : $(OBJDIR)%_test.o $(MICROLITE_LIB_PATH) $(BINDIR)%.test_target: $(BINDIR)%_test $(TEST_SCRIPT) $< '~~~ALL TESTS PASSED~~~' +# snease: Add %.bin rule here since BINDIR is now defined +$(BINDIR)%.bin: $(BINDIR)% + @mkdir -p $(dir $@) + $(OBJCOPY) $< $@ -O binary + $(info $(MICROLITE_TEST_TARGETS)) test: test_micro_speech $(MICROLITE_TEST_TARGETS) diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc index f722204fea..6a5fc7895c 100644 --- a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc +++ b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc @@ -4,10 +4,10 @@ ifeq ($(TARGET), apollo3evb) TARGET_TOOLCHAIN_PREFIX := arm-none-eabi- # Download the Ambiq Apollo3 SDK and set this variable to find the header # files: - APOLLO3_SDK := /ssd/ambiq/AmbiqSuite\ SDK\ for\ Apollo3/Apollo3-SDK-2018.08.13/ + APOLLO3_SDK := /home/snease/work/Apollo3-SDK-2018.08.13 # Need a pointer to the GNU ARM toolchain for crtbegin.o for the fp functions # with the softfp interfaces. - GCC_ARM := /ssd/gnu_arm_toolchain/gcc-arm-none-eabi-7-2018-q2-update/ + GCC_ARM := /home/snease/work/gcc-arm-none-eabi-7-2018-q2-update/ PLATFORM_FLAGS = \ -DPART_apollo3 \ @@ -16,6 +16,7 @@ ifeq ($(TARGET), apollo3evb) -DGEMMLOWP_ALLOW_SLOW_SCALAR_FALLBACK \ -DTF_LITE_STATIC_MEMORY \ -DTF_LITE_MCU_DEBUG_LOG \ + -D __FPU_PRESENT=1 \ -fno-rtti \ -fmessage-length=0 \ -fno-exceptions \ @@ -28,7 +29,7 @@ ifeq ($(TARGET), apollo3evb) -mcpu=cortex-m4 \ -mthumb \ -mfpu=fpv4-sp-d16 \ - -mfloat-abi=softfp \ + -mfloat-abi=softfp\ -std=gnu++11 \ -Wvla \ -Wall \ @@ -46,7 +47,7 @@ ifeq ($(TARGET), apollo3evb) CXXFLAGS += $(PLATFORM_FLAGS) CCFLAGS += $(PLATFORM_FLAGS) LDFLAGS += \ - -mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=softfp \ + -mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=softfp\ -nostartfiles -static \ -Wl,--gc-sections -Wl,--entry,Reset_Handler \ -Wl,--start-group -lm -lc -lgcc -Wl,--end-group \ @@ -59,10 +60,12 @@ ifeq ($(TARGET), apollo3evb) MICROLITE_LIBS := \ $(MAKEFILE_DIR)/targets/apollo3evb/libam_bsp.a \ $(MAKEFILE_DIR)/targets/apollo3evb/libam_hal.a \ + $(MAKEFILE_DIR)/downloads/cmsis/CMSIS/Lib/GCC/libarm_cortexM4lf_math_softfp.a \ $(GCC_ARM)/lib/gcc/arm-none-eabi/7.3.1/thumb/v7e-m/fpv4-sp/softfp/crtbegin.o \ -lm INCLUDES += \ -isystem$(MAKEFILE_DIR)/downloads/cmsis/CMSIS/Core/Include/ \ + -isystem$(MAKEFILE_DIR)/downloads/cmsis/CMSIS/DSP/Include/ \ -I$(GCC_ARM)/arm-none-eabi/ \ -I$(APOLLO3_SDK)/mcu/apollo3/ \ -I$(APOLLO3_SDK)/CMSIS/AmbiqMicro/Include/ \ @@ -86,19 +89,21 @@ ifeq ($(TARGET), apollo3evb) $(MAKEFILE_DIR)/targets/apollo3evb/am_util_id.c \ $(MAKEFILE_DIR)/targets/apollo3evb/am_util_stdio.c - TEST_SCRIPT := tensorflow/lite/experimental/log_test/test_apollo3evb_binary.sh + TEST_SCRIPT := tensorflow/contrib/lite/experimental/log_test/test_apollo3evb_binary.sh # These are tests that don't currently work on the blue pill. EXCLUDED_TESTS := \ - tensorflow/lite/experimental/micro/micro_interpreter_test.cc \ - tensorflow/lite/experimental/micro/simple_tensor_allocator_test.cc + tensorflow/contrib/lite/experimental/micro/micro_interpreter_test.cc \ + tensorflow/contrib/lite/experimental/micro/simple_tensor_allocator_test.cc MICROLITE_TEST_SRCS := $(filter-out $(EXCLUDED_TESTS), $(MICROLITE_TEST_SRCS)) # These are microcontroller-specific rules for converting the ELF output # of the linker into a binary image that can be loaded directly. OBJCOPY := $(TARGET_TOOLCHAIN_PREFIX)objcopy -$(BINDIR)/%.bin: $(BINDIR)/% - @mkdir -p $(dir $@) - $(OBJCOPY) $< $@ -O binary +# BINDIR isn't defined yet +# Put this at the end of the main makefile +# $(BINDIR)/%.bin: $(BINDIR)/% +# @mkdir -p $(dir $@) +# $(OBJCOPY) $< $@ -O binary endif diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/cmsis-dsp_makefile.inc b/tensorflow/lite/experimental/micro/tools/make/targets/cmsis-dsp_makefile.inc new file mode 100644 index 0000000000..9bbb7c12a3 --- /dev/null +++ b/tensorflow/lite/experimental/micro/tools/make/targets/cmsis-dsp_makefile.inc @@ -0,0 +1,9 @@ +ifeq ($(VENDORLIB), cmsis-dsp) +PREPROCESSOR_TEST_SRCS := \ +tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.cc \ +tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor_test.cc \ +tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.cc \ +tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.cc \ +tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.cc \ +tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.cc +endif -- GitLab From 2d7b55a9aa4abab59a88821ec05f6e3318cd499a Mon Sep 17 00:00:00 2001 From: Steve Nesae Date: Sun, 4 Nov 2018 15:55:24 -0600 Subject: [PATCH 011/622] gitignore for some subdirectories of make; CMSIS files fixed for new location of lite --- .../micro_speech/CMSIS/no_30ms_sample_data.cc | 2 +- .../micro_speech/CMSIS/no_power_spectrum_data.cc | 2 +- .../examples/micro_speech/CMSIS/preprocessor.cc | 4 ++-- .../examples/micro_speech/CMSIS/preprocessor.h | 4 ++-- .../micro_speech/CMSIS/preprocessor_test.cc | 16 ++++++++-------- .../micro_speech/CMSIS/yes_30ms_sample_data.cc | 2 +- .../CMSIS/yes_power_spectrum_data.cc | 2 +- .../lite/experimental/micro/tools/make/Makefile | 3 +++ .../tools/make/targets/apollo3evb_makefile.inc | 16 ++++------------ .../tools/make/targets/cmsis-dsp_makefile.inc | 12 ++++++------ 10 files changed, 29 insertions(+), 34 deletions(-) diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.cc index 924f16f285..6d9147bd44 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.cc @@ -15,7 +15,7 @@ limitations under the License. // See the header for documentation on the meaning of this data. -#include "tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.h" const int g_no_30ms_sample_data_size = 480; const int16_t g_no_30ms_sample_data[480] = { diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.cc index 064363e584..cdd7d46c8c 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.cc @@ -15,7 +15,7 @@ limitations under the License. // See the header for documentation on the meaning of this data. -#include "tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.h" const uint8_t g_no_power_spectrum_data[g_no_power_spectrum_data_size] = { 255, 7, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.cc index 7932258a5d..24119cbdda 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.cc @@ -19,10 +19,10 @@ extern "C" { #define BIT_REVERSE_FLAG 1 #define FFT_SIZE 512 #include - #include "tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/hann.h" + #include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hann.h" } - #include "tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.h" + #include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.h" q15_t bufA[FFT_SIZE]; q15_t bufB[FFT_SIZE]; diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.h b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.h index 571eb5921b..3d02406371 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.h +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.h @@ -16,8 +16,8 @@ limitations under the License. #ifndef TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_PREPROCESSOR_H_ #define TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_PREPROCESSOR_H_ -#include "tensorflow/contrib/lite/c/c_api_internal.h" -#include "tensorflow/contrib/lite/experimental/micro/micro_error_reporter.h" +#include "tensorflow/lite/c/c_api_internal.h" +#include "tensorflow/lite/experimental/micro/micro_error_reporter.h" TfLiteStatus Preprocess(tflite::ErrorReporter* error_reporter, const int16_t* input, int input_size, int output_size, diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor_test.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor_test.cc index ef82707b12..3560a17cd4 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor_test.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor_test.cc @@ -13,14 +13,14 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.h" -#include "tensorflow/contrib/lite/c/c_api_internal.h" -#include "tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.h" -#include "tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.h" -#include "tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.h" -#include "tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.h" -#include "tensorflow/contrib/lite/experimental/micro/micro_error_reporter.h" -#include "tensorflow/contrib/lite/experimental/micro/testing/micro_test.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.h" +#include "tensorflow/lite/c/c_api_internal.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.h" +#include "tensorflow/lite/experimental/micro/micro_error_reporter.h" +#include "tensorflow/lite/experimental/micro/testing/micro_test.h" TF_LITE_MICRO_TESTS_BEGIN diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.cc index 22a44e756d..056ee544da 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.cc @@ -15,7 +15,7 @@ limitations under the License. // See the header for documentation on the meaning of this data. -#include "tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.h" const int g_yes_30ms_sample_data_size = 480; const int16_t g_yes_30ms_sample_data[480] = { diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.cc index 96a84e5eaa..235a853e68 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.cc @@ -15,7 +15,7 @@ limitations under the License. // See the header for documentation on the meaning of this data. -#include "tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.h" const uint8_t g_yes_power_spectrum_data[g_yes_power_spectrum_data_size] = { 8, 89, 8, 0, 0, 0, 0, 0, 0, 0, 0, 4, 13, 1, 6, 23, 20, 6, 4, 0, 0, 0, diff --git a/tensorflow/lite/experimental/micro/tools/make/Makefile b/tensorflow/lite/experimental/micro/tools/make/Makefile index b80ace859a..4dbfc629a2 100644 --- a/tensorflow/lite/experimental/micro/tools/make/Makefile +++ b/tensorflow/lite/experimental/micro/tools/make/Makefile @@ -181,6 +181,9 @@ $(BINDIR)%.test_target: $(BINDIR)%_test $(TEST_SCRIPT) $< '~~~ALL TESTS PASSED~~~' # snease: Add %.bin rule here since BINDIR is now defined +# These are microcontroller-specific rules for converting the ELF output +# of the linker into a binary image that can be loaded directly. +OBJCOPY := $(TARGET_TOOLCHAIN_PREFIX)objcopy $(BINDIR)%.bin: $(BINDIR)% @mkdir -p $(dir $@) $(OBJCOPY) $< $@ -O binary diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc index 6a5fc7895c..f1353c6150 100644 --- a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc +++ b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc @@ -4,10 +4,10 @@ ifeq ($(TARGET), apollo3evb) TARGET_TOOLCHAIN_PREFIX := arm-none-eabi- # Download the Ambiq Apollo3 SDK and set this variable to find the header # files: - APOLLO3_SDK := /home/snease/work/Apollo3-SDK-2018.08.13 + APOLLO3_SDK := ../Apollo3-SDK-2018.08.13 # Need a pointer to the GNU ARM toolchain for crtbegin.o for the fp functions # with the softfp interfaces. - GCC_ARM := /home/snease/work/gcc-arm-none-eabi-7-2018-q2-update/ + GCC_ARM := ../gcc-arm-none-eabi-7-2018-q2-update/ PLATFORM_FLAGS = \ -DPART_apollo3 \ @@ -73,6 +73,8 @@ ifeq ($(TARGET), apollo3evb) -I$(APOLLO3_SDK)/devices/ \ -I$(APOLLO3_SDK)/utils/ +$(warning INCLUDES IS $(INCLUDES)) + # The startup_gcc.c file is an altered version of the examples/hello_world/gcc/startup_gcc.c # file from Ambiq: # - Increase the stack size from 1k to 20k @@ -96,14 +98,4 @@ ifeq ($(TARGET), apollo3evb) tensorflow/contrib/lite/experimental/micro/simple_tensor_allocator_test.cc MICROLITE_TEST_SRCS := $(filter-out $(EXCLUDED_TESTS), $(MICROLITE_TEST_SRCS)) -# These are microcontroller-specific rules for converting the ELF output -# of the linker into a binary image that can be loaded directly. -OBJCOPY := $(TARGET_TOOLCHAIN_PREFIX)objcopy - -# BINDIR isn't defined yet -# Put this at the end of the main makefile -# $(BINDIR)/%.bin: $(BINDIR)/% -# @mkdir -p $(dir $@) -# $(OBJCOPY) $< $@ -O binary - endif diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/cmsis-dsp_makefile.inc b/tensorflow/lite/experimental/micro/tools/make/targets/cmsis-dsp_makefile.inc index 9bbb7c12a3..fecb98de7c 100644 --- a/tensorflow/lite/experimental/micro/tools/make/targets/cmsis-dsp_makefile.inc +++ b/tensorflow/lite/experimental/micro/tools/make/targets/cmsis-dsp_makefile.inc @@ -1,9 +1,9 @@ ifeq ($(VENDORLIB), cmsis-dsp) PREPROCESSOR_TEST_SRCS := \ -tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.cc \ -tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor_test.cc \ -tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.cc \ -tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.cc \ -tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.cc \ -tensorflow/contrib/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.cc +tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.cc \ +tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor_test.cc \ +tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.cc \ +tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.cc \ +tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.cc \ +tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.cc endif -- GitLab From c5e00121e50ca8a52837d205e6da766bd3360f55 Mon Sep 17 00:00:00 2001 From: Steve Nesae Date: Sun, 4 Nov 2018 15:56:51 -0600 Subject: [PATCH 012/622] Forgot to add .gitignores in previous commit, adding now; script to copy apollo3 files --- .../experimental/micro/tools/make/.gitignore | 2 ++ .../tools/make/targets/apollo3evb/.gitignore | 4 +++ .../tools/make/targets/copy_apollo3files.sh | 30 +++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 tensorflow/lite/experimental/micro/tools/make/.gitignore create mode 100644 tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/.gitignore create mode 100755 tensorflow/lite/experimental/micro/tools/make/targets/copy_apollo3files.sh diff --git a/tensorflow/lite/experimental/micro/tools/make/.gitignore b/tensorflow/lite/experimental/micro/tools/make/.gitignore new file mode 100644 index 0000000000..752f078fb5 --- /dev/null +++ b/tensorflow/lite/experimental/micro/tools/make/.gitignore @@ -0,0 +1,2 @@ +downloads +gen diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/.gitignore b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/.gitignore new file mode 100644 index 0000000000..cb646e29d9 --- /dev/null +++ b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/.gitignore @@ -0,0 +1,4 @@ +startup_gcc.c +am_*.c +libam*.a + diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/copy_apollo3files.sh b/tensorflow/lite/experimental/micro/tools/make/targets/copy_apollo3files.sh new file mode 100755 index 0000000000..1bab307a73 --- /dev/null +++ b/tensorflow/lite/experimental/micro/tools/make/targets/copy_apollo3files.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# 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. +# ============================================================================== + +if [ ! -d "../Apollo3-SDK-2018.08.13" ]; then + echo "Apollo 3 SDK does not exist" + echo "Either the SDK has not been downloaded, or this script is not being done from the root of the repository" +else + DEST_DIR="tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb" + AP3_DIR="../Apollo3-SDK-2018.08.13" + cp "$AP3_DIR/boards/apollo3_evb/examples/hello_world/gcc/startup_gcc.c" "$DEST_DIR" + cp "$AP3_DIR/utils/am_util_delay.c" "$DEST_DIR" + cp "$AP3_DIR/utils/am_util_faultisr.c" "$DEST_DIR" + cp "$AP3_DIR/utils/am_util_id.c" "$DEST_DIR" + cp "$AP3_DIR/utils/am_util_stdio.c" "$DEST_DIR" + cp "$AP3_DIR/boards/apollo3_evb/bsp/gcc/bin/libam_bsp.a" "$DEST_DIR" + cp "$AP3_DIR/mcu/apollo3/hal/gcc/bin/libam_hal.a" "$DEST_DIR" +fi -- GitLab From 061415d20914f6ee09b7fec12a8d59f6d81e37df Mon Sep 17 00:00:00 2001 From: Steve Nesae Date: Mon, 5 Nov 2018 14:46:41 -0600 Subject: [PATCH 013/622] cmd file and py file to update yes/no data; also added the data itself --- .../CMSIS/no_power_spectrum_data.cc | 5 +-- .../CMSIS/yes_power_spectrum_data.cc | 5 +-- .../targets/apollo3evb/get_yesno_data.cmd | 10 ++++++ .../apollo3evb/replace_calculated_data.py | 33 +++++++++++++++++++ 4 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/get_yesno_data.cmd create mode 100644 tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/replace_calculated_data.py diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.cc index cdd7d46c8c..a3e561a8b5 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.cc @@ -17,7 +17,4 @@ limitations under the License. #include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.h" -const uint8_t g_no_power_spectrum_data[g_no_power_spectrum_data_size] = { - 255, 7, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; +const uint8_t g_no_power_spectrum_data[g_no_power_spectrum_data_size] = {233,6,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,40}; diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.cc index 235a853e68..89edb51040 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.cc @@ -17,7 +17,4 @@ limitations under the License. #include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.h" -const uint8_t g_yes_power_spectrum_data[g_yes_power_spectrum_data_size] = { - 8, 89, 8, 0, 0, 0, 0, 0, 0, 0, 0, 4, 13, 1, 6, 23, 20, 6, 4, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; +const uint8_t g_yes_power_spectrum_data[g_yes_power_spectrum_data_size] = {8,88,8,0,0,0,0,0,0,0,0,3,12,0,5,22,19,5,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,9,1}; diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/get_yesno_data.cmd b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/get_yesno_data.cmd new file mode 100644 index 0000000000..71d5389ee3 --- /dev/null +++ b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/get_yesno_data.cmd @@ -0,0 +1,10 @@ +file ../../gen/apollo3evb_cortex-m4/bin/preprocessor_test +target remote localhost:2331 +load ../../gen/apollo3evb_cortex-m4/bin/preprocessor_test +monitor reset +break preprocessor_test.cc:35 +break preprocessor_test.cc:51 +c +dump verilog value yes_calculated_data.txt yes_calculated_data +c +dump verilog value no_calculated_data.txt no_calculated_data diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/replace_calculated_data.py b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/replace_calculated_data.py new file mode 100644 index 0000000000..3c1b0110fc --- /dev/null +++ b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/replace_calculated_data.py @@ -0,0 +1,33 @@ +import numpy as np +import re + +# This should be run from make/targets/apollo3evb + +def new_data_to_array(fn): + vals = [] + with open(fn) as f: + for n, line in enumerate(f): + if n is not 0: + vals.extend([str(int(v, 16)) for v in line.split()]) + + return ','.join(vals) + +def replace_data(fn_old, new_data): + patt = '(?<=\{).+?(?=\})' + with open(fn_old,'r') as f: + str_old = f.read() + str_new = re.sub(patt, new_data, str_old, flags=re.DOTALL) + with open(fn_old,'w') as f: + f.write(str_new) + + +yes_old = '../../../../examples/micro_speech/CMSIS/yes_power_spectrum_data.cc' +no_old = '../../../../examples/micro_speech/CMSIS/no_power_spectrum_data.cc' +yes_new = 'yes_calculated_data.txt' +no_new = 'no_calculated_data.txt' + +yes_new_vals = new_data_to_array(yes_new) +no_new_vals = new_data_to_array(no_new) + +replace_data(yes_old, yes_new_vals) +replace_data(no_old, no_new_vals) -- GitLab From 2d8fce07dc8604c29f566418dd2610573f77cc05 Mon Sep 17 00:00:00 2001 From: Steve Nesae Date: Mon, 5 Nov 2018 14:47:17 -0600 Subject: [PATCH 014/622] added script to copy ap3 files to project --- .../targets/apollo3evb/copy_apollo3files.sh | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100755 tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/copy_apollo3files.sh diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/copy_apollo3files.sh b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/copy_apollo3files.sh new file mode 100755 index 0000000000..1bab307a73 --- /dev/null +++ b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/copy_apollo3files.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# 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. +# ============================================================================== + +if [ ! -d "../Apollo3-SDK-2018.08.13" ]; then + echo "Apollo 3 SDK does not exist" + echo "Either the SDK has not been downloaded, or this script is not being done from the root of the repository" +else + DEST_DIR="tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb" + AP3_DIR="../Apollo3-SDK-2018.08.13" + cp "$AP3_DIR/boards/apollo3_evb/examples/hello_world/gcc/startup_gcc.c" "$DEST_DIR" + cp "$AP3_DIR/utils/am_util_delay.c" "$DEST_DIR" + cp "$AP3_DIR/utils/am_util_faultisr.c" "$DEST_DIR" + cp "$AP3_DIR/utils/am_util_id.c" "$DEST_DIR" + cp "$AP3_DIR/utils/am_util_stdio.c" "$DEST_DIR" + cp "$AP3_DIR/boards/apollo3_evb/bsp/gcc/bin/libam_bsp.a" "$DEST_DIR" + cp "$AP3_DIR/mcu/apollo3/hal/gcc/bin/libam_hal.a" "$DEST_DIR" +fi -- GitLab From 62d708c3bd3377e8588eddbb8d7940d2a55ca530 Mon Sep 17 00:00:00 2001 From: Steve Nesae Date: Mon, 5 Nov 2018 14:49:02 -0600 Subject: [PATCH 015/622] Using O3 instead of Os since Os was causing stalls --- .../micro/tools/make/targets/apollo3evb_makefile.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc index f1353c6150..0116670615 100644 --- a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc +++ b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc @@ -43,7 +43,7 @@ ifeq ($(TARGET), apollo3evb) -fpermissive \ -nostdlib \ -g \ - -Os + -O3 CXXFLAGS += $(PLATFORM_FLAGS) CCFLAGS += $(PLATFORM_FLAGS) LDFLAGS += \ -- GitLab From e19bd69fa17f2f4b881ca23f46203c241c4f435b Mon Sep 17 00:00:00 2001 From: Steve Nesae Date: Mon, 5 Nov 2018 14:49:46 -0600 Subject: [PATCH 016/622] moved copy_apollo3files.sh and added gdb *.cmd file for running the preprocessor test --- .../targets/apollo3evb/preprocessor_test.cmd | 4 +++ .../tools/make/targets/copy_apollo3files.sh | 30 ------------------- 2 files changed, 4 insertions(+), 30 deletions(-) create mode 100644 tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/preprocessor_test.cmd delete mode 100755 tensorflow/lite/experimental/micro/tools/make/targets/copy_apollo3files.sh diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/preprocessor_test.cmd b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/preprocessor_test.cmd new file mode 100644 index 0000000000..1b1db457fe --- /dev/null +++ b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/preprocessor_test.cmd @@ -0,0 +1,4 @@ +file ../../gen/apollo3evb_cortex-m4/bin/preprocessor_test +target remote localhost:2331 +load ../../gen/apollo3evb_cortex-m4/bin/preprocessor_test +monitor reset diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/copy_apollo3files.sh b/tensorflow/lite/experimental/micro/tools/make/targets/copy_apollo3files.sh deleted file mode 100755 index 1bab307a73..0000000000 --- a/tensorflow/lite/experimental/micro/tools/make/targets/copy_apollo3files.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash -# 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. -# ============================================================================== - -if [ ! -d "../Apollo3-SDK-2018.08.13" ]; then - echo "Apollo 3 SDK does not exist" - echo "Either the SDK has not been downloaded, or this script is not being done from the root of the repository" -else - DEST_DIR="tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb" - AP3_DIR="../Apollo3-SDK-2018.08.13" - cp "$AP3_DIR/boards/apollo3_evb/examples/hello_world/gcc/startup_gcc.c" "$DEST_DIR" - cp "$AP3_DIR/utils/am_util_delay.c" "$DEST_DIR" - cp "$AP3_DIR/utils/am_util_faultisr.c" "$DEST_DIR" - cp "$AP3_DIR/utils/am_util_id.c" "$DEST_DIR" - cp "$AP3_DIR/utils/am_util_stdio.c" "$DEST_DIR" - cp "$AP3_DIR/boards/apollo3_evb/bsp/gcc/bin/libam_bsp.a" "$DEST_DIR" - cp "$AP3_DIR/mcu/apollo3/hal/gcc/bin/libam_hal.a" "$DEST_DIR" -fi -- GitLab From 47c17d63fc1d32c6561874422cd6d5920c4f15f8 Mon Sep 17 00:00:00 2001 From: Steve Nesae Date: Mon, 5 Nov 2018 17:12:47 -0600 Subject: [PATCH 017/622] Added README and added text substitution to prep_apollo3_files.sh --- .../micro/tools/make/targets/apollo3evb/README.md | 13 +++++++++++++ .../{copy_apollo3files.sh => prep_apollo3_files.sh} | 2 ++ 2 files changed, 15 insertions(+) create mode 100644 tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/README.md rename tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/{copy_apollo3files.sh => prep_apollo3_files.sh} (92%) diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/README.md b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/README.md new file mode 100644 index 0000000000..6108c3c498 --- /dev/null +++ b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/README.md @@ -0,0 +1,13 @@ +Follow these steps to get the preprocessor test working on Apollo 3: + +1) Download the SDK to the be at the same level as tensorflow.git +2) Copy and prepare files by running tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/prep_apollo3_files.sh +3) Recompile libarm_cortexM4lf_math.a with the softfp option, and call it libarm_cartexM4lf_math_softfp.a. The original version was compiled with the hard option, and this caused conflicts with existing software. We might be able to fix this in the future +4) Install Segger JLink tools from https://www.segger.com/downloads/jlink/ +5) Compile the preprocessor_test_bin project with the following command: make -f tensorflow/lite/experimental/micro/tools/make/Makefile TARGET=apollo3evb VENDORLIB=cmsis-dsp preprocessor_test_bin +6) Download to the target with JFlashLiteExe with the following settings: + a) Device = AMA3B1KK-KBR + b) Interface = SWD at 1000 kHz + c) Data file = tensorflow/lite/experimental/micro/tools/make/gen/apollo3evb_cortex-m4/bin/preprocessor_test.bin + d) Prog Addr = 0x0000C000 +7) Connect to device via serial port (115200 baud) and press reset button. Should see all tests passed. Seeing a discrepance between Windows and Linux testing --> need to debug diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/copy_apollo3files.sh b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/prep_apollo3_files.sh similarity index 92% rename from tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/copy_apollo3files.sh rename to tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/prep_apollo3_files.sh index 1bab307a73..79ce18f11f 100755 --- a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/copy_apollo3files.sh +++ b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/prep_apollo3_files.sh @@ -27,4 +27,6 @@ else cp "$AP3_DIR/utils/am_util_stdio.c" "$DEST_DIR" cp "$AP3_DIR/boards/apollo3_evb/bsp/gcc/bin/libam_bsp.a" "$DEST_DIR" cp "$AP3_DIR/mcu/apollo3/hal/gcc/bin/libam_hal.a" "$DEST_DIR" + sed -i -e '131s/1024/1024\*20/g' "$DEST_DIR/startup_gcc.c" + sed -i -e 's/main/_main/g' "$DEST_DIR/startup_gcc.c" fi -- GitLab From 2b2b01f7ba2a7b131a3015ef9164b85823c8e2bf Mon Sep 17 00:00:00 2001 From: Steve Nesae Date: Mon, 5 Nov 2018 17:18:47 -0600 Subject: [PATCH 018/622] fixed some markup --- .../tools/make/targets/apollo3evb/README.md | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/README.md b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/README.md index 6108c3c498..fec4923e0e 100644 --- a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/README.md +++ b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/README.md @@ -1,13 +1,13 @@ Follow these steps to get the preprocessor test working on Apollo 3: -1) Download the SDK to the be at the same level as tensorflow.git -2) Copy and prepare files by running tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/prep_apollo3_files.sh -3) Recompile libarm_cortexM4lf_math.a with the softfp option, and call it libarm_cartexM4lf_math_softfp.a. The original version was compiled with the hard option, and this caused conflicts with existing software. We might be able to fix this in the future -4) Install Segger JLink tools from https://www.segger.com/downloads/jlink/ -5) Compile the preprocessor_test_bin project with the following command: make -f tensorflow/lite/experimental/micro/tools/make/Makefile TARGET=apollo3evb VENDORLIB=cmsis-dsp preprocessor_test_bin -6) Download to the target with JFlashLiteExe with the following settings: - a) Device = AMA3B1KK-KBR - b) Interface = SWD at 1000 kHz - c) Data file = tensorflow/lite/experimental/micro/tools/make/gen/apollo3evb_cortex-m4/bin/preprocessor_test.bin - d) Prog Addr = 0x0000C000 -7) Connect to device via serial port (115200 baud) and press reset button. Should see all tests passed. Seeing a discrepance between Windows and Linux testing --> need to debug +1. Download the SDK to the be at the same level as tensorflow.git +2. Copy and prepare files by running tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/prep_apollo3_files.sh +3. Recompile libarm_cortexM4lf_math.a with the softfp option, and call it libarm_cartexM4lf_math_softfp.a. The original version was compiled with the hard option, and this caused conflicts with existing software. We might be able to fix this in the future +4. Install Segger JLink tools from https://www.segger.com/downloads/jlink/ +5. Compile the preprocessor_test_bin project with the following command: make -f tensorflow/lite/experimental/micro/tools/make/Makefile TARGET=apollo3evb VENDORLIB=cmsis-dsp preprocessor_test_bin +6. Download to the target with JFlashLiteExe with the following settings: + 1. Device = AMA3B1KK-KBR + 2. Interface = SWD at 1000 kHz + 3. Data file = tensorflow/lite/experimental/micro/tools/make/gen/apollo3evb_cortex-m4/bin/preprocessor_test.bin + 4. Prog Addr = 0x0000C000 +7. Connect to device via serial port (115200 baud) and press reset button. Should see all tests passed. Seeing a discrepance between Windows and Linux testing --> need to debug -- GitLab From 75ef9f1888867cb2943fc0ce75c258c2cdd8f8aa Mon Sep 17 00:00:00 2001 From: Steve Nesae Date: Mon, 5 Nov 2018 17:34:03 -0600 Subject: [PATCH 019/622] Removed redundant yes/no sample data from the CMSIS directory and changed the Makefile and source to point to the original raw sample data --- .../micro_speech/CMSIS/no_30ms_sample_data.cc | 66 ----------------- .../micro_speech/CMSIS/no_30ms_sample_data.h | 32 --------- .../micro_speech/CMSIS/preprocessor_test.cc | 4 +- .../CMSIS/yes_30ms_sample_data.cc | 70 ------------------- .../micro_speech/CMSIS/yes_30ms_sample_data.h | 32 --------- .../tools/make/targets/cmsis-dsp_makefile.inc | 4 +- 6 files changed, 4 insertions(+), 204 deletions(-) delete mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.cc delete mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.h delete mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.cc delete mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.h diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.cc deleted file mode 100644 index 6d9147bd44..0000000000 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.cc +++ /dev/null @@ -1,66 +0,0 @@ -/* Copyright 2018 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. -==============================================================================*/ - -// See the header for documentation on the meaning of this data. - -#include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.h" - -const int g_no_30ms_sample_data_size = 480; -const int16_t g_no_30ms_sample_data[480] = { - 5713, 5735, 5735, 5737, 5701, 5691, 5656, 5633, 5611, 5552, 5475, - 5394, 5293, 5177, 5064, 4924, 4737, 4599, 4420, 4237, 4048, 3828, - 3623, 3413, 3183, 2915, 2622, 2308, 1980, 1657, 1261, 901, 549, - 205, -85, -383, -688, -969, -1246, -1530, -1850, -2206, -2561, -2915, - -3224, -3482, -3713, -3921, -4107, -4287, -4470, -4660, -4850, -5057, -5239, - -5395, -5540, -5619, -5697, -5724, -5697, -5675, -5633, -5590, -5579, -5530, - -5486, -5442, -5426, -5391, -5348, -5276, -5197, -5124, -5039, -4925, -4808, - -4677, -4581, -4479, -4343, -4218, -4087, -3970, -3858, -3729, -3570, -3384, - -3206, -3020, -2839, -2636, -2453, -2287, -2185, -2154, -1926, -1562, -1223, - -758, -473, -64, 395, 599, 880, 814, 938, 1172, 1498, 1928, - 2127, 2422, 2608, 2841, 2937, 2886, 2815, 2985, 3324, 3757, 4152, - 4481, 4652, 4917, 4965, 4766, 4583, 4328, 4503, 4815, 5118, 5408, - 5682, 5956, 6082, 6055, 5744, 5426, 5341, 5427, 5606, 5882, 6065, - 6226, 6428, 6477, 6385, 6009, 5728, 5552, 5439, 5339, 5200, 5008, - 4947, 4835, 4614, 4330, 3887, 3521, 3111, 2460, 1983, 1297, 650, - 279, -353, -720, -1044, -1518, -1668, -2117, -2496, -2743, -3266, -3607, - -3790, -4149, -4075, -4042, -4096, -3981, -4138, -4226, -4214, -4503, -4455, - -4577, -4642, -4346, -4351, -4270, -4263, -4522, -4521, -4673, -4814, -4731, - -4950, -5011, -5004, -5288, -5341, -5566, -5833, -5783, -5929, -5847, -5765, - -5828, -5644, -5613, -5615, -5428, -5291, -5014, -4554, -4277, -3964, -3854, - -3829, -3612, -3603, -3438, -3137, -2831, -2164, -1438, -939, -330, -156, - 46, 242, 73, 242, 220, 239, 542, 565, 739, 872, 801, - 857, 676, 543, 586, 567, 828, 1142, 1490, 1985, 2508, 2982, - 3438, 3699, 3939, 4069, 4178, 4420, 4622, 4917, 5338, 5801, 6285, - 6658, 6963, 7213, 7233, 7328, 7176, 7038, 7031, 6860, 6957, 6767, - 6599, 6523, 6212, 6147, 6063, 5860, 6020, 6015, 6033, 6184, 5722, - 5607, 5016, 4337, 4063, 3229, 3080, 3006, 2804, 3035, 2541, 2136, - 1879, 1012, 401, -575, -1584, -1930, -2278, -2485, -2477, -2712, -2747, - -2766, -3320, -3592, -4188, -4669, -4672, -4939, -4789, -4426, -4203, -3674, - -3563, -3656, -3759, -4067, -4257, -4522, -4970, -5204, -5237, -5139, -4907, - -4911, -4917, -4921, -5007, -5230, -5654, -6122, -6464, -6733, -6948, -7067, - -6972, -6800, -6520, -6132, -5830, -5382, -5091, -4797, -4546, -4472, -4362, - -4350, -4235, -3851, -3454, -3144, -2735, -2341, -1845, -1262, -958, -549, - -166, 66, 382, 366, 352, 341, 85, -13, -176, -303, -235, - -341, -309, -227, -249, -50, 143, 384, 874, 1149, 1552, 2155, - 2767, 3499, 3994, 4460, 4920, 5288, 5569, 5704, 5881, 6094, 6461, - 6653, 6803, 7115, 7311, 7521, 7612, 7443, 7380, 7124, 6742, 6495, - 5964, 5656, 5415, 5167, 5656, 5813, 6027, 6401, 6351, 6787, 7019, - 6581, 6512, 5965, 5308, 5140, 4336, 4147, 3899, 3398, 3360, 2830, - 2624, 1968, 1026, 395, -699, -1424, -2327, -3006, -3192, -3435, -3337, - -3686, -3513, -3350, -3502, -3261, -3878, -4005, -4063, -4187, -3767, -3598, - -3384, -3300, -3094, -2857, -3023, -3274, -3851, -4352, -4523, -4943, -5477, - -5612, -5682, -5733, -5714, -5965, -6110, -5950, -6158, -6548, -6897, -7165, - -7281, -7352, -7258, -7185, -6659, -5946, -5470, -}; diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.h b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.h deleted file mode 100644 index 5299930659..0000000000 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.h +++ /dev/null @@ -1,32 +0,0 @@ -/* Copyright 2018 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. -==============================================================================*/ - -// This data was created from the PCM data in a WAV file held in v2 of the -// Speech Commands test dataset, at the path: -// speech_commands_test_set_v0.02/no/f9643d42_nohash_4.wav -// The data was extracted starting at an offset of 8,960, which corresponds to -// the 29th spectrogram slice. It's designed to be used to test the -// preprocessing pipeline, to ensure that the expected spectrogram slice is -// produced given this input. - -#ifndef TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_NO_30MS_SAMPLE_DATA_H_ -#define TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_NO_30MS_SAMPLE_DATA_H_ - -#include - -extern const int g_no_30ms_sample_data_size; -extern const int16_t g_no_30ms_sample_data[]; - -#endif // TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_NO_30MS_SAMPLE_DATA_H_ diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor_test.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor_test.cc index 3560a17cd4..5986fb4913 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor_test.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor_test.cc @@ -15,9 +15,9 @@ limitations under the License. #include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.h" #include "tensorflow/lite/c/c_api_internal.h" -#include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/no_30ms_sample_data.h" #include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.h" -#include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/yes_30ms_sample_data.h" #include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.h" #include "tensorflow/lite/experimental/micro/micro_error_reporter.h" #include "tensorflow/lite/experimental/micro/testing/micro_test.h" diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.cc deleted file mode 100644 index 056ee544da..0000000000 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.cc +++ /dev/null @@ -1,70 +0,0 @@ -/* Copyright 2018 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. -==============================================================================*/ - -// See the header for documentation on the meaning of this data. - -#include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.h" - -const int g_yes_30ms_sample_data_size = 480; -const int16_t g_yes_30ms_sample_data[480] = { - -876, -470, 510, 803, 170, -787, -1568, -1893, -1598, -1027, - -992, -1803, -2610, -2484, -1905, -2113, -3113, -3399, -2267, -1261, - -2007, -3637, -3909, -2340, -893, -1158, -2272, -2486, -1639, -915, - -777, -596, -91, 196, 85, 210, 875, 1373, 1247, 1219, - 1958, 2718, 2328, 1196, 1008, 2350, 3677, 3269, 1503, 366, - 922, 2264, 2810, 1996, 608, -168, 75, 680, 811, 395, - -56, -318, -607, -966, -1108, -925, -613, -368, -369, -919, - -1926, -2460, -1685, -300, 155, -611, -1524, -2204, -3227, -3859, - -2037, 1622, 2382, -2583, -8448, -7544, -84, 4814, 915, -6423, - -7558, -1746, 2515, -59, -4587, -3858, 1260, 3625, 187, -4148, - -3500, 1542, 5467, 4780, 1256, -1127, -403, 2481, 5332, 6346, - 5014, 2536, 1216, 2467, 5039, 6238, 5070, 3381, 3269, 4173, - 3905, 2248, 1586, 3299, 5240, 4362, 1004, -1382, -489, 2113, - 3168, 1620, -742, -1824, -1435, -897, -1058, -1500, -1545, -1398, - -1965, -3266, -4136, -3756, -2609, -1804, -1986, -3087, -4599, -5296, - -4051, -1731, -781, -2228, -4092, -3977, -2325, -1353, -1568, -1490, - -428, 178, -672, -1650, -1058, 749, 2039, 2079, 1540, 897, - 310, 572, 2266, 4265, 4265, 1869, -231, 559, 3332, 4752, - 3229, 768, 101, 1364, 2463, 1984, 819, 411, 723, 675, - -162, -923, -743, -32, 185, -516, -1653, -2359, -2103, -986, - 42, -205, -1702, -2870, -2337, -809, -221, -982, -1544, -946, - -598, -2117, -4291, -4100, -857, 1948, 338, -4799, -7972, -5403, - 173, 2371, -1063, -5533, -5578, -1777, 605, -985, -3249, -2213, - 1184, 2691, 560, -2356, -2288, 1233, 5244, 6441, 4004, 370, - -663, 2555, 7404, 9282, 6573, 2612, 1836, 4662, 7467, 7393, - 5421, 4262, 4741, 5362, 4705, 3163, 2397, 3337, 4887, 4810, - 2254, -749, -1316, 772, 2706, 2016, -573, -2552, -2746, -2012, - -1647, -1978, -2579, -3105, -3473, -3911, -4484, -4891, -4795, -4163, - -3543, -3538, -4275, -5356, -5743, -4637, -2614, -1301, -1825, -3341, - -4011, -2937, -751, 1007, 1245, 235, -639, -61, 1626, 2864, - 2967, 2734, 3013, 3329, 2914, 2312, 2666, 3839, 4308, 3162, - 1453, 768, 1255, 1887, 2006, 1715, 1031, -297, -1660, -1690, - -277, 813, -30, -2137, -3370, -2854, -1553, -593, -413, -1146, - -2567, -3440, -2369, -205, 379, -1258, -2315, -812, 262, -3205, - -8576, -7894, 738, 7492, 1951, -11595, -17098, -6934, 7139, 8065, - -4575, -14199, -8946, 3606, 7504, -547, -8242, -5113, 4406, 8113, - 2134, -5040, -4089, 4157, 10934, 10158, 4167, -565, -192, 4428, - 9765, 12201, 9861, 4512, 1225, 3451, 8483, 10133, 6497, 2574, - 3333, 6806, 6986, 2487, -1214, 623, 5416, 6647, 2204, -3289, - -4556, -1565, 1544, 1525, -1236, -4293, -5695, -5174, -3995, -3403, - -3449, -3750, -4505, -6014, -7296, -6523, -3849, -2096, -3288, -5722, - -6004, -3581, -1497, -1960, -3330, -2800, -434, 964, -111, -1739, - -1136, 1736, 4151, 3736, 1274, -451, 469, 3386, 5833, 5898, - 3646, 1085, 272, 1743, 4061, 5108, 3837, 1490, 246, 967, - 1866, 859, -1069, -974, 1542, 2835, 47, -4285, -5068, -1567, - 1781, 1223, -1997, -4227, -3747, -1720, 41, 245, -1228, -2972, - -2673, 22, 1980, -930, -7721, -11271, -5725, 4974, 8484, -2007, - -16979, -19255, -4670, 11057, 9690, -6417, -17537, -10841, 4262, 9292, -}; diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.h b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.h deleted file mode 100644 index cb757e84ba..0000000000 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.h +++ /dev/null @@ -1,32 +0,0 @@ -/* Copyright 2018 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. -==============================================================================*/ - -// This data was created from the PCM data in a WAV file held in v2 of the -// Speech Commands test dataset, at the path: -// speech_commands_test_set_v0.02/yes/f2e59fea_nohash_1.wav -// The data was extracted starting at an offset of 8,000, which corresponds to -// the 26th spectrogram slice. It's designed to be used to test the -// preprocessing pipeline, to ensure that the expected spectrogram slice is -// produced given this input. - -#ifndef TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_YES_30MS_SAMPLE_DATA_H_ -#define TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_YES_30MS_SAMPLE_DATA_H_ - -#include - -extern const int g_yes_30ms_sample_data_size; -extern const int16_t g_yes_30ms_sample_data[]; - -#endif // TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_YES_30MS_SAMPLE_DATA_H_ diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/cmsis-dsp_makefile.inc b/tensorflow/lite/experimental/micro/tools/make/targets/cmsis-dsp_makefile.inc index fecb98de7c..54b3393229 100644 --- a/tensorflow/lite/experimental/micro/tools/make/targets/cmsis-dsp_makefile.inc +++ b/tensorflow/lite/experimental/micro/tools/make/targets/cmsis-dsp_makefile.inc @@ -1,9 +1,9 @@ ifeq ($(VENDORLIB), cmsis-dsp) PREPROCESSOR_TEST_SRCS := \ +tensorflow/lite/experimental/micro/examples/micro_speech/no_30ms_sample_data.cc \ +tensorflow/lite/experimental/micro/examples/micro_speech/yes_30ms_sample_data.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor_test.cc \ -tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_30ms_sample_data.cc \ -tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_30ms_sample_data.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.cc endif -- GitLab From 9bf90b543f6a00d0f8eb1ab980bb4cbca9036491 Mon Sep 17 00:00:00 2001 From: Steve Nesae Date: Mon, 5 Nov 2018 17:38:12 -0600 Subject: [PATCH 020/622] removed unnecessary debug command in .inc --- .../micro/tools/make/targets/apollo3evb_makefile.inc | 2 -- 1 file changed, 2 deletions(-) diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc index 0116670615..0674f48c6d 100644 --- a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc +++ b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc @@ -73,8 +73,6 @@ ifeq ($(TARGET), apollo3evb) -I$(APOLLO3_SDK)/devices/ \ -I$(APOLLO3_SDK)/utils/ -$(warning INCLUDES IS $(INCLUDES)) - # The startup_gcc.c file is an altered version of the examples/hello_world/gcc/startup_gcc.c # file from Ambiq: # - Increase the stack size from 1k to 20k -- GitLab From 38a3d5ea22ad41eeece92624951132b79357b5a0 Mon Sep 17 00:00:00 2001 From: Steve Nesae Date: Mon, 5 Nov 2018 17:42:33 -0600 Subject: [PATCH 021/622] removed contrib paths in makefile --- .../micro/tools/make/targets/apollo3evb_makefile.inc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc index 0674f48c6d..153a19fcd4 100644 --- a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc +++ b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc @@ -29,7 +29,7 @@ ifeq ($(TARGET), apollo3evb) -mcpu=cortex-m4 \ -mthumb \ -mfpu=fpv4-sp-d16 \ - -mfloat-abi=softfp\ + -mfloat-abi=softfp \ -std=gnu++11 \ -Wvla \ -Wall \ @@ -47,7 +47,7 @@ ifeq ($(TARGET), apollo3evb) CXXFLAGS += $(PLATFORM_FLAGS) CCFLAGS += $(PLATFORM_FLAGS) LDFLAGS += \ - -mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=softfp\ + -mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=softfp \ -nostartfiles -static \ -Wl,--gc-sections -Wl,--entry,Reset_Handler \ -Wl,--start-group -lm -lc -lgcc -Wl,--end-group \ @@ -89,11 +89,11 @@ ifeq ($(TARGET), apollo3evb) $(MAKEFILE_DIR)/targets/apollo3evb/am_util_id.c \ $(MAKEFILE_DIR)/targets/apollo3evb/am_util_stdio.c - TEST_SCRIPT := tensorflow/contrib/lite/experimental/log_test/test_apollo3evb_binary.sh + TEST_SCRIPT := tensorflow/lite/experimental/log_test/test_apollo3evb_binary.sh # These are tests that don't currently work on the blue pill. EXCLUDED_TESTS := \ - tensorflow/contrib/lite/experimental/micro/micro_interpreter_test.cc \ - tensorflow/contrib/lite/experimental/micro/simple_tensor_allocator_test.cc + tensorflow/lite/experimental/micro/micro_interpreter_test.cc \ + tensorflow/lite/experimental/micro/simple_tensor_allocator_test.cc MICROLITE_TEST_SRCS := $(filter-out $(EXCLUDED_TESTS), $(MICROLITE_TEST_SRCS)) endif -- GitLab From 42321707242771cf28deb1d577dfdd6a17e9eae9 Mon Sep 17 00:00:00 2001 From: Guozhong Zhuang Date: Mon, 12 Nov 2018 15:24:22 -0800 Subject: [PATCH 022/622] fix issues related to clang format check --- .../core/kernels/mkl_fused_batch_norm_op.cc | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/tensorflow/core/kernels/mkl_fused_batch_norm_op.cc b/tensorflow/core/kernels/mkl_fused_batch_norm_op.cc index ff46e75a36..685db657e2 100644 --- a/tensorflow/core/kernels/mkl_fused_batch_norm_op.cc +++ b/tensorflow/core/kernels/mkl_fused_batch_norm_op.cc @@ -13,15 +13,14 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ #ifdef INTEL_MKL - -#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor" +#include "mkldnn.hpp" #include "tensorflow/core/framework/op_kernel.h" #include "tensorflow/core/framework/register_types.h" #include "tensorflow/core/framework/tensor.h" #include "tensorflow/core/framework/tensor_types.h" #include "tensorflow/core/util/mkl_util.h" #include "tensorflow/core/util/tensor_format.h" -#include "mkldnn.hpp" +#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor" using mkldnn::batch_normalization_backward; using mkldnn::batch_normalization_forward; @@ -705,9 +704,9 @@ class MklFusedBatchNormOp : public OpKernel { std::memcpy(batch_variance_data, variance_data, depth_ * sizeof(T)); } } catch (mkldnn::error& e) { - string error_msg = "Status: " + std::to_string(e.status) + - ", message: " + string(e.message) + ", in file " + - string(__FILE__) + ":" + std::to_string(__LINE__); + string error_msg = "Status: " + std::to_string(e.status) + ", message: " + + string(e.message) + ", in file " + string(__FILE__) + + ":" + std::to_string(__LINE__); OP_REQUIRES_OK( context, errors::Aborted("Operation received an exception:", error_msg)); @@ -1029,9 +1028,9 @@ class MklFusedBatchNormGradOp : public OpKernel { reinterpret_cast(diff_weights_data + depth_), depth_ * sizeof(T)); } catch (mkldnn::error& e) { - string error_msg = "Status: " + std::to_string(e.status) + - ", message: " + string(e.message) + ", in file " + - string(__FILE__) + ":" + std::to_string(__LINE__); + string error_msg = "Status: " + std::to_string(e.status) + ", message: " + + string(e.message) + ", in file " + string(__FILE__) + + ":" + std::to_string(__LINE__); OP_REQUIRES_OK( context, errors::Aborted("Operation received an exception:", error_msg)); -- GitLab From d45cd461242e3a27e505a67c17d5ddb7f4a84641 Mon Sep 17 00:00:00 2001 From: Yves-Noel Weweler Date: Fri, 30 Nov 2018 14:46:12 +0100 Subject: [PATCH 023/622] Add drop_remainder argument to bucket_by_sequence_length `tf.data.experimental.bucket_by_sequence_length` does not allow to drop the last batch in case it has fewer than `batch_size` elements. This patch does implement `drop_remainder` for `bucket_by_sequence_length` to enable thhis behaviour. `drop_remainder` is optinal and set to `False` by default to maintain compatibility. --- tensorflow/python/data/experimental/ops/grouping.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tensorflow/python/data/experimental/ops/grouping.py b/tensorflow/python/data/experimental/ops/grouping.py index db10ea3b7f..71e4b3391f 100644 --- a/tensorflow/python/data/experimental/ops/grouping.py +++ b/tensorflow/python/data/experimental/ops/grouping.py @@ -130,7 +130,8 @@ def bucket_by_sequence_length(element_length_func, padded_shapes=None, padding_values=None, pad_to_bucket_boundary=False, - no_padding=False): + no_padding=False, + drop_remainder=False): """A transformation that buckets elements in a `Dataset` by length. Elements of the `Dataset` are grouped together by length and then are padded @@ -160,6 +161,10 @@ def bucket_by_sequence_length(element_length_func, any elements with length longer than `max(bucket_boundaries)`. no_padding: `bool`, indicates whether to pad the batch features (features need to be either of type `tf.SparseTensor` or of same shape). + drop_remainder: (Optional.) A `tf.bool` scalar `tf.Tensor`, representing + whether the last batch should be dropped in the case its has fewer than + batch_size` elements; the default behavior is not to drop the smaller + batch. Returns: A `Dataset` transformation function, which can be passed to @@ -209,7 +214,7 @@ def bucket_by_sequence_length(element_length_func, """Batch elements in dataset.""" batch_size = window_size_fn(bucket_id) if no_padding: - return grouped_dataset.batch(batch_size) + return grouped_dataset.batch(batch_size, drop_remainder=drop_remainder) none_filler = None if pad_to_bucket_boundary: err_msg = ("When pad_to_bucket_boundary=True, elements must have " @@ -227,7 +232,8 @@ def bucket_by_sequence_length(element_length_func, shapes = make_padded_shapes( padded_shapes or grouped_dataset.output_shapes, none_filler=none_filler) - return grouped_dataset.padded_batch(batch_size, shapes, padding_values) + return grouped_dataset.padded_batch(batch_size, shapes, padding_values, + drop_remainder=drop_remainder) def _apply_fn(dataset): return dataset.apply( -- GitLab From 916c5238d864e04861a58b3f77b9d29cca03b0e0 Mon Sep 17 00:00:00 2001 From: Yves-Noel Weweler Date: Sun, 2 Dec 2018 18:27:55 +0100 Subject: [PATCH 024/622] Add drop_reminder support to the test of bucketing of sparse tensors --- .../bucket_by_sequence_length_test.py | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py b/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py index af20e50fb9..fb2d1cf63c 100644 --- a/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py @@ -274,11 +274,16 @@ class BucketBySequenceLengthTest(test_base.DatasetTestBase): dataset = dataset.map(_to_sparse_tensor) return dataset - def _compute_expected_batches(): + def _compute_expected_batches(drop_remainder): """Computes expected batch outputs and stores in a set.""" all_expected_sparse_tensors = set() for bucket_start_len in range(min_len, max_len, bucket_size): - for batch_offset in range(0, bucket_size, batch_size): + if drop_remainder: + batch_offsets = [0] + else: + batch_offsets = range(0, bucket_size, batch_size) + + for batch_offset in batch_offsets: batch_start_len = bucket_start_len + batch_offset batch_end_len = min(batch_start_len + batch_size, bucket_start_len + bucket_size) @@ -306,16 +311,18 @@ class BucketBySequenceLengthTest(test_base.DatasetTestBase): all_sparse_tensors.add(sprs_tensor) return all_sparse_tensors - dataset = _build_dataset() - boundaries = range(min_len + bucket_size + 1, max_len, bucket_size) - dataset = dataset.apply(grouping.bucket_by_sequence_length( - _element_length_fn, - boundaries, - [batch_size] * (len(boundaries) + 1), - no_padding=True)) - batches = _compute_batches(dataset) - expected_batches = _compute_expected_batches() - self.assertEqual(batches, expected_batches) + for drop_remainder in (True, False): + dataset = _build_dataset() + boundaries = range(min_len + bucket_size + 1, max_len, bucket_size) + dataset = dataset.apply(grouping.bucket_by_sequence_length( + _element_length_fn, + boundaries, + [batch_size] * (len(boundaries) + 1), + no_padding=True, + drop_remainder=drop_remainder)) + batches = _compute_batches(dataset) + expected_batches = _compute_expected_batches() + self.assertEqual(batches, expected_batches) if __name__ == "__main__": -- GitLab From d372f19f132e8f037390a9a2a6fde7bafa2620b9 Mon Sep 17 00:00:00 2001 From: Yves-Noel Weweler Date: Sun, 2 Dec 2018 18:29:03 +0100 Subject: [PATCH 025/622] Add a drop_reminder version of the testBucket test Squeezing both cases into a single test would make it way to complicated. Therefore, I created a separate test. --- .../bucket_by_sequence_length_test.py | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py b/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py index fb2d1cf63c..7b2e922d55 100644 --- a/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py @@ -71,6 +71,117 @@ def _get_record_shape(sparse): class BucketBySequenceLengthTest(test_base.DatasetTestBase): + def testBucketDropReminder(self): + + boundaries = [10, 20, 30] + batch_sizes = [10, 8, 4, 2] + lengths = [8, 13, 25, 35] + + n_bucket_elements = [28, 7, 6, 5] + n_expected_batches = 5 + + # Expected sequence lengths of the individual batches. + expected_lengths = [] + + # Expected sum of all batches with an equal sequence length. + # : + expected_sums = dict() + + # Expected batch sizes of batches depending on the sequence length. + # : [batch1_size, ..., batchN_size] + expected_batch_sizes = dict() + + for length, batch_size, bucket_elements in zip(lengths, batch_sizes, n_bucket_elements): + # Calculate the expected sum across all batches of a specific sequence length. + expected_sums[length] = (bucket_elements - bucket_elements % batch_size) * length + # Calculate the expected occurrence of individual batch sizes. + expected_batch_sizes[length] = [batch_size] * (bucket_elements // batch_size) + # Calculate the expected occurence of individual sequence lengths. + expected_lengths.extend([length] * (bucket_elements // batch_size)) + + def build_dataset(sparse): + def _generator(): + # Produce 1 batch for each bucket + elements = [] + for bucket_elements, length in zip(n_bucket_elements, lengths): + # Using only full sequences (opposed to the strategy employed in `testBucket`) makes + # checking the sum a lot easier. + record_len = length + for _ in range(bucket_elements): + elements.append([1] * record_len) + random.shuffle(elements) + for el in elements: + yield (_format_record(el, sparse),) + dataset = dataset_ops.Dataset.from_generator( + _generator, + (_get_record_type(sparse),), + (_get_record_shape(sparse),)) + if sparse: + dataset = dataset.map(lambda x: (_to_sparse_tensor(x),)) + return dataset + + def _test_bucket_by_padding(no_padding): + dataset = build_dataset(sparse=no_padding) + dataset = dataset.apply( + grouping.bucket_by_sequence_length( + _element_length_fn, + boundaries, + batch_sizes, + no_padding=no_padding, + drop_remainder=True)) + batch, = dataset.make_one_shot_iterator().get_next() + + with self.cached_session() as sess: + batches = [] + for _ in range(n_expected_batches): + batches.append(self.evaluate(batch)) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(batch) + + generated_lengths = [] + + # : + generated_sums = dict() + + # : [, ...] + generated_batch_sizes = dict() + + for length, batch_size, bucket_elements in zip(lengths, batch_sizes, n_bucket_elements): + # Initialize the sum across all batches. + expected_sums[length] = 0 + # Initialize the individual batch sizes. + expected_batch_sizes[length] = [] + + for batch in batches: + shape = batch.dense_shape if no_padding else batch.shape + length = shape[1] + generated_lengths.append(length) + + batch_size = shape[0] + generated_batch_sizes[length].append(batch_size) + + batch_sum = batch.values.sum() if no_padding else batch.sum() + generated_sums[length] += batch_sum + + for l in lengths: + # Make sure the sum of the batch contents is correct for the individual sequence lengths. + self.assertEqual(generated_sums[l], expected_sums[l], + 'Tensor sums did not match! expected: {}, generated: {}' + .format(expected_sums, generated_sums)) + + # Make sure the individual batch sizes are generated as expected. + self.assertEqual(sorted(generated_batch_sizes[l]), sorted(expected_batch_sizes[l]), + 'Batch-sizes did not match! expected: {}, generated: {}' + .format(sorted(expected_batch_sizes[l]), sorted(generated_batch_sizes[l]))) + + # Make sure the generated sequence lengths appear as often as expected. + self.assertEqual(sorted(generated_lengths), sorted(expected_lengths), + 'The generated sequence lengths did not match! expected: {}, generated: {}' + .format(sorted(expected_lengths), sorted(generated_lengths))) + + for no_padding in (True, False): + _test_bucket_by_padding(no_padding) + def testBucket(self): boundaries = [10, 20, 30] -- GitLab From 68426789549cb9d2edc8726fc5edabf4f221bd9b Mon Sep 17 00:00:00 2001 From: Yves-Noel Weweler Date: Sun, 2 Dec 2018 18:40:12 +0100 Subject: [PATCH 026/622] Fix pylint warnings * Missing argument drop_remainder in the testBucketSparse test. * Insert line breaks --- .../bucket_by_sequence_length_test.py | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py b/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py index 7b2e922d55..5b96101b7c 100644 --- a/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py @@ -91,11 +91,15 @@ class BucketBySequenceLengthTest(test_base.DatasetTestBase): # : [batch1_size, ..., batchN_size] expected_batch_sizes = dict() - for length, batch_size, bucket_elements in zip(lengths, batch_sizes, n_bucket_elements): + for length, batch_size, bucket_elements in zip(lengths, + batch_sizes, + n_bucket_elements): # Calculate the expected sum across all batches of a specific sequence length. - expected_sums[length] = (bucket_elements - bucket_elements % batch_size) * length + expected_sums[length] = \ + (bucket_elements - bucket_elements % batch_size) * length # Calculate the expected occurrence of individual batch sizes. - expected_batch_sizes[length] = [batch_size] * (bucket_elements // batch_size) + expected_batch_sizes[length] = \ + [batch_size] * (bucket_elements // batch_size) # Calculate the expected occurence of individual sequence lengths. expected_lengths.extend([length] * (bucket_elements // batch_size)) @@ -146,11 +150,13 @@ class BucketBySequenceLengthTest(test_base.DatasetTestBase): # : [, ...] generated_batch_sizes = dict() - for length, batch_size, bucket_elements in zip(lengths, batch_sizes, n_bucket_elements): - # Initialize the sum across all batches. - expected_sums[length] = 0 - # Initialize the individual batch sizes. - expected_batch_sizes[length] = [] + for length, batch_size, bucket_elements in zip(lengths, + batch_sizes, + n_bucket_elements): + # Initialize the sum across all batches. + expected_sums[length] = 0 + # Initialize the individual batch sizes. + expected_batch_sizes[length] = [] for batch in batches: shape = batch.dense_shape if no_padding else batch.shape @@ -165,19 +171,26 @@ class BucketBySequenceLengthTest(test_base.DatasetTestBase): for l in lengths: # Make sure the sum of the batch contents is correct for the individual sequence lengths. - self.assertEqual(generated_sums[l], expected_sums[l], - 'Tensor sums did not match! expected: {}, generated: {}' + self.assertEqual(generated_sums[l], + expected_sums[l], + 'Tensor sums did not match! ' + 'expected: {}, generated: {}' .format(expected_sums, generated_sums)) # Make sure the individual batch sizes are generated as expected. - self.assertEqual(sorted(generated_batch_sizes[l]), sorted(expected_batch_sizes[l]), - 'Batch-sizes did not match! expected: {}, generated: {}' - .format(sorted(expected_batch_sizes[l]), sorted(generated_batch_sizes[l]))) + self.assertEqual(sorted(generated_batch_sizes[l]), + sorted(expected_batch_sizes[l]), + 'Batch-sizes did not match! ' + 'expected: {}, generated: {}' + .format(sorted(expected_batch_sizes[l]), + sorted(generated_batch_sizes[l]))) # Make sure the generated sequence lengths appear as often as expected. self.assertEqual(sorted(generated_lengths), sorted(expected_lengths), - 'The generated sequence lengths did not match! expected: {}, generated: {}' - .format(sorted(expected_lengths), sorted(generated_lengths))) + 'The generated sequence lengths did not match! ' + 'expected: {}, generated: {}' + .format(sorted(expected_lengths), + sorted(generated_lengths))) for no_padding in (True, False): _test_bucket_by_padding(no_padding) @@ -432,7 +445,7 @@ class BucketBySequenceLengthTest(test_base.DatasetTestBase): no_padding=True, drop_remainder=drop_remainder)) batches = _compute_batches(dataset) - expected_batches = _compute_expected_batches() + expected_batches = _compute_expected_batches(drop_remainder) self.assertEqual(batches, expected_batches) -- GitLab From 4ddd2ab07fdfea81a53d110fee19bb88c41251a0 Mon Sep 17 00:00:00 2001 From: Yves-Noel Weweler Date: Mon, 3 Dec 2018 13:58:58 +0100 Subject: [PATCH 027/622] Fix copy paste bug Pasted the wrong variable names from the docker test image. --- .../kernel_tests/bucket_by_sequence_length_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py b/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py index 5b96101b7c..fab79619a0 100644 --- a/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py @@ -154,9 +154,9 @@ class BucketBySequenceLengthTest(test_base.DatasetTestBase): batch_sizes, n_bucket_elements): # Initialize the sum across all batches. - expected_sums[length] = 0 + generated_sums[length] = 0 # Initialize the individual batch sizes. - expected_batch_sizes[length] = [] + generated_batch_sizes[length] = [] for batch in batches: shape = batch.dense_shape if no_padding else batch.shape -- GitLab From 4925226ba86fcd62431de2ea7eee9ce8c5ee91e1 Mon Sep 17 00:00:00 2001 From: Jakub Lipinski Date: Wed, 5 Dec 2018 13:30:24 +0100 Subject: [PATCH 028/622] remove hardcoded output size calculate output size based on the output tensor dimensions --- .../examples/ios/camera/CameraExampleViewController.mm | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tensorflow/lite/examples/ios/camera/CameraExampleViewController.mm b/tensorflow/lite/examples/ios/camera/CameraExampleViewController.mm index a3e6e11095..8b192fb5a4 100644 --- a/tensorflow/lite/examples/ios/camera/CameraExampleViewController.mm +++ b/tensorflow/lite/examples/ios/camera/CameraExampleViewController.mm @@ -326,7 +326,13 @@ void ProcessInputWithQuantizedModel( NSLog(@"Time: %.4lf, avg: %.4lf, count: %d", end - start, total_latency / total_count, total_count); - const int output_size = 1000; + // read output size from the output sensor + const int output_tensor_index = interpreter->outputs()[0]; + TfLiteTensor* output_tensor = interpreter->tensor(output_tensor_index); + TfLiteIntArray* output_dims = output_tensor->dims; + assert(output_dims->size == 2); + const int output_size = output_dims->data[1]-output_dims->data[0]; + const int kNumResults = 5; const float kThreshold = 0.1f; -- GitLab From 9caf68cf7b47fb94ac4f574772bf5efdb5785555 Mon Sep 17 00:00:00 2001 From: Steve Nesae Date: Tue, 6 Nov 2018 12:47:02 -0600 Subject: [PATCH 029/622] Ambiq squashed commits --- .../ISSUE_TEMPLATE/40-tflite-op-request.md | 24 + README.md | 6 +- WORKSPACE | 22 +- configure.py | 22 +- tensorflow/BUILD | 38 +- tensorflow/api_template.__init__.py | 12 +- tensorflow/c/BUILD | 62 +- tensorflow/c/c_api_experimental.cc | 130 +- tensorflow/c/c_api_experimental.h | 37 + tensorflow/c/c_api_experimental_test.cc | 134 + tensorflow/c/c_api_function.cc | 22 +- tensorflow/c/eager/BUILD | 2 + tensorflow/c/eager/c_api.cc | 49 +- tensorflow/c/eager/c_api.h | 23 + tensorflow/c/eager/c_api_internal.h | 7 +- tensorflow/c/eager/c_api_test.cc | 80 +- tensorflow/c/eager/c_api_test_util.cc | 13 + tensorflow/c/eager/c_api_test_util.h | 3 + tensorflow/c/eager/tape.h | 23 +- tensorflow/c/kernels.cc | 143 + tensorflow/c/kernels.h | 110 + tensorflow/c/kernels_test.cc | 194 + tensorflow/c/python_api.cc | 13 + tensorflow/c/python_api.h | 8 + tensorflow/cc/BUILD | 1 + tensorflow/cc/saved_model/BUILD | 1 + tensorflow/cc/saved_model/constants.h | 9 +- tensorflow/cc/saved_model/loader.cc | 84 +- tensorflow/cc/saved_model/loader_test.cc | 14 + .../half_plus_two_v2/00000123/assets/foo.txt | 1 + .../half_plus_two_v2/00000123/saved_model.pb | Bin 0 -> 10774 bytes .../variables/variables.data-00000-of-00001 | Bin 0 -> 12 bytes .../00000123/variables/variables.index | Bin 0 -> 151 bytes tensorflow/compiler/aot/codegen.cc | 30 +- tensorflow/compiler/aot/codegen.h | 2 +- tensorflow/compiler/aot/codegen_test.cc | 16 +- tensorflow/compiler/aot/codegen_test_h.golden | 16 +- tensorflow/compiler/aot/codegen_test_o.golden | Bin 712 -> 720 bytes tensorflow/compiler/aot/compile.cc | 20 +- tensorflow/compiler/aot/compile.h | 6 +- .../compiler/aot/tests/tfcompile_test.cc | 10 +- tensorflow/compiler/jit/BUILD | 28 +- tensorflow/compiler/jit/build_xla_ops_pass.cc | 10 +- .../compiler/jit/build_xla_ops_pass_test.cc | 8 +- .../compiler/jit/create_xla_launch_op_test.cc | 6 +- tensorflow/compiler/jit/encapsulate_util.cc | 325 +- tensorflow/compiler/jit/encapsulate_util.h | 97 +- .../compiler/jit/encapsulate_util_test.cc | 67 +- .../jit/encapsulate_xla_computations_pass.cc | 7 +- .../jit/extract_outside_compilation_pass.cc | 16 +- .../extract_outside_compilation_pass_test.cc | 15 +- tensorflow/compiler/jit/flags.cc | 152 + ...k_for_compilation_pass_flags.h => flags.h} | 65 +- .../increase_dynamism_for_auto_jit_pass.cc | 56 +- ...ncrease_dynamism_for_auto_jit_pass_test.cc | 130 +- tensorflow/compiler/jit/kernels/BUILD | 2 +- tensorflow/compiler/jit/kernels/xla_ops.cc | 4 +- tensorflow/compiler/jit/legacy_flags/BUILD | 65 - .../legacy_flags/build_xla_ops_pass_flags.cc | 47 - .../legacy_flags/build_xla_ops_pass_flags.h | 37 - .../mark_for_compilation_pass_flags.cc | 98 - .../jit/legacy_flags/xla_device_flags.cc | 56 - .../jit/legacy_flags/xla_device_flags.h | 47 - .../jit/legacy_flags/xla_ops_common_flags.cc | 52 - .../jit/legacy_flags/xla_ops_common_flags.h | 36 - .../compiler/jit/mark_for_compilation_pass.cc | 53 +- .../jit/mark_for_compilation_pass_test.cc | 76 + .../mark_for_compilation_pass_test_helper.cc | 8 +- tensorflow/compiler/jit/ops/BUILD | 6 + .../jit/ops/xla_ops_grad.py} | 19 +- .../compiler/jit/partially_decluster_pass.cc | 19 +- .../jit/partially_decluster_pass_test.cc | 6 +- tensorflow/compiler/jit/xla_cpu_device.cc | 15 +- tensorflow/compiler/jit/xla_device.cc | 82 +- tensorflow/compiler/jit/xla_device.h | 29 + tensorflow/compiler/jit/xla_gpu_device.cc | 10 +- .../compiler/jit/xla_interpreter_device.cc | 7 +- tensorflow/compiler/tests/BUILD | 22 - tensorflow/compiler/tests/adagrad_da_test.py | 32 +- tensorflow/compiler/tests/adagrad_test.py | 30 +- tensorflow/compiler/tests/adam_test.py | 39 +- tensorflow/compiler/tests/adamax_test.py | 24 +- tensorflow/compiler/tests/addsign_test.py | 8 +- tensorflow/compiler/tests/binary_ops_test.py | 15 + .../compiler/tests/categorical_op_test.py | 68 +- tensorflow/compiler/tests/clustering_test.py | 4 +- tensorflow/compiler/tests/concat_ops_test.py | 32 +- tensorflow/compiler/tests/conv3d_test.py | 6 +- tensorflow/compiler/tests/dense_layer_test.py | 8 +- .../compiler/tests/dynamic_stitch_test.py | 9 + tensorflow/compiler/tests/eager_test.py | 4 +- tensorflow/compiler/tests/fft_test.py | 68 +- tensorflow/compiler/tests/fifo_queue_test.py | 6 +- tensorflow/compiler/tests/ftrl_test.py | 84 +- tensorflow/compiler/tests/function_test.py | 12 +- tensorflow/compiler/tests/jit_test.py | 2 +- tensorflow/compiler/tests/listdiff_op_test.py | 4 +- tensorflow/compiler/tests/lrn_ops_test.py | 4 +- tensorflow/compiler/tests/lstm_test.py | 8 +- tensorflow/compiler/tests/momentum_test.py | 64 +- tensorflow/compiler/tests/placeholder_test.py | 2 +- tensorflow/compiler/tests/powersign_test.py | 8 +- .../compiler/tests/proximal_adagrad_test.py | 40 +- .../tests/proximal_gradient_descent_test.py | 38 +- tensorflow/compiler/tests/qr_op_test.py | 2 +- tensorflow/compiler/tests/random_ops_test.py | 16 +- tensorflow/compiler/tests/randomized_tests.cc | 4 +- tensorflow/compiler/tests/reduce_ops_test.py | 6 + tensorflow/compiler/tests/rmsprop_test.py | 24 +- tensorflow/compiler/tests/scan_ops_test.py | 4 +- .../tests/stateless_random_ops_test.py | 2 +- .../compiler/tests/tensor_array_ops_test.py | 27 +- tensorflow/compiler/tests/unary_ops_test.py | 66 + .../compiler/tests/variable_ops_test.py | 42 +- tensorflow/compiler/tests/xla_device_test.py | 2 +- tensorflow/compiler/tf2xla/BUILD | 16 +- tensorflow/compiler/tf2xla/dump_graph.cc | 77 +- .../compiler/tf2xla/dump_graph_flags.cc | 63 - tensorflow/compiler/tf2xla/dump_graph_flags.h | 48 - .../tf2xla/functionalize_control_flow.cc | 19 + .../tf2xla/functionalize_control_flow.h | 6 + .../tf2xla/functionalize_control_flow_test.cc | 1013 +- tensorflow/compiler/tf2xla/kernels/BUILD | 5 +- tensorflow/compiler/tf2xla/kernels/arg_op.cc | 2 +- .../tf2xla/kernels/batch_matmul_op.cc | 12 +- .../compiler/tf2xla/kernels/batch_norm_op.cc | 15 +- .../compiler/tf2xla/kernels/bias_ops.cc | 4 +- .../compiler/tf2xla/kernels/binary_ops.cc | 19 +- .../compiler/tf2xla/kernels/categorical_op.cc | 68 +- .../tf2xla/kernels/conv_op_helpers.cc | 78 +- .../compiler/tf2xla/kernels/conv_ops.cc | 2 +- tensorflow/compiler/tf2xla/kernels/diag_op.cc | 2 +- .../tf2xla/kernels/dynamic_stitch_op.cc | 25 +- .../kernels/extract_image_patches_op.cc | 1 - .../tf2xla/kernels/fake_quantize_ops.cc | 8 +- tensorflow/compiler/tf2xla/kernels/fft_ops.cc | 28 +- tensorflow/compiler/tf2xla/kernels/if_op.cc | 2 +- .../compiler/tf2xla/kernels/image_ops.cc | 5 +- .../tf2xla/kernels/image_resize_ops.cc | 1 - .../compiler/tf2xla/kernels/index_ops_cpu.cc | 70 +- .../compiler/tf2xla/kernels/l2loss_op.cc | 5 +- tensorflow/compiler/tf2xla/kernels/lrn_ops.cc | 17 +- .../tf2xla/kernels/matrix_band_part_op.cc | 10 +- .../tf2xla/kernels/matrix_set_diag_op.cc | 1 - .../compiler/tf2xla/kernels/permute_op.cc | 3 +- .../compiler/tf2xla/kernels/pooling_ops.cc | 31 +- .../kernels/quantize_and_dequantize_op.cc | 43 +- .../compiler/tf2xla/kernels/random_ops.cc | 1 - .../compiler/tf2xla/kernels/reduction_ops.cc | 21 +- .../compiler/tf2xla/kernels/reduction_ops.h | 15 +- .../tf2xla/kernels/reduction_ops_common.cc | 18 +- .../compiler/tf2xla/kernels/resampler_ops.cc | 83 +- .../compiler/tf2xla/kernels/retval_op.cc | 3 +- .../tf2xla/kernels/reverse_sequence_op.cc | 1 - .../compiler/tf2xla/kernels/scan_ops.cc | 11 +- .../compiler/tf2xla/kernels/sendrecv_ops.cc | 4 +- .../compiler/tf2xla/kernels/sequence_ops.cc | 2 +- .../compiler/tf2xla/kernels/softmax_op.cc | 15 +- .../compiler/tf2xla/kernels/stack_ops.cc | 19 +- .../tf2xla/kernels/stateless_random_ops.cc | 1 - .../tf2xla/kernels/tensor_array_ops.cc | 33 +- tensorflow/compiler/tf2xla/kernels/topk_op.cc | 1 - .../compiler/tf2xla/kernels/training_ops.cc | 3 +- .../compiler/tf2xla/kernels/while_op.cc | 2 +- .../tf2xla/kernels/xla_broadcast_helper_op.cc | 7 +- tensorflow/compiler/tf2xla/lib/BUILD | 49 +- tensorflow/compiler/tf2xla/lib/batch_dot.cc | 115 - tensorflow/compiler/tf2xla/lib/batch_dot.h | 54 - tensorflow/compiler/tf2xla/lib/broadcast.cc | 6 +- tensorflow/compiler/tf2xla/lib/cholesky.cc | 17 +- tensorflow/compiler/tf2xla/lib/qr.cc | 35 +- .../compiler/tf2xla/lib/triangular_solve.cc | 28 +- tensorflow/compiler/tf2xla/lib/util.cc | 126 - tensorflow/compiler/tf2xla/lib/util.h | 34 - tensorflow/compiler/tf2xla/python/BUILD | 10 +- tensorflow/compiler/tf2xla/shape_util.h | 1 + .../tf2xla/xla_compiled_cpu_function.h | 25 +- tensorflow/compiler/tf2xla/xla_compiler.cc | 81 +- tensorflow/compiler/tf2xla/xla_compiler.h | 2 +- .../compiler/tf2xla/xla_compiler_test.cc | 83 +- tensorflow/compiler/tf2xla/xla_context.cc | 35 +- tensorflow/compiler/tf2xla/xla_context.h | 32 +- tensorflow/compiler/tf2xla/xla_helpers.cc | 4 +- tensorflow/compiler/tf2xla/xla_helpers.h | 3 +- .../tf2xla/xla_jit_compiled_cpu_function.cc | 3 +- .../tf2xla/xla_jit_compiled_cpu_function.h | 6 +- .../xla_jit_compiled_cpu_function_test.cc | 12 +- tensorflow/compiler/tf2xla/xla_op_kernel.cc | 55 +- tensorflow/compiler/tf2xla/xla_op_kernel.h | 2 + tensorflow/compiler/tf2xla/xla_op_registry.cc | 5 +- tensorflow/compiler/tf2xla/xla_resource.cc | 35 +- tensorflow/compiler/tf2xla/xla_resource.h | 19 +- tensorflow/compiler/xla/BUILD | 21 + tensorflow/compiler/xla/array2d.h | 2 +- tensorflow/compiler/xla/client/BUILD | 5 +- tensorflow/compiler/xla/client/client.cc | 18 +- .../xla/client/executable_build_options.cc | 68 +- .../xla/client/executable_build_options.h | 50 +- tensorflow/compiler/xla/client/lib/BUILD | 57 +- tensorflow/compiler/xla/client/lib/math.cc | 26 +- tensorflow/compiler/xla/client/lib/math.h | 4 + tensorflow/compiler/xla/client/lib/matrix.cc | 185 + .../xla/client/lib/{numeric.h => matrix.h} | 37 +- .../lib/{numeric_test.cc => matrix_test.cc} | 51 +- tensorflow/compiler/xla/client/lib/numeric.cc | 89 - tensorflow/compiler/xla/client/lib/prng.cc | 1 - tensorflow/compiler/xla/client/lib/slicing.cc | 134 + tensorflow/compiler/xla/client/lib/slicing.h | 48 + .../client/lib/slicing_test.cc} | 48 +- tensorflow/compiler/xla/client/lib/sorting.cc | 13 +- .../compiler/xla/client/lib/sorting_test.cc | 33 + tensorflow/compiler/xla/client/lib/testing.cc | 6 +- .../compiler/xla/client/local_client.cc | 24 + tensorflow/compiler/xla/client/local_client.h | 4 + .../compiler/xla/client/sharding_builder.cc | 4 +- tensorflow/compiler/xla/client/xla_builder.cc | 353 +- tensorflow/compiler/xla/client/xla_builder.h | 320 +- .../compiler/xla/client/xla_builder_test.cc | 13 +- .../compiler/xla/client/xla_computation.cc | 2 +- .../compiler/xla/client/xla_computation.h | 1 + .../compiler/xla/debug_options_flags.cc | 14 +- .../experimental/xla_sharding/xla_sharding.py | 30 +- tensorflow/compiler/xla/g3doc/_book.yaml | 12 +- tensorflow/compiler/xla/g3doc/_index.yaml | 4 +- .../g3doc/images/xla_array_layout_figure1.png | Bin 0 -> 20398 bytes .../g3doc/images/xla_array_layout_figure2.png | Bin 0 -> 7913 bytes tensorflow/compiler/xla/g3doc/jit.md | 4 +- .../compiler/xla/g3doc/layout_with_tiling.md | 159 + .../compiler/xla/g3doc/operation_semantics.md | 55 +- .../xla/g3doc/tutorials/xla_compile.ipynb | 301 +- tensorflow/compiler/xla/index_util.h | 1 + tensorflow/compiler/xla/layout_util.cc | 7 + tensorflow/compiler/xla/layout_util.h | 1 + tensorflow/compiler/xla/literal.cc | 318 +- tensorflow/compiler/xla/literal.h | 2 +- tensorflow/compiler/xla/literal_test.cc | 201 +- .../compiler/xla/parse_flags_from_env.cc | 116 +- .../compiler/xla/parse_flags_from_env.h | 56 +- .../compiler/xla/parse_flags_from_env_test.cc | 20 +- tensorflow/compiler/xla/protobuf_util.cc | 10 - .../xla/python/local_computation_builder.cc | 174 +- .../xla/python/local_computation_builder.h | 12 +- .../xla/python/local_computation_builder.i | 43 +- tensorflow/compiler/xla/python/xla_client.py | 138 +- .../compiler/xla/python_api/xla_shape.py | 7 +- tensorflow/compiler/xla/rpc/BUILD | 1 - tensorflow/compiler/xla/rpc/xla_service.proto | 1 - tensorflow/compiler/xla/service/BUILD | 148 +- .../xla/service/algebraic_simplifier.cc | 360 +- .../xla/service/algebraic_simplifier.h | 79 +- ..._simplifier_proof_distributive_property.py | 82 + .../xla/service/algebraic_simplifier_test.cc | 1176 +- .../compiler/xla/service/ar_crs_combiner.cc | 286 + .../compiler/xla/service/ar_crs_combiner.h | 88 + .../xla/service/ar_crs_combiner_test.cc | 415 + .../xla/service/batchnorm_expander.cc | 65 +- .../xla/service/batchnorm_expander_test.cc | 18 +- .../compiler/xla/service/buffer_assignment.cc | 56 +- .../compiler/xla/service/buffer_assignment.h | 10 +- .../xla/service/buffer_assignment_test.cc | 18 +- .../xla/service/buffer_liveness_test.cc | 28 +- tensorflow/compiler/xla/service/call_graph.cc | 9 + tensorflow/compiler/xla/service/call_graph.h | 4 + .../xla/service/compile_only_service.cc | 8 +- .../compiler/xla/service/computation_placer.h | 2 - .../convolution_feature_group_converter.cc | 253 +- ...onvolution_feature_group_converter_test.cc | 20 +- .../compiler/xla/service/copy_insertion.cc | 2 - .../xla/service/copy_insertion_test.cc | 44 +- tensorflow/compiler/xla/service/cpu/BUILD | 1 + .../xla/service/cpu/compiler_functor.cc | 16 +- .../compiler/xla/service/cpu/cpu_compiler.cc | 27 +- .../xla/service/cpu/cpu_executable.cc | 3 +- .../compiler/xla/service/cpu/cpu_executable.h | 2 +- .../xla/service/cpu/cpu_instruction_fusion.cc | 7 +- .../cpu/cpu_instruction_fusion_test.cc | 50 +- .../service/cpu/cpu_layout_assignment_test.cc | 22 +- .../compiler/xla/service/cpu/cpu_options.cc | 1 - .../compiler/xla/service/cpu/ir_emitter.cc | 52 +- .../compiler/xla/service/cpu/ir_emitter.h | 13 +- .../xla/service/cpu/runtime_key_value_sort.cc | 82 +- .../xla/service/cpu/simple_orc_jit.cc | 12 +- .../cpu/tests/cpu_eigen_dot_operation_test.cc | 2 +- .../cpu/tests/cpu_external_constants_test.cc | 2 +- .../service/cpu/tests/cpu_intrinsic_test.cc | 2 +- .../cpu/tests/cpu_literal_caching_test.cc | 8 +- .../xla/service/cpu/tests/cpu_noalias_test.cc | 2 +- .../compiler/xla/service/cpu/xfeed_manager.h | 1 + .../compiler/xla/service/dfs_hlo_visitor.h | 1 + .../service/dfs_hlo_visitor_with_default.h | 3 + .../xla/service/dynamic_parameter_binding.cc | 138 + .../xla/service/dynamic_parameter_binding.h | 125 + .../service/dynamic_parameter_binding_test.cc | 153 + .../xla/service/elemental_ir_emitter.cc | 71 +- tensorflow/compiler/xla/service/executable.h | 6 +- tensorflow/compiler/xla/service/gpu/BUILD | 1 + .../xla/service/gpu/cudnn_conv_rewriter.cc | 6 +- .../xla/service/gpu/cudnn_conv_runner.cc | 15 +- .../xla/service/gpu/elemental_ir_emitter.cc | 10 + .../compiler/xla/service/gpu/fusion_merger.cc | 2 +- .../xla/service/gpu/gpu_executable.cc | 2 +- .../compiler/xla/service/gpu/gpu_executable.h | 2 +- .../compiler/xla/service/gpu/gpu_fusible.cc | 71 +- .../compiler/xla/service/gpu/gpu_fusible.h | 21 +- .../xla/service/gpu/gpu_fusible_test.cc | 319 +- .../xla/service/gpu/gpu_hlo_schedule.cc | 18 +- .../xla/service/gpu/gpu_hlo_schedule.h | 4 +- .../xla/service/gpu/gpu_hlo_schedule_test.cc | 12 +- .../xla/service/gpu/gpu_layout_assignment.cc | 20 +- .../service/gpu/gpu_layout_assignment_test.cc | 8 +- .../xla/service/gpu/instruction_fusion.cc | 5 +- .../service/gpu/instruction_fusion_test.cc | 29 +- .../compiler/xla/service/gpu/ir_emitter.cc | 15 +- .../compiler/xla/service/gpu/ir_emitter.h | 1 + .../xla/service/gpu/ir_emitter_unnested.cc | 2459 +- .../xla/service/gpu/ir_emitter_unnested.h | 226 +- .../gpu/llvm_gpu_backend/nvptx_backend_lib.cc | 16 +- .../xla/service/gpu/multi_output_fusion.cc | 47 +- .../service/gpu/multi_output_fusion_test.cc | 2 +- .../xla/service/gpu/nvptx_compiler.cc | 16 +- .../xla/service/gpu/stream_assignment_test.cc | 8 +- .../xla/service/gpu/tests/gpu_codegen_test.h | 2 +- .../xla/service/gpu/tests/gpu_copy_test.cc | 2 +- .../xla/service/gpu/tests/gpu_ftz_test.cc | 12 +- .../xla/service/gpu/tests/gpu_index_test.cc | 2 +- .../xla/service/gpu/tests/gpu_ldg_test.cc | 6 +- .../xla/service/gpu/tests/gpu_noalias_test.cc | 2 +- .../xla/service/gpu/thunk_schedule.cc | 4 +- .../compiler/xla/service/gpu/thunk_schedule.h | 2 +- .../xla/service/gpu/while_transformer_test.cc | 2 +- .../xla/service/heap_simulator_test.cc | 6 +- tensorflow/compiler/xla/service/hlo.proto | 48 +- .../compiler/xla/service/hlo_computation.cc | 10 +- .../compiler/xla/service/hlo_computation.h | 8 +- .../xla/service/hlo_computation_test.cc | 34 +- .../xla/service/hlo_constant_folding_test.cc | 33 +- .../compiler/xla/service/hlo_cost_analysis.cc | 15 + .../compiler/xla/service/hlo_cost_analysis.h | 1 + .../xla/service/hlo_cost_analysis_test.cc | 6 +- .../xla/service/hlo_dataflow_analysis.cc | 18 + .../xla/service/hlo_dataflow_analysis.h | 1 + .../xla/service/hlo_dataflow_analysis_test.cc | 26 +- .../compiler/xla/service/hlo_dce_test.cc | 10 +- .../compiler/xla/service/hlo_evaluator.cc | 30 +- .../compiler/xla/service/hlo_evaluator.h | 17 +- .../xla/service/hlo_evaluator_test.cc | 29 + .../xla/service/hlo_evaluator_typed_visitor.h | 57 +- .../hlo_get_dimension_size_rewriter.cc | 61 + .../service/hlo_get_dimension_size_rewriter.h | 36 + .../hlo_get_dimension_size_rewriter_test.cc | 83 + .../compiler/xla/service/hlo_graph_dumper.cc | 141 +- .../compiler/xla/service/hlo_graph_dumper.h | 16 +- .../compiler/xla/service/hlo_instruction.cc | 233 +- .../compiler/xla/service/hlo_instruction.h | 34 +- .../compiler/xla/service/hlo_instructions.cc | 13 +- .../compiler/xla/service/hlo_instructions.h | 5 + tensorflow/compiler/xla/service/hlo_lexer.h | 1 + .../compiler/xla/service/hlo_matchers.cc | 4 - .../compiler/xla/service/hlo_matchers.h | 1 - .../xla/service/hlo_memory_scheduler.cc | 82 +- .../xla/service/hlo_memory_scheduler.h | 14 +- .../xla/service/hlo_memory_scheduler_test.cc | 41 +- tensorflow/compiler/xla/service/hlo_module.cc | 14 +- tensorflow/compiler/xla/service/hlo_module.h | 26 +- .../compiler/xla/service/hlo_module_test.cc | 16 +- tensorflow/compiler/xla/service/hlo_opcode.h | 3 +- .../compiler/xla/service/hlo_ordering.cc | 3 +- .../compiler/xla/service/hlo_ordering_test.cc | 12 +- tensorflow/compiler/xla/service/hlo_parser.cc | 15 +- .../compiler/xla/service/hlo_parser_test.cc | 69 +- .../compiler/xla/service/hlo_proto_util.cc | 9 +- .../compiler/xla/service/hlo_proto_util.h | 5 +- .../xla/service/hlo_rematerialization.cc | 19 +- .../xla/service/hlo_rematerialization.h | 5 +- tensorflow/compiler/xla/service/hlo_runner.cc | 34 + tensorflow/compiler/xla/service/hlo_runner.h | 20 +- .../compiler/xla/service/hlo_schedule.cc | 25 +- .../compiler/xla/service/hlo_schedule.h | 12 +- .../compiler/xla/service/hlo_schedule_test.cc | 14 +- .../xla/service/hlo_sharding_metadata.cc | 2 +- .../hlo_subcomputation_unification_test.cc | 6 +- tensorflow/compiler/xla/service/hlo_value.h | 3 - .../compiler/xla/service/hlo_verifier.cc | 21 +- .../compiler/xla/service/hlo_verifier.h | 1 + .../compiler/xla/service/hlo_verifier_test.cc | 16 +- .../service/indexed_array_analysis_test.cc | 8 +- .../xla/service/instruction_fusion.cc | 19 +- .../xla/service/instruction_fusion_test.cc | 56 +- .../xla/service/interpreter/executable.cc | 2 +- .../xla/service/interpreter/executable.h | 2 +- .../xla/service/interpreter/executor.cc | 11 +- .../xla/service/interpreter/executor.h | 3 +- .../compiler/xla/service/layout_assignment.cc | 1 + .../xla/service/layout_assignment_test.cc | 63 +- .../compiler/xla/service/llvm_ir/ir_array.h | 9 + .../xla/service/llvm_ir/kernel_tiling.cc | 187 +- .../xla/service/llvm_ir/kernel_tiling.h | 168 +- .../compiler/xla/service/llvm_ir/llvm_util.cc | 7 +- .../compiler/xla/service/llvm_ir/sort_util.cc | 85 +- .../compiler/xla/service/llvm_ir/sort_util.h | 5 +- .../compiler/xla/service/local_service.cc | 46 +- .../compiler/xla/service/local_service.h | 5 + .../xla/service/logical_buffer_analysis.cc | 7 + .../xla/service/logical_buffer_analysis.h | 1 + .../compiler/xla/service/pattern_matcher.h | 1352 +- .../xla/service/pattern_matcher_gmock.h | 92 + .../xla/service/pattern_matcher_gmock_test.cc | 76 + .../xla/service/pattern_matcher_test.cc | 530 +- .../reduce_precision_insertion_test.cc | 28 +- tensorflow/compiler/xla/service/service.cc | 69 +- .../compiler/xla/service/shape_inference.cc | 24 +- .../compiler/xla/service/shape_inference.h | 7 - .../xla/service/transpose_folding_test.cc | 10 +- .../xla/service/tuple_points_to_analysis.cc | 7 + .../xla/service/tuple_points_to_analysis.h | 1 + .../service/tuple_points_to_analysis_test.cc | 16 + .../while_loop_invariant_code_motion.cc | 32 + .../while_loop_invariant_code_motion.h | 11 +- .../while_loop_invariant_code_motion_test.cc | 54 + .../xla/service/while_loop_simplifier.cc | 447 +- .../xla/service/while_loop_simplifier_test.cc | 190 +- tensorflow/compiler/xla/shape.cc | 107 + tensorflow/compiler/xla/shape.h | 204 + tensorflow/compiler/xla/shape_test.cc | 149 + tensorflow/compiler/xla/shape_tree.h | 11 +- tensorflow/compiler/xla/shape_tree_test.cc | 4 +- tensorflow/compiler/xla/shape_util.cc | 43 +- tensorflow/compiler/xla/shape_util.h | 31 +- tensorflow/compiler/xla/shape_util_test.cc | 72 +- tensorflow/compiler/xla/tests/BUILD | 54 + .../xla/tests/array_elementwise_ops_test.cc | 50 +- .../xla/tests/broadcast_simple_test.cc | 17 +- .../xla/tests/client_library_test_base.cc | 4 +- .../xla/tests/client_library_test_base.h | 2 +- tensorflow/compiler/xla/tests/client_test.cc | 6 +- tensorflow/compiler/xla/tests/concat_test.cc | 26 + .../compiler/xla/tests/conv_depthwise_test.cc | 234 + .../compiler/xla/tests/convolution_test.cc | 826 +- .../compiler/xla/tests/dot_operation_test.cc | 70 + .../xla/tests/grouped_convolution_test.cc | 245 + .../compiler/xla/tests/hlo_test_base.cc | 10 +- tensorflow/compiler/xla/tests/hlo_test_base.h | 8 +- tensorflow/compiler/xla/tests/iota_test.cc | 21 + tensorflow/compiler/xla/tests/replay_test.cc | 9 +- tensorflow/compiler/xla/tests/reshape_test.cc | 6 +- tensorflow/compiler/xla/tests/scatter_test.cc | 36 + tensorflow/compiler/xla/tests/test_utils.cc | 147 +- .../compiler/xla/tests/test_utils_test.cc | 23 + .../compiler/xla/tests/token_hlo_test.cc | 111 +- .../xla/tests/xla_hlo_profile_test.cc | 6 +- .../compiler/xla/tools/replay_computation.cc | 21 +- tensorflow/compiler/xla/util.h | 20 + tensorflow/compiler/xla/window_util.cc | 11 + tensorflow/compiler/xla/window_util.h | 1 + tensorflow/compiler/xla/xla.proto | 20 +- tensorflow/compiler/xla/xla_data.proto | 38 +- tensorflow/compiler/xrt/BUILD | 6 + .../compiler/xrt/kernels/xrt_compile_ops.cc | 27 +- .../compiler/xrt/kernels/xrt_execute_op.cc | 37 +- .../compiler/xrt/kernels/xrt_state_ops.cc | 13 + .../compiler/xrt/kernels/xrt_state_ops.h | 50 + tensorflow/compiler/xrt/ops/xrt_state_ops.cc | 14 + tensorflow/compiler/xrt/tests/raw_api_test.cc | 203 +- tensorflow/compiler/xrt/xrt.proto | 11 +- tensorflow/compiler/xrt/xrt_state.cc | 14 + tensorflow/compiler/xrt/xrt_state.h | 3 + tensorflow/compiler/xrt/xrt_util.cc | 76 + tensorflow/compiler/xrt/xrt_util.h | 34 + tensorflow/contrib/all_reduce/BUILD | 27 +- .../contrib/all_reduce/python/all_reduce.py | 841 +- .../autograph/examples/benchmarks/BUILD | 36 + .../examples/benchmarks/benchmark_base.py | 62 + .../examples/benchmarks/cartpole_benchmark.py | 492 + .../contrib/batching/python/ops/batch_ops.py | 12 +- .../batching/python/ops/batch_ops_test.py | 1 + .../python/kernel_tests/monte_carlo_test.py | 2 +- .../bayesflow/python/ops/monte_carlo_impl.py | 4 +- tensorflow/contrib/bigtable/README.md | 23 +- .../test_kernels/bigtable_test_client.cc | 33 + .../test_kernels/bigtable_test_client.h | 19 + .../python/kernel_tests/bigtable_ops_test.py | 18 +- .../bigtable/python/ops/bigtable_api.py | 16 +- .../boosted_trees/estimator_batch/BUILD | 1 + .../estimator_batch/custom_export_strategy.py | 9 +- .../dnn_tree_combined_estimator.py | 2 +- .../estimator_batch/estimator.py | 240 +- .../estimator_batch/estimator_test.py | 316 +- .../contrib/boosted_trees/examples/boston.py | 4 +- .../boosted_trees/examples/boston_combined.py | 4 +- .../kernels/split_handler_ops.cc | 11 +- .../batch/categorical_split_handler.py | 2 +- .../batch/categorical_split_handler_test.py | 8 +- .../learner/batch/ordinal_split_handler.py | 12 +- .../batch/ordinal_split_handler_test.py | 13 +- .../python/training/functions/gbdt_batch.py | 11 +- .../boosted_trees/python/utils/losses.py | 44 +- .../contrib/checkpoint/python/containers.py | 4 + tensorflow/contrib/cluster_resolver/BUILD | 162 +- .../contrib/cluster_resolver/__init__.py | 16 +- .../cluster_resolver_initialization_test.py | 53 + .../python/training/__init__.py | 41 +- .../python/training/cluster_resolver.py | 338 +- .../python/training/gce_cluster_resolver.py | 199 +- .../training/kubernetes_cluster_resolver.py | 120 +- .../python/training/slurm_cluster_resolver.py | 185 +- .../training/tfconfig_cluster_resolver.py | 80 +- .../python/training/tpu_cluster_resolver.py | 343 +- tensorflow/contrib/cmake/CMakeLists.txt | 33 +- tensorflow/contrib/cmake/README.md | 162 +- .../contrib/cmake/TensorflowConfig.cmake.in | 16 + .../cmake/TensorflowConfigVersion.cmake.in | 11 + .../contrib/cmake/external/abseil_cpp.cmake | 22 +- tensorflow/contrib/cmake/external/png.cmake | 1 + .../contrib/cmake/modules/FindAbseilCpp.cmake | 6 +- tensorflow/contrib/cmake/tf_c.cmake | 1 + tensorflow/contrib/cmake/tf_core_cpu.cmake | 2 + .../contrib/cmake/tf_core_eager_runtime.cmake | 57 + .../contrib/cmake/tf_core_framework.cmake | 8 +- tensorflow/contrib/cmake/tf_core_ops.cmake | 9 +- tensorflow/contrib/cmake/tf_python.cmake | 41 +- tensorflow/contrib/cmake/tf_shared_lib.cmake | 86 +- tensorflow/contrib/compiler/BUILD | 1 + tensorflow/contrib/compiler/xla.py | 1 + .../constrained_minimization_problem.py | 4 +- tensorflow/contrib/crf/python/ops/crf.py | 10 +- tensorflow/contrib/cudnn_rnn/BUILD | 5 +- .../python/kernel_tests/cudnn_rnn_ops_test.py | 1625 +- .../python/kernel_tests/cudnn_rnn_test.py | 16 +- .../cudnn_rnn/python/layers/cudnn_rnn.py | 3 +- .../cudnn_rnn/python/ops/cudnn_rnn_ops.py | 2 +- .../kernel_tests/assert_element_shape_test.py | 18 +- .../kernel_tests/lmdb_dataset_op_test.py | 3 +- .../kernel_tests/slide_dataset_op_test.py | 34 +- tensorflow/contrib/data/python/ops/BUILD | 3 +- tensorflow/contrib/data/python/ops/readers.py | 14 +- tensorflow/contrib/data/python/ops/sliding.py | 25 +- tensorflow/contrib/distribute/BUILD | 2 +- tensorflow/contrib/distribute/README.md | 6 +- tensorflow/contrib/distribute/__init__.py | 4 +- tensorflow/contrib/distribute/python/BUILD | 250 +- .../python/checkpoint_utils_test.py | 4 +- .../python/collective_all_reduce_strategy.py | 124 +- .../collective_all_reduce_strategy_test.py | 152 +- .../contrib/distribute/python/combinations.py | 42 +- ...r_ops_test.py => cross_device_ops_test.py} | 173 +- ...ils_test.py => cross_device_utils_test.py} | 28 +- .../python/estimator_integration_test.py | 10 +- .../python/estimator_training_test.py | 198 +- .../distribute/python/examples/keras_mnist.py | 13 +- .../python/keras_optimizer_v2_test.py | 102 +- .../contrib/distribute/python/keras_test.py | 666 +- .../distribute/python/metrics_v1_test.py | 13 +- .../distribute/python/minimize_loss_test.py | 73 +- .../distribute/python/mirrored_strategy.py | 788 +- .../python/mirrored_strategy_multigpu_test.py | 1346 +- .../python/mirrored_strategy_test.py | 107 - .../distribute/python/moving_averages_test.py | 3 +- .../python/multi_worker_test_base.py | 102 +- .../distribute/python/one_device_strategy.py | 92 +- .../python/one_device_strategy_test.py | 18 +- .../python/parameter_server_strategy.py | 152 +- .../python/parameter_server_strategy_test.py | 159 +- .../contrib/distribute/python/step_fn.py | 2 +- .../distribute/python/strategy_test_lib.py | 122 +- .../contrib/distribute/python/tpu_strategy.py | 224 +- .../contrib/distribute/python/values_test.py | 380 +- .../python/warm_starting_util_test.py | 4 +- tensorflow/contrib/distributions/BUILD | 2 +- .../normal_conjugate_posteriors_test.py | 2 +- .../python/kernel_tests/wishart_test.py | 5 +- .../python/ops/bijectors/softmax_centered.py | 2 +- .../distributions/python/ops/sample_stats.py | 6 +- tensorflow/contrib/eager/python/datasets.py | 6 - .../contrib/eager/python/datasets_test.py | 13 - tensorflow/contrib/eager/python/evaluator.py | 5 +- .../eager/python/examples/densenet/BUILD | 1 + .../examples/densenet/densenet_graph_test.py | 3 +- .../python/examples/gan/mnist_graph_test.py | 3 +- .../examples/generative_examples/cvae.ipynb | 2 +- .../image_captioning_with_attention.ipynb | 2284 +- .../linear_regression_graph_test.py | 2 +- .../nmt_with_attention.ipynb | 8 +- .../examples/resnet50/resnet50_graph_test.py | 3 +- .../eager/python/examples/revnet/main.py | 13 +- .../examples/rnn_ptb/rnn_ptb_graph_test.py | 5 +- .../contrib/eager/python/metrics_impl.py | 6 +- .../contrib/eager/python/metrics_test.py | 12 - tensorflow/contrib/eager/python/network.py | 4 +- tensorflow/contrib/eager/python/saver.py | 5 + tensorflow/contrib/eager/python/tfe_test.py | 4 +- tensorflow/contrib/estimator/BUILD | 25 - tensorflow/contrib/estimator/__init__.py | 2 - .../python/estimator/dnn_linear_combined.py | 34 - .../factorization/python/ops/kmeans.py | 2 +- .../factorization/python/ops/kmeans_test.py | 2 +- tensorflow/contrib/feature_column/BUILD | 11 +- .../feature_column/sequence_feature_column.py | 16 +- ...equence_feature_column_integration_test.py | 17 +- .../sequence_feature_column_test.py | 62 +- .../sequence_feature_column_v2.py | 20 +- .../sequence_feature_column_v2_test.py | 59 +- tensorflow/contrib/framework/BUILD | 20 +- .../contrib/framework/python/ops/sort_ops.py | 172 +- .../estimator/python/gan_estimator_impl.py | 15 +- .../estimator/python/gan_estimator_test.py | 35 +- .../gan/python/losses/python/losses_impl.py | 15 +- tensorflow/contrib/gan/python/namedtuples.py | 12 +- tensorflow/contrib/gan/python/train.py | 37 +- tensorflow/contrib/gan/python/train_test.py | 84 +- tensorflow/contrib/gdr/gdr_rendezvous_mgr.cc | 8 + .../hadoop/python/kernel_tests/hadoop_test.py | 3 +- .../hadoop/python/ops/hadoop_dataset_ops.py | 11 +- tensorflow/contrib/ignite/README.md | 45 +- .../python/tests/ignite_dataset_test.py | 3 +- .../image/kernels/adjust_hsv_in_yiq_op.cc | 2 +- .../image/python/ops/dense_image_warp.py | 79 +- .../keras/api/keras/layers/__init__.py | 2 +- .../contrib/keras/api/keras/utils/__init__.py | 1 + .../python/kernel_estimators.py | 2 +- .../kinesis/python/ops/kinesis_dataset_ops.py | 11 +- tensorflow/contrib/layers/BUILD | 9 +- .../python/layers/embedding_ops_test.py | 52 +- .../contrib/layers/python/layers/encoders.py | 3 +- .../layers/python/layers/feature_column.py | 3 +- .../python/layers/feature_column_ops_test.py | 2 +- .../python/layers/feature_column_test.py | 2 +- .../contrib/layers/python/layers/layers.py | 5 +- .../layers/python/layers/layers_test.py | 4 +- .../layers/python/layers/regularizers_test.py | 2 +- tensorflow/contrib/learn/BUILD | 6 + .../learn/python/learn/estimators/dnn.py | 8 +- .../learn/estimators/dnn_linear_combined.py | 10 +- .../estimators/dnn_linear_combined_test.py | 2 +- .../learn/python/learn/estimators/dnn_test.py | 2 +- .../estimators/dynamic_rnn_estimator_test.py | 6 +- .../python/learn/estimators/estimator.py | 6 +- .../learn/python/learn/estimators/linear.py | 6 +- .../python/learn/estimators/linear_test.py | 4 +- .../learn/python/learn/learn_io/numpy_io.py | 2 +- .../learn/python/learn/learn_io/pandas_io.py | 2 +- .../python/sdca_estimator_test.py | 2 +- tensorflow/contrib/lookup/lookup_ops_test.py | 5 +- .../contrib/losses/python/losses/loss_ops.py | 61 +- .../contrib/makefile/download_dependencies.sh | 2 +- tensorflow/contrib/makefile/tf_op_files.txt | 2 + .../metrics/python/metrics/classification.py | 2 +- .../python/metrics/classification_test.py | 11 +- .../contrib/metrics/python/ops/metric_ops.py | 54 +- .../python/loss_scale_manager_test.py | 2 +- .../python/loss_scale_optimizer_test.py | 4 +- .../python/layers/core_layers.py | 9 +- .../opt/python/training/lars_optimizer.py | 12 + .../opt/python/training/nadam_optimizer.py | 9 +- .../python/training/nadam_optimizer_test.py | 37 +- tensorflow/contrib/optimizer_v2/BUILD | 3 +- .../contrib/optimizer_v2/optimizer_v2.py | 44 +- tensorflow/contrib/predictor/BUILD | 1 + tensorflow/contrib/quantize/README.md | 4 +- .../contrib/quantize/python/quant_ops.py | 8 +- .../contrib/quantize/python/quantize.py | 4 +- tensorflow/contrib/resampler/BUILD | 32 +- .../resampler/xla/resampler_ops_xla_test.py} | 53 +- .../python/kernel_tests/core_rnn_cell_test.py | 6 +- .../rnn/python/kernel_tests/core_rnn_test.py | 2 +- tensorflow/contrib/rnn/python/ops/gru_ops.py | 4 +- tensorflow/contrib/rnn/python/ops/lstm_ops.py | 5 +- tensorflow/contrib/rnn/python/ops/rnn_cell.py | 14 +- tensorflow/contrib/saved_model/BUILD | 5 +- .../python/saved_model/keras_saved_model.py | 35 +- .../saved_model/keras_saved_model_test.py | 39 +- .../kernel_tests/attention_wrapper_test.py | 4 +- .../contrib/summary/summary_ops_test.py | 30 +- .../tensorboard/db/summary_file_writer.cc | 11 +- .../db/summary_file_writer_test.cc | 18 + tensorflow/contrib/tensorrt/BUILD | 29 + .../contrib/tensorrt/convert/convert_graph.cc | 201 +- .../contrib/tensorrt/convert/convert_graph.h | 10 +- .../tensorrt/convert/convert_graph_test.cc | 43 +- .../contrib/tensorrt/convert/convert_nodes.cc | 1130 +- .../contrib/tensorrt/convert/convert_nodes.h | 81 +- .../tensorrt/convert/convert_nodes_test.cc | 1293 +- .../tensorrt/convert/trt_optimization_pass.cc | 10 + .../tensorrt/convert/trt_optimization_pass.h | 4 +- .../contrib/tensorrt/kernels/trt_engine_op.cc | 27 +- .../contrib/tensorrt/kernels/trt_engine_op.h | 4 + .../contrib/tensorrt/ops/trt_engine_op.cc | 22 +- .../contrib/tensorrt/python/trt_convert.py | 79 +- .../tensorrt/python/trt_convert_test.py | 13 +- .../tensorrt/resources/trt_resources.h | 3 +- .../contrib/tensorrt/segment/segment.cc | 35 +- tensorflow/contrib/tensorrt/test/base_test.py | 77 +- .../tensorrt/test/batch_matmul_test.py | 6 +- .../tensorrt/test/biasadd_matmul_test.py | 34 +- .../binary_tensor_weight_broadcast_test.py | 7 +- .../tensorrt/test/concatenation_test.py | 2 +- .../tensorrt/test/const_broadcast_test.py | 2 +- .../tensorrt/test/memory_alignment_test.py | 2 +- .../multi_connection_neighbor_engine_test.py | 10 +- .../tensorrt/test/neighboring_engine_test.py | 4 +- .../tensorrt/test/quantization_mnist_test.py | 290 + .../tensorrt/test/quantization_test.py | 144 + .../contrib/tensorrt/test/rank_two_test.py | 4 +- .../tensorrt/test/reshape_transpose_test.py | 8 +- .../contrib/tensorrt/test/testdata/checkpoint | 3 + .../model.ckpt-46900.data-00000-of-00001 | Bin 0 -> 686728 bytes .../test/testdata/model.ckpt-46900.index | Bin 0 -> 652 bytes .../test/tf_trt_integration_test_base.py | 112 +- .../contrib/tensorrt/test/unary_test.py | 4 +- .../tensorrt/test/vgg_block_nchw_test.py | 2 +- .../contrib/tensorrt/test/vgg_block_test.py | 2 +- .../timeseries/python/timeseries/BUILD | 2 + .../python/timeseries/estimators.py | 2 +- .../python/timeseries/estimators_test.py | 2 +- .../timeseries/python/timeseries/head_test.py | 2 +- .../python/timeseries/math_utils.py | 14 +- .../timeseries/python/timeseries/model.py | 2 +- .../timeseries/state_space_models/BUILD | 5 +- tensorflow/contrib/tpu/BUILD | 5 +- .../pip_package/cloud_tpu_profiler/main.py | 6 +- .../tpu/python/tpu/async_checkpoint.py | 7 +- tensorflow/contrib/tpu/python/tpu/datasets.py | 4 +- .../contrib/tpu/python/tpu/datasets_test.py | 10 +- .../contrib/tpu/python/tpu/keras_support.py | 54 +- .../tpu/python/tpu/keras_tpu_variables.py | 4 + tensorflow/contrib/tpu/python/tpu/tpu.py | 6 +- .../contrib/tpu/python/tpu/tpu_context.py | 2 +- .../contrib/tpu/python/tpu/tpu_embedding.py | 11 +- .../contrib/tpu/python/tpu/tpu_estimator.py | 17 +- .../python/tpu/tpu_estimator_signals_test.py | 2 +- tensorflow/contrib/tpu/python/tpu/tpu_feed.py | 69 +- .../contrib/tpu/python/tpu/training_loop.py | 4 +- tensorflow/contrib/tpu/tpu_estimator.md | 9 +- tensorflow/contrib/training/BUILD | 23 - tensorflow/contrib/training/__init__.py | 5 +- .../python/training/tensor_queue_dataset.py | 201 - .../training/tensor_queue_dataset_test.py | 355 - tensorflow/contrib/verbs/rdma.cc | 6 +- tensorflow/core/BUILD | 108 +- tensorflow/core/api_def/api_test.cc | 13 +- .../base_api/api_def_BatchDataset.pbtxt | 1 + .../base_api/api_def_CacheDataset.pbtxt | 1 + .../base_api/api_def_ConcatenateDataset.pbtxt | 1 + .../api_def_DatasetToSingleElement.pbtxt | 1 + .../api_def_EnqueueInQueueDataset.pbtxt | 3 - ...perimentalBytesProducedStatsDataset.pbtxt} | 3 +- ...i_def_ExperimentalDatasetCardinality.pbtxt | 21 + ...i_def_ExperimentalDatasetToTFRecord.pbtxt} | 2 +- ...perimentalDenseToSparseBatchDataset.pbtxt} | 3 +- ...xperimentalFunctionBufferingResource.pbtxt | 58 - ...ntalFunctionBufferingResourceGetNext.pbtxt | 25 - ...mentalFunctionBufferingResourceReset.pbtxt | 13 - ...f_ExperimentalGroupByReducerDataset.pbtxt} | 2 +- ...ef_ExperimentalGroupByWindowDataset.pbtxt} | 3 +- ...def_ExperimentalLatencyStatsDataset.pbtxt} | 3 +- ..._def_ExperimentalMapAndBatchDataset.pbtxt} | 2 +- ...def_ExperimentalMatchingFilesDataset.pbtxt | 4 + ...rimentalMaxIntraOpParallelismDataset.pbtxt | 13 + ...perimentalParallelInterleaveDataset.pbtxt} | 3 +- ...def_ExperimentalParseExampleDataset.pbtxt} | 3 +- ...ExperimentalPrivateThreadPoolDataset.pbtxt | 13 + ...> api_def_ExperimentalRandomDataset.pbtxt} | 3 +- ... => api_def_ExperimentalScanDataset.pbtxt} | 3 +- ...xperimentalSetStatsAggregatorDataset.pbtxt | 4 + ...ef_ExperimentalSlidingWindowDataset.pbtxt} | 3 +- ...t => api_def_ExperimentalSqlDataset.pbtxt} | 3 +- ...ef_ExperimentalStatsAggregatorHandle.pbtxt | 5 + ..._ExperimentalStatsAggregatorSummary.pbtxt} | 3 +- ... api_def_ExperimentalUnbatchDataset.pbtxt} | 3 +- .../core/api_def/base_api/api_def_FFT.pbtxt | 4 +- .../core/api_def/base_api/api_def_FFT2D.pbtxt | 4 +- .../base_api/api_def_FilterDataset.pbtxt | 1 + .../api_def_FixedLengthRecordDataset.pbtxt | 1 + .../api_def_FixedLengthRecordDatasetV2.pbtxt | 1 + .../base_api/api_def_FlatMapDataset.pbtxt | 1 + .../base_api/api_def_GeneratorDataset.pbtxt | 1 + .../core/api_def/base_api/api_def_IFFT.pbtxt | 4 +- .../api_def/base_api/api_def_IFFT2D.pbtxt | 4 +- .../base_api/api_def_InterleaveDataset.pbtxt | 1 + .../base_api/api_def_MapAndBatchDataset.pbtxt | 53 - .../api_def/base_api/api_def_MapDataset.pbtxt | 1 + .../api_def_MatchingFilesDataset.pbtxt | 4 - .../base_api/api_def_PaddedBatchDataset.pbtxt | 1 + .../base_api/api_def_ParallelMapDataset.pbtxt | 1 + .../base_api/api_def_PrefetchDataset.pbtxt | 1 + ...rependFromQueueAndPaddedBatchDataset.pbtxt | 3 - .../api_def_QuantizeAndDequantizeV2.pbtxt | 15 +- .../base_api/api_def_RangeDataset.pbtxt | 1 + .../base_api/api_def_RepeatDataset.pbtxt | 1 + ...api_def_ResourceApplyAdamWithAmsgrad.pbtxt | 85 + .../api_def_ResourceApplyKerasMomentum.pbtxt | 56 + ...def_ResourceSparseApplyKerasMomentum.pbtxt | 64 + .../api_def/base_api/api_def_ScatterNd.pbtxt | 4 + .../api_def_SetStatsAggregatorDataset.pbtxt | 3 - .../api_def_ShuffleAndRepeatDataset.pbtxt | 1 + .../base_api/api_def_ShuffleDataset.pbtxt | 1 + .../base_api/api_def_SkipDataset.pbtxt | 1 + .../api_def_SparseTensorSliceDataset.pbtxt | 1 + .../api_def_StatsAggregatorHandle.pbtxt | 4 - .../base_api/api_def_TFRecordDataset.pbtxt | 1 + .../base_api/api_def_TakeDataset.pbtxt | 1 + .../base_api/api_def_TensorDataset.pbtxt | 1 + ...i_def_TensorForestCreateTreeVariable.pbtxt | 17 + .../api_def_TensorForestTreeDeserialize.pbtxt | 17 + ..._def_TensorForestTreeIsInitializedOp.pbtxt | 17 + .../api_def_TensorForestTreePredict.pbtxt | 29 + ...def_TensorForestTreeResourceHandleOp.pbtxt | 5 + .../api_def_TensorForestTreeSerialize.pbtxt | 17 + .../api_def_TensorForestTreeSize.pbtxt | 17 + .../base_api/api_def_TensorListConcat.pbtxt | 12 + .../base_api/api_def_TensorListSplit.pbtxt | 13 + .../base_api/api_def_TensorScatterAdd.pbtxt | 94 + .../base_api/api_def_TensorScatterSub.pbtxt | 94 + .../api_def_TensorScatterUpdate.pbtxt | 106 + .../base_api/api_def_TensorSliceDataset.pbtxt | 1 + .../base_api/api_def_TextLineDataset.pbtxt | 1 + .../api_def_UnicodeDecodeWithOffsets.pbtxt | 87 + .../base_api/api_def_UnicodeEncode.pbtxt | 73 + .../api_def/base_api/api_def_ZipDataset.pbtxt | 1 + .../python_api/api_def_BatchDataset.pbtxt | 4 - .../python_api/api_def_BatchToSpaceND.pbtxt | 2 + .../python_api/api_def_BesselI0e.pbtxt | 4 +- .../python_api/api_def_BesselI1e.pbtxt | 4 +- .../api_def_BytesProducedStatsDataset.pbtxt | 4 - .../python_api/api_def_CacheDataset.pbtxt | 4 - .../python_api/api_def_CheckNumerics.pbtxt | 2 + .../api_def_ConcatenateDataset.pbtxt | 4 - .../api_def/python_api/api_def_Conv2D.pbtxt | 4 +- .../api_def_Conv2DBackpropFilter.pbtxt | 4 +- .../api_def_Conv2DBackpropInput.pbtxt | 4 +- .../api_def/python_api/api_def_Conv3D.pbtxt | 4 +- .../api_def_Conv3DBackpropFilterV2.pbtxt | 4 + .../python_api/api_def_CropAndResize.pbtxt | 4 +- .../api_def_DatasetToSingleElement.pbtxt | 4 - .../api_def_DecodeAndCropJpeg.pbtxt | 4 +- .../python_api/api_def_DecodeBmp.pbtxt | 4 +- .../python_api/api_def_DecodeGif.pbtxt | 4 +- .../python_api/api_def_DecodeJpeg.pbtxt | 4 +- .../python_api/api_def_DecodePng.pbtxt | 4 +- .../api_def_DenseToSparseBatchDataset.pbtxt | 4 - .../api_def_DepthwiseConv2dNative.pbtxt | 2 + ..._DepthwiseConv2dNativeBackpropFilter.pbtxt | 5 + ...f_DepthwiseConv2dNativeBackpropInput.pbtxt | 5 + .../python_api/api_def_Dilation2D.pbtxt | 1 + .../python_api/api_def_EncodeJpeg.pbtxt | 4 +- .../api_def_EnqueueInQueueDataset.pbtxt | 4 - .../core/api_def/python_api/api_def_Erf.pbtxt | 8 +- .../api_def_ExtractImagePatches.pbtxt | 8 +- .../python_api/api_def_ExtractJpegShape.pbtxt | 4 +- .../python_api/api_def_FilterDataset.pbtxt | 4 - .../api_def_FixedLengthRecordDataset.pbtxt | 4 - .../api_def_FixedLengthRecordDatasetV2.pbtxt | 4 - .../python_api/api_def_FlatMapDataset.pbtxt | 4 - .../api_def_FractionalAvgPool.pbtxt | 4 +- .../api_def_FractionalMaxPool.pbtxt | 4 +- .../python_api/api_def_GeneratorDataset.pbtxt | 4 - .../api_def_GroupByWindowDataset.pbtxt | 4 - .../api_def_InterleaveDataset.pbtxt | 4 - .../api_def/python_api/api_def_IsFinite.pbtxt | 4 + .../api_def/python_api/api_def_IsInf.pbtxt | 4 + .../api_def/python_api/api_def_IsNan.pbtxt | 4 + .../api_def_LatencyStatsDataset.pbtxt | 4 - .../api_def/python_api/api_def_LinSpace.pbtxt | 1 + .../core/api_def/python_api/api_def_Log.pbtxt | 1 + .../api_def/python_api/api_def_Log1p.pbtxt | 1 + .../api_def_MapAndBatchDataset.pbtxt | 4 - .../python_api/api_def_MapDataset.pbtxt | 4 - .../api_def_MaxPoolWithArgmax.pbtxt | 1 + .../core/api_def/python_api/api_def_Neg.pbtxt | 7 +- .../api_def_PaddedBatchDataset.pbtxt | 4 - .../api_def_ParallelInterleaveDataset.pbtxt | 4 - .../api_def_ParallelMapDataset.pbtxt | 4 - .../api_def_ParseExampleDataset.pbtxt | 4 - .../python_api/api_def_PrefetchDataset.pbtxt | 4 - ...rependFromQueueAndPaddedBatchDataset.pbtxt | 4 - .../python_api/api_def_QuantizedAvgPool.pbtxt | 2 + .../python_api/api_def_QuantizedConv2D.pbtxt | 2 + .../python_api/api_def_QuantizedMaxPool.pbtxt | 2 + .../python_api/api_def_QuantizedReluX.pbtxt | 2 + .../python_api/api_def_RandomDataset.pbtxt | 4 - .../python_api/api_def_RangeDataset.pbtxt | 4 - .../python_api/api_def_RepeatDataset.pbtxt | 4 - .../python_api/api_def_ResizeArea.pbtxt | 4 +- .../python_api/api_def_ResizeBicubic.pbtxt | 4 +- .../python_api/api_def_ResizeBilinear.pbtxt | 4 +- .../api_def_ResizeNearestNeighbor.pbtxt | 4 +- ...api_def_ResourceApplyAdamWithAmsgrad.pbtxt | 4 + .../api_def_ResourceApplyKerasMomentum.pbtxt | 4 + ...def_ResourceSparseApplyKerasMomentum.pbtxt | 4 + .../python_api/api_def_ScanDataset.pbtxt | 4 - .../api_def_SetStatsAggregatorDataset.pbtxt | 4 - .../api_def_ShuffleAndRepeatDataset.pbtxt | 4 - .../python_api/api_def_ShuffleDataset.pbtxt | 4 - .../api_def/python_api/api_def_Sign.pbtxt | 7 +- .../python_api/api_def_SkipDataset.pbtxt | 4 - .../python_api/api_def_SlideDataset.pbtxt | 4 - .../api_def_SparseTensorSliceDataset.pbtxt | 4 - .../python_api/api_def_SqlDataset.pbtxt | 4 - .../api_def/python_api/api_def_Sqrt.pbtxt | 7 +- .../api_def/python_api/api_def_Square.pbtxt | 7 +- .../api_def_StatsAggregatorHandle.pbtxt | 4 - .../api_def_StatsAggregatorSummary.pbtxt | 4 - .../api_def_StringToHashBucket.pbtxt | 8 +- .../python_api/api_def_StringToNumber.pbtxt | 8 +- .../python_api/api_def_TFRecordDataset.pbtxt | 4 - .../python_api/api_def_TakeDataset.pbtxt | 4 - .../api_def/python_api/api_def_Tanh.pbtxt | 10 +- .../python_api/api_def_TensorDataset.pbtxt | 4 - .../python_api/api_def_TensorListConcat.pbtxt | 4 + ... => api_def_TensorListPushBackBatch.pbtxt} | 0 .../python_api/api_def_TensorListSplit.pbtxt | 4 + .../api_def_TensorSliceDataset.pbtxt | 4 - .../python_api/api_def_TextLineDataset.pbtxt | 4 - .../python_api/api_def_UnbatchDataset.pbtxt | 4 - .../python_api/api_def_ZipDataset.pbtxt | 4 - .../common_runtime/accumulate_n_optimizer.cc | 5 +- .../collective_executor_mgr_test.cc | 6 +- .../collective_param_resolver_local.cc | 4 +- .../collective_param_resolver_local_test.cc | 6 +- .../common_runtime/collective_rma_local.cc | 2 +- .../collective_rma_local_test.cc | 6 +- .../core/common_runtime/constant_folding.cc | 26 +- .../common_runtime/constant_folding_test.cc | 46 + tensorflow/core/common_runtime/device.cc | 2 + tensorflow/core/common_runtime/device.h | 10 + .../core/common_runtime/device_factory.cc | 24 +- .../core/common_runtime/device_factory.h | 13 +- tensorflow/core/common_runtime/device_mgr.cc | 37 +- tensorflow/core/common_runtime/device_mgr.h | 15 +- .../device_resolver_local_test.cc | 6 +- .../core/common_runtime/device_set_test.cc | 2 +- .../core/common_runtime/direct_session.cc | 28 +- .../core/common_runtime/direct_session.h | 6 +- tensorflow/core/common_runtime/eager/BUILD | 1 + .../core/common_runtime/eager/attr_builder.cc | 32 +- .../core/common_runtime/eager/attr_builder.h | 16 +- .../common_runtime/eager/attr_builder_test.cc | 17 +- .../core/common_runtime/eager/context.cc | 14 +- .../core/common_runtime/eager/context.h | 2 + .../common_runtime/eager/eager_operation.h | 14 +- .../core/common_runtime/eager/execute.cc | 15 +- .../eager/kernel_and_device_test.cc | 14 +- tensorflow/core/common_runtime/executor.cc | 44 +- .../core/common_runtime/executor_test.cc | 8 +- .../core/common_runtime/function_test.cc | 11 +- .../function_threadpool_test.cc | 12 +- .../core/common_runtime/gpu/gpu_device.cc | 20 +- .../core/common_runtime/gpu/gpu_device.h | 18 +- .../common_runtime/gpu/gpu_device_factory.cc | 21 +- .../gpu/gpu_device_on_non_gpu_machine_test.cc | 2 +- .../common_runtime/gpu/gpu_device_test.cc | 32 +- .../common_runtime/gpu/gpu_process_state.cc | 27 +- .../common_runtime/gpu/gpu_process_state.h | 22 +- .../common_runtime/graph_execution_state.cc | 96 +- .../hierarchical_tree_broadcaster_test.cc | 11 +- .../kernel_benchmark_testlib.cc | 8 +- .../common_runtime/kernel_benchmark_testlib.h | 2 +- .../core/common_runtime/local_device.cc | 73 +- tensorflow/core/common_runtime/local_device.h | 7 + tensorflow/core/common_runtime/lower_if_op.cc | 10 +- tensorflow/core/common_runtime/metrics.cc | 16 +- tensorflow/core/common_runtime/metrics.h | 4 +- .../common_runtime/optimization_registry.cc | 14 + tensorflow/core/common_runtime/placer.cc | 2 +- tensorflow/core/common_runtime/placer_test.cc | 2 +- .../core/common_runtime/pool_allocator.cc | 14 +- .../core/common_runtime/pool_allocator.h | 1 - .../process_function_library_runtime_test.cc | 18 +- .../core/common_runtime/process_state.cc | 28 +- .../core/common_runtime/process_state.h | 8 +- .../core/common_runtime/renamed_device.cc | 14 +- .../core/common_runtime/renamed_device.h | 7 +- .../core/common_runtime/ring_reducer.cc | 2 +- .../core/common_runtime/ring_reducer_test.cc | 11 +- .../core/common_runtime/threadpool_device.cc | 2 +- .../threadpool_device_factory.cc | 36 +- tensorflow/core/distributed_runtime/BUILD | 13 +- ...lective_param_resolver_distributed_test.cc | 11 +- .../collective_rma_distributed_test.cc | 11 +- .../device_resolver_distributed_test.cc | 13 +- .../core/distributed_runtime/eager/BUILD | 1 + .../eager/eager_service_impl.cc | 35 +- .../eager/eager_service_impl_test.cc | 9 +- .../core/distributed_runtime/graph_mgr.cc | 8 +- tensorflow/core/distributed_runtime/rpc/BUILD | 1 + .../rpc/eager/grpc_eager_client.cc | 2 +- .../distributed_runtime/rpc/grpc_channel.cc | 44 +- .../distributed_runtime/rpc/grpc_channel.h | 8 +- .../rpc/grpc_channel_test.cc | 45 +- .../rpc/grpc_remote_worker.cc | 14 +- .../rpc/grpc_remote_worker.h | 3 +- .../rpc/grpc_rpc_factory.cc | 10 +- .../rpc/grpc_server_lib.cc | 11 +- .../distributed_runtime/rpc/grpc_session.cc | 20 +- .../distributed_runtime/rpc/grpc_session.h | 3 + .../rpc/grpc_session_test.cc | 27 + .../core/distributed_runtime/rpc/grpc_state.h | 36 +- .../rpc/grpc_worker_cache.cc | 16 +- .../rpc/rpc_rendezvous_mgr.cc | 9 + .../rpc_collective_executor_mgr_test.cc | 6 +- .../core/distributed_runtime/server_lib.cc | 10 +- .../distributed_runtime/server_lib_test.cc | 56 + .../core/distributed_runtime/session_mgr.cc | 8 +- .../distributed_runtime/session_mgr_test.cc | 8 +- tensorflow/core/distributed_runtime/worker.cc | 4 +- tensorflow/core/framework/allocator.cc | 2 + tensorflow/core/framework/allocator.h | 2 + tensorflow/core/framework/common_shape_fns.cc | 45 + tensorflow/core/framework/common_shape_fns.h | 3 + tensorflow/core/framework/dataset.cc | 3 + tensorflow/core/framework/dataset.h | 32 +- tensorflow/core/framework/device_base.cc | 6 +- tensorflow/core/framework/function.cc | 187 +- tensorflow/core/framework/function.h | 8 + .../core/framework/function_handle_cache.cc | 66 + .../core/framework/function_handle_cache.h | 53 + tensorflow/core/framework/function_test.cc | 84 + tensorflow/core/framework/node_def_util.cc | 4 + tensorflow/core/framework/node_def_util.h | 1 + tensorflow/core/framework/op_kernel.cc | 65 +- tensorflow/core/framework/resource_mgr.cc | 9 +- tensorflow/core/framework/tensor.h | 91 +- tensorflow/core/framework/tensor_test.cc | 11 +- tensorflow/core/graph/edgeset.cc | 2 +- tensorflow/core/graph/edgeset.h | 20 +- tensorflow/core/graph/graph.cc | 24 + tensorflow/core/graph/graph.h | 21 +- tensorflow/core/graph/graph_partition.cc | 4 +- tensorflow/core/graph/graph_partition_test.cc | 10 +- tensorflow/core/graph/graph_test.cc | 39 - tensorflow/core/graph/mkl_layout_pass.cc | 490 +- tensorflow/core/graph/mkl_layout_pass_test.cc | 311 + tensorflow/core/graph/node_builder.cc | 2 + tensorflow/core/graph/node_builder.h | 1 + tensorflow/core/graph/optimizer_cse_test.cc | 10 +- tensorflow/core/graph/testlib.cc | 11 + tensorflow/core/graph/testlib.h | 4 + tensorflow/core/grappler/BUILD | 4 + .../core/grappler/costs/graph_properties.cc | 5 + .../costs/op_level_cost_estimator_test.cc | 2 +- .../core/grappler/costs/virtual_scheduler.cc | 20 +- .../core/grappler/costs/virtual_scheduler.h | 8 +- .../core/grappler/graph_analyzer/sig_node.h | 2 +- tensorflow/core/grappler/graph_view.cc | 6 + tensorflow/core/grappler/graph_view.h | 5 +- tensorflow/core/grappler/grappler_item.cc | 64 +- tensorflow/core/grappler/grappler_item.h | 35 +- .../core/grappler/grappler_item_builder.cc | 11 +- .../core/grappler/grappler_item_test.cc | 28 + tensorflow/core/grappler/op_types.cc | 17 +- tensorflow/core/grappler/op_types.h | 6 +- tensorflow/core/grappler/optimizers/BUILD | 29 +- .../optimizers/arithmetic_optimizer.cc | 24 +- .../optimizers/arithmetic_optimizer_test.cc | 27 + .../optimizers/constant_folding_test.cc | 326 +- .../core/grappler/optimizers/data/BUILD | 1 + .../optimizers/data/graph_test_utils.cc | 2 +- .../optimizers/data/hoist_random_uniform.cc | 2 +- .../data/hoist_random_uniform_test.cc | 2 +- .../optimizers/data/latency_all_edges.cc | 2 +- .../optimizers/data/latency_all_edges_test.cc | 7 +- .../optimizers/data/make_numa_aware.cc | 2 +- .../optimizers/data/make_numa_aware_test.cc | 6 +- .../optimizers/data/map_and_batch_fusion.cc | 2 +- .../data/map_and_batch_fusion_test.cc | 21 +- .../experimental_implementation_selector.cc | 114 +- ...perimental_implementation_selector_test.cc | 95 + .../grappler/optimizers/function_api_info.cc | 180 +- .../grappler/optimizers/function_api_info.h | 38 +- .../optimizers/function_api_info_test.cc | 118 +- .../grappler/optimizers/function_optimizer.cc | 848 +- .../optimizers/function_optimizer_test.cc | 235 + .../optimizers/graph_optimizer_stage.h | 3 +- .../grappler/optimizers/graph_rewriter.cc | 214 - .../core/grappler/optimizers/graph_rewriter.h | 102 - .../grappler/optimizers/layout_optimizer.cc | 4 + .../grappler/optimizers/memory_optimizer.cc | 1 - .../grappler/optimizers/meta_optimizer.cc | 66 +- .../core/grappler/optimizers/meta_optimizer.h | 10 +- .../optimizers/meta_optimizer_test.cc | 65 +- .../core/grappler/optimizers/model_pruner.cc | 164 +- .../core/grappler/optimizers/remapper.cc | 447 +- .../core/grappler/optimizers/remapper_test.cc | 244 +- tensorflow/core/grappler/utils.cc | 4 +- tensorflow/core/grappler/utils/BUILD | 1 - tensorflow/core/grappler/utils/functions.cc | 124 +- tensorflow/core/grappler/utils/functions.h | 30 +- .../core/grappler/utils/functions_test.cc | 86 +- .../core/grappler/utils/grappler_test.cc | 6 +- tensorflow/core/grappler/utils_test.cc | 23 + tensorflow/core/kernels/BUILD | 90 +- tensorflow/core/kernels/adjust_contrast_op.cc | 4 +- tensorflow/core/kernels/adjust_hue_op.cc | 4 +- tensorflow/core/kernels/barrier_ops.cc | 2 +- .../kernels/boosted_trees/boosted_trees.proto | 14 + tensorflow/core/kernels/control_flow_ops.cc | 4 + tensorflow/core/kernels/conv_2d.h | 2 +- .../{conv_ops_gpu_3.cu.cc => conv_2d_gpu.h} | 126 +- .../core/kernels/conv_2d_gpu_double.cu.cc | 50 + .../core/kernels/conv_2d_gpu_float.cu.cc | 63 + .../core/kernels/conv_2d_gpu_half.cu.cc | 57 + .../kernels/conv_2d_gpu_int.cu.cc} | 28 +- .../core/kernels/conv_2d_gpu_uint16.cu.cc | 38 + .../core/kernels/conv_2d_gpu_uint32.cu.cc | 38 + .../core/kernels/conv_2d_gpu_uint64.cu.cc | 38 + .../core/kernels/conv_2d_gpu_uint8.cu.cc | 38 + tensorflow/core/kernels/conv_grad_ops_3d.cc | 2 + tensorflow/core/kernels/conv_ops_3d.cc | 14 +- tensorflow/core/kernels/conv_ops_fused.cc | 1185 +- .../kernels/conv_ops_fused_image_transform.cc | 902 + tensorflow/core/kernels/conv_ops_test.cc | 735 +- tensorflow/core/kernels/cwise_ops.h | 21 + tensorflow/core/kernels/data/BUILD | 215 +- .../core/kernels/data/batch_dataset_op.cc | 9 + .../core/kernels/data/cache_dataset_ops.cc | 4 + .../core/kernels/data/captured_function.cc | 214 +- .../core/kernels/data/captured_function.h | 127 +- .../kernels/data/concatenate_dataset_op.cc | 12 + tensorflow/core/kernels/data/dataset_ops.cc | 17 + tensorflow/core/kernels/data/dataset_utils.cc | 8 +- tensorflow/core/kernels/data/dataset_utils.h | 4 +- .../core/kernels/data/experimental/BUILD | 253 +- .../experimental/assert_next_dataset_op.cc | 2 + .../dense_to_sparse_batch_dataset_op.cc | 13 +- .../group_by_reducer_dataset_op.cc | 36 +- .../group_by_window_dataset_op.cc | 30 +- .../experimental/identity_indexed_dataset.cc | 163 - .../experimental/ignore_errors_dataset_op.cc | 2 + .../data/experimental/indexed_dataset.h | 119 - ...dexed_dataset.cc => indexed_dataset_op.cc} | 249 +- .../map_and_batch_dataset_op.cc | 90 +- .../matching_files_dataset_op.cc | 5 +- .../non_serializable_dataset_op.cc | 2 + .../numa_map_and_batch_dataset_op.cc | 17 +- .../parallel_interleave_dataset_op.cc | 1085 + .../parse_example_dataset_op.cc | 206 +- .../data/experimental/prefetching_kernels.cc | 428 - .../{ => experimental}/random_dataset_op.cc | 4 +- .../{ => experimental}/scan_dataset_op.cc | 15 +- .../set_stats_aggregator_dataset_op.cc} | 7 +- .../data/experimental/sleep_dataset_op.cc | 2 + .../sliding_window_dataset_op.cc} | 19 +- .../kernels/data/{ => experimental}/sql/BUILD | 0 .../{ => experimental}/sql/driver_manager.cc | 4 +- .../{ => experimental}/sql/driver_manager.h | 8 +- .../{ => experimental}/sql/query_connection.h | 6 +- .../sql/sqlite_query_connection.cc | 4 +- .../sql/sqlite_query_connection.h | 8 +- .../sql_dataset_op.cc} | 7 +- .../stats_aggregator_ops.cc | 10 +- .../{ => experimental}/stats_dataset_ops.cc | 14 +- .../experimental/threadpool_dataset_op.cc | 238 +- .../to_tf_record_op.cc} | 21 +- .../{ => experimental}/unbatch_dataset_op.cc | 4 +- .../core/kernels/data/filter_dataset_op.cc | 20 +- .../core/kernels/data/flat_map_dataset_op.cc | 7 +- .../core/kernels/data/generator_dataset_op.cc | 33 +- .../kernels/data/interleave_dataset_op.cc | 8 +- tensorflow/core/kernels/data/iterator_ops.cc | 307 +- .../core/kernels/data/map_dataset_op.cc | 29 +- .../core/kernels/data/model_dataset_op.cc | 2 + .../kernels/data/multi_device_iterator_ops.cc | 50 +- .../core/kernels/data/optimize_dataset_op.cc | 81 +- tensorflow/core/kernels/data/optional_ops.cc | 132 +- .../core/kernels/data/optional_ops.cu.cc | 37 + tensorflow/core/kernels/data/optional_ops.h | 119 + .../kernels/data/padded_batch_dataset_op.cc | 9 + .../data/parallel_interleave_dataset_op.cc | 1058 +- .../kernels/data/parallel_map_dataset_op.cc | 135 +- .../kernels/data/parallel_map_iterator.cc | 35 +- .../core/kernels/data/parallel_map_iterator.h | 43 +- .../core/kernels/data/prefetch_dataset_op.cc | 7 +- .../core/kernels/data/range_dataset_op.cc | 8 + .../core/kernels/data/repeat_dataset_op.cc | 17 + .../core/kernels/data/shuffle_dataset_op.cc | 2 + .../kernels/data/single_threaded_executor.cc | 15 +- .../data/single_threaded_executor_test.cc | 8 +- .../core/kernels/data/skip_dataset_op.cc | 8 + .../data/sparse_tensor_slice_dataset_op.cc | 2 + .../core/kernels/data/take_dataset_op.cc | 11 + .../core/kernels/data/tensor_dataset_op.cc | 2 + .../kernels/data/tensor_queue_dataset_op.cc | 657 - .../kernels/data/tensor_slice_dataset_op.cc | 2 + .../core/kernels/data/window_dataset.cc | 2 + .../core/kernels/data/window_dataset_op.cc | 9 + .../core/kernels/data/zip_dataset_op.cc | 15 + tensorflow/core/kernels/data_format_ops.cc | 10 + tensorflow/core/kernels/deep_conv2d.cc | 21 +- .../core/kernels/depthwise_conv_op_gpu.cu.cc | 8 +- .../core/kernels/dynamic_partition_op.cc | 2 +- .../core/kernels/eigen_spatial_convolutions.h | 194 +- .../eigen_spatial_convolutions_test.cc | 103 +- .../core/kernels/fractional_avg_pool_op.cc | 2 +- tensorflow/core/kernels/functional_ops.cc | 31 +- .../core/kernels/fused_batch_norm_op.cc | 4 +- tensorflow/core/kernels/fuzzing/BUILD | 22 + .../012e3ad384a4a1165f8498b5c94ba0d32a73e187 | Bin 0 -> 420 bytes .../055d77f7810048caa28323f6eb552a53d156040b | Bin 0 -> 594 bytes .../131e251bfb82c681cb075d32b99f18fceaca115d | Bin 0 -> 287 bytes .../1399ab0bd9f2c91d270cb43251bdc5729bef3526 | Bin 0 -> 1684 bytes .../16a6ce88f66d2e9686c8354cad8ba915cf0c11de | Bin 0 -> 207 bytes .../185097ed0588195164619ea930ddd8274a5f32ad | Bin 0 -> 3648 bytes .../27711a87e06a50c81571c27c3aa403a6ad5dc55c | Bin 0 -> 2218 bytes .../298c3787ad1722b22569cbc405c464d2 | Bin 0 -> 4234 bytes .../2b95ba6d8141ce0d29ff279770903922 | Bin 0 -> 4234 bytes .../321fb3d758b86e37fc340ae2b09b8ed9fa73a4cb | Bin 0 -> 4234 bytes .../331a98b4e4c87840efea69223766ebd0e1736542 | Bin 0 -> 420 bytes .../352d73f841223ecb630b5836585d2ba7b0f9d883 | Bin 0 -> 2249 bytes .../3a84f409d4c117edfdebc508cd23e8fc | Bin 0 -> 3194 bytes .../3ef5cc982c0b45f69a26fd0f7d376415fdebabd1 | Bin 0 -> 220 bytes .../401c7de8e122018a0e17f57c93db7ee49ab0e906 | Bin 0 -> 865 bytes .../52fee71bb8c9c79068e1fe580677ad739a2d0415 | Bin 0 -> 361 bytes .../57b11507813d5727b7789354d888eda83d5f3d86 | Bin 0 -> 2846 bytes .../57dff0fa53ee0ef24a43cca6ab0523bfdc1f720d | Bin 0 -> 864 bytes .../5c42d3df0dc400a7a4175b8d4eec6cc8ee2437b2 | Bin 0 -> 2246 bytes .../5cca20637ae75fddad9370ee930837baef8aeb43 | Bin 0 -> 4234 bytes .../5d34bc9cef0c844b9c5ebe948145c4ca11b5ca09 | Bin 0 -> 2639 bytes .../5e162fe883bd12fb1c4131d4e0c979a12bd15eac | Bin 0 -> 76 bytes .../5e83f8faab9c1a51a33d5e29edbb9dcec23c6092 | Bin 0 -> 1412 bytes .../61b29dc2fcef7b6fbe3e0cc88769a7ef | Bin 0 -> 3194 bytes .../6361eca190157ece389665ee523ccc3aefcd957f | Bin 0 -> 3708 bytes .../65150515ab3b11d657519b22bb887d74e94b2d7f | Bin 0 -> 4234 bytes .../656f38ef6dcd58c6a909d61db11f777def69c394 | Bin 0 -> 1406 bytes .../66e0d2cafd592bf9d61ad900fade8ee530d5f3d7 | Bin 0 -> 203 bytes .../6b5b42cb105a2c4c5fd6034e9885cbe457f1b50c | Bin 0 -> 361 bytes .../722ed0197cb92ecbf9745edb38275e7a9aaf322f | Bin 0 -> 454 bytes .../77bdd2efdf328366cbbf3c5688768dc0a88d02b1 | Bin 0 -> 16 bytes .../7841bfa002c05c61d5a5d9241f214cc17a336166 | Bin 0 -> 864 bytes .../7899e22fc83f6be28e9130c4a1c91a48 | Bin 0 -> 4234 bytes .../7dddccaebd16ae0c26daeffc42df50f529891119 | Bin 0 -> 2249 bytes .../8157442eee4bbfdd9716e264b11085d61a9955b7 | Bin 0 -> 48 bytes .../81ff28ed63d5435ddc4c8771dd5d40aa658cbbe0 | Bin 0 -> 2052 bytes .../820c8c0d33c18f6c4d9edd314e91289186931ad0 | Bin 0 -> 1318 bytes .../849e9d7cee1c52105242327086997296e452b981 | Bin 0 -> 1237 bytes .../84ddb92c63e0fad7018f6069daf8779ce11501e2 | Bin 0 -> 1167 bytes .../86bc3d5dbb9313137502080e58551edd2e649c70 | Bin 0 -> 929 bytes .../87d94d88fe29d277c76e1a52042b02c092d5ae14 | Bin 0 -> 420 bytes .../8c4646f3357945c4e19a59ff79fffe3c874dbf16 | Bin 0 -> 189 bytes .../90632bc6dee4eb836f3d7db1d16446a9c8510080 | Bin 0 -> 3895 bytes .../94d06016aa949e8e7203217e4cc6625ded7f4244 | Bin 0 -> 3102 bytes .../9875819b9e5783e7489c29a81cc9d4279209956a | Bin 0 -> 22 bytes .../9c1cc734114b29aac6c51782d5c17e9dbe1faca2 | Bin 0 -> 3194 bytes .../9d2961871eeb201ef8a6f5503d8a8b62 | Bin 0 -> 3194 bytes .../9f39e11cdd88344a4894b678e5a04a810880064d | Bin 0 -> 972 bytes .../a350588a6dabe4376a066aed44ef8786d8e752e7 | Bin 0 -> 861 bytes .../a6101a79919d444e1fc50aefab5837c39e3f4a19 | Bin 0 -> 1237 bytes .../a9c8793f8fb063bec839ee1280406fe5396545e5 | Bin 0 -> 420 bytes .../ad4e9d2234e8599bdf12607c6b8cab4edae82c4e | Bin 0 -> 420 bytes .../b90b6830917919e94186d312f06481bd | Bin 0 -> 3194 bytes .../b98fd4cb1d7031240414301c19b03097c0035c6b | Bin 0 -> 857 bytes .../ba976fcdb4daf092ef17ce43bf2b78d9d8bc2aeb | Bin 0 -> 2262 bytes .../bc112b571eafee0f5a031f3c9cce6244216d128d | Bin 0 -> 4234 bytes .../c42b981c28a1715c375050f6fcf53f1d | Bin 0 -> 3194 bytes .../c6049874b33eadb016fccf0c5fa66e556ae069b9 | Bin 0 -> 3194 bytes .../c8697bf2369f6ab85f501376c4d93bb8a56974a3 | Bin 0 -> 420 bytes .../c8daf283e0aef2fd7b630c0430e05dc28f24ecf6 | Bin 0 -> 220 bytes .../cacff56e1af4b8fde912822da06b10fb8c545a19 | Bin 0 -> 1033 bytes .../ce4dcc22b1d595c49a25121c0b580104 | Bin 0 -> 4234 bytes .../d0cd71dbf039fd64cf42eff30da92a71a919226a | Bin 0 -> 587 bytes .../d5ce626ac3264bed6af5580e341a89406857cbb9 | Bin 0 -> 1760 bytes .../d77ada02e9bc8c24b2711eca6a8f52ae356bfc21 | Bin 0 -> 361 bytes .../d7eb9c5a0f9803df4c00390793b8ab57bd7c9484 | Bin 0 -> 3792 bytes .../dc1efccdeec17e151a1ec8228c09ab61c3040b33 | Bin 0 -> 35 bytes .../dcea22c66c60088165a2f1772036473f | Bin 0 -> 4234 bytes .../de539ae7442fa05dafcfe1a021f0186ef74a2b0e | Bin 0 -> 103 bytes .../e2306b1d6b88d0ccc4e2c3a9edb07462a5a32215 | Bin 0 -> 110 bytes .../e2778da0240fdd15ef5844905d81c4e05f34a8bd | Bin 0 -> 3194 bytes .../e6642e9266875f9d908942e534bf898103a2c794 | Bin 0 -> 2186 bytes .../ec6cdb929c08d8daf2bd7fc185fbf4d787b45120 | Bin 0 -> 1942 bytes .../ed8636357f79439b6a03eb14469b686cc401a1c9 | Bin 0 -> 3895 bytes .../ee313e9acecb5c688ce8c9bb10e70e136fbb9c6d | Bin 0 -> 3616 bytes .../ef689af320e7d9e22231109faae2e8149cb86e1c | Bin 0 -> 834 bytes .../fda6b9a9f6ffdf4765c00465619c7ceb3f7db2e4 | Bin 0 -> 1136 bytes .../ffe829bb0adac20d9c0756f68a22d1255e4fdb54 | Bin 0 -> 3194 bytes .../013a29ea098a178f8a36741c9fd91144 | 48 + .../0875575fb76d630ccb19c5da8aab66b2 | 1 + .../7e7f58fc443a11a0a2c5d9b643b7e99b | 1 + .../849a23936269a261c0370b5e9abe2416 | 1 + .../85282c1696d98b9843ce3e8bd1cd899f | 1 + .../90388b9c8093d8adedad0644b618da87 | 33 + .../9fa2f86ea6d3ade36e961247c3026f8d | 33 + .../c4f18ca60a84e9869a28faf6f65dc758 | 32 + .../d456ee029700adef5d28438593010223 | 1 + .../e9f0ff6ee8d691ae69d2ecb4710030a2 | 36 + .../010dc3d4b05288fcc40de2721052b3dc699f1cb3 | Bin 0 -> 233 bytes .../0555cd5e9d99629819cc985285f80da0f00be1e9 | Bin 0 -> 474 bytes .../0a0352aa168803ff65455792d9f6ee555c3e7c3f | Bin 0 -> 232 bytes .../0ed54162df93ef8d00f993ce6b59ba422903d381 | Bin 0 -> 221 bytes .../1547b448171c700613c3946d730de496c9b9863f | Bin 0 -> 260 bytes .../17859046cbe4ac598a645173d679ce2a52c6afba | Bin 0 -> 525 bytes .../1df76c07817fbc3653a26f34d97658e9973627c2 | Bin 0 -> 666 bytes .../1f0717f8856d7782e3ab7992d3a72d783a018443 | Bin 0 -> 122 bytes .../23b911e4ce936def88bc9a46b8b433c0e83fba2a | Bin 0 -> 375 bytes .../25592201c3edff0578dbdac6b0e4f2be109ce151 | Bin 0 -> 329 bytes .../266fd8495e0b8eb64387c1a62264185e061fee73 | Bin 0 -> 360 bytes .../27f178cf415b4ff8671131ddf1d042dafac2fb3e | Bin 0 -> 677 bytes .../2a0bdc4d9cc5ea5bb21dd256d6ac96075376a94f | Bin 0 -> 246 bytes .../2e5d25add6adc68e0457b358c7a34abf3d41c938 | Bin 0 -> 165 bytes .../2e6c5b6a766dd5e9bd41eacfd0a36572bd2f7544 | Bin 0 -> 320 bytes .../2e9c935cf82f6ca640e9a9abc3c30a578ad46176 | Bin 0 -> 666 bytes .../2fcf1ed4477f7eaee028f5b3f9edeb5f1a737826 | Bin 0 -> 507 bytes .../3480713774f590908ca5dba16d121cdfb8fba62b | Bin 0 -> 665 bytes .../39289afcec60d98802b333e0fbb1da4d7aed4ce5 | Bin 0 -> 247 bytes .../3adc488e21d4aca7bed9422f0241a42d0f93e7d9 | Bin 0 -> 366 bytes .../3cbf274da522483dc991fad9df43a22ac4fb3173 | Bin 0 -> 474 bytes .../3d840cdff7f5ad16fe8bcb985ed4946c03459432 | Bin 0 -> 666 bytes .../3f1e6753c1fca958e859189857449746592158ea | Bin 0 -> 94 bytes .../3fa4075993cb0f9bfa8eea785174a2038a69aa1b | Bin 0 -> 343 bytes .../4023a373e977be58413e55350380310c5dd1fd6a | Bin 0 -> 241 bytes .../40caba69dce1cfc48e0e43184d2bfbc6daa4399a | Bin 0 -> 470 bytes .../41841e9561d8135945c1c1e55ab9e9a1e933653b | Bin 0 -> 247 bytes .../41d40f2d66fa43e34537385594ee9911e65deadf | Bin 0 -> 677 bytes .../421bd39810b50309a71adb2dadc3b19f01a52312 | Bin 0 -> 221 bytes .../446c305b2c0665736f94fb2b62dbdef445eff0cf | Bin 0 -> 174 bytes .../449cee952bb645f6f4241a6665d3c6028c073c7a | Bin 0 -> 374 bytes .../45520b07609978c5aa3516d803527438b93fbadb | Bin 0 -> 158 bytes .../4da74a34bcede234b0415f77fbd87d70bf9a777e | Bin 0 -> 94 bytes .../51db5d31d2c5300d34831d9f23bcdd0aff9a998b | Bin 0 -> 677 bytes .../5cde2a9167798cb77f10abbfb2640a5c357f99fc | Bin 0 -> 507 bytes .../5e352fc10ac476cfbe1d755f092e069820223249 | Bin 0 -> 169 bytes .../63661677dd1306cec4b5a565190e65adf2446e52 | Bin 0 -> 374 bytes .../65887ed3db382aab1d9485c500f4401318d303b9 | Bin 0 -> 224 bytes .../67b5181f8f0644597e9bde539e8f083b5cacd0e7 | Bin 0 -> 165 bytes .../74c9dcf7afee2a6cb1ab3a2c0de744d1b03c1466 | Bin 0 -> 307 bytes .../792181ca19e6ded261434e588bb7fc2a4816d4ce | Bin 0 -> 544 bytes .../79f0e2a475487f8fa69e68c1cc947c5851bda741 | Bin 0 -> 355 bytes .../7e5fcdfeb557ce379ed96925c68505eaac0112db | Bin 0 -> 315 bytes .../7eec7530acf34b3a96fa9189783453999f7b6838 | Bin 0 -> 671 bytes .../80114bf9781bffc9db411413d83541d8deaaf7c1 | Bin 0 -> 343 bytes .../80425fb92bb86627e854892f23823fa804e5fdc3 | Bin 0 -> 441 bytes .../821cdd6eeb919a8dd7f35289abbd583828dd4945 | Bin 0 -> 677 bytes .../83e1a31785285338b0ddb3334b0ed098e63dedde | Bin 0 -> 285 bytes .../8a4c8100dedd0fb5f2a8b468c678f7ad8269deeb | Bin 0 -> 666 bytes .../8ae8268c24dc866c1edb3826b93a1c75dbf74ff4 | Bin 0 -> 677 bytes .../90f72038cc627f34f074ea72eadbba87a5e3e288 | Bin 0 -> 677 bytes .../92b67faee4a49df2cdbed785e27b4a1cddcfffa3 | Bin 0 -> 507 bytes .../9463810467aacdc9923b2b20a2236116b760d75b | Bin 0 -> 258 bytes .../94d7c96aea32ad41ce643d35b951a6d8990b81d6 | Bin 0 -> 546 bytes .../98cc7e9fe87df914d89a0aef008930f27b3c26f5 | Bin 0 -> 311 bytes .../99172dfdb4f59aaced29c7681ac6e6ce8356e814 | Bin 0 -> 104 bytes .../9ae3b647d895af97fe872c0b1442df7b5b767160 | Bin 0 -> 332 bytes .../9d2b1d2121b0508a4fa8d1508adb9d05633fdac3 | Bin 0 -> 222 bytes .../a335af37917ccf0c8b11bb884a3a74f3f1d2a7c6 | Bin 0 -> 677 bytes .../a738609112d3a6772c50a71e2c3504ebc515b709 | Bin 0 -> 337 bytes .../a8cecab5d917da5a4729632a7a18c564d7e1607d | Bin 0 -> 258 bytes .../ade919ab2b4a458e806575c941dfe50ae3fd3621 | Bin 0 -> 677 bytes .../b1251621a5eb5e7fda9cac9baead1c993a285c36 | Bin 0 -> 277 bytes .../b1516b78c3dfe77eeb554985fd7344c0478fbbcb | Bin 0 -> 258 bytes .../b41241740f5f8ad2c1d408f7bb6a313bd863c158 | Bin 0 -> 133 bytes .../b799c8596523a7ebeb8e11ada08818c10f7eabfc | Bin 0 -> 582 bytes .../ba48d0521a111222dc95a3a997c7c92dea5f4443 | Bin 0 -> 249 bytes .../c01457c6889fb1b597d308363a36412c0b7f90e7 | Bin 0 -> 497 bytes .../c82ebc0d6688d104af04fd20d6d3da591dc391f7 | Bin 0 -> 198 bytes .../c9a03eb758dd84e954e3d70916e2311e8fd21f3c | Bin 0 -> 249 bytes .../cf892756b33578a54ab20044514e573328d2f1d7 | Bin 0 -> 249 bytes .../d3bc3f158a63f1d50b474addd3f7b3d17f23e8e9 | Bin 0 -> 507 bytes .../d4906950aa9d60ad09dc0f5413c3d88080c3bc37 | Bin 0 -> 262 bytes .../da31578a8068bad65e1c7a3d06e8f543a2a0bc65 | Bin 0 -> 332 bytes .../dd4a9b5d0740679c249fc884efc499433b29436b | Bin 0 -> 666 bytes .../deea4ecc6f0b2a6d89fd25ff76762299f21602fb | Bin 0 -> 677 bytes .../e1040c7ffcb39915e0f539018c81f9798924cba6 | Bin 0 -> 94 bytes .../e381dc85682cc33ad99f622b89d145b47f7d6392 | Bin 0 -> 485 bytes .../ea24498fc7a144fccc6f1665ebf7020df803dd1a | Bin 0 -> 677 bytes .../eaa5d677e797c07bac98c3c7051abad91852e7c6 | Bin 0 -> 507 bytes .../ed7871269315725535d8bffec7836c45a3fc5c26 | Bin 0 -> 527 bytes .../ee8460f4077064c5a2137075b48eba7d3db5c570 | Bin 0 -> 94 bytes .../ef09f26e0ee61329f84a9f589629a865ae9ee0a6 | Bin 0 -> 276 bytes .../f477da4d7d8ff2066041e1dd5ee4e833b7111a1a | Bin 0 -> 527 bytes .../f8a379b2498a4eb452a85791a49adf065dab59ae | Bin 0 -> 666 bytes .../fe67bccb06f2174523943cc684518fcf1f7f8046 | Bin 0 -> 80 bytes .../ff1e67d17c1c27ef0d97900d0ea276b563a64628 | Bin 0 -> 271 bytes .../02cc44cdfec1d9d0d0c66c5a5f40d3d20e4c4c3a | Bin 0 -> 11564 bytes .../087e1d7fae1c1ddcbaa3b5f822a171ad15498186 | Bin 0 -> 146 bytes .../0f61c33027394a0f14d29dcd22f405cad943b7cf | Bin 0 -> 29936 bytes .../10cdebea1659c21a0248f88654ae41f62786abf1 | Bin 0 -> 58657 bytes .../126e68def9fd973a100e0f66cadf09448a716b57 | Bin 0 -> 9209 bytes .../1275d41ebf8788ce3a949352e4bc654b04012da3 | Bin 0 -> 70 bytes .../1a7f1c407fb3864ddb559f88f373a21d1be51584 | Bin 0 -> 35640 bytes .../1c3e1c91f187f6bcea86f172ff5bbbd955a9654d | Bin 0 -> 57 bytes .../300fe1e0a47543037cbf0243b6756c9aa48799c4 | Bin 0 -> 72238 bytes .../31ec5b0134bedcfe283f4978e6e65b7d35d5d4ad | Bin 0 -> 26123 bytes .../4e7cbb27667bcfca92838aa8020749990013a9b1 | Bin 0 -> 66423 bytes .../585e469231d202812bfba8285fb30c8e31c857b9 | Bin 0 -> 70833 bytes .../58eab6bc2386e2ef43fe4f55cb6ad3611399d5de | Bin 0 -> 22 bytes .../63448c6a9feb8c72b3e82af4d735ec2e62ddd328 | Bin 0 -> 58203 bytes .../6874d5b1c7a64b596c61f24877d422e89bebe58b | Bin 0 -> 25086 bytes .../7501f79cb067da108020579ed654349c7933d22f | Bin 0 -> 54715 bytes .../782051f8120182b860c7fe1b265179cfa2fe03fd | Bin 0 -> 66439 bytes .../793feab2deb35e284a975f6527d76a8be5540fe6 | Bin 0 -> 61 bytes .../7f41ec3a9805c6b8f3656c4f9f6d0ff7dbf8a329 | Bin 0 -> 6150 bytes .../8210dc595a2652f2f812093b01e239e7918ea065 | Bin 0 -> 55329 bytes .../8dffe4c5c26d891b578fd2ea4b9adfc0c96ad5f7 | Bin 0 -> 71409 bytes .../91d787a9298ddc015efa783a92c4bdba8af0d7de | Bin 0 -> 44 bytes .../92c065286f956f086e977556358f6b54b12bcacc | Bin 0 -> 315 bytes .../a35c9bb71792b60a13dea23a41b41847ad4b93d6 | Bin 0 -> 44 bytes .../a6ea960c7b4d42772888280277b26e645ceee904 | Bin 0 -> 22470 bytes .../aa526aa853333f0bb11804b5243df411452cecd2 | Bin 0 -> 21331 bytes .../ca533cd26c7ca6bf69e62351b265ded496fdf1d9 | Bin 0 -> 52741 bytes .../f38c61da15f2cb7a39ff02e69f0b00e99f37ec86 | Bin 0 -> 66555 bytes .../f88f1012473e6cfcc9b39b2552f682b2f73eff8c | Bin 0 -> 54757 bytes .../fa79819c5de04bc06c69bec3fa7f2e982826ea2f | Bin 0 -> 20280 bytes .../fce08de222896ac3a20657a3b4f42d5b6c54a96a | Bin 0 -> 69522 bytes .../4c01a1504da9de2216894743ecc44424 | 1 + .../5bf16424630b5afbcffe711fb9834440 | 1 + .../a7185605aef0a8fd682fcb4656e4a736 | 1 + .../d5606def44fdbb9385dd764612069db0 | Bin 0 -> 42 bytes .../dbac766f3160de65894bf5153f478146 | 1 + .../e85ff62f6d457666f54a37a19a115a24 | 1 + .../00fd47bf73afcb72e7ed51bffd5f5fec | 1 + .../14908973e6720513a5f37676cb9fcc29 | 1 + .../2779ba7c4d23eee9f79efa3660084c5d | 1 + .../5bf16424630b5afbcffe711fb9834440 | 1 + .../89734a96b93275e495a9498b806fafe1 | 1 + .../d5606def44fdbb9385dd764612069db0 | Bin 0 -> 42 bytes .../2db83ea58639b6d7d585fa12e3947a82 | 1 + .../36b4a931886b941dc41180050d12ca94 | 1 + .../50a2fabfdd276f573ff97ace8b11c5f4 | 1 + .../62edb2a1eee34b001652cd86584becf2 | 1 + .../90013d1ec28c46a5c00574e60c70b6fc | 1 + .../94f3e3cee6957ce5815326d6788c85f4 | 1 + .../96f547bc04bb913da0bc08915238ebd8 | 1 + .../d3a903d18fc11e1f35c572ad4da690ed | 1 + .../e3b629c92af44260c189deb32d6f06f3 | 1 + .../f03eecf3bcfe4967a1888156a3115c8d | 1 + .../fa54ca9186f77122ae2a82684a062e16 | 1 + .../dictionaries/decode_json_example.dict | 6 + .../fuzzing/dictionaries/decode_png.dict | 50 + .../fuzzing/dictionaries/decode_wav.dict | 4 + .../kernels/fuzzing/encode_base64_fuzz.cc | 2 +- .../core/kernels/fuzzing/fuzz_session.h | 4 +- .../core/kernels/fuzzing/string_split_fuzz.cc | 3 +- .../fuzzing/tf_ops_fuzz_target_lib.bzl | 32 +- tensorflow/core/kernels/list_kernels.cc | 44 +- tensorflow/core/kernels/list_kernels.cu.cc | 15 +- tensorflow/core/kernels/list_kernels.h | 384 +- .../core/kernels/maxpooling_op_gpu.cu.cc | 12 + tensorflow/core/kernels/mkl_avgpooling_op.cc | 129 +- tensorflow/core/kernels/mkl_conv_ops.cc | 64 +- tensorflow/core/kernels/mkl_lrn_op.cc | 722 +- tensorflow/core/kernels/mkl_maxpooling_op.cc | 161 +- .../core/kernels/mkl_pooling_ops_common.cc | 30 +- .../core/kernels/mkl_pooling_ops_common.h | 29 +- .../kernels/mkl_quantized_pooling_ops_test.cc | 201 + tensorflow/core/kernels/mkl_softmax_op.cc | 4 +- .../core/kernels/partitioned_function_ops.cc | 43 +- .../kernels/quantize_and_dequantize_op.cc | 29 +- .../core/kernels/quantize_and_dequantize_op.h | 81 +- .../quantize_and_dequantize_op_gpu.cu.cc | 4 +- .../quantize_and_dequantize_op_test.cc | 136 +- .../quantized_resize_bilinear_op_test.cc | 2 +- tensorflow/core/kernels/ragged_gather_op.cc | 6 +- tensorflow/core/kernels/scan_ops_gpu.cu.cc | 266 +- tensorflow/core/kernels/scan_ops_test.cc | 146 + tensorflow/core/kernels/scatter_nd_op.cc | 164 + tensorflow/core/kernels/stage_op.cc | 2 +- tensorflow/core/kernels/tensor_array_ops.cc | 6 +- tensorflow/core/kernels/tensor_forest/BUILD | 53 + .../kernels/tensor_forest/prediction_ops.cc | 93 + .../kernels/tensor_forest/resource_ops.cc | 135 + .../core/kernels/tensor_forest/resources.cc | 59 + .../core/kernels/tensor_forest/resources.h | 63 + tensorflow/core/kernels/training_ops.cc | 390 + tensorflow/core/kernels/training_ops.h | 23 + .../core/kernels/training_ops_gpu.cu.cc | 62 + tensorflow/core/kernels/training_ops_test.cc | 78 + tensorflow/core/kernels/unicode_ops.cc | 355 +- tensorflow/core/lib/core/threadpool.cc | 21 +- tensorflow/core/lib/core/threadpool.h | 6 +- tensorflow/core/lib/png/png_io.cc | 6 +- tensorflow/core/nccl/BUILD | 38 +- tensorflow/core/nccl/nccl_manager.cc | 66 +- tensorflow/core/nccl/nccl_manager.h | 8 +- tensorflow/core/nccl/nccl_manager_test.cc | 49 +- tensorflow/core/ops/array_ops.cc | 61 +- .../core/ops/compat/ops_history.v1.pbtxt | 4080 +-- tensorflow/core/ops/dataset_ops.cc | 300 - .../core/ops/experimental_dataset_ops.cc | 294 +- tensorflow/core/ops/functional_ops.cc | 4 +- tensorflow/core/ops/list_ops.cc | 249 +- tensorflow/core/ops/nn_ops.cc | 11 +- tensorflow/core/ops/ops.pbtxt | 2419 +- tensorflow/core/ops/sparse_ops.cc | 4 +- tensorflow/core/ops/sparse_ops_test.cc | 7 + tensorflow/core/ops/string_ops.cc | 60 + tensorflow/core/ops/tensor_forest_ops.cc | 79 + tensorflow/core/ops/training_ops.cc | 66 + tensorflow/core/platform/cpu_feature_guard.cc | 2 +- .../core/platform/default/build_config.bzl | 3 + .../core/platform/default/device_tracer.cc | 55 +- tensorflow/core/platform/default/logger.cc | 34 + tensorflow/core/platform/default/logging.cc | 147 +- tensorflow/core/platform/default/logging.h | 44 +- tensorflow/core/platform/env.h | 2 + tensorflow/core/platform/logger.h | 51 + tensorflow/core/platform/numa_test.cc | 2 +- tensorflow/core/platform/platform_strings.cc | 64 + tensorflow/core/platform/platform_strings.h | 364 + .../core/platform/platform_strings_computed.h | 735 + .../core/platform/platform_strings_test.cc | 146 + .../core/platform/posix/posix_file_system.cc | 11 +- tensorflow/core/platform/regexp.h | 3 +- .../platform/windows/windows_file_system.cc | 3 + .../core/profiler/internal/tfprof_code.cc | 2 +- .../core/profiler/internal/tfprof_node.cc | 2 +- tensorflow/core/protobuf/config.proto | 12 + tensorflow/core/protobuf/master.proto | 2 +- .../core/protobuf/rewriter_config.proto | 2 +- tensorflow/core/util/dump_graph.cc | 131 + tensorflow/core/util/dump_graph.h | 52 + tensorflow/core/util/dump_graph_test.cc | 62 + tensorflow/core/util/mkl_util.h | 3 + .../core/util/permutation_input_iterator.h | 6 +- .../core/util/permutation_output_iterator.h | 129 + tensorflow/core/util/sparse/sparse_tensor.h | 50 +- tensorflow/core/util/stats_calculator.cc | 4 +- tensorflow/core/util/strided_slice_op.cc | 21 +- .../core/util/tensor_bundle/tensor_bundle.cc | 4 +- tensorflow/core/util/tensor_format.h | 3 +- tensorflow/core/util/tensor_ops_util.h | 128 + tensorflow/examples/adding_an_op/fact_test.py | 2 + .../examples/adding_an_op/zero_out_1_test.py | 2 + .../examples/adding_an_op/zero_out_2_test.py | 5 + .../examples/adding_an_op/zero_out_3_test.py | 9 +- .../autograph/integration_tests/BUILD | 2 - .../autograph/integration_tests/keras_test.py | 6 +- .../integration_tests/list_literals_test.py | 2 +- .../regression/custom_regression.py | 5 +- .../get_started/regression/dnn_regression.py | 5 +- .../linear_regression_categorical.py | 5 +- .../reading_data/fully_connected_reader.py | 2 +- .../examples/learn/iris_custom_decay_dnn.py | 4 +- .../examples/learn/iris_custom_model.py | 4 +- .../examples/speech_commands/freeze_test.py | 4 + .../speech_commands/input_data_test.py | 11 +- .../speech_commands/label_wav_test.py | 2 +- .../examples/speech_commands/models_test.py | 6 + .../speech_commands/wav_to_features_test.py | 4 +- .../examples/tutorials/layers/cnn_mnist.py | 9 +- tensorflow/go/README.md | 2 +- tensorflow/go/graph.go | 76 +- tensorflow/go/graph_test.go | 258 + tensorflow/go/op/gradients.go | 50 + tensorflow/go/op/gradients_test.go | 246 + tensorflow/go/op/wrappers.go | 2763 +- tensorflow/java/README.md | 2 +- .../test/java/org/tensorflow/TensorTest.java | 46 +- tensorflow/lite/BUILD | 2 + tensorflow/lite/build_def.bzl | 10 + tensorflow/lite/builtin_ops.h | 5 + tensorflow/lite/c/builtin_op_data.h | 18 + tensorflow/lite/c/builtin_op_data_test.cc | 1 + tensorflow/lite/c/c_api_internal.c | 15 +- tensorflow/lite/c/c_api_internal.h | 21 +- tensorflow/lite/c/c_api_internal_test.cc | 1 + .../lite/core/api/flatbuffer_conversions.cc | 35 + tensorflow/lite/core/subgraph.cc | 970 + tensorflow/lite/core/subgraph.h | 501 + tensorflow/lite/delegates/flex/BUILD | 3 + tensorflow/lite/delegates/flex/buffer_map.cc | 5 + tensorflow/lite/delegates/flex/delegate.cc | 54 +- .../lite/delegates/flex/delegate_data.cc | 7 +- .../lite/delegates/flex/delegate_test.cc | 19 + tensorflow/lite/delegates/flex/kernel.cc | 24 +- tensorflow/lite/delegates/flex/kernel_test.cc | 4 +- tensorflow/lite/delegates/flex/test_util.cc | 45 +- tensorflow/lite/delegates/flex/test_util.h | 2 + tensorflow/lite/delegates/flex/util.cc | 4 + .../lite/delegates/nnapi/nnapi_delegate.cc | 2 +- .../delegates/nnapi/nnapi_delegate_test.cc | 16 + .../ios/camera/CameraExampleViewController.h | 4 +- .../ios/camera/CameraExampleViewController.mm | 8 +- tensorflow/lite/examples/ios/camera/Podfile | 2 +- tensorflow/lite/examples/ios/simple/Podfile | 2 +- .../ios/simple/RunModelViewController.mm | 8 +- .../lstm/unidirectional_sequence_lstm_test.py | 2 +- tensorflow/lite/experimental/micro/README.md | 42 + .../micro/examples/micro_speech/.gitignore | 1 + .../micro/examples/micro_speech/BUILD | 211 +- .../examples/micro_speech/CMSIS/README.md | 10 + .../CMSIS/arm_cmplx_mag_squared_q10p6.c | 141 + .../CMSIS/arm_cmplx_mag_squared_q10p6.h | 33 + .../micro_speech/CMSIS/create_constants.py | 62 + .../micro/examples/micro_speech/CMSIS/hann.h | 1 - .../examples/micro_speech/CMSIS/hanning.cc | 67 + .../examples/micro_speech/CMSIS/hanning.h | 24 + .../CMSIS/no_power_spectrum_data.h | 29 - .../micro_speech/CMSIS/preprocessor.cc | 44 +- .../micro_speech/CMSIS/preprocessor_test.cc | 63 - .../examples/micro_speech/CMSIS/sin_1k.cc | 67 + .../examples/micro_speech/CMSIS/sin_1k.h | 24 + .../CMSIS/yes_power_spectrum_data.h | 29 - .../examples/micro_speech/apollo3/.gitignore | 4 + .../examples/micro_speech/apollo3/README.md | 72 + .../micro_speech/apollo3}/_main.c | 0 .../examples/micro_speech/apollo3/apollo3.h | 23332 ++++++++++++++++ .../apollo3/captured_data_to_wav.py | 40 + .../micro_speech/apollo3/compare_1k.py | 153 + .../micro_speech/apollo3/preprocessor_1k.cc | 57 + .../apollo3/preprocessor_1k_cmsis_test.cmd | 44 + .../apollo3/preprocessor_1k_micro_test.cmd | 32 + .../apollo3/preprocessor_test.cmd | 18 + .../apollo3/pushbutton_cmsis_scores.cmd | 33 + .../apollo3/pushbutton_cmsis_voice.cmd | 24 + .../micro_speech/apollo3/pushbutton_main.c | 339 + .../micro_speech/apollo3/pushbutton_test.cc | 120 + .../micro_speech/apollo3/system_apollo3.c | 116 + .../micro_speech/apollo3/system_apollo3.h | 72 + .../examples/micro_speech/audio_provider.cc | 33 + .../examples/micro_speech/audio_provider.h | 36 + .../micro_speech/audio_provider_test.cc | 44 + .../examples/micro_speech/feature_provider.cc | 121 + .../examples/micro_speech/feature_provider.h | 48 + .../micro_speech/feature_provider_test.cc | 38 + .../micro_speech/fixed_point/preprocessor.cc | 44 +- .../micro/examples/micro_speech/main.cc | 112 + ...wer_spectrum_data.cc => model_settings.cc} | 11 +- .../examples/micro_speech/model_settings.h | 42 + .../examples/micro_speech/preprocessor.cc | 46 +- .../examples/micro_speech/preprocessor.h | 8 + .../micro_speech/preprocessor_test.cc | 1 + .../yes_power_spectrum_data.cc => timer.cc} | 10 +- .../micro/examples/micro_speech/timer.h | 31 + .../micro/examples/micro_speech/timer_test.cc | 49 + .../experimental/micro/testing/micro_test.h | 18 + .../experimental/micro/tools/make/Makefile | 63 +- .../tools/make/targets/apollo3evb/README.md | 13 - .../targets/apollo3evb/get_yesno_data.cmd | 10 - .../targets/apollo3evb/prep_apollo3_files.sh | 21 +- .../targets/apollo3evb/preprocessor_test.cmd | 4 - .../apollo3evb/replace_calculated_data.py | 33 - .../make/targets/apollo3evb_makefile.inc | 76 +- .../tools/make/targets/cmsis-dsp_makefile.inc | 9 - .../audio_microfrontend_op_test.py | 4 +- tensorflow/lite/experimental/writer/BUILD | 9 +- .../writer/option_writer_generator.cc | 4 + tensorflow/lite/g3doc/_book.yaml | 6 +- tensorflow/lite/g3doc/_index.yaml | 6 +- tensorflow/lite/g3doc/convert/index.md | 14 +- tensorflow/lite/g3doc/convert/python_api.md | 29 +- tensorflow/lite/g3doc/devguide.md | 181 +- tensorflow/lite/g3doc/models.md | 7 +- tensorflow/lite/g3doc/tf_ops_compatibility.md | 260 +- tensorflow/lite/g3doc/using_select_tf_ops.md | 249 + tensorflow/lite/interpreter.cc | 931 +- tensorflow/lite/interpreter.h | 320 +- tensorflow/lite/interpreter_test.cc | 16 +- .../java/org/tensorflow/lite/DataType.java | 9 +- .../main/java/org/tensorflow/lite/Tensor.java | 2 + tensorflow/lite/java/src/main/native/BUILD | 1 + .../native/nativeinterpreterwrapper_jni.cc | 2 + .../lite/java/src/main/native/tensor_jni.cc | 115 +- .../org/tensorflow/lite/DataTypeTest.java | 1 + .../lite/NativeInterpreterWrapperTest.java | 47 + tensorflow/lite/java/src/testdata/string.bin | Bin 0 -> 584 bytes tensorflow/lite/kernels/BUILD | 57 + tensorflow/lite/kernels/activations.cc | 138 +- tensorflow/lite/kernels/activations_test.cc | 137 +- tensorflow/lite/kernels/conv.cc | 16 +- tensorflow/lite/kernels/eigen_support.cc | 13 +- tensorflow/lite/kernels/elementwise.cc | 13 + tensorflow/lite/kernels/elementwise_test.cc | 13 + tensorflow/lite/kernels/fill.cc | 141 + tensorflow/lite/kernels/fill_test.cc | 94 + tensorflow/lite/kernels/fully_connected.cc | 2 +- tensorflow/lite/kernels/gather.cc | 2 +- tensorflow/lite/kernels/hashtable_lookup.cc | 2 +- tensorflow/lite/kernels/internal/BUILD | 2 - .../kernels/internal/optimized/cblas_conv.h | 109 - .../internal/optimized/cblas_reference.h | 69 - .../internal/optimized/depthwiseconv_float.h | 28 +- .../internal/optimized/depthwiseconv_uint8.h | 28 +- .../depthwiseconv_uint8_3x3_filter.h | 168 +- .../internal/optimized/optimized_ops.h | 58 +- .../internal/reference/reference_ops.h | 79 +- .../lite/kernels/internal/tensor_ctypes.h | 10 + tensorflow/lite/kernels/internal/types.h | 12 + tensorflow/lite/kernels/mirror_pad.cc | 374 + tensorflow/lite/kernels/mirror_pad_test.cc | 189 + tensorflow/lite/kernels/pooling_test.cc | 43 + tensorflow/lite/kernels/reduce.cc | 36 +- tensorflow/lite/kernels/register.cc | 12 + tensorflow/lite/kernels/register.h | 1 - tensorflow/lite/kernels/skip_gram.cc | 4 +- tensorflow/lite/kernels/split_v.cc | 207 + tensorflow/lite/kernels/split_v_test.cc | 175 + tensorflow/lite/kernels/squared_difference.cc | 129 + .../lite/kernels/squared_difference_test.cc | 157 + tensorflow/lite/kernels/test_util.h | 3 +- .../lite/lib_package/create_ios_frameworks.sh | 6 +- .../android/smartreply/SmartReplyClient.java | 21 +- .../lite/models/smartreply/ops/normalize.cc | 2 +- .../lite/models/smartreply/predictor.cc | 2 +- tensorflow/lite/nnapi_delegate.cc | 92 +- tensorflow/lite/nnapi_delegate.h | 5 +- tensorflow/lite/nnapi_delegate_disabled.cc | 6 +- tensorflow/lite/optional_debug_tools.cc | 2 + tensorflow/lite/python/BUILD | 2 + tensorflow/lite/python/convert.py | 60 +- tensorflow/lite/python/convert_saved_model.py | 21 +- .../lite/python/convert_saved_model_test.py | 24 +- tensorflow/lite/python/convert_test.py | 58 + .../interpreter_wrapper.cc | 4 + tensorflow/lite/python/lite.py | 17 +- tensorflow/lite/python/lite_constants.py | 25 +- tensorflow/lite/python/lite_test.py | 30 +- tensorflow/lite/python/op_hint.py | 6 +- tensorflow/lite/python/tflite_convert.py | 33 +- tensorflow/lite/schema/schema.fbs | 36 + tensorflow/lite/schema/schema_generated.h | 652 +- tensorflow/lite/string_util.cc | 7 +- tensorflow/lite/string_util.h | 12 +- tensorflow/lite/string_util_test.cc | 37 +- tensorflow/lite/testing/generate_examples.py | 427 +- .../testing/generated_examples_zip_test.cc | 4 + tensorflow/lite/testing/tflite_driver.cc | 11 +- tensorflow/lite/testing/tflite_driver.h | 14 +- tensorflow/lite/toco/BUILD | 46 + tensorflow/lite/toco/README.md | 8 +- tensorflow/lite/toco/export_tensorflow.cc | 48 +- .../fuse_binary_into_following_affine.cc | 10 +- .../propagate_array_data_types.cc | 7 + .../propagate_fixed_sizes.cc | 147 + .../toco/graph_transformations/quantize.cc | 5 +- .../resolve_constant_gather.cc | 4 + .../resolve_reduce_attributes.cc | 5 + .../unroll_batch_matmul.cc | 3 +- tensorflow/lite/toco/import_tensorflow.cc | 72 +- tensorflow/lite/toco/model.h | 65 +- tensorflow/lite/toco/tflite/export.cc | 31 +- tensorflow/lite/toco/tflite/export_test.cc | 40 +- tensorflow/lite/toco/tflite/operator.cc | 95 +- tensorflow/lite/toco/tflite/operator_test.cc | 32 + .../lite/toco/tflite/whitelisted_flex_ops.cc | 1 + tensorflow/lite/toco/toco.cc | 82 +- tensorflow/lite/toco/toco_convert.cc | 108 + tensorflow/lite/toco/toco_convert.h | 34 + tensorflow/lite/toco/toco_convert_test.cc | 173 + tensorflow/lite/toco/toco_tooling.cc | 7 +- tensorflow/lite/toco/tooling_util.cc | 52 +- tensorflow/lite/toco/types.proto | 3 + .../lite/tools/benchmark/benchmark_model.cc | 49 +- .../lite/tools/benchmark/benchmark_model.h | 3 +- .../lite/tools/benchmark/benchmark_test.cc | 2 + .../tools/benchmark/benchmark_tflite_model.cc | 11 +- .../tools/benchmark/benchmark_tflite_model.h | 7 +- .../TFLiteBenchmark.xcodeproj/project.pbxproj | 30 +- tensorflow/lite/tools/make/Makefile | 8 + .../lite/tools/make/targets/ios_makefile.inc | 2 +- .../tools/optimize/g3doc/quantize_weights.md | 2 +- tensorflow/lite/tools/pip_package/MANIFEST.in | 1 + tensorflow/lite/tools/pip_package/README.md | 33 + .../tools/pip_package/build_pip_package.sh | 54 + tensorflow/lite/tools/pip_package/setup.py | 150 + tensorflow/lite/tutorials/mnist_tflite.py | 4 +- tensorflow/lite/util.h | 6 + tensorflow/opensource_only.files | 17 + tensorflow/python/BUILD | 199 +- tensorflow/python/__init__.py | 6 +- tensorflow/python/autograph/converters/BUILD | 2 - .../autograph/converters/asserts_test.py | 4 +- .../converters/builtin_functions_test.py | 4 + .../python/autograph/converters/call_trees.py | 22 +- .../autograph/converters/call_trees_test.py | 34 +- .../converters/continue_statements.py | 91 +- .../autograph/converters/control_flow.py | 42 +- .../autograph/converters/control_flow_test.py | 13 + .../converters/function_scopes_test.py | 5 + .../python/autograph/converters/lists_test.py | 8 +- .../converters/logical_expressions_test.py | 4 + .../converters/side_effect_guards_test.py | 33 +- .../autograph/converters/slices_test.py | 2 +- tensorflow/python/autograph/core/converter.py | 13 +- .../autograph/core/converter_testing.py | 6 +- .../python/autograph/core/errors_test.py | 10 +- .../autograph/core/function_wrapping_test.py | 2 + tensorflow/python/autograph/core/naming.py | 5 +- tensorflow/python/autograph/impl/BUILD | 2 - tensorflow/python/autograph/impl/api.py | 11 + tensorflow/python/autograph/impl/api_test.py | 77 +- .../python/autograph/impl/conversion.py | 35 +- .../autograph/lang/special_functions_test.py | 12 +- .../autograph/operators/control_flow.py | 4 +- .../autograph/operators/control_flow_test.py | 20 +- .../operators/data_structures_test.py | 27 +- .../autograph/operators/exceptions_test.py | 9 +- .../autograph/operators/logical_test.py | 17 +- .../autograph/operators/py_builtins_test.py | 32 +- .../python/autograph/operators/slices_test.py | 8 +- tensorflow/python/autograph/pyct/BUILD | 2 - .../python/autograph/pyct/inspect_utils.py | 22 + .../autograph/pyct/inspect_utils_test.py | 37 +- .../autograph/pyct/static_analysis/BUILD | 2 - .../pyct/static_analysis/liveness.py | 14 + .../python/autograph/utils/misc_test.py | 7 +- .../python/autograph/utils/py_func_test.py | 18 +- .../autograph/utils/tensor_list_test.py | 33 +- .../python/autograph/utils/type_check.py | 2 +- .../python/autograph/utils/type_check_test.py | 1 + tensorflow/python/client/device_lib.i | 7 +- tensorflow/python/client/session.py | 4 +- .../client/session_clusterspec_prop_test.py | 6 +- .../python/client/session_partial_run_test.py | 26 +- tensorflow/python/client/timeline_test.py | 5 +- tensorflow/python/client/virtual_gpu_test.py | 2 +- tensorflow/python/compat/BUILD | 5 +- tensorflow/python/compat/compat.py | 45 +- tensorflow/python/data/__init__.py | 2 + tensorflow/python/data/benchmarks/BUILD | 55 + .../python/data/benchmarks/batch_benchmark.py | 85 + .../data/benchmarks/filter_benchmark.py | 69 + .../from_tensor_slices_benchmark.py | 188 + .../python/data/benchmarks/map_benchmark.py | 135 + .../python/data/benchmarks/range_benchmark.py | 2 +- .../python/data/experimental/__init__.py | 15 +- .../python/data/experimental/benchmarks/BUILD | 96 +- .../benchmarks/autotune_benchmark.py | 187 + .../benchmarks/csv_dataset_benchmark.py | 130 + .../benchmarks/map_and_batch_benchmark.py | 131 +- .../experimental/benchmarks/map_benchmark.py | 245 - .../benchmarks/map_vectorization_benchmark.py | 194 + .../benchmarks/matching_files_benchmark.py | 102 + .../benchmarks/optimize_benchmark.py | 120 + .../benchmarks/unbatch_benchmark.py | 107 + .../data/experimental/kernel_tests/BUILD | 60 +- .../kernel_tests/batch_dataset_op_test.py | 688 - .../bucket_by_sequence_length_test.py | 22 +- .../kernel_tests/cardinality_test.py | 158 + .../kernel_tests/copy_to_device_test.py | 238 +- .../experimental/kernel_tests/counter_test.py | 23 +- .../kernel_tests/csv_dataset_test.py | 99 - .../dense_to_sparse_batch_test.py | 41 +- .../directed_interleave_dataset_test.py | 24 +- .../kernel_tests/enumerate_dataset_test.py | 15 +- .../kernel_tests/filter_dataset_op_test.py | 6 +- .../function_buffering_resource_test.py | 248 - .../kernel_tests/get_single_element_test.py | 13 + .../kernel_tests/group_by_reducer_test.py | 22 +- .../kernel_tests/group_by_window_test.py | 102 +- .../kernel_tests/ignore_errors_test.py | 34 +- .../kernel_tests/indexed_dataset_ops_test.py | 14 +- .../make_batched_features_dataset_test.py | 103 +- .../kernel_tests/make_csv_dataset_test.py | 32 +- .../make_tf_record_dataset_test.py | 32 +- .../kernel_tests/map_and_batch_test.py | 109 +- .../kernel_tests/map_defun_op_test.py | 6 +- .../kernel_tests/matching_files_test.py} | 125 +- .../kernel_tests/optimization/BUILD | 25 +- .../optimization/assert_next_dataset_test.py | 36 +- .../optimization/filter_fusion_test.py | 41 +- .../optimization/hoist_random_uniform_test.py | 41 +- .../optimization/latency_all_edges_test.py | 58 +- .../optimization/make_numa_aware_test.py | 12 +- .../optimization/map_and_batch_fusion_test.py | 16 +- .../map_and_filter_fusion_test.py | 34 +- .../optimization/map_fusion_test.py | 32 +- .../optimization/map_parallelization_test.py | 25 +- .../optimization/map_vectorization_test.py | 118 +- .../optimization/model_dataset_test.py | 171 +- .../optimization/noop_elimination_test.py | 18 +- .../optimization/optimize_dataset_test.py | 197 +- .../shuffle_and_repeat_fusion_test.py | 26 +- .../kernel_tests/override_threadpool_test.py | 108 +- .../kernel_tests/parallel_interleave_test.py | 85 +- .../parse_example_dataset_test.py | 4 + .../kernel_tests/prefetch_to_device_test.py | 116 +- .../kernel_tests/rejection_resample_test.py | 14 +- .../kernel_tests/restructured_dataset_test.py | 2 + .../experimental/kernel_tests/scan_test.py | 32 +- .../kernel_tests/serialization/BUILD | 11 +- .../checkpoint_input_pipeline_hook_test.py | 8 +- .../dataset_serialization_test_base.py | 3 +- .../filter_dataset_serialization_test.py | 6 +- ...tching_files_dataset_serialization_test.py | 4 +- .../range_dataset_serialization_test.py | 34 +- .../serialization_integration_test.py | 6 +- .../shuffle_dataset_serialization_test.py | 4 +- .../kernel_tests/shuffle_and_repeat_test.py | 15 +- .../experimental/kernel_tests/sleep_test.py | 10 +- .../kernel_tests/sql_dataset_test.py | 169 +- .../kernel_tests/sql_dataset_test_base.py | 3 +- .../kernel_tests/stats_dataset_ops_test.py | 200 +- .../kernel_tests/stats_dataset_test_base.py | 3 +- .../experimental/kernel_tests/unbatch_test.py | 137 +- .../experimental/kernel_tests/unique_test.py | 11 +- tensorflow/python/data/experimental/ops/BUILD | 90 +- .../python/data/experimental/ops/batching.py | 54 +- .../data/experimental/ops/cardinality.py | 50 + .../python/data/experimental/ops/counter.py | 14 +- .../data/experimental/ops/enumerate_ops.py | 4 +- .../python/data/experimental/ops/error_ops.py | 14 +- .../experimental/ops/filter_for_shard_ops.py | 106 + .../experimental/ops/get_single_element.py | 2 +- .../python/data/experimental/ops/grouping.py | 142 +- .../experimental/ops/indexed_dataset_ops.py | 2 + .../data/experimental/ops/interleave_ops.py | 30 +- .../data/experimental/ops/matching_files.py | 51 + .../data/experimental/ops/optimization.py | 28 +- .../experimental/ops/optimization_options.py | 83 + .../data/experimental/ops/parsing_ops.py | 8 +- .../data/experimental/ops/prefetching_ops.py | 342 +- .../data/experimental/ops/random_ops.py | 27 +- .../python/data/experimental/ops/readers.py | 176 +- .../python/data/experimental/ops/scan_ops.py | 15 +- .../data/experimental/ops/shuffle_ops.py | 14 +- .../python/data/experimental/ops/sleep.py | 2 +- .../data/experimental/ops/stats_aggregator.py | 7 +- .../python/data/experimental/ops/stats_ops.py | 26 +- .../data/experimental/ops/stats_options.py | 84 +- .../experimental/ops/threading_options.py | 50 + .../data/experimental/ops/threadpool.py | 14 +- .../python/data/experimental/ops/unique.py | 14 +- .../python/data/experimental/ops/writers.py | 6 +- tensorflow/python/data/kernel_tests/BUILD | 422 +- .../kernel_tests/batch_dataset_op_test.py | 515 - .../python/data/kernel_tests/batch_test.py | 173 + .../kernel_tests/cache_dataset_op_test.py | 318 - .../python/data/kernel_tests/cache_test.py | 253 + ...dataset_op_test.py => concatenate_test.py} | 66 +- ..._op_test.py => dataset_checkpoint_test.py} | 71 +- .../dataset_constructor_op_test.py | 650 - .../{dataset_ops_test.py => dataset_test.py} | 87 +- .../kernel_tests/filter_dataset_op_test.py | 220 - .../python/data/kernel_tests/filter_test.py | 128 + .../fixed_length_record_dataset_test.py | 171 + ...ap_dataset_op_test.py => flat_map_test.py} | 104 +- ...ator_op_test.py => from_generator_test.py} | 103 +- .../from_sparse_tensor_slices_test.py | 86 + .../kernel_tests/from_tensor_slices_test.py | 177 + .../data/kernel_tests/from_tensors_test.py | 259 + .../python/data/kernel_tests/inputs_test.py | 149 - ..._dataset_op_test.py => interleave_test.py} | 77 +- .../kernel_tests/iterator_checkpoint_test.py | 129 + ...uster_test.py => iterator_cluster_test.py} | 8 +- ...{iterator_ops_test.py => iterator_test.py} | 208 +- .../list_files_dataset_op_test.py | 291 - .../data/kernel_tests/list_files_test.py | 214 + .../{map_dataset_op_test.py => map_test.py} | 272 +- .../multi_device_iterator_test.py | 122 +- ...{optional_ops_test.py => optional_test.py} | 137 +- .../data/kernel_tests/padded_batch_test.py | 243 + ...ch_dataset_op_test.py => prefetch_test.py} | 34 +- .../python/data/kernel_tests/range_test.py | 72 + .../kernel_tests/reader_dataset_ops_test.py | 846 - ...duce_dataset_op_test.py => reduce_test.py} | 45 +- .../python/data/kernel_tests/repeat_test.py | 84 + .../kernel_tests/sequence_dataset_op_test.py | 210 - ...shard_dataset_op_test.py => shard_test.py} | 58 +- .../kernel_tests/shuffle_dataset_op_test.py | 278 - .../python/data/kernel_tests/shuffle_test.py | 249 + .../python/data/kernel_tests/skip_test.py | 62 + .../python/data/kernel_tests/take_test.py | 55 + .../python/data/kernel_tests/test_base.py | 102 +- .../kernel_tests/text_line_dataset_test.py | 165 + .../kernel_tests/tf_record_dataset_test.py | 170 + .../kernel_tests/window_dataset_op_test.py | 291 - .../python/data/kernel_tests/window_test.py | 231 + .../data/kernel_tests/zip_dataset_op_test.py | 115 - .../python/data/kernel_tests/zip_test.py | 101 + tensorflow/python/data/ops/BUILD | 5 + tensorflow/python/data/ops/dataset_ops.py | 1569 +- tensorflow/python/data/ops/iterator_ops.py | 2 +- tensorflow/python/data/ops/optional_ops.py | 16 +- tensorflow/python/data/ops/readers.py | 126 +- tensorflow/python/data/util/BUILD | 17 + tensorflow/python/data/util/convert_test.py | 150 +- tensorflow/python/data/util/options.py | 131 + tensorflow/python/data/util/options_test.py | 96 + tensorflow/python/data/util/sparse.py | 2 +- tensorflow/python/data/util/sparse_test.py | 9 +- tensorflow/python/data/util/structure.py | 69 +- tensorflow/python/data/util/structure_test.py | 71 +- tensorflow/python/debug/BUILD | 2 + .../python/debug/cli/analyzer_cli_test.py | 35 +- .../python/debug/cli/cli_shared_test.py | 11 + .../debug/cli/profile_analyzer_cli_test.py | 5 + tensorflow/python/debug/lib/common_test.py | 3 + .../python/debug/lib/debug_gradients_test.py | 14 + .../lib/debug_graph_reconstruction_test.py | 14 +- .../python/debug/lib/debug_utils_test.py | 10 + .../debug/lib/session_debug_file_test.py | 2 + .../debug/lib/session_debug_multi_gpu_test.py | 2 +- .../python/debug/lib/source_utils_test.py | 5 +- .../python/debug/wrappers/framework_test.py | 2 +- tensorflow/python/distribute/BUILD | 240 +- tensorflow/python/distribute/all_reduce.py | 860 + .../distribute}/all_reduce_test.py | 11 +- .../python/distribute/cluster_resolver/BUILD | 180 + .../distribute}/cluster_resolver/README.md | 0 .../distribute/cluster_resolver}/README.slurm | 0 .../distribute/cluster_resolver/__init__.py | 57 + .../cluster_resolver/cluster_resolver.py | 374 + .../cluster_resolver_test.py | 4 +- .../cluster_resolver/gce_cluster_resolver.py | 206 + .../gce_cluster_resolver_test.py | 4 +- .../kubernetes_cluster_resolver.py | 173 + .../kubernetes_cluster_resolver_test.py | 19 +- .../slurm_cluster_resolver.py | 226 + .../slurm_cluster_resolver_test.py | 27 +- .../tfconfig_cluster_resolver.py | 171 + .../tfconfig_cluster_resolver_test.py | 54 +- .../cluster_resolver/tpu_cluster_resolver.py | 423 + .../tpu_cluster_resolver_test.py | 64 +- .../distribute/cross_device_ops.py} | 161 +- .../distribute/cross_device_utils.py} | 8 +- .../{training => distribute}/device_util.py | 2 +- .../device_util_test.py | 5 +- .../distribute/distribute_coordinator.py | 27 +- .../distribute/distribute_coordinator_test.py | 78 +- .../python/distribute/distribute_lib.py | 1682 ++ .../distribute_lib_test.py} | 47 +- .../distribution_strategy_context.py | 236 + .../python/distribute/estimator_training.py | 4 +- .../python => python/distribute}/input_ops.py | 20 +- .../distribute}/input_ops_test.py | 35 +- .../python/distribute/mirrored_strategy.py | 908 + .../python/distribute/multi_worker_util.py | 98 +- .../distribute/multi_worker_util_test.py | 91 +- tensorflow/python/distribute/reduce_util.py | 53 + .../distribute}/shared_variable_creator.py | 0 .../shared_variable_creator_test.py | 2 +- .../python => python/distribute}/values.py | 454 +- tensorflow/python/eager/BUILD | 32 +- tensorflow/python/eager/backprop.py | 231 +- tensorflow/python/eager/backprop_test.py | 253 +- tensorflow/python/eager/benchmarks_test.py | 58 +- tensorflow/python/eager/context.py | 55 +- tensorflow/python/eager/def_function.py | 8 +- tensorflow/python/eager/def_function_test.py | 21 +- .../python/eager/execution_callbacks.py | 45 +- .../python/eager/execution_callbacks_test.py | 55 + tensorflow/python/eager/function.py | 151 +- .../python/eager/function_gradients_test.py | 756 + tensorflow/python/eager/function_test.py | 745 +- .../python/eager/graph_only_ops_test.py | 4 +- tensorflow/python/eager/pywrap_tensor.cc | 30 +- tensorflow/python/eager/pywrap_tfe_src.cc | 32 +- tensorflow/python/eager/tape.py | 4 +- tensorflow/python/eager/tape_test.py | 8 +- tensorflow/python/eager/tensor_test.py | 47 +- tensorflow/python/eager/test.py | 2 +- tensorflow/python/eager/wrap_function.py | 18 + .../python/feature_column/feature_column.py | 242 +- .../feature_column/feature_column_lib.py | 1 + .../feature_column/feature_column_test.py | 1881 +- .../feature_column/feature_column_v2.py | 491 +- .../feature_column/feature_column_v2_test.py | 3191 ++- .../python/framework/auto_control_deps.py | 13 + tensorflow/python/framework/constant_op.py | 81 +- tensorflow/python/framework/device.py | 2 +- tensorflow/python/framework/dtypes.py | 10 +- tensorflow/python/framework/dtypes_test.py | 4 +- .../python/framework/file_system_test.py | 4 +- tensorflow/python/framework/func_graph.py | 72 +- tensorflow/python/framework/function.py | 4 +- .../framework/function_def_to_graph_test.py | 3 + tensorflow/python/framework/function_test.py | 92 +- .../python/framework/graph_util_test.py | 14 +- tensorflow/python/framework/importer.py | 24 +- tensorflow/python/framework/importer_test.py | 20 +- tensorflow/python/framework/load_library.py | 4 +- .../python/framework/meta_graph_test.py | 39 +- tensorflow/python/framework/op_def_library.py | 2 +- tensorflow/python/framework/ops.py | 355 +- tensorflow/python/framework/ops_test.py | 254 +- tensorflow/python/framework/python_op_gen.cc | 28 +- .../framework/python_op_gen_internal.cc | 6 +- tensorflow/python/framework/random_seed.py | 108 +- tensorflow/python/framework/registry.py | 11 +- tensorflow/python/framework/registry_test.py | 4 +- .../python/framework/smart_cond_test.py | 13 +- .../python/framework/sparse_tensor_test.py | 8 +- tensorflow/python/framework/subscribe_test.py | 38 +- tensorflow/python/framework/tensor_shape.py | 2 +- tensorflow/python/framework/tensor_spec.py | 40 +- .../python/framework/tensor_spec_test.py | 25 +- tensorflow/python/framework/tensor_util.py | 28 +- .../python/framework/tensor_util_test.py | 14 +- tensorflow/python/framework/test_util.py | 301 +- tensorflow/python/framework/test_util_test.py | 7 +- .../python/grappler/constant_folding_test.py | 2 +- .../python/grappler/cost_analyzer_test.py | 8 +- .../python/grappler/cost_analyzer_tool.py | 9 +- tensorflow/python/grappler/datasets_test.py | 20 +- tensorflow/python/grappler/graph_placer.py | 6 +- tensorflow/python/grappler/item_test.py | 2 + .../python/grappler/layout_optimizer_test.py | 71 +- .../python/grappler/memory_optimizer_test.py | 80 +- .../python/grappler/model_analyzer_test.py | 3 + tensorflow/python/grappler/tf_optimizer.i | 19 +- tensorflow/python/grappler/tf_optimizer.py | 8 +- .../python/grappler/tf_optimizer_test.py | 36 +- tensorflow/python/keras/BUILD | 67 +- tensorflow/python/keras/activations_test.py | 4 + tensorflow/python/keras/backend.py | 135 +- tensorflow/python/keras/backend_test.py | 139 +- tensorflow/python/keras/callbacks.py | 80 +- tensorflow/python/keras/callbacks_test.py | 84 +- tensorflow/python/keras/engine/__init__.py | 2 +- tensorflow/python/keras/engine/base_layer.py | 2093 +- .../python/keras/engine/base_layer_test.py | 8 +- .../python/keras/engine/base_layer_utils.py | 236 + .../engine/distributed_training_utils.py | 273 +- .../feature_columns_integration_test.py | 29 +- tensorflow/python/keras/engine/input_layer.py | 29 +- tensorflow/python/keras/engine/input_spec.py | 170 + tensorflow/python/keras/engine/network.py | 317 +- tensorflow/python/keras/engine/saving.py | 10 +- tensorflow/python/keras/engine/saving_test.py | 62 +- tensorflow/python/keras/engine/sequential.py | 65 +- .../python/keras/engine/sequential_test.py | 28 +- .../python/keras/engine/topology_test.py | 10 + tensorflow/python/keras/engine/training.py | 980 +- .../python/keras/engine/training_arrays.py | 215 +- .../keras/engine/training_dataset_test.py | 344 + .../keras/engine/training_distributed.py | 633 +- .../python/keras/engine/training_eager.py | 696 +- .../keras/engine/training_eager_test.py | 63 +- .../python/keras/engine/training_generator.py | 789 +- .../keras/engine/training_generator_test.py | 446 +- .../python/keras/engine/training_gpu_test.py | 2 +- .../python/keras/engine/training_test.py | 787 +- .../python/keras/engine/training_utils.py | 391 +- .../keras/engine/training_utils_test.py | 178 +- tensorflow/python/keras/estimator/__init__.py | 61 +- tensorflow/python/keras/initializers_test.py | 11 + tensorflow/python/keras/integration_test.py | 17 + tensorflow/python/keras/layers/__init__.py | 2 +- .../keras/layers/advanced_activations.py | 8 +- .../python/keras/layers/convolutional.py | 12 +- .../keras/layers/convolutional_recurrent.py | 2 +- tensorflow/python/keras/layers/core.py | 11 +- .../python/keras/layers/cudnn_recurrent.py | 16 +- tensorflow/python/keras/layers/embeddings.py | 7 +- tensorflow/python/keras/layers/local.py | 4 +- tensorflow/python/keras/layers/local_test.py | 280 +- tensorflow/python/keras/layers/lstm_test.py | 23 +- tensorflow/python/keras/layers/merge.py | 9 +- tensorflow/python/keras/layers/merge_test.py | 1 + tensorflow/python/keras/layers/noise.py | 3 - .../python/keras/layers/normalization.py | 126 +- .../python/keras/layers/normalization_test.py | 114 +- tensorflow/python/keras/layers/pooling.py | 8 +- tensorflow/python/keras/layers/recurrent.py | 482 +- .../python/keras/layers/recurrent_test.py | 30 +- .../python/keras/layers/simplernn_test.py | 3 + .../python/keras/layers/unified_lstm_test.py | 724 + tensorflow/python/keras/layers/wrappers.py | 6 +- .../python/keras/layers/wrappers_test.py | 36 +- tensorflow/python/keras/losses.py | 420 +- tensorflow/python/keras/losses_test.py | 633 + tensorflow/python/keras/metrics.py | 1113 +- tensorflow/python/keras/metrics_test.py | 997 +- .../python/keras/model_subclassing_test.py | 132 +- tensorflow/python/keras/models.py | 36 +- tensorflow/python/keras/models_test.py | 55 +- tensorflow/python/keras/optimizer_v2/BUILD | 150 +- .../python/keras/optimizer_v2/adadelta.py | 148 + .../keras/optimizer_v2/adadelta_test.py | 170 + .../python/keras/optimizer_v2/adagrad.py | 171 + .../python/keras/optimizer_v2/adagrad_test.py | 400 + tensorflow/python/keras/optimizer_v2/adam.py | 182 +- .../python/keras/optimizer_v2/adam_test.py | 508 + .../python/keras/optimizer_v2/adamax.py | 159 + .../python/keras/optimizer_v2/adamax_test.py | 367 + tensorflow/python/keras/optimizer_v2/ftrl.py | 210 + .../python/keras/optimizer_v2/ftrl_test.py | 440 + .../keras/optimizer_v2/gradient_descent.py | 39 +- .../optimizer_v2/gradient_descent_test.py | 212 +- tensorflow/python/keras/optimizer_v2/nadam.py | 143 + .../python/keras/optimizer_v2/nadam_test.py | 213 + .../python/keras/optimizer_v2/optimizer_v2.py | 82 +- .../keras/optimizer_v2/optimizer_v2_test.py | 259 +- .../python/keras/optimizer_v2/rmsprop.py | 196 + .../python/keras/optimizer_v2/rmsprop_test.py | 410 + tensorflow/python/keras/optimizers.py | 54 +- tensorflow/python/keras/optimizers_test.py | 47 + tensorflow/python/keras/regularizers_test.py | 2 + tensorflow/python/keras/testing_utils.py | 4 + tensorflow/python/keras/utils/__init__.py | 1 + .../python/keras/utils/generic_utils.py | 12 +- tensorflow/python/keras/utils/layer_utils.py | 2 +- tensorflow/python/keras/utils/losses_utils.py | 189 + .../keras/utils/multi_gpu_utils_test.py | 2 +- tensorflow/python/keras/utils/tf_utils.py | 44 +- .../python/keras/utils/tf_utils_test.py | 134 + tensorflow/python/kernel_tests/BUILD | 82 +- .../python/kernel_tests/accumulate_n_test.py | 10 +- .../python/kernel_tests/ackermann_test.py | 2 + .../python/kernel_tests/aggregate_ops_test.py | 5 +- .../python/kernel_tests/argmax_op_test.py | 11 +- .../python/kernel_tests/array_ops_test.py | 151 +- .../python/kernel_tests/as_string_op_test.py | 7 + .../python/kernel_tests/atrous_conv2d_test.py | 18 +- .../python/kernel_tests/attention_ops_test.py | 5 +- .../python/kernel_tests/barrier_ops_test.py | 24 +- .../python/kernel_tests/base64_ops_test.py | 2 +- .../python/kernel_tests/basic_gpu_test.py | 13 +- .../kernel_tests/batch_gather_op_test.py | 10 +- .../kernel_tests/batch_matmul_op_test.py | 2 +- .../kernel_tests/batch_scatter_ops_test.py | 6 +- .../kernel_tests/batchtospace_op_test.py | 21 + .../python/kernel_tests/bcast_ops_test.py | 6 + .../python/kernel_tests/benchmark_test.py | 15 +- .../python/kernel_tests/betainc_op_test.py | 10 +- .../python/kernel_tests/bias_op_test.py | 12 + .../python/kernel_tests/bincount_op_test.py | 7 + .../python/kernel_tests/bitcast_op_test.py | 7 +- .../boosted_trees/prediction_ops_test.py | 15 + .../boosted_trees/quantile_ops_test.py | 18 +- .../boosted_trees/resource_ops_test.py | 45 +- .../boosted_trees/stats_ops_test.py | 65 +- .../boosted_trees/training_ops_test.py | 10 + .../kernel_tests/broadcast_to_ops_test.py | 11 + .../python/kernel_tests/bucketize_op_test.py | 10 +- .../candidate_sampler_ops_test.py | 13 +- .../python/kernel_tests/cast_op_test.py | 17 +- .../python/kernel_tests/check_ops_test.py | 109 + .../kernel_tests/checkpoint_ops_test.py | 53 +- .../python/kernel_tests/cholesky_op_test.py | 15 +- .../python/kernel_tests/clip_ops_test.py | 74 +- .../compare_and_bitpack_op_test.py | 7 +- .../python/kernel_tests/concat_op_test.py | 176 +- .../python/kernel_tests/cond_v2_test.py | 130 +- .../conditional_accumulator_test.py | 66 +- .../kernel_tests/confusion_matrix_test.py | 57 +- .../python/kernel_tests/constant_op_test.py | 114 +- .../kernel_tests/control_flow_ops_py_test.py | 555 +- tensorflow/python/kernel_tests/conv1d_test.py | 4 +- .../conv2d_backprop_filter_grad_test.py | 2 + .../kernel_tests/conv2d_transpose_test.py | 14 +- .../conv3d_backprop_filter_v2_grad_test.py | 2 + .../kernel_tests/conv3d_transpose_test.py | 11 +- .../python/kernel_tests/conv_ops_3d_test.py | 31 +- .../python/kernel_tests/conv_ops_test.py | 18 +- .../python/kernel_tests/cross_grad_test.py | 2 + .../kernel_tests/ctc_decoder_ops_test.py | 3 + .../python/kernel_tests/ctc_loss_op_test.py | 584 +- .../kernel_tests/cwise_ops_binary_test.py | 123 +- .../python/kernel_tests/cwise_ops_test.py | 133 +- .../kernel_tests/cwise_ops_unary_test.py | 30 +- .../python/kernel_tests/decode_bmp_op_test.py | 4 +- .../kernel_tests/decode_compressed_op_test.py | 3 + .../kernel_tests/decode_image_op_test.py | 18 +- .../kernel_tests/decode_jpeg_op_test.py | 6 +- .../python/kernel_tests/decode_png_op_test.py | 2 +- .../python/kernel_tests/decode_raw_op_test.py | 7 + .../python/kernel_tests/denormal_test.py | 3 + .../dense_update_ops_no_tsan_test.py | 21 +- .../kernel_tests/dense_update_ops_test.py | 21 +- .../kernel_tests/depthtospace_op_test.py | 31 +- .../kernel_tests/depthwise_conv_op_test.py | 14 +- .../kernel_tests/determinant_op_test.py | 11 +- .../python/kernel_tests/diag_op_test.py | 44 +- .../distributions/bernoulli_test.py | 5 + .../distributions/bijector_test.py | 2 + .../distributions/categorical_test.py | 39 +- .../dirichlet_multinomial_test.py | 43 +- .../distributions/identity_bijector_test.py | 2 + .../distributions/kullback_leibler_test.py | 10 +- .../distributions/multinomial_test.py | 12 +- .../kernel_tests/distributions/normal_test.py | 1 + .../distributions/special_math_test.py | 15 +- .../kernel_tests/distributions/util_test.py | 52 +- .../kernel_tests/division_future_test.py | 2 +- .../python/kernel_tests/division_past_test.py | 2 +- .../kernel_tests/draw_bounding_box_op_test.py | 2 +- .../python/kernel_tests/duplicate_op_test.py | 2 + .../kernel_tests/dynamic_partition_op_test.py | 40 +- .../kernel_tests/dynamic_stitch_op_test.py | 49 +- .../kernel_tests/edit_distance_op_test.py | 4 +- .../python/kernel_tests/embedding_ops_test.py | 64 +- .../extract_image_patches_grad_test.py | 3 + .../extract_image_patches_op_test.py | 5 +- .../extract_volume_patches_op_test.py | 5 +- .../python/kernel_tests/fifo_queue_test.py | 190 +- .../fractional_avg_pool_op_test.py | 95 +- .../fractional_max_pool_op_test.py | 67 +- .../kernel_tests/functional_ops_test.py | 90 +- .../python/kernel_tests/gather_nd_op_test.py | 57 +- .../python/kernel_tests/gather_op_test.py | 21 +- .../kernel_tests/gradient_correctness_test.py | 19 +- .../python/kernel_tests/huge_slice_op_test.py | 4 +- .../kernel_tests/identity_n_op_py_test.py | 4 + .../kernel_tests/identity_op_py_test.py | 5 + .../python/kernel_tests/in_topk_op_test.py | 4 +- .../python/kernel_tests/init_ops_test.py | 102 +- .../python/kernel_tests/inplace_ops_test.py | 9 +- tensorflow/python/kernel_tests/io_ops_test.py | 7 +- .../kernel_tests/large_concat_op_test.py | 2 +- tensorflow/python/kernel_tests/linalg/BUILD | 63 +- .../linalg/linear_operator_addition_test.py | 13 + .../linalg/linear_operator_adjoint_test.py | 118 + .../linalg/linear_operator_algebra_test.py | 133 + .../linalg/linear_operator_block_diag_test.py | 45 +- .../linalg/linear_operator_circulant_test.py | 116 +- .../linear_operator_composition_test.py | 10 +- .../linalg/linear_operator_diag_test.py | 61 +- .../linear_operator_full_matrix_test.py | 27 +- .../linalg/linear_operator_identity_test.py | 97 +- .../linalg/linear_operator_inversion_test.py | 130 + .../linalg/linear_operator_kronecker_test.py | 54 +- .../linear_operator_low_rank_update_test.py | 13 +- .../linear_operator_lower_triangular_test.py | 30 + .../linalg/linear_operator_test.py | 86 +- .../linalg/linear_operator_util_test.py | 53 +- .../linalg/linear_operator_zeros_test.py | 25 +- .../python/kernel_tests/linalg_grad_test.py | 6 +- .../python/kernel_tests/linalg_ops_test.py | 21 +- .../python/kernel_tests/list_ops_test.py | 408 +- .../python/kernel_tests/listdiff_op_test.py | 2 +- .../python/kernel_tests/logging_ops_test.py | 12 +- .../python/kernel_tests/lookup_ops_test.py | 216 +- tensorflow/python/kernel_tests/losses_test.py | 219 +- tensorflow/python/kernel_tests/lrn_op_test.py | 4 + .../python/kernel_tests/manip_ops_test.py | 12 + .../python/kernel_tests/map_stage_op_test.py | 13 + .../python/kernel_tests/matmul_op_test.py | 10 +- .../kernel_tests/matrix_band_part_op_test.py | 2 +- .../matrix_exponential_op_test.py | 11 +- .../kernel_tests/matrix_inverse_op_test.py | 4 +- .../kernel_tests/matrix_logarithm_op_test.py | 7 +- .../kernel_tests/matrix_solve_ls_op_test.py | 4 + .../kernel_tests/matrix_solve_op_test.py | 10 +- .../matrix_square_root_op_test.py | 20 +- .../matrix_triangular_solve_op_test.py | 9 +- .../python/kernel_tests/metrics_test.py | 683 +- .../kernel_tests/morphological_ops_test.py | 11 +- .../neon_depthwise_conv_op_test.py | 9 +- .../python/kernel_tests/norm_op_test.py | 2 +- .../kernel_tests/nth_element_op_test.py | 10 +- .../python/kernel_tests/numerics_test.py | 26 +- .../python/kernel_tests/one_hot_op_test.py | 4 +- tensorflow/python/kernel_tests/pad_op_test.py | 41 +- .../kernel_tests/padding_fifo_queue_test.py | 182 +- .../parameterized_truncated_normal_op_test.py | 11 + .../parse_single_example_op_test.py | 9 +- .../python/kernel_tests/parsing_ops_test.py | 28 +- .../partitioned_variables_test.py | 29 +- tensorflow/python/kernel_tests/pool_test.py | 6 +- .../kernel_tests/pooling_ops_3d_test.py | 22 +- .../python/kernel_tests/pooling_ops_test.py | 44 +- .../kernel_tests/priority_queue_test.py | 22 +- .../python/kernel_tests/py_func_test.py | 22 +- tensorflow/python/kernel_tests/qr_op_test.py | 8 +- .../random/multinomial_op_big_test.py | 8 +- .../random/multinomial_op_test.py | 40 +- .../kernel_tests/random/random_crop_test.py | 7 +- .../kernel_tests/random/random_gamma_test.py | 8 +- .../kernel_tests/random/random_grad_test.py | 11 + .../kernel_tests/random/random_ops_test.py | 34 +- .../random/random_poisson_test.py | 10 +- .../random/random_shuffle_queue_test.py | 150 +- .../random/stateless_random_ops_test.py | 11 +- .../python/kernel_tests/reader_ops_test.py | 663 +- .../python/kernel_tests/record_input_test.py | 20 +- .../kernel_tests/reduce_benchmark_test.py | 4 +- .../kernel_tests/reduce_join_op_test.py | 20 +- .../python/kernel_tests/reduction_ops_test.py | 87 +- .../kernel_tests/regex_full_match_op_test.py | 9 +- .../kernel_tests/regex_replace_op_test.py | 11 +- .../python/kernel_tests/relu_op_test.py | 282 +- .../python/kernel_tests/reshape_op_test.py | 8 +- .../resource_variable_ops_test.py | 54 +- .../kernel_tests/reverse_sequence_op_test.py | 7 +- tensorflow/python/kernel_tests/rnn_test.py | 10 + .../kernel_tests/save_restore_ops_test.py | 19 + .../python/kernel_tests/scan_ops_test.py | 29 + .../kernel_tests/scatter_nd_ops_test.py | 130 +- .../python/kernel_tests/scatter_ops_test.py | 39 +- .../segment_reduction_ops_test.py | 103 +- .../kernel_tests/self_adjoint_eig_op_test.py | 8 +- .../python/kernel_tests/session_ops_test.py | 46 +- tensorflow/python/kernel_tests/sets_test.py | 24 +- .../python/kernel_tests/shape_ops_test.py | 77 +- tensorflow/python/kernel_tests/signal/BUILD | 31 +- .../kernel_tests/{ => signal}/dct_ops_test.py | 70 +- .../kernel_tests/{ => signal}/fft_ops_test.py | 42 +- .../kernel_tests/signal/mel_ops_test.py | 6 +- .../kernel_tests/signal/mfcc_ops_test.py | 4 + .../signal/reconstruction_ops_test.py | 82 +- .../kernel_tests/signal/shape_ops_test.py | 20 +- .../kernel_tests/signal/spectral_ops_test.py | 18 +- .../python/kernel_tests/signal/test_util.py | 14 +- .../kernel_tests/signal/window_ops_test.py | 3 + .../python/kernel_tests/slice_op_test.py | 54 +- .../python/kernel_tests/softmax_op_test.py | 7 +- .../python/kernel_tests/softplus_op_test.py | 7 +- .../python/kernel_tests/softsign_op_test.py | 5 +- .../kernel_tests/spacetobatch_op_test.py | 36 + .../kernel_tests/spacetodepth_op_test.py | 43 +- .../python/kernel_tests/sparse_add_op_test.py | 34 +- .../kernel_tests/sparse_concat_op_test.py | 21 +- .../sparse_conditional_accumulator_test.py | 60 +- .../kernel_tests/sparse_cross_op_test.py | 54 +- .../kernel_tests/sparse_matmul_op_test.py | 9 +- .../python/kernel_tests/sparse_ops_test.py | 121 +- .../kernel_tests/sparse_reorder_op_test.py | 8 +- .../kernel_tests/sparse_reshape_op_test.py | 22 +- .../sparse_serialization_ops_test.py | 27 +- .../kernel_tests/sparse_slice_op_test.py | 8 + .../kernel_tests/sparse_split_op_test.py | 8 + .../sparse_tensor_dense_matmul_grad_test.py | 2 + .../sparse_tensor_dense_matmul_op_test.py | 6 +- .../sparse_tensors_map_ops_test.py | 21 +- .../sparse_to_dense_op_py_test.py | 36 +- .../kernel_tests/sparse_xent_op_test.py | 22 +- .../python/kernel_tests/sparsemask_op_test.py | 2 + .../python/kernel_tests/split_op_test.py | 9 +- .../python/kernel_tests/stack_op_test.py | 29 +- .../python/kernel_tests/stack_ops_test.py | 43 +- .../python/kernel_tests/stage_op_test.py | 8 + .../kernel_tests/string_join_op_test.py | 2 + .../kernel_tests/string_length_op_test.py | 9 +- .../kernel_tests/string_split_op_test.py | 33 +- .../kernel_tests/string_strip_op_test.py | 6 +- .../string_to_hash_bucket_op_test.py | 9 +- .../kernel_tests/string_to_number_op_test.py | 5 + .../python/kernel_tests/substr_op_test.py | 45 +- .../kernel_tests/summary_v1_audio_op_test.py | 2 +- .../kernel_tests/summary_v1_image_op_test.py | 7 +- .../kernel_tests/summary_v1_ops_test.py | 8 +- .../kernel_tests/summary_v1_tensor_op_test.py | 14 +- tensorflow/python/kernel_tests/svd_op_test.py | 8 +- .../python/kernel_tests/template_test.py | 13 +- .../kernel_tests/tensor_array_ops_test.py | 308 +- .../python/kernel_tests/topk_op_test.py | 6 +- .../python/kernel_tests/trace_op_test.py | 2 + .../python/kernel_tests/transpose_op_test.py | 18 +- .../kernel_tests/unicode_decode_op_test.py | 153 + .../kernel_tests/unicode_encode_op_test.py | 301 + .../kernel_tests/unicode_script_op_test.py | 3 + .../kernel_tests/unicode_transcode_op_test.py | 119 +- .../python/kernel_tests/unique_op_test.py | 24 +- .../python/kernel_tests/unstack_op_test.py | 30 +- .../python/kernel_tests/variable_ops_test.py | 57 +- .../kernel_tests/variable_scope_test.py | 85 +- .../python/kernel_tests/variables_test.py | 263 +- .../kernel_tests/weights_broadcast_test.py | 33 +- .../python/kernel_tests/where_op_test.py | 21 +- .../python/kernel_tests/while_v2_test.py | 179 +- .../python/kernel_tests/xent_op_test.py | 17 +- .../python/kernel_tests/zero_division_test.py | 6 +- tensorflow/python/layers/base.py | 15 +- tensorflow/python/layers/base_test.py | 17 +- .../python/layers/convolutional_test.py | 72 +- tensorflow/python/layers/core_test.py | 21 +- tensorflow/python/layers/layers.py | 2 +- .../python/layers/normalization_test.py | 176 +- tensorflow/python/layers/pooling_test.py | 4 + tensorflow/python/lib/io/file_io.py | 258 +- tensorflow/python/lib/io/tf_record.py | 9 +- tensorflow/python/ops/array_grad.py | 32 +- tensorflow/python/ops/array_ops.py | 518 +- tensorflow/python/ops/bitwise_ops_test.py | 13 +- .../python/ops/candidate_sampling_ops.py | 7 +- tensorflow/python/ops/check_ops.py | 543 +- tensorflow/python/ops/clip_ops.py | 7 +- tensorflow/python/ops/clip_ops_test.py | 6 +- tensorflow/python/ops/collective_ops_test.py | 5 + tensorflow/python/ops/cond_v2.py | 316 +- tensorflow/python/ops/confusion_matrix.py | 77 +- tensorflow/python/ops/control_flow_ops.py | 307 +- .../python/ops/control_flow_ops_test.py | 30 +- tensorflow/python/ops/control_flow_util.py | 5 + tensorflow/python/ops/control_flow_util_v2.py | 30 + tensorflow/python/ops/ctc_ops.py | 793 +- tensorflow/python/ops/data_flow_ops.py | 15 +- tensorflow/python/ops/dequantize_op_test.py | 2 +- tensorflow/python/ops/distributions/util.py | 2 +- tensorflow/python/ops/embedding_ops.py | 5 +- tensorflow/python/ops/functional_ops.py | 19 +- tensorflow/python/ops/gradient_checker.py | 78 +- .../python/ops/gradient_checker_test.py | 14 + tensorflow/python/ops/gradient_checker_v2.py | 318 + .../python/ops/gradient_checker_v2_test.py | 300 + tensorflow/python/ops/gradients_impl.py | 140 +- tensorflow/python/ops/gradients_test.py | 46 +- tensorflow/python/ops/histogram_ops_test.py | 22 +- tensorflow/python/ops/image_grad_test.py | 20 +- tensorflow/python/ops/image_ops_impl.py | 272 +- tensorflow/python/ops/image_ops_test.py | 344 +- tensorflow/python/ops/init_ops.py | 189 +- tensorflow/python/ops/init_ops_test.py | 4 +- tensorflow/python/ops/linalg/BUILD | 1 + .../ops/linalg/cholesky_registrations.py | 101 + tensorflow/python/ops/linalg/linalg.py | 3 + tensorflow/python/ops/linalg/linalg_impl.py | 2 +- .../python/ops/linalg/linear_operator.py | 67 +- .../ops/linalg/linear_operator_adjoint.py | 207 + .../ops/linalg/linear_operator_algebra.py | 191 + .../ops/linalg/linear_operator_block_diag.py | 4 +- .../ops/linalg/linear_operator_circulant.py | 15 +- .../ops/linalg/linear_operator_composition.py | 3 - .../python/ops/linalg/linear_operator_diag.py | 4 +- .../ops/linalg/linear_operator_inversion.py | 207 + .../ops/linalg/linear_operator_kronecker.py | 4 +- .../linalg/linear_operator_low_rank_update.py | 2 +- .../linear_operator_lower_triangular.py | 4 +- .../ops/linalg/linear_operator_test_util.py | 26 +- .../python/ops/linalg/matmul_registrations.py | 252 + tensorflow/python/ops/linalg_ops.py | 73 +- tensorflow/python/ops/list_ops.py | 98 +- tensorflow/python/ops/lookup_ops.py | 11 +- tensorflow/python/ops/losses/losses_impl.py | 111 +- tensorflow/python/ops/losses/util_test.py | 2 + tensorflow/python/ops/math_grad.py | 7 +- tensorflow/python/ops/math_grad_test.py | 49 + tensorflow/python/ops/math_ops.py | 1198 +- tensorflow/python/ops/math_ops_test.py | 45 +- tensorflow/python/ops/metrics_impl.py | 136 +- tensorflow/python/ops/nccl_ops_test.py | 2 +- tensorflow/python/ops/nn_batchnorm_test.py | 31 +- .../python/ops/nn_fused_batchnorm_test.py | 12 +- tensorflow/python/ops/nn_grad.py | 14 +- tensorflow/python/ops/nn_grad_test.py | 2 + tensorflow/python/ops/nn_impl.py | 408 +- tensorflow/python/ops/nn_ops.py | 1201 +- tensorflow/python/ops/nn_test.py | 767 +- tensorflow/python/ops/nn_xent_test.py | 14 +- tensorflow/python/ops/numerics.py | 29 +- .../ops/optional_grad.py} | 23 +- .../ops/parallel_for/control_flow_ops.py | 119 +- .../ops/parallel_for/control_flow_ops_test.py | 264 +- .../python/ops/parallel_for/gradients.py | 25 +- .../python/ops/parallel_for/gradients_test.py | 23 +- tensorflow/python/ops/parallel_for/pfor.py | 10 +- tensorflow/python/ops/parsing_ops.py | 314 +- .../python/ops/partitioned_variables.py | 24 +- .../python/ops/quantized_conv_ops_test.py | 2 +- tensorflow/python/ops/quantized_ops_test.py | 4 +- tensorflow/python/ops/ragged/BUILD | 59 + tensorflow/python/ops/ragged/__init__.py | 10 + ...vert_to_tensor_or_ragged_tensor_op_test.py | 4 +- .../python/ops/ragged/ragged_array_ops.py | 100 +- .../ops/ragged/ragged_batch_gather_op_test.py | 3 + .../ops/ragged/ragged_boolean_mask_op_test.py | 2 + .../ops/ragged/ragged_concat_op_test.py | 10 + .../python/ops/ragged/ragged_const_op_test.py | 8 +- .../ops/ragged/ragged_conversion_ops.py | 29 +- .../ops/ragged/ragged_elementwise_ops.py | 62 +- .../ops/ragged/ragged_elementwise_ops_test.py | 56 +- .../ops/ragged/ragged_expand_dims_op_test.py | 1 + .../python/ops/ragged/ragged_factory_ops.py | 30 + .../ops/ragged/ragged_from_sparse_op_test.py | 19 + .../ops/ragged/ragged_from_tensor_op_test.py | 7 +- .../ops/ragged/ragged_gather_nd_op_test.py | 5 +- .../ops/ragged/ragged_gather_op_test.py | 10 + .../ops/ragged/ragged_map_fn_op_test.py | 11 +- .../ragged/ragged_map_inner_values_op_test.py | 13 + .../ops/ragged/ragged_operators_test.py | 3 + .../python/ops/ragged/ragged_range_op_test.py | 9 + .../ops/ragged/ragged_reduce_op_test.py | 4 + .../ops/ragged/ragged_row_lengths_op_test.py | 1 + ...agged_row_splits_to_segment_ids_op_test.py | 3 + ...agged_segment_ids_to_row_splits_op_test.py | 4 + .../ops/ragged/ragged_segment_op_test.py | 24 +- .../python/ops/ragged/ragged_stack_op_test.py | 2 + .../python/ops/ragged/ragged_string_ops.py | 119 + tensorflow/python/ops/ragged/ragged_tensor.py | 3 +- .../ragged_tensor_bounding_shape_op_test.py | 32 +- .../python/ops/ragged/ragged_tensor_shape.py | 570 + .../ops/ragged/ragged_tensor_shape_test.py | 487 + .../python/ops/ragged/ragged_tensor_test.py | 466 +- .../python/ops/ragged/ragged_tile_op_test.py | 11 + .../ops/ragged/ragged_to_sparse_op_test.py | 10 +- .../ops/ragged/ragged_to_tensor_op_test.py | 50 +- tensorflow/python/ops/ragged/ragged_util.py | 49 + .../python/ops/ragged/ragged_where_op_test.py | 3 +- tensorflow/python/ops/random_ops.py | 94 +- .../python/ops/resource_variable_ops.py | 57 +- tensorflow/python/ops/resources.py | 4 +- tensorflow/python/ops/rnn.py | 12 +- tensorflow/python/ops/rnn_cell_impl.py | 16 +- tensorflow/python/ops/sets_impl.py | 11 +- tensorflow/python/ops/signal/BUILD | 17 +- tensorflow/python/ops/signal/__init__.py | 37 - tensorflow/python/ops/signal/dct_ops.py | 192 + .../{spectral_ops.py => signal/fft_ops.py} | 328 +- tensorflow/python/ops/signal/mfcc_ops.py | 4 +- .../python/ops/signal/reconstruction_ops.py | 173 +- tensorflow/python/ops/signal/shape_ops.py | 2 +- tensorflow/python/ops/signal/signal.py | 65 + tensorflow/python/ops/signal/spectral_ops.py | 8 +- tensorflow/python/ops/sort_ops.py | 197 + .../python/ops/sort_ops_test.py | 16 +- tensorflow/python/ops/sparse_grad.py | 2 +- tensorflow/python/ops/sparse_ops.py | 410 +- tensorflow/python/ops/sparse_ops_test.py | 22 +- tensorflow/python/ops/special_math_ops.py | 7 +- .../python/ops/special_math_ops_test.py | 7 + tensorflow/python/ops/spectral_grad.py | 185 - tensorflow/python/ops/standard_ops.py | 3 +- tensorflow/python/ops/stateless_random_ops.py | 58 +- tensorflow/python/ops/string_ops.py | 77 +- tensorflow/python/ops/summary_op_util.py | 21 +- tensorflow/python/ops/summary_ops_v2.py | 93 +- tensorflow/python/ops/tensor_array_ops.py | 295 +- tensorflow/python/ops/tensor_forest_ops.py | 110 + tensorflow/python/ops/variable_scope.py | 185 +- tensorflow/python/ops/variables.py | 149 +- tensorflow/python/ops/while_v2.py | 79 +- tensorflow/python/platform/__init__.py | 0 tensorflow/python/platform/app.py | 2 +- tensorflow/python/platform/benchmark.py | 13 + tensorflow/python/platform/gfile.py | 4 +- tensorflow/python/platform/googletest.py | 11 +- tensorflow/python/platform/test.py | 4 +- tensorflow/python/platform/tf_logging.py | 65 +- .../profiler/internal/run_metadata_test.py | 3 + tensorflow/python/profiler/model_analyzer.py | 6 +- .../python/profiler/model_analyzer_test.py | 30 +- tensorflow/python/profiler/option_builder.py | 2 +- .../python/profiler/pprof_profiler_test.py | 2 + .../python/profiler/profile_context_test.py | 24 +- tensorflow/python/profiler/profiler.py | 8 +- tensorflow/python/profiler/profiler_test.py | 3 + tensorflow/python/profiler/tfprof_logger.py | 2 +- tensorflow/python/saved_model/BUILD | 63 +- tensorflow/python/saved_model/builder.py | 1 + tensorflow/python/saved_model/builder_impl.py | 465 +- tensorflow/python/saved_model/constants.py | 12 +- tensorflow/python/saved_model/load.py | 61 + tensorflow/python/saved_model/load_test.py | 51 + tensorflow/python/saved_model/loader_impl.py | 113 +- tensorflow/python/saved_model/loader_test.py | 130 +- tensorflow/python/saved_model/save.py | 364 +- tensorflow/python/saved_model/save_test.py | 156 +- .../python/saved_model/saved_model_test.py | 726 +- .../saved_model/saved_object_graph.proto | 38 + .../python/saved_model/signature_constants.py | 2 +- .../python/saved_model/signature_def_utils.py | 2 + .../saved_model/signature_def_utils_impl.py | 51 +- .../saved_model/signature_def_utils_test.py | 27 + .../python/saved_model/simple_save_test.py | 6 +- tensorflow/python/saved_model/utils_impl.py | 21 + tensorflow/python/summary/summary.py | 18 +- tensorflow/python/summary/summary_test.py | 13 + .../python/summary/writer/writer_test.py | 22 +- tensorflow/python/tf2.py | 17 +- tensorflow/python/tools/BUILD | 2 + .../python/tools/api/generator/api_gen.bzl | 11 +- .../tools/api/generator/api_init_files.bzl | 7 +- .../tools/api/generator/api_init_files_v1.bzl | 2 + .../tools/api/generator/create_python_api.py | 9 +- .../python/tools/api/generator/doc_srcs.py | 6 +- tensorflow/python/tools/freeze_graph_test.py | 2 + tensorflow/python/tools/inspect_checkpoint.py | 2 +- .../tools/optimize_for_inference_test.py | 5 + tensorflow/python/tools/strip_unused_test.py | 4 +- tensorflow/python/training/adadelta.py | 2 +- tensorflow/python/training/adadelta_test.py | 6 +- tensorflow/python/training/adagrad.py | 2 +- tensorflow/python/training/adagrad_da.py | 2 +- tensorflow/python/training/adagrad_da_test.py | 30 +- tensorflow/python/training/adagrad_test.py | 58 +- tensorflow/python/training/adam.py | 2 +- tensorflow/python/training/adam_test.py | 49 +- .../python/training/basic_loops_test.py | 4 + .../training/basic_session_run_hooks.py | 8 +- .../training/basic_session_run_hooks_test.py | 209 +- .../training/checkpoint_management_test.py | 9 +- .../python/training/checkpoint_ops_test.py | 5 +- .../python/training/checkpoint_utils.py | 4 +- .../python/training/checkpointable/BUILD | 2 +- .../checkpointable/data_structures.py | 50 +- .../checkpointable/data_structures_test.py | 21 + .../training/checkpointable/tracking.py | 37 + .../python/training/checkpointable/util.py | 85 +- .../training/checkpointable/util_test.py | 16 +- tensorflow/python/training/coordinator.py | 2 +- tensorflow/python/training/device_setter.py | 2 +- .../python/training/device_setter_test.py | 11 + tensorflow/python/training/distribute.py | 1229 +- .../training/distribution_strategy_context.py | 190 +- tensorflow/python/training/evaluation.py | 2 +- tensorflow/python/training/ftrl.py | 2 +- tensorflow/python/training/ftrl_test.py | 56 +- .../python/training/gradient_descent.py | 2 +- .../python/training/gradient_descent_test.py | 78 +- tensorflow/python/training/input_test.py | 264 +- .../python/training/learning_rate_decay.py | 2 +- .../training/learning_rate_decay_test.py | 35 +- .../python/training/learning_rate_decay_v2.py | 2 +- .../training/learning_rate_decay_v2_test.py | 34 +- tensorflow/python/training/momentum.py | 2 +- tensorflow/python/training/momentum_test.py | 126 +- .../python/training/monitored_session.py | 20 +- .../python/training/monitored_session_test.py | 75 +- tensorflow/python/training/moving_averages.py | 7 +- .../python/training/moving_averages_test.py | 73 +- tensorflow/python/training/optimizer.py | 47 +- tensorflow/python/training/optimizer_test.py | 28 +- .../python/training/proximal_adagrad.py | 2 +- .../python/training/proximal_adagrad_test.py | 36 +- .../proximal_gradient_descent_test.py | 31 +- .../python/training/quantize_training_test.py | 6 +- .../python/training/queue_runner_test.py | 34 +- tensorflow/python/training/rmsprop.py | 2 +- tensorflow/python/training/rmsprop_test.py | 155 +- tensorflow/python/training/saver.py | 58 +- .../saver_large_partitioned_variable_test.py | 9 +- tensorflow/python/training/saver_test.py | 173 +- .../server_lib_multiple_containers_test.py | 2 + ...lib_same_variables_clear_container_test.py | 10 +- .../server_lib_same_variables_clear_test.py | 2 + ...server_lib_same_variables_no_clear_test.py | 2 + .../training/server_lib_sparse_job_test.py | 4 +- tensorflow/python/training/server_lib_test.py | 2 +- tensorflow/python/training/session_manager.py | 2 +- .../python/training/session_manager_test.py | 15 + .../python/training/session_run_hook.py | 6 +- .../python/training/slot_creator_test.py | 20 +- tensorflow/python/training/supervisor.py | 2 +- tensorflow/python/training/supervisor_test.py | 26 +- .../training/sync_replicas_optimizer.py | 9 +- .../python/training/training_ops_test.py | 57 +- .../python/training/training_util_test.py | 3 + .../python/training/warm_starting_util.py | 10 +- .../training/warm_starting_util_test.py | 82 +- tensorflow/python/util/deprecation.py | 19 +- tensorflow/python/util/deprecation_test.py | 16 + tensorflow/python/util/dispatch.py | 192 + tensorflow/python/util/dispatch_test.py | 120 + tensorflow/python/util/nest_test.py | 1 + tensorflow/python/util/py_checkpoint_reader.i | 1 - tensorflow/python/util/tf_export.py | 40 + tensorflow/python/util/tf_export_test.py | 20 + tensorflow/python/util/tf_should_use_test.py | 9 +- tensorflow/stream_executor/BUILD | 15 +- tensorflow/stream_executor/cuda/cuda_blas.cc | 122 +- tensorflow/stream_executor/cuda/cuda_dnn.cc | 247 +- tensorflow/stream_executor/cuda/cuda_dnn.h | 13 +- tensorflow/stream_executor/cuda/cuda_fft.cc | 81 +- .../stream_executor/cuda/cuda_gpu_executor.cc | 9 +- .../stream_executor/cuda/cuda_gpu_executor.h | 3 +- tensorflow/stream_executor/cuda/cuda_rng.cc | 36 + .../stream_executor/device_description.cc | 12 +- tensorflow/stream_executor/dnn.cc | 133 +- tensorflow/stream_executor/dnn.h | 272 +- tensorflow/stream_executor/dnn.proto | 103 + .../stream_executor/host/host_gpu_executor.cc | 9 +- .../stream_executor/host/host_gpu_executor.h | 3 +- tensorflow/stream_executor/stream.cc | 5 +- tensorflow/stream_executor/stream.h | 16 +- .../stream_executor_internal.cc | 19 +- .../stream_executor_internal.h | 4 +- tensorflow/tensorflow.bzl | 27 +- ...nsorflow.-config-proto.-experimental.pbtxt | 8 +- .../golden/v1/tensorflow.-config-proto.pbtxt | 6 + .../golden/v1/tensorflow.-gradient-tape.pbtxt | 8 + .../golden/v1/tensorflow.-tensor-spec.pbtxt | 33 + .../golden/v1/tensorflow.data.-dataset.pbtxt | 3 +- ...ow.data.-fixed-length-record-dataset.pbtxt | 7 +- .../golden/v1/tensorflow.data.-options.pbtxt | 37 +- .../tensorflow.data.-t-f-record-dataset.pbtxt | 6 +- .../tensorflow.data.-text-line-dataset.pbtxt | 7 +- ...rflow.data.experimental.-csv-dataset.pbtxt | 7 +- ...a.experimental.-optimization-options.pbtxt | 46 + ...ow.data.experimental.-random-dataset.pbtxt | 7 +- ...rflow.data.experimental.-sql-dataset.pbtxt | 7 +- ...low.data.experimental.-stats-options.pbtxt | 3 +- ...data.experimental.-threading-options.pbtxt | 18 + .../v1/tensorflow.data.experimental.pbtxt | 26 +- .../tools/api/golden/v1/tensorflow.data.pbtxt | 8 + .../api/golden/v1/tensorflow.debugging.pbtxt | 2 +- ...tensorflow.distribute.-input-context.pbtxt | 25 + ...w.distribute.-input-replication-mode.pbtxt | 8 + .../v1/tensorflow.distribute.-reduce-op.pbtxt | 12 + ...nsorflow.distribute.-replica-context.pbtxt | 33 + ...orflow.distribute.-strategy-extended.pbtxt | 81 + .../v1/tensorflow.distribute.-strategy.pbtxt | 137 + .../api/golden/v1/tensorflow.distribute.pbtxt | 47 + ...rflow.estimator.-baseline-classifier.pbtxt | 7 +- ...orflow.estimator.-baseline-estimator.pbtxt | 7 +- ...orflow.estimator.-baseline-regressor.pbtxt | 7 +- ....estimator.-boosted-trees-classifier.pbtxt | 7 +- ...w.estimator.-boosted-trees-regressor.pbtxt | 7 +- ...nsorflow.estimator.-d-n-n-classifier.pbtxt | 7 +- ...ensorflow.estimator.-d-n-n-estimator.pbtxt | 7 +- ...or.-d-n-n-linear-combined-classifier.pbtxt | 7 +- ...tor.-d-n-n-linear-combined-estimator.pbtxt | 7 +- ...tor.-d-n-n-linear-combined-regressor.pbtxt | 7 +- ...ensorflow.estimator.-d-n-n-regressor.pbtxt | 7 +- .../v1/tensorflow.estimator.-estimator.pbtxt | 7 +- ...sorflow.estimator.-linear-classifier.pbtxt | 7 +- ...nsorflow.estimator.-linear-estimator.pbtxt | 7 +- ...nsorflow.estimator.-linear-regressor.pbtxt | 7 +- ...erimental.-in-memory-evaluator-hook.pbtxt} | 6 +- .../tensorflow.estimator.experimental.pbtxt | 12 + .../api/golden/v1/tensorflow.image.pbtxt | 8 + .../api/golden/v1/tensorflow.io.gfile.pbtxt | 51 + .../tools/api/golden/v1/tensorflow.io.pbtxt | 40 + .../golden/v1/tensorflow.keras.-model.pbtxt | 26 +- .../v1/tensorflow.keras.-sequential.pbtxt | 26 +- .../golden/v1/tensorflow.keras.backend.pbtxt | 6 +- ....experimental.-peephole-l-s-t-m-cell.pbtxt | 4 + .../tensorflow.keras.layers.-activation.pbtxt | 4 + ...eras.layers.-activity-regularization.pbtxt | 4 + .../v1/tensorflow.keras.layers.-add.pbtxt | 4 + ...nsorflow.keras.layers.-alpha-dropout.pbtxt | 4 + ...low.keras.layers.-average-pooling1-d.pbtxt | 4 + ...low.keras.layers.-average-pooling2-d.pbtxt | 4 + ...low.keras.layers.-average-pooling3-d.pbtxt | 4 + .../v1/tensorflow.keras.layers.-average.pbtxt | 4 + ...tensorflow.keras.layers.-avg-pool1-d.pbtxt | 4 + ...tensorflow.keras.layers.-avg-pool2-d.pbtxt | 4 + ...tensorflow.keras.layers.-avg-pool3-d.pbtxt | 4 + ...ow.keras.layers.-batch-normalization.pbtxt | 7 +- ...nsorflow.keras.layers.-bidirectional.pbtxt | 4 + ...tensorflow.keras.layers.-concatenate.pbtxt | 4 + ...orflow.keras.layers.-conv-l-s-t-m2-d.pbtxt | 4 + .../v1/tensorflow.keras.layers.-conv1-d.pbtxt | 4 + ...flow.keras.layers.-conv2-d-transpose.pbtxt | 4 + .../v1/tensorflow.keras.layers.-conv2-d.pbtxt | 4 + ...flow.keras.layers.-conv3-d-transpose.pbtxt | 4 + .../v1/tensorflow.keras.layers.-conv3-d.pbtxt | 4 + ...sorflow.keras.layers.-convolution1-d.pbtxt | 4 + ...ras.layers.-convolution2-d-transpose.pbtxt | 4 + ...sorflow.keras.layers.-convolution2-d.pbtxt | 4 + ...ras.layers.-convolution3-d-transpose.pbtxt | 4 + ...sorflow.keras.layers.-convolution3-d.pbtxt | 4 + ...tensorflow.keras.layers.-cropping1-d.pbtxt | 4 + ...tensorflow.keras.layers.-cropping2-d.pbtxt | 4 + ...tensorflow.keras.layers.-cropping3-d.pbtxt | 4 + ...sorflow.keras.layers.-cu-d-n-n-g-r-u.pbtxt | 4 + ...rflow.keras.layers.-cu-d-n-n-l-s-t-m.pbtxt | 4 + .../v1/tensorflow.keras.layers.-dense.pbtxt | 4 + ...flow.keras.layers.-depthwise-conv2-d.pbtxt | 4 + .../v1/tensorflow.keras.layers.-dot.pbtxt | 4 + .../v1/tensorflow.keras.layers.-dropout.pbtxt | 4 + .../v1/tensorflow.keras.layers.-e-l-u.pbtxt | 4 + .../tensorflow.keras.layers.-embedding.pbtxt | 4 + .../v1/tensorflow.keras.layers.-flatten.pbtxt | 4 + .../tensorflow.keras.layers.-g-r-u-cell.pbtxt | 4 + .../v1/tensorflow.keras.layers.-g-r-u.pbtxt | 4 + ...rflow.keras.layers.-gaussian-dropout.pbtxt | 4 + ...sorflow.keras.layers.-gaussian-noise.pbtxt | 4 + ...as.layers.-global-average-pooling1-d.pbtxt | 4 + ...as.layers.-global-average-pooling2-d.pbtxt | 4 + ...as.layers.-global-average-pooling3-d.pbtxt | 4 + ...low.keras.layers.-global-avg-pool1-d.pbtxt | 4 + ...low.keras.layers.-global-avg-pool2-d.pbtxt | 4 + ...low.keras.layers.-global-avg-pool3-d.pbtxt | 4 + ...low.keras.layers.-global-max-pool1-d.pbtxt | 4 + ...low.keras.layers.-global-max-pool2-d.pbtxt | 4 + ...low.keras.layers.-global-max-pool3-d.pbtxt | 4 + ....keras.layers.-global-max-pooling1-d.pbtxt | 4 + ....keras.layers.-global-max-pooling2-d.pbtxt | 4 + ....keras.layers.-global-max-pooling3-d.pbtxt | 4 + ...tensorflow.keras.layers.-input-layer.pbtxt | 4 + .../tensorflow.keras.layers.-input-spec.pbtxt | 2 +- ...ensorflow.keras.layers.-l-s-t-m-cell.pbtxt | 4 + .../v1/tensorflow.keras.layers.-l-s-t-m.pbtxt | 4 + .../v1/tensorflow.keras.layers.-lambda.pbtxt | 4 + .../v1/tensorflow.keras.layers.-layer.pbtxt | 4 + ...ensorflow.keras.layers.-leaky-re-l-u.pbtxt | 4 + ...w.keras.layers.-locally-connected1-d.pbtxt | 4 + ...w.keras.layers.-locally-connected2-d.pbtxt | 4 + .../v1/tensorflow.keras.layers.-masking.pbtxt | 4 + ...tensorflow.keras.layers.-max-pool1-d.pbtxt | 4 + ...tensorflow.keras.layers.-max-pool2-d.pbtxt | 4 + ...tensorflow.keras.layers.-max-pool3-d.pbtxt | 4 + ...sorflow.keras.layers.-max-pooling1-d.pbtxt | 4 + ...sorflow.keras.layers.-max-pooling2-d.pbtxt | 4 + ...sorflow.keras.layers.-max-pooling3-d.pbtxt | 4 + .../v1/tensorflow.keras.layers.-maximum.pbtxt | 4 + .../v1/tensorflow.keras.layers.-minimum.pbtxt | 4 + .../tensorflow.keras.layers.-multiply.pbtxt | 4 + .../tensorflow.keras.layers.-p-re-l-u.pbtxt | 4 + .../v1/tensorflow.keras.layers.-permute.pbtxt | 4 + .../v1/tensorflow.keras.layers.-r-n-n.pbtxt | 4 + .../v1/tensorflow.keras.layers.-re-l-u.pbtxt | 4 + ...nsorflow.keras.layers.-repeat-vector.pbtxt | 4 + .../v1/tensorflow.keras.layers.-reshape.pbtxt | 4 + ...flow.keras.layers.-separable-conv1-d.pbtxt | 4 + ...flow.keras.layers.-separable-conv2-d.pbtxt | 4 + ...ras.layers.-separable-convolution1-d.pbtxt | 4 + ...ras.layers.-separable-convolution2-d.pbtxt | 4 + ...flow.keras.layers.-simple-r-n-n-cell.pbtxt | 4 + ...ensorflow.keras.layers.-simple-r-n-n.pbtxt | 4 + .../v1/tensorflow.keras.layers.-softmax.pbtxt | 4 + ...low.keras.layers.-spatial-dropout1-d.pbtxt | 4 + ...low.keras.layers.-spatial-dropout2-d.pbtxt | 4 + ...low.keras.layers.-spatial-dropout3-d.pbtxt | 4 + ...ow.keras.layers.-stacked-r-n-n-cells.pbtxt | 4 + .../tensorflow.keras.layers.-subtract.pbtxt | 4 + ...low.keras.layers.-thresholded-re-l-u.pbtxt | 4 + ...rflow.keras.layers.-time-distributed.pbtxt | 4 + ...sorflow.keras.layers.-up-sampling1-d.pbtxt | 4 + ...sorflow.keras.layers.-up-sampling2-d.pbtxt | 4 + ...sorflow.keras.layers.-up-sampling3-d.pbtxt | 4 + .../v1/tensorflow.keras.layers.-wrapper.pbtxt | 4 + ...orflow.keras.layers.-zero-padding1-d.pbtxt | 4 + ...orflow.keras.layers.-zero-padding2-d.pbtxt | 4 + ...orflow.keras.layers.-zero-padding3-d.pbtxt | 4 + ...ow.keras.losses.-binary-crossentropy.pbtxt | 22 + ...ras.losses.-categorical-crossentropy.pbtxt | 22 + ...ow.keras.losses.-mean-absolute-error.pbtxt | 22 + ...sses.-mean-absolute-percentage-error.pbtxt | 22 + ...low.keras.losses.-mean-squared-error.pbtxt | 22 + ...sses.-mean-squared-logarithmic-error.pbtxt | 22 + .../golden/v1/tensorflow.keras.losses.pbtxt | 30 +- .../tensorflow.keras.metrics.-accuracy.pbtxt | 194 + ...rflow.keras.metrics.-binary-accuracy.pbtxt | 194 + ....keras.metrics.-categorical-accuracy.pbtxt | 194 + ...rflow.keras.metrics.-false-negatives.pbtxt | 193 + ...rflow.keras.metrics.-false-positives.pbtxt | 193 + .../v1/tensorflow.keras.metrics.-mean.pbtxt | 192 + .../tensorflow.keras.metrics.-precision.pbtxt | 192 + .../v1/tensorflow.keras.metrics.-recall.pbtxt | 192 + ...metrics.-sparse-categorical-accuracy.pbtxt | 194 + ...orflow.keras.metrics.-true-negatives.pbtxt | 193 + ...orflow.keras.metrics.-true-positives.pbtxt | 193 + .../golden/v1/tensorflow.keras.metrics.pbtxt | 50 +- .../v1/tensorflow.keras.models.-model.pbtxt | 26 +- .../tensorflow.keras.models.-sequential.pbtxt | 26 +- .../v1/tensorflow.keras.utils.-progbar.pbtxt | 2 +- ...ensorflow.layers.-average-pooling1-d.pbtxt | 4 + ...ensorflow.layers.-average-pooling2-d.pbtxt | 4 + ...ensorflow.layers.-average-pooling3-d.pbtxt | 4 + ...nsorflow.layers.-batch-normalization.pbtxt | 7 +- .../v1/tensorflow.layers.-conv1-d.pbtxt | 4 + ...tensorflow.layers.-conv2-d-transpose.pbtxt | 4 + .../v1/tensorflow.layers.-conv2-d.pbtxt | 4 + ...tensorflow.layers.-conv3-d-transpose.pbtxt | 4 + .../v1/tensorflow.layers.-conv3-d.pbtxt | 4 + .../golden/v1/tensorflow.layers.-dense.pbtxt | 4 + .../v1/tensorflow.layers.-dropout.pbtxt | 4 + .../v1/tensorflow.layers.-flatten.pbtxt | 4 + .../v1/tensorflow.layers.-input-spec.pbtxt | 2 +- .../golden/v1/tensorflow.layers.-layer.pbtxt | 4 + .../tensorflow.layers.-max-pooling1-d.pbtxt | 4 + .../tensorflow.layers.-max-pooling2-d.pbtxt | 4 + .../tensorflow.layers.-max-pooling3-d.pbtxt | 4 + ...tensorflow.layers.-separable-conv1-d.pbtxt | 4 + ...tensorflow.layers.-separable-conv2-d.pbtxt | 4 + ...w.linalg.-linear-operator-block-diag.pbtxt | 4 + ...ow.linalg.-linear-operator-circulant.pbtxt | 4 + ...linalg.-linear-operator-circulant2-d.pbtxt | 4 + ...linalg.-linear-operator-circulant3-d.pbtxt | 4 + ....linalg.-linear-operator-composition.pbtxt | 4 + ...sorflow.linalg.-linear-operator-diag.pbtxt | 4 + ....linalg.-linear-operator-full-matrix.pbtxt | 4 + ...low.linalg.-linear-operator-identity.pbtxt | 4 + ...ow.linalg.-linear-operator-kronecker.pbtxt | 4 + ...alg.-linear-operator-low-rank-update.pbtxt | 4 + ...lg.-linear-operator-lower-triangular.pbtxt | 4 + ...alg.-linear-operator-scaled-identity.pbtxt | 4 + ...orflow.linalg.-linear-operator-zeros.pbtxt | 4 + .../tensorflow.linalg.-linear-operator.pbtxt | 4 + .../golden/v1/tensorflow.lite.constants.pbtxt | 10 +- .../tools/api/golden/v1/tensorflow.math.pbtxt | 26 +- .../v1/tensorflow.metrics.-accuracy.pbtxt | 194 + .../tensorflow.metrics.-binary-accuracy.pbtxt | 194 + ...orflow.metrics.-categorical-accuracy.pbtxt | 194 + .../tensorflow.metrics.-false-negatives.pbtxt | 193 + .../tensorflow.metrics.-false-positives.pbtxt | 193 + .../golden/v1/tensorflow.metrics.-mean.pbtxt | 192 + .../v1/tensorflow.metrics.-precision.pbtxt | 192 + .../v1/tensorflow.metrics.-recall.pbtxt | 192 + ...metrics.-sparse-categorical-accuracy.pbtxt | 194 + .../tensorflow.metrics.-true-negatives.pbtxt | 193 + .../tensorflow.metrics.-true-positives.pbtxt | 193 + .../api/golden/v1/tensorflow.metrics.pbtxt | 44 + .../tools/api/golden/v1/tensorflow.nn.pbtxt | 28 +- ...flow.nn.rnn_cell.-basic-l-s-t-m-cell.pbtxt | 4 + ...orflow.nn.rnn_cell.-basic-r-n-n-cell.pbtxt | 4 + ...nsorflow.nn.rnn_cell.-device-wrapper.pbtxt | 4 + ...sorflow.nn.rnn_cell.-dropout-wrapper.pbtxt | 4 + .../tensorflow.nn.rnn_cell.-g-r-u-cell.pbtxt | 4 + ...tensorflow.nn.rnn_cell.-l-s-t-m-cell.pbtxt | 4 + ...orflow.nn.rnn_cell.-multi-r-n-n-cell.pbtxt | 4 + .../tensorflow.nn.rnn_cell.-r-n-n-cell.pbtxt | 4 + ...orflow.nn.rnn_cell.-residual-wrapper.pbtxt | 4 + .../tools/api/golden/v1/tensorflow.pbtxt | 56 +- .../golden/v1/tensorflow.quantization.pbtxt | 2 +- .../api/golden/v1/tensorflow.random.pbtxt | 8 + .../v1/tensorflow.saved_model.-builder.pbtxt | 1 + ...d_model.builder.-saved-model-builder.pbtxt | 1 + .../golden/v1/tensorflow.saved_model.pbtxt | 4 + .../tools/api/golden/v1/tensorflow.sets.pbtxt | 16 + .../api/golden/v1/tensorflow.signal.pbtxt | 32 + .../api/golden/v1/tensorflow.sparse.pbtxt | 6 +- .../api/golden/v1/tensorflow.strings.pbtxt | 4 + .../v1/tensorflow.test.-benchmark.pbtxt | 4 + .../api/golden/v1/tensorflow.train.pbtxt | 4 + ...orflow.-conditional-accumulator-base.pbtxt | 29 - .../tensorflow.-conditional-accumulator.pbtxt | 38 - ...nsorflow.-config-proto.-experimental.pbtxt | 6 + .../golden/v2/tensorflow.-config-proto.pbtxt | 6 + .../golden/v2/tensorflow.-device-spec.pbtxt | 37 - .../api/golden/v2/tensorflow.-dimension.pbtxt | 25 - .../golden/v2/tensorflow.-gradient-tape.pbtxt | 8 + .../golden/v2/tensorflow.-graph-keys.pbtxt | 140 - .../tensorflow.-tensor-info.-coo-sparse.pbtxt | 24 - .../golden/v2/tensorflow.-tensor-info.pbtxt | 59 - .../golden/v2/tensorflow.-tensor-spec.pbtxt | 33 + .../tools/api/golden/v2/tensorflow.app.pbtxt | 7 - .../golden/v2/tensorflow.data.-dataset.pbtxt | 19 +- ...ow.data.-fixed-length-record-dataset.pbtxt | 20 +- .../golden/v2/tensorflow.data.-iterator.pbtxt | 46 - .../golden/v2/tensorflow.data.-options.pbtxt | 37 +- .../tensorflow.data.-t-f-record-dataset.pbtxt | 20 +- .../tensorflow.data.-text-line-dataset.pbtxt | 20 +- ...rflow.data.experimental.-csv-dataset.pbtxt | 20 +- ...a.experimental.-optimization-options.pbtxt | 46 + ...ow.data.experimental.-random-dataset.pbtxt | 20 +- ...rflow.data.experimental.-sql-dataset.pbtxt | 20 +- ...low.data.experimental.-stats-options.pbtxt | 3 +- ...data.experimental.-threading-options.pbtxt | 18 + .../v2/tensorflow.data.experimental.pbtxt | 26 +- .../tools/api/golden/v2/tensorflow.data.pbtxt | 4 - .../api/golden/v2/tensorflow.debugging.pbtxt | 52 +- ...tensorflow.distribute.-input-context.pbtxt | 25 + ...w.distribute.-input-replication-mode.pbtxt | 8 + .../v2/tensorflow.distribute.-reduce-op.pbtxt | 12 + ...nsorflow.distribute.-replica-context.pbtxt | 33 + ...orflow.distribute.-strategy-extended.pbtxt | 81 + .../v2/tensorflow.distribute.-strategy.pbtxt | 137 + .../api/golden/v2/tensorflow.distribute.pbtxt | 47 + ...rflow.estimator.-baseline-classifier.pbtxt | 14 +- ...orflow.estimator.-baseline-estimator.pbtxt | 12 +- ...orflow.estimator.-baseline-regressor.pbtxt | 14 +- ....estimator.-boosted-trees-classifier.pbtxt | 7 +- ...w.estimator.-boosted-trees-regressor.pbtxt | 7 +- ...nsorflow.estimator.-d-n-n-classifier.pbtxt | 14 +- ...ensorflow.estimator.-d-n-n-estimator.pbtxt | 12 +- ...or.-d-n-n-linear-combined-classifier.pbtxt | 14 +- ...tor.-d-n-n-linear-combined-estimator.pbtxt | 12 +- ...tor.-d-n-n-linear-combined-regressor.pbtxt | 14 +- ...ensorflow.estimator.-d-n-n-regressor.pbtxt | 14 +- .../v2/tensorflow.estimator.-estimator.pbtxt | 10 +- ...sorflow.estimator.-linear-classifier.pbtxt | 10 +- ...nsorflow.estimator.-linear-estimator.pbtxt | 12 +- ...nsorflow.estimator.-linear-regressor.pbtxt | 14 +- ...perimental.-in-memory-evaluator-hook.pbtxt | 30 + .../tensorflow.estimator.experimental.pbtxt | 12 + .../golden/v2/tensorflow.feature_column.pbtxt | 10 +- .../v2/tensorflow.gfile.-fast-g-file.pbtxt | 58 - .../golden/v2/tensorflow.gfile.-g-file.pbtxt | 58 - .../golden/v2/tensorflow.gfile.-open.pbtxt | 58 - .../api/golden/v2/tensorflow.gfile.pbtxt | 63 - .../api/golden/v2/tensorflow.image.pbtxt | 30 +- .../api/golden/v2/tensorflow.io.gfile.pbtxt | 51 + .../tools/api/golden/v2/tensorflow.io.pbtxt | 54 +- .../golden/v2/tensorflow.keras.-model.pbtxt | 26 +- .../v2/tensorflow.keras.-sequential.pbtxt | 26 +- .../golden/v2/tensorflow.keras.backend.pbtxt | 6 +- ....experimental.-peephole-l-s-t-m-cell.pbtxt | 4 + .../tensorflow.keras.layers.-activation.pbtxt | 4 + ...eras.layers.-activity-regularization.pbtxt | 4 + .../v2/tensorflow.keras.layers.-add.pbtxt | 4 + ...nsorflow.keras.layers.-alpha-dropout.pbtxt | 4 + ...low.keras.layers.-average-pooling1-d.pbtxt | 4 + ...low.keras.layers.-average-pooling2-d.pbtxt | 4 + ...low.keras.layers.-average-pooling3-d.pbtxt | 4 + .../v2/tensorflow.keras.layers.-average.pbtxt | 4 + ...tensorflow.keras.layers.-avg-pool1-d.pbtxt | 4 + ...tensorflow.keras.layers.-avg-pool2-d.pbtxt | 4 + ...tensorflow.keras.layers.-avg-pool3-d.pbtxt | 4 + ...ow.keras.layers.-batch-normalization.pbtxt | 6 +- ...nsorflow.keras.layers.-bidirectional.pbtxt | 4 + ...tensorflow.keras.layers.-concatenate.pbtxt | 4 + ...orflow.keras.layers.-conv-l-s-t-m2-d.pbtxt | 4 + .../v2/tensorflow.keras.layers.-conv1-d.pbtxt | 4 + ...flow.keras.layers.-conv2-d-transpose.pbtxt | 4 + .../v2/tensorflow.keras.layers.-conv2-d.pbtxt | 4 + ...flow.keras.layers.-conv3-d-transpose.pbtxt | 4 + .../v2/tensorflow.keras.layers.-conv3-d.pbtxt | 4 + ...sorflow.keras.layers.-convolution1-d.pbtxt | 4 + ...ras.layers.-convolution2-d-transpose.pbtxt | 4 + ...sorflow.keras.layers.-convolution2-d.pbtxt | 4 + ...ras.layers.-convolution3-d-transpose.pbtxt | 4 + ...sorflow.keras.layers.-convolution3-d.pbtxt | 4 + ...tensorflow.keras.layers.-cropping1-d.pbtxt | 4 + ...tensorflow.keras.layers.-cropping2-d.pbtxt | 4 + ...tensorflow.keras.layers.-cropping3-d.pbtxt | 4 + ...sorflow.keras.layers.-cu-d-n-n-g-r-u.pbtxt | 4 + ...rflow.keras.layers.-cu-d-n-n-l-s-t-m.pbtxt | 4 + ...orflow.keras.layers.-dense-features.pbtxt} | 40 +- .../v2/tensorflow.keras.layers.-dense.pbtxt | 4 + ...flow.keras.layers.-depthwise-conv2-d.pbtxt | 4 + .../v2/tensorflow.keras.layers.-dot.pbtxt | 4 + .../v2/tensorflow.keras.layers.-dropout.pbtxt | 4 + .../v2/tensorflow.keras.layers.-e-l-u.pbtxt | 4 + .../tensorflow.keras.layers.-embedding.pbtxt | 4 + .../v2/tensorflow.keras.layers.-flatten.pbtxt | 4 + .../tensorflow.keras.layers.-g-r-u-cell.pbtxt | 4 + .../v2/tensorflow.keras.layers.-g-r-u.pbtxt | 4 + ...rflow.keras.layers.-gaussian-dropout.pbtxt | 4 + ...sorflow.keras.layers.-gaussian-noise.pbtxt | 4 + ...as.layers.-global-average-pooling1-d.pbtxt | 4 + ...as.layers.-global-average-pooling2-d.pbtxt | 4 + ...as.layers.-global-average-pooling3-d.pbtxt | 4 + ...low.keras.layers.-global-avg-pool1-d.pbtxt | 4 + ...low.keras.layers.-global-avg-pool2-d.pbtxt | 4 + ...low.keras.layers.-global-avg-pool3-d.pbtxt | 4 + ...low.keras.layers.-global-max-pool1-d.pbtxt | 4 + ...low.keras.layers.-global-max-pool2-d.pbtxt | 4 + ...low.keras.layers.-global-max-pool3-d.pbtxt | 4 + ....keras.layers.-global-max-pooling1-d.pbtxt | 4 + ....keras.layers.-global-max-pooling2-d.pbtxt | 4 + ....keras.layers.-global-max-pooling3-d.pbtxt | 4 + ...tensorflow.keras.layers.-input-layer.pbtxt | 4 + .../tensorflow.keras.layers.-input-spec.pbtxt | 2 +- ...ensorflow.keras.layers.-l-s-t-m-cell.pbtxt | 4 + .../v2/tensorflow.keras.layers.-l-s-t-m.pbtxt | 4 + .../v2/tensorflow.keras.layers.-lambda.pbtxt | 4 + .../v2/tensorflow.keras.layers.-layer.pbtxt | 4 + ...ensorflow.keras.layers.-leaky-re-l-u.pbtxt | 4 + ...ensorflow.keras.layers.-linear-model.pbtxt | 289 + ...w.keras.layers.-locally-connected1-d.pbtxt | 4 + ...w.keras.layers.-locally-connected2-d.pbtxt | 4 + .../v2/tensorflow.keras.layers.-masking.pbtxt | 4 + ...tensorflow.keras.layers.-max-pool1-d.pbtxt | 4 + ...tensorflow.keras.layers.-max-pool2-d.pbtxt | 4 + ...tensorflow.keras.layers.-max-pool3-d.pbtxt | 4 + ...sorflow.keras.layers.-max-pooling1-d.pbtxt | 4 + ...sorflow.keras.layers.-max-pooling2-d.pbtxt | 4 + ...sorflow.keras.layers.-max-pooling3-d.pbtxt | 4 + .../v2/tensorflow.keras.layers.-maximum.pbtxt | 4 + .../v2/tensorflow.keras.layers.-minimum.pbtxt | 4 + .../tensorflow.keras.layers.-multiply.pbtxt | 4 + .../tensorflow.keras.layers.-p-re-l-u.pbtxt | 4 + .../v2/tensorflow.keras.layers.-permute.pbtxt | 4 + .../v2/tensorflow.keras.layers.-r-n-n.pbtxt | 4 + .../v2/tensorflow.keras.layers.-re-l-u.pbtxt | 4 + ...nsorflow.keras.layers.-repeat-vector.pbtxt | 4 + .../v2/tensorflow.keras.layers.-reshape.pbtxt | 4 + ...flow.keras.layers.-separable-conv1-d.pbtxt | 4 + ...flow.keras.layers.-separable-conv2-d.pbtxt | 4 + ...ras.layers.-separable-convolution1-d.pbtxt | 4 + ...ras.layers.-separable-convolution2-d.pbtxt | 4 + ...flow.keras.layers.-simple-r-n-n-cell.pbtxt | 4 + ...ensorflow.keras.layers.-simple-r-n-n.pbtxt | 4 + .../v2/tensorflow.keras.layers.-softmax.pbtxt | 4 + ...low.keras.layers.-spatial-dropout1-d.pbtxt | 4 + ...low.keras.layers.-spatial-dropout2-d.pbtxt | 4 + ...low.keras.layers.-spatial-dropout3-d.pbtxt | 4 + ...ow.keras.layers.-stacked-r-n-n-cells.pbtxt | 4 + .../tensorflow.keras.layers.-subtract.pbtxt | 4 + ...low.keras.layers.-thresholded-re-l-u.pbtxt | 4 + ...rflow.keras.layers.-time-distributed.pbtxt | 4 + ...sorflow.keras.layers.-up-sampling1-d.pbtxt | 4 + ...sorflow.keras.layers.-up-sampling2-d.pbtxt | 4 + ...sorflow.keras.layers.-up-sampling3-d.pbtxt | 4 + .../v2/tensorflow.keras.layers.-wrapper.pbtxt | 4 + ...orflow.keras.layers.-zero-padding1-d.pbtxt | 4 + ...orflow.keras.layers.-zero-padding2-d.pbtxt | 4 + ...orflow.keras.layers.-zero-padding3-d.pbtxt | 4 + .../golden/v2/tensorflow.keras.layers.pbtxt | 8 + ...ow.keras.losses.-binary-crossentropy.pbtxt | 22 + ...ras.losses.-categorical-crossentropy.pbtxt | 22 + ...ow.keras.losses.-mean-absolute-error.pbtxt | 22 + ...sses.-mean-absolute-percentage-error.pbtxt | 22 + ...low.keras.losses.-mean-squared-error.pbtxt | 22 + ...sses.-mean-squared-logarithmic-error.pbtxt | 22 + .../tensorflow.keras.losses.-reduction.pbtxt | 28 + .../golden/v2/tensorflow.keras.losses.pbtxt | 34 +- .../tensorflow.keras.metrics.-accuracy.pbtxt | 194 + ...rflow.keras.metrics.-binary-accuracy.pbtxt | 194 + ....keras.metrics.-categorical-accuracy.pbtxt | 194 + ...rflow.keras.metrics.-false-negatives.pbtxt | 193 + ...rflow.keras.metrics.-false-positives.pbtxt | 193 + .../v2/tensorflow.keras.metrics.-mean.pbtxt | 192 + .../tensorflow.keras.metrics.-precision.pbtxt | 192 + .../v2/tensorflow.keras.metrics.-recall.pbtxt | 192 + ...metrics.-sparse-categorical-accuracy.pbtxt | 194 + ...orflow.keras.metrics.-true-negatives.pbtxt | 193 + ...orflow.keras.metrics.-true-positives.pbtxt | 193 + .../golden/v2/tensorflow.keras.metrics.pbtxt | 50 +- .../v2/tensorflow.keras.models.-model.pbtxt | 26 +- .../tensorflow.keras.models.-sequential.pbtxt | 26 +- .../v2/tensorflow.keras.utils.-progbar.pbtxt | 2 +- ...w.linalg.-linear-operator-block-diag.pbtxt | 4 + ...ow.linalg.-linear-operator-circulant.pbtxt | 4 + ...linalg.-linear-operator-circulant2-d.pbtxt | 4 + ...linalg.-linear-operator-circulant3-d.pbtxt | 4 + ....linalg.-linear-operator-composition.pbtxt | 4 + ...sorflow.linalg.-linear-operator-diag.pbtxt | 4 + ....linalg.-linear-operator-full-matrix.pbtxt | 4 + ...low.linalg.-linear-operator-identity.pbtxt | 4 + ...ow.linalg.-linear-operator-kronecker.pbtxt | 4 + ...alg.-linear-operator-low-rank-update.pbtxt | 4 + ...lg.-linear-operator-lower-triangular.pbtxt | 4 + ...alg.-linear-operator-scaled-identity.pbtxt | 4 + ...orflow.linalg.-linear-operator-zeros.pbtxt | 4 + .../tensorflow.linalg.-linear-operator.pbtxt | 4 + .../api/golden/v2/tensorflow.linalg.pbtxt | 4 +- .../golden/v2/tensorflow.lite.constants.pbtxt | 20 - .../api/golden/v2/tensorflow.logging.pbtxt | 83 - .../v2/tensorflow.losses.-reduction.pbtxt | 14 +- .../api/golden/v2/tensorflow.losses.pbtxt | 44 - .../tools/api/golden/v2/tensorflow.math.pbtxt | 54 +- .../v2/tensorflow.metrics.-accuracy.pbtxt | 194 + .../tensorflow.metrics.-binary-accuracy.pbtxt | 194 + ...orflow.metrics.-categorical-accuracy.pbtxt | 194 + .../tensorflow.metrics.-false-negatives.pbtxt | 193 + .../tensorflow.metrics.-false-positives.pbtxt | 193 + .../golden/v2/tensorflow.metrics.-mean.pbtxt | 192 + .../v2/tensorflow.metrics.-precision.pbtxt | 192 + .../v2/tensorflow.metrics.-recall.pbtxt | 192 + ...metrics.-sparse-categorical-accuracy.pbtxt | 194 + .../tensorflow.metrics.-true-negatives.pbtxt | 193 + .../tensorflow.metrics.-true-positives.pbtxt | 193 + .../api/golden/v2/tensorflow.metrics.pbtxt | 154 +- .../tools/api/golden/v2/tensorflow.nn.pbtxt | 102 +- ...nsorflow.nn.rnn_cell.-device-wrapper.pbtxt | 4 + ...sorflow.nn.rnn_cell.-dropout-wrapper.pbtxt | 4 + .../tensorflow.nn.rnn_cell.-r-n-n-cell.pbtxt | 4 + ...orflow.nn.rnn_cell.-residual-wrapper.pbtxt | 4 + .../golden/v2/tensorflow.nn.rnn_cell.pbtxt | 4 - .../tools/api/golden/v2/tensorflow.pbtxt | 258 +- ...flow.profiler.-advice-proto.-checker.pbtxt | 12 - ...ofiler.-advice-proto.-checkers-entry.pbtxt | 22 - .../tensorflow.profiler.-advice-proto.pbtxt | 41 - ...graph-node-proto.-input-shapes-entry.pbtxt | 22 - ...ensorflow.profiler.-graph-node-proto.pbtxt | 191 - ...low.profiler.-multi-graph-node-proto.pbtxt | 134 - ...er.-op-log-proto.-id-to-string-entry.pbtxt | 21 - .../tensorflow.profiler.-op-log-proto.pbtxt | 38 - ...low.profiler.-profile-option-builder.pbtxt | 93 - .../v2/tensorflow.profiler.-profiler.pbtxt | 37 - .../api/golden/v2/tensorflow.profiler.pbtxt | 39 - .../golden/v2/tensorflow.quantization.pbtxt | 2 +- .../api/golden/v2/tensorflow.random.pbtxt | 28 +- .../golden/v2/tensorflow.saved_model.pbtxt | 16 +- .../tools/api/golden/v2/tensorflow.sets.pbtxt | 8 +- .../api/golden/v2/tensorflow.signal.pbtxt | 32 + .../api/golden/v2/tensorflow.sparse.pbtxt | 36 +- .../api/golden/v2/tensorflow.spectral.pbtxt | 35 - .../api/golden/v2/tensorflow.strings.pbtxt | 14 +- .../tensorflow.summary.-summary-writer.pbtxt | 29 + .../api/golden/v2/tensorflow.summary.pbtxt | 40 +- .../v2/tensorflow.test.-benchmark.pbtxt | 4 + .../tools/api/golden/v2/tensorflow.test.pbtxt | 14 +- ...tensorflow.train.-adadelta-optimizer.pbtxt | 51 - ...sorflow.train.-adagrad-d-a-optimizer.pbtxt | 51 - .../tensorflow.train.-adagrad-optimizer.pbtxt | 51 - .../v2/tensorflow.train.-adam-optimizer.pbtxt | 51 - ...low.train.-checkpoint-saver-listener.pbtxt | 24 - ...sorflow.train.-chief-session-creator.pbtxt | 14 - .../v2/tensorflow.train.-ftrl-optimizer.pbtxt | 51 - ...ow.train.-gradient-descent-optimizer.pbtxt | 51 - .../v2/tensorflow.train.-looper-thread.pbtxt | 73 - ...tensorflow.train.-momentum-optimizer.pbtxt | 51 - ...ain.-monitored-session.-step-context.pbtxt | 21 - .../tensorflow.train.-monitored-session.pbtxt | 34 - ...rain.-nan-loss-during-training-error.pbtxt | 12 - .../v2/tensorflow.train.-optimizer.pbtxt | 50 - ...ow.train.-proximal-adagrad-optimizer.pbtxt | 51 - ...nsorflow.train.-r-m-s-prop-optimizer.pbtxt | 51 - .../v2/tensorflow.train.-scaffold.pbtxt | 53 - ...nsorflow.train.-second-or-step-timer.pbtxt | 26 - .../tensorflow.train.-session-creator.pbtxt | 12 - .../tensorflow.train.-session-manager.pbtxt | 21 - .../tensorflow.train.-session-run-args.pbtxt | 27 - ...ensorflow.train.-session-run-context.pbtxt | 25 - ...tensorflow.train.-session-run-values.pbtxt | 27 - ...ular-monitored-session.-step-context.pbtxt | 21 - ...ow.train.-singular-monitored-session.pbtxt | 38 - .../v2/tensorflow.train.-supervisor.pbtxt | 153 - .../v2/tensorflow.train.-vocab-info.pbtxt | 43 - ...orflow.train.-worker-session-creator.pbtxt | 14 - .../api/golden/v2/tensorflow.train.pbtxt | 126 +- .../tools/api/tests/api_compatibility_test.py | 9 +- ...Dockerfile.rbe.cuda10.0-cudnn7-ubuntu14.04 | 75 + .../tools/ci_build/builds/libtensorflow.sh | 1 + .../windows/cpu/pip/build_tf_windows.sh | 31 +- .../windows/gpu/pip/build_tf_windows.sh | 26 +- tensorflow/tools/compatibility/BUILD | 46 +- tensorflow/tools/compatibility/README.md | 63 +- tensorflow/tools/compatibility/ast_edits.py | 60 +- .../tools/compatibility/ast_edits_test.py | 420 + tensorflow/tools/compatibility/renames_v2.py | 303 +- .../compatibility/testdata/test_file_v1_12.py | 71 + .../tools/compatibility/tf_upgrade_v2.py | 893 +- .../tools/compatibility/tf_upgrade_v2_main.py | 104 + .../tools/compatibility/tf_upgrade_v2_test.py | 369 +- tensorflow/tools/compatibility/update/BUILD | 1 + .../update/generate_v2_renames_map.py | 92 +- tensorflow/tools/docker/Dockerfile | 4 +- tensorflow/tools/docker/Dockerfile.devel | 4 +- tensorflow/tools/docker/Dockerfile.devel-mkl | 4 +- .../tools/docker/Dockerfile.devel-mkl-horovod | 4 +- tensorflow/tools/docker/Dockerfile.mkl | 4 +- .../tools/docker/Dockerfile.mkl-horovod | 4 +- tensorflow/tools/dockerfiles/.gitignore | 1 + tensorflow/tools/dockerfiles/README.md | 49 +- tensorflow/tools/dockerfiles/assembler.py | 887 +- .../dockerfiles/cpu-devel-jupyter.Dockerfile | 64 +- .../dockerfiles/cpu-devel.Dockerfile | 46 +- .../dockerfiles/cpu-jupyter.Dockerfile | 53 +- .../dockerfiles/dockerfiles/cpu.Dockerfile | 35 +- ...ockerfile => gpu-devel-jupyter.Dockerfile} | 76 +- ...-devel.Dockerfile => gpu-devel.Dockerfile} | 58 +- ...yter.Dockerfile => gpu-jupyter.Dockerfile} | 62 +- .../{nvidia.Dockerfile => gpu.Dockerfile} | 44 +- .../partials/jupyter.partial.Dockerfile | 16 +- .../partials/tensorflow.partial.Dockerfile | 7 +- .../partials/test-import.partial.Dockerfile | 0 .../partials/ubuntu.partial.Dockerfile | 2 - .../{ => ubuntu}/bazel.partial.Dockerfile | 14 + .../cpu-devel.partial.Dockerfile} | 7 +- .../partials/ubuntu/cpu.partial.Dockerfile | 1 + .../nvidia-devel.partial.Dockerfile | 18 +- .../{ => ubuntu}/nvidia.partial.Dockerfile | 8 +- .../{ => ubuntu}/python.partial.Dockerfile | 5 +- .../ubuntu/test-devel.partial.Dockerfile | 0 .../ubuntu/version.partial.Dockerfile | 1 + .../tools/dockerfiles/readme-for-jupyter.md | 3 + tensorflow/tools/dockerfiles/spec.yml | 320 +- .../tools/dockerfiles/tests/build-cpu.sh | 37 + .../tools/dockerfiles/tests/build-gpu.sh | 36 + .../tools/dockerfiles/tests/import-gpu.sh | 18 + tensorflow/tools/dockerfiles/tests/import.sh | 19 + ...{assembler.Dockerfile => tools.Dockerfile} | 5 +- tensorflow/tools/docs/BUILD | 25 +- tensorflow/tools/docs/generate2.py | 82 + .../docs/generate2_test.py} | 29 +- tensorflow/tools/docs/generate_1_0.py | 92 - tensorflow/tools/docs/parser.py | 2 +- tensorflow/tools/lib_package/BUILD | 2 + tensorflow/tools/pip_package/BUILD | 12 +- tensorflow/tools/pip_package/setup.py | 4 +- tensorflow/workspace.bzl | 73 +- .../clang_toolchain/download_clang.bzl | 8 +- third_party/eigen_reshaped.patch | 48 - third_party/googleapis.BUILD | 14 +- third_party/gpus/crosstool/CROSSTOOL.tpl | 3 +- third_party/gpus/cuda_configure.bzl | 9 + third_party/gpus/rocm_configure.bzl | 2 +- third_party/icu/data/BUILD.bazel | 46 + third_party/icu/data/LICENSE | 414 + .../icu/data/icu_conversion_data.c.gz.aa | Bin 0 -> 177696 bytes .../icu/data/icu_conversion_data.c.gz.ab | Bin 0 -> 177696 bytes .../icu/data/icu_conversion_data.c.gz.ac | Bin 0 -> 177696 bytes .../icu/data/icu_conversion_data.c.gz.ad | Bin 0 -> 177696 bytes .../icu/data/icu_conversion_data.c.gz.ae | Bin 0 -> 177696 bytes .../icu/data/icu_conversion_data.c.gz.af | Bin 0 -> 177696 bytes .../icu/data/icu_conversion_data.c.gz.ag | Bin 0 -> 177696 bytes .../icu/data/icu_conversion_data.c.gz.ah | Bin 0 -> 177696 bytes .../icu/data/icu_conversion_data.c.gz.ai | Bin 0 -> 177696 bytes .../icu/data/icu_conversion_data.c.gz.aj | Bin 0 -> 177689 bytes third_party/icu/udata.patch | 53 + third_party/icu/workspace.bzl | 6 + third_party/libxsmm.BUILD | 2 +- third_party/llvm/llvm.autogenerated.BUILD | 2 + third_party/llvm/llvm.bzl | 1 + third_party/mkl_dnn/mkldnn.BUILD | 2 +- third_party/nccl/archive.BUILD | 26 +- third_party/ngraph/ngraph.BUILD | 26 +- third_party/ngraph/ngraph_tf.BUILD | 40 +- third_party/ngraph/tbb.BUILD | 10 +- third_party/png.BUILD | 2 +- third_party/repo.bzl | 4 +- third_party/toolchains/BUILD | 13 + .../preconfig/generate/containers.bzl | 2 +- .../ubuntu14.04/cuda10.0-cudnn7/WORKSPACE | 2 + .../ubuntu14.04/cuda10.0-cudnn7/cuda/BUILD | 1275 + .../cuda10.0-cudnn7/cuda/build_defs.bzl | 31 + .../cuda10.0-cudnn7/cuda/cuda/cuda_config.h | 26 + .../ubuntu14.04/cuda9.0-cudnn7/cuda/BUILD | 10 +- .../cuda9.0-cudnn7/cuda/build_defs.bzl | 6 +- .../ubuntu14.04/gcc-nvcc-cuda10.0/BUILD | 87 + .../ubuntu14.04/gcc-nvcc-cuda10.0/CROSSTOOL | 1431 + .../bin/crosstool_wrapper_driver_is_not_gcc | 264 + .../windows/msvc_wrapper_for_nvcc.bat | 20 + .../windows/msvc_wrapper_for_nvcc.py | 192 + .../ubuntu14.04/gcc-nvcc-cuda9.0/BUILD | 87 + .../ubuntu14.04/gcc-nvcc-cuda9.0/CROSSTOOL | 1431 + .../bin/crosstool_wrapper_driver_is_not_gcc | 264 + .../windows/msvc_wrapper_for_nvcc.bat | 20 + .../windows/msvc_wrapper_for_nvcc.py | 192 + .../preconfig/ubuntu14.04/py3/BUILD | 4 +- .../toolchains/preconfig/win_1803/BUILD | 2 +- tools/bazel.rc | 1 + 3376 files changed, 171721 insertions(+), 66372 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/40-tflite-op-request.md create mode 100644 tensorflow/c/kernels.cc create mode 100644 tensorflow/c/kernels.h create mode 100644 tensorflow/c/kernels_test.cc create mode 100644 tensorflow/cc/saved_model/testdata/half_plus_two_v2/00000123/assets/foo.txt create mode 100644 tensorflow/cc/saved_model/testdata/half_plus_two_v2/00000123/saved_model.pb create mode 100644 tensorflow/cc/saved_model/testdata/half_plus_two_v2/00000123/variables/variables.data-00000-of-00001 create mode 100644 tensorflow/cc/saved_model/testdata/half_plus_two_v2/00000123/variables/variables.index create mode 100644 tensorflow/compiler/jit/flags.cc rename tensorflow/compiler/jit/{legacy_flags/mark_for_compilation_pass_flags.h => flags.h} (57%) delete mode 100644 tensorflow/compiler/jit/legacy_flags/BUILD delete mode 100644 tensorflow/compiler/jit/legacy_flags/build_xla_ops_pass_flags.cc delete mode 100644 tensorflow/compiler/jit/legacy_flags/build_xla_ops_pass_flags.h delete mode 100644 tensorflow/compiler/jit/legacy_flags/mark_for_compilation_pass_flags.cc delete mode 100644 tensorflow/compiler/jit/legacy_flags/xla_device_flags.cc delete mode 100644 tensorflow/compiler/jit/legacy_flags/xla_device_flags.h delete mode 100644 tensorflow/compiler/jit/legacy_flags/xla_ops_common_flags.cc delete mode 100644 tensorflow/compiler/jit/legacy_flags/xla_ops_common_flags.h rename tensorflow/{contrib/estimator/python/estimator/dnn.py => compiler/jit/ops/xla_ops_grad.py} (62%) delete mode 100644 tensorflow/compiler/tf2xla/dump_graph_flags.cc delete mode 100644 tensorflow/compiler/tf2xla/dump_graph_flags.h delete mode 100644 tensorflow/compiler/tf2xla/lib/batch_dot.cc delete mode 100644 tensorflow/compiler/tf2xla/lib/batch_dot.h create mode 100644 tensorflow/compiler/xla/client/lib/matrix.cc rename tensorflow/compiler/xla/client/lib/{numeric.h => matrix.h} (56%) rename tensorflow/compiler/xla/client/lib/{numeric_test.cc => matrix_test.cc} (53%) delete mode 100644 tensorflow/compiler/xla/client/lib/numeric.cc create mode 100644 tensorflow/compiler/xla/client/lib/slicing.cc create mode 100644 tensorflow/compiler/xla/client/lib/slicing.h rename tensorflow/compiler/{tf2xla/lib/util_test.cc => xla/client/lib/slicing_test.cc} (67%) create mode 100644 tensorflow/compiler/xla/g3doc/images/xla_array_layout_figure1.png create mode 100644 tensorflow/compiler/xla/g3doc/images/xla_array_layout_figure2.png create mode 100644 tensorflow/compiler/xla/g3doc/layout_with_tiling.md create mode 100644 tensorflow/compiler/xla/service/algebraic_simplifier_proof_distributive_property.py create mode 100644 tensorflow/compiler/xla/service/ar_crs_combiner.cc create mode 100644 tensorflow/compiler/xla/service/ar_crs_combiner.h create mode 100644 tensorflow/compiler/xla/service/ar_crs_combiner_test.cc create mode 100644 tensorflow/compiler/xla/service/dynamic_parameter_binding.cc create mode 100644 tensorflow/compiler/xla/service/dynamic_parameter_binding.h create mode 100644 tensorflow/compiler/xla/service/dynamic_parameter_binding_test.cc create mode 100644 tensorflow/compiler/xla/service/hlo_get_dimension_size_rewriter.cc create mode 100644 tensorflow/compiler/xla/service/hlo_get_dimension_size_rewriter.h create mode 100644 tensorflow/compiler/xla/service/hlo_get_dimension_size_rewriter_test.cc create mode 100644 tensorflow/compiler/xla/service/pattern_matcher_gmock.h create mode 100644 tensorflow/compiler/xla/service/pattern_matcher_gmock_test.cc create mode 100644 tensorflow/compiler/xla/shape.cc create mode 100644 tensorflow/compiler/xla/shape.h create mode 100644 tensorflow/compiler/xla/shape_test.cc create mode 100644 tensorflow/compiler/xla/tests/conv_depthwise_test.cc create mode 100644 tensorflow/compiler/xla/tests/grouped_convolution_test.cc create mode 100644 tensorflow/compiler/xrt/xrt_util.cc create mode 100644 tensorflow/compiler/xrt/xrt_util.h create mode 100644 tensorflow/contrib/autograph/examples/benchmarks/BUILD create mode 100644 tensorflow/contrib/autograph/examples/benchmarks/benchmark_base.py create mode 100644 tensorflow/contrib/autograph/examples/benchmarks/cartpole_benchmark.py create mode 100644 tensorflow/contrib/cluster_resolver/cluster_resolver_initialization_test.py create mode 100644 tensorflow/contrib/cmake/TensorflowConfig.cmake.in create mode 100644 tensorflow/contrib/cmake/TensorflowConfigVersion.cmake.in create mode 100644 tensorflow/contrib/cmake/tf_core_eager_runtime.cmake rename tensorflow/contrib/distribute/python/{cross_tower_ops_test.py => cross_device_ops_test.py} (79%) rename tensorflow/contrib/distribute/python/{cross_tower_utils_test.py => cross_device_utils_test.py} (83%) delete mode 100644 tensorflow/contrib/distribute/python/mirrored_strategy_test.py delete mode 100644 tensorflow/contrib/estimator/python/estimator/dnn_linear_combined.py rename tensorflow/{compiler/tests/resampler_ops_test.py => contrib/resampler/xla/resampler_ops_xla_test.py} (76%) create mode 100644 tensorflow/contrib/tensorrt/test/quantization_mnist_test.py create mode 100644 tensorflow/contrib/tensorrt/test/quantization_test.py create mode 100644 tensorflow/contrib/tensorrt/test/testdata/checkpoint create mode 100644 tensorflow/contrib/tensorrt/test/testdata/model.ckpt-46900.data-00000-of-00001 create mode 100644 tensorflow/contrib/tensorrt/test/testdata/model.ckpt-46900.index delete mode 100644 tensorflow/contrib/training/python/training/tensor_queue_dataset.py delete mode 100644 tensorflow/contrib/training/python/training/tensor_queue_dataset_test.py delete mode 100644 tensorflow/core/api_def/base_api/api_def_EnqueueInQueueDataset.pbtxt rename tensorflow/core/api_def/base_api/{api_def_BytesProducedStatsDataset.pbtxt => api_def_ExperimentalBytesProducedStatsDataset.pbtxt} (56%) create mode 100644 tensorflow/core/api_def/base_api/api_def_ExperimentalDatasetCardinality.pbtxt rename tensorflow/core/api_def/base_api/{api_def_DatasetToTFRecord.pbtxt => api_def_ExperimentalDatasetToTFRecord.pbtxt} (91%) rename tensorflow/core/api_def/base_api/{api_def_DenseToSparseBatchDataset.pbtxt => api_def_ExperimentalDenseToSparseBatchDataset.pbtxt} (89%) delete mode 100644 tensorflow/core/api_def/base_api/api_def_ExperimentalFunctionBufferingResource.pbtxt delete mode 100644 tensorflow/core/api_def/base_api/api_def_ExperimentalFunctionBufferingResourceGetNext.pbtxt delete mode 100644 tensorflow/core/api_def/base_api/api_def_ExperimentalFunctionBufferingResourceReset.pbtxt rename tensorflow/core/api_def/base_api/{api_def_GroupByReducerDataset.pbtxt => api_def_ExperimentalGroupByReducerDataset.pbtxt} (97%) rename tensorflow/core/api_def/base_api/{api_def_GroupByWindowDataset.pbtxt => api_def_ExperimentalGroupByWindowDataset.pbtxt} (82%) rename tensorflow/core/api_def/base_api/{api_def_LatencyStatsDataset.pbtxt => api_def_ExperimentalLatencyStatsDataset.pbtxt} (58%) rename tensorflow/core/api_def/base_api/{api_def_MapAndBatchDatasetV2.pbtxt => api_def_ExperimentalMapAndBatchDataset.pbtxt} (96%) create mode 100644 tensorflow/core/api_def/base_api/api_def_ExperimentalMatchingFilesDataset.pbtxt create mode 100644 tensorflow/core/api_def/base_api/api_def_ExperimentalMaxIntraOpParallelismDataset.pbtxt rename tensorflow/core/api_def/base_api/{api_def_ParallelInterleaveDataset.pbtxt => api_def_ExperimentalParallelInterleaveDataset.pbtxt} (90%) rename tensorflow/core/api_def/base_api/{api_def_ParseExampleDataset.pbtxt => api_def_ExperimentalParseExampleDataset.pbtxt} (96%) create mode 100644 tensorflow/core/api_def/base_api/api_def_ExperimentalPrivateThreadPoolDataset.pbtxt rename tensorflow/core/api_def/base_api/{api_def_RandomDataset.pbtxt => api_def_ExperimentalRandomDataset.pbtxt} (86%) rename tensorflow/core/api_def/base_api/{api_def_ScanDataset.pbtxt => api_def_ExperimentalScanDataset.pbtxt} (61%) create mode 100644 tensorflow/core/api_def/base_api/api_def_ExperimentalSetStatsAggregatorDataset.pbtxt rename tensorflow/core/api_def/base_api/{api_def_SlideDataset.pbtxt => api_def_ExperimentalSlidingWindowDataset.pbtxt} (88%) rename tensorflow/core/api_def/base_api/{api_def_SqlDataset.pbtxt => api_def_ExperimentalSqlDataset.pbtxt} (87%) create mode 100644 tensorflow/core/api_def/base_api/api_def_ExperimentalStatsAggregatorHandle.pbtxt rename tensorflow/core/api_def/base_api/{api_def_StatsAggregatorSummary.pbtxt => api_def_ExperimentalStatsAggregatorSummary.pbtxt} (56%) rename tensorflow/core/api_def/base_api/{api_def_UnbatchDataset.pbtxt => api_def_ExperimentalUnbatchDataset.pbtxt} (57%) delete mode 100644 tensorflow/core/api_def/base_api/api_def_MapAndBatchDataset.pbtxt delete mode 100644 tensorflow/core/api_def/base_api/api_def_MatchingFilesDataset.pbtxt delete mode 100644 tensorflow/core/api_def/base_api/api_def_PrependFromQueueAndPaddedBatchDataset.pbtxt create mode 100644 tensorflow/core/api_def/base_api/api_def_ResourceApplyAdamWithAmsgrad.pbtxt create mode 100644 tensorflow/core/api_def/base_api/api_def_ResourceApplyKerasMomentum.pbtxt create mode 100644 tensorflow/core/api_def/base_api/api_def_ResourceSparseApplyKerasMomentum.pbtxt delete mode 100644 tensorflow/core/api_def/base_api/api_def_SetStatsAggregatorDataset.pbtxt delete mode 100644 tensorflow/core/api_def/base_api/api_def_StatsAggregatorHandle.pbtxt create mode 100644 tensorflow/core/api_def/base_api/api_def_TensorForestCreateTreeVariable.pbtxt create mode 100644 tensorflow/core/api_def/base_api/api_def_TensorForestTreeDeserialize.pbtxt create mode 100644 tensorflow/core/api_def/base_api/api_def_TensorForestTreeIsInitializedOp.pbtxt create mode 100644 tensorflow/core/api_def/base_api/api_def_TensorForestTreePredict.pbtxt create mode 100644 tensorflow/core/api_def/base_api/api_def_TensorForestTreeResourceHandleOp.pbtxt create mode 100644 tensorflow/core/api_def/base_api/api_def_TensorForestTreeSerialize.pbtxt create mode 100644 tensorflow/core/api_def/base_api/api_def_TensorForestTreeSize.pbtxt create mode 100644 tensorflow/core/api_def/base_api/api_def_TensorListConcat.pbtxt create mode 100644 tensorflow/core/api_def/base_api/api_def_TensorListSplit.pbtxt create mode 100644 tensorflow/core/api_def/base_api/api_def_TensorScatterAdd.pbtxt create mode 100644 tensorflow/core/api_def/base_api/api_def_TensorScatterSub.pbtxt create mode 100644 tensorflow/core/api_def/base_api/api_def_TensorScatterUpdate.pbtxt create mode 100644 tensorflow/core/api_def/base_api/api_def_UnicodeDecodeWithOffsets.pbtxt create mode 100644 tensorflow/core/api_def/base_api/api_def_UnicodeEncode.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_BatchDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_BytesProducedStatsDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_CacheDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_ConcatenateDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_DatasetToSingleElement.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_DenseToSparseBatchDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_EnqueueInQueueDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_FilterDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_FixedLengthRecordDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_FixedLengthRecordDatasetV2.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_FlatMapDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_GeneratorDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_GroupByWindowDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_InterleaveDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_LatencyStatsDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_MapAndBatchDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_MapDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_PaddedBatchDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_ParallelInterleaveDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_ParallelMapDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_ParseExampleDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_PrefetchDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_PrependFromQueueAndPaddedBatchDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_RandomDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_RangeDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_RepeatDataset.pbtxt create mode 100644 tensorflow/core/api_def/python_api/api_def_ResourceApplyAdamWithAmsgrad.pbtxt create mode 100644 tensorflow/core/api_def/python_api/api_def_ResourceApplyKerasMomentum.pbtxt create mode 100644 tensorflow/core/api_def/python_api/api_def_ResourceSparseApplyKerasMomentum.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_ScanDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_SetStatsAggregatorDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_ShuffleAndRepeatDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_ShuffleDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_SkipDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_SlideDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_SparseTensorSliceDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_SqlDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_StatsAggregatorHandle.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_StatsAggregatorSummary.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_TFRecordDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_TakeDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_TensorDataset.pbtxt create mode 100644 tensorflow/core/api_def/python_api/api_def_TensorListConcat.pbtxt rename tensorflow/core/api_def/python_api/{api_defTensorListPushBackBatch.pbtxt => api_def_TensorListPushBackBatch.pbtxt} (100%) create mode 100644 tensorflow/core/api_def/python_api/api_def_TensorListSplit.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_TensorSliceDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_TextLineDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_UnbatchDataset.pbtxt delete mode 100644 tensorflow/core/api_def/python_api/api_def_ZipDataset.pbtxt create mode 100644 tensorflow/core/distributed_runtime/server_lib_test.cc create mode 100644 tensorflow/core/framework/function_handle_cache.cc create mode 100644 tensorflow/core/framework/function_handle_cache.h delete mode 100644 tensorflow/core/grappler/optimizers/graph_rewriter.cc delete mode 100644 tensorflow/core/grappler/optimizers/graph_rewriter.h rename tensorflow/core/kernels/{conv_ops_gpu_3.cu.cc => conv_2d_gpu.h} (91%) create mode 100644 tensorflow/core/kernels/conv_2d_gpu_double.cu.cc create mode 100644 tensorflow/core/kernels/conv_2d_gpu_float.cu.cc create mode 100644 tensorflow/core/kernels/conv_2d_gpu_half.cu.cc rename tensorflow/{lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.h => core/kernels/conv_2d_gpu_int.cu.cc} (54%) create mode 100644 tensorflow/core/kernels/conv_2d_gpu_uint16.cu.cc create mode 100644 tensorflow/core/kernels/conv_2d_gpu_uint32.cu.cc create mode 100644 tensorflow/core/kernels/conv_2d_gpu_uint64.cu.cc create mode 100644 tensorflow/core/kernels/conv_2d_gpu_uint8.cu.cc create mode 100644 tensorflow/core/kernels/conv_ops_fused_image_transform.cc rename tensorflow/core/kernels/data/{ => experimental}/dense_to_sparse_batch_dataset_op.cc (97%) rename tensorflow/core/kernels/data/{ => experimental}/group_by_reducer_dataset_op.cc (93%) rename tensorflow/core/kernels/data/{ => experimental}/group_by_window_dataset_op.cc (95%) delete mode 100644 tensorflow/core/kernels/data/experimental/identity_indexed_dataset.cc delete mode 100644 tensorflow/core/kernels/data/experimental/indexed_dataset.h rename tensorflow/core/kernels/data/experimental/{indexed_dataset.cc => indexed_dataset_op.cc} (62%) rename tensorflow/core/kernels/data/{ => experimental}/map_and_batch_dataset_op.cc (90%) rename tensorflow/core/kernels/data/{ => experimental}/matching_files_dataset_op.cc (99%) create mode 100644 tensorflow/core/kernels/data/experimental/parallel_interleave_dataset_op.cc rename tensorflow/core/kernels/data/{ => experimental}/parse_example_dataset_op.cc (85%) rename tensorflow/core/kernels/data/{ => experimental}/random_dataset_op.cc (97%) rename tensorflow/core/kernels/data/{ => experimental}/scan_dataset_op.cc (94%) rename tensorflow/core/kernels/data/{stats_aggregator_dataset_op.cc => experimental/set_stats_aggregator_dataset_op.cc} (97%) rename tensorflow/core/kernels/data/{slide_dataset_op.cc => experimental/sliding_window_dataset_op.cc} (95%) rename tensorflow/core/kernels/data/{ => experimental}/sql/BUILD (100%) rename tensorflow/core/kernels/data/{ => experimental}/sql/driver_manager.cc (88%) rename tensorflow/core/kernels/data/{ => experimental}/sql/driver_manager.h (81%) rename tensorflow/core/kernels/data/{ => experimental}/sql/query_connection.h (92%) rename tensorflow/core/kernels/data/{ => experimental}/sql/sqlite_query_connection.cc (97%) rename tensorflow/core/kernels/data/{ => experimental}/sql/sqlite_query_connection.h (84%) rename tensorflow/core/kernels/data/{sql_dataset_ops.cc => experimental/sql_dataset_op.cc} (96%) rename tensorflow/core/kernels/data/{ => experimental}/stats_aggregator_ops.cc (95%) rename tensorflow/core/kernels/data/{ => experimental}/stats_dataset_ops.cc (95%) rename tensorflow/core/kernels/data/{writer_ops.cc => experimental/to_tf_record_op.cc} (84%) rename tensorflow/core/kernels/data/{ => experimental}/unbatch_dataset_op.cc (98%) create mode 100644 tensorflow/core/kernels/data/optional_ops.cu.cc delete mode 100644 tensorflow/core/kernels/data/tensor_queue_dataset_op.cc create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/012e3ad384a4a1165f8498b5c94ba0d32a73e187 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/055d77f7810048caa28323f6eb552a53d156040b create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/131e251bfb82c681cb075d32b99f18fceaca115d create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/1399ab0bd9f2c91d270cb43251bdc5729bef3526 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/16a6ce88f66d2e9686c8354cad8ba915cf0c11de create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/185097ed0588195164619ea930ddd8274a5f32ad create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/27711a87e06a50c81571c27c3aa403a6ad5dc55c create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/298c3787ad1722b22569cbc405c464d2 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/2b95ba6d8141ce0d29ff279770903922 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/321fb3d758b86e37fc340ae2b09b8ed9fa73a4cb create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/331a98b4e4c87840efea69223766ebd0e1736542 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/352d73f841223ecb630b5836585d2ba7b0f9d883 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/3a84f409d4c117edfdebc508cd23e8fc create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/3ef5cc982c0b45f69a26fd0f7d376415fdebabd1 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/401c7de8e122018a0e17f57c93db7ee49ab0e906 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/52fee71bb8c9c79068e1fe580677ad739a2d0415 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/57b11507813d5727b7789354d888eda83d5f3d86 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/57dff0fa53ee0ef24a43cca6ab0523bfdc1f720d create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/5c42d3df0dc400a7a4175b8d4eec6cc8ee2437b2 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/5cca20637ae75fddad9370ee930837baef8aeb43 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/5d34bc9cef0c844b9c5ebe948145c4ca11b5ca09 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/5e162fe883bd12fb1c4131d4e0c979a12bd15eac create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/5e83f8faab9c1a51a33d5e29edbb9dcec23c6092 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/61b29dc2fcef7b6fbe3e0cc88769a7ef create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/6361eca190157ece389665ee523ccc3aefcd957f create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/65150515ab3b11d657519b22bb887d74e94b2d7f create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/656f38ef6dcd58c6a909d61db11f777def69c394 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/66e0d2cafd592bf9d61ad900fade8ee530d5f3d7 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/6b5b42cb105a2c4c5fd6034e9885cbe457f1b50c create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/722ed0197cb92ecbf9745edb38275e7a9aaf322f create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/77bdd2efdf328366cbbf3c5688768dc0a88d02b1 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/7841bfa002c05c61d5a5d9241f214cc17a336166 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/7899e22fc83f6be28e9130c4a1c91a48 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/7dddccaebd16ae0c26daeffc42df50f529891119 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/8157442eee4bbfdd9716e264b11085d61a9955b7 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/81ff28ed63d5435ddc4c8771dd5d40aa658cbbe0 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/820c8c0d33c18f6c4d9edd314e91289186931ad0 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/849e9d7cee1c52105242327086997296e452b981 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/84ddb92c63e0fad7018f6069daf8779ce11501e2 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/86bc3d5dbb9313137502080e58551edd2e649c70 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/87d94d88fe29d277c76e1a52042b02c092d5ae14 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/8c4646f3357945c4e19a59ff79fffe3c874dbf16 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/90632bc6dee4eb836f3d7db1d16446a9c8510080 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/94d06016aa949e8e7203217e4cc6625ded7f4244 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/9875819b9e5783e7489c29a81cc9d4279209956a create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/9c1cc734114b29aac6c51782d5c17e9dbe1faca2 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/9d2961871eeb201ef8a6f5503d8a8b62 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/9f39e11cdd88344a4894b678e5a04a810880064d create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/a350588a6dabe4376a066aed44ef8786d8e752e7 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/a6101a79919d444e1fc50aefab5837c39e3f4a19 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/a9c8793f8fb063bec839ee1280406fe5396545e5 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/ad4e9d2234e8599bdf12607c6b8cab4edae82c4e create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/b90b6830917919e94186d312f06481bd create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/b98fd4cb1d7031240414301c19b03097c0035c6b create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/ba976fcdb4daf092ef17ce43bf2b78d9d8bc2aeb create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/bc112b571eafee0f5a031f3c9cce6244216d128d create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/c42b981c28a1715c375050f6fcf53f1d create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/c6049874b33eadb016fccf0c5fa66e556ae069b9 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/c8697bf2369f6ab85f501376c4d93bb8a56974a3 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/c8daf283e0aef2fd7b630c0430e05dc28f24ecf6 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/cacff56e1af4b8fde912822da06b10fb8c545a19 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/ce4dcc22b1d595c49a25121c0b580104 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/d0cd71dbf039fd64cf42eff30da92a71a919226a create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/d5ce626ac3264bed6af5580e341a89406857cbb9 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/d77ada02e9bc8c24b2711eca6a8f52ae356bfc21 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/d7eb9c5a0f9803df4c00390793b8ab57bd7c9484 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/dc1efccdeec17e151a1ec8228c09ab61c3040b33 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/dcea22c66c60088165a2f1772036473f create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/de539ae7442fa05dafcfe1a021f0186ef74a2b0e create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/e2306b1d6b88d0ccc4e2c3a9edb07462a5a32215 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/e2778da0240fdd15ef5844905d81c4e05f34a8bd create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/e6642e9266875f9d908942e534bf898103a2c794 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/ec6cdb929c08d8daf2bd7fc185fbf4d787b45120 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/ed8636357f79439b6a03eb14469b686cc401a1c9 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/ee313e9acecb5c688ce8c9bb10e70e136fbb9c6d create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/ef689af320e7d9e22231109faae2e8149cb86e1c create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/fda6b9a9f6ffdf4765c00465619c7ceb3f7db2e4 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_bmp/ffe829bb0adac20d9c0756f68a22d1255e4fdb54 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_json_example/013a29ea098a178f8a36741c9fd91144 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_json_example/0875575fb76d630ccb19c5da8aab66b2 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_json_example/7e7f58fc443a11a0a2c5d9b643b7e99b create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_json_example/849a23936269a261c0370b5e9abe2416 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_json_example/85282c1696d98b9843ce3e8bd1cd899f create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_json_example/90388b9c8093d8adedad0644b618da87 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_json_example/9fa2f86ea6d3ade36e961247c3026f8d create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_json_example/c4f18ca60a84e9869a28faf6f65dc758 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_json_example/d456ee029700adef5d28438593010223 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_json_example/e9f0ff6ee8d691ae69d2ecb4710030a2 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/010dc3d4b05288fcc40de2721052b3dc699f1cb3 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/0555cd5e9d99629819cc985285f80da0f00be1e9 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/0a0352aa168803ff65455792d9f6ee555c3e7c3f create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/0ed54162df93ef8d00f993ce6b59ba422903d381 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/1547b448171c700613c3946d730de496c9b9863f create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/17859046cbe4ac598a645173d679ce2a52c6afba create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/1df76c07817fbc3653a26f34d97658e9973627c2 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/1f0717f8856d7782e3ab7992d3a72d783a018443 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/23b911e4ce936def88bc9a46b8b433c0e83fba2a create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/25592201c3edff0578dbdac6b0e4f2be109ce151 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/266fd8495e0b8eb64387c1a62264185e061fee73 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/27f178cf415b4ff8671131ddf1d042dafac2fb3e create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/2a0bdc4d9cc5ea5bb21dd256d6ac96075376a94f create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/2e5d25add6adc68e0457b358c7a34abf3d41c938 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/2e6c5b6a766dd5e9bd41eacfd0a36572bd2f7544 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/2e9c935cf82f6ca640e9a9abc3c30a578ad46176 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/2fcf1ed4477f7eaee028f5b3f9edeb5f1a737826 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/3480713774f590908ca5dba16d121cdfb8fba62b create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/39289afcec60d98802b333e0fbb1da4d7aed4ce5 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/3adc488e21d4aca7bed9422f0241a42d0f93e7d9 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/3cbf274da522483dc991fad9df43a22ac4fb3173 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/3d840cdff7f5ad16fe8bcb985ed4946c03459432 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/3f1e6753c1fca958e859189857449746592158ea create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/3fa4075993cb0f9bfa8eea785174a2038a69aa1b create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/4023a373e977be58413e55350380310c5dd1fd6a create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/40caba69dce1cfc48e0e43184d2bfbc6daa4399a create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/41841e9561d8135945c1c1e55ab9e9a1e933653b create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/41d40f2d66fa43e34537385594ee9911e65deadf create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/421bd39810b50309a71adb2dadc3b19f01a52312 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/446c305b2c0665736f94fb2b62dbdef445eff0cf create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/449cee952bb645f6f4241a6665d3c6028c073c7a create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/45520b07609978c5aa3516d803527438b93fbadb create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/4da74a34bcede234b0415f77fbd87d70bf9a777e create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/51db5d31d2c5300d34831d9f23bcdd0aff9a998b create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/5cde2a9167798cb77f10abbfb2640a5c357f99fc create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/5e352fc10ac476cfbe1d755f092e069820223249 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/63661677dd1306cec4b5a565190e65adf2446e52 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/65887ed3db382aab1d9485c500f4401318d303b9 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/67b5181f8f0644597e9bde539e8f083b5cacd0e7 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/74c9dcf7afee2a6cb1ab3a2c0de744d1b03c1466 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/792181ca19e6ded261434e588bb7fc2a4816d4ce create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/79f0e2a475487f8fa69e68c1cc947c5851bda741 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/7e5fcdfeb557ce379ed96925c68505eaac0112db create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/7eec7530acf34b3a96fa9189783453999f7b6838 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/80114bf9781bffc9db411413d83541d8deaaf7c1 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/80425fb92bb86627e854892f23823fa804e5fdc3 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/821cdd6eeb919a8dd7f35289abbd583828dd4945 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/83e1a31785285338b0ddb3334b0ed098e63dedde create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/8a4c8100dedd0fb5f2a8b468c678f7ad8269deeb create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/8ae8268c24dc866c1edb3826b93a1c75dbf74ff4 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/90f72038cc627f34f074ea72eadbba87a5e3e288 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/92b67faee4a49df2cdbed785e27b4a1cddcfffa3 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/9463810467aacdc9923b2b20a2236116b760d75b create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/94d7c96aea32ad41ce643d35b951a6d8990b81d6 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/98cc7e9fe87df914d89a0aef008930f27b3c26f5 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/99172dfdb4f59aaced29c7681ac6e6ce8356e814 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/9ae3b647d895af97fe872c0b1442df7b5b767160 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/9d2b1d2121b0508a4fa8d1508adb9d05633fdac3 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/a335af37917ccf0c8b11bb884a3a74f3f1d2a7c6 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/a738609112d3a6772c50a71e2c3504ebc515b709 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/a8cecab5d917da5a4729632a7a18c564d7e1607d create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/ade919ab2b4a458e806575c941dfe50ae3fd3621 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/b1251621a5eb5e7fda9cac9baead1c993a285c36 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/b1516b78c3dfe77eeb554985fd7344c0478fbbcb create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/b41241740f5f8ad2c1d408f7bb6a313bd863c158 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/b799c8596523a7ebeb8e11ada08818c10f7eabfc create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/ba48d0521a111222dc95a3a997c7c92dea5f4443 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/c01457c6889fb1b597d308363a36412c0b7f90e7 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/c82ebc0d6688d104af04fd20d6d3da591dc391f7 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/c9a03eb758dd84e954e3d70916e2311e8fd21f3c create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/cf892756b33578a54ab20044514e573328d2f1d7 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/d3bc3f158a63f1d50b474addd3f7b3d17f23e8e9 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/d4906950aa9d60ad09dc0f5413c3d88080c3bc37 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/da31578a8068bad65e1c7a3d06e8f543a2a0bc65 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/dd4a9b5d0740679c249fc884efc499433b29436b create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/deea4ecc6f0b2a6d89fd25ff76762299f21602fb create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/e1040c7ffcb39915e0f539018c81f9798924cba6 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/e381dc85682cc33ad99f622b89d145b47f7d6392 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/ea24498fc7a144fccc6f1665ebf7020df803dd1a create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/eaa5d677e797c07bac98c3c7051abad91852e7c6 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/ed7871269315725535d8bffec7836c45a3fc5c26 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/ee8460f4077064c5a2137075b48eba7d3db5c570 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/ef09f26e0ee61329f84a9f589629a865ae9ee0a6 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/f477da4d7d8ff2066041e1dd5ee4e833b7111a1a create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/f8a379b2498a4eb452a85791a49adf065dab59ae create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/fe67bccb06f2174523943cc684518fcf1f7f8046 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_png/ff1e67d17c1c27ef0d97900d0ea276b563a64628 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/02cc44cdfec1d9d0d0c66c5a5f40d3d20e4c4c3a create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/087e1d7fae1c1ddcbaa3b5f822a171ad15498186 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/0f61c33027394a0f14d29dcd22f405cad943b7cf create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/10cdebea1659c21a0248f88654ae41f62786abf1 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/126e68def9fd973a100e0f66cadf09448a716b57 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/1275d41ebf8788ce3a949352e4bc654b04012da3 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/1a7f1c407fb3864ddb559f88f373a21d1be51584 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/1c3e1c91f187f6bcea86f172ff5bbbd955a9654d create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/300fe1e0a47543037cbf0243b6756c9aa48799c4 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/31ec5b0134bedcfe283f4978e6e65b7d35d5d4ad create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/4e7cbb27667bcfca92838aa8020749990013a9b1 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/585e469231d202812bfba8285fb30c8e31c857b9 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/58eab6bc2386e2ef43fe4f55cb6ad3611399d5de create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/63448c6a9feb8c72b3e82af4d735ec2e62ddd328 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/6874d5b1c7a64b596c61f24877d422e89bebe58b create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/7501f79cb067da108020579ed654349c7933d22f create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/782051f8120182b860c7fe1b265179cfa2fe03fd create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/793feab2deb35e284a975f6527d76a8be5540fe6 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/7f41ec3a9805c6b8f3656c4f9f6d0ff7dbf8a329 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/8210dc595a2652f2f812093b01e239e7918ea065 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/8dffe4c5c26d891b578fd2ea4b9adfc0c96ad5f7 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/91d787a9298ddc015efa783a92c4bdba8af0d7de create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/92c065286f956f086e977556358f6b54b12bcacc create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/a35c9bb71792b60a13dea23a41b41847ad4b93d6 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/a6ea960c7b4d42772888280277b26e645ceee904 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/aa526aa853333f0bb11804b5243df411452cecd2 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/ca533cd26c7ca6bf69e62351b265ded496fdf1d9 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/f38c61da15f2cb7a39ff02e69f0b00e99f37ec86 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/f88f1012473e6cfcc9b39b2552f682b2f73eff8c create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/fa79819c5de04bc06c69bec3fa7f2e982826ea2f create mode 100644 tensorflow/core/kernels/fuzzing/corpus/decode_wav/fce08de222896ac3a20657a3b4f42d5b6c54a96a create mode 100644 tensorflow/core/kernels/fuzzing/corpus/string_split/4c01a1504da9de2216894743ecc44424 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/string_split/5bf16424630b5afbcffe711fb9834440 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/string_split/a7185605aef0a8fd682fcb4656e4a736 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/string_split/d5606def44fdbb9385dd764612069db0 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/string_split/dbac766f3160de65894bf5153f478146 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/string_split/e85ff62f6d457666f54a37a19a115a24 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/string_split_v2/00fd47bf73afcb72e7ed51bffd5f5fec create mode 100644 tensorflow/core/kernels/fuzzing/corpus/string_split_v2/14908973e6720513a5f37676cb9fcc29 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/string_split_v2/2779ba7c4d23eee9f79efa3660084c5d create mode 100644 tensorflow/core/kernels/fuzzing/corpus/string_split_v2/5bf16424630b5afbcffe711fb9834440 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/string_split_v2/89734a96b93275e495a9498b806fafe1 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/string_split_v2/d5606def44fdbb9385dd764612069db0 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/string_to_number/2db83ea58639b6d7d585fa12e3947a82 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/string_to_number/36b4a931886b941dc41180050d12ca94 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/string_to_number/50a2fabfdd276f573ff97ace8b11c5f4 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/string_to_number/62edb2a1eee34b001652cd86584becf2 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/string_to_number/90013d1ec28c46a5c00574e60c70b6fc create mode 100644 tensorflow/core/kernels/fuzzing/corpus/string_to_number/94f3e3cee6957ce5815326d6788c85f4 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/string_to_number/96f547bc04bb913da0bc08915238ebd8 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/string_to_number/d3a903d18fc11e1f35c572ad4da690ed create mode 100644 tensorflow/core/kernels/fuzzing/corpus/string_to_number/e3b629c92af44260c189deb32d6f06f3 create mode 100644 tensorflow/core/kernels/fuzzing/corpus/string_to_number/f03eecf3bcfe4967a1888156a3115c8d create mode 100644 tensorflow/core/kernels/fuzzing/corpus/string_to_number/fa54ca9186f77122ae2a82684a062e16 create mode 100644 tensorflow/core/kernels/fuzzing/dictionaries/decode_json_example.dict create mode 100644 tensorflow/core/kernels/fuzzing/dictionaries/decode_png.dict create mode 100644 tensorflow/core/kernels/fuzzing/dictionaries/decode_wav.dict create mode 100644 tensorflow/core/kernels/mkl_quantized_pooling_ops_test.cc create mode 100644 tensorflow/core/kernels/scan_ops_test.cc create mode 100644 tensorflow/core/kernels/tensor_forest/BUILD create mode 100644 tensorflow/core/kernels/tensor_forest/prediction_ops.cc create mode 100644 tensorflow/core/kernels/tensor_forest/resource_ops.cc create mode 100644 tensorflow/core/kernels/tensor_forest/resources.cc create mode 100644 tensorflow/core/kernels/tensor_forest/resources.h create mode 100644 tensorflow/core/ops/tensor_forest_ops.cc create mode 100644 tensorflow/core/platform/default/logger.cc create mode 100644 tensorflow/core/platform/logger.h create mode 100644 tensorflow/core/platform/platform_strings.cc create mode 100644 tensorflow/core/platform/platform_strings.h create mode 100644 tensorflow/core/platform/platform_strings_computed.h create mode 100644 tensorflow/core/platform/platform_strings_test.cc create mode 100644 tensorflow/core/util/dump_graph.cc create mode 100644 tensorflow/core/util/dump_graph.h create mode 100644 tensorflow/core/util/dump_graph_test.cc create mode 100644 tensorflow/core/util/permutation_output_iterator.h create mode 100644 tensorflow/core/util/tensor_ops_util.h create mode 100644 tensorflow/go/op/gradients.go create mode 100644 tensorflow/go/op/gradients_test.go create mode 100644 tensorflow/lite/core/subgraph.cc create mode 100644 tensorflow/lite/core/subgraph.h create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/.gitignore create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/README.md create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/arm_cmplx_mag_squared_q10p6.c create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/arm_cmplx_mag_squared_q10p6.h create mode 100755 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/create_constants.py delete mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hann.h create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.cc create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.h delete mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.h delete mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor_test.cc create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.cc create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.h delete mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.h create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/.gitignore create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/README.md rename tensorflow/lite/experimental/micro/{tools/make/targets/apollo3evb => examples/micro_speech/apollo3}/_main.c (100%) create mode 100755 tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/apollo3.h create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/captured_data_to_wav.py create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/compare_1k.py create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k.cc create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k_cmsis_test.cmd create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k_micro_test.cmd create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_test.cmd create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_cmsis_scores.cmd create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_cmsis_voice.cmd create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_main.c create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_test.cc create mode 100755 tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/system_apollo3.c create mode 100755 tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/system_apollo3.h create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.cc create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.h create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider_test.cc create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider.cc create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider.h create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider_test.cc create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/main.cc rename tensorflow/lite/experimental/micro/examples/micro_speech/{CMSIS/no_power_spectrum_data.cc => model_settings.cc} (73%) create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h rename tensorflow/lite/experimental/micro/examples/micro_speech/{CMSIS/yes_power_spectrum_data.cc => timer.cc} (73%) create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/timer.h create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/timer_test.cc delete mode 100644 tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/README.md delete mode 100644 tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/get_yesno_data.cmd delete mode 100644 tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/preprocessor_test.cmd delete mode 100644 tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/replace_calculated_data.py delete mode 100644 tensorflow/lite/experimental/micro/tools/make/targets/cmsis-dsp_makefile.inc create mode 100644 tensorflow/lite/g3doc/using_select_tf_ops.md create mode 100644 tensorflow/lite/java/src/testdata/string.bin create mode 100644 tensorflow/lite/kernels/fill.cc create mode 100644 tensorflow/lite/kernels/fill_test.cc delete mode 100644 tensorflow/lite/kernels/internal/optimized/cblas_conv.h delete mode 100644 tensorflow/lite/kernels/internal/optimized/cblas_reference.h create mode 100644 tensorflow/lite/kernels/mirror_pad.cc create mode 100644 tensorflow/lite/kernels/mirror_pad_test.cc create mode 100644 tensorflow/lite/kernels/split_v.cc create mode 100644 tensorflow/lite/kernels/split_v_test.cc create mode 100644 tensorflow/lite/kernels/squared_difference.cc create mode 100644 tensorflow/lite/kernels/squared_difference_test.cc create mode 100644 tensorflow/lite/toco/toco_convert.cc create mode 100644 tensorflow/lite/toco/toco_convert.h create mode 100644 tensorflow/lite/toco/toco_convert_test.cc create mode 100644 tensorflow/lite/tools/pip_package/MANIFEST.in create mode 100644 tensorflow/lite/tools/pip_package/README.md create mode 100644 tensorflow/lite/tools/pip_package/build_pip_package.sh create mode 100644 tensorflow/lite/tools/pip_package/setup.py create mode 100644 tensorflow/opensource_only.files create mode 100644 tensorflow/python/data/benchmarks/batch_benchmark.py create mode 100644 tensorflow/python/data/benchmarks/filter_benchmark.py create mode 100644 tensorflow/python/data/benchmarks/from_tensor_slices_benchmark.py create mode 100644 tensorflow/python/data/benchmarks/map_benchmark.py create mode 100644 tensorflow/python/data/experimental/benchmarks/autotune_benchmark.py create mode 100644 tensorflow/python/data/experimental/benchmarks/csv_dataset_benchmark.py delete mode 100644 tensorflow/python/data/experimental/benchmarks/map_benchmark.py create mode 100644 tensorflow/python/data/experimental/benchmarks/map_vectorization_benchmark.py create mode 100644 tensorflow/python/data/experimental/benchmarks/matching_files_benchmark.py create mode 100644 tensorflow/python/data/experimental/benchmarks/optimize_benchmark.py create mode 100644 tensorflow/python/data/experimental/benchmarks/unbatch_benchmark.py delete mode 100644 tensorflow/python/data/experimental/kernel_tests/batch_dataset_op_test.py create mode 100644 tensorflow/python/data/experimental/kernel_tests/cardinality_test.py delete mode 100644 tensorflow/python/data/experimental/kernel_tests/function_buffering_resource_test.py rename tensorflow/python/data/{kernel_tests/matching_files_dataset_op_test.py => experimental/kernel_tests/matching_files_test.py} (57%) create mode 100644 tensorflow/python/data/experimental/ops/cardinality.py create mode 100644 tensorflow/python/data/experimental/ops/filter_for_shard_ops.py create mode 100644 tensorflow/python/data/experimental/ops/matching_files.py create mode 100644 tensorflow/python/data/experimental/ops/optimization_options.py create mode 100644 tensorflow/python/data/experimental/ops/threading_options.py delete mode 100644 tensorflow/python/data/kernel_tests/batch_dataset_op_test.py create mode 100644 tensorflow/python/data/kernel_tests/batch_test.py delete mode 100644 tensorflow/python/data/kernel_tests/cache_dataset_op_test.py create mode 100644 tensorflow/python/data/kernel_tests/cache_test.py rename tensorflow/python/data/kernel_tests/{concatenate_dataset_op_test.py => concatenate_test.py} (75%) rename tensorflow/python/data/kernel_tests/{range_dataset_op_test.py => dataset_checkpoint_test.py} (84%) delete mode 100644 tensorflow/python/data/kernel_tests/dataset_constructor_op_test.py rename tensorflow/python/data/kernel_tests/{dataset_ops_test.py => dataset_test.py} (69%) delete mode 100644 tensorflow/python/data/kernel_tests/filter_dataset_op_test.py create mode 100644 tensorflow/python/data/kernel_tests/filter_test.py create mode 100644 tensorflow/python/data/kernel_tests/fixed_length_record_dataset_test.py rename tensorflow/python/data/kernel_tests/{flat_map_dataset_op_test.py => flat_map_test.py} (57%) rename tensorflow/python/data/kernel_tests/{dataset_from_generator_op_test.py => from_generator_test.py} (85%) create mode 100644 tensorflow/python/data/kernel_tests/from_sparse_tensor_slices_test.py create mode 100644 tensorflow/python/data/kernel_tests/from_tensor_slices_test.py create mode 100644 tensorflow/python/data/kernel_tests/from_tensors_test.py delete mode 100644 tensorflow/python/data/kernel_tests/inputs_test.py rename tensorflow/python/data/kernel_tests/{interleave_dataset_op_test.py => interleave_test.py} (85%) create mode 100644 tensorflow/python/data/kernel_tests/iterator_checkpoint_test.py rename tensorflow/python/data/kernel_tests/{iterator_ops_cluster_test.py => iterator_cluster_test.py} (96%) rename tensorflow/python/data/kernel_tests/{iterator_ops_test.py => iterator_test.py} (83%) delete mode 100644 tensorflow/python/data/kernel_tests/list_files_dataset_op_test.py create mode 100644 tensorflow/python/data/kernel_tests/list_files_test.py rename tensorflow/python/data/kernel_tests/{map_dataset_op_test.py => map_test.py} (80%) rename tensorflow/python/data/kernel_tests/{optional_ops_test.py => optional_test.py} (64%) create mode 100644 tensorflow/python/data/kernel_tests/padded_batch_test.py rename tensorflow/python/data/kernel_tests/{prefetch_dataset_op_test.py => prefetch_test.py} (52%) create mode 100644 tensorflow/python/data/kernel_tests/range_test.py delete mode 100644 tensorflow/python/data/kernel_tests/reader_dataset_ops_test.py rename tensorflow/python/data/kernel_tests/{reduce_dataset_op_test.py => reduce_test.py} (72%) create mode 100644 tensorflow/python/data/kernel_tests/repeat_test.py delete mode 100644 tensorflow/python/data/kernel_tests/sequence_dataset_op_test.py rename tensorflow/python/data/kernel_tests/{shard_dataset_op_test.py => shard_test.py} (52%) delete mode 100644 tensorflow/python/data/kernel_tests/shuffle_dataset_op_test.py create mode 100644 tensorflow/python/data/kernel_tests/shuffle_test.py create mode 100644 tensorflow/python/data/kernel_tests/skip_test.py create mode 100644 tensorflow/python/data/kernel_tests/take_test.py create mode 100644 tensorflow/python/data/kernel_tests/text_line_dataset_test.py create mode 100644 tensorflow/python/data/kernel_tests/tf_record_dataset_test.py delete mode 100644 tensorflow/python/data/kernel_tests/window_dataset_op_test.py create mode 100644 tensorflow/python/data/kernel_tests/window_test.py delete mode 100644 tensorflow/python/data/kernel_tests/zip_dataset_op_test.py create mode 100644 tensorflow/python/data/kernel_tests/zip_test.py create mode 100644 tensorflow/python/data/util/options.py create mode 100644 tensorflow/python/data/util/options_test.py create mode 100644 tensorflow/python/distribute/all_reduce.py rename tensorflow/{contrib/all_reduce/python => python/distribute}/all_reduce_test.py (97%) create mode 100644 tensorflow/python/distribute/cluster_resolver/BUILD rename tensorflow/{contrib => python/distribute}/cluster_resolver/README.md (100%) rename tensorflow/{contrib/cluster_resolver/python/training => python/distribute/cluster_resolver}/README.slurm (100%) create mode 100644 tensorflow/python/distribute/cluster_resolver/__init__.py create mode 100644 tensorflow/python/distribute/cluster_resolver/cluster_resolver.py rename tensorflow/{contrib/cluster_resolver/python/training => python/distribute/cluster_resolver}/cluster_resolver_test.py (98%) create mode 100644 tensorflow/python/distribute/cluster_resolver/gce_cluster_resolver.py rename tensorflow/{contrib/cluster_resolver/python/training => python/distribute/cluster_resolver}/gce_cluster_resolver_test.py (98%) create mode 100644 tensorflow/python/distribute/cluster_resolver/kubernetes_cluster_resolver.py rename tensorflow/{contrib/cluster_resolver/python/training => python/distribute/cluster_resolver}/kubernetes_cluster_resolver_test.py (87%) create mode 100644 tensorflow/python/distribute/cluster_resolver/slurm_cluster_resolver.py rename tensorflow/{contrib/cluster_resolver/python/training => python/distribute/cluster_resolver}/slurm_cluster_resolver_test.py (85%) create mode 100644 tensorflow/python/distribute/cluster_resolver/tfconfig_cluster_resolver.py rename tensorflow/{contrib/cluster_resolver/python/training => python/distribute/cluster_resolver}/tfconfig_cluster_resolver_test.py (72%) create mode 100644 tensorflow/python/distribute/cluster_resolver/tpu_cluster_resolver.py rename tensorflow/{contrib/cluster_resolver/python/training => python/distribute/cluster_resolver}/tpu_cluster_resolver_test.py (86%) rename tensorflow/{contrib/distribute/python/cross_tower_ops.py => python/distribute/cross_device_ops.py} (86%) rename tensorflow/{contrib/distribute/python/cross_tower_utils.py => python/distribute/cross_device_utils.py} (99%) rename tensorflow/python/{training => distribute}/device_util.py (98%) rename tensorflow/python/{training => distribute}/device_util_test.py (95%) create mode 100644 tensorflow/python/distribute/distribute_lib.py rename tensorflow/python/{training/distribute_test.py => distribute/distribute_lib_test.py} (75%) create mode 100644 tensorflow/python/distribute/distribution_strategy_context.py rename tensorflow/{contrib/distribute/python => python/distribute}/input_ops.py (89%) rename tensorflow/{contrib/distribute/python => python/distribute}/input_ops_test.py (89%) create mode 100644 tensorflow/python/distribute/mirrored_strategy.py create mode 100644 tensorflow/python/distribute/reduce_util.py rename tensorflow/{contrib/distribute/python => python/distribute}/shared_variable_creator.py (100%) rename tensorflow/{contrib/distribute/python => python/distribute}/shared_variable_creator_test.py (97%) rename tensorflow/{contrib/distribute/python => python/distribute}/values.py (77%) create mode 100644 tensorflow/python/eager/execution_callbacks_test.py create mode 100644 tensorflow/python/eager/function_gradients_test.py create mode 100644 tensorflow/python/keras/engine/base_layer_utils.py create mode 100644 tensorflow/python/keras/engine/input_spec.py create mode 100644 tensorflow/python/keras/engine/training_dataset_test.py create mode 100644 tensorflow/python/keras/layers/unified_lstm_test.py create mode 100644 tensorflow/python/keras/optimizer_v2/adadelta.py create mode 100644 tensorflow/python/keras/optimizer_v2/adadelta_test.py create mode 100644 tensorflow/python/keras/optimizer_v2/adagrad.py create mode 100644 tensorflow/python/keras/optimizer_v2/adagrad_test.py create mode 100644 tensorflow/python/keras/optimizer_v2/adam_test.py create mode 100644 tensorflow/python/keras/optimizer_v2/adamax.py create mode 100644 tensorflow/python/keras/optimizer_v2/adamax_test.py create mode 100644 tensorflow/python/keras/optimizer_v2/ftrl.py create mode 100644 tensorflow/python/keras/optimizer_v2/ftrl_test.py create mode 100644 tensorflow/python/keras/optimizer_v2/nadam.py create mode 100644 tensorflow/python/keras/optimizer_v2/nadam_test.py create mode 100644 tensorflow/python/keras/optimizer_v2/rmsprop.py create mode 100644 tensorflow/python/keras/optimizer_v2/rmsprop_test.py create mode 100644 tensorflow/python/keras/utils/losses_utils.py create mode 100644 tensorflow/python/keras/utils/tf_utils_test.py create mode 100644 tensorflow/python/kernel_tests/linalg/linear_operator_adjoint_test.py create mode 100644 tensorflow/python/kernel_tests/linalg/linear_operator_algebra_test.py create mode 100644 tensorflow/python/kernel_tests/linalg/linear_operator_inversion_test.py rename tensorflow/python/kernel_tests/{ => signal}/dct_ops_test.py (67%) rename tensorflow/python/kernel_tests/{ => signal}/fft_ops_test.py (96%) create mode 100644 tensorflow/python/kernel_tests/unicode_decode_op_test.py create mode 100644 tensorflow/python/kernel_tests/unicode_encode_op_test.py create mode 100644 tensorflow/python/ops/gradient_checker_v2.py create mode 100644 tensorflow/python/ops/gradient_checker_v2_test.py create mode 100644 tensorflow/python/ops/linalg/cholesky_registrations.py create mode 100644 tensorflow/python/ops/linalg/linear_operator_adjoint.py create mode 100644 tensorflow/python/ops/linalg/linear_operator_algebra.py create mode 100644 tensorflow/python/ops/linalg/linear_operator_inversion.py create mode 100644 tensorflow/python/ops/linalg/matmul_registrations.py rename tensorflow/{tools/compatibility/testdata/test_file_v1_10.py => python/ops/optional_grad.py} (62%) create mode 100644 tensorflow/python/ops/ragged/ragged_string_ops.py create mode 100644 tensorflow/python/ops/ragged/ragged_tensor_shape.py create mode 100644 tensorflow/python/ops/ragged/ragged_tensor_shape_test.py delete mode 100644 tensorflow/python/ops/signal/__init__.py create mode 100644 tensorflow/python/ops/signal/dct_ops.py rename tensorflow/python/ops/{spectral_ops.py => signal/fft_ops.py} (51%) create mode 100644 tensorflow/python/ops/signal/signal.py create mode 100644 tensorflow/python/ops/sort_ops.py rename tensorflow/{contrib/framework => }/python/ops/sort_ops_test.py (90%) delete mode 100644 tensorflow/python/ops/spectral_grad.py create mode 100644 tensorflow/python/ops/tensor_forest_ops.py create mode 100644 tensorflow/python/platform/__init__.py create mode 100644 tensorflow/python/saved_model/load.py create mode 100644 tensorflow/python/saved_model/load_test.py create mode 100644 tensorflow/python/saved_model/saved_object_graph.proto create mode 100644 tensorflow/python/util/dispatch.py create mode 100644 tensorflow/python/util/dispatch_test.py create mode 100644 tensorflow/stream_executor/dnn.proto create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.-tensor-spec.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.data.experimental.-optimization-options.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.data.experimental.-threading-options.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.distribute.-input-context.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.distribute.-input-replication-mode.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.distribute.-reduce-op.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.distribute.-replica-context.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.distribute.-strategy-extended.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.distribute.-strategy.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.distribute.pbtxt rename tensorflow/tools/api/golden/{v2/tensorflow.train.-profiler-hook.pbtxt => v1/tensorflow.estimator.experimental.-in-memory-evaluator-hook.pbtxt} (71%) create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.io.gfile.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-binary-crossentropy.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-categorical-crossentropy.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-mean-absolute-error.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-mean-absolute-percentage-error.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-mean-squared-error.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-mean-squared-logarithmic-error.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-accuracy.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-binary-accuracy.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-categorical-accuracy.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-false-negatives.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-false-positives.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-mean.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-precision.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-recall.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-sparse-categorical-accuracy.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-true-negatives.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-true-positives.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.metrics.-accuracy.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.metrics.-binary-accuracy.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.metrics.-categorical-accuracy.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.metrics.-false-negatives.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.metrics.-false-positives.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.metrics.-mean.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.metrics.-precision.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.metrics.-recall.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.metrics.-sparse-categorical-accuracy.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.metrics.-true-negatives.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.metrics.-true-positives.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.-conditional-accumulator-base.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.-conditional-accumulator.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.-device-spec.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.-dimension.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.-graph-keys.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.-tensor-info.-coo-sparse.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.-tensor-info.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.-tensor-spec.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.app.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.data.-iterator.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.data.experimental.-optimization-options.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.data.experimental.-threading-options.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.distribute.-input-context.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.distribute.-input-replication-mode.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.distribute.-reduce-op.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.distribute.-replica-context.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.distribute.-strategy-extended.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.distribute.-strategy.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.distribute.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.estimator.experimental.-in-memory-evaluator-hook.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.gfile.-fast-g-file.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.gfile.-g-file.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.gfile.-open.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.gfile.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.io.gfile.pbtxt rename tensorflow/tools/api/golden/v2/{tensorflow.nn.rnn_cell.-multi-r-n-n-cell.pbtxt => tensorflow.keras.layers.-dense-features.pbtxt} (77%) create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-linear-model.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-binary-crossentropy.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-categorical-crossentropy.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-mean-absolute-error.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-mean-absolute-percentage-error.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-mean-squared-error.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-mean-squared-logarithmic-error.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-reduction.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-accuracy.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-binary-accuracy.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-categorical-accuracy.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-false-negatives.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-false-positives.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-mean.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-precision.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-recall.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-sparse-categorical-accuracy.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-true-negatives.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-true-positives.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.logging.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.metrics.-accuracy.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.metrics.-binary-accuracy.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.metrics.-categorical-accuracy.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.metrics.-false-negatives.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.metrics.-false-positives.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.metrics.-mean.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.metrics.-precision.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.metrics.-recall.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.metrics.-sparse-categorical-accuracy.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.metrics.-true-negatives.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.metrics.-true-positives.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.profiler.-advice-proto.-checker.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.profiler.-advice-proto.-checkers-entry.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.profiler.-advice-proto.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.profiler.-graph-node-proto.-input-shapes-entry.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.profiler.-graph-node-proto.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.profiler.-multi-graph-node-proto.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.profiler.-op-log-proto.-id-to-string-entry.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.profiler.-op-log-proto.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.profiler.-profile-option-builder.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.profiler.-profiler.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.profiler.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.spectral.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.summary.-summary-writer.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-adadelta-optimizer.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-adagrad-d-a-optimizer.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-adagrad-optimizer.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-adam-optimizer.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-checkpoint-saver-listener.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-chief-session-creator.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-ftrl-optimizer.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-gradient-descent-optimizer.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-looper-thread.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-momentum-optimizer.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-monitored-session.-step-context.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-monitored-session.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-nan-loss-during-training-error.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-optimizer.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-proximal-adagrad-optimizer.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-r-m-s-prop-optimizer.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-scaffold.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-second-or-step-timer.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-session-creator.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-session-manager.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-session-run-args.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-session-run-context.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-session-run-values.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-singular-monitored-session.-step-context.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-singular-monitored-session.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-supervisor.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-vocab-info.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.train.-worker-session-creator.pbtxt create mode 100644 tensorflow/tools/ci_build/Dockerfile.rbe.cuda10.0-cudnn7-ubuntu14.04 create mode 100644 tensorflow/tools/compatibility/ast_edits_test.py create mode 100644 tensorflow/tools/compatibility/testdata/test_file_v1_12.py create mode 100644 tensorflow/tools/compatibility/tf_upgrade_v2_main.py create mode 100644 tensorflow/tools/dockerfiles/.gitignore rename tensorflow/tools/dockerfiles/dockerfiles/{nvidia-devel-jupyter.Dockerfile => gpu-devel-jupyter.Dockerfile} (66%) rename tensorflow/tools/dockerfiles/dockerfiles/{nvidia-devel.Dockerfile => gpu-devel.Dockerfile} (76%) rename tensorflow/tools/dockerfiles/dockerfiles/{nvidia-jupyter.Dockerfile => gpu-jupyter.Dockerfile} (62%) rename tensorflow/tools/dockerfiles/dockerfiles/{nvidia.Dockerfile => gpu.Dockerfile} (69%) create mode 100644 tensorflow/tools/dockerfiles/partials/test-import.partial.Dockerfile delete mode 100644 tensorflow/tools/dockerfiles/partials/ubuntu.partial.Dockerfile rename tensorflow/tools/dockerfiles/partials/{ => ubuntu}/bazel.partial.Dockerfile (58%) rename tensorflow/tools/dockerfiles/partials/{ubuntu-devel.partial.Dockerfile => ubuntu/cpu-devel.partial.Dockerfile} (86%) create mode 100644 tensorflow/tools/dockerfiles/partials/ubuntu/cpu.partial.Dockerfile rename tensorflow/tools/dockerfiles/partials/{ => ubuntu}/nvidia-devel.partial.Dockerfile (78%) rename tensorflow/tools/dockerfiles/partials/{ => ubuntu}/nvidia.partial.Dockerfile (78%) rename tensorflow/tools/dockerfiles/partials/{ => ubuntu}/python.partial.Dockerfile (66%) create mode 100644 tensorflow/tools/dockerfiles/partials/ubuntu/test-devel.partial.Dockerfile create mode 100644 tensorflow/tools/dockerfiles/partials/ubuntu/version.partial.Dockerfile create mode 100644 tensorflow/tools/dockerfiles/readme-for-jupyter.md create mode 100755 tensorflow/tools/dockerfiles/tests/build-cpu.sh create mode 100755 tensorflow/tools/dockerfiles/tests/build-gpu.sh create mode 100755 tensorflow/tools/dockerfiles/tests/import-gpu.sh create mode 100755 tensorflow/tools/dockerfiles/tests/import.sh rename tensorflow/tools/dockerfiles/{assembler.Dockerfile => tools.Dockerfile} (95%) create mode 100644 tensorflow/tools/docs/generate2.py rename tensorflow/{contrib/estimator/python/estimator/linear.py => tools/docs/generate2_test.py} (60%) delete mode 100644 tensorflow/tools/docs/generate_1_0.py delete mode 100644 third_party/eigen_reshaped.patch create mode 100644 third_party/icu/data/BUILD.bazel create mode 100644 third_party/icu/data/LICENSE create mode 100644 third_party/icu/data/icu_conversion_data.c.gz.aa create mode 100644 third_party/icu/data/icu_conversion_data.c.gz.ab create mode 100644 third_party/icu/data/icu_conversion_data.c.gz.ac create mode 100644 third_party/icu/data/icu_conversion_data.c.gz.ad create mode 100644 third_party/icu/data/icu_conversion_data.c.gz.ae create mode 100644 third_party/icu/data/icu_conversion_data.c.gz.af create mode 100644 third_party/icu/data/icu_conversion_data.c.gz.ag create mode 100644 third_party/icu/data/icu_conversion_data.c.gz.ah create mode 100644 third_party/icu/data/icu_conversion_data.c.gz.ai create mode 100644 third_party/icu/data/icu_conversion_data.c.gz.aj create mode 100644 third_party/icu/udata.patch create mode 100644 third_party/toolchains/preconfig/ubuntu14.04/cuda10.0-cudnn7/WORKSPACE create mode 100755 third_party/toolchains/preconfig/ubuntu14.04/cuda10.0-cudnn7/cuda/BUILD create mode 100755 third_party/toolchains/preconfig/ubuntu14.04/cuda10.0-cudnn7/cuda/build_defs.bzl create mode 100755 third_party/toolchains/preconfig/ubuntu14.04/cuda10.0-cudnn7/cuda/cuda/cuda_config.h create mode 100755 third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda10.0/BUILD create mode 100755 third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda10.0/CROSSTOOL create mode 100755 third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda10.0/clang/bin/crosstool_wrapper_driver_is_not_gcc create mode 100755 third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda10.0/windows/msvc_wrapper_for_nvcc.bat create mode 100755 third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda10.0/windows/msvc_wrapper_for_nvcc.py create mode 100755 third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/BUILD create mode 100755 third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/CROSSTOOL create mode 100755 third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/clang/bin/crosstool_wrapper_driver_is_not_gcc create mode 100755 third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/windows/msvc_wrapper_for_nvcc.bat create mode 100755 third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/windows/msvc_wrapper_for_nvcc.py diff --git a/.github/ISSUE_TEMPLATE/40-tflite-op-request.md b/.github/ISSUE_TEMPLATE/40-tflite-op-request.md new file mode 100644 index 0000000000..7b391279e4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/40-tflite-op-request.md @@ -0,0 +1,24 @@ +--- +name: TensorFlow Lite Op Request +about: Use this template for reporting ops you are using or missing. + +--- + + +**System information** +- OS Platform and Distribution (e.g., Linux Ubuntu 16.04): +- TensorFlow installed from (source or binary): +- TensorFlow version (or github SHA if from source): + + +**Provide the text output from tflite_convert** + +``` +# Copy and paste here +``` + +Also, please include a link to a GraphDef or the model if possible. + +**Any other info / logs** + +Include any logs or source code that would be helpful to diagnose the problem. If including tracebacks, please include the full traceback. Large logs and files should be attached. diff --git a/README.md b/README.md index 8af5370bef..6fefdd3224 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,8 @@ data flow graphs. The graph nodes represent mathematical operations, while the graph edges represent the multidimensional data arrays (tensors) that flow between them. This flexible architecture enables you to deploy computation to one or more CPUs or GPUs in a desktop, server, or mobile device without rewriting -code. TensorFlow also includes [TensorBoard](https://www.tensorflow.org/guide/summaries_and_tensorboard), a data visualization toolkit. +code. TensorFlow also includes [TensorBoard](https://github.com/tensorflow/tensorboard), +a data visualization toolkit. TensorFlow was originally developed by researchers and engineers working on the Google Brain team within Google's Machine Intelligence Research @@ -111,7 +112,7 @@ The TensorFlow project strives to abide by generally accepted best practices in Build Type | Status | Artifacts ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- **IBM s390x** | [![Build Status](http://ibmz-ci.osuosl.org/job/TensorFlow_IBMZ_CI/badge/icon)](http://ibmz-ci.osuosl.org/job/TensorFlow_IBMZ_CI/) | TBA -**IBM ppc64le CPU** | [![Build Status](http://powerci.osuosl.org/job/TensorFlow_Ubuntu_16.04_CPU/badge/icon)](http://powerci.osuosl.org/job/TensorFlow_Ubuntu_16.04_CPU/) | TBA +**IBM ppc64le CPU** | [![Build Status](http://powerci.osuosl.org/job/TensorFlow_PPC64LE_CPU_Build/badge/icon)](http://powerci.osuosl.org/job/TensorFlow_PPC64LE_CPU_Build/) | TBA **IBM ppc64le GPU** Nightly | [![Build Status](https://powerci.osuosl.org/job/TensorFlow_PPC64LE_GPU_Nightly_Artifact/badge/icon)](https://powerci.osuosl.org/job/TensorFlow_PPC64LE_GPU_Nightly_Artifact/) | [Nightly](https://powerci.osuosl.org/job/TensorFlow_PPC64LE_GPU_Nightly_Artifact/) **IBM ppc64le GPU** Stable Release | [![Build Status](https://powerci.osuosl.org/job/TensorFlow_PPC64LE_GPU_Release_Build/badge/icon)](https://powerci.osuosl.org/job/TensorFlow_PPC64LE_GPU_Release_Build/) | [Release](https://powerci.osuosl.org/job/TensorFlow_PPC64LE_GPU_Release_Build/) **Linux CPU with Intel® MKL-DNN** Nightly | [![Build Status](https://tensorflow-ci.intel.com/job/tensorflow-mkl-linux-cpu/badge/icon)](https://tensorflow-ci.intel.com/job/tensorflow-mkl-linux-cpu/) | [Nightly](https://tensorflow-ci.intel.com/job/tensorflow-mkl-build-whl-nightly/) @@ -127,6 +128,7 @@ Build Type * [TensorFlow Roadmap](https://www.tensorflow.org/community/roadmap) * [TensorFlow White Papers](https://www.tensorflow.org/about/bib) * [TensorFlow YouTube Channel](https://www.youtube.com/channel/UC0rqucBdTuFTjJiefW5t-IQ) +* [TensorFlow Visualization Toolkit](https://github.com/tensorflow/tensorboard) Learn more about the TensorFlow community at the [community page of tensorflow.org](https://www.tensorflow.org/community) for a few ways to participate. diff --git a/WORKSPACE b/WORKSPACE index 0c7bc085b5..7cc08e0164 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,5 +1,7 @@ workspace(name = "org_tensorflow") +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + http_archive( name = "io_bazel_rules_closure", sha256 = "a38539c5b5c358548e75b44141b4ab637bba7c4dc02b46b1f62a96d6433f56ae", @@ -57,9 +59,9 @@ android_workspace() # Please add all new TensorFlow dependencies in workspace.bzl. tf_workspace() -new_http_archive( +http_archive( name = "inception_v1", - build_file = "models.BUILD", + build_file = "//:models.BUILD", sha256 = "7efe12a8363f09bc24d7b7a450304a15655a57a7751929b2c1593a71183bb105", urls = [ "http://storage.googleapis.com/download.tensorflow.org/models/inception_v1.zip", @@ -67,9 +69,9 @@ new_http_archive( ], ) -new_http_archive( +http_archive( name = "mobile_ssd", - build_file = "models.BUILD", + build_file = "//:models.BUILD", sha256 = "bddd81ea5c80a97adfac1c9f770e6f55cbafd7cce4d3bbe15fbeb041e6b8f3e8", urls = [ "http://storage.googleapis.com/download.tensorflow.org/models/object_detection/ssd_mobilenet_v1_android_export.zip", @@ -77,9 +79,9 @@ new_http_archive( ], ) -new_http_archive( +http_archive( name = "mobile_multibox", - build_file = "models.BUILD", + build_file = "//:models.BUILD", sha256 = "859edcddf84dddb974c36c36cfc1f74555148e9c9213dedacf1d6b613ad52b96", urls = [ "http://storage.googleapis.com/download.tensorflow.org/models/mobile_multibox_v1a.zip", @@ -87,9 +89,9 @@ new_http_archive( ], ) -new_http_archive( +http_archive( name = "stylize", - build_file = "models.BUILD", + build_file = "//:models.BUILD", sha256 = "3d374a730aef330424a356a8d4f04d8a54277c425e274ecb7d9c83aa912c6bfa", urls = [ "http://storage.googleapis.com/download.tensorflow.org/models/stylize_v1.zip", @@ -97,9 +99,9 @@ new_http_archive( ], ) -new_http_archive( +http_archive( name = "speech_commands", - build_file = "models.BUILD", + build_file = "//:models.BUILD", sha256 = "c3ec4fea3158eb111f1d932336351edfe8bd515bb6e87aad4f25dbad0a600d0c", urls = [ "http://storage.googleapis.com/download.tensorflow.org/models/speech_commands_v0.01.zip", diff --git a/configure.py b/configure.py index 234561d94a..5f429c3de8 100644 --- a/configure.py +++ b/configure.py @@ -238,6 +238,13 @@ def setup_python(environ_cp): write_to_bazelrc('build --python_path=\"%s"' % python_bin_path) environ_cp['PYTHON_BIN_PATH'] = python_bin_path + # If choosen python_lib_path is from a path specified in the PYTHONPATH + # variable, need to tell bazel to include PYTHONPATH + if environ_cp.get('PYTHONPATH'): + python_paths = environ_cp.get('PYTHONPATH').split(':') + if python_lib_path in python_paths: + write_action_env_to_bazelrc('PYTHONPATH', environ_cp.get('PYTHONPATH')) + # Write tools/python_bin_path.sh with open( os.path.join(_TF_WORKSPACE_ROOT, 'tools', 'python_bin_path.sh'), @@ -445,11 +452,12 @@ def convert_version_to_int(version): return int(version_str) -def check_bazel_version(min_version): - """Check installed bazel version is at least min_version. +def check_bazel_version(min_version, max_version): + """Check installed bazel version is between min_version and max_version. Args: min_version: string for minimum bazel version. + max_version: string for maximum bazel version. Returns: The bazel version detected. @@ -467,6 +475,7 @@ def check_bazel_version(min_version): min_version_int = convert_version_to_int(min_version) curr_version_int = convert_version_to_int(curr_version) + max_version_int = convert_version_to_int(max_version) # Check if current bazel version can be detected properly. if not curr_version_int: @@ -480,6 +489,10 @@ def check_bazel_version(min_version): print('Please upgrade your bazel installation to version %s or higher to ' 'build TensorFlow!' % min_version) sys.exit(0) + if curr_version_int > max_version_int: + print('Please downgrade your bazel installation to version %s or lower to ' + 'build TensorFlow!' % max_version) + sys.exit(0) return curr_version @@ -859,7 +872,7 @@ def set_tf_cuda_version(environ_cp): cuda_toolkit_paths_full = [ os.path.join(cuda_toolkit_path, x) for x in cuda_rt_lib_paths ] - if any([os.path.exists(x) for x in cuda_toolkit_paths_full]): + if any(os.path.exists(x) for x in cuda_toolkit_paths_full): break # Reset and retry @@ -1552,7 +1565,7 @@ def main(): # environment variables. environ_cp = dict(os.environ) - check_bazel_version('0.15.0') + check_bazel_version('0.15.0', '0.20.0') reset_tf_configure_bazelrc() # Explicitly import tools/bazel.rc, this is needed for Bazel 0.19.0 or later @@ -1694,6 +1707,7 @@ def main(): config_info_line('nohdfs', 'Disable HDFS support.') config_info_line('noignite', 'Disable Apacha Ignite support.') config_info_line('nokafka', 'Disable Apache Kafka support.') + config_info_line('nonccl', 'Disable NVIDIA NCCL support.') if __name__ == '__main__': diff --git a/tensorflow/BUILD b/tensorflow/BUILD index 859dc3b8d7..fd4b94202a 100644 --- a/tensorflow/BUILD +++ b/tensorflow/BUILD @@ -43,6 +43,11 @@ TENSORFLOW_API_INIT_FILES_V2 = ( TENSORFLOW_API_INIT_FILES + get_compat_files(TENSORFLOW_API_INIT_FILES_V1, 1) ) +# @unused +TENSORFLOW_API_INIT_FILES_V1_WITH_COMPAT = ( + TENSORFLOW_API_INIT_FILES_V1 + get_compat_files(TENSORFLOW_API_INIT_FILES_V1, 1) +) + # Config setting used when building for products # which requires restricted licenses to be avoided. config_setting( @@ -213,31 +218,37 @@ config_setting( # config_setting( name = "no_aws_support", - define_values = {"no_aws_support": "false"}, + define_values = {"no_aws_support": "true"}, visibility = ["//visibility:public"], ) config_setting( name = "no_gcp_support", - define_values = {"no_gcp_support": "false"}, + define_values = {"no_gcp_support": "true"}, visibility = ["//visibility:public"], ) config_setting( name = "no_hdfs_support", - define_values = {"no_hdfs_support": "false"}, + define_values = {"no_hdfs_support": "true"}, visibility = ["//visibility:public"], ) config_setting( name = "no_ignite_support", - define_values = {"no_ignite_support": "false"}, + define_values = {"no_ignite_support": "true"}, visibility = ["//visibility:public"], ) config_setting( name = "no_kafka_support", - define_values = {"no_kafka_support": "false"}, + define_values = {"no_kafka_support": "true"}, + visibility = ["//visibility:public"], +) + +config_setting( + name = "no_nccl_support", + define_values = {"no_nccl_support": "true"}, visibility = ["//visibility:public"], ) @@ -350,7 +361,7 @@ package_group( "-//third_party/tensorflow/python/estimator", "//learning/meta_rank/...", "//tensorflow/...", - "//tensorflow_estimator/...", + "//tensorflow_estimator/contrib/...", "//tensorflow_fold/llgtm/...", "//tensorflow_text/...", "//third_party/py/tensor2tensor/...", @@ -554,18 +565,24 @@ genrule( }), outs = ["__init__.py"], cmd = select({ - "api_version_2": "cp $(@D)/_api/v2/__init__.py $(OUTS)", - "//conditions:default": "cp $(@D)/_api/v1/__init__.py $(OUTS)", + "api_version_2": "cp $(@D)/_api/v2/v2.py $(OUTS)", + "//conditions:default": "cp $(@D)/_api/v1/v1.py $(OUTS)", }), ) gen_api_init_files( name = "tf_python_api_gen_v1", - srcs = ["api_template_v1.__init__.py"], + srcs = [ + "api_template_v1.__init__.py", + "compat_template_v1.__init__.py", + ], api_version = 1, + compat_api_versions = [1], + compat_init_templates = ["compat_template_v1.__init__.py"], output_dir = "_api/v1/", - output_files = TENSORFLOW_API_INIT_FILES_V1, + output_files = TENSORFLOW_API_INIT_FILES_V1_WITH_COMPAT, output_package = "tensorflow._api.v1", + root_file_name = "v1.py", root_init_template = "api_template_v1.__init__.py", ) @@ -581,6 +598,7 @@ gen_api_init_files( output_dir = "_api/v2/", output_files = TENSORFLOW_API_INIT_FILES_V2, output_package = "tensorflow._api.v2", + root_file_name = "v2.py", root_init_template = "api_template.__init__.py", ) diff --git a/tensorflow/api_template.__init__.py b/tensorflow/api_template.__init__.py index 0d49756838..d81cf067eb 100644 --- a/tensorflow/api_template.__init__.py +++ b/tensorflow/api_template.__init__.py @@ -21,8 +21,6 @@ from __future__ import print_function as _print_function import os as _os # pylint: disable=g-bad-import-order -from tensorflow.python import pywrap_tensorflow # pylint: disable=unused-import - from tensorflow.python.tools import component_api_helper as _component_api_helper _component_api_helper.package_hook( parent_package_str=__name__, @@ -30,16 +28,16 @@ _component_api_helper.package_hook( # API IMPORTS PLACEHOLDER -from tensorflow.python.platform import flags # pylint: disable=g-import-not-at-top - # Make sure directory containing top level submodules is in # the __path__ so that "from tensorflow.foo import bar" works. -_tf_api_dir = _os.path.dirname(_os.path.dirname(app.__file__)) # pylint: disable=undefined-variable +# We're using bitwise, but there's nothing special about that. +_tf_api_dir = _os.path.dirname(_os.path.dirname(bitwise.__file__)) # pylint: disable=undefined-variable if _tf_api_dir not in __path__: __path__.append(_tf_api_dir) -# Calls to enable and disable features. -enable_eager_execution() # pylint: disable=undefined-variable +# Enable TF2 behaviors +from tensorflow.python.compat import compat as _compat # pylint: disable=g-import-not-at-top +_compat.enable_v2_behavior() # These symbols appear because we import the python package which # in turn imports from tensorflow.core and tensorflow.python. They diff --git a/tensorflow/c/BUILD b/tensorflow/c/BUILD index b8db1b2144..59c23e7c18 100644 --- a/tensorflow/c/BUILD +++ b/tensorflow/c/BUILD @@ -60,6 +60,7 @@ tf_cuda_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:op_gen_lib", + "//tensorflow/core/distributed_runtime:server_lib", ], }), ) @@ -120,7 +121,8 @@ tf_cuda_library( ":c_api", ":c_api_internal", "//tensorflow/c/eager:c_api", - "//tensorflow/compiler/jit/legacy_flags:mark_for_compilation_pass_flags", + "//tensorflow/c/eager:c_api_internal", + "//tensorflow/compiler/jit:flags", "//tensorflow/contrib/tpu:all_ops", "//tensorflow/core:core_cpu", "//tensorflow/core:framework", @@ -173,6 +175,30 @@ tf_cuda_library( ], ) +tf_cuda_library( + name = "kernels", + srcs = [ + "kernels.cc", + ], + hdrs = [ + "kernels.h", + ], + copts = tf_copts(), + visibility = ["//visibility:public"], + deps = select({ + "//tensorflow:android": [ + ":c_api", + ":c_api_internal", + "//tensorflow/core:android_tensorflow_lib_lite", + ], + "//conditions:default": [ + ":c_api", + ":c_api_internal", + "//tensorflow/core:framework", + ], + }), +) + # ----------------------------------------------------------------------------- # Tests @@ -208,7 +234,10 @@ tf_cuda_cc_test( "//tensorflow:darwin": ["-headerpad_max_install_names"], "//conditions:default": [], }), - tags = ["noasan"], + tags = [ + "no_oss", # http://b/119522529 + "noasan", + ], # We must ensure that the dependencies can be dynamically linked since # the shared library must be able to use core:framework. # linkstatic = tf_kernel_tests_linkstatic(), @@ -237,7 +266,7 @@ tf_cuda_cc_test( tf_cc_test( name = "c_api_experimental_test", - size = "small", + size = "medium", srcs = ["c_api_experimental_test.cc"], data = ["testdata/tf_record"], linkopts = select({ @@ -248,8 +277,11 @@ tf_cc_test( # the shared library must be able to use core:framework. # linkstatic = tf_kernel_tests_linkstatic(), deps = [ + ":c_api", ":c_api_experimental", ":c_test_util", + "//tensorflow/c/eager:c_api", + "//tensorflow/c/eager:c_api_test_util", "//tensorflow/core:lib", "//tensorflow/core:protos_all_cc", "//tensorflow/core:test", @@ -300,6 +332,30 @@ tf_kernel_library( alwayslink = 1, ) +tf_cuda_cc_test( + name = "kernels_test", + size = "small", + srcs = ["kernels_test.cc"], + linkopts = select({ + "//tensorflow:darwin": ["-headerpad_max_install_names"], + "//conditions:default": [], + }), + tags = ["noasan"], + # We must ensure that the dependencies can be dynamically linked since + # the shared library must be able to use core:framework. + # linkstatic = tf_kernel_tests_linkstatic(), + deps = [ + ":c_api", + ":kernels", + "//tensorflow/core:framework", + "//tensorflow/core:lib", + "//tensorflow/core:proto_text", + "//tensorflow/core:protos_all_cc", + "//tensorflow/core:test", + "//tensorflow/core:test_main", + ], +) + # ----------------------------------------------------------------------------- # Python API target diff --git a/tensorflow/c/c_api_experimental.cc b/tensorflow/c/c_api_experimental.cc index fabe2fa0f6..38e29aa74a 100644 --- a/tensorflow/c/c_api_experimental.cc +++ b/tensorflow/c/c_api_experimental.cc @@ -15,13 +15,18 @@ limitations under the License. #include "tensorflow/c/c_api_experimental.h" +#include "tensorflow/c/c_api.h" #include "tensorflow/c/c_api_internal.h" -#include "tensorflow/compiler/jit/legacy_flags/mark_for_compilation_pass_flags.h" +#include "tensorflow/c/eager/c_api.h" +#include "tensorflow/c/eager/c_api_internal.h" +#include "tensorflow/compiler/jit/flags.h" #include "tensorflow/core/common_runtime/eager/attr_builder.h" #include "tensorflow/core/framework/tensor.pb.h" #include "tensorflow/core/graph/graph.h" #include "tensorflow/core/graph/node_builder.h" #include "tensorflow/core/lib/strings/strcat.h" +#include "tensorflow/core/platform/init_main.h" +#include "tensorflow/core/platform/net.h" #include "tensorflow/core/platform/platform.h" #include "tensorflow/core/protobuf/config.pb.h" #include "tensorflow/core/protobuf/tensorflow_server.pb.h" @@ -51,8 +56,8 @@ void TF_EnableXLACompilation(TF_SessionOptions* options, unsigned char enable) { // These XLA flags are needed to trigger XLA properly from C (more generally // non-Python) clients. If this API is called again with `enable` set to // false, it is safe to keep these flag values as is. - tensorflow::legacy_flags::MarkForCompilationPassFlags* flags = - tensorflow::legacy_flags::GetMarkForCompilationPassFlags(); + tensorflow::MarkForCompilationPassFlags* flags = + tensorflow::GetMarkForCompilationPassFlags(); flags->tf_xla_cpu_global_jit = true; flags->tf_xla_min_cluster_size = 1; } else { @@ -71,8 +76,8 @@ TF_Buffer* TF_CreateConfig(unsigned char enable_xla_compilation, // These XLA flags are needed to trigger XLA properly from C (more generally // non-Python) clients. If this API is called again with `enable` set to // false, it is safe to keep these flag values as is. - tensorflow::legacy_flags::MarkForCompilationPassFlags* flags = - tensorflow::legacy_flags::GetMarkForCompilationPassFlags(); + tensorflow::MarkForCompilationPassFlags* flags = + tensorflow::GetMarkForCompilationPassFlags(); flags->tf_xla_cpu_global_jit = true; flags->tf_xla_min_cluster_size = 1; } else { @@ -6525,7 +6530,7 @@ library { } } node_def { - name: "ParallelInterleaveDataset/cycle_length" + name: "ExperimentalParallelInterleaveDataset/cycle_length" op: "Const" attr { key: "dtype" @@ -6546,7 +6551,7 @@ library { } } node_def { - name: "ParallelInterleaveDataset/block_length" + name: "ExperimentalParallelInterleaveDataset/block_length" op: "Const" attr { key: "dtype" @@ -6567,7 +6572,7 @@ library { } } node_def { - name: "ParallelInterleaveDataset/sloppy" + name: "ExperimentalParallelInterleaveDataset/sloppy" op: "Const" attr { key: "dtype" @@ -6588,7 +6593,7 @@ library { } } node_def { - name: "ParallelInterleaveDataset/buffer_output_elements" + name: "ExperimentalParallelInterleaveDataset/buffer_output_elements" op: "Const" attr { key: "dtype" @@ -6609,7 +6614,7 @@ library { } } node_def { - name: "ParallelInterleaveDataset/prefetch_input_elements" + name: "ExperimentalParallelInterleaveDataset/prefetch_input_elements" op: "Const" attr { key: "dtype" @@ -6630,14 +6635,14 @@ library { } } node_def { - name: "ParallelInterleaveDataset" - op: "ParallelInterleaveDataset" + name: "ExperimentalParallelInterleaveDataset" + op: "ExperimentalParallelInterleaveDataset" input: "RepeatDataset:handle:0" - input: "ParallelInterleaveDataset/cycle_length:output:0" - input: "ParallelInterleaveDataset/block_length:output:0" - input: "ParallelInterleaveDataset/sloppy:output:0" - input: "ParallelInterleaveDataset/buffer_output_elements:output:0" - input: "ParallelInterleaveDataset/prefetch_input_elements:output:0" + input: "ExperimentalParallelInterleaveDataset/cycle_length:output:0" + input: "ExperimentalParallelInterleaveDataset/block_length:output:0" + input: "ExperimentalParallelInterleaveDataset/sloppy:output:0" + input: "ExperimentalParallelInterleaveDataset/buffer_output_elements:output:0" + input: "ExperimentalParallelInterleaveDataset/prefetch_input_elements:output:0" attr { key: "Targuments" value { @@ -6737,7 +6742,7 @@ library { node_def { name: "ShuffleDataset_2" op: "ShuffleDataset" - input: "ParallelInterleaveDataset:handle:0" + input: "ExperimentalParallelInterleaveDataset:handle:0" input: "ShuffleDataset_2/buffer_size_1:output:0" input: "ShuffleDataset_2/seed_2:output:0" input: "ShuffleDataset_2/seed2_2:output:0" @@ -8739,14 +8744,65 @@ void TFE_TensorHandlePrintDebugString(TFE_TensorHandle* handle) { TF_DeleteStatus(status); } -TF_CAPI_EXPORT extern void TF_MakeInternalErrorStatus(TF_Status* status, - const char* errMsg) { +struct TFE_ExecuteOpNotification { + TFE_ExecuteOpNotification() : status(TF_NewStatus(), TF_DeleteStatus) {} + tensorflow::Notification n; + std::unique_ptr thread; + std::unique_ptr status; +}; + +TFE_ExecuteOpNotification* TFE_ExecuteOpInNewThread(TFE_Op* op, + TFE_TensorHandle** retvals, + int* num_retvals, + TF_Status* status) { + TFE_ExecuteOpNotification* n = new TFE_ExecuteOpNotification; + + n->thread.reset(op->operation.EagerContext()->TFEnv()->StartThread( + tensorflow::ThreadOptions(), "ExecuteOpThread", + [op, retvals, num_retvals, n]() { + TFE_Execute(op, retvals, num_retvals, n->status.get()); + n->n.Notify(); + })); + + return n; +} + +void TFE_ExecuteOpNotificationWaitAndDelete( + TFE_ExecuteOpNotification* notification, TF_Status* status) { + if (notification == nullptr) { + status->status = tensorflow::errors::InvalidArgument( + "Passed in notification is a nullptr."); + + return; + } + if (notification->thread == nullptr) { + status->status = tensorflow::errors::InvalidArgument( + "Passed in notification didn't start a thread correctly. Cleaning up " + "this notification. Please re-execute the operation to get a new " + "notification."); + + delete notification; + return; + } + + notification->n.WaitForNotification(); + + status->status = notification->status->status; + + delete notification; +} + +void TF_MakeInternalErrorStatus(TF_Status* status, const char* errMsg) { status->status = tensorflow::errors::Internal(errMsg); } // This builder is used in the eager API to build a NodeDef. struct TF_AttrBuilder : public tensorflow::AttrBuilder { using tensorflow::AttrBuilder::AttrBuilder; + // The string buffers to make sure that any `attr_name` we pass into + // `builder->Set()` will outlive the subsequent + // `TF_AttrBuilderCheckCanRunOnDevice()` call(s) on the same `builder`. + std::set attr_names; }; TF_AttrBuilder* TF_NewAttrBuilder(const char* op_name) { @@ -8757,13 +8813,15 @@ void TF_DeleteAttrBuilder(TF_AttrBuilder* builder) { delete builder; } void TF_AttrBuilderSetType(TF_AttrBuilder* builder, const char* attr_name, TF_DataType value) { - builder->Set(attr_name, static_cast(value)); + auto iter = builder->attr_names.insert(attr_name).first; + builder->Set((*iter).c_str(), static_cast(value)); } void TF_AttrBuilderSetTypeList(TF_AttrBuilder* builder, const char* attr_name, const TF_DataType* values, int num_values) { + auto iter = builder->attr_names.insert(attr_name).first; builder->Set( - attr_name, + (*iter).c_str(), tensorflow::gtl::ArraySlice( reinterpret_cast(values), num_values)); } @@ -8800,3 +8858,31 @@ const char* TF_GetNumberAttrForOpListInput(const char* op_name, int input_index, // The returned string is owned by OpRegistry, so liveness is not a concern. return input_arg.number_attr().c_str(); } + +int TF_OpIsStateful(const char* op_type, TF_Status* status) { + const tensorflow::OpRegistrationData* op_reg_data; + status->status = + tensorflow::OpRegistry::Global()->LookUp(op_type, &op_reg_data); + if (!status->status.ok()) { + return 0; + } + return op_reg_data->op_def.is_stateful(); +} + +void TF_InitMain(const char* usage, int* argc, char*** argv) { + tensorflow::port::InitMain(usage, argc, argv); +} + +int TF_PickUnusedPortOrDie() { + return tensorflow::internal::PickUnusedPortOrDie(); +} + +TFE_TensorHandle* TFE_NewTensorHandleFromScalar(TF_DataType dtype_arg, + void* data, size_t len) { + auto dtype = static_cast(dtype_arg); + DCHECK(tensorflow::DataTypeCanUseMemcpy(dtype)); + + tensorflow::Tensor tensor(dtype, tensorflow::TensorShape({})); + std::memcpy(tensorflow::TensorCApi::Buffer(tensor)->data(), data, len); + return new TFE_TensorHandle(tensor, nullptr, nullptr); +} diff --git a/tensorflow/c/c_api_experimental.h b/tensorflow/c/c_api_experimental.h index 6639b0be72..80c8bfe594 100644 --- a/tensorflow/c/c_api_experimental.h +++ b/tensorflow/c/c_api_experimental.h @@ -180,6 +180,25 @@ TF_CAPI_EXPORT extern TFE_TensorHandle* TFE_DequeueVariantTensor( TF_CAPI_EXPORT extern void TFE_TensorHandlePrintDebugString( TFE_TensorHandle* handle); +typedef struct TFE_ExecuteOpNotification TFE_ExecuteOpNotification; + +// Allows invoking a kernel asynchronously, and explicitly returns a +// notification that can be waited upon. This always executes the kernel in a +// new thread. +// 1. `retvals` and `num_retvals` can only be consumed after +// `TFE_ExecuteOp` returns successfully. They shouldn't be used +// if the return is unsuccessful +// 2. These new APIs cannot be used together with the TFE context level async +// support. +TF_CAPI_EXPORT extern TFE_ExecuteOpNotification* TFE_ExecuteOpInNewThread( + TFE_Op* op, TFE_TensorHandle** retvals, int* num_retvals, + TF_Status* status); + +// Waits to complete the op execution, and cleans up the notification. +// Errors reported by op execution are set in `status`. +TF_CAPI_EXPORT extern void TFE_ExecuteOpNotificationWaitAndDelete( + TFE_ExecuteOpNotification* notification, TF_Status* status); + TF_CAPI_EXPORT extern void TF_MakeInternalErrorStatus(TF_Status* status, const char* errMsg); @@ -209,6 +228,24 @@ TF_CAPI_EXPORT extern void TF_AttrBuilderCheckCanRunOnDevice( TF_CAPI_EXPORT extern const char* TF_GetNumberAttrForOpListInput( const char* op_name, int input_index, TF_Status* status); +// Returns 1 if the op is stateful, 0 otherwise. The return value is undefined +// if the status is not ok. +TF_CAPI_EXPORT extern int TF_OpIsStateful(const char* op_type, + TF_Status* status); + +// Platform specific initialization routine. Very few platforms actually require +// this to be called. +TF_CAPI_EXPORT void TF_InitMain(const char* usage, int* argc, char*** argv); + +// Platform-specific implementation to return an unused port. (This should used +// in tests only.) +TF_CAPI_EXPORT int TF_PickUnusedPortOrDie(); + +// Fast path method that makes constructing a single scalar tensor require less +// overhead and copies. +TF_CAPI_EXPORT extern TFE_TensorHandle* TFE_NewTensorHandleFromScalar( + TF_DataType dtype, void* scalar, size_t len); + #ifdef __cplusplus } /* end extern "C" */ #endif diff --git a/tensorflow/c/c_api_experimental_test.cc b/tensorflow/c/c_api_experimental_test.cc index c6effd3969..daa7701b7f 100644 --- a/tensorflow/c/c_api_experimental_test.cc +++ b/tensorflow/c/c_api_experimental_test.cc @@ -15,6 +15,8 @@ limitations under the License. #include "tensorflow/c/c_api_experimental.h" #include "tensorflow/c/c_test_util.h" +#include "tensorflow/c/eager/c_api.h" +#include "tensorflow/c/eager/c_api_test_util.h" #include "tensorflow/core/lib/io/path.h" #include "tensorflow/core/platform/env.h" #include "tensorflow/core/platform/logging.h" @@ -162,5 +164,137 @@ protocol: "grpc" TF_DeleteStatus(status); } +TEST(CAPI_EXPERIMENTAL, IsStateful) { + std::unique_ptr status( + TF_NewStatus(), TF_DeleteStatus); + int assign = TF_OpIsStateful("AssignAddVariableOp", status.get()); + ASSERT_EQ(TF_OK, TF_GetCode(status.get())) << TF_Message(status.get()); + EXPECT_EQ(assign, 1); + int id = TF_OpIsStateful("Identity", status.get()); + ASSERT_EQ(TF_OK, TF_GetCode(status.get())) << TF_Message(status.get()); + EXPECT_EQ(id, 0); +} + +TEST(CAPI_EXPERIMENTAL, TFE_ExecuteOpInNewThreadTest_Simple) { + TF_Status* status = TF_NewStatus(); + TFE_ContextOptions* opts = TFE_NewContextOptions(); + TFE_Context* ctx = TFE_NewContext(opts, status); + CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status); + TFE_DeleteContextOptions(opts); + + TFE_TensorHandle* m = TestMatrixTensorHandle(); + + TFE_Op* matmul_op = MatMulOp(ctx, m, m); + + TFE_TensorHandle* retvals[1] = {nullptr}; + int num_retvals = 1; + + auto* r = + TFE_ExecuteOpInNewThread(matmul_op, &retvals[0], &num_retvals, status); + + TFE_ExecuteOpNotificationWaitAndDelete(r, status); + CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status); + + TF_Tensor* t = TFE_TensorHandleResolve(retvals[0], status); + ASSERT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status); + float product[4] = {0}; + EXPECT_EQ(sizeof(product), TF_TensorByteSize(t)); + memcpy(&product[0], TF_TensorData(t), TF_TensorByteSize(t)); + TF_DeleteTensor(t); + EXPECT_EQ(7, product[0]); + EXPECT_EQ(10, product[1]); + EXPECT_EQ(15, product[2]); + EXPECT_EQ(22, product[3]); + + TFE_DeleteOp(matmul_op); + TFE_DeleteTensorHandle(m); + + TFE_DeleteTensorHandle(retvals[0]); + TFE_DeleteContext(ctx); + TF_DeleteStatus(status); +} + +// Perform a send/recv test. Recv blocks, so they need to be executed +// asynchronously. +TEST(CAPI_EXPERIMENTAL, TFE_ExecuteOpInNewThreadTest_Blocking) { + TF_Status* status = TF_NewStatus(); + TFE_ContextOptions* opts = TFE_NewContextOptions(); + CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status); + TFE_Context* ctx = TFE_NewContext(opts, status); + CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status); + TFE_DeleteContextOptions(opts); + + // Returns a 2x2 float32 Tensor on the CPU, with data 1., 2., 3., 4. + TFE_TensorHandle* m = TestMatrixTensorHandle(); + + // Build a send op. + TFE_Op* send_op = TFE_NewOp(ctx, "_Send", status); + CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status); + TFE_OpAddInput(send_op, m, status); + CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status); + + string tensor_name = "Tensor"; + TFE_OpSetAttrType(send_op, "T", TF_FLOAT); + TFE_OpSetAttrString(send_op, "tensor_name", tensor_name.c_str(), + tensor_name.size()); + string send_device = "/job:localhost/replica:0/task:0/device:CPU:0"; + TFE_OpSetAttrString(send_op, "send_device", send_device.c_str(), + send_device.size()); + TFE_OpSetAttrInt(send_op, "send_device_incarnation", 1234); + string recv_device = "/job:localhost/replica:0/task:0/device:CPU:0"; + TFE_OpSetAttrString(send_op, "recv_device", recv_device.c_str(), + recv_device.size()); + TFE_OpSetAttrBool(send_op, "client_terminated", true); + + // Build a recv op. + TFE_Op* recv_op = TFE_NewOp(ctx, "_Recv", status); + CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status); + + TFE_OpSetAttrType(recv_op, "tensor_type", TF_FLOAT); + TFE_OpSetAttrString(recv_op, "tensor_name", tensor_name.c_str(), + tensor_name.size()); + TFE_OpSetAttrString(recv_op, "send_device", send_device.c_str(), + send_device.size()); + TFE_OpSetAttrInt(recv_op, "send_device_incarnation", 1234); + TFE_OpSetAttrString(recv_op, "recv_device", recv_device.c_str(), + recv_device.size()); + TFE_OpSetAttrBool(recv_op, "client_terminated", true); + + TFE_TensorHandle* send_retvals; + int send_num_retvals = 0; + auto* send_result = TFE_ExecuteOpInNewThread(send_op, &send_retvals, + &send_num_retvals, status); + + TFE_TensorHandle* recv_retvals[1] = {nullptr}; + int recv_num_retvals = 1; + auto* recv_result = TFE_ExecuteOpInNewThread(recv_op, &recv_retvals[0], + &recv_num_retvals, status); + + TFE_ExecuteOpNotificationWaitAndDelete(send_result, status); + CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status); + TFE_ExecuteOpNotificationWaitAndDelete(recv_result, status); + CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status); + + TF_Tensor* t = TFE_TensorHandleResolve(recv_retvals[0], status); + ASSERT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status); + + float product[4] = {0}; + EXPECT_EQ(sizeof(product), TF_TensorByteSize(t)); + memcpy(&product[0], TF_TensorData(t), TF_TensorByteSize(t)); + TF_DeleteTensor(t); + EXPECT_EQ(1, product[0]); + EXPECT_EQ(2, product[1]); + EXPECT_EQ(3, product[2]); + EXPECT_EQ(4, product[3]); + + TFE_DeleteOp(send_op); + TFE_DeleteOp(recv_op); + TFE_DeleteTensorHandle(m); + + TFE_DeleteTensorHandle(recv_retvals[0]); + TFE_DeleteContext(ctx); + TF_DeleteStatus(status); +} + } // namespace } // namespace tensorflow diff --git a/tensorflow/c/c_api_function.cc b/tensorflow/c/c_api_function.cc index f68f8a3e90..28b9f8df9c 100644 --- a/tensorflow/c/c_api_function.cc +++ b/tensorflow/c/c_api_function.cc @@ -392,26 +392,26 @@ Status ProcessInputs( EXCLUSIVE_LOCKS_REQUIRED(fn_body->mu) { input_tensors->reserve(ninputs); for (int i = 0; i < ninputs; ++i) { - const Node& node = inputs[i].oper->node; + Node* node = &inputs[i].oper->node; int idx = inputs[i].index; TF_RETURN_WITH_CONTEXT_IF_ERROR( - fn_body->graph.IsValidOutputTensor(&node, idx), + fn_body->graph.IsValidOutputTensor(node, idx), "Encountered while processing input ", i, " into function '", fn_name, "'"); - TF_RETURN_WITH_CONTEXT_IF_ERROR(ValidateNonRefOutput(&node, idx), + TF_RETURN_WITH_CONTEXT_IF_ERROR(ValidateNonRefOutput(node, idx), "Encountered while processing input ", i, " into function '", fn_name, "'"); - input_tensors->emplace_back(&node, idx); + input_tensors->emplace_back(node, idx); - const auto& iter = input_nodes->find(&node); + const auto& iter = input_nodes->find(node); if (iter == input_nodes->end()) { - input_nodes->insert({&node, {idx}}); + input_nodes->insert({node, {idx}}); } else { auto& indices = iter->second; if (std::find(indices.begin(), indices.end(), idx) != indices.end()) { - return InvalidArgument("TF_Output ", node.name(), ":", idx, + return InvalidArgument("TF_Output ", node->name(), ":", idx, " appears more than once in the input list"); } indices.push_back(idx); @@ -428,16 +428,16 @@ Status ProcessOutputs(const TF_Graph* fn_body, const char* fn_name, EXCLUSIVE_LOCKS_REQUIRED(fn_body->mu) { output_tensors->reserve(noutputs); for (int i = 0; i < noutputs; ++i) { - const Node& node = outputs[i].oper->node; + Node* node = &outputs[i].oper->node; int idx = outputs[i].index; TF_RETURN_WITH_CONTEXT_IF_ERROR( - fn_body->graph.IsValidOutputTensor(&node, idx), + fn_body->graph.IsValidOutputTensor(node, idx), "Encountered while processing output ", i, " from function '", fn_name, "'"); - TF_RETURN_WITH_CONTEXT_IF_ERROR(ValidateNonRefOutput(&node, idx), + TF_RETURN_WITH_CONTEXT_IF_ERROR(ValidateNonRefOutput(node, idx), "Encountered while creating function '", fn_name, "'"); - output_tensors->emplace_back(&node, idx); + output_tensors->emplace_back(node, idx); } return Status::OK(); } diff --git a/tensorflow/c/eager/BUILD b/tensorflow/c/eager/BUILD index ba3d8533db..c34a84fcfe 100644 --- a/tensorflow/c/eager/BUILD +++ b/tensorflow/c/eager/BUILD @@ -50,6 +50,7 @@ tf_cuda_library( ], "//conditions:default": [], }) + [ + "@com_google_absl//absl/memory", "//tensorflow/core/common_runtime/eager:eager_operation", "//tensorflow/core/distributed_runtime/eager:eager_client", "//tensorflow/core/distributed_runtime/rpc/eager:grpc_eager_client", @@ -143,6 +144,7 @@ tf_cuda_cc_test( "//tensorflow/core:test", "//tensorflow/core:test_main", "//tensorflow/core/distributed_runtime/rpc:grpc_server_lib", + "@com_google_absl//absl/strings", ], ) diff --git a/tensorflow/c/eager/c_api.cc b/tensorflow/c/eager/c_api.cc index 408277468d..027d752f42 100755 --- a/tensorflow/c/eager/c_api.cc +++ b/tensorflow/c/eager/c_api.cc @@ -21,9 +21,11 @@ limitations under the License. #include #include +#include "absl/memory/memory.h" #include "tensorflow/c/c_api.h" #include "tensorflow/c/c_api_internal.h" #include "tensorflow/c/eager/c_api_internal.h" +#include "tensorflow/core/platform/host_info.h" #ifdef TENSORFLOW_EAGER_USE_XLA #include "tensorflow/compiler/tf2xla/xla_op_registry.h" #endif // TENSORFLOW_EAGER_USE_XLA @@ -79,7 +81,7 @@ tensorflow::Status GetAllRemoteDevices( const std::vector& remote_workers, tensorflow::WorkerCacheInterface* worker_cache, std::unique_ptr* device_mgr) { - std::vector remote_devices; + std::vector> remote_devices; tensorflow::Status status; // TODO(nareshmodi) do this in parallel instead of serially. for (const string& remote_worker : remote_workers) { @@ -92,7 +94,7 @@ tensorflow::Status GetAllRemoteDevices( status = s; if (s.ok()) { for (tensorflow::Device* d : *devices) { - remote_devices.push_back(d); + remote_devices.emplace_back(d); } } n.Notify(); @@ -100,7 +102,7 @@ tensorflow::Status GetAllRemoteDevices( n.WaitForNotification(); } std::unique_ptr remote_device_mgr( - new tensorflow::DeviceMgr(remote_devices)); + new tensorflow::DeviceMgr(std::move(remote_devices))); TF_RETURN_IF_ERROR(status); @@ -261,13 +263,13 @@ TF_CAPI_EXPORT extern void TFE_ContextSetAsyncForThread(TFE_Context* ctx, void TFE_DeleteContextOptions(TFE_ContextOptions* options) { delete options; } TFE_Context* TFE_NewContext(const TFE_ContextOptions* opts, TF_Status* status) { - std::vector devices; + std::vector> devices; status->status = tensorflow::DeviceFactory::AddDevices( opts->session_options.options, "/job:localhost/replica:0/task:0", &devices); if (!status->status.ok()) return nullptr; std::unique_ptr device_mgr( - new tensorflow::DeviceMgr(devices)); + new tensorflow::DeviceMgr(std::move(devices))); tensorflow::Rendezvous* r = new tensorflow::IntraProcessRendezvous(device_mgr.get()); @@ -409,6 +411,18 @@ const char* TFE_TensorHandleDeviceName(TFE_TensorHandle* h, TF_Status* status) { : d->name().c_str(); } +const char* TFE_TensorHandleBackingDeviceName(TFE_TensorHandle* h, + TF_Status* status) { + if (h == nullptr || h->handle == nullptr) { + status->status = tensorflow::errors::InvalidArgument( + "The passed in handle is a nullptr"); + return nullptr; + } + tensorflow::Device* d = h->handle->device(); + return (d == nullptr) ? "/job:localhost/replica:0/task:0/device:CPU:0" + : d->name().c_str(); +} + TF_CAPI_EXPORT extern TFE_TensorHandle* TFE_TensorHandleCopySharingTensor( TFE_TensorHandle* h, TF_Status* status) { if (h == nullptr || h->handle == nullptr) { @@ -458,13 +472,20 @@ TFE_Op* TFE_NewOp(TFE_Context* ctx, const char* op_or_function_name, TF_Status* status) { const char* name = op_or_function_name; // Shorthand const tensorflow::AttrTypeMap* types; - status->status = tensorflow::AttrTypeMapForOp(name, &types); - if (status->status.ok()) return new TFE_Op(ctx, name, types); - if (TF_GetCode(status) == TF_NOT_FOUND) { - if (ctx->context.FindFunctionByName(name)) { - status->status = tensorflow::Status::OK(); - return new TFE_Op(ctx, name, nullptr); + bool is_function = false; + status->status = tensorflow::AttrTypeMapForOp(name, &types, &is_function); + if (status->status.ok()) { + if (is_function && !ctx->context.FindFunctionByName(name)) { + status->status = tensorflow::errors::NotFound( + "'", name, + "' is neither a type of a primitive operation nor a name " + "of a function registered in binary running on ", + tensorflow::port::Hostname(), + ". Make sure the operation or function is " + "registered in the binary running in this process."); + return nullptr; } + return new TFE_Op(ctx, name, is_function, types); } return nullptr; } @@ -497,12 +518,6 @@ void TFE_OpAddInput(TFE_Op* op, TFE_TensorHandle* h, TF_Status* status) { TF_AttrType TFE_OpGetAttrType(TFE_Op* op, const char* attr_name, unsigned char* is_list, TF_Status* status) { TF_AttrType ret; - if (op->operation.is_function()) { - status->status = tensorflow::errors::Unimplemented( - "TODO(apassos): Support for attributes for TensorFlow functions is not " - "ready yet."); - return TF_ATTR_INT; // The compiler requires that we return something. - } status->status = tensorflow::AttrTypeByName(*op->operation.AttrTypes(), attr_name, &ret, is_list); return ret; diff --git a/tensorflow/c/eager/c_api.h b/tensorflow/c/eager/c_api.h index b2454d8722..8d6c8d958d 100755 --- a/tensorflow/c/eager/c_api.h +++ b/tensorflow/c/eager/c_api.h @@ -169,10 +169,33 @@ TF_CAPI_EXPORT extern int64_t TFE_TensorHandleNumElements(TFE_TensorHandle* h, TF_CAPI_EXPORT extern int64_t TFE_TensorHandleDim(TFE_TensorHandle* h, int dim_index, TF_Status* status); + +// Returns the device of the operation that produced `h`. +// If `h` was produced by a copy, returns the destination device of +// the copy. Note that returned device name is not always the device +// holding the tensor handle's memory. If you want the latter, use +// TFE_TensorHandleBackingDeviceName. +// This function will block till the operation that produces `h` has completed. +// +// Device on which the kernel of the operation that produced `h` ran. +// +// If `h` was produced by a copy, returns the destination device of +// the copy. +// +// Note that returned device name is not always the device that owns the memory +// that backs the tensor handle. For the latter see +// TFE_TensorHandleBackingDeviceName. +// // This function will block till the operation that produces `h` has completed. TF_CAPI_EXPORT extern const char* TFE_TensorHandleDeviceName( TFE_TensorHandle* h, TF_Status* status); +// Returns the name of the device in whose memory `h` resides. +// +// This function will block till the operation that produces `h` has completed. +TF_CAPI_EXPORT extern const char* TFE_TensorHandleBackingDeviceName( + TFE_TensorHandle* h, TF_Status* status); + // Return a pointer to a new TFE_TensorHandle that shares the underlying tensor // with `h`. On success, `status` is set to OK. On failure, `status` reflects // the error and a nullptr is returned. diff --git a/tensorflow/c/eager/c_api_internal.h b/tensorflow/c/eager/c_api_internal.h index fa1b22e3af..67bc1bcd24 100644 --- a/tensorflow/c/eager/c_api_internal.h +++ b/tensorflow/c/eager/c_api_internal.h @@ -93,10 +93,9 @@ struct TFE_TensorDebugInfo { }; struct TFE_Op { - // t is NULL iff the TFE_Op corresponds to a TensorFlow function instead of a - // primitive operation. - TFE_Op(TFE_Context* ctx, const char* op, const tensorflow::AttrTypeMap* t) - : operation(&ctx->context, op, t) {} + TFE_Op(TFE_Context* ctx, const char* op, bool is_function, + const tensorflow::AttrTypeMap* t) + : operation(&ctx->context, op, is_function, t) {} tensorflow::EagerOperation operation; }; diff --git a/tensorflow/c/eager/c_api_test.cc b/tensorflow/c/eager/c_api_test.cc index 55331022b9..6b39b79ee8 100644 --- a/tensorflow/c/eager/c_api_test.cc +++ b/tensorflow/c/eager/c_api_test.cc @@ -16,6 +16,7 @@ limitations under the License. #include "tensorflow/c/eager/c_api.h" #include +#include "absl/strings/match.h" #include "tensorflow/c/eager/c_api_test_util.h" #include "tensorflow/core/distributed_runtime/rpc/grpc_server_lib.h" #include "tensorflow/core/framework/function.pb.h" @@ -589,9 +590,22 @@ void TensorHandleCopyBetweenTwoGPUDevices(bool async) { TF_DeviceList* devices = TFE_ContextListDevices(ctx, status.get()); ASSERT_EQ(TF_OK, TF_GetCode(status.get())) << TF_Message(status.get()); const int num_devices = TF_DeviceListCount(devices); + bool has_gpu0 = false; + bool has_gpu1 = false; + for (int i = 0; i < num_devices; ++i) { + const char* dev = TF_DeviceListName(devices, i, status.get()); + ASSERT_EQ(TF_OK, TF_GetCode(status.get())) << TF_Message(status.get()); + string device_name(dev); + if (device_name.find("GPU:0") != string::npos) { + has_gpu0 = true; + } + if (device_name.find("GPU:1") != string::npos) { + has_gpu1 = true; + } + } const char* kCPUDevice = "CPU:0"; - if (num_devices < 3) { + if (!has_gpu0 || !has_gpu1) { TF_DeleteDeviceList(devices); TF_DeleteTensor(t); TFE_DeleteTensorHandle(hcpu); @@ -781,6 +795,14 @@ TEST(CAPI, TensorHandleNullptr) { TF_SetStatus(status.get(), TF_OK, ""); + device_name = TFE_TensorHandleBackingDeviceName(h, status.get()); + ASSERT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(status.get())); + ASSERT_EQ(device_name, nullptr); + ASSERT_EQ("The passed in handle is a nullptr", + string(TF_Message(status.get()))); + + TF_SetStatus(status.get(), TF_OK, ""); + int num_dims = TFE_TensorHandleNumDims(h, status.get()); ASSERT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(status.get())); ASSERT_EQ(num_dims, -1); @@ -796,6 +818,62 @@ TEST(CAPI, TensorHandleNullptr) { string(TF_Message(status.get()))); } +TEST(CAPI, TensorHandleDevices) { + std::unique_ptr status( + TF_NewStatus(), TF_DeleteStatus); + TFE_ContextOptions* opts = TFE_NewContextOptions(); + TFE_Context* ctx = TFE_NewContext(opts, status.get()); + TFE_DeleteContextOptions(opts); + ASSERT_EQ(TF_OK, TF_GetCode(status.get())) << TF_Message(status.get()); + + TFE_TensorHandle* hcpu = TestMatrixTensorHandle(); + const char* device_name = TFE_TensorHandleDeviceName(hcpu, status.get()); + ASSERT_EQ(TF_OK, TF_GetCode(status.get())) << TF_Message(status.get()); + ASSERT_TRUE(absl::StrContains(device_name, "CPU:0")) << device_name; + const char* backing_device_name = + TFE_TensorHandleBackingDeviceName(hcpu, status.get()); + ASSERT_EQ(TF_OK, TF_GetCode(status.get())) << TF_Message(status.get()); + ASSERT_TRUE(absl::StrContains(backing_device_name, "CPU:0")) + << backing_device_name; + + // Disable the test if no GPU is present. + string gpu_device_name; + if (GetDeviceName(ctx, &gpu_device_name, "GPU")) { + TFE_TensorHandle* hgpu = TFE_TensorHandleCopyToDevice( + hcpu, ctx, gpu_device_name.c_str(), status.get()); + ASSERT_TRUE(TF_GetCode(status.get()) == TF_OK) << TF_Message(status.get()); + + TFE_Op* shape_op = ShapeOp(ctx, hgpu); + TFE_OpSetDevice(shape_op, gpu_device_name.c_str(), status.get()); + ASSERT_TRUE(TF_GetCode(status.get()) == TF_OK) << TF_Message(status.get()); + TFE_TensorHandle* retvals[1]; + int num_retvals = 1; + TFE_Execute(shape_op, &retvals[0], &num_retvals, status.get()); + ASSERT_TRUE(TF_GetCode(status.get()) == TF_OK) << TF_Message(status.get()); + + // .device of shape is GPU since the op is executed on GPU + device_name = TFE_TensorHandleDeviceName(retvals[0], status.get()); + ASSERT_EQ(TF_OK, TF_GetCode(status.get())) << TF_Message(status.get()); + ASSERT_TRUE(absl::StrContains(device_name, "GPU:0")) << device_name; + + // .backing_device of shape is CPU since the tensor is backed by CPU + backing_device_name = + TFE_TensorHandleBackingDeviceName(retvals[0], status.get()); + ASSERT_EQ(TF_OK, TF_GetCode(status.get())) << TF_Message(status.get()); + ASSERT_TRUE(absl::StrContains(backing_device_name, "CPU:0")) + << backing_device_name; + + TFE_DeleteOp(shape_op); + TFE_DeleteTensorHandle(retvals[0]); + TFE_DeleteTensorHandle(hgpu); + } + + TFE_DeleteTensorHandle(hcpu); + TFE_ContextAsyncWait(ctx, status.get()); + EXPECT_EQ(TF_OK, TF_GetCode(status.get())) << TF_Message(status.get()); + TFE_DeleteContext(ctx); +} + void Execute_MatMul_CPU(bool async) { TF_Status* status = TF_NewStatus(); TFE_ContextOptions* opts = TFE_NewContextOptions(); diff --git a/tensorflow/c/eager/c_api_test_util.cc b/tensorflow/c/eager/c_api_test_util.cc index 008f088c2d..bd38127d50 100644 --- a/tensorflow/c/eager/c_api_test_util.cc +++ b/tensorflow/c/eager/c_api_test_util.cc @@ -104,6 +104,19 @@ TFE_Op* MatMulOp(TFE_Context* ctx, TFE_TensorHandle* a, TFE_TensorHandle* b) { return op; } +TFE_Op* ShapeOp(TFE_Context* ctx, TFE_TensorHandle* a) { + TF_Status* status = TF_NewStatus(); + + TFE_Op* op = TFE_NewOp(ctx, "Shape", status); + CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status); + TFE_OpAddInput(op, a, status); + CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status); + TF_DeleteStatus(status); + TFE_OpSetAttrType(op, "T", TFE_TensorHandleDataType(a)); + + return op; +} + TFE_TensorHandle* TestAxisTensorHandle() { int64_t dims[] = {1}; int data[] = {1}; diff --git a/tensorflow/c/eager/c_api_test_util.h b/tensorflow/c/eager/c_api_test_util.h index 474cae67c8..75ef9459e9 100644 --- a/tensorflow/c/eager/c_api_test_util.h +++ b/tensorflow/c/eager/c_api_test_util.h @@ -37,6 +37,9 @@ TFE_TensorHandle* TestMatrixTensorHandle3X2(); // Return a matmul op multiplying `a` by `b`. TFE_Op* MatMulOp(TFE_Context* ctx, TFE_TensorHandle* a, TFE_TensorHandle* b); +// Return a shape op fetching the shape of `a`. +TFE_Op* ShapeOp(TFE_Context* ctx, TFE_TensorHandle* a); + // Return an 1-D INT32 tensor containing a single value 1. TFE_TensorHandle* TestAxisTensorHandle(); diff --git a/tensorflow/c/eager/tape.h b/tensorflow/c/eager/tape.h index 5ba55a203f..5c11f51e87 100644 --- a/tensorflow/c/eager/tape.h +++ b/tensorflow/c/eager/tape.h @@ -141,8 +141,9 @@ class GradientTape { // null. The result is populated with one tensor per target element. Status ComputeGradient( const VSpace& vspace, - gtl::ArraySlice target_tensor_ids, - gtl::ArraySlice source_tensor_id, + const gtl::ArraySlice target_tensor_ids, + const gtl::ArraySlice source_tensor_ids, + const gtl::FlatMap sources_that_are_targets, gtl::ArraySlice output_gradients, std::vector* result); @@ -396,6 +397,7 @@ template Status InitialGradients( const VSpace& vspace, gtl::ArraySlice target_tensor_ids, + gtl::FlatMap sources_that_are_targets, gtl::ArraySlice output_gradients, const TensorTape& tensor_tape, const OpTape& op_tape, gtl::FlatMap>* result) { @@ -425,8 +427,13 @@ Status InitialGradients( "none of operations outputs match expected tensor"); } } else { - // No record of the target tensor found on the tape, so no gradient - // needs to be computed from it. Do nothing. + // This target tensor was not generated by any operation recorded on + // the tape, so no gradient needs to be computed from it unless this + // target is also a source. + auto source_tensor = sources_that_are_targets.find(id); + if (source_tensor != sources_that_are_targets.end()) { + (*result)[id].push_back(vspace.Ones(source_tensor->second)); + } } } else { (*result)[id].push_back(output_gradients[i]); @@ -467,8 +474,9 @@ constexpr int kMinAggregateBytes = 128 * 1024 * 1024; template Status GradientTape::ComputeGradient( const VSpace& vspace, - gtl::ArraySlice target_tensor_ids, - gtl::ArraySlice source_tensor_ids, + const gtl::ArraySlice target_tensor_ids, + const gtl::ArraySlice source_tensor_ids, + const gtl::FlatMap sources_that_are_targets, gtl::ArraySlice output_gradients, std::vector* result) { gtl::FlatSet sources_set(source_tensor_ids.begin(), @@ -478,7 +486,8 @@ Status GradientTape::ComputeGradient( std::vector op_stack = InitialStack(state.op_tape, state.op_missing_tensor); gtl::FlatMap> gradients; - Status s = InitialGradients(vspace, target_tensor_ids, output_gradients, + Status s = InitialGradients(vspace, target_tensor_ids, + sources_that_are_targets, output_gradients, tensor_tape_, state.op_tape, &gradients); auto cleanup = [this, &state]() { if (!persistent_) { diff --git a/tensorflow/c/kernels.cc b/tensorflow/c/kernels.cc new file mode 100644 index 0000000000..3caa5bcb03 --- /dev/null +++ b/tensorflow/c/kernels.cc @@ -0,0 +1,143 @@ +/* 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 + +#include "tensorflow/c/c_api_internal.h" +#include "tensorflow/c/kernels.h" +#include "tensorflow/core/framework/kernel_def_builder.h" +#include "tensorflow/core/framework/op_kernel.h" + +// This file forms the basis of a stable ABI for third-party kernel +// implementations. It is crucial that changes to this file are made cautiously +// and with a focus on maintaining both source and binary compatibility. + +struct TF_KernelBuilder { + ::tensorflow::KernelDefBuilder* cc_builder; + + void* (*create_function)(TF_OpKernelConstruction*); + void (*compute_function)(void*, TF_OpKernelContext*); + void (*delete_function)(void*); +}; + +TF_KernelBuilder* TF_NewKernelBuilder( + const char* op_name, const char* device_name, + void* (*create_func)(TF_OpKernelConstruction*), + void (*compute_func)(void*, TF_OpKernelContext*), + void (*delete_func)(void*)) { + TF_KernelBuilder* result = new TF_KernelBuilder; + result->cc_builder = new ::tensorflow::KernelDefBuilder(op_name); + result->cc_builder->Device(device_name); + result->create_function = create_func; + result->compute_function = compute_func; + result->delete_function = delete_func; + return result; +} + +void TF_DeleteKernelBuilder(TF_KernelBuilder* builder) { + DCHECK_NE(builder, nullptr); + delete builder->cc_builder; + delete builder; +} + +namespace tensorflow { +namespace { + +// An OpKernel whose methods delegate to C function pointers. +class COpKernel : public OpKernel { + public: + explicit COpKernel(OpKernelConstruction* ctx, + void* (*create_func)(TF_OpKernelConstruction*), + void (*compute_func)(void*, TF_OpKernelContext*), + void (*delete_func)(void*)) + : OpKernel(ctx), compute_func_(compute_func), delete_func_(delete_func) { + if (create_func != nullptr) { + c_kernel_ = + (*create_func)(reinterpret_cast(ctx)); + } else { + c_kernel_ = nullptr; + } + } + + void Compute(OpKernelContext* ctx) override { + (*compute_func_)(c_kernel_, reinterpret_cast(ctx)); + } + + ~COpKernel() override { + if (delete_func_ != nullptr) { + (*delete_func_)(c_kernel_); + } + } + + private: + void (*compute_func_)(void*, TF_OpKernelContext* context); + void (*delete_func_)(void*); + void* c_kernel_; +}; + +// A KernelFactory that returns COpKernel instances. +class KernelBuilderFactory + : public ::tensorflow::kernel_factory::OpKernelFactory { + public: + explicit KernelBuilderFactory(TF_KernelBuilder* builder) + : builder_(builder) {} + ::tensorflow::OpKernel* Create( + ::tensorflow::OpKernelConstruction* context) override { + return new ::tensorflow::COpKernel(context, builder_->create_function, + builder_->compute_function, + builder_->delete_function); + } + ~KernelBuilderFactory() override { TF_DeleteKernelBuilder(builder_); } + + private: + TF_KernelBuilder* builder_; +}; +} // namespace +} // namespace tensorflow + +void TF_RegisterKernelBuilder(const char* name, TF_KernelBuilder* builder, + TF_Status* status) { + using tensorflow::register_kernel::Name; + + tensorflow::kernel_factory::OpKernelRegistrar( + builder->cc_builder->Build(), name, + absl::make_unique(builder)); + + TF_SetStatus(status, TF_OK, ""); +} + +int TF_NumInputs(TF_OpKernelContext* ctx) { + auto* cc_ctx = reinterpret_cast<::tensorflow::OpKernelContext*>(ctx); + return cc_ctx->num_inputs(); +} + +int TF_NumOutputs(TF_OpKernelContext* ctx) { + auto* cc_ctx = reinterpret_cast<::tensorflow::OpKernelContext*>(ctx); + return cc_ctx->num_outputs(); +} + +void TF_GetInput(TF_OpKernelContext* ctx, int i, TF_Tensor** tensor, + TF_Status* status) { + auto* cc_ctx = reinterpret_cast<::tensorflow::OpKernelContext*>(ctx); + if (i < 0 || i >= cc_ctx->num_inputs()) { + TF_SetStatus(status, TF_OUT_OF_RANGE, "input index out of range"); + return; + } + const ::tensorflow::Tensor& cc_tensor(cc_ctx->input(i)); + TF_Tensor* result = ::tensorflow::TF_TensorFromTensor(cc_tensor, status); + if (TF_GetCode(status) == TF_OK) { + *tensor = result; + } +} diff --git a/tensorflow/c/kernels.h b/tensorflow/c/kernels.h new file mode 100644 index 0000000000..d7778829bc --- /dev/null +++ b/tensorflow/c/kernels.h @@ -0,0 +1,110 @@ +/* Copyright 2018 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_C_KERNELS_H_ +#define TENSORFLOW_C_KERNELS_H_ + +#include "tensorflow/c/c_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// -------------------------------------------------------------------------- +// C API for TensorFlow Kernels. +// +// This API allows developers to register custom kernel implementations for +// TensorFlow. +// +// See c_api.h header comments for a discussion about API conventions. +// +// Users wishing to extend TensorFlow with new kernels will call +// `TF_NewKernelBuilder`. The resulting kernel builder can be registered with +// `TF_RegisterKernelBuilder`, which will allow TF to construct user-provided +// kernels when necessary. + +struct TF_KernelBuilder; +struct TF_OpKernelConstruction; +struct TF_OpKernelContext; + +// Allocates a new kernel builder and returns a pointer to it. +// +// If non-null, TensorFlow will call create_func when it needs to instantiate +// the kernel. The pointer returned by create_func will be passed to +// compute_func and delete_func, thereby functioning as a "this" pointer for +// referring to kernel instances. +// +// The TF_OpKernelConstruction pointer passed to create_func is owned by +// TensorFlow and will be deleted once create_func returns. It must not be used +// after this. +// +// When TensorFlow needs to perform a computation with this kernel, it will +// call compute_func. This function will receive the pointer returned by +// create_func (or null if no create_func was provided), along with the inputs +// to the computation. +// +// The TF_OpKernelContext pointer received by compute_func is owned by +// TensorFlow and will be deleted once compute_func returns. It must not be used +// after this. +// +// Finally, when TensorFlow no longer needs the kernel, it will call +// delete_func if one is provided. This function will receive the pointer +// returned in `create_func` or nullptr if no `create_func` was provided. +// +// The caller should pass the result of this function to +// TF_RegisterKernelBuilder, which will take ownership of the pointer. If, for +// some reason, the kernel builder will not be registered, the caller should +// delete it with TF_DeleteKernelBuilder. +TF_CAPI_EXPORT extern TF_KernelBuilder* TF_NewKernelBuilder( + const char* op_name, const char* device_name, + void* (*create_func)(TF_OpKernelConstruction*), + void (*compute_func)(void*, TF_OpKernelContext*), + void (*delete_func)(void*)); + +// Register the given kernel builder with the TensorFlow runtime. If +// registration fails, the given status will be populated. +// +// This call takes ownership of the `builder` pointer. +TF_CAPI_EXPORT extern void TF_RegisterKernelBuilder(const char* kernel_name, + TF_KernelBuilder* builder, + TF_Status* status); + +// Deletes the given TF_KernelBuilder. This should be called only if the kernel +// builder is not registered with TensorFlow via TF_RegisterKernelBuilder. +TF_CAPI_EXPORT extern void TF_DeleteKernelBuilder(TF_KernelBuilder* builder); + +// -------------------------------------------------------------------------- +// OpKernelContext routines + +// TF_NumInputs returns the number of inputs available in ctx. +TF_CAPI_EXPORT extern int TF_NumInputs(TF_OpKernelContext* ctx); + +// TF_NumOutputs returns the number of outputs to be placed in *ctx by the +// kernel. +TF_CAPI_EXPORT extern int TF_NumOutputs(TF_OpKernelContext* ctx); + +// Retrieves the ith input from ctx. If TF_GetCode(status) is TF_OK, *tensor is +// populated and its ownership is passed to the caller. In any other case, +// *tensor is not modified. +// +// If i < 0 or i >= TF_NumInputs(ctx), *status is set to TF_OUT_OF_RANGE. +TF_CAPI_EXPORT extern void TF_GetInput(TF_OpKernelContext* ctx, int i, + TF_Tensor** tensor, TF_Status* status); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif // TENSORFLOW_C_KERNELS_H_ diff --git a/tensorflow/c/kernels_test.cc b/tensorflow/c/kernels_test.cc new file mode 100644 index 0000000000..80bf12c096 --- /dev/null +++ b/tensorflow/c/kernels_test.cc @@ -0,0 +1,194 @@ +/* Copyright 2018 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/c/kernels.h" + +#include "tensorflow/c/c_api.h" +#include "tensorflow/core/framework/kernel_def.pb.h" +#include "tensorflow/core/framework/node_def.pb_text.h" +#include "tensorflow/core/framework/op.h" +#include "tensorflow/core/framework/op_kernel.h" +#include "tensorflow/core/framework/types.h" +#include "tensorflow/core/lib/core/status_test_util.h" +#include "tensorflow/core/platform/test.h" + +struct MyCustomKernel { + bool created; + bool compute_called; +}; + +static bool delete_called = false; + +static void* MyCreateFunc(TF_OpKernelConstruction* ctx) { + struct MyCustomKernel* s = new struct MyCustomKernel; + s->created = true; + s->compute_called = false; + return s; +} + +static void MyComputeFunc(void* kernel, TF_OpKernelContext* ctx) { + struct MyCustomKernel* s = static_cast(kernel); + s->compute_called = true; +} + +static void MyDeleteFunc(void* kernel) { + struct MyCustomKernel* s = static_cast(kernel); + EXPECT_TRUE(s->created); + EXPECT_TRUE(s->compute_called); + delete_called = true; + delete s; +} + +namespace tensorflow { + +static std::unique_ptr GetFakeKernel(const char* device_name, + const char* op_name, + Status* status) { + NodeDef def; + def.set_op(op_name); + def.set_device(device_name); + def.add_input("input1"); + def.add_input("input2"); + return CreateOpKernel(DeviceType(device_name), nullptr, nullptr, def, 1, + status); +} + +// Tests registration of a single C kernel and checks that calls through the +// C/C++ boundary are being made. +TEST(TestKernel, TestRegisterKernelBuilder) { + const char* kernel_name = "SomeKernelName"; + const char* op_name = "FooOp"; + const char* device_name = "FakeDeviceName1"; + + REGISTER_OP(op_name) + .Input("input1: double") + .Input("input2: uint8") + .Output("output1: uint8"); + + TF_KernelBuilder* builder = TF_NewKernelBuilder( + op_name, device_name, &MyCreateFunc, &MyComputeFunc, &MyDeleteFunc); + + { + TF_Status* status = TF_NewStatus(); + TF_RegisterKernelBuilder(kernel_name, builder, status); + EXPECT_EQ(TF_OK, TF_GetCode(status)); + TF_Buffer* buf = TF_GetRegisteredKernelsForOp(op_name, status); + EXPECT_EQ(TF_OK, TF_GetCode(status)); + KernelList list; + list.ParseFromArray(buf->data, buf->length); + ASSERT_EQ(1, list.kernel_size()); + ASSERT_EQ(device_name, list.kernel(0).device_type()); + TF_DeleteBuffer(buf); + TF_DeleteStatus(status); + } + + { + Status status; + std::unique_ptr kernel = + GetFakeKernel(device_name, op_name, &status); + TF_EXPECT_OK(status); + ASSERT_NE(nullptr, kernel.get()); + kernel->Compute(nullptr); + } + + ASSERT_TRUE(delete_called); +} + +class DummyDevice : public DeviceBase { + public: + DummyDevice(Env* env, bool save) : DeviceBase(env), save_(save) {} + bool RequiresRecordingAccessedTensors() const override { return save_; } + Allocator* GetAllocator(AllocatorAttributes /*attr*/) override { + return cpu_allocator(); + } + + private: + bool save_; +}; + +TEST(TestKernel, TestInputAndOutputCount) { + const char* kernel_name = "InputOutputCounterKernel"; + const char* op_name = "BarOp"; + const char* device_name = "FakeDeviceName2"; + + REGISTER_OP(op_name) + .Input("input1: double") + .Input("input2: uint8") + .Output("output1: uint8"); + + static int num_inputs = 0; + static int num_outputs = 0; + + // A kernel whose Compute function has a side-effect of updating num_inputs + // and num_outputs. Various functions on TF_OpKernelContext are also + // exercised. + auto my_compute_func = [](void* kernel, TF_OpKernelContext* ctx) { + num_inputs = TF_NumInputs(ctx); + num_outputs = TF_NumOutputs(ctx); + + TF_Tensor* input = nullptr; + TF_Status* s = TF_NewStatus(); + TF_GetInput(ctx, 0, &input, s); + EXPECT_EQ(TF_OK, TF_GetCode(s)) << "Failed to get input: " << TF_Message(s); + EXPECT_EQ(123, *static_cast(TF_TensorData(input))); + TF_GetInput(ctx, -1, &input, s); + EXPECT_EQ(TF_OUT_OF_RANGE, TF_GetCode(s)); + TF_GetInput(ctx, 3, &input, s); + EXPECT_EQ(TF_OUT_OF_RANGE, TF_GetCode(s)); + TF_DeleteStatus(s); + if (input != nullptr) { + TF_DeleteTensor(input); + } + }; + + TF_KernelBuilder* builder = TF_NewKernelBuilder(op_name, device_name, nullptr, + my_compute_func, nullptr); + + { + TF_Status* status = TF_NewStatus(); + TF_RegisterKernelBuilder(kernel_name, builder, status); + EXPECT_EQ(TF_OK, TF_GetCode(status)); + TF_DeleteStatus(status); + } + + { + OpKernelContext::Params p; + DummyDevice dummy_device(nullptr, false); + p.device = &dummy_device; + + Tensor t(tensorflow::uint8(123)); + + gtl::InlinedVector inputs; + // Simulate 2 inputs + inputs.emplace_back(&t); + inputs.emplace_back(); + p.inputs = &inputs; + + Status status; + std::unique_ptr kernel = + GetFakeKernel(device_name, op_name, &status); + TF_EXPECT_OK(status); + ASSERT_NE(nullptr, kernel.get()); + + p.op_kernel = kernel.get(); + OpKernelContext ctx(&p); + kernel->Compute(&ctx); + + ASSERT_EQ(2, num_inputs); + ASSERT_EQ(1, num_outputs); + } +} + +} // namespace tensorflow diff --git a/tensorflow/c/python_api.cc b/tensorflow/c/python_api.cc index 247236b760..98d8393332 100644 --- a/tensorflow/c/python_api.cc +++ b/tensorflow/c/python_api.cc @@ -160,4 +160,17 @@ void SetHandleShapeAndType(TF_Graph* graph, TF_Output output, const void* proto, ic->set_output_handle_shapes_and_types(output.index, shapes_and_types); } +void AddWhileInputHack(TF_Graph* graph, TF_Output new_src, TF_Operation* dst, + TF_Status* status) { + mutex_lock l(graph->mu); + status->status = graph->graph.AddWhileInputHack(&new_src.oper->node, + new_src.index, &dst->node); + if (status->status.ok()) { + // This modification only updates the destination node for + // the purposes of running this graph in a session. Thus, we don't + // record the source node as being modified. + RecordMutation(graph, *dst, "adding input tensor"); + } +} + } // namespace tensorflow diff --git a/tensorflow/c/python_api.h b/tensorflow/c/python_api.h index 5cce84020b..44779ca656 100644 --- a/tensorflow/c/python_api.h +++ b/tensorflow/c/python_api.h @@ -34,6 +34,7 @@ void SetAttr(TF_Graph* graph, TF_Operation* op, const char* attr_name, void SetRequestedDevice(TF_Graph* graph, TF_Operation* op, const char* device); +// Updates 'dst' to consume 'new_src'. void UpdateEdge(TF_Graph* graph, TF_Output new_src, TF_Input dst, TF_Status* status); @@ -65,6 +66,13 @@ std::string GetHandleShapeAndType(TF_Graph* graph, TF_Output output); // because I couldn't get SWIG to work otherwise. void SetHandleShapeAndType(TF_Graph* graph, TF_Output output, const void* proto, size_t proto_len, TF_Status* status); + +// This method is used to add a new input edge to 'dst', which must be a While +// op. The While op's "T" attribute must have already been updated to include +// the new edge. This is used to construct tf.while_loop gradients. +void AddWhileInputHack(TF_Graph* graph, TF_Output new_src, TF_Operation* dst, + TF_Status* status); + } // namespace tensorflow #endif // TENSORFLOW_C_PYTHON_API_H_ diff --git a/tensorflow/cc/BUILD b/tensorflow/cc/BUILD index 83353b79f7..a09becc49b 100644 --- a/tensorflow/cc/BUILD +++ b/tensorflow/cc/BUILD @@ -489,6 +489,7 @@ tf_gen_op_wrappers_cc( "image_ops", "io_ops", "linalg_ops", + "list_ops", "logging_ops", "lookup_ops", "manip_ops", diff --git a/tensorflow/cc/saved_model/BUILD b/tensorflow/cc/saved_model/BUILD index 3d3895c8fa..52345a376c 100644 --- a/tensorflow/cc/saved_model/BUILD +++ b/tensorflow/cc/saved_model/BUILD @@ -133,5 +133,6 @@ filegroup( "testdata/half_plus_two_pbtxt/**", "testdata/half_plus_two_main_op/**", "testdata/half_plus_two/**", + "testdata/half_plus_two_v2/**", ]), ) diff --git a/tensorflow/cc/saved_model/constants.h b/tensorflow/cc/saved_model/constants.h index 645a3f101d..6f00dc324b 100644 --- a/tensorflow/cc/saved_model/constants.h +++ b/tensorflow/cc/saved_model/constants.h @@ -33,10 +33,10 @@ constexpr char kSavedModelFilenamePb[] = "saved_model.pb"; /// SavedModel text format proto filename. constexpr char kSavedModelFilenamePbTxt[] = "saved_model.pbtxt"; -/// SavedModel legacy init op key. +/// SavedModel legacy init op collection key. Used in v1 SavedModels. constexpr char kSavedModelLegacyInitOpKey[] = "legacy_init_op"; -/// SavedModel main op key. +/// SavedModel main op collection key. Used in v1 SavedModels. constexpr char kSavedModelMainOpKey[] = "saved_model_main_op"; /// Directory in which to save the SavedModel variables. @@ -45,6 +45,11 @@ constexpr char kSavedModelVariablesDirectory[] = "variables"; /// SavedModel variables filename. constexpr char kSavedModelVariablesFilename[] = "variables"; +/// SavedModel SignatureDef keys for the initialization and train ops. Used in +/// V2 SavedModels. +constexpr char kSavedModelInitOpSignatureKey[] = "__saved_model_init_op"; +constexpr char kSavedModelTrainOpSignatureKey[] = "__saved_model_train_op"; + } // namespace tensorflow #endif // TENSORFLOW_CC_SAVED_MODEL_CONSTANTS_H_ diff --git a/tensorflow/cc/saved_model/loader.cc b/tensorflow/cc/saved_model/loader.cc index c6abe2f41b..85d3dd01fa 100644 --- a/tensorflow/cc/saved_model/loader.cc +++ b/tensorflow/cc/saved_model/loader.cc @@ -122,34 +122,54 @@ Status RunOnce(const RunOptions& run_options, return run_status; } -bool HasMainOp(const MetaGraphDef& meta_graph_def) { +// RunInitOp will return OK if the initialization op was run successfully. +// An empty init_op_name indicates that there are no init ops to run. +Status RunInitOp(const RunOptions& run_options, const string& export_dir, + const MetaGraphDef& meta_graph_def, + const std::vector& asset_file_defs, + Session* session, const string& init_op_name) { + if (!init_op_name.empty()) { + LOG(INFO) << "Running initialization op on SavedModel bundle."; + std::vector> inputs; + AddAssetsTensorsToInputs(export_dir, asset_file_defs, &inputs); + RunMetadata run_metadata; + return RunOnce(run_options, inputs, {}, {init_op_name}, + nullptr /* outputs */, &run_metadata, session); + } + return Status::OK(); +} + +// A SavedModel may store the name of the initialization op to run in the +// in the SignatureDef (v2) or a collection (v1). If an init_op collection +// exists, then the collection must contain exactly one op. +Status GetInitOp(const string& export_dir, const MetaGraphDef& meta_graph_def, + string* init_op_name) { + const auto& sig_def_map = meta_graph_def.signature_def(); + const auto& init_op_sig_it = + meta_graph_def.signature_def().find(kSavedModelInitOpSignatureKey); + if (init_op_sig_it != sig_def_map.end()) { + *init_op_name = init_op_sig_it->second.outputs() + .find(kSavedModelInitOpSignatureKey) + ->second.name(); + return Status::OK(); + } + const auto& collection_def_map = meta_graph_def.collection_def(); + string init_op_collection_key; if (collection_def_map.find(kSavedModelMainOpKey) != collection_def_map.end()) { - return true; + init_op_collection_key = kSavedModelMainOpKey; + } else { + init_op_collection_key = kSavedModelLegacyInitOpKey; } - return false; -} -Status RunMainOp(const RunOptions& run_options, const string& export_dir, - const MetaGraphDef& meta_graph_def, - const std::vector& asset_file_defs, - Session* session, const string& main_op_key) { - LOG(INFO) << "Running MainOp with key " << main_op_key - << " on SavedModel bundle."; - const auto& collection_def_map = meta_graph_def.collection_def(); - const auto main_op_it = collection_def_map.find(main_op_key); - if (main_op_it != collection_def_map.end()) { - if (main_op_it->second.node_list().value_size() != 1) { + const auto init_op_it = collection_def_map.find(init_op_collection_key); + if (init_op_it != collection_def_map.end()) { + if (init_op_it->second.node_list().value_size() != 1) { return errors::FailedPrecondition( strings::StrCat("Expected exactly one main op in : ", export_dir)); } - std::vector> inputs; - AddAssetsTensorsToInputs(export_dir, asset_file_defs, &inputs); - RunMetadata run_metadata; - const StringPiece main_op_name = main_op_it->second.node_list().value(0); - return RunOnce(run_options, inputs, {}, {string(main_op_name)}, - nullptr /* outputs */, &run_metadata, session); + *init_op_name = init_op_it->second.node_list().value(0); } return Status::OK(); } @@ -193,6 +213,15 @@ Status RunRestore(const RunOptions& run_options, const string& export_dir, Status GetAssetFileDefs(const MetaGraphDef& meta_graph_def, std::vector* asset_file_defs) { + // With SavedModel v2, we write asset file def into metagraph instead of + // collection, so read from metagraph first. + if (meta_graph_def.asset_file_def_size() > 0) { + for (const auto& asset : meta_graph_def.asset_file_def()) { + asset_file_defs->push_back(asset); + } + return Status::OK(); + } + // Fall back to read from collection to be backward compatible with v1. const auto& collection_def_map = meta_graph_def.collection_def(); const auto assets_it = collection_def_map.find(kSavedModelAssetsKey); if (assets_it == collection_def_map.end()) { @@ -227,15 +256,12 @@ Status LoadSavedModelInternal(const SessionOptions& session_options, bundle->meta_graph_def.saver_def().restore_op_name(), bundle->meta_graph_def.saver_def().filename_tensor_name(), asset_file_defs, bundle->session.get())); - if (HasMainOp(bundle->meta_graph_def)) { - TF_RETURN_IF_ERROR(RunMainOp(run_options, export_dir, - bundle->meta_graph_def, asset_file_defs, - bundle->session.get(), kSavedModelMainOpKey)); - } else { - TF_RETURN_IF_ERROR(RunMainOp( - run_options, export_dir, bundle->meta_graph_def, asset_file_defs, - bundle->session.get(), kSavedModelLegacyInitOpKey)); - } + string init_op_name; + TF_RETURN_IF_ERROR( + GetInitOp(export_dir, bundle->meta_graph_def, &init_op_name)); + TF_RETURN_IF_ERROR(RunInitOp(run_options, export_dir, bundle->meta_graph_def, + asset_file_defs, bundle->session.get(), + init_op_name)); return Status::OK(); } diff --git a/tensorflow/cc/saved_model/loader_test.cc b/tensorflow/cc/saved_model/loader_test.cc index 72b8bc1871..597e42bb65 100644 --- a/tensorflow/cc/saved_model/loader_test.cc +++ b/tensorflow/cc/saved_model/loader_test.cc @@ -36,6 +36,8 @@ constexpr char kTestDataMainOp[] = "cc/saved_model/testdata/half_plus_two_main_op/00000123"; constexpr char kTestDataSharded[] = "cc/saved_model/testdata/half_plus_two/00000123"; +constexpr char kTestDataInitOpV2[] = + "cc/saved_model/testdata/half_plus_two_v2/00000123"; class LoaderTest : public ::testing::Test { protected: @@ -227,5 +229,17 @@ TEST_F(LoaderTest, MaybeSavedModelDirectory) { EXPECT_FALSE(MaybeSavedModelDirectory(invalid_export_dir)); } +TEST_F(LoaderTest, SavedModelInitOpV2Format) { + SavedModelBundle bundle; + SessionOptions session_options; + RunOptions run_options; + + const string export_dir = + io::JoinPath(testing::TensorFlowSrcRoot(), kTestDataInitOpV2); + TF_ASSERT_OK(LoadSavedModel(session_options, run_options, export_dir, + {kSavedModelTagServe}, &bundle)); + CheckSavedModelBundle(export_dir, bundle); +} + } // namespace } // namespace tensorflow diff --git a/tensorflow/cc/saved_model/testdata/half_plus_two_v2/00000123/assets/foo.txt b/tensorflow/cc/saved_model/testdata/half_plus_two_v2/00000123/assets/foo.txt new file mode 100644 index 0000000000..f9ff036688 --- /dev/null +++ b/tensorflow/cc/saved_model/testdata/half_plus_two_v2/00000123/assets/foo.txt @@ -0,0 +1 @@ +asset-file-contents \ No newline at end of file diff --git a/tensorflow/cc/saved_model/testdata/half_plus_two_v2/00000123/saved_model.pb b/tensorflow/cc/saved_model/testdata/half_plus_two_v2/00000123/saved_model.pb new file mode 100644 index 0000000000000000000000000000000000000000..a10bbf8fb6bca0fcee6414b2927d2f706de85ebc GIT binary patch literal 10774 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 rewrites; - TF_RETURN_IF_ERROR(AddRewritesForShape(i, ps.parameters(i), &rewrites)); + TF_RETURN_IF_ERROR( + AddRewritesForShape(i, xla::Shape(ps.parameters(i)), &rewrites)); const string code = R"( - void set_arg{{NAME}}_data(void* data) { + void set_arg{{NAME}}_data(const void* data) { set_arg_data({{I}}, data); } {{TYPE}}* arg{{NAME}}_data() { @@ -204,7 +206,7 @@ Status GenArgMethods(const tf2xla::Config& config, const xla::ProgramShape& ps, // Generate methods for results (outputs). Status GenResultMethods(const tf2xla::Config& config, - const xla::ProgramShape& ps, string* methods) { + const xla::ProgramShapeProto& ps, string* methods) { if (ps.result().element_type() != xla::TUPLE) { // The XlaCompiler we use to build the xla computation always generates a // tuple result, and we rely on this to simplify code generation. @@ -217,8 +219,8 @@ Status GenResultMethods(const tf2xla::Config& config, } for (int i = 0; i < ps.result().tuple_shapes_size(); ++i) { std::vector> rewrites; - TF_RETURN_IF_ERROR( - AddRewritesForShape(i, ps.result().tuple_shapes(i), &rewrites)); + TF_RETURN_IF_ERROR(AddRewritesForShape( + i, xla::Shape(ps.result().tuple_shapes(i)), &rewrites)); string code = R"( {{TYPE}}* result{{NAME}}_data() { return static_cast<{{TYPE}}*>(result_data({{I}})); @@ -336,7 +338,7 @@ Status GenerateHeader(const CodegenOpts& opts, const tf2xla::Config& config, ExtractEntryParamBufferInfos(buffer_infos); std::vector buffer_infos_for_temps = ExtractTempBufferInfos(buffer_infos); - const xla::ProgramShape& ps = compile_result.program_shape; + const xla::ProgramShapeProto& ps = compile_result.program_shape; string methods_arg, methods_result; TF_RETURN_IF_ERROR(GenArgMethods(config, ps, compile_result, &methods_arg)); TF_RETURN_IF_ERROR(GenResultMethods(config, ps, &methods_result)); @@ -548,8 +550,8 @@ class {{CLASS}} : public tensorflow::XlaCompiledCpuFunction { static const char** StaticResultNames() {{RESULT_NAMES_CODE}} // Shape of the args and results. - static const xla::ProgramShape* StaticProgramShape() { - static const xla::ProgramShape* kShape = {{PROGRAM_SHAPE_SHIM_EXPRESSION}}; + static const xla::ProgramShapeProto* StaticProgramShape() { + static const xla::ProgramShapeProto* kShape = {{PROGRAM_SHAPE_SHIM_EXPRESSION}}; return kShape; } @@ -587,7 +589,7 @@ class {{CLASS}} : public tensorflow::XlaCompiledCpuFunction { {"{{METHODS_RESULT}}\n", methods_result}, {"{{NS_END}}\n", ns_end}, {"{{NS_START}}\n", ns_start}, - {"{{PROGRAM_SHAPE}}", xla::ShapeUtil::HumanString(ps)}, + {"{{PROGRAM_SHAPE}}", xla::ShapeUtil::HumanString(xla::ProgramShape(ps))}, {"{{PROGRAM_SHAPE_SHIM_EXPRESSION}}", metadata_result.program_shape_access_shim}, {"{{RESULT_INDEX}}", absl::StrCat(result_index)}, @@ -615,11 +617,11 @@ static string CreateUniqueIdentifier(const CodegenOpts& opts, Status GenerateMetadata(const CodegenOpts& opts, const CompileResult& compile_result, MetadataResult* metadata_result) { - std::unique_ptr program_shape; + std::unique_ptr program_shape; if (opts.gen_program_shape) { program_shape = - absl::make_unique(compile_result.program_shape); + absl::make_unique(compile_result.program_shape); // The parameter names are currently meaningless, and redundant with the // rest of our metadata, so clear them out to avoid confusion and save @@ -631,8 +633,8 @@ Status GenerateMetadata(const CodegenOpts& opts, // a shim that evaluates to nullptr, which is what we want. ProtobufToEmbed program_shape_protobuf{ - CreateUniqueIdentifier(opts, "ProgramShape"), "xla::ProgramShape", - program_shape.get()}; + CreateUniqueIdentifier(opts, "ProgramShapeProto"), + "xla::ProgramShapeProto", program_shape.get()}; ProtobufToEmbed hlo_profile_printer_data_protobuf{ CreateUniqueIdentifier(opts, "HloProfilePrinterData"), diff --git a/tensorflow/compiler/aot/codegen.h b/tensorflow/compiler/aot/codegen.h index 90410c46a8..9485e86b10 100644 --- a/tensorflow/compiler/aot/codegen.h +++ b/tensorflow/compiler/aot/codegen.h @@ -57,7 +57,7 @@ struct MetadataResult { std::vector header_variable_decls; // program_shape_access_shim is a C++ expression that constructs the - // xla::ProgramShape instance for the CompileResult passed to + // xla::ProgramShapeProto instance for the CompileResult passed to // GenerateMetadata. string program_shape_access_shim; diff --git a/tensorflow/compiler/aot/codegen_test.cc b/tensorflow/compiler/aot/codegen_test.cc index bb288d2300..c1788ca32a 100644 --- a/tensorflow/compiler/aot/codegen_test.cc +++ b/tensorflow/compiler/aot/codegen_test.cc @@ -181,13 +181,15 @@ TEST(CodegenTest, Golden) { BufferInfo::MakeEntryParameter(/*size=*/96, /*param_number=*/1), BufferInfo::MakeTempBuffer(3), BufferInfo::MakeTempBuffer(120)}, 5, {})); - compile_result.program_shape = xla::ShapeUtil::MakeProgramShape( - { - xla::ShapeUtil::MakeShape(xla::F32, {1, 2}), - xla::ShapeUtil::MakeShape(xla::S64, {3, 4}), - }, - xla::ShapeUtil::MakeTupleShape( - {xla::ShapeUtil::MakeShape(xla::U32, {5, 6})})); + compile_result.program_shape = + xla::ShapeUtil::MakeProgramShape( + { + xla::ShapeUtil::MakeShape(xla::F32, {1, 2}), + xla::ShapeUtil::MakeShape(xla::S64, {3, 4}), + }, + xla::ShapeUtil::MakeTupleShape( + {xla::ShapeUtil::MakeShape(xla::U32, {5, 6})})) + .ToProto(); compile_result.entry_point = "entry_point"; compile_result.pointer_size = 8; diff --git a/tensorflow/compiler/aot/codegen_test_h.golden b/tensorflow/compiler/aot/codegen_test_h.golden index e4d8a02877..968afad65e 100644 --- a/tensorflow/compiler/aot/codegen_test_h.golden +++ b/tensorflow/compiler/aot/codegen_test_h.golden @@ -22,7 +22,7 @@ extern "C" void entry_point( void* result, const xla::ExecutableRunOptions* run_options, const void** args, void** temps, tensorflow::int64* profile_counters); -extern "C" char __tfcompile_foo_bar_MyClass_ProgramShape_protobuf_array_contents[]; +extern "C" char __tfcompile_foo_bar_MyClass_ProgramShapeProto_protobuf_array_contents[]; namespace foo { @@ -114,7 +114,7 @@ class MyClass : public tensorflow::XlaCompiledCpuFunction { // with dim indices specifying which value. No bounds checking is performed // on dim indices. - void set_arg0_data(void* data) { + void set_arg0_data(const void* data) { set_arg_data(0, data); } float* arg0_data() { @@ -132,7 +132,7 @@ class MyClass : public tensorflow::XlaCompiledCpuFunction { arg_data(0)))[dim0][dim1]; } - void set_arg_myfeed_data(void* data) { + void set_arg_myfeed_data(const void* data) { set_arg_data(0, data); } float* arg_myfeed_data() { @@ -150,7 +150,7 @@ class MyClass : public tensorflow::XlaCompiledCpuFunction { arg_data(0)))[dim0][dim1]; } - void set_arg1_data(void* data) { + void set_arg1_data(const void* data) { set_arg_data(1, data); } tensorflow::int64* arg1_data() { @@ -253,10 +253,10 @@ class MyClass : public tensorflow::XlaCompiledCpuFunction { } // Shape of the args and results. - static const xla::ProgramShape* StaticProgramShape() { - static const xla::ProgramShape* kShape = []() { - xla::ProgramShape* proto = new xla::ProgramShape; - proto->ParseFromArray(&__tfcompile_foo_bar_MyClass_ProgramShape_protobuf_array_contents[0], 52); + static const xla::ProgramShapeProto* StaticProgramShape() { + static const xla::ProgramShapeProto* kShape = []() { + xla::ProgramShapeProto* proto = new xla::ProgramShapeProto; + proto->ParseFromArray(&__tfcompile_foo_bar_MyClass_ProgramShapeProto_protobuf_array_contents[0], 52); return proto; }(); return kShape; diff --git a/tensorflow/compiler/aot/codegen_test_o.golden b/tensorflow/compiler/aot/codegen_test_o.golden index eb001c5d45bdfefc76629d7303d89f5480432235..ce8e5ec8c96a2c3696f14b8eea206d648182ecb5 100644 GIT binary patch delta 82 zcmX@XdVzI<24lcP&2+}ti4)^k1B&uX@+ZEZ$-%(DP{hE%z&tsSQF!tKMh?cV$txM- g8Ji|^GTF2GGB7YOf@KnzG+3(`7#Ntqyah}e0OJr9Qvd(} delta 49 zcmcb>dV+O=2BXJB&2+|yi4)@{ewoRbJ9#3bJY(zRjg0Y(wUY&z>=`{K2Qt|+mQJ3? GWDfwepb$g= diff --git a/tensorflow/compiler/aot/compile.cc b/tensorflow/compiler/aot/compile.cc index 2b5f97b34c..9fc223bdc7 100644 --- a/tensorflow/compiler/aot/compile.cc +++ b/tensorflow/compiler/aot/compile.cc @@ -56,17 +56,23 @@ Status CompileXla(xla::CompileOnlyClient* client, return errors::Unknown("Couldn't get XLA program shape: ", pshape_or.status().error_message()); } - compile_result->program_shape = *pshape_or.ValueOrDie(); - xla::ProgramShape* pshape = &compile_result->program_shape; - std::vector arg_layouts; - arg_layouts.reserve(pshape->parameters_size()); + compile_result->program_shape = pshape_or.ValueOrDie()->ToProto(); + xla::ProgramShapeProto* pshape = &compile_result->program_shape; + + // AotXlaComputationInstance::argument_layouts is a vector of Shape + // pointers. Accumulate the Shape objects themselves in a separate vector + // while building the vector of pointers. + std::vector arg_layout_ptrs(pshape->parameters_size()); + std::vector arg_layouts(pshape->parameters_size()); for (int i = 0; i < pshape->parameters_size(); ++i) { - arg_layouts.push_back(pshape->mutable_parameters(i)); + arg_layouts[i] = xla::Shape(*pshape->mutable_parameters(i)); + arg_layout_ptrs[i] = &arg_layouts[i]; } xla::CompileOnlyClient::AotXlaComputationInstance instance; instance.computation = &computation; - instance.argument_layouts = std::move(arg_layouts); - instance.result_layout = &pshape->result(); + instance.argument_layouts = std::move(arg_layout_ptrs); + xla::Shape result_shape(pshape->result()); + instance.result_layout = &result_shape; xla::StatusOr>> aot_or = client->CompileAheadOfTime({instance}, aot_opts); if (!aot_or.ok()) { diff --git a/tensorflow/compiler/aot/compile.h b/tensorflow/compiler/aot/compile.h index e03c5b1aa7..ee7bb26fab 100644 --- a/tensorflow/compiler/aot/compile.h +++ b/tensorflow/compiler/aot/compile.h @@ -33,9 +33,9 @@ namespace tfcompile { struct CompileResult { // Contains object file and meta-info. std::unique_ptr aot; - xla::ProgramShape program_shape; // Static shape of args and results. - string entry_point; // Name of generated function. - int pointer_size = 0; // Size of a pointer in bytes. + xla::ProgramShapeProto program_shape; // Static shape of args and results. + string entry_point; // Name of generated function. + int pointer_size = 0; // Size of a pointer in bytes. }; // CompileGraph compiles the graph_def into an object file containing a function diff --git a/tensorflow/compiler/aot/tests/tfcompile_test.cc b/tensorflow/compiler/aot/tests/tfcompile_test.cc index f10852c785..4dd79e5882 100644 --- a/tensorflow/compiler/aot/tests/tfcompile_test.cc +++ b/tensorflow/compiler/aot/tests/tfcompile_test.cc @@ -526,13 +526,15 @@ TEST(TFCompileTest, ProgramShape) { // muladd has the program shape defined. MatMulAndAddComp muladd; - const xla::ProgramShape* muladd_shape = muladd.ProgramShape(); + const xla::ProgramShapeProto* muladd_shape = muladd.ProgramShape(); ASSERT_TRUE(muladd_shape != nullptr); ASSERT_EQ(muladd_shape->parameters_size(), 2); - EXPECT_TRUE(ShapeUtil::Compatible(muladd_shape->parameters(0), f32_2x2)); - EXPECT_TRUE(ShapeUtil::Compatible(muladd_shape->parameters(1), f32_2x2)); + EXPECT_TRUE( + ShapeUtil::Compatible(xla::Shape(muladd_shape->parameters(0)), f32_2x2)); + EXPECT_TRUE( + ShapeUtil::Compatible(xla::Shape(muladd_shape->parameters(1)), f32_2x2)); - const xla::Shape& muladd_result = muladd_shape->result(); + const xla::Shape muladd_result(muladd_shape->result()); ASSERT_EQ(muladd_result.element_type(), xla::TUPLE); ASSERT_EQ(ShapeUtil::TupleElementCount(muladd_result), 2); const xla::Shape& muladd_result0 = diff --git a/tensorflow/compiler/jit/BUILD b/tensorflow/compiler/jit/BUILD index 5f25e4626a..be91ed4f43 100644 --- a/tensorflow/compiler/jit/BUILD +++ b/tensorflow/compiler/jit/BUILD @@ -23,7 +23,6 @@ package( load("//tensorflow:tensorflow.bzl", "cc_header_only_library") load("//tensorflow:tensorflow.bzl", "tf_cc_test") load("@local_config_cuda//cuda:build_defs.bzl", "if_cuda") -load("@local_config_cuda//cuda:build_defs.bzl", "if_cuda_is_configured") load("//tensorflow:tensorflow.bzl", "tf_cuda_cc_test") load("//tensorflow:tensorflow.bzl", "tf_custom_op_py_library") @@ -38,7 +37,7 @@ cc_library( ":xla_cpu_device", ":xla_cpu_jit", "//tensorflow/compiler/plugin", - ] + if_cuda_is_configured([ + ] + if_cuda([ ":xla_gpu_device", ":xla_gpu_jit", ]), @@ -51,6 +50,7 @@ cc_library( deps = [ ":jit_compilation_passes", "//tensorflow/compiler/jit/kernels:xla_ops", + "//tensorflow/compiler/tf2xla/kernels:xla_cpu_only_ops", "//tensorflow/compiler/tf2xla/kernels:xla_dummy_ops", "//tensorflow/compiler/tf2xla/kernels:xla_ops", "//tensorflow/compiler/xla/service:cpu_plugin", @@ -76,10 +76,10 @@ cc_library( srcs = ["xla_cpu_device.cc"], visibility = [":friends"], deps = [ + ":flags", ":jit_compilation_passes", ":xla_device", "//tensorflow/compiler/jit/kernels:xla_ops", - "//tensorflow/compiler/jit/legacy_flags:xla_device_flags", "//tensorflow/compiler/tf2xla:xla_compiler", "//tensorflow/compiler/tf2xla/kernels:xla_ops", "//tensorflow/compiler/xla/service:cpu_plugin", # buildcleaner: keep @@ -210,6 +210,18 @@ cc_library( # Internal targets below this point. +cc_library( + name = "flags", + srcs = ["flags.cc"], + hdrs = ["flags.h"], + visibility = [":friends"], + deps = [ + "//tensorflow/compiler/xla:parse_flags_from_env", + "//tensorflow/core:framework_internal", + "//tensorflow/core:lib", + ], +) + cc_library( name = "common", srcs = [ @@ -256,6 +268,7 @@ cc_library( "//tensorflow/compiler/tf2xla:common", "//tensorflow/compiler/tf2xla:dump_graph", "//tensorflow/compiler/tf2xla:xla_compiler", + "//tensorflow/compiler/xla:debug_options_flags", "//tensorflow/compiler/xla:statusor", "//tensorflow/compiler/xla/client:client_library", "//tensorflow/compiler/xla/client:local_client", @@ -268,6 +281,7 @@ cc_library( "//tensorflow/core/kernels:variable_ops", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:optional", "@com_google_absl//absl/types:span", ], ) @@ -487,6 +501,7 @@ cc_library( deps = [ ":common", ":encapsulate_util", + ":flags", ":shape_inference_helpers", ":union_find", ":xla_cluster_util", @@ -494,8 +509,6 @@ cc_library( "//tensorflow/cc:ops", "//tensorflow/cc:scope_internal", "//tensorflow/compiler/jit/graphcycles", - "//tensorflow/compiler/jit/legacy_flags:build_xla_ops_pass_flags", - "//tensorflow/compiler/jit/legacy_flags:mark_for_compilation_pass_flags", "//tensorflow/compiler/jit/ops:xla_ops", "//tensorflow/compiler/tf2xla:dump_graph", "//tensorflow/compiler/tf2xla:resource_operation_table", @@ -724,7 +737,10 @@ tf_custom_op_py_library( visibility = [ ":friends", ], - deps = ["//tensorflow/compiler/jit/ops:xla_ops_wrapper_py"], + deps = [ + "//tensorflow/compiler/jit/ops:xla_ops_grad", + "//tensorflow/compiler/jit/ops:xla_ops_wrapper_py", + ], ) # This target can be used by XLA device plugins to prevent circular dependencies, and provides access to all of the required headers for building a device library. diff --git a/tensorflow/compiler/jit/build_xla_ops_pass.cc b/tensorflow/compiler/jit/build_xla_ops_pass.cc index 93637a69d5..9f4042630e 100644 --- a/tensorflow/compiler/jit/build_xla_ops_pass.cc +++ b/tensorflow/compiler/jit/build_xla_ops_pass.cc @@ -23,7 +23,7 @@ limitations under the License. #include "tensorflow/cc/ops/control_flow_ops.h" #include "tensorflow/compiler/jit/defs.h" #include "tensorflow/compiler/jit/encapsulate_subgraphs_pass.h" -#include "tensorflow/compiler/jit/legacy_flags/build_xla_ops_pass_flags.h" +#include "tensorflow/compiler/jit/flags.h" #include "tensorflow/compiler/jit/xla_cluster_util.h" #include "tensorflow/compiler/tf2xla/cc/ops/xla_jit_ops.h" #include "tensorflow/compiler/tf2xla/dump_graph.h" @@ -320,10 +320,10 @@ Status BuildXlaOpsPass::Run(const GraphOptimizationPassOptions& options) { return IsXlaCompiledKernel(*n); }); - bool lazy_compilation_enabled = enable_lazy_compilation_ - ? *enable_lazy_compilation_ - : legacy_flags::GetBuildXlaOpsPassFlags() - .tf_xla_enable_lazy_compilation; + bool lazy_compilation_enabled = + enable_lazy_compilation_ + ? *enable_lazy_compilation_ + : GetBuildXlaOpsPassFlags().tf_xla_enable_lazy_compilation; for (Node* n : xla_compiled_kernels) { TF_RETURN_IF_ERROR(ReplaceNodeWithXlaCompileAndXlaRun( diff --git a/tensorflow/compiler/jit/build_xla_ops_pass_test.cc b/tensorflow/compiler/jit/build_xla_ops_pass_test.cc index 11df946cc1..48a23a4c17 100644 --- a/tensorflow/compiler/jit/build_xla_ops_pass_test.cc +++ b/tensorflow/compiler/jit/build_xla_ops_pass_test.cc @@ -42,14 +42,8 @@ class BuildXlaOpsTest : public ::testing::Test { .ok()); } - void TearDown() override { - for (Device* device : devices_) { - delete device; - } - } - private: - std::vector devices_; + std::vector> devices_; }; using ::tensorflow::testing::FindNodeByName; diff --git a/tensorflow/compiler/jit/create_xla_launch_op_test.cc b/tensorflow/compiler/jit/create_xla_launch_op_test.cc index 7386660762..0f872a480f 100644 --- a/tensorflow/compiler/jit/create_xla_launch_op_test.cc +++ b/tensorflow/compiler/jit/create_xla_launch_op_test.cc @@ -59,8 +59,9 @@ class CreateXlaLaunchOpTest : public ::testing::Test { SessionOptions options; auto* device_count = options.config.mutable_device_count(); device_count->insert({"CPU", 1}); + std::vector> devices; TF_CHECK_OK(DeviceFactory::AddDevices( - options, "/job:localhost/replica:0/task:0", &devices_)); + options, "/job:localhost/replica:0/task:0", &devices)); FunctionDefLibrary proto; for (const auto& fdef : flib) { @@ -69,7 +70,7 @@ class CreateXlaLaunchOpTest : public ::testing::Test { lib_def_ = absl::make_unique( OpRegistry::Global(), proto); OptimizerOptions opts; - device_mgr_ = absl::make_unique(devices_); + device_mgr_ = absl::make_unique(std::move(devices)); pflr_ = absl::make_unique( device_mgr_.get(), Env::Default(), TF_GRAPH_DEF_VERSION, lib_def_.get(), opts, /*default_thread_pool=*/nullptr, /*cluster_flr=*/nullptr); @@ -77,7 +78,6 @@ class CreateXlaLaunchOpTest : public ::testing::Test { } FunctionLibraryRuntime* flr_; - std::vector devices_; std::unique_ptr device_mgr_; std::unique_ptr lib_def_; std::unique_ptr pflr_; diff --git a/tensorflow/compiler/jit/encapsulate_util.cc b/tensorflow/compiler/jit/encapsulate_util.cc index 28ec37b1b9..1f4b9c90a4 100644 --- a/tensorflow/compiler/jit/encapsulate_util.cc +++ b/tensorflow/compiler/jit/encapsulate_util.cc @@ -86,7 +86,7 @@ Status ProcessControlEdges(Graph* g, const string& xla_computation_attr_name, continue; } else if (src_xla_computation && !dst_xla_computation) { if (src_outside_compilation) { - // Case 1d: outside compilation to host computation control edge. + // Case 1c: outside compilation to host computation control edge. edges_to_remove.push_back(e); TF_RETURN_IF_ERROR(AppendToListAttr( @@ -94,7 +94,7 @@ Status ProcessControlEdges(Graph* g, const string& xla_computation_attr_name, } } else if (!src_xla_computation && dst_xla_computation) { if (dst_outside_compilation) { - // Case 1d: host computation control to outside compilation edge. + // Case 1c: host computation control to outside compilation edge. edges_to_remove.push_back(e); TF_RETURN_IF_ERROR(AppendToListAttr( @@ -103,40 +103,24 @@ Status ProcessControlEdges(Graph* g, const string& xla_computation_attr_name, } else { // src_xla_computation && dst_xla_computation if (*src_xla_computation != *dst_xla_computation) { if (src_outside_compilation && dst_outside_compilation) { - // Case 1c: outside compilation to outside compilation control edge. + // Case 1b: outside compilation to outside compilation control edge. edges_to_remove.push_back(e); TF_RETURN_IF_ERROR(AppendToListAttr( e->dst(), kXlaControlDependenciesAttrName, e->src()->name())); } else if (src_outside_compilation && !dst_outside_compilation) { - // Case 1b: outside compilation to another XLA computaition control + // Case 1a: outside compilation to another XLA computaition control // edge. TF_RETURN_IF_ERROR(AppendToListAttr( e->src(), kXlaConnectedToOtherXlaComputationAttrName, *dst_xla_computation)); } else if (!src_outside_compilation && dst_outside_compilation) { - // Case 1b: another XLA computaition to outside compilation control + // Case 1a: another XLA computaition to outside compilation control // edge. TF_RETURN_IF_ERROR(AppendToListAttr( e->dst(), kXlaConnectedFromOtherXlaComputationAttrName, *src_xla_computation)); } - } else { // *src_xla_computation == *dst_xla_computation - if (src_outside_compilation && dst_outside_compilation) { - if (*src_outside_compilation != *dst_outside_compilation) { - // Case 1c: outside compilation to outside compilation control edge. - edges_to_remove.push_back(e); - - TF_RETURN_IF_ERROR(AppendToListAttr( - e->dst(), kXlaControlDependenciesAttrName, e->src()->name())); - } - } else if (src_outside_compilation && !dst_outside_compilation) { - // Case 1a: outside compilation to its XLA computation control edge. - ReplaceAttr(e->src(), kXlaConnectedToXlaComputationAttrName, true); - } else if (!src_outside_compilation && dst_outside_compilation) { - // Case 1a: XLA computation to outside compilation in it control edge. - ReplaceAttr(e->dst(), kXlaConnectedFromXlaComputationAttrName, true); - } } } } @@ -181,12 +165,6 @@ Status ProcessXlaToXlaDataEdges(Graph* g, edges.push_back(EdgeInfo{e->dst_input(), e->dst()->id()}); VLOG(4) << "XLA -> XLA edge: " << e->DebugString(); } - } else { // *src_xla_computation == *dst_xla_computation - if (src_outside_compilation && dst_outside_compilation && - *src_outside_compilation != *dst_outside_compilation) { - edges.push_back(EdgeInfo{e->dst_input(), e->dst()->id()}); - VLOG(4) << "XLA -> XLA edge: " << e->DebugString(); - } } } @@ -263,7 +241,7 @@ Status ProcessDataEdgeBetweenOutsideCompilationAndHostComputation( // Remove the edge from host to outside compilation. Add a placeholder as // outside compilation node input. - std::map placeholders; + std::map, Node*> placeholders; for (int i = 0; i < edges.size(); i++) { Node* dst = g->FindNodeId(edges[i].dst_node_id); const Edge* e; @@ -275,9 +253,10 @@ Status ProcessDataEdgeBetweenOutsideCompilationAndHostComputation( // Find or create placeholder node. string new_name = edges[i].is_host_to_outside_compilation - ? absl::StrCat(src->name(), "_host_to_oc_placeholder") - : absl::StrCat(src->name(), "_oc_to_host_placeholder"); - auto iter = placeholders.find(new_name); + ? absl::StrCat(src->name(), "_host_to_oc_placeholder_", src_output) + : absl::StrCat(src->name(), "_oc_to_host_placeholder_", src_output); + auto placeholder_index = std::make_pair(src->name(), src_output); + auto iter = placeholders.find(placeholder_index); Node* placeholder_node; if (iter == placeholders.end()) { NodeDefBuilder placeholder_builder(new_name, "Placeholder"); @@ -310,7 +289,7 @@ Status ProcessDataEdgeBetweenOutsideCompilationAndHostComputation( Status s; placeholder_node = g->AddNode(placeholder_def, &s); TF_RETURN_IF_ERROR(s); - placeholders[new_name] = placeholder_node; + placeholders[placeholder_index] = placeholder_node; } else { placeholder_node = iter->second; } @@ -594,14 +573,244 @@ Status AddControlDependencies( return Status::OK(); } +// Step 1 for `PreprocessEdgesBetweenOutsideCompilations`. See comments of +// `PreprocessEdgesBetweenOutsideCompilations` for details. +Status PreprocessControlEdgesBetweenOutsideCompilations( + Graph* g, const string& outside_compilation_attr_name) { + // Gather edges to remove. We should not remove the edge while iterating. + std::vector edges_to_remove; + for (const Edge* e : g->edges()) { + if (!e->IsControlEdge()) { + continue; + } + + auto src_outside_compilation = + GetStringAttr(*e->src(), outside_compilation_attr_name); + auto dst_outside_compilation = + GetStringAttr(*e->dst(), outside_compilation_attr_name); + + if (src_outside_compilation && dst_outside_compilation) { + if (*src_outside_compilation != *dst_outside_compilation) { + // Case 1a: outside compilation to outside compilation control edge. + edges_to_remove.push_back(e); + + TF_RETURN_IF_ERROR(AppendToListAttr( + e->dst(), kXlaControlDependenciesWithinXlaClusterAttrName, + e->src()->name())); + } + } else if (src_outside_compilation && !dst_outside_compilation) { + // Case 1b: outside compilation to its XLA computation control edge. + ReplaceAttr(e->src(), kXlaConnectedToXlaComputationAttrName, true); + } else if (!src_outside_compilation && dst_outside_compilation) { + // Case 1b: XLA computation to outside compilation in it control edge. + ReplaceAttr(e->dst(), kXlaConnectedFromXlaComputationAttrName, true); + } + } + + for (auto e : edges_to_remove) { + g->RemoveEdge(e); + } + return Status::OK(); +} + +// Step 2 for `PreprocessEdgesBetweenOutsideCompilations`. See comments of +// `PreprocessEdgesBetweenOutsideCompilations` for details. +Status PreprocessDataEdgesBetweenOutsideCompilations( + Graph* g, const string& outside_compilation_attr_name) { + // Gather edges between outside compilation and host computation. Notice that + // we do not store `Edge*` directly because we remove some nodes while adding + // Identity nodes, and those Edge pointers might be invalidated. + struct EdgeInfo { + int dst_input, dst_node_id; + }; + std::vector edges; + for (const Edge* e : g->edges()) { + if (e->IsControlEdge()) { + continue; + } + + auto src_outside_compilation = + GetStringAttr(*e->src(), outside_compilation_attr_name); + auto dst_outside_compilation = + GetStringAttr(*e->dst(), outside_compilation_attr_name); + + if (src_outside_compilation && dst_outside_compilation && + *src_outside_compilation != *dst_outside_compilation) { + edges.push_back(EdgeInfo{e->dst_input(), e->dst()->id()}); + VLOG(4) << "Oc -> oc edge: " << e->DebugString(); + } + } + + // Remove the edge from host to outside compilation. Add a placeholder as + // outside compilation node input. + std::map, Node*> placeholders; + for (int i = 0; i < edges.size(); i++) { + Node* dst = g->FindNodeId(edges[i].dst_node_id); + const Edge* e; + TF_RETURN_IF_ERROR(dst->input_edge(edges[i].dst_input, &e)); + Node* src = e->src(); + int src_output = e->src_output(), dst_input = e->dst_input(); + g->RemoveEdge(e); + + // Find or create placeholder node. + string new_name = + absl::StrCat(src->name(), "_oc_to_oc_placeholder_", src_output); + auto placeholder_index = std::make_pair(src->name(), src_output); + auto iter = placeholders.find(placeholder_index); + Node* placeholder_node; + if (iter == placeholders.end()) { + NodeDefBuilder placeholder_builder(new_name, "Placeholder"); + placeholder_builder.Attr("dtype", src->output_type(src_output)); + string outside_compilation_attr; + TF_RETURN_IF_ERROR(GetNodeAttr(dst->attrs(), + outside_compilation_attr_name, + &outside_compilation_attr)); + placeholder_builder.Attr(outside_compilation_attr_name, + outside_compilation_attr); + placeholder_builder.Attr(kOutsideCompilationOriginalNodeAttrName, + src->name()); + placeholder_builder.Attr(kOutsideCompilationSrcOutputAttrName, + src_output); + NodeDef placeholder_def; + TF_RETURN_IF_ERROR(placeholder_builder.Finalize(&placeholder_def)); + Status s; + placeholder_node = g->AddNode(placeholder_def, &s); + TF_RETURN_IF_ERROR(s); + placeholders[placeholder_index] = placeholder_node; + } else { + placeholder_node = iter->second; + } + g->AddEdge(placeholder_node, 0, dst, dst_input); + + // Replace `e->dst()` because its input node changed. + NodeDef new_def = dst->def(); + *new_def.mutable_input(dst_input) = placeholder_node->name(); + TF_ASSIGN_OR_RETURN(Node * dst_replace_node, ReplaceNode(g, dst, new_def)); + + // Other edge in `edges` might have `e->dst()` as src or dst + // node. Before removing `e->dst()`, replace those edges with + // corresponding edges for `dst_replace_node`. + for (int j = i + 1; j < edges.size(); j++) { + if (edges[j].dst_node_id == edges[i].dst_node_id) { + edges[j].dst_node_id = dst_replace_node->id(); + } + } + } + return Status::OK(); +} + +// Step 1 for `PostprocessEdgesBetweenOutsideCompilations`. See comments of +// `PostprocessEdgesBetweenOutsideCompilations` for details. +Status PostprocessDataEdgesBetweenOutsideCompilations( + Graph* g, const string& outside_compilation_attr_name) { + // Gather all outside compilation to outside compilation nodes. + std::vector placeholder_nodes; + for (Node* n : g->nodes()) { + if (n->type_string() == "Placeholder" && + HasNodeAttr(n->def(), kOutsideCompilationOriginalNodeAttrName)) { + placeholder_nodes.push_back(n); + } + } + + // Remove the placeholder nodes, and reconnect original edge. + auto node_name_index = g->BuildNodeNameIndex(); + for (auto n : placeholder_nodes) { + string node_name; + int node_src_output; + TF_RETURN_IF_ERROR(GetNodeAttr( + n->attrs(), kOutsideCompilationOriginalNodeAttrName, &node_name)); + TF_RETURN_IF_ERROR(GetNodeAttr( + n->attrs(), kOutsideCompilationSrcOutputAttrName, &node_src_output)); + auto iter = node_name_index.find(node_name); + if (iter == node_name_index.end()) { + return errors::Internal( + "Cannot find original node for oc -> host placeholder node ", + node_name); + } + + // Change all usage node to use the original node instead. + Node* original_node = iter->second; + std::vector control_edges; + std::vector data_edges; + for (auto e : n->out_edges()) { + if (e->IsControlEdge()) { + control_edges.push_back(e); + } else { + data_edges.push_back({e->dst(), e->src_output(), e->dst_input()}); + } + } + for (const Edge* e : control_edges) { + g->AddControlEdge(original_node, e->dst()); + g->RemoveEdge(e); + } + for (int i = 0; i < data_edges.size(); i++) { + Node* dst = data_edges[i].dst; + NodeDef new_def = dst->def(); + int dst_input = data_edges[i].dst_input; + *new_def.mutable_input(dst_input) = + absl::StrCat(original_node->name(), ":", node_src_output); + TF_ASSIGN_OR_RETURN(Node * replace_node, ReplaceNode(g, dst, new_def)); + + const Edge* edge_to_replace = nullptr; + TF_RETURN_IF_ERROR(replace_node->input_edge(dst_input, &edge_to_replace)); + g->RemoveEdge(edge_to_replace); + g->AddEdge(original_node, node_src_output, replace_node, dst_input); + + // Other edges might have `dst` as dst node. Update those edges with + // `replace_node`. + for (int j = i + 1; j < data_edges.size(); j++) { + if (data_edges[j].dst == dst) { + data_edges[j].dst = replace_node; + } + } + + // Other placeholder node might have `dst` as original node. Update + // `node_name_index` with `replace_node`. + node_name_index[replace_node->name()] = replace_node; + } + + // Remove placeholder node. + g->RemoveNode(n); + } + return Status::OK(); +} + +// Step 2 for `PostprocessEdgesBetweenOutsideCompilations`. See comments of +// `PostprocessEdgesBetweenOutsideCompilations` for details. +Status PostprocessControlEdgesBetweenOutsideCompilations( + Graph* g, const string& outside_compilation_attr_name) { + auto node_name_index = g->BuildNodeNameIndex(); + + // Reconnect outside compilation to outside compilation control edge. + for (Node* n : g->nodes()) { + std::vector control_deps; + Status s = + GetNodeAttr(n->attrs(), kXlaControlDependenciesWithinXlaClusterAttrName, + &control_deps); + if (!s.ok()) { + if (s.code() != error::NOT_FOUND) { + return s; + } else { + continue; + } + } else { + n->ClearAttr(kXlaControlDependenciesWithinXlaClusterAttrName); + for (const string& control_input : control_deps) { + auto iter = node_name_index.find(control_input); + if (iter == node_name_index.end()) { + return errors::Internal("Cannot find original node for ", + control_input); + } + g->AddControlEdge(iter->second, n); + } + } + } + return Status::OK(); +} } // namespace const char kXlaInferredShapesAttrName[] = "_xla_inferred_shapes"; -const char kXlaConnectedToXlaComputationAttrName[] = - "_xla_connected_to_xla_computation"; -const char kXlaConnectedFromXlaComputationAttrName[] = - "_xla_connected_from_xla_computation"; const char kXlaConnectedToOtherXlaComputationAttrName[] = "_xla_connected_to_other_xla_computation"; const char kXlaConnectedFromOtherXlaComputationAttrName[] = @@ -616,6 +825,15 @@ const char kHostToOutsideCompilationOriginalNodeAttrName[] = "_xla_host_to_oc_node_name"; const char kHostToOutsideCompilationSrcOutputAttrName[] = "_xla_host_to_oc_src_output"; +const char kXlaConnectedToXlaComputationAttrName[] = + "_xla_connected_to_xla_computation"; +const char kXlaConnectedFromXlaComputationAttrName[] = + "_xla_connected_from_xla_computation"; +const char kOutsideCompilationOriginalNodeAttrName[] = + "_xla_oc_to_oc_node_name"; +const char kOutsideCompilationSrcOutputAttrName[] = "_xla_oc_to_oc_src_output"; +const char kXlaControlDependenciesWithinXlaClusterAttrName[] = + "_xla_control_dependencies_within_xla_cluster"; Status PerformStaticShapeInferenceBeforeEncapsulation( Graph* g, const string& xla_computation_attr_name, @@ -699,4 +917,39 @@ Status PostprocessForEncapsulation( return Status::OK(); } +Status PreprocessEdgesBetweenOutsideCompilations( + Graph* g, const string& outside_compilation_attr_name) { + // Remove edges from source node to outside compilation nodes, and edges + // from outside compilation nodes to sink node. + std::vector edges_to_remove; + for (const Edge* e : g->source_node()->out_edges()) { + if (HasNodeAttr(e->dst()->def(), outside_compilation_attr_name)) { + edges_to_remove.push_back(e); + } + } + for (const Edge* e : g->sink_node()->in_edges()) { + if (HasNodeAttr(e->src()->def(), outside_compilation_attr_name)) { + edges_to_remove.push_back(e); + } + } + for (auto e : edges_to_remove) { + g->RemoveEdge(e); + } + + TF_RETURN_IF_ERROR(PreprocessControlEdgesBetweenOutsideCompilations( + g, outside_compilation_attr_name)); + TF_RETURN_IF_ERROR(PreprocessDataEdgesBetweenOutsideCompilations( + g, outside_compilation_attr_name)); + return Status::OK(); +} + +Status PostprocessEdgesBetweenOutsideCompilations( + Graph* g, const string& outside_compilation_attr_name) { + TF_RETURN_IF_ERROR(PostprocessDataEdgesBetweenOutsideCompilations( + g, outside_compilation_attr_name)); + TF_RETURN_IF_ERROR(PostprocessControlEdgesBetweenOutsideCompilations( + g, outside_compilation_attr_name)); + return Status::OK(); +} + } // namespace tensorflow diff --git a/tensorflow/compiler/jit/encapsulate_util.h b/tensorflow/compiler/jit/encapsulate_util.h index 5e0c4bf6a0..e363bc5754 100644 --- a/tensorflow/compiler/jit/encapsulate_util.h +++ b/tensorflow/compiler/jit/encapsulate_util.h @@ -44,14 +44,6 @@ Status PerformStaticShapeInferenceBeforeEncapsulation( Graph* g, const string& xla_computation_attr_name, const string& outside_compilation_attr_name); -// Attribute indicating that some ops in this node's XLA computation has control -// dependency on this node. Attribute value will always be "true". -extern const char kXlaConnectedToXlaComputationAttrName[]; - -// Attribute indicating that this node has control dependency on some ops in -// this node's XLA computation. Attribute value will always be "true". -extern const char kXlaConnectedFromXlaComputationAttrName[]; - // Attribute indicating that some ops in other XLA computation has control // dependency on this node. Attribute value will be a list of string (XLA // computation names). @@ -81,6 +73,14 @@ extern const char kOutsideCompilationToHostOriginalNodeAttrName[]; // int (src_output for original edge). extern const char kOutsideCompilationToHostSrcOutputAttrName[]; +// Attribute indicating that some ops in this node's XLA computation has control +// dependency on this node. Attribute value will always be "true". +extern const char kXlaConnectedToXlaComputationAttrName[]; + +// Attribute indicating that this node has control dependency on some ops in +// this node's XLA computation. Attribute value will always be "true". +extern const char kXlaConnectedFromXlaComputationAttrName[]; + // Attribute indicating that this is an Placeholder node added to act as a // temporary input node for an host node. Attribute value will be string // (original input node name). @@ -91,19 +91,31 @@ extern const char kHostToOutsideCompilationOriginalNodeAttrName[]; // for original edge). extern const char kHostToOutsideCompilationSrcOutputAttrName[]; -// Preprocesses the graph for encapsulation. It will perform the following -// operations in order: +// Attribute indicating that this is an Placeholder node added to act as a +// temporary input node for an outside compilation node. Attribute value will be +// string (original input node name). +extern const char kOutsideCompilationOriginalNodeAttrName[]; + +// Attribute indicating that this is an Placeholder node added to act as a +// temporary input node for an outside compilation node. Attribute value will be +// int (src_output for original edge). +extern const char kOutsideCompilationSrcOutputAttrName[]; + +// Attribute indicating that this node has control dependencies on some other +// nodes within the same XLA cluster. Attribute value will be a list of string +// (node names). +extern const char kXlaControlDependenciesWithinXlaClusterAttrName[]; + +// Preprocesses edges between different XLA clusters for encapsulation. It will +// perform the following operations in order: // -// 1a. For control edges between outside compilation and its XLA computation, -// add attr "kXlaConnected{From, To}XlaComputationAttrName = true" to the -// outside compilation node. -// 1b. For control edges between outside compilation and another XLA +// 1a. For control edges between outside compilation and another XLA // computation, add attr "kXlaConnected{From, To}OtherXlaComputationAttrName // = XLA computation node name" to the outside compilation node. -// 1c. For control edges between different outside compilations, remove the edge -// and add attr "kXlaControlDependenciesAttrName = src node name" to dst -// node. -// 1d. For control edges between outside compilation and host computation, +// 1b. For control edges between different outside compilations (in different +// XLA computations), remove the edge and add attr +// "kXlaControlDependenciesAttrName = src node name" to dst node. +// 1c. For control edges between outside compilation and host computation, // remove the edge and add attr "kXlaControlDependenciesAttrName = src node // name" to dst node. // 2. For data edges between different XLA computations, if either src or dst @@ -146,26 +158,53 @@ struct XlaClusterInfo { const std::map host_compute_core; }; -// Postprocesses the graph for encapsulation. This function reverts what -// `PreprocessForEncapsulation` did. It will perform the following operations in -// order: +// Postprocesses edges between different XLA clusters for encapsulation. This +// function reverts what `PreprocessForEncapsulation` did. It will perform the +// following operations in order: // // 1. Remove Placeholder nodes between outside compilation and host computation // (created in `PreprocessForEncapsulation` step 3). // 2. Remove Identity nodes created in `PreprocessForEncapsulation` step 2. -// 3a. Reconnect control edges between different outside compilations (marked by -// `PreprocessForEncapsulation` step 1c) and control edges between outside -// compilation and host computation (marked by `PreprocessForEncapsulation` -// step 1d). -// 3b. Reconnect control edges between outside compilation and another XLA -// computation (marked by `PreprocessForEncapsulation` step 1b). -// Notice that control edges marked by `PreprocessForEncapsulation` step 1a are -// not handled here. They are handled in `RewriteOutsideCompilationSubgraphFn`. +// 3a. Reconnect control edges between outside compilation and another XLA +// computation (marked by `PreprocessForEncapsulation` step 1a). +// 3b. Reconnect control edges between different outside compilations (marked by +// `PreprocessForEncapsulation` step 1b). +// 3c. Reconnect control edges between outside compilation and host computation +// (marked by `PreprocessForEncapsulation` step 1c). Status PostprocessForEncapsulation( Graph* g, const string& xla_computation_attr_name, const string& outside_compilation_attr_name, const std::unordered_map& clusters); +// Preprocesses edges within the same XLA cluster. It will perform the following +// operations in order: +// +// 0. Remove edges from source node to outside compilation nodes, and edges +// from outside compilation nodes to sink node. +// 1a. For edges between different outside compilation clusters, remove the edge +// and add attr "kXlaControlDependenciesWithinXlaClusterAttrName = src node +// name" to dst node. +// 1b. For control edges between outside compilation and its XLA computation, +// add attr "kXlaConnected{From, To}XlaComputationAttrName = true" to the +// outside compilation node. +// 2. For data edges between different outside compilations, remove the edge +// and create a Placeholder node as dst node's input. +Status PreprocessEdgesBetweenOutsideCompilations( + Graph* g, const string& outside_compilation_attr_name); + +// Postprocesses edges within the same XLA cluster. This function reverts what +// `PreprocessEdgesBetweenOutsideCompilations` did. It will perform the +// following operations in order: +// +// 1. Remove Placeholder nodes between different outside compilations (created +// in `PreprocessEdgesBetweenOutsideCompilations` step 2). +// 2a. Reconnect control edges between different outside compilations (marked by +// `PreprocessEdgesBetweenOutsideCompilations` step 1a). +// Notice that control edges marked by +// `PreprocessEdgesBetweenOutsideCompilations` step 1b are not handled here. +// They are handled in `RewriteOutsideCompilationSubgraphFn`. +Status PostprocessEdgesBetweenOutsideCompilations( + Graph* g, const string& outside_compilation_attr_name); } // namespace tensorflow #endif // TENSORFLOW_COMPILER_JIT_ENCAPSULATE_UTIL_H_ diff --git a/tensorflow/compiler/jit/encapsulate_util_test.cc b/tensorflow/compiler/jit/encapsulate_util_test.cc index 7255df3112..3b8b49cb92 100644 --- a/tensorflow/compiler/jit/encapsulate_util_test.cc +++ b/tensorflow/compiler/jit/encapsulate_util_test.cc @@ -107,28 +107,19 @@ TEST(PreprocessForEncapsulationTest, ControlEdges) { identity4_node->AddAttr("_xla", "1"); identity4_node->AddAttr("_oc", "0"); identity5_node->AddAttr("_xla", "1"); - // Case 1a: control edges between outside compilation and its XLA computation. - g.AddControlEdge(add_node, identity0_node); - g.AddControlEdge(identity0_node, identity1_node); - // Case 1b: control edges between outside compilation and another XLA + // Case 1a: control edges between outside compilation and another XLA // computation. g.AddControlEdge(identity0_node, identity3_node); g.AddControlEdge(identity1_node, identity4_node); - // Case 1c: control edges between different outside compilations. + // Case 1b: control edges between different outside compilations. g.AddControlEdge(identity0_node, identity4_node); - // Case 1d: control edges between outside compilation and host computation. + // Case 1c: control edges between outside compilation and host computation. g.AddControlEdge(const0_node, identity0_node); g.AddControlEdge(identity0_node, identity2_node); TF_CHECK_OK(PreprocessForEncapsulation(&g, "_xla", "_oc")); - // Case 1a: add attr "_xla_connected_{from/to}_xla_computation = true" to the - // outside compilation node. - EXPECT_TRUE(HasNodeAttr(identity0_node->def(), - kXlaConnectedFromXlaComputationAttrName)); - EXPECT_TRUE(HasNodeAttr(identity0_node->def(), - kXlaConnectedToXlaComputationAttrName)); - // Case 1b: add attr "_xla_control_deps_{from/to} = XLA computation node name" + // Case 1a: add attr "_xla_control_deps_{from/to} = XLA computation node name" // to the outside compilation node. std::vector attr; TF_CHECK_OK(GetNodeAttr(identity0_node->def(), @@ -140,13 +131,13 @@ TEST(PreprocessForEncapsulationTest, ControlEdges) { kXlaConnectedFromOtherXlaComputationAttrName, &attr)); EXPECT_EQ(attr.size(), 1); EXPECT_EQ(attr[0], "0"); - // Case 1c: add attr "_xla_control_deps = src node name" to dst node. + // Case 1b: add attr "_xla_control_deps = src node name" to dst node. attr.clear(); TF_CHECK_OK(GetNodeAttr(identity4_node->def(), kXlaControlDependenciesAttrName, &attr)); EXPECT_EQ(attr.size(), 1); EXPECT_EQ(attr[0], "identity0"); - // Case 1d: add attr "_xla_control_deps = src node name" to dst node. + // Case 1c: add attr "_xla_control_deps = src node name" to dst node. attr.clear(); TF_CHECK_OK(GetNodeAttr(identity0_node->def(), kXlaControlDependenciesAttrName, &attr)); @@ -162,23 +153,33 @@ TEST(PreprocessForEncapsulationTest, ControlEdges) { TEST(PreprocessForEncapsulationTest, DataEdges) { // Build the graph: // "const_0" and "const_1" in host computation + // "identityn0" = ("const_0", "const_1") in host computation 0 // "add0" = "const_0" + "const_1" in XLA computation 0 // "add1" = "add0" + "const_0" in XLA computation 0 & outside compilation 0 // "identity0" = "add1" in XLA computation 0 // "add2" = "add1" + "identity0" in host computation // "add3" = "add1" + "add2" in XLA computation 1 - // "add4" = "identity0" + "add2" in XLA computation 1 & outside compilation 1 + // "add4" = "identity0" + "add2" in XLA computation 1 & outside compilation 0 + // "add5" = "identityn0"[0] + "identityn0"[1] in XLA computation 1 & + // outside compilation 0 + // "identityn1" = ("identityn0"[0], "identityn0"[1]) in XLA computation 1 & + // outside compilation 0 // "identity1" = "add4" in XLA computation 1 // "identity2" = "identity1" in host computation tensorflow::Scope s = tensorflow::Scope::NewRootScope(); Output const_0 = ops::Const(s.WithOpName("const_0"), 1, {}); Output const_1 = ops::Const(s.WithOpName("const_1"), 2, {}); + auto identityn0 = + ops::IdentityN(s.WithOpName("identityn_0"), {const_0, const_1}); Output add0 = ops::Add(s.WithOpName("add0"), const_0, const_1); Output add1 = ops::Add(s.WithOpName("add1"), add0, const_0); Output identity0 = ops::Identity(s.WithOpName("identity0"), add1); Output add2 = ops::Add(s.WithOpName("add2"), add1, identity0); Output add3 = ops::Add(s.WithOpName("add3"), add1, add2); Output add4 = ops::Add(s.WithOpName("add4"), identity0, add2); + Output add5 = ops::Add(s.WithOpName("add5"), identityn0[0], identityn0[1]); + auto identityn1 = ops::IdentityN(s.WithOpName("identityn_1"), + {identityn0[0], identityn0[1]}); Output identity1 = ops::Identity(s.WithOpName("identity1"), add4); Output identity2 = ops::Identity(s.WithOpName("identity2"), add4); Graph g(OpRegistry::Global()); @@ -189,6 +190,8 @@ TEST(PreprocessForEncapsulationTest, DataEdges) { Node *add0_node = node_index["add0"], *add1_node = node_index["add1"], *identity0_node = node_index["identity0"], *add3_node = node_index["add3"], *add4_node = node_index["add4"], + *add5_node = node_index["add5"], + *identityn1_node = node_index["identityn_1"], *identity1_node = node_index["identity1"]; add0_node->AddAttr("_xla", "0"); add1_node->AddAttr("_xla", "0"); @@ -197,6 +200,10 @@ TEST(PreprocessForEncapsulationTest, DataEdges) { add3_node->AddAttr("_xla", "1"); add4_node->AddAttr("_xla", "1"); add4_node->AddAttr("_oc", "0"); + add5_node->AddAttr("_xla", "1"); + add5_node->AddAttr("_oc", "0"); + identityn1_node->AddAttr("_xla", "1"); + identityn1_node->AddAttr("_oc", "0"); identity1_node->AddAttr("_xla", "1"); TF_CHECK_OK(PreprocessForEncapsulation(&g, "_xla", "_oc")); @@ -214,8 +221,9 @@ TEST(PreprocessForEncapsulationTest, DataEdges) { EXPECT_NE(bridge_identity0_add4, nullptr); // Step 3: add placeholder for edges between host computation and outside // compilation. - EXPECT_EQ(bridge_add1_add3->def().input(0), "add1_oc_to_host_placeholder"); - Node *add1_oc_to_host_placeholder = node_index["add1_oc_to_host_placeholder"]; + EXPECT_EQ(bridge_add1_add3->def().input(0), "add1_oc_to_host_placeholder_0"); + Node *add1_oc_to_host_placeholder = + node_index["add1_oc_to_host_placeholder_0"]; TF_CHECK_OK(GetNodeAttr(add1_oc_to_host_placeholder->attrs(), kOutsideCompilationToHostOriginalNodeAttrName, &str)); EXPECT_EQ(str, "add1"); @@ -226,15 +234,34 @@ TEST(PreprocessForEncapsulationTest, DataEdges) { add4_node = node_index["add4"]; ASSERT_NE(add4_node, nullptr); EXPECT_EQ(add4_node->def().input(0), - "bridge_identity0_add4_host_to_oc_placeholder"); + "bridge_identity0_add4_host_to_oc_placeholder_0"); Node *identity0_host_to_oc_placeholder = - node_index["bridge_identity0_add4_host_to_oc_placeholder"]; + node_index["bridge_identity0_add4_host_to_oc_placeholder_0"]; TF_CHECK_OK(GetNodeAttr(identity0_host_to_oc_placeholder->attrs(), kHostToOutsideCompilationOriginalNodeAttrName, &str)); EXPECT_EQ(str, "bridge_identity0_add4"); TF_CHECK_OK(GetNodeAttr(identity0_host_to_oc_placeholder->attrs(), kHostToOutsideCompilationSrcOutputAttrName, &i)); EXPECT_EQ(i, 0); + + // Check different placeholder nodes are created for different src_output. + Node *placeholder0 = node_index["identityn_0_host_to_oc_placeholder_0"], + *placeholder1 = node_index["identityn_0_host_to_oc_placeholder_1"]; + EXPECT_NE(placeholder0, nullptr); + EXPECT_NE(placeholder1, nullptr); + // Check we only have 2 placeholder nodes created for "identityn_0". + int placeholder_count = 0; + for (Node *n : g.nodes()) { + if (HasNodeAttr(n->def(), kHostToOutsideCompilationOriginalNodeAttrName)) { + string attr; + TF_CHECK_OK(GetNodeAttr( + n->attrs(), kHostToOutsideCompilationOriginalNodeAttrName, &attr)); + if (attr == "identityn_0") { + ++placeholder_count; + } + } + } + EXPECT_EQ(placeholder_count, 2); } TEST(PostprocessForEncapsulationTest, ControlEdges) { diff --git a/tensorflow/compiler/jit/encapsulate_xla_computations_pass.cc b/tensorflow/compiler/jit/encapsulate_xla_computations_pass.cc index 2ce6fa73fc..d334100aa4 100644 --- a/tensorflow/compiler/jit/encapsulate_xla_computations_pass.cc +++ b/tensorflow/compiler/jit/encapsulate_xla_computations_pass.cc @@ -195,8 +195,11 @@ Status RewriteSubgraph(const std::vector& arg_source_tensors, e->dst()->attrs().Find(kXlaClusterAttr) == nullptr && e->dst()->type_string() != kXlaClusterOutput) { return errors::InvalidArgument( - "Undeclared output of XLA computation. A common cause of this error " - "is variable initializers that depend on the XLA computation. Edge: ", + "Undeclared output of XLA computation. Some common causes of this " + "error are: 1) variable initializers that depend on the XLA " + "computation; 2) gradient computations that depend on the XLA " + "computation, which can be mitigated by moving gradient computations " + "inside XLA computation. Offending edge: ", e->src()->name(), ":", e->src_output(), " -> ", e->dst()->name(), ":", e->dst_input()); } diff --git a/tensorflow/compiler/jit/extract_outside_compilation_pass.cc b/tensorflow/compiler/jit/extract_outside_compilation_pass.cc index 8b3587c508..e3c7e2f89b 100644 --- a/tensorflow/compiler/jit/extract_outside_compilation_pass.cc +++ b/tensorflow/compiler/jit/extract_outside_compilation_pass.cc @@ -366,7 +366,7 @@ Status ReplaceOrRemoveOutsideCompilationCallNode( // replace this node with compilation result node. // 3) all outside compilation graphs. Status ConstructHostGraph( - const string& xla_cluster_name, + const string& xla_cluster_name, const string& outside_compilation_attr_name, const std::vector& outside_compilation_host_graphs, FunctionLibraryDefinition* fld, std::unique_ptr* host_graph) { host_graph->reset(new Graph(fld)); @@ -476,6 +476,10 @@ Status ConstructHostGraph( host_graph->get(), std::unordered_set{(*host_graph)->sink_node()}); + // Postprocess edges between different outside compilations. + TF_RETURN_IF_ERROR(PostprocessEdgesBetweenOutsideCompilations( + host_graph->get(), outside_compilation_attr_name)); + if (VLOG_IS_ON(4)) { dump_graph::DumpGraphToFile( absl::StrCat("extract_outside_compilation_host_graph_for_", @@ -801,6 +805,11 @@ Status ExtractOutsideCompilationForFunction( }, &fbody)); std::unique_ptr fbody_deleter(fbody); + + // Preprocess edges between different outside compilations. They will be + // restored in `ConstructHostGraph()`. + TF_RETURN_IF_ERROR(PreprocessEdgesBetweenOutsideCompilations( + fbody->graph, outside_compilation_attr_name)); if (VLOG_IS_ON(4)) { dump_graph::DumpGraphToFile( absl::StrCat("extract_outside_compilation_for_func_before_", func_name), @@ -860,8 +869,9 @@ Status ExtractOutsideCompilationForFunction( // Construct host graph. if (!outside_compilation_host_graphs.empty()) { - TF_RETURN_IF_ERROR(ConstructHostGraph( - xla_cluster_name, outside_compilation_host_graphs, fld, host_graph)); + TF_RETURN_IF_ERROR( + ConstructHostGraph(xla_cluster_name, outside_compilation_attr_name, + outside_compilation_host_graphs, fld, host_graph)); } // Remove the outside compilation graphs from function library. diff --git a/tensorflow/compiler/jit/extract_outside_compilation_pass_test.cc b/tensorflow/compiler/jit/extract_outside_compilation_pass_test.cc index c5bd64f004..bff956100d 100644 --- a/tensorflow/compiler/jit/extract_outside_compilation_pass_test.cc +++ b/tensorflow/compiler/jit/extract_outside_compilation_pass_test.cc @@ -290,21 +290,18 @@ TEST(ExtractOutsideCompilationForFunctionTest, Basic) { TF_CHECK_OK(GetNodeAttr(host_compute_1->attrs(), "shapes", &shapes)); EXPECT_EQ(shapes.size(), 1); EXPECT_EQ(shapes[0].dim_size(), 1); - // Check XlaHostCompute nodes' "shape_inference_graph" attr. "0" should have a - // non-empty value, and "1" should have an empty value. + // Check XlaHostCompute nodes' "shape_inference_graph" attr. Both should have + // empty values. string shape_inference_graph; TF_CHECK_OK(GetNodeAttr(host_compute_0->attrs(), "shape_inference_graph", &shape_inference_graph)); - EXPECT_EQ(shape_inference_graph, - "_outside_compilation_shape_inference_cluster_0"); + EXPECT_EQ(shape_inference_graph, ""); TF_CHECK_OK(GetNodeAttr(host_compute_1->attrs(), "shape_inference_graph", &shape_inference_graph)); EXPECT_EQ(shape_inference_graph, ""); // Check `shape_inference_graphs`. - EXPECT_EQ(shape_inference_graphs.size(), 1); - EXPECT_EQ(shape_inference_graphs[0], - "_outside_compilation_shape_inference_cluster_0"); + EXPECT_EQ(shape_inference_graphs.size(), 0); // Check `host_graph`: verify we have key placeholder and sequencer. Node *key_placeholder = nullptr, *sequencer = nullptr; @@ -333,8 +330,8 @@ TEST(ExtractOutsideCompilationForFunctionTest, Basic) { send_recv_nodes.push_back(n); } } - EXPECT_EQ(num_send_from_host, 2); - EXPECT_EQ(num_recv_at_host, 2); + EXPECT_EQ(num_send_from_host, 1); + EXPECT_EQ(num_recv_at_host, 1); for (Node *n : send_recv_nodes) { Node *input_node; TF_CHECK_OK(n->input_node(n->num_inputs() - 1, &input_node)); diff --git a/tensorflow/compiler/jit/flags.cc b/tensorflow/compiler/jit/flags.cc new file mode 100644 index 0000000000..98e344b3a0 --- /dev/null +++ b/tensorflow/compiler/jit/flags.cc @@ -0,0 +1,152 @@ +/* Copyright 2018 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 // NOLINT + +#include "tensorflow/compiler/jit/flags.h" +#include "tensorflow/compiler/xla/parse_flags_from_env.h" +#include "tensorflow/core/util/command_line_flags.h" + +namespace tensorflow { +namespace { + +BuildXlaOpsPassFlags* build_ops_flags; +DumpGraphFlags* dump_graph_flags; +MarkForCompilationPassFlags* mark_for_compilation_flags; +XlaDeviceFlags* device_flags; +XlaOpsCommonFlags* ops_flags; + +std::vector* flag_list; +std::once_flag flags_init; + +void AppendDumpGraphFlagsInternal(std::vector* flag_list) { + std::vector new_flags = { + Flag("tf_dump_graph_prefix", &dump_graph_flags->tf_dump_graph_prefix, + "Path prefix to which graphs dumped during debugging should be " + "written."), + }; + flag_list->insert(flag_list->end(), new_flags.begin(), new_flags.end()); +} + +void AppendMarkForCompilationPassFlagsInternal(std::vector* flag_list) { + std::vector new_flags = { + Flag("tf_xla_auto_jit", &mark_for_compilation_flags->tf_xla_auto_jit, + "Control compilation of operators into XLA computations on CPU and " + "GPU devices. 0 = use ConfigProto setting; -1 = off; 1 = on for " + "things very likely to be improved; 2 = on for everything. " + "Experimental."), + Flag("tf_xla_min_cluster_size", + &mark_for_compilation_flags->tf_xla_min_cluster_size, + "Minimum number of operators in an XLA compilation. Ignored for " + "operators placed on an XLA device or operators explicitly marked " + "for compilation."), + Flag("tf_xla_max_cluster_size", + &mark_for_compilation_flags->tf_xla_max_cluster_size, + "Maximum number of operators in an XLA compilation."), + Flag("tf_xla_clustering_debug", + &mark_for_compilation_flags->tf_xla_clustering_debug, + "Dump graphs during XLA compilation."), + Flag("tf_xla_cpu_global_jit", + &mark_for_compilation_flags->tf_xla_cpu_global_jit, + "Enables global JIT compilation for CPU via SessionOptions."), + Flag("tf_xla_clustering_fuel", + &mark_for_compilation_flags->tf_xla_clustering_fuel, + "Places an artificial limit on the number of ops marked as " + "eligible for clustering."), + Flag("tf_xla_fusion_only", + &mark_for_compilation_flags->tf_xla_fusion_only, + "enable fusion of element-wise operations only using XLA when " + "global_jit_level is ON*.")}; + flag_list->insert(flag_list->end(), new_flags.begin(), new_flags.end()); +} + +void AllocateAndParseFlags() { + build_ops_flags = new BuildXlaOpsPassFlags; + build_ops_flags->tf_xla_enable_lazy_compilation = true; + + dump_graph_flags = new DumpGraphFlags; + dump_graph_flags->tf_dump_graph_prefix = "/tmp/"; + + mark_for_compilation_flags = new MarkForCompilationPassFlags; + mark_for_compilation_flags->tf_xla_auto_jit = 0; + mark_for_compilation_flags->tf_xla_min_cluster_size = 2; + mark_for_compilation_flags->tf_xla_max_cluster_size = + std::numeric_limits::max(); + mark_for_compilation_flags->tf_xla_clustering_debug = false; + mark_for_compilation_flags->tf_xla_cpu_global_jit = false; + mark_for_compilation_flags->tf_xla_clustering_fuel = + std::numeric_limits::max(); + mark_for_compilation_flags->tf_xla_fusion_only = false; + + device_flags = new XlaDeviceFlags; + device_flags->tf_xla_compile_on_demand = false; + + ops_flags = new XlaOpsCommonFlags; + ops_flags->tf_xla_always_defer_compilation = false; + + flag_list = new std::vector({ + Flag("tf_xla_enable_lazy_compilation", + &build_ops_flags->tf_xla_enable_lazy_compilation, ""), + + Flag("tf_xla_compile_on_demand", &device_flags->tf_xla_compile_on_demand, + "Switch a device into 'on-demand' mode, where instead of " + "autoclustering ops are compiled one by one just-in-time."), + + Flag("tf_xla_always_defer_compilation", + &ops_flags->tf_xla_always_defer_compilation, ""), + }); + AppendDumpGraphFlagsInternal(flag_list); + AppendMarkForCompilationPassFlagsInternal(flag_list); + xla::ParseFlagsFromEnvAndDieIfUnknown("TF_XLA_FLAGS", *flag_list); +} + +} // namespace + +const BuildXlaOpsPassFlags& GetBuildXlaOpsPassFlags() { + std::call_once(flags_init, &AllocateAndParseFlags); + return *build_ops_flags; +} + +DumpGraphFlags* GetDumpGraphFlags() { + std::call_once(flags_init, &AllocateAndParseFlags); + return dump_graph_flags; +} + +MarkForCompilationPassFlags* GetMarkForCompilationPassFlags() { + std::call_once(flags_init, &AllocateAndParseFlags); + return mark_for_compilation_flags; +} + +XlaDeviceFlags* GetXlaDeviceFlags() { + std::call_once(flags_init, &AllocateAndParseFlags); + return device_flags; +} + +const XlaOpsCommonFlags& GetXlaOpsCommonFlags() { + std::call_once(flags_init, &AllocateAndParseFlags); + return *ops_flags; +} + +void AppendMarkForCompilationPassFlags(std::vector* flag_list) { + std::call_once(flags_init, &AllocateAndParseFlags); + AppendMarkForCompilationPassFlagsInternal(flag_list); +} + +void AppendDumpGraphFlags(std::vector* flag_list) { + std::call_once(flags_init, &AllocateAndParseFlags); + AppendDumpGraphFlagsInternal(flag_list); +} + +} // namespace tensorflow diff --git a/tensorflow/compiler/jit/legacy_flags/mark_for_compilation_pass_flags.h b/tensorflow/compiler/jit/flags.h similarity index 57% rename from tensorflow/compiler/jit/legacy_flags/mark_for_compilation_pass_flags.h rename to tensorflow/compiler/jit/flags.h index 79b47357a1..5ddea588ee 100644 --- a/tensorflow/compiler/jit/legacy_flags/mark_for_compilation_pass_flags.h +++ b/tensorflow/compiler/jit/flags.h @@ -13,10 +13,8 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#ifndef TENSORFLOW_COMPILER_JIT_LEGACY_FLAGS_MARK_FOR_COMPILATION_PASS_FLAGS_H_ -#define TENSORFLOW_COMPILER_JIT_LEGACY_FLAGS_MARK_FOR_COMPILATION_PASS_FLAGS_H_ - -// Legacy flags for the XLA bridge's mark_for_compilation_pass module. +#ifndef TENSORFLOW_COMPILER_JIT_FLAGS_H_ +#define TENSORFLOW_COMPILER_JIT_FLAGS_H_ #include @@ -24,15 +22,8 @@ limitations under the License. #include "tensorflow/core/util/command_line_flags.h" namespace tensorflow { -namespace legacy_flags { - -// Append to *flag_list flag definitions associated with the XLA bridge's -// mark_for_compilation_pass module. -void AppendMarkForCompilationPassFlags( - std::vector* flag_list); -// The values of flags associated with the XLA bridge's -// mark_for_compilation_pass module. +// Flags associated with the XLA bridge's mark_for_compilation_pass module. struct MarkForCompilationPassFlags { int32 tf_xla_auto_jit; // Control compilation of operators into XLA // computations on CPU and GPU devices. 0 = use @@ -57,12 +48,56 @@ struct MarkForCompilationPassFlags { // only using XLA. }; -// Return a pointer to the MarkForCompilationPassFlags struct; +// Flags associated with the XLA bridge's xla_device module. +struct XlaDeviceFlags { + // Switch the CPU device into "on-demand" mode, where instead of + // autoclustering ops are compiled one by one just-in-time. + // Enabling this mode by a legacy flag is a temporary mechanism. When this + // feature is battle-tested, we will switch this to be a session option. + bool tf_xla_compile_on_demand; +}; + +// Flags common to the _Xla* ops and their kernels. +struct XlaOpsCommonFlags { + // If true, _XlaCompile always refuses to compile the cluster, which means the + // XLA clusters always run in the TF executor. Defaults to false. + bool tf_xla_always_defer_compilation; +}; + +// Flags for the build_xla_ops pass. +struct BuildXlaOpsPassFlags { + // Enables lazy compilation for TF/XLA (only when auto-clustering) if true. + // Defaults to true. + bool tf_xla_enable_lazy_compilation; +}; + +// Flags for the XLA bridge's dump_graph module. +struct DumpGraphFlags { + // Path prefix to which graphs dumped during debugging should be written. + string tf_dump_graph_prefix; +}; + +// Return a pointer to the DumpGraphFlags struct; // repeated calls return the same pointer. // This should be called only after Flags::Parse() has returned. + +// Getters for flags structs defined above. The first call to any of these +// parses TF_XLA_FLAGS for all of them. Those functions which return a pointer +// always return the same pointer. MarkForCompilationPassFlags* GetMarkForCompilationPassFlags(); +const BuildXlaOpsPassFlags& GetBuildXlaOpsPassFlags(); +XlaDeviceFlags* GetXlaDeviceFlags(); +const XlaOpsCommonFlags& GetXlaOpsCommonFlags(); +DumpGraphFlags* GetDumpGraphFlags(); + +// Appends the flag definitions associated with +// MarkForCompilationPassFlags/DumpGraphFlags to `flag_list`. +// +// Has the side-effect of parsing TF_XLA_FLAGS if that hasn't happened yet. +void AppendMarkForCompilationPassFlags( + std::vector* flag_list); +void AppendDumpGraphFlags(std::vector* flag_list); -} // namespace legacy_flags } // namespace tensorflow -#endif // TENSORFLOW_COMPILER_JIT_LEGACY_FLAGS_MARK_FOR_COMPILATION_PASS_FLAGS_H_ +#endif // TENSORFLOW_COMPILER_JIT_FLAGS_H_ diff --git a/tensorflow/compiler/jit/increase_dynamism_for_auto_jit_pass.cc b/tensorflow/compiler/jit/increase_dynamism_for_auto_jit_pass.cc index d984ca15cb..ce53f70b79 100644 --- a/tensorflow/compiler/jit/increase_dynamism_for_auto_jit_pass.cc +++ b/tensorflow/compiler/jit/increase_dynamism_for_auto_jit_pass.cc @@ -23,7 +23,7 @@ limitations under the License. #include "tensorflow/cc/ops/array_ops.h" #include "tensorflow/cc/ops/const_op.h" #include "tensorflow/cc/ops/math_ops.h" -#include "tensorflow/compiler/jit/legacy_flags/mark_for_compilation_pass_flags.h" +#include "tensorflow/compiler/jit/flags.h" #include "tensorflow/compiler/jit/xla_cluster_util.h" #include "tensorflow/compiler/tf2xla/cc/ops/xla_ops.h" #include "tensorflow/compiler/tf2xla/dump_graph.h" @@ -208,8 +208,12 @@ Status ComputeSliceSize(const Scope& host_scope, DCHECK_EQ(slice_size.back().type(), DT_INT64); } - *size = ops::Concat(host_scope.WithOpName("slice_size"), slice_size, - ops::Const(host_scope.WithOpName("concat_axis"), 0)); + // Trivial ConcatV2 nodes (with exactly one input) are disallowed. + *size = + slice_size.size() == 1 + ? slice_size[0] + : ops::Concat(host_scope.WithOpName("slice_size"), slice_size, + ops::Const(host_scope.WithOpName("concat_axis"), 0)); return Status::OK(); } @@ -242,6 +246,9 @@ Status ConvertTensorFlowSliceToStaticShapedSlice( .WithOpName("static_shaped_slice"), slice_inputs_int64.input, slice_inputs_int64.begin, slice_size) .node(); + + TF_RETURN_IF_ERROR(main_scope.status()); + std::vector compile_time_const_inputs; compile_time_const_inputs.push_back("size"); (*result)->AddAttr(kXlaCompileTimeConstantInputsAttr, @@ -284,49 +291,45 @@ Status RewriteSlice(Graph* g, Node* slice, const SliceInputs& slice_inputs, return Status::OK(); } -// If `n` is a slice we can rewrite to have a static shape (i.e. have the output -// shape only depend on the "size" input) then returns the a SliceInputs -// representing the inputs to `n`. Otherwise returns nullopt. -StatusOrOptional IsRewritableSlice(Node* n) { +// Return true if `n` is a slice we can rewrite to have a static shape +// (i.e. have the output shape only depend on the "size" input). +xla::StatusOr IsRewritableSlice(Node* n) { if (n->type_string() != "Slice") { - return {absl::nullopt}; + return false; } if (!GetXlaClusterForNode(*n).has_value()) { // There is no need to change slice ops outside XLA clusters. - return {absl::nullopt}; + return false; } TF_ASSIGN_OR_RETURN(absl::optional slice_inputs, GetSliceInputs(n)); if (!slice_inputs.has_value()) { - return {absl::nullopt}; + return false; } // If slice_size[i] < -1 for any i then executing the slice will throw an // error, and we don't do anything here. - bool slice_is_ok = absl::c_all_of(slice_inputs->size_as_vector, - [](int64 size_i) { return size_i >= -1; }); - if (!slice_is_ok) { - return {absl::nullopt}; - } - - return slice_inputs; + return absl::c_all_of(slice_inputs->size_as_vector, + [](int64 size_i) { return size_i >= -1; }); } Status FindAndRewriteSlices(Graph* g, bool* changed) { - std::vector> slices_to_rewrite; + std::vector slices_to_rewrite; for (Node* n : g->nodes()) { - TF_ASSIGN_OR_RETURN(absl::optional slice_inputs, - IsRewritableSlice(n)); - if (slice_inputs.has_value()) { - slices_to_rewrite.push_back({n, std::move(*slice_inputs)}); + TF_ASSIGN_OR_RETURN(bool is_rewritable, IsRewritableSlice(n)); + if (is_rewritable) { + slices_to_rewrite.push_back(n); } } - for (const auto& pair : slices_to_rewrite) { - TF_RETURN_IF_ERROR(RewriteSlice(g, pair.first, pair.second, - *GetXlaClusterForNode(*pair.first))); + for (Node* n : slices_to_rewrite) { + TF_ASSIGN_OR_RETURN(absl::optional slice_inputs, + GetSliceInputs(n)); + TF_RET_CHECK(slice_inputs.has_value()); + TF_RETURN_IF_ERROR( + RewriteSlice(g, n, *slice_inputs, *GetXlaClusterForNode(*n))); } if (!slices_to_rewrite.empty()) { @@ -342,8 +345,7 @@ Status FindAndRewriteSlices(Graph* g, bool* changed) { Status IncreaseDynamismForAutoJitPass::Run( const GraphOptimizationPassOptions& options) { - legacy_flags::MarkForCompilationPassFlags* flags = - legacy_flags::GetMarkForCompilationPassFlags(); + MarkForCompilationPassFlags* flags = GetMarkForCompilationPassFlags(); if (flags->tf_xla_clustering_debug) { dump_graph::DumpGraphToFile("before_increase_dynamism_for_auto_jit_pass", **options.graph, options.flib_def); diff --git a/tensorflow/compiler/jit/increase_dynamism_for_auto_jit_pass_test.cc b/tensorflow/compiler/jit/increase_dynamism_for_auto_jit_pass_test.cc index 0f6f612e96..a2f1b831ad 100644 --- a/tensorflow/compiler/jit/increase_dynamism_for_auto_jit_pass_test.cc +++ b/tensorflow/compiler/jit/increase_dynamism_for_auto_jit_pass_test.cc @@ -27,6 +27,7 @@ limitations under the License. namespace tensorflow { namespace { +using ::testing::_; using testing::matchers::AssignedDevice; using testing::matchers::Attr; using testing::matchers::Const; @@ -142,6 +143,26 @@ TEST(SliceToDynamicSliceRewriteTest, Basic) { EXPECT_THAT(static_shaped_slice, m_dynamic_slice); } +TEST(SliceToDynamicSliceRewriteTest, SliceFromVector) { + Scope root = Scope::NewRootScope() + .ExitOnError() + .WithAssignedDevice(kDeviceName) + .WithXlaCluster("cluster_0"); + + Output input = ops::Placeholder(root.WithOpName("input"), DT_FLOAT); + Output begin = ops::Placeholder(root.WithOpName("begin"), DT_INT32); + Output size = ops::Const(root.WithOpName("size"), {-1}); + Output slice = ops::Slice(root.WithOpName("slice"), input, begin, size); + + std::unique_ptr result; + TF_ASSERT_OK(IncreaseDynamismForAutoJit(root, &result)); + + Node* static_shaped_slice = testing::FindNodeByName( + result.get(), "slice/static_shaped_slice/static_shaped_slice"); + EXPECT_NE(static_shaped_slice, nullptr); + EXPECT_THAT(result->nodes(), Not(Contains(NodeWith(Op("ConcatV2"))))); +} + TEST(SliceToDynamicSliceRewriteTest, ControlDependencePreserved) { Scope root = Scope::NewRootScope() .ExitOnError() @@ -166,18 +187,18 @@ TEST(SliceToDynamicSliceRewriteTest, ControlDependencePreserved) { CtrlDeps(NodeWith(Op("Placeholder"), Name("control"))))); } +int64 ToInt64(int v) { return static_cast(v); } + TEST(SliceToDynamicSliceRewriteTest, Int64Indices) { Scope root = Scope::NewRootScope() .ExitOnError() .WithAssignedDevice(kDeviceName) .WithXlaCluster("cluster_0"); - auto to_int64 = [](int v) { return static_cast(v); }; - Output input = ops::Placeholder(root.WithOpName("input"), DT_FLOAT); Output begin = ops::Placeholder(root.WithOpName("begin"), DT_INT64); Output size = - ops::Const(root.WithOpName("size"), {to_int64(-1), to_int64(500)}); + ops::Const(root.WithOpName("size"), {ToInt64(-1), ToInt64(500)}); Output slice = ops::Slice(root.WithOpName("slice"), input, begin, size); std::unique_ptr result; @@ -252,13 +273,35 @@ TEST(SliceToDynamicSliceRewriteTest, DontRewriteSliceWithNonConstSize) { Attr(kXlaCompileTimeConstantInputsAttr))))); } +TEST(SliceToDynamicSliceRewriteTest, ScalarSlice) { + Scope root = Scope::NewRootScope() + .ExitOnError() + .WithAssignedDevice(kDeviceName) + .WithXlaCluster("cluster_0"); + + Output input = ops::Placeholder(root.WithOpName("input"), DT_FLOAT); + Output begin = ops::Placeholder(root.WithOpName("begin"), DT_INT64); + Output size = ops::Const(root.WithOpName("size"), {}); + Output slice = ops::Slice(root.WithOpName("slice"), input, begin, size); + + std::unique_ptr result; + TF_ASSERT_OK(IncreaseDynamismForAutoJit(root, &result)); + + Node* static_shaped_slice = testing::FindNodeByName( + result.get(), "slice/static_shaped_slice/static_shaped_slice"); + ASSERT_NE(static_shaped_slice, nullptr); + EXPECT_THAT(static_shaped_slice, + NodeWith(Op("Slice"), Attr(kXlaCompileTimeConstantInputsAttr), + Inputs(_, _, Out(NodeWith(Name(size.node()->name())))))); +} + TEST(SliceToDynamicSliceRewriteTest, IndicesNotVector) { Scope root = Scope::NewRootScope() .ExitOnError() .WithAssignedDevice(kDeviceName) .WithXlaCluster("cluster_0"); - auto to_int64 = [](int v) { return static_cast(v); }; + auto ToInt64 = [](int v) { return static_cast(v); }; Output input = ops::Placeholder(root.WithOpName("input"), DT_FLOAT); Output begin = ops::Placeholder(root.WithOpName("begin"), DT_INT64); @@ -271,7 +314,7 @@ TEST(SliceToDynamicSliceRewriteTest, IndicesNotVector) { ops::Slice(root.WithOpName("slice"), input, begin, size_placeholder); Output size = - ops::Const(root.WithOpName("size"), {{to_int64(-1)}, {to_int64(500)}}); + ops::Const(root.WithOpName("size"), {{ToInt64(-1)}, {ToInt64(500)}}); TF_ASSERT_OK(root.graph()->UpdateEdge(size.node(), 0, slice.node(), 2)); std::unique_ptr result; @@ -281,5 +324,82 @@ TEST(SliceToDynamicSliceRewriteTest, IndicesNotVector) { Not(Contains(NodeWith(Op("Slice"), Attr(kXlaCompileTimeConstantInputsAttr))))); } + +TEST(SliceToDynamicSliceRewriteTest, SliceWithSliceInput) { + Scope root = Scope::NewRootScope() + .ExitOnError() + .WithAssignedDevice(kDeviceName) + .WithXlaCluster("cluster_0"); + + Output input = ops::Placeholder(root.WithOpName("input"), DT_FLOAT); + Output begin = ops::Placeholder(root.WithOpName("begin"), DT_INT32); + Output size_a = ops::Const(root.WithOpName("size_a"), {-1, 500}); + Output slice = ops::Slice(root.WithOpName("slice"), input, begin, size_a); + + Output size_b = ops::Const(root.WithOpName("size_a"), {-1, 200}); + Output slice_with_slice_input = ops::Slice( + root.WithOpName("slice_with_slice_input"), slice, begin, size_b); + + std::unique_ptr result; + TF_ASSERT_OK(IncreaseDynamismForAutoJit(root, &result)); + + Node* static_shaped_slice = testing::FindNodeByName( + result.get(), + "slice_with_slice_input/static_shaped_slice/static_shaped_slice"); + ASSERT_NE(static_shaped_slice, nullptr); + EXPECT_EQ(static_shaped_slice->output_type(0), DT_FLOAT) + << "Expected DT_FLOAT, was " + << DataType_Name(static_shaped_slice->output_type(0)); + EXPECT_THAT( + static_shaped_slice, + NodeWith( + Op("Slice"), + Inputs(Out(NodeWith( + Op("Slice"), + Name("slice/static_shaped_slice/static_shaped_slice"))), + _, _))); +} + +TEST(SliceToDynamicSliceRewriteTest, SliceWithSliceBegin) { + Scope root = Scope::NewRootScope() + .ExitOnError() + .WithAssignedDevice(kDeviceName) + .WithXlaCluster("cluster_0"); + + Output input_float = + ops::Placeholder(root.WithOpName("input_float"), DT_FLOAT); + Output input_i64 = ops::Placeholder(root.WithOpName("input_i64"), DT_INT64); + + Output begin_begin = + ops::Placeholder(root.WithOpName("begin_begin"), DT_INT32); + Output begin_size = ops::Const(root.WithOpName("begin_size"), {-1}); + Output begin = + ops::Slice(root.WithOpName("begin"), input_i64, begin_begin, begin_size); + + Output size = + ops::Const(root.WithOpName("size"), {ToInt64(-1), ToInt64(200)}); + Output slice_with_slice_begin = ops::Slice( + root.WithOpName("slice_with_slice_begin"), input_float, begin, size); + + std::unique_ptr result; + TF_ASSERT_OK(IncreaseDynamismForAutoJit(root, &result)); + + Node* static_shaped_slice = testing::FindNodeByName( + result.get(), + "slice_with_slice_begin/static_shaped_slice/static_shaped_slice"); + ASSERT_NE(static_shaped_slice, nullptr); + EXPECT_EQ(static_shaped_slice->output_type(0), DT_FLOAT) + << "Expected DT_FLOAT, was " + << DataType_Name(static_shaped_slice->output_type(0)); + EXPECT_THAT( + static_shaped_slice, + NodeWith( + Op("Slice"), + Inputs(_, + Out(NodeWith( + Op("Slice"), + Name("begin/static_shaped_slice/static_shaped_slice"))), + _))); +} } // namespace } // namespace tensorflow diff --git a/tensorflow/compiler/jit/kernels/BUILD b/tensorflow/compiler/jit/kernels/BUILD index 830db9ebdd..0583774714 100644 --- a/tensorflow/compiler/jit/kernels/BUILD +++ b/tensorflow/compiler/jit/kernels/BUILD @@ -12,10 +12,10 @@ cc_library( hdrs = ["xla_ops.h"], deps = [ "//tensorflow/compiler/jit:common", + "//tensorflow/compiler/jit:flags", "//tensorflow/compiler/jit:xla_compilation_cache", "//tensorflow/compiler/jit:xla_device", "//tensorflow/compiler/jit:xla_launch_util", - "//tensorflow/compiler/jit/legacy_flags:xla_ops_common_flags", "//tensorflow/compiler/tf2xla:common", "//tensorflow/compiler/tf2xla:tf2xla_util", "//tensorflow/compiler/tf2xla:xla_compiler", diff --git a/tensorflow/compiler/jit/kernels/xla_ops.cc b/tensorflow/compiler/jit/kernels/xla_ops.cc index 055de7afcc..ad71df5a69 100644 --- a/tensorflow/compiler/jit/kernels/xla_ops.cc +++ b/tensorflow/compiler/jit/kernels/xla_ops.cc @@ -18,7 +18,7 @@ limitations under the License. #include "absl/container/flat_hash_map.h" #include "absl/memory/memory.h" #include "tensorflow/compiler/jit/defs.h" -#include "tensorflow/compiler/jit/legacy_flags/xla_ops_common_flags.h" +#include "tensorflow/compiler/jit/flags.h" #include "tensorflow/compiler/tf2xla/shape_util.h" #include "tensorflow/compiler/tf2xla/tf2xla_util.h" #include "tensorflow/compiler/tf2xla/xla_compiler.h" @@ -418,7 +418,7 @@ void XlaCompileOp::Compute(OpKernelContext* ctx) { cannot_compile_cluster = cannot_compile_cluster_; } - if (legacy_flags::GetXlaOpsCommonFlags().tf_xla_always_defer_compilation || + if (GetXlaOpsCommonFlags().tf_xla_always_defer_compilation || cannot_compile_cluster) { executable = nullptr; } else { diff --git a/tensorflow/compiler/jit/legacy_flags/BUILD b/tensorflow/compiler/jit/legacy_flags/BUILD deleted file mode 100644 index 5fa6c85f06..0000000000 --- a/tensorflow/compiler/jit/legacy_flags/BUILD +++ /dev/null @@ -1,65 +0,0 @@ -# Legacy command line flags for the XLA bridge libraries. - -# Please do not add more flags to this package. - -# The XLA bridge libraries were written in an environment that allowed -# command-line flags to be scattered freely throughout the libraries. This -# model, while initially convenient, leads to a proliferation in unused command -# line flags in tests and binaries, and serious problems in servers, where one -# might wish parameters to be different in independent RPC calls to the same -# routine. -# -# Please don't add more flags. If you're a library author, pass options and -# parameters explicitly through the library's interface. - -licenses(["notice"]) # Apache 2.0 - -package(default_visibility = ["//tensorflow:internal"]) - -cc_library( - name = "mark_for_compilation_pass_flags", - srcs = ["mark_for_compilation_pass_flags.cc"], - hdrs = ["mark_for_compilation_pass_flags.h"], - deps = - [ - "//tensorflow/compiler/xla:parse_flags_from_env", - "//tensorflow/core:framework_internal", - "//tensorflow/core:lib", - ], -) - -cc_library( - name = "xla_device_flags", - srcs = ["xla_device_flags.cc"], - hdrs = ["xla_device_flags.h"], - deps = - [ - "//tensorflow/compiler/xla:parse_flags_from_env", - "//tensorflow/core:framework_internal", - "//tensorflow/core:lib", - ], -) - -cc_library( - name = "build_xla_ops_pass_flags", - srcs = ["build_xla_ops_pass_flags.cc"], - hdrs = ["build_xla_ops_pass_flags.h"], - deps = - [ - "//tensorflow/compiler/xla:parse_flags_from_env", - "//tensorflow/core:framework_internal", - "//tensorflow/core:lib", - ], -) - -cc_library( - name = "xla_ops_common_flags", - srcs = ["xla_ops_common_flags.cc"], - hdrs = ["xla_ops_common_flags.h"], - deps = - [ - "//tensorflow/compiler/xla:parse_flags_from_env", - "//tensorflow/core:framework_internal", - "//tensorflow/core:lib", - ], -) diff --git a/tensorflow/compiler/jit/legacy_flags/build_xla_ops_pass_flags.cc b/tensorflow/compiler/jit/legacy_flags/build_xla_ops_pass_flags.cc deleted file mode 100644 index 961c17c17e..0000000000 --- a/tensorflow/compiler/jit/legacy_flags/build_xla_ops_pass_flags.cc +++ /dev/null @@ -1,47 +0,0 @@ -/* Copyright 2018 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 // NOLINT - -#include "tensorflow/compiler/jit/legacy_flags/build_xla_ops_pass_flags.h" -#include "tensorflow/compiler/xla/parse_flags_from_env.h" -#include "tensorflow/core/util/command_line_flags.h" - -namespace tensorflow { -namespace legacy_flags { -namespace { - -BuildXlaOpsPassFlags* flags; -std::vector* flag_list; -std::once_flag flags_init; - -void AllocateAndParseFlags() { - flags = new BuildXlaOpsPassFlags; - flags->tf_xla_enable_lazy_compilation = true; - flag_list = new std::vector({ - Flag("tf_xla_enable_lazy_compilation", - &flags->tf_xla_enable_lazy_compilation, ""), - }); - xla::ParseFlagsFromEnv(*flag_list); -} - -} // namespace - -const BuildXlaOpsPassFlags& GetBuildXlaOpsPassFlags() { - std::call_once(flags_init, &AllocateAndParseFlags); - return *flags; -} -} // namespace legacy_flags -} // namespace tensorflow diff --git a/tensorflow/compiler/jit/legacy_flags/build_xla_ops_pass_flags.h b/tensorflow/compiler/jit/legacy_flags/build_xla_ops_pass_flags.h deleted file mode 100644 index 9aa5cf64d6..0000000000 --- a/tensorflow/compiler/jit/legacy_flags/build_xla_ops_pass_flags.h +++ /dev/null @@ -1,37 +0,0 @@ -/* Copyright 2018 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_COMPILER_JIT_LEGACY_FLAGS_BUILD_XLA_OPS_PASS_FLAGS_H_ -#define TENSORFLOW_COMPILER_JIT_LEGACY_FLAGS_BUILD_XLA_OPS_PASS_FLAGS_H_ - -namespace tensorflow { -namespace legacy_flags { - -// Flags for the build_xla_ops pass. -struct BuildXlaOpsPassFlags { - // Enables lazy compilation for TF/XLA (only when auto-clustering) if true. - // Defaults to true. - bool tf_xla_enable_lazy_compilation; -}; - -// Parses the flags in BuildXlaOpsPassFlags from the TF_XLA_FLAGS environment -// variable and returns a reference to the parsed copy. Parses TF_XLA_FLAGS -// only the first time this routine is called. -const BuildXlaOpsPassFlags& GetBuildXlaOpsPassFlags(); - -} // namespace legacy_flags -} // namespace tensorflow - -#endif // TENSORFLOW_COMPILER_JIT_LEGACY_FLAGS_BUILD_XLA_OPS_PASS_FLAGS_H_ diff --git a/tensorflow/compiler/jit/legacy_flags/mark_for_compilation_pass_flags.cc b/tensorflow/compiler/jit/legacy_flags/mark_for_compilation_pass_flags.cc deleted file mode 100644 index bad306e0b0..0000000000 --- a/tensorflow/compiler/jit/legacy_flags/mark_for_compilation_pass_flags.cc +++ /dev/null @@ -1,98 +0,0 @@ -/* 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. -==============================================================================*/ - -// Legacy flags for the XLA bridge's mark_for_compilation_pass module. - -#include -#include - -#include "tensorflow/compiler/jit/legacy_flags/mark_for_compilation_pass_flags.h" -#include "tensorflow/compiler/xla/parse_flags_from_env.h" -#include "tensorflow/core/platform/logging.h" -#include "tensorflow/core/platform/types.h" -#include "tensorflow/core/util/command_line_flags.h" - -namespace tensorflow { -namespace legacy_flags { - -// Pointers to the parsed value of the flags and flag descriptors, initialized -// via flags_init. -static MarkForCompilationPassFlags* flags; -static std::vector* flag_list; -static std::once_flag flags_init; - -// Allocate *flags. Called via call_once(&flags_init,...). -static void AllocateFlags() { - flags = new MarkForCompilationPassFlags; - flags->tf_xla_auto_jit = 0; - flags->tf_xla_min_cluster_size = 2; - flags->tf_xla_max_cluster_size = std::numeric_limits::max(); - flags->tf_xla_clustering_debug = false; - flags->tf_xla_cpu_global_jit = false; - flags->tf_xla_clustering_fuel = std::numeric_limits::max(); - flags->tf_xla_fusion_only = false; - flag_list = new std::vector( - {Flag("tf_xla_auto_jit", &flags->tf_xla_auto_jit, - "Control compilation of operators into XLA computations on CPU and " - "GPU devices. 0 = use ConfigProto setting; -1 = off; 1 = on for " - "things very likely to be improved; 2 = on for everything. " - "Experimental."), - Flag("tf_xla_min_cluster_size", &flags->tf_xla_min_cluster_size, - "Minimum number of operators in an XLA compilation. Ignored for " - "operators placed on an XLA device or operators explicitly marked " - "for compilation."), - Flag("tf_xla_max_cluster_size", &flags->tf_xla_max_cluster_size, - "Maximum number of operators in an XLA compilation."), - Flag("tf_xla_clustering_debug", &flags->tf_xla_clustering_debug, - "Dump graphs during XLA compilation."), - Flag("tf_xla_cpu_global_jit", &flags->tf_xla_cpu_global_jit, - "Enables global JIT compilation for CPU via SessionOptions."), - Flag("tf_xla_clustering_fuel", &flags->tf_xla_clustering_fuel, - "Places an artificial limit on the number of ops marked as " - "eligible for clustering."), - Flag("tf_xla_fusion_only", &flags->tf_xla_fusion_only, - "enable fusion of element-wise operations only using XLA when " - "global_jit_level is ON*.")}); - xla::ParseFlagsFromEnv(*flag_list); - - if (VLOG_IS_ON(1)) { - VLOG(1) << "Parsed MarkForCompilationPassFlags:"; - VLOG(1) << " tf_xla_auto_jit = " << flags->tf_xla_auto_jit; - VLOG(1) << " tf_xla_min_cluster_size = " << flags->tf_xla_min_cluster_size; - VLOG(1) << " tf_xla_max_cluster_size = " << flags->tf_xla_max_cluster_size; - VLOG(1) << " tf_xla_clustering_debug = " << flags->tf_xla_clustering_debug; - VLOG(1) << " tf_xla_cpu_global_jit = " << flags->tf_xla_cpu_global_jit; - VLOG(1) << " tf_xla_clustering_fuel = " << flags->tf_xla_clustering_fuel; - VLOG(1) << " tf_xla_fusion_only = " << flags->tf_xla_fusion_only; - } -} - -// Append to *append_to flag definitions associated with the XLA bridge's -// mark_for_compilation_pass module. -void AppendMarkForCompilationPassFlags(std::vector* append_to) { - std::call_once(flags_init, &AllocateFlags); - append_to->insert(append_to->end(), flag_list->begin(), flag_list->end()); -} - -// Return a pointer to the MarkForCompilationPassFlags struct; -// repeated calls return the same pointer. -// This should be called only after Flags::Parse() has returned. -MarkForCompilationPassFlags* GetMarkForCompilationPassFlags() { - std::call_once(flags_init, &AllocateFlags); - return flags; -} - -} // namespace legacy_flags -} // namespace tensorflow diff --git a/tensorflow/compiler/jit/legacy_flags/xla_device_flags.cc b/tensorflow/compiler/jit/legacy_flags/xla_device_flags.cc deleted file mode 100644 index 76b80d3034..0000000000 --- a/tensorflow/compiler/jit/legacy_flags/xla_device_flags.cc +++ /dev/null @@ -1,56 +0,0 @@ -/* Copyright 2018 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. -==============================================================================*/ - -// Legacy flags for the XLA bridge's xla_device module. - -#include -#include - -#include "tensorflow/compiler/jit/legacy_flags/xla_device_flags.h" -#include "tensorflow/compiler/xla/parse_flags_from_env.h" -#include "tensorflow/core/platform/types.h" -#include "tensorflow/core/util/command_line_flags.h" - -namespace tensorflow { -namespace legacy_flags { - -// Pointers to the parsed value of the flags and flag descriptors, initialized -// via flags_init. -static XlaDeviceFlags* flags; -static std::vector* flag_list; -static std::once_flag flags_init; - -// Allocate *flags. Called via call_once(&flags_init,...). -static void AllocateFlags() { - flags = new XlaDeviceFlags; - flags->tf_xla_compile_on_demand = false; - flag_list = new std::vector({ - Flag("tf_xla_compile_on_demand", &flags->tf_xla_compile_on_demand, - "Switch a device into 'on-demand' mode, where instead of " - "autoclustering ops are compiled one by one just-in-time."), - }); - xla::ParseFlagsFromEnv(*flag_list); -} - -// Return a pointer to the XlaDeviceFlags struct; -// repeated calls return the same pointer. -// This should be called only after Flags::Parse() has returned. -XlaDeviceFlags* GetXlaDeviceFlags() { - std::call_once(flags_init, &AllocateFlags); - return flags; -} - -} // namespace legacy_flags -} // namespace tensorflow diff --git a/tensorflow/compiler/jit/legacy_flags/xla_device_flags.h b/tensorflow/compiler/jit/legacy_flags/xla_device_flags.h deleted file mode 100644 index 27b22121ac..0000000000 --- a/tensorflow/compiler/jit/legacy_flags/xla_device_flags.h +++ /dev/null @@ -1,47 +0,0 @@ -/* Copyright 2018 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_COMPILER_JIT_LEGACY_FLAGS_XLA_DEVICE_FLAGS_H_ -#define TENSORFLOW_COMPILER_JIT_LEGACY_FLAGS_XLA_DEVICE_FLAGS_H_ - -// Legacy flags for the XLA bridge's xla_device module. - -#include - -#include "tensorflow/core/platform/types.h" -#include "tensorflow/core/util/command_line_flags.h" - -namespace tensorflow { -namespace legacy_flags { - -// The values of flags associated with the XLA bridge's -// xla_device module. -typedef struct { - // Switch the CPU device into "on-demand" mode, where instead of - // autoclustering ops are compiled one by one just-in-time. - // Enabling this mode by a legacy flag is a temporary mechanism. When this - // feature is battle-tested, we will switch this to be a session option. - bool tf_xla_compile_on_demand; -} XlaDeviceFlags; - -// Return a pointer to the XlaDeviceFlags struct; -// repeated calls return the same pointer. -// This should be called only after Flags::Parse() has returned. -XlaDeviceFlags* GetXlaDeviceFlags(); - -} // namespace legacy_flags -} // namespace tensorflow - -#endif // TENSORFLOW_COMPILER_JIT_LEGACY_FLAGS_XLA_DEVICE_FLAGS_H_ diff --git a/tensorflow/compiler/jit/legacy_flags/xla_ops_common_flags.cc b/tensorflow/compiler/jit/legacy_flags/xla_ops_common_flags.cc deleted file mode 100644 index 1443d48a73..0000000000 --- a/tensorflow/compiler/jit/legacy_flags/xla_ops_common_flags.cc +++ /dev/null @@ -1,52 +0,0 @@ -/* Copyright 2018 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 // NOLINT -#include - -#include "tensorflow/compiler/jit/legacy_flags/xla_ops_common_flags.h" -#include "tensorflow/compiler/xla/parse_flags_from_env.h" -#include "tensorflow/core/platform/logging.h" -#include "tensorflow/core/util/command_line_flags.h" - -namespace tensorflow { -namespace legacy_flags { - -XlaOpsCommonFlags* flags; -std::vector* flag_list; -std::once_flag flags_init; - -void AllocateAndParseFlags() { - flags = new XlaOpsCommonFlags; - flags->tf_xla_always_defer_compilation = false; - flag_list = new std::vector({ - Flag("tf_xla_always_defer_compilation", - &flags->tf_xla_always_defer_compilation, ""), - }); - xla::ParseFlagsFromEnv(*flag_list); - - if (VLOG_IS_ON(1)) { - VLOG(1) << "Parsed XlaOpsCommonFlags:"; - VLOG(1) << " tf_xla_always_defer_compilation = " - << flags->tf_xla_always_defer_compilation; - } -} - -const XlaOpsCommonFlags& GetXlaOpsCommonFlags() { - std::call_once(flags_init, &AllocateAndParseFlags); - return *flags; -} -} // namespace legacy_flags -} // namespace tensorflow diff --git a/tensorflow/compiler/jit/legacy_flags/xla_ops_common_flags.h b/tensorflow/compiler/jit/legacy_flags/xla_ops_common_flags.h deleted file mode 100644 index 7c5c1818ef..0000000000 --- a/tensorflow/compiler/jit/legacy_flags/xla_ops_common_flags.h +++ /dev/null @@ -1,36 +0,0 @@ -/* Copyright 2018 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_COMPILER_JIT_LEGACY_FLAGS_XLA_OPS_COMMON_FLAGS_H_ -#define TENSORFLOW_COMPILER_JIT_LEGACY_FLAGS_XLA_OPS_COMMON_FLAGS_H_ - -namespace tensorflow { -namespace legacy_flags { - -// Flags common to the _Xla* ops and their kernels. -struct XlaOpsCommonFlags { - // If true, _XlaCompile always refuses to compile the cluster, which means the - // XLA clusters always run in the TF executor. Defaults to false. - bool tf_xla_always_defer_compilation; -}; - -// Parses the flags in XlaOpsCommonFlags from the TF_XLA_FLAGS environment -// variable and returns a reference to the parsed copy. Parses TF_XLA_FLAGS -// only the first time this routine is called. -const XlaOpsCommonFlags& GetXlaOpsCommonFlags(); - -} // namespace legacy_flags -} // namespace tensorflow - -#endif // TENSORFLOW_COMPILER_JIT_LEGACY_FLAGS_XLA_OPS_COMMON_FLAGS_H_ diff --git a/tensorflow/compiler/jit/mark_for_compilation_pass.cc b/tensorflow/compiler/jit/mark_for_compilation_pass.cc index 70033cae0a..6618e3a58a 100644 --- a/tensorflow/compiler/jit/mark_for_compilation_pass.cc +++ b/tensorflow/compiler/jit/mark_for_compilation_pass.cc @@ -24,8 +24,8 @@ limitations under the License. #include "absl/container/flat_hash_set.h" #include "tensorflow/compiler/jit/deadness_analysis.h" #include "tensorflow/compiler/jit/defs.h" +#include "tensorflow/compiler/jit/flags.h" #include "tensorflow/compiler/jit/graphcycles/graphcycles.h" -#include "tensorflow/compiler/jit/legacy_flags/mark_for_compilation_pass_flags.h" #include "tensorflow/compiler/jit/union_find.h" #include "tensorflow/compiler/jit/xla_cluster_util.h" #include "tensorflow/compiler/tf2xla/const_analysis.h" @@ -72,6 +72,11 @@ struct OperationFilter { // to resort to a dummy implementation. Currently Assert and CheckNumerics ops // have dummy XLA implementations. bool allow_dummy_ops; + + // Whether ops that produce or consume DT_VARIANT values are allowed. We + // don't auto-cluster these ops because we don't yet support live-in or + // live-out DT_VARIANT values. + bool allow_ops_producing_or_consuming_variant; }; bool IsDummyImplOp(absl::string_view op_name) { @@ -81,7 +86,13 @@ bool IsDummyImplOp(absl::string_view op_name) { bool IsStatefulRandomOp(absl::string_view op_name) { return op_name == "RandomUniform" || op_name == "RandomShuffle" || op_name == "RandomUniformInt" || op_name == "RandomStandardNormal" || - op_name == "TruncatedNormal"; + op_name == "TruncatedNormal" || op_name == "Multinomial"; +} + +bool OpProducesOrConsumesVariant(const Node& node) { + auto is_variant = [](DataType dtype) { return dtype == DT_VARIANT; }; + return absl::c_any_of(node.input_types(), is_variant) || + absl::c_any_of(node.output_types(), is_variant); } bool HasXLAKernel(const Node& node, const DeviceType& jit_device_type) { @@ -246,6 +257,10 @@ bool IsCompilableCall(const NodeDef& call_def, if (!op_filter.allow_dummy_ops && IsDummyImplOp(node->type_string())) { return false; } + if (!op_filter.allow_ops_producing_or_consuming_variant && + OpProducesOrConsumesVariant(*node)) { + return false; + } if (!HasXLAKernel(*node, jit_device_type) && !IsCompilableCall(node->def(), jit_device_type, op_filter, depth + 1, lib_runtime)) { @@ -427,8 +442,7 @@ Status FindCompilationCandidates( BackwardsConstAnalysis(graph, /*compile_time_const_arg_indices=*/nullptr, &compile_time_const_nodes)); - int64& fuel = - legacy_flags::GetMarkForCompilationPassFlags()->tf_xla_clustering_fuel; + int64& fuel = GetMarkForCompilationPassFlags()->tf_xla_clustering_fuel; // Iterate over nodes in sorted order so that compiler fuel is deterministic. // We can't simply pass op_nodes().begin() and op_nodes().end to the @@ -471,16 +485,15 @@ Status FindCompilationCandidates( XlaOpRegistry::GetCompilationDevice(device_type.type(), ®istration)); DeviceType jit_device_type(registration->compilation_device_name); + bool always_auto_cluster = registration->autoclustering_policy == + XlaOpRegistry::AutoclusteringPolicy::kAlways; + OperationFilter op_filter; op_filter.allow_resource_ops = registration->compile_resource_ops; - op_filter.allow_stateful_rng_ops = - (registration->autoclustering_policy == - XlaOpRegistry::AutoclusteringPolicy::kAlways); - op_filter.allow_control_trigger = - (registration->autoclustering_policy == - XlaOpRegistry::AutoclusteringPolicy::kAlways); - op_filter.allow_dummy_ops = (registration->autoclustering_policy == - XlaOpRegistry::AutoclusteringPolicy::kAlways); + op_filter.allow_stateful_rng_ops = always_auto_cluster; + op_filter.allow_control_trigger = always_auto_cluster; + op_filter.allow_dummy_ops = always_auto_cluster; + op_filter.allow_ops_producing_or_consuming_variant = always_auto_cluster; if (!HasXLAKernel(*node, jit_device_type) && !IsCompilableCall(node->def(), jit_device_type, op_filter, 0, @@ -504,6 +517,12 @@ Status FindCompilationCandidates( << node->type_string() << ")"; continue; } + if (!op_filter.allow_ops_producing_or_consuming_variant && + OpProducesOrConsumesVariant(*node)) { + VLOG(2) << "Rejecting " << node->name() + << ": produces or consumes DT_VARIANT"; + continue; + } if (!op_filter.allow_resource_ops && (HasResourceOutput(*node) || IsNonResourceVarResourceOp(*node))) { @@ -607,8 +626,7 @@ OptimizerOptions::GlobalJitLevel GetGlobalJitLevel( // To set compilation to be on by default, change the following line. global_jit_level = OptimizerOptions::OFF; } - legacy_flags::MarkForCompilationPassFlags* flags = - legacy_flags::GetMarkForCompilationPassFlags(); + MarkForCompilationPassFlags* flags = GetMarkForCompilationPassFlags(); if (flags->tf_xla_auto_jit == -1 || (1 <= flags->tf_xla_auto_jit && flags->tf_xla_auto_jit <= 2)) { // If the flag tf_xla_auto_jit is a valid, non-zero setting, it overrides @@ -641,6 +659,7 @@ bool IsCompilable(FunctionLibraryRuntime* flr, const NodeDef& ndef) { op_filter.allow_stateful_rng_ops = true; op_filter.allow_control_trigger = true; op_filter.allow_dummy_ops = true; + op_filter.allow_ops_producing_or_consuming_variant = true; return IsCompilableCall(ndef, jit_device_type, op_filter, 0, flr); } @@ -651,8 +670,7 @@ Status MarkForCompilationPass::Run( // device ahead of time. OptimizerOptions::GlobalJitLevel global_jit_level = GetGlobalJitLevel(options); - legacy_flags::MarkForCompilationPassFlags* flags = - legacy_flags::GetMarkForCompilationPassFlags(); + MarkForCompilationPassFlags* flags = GetMarkForCompilationPassFlags(); bool fusion_only = flags->tf_xla_fusion_only; VLOG(1) << "flags->tf_xla_fusion_only = " << flags->tf_xla_fusion_only; @@ -953,8 +971,7 @@ Status MarkForCompilationPass::RunImpl( OptimizerOptions::GlobalJitLevel global_jit_level = GetGlobalJitLevel(options); - legacy_flags::MarkForCompilationPassFlags* flags = - legacy_flags::GetMarkForCompilationPassFlags(); + MarkForCompilationPassFlags* flags = GetMarkForCompilationPassFlags(); // Repeatedly contract edges between clusters that are on the same device, // provided the contraction would not create a cycle. diff --git a/tensorflow/compiler/jit/mark_for_compilation_pass_test.cc b/tensorflow/compiler/jit/mark_for_compilation_pass_test.cc index 24d78c0772..bf2c5508ea 100644 --- a/tensorflow/compiler/jit/mark_for_compilation_pass_test.cc +++ b/tensorflow/compiler/jit/mark_for_compilation_pass_test.cc @@ -22,6 +22,7 @@ limitations under the License. #include "tensorflow/cc/ops/array_ops.h" #include "tensorflow/cc/ops/control_flow_ops_internal.h" #include "tensorflow/cc/ops/function_ops.h" +#include "tensorflow/cc/ops/list_ops.h" #include "tensorflow/cc/ops/resource_variable_ops.h" #include "tensorflow/cc/ops/sendrecv_ops.h" #include "tensorflow/cc/ops/standard_ops.h" @@ -1147,5 +1148,80 @@ TEST(XlaCompilationTest, DontAutoClusterDummyOps) { EXPECT_EQ(clusters["test/check"], ""); } +TEST(XlaCompilationTest, DontAutoClusterOpsProducingVariant) { + Scope root = Scope::NewRootScope().ExitOnError(); + Output a = ops::Placeholder(root.WithOpName("test/a"), DT_INT64); + Output b = ops::Placeholder(root.WithOpName("test/b"), DT_INT64); + + Output cast_a = ops::Cast(root.WithOpName("test/cast_a"), a, DT_INT32); + Output cast_b = ops::Cast(root.WithOpName("test/cast_b"), b, DT_INT32); + + Output tensor_list_reserve = ops::TensorListReserve( + root.WithOpName("test/tensor_list_reserve"), cast_a, cast_b, DT_FLOAT); + + std::unique_ptr graph(new Graph(OpRegistry::Global())); + TF_ASSERT_OK(root.ToGraph(graph.get())); + + TF_ASSERT_OK(MarkForCompilationPassTestHelper::MarkForCompilation(&graph)); + + std::unordered_map clusters = GetClusters(*graph); + EXPECT_EQ(clusters["test/tensor_list_reserve"], ""); +} + +TEST(XlaCompilationTest, DontAutoClusterOpsConsumingVariant) { + Scope root = Scope::NewRootScope().ExitOnError(); + Output dummy_input = + ops::Placeholder(root.WithOpName("test/dummy_input"), DT_INT64); + Output variant_input = + ops::Placeholder(root.WithOpName("test/variant_input"), DT_VARIANT); + + // Create one more node so that we don't avoid creating a cluster solely + // because it would be trivial. + Output dummy_cast = + ops::Cast(root.WithOpName("test/dummy_cast"), dummy_input, DT_INT32); + + Output tensor_list_element_shape = ops::TensorListElementShape( + root.WithOpName("test/tensor_list_element_shape"), variant_input, + DT_INT32); + + root.graph()->AddControlEdge(dummy_cast.node(), + tensor_list_element_shape.node()); + + std::unique_ptr graph(new Graph(OpRegistry::Global())); + TF_ASSERT_OK(root.ToGraph(graph.get())); + + TF_ASSERT_OK(MarkForCompilationPassTestHelper::MarkForCompilation(&graph)); + + std::unordered_map clusters = GetClusters(*graph); + EXPECT_EQ(clusters["test/tensor_list_element_shape"], ""); +} + +TEST(XlaCompilationTest, ClusterOpsProducingVariantIfOnXlaDevice) { + Scope root = Scope::NewRootScope().ExitOnError(); + Output a = ops::Placeholder(root.WithOpName("test/a"), DT_INT64); + Output b = ops::Placeholder(root.WithOpName("test/b"), DT_INT64); + + Output cast_a = ops::Cast(root.WithOpName("test/cast_a"), a, DT_INT32); + Output cast_b = ops::Cast(root.WithOpName("test/cast_b"), b, DT_INT32); + + Output tensor_list_reserve = ops::TensorListReserve( + root.WithOpName("test/tensor_list_reserve"), cast_a, cast_b, DT_FLOAT); + + std::unique_ptr graph(new Graph(OpRegistry::Global())); + TF_ASSERT_OK(root.ToGraph(graph.get())); + + string xla_cpu_device = "/job:worker/replica:0/task:0/device:XLA_CPU:0"; + for (Node* n : graph->nodes()) { + if (absl::StartsWith(n->name(), /*prefix=*/"test/")) { + n->set_assigned_device_name(xla_cpu_device); + } + } + + TF_ASSERT_OK(MarkForCompilationPassTestHelper::MarkForCompilation(&graph)); + + std::unordered_map clusters = GetClusters(*graph); + EXPECT_NE(clusters["test/tensor_list_reserve"], ""); +} + } // namespace } // namespace tensorflow diff --git a/tensorflow/compiler/jit/mark_for_compilation_pass_test_helper.cc b/tensorflow/compiler/jit/mark_for_compilation_pass_test_helper.cc index d56d0f8ccf..64a3301745 100644 --- a/tensorflow/compiler/jit/mark_for_compilation_pass_test_helper.cc +++ b/tensorflow/compiler/jit/mark_for_compilation_pass_test_helper.cc @@ -34,15 +34,9 @@ namespace tensorflow { // // It may be worth refactoring out XlaOpRegistry::RegisterCompilationDevice to // make this more direct, but probably not worth it solely for this test. - std::vector devices; + std::vector> devices; TF_RETURN_IF_ERROR(DeviceFactory::AddDevices(*session_options, "", &devices)); - auto delete_devices = gtl::MakeCleanup([&] { - for (Device* d : devices) { - delete d; - } - }); - GraphOptimizationPassOptions opt_options; opt_options.graph = graph; opt_options.session_options = session_options; diff --git a/tensorflow/compiler/jit/ops/BUILD b/tensorflow/compiler/jit/ops/BUILD index f72224545b..64409d9334 100644 --- a/tensorflow/compiler/jit/ops/BUILD +++ b/tensorflow/compiler/jit/ops/BUILD @@ -18,3 +18,9 @@ tf_gen_op_wrapper_py( out = "xla_ops.py", deps = ["//tensorflow/compiler/jit/ops:xla_ops"], ) + +py_library( + name = "xla_ops_grad", + srcs = ["xla_ops_grad.py"], + deps = ["//tensorflow/python:framework_ops"], +) diff --git a/tensorflow/contrib/estimator/python/estimator/dnn.py b/tensorflow/compiler/jit/ops/xla_ops_grad.py similarity index 62% rename from tensorflow/contrib/estimator/python/estimator/dnn.py rename to tensorflow/compiler/jit/ops/xla_ops_grad.py index 10f657df8d..2d31d8dc71 100644 --- a/tensorflow/contrib/estimator/python/estimator/dnn.py +++ b/tensorflow/compiler/jit/ops/xla_ops_grad.py @@ -1,3 +1,4 @@ +"""Gradients for XLA ops.""" # Copyright 2018 The TensorFlow Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,21 +13,17 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""dnn python module. - -Importing from tensorflow.python.estimator is unsupported -and will soon break! -""" -# pylint: disable=unused-import,g-bad-import-order,g-import-not-at-top,wildcard-import from __future__ import absolute_import from __future__ import division from __future__ import print_function -from tensorflow_estimator.contrib.estimator.python.estimator import dnn +from tensorflow.python.framework import ops -# Include attrs that start with single underscore. -_HAS_DYNAMIC_ATTRIBUTES = True -dnn.__all__ = [s for s in dir(dnn) if not s.startswith('__')] -from tensorflow_estimator.contrib.estimator.python.estimator.dnn import * +@ops.RegisterGradient("XlaClusterOutput") +def _XlaClusterOutputGrad(_, grad): + del grad # unused + raise RuntimeError("Gradient computation of graph in xla.compile() is " + "prohibited because it can cause performance degradation." + "Please move gradient computation inside xla.compile().") diff --git a/tensorflow/compiler/jit/partially_decluster_pass.cc b/tensorflow/compiler/jit/partially_decluster_pass.cc index 36b345ecbf..42ea3926e1 100644 --- a/tensorflow/compiler/jit/partially_decluster_pass.cc +++ b/tensorflow/compiler/jit/partially_decluster_pass.cc @@ -26,6 +26,10 @@ limitations under the License. namespace tensorflow { namespace { + +bool NotBackedge(const Edge& edge) { return !edge.src()->IsNextIteration(); } + +namespace reduce_device_to_host_copies { Status FindNodesToDecluster(const Graph& graph, absl::flat_hash_set* result, absl::Span post_order) { @@ -140,8 +144,6 @@ Status PartiallyDeclusterNode(Graph* graph, Node* n) { return Status::OK(); } -bool NotBackedge(const Edge& edge) { return !edge.src()->IsNextIteration(); } - // Clones nodes to outside their cluster to avoid device-to-host copies. For // instance, converts this: // @@ -168,7 +170,7 @@ bool NotBackedge(const Edge& edge) { return !edge.src()->IsNextIteration(); } // where the ===> arrow has a hostmem source and destination and would entail a // device to host copy if the source and destination were not in the same XLA // cluster. -Status PartiallyDeclusterToRemoveDeviceToHostCopies(Graph* graph) { +Status PartiallyDeclusterGraph(Graph* graph) { // When deciding whether to decluster a particular node, we base our decision // on if we've decided that some of its consumers have to be declustered too. // Iterating the graph in post-order guarantees that consumers have been @@ -206,7 +208,9 @@ Status PartiallyDeclusterToRemoveDeviceToHostCopies(Graph* graph) { return Status::OK(); } +} // namespace reduce_device_to_host_copies +namespace reduce_recompilation { bool IsIntraClusterEdge(const Edge& edge) { absl::optional src_cluster_name = GetXlaClusterForNode(*edge.src()); @@ -269,7 +273,7 @@ Status MustCompileNode(const Node* n, bool* must_compile) { // regress performance in any significant manner. We will have to revisit this // algorith with a more complex cost model if this assumption turns out to be // incorrect. -Status DeclusterNodesToReduceRecompilations(Graph* graph) { +Status PartiallyDeclusterGraph(Graph* graph) { std::vector compile_time_const_nodes(graph->num_node_ids()); TF_RETURN_IF_ERROR(BackwardsConstAnalysis( *graph, nullptr, &compile_time_const_nodes, IsIntraClusterEdge)); @@ -322,7 +326,7 @@ Status DeclusterNodesToReduceRecompilations(Graph* graph) { return Status::OK(); } - +} // namespace reduce_recompilation } // namespace Status PartiallyDeclusterPass::Run( @@ -334,8 +338,9 @@ Status PartiallyDeclusterPass::Run( Graph* graph = options.graph->get(); - TF_RETURN_IF_ERROR(PartiallyDeclusterToRemoveDeviceToHostCopies(graph)); - TF_RETURN_IF_ERROR(DeclusterNodesToReduceRecompilations(graph)); + TF_RETURN_IF_ERROR( + reduce_device_to_host_copies::PartiallyDeclusterGraph(graph)); + TF_RETURN_IF_ERROR(reduce_recompilation::PartiallyDeclusterGraph(graph)); return Status::OK(); } diff --git a/tensorflow/compiler/jit/partially_decluster_pass_test.cc b/tensorflow/compiler/jit/partially_decluster_pass_test.cc index 1fc5da5071..38a54cc5ef 100644 --- a/tensorflow/compiler/jit/partially_decluster_pass_test.cc +++ b/tensorflow/compiler/jit/partially_decluster_pass_test.cc @@ -386,7 +386,7 @@ TEST(PartiallyDeclusterPassTest, DontDeclusterXlaDeviceOps) { TF_ASSERT_OK(s.ToGraph(graph.get())); // This is needed to register the XLA_GPU device. - std::vector devices; + std::vector> devices; TF_ASSERT_OK(DeviceFactory::AddDevices( SessionOptions(), "/job:localhost/replica:0/task:0", &devices)); @@ -400,10 +400,6 @@ TEST(PartiallyDeclusterPassTest, DontDeclusterXlaDeviceOps) { TF_ASSERT_OK(PartiallyDecluster(&graph)); EXPECT_EQ(GetXlaClusterForNode(*n), "cluster_0"); - - for (Device* d : devices) { - delete d; - } } TEST(PartiallyDeclusterPassTest, DontDeclusterNonTensorFlowOps) { diff --git a/tensorflow/compiler/jit/xla_cpu_device.cc b/tensorflow/compiler/jit/xla_cpu_device.cc index 116e075603..7df898ad12 100644 --- a/tensorflow/compiler/jit/xla_cpu_device.cc +++ b/tensorflow/compiler/jit/xla_cpu_device.cc @@ -17,8 +17,8 @@ limitations under the License. // operators using XLA via the XLA "Host" (CPU) backend. #include "absl/memory/memory.h" +#include "tensorflow/compiler/jit/flags.h" #include "tensorflow/compiler/jit/kernels/xla_ops.h" -#include "tensorflow/compiler/jit/legacy_flags/xla_device_flags.h" #include "tensorflow/compiler/jit/xla_compile_on_demand_op.h" #include "tensorflow/compiler/jit/xla_device.h" #include "tensorflow/compiler/jit/xla_device_ops.h" @@ -31,13 +31,13 @@ namespace tensorflow { class XlaCpuDeviceFactory : public DeviceFactory { public: Status CreateDevices(const SessionOptions& options, const string& name_prefix, - std::vector* devices) override; + std::vector>* devices) override; }; -Status XlaCpuDeviceFactory::CreateDevices(const SessionOptions& session_options, - const string& name_prefix, - std::vector* devices) { - legacy_flags::XlaDeviceFlags* flags = legacy_flags::GetXlaDeviceFlags(); +Status XlaCpuDeviceFactory::CreateDevices( + const SessionOptions& session_options, const string& name_prefix, + std::vector>* devices) { + XlaDeviceFlags* flags = GetXlaDeviceFlags(); bool compile_on_demand = flags->tf_xla_compile_on_demand; XlaOpRegistry::DeviceRegistration registration; @@ -63,8 +63,7 @@ Status XlaCpuDeviceFactory::CreateDevices(const SessionOptions& session_options, options.device_ordinal = 0; options.compilation_device_name = DEVICE_CPU_XLA_JIT; options.use_multiple_streams = false; - auto device = absl::make_unique(session_options, options); - devices->push_back(device.release()); + devices->push_back(absl::make_unique(session_options, options)); return Status::OK(); } diff --git a/tensorflow/compiler/jit/xla_device.cc b/tensorflow/compiler/jit/xla_device.cc index 5c1b55cb57..4201ff91a8 100644 --- a/tensorflow/compiler/jit/xla_device.cc +++ b/tensorflow/compiler/jit/xla_device.cc @@ -218,6 +218,9 @@ XlaDevice::XlaDevice(const SessionOptions& session_options, XlaDevice::~XlaDevice() { VLOG(1) << "Destroying XLA device " << jit_device_name_ << " " << this; mutex_lock lock(mu_); + while (outstanding_asynchronous_operations_ > 0) { + outstanding_asynchronous_operations_cv_.wait(lock); + } if (device_context_) { device_context_->Unref(); } @@ -384,6 +387,7 @@ void XlaDevice::ComputeAsync(AsyncOpKernel* op_kernel, OpKernelContext* context, Status XlaDevice::Sync() { VLOG(1) << "XlaDevice::Sync"; + tracing::ScopedActivity activity("XlaDevice::Sync", /*is_expensive=*/true); std::shared_ptr stream; { mutex_lock lock(mu_); @@ -391,13 +395,46 @@ Status XlaDevice::Sync() { } if (!stream) return Status::OK(); - if (!stream->parent()->SynchronizeAllActivity() || !stream->ok()) { + Status status = stream->BlockHostUntilDone(); + { + mutex_lock lock(mu_); + while (outstanding_asynchronous_operations_ > 0) { + outstanding_asynchronous_operations_cv_.wait(lock); + } + } + TF_RETURN_IF_ERROR(status); + if (!stream->ok()) { return errors::Internal("XlaDevice::Sync() failed."); } VLOG(1) << "XlaDevice::Sync completed"; return Status::OK(); } +void XlaDevice::Sync(const DoneCallback& done) { + VLOG(1) << "XlaDevice::Sync (asynchronous)"; + std::shared_ptr stream; + { + mutex_lock lock(mu_); + stream = stream_; + } + if (!stream) { + done(Status::OK()); + return; + } + + stream->ThenEnqueueOnBackgroundThread( + [this, stream, done](se::StreamExecutor*) { + tracing::ScopedActivity activity("XlaDevice::Sync::Callback", + /*is_expensive=*/true); + mutex_lock lock(mu_); + while (outstanding_asynchronous_operations_ > 0) { + outstanding_asynchronous_operations_cv_.wait(lock); + } + done(stream->ok() ? Status::OK() + : errors::Internal("XlaDevice::Sync() failed.")); + }); +} + Status XlaDevice::MakeTensorFromProto(const TensorProto& tensor_proto, const AllocatorAttributes alloc_attrs, Tensor* tensor) { @@ -441,6 +478,49 @@ bool XlaDevice::RequiresSyncOnCompletion() const { return sync_on_completion_; } +XlaDevice::AsynchronousOperationHandle::AsynchronousOperationHandle( + XlaDevice* device) + : device_(device) { + mutex_lock lock(device_->mu_); + ++device_->outstanding_asynchronous_operations_; +} + +XlaDevice::AsynchronousOperationHandle::~AsynchronousOperationHandle() { + if (device_) { + mutex_lock lock(device_->mu_); + --device_->outstanding_asynchronous_operations_; + device_->outstanding_asynchronous_operations_cv_.notify_all(); + } +} + +XlaDevice::AsynchronousOperationHandle::AsynchronousOperationHandle( + const XlaDevice::AsynchronousOperationHandle& other) + : device_(other.device_) { + mutex_lock lock(device_->mu_); + ++device_->outstanding_asynchronous_operations_; +} + +XlaDevice::AsynchronousOperationHandle::AsynchronousOperationHandle( + XlaDevice::AsynchronousOperationHandle&& other) + : device_(other.device_) { + other.device_ = nullptr; +} + +XlaDevice::AsynchronousOperationHandle& XlaDevice::AsynchronousOperationHandle:: +operator=(const XlaDevice::AsynchronousOperationHandle& other) { + device_ = other.device_; + mutex_lock lock(device_->mu_); + ++device_->outstanding_asynchronous_operations_; + return *this; +} + +XlaDevice::AsynchronousOperationHandle& XlaDevice::AsynchronousOperationHandle:: +operator=(XlaDevice::AsynchronousOperationHandle&& other) { + device_ = other.device_; + other.device_ = nullptr; + return *this; +} + XlaDeviceOpRegistrations* RegisterXlaDeviceKernels(const char* device, const char* jit_device) { // Any op assigned to the device that isn't rewritten by the graph rewriter diff --git a/tensorflow/compiler/jit/xla_device.h b/tensorflow/compiler/jit/xla_device.h index 49f53b477e..c8bb276cdb 100644 --- a/tensorflow/compiler/jit/xla_device.h +++ b/tensorflow/compiler/jit/xla_device.h @@ -135,6 +135,7 @@ class XlaDevice : public LocalDevice { void ComputeAsync(AsyncOpKernel* op_kernel, OpKernelContext* context, AsyncOpKernel::DoneCallback done) override; Status Sync() override; + void Sync(const DoneCallback& done) override; Status FillContextMap(const Graph* graph, DeviceContextMap* device_context_map) override @@ -164,7 +165,30 @@ class XlaDevice : public LocalDevice { bool RequiresSyncOnCompletion() const override LOCKS_EXCLUDED(mu_); + // A simple RAII handle. On construction the device's + // outstanding_asynchronous_operations_ field is incremented; on destruction + // it is decremented. + class AsynchronousOperationHandle { + public: + AsynchronousOperationHandle(XlaDevice* device); + ~AsynchronousOperationHandle(); + AsynchronousOperationHandle(const AsynchronousOperationHandle& other); + AsynchronousOperationHandle(AsynchronousOperationHandle&& other); + AsynchronousOperationHandle& operator=( + const AsynchronousOperationHandle& other); + AsynchronousOperationHandle& operator=(AsynchronousOperationHandle&& other); + + private: + XlaDevice* device_ = nullptr; + }; + + AsynchronousOperationHandle CreateAsynchronousOperationHandle() { + return AsynchronousOperationHandle(this); + } + private: + friend class AsynchronousOperationHandle; + xla::LocalClient* client() const; Allocator* GetAllocatorLocked(AllocatorAttributes attr) EXCLUSIVE_LOCKS_REQUIRED(mu_); @@ -227,6 +251,11 @@ class XlaDevice : public LocalDevice { // True if the device requires XlaDevice::Sync to be called on completion // regardless of status. bool sync_on_completion_ GUARDED_BY(mu_) = false; + + // Count of outstanding asynchronous operations which must be zero on Sync() + // completion. + int64 outstanding_asynchronous_operations_ GUARDED_BY(mu_) = 0; + condition_variable outstanding_asynchronous_operations_cv_; }; // Builds OpKernel registrations on 'device' for the JIT operators diff --git a/tensorflow/compiler/jit/xla_gpu_device.cc b/tensorflow/compiler/jit/xla_gpu_device.cc index 4419701695..944f732b99 100644 --- a/tensorflow/compiler/jit/xla_gpu_device.cc +++ b/tensorflow/compiler/jit/xla_gpu_device.cc @@ -29,12 +29,12 @@ namespace tensorflow { class XlaGpuDeviceFactory : public DeviceFactory { public: Status CreateDevices(const SessionOptions& options, const string& name_prefix, - std::vector* devices) override; + std::vector>* devices) override; }; -Status XlaGpuDeviceFactory::CreateDevices(const SessionOptions& session_options, - const string& name_prefix, - std::vector* devices) { +Status XlaGpuDeviceFactory::CreateDevices( + const SessionOptions& session_options, const string& name_prefix, + std::vector>* devices) { XlaOpRegistry::DeviceRegistration registration; registration.compilation_device_name = DEVICE_GPU_XLA_JIT; registration.autoclustering_policy = @@ -70,7 +70,7 @@ Status XlaGpuDeviceFactory::CreateDevices(const SessionOptions& session_options, return status; } - devices->push_back(device.release()); + devices->push_back(std::move(device)); } return Status::OK(); } diff --git a/tensorflow/compiler/jit/xla_interpreter_device.cc b/tensorflow/compiler/jit/xla_interpreter_device.cc index e828bae865..4007309ed1 100644 --- a/tensorflow/compiler/jit/xla_interpreter_device.cc +++ b/tensorflow/compiler/jit/xla_interpreter_device.cc @@ -33,12 +33,12 @@ constexpr std::array kExecAllTypes = { class XlaInterpreterDeviceFactory : public DeviceFactory { public: Status CreateDevices(const SessionOptions& options, const string& name_prefix, - std::vector* devices) override; + std::vector>* devices) override; }; Status XlaInterpreterDeviceFactory::CreateDevices( const SessionOptions& session_options, const string& name_prefix, - std::vector* devices) { + std::vector>* devices) { static XlaDeviceOpRegistrations* registrations = RegisterXlaDeviceKernels( DEVICE_XLA_INTERPRETER, DEVICE_INTERPRETER_XLA_JIT); (void)registrations; @@ -61,8 +61,7 @@ Status XlaInterpreterDeviceFactory::CreateDevices( options.device_ordinal = 0; options.compilation_device_name = DEVICE_INTERPRETER_XLA_JIT; options.use_multiple_streams = false; - auto device = absl::make_unique(session_options, options); - devices->push_back(device.release()); + devices->push_back(absl::make_unique(session_options, options)); return Status::OK(); } diff --git a/tensorflow/compiler/tests/BUILD b/tensorflow/compiler/tests/BUILD index 6b8e6bba1e..bc3d60b90e 100644 --- a/tensorflow/compiler/tests/BUILD +++ b/tensorflow/compiler/tests/BUILD @@ -375,27 +375,6 @@ tf_xla_py_test( ], ) -tf_xla_py_test( - name = "resampler_ops_test", - size = "small", - srcs = ["resampler_ops_test.py"], - disabled_backends = [ - # TODO(b/74459949) Support BatchDot in CPU backend. - "cpu", - "cpu_ondemand", - ], - # TODO(b/112295522): figure out how to make OSS build pass. - tags = ["no_oss"], - deps = [ - ":xla_test", - "//tensorflow/contrib/resampler:resampler_ops", - "//tensorflow/contrib/resampler:resampler_py", - "//tensorflow/python:array_ops", - "//tensorflow/python:client_testlib", - "//tensorflow/python:platform_test", - ], -) - tf_xla_py_test( name = "dynamic_stitch_test", size = "small", @@ -474,7 +453,6 @@ tf_xla_py_test( "//tensorflow/python:extra_py_tests_deps", "//tensorflow/python:framework", "//tensorflow/python:platform_test", - "//tensorflow/python:spectral_ops", "//tensorflow/python/ops/signal", ], ) diff --git a/tensorflow/compiler/tests/adagrad_da_test.py b/tensorflow/compiler/tests/adagrad_da_test.py index 69fb3ec296..e9c2d363ac 100644 --- a/tensorflow/compiler/tests/adagrad_da_test.py +++ b/tensorflow/compiler/tests/adagrad_da_test.py @@ -50,8 +50,8 @@ class AdagradDAOptimizerTest(xla_test.XLATestCase): zip([grads0, grads1], [var0, var1]), global_step=global_step) variables.global_variables_initializer().run() - self.assertAllClose([0.0, 0.0], var0.eval()) - self.assertAllClose([0.0, 0.0], var1.eval()) + self.assertAllClose([0.0, 0.0], self.evaluate(var0)) + self.assertAllClose([0.0, 0.0], self.evaluate(var1)) # Run a step of AdagradDA update.run() @@ -63,9 +63,9 @@ class AdagradDAOptimizerTest(xla_test.XLATestCase): # For -0.1*3.0*(0.1 - 0)/(0 + sqrt(0.1 + 0.1*0.1)) = -0.904534 # similarly for others. self.assertAllCloseAccordingToType( - np.array([-0.904534, -1.603567]), var0.eval()) + np.array([-0.904534, -1.603567]), self.evaluate(var0)) self.assertAllCloseAccordingToType( - np.array([-0.094821, -0.189358]), var1.eval()) + np.array([-0.094821, -0.189358]), self.evaluate(var1)) def testAdagradDAwithoutRegularizationBasic2(self): for dtype in self.float_types: @@ -87,16 +87,16 @@ class AdagradDAOptimizerTest(xla_test.XLATestCase): zip([grads0, grads1], [var0, var1]), global_step=global_step) variables.global_variables_initializer().run() - self.assertAllCloseAccordingToType([1.0, 2.0], var0.eval()) - self.assertAllCloseAccordingToType([4.0, 3.0], var1.eval()) + self.assertAllCloseAccordingToType([1.0, 2.0], self.evaluate(var0)) + self.assertAllCloseAccordingToType([4.0, 3.0], self.evaluate(var1)) # Run a step of AdagradDA update.run() self.assertAllCloseAccordingToType( - np.array([-0.904534, -1.603567]), var0.eval()) + np.array([-0.904534, -1.603567]), self.evaluate(var0)) self.assertAllCloseAccordingToType( - np.array([-0.094821, -0.189358]), var1.eval()) + np.array([-0.094821, -0.189358]), self.evaluate(var1)) def testAdagradDAWithL1(self): for dtype in self.float_types: @@ -118,16 +118,16 @@ class AdagradDAOptimizerTest(xla_test.XLATestCase): zip([grads0, grads1], [var0, var1]), global_step=global_step) variables.global_variables_initializer().run() - self.assertAllCloseAccordingToType([1.0, 2.0], var0.eval()) - self.assertAllCloseAccordingToType([4.0, 3.0], var1.eval()) + self.assertAllCloseAccordingToType([1.0, 2.0], self.evaluate(var0)) + self.assertAllCloseAccordingToType([4.0, 3.0], self.evaluate(var1)) # Run a step of AdagradDA update.run() self.assertAllCloseAccordingToType( - np.array([-0.895489, -1.59555]), var0.eval()) + np.array([-0.895489, -1.59555]), self.evaluate(var0)) self.assertAllCloseAccordingToType( - np.array([-0.085339, -0.17989]), var1.eval()) + np.array([-0.085339, -0.17989]), self.evaluate(var1)) def testAdagradDAWithL1_L2(self): for dtype in self.float_types: @@ -149,16 +149,16 @@ class AdagradDAOptimizerTest(xla_test.XLATestCase): zip([grads0, grads1], [var0, var1]), global_step=global_step) variables.global_variables_initializer().run() - self.assertAllCloseAccordingToType([1.0, 2.0], var0.eval()) - self.assertAllCloseAccordingToType([4.0, 3.0], var1.eval()) + self.assertAllCloseAccordingToType([1.0, 2.0], self.evaluate(var0)) + self.assertAllCloseAccordingToType([4.0, 3.0], self.evaluate(var1)) # Run a step of AdagradDA update.run() self.assertAllCloseAccordingToType( - np.array([-0.046907, -0.093659]), var0.eval()) + np.array([-0.046907, -0.093659]), self.evaluate(var0)) self.assertAllCloseAccordingToType( - np.array([-0.004275, -0.009023]), var1.eval()) + np.array([-0.004275, -0.009023]), self.evaluate(var1)) if __name__ == "__main__": diff --git a/tensorflow/compiler/tests/adagrad_test.py b/tensorflow/compiler/tests/adagrad_test.py index ab69319c59..e26483303c 100644 --- a/tensorflow/compiler/tests/adagrad_test.py +++ b/tensorflow/compiler/tests/adagrad_test.py @@ -42,17 +42,19 @@ class AdagradOptimizerTest(xla_test.XLATestCase): zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([3.0, 4.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) # Run 3 steps of adagrad for _ in range(3): ada_update.run() # Validate updated params self.assertAllCloseAccordingToType( - np.array([-1.6026098728179932, -0.6026098728179932]), var0.eval(), + np.array([-1.6026098728179932, -0.6026098728179932]), + self.evaluate(var0), float_rtol=1e-5) self.assertAllCloseAccordingToType( - np.array([2.715679168701172, 3.715679168701172]), var1.eval(), + np.array([2.715679168701172, 3.715679168701172]), + self.evaluate(var1), float_rtol=1e-5) def testTensorLearningRate(self): @@ -68,17 +70,19 @@ class AdagradOptimizerTest(xla_test.XLATestCase): zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([3.0, 4.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) # Run 3 steps of adagrad for _ in range(3): ada_update.run() # Validate updated params self.assertAllCloseAccordingToType( - np.array([-1.6026098728179932, -0.6026098728179932]), var0.eval(), + np.array([-1.6026098728179932, -0.6026098728179932]), + self.evaluate(var0), float_rtol=1e-5) self.assertAllCloseAccordingToType( - np.array([2.715679168701172, 3.715679168701172]), var1.eval(), + np.array([2.715679168701172, 3.715679168701172]), + self.evaluate(var1), float_rtol=1e-5) def testSharing(self): @@ -103,18 +107,20 @@ class AdagradOptimizerTest(xla_test.XLATestCase): variables.global_variables_initializer().run() # Fetch params to validate initial values. - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([3.0, 4.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) # Mix the first and the second adagrad for 3 steps. ada_update1.run() ada_update2.run() ada_update1.run() # Validate updated params (the same as with only 1 Adagrad). self.assertAllCloseAccordingToType( - np.array([-1.6026098728179932, -0.6026098728179932]), var0.eval(), + np.array([-1.6026098728179932, -0.6026098728179932]), + self.evaluate(var0), float_rtol=1e-5) self.assertAllCloseAccordingToType( - np.array([2.715679168701172, 3.715679168701172]), var1.eval(), + np.array([2.715679168701172, 3.715679168701172]), + self.evaluate(var1), float_rtol=1e-5) diff --git a/tensorflow/compiler/tests/adam_test.py b/tensorflow/compiler/tests/adam_test.py index 058576b3d4..8bcff9d379 100644 --- a/tensorflow/compiler/tests/adam_test.py +++ b/tensorflow/compiler/tests/adam_test.py @@ -75,23 +75,24 @@ class AdamOptimizerTest(xla_test.XLATestCase): variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([3.0, 4.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) beta1_power, beta2_power = opt._get_beta_accumulators() # Run 3 steps of Adam for t in range(1, 4): - self.assertAllCloseAccordingToType(0.9**t, beta1_power.eval()) - self.assertAllCloseAccordingToType(0.999**t, beta2_power.eval()) + self.assertAllCloseAccordingToType(0.9**t, self.evaluate(beta1_power)) + self.assertAllCloseAccordingToType(0.999**t, + self.evaluate(beta2_power)) update.run(feed_dict={grads0: grads0_np, grads1: grads1_np}) var0_np, m0, v0 = adam_update_numpy(var0_np, grads0_np, t, m0, v0) var1_np, m1, v1 = adam_update_numpy(var1_np, grads1_np, t, m1, v1) # Validate updated params - self.assertAllCloseAccordingToType(var0_np, var0.eval()) - self.assertAllCloseAccordingToType(var1_np, var1.eval()) + self.assertAllCloseAccordingToType(var0_np, self.evaluate(var0)) + self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1)) def testTensorLearningRate(self): for dtype in self.float_types: @@ -117,23 +118,24 @@ class AdamOptimizerTest(xla_test.XLATestCase): variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([3.0, 4.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) beta1_power, beta2_power = opt._get_beta_accumulators() # Run 3 steps of Adam for t in range(1, 4): - self.assertAllCloseAccordingToType(0.9**t, beta1_power.eval()) - self.assertAllCloseAccordingToType(0.999**t, beta2_power.eval()) + self.assertAllCloseAccordingToType(0.9**t, self.evaluate(beta1_power)) + self.assertAllCloseAccordingToType(0.999**t, + self.evaluate(beta2_power)) update.run(feed_dict={grads0: grads0_np, grads1: grads1_np}) var0_np, m0, v0 = adam_update_numpy(var0_np, grads0_np, t, m0, v0) var1_np, m1, v1 = adam_update_numpy(var1_np, grads1_np, t, m1, v1) # Validate updated params - self.assertAllCloseAccordingToType(var0_np, var0.eval()) - self.assertAllCloseAccordingToType(var1_np, var1.eval()) + self.assertAllCloseAccordingToType(var0_np, self.evaluate(var0)) + self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1)) def testSharing(self): for dtype in self.float_types: @@ -162,13 +164,14 @@ class AdamOptimizerTest(xla_test.XLATestCase): beta1_power, beta2_power = opt._get_beta_accumulators() # Fetch params to validate initial values - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([3.0, 4.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) # Run 3 steps of intertwined Adam1 and Adam2. for t in range(1, 4): - self.assertAllCloseAccordingToType(0.9**t, beta1_power.eval()) - self.assertAllCloseAccordingToType(0.999**t, beta2_power.eval()) + self.assertAllCloseAccordingToType(0.9**t, self.evaluate(beta1_power)) + self.assertAllCloseAccordingToType(0.999**t, + self.evaluate(beta2_power)) if t % 2 == 0: update1.run(feed_dict={grads0: grads0_np, grads1: grads1_np}) else: @@ -178,8 +181,8 @@ class AdamOptimizerTest(xla_test.XLATestCase): var1_np, m1, v1 = adam_update_numpy(var1_np, grads1_np, t, m1, v1) # Validate updated params - self.assertAllCloseAccordingToType(var0_np, var0.eval()) - self.assertAllCloseAccordingToType(var1_np, var1.eval()) + self.assertAllCloseAccordingToType(var0_np, self.evaluate(var0)) + self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1)) if __name__ == "__main__": diff --git a/tensorflow/compiler/tests/adamax_test.py b/tensorflow/compiler/tests/adamax_test.py index 3ed1d41b71..961b46375c 100644 --- a/tensorflow/compiler/tests/adamax_test.py +++ b/tensorflow/compiler/tests/adamax_test.py @@ -78,8 +78,8 @@ class AdaMaxOptimizerTest(xla_test.XLATestCase): variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([3.0, 4.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) beta1_power = opt._get_beta_accumulators() @@ -87,14 +87,17 @@ class AdaMaxOptimizerTest(xla_test.XLATestCase): for t in range(1, 4): update.run() - self.assertAllCloseAccordingToType(0.9**(t + 1), beta1_power.eval()) + self.assertAllCloseAccordingToType(0.9**(t + 1), + self.evaluate(beta1_power)) var0_np, m0, v0 = adamax_update_numpy(var0_np, grads0_np, t, m0, v0) var1_np, m1, v1 = adamax_update_numpy(var1_np, grads1_np, t, m1, v1) # Validate updated params - self.assertAllCloseAccordingToType(var0_np, var0.eval(), rtol=1e-2) - self.assertAllCloseAccordingToType(var1_np, var1.eval(), rtol=1e-2) + self.assertAllCloseAccordingToType( + var0_np, self.evaluate(var0), rtol=1e-2) + self.assertAllCloseAccordingToType( + var1_np, self.evaluate(var1), rtol=1e-2) self.assertEqual("var0_%d/AdaMax:0" % (i,), opt.get_slot(var=var0, name="m").name) @@ -118,22 +121,23 @@ class AdaMaxOptimizerTest(xla_test.XLATestCase): variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([3.0, 4.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) beta1_power = opt._get_beta_accumulators() # Run 3 steps of AdaMax for t in range(1, 4): - self.assertAllCloseAccordingToType(0.9**t, beta1_power.eval()) + self.assertAllCloseAccordingToType(0.9**t, self.evaluate(beta1_power)) update.run() var0_np, m0, v0 = adamax_update_numpy(var0_np, grads0_np, t, m0, v0) var1_np, m1, v1 = adamax_update_numpy(var1_np, grads1_np, t, m1, v1) # Validate updated params - self.assertAllCloseAccordingToType(var0_np, var0.eval()) - self.assertAllCloseAccordingToType(var1_np, var1.eval()) + self.assertAllCloseAccordingToType(var0_np, self.evaluate(var0)) + self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1)) + if __name__ == "__main__": test.main() diff --git a/tensorflow/compiler/tests/addsign_test.py b/tensorflow/compiler/tests/addsign_test.py index 1bc07ace23..a37c97e6d3 100644 --- a/tensorflow/compiler/tests/addsign_test.py +++ b/tensorflow/compiler/tests/addsign_test.py @@ -90,8 +90,8 @@ class AddSignTest(xla_test.XLATestCase): variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([3.0, 4.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) # Run 7 steps of AddSign # first 4 steps with positive gradient @@ -125,8 +125,8 @@ class AddSignTest(xla_test.XLATestCase): # Validate updated params self.assertAllCloseAccordingToType( - var0_np, var0.eval(), half_rtol=1e-2) - self.assertAllCloseAccordingToType(var1_np, var1.eval()) + var0_np, self.evaluate(var0), half_rtol=1e-2) + self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1)) def testDense(self): decay_steps = 10 diff --git a/tensorflow/compiler/tests/binary_ops_test.py b/tensorflow/compiler/tests/binary_ops_test.py index 332381c59e..9a5423c1b2 100644 --- a/tensorflow/compiler/tests/binary_ops_test.py +++ b/tensorflow/compiler/tests/binary_ops_test.py @@ -218,6 +218,21 @@ class BinaryOpsTest(xla_test.XLATestCase): ], equality_test=self.ListsAreClose) + # TF doesn't define these for bf16. + if dtype != dtypes.bfloat16.as_numpy_dtype: + self._testBinary( + gen_math_ops.xdivy, + np.array([0, 4, 3, 2, 1, 0], dtype=dtype), + np.array([0, 5, 6, 7, 8, float("NaN")], dtype=dtype), + expected=np.array([0, 0.8, 0.5, 0.285714, 0.125, 0], dtype=dtype)) + + self._testBinary( + gen_math_ops.xlogy, + np.array([0, 4, 3, 2, 1, 0], dtype=dtype), + np.array([0, 5, 6, 7, 8, float("NaN")], dtype=dtype), + expected=np.array([0, 6.437752, 5.375278, 3.89182, 2.079442, 0], + dtype=dtype)) + def testIntOps(self): for dtype in self.signed_int_types: self._testBinary( diff --git a/tensorflow/compiler/tests/categorical_op_test.py b/tensorflow/compiler/tests/categorical_op_test.py index a57d1dc81e..5d5e486f61 100644 --- a/tensorflow/compiler/tests/categorical_op_test.py +++ b/tensorflow/compiler/tests/categorical_op_test.py @@ -27,6 +27,7 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import random_seed from tensorflow.python.ops import array_ops from tensorflow.python.ops import random_ops +from tensorflow.python.ops import stateless_random_ops from tensorflow.python.platform import googletest @@ -56,11 +57,11 @@ class CategoricalTest(xla_test.XLATestCase): Returns: Frequencies from sampled classes; shape [batch_size, num_classes]. """ - with self.cached_session() as sess, self.test_scope(): + with self.cached_session(), self.test_scope(): random_seed.set_random_seed(1618) op = random_ops.multinomial(logits, num_samples, output_dtype=dtypes.int32) - d = sess.run(op) + d = self.evaluate(op) batch_size, num_classes = logits.shape freqs_mat = [] @@ -79,15 +80,15 @@ class CategoricalTest(xla_test.XLATestCase): def _testRngIsNotConstant(self, rng, dtype, output_dtype): # Tests that 'rng' does not always return the same value. - with self.cached_session() as sess: + with self.cached_session(): with self.test_scope(): x = rng(dtype, output_dtype) # The random-number generator, if working correctly, should produce the # same output multiple times with low probability. - y = sess.run(x) - z = sess.run(x) - w = sess.run(x) + y = self.evaluate(x) + z = self.evaluate(x) + w = self.evaluate(x) # We use exact equality here. If the random-number generator is producing # deterministic output, all three outputs will be bitwise identical. @@ -107,12 +108,12 @@ class CategoricalTest(xla_test.XLATestCase): def testCategoricalIsInRange(self): for dtype in self.float_types: for output_dtype in self.output_dtypes(): - with self.cached_session() as sess: + with self.cached_session(): with self.test_scope(): x = random_ops.multinomial( array_ops.ones(shape=[1, 20], dtype=dtype), 1000, output_dtype=output_dtype) - y = sess.run(x) + y = self.evaluate(x) self.assertTrue((y >= 0).sum() == 1000) self.assertTrue((y < 20).sum() == 1000) @@ -138,6 +139,57 @@ class CategoricalTest(xla_test.XLATestCase): chi2 = self._chi2(probs, freqs) self.assertLess(chi2, 1e-3) + def testStatelessMultinomialIsInRange(self): + for dtype in self.float_types: + for output_dtype in self.output_dtypes(): + with self.cached_session() as sess: + with self.test_scope(): + seed_t = array_ops.placeholder(dtypes.int32, shape=[2]) + x = stateless_random_ops.stateless_multinomial( + array_ops.ones(shape=[1, 20], dtype=dtype), + 1000, + seed_t, + output_dtype=output_dtype) + y = sess.run(x, {seed_t: [0x12345678, 0xabcdef12]}) + self.assertTrue((y >= 0).sum() == 1000) + self.assertTrue((y < 20).sum() == 1000) + + def testDeterminismMultinomial(self): + # Stateless values should be equal iff the seeds are equal (roughly) + num_samples = 10 + with self.cached_session(), self.test_scope(): + seed_t = array_ops.placeholder(dtypes.int32, shape=[2]) + seeds = [(x, y) for x in range(5) for y in range(5)] * 3 + for logits in ([[0.1, 0.25, 0.5, 0.15]], [[0.5, 0.5], [0.8, 0.2], + [0.25, 0.75]]): + pure = stateless_random_ops.stateless_multinomial( + logits, num_samples, seed=seed_t) + values = [(seed, pure.eval(feed_dict={seed_t: seed})) for seed in seeds] + for s0, v0 in values: + for s1, v1 in values: + self.assertEqual(s0 == s1, np.all(v0 == v1)) + + def testEmpty(self): + with self.cached_session(): + with self.test_scope(): + x = random_ops.multinomial( + array_ops.zeros([42, 40]), 0, output_dtype=dtypes.int32) + y = self.evaluate(x) + self.assertEqual(y.shape, (42, 0)) + + def testEmptyStateless(self): + with self.cached_session() as sess: + with self.test_scope(): + seed_t = array_ops.placeholder(dtypes.int32, shape=[2]) + x = stateless_random_ops.stateless_multinomial( + array_ops.zeros([42, 40]), + 0, + seed=seed_t, + output_dtype=dtypes.int32) + y = sess.run(x, {seed_t: [0x12345678, 0xabcdef12]}) + self.assertEqual(y.shape, (42, 0)) + + if __name__ == '__main__': googletest.main() diff --git a/tensorflow/compiler/tests/clustering_test.py b/tensorflow/compiler/tests/clustering_test.py index 88bd58b2da..ef2d7af69d 100644 --- a/tensorflow/compiler/tests/clustering_test.py +++ b/tensorflow/compiler/tests/clustering_test.py @@ -43,7 +43,7 @@ class ClusteringTest(xla_test.XLATestCase): input1 = constant_op.constant(val1, name="const1") input2 = constant_op.constant(val2, name="const2") output = math_ops.add(input1, input2) - result = output.eval() + result = self.evaluate(output) self.assertAllClose(result, expected, rtol=1e-3) def testAddFromCpuMultiple(self): @@ -57,7 +57,7 @@ class ClusteringTest(xla_test.XLATestCase): with self.test_scope(): output = math_ops.add(input1, input2) for _ in xrange(10): - result = output.eval() + result = self.evaluate(output) self.assertAllClose(result, expected, rtol=1e-3) def testDeadlock(self): diff --git a/tensorflow/compiler/tests/concat_ops_test.py b/tensorflow/compiler/tests/concat_ops_test.py index 2d225ad226..2187f57960 100644 --- a/tensorflow/compiler/tests/concat_ops_test.py +++ b/tensorflow/compiler/tests/concat_ops_test.py @@ -72,7 +72,7 @@ class ConcatTest(xla_test.XLATestCase): x2 = constant_op.constant(p2) with self.test_scope(): c = array_ops.concat([x1, x2], 0) - result = c.eval() + result = self.evaluate(c) self.assertAllEqual(result[:2, :], p1) self.assertAllEqual(result[2:, :], p2) @@ -150,7 +150,7 @@ class ConcatTest(xla_test.XLATestCase): [float(x) for x in grad_inp.flatten()], shape=output_shape) grad = gradients_impl.gradients([c], inp_tensors, [grad_tensor]) concated_grad = array_ops.concat(grad, 1) - result = concated_grad.eval() + result = self.evaluate(concated_grad) self.assertAllEqual(result, grad_inp) def testGradientsSimpleAll(self): @@ -177,7 +177,7 @@ class ConcatTest(xla_test.XLATestCase): [float(x) for x in grad_inp.flatten()], shape=output_shape) grad = gradients_impl.gradients([c], inp_tensors, [grad_tensor]) concated_grad = array_ops.concat(grad, 0) - result = concated_grad.eval() + result = self.evaluate(concated_grad) self.assertAllEqual(result, grad_inp) @@ -205,7 +205,7 @@ class ConcatTest(xla_test.XLATestCase): [float(x) for x in grad_inp.flatten()], shape=output_shape) grad = gradients_impl.gradients([c], inp_tensors, [grad_tensor]) concated_grad = array_ops.concat(grad, 2) - result = concated_grad.eval() + result = self.evaluate(concated_grad) self.assertAllEqual(result, grad_inp) @@ -242,7 +242,7 @@ class ConcatTest(xla_test.XLATestCase): [float(x) for x in grad_inp.flatten()], shape=output_shape) grad = gradients_impl.gradients([c], inp_tensors, [grad_tensor]) concated_grad = array_ops.concat(grad, concat_dim) - result = concated_grad.eval() + result = self.evaluate(concated_grad) self.assertAllEqual(result, grad_inp) @@ -254,7 +254,7 @@ class ConcatTest(xla_test.XLATestCase): def DISABLED_testZeroSize(self): # Verify that concat doesn't crash and burn for zero size inputs np.random.seed(7) - with self.cached_session() as sess: + with self.cached_session(): with self.test_scope(): for shape0 in (), (2,): axis = len(shape0) @@ -270,7 +270,7 @@ class ConcatTest(xla_test.XLATestCase): self.assertAllEqual(c.eval(), correct) # Check gradients dc = np.random.randn(*c.get_shape().as_list()) - dxs = sess.run(gradients_impl.gradients(c, xs, dc)) + dxs = self.evaluate(gradients_impl.gradients(c, xs, dc)) self.assertAllEqual(dc, np.concatenate(dxs, axis=axis)) def testConcatTuple(self): @@ -280,7 +280,7 @@ class ConcatTest(xla_test.XLATestCase): with self.test_scope(): concat_list_t = array_ops.concat([c1, c2], 0) concat_tuple_t = array_ops.concat((c1, c2), 0) - self.assertAllEqual(concat_list_t.eval(), concat_tuple_t.eval()) + self.assertAllEqual(concat_list_t.eval(), self.evaluate(concat_tuple_t)) def testConcatNoScalars(self): with self.cached_session(): @@ -330,47 +330,47 @@ class ConcatTest(xla_test.XLATestCase): class ConcatOffsetTest(xla_test.XLATestCase): def testBasic(self): - with self.cached_session() as sess: + with self.cached_session(): with self.test_scope(): cdim = constant_op.constant(1, dtypes.int32) s0 = constant_op.constant([2, 3, 5], dtypes.int32) s1 = constant_op.constant([2, 7, 5], dtypes.int32) s2 = constant_op.constant([2, 20, 5], dtypes.int32) off = gen_array_ops.concat_offset(cdim, [s0, s1, s2]) - ans = sess.run(off) + ans = self.evaluate(off) self.assertAllEqual(ans, [[0, 0, 0], [0, 3, 0], [0, 10, 0]]) class PackTest(xla_test.XLATestCase): def testBasic(self): - with self.cached_session() as sess: + with self.cached_session(): with self.test_scope(): s0 = constant_op.constant([2, 3, 5], dtypes.int32) s1 = constant_op.constant([2, 7, 5], dtypes.int32) s2 = constant_op.constant([2, 20, 5], dtypes.int32) packed = array_ops.stack([s0, s1, s2]) - ans = sess.run(packed) + ans = self.evaluate(packed) self.assertAllEqual(ans, [[2, 3, 5], [2, 7, 5], [2, 20, 5]]) def testScalars(self): - with self.cached_session() as sess: + with self.cached_session(): with self.test_scope(): s0 = constant_op.constant(2, dtypes.int32) s1 = constant_op.constant(3, dtypes.int32) s2 = constant_op.constant(5, dtypes.int32) packed = array_ops.stack([s0, s1, s2]) - ans = sess.run(packed) + ans = self.evaluate(packed) self.assertAllEqual(ans, [2, 3, 5]) def testEmpty(self): - with self.cached_session() as sess: + with self.cached_session(): with self.test_scope(): s0 = constant_op.constant([[]], dtypes.int32) s1 = constant_op.constant([[]], dtypes.int32) s2 = constant_op.constant([[]], dtypes.int32) packed = array_ops.stack([s0, s1, s2]) - ans = sess.run(packed) + ans = self.evaluate(packed) self.assertAllEqual(ans, [[[]], [[]], [[]]]) diff --git a/tensorflow/compiler/tests/conv3d_test.py b/tensorflow/compiler/tests/conv3d_test.py index d59fd0236f..01cc1b6392 100644 --- a/tensorflow/compiler/tests/conv3d_test.py +++ b/tensorflow/compiler/tests/conv3d_test.py @@ -85,7 +85,7 @@ class Conv3DTransposeTest(xla_test.XLATestCase): 1.0, shape=f_shape, name="filter", dtype=dtypes.float32) output = nn_ops.conv3d_transpose( x, f, y_shape, strides=strides, padding="SAME") - value = output.eval() + value = self.evaluate(output) # We count the number of cells being added at the locations in the output. # At the center, #cells = kernel_depth * kernel_height * kernel_width @@ -135,7 +135,7 @@ class Conv3DTransposeTest(xla_test.XLATestCase): 1.0, shape=f_shape, name="filter", dtype=dtypes.float32) output = nn_ops.conv3d_transpose( x, f, y_shape, strides=strides, padding="SAME") - value = output.eval() + value = self.evaluate(output) for n in xrange(x_shape[0]): for k in xrange(f_shape[3]): @@ -173,7 +173,7 @@ class Conv3DTransposeTest(xla_test.XLATestCase): 1.0, shape=f_shape, name="filter", dtype=dtypes.float32) output = nn_ops.conv3d_transpose( x, f, y_shape, strides=strides, padding="VALID") - value = output.eval() + value = self.evaluate(output) cache_values = np.zeros(y_shape, dtype=np.float32) diff --git a/tensorflow/compiler/tests/dense_layer_test.py b/tensorflow/compiler/tests/dense_layer_test.py index d1b90f098d..bf5ea7b1fb 100644 --- a/tensorflow/compiler/tests/dense_layer_test.py +++ b/tensorflow/compiler/tests/dense_layer_test.py @@ -42,7 +42,7 @@ def GetRunMetadataLabels(run_metadata): def InLabels(labels, substr): """Returns true iff one of the labels contains substr.""" - return any([substr in x for x in labels]) + return any(substr in x for x in labels) class DenseLayerTest(test.TestCase): @@ -72,7 +72,7 @@ class DenseLayerTest(test.TestCase): x = array_ops.placeholder(shape=[None, None, 3], dtype=np.float32) y = layers.dense(x, 3) - sess.run(variables.initialize_all_variables()) + self.evaluate(variables.initialize_all_variables()) run_metadata = config_pb2.RunMetadata() test_utils.RunWithWarmup( sess, @@ -97,7 +97,7 @@ class DenseLayerTest(test.TestCase): with jit_scope(): y = layers.dense(x, 3) - sess.run(variables.initialize_all_variables()) + self.evaluate(variables.initialize_all_variables()) run_metadata = config_pb2.RunMetadata() test_utils.RunWithWarmup( sess, @@ -126,7 +126,7 @@ class DenseLayerTest(test.TestCase): with jit_scope(): y = layers.dense(x, 3) - sess.run(variables.initialize_all_variables()) + self.evaluate(variables.initialize_all_variables()) run_metadata = config_pb2.RunMetadata() test_utils.RunWithWarmup( sess, diff --git a/tensorflow/compiler/tests/dynamic_stitch_test.py b/tensorflow/compiler/tests/dynamic_stitch_test.py index 50b04daa6b..e89cf975f5 100644 --- a/tensorflow/compiler/tests/dynamic_stitch_test.py +++ b/tensorflow/compiler/tests/dynamic_stitch_test.py @@ -58,6 +58,15 @@ class DynamicStitchTest(xla_test.XLATestCase): [idx1, idx2], [val1, val2], expected=np.array([[], [], [], []], np.int32)) + def testEmptyIndex(self): + idx1 = np.array([], dtype=np.int32) + idx2 = np.array([[], []], dtype=np.int32) + val1 = np.ndarray(shape=(0, 9), dtype=np.int32) + val2 = np.ndarray(shape=(2, 0, 9), dtype=np.int32) + self._AssertDynamicStitchResultIs([idx1, idx2], [val1, val2], + expected=np.ndarray( + shape=(0, 9), dtype=np.int32)) + def testSimple1D(self): val1 = np.array([0, 4, 7], dtype=np.int32) val2 = np.array([1, 6, 2, 3, 5], dtype=np.int32) diff --git a/tensorflow/compiler/tests/eager_test.py b/tensorflow/compiler/tests/eager_test.py index 63cee550fd..2af32b537b 100644 --- a/tensorflow/compiler/tests/eager_test.py +++ b/tensorflow/compiler/tests/eager_test.py @@ -101,12 +101,12 @@ class EagerTest(xla_test.XLATestCase): self.assertAllEqual(15, product) # Run some ops graphly - with context.graph_mode(), self.cached_session() as sess: + with context.graph_mode(), self.cached_session(): with self.test_scope(): three = constant_op.constant(3) five = constant_op.constant(5) product = three * five - self.assertAllEqual(15, sess.run(product)) + self.assertAllEqual(15, self.evaluate(product)) def testDegenerateSlices(self): with self.test_scope(): diff --git a/tensorflow/compiler/tests/fft_test.py b/tensorflow/compiler/tests/fft_test.py index e92afd5d6f..0edd0c35aa 100644 --- a/tensorflow/compiler/tests/fft_test.py +++ b/tensorflow/compiler/tests/fft_test.py @@ -27,8 +27,7 @@ from tensorflow.compiler.tests import xla_test from tensorflow.python.framework import dtypes from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradients_impl -from tensorflow.python.ops import signal -from tensorflow.python.ops import spectral_ops +from tensorflow.python.ops.signal import signal from tensorflow.python.platform import googletest BATCH_DIMS = (3, 5) @@ -107,39 +106,39 @@ class FFTTest(xla_test.XLATestCase): def testFFT(self): self._VerifyFftMethod(INNER_DIMS_1D, lambda x: x, np.fft.fft, - spectral_ops.fft) + signal.fft) def testFFT2D(self): self._VerifyFftMethod(INNER_DIMS_2D, lambda x: x, np.fft.fft2, - spectral_ops.fft2d) + signal.fft2d) def testFFT3D(self): self._VerifyFftMethod(INNER_DIMS_3D, lambda x: x, lambda x: np.fft.fftn(x, axes=(-3, -2, -1)), - spectral_ops.fft3d) + signal.fft3d) def testIFFT(self): self._VerifyFftMethod(INNER_DIMS_1D, lambda x: x, np.fft.ifft, - spectral_ops.ifft) + signal.ifft) def testIFFT2D(self): self._VerifyFftMethod(INNER_DIMS_2D, lambda x: x, np.fft.ifft2, - spectral_ops.ifft2d) + signal.ifft2d) def testIFFT3D(self): self._VerifyFftMethod(INNER_DIMS_3D, lambda x: x, lambda x: np.fft.ifftn(x, axes=(-3, -2, -1)), - spectral_ops.ifft3d) + signal.ifft3d) def testRFFT(self): self._VerifyFftMethod( INNER_DIMS_1D, np.real, lambda x: np.fft.rfft(x, n=x.shape[-1]), - lambda x: spectral_ops.rfft(x, fft_length=[x.shape[-1].value])) + lambda x: signal.rfft(x, fft_length=[x.shape[-1].value])) def testRFFT2D(self): def _tf_fn(x): - return spectral_ops.rfft2d( + return signal.rfft2d( x, fft_length=[x.shape[-2].value, x.shape[-1].value]) self._VerifyFftMethod( @@ -153,16 +152,33 @@ class FFTTest(xla_test.XLATestCase): x, axes=(-3, -2, -1), s=[x.shape[-3], x.shape[-2], x.shape[-1]]) def _tf_fn(x): - return spectral_ops.rfft3d( + return signal.rfft3d( x, fft_length=[x.shape[-3].value, x.shape[-2].value, x.shape[-1].value]) self._VerifyFftMethod(INNER_DIMS_3D, np.real, _to_expected, _tf_fn) + def testRFFT3DMismatchedSize(self): + + def _to_expected(x): + return np.fft.rfftn( + x, + axes=(-3, -2, -1), + s=[x.shape[-3] // 2, x.shape[-2], x.shape[-1] * 2]) + + def _tf_fn(x): + return signal.rfft3d( + x, + fft_length=[ + x.shape[-3].value // 2, x.shape[-2].value, x.shape[-1].value * 2 + ]) + + self._VerifyFftMethod(INNER_DIMS_3D, np.real, _to_expected, _tf_fn) + def testIRFFT(self): def _tf_fn(x): - return spectral_ops.irfft(x, fft_length=[2 * (x.shape[-1].value - 1)]) + return signal.irfft(x, fft_length=[2 * (x.shape[-1].value - 1)]) self._VerifyFftMethod( INNER_DIMS_1D, lambda x: np.fft.rfft(np.real(x), n=x.shape[-1]), @@ -171,7 +187,7 @@ class FFTTest(xla_test.XLATestCase): def testIRFFT2D(self): def _tf_fn(x): - return spectral_ops.irfft2d( + return signal.irfft2d( x, fft_length=[x.shape[-2].value, 2 * (x.shape[-1].value - 1)]) self._VerifyFftMethod( @@ -195,7 +211,7 @@ class FFTTest(xla_test.XLATestCase): s=[x.shape[-3], x.shape[-2], 2 * (x.shape[-1] - 1)]) def _tf_fn(x): - return spectral_ops.irfft3d( + return signal.irfft3d( x, fft_length=[ x.shape[-3].value, x.shape[-2].value, 2 * (x.shape[-1].value - 1) @@ -203,6 +219,30 @@ class FFTTest(xla_test.XLATestCase): self._VerifyFftMethod(INNER_DIMS_3D, _to_input, _to_expected, _tf_fn) + def testIRFFT3DMismatchedSize(self): + + def _to_input(x): + return np.fft.rfftn( + np.real(x), + axes=(-3, -2, -1), + s=[x.shape[-3] // 2, x.shape[-2], x.shape[-1] * 2]) + + def _to_expected(x): + return np.fft.irfftn( + x, + axes=(-3, -2, -1), + s=[x.shape[-3] // 2, x.shape[-2], x.shape[-1] * 2]) + + def _tf_fn(x): + return signal.irfft3d( + x, + fft_length=[ + x.shape[-3].value // 2, x.shape[-2].value, x.shape[-1].value * 2 + ]) + + self._VerifyFftMethod(INNER_DIMS_3D, _to_input, _to_expected, _tf_fn) + + if __name__ == "__main__": googletest.main() diff --git a/tensorflow/compiler/tests/fifo_queue_test.py b/tensorflow/compiler/tests/fifo_queue_test.py index 8c7edfd277..91d77d2f79 100644 --- a/tensorflow/compiler/tests/fifo_queue_test.py +++ b/tensorflow/compiler/tests/fifo_queue_test.py @@ -129,7 +129,7 @@ class FIFOQueueTest(xla_test.XLATestCase): enqueue_op.run() for i in xrange(len(elems)): - vals = dequeued_t.eval() + vals = self.evaluate(dequeued_t) self.assertEqual([elems[i]], vals) def testEnqueueAndBlockingDequeue(self): @@ -192,9 +192,9 @@ class FIFOQueueTest(xla_test.XLATestCase): self.assertEqual([], size.get_shape()) enqueue_op.run() - self.assertEqual(1, size.eval()) + self.assertEqual(1, self.evaluate(size)) dequeued_t.op.run() - self.assertEqual(0, size.eval()) + self.assertEqual(0, self.evaluate(size)) if __name__ == "__main__": diff --git a/tensorflow/compiler/tests/ftrl_test.py b/tensorflow/compiler/tests/ftrl_test.py index 5b197afd65..b078053cdb 100644 --- a/tensorflow/compiler/tests/ftrl_test.py +++ b/tensorflow/compiler/tests/ftrl_test.py @@ -50,14 +50,14 @@ class FtrlOptimizerTest(xla_test.XLATestCase): ftrl_update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllClose([0.0, 0.0], var0.eval()) - self.assertAllClose([0.0, 0.0], var1.eval()) + self.assertAllClose([0.0, 0.0], self.evaluate(var0)) + self.assertAllClose([0.0, 0.0], self.evaluate(var1)) # Run Ftrl for a few steps for _ in range(steps): ftrl_update.run() - return var0.eval(), var1.eval() + return self.evaluate(var0), self.evaluate(var1) def equivAdagradTest_AdagradPart(self, steps, dtype): var0, var1, grads0, grads1 = self.initVariableAndGradient(dtype) @@ -65,14 +65,14 @@ class FtrlOptimizerTest(xla_test.XLATestCase): adagrad_update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllClose([0.0, 0.0], var0.eval()) - self.assertAllClose([0.0, 0.0], var1.eval()) + self.assertAllClose([0.0, 0.0], self.evaluate(var0)) + self.assertAllClose([0.0, 0.0], self.evaluate(var1)) # Run Adagrad for a few steps for _ in range(steps): adagrad_update.run() - return var0.eval(), var1.eval() + return self.evaluate(var0), self.evaluate(var1) def equivGradientDescentTest_FtrlPart(self, steps, dtype): var0, var1, grads0, grads1 = self.initVariableAndGradient(dtype) @@ -85,14 +85,14 @@ class FtrlOptimizerTest(xla_test.XLATestCase): ftrl_update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllClose([0.0, 0.0], var0.eval()) - self.assertAllClose([0.0, 0.0], var1.eval()) + self.assertAllClose([0.0, 0.0], self.evaluate(var0)) + self.assertAllClose([0.0, 0.0], self.evaluate(var1)) # Run Ftrl for a few steps for _ in range(steps): ftrl_update.run() - return var0.eval(), var1.eval() + return self.evaluate(var0), self.evaluate(var1) def equivGradientDescentTest_GradientDescentPart(self, steps, dtype): var0, var1, grads0, grads1 = self.initVariableAndGradient(dtype) @@ -100,14 +100,14 @@ class FtrlOptimizerTest(xla_test.XLATestCase): sgd_update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllClose([0.0, 0.0], var0.eval()) - self.assertAllClose([0.0, 0.0], var1.eval()) + self.assertAllClose([0.0, 0.0], self.evaluate(var0)) + self.assertAllClose([0.0, 0.0], self.evaluate(var1)) # Run GradientDescent for a few steps for _ in range(steps): sgd_update.run() - return var0.eval(), var1.eval() + return self.evaluate(var0), self.evaluate(var1) def testFtrlwithoutRegularization(self): for dtype in self.float_types: @@ -124,8 +124,8 @@ class FtrlOptimizerTest(xla_test.XLATestCase): ftrl_update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllClose([0.0, 0.0], var0.eval()) - self.assertAllClose([0.0, 0.0], var1.eval()) + self.assertAllClose([0.0, 0.0], self.evaluate(var0)) + self.assertAllClose([0.0, 0.0], self.evaluate(var1)) # Run 3 steps FTRL for _ in range(3): @@ -134,12 +134,12 @@ class FtrlOptimizerTest(xla_test.XLATestCase): # Validate updated params self.assertAllCloseAccordingToType( np.array([-2.60260963, -4.29698515]), - var0.eval(), + self.evaluate(var0), float_rtol=1e-4, half_rtol=1e-2) self.assertAllCloseAccordingToType( np.array([-0.28432083, -0.56694895]), - var1.eval(), + self.evaluate(var1), float_rtol=1e-5, half_rtol=1e-2) @@ -158,8 +158,8 @@ class FtrlOptimizerTest(xla_test.XLATestCase): ftrl_update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([4.0, 3.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([4.0, 3.0], self.evaluate(var1)) # Run 3 steps FTRL for _ in range(3): @@ -167,10 +167,14 @@ class FtrlOptimizerTest(xla_test.XLATestCase): # Validate updated params self.assertAllCloseAccordingToType( - np.array([-2.55607247, -3.98729396]), var0.eval(), 1e-5, 1e-5, + np.array([-2.55607247, -3.98729396]), + self.evaluate(var0), + 1e-5, + 1e-5, float_rtol=1e-4) self.assertAllCloseAccordingToType( - np.array([-0.28232238, -0.56096673]), var1.eval(), 1e-5, 1e-5) + np.array([-0.28232238, -0.56096673]), self.evaluate(var1), 1e-5, + 1e-5) def testFtrlWithL1(self): for dtype in self.float_types: @@ -187,8 +191,8 @@ class FtrlOptimizerTest(xla_test.XLATestCase): ftrl_update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([4.0, 3.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([4.0, 3.0], self.evaluate(var1)) # Run 10 steps FTRL for _ in range(10): @@ -197,12 +201,14 @@ class FtrlOptimizerTest(xla_test.XLATestCase): # Validate updated params self.assertAllCloseAccordingToType( np.array([-7.66718769, -10.91273689]), - var0.eval(), + self.evaluate(var0), rtol=1e-4, bfloat16_rtol=1e-1, bfloat16_atol=1e-1) self.assertAllCloseAccordingToType( - np.array([-0.93460727, -1.86147261]), var1.eval(), rtol=1e-4) + np.array([-0.93460727, -1.86147261]), + self.evaluate(var1), + rtol=1e-4) def testFtrlWithL1_L2(self): for dtype in self.float_types: @@ -219,8 +225,8 @@ class FtrlOptimizerTest(xla_test.XLATestCase): ftrl_update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([4.0, 3.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([4.0, 3.0], self.evaluate(var1)) # Run 10 steps FTRL for _ in range(10): @@ -228,9 +234,13 @@ class FtrlOptimizerTest(xla_test.XLATestCase): # Validate updated params self.assertAllCloseAccordingToType( - np.array([-0.24059935, -0.46829352]), var0.eval(), rtol=1e-5) + np.array([-0.24059935, -0.46829352]), + self.evaluate(var0), + rtol=1e-5) self.assertAllCloseAccordingToType( - np.array([-0.02406147, -0.04830509]), var1.eval(), rtol=1e-5) + np.array([-0.02406147, -0.04830509]), + self.evaluate(var1), + rtol=1e-5) def testFtrlWithL1_L2_L2Shrinkage(self): """Test the new FTRL op with support for l2 shrinkage. @@ -254,8 +264,8 @@ class FtrlOptimizerTest(xla_test.XLATestCase): ftrl_update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllCloseAccordingToType([1.0, 2.0], var0.eval()) - self.assertAllCloseAccordingToType([4.0, 3.0], var1.eval()) + self.assertAllCloseAccordingToType([1.0, 2.0], self.evaluate(var0)) + self.assertAllCloseAccordingToType([4.0, 3.0], self.evaluate(var1)) # Run 10 steps FTRL for _ in range(10): @@ -263,9 +273,13 @@ class FtrlOptimizerTest(xla_test.XLATestCase): # Validate updated params self.assertAllCloseAccordingToType( - np.array([-0.22578996, -0.44345799]), var0.eval(), rtol=1e-4) + np.array([-0.22578996, -0.44345799]), + self.evaluate(var0), + rtol=1e-4) self.assertAllCloseAccordingToType( - np.array([-0.14378493, -0.13229476]), var1.eval(), rtol=1e-4) + np.array([-0.14378493, -0.13229476]), + self.evaluate(var1), + rtol=1e-4) def testFtrlWithL2ShrinkageDoesNotChangeLrSchedule(self): """Verifies that l2 shrinkage in FTRL does not change lr schedule.""" @@ -291,8 +305,8 @@ class FtrlOptimizerTest(xla_test.XLATestCase): update1 = opt1.apply_gradients([(grads1, var1)]) variables.global_variables_initializer().run() - self.assertAllCloseAccordingToType([1.0, 2.0], var0.eval()) - self.assertAllCloseAccordingToType([1.0, 2.0], var1.eval()) + self.assertAllCloseAccordingToType([1.0, 2.0], self.evaluate(var0)) + self.assertAllCloseAccordingToType([1.0, 2.0], self.evaluate(var1)) # Run 10 steps FTRL for _ in range(10): @@ -301,7 +315,7 @@ class FtrlOptimizerTest(xla_test.XLATestCase): # var0 is experiencing L2 shrinkage so it should be smaller than var1 # in magnitude. - self.assertTrue((var0.eval()**2 < var1.eval()**2).all()) + self.assertTrue((var0.eval()**2 < self.evaluate(var1)**2).all()) accum0 = list(opt0._slots["accum"].values())[0].eval() accum1 = list(opt1._slots["accum"].values())[0].eval() # L2 shrinkage should not change how we update grad accumulator. diff --git a/tensorflow/compiler/tests/function_test.py b/tensorflow/compiler/tests/function_test.py index b1891b918c..a61827c2ae 100644 --- a/tensorflow/compiler/tests/function_test.py +++ b/tensorflow/compiler/tests/function_test.py @@ -40,7 +40,7 @@ class FunctionTest(xla_test.XLATestCase): bval = np.array([5, 6, 7, 8]).reshape([2, 2]).astype(np.float32) expected = APlus2B(aval, bval) - with self.cached_session() as sess: + with self.cached_session(): @function.Defun(dtypes.float32, dtypes.float32) def Foo(a, b): @@ -50,7 +50,7 @@ class FunctionTest(xla_test.XLATestCase): b = constant_op.constant(bval, name="b") with self.test_scope(): call_f = Foo(a, b) - result = sess.run(call_f) + result = self.evaluate(call_f) self.assertAllClose(result, expected, rtol=1e-3) def testNestedFunctions(self): @@ -66,7 +66,7 @@ class FunctionTest(xla_test.XLATestCase): bval = np.array([4, 3, 2, 1]).reshape([2, 2]).astype(np.float32) expected = APlus2B(aval, bval) - with self.cached_session() as sess: + with self.cached_session(): @function.Defun(dtypes.float32, dtypes.float32) def Foo(a, b): @@ -76,7 +76,7 @@ class FunctionTest(xla_test.XLATestCase): b = constant_op.constant(bval, name="b") with self.test_scope(): call_g = Foo(a, b) - result = sess.run(call_g) + result = self.evaluate(call_g) self.assertAllClose(result, expected, rtol=1e-3) def testFunctionMultipleRetvals(self): @@ -90,7 +90,7 @@ class FunctionTest(xla_test.XLATestCase): bval = np.array([5, 6, 7, 8]).reshape([2, 2]).astype(np.float32) expected = Func(aval, bval) - with self.cached_session() as sess: + with self.cached_session(): @function.Defun(dtypes.float32, dtypes.float32) def Foo(a, b): @@ -100,7 +100,7 @@ class FunctionTest(xla_test.XLATestCase): b = constant_op.constant(bval, name="b") with self.test_scope(): call_f = Foo(a, b) - result = sess.run(call_f) + result = self.evaluate(call_f) self.assertAllClose(result, expected, rtol=1e-3) def testCompileTimeConstantsInDefun(self): diff --git a/tensorflow/compiler/tests/jit_test.py b/tensorflow/compiler/tests/jit_test.py index 6f51ae33a1..dbea9849e2 100644 --- a/tensorflow/compiler/tests/jit_test.py +++ b/tensorflow/compiler/tests/jit_test.py @@ -75,7 +75,7 @@ def RunMetadataLabels(run_metadata): def InLabels(labels, substr): """Returns true iff one of the labels contains substr.""" - return any([substr in x for x in labels]) + return any(substr in x for x in labels) def MetadataHasXlaRunOp(run_metadata): diff --git a/tensorflow/compiler/tests/listdiff_op_test.py b/tensorflow/compiler/tests/listdiff_op_test.py index 58622114e4..0210201fa7 100644 --- a/tensorflow/compiler/tests/listdiff_op_test.py +++ b/tensorflow/compiler/tests/listdiff_op_test.py @@ -33,13 +33,13 @@ class ListDiffTest(xla_test.XLATestCase): def _testListDiff(self, x, y, out, idx): for dtype in [dtypes.int32, dtypes.int64]: for index_dtype in [dtypes.int32, dtypes.int64]: - with self.cached_session() as sess: + with self.cached_session(): x_tensor = ops.convert_to_tensor(x, dtype=dtype) y_tensor = ops.convert_to_tensor(y, dtype=dtype) with self.test_scope(): out_tensor, idx_tensor = array_ops.listdiff( x_tensor, y_tensor, out_idx=index_dtype) - tf_out, tf_idx = sess.run([out_tensor, idx_tensor]) + tf_out, tf_idx = self.evaluate([out_tensor, idx_tensor]) self.assertAllEqual(out, tf_out) self.assertAllEqual(idx, tf_idx) self.assertEqual(1, out_tensor.get_shape().ndims) diff --git a/tensorflow/compiler/tests/lrn_ops_test.py b/tensorflow/compiler/tests/lrn_ops_test.py index c6ad67993e..5dddf6ae4e 100644 --- a/tensorflow/compiler/tests/lrn_ops_test.py +++ b/tensorflow/compiler/tests/lrn_ops_test.py @@ -120,8 +120,8 @@ class LRNTest(xla_test.XLATestCase): with self.test_scope(): actual = gen_nn_ops.lrn_grad(out_grads, in_image, out_image, depth_radius, bias, alpha, beta) - expected_val = expected.eval() - actual_val = actual.eval() + expected_val = self.evaluate(expected) + actual_val = self.evaluate(actual) self.assertAllClose(actual_val, expected_val, rtol=1e-3) diff --git a/tensorflow/compiler/tests/lstm_test.py b/tensorflow/compiler/tests/lstm_test.py index 265c0b6d14..776ed899e6 100644 --- a/tensorflow/compiler/tests/lstm_test.py +++ b/tensorflow/compiler/tests/lstm_test.py @@ -88,8 +88,8 @@ class LSTMTest(test.TestCase): (basename, m_prev_scalar, c_prev_scalar, pad_scalar)) # Initialize variables and run the unrolled LSTM step. - sess.run(variables.global_variables_initializer()) - return sess.run([m, c]) + self.evaluate(variables.global_variables_initializer()) + return self.evaluate([m, c]) def testLSTMCell(self): # Run with all-0 weights, no padding. @@ -173,8 +173,8 @@ class LSTMTest(test.TestCase): (basename, m_init_scalar, c_init_scalar, pad_scalar)) # Initialize variables and run the unrolled LSTM layer. - sess.run(variables.global_variables_initializer()) - return sess.run(out_seq) + self.evaluate(variables.global_variables_initializer()) + return self.evaluate(out_seq) def testLSTMLayer(self): # Run with all-0 weights, no padding. diff --git a/tensorflow/compiler/tests/momentum_test.py b/tensorflow/compiler/tests/momentum_test.py index f77521a7c4..3416f7dbd6 100644 --- a/tensorflow/compiler/tests/momentum_test.py +++ b/tensorflow/compiler/tests/momentum_test.py @@ -61,37 +61,43 @@ class MomentumOptimizerTest(xla_test.XLATestCase): self.assertFalse(slot1 in variables.trainable_variables()) # Fetch params to validate initial values - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([3.0, 4.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) # Step 1: the momentum accumulators where 0. So we should see a normal # update: v -= grad * learning_rate mom_update.run() # Check that the momentum accumulators have been updated. - self.assertAllCloseAccordingToType(np.array([0.1, 0.1]), slot0.eval()) - self.assertAllCloseAccordingToType(np.array([0.01, 0.01]), slot1.eval()) + self.assertAllCloseAccordingToType( + np.array([0.1, 0.1]), self.evaluate(slot0)) + self.assertAllCloseAccordingToType( + np.array([0.01, 0.01]), self.evaluate(slot1)) # Check that the parameters have been updated. self.assertAllCloseAccordingToType( - np.array([1.0 - (0.1 * 2.0), 2.0 - (0.1 * 2.0)]), var0.eval()) + np.array([1.0 - (0.1 * 2.0), 2.0 - (0.1 * 2.0)]), + self.evaluate(var0)) self.assertAllCloseAccordingToType( - np.array([3.0 - (0.01 * 2.0), 4.0 - (0.01 * 2.0)]), var1.eval()) + np.array([3.0 - (0.01 * 2.0), 4.0 - (0.01 * 2.0)]), + self.evaluate(var1)) # Step 2: the momentum accumulators contain the previous update. mom_update.run() # Check that the momentum accumulators have been updated. self.assertAllCloseAccordingToType( - np.array([(0.9 * 0.1 + 0.1), (0.9 * 0.1 + 0.1)]), slot0.eval()) + np.array([(0.9 * 0.1 + 0.1), (0.9 * 0.1 + 0.1)]), + self.evaluate(slot0)) self.assertAllCloseAccordingToType( - np.array([(0.9 * 0.01 + 0.01), (0.9 * 0.01 + 0.01)]), slot1.eval()) + np.array([(0.9 * 0.01 + 0.01), (0.9 * 0.01 + 0.01)]), + self.evaluate(slot1)) # Check that the parameters have been updated. self.assertAllCloseAccordingToType( np.array([ 1.0 - (0.1 * 2.0) - ((0.9 * 0.1 + 0.1) * 2.0), 2.0 - (0.1 * 2.0) - ((0.9 * 0.1 + 0.1) * 2.0) - ]), var0.eval()) + ]), self.evaluate(var0)) self.assertAllCloseAccordingToType( np.array([ - 2.98 - ((0.9 * 0.01 + 0.01) * 2.0), 3.98 - ( - (0.9 * 0.01 + 0.01) * 2.0) - ]), var1.eval()) + 2.98 - ((0.9 * 0.01 + 0.01) * 2.0), + 3.98 - ((0.9 * 0.01 + 0.01) * 2.0) + ]), self.evaluate(var1)) def testNesterovMomentum(self): for dtype in self.float_types: @@ -115,8 +121,8 @@ class MomentumOptimizerTest(xla_test.XLATestCase): var0_np, accum0_np, var0_np * 0.8, 0.1, 0.9) var1_np, accum1_np = self._update_nesterov_momentum_numpy( var1_np, accum1_np, 0.9, 0.1, 0.9) - self.assertAllCloseAccordingToType(var0_np, var0.eval()) - self.assertAllCloseAccordingToType(var1_np, var1.eval()) + self.assertAllCloseAccordingToType(var0_np, self.evaluate(var0)) + self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1)) def testTensorLearningRateAndMomentum(self): for dtype in self.float_types: @@ -141,37 +147,43 @@ class MomentumOptimizerTest(xla_test.XLATestCase): self.assertFalse(slot1 in variables.trainable_variables()) # Fetch params to validate initial values - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([3.0, 4.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) # Step 1: the momentum accumulators where 0. So we should see a normal # update: v -= grad * learning_rate mom_update.run() # Check that the momentum accumulators have been updated. - self.assertAllCloseAccordingToType(np.array([0.1, 0.1]), slot0.eval()) - self.assertAllCloseAccordingToType(np.array([0.01, 0.01]), slot1.eval()) + self.assertAllCloseAccordingToType( + np.array([0.1, 0.1]), self.evaluate(slot0)) + self.assertAllCloseAccordingToType( + np.array([0.01, 0.01]), self.evaluate(slot1)) # Check that the parameters have been updated. self.assertAllCloseAccordingToType( - np.array([1.0 - (0.1 * 2.0), 2.0 - (0.1 * 2.0)]), var0.eval()) + np.array([1.0 - (0.1 * 2.0), 2.0 - (0.1 * 2.0)]), + self.evaluate(var0)) self.assertAllCloseAccordingToType( - np.array([3.0 - (0.01 * 2.0), 4.0 - (0.01 * 2.0)]), var1.eval()) + np.array([3.0 - (0.01 * 2.0), 4.0 - (0.01 * 2.0)]), + self.evaluate(var1)) # Step 2: the momentum accumulators contain the previous update. mom_update.run() # Check that the momentum accumulators have been updated. self.assertAllCloseAccordingToType( - np.array([(0.9 * 0.1 + 0.1), (0.9 * 0.1 + 0.1)]), slot0.eval()) + np.array([(0.9 * 0.1 + 0.1), (0.9 * 0.1 + 0.1)]), + self.evaluate(slot0)) self.assertAllCloseAccordingToType( - np.array([(0.9 * 0.01 + 0.01), (0.9 * 0.01 + 0.01)]), slot1.eval()) + np.array([(0.9 * 0.01 + 0.01), (0.9 * 0.01 + 0.01)]), + self.evaluate(slot1)) # Check that the parameters have been updated. self.assertAllCloseAccordingToType( np.array([ 1.0 - (0.1 * 2.0) - ((0.9 * 0.1 + 0.1) * 2.0), 2.0 - (0.1 * 2.0) - ((0.9 * 0.1 + 0.1) * 2.0) - ]), var0.eval()) + ]), self.evaluate(var0)) self.assertAllCloseAccordingToType( np.array([ - 2.98 - ((0.9 * 0.01 + 0.01) * 2.0), 3.98 - ( - (0.9 * 0.01 + 0.01) * 2.0) - ]), var1.eval()) + 2.98 - ((0.9 * 0.01 + 0.01) * 2.0), + 3.98 - ((0.9 * 0.01 + 0.01) * 2.0) + ]), self.evaluate(var1)) if __name__ == "__main__": diff --git a/tensorflow/compiler/tests/placeholder_test.py b/tensorflow/compiler/tests/placeholder_test.py index 77bb839409..9671ae0ae9 100644 --- a/tensorflow/compiler/tests/placeholder_test.py +++ b/tensorflow/compiler/tests/placeholder_test.py @@ -33,7 +33,7 @@ class PlaceholderTest(xla_test.XLATestCase): ph = array_ops.placeholder_with_default(v, shape=[]) out = ph * 2 sess.run(variables.variables_initializer([v])) - self.assertEqual(8.0, sess.run(out)) + self.assertEqual(8.0, self.evaluate(out)) def test_placeholder_with_default_fed(self): with self.cached_session() as sess, self.test_scope(): diff --git a/tensorflow/compiler/tests/powersign_test.py b/tensorflow/compiler/tests/powersign_test.py index 86536da7fe..5b35c20027 100644 --- a/tensorflow/compiler/tests/powersign_test.py +++ b/tensorflow/compiler/tests/powersign_test.py @@ -91,8 +91,8 @@ class PowerSignTest(xla_test.XLATestCase): variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([3.0, 4.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) # Run 7 steps of powersign # first 4 steps with positive gradient @@ -125,8 +125,8 @@ class PowerSignTest(xla_test.XLATestCase): ) # Validate updated params - self.assertAllCloseAccordingToType(var0_np, var0.eval()) - self.assertAllCloseAccordingToType(var1_np, var1.eval()) + self.assertAllCloseAccordingToType(var0_np, self.evaluate(var0)) + self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1)) def testDense(self): decay_steps = 10 diff --git a/tensorflow/compiler/tests/proximal_adagrad_test.py b/tensorflow/compiler/tests/proximal_adagrad_test.py index c41b4171e2..63cc51a470 100644 --- a/tensorflow/compiler/tests/proximal_adagrad_test.py +++ b/tensorflow/compiler/tests/proximal_adagrad_test.py @@ -45,15 +45,17 @@ class ProximalAdagradOptimizerTest(xla_test.XLATestCase): update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() - self.assertAllClose([0.0, 0.0], var0.eval()) - self.assertAllClose([0.0, 0.0], var1.eval()) + self.assertAllClose([0.0, 0.0], self.evaluate(var0)) + self.assertAllClose([0.0, 0.0], self.evaluate(var1)) # Run 3 steps Proximal Adagrad. for _ in range(3): update.run() - self.assertAllClose(np.array([-2.60260963, -4.29698515]), var0.eval()) - self.assertAllClose(np.array([-0.28432083, -0.56694895]), var1.eval()) + self.assertAllClose( + np.array([-2.60260963, -4.29698515]), self.evaluate(var0)) + self.assertAllClose( + np.array([-0.28432083, -0.56694895]), self.evaluate(var1)) opt_vars = opt.variables() self.assertStartsWith(opt_vars[0].name, var0._shared_name) self.assertStartsWith(opt_vars[1].name, var1._shared_name) @@ -74,14 +76,14 @@ class ProximalAdagradOptimizerTest(xla_test.XLATestCase): update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([4.0, 3.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([4.0, 3.0], self.evaluate(var1)) # Run 3 steps Proximal Adagrad. for _ in range(3): update.run() - self.assertAllClose(np.array([-1.60261, -2.296985]), var0.eval()) - self.assertAllClose(np.array([3.715679, 2.433051]), var1.eval()) + self.assertAllClose(np.array([-1.60261, -2.296985]), self.evaluate(var0)) + self.assertAllClose(np.array([3.715679, 2.433051]), self.evaluate(var1)) def testProximalAdagradWithL1(self): with self.cached_session(), self.test_scope(): @@ -98,14 +100,14 @@ class ProximalAdagradOptimizerTest(xla_test.XLATestCase): update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([4.0, 3.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([4.0, 3.0], self.evaluate(var1)) # Run 10 steps Proximal Adagrad for _ in range(10): update.run() - self.assertAllClose(np.array([-6.663634, -9.190331]), var0.eval()) - self.assertAllClose(np.array([2.959304, 1.029232]), var1.eval()) + self.assertAllClose(np.array([-6.663634, -9.190331]), self.evaluate(var0)) + self.assertAllClose(np.array([2.959304, 1.029232]), self.evaluate(var1)) def testProximalAdagradWithL1_L2(self): with self.cached_session(), self.test_scope(): @@ -122,15 +124,15 @@ class ProximalAdagradOptimizerTest(xla_test.XLATestCase): update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([4.0, 3.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([4.0, 3.0], self.evaluate(var1)) # Run 10 steps Proximal Adagrad. for _ in range(10): update.run() - self.assertAllClose(np.array([-0.0495, -0.0995]), var0.eval()) - self.assertAllClose(np.array([-0.0045, -0.0095]), var1.eval()) + self.assertAllClose(np.array([-0.0495, -0.0995]), self.evaluate(var0)) + self.assertAllClose(np.array([-0.0045, -0.0095]), self.evaluate(var1)) def applyOptimizer(self, opt, steps=5): var0 = resource_variable_ops.ResourceVariable([1.0, 2.0]) @@ -141,14 +143,14 @@ class ProximalAdagradOptimizerTest(xla_test.XLATestCase): update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([3.0, 4.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) # Run ProximalAdagrad for a few steps for _ in range(steps): update.run() - return var0.eval(), var1.eval() + return self.evaluate(var0), self.evaluate(var1) def testEquivAdagradwithoutRegularization(self): with self.cached_session(), self.test_scope(): diff --git a/tensorflow/compiler/tests/proximal_gradient_descent_test.py b/tensorflow/compiler/tests/proximal_gradient_descent_test.py index 3d808e6b8a..5aec433be7 100644 --- a/tensorflow/compiler/tests/proximal_gradient_descent_test.py +++ b/tensorflow/compiler/tests/proximal_gradient_descent_test.py @@ -42,15 +42,15 @@ class ProximalGradientDescentOptimizerTest(xla_test.XLATestCase): update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() - self.assertAllClose([0.0, 0.0], var0.eval()) - self.assertAllClose([0.0, 0.0], var1.eval()) + self.assertAllClose([0.0, 0.0], self.evaluate(var0)) + self.assertAllClose([0.0, 0.0], self.evaluate(var1)) # Run 3 steps Proximal Gradient Descent. for _ in range(3): update.run() - self.assertAllClose(np.array([-0.9, -1.8]), var0.eval()) - self.assertAllClose(np.array([-0.09, -0.18]), var1.eval()) + self.assertAllClose(np.array([-0.9, -1.8]), self.evaluate(var0)) + self.assertAllClose(np.array([-0.09, -0.18]), self.evaluate(var1)) def testProximalGradientDescentwithoutRegularization2(self): with self.cached_session(), self.test_scope(): @@ -64,15 +64,15 @@ class ProximalGradientDescentOptimizerTest(xla_test.XLATestCase): update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([4.0, 3.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([4.0, 3.0], self.evaluate(var1)) # Run 3 steps Proximal Gradient Descent for _ in range(3): update.run() - self.assertAllClose(np.array([0.1, 0.2]), var0.eval()) - self.assertAllClose(np.array([3.91, 2.82]), var1.eval()) + self.assertAllClose(np.array([0.1, 0.2]), self.evaluate(var0)) + self.assertAllClose(np.array([3.91, 2.82]), self.evaluate(var1)) def testProximalGradientDescentWithL1(self): with self.cached_session(), self.test_scope(): @@ -86,15 +86,15 @@ class ProximalGradientDescentOptimizerTest(xla_test.XLATestCase): update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([4.0, 3.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([4.0, 3.0], self.evaluate(var1)) # Run 10 steps proximal gradient descent. for _ in range(10): update.run() - self.assertAllClose(np.array([-1.988, -3.988001]), var0.eval()) - self.assertAllClose(np.array([3.67, 2.37]), var1.eval()) + self.assertAllClose(np.array([-1.988, -3.988001]), self.evaluate(var0)) + self.assertAllClose(np.array([3.67, 2.37]), self.evaluate(var1)) def testProximalGradientDescentWithL1_L2(self): with self.cached_session(), self.test_scope(): @@ -108,15 +108,15 @@ class ProximalGradientDescentOptimizerTest(xla_test.XLATestCase): update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([4.0, 3.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([4.0, 3.0], self.evaluate(var1)) # Run 10 steps Proximal Gradient Descent for _ in range(10): update.run() - self.assertAllClose(np.array([-0.0495, -0.0995]), var0.eval()) - self.assertAllClose(np.array([-0.0045, -0.0095]), var1.eval()) + self.assertAllClose(np.array([-0.0495, -0.0995]), self.evaluate(var0)) + self.assertAllClose(np.array([-0.0045, -0.0095]), self.evaluate(var1)) def applyOptimizer(self, opt, steps=5): var0 = resource_variable_ops.ResourceVariable([1.0, 2.0]) @@ -127,14 +127,14 @@ class ProximalGradientDescentOptimizerTest(xla_test.XLATestCase): update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([3.0, 4.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) # Run ProximalAdagrad for a few steps for _ in range(steps): update.run() - return var0.eval(), var1.eval() + return self.evaluate(var0), self.evaluate(var1) def testEquivGradientDescentwithoutRegularization(self): with self.cached_session(), self.test_scope(): diff --git a/tensorflow/compiler/tests/qr_op_test.py b/tensorflow/compiler/tests/qr_op_test.py index 236b1b881d..b4d4193e35 100644 --- a/tensorflow/compiler/tests/qr_op_test.py +++ b/tensorflow/compiler/tests/qr_op_test.py @@ -63,7 +63,7 @@ class QrOpTest(xla_test.XLATestCase, parameterized.TestCase): # Tests that x[...,:,:]^H * x[...,:,:] is close to the identity. xx = math_ops.matmul(x, x, adjoint_a=True) identity = array_ops.matrix_band_part(array_ops.ones_like(xx), 0, 0) - precision = self.AdjustedNorm(xx.eval() - identity.eval()) + precision = self.AdjustedNorm(xx.eval() - self.evaluate(identity)) self.assertTrue(np.all(precision < 5.0)) def _test(self, dtype, shape, full_matrices): diff --git a/tensorflow/compiler/tests/random_ops_test.py b/tensorflow/compiler/tests/random_ops_test.py index 36ef6ed5fe..97ffad34c0 100644 --- a/tensorflow/compiler/tests/random_ops_test.py +++ b/tensorflow/compiler/tests/random_ops_test.py @@ -46,9 +46,9 @@ class RandomOpsTest(xla_test.XLATestCase): # The random-number generator, if working correctly, should produce the # same output multiple times with low probability. - y = sess.run(x) - z = sess.run(x) - w = sess.run(x) + y = self.evaluate(x) + z = self.evaluate(x) + w = self.evaluate(x) # We use exact equality here. If the random-number generator is producing # deterministic output, all three outputs will be bitwise identical. @@ -83,7 +83,7 @@ class RandomOpsTest(xla_test.XLATestCase): with self.test_scope(): x = random_ops.random_uniform( shape=[1000], dtype=dtype, minval=-2, maxval=33) - y = sess.run(x) + y = self.evaluate(x) self.assertTrue((y >= -2).sum() == 1000) self.assertTrue((y < 33).sum() == 1000) @@ -102,7 +102,7 @@ class RandomOpsTest(xla_test.XLATestCase): with self.cached_session() as sess: with self.test_scope(): x = random_ops.truncated_normal(shape=[count], dtype=dtype) - y = sess.run(x) + y = self.evaluate(x) def normal_cdf(x): return .5 * math.erfc(-x / math.sqrt(2)) @@ -111,7 +111,7 @@ class RandomOpsTest(xla_test.XLATestCase): return math.exp(-(x**2) / 2.) / math.sqrt(2 * math.pi) def probit(x, sess=sess): - return sess.run(special_math.ndtri(x)) + return self.evaluate(special_math.ndtri(x)) a = -2. b = 2. @@ -148,7 +148,7 @@ class RandomOpsTest(xla_test.XLATestCase): with self.test_scope(): x = math_ops.range(1 << 16) shuffle = random_ops.random_shuffle(x) - result = sess.run(shuffle) + result = self.evaluate(shuffle) expected = range(1 << 16) # Compare sets to avoid randomness behavior changes but make sure still # have all the values. @@ -159,7 +159,7 @@ class RandomOpsTest(xla_test.XLATestCase): with self.test_scope(): x = array_ops.diag(math_ops.range(20)) shuffle = random_ops.random_shuffle(x) - result = sess.run(shuffle) + result = self.evaluate(shuffle) expected = np.diag(range(20)).flatten() # Compare sets to avoid randomness behavior changes but make sure still # have all the values. diff --git a/tensorflow/compiler/tests/randomized_tests.cc b/tensorflow/compiler/tests/randomized_tests.cc index a6b5802012..d23fd12516 100644 --- a/tensorflow/compiler/tests/randomized_tests.cc +++ b/tensorflow/compiler/tests/randomized_tests.cc @@ -3382,10 +3382,10 @@ int main(int argc, char** argv) { } // XLA devices register kernels at construction time; create all known devices // to make sure the kernels are registered. - std::vector devices; + std::vector> devices; TF_CHECK_OK(tensorflow::DeviceFactory::AddDevices( tensorflow::SessionOptions(), "", &devices)); - tensorflow::DeviceMgr device_mgr(devices); + tensorflow::DeviceMgr device_mgr(std::move(devices)); tensorflow::Device* ignored; TF_QCHECK_OK( diff --git a/tensorflow/compiler/tests/reduce_ops_test.py b/tensorflow/compiler/tests/reduce_ops_test.py index 132c59c32c..e8fc81bbb5 100644 --- a/tensorflow/compiler/tests/reduce_ops_test.py +++ b/tensorflow/compiler/tests/reduce_ops_test.py @@ -91,6 +91,7 @@ class ReduceOpsTest(xla_test.XLATestCase, parameterized.TestCase): np.array([], dtype=np.bool).reshape(0, 3), np.array([[False, True, False], [True, True, False]]), ] + ONES = [np.ones([34000, 2])] def testReduceSumF32(self, index_dtype): self._testReduction(math_ops.reduce_sum, np.sum, np.float32, self.REAL_DATA, @@ -149,6 +150,11 @@ class ReduceOpsTest(xla_test.XLATestCase, parameterized.TestCase): self._testReduction(math_ops.reduce_mean, np.mean, np.float32, self.NONEMPTY_REAL_DATA, index_dtype) + def testReduceMeanF16(self, index_dtype): + if np.float16 in self.all_types: + self._testReduction(math_ops.reduce_mean, np.mean, np.float16, self.ONES, + index_dtype) + def testReduceMeanC64(self, index_dtype): self._testReduction(math_ops.reduce_mean, np.mean, np.complex64, self.NONEMPTY_COMPLEX_DATA, index_dtype) diff --git a/tensorflow/compiler/tests/rmsprop_test.py b/tensorflow/compiler/tests/rmsprop_test.py index 8840a1329a..dc3e90b4af 100644 --- a/tensorflow/compiler/tests/rmsprop_test.py +++ b/tensorflow/compiler/tests/rmsprop_test.py @@ -76,7 +76,7 @@ class RmspropTest(xla_test.XLATestCase): rms_opt = rmsprop.RMSPropOptimizer(learning_rate, centered=centered) rms_update = rms_opt.apply_gradients( zip([grads0, grads1], [var0, var1])) - variables.global_variables_initializer().run() + self.evaluate(variables.global_variables_initializer()) mg0 = rms_opt.get_slot(var0, "mg") self.assertEqual(mg0 is not None, centered) @@ -92,12 +92,12 @@ class RmspropTest(xla_test.XLATestCase): self.assertTrue(mom1 is not None) # Fetch params to validate initial values - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([3.0, 4.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) # Run 3 steps of RMSProp for _ in range(3): - rms_update.run() + self.evaluate(rms_update) var0_np, mg0_np, rms0_np, mom0_np = self._rmsprop_update_numpy( var0_np, @@ -118,14 +118,14 @@ class RmspropTest(xla_test.XLATestCase): # Validate updated params if centered: - self.assertAllCloseAccordingToType(mg0_np, mg0.eval()) - self.assertAllCloseAccordingToType(mg1_np, mg1.eval()) - self.assertAllCloseAccordingToType(rms0_np, rms0.eval()) - self.assertAllCloseAccordingToType(rms1_np, rms1.eval()) - self.assertAllCloseAccordingToType(mom0_np, mom0.eval()) - self.assertAllCloseAccordingToType(mom1_np, mom1.eval()) - self.assertAllCloseAccordingToType(var0_np, var0.eval()) - self.assertAllCloseAccordingToType(var1_np, var1.eval()) + self.assertAllCloseAccordingToType(mg0_np, self.evaluate(mg0)) + self.assertAllCloseAccordingToType(mg1_np, self.evaluate(mg1)) + self.assertAllCloseAccordingToType(rms0_np, self.evaluate(rms0)) + self.assertAllCloseAccordingToType(rms1_np, self.evaluate(rms1)) + self.assertAllCloseAccordingToType(mom0_np, self.evaluate(mom0)) + self.assertAllCloseAccordingToType(mom1_np, self.evaluate(mom1)) + self.assertAllCloseAccordingToType(var0_np, self.evaluate(var0)) + self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1)) if __name__ == "__main__": diff --git a/tensorflow/compiler/tests/scan_ops_test.py b/tensorflow/compiler/tests/scan_ops_test.py index 897db384b7..17639bd8a7 100644 --- a/tensorflow/compiler/tests/scan_ops_test.py +++ b/tensorflow/compiler/tests/scan_ops_test.py @@ -71,7 +71,7 @@ def handle_options(func, x, axis, exclusive, reverse): class CumsumTest(xla_test.XLATestCase): - valid_dtypes = [np.float32] + valid_dtypes = [np.float32, np.int32] def axis_dtypes(self): return set(self.int_types).intersection([np.int32, np.int64]) @@ -149,7 +149,7 @@ class CumsumTest(xla_test.XLATestCase): class CumprodTest(xla_test.XLATestCase): - valid_dtypes = [np.float32] + valid_dtypes = [np.float32, np.int32] def axis_dtypes(self): return set(self.int_types).intersection([np.int32, np.int64]) diff --git a/tensorflow/compiler/tests/stateless_random_ops_test.py b/tensorflow/compiler/tests/stateless_random_ops_test.py index 21708aa158..ee7ca7e6f1 100644 --- a/tensorflow/compiler/tests/stateless_random_ops_test.py +++ b/tensorflow/compiler/tests/stateless_random_ops_test.py @@ -156,7 +156,7 @@ class StatelessRandomOpsTest(xla_test.XLATestCase): return math.exp(-(x**2) / 2.) / math.sqrt(2 * math.pi) def probit(x, sess=sess): - return sess.run(special_math.ndtri(x)) + return self.evaluate(special_math.ndtri(x)) a = -2. b = 2. diff --git a/tensorflow/compiler/tests/tensor_array_ops_test.py b/tensorflow/compiler/tests/tensor_array_ops_test.py index 46ca371c8a..d7e26d79c4 100644 --- a/tensorflow/compiler/tests/tensor_array_ops_test.py +++ b/tensorflow/compiler/tests/tensor_array_ops_test.py @@ -79,7 +79,8 @@ class TensorArrayTest(xla_test.XLATestCase): c0 = w2.stack() self.assertAllEqual( - convert([[[4.0, 5.0]], [[6.0, 7.0]], [[8.0, 9.0]]]), c0.eval()) + convert([[[4.0, 5.0]], [[6.0, 7.0]], [[8.0, 9.0]]]), + self.evaluate(c0)) def testTensorArrayWritePack(self): for dtype in self.numeric_tf_types: @@ -97,7 +98,7 @@ class TensorArrayTest(xla_test.XLATestCase): c0 = w2.stack() - self.assertAllEqual([3, 0, 1], c0.eval().shape) + self.assertAllEqual([3, 0, 1], self.evaluate(c0).shape) def _testTensorArrayWriteConcat(self, tf_dtype): with self.cached_session(), self.test_scope(): @@ -113,8 +114,8 @@ class TensorArrayTest(xla_test.XLATestCase): c0 = w2.concat() self.assertAllEqual( - convert([[4.0, 5.0], [104.0, 105.0], [6.0, 7.0], - [106.0, 107.0], [8.0, 9.0], [204.0, 205.0]]), c0.eval()) + convert([[4.0, 5.0], [104.0, 105.0], [6.0, 7.0], [106.0, 107.0], + [8.0, 9.0], [204.0, 205.0]]), self.evaluate(c0)) def testTensorArrayWriteConcat(self): for dtype in self.numeric_tf_types: @@ -341,7 +342,7 @@ class TensorArrayTest(xla_test.XLATestCase): r0_bad = gen_data_flow_ops.tensor_array_read_v3( handle=w0.handle, index=0, dtype=dtype2, flow_in=w0.flow) with self.assertRaisesOpError("TensorArray dtype is "): - r0_bad.eval() + self.evaluate(r0_bad) # Test reading from a different index than the one we wrote to w0.read(1) @@ -422,7 +423,7 @@ class TensorArrayTest(xla_test.XLATestCase): w2 = h2.write(0, 5.0) r2 = w2.read(0) r = r1 + r2 - self.assertAllClose(9.0, r.eval()) + self.assertAllClose(9.0, self.evaluate(r)) def _testTensorArrayGradientWriteReadType(self, dtype): with self.cached_session() as session, self.test_scope(): @@ -504,7 +505,7 @@ class TensorArrayTest(xla_test.XLATestCase): [-0.5, 1.5], # read(0) gradient [20.0, 30.0, 40.0, 50.0], # concat gradient ]) - grad_vals = sess.run(grad_r) # 2 + 2 entries + grad_vals = self.evaluate(grad_r) # 2 + 2 entries self.assertAllClose([2.0 - 0.5 + 20.0, 3.0 + 1.5 + 30.0], grad_vals[0]) self.assertAllEqual([4.0 + 40.0, 5.0 + 50.0], grad_vals[1]) @@ -526,7 +527,7 @@ class TensorArrayTest(xla_test.XLATestCase): with ops.control_dependencies([r0_readtwice]): r1_readtwice = w_readtwice.read(0) - self.assertAllEqual([1.0, -1.0], r1_readtwice.eval()) + self.assertAllEqual([1.0, -1.0], self.evaluate(r1_readtwice)) def _testTensorArrayGradientUnpackRead(self): with self.cached_session() as session, self.test_scope(): @@ -592,7 +593,7 @@ class TensorArrayTest(xla_test.XLATestCase): ta = tensor_array_ops.TensorArray( dtype=dtypes.float32, tensor_array_name="foo", size=3) s = ta.size() - self.assertAllEqual(3, s.eval()) + self.assertAllEqual(3, self.evaluate(s)) def testWriteCloseTensorArray(self): with self.cached_session(), self.test_scope(): @@ -722,7 +723,7 @@ class TensorArrayTest(xla_test.XLATestCase): # r = acc2.stack() # grad = gradients_impl.gradients(r, [x])[0] - # self.assertAllClose(31.0, grad.eval()) + # self.assertAllClose(31.0, self.evaluate(grad)) def testSumOfTwoReadVariablesWithoutRepeatGrad(self): with self.cached_session() as session, self.test_scope(): @@ -912,7 +913,7 @@ class TensorArrayTest(xla_test.XLATestCase): self.assertEqual(0, ta.size().eval()) ta = ta.unstack(array_ops.zeros([0, 3, 5])) packed = ta.stack() - self.assertAllEqual([0, 3, 5], packed.eval().shape) + self.assertAllEqual([0, 3, 5], self.evaluate(packed).shape) # Concatenating zero tensors along their first dimension gives a # first dimension of zero self.assertAllEqual([0, 5], ta.concat().eval().shape) @@ -1041,8 +1042,8 @@ class TensorArrayTest(xla_test.XLATestCase): (read0, read1, size0, size1)) # Tests that the control dependencies was added and executed. - self.assertEqual(1, v0.eval()) - self.assertEqual(1, v1.eval()) + self.assertEqual(1, self.evaluate(v0)) + self.assertEqual(1, self.evaluate(v1)) # Tests correct TensorArray. self.assertEqual(read0_v, 0) diff --git a/tensorflow/compiler/tests/unary_ops_test.py b/tensorflow/compiler/tests/unary_ops_test.py index d612d3b32d..95c9e7ffd4 100644 --- a/tensorflow/compiler/tests/unary_ops_test.py +++ b/tensorflow/compiler/tests/unary_ops_test.py @@ -481,6 +481,72 @@ class UnaryOpsTest(xla_test.XLATestCase): np.array([-1, -0.5, 0, 0.3], dtype=dtype), expected=np.array([-1., -0.5, 0., 0.296875], dtype=dtype)) + def quantize_and_dequantize_v2_round_half_up(x): + return array_ops.quantize_and_dequantize_v2( + x, + -1, + 1.0, + signed_input=True, + num_bits=8, + range_given=True, + round_mode="HALF_UP") + + self._assertOpOutputMatchesExpected( + quantize_and_dequantize_v2_round_half_up, + np.array([-0.8, -0.5, 0, 0.3, 0.8, -2, 33], dtype=dtype), + expected=np.array([ + -102.0 / 127, + -63.0 / 127, + 0, + 38.0 / 127, + 102.0 / 127, + -128.0 / 127, + 1, + ], + dtype=dtype)) + + def quantize_and_dequantize_v2_round_half_to_even(x): + return array_ops.quantize_and_dequantize_v2( + x, + -1.0, + 1.0, + signed_input=True, + num_bits=8, + range_given=True, + round_mode="HALF_TO_EVEN") + + self._assertOpOutputMatchesExpected( + quantize_and_dequantize_v2_round_half_to_even, + np.array( + [ + -0.8, + # The -0.5 should become -63.5 after scaling and with + # rounding this should become -64. But with the test + # unary_ops_test_cpu_ondemand, this fails as the result + # before scaling becomes -63.499996 and gets rounded to -63. + # TODO(sreenik): Some one more familiar with this test needs + # to take a look and resolve this. This works on all other + # variations of the platform like cpu, and gpu. + # -0.5, + 0, + 0.3, + 0.8, + -2, + 33 + ], + dtype=dtype), + expected=np.array( + [ + -102.0 / 127, + # -64.0 / 127, + 0, + 38.0 / 127, + 102.0 / 127, + -128.0 / 127, + 1, + ], + dtype=dtype)) + def quantize_and_dequantize_v3(x): return array_ops.quantize_and_dequantize_v3( x, -127, 127, num_bits=8, signed_input=True, range_given=False) diff --git a/tensorflow/compiler/tests/variable_ops_test.py b/tensorflow/compiler/tests/variable_ops_test.py index 77cdeac816..fcd7ac5ba1 100644 --- a/tensorflow/compiler/tests/variable_ops_test.py +++ b/tensorflow/compiler/tests/variable_ops_test.py @@ -77,7 +77,7 @@ class VariableOpsTest(xla_test.XLATestCase): sess.run(variables.variables_initializer([v])) x = v.sparse_read(2) self.assertAllClose( - np.array([8j, 9, 10, 11]).astype(dtype), sess.run(x)) + np.array([8j, 9, 10, 11]).astype(dtype), self.evaluate(x)) def testSparseRead1DIndices(self): for dtype in self.numeric_types: @@ -89,7 +89,7 @@ class VariableOpsTest(xla_test.XLATestCase): x = v.sparse_read([2, 1]) self.assertAllClose( np.array([[8, 9, 10, 11], [4, 5, 6j, 7]]).astype(dtype), - sess.run(x)) + self.evaluate(x)) def testSparseRead2DIndices(self): for dtype in self.numeric_types: @@ -102,7 +102,7 @@ class VariableOpsTest(xla_test.XLATestCase): self.assertAllClose( np.array([[[8, 9, 10, 11], [4, 5, 6, 7]], [[0, 1, 2j, 3], [8, 9, 10, 11]]]).astype(dtype), - sess.run(x)) + self.evaluate(x)) def testSparseRead2DIndices3DTensor(self): for dtype in self.numeric_types: @@ -115,9 +115,9 @@ class VariableOpsTest(xla_test.XLATestCase): x = v.sparse_read([[2, 1], [3, 0]]) self.assertAllClose( np.array( - [[[[20, 21, 22], [23, 24j, 25]], [[10, 11, 12], [13, 14, 15]] - ], [[[30, 31, 32], [33, 34, 35]], [[0, 1, 2], [3, 4, 5]]] - ],).astype(dtype), sess.run(x)) + [[[[20, 21, 22], [23, 24j, 25]], [[10, 11, 12], [13, 14, 15]]], + [[[30, 31, 32], [33, 34, 35]], [[0, 1, 2], [3, 4, 5]]] + ],).astype(dtype), self.evaluate(x)) def testShape(self): for dtype in self.numeric_types: @@ -229,7 +229,7 @@ class VariableOpsTest(xla_test.XLATestCase): resource_variable_ops.resource_scatter_add( handle, [0], constant_op.constant([[2]], dtype=dtypes.int32))) read = resource_variable_ops.read_variable_op(handle, dtype=dtypes.int32) - self.assertAllEqual(sess.run(read), [[3], [7]]) + self.assertAllEqual(self.evaluate(read), [[3], [7]]) def testScatterSub(self): with self.test_session() as sess, self.test_scope(): @@ -242,7 +242,7 @@ class VariableOpsTest(xla_test.XLATestCase): resource_variable_ops.resource_scatter_sub( handle, [1], constant_op.constant([[2]], dtype=dtypes.int32))) read = resource_variable_ops.read_variable_op(handle, dtype=dtypes.int32) - self.assertAllEqual(sess.run(read), [[4], [-1]]) + self.assertAllEqual(self.evaluate(read), [[4], [-1]]) def testScatterMul(self): with self.test_session() as sess, self.test_scope(): @@ -255,7 +255,7 @@ class VariableOpsTest(xla_test.XLATestCase): resource_variable_ops.resource_scatter_mul( handle, [0], constant_op.constant([[5]], dtype=dtypes.int32))) read = resource_variable_ops.read_variable_op(handle, dtype=dtypes.int32) - self.assertEqual(sess.run(read), [[5]]) + self.assertEqual(self.evaluate(read), [[5]]) def testScatterDiv(self): with self.test_session() as sess, self.test_scope(): @@ -268,7 +268,7 @@ class VariableOpsTest(xla_test.XLATestCase): resource_variable_ops.resource_scatter_div( handle, [0], constant_op.constant([[3]], dtype=dtypes.int32))) read = resource_variable_ops.read_variable_op(handle, dtype=dtypes.int32) - self.assertAllEqual(sess.run(read), [[2]]) + self.assertAllEqual(self.evaluate(read), [[2]]) def testScatterMin(self): with self.test_session() as sess, self.test_scope(): @@ -281,7 +281,7 @@ class VariableOpsTest(xla_test.XLATestCase): resource_variable_ops.resource_scatter_min( handle, [0], constant_op.constant([[3]], dtype=dtypes.int32))) read = resource_variable_ops.read_variable_op(handle, dtype=dtypes.int32) - self.assertEqual(sess.run(read), [[3]]) + self.assertEqual(self.evaluate(read), [[3]]) def testScatterMax(self): with self.test_session() as sess, self.test_scope(): @@ -294,7 +294,7 @@ class VariableOpsTest(xla_test.XLATestCase): resource_variable_ops.resource_scatter_max( handle, [0], constant_op.constant([[3]], dtype=dtypes.int32))) read = resource_variable_ops.read_variable_op(handle, dtype=dtypes.int32) - self.assertEqual(sess.run(read), [[6]]) + self.assertEqual(self.evaluate(read), [[6]]) def testScatterUpdate(self): with self.test_session() as sess, self.test_scope(): @@ -307,7 +307,7 @@ class VariableOpsTest(xla_test.XLATestCase): resource_variable_ops.resource_scatter_update( handle, [0], constant_op.constant([[3]], dtype=dtypes.int32))) read = resource_variable_ops.read_variable_op(handle, dtype=dtypes.int32) - self.assertEqual(sess.run(read), [[3]]) + self.assertEqual(self.evaluate(read), [[3]]) def testScatterAddScalar(self): with self.test_session() as sess, self.test_scope(): @@ -320,7 +320,7 @@ class VariableOpsTest(xla_test.XLATestCase): resource_variable_ops.resource_scatter_add( handle, [0], constant_op.constant(2, dtype=dtypes.int32))) read = resource_variable_ops.read_variable_op(handle, dtype=dtypes.int32) - self.assertEqual(sess.run(read), [[3]]) + self.assertEqual(self.evaluate(read), [[3]]) def testScatterSubScalar(self): with self.test_session() as sess, self.test_scope(): @@ -333,7 +333,7 @@ class VariableOpsTest(xla_test.XLATestCase): resource_variable_ops.resource_scatter_sub( handle, [0], constant_op.constant(2, dtype=dtypes.int32))) read = resource_variable_ops.read_variable_op(handle, dtype=dtypes.int32) - self.assertEqual(sess.run(read), [[-1]]) + self.assertEqual(self.evaluate(read), [[-1]]) def testScatterMulScalar(self): with self.test_session() as sess, self.test_scope(): @@ -346,7 +346,7 @@ class VariableOpsTest(xla_test.XLATestCase): resource_variable_ops.resource_scatter_mul( handle, [0], constant_op.constant(5, dtype=dtypes.int32))) read = resource_variable_ops.read_variable_op(handle, dtype=dtypes.int32) - self.assertEqual(sess.run(read), [[5]]) + self.assertEqual(self.evaluate(read), [[5]]) def testScatterDivScalar(self): with self.test_session() as sess, self.test_scope(): @@ -359,7 +359,7 @@ class VariableOpsTest(xla_test.XLATestCase): resource_variable_ops.resource_scatter_div( handle, [0], constant_op.constant(3, dtype=dtypes.int32))) read = resource_variable_ops.read_variable_op(handle, dtype=dtypes.int32) - self.assertEqual(sess.run(read), [[2]]) + self.assertEqual(self.evaluate(read), [[2]]) def testScatterMinScalar(self): with self.test_session() as sess, self.test_scope(): @@ -372,7 +372,7 @@ class VariableOpsTest(xla_test.XLATestCase): resource_variable_ops.resource_scatter_min( handle, [0], constant_op.constant(3, dtype=dtypes.int32))) read = resource_variable_ops.read_variable_op(handle, dtype=dtypes.int32) - self.assertEqual(sess.run(read), [[3]]) + self.assertEqual(self.evaluate(read), [[3]]) def testScatterMaxScalar(self): with self.test_session() as sess, self.test_scope(): @@ -385,7 +385,7 @@ class VariableOpsTest(xla_test.XLATestCase): resource_variable_ops.resource_scatter_max( handle, [0], constant_op.constant(3, dtype=dtypes.int32))) read = resource_variable_ops.read_variable_op(handle, dtype=dtypes.int32) - self.assertEqual(sess.run(read), [[6]]) + self.assertEqual(self.evaluate(read), [[6]]) def testScatterNdAddOps(self): with self.test_session() as sess, self.test_scope(): @@ -400,7 +400,7 @@ class VariableOpsTest(xla_test.XLATestCase): sess.run(gen_state_ops.resource_scatter_nd_add(handle, indices, updates)) read = resource_variable_ops.read_variable_op( handle, dtype=dtypes.float32) - self.assertAllClose(expected, sess.run(read)) + self.assertAllClose(expected, self.evaluate(read)) def testScatterNdUpdateAddOps(self): with self.test_session() as sess, self.test_scope(): @@ -416,7 +416,7 @@ class VariableOpsTest(xla_test.XLATestCase): gen_state_ops.resource_scatter_nd_update(handle, indices, updates)) read = resource_variable_ops.read_variable_op( handle, dtype=dtypes.float32) - self.assertAllClose(expected, sess.run(read)) + self.assertAllClose(expected, self.evaluate(read)) class StridedSliceAssignChecker(object): diff --git a/tensorflow/compiler/tests/xla_device_test.py b/tensorflow/compiler/tests/xla_device_test.py index 28d61fb07d..ef55292b1b 100644 --- a/tensorflow/compiler/tests/xla_device_test.py +++ b/tensorflow/compiler/tests/xla_device_test.py @@ -81,7 +81,7 @@ class XlaDeviceTest(xla_test.XLATestCase): with self.cached_session() as sess: with self.test_scope(): x = gen_control_flow_ops.control_trigger() - sess.run(x) + self.evaluate(x) if __name__ == "__main__": diff --git a/tensorflow/compiler/tf2xla/BUILD b/tensorflow/compiler/tf2xla/BUILD index e017141549..5a0d9b9af9 100644 --- a/tensorflow/compiler/tf2xla/BUILD +++ b/tensorflow/compiler/tf2xla/BUILD @@ -9,6 +9,7 @@ package_group( "//tensorflow/compiler/jit/...", "//tensorflow/compiler/tests/...", "//tensorflow/compiler/tf2xla/...", + "//tensorflow/contrib/compiler/...", ], ) @@ -195,8 +196,8 @@ cc_library( ":sharding_util", ":side_effect_util", ":tf2xla_util", + "//tensorflow/compiler/jit:flags", "//tensorflow/compiler/jit:xla_cluster_util", - "//tensorflow/compiler/jit/legacy_flags:mark_for_compilation_pass_flags", "//tensorflow/compiler/tf2xla/lib:util", "//tensorflow/compiler/xla:literal", "//tensorflow/compiler/xla:shape_util", @@ -204,13 +205,13 @@ cc_library( "//tensorflow/compiler/xla:statusor", "//tensorflow/compiler/xla:types", "//tensorflow/compiler/xla:xla_data_proto", + "//tensorflow/compiler/xla/client", "//tensorflow/compiler/xla/client:client_library", "//tensorflow/compiler/xla/client:local_client", "//tensorflow/compiler/xla/client:xla_builder", "//tensorflow/compiler/xla/client:xla_computation", "//tensorflow/compiler/xla/client/lib:arithmetic", "//tensorflow/compiler/xla/client/lib:constants", - "//tensorflow/compiler/xla/client/lib:numeric", "//tensorflow/core:core_cpu", "//tensorflow/core:core_cpu_internal", "//tensorflow/core:framework", @@ -221,6 +222,7 @@ cc_library( "@com_google_absl//absl/algorithm:container", "@com_google_absl//absl/memory", "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:optional", "@com_google_absl//absl/types:span", ], alwayslink = 1, @@ -437,21 +439,15 @@ cc_library( name = "dump_graph", srcs = [ "dump_graph.cc", - "dump_graph_flags.cc", - "dump_graph_flags.h", ], hdrs = [ "dump_graph.h", ], deps = [ - "//tensorflow/compiler/xla:parse_flags_from_env", - "//tensorflow/core:core_cpu", - "//tensorflow/core:core_cpu_internal", + "//tensorflow/compiler/jit:flags", "//tensorflow/core:framework", - "//tensorflow/core:framework_internal", - "//tensorflow/core:lib", + "//tensorflow/core:graph", "//tensorflow/core:protos_all_cc", - "@com_google_absl//absl/strings", ], ) diff --git a/tensorflow/compiler/tf2xla/dump_graph.cc b/tensorflow/compiler/tf2xla/dump_graph.cc index 380c6a7e23..64fdbbebc6 100644 --- a/tensorflow/compiler/tf2xla/dump_graph.cc +++ b/tensorflow/compiler/tf2xla/dump_graph.cc @@ -18,87 +18,26 @@ limitations under the License. #include "tensorflow/compiler/tf2xla/dump_graph.h" -#include "absl/strings/str_cat.h" -#include "tensorflow/compiler/tf2xla/dump_graph_flags.h" -#include "tensorflow/core/platform/env.h" -#include "tensorflow/core/platform/mutex.h" +#include "tensorflow/compiler/jit/flags.h" +#include "tensorflow/core/util/dump_graph.h" namespace tensorflow { namespace dump_graph { -namespace { - -struct NameCounts { - mutex counts_mutex; - std::unordered_map counts; -}; - -string MakeUniqueFilename(string name) { - static NameCounts& instance = *new NameCounts; - - // Remove illegal characters from `name`. - for (int i = 0; i < name.size(); ++i) { - char ch = name[i]; - if (ch == '/' || ch == '[' || ch == ']' || ch == '*' || ch == '?') { - name[i] = '_'; - } - } - - int count; - { - mutex_lock lock(instance.counts_mutex); - count = instance.counts[name]++; - } - - string filename = name; - if (count > 0) { - absl::StrAppend(&filename, "_", count); - } - absl::StrAppend(&filename, ".pbtxt"); - return filename; -} - -string WriteTextProtoToUniqueFile( - Env* env, const string& name, const char* proto_type, - const ::tensorflow::protobuf::Message& proto) { - const string& dirname = - legacy_flags::GetDumpGraphFlags()->tf_dump_graph_prefix; - Status status = env->RecursivelyCreateDir(dirname); - if (!status.ok()) { - LOG(WARNING) << "Failed to create " << dirname << " for dumping " - << proto_type << ": " << status; - return "(unavailable)"; - } - string filepath = absl::StrCat(dirname, "/", MakeUniqueFilename(name)); - status = WriteTextProto(Env::Default(), filepath, proto); - if (!status.ok()) { - LOG(WARNING) << "Failed to dump " << proto_type << " to file: " << filepath - << " : " << status; - return "(unavailable)"; - } - LOG(INFO) << "Dumped " << proto_type << " to " << filepath; - return filepath; -} - -} // anonymous namespace - string DumpGraphDefToFile(const string& name, GraphDef const& graph_def) { - return WriteTextProtoToUniqueFile(Env::Default(), name, "GraphDef", - graph_def); + return tensorflow::DumpGraphDefToFile( + name, graph_def, GetDumpGraphFlags()->tf_dump_graph_prefix); } string DumpGraphToFile(const string& name, Graph const& graph, const FunctionLibraryDefinition* flib_def) { - GraphDef graph_def; - graph.ToGraphDef(&graph_def); - if (flib_def) { - *graph_def.mutable_library() = flib_def->ToProto(); - } - return DumpGraphDefToFile(name, graph_def); + return tensorflow::DumpGraphToFile(name, graph, flib_def, + GetDumpGraphFlags()->tf_dump_graph_prefix); } string DumpFunctionDefToFile(const string& name, FunctionDef const& fdef) { - return WriteTextProtoToUniqueFile(Env::Default(), name, "FunctionDef", fdef); + return tensorflow::DumpFunctionDefToFile( + name, fdef, GetDumpGraphFlags()->tf_dump_graph_prefix); } } // namespace dump_graph diff --git a/tensorflow/compiler/tf2xla/dump_graph_flags.cc b/tensorflow/compiler/tf2xla/dump_graph_flags.cc deleted file mode 100644 index 2eb1f8cd84..0000000000 --- a/tensorflow/compiler/tf2xla/dump_graph_flags.cc +++ /dev/null @@ -1,63 +0,0 @@ -/* 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. -==============================================================================*/ - -// Legacy flags for the XLA bridge's dump_graph module. - -#include -#include - -#include "tensorflow/compiler/tf2xla/dump_graph_flags.h" -#include "tensorflow/compiler/xla/parse_flags_from_env.h" -#include "tensorflow/core/platform/types.h" -#include "tensorflow/core/util/command_line_flags.h" - -namespace tensorflow { -namespace legacy_flags { - -// Pointers to the parsed value of the flags and flag descriptors, initialized -// via flags_init. -static DumpGraphFlags* flags; -static std::vector* flag_list; -static std::once_flag flags_init; - -// Allocate *flags. Called via call_once(&flags_init,...). -static void AllocateFlags() { - flags = new DumpGraphFlags; - flags->tf_dump_graph_prefix = "/tmp/"; - flag_list = new std::vector({ - Flag("tf_dump_graph_prefix", &flags->tf_dump_graph_prefix, - "Path prefix to which graphs dumped during debugging should be " - "written."), - }); - xla::ParseFlagsFromEnv(*flag_list); -} - -// Append to *append_to flag definitions associated with the XLA bridge's -// dump_graph module. -void AppendDumpGraphFlags(std::vector* append_to) { - std::call_once(flags_init, &AllocateFlags); - append_to->insert(append_to->end(), flag_list->begin(), flag_list->end()); -} - -// Return a pointer to the DumpGraphFlags struct; -// repeated calls return the same pointer. -// This should be called only after Flags::Parse() has returned. -DumpGraphFlags* GetDumpGraphFlags() { - std::call_once(flags_init, &AllocateFlags); - return flags; -} - -} // namespace legacy_flags -} // namespace tensorflow diff --git a/tensorflow/compiler/tf2xla/dump_graph_flags.h b/tensorflow/compiler/tf2xla/dump_graph_flags.h deleted file mode 100644 index 80a3307d92..0000000000 --- a/tensorflow/compiler/tf2xla/dump_graph_flags.h +++ /dev/null @@ -1,48 +0,0 @@ -/* 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_COMPILER_TF2XLA_DUMP_GRAPH_FLAGS_H_ -#define TENSORFLOW_COMPILER_TF2XLA_DUMP_GRAPH_FLAGS_H_ - -// Legacy flags for the XLA bridge's dump_graph module. - -#include - -#include "tensorflow/core/platform/types.h" -#include "tensorflow/core/util/command_line_flags.h" - -namespace tensorflow { -namespace legacy_flags { - -// Append to *flag_list flag definitions associated with the XLA bridge's -// dump_graph module. -void AppendDumpGraphFlags(std::vector* flag_list); - -// The values of flags associated with the XLA bridge's -// dump_graph module. -typedef struct { - string tf_dump_graph_prefix; // Path prefix to which graphs dumped during - // debugging should be written. -} DumpGraphFlags; - -// Return a pointer to the DumpGraphFlags struct; -// repeated calls return the same pointer. -// This should be called only after Flags::Parse() has returned. -DumpGraphFlags* GetDumpGraphFlags(); - -} // namespace legacy_flags -} // namespace tensorflow - -#endif // TENSORFLOW_COMPILER_TF2XLA_DUMP_GRAPH_FLAGS_H_ diff --git a/tensorflow/compiler/tf2xla/functionalize_control_flow.cc b/tensorflow/compiler/tf2xla/functionalize_control_flow.cc index 9ef9f49f42..3dfd3f854c 100644 --- a/tensorflow/compiler/tf2xla/functionalize_control_flow.cc +++ b/tensorflow/compiler/tf2xla/functionalize_control_flow.cc @@ -75,6 +75,25 @@ Status FunctionalizeControlFlow(Graph* graph, return FunctionalizeControlFlow(/*lookup_library=*/nullptr, graph, library); } +Status FunctionalizeControlFlowForGraphDef(GraphDef* graph_def, + FunctionLibraryDefinition* library) { + return FunctionalizeControlFlowForGraphDef(/*lookup_library=*/nullptr, + graph_def, library); +} + +Status FunctionalizeControlFlowForGraphDef( + const FunctionLibraryDefinition* lookup_library, GraphDef* graph_def, + FunctionLibraryDefinition* library) { + FunctionDefLibrary function_lib = graph_def->library(); + Graph graph(OpRegistry::Global()); + + TF_RETURN_IF_ERROR(ConvertGraphDefToGraph({}, *graph_def, &graph)); + TF_RETURN_IF_ERROR(FunctionalizeControlFlow(lookup_library, &graph, library)); + graph.ToGraphDef(graph_def); + std::swap(*graph_def->mutable_library(), function_lib); + return Status::OK(); +} + Status FunctionalizeControlFlowForFunction( const string& func_name, const string& new_func_name, const protobuf::Map& attrs, diff --git a/tensorflow/compiler/tf2xla/functionalize_control_flow.h b/tensorflow/compiler/tf2xla/functionalize_control_flow.h index ba99205640..91d33fa405 100644 --- a/tensorflow/compiler/tf2xla/functionalize_control_flow.h +++ b/tensorflow/compiler/tf2xla/functionalize_control_flow.h @@ -33,6 +33,12 @@ Status FunctionalizeControlFlow(const FunctionLibraryDefinition* lookup_library, Graph* graph, FunctionLibraryDefinition* library); +Status FunctionalizeControlFlowForGraphDef(GraphDef* graph_def, + FunctionLibraryDefinition* library); +Status FunctionalizeControlFlowForGraphDef( + const FunctionLibraryDefinition* lookup_library, GraphDef* graph_def, + FunctionLibraryDefinition* library); + // This pass looks at the graph and all associated FunctionDefs, and turns // traditional control flow structure (Switch/Merge/etc.) into functional // control flow structure (If/While). diff --git a/tensorflow/compiler/tf2xla/functionalize_control_flow_test.cc b/tensorflow/compiler/tf2xla/functionalize_control_flow_test.cc index c3841f996f..9784985af8 100644 --- a/tensorflow/compiler/tf2xla/functionalize_control_flow_test.cc +++ b/tensorflow/compiler/tf2xla/functionalize_control_flow_test.cc @@ -95,77 +95,87 @@ TEST(FunctionalizeControlFlow, Conditional) { } FunctionLibraryDefinition library(OpRegistry::Global(), {}); + GraphDef optimized_graph_def; + graph.ToGraphDef(&optimized_graph_def); + TF_ASSERT_OK( + FunctionalizeControlFlowForGraphDef(&optimized_graph_def, &library)); TF_ASSERT_OK(FunctionalizeControlFlow(&graph, &library)); + GraphDef converted_graph_def; + graph.ToGraphDef(&converted_graph_def); + + for (const GraphDef& graph_def : {optimized_graph_def, converted_graph_def}) { + string op_name; + NameAttrList then_fn; + NameAttrList else_fn; + TF_EXPECT_OK(FindIfThenAndElse(graph_def, &op_name, &then_fn, &else_fn)); + InstantiationResultForTest else_result; + TF_EXPECT_OK( + InstantiateFunctionForTest(else_fn.name(), library, &else_result)); + + // Outer graph + { + Scope scope = Scope::NewRootScope().ExitOnError(); + auto y = ops::Placeholder(scope.WithOpName("y"), DT_INT32); + auto x = ops::Placeholder(scope.WithOpName("x"), DT_INT32); + auto less = ops::Less(scope.WithOpName("cond/Less"), y, x); + auto if_op = ops::If(scope.WithOpName(op_name), less, + std::initializer_list{less, y, x}, {DT_INT32}, + then_fn, else_fn); + auto id = ops::Identity(scope.WithOpName("cond/Merge"), if_op.output[0]); + GraphDef expected; + TF_EXPECT_OK(scope.ToGraphDef(&expected)); + TF_EXPECT_GRAPH_EQ(expected, graph_def); + } - GraphDef graph_def; - graph.ToGraphDef(&graph_def); - string op_name; - NameAttrList then_fn; - NameAttrList else_fn; - TF_EXPECT_OK(FindIfThenAndElse(graph_def, &op_name, &then_fn, &else_fn)); - InstantiationResultForTest else_result; - TF_EXPECT_OK( - InstantiateFunctionForTest(else_fn.name(), library, &else_result)); - - // Outer graph - { - Scope scope = Scope::NewRootScope().ExitOnError(); - auto y = ops::Placeholder(scope.WithOpName("y"), DT_INT32); - auto x = ops::Placeholder(scope.WithOpName("x"), DT_INT32); - auto less = ops::Less(scope.WithOpName("cond/Less"), y, x); - auto if_op = ops::If(scope.WithOpName(op_name), less, - std::initializer_list{less, y, x}, {DT_INT32}, - then_fn, else_fn); - auto id = ops::Identity(scope.WithOpName("cond/Merge"), if_op.output[0]); - GraphDef expected; - TF_EXPECT_OK(scope.ToGraphDef(&expected)); - TF_EXPECT_GRAPH_EQ(expected, graph_def); - } - - // then body. - { - Scope scope = Scope::NewRootScope().ExitOnError(); - auto arg_0 = ops::_Arg(scope.WithOpName("_arg0"), DT_BOOL, 0); - auto arg_1 = ops::_Arg(scope.WithOpName("_arg1"), DT_INT32, 1); - auto arg_2 = ops::_Arg(scope.WithOpName("_arg2"), DT_INT32, 2); - auto identity = ops::Identity(scope.WithOpName("cond/Identity"), arg_0); - auto cond = ops::Const( - scope.WithOpName("cond").WithControlDependencies(identity), 17); - auto mul = ops::Mul(scope.WithOpName("cond/Mul"), arg_1, cond); - auto retval0 = ops::_Retval(scope.WithOpName("_retval0_RetVal"), mul, 0); - - GraphDef expected; - TF_EXPECT_OK(scope.ToGraphDef(&expected)); - - InstantiationResultForTest result; - TF_EXPECT_OK(InstantiateFunctionForTest(then_fn.name(), library, &result)); - - EXPECT_EQ(DataTypeVector{DT_INT32}, result.ret_types); - EXPECT_EQ((DataTypeVector{DT_BOOL, DT_INT32, DT_INT32}), result.arg_types); - TF_EXPECT_GRAPH_EQ(expected, result.gdef); - } + // then body. + { + Scope scope = Scope::NewRootScope().ExitOnError(); + auto arg_0 = ops::_Arg(scope.WithOpName("_arg0"), DT_BOOL, 0); + auto arg_1 = ops::_Arg(scope.WithOpName("_arg1"), DT_INT32, 1); + auto arg_2 = ops::_Arg(scope.WithOpName("_arg2"), DT_INT32, 2); + auto identity = ops::Identity(scope.WithOpName("cond/Identity"), arg_0); + auto cond = ops::Const( + scope.WithOpName("cond").WithControlDependencies(identity), 17); + auto mul = ops::Mul(scope.WithOpName("cond/Mul"), arg_1, cond); + auto retval0 = ops::_Retval(scope.WithOpName("_retval0_RetVal"), mul, 0); + + GraphDef expected; + TF_EXPECT_OK(scope.ToGraphDef(&expected)); + + InstantiationResultForTest result; + TF_EXPECT_OK( + InstantiateFunctionForTest(then_fn.name(), library, &result)); + + EXPECT_EQ(DataTypeVector{DT_INT32}, result.ret_types); + EXPECT_EQ((DataTypeVector{DT_BOOL, DT_INT32, DT_INT32}), + result.arg_types); + TF_EXPECT_GRAPH_EQ(expected, result.gdef); + } - // else body. - { - Scope scope = Scope::NewRootScope().ExitOnError(); - auto arg_0 = ops::_Arg(scope.WithOpName("_arg0"), DT_BOOL, 0); - auto arg_1 = ops::_Arg(scope.WithOpName("_arg1"), DT_INT32, 1); - auto arg_2 = ops::_Arg(scope.WithOpName("_arg2"), DT_INT32, 2); - auto identity = ops::Identity(scope.WithOpName("cond/Identity_1"), arg_0); - auto cond_1 = ops::Const( - scope.WithOpName("cond_1").WithControlDependencies(identity), 23); - auto add = ops::Add(scope.WithOpName("cond/false/add"), arg_2, cond_1); - auto retval0 = ops::_Retval(scope.WithOpName("_retval0_RetVal"), add, 0); - - GraphDef expected; - TF_EXPECT_OK(scope.ToGraphDef(&expected)); - - InstantiationResultForTest result; - TF_EXPECT_OK(InstantiateFunctionForTest(else_fn.name(), library, &result)); - - EXPECT_EQ(DataTypeVector{DT_INT32}, result.ret_types); - EXPECT_EQ((DataTypeVector{DT_BOOL, DT_INT32, DT_INT32}), result.arg_types); - TF_EXPECT_GRAPH_EQ(expected, result.gdef); + // else body. + { + Scope scope = Scope::NewRootScope().ExitOnError(); + auto arg_0 = ops::_Arg(scope.WithOpName("_arg0"), DT_BOOL, 0); + auto arg_1 = ops::_Arg(scope.WithOpName("_arg1"), DT_INT32, 1); + auto arg_2 = ops::_Arg(scope.WithOpName("_arg2"), DT_INT32, 2); + auto identity = ops::Identity(scope.WithOpName("cond/Identity_1"), arg_0); + auto cond_1 = ops::Const( + scope.WithOpName("cond_1").WithControlDependencies(identity), 23); + auto add = ops::Add(scope.WithOpName("cond/false/add"), arg_2, cond_1); + auto retval0 = ops::_Retval(scope.WithOpName("_retval0_RetVal"), add, 0); + + GraphDef expected; + TF_EXPECT_OK(scope.ToGraphDef(&expected)); + + InstantiationResultForTest result; + TF_EXPECT_OK( + InstantiateFunctionForTest(else_fn.name(), library, &result)); + + EXPECT_EQ(DataTypeVector{DT_INT32}, result.ret_types); + EXPECT_EQ((DataTypeVector{DT_BOOL, DT_INT32, DT_INT32}), + result.arg_types); + TF_EXPECT_GRAPH_EQ(expected, result.gdef); + } } } @@ -239,75 +249,77 @@ TEST(FunctionalizeControlFlow, OneLoopVar) { } FunctionLibraryDefinition library(OpRegistry::Global(), {}); + GraphDef optimized_graph_def; + graph.ToGraphDef(&optimized_graph_def); + TF_ASSERT_OK( + FunctionalizeControlFlowForGraphDef(&optimized_graph_def, &library)); TF_ASSERT_OK(FunctionalizeControlFlow(&graph, &library)); + GraphDef converted_graph_def; + graph.ToGraphDef(&converted_graph_def); + + for (const GraphDef& graph_def : {optimized_graph_def, converted_graph_def}) { + NameAttrList cond_fn, body_fn; + TF_EXPECT_OK(FindWhileCondAndBody(graph_def, &cond_fn, &body_fn)); + + // Outer graph + { + Scope scope = Scope::NewRootScope().ExitOnError(); + auto source = ops::Placeholder(scope.WithOpName("source"), DT_INT32); + auto while_op = + ops::While(scope.WithOpName("while/LoopCond"), + std::initializer_list{source}, cond_fn, body_fn); + auto sink = ops::Identity(scope.WithOpName("sink"), while_op[0]); + GraphDef expected; + TF_EXPECT_OK(scope.ToGraphDef(&expected)); + TF_EXPECT_GRAPH_EQ(expected, graph_def); + } - GraphDef graph_def; - graph.ToGraphDef(&graph_def); - - NameAttrList cond_fn, body_fn; - TF_EXPECT_OK(FindWhileCondAndBody(graph_def, &cond_fn, &body_fn)); - - // Outer graph - { - Scope scope = Scope::NewRootScope().ExitOnError(); - auto source = ops::Placeholder(scope.WithOpName("source"), DT_INT32); - auto while_op = - ops::While(scope.WithOpName("while/LoopCond"), - std::initializer_list{source}, cond_fn, body_fn); - auto sink = ops::Identity(scope.WithOpName("sink"), while_op[0]); - GraphDef expected; - TF_EXPECT_OK(scope.ToGraphDef(&expected)); - TF_EXPECT_GRAPH_EQ(expected, graph_def); - } - - // Condition graph - { - Scope scope = Scope::NewRootScope().ExitOnError(); - auto arg = ops::_Arg(scope.WithOpName("_arg0"), DT_INT32, 0); - auto ten = ops::Const( - scope.WithOpName("while/Less/y").WithControlDependencies(arg), 10); - auto less = ops::Less(scope.WithOpName("while/Less"), arg, ten); - auto retval = ops::_Retval(scope.WithOpName("_retval0_RetVal"), less, 0); - - GraphDef expected; - TF_EXPECT_OK(scope.ToGraphDef(&expected)); - - InstantiationResultForTest result; - TF_EXPECT_OK(InstantiateFunctionForTest(cond_fn.name(), library, &result)); - - EXPECT_EQ(DataTypeVector{DT_INT32}, result.arg_types); - EXPECT_EQ(DataTypeVector{DT_BOOL}, result.ret_types); - TF_EXPECT_GRAPH_EQ(expected, result.gdef); - } - - // Body graph. - { - Scope scope = Scope::NewRootScope().ExitOnError(); - auto arg = ops::_Arg(scope.WithOpName("_arg0"), DT_INT32, 0); - auto identity = ops::Identity(scope.WithOpName("while/Identity"), arg); - auto one = ops::Const( - scope.WithOpName("while/add/y").WithControlDependencies(identity), 1); - auto add = ops::Add(scope.WithOpName("while/add"), identity, one); - auto retval = ops::_Retval(scope.WithOpName("_retval0_RetVal"), add, 0); - - GraphDef expected; - TF_EXPECT_OK(scope.ToGraphDef(&expected)); - - InstantiationResultForTest result; - TF_EXPECT_OK(InstantiateFunctionForTest(body_fn.name(), library, &result)); + // Condition graph + { + Scope scope = Scope::NewRootScope().ExitOnError(); + auto arg = ops::_Arg(scope.WithOpName("_arg0"), DT_INT32, 0); + auto ten = ops::Const( + scope.WithOpName("while/Less/y").WithControlDependencies(arg), 10); + auto less = ops::Less(scope.WithOpName("while/Less"), arg, ten); + auto retval = ops::_Retval(scope.WithOpName("_retval0_RetVal"), less, 0); + + GraphDef expected; + TF_EXPECT_OK(scope.ToGraphDef(&expected)); + + InstantiationResultForTest result; + TF_EXPECT_OK( + InstantiateFunctionForTest(cond_fn.name(), library, &result)); + + EXPECT_EQ(DataTypeVector{DT_INT32}, result.arg_types); + EXPECT_EQ(DataTypeVector{DT_BOOL}, result.ret_types); + TF_EXPECT_GRAPH_EQ(expected, result.gdef); + } - EXPECT_EQ(DataTypeVector{DT_INT32}, result.arg_types); - EXPECT_EQ(DataTypeVector{DT_INT32}, result.ret_types); - TF_EXPECT_GRAPH_EQ(expected, result.gdef); + // Body graph. + { + Scope scope = Scope::NewRootScope().ExitOnError(); + auto arg = ops::_Arg(scope.WithOpName("_arg0"), DT_INT32, 0); + auto identity = ops::Identity(scope.WithOpName("while/Identity"), arg); + auto one = ops::Const( + scope.WithOpName("while/add/y").WithControlDependencies(identity), 1); + auto add = ops::Add(scope.WithOpName("while/add"), identity, one); + auto retval = ops::_Retval(scope.WithOpName("_retval0_RetVal"), add, 0); + + GraphDef expected; + TF_EXPECT_OK(scope.ToGraphDef(&expected)); + + InstantiationResultForTest result; + TF_EXPECT_OK( + InstantiateFunctionForTest(body_fn.name(), library, &result)); + + EXPECT_EQ(DataTypeVector{DT_INT32}, result.arg_types); + EXPECT_EQ(DataTypeVector{DT_INT32}, result.ret_types); + TF_EXPECT_GRAPH_EQ(expected, result.gdef); + } } } -// @function.Defun(noinline=True) -// def increment_fn(x): -// return [x + 1] -// Define the above function, and add it to the given graph. It's used as the -// while loop body in NoinlineLoopBody test. -Status AddNoinlineFunctionToGraph(const string& node_name, Graph* graph) { +FunctionDef GetNoinlineFunctionDef() { FunctionDef fdef = FunctionDefHelper::Create( "increment_fn", {"x:int32"}, {"add:int32"}, {}, { @@ -316,8 +328,17 @@ Status AddNoinlineFunctionToGraph(const string& node_name, Graph* graph) { }, {{"add", "add_0:z:0"}}); (*fdef.mutable_attr())["_noinline"].set_b(true); + return fdef; +} + +// @function.Defun(noinline=True) +// def increment_fn(x): +// return [x + 1] +// Define the above function, and add it to the given graph. It's used as the +// while loop body in NoinlineLoopBody test. +Status AddNoinlineFunctionToGraph(const string& node_name, Graph* graph) { FunctionDefLibrary fdef_lib; - *(fdef_lib.add_function()) = fdef; + *(fdef_lib.add_function()) = GetNoinlineFunctionDef(); TF_RETURN_IF_ERROR(graph->AddFunctionLibrary(fdef_lib)); NodeDef increment_fn; increment_fn.set_name(node_name); @@ -376,55 +397,88 @@ TEST(FunctionalizeControlFlow, NoinlineLoopBody) { FunctionLibraryDefinition lookup_lib(graph.flib_def()); FunctionLibraryDefinition library(OpRegistry::Global(), {}); // Function increment_fn will be copied from lookup_lib to library. - TF_ASSERT_OK(FunctionalizeControlFlow(&lookup_lib, &graph, &library)); + GraphDef optimized_graph_def; + graph.ToGraphDef(&optimized_graph_def); - GraphDef graph_def; - graph.ToGraphDef(&graph_def); + *(optimized_graph_def.mutable_library()->add_function()) = + GetNoinlineFunctionDef(); - NameAttrList cond_fn, body_fn; - TF_ASSERT_OK(FindWhileCondAndBody(graph_def, &cond_fn, &body_fn)); + TF_ASSERT_OK(FunctionalizeControlFlowForGraphDef( + &lookup_lib, &optimized_graph_def, &library)); + TF_ASSERT_OK(FunctionalizeControlFlow(&lookup_lib, &graph, &library)); + GraphDef converted_graph_def; + graph.ToGraphDef(&converted_graph_def); + + for (const GraphDef& graph_def : {optimized_graph_def, converted_graph_def}) { + NameAttrList cond_fn, body_fn; + TF_ASSERT_OK(FindWhileCondAndBody(graph_def, &cond_fn, &body_fn)); + + // Outer graph + { + Scope scope = Scope::NewRootScope().ExitOnError(); + auto source = ops::Placeholder(scope.WithOpName("source"), DT_INT32); + auto while_op = + ops::While(scope.WithOpName("while/LoopCond"), + std::initializer_list{source}, cond_fn, body_fn); + GraphDef expected; + TF_ASSERT_OK(scope.ToGraphDef(&expected)); + TF_EXPECT_GRAPH_EQ(expected, graph_def); + } - // Outer graph - { - Scope scope = Scope::NewRootScope().ExitOnError(); - auto source = ops::Placeholder(scope.WithOpName("source"), DT_INT32); - auto while_op = - ops::While(scope.WithOpName("while/LoopCond"), - std::initializer_list{source}, cond_fn, body_fn); - GraphDef expected; - TF_ASSERT_OK(scope.ToGraphDef(&expected)); - TF_EXPECT_GRAPH_EQ(expected, graph_def); + // Body graph. + { + Scope scope = Scope::NewRootScope().ExitOnError(); + auto arg = ops::_Arg(scope.WithOpName("_arg0"), DT_INT32, 0); + TF_ASSERT_OK( + AddNoinlineFunctionToGraph(noinline_node_name, scope.graph())); + auto identity = ops::Identity(scope.WithOpName("while/Identity"), arg); + NodeDef retval; + retval.set_name("_retval0_RetVal"); + retval.set_op(FunctionLibraryDefinition::kRetOp); + *retval.add_input() = noinline_node_name; + (*retval.mutable_attr())["T"].set_type(DT_INT32); + (*retval.mutable_attr())["index"].set_i(0); + Status status; + scope.graph()->AddNode(retval, &status); + TF_ASSERT_OK(status); + + GraphDef expected; + TF_ASSERT_OK(scope.ToGraphDef(&expected)); + + InstantiationResultForTest result; + // Verify that increment_fn has been copied to library. + TF_EXPECT_OK( + InstantiateFunctionForTest(body_fn.name(), library, &result)); + + EXPECT_EQ(DataTypeVector{DT_INT32}, result.arg_types); + EXPECT_EQ(DataTypeVector{DT_INT32}, result.ret_types); + // Ignore the function library when comparing the graphs. + expected.clear_library(); + TF_EXPECT_GRAPH_EQ(expected, result.gdef); + } } +} - // Body graph. +TEST(FunctionalizeControlFlow, MissingFunctionDefInLibrary) { + const string& noinline_node_name = "while/increment_fn"; + Graph graph(OpRegistry::Global()); { Scope scope = Scope::NewRootScope().ExitOnError(); - auto arg = ops::_Arg(scope.WithOpName("_arg0"), DT_INT32, 0); + auto source = ops::Placeholder(scope.WithOpName("source"), DT_INT32); + auto identity = ops::Identity(scope.WithOpName("while/Identity"), source); TF_ASSERT_OK(AddNoinlineFunctionToGraph(noinline_node_name, scope.graph())); - auto identity = ops::Identity(scope.WithOpName("while/Identity"), arg); - NodeDef retval; - retval.set_name("_retval0_RetVal"); - retval.set_op(FunctionLibraryDefinition::kRetOp); - *retval.add_input() = noinline_node_name; - (*retval.mutable_attr())["T"].set_type(DT_INT32); - (*retval.mutable_attr())["index"].set_i(0); - Status status; - scope.graph()->AddNode(retval, &status); - TF_ASSERT_OK(status); - - GraphDef expected; - TF_ASSERT_OK(scope.ToGraphDef(&expected)); + TF_ASSERT_OK(scope.ToGraph(&graph)); + } - InstantiationResultForTest result; - // Verify that increment_fn has been copied to library. - TF_EXPECT_OK(InstantiateFunctionForTest(body_fn.name(), library, &result)); + FunctionLibraryDefinition lookup_lib(graph.flib_def()); + FunctionLibraryDefinition library(OpRegistry::Global(), {}); + GraphDef graph_def; + graph.ToGraphDef(&graph_def); + graph_def.clear_library(); - EXPECT_EQ(DataTypeVector{DT_INT32}, result.arg_types); - EXPECT_EQ(DataTypeVector{DT_INT32}, result.ret_types); - // Ignore the function library when comparing the graphs. - expected.clear_library(); - TF_EXPECT_GRAPH_EQ(expected, result.gdef); - } + Status status = + FunctionalizeControlFlowForGraphDef(&lookup_lib, &graph_def, &library); + EXPECT_EQ(tensorflow::error::NOT_FOUND, status.code()); } // Tests functionalizing OneLoopVar where the loop value is not used post the @@ -467,65 +521,72 @@ TEST(FunctionalizeControlFlow, OneLoopVarWithoutExit) { } FunctionLibraryDefinition library(OpRegistry::Global(), {}); + GraphDef optimized_graph_def; + graph.ToGraphDef(&optimized_graph_def); + TF_ASSERT_OK( + FunctionalizeControlFlowForGraphDef(&optimized_graph_def, &library)); TF_ASSERT_OK(FunctionalizeControlFlow(&graph, &library)); + GraphDef converted_graph_def; + graph.ToGraphDef(&converted_graph_def); + + for (const GraphDef& graph_def : {optimized_graph_def, converted_graph_def}) { + NameAttrList cond_fn, body_fn; + TF_EXPECT_OK(FindWhileCondAndBody(graph_def, &cond_fn, &body_fn)); + + // Outer graph + { + Scope scope = Scope::NewRootScope().ExitOnError(); + auto source = ops::Placeholder(scope.WithOpName("source"), DT_INT32); + auto while_op = + ops::While(scope.WithOpName("while/LoopCond"), + std::initializer_list{source}, cond_fn, body_fn); + GraphDef expected; + TF_EXPECT_OK(scope.ToGraphDef(&expected)); + TF_EXPECT_GRAPH_EQ(expected, graph_def); + } - GraphDef graph_def; - graph.ToGraphDef(&graph_def); - - NameAttrList cond_fn, body_fn; - TF_EXPECT_OK(FindWhileCondAndBody(graph_def, &cond_fn, &body_fn)); - - // Outer graph - { - Scope scope = Scope::NewRootScope().ExitOnError(); - auto source = ops::Placeholder(scope.WithOpName("source"), DT_INT32); - auto while_op = - ops::While(scope.WithOpName("while/LoopCond"), - std::initializer_list{source}, cond_fn, body_fn); - GraphDef expected; - TF_EXPECT_OK(scope.ToGraphDef(&expected)); - TF_EXPECT_GRAPH_EQ(expected, graph_def); - } - - // Condition graph - { - Scope scope = Scope::NewRootScope().ExitOnError(); - auto arg = ops::_Arg(scope.WithOpName("_arg0"), DT_INT32, 0); - auto ten = ops::Const( - scope.WithOpName("while/Less/y").WithControlDependencies(arg), 10); - auto less = ops::Less(scope.WithOpName("while/Less"), arg, ten); - auto retval = ops::_Retval(scope.WithOpName("_retval0_RetVal"), less, 0); - - GraphDef expected; - TF_EXPECT_OK(scope.ToGraphDef(&expected)); - - InstantiationResultForTest result; - TF_EXPECT_OK(InstantiateFunctionForTest(cond_fn.name(), library, &result)); - - EXPECT_EQ(DataTypeVector{DT_INT32}, result.arg_types); - EXPECT_EQ(DataTypeVector{DT_BOOL}, result.ret_types); - TF_EXPECT_GRAPH_EQ(expected, result.gdef); - } - - // Body graph. - { - Scope scope = Scope::NewRootScope().ExitOnError(); - auto arg = ops::_Arg(scope.WithOpName("_arg0"), DT_INT32, 0); - auto identity = ops::Identity(scope.WithOpName("while/Identity"), arg); - auto one = ops::Const( - scope.WithOpName("while/add/y").WithControlDependencies(identity), 1); - auto add = ops::Add(scope.WithOpName("while/add"), identity, one); - auto retval = ops::_Retval(scope.WithOpName("_retval0_RetVal"), add, 0); - - GraphDef expected; - TF_EXPECT_OK(scope.ToGraphDef(&expected)); - - InstantiationResultForTest result; - TF_EXPECT_OK(InstantiateFunctionForTest(body_fn.name(), library, &result)); + // Condition graph + { + Scope scope = Scope::NewRootScope().ExitOnError(); + auto arg = ops::_Arg(scope.WithOpName("_arg0"), DT_INT32, 0); + auto ten = ops::Const( + scope.WithOpName("while/Less/y").WithControlDependencies(arg), 10); + auto less = ops::Less(scope.WithOpName("while/Less"), arg, ten); + auto retval = ops::_Retval(scope.WithOpName("_retval0_RetVal"), less, 0); + + GraphDef expected; + TF_EXPECT_OK(scope.ToGraphDef(&expected)); + + InstantiationResultForTest result; + TF_EXPECT_OK( + InstantiateFunctionForTest(cond_fn.name(), library, &result)); + + EXPECT_EQ(DataTypeVector{DT_INT32}, result.arg_types); + EXPECT_EQ(DataTypeVector{DT_BOOL}, result.ret_types); + TF_EXPECT_GRAPH_EQ(expected, result.gdef); + } - EXPECT_EQ(DataTypeVector{DT_INT32}, result.arg_types); - EXPECT_EQ(DataTypeVector{DT_INT32}, result.ret_types); - TF_EXPECT_GRAPH_EQ(expected, result.gdef); + // Body graph. + { + Scope scope = Scope::NewRootScope().ExitOnError(); + auto arg = ops::_Arg(scope.WithOpName("_arg0"), DT_INT32, 0); + auto identity = ops::Identity(scope.WithOpName("while/Identity"), arg); + auto one = ops::Const( + scope.WithOpName("while/add/y").WithControlDependencies(identity), 1); + auto add = ops::Add(scope.WithOpName("while/add"), identity, one); + auto retval = ops::_Retval(scope.WithOpName("_retval0_RetVal"), add, 0); + + GraphDef expected; + TF_EXPECT_OK(scope.ToGraphDef(&expected)); + + InstantiationResultForTest result; + TF_EXPECT_OK( + InstantiateFunctionForTest(body_fn.name(), library, &result)); + + EXPECT_EQ(DataTypeVector{DT_INT32}, result.arg_types); + EXPECT_EQ(DataTypeVector{DT_INT32}, result.ret_types); + TF_EXPECT_GRAPH_EQ(expected, result.gdef); + } } } @@ -608,86 +669,95 @@ TEST(FunctionalizeControlFlow, TwoLoopVars) { } FunctionLibraryDefinition library(OpRegistry::Global(), {}); + GraphDef optimized_graph_def; + graph.ToGraphDef(&optimized_graph_def); + TF_ASSERT_OK( + FunctionalizeControlFlowForGraphDef(&optimized_graph_def, &library)); TF_ASSERT_OK(FunctionalizeControlFlow(&graph, &library)); + GraphDef converted_graph_def; + graph.ToGraphDef(&converted_graph_def); + + for (const GraphDef& graph_def : {optimized_graph_def, converted_graph_def}) { + NameAttrList cond_fn, body_fn; + TF_EXPECT_OK(FindWhileCondAndBody(graph_def, &cond_fn, &body_fn)); + + // Outer graph. + { + Scope scope = Scope::NewRootScope().ExitOnError(); + auto x = ops::Placeholder(scope.WithOpName("Placeholder/x"), DT_INT32); + auto y = ops::Placeholder(scope.WithOpName("Placeholder/y"), DT_INT32); + auto while_op = + ops::While(scope.WithOpName("while/LoopCond"), + std::initializer_list{x, y}, cond_fn, body_fn); + auto sink_x = ops::Identity(scope.WithOpName("sink_x"), while_op[0]); + auto sink_y = ops::Identity(scope.WithOpName("sink_y"), while_op[1]); + GraphDef expected; + TF_EXPECT_OK(scope.ToGraphDef(&expected)); + TF_EXPECT_GRAPH_EQ(expected, graph_def); + } - GraphDef graph_def; - graph.ToGraphDef(&graph_def); - - NameAttrList cond_fn, body_fn; - TF_EXPECT_OK(FindWhileCondAndBody(graph_def, &cond_fn, &body_fn)); - - // Outer graph. - { - Scope scope = Scope::NewRootScope().ExitOnError(); - auto x = ops::Placeholder(scope.WithOpName("Placeholder/x"), DT_INT32); - auto y = ops::Placeholder(scope.WithOpName("Placeholder/y"), DT_INT32); - auto while_op = - ops::While(scope.WithOpName("while/LoopCond"), - std::initializer_list{x, y}, cond_fn, body_fn); - auto sink_x = ops::Identity(scope.WithOpName("sink_x"), while_op[0]); - auto sink_y = ops::Identity(scope.WithOpName("sink_y"), while_op[1]); - GraphDef expected; - TF_EXPECT_OK(scope.ToGraphDef(&expected)); - TF_EXPECT_GRAPH_EQ(expected, graph_def); - } - - // Condition graph. - { - Scope scope = Scope::NewRootScope().ExitOnError(); - auto arg0 = ops::_Arg(scope.WithOpName("_arg0"), DT_INT32, 0); - auto arg1 = ops::_Arg(scope.WithOpName("_arg1"), DT_INT32, 1); - auto three = ops::Const(scope.WithOpName("while/cond/three") + // Condition graph. + { + Scope scope = Scope::NewRootScope().ExitOnError(); + auto arg0 = ops::_Arg(scope.WithOpName("_arg0"), DT_INT32, 0); + auto arg1 = ops::_Arg(scope.WithOpName("_arg1"), DT_INT32, 1); + auto three = ops::Const(scope.WithOpName("while/cond/three") + .WithControlDependencies(arg0.output), + 3); + auto cond_add = + ops::Add(scope.WithOpName("while/cond/Add"), arg0.output, three); + auto ten = ops::Const(scope.WithOpName("while/cond/ten") .WithControlDependencies(arg0.output), - 3); - auto cond_add = - ops::Add(scope.WithOpName("while/cond/Add"), arg0.output, three); - auto ten = ops::Const( - scope.WithOpName("while/cond/ten").WithControlDependencies(arg0.output), - 10); - auto less = ops::Less(scope.WithOpName("while/cond/Less"), cond_add, ten); - auto retval = ops::_Retval(scope.WithOpName("_retval0_RetVal"), less, 0); - - GraphDef expected; - TF_EXPECT_OK(scope.ToGraphDef(&expected)); - - InstantiationResultForTest result; - TF_EXPECT_OK(InstantiateFunctionForTest(cond_fn.name(), library, &result)); - - EXPECT_EQ((DataTypeVector{DT_INT32, DT_INT32}), result.arg_types); - EXPECT_EQ(DataTypeVector{DT_BOOL}, result.ret_types); - TF_EXPECT_GRAPH_EQ(expected, result.gdef); - } - - // Body graph. - { - Scope scope = Scope::NewRootScope().ExitOnError(); - auto arg0 = ops::_Arg(scope.WithOpName("_arg0"), DT_INT32, 0); - auto arg1 = ops::_Arg(scope.WithOpName("_arg1"), DT_INT32, 1); - - auto identity_x = ops::Identity(scope.WithOpName("while/Identity/x"), arg0); - auto identity_y = ops::Identity(scope.WithOpName("while/Identity/y"), arg1); - - auto one = ops::Const( - scope.WithOpName("while/add/one").WithControlDependencies(identity_x), - 1); - auto two = ops::Const( - scope.WithOpName("while/mul/two").WithControlDependencies(identity_x), - 2); + 10); + auto less = ops::Less(scope.WithOpName("while/cond/Less"), cond_add, ten); + auto retval = ops::_Retval(scope.WithOpName("_retval0_RetVal"), less, 0); - auto add = ops::Add(scope.WithOpName("while/add"), identity_x, one); - auto mul = ops::Add(scope.WithOpName("while/mul"), identity_y, two); - auto retval0 = ops::_Retval(scope.WithOpName("_retval0_RetVal"), add, 0); - auto retval1 = ops::_Retval(scope.WithOpName("_retval1_RetVal"), mul, 1); + GraphDef expected; + TF_EXPECT_OK(scope.ToGraphDef(&expected)); - GraphDef expected; - TF_EXPECT_OK(scope.ToGraphDef(&expected)); + InstantiationResultForTest result; + TF_EXPECT_OK( + InstantiateFunctionForTest(cond_fn.name(), library, &result)); - InstantiationResultForTest result; - TF_EXPECT_OK(InstantiateFunctionForTest(body_fn.name(), library, &result)); + EXPECT_EQ((DataTypeVector{DT_INT32, DT_INT32}), result.arg_types); + EXPECT_EQ(DataTypeVector{DT_BOOL}, result.ret_types); + TF_EXPECT_GRAPH_EQ(expected, result.gdef); + } - EXPECT_EQ((DataTypeVector{DT_INT32, DT_INT32}), result.arg_types); - EXPECT_EQ((DataTypeVector{DT_INT32, DT_INT32}), result.ret_types); - TF_EXPECT_GRAPH_EQ(expected, result.gdef); + // Body graph. + { + Scope scope = Scope::NewRootScope().ExitOnError(); + auto arg0 = ops::_Arg(scope.WithOpName("_arg0"), DT_INT32, 0); + auto arg1 = ops::_Arg(scope.WithOpName("_arg1"), DT_INT32, 1); + + auto identity_x = + ops::Identity(scope.WithOpName("while/Identity/x"), arg0); + auto identity_y = + ops::Identity(scope.WithOpName("while/Identity/y"), arg1); + + auto one = ops::Const( + scope.WithOpName("while/add/one").WithControlDependencies(identity_x), + 1); + auto two = ops::Const( + scope.WithOpName("while/mul/two").WithControlDependencies(identity_x), + 2); + + auto add = ops::Add(scope.WithOpName("while/add"), identity_x, one); + auto mul = ops::Add(scope.WithOpName("while/mul"), identity_y, two); + auto retval0 = ops::_Retval(scope.WithOpName("_retval0_RetVal"), add, 0); + auto retval1 = ops::_Retval(scope.WithOpName("_retval1_RetVal"), mul, 1); + + GraphDef expected; + TF_EXPECT_OK(scope.ToGraphDef(&expected)); + + InstantiationResultForTest result; + TF_EXPECT_OK( + InstantiateFunctionForTest(body_fn.name(), library, &result)); + + EXPECT_EQ((DataTypeVector{DT_INT32, DT_INT32}), result.arg_types); + EXPECT_EQ((DataTypeVector{DT_INT32, DT_INT32}), result.ret_types); + TF_EXPECT_GRAPH_EQ(expected, result.gdef); + } } } @@ -841,177 +911,192 @@ TEST(FunctionalizeControlFlow, Complex) { } FunctionLibraryDefinition library(OpRegistry::Global(), {}); + GraphDef optimized_graph_def; + graph.ToGraphDef(&optimized_graph_def); + TF_ASSERT_OK( + FunctionalizeControlFlowForGraphDef(&optimized_graph_def, &library)); TF_ASSERT_OK(FunctionalizeControlFlow(&graph, &library)); + GraphDef converted_graph_def; + graph.ToGraphDef(&converted_graph_def); - GraphDef graph_def; - graph.ToGraphDef(&graph_def); - - NameAttrList outer_cond_fn, outer_body_fn; - TF_EXPECT_OK(FindWhileCondAndBody(graph_def, &outer_cond_fn, &outer_body_fn)); - - // Outer graph. - { - Scope scope = Scope::NewRootScope().ExitOnError(); - auto x = ops::Placeholder(scope.WithOpName("x"), DT_INT32); - auto three = ops::Const(scope.WithOpName("three"), 3); - auto y = ops::Add(scope.WithOpName("y"), x, three); - - auto var = ops::VarHandleOp(scope.WithOpName("Variable"), DT_INT32, - TensorShape({})); - - auto zero = ops::Const(scope.WithOpName("outer/Const"), 0); - - auto while_op = ops::While(scope.WithOpName("outer/LoopCond"), - std::initializer_list{zero, y, x, var}, - outer_cond_fn, outer_body_fn); - auto sink = ops::Identity(scope.WithOpName("sink"), while_op[0]); - GraphDef expected; - TF_EXPECT_OK(scope.ToGraphDef(&expected)); - TF_EXPECT_GRAPH_EQ(expected, graph_def); - } - - // Outer condition graph. - { - Scope scope = Scope::NewRootScope().ExitOnError(); - auto arg0 = ops::_Arg(scope.WithOpName("_arg0"), DT_INT32, 0); - auto arg1 = ops::_Arg(scope.WithOpName("_arg1"), DT_INT32, 1); - auto arg2 = ops::_Arg(scope.WithOpName("_arg2"), DT_INT32, 2); - auto arg3 = ops::_Arg(scope.WithOpName("_arg3"), DT_RESOURCE, 3); - - auto ten = ops::Const( - scope.WithOpName("outer/Less/y").WithControlDependencies(arg0.output), - 10); - auto less = ops::Less(scope.WithOpName("outer/Less_i"), arg0, ten); - auto retval = ops::_Retval(scope.WithOpName("_retval0_RetVal"), less, 0); - - GraphDef expected; - TF_EXPECT_OK(scope.ToGraphDef(&expected)); - - InstantiationResultForTest result; - TF_EXPECT_OK( - InstantiateFunctionForTest(outer_cond_fn.name(), library, &result)); - - EXPECT_EQ((DataTypeVector{DT_INT32, DT_INT32, DT_INT32, DT_RESOURCE}), - result.arg_types); - EXPECT_EQ(DataTypeVector{DT_BOOL}, result.ret_types); - TF_EXPECT_GRAPH_EQ(expected, result.gdef); - } - - // Outer body graph. - NameAttrList inner_cond_fn, inner_body_fn; - { - InstantiationResultForTest result; - TF_EXPECT_OK( - InstantiateFunctionForTest(outer_body_fn.name(), library, &result)); - - // Find the inner condition and body names. - TF_EXPECT_OK( - FindWhileCondAndBody(result.gdef, &inner_cond_fn, &inner_body_fn)); - - Scope scope = Scope::NewRootScope().ExitOnError(); - auto arg0 = ops::_Arg(scope.WithOpName("_arg0"), DT_INT32, 0); - auto arg1 = ops::_Arg(scope.WithOpName("_arg1"), DT_INT32, 1); - auto arg2 = ops::_Arg(scope.WithOpName("_arg2"), DT_INT32, 2); - auto arg3 = ops::_Arg(scope.WithOpName("_arg3"), DT_RESOURCE, 3); - - auto identity_i = ops::Identity(scope.WithOpName("outer/Identity"), arg0); - auto one_j = ops::Const( - scope.WithOpName("outer/j").WithControlDependencies(identity_i), 1); - auto while_op = - ops::While(scope.WithOpName("outer/LoopCond_1"), - std::initializer_list{one_j, arg1, arg2, arg3}, - inner_cond_fn, inner_body_fn); - - auto one_outer = ops::Const( - scope.WithOpName("outer/add/y").WithControlDependencies(identity_i), 1); - auto add_i = - ops::Add(scope.WithOpName("outer/add") - .WithControlDependencies(absl::Span{ - while_op[0].op(), while_op[1].op()}), - identity_i, one_outer); - - auto retval0 = ops::_Retval(scope.WithOpName("_retval0_RetVal"), add_i, 0); - auto retval1 = ops::_Retval(scope.WithOpName("_retval1_RetVal"), arg1, 1); - auto retval2 = ops::_Retval(scope.WithOpName("_retval2_RetVal"), arg2, 2); - - GraphDef expected; - TF_EXPECT_OK(scope.ToGraphDef(&expected)); - - EXPECT_EQ((DataTypeVector{DT_INT32, DT_INT32, DT_INT32, DT_RESOURCE}), - result.arg_types); - EXPECT_EQ((DataTypeVector{DT_INT32, DT_INT32, DT_INT32}), result.ret_types); - TF_EXPECT_GRAPH_EQ(expected, result.gdef); - } - - // Inner condition graph. - { - Scope scope = Scope::NewRootScope().ExitOnError(); - auto arg0 = ops::_Arg(scope.WithOpName("_arg0"), DT_INT32, 0); - auto arg1 = ops::_Arg(scope.WithOpName("_arg1"), DT_INT32, 1); - auto arg2 = ops::_Arg(scope.WithOpName("_arg2"), DT_INT32, 2); - auto arg3 = ops::_Arg(scope.WithOpName("_arg3"), DT_RESOURCE, 3); - - auto five = ops::Const( - scope.WithOpName("outer/inner/Five").WithControlDependencies(arg0), 5); - auto less_j = ops::Less(scope.WithOpName("outer/inner/Less_j"), arg0, five); - auto retval = ops::_Retval(scope.WithOpName("_retval0_RetVal"), less_j, 0); - - GraphDef expected; - TF_EXPECT_OK(scope.ToGraphDef(&expected)); - - InstantiationResultForTest result; + for (const GraphDef& graph_def : {optimized_graph_def, converted_graph_def}) { + NameAttrList outer_cond_fn, outer_body_fn; TF_EXPECT_OK( - InstantiateFunctionForTest(inner_cond_fn.name(), library, &result)); - - EXPECT_EQ((DataTypeVector{DT_INT32, DT_INT32, DT_INT32, DT_RESOURCE}), - result.arg_types); - EXPECT_EQ(DataTypeVector{DT_BOOL}, result.ret_types); - TF_EXPECT_GRAPH_EQ(expected, result.gdef); - } - - // Inner body graph. - { - Scope scope = Scope::NewRootScope().ExitOnError(); - auto arg0 = ops::_Arg(scope.WithOpName("_arg0"), DT_INT32, 0); - auto arg1 = ops::_Arg(scope.WithOpName("_arg1"), DT_INT32, 1); - auto arg2 = ops::_Arg(scope.WithOpName("_arg2"), DT_INT32, 2); - auto arg3 = ops::_Arg(scope.WithOpName("_arg3"), DT_RESOURCE, 3); - - auto identity_j = - ops::Identity(scope.WithOpName("outer/inner/Identity_j"), arg0); - auto identity_k = - ops::Identity(scope.WithOpName("outer/inner/Identity_k"), arg1); - - auto mul_jk = - ops::Mul(scope.WithOpName("outer/inner/mul"), identity_j, identity_k); - auto add_jkx = ops::Add(scope.WithOpName("outer/inner/add"), mul_jk, arg2); - auto assign = ops::AssignAddVariableOp( - scope.WithOpName("outer/inner/assign_add"), arg3, add_jkx); - - auto one = ops::Const( - scope.WithOpName("outer/inner/One") - .WithControlDependencies( - absl::Span{assign.operation}), - 1); - auto add_j = - ops::Add(scope.WithOpName("outer/inner/add_j"), identity_j, one); + FindWhileCondAndBody(graph_def, &outer_cond_fn, &outer_body_fn)); + + // Outer graph. + { + Scope scope = Scope::NewRootScope().ExitOnError(); + auto x = ops::Placeholder(scope.WithOpName("x"), DT_INT32); + auto three = ops::Const(scope.WithOpName("three"), 3); + auto y = ops::Add(scope.WithOpName("y"), x, three); + + auto var = ops::VarHandleOp(scope.WithOpName("Variable"), DT_INT32, + TensorShape({})); + + auto zero = ops::Const(scope.WithOpName("outer/Const"), 0); + + auto while_op = ops::While(scope.WithOpName("outer/LoopCond"), + std::initializer_list{zero, y, x, var}, + outer_cond_fn, outer_body_fn); + auto sink = ops::Identity(scope.WithOpName("sink"), while_op[0]); + GraphDef expected; + TF_EXPECT_OK(scope.ToGraphDef(&expected)); + TF_EXPECT_GRAPH_EQ(expected, graph_def); + } - auto retval0 = ops::_Retval(scope.WithOpName("_retval0_RetVal"), add_j, 0); - auto retval1 = - ops::_Retval(scope.WithOpName("_retval1_RetVal"), identity_k, 1); - auto retval2 = ops::_Retval(scope.WithOpName("_retval2_RetVal"), arg2, 2); + // Outer condition graph. + { + Scope scope = Scope::NewRootScope().ExitOnError(); + auto arg0 = ops::_Arg(scope.WithOpName("_arg0"), DT_INT32, 0); + auto arg1 = ops::_Arg(scope.WithOpName("_arg1"), DT_INT32, 1); + auto arg2 = ops::_Arg(scope.WithOpName("_arg2"), DT_INT32, 2); + auto arg3 = ops::_Arg(scope.WithOpName("_arg3"), DT_RESOURCE, 3); + + auto ten = ops::Const( + scope.WithOpName("outer/Less/y").WithControlDependencies(arg0.output), + 10); + auto less = ops::Less(scope.WithOpName("outer/Less_i"), arg0, ten); + auto retval = ops::_Retval(scope.WithOpName("_retval0_RetVal"), less, 0); + + GraphDef expected; + TF_EXPECT_OK(scope.ToGraphDef(&expected)); + + InstantiationResultForTest result; + TF_EXPECT_OK( + InstantiateFunctionForTest(outer_cond_fn.name(), library, &result)); + + EXPECT_EQ((DataTypeVector{DT_INT32, DT_INT32, DT_INT32, DT_RESOURCE}), + result.arg_types); + EXPECT_EQ(DataTypeVector{DT_BOOL}, result.ret_types); + TF_EXPECT_GRAPH_EQ(expected, result.gdef); + } - GraphDef expected; - TF_EXPECT_OK(scope.ToGraphDef(&expected)); + // Outer body graph. + NameAttrList inner_cond_fn, inner_body_fn; + { + InstantiationResultForTest result; + TF_EXPECT_OK( + InstantiateFunctionForTest(outer_body_fn.name(), library, &result)); + + // Find the inner condition and body names. + TF_EXPECT_OK( + FindWhileCondAndBody(result.gdef, &inner_cond_fn, &inner_body_fn)); + + Scope scope = Scope::NewRootScope().ExitOnError(); + auto arg0 = ops::_Arg(scope.WithOpName("_arg0"), DT_INT32, 0); + auto arg1 = ops::_Arg(scope.WithOpName("_arg1"), DT_INT32, 1); + auto arg2 = ops::_Arg(scope.WithOpName("_arg2"), DT_INT32, 2); + auto arg3 = ops::_Arg(scope.WithOpName("_arg3"), DT_RESOURCE, 3); + + auto identity_i = ops::Identity(scope.WithOpName("outer/Identity"), arg0); + auto one_j = ops::Const( + scope.WithOpName("outer/j").WithControlDependencies(identity_i), 1); + auto while_op = + ops::While(scope.WithOpName("outer/LoopCond_1"), + std::initializer_list{one_j, arg1, arg2, arg3}, + inner_cond_fn, inner_body_fn); + + auto one_outer = ops::Const( + scope.WithOpName("outer/add/y").WithControlDependencies(identity_i), + 1); + auto add_i = + ops::Add(scope.WithOpName("outer/add") + .WithControlDependencies(absl::Span{ + while_op[0].op(), while_op[1].op()}), + identity_i, one_outer); + + auto retval0 = + ops::_Retval(scope.WithOpName("_retval0_RetVal"), add_i, 0); + auto retval1 = ops::_Retval(scope.WithOpName("_retval1_RetVal"), arg1, 1); + auto retval2 = ops::_Retval(scope.WithOpName("_retval2_RetVal"), arg2, 2); + + GraphDef expected; + TF_EXPECT_OK(scope.ToGraphDef(&expected)); + + EXPECT_EQ((DataTypeVector{DT_INT32, DT_INT32, DT_INT32, DT_RESOURCE}), + result.arg_types); + EXPECT_EQ((DataTypeVector{DT_INT32, DT_INT32, DT_INT32}), + result.ret_types); + TF_EXPECT_GRAPH_EQ(expected, result.gdef); + } - InstantiationResultForTest result; - TF_EXPECT_OK( - InstantiateFunctionForTest(inner_body_fn.name(), library, &result)); + // Inner condition graph. + { + Scope scope = Scope::NewRootScope().ExitOnError(); + auto arg0 = ops::_Arg(scope.WithOpName("_arg0"), DT_INT32, 0); + auto arg1 = ops::_Arg(scope.WithOpName("_arg1"), DT_INT32, 1); + auto arg2 = ops::_Arg(scope.WithOpName("_arg2"), DT_INT32, 2); + auto arg3 = ops::_Arg(scope.WithOpName("_arg3"), DT_RESOURCE, 3); + + auto five = ops::Const( + scope.WithOpName("outer/inner/Five").WithControlDependencies(arg0), + 5); + auto less_j = + ops::Less(scope.WithOpName("outer/inner/Less_j"), arg0, five); + auto retval = + ops::_Retval(scope.WithOpName("_retval0_RetVal"), less_j, 0); + + GraphDef expected; + TF_EXPECT_OK(scope.ToGraphDef(&expected)); + + InstantiationResultForTest result; + TF_EXPECT_OK( + InstantiateFunctionForTest(inner_cond_fn.name(), library, &result)); + + EXPECT_EQ((DataTypeVector{DT_INT32, DT_INT32, DT_INT32, DT_RESOURCE}), + result.arg_types); + EXPECT_EQ(DataTypeVector{DT_BOOL}, result.ret_types); + TF_EXPECT_GRAPH_EQ(expected, result.gdef); + } - EXPECT_EQ((DataTypeVector{DT_INT32, DT_INT32, DT_INT32, DT_RESOURCE}), - result.arg_types); - EXPECT_EQ((DataTypeVector{DT_INT32, DT_INT32, DT_INT32}), result.ret_types); - TF_EXPECT_GRAPH_EQ(expected, result.gdef); + // Inner body graph. + { + Scope scope = Scope::NewRootScope().ExitOnError(); + auto arg0 = ops::_Arg(scope.WithOpName("_arg0"), DT_INT32, 0); + auto arg1 = ops::_Arg(scope.WithOpName("_arg1"), DT_INT32, 1); + auto arg2 = ops::_Arg(scope.WithOpName("_arg2"), DT_INT32, 2); + auto arg3 = ops::_Arg(scope.WithOpName("_arg3"), DT_RESOURCE, 3); + + auto identity_j = + ops::Identity(scope.WithOpName("outer/inner/Identity_j"), arg0); + auto identity_k = + ops::Identity(scope.WithOpName("outer/inner/Identity_k"), arg1); + + auto mul_jk = + ops::Mul(scope.WithOpName("outer/inner/mul"), identity_j, identity_k); + auto add_jkx = + ops::Add(scope.WithOpName("outer/inner/add"), mul_jk, arg2); + auto assign = ops::AssignAddVariableOp( + scope.WithOpName("outer/inner/assign_add"), arg3, add_jkx); + + auto one = ops::Const( + scope.WithOpName("outer/inner/One") + .WithControlDependencies( + absl::Span{assign.operation}), + 1); + auto add_j = + ops::Add(scope.WithOpName("outer/inner/add_j"), identity_j, one); + + auto retval0 = + ops::_Retval(scope.WithOpName("_retval0_RetVal"), add_j, 0); + auto retval1 = + ops::_Retval(scope.WithOpName("_retval1_RetVal"), identity_k, 1); + auto retval2 = ops::_Retval(scope.WithOpName("_retval2_RetVal"), arg2, 2); + + GraphDef expected; + TF_EXPECT_OK(scope.ToGraphDef(&expected)); + + InstantiationResultForTest result; + TF_EXPECT_OK( + InstantiateFunctionForTest(inner_body_fn.name(), library, &result)); + + EXPECT_EQ((DataTypeVector{DT_INT32, DT_INT32, DT_INT32, DT_RESOURCE}), + result.arg_types); + EXPECT_EQ((DataTypeVector{DT_INT32, DT_INT32, DT_INT32}), + result.ret_types); + TF_EXPECT_GRAPH_EQ(expected, result.gdef); + } } } diff --git a/tensorflow/compiler/tf2xla/kernels/BUILD b/tensorflow/compiler/tf2xla/kernels/BUILD index d85b4f5ae0..fa51a72aea 100644 --- a/tensorflow/compiler/tf2xla/kernels/BUILD +++ b/tensorflow/compiler/tf2xla/kernels/BUILD @@ -121,7 +121,6 @@ tf_kernel_library( ":while_op", "//tensorflow/compiler/tf2xla:common", "//tensorflow/compiler/tf2xla:xla_compiler", - "//tensorflow/compiler/tf2xla/lib:batch_dot", "//tensorflow/compiler/tf2xla/lib:broadcast", "//tensorflow/compiler/tf2xla/lib:cholesky", "//tensorflow/compiler/tf2xla/lib:qr", @@ -144,7 +143,7 @@ tf_kernel_library( "//tensorflow/compiler/xla/client/lib:arithmetic", "//tensorflow/compiler/xla/client/lib:constants", "//tensorflow/compiler/xla/client/lib:math", - "//tensorflow/compiler/xla/client/lib:numeric", + "//tensorflow/compiler/xla/client/lib:matrix", "//tensorflow/compiler/xla/client/lib:pooling", "//tensorflow/compiler/xla/client/lib:prng", "//tensorflow/compiler/xla/client/lib:sorting", @@ -196,7 +195,6 @@ cc_library( "//tensorflow/compiler/xla/client:xla_builder", "//tensorflow/compiler/xla/client/lib:arithmetic", "//tensorflow/compiler/xla/client/lib:constants", - "//tensorflow/compiler/xla/client/lib:numeric", "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:protos_all_cc", @@ -216,7 +214,6 @@ cc_library( "//tensorflow/compiler/xla/client:xla_builder", "//tensorflow/compiler/xla/client/lib:arithmetic", "//tensorflow/compiler/xla/client/lib:constants", - "//tensorflow/compiler/xla/client/lib:numeric", "//tensorflow/core:framework", "//tensorflow/core/kernels:bounds_check", "//tensorflow/core/kernels:conv_ops", diff --git a/tensorflow/compiler/tf2xla/kernels/arg_op.cc b/tensorflow/compiler/tf2xla/kernels/arg_op.cc index 2db2514397..795ea09831 100644 --- a/tensorflow/compiler/tf2xla/kernels/arg_op.cc +++ b/tensorflow/compiler/tf2xla/kernels/arg_op.cc @@ -50,7 +50,7 @@ class XlaArgOp : public XlaOpKernel { return; } - const XlaExpression& arg = XlaContext::Get(ctx).args()[index_]; + const XlaExpression& arg = ctx->xla_context()->args()[index_]; OP_REQUIRES(ctx, arg.kind() != XlaExpression::Kind::kInvalid, errors::InvalidArgument("Invalid/missing argument expression")); ctx->SetOutputExpression(0, arg); diff --git a/tensorflow/compiler/tf2xla/kernels/batch_matmul_op.cc b/tensorflow/compiler/tf2xla/kernels/batch_matmul_op.cc index 4cfe946b2e..1b254e328a 100644 --- a/tensorflow/compiler/tf2xla/kernels/batch_matmul_op.cc +++ b/tensorflow/compiler/tf2xla/kernels/batch_matmul_op.cc @@ -13,9 +13,11 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "tensorflow/compiler/tf2xla/lib/batch_dot.h" +#include "tensorflow/compiler/tf2xla/lib/util.h" #include "tensorflow/compiler/tf2xla/xla_op_kernel.h" #include "tensorflow/compiler/tf2xla/xla_op_registry.h" +#include "tensorflow/compiler/xla/client/lib/math.h" +#include "tensorflow/compiler/xla/client/lib/matrix.h" namespace tensorflow { namespace { @@ -28,9 +30,11 @@ class BatchMatMulOp : public XlaOpKernel { } void Compile(XlaOpKernelContext* ctx) override { - auto result = BatchDot(ctx->Input(0), ctx->Input(1), - /*transpose_x=*/adj_x_, /*transpose_y=*/adj_y_, - /*conjugate_x=*/adj_x_, /*conjugate_y=*/adj_y_); + auto result = + xla::BatchDot(MaybeTransposeInMinorDims( + MaybeConjugate(ctx->Input(0), adj_x_), adj_x_), + MaybeTransposeInMinorDims( + MaybeConjugate(ctx->Input(1), adj_y_), adj_y_)); ctx->SetOutput(0, result); } diff --git a/tensorflow/compiler/tf2xla/kernels/batch_norm_op.cc b/tensorflow/compiler/tf2xla/kernels/batch_norm_op.cc index a267c0c72f..0e2f335f33 100644 --- a/tensorflow/compiler/tf2xla/kernels/batch_norm_op.cc +++ b/tensorflow/compiler/tf2xla/kernels/batch_norm_op.cc @@ -115,9 +115,9 @@ class FusedBatchNormGradOp : public XlaOpKernel { // operators. For now, cast everything to the statistics type (which // may be more precise than the input type). auto grad_backprop = - XlaHelpers::ConvertElementType(b, ctx->Input(0), scale_dtype); + XlaHelpers::ConvertElementType(ctx->Input(0), scale_dtype); auto activations = - XlaHelpers::ConvertElementType(b, ctx->Input(1), scale_dtype); + XlaHelpers::ConvertElementType(ctx->Input(1), scale_dtype); auto scale = ctx->Input(2); auto mean = ctx->Input(3); auto var = ctx->Input(4); @@ -151,11 +151,11 @@ class FusedBatchNormGradOp : public XlaOpKernel { const DataType accumulation_type = XlaHelpers::SumAccumulationType(scale_dtype); auto converted = - XlaHelpers::ConvertElementType(b, grad_backprop, accumulation_type); + XlaHelpers::ConvertElementType(grad_backprop, accumulation_type); auto reduce = xla::Reduce(converted, XlaHelpers::Zero(b, accumulation_type), *ctx->GetOrCreateAdd(accumulation_type), reduction_dims); - offset_backprop = XlaHelpers::ConvertElementType(b, reduce, scale_dtype); + offset_backprop = XlaHelpers::ConvertElementType(reduce, scale_dtype); // scratch1 = rsqrt(pop_var + epsilon) auto neg_half = XlaHelpers::FloatLiteral(b, scale_dtype, -0.5); @@ -165,19 +165,18 @@ class FusedBatchNormGradOp : public XlaOpKernel { // scratch2 = sum(y_backprop * (x - mean)) auto mul = xla::Mul(grad_backprop, xla::Sub(activations, mean, {feature_index})); - converted = XlaHelpers::ConvertElementType(b, mul, accumulation_type); + converted = XlaHelpers::ConvertElementType(mul, accumulation_type); reduce = xla::Reduce(converted, XlaHelpers::Zero(b, accumulation_type), *ctx->GetOrCreateAdd(accumulation_type), reduction_dims); - auto scratch2 = XlaHelpers::ConvertElementType(b, reduce, scale_dtype); + auto scratch2 = XlaHelpers::ConvertElementType(reduce, scale_dtype); x_backprop = xla::Mul(grad_backprop, xla::Mul(scratch1, scale), {feature_index}); scale_backprop = xla::Mul(scratch1, scratch2); } - ctx->SetOutput(0, - XlaHelpers::ConvertElementType(b, x_backprop, input_dtype)); + ctx->SetOutput(0, XlaHelpers::ConvertElementType(x_backprop, input_dtype)); ctx->SetOutput(1, scale_backprop); ctx->SetOutput(2, offset_backprop); ctx->SetConstantOutput(3, Tensor()); diff --git a/tensorflow/compiler/tf2xla/kernels/bias_ops.cc b/tensorflow/compiler/tf2xla/kernels/bias_ops.cc index 41f540506b..e7f369b761 100644 --- a/tensorflow/compiler/tf2xla/kernels/bias_ops.cc +++ b/tensorflow/compiler/tf2xla/kernels/bias_ops.cc @@ -107,11 +107,11 @@ class BiasAddGradOp : public XlaOpKernel { const DataType accumulation_type = XlaHelpers::SumAccumulationType(input_type(0)); auto converted = - XlaHelpers::ConvertElementType(b, ctx->Input(0), accumulation_type); + XlaHelpers::ConvertElementType(ctx->Input(0), accumulation_type); auto reduce = xla::Reduce(converted, XlaHelpers::Zero(b, accumulation_type), *ctx->GetOrCreateAdd(accumulation_type), reduce_dims); - ctx->SetOutput(0, XlaHelpers::ConvertElementType(b, reduce, input_type(0))); + ctx->SetOutput(0, XlaHelpers::ConvertElementType(reduce, input_type(0))); } private: diff --git a/tensorflow/compiler/tf2xla/kernels/binary_ops.cc b/tensorflow/compiler/tf2xla/kernels/binary_ops.cc index 47e517a657..5e9280c1fe 100644 --- a/tensorflow/compiler/tf2xla/kernels/binary_ops.cc +++ b/tensorflow/compiler/tf2xla/kernels/binary_ops.cc @@ -43,6 +43,9 @@ namespace { const std::vector& extend_dimensions) override { \ xla::XlaBuilder* b = ctx->builder(); \ (void)b; \ + (void)lhs_shape; \ + (void)rhs_shape; \ + (void)extend_dimensions; \ return HLO; \ } \ }; \ @@ -103,23 +106,23 @@ static xla::XlaOp FloorDivImpl(xla::XlaBuilder* b, DataType dtype, xla::XlaOp x, XLA_MAKE_BINARY(FloorDiv, FloorDivImpl(b, input_type(0), lhs, rhs, broadcast_helper)); -static xla::XlaOp XlogyImpl(xla::XlaBuilder* b, DataType dtype, xla::XlaOp x, - xla::XlaOp y, const BCast& broadcast_helper) { +xla::XlaOp XlogyImpl(xla::XlaOp x, xla::XlaOp y, + const BCast& broadcast_helper) { std::tie(x, y) = XlaBinaryOp::Broadcast(x, y, broadcast_helper); - auto zero = XlaHelpers::Zero(b, dtype); + auto zero = xla::ZerosLike(x); auto is_zero = xla::Eq(x, zero); return xla::Select(is_zero, zero, xla::Mul(x, xla::Log(y))); } -XLA_MAKE_BINARY(Xlogy, XlogyImpl(b, input_type(0), lhs, rhs, broadcast_helper)); +XLA_MAKE_BINARY(Xlogy, XlogyImpl(lhs, rhs, broadcast_helper)); -static xla::XlaOp XdivyImpl(xla::XlaBuilder* b, DataType dtype, xla::XlaOp x, - xla::XlaOp y, const BCast& broadcast_helper) { +xla::XlaOp XdivyImpl(xla::XlaOp x, xla::XlaOp y, + const BCast& broadcast_helper) { std::tie(x, y) = XlaBinaryOp::Broadcast(x, y, broadcast_helper); - auto zero = XlaHelpers::Zero(b, dtype); + auto zero = xla::ZerosLike(x); auto is_zero = xla::Eq(x, zero); return xla::Select(is_zero, zero, xla::Div(x, y)); } -XLA_MAKE_BINARY(Xdivy, XdivyImpl(b, input_type(0), lhs, rhs, broadcast_helper)); +XLA_MAKE_BINARY(Xdivy, XdivyImpl(lhs, rhs, broadcast_helper)); // Implementation of FloorMod. Pseudo-code: // T trunc_mod = std::fmod(x, y); diff --git a/tensorflow/compiler/tf2xla/kernels/categorical_op.cc b/tensorflow/compiler/tf2xla/kernels/categorical_op.cc index ad85940920..7199b9b6fe 100644 --- a/tensorflow/compiler/tf2xla/kernels/categorical_op.cc +++ b/tensorflow/compiler/tf2xla/kernels/categorical_op.cc @@ -21,10 +21,13 @@ limitations under the License. #include "tensorflow/compiler/tf2xla/xla_op_kernel.h" #include "tensorflow/compiler/tf2xla/xla_op_registry.h" #include "tensorflow/compiler/xla/client/lib/arithmetic.h" +#include "tensorflow/compiler/xla/client/lib/prng.h" #include "tensorflow/compiler/xla/client/xla_builder.h" +#include "tensorflow/compiler/xla/xla_data.pb.h" #include "tensorflow/core/framework/op_kernel.h" #include "tensorflow/core/framework/tensor.h" #include "tensorflow/core/framework/tensor_shape.h" +#include "tensorflow/core/framework/types.pb.h" namespace tensorflow { namespace { @@ -57,11 +60,9 @@ class CategoricalOp : public XlaOpKernel { const int64 batch_size = logits_shape.dim_size(0); const int64 num_classes = logits_shape.dim_size(1); - xla::XlaBuilder* builder = ctx->builder(); - xla::Shape uniform_shape; int class_dimension; - if (num_samples > 1) { + if (num_samples != 1) { std::array uniform_shape_array = { {batch_size, num_samples, num_classes}}; xla::PrimitiveType uniform_xla_type; @@ -83,16 +84,16 @@ class CategoricalOp : public XlaOpKernel { xla::ShapeUtil::MakeShape(uniform_xla_type, uniform_shape_array); class_dimension = 1; } - xla::XlaOp uniforms = - xla::RngUniform(XlaHelpers::Zero(builder, input_type(0)), - XlaHelpers::One(builder, input_type(0)), uniform_shape); + xla::PrimitiveType type; + OP_REQUIRES_OK(ctx, DataTypeToPrimitiveType(input_type(0), &type)); + xla::XlaOp log_uniforms = GetLogUniforms(uniform_shape, type, ctx); // Use Gumbel softmax trick to generate categorical samples. // See: // https://hips.seas.harvard.edu/blog/2013/04/06/the-gumbel-max-trick-for-discrete-distributions/ // TODO(b/68769470): Switch to using a cumulative sum approach. auto softmax_entries = - xla::Sub(logits, xla::Log(-xla::Log(uniforms)), + xla::Sub(logits, log_uniforms, /*broadcast_dimensions=*/{0, class_dimension}); xla::PrimitiveType xla_output_type; @@ -107,6 +108,16 @@ class CategoricalOp : public XlaOpKernel { ctx->SetOutput(0, argmax); } + virtual xla::XlaOp GetLogUniforms(xla::Shape uniform_shape, + xla::PrimitiveType type, + XlaOpKernelContext* ctx) { + xla::XlaBuilder* builder = ctx->builder(); + auto uniforms = + xla::RngUniform(XlaHelpers::Zero(builder, input_type(0)), + XlaHelpers::One(builder, input_type(0)), uniform_shape); + return xla::Log(-xla::Log(uniforms)); + } + private: TF_DISALLOW_COPY_AND_ASSIGN(CategoricalOp); }; @@ -115,5 +126,48 @@ class CategoricalOp : public XlaOpKernel { REGISTER_XLA_OP(Name("Multinomial").CompileTimeConstantInput("num_samples"), CategoricalOp); +class StatelessCategoricalOp : public CategoricalOp { + public: + explicit StatelessCategoricalOp(OpKernelConstruction* ctx) + : CategoricalOp(ctx) { + OP_REQUIRES_OK(ctx, ctx->GetAttr("T", &dtype_)); + } + + xla::XlaOp GetLogUniforms(xla::Shape uniform_shape, xla::PrimitiveType type, + XlaOpKernelContext* ctx) override { + xla::XlaOp seed = ctx->Input(2); + auto seed0 = xla::Reshape(xla::Slice(seed, {0}, {1}, {1}), {}); + auto seed1 = xla::Reshape(xla::Slice(seed, {1}, {2}, {1}), {}); + + xla::XlaBuilder* builder = ctx->builder(); + if (uniform_shape.element_type() == xla::BF16) { + uniform_shape.set_element_type(xla::F32); + } + auto uniforms = xla::StatelessRngUniform( + {seed0, seed1}, uniform_shape, XlaHelpers::Zero(builder, DT_FLOAT), + XlaHelpers::One(builder, DT_FLOAT)); + return xla::ConvertElementType(xla::Log(-xla::Log(uniforms)), type); + } + + void Compile(XlaOpKernelContext* ctx) override { + TensorShape seed_shape = ctx->InputShape(2); + OP_REQUIRES(ctx, seed_shape.dims() == 1 && seed_shape.dim_size(0) == 2, + errors::InvalidArgument("seed must have shape [2], not ", + seed_shape.DebugString())); + CategoricalOp::Compile(ctx); + } + + private: + DataType dtype_; + + TF_DISALLOW_COPY_AND_ASSIGN(StatelessCategoricalOp); +}; + +REGISTER_XLA_OP(Name("StatelessMultinomial") + .CompileTimeConstantInput("num_samples") + .TypeConstraint("T", {DT_FLOAT, DT_BFLOAT16}) + .TypeConstraint("Tseed", DT_INT32), + StatelessCategoricalOp); + } // anonymous namespace } // namespace tensorflow diff --git a/tensorflow/compiler/tf2xla/kernels/conv_op_helpers.cc b/tensorflow/compiler/tf2xla/kernels/conv_op_helpers.cc index c9a1be4940..641fefafb3 100644 --- a/tensorflow/compiler/tf2xla/kernels/conv_op_helpers.cc +++ b/tensorflow/compiler/tf2xla/kernels/conv_op_helpers.cc @@ -24,7 +24,6 @@ limitations under the License. #include "tensorflow/compiler/tf2xla/xla_op_registry.h" #include "tensorflow/compiler/xla/client/lib/arithmetic.h" #include "tensorflow/compiler/xla/client/lib/constants.h" -#include "tensorflow/compiler/xla/client/lib/numeric.h" #include "tensorflow/compiler/xla/client/xla_builder.h" #include "tensorflow/compiler/xla/literal_util.h" #include "tensorflow/core/framework/node_def_util.h" @@ -65,60 +64,63 @@ xla::Shape ExpandedFilterShapeForDepthwiseConvolution(const xla::Shape& shape) { // 0 0 1 1 0 0 0 0 1 1 0 0 // 0 0 0 0 1 1 0 0 0 0 1 1 // -// The first step is to create a one tensor, A, that is [3] -// 0 1 2 +// The first step is to create a iota A with iota_dimension = 2 +// 0 0 0 0 0 0 0 0 0 0 0 0 +// 1 1 1 1 1 1 1 1 1 1 1 1 +// 2 2 2 2 2 2 2 2 2 2 2 2 // -// and another tensor, B, that is [3 * 2] -// 0 1 2 3 4 5 +// 0 0 0 0 0 0 0 0 0 0 0 0 +// 1 1 1 1 1 1 1 1 1 1 1 1 +// 2 2 2 2 2 2 2 2 2 2 2 2 // -// and divide B it by 2 to get -// 0 0 1 1 2 2 +// and another iota B with iota_dimension = 3 +// 0 1 2 3 4 5 0 1 2 3 4 5 +// 0 1 2 3 4 5 0 1 2 3 4 5 +// 0 1 2 3 4 5 0 1 2 3 4 5 // -// then we broadcast the B to [2, 2, 3, 3 * 2] -// 0 0 1 1 2 2 0 0 1 1 2 2 -// 0 0 1 1 2 2 0 0 1 1 2 2 -// 0 0 1 1 2 2 0 0 1 1 2 2 +// 0 1 2 3 4 5 0 1 2 3 4 5 +// 0 1 2 3 4 5 0 1 2 3 4 5 +// 0 1 2 3 4 5 0 1 2 3 4 5 // -// 0 0 1 1 2 2 0 0 1 1 2 2 -// 0 0 1 1 2 2 0 0 1 1 2 2 -// 0 0 1 1 2 2 0 0 1 1 2 2 +// and divide B by 2 to get +// 0 0 1 1 2 2 0 0 1 1 2 2 +// 0 0 1 1 2 2 0 0 1 1 2 2 +// 0 0 1 1 2 2 0 0 1 1 2 2 // -// Finally compare A and broadcasted B in dimension 2 amd return the result at -// the beginning of the comment. +// 0 0 1 1 2 2 0 0 1 1 2 2 +// 0 0 1 1 2 2 0 0 1 1 2 2 +// 0 0 1 1 2 2 0 0 1 1 2 2 +// +// Finally compare A and B and return the result at the beginning of the +// comment. xla::XlaOp CreateExpandedFilterMask(const xla::Shape& filter_shape, xla::XlaBuilder* builder) { xla::Shape expanded_filter_shape = ExpandedFilterShapeForDepthwiseConvolution(filter_shape); int64 depthwise_multiplier = filter_shape.dimensions(filter_shape.dimensions_size() - 1); - int64 input_feature = - filter_shape.dimensions(filter_shape.dimensions_size() - 2); - - // Create a M sized linspace and an M*N sized linspace that will be - // broadcasted into perpendicular dimensions and compared. - xla::XlaOp input_feature_iota = xla::Iota(builder, xla::S32, input_feature); - xla::XlaOp expanded_feature_iota = - xla::Iota(builder, xla::S32, input_feature * depthwise_multiplier); - // Divide the M*N sized linspace by the depthwise_multiplier to create - // [0 0 1 1 2 2] in the example in the function comment. + // Create two iotas with the shape of the expanded filter, one of them with + // the iota dimension chosen as the feature dimension, and the other a iota + // with the iota dimension chosen as the expanded output feature dimension. + std::vector iota_dimensions(expanded_filter_shape.dimensions().begin(), + expanded_filter_shape.dimensions().end()); + xla::Shape iota_shape = xla::ShapeUtil::MakeShape(xla::S32, iota_dimensions); + xla::XlaOp input_feature_iota = xla::Iota( + builder, iota_shape, /*iota_dimension=*/iota_dimensions.size() - 2); + xla::XlaOp expanded_feature_iota = xla::Iota( + builder, iota_shape, /*iota_dimension=*/iota_dimensions.size() - 1); + + // Divide 'expanded_feature_iota' by the depthwise_multiplier to create + // [0 0 1 1 2 2] ... in the example in the function comment. expanded_feature_iota = xla::Div(expanded_feature_iota, XlaHelpers::IntegerLiteral(builder, DataType::DT_INT32, depthwise_multiplier)); - // Broadcast the N*M linspace to [H, W, ..., M, M*N]. - std::vector expanded_feature_broadcast_dims( - expanded_filter_shape.dimensions().begin(), - expanded_filter_shape.dimensions().end()); - expanded_feature_broadcast_dims.pop_back(); - auto broadcasted_expanded_feature_iota = - xla::Broadcast(expanded_feature_iota, expanded_feature_broadcast_dims); - - // Compare the broadcasted linspace to the input feature linspace in the - // input feature dimension to create a diagonal predicate. - return xla::Eq(broadcasted_expanded_feature_iota, input_feature_iota, - {expanded_filter_shape.dimensions_size() - 2}); + // Compare 'input_feature_iota' with 'expanded_feature_iota' to create a + // diagonal predicate. + return xla::Eq(expanded_feature_iota, input_feature_iota); } // Reshapes a filter of shape [H, W, ..., M, N] to [H, W, ..., 1, M*N]. Used to diff --git a/tensorflow/compiler/tf2xla/kernels/conv_ops.cc b/tensorflow/compiler/tf2xla/kernels/conv_ops.cc index d820528a43..eafdba876a 100644 --- a/tensorflow/compiler/tf2xla/kernels/conv_ops.cc +++ b/tensorflow/compiler/tf2xla/kernels/conv_ops.cc @@ -22,7 +22,7 @@ limitations under the License. #include "tensorflow/compiler/tf2xla/xla_op_kernel.h" #include "tensorflow/compiler/tf2xla/xla_op_registry.h" #include "tensorflow/compiler/xla/client/lib/constants.h" -#include "tensorflow/compiler/xla/client/lib/numeric.h" +#include "tensorflow/compiler/xla/client/lib/matrix.h" #include "tensorflow/compiler/xla/client/xla_builder.h" #include "tensorflow/compiler/xla/literal_util.h" #include "tensorflow/core/framework/node_def_util.h" diff --git a/tensorflow/compiler/tf2xla/kernels/diag_op.cc b/tensorflow/compiler/tf2xla/kernels/diag_op.cc index 49c12fc232..ee79cbc70d 100644 --- a/tensorflow/compiler/tf2xla/kernels/diag_op.cc +++ b/tensorflow/compiler/tf2xla/kernels/diag_op.cc @@ -19,7 +19,7 @@ limitations under the License. #include "tensorflow/compiler/tf2xla/xla_op_kernel.h" #include "tensorflow/compiler/tf2xla/xla_op_registry.h" #include "tensorflow/compiler/xla/client/lib/constants.h" -#include "tensorflow/compiler/xla/client/lib/numeric.h" +#include "tensorflow/compiler/xla/client/lib/matrix.h" #include "tensorflow/compiler/xla/client/xla_builder.h" #include "tensorflow/compiler/xla/util.h" #include "tensorflow/core/framework/op_kernel.h" diff --git a/tensorflow/compiler/tf2xla/kernels/dynamic_stitch_op.cc b/tensorflow/compiler/tf2xla/kernels/dynamic_stitch_op.cc index b2f6ef43fa..6e6ba21daf 100644 --- a/tensorflow/compiler/tf2xla/kernels/dynamic_stitch_op.cc +++ b/tensorflow/compiler/tf2xla/kernels/dynamic_stitch_op.cc @@ -113,8 +113,20 @@ class DynamicStitchOp : public XlaOpKernel { } } int number_of_indices = max_index + 1; - OP_REQUIRES(ctx, number_of_indices > 0, - errors::InvalidArgument("no indices supplied")); + int64 result_rank = 1 + data0_shape.dims() - indices0_shape.dims(); + if (number_of_indices == 0) { + std::vector result_shape(result_rank); + for (int d = indices0_shape.dims(); d < data0_shape.dims(); d++) { + result_shape[d - indices0_shape.dims() + 1] = data0_shape.dim_size(d); + } + xla::PrimitiveType element_type = + ctx->input_xla_type(ctx->num_inputs() - 1); + xla::Literal empty_literal = xla::Literal::CreateFromShape( + xla::ShapeUtil::MakeShape(element_type, result_shape)); + ctx->SetOutput(0, xla::ConstantLiteral(ctx->builder(), empty_literal)); + return; + } + // Construct the reverse mapping, for each index, of which slice of which // input it comes from. std::vector src_input_vector(number_of_indices); @@ -157,12 +169,9 @@ class DynamicStitchOp : public XlaOpKernel { // Set up the vectors for slicing: the first dimension will vary // slice by slice, and the rest take the full common extra shape. - std::vector slice_start(1 + data0_shape.dims() - - indices0_shape.dims()); - std::vector slice_limit(1 + data0_shape.dims() - - indices0_shape.dims()); - std::vector stride(1 + data0_shape.dims() - indices0_shape.dims(), - 1); + std::vector slice_start(result_rank); + std::vector slice_limit(result_rank); + std::vector stride(result_rank, 1); for (int d = indices0_shape.dims(); d < data0_shape.dims(); d++) { slice_limit[1 + d - indices0_shape.dims()] = data0_shape.dim_size(d); } diff --git a/tensorflow/compiler/tf2xla/kernels/extract_image_patches_op.cc b/tensorflow/compiler/tf2xla/kernels/extract_image_patches_op.cc index c68b0bfd79..29687c7b82 100644 --- a/tensorflow/compiler/tf2xla/kernels/extract_image_patches_op.cc +++ b/tensorflow/compiler/tf2xla/kernels/extract_image_patches_op.cc @@ -17,7 +17,6 @@ limitations under the License. #include "tensorflow/compiler/tf2xla/xla_helpers.h" #include "tensorflow/compiler/tf2xla/xla_op_kernel.h" #include "tensorflow/compiler/tf2xla/xla_op_registry.h" -#include "tensorflow/compiler/xla/client/lib/numeric.h" #include "tensorflow/compiler/xla/client/xla_builder.h" #include "tensorflow/core/util/tensor_format.h" diff --git a/tensorflow/compiler/tf2xla/kernels/fake_quantize_ops.cc b/tensorflow/compiler/tf2xla/kernels/fake_quantize_ops.cc index cdba6680de..142be030f7 100644 --- a/tensorflow/compiler/tf2xla/kernels/fake_quantize_ops.cc +++ b/tensorflow/compiler/tf2xla/kernels/fake_quantize_ops.cc @@ -260,19 +260,19 @@ class FakeQuantWithMinMaxVarsGradOp : public XlaOpKernel { xla::XlaOp below_min = xla::Lt(input, nudged_input_min); xla::XlaOp select1 = xla::Select(below_min, gradient, zeroes); xla::XlaOp reduce1 = xla::ReduceAll( - XlaHelpers::ConvertElementType(b, select1, accumulation_type), + XlaHelpers::ConvertElementType(select1, accumulation_type), XlaHelpers::Zero(b, accumulation_type), *ctx->GetOrCreateAdd(accumulation_type)); - xla::XlaOp output1 = XlaHelpers::ConvertElementType(b, reduce1, data_type); + xla::XlaOp output1 = XlaHelpers::ConvertElementType(reduce1, data_type); ctx->SetOutput(1, output1); xla::XlaOp above_max = xla::Gt(input, nudged_input_max); xla::XlaOp select2 = xla::Select(above_max, gradient, zeroes); xla::XlaOp reduce2 = xla::ReduceAll( - XlaHelpers::ConvertElementType(b, select2, accumulation_type), + XlaHelpers::ConvertElementType(select2, accumulation_type), XlaHelpers::Zero(b, accumulation_type), *ctx->GetOrCreateAdd(accumulation_type)); - xla::XlaOp output2 = XlaHelpers::ConvertElementType(b, reduce2, data_type); + xla::XlaOp output2 = XlaHelpers::ConvertElementType(reduce2, data_type); ctx->SetOutput(2, output2); } diff --git a/tensorflow/compiler/tf2xla/kernels/fft_ops.cc b/tensorflow/compiler/tf2xla/kernels/fft_ops.cc index 9b06357d9b..6df8b5367d 100644 --- a/tensorflow/compiler/tf2xla/kernels/fft_ops.cc +++ b/tensorflow/compiler/tf2xla/kernels/fft_ops.cc @@ -20,6 +20,7 @@ limitations under the License. #include "tensorflow/compiler/tf2xla/xla_op_registry.h" #include "tensorflow/compiler/xla/client/xla_builder.h" #include "tensorflow/compiler/xla/literal_util.h" +#include "tensorflow/compiler/xla/util.h" #include "tensorflow/core/framework/numeric_op.h" #include "tensorflow/core/framework/op_kernel.h" #include "tensorflow/core/framework/tensor.h" @@ -50,11 +51,36 @@ class GenericFftOp : public XlaOpKernel { errors::InvalidArgument("input must be at least 1 dimensional")); std::vector fft_length; + xla::XlaOp input = ctx->Input(0); if (fft_type_ == FftType::RFFT || fft_type_ == FftType::IRFFT) { OP_REQUIRES_OK(ctx, ctx->ConstantInputAsIntVector(1, &fft_length)); OP_REQUIRES(ctx, fft_length.size() == fft_rank_, errors::InvalidArgument("fft_length must be length ", fft_rank_, " vector")); + + // Zero pad or truncate the axes we're doing FFT on. + absl::InlinedVector slice_sizes = input_shape.dim_sizes(); + std::vector> padding_sizes(slice_sizes.size()); + std::vector expected_sizes = fft_length; + // IRFFT wants the innermost axis to be n / 2 + 1. + if (fft_type_ == FftType::IRFFT) { + expected_sizes[fft_rank_ - 1] = fft_length[fft_rank_ - 1] / 2 + 1; + } + for (int i = 0; i < fft_rank_; i++) { + int index = input_shape.dims() - fft_rank_ + i; + if (input_shape.dim_size(index) > expected_sizes[i]) { + slice_sizes[index] = expected_sizes[i]; + } else { + padding_sizes[index].second = + expected_sizes[i] - input_shape.dim_size(index); + } + } + + std::vector start_indices(input_shape.dims(), 0); + std::vector strides(input_shape.dims(), 1); + input = xla::Pad(xla::Slice(input, start_indices, slice_sizes, strides), + XlaHelpers::Zero(ctx->builder(), ctx->input_type(0)), + xla::MakeEdgePaddingConfig(padding_sizes)); } else { // Innermost axis provides the FFT length. for (int i = 0; i < fft_rank_; i++) { @@ -63,7 +89,7 @@ class GenericFftOp : public XlaOpKernel { } } - xla::XlaOp fft = xla::Fft(ctx->Input(0), fft_type_, fft_length); + xla::XlaOp fft = xla::Fft(input, fft_type_, fft_length); ctx->SetOutput(0, fft); } diff --git a/tensorflow/compiler/tf2xla/kernels/if_op.cc b/tensorflow/compiler/tf2xla/kernels/if_op.cc index 56da50f140..b5e0839125 100644 --- a/tensorflow/compiler/tf2xla/kernels/if_op.cc +++ b/tensorflow/compiler/tf2xla/kernels/if_op.cc @@ -72,7 +72,7 @@ void XlaIfOp::Compile(XlaOpKernelContext* ctx) { arg.shape = resource->shape(); OP_REQUIRES(ctx, arg.initialized, errors::Unimplemented("Uninitialized arguments: ", arg.name)); - arg.tensor_array_size = resource->tensor_array_size(); + arg.max_array_size = resource->max_array_size(); for (const auto& gradient : resource->tensor_array_gradients()) { arg.tensor_array_gradients.insert(gradient.first); } diff --git a/tensorflow/compiler/tf2xla/kernels/image_ops.cc b/tensorflow/compiler/tf2xla/kernels/image_ops.cc index b49b2516d8..e9bb0a77e9 100644 --- a/tensorflow/compiler/tf2xla/kernels/image_ops.cc +++ b/tensorflow/compiler/tf2xla/kernels/image_ops.cc @@ -191,12 +191,11 @@ class AdjustContrastOpV2 : public XlaOpKernel { DataType type = context->input_type(0); const DataType accumulation_type = XlaHelpers::SumAccumulationType(type); - auto converted = - XlaHelpers::ConvertElementType(b, input, accumulation_type); + auto converted = XlaHelpers::ConvertElementType(input, accumulation_type); auto reduce = xla::Reduce(converted, XlaHelpers::Zero(b, accumulation_type), *context->GetOrCreateAdd(accumulation_type), {height_dim, width_dim}); - auto output = XlaHelpers::ConvertElementType(b, reduce, type); + auto output = XlaHelpers::ConvertElementType(reduce, type); output = xla::Div(output, XlaHelpers::FloatLiteral(b, type, height * width)); diff --git a/tensorflow/compiler/tf2xla/kernels/image_resize_ops.cc b/tensorflow/compiler/tf2xla/kernels/image_resize_ops.cc index 0c7ca602bf..5a10c52ba8 100644 --- a/tensorflow/compiler/tf2xla/kernels/image_resize_ops.cc +++ b/tensorflow/compiler/tf2xla/kernels/image_resize_ops.cc @@ -19,7 +19,6 @@ limitations under the License. #include "tensorflow/compiler/tf2xla/xla_op_registry.h" #include "tensorflow/compiler/xla/array4d.h" #include "tensorflow/compiler/xla/client/lib/constants.h" -#include "tensorflow/compiler/xla/client/lib/numeric.h" #include "tensorflow/compiler/xla/client/xla_builder.h" #include "tensorflow/core/framework/kernel_def_builder.h" #include "tensorflow/core/framework/register_types.h" diff --git a/tensorflow/compiler/tf2xla/kernels/index_ops_cpu.cc b/tensorflow/compiler/tf2xla/kernels/index_ops_cpu.cc index e310db2162..e2c05b648b 100644 --- a/tensorflow/compiler/tf2xla/kernels/index_ops_cpu.cc +++ b/tensorflow/compiler/tf2xla/kernels/index_ops_cpu.cc @@ -30,7 +30,9 @@ limitations under the License. namespace tensorflow { namespace { -// The logic below uses a custom-call to implement argmax. +// The logic below uses a custom-call to implement argmax when possible. When +// custom-call is not allowed or input shapes are not supported, this kernel +// falls back to using XLA HLO native ArgMax. // // Also see b/29507024 for first-class XLA support for indexing ops. class ArgMaxCustomCallOp : public XlaOpKernel { @@ -50,27 +52,40 @@ class ArgMaxCustomCallOp : public XlaOpKernel { // overhead, when compiling ahead-of-time. int64 dim; OP_REQUIRES_OK(ctx, ctx->ConstantInputAsIntScalar(1, &dim)); - OP_REQUIRES(ctx, dim >= 0, errors::InvalidArgument("dim must be >= 0")); - OP_REQUIRES( - ctx, dim < input_shape.dims(), - errors::InvalidArgument("dim must be < input rank (", - input_shape.dims(), "), but got: ", dim)); - const int64 dim_size = input_shape.dim_size(dim); - OP_REQUIRES(ctx, dim_size > 0, + + const int input_dims = input_shape.dims(); + const int axis = dim < 0 ? dim + input_dims : dim; + OP_REQUIRES(ctx, axis >= 0 && axis < input_dims, + errors::InvalidArgument("Expected dimension in the range [", + -input_dims, ", ", input_dims, + "), but got ", dim)); + + const int64 axis_size = input_shape.dim_size(axis); + OP_REQUIRES(ctx, axis_size > 0, errors::InvalidArgument( "Reduction axis ", dim, " is empty in shape: ", input_shape.DebugString())); - // The output shape is the input shape contracted along dim. + const DataType dtype = output_type(0); + xla::PrimitiveType output_type; + OP_REQUIRES_OK(ctx, DataTypeToPrimitiveType(dtype, &output_type)); + + // Fall back to XLA ArgMax HLO when CustomCall is not allowed or when input + // shape isn't supported. + if (!ctx->compiler()->options().allow_cpu_custom_calls || + (input_dims != 1 && input_dims != 2)) { + xla::XlaOp output = XlaHelpers::ArgMax(ctx->Input(0), output_type, axis); + ctx->SetOutput(0, output); + return; + } + + xla::XlaOp output; + // The output shape is the input shape contracted along axis. TensorShape output_shape; for (int d = 0; d < input_shape.dims() - 1; ++d) { - output_shape.AddDim(input_shape.dim_size((d < dim) ? d : d + 1)); + output_shape.AddDim(input_shape.dim_size((d < axis) ? d : d + 1)); } - // For now we use a custom-call, only for the 1d and 2d cases. - OP_REQUIRES(ctx, XlaContext::Get(ctx).allow_cpu_custom_calls(), - errors::InvalidArgument( - "ArgMax implementation requires a CustomCall on CPU")); xla::XlaBuilder& b = *ctx->builder(); // XLA passes to the function, so it is not included here. @@ -84,7 +99,7 @@ class ArgMaxCustomCallOp : public XlaOpKernel { args.push_back(xla::ConstantLiteral( &b, xla::LiteralUtil::CreateR1(output_shape.dim_sizes()))); args.push_back( - xla::ConstantLiteral(&b, xla::LiteralUtil::CreateR0(dim))); + xla::ConstantLiteral(&b, xla::LiteralUtil::CreateR0(axis))); } // The argmax function expects row-major layout. @@ -101,24 +116,15 @@ class ArgMaxCustomCallOp : public XlaOpKernel { } // Tell XLA to call the custom code, defined in - // index_ops_kernel_argmax_float_1d.cc. - xla::XlaOp output; - switch (input_shape.dims()) { - case 1: - output = xla::CustomCallWithLayout(&b, "argmax_float_1d_xla_impl", args, - xla_shape, arg_shapes); - break; - case 2: - output = xla::CustomCallWithLayout(&b, "argmax_float_2d_xla_impl", args, - xla_shape, arg_shapes); - break; - default: - OP_REQUIRES(ctx, false, - errors::Unimplemented( - "Argmax is only implemented for 1d and 2d tensors" - ", but got shape: ", - input_shape.DebugString())); + // index_ops_kernel_argmax_float_{1, 2}d.cc. + if (input_dims == 1) { + output = xla::CustomCallWithLayout(&b, "argmax_float_1d_xla_impl", args, + xla_shape, arg_shapes); + } else { + output = xla::CustomCallWithLayout(&b, "argmax_float_2d_xla_impl", args, + xla_shape, arg_shapes); } + output = xla::ConvertElementType(output, output_type); ctx->SetOutput(0, output); } diff --git a/tensorflow/compiler/tf2xla/kernels/l2loss_op.cc b/tensorflow/compiler/tf2xla/kernels/l2loss_op.cc index f028e361bc..93f029731c 100644 --- a/tensorflow/compiler/tf2xla/kernels/l2loss_op.cc +++ b/tensorflow/compiler/tf2xla/kernels/l2loss_op.cc @@ -37,12 +37,11 @@ class L2LossOp : public XlaOpKernel { // output = sum(t ** 2) / 2 const DataType accumulation_type = XlaHelpers::SumAccumulationType(dtype); - auto t = - XlaHelpers::ConvertElementType(b, ctx->Input(0), accumulation_type); + auto t = XlaHelpers::ConvertElementType(ctx->Input(0), accumulation_type); auto square = xla::Mul(t, t); auto reduce = xla::Reduce(square, XlaHelpers::Zero(b, accumulation_type), *ctx->GetOrCreateAdd(accumulation_type), dims); - auto deconverted = XlaHelpers::ConvertElementType(b, reduce, dtype); + auto deconverted = XlaHelpers::ConvertElementType(reduce, dtype); auto two = XlaHelpers::IntegerLiteral(b, dtype, 2); ctx->SetOutput(0, xla::Div(deconverted, two)); } diff --git a/tensorflow/compiler/tf2xla/kernels/lrn_ops.cc b/tensorflow/compiler/tf2xla/kernels/lrn_ops.cc index 87ee2d3aed..987901d82b 100644 --- a/tensorflow/compiler/tf2xla/kernels/lrn_ops.cc +++ b/tensorflow/compiler/tf2xla/kernels/lrn_ops.cc @@ -49,16 +49,14 @@ class LRNOp : public XlaOpKernel { // We use a window of depth_radius_ * 2 + 1, to account for the current // element and a depth_radius_ on either side. auto accumulation_type = XlaHelpers::SumAccumulationType(input_type(0)); - auto converted = - XlaHelpers::ConvertElementType(builder, input, accumulation_type); + auto converted = XlaHelpers::ConvertElementType(input, accumulation_type); auto squared = xla::Mul(converted, converted); auto reduce = xla::ReduceWindow( squared, XlaHelpers::Zero(builder, accumulation_type), *ctx->GetOrCreateAdd(accumulation_type), /* window_dimensions = */ {1, 1, 1, depth_radius_ * 2 + 1}, /* window_strides = */ {1, 1, 1, 1}, xla::Padding::kSame); - auto sqr_sum = - XlaHelpers::ConvertElementType(builder, reduce, input_type(0)); + auto sqr_sum = XlaHelpers::ConvertElementType(reduce, input_type(0)); auto scale = xla::Pow( xla::Add(xla::ConstantR0(builder, bias_), @@ -138,15 +136,14 @@ class LRNGradOp : public XlaOpKernel { auto accumulation_type = XlaHelpers::SumAccumulationType(input_type(0)); auto converted = - XlaHelpers::ConvertElementType(builder, in_image, accumulation_type); + XlaHelpers::ConvertElementType(in_image, accumulation_type); auto squared = xla::Mul(converted, converted); auto reduce = xla::ReduceWindow( squared, XlaHelpers::Zero(builder, accumulation_type), *ctx->GetOrCreateAdd(accumulation_type), /* window_dimensions = */ {1, 1, 1, depth_radius_ * 2 + 1}, /* window_strides = */ {1, 1, 1, 1}, xla::Padding::kSame); - auto sqr_sum = - XlaHelpers::ConvertElementType(builder, reduce, input_type(0)); + auto sqr_sum = XlaHelpers::ConvertElementType(reduce, input_type(0)); auto norm = xla::Add(xla::ConstantR0(builder, bias_), @@ -157,15 +154,13 @@ class LRNGradOp : public XlaOpKernel { xla::Div(out_image, norm)), in_grads); - auto converted_dy = - XlaHelpers::ConvertElementType(builder, dy, accumulation_type); + auto converted_dy = XlaHelpers::ConvertElementType(dy, accumulation_type); auto dy_reduce = xla::ReduceWindow( converted_dy, XlaHelpers::Zero(builder, accumulation_type), *ctx->GetOrCreateAdd(accumulation_type), /* window_dimensions = */ {1, 1, 1, depth_radius_ * 2 + 1}, /* window_strides = */ {1, 1, 1, 1}, xla::Padding::kSame); - auto dy_reduced = - XlaHelpers::ConvertElementType(builder, dy_reduce, input_type(0)); + auto dy_reduced = XlaHelpers::ConvertElementType(dy_reduce, input_type(0)); xla::XlaOp gradients = xla::Add( xla::Mul(in_image, dy_reduced), diff --git a/tensorflow/compiler/tf2xla/kernels/matrix_band_part_op.cc b/tensorflow/compiler/tf2xla/kernels/matrix_band_part_op.cc index 8dfd7de591..2dd0a710e4 100644 --- a/tensorflow/compiler/tf2xla/kernels/matrix_band_part_op.cc +++ b/tensorflow/compiler/tf2xla/kernels/matrix_band_part_op.cc @@ -16,8 +16,8 @@ limitations under the License. #include "tensorflow/compiler/tf2xla/xla_helpers.h" #include "tensorflow/compiler/tf2xla/xla_op_kernel.h" #include "tensorflow/compiler/tf2xla/xla_op_registry.h" -#include "tensorflow/compiler/xla/client/lib/numeric.h" #include "tensorflow/compiler/xla/client/xla_builder.h" +#include "tensorflow/compiler/xla/shape_util.h" #include "tensorflow/core/framework/tensor_shape.h" namespace tensorflow { @@ -61,11 +61,11 @@ class MatrixBandPartOp : public XlaOpKernel { // Compute 'offset', which is how many diagonals we are above/below the // diagonal. - xla::XlaOp iota_m = xla::Iota(builder, index_xla_type, m); - xla::XlaOp iota_n = xla::Iota(builder, index_xla_type, n); + xla::Shape iota_shape = xla::ShapeUtil::MakeShape(index_xla_type, {m, n}); + xla::XlaOp iota_m = xla::Iota(builder, iota_shape, /*iota_dimension=*/0); + xla::XlaOp iota_n = xla::Iota(builder, iota_shape, /*iota_dimension=*/1); - auto offset = xla::Sub(xla::Broadcast(iota_n, {m}), iota_m, - /*broadcast_dimensions=*/{0}); + auto offset = xla::Sub(iota_n, iota_m); // If num_lower or num_upper are negative, include all lower/upper // diagonals. diff --git a/tensorflow/compiler/tf2xla/kernels/matrix_set_diag_op.cc b/tensorflow/compiler/tf2xla/kernels/matrix_set_diag_op.cc index c0ca881ff8..4f980b6d14 100644 --- a/tensorflow/compiler/tf2xla/kernels/matrix_set_diag_op.cc +++ b/tensorflow/compiler/tf2xla/kernels/matrix_set_diag_op.cc @@ -16,7 +16,6 @@ limitations under the License. #include "tensorflow/compiler/tf2xla/xla_helpers.h" #include "tensorflow/compiler/tf2xla/xla_op_kernel.h" #include "tensorflow/compiler/tf2xla/xla_op_registry.h" -#include "tensorflow/compiler/xla/client/lib/numeric.h" #include "tensorflow/compiler/xla/client/xla_builder.h" namespace tensorflow { diff --git a/tensorflow/compiler/tf2xla/kernels/permute_op.cc b/tensorflow/compiler/tf2xla/kernels/permute_op.cc index 94b51e1a58..71920bf5c1 100644 --- a/tensorflow/compiler/tf2xla/kernels/permute_op.cc +++ b/tensorflow/compiler/tf2xla/kernels/permute_op.cc @@ -75,8 +75,7 @@ class DataFormatVecPermuteOp : public XlaOpKernel { } auto keys = xla::ConstantR1(builder, absl::Span(dst_indices)); if (input_rank == 2) { - keys = xla::BroadcastInDim( - keys, xla::ShapeUtil::MakeShape(xla::S32, {4, 2}), {0}); + keys = xla::BroadcastInDim(keys, {4, 2}, {0}); } auto sorted = xla::Sort(keys, {ctx->Input(0)}, 0); auto output = xla::GetTupleElement(sorted, 1); diff --git a/tensorflow/compiler/tf2xla/kernels/pooling_ops.cc b/tensorflow/compiler/tf2xla/kernels/pooling_ops.cc index a259da6383..06c6cc37ec 100644 --- a/tensorflow/compiler/tf2xla/kernels/pooling_ops.cc +++ b/tensorflow/compiler/tf2xla/kernels/pooling_ops.cc @@ -152,7 +152,12 @@ class MaxPoolOp : public PoolingOp { public: MaxPoolOp(OpKernelConstruction* ctx, int num_spatial_dims) : PoolingOp(ctx, /*num_spatial_dims=*/num_spatial_dims, - /*reduction_type=*/ctx->input_type(0)) {} + /*reduction_type=*/ctx->input_type(0)) { + string data_format_str; + OP_REQUIRES_OK(ctx, ctx->GetAttr("data_format", &data_format_str)); + OP_REQUIRES(ctx, FormatFromString(data_format_str, &data_format_), + errors::InvalidArgument("Invalid data format")); + } void Compile(XlaOpKernelContext* ctx) override { auto ksize_or_error = GetKernelSize(ctx); @@ -180,10 +185,6 @@ class MaxPool2DOp : public MaxPoolOp { public: explicit MaxPool2DOp(OpKernelConstruction* ctx) : MaxPoolOp(ctx, /*num_spatial_dims=*/2) { - string data_format_str; - OP_REQUIRES_OK(ctx, ctx->GetAttr("data_format", &data_format_str)); - OP_REQUIRES(ctx, FormatFromString(data_format_str, &data_format_), - errors::InvalidArgument("Invalid data format")); } }; REGISTER_XLA_OP(Name("MaxPool"), MaxPool2DOp); @@ -204,7 +205,12 @@ class AvgPoolOp : public PoolingOp { AvgPoolOp(OpKernelConstruction* ctx, int num_spatial_dims) : PoolingOp(ctx, /*num_spatial_dims=*/num_spatial_dims, /*reduction_type=*/ - XlaHelpers::SumAccumulationType(ctx->input_type(0))) {} + XlaHelpers::SumAccumulationType(ctx->input_type(0))) { + string data_format_str; + OP_REQUIRES_OK(ctx, ctx->GetAttr("data_format", &data_format_str)); + OP_REQUIRES(ctx, FormatFromString(data_format_str, &data_format_), + errors::InvalidArgument("Invalid data format")); + } void Compile(XlaOpKernelContext* ctx) override { auto ksize_or_error = GetKernelSize(ctx); @@ -241,10 +247,6 @@ class AvgPool2DOp : public AvgPoolOp { public: explicit AvgPool2DOp(OpKernelConstruction* ctx) : AvgPoolOp(ctx, /*num_spatial_dims=*/2) { - string data_format_str; - OP_REQUIRES_OK(ctx, ctx->GetAttr("data_format", &data_format_str)); - OP_REQUIRES(ctx, FormatFromString(data_format_str, &data_format_), - errors::InvalidArgument("Invalid data format")); } }; REGISTER_XLA_OP(Name("AvgPool"), AvgPool2DOp); @@ -390,6 +392,11 @@ class AvgPoolGradOp : public XlaOpKernel { OP_REQUIRES(ctx, ksize_[0] == 1 && stride_[0] == 1, errors::Unimplemented( "Pooling is not yet supported on the batch dimension.")); + + string data_format; + OP_REQUIRES_OK(ctx, ctx->GetAttr("data_format", &data_format)); + OP_REQUIRES(ctx, FormatFromString(data_format, &data_format_), + errors::InvalidArgument("Invalid data format")); } int num_dims() const { return num_spatial_dims_ + 2; } @@ -449,10 +456,6 @@ class AvgPool2DGradOp : public AvgPoolGradOp { public: explicit AvgPool2DGradOp(OpKernelConstruction* ctx) : AvgPoolGradOp(ctx, /*num_spatial_dims=*/2) { - string data_format; - OP_REQUIRES_OK(ctx, ctx->GetAttr("data_format", &data_format)); - OP_REQUIRES(ctx, FormatFromString(data_format, &data_format_), - errors::InvalidArgument("Invalid data format")); } }; REGISTER_XLA_OP( diff --git a/tensorflow/compiler/tf2xla/kernels/quantize_and_dequantize_op.cc b/tensorflow/compiler/tf2xla/kernels/quantize_and_dequantize_op.cc index 6f4ed496a1..7fe102428d 100644 --- a/tensorflow/compiler/tf2xla/kernels/quantize_and_dequantize_op.cc +++ b/tensorflow/compiler/tf2xla/kernels/quantize_and_dequantize_op.cc @@ -19,6 +19,7 @@ limitations under the License. #include "tensorflow/compiler/tf2xla/xla_op_registry.h" #include "tensorflow/compiler/xla/client/lib/arithmetic.h" #include "tensorflow/compiler/xla/client/lib/constants.h" +#include "tensorflow/compiler/xla/client/lib/math.h" #include "tensorflow/compiler/xla/client/xla_builder.h" #include "tensorflow/compiler/xla/client/xla_computation.h" #include "tensorflow/core/platform/macros.h" @@ -26,12 +27,26 @@ limitations under the License. namespace tensorflow { namespace { +enum QuantizerRoundMode { + // Round half up: if the fraction of y is exactly 0.5, then + // round(y) = y + 0.5 + // E.g., -5.5 gets rounded to -5, -5.4 goes to -5, + // 5.4 goes to 5, and 5.5 goes to 6. + ROUND_HALF_UP, + // Round half to even: if the fraction of y is exactly 0.5, then round(y) is + // the nearest even integer to y. + // E.g., 23.5 gets rounded to 24, 24.5 gets rounded to 24, while -23.5 becomes + // -24, and -24.5 gets rounded to 24. + ROUND_HALF_TO_EVEN, +}; + class QuantizeAndDequantizeOp : public XlaOpKernel { public: explicit QuantizeAndDequantizeOp(OpKernelConstruction* ctx) : XlaOpKernel(ctx) { OP_REQUIRES_OK(ctx, ctx->GetAttr("signed_input", &signed_input_)); OP_REQUIRES_OK(ctx, ctx->GetAttr("range_given", &range_given_)); + round_mode_ = ROUND_HALF_TO_EVEN; } void Compile(XlaOpKernelContext* ctx) override { @@ -117,8 +132,17 @@ class QuantizeAndDequantizeOp : public XlaOpKernel { // in that case they were measured from the tensor. input = Clamp(min_range, input, max_range); } - xla::XlaOp result = - Floor((input - min_range) * scale + half) * inverse_scale + min_range; + xla::XlaOp result; + switch (round_mode_) { + case ROUND_HALF_TO_EVEN: { + result = xla::RoundToEven(input * scale) * inverse_scale; + break; + } + case ROUND_HALF_UP: { + result = Floor(input * scale + half) * inverse_scale; + break; + } + } ctx->SetOutput(0, result); } @@ -126,6 +150,7 @@ class QuantizeAndDequantizeOp : public XlaOpKernel { int64 num_bits_ = -1; bool signed_input_; bool range_given_; + QuantizerRoundMode round_mode_; }; class QuantizeAndDequantizeV2Op : public QuantizeAndDequantizeOp { @@ -136,6 +161,20 @@ class QuantizeAndDequantizeV2Op : public QuantizeAndDequantizeOp { OP_REQUIRES(ctx, num_bits_ > 0 && num_bits_ < (signed_input_ ? 62 : 63), errors::InvalidArgument("num_bits is out of range: ", num_bits_, " with signed_input_ ", signed_input_)); + string round_mode_string; + OP_REQUIRES_OK(ctx, ctx->GetAttr("round_mode", &round_mode_string)); + OP_REQUIRES( + ctx, + (round_mode_string == "HALF_UP" || round_mode_string == "HALF_TO_EVEN"), + errors::InvalidArgument("Round mode string must be " + "'HALF_UP' or " + "'HALF_TO_EVEN', is '" + + round_mode_string + "'")); + if (round_mode_string == "HALF_UP") { + round_mode_ = ROUND_HALF_UP; + } else if (round_mode_string == "HALF_TO_EVEN") { + round_mode_ = ROUND_HALF_TO_EVEN; + } } }; diff --git a/tensorflow/compiler/tf2xla/kernels/random_ops.cc b/tensorflow/compiler/tf2xla/kernels/random_ops.cc index 415ce9b77f..8822e29f7e 100644 --- a/tensorflow/compiler/tf2xla/kernels/random_ops.cc +++ b/tensorflow/compiler/tf2xla/kernels/random_ops.cc @@ -26,7 +26,6 @@ limitations under the License. #include "tensorflow/compiler/tf2xla/xla_op_kernel.h" #include "tensorflow/compiler/tf2xla/xla_op_registry.h" #include "tensorflow/compiler/xla/client/lib/arithmetic.h" -#include "tensorflow/compiler/xla/client/lib/numeric.h" #include "tensorflow/compiler/xla/client/xla_builder.h" #include "tensorflow/core/framework/op_kernel.h" #include "tensorflow/core/framework/tensor.h" diff --git a/tensorflow/compiler/tf2xla/kernels/reduction_ops.cc b/tensorflow/compiler/tf2xla/kernels/reduction_ops.cc index 107fa62967..65e158d64f 100644 --- a/tensorflow/compiler/tf2xla/kernels/reduction_ops.cc +++ b/tensorflow/compiler/tf2xla/kernels/reduction_ops.cc @@ -113,12 +113,21 @@ class MeanOp : public XlaReductionOp { xla::Add(scalar_lhs, scalar_rhs); } - xla::XlaOp BuildFinalizer(xla::XlaBuilder* builder, - const xla::XlaOp& reduce_output, - int64 num_elements_reduced) override { - auto divisor = XlaHelpers::IntegerLiteral(builder, input_type(0), - num_elements_reduced); - return reduce_output / divisor; + xla::XlaOp BuildFinalizer( + xla::XlaBuilder* /*builder*/, const xla::XlaOp& input, + const xla::XlaOp& reduce_output, + const std::vector& dimensions_to_reduce) override { + if (dimensions_to_reduce.empty()) { + return reduce_output; + } + auto divisor = xla::GetDimensionSize(input, dimensions_to_reduce[0]); + for (int i = 1; i < dimensions_to_reduce.size(); i++) { + auto size = xla::GetDimensionSize(input, dimensions_to_reduce[i]); + divisor = xla::Mul(divisor, size); + } + divisor = xla::ConvertElementType(divisor, xla_reduction_type_); + return XlaHelpers::ConvertElementType(reduce_output / divisor, + input_type(0)); } }; diff --git a/tensorflow/compiler/tf2xla/kernels/reduction_ops.h b/tensorflow/compiler/tf2xla/kernels/reduction_ops.h index 466e79828d..af716eab79 100644 --- a/tensorflow/compiler/tf2xla/kernels/reduction_ops.h +++ b/tensorflow/compiler/tf2xla/kernels/reduction_ops.h @@ -48,13 +48,14 @@ class XlaReductionOp : public XlaOpKernel { const xla::XlaOp& scalar_rhs) = 0; // Applies a transformation to the output of the reduction. The desired - // computation should be added to 'builder'. Argument 'reduce_output' is the - // output of the reduction. 'num_elements_reduced' is the number of elements - // that contributed to the reduction. Returns the transformed reduction - // output, Defaults to returning 'reduce_output' unchanged. - virtual xla::XlaOp BuildFinalizer(xla::XlaBuilder* builder, - const xla::XlaOp& reduce_output, - int64 num_elements_reduced); + // computation should be added to 'builder'. Argument 'input' is the original + // input of the reduction; 'reduce_output' is the output of the reduction. + // Returns the transformed reduction output. Defaults to returning + // 'reduce_output' converted to the input type. + virtual xla::XlaOp BuildFinalizer( + xla::XlaBuilder* builder, const xla::XlaOp& input, + const xla::XlaOp& reduce_output, + const std::vector& dimensions_to_reduce); void Compile(XlaOpKernelContext* ctx) override; diff --git a/tensorflow/compiler/tf2xla/kernels/reduction_ops_common.cc b/tensorflow/compiler/tf2xla/kernels/reduction_ops_common.cc index 118f2798d5..2ca2a85244 100644 --- a/tensorflow/compiler/tf2xla/kernels/reduction_ops_common.cc +++ b/tensorflow/compiler/tf2xla/kernels/reduction_ops_common.cc @@ -35,12 +35,13 @@ XlaReductionOp::XlaReductionOp(OpKernelConstruction* ctx, ctx, DataTypeToPrimitiveType(reduction_type_, &xla_reduction_type_)); } -// Unless BuildFinalizer is overridden the reduction has no -// finalizer. -xla::XlaOp XlaReductionOp::BuildFinalizer(xla::XlaBuilder* builder, - const xla::XlaOp& reduce_output, - int64 num_elements_reduced) { - return reduce_output; +// The default finalizer converts the results back into the input type. This can +// be overridden. +xla::XlaOp XlaReductionOp::BuildFinalizer( + xla::XlaBuilder* /*builder*/, const xla::XlaOp& /*input*/, + const xla::XlaOp& reduce_output, + const std::vector& /*dimensions_to_reduce*/) { + return XlaHelpers::ConvertElementType(reduce_output, input_type(0)); } void XlaReductionOp::Compile(XlaOpKernelContext* ctx) { @@ -71,7 +72,6 @@ void XlaReductionOp::Compile(XlaOpKernelContext* ctx) { absl::InlinedVector bitmap(data_shape.dims(), false); std::vector xla_axes; - int64 num_elements_reduced = 1LL; for (int64 i = 0; i < axes_tensor_shape.num_elements(); ++i) { int64 index = axes[i]; OP_REQUIRES(ctx, @@ -82,7 +82,6 @@ void XlaReductionOp::Compile(XlaOpKernelContext* ctx) { index = (index + data_shape.dims()) % data_shape.dims(); bitmap[index] = true; xla_axes.push_back(index); - num_elements_reduced *= data_shape.dim_size(index); } std::vector final_shape; @@ -118,8 +117,7 @@ void XlaReductionOp::Compile(XlaOpKernelContext* ctx) { xla::XlaComputation reduction_computation = r.Build().ConsumeValueOrDie(); auto reduce = xla::Reduce(data, initial, reduction_computation, xla_axes); - auto deconverted = XlaHelpers::ConvertElementType(b, reduce, input_type(0)); - auto finalized = BuildFinalizer(b, deconverted, num_elements_reduced); + auto finalized = BuildFinalizer(b, data, reduce, xla_axes); auto result = keep_dims_ ? xla::Reshape(finalized, final_shape) : finalized; ctx->SetOutput(0, result); } diff --git a/tensorflow/compiler/tf2xla/kernels/resampler_ops.cc b/tensorflow/compiler/tf2xla/kernels/resampler_ops.cc index 847704608f..54d34a38ab 100644 --- a/tensorflow/compiler/tf2xla/kernels/resampler_ops.cc +++ b/tensorflow/compiler/tf2xla/kernels/resampler_ops.cc @@ -24,7 +24,6 @@ limitations under the License. #include "tensorflow/compiler/xla/array4d.h" #include "tensorflow/compiler/xla/client/lib/arithmetic.h" #include "tensorflow/compiler/xla/client/lib/constants.h" -#include "tensorflow/compiler/xla/client/lib/numeric.h" #include "tensorflow/compiler/xla/client/xla_builder.h" #include "tensorflow/compiler/xla/literal.h" #include "tensorflow/compiler/xla/shape_util.h" @@ -44,9 +43,6 @@ namespace { using xla::XlaOp; -// TODO(b/112295522): note that sampling from image boundary is not currently -// being handled properly. - // Calculates the bilinear weight tensor, given basis ratio (px, py) of the // sampling position: // W = [(1-px)*(1-py), px*(1-py), (1-px)*py, px*py] @@ -70,11 +66,8 @@ XlaOp BilinearWeights(XlaOpKernelContext* ctx, XlaOp ratio, std::vector last_two_dims_indices = {(broadcast_dims_size - 2), (broadcast_dims_size - 1)}; - xla::Shape broadcast_shape = - xla::ShapeUtil::MakeShape(xla_type, broadcast_dims); - auto broadcast_first_term = - xla::BroadcastInDim(first_term, broadcast_shape, last_two_dims_indices); + xla::BroadcastInDim(first_term, broadcast_dims, last_two_dims_indices); // Ratio is of the same dimension as warp, which is [batch, dim_0,... dim_n, // 2], we broadcast ratio tensor to 'broadcast_dim' by keeping the @@ -85,7 +78,7 @@ XlaOp BilinearWeights(XlaOpKernelContext* ctx, XlaOp ratio, ratio_broadcast_indices.erase(ratio_broadcast_indices.end() - 2); auto broadcast_ratio = - xla::BroadcastInDim(ratio, broadcast_shape, ratio_broadcast_indices); + xla::BroadcastInDim(ratio, broadcast_dims, ratio_broadcast_indices); auto first_term_subtract_weights = broadcast_first_term - broadcast_ratio; @@ -96,7 +89,7 @@ XlaOp BilinearWeights(XlaOpKernelContext* ctx, XlaOp ratio, sign_change = xla::ConvertElementType(sign_change, xla_type); auto broadcast_sign_change = - xla::BroadcastInDim(sign_change, broadcast_shape, last_two_dims_indices); + xla::BroadcastInDim(sign_change, broadcast_dims, last_two_dims_indices); auto flipped = first_term_subtract_weights * broadcast_sign_change; @@ -232,21 +225,19 @@ XlaOp CalculateGradData(XlaOpKernelContext* ctx, XlaOp grad_output, XlaOp ratio, std::vector weights_with_channels_dims = reshaped_weights_dims; weights_with_channels_dims.push_back(data_channels); - auto weights_with_channels_shape = - xla::ShapeUtil::MakeShape(warp_type, weights_with_channels_dims); std::vector reshaped_weights_indices(reshaped_weights_dims.size()); std::iota(reshaped_weights_indices.begin(), reshaped_weights_indices.end(), 0); // The dimension is [batch, dim_0, ..., dim_n, 2, 2, data_channel]. auto broadcast_reshaped_weights = xla::BroadcastInDim( - reshaped_weights, weights_with_channels_shape, reshaped_weights_indices); + reshaped_weights, weights_with_channels_dims, reshaped_weights_indices); std::vector grad_output_indices(warp_dims_without_last_dims.size()); std::iota(grad_output_indices.begin(), grad_output_indices.end(), 0); grad_output_indices.push_back(weights_with_channels_dims.size() - 1); XlaOp broadcast_grad_output = xla::BroadcastInDim( - grad_output, weights_with_channels_shape, grad_output_indices); + grad_output, weights_with_channels_dims, grad_output_indices); auto grad_output_multiply_weights = broadcast_grad_output * broadcast_reshaped_weights; @@ -294,13 +285,10 @@ XlaOp CalculateGradWarp(XlaOpKernelContext* ctx, XlaOp grad_output, XlaOp ratio, std::vector warp_dims_without_last_dims(warp_dims.begin(), warp_dims.end() - 1); + // With dimension [batch, dim_0, ...dim_n, 4] std::vector neighbor_broadcast_dims = warp_dims_without_last_dims; neighbor_broadcast_dims.push_back(4); - // With dimension [batch, dim_0, ...dim_n, 4] - auto neighbor_broadcast_shape = - xla::ShapeUtil::MakeShape(data_type, neighbor_broadcast_dims); - // The dimension is [batch, dim_0, ... dim_n, 4, data_channels] auto neighbors_data = Gather2by2Neighbors( ctx->builder(), data, gather_indices, data_channels, warp_shape.dims()); @@ -326,7 +314,7 @@ XlaOp CalculateGradWarp(XlaOpKernelContext* ctx, XlaOp grad_output, XlaOp ratio, xla::BroadcastInDim( xla::ConvertElementType( xla::ConstantR1(ctx->builder(), {0, 0, -1, 1}), data_type), - neighbor_broadcast_shape, {last_warp_dim}), + neighbor_broadcast_dims, {last_warp_dim}), neighbors_data, dot_dims, /*precision_config=*/nullptr); // img_cxfy - img_fxfy @@ -334,7 +322,7 @@ XlaOp CalculateGradWarp(XlaOpKernelContext* ctx, XlaOp grad_output, XlaOp ratio, xla::BroadcastInDim( xla::ConvertElementType( xla::ConstantR1(ctx->builder(), {-1, 1, 0, 0}), data_type), - neighbor_broadcast_shape, {last_warp_dim}), + neighbor_broadcast_dims, {last_warp_dim}), neighbors_data, dot_dims, /*precision_config=*/nullptr); // img_cxcy - img_cxfy @@ -342,7 +330,7 @@ XlaOp CalculateGradWarp(XlaOpKernelContext* ctx, XlaOp grad_output, XlaOp ratio, xla::BroadcastInDim( xla::ConvertElementType( xla::ConstantR1(ctx->builder(), {0, -1, 0, 1}), data_type), - neighbor_broadcast_shape, {last_warp_dim}), + neighbor_broadcast_dims, {last_warp_dim}), neighbors_data, dot_dims, /*precision_config=*/nullptr); // img_fxcy - img_fxfy @@ -350,7 +338,7 @@ XlaOp CalculateGradWarp(XlaOpKernelContext* ctx, XlaOp grad_output, XlaOp ratio, xla::BroadcastInDim( xla::ConvertElementType( xla::ConstantR1(ctx->builder(), {-1, 0, 1, 0}), data_type), - neighbor_broadcast_shape, {last_warp_dim}), + neighbor_broadcast_dims, {last_warp_dim}), neighbors_data, dot_dims, /*precision_config=*/nullptr); // Slice out x and y. @@ -421,12 +409,13 @@ class ResamplerOp : public XlaOpKernel { OP_REQUIRES(ctx, warp_shape.dim_size(last_warp_dim) == 2, errors::InvalidArgument( "the last dimension of warp must be exactly size 2.")); + xla::PrimitiveType warp_type = ctx->input_xla_type(1); XlaOp data = ctx->Input("data"); XlaOp warp = ctx->Input("warp"); // Find the coordinates of the top left corner for the 2x2 region to be - // sampled from. The dimensions are (batch, dim_0, ... dim_n, 2) where the + // sampled from. The dimensions are [batch, dim_0, ... dim_n, 2] where the // last dimension of size 2 in turn is [x, y]. XlaOp top_left = xla::ConvertElementType(warp, xla::U32); @@ -457,10 +446,54 @@ class ResamplerOp : public XlaOpKernel { dot_dims.add_lhs_contracting_dimensions(warp_shape.dims() - 1); dot_dims.add_rhs_contracting_dimensions(warp_shape.dims() - 1); + // The dimension is [batch, dim_0, ...dim_n, data_channels]. auto blended_pixels = xla::DotGeneral(weights, neighbors_data, dot_dims, /*precision_config=*/nullptr); - ctx->SetOutput(0, blended_pixels); + // Handle out of boundary cases by constructing a predicate mask array based + // on the in-bound condition, and output 0 for the blended pixel value if + // out-bound. The dimension is the same as top_left: [batch, dim_0, + // ...dim_n, 2] where the last dimension of size 2 is the [x, y] coordinate. + + auto is_ge_zero = xla::Ge(warp, xla::ZerosLike(warp)); + + auto is_lt_image_size = xla::Lt( + warp, + xla::ConvertElementType( + xla::ConstantR1( + ctx->builder(), + {/*width=*/static_cast(data_shape.dim_size(2) - 1), + /*height=*/static_cast(data_shape.dim_size(1) - 1)}), + warp_type), + /*broadcast_dimensions=*/{warp_shape.dims() - 1}); + + auto is_in_bound_x_y = xla::And(is_ge_zero, is_lt_image_size); + // Reduce along last dimension. The resulting dimension is: + // [batch, dim_0, ...dim_n]. + auto is_in_bound = xla::Reduce( + is_in_bound_x_y, xla::ConstantR0(ctx->builder(), true), + xla::CreateScalarAndComputation(xla::PrimitiveType::PRED, + ctx->builder()), + {last_warp_dim}); + + // Broadcast 'is_in_bound' to the same dimension as 'blended_pixels', which + // is the dimension of the result: + // [batch, dim_0, ...dim_n, data_channels]. + auto warp_dims = warp_shape.dim_sizes(); + std::vector result_dims(warp_dims.begin(), warp_dims.end() - 1); + result_dims.push_back(data_channels); + + std::vector broadcasted_dims(warp_dims.size() - 1); + std::iota(broadcasted_dims.begin(), broadcasted_dims.end(), 0); + auto broadcasted_is_in_bound = + xla::BroadcastInDim(is_in_bound, result_dims, broadcasted_dims); + + // Set out of bound samples to zero. + auto zeros = + xla::Broadcast(xla::Zero(ctx->builder(), data_type), result_dims); + auto result = xla::Select(broadcasted_is_in_bound, blended_pixels, zeros); + + ctx->SetOutput(0, result); } }; @@ -473,6 +506,8 @@ class ResamplerGradOp : public XlaOpKernel { OP_REQUIRES_OK(ctx, ctx->GetAttr("T", &output_dtype)); } + // TODO(b/112295522): note that sampling from image boundary is not currently + // being handled properly. void Compile(XlaOpKernelContext* ctx) override { TensorShape data_shape_tf = ctx->InputShape("data"); OP_REQUIRES(ctx, data_shape_tf.dims() == 4, diff --git a/tensorflow/compiler/tf2xla/kernels/retval_op.cc b/tensorflow/compiler/tf2xla/kernels/retval_op.cc index 6970dd0a00..e4046c7955 100644 --- a/tensorflow/compiler/tf2xla/kernels/retval_op.cc +++ b/tensorflow/compiler/tf2xla/kernels/retval_op.cc @@ -47,8 +47,7 @@ class RetvalOp : public XlaOpKernel { // compilation. OP_REQUIRES_OK(ctx, frame->SetRetval(index_, input)); } else { - XlaContext& xla_context = XlaContext::Get(ctx); - xla_context.SetRetval(index_, ctx->InputExpression(0)); + ctx->xla_context()->SetRetval(index_, ctx->InputExpression(0)); } } diff --git a/tensorflow/compiler/tf2xla/kernels/reverse_sequence_op.cc b/tensorflow/compiler/tf2xla/kernels/reverse_sequence_op.cc index 7ff3e91638..d7b38e86cc 100644 --- a/tensorflow/compiler/tf2xla/kernels/reverse_sequence_op.cc +++ b/tensorflow/compiler/tf2xla/kernels/reverse_sequence_op.cc @@ -18,7 +18,6 @@ limitations under the License. #include "tensorflow/compiler/tf2xla/xla_op_kernel.h" #include "tensorflow/compiler/tf2xla/xla_op_registry.h" #include "tensorflow/compiler/xla/client/lib/constants.h" -#include "tensorflow/compiler/xla/client/lib/numeric.h" #include "tensorflow/compiler/xla/client/xla_builder.h" #include "tensorflow/compiler/xla/xla_data.pb.h" #include "tensorflow/core/framework/tensor_shape.h" diff --git a/tensorflow/compiler/tf2xla/kernels/scan_ops.cc b/tensorflow/compiler/tf2xla/kernels/scan_ops.cc index b5fd7850bf..4b9e1a578b 100644 --- a/tensorflow/compiler/tf2xla/kernels/scan_ops.cc +++ b/tensorflow/compiler/tf2xla/kernels/scan_ops.cc @@ -39,8 +39,8 @@ namespace { // TODO(phawkins): implement double-sized windowed reductions in XLA and remove // the type constraint. -constexpr std::array kScanOpTypes = { - {DT_HALF, DT_BFLOAT16, DT_FLOAT}}; +constexpr std::array kScanOpTypes = { + {DT_HALF, DT_BFLOAT16, DT_FLOAT, DT_INT32}}; class ScanOp : public XlaOpKernel { public: @@ -103,11 +103,10 @@ class ScanOp : public XlaOpKernel { reducer = ctx->GetOrCreateMul(dtype); } auto output = xla::ReduceWindowWithGeneralPadding( - XlaHelpers::ConvertElementType(builder, ctx->Input(0), dtype), init, - *reducer, window_dims, window_strides, + XlaHelpers::ConvertElementType(ctx->Input(0), dtype), init, *reducer, + window_dims, window_strides, /*base_dilations=*/{}, /*window_dilations=*/{}, padding); - output = - XlaHelpers::ConvertElementType(builder, output, ctx->input_type(0)); + output = XlaHelpers::ConvertElementType(output, ctx->input_type(0)); // In exclusive mode, we have computed an extra element containing the sum // of all the input elements. Slice off this extra "last" element. diff --git a/tensorflow/compiler/tf2xla/kernels/sendrecv_ops.cc b/tensorflow/compiler/tf2xla/kernels/sendrecv_ops.cc index a7f5a8f169..84470b230d 100644 --- a/tensorflow/compiler/tf2xla/kernels/sendrecv_ops.cc +++ b/tensorflow/compiler/tf2xla/kernels/sendrecv_ops.cc @@ -42,7 +42,7 @@ SendOp::SendOp(OpKernelConstruction* ctx) : XlaOpKernel(ctx) { } void SendOp::Compile(XlaOpKernelContext* ctx) { - XlaCompiler* compiler = XlaContext::Get(ctx).compiler(); + XlaCompiler* compiler = ctx->compiler(); xla::ChannelHandle channel; OP_REQUIRES_OK(ctx, compiler->GetChannelHandle(tensor_name_, &channel)); xla::Send(ctx->Input(0), channel); @@ -73,7 +73,7 @@ RecvOp::RecvOp(OpKernelConstruction* ctx) : XlaOpKernel(ctx) { } void RecvOp::Compile(XlaOpKernelContext* ctx) { - XlaCompiler* compiler = XlaContext::Get(ctx).compiler(); + XlaCompiler* compiler = ctx->compiler(); xla::ChannelHandle channel; OP_REQUIRES_OK(ctx, compiler->GetChannelHandle(tensor_name_, &channel)); ctx->SetOutput(0, xla::Recv(ctx->builder(), shape_, channel)); diff --git a/tensorflow/compiler/tf2xla/kernels/sequence_ops.cc b/tensorflow/compiler/tf2xla/kernels/sequence_ops.cc index 60b011ba6d..b1fa2915d5 100644 --- a/tensorflow/compiler/tf2xla/kernels/sequence_ops.cc +++ b/tensorflow/compiler/tf2xla/kernels/sequence_ops.cc @@ -18,7 +18,7 @@ limitations under the License. #include "tensorflow/compiler/tf2xla/xla_helpers.h" #include "tensorflow/compiler/tf2xla/xla_op_kernel.h" #include "tensorflow/compiler/tf2xla/xla_op_registry.h" -#include "tensorflow/compiler/xla/client/lib/numeric.h" +#include "tensorflow/compiler/xla/client/xla_builder.h" #include "tensorflow/compiler/xla/literal.h" #include "tensorflow/compiler/xla/primitive_util.h" #include "tensorflow/core/framework/op_kernel.h" diff --git a/tensorflow/compiler/tf2xla/kernels/softmax_op.cc b/tensorflow/compiler/tf2xla/kernels/softmax_op.cc index d6bd927135..20da803353 100644 --- a/tensorflow/compiler/tf2xla/kernels/softmax_op.cc +++ b/tensorflow/compiler/tf2xla/kernels/softmax_op.cc @@ -71,7 +71,7 @@ class SoftmaxOp : public XlaOpKernel { auto reduce = xla::Reduce(converted, xla::Zero(b, xla_accumulation_type), *ctx->GetOrCreateAdd(accumulation_type), {kClassDim}); - auto sum = XlaHelpers::ConvertElementType(b, reduce, type); + auto sum = XlaHelpers::ConvertElementType(reduce, type); auto softmax = log_ // softmax = shifted_logits - log(sum(exp(shifted_logits))) @@ -111,11 +111,11 @@ std::pair CrossEntropyWithLogits( // sum_{class} (exp(logits - max_logits)) const DataType accumulation_type = XlaHelpers::SumAccumulationType(type); auto converted = - XlaHelpers::ConvertElementType(b, exp_shifted_logits, accumulation_type); + XlaHelpers::ConvertElementType(exp_shifted_logits, accumulation_type); auto reduce = xla::Reduce(converted, XlaHelpers::Zero(b, accumulation_type), *ctx->GetOrCreateAdd(accumulation_type), {kClassDim}); - auto sum_exp = XlaHelpers::ConvertElementType(b, reduce, type); + auto sum_exp = XlaHelpers::ConvertElementType(reduce, type); // log(sum(exp(logits - max_logits))) auto log_sum_exp = xla::Log(sum_exp); @@ -126,11 +126,10 @@ std::pair CrossEntropyWithLogits( // (The subtraction broadcasts along the batch dimension.) auto sub = xla::Sub(shifted_logits, log_sum_exp, {kBatchDim}); auto mul = xla::Mul(xla::Neg(labels), sub); - auto sum = - xla::Reduce(XlaHelpers::ConvertElementType(b, mul, accumulation_type), - XlaHelpers::Zero(b, accumulation_type), - *ctx->GetOrCreateAdd(accumulation_type), {kClassDim}); - auto loss = XlaHelpers::ConvertElementType(b, sum, type); + auto sum = xla::Reduce(XlaHelpers::ConvertElementType(mul, accumulation_type), + XlaHelpers::Zero(b, accumulation_type), + *ctx->GetOrCreateAdd(accumulation_type), {kClassDim}); + auto loss = XlaHelpers::ConvertElementType(sum, type); // backprop: prob - labels, where // prob = exp(logits - max_logits) / sum(exp(logits - max_logits)) diff --git a/tensorflow/compiler/tf2xla/kernels/stack_ops.cc b/tensorflow/compiler/tf2xla/kernels/stack_ops.cc index 7b96b43ad8..8e9e4daf99 100644 --- a/tensorflow/compiler/tf2xla/kernels/stack_ops.cc +++ b/tensorflow/compiler/tf2xla/kernels/stack_ops.cc @@ -69,7 +69,7 @@ Status MaybeInitializeStack(xla::XlaBuilder* builder, XlaResource* resource, } TensorShape stack_shape; - stack_shape.AddDim(resource->tensor_array_size()); + stack_shape.AddDim(resource->max_array_size()); stack_shape.AppendShape(elem_shape); if (!resource->initialized()) { @@ -97,10 +97,10 @@ class StackOp : public XlaOpKernel { } void Compile(XlaOpKernelContext* ctx) override { - int64 size; - OP_REQUIRES_OK(ctx, ctx->ConstantInputAsIntScalar(0, &size)); + int64 max_size; + OP_REQUIRES_OK(ctx, ctx->ConstantInputAsIntScalar(0, &max_size)); OP_REQUIRES( - ctx, size >= 0, + ctx, max_size >= 0, errors::InvalidArgument( "XLA compilation requires a fixed stack size upper bound. If " "you are using tf.while_loop, set the maximum_iterations parameter " @@ -108,14 +108,9 @@ class StackOp : public XlaOpKernel { // We defer initializing the Stack resource until we see the first push. // Otherwise we do not know the shape of the stack elements. - xla::XlaOp value; - XlaContext& xc = XlaContext::Get(ctx); - XlaResource* resource; - string name = absl::StrCat("Stack: ", stack_name_); - OP_REQUIRES_OK( - ctx, xc.CreateResource(XlaResource::kStack, -1, std::move(name), dtype_, - TensorShape(), value, /*tensor_array_size=*/size, - /*tensor_array_gradients=*/{}, &resource)); + XlaResource* resource = + ctx->xla_context()->AddResource(XlaResource::CreateStack( + /*name=*/absl::StrCat("Stack: ", stack_name_), dtype_, max_size)); ctx->SetResourceOutput(0, resource); } diff --git a/tensorflow/compiler/tf2xla/kernels/stateless_random_ops.cc b/tensorflow/compiler/tf2xla/kernels/stateless_random_ops.cc index 5db52781be..50653d7b39 100644 --- a/tensorflow/compiler/tf2xla/kernels/stateless_random_ops.cc +++ b/tensorflow/compiler/tf2xla/kernels/stateless_random_ops.cc @@ -23,7 +23,6 @@ limitations under the License. #include "tensorflow/compiler/tf2xla/xla_op_registry.h" #include "tensorflow/compiler/xla/client/lib/constants.h" #include "tensorflow/compiler/xla/client/lib/math.h" -#include "tensorflow/compiler/xla/client/lib/numeric.h" #include "tensorflow/compiler/xla/client/lib/prng.h" #include "tensorflow/compiler/xla/client/xla_builder.h" #include "tensorflow/core/framework/op_kernel.h" diff --git a/tensorflow/compiler/tf2xla/kernels/tensor_array_ops.cc b/tensorflow/compiler/tf2xla/kernels/tensor_array_ops.cc index 252967a746..939d7e1951 100644 --- a/tensorflow/compiler/tf2xla/kernels/tensor_array_ops.cc +++ b/tensorflow/compiler/tf2xla/kernels/tensor_array_ops.cc @@ -61,8 +61,8 @@ Status MaybeInitializeTensorArray(xla::XlaBuilder* builder, " but op has dtype ", DataTypeString(dtype), "."); } - TF_RET_CHECK(resource->tensor_array_size() >= 0) - << resource->name() << " size " << resource->tensor_array_size(); + TF_RET_CHECK(resource->max_array_size() >= 0) + << resource->name() << " size " << resource->max_array_size(); if (!resource->initialized()) { TF_RETURN_IF_ERROR(resource->SetTypeAndShape(dtype, elem_shape)); @@ -78,7 +78,7 @@ Status MaybeInitializeTensorArray(xla::XlaBuilder* builder, XLAShapeToTensorShape(shape_or_status.ValueOrDie(), &shape)); TensorShape ta_shape; - ta_shape.AddDim(resource->tensor_array_size()); + ta_shape.AddDim(resource->max_array_size()); ta_shape.AppendShape(elem_shape); if (ta_shape != shape) { return errors::InvalidArgument( @@ -114,7 +114,7 @@ Status CheckTensorArrayIsInitialized(const string& op_name, Status GetTensorArrayShape(const XlaResource* resource, xla::XlaBuilder* builder, TensorShape* shape) { *shape = resource->shape(); - shape->InsertDim(0, resource->tensor_array_size()); + shape->InsertDim(0, resource->max_array_size()); return Status::OK(); } @@ -166,13 +166,10 @@ class TensorArrayOp : public XlaOpKernel { value = xla::Broadcast(zero, ta_shape.dim_sizes()); } - XlaContext& xc = XlaContext::Get(ctx); - XlaResource* var; - string name = absl::StrCat("TensorArray: ", tensor_array_name_); - OP_REQUIRES_OK( - ctx, xc.CreateResource(XlaResource::kTensorArray, -1, std::move(name), - dtype_, shape, value, /*tensor_array_size=*/size, - /*tensor_array_gradients=*/{}, &var)); + XlaResource* var = + ctx->xla_context()->AddResource(XlaResource::CreateTensorArray( + /*name=*/absl::StrCat("TensorArray: ", tensor_array_name_), dtype_, + shape, /*initial_value=*/value, /*max_array_size=*/size)); ctx->SetResourceOutput(0, var); Tensor flow(DT_FLOAT, TensorShape({})); @@ -517,14 +514,13 @@ class TensorArraySplitOp : public XlaOpKernel { xla::XlaOp ta = resource->value(); TensorShape ta_shape; - ta_shape.AddDim(resource->tensor_array_size()); + ta_shape.AddDim(resource->max_array_size()); ta_shape.AppendShape(elem_shape); - OP_REQUIRES( - ctx, lengths.size() == resource->tensor_array_size(), - errors::InvalidArgument( - "TensorArray's size is not equal to the size of lengths (", - lengths.size(), " vs. ", resource->tensor_array_size(), ")")); + OP_REQUIRES(ctx, lengths.size() == resource->max_array_size(), + errors::InvalidArgument( + "TensorArray's size is not equal to the size of lengths (", + lengths.size(), " vs. ", resource->max_array_size(), ")")); const xla::XlaOp value = ctx->Input(1); const xla::XlaOp flow = ctx->Input(3); @@ -562,8 +558,7 @@ class TensorArraySizeOp : public XlaOpKernel { XlaResource* var; OP_REQUIRES_OK(ctx, ctx->GetResourceInput(0, &var)); Tensor size_tensor(DT_INT32, {}); - size_tensor.scalar()() = - static_cast(var->tensor_array_size()); + size_tensor.scalar()() = static_cast(var->max_array_size()); ctx->SetConstantOutput(0, size_tensor); } diff --git a/tensorflow/compiler/tf2xla/kernels/topk_op.cc b/tensorflow/compiler/tf2xla/kernels/topk_op.cc index 8a0c94cfae..ee3bdf3394 100644 --- a/tensorflow/compiler/tf2xla/kernels/topk_op.cc +++ b/tensorflow/compiler/tf2xla/kernels/topk_op.cc @@ -15,7 +15,6 @@ limitations under the License. #include "tensorflow/compiler/tf2xla/xla_op_kernel.h" #include "tensorflow/compiler/tf2xla/xla_op_registry.h" -#include "tensorflow/compiler/xla/client/lib/numeric.h" #include "tensorflow/compiler/xla/client/lib/sorting.h" #include "tensorflow/compiler/xla/client/xla_builder.h" #include "tensorflow/compiler/xla/literal.h" diff --git a/tensorflow/compiler/tf2xla/kernels/training_ops.cc b/tensorflow/compiler/tf2xla/kernels/training_ops.cc index 7077c2e3a5..960c1462ce 100644 --- a/tensorflow/compiler/tf2xla/kernels/training_ops.cc +++ b/tensorflow/compiler/tf2xla/kernels/training_ops.cc @@ -320,9 +320,8 @@ class ResourceApplyAdagradDA : public XlaOpKernel { xla::XlaOp lr = ctx->Input(4); xla::XlaOp l1 = ctx->Input(5); xla::XlaOp l2 = ctx->Input(6); - xla::XlaBuilder* const b = ctx->builder(); xla::XlaOp global_step = - XlaHelpers::ConvertElementType(b, ctx->Input(7), dtype_); + XlaHelpers::ConvertElementType(ctx->Input(7), dtype_); accum = accum + grad; squared_accum = squared_accum + xla::Square(grad); diff --git a/tensorflow/compiler/tf2xla/kernels/while_op.cc b/tensorflow/compiler/tf2xla/kernels/while_op.cc index 559414eeaa..ce007fc04a 100644 --- a/tensorflow/compiler/tf2xla/kernels/while_op.cc +++ b/tensorflow/compiler/tf2xla/kernels/while_op.cc @@ -64,7 +64,7 @@ Status MakeXlaCompilerArgumentsFromInputs( if (!arg.initialized) { *has_uninitialized_vars = true; } - arg.tensor_array_size = resource->tensor_array_size(); + arg.max_array_size = resource->max_array_size(); for (const auto& gradient : resource->tensor_array_gradients()) { arg.tensor_array_gradients.insert(gradient.first); } diff --git a/tensorflow/compiler/tf2xla/kernels/xla_broadcast_helper_op.cc b/tensorflow/compiler/tf2xla/kernels/xla_broadcast_helper_op.cc index a9f88a6df2..ad8e707e11 100644 --- a/tensorflow/compiler/tf2xla/kernels/xla_broadcast_helper_op.cc +++ b/tensorflow/compiler/tf2xla/kernels/xla_broadcast_helper_op.cc @@ -89,13 +89,10 @@ class XlaBroadcastHelperOp : public XlaOpKernel { lhs_shape.DebugString(), " and ", rhs_shape.DebugString())); broadcast_shape[dim] = min_rank_shape->dim_size(i); } - xla::PrimitiveType type = context->input_xla_type(0); - xla::Shape broadcast_xla_shape = - xla::ShapeUtil::MakeShape(type, broadcast_shape); if (broadcast_lhs) { - lhs = xla::BroadcastInDim(lhs, broadcast_xla_shape, broadcast_dims); + lhs = xla::BroadcastInDim(lhs, broadcast_shape, broadcast_dims); } else { - rhs = xla::BroadcastInDim(rhs, broadcast_xla_shape, broadcast_dims); + rhs = xla::BroadcastInDim(rhs, broadcast_shape, broadcast_dims); } context->SetOutput(0, lhs); context->SetOutput(1, rhs); diff --git a/tensorflow/compiler/tf2xla/lib/BUILD b/tensorflow/compiler/tf2xla/lib/BUILD index 1ce3930fd1..422781d536 100644 --- a/tensorflow/compiler/tf2xla/lib/BUILD +++ b/tensorflow/compiler/tf2xla/lib/BUILD @@ -17,20 +17,6 @@ filegroup( load("//tensorflow/compiler/xla/tests:build_defs.bzl", "xla_test") -cc_library( - name = "batch_dot", - srcs = ["batch_dot.cc"], - hdrs = ["batch_dot.h"], - deps = [ - "//tensorflow/compiler/xla:shape_util", - "//tensorflow/compiler/xla:status_macros", - "//tensorflow/compiler/xla:statusor", - "//tensorflow/compiler/xla:xla_data_proto", - "//tensorflow/compiler/xla/client:xla_builder", - "//tensorflow/core:lib", - ], -) - cc_library( name = "broadcast", srcs = ["broadcast.cc"], @@ -52,7 +38,6 @@ cc_library( srcs = ["cholesky.cc"], hdrs = ["cholesky.h"], deps = [ - ":batch_dot", ":triangular_solve", ":util", ":while_loop", @@ -63,6 +48,8 @@ cc_library( "//tensorflow/compiler/xla:xla_data_proto", "//tensorflow/compiler/xla/client:xla_builder", "//tensorflow/compiler/xla/client/lib:constants", + "//tensorflow/compiler/xla/client/lib:matrix", + "//tensorflow/compiler/xla/client/lib:slicing", "//tensorflow/core:lib", ], ) @@ -87,7 +74,6 @@ cc_library( srcs = ["qr.cc"], hdrs = ["qr.h"], deps = [ - ":batch_dot", ":util", ":while_loop", "//tensorflow/compiler/xla:literal_util", @@ -99,7 +85,8 @@ cc_library( "//tensorflow/compiler/xla/client/lib:arithmetic", "//tensorflow/compiler/xla/client/lib:constants", "//tensorflow/compiler/xla/client/lib:math", - "//tensorflow/compiler/xla/client/lib:numeric", + "//tensorflow/compiler/xla/client/lib:matrix", + "//tensorflow/compiler/xla/client/lib:slicing", "//tensorflow/core:lib", ], ) @@ -129,7 +116,6 @@ cc_library( srcs = ["triangular_solve.cc"], hdrs = ["triangular_solve.h"], deps = [ - ":batch_dot", ":util", "//tensorflow/compiler/xla:literal", "//tensorflow/compiler/xla:shape_util", @@ -140,7 +126,9 @@ cc_library( "//tensorflow/compiler/xla/client:xla_builder", "//tensorflow/compiler/xla/client:xla_computation", "//tensorflow/compiler/xla/client/lib:constants", - "//tensorflow/compiler/xla/client/lib:numeric", + "//tensorflow/compiler/xla/client/lib:math", + "//tensorflow/compiler/xla/client/lib:matrix", + "//tensorflow/compiler/xla/client/lib:slicing", "//tensorflow/core:lib", ], ) @@ -187,29 +175,6 @@ cc_library( ], ) -xla_test( - name = "util_test", - srcs = ["util_test.cc"], - deps = [ - ":batch_dot", - ":util", - "//tensorflow/compiler/xla:array2d", - "//tensorflow/compiler/xla:literal", - "//tensorflow/compiler/xla:shape_util", - "//tensorflow/compiler/xla:statusor", - "//tensorflow/compiler/xla:test", - "//tensorflow/compiler/xla:types", - "//tensorflow/compiler/xla:xla_data_proto", - "//tensorflow/compiler/xla/client:global_data", - "//tensorflow/compiler/xla/client:local_client", - "//tensorflow/compiler/xla/tests:client_library_test_base", - "//tensorflow/compiler/xla/tests:literal_test_util", - "//tensorflow/compiler/xla/tests:xla_internal_test_main", - "//tensorflow/core:lib", - "//tensorflow/core:test", - ], -) - cc_library( name = "while_loop", srcs = ["while_loop.cc"], diff --git a/tensorflow/compiler/tf2xla/lib/batch_dot.cc b/tensorflow/compiler/tf2xla/lib/batch_dot.cc deleted file mode 100644 index 5400e8834c..0000000000 --- a/tensorflow/compiler/tf2xla/lib/batch_dot.cc +++ /dev/null @@ -1,115 +0,0 @@ -/* 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/compiler/tf2xla/lib/batch_dot.h" - -#include -#include - -#include "tensorflow/compiler/xla/client/xla_builder.h" -#include "tensorflow/compiler/xla/shape_util.h" -#include "tensorflow/compiler/xla/status_macros.h" -#include "tensorflow/compiler/xla/statusor.h" -#include "tensorflow/core/lib/core/errors.h" - -namespace tensorflow { - -xla::XlaOp BatchDot(xla::XlaOp x, xla::XlaOp y, bool transpose_x, - bool transpose_y, bool conjugate_x, bool conjugate_y, - xla::PrecisionConfig::Precision precision) { - xla::XlaBuilder* builder = x.builder(); - return builder->ReportErrorOrReturn([&]() -> xla::StatusOr { - TF_ASSIGN_OR_RETURN(xla::Shape x_shape, builder->GetShape(x)); - TF_ASSIGN_OR_RETURN(xla::Shape y_shape, builder->GetShape(y)); - - // Check that both tensors have the same number of dimensions. There must be - // at least two (the batch dimensions can be empty). - if (xla::ShapeUtil::Rank(x_shape) != xla::ShapeUtil::Rank(y_shape)) { - return errors::InvalidArgument( - "Arguments to BatchedDot have different ranks: ", - xla::ShapeUtil::HumanString(x_shape), " vs. ", - xla::ShapeUtil::HumanString(y_shape)); - } - const int ndims = xla::ShapeUtil::Rank(x_shape); - if (ndims < 2) { - return errors::InvalidArgument( - "Arguments to BatchedDot must have rank >= 2: ", ndims); - } - - // The batch dimensions must be equal and the matrix dimensions must be - // valid. - std::vector batch_dimension_numbers; - for (int i = 0; i < ndims - 2; ++i) { - if (x_shape.dimensions(i) != y_shape.dimensions(i)) { - return errors::InvalidArgument( - "Dimension ", i, " of inputs to BatchedDot must be equal: ", - xla::ShapeUtil::HumanString(x_shape), " vs ", - xla::ShapeUtil::HumanString(y_shape)); - } - batch_dimension_numbers.push_back(i); - } - - int x_inner_dim = transpose_x ? (ndims - 2) : (ndims - 1); - int y_inner_dim = transpose_y ? (ndims - 1) : (ndims - 2); - if (x_shape.dimensions(x_inner_dim) != y_shape.dimensions(y_inner_dim)) { - return errors::InvalidArgument( - "Dimensions ", x_inner_dim, " and ", y_inner_dim, - " of arguments to BatchedDot must be equal: ", - xla::ShapeUtil::HumanString(x_shape), " transpose: ", transpose_x, - " vs. ", xla::ShapeUtil::HumanString(y_shape), - " transpose: ", transpose_y); - } - - // Check for zero lhs/rhs dim size. - if (xla::ShapeUtil::IsZeroElementArray(x_shape) || - xla::ShapeUtil::IsZeroElementArray(y_shape)) { - std::vector dimensions(batch_dimension_numbers.size()); - for (int i = 0; i < batch_dimension_numbers.size(); ++i) { - dimensions[i] = x_shape.dimensions(batch_dimension_numbers[i]); - } - int x_outer_dim = transpose_x ? (ndims - 1) : (ndims - 2); - int y_outer_dim = transpose_y ? (ndims - 2) : (ndims - 1); - dimensions.push_back(x_shape.dimensions(x_outer_dim)); - dimensions.push_back(y_shape.dimensions(y_outer_dim)); - return xla::Broadcast( - xla::ConstantLiteral(builder, - xla::LiteralUtil::Zero(x_shape.element_type())), - dimensions); - } - - if (x_shape.element_type() == xla::C64 && conjugate_x) { - x = xla::Conj(x); - } - if (y_shape.element_type() == xla::C64 && conjugate_y) { - y = xla::Conj(y); - } - - xla::PrecisionConfig precision_proto; - precision_proto.add_operand_precision(precision); - precision_proto.add_operand_precision(precision); - - xla::DotDimensionNumbers dot_dnums; - dot_dnums.add_lhs_contracting_dimensions(x_inner_dim); - dot_dnums.add_rhs_contracting_dimensions(y_inner_dim); - for (auto batch_dimension_number : batch_dimension_numbers) { - dot_dnums.add_lhs_batch_dimensions(batch_dimension_number); - dot_dnums.add_rhs_batch_dimensions(batch_dimension_number); - } - - return xla::DotGeneral(x, y, dot_dnums, &precision_proto); - }); -} - -} // namespace tensorflow diff --git a/tensorflow/compiler/tf2xla/lib/batch_dot.h b/tensorflow/compiler/tf2xla/lib/batch_dot.h deleted file mode 100644 index 6edd63a4d3..0000000000 --- a/tensorflow/compiler/tf2xla/lib/batch_dot.h +++ /dev/null @@ -1,54 +0,0 @@ -/* 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_COMPILER_TF2XLA_LIB_BATCH_DOT_H_ -#define TENSORFLOW_COMPILER_TF2XLA_LIB_BATCH_DOT_H_ - -#include "tensorflow/compiler/xla/client/xla_builder.h" -#include "tensorflow/compiler/xla/xla_data.pb.h" - -namespace tensorflow { - -// Multiplies slices of two tensors in batches. - -// Multiplies all slices of `Tensor` `x` and `y` (each slice can be -// viewed as an element of a batch), and arranges the individual results -// in a single output tensor of the same batch size. Each of the -// individual slices can optionally be transposed before multiplication by -// setting the `transpose_x` or `transpose_y` flag to `true`. Similarly, each -// can be elementwise-complex-conjugated by setting the `conjugate_x` or -// `conjugate_y` flag to `true`. To apply a Hermitian adjoint to `x`, set both -// `transpose_x` and `conjugate_x` to `true`, and analogously for `y`. -// -// The input tensors `x` and `y` are 2-D or higher with shape `[..., r_x, c_x]` -// and `[..., r_y, c_y]`. -// -// The output tensor is 2-D or higher with shape `[..., r_o, c_o]`, where: -// -// r_o = c_x if transpose_x else r_x -// c_o = r_y if transpose_y else c_y -// -// It is computed as: -// -// output[..., :, :] = matrix(x[..., :, :]) * matrix(y[..., :, :]) -xla::XlaOp BatchDot( - xla::XlaOp x, xla::XlaOp y, bool transpose_x = false, - bool transpose_y = false, bool conjugate_x = false, - bool conjugate_y = false, - xla::PrecisionConfig::Precision precision = xla::PrecisionConfig::DEFAULT); - -} // namespace tensorflow - -#endif // TENSORFLOW_COMPILER_TF2XLA_LIB_BATCH_DOT_H_ diff --git a/tensorflow/compiler/tf2xla/lib/broadcast.cc b/tensorflow/compiler/tf2xla/lib/broadcast.cc index 3e402ef855..be31f11668 100644 --- a/tensorflow/compiler/tf2xla/lib/broadcast.cc +++ b/tensorflow/compiler/tf2xla/lib/broadcast.cc @@ -80,10 +80,8 @@ xla::StatusOr BroadcastTo(xla::XlaOp input, broadcast_dim = broadcast_shape_size - broadcast_dim - 1; } absl::c_reverse(broadcast_shape); - xla::XlaOp output = xla::BroadcastInDim( - input, - xla::ShapeUtil::MakeShape(input_shape.element_type(), broadcast_shape), - broadcast_dims); + xla::XlaOp output = + xla::BroadcastInDim(input, broadcast_shape, broadcast_dims); if (broadcast_shape != output_dims) { output = xla::Reshape(output, output_dims); } diff --git a/tensorflow/compiler/tf2xla/lib/cholesky.cc b/tensorflow/compiler/tf2xla/lib/cholesky.cc index ab3d0a5668..7ef8659992 100644 --- a/tensorflow/compiler/tf2xla/lib/cholesky.cc +++ b/tensorflow/compiler/tf2xla/lib/cholesky.cc @@ -18,11 +18,12 @@ limitations under the License. #include #include -#include "tensorflow/compiler/tf2xla/lib/batch_dot.h" #include "tensorflow/compiler/tf2xla/lib/triangular_solve.h" #include "tensorflow/compiler/tf2xla/lib/util.h" #include "tensorflow/compiler/tf2xla/lib/while_loop.h" #include "tensorflow/compiler/xla/client/lib/constants.h" +#include "tensorflow/compiler/xla/client/lib/matrix.h" +#include "tensorflow/compiler/xla/client/lib/slicing.h" #include "tensorflow/compiler/xla/client/xla_builder.h" #include "tensorflow/compiler/xla/literal.h" #include "tensorflow/compiler/xla/shape_util.h" @@ -101,10 +102,7 @@ xla::XlaOp CholeskyUnblocked(xla::XlaOp a, // a[..., i, i] auto a_ii = DynamicSliceInMinorDims(body_a, {i, i}, {1, 1}); // np.dot(row, np.swapaxes(row, -1, -2)) - auto diag_dot = BatchDot(row, row, - /*transpose_x=*/false, - /*transpose_y=*/true, /*conjugate_x=*/false, - /*conjugate_y=*/false, precision); + auto diag_dot = BatchDot(row, TransposeInMinorDims(row), precision); // l[..., i, i] = np.sqrt(a[..., i, i] - np.dot(row, // np.swapaxes(row, -1, -2))) auto l_ii = @@ -122,10 +120,7 @@ xla::XlaOp CholeskyUnblocked(xla::XlaOp a, // The columns in [i, n] are zeroed out in `row`, so we just have to // zero out rows above i+1 after the BatchDot. np.dot(l[..., :, :i], // r.T) - auto dot = BatchDot(body_l, row, - /*transpose_x=*/false, - /*transpose_y=*/true, /*conjugate_x=*/false, - /*conjugate_y=*/false, precision); + auto dot = BatchDot(body_l, TransposeInMinorDims(row), precision); // np.dot(l[..., i+1:, :i], r.T) auto dot_ip1 = xla::Select(xla::Le(mask_range_col, i), mask_zeros_col, dot); @@ -185,9 +180,7 @@ xla::XlaOp Cholesky(xla::XlaOp a, int64 block_size, // a[i:, i:i+k] -= np.dot(l[i:, :i], np.transpose(l[i:i+k, :i])) auto lhs = SliceInMinorDims(l, {i, 0}, {n, i}); auto rhs = SliceInMinorDims(l, {i, 0}, {i + k, i}); - auto delta = BatchDot(lhs, rhs, /*transpose_x=*/false, - /*transpose_y=*/true, /*conjugate_x=*/false, - /*conjugate_y=*/false, precision); + auto delta = BatchDot(lhs, TransposeInMinorDims(rhs), precision); auto before = SliceInMinorDims(a, {i, i}, {n, i + k}); a = UpdateSliceInMinorDims(a, before - delta, {i, i}); } diff --git a/tensorflow/compiler/tf2xla/lib/qr.cc b/tensorflow/compiler/tf2xla/lib/qr.cc index 6b3f2b6e06..d600774860 100644 --- a/tensorflow/compiler/tf2xla/lib/qr.cc +++ b/tensorflow/compiler/tf2xla/lib/qr.cc @@ -18,13 +18,13 @@ limitations under the License. #include #include -#include "tensorflow/compiler/tf2xla/lib/batch_dot.h" #include "tensorflow/compiler/tf2xla/lib/util.h" #include "tensorflow/compiler/tf2xla/lib/while_loop.h" #include "tensorflow/compiler/xla/client/lib/arithmetic.h" #include "tensorflow/compiler/xla/client/lib/constants.h" #include "tensorflow/compiler/xla/client/lib/math.h" -#include "tensorflow/compiler/xla/client/lib/numeric.h" +#include "tensorflow/compiler/xla/client/lib/matrix.h" +#include "tensorflow/compiler/xla/client/lib/slicing.h" #include "tensorflow/compiler/xla/client/xla_builder.h" #include "tensorflow/compiler/xla/literal_util.h" #include "tensorflow/compiler/xla/shape_util.h" @@ -191,12 +191,8 @@ xla::StatusOr QRBlock( auto v_broadcast = xla::Reshape(v, shape); // a[:, :] -= tau * np.dot(v[:, np.newaxis], // np.dot(v[np.newaxis, :], a[:, :])) - auto vva = - BatchDot(v_broadcast, a, /*transpose_x=*/false, /*transpose_y=*/false, - /*conjugate_x=*/false, /*conjugate_y=*/false, precision); - vva = - BatchDot(v_broadcast, vva, /*transpose_x=*/true, /*transpose_y=*/false, - /*conjugate_x=*/false, /*conjugate_y=*/false, precision); + auto vva = BatchDot(v_broadcast, a, precision); + vva = BatchDot(TransposeInMinorDims(v_broadcast), vva, precision); a = a - xla::Mul(tau, vva, /*broadcast_dimensions=*/batch_dim_indices); @@ -278,12 +274,9 @@ xla::StatusOr ComputeWYRepresentation( auto beta = DynamicSliceInMinorDims(taus, {j}, {1}); // yv has shape [..., n, 1] - auto yv = BatchDot(y, v, /*transpose_x=*/true, /*transpose_y=*/false, - /*conjugate_x=*/false, /*conjugate_y=*/false, precision); + auto yv = BatchDot(TransposeInMinorDims(y), v, precision); // wyv has shape [..., m, 1] - auto wyv = - BatchDot(w, yv, /*transpose_x=*/false, /*transpose_y=*/false, - /*conjugate_x=*/false, /*conjugate_y=*/false, precision); + auto wyv = BatchDot(w, yv, precision); auto z = xla::Mul( -beta, v + wyv, @@ -375,23 +368,15 @@ xla::StatusOr QRDecomposition( // a[i:, i+k:] += np.dot(Y, np.dot(W.T, a[i:, i+k:])) auto a_panel = SliceInMinorDims(a, {i, i + k}, {m, n}); - auto a_update = - BatchDot(w, a_panel, /*transpose_x=*/true, /*transpose_y=*/false, - /*conjugate_x=*/false, /*conjugate_y=*/false, precision); - a_update = - BatchDot(y, a_update, /*transpose_x=*/false, /*transpose_y=*/false, - /*conjugate_x=*/false, /*conjugate_y=*/false, precision); + auto a_update = BatchDot(TransposeInMinorDims(w), a_panel, precision); + a_update = BatchDot(y, a_update, precision); a_panel = a_panel + a_update; a = UpdateSliceInMinorDims(a, a_panel, {i, i + k}); // q[:, i:] += np.dot(np.dot(q[:, i:], W), Y.T)) auto q_panel = SliceInMinorDims(q, {0, i}, {m, m}); - auto q_update = - BatchDot(q_panel, w, /*transpose_x=*/false, /*transpose_y=*/false, - /*conjugate_x=*/false, /*conjugate_y=*/false, precision); - q_update = BatchDot(q_update, y, /*transpose_x=*/false, - /*transpose_y=*/true, /*conjugate_x=*/false, - /*conjugate_y=*/false, precision); + auto q_update = BatchDot(q_panel, w, precision); + q_update = BatchDot(q_update, TransposeInMinorDims(y), precision); q_panel = q_panel + q_update; q = UpdateSliceInMinorDims(q, q_panel, {0, i}); } diff --git a/tensorflow/compiler/tf2xla/lib/triangular_solve.cc b/tensorflow/compiler/tf2xla/lib/triangular_solve.cc index 6524c2a9b1..192a61dca2 100644 --- a/tensorflow/compiler/tf2xla/lib/triangular_solve.cc +++ b/tensorflow/compiler/tf2xla/lib/triangular_solve.cc @@ -18,10 +18,11 @@ limitations under the License. #include #include -#include "tensorflow/compiler/tf2xla/lib/batch_dot.h" #include "tensorflow/compiler/tf2xla/lib/util.h" #include "tensorflow/compiler/xla/client/lib/constants.h" -#include "tensorflow/compiler/xla/client/lib/numeric.h" +#include "tensorflow/compiler/xla/client/lib/math.h" +#include "tensorflow/compiler/xla/client/lib/matrix.h" +#include "tensorflow/compiler/xla/client/lib/slicing.h" #include "tensorflow/compiler/xla/client/xla_builder.h" #include "tensorflow/compiler/xla/client/xla_computation.h" #include "tensorflow/compiler/xla/literal.h" @@ -311,13 +312,13 @@ xla::XlaOp SolveWithInvertedDiagonalBlocks( auto a_row = MaybeConjugate(SliceInMinorDims(a, start, end), conjugate_a); if (left_side) { - remainder = b_row - BatchDot(a_row, x, transpose_a, false, - /*conjugate_x=*/false, - /*conjugate_y=*/false, precision); + remainder = + b_row - BatchDot(MaybeTransposeInMinorDims(a_row, transpose_a), x, + precision); } else { - remainder = b_row - BatchDot(x, a_row, false, transpose_a, - /*conjugate_x=*/false, - /*conjugate_y=*/false, precision); + remainder = + b_row - BatchDot(x, MaybeTransposeInMinorDims(a_row, transpose_a), + precision); } } @@ -327,13 +328,12 @@ xla::XlaOp SolveWithInvertedDiagonalBlocks( xla::ConstantR0WithType(builder, xla::S32, j * block_size); std::vector update_starts = {start_index, zero}; if (left_side) { - x_update = - BatchDot(inv_block, remainder, transpose_a, false, - /*conjugate_x=*/false, /*conjugate_y=*/false, precision); + x_update = BatchDot(MaybeTransposeInMinorDims(inv_block, transpose_a), + remainder, precision); } else { - x_update = - BatchDot(remainder, inv_block, false, transpose_a, - /*conjugate_x=*/false, /*conjugate_y=*/false, precision); + x_update = BatchDot(remainder, + MaybeTransposeInMinorDims(inv_block, transpose_a), + precision); std::swap(update_starts[0], update_starts[1]); } x = DynamicUpdateSliceInMinorDims(x, x_update, /*starts=*/update_starts); diff --git a/tensorflow/compiler/tf2xla/lib/util.cc b/tensorflow/compiler/tf2xla/lib/util.cc index 804671fbc7..c0bd172d17 100644 --- a/tensorflow/compiler/tf2xla/lib/util.cc +++ b/tensorflow/compiler/tf2xla/lib/util.cc @@ -113,36 +113,6 @@ xla::XlaOp IntegerLiteral(xla::XlaBuilder* builder, xla::PrimitiveType type, return xla::ConstantLiteral(builder, literal); } -xla::XlaOp SliceInMinorDims(xla::XlaOp x, absl::Span start, - absl::Span end) { - xla::XlaBuilder* builder = x.builder(); - return builder->ReportErrorOrReturn([&]() -> xla::StatusOr { - TF_RET_CHECK(start.size() == end.size()); - int64 n_minor_dims = start.size(); - - TF_ASSIGN_OR_RETURN(xla::Shape shape, builder->GetShape(x)); - - const int64 n_dims = xla::ShapeUtil::Rank(shape); - TF_RET_CHECK(n_minor_dims <= n_dims); - auto major_dims = xla::AsInt64Slice(shape.dimensions()) - .subspan( - /*pos=*/0, - /*len=*/n_dims - n_minor_dims); - - // Prepends 0s in the major dim - std::vector padded_start(n_dims, 0); - std::copy(start.begin(), start.end(), - padded_start.begin() + major_dims.size()); - - // Prepends the shape of the major dims. - std::vector padded_end(n_dims); - std::copy(major_dims.begin(), major_dims.end(), padded_end.begin()); - std::copy(end.begin(), end.end(), padded_end.begin() + major_dims.size()); - - std::vector strides(n_dims, 1); - return xla::Slice(x, padded_start, padded_end, strides); - }); -} std::vector ConcatVectors(absl::Span xs, absl::Span ys) { @@ -152,100 +122,4 @@ std::vector ConcatVectors(absl::Span xs, return output; } -xla::XlaOp DynamicSliceInMinorDims(xla::XlaOp x, - absl::Span starts, - absl::Span sizes) { - xla::XlaBuilder* builder = x.builder(); - return builder->ReportErrorOrReturn([&]() -> xla::StatusOr { - TF_ASSIGN_OR_RETURN(xla::Shape shape, builder->GetShape(x)); - const int64 n_dims = xla::ShapeUtil::Rank(shape); - int64 n_minor_dims = starts.size(); - TF_RET_CHECK(n_minor_dims == sizes.size()); - TF_RET_CHECK(n_minor_dims <= n_dims); - auto major_dims = xla::AsInt64Slice(shape.dimensions()) - .subspan( - /*pos=*/0, - /*len=*/n_dims - sizes.size()); - auto padded_starts = PrependZerosInMajorDims(x, starts); - auto padded_sizes = ConcatVectors(major_dims, sizes); - return xla::DynamicSlice(x, padded_starts, padded_sizes); - }); -} - -xla::XlaOp UpdateSlice(xla::XlaOp x, xla::XlaOp update, - absl::Span start) { - xla::XlaBuilder* builder = x.builder(); - return builder->ReportErrorOrReturn([&]() -> xla::StatusOr { - // TODO(phawkins): make int64 work on all backends, remove the int32 cast. - std::vector start_as_int32(start.begin(), start.end()); - auto start_constant = xla::ConstantR1(builder, start_as_int32); - TF_ASSIGN_OR_RETURN(xla::Shape shape, builder->GetShape(x)); - const int64 n_dims = xla::ShapeUtil::Rank(shape); - TF_ASSIGN_OR_RETURN(xla::Shape start_constant_shape, - builder->GetShape(start_constant)); - const int64 start_length = - xla::ShapeUtil::GetDimension(start_constant_shape, -1); - TF_RET_CHECK(start_length == n_dims); - return xla::DynamicUpdateSlice(x, update, start_constant); - }); -} - -xla::XlaOp UpdateSliceInMinorDims(xla::XlaOp x, xla::XlaOp update, - absl::Span start) { - xla::XlaBuilder* builder = x.builder(); - return builder->ReportErrorOrReturn([&]() -> xla::StatusOr { - TF_ASSIGN_OR_RETURN(xla::Shape shape, builder->GetShape(x)); - const int64 n_dims = xla::ShapeUtil::Rank(shape); - const int64 n_minor_dims = start.size(); - TF_RET_CHECK(n_minor_dims <= n_dims); - std::vector padded_start(n_dims, 0); - std::copy(start.begin(), start.end(), - padded_start.begin() + (n_dims - n_minor_dims)); - return UpdateSlice(x, update, padded_start); - }); -} - -xla::XlaOp DynamicUpdateSliceInMinorDims(xla::XlaOp x, xla::XlaOp update, - absl::Span starts) { - auto padded_starts = PrependZerosInMajorDims(x, starts); - return xla::DynamicUpdateSlice(x, update, padded_starts); -} - -xla::XlaOp PrependZerosInMajorDims(xla::XlaOp x, - absl::Span starts) { - xla::XlaBuilder* builder = x.builder(); - return builder->ReportErrorOrReturn([&]() -> xla::StatusOr { - TF_ASSIGN_OR_RETURN(xla::Shape shape, builder->GetShape(x)); - const int64 n_dims = xla::ShapeUtil::Rank(shape); - auto zero = xla::Reshape(xla::ConstantR0(builder, 0), {1}); - std::vector padded_starts(n_dims, zero); - for (int i = 0; i < starts.size(); ++i) { - padded_starts[n_dims - starts.size() + i] = xla::Reshape(starts[i], {1}); - } - return xla::ConcatInDim(builder, padded_starts, 0); - }); -} - -xla::XlaOp TransposeInMinorDims(xla::XlaOp x) { - xla::XlaBuilder* builder = x.builder(); - return builder->ReportErrorOrReturn([&]() -> xla::StatusOr { - TF_ASSIGN_OR_RETURN(xla::Shape shape, builder->GetShape(x)); - const int64 n_dims = xla::ShapeUtil::Rank(shape); - TF_RET_CHECK(n_dims >= 2); - std::vector permutation(n_dims); - std::iota(permutation.begin(), permutation.end(), 0); - std::swap(permutation[n_dims - 1], permutation[n_dims - 2]); - return xla::Transpose(x, permutation); - }); -} - -xla::XlaOp MaybeConjugate(xla::XlaOp x, bool conjugate) { - xla::XlaBuilder* builder = x.builder(); - return builder->ReportErrorOrReturn([&]() -> xla::StatusOr { - TF_ASSIGN_OR_RETURN(xla::Shape shape, builder->GetShape(x)); - auto perform_conj = shape.element_type() == xla::C64 && conjugate; - return perform_conj ? xla::Conj(x) : x; - }); -} - } // namespace tensorflow diff --git a/tensorflow/compiler/tf2xla/lib/util.h b/tensorflow/compiler/tf2xla/lib/util.h index 80e9e5b002..aec8061cb4 100644 --- a/tensorflow/compiler/tf2xla/lib/util.h +++ b/tensorflow/compiler/tf2xla/lib/util.h @@ -38,44 +38,10 @@ xla::XlaOp PrependZerosInMajorDims(xla::XlaOp x, xla::XlaOp IntegerLiteral(xla::XlaBuilder* builder, xla::PrimitiveType type, int64 value); -// Builds a vector of zeros of length rank(x) with the last values being -// those in `starts`. -xla::XlaOp PrependZerosInMajorDims(xla::XlaOp x, - absl::Span starts); - -// Performs a slice in the minor dimensions of a Tensor. -xla::XlaOp SliceInMinorDims(xla::XlaOp x, absl::Span start, - absl::Span end); - // Returns the concatenation of `xs` and `ys`. std::vector ConcatVectors(absl::Span xs, absl::Span ys); -// Performs a dynamic slice in the minor dimensions of a Tensor. -xla::XlaOp DynamicSliceInMinorDims(xla::XlaOp x, - absl::Span starts, - absl::Span sizes); - -// Updates a slice of 'x', i.e., -// x[start[0], ..., start[n]] = update -xla::XlaOp UpdateSlice(xla::XlaOp x, xla::XlaOp update, - absl::Span start); - -// Updates a slice of 'x', where 'start' contains a list of minor dimensions: -// x[..., start[0], ..., start[n]] = update -xla::XlaOp UpdateSliceInMinorDims(xla::XlaOp x, xla::XlaOp update, - absl::Span start); - -xla::XlaOp DynamicUpdateSliceInMinorDims(xla::XlaOp x, xla::XlaOp update, - absl::Span starts); - -// Transposes a stack of matrices `x` by swapping the last two dimensions. -xla::XlaOp TransposeInMinorDims(xla::XlaOp x); - -// Applies a complex conjugation operation if `a` is complex and `conjugate_a` -// is true, otherwise returns its argument. -xla::XlaOp MaybeConjugate(xla::XlaOp x, bool conjugate); - } // namespace tensorflow #endif // TENSORFLOW_COMPILER_TF2XLA_LIB_UTIL_H_ diff --git a/tensorflow/compiler/tf2xla/python/BUILD b/tensorflow/compiler/tf2xla/python/BUILD index c9f486edc8..fef97b98c3 100644 --- a/tensorflow/compiler/tf2xla/python/BUILD +++ b/tensorflow/compiler/tf2xla/python/BUILD @@ -1,11 +1,13 @@ licenses(["notice"]) # Apache 2.0 +package_group( + name = "friends", + includes = ["//tensorflow:internal"], +) + package( default_visibility = [ - "//learning/deepmind/public/wavenet/python:__subpackages__", - "//learning/deepmind/research/alphastar:__subpackages__", - "//learning/tfx:__subpackages__", - "//tensorflow:internal", + ":friends", ], ) diff --git a/tensorflow/compiler/tf2xla/shape_util.h b/tensorflow/compiler/tf2xla/shape_util.h index f7e34a5b40..0b231ea8e7 100644 --- a/tensorflow/compiler/tf2xla/shape_util.h +++ b/tensorflow/compiler/tf2xla/shape_util.h @@ -18,6 +18,7 @@ limitations under the License. #ifndef TENSORFLOW_COMPILER_TF2XLA_SHAPE_UTIL_H_ #define TENSORFLOW_COMPILER_TF2XLA_SHAPE_UTIL_H_ +#include "tensorflow/compiler/xla/shape.h" #include "tensorflow/compiler/xla/xla_data.pb.h" #include "tensorflow/core/framework/tensor_shape.h" #include "tensorflow/core/framework/types.pb.h" diff --git a/tensorflow/compiler/tf2xla/xla_compiled_cpu_function.h b/tensorflow/compiler/tf2xla/xla_compiled_cpu_function.h index 425e769346..c7341cf8b9 100644 --- a/tensorflow/compiler/tf2xla/xla_compiled_cpu_function.h +++ b/tensorflow/compiler/tf2xla/xla_compiled_cpu_function.h @@ -26,7 +26,7 @@ limitations under the License. // Forward-declare, rather than include, to reduce code size for users that // never use this functionality. namespace xla { -class ProgramShape; +class ProgramShapeProto; class HloProfilePrinterData; } @@ -84,7 +84,7 @@ class XlaCompiledCpuFunction { void set_result_names(const char** result_names) { result_names_ = result_names; } - void set_program_shape(const xla::ProgramShape* program_shape) { + void set_program_shape(const xla::ProgramShapeProto* program_shape) { program_shape_ = program_shape; } const xla::HloProfilePrinterData* hlo_profile_printer_data() const { @@ -122,7 +122,7 @@ class XlaCompiledCpuFunction { const char** result_names_ = nullptr; // [Optional] Arg and result shapes. - const xla::ProgramShape* program_shape_ = nullptr; + const xla::ProgramShapeProto* program_shape_ = nullptr; // [Optional] Profile printer data. Null if profiling is disabled. const xla::HloProfilePrinterData* hlo_profile_printer_data_ = nullptr; @@ -206,8 +206,14 @@ class XlaCompiledCpuFunction { // // Aliasing of argument and result buffers is not allowed, and results in // undefined behavior. - void set_arg_data(size_t index, void* data) { - buffer_table_[arg_index_table_[index]] = data; + void set_arg_data(size_t index, const void* data) { + // The const_cast is safe because the generated code does not write to arg + // buffers. + // + // buffer_table_ contains pointers to buffers that _will_ be written to by + // generated code so it would be misleading to make buffer_table_ a `const + // void**`. + buffer_table_[arg_index_table_[index]] = const_cast(data); } // ------------------------------ @@ -264,7 +270,7 @@ class XlaCompiledCpuFunction { // Returns the shape of the args and results. May return nullptr if the // program shape isn't available. - const xla::ProgramShape* ProgramShape() const { return program_shape_; } + const xla::ProgramShapeProto* ProgramShape() const { return program_shape_; } bool hlo_profiling_enabled() const { return hlo_profile_printer_data_ != nullptr; @@ -287,11 +293,6 @@ class XlaCompiledCpuFunction { // Argument i needs to be placed in buffer_table_[arg_index_to_temp_index_[i]] // for XLA generated code to be able to find it. - // - // For now we need to keep around the args_ array because there is code that - // depends on args() returning a void**. However, in the future we may remove - // args_ in favor of using buffer_table_ as the sole storage for the - // arguments. const int32* const arg_index_table_; // The number of incoming arguments. @@ -310,7 +311,7 @@ class XlaCompiledCpuFunction { // Optional metadata. const char** arg_names_ = nullptr; const char** result_names_ = nullptr; - const xla::ProgramShape* program_shape_ = nullptr; + const xla::ProgramShapeProto* program_shape_ = nullptr; const xla::HloProfilePrinterData* hlo_profile_printer_data_ = nullptr; }; diff --git a/tensorflow/compiler/tf2xla/xla_compiler.cc b/tensorflow/compiler/tf2xla/xla_compiler.cc index a08d030ce7..ee461a3c07 100644 --- a/tensorflow/compiler/tf2xla/xla_compiler.cc +++ b/tensorflow/compiler/tf2xla/xla_compiler.cc @@ -158,7 +158,8 @@ Status BuildComputation( xla::XlaBuilder* builder, xla::XlaComputation* computation, int* num_computation_outputs, int* num_nonconst_outputs, std::vector* outputs, - std::vector* resource_updates) { + std::vector* resource_updates, + xla::Shape* output_shape) { // Attach a common operator name as metadata. This has no semantic effect — it // merely makes the HLO graph more readable when visualized via TensorBoard, // since TensorBoard forms groups out of operators with similar names. @@ -176,6 +177,10 @@ Status BuildComputation( std::vector elems; elems.reserve(retvals.size()); + + // Keeps track of which retvals have layout to update. The first element is + // the output index, second element is the new layout. + std::vector> retval_to_update_layout; for (int i = 0; i < retvals.size(); ++i) { XlaCompiler::OutputDescription& output = (*outputs)[i]; const XlaExpression& retval = retvals[i]; @@ -202,10 +207,12 @@ Status BuildComputation( TF_ASSIGN_OR_RETURN(xla::Shape shape, shape_representation_fn( output.shape, output.type)); value = xla::Reshape(value, xla::AsInt64Slice(shape.dimensions())); + retval_to_update_layout.emplace_back(elems.size(), shape.layout()); } else if (it != retval_cores.end()) { // Apply the sharding to the output, if there is a core assignment. value = identity_op(value); } + elems.push_back(value); break; } @@ -297,6 +304,21 @@ Status BuildComputation( return computation_status.status(); } *computation = computation_status.ConsumeValueOrDie(); + + TF_ASSIGN_OR_RETURN(const auto& program_shape, + computation->GetProgramShape()); + *output_shape = program_shape.result(); + // Update the output layout to the layout of retval. + for (auto& update : retval_to_update_layout) { + if (!always_return_tuple && elems.size() == 1) { + *output_shape->mutable_layout() = update.second; + continue; + } + + xla::Shape* output_sub_shape = + xla::ShapeUtil::GetMutableSubshape(output_shape, {update.first}); + *output_sub_shape->mutable_layout() = update.second; + } return Status::OK(); } @@ -304,10 +326,10 @@ Status BuildComputation( bool XlaCompiler::Argument::operator==( const XlaCompiler::Argument& other) const { - if (std::tie(kind, resource_kind, type, name, initialized, tensor_array_size, + if (std::tie(kind, resource_kind, type, name, initialized, max_array_size, tensor_array_gradients) != std::tie(other.kind, other.resource_kind, other.type, other.name, - other.initialized, other.tensor_array_size, + other.initialized, other.max_array_size, other.tensor_array_gradients)) { return false; } @@ -337,8 +359,8 @@ string XlaCompiler::Argument::HumanString() const { string output = absl::StrCat("kind=resource", common, " resource_kind=", XlaResource::KindToString(resource_kind), " initialized=", initialized); - if (tensor_array_size >= 0) { - absl::StrAppend(&output, " tensor_array_size=", tensor_array_size); + if (max_array_size >= 0) { + absl::StrAppend(&output, " max_array_size=", max_array_size); } if (!tensor_array_gradients.empty()) { absl::StrAppend(&output, " tensor_array_gradients=", @@ -358,7 +380,7 @@ XlaCompiler::XlaCompiler(XlaCompiler::Options options) initialization_status_(Status::OK()), next_step_id_(1), device_(new XlaCompilationDevice(SessionOptions(), options_.device_type)), - device_mgr_({device_}) { + device_mgr_(absl::WrapUnique(device_)) { CHECK(!options_.device_type.type_string().empty()); if (options_.populate_resource_manager) { initialization_status_ = @@ -545,12 +567,12 @@ Status XlaCompiler::XLAShapeForArgument(const XlaCompiler::Argument& arg, return Status::OK(); } case XlaResource::kTensorArray: { - if (arg.tensor_array_size < 0) { + if (arg.max_array_size < 0) { return errors::InvalidArgument( - "Negative tensor_array_size in XLAShapeForArgument"); + "Negative max_array_size in XLAShapeForArgument"); } TensorShape shape; - shape.AddDim(arg.tensor_array_size); + shape.AddDim(arg.max_array_size); shape.AppendShape(arg.shape); TF_RETURN_IF_ERROR(TensorShapeToXLAShape(arg.type, shape, xla_shape)); @@ -562,12 +584,12 @@ Status XlaCompiler::XLAShapeForArgument(const XlaCompiler::Argument& arg, return Status::OK(); } case XlaResource::kStack: { - if (arg.tensor_array_size < 0) { + if (arg.max_array_size < 0) { return errors::InvalidArgument( - "Negative tensor_array_size in XLAShapeForArgument"); + "Negative max_array_size in XLAShapeForArgument"); } TensorShape shape; - shape.AddDim(arg.tensor_array_size); + shape.AddDim(arg.max_array_size); shape.AppendShape(arg.shape); xla::Shape buffer_shape; TF_RETURN_IF_ERROR( @@ -613,21 +635,23 @@ Status XlaCompiler::BuildArguments( const XlaCompiler::Argument& arg = args[i]; XlaExpression& arg_expression = (*arg_expressions)[i]; switch (arg.kind) { - case XlaCompiler::Argument::kResource: + case XlaCompiler::Argument::kResource: { TF_RET_CHECK(arg.resource_kind != XlaResource::kInvalid); // TODO(phawkins): this code assumes that resource arguments do not // alias. - XlaResource* resource; - TF_RETURN_IF_ERROR(context->CreateResource( - arg.resource_kind, i, arg.name, arg.type, arg.shape, xla::XlaOp(), - /*tensor_array_size=*/arg.tensor_array_size, - /*tensor_array_gradients=*/arg.tensor_array_gradients, &resource)); + XlaResource* resource = + context->AddResource(absl::make_unique( + arg.resource_kind, i, arg.name, arg.type, arg.shape, + xla::XlaOp(), + /*max_array_size=*/arg.max_array_size, + /*tensor_array_gradients=*/arg.tensor_array_gradients, + /*tensor_array_multiple_writes_aggregate=*/true)); arg_expression = XlaExpression::Resource(resource); if (arg.initialized) { input_mapping->push_back(i); } - break; + } case XlaCompiler::Argument::kParameter: case XlaCompiler::Argument::kToken: { input_mapping->push_back(i); @@ -901,9 +925,7 @@ Status XlaCompiler::CompileGraph(const XlaCompiler::CompileOptions& options, options_.device_type, name)); xla::XlaBuilder builder(name); - XlaContext* context = - new XlaContext(this, &builder, options_.allow_cpu_custom_calls, - &options_.shape_representation_fn); + XlaContext* context = new XlaContext(this, &builder); core::ScopedUnref context_unref(context); std::vector real_args(args.begin(), args.end()); @@ -988,23 +1010,12 @@ Status XlaCompiler::CompileGraph(const XlaCompiler::CompileOptions& options, options.return_updated_values_for_all_resources, options.always_return_tuple, &builder, result->computation.get(), &num_computation_outputs, &num_nonconst_outputs, &result->outputs, - &result->resource_updates)); + &result->resource_updates, &result->xla_output_shape)); VLOG(2) << "Outputs: total: " << context->retvals().size() << " nonconstant: " << num_nonconst_outputs; - - // Compute the XLA output shape, if there is a computation with non-constant - // outputs. - TF_ASSIGN_OR_RETURN(std::unique_ptr computation_shape, - client()->GetComputationShape(*result->computation)); - - result->xla_output_shape.Swap(computation_shape->mutable_result()); VLOG(2) << "XLA output shape: " - << xla::ShapeUtil::HumanString(result->xla_output_shape); - - // Tensorflow expects a major-to-minor order of results. - xla::LayoutUtil::SetToDefaultLayout(&result->xla_output_shape); - + << xla::ShapeUtil::HumanStringWithLayout(result->xla_output_shape); return Status::OK(); } diff --git a/tensorflow/compiler/tf2xla/xla_compiler.h b/tensorflow/compiler/tf2xla/xla_compiler.h index 6342612468..0d801b73a8 100644 --- a/tensorflow/compiler/tf2xla/xla_compiler.h +++ b/tensorflow/compiler/tf2xla/xla_compiler.h @@ -150,7 +150,7 @@ class XlaCompiler { // For a TensorArray or Stack resource, what is the array's declared size? // (Used for lazy initialization.) - int64 tensor_array_size = -1; + int64 max_array_size = -1; // TensorArray resource parameters are passed as (array, gradient array 0, // ..., gradient array k), where the gradient arrays are in the same order diff --git a/tensorflow/compiler/tf2xla/xla_compiler_test.cc b/tensorflow/compiler/tf2xla/xla_compiler_test.cc index aaee208f63..fe2a5f5b0c 100644 --- a/tensorflow/compiler/tf2xla/xla_compiler_test.cc +++ b/tensorflow/compiler/tf2xla/xla_compiler_test.cc @@ -20,6 +20,7 @@ limitations under the License. #include "tensorflow/cc/ops/function_ops.h" #include "tensorflow/cc/ops/resource_variable_ops.h" #include "tensorflow/cc/ops/standard_ops.h" +#include "tensorflow/compiler/tf2xla/shape_util.h" #include "tensorflow/compiler/tf2xla/side_effect_util.h" #include "tensorflow/compiler/tf2xla/type_util.h" #include "tensorflow/compiler/tf2xla/xla_op_kernel.h" @@ -649,7 +650,7 @@ TEST_F(XlaCompilerTest, CanPassTensorArraysToAndFromComputation) { args[0].initialized = true; args[0].type = DT_INT32; args[0].shape = TensorShape({}); - args[0].tensor_array_size = 2; + args[0].max_array_size = 2; args[0].tensor_array_gradients = {"grad2"}; // Compiles the graph. @@ -708,7 +709,7 @@ TEST_F(XlaCompilerTest, UnwrittenTensorArrayGradientsAreNotComputationOutputs) { args[0].initialized = true; args[0].type = DT_INT32; args[0].shape = TensorShape({}); - args[0].tensor_array_size = 2; + args[0].max_array_size = 2; args[0].tensor_array_gradients = {"grad1"}; // Compiles the graph. @@ -740,7 +741,7 @@ TEST_F(XlaCompilerTest, NewTensorArrayGradientsAreComputationOutputs) { args[0].initialized = true; args[0].type = DT_INT32; args[0].shape = TensorShape({}); - args[0].tensor_array_size = 2; + args[0].max_array_size = 2; args[0].tensor_array_gradients = {"grad1"}; // Compiles the graph. @@ -910,6 +911,82 @@ TEST_F(XlaCompilerTest, Variables) { RunAndCheckVariablesComputation(client_, result); } +TEST_F(XlaCompilerTest, ResultLayoutSingle) { + Scope scope = Scope::NewRootScope().ExitOnError(); + auto a = ops::_Arg(scope.WithOpName("A"), DT_INT32, 0); + auto b = ops::_Retval(scope.WithOpName("RET"), a, 0); + + std::unique_ptr graph(new Graph(OpRegistry::Global())); + TF_ASSERT_OK(scope.ToGraph(graph.get())); + + // Builds a description of the arguments. + std::vector args(1); + args[0].kind = XlaCompiler::Argument::kParameter; + args[0].type = DT_INT32; + args[0].shape = TensorShape({2, 3}); + + auto options = DefaultOptions(); + // Sets the representation function to return a non-default layout. + options.shape_representation_fn = + [](const TensorShape& shape, DataType type) -> xla::StatusOr { + xla::Shape xla_shape; + TF_RETURN_IF_ERROR(TensorShapeToXLAShape(type, shape, &xla_shape)); + *xla_shape.mutable_layout() = xla::LayoutUtil::MakeLayout({0, 1}); + return xla_shape; + }; + + // Compiles the graph. + XlaCompiler compiler(options); + + XlaCompiler::CompilationResult result; + auto compile_options = XlaCompiler::CompileOptions(); + compile_options.always_return_tuple = false; + TF_ASSERT_OK(compiler.CompileGraph(compile_options, "id", std::move(graph), + args, &result)); + EXPECT_TRUE(xla::ShapeUtil::Equal( + result.xla_output_shape, + xla::ShapeUtil::MakeShapeWithLayout(xla::S32, {2, 3}, {0, 1}))); +} + +TEST_F(XlaCompilerTest, ResultLayoutMultiple) { + Scope scope = Scope::NewRootScope().ExitOnError(); + auto a = ops::_Arg(scope.WithOpName("A"), DT_INT32, 0); + auto b = ops::_Retval(scope.WithOpName("RET1"), a, 0); + auto c = ops::_Retval(scope.WithOpName("RET2"), a, 1); + + std::unique_ptr graph(new Graph(OpRegistry::Global())); + TF_ASSERT_OK(scope.ToGraph(graph.get())); + + // Builds a description of the arguments. + std::vector args(1); + args[0].kind = XlaCompiler::Argument::kParameter; + args[0].type = DT_INT32; + args[0].shape = TensorShape({2, 3}); + + auto options = DefaultOptions(); + // Sets the representation function to return a non-default layout. + options.shape_representation_fn = + [](const TensorShape& shape, DataType type) -> xla::StatusOr { + xla::Shape xla_shape; + TF_RETURN_IF_ERROR(TensorShapeToXLAShape(type, shape, &xla_shape)); + *xla_shape.mutable_layout() = xla::LayoutUtil::MakeLayout({0, 1}); + return xla_shape; + }; + + // Compiles the graph. + XlaCompiler compiler(options); + + XlaCompiler::CompilationResult result; + TF_ASSERT_OK(compiler.CompileGraph(XlaCompiler::CompileOptions(), "id", + std::move(graph), args, &result)); + xla::Shape result_shape = + xla::ShapeUtil::MakeShapeWithLayout(xla::S32, {2, 3}, {0, 1}); + + EXPECT_TRUE(xla::ShapeUtil::Equal( + result.xla_output_shape, + xla::ShapeUtil::MakeTupleShape({result_shape, result_shape}))); +} + // Tests a simple graph that reads and writes a variable. TEST_F(XlaCompilerTest, ReturnResourceHandleOnly) { Scope scope = Scope::NewRootScope().ExitOnError(); diff --git a/tensorflow/compiler/tf2xla/xla_context.cc b/tensorflow/compiler/tf2xla/xla_context.cc index 43095fbb47..a69af70503 100644 --- a/tensorflow/compiler/tf2xla/xla_context.cc +++ b/tensorflow/compiler/tf2xla/xla_context.cc @@ -54,25 +54,14 @@ const char XlaContext::kXlaContextResourceName[] = "_xla_context"; return *context; } -/* static */ XlaContext& XlaContext::Get(const XlaOpKernelContext* ctx) { - return Get(ctx->op_kernel_context()); -} - void XlaContext::set_args(std::vector args) { args_ = std::move(args); } -XlaContext::XlaContext( - XlaCompiler* compiler, xla::XlaBuilder* builder, - bool allow_cpu_custom_calls, - const std::function( - const TensorShape&, DataType)>* shape_representation_fn) - : compiler_(compiler), - builder_(builder), - allow_cpu_custom_calls_(allow_cpu_custom_calls), - shape_representation_fn_(shape_representation_fn) {} +XlaContext::XlaContext(XlaCompiler* compiler, xla::XlaBuilder* builder) + : compiler_(compiler), builder_(builder) {} -string XlaContext::DebugString() { return "TLA JIT context"; } +string XlaContext::DebugString() { return "XLA JIT context"; } void XlaContext::SetRetval(int index, const XlaExpression& expression) { if (retvals_.size() <= index) { @@ -81,21 +70,9 @@ void XlaContext::SetRetval(int index, const XlaExpression& expression) { retvals_[index] = expression; } -Status XlaContext::CreateResource( - XlaResource::Kind kind, int arg_num, string name, DataType type, - TensorShape shape, const xla::XlaOp& handle, int64 tensor_array_size, - const std::set& tensor_array_gradients, XlaResource** resource) { - resources_.emplace_back( - new XlaResource(kind, arg_num, std::move(name), type, std::move(shape), - handle, tensor_array_size, tensor_array_gradients, - /*tensor_array_multiple_writes_aggregate=*/false)); - *resource = resources_.back().get(); - return Status::OK(); -} - -xla::StatusOr XlaContext::RepresentationShape( - const TensorShape& shape, DataType type) const { - return (*shape_representation_fn_)(shape, type); +XlaResource* XlaContext::AddResource(std::unique_ptr resource) { + resources_.push_back(std::move(resource)); + return resources_.back().get(); } const xla::XlaComputation* XlaContext::GetOrCreateMax(const DataType type) { diff --git a/tensorflow/compiler/tf2xla/xla_context.h b/tensorflow/compiler/tf2xla/xla_context.h index dbfd344c9b..0767d1faac 100644 --- a/tensorflow/compiler/tf2xla/xla_context.h +++ b/tensorflow/compiler/tf2xla/xla_context.h @@ -41,14 +41,10 @@ class XlaContext : public ResourceBase { public: // Retrieves the XlaContext of the current compilation. static XlaContext& Get(const OpKernelContext* ctx); - static XlaContext& Get(const XlaOpKernelContext* ctx); // Creates a new XlaContext. See the documentation on the class data fields // for descriptions of the arguments. - XlaContext(XlaCompiler* compiler, xla::XlaBuilder* builder, - bool allow_cpu_custom_calls, - const std::function( - const TensorShape&, DataType)>* shape_representation_fn); + XlaContext(XlaCompiler* compiler, xla::XlaBuilder* builder); // Virtual method defined by ResourceBase. string DebugString() override; @@ -58,8 +54,6 @@ class XlaContext : public ResourceBase { // Returns the XlaBuilder that Ops use for compiling new expressions. xla::XlaBuilder* builder() { return builder_; } - bool allow_cpu_custom_calls() const { return allow_cpu_custom_calls_; } - const std::vector& args() const { return args_; } void set_args(std::vector args); @@ -70,25 +64,13 @@ class XlaContext : public ResourceBase { // grows the return values vector to size index+1 if it is smaller. void SetRetval(int index, const XlaExpression& expression); - // Creates a resource with resource `kind` and initial value `handle`. `name` - // is a descriptive name for use in error messages. See the `XlaResource` - // constructor for a description of the remaining arguments. - // Fails if the resource already exists. - Status CreateResource(XlaResource::Kind kind, int arg_num, string name, - DataType type, TensorShape shape, - const xla::XlaOp& handle, int64 tensor_array_size, - const std::set& tensor_array_gradients, - XlaResource** resource); + // Adds 'resource' to the set of resources owned by the context. + XlaResource* AddResource(std::unique_ptr resource); const std::vector>& resources() { return resources_; } - // Returns the XLA shape to be used to represent a variable of TF `shape` - // and `type`, or of an argument or return value of a top-level computation. - xla::StatusOr RepresentationShape(const TensorShape& shape, - DataType type) const; - // Get an XLA lambda to compute Max. This is cached in the // XlaContext since it may be used by multiple Ops. There is a // separate specialization of the computation for each DataType. @@ -118,9 +100,6 @@ class XlaContext : public ResourceBase { // The XlaBuilder used to construct the subgraph's compiled representation. xla::XlaBuilder* builder_; - // Allow ops to emit CustomCall operations for CPU. - const bool allow_cpu_custom_calls_; - // Arguments to the Tensorflow graph, indexed by _Arg index. // Includes both compile-time constant arguments and runtime parameters. std::vector args_; @@ -131,11 +110,6 @@ class XlaContext : public ResourceBase { // Holds ownership of resources. The resources are not ordered. std::vector> resources_; - // Describes the on-host shapes of parameters and return values. Also see: - // XlaDevice::Options::shape_representation_fn. - const std::function(const TensorShape&, DataType)>* - shape_representation_fn_; - // Cache of prebuilt computations indexed by their type. using ComputationMap = std::map; diff --git a/tensorflow/compiler/tf2xla/xla_helpers.cc b/tensorflow/compiler/tf2xla/xla_helpers.cc index 9a34cd8c6a..c2c0751211 100644 --- a/tensorflow/compiler/tf2xla/xla_helpers.cc +++ b/tensorflow/compiler/tf2xla/xla_helpers.cc @@ -26,7 +26,6 @@ limitations under the License. #include "tensorflow/compiler/tf2xla/xla_op_kernel.h" #include "tensorflow/compiler/xla/client/lib/arithmetic.h" #include "tensorflow/compiler/xla/client/lib/constants.h" -#include "tensorflow/compiler/xla/client/lib/numeric.h" #include "tensorflow/compiler/xla/client/xla_builder.h" #include "tensorflow/compiler/xla/client/xla_computation.h" #include "tensorflow/compiler/xla/types.h" @@ -216,8 +215,7 @@ DataType XlaHelpers::SumAccumulationType(const DataType& dtype) { return dtype; } -xla::XlaOp XlaHelpers::ConvertElementType(xla::XlaBuilder* const builder, - const xla::XlaOp& operand, +xla::XlaOp XlaHelpers::ConvertElementType(const xla::XlaOp& operand, const DataType new_element_type) { xla::PrimitiveType convert_to; TF_CHECK_OK(DataTypeToPrimitiveType(new_element_type, &convert_to)); diff --git a/tensorflow/compiler/tf2xla/xla_helpers.h b/tensorflow/compiler/tf2xla/xla_helpers.h index 39578144ca..4858dfee55 100644 --- a/tensorflow/compiler/tf2xla/xla_helpers.h +++ b/tensorflow/compiler/tf2xla/xla_helpers.h @@ -80,8 +80,7 @@ class XlaHelpers { // A helper for creating a ConvertElementType xla op given a DataType rather // than the xla::PrimitiveType. - static xla::XlaOp ConvertElementType(xla::XlaBuilder* const builder, - const xla::XlaOp& operand, + static xla::XlaOp ConvertElementType(const xla::XlaOp& operand, const DataType new_element_type); }; diff --git a/tensorflow/compiler/tf2xla/xla_jit_compiled_cpu_function.cc b/tensorflow/compiler/tf2xla/xla_jit_compiled_cpu_function.cc index 86a78ee429..fabbcd04fe 100644 --- a/tensorflow/compiler/tf2xla/xla_jit_compiled_cpu_function.cc +++ b/tensorflow/compiler/tf2xla/xla_jit_compiled_cpu_function.cc @@ -133,7 +133,8 @@ XlaJitCompiledCpuFunction::Compile( jit->executable_ = std::move(executable); jit->buffer_infos_ = std::move(buffer_infos); jit->arg_index_table_ = std::move(arg_index_table); - jit->program_shape_ = std::move(program_shape); + jit->program_shape_ = + absl::make_unique(program_shape->ToProto()); jit->static_data_.set_raw_function(raw_function); jit->static_data_.set_buffer_infos(jit->buffer_infos_.data()); jit->static_data_.set_num_buffers(jit->buffer_infos_.size()); diff --git a/tensorflow/compiler/tf2xla/xla_jit_compiled_cpu_function.h b/tensorflow/compiler/tf2xla/xla_jit_compiled_cpu_function.h index d3c8f22a80..a539205717 100644 --- a/tensorflow/compiler/tf2xla/xla_jit_compiled_cpu_function.h +++ b/tensorflow/compiler/tf2xla/xla_jit_compiled_cpu_function.h @@ -80,8 +80,10 @@ class XlaJitCompiledCpuFunction { std::vector arg_names_; std::vector result_names_; - // The backing data for the program shape. - std::unique_ptr program_shape_; + // The backing data for the program shape. The proto form of program shape is + // used because the program shape is serialized and embedded in the object + // file. + std::unique_ptr program_shape_; }; } // namespace tensorflow diff --git a/tensorflow/compiler/tf2xla/xla_jit_compiled_cpu_function_test.cc b/tensorflow/compiler/tf2xla/xla_jit_compiled_cpu_function_test.cc index 6d49298a6f..8846088678 100644 --- a/tensorflow/compiler/tf2xla/xla_jit_compiled_cpu_function_test.cc +++ b/tensorflow/compiler/tf2xla/xla_jit_compiled_cpu_function_test.cc @@ -116,13 +116,13 @@ TEST(XlaJitCompiledCpuFunction, Sum) { // Check program shape. using xla::ShapeUtil; const xla::Shape s32 = ShapeUtil::MakeShape(xla::S32, {}); - const xla::ProgramShape* program_shape = function.ProgramShape(); - ASSERT_TRUE(program_shape != nullptr); - ASSERT_EQ(program_shape->parameters_size(), 2); - EXPECT_TRUE(ShapeUtil::Compatible(program_shape->parameters(0), s32)); - EXPECT_TRUE(ShapeUtil::Compatible(program_shape->parameters(1), s32)); + ASSERT_TRUE(function.ProgramShape() != nullptr); + const xla::ProgramShape program_shape(*function.ProgramShape()); + ASSERT_EQ(program_shape.parameters_size(), 2); + EXPECT_TRUE(ShapeUtil::Compatible(program_shape.parameters(0), s32)); + EXPECT_TRUE(ShapeUtil::Compatible(program_shape.parameters(1), s32)); - const xla::Shape& result = program_shape->result(); + const xla::Shape& result = program_shape.result(); ASSERT_EQ(result.element_type(), xla::TUPLE); ASSERT_EQ(ShapeUtil::TupleElementCount(result), 1); const xla::Shape& result0 = ShapeUtil::GetTupleElementShape(result, 0); diff --git a/tensorflow/compiler/tf2xla/xla_op_kernel.cc b/tensorflow/compiler/tf2xla/xla_op_kernel.cc index 8dd8def054..58808c76de 100644 --- a/tensorflow/compiler/tf2xla/xla_op_kernel.cc +++ b/tensorflow/compiler/tf2xla/xla_op_kernel.cc @@ -36,8 +36,16 @@ bool XlaOpKernelContext::ValidateInputsAreSameShape(OpKernel* op) { return context_->ValidateInputsAreSameShape(op); } +XlaContext* XlaOpKernelContext::xla_context() const { + return &XlaContext::Get(context_); +} + xla::XlaBuilder* XlaOpKernelContext::builder() const { - return XlaContext::Get(this).builder(); + return xla_context()->builder(); +} + +XlaCompiler* XlaOpKernelContext::compiler() const { + return xla_context()->compiler(); } // Retrieves an XlaExpression that was allocated by a previous Op. @@ -338,8 +346,8 @@ Status XlaOpKernelContext::ConstantInputList( namespace { Status ReadVariableInputTensor(const Tensor& tensor, DataType type, - const OpKernelContext* ctx, TensorShape* shape, - xla::XlaOp* value) { + const XlaOpKernelContext* ctx, + TensorShape* shape, xla::XlaOp* value) { const XlaExpression* expression = CastExpressionFromTensor(tensor); XlaResource* variable = expression->resource(); TF_RET_CHECK(variable != nullptr); @@ -357,10 +365,9 @@ Status ReadVariableInputTensor(const Tensor& tensor, DataType type, *shape = variable->shape(); } - XlaContext& xla_context = XlaContext::Get(ctx); - TF_ASSIGN_OR_RETURN( - xla::Shape representation_shape, - xla_context.RepresentationShape(variable->shape(), variable->type())); + TF_ASSIGN_OR_RETURN(xla::Shape representation_shape, + ctx->compiler()->options().shape_representation_fn( + variable->shape(), variable->type())); xla::Shape xla_shape; TF_RETURN_IF_ERROR( TensorShapeToXLAShape(variable->type(), variable->shape(), &xla_shape)); @@ -377,15 +384,15 @@ Status ReadVariableInputTensor(const Tensor& tensor, DataType type, Status XlaOpKernelContext::ReadVariableInput(int index, DataType type, TensorShape* shape, xla::XlaOp* value) { - return ReadVariableInputTensor(context_->input(index), type, context_, shape, + return ReadVariableInputTensor(context_->input(index), type, this, shape, value); } Status XlaOpKernelContext::ReadVariableInput(absl::string_view name, DataType type, TensorShape* shape, xla::XlaOp* value) { - return ReadVariableInputTensor(GetInputTensorByName(name), type, context_, - shape, value); + return ReadVariableInputTensor(GetInputTensorByName(name), type, this, shape, + value); } Status XlaOpKernelContext::GetVariableTypeAndShape(int index, DataType* type, @@ -464,7 +471,7 @@ Status XlaOpKernelContext::GetResourceInput(int index, XlaResource** resource) { namespace { Status AssignVariableTensor(const Tensor& tensor, DataType type, - const OpKernelContext* ctx, xla::XlaOp handle, + const XlaOpKernelContext* ctx, xla::XlaOp handle, xla::XlaBuilder* builder) { const XlaExpression* expression = CastExpressionFromTensor(tensor); XlaResource* variable = expression->resource(); @@ -481,9 +488,9 @@ Status AssignVariableTensor(const Tensor& tensor, DataType type, TF_RETURN_IF_ERROR(variable->SetTypeAndShape(type, shape)); - XlaContext& xla_context = XlaContext::Get(ctx); - TF_ASSIGN_OR_RETURN(xla::Shape representation_shape, - xla_context.RepresentationShape(shape, type)); + TF_ASSIGN_OR_RETURN( + xla::Shape representation_shape, + ctx->compiler()->options().shape_representation_fn(shape, type)); xla::Shape xla_shape; TF_RETURN_IF_ERROR(TensorShapeToXLAShape(type, shape, &xla_shape)); if (!xla::ShapeUtil::Compatible(xla_shape, representation_shape)) { @@ -498,19 +505,15 @@ Status AssignVariableTensor(const Tensor& tensor, DataType type, Status XlaOpKernelContext::AssignVariable(int input_index, DataType type, xla::XlaOp handle) { TF_RET_CHECK(handle.valid()); - return AssignVariableTensor(context_->input(input_index), type, context_, - handle, builder()); + return AssignVariableTensor(context_->input(input_index), type, this, handle, + builder()); } Status XlaOpKernelContext::AssignVariable(absl::string_view name, DataType type, xla::XlaOp handle) { TF_RET_CHECK(handle.valid()); - return AssignVariableTensor(GetInputTensorByName(name), type, context_, - handle, builder()); -} - -XlaCompiler* XlaOpKernelContext::compiler() const { - return XlaContext::Get(context_).compiler(); + return AssignVariableTensor(GetInputTensorByName(name), type, this, handle, + builder()); } void XlaOpKernelContext::CtxFailure(const Status& s) { @@ -530,22 +533,22 @@ void XlaOpKernelContext::CtxFailureWithWarning(const char* file, int line, const xla::XlaComputation* XlaOpKernelContext::GetOrCreateMax( const DataType type) { - return XlaContext::Get(context_).GetOrCreateMax(type); + return xla_context()->GetOrCreateMax(type); } const xla::XlaComputation* XlaOpKernelContext::GetOrCreateMin( const DataType type) { - return XlaContext::Get(context_).GetOrCreateMin(type); + return xla_context()->GetOrCreateMin(type); } const xla::XlaComputation* XlaOpKernelContext::GetOrCreateAdd( const DataType type) { - return XlaContext::Get(context_).GetOrCreateAdd(type); + return xla_context()->GetOrCreateAdd(type); } const xla::XlaComputation* XlaOpKernelContext::GetOrCreateMul( const DataType type) { - return XlaContext::Get(context_).GetOrCreateMul(type); + return xla_context()->GetOrCreateMul(type); } const Tensor& XlaOpKernelContext::GetInputTensorByName(absl::string_view name) { diff --git a/tensorflow/compiler/tf2xla/xla_op_kernel.h b/tensorflow/compiler/tf2xla/xla_op_kernel.h index c06efa2c47..1858844bc0 100644 --- a/tensorflow/compiler/tf2xla/xla_op_kernel.h +++ b/tensorflow/compiler/tf2xla/xla_op_kernel.h @@ -60,6 +60,8 @@ class XlaOpKernelContext { public: explicit XlaOpKernelContext(OpKernelContext* context); + XlaContext* xla_context() const; + // Returns the XLA XlaBuilder containing the output of compilation. xla::XlaBuilder* builder() const; diff --git a/tensorflow/compiler/tf2xla/xla_op_registry.cc b/tensorflow/compiler/tf2xla/xla_op_registry.cc index dcd0e9c5c1..14237df690 100644 --- a/tensorflow/compiler/tf2xla/xla_op_registry.cc +++ b/tensorflow/compiler/tf2xla/xla_op_registry.cc @@ -18,7 +18,7 @@ limitations under the License. #include #include -#include "tensorflow/compiler/jit/legacy_flags/mark_for_compilation_pass_flags.h" +#include "tensorflow/compiler/jit/flags.h" #include "tensorflow/compiler/jit/xla_cluster_util.h" #include "tensorflow/compiler/tf2xla/type_util.h" #include "tensorflow/compiler/tf2xla/xla_context.h" @@ -130,8 +130,7 @@ XlaOpRegistry::~XlaOpRegistry() = default; // Lazily register the CPU and GPU JIT devices the first time // GetCompilationDevice is called. static void* registration_init = [®istry]() { - legacy_flags::MarkForCompilationPassFlags* flags = - legacy_flags::GetMarkForCompilationPassFlags(); + MarkForCompilationPassFlags* flags = GetMarkForCompilationPassFlags(); bool cpu_global_jit = flags->tf_xla_cpu_global_jit; mutex_lock lock(registry.mutex_); diff --git a/tensorflow/compiler/tf2xla/xla_resource.cc b/tensorflow/compiler/tf2xla/xla_resource.cc index a322eb9015..48a3c01272 100644 --- a/tensorflow/compiler/tf2xla/xla_resource.cc +++ b/tensorflow/compiler/tf2xla/xla_resource.cc @@ -18,6 +18,7 @@ limitations under the License. #include #include +#include "absl/memory/memory.h" #include "tensorflow/compiler/tf2xla/shape_util.h" #include "tensorflow/compiler/tf2xla/sharding_util.h" #include "tensorflow/compiler/tf2xla/xla_context.h" @@ -39,9 +40,29 @@ namespace tensorflow { } } +/*static*/ std::unique_ptr XlaResource::CreateStack( + string name, DataType type, int64 max_size) { + return absl::make_unique( + XlaResource::kStack, /*arg_num=*/-1, std::move(name), type, TensorShape(), + /*initial_value=*/xla::XlaOp(), + /*max_array_size=*/max_size, + /*tensor_array_gradients=*/std::set{}, + /*tensor_array_multiple_writes_aggregate=*/false); +} + +/*static*/ std::unique_ptr XlaResource::CreateTensorArray( + string name, DataType type, TensorShape shape, xla::XlaOp initial_value, + int64 max_array_size) { + return absl::make_unique( + XlaResource::kTensorArray, /*arg_num=*/-1, std::move(name), type, shape, + initial_value, max_array_size, + /*tensor_array_gradients=*/std::set{}, + /*tensor_array_multiple_writes_aggregate=*/false); +} + XlaResource::XlaResource(Kind kind, int arg_num, string name, DataType type, TensorShape shape, const xla::XlaOp& initial_value, - int64 tensor_array_size, + int64 max_array_size, const std::set& tensor_array_gradients, bool tensor_array_multiple_writes_aggregate) : kind_(kind), @@ -51,7 +72,7 @@ XlaResource::XlaResource(Kind kind, int arg_num, string name, DataType type, shape_(std::move(shape)), value_(initial_value), initial_value_(initial_value), - tensor_array_size_(tensor_array_size), + max_array_size_(max_array_size), tensor_array_multiple_writes_aggregate_( tensor_array_multiple_writes_aggregate) { CHECK(kind_ != kInvalid); @@ -60,7 +81,7 @@ XlaResource::XlaResource(Kind kind, int arg_num, string name, DataType type, tensor_array_gradients_[gradient].reset(new XlaResource( /*kind=*/kTensorArray, /*arg_num=*/-1, /*name=*/absl::StrCat("TensorArrayGrad: ", name_), type_, shape_, - xla::XlaOp(), tensor_array_size_, /*tensor_array_gradients=*/{}, + xla::XlaOp(), max_array_size_, /*tensor_array_gradients=*/{}, /*tensor_array_multiple_writes_aggregate=*/true)); } } @@ -113,7 +134,7 @@ Status XlaResource::SetZeroValue(xla::XlaBuilder* builder) { } case kTensorArray: { TensorShape ta_shape; - ta_shape.AddDim(tensor_array_size_); + ta_shape.AddDim(max_array_size_); ta_shape.AppendShape(shape_); value_ = xla::Broadcast(XlaHelpers::Zero(builder, type_), ta_shape.dim_sizes()); @@ -121,7 +142,7 @@ Status XlaResource::SetZeroValue(xla::XlaBuilder* builder) { } case kStack: { TensorShape ta_shape; - ta_shape.AddDim(tensor_array_size_); + ta_shape.AddDim(max_array_size_); ta_shape.AppendShape(shape_); value_ = xla::Tuple(builder, {xla::Broadcast(XlaHelpers::Zero(builder, type_), @@ -146,14 +167,14 @@ Status XlaResource::GetOrCreateTensorArrayGradient(const string& source, std::unique_ptr& gradient = tensor_array_gradients_[source]; if (!gradient) { TensorShape ta_shape; - ta_shape.AddDim(tensor_array_size_); + ta_shape.AddDim(max_array_size_); ta_shape.AppendShape(shape_); xla::XlaOp gradient_value = xla::Broadcast(XlaHelpers::Zero(builder, type_), ta_shape.dim_sizes()); gradient.reset( new XlaResource(/*kind=*/kTensorArray, /*arg_num=*/-1, /*name=*/absl::StrCat("TensorArrayGrad: ", name_), - type_, shape_, gradient_value, tensor_array_size_, + type_, shape_, gradient_value, max_array_size_, /*tensor_array_gradients=*/{}, /*tensor_array_multiple_writes_aggregate=*/true)); } diff --git a/tensorflow/compiler/tf2xla/xla_resource.h b/tensorflow/compiler/tf2xla/xla_resource.h index 857b9a928b..736588bb8b 100644 --- a/tensorflow/compiler/tf2xla/xla_resource.h +++ b/tensorflow/compiler/tf2xla/xla_resource.h @@ -38,9 +38,18 @@ class XlaResource { }; static absl::string_view KindToString(Kind kind); + // Creates a new Stack resource. + static std::unique_ptr CreateStack(string name, DataType type, + int64 max_size); + + // Creates a new TensorArray resource. + static std::unique_ptr CreateTensorArray( + string name, DataType type, TensorShape shape, xla::XlaOp initial_value, + int64 max_array_size); + XlaResource(Kind kind, int arg_num, string name, DataType type, TensorShape shape, const xla::XlaOp& initial_value, - int64 tensor_array_size, + int64 max_array_size, const std::set& tensor_array_gradients, bool tensor_array_multiple_writes_aggregate); @@ -119,12 +128,12 @@ class XlaResource { // TODO(phawkins): refactor this code to use subclasses, rather than putting // kind-specific fields in XlaResource. - // 'tensor_array_size' stores the expected size of the TensorArray or Stack. + // 'max_array_size' stores the expected size of the TensorArray or Stack. // We need to store this since sometimes TensorArrays must be initialized // lazily since we do not know the element shape at construction time. // Used by both TensorArrays and Stacks. - int64 tensor_array_size() const { return tensor_array_size_; } - void set_tensor_array_size(int64 size) { tensor_array_size_ = size; } + int64 max_array_size() const { return max_array_size_; } + void set_max_array_size(int64 size) { max_array_size_ = size; } bool tensor_array_multiple_writes_aggregate() const { return tensor_array_multiple_writes_aggregate_; @@ -151,7 +160,7 @@ class XlaResource { xla::XlaOp value_; xla::XlaOp initial_value_; - int64 tensor_array_size_ = -1; + int64 max_array_size_ = -1; bool tensor_array_multiple_writes_aggregate_ = false; std::map> tensor_array_gradients_; diff --git a/tensorflow/compiler/xla/BUILD b/tensorflow/compiler/xla/BUILD index 91096cf1d0..4360e08579 100644 --- a/tensorflow/compiler/xla/BUILD +++ b/tensorflow/compiler/xla/BUILD @@ -226,12 +226,14 @@ cc_library( "index_util.cc", "layout_util.cc", "primitive_util.cc", + "shape.cc", "shape_util.cc", ], hdrs = [ "index_util.h", "layout_util.h", "primitive_util.h", + "shape.h", "shape_util.h", ], visibility = ["//visibility:public"], @@ -254,6 +256,23 @@ cc_library( ], ) +tf_cc_test( + name = "shape_test", + srcs = ["shape_test.cc"], + deps = [ + ":shape_util", + ":status_macros", + ":test", + ":test_helpers", + ":types", + ":util", + ":xla_data_proto", + "//tensorflow/core:lib", + "//tensorflow/core:test_main", + "@com_google_absl//absl/strings", + ], +) + tf_cc_test( name = "shape_util_test", srcs = ["shape_util_test.cc"], @@ -745,6 +764,8 @@ cc_library( "//tensorflow/core:framework_internal", "//tensorflow/core:lib", "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:str_format", + "@com_google_absl//absl/types:span", ], ) diff --git a/tensorflow/compiler/xla/array2d.h b/tensorflow/compiler/xla/array2d.h index 782c966b4c..e4aca98f67 100644 --- a/tensorflow/compiler/xla/array2d.h +++ b/tensorflow/compiler/xla/array2d.h @@ -104,7 +104,7 @@ std::unique_ptr> MakeLinspaceArray2D(double from, double to, int64 count = n1 * n2; NativeT step = static_cast((count > 1) ? (to - from) / (count - 1) : 0); - auto set = [&array, n1, n2](int64 index, NativeT value) { + auto set = [&array, n2](int64 index, NativeT value) { (*array)(index / n2, index % n2) = value; }; for (int64 i = 0; i < count - 1; ++i) { diff --git a/tensorflow/compiler/xla/client/BUILD b/tensorflow/compiler/xla/client/BUILD index 42da0ebf49..fe99564d3c 100644 --- a/tensorflow/compiler/xla/client/BUILD +++ b/tensorflow/compiler/xla/client/BUILD @@ -81,6 +81,7 @@ cc_library( "//tensorflow/core:lib", "@com_google_absl//absl/memory", "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:optional", "@com_google_absl//absl/types:span", ], ) @@ -90,11 +91,12 @@ cc_library( srcs = ["executable_build_options.cc"], hdrs = ["executable_build_options.h"], deps = [ + "//tensorflow/compiler/xla:debug_options_flags", "//tensorflow/compiler/xla:shape_util", "//tensorflow/compiler/xla:util", "//tensorflow/compiler/xla:xla_data_proto", + "//tensorflow/compiler/xla:xla_proto", "//tensorflow/compiler/xla/service:device_memory_allocator", - "//tensorflow/core:lib", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", "@com_google_absl//absl/types:optional", @@ -191,6 +193,7 @@ cc_library( hdrs = ["xla_computation.h"], visibility = ["//visibility:public"], deps = [ + "//tensorflow/compiler/xla:shape_util", "//tensorflow/compiler/xla:status_macros", "//tensorflow/compiler/xla:util", "//tensorflow/compiler/xla:xla_data_proto", diff --git a/tensorflow/compiler/xla/client/client.cc b/tensorflow/compiler/xla/client/client.cc index eef2844e0d..74b76f9299 100644 --- a/tensorflow/compiler/xla/client/client.cc +++ b/tensorflow/compiler/xla/client/client.cc @@ -20,6 +20,7 @@ limitations under the License. #include "absl/memory/memory.h" #include "absl/strings/str_cat.h" +#include "absl/types/optional.h" #include "tensorflow/compiler/xla/client/xla_computation.h" #include "tensorflow/compiler/xla/debug_options_flags.h" #include "tensorflow/compiler/xla/execution_options_util.h" @@ -42,7 +43,7 @@ StatusOr Client::Transfer(const GlobalData& data, TransferToClientRequest request; *request.mutable_data() = data.handle(); if (shape_with_layout != nullptr) { - *request.mutable_shape_with_layout() = *shape_with_layout; + *request.mutable_shape_with_layout() = shape_with_layout->ToProto(); } TransferToClientResponse response; @@ -123,7 +124,7 @@ StatusOr Client::TransferFromOutfeed( } request.set_replica_id(replica_id); if (shape_with_layout != nullptr) { - *request.mutable_shape_with_layout() = *shape_with_layout; + *request.mutable_shape_with_layout() = shape_with_layout->ToProto(); } TransferFromOutfeedResponse response; @@ -170,11 +171,14 @@ StatusOr Client::ExecuteAndTransfer( std::unique_ptr data, Execute(computation, arguments, execution_options, execution_profile)); - const Shape* shape_with_output_layout = nullptr; + absl::optional shape_with_output_layout; if (execution_options && execution_options->has_shape_with_output_layout()) { - shape_with_output_layout = &execution_options->shape_with_output_layout(); + shape_with_output_layout = + Shape(execution_options->shape_with_output_layout()); } - return Transfer(*data, shape_with_output_layout); + return Transfer(*data, shape_with_output_layout.has_value() + ? &(*shape_with_output_layout) + : nullptr); } StatusOr Client::ComputeConstant(const XlaComputation& computation, @@ -229,7 +233,7 @@ StatusOr Client::Compile( // The argument shapes affect how the computation is compiled. for (const auto& arg_shape : argument_shapes) { - *request.add_input_shape_with_layout() = arg_shape; + *request.add_input_shape_with_layout() = arg_shape.ToProto(); } CompileResponse response; @@ -458,7 +462,7 @@ StatusOr Client::GetShape(const GlobalData& data) { return s; } - return response.shape(); + return Shape(response.shape()); } StatusOr Client::ExecutionStatsAsString( diff --git a/tensorflow/compiler/xla/client/executable_build_options.cc b/tensorflow/compiler/xla/client/executable_build_options.cc index 0f1745366b..1f594e551a 100644 --- a/tensorflow/compiler/xla/client/executable_build_options.cc +++ b/tensorflow/compiler/xla/client/executable_build_options.cc @@ -16,6 +16,7 @@ limitations under the License. #include "tensorflow/compiler/xla/client/executable_build_options.h" #include "absl/strings/str_format.h" +#include "tensorflow/compiler/xla/debug_options_flags.h" #include "tensorflow/compiler/xla/shape_util.h" namespace xla { @@ -39,6 +40,13 @@ ExecutableBuildOptions& ExecutableBuildOptions::set_device_ordinal( int ExecutableBuildOptions::device_ordinal() const { return device_ordinal_; } +DebugOptions* ExecutableBuildOptions::mutable_debug_options() { + if (!has_debug_options()) { + debug_options_ = GetDebugOptionsFromFlags(); + } + return &debug_options_.value(); +} + ExecutableBuildOptions& ExecutableBuildOptions::set_result_layout( const Shape& shape_with_layout) { result_layout_set_ = true; @@ -55,68 +63,10 @@ string ExecutableBuildOptions::ToString() const { if (result_layout_set_) { result_layout = ShapeUtil::HumanStringWithLayout(result_layout_); } - string generate_hlo_graph = "nullopt"; - if (generate_hlo_graph_.has_value()) { - generate_hlo_graph = generate_hlo_graph_.value(); - } return absl::StrFormat( "ExecutableBuildOptions{device_ordinal=%d, result_layout=%s, " "generate_hlo_graph=%s}", - device_ordinal_, result_layout, generate_hlo_graph); -} - -ExecutableBuildOptions& ExecutableBuildOptions::set_generate_hlo_graph( - string regex) { - generate_hlo_graph_ = std::move(regex); - return *this; -} - -const absl::optional& ExecutableBuildOptions::generate_hlo_graph() - const { - return generate_hlo_graph_; -} - -ExecutableBuildOptions& ExecutableBuildOptions::set_dump_optimized_hlo_proto_to( - absl::string_view dirpath) { - dump_optimized_hlo_proto_to_ = string(dirpath); - return *this; -} - -const absl::optional& -ExecutableBuildOptions::dump_optimized_hlo_proto_to() const { - return dump_optimized_hlo_proto_to_; -} - -ExecutableBuildOptions& -ExecutableBuildOptions::set_dump_unoptimized_hlo_proto_to( - absl::string_view dirpath) { - dump_unoptimized_hlo_proto_to_ = string(dirpath); - return *this; -} - -const absl::optional& -ExecutableBuildOptions::dump_unoptimized_hlo_proto_to() const { - return dump_unoptimized_hlo_proto_to_; -} - -ExecutableBuildOptions& ExecutableBuildOptions::set_dump_per_pass_hlo_proto_to( - absl::string_view dirpath) { - dump_per_pass_hlo_proto_to_ = string(dirpath); - return *this; -} - -const absl::optional& -ExecutableBuildOptions::dump_per_pass_hlo_proto_to() const { - return dump_per_pass_hlo_proto_to_; -} - -ExecutableBuildOptions& ExecutableBuildOptions::set_hlo_profile(bool enabled) { - hlo_profile_ = enabled; - return *this; -} - -absl::optional ExecutableBuildOptions::hlo_profile() const { - return hlo_profile_; + device_ordinal_, result_layout, debug_options().xla_generate_hlo_graph()); } } // namespace xla diff --git a/tensorflow/compiler/xla/client/executable_build_options.h b/tensorflow/compiler/xla/client/executable_build_options.h index 93334db88b..a58090253b 100644 --- a/tensorflow/compiler/xla/client/executable_build_options.h +++ b/tensorflow/compiler/xla/client/executable_build_options.h @@ -19,7 +19,9 @@ limitations under the License. #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "tensorflow/compiler/xla/service/device_memory_allocator.h" +#include "tensorflow/compiler/xla/shape.h" #include "tensorflow/compiler/xla/util.h" +#include "tensorflow/compiler/xla/xla.pb.h" #include "tensorflow/compiler/xla/xla_data.pb.h" namespace xla { @@ -44,6 +46,12 @@ class ExecutableBuildOptions { ExecutableBuildOptions& set_result_layout(const Shape& shape_with_layout); const Shape* result_layout() const; + // Expose access to the XLA debug options which will be passed to the + // compilation process. + bool has_debug_options() const { return debug_options_.has_value(); } + const DebugOptions& debug_options() const { return *debug_options_; } + DebugOptions* mutable_debug_options(); + // If set, this specifies an allocator that can be used to allocate temporary // space on the device during compilation. For example, the compiler might // want to run various algorithms on the device and pick the fastest one -- it @@ -55,56 +63,16 @@ class ExecutableBuildOptions { DeviceMemoryAllocator* allocator); DeviceMemoryAllocator* device_allocator() const; - // If set, specifies a regexp of HLO graphs to dump (as in DebugOptions). - ExecutableBuildOptions& set_generate_hlo_graph(string regex); - const absl::optional& generate_hlo_graph() const; - - // If set, specifies a dirpath to dump the end-of-optimization-pipeline HLO - // protobuf to (as in DebugOptions). - ExecutableBuildOptions& set_dump_optimized_hlo_proto_to( - absl::string_view dirpath); - const absl::optional& dump_optimized_hlo_proto_to() const; - - // If set, specifies a dirpath to dump the start-of-optimization-pipeline HLO - // protobuf to (as in DebugOptions). - ExecutableBuildOptions& set_dump_unoptimized_hlo_proto_to( - absl::string_view dirpath); - const absl::optional& dump_unoptimized_hlo_proto_to() const; - - // If set, specifies a dirpath to dump the per-pass-in-pipeline HLO protobufs - // to (as in DebugOptions). - ExecutableBuildOptions& set_dump_per_pass_hlo_proto_to( - absl::string_view dirpath); - const absl::optional& dump_per_pass_hlo_proto_to() const; - - // If true, specifies that we should record an HLO profile during execution - // and log it after execution (as in DebugOptions). If nullopt the default is - // used. - ExecutableBuildOptions& set_hlo_profile(bool enabled); - absl::optional hlo_profile() const; - - void add_disabled_hlo_pass(absl::string_view pass_name) { - disabled_hlo_passes_.push_back(std::string(pass_name)); - } - const absl::Span disabled_hlo_passes() const { - return disabled_hlo_passes_; - } - // Returns a string representation of the build options, suitable for // debugging. string ToString() const; private: - absl::optional hlo_profile_; int device_ordinal_ = -1; Shape result_layout_; bool result_layout_set_ = false; - absl::optional generate_hlo_graph_; - absl::optional dump_optimized_hlo_proto_to_; - absl::optional dump_unoptimized_hlo_proto_to_; - absl::optional dump_per_pass_hlo_proto_to_; + absl::optional debug_options_; DeviceMemoryAllocator* device_allocator_ = nullptr; - std::vector disabled_hlo_passes_; }; } // namespace xla diff --git a/tensorflow/compiler/xla/client/lib/BUILD b/tensorflow/compiler/xla/client/lib/BUILD index f833ddcd32..f0f530d7d7 100644 --- a/tensorflow/compiler/xla/client/lib/BUILD +++ b/tensorflow/compiler/xla/client/lib/BUILD @@ -104,13 +104,17 @@ xla_test( ) cc_library( - name = "numeric", - srcs = ["numeric.cc"], - hdrs = ["numeric.h"], + name = "matrix", + srcs = ["matrix.cc"], + hdrs = ["matrix.h"], deps = [ ":arithmetic", ":constants", + "//tensorflow/compiler/xla:shape_util", + "//tensorflow/compiler/xla:status_macros", + "//tensorflow/compiler/xla:statusor", "//tensorflow/compiler/xla:types", + "//tensorflow/compiler/xla:util", "//tensorflow/compiler/xla:xla_data_proto", "//tensorflow/compiler/xla/client:xla_builder", "@com_google_absl//absl/types:span", @@ -118,11 +122,12 @@ cc_library( ) xla_test( - name = "numeric_test", - srcs = ["numeric_test.cc"], + name = "matrix_test", + srcs = ["matrix_test.cc"], tags = ["enable_for_xla_interpreter"], deps = [ - ":numeric", + ":matrix", + ":slicing", "//tensorflow/compiler/xla:test", "//tensorflow/compiler/xla:types", "//tensorflow/compiler/xla:xla_data_proto", @@ -164,7 +169,6 @@ cc_library( deps = [ ":constants", ":math", - ":numeric", "//tensorflow/compiler/xla:util", "//tensorflow/compiler/xla:xla_data_proto", "//tensorflow/compiler/xla/client:xla_builder", @@ -173,13 +177,46 @@ cc_library( ], ) +cc_library( + name = "slicing", + srcs = ["slicing.cc"], + hdrs = ["slicing.h"], + deps = [ + "//tensorflow/compiler/xla:shape_util", + "//tensorflow/compiler/xla:status_macros", + "//tensorflow/compiler/xla:statusor", + "//tensorflow/compiler/xla:types", + "//tensorflow/compiler/xla:util", + "//tensorflow/compiler/xla:xla_data_proto", + "//tensorflow/compiler/xla/client:xla_builder", + "@com_google_absl//absl/types:span", + ], +) + +xla_test( + name = "slicing_test", + srcs = ["slicing_test.cc"], + tags = ["enable_for_xla_interpreter"], + deps = [ + ":slicing", + "//tensorflow/compiler/xla:literal_util", + "//tensorflow/compiler/xla:test", + "//tensorflow/compiler/xla:types", + "//tensorflow/compiler/xla:xla_data_proto", + "//tensorflow/compiler/xla/client:xla_builder", + "//tensorflow/compiler/xla/tests:client_library_test_base", + "//tensorflow/compiler/xla/tests:xla_internal_test_main", + ], +) + cc_library( name = "sorting", srcs = ["sorting.cc"], hdrs = ["sorting.h"], deps = [ - ":numeric", + "//tensorflow/compiler/xla:shape_util", "//tensorflow/compiler/xla:types", + "//tensorflow/compiler/xla:util", "//tensorflow/compiler/xla:xla_data_proto", "//tensorflow/compiler/xla/client:xla_builder", ], @@ -188,10 +225,6 @@ cc_library( xla_test( name = "sorting_test", srcs = ["sorting_test.cc"], - blacklisted_backends = [ - "cpu", - "gpu", - ], tags = ["enable_for_xla_interpreter"], deps = [ ":sorting", diff --git a/tensorflow/compiler/xla/client/lib/math.cc b/tensorflow/compiler/xla/client/lib/math.cc index 08a887a6e4..36fdda39b4 100644 --- a/tensorflow/compiler/xla/client/lib/math.cc +++ b/tensorflow/compiler/xla/client/lib/math.cc @@ -268,17 +268,16 @@ XlaOp Digamma(XlaOp input) { // Implements Banker's rounding: numbers that are equidistant between two // integers are rounded towards even. XlaOp RoundToEven(XlaOp x) { - auto half = xla::ScalarLike(x, 0.5); - auto one = xla::ScalarLike(x, 1.0); - auto two = xla::ScalarLike(x, 2.0); + auto half = ScalarLike(x, 0.5); + auto one = ScalarLike(x, 1.0); + auto two = ScalarLike(x, 2.0); - auto round_val = xla::Floor(x); + auto round_val = Floor(x); auto fraction = x - round_val; - auto nearest_even_int = round_val - two * xla::Floor(half * x); - auto is_odd = xla::Eq(nearest_even_int, one); - return xla::Select(xla::Or(xla::Gt(fraction, half), - xla::And(xla::Eq(fraction, half), is_odd)), - round_val + one, round_val); + auto nearest_even_int = round_val - two * Floor(half * x); + auto is_odd = Eq(nearest_even_int, one); + return Select(Or(Gt(fraction, half), And(Eq(fraction, half), is_odd)), + round_val + one, round_val); } // Trigonometric functions. @@ -320,4 +319,13 @@ XlaOp Cosh(XlaOp x) { return (Exp(x) + Exp(-x)) * ScalarLike(x, 0.5); } XlaOp Sinh(XlaOp x) { return (Exp(x) - Exp(-x)) * ScalarLike(x, 0.5); } +XlaOp MaybeConjugate(XlaOp x, bool conjugate) { + XlaBuilder* builder = x.builder(); + return builder->ReportErrorOrReturn([&]() -> StatusOr { + TF_ASSIGN_OR_RETURN(Shape shape, builder->GetShape(x)); + auto perform_conj = shape.element_type() == C64 && conjugate; + return perform_conj ? Conj(x) : x; + }); +} + } // namespace xla diff --git a/tensorflow/compiler/xla/client/lib/math.h b/tensorflow/compiler/xla/client/lib/math.h index 3f06d04b9a..17612bf9fd 100644 --- a/tensorflow/compiler/xla/client/lib/math.h +++ b/tensorflow/compiler/xla/client/lib/math.h @@ -86,6 +86,10 @@ XlaOp Cosh(XlaOp x); // Computes the hyperbolic sine of 'x'. XlaOp Sinh(XlaOp x); +// Applies a complex conjugation operation if `a` is complex and `conjugate` +// is true, otherwise returns its argument. +xla::XlaOp MaybeConjugate(xla::XlaOp x, bool conjugate); + } // namespace xla #endif // TENSORFLOW_COMPILER_XLA_CLIENT_LIB_MATH_H_ diff --git a/tensorflow/compiler/xla/client/lib/matrix.cc b/tensorflow/compiler/xla/client/lib/matrix.cc new file mode 100644 index 0000000000..ffd744d190 --- /dev/null +++ b/tensorflow/compiler/xla/client/lib/matrix.cc @@ -0,0 +1,185 @@ +/* Copyright 2018 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/compiler/xla/client/lib/matrix.h" + +#include +#include + +#include "absl/types/span.h" +#include "tensorflow/compiler/xla/client/lib/arithmetic.h" +#include "tensorflow/compiler/xla/client/lib/constants.h" +#include "tensorflow/compiler/xla/client/xla_builder.h" +#include "tensorflow/compiler/xla/shape_util.h" +#include "tensorflow/compiler/xla/status_macros.h" +#include "tensorflow/compiler/xla/statusor.h" +#include "tensorflow/compiler/xla/util.h" + +namespace xla { + +XlaOp IdentityMatrix(XlaBuilder* builder, PrimitiveType type, int64 m, + int64 n) { + auto a = Iota(builder, type, m); + auto b = Iota(builder, type, n); + auto indicator = Eq(a, Broadcast(b, {m}), /*broadcast_dimensions=*/{0}); + return ConvertElementType(indicator, type); +} + +XlaOp GetMatrixDiagonal(XlaOp x) { + XlaBuilder* builder = x.builder(); + return builder->ReportErrorOrReturn([&]() -> StatusOr { + TF_ASSIGN_OR_RETURN(Shape shape, builder->GetShape(x)); + const int64 n_dims = ShapeUtil::Rank(shape); + TF_RET_CHECK(n_dims >= 2); + const int64 m = shape.dimensions(n_dims - 2); + const int64 n = shape.dimensions(n_dims - 1); + absl::Span major_dims = + AsInt64Slice(shape.dimensions()).subspan(/*pos=*/0, /*len=*/n_dims - 2); + auto a = Iota(builder, U32, n); + auto b = Iota(builder, U32, m); + auto indicator = Eq(b, Broadcast(a, {m}), /*broadcast_dimensions=*/{0}); + auto mask = Broadcast(indicator, major_dims); + + // TPUs don't support S64 add reduction at the moment. But fortunately + // OR-reductions work just as well for integers. + XlaComputation reducer = + primitive_util::IsIntegralType(shape.element_type()) + ? CreateScalarOrComputation(shape.element_type(), builder) + : CreateScalarAddComputation(shape.element_type(), builder); + + return Reduce(Select(mask, x, Zeros(builder, shape)), ScalarLike(x, 0), + reducer, {m >= n ? n_dims - 2 : n_dims - 1}); + }); +} + +XlaOp Triangle(XlaOp x, bool lower) { + XlaBuilder* builder = x.builder(); + return builder->ReportErrorOrReturn([&]() -> StatusOr { + TF_ASSIGN_OR_RETURN(Shape shape, builder->GetShape(x)); + const int64 n_dims = ShapeUtil::Rank(shape); + TF_RET_CHECK(n_dims >= 2); + const int64 m = shape.dimensions(n_dims - 2); + const int64 n = shape.dimensions(n_dims - 1); + absl::Span major_dims = + AsInt64Slice(shape.dimensions()).subspan(/*pos=*/0, /*len=*/n_dims - 2); + auto a = Iota(builder, U32, n); + auto b = Iota(builder, U32, m); + XlaOp indicator; + if (lower) { + indicator = Ge(b, Broadcast(a, {m}), /*broadcast_dimensions=*/{0}); + } else { + indicator = Le(b, Broadcast(a, {m}), /*broadcast_dimensions=*/{0}); + } + auto mask = Broadcast(indicator, major_dims); + + return Select(mask, x, Zeros(builder, shape)); + }); +} + +XlaOp UpperTriangle(XlaOp x) { return Triangle(x, false); } + +XlaOp LowerTriangle(XlaOp x) { return Triangle(x, true); } + +XlaOp BatchDot(XlaOp x, XlaOp y, PrecisionConfig::Precision precision) { + XlaBuilder* builder = x.builder(); + return builder->ReportErrorOrReturn([&]() -> StatusOr { + TF_ASSIGN_OR_RETURN(Shape x_shape, builder->GetShape(x)); + TF_ASSIGN_OR_RETURN(Shape y_shape, builder->GetShape(y)); + + // Check that both tensors have the same number of dimensions. There must be + // at least two (the batch dimensions can be empty). + if (ShapeUtil::Rank(x_shape) != ShapeUtil::Rank(y_shape)) { + return InvalidArgument( + "Arguments to BatchDot have different ranks: %s vs. %s", + ShapeUtil::HumanString(x_shape), ShapeUtil::HumanString(y_shape)); + } + const int ndims = ShapeUtil::Rank(x_shape); + if (ndims < 2) { + return InvalidArgument( + "Arguments to BatchDot must have rank >= 2: got %d", ndims); + } + + // The batch dimensions must be equal and the matrix dimensions must be + // valid. + std::vector batch_dimension_numbers; + for (int i = 0; i < ndims - 2; ++i) { + if (x_shape.dimensions(i) != y_shape.dimensions(i)) { + return InvalidArgument( + "Dimension %d of inputs to BatchDot must be equal: shapes %s vs %s", + i, ShapeUtil::HumanString(x_shape), + ShapeUtil::HumanString(y_shape)); + } + batch_dimension_numbers.push_back(i); + } + + int x_inner_dim = ndims - 1; + int y_inner_dim = ndims - 2; + if (x_shape.dimensions(x_inner_dim) != y_shape.dimensions(y_inner_dim)) { + return InvalidArgument( + "Dimensions %d and %d of arguments to BatchDot must be equal: " + "shapes %s vs %s", + x_inner_dim, y_inner_dim, ShapeUtil::HumanString(x_shape), + ShapeUtil::HumanString(y_shape)); + } + + // Check for zero lhs/rhs dim size. + if (ShapeUtil::IsZeroElementArray(x_shape) || + ShapeUtil::IsZeroElementArray(y_shape)) { + std::vector dimensions(batch_dimension_numbers.size()); + for (int i = 0; i < batch_dimension_numbers.size(); ++i) { + dimensions[i] = x_shape.dimensions(batch_dimension_numbers[i]); + } + int x_outer_dim = ndims - 2; + int y_outer_dim = ndims - 1; + dimensions.push_back(x_shape.dimensions(x_outer_dim)); + dimensions.push_back(y_shape.dimensions(y_outer_dim)); + return Broadcast( + ConstantLiteral(builder, LiteralUtil::Zero(x_shape.element_type())), + dimensions); + } + + PrecisionConfig precision_proto; + precision_proto.add_operand_precision(precision); + precision_proto.add_operand_precision(precision); + + DotDimensionNumbers dot_dnums; + dot_dnums.add_lhs_contracting_dimensions(x_inner_dim); + dot_dnums.add_rhs_contracting_dimensions(y_inner_dim); + for (auto batch_dimension_number : batch_dimension_numbers) { + dot_dnums.add_lhs_batch_dimensions(batch_dimension_number); + dot_dnums.add_rhs_batch_dimensions(batch_dimension_number); + } + + return DotGeneral(x, y, dot_dnums, &precision_proto); + }); +} + +XlaOp TransposeInMinorDims(XlaOp x) { + XlaBuilder* builder = x.builder(); + return builder->ReportErrorOrReturn([&]() -> StatusOr { + TF_ASSIGN_OR_RETURN(Shape shape, builder->GetShape(x)); + const int64 n_dims = ShapeUtil::Rank(shape); + TF_RET_CHECK(n_dims >= 2); + std::vector permutation(n_dims); + std::iota(permutation.begin(), permutation.end(), 0); + std::swap(permutation[n_dims - 1], permutation[n_dims - 2]); + return Transpose(x, permutation); + }); +} + +XlaOp MaybeTransposeInMinorDims(XlaOp x, bool transpose) { + return transpose ? TransposeInMinorDims(x) : x; +} +} // namespace xla diff --git a/tensorflow/compiler/xla/client/lib/numeric.h b/tensorflow/compiler/xla/client/lib/matrix.h similarity index 56% rename from tensorflow/compiler/xla/client/lib/numeric.h rename to tensorflow/compiler/xla/client/lib/matrix.h index efd8cdc257..8856f99c7a 100644 --- a/tensorflow/compiler/xla/client/lib/numeric.h +++ b/tensorflow/compiler/xla/client/lib/matrix.h @@ -13,8 +13,8 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#ifndef TENSORFLOW_COMPILER_XLA_CLIENT_LIB_NUMERIC_H_ -#define TENSORFLOW_COMPILER_XLA_CLIENT_LIB_NUMERIC_H_ +#ifndef TENSORFLOW_COMPILER_XLA_CLIENT_LIB_MATRIX_H_ +#define TENSORFLOW_COMPILER_XLA_CLIENT_LIB_MATRIX_H_ #include "tensorflow/compiler/xla/client/xla_builder.h" #include "tensorflow/compiler/xla/types.h" @@ -22,9 +22,6 @@ limitations under the License. namespace xla { -// Returns a rank 1 tensor of `type` containing values [0, 1, 2, ...]. -XlaOp Iota(XlaBuilder* builder, PrimitiveType type, int64 size); - // Returns an m x n matrix with 1s on the diagonal elements, zeros everywhere // else. XlaOp IdentityMatrix(XlaBuilder* builder, PrimitiveType type, int64 m, int64 n); @@ -43,6 +40,34 @@ XlaOp UpperTriangle(XlaOp x); // Get the lower triangle part of the last two dimensions XlaOp LowerTriangle(XlaOp x); +// Multiplies slices of two tensors in batches. + +// Multiplies all slices of `Tensor` `x` and `y` (each slice can be +// viewed as an element of a batch), and arranges the individual results +// in a single output tensor of the same batch size. +// +// The input tensors `x` and `y` are 2-D or higher with shape `[..., r_x, c_x]` +// and `[..., r_y, c_y]`. +// +// The output tensor is 2-D or higher with shape `[..., r_o, c_o]`, where: +// +// r_o = c_x if transpose_x else r_x +// c_o = r_y if transpose_y else c_y +// +// It is computed as: +// +// output[..., :, :] = matrix(x[..., :, :]) * matrix(y[..., :, :]) +xla::XlaOp BatchDot( + xla::XlaOp x, xla::XlaOp y, + xla::PrecisionConfig::Precision precision = xla::PrecisionConfig::DEFAULT); + +// Transposes a stack of matrices `x` by swapping the last two dimensions. +xla::XlaOp TransposeInMinorDims(xla::XlaOp x); + +// Transposes `x` in its minor dimensions if `transpose` is true, otherwise +// returns `x` unchanged. +xla::XlaOp MaybeTransposeInMinorDims(xla::XlaOp x, bool transpose); + } // namespace xla -#endif // TENSORFLOW_COMPILER_XLA_CLIENT_LIB_NUMERIC_H_ +#endif // TENSORFLOW_COMPILER_XLA_CLIENT_LIB_MATRIX_H_ diff --git a/tensorflow/compiler/xla/client/lib/numeric_test.cc b/tensorflow/compiler/xla/client/lib/matrix_test.cc similarity index 53% rename from tensorflow/compiler/xla/client/lib/numeric_test.cc rename to tensorflow/compiler/xla/client/lib/matrix_test.cc index 7d6aedd494..0593a7517a 100644 --- a/tensorflow/compiler/xla/client/lib/numeric_test.cc +++ b/tensorflow/compiler/xla/client/lib/matrix_test.cc @@ -13,7 +13,9 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "tensorflow/compiler/xla/client/lib/numeric.h" +#include "tensorflow/compiler/xla/client/lib/matrix.h" + +#include "tensorflow/compiler/xla/client/lib/slicing.h" #include "tensorflow/compiler/xla/client/xla_builder.h" #include "tensorflow/compiler/xla/test.h" #include "tensorflow/compiler/xla/tests/client_library_test_base.h" @@ -24,13 +26,13 @@ limitations under the License. namespace xla { namespace { -class NumericTest : public ClientLibraryTestBase { +class MatrixTest : public ClientLibraryTestBase { protected: template void TestMatrixDiagonal(); }; -XLA_TEST_F(NumericTest, Triangle) { +XLA_TEST_F(MatrixTest, Triangle) { XlaBuilder builder(TestName()); Array3D input(2, 3, 4); input.FillIota(0); @@ -45,7 +47,7 @@ XLA_TEST_F(NumericTest, Triangle) { } template -void NumericTest::TestMatrixDiagonal() { +void MatrixTest::TestMatrixDiagonal() { XlaBuilder builder("GetMatrixDiagonal"); Array3D input(2, 3, 4); input.FillIota(0); @@ -58,11 +60,46 @@ void NumericTest::TestMatrixDiagonal() { ComputeAndCompareR2(&builder, expected, {a_data.get()}); } -XLA_TEST_F(NumericTest, GetMatrixDiagonal_S32) { TestMatrixDiagonal(); } +XLA_TEST_F(MatrixTest, GetMatrixDiagonal_S32) { TestMatrixDiagonal(); } + +XLA_TEST_F(MatrixTest, GetMatrixDiagonal_S64) { TestMatrixDiagonal(); } + +XLA_TEST_F(MatrixTest, GetMatrixDiagonal_F32) { TestMatrixDiagonal(); } + +Array3D BatchedAValsFull() { + return {{ + {2, 0, 1, 2}, + {3, 6, 0, 1}, + {4, 7, 9, 0}, + {5, 8, 10, 11}, + }, + { + {16, 24, 8, 12}, + {24, 61, 82, 48}, + {8, 82, 456, 106}, + {12, 48, 106, 62}, + }}; +} + +XLA_TEST_F(MatrixTest, RowBatchDot) { + XlaBuilder builder(TestName()); + + int n = 4; -XLA_TEST_F(NumericTest, GetMatrixDiagonal_S64) { TestMatrixDiagonal(); } + XlaOp a, row, index; + auto a_data = + CreateR3Parameter(BatchedAValsFull(), 0, "a", &builder, &a); + auto row_data = CreateR3Parameter({{{9, 1, 0, 0}}, {{2, 4, 0, 0}}}, 1, + "row", &builder, &row); + // Select {{3, 6, 0, 1}, {24, 61, 82, 48}} out of BatchedAValsFull(). + auto index_data = CreateR0Parameter(1, 2, "index", &builder, &index); -XLA_TEST_F(NumericTest, GetMatrixDiagonal_F32) { TestMatrixDiagonal(); } + auto l_index = DynamicSliceInMinorDims( + a, {index, ConstantR0(&builder, 0)}, {1, n}); + BatchDot(l_index, TransposeInMinorDims(row)); + ComputeAndCompareR3(&builder, {{{33}}, {{292}}}, + {a_data.get(), row_data.get(), index_data.get()}); +} } // namespace } // namespace xla diff --git a/tensorflow/compiler/xla/client/lib/numeric.cc b/tensorflow/compiler/xla/client/lib/numeric.cc deleted file mode 100644 index 377654220b..0000000000 --- a/tensorflow/compiler/xla/client/lib/numeric.cc +++ /dev/null @@ -1,89 +0,0 @@ -/* Copyright 2018 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 -#include - -#include "absl/types/span.h" -#include "tensorflow/compiler/xla/client/lib/arithmetic.h" -#include "tensorflow/compiler/xla/client/lib/constants.h" -#include "tensorflow/compiler/xla/client/lib/numeric.h" - -namespace xla { - -XlaOp IdentityMatrix(XlaBuilder* builder, PrimitiveType type, int64 m, - int64 n) { - auto a = Iota(builder, type, m); - auto b = Iota(builder, type, n); - auto indicator = Eq(a, Broadcast(b, {m}), /*broadcast_dimensions=*/{0}); - return ConvertElementType(indicator, type); -} - -XlaOp GetMatrixDiagonal(XlaOp x) { - XlaBuilder* builder = x.builder(); - return builder->ReportErrorOrReturn([&]() -> StatusOr { - TF_ASSIGN_OR_RETURN(Shape shape, builder->GetShape(x)); - const int64 n_dims = ShapeUtil::Rank(shape); - TF_RET_CHECK(n_dims >= 2); - const int64 m = shape.dimensions(n_dims - 2); - const int64 n = shape.dimensions(n_dims - 1); - absl::Span major_dims = - AsInt64Slice(shape.dimensions()).subspan(/*pos=*/0, /*len=*/n_dims - 2); - auto a = Iota(builder, U32, n); - auto b = Iota(builder, U32, m); - auto indicator = Eq(b, Broadcast(a, {m}), /*broadcast_dimensions=*/{0}); - auto mask = Broadcast(indicator, major_dims); - - // TPUs don't support S64 add reduction at the moment. But fortunately - // OR-reductions work just as well for integers. - XlaComputation reducer = - primitive_util::IsIntegralType(shape.element_type()) - ? CreateScalarOrComputation(shape.element_type(), builder) - : CreateScalarAddComputation(shape.element_type(), builder); - - return Reduce(Select(mask, x, Zeros(builder, shape)), ScalarLike(x, 0), - reducer, {m >= n ? n_dims - 2 : n_dims - 1}); - }); -} - -XlaOp Triangle(XlaOp x, bool lower) { - XlaBuilder* builder = x.builder(); - return builder->ReportErrorOrReturn([&]() -> StatusOr { - TF_ASSIGN_OR_RETURN(Shape shape, builder->GetShape(x)); - const int64 n_dims = ShapeUtil::Rank(shape); - TF_RET_CHECK(n_dims >= 2); - const int64 m = shape.dimensions(n_dims - 2); - const int64 n = shape.dimensions(n_dims - 1); - absl::Span major_dims = - AsInt64Slice(shape.dimensions()).subspan(/*pos=*/0, /*len=*/n_dims - 2); - auto a = Iota(builder, U32, n); - auto b = Iota(builder, U32, m); - xla::XlaOp indicator; - if (lower) { - indicator = Ge(b, Broadcast(a, {m}), /*broadcast_dimensions=*/{0}); - } else { - indicator = Le(b, Broadcast(a, {m}), /*broadcast_dimensions=*/{0}); - } - auto mask = Broadcast(indicator, major_dims); - - return Select(mask, x, Zeros(builder, shape)); - }); -} - -XlaOp UpperTriangle(XlaOp x) { return Triangle(x, false); } - -XlaOp LowerTriangle(XlaOp x) { return Triangle(x, true); } - -} // namespace xla diff --git a/tensorflow/compiler/xla/client/lib/prng.cc b/tensorflow/compiler/xla/client/lib/prng.cc index c6f68c8ee2..85b9e1827d 100644 --- a/tensorflow/compiler/xla/client/lib/prng.cc +++ b/tensorflow/compiler/xla/client/lib/prng.cc @@ -18,7 +18,6 @@ limitations under the License. #include "absl/base/casts.h" #include "tensorflow/compiler/xla/client/lib/constants.h" #include "tensorflow/compiler/xla/client/lib/math.h" -#include "tensorflow/compiler/xla/client/lib/numeric.h" #include "tensorflow/compiler/xla/client/xla_builder.h" #include "tensorflow/compiler/xla/util.h" diff --git a/tensorflow/compiler/xla/client/lib/slicing.cc b/tensorflow/compiler/xla/client/lib/slicing.cc new file mode 100644 index 0000000000..f8c7df3ff5 --- /dev/null +++ b/tensorflow/compiler/xla/client/lib/slicing.cc @@ -0,0 +1,134 @@ +/* Copyright 2018 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/compiler/xla/client/lib/slicing.h" + +namespace xla { + +XlaOp SliceInMinorDims(XlaOp x, absl::Span start, + absl::Span end) { + XlaBuilder* builder = x.builder(); + return builder->ReportErrorOrReturn([&]() -> StatusOr { + TF_RET_CHECK(start.size() == end.size()); + int64 n_minor_dims = start.size(); + + TF_ASSIGN_OR_RETURN(Shape shape, builder->GetShape(x)); + + const int64 n_dims = ShapeUtil::Rank(shape); + TF_RET_CHECK(n_minor_dims <= n_dims); + auto major_dims = AsInt64Slice(shape.dimensions()) + .subspan( + /*pos=*/0, + /*len=*/n_dims - n_minor_dims); + + // Prepends 0s in the major dim + std::vector padded_start(n_dims, 0); + std::copy(start.begin(), start.end(), + padded_start.begin() + major_dims.size()); + + // Prepends the shape of the major dims. + std::vector padded_end(n_dims); + std::copy(major_dims.begin(), major_dims.end(), padded_end.begin()); + std::copy(end.begin(), end.end(), padded_end.begin() + major_dims.size()); + + std::vector strides(n_dims, 1); + return Slice(x, padded_start, padded_end, strides); + }); +} + +XlaOp UpdateSlice(XlaOp x, XlaOp update, absl::Span start) { + XlaBuilder* builder = x.builder(); + return builder->ReportErrorOrReturn([&]() -> StatusOr { + // TODO(phawkins): make int64 work on all backends, remove the int32 cast. + std::vector start_as_int32(start.begin(), start.end()); + auto start_constant = ConstantR1(builder, start_as_int32); + TF_ASSIGN_OR_RETURN(Shape shape, builder->GetShape(x)); + const int64 n_dims = ShapeUtil::Rank(shape); + TF_ASSIGN_OR_RETURN(Shape start_constant_shape, + builder->GetShape(start_constant)); + const int64 start_length = + ShapeUtil::GetDimension(start_constant_shape, -1); + TF_RET_CHECK(start_length == n_dims); + return DynamicUpdateSlice(x, update, start_constant); + }); +} + +XlaOp UpdateSliceInMinorDims(XlaOp x, XlaOp update, + absl::Span start) { + XlaBuilder* builder = x.builder(); + return builder->ReportErrorOrReturn([&]() -> StatusOr { + TF_ASSIGN_OR_RETURN(Shape shape, builder->GetShape(x)); + const int64 n_dims = ShapeUtil::Rank(shape); + const int64 n_minor_dims = start.size(); + TF_RET_CHECK(n_minor_dims <= n_dims); + std::vector padded_start(n_dims, 0); + std::copy(start.begin(), start.end(), + padded_start.begin() + (n_dims - n_minor_dims)); + return UpdateSlice(x, update, padded_start); + }); +} + +namespace { + +std::vector ConcatVectors(absl::Span xs, + absl::Span ys) { + std::vector output(xs.size() + ys.size()); + std::copy(xs.begin(), xs.end(), output.begin()); + std::copy(ys.begin(), ys.end(), output.begin() + xs.size()); + return output; +} + +XlaOp PrependZerosInMajorDims(XlaOp x, absl::Span starts) { + XlaBuilder* builder = x.builder(); + return builder->ReportErrorOrReturn([&]() -> StatusOr { + TF_ASSIGN_OR_RETURN(Shape shape, builder->GetShape(x)); + const int64 n_dims = ShapeUtil::Rank(shape); + auto zero = Reshape(ConstantR0(builder, 0), {1}); + std::vector padded_starts(n_dims, zero); + for (int i = 0; i < starts.size(); ++i) { + padded_starts[n_dims - starts.size() + i] = Reshape(starts[i], {1}); + } + return ConcatInDim(builder, padded_starts, 0); + }); +} + +} // namespace + +XlaOp DynamicSliceInMinorDims(XlaOp x, absl::Span starts, + absl::Span sizes) { + XlaBuilder* builder = x.builder(); + return builder->ReportErrorOrReturn([&]() -> StatusOr { + TF_ASSIGN_OR_RETURN(Shape shape, builder->GetShape(x)); + const int64 n_dims = ShapeUtil::Rank(shape); + int64 n_minor_dims = starts.size(); + TF_RET_CHECK(n_minor_dims == sizes.size()); + TF_RET_CHECK(n_minor_dims <= n_dims); + auto major_dims = AsInt64Slice(shape.dimensions()) + .subspan( + /*pos=*/0, + /*len=*/n_dims - sizes.size()); + auto padded_starts = PrependZerosInMajorDims(x, starts); + auto padded_sizes = ConcatVectors(major_dims, sizes); + return DynamicSlice(x, padded_starts, padded_sizes); + }); +} + +XlaOp DynamicUpdateSliceInMinorDims(XlaOp x, XlaOp update, + absl::Span starts) { + auto padded_starts = PrependZerosInMajorDims(x, starts); + return DynamicUpdateSlice(x, update, padded_starts); +} + +} // namespace xla diff --git a/tensorflow/compiler/xla/client/lib/slicing.h b/tensorflow/compiler/xla/client/lib/slicing.h new file mode 100644 index 0000000000..6c482a38b5 --- /dev/null +++ b/tensorflow/compiler/xla/client/lib/slicing.h @@ -0,0 +1,48 @@ +/* Copyright 2018 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 "absl/types/span.h" +#include "tensorflow/compiler/xla/client/xla_builder.h" +#include "tensorflow/compiler/xla/types.h" + +#ifndef TENSORFLOW_COMPILER_XLA_CLIENT_LIB_SLICING_H_ +#define TENSORFLOW_COMPILER_XLA_CLIENT_LIB_SLICING_H_ + +namespace xla { + +// Updates a slice of 'x', i.e., +// x[start[0], ..., start[n]] = update +XlaOp UpdateSlice(XlaOp x, XlaOp update, absl::Span start); + +// Performs a slice in the minor dimensions of a tensor. +// x[..., start[0]:end[0], ..., start[n]:end[n]] +XlaOp SliceInMinorDims(XlaOp x, absl::Span start, + absl::Span end); + +// Updates a slice of 'x', where 'start' contains a list of minor dimensions: +// x[..., start[0]:..., ..., start[n]:...] = update +XlaOp UpdateSliceInMinorDims(XlaOp x, XlaOp update, + absl::Span start); + +// Performs a dynamic slice in the minor dimensions of a tensor. +XlaOp DynamicSliceInMinorDims(XlaOp x, absl::Span starts, + absl::Span sizes); + +XlaOp DynamicUpdateSliceInMinorDims(XlaOp x, XlaOp update, + absl::Span starts); + +} // namespace xla + +#endif // TENSORFLOW_COMPILER_XLA_CLIENT_LIB_SLICING_H_ diff --git a/tensorflow/compiler/tf2xla/lib/util_test.cc b/tensorflow/compiler/xla/client/lib/slicing_test.cc similarity index 67% rename from tensorflow/compiler/tf2xla/lib/util_test.cc rename to tensorflow/compiler/xla/client/lib/slicing_test.cc index 442fe92c34..8d362119e0 100644 --- a/tensorflow/compiler/tf2xla/lib/util_test.cc +++ b/tensorflow/compiler/xla/client/lib/slicing_test.cc @@ -13,28 +13,19 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "tensorflow/compiler/tf2xla/lib/util.h" +#include "tensorflow/compiler/xla/client/lib/slicing.h" -#include -#include -#include - -#include "tensorflow/compiler/tf2xla/lib/batch_dot.h" -#include "tensorflow/compiler/xla/array2d.h" -#include "tensorflow/compiler/xla/literal.h" -#include "tensorflow/compiler/xla/statusor.h" +#include "tensorflow/compiler/xla/client/xla_builder.h" +#include "tensorflow/compiler/xla/literal_util.h" #include "tensorflow/compiler/xla/test.h" #include "tensorflow/compiler/xla/tests/client_library_test_base.h" -#include "tensorflow/compiler/xla/tests/literal_test_util.h" #include "tensorflow/compiler/xla/tests/test_macros.h" #include "tensorflow/compiler/xla/types.h" -#include "tensorflow/core/lib/core/status_test_util.h" -namespace tensorflow { +namespace xla { namespace { -using UtilTest = xla::ClientLibraryTestBase; -using UtilLeftLookingTest = xla::ClientLibraryTestBase; +using SlicingTest = xla::ClientLibraryTestBase; xla::Array2D BValsRight() { return {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}; @@ -63,7 +54,7 @@ xla::Array3D BatchedAValsFull() { }}; } -XLA_TEST_F(UtilTest, Simple2dLookup) { +XLA_TEST_F(SlicingTest, Simple2dLookup) { xla::XlaBuilder builder(TestName()); xla::XlaOp a, x, y; @@ -77,7 +68,7 @@ XLA_TEST_F(UtilTest, Simple2dLookup) { xla::ErrorSpec(1e-2, 1e-2)); } -XLA_TEST_F(UtilTest, Simple3dLookup) { +XLA_TEST_F(SlicingTest, Simple3dLookup) { xla::XlaBuilder builder(TestName()); xla::XlaOp a, index; @@ -92,7 +83,7 @@ XLA_TEST_F(UtilTest, Simple3dLookup) { {a_data.get(), index_data.get()}); } -XLA_TEST_F(UtilTest, SimpleSliceUpdate) { +XLA_TEST_F(SlicingTest, SimpleSliceUpdate) { xla::XlaBuilder builder(TestName()); xla::XlaOp a, b, x, y; @@ -111,26 +102,5 @@ XLA_TEST_F(UtilTest, SimpleSliceUpdate) { {a_data.get(), b_data.get(), x_data.get(), y_data.get()}); } -XLA_TEST_F(UtilTest, RowBatchDot) { - xla::XlaBuilder builder(TestName()); - - int n = 4; - - xla::XlaOp a, row, index; - auto a_data = - CreateR3Parameter(BatchedAValsFull(), 0, "a", &builder, &a); - auto row_data = CreateR3Parameter({{{9, 1, 0, 0}}, {{2, 4, 0, 0}}}, 1, - "row", &builder, &row); - // Select {{3, 6, 0, 1}, {24, 61, 82, 48}} out of BatchedAValsFull(). - auto index_data = CreateR0Parameter(1, 2, "index", &builder, &index); - - auto l_index = DynamicSliceInMinorDims( - a, {index, xla::ConstantR0(&builder, 0)}, {1, n}); - BatchDot(l_index, row, /*transpose_x=*/false, /*transpose_y=*/true); - - ComputeAndCompareR3(&builder, {{{33}}, {{292}}}, - {a_data.get(), row_data.get(), index_data.get()}); -} - } // namespace -} // namespace tensorflow +} // namespace xla diff --git a/tensorflow/compiler/xla/client/lib/sorting.cc b/tensorflow/compiler/xla/client/lib/sorting.cc index 0475fd9c94..e8553a08bb 100644 --- a/tensorflow/compiler/xla/client/lib/sorting.cc +++ b/tensorflow/compiler/xla/client/lib/sorting.cc @@ -14,7 +14,9 @@ limitations under the License. ==============================================================================*/ #include "tensorflow/compiler/xla/client/lib/sorting.h" -#include "tensorflow/compiler/xla/client/lib/numeric.h" +#include "tensorflow/compiler/xla/client/xla_builder.h" +#include "tensorflow/compiler/xla/shape_util.h" +#include "tensorflow/compiler/xla/util.h" namespace xla { @@ -23,13 +25,12 @@ XlaOp TopK(XlaOp input, int64 k) { return builder->ReportErrorOrReturn([&]() -> StatusOr { TF_ASSIGN_OR_RETURN(Shape input_shape, builder->GetShape(input)); int last_dim = input_shape.dimensions_size() - 1; - int last_dim_size = input_shape.dimensions(last_dim); - XlaOp iota_s32 = Iota(builder, S32, last_dim_size); + Shape iota_shape = + ShapeUtil::MakeShape(S32, AsInt64Slice(input_shape.dimensions())); + XlaOp iota_s32 = Iota(builder, iota_shape, last_dim); auto input_dims = input_shape.dimensions(); - std::vector broadcast_dims(input_dims.begin(), input_dims.end() - 1); - XlaOp broadcast_s32 = Broadcast(iota_s32, broadcast_dims); - XlaOp sort_result = Sort(Neg(input), {broadcast_s32}); + XlaOp sort_result = Sort(Neg(input), {iota_s32}); std::vector start_indices(input_shape.dimensions_size(), 0); std::vector limit_indices(input_dims.begin(), input_dims.end()); limit_indices[last_dim] = k; diff --git a/tensorflow/compiler/xla/client/lib/sorting_test.cc b/tensorflow/compiler/xla/client/lib/sorting_test.cc index fef98c9923..27ff36c749 100644 --- a/tensorflow/compiler/xla/client/lib/sorting_test.cc +++ b/tensorflow/compiler/xla/client/lib/sorting_test.cc @@ -14,6 +14,9 @@ limitations under the License. ==============================================================================*/ #include "tensorflow/compiler/xla/client/lib/sorting.h" + +#include + #include "tensorflow/compiler/xla/client/xla_builder.h" #include "tensorflow/compiler/xla/test.h" #include "tensorflow/compiler/xla/tests/client_library_test_base.h" @@ -41,6 +44,28 @@ XLA_TEST_F(SortingTest, TopK3From8Indices) { ComputeAndCompareR1(&builder, {0, 1, 2}, {}); } +// TODO(b/119930279): enable this test. +XLA_TEST_F(SortingTest, DISABLED_TopKFullSortMinInt) { + XlaBuilder builder(TestName()); + auto x_rev = ConstantR1(&builder, {std::numeric_limits::min(), + std::numeric_limits::min() + 1, + std::numeric_limits::max()}); + xla::GetTupleElement(xla::TopK(x_rev, 3), 1); + ComputeAndCompareR1(&builder, {2, 1, 0}, {}); +} + +XLA_TEST_F(SortingTest, NOT_TopKFullSortMinInt) { + XlaBuilder builder(TestName()); + auto x_rev = ConstantR1(&builder, {std::numeric_limits::min(), + std::numeric_limits::min() + 1, + std::numeric_limits::max()}); + xla::GetTupleElement(xla::TopK(x_rev, 3), 1); + // TopK currently negates the keys, which doesn't work correctly for + // std::numeric_limits::min(). Therefore, it will sort this key to the + // front instead of to the back. + ComputeAndCompareR1(&builder, {0, 2, 1}, {}); +} + XLA_TEST_F(SortingTest, TopKFullSort) { XlaBuilder builder(TestName()); const int kSize = 16; @@ -56,5 +81,13 @@ XLA_TEST_F(SortingTest, TopKFullSort) { ComputeAndCompareR1(&builder, inputs, {}); } +XLA_TEST_F(SortingTest, TopKFullSortWithDuplicates) { + XlaBuilder builder(TestName()); + XlaOp a; + auto a_data = CreateR1Parameter({1, 1, 2, 2, 1}, 0, "a", &builder, &a); + xla::GetTupleElement(xla::TopK(a, 5), 1); + ComputeAndCompareR1(&builder, {2, 3, 0, 1, 4}, {a_data.get()}); +} + } // namespace } // namespace xla diff --git a/tensorflow/compiler/xla/client/lib/testing.cc b/tensorflow/compiler/xla/client/lib/testing.cc index a44681f586..a95bbf2c8c 100644 --- a/tensorflow/compiler/xla/client/lib/testing.cc +++ b/tensorflow/compiler/xla/client/lib/testing.cc @@ -66,7 +66,7 @@ std::unique_ptr MakeFakeDataViaDeviceOrDie(const Shape& shape, XlaComputation computation = b.Build().ConsumeValueOrDie(); auto execution_options = CreateDefaultExecutionOptions(); - *execution_options.mutable_shape_with_output_layout() = shape; + *execution_options.mutable_shape_with_output_layout() = shape.ToProto(); return client->Execute(computation, /*arguments=*/{}, &execution_options) .ConsumeValueOrDie(); } @@ -98,8 +98,8 @@ std::vector> MakeFakeArgumentsOrDie( auto program_shape = computation.proto().host_program_shape(); std::vector> results; - for (const Shape& shape : program_shape.parameters()) { - results.push_back(MakeFakeDataOrDie(shape, client)); + for (const ShapeProto& shape : program_shape.parameters()) { + results.push_back(MakeFakeDataOrDie(Shape(shape), client)); } return results; } diff --git a/tensorflow/compiler/xla/client/local_client.cc b/tensorflow/compiler/xla/client/local_client.cc index f96b6c9c26..aaa5d6989e 100644 --- a/tensorflow/compiler/xla/client/local_client.cc +++ b/tensorflow/compiler/xla/client/local_client.cc @@ -310,4 +310,28 @@ StatusOr LocalClient::ReplicaNumberToDeviceOrdinal(int replica_number) { return local_service_->ReplicaNumberToDeviceOrdinal(replica_number); } +StatusOr LocalClient::TransferToLocalServer( + const ::xla::BorrowingLiteral& literal, int device_oridinal) { + const ::xla::Shape& shape = literal.shape(); + + TF_ASSIGN_OR_RETURN( + ::xla::ScopedShapedBuffer shaped_buffer, + backend().transfer_manager()->AllocateScopedShapedBuffer( + shape, backend().memory_allocator(), device_oridinal)); + TF_ASSIGN_OR_RETURN(auto stream, + mutable_backend()->BorrowStream(device_oridinal)); + TF_RETURN_IF_ERROR(backend().transfer_manager()->TransferLiteralToDevice( + stream.get(), literal, shaped_buffer)); + std::vector<::xla::ScopedShapedBuffer> replicated_buffer; + replicated_buffer.emplace_back(std::move(shaped_buffer)); + ::xla::TransferToServerResponse result; + TF_ASSIGN_OR_RETURN(*result.mutable_data(), + local_service_->RegisterReplicatedBuffers( + std::move(replicated_buffer), + absl::StrCat("TransferToServer literal of shape ", + ::xla::ShapeUtil::HumanString(shape)))); + + return result; +} + } // namespace xla diff --git a/tensorflow/compiler/xla/client/local_client.h b/tensorflow/compiler/xla/client/local_client.h index e49451ca97..ddb36680e8 100644 --- a/tensorflow/compiler/xla/client/local_client.h +++ b/tensorflow/compiler/xla/client/local_client.h @@ -129,6 +129,10 @@ class LocalClient : public Client { const Literal& literal, int device_ordinal, DeviceMemoryAllocator* allocator = nullptr); + // Transfer the BorrowingLiteral to the device with the given ordinal. + StatusOr TransferToLocalServer( + const ::xla::BorrowingLiteral& literal, int device_oridinal); + // Copy the data from the device contained in the given ShapedBuffer and // return as a Literal. StatusOr ShapedBufferToLiteral(const ShapedBuffer& shaped_buffer); diff --git a/tensorflow/compiler/xla/client/sharding_builder.cc b/tensorflow/compiler/xla/client/sharding_builder.cc index 176802b33e..fb9ea6ec3f 100644 --- a/tensorflow/compiler/xla/client/sharding_builder.cc +++ b/tensorflow/compiler/xla/client/sharding_builder.cc @@ -36,7 +36,7 @@ OpSharding Tile(const Shape& tile_shape, const TileAssignment& tile_assignment) { OpSharding result; result.set_type(OpSharding::Type::OpSharding_Type_OTHER); - *result.mutable_tile_shape() = tile_shape; + *result.mutable_tile_shape() = tile_shape.ToProto(); for (int64 dim : tile_assignment.dimensions()) { result.add_tile_assignment_dimensions(dim); } @@ -52,7 +52,7 @@ OpSharding Tile1D(const Shape& tile_shape, int64 num_tiles) { CHECK_EQ(ShapeUtil::Rank(tile_shape), 1); std::vector dimensions(1, num_tiles); - *result.mutable_tile_shape() = tile_shape; + *result.mutable_tile_shape() = tile_shape.ToProto(); auto& tile_dimension = (*result.mutable_tile_shape()->mutable_dimensions())[0]; tile_dimension = CeilOfRatio(static_cast(tile_dimension), num_tiles); diff --git a/tensorflow/compiler/xla/client/xla_builder.cc b/tensorflow/compiler/xla/client/xla_builder.cc index 0a587725d2..60df2ec395 100644 --- a/tensorflow/compiler/xla/client/xla_builder.cc +++ b/tensorflow/compiler/xla/client/xla_builder.cc @@ -102,7 +102,7 @@ StatusOr XlaBuilder::GetShape(const XlaOp& op) const { TF_RETURN_IF_ERROR(first_error_); TF_ASSIGN_OR_RETURN(auto instr, LookUpInstruction(op)); - return instr->shape(); + return Shape(instr->shape()); } StatusOr> XlaBuilder::GetOperandShapes( @@ -155,7 +155,7 @@ StatusOr XlaBuilder::GetProgramShape(int64 root_id) const { ProgramShape program_shape; - *program_shape.mutable_result() = root_proto->shape(); + *program_shape.mutable_result() = Shape(root_proto->shape()); // Check that the parameter numbers are continuous from 0, and add parameter // shapes and names to the program shape. @@ -172,7 +172,7 @@ StatusOr XlaBuilder::GetProgramShape(int64 root_id) const { const int64 index = instr.parameter_number(); TF_RET_CHECK(index >= 0 && index < param_count) << "invalid parameter number: " << index; - *program_shape.mutable_parameters(index) = instr.shape(); + *program_shape.mutable_parameters(index) = Shape(instr.shape()); *program_shape.mutable_parameter_names(index) = instr.name(); } } @@ -239,6 +239,19 @@ void XlaBuilder::IsConstantVisitor(const int64 op_handle, visited->insert(op_handle); } +Status XlaBuilder::SetDynamicBinding(int64 dynamic_size_param_num, + ShapeIndex dynamic_size_param_index, + int64 target_param_num, + ShapeIndex target_param_index, + int64 target_dim_num) { + TF_RETURN_IF_ERROR(dynamic_parameter_binding_.Bind( + DynamicParameterBinding::DynamicParameter{dynamic_size_param_num, + dynamic_size_param_index}, + DynamicParameterBinding::DynamicDimension{ + target_param_num, target_param_index, target_dim_num})); + return Status::OK(); +} + XlaComputation XlaBuilder::BuildAndNoteError() { DCHECK(parent_builder_ != nullptr); auto build_status = Build(); @@ -275,7 +288,8 @@ StatusOr XlaBuilder::Build(int64 root_id) { HloComputationProto entry; SetProtoIdAndName(&entry, name_, kNameSeparator, GetNextId()); - TF_ASSIGN_OR_RETURN(*entry.mutable_program_shape(), GetProgramShape(root_id)); + TF_ASSIGN_OR_RETURN(ProgramShape program_shape, GetProgramShape(root_id)); + *entry.mutable_program_shape() = program_shape.ToProto(); entry.set_root_id(root_id); for (auto& instruction : instructions_) { @@ -297,6 +311,9 @@ StatusOr XlaBuilder::Build(int64 root_id) { } module->add_computations()->Swap(&entry); + *(module->mutable_dynamic_parameter_binding()) = + dynamic_parameter_binding_.ToProto(); + // Clear data held by this builder. this->instructions_.clear(); this->handle_to_index_.clear(); @@ -312,7 +329,7 @@ StatusOr XlaBuilder::InDimBroadcast( TF_RETURN_IF_ERROR(first_error_); HloInstructionProto instr; - *instr.mutable_shape() = shape; + *instr.mutable_shape() = shape.ToProto(); for (int64 dim : broadcast_dimensions) { instr.add_dimensions(dim); } @@ -363,8 +380,9 @@ XlaOp XlaBuilder::UnaryOp(HloOpcode unop, const XlaOp& operand) { return ReportErrorOrReturn([&]() -> StatusOr { HloInstructionProto instr; TF_ASSIGN_OR_RETURN(const Shape& operand_shape, GetShape(operand)); - TF_ASSIGN_OR_RETURN(*instr.mutable_shape(), + TF_ASSIGN_OR_RETURN(Shape shape, ShapeInference::InferUnaryOpShape(unop, operand_shape)); + *instr.mutable_shape() = shape.ToProto(); return AddInstruction(std::move(instr), unop, {operand}); }); } @@ -375,9 +393,10 @@ XlaOp XlaBuilder::BinaryOp(HloOpcode binop, const XlaOp& lhs, const XlaOp& rhs, HloInstructionProto instr; TF_ASSIGN_OR_RETURN(const Shape& lhs_shape, GetShape(lhs)); TF_ASSIGN_OR_RETURN(const Shape& rhs_shape, GetShape(rhs)); - TF_ASSIGN_OR_RETURN(*instr.mutable_shape(), + TF_ASSIGN_OR_RETURN(Shape shape, ShapeInference::InferBinaryOpShape( binop, lhs_shape, rhs_shape, broadcast_dimensions)); + *instr.mutable_shape() = shape.ToProto(); const int64 lhs_rank = ShapeUtil::Rank(lhs_shape); const int64 rhs_rank = ShapeUtil::Rank(rhs_shape); @@ -391,7 +410,7 @@ XlaOp XlaBuilder::BinaryOp(HloOpcode binop, const XlaOp& lhs, const XlaOp& rhs, const Shape& from_shape = should_broadcast_lhs ? lhs_shape : rhs_shape; std::vector to_size; - for (int64 size : instr.shape().dimensions()) { + for (int64 size : shape.dimensions()) { to_size.push_back(size); } for (int64 from_dim = 0; from_dim < ShapeUtil::Rank(from_shape); @@ -411,14 +430,14 @@ XlaOp XlaBuilder::BinaryOp(HloOpcode binop, const XlaOp& lhs, const XlaOp& rhs, } TF_ASSIGN_OR_RETURN(Shape updated_lhs_shape, GetShape(updated_lhs)); - if (!ShapeUtil::SameDimensions(instr.shape(), updated_lhs_shape)) { + if (!ShapeUtil::SameDimensions(shape, updated_lhs_shape)) { TF_ASSIGN_OR_RETURN(updated_lhs, - AddBroadcastSequence(instr.shape(), updated_lhs)); + AddBroadcastSequence(shape, updated_lhs)); } TF_ASSIGN_OR_RETURN(Shape updated_rhs_shape, GetShape(updated_rhs)); - if (!ShapeUtil::SameDimensions(instr.shape(), updated_rhs_shape)) { + if (!ShapeUtil::SameDimensions(shape, updated_rhs_shape)) { TF_ASSIGN_OR_RETURN(updated_rhs, - AddBroadcastSequence(instr.shape(), updated_rhs)); + AddBroadcastSequence(shape, updated_rhs)); } return AddInstruction(std::move(instr), binop, {updated_lhs, updated_rhs}); @@ -432,30 +451,28 @@ XlaOp XlaBuilder::TernaryOp(HloOpcode triop, const XlaOp& lhs, const XlaOp& rhs, TF_ASSIGN_OR_RETURN(const Shape& lhs_shape, GetShape(lhs)); TF_ASSIGN_OR_RETURN(const Shape& rhs_shape, GetShape(rhs)); TF_ASSIGN_OR_RETURN(const Shape& ehs_shape, GetShape(ehs)); - TF_ASSIGN_OR_RETURN(*instr.mutable_shape(), - ShapeInference::InferTernaryOpShape( - triop, lhs_shape, rhs_shape, ehs_shape)); + TF_ASSIGN_OR_RETURN( + Shape shape, ShapeInference::InferTernaryOpShape(triop, lhs_shape, + rhs_shape, ehs_shape)); + *instr.mutable_shape() = shape.ToProto(); XlaOp updated_lhs = lhs; XlaOp updated_rhs = rhs; XlaOp updated_ehs = ehs; - if (!ShapeUtil::IsTuple(instr.shape())) { + if (!ShapeUtil::IsTuple(shape)) { if (!ShapeUtil::IsTuple(lhs_shape) && - !ShapeUtil::SameDimensions(instr.shape(), lhs_shape)) { + !ShapeUtil::SameDimensions(shape, lhs_shape)) { // lhs is being implicitly broadcasted. Change to explicit. - TF_ASSIGN_OR_RETURN(updated_lhs, - AddBroadcastSequence(instr.shape(), lhs)); + TF_ASSIGN_OR_RETURN(updated_lhs, AddBroadcastSequence(shape, lhs)); } if (!ShapeUtil::IsTuple(rhs_shape) && - !ShapeUtil::SameDimensions(instr.shape(), rhs_shape)) { + !ShapeUtil::SameDimensions(shape, rhs_shape)) { // rhs is being implicitly broadcasted. Change to explicit. - TF_ASSIGN_OR_RETURN(updated_rhs, - AddBroadcastSequence(instr.shape(), rhs)); + TF_ASSIGN_OR_RETURN(updated_rhs, AddBroadcastSequence(shape, rhs)); } if (!ShapeUtil::IsTuple(ehs_shape) && - !ShapeUtil::SameDimensions(instr.shape(), ehs_shape)) { + !ShapeUtil::SameDimensions(shape, ehs_shape)) { // ehs is being implicitly broadcasted. Change to explicit. - TF_ASSIGN_OR_RETURN(updated_ehs, - AddBroadcastSequence(instr.shape(), ehs)); + TF_ASSIGN_OR_RETURN(updated_ehs, AddBroadcastSequence(shape, ehs)); } } return AddInstruction(std::move(instr), triop, @@ -476,7 +493,7 @@ XlaOp XlaBuilder::Mul(const XlaOp& lhs, const XlaOp& rhs, XlaOp XlaBuilder::ConstantLiteral(const LiteralSlice& literal) { return ReportErrorOrReturn([&]() -> StatusOr { HloInstructionProto instr; - *instr.mutable_shape() = literal.shape(); + *instr.mutable_shape() = literal.shape().ToProto(); *instr.mutable_literal() = literal.ToProto(); return AddInstruction(std::move(instr), HloOpcode::kConstant); }); @@ -485,7 +502,7 @@ XlaOp XlaBuilder::ConstantLiteral(const LiteralSlice& literal) { XlaOp XlaBuilder::Iota(const Shape& shape, int64 iota_dimension) { return ReportErrorOrReturn([&]() -> StatusOr { HloInstructionProto instr; - *instr.mutable_shape() = shape; + *instr.mutable_shape() = shape.ToProto(); instr.add_dimensions(iota_dimension); return AddInstruction(std::move(instr), HloOpcode::kIota); }); @@ -505,10 +522,10 @@ XlaOp XlaBuilder::Call(const XlaComputation& computation, [](const Shape& shape) { return &shape; }); TF_ASSIGN_OR_RETURN(const ProgramShape& called_program_shape, computation.GetProgramShape()); - TF_ASSIGN_OR_RETURN( - *instr.mutable_shape(), - ShapeInference::InferCallShape(operand_shape_ptrs, - /*to_apply=*/called_program_shape)); + TF_ASSIGN_OR_RETURN(Shape shape, ShapeInference::InferCallShape( + operand_shape_ptrs, + /*to_apply=*/called_program_shape)); + *instr.mutable_shape() = shape.ToProto(); AddCalledComputation(computation, &instr); @@ -526,7 +543,7 @@ XlaOp XlaBuilder::Parameter(int64 parameter_number, const Shape& shape, } instr.set_parameter_number(parameter_number); instr.set_name(name); - *instr.mutable_shape() = shape; + *instr.mutable_shape() = shape.ToProto(); return AddInstruction(std::move(instr), HloOpcode::kParameter); }); } @@ -556,27 +573,35 @@ XlaOp XlaBuilder::Broadcast(const XlaOp& operand, } XlaOp XlaBuilder::BroadcastInDim( - const XlaOp& operand, const Shape& shape, + const XlaOp& operand, const absl::Span out_dim_size, const absl::Span broadcast_dimensions) { return ReportErrorOrReturn([&]() -> StatusOr { TF_ASSIGN_OR_RETURN(const Shape& operand_shape, GetShape(operand)); - TF_RETURN_IF_ERROR(ShapeInference::InferBroadcastShape(operand_shape, shape, - broadcast_dimensions) + // Output shape, in the case of degenerate broadcast, the out_dim_size is + // not necessarily the same as the dimension sizes of the output shape. + const auto& output_shape = + ShapeUtil::MakeShape(operand_shape.element_type(), out_dim_size); + + TF_RETURN_IF_ERROR(ShapeInference::InferBroadcastShape( + operand_shape, output_shape, broadcast_dimensions) .status()); - std::vector in_dim_size(ShapeUtil::Rank(shape)); - absl::c_copy(shape.dimensions(), in_dim_size.begin()); + std::vector in_dim_size(out_dim_size.begin(), out_dim_size.end()); for (int i = 0; i < broadcast_dimensions.size(); i++) { in_dim_size[broadcast_dimensions[i]] = operand_shape.dimensions(i); } const auto& in_dim_shape = - ShapeUtil::MakeShape(shape.element_type(), in_dim_size); + ShapeUtil::MakeShape(operand_shape.element_type(), in_dim_size); TF_ASSIGN_OR_RETURN( XlaOp in_dim_broadcast, InDimBroadcast(in_dim_shape, operand, broadcast_dimensions)); - if (ShapeUtil::Equal(in_dim_shape, shape)) { + + // If broadcast is not degenerate, return broadcasted result. + if (ShapeUtil::Equal(in_dim_shape, output_shape)) { return in_dim_broadcast; } - return AddBroadcastSequence(shape, in_dim_broadcast); + + // Otherwise handle degenerate broadcast case. + return AddBroadcastSequence(output_shape, in_dim_broadcast); }); } @@ -584,7 +609,7 @@ StatusOr XlaBuilder::Reshape(const Shape& shape, const XlaOp& operand) { TF_RETURN_IF_ERROR(first_error_); HloInstructionProto instr; - *instr.mutable_shape() = shape; + *instr.mutable_shape() = shape.ToProto(); return AddInstruction(std::move(instr), HloOpcode::kReshape, {operand}); } @@ -596,9 +621,9 @@ XlaOp XlaBuilder::Slice(const XlaOp& operand, HloInstructionProto instr; TF_ASSIGN_OR_RETURN(const Shape& operand_shape, GetShape(operand)); TF_ASSIGN_OR_RETURN( - *instr.mutable_shape(), - ShapeInference::InferSliceShape(operand_shape, start_indices, - limit_indices, strides)); + Shape shape, ShapeInference::InferSliceShape( + operand_shape, start_indices, limit_indices, strides)); + *instr.mutable_shape() = shape.ToProto(); for (int i = 0; i < start_indices.size(); i++) { auto* slice_config = instr.add_slice_dimensions(); slice_config->set_start(start_indices[i]); @@ -633,9 +658,10 @@ XlaOp XlaBuilder::DynamicSlice(const XlaOp& operand, const XlaOp& start_indices, TF_ASSIGN_OR_RETURN(const Shape& operand_shape, GetShape(operand)); TF_ASSIGN_OR_RETURN(const Shape& start_indices_shape, GetShape(start_indices)); - TF_ASSIGN_OR_RETURN(*instr.mutable_shape(), + TF_ASSIGN_OR_RETURN(Shape shape, ShapeInference::InferDynamicSliceShape( operand_shape, start_indices_shape, slice_sizes)); + *instr.mutable_shape() = shape.ToProto(); for (int64 size : slice_sizes) { instr.add_dynamic_slice_sizes(size); @@ -655,9 +681,10 @@ XlaOp XlaBuilder::DynamicUpdateSlice(const XlaOp& operand, const XlaOp& update, TF_ASSIGN_OR_RETURN(const Shape& update_shape, GetShape(update)); TF_ASSIGN_OR_RETURN(const Shape& start_indices_shape, GetShape(start_indices)); - TF_ASSIGN_OR_RETURN(*instr.mutable_shape(), + TF_ASSIGN_OR_RETURN(Shape shape, ShapeInference::InferDynamicUpdateSliceShape( operand_shape, update_shape, start_indices_shape)); + *instr.mutable_shape() = shape.ToProto(); return AddInstruction(std::move(instr), HloOpcode::kDynamicUpdateSlice, {operand, update, start_indices}); @@ -673,9 +700,9 @@ XlaOp XlaBuilder::ConcatInDim(absl::Span operands, TF_ASSIGN_OR_RETURN(const auto& operand_shapes, GetOperandShapes(operands)); absl::c_transform(operand_shapes, std::back_inserter(operand_shape_ptrs), [](const Shape& shape) { return &shape; }); - TF_ASSIGN_OR_RETURN( - *instr.mutable_shape(), - ShapeInference::InferConcatOpShape(operand_shape_ptrs, dimension)); + TF_ASSIGN_OR_RETURN(Shape shape, ShapeInference::InferConcatOpShape( + operand_shape_ptrs, dimension)); + *instr.mutable_shape() = shape.ToProto(); instr.add_dimensions(dimension); @@ -692,10 +719,9 @@ XlaOp XlaBuilder::Pad(const XlaOp& operand, const XlaOp& padding_value, TF_ASSIGN_OR_RETURN(const Shape& padding_value_shape, GetShape(padding_value)); TF_ASSIGN_OR_RETURN( - *instr.mutable_shape(), - ShapeInference::InferPadShape(operand_shape, padding_value_shape, - padding_config)); - + Shape shape, ShapeInference::InferPadShape( + operand_shape, padding_value_shape, padding_config)); + *instr.mutable_shape() = shape.ToProto(); *instr.mutable_padding_config() = padding_config; return AddInstruction(std::move(instr), HloOpcode::kPad, @@ -708,7 +734,7 @@ XlaOp XlaBuilder::Reshape(const XlaOp& operand, absl::Span new_sizes) { return ReportErrorOrReturn([&]() -> StatusOr { TF_ASSIGN_OR_RETURN(const Shape& operand_shape, GetShape(operand)); - TF_ASSIGN_OR_RETURN(const Shape& shape, + TF_ASSIGN_OR_RETURN(const Shape shape, ShapeInference::InferReshapeShape( operand_shape, dimensions, new_sizes)); XlaOp transposed = IsIdentityPermutation(dimensions) @@ -721,7 +747,7 @@ XlaOp XlaBuilder::Reshape(const XlaOp& operand, XlaOp XlaBuilder::Reshape(const XlaOp& operand, absl::Span new_sizes) { return ReportErrorOrReturn([&]() -> StatusOr { - TF_ASSIGN_OR_RETURN(auto shape, GetShape(operand)); + TF_ASSIGN_OR_RETURN(Shape shape, GetShape(operand)); std::vector dimensions(shape.dimensions_size()); std::iota(dimensions.begin(), dimensions.end(), 0); return Reshape(operand, dimensions, new_sizes); @@ -771,7 +797,7 @@ XlaOp XlaBuilder::Collapse(const XlaOp& operand, void XlaBuilder::Trace(const string& tag, const XlaOp& operand) { ReportErrorOrReturn([&]() -> StatusOr { HloInstructionProto instr; - *instr.mutable_shape() = ShapeUtil::MakeNil(); + *instr.mutable_shape() = ShapeUtil::MakeNil().ToProto(); *instr.mutable_literal() = LiteralUtil::CreateR1U8(tag).ToProto(); return AddInstruction(std::move(instr), HloOpcode::kTrace, {operand}); }); @@ -797,9 +823,10 @@ XlaOp XlaBuilder::Tuple(absl::Span elements) { TF_ASSIGN_OR_RETURN(const auto& operand_shapes, GetOperandShapes(elements)); absl::c_transform(operand_shapes, std::back_inserter(operand_shape_ptrs), [](const Shape& shape) { return &shape; }); - TF_ASSIGN_OR_RETURN(*instr.mutable_shape(), + TF_ASSIGN_OR_RETURN(const Shape shape, ShapeInference::InferVariadicOpShape( HloOpcode::kTuple, operand_shape_ptrs)); + *instr.mutable_shape() = shape.ToProto(); return AddInstruction(std::move(instr), HloOpcode::kTuple, elements); }); } @@ -814,7 +841,7 @@ XlaOp XlaBuilder::GetTupleElement(const XlaOp& tuple_data, int64 index) { ShapeUtil::HumanString(tuple_shape)); } *instr.mutable_shape() = - ShapeUtil::GetTupleElementShape(tuple_shape, index); + ShapeUtil::GetTupleElementShape(tuple_shape, index).ToProto(); instr.set_tuple_index(index); @@ -873,9 +900,10 @@ XlaOp XlaBuilder::DotGeneral(const XlaOp& lhs, const XlaOp& rhs, HloInstructionProto instr; TF_ASSIGN_OR_RETURN(const Shape& lhs_shape, GetShape(lhs)); TF_ASSIGN_OR_RETURN(const Shape& rhs_shape, GetShape(rhs)); - TF_ASSIGN_OR_RETURN(*instr.mutable_shape(), + TF_ASSIGN_OR_RETURN(Shape shape, ShapeInference::InferDotOpShape(lhs_shape, rhs_shape, dimension_numbers)); + *instr.mutable_shape() = shape.ToProto(); *instr.mutable_dot_dimension_numbers() = dimension_numbers; if (precision_config != nullptr) { *instr.mutable_precision_config() = *precision_config; @@ -1017,10 +1045,11 @@ XlaOp XlaBuilder::ConvGeneralDilated( MakeWindow(window_dimensions, window_strides, padding, lhs_dilation, rhs_dilation)); - TF_ASSIGN_OR_RETURN(*instr.mutable_shape(), + TF_ASSIGN_OR_RETURN(Shape shape, ShapeInference::InferConvolveShape( lhs_shape, rhs_shape, feature_group_count, instr.window(), dimension_numbers)); + *instr.mutable_shape() = shape.ToProto(); *instr.mutable_convolution_dimension_numbers() = dimension_numbers; instr.set_feature_group_count(feature_group_count); @@ -1093,10 +1122,9 @@ XlaOp XlaBuilder::Fft(const XlaOp& operand, const FftType fft_type, return ReportErrorOrReturn([&]() -> StatusOr { HloInstructionProto instr; TF_ASSIGN_OR_RETURN(const Shape& operand_shape, GetShape(operand)); - TF_ASSIGN_OR_RETURN( - *instr.mutable_shape(), - ShapeInference::InferFftShape(operand_shape, fft_type, fft_length)); - + TF_ASSIGN_OR_RETURN(Shape shape, ShapeInference::InferFftShape( + operand_shape, fft_type, fft_length)); + *instr.mutable_shape() = shape.ToProto(); instr.set_fft_type(fft_type); for (int64 i : fft_length) { instr.add_fft_length(i); @@ -1114,7 +1142,7 @@ XlaOp XlaBuilder::Infeed(const Shape& shape, const string& config) { } const Shape infeed_instruction_shape = ShapeUtil::MakeTupleShape({shape, ShapeUtil::MakeTokenShape()}); - *instr.mutable_shape() = infeed_instruction_shape; + *instr.mutable_shape() = infeed_instruction_shape.ToProto(); instr.set_infeed_config(config); if (ShapeUtil::IsArray(shape) && sharding() && @@ -1135,7 +1163,7 @@ XlaOp XlaBuilder::Infeed(const Shape& shape, const string& config) { XlaOp token; auto make_token = [&]() { HloInstructionProto token_instr; - *token_instr.mutable_shape() = ShapeUtil::MakeTokenShape(); + *token_instr.mutable_shape() = ShapeUtil::MakeTokenShape().ToProto(); return AddInstruction(std::move(token_instr), HloOpcode::kAfterAll, {}); }; if (sharding()) { @@ -1174,7 +1202,7 @@ XlaOp XlaBuilder::Infeed(const Shape& shape, const string& config) { // TODO(b/80000000): Remove this when clients have been updated to handle // tokens. HloInstructionProto infeed_data; - *infeed_data.mutable_shape() = shape; + *infeed_data.mutable_shape() = shape.ToProto(); infeed_data.set_tuple_index(0); return AddInstruction(std::move(infeed_data), HloOpcode::kGetTupleElement, {infeed}); @@ -1190,7 +1218,7 @@ XlaOp XlaBuilder::InfeedWithToken(const XlaOp& token, const Shape& shape, } const Shape infeed_instruction_shape = ShapeUtil::MakeTupleShape({shape, ShapeUtil::MakeTokenShape()}); - *instr.mutable_shape() = infeed_instruction_shape; + *instr.mutable_shape() = infeed_instruction_shape.ToProto(); instr.set_infeed_config(config); if (ShapeUtil::IsArray(shape) && sharding() && @@ -1215,7 +1243,7 @@ void XlaBuilder::Outfeed(const XlaOp& operand, const Shape& shape_with_layout, ReportErrorOrReturn([&]() -> StatusOr { HloInstructionProto instr; - *instr.mutable_shape() = ShapeUtil::MakeTokenShape(); + *instr.mutable_shape() = ShapeUtil::MakeTokenShape().ToProto(); // Check and set outfeed shape. if (!LayoutUtil::HasLayout(shape_with_layout)) { @@ -1228,14 +1256,14 @@ void XlaBuilder::Outfeed(const XlaOp& operand, const Shape& shape_with_layout, ShapeUtil::HumanStringWithLayout(shape_with_layout), ShapeUtil::HumanStringWithLayout(operand_shape)); } - *instr.mutable_outfeed_shape() = shape_with_layout; + *instr.mutable_outfeed_shape() = shape_with_layout.ToProto(); instr.set_outfeed_config(outfeed_config); // Outfeed takes a token as its second operand. Generate the token to pass // to the outfeed. HloInstructionProto token_instr; - *token_instr.mutable_shape() = ShapeUtil::MakeTokenShape(); + *token_instr.mutable_shape() = ShapeUtil::MakeTokenShape().ToProto(); TF_ASSIGN_OR_RETURN(XlaOp token, AddInstruction(std::move(token_instr), HloOpcode::kAfterAll, {})); @@ -1249,7 +1277,7 @@ void XlaBuilder::Outfeed(const XlaOp& operand, const Shape& shape_with_layout, // TODO(b/80000000): Remove this when clients have been updated to handle // tokens. HloInstructionProto tuple_instr; - *tuple_instr.mutable_shape() = ShapeUtil::MakeNil(); + *tuple_instr.mutable_shape() = ShapeUtil::MakeNil().ToProto(); // The dummy tuple should have no sharding. { @@ -1268,7 +1296,7 @@ XlaOp XlaBuilder::OutfeedWithToken(const XlaOp& operand, const XlaOp& token, return ReportErrorOrReturn([&]() -> StatusOr { HloInstructionProto instr; - *instr.mutable_shape() = ShapeUtil::MakeTokenShape(); + *instr.mutable_shape() = ShapeUtil::MakeTokenShape().ToProto(); // Check and set outfeed shape. if (!LayoutUtil::HasLayout(shape_with_layout)) { @@ -1281,7 +1309,7 @@ XlaOp XlaBuilder::OutfeedWithToken(const XlaOp& operand, const XlaOp& token, ShapeUtil::HumanStringWithLayout(shape_with_layout), ShapeUtil::HumanStringWithLayout(operand_shape)); } - *instr.mutable_outfeed_shape() = shape_with_layout; + *instr.mutable_outfeed_shape() = shape_with_layout.ToProto(); instr.set_outfeed_config(outfeed_config); @@ -1293,7 +1321,7 @@ XlaOp XlaBuilder::OutfeedWithToken(const XlaOp& operand, const XlaOp& token, XlaOp XlaBuilder::CreateToken() { return ReportErrorOrReturn([&]() -> StatusOr { HloInstructionProto instr; - *instr.mutable_shape() = ShapeUtil::MakeTokenShape(); + *instr.mutable_shape() = ShapeUtil::MakeTokenShape().ToProto(); return AddInstruction(std::move(instr), HloOpcode::kAfterAll); }); } @@ -1303,8 +1331,17 @@ XlaOp XlaBuilder::AfterAll(absl::Span tokens) { if (tokens.empty()) { return InvalidArgument("AfterAll requires at least one operand"); } + for (int i = 0; i < tokens.size(); ++i) { + const XlaOp& operand = tokens[i]; + TF_ASSIGN_OR_RETURN(const Shape& operand_shape, GetShape(operand)); + if (!ShapeUtil::IsToken(operand_shape)) { + return InvalidArgument( + "All operands to AfterAll must be tokens; operand %d has shape %s", + i, ShapeUtil::HumanString(operand_shape)); + } + } HloInstructionProto instr; - *instr.mutable_shape() = ShapeUtil::MakeTokenShape(); + *instr.mutable_shape() = ShapeUtil::MakeTokenShape().ToProto(); return AddInstruction(std::move(instr), HloOpcode::kAfterAll, tokens); }); } @@ -1321,7 +1358,7 @@ XlaOp XlaBuilder::CustomCall( "are reserved for internal use.", call_target_name); } - *instr.mutable_shape() = shape; + *instr.mutable_shape() = shape.ToProto(); instr.set_custom_call_target(call_target_name); instr.set_custom_call_opaque(opaque); if (operand_shapes_with_layout.has_value()) { @@ -1345,7 +1382,7 @@ XlaOp XlaBuilder::CustomCall( "constrained layout.", operand_num); } - *instr.add_operand_shapes_with_layout() = operand_shape; + *instr.add_operand_shapes_with_layout() = operand_shape.ToProto(); ++operand_num; } } @@ -1499,9 +1536,9 @@ XlaOp XlaBuilder::Transpose(const XlaOp& operand, return ReportErrorOrReturn([&]() -> StatusOr { HloInstructionProto instr; TF_ASSIGN_OR_RETURN(const Shape& operand_shape, GetShape(operand)); - TF_ASSIGN_OR_RETURN( - *instr.mutable_shape(), - ShapeInference::InferTransposeShape(operand_shape, permutation)); + TF_ASSIGN_OR_RETURN(Shape shape, ShapeInference::InferTransposeShape( + operand_shape, permutation)); + *instr.mutable_shape() = shape.ToProto(); for (int64 dim : permutation) { instr.add_dimensions(dim); } @@ -1514,9 +1551,9 @@ XlaOp XlaBuilder::Rev(const XlaOp& operand, return ReportErrorOrReturn([&]() -> StatusOr { HloInstructionProto instr; TF_ASSIGN_OR_RETURN(const Shape& operand_shape, GetShape(operand)); - TF_ASSIGN_OR_RETURN( - *instr.mutable_shape(), - ShapeInference::InferReverseShape(operand_shape, dimensions)); + TF_ASSIGN_OR_RETURN(Shape shape, ShapeInference::InferReverseShape( + operand_shape, dimensions)); + *instr.mutable_shape() = shape.ToProto(); for (int64 dim : dimensions) { instr.add_dimensions(dim); } @@ -1535,9 +1572,9 @@ XlaOp XlaBuilder::Sort(const XlaOp& keys, absl::Span values, GetOperandShapes(values)); absl::c_transform(values_shapes, std::back_inserter(operand_shape_ptrs), [](const Shape& shape) { return &shape; }); - TF_ASSIGN_OR_RETURN(*instr.mutable_shape(), - ShapeInference::InferVariadicOpShape( - HloOpcode::kSort, operand_shape_ptrs)); + TF_ASSIGN_OR_RETURN(Shape shape, ShapeInference::InferVariadicOpShape( + HloOpcode::kSort, operand_shape_ptrs)); + *instr.mutable_shape() = shape.ToProto(); if (dimension == -1) { TF_ASSIGN_OR_RETURN(const Shape& keys_shape, GetShape(keys)); dimension = ShapeUtil::Rank(keys_shape) - 1; @@ -1559,9 +1596,9 @@ XlaOp XlaBuilder::ConvertElementType(const XlaOp& operand, return ReportErrorOrReturn([&]() -> StatusOr { HloInstructionProto instr; TF_ASSIGN_OR_RETURN(const Shape& operand_shape, GetShape(operand)); - TF_ASSIGN_OR_RETURN( - *instr.mutable_shape(), - ShapeInference::InferConvertShape(operand_shape, new_element_type)); + TF_ASSIGN_OR_RETURN(Shape shape, ShapeInference::InferConvertShape( + operand_shape, new_element_type)); + *instr.mutable_shape() = shape.ToProto(); return AddInstruction(std::move(instr), HloOpcode::kConvert, {operand}); }); } @@ -1571,9 +1608,9 @@ XlaOp XlaBuilder::BitcastConvertType(const XlaOp& operand, return ReportErrorOrReturn([&]() -> StatusOr { HloInstructionProto instr; TF_ASSIGN_OR_RETURN(const Shape& operand_shape, GetShape(operand)); - TF_ASSIGN_OR_RETURN( - *instr.mutable_shape(), - ShapeInference::InferConvertShape(operand_shape, new_element_type)); + TF_ASSIGN_OR_RETURN(Shape shape, ShapeInference::InferConvertShape( + operand_shape, new_element_type)); + *instr.mutable_shape() = shape.ToProto(); return AddInstruction(std::move(instr), HloOpcode::kBitcastConvert, {operand}); }); @@ -1605,11 +1642,11 @@ XlaOp XlaBuilder::Map(absl::Span operands, TF_ASSIGN_OR_RETURN(const ProgramShape& called_program_shape, computation.GetProgramShape()); TF_ASSIGN_OR_RETURN( - *instr.mutable_shape(), - ShapeInference::InferMapShape(operand_shape_ptrs, called_program_shape, - dimensions)); + Shape shape, ShapeInference::InferMapShape( + operand_shape_ptrs, called_program_shape, dimensions)); + *instr.mutable_shape() = shape.ToProto(); - const Shape& output_shape = instr.shape(); + Shape output_shape(instr.shape()); const int64 output_rank = ShapeUtil::Rank(output_shape); AddCalledComputation(computation, &instr); std::vector new_operands(operands.begin(), operands.end()); @@ -1652,7 +1689,7 @@ XlaOp XlaBuilder::RngOp(RandomDistribution distribution, } TF_RETURN_IF_ERROR(ShapeUtil::ValidateShapeWithOptionalLayout(shape)); - *instr.mutable_shape() = shape; + *instr.mutable_shape() = shape.ToProto(); instr.set_distribution(distribution); @@ -1680,10 +1717,10 @@ XlaOp XlaBuilder::While(const XlaComputation& condition, TF_ASSIGN_OR_RETURN(const auto& condition_program_shape, condition.GetProgramShape()); TF_ASSIGN_OR_RETURN(const Shape& init_shape, GetShape(init)); - TF_ASSIGN_OR_RETURN( - *instr.mutable_shape(), - ShapeInference::InferWhileShape(condition_program_shape, - body_program_shape, init_shape)); + TF_ASSIGN_OR_RETURN(Shape shape, ShapeInference::InferWhileShape( + condition_program_shape, + body_program_shape, init_shape)); + *instr.mutable_shape() = shape.ToProto(); // Body comes before condition computation in the vector. AddCalledComputation(body, &instr); AddCalledComputation(condition, &instr); @@ -1700,10 +1737,10 @@ XlaOp XlaBuilder::Gather(const XlaOp& input, const XlaOp& start_indices, TF_ASSIGN_OR_RETURN(const Shape& input_shape, GetShape(input)); TF_ASSIGN_OR_RETURN(const Shape& start_indices_shape, GetShape(start_indices)); - TF_ASSIGN_OR_RETURN( - *instr.mutable_shape(), - ShapeInference::InferGatherShape(input_shape, start_indices_shape, + TF_ASSIGN_OR_RETURN(Shape shape, ShapeInference::InferGatherShape( + input_shape, start_indices_shape, dimension_numbers, slice_sizes)); + *instr.mutable_shape() = shape.ToProto(); *instr.mutable_gather_dimension_numbers() = dimension_numbers; for (int64 bound : slice_sizes) { @@ -1728,10 +1765,11 @@ XlaOp XlaBuilder::Scatter(const XlaOp& input, const XlaOp& scatter_indices, TF_ASSIGN_OR_RETURN(const Shape& updates_shape, GetShape(updates)); TF_ASSIGN_OR_RETURN(const ProgramShape& to_apply_shape, update_computation.GetProgramShape()); - TF_ASSIGN_OR_RETURN(*instr.mutable_shape(), + TF_ASSIGN_OR_RETURN(Shape shape, ShapeInference::InferScatterShape( input_shape, scatter_indices_shape, updates_shape, to_apply_shape, dimension_numbers)); + *instr.mutable_shape() = shape.ToProto(); *instr.mutable_scatter_dimension_numbers() = dimension_numbers; @@ -1758,10 +1796,11 @@ XlaOp XlaBuilder::Conditional(const XlaOp& predicate, const XlaOp& true_operand, TF_ASSIGN_OR_RETURN(const ProgramShape& false_computation_shape, false_computation.GetProgramShape()); TF_ASSIGN_OR_RETURN( - *instr.mutable_shape(), + Shape shape, ShapeInference::InferConditionalShape( predicate_shape, true_operand_shape, false_operand_shape, true_computation_shape, false_computation_shape)); + *instr.mutable_shape() = shape.ToProto(); // The index of true_computation must be 0 and that of false computation // must be 1. @@ -1803,9 +1842,10 @@ XlaOp XlaBuilder::Reduce(absl::Span operands, [](const Shape& shape) { return &shape; }); TF_ASSIGN_OR_RETURN( - *instr.mutable_shape(), + Shape shape, ShapeInference::InferReduceShape( operand_shape_ptrs, dimensions_to_reduce, called_program_shape)); + *instr.mutable_shape() = shape.ToProto(); for (int64 dim : dimensions_to_reduce) { instr.add_dimensions(dim); @@ -1868,10 +1908,10 @@ XlaOp XlaBuilder::ReduceWindowWithGeneralPadding( MakeWindow(window_dimensions, window_strides, padding, /*lhs_dilation=*/base_dilations, /*rhs_dilation=*/window_dilations)); - TF_ASSIGN_OR_RETURN( - *instr.mutable_shape(), - ShapeInference::InferReduceWindowShape(operand_shape, init_shape, - instr.window(), to_apply_shape)); + TF_ASSIGN_OR_RETURN(Shape shape, ShapeInference::InferReduceWindowShape( + operand_shape, init_shape, + instr.window(), to_apply_shape)); + *instr.mutable_shape() = shape.ToProto(); AddCalledComputation(computation, &instr); return AddInstruction(std::move(instr), HloOpcode::kReduceWindow, @@ -1889,9 +1929,10 @@ XlaOp XlaBuilder::BatchNormTraining(const XlaOp& operand, const XlaOp& scale, TF_ASSIGN_OR_RETURN(const Shape& scale_shape, GetShape(scale)); TF_ASSIGN_OR_RETURN(const Shape& offset_shape, GetShape(offset)); TF_ASSIGN_OR_RETURN( - *instr.mutable_shape(), + Shape shape, ShapeInference::InferBatchNormTrainingShape( operand_shape, scale_shape, offset_shape, feature_index)); + *instr.mutable_shape() = shape.ToProto(); instr.set_epsilon(epsilon); instr.set_feature_index(feature_index); @@ -1913,10 +1954,11 @@ XlaOp XlaBuilder::BatchNormInference(const XlaOp& operand, const XlaOp& scale, TF_ASSIGN_OR_RETURN(const Shape& offset_shape, GetShape(offset)); TF_ASSIGN_OR_RETURN(const Shape& mean_shape, GetShape(mean)); TF_ASSIGN_OR_RETURN(const Shape& variance_shape, GetShape(variance)); - TF_ASSIGN_OR_RETURN(*instr.mutable_shape(), - ShapeInference::InferBatchNormInferenceShape( - operand_shape, scale_shape, offset_shape, - mean_shape, variance_shape, feature_index)); + TF_ASSIGN_OR_RETURN( + Shape shape, ShapeInference::InferBatchNormInferenceShape( + operand_shape, scale_shape, offset_shape, mean_shape, + variance_shape, feature_index)); + *instr.mutable_shape() = shape.ToProto(); instr.set_epsilon(epsilon); instr.set_feature_index(feature_index); @@ -1938,10 +1980,11 @@ XlaOp XlaBuilder::BatchNormGrad(const XlaOp& operand, const XlaOp& scale, TF_ASSIGN_OR_RETURN(const Shape& batch_mean_shape, GetShape(batch_mean)); TF_ASSIGN_OR_RETURN(const Shape& batch_var_shape, GetShape(batch_var)); TF_ASSIGN_OR_RETURN(const Shape& grad_output_shape, GetShape(grad_output)); - TF_ASSIGN_OR_RETURN(*instr.mutable_shape(), + TF_ASSIGN_OR_RETURN(Shape shape, ShapeInference::InferBatchNormGradShape( operand_shape, scale_shape, batch_mean_shape, batch_var_shape, grad_output_shape, feature_index)); + *instr.mutable_shape() = shape.ToProto(); instr.set_epsilon(epsilon); instr.set_feature_index(feature_index); @@ -1972,9 +2015,9 @@ XlaOp XlaBuilder::CrossReplicaSum( return ReportErrorOrReturn([&]() -> StatusOr { HloInstructionProto instr; TF_ASSIGN_OR_RETURN(const Shape& operand_shape, GetShape(operand)); - TF_ASSIGN_OR_RETURN( - *instr.mutable_shape(), - ShapeInference::InferCrossReplicaSumShape({&operand_shape})); + TF_ASSIGN_OR_RETURN(Shape shape, ShapeInference::InferCrossReplicaSumShape( + {&operand_shape})); + *instr.mutable_shape() = shape.ToProto(); for (const ReplicaGroup& group : replica_groups) { *instr.add_replica_groups() = group; @@ -2027,8 +2070,8 @@ XlaOp XlaBuilder::AllToAll(const XlaOp& operand, int64 split_dimension, absl::c_transform(slice_shapes, std::back_inserter(slice_shape_ptrs), [](const Shape& shape) { return &shape; }); TF_ASSIGN_OR_RETURN( - *instr.mutable_shape(), - ShapeInference::InferAllToAllTupleShape(slice_shape_ptrs)); + Shape shape, ShapeInference::InferAllToAllTupleShape(slice_shape_ptrs)); + *instr.mutable_shape() = shape.ToProto(); for (const ReplicaGroup& group : replica_groups) { *instr.add_replica_groups() = group; } @@ -2053,8 +2096,9 @@ XlaOp XlaBuilder::CollectivePermute( TF_ASSIGN_OR_RETURN(const Shape& operand_shape, GetShape(operand)); HloInstructionProto instr; TF_ASSIGN_OR_RETURN( - *instr.mutable_shape(), + Shape shape, ShapeInference::InferCollectivePermuteShape(operand_shape)); + *instr.mutable_shape() = shape.ToProto(); for (const auto& pair : source_target_pairs) { auto* proto_pair = instr.add_source_target_pairs(); @@ -2103,10 +2147,11 @@ XlaOp XlaBuilder::SelectAndScatterWithGeneralPadding( TF_ASSIGN_OR_RETURN(*instr.mutable_window(), MakeWindow(window_dimensions, window_strides, padding, /*lhs_dilation=*/{}, /*rhs_dilation=*/{})); - TF_ASSIGN_OR_RETURN(*instr.mutable_shape(), + TF_ASSIGN_OR_RETURN(Shape shape, ShapeInference::InferSelectAndScatterShape( operand_shape, select_shape, instr.window(), source_shape, init_shape, scatter_shape)); + *instr.mutable_shape() = shape.ToProto(); AddCalledComputation(select, &instr); AddCalledComputation(scatter, &instr); @@ -2121,9 +2166,10 @@ XlaOp XlaBuilder::ReducePrecision(const XlaOp& operand, const int exponent_bits, return ReportErrorOrReturn([&]() -> StatusOr { HloInstructionProto instr; TF_ASSIGN_OR_RETURN(const Shape& operand_shape, GetShape(operand)); - TF_ASSIGN_OR_RETURN(*instr.mutable_shape(), + TF_ASSIGN_OR_RETURN(Shape shape, ShapeInference::InferReducePrecisionShape( operand_shape, exponent_bits, mantissa_bits)); + *instr.mutable_shape() = shape.ToProto(); instr.set_exponent_bits(exponent_bits); instr.set_mantissa_bits(mantissa_bits); return AddInstruction(std::move(instr), HloOpcode::kReducePrecision, @@ -2138,7 +2184,7 @@ void XlaBuilder::Send(const XlaOp& operand, const ChannelHandle& handle) { // TODO(b/80000000): Remove this when clients have been updated to handle // tokens. HloInstructionProto token_instr; - *token_instr.mutable_shape() = ShapeUtil::MakeTokenShape(); + *token_instr.mutable_shape() = ShapeUtil::MakeTokenShape().ToProto(); TF_ASSIGN_OR_RETURN(XlaOp token, AddInstruction(std::move(token_instr), HloOpcode::kAfterAll, {})); @@ -2157,15 +2203,17 @@ XlaOp XlaBuilder::SendWithToken(const XlaOp& operand, const XlaOp& token, // token}. HloInstructionProto send_instr; TF_ASSIGN_OR_RETURN(const Shape& shape, GetShape(operand)); - *send_instr.mutable_shape() = ShapeUtil::MakeTupleShape( - {shape, ShapeUtil::MakeShape(U32, {}), ShapeUtil::MakeTokenShape()}); + *send_instr.mutable_shape() = + ShapeUtil::MakeTupleShape( + {shape, ShapeUtil::MakeShape(U32, {}), ShapeUtil::MakeTokenShape()}) + .ToProto(); send_instr.set_channel_id(handle.handle()); TF_ASSIGN_OR_RETURN(XlaOp send, AddInstruction(std::move(send_instr), HloOpcode::kSend, {operand, token})); HloInstructionProto send_done_instr; - *send_done_instr.mutable_shape() = ShapeUtil::MakeTokenShape(); + *send_done_instr.mutable_shape() = ShapeUtil::MakeTokenShape().ToProto(); send_done_instr.set_channel_id(handle.handle()); return AddInstruction(std::move(send_done_instr), HloOpcode::kSendDone, {send}); @@ -2179,7 +2227,7 @@ XlaOp XlaBuilder::Recv(const Shape& shape, const ChannelHandle& handle) { // TODO(b/80000000): Remove this when clients have been updated to handle // tokens. HloInstructionProto token_instr; - *token_instr.mutable_shape() = ShapeUtil::MakeTokenShape(); + *token_instr.mutable_shape() = ShapeUtil::MakeTokenShape().ToProto(); TF_ASSIGN_OR_RETURN(XlaOp token, AddInstruction(std::move(token_instr), HloOpcode::kAfterAll, {})); @@ -2190,7 +2238,7 @@ XlaOp XlaBuilder::Recv(const Shape& shape, const ChannelHandle& handle) { // TODO(b/80000000): Remove this when clients have been updated to handle // tokens. HloInstructionProto recv_data; - *recv_data.mutable_shape() = shape; + *recv_data.mutable_shape() = shape.ToProto(); recv_data.set_tuple_index(0); return AddInstruction(std::move(recv_data), HloOpcode::kGetTupleElement, {recv}); @@ -2207,15 +2255,18 @@ XlaOp XlaBuilder::RecvWithToken(const XlaOp& token, const Shape& shape, // Recv instruction produces a tuple of {receive buffer, U32 context, // token}. HloInstructionProto recv_instr; - *recv_instr.mutable_shape() = ShapeUtil::MakeTupleShape( - {shape, ShapeUtil::MakeShape(U32, {}), ShapeUtil::MakeTokenShape()}); + *recv_instr.mutable_shape() = + ShapeUtil::MakeTupleShape( + {shape, ShapeUtil::MakeShape(U32, {}), ShapeUtil::MakeTokenShape()}) + .ToProto(); recv_instr.set_channel_id(handle.handle()); TF_ASSIGN_OR_RETURN(XlaOp recv, AddInstruction(std::move(recv_instr), HloOpcode::kRecv, {token})); HloInstructionProto recv_done_instr; *recv_done_instr.mutable_shape() = - ShapeUtil::MakeTupleShape({shape, ShapeUtil::MakeTokenShape()}); + ShapeUtil::MakeTupleShape({shape, ShapeUtil::MakeTokenShape()}) + .ToProto(); recv_done_instr.set_channel_id(handle.handle()); return AddInstruction(std::move(recv_done_instr), HloOpcode::kRecvDone, {recv}); @@ -2249,9 +2300,11 @@ XlaOp XlaBuilder::SendToHost(const XlaOp& operand, const XlaOp& token, // Send instruction produces a tuple of {aliased operand, U32 context, // token}. HloInstructionProto send_instr; - *send_instr.mutable_shape() = ShapeUtil::MakeTupleShape( - {shape_with_layout, ShapeUtil::MakeShape(U32, {}), - ShapeUtil::MakeTokenShape()}); + *send_instr.mutable_shape() = + ShapeUtil::MakeTupleShape({shape_with_layout, + ShapeUtil::MakeShape(U32, {}), + ShapeUtil::MakeTokenShape()}) + .ToProto(); send_instr.set_channel_id(handle.handle()); send_instr.set_is_host_transfer(true); TF_ASSIGN_OR_RETURN(XlaOp send, @@ -2259,7 +2312,7 @@ XlaOp XlaBuilder::SendToHost(const XlaOp& operand, const XlaOp& token, {operand, token})); HloInstructionProto send_done_instr; - *send_done_instr.mutable_shape() = ShapeUtil::MakeTokenShape(); + *send_done_instr.mutable_shape() = ShapeUtil::MakeTokenShape().ToProto(); send_done_instr.set_channel_id(handle.handle()); send_done_instr.set_is_host_transfer(true); return AddInstruction(std::move(send_done_instr), HloOpcode::kSendDone, @@ -2288,8 +2341,10 @@ XlaOp XlaBuilder::RecvFromHost(const XlaOp& token, const Shape& shape, // Recv instruction produces a tuple of {receive buffer, U32 context, // token}. HloInstructionProto recv_instr; - *recv_instr.mutable_shape() = ShapeUtil::MakeTupleShape( - {shape, ShapeUtil::MakeShape(U32, {}), ShapeUtil::MakeTokenShape()}); + *recv_instr.mutable_shape() = + ShapeUtil::MakeTupleShape( + {shape, ShapeUtil::MakeShape(U32, {}), ShapeUtil::MakeTokenShape()}) + .ToProto(); recv_instr.set_channel_id(handle.handle()); recv_instr.set_is_host_transfer(true); TF_ASSIGN_OR_RETURN(XlaOp recv, AddInstruction(std::move(recv_instr), @@ -2297,7 +2352,8 @@ XlaOp XlaBuilder::RecvFromHost(const XlaOp& token, const Shape& shape, HloInstructionProto recv_done_instr; *recv_done_instr.mutable_shape() = - ShapeUtil::MakeTupleShape({shape, ShapeUtil::MakeTokenShape()}); + ShapeUtil::MakeTupleShape({shape, ShapeUtil::MakeTokenShape()}) + .ToProto(); recv_done_instr.set_channel_id(handle.handle()); recv_done_instr.set_is_host_transfer(true); return AddInstruction(std::move(recv_done_instr), HloOpcode::kRecvDone, @@ -2309,9 +2365,9 @@ XlaOp XlaBuilder::GetDimensionSize(const XlaOp& operand, int64 dimension) { return ReportErrorOrReturn([&]() -> StatusOr { HloInstructionProto instr; TF_ASSIGN_OR_RETURN(const auto& operand_shape, GetShape(operand)); - TF_ASSIGN_OR_RETURN( - *instr.mutable_shape(), - ShapeInference::InferGetDimensionSizeShape(operand_shape, dimension)); + TF_ASSIGN_OR_RETURN(Shape shape, ShapeInference::InferGetDimensionSizeShape( + operand_shape, dimension)); + *instr.mutable_shape() = shape.ToProto(); instr.add_dimensions(dimension); return AddInstruction(std::move(instr), HloOpcode::kGetDimensionSize, {operand}); @@ -2356,7 +2412,7 @@ StatusOr XlaBuilder::BuildConstantSubGraph( SetProtoIdAndName(&entry, StrCat(name_, "_compute_constant"), kNameSeparator, GetNextId()); entry.set_root_id(root->id()); - ProgramShape* program_shape = entry.mutable_program_shape(); + ProgramShapeProto* program_shape = entry.mutable_program_shape(); *program_shape->mutable_result() = root->shape(); // We use std::set to keep the instruction ids in ascending order (which is @@ -2617,9 +2673,10 @@ XlaOp Broadcast(const XlaOp& operand, absl::Span broadcast_sizes) { return operand.builder()->Broadcast(operand, broadcast_sizes); } -XlaOp BroadcastInDim(const XlaOp& operand, const Shape& shape, +XlaOp BroadcastInDim(const XlaOp& operand, + const absl::Span out_dim_size, const absl::Span broadcast_dimensions) { - return operand.builder()->BroadcastInDim(operand, shape, + return operand.builder()->BroadcastInDim(operand, out_dim_size, broadcast_dimensions); } diff --git a/tensorflow/compiler/xla/client/xla_builder.h b/tensorflow/compiler/xla/client/xla_builder.h index 68314a026e..098efb60f9 100644 --- a/tensorflow/compiler/xla/client/xla_builder.h +++ b/tensorflow/compiler/xla/client/xla_builder.h @@ -29,6 +29,7 @@ limitations under the License. #include "tensorflow/compiler/xla/client/xla_computation.h" #include "tensorflow/compiler/xla/literal.h" #include "tensorflow/compiler/xla/literal_util.h" +#include "tensorflow/compiler/xla/service/dynamic_parameter_binding.h" #include "tensorflow/compiler/xla/service/hlo.pb.h" #include "tensorflow/compiler/xla/service/hlo_opcode.h" #include "tensorflow/compiler/xla/shape_util.h" @@ -263,35 +264,30 @@ class XlaBuilder { // evaluating the computation. StatusOr IsConstant(const XlaOp& operand) const; + // Sets up binding which indicates that the `target_dim_num` in the subshape + // `target_param_index` of parameter `target_param_num` is a dynamic dimension + // and its real dynamic size is represented by `dynamic_param_index` in + // parameter `dynamic_param_num`. + // + // TODO(b/119520625): Remove this API once we have more dynamic shape infra + // ready. + Status SetDynamicBinding(int64 dynamic_size_param_num, + ShapeIndex dynamic_size_param_index, + int64 target_param_num, + ShapeIndex target_param_index, int64 target_dim_num); + private: // Build helper which takes the id of the root operation.. StatusOr Build(int64 root_id); - // Enqueues a "retrieve parameter value" instruction for a parameter that was - // passed to the computation. + // Description for the methods below can be found in the corresponding public + // functions section in this file. + XlaOp Parameter(int64 parameter_number, const Shape& shape, const string& name); - // Enqueues a constant with the value of the given literal onto the - // computation. XlaOp ConstantLiteral(const LiteralSlice& literal); - // Enqueues a constant onto the computation. Methods are templated on the - // native host type (NativeT) which corresponds to a specific XLA - // PrimitiveType as given in the following table: - // - // Native Type PrimitiveType - // ----------------------------- - // bool PRED - // int32 S32 - // int64 S64 - // uint32 U32 - // uint64 U64 - // float F32 - // double F64 - // - // Note: not all primitive types defined in xla_data.proto have a - // corresponding native type yet. template XlaOp ConstantR0(NativeT value); template @@ -321,181 +317,79 @@ class XlaBuilder { template XlaOp ConstantR4FromArray4D(const Array4D& values); - // Enqueues a rank one constant (vector) onto the computation. The vector has - // size 'length' and every element has the value 'value'. template XlaOp ConstantR1(int64 length, NativeT value); - // Adds dimensions to an array by duplicating the data in the array. - // - // The new dimensions are inserted on the left, i.e. if - // broadcast_sizes has values {a0, ..., aN} and the operand shape - // has dimensions {b0, ..., bM} then the shape of the output has - // dimensions {a0, ..., aN, b0, ..., bM}. - // - // The new dimensions index into copies of the operand, i.e. - // - // output[i0, ..., iN, j0, ..., jM] = operand[j0, ..., jM] XlaOp Broadcast(const XlaOp& operand, absl::Span broadcast_sizes); - XlaOp BroadcastInDim(const XlaOp& operand, const Shape& shape, + XlaOp BroadcastInDim(const XlaOp& operand, + const absl::Span out_dim_size, const absl::Span broadcast_dimensions); - // Enqueues a pad operation onto the computation that pads the given value on - // the edges as well as between the elements of the input. padding_config - // specifies the padding amount for each dimension. XlaOp Pad(const XlaOp& operand, const XlaOp& padding_value, const PaddingConfig& padding_config); - // Enqueues an operation onto the computation that flattens the operand based - // on the dimension order (major/slowest-varying to minor/fastest-varying) - // given, followed by reshaping it into the shape with the given dimension - // sizes (also major to minor). Conceptually, this is a limited form of - // "shape casting". XlaOp Reshape(const XlaOp& operand, absl::Span dimensions, absl::Span new_sizes); - // Enqueues an operation onto the computation that collapses the operand, from - // first to last dimension (C order), then reshapes it to the given dimension - // sizes. Conceptually, this is a limited form of "shape casting". XlaOp Reshape(const XlaOp& operand, absl::Span new_sizes); - // Wrapper for Reshape. - // Enqueues an operation to collapse the provided dimensions; e.g. an - // operand with dimensions {x=256, y=2, z=2, p=32} can be collapsed to - // {x=1024, y=32} by collapsing dims {0, 1, 2}. Collapsing dimensions must - // be a consecutive, in-order subsequence of the operand dimensions. - // - // Note that collapsing a single dimension does nothing: - // - // {256} collapsing {0} => {256} - // {1} collapsing {0} => {1} - // - // Collapsing multiple dimensions produces a single result dimension: - // - // {256, 2} collapsing {0,1} => {512} - // {256, 2, 3} collapsing {0,1} => {512, 3} - // - // This could potentially cause data to be moved -- it provides a more - // structured form of reshaping than an arbitrary Reshape operation. XlaOp Collapse(const XlaOp& operand, absl::Span dimensions); - // Enqueues a slice operation onto the computation that slices the operand - // from the start indices to the limit indices; e.g. - // - // x - // [ 0 1 2 3 ] - // y [ 4 5 6 7 ] => slice(start={1, 1}, limit={2, 3}) => [ 5 6 ] - // [ 8 9 a b ] - // - // Note that "limit" means up-to-but-not-including; i.e. [start, limit) in 1D - // range notation. - // The strides parameter determines the stride over the slice XlaOp Slice(const XlaOp& operand, absl::Span start_indices, absl::Span limit_indices, absl::Span strides); - // Enqueues a slice operation in a given dimension, taking all other - // dimensions as they are; e.g. if dimno is 1 from start_index 2 to - // limit_index 4 by 1, and the shape is f32[7,8,9], this call is short-hand - // for: - // - // array[:, 2:4:1, :] XlaOp SliceInDim(const XlaOp& operand, int64 start_index, int64 limit_index, int64 stride, int64 dimno); - // Enqueues a slice operation onto the computation that slices the 'operand' - // from dynamic start indices which are passed in 'start_indices'. - // The size of the slice in each dimension is passed in 'slice_sizes', - // which specify the end point of exclusive slice intervals in each - // dimension [start, start + size). - // The shape of 'start_indices' must be rank == 1, with dimension size - // equal to the rank of the 'operand'. - // Slice index calculations are computed modulo input dimension sizes to - // prevent dynamic start indices from generating out-of-bound array accesses. XlaOp DynamicSlice(const XlaOp& operand, const XlaOp& start_indices, absl::Span slice_sizes); - // Enqueues a dynamic update slice operation onto the computation, which - // updates a slice of 'operand' with 'update' at dynamic 'start_indices'. - // The shape of 'update' determines the shape of the slice of 'operand' - // which is updated. - // The indices specified in 'start_indices' specify the offset of the slice - // of 'operand' which is updated. - // - // update = {10, 11} // calculated at runtime. - // [1 2 3] start = {1, 1} // calculated at runtime. [1 2 3 ] - // [4 5 6] => DynamicUpdateslice(data, update, start) => [4 10 11] - // [7 8 9] [7 8 9 ] - // - // The shape of 'start_indices' must be rank == 1, with dimension size - // equal to the rank of the 'operand'. - // Slice index calculations are computed modulo update dimension sizes to - // prevent dynamic start indices from generating out-of-bound array accesses. XlaOp DynamicUpdateSlice(const XlaOp& operand, const XlaOp& update, const XlaOp& start_indices); - // Enqueues a concatenate instruction onto the computation. 'operands' must - // have >= 1 entry. XlaOp ConcatInDim(absl::Span operands, int64 dimension); - // Enqueue a tracing operation onto the computation; the computation will emit - // a logging message with the operand. void Trace(const string& tag, const XlaOp& operand); - // Enqueues a conditional-move-like select operation onto the computation; - // predicated on pred, selects between on_true and on_false. XlaOp Select(const XlaOp& pred, const XlaOp& on_true, const XlaOp& on_false); - // Enqueues a tuple-creation instruction onto the computation. XlaOp Tuple(absl::Span elements); - // Enqueues a tuple-element-get instruction onto the computation. XlaOp GetTupleElement(const XlaOp& tuple_data, int64 index); - // Enqueues an equal-to comparison instruction onto the computation. XlaOp Eq(const XlaOp& lhs, const XlaOp& rhs, absl::Span broadcast_dimensions = {}); - // Enqueues a not-equal comparison instruction onto the computation. XlaOp Ne(const XlaOp& lhs, const XlaOp& rhs, absl::Span broadcast_dimensions = {}); - // Enqueues a greater-or-equal comparison instruction onto the computation. XlaOp Ge(const XlaOp& lhs, const XlaOp& rhs, absl::Span broadcast_dimensions = {}); - // Enqueues a greater-than comparison instruction onto the computation. XlaOp Gt(const XlaOp& lhs, const XlaOp& rhs, absl::Span broadcast_dimensions = {}); - // Enqueues a less-than comparison instruction onto the computation. XlaOp Lt(const XlaOp& lhs, const XlaOp& rhs, absl::Span broadcast_dimensions = {}); - // Enqueues a less-or-equal comparison instruction onto the computation. XlaOp Le(const XlaOp& lhs, const XlaOp& rhs, absl::Span broadcast_dimensions = {}); - // Enqueues a dot instruction onto the computation. XlaOp Dot(const XlaOp& lhs, const XlaOp& rhs, const PrecisionConfig* precision_config = nullptr); - // Enqueues a general dot instruction onto the computation. XlaOp DotGeneral(const XlaOp& lhs, const XlaOp& rhs, const DotDimensionNumbers& dimension_numbers, const PrecisionConfig* precision_config = nullptr); - // Enqueues a convolution instruction onto the computation, which uses the - // default convolution dimension numbers. XlaOp Conv(const XlaOp& lhs, const XlaOp& rhs, absl::Span window_strides, Padding padding, int64 feature_group_count = 1, const PrecisionConfig* precision_config = nullptr); - // Enqueues a convolution instruction onto the computation, with the caller - // provided padding configuration in the format returned by MakePadding(). XlaOp ConvWithGeneralPadding( const XlaOp& lhs, const XlaOp& rhs, absl::Span window_strides, @@ -503,8 +397,6 @@ class XlaBuilder { int64 feature_group_count = 1, const PrecisionConfig* precision_config = nullptr); - // Enqueues a convolution instruction onto the computation, with the caller - // provided dimension numbers configuration. XlaOp ConvWithGeneralDimensions( const XlaOp& lhs, const XlaOp& rhs, absl::Span window_strides, Padding padding, @@ -512,8 +404,6 @@ class XlaBuilder { int64 feature_group_count = 1, const PrecisionConfig* precision_config = nullptr); - // Enqueues a convolution instruction onto the computation, with the caller - // provided padding configuration as well as the dimension numbers. XlaOp ConvGeneral(const XlaOp& lhs, const XlaOp& rhs, absl::Span window_strides, absl::Span> padding, @@ -521,8 +411,6 @@ class XlaBuilder { int64 feature_group_count = 1, const PrecisionConfig* precision_config = nullptr); - // Enqueues a convolution instruction onto the computation, with the caller - // provided padding configuration, dilation factors and dimension numbers. XlaOp ConvGeneralDilated(const XlaOp& lhs, const XlaOp& rhs, absl::Span window_strides, absl::Span> padding, @@ -532,80 +420,53 @@ class XlaBuilder { int64 feature_group_count = 1, const PrecisionConfig* precision_config = nullptr); - // Enqueues an FFT instruction onto the computation, of the given type and - // with the given FFT length. XlaOp Fft(const XlaOp& operand, FftType fft_type, absl::Span fft_length); - // Enqueues an infeed instruction onto the computation, which writes data of - // the given shape to the infeed buffer of the device. XlaOp Infeed(const Shape& shape, const string& config = ""); XlaOp InfeedWithToken(const XlaOp& token, const Shape& shape, const string& config = ""); - // Enqueues an outfeed instruction onto the computation. This instruction - // generates outgoing data transfers for the given data. - // - // shape_with_layout communicates the laid out shape that we want to outfeed - // -- if !ShapeUtil::Compatible(GetShape(operand), shape_with_layout) an error - // will occur. void Outfeed(const XlaOp& operand, const Shape& shape_with_layout, const string& outfeed_config); XlaOp OutfeedWithToken(const XlaOp& operand, const XlaOp& token, const Shape& shape_with_layout, const string& outfeed_config); - // Enqueues a call instruction onto the computation. XlaOp Call(const XlaComputation& computation, absl::Span operands); - // Enqueues a custom call instruction onto the computation. XlaOp CustomCall( const string& call_target_name, absl::Span operands, const Shape& shape_with_layout, const string& opaque, absl::optional> operand_shapes_with_layout); - // The following methods enqueue element-wise binary arithmetic operations - // onto the computation. The shapes of the operands have to match unless one - // of the operands is a scalar, or an explicit broadcast dimension is given - // (see g3doc for more details). - - // Enqueues a complex compose instruction onto the computation. XlaOp Complex(const XlaOp& real, const XlaOp& imag, absl::Span broadcast_dimensions = {}); - // Enqueues a complex conjugate instruction onto the computation. XlaOp Conj(const XlaOp& operand); - // Enqueues an add instruction onto the computation. XlaOp Add(const XlaOp& lhs, const XlaOp& rhs, absl::Span broadcast_dimensions = {}); - // Enqueues a subtract instruction onto the computation. XlaOp Sub(const XlaOp& lhs, const XlaOp& rhs, absl::Span broadcast_dimensions = {}); - // Enqueues a multiply instruction onto the computation. XlaOp Mul(const XlaOp& lhs, const XlaOp& rhs, absl::Span broadcast_dimensions = {}); - // Enqueues a divide instruction onto the computation. XlaOp Div(const XlaOp& lhs, const XlaOp& rhs, absl::Span broadcast_dimensions = {}); - // Enqueues a remainder instruction onto the computation. XlaOp Rem(const XlaOp& lhs, const XlaOp& rhs, absl::Span broadcast_dimensions = {}); - // Enqueues a max instruction onto the computation. XlaOp Max(const XlaOp& lhs, const XlaOp& rhs, absl::Span broadcast_dimensions = {}); - // Enqueues a min instruction onto the computation. XlaOp Min(const XlaOp& lhs, const XlaOp& rhs, absl::Span broadcast_dimensions = {}); - // Element-wise logical operators XlaOp And(const XlaOp& lhs, const XlaOp& rhs, absl::Span broadcast_dimensions = {}); @@ -624,32 +485,23 @@ class XlaBuilder { XlaOp ShiftRightLogical(const XlaOp& lhs, const XlaOp& rhs, absl::Span broadcast_dimensions = {}); - // Reduces an array among the provided dimensions, given "computation" as a - // reduction operator. XlaOp Reduce(const XlaOp& operand, const XlaOp& init_value, const XlaComputation& computation, absl::Span dimensions_to_reduce); - // Reduces several arrays simultaneously among the provided dimensions, given - // "computation" as a reduction operator. XlaOp Reduce(absl::Span operands, absl::Span init_values, const XlaComputation& computation, absl::Span dimensions_to_reduce); - // Convenience wrapper around the above that reduces all the dimensions in the - // operand shape. XlaOp ReduceAll(const XlaOp& operand, const XlaOp& init_value, const XlaComputation& computation); - // Enqueues a windowed reduce instruction onto the computation. XlaOp ReduceWindow(const XlaOp& operand, const XlaOp& init_value, const XlaComputation& computation, absl::Span window_dimensions, absl::Span window_strides, Padding padding); - // As ReduceWindow(), but the padding is given in the format - // returned by MakePadding(). XlaOp ReduceWindowWithGeneralPadding( const XlaOp& operand, const XlaOp& init_value, const XlaComputation& computation, @@ -659,48 +511,22 @@ class XlaBuilder { absl::Span window_dilations, absl::Span> padding); - // Returns the sum of the operand value within each subgroup of replicas. All - // replicas supply one input to the sum and all replicas receive the resulting - // sum for each subgroup. XlaOp CrossReplicaSum(const XlaOp& operand, absl::Span replica_groups = {}); - // Enqueues an operation that do an AllReduce of the operand cross cores. Here - // AllReduce means doing a reduction on the input operand cross cores and then - // broadcasting the reduction result to those cores. The reduction function is - // defined by `computation`, which should be a commutative computation on - // scalars, e.g., add, min, or max. The way that AllReduce is applied is - // configured by: - // - // - `replica_groups`: each ReplicaGroup contains a list of replica id. If - // empty, all replicas belong to one group. Allreduce will be applied within - // subgroups. For example, we have 4 replicas, then - // replica_groups={{0,2},{1,3}} means, replica 0 and 2 are in subgroup 0, - // replica 1 and 3 are in subgroup 1. - // - // - `channel_id`: for Allreduce nodes from different modules, if they have - // the same channel_id, they will be 'Allreduce'd. If empty, Allreduce will - // not be applied cross modules. - // - // TODO(b/117564385): Rename this to AllReduce when it's ready to use. XlaOp CrossReplicaSum( const XlaOp& operand, const XlaComputation& computation, absl::Span replica_groups = {}, const absl::optional& channel_id = absl::nullopt); - // Enqueues an operation that do an Alltoall of the operand cross cores. XlaOp AllToAll(const XlaOp& operand, int64 split_dimension, int64 concat_dimension, int64 split_count, const std::vector& replica_groups); - // Enqueues an operation that do an CollectivePermute of the operand cross - // cores. XlaOp CollectivePermute( const XlaOp& operand, const std::vector>& source_target_pairs); - // Enqueues an operation that scatters the `source` array to the selected - // indices of each window. XlaOp SelectAndScatter(const XlaOp& operand, const XlaComputation& select, absl::Span window_dimensions, absl::Span window_strides, @@ -708,8 +534,6 @@ class XlaBuilder { const XlaOp& init_value, const XlaComputation& scatter); - // As SelectAndScatter(), but the padding is given in the format - // returned by MakePadding(). XlaOp SelectAndScatterWithGeneralPadding( const XlaOp& operand, const XlaComputation& select, absl::Span window_dimensions, @@ -717,217 +541,119 @@ class XlaBuilder { absl::Span> padding, const XlaOp& source, const XlaOp& init_value, const XlaComputation& scatter); - // Enqueues an abs instruction onto the computation. XlaOp Abs(const XlaOp& operand); - // Enqueues a atan2 instruction onto the computation. XlaOp Atan2(const XlaOp& y, const XlaOp& x, absl::Span broadcast_dimensions = {}); - // Enqueues an exp instruction onto the computation. XlaOp Exp(const XlaOp& operand); - // Enqueues an expm1 instruction onto the computation. XlaOp Expm1(const XlaOp& operand); - // Enqueues a floor instruction onto the computation. XlaOp Floor(const XlaOp& operand); - // Enqueues a ceil instruction onto the computation. XlaOp Ceil(const XlaOp& operand); - // Enqueues a round instruction onto the computation, rounding to nearest even - // with half-way cases rounding away from zero. XlaOp Round(const XlaOp& operand); - // Enqueues an log instruction (natural logarithm) onto the computation. XlaOp Log(const XlaOp& operand); - // Enqueues an log1p instruction (log(x+1)) onto the computation. XlaOp Log1p(const XlaOp& operand); - // Enqueues a sign instruction onto the computation. XlaOp Sign(const XlaOp& operand); - // Enqueues a count leading zeros instruction onto the computation. XlaOp Clz(const XlaOp& operand); - // Enqueues a cosine instruction onto the computation. XlaOp Cos(const XlaOp& operand); - // Enqueues a sine instruction onto the computation. XlaOp Sin(const XlaOp& operand); - // Enqueues a tanh instruction onto the computation. XlaOp Tanh(const XlaOp& operand); - // Enqueues a real-part instruction onto the computation. XlaOp Real(const XlaOp& operand); - // Enqueues an imaginary-part instruction onto the computation. XlaOp Imag(const XlaOp& operand); - // Enqueues a lhs^rhs computation onto the computation. XlaOp Pow(const XlaOp& lhs, const XlaOp& rhs, absl::Span broadcast_dimensions = {}); - // Enqueues an operator that tests if the operand's values are finite, i.e., - // not Inf or NaN. Defined only for floating-point types. Returns an array of - // booleans with the same shape where entries are true iff the corresponding - // entry was NaN. XlaOp IsFinite(const XlaOp& operand); - // Enqueues an iota operation onto the computation. XlaOp Iota(const Shape& shape, int64 iota_dimension); - // Enqueues a rank-1 iota operation onto the computation. XlaOp Iota(PrimitiveType type, int64 size); - // Enqueues a convert instruction onto the computation that changes the - // element type of the operand array to primitive_type. XlaOp ConvertElementType(const XlaOp& operand, PrimitiveType new_element_type); - // Enqueues a no-op instruction onto the computation that changes - // the element type of the operand array to primitive_type. The - // bit-widths of the source and destination element types must be - // identical. XlaOp BitcastConvertType(const XlaOp& operand, PrimitiveType new_element_type); - // Enqueues a negate instruction onto the computation. XlaOp Neg(const XlaOp& operand); - // Enqueues a transpose instruction onto the computation. XlaOp Transpose(const XlaOp& operand, absl::Span permutation); - // Enqueues a reverse instruction onto the computation. The order of the - // elements in the given dimensions is reversed (i.e., the element at index i - // is moved to index dimension_size - 1 - i). XlaOp Rev(const XlaOp& operand, absl::Span dimensions); - // Enqueues a sort (as increasing order) instruction onto the computation. - // If only keys are provided: - // * If the keys are an rank-1 tensor (an array), the result is a sorted array - // of keys, in ascending order. - // * If the keys have higher rank, the keys are sorted along the provided - // dimension. For example, for a rank-2 tensor (a matrix) of keys, a dimension - // value of 0 will indepenently sort every column, and a dimension value of 1 - // will independently sort each row. If no dimension number is provided, then - // the last dimension is chosen by default. - // - // If both keys and values are provided: - // * The keys and all values must be tensors with the same dimensions. The - // element types of the tensors may be different. - // * The result is a tuple that consists of a sorted tensor of keys (along the - // provided dimension, as above) as the first element, and tensors with their - // corresponding values as the other elements. XlaOp Sort(const XlaOp& keys, absl::Span values = {}, int64 dimension = -1); - // Enqueues a clamp instruction onto the computation. XlaOp Clamp(const XlaOp& min, const XlaOp& operand, const XlaOp& max); - // Enqueues a map instruction onto the computation. XlaOp Map(absl::Span operands, const XlaComputation& computation, absl::Span dimensions, absl::Span static_operands = {}); - // Enqueues a N(mu, sigma) random number generation instruction onto the - // computation. XlaOp RngNormal(const XlaOp& mu, const XlaOp& sigma, const Shape& shape); - // Enqueues a U(a, b) random number generation instruction onto the - // computation. Returns values in the semi-open interval [a, b). XlaOp RngUniform(const XlaOp& a, const XlaOp& b, const Shape& shape); - // Enqueues a while node onto the computation. XlaOp While(const XlaComputation& condition, const XlaComputation& body, const XlaOp& init); - // Enqueues a conditional node onto the computation. XlaOp Conditional(const XlaOp& predicate, const XlaOp& true_operand, const XlaComputation& true_computation, const XlaOp& false_operand, const XlaComputation& false_computation); - // Enqueues a ReducePrecision node onto the computation. XlaOp ReducePrecision(const XlaOp& operand, const int exponent_bits, const int mantissa_bits); - // Enqueues a Gather node onto the computation. XlaOp Gather(const XlaOp& input, const XlaOp& start_indices, const GatherDimensionNumbers& dimension_numbers, absl::Span slice_sizes); - // Enqueues a Scatter node onto the computation. XlaOp Scatter(const XlaOp& input, const XlaOp& scatter_indices, const XlaOp& updates, const XlaComputation& update_computation, const ScatterDimensionNumbers& dimension_numbers); - // Enqueues a Send node onto the computation for device-to-device - // communication, to send the given operand to a Recv instruction that shares - // the same channel handle. void Send(const XlaOp& operand, const ChannelHandle& handle); XlaOp SendWithToken(const XlaOp& operand, const XlaOp& token, const ChannelHandle& handle); - // Enqueues a Send node which sends data to the host. XlaOp SendToHost(const XlaOp& operand, const XlaOp& token, const Shape& shape_with_layout, const ChannelHandle& handle); - // Enqueues a Recv node which receives data from the host. XlaOp RecvFromHost(const XlaOp& token, const Shape& shape, const ChannelHandle& handle); - // Enqueues an AfterAll operation with no operands producing a token-shaped - // value. XlaOp CreateToken(); - // Enqueues an AfterAll operation with no operands producing a token-shaped - // value. XlaOp AfterAll(absl::Span tokens); - // Enqueues a Recv node onto the computation. The data comes from a Send - // instruction that shares the same channel handle and its shape must - // be the same as the given shape. XlaOp Recv(const Shape& shape, const ChannelHandle& handle); XlaOp RecvWithToken(const XlaOp& token, const Shape& shape, const ChannelHandle& handle); - // Normalizes operand across spatial and batch dimensions for each feature. - // - // Returns a tuple (normalized, batch_mean, batch_var) where `normalized` - // is the normalized result and batch_mean and batch_var are the mean and - // variance, respectively, across batch for the operand. XlaOp BatchNormTraining(const XlaOp& operand, const XlaOp& scale, const XlaOp& offset, float epsilon, int64 feature_index); - // Normalizes operand across spatial and batch dimensions for each feature. - // - // `BatchNormInference` is equivalent to calling `BatchNormTraining` without - // computing `mean` and `variance` for each batch inside the operation. It - // uses the input `mean` and `variance` instead as estimated values. The - // purpose of this op is to reduce latency in inference, hence the name - // `BatchNormInference`. - // - // The output has the same shape as `operand`, and contains the normalized - // values for each batch. XlaOp BatchNormInference(const XlaOp& operand, const XlaOp& scale, const XlaOp& offset, const XlaOp& mean, const XlaOp& variance, float epsilon, int64 feature_index); - // Calculates the gradients of a batch norm op. - // - // The inputs `batch_mean` and `batch_var` represent the mean and variance - // across the batch. - // - // Returns a tuple of three elements: - // - grad_operand: Gradient with respect to input `operand` - // - grad_offset: Gradient with respect to input `offset` - // - grad_scale: Gradient with respect to input `scale` XlaOp BatchNormGrad(const XlaOp& operand, const XlaOp& scale, const XlaOp& batch_mean, const XlaOp& batch_var, const XlaOp& grad_output, float epsilon, @@ -1019,6 +745,9 @@ class XlaBuilder { // The instructions of this computation. std::vector instructions_; + // Dynamic parameter configuration of this computation. + DynamicParameterBinding dynamic_parameter_binding_; + // A map from XlaOp::Handle to the index in the instructions_ vector where the // instruction is held. absl::flat_hash_map handle_to_index_; @@ -1096,7 +825,7 @@ class XlaBuilder { absl::Span broadcast_sizes); friend XlaOp BroadcastInDim( - const XlaOp& operand, const Shape& shape, + const XlaOp& operand, const absl::Span out_dim_size, const absl::Span broadcast_dimensions); friend XlaOp Pad(const XlaOp& operand, const XlaOp& padding_value, @@ -1393,6 +1122,7 @@ class XlaScopedShardingAssignment { // Free functions for building XlaOps. The intention is that these will // become the public API for building XlaOps rather than calling methods on // XlaBuilder directly. +// // Enqueues a "retrieve parameter value" instruction for a parameter that was // passed to the computation. @@ -1488,7 +1218,8 @@ XlaOp Broadcast(const XlaOp& operand, absl::Span broadcast_sizes); // will generate output // {{1 , 1}, // {2 , 2}} -XlaOp BroadcastInDim(const XlaOp& operand, const Shape& shape, +XlaOp BroadcastInDim(const XlaOp& operand, + const absl::Span out_dim_size, const absl::Span broadcast_dimensions); // Enqueues a pad operation onto the computation that pads the given value on @@ -2138,6 +1869,7 @@ XlaOp BatchNormGrad(const XlaOp& operand, const XlaOp& scale, XlaOp GetDimensionSize(const XlaOp& operand, int64 dimension); // Implementation details below this point. +// template XlaOp XlaBuilder::ConstantR0(NativeT value) { diff --git a/tensorflow/compiler/xla/client/xla_builder_test.cc b/tensorflow/compiler/xla/client/xla_builder_test.cc index 8aa85c3cd6..b3f5be300d 100644 --- a/tensorflow/compiler/xla/client/xla_builder_test.cc +++ b/tensorflow/compiler/xla/client/xla_builder_test.cc @@ -267,7 +267,7 @@ TEST_F(XlaBuilderTest, BinopHasInDimAndDegenerateBroadcast) { TEST_F(XlaBuilderTest, BroadcastInDim) { XlaBuilder b(TestName()); auto x = Parameter(&b, 0, ShapeUtil::MakeShape(F32, {2, 3}), "x"); - BroadcastInDim(x, ShapeUtil::MakeShape(F32, {2, 4, 3}), + BroadcastInDim(x, {2, 4, 3}, /*broadcast_dimensions=*/{0, 2}); TF_ASSERT_OK_AND_ASSIGN(auto module, BuildHloModule(&b)); auto root = module->entry_computation()->root_instruction(); @@ -277,7 +277,7 @@ TEST_F(XlaBuilderTest, BroadcastInDim) { TEST_F(XlaBuilderTest, BroadcastInDimWithDegeneratedDim) { XlaBuilder b(TestName()); auto x = Parameter(&b, 0, ShapeUtil::MakeShape(F32, {2, 1, 4}), "x"); - BroadcastInDim(x, ShapeUtil::MakeShape(F32, {2, 3, 4}), + BroadcastInDim(x, {2, 3, 4}, /*broadcast_dimensions=*/{0, 1, 2}); TF_ASSERT_OK_AND_ASSIGN(auto module, BuildHloModule(&b)); EXPECT_THAT(module->entry_computation()->root_instruction(), @@ -446,5 +446,14 @@ TEST_F(XlaBuilderTest, ProtoMatches) { EXPECT_EQ(c0_string, c1_string); } +TEST_F(XlaBuilderTest, AfterAllWithNonTokenOperands) { + XlaBuilder b(TestName()); + AfterAll(&b, {CreateToken(&b), ConstantR0(&b, 1.0)}); + Status status = b.Build().status(); + ASSERT_IS_NOT_OK(status); + EXPECT_THAT(status.error_message(), + ::testing::HasSubstr("All operands to AfterAll must be tokens")); +} + } // namespace } // namespace xla diff --git a/tensorflow/compiler/xla/client/xla_computation.cc b/tensorflow/compiler/xla/client/xla_computation.cc index c9870b65b9..f317892c12 100644 --- a/tensorflow/compiler/xla/client/xla_computation.cc +++ b/tensorflow/compiler/xla/client/xla_computation.cc @@ -25,7 +25,7 @@ namespace xla { StatusOr XlaComputation::GetProgramShape() const { TF_RET_CHECK(proto_.has_host_program_shape()); - return proto_.host_program_shape(); + return ProgramShape(proto_.host_program_shape()); } StatusOr> XlaComputation::Snapshot() const { diff --git a/tensorflow/compiler/xla/client/xla_computation.h b/tensorflow/compiler/xla/client/xla_computation.h index 71598ef8b2..3ccbfb28bd 100644 --- a/tensorflow/compiler/xla/client/xla_computation.h +++ b/tensorflow/compiler/xla/client/xla_computation.h @@ -19,6 +19,7 @@ limitations under the License. #include #include "tensorflow/compiler/xla/service/hlo.pb.h" +#include "tensorflow/compiler/xla/shape.h" #include "tensorflow/compiler/xla/status_macros.h" #include "tensorflow/compiler/xla/xla_data.pb.h" diff --git a/tensorflow/compiler/xla/debug_options_flags.cc b/tensorflow/compiler/xla/debug_options_flags.cc index 033887d7c1..d7e7b9e621 100644 --- a/tensorflow/compiler/xla/debug_options_flags.cc +++ b/tensorflow/compiler/xla/debug_options_flags.cc @@ -54,7 +54,7 @@ void SetDebugOptionsDefaults(DebugOptions* flags) { // TODO(jlebar): Disable fastmath once doing so is not a performance // regression. flags->set_xla_cpu_enable_fast_math(true); - flags->set_xla_gpu_enable_fast_math(true); + flags->set_xla_gpu_enable_fast_min_max(true); flags->set_xla_force_host_platform_device_count(1); } @@ -160,11 +160,11 @@ void AllocateFlags() { "Enable unsafe fast-math optimizations in the CPU compiler; " "this may produce faster code at the expense of some accuracy."), tensorflow::Flag( - "xla_gpu_enable_fast_math", - bool_setter_for(&DebugOptions::set_xla_cpu_enable_fast_math), - flag_values->xla_cpu_enable_fast_math(), - "Enable unsafe fast-math optimizations in the GPU compiler; " - "this may produce faster code at the expense of some accuracy."), + "xla_gpu_enable_fast_min_max", + bool_setter_for(&DebugOptions::set_xla_gpu_enable_fast_min_max), + flag_values->xla_gpu_enable_fast_min_max(), + "Enable fast floating point min/max lowering that does not propagate " + "NaNs."), tensorflow::Flag( "xla_llvm_enable_alias_scope_metadata", bool_setter_for( @@ -335,7 +335,7 @@ void AllocateFlags() { "behavior to help run tests on the host that run models in parallel " "across multiple devices."), }); - ParseFlagsFromEnv(*flag_objects); + ParseFlagsFromEnvAndDieIfUnknown("XLA_FLAGS", *flag_objects); } } // namespace diff --git a/tensorflow/compiler/xla/experimental/xla_sharding/xla_sharding.py b/tensorflow/compiler/xla/experimental/xla_sharding/xla_sharding.py index fb135f5ced..1fea816a80 100644 --- a/tensorflow/compiler/xla/experimental/xla_sharding/xla_sharding.py +++ b/tensorflow/compiler/xla/experimental/xla_sharding/xla_sharding.py @@ -18,12 +18,9 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -import math - import numpy as _np # Avoids becoming a part of public Tensorflow API. from tensorflow.compiler.xla import xla_data_pb2 -from tensorflow.compiler.xla.python_api import xla_shape from tensorflow.core.framework import attr_value_pb2 @@ -64,22 +61,18 @@ class Sharding(object): tile_assignment_devices=[core])) @classmethod - def tile(cls, tile_shape, tile_assignment): + def tile(cls, tile_assignment): """Returns a Tiled sharding attribute. This causes an op to be partially computed on multiple cores in the XLA device. Args: - tile_shape: A xla_shape.Shape describing the tile shape that each core - will compute. - The tile shape does not need to be divisible by the tile assignment. tile_assignment: An np.ndarray describing the topology of the tiling and which device will compute which part of the topology. Raises: - TypeError: tile_assignment was not of np.array type or tile_shape was - not of xla_shape.Shape type. + TypeError: tile_assignment was not of np.array type. TODO(jmolloy): This concept is nefarious and is not something we really want to expose to users (especially as the @@ -87,14 +80,11 @@ class Sharding(object): """ if not isinstance(tile_assignment, _np.ndarray): raise TypeError('Tile assignment must be of type np.ndarray') - if not isinstance(tile_shape, xla_shape.Shape): - raise TypeError('Tile shape must be of type xla_shape.Shape') dims = list(tile_assignment.shape) flattened_devices = tile_assignment.reshape(-1, order='C') return Sharding( proto=xla_data_pb2.OpSharding( type=xla_data_pb2.OpSharding.OTHER, - tile_shape=tile_shape.message, tile_assignment_dimensions=dims, tile_assignment_devices=list(flattened_devices))) @@ -118,14 +108,8 @@ class Sharding(object): shape = tensor.shape.as_list() if shape[split_dimension] < num_devices: raise ValueError('Split dimension was smaller than the required number ' - 'of splits: shape=%r, dimension=%r, num_devices=%r', - shape, split_dimension, num_devices) - - tile_shape = shape - tile_shape[split_dimension] = int( - math.ceil(tile_shape[split_dimension] / num_devices)) - tile_shape_proto = xla_data_pb2.Shape( - element_type=xla_data_pb2.F32, dimensions=tile_shape) + 'of splits: shape=%r, dimension=%r, num_devices=%r' % + (shape, split_dimension, num_devices)) tile_assignment_dims = [1] * len(shape) tile_assignment_dims[split_dimension] = num_devices @@ -133,7 +117,6 @@ class Sharding(object): return Sharding( proto=xla_data_pb2.OpSharding( type=xla_data_pb2.OpSharding.OTHER, - tile_shape=tile_shape_proto, tile_assignment_dimensions=tile_assignment_dims, tile_assignment_devices=range(num_devices))) @@ -149,7 +132,6 @@ class Sharding(object): type=xla_data_pb2.OpSharding.TUPLE, tuple_shardings=tuple_shardings) else: proto = self._proto - attr_value = attr_value_pb2.AttrValue(s=proto.SerializeToString()) # TODO(jmolloy): This need to be seriously revisited before declaring this # API available for public use. @@ -194,8 +176,8 @@ def assign_device(tensor, device): return tensor -def tile(tensor, tile_shape, tile_assignment): - Sharding.tile(tile_shape, tile_assignment).apply_to_tensor(tensor) +def tile(tensor, tile_assignment): + Sharding.tile(tile_assignment).apply_to_tensor(tensor) return tensor diff --git a/tensorflow/compiler/xla/g3doc/_book.yaml b/tensorflow/compiler/xla/g3doc/_book.yaml index bcfbcc3a22..12b7094705 100644 --- a/tensorflow/compiler/xla/g3doc/_book.yaml +++ b/tensorflow/compiler/xla/g3doc/_book.yaml @@ -3,15 +3,15 @@ upper_tabs: - include: /_upper_tabs_left.yaml - include: /api_docs/_upper_tabs_api.yaml # Dropdown menu -- name: Ecosystem - path: /ecosystem +- name: Resources + path: /resources is_default: true menu: - - include: /ecosystem/_menu_toc.yaml + - include: /resources/_menu_toc.yaml lower_tabs: # Subsite tabs other: - - name: Guide + - name: Guide & Tutorials contents: - title: XLA overview path: /xla/overview @@ -27,3 +27,7 @@ upper_tabs: path: /xla/shapes - title: Using AOT compilation path: /xla/tfcompile + - heading: Tutorials + - title: XLA compile API + path: /xla/tutorials/xla_compile + status: experimental diff --git a/tensorflow/compiler/xla/g3doc/_index.yaml b/tensorflow/compiler/xla/g3doc/_index.yaml index 7934cd11ba..858de42711 100644 --- a/tensorflow/compiler/xla/g3doc/_index.yaml +++ b/tensorflow/compiler/xla/g3doc/_index.yaml @@ -17,7 +17,7 @@ landing_page: - classname: devsite-landing-row-cards items: - heading: XLA - TensorFlow, compiled - image_path: /ecosystem/images/tf-logo-card-16x9.png + image_path: /resources/images/tf-logo-card-16x9.png path: https://developers.googleblog.com/2017/03/xla-tensorflow-compiled.html buttons: - label: Read on Google Developers blog @@ -28,7 +28,7 @@ landing_page: - label: Watch the video path: https://www.youtube.com/watch?v=kAOanJczHA0 - heading: XLA on GitHub - image_path: /ecosystem/images/github-card-16x9.png + image_path: /resources/images/github-card-16x9.png path: https://github.com/tensorflow/tensorflow/tree/master/tensorflow/compiler/xla buttons: - label: View on GitHub diff --git a/tensorflow/compiler/xla/g3doc/images/xla_array_layout_figure1.png b/tensorflow/compiler/xla/g3doc/images/xla_array_layout_figure1.png new file mode 100644 index 0000000000000000000000000000000000000000..00cefe4c7806c1c09dd51499375e720bfb0baac6 GIT binary patch literal 20398 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 Jmvv4FO#qulvt$4O literal 0 HcmV?d00001 diff --git a/tensorflow/compiler/xla/g3doc/images/xla_array_layout_figure2.png b/tensorflow/compiler/xla/g3doc/images/xla_array_layout_figure2.png new file mode 100644 index 0000000000000000000000000000000000000000..6439c6e40272ae6b2954e9d7f3de2df470a2b36d GIT binary patch literal 7913 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 YV|3EW+_cv-FfcH9y85}Sb4q9e0BqX ![](images/xla_array_layout_figure1.png) + +Figure 1 + +Figure 1 shows how an array F32[3,5] is laid out in memory with 2x2 tiling. A +shape with this layout is written as F32[3,5]{1,0:(2,2)}, where 1,0 relates to +the physical order of dimensions (minor_to_major field in Layout) while (2,2) +after the colon indicates tiling of the physical dimensions by a 2x2 tile. + +Intuitively tiles are laid out to cover the shape and then within each tile, +elements are then laid out without tiling, as in the example above, where the +right part of the example shows the layout in memory, including the white +padding elements that are added in order to have complete 2x2 tiles even though +the original array bounds are not even. + +The extra elements in the padding are not required to contain any particular +value. + +## Linear index formulas for tiling given a shape and a tile + +Without tiling, an element e=(en, en-1, ... , +e1) in an array with array bounds d=(dn, dn-1, +... , d1) (d1 is the most minor dimension) is laid out by major to +minor order at position: + +   linear_index(e, d) \ += linear_index((en, en-1, ... , e1), +(dn, dn-1, ... , d1)) \ += endn-1...d1 + +en-1dn-2...d1 + ... + e1 + +For simplicity of notation in this document we assume a tile has the same number +of dimensions as the array. In XLA's implementation of tiling, this is +generalized to tilings with fewer dimensions by leaving the initial most-major +dimensions unchanged and applying the tiling only to the most minor dimensions, +so that the tiling that is specified mentions a suffix of the physical +dimensions of the shape being tiled. + +When tiling of size (tn, tn-1, ... , t1) is +used, an element in the array with indices (en, en-1, ... +, e1) is mapped to this position in the final layout: + +   linear_index_with_tile(e, d, t) \ += linear_index((⌊e/t⌋, e mod t), (⌈d/t⌉, t))     (arithmetic is +elementwise, (a,b) is concatenation) \ += linear_index((⌊en/tn⌋, ... , +⌊e1/t1⌋, en mod tn, ... , +e1 mod t1), (⌈dn/tn⌉, ... , +⌈d1/t1⌉, tn, tn-1, ... , +t1)) \ += linear_index((⌊en/tn⌋, ... , +⌊e1/t1⌋), (⌈dn/tn⌉, ... , +⌈d1/t1⌉))∙tntn-1...t1 + +linear_index((en mod tn, ... , e1 mod +t1), (tn, tn-1, ... , t1)) + +The layout can be thought of as having two parts: +(⌊en/tn⌋, ... , ⌊e1/t1⌋), which +corresponds to a tile index in an array of tiles of size +(⌈dn/tn⌉, ... , ⌈d1/t1⌉), and +(en mod tn, ... , e1 mod t1), which +corresponds to a within-tile index. The ceil function appears in +⌈di/ti⌉ because if tiles overrun the bounds of the larger +array, padding is inserted as in Figure 1. Both the tiles and elements within +tiles are laid out recursively without tiling. + +For the example in Figure 1, element (2,3) has tile index (1,1), and within-tile +index (0,1), for a combined coordinate vector of (1, 1, 0, 1). The tile indices +have bounds (2, 3) and the tile itself is (2, 2) for a combined vector of (2, 3, +2, 2). The linear index with tile for the element with index (2, 3) in the +logical shape is then + +   linear_index_with_tile((2,3), (3,5), (2,2)) \ += linear_index((1,1,0,1), (2,3,2,2)) \ += linear_index((1,1), (2,3)) ∙ 2 ∙ 2 + linear_index((0,1), (2,2)) \ += (1 ∙ 3 + 1) ∙ 2 ∙ 2 + (0 ∙ 2 + 1) \ += 17. + +# Tiling as pad-reshape-transpose + +Tiling-based layout operates as follows: \ +Consider an array of dimensions (dn, dn-1, ... , d1) (d1 +is the most minor dimension). When it’s laid out with tiling of size +(tn, tn-1, ... , t1) (t1 is the most +minor dimension), that tiling can be described in terms of pad-reshape-transpose +in the following way. + +1. The array is padded to (⌈dn/tn⌉∙tn, ... , + ⌈d1/t1⌉∙t1). +2. Each dimension i is broken into (⌈di/ti⌉, + ti), i.e. the array is reshaped to \ +     (⌈dn/tn⌉, tn, ... , + ⌈d1/t1⌉, t1). \ + There is no physical layout change in this reshape by itself, so this + reshape is a bitcast. If one is not explicitly thinking of a tiling, this + reshape could express any shape with the same number of elements as the + padded shape - the example here is of how to express a tile in this way. +3. A transpose happens by moving tn, ... , t1 to the most + minor dimensions while keeping their relative order, so that the order of + dimensions from most major to most minor becomes \ +     (⌈dn/tn⌉, ... , + ⌈d1/t1⌉, tn, ... , t1). + +The final shape has the prefix \ +    (⌈dn/tn⌉, ... , +⌈d1/t1⌉), which describes the number of tiles in each +dimension. An element in the array (en, ... , e1) is +mapped to this element in the final shape: \ +    (⌊en/tn⌋, ... , +⌊e0/t0⌋, en mod tn, ... , +e1 mod t1). It is easy to see that the linear index of the +element follows the formula above as expected. + +# Repeated tiling + +XLA's tiling becomes even more flexible by applying it repeatedly. + +
![](images/xla_array_layout_figure2.png) + +Figure 2
+ +Figure 2 shows how an array of size 4x8 is tiled by two levels of tiling (first +2x4 then 2x1). We represent this repeated tiling as (2,4)(2,1). Each color +indicates a 2x4 tile and each red border box is a 2x1 tile. The numbers +indicates the linear index in memory of that element in the tiled format. This +format matches the format used for BF16 on TPU, except that the initial tile is +bigger, namely the tiling is (8,128)(2,1), where the purpose of the second +tiling by 2x1 is to collect together two 16 bit values to form one 32 bit value +in a way that aligns with the architecture of a TPU. + +Note that a second or later tile can refer to both the minor within-tile +dimensions, which just rearranges data within the tile, as in this example with +(8,128)(2,1), but can also refer to the major cross-tile dimensions from the +prior tiling. + +# Combining dimensions using tiles + +XLA's tiling also supports combining dimensions. For example, it can combine +dimensions in F32[2,7,8,11,10]{4,3,2,1,0} into F32[112,110]{1,0} first before +tiling it with (2,3). The tile used is (∗,∗,2,∗,3). Here an +asterisk in a tile implies taking that dimension and combining it with the next +more minor dimension. Multiple adjacent dimensions can be subsumed together into +one dimension. A subsumed dimension is represented by a tile value of -1 in that +dimension of the tile, which is not otherwise valid in a tile as a dimension +size. + +More precisely, if dimension i of the shape is eliminated via an asterisk in the +tile, then before the prior definition of tiling is applied, that dimension is +removed from both the shape being tiled and the tile vector, and what was +dimension i-1 of the shape has its array bound increased from di-1 to +didi-1. This step is repeated for each asterisk in the +tile vector. diff --git a/tensorflow/compiler/xla/g3doc/operation_semantics.md b/tensorflow/compiler/xla/g3doc/operation_semantics.md index 73a9db75f6..d888b1f23f 100644 --- a/tensorflow/compiler/xla/g3doc/operation_semantics.md +++ b/tensorflow/compiler/xla/g3doc/operation_semantics.md @@ -13,6 +13,22 @@ arbitrary-dimensional array. For convenience, special cases have more specific and familiar names; for example a *vector* is a 1-dimensional array and a *matrix* is a 2-dimensional array. +## AfterAll + +See also +[`XlaBuilder::AfterAll`](https://www.tensorflow.org/code/tensorflow/compiler/xla/client/xla_builder.h). + +AfterAll takes a variadic number of tokens and produces a single token. Tokens +are primitive types which can be threaded between side-effecting operations to +enforce ordering. `AfterAll` can be used as a join of tokens for ordering a +operation after a set operations. + + `AfterAll(operands)` + +Arguments | Type | Semantics +---------- | ------- | ------------------------- +`operands` | `XlaOp` | variadic number of tokens + ## AllToAll See also @@ -402,6 +418,33 @@ then v12 == f32[8x3] {{10, 11, 12}, ``` +## CollectivePermute + +See also +[`XlaBuilder::CollectivePermute`](https://www.tensorflow.org/code/tensorflow/compiler/xla/client/xla_builder.h). + +CollectivePermute is a collective operation that sends and receives data cross +replicas. + + `CollectivePermute(operand, source_target_pairs)` + +| Arguments | Type | Semantics | +| --------------------- | ----------------------- | -------------------------- | +| `operand` | `XlaOp` | n dimensional input array | +| `source_target_pairs` | `` vector | A list of | +: : : (source_replica_id, : +: : : target_replica_id) pairs. : +: : : For each pair, the operand : +: : : is sent from source : +: : : replica to target replica. : + +Note that there are the following restrictions on the `source_target_pair`: + +- Any two pairs should not have the same target replica id, and they should + not have the same source replica id. +- If a replica id is not a target in any pair, then the output on that replica + is a tensor consists of 0(s) with the same shape as the input. + ## Concatenate See also @@ -1423,10 +1466,11 @@ Builds a constant literal on device rather than a potentially large host transfer. Creates a rank 1 array of values starting at zero and incrementing by one. -Arguments | Type | Semantics ---------- | --------------- | ------------------------------------ -`type` | `PrimitiveType` | type U -`size` | `int64` | The number of elements in the array. +Arguments | Type | Semantics +---------------- | --------------- | ------------------------------------ +`type` | `PrimitiveType` | type U +`size` | `int64` | The number of elements in the array. +`iota_dimension` | `int64` | The dimension to increment along. ## Map @@ -1780,8 +1824,9 @@ XlaBuilder builder(client_, "reduce_window_2x3"); auto shape = ShapeUtil::MakeShape(F32, {4, 6}); auto input = builder.Parameter(0, shape, "input"); builder.ReduceWindow( - input, *max, + input, /*init_val=*/builder.ConstantLiteral(LiteralUtil::MinValue(F32)), + *max, /*window_dimensions=*/{2, 3}, /*window_stride_dimensions=*/{2, 3}, Padding::kValid); diff --git a/tensorflow/compiler/xla/g3doc/tutorials/xla_compile.ipynb b/tensorflow/compiler/xla/g3doc/tutorials/xla_compile.ipynb index a83e3f7859..2a83092805 100644 --- a/tensorflow/compiler/xla/g3doc/tutorials/xla_compile.ipynb +++ b/tensorflow/compiler/xla/g3doc/tutorials/xla_compile.ipynb @@ -1,25 +1,38 @@ { + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "The XLA compile API", + "version": "0.3.2", + "provenance": [], + "collapsed_sections": [], + "toc_visible": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + } + }, "cells": [ { - "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "f4TSNCvpENrW" }, + "cell_type": "markdown", "source": [ "##### Copyright 2018 The TensorFlow Authors." ] }, { - "cell_type": "code", - "execution_count": 0, "metadata": { "cellView": "form", - "colab": {}, "colab_type": "code", - "id": "vamNSA0vEP-m" + "id": "vamNSA0vEP-m", + "colab": {} }, - "outputs": [], + "cell_type": "code", "source": [ "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", "# you may not use this file except in compliance with the License.\n", @@ -32,139 +45,84 @@ "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", "# See the License for the specific language governing permissions and\n", "# limitations under the License." - ] - }, - { - "cell_type": "code", + ], "execution_count": 0, - "metadata": { - "cellView": "form", - "colab": {}, - "colab_type": "code", - "id": "xD_ydfejEV7H" - }, - "outputs": [], - "source": [ - "#@title MIT License\n", - "#\n", - "# Copyright (c) 2017 François Chollet\n", - "#\n", - "# Permission is hereby granted, free of charge, to any person obtaining a\n", - "# copy of this software and associated documentation files (the \"Software\"),\n", - "# to deal in the Software without restriction, including without limitation\n", - "# the rights to use, copy, modify, merge, publish, distribute, sublicense,\n", - "# and/or sell copies of the Software, and to permit persons to whom the\n", - "# Software is furnished to do so, subject to the following conditions:\n", - "#\n", - "# The above copyright notice and this permission notice shall be included in\n", - "# all copies or substantial portions of the Software.\n", - "#\n", - "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n", - "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n", - "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n", - "# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n", - "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n", - "# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n", - "# DEALINGS IN THE SOFTWARE." - ] + "outputs": [] }, { - "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "e1oSi4lHFt3z" }, + "cell_type": "markdown", "source": [ - "# Welcome to `xla.compile()` tutorial" + "# The XLA compile API" ] }, { - "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "b7noD9NjFRL-" }, + "cell_type": "markdown", "source": [ - "\u003ctable class=\"tfo-notebook-buttons\" align=\"left\"\u003e\n", - " \u003ctd\u003e\n", - " \u003ca target=\"_blank\" href=\"https://www.tensorflow.org/xla/jit#turning_on_jit_compilation\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" /\u003eView on TensorFlow.org\u003c/a\u003e\n", - " \u003c/td\u003e\n", - " \u003ctd\u003e\n", - " \u003ca target=\"_blank\" href=\"https://colab.sandbox.google.com/github/tensorflow/compiler/xla/g3doc/tutorials/xla_compile.ipynb\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" /\u003eRun in Google Colab\u003c/a\u003e\n", - " \u003c/td\u003e\n", - " \u003ctd\u003e\n", - " \u003ca target=\"_blank\" href=\"https://github.com/tensorflow/compiler/xla/g3doc/tutorials/xla_compile.ipynb\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" /\u003eView source on GitHub\u003c/a\u003e\n", - " \u003c/td\u003e\n", - "\u003c/table\u003e" + "\n", + " \n", + " \n", + " \n", + "
\n", + " View on TensorFlow.org\n", + " \n", + " Run in Google Colab\n", + " \n", + " View source on GitHub\n", + "
" ] }, { - "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "v9YbsuLZaBXy" }, + "cell_type": "markdown", "source": [ - "xla.compile() is a new experimental API that compiles part or all of a model with [XLA](https://www.tensorflow.org/extend/xla/).\n", "\n", - "Please run all code blocks in order." + "\n", + "Import TensorFlow and the XLA library. XLA contains `xla.compile()`, an experimental API that compiles part or all of a model with [XLA](https://www.tensorflow.org/extend/xla/)." ] }, { - "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "45kUPj5ZFrRa" - }, - "outputs": [], - "source": [ - "import tensorflow as tf" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "9NMQFjroSMns" + "id": "45kUPj5ZFrRa", + "colab": {} }, - "source": [ - "Imports XLA library, which includes xla.compile() experimental API." - ] - }, - { "cell_type": "code", - "execution_count": 0, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "-Uggy03rSGJm" - }, - "outputs": [], "source": [ + "import tensorflow as tf\n", + "\n", "from tensorflow.contrib.compiler import xla" - ] + ], + "execution_count": 0, + "outputs": [] }, { - "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "GZVNiRmTDV-5" }, + "cell_type": "markdown", "source": [ - "Define some necessary constants and prepare MNIST dataset." + "Define some necessary constants and prepare the MNIST dataset." ] }, { - "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "f37TSEGvGX4_" + "id": "f37TSEGvGX4_", + "colab": {} }, - "outputs": [], + "cell_type": "code", "source": [ "# Size of each input image, 28 x 28 pixels\n", "IMAGE_SIZE = 28 * 28\n", @@ -174,17 +132,17 @@ "TRAIN_BATCH_SIZE = 100\n", "# Number of training steps to run\n", "TRAIN_STEPS = 1000" - ] + ], + "execution_count": 0, + "outputs": [] }, { - "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "TiVXchblG5hK" + "id": "TiVXchblG5hK", + "colab": {} }, - "outputs": [], + "cell_type": "code", "source": [ "# Loads MNIST dataset.\n", "train, test = tf.keras.datasets.mnist.load_data()\n", @@ -195,16 +153,18 @@ "images, labels = iterator.get_next()\n", "images = tf.reshape(images, [-1, IMAGE_SIZE])\n", "images, labels = tf.cast(images, tf.float32), tf.cast(labels, tf.int64)" - ] + ], + "execution_count": 0, + "outputs": [] }, { - "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "x_ZehpZP-SfS" }, + "cell_type": "markdown", "source": [ - "## Defines build_mnist_model function to construct model\n", + "# Define the model constructing function\n", "\n", "Following code block contains a function that constructs a simple model with one dense layer, including both forward and backward propagation.\n", "\n", @@ -212,14 +172,12 @@ ] }, { - "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "ZbhJl_WvGa3g" + "id": "ZbhJl_WvGa3g", + "colab": {} }, - "outputs": [], + "cell_type": "code", "source": [ "def build_mnist_model(x, y_):\n", " y = tf.keras.layers.Dense(NUM_CLASSES).apply(x)\n", @@ -228,47 +186,41 @@ " train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)\n", "\n", " return y, train_step" - ] + ], + "execution_count": 0, + "outputs": [] }, { - "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "7Jh3lyQHDfM9" }, - "source": [ - "## Uses xla.compile with build_mnist_model function to enable XLA" - ] - }, - { "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "EtDwez_1gjzv" - }, "source": [ - "Following code block wraps the model with xla.compile(), which allows the target function with provided inputs to be executed by XLA." + "# Enable XLA\n", + "\n", + "Use `xla.compile` with the `build_mnist_model` function to enable XLA. Following code block wraps the model with `xla.compile()`, which allows the target function with provided inputs to be executed by XLA." ] }, { - "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "kYpCXCdRHNuN" + "id": "kYpCXCdRHNuN", + "colab": {} }, - "outputs": [], + "cell_type": "code", "source": [ "[y] = xla.compile(build_mnist_model, inputs=[images, labels])" - ] + ], + "execution_count": 0, + "outputs": [] }, { - "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "4giQh62IrZGF" }, + "cell_type": "markdown", "source": [ "When compiling the graph, XLA replaces all the graph nodes constructed in the target function with a few XLA ops.\n", "\n", @@ -293,62 +245,62 @@ ] }, { - "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "TPGas4jjFLZl" }, + "cell_type": "markdown", "source": [ "If you were to print the constructed graph now, you will see that it is not much different from a normal Tensorflow graph and you won't be able to find XLA ops mentioned before. This is because the actual compilation happens later when you try to execute the graph with `sess.run()`. At that time, Tensorflow triggers a series of graph rewrite passes that actually generate XLA ops, which compiles and executes computation when all inputs are ready." ] }, { - "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "EZD1m_n1DxAF" }, + "cell_type": "markdown", "source": [ - "## Trains and tests the model" + "# Train and test the model" ] }, { - "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "qe28bAHNHUG2" + "id": "qe28bAHNHUG2", + "colab": {} }, - "outputs": [], + "cell_type": "code", "source": [ "# Creates session and initialize all variables.\n", "# xla.compile() doesn't work with Keras model.fit() API or TF eager mode yet.\n", "sess = tf.Session()\n", "sess.run(tf.global_variables_initializer())" - ] + ], + "execution_count": 0, + "outputs": [] }, { - "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "qgsKmz3n2UiW" }, + "cell_type": "markdown", "source": [ - "Following code block trains model.\n", - "\n", - "Note that evaluating `y` also triggers its control dependency node `train_step`, which updates model variables." + "Following code block trains model. Evaluating `y` also triggers its control dependency node `train_step`, which updates model variables." ] }, { - "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "_GxF6jTRHVuA" + "id": "_GxF6jTRHVuA", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 + }, + "outputId": "fbf299ca-02d5-4e95-f9fe-8f3c0432d132" }, - "outputs": [], + "cell_type": "code", "source": [ "# Feeds training dataset\n", "sess.run(iterator.make_initializer(train_ds))\n", @@ -356,18 +308,31 @@ "# Runs TRAIN_STEPS steps\n", "for i in range(TRAIN_STEPS):\n", " sess.run(y)\n", + "\n", "print(\"Model trained for %s steps.\" % TRAIN_STEPS)" + ], + "execution_count": 21, + "outputs": [ + { + "output_type": "stream", + "text": [ + "Model trained for 1000 steps.\n" + ], + "name": "stdout" + } ] }, { - "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "dHlQlRSRHXD1" + "id": "dHlQlRSRHXD1", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 + }, + "outputId": "9c3677a2-ec84-406f-9d2c-d722844f3093" }, - "outputs": [], + "cell_type": "code", "source": [ "# Tests trained model\n", "\n", @@ -378,35 +343,31 @@ "correct_prediction = tf.equal(tf.argmax(y, 1), labels)\n", "accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))\n", "print(\"Prediction accuracy after training: %s\" % sess.run(accuracy))" + ], + "execution_count": 22, + "outputs": [ + { + "output_type": "stream", + "text": [ + "Prediction accuracy after training: 0.91\n" + ], + "name": "stdout" + } ] }, { - "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "ynJQIuzjHYOb" + "id": "ynJQIuzjHYOb", + "colab": {} }, - "outputs": [], + "cell_type": "code", "source": [ "# Cleans up session\n", "sess.close()" - ] - } - ], - "metadata": { - "colab": { - "collapsed_sections": [], - "name": "xla.compile() Tutorial", - "provenance": [], - "version": "0.3.2" - }, - "kernelspec": { - "display_name": "Python 2", - "name": "python2" + ], + "execution_count": 0, + "outputs": [] } - }, - "nbformat": 4, - "nbformat_minor": 0 -} + ] +} \ No newline at end of file diff --git a/tensorflow/compiler/xla/index_util.h b/tensorflow/compiler/xla/index_util.h index 458bdaf2f8..d76f61eb62 100644 --- a/tensorflow/compiler/xla/index_util.h +++ b/tensorflow/compiler/xla/index_util.h @@ -21,6 +21,7 @@ limitations under the License. #include #include "absl/types/span.h" +#include "tensorflow/compiler/xla/shape.h" #include "tensorflow/compiler/xla/types.h" #include "tensorflow/compiler/xla/xla_data.pb.h" #include "tensorflow/core/platform/macros.h" diff --git a/tensorflow/compiler/xla/layout_util.cc b/tensorflow/compiler/xla/layout_util.cc index 2398470dd4..dbb81381ac 100644 --- a/tensorflow/compiler/xla/layout_util.cc +++ b/tensorflow/compiler/xla/layout_util.cc @@ -460,6 +460,13 @@ std::ostream& operator<<(std::ostream& out, const Layout& layout) { } hash_value = Hash64Combine(hash_value, layout.max_sparse_elements()); + for (Tile tile : layout.tiles()) { + for (int64 tile_dim : tile.dimensions()) { + hash_value = Hash64Combine(hash_value, hash()(tile_dim)); + } + } + hash_value = Hash64Combine(hash_value, layout.element_size_in_bits()); + return hash_value; } diff --git a/tensorflow/compiler/xla/layout_util.h b/tensorflow/compiler/xla/layout_util.h index 6e0390763d..6c298e5725 100644 --- a/tensorflow/compiler/xla/layout_util.h +++ b/tensorflow/compiler/xla/layout_util.h @@ -21,6 +21,7 @@ limitations under the License. #include #include "absl/types/span.h" +#include "tensorflow/compiler/xla/shape.h" #include "tensorflow/compiler/xla/status.h" #include "tensorflow/compiler/xla/types.h" #include "tensorflow/compiler/xla/xla_data.pb.h" diff --git a/tensorflow/compiler/xla/literal.cc b/tensorflow/compiler/xla/literal.cc index cb00a0ab16..8f480c1f10 100644 --- a/tensorflow/compiler/xla/literal.cc +++ b/tensorflow/compiler/xla/literal.cc @@ -27,6 +27,7 @@ limitations under the License. #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/strings/str_join.h" +#include "absl/types/span.h" #include "tensorflow/compiler/xla/index_util.h" #include "tensorflow/compiler/xla/shape_util.h" #include "tensorflow/compiler/xla/status_macros.h" @@ -62,6 +63,14 @@ void ConvertEndianShort(char* bytes, int64 size) { } } +// Since Eigen::half doesn't satisfy the absl::bit_cast contract, we need to be +// able to transparently access the raw 16-bit value contained within. +template +T GetRawValue(T val) { + return val; +} +uint16 GetRawValue(Eigen::half val) { return val.x; } + } // namespace LiteralBase::~LiteralBase() {} @@ -283,16 +292,17 @@ Status MutableLiteralBase::CopyElementFrom(const LiteralSlice& src_literal, if (!proto.has_shape()) { return InvalidArgument("LiteralProto has no shape"); } - if (ShapeUtil::HasPrimitiveType(proto.shape(), OPAQUE)) { + Shape shape(proto.shape()); + if (ShapeUtil::HasPrimitiveType(shape, OPAQUE)) { return InvalidArgument("Literal shape cannot include OPAQUE sub-shape"); } - if (!LayoutUtil::HasLayout(proto.shape())) { + if (!LayoutUtil::HasLayout(shape)) { return InvalidArgument("LiteralProto has no layout"); } - TF_RETURN_IF_ERROR(ShapeUtil::ValidateShapeWithOptionalLayout(proto.shape())); + TF_RETURN_IF_ERROR(ShapeUtil::ValidateShapeWithOptionalLayout(shape)); - Literal literal(proto.shape()); + Literal literal(shape); TF_RETURN_IF_ERROR(literal.root_piece_->ForEachMutableSubpieceWithStatus( [&](const ShapeIndex& index, Piece* piece) { @@ -1012,166 +1022,143 @@ void LiteralBase::Piece::SortSparseElementsInternal() { namespace { -void ToStringHelper(const LiteralBase& literal, const ShapeIndex& shape_index, - bool print_layout, std::vector* pieces) { - const Shape& subshape = ShapeUtil::GetSubshape(literal.shape(), shape_index); - CHECK(LayoutUtil::HasLayout(literal.shape())); - CHECK(LayoutUtil::HasLayout(subshape)); +string ShapeToString(bool print_layout, const Shape& shape) { + return print_layout ? ShapeUtil::HumanStringWithLayout(shape) + : ShapeUtil::HumanString(shape); +} - auto shape_to_string = [print_layout](const Shape& shape) { - if (print_layout) { - return ShapeUtil::HumanStringWithLayout(shape); - } else { - return ShapeUtil::HumanString(shape); - } - }; +void ToStringHelper(const LiteralBase& literal, const ShapeIndex& shape_index, + bool print_layout, std::vector* pieces); - // TODO(b/32894291): refactor this code to reduce code duplication. - if (ShapeUtil::IsTuple(subshape)) { - pieces->push_back(shape_to_string(subshape)); - pieces->push_back(" (\n"); - std::vector tuple_pieces; - for (int i = 0; i < ShapeUtil::TupleElementCount(subshape); ++i) { - ShapeIndex element_index = shape_index; - element_index.push_back(i); - std::vector element_pieces; - ToStringHelper(literal, element_index, print_layout, &element_pieces); - tuple_pieces.push_back(absl::StrJoin(element_pieces, "")); +void TupleToStringHelper(const LiteralBase& literal, + const ShapeIndex& shape_index, bool print_layout, + std::vector* pieces) { + const Shape& subshape = ShapeUtil::GetSubshape(literal.shape(), shape_index); + pieces->push_back(ShapeToString(print_layout, subshape)); + pieces->push_back(" (\n"); + std::vector tuple_pieces; + for (int i = 0; i < ShapeUtil::TupleElementCount(subshape); ++i) { + ShapeIndex element_index = shape_index; + element_index.push_back(i); + std::vector element_pieces; + ToStringHelper(literal, element_index, print_layout, &element_pieces); + tuple_pieces.push_back(absl::StrJoin(element_pieces, "")); + } + pieces->push_back(absl::StrJoin(tuple_pieces, ",\n")); + pieces->push_back("\n)"); +} + +void SparseArrayToStringHelper(const LiteralBase& literal, + const Shape& subshape, bool print_layout, + std::vector* pieces) { + pieces->push_back(ShapeToString(print_layout, subshape)); + pieces->push_back("{"); + int64 rank = ShapeUtil::Rank(subshape); + int64 num_elements = literal.sparse_element_count(); + for (int64 i = 0; i < num_elements; ++i) { + if (i > 0) { + pieces->push_back(", "); } - pieces->push_back(absl::StrJoin(tuple_pieces, ",\n")); - pieces->push_back("\n)"); - return; - } - - if (ShapeUtil::IsToken(subshape)) { - pieces->push_back("token"); - return; - } - - if (LayoutUtil::IsSparseArray(subshape)) { - pieces->push_back(shape_to_string(subshape)); - pieces->push_back("{"); - int64 rank = ShapeUtil::Rank(subshape); - int64 num_elements = literal.sparse_element_count(); - for (int64 i = 0; i < num_elements; ++i) { - if (i > 0) { - pieces->push_back(", "); - } - if (rank == 1) { - pieces->push_back(StrCat(literal.GetSparseIndex(i)[0])); - pieces->push_back(": "); - } else { - pieces->push_back("["); - pieces->push_back(absl::StrJoin(literal.GetSparseIndex(i), ", ")); - pieces->push_back("]: "); - } - pieces->push_back(literal.GetSparseElementAsString(i)); + if (rank == 1) { + pieces->push_back(StrCat(literal.GetSparseIndex(i)[0])); + pieces->push_back(": "); + } else { + pieces->push_back("["); + pieces->push_back(absl::StrJoin(literal.GetSparseIndex(i), ", ")); + pieces->push_back("]: "); } - pieces->push_back("}"); - return; + pieces->push_back(literal.GetSparseElementAsString(i)); } + pieces->push_back("}"); +} - CHECK(LayoutUtil::IsDenseArray(subshape)); - - auto element_to_string = [&](absl::Span indices) -> string { - PrimitiveType element_type = subshape.element_type(); - // We display predicates as 0s and 1s so that the string is more dense. - string elem = element_type == PRED - ? literal.Get(indices, shape_index) ? "1" : "0" - : literal.GetAsString(indices, shape_index); - return ((!indices.empty() && indices.back() > 0) ? ", " : "") + elem; - }; +void DenseArrayToStringHelper(const LiteralBase& literal, + const ShapeIndex& shape_index, bool print_layout, + std::vector* pieces) { + const Shape& subshape = ShapeUtil::GetSubshape(literal.shape(), shape_index); + int64 rank = ShapeUtil::Rank(subshape); + + std::function dimensions, std::vector*)> + to_string_recursive = [&](absl::Span dimensions, + std::vector* accum_indices) { + // dimensions.size() decreases by 1 at each recursive call, + // and accum_indices->size() increases by 1. + // Their sum is equal to the rank of the tensor. + CHECK_EQ(rank, dimensions.size() + accum_indices->size()); + + auto brace_to_string = [&](string brace) -> string { + // Handle 1D tensor + if (rank == 1) { + return brace; + } + // Handle the innermost tensor of a 2D+ tensor. + if (dimensions.size() == 1 && brace == "{") { + return StrCat(" ", brace, dimensions[0] <= 1 ? "" : " "); + } + if (dimensions.size() == 1 && brace == "}") { + return StrCat(dimensions[0] <= 1 ? "" : " ", brace); + } + // Handle the non-innermost tensors of a 2D+ tensor. + if (brace == "{") { + if (rank > 3 && !accum_indices->empty() && + accum_indices->size() < rank) { + int index = accum_indices->size() - 1; + int value = accum_indices->back(); + return StrCat(brace, " /*i", index, "=", value, "*/\n"); + } + return StrCat(brace, "\n"); + } + return StrCat("\n", brace); + }; - if (ShapeUtil::Rank(subshape) == 0) { - pieces->push_back(literal.GetAsString({}, shape_index)); - } else if (ShapeUtil::Rank(subshape) == 1) { - pieces->push_back("{"); - for (int64 i0 = 0; i0 < subshape.dimensions(0); ++i0) { - pieces->push_back(element_to_string({i0})); - } - pieces->push_back("}"); - } else if (ShapeUtil::Rank(subshape) == 2) { - pieces->push_back(shape_to_string(subshape)); - pieces->push_back(" {\n"); - for (int64 i0 = 0; i0 < subshape.dimensions(0); ++i0) { - pieces->push_back(" { "); - for (int64 i1 = 0; i1 < subshape.dimensions(1); ++i1) { - pieces->push_back(element_to_string({i0, i1})); - } - pieces->push_back(" "); - pieces->push_back(i0 == subshape.dimensions(0) - 1 ? "}\n" : "},\n"); - } - pieces->push_back("}"); - } else if (ShapeUtil::Rank(subshape) == 3) { - pieces->push_back(shape_to_string(subshape)); - pieces->push_back(" {\n"); - for (int64 i0 = 0; i0 < subshape.dimensions(0); ++i0) { - pieces->push_back(i0 > 0 ? ",\n{" : "{"); - for (int64 i1 = 0; i1 < subshape.dimensions(1); ++i1) { - pieces->push_back(i1 > 0 ? ",\n { " : " { "); - for (int64 i2 = 0; i2 < subshape.dimensions(2); ++i2) { - pieces->push_back(element_to_string({i0, i1, i2})); - } - pieces->push_back(" }"); - } - pieces->push_back(" }"); - } - pieces->push_back("\n}"); - } else if (ShapeUtil::Rank(subshape) == 4) { - pieces->push_back(shape_to_string(subshape)); - pieces->push_back(" {\n"); - for (int64 i0 = 0; i0 < subshape.dimensions(0); ++i0) { - pieces->push_back(StrFormat(" { /*i0=%d*/\n", i0)); - for (int64 i1 = 0; i1 < subshape.dimensions(1); ++i1) { - pieces->push_back(StrFormat(" { /*i1=%d*/\n", i1)); - for (int64 i2 = 0; i2 < subshape.dimensions(2); ++i2) { - pieces->push_back(" {"); - for (int64 i3 = 0; i3 < subshape.dimensions(3); ++i3) { - pieces->push_back(element_to_string({i0, i1, i2, i3})); + if (dimensions.empty()) { + // Display predicates as 0s and 1s so that the string is more dense. + string elem; + if (subshape.element_type() == PRED && rank > 0) { + elem = literal.Get(*accum_indices, shape_index) ? "1" : "0"; + } else { + elem = literal.GetAsString(*accum_indices, shape_index); } - pieces->push_back(i2 == subshape.dimensions(2) - 1 ? "}\n" : "},\n"); - } - pieces->push_back(i1 == subshape.dimensions(1) - 1 ? " }\n" - : " },\n"); - } - pieces->push_back(i0 == subshape.dimensions(0) - 1 ? " }\n" : " },\n"); - } - pieces->push_back("}"); - } else if (ShapeUtil::Rank(subshape) == 5) { - pieces->push_back(shape_to_string(subshape)); - pieces->push_back(" {\n"); - for (int64 i0 = 0; i0 < subshape.dimensions(0); ++i0) { - pieces->push_back(StrFormat(" { /*i0=%d*/\n", i0)); - for (int64 i1 = 0; i1 < subshape.dimensions(1); ++i1) { - pieces->push_back(StrFormat(" { /*i1=%d*/\n", i1)); - for (int64 i2 = 0; i2 < subshape.dimensions(2); ++i2) { - pieces->push_back(StrFormat(" { /*i2=%d*/\n", i2)); - for (int64 i3 = 0; i3 < subshape.dimensions(3); ++i3) { - pieces->push_back(" {"); - for (int64 i4 = 0; i4 < subshape.dimensions(4); ++i4) { - pieces->push_back(element_to_string({i0, i1, i2, i3, i4})); + pieces->push_back(elem); + } else { + pieces->push_back(brace_to_string("{")); + for (int i = 0; i < dimensions[0]; ++i) { + std::vector cloned_indices(*accum_indices); + cloned_indices.push_back(i); + to_string_recursive(dimensions.subspan(1), &cloned_indices); + if (i < dimensions[0] - 1) { + pieces->push_back(","); + pieces->push_back(dimensions.size() > 1 ? "\n" : " "); } - pieces->push_back(i3 == subshape.dimensions(3) - 1 ? "}\n" - : "},\n"); } - pieces->push_back(i2 == subshape.dimensions(2) - 1 ? " }\n" - : " },\n"); + pieces->push_back(brace_to_string("}")); } - pieces->push_back(i1 == subshape.dimensions(1) - 1 ? " }\n" - : " },\n"); - } - pieces->push_back(i0 == subshape.dimensions(0) - 1 ? " }\n" : " },\n"); - } - pieces->push_back("}"); + }; + + if (rank > 1) { + pieces->push_back(ShapeToString(print_layout, subshape)); + pieces->push_back(" "); + } + std::vector indices = {}; + std::vector dimensions(subshape.dimensions().begin(), + subshape.dimensions().end()); + to_string_recursive(dimensions, &indices); +} + +void ToStringHelper(const LiteralBase& literal, const ShapeIndex& shape_index, + bool print_layout, std::vector* pieces) { + const Shape& subshape = ShapeUtil::GetSubshape(literal.shape(), shape_index); + CHECK(LayoutUtil::HasLayout(literal.shape())); + CHECK(LayoutUtil::HasLayout(subshape)); + if (ShapeUtil::IsTuple(subshape)) { + TupleToStringHelper(literal, shape_index, print_layout, pieces); + } else if (ShapeUtil::IsToken(subshape)) { + pieces->push_back("token"); + } else if (LayoutUtil::IsSparseArray(subshape)) { + SparseArrayToStringHelper(literal, subshape, print_layout, pieces); } else { - pieces->push_back(shape_to_string(subshape)); - pieces->push_back(" {"); - literal.EachCellAsString( - [&](absl::Span indices, const string& value) { - pieces->push_back(" "); - pieces->push_back(value); - }); - pieces->push_back("}"); + CHECK(LayoutUtil::IsDenseArray(subshape)); + DenseArrayToStringHelper(literal, shape_index, print_layout, pieces); } } @@ -1228,16 +1215,32 @@ Literal ConvertBetweenNativeTypes(const LiteralBase& src_literal) { } template -typename std::enable_if<(sizeof(NativeSrcT) == sizeof(NativeDestT)), +typename std::enable_if<(sizeof(NativeSrcT) == sizeof(NativeDestT) && + !std::is_same::value), Literal>::type BitcastBetweenNativeTypes(const LiteralBase& src_literal) { auto converter = [](NativeSrcT src) { - return absl::bit_cast(src); + return absl::bit_cast(GetRawValue(src)); }; return ConvertBetweenNativeTypesWithConverter( src_literal, converter); } +template +typename std::enable_if<(sizeof(NativeSrcT) == sizeof(Eigen::half) && + std::is_same::value), + Literal>::type +BitcastBetweenNativeTypes(const LiteralBase& src_literal) { + // Eigen::half doesn't satisfy the absl::bit_cast contract, so explicitly + // cast to unsigned short and then use raw_uint16_to_half. + auto converter = [](NativeSrcT src) { + return Eigen::half_impl::raw_uint16_to_half( + absl::bit_cast(GetRawValue(src))); + }; + return ConvertBetweenNativeTypesWithConverter( + src_literal, converter); +} + // This template specialization is here to make the compiler happy. bit_cast has // a static check that the types are the same size. This specialization should // never be used because the source and destination types are checked for @@ -1792,7 +1795,7 @@ void CopyToRepeatedField(RepeatedFieldT* dest, } // namespace void LiteralBase::Piece::WriteToProto(LiteralProto* proto) const { - *proto->mutable_shape() = subshape(); + *proto->mutable_shape() = subshape().ToProto(); switch (subshape().element_type()) { case PRED: CopyToRepeatedField(proto->mutable_preds(), data()); @@ -1898,8 +1901,9 @@ Status LiteralBase::Piece::CopyFromProto(const LiteralProto& proto) { // These conditions should have been checked in // MutableLiteralBase::CreateFromProto. TF_RET_CHECK(proto.has_shape()); - TF_RET_CHECK(LayoutUtil::HasLayout(proto.shape())); - TF_RET_CHECK(ShapeUtil::Equal(proto.shape(), subshape())); + Shape shape(proto.shape()); + TF_RET_CHECK(LayoutUtil::HasLayout(shape)); + TF_RET_CHECK(ShapeUtil::Equal(shape, subshape())); if (LayoutUtil::IsSparseArray(subshape())) { // Compute the number of elements (indices) in the sparse shape and reserve diff --git a/tensorflow/compiler/xla/literal.h b/tensorflow/compiler/xla/literal.h index e791048b4d..fa9a71af4c 100644 --- a/tensorflow/compiler/xla/literal.h +++ b/tensorflow/compiler/xla/literal.h @@ -301,7 +301,7 @@ class LiteralBase { // // Note: It's an antipattern to use this method then immediately call // MutableLiteralBase::Populate on the result (since that results in zero - // initialization, then reinitialization. Conside if a call to + // initialization, then reinitialization. Consider if a call to // absl::make_unique(shape), followed by the call to // MutableLiteralBase::Populate can be used instead. static Literal CreateFromShape(const Shape& shape); diff --git a/tensorflow/compiler/xla/literal_test.cc b/tensorflow/compiler/xla/literal_test.cc index 8cec37897a..49363ad802 100644 --- a/tensorflow/compiler/xla/literal_test.cc +++ b/tensorflow/compiler/xla/literal_test.cc @@ -150,12 +150,58 @@ TEST_F(LiteralUtilTest, R3ToString) { const auto literal = LiteralUtil::CreateR3({{{1}, {2}}, {{3}, {4}}, {{5}, {6}}}); const string expected = R"(s32[3,2,1] { -{ { 1 }, - { 2 } }, -{ { 3 }, - { 4 } }, -{ { 5 }, - { 6 } } +{ + {1}, + {2} +}, +{ + {3}, + {4} +}, +{ + {5}, + {6} +} +})"; + EXPECT_EQ(expected, literal.ToString()); +} + +TEST_F(LiteralUtilTest, R6ToString) { + const auto literal = + LiteralUtil::CreateFromDimensions(S32, {2, 2, 1, 1, 1, 2}); + const string expected = R"(s32[2,2,1,1,1,2] { +{ /*i0=0*/ +{ /*i1=0*/ +{ /*i2=0*/ +{ /*i3=0*/ + { 0, 0 } +} +} +}, +{ /*i1=1*/ +{ /*i2=0*/ +{ /*i3=0*/ + { 0, 0 } +} +} +} +}, +{ /*i0=1*/ +{ /*i1=0*/ +{ /*i2=0*/ +{ /*i3=0*/ + { 0, 0 } +} +} +}, +{ /*i1=1*/ +{ /*i2=0*/ +{ /*i3=0*/ + { 0, 0 } +} +} +} +} })"; EXPECT_EQ(expected, literal.ToString()); } @@ -190,12 +236,16 @@ TEST_F(LiteralUtilTest, CreateR3FromArray3d) { EXPECT_THAT(literal.shape().dimensions(), ElementsAre(2, 3, 2)); string result = literal.ToString(); const string expected = R"(f32[2,3,2] { -{ { 1, 2 }, +{ + { 1, 2 }, { 3, 4 }, - { 5, 6 } }, -{ { 7, 8 }, + { 5, 6 } +}, +{ + { 7, 8 }, { 9, 10 }, - { 11, 12 } } + { 11, 12 } +} })"; EXPECT_EQ(expected, result); } @@ -247,18 +297,18 @@ TEST_F(LiteralUtilTest, LiteralR4F32ProjectedStringifies) { EXPECT_THAT(literal.shape().dimensions(), ElementsAre(1, 2, 3, 2)); string result = literal.ToString(); const string expected = R"(f32[1,2,3,2] { - { /*i0=0*/ - { /*i1=0*/ - {1, 2}, - {1001, 1002}, - {2001, 2002} - }, - { /*i1=1*/ - {1, 2}, - {1001, 1002}, - {2001, 2002} - } - } +{ /*i0=0*/ +{ /*i1=0*/ + { 1, 2 }, + { 1001, 1002 }, + { 2001, 2002 } +}, +{ /*i1=1*/ + { 1, 2 }, + { 1001, 1002 }, + { 2001, 2002 } +} +} })"; EXPECT_EQ(expected, result); } @@ -268,30 +318,30 @@ TEST_F(LiteralUtilTest, LiteralR4F32Stringifies) { ElementsAre(2, 2, 3, 3)); string result = literal_r4_2x2x3x3_dim0major_.ToString(); const string expected = R"(f32[2,2,3,3] { - { /*i0=0*/ - { /*i1=0*/ - {1, 2, 3}, - {4, 5, 6}, - {7, 8, 9} - }, - { /*i1=1*/ - {11, 12, 13}, - {14, 15, 16}, - {17, 18, 19} - } - }, - { /*i0=1*/ - { /*i1=0*/ - {101, 102, 103}, - {104, 105, 106}, - {107, 108, 109} - }, - { /*i1=1*/ - {201, 202, 203}, - {204, 205, 206}, - {207, 208, 209} - } - } +{ /*i0=0*/ +{ /*i1=0*/ + { 1, 2, 3 }, + { 4, 5, 6 }, + { 7, 8, 9 } +}, +{ /*i1=1*/ + { 11, 12, 13 }, + { 14, 15, 16 }, + { 17, 18, 19 } +} +}, +{ /*i0=1*/ +{ /*i1=0*/ + { 101, 102, 103 }, + { 104, 105, 106 }, + { 107, 108, 109 } +}, +{ /*i1=1*/ + { 201, 202, 203 }, + { 204, 205, 206 }, + { 207, 208, 209 } +} +} })"; EXPECT_EQ(expected, result); } @@ -1327,13 +1377,26 @@ TEST_F(LiteralUtilTest, BitcastConvertBetweenInvalidTypes) { absl::StrContains(status.error_message(), "bit widths are different")); } +// Sets the layout of the given ShapeProto to the default. +void SetDefaultLayoutOnProto(ShapeProto* shape_proto) { + CHECK(ShapeUtil::IsArrayPrimitiveType(shape_proto->element_type())); + shape_proto->mutable_layout()->set_format(DENSE); + auto* minor_to_major = + shape_proto->mutable_layout()->mutable_minor_to_major(); + minor_to_major->Resize(shape_proto->dimensions_size(), 0); + const int64 size = minor_to_major->size(); + for (int64 i = 0; i < size; ++i) { + minor_to_major->Set(i, size - 1 - i); + } +} + TEST_F(LiteralUtilTest, CopyFromProto_Bool) { LiteralProto p; p.mutable_shape()->set_element_type(PRED); for (int len = 0; len < 25; ++len) { p.mutable_shape()->clear_dimensions(); p.mutable_shape()->add_dimensions(len); - LayoutUtil::SetToDefaultLayout(p.mutable_shape()); + SetDefaultLayoutOnProto(p.mutable_shape()); p.clear_preds(); for (int i = 0; i < len; ++i) { p.add_preds((i % 2) == (len % 2)); @@ -1359,7 +1422,7 @@ TEST_F(LiteralUtilTest, ToProto_f16) { EXPECT_EQ(4, m.data().size()); LiteralProto p = m.ToProto(); - EXPECT_EQ(4, ShapeUtil::ElementsIn(p.shape())); + EXPECT_EQ(4, ShapeUtil::ElementsIn(Shape(p.shape()))); EXPECT_EQ(8, p.f16s().size()); const char* d = p.f16s().data(); EXPECT_EQ(d[0], 0); @@ -1382,7 +1445,7 @@ TEST_F(LiteralUtilTest, CopyFromProto_f16) { p.mutable_shape()->set_element_type(F16); p.mutable_shape()->clear_dimensions(); p.mutable_shape()->add_dimensions(4); - LayoutUtil::SetToDefaultLayout(p.mutable_shape()); + SetDefaultLayoutOnProto(p.mutable_shape()); p.clear_f16s(); p.set_f16s(half_vals, 8); TF_ASSERT_OK_AND_ASSIGN(Literal literal, Literal::CreateFromProto(p)); @@ -1404,7 +1467,7 @@ TEST_F(LiteralUtilTest, CopyFromProto_u16) { p.mutable_shape()->set_element_type(U16); p.mutable_shape()->clear_dimensions(); p.mutable_shape()->add_dimensions(4); - LayoutUtil::SetToDefaultLayout(p.mutable_shape()); + SetDefaultLayoutOnProto(p.mutable_shape()); p.clear_u16s(); p.set_u16s(uint16_vals, 8); TF_ASSERT_OK_AND_ASSIGN(Literal literal, Literal::CreateFromProto(p)); @@ -1537,9 +1600,9 @@ TEST_F(LiteralUtilTest, DecomposeTuple) { Literal nested_tuple = LiteralUtil::MakeTuple( {&tuple_elements[0], &tuple_elements[1], &nil_literal}); - EXPECT_FALSE(ShapeUtil::IsNil(nested_tuple.shape())); + EXPECT_FALSE(ShapeUtil::IsEmptyTuple(nested_tuple.shape())); std::vector elements = nested_tuple.DecomposeTuple(); - EXPECT_TRUE(ShapeUtil::IsNil(nested_tuple.shape())); + EXPECT_TRUE(ShapeUtil::IsEmptyTuple(nested_tuple.shape())); ASSERT_EQ(elements.size(), 3); @@ -1590,7 +1653,7 @@ TEST_F(LiteralUtilTest, MoveIntoTuple) { EXPECT_EQ(literal.Get({1}, /*shape_index=*/{2, 1}), 44.0); for (const Literal& element : elements) { - EXPECT_TRUE(ShapeUtil::IsNil(element.shape())); + EXPECT_TRUE(ShapeUtil::IsEmptyTuple(element.shape())); } } @@ -1706,7 +1769,7 @@ TEST_F(LiteralUtilTest, ProtoRoundTrip) { TEST_F(LiteralUtilTest, InvalidProtoNoValues) { // Proto contains a shape, but no values. LiteralProto proto; - *proto.mutable_shape() = ShapeUtil::MakeShape(F32, {3}); + *proto.mutable_shape() = ShapeUtil::MakeShape(F32, {3}).ToProto(); Status status = Literal::CreateFromProto(proto).status(); ASSERT_FALSE(status.ok()); EXPECT_THAT(status.error_message(), @@ -1727,7 +1790,7 @@ TEST_F(LiteralUtilTest, InvalidProtoNoShape) { TEST_F(LiteralUtilTest, InvalidProtoWrongContainer) { // Proto contains values in wrong container. LiteralProto proto; - *proto.mutable_shape() = ShapeUtil::MakeShape(F32, {3}); + *proto.mutable_shape() = ShapeUtil::MakeShape(F32, {3}).ToProto(); proto.add_preds(false); proto.add_preds(true); proto.add_preds(false); @@ -1740,7 +1803,7 @@ TEST_F(LiteralUtilTest, InvalidProtoWrongContainer) { TEST_F(LiteralUtilTest, InvalidProtoTooFewValues) { // Proto contains too few values. LiteralProto proto; - *proto.mutable_shape() = ShapeUtil::MakeShape(F32, {42, 2}); + *proto.mutable_shape() = ShapeUtil::MakeShape(F32, {42, 2}).ToProto(); proto.add_f32s(1.0); proto.add_f32s(2.0); proto.add_f32s(3.0); @@ -1753,7 +1816,7 @@ TEST_F(LiteralUtilTest, InvalidProtoTooFewValues) { TEST_F(LiteralUtilTest, InvalidProtoTooManyValues) { // Proto contains too many values. LiteralProto proto; - *proto.mutable_shape() = ShapeUtil::MakeShape(S32, {2}); + *proto.mutable_shape() = ShapeUtil::MakeShape(S32, {2}).ToProto(); proto.add_s32s(42); proto.add_s32s(-10); proto.add_s32s(100); @@ -1766,8 +1829,8 @@ TEST_F(LiteralUtilTest, InvalidProtoTooManyValues) { TEST_F(LiteralUtilTest, InvalidProtoMissingLayout) { // Proto shape missing layout. LiteralProto proto; - *proto.mutable_shape() = ShapeUtil::MakeShape(PRED, {2, 2}); - LayoutUtil::ClearLayout(proto.mutable_shape()); + *proto.mutable_shape() = ShapeUtil::MakeShape(PRED, {2, 2}).ToProto(); + proto.mutable_shape()->clear_layout(); proto.add_preds(true); proto.add_preds(false); proto.add_preds(true); @@ -1780,11 +1843,13 @@ TEST_F(LiteralUtilTest, InvalidProtoMissingLayout) { TEST_F(LiteralUtilTest, InvalidProtoTooFewTupleElements) { // Proto has the too few tuple elements. LiteralProto proto; - *proto.mutable_shape() = ShapeUtil::MakeTupleShape( - {ShapeUtil::MakeShape(PRED, {2}), ShapeUtil::MakeShape(F32, {})}); + *proto.mutable_shape() = + ShapeUtil::MakeTupleShape( + {ShapeUtil::MakeShape(PRED, {2}), ShapeUtil::MakeShape(F32, {})}) + .ToProto(); LiteralProto* element0 = proto.add_tuple_literals(); *element0->mutable_shape() = - ShapeUtil::GetTupleElementShape(proto.shape(), 0); + ShapeUtil::GetTupleElementShape(Shape(proto.shape()), 0).ToProto(); element0->add_preds(false); element0->add_preds(true); @@ -1796,19 +1861,21 @@ TEST_F(LiteralUtilTest, InvalidProtoTooFewTupleElements) { TEST_F(LiteralUtilTest, InvalidProtoTooManyTupleElements) { // Proto has the too many tuple elements. LiteralProto proto; - *proto.mutable_shape() = ShapeUtil::MakeTupleShape( - {ShapeUtil::MakeShape(PRED, {2}), ShapeUtil::MakeShape(F32, {})}); + *proto.mutable_shape() = + ShapeUtil::MakeTupleShape( + {ShapeUtil::MakeShape(PRED, {2}), ShapeUtil::MakeShape(F32, {})}) + .ToProto(); LiteralProto* element0 = proto.add_tuple_literals(); *element0->mutable_shape() = - ShapeUtil::GetTupleElementShape(proto.shape(), 0); + ShapeUtil::GetTupleElementShape(Shape(proto.shape()), 0).ToProto(); element0->add_preds(false); element0->add_preds(true); LiteralProto* element1 = proto.add_tuple_literals(); *element1->mutable_shape() = - ShapeUtil::GetTupleElementShape(proto.shape(), 1); + ShapeUtil::GetTupleElementShape(Shape(proto.shape()), 1).ToProto(); element1->add_f32s(42.0); LiteralProto* element2 = proto.add_tuple_literals(); - *element2->mutable_shape() = ShapeUtil::MakeShape(F32, {}); + *element2->mutable_shape() = ShapeUtil::MakeShape(F32, {}).ToProto(); element2->add_f32s(123.0); Status status = Literal::CreateFromProto(proto).status(); diff --git a/tensorflow/compiler/xla/parse_flags_from_env.cc b/tensorflow/compiler/xla/parse_flags_from_env.cc index 40481331b6..5b568888d1 100644 --- a/tensorflow/compiler/xla/parse_flags_from_env.cc +++ b/tensorflow/compiler/xla/parse_flags_from_env.cc @@ -13,15 +13,20 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -// This module exports ParseFlagsFromEnv(), which allows other modules to parse -// flags from an environtment variable, or a file named by the environment -// variable. +// This module exports ParseFlagsFromEnvAndDieIfUnknown(), which allows other +// modules to parse flags from an environtment variable, or a file named by the +// environment variable. #include #include #include +#include +#include #include +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" +#include "absl/types/span.h" #include "tensorflow/compiler/xla/parse_flags_from_env.h" #include "tensorflow/compiler/xla/types.h" #include "tensorflow/core/platform/logging.h" @@ -32,7 +37,6 @@ limitations under the License. namespace xla { -static const char kEnvVar[] = "TF_XLA_FLAGS"; // environment variable queried static const char kWS[] = " \t\r\n"; // whitespace // The following struct represents an argv[]-style array, parsed @@ -42,12 +46,20 @@ static const char kWS[] = " \t\r\n"; // whitespace // constructor/destructor collisions with other "private" types // in the same named namespace. namespace { + +// Functor which deletes objects by calling `free`. Necessary to free strdup'ed +// strings created by AppendToEnvArgv. +struct FreeDeleter { + void operator()(char* ptr) { free(ptr); } +}; + struct EnvArgv { EnvArgv() : initialized(false), argc(0) {} bool initialized; // whether the other fields have been set. int argc; // elements used in argv[] std::vector argv; // flag arguments parsed from environment string. - std::vector argv_save; // saved values from argv[] to avoid leaks + // saved values from argv[] to avoid leaks + std::vector> argv_save; }; } // anonymous namespace @@ -63,7 +75,7 @@ static void AppendToEnvArgv(const char* s0, size_t s0len, const char* s1, string s = string(s0, s0len) + string(s1, s1len); char* str = strdup(s.c_str()); a->argv.push_back(str); - a->argv_save.push_back(str); + a->argv_save.emplace_back(str); a->argc++; } } @@ -127,14 +139,14 @@ static void ParseArgvFromString(const string& flag_str, EnvArgv* a) { } } -// Call ParseArgvFromString(..., a) on a string derived from the setting of an -// environment variable kEnvVar, or a file it points to. -static void SetArgvFromEnv(EnvArgv* a) { +// Call ParseArgvFromString(..., a) on a string derived from the setting of the +// environment variable `envvar`, or a file it points to. +static void SetArgvFromEnv(absl::string_view envvar, EnvArgv* a) { if (!a->initialized) { static const char kDummyArgv[] = ""; AppendToEnvArgv(kDummyArgv, strlen(kDummyArgv), nullptr, 0, a); // dummy argv[0] - const char* env = getenv(kEnvVar); + const char* env = getenv(string(envvar).c_str()); if (env == nullptr || env[0] == '\0') { // nothing } else if (env[strspn(env, kWS)] == '-') { // flags in env var value @@ -157,48 +169,66 @@ static void SetArgvFromEnv(EnvArgv* a) { } } -// The simulated argv[] parsed from the environment. -static EnvArgv* env_argv; +// The simulated argv[] parsed from the environment, one for each different +// environment variable we've seen. +static std::unordered_map& EnvArgvs() { + static auto* env_argvs = new std::unordered_map(); + return *env_argvs; +} -// Used to protect accesses to env_argv. +// Used to protect accesses to env_argvs. static tensorflow::mutex env_argv_mu(tensorflow::LINKER_INITIALIZED); -// Call Flags::Parse(argc, argv, flag_list) against any as yet unrecognized -// flags passed in from the environment. -bool ParseFlagsFromEnv(const std::vector& flag_list) { - env_argv_mu.lock(); - if (env_argv == nullptr) { - env_argv = new EnvArgv; - } - SetArgvFromEnv(env_argv); // a no-op if already initialized +bool ParseFlagsFromEnvAndDieIfUnknown( + absl::string_view envvar, const std::vector& flag_list) { + tensorflow::mutex_lock lock(env_argv_mu); + auto* env_argv = &EnvArgvs()[string(envvar)]; + SetArgvFromEnv(envvar, env_argv); // a no-op if already initialized bool result = tensorflow::Flags::Parse(&env_argv->argc, &env_argv->argv[0], flag_list); - env_argv_mu.unlock(); + + // There's always at least one unparsed argc, namely the fake argv[0]. + if (result && env_argv->argc != 1) { + // Skip the first argv, which is the fake argv[0]. + auto unknown_flags = absl::MakeSpan(env_argv->argv); + unknown_flags.remove_prefix(1); + + // Some flags are set on XLA_FLAGS, others on TF_XLA_FLAGS. If we find an + // unrecognized flag, suggest the alternative. + string alternate_envvar; + if (envvar == "TF_XLA_FLAGS") { + alternate_envvar = "XLA_FLAGS"; + } else if (envvar == "XLA_FLAGS") { + alternate_envvar = "TF_XLA_FLAGS"; + } + string did_you_mean; + if (!alternate_envvar.empty()) { + did_you_mean = absl::StrFormat( + "\nPerhaps you meant to specify these on the %s envvar?", + alternate_envvar); + } + + LOG(FATAL) << "Unknown flag" << (unknown_flags.size() > 1 ? "s" : "") + << " in " << envvar << ": " << absl::StrJoin(unknown_flags, " ") + << did_you_mean; + return false; + } return result; } // Testing only. -// Reset the env_argv struct so that subsequent calls to ParseFlagsFromEnv() -// will parse the environment variable (or the file it points to) anew, and set -// *pargc, and *pargv to point to the internal locations of the argc and argv -// constructed from the environment. -void ResetFlagsFromEnvForTesting(int** pargc, std::vector** pargv) { - env_argv_mu.lock(); - if (env_argv == nullptr) { - env_argv = new EnvArgv; - } - if (!env_argv->argv_save.empty()) { - for (int i = 0; env_argv->argv_save[i] != nullptr; i++) { - free(env_argv->argv_save[i]); - } - } - env_argv->initialized = false; - env_argv->argc = 0; - env_argv->argv.clear(); - env_argv->argv_save.clear(); - env_argv_mu.unlock(); - *pargc = &env_argv->argc; - *pargv = &env_argv->argv; +// +// Resets the env_argv struct so that subsequent calls to +// ParseFlagsFromEnvAndDieIfUnknown() will parse the environment variable (or +// the file it points to) anew, and set *pargc, and *pargv to point to the +// internal locations of the argc and argv constructed from the environment. +void ResetFlagsFromEnvForTesting(absl::string_view envvar, int** pargc, + std::vector** pargv) { + tensorflow::mutex_lock lock(env_argv_mu); + EnvArgvs().erase(string(envvar)); + auto& env_argv = EnvArgvs()[string(envvar)]; + *pargc = &env_argv.argc; + *pargv = &env_argv.argv; } } // namespace xla diff --git a/tensorflow/compiler/xla/parse_flags_from_env.h b/tensorflow/compiler/xla/parse_flags_from_env.h index fe86ee687f..76940a4299 100644 --- a/tensorflow/compiler/xla/parse_flags_from_env.h +++ b/tensorflow/compiler/xla/parse_flags_from_env.h @@ -16,48 +16,58 @@ limitations under the License. #ifndef TENSORFLOW_COMPILER_XLA_PARSE_FLAGS_FROM_ENV_H_ #define TENSORFLOW_COMPILER_XLA_PARSE_FLAGS_FROM_ENV_H_ -// This module exports ParseFlagsFromEnv(), which allows other modules to parse -// flags from the environtment variable TF_XLA_FLAGS, or (if the first +// This module exports ParseFlagsFromEnvAndDieIfUnknown(), which allows other +// modules to parse flags from an environtment variable, or (if the first // non-whitespace in the variable value is not '-'), a file named by that -// environment variable. The accepted syntax is that flags arguments are of -// the form --flag=value or (for boolean flags) --flag, and are whitespace -// separated. The may be one of: -// - -// in which case the effective value is the string itself -// - in which case the effective value is the -// string with the single-quotes removed -// - in which case the effective value if the -// string with the double-quotes removed, and escaped sequences of -// replaced by . +// environment variable. +// +// The accepted syntax is that flags arguments are of the form --flag=value or +// (for boolean flags) --flag, and are whitespace separated. The may be +// one of: +// +// - +// in which case the effective value is the string itself +// - in which case the effective value is the +// string with the single-quotes removed +// - in which case the effective value if the +// string with the double-quotes removed, and escaped sequences of +// replaced by . // // Flags values inconsistent with the type of the flag will be rejected by the // flag parser. // // Examples: -// TF_XLA_FLAGS="--foo=bar --wombat='value with a space'" // -// TF_XLA_FLAGS=/tmp/flagfile +// - TF_XLA_FLAGS="--foo=bar --wombat='value with a space'" +// - TF_XLA_FLAGS=/tmp/flagfile +// // where /tmp/flagfile might contain -// --some_flag="This is a string containing a \" and a '." -// --another_flag=wombats +// +// --some_flag="This is a string containing a \" and a '." +// --another_flag=wombats #include +#include "absl/strings/string_view.h" #include "tensorflow/compiler/xla/types.h" #include "tensorflow/core/platform/types.h" #include "tensorflow/core/util/command_line_flags.h" namespace xla { -// Call tensorflow::Flags::Parse(argc, argv, flag_list) against any as yet -// unrecognized flags passed in from the environment, and return its -// return value. -bool ParseFlagsFromEnv(const std::vector& flag_list); +// Calls tensorflow::Flags::Parse(argc, argv, flag_list) against any as yet +// unrecognized flags passed in the environment variable `envvar`, and returns +// its return value. +// +// Raises a fatal error if any flags in `envvar` were not recognized. +bool ParseFlagsFromEnvAndDieIfUnknown( + absl::string_view envvar, const std::vector& flag_list); // Used only for testing. Not to be used by clients. -void ResetFlagsFromEnvForTesting(int** pargc, std::vector** pargv); +void ResetFlagsFromEnvForTesting(absl::string_view envvar, int** pargc, + std::vector** pargv); } // namespace xla diff --git a/tensorflow/compiler/xla/parse_flags_from_env_test.cc b/tensorflow/compiler/xla/parse_flags_from_env_test.cc index edd6538402..3465552ebb 100644 --- a/tensorflow/compiler/xla/parse_flags_from_env_test.cc +++ b/tensorflow/compiler/xla/parse_flags_from_env_test.cc @@ -37,20 +37,7 @@ static void TestParseFlagsFromEnv(const char* msg) { // Initialize module under test. int* pargc; std::vector* pargv; - ResetFlagsFromEnvForTesting(&pargc, &pargv); - - // Ensure that environment variable can be parsed when - // no flags are expected. - std::vector empty_flag_list; - bool parsed_ok = ParseFlagsFromEnv(empty_flag_list); - CHECK(parsed_ok) << msg; - const std::vector& argv_first = *pargv; - CHECK_NE(argv_first[0], nullptr) << msg; - int i = 0; - while (argv_first[i] != nullptr) { - i++; - } - CHECK_EQ(i, *pargc) << msg; + ResetFlagsFromEnvForTesting("TF_XLA_FLAGS", &pargc, &pargv); // Check that actual flags can be parsed. bool simple = false; @@ -65,7 +52,7 @@ static void TestParseFlagsFromEnv(const char* msg) { tensorflow::Flag("single_quoted", &single_quoted, ""), tensorflow::Flag("double_quoted", &double_quoted, ""), }; - parsed_ok = ParseFlagsFromEnv(flag_list); + bool parsed_ok = ParseFlagsFromEnvAndDieIfUnknown("TF_XLA_FLAGS", flag_list); CHECK_EQ(*pargc, 1) << msg; const std::vector& argv_second = *pargv; CHECK_NE(argv_second[0], nullptr) << msg; @@ -171,7 +158,8 @@ int main(int argc, char* argv[]) { tensorflow::Flag("int_flag", &int_flag, "An integer flag to test with"), }; xla::string usage = tensorflow::Flags::Usage(argv[0], flag_list); - bool parse_ok = xla::ParseFlagsFromEnv(flag_list); + bool parse_ok = + xla::ParseFlagsFromEnvAndDieIfUnknown("TF_XLA_FLAGS", flag_list); if (!parse_ok) { LOG(QFATAL) << "can't parse from environment\n" << usage; } diff --git a/tensorflow/compiler/xla/protobuf_util.cc b/tensorflow/compiler/xla/protobuf_util.cc index b507a2ef79..ac342bf40f 100644 --- a/tensorflow/compiler/xla/protobuf_util.cc +++ b/tensorflow/compiler/xla/protobuf_util.cc @@ -40,16 +40,6 @@ bool ProtobufEquals(const tensorflow::protobuf::Message& m1, namespace { -string SanitizeFilename(const string& file_name) { - string safe_file_name = file_name; - for (char& c : safe_file_name) { - if (c == '/' || c == '\\') { - c = '_'; - } - } - return safe_file_name; -} - std::pair>*> GetDirectoryExpanders() { static auto* mutex = new tensorflow::mutex; diff --git a/tensorflow/compiler/xla/python/local_computation_builder.cc b/tensorflow/compiler/xla/python/local_computation_builder.cc index 4d2a37cfac..6e2ee86632 100644 --- a/tensorflow/compiler/xla/python/local_computation_builder.cc +++ b/tensorflow/compiler/xla/python/local_computation_builder.cc @@ -148,14 +148,19 @@ static StatusOr ToBuffer(LocalClient* client, /* static */ StatusOr LocalShapedBuffer::FromLiteral( - const Literal& argument, const absl::optional& shape_with_layout) { + const Literal& argument, const absl::optional& shape_with_layout, + int replica_number) { LocalClient* client = GetOrCreateLocalClient(); + TF_ASSIGN_OR_RETURN(int device_ordinal, + client->ReplicaNumberToDeviceOrdinal(replica_number)); + VLOG(1) << "Creating shaped buffer from literal on replica/ordinal: " + << replica_number << "/" << device_ordinal; StatusOr buf = [&] { if (shape_with_layout) { Literal relaid = argument.Relayout(shape_with_layout.value()); - return ToBuffer(client, /*device_ordinal=*/0, relaid); + return ToBuffer(client, device_ordinal, relaid); } - return ToBuffer(client, /*device_ordinal=*/0, argument); + return ToBuffer(client, device_ordinal, argument); }(); TF_RETURN_IF_ERROR(buf.status()); return new LocalShapedBuffer(std::move(buf).ValueOrDie()); @@ -312,67 +317,127 @@ CompiledLocalComputation::CompiledLocalComputation( StatusOr CompiledLocalComputation::Execute( absl::Span argument_handles) { LocalClient* client = GetOrCreateLocalClient(); + StatusOr device_ordinal_status = client->ReplicaNumberToDeviceOrdinal(0); + StatusOr result_buffer_status; + if (!device_ordinal_status.ok()) { + result_buffer_status = device_ordinal_status.status(); + } else { + const int device_ordinal = device_ordinal_status.ValueOrDie(); + VLOG(3) << "Replica 0 mapped to device ordinal for execution: " + << device_ordinal; + + std::vector argument_buffers; + argument_buffers.reserve(argument_handles.size()); + for (auto& handle : argument_handles) { + argument_buffers.push_back(handle->shaped_buffer()); + } + + DeviceAssignment device_assignment = + client->backend() + .computation_placer() + ->AssignDevices(1, /*computation_count=*/1) + .ConsumeValueOrDie(); + + ExecutableRunOptions options; + options.set_device_ordinal(device_ordinal); + options.set_allocator(client->backend().memory_allocator()); + options.set_intra_op_thread_pool( + client->backend().eigen_intra_op_thread_pool_device()); + options.set_device_assignment(&device_assignment); - VLOG(1) << "Execution requested with " << GetReplicaCount() << " replicas."; + result_buffer_status = executable_->Run(argument_buffers, options); + } + + if (!result_buffer_status.ok()) { + return InternalError( + "Failed running replica 0 (other replicas may have failed as well): " + "%s.", + result_buffer_status.status().ToString()); + } + return new LocalShapedBuffer(std::move(result_buffer_status).ValueOrDie()); +} + +StatusOr CompiledLocalComputation::ExecutePerReplica( + absl::Span> argument_handles) { + LocalClient* client = GetOrCreateLocalClient(); + const int num_replicas = GetReplicaCount(); + + if (argument_handles.size() != num_replicas) { + return InvalidArgument( + "Attempted to execute with %d replicas when replica count is %d", + argument_handles.size(), num_replicas); + } + + VLOG(1) << "Executing with " << num_replicas << " replicas."; // Each replica populates a StatusOr result, but only the output value of // replica zero is returned. - std::vector> results(GetReplicaCount()); - { + std::vector> results(num_replicas); + auto execute = [this, client, num_replicas, &argument_handles, + &results](int replica) { + StatusOr device_ordinal_status = + client->ReplicaNumberToDeviceOrdinal(replica); + if (!device_ordinal_status.ok()) { + results[replica] = device_ordinal_status.status(); + return; + } + const int device_ordinal = device_ordinal_status.ValueOrDie(); + VLOG(3) << "Replica " << replica + << " mapped to device ordinal for execution: " << device_ordinal; + + std::vector argument_buffers; + argument_buffers.reserve(argument_handles[replica].size()); + for (auto& handle : argument_handles[replica]) { + argument_buffers.push_back(handle->shaped_buffer()); + } + + DeviceAssignment device_assignment = + client->backend() + .computation_placer() + ->AssignDevices(num_replicas, /*computation_count=*/1) + .ConsumeValueOrDie(); + + ExecutableRunOptions options; + options.set_device_ordinal(device_ordinal); + options.set_allocator(client->backend().memory_allocator()); + options.set_intra_op_thread_pool( + client->backend().eigen_intra_op_thread_pool_device()); + options.set_device_assignment(&device_assignment); + StatusOr result_buffer_status = + executable_->Run(argument_buffers, options); + + results[replica] = std::move(result_buffer_status); + }; + + if (num_replicas == 1) { + // Fast-path if there is only one replica — run the computation on the + // current thread. + execute(0); + } else { + // TODO(phawkins): don't recreate the threadpool for each execution. tensorflow::thread::ThreadPool pool(tensorflow::Env::Default(), "xlarun", - GetReplicaCount()); - - for (int replica = 0; replica < GetReplicaCount(); ++replica) { - pool.Schedule( - [this, client, replica, &argument_handles, &results] { - StatusOr device_ordinal_status = - client->ReplicaNumberToDeviceOrdinal(replica); - if (!device_ordinal_status.ok()) { - results[replica] = device_ordinal_status.status(); - return; - } - const int device_ordinal = device_ordinal_status.ValueOrDie(); - VLOG(3) << "Replica " << replica - << " mapped to device ordinal for execution: " - << device_ordinal; - - std::vector argument_buffers; - argument_buffers.reserve(argument_handles.size()); - for (auto& handle : argument_handles) { - argument_buffers.push_back(handle->shaped_buffer()); - } - - DeviceAssignment device_assignment = - client->backend() - .computation_placer() - ->AssignDevices(GetReplicaCount(), /*computation_count=*/1) - .ConsumeValueOrDie(); - - ExecutableRunOptions options; - options.set_device_ordinal(device_ordinal); - options.set_allocator(client->backend().memory_allocator()); - options.set_intra_op_thread_pool( - client->backend().eigen_intra_op_thread_pool_device()); - options.set_device_assignment(&device_assignment); - StatusOr result_buffer_status = - executable_->Run(argument_buffers, options); - - results[replica] = std::move(result_buffer_status); - }); + num_replicas - 1); + + for (int replica = 0; replica < num_replicas - 1; ++replica) { + pool.Schedule([&execute, replica] { execute(replica); }); } + execute(num_replicas - 1); } - for (int replica = 0; replica < GetReplicaCount(); ++replica) { - const auto& statusor = results[replica]; + std::vector wrapped_results(num_replicas); + for (int replica = 0; replica < num_replicas; ++replica) { + auto& statusor = results[replica]; if (!statusor.ok()) { return InternalError( "Failed running replica %d (other replicas may have failed as well): " "%s.", replica, statusor.status().ToString()); } + wrapped_results[replica] = + new LocalShapedBuffer(std::move(statusor).ValueOrDie()); } - return new LocalShapedBuffer(std::move(results[0]).ValueOrDie()); + return new LocalShapedBufferTuple(std::move(wrapped_results)); } static StatusOr GetReturnValueShape(const XlaComputation& computation) { @@ -487,12 +552,13 @@ StatusOr LocalComputation::CompileForXrt( xrt::XLAComputation c; auto config = c.mutable_config(); - auto shapes = config->mutable_program_shape(); + ProgramShape shapes; for (auto& shape : argument_shapes) { - *shapes->add_parameters() = shape; + *shapes.add_parameters() = shape; } - TF_ASSIGN_OR_RETURN(*shapes->mutable_result(), GetReturnValueShape()); - LayoutUtil::SetToDefaultLayout(shapes); + TF_ASSIGN_OR_RETURN(*shapes.mutable_result(), GetReturnValueShape()); + LayoutUtil::SetToDefaultLayout(&shapes); + *config->mutable_program_shape() = shapes.ToProto(); auto snapshot = computation().Snapshot().ValueOrDie(); *c.mutable_hlo_snapshot() = *snapshot; @@ -584,9 +650,9 @@ LocalOp LocalComputationBuilder::Broadcast( } LocalOp LocalComputationBuilder::BroadcastInDim( - const LocalOp& operand, const Shape& shape, + const LocalOp& operand, absl::Span out_dim_sizes, absl::Span broadcast_dimensions) { - return xla::BroadcastInDim(operand.op(), shape, broadcast_dimensions); + return xla::BroadcastInDim(operand.op(), out_dim_sizes, broadcast_dimensions); } LocalOp LocalComputationBuilder::Pad(const LocalOp& operand, diff --git a/tensorflow/compiler/xla/python/local_computation_builder.h b/tensorflow/compiler/xla/python/local_computation_builder.h index 9e617c48bd..149e44570d 100644 --- a/tensorflow/compiler/xla/python/local_computation_builder.h +++ b/tensorflow/compiler/xla/python/local_computation_builder.h @@ -71,7 +71,8 @@ StatusOr TransferFromOutfeedLocalReplica(const Shape& shape, class LocalShapedBuffer { public: static StatusOr FromLiteral( - const Literal& argument, const absl::optional& shape_with_layout); + const Literal& argument, const absl::optional& shape_with_layout, + int replica_number); LocalShapedBuffer(ScopedShapedBuffer shaped_buffer); StatusOr ToLiteral() const; @@ -175,6 +176,12 @@ class CompiledLocalComputation { StatusOr Execute( absl::Span argument_handles); + // Execute on many replicas. Takes a sequence of argument lists (one argument + // list per replica) and returns a tuple of results (one result per replica). + // The number of argument lists must be equal to the replica count. + StatusOr ExecutePerReplica( + absl::Span > argument_handles); + private: std::unique_ptr executable_; }; @@ -282,7 +289,8 @@ class LocalComputationBuilder { LocalOp Broadcast(const LocalOp& operand, absl::Span broadcast_sizes); - LocalOp BroadcastInDim(const LocalOp& operand, const Shape& shape, + LocalOp BroadcastInDim(const LocalOp& operand, + absl::Span out_dim_sizes, absl::Span broadcast_dimensions); LocalOp Pad(const LocalOp& operand, const LocalOp& padding_value, diff --git a/tensorflow/compiler/xla/python/local_computation_builder.i b/tensorflow/compiler/xla/python/local_computation_builder.i index feabfdb889..d23d693c1e 100644 --- a/tensorflow/compiler/xla/python/local_computation_builder.i +++ b/tensorflow/compiler/xla/python/local_computation_builder.i @@ -363,6 +363,37 @@ tensorflow::ImportNumpy(); $1 = temps; } +%typemap(in) absl::Span > + (std::vector > temps) { + if (!PySequence_Check($input)) { + PyErr_SetString(PyExc_TypeError, "Argument is not a sequence"); + SWIG_fail; + } + const int size = PySequence_Size($input); + temps.reserve(size); + for (int i = 0; i < size; ++i) { + PyObject* o = PySequence_GetItem($input, i); + std::vector vec; + const int vec_size = PySequence_Size(o); + vec.reserve(vec_size); + for (int j = 0; j < vec_size; ++j) { + PyObject* vec_elt = PySequence_GetItem(o, j); + LocalShapedBuffer* lsbp; + if ((SWIG_ConvertPtr(vec_elt, (void**) &lsbp, $descriptor(xla::swig::LocalShapedBuffer*), + SWIG_POINTER_EXCEPTION)) == -1) { + Py_DECREF(vec_elt); + Py_DECREF(o); + SWIG_fail; + } + vec.push_back(lsbp); + Py_DECREF(vec_elt); + } + temps.push_back(vec); + Py_DECREF(o); + } + $1 = temps; +} + %typemap(in) absl::Span (std::vector temps) { if (!PySequence_Check($input)) { @@ -921,22 +952,22 @@ tensorflow::ImportNumpy(); $1 = NULL; } else { if (!HandleStringAttribute($input, "generate_hlo_graph", [&](string s) { - build_options.set_generate_hlo_graph(std::move(s)); + build_options.mutable_debug_options()->set_xla_generate_hlo_graph(std::move(s)); })) { return nullptr; } if (!HandleStringAttribute($input, "dump_optimized_hlo_proto_to", [&](string s) { - build_options.set_dump_optimized_hlo_proto_to(std::move(s)); + build_options.mutable_debug_options()->set_xla_dump_optimized_hlo_proto_to(std::move(s)); })) { return nullptr; } if (!HandleStringAttribute($input, "dump_unoptimized_hlo_proto_to", [&](string s) { - build_options.set_dump_unoptimized_hlo_proto_to(std::move(s)); + build_options.mutable_debug_options()->set_xla_dump_unoptimized_hlo_proto_to(std::move(s)); })) { return nullptr; } if (!HandleStringAttribute($input, "dump_per_pass_hlo_proto_to", [&](string s) { - build_options.set_dump_per_pass_hlo_proto_to(std::move(s)); + build_options.mutable_debug_options()->set_xla_dump_per_pass_hlo_proto_to(std::move(s)); })) { return nullptr; } @@ -950,7 +981,7 @@ tensorflow::ImportNumpy(); PyErr_SetString(PyExc_TypeError, "ExecutableBuildOptions.hlo_profile must be a bool or None."); SWIG_fail; } - build_options.set_hlo_profile(o == Py_True); + build_options.mutable_debug_options()->set_xla_hlo_profile(o == Py_True); } Py_DECREF(o); @@ -992,11 +1023,13 @@ tensorflow::ImportNumpy(); %unignore xla::swig::XrtAllocation; %unignore xla::swig::XrtAllocation::FromLiteral; %unignore xla::swig::XrtAllocation::ToLiteral; +%unignore xla::swig::XrtAllocation::shape; %unignore xla::swig::XrtAllocationTuple; %unignore xla::swig::XrtAllocationTuple::Release; %unignore xla::swig::XrtAllocationTuple::size; %unignore xla::swig::CompiledLocalComputation; %unignore xla::swig::CompiledLocalComputation::Execute; +%unignore xla::swig::CompiledLocalComputation::ExecutePerReplica; %unignore xla::swig::CompiledXrtComputation; %unignore xla::swig::CompiledXrtComputation::Execute; %unignore xla::swig::LocalComputation; diff --git a/tensorflow/compiler/xla/python/xla_client.py b/tensorflow/compiler/xla/python/xla_client.py index 92b0685dbb..c91a2aaf56 100644 --- a/tensorflow/compiler/xla/python/xla_client.py +++ b/tensorflow/compiler/xla/python/xla_client.py @@ -26,6 +26,9 @@ import os import numpy as np +import six +from six.moves import xrange + from tensorflow.compiler.xla import xla_data_pb2 from tensorflow.compiler.xla.python import pywrap_xla as c_api from tensorflow.compiler.xla.service import hlo_pb2 @@ -75,6 +78,13 @@ def CurrentSourceInfoMetadata(op_type=None, op_name=None, skip_frames=1): source_line=lineno) +def _maybe_encode_string(s): + if six.PY3: + return s.encode('utf-8') + else: + return s + + class PaddingType(enum.Enum): VALID = 1 SAME = 2 @@ -212,23 +222,33 @@ class LocalBuffer(object): means the referent is in device memory. """ - def __init__(self, c_buffer, backend): + def __init__(self, c_buffer, backend, replica): self.c_buffer = c_buffer self._backend = backend + self._replica = replica if backend.backend_type == BackendType.XRT: self._delete = c_api.DeleteXrtAllocation else: self._delete = c_api.DeleteLocalShapedBuffer @staticmethod - def from_pyval(pyval, backend=XLA_LOCAL_BACKEND): + def from_pyval(pyval, replica=0, backend=XLA_LOCAL_BACKEND): """Allocate and copy to XLA the given python value.""" pyval = require_numpy_array_layout(pyval) + num_replicas = get_replica_count() + if not 0 <= replica < num_replicas: + raise ValueError( + 'Attempt to place buffer on replica {} when the replica count is {}' + .format(replica, num_replicas)) if backend.backend_type == BackendType.XRT: - cbuf = c_api.XrtAllocation.FromLiteral(pyval, backend.target) + if replica != 0: + raise NotImplementedError( + 'Multi-replica execution is not yet supported via the XRT backend.') + cbuf = c_api.XrtAllocation.FromLiteral( + pyval, _maybe_encode_string(backend.target)) else: - cbuf = c_api.LocalShapedBuffer.FromLiteral(pyval, None) - return LocalBuffer(cbuf, backend) + cbuf = c_api.LocalShapedBuffer.FromLiteral(pyval, None, replica) + return LocalBuffer(cbuf, backend, replica) def to_py(self): return self.c_buffer.ToLiteral() @@ -236,6 +256,9 @@ class LocalBuffer(object): def shape(self): return _wrap_shape(self.c_buffer.shape()) + def replica(self): + return self._replica + def delete(self): if self.c_buffer is not None: self._delete(self.c_buffer) @@ -245,14 +268,15 @@ class LocalBuffer(object): """Assuming a tuple buffer, unpack it into constituent tuple elements.""" assert self.c_buffer is not None if self._backend.backend_type == BackendType.XRT: - result = c_api.DestructureXrtAllocationTuple(self.c_buffer, - self._backend.target) + result = c_api.DestructureXrtAllocationTuple( + self.c_buffer, _maybe_encode_string(self._backend.target)) else: result = c_api.DestructureLocalShapedBufferTuple(self.c_buffer) self.delete() size = result.size() destructured = tuple( - LocalBuffer(result.Release(i), backend=self._backend) + LocalBuffer( + result.Release(i), replica=self._replica, backend=self._backend) for i in xrange(size)) return destructured @@ -322,6 +346,9 @@ class Shape(object): def __ne__(self, other): return not self == other + def __hash__(self): + return hash((self._dtype, self._dimensions, self._minor_to_major)) + def __repr__(self): return ('xla_client.Shape(_dtype={!r}, _dimensions={!r}, ' '_is_tuple={!r}, _minor_to_major={!r})').format( @@ -541,10 +568,13 @@ class LocalComputation(object): ] result_shape = result_shape.map_leaves(layout_fn) + argument_shapes = list(argument_shapes) + compile_options = compile_options or CompileOptions() compile_options.result_shape = result_shape if self._backend.backend_type == BackendType.XRT: - c = self.computation.CompileForXrt(argument_shapes, self._backend.target) + c = self.computation.CompileForXrt( + argument_shapes, _maybe_encode_string(self._backend.target)) else: c = self.computation.Compile(argument_shapes, compile_options) return LocalComputation(c, is_compiled=True, backend=self._backend) @@ -558,23 +588,87 @@ class LocalComputation(object): compile_options=compile_options, layout_fn=layout_fn) - def Execute(self, arguments=()): - """Execute with LocalBuffer arguments and return value.""" + def GetReturnValueShape(self): + return _wrap_shape(self._c_computation.GetReturnValueShape()) + + def Execute(self, arguments=(), check_for_deleted_args=True): + """Execute on one replica with LocalBuffer arguments and return value.""" + if check_for_deleted_args and any(arg.is_deleted() for arg in arguments): + raise ValueError('Executing with deleted local buffer argument') + raw_args = [arg.c_buffer for arg in arguments] + output_buffer = self._c_computation.Execute(raw_args) + return LocalBuffer(output_buffer, backend=self._backend, replica=0) + + def ExecutePerReplica(self, arguments=None): + """Execute on many replicas with LocalBuffer arguments and return value. + + Args: + arguments: A sequence of sequences of LocalBuffers. The i'th inner + sequence comprises the arguments for execution on the i'th replica. + + Returns: + A list of the computation's outputs on each replica, as a LocalBuffer. If + a shallow sequence of arguments was passed in for `arguments`, then the + sole, zero'th replica's output is returned instead, as a LocalBuffer. + """ if not self._is_compiled: raise ValueError('Cannot execute an uncompiled local XLA computation.') - arguments = tuple(arguments) - if any(arg.is_deleted() for arg in arguments): - raise ValueError('Executing with deleted local buffer argument') - return LocalBuffer( - self._c_computation.Execute([arg.c_buffer for arg in arguments]), - backend=self._backend) + if arguments is None: + arguments = ((),) * get_replica_count() + else: + arguments = [list(replica_args) for replica_args in arguments] + + # Check arguments + for replica, replica_args in enumerate(arguments): + for arg in replica_args: + if arg.is_deleted(): + raise ValueError('Executing with deleted local buffer argument') + if arg.replica() != replica: + raise ValueError( + 'Executing on replica {} with argument from replica {}'.format( + replica, arg.replica())) + + # Pull out argument buffer handles + stripped_args = [ + [arg.c_buffer for arg in replica_args] for replica_args in arguments + ] + + # Execute + if self._backend.backend_type == BackendType.XRT: + if len(stripped_args) > 1: + raise NotImplementedError( + 'Multi-replica execution is not yet supported via the XRT backend.') + output_buffers = [self._c_computation.Execute(stripped_args[0])] + else: + output_buffer_tup = self._c_computation.ExecutePerReplica(stripped_args) + size = output_buffer_tup.size() + output_buffers = [output_buffer_tup.Release(i) for i in xrange(size)] + + # Wrap output handles in LocalBuffer instances + return tuple( + LocalBuffer(output_buffer, backend=self._backend, replica=replica) + for replica, output_buffer in enumerate(output_buffers)) def ExecuteWithPythonValues(self, arguments=()): - """Execute with Python values as arguments and return value.""" - arguments = tuple( - LocalBuffer.from_pyval(arg, backend=self._backend) for arg in arguments) + """Execute on one replica with Python values as arguments and output.""" + + def put(arg): + return LocalBuffer.from_pyval(arg, backend=self._backend) + + arguments = [put(arg) for arg in arguments] return self.Execute(arguments).to_py() + def ExecuteWithPythonValuesPerReplica(self, arguments): + """Execute on many replicas with Python values as arguments and output.""" + + def put(arg, replica): + return LocalBuffer.from_pyval(arg, replica, backend=self._backend) + + arguments = [[put(arg, replica) + for arg in replica_args] + for replica, replica_args in enumerate(arguments)] + return [out.to_py() for out in self.ExecutePerReplica(arguments)] + def __del__(self): self._delete(self._c_computation) @@ -761,8 +855,7 @@ class ComputationBuilder(object): Returns: A LocalOp representing the added broadcast-in-dimensions op. """ - xla_shape = Shape.array_shape(self.GetShape(operand).element_type(), shape) - return self._client.BroadcastInDim(operand, xla_shape, broadcast_dimensions) + return self._client.BroadcastInDim(operand, shape, broadcast_dimensions) def Concatenate(self, operands, dimension): """Enqueues a concatenate operation onto the computation. @@ -1380,6 +1473,7 @@ def initialize_platform_name(platform_name): Raises: A runtime exception if the XLA service has already been initialized. """ + platform_name = _maybe_encode_string(platform_name) c_api.InitializePlatformName(platform_name) diff --git a/tensorflow/compiler/xla/python_api/xla_shape.py b/tensorflow/compiler/xla/python_api/xla_shape.py index f158f6b241..95b2bf300e 100644 --- a/tensorflow/compiler/xla/python_api/xla_shape.py +++ b/tensorflow/compiler/xla/python_api/xla_shape.py @@ -25,9 +25,10 @@ from tensorflow.compiler.xla.python_api import types class Shape(object): - """Wraps a xla_data_pb2.Shape message with a convenient Python type. + """Wraps a xla_data_pb2.ShapeProto message with a convenient Python type. - Provides direct access to the underlying xla_data_pb2.Shape message in the + Provides direct access to the underlying xla_data_pb2.ShapeProto message in + the message attribute, along with accessor wrappers to the message's fields. Avoid direct access to .message unless interacting directly with protobuf APIs like CopyFrom. In other words, prefer hauling the shape around in a Shape, and @@ -48,7 +49,7 @@ class Shape(object): Raises: ValueError: if element_type is TUPLE but dimensions are not Shape objects. """ - self.message = xla_data_pb2.Shape() + self.message = xla_data_pb2.ShapeProto() self.message.element_type = element_type if element_type == xla_data_pb2.TUPLE: if not all(isinstance(subshape, Shape) for subshape in dimensions): diff --git a/tensorflow/compiler/xla/rpc/BUILD b/tensorflow/compiler/xla/rpc/BUILD index 3abb3855a4..26affbcceb 100644 --- a/tensorflow/compiler/xla/rpc/BUILD +++ b/tensorflow/compiler/xla/rpc/BUILD @@ -16,7 +16,6 @@ xla_proto_library( use_grpc_plugin = True, visibility = ["//visibility:public"], deps = [ - "//tensorflow/compiler/xla:xla_data_proto", "//tensorflow/compiler/xla:xla_proto", ], ) diff --git a/tensorflow/compiler/xla/rpc/xla_service.proto b/tensorflow/compiler/xla/rpc/xla_service.proto index e4f332cda2..0ff8adc2ac 100644 --- a/tensorflow/compiler/xla/rpc/xla_service.proto +++ b/tensorflow/compiler/xla/rpc/xla_service.proto @@ -43,7 +43,6 @@ limitations under the License. syntax = "proto3"; import "tensorflow/compiler/xla/xla.proto"; -import "tensorflow/compiler/xla/xla_data.proto"; package xla; diff --git a/tensorflow/compiler/xla/service/BUILD b/tensorflow/compiler/xla/service/BUILD index 19b5c1ca25..81e71eee52 100644 --- a/tensorflow/compiler/xla/service/BUILD +++ b/tensorflow/compiler/xla/service/BUILD @@ -281,10 +281,12 @@ tf_cc_test( "//tensorflow/compiler/xla/service:hlo_element_type_converter", "//tensorflow/compiler/xla/tests:hlo_test_base", "//tensorflow/compiler/xla/tests:literal_test_util", + "//tensorflow/compiler/xla/tests:test_utils", "//tensorflow/compiler/xla/tests:xla_internal_test_main", # fixdeps: keep "//tensorflow/core:lib", "//tensorflow/core:test", "@com_google_absl//absl/memory", + "@com_google_absl//absl/strings:str_format", ], ) @@ -292,6 +294,7 @@ cc_library( name = "hlo", srcs = [ "dfs_hlo_visitor.cc", + "dynamic_parameter_binding.cc", "hlo_computation.cc", "hlo_input_output_alias_config.cc", "hlo_instruction.cc", @@ -305,6 +308,7 @@ cc_library( hdrs = [ "dfs_hlo_visitor.h", "dfs_hlo_visitor_with_default.h", + "dynamic_parameter_binding.h", "hlo_clone_context.h", "hlo_computation.h", "hlo_domain_metadata.h", @@ -350,6 +354,25 @@ cc_library( ], ) +tf_cc_test( + name = "dynamic_parameter_binding_test", + srcs = ["dynamic_parameter_binding_test.cc"], + deps = [ + ":hlo", + ":hlo_dce", + ":hlo_memory_scheduler", + ":hlo_ordering", + ":hlo_parser", + "//tensorflow/compiler/xla:shape_util", + "//tensorflow/compiler/xla:types", + "//tensorflow/compiler/xla:xla_data_proto", + "//tensorflow/compiler/xla/tests:hlo_test_base", + "//tensorflow/compiler/xla/tests:xla_internal_test_main", + "//tensorflow/core:test", + "@com_google_absl//absl/algorithm:container", + ], +) + tf_cc_test( name = "dfs_hlo_visitor_with_default_test", srcs = ["dfs_hlo_visitor_with_default_test.cc"], @@ -387,9 +410,36 @@ tf_cc_test( ":hlo", ":pattern_matcher", "//tensorflow/compiler/xla:shape_util", + "//tensorflow/compiler/xla:test", "//tensorflow/compiler/xla/service:hlo_parser", "//tensorflow/compiler/xla/tests:xla_internal_test_main", "//tensorflow/core:test", + "@com_google_absl//absl/strings", + ], +) + +cc_library( + name = "pattern_matcher_gmock", + testonly = 1, + hdrs = ["pattern_matcher_gmock.h"], + deps = [ + ":pattern_matcher", + "//tensorflow/compiler/xla:test", + "//tensorflow/core:test", + ], +) + +tf_cc_test( + name = "pattern_matcher_gmock_test", + srcs = ["pattern_matcher_gmock_test.cc"], + deps = [ + ":hlo", + ":pattern_matcher", + ":pattern_matcher_gmock", + "//tensorflow/compiler/xla:shape_util", + "//tensorflow/compiler/xla:test", + "//tensorflow/compiler/xla/tests:xla_internal_test_main", + "//tensorflow/core:test", ], ) @@ -403,6 +453,7 @@ cc_library( "//tensorflow/compiler/xla:util", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", + "@com_google_absl//absl/base", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/types:span", ], @@ -1336,6 +1387,7 @@ cc_library( ":hlo", "//tensorflow/compiler/xla:literal", "//tensorflow/compiler/xla:shape_util", + "@com_google_absl//absl/container:flat_hash_set", ], ) @@ -1539,7 +1591,10 @@ tf_cc_test( ":hlo", ":hlo_casting_utils", ":hlo_matchers", + ":hlo_parser", ":hlo_pass", + ":pattern_matcher", + ":pattern_matcher_gmock", "//tensorflow/compiler/xla:literal", "//tensorflow/compiler/xla:shape_util", "//tensorflow/compiler/xla:test", @@ -1707,7 +1762,9 @@ cc_library( ":hlo", ":hlo_pass", ":hlo_query", + ":pattern_matcher", ":while_loop_analysis", + "//tensorflow/compiler/xla:shape_util", "//tensorflow/compiler/xla:statusor", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/container:flat_hash_set", @@ -1720,9 +1777,14 @@ tf_cc_test( name = "while_loop_simplifier_test", srcs = ["while_loop_simplifier_test.cc"], deps = [ + ":algebraic_simplifier", ":hlo", + ":hlo_cse", ":hlo_dce", ":hlo_matchers", + ":hlo_pass", + ":hlo_pass_pipeline", + ":tuple_simplifier", ":while_loop_simplifier", "//tensorflow/compiler/xla:test", "//tensorflow/compiler/xla/tests:hlo_test_base", @@ -2347,6 +2409,7 @@ cc_library( "//tensorflow/compiler/xla:util", "//tensorflow/compiler/xla:xla_data_proto", "//tensorflow/core:lib", + "@com_google_absl//absl/container:flat_hash_map", ], ) @@ -2600,6 +2663,8 @@ tf_cc_test( ":hlo", ":hlo_matchers", ":layout_assignment", + ":pattern_matcher", + ":pattern_matcher_gmock", "//tensorflow/compiler/xla:literal", "//tensorflow/compiler/xla:shape_layout", "//tensorflow/compiler/xla:shape_util", @@ -2744,6 +2809,8 @@ tf_cc_test( ":hlo_matchers", ":hlo_parser", ":hlo_pass", + ":pattern_matcher", + ":pattern_matcher_gmock", "//tensorflow/compiler/xla:literal", "//tensorflow/compiler/xla:shape_util", "//tensorflow/compiler/xla:test", @@ -2855,6 +2922,46 @@ tf_cc_test( ], ) +cc_library( + name = "hlo_get_dimension_size_rewriter", + srcs = ["hlo_get_dimension_size_rewriter.cc"], + hdrs = ["hlo_get_dimension_size_rewriter.h"], + deps = [ + ":hlo", + ":hlo_pass", + ":shape_inference", + "//tensorflow/compiler/xla:literal", + "//tensorflow/compiler/xla:literal_util", + "//tensorflow/compiler/xla:shape_util", + "//tensorflow/compiler/xla:types", + "//tensorflow/compiler/xla:xla_data_proto", + "//tensorflow/core:lib", + "@com_google_absl//absl/algorithm:container", + ], +) + +tf_cc_test( + name = "hlo_get_dimension_size_rewriter_test", + srcs = ["hlo_get_dimension_size_rewriter_test.cc"], + deps = [ + ":hlo", + ":hlo_get_dimension_size_rewriter", + ":hlo_matchers", + ":hlo_parser", + "//tensorflow/compiler/xla:literal", + "//tensorflow/compiler/xla:shape_util", + "//tensorflow/compiler/xla:types", + "//tensorflow/compiler/xla:util", + "//tensorflow/compiler/xla:xla_data_proto", + "//tensorflow/compiler/xla/tests:hlo_test_base", + "//tensorflow/compiler/xla/tests:literal_test_util", + "//tensorflow/compiler/xla/tests:test_utils", + "//tensorflow/compiler/xla/tests:xla_internal_test_main", + "//tensorflow/core:lib", + "//tensorflow/core:test", + ], +) + cc_library( name = "device_memory_allocator", srcs = [ @@ -2913,6 +3020,7 @@ cc_library( "//tensorflow/core:lib", "//tensorflow/core:lib_internal", "@com_google_absl//absl/algorithm:container", + "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/strings", "@llvm//:core", "@llvm//:transform_utils", @@ -3026,6 +3134,7 @@ cc_library( ":hlo_casting_utils", ":hlo_execution_profile", ":hlo_tfgraph_builder", + ":pattern_matcher", "//tensorflow/compiler/xla:literal", "//tensorflow/compiler/xla:shape_util", "//tensorflow/compiler/xla:types", @@ -3318,9 +3427,9 @@ cc_library( ":tuple_util", ":while_loop_analysis", ":while_util", + "//tensorflow/compiler/xla:shape_util", "//tensorflow/compiler/xla:statusor", "//tensorflow/compiler/xla:util", - "//tensorflow/core:lib", "@com_google_absl//absl/algorithm:container", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/container:flat_hash_set", @@ -3463,6 +3572,8 @@ tf_cc_test( ":hlo_casting_utils", ":hlo_matchers", ":hlo_parser", + ":pattern_matcher", + ":pattern_matcher_gmock", "//tensorflow/compiler/xla:test_helpers", "//tensorflow/compiler/xla:window_util", "//tensorflow/core:lib", @@ -3513,6 +3624,41 @@ cc_library( ], ) +cc_library( + name = "ar_crs_combiner", + srcs = ["ar_crs_combiner.cc"], + hdrs = ["ar_crs_combiner.h"], + deps = [ + ":call_graph", + ":pattern_matcher", + "//tensorflow/compiler/xla:literal", + "//tensorflow/compiler/xla:literal_util", + "//tensorflow/compiler/xla:status_macros", + "//tensorflow/compiler/xla:statusor", + "//tensorflow/compiler/xla:types", + "//tensorflow/compiler/xla:xla_data_proto", + "//tensorflow/compiler/xla/service:hlo", + "//tensorflow/compiler/xla/service:hlo_pass", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/strings", + ], +) + +tf_cc_test( + name = "ar_crs_combiner_test", + srcs = ["ar_crs_combiner_test.cc"], + deps = [ + ":ar_crs_combiner", + ":hlo", + ":hlo_matchers", + "//tensorflow/compiler/xla:statusor", + "//tensorflow/compiler/xla/tests:hlo_test_base", + "//tensorflow/compiler/xla/tests:xla_internal_test_main", + "//tensorflow/core:lib", + "//tensorflow/core:test", + ], +) + tf_cc_test( name = "map_inliner_test", srcs = ["map_inliner_test.cc"], diff --git a/tensorflow/compiler/xla/service/algebraic_simplifier.cc b/tensorflow/compiler/xla/service/algebraic_simplifier.cc index 89e62bd2f0..985c5af1c4 100644 --- a/tensorflow/compiler/xla/service/algebraic_simplifier.cc +++ b/tensorflow/compiler/xla/service/algebraic_simplifier.cc @@ -16,6 +16,7 @@ limitations under the License. #include "tensorflow/compiler/xla/service/algebraic_simplifier.h" #include +#include #include #include #include @@ -68,6 +69,45 @@ bool IsAll(const HloInstruction* op, int8 value) { } } +// Checks whether `op` is a floating-point constant or broadcast of a constant +// of the form +/- 2^k for some integer k positive, negative, or zero. Such +// values are interesting because multiplying by a power of 2 just moves the +// exponent. +bool IsAllFpConstantPowerOf2(const HloInstruction* op) { + // Unwrap the broadcast if necessary. + const HloInstruction* c; + if (!Match(op, m::ConstantEffectiveScalar(&c)) && + !Match(op, m::Broadcast(m::Constant(&c).WithShape( + m::Shape().IsEffectiveScalar())))) { + return false; + } + auto val = [&]() -> absl::optional { + switch (c->shape().element_type()) { + case BF16: + return static_cast(c->literal().GetFirstElement()); + case F16: + return static_cast(c->literal().GetFirstElement()); + case F32: + return c->literal().GetFirstElement(); + case F64: + return c->literal().GetFirstElement(); + default: + // Cowardly refuse to consider complex types. + return absl::nullopt; + } + }(); + if (!val) { + return false; + } + + int exp; + double mantissa = std::frexp(*val, &exp); + // frexp returns a value in the range (-1; -0.5] U [0.5, 1). A return value + // of +/-0.5 therefore indicates that the floating point value is a power of + // 2. + return mantissa == 0.5 || mantissa == -0.5; +} + // Returns whether the given transpose produces a result which is bit-wise // identical to its operand and thus may be replaced with a bitcast. bool TransposeIsBitcast(const HloInstruction* transpose) { @@ -84,7 +124,8 @@ bool TransposeIsBitcast(const HloInstruction* transpose) { // reshape may still be a bitcast. For example, a reshape from [28x28] to [784]. bool ReshapeOrCopyIsBitcast( const HloInstruction* instr, - const AlgebraicSimplifier::ValidBitcastCallback& valid_bitcast_callback) { + const AlgebraicSimplifierOptions::ValidBitcastCallback& + valid_bitcast_callback) { CHECK(HloOpcode::kReshape == instr->opcode() || HloOpcode::kCopy == instr->opcode()); @@ -95,6 +136,11 @@ bool ReshapeOrCopyIsBitcast( valid_bitcast_callback(operand->shape(), instr->shape()); } +bool IsUnstridedSlice(const HloInstruction* hlo) { + return absl::c_all_of(hlo->slice_strides(), + [](int64 stride) { return stride == 1; }); +} + // AlgebraicSimplifierVisitor traverses the HLO computation and reduces certain // algebraic expressions to simplified forms. Note: This only supports // simplifications that simply look at the operands of an instruction. For the @@ -180,21 +226,13 @@ class AlgebraicSimplifierVisitor : public DfsHloVisitorWithDefault { const bool changed() const { return changed_; } // Runs the visitor on a computation. - static bool Run( - HloComputation* computation, bool is_layout_sensitive, - AlgebraicSimplifier::ValidBitcastCallback valid_bitcast_callback, - bool enable_dot_strength_reduction, bool enable_conv_simplification); + static bool Run(HloComputation* computation, + const AlgebraicSimplifierOptions& options); private: - explicit AlgebraicSimplifierVisitor( - HloComputation* computation, bool is_layout_sensitive, - AlgebraicSimplifier::ValidBitcastCallback valid_bitcast_callback, - bool enable_dot_strength_reduction, bool enable_conv_simplification) - : computation_(computation), - is_layout_sensitive_(is_layout_sensitive), - valid_bitcast_callback_(std::move(valid_bitcast_callback)), - enable_dot_strength_reduction_(enable_dot_strength_reduction), - enable_conv_simplification_(enable_conv_simplification) {} + explicit AlgebraicSimplifierVisitor(HloComputation* computation, + const AlgebraicSimplifierOptions& options) + : computation_(computation), options_(options) {} // Transforms Dots where at least one input is a vector or has a degenerate // dimension and converts it into a multiply and reduce. This should enable @@ -233,10 +271,10 @@ class AlgebraicSimplifierVisitor : public DfsHloVisitorWithDefault { HloInstruction* new_instruction); // Returns whether the shape of the output of the given instructions are the - // same for the purposes of simplification. If is_layout_sensitive_ is true, - // then this tests shape equality including layout (ShapeUtil::Equal). If - // is_layout_sensitive_ is false, then the tests shape compatibility - // (ShapeUtil::Compatible). + // same for the purposes of simplification. If options_.is_layout_sensitive() + // is true, then this tests shape equality including layout + // (ShapeUtil::Equal). If options_.is_layout_sensitive() is false, then the + // tests shape compatibility (ShapeUtil::Compatible). bool SameShape(const HloInstruction* lhs, const HloInstruction* rhs) const; // Returns whether it was possible to transform `root` to a clamp instruction. @@ -325,22 +363,12 @@ class AlgebraicSimplifierVisitor : public DfsHloVisitorWithDefault { // traversing. HloComputation* computation_; + // The backend-specific options selected for the algebraic simplifier. + const AlgebraicSimplifierOptions& options_; + // Whether algebraic simplification has occurred. bool changed_ = false; - // Whether layout is considered during transformation. - bool is_layout_sensitive_; - - // Callback used to determine if a bitcast is possible. - AlgebraicSimplifier::ValidBitcastCallback valid_bitcast_callback_; - - // Disable dot strength reduction on platforms where it causes a slowdown. - bool enable_dot_strength_reduction_; - - // Disable convolution -> dot simplification on platforms where it causes a - // slowdown. - bool enable_conv_simplification_; - // Cached computation for adding two scalar F32. HloComputation* scalar_add_computation_ = nullptr; }; @@ -348,19 +376,15 @@ class AlgebraicSimplifierVisitor : public DfsHloVisitorWithDefault { } // namespace bool AlgebraicSimplifierVisitor::Run( - HloComputation* computation, bool is_layout_sensitive, - AlgebraicSimplifier::ValidBitcastCallback valid_bitcast_callback, - bool enable_dot_strength_reduction, bool enable_conv_simplification) { - AlgebraicSimplifierVisitor visitor( - computation, is_layout_sensitive, std::move(valid_bitcast_callback), - enable_dot_strength_reduction, enable_conv_simplification); + HloComputation* computation, const AlgebraicSimplifierOptions& options) { + AlgebraicSimplifierVisitor visitor(computation, options); TF_CHECK_OK(computation->Accept(&visitor)); return visitor.changed_; } bool AlgebraicSimplifierVisitor::SameShape(const HloInstruction* lhs, const HloInstruction* rhs) const { - if (is_layout_sensitive_) { + if (options_.is_layout_sensitive()) { return ShapeUtil::Equal(lhs->shape(), rhs->shape()); } else { return ShapeUtil::Compatible(lhs->shape(), rhs->shape()); @@ -431,6 +455,40 @@ Status AlgebraicSimplifierVisitor::HandleAdd(HloInstruction* add) { sum_of_constants)); } + // A*C + B*C => (A+B)*C + // + // - If A, B, and C are integers, do this unconditionally. Proof of + // correctness: https://rise4fun.com/Alive/u9X. + // + // - If A, B, and C are floating point, do this if C is a scalar constant or + // broadcast of scalar constant and is equal to +/- 2^k for some (possibly + // negative) integer k. + // + // Multiplying by a power of 2 just moves the exponent, so our answer is + // exact modulo rounding of intermediate results so long as + // + // - none of the three products has an exponent which underflows (so the + // result is 0 or denormal), and + // - none of the three products overflows to inf. + // + // Proof: See algebraic_simplifier_proof_distributive_property.py. + // + // We deem these differences in rounding, underflow, and overflow + // acceptable in the ML context. + HloInstruction *b, *c; + if (((Match(lhs, m::Multiply(m::Op(&a), m::Op(&c))) && + Match(rhs, m::MultiplyAnyOrder(m::Op().Is(c), m::Op(&b)))) || + (Match(lhs, m::Multiply(m::Op(&c), m::Op(&a))) && + Match(rhs, m::MultiplyAnyOrder(m::Op().Is(c), m::Op(&b))))) && + (ShapeUtil::ElementIsIntegral(add->shape()) || + IsAllFpConstantPowerOf2(c))) { + return ReplaceWithNewInstruction( + add, HloInstruction::CreateBinary( + add->shape(), HloOpcode::kMultiply, + computation_->AddInstruction(HloInstruction::CreateBinary( + add->shape(), HloOpcode::kAdd, a, b)), + c)); + } return Status::OK(); } @@ -504,8 +562,8 @@ Status AlgebraicSimplifierVisitor::HandleCopy(HloInstruction* copy) { return Status::OK(); } - if (is_layout_sensitive_ && - ReshapeOrCopyIsBitcast(copy, valid_bitcast_callback_)) { + if (options_.is_layout_sensitive() && + ReshapeOrCopyIsBitcast(copy, options_.valid_bitcast_callback())) { ReplaceWithBitcast(copy); } @@ -541,7 +599,74 @@ Status AlgebraicSimplifierVisitor::HandleConcatenate( VLOG(10) << "trying to replace " << concatenate->ToString() << " with " << replacement->ToString(); ReplaceInstructionIfSameShape(concatenate, replacement); - } else if (operands.size() == 2) { + return Status::OK(); + } + + // Check if we can merge "adjacent" slice operands which take slices from the + // same other op. For simplicity we only merge unstrided slices. + int64 concatenate_dimension = concatenate->concatenate_dimension(); + for (int64 i = 0; i < operands.size(); ++i) { + if (operands[i]->opcode() != HloOpcode::kSlice || + !IsUnstridedSlice(operands[i])) { + continue; + } + int64 slice_end = operands[i]->slice_limits(concatenate_dimension); + HloInstruction* slice_operand = operands[i]->mutable_operand(0); + int64 j = i + 1; + while (j < operands.size() && operands[j]->opcode() == HloOpcode::kSlice && + IsUnstridedSlice(operands[j]) && + operands[j]->operand(0) == slice_operand && + operands[j]->slice_starts(concatenate_dimension) == slice_end) { + // Check that all the slice_start values are the same in all other + // dimensions. This implies that the slice_limit values are also the same, + // because operands of concatenate need to have the same shape, and we + // already checked that the slices are unstrided. + bool same_other_starts = true; + for (int64 k = 0; k < operands[j]->slice_starts().size(); ++k) { + if (k == concatenate_dimension) { + continue; + } + if (operands[i]->slice_starts(k) != operands[j]->slice_starts(k)) { + same_other_starts = false; + break; + } + } + if (!same_other_starts) { + break; + } + slice_end = operands[j]->slice_limits(concatenate_dimension); + ++j; + } + if (j - i > 1) { + Shape new_slice_shape = operands[i]->shape(); + new_slice_shape.set_dimensions( + concatenate_dimension, + slice_end - operands[i]->slice_starts(concatenate_dimension)); + auto new_limit_indices = operands[i]->slice_limits(); + new_limit_indices[concatenate_dimension] = slice_end; + auto new_slice_op = + computation_->AddInstruction(HloInstruction::CreateSlice( + new_slice_shape, slice_operand, + /*start_indices=*/operands[i]->slice_starts(), + /*limit_indices=*/new_limit_indices, + /*strides=*/operands[i]->slice_strides())); + std::vector new_operands; + for (int64 k = 0; k < i; ++k) { + new_operands.push_back(operands[k]); + } + new_operands.push_back(new_slice_op); + for (int64 k = j; k < operands.size(); ++k) { + new_operands.push_back(operands[k]); + } + auto replacement = + computation_->AddInstruction(concatenate->CloneWithNewOperands( + concatenate->shape(), new_operands)); + ReplaceInstructionIfSameShape(concatenate, replacement); + return Status::OK(); + } + } + + if (operands.size() == 2) { // A binary concat with a broadcasted scalar as an operand can be converted // into a pad which is simpler to fold into other operations. bool is_effective_low_pad = Match( @@ -557,7 +682,7 @@ Status AlgebraicSimplifierVisitor::HandleConcatenate( padding_config_dim->set_edge_padding_high(0); padding_config_dim->set_edge_padding_low(0); padding_config_dim->set_interior_padding(0); - if (dim == concatenate->concatenate_dimension()) { + if (dim == concatenate_dimension) { if (is_effective_low_pad) { padding_config_dim->set_edge_padding_low( operands[0]->shape().dimensions(dim)); @@ -1215,7 +1340,8 @@ Status AlgebraicSimplifierVisitor::HandleDot(HloInstruction* dot) { return ReplaceInstruction(dot, dot_of_gather_optimized); } - if (enable_dot_strength_reduction_ && !is_layout_sensitive_) { + if (options_.enable_dot_strength_reduction() && + !options_.is_layout_sensitive()) { TF_ASSIGN_OR_RETURN(bool did_strength_reduction, HandleDotStrengthReduction(dot)); if (did_strength_reduction) { @@ -1619,6 +1745,27 @@ Status AlgebraicSimplifierVisitor::HandlePad(HloInstruction* pad) { pad, HloInstruction::CreateBroadcast(pad->shape(), pad->mutable_operand(1), {})); } + + // Interior padding on one sized dimensions have no effect. As a result it + // makes other simplifications possible if there is no interior padding. + if (HasInteriorPadding(pad->padding_config())) { + PaddingConfig padding_config = pad->padding_config(); + bool cleared_interior_padding = false; + for (int64 i = 0; i < ShapeUtil::Rank(pad->shape()); ++i) { + if (padding_config.dimensions(i).interior_padding() > 0 && + pad->operand(0)->shape().dimensions(i) == 1) { + cleared_interior_padding = true; + padding_config.mutable_dimensions(i)->set_interior_padding(0); + } + } + if (cleared_interior_padding) { + return ReplaceWithNewInstruction( + pad, + HloInstruction::CreatePad(pad->shape(), pad->mutable_operand(0), + pad->mutable_operand(1), padding_config)); + } + } + // Eliminate nop pads (padding all zero), and replace a pad with negative // padding with a pad with non-negative padding followed by a slice. bool all_zero = true; @@ -1910,8 +2057,8 @@ Status AlgebraicSimplifierVisitor::HandleReshape(HloInstruction* reshape) { } // Make this a bitcast if possible. - if (is_layout_sensitive_ && - ReshapeOrCopyIsBitcast(reshape, valid_bitcast_callback_)) { + if (options_.is_layout_sensitive() && + ReshapeOrCopyIsBitcast(reshape, options_.valid_bitcast_callback())) { ReplaceWithBitcast(reshape); return Status::OK(); } @@ -2030,11 +2177,6 @@ StatusOr AlgebraicSimplifierVisitor::TrySimplifyScalarSlice( return false; } -bool IsUnstridedSlice(const HloInstruction* hlo) { - return absl::c_all_of(hlo->slice_strides(), - [](int64 stride) { return stride == 1; }); -} - StatusOr AlgebraicSimplifierVisitor::TryToReorderSliceAndReshape( HloInstruction* slice) { CHECK_EQ(slice->opcode(), HloOpcode::kSlice); @@ -2501,6 +2643,108 @@ Status AlgebraicSimplifierVisitor::HandleSort(HloInstruction* sort) { return ReplaceWithNewInstruction( sort, HloInstruction::CreateTuple(sort->operands())); } + if (!options_.enable_permutation_sort_replacement()) { + return Status::OK(); + } + // Check if we are sorting a permutation. In that case, we know that the keys + // will be sorted to the identity permutation, and we can represent the + // changes to the 'values' parameter as a scatter. + if (sort->operand_count() == 2 && + operand->opcode() == HloOpcode::kGetTupleElement) { + const HloInstruction* other_sort = operand->operand(0); + // Check whether the 'values' parameter is the result of another sort with + // the same sort dimension. + if (other_sort->opcode() == HloOpcode::kSort && + other_sort->operand_count() >= 2 && + other_sort->dimensions(0) == dimension_to_sort && + other_sort->operand(operand->tuple_index())->opcode() == + HloOpcode::kIota) { + auto* iota = + Cast(other_sort->operand(operand->tuple_index())); + // The sort operand needs to be an integral iota, and the iota dimension + // needs to be the dimension that was sorted. + if (iota->iota_dimension() == dimension_to_sort && + ShapeUtil::ElementIsIntegral(iota->shape())) { + // We use the following construction method for a Scatter that applies + // the permutation from 'keys' to the 'values' parameter. + // - Take the "keys" parameter of the second sort and reshape it to have + // another "1" dimension at the end. + // - Concatenate it with iotas of the same extended shape with all + // different iota_dimensions except the dimension_to_sort in the order + // of iota_dimensions/dimension_to_sort, so e.g. with rank 3 and + // dimension_to_sort = 1, we would have concatenate of (iota with + // iota_dimension=0, keys, iota with iota_dimension = 2) + // - Use this as the indices parameter of scatter, and set updates + // of the scatter to be a reshaped 'values' parameter of sort (adding + // 'rank' many 1 dimensions at the end). + int64 rank = ShapeUtil::Rank(operand->shape()); + Shape extended_shape = operand->shape(); + extended_shape.add_dimensions(1); + extended_shape.mutable_layout()->add_minor_to_major(rank); + auto reshaped_permutation = computation_->AddInstruction( + HloInstruction::CreateReshape(extended_shape, operand)); + std::vector concat_operands; + for (int64 i = 0; i < rank; ++i) { + if (i == dimension_to_sort) { + concat_operands.push_back(reshaped_permutation); + } else { + concat_operands.push_back(computation_->AddInstruction( + HloInstruction::CreateIota(extended_shape, i))); + } + } + Shape concat_shape = operand->shape(); + concat_shape.add_dimensions(rank); + concat_shape.mutable_layout()->add_minor_to_major(rank); + auto scatter_indices = + rank > 1 ? computation_->AddInstruction( + HloInstruction::CreateConcatenate( + concat_shape, concat_operands, rank)) + : reshaped_permutation; + + // We don't care about the operand, it will be completely overridden by + // the updates. + auto scatter_operand = computation_->AddInstruction( + HloInstruction::CreateIota(sort->operand(1)->shape(), 0)); + + // Construct the updates operand of scatter. + Shape update_shape = sort->operand(1)->shape(); + for (int64 i = 0; i < rank; ++i) { + update_shape.add_dimensions(1); + update_shape.mutable_layout()->add_minor_to_major(rank + i); + } + auto scatter_updates = + computation_->AddInstruction(HloInstruction::CreateReshape( + update_shape, sort->mutable_operand(1))); + + // Construct the updates computation, which simply replaces the operand + // values with the update values. + HloComputation::Builder b("update_replace_computation"); + Shape scalar_shape = ShapeUtil::MakeShape(S32, {}); + b.AddInstruction( + HloInstruction::CreateParameter(0, scalar_shape, "scalar_lhs")); + auto scalar_rhs = b.AddInstruction( + HloInstruction::CreateParameter(1, scalar_shape, "scalar_rhs")); + auto update_replace_computation = + computation_->parent()->AddEmbeddedComputation(b.Build(scalar_rhs)); + + ScatterDimensionNumbers dim_numbers; + dim_numbers.set_index_vector_dim(rank); + for (int64 i = 0; i < rank; ++i) { + dim_numbers.add_update_window_dims(rank + i); + dim_numbers.add_scatter_dims_to_operand_dims(i); + } + auto scatter = + computation_->AddInstruction(HloInstruction::CreateScatter( + sort->operand(1)->shape(), scatter_operand, scatter_indices, + scatter_updates, update_replace_computation, dim_numbers)); + return ReplaceWithNewInstruction( + sort, HloInstruction::CreateTuple( + {computation_->AddInstruction(HloInstruction::CreateIota( + operand->shape(), dimension_to_sort)), + scatter})); + } + } + } return Status::OK(); } @@ -2525,7 +2769,7 @@ Status AlgebraicSimplifierVisitor::HandleTranspose(HloInstruction* transpose) { return ReplaceInstruction(transpose, operand); } - if (is_layout_sensitive_ && TransposeIsBitcast(transpose)) { + if (options_.is_layout_sensitive() && TransposeIsBitcast(transpose)) { ReplaceWithBitcast(transpose); return Status::OK(); } @@ -2674,13 +2918,13 @@ StatusOr AlgebraicSimplifierVisitor::SimplifyConvToDot( const ConvolutionDimensionNumbers& dnums = convolution->convolution_dimension_numbers(); - if (!enable_conv_simplification_) { + if (!options_.enable_conv_simplification()) { return false; } // TODO(b/31337498): For now, we cowardly refuse to do this optimization in // layout-insensitive mode, for fear of adding nontrivial reshapes. - if (!is_layout_sensitive_) { + if (!options_.is_layout_sensitive()) { return false; } @@ -2770,9 +3014,9 @@ StatusOr AlgebraicSimplifierVisitor::SimplifyConvToDot( // We cannot insert bitcasts if the layouts will not be compatible. // TODO(b/33178038): Consider inserting a transpose if a bitcast would be // invalid. - if (!valid_bitcast_callback_(input_shape, new_input_shape) || - !valid_bitcast_callback_(filter_shape, new_filter_shape) || - !valid_bitcast_callback_(dot_output_shape, convolution_shape)) { + if (!options_.valid_bitcast_callback()(input_shape, new_input_shape) || + !options_.valid_bitcast_callback()(filter_shape, new_filter_shape) || + !options_.valid_bitcast_callback()(dot_output_shape, convolution_shape)) { return false; } @@ -2878,9 +3122,7 @@ StatusOr AlgebraicSimplifier::Run(HloModule* module) { "AlgebraicSimplifier::Run(), before:\n" + module->ToString()); bool changed = false; for (auto* comp : module->MakeNonfusionComputations()) { - if (AlgebraicSimplifierVisitor::Run( - comp, is_layout_sensitive_, valid_bitcast_callback_, - enable_dot_strength_reduction_, enable_conv_simplification_)) { + if (AlgebraicSimplifierVisitor::Run(comp, options_)) { changed = true; } } diff --git a/tensorflow/compiler/xla/service/algebraic_simplifier.h b/tensorflow/compiler/xla/service/algebraic_simplifier.h index 9f8d0ee88b..d2775b9faf 100644 --- a/tensorflow/compiler/xla/service/algebraic_simplifier.h +++ b/tensorflow/compiler/xla/service/algebraic_simplifier.h @@ -23,8 +23,7 @@ limitations under the License. namespace xla { -// A pass which performs algebraic simplifications. -class AlgebraicSimplifier : public HloModulePass { +class AlgebraicSimplifierOptions { public: // Given shapes 'from_shape' and 'to_shape', determines if it is valid to // bitcast from 'from_shape' to 'to_shape' after considering platform @@ -34,18 +33,63 @@ class AlgebraicSimplifier : public HloModulePass { using ValidBitcastCallback = std::function; + explicit AlgebraicSimplifierOptions( + ValidBitcastCallback valid_bitcast_callback) + : valid_bitcast_callback_(std::move(valid_bitcast_callback)) {} + // If valid_bitcast_callback returns true, then the pass will replace reshapes + // and transposes with bitcasts. + const ValidBitcastCallback& valid_bitcast_callback() const { + return valid_bitcast_callback_; + } + + // If is_layout_sensitive is true, then the simplifier preserves layout during + // transformation. Otherwise, layout is ignored. + void set_is_layout_sensitive(bool is_layout_sensitive) { + is_layout_sensitive_ = is_layout_sensitive; + } + bool is_layout_sensitive() const { return is_layout_sensitive_; } + + // Enable dot simplification on platforms where it is profitable. + void set_enable_dot_strength_reduction(bool enable_dot_strength_reduction) { + enable_dot_strength_reduction_ = enable_dot_strength_reduction; + } + bool enable_dot_strength_reduction() const { + return enable_dot_strength_reduction_; + } + + // Enable convolution simplification on platforms where it is profitable. + void set_enable_conv_simplification(bool enable_conv_simplification) { + enable_conv_simplification_ = enable_conv_simplification; + } + bool enable_conv_simplification() const { + return enable_conv_simplification_; + } + + // If enable_permutation_sort_replacement is true, a sort op that is known to + // sort a permutation will be replaced with a scatter op. + void set_enable_permutation_sort_replacement( + bool enable_permutation_sort_replacement) { + enable_permutation_sort_replacement_ = enable_permutation_sort_replacement; + } + bool enable_permutation_sort_replacement() const { + return enable_permutation_sort_replacement_; + } + + private: + ValidBitcastCallback valid_bitcast_callback_; + bool is_layout_sensitive_{false}; + bool enable_dot_strength_reduction_{true}; + bool enable_conv_simplification_{true}; + bool enable_permutation_sort_replacement_{false}; +}; + +// A pass which performs algebraic simplifications. +class AlgebraicSimplifier : public HloModulePass { + public: // If is_layout_sensitive is true, then the simplifier preserves layout during - // transformation. Otherwise, layout is ignored. If valid_bitcast_callback - // returns true, then the pass will replace reshapes and transposes with - // bitcasts. - AlgebraicSimplifier(bool is_layout_sensitive, - ValidBitcastCallback valid_bitcast_callback, - bool enable_dot_strength_reduction = true, - bool enable_conv_simplification = true) - : is_layout_sensitive_(is_layout_sensitive), - valid_bitcast_callback_(std::move(valid_bitcast_callback)), - enable_dot_strength_reduction_(enable_dot_strength_reduction), - enable_conv_simplification_(enable_conv_simplification) {} + // transformation. Otherwise, layout is ignored. + explicit AlgebraicSimplifier(const AlgebraicSimplifierOptions& options) + : options_(options) {} ~AlgebraicSimplifier() override = default; absl::string_view name() const override { return "algsimp"; } @@ -54,14 +98,7 @@ class AlgebraicSimplifier : public HloModulePass { StatusOr Run(HloModule* module) override; private: - bool is_layout_sensitive_; - ValidBitcastCallback valid_bitcast_callback_; - - // Enable dot simplification on platforms where it is profitable. - bool enable_dot_strength_reduction_; - - // Enable convolution simplification on platforms where it is profitable. - bool enable_conv_simplification_; + AlgebraicSimplifierOptions options_; }; } // namespace xla diff --git a/tensorflow/compiler/xla/service/algebraic_simplifier_proof_distributive_property.py b/tensorflow/compiler/xla/service/algebraic_simplifier_proof_distributive_property.py new file mode 100644 index 0000000000..5da13da041 --- /dev/null +++ b/tensorflow/compiler/xla/service/algebraic_simplifier_proof_distributive_property.py @@ -0,0 +1,82 @@ +# Copyright 2018 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. +# ============================================================================== +"""Proof that transforming (A*C)+(B*C) <=> (A+B)*C is "safe" if C=2^k. + +Specifically, for all floating-point values A, B, and C, if + + - C is equal to +/- 2^k for some (possibly negative) integer k, and + - A, B, C, A*C, B*C, and A+B are not subnormal, zero, or inf, + +then there exists a rounding mode rm in [RTZ, RNE] such that + + (A*C) + (B*C) == (A+B) * C (computed with rounding mode rm). + +Informally, this means that the equivalence holds for powers of 2 C, modulo +flushing to zero or inf, and modulo rounding of intermediate results. + +Requires z3 python bindings; try `pip install z3-solver`. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +import z3 + +# We do float16 because it lets the solver run much faster. These results +# should generalize to fp32 and fp64, and you can verify this by changing the +# value of FLOAT_TY (and then waiting a while). +FLOAT_TY = z3.Float16 + +a = z3.FP("a", FLOAT_TY()) +b = z3.FP("b", FLOAT_TY()) +c = z3.FP("c", FLOAT_TY()) + +s = z3.Solver() + +# C must be a power of 2, i.e. significand bits must all be 0. +s.add(z3.Extract(FLOAT_TY().sbits() - 1, 0, z3.fpToIEEEBV(c)) == 0) + +for rm in [z3.RTZ(), z3.RNE()]: + z3.set_default_rounding_mode(rm) + before = a * c + b * c + after = (a + b) * c + + # Check that before == after, allowing that 0 == -0. + s.add( + z3.Not( + z3.Or( + before == after, # + z3.And(z3.fpIsZero(before), z3.fpIsZero(after))))) + + for x in [ + (a * c), + (b * c), + (a + b), + ]: + s.add(z3.Not(z3.fpIsSubnormal(x))) + s.add(z3.Not(z3.fpIsZero(x))) + s.add(z3.Not(z3.fpIsInf(x))) + +if s.check() == z3.sat: + m = s.model() + print("Counterexample found!") + print(m) + print("a*c: ", z3.simplify(m[a] * m[c])) + print("b*c: ", z3.simplify(m[b] * m[c])) + print("a+b: ", z3.simplify(m[a] + m[b])) + print("a*c + b*c: ", z3.simplify(m[a] * m[c] + m[b] * m[c])) + print("(a+b) * c: ", z3.simplify((m[a] + m[b]) * m[c])) +else: + print("Proved!") diff --git a/tensorflow/compiler/xla/service/algebraic_simplifier_test.cc b/tensorflow/compiler/xla/service/algebraic_simplifier_test.cc index e4c4da1b0e..14ce519b6a 100644 --- a/tensorflow/compiler/xla/service/algebraic_simplifier_test.cc +++ b/tensorflow/compiler/xla/service/algebraic_simplifier_test.cc @@ -27,9 +27,11 @@ limitations under the License. #include "tensorflow/compiler/xla/service/hlo_computation.h" #include "tensorflow/compiler/xla/service/hlo_instruction.h" #include "tensorflow/compiler/xla/service/hlo_instructions.h" -#include "tensorflow/compiler/xla/service/hlo_matchers.h" #include "tensorflow/compiler/xla/service/hlo_opcode.h" +#include "tensorflow/compiler/xla/service/hlo_parser.h" #include "tensorflow/compiler/xla/service/hlo_pass_fix.h" +#include "tensorflow/compiler/xla/service/pattern_matcher.h" +#include "tensorflow/compiler/xla/service/pattern_matcher_gmock.h" #include "tensorflow/compiler/xla/shape_util.h" #include "tensorflow/compiler/xla/test.h" #include "tensorflow/compiler/xla/tests/hlo_test_base.h" @@ -42,18 +44,20 @@ namespace xla { namespace { using ::testing::ElementsAre; +namespace m = match; -namespace op = xla::testing::opcode_matchers; - -AlgebraicSimplifier::ValidBitcastCallback bitcasting_callback() { +AlgebraicSimplifierOptions::ValidBitcastCallback bitcasting_callback() { return [](const Shape&, const Shape&) { return true; }; } -AlgebraicSimplifier::ValidBitcastCallback non_bitcasting_callback() { +AlgebraicSimplifierOptions::ValidBitcastCallback non_bitcasting_callback() { return [](const Shape&, const Shape&) { return false; }; } -class AlgebraicSimplifierTest : public HloTestBase {}; +class AlgebraicSimplifierTest : public HloTestBase { + protected: + AlgebraicSimplifierOptions default_options_{non_bitcasting_callback()}; +}; // Test that A + 0 is simplified to A TEST_F(AlgebraicSimplifierTest, AddZero) { @@ -70,13 +74,134 @@ TEST_F(AlgebraicSimplifierTest, AddZero) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root->opcode(), HloOpcode::kAdd); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); EXPECT_EQ(root, param0); } +TEST_F(AlgebraicSimplifierTest, FactorIntegerAddition) { + const char* kModuleStr = R"( + HloModule m + test { + p0 = s32[8] parameter(0) + p1 = s32[8] parameter(1) + p2 = s32[8] parameter(2) + x = s32[8] multiply(p0, p2) + y = s32[8] multiply(p1, p2) + ROOT sum = s32[8] add(x, y) + } + )"; + TF_ASSERT_OK_AND_ASSIGN(auto m, ParseAndReturnVerifiedModule(kModuleStr)); + AlgebraicSimplifier simplifier(default_options_); + ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); + EXPECT_THAT( + m->entry_computation()->root_instruction(), + GmockMatch(m::MultiplyAnyOrder( + m::AddAnyOrder(m::Parameter(0), m::Parameter(1)), m::Parameter(2)))); +} + +// A*C + B*C => (A+B)*C if C is a floating-point power of 2. +TEST_F(AlgebraicSimplifierTest, FactorFpAddition) { + const char* kModuleStr = R"( + HloModule m + test { + p0 = f32[] parameter(0) + p1 = f32[] parameter(1) + c = f32[] constant(0.125) + x = f32[] multiply(p0, c) + y = f32[] multiply(p1, c) + ROOT sum = f32[] add(x, y) + } + )"; + TF_ASSERT_OK_AND_ASSIGN(auto m, ParseAndReturnVerifiedModule(kModuleStr)); + ASSERT_TRUE(AlgebraicSimplifier(default_options_).Run(m.get()).ValueOrDie()); + EXPECT_THAT(m->entry_computation()->root_instruction(), + GmockMatch(m::MultiplyAnyOrder( + m::AddAnyOrder(m::Parameter(0), m::Parameter(1)), + m::ConstantScalar(0.125)))); +} + +// A*C + B*C => (A+B)*C if C is a broadcast of a floating-point power of 2. +TEST_F(AlgebraicSimplifierTest, FactorFpAdditionWithBroadcast) { + const char* kModuleStr = R"( + HloModule m + test { + p0 = f32[4] parameter(0) + p1 = f32[4] parameter(1) + c = f32[] constant(0.125) + b = f32[4] broadcast(c), dimensions={} + x = f32[4] multiply(p0, b) + y = f32[4] multiply(p1, b) + ROOT sum = f32[4] add(x, y) + } + )"; + TF_ASSERT_OK_AND_ASSIGN(auto m, ParseAndReturnVerifiedModule(kModuleStr)); + ASSERT_TRUE(AlgebraicSimplifier(default_options_).Run(m.get()).ValueOrDie()); + EXPECT_THAT(m->entry_computation()->root_instruction(), + GmockMatch(m::MultiplyAnyOrder( + m::AddAnyOrder(m::Parameter(0), m::Parameter(1)), + m::Broadcast(m::ConstantScalar(0.125))))); +} + +// A*C + B*C => (A+B)*C simplification should not happen if C is not a +// floating-point power of 2. +TEST_F(AlgebraicSimplifierTest, FactorFpAdditionNotPowerOf2) { + const char* kModuleStr = R"( + HloModule m + test { + p0 = f32[] parameter(0) + p1 = f32[] parameter(1) + c = f32[] constant(0.3) + x = f32[] multiply(p0, c) + y = f32[] multiply(p1, c) + ROOT sum = f32[] add(x, y) + } + )"; + TF_ASSERT_OK_AND_ASSIGN(auto m, ParseAndReturnVerifiedModule(kModuleStr)); + EXPECT_FALSE(AlgebraicSimplifier(default_options_).Run(m.get()).ValueOrDie()); +} + +// A*C + B*C => (A+B)*C simplification should not happen if A, B, and C are +// complex numbers. +TEST_F(AlgebraicSimplifierTest, FactorFpAdditionComplex) { + const char* kModuleStr = R"( + HloModule m + test { + p0 = c64[8] parameter(0) + p1 = c64[8] parameter(1) + p2 = c64[8] parameter(2) + x = c64[8] multiply(p0, p2) + y = c64[8] multiply(p1, p2) + ROOT sum = c64[8] add(x, y) + } + )"; + TF_ASSERT_OK_AND_ASSIGN(auto m, ParseAndReturnVerifiedModule(kModuleStr)); + EXPECT_FALSE(AlgebraicSimplifier(default_options_).Run(m.get()).ValueOrDie()); +} + +// A*C + B*C => (A+B)*C simplification is OK if A, B, and C are complex. +TEST_F(AlgebraicSimplifierTest, FactorFpAdditionBfloat16) { + const char* kModuleStr = R"( + HloModule m + test { + p0 = bf16[4] parameter(0) + p1 = bf16[4] parameter(1) + c = bf16[] constant(0.125) + b = bf16[4] broadcast(c), dimensions={} + x = bf16[4] multiply(p0, b) + y = bf16[4] multiply(p1, b) + ROOT sum = bf16[4] add(x, y) + } + )"; + TF_ASSERT_OK_AND_ASSIGN(auto m, ParseAndReturnVerifiedModule(kModuleStr)); + ASSERT_TRUE(AlgebraicSimplifier(default_options_).Run(m.get()).ValueOrDie()); + EXPECT_THAT(m->entry_computation()->root_instruction(), + GmockMatch(m::MultiplyAnyOrder( + m::AddAnyOrder(m::Parameter(0), m::Parameter(1)), + m::Broadcast(m::ConstantScalar(0.125))))); +} + // Test that A * 0 is simplified to 0 TEST_F(AlgebraicSimplifierTest, MulZero) { auto m = CreateNewVerifiedModule(); @@ -92,8 +217,7 @@ TEST_F(AlgebraicSimplifierTest, MulZero) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root->opcode(), HloOpcode::kMultiply); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); EXPECT_EQ(computation->root_instruction(), zero); } @@ -115,8 +239,7 @@ TEST_F(AlgebraicSimplifierTest, SelectTrue) { auto computation = module->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root->opcode(), HloOpcode::kSelect); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(module.get()).ValueOrDie()); EXPECT_EQ(computation->root_instruction(), param0); } @@ -138,8 +261,7 @@ TEST_F(AlgebraicSimplifierTest, SelectFalse) { auto computation = module->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root->opcode(), HloOpcode::kSelect); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(module.get()).ValueOrDie()); EXPECT_EQ(computation->root_instruction(), param1); } @@ -159,8 +281,7 @@ TEST_F(AlgebraicSimplifierTest, SelectIdentical) { auto computation = module->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root->opcode(), HloOpcode::kSelect); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(module.get()).ValueOrDie()); EXPECT_EQ(computation->root_instruction(), param1); } @@ -196,11 +317,10 @@ TEST_F(AlgebraicSimplifierTest, TwoReducesToOne) { builder.AddInstruction(HloInstruction::CreateReduce(r1f32, reduce0, zero, dims1, add_computation)); m->AddEntryComputation(builder.Build()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); HloInstruction* root = m->entry_computation()->root_instruction(); - EXPECT_THAT(root, op::Reduce(param, zero)); + EXPECT_THAT(root, GmockMatch(m::Reduce(m::Parameter(0), m::Op().Is(zero)))); EXPECT_EQ(root->dimensions(), std::vector({0, 2, 3})); } @@ -219,11 +339,10 @@ TEST_F(AlgebraicSimplifierTest, AddConstOnLHS) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root->opcode(), HloOpcode::kAdd); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); - EXPECT_THAT(root, op::Add(param0, op::Constant())); + EXPECT_THAT(root, GmockMatch(m::Add(m::Parameter(0), m::Constant()))); } // Test that [(A + C1) + C2] => [A + (C1 + C2)] for constants C1 and C2. @@ -246,11 +365,12 @@ TEST_F(AlgebraicSimplifierTest, AddReassociateMergeConstants) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root->opcode(), HloOpcode::kAdd); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); - EXPECT_THAT(root, op::Add(param0, op::Add(constant1, constant2))); + EXPECT_THAT(root, GmockMatch(m::Add( + m::Op().Is(param0), + m::Add(m::Op().Is(constant1), m::Op().Is(constant2))))); } TEST_F(AlgebraicSimplifierTest, AddBroadcastZeroR0Operand) { @@ -269,8 +389,7 @@ TEST_F(AlgebraicSimplifierTest, AddBroadcastZeroR0Operand) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root->opcode(), HloOpcode::kAdd); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); EXPECT_EQ(root, param0); @@ -306,11 +425,11 @@ TEST_F(AlgebraicSimplifierTest, InlineTrivialMap) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root->opcode(), HloOpcode::kMap); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); - EXPECT_THAT(root, op::Add(param0, op::Broadcast(zero))); + EXPECT_THAT(root, GmockMatch(m::Add(m::Parameter(0), + m::Broadcast(m::Op().Is(zero))))); } TEST_F(AlgebraicSimplifierTest, AddBroadcastZeroR1Operand) { @@ -329,8 +448,7 @@ TEST_F(AlgebraicSimplifierTest, AddBroadcastZeroR1Operand) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root->opcode(), HloOpcode::kAdd); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); EXPECT_EQ(root, param0); @@ -344,12 +462,11 @@ TEST_F(AlgebraicSimplifierTest, ConstantToBroadcast) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); - EXPECT_THAT(root, op::Constant()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + EXPECT_THAT(root, GmockMatch(m::Constant())); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); - EXPECT_THAT(root, op::Broadcast(op::Constant())); + EXPECT_THAT(root, GmockMatch(m::Broadcast(m::Constant()))); EXPECT_EQ(3.14f, root->operand(0)->literal().GetFirstElement()); } @@ -361,12 +478,11 @@ TEST_F(AlgebraicSimplifierTest, ConstantNotToBroadcast) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); - EXPECT_THAT(root, op::Constant()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + EXPECT_THAT(root, GmockMatch(m::Constant())); + AlgebraicSimplifier simplifier(default_options_); ASSERT_FALSE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); - EXPECT_THAT(root, op::Constant()); + EXPECT_THAT(root, GmockMatch(m::Constant())); } TEST_F(AlgebraicSimplifierTest, IotaToBroadcast) { @@ -377,12 +493,11 @@ TEST_F(AlgebraicSimplifierTest, IotaToBroadcast) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); - EXPECT_THAT(root, op::Constant()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + EXPECT_THAT(root, GmockMatch(m::Constant())); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); - EXPECT_THAT(root, op::Iota()); + EXPECT_THAT(root, GmockMatch(m::Iota())); } // Test that A - 0 is simplified to A @@ -400,8 +515,7 @@ TEST_F(AlgebraicSimplifierTest, SubZero) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root->opcode(), HloOpcode::kSubtract); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); EXPECT_EQ(root, param0); @@ -422,11 +536,11 @@ TEST_F(AlgebraicSimplifierTest, SubConstCanonicalization) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root->opcode(), HloOpcode::kSubtract); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); - EXPECT_THAT(root, op::Add(param0, op::Negate(constant))); + EXPECT_THAT(root, GmockMatch(m::Add(m::Parameter(0), + m::Negate(m::Op().Is(constant))))); } // Test that (A/B)/C is simplified to A/(B*C). @@ -448,14 +562,16 @@ TEST_F(AlgebraicSimplifierTest, LhsDivOfDiv) { auto computation = m->AddEntryComputation(builder.Build()); EXPECT_THAT(computation->root_instruction(), - op::Divide(op::Divide(param0, param1), param2)); + GmockMatch(m::Divide(m::Divide(m::Parameter(0), m::Parameter(1)), + m::Parameter(2)))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); - EXPECT_THAT(computation->root_instruction(), - op::Divide(param0, op::Multiply(param1, param2))); + EXPECT_THAT( + computation->root_instruction(), + GmockMatch(m::Divide(m::Parameter(0), + m::Multiply(m::Parameter(1), m::Parameter(2))))); } // Test that A/(B/C) is simplified to (A*C)/B. @@ -476,15 +592,18 @@ TEST_F(AlgebraicSimplifierTest, RhsDivOfDiv) { auto computation = m->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), - op::Divide(param0, op::Divide(param1, param2))); + EXPECT_THAT( + computation->root_instruction(), + GmockMatch(m::Divide(m::Parameter(0), + m::Divide(m::Parameter(1), m::Parameter(2))))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); - EXPECT_THAT(computation->root_instruction(), - op::Divide(op::Multiply(param0, param2), param1)); + EXPECT_THAT( + computation->root_instruction(), + GmockMatch(m::Divide(m::Multiply(m::Parameter(0), m::Parameter(2)), + m::Parameter(1)))); } // Test that (A/B)/(C/D) is simplified to (A*D)/(B*C). @@ -511,15 +630,16 @@ TEST_F(AlgebraicSimplifierTest, DivOfDivAndDiv) { EXPECT_THAT( computation->root_instruction(), - op::Divide(op::Divide(param0, param1), op::Divide(param2, param3))); + GmockMatch(m::Divide(m::Divide(m::Parameter(0), m::Parameter(1)), + m::Divide(m::Parameter(2), m::Parameter(3))))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); EXPECT_THAT( computation->root_instruction(), - op::Divide(op::Multiply(param0, param3), op::Multiply(param1, param2))); + GmockMatch(m::Divide(m::Multiply(m::Parameter(0), m::Parameter(3)), + m::Multiply(m::Parameter(1), m::Parameter(2))))); } // Test that A/exp(B) is simplified to A*exp(-B). @@ -539,14 +659,14 @@ TEST_F(AlgebraicSimplifierTest, DivOfExp) { auto computation = m->AddEntryComputation(builder.Build()); EXPECT_THAT(computation->root_instruction(), - op::Divide(param0, op::Exp(param1))); + GmockMatch(m::Divide(m::Parameter(0), m::Exp(m::Parameter(1))))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); EXPECT_THAT(computation->root_instruction(), - op::Multiply(param0, op::Exp(op::Negate(param1)))); + GmockMatch(m::Multiply(m::Parameter(0), + m::Exp(m::Negate(m::Parameter(1)))))); } // Test that A/pow(B,C) is simplified to A*pow(B,-C). @@ -567,15 +687,18 @@ TEST_F(AlgebraicSimplifierTest, DivOfPower) { auto computation = m->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), - op::Divide(param0, op::Power(param1, param2))); + EXPECT_THAT( + computation->root_instruction(), + GmockMatch(m::Divide(m::Parameter(0), + m::Power(m::Parameter(1), m::Parameter(2))))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); EXPECT_THAT(computation->root_instruction(), - op::Multiply(param0, op::Power(param1, op::Negate(param2)))); + GmockMatch(m::Multiply( + m::Parameter(0), + m::Power(m::Parameter(1), m::Negate(m::Parameter(2)))))); } // Test that broadcasting is done on the right step when simplifying A/pow(B,C) @@ -597,15 +720,18 @@ TEST_F(AlgebraicSimplifierTest, DivOfBroadcastingPower) { auto computation = m->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), - op::Divide(param0, op::Power(param1, param2))); + EXPECT_THAT( + computation->root_instruction(), + GmockMatch(m::Divide(m::Parameter(0), + m::Power(m::Parameter(1), m::Parameter(2))))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); ASSERT_THAT(computation->root_instruction(), - op::Multiply(param0, op::Power(param1, op::Negate(param2)))); + GmockMatch(m::Multiply( + m::Parameter(0), + m::Power(m::Parameter(1), m::Negate(m::Parameter(2)))))); } // A / Const => A * InvertedConst @@ -623,12 +749,11 @@ TEST_F(AlgebraicSimplifierTest, DivideByConstant) { auto computation = m->AddEntryComputation(builder.Build()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); EXPECT_THAT(computation->root_instruction(), - op::Multiply(param0, op::Constant())); + GmockMatch(m::Multiply(m::Parameter(0), m::Constant()))); } // pow(pow(A, X), Y) => pow(A, X*Y) @@ -648,11 +773,12 @@ TEST_F(AlgebraicSimplifierTest, PowerOfPower) { inner_power, exp2)); auto computation = m->AddEntryComputation(builder.Build()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); - EXPECT_THAT(computation->root_instruction(), - op::Power(base, op::Multiply(exp1, exp2))); + EXPECT_THAT( + computation->root_instruction(), + GmockMatch(m::Power(m::Op().Is(base), + m::Multiply(m::Op().Is(exp1), m::Op().Is(exp2))))); } // Don't simplify pow(pow(A, X), Y) => pow(A, X*Y) if X and Y are complex @@ -673,8 +799,7 @@ TEST_F(AlgebraicSimplifierTest, PowerOfPowerComplex) { inner_power, exp2)); m->AddEntryComputation(builder.Build()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_FALSE(simplifier.Run(m.get()).ValueOrDie()); } @@ -693,8 +818,7 @@ TEST_F(AlgebraicSimplifierTest, DivOneScalar) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root, div); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); EXPECT_EQ(root, param0); @@ -715,8 +839,7 @@ TEST_F(AlgebraicSimplifierTest, DivOneArray) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root, div); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); EXPECT_EQ(root, param0); @@ -740,8 +863,7 @@ TEST_F(AlgebraicSimplifierTest, ComplexOfRealImagC) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root, cplx); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); EXPECT_EQ(root, param0); @@ -765,8 +887,7 @@ TEST_F(AlgebraicSimplifierTest, RealOfComplex) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root, real); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); EXPECT_EQ(root, param0); @@ -790,8 +911,7 @@ TEST_F(AlgebraicSimplifierTest, ImagOfComplex) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root, imag); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); EXPECT_EQ(root, param1); @@ -818,11 +938,10 @@ TEST_F(AlgebraicSimplifierTest, SelectMakeTuple) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root, add); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); - EXPECT_THAT(root, op::Add(param1, param2)); + EXPECT_THAT(root, GmockMatch(m::Add(m::Parameter(1), m::Parameter(2)))); } // Test that exp(A)/exp(B) is simplified to exp(A-B) @@ -843,15 +962,16 @@ TEST_F(AlgebraicSimplifierTest, ExpDiv) { auto computation = m->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), - op::Divide(op::Exp(param0), op::Exp(param1))); + EXPECT_THAT( + computation->root_instruction(), + GmockMatch(m::Divide(m::Exp(m::Parameter(0)), m::Exp(m::Parameter(1))))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); - EXPECT_THAT(computation->root_instruction(), - op::Exp(op::Subtract(param0, param1))); + EXPECT_THAT( + computation->root_instruction(), + GmockMatch(m::Exp(m::Subtract(m::Parameter(0), m::Parameter(1))))); } // Test that exp(A)*exp(B) is simplified to exp(A+B) @@ -873,14 +993,14 @@ TEST_F(AlgebraicSimplifierTest, ExpMul) { auto computation = m->AddEntryComputation(builder.Build()); EXPECT_THAT(computation->root_instruction(), - op::Multiply(op::Exp(param0), op::Exp(param1))); + GmockMatch(m::Multiply(m::Exp(m::Parameter(0)), + m::Exp(m::Parameter(1))))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); EXPECT_THAT(computation->root_instruction(), - op::Exp(op::Add(param0, param1))); + GmockMatch(m::Exp(m::Add(m::Parameter(0), m::Parameter(1))))); } // Test that pow(exp(A), B) is simplified to exp(A*B) @@ -900,14 +1020,14 @@ TEST_F(AlgebraicSimplifierTest, PowExp) { auto computation = m->AddEntryComputation(builder.Build()); EXPECT_THAT(computation->root_instruction(), - op::Power(op::Exp(param0), param1)); + GmockMatch(m::Power(m::Exp(m::Parameter(0)), m::Parameter(1)))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); - EXPECT_THAT(computation->root_instruction(), - op::Exp(op::Multiply(param0, param1))); + EXPECT_THAT( + computation->root_instruction(), + GmockMatch(m::Exp(m::Multiply(m::Parameter(0), m::Parameter(1))))); } // Test that ln(pow(A, B)) is simplified to ln(A)*B @@ -927,14 +1047,14 @@ TEST_F(AlgebraicSimplifierTest, LnPow) { auto computation = m->AddEntryComputation(builder.Build()); EXPECT_THAT(computation->root_instruction(), - op::Log(op::Power(param0, param1))); + GmockMatch(m::Log(m::Power(m::Parameter(0), m::Parameter(1))))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); - EXPECT_THAT(computation->root_instruction(), - op::Multiply(op::Log(param0), param1)); + EXPECT_THAT( + computation->root_instruction(), + GmockMatch(m::Multiply(m::Log(m::Parameter(0)), m::Parameter(1)))); } // Test that ln(exp(A)) is simplified to A @@ -951,10 +1071,10 @@ TEST_F(AlgebraicSimplifierTest, LnExp) { auto computation = m->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Log(op::Exp(param0))); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Log(m::Exp(m::Parameter(0))))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); EXPECT_EQ(computation->root_instruction(), param0); @@ -981,13 +1101,14 @@ TEST_F(AlgebraicSimplifierTest, LnExpDiv) { auto computation = m->AddEntryComputation(builder.Build()); EXPECT_THAT(computation->root_instruction(), - op::Log(op::Divide(op::Exp(param0), op::Exp(param1)))); + GmockMatch(m::Log(m::Divide(m::Exp(m::Parameter(0)), + m::Exp(m::Parameter(1)))))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); - EXPECT_THAT(computation->root_instruction(), op::Subtract(param0, param1)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Subtract(m::Parameter(0), m::Parameter(1)))); } // Test that pow(A, 0) where A is a scalar is simplified to the scalar @@ -1005,14 +1126,14 @@ TEST_F(AlgebraicSimplifierTest, Pow0Scalar) { auto computation = m->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Power(param0, zero)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Power(m::Parameter(0), m::Op().Is(zero)))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); HloInstruction* root = computation->root_instruction(); - EXPECT_THAT(root, op::Constant()); + EXPECT_THAT(root, GmockMatch(m::Constant())); EXPECT_EQ(root->literal().GetFirstElement(), 1); } @@ -1030,14 +1151,14 @@ TEST_F(AlgebraicSimplifierTest, Pow0Vector) { auto computation = m->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Power(param0, zero)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Power(m::Parameter(0), m::Op().Is(zero)))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); HloInstruction* root = computation->root_instruction(); - EXPECT_THAT(root, op::Broadcast()); + EXPECT_THAT(root, GmockMatch(m::Broadcast())); EXPECT_TRUE(ShapeUtil::Equal(root->shape(), r1f32)) << ShapeUtil::HumanString(root->shape()); EXPECT_EQ(root->dimensions().size(), 0); @@ -1059,10 +1180,10 @@ TEST_F(AlgebraicSimplifierTest, Pow1) { auto computation = m->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Power(param0, one)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Power(m::Parameter(0), m::Op().Is(one)))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); EXPECT_EQ(computation->root_instruction(), param0); @@ -1082,13 +1203,14 @@ TEST_F(AlgebraicSimplifierTest, Pow2) { auto computation = m->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Power(param0, two)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Power(m::Parameter(0), m::Op().Is(two)))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); - EXPECT_THAT(computation->root_instruction(), op::Multiply(param0, param0)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Multiply(m::Parameter(0), m::Parameter(0)))); } // Test that pow(A, -1) is simplified to 1/A. @@ -1105,14 +1227,14 @@ TEST_F(AlgebraicSimplifierTest, PowNegative1) { auto computation = m->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Power(param0, negative_one)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Power(m::Parameter(0), m::Op().Is(negative_one)))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); HloInstruction* root = computation->root_instruction(); - EXPECT_THAT(root, op::Divide(op::Broadcast(), param0)); + EXPECT_THAT(root, GmockMatch(m::Divide(m::Broadcast(), m::Parameter(0)))); EXPECT_EQ(root->operand(0)->opcode(), HloOpcode::kBroadcast); EXPECT_EQ(root->operand(0)->operand(0)->literal().GetFirstElement(), 1); @@ -1153,13 +1275,12 @@ TEST_F(AlgebraicSimplifierTest, ZeroSizedConvolution) { ShapeUtil::MakeShape(F32, {3, 3, 3}), lhs, rhs, /*feature_group_count=*/1, window, dnums, DefaultPrecisionConfig(2))); m->AddEntryComputation(builder.Build()); - HloPassFix simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + HloPassFix simplifier(default_options_); EXPECT_THAT(m->entry_computation()->root_instruction(), - op::Convolution(lhs, rhs)); + GmockMatch(m::Convolution(m::Op().Is(lhs), m::Op().Is(rhs)))); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); EXPECT_THAT(m->entry_computation()->root_instruction(), - op::Broadcast(op::Constant())); + GmockMatch(m::Broadcast(m::Constant()))); } TEST_F(AlgebraicSimplifierTest, ZeroSizedReduceWindow) { @@ -1196,13 +1317,12 @@ TEST_F(AlgebraicSimplifierTest, ZeroSizedReduceWindow) { HloInstruction::CreateConstant(LiteralUtil::CreateR0(0.0f))), window, add_computation)); m->AddEntryComputation(builder.Build()); - HloPassFix simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + HloPassFix simplifier(default_options_); EXPECT_THAT(m->entry_computation()->root_instruction(), - op::ReduceWindow(param, op::Constant())); + GmockMatch(m::ReduceWindow(m::Parameter(0), m::Constant()))); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); EXPECT_THAT(m->entry_computation()->root_instruction(), - op::Broadcast(op::Constant())); + GmockMatch(m::Broadcast(m::Constant()))); } TEST_F(AlgebraicSimplifierTest, ZeroSizedPad) { @@ -1225,12 +1345,11 @@ TEST_F(AlgebraicSimplifierTest, ZeroSizedPad) { padding)); m->AddEntryComputation(builder.Build()); EXPECT_THAT(m->entry_computation()->root_instruction(), - op::Pad(param, op::Constant())); - HloPassFix simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + GmockMatch(m::Pad(m::Parameter(0), m::Constant()))); + HloPassFix simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); EXPECT_THAT(m->entry_computation()->root_instruction(), - op::Broadcast(op::Constant())); + GmockMatch(m::Broadcast(m::Constant()))); } TEST_F(AlgebraicSimplifierTest, ReshapeBroadcast) { @@ -1251,10 +1370,9 @@ TEST_F(AlgebraicSimplifierTest, ReshapeBroadcast) { m->AddEntryComputation(std::move(computation)); EXPECT_THAT(m->entry_computation()->root_instruction(), - op::Reshape(op::Broadcast(op::Reshape(op)))); + GmockMatch(m::Reshape(m::Broadcast(m::Reshape(m::Op().Is(op)))))); - HloPassFix simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + HloPassFix simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); EXPECT_THAT(m->entry_computation()->root_instruction(), op); @@ -1271,10 +1389,10 @@ TEST_F(AlgebraicSimplifierTest, ConvertBetweenSameType) { auto computation = m->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Convert(input)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Convert(m::Op().Is(input)))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); EXPECT_THAT(computation->root_instruction(), input); @@ -1292,10 +1410,10 @@ TEST_F(AlgebraicSimplifierTest, RemoveCopy) { auto computation = m->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Copy(param0)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Copy(m::Parameter(0)))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); EXPECT_THAT(computation->root_instruction(), param0); @@ -1314,19 +1432,24 @@ TEST_F(AlgebraicSimplifierTest, CopyEqualsBitcast) { *copy->mutable_shape()->mutable_layout() = LayoutUtil::MakeLayout({1, 2, 0, 3}); auto computation = m->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Copy(param)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Copy(m::Parameter(0)))); - AlgebraicSimplifier simplifier1(/*is_layout_sensitive=*/true, - non_bitcasting_callback()); + AlgebraicSimplifierOptions options(non_bitcasting_callback()); + options.set_is_layout_sensitive(true); + AlgebraicSimplifier simplifier1(options); ASSERT_FALSE(simplifier1.Run(m.get()).ValueOrDie()); // Verify that the copy is not replaced. - EXPECT_THAT(computation->root_instruction(), op::Copy(param)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Copy(m::Parameter(0)))); - AlgebraicSimplifier simplifier2(/*is_layout_sensitive=*/true, - bitcasting_callback()); + AlgebraicSimplifierOptions options2(bitcasting_callback()); + options2.set_is_layout_sensitive(true); + AlgebraicSimplifier simplifier2(options2); ASSERT_TRUE(simplifier2.Run(m.get()).ValueOrDie()); // Verify that the copy is replaced. - EXPECT_THAT(computation->root_instruction(), op::Bitcast(param)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Bitcast(m::Parameter(0)))); } // Test that unary concatenates are removed. @@ -1341,10 +1464,10 @@ TEST_F(AlgebraicSimplifierTest, RemoveUnaryConcatenate) { auto computation = m->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Concatenate(param0)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Concatenate(m::Parameter(0)))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); EXPECT_THAT(computation->root_instruction(), param0); @@ -1371,16 +1494,17 @@ TEST_F(AlgebraicSimplifierTest, RemoveEmptyConcatenateOperands) { auto computation = m->AddEntryComputation(builder.Build()); - EXPECT_THAT( - computation->root_instruction(), - op::Concatenate(empty_literal, param0, param0, empty_slice, param1)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Concatenate( + m::Op().Is(empty_literal), m::Parameter(0), m::Parameter(0), + m::Op().Is(empty_slice), m::Parameter(1)))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); EXPECT_THAT(computation->root_instruction(), - op::Concatenate(param0, param0, param1)); + GmockMatch(m::Concatenate(m::Parameter(0), m::Parameter(0), + m::Parameter(1)))); } // Test that reduce of concat is simplified. @@ -1423,14 +1547,14 @@ TEST_F(AlgebraicSimplifierTest, SimplifyReduceOfConcat) { auto computation = m->AddEntryComputation(builder.Build()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); EXPECT_THAT( computation->root_instruction(), - op::Map(op::Map(op::Reduce(param0, zero), op::Reduce(param1, zero)), - op::Reduce(param2, zero))); + GmockMatch(m::Map(m::Map(m::Reduce(m::Parameter(0), m::Op().Is(zero)), + m::Reduce(m::Parameter(1), m::Op().Is(zero))), + m::Reduce(m::Parameter(2), m::Op().Is(zero))))); } // Test a concatenate with only empty operands is removed. @@ -1453,10 +1577,10 @@ TEST_F(AlgebraicSimplifierTest, OnlyEmptyConcatenateOperands) { auto computation = m->AddEntryComputation(builder.Build()); EXPECT_THAT(computation->root_instruction(), - op::Concatenate(empty_literal, empty_slice)); + GmockMatch(m::Concatenate(m::Op().Is(empty_literal), + m::Op().Is(empty_slice)))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); EXPECT_EQ(computation->root_instruction(), empty_literal); @@ -1479,10 +1603,80 @@ TEST_F(AlgebraicSimplifierTest, ConcatenateOfBroadcastBecomesPad) { auto computation = m->AddEntryComputation(builder.Build()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); - EXPECT_THAT(computation->root_instruction(), op::Pad(param0, param1)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Pad(m::Parameter(0), m::Parameter(1)))); +} + +TEST_F(AlgebraicSimplifierTest, SimplifyConcatenateOfSlices) { + auto m = CreateNewVerifiedModule(); + Shape r2f32 = ShapeUtil::MakeShape(F32, {100, 99}); + Shape concat_shape = ShapeUtil::MakeShape(F32, {50, 80}); + HloComputation::Builder builder(TestName()); + HloInstruction* param0 = builder.AddInstruction( + HloInstruction::CreateParameter(0, r2f32, "param0")); + HloInstruction* param1 = builder.AddInstruction( + HloInstruction::CreateParameter(1, r2f32, "param1")); + + HloInstruction* slice0 = builder.AddInstruction(HloInstruction::CreateSlice( + ShapeUtil::MakeShape(F32, {50, 10}), param0, /*start_indices=*/{0, 0}, + /*limit_indices=*/{50, 10}, /*strides=*/{1, 1})); + + // Cannot merge 'slice0' and 'slice1' because of different start indices in + // dimension 0. + HloInstruction* slice1 = builder.AddInstruction(HloInstruction::CreateSlice( + ShapeUtil::MakeShape(F32, {50, 10}), param0, /*start_indices=*/{50, 10}, + /*limit_indices=*/{100, 20}, /*strides=*/{1, 1})); + + // Cannot merge 'slice1' and 'slice2' because of stride in dimension 2. + HloInstruction* slice2 = builder.AddInstruction(HloInstruction::CreateSlice( + ShapeUtil::MakeShape(F32, {50, 10}), param0, /*start_indices=*/{50, 20}, + /*limit_indices=*/{100, 40}, /*strides=*/{1, 2})); + + // Cannot merge 'slice2' and 'slice3' because of stride in dimension 2. + HloInstruction* slice3 = builder.AddInstruction(HloInstruction::CreateSlice( + ShapeUtil::MakeShape(F32, {50, 10}), param0, /*start_indices=*/{50, 40}, + /*limit_indices=*/{100, 50}, /*strides=*/{1, 1})); + + // Can merge 'slice3' and 'slice4'. + HloInstruction* slice4 = builder.AddInstruction(HloInstruction::CreateSlice( + ShapeUtil::MakeShape(F32, {50, 10}), param0, /*start_indices=*/{50, 50}, + /*limit_indices=*/{100, 60}, /*strides=*/{1, 1})); + + // Can merge 'slice4' and 'slice5'. + HloInstruction* slice5 = builder.AddInstruction(HloInstruction::CreateSlice( + ShapeUtil::MakeShape(F32, {50, 10}), param0, /*start_indices=*/{50, 60}, + /*limit_indices=*/{100, 70}, /*strides=*/{1, 1})); + + // Cannot merge 'slice5' and 'slice6' because of overlap. + HloInstruction* slice6 = builder.AddInstruction(HloInstruction::CreateSlice( + ShapeUtil::MakeShape(F32, {50, 10}), param0, /*start_indices=*/{50, 69}, + /*limit_indices=*/{100, 79}, /*strides=*/{1, 1})); + + // Cannot merge 'slice6' and 'slice7' because of slicing from a different + // parameter. + HloInstruction* slice7 = builder.AddInstruction(HloInstruction::CreateSlice( + ShapeUtil::MakeShape(F32, {50, 10}), param1, /*start_indices=*/{50, 79}, + /*limit_indices=*/{100, 89}, /*strides=*/{1, 1})); + + builder.AddInstruction(HloInstruction::CreateConcatenate( + concat_shape, + {slice0, slice1, slice2, slice3, slice4, slice5, slice6, slice7}, 1)); + auto computation = m->AddEntryComputation(builder.Build()); + + AlgebraicSimplifier simplifier(default_options_); + ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); + auto s = m::Slice(m::Parameter(0)); + EXPECT_THAT( + computation->root_instruction(), + GmockMatch(m::Concatenate(s, s, s, s, s, m::Slice(m::Parameter(1))))); + // The operand 3 should be a merge of 'slice3', 'slice4' and 'slice5', so its + // shape should have dimensions {50, 30}. + EXPECT_TRUE( + ShapeUtil::Equal(computation->root_instruction()->operand(3)->shape(), + ShapeUtil::MakeShape(F32, {50, 30}))); + EXPECT_EQ(computation->root_instruction()->operand(3)->slice_starts(1), 40); } // Test that a simplification which changes layouts is not performed if layout @@ -1502,14 +1696,17 @@ TEST_F(AlgebraicSimplifierTest, CopyWithDifferentLayout) { *param0->mutable_shape()->mutable_layout() = LayoutUtil::MakeLayout({0, 1}); *copy->mutable_shape()->mutable_layout() = LayoutUtil::MakeLayout({1, 0}); - EXPECT_THAT(computation->root_instruction(), op::Copy(param0)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Copy(m::Parameter(0)))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/true, - non_bitcasting_callback()); + AlgebraicSimplifierOptions options(non_bitcasting_callback()); + options.set_is_layout_sensitive(true); + AlgebraicSimplifier simplifier(options); EXPECT_FALSE(simplifier.Run(m.get()).ValueOrDie()); // Copy has not been removed. - EXPECT_THAT(computation->root_instruction(), op::Copy(param0)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Copy(m::Parameter(0)))); } // Test that a simplification which preserves layouts is performed if layout @@ -1529,10 +1726,12 @@ TEST_F(AlgebraicSimplifierTest, CopyWithSameLayout) { *param0->mutable_shape()->mutable_layout() = LayoutUtil::MakeLayout({0, 1}); *copy->mutable_shape()->mutable_layout() = LayoutUtil::MakeLayout({0, 1}); - EXPECT_THAT(computation->root_instruction(), op::Copy(param0)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Copy(m::Parameter(0)))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/true, - non_bitcasting_callback()); + AlgebraicSimplifierOptions options(non_bitcasting_callback()); + options.set_is_layout_sensitive(true); + AlgebraicSimplifier simplifier(options); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); // Copy has been removed. @@ -1557,14 +1756,17 @@ TEST_F(AlgebraicSimplifierTest, NoBitcastAdded) { auto computation = m->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Reshape(param0)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Reshape(m::Parameter(0)))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/true, - non_bitcasting_callback()); + AlgebraicSimplifierOptions options(non_bitcasting_callback()); + options.set_is_layout_sensitive(true); + AlgebraicSimplifier simplifier(options); EXPECT_FALSE(simplifier.Run(m.get()).ValueOrDie()); // Reshape is not replaced with a bitcast. - EXPECT_THAT(computation->root_instruction(), op::Reshape(param0)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Reshape(m::Parameter(0)))); } // Test transforming reshapes and transposes of rng. @@ -1588,13 +1790,13 @@ TEST_F(AlgebraicSimplifierTest, ReshapeOfTransposeOfRngToRng) { auto computation = m->AddEntryComputation(builder.Build()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - bitcasting_callback()); + AlgebraicSimplifier simplifier( + (AlgebraicSimplifierOptions(bitcasting_callback()))); EXPECT_TRUE(simplifier.Run(m.get()).ValueOrDie()); - // Verify that that reshape(transpose(rng)) is replace by a single rng of the + // Verify that reshape(transpose(rng)) is replace by a single rng of the // same shape as the reshape. - EXPECT_THAT(computation->root_instruction(), op::Rng()); + EXPECT_THAT(computation->root_instruction(), GmockMatch(m::Rng())); EXPECT_TRUE(ShapeUtil::Equal(computation->root_instruction()->shape(), reshape_shape)); } @@ -1636,17 +1838,20 @@ TEST_F(AlgebraicSimplifierTest, ReshapeReplacedWithBitcast) { auto computation = m->AddEntryComputation(builder.Build()); EXPECT_THAT(computation->root_instruction(), - op::Tuple(transformable_reshape, dimensions_wrong_reshape, - layout_wrong_reshape)); + GmockMatch(m::Tuple(m::Op().Is(transformable_reshape), + m::Op().Is(dimensions_wrong_reshape), + m::Op().Is(layout_wrong_reshape)))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/true, - bitcasting_callback()); + AlgebraicSimplifierOptions options(bitcasting_callback()); + options.set_is_layout_sensitive(true); + AlgebraicSimplifier simplifier(options); simplifier.Run(m.get()).ValueOrDie(); // Verify that only the first reshape is replaced. EXPECT_THAT( computation->root_instruction(), - op::Tuple(op::Bitcast(), dimensions_wrong_reshape, layout_wrong_reshape)); + GmockMatch(m::Tuple(m::Bitcast(), m::Op().Is(dimensions_wrong_reshape), + m::Op().Is(layout_wrong_reshape)))); } // Regression test for a bug where if we failed to sink a reshape, we'd set the @@ -1667,8 +1872,8 @@ TEST_F(AlgebraicSimplifierTest, FailureToSinkReshapeDoesntAffectChangedBit) { builder.AddInstruction( HloInstruction::CreateReshape(ShapeUtil::MakeShape(F32, {4}), add)); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - bitcasting_callback()); + AlgebraicSimplifier simplifier( + (AlgebraicSimplifierOptions(bitcasting_callback()))); m->AddEntryComputation(builder.Build()); EXPECT_TRUE(simplifier.Run(m.get()).ValueOrDie()); } @@ -1692,8 +1897,8 @@ TEST_F(AlgebraicSimplifierTest, FailureToSinkBroadcastDoesntAffectChangedBit) { HloInstruction::CreateBroadcast(ShapeUtil::MakeShape(F32, {2, 2, 2}), add, /*broadcast_dimensions=*/{0, 1})); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - bitcasting_callback()); + AlgebraicSimplifier simplifier( + (AlgebraicSimplifierOptions(bitcasting_callback()))); m->AddEntryComputation(builder.Build()); EXPECT_TRUE(simplifier.Run(m.get()).ValueOrDie()); } @@ -1715,14 +1920,17 @@ TEST_F(AlgebraicSimplifierTest, TransposeEqualsBitcast1) { auto computation = m->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Transpose(param)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Transpose(m::Parameter(0)))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/true, - bitcasting_callback()); + AlgebraicSimplifierOptions options(bitcasting_callback()); + options.set_is_layout_sensitive(true); + AlgebraicSimplifier simplifier(options); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); // Verify that the reshape is replaced. - EXPECT_THAT(computation->root_instruction(), op::Bitcast(param)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Bitcast(m::Parameter(0)))); } TEST_F(AlgebraicSimplifierTest, TransposeEqualsBitcast2) { @@ -1742,14 +1950,17 @@ TEST_F(AlgebraicSimplifierTest, TransposeEqualsBitcast2) { auto computation = m->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Transpose(param)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Transpose(m::Parameter(0)))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/true, - bitcasting_callback()); + AlgebraicSimplifierOptions options(bitcasting_callback()); + options.set_is_layout_sensitive(true); + AlgebraicSimplifier simplifier(options); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); // Verify that the reshape is replaced. - EXPECT_THAT(computation->root_instruction(), op::Bitcast(param)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Bitcast(m::Parameter(0)))); } TEST_F(AlgebraicSimplifierTest, ReshapesMerged) { @@ -1769,13 +1980,13 @@ TEST_F(AlgebraicSimplifierTest, ReshapesMerged) { auto computation = m->AddEntryComputation(builder.Build()); EXPECT_THAT(computation->root_instruction(), - op::Reshape(op::Reshape(param0))); + GmockMatch(m::Reshape(m::Reshape(m::Parameter(0))))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); - EXPECT_THAT(computation->root_instruction(), op::Reshape(param0)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Reshape(m::Parameter(0)))); } TEST_F(AlgebraicSimplifierTest, CopiesMerged) { @@ -1796,13 +2007,16 @@ TEST_F(AlgebraicSimplifierTest, CopiesMerged) { auto computation = m->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Copy(op::Copy(param0))); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Copy(m::Copy(m::Parameter(0))))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/true, - non_bitcasting_callback()); + AlgebraicSimplifierOptions options(non_bitcasting_callback()); + options.set_is_layout_sensitive(true); + AlgebraicSimplifier simplifier(options); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); - EXPECT_THAT(computation->root_instruction(), op::Copy(param0)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Copy(m::Parameter(0)))); } TEST_F(AlgebraicSimplifierTest, TransposesMerged) { @@ -1821,13 +2035,14 @@ TEST_F(AlgebraicSimplifierTest, TransposesMerged) { auto computation = m->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Transpose(transpose1)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Transpose(m::Op().Is(transpose1)))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); - EXPECT_THAT(computation->root_instruction(), op::Transpose(param0)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Transpose(m::Parameter(0)))); EXPECT_EQ(std::vector({2, 1, 0}), computation->root_instruction()->dimensions()); } @@ -1846,13 +2061,13 @@ TEST_F(AlgebraicSimplifierTest, ReshapeAndBroadcastMerged) { auto computation = m->AddEntryComputation(builder.Build()); EXPECT_THAT(computation->root_instruction(), - op::Broadcast(op::Reshape(param0))); + GmockMatch(m::Broadcast(m::Reshape(m::Parameter(0))))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); - EXPECT_THAT(computation->root_instruction(), op::Broadcast(param0)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Broadcast(m::Parameter(0)))); } // Test merging broadcast and reshape. @@ -1869,13 +2084,13 @@ TEST_F(AlgebraicSimplifierTest, BroadcastAndReshapeMerged) { auto computation = m->AddEntryComputation(builder.Build()); EXPECT_THAT(computation->root_instruction(), - op::Reshape(op::Broadcast(param0))); + GmockMatch(m::Reshape(m::Broadcast(m::Parameter(0))))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); - EXPECT_THAT(computation->root_instruction(), op::Broadcast(param0)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Broadcast(m::Parameter(0)))); } TEST_F(AlgebraicSimplifierTest, BroadcastAndReshape_1_3x1_3) { @@ -1891,14 +2106,13 @@ TEST_F(AlgebraicSimplifierTest, BroadcastAndReshape_1_3x1_3) { auto computation = m->AddEntryComputation(builder.Build()); EXPECT_THAT(computation->root_instruction(), - op::Reshape(op::Broadcast(param))); + GmockMatch(m::Reshape(m::Broadcast(m::Parameter(0))))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); EXPECT_FALSE(simplifier.Run(m.get()).ValueOrDie()); EXPECT_THAT(computation->root_instruction(), - op::Reshape(op::Broadcast(param))); + GmockMatch(m::Reshape(m::Broadcast(m::Parameter(0))))); } TEST_F(AlgebraicSimplifierTest, BroadcastAndReshape_4_3x2x4_6x1x1x4) { @@ -1914,13 +2128,13 @@ TEST_F(AlgebraicSimplifierTest, BroadcastAndReshape_4_3x2x4_6x1x1x4) { HloComputation* computation = m->AddEntryComputation(builder.Build()); EXPECT_THAT(computation->root_instruction(), - op::Reshape(op::Broadcast(param))); + GmockMatch(m::Reshape(m::Broadcast(m::Parameter(0))))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); - EXPECT_THAT(computation->root_instruction(), op::Broadcast(param)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Broadcast(m::Parameter(0)))); EXPECT_THAT(computation->root_instruction()->dimensions(), ::testing::ElementsAre(3)); } @@ -1938,13 +2152,13 @@ TEST_F(AlgebraicSimplifierTest, BroadcastAndReshape_1_3x2x1_6x1x1x1) { HloComputation* computation = m->AddEntryComputation(builder.Build()); EXPECT_THAT(computation->root_instruction(), - op::Reshape(op::Broadcast(param))); + GmockMatch(m::Reshape(m::Broadcast(m::Parameter(0))))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); - EXPECT_THAT(computation->root_instruction(), op::Broadcast(param)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Broadcast(m::Parameter(0)))); const std::vector broadcast_dims = computation->root_instruction()->dimensions(); EXPECT_EQ(1, broadcast_dims.size()); @@ -1964,14 +2178,13 @@ TEST_F(AlgebraicSimplifierTest, BroadcastAndReshape_4_3x2x4x2_6x8) { HloComputation* computation = m->AddEntryComputation(builder.Build()); EXPECT_THAT(computation->root_instruction(), - op::Reshape(op::Broadcast(param))); + GmockMatch(m::Reshape(m::Broadcast(m::Parameter(0))))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); EXPECT_FALSE(simplifier.Run(m.get()).ValueOrDie()); EXPECT_THAT(computation->root_instruction(), - op::Reshape(op::Broadcast(param))); + GmockMatch(m::Reshape(m::Broadcast(m::Parameter(0))))); } TEST_F(AlgebraicSimplifierTest, IotaAndReshapeMerged) { @@ -1984,13 +2197,13 @@ TEST_F(AlgebraicSimplifierTest, IotaAndReshapeMerged) { auto computation = m->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Reshape(op::Iota())); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Reshape(m::Iota()))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); - EXPECT_THAT(computation->root_instruction(), op::Iota()); + EXPECT_THAT(computation->root_instruction(), GmockMatch(m::Iota())); EXPECT_TRUE( ShapeUtil::Equal(computation->root_instruction()->shape(), result_shape)); } @@ -2004,14 +2217,13 @@ TEST_F(AlgebraicSimplifierTest, IotaEffectiveScalar) { auto computation = m->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Iota()); + EXPECT_THAT(computation->root_instruction(), GmockMatch(m::Iota())); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); auto root = computation->root_instruction(); - EXPECT_THAT(root, op::Broadcast(op::Constant())); + EXPECT_THAT(root, GmockMatch(m::Broadcast(m::Constant()))); EXPECT_EQ(0.0f, root->operand(0)->literal().GetFirstElement()); EXPECT_TRUE( ShapeUtil::Equal(computation->root_instruction()->shape(), result_shape)); @@ -2027,13 +2239,14 @@ TEST_F(AlgebraicSimplifierTest, IotaAndReshape_1_3x2_6) { auto computation = m->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Reshape(op::Iota())); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Reshape(m::Iota()))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); EXPECT_FALSE(simplifier.Run(m.get()).ValueOrDie()); - EXPECT_THAT(computation->root_instruction(), op::Reshape(op::Iota())); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Reshape(m::Iota()))); } TEST_F(AlgebraicSimplifierTest, IotaAndReshape_4_3x2x4_6x1x1x4) { @@ -2046,13 +2259,13 @@ TEST_F(AlgebraicSimplifierTest, IotaAndReshape_4_3x2x4_6x1x1x4) { HloComputation* computation = m->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Reshape(op::Iota())); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Reshape(m::Iota()))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); - EXPECT_THAT(computation->root_instruction(), op::Iota()); + EXPECT_THAT(computation->root_instruction(), GmockMatch(m::Iota())); EXPECT_EQ(Cast(computation->root_instruction()) ->iota_dimension(), 3); @@ -2068,13 +2281,13 @@ TEST_F(AlgebraicSimplifierTest, IotaAndReshape_1_3x2x2_6x1x1x2) { HloComputation* computation = m->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Reshape(op::Iota())); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Reshape(m::Iota()))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); - EXPECT_THAT(computation->root_instruction(), op::Iota()); + EXPECT_THAT(computation->root_instruction(), GmockMatch(m::Iota())); const int64 iota_dim = Cast(computation->root_instruction()) ->iota_dimension(); @@ -2091,13 +2304,14 @@ TEST_F(AlgebraicSimplifierTest, IotaAndReshape_4_3x2x4x2_6x8) { HloComputation* computation = m->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Reshape(op::Iota())); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Reshape(m::Iota()))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); EXPECT_FALSE(simplifier.Run(m.get()).ValueOrDie()); - EXPECT_THAT(computation->root_instruction(), op::Reshape(op::Iota())); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Reshape(m::Iota()))); } TEST_F(AlgebraicSimplifierTest, RemoveNoopPad) { @@ -2120,10 +2334,10 @@ TEST_F(AlgebraicSimplifierTest, RemoveNoopPad) { auto module = CreateNewVerifiedModule(); HloComputation* computation = module->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Pad(param, zero)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Pad(m::Parameter(0), m::Op().Is(zero)))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(module.get()).ValueOrDie()); EXPECT_THAT(computation->root_instruction(), param); @@ -2153,8 +2367,7 @@ TEST_F(AlgebraicSimplifierTest, NegativePadding) { auto module = CreateNewVerifiedModule(); HloComputation* computation = module->AddEntryComputation(builder.Build()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); auto has_negative_padding = [](const HloInstruction* pad) { for (auto& padding_dimension : pad->padding_config().dimensions()) { @@ -2166,16 +2379,54 @@ TEST_F(AlgebraicSimplifierTest, NegativePadding) { return false; }; - EXPECT_THAT(computation->root_instruction(), op::Pad(param, zero)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Pad(m::Parameter(0), m::Op().Is(zero)))); EXPECT_TRUE(has_negative_padding(pad)); ASSERT_TRUE(simplifier.Run(module.get()).ValueOrDie()); - EXPECT_THAT(computation->root_instruction(), op::Slice(op::Pad(param, zero))); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Slice(m::Pad(m::Parameter(0), m::Op().Is(zero))))); EXPECT_FALSE( has_negative_padding(computation->root_instruction()->operand(0))); } +TEST_F(AlgebraicSimplifierTest, TrivialInteriorPadding) { + // Verify that a pad instruction with interior padding on one-sized + // dimensions, removes the interior padding. + HloComputation::Builder builder(TestName()); + HloInstruction* param = + builder.AddInstruction(HloInstruction::CreateParameter( + 0, ShapeUtil::MakeShape(F32, {2, 1}), "param")); + HloInstruction* zero = builder.AddInstruction( + HloInstruction::CreateConstant(LiteralUtil::CreateR0(0.0f))); + PaddingConfig padding; + for (int i = 0; i < 2; ++i) { + auto dimension = padding.add_dimensions(); + dimension->set_edge_padding_low(3); + dimension->set_edge_padding_high(3); + dimension->set_interior_padding(i * 3); + } + HloInstruction* pad = builder.AddInstruction(HloInstruction::CreatePad( + ShapeUtil::MakeShape(F32, {8, 7}), param, zero, padding)); + + auto module = CreateNewVerifiedModule(); + HloComputation* computation = module->AddEntryComputation(builder.Build()); + + AlgebraicSimplifier simplifier(default_options_); + + ASSERT_THAT(computation->root_instruction(), + GmockMatch(m::Pad(m::Parameter(0), m::Op().Is(zero)))); + ASSERT_TRUE(HasInteriorPadding(pad->padding_config())); + + EXPECT_TRUE(simplifier.Run(module.get()).ValueOrDie()); + + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Pad(m::Parameter(0), m::Op().Is(zero)))); + EXPECT_FALSE( + HasInteriorPadding(computation->root_instruction()->padding_config())); +} + TEST_F(AlgebraicSimplifierTest, RemoveNoopReshape) { HloComputation::Builder builder(TestName()); HloInstruction* param = @@ -2187,10 +2438,10 @@ TEST_F(AlgebraicSimplifierTest, RemoveNoopReshape) { auto module = CreateNewVerifiedModule(); HloComputation* computation = module->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Reshape(param)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Reshape(m::Parameter(0)))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(module.get()).ValueOrDie()); EXPECT_THAT(computation->root_instruction(), param); @@ -2210,10 +2461,10 @@ TEST_F(AlgebraicSimplifierTest, RemoveNoopSlice) { auto module = CreateNewVerifiedModule(); HloComputation* computation = module->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Slice(param)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Slice(m::Parameter(0)))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(module.get()).ValueOrDie()); EXPECT_THAT(computation->root_instruction(), param); @@ -2239,13 +2490,14 @@ TEST_F(AlgebraicSimplifierTest, SliceOfSliceToSlice) { auto module = CreateNewVerifiedModule(); HloComputation* computation = module->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Slice(op::Slice(param))); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Slice(m::Slice(m::Parameter(0))))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(module.get()).ValueOrDie()); - EXPECT_THAT(computation->root_instruction(), op::Slice(param)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Slice(m::Parameter(0)))); EXPECT_EQ(computation->root_instruction()->slice_starts(0), 3); EXPECT_EQ(computation->root_instruction()->slice_starts(1), 5); EXPECT_EQ(computation->root_instruction()->slice_limits(0), dim0 - 2); @@ -2271,13 +2523,14 @@ TEST_F(AlgebraicSimplifierTest, SliceOfReshapeToReshapeOfSlice) { auto module = CreateNewVerifiedModule(); HloComputation* computation = module->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Slice(op::Reshape(param))); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Slice(m::Reshape(m::Parameter(0))))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(module.get()).ValueOrDie()); - EXPECT_THAT(computation->root_instruction(), op::Reshape(op::Slice(param))); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Reshape(m::Slice(m::Parameter(0))))); } TEST_F(AlgebraicSimplifierTest, SliceOfReshapeUnchanged) { @@ -2296,10 +2549,10 @@ TEST_F(AlgebraicSimplifierTest, SliceOfReshapeUnchanged) { auto module = CreateNewVerifiedModule(); HloComputation* computation = module->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Slice(op::Reshape(param))); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Slice(m::Reshape(m::Parameter(0))))); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_FALSE(simplifier.Run(module.get()).ValueOrDie()); } @@ -2312,12 +2565,84 @@ TEST_F(AlgebraicSimplifierTest, RemoveNoopSort) { builder.AddInstruction(HloInstruction::CreateSort(keys_shape, 0, keys)); auto module = CreateNewVerifiedModule(); HloComputation* computation = module->AddEntryComputation(builder.Build()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(module.get()).ValueOrDie()); EXPECT_THAT(computation->root_instruction(), keys); } +TEST_F(AlgebraicSimplifierTest, ReplacePermutationSortWithScatter) { + const char* hlo_string = R"( + HloModule permutation_sort + + ENTRY sort_computation { + keys = f32[64,8732]{1,0} parameter(0) + values = s32[64,8732]{1,0} iota(), iota_dimension=1 + sort = (f32[64,8732]{1,0}, s32[64,8732]{1,0}) sort(keys, values), dimensions={1} + gte = s32[64,8732]{1,0} get-tuple-element(sort), index=1 + ROOT sort2 = (s32[64,8732]{1,0}, s32[64,8732]{1,0}) sort(gte, values), dimensions={1} + } + )"; + TF_ASSERT_OK_AND_ASSIGN(auto module, + ParseAndReturnVerifiedModule(hlo_string)); + + AlgebraicSimplifierOptions options(non_bitcasting_callback()); + options.set_enable_permutation_sort_replacement(true); + AlgebraicSimplifier simplifier(options); + EXPECT_TRUE(simplifier.Run(module.get()).ValueOrDie()); + auto root = module->entry_computation()->root_instruction(); + EXPECT_THAT(root, + GmockMatch(m::Tuple( + m::Iota(), + m::Scatter(m::Iota(), m::Concatenate(m::Iota(), m::Reshape()), + m::Reshape())))); +} + +TEST_F(AlgebraicSimplifierTest, DontReplacePermutationSortIfNonIntegral) { + // Same as ReplacePermutationSortWithScatter except that the iota has F32 + // type. + const char* hlo_string = R"( + HloModule permutation_sort + + ENTRY sort_computation { + keys = f32[64,8732]{1,0} parameter(0) + values = f32[64,8732]{1,0} iota(), iota_dimension=1 + sort = (f32[64,8732]{1,0}, f32[64,8732]{1,0}) sort(keys, values), dimensions={1} + gte = f32[64,8732]{1,0} get-tuple-element(sort), index=1 + ROOT sort2 = (f32[64,8732]{1,0}, f32[64,8732]{1,0}) sort(gte, values), dimensions={1} + } + )"; + TF_ASSERT_OK_AND_ASSIGN(auto module, + ParseAndReturnVerifiedModule(hlo_string)); + + AlgebraicSimplifierOptions options(non_bitcasting_callback()); + options.set_enable_permutation_sort_replacement(true); + AlgebraicSimplifier simplifier(options); + EXPECT_FALSE(simplifier.Run(module.get()).ValueOrDie()); +} + +TEST_F(AlgebraicSimplifierTest, DontReplacePermutationSortWrongDimensions) { + // Same as ReplacePermutationSortWithScatter except that the sort dimensions + // don't match. + const char* hlo_string = R"( + HloModule permutation_sort + + ENTRY sort_computation { + keys = f32[64,8732]{1,0} parameter(0) + values = s32[64,8732]{1,0} iota(), iota_dimension=1 + sort = (f32[64,8732]{1,0}, s32[64,8732]{1,0}) sort(keys, values), dimensions={1} + gte = s32[64,8732]{1,0} get-tuple-element(sort), index=1 + ROOT sort2 = (s32[64,8732]{1,0}, s32[64,8732]{1,0}) sort(gte, values), dimensions={0} + } + )"; + TF_ASSERT_OK_AND_ASSIGN(auto module, + ParseAndReturnVerifiedModule(hlo_string)); + + AlgebraicSimplifierOptions options(non_bitcasting_callback()); + options.set_enable_permutation_sort_replacement(true); + AlgebraicSimplifier simplifier(options); + EXPECT_FALSE(simplifier.Run(module.get()).ValueOrDie()); +} + TEST_F(AlgebraicSimplifierTest, ReplaceEffectiveScalarKeyValueSortWithTuple) { auto builder = HloComputation::Builder(TestName()); @@ -2334,11 +2659,11 @@ TEST_F(AlgebraicSimplifierTest, ReplaceEffectiveScalarKeyValueSortWithTuple) { keys, {values0, values1})); auto module = CreateNewVerifiedModule(); HloComputation* computation = module->AddEntryComputation(builder.Build()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(module.get()).ValueOrDie()); EXPECT_THAT(computation->root_instruction(), - op::Tuple(keys, values0, values1)); + GmockMatch(m::Tuple(m::Op().Is(keys), m::Op().Is(values0), + m::Op().Is(values1)))); } // Test that A && True is simplified to A @@ -2356,8 +2681,7 @@ TEST_F(AlgebraicSimplifierTest, AndTrue) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root->opcode(), HloOpcode::kAnd); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); EXPECT_EQ(root, param0); @@ -2378,8 +2702,7 @@ TEST_F(AlgebraicSimplifierTest, AndTrue2) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root->opcode(), HloOpcode::kAnd); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); EXPECT_EQ(root, param0); @@ -2400,8 +2723,7 @@ TEST_F(AlgebraicSimplifierTest, AndFalse) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root->opcode(), HloOpcode::kAnd); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); EXPECT_EQ(root, const_false); @@ -2422,8 +2744,7 @@ TEST_F(AlgebraicSimplifierTest, AndFalse2) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root->opcode(), HloOpcode::kAnd); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); EXPECT_EQ(root, const_false); @@ -2444,8 +2765,7 @@ TEST_F(AlgebraicSimplifierTest, OrTrue) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root->opcode(), HloOpcode::kOr); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); EXPECT_EQ(root, const_true); @@ -2466,8 +2786,7 @@ TEST_F(AlgebraicSimplifierTest, OrTrue2) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root->opcode(), HloOpcode::kOr); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); EXPECT_EQ(root, const_true); @@ -2488,8 +2807,7 @@ TEST_F(AlgebraicSimplifierTest, OrFalse) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root->opcode(), HloOpcode::kOr); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); EXPECT_EQ(root, param0); @@ -2510,8 +2828,7 @@ TEST_F(AlgebraicSimplifierTest, OrFalse2) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root->opcode(), HloOpcode::kOr); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); EXPECT_EQ(root, param0); @@ -2641,15 +2958,15 @@ TEST_P(ConvInputPaddingTest, DoTest) { auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); if (testcase.expected_conv_window.empty()) { ASSERT_FALSE(simplifier.Run(module.get()).ValueOrDie()); } else { ASSERT_TRUE(simplifier.Run(module.get()).ValueOrDie()); auto* conv = module->entry_computation()->root_instruction(); SCOPED_TRACE(module->ToString()); - ASSERT_THAT(conv, op::Convolution(op::Parameter(), op::Parameter())); + ASSERT_THAT(conv, + GmockMatch(m::Convolution(m::Parameter(), m::Parameter()))); EXPECT_EQ(window_util::ToString(conv->window()), absl::StrCat("size=3x3 ", testcase.expected_conv_window)); } @@ -2759,15 +3076,15 @@ TEST_P(ConvFilterPaddingTest, DoIt) { auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); if (testcase.expected_conv_window.empty()) { ASSERT_FALSE(simplifier.Run(module.get()).ValueOrDie()); } else { ASSERT_TRUE(simplifier.Run(module.get()).ValueOrDie()); auto* conv = module->entry_computation()->root_instruction(); SCOPED_TRACE(module->ToString()); - ASSERT_THAT(conv, op::Convolution(op::Parameter(), op::Parameter())); + ASSERT_THAT(conv, + GmockMatch(m::Convolution(m::Parameter(), m::Parameter()))); EXPECT_EQ(window_util::ToString(conv->window()), absl::StrFormat("size=%dx%d %s", conv->operand(1)->shape().dimensions(2), @@ -2908,8 +3225,9 @@ TEST_F(AlgebraicSimplifierTest, ConvertConvToMatmul) { auto module = CreateNewUnverifiedModule(); auto* computation = module->AddEntryComputation(b.Build()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/true, - bitcasting_callback()); + AlgebraicSimplifierOptions simplifier_options(bitcasting_callback()); + simplifier_options.set_is_layout_sensitive(true); + AlgebraicSimplifier simplifier(simplifier_options); if (!simplifier.Run(module.get()).ValueOrDie()) { return "NO_CHANGE"; } @@ -3032,17 +3350,15 @@ TEST_F(AlgebraicSimplifierTest, ScalarBroadcastToSlice) { EXPECT_EQ(root, slice); EXPECT_TRUE(ShapeUtil::Equal(root->shape(), slice_shape)); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(module.get()).ValueOrDie()); // Running simplification again should not result in any further changes. ASSERT_FALSE(simplifier.Run(module.get()).ValueOrDie()); - - root = computation->root_instruction(); - EXPECT_THAT(root, op::Broadcast(scalar_param)); - EXPECT_TRUE(ShapeUtil::Equal(root->shape(), slice_shape)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Broadcast(m::Op().Is(scalar_param)) + .WithShapeEqualTo(&slice_shape))); } // Test that reshape(transpose(broadcast(/*scalar value*/))) simplifies to a @@ -3071,13 +3387,11 @@ TEST_F(AlgebraicSimplifierTest, ScalarBroadcastToTransposeReshape) { EXPECT_EQ(root, reshape); EXPECT_TRUE(ShapeUtil::Equal(root->shape(), reshape_shape)); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(module.get()).ValueOrDie()); - - root = computation->root_instruction(); - EXPECT_THAT(root, op::Broadcast(forty_two)); - EXPECT_TRUE(ShapeUtil::Equal(root->shape(), reshape_shape)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Broadcast(m::Op().Is(forty_two)) + .WithShapeEqualTo(&reshape_shape))); } // Test that ReduceWindow(Pad(op, x), y) can simplify to ReduceWindow(op, x). @@ -3138,8 +3452,7 @@ TEST_F(AlgebraicSimplifierTest, FoldPadIntoReduceWindow) { auto computation = module->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root, reduce_window); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(module.get()).ValueOrDie()); // Running simplification again should not result in any further changes. @@ -3147,7 +3460,8 @@ TEST_F(AlgebraicSimplifierTest, FoldPadIntoReduceWindow) { // Verify the result root = computation->root_instruction(); - EXPECT_THAT(root, op::ReduceWindow(operand, op::Constant())); + EXPECT_THAT(root, + GmockMatch(m::ReduceWindow(m::Op().Is(operand), m::Constant()))); EXPECT_TRUE(ShapeUtil::Equal(root->shape(), reduce_window_shape)) << ShapeUtil::HumanString(root->shape()) << " vs " << ShapeUtil::HumanString(reduce_window_shape); @@ -3224,8 +3538,7 @@ TEST_F(AlgebraicSimplifierTest, FoldConvertedPadIntoReduceWindow) { auto computation = module->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root, reduce_window); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(module.get()).ValueOrDie()); // Running simplification again should not result in any further changes. @@ -3233,7 +3546,8 @@ TEST_F(AlgebraicSimplifierTest, FoldConvertedPadIntoReduceWindow) { // Verify the result root = computation->root_instruction(); - EXPECT_THAT(root, op::ReduceWindow(op::Convert(parameter), op::Constant())); + EXPECT_THAT(root, GmockMatch(m::ReduceWindow(m::Convert(m::Parameter(0)), + m::Constant()))); EXPECT_TRUE(ShapeUtil::Equal(root->shape(), reduce_window_shape)) << ShapeUtil::HumanString(root->shape()) << " vs " << ShapeUtil::HumanString(reduce_window_shape); @@ -3258,8 +3572,7 @@ TEST_F(AlgebraicSimplifierTest, ReversalOfTrivialDimensionsToBitcast) { auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(module.get()).ValueOrDie()); HloInstruction* root = computation->root_instruction(); @@ -3295,8 +3608,7 @@ TEST_F(AlgebraicSimplifierTest, IteratorInvalidation) { m->AddEmbeddedComputation(std::move(dot_computation)); m->AddEntryComputation(call_builder.Build()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); } @@ -3313,11 +3625,10 @@ TEST_F(AlgebraicSimplifierTest, ConstantTupleBecomesTupleOfConstants) { auto computation = m->AddEntryComputation(builder.Build()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); EXPECT_THAT(computation->root_instruction(), - op::Tuple(op::Constant(), op::Constant())); + GmockMatch(m::Tuple(m::Constant(), m::Constant()))); } // A dynamic-slice is trivial if its start indices are all zeroes and the size @@ -3337,10 +3648,9 @@ TEST_F(AlgebraicSimplifierTest, TrivialDynamicSlice) { /*slice_sizes=*/{10, 100, 1000})); auto computation = m->AddEntryComputation(builder.Build()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); - EXPECT_THAT(computation->root_instruction(), op::Parameter()); + EXPECT_THAT(computation->root_instruction(), GmockMatch(m::Parameter())); } // A dynamic-update-slice is trivial if its start indices are all zeroes and the @@ -3371,11 +3681,10 @@ TEST_F(AlgebraicSimplifierTest, TrivialDynamicUpdateSlice) { 3, ShapeUtil::MakeShape(U32, {3}), "update_indices")))); auto computation = m->AddEntryComputation(builder.Build()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); EXPECT_THAT(computation->root_instruction(), - op::DynamicSlice(op::Parameter(), op::Parameter())); + GmockMatch(m::DynamicSlice(m::Parameter(), m::Parameter()))); } // Test that two consecutive broadcasts can be merged to one. @@ -3394,11 +3703,10 @@ TEST_F(AlgebraicSimplifierTest, MergeBroadcasts) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root->opcode(), HloOpcode::kBroadcast); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); - EXPECT_THAT(root, op::Broadcast(op::Constant())); + EXPECT_THAT(root, GmockMatch(m::Broadcast(m::Constant()))); EXPECT_THAT(root->dimensions(), ElementsAre(2)); } @@ -3421,11 +3729,10 @@ TEST_F(AlgebraicSimplifierTest, MergeBroadcasts2) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root->opcode(), HloOpcode::kBroadcast); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); - EXPECT_THAT(root, op::Broadcast(op::Parameter(0))); + EXPECT_THAT(root, GmockMatch(m::Broadcast(m::Parameter(0)))); EXPECT_THAT(root->dimensions(), ElementsAre(1, 3)); } @@ -3442,11 +3749,10 @@ TEST_F(AlgebraicSimplifierTest, MergeBroadcastAndIota) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root->opcode(), HloOpcode::kBroadcast); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); - EXPECT_THAT(root, op::Iota()); + EXPECT_THAT(root, GmockMatch(m::Iota())); EXPECT_EQ(Cast(root)->iota_dimension(), 2); } @@ -3464,11 +3770,10 @@ TEST_F(AlgebraicSimplifierTest, MergeBroadcastAndIota2) { auto computation = m->AddEntryComputation(builder.Build()); HloInstruction* root = computation->root_instruction(); EXPECT_EQ(root->opcode(), HloOpcode::kBroadcast); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); root = computation->root_instruction(); - EXPECT_THAT(root, op::Iota()); + EXPECT_THAT(root, GmockMatch(m::Iota())); EXPECT_EQ(Cast(root)->iota_dimension(), 2); } @@ -3486,11 +3791,11 @@ TEST_F(AlgebraicSimplifierTest, SliceOfPadLow) { TF_ASSERT_OK_AND_ASSIGN(auto module, ParseAndReturnVerifiedModule(hlo_string)); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - bitcasting_callback()); + AlgebraicSimplifierOptions options(bitcasting_callback()); + AlgebraicSimplifier simplifier(options); EXPECT_TRUE(simplifier.Run(module.get()).ValueOrDie()); auto root = module->entry_computation()->root_instruction(); - EXPECT_THAT(root, op::Reshape(op::Constant())); + EXPECT_THAT(root, GmockMatch(m::Reshape(m::Constant()))); } TEST_F(AlgebraicSimplifierTest, SliceOfPadHigh) { @@ -3507,11 +3812,11 @@ TEST_F(AlgebraicSimplifierTest, SliceOfPadHigh) { TF_ASSERT_OK_AND_ASSIGN(auto module, ParseAndReturnVerifiedModule(hlo_string)); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - bitcasting_callback()); + AlgebraicSimplifierOptions options(bitcasting_callback()); + AlgebraicSimplifier simplifier(options); EXPECT_TRUE(simplifier.Run(module.get()).ValueOrDie()); auto root = module->entry_computation()->root_instruction(); - EXPECT_THAT(root, op::Reshape(op::Constant())); + EXPECT_THAT(root, GmockMatch(m::Reshape(m::Constant()))); } TEST_F(AlgebraicSimplifierTest, SliceOfPadMidNonScalar) { @@ -3528,8 +3833,8 @@ TEST_F(AlgebraicSimplifierTest, SliceOfPadMidNonScalar) { TF_ASSERT_OK_AND_ASSIGN(auto module, ParseAndReturnVerifiedModule(hlo_string)); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - bitcasting_callback()); + AlgebraicSimplifierOptions options(bitcasting_callback()); + AlgebraicSimplifier simplifier(options); EXPECT_FALSE(simplifier.Run(module.get()).ValueOrDie()); } @@ -3547,11 +3852,11 @@ TEST_F(AlgebraicSimplifierTest, SliceOfPadMidScalar) { TF_ASSERT_OK_AND_ASSIGN(auto module, ParseAndReturnVerifiedModule(hlo_string)); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - bitcasting_callback()); + AlgebraicSimplifierOptions options(bitcasting_callback()); + AlgebraicSimplifier simplifier(options); EXPECT_TRUE(simplifier.Run(module.get()).ValueOrDie()); auto root = module->entry_computation()->root_instruction(); - EXPECT_THAT(root, op::Parameter()); + EXPECT_THAT(root, GmockMatch(m::Parameter())); } TEST_F(AlgebraicSimplifierTest, SliceOfConcatScalarInput) { @@ -3569,11 +3874,11 @@ TEST_F(AlgebraicSimplifierTest, SliceOfConcatScalarInput) { TF_ASSERT_OK_AND_ASSIGN(auto module, ParseAndReturnVerifiedModule(hlo_string)); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - bitcasting_callback()); + AlgebraicSimplifierOptions options(bitcasting_callback()); + AlgebraicSimplifier simplifier(options); EXPECT_TRUE(simplifier.Run(module.get()).ValueOrDie()); auto root = module->entry_computation()->root_instruction(); - EXPECT_THAT(root, op::Parameter(1)); + EXPECT_THAT(root, GmockMatch(m::Parameter(1))); } TEST_F(AlgebraicSimplifierTest, SliceOfConcatNonScalarInput) { @@ -3591,11 +3896,11 @@ TEST_F(AlgebraicSimplifierTest, SliceOfConcatNonScalarInput) { TF_ASSERT_OK_AND_ASSIGN(auto module, ParseAndReturnVerifiedModule(hlo_string)); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - bitcasting_callback()); + AlgebraicSimplifierOptions options(bitcasting_callback()); + AlgebraicSimplifier simplifier(options); EXPECT_TRUE(simplifier.Run(module.get()).ValueOrDie()); auto root = module->entry_computation()->root_instruction(); - EXPECT_THAT(root, op::Slice(op::Parameter(2))); + EXPECT_THAT(root, GmockMatch(m::Slice(m::Parameter(2)))); EXPECT_EQ(root->slice_starts(0), 1); EXPECT_EQ(root->slice_limits(0), 2); } @@ -3613,11 +3918,11 @@ TEST_F(AlgebraicSimplifierTest, NegateNegate) { TF_ASSERT_OK_AND_ASSIGN(auto module, ParseAndReturnVerifiedModule(hlo_string)); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - bitcasting_callback()); + AlgebraicSimplifierOptions options(bitcasting_callback()); + AlgebraicSimplifier simplifier(options); EXPECT_TRUE(simplifier.Run(module.get()).ValueOrDie()); auto root = module->entry_computation()->root_instruction(); - EXPECT_THAT(root, op::Parameter(0)); + EXPECT_THAT(root, GmockMatch(m::Parameter(0))); } TEST_F(AlgebraicSimplifierTest, NotNot) { @@ -3633,11 +3938,11 @@ TEST_F(AlgebraicSimplifierTest, NotNot) { TF_ASSERT_OK_AND_ASSIGN(auto module, ParseAndReturnVerifiedModule(hlo_string)); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - bitcasting_callback()); + AlgebraicSimplifierOptions options(bitcasting_callback()); + AlgebraicSimplifier simplifier(options); EXPECT_TRUE(simplifier.Run(module.get()).ValueOrDie()); auto root = module->entry_computation()->root_instruction(); - EXPECT_THAT(root, op::Parameter(0)); + EXPECT_THAT(root, GmockMatch(m::Parameter(0))); } struct PadReduceWindowEffectiveBroadcastCase { @@ -3733,8 +4038,7 @@ TEST_P(PadReduceWindowEffectiveBroadcastTest, DoIt) { output_shape, pad, zero, window, add_computation)); auto computation = m->AddEntryComputation(builder.Build()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); TF_ASSERT_OK_AND_ASSIGN(bool run_successful, simplifier.Run(m.get())); ASSERT_TRUE(run_successful); @@ -3742,10 +4046,10 @@ TEST_P(PadReduceWindowEffectiveBroadcastTest, DoIt) { ShapeUtil::Equal(computation->root_instruction()->shape(), output_shape)); if (param.should_become_broadcast) { - EXPECT_THAT(computation->root_instruction(), op::Broadcast(::testing::_)); + EXPECT_THAT(computation->root_instruction(), GmockMatch(m::Broadcast())); } else { EXPECT_THAT(computation->root_instruction(), - op::ReduceWindow(::testing::_, zero)); + GmockMatch(m::ReduceWindow(m::Op(), m::Op().Is(zero)))); } } @@ -3815,8 +4119,7 @@ TEST_P(DotStrengthReductionTest, DotStrengthReduction) { builder.AddInstruction(HloInstruction::CreateDot( dot_shape, lhs, rhs, dot_dnums, DefaultPrecisionConfig(2))); auto computation = module->AddEntryComputation(builder.Build()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); TF_ASSERT_OK_AND_ASSIGN(bool changed, simplifier.Run(module.get())); const bool dot_should_be_transformed = m == 1 || k == 1 || n == 1; const bool computation_should_be_modified = @@ -3845,7 +4148,7 @@ struct DotOfConcatTestSpec { }; class DotOfConcatSimplificationTest - : public HloTestBase, + : public AlgebraicSimplifierTest, public ::testing::WithParamInterface {}; // Test that we transform @@ -3893,19 +4196,19 @@ TEST_P(DotOfConcatSimplificationTest, ConstantLHS) { dot_shape, lhs, rhs, dot_dnums, DefaultPrecisionConfig(2))); auto computation = m->AddEntryComputation(builder.Build()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); TF_ASSERT_OK_AND_ASSIGN(bool run_successful, simplifier.Run(m.get())); ASSERT_TRUE(run_successful); EXPECT_TRUE( ShapeUtil::Equal(computation->root_instruction()->shape(), dot_shape)); - auto match_dot_0 = op::Dot(op::Slice(op::Constant()), op::Parameter(0)); - auto match_dot_1 = op::Dot(op::Slice(op::Constant()), op::Parameter(1)); - auto match_dot_2 = op::Dot(op::Slice(op::Constant()), op::Parameter(2)); - EXPECT_THAT(computation->root_instruction(), - op::Add(op::Add(match_dot_0, match_dot_1), match_dot_2)); + auto match_dot_0 = m::Dot(m::Slice(m::Constant()), m::Parameter(0)); + auto match_dot_1 = m::Dot(m::Slice(m::Constant()), m::Parameter(1)); + auto match_dot_2 = m::Dot(m::Slice(m::Constant()), m::Parameter(2)); + EXPECT_THAT( + computation->root_instruction(), + GmockMatch(m::Add(m::Add(match_dot_0, match_dot_1), match_dot_2))); } // Test that we transform @@ -3958,20 +4261,20 @@ TEST_P(DotOfConcatSimplificationTest, ConstantRHS) { dot_shape, lhs, rhs, dot_dnums, DefaultPrecisionConfig(2))); auto computation = m->AddEntryComputation(builder.Build()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); TF_ASSERT_OK_AND_ASSIGN(bool run_successful, simplifier.Run(m.get())); ASSERT_TRUE(run_successful); EXPECT_TRUE( ShapeUtil::Equal(computation->root_instruction()->shape(), dot_shape)); - auto match_dot_0 = op::Dot(op::Parameter(0), op::Slice(op::Constant())); - auto match_dot_1 = op::Dot(op::Parameter(1), op::Slice(op::Constant())); - auto match_dot_2 = op::Dot(op::Parameter(2), op::Slice(op::Constant())); - auto match_dot_3 = op::Dot(op::Parameter(3), op::Slice(op::Constant())); - EXPECT_THAT(computation->root_instruction(), - op::Add(op::Add(op::Add(match_dot_0, match_dot_1), match_dot_2), - match_dot_3)); + auto match_dot_0 = m::Dot(m::Parameter(0), m::Slice(m::Constant())); + auto match_dot_1 = m::Dot(m::Parameter(1), m::Slice(m::Constant())); + auto match_dot_2 = m::Dot(m::Parameter(2), m::Slice(m::Constant())); + auto match_dot_3 = m::Dot(m::Parameter(3), m::Slice(m::Constant())); + EXPECT_THAT( + computation->root_instruction(), + GmockMatch(m::Add(m::Add(m::Add(match_dot_0, match_dot_1), match_dot_2), + match_dot_3))); } DotOfConcatTestSpec kDotOfConcatTestSpecs[] = { @@ -4000,8 +4303,7 @@ TEST_F(AlgebraicSimplifierTest, DynamicUpdateSliceZeroUpdate) { const HloComputation* const computation = m->AddEntryComputation(builder.Build()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); EXPECT_THAT(computation->root_instruction(), operand); } @@ -4021,7 +4323,7 @@ struct DotOfGatherTestSpec { }; class DotOfGatherSimplificationTest - : public HloTestBase, + : public AlgebraicSimplifierTest, public ::testing::WithParamInterface {}; // input: dot(DS(ctA), ctB)) @@ -4078,8 +4380,7 @@ TEST_P(DotOfGatherSimplificationTest, ConstantRHS) { dot_shape, ds, rhs, dot_dnums, DefaultPrecisionConfig(2))); auto computation = m->AddEntryComputation(builder.Build()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); TF_ASSERT_OK_AND_ASSIGN(bool run_successful, simplifier.Run(m.get())); ASSERT_TRUE(run_successful); EXPECT_TRUE( @@ -4090,8 +4391,8 @@ TEST_P(DotOfGatherSimplificationTest, ConstantRHS) { HloOpcode::kDynamicSlice); } else { EXPECT_THAT(computation->root_instruction(), - op::DynamicSlice(op::Dot(op::Constant(), op::Constant()), - op::Concatenate())); + GmockMatch(m::DynamicSlice(m::Dot(m::Constant(), m::Constant()), + m::Concatenate()))); } } @@ -4149,8 +4450,7 @@ TEST_P(DotOfGatherSimplificationTest, ConstantLHS) { dot_shape, lhs, ds, dot_dnums, DefaultPrecisionConfig(2))); auto computation = m->AddEntryComputation(builder.Build()); - AlgebraicSimplifier simplifier(/*is_layout_sensitive=*/false, - non_bitcasting_callback()); + AlgebraicSimplifier simplifier(default_options_); TF_ASSERT_OK_AND_ASSIGN(bool run_successful, simplifier.Run(m.get())); ASSERT_TRUE(run_successful); EXPECT_TRUE( @@ -4161,8 +4461,8 @@ TEST_P(DotOfGatherSimplificationTest, ConstantLHS) { HloOpcode::kDynamicSlice); } else { EXPECT_THAT(computation->root_instruction(), - op::DynamicSlice(op::Dot(op::Constant(), op::Constant()), - op::Concatenate())); + GmockMatch(m::DynamicSlice(m::Dot(m::Constant(), m::Constant()), + m::Concatenate()))); } } diff --git a/tensorflow/compiler/xla/service/ar_crs_combiner.cc b/tensorflow/compiler/xla/service/ar_crs_combiner.cc new file mode 100644 index 0000000000..c11452a6fb --- /dev/null +++ b/tensorflow/compiler/xla/service/ar_crs_combiner.cc @@ -0,0 +1,286 @@ +/* Copyright 2018 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/compiler/xla/service/ar_crs_combiner.h" + +#include +#include +#include + +#include "tensorflow/compiler/xla/literal.h" +#include "tensorflow/compiler/xla/literal_util.h" +#include "tensorflow/compiler/xla/service/call_graph.h" +#include "tensorflow/compiler/xla/service/hlo_computation.h" +#include "tensorflow/compiler/xla/service/hlo_instruction.h" +#include "tensorflow/compiler/xla/service/hlo_opcode.h" +#include "tensorflow/compiler/xla/service/pattern_matcher.h" +#include "tensorflow/compiler/xla/status_macros.h" +#include "tensorflow/compiler/xla/types.h" +#include "tensorflow/compiler/xla/xla_data.pb.h" + +namespace xla { + +namespace { + +namespace m = match; + +// If the argument instruction is a CRS in the sequence +// AR -> Convert -> Add -> CRS +// then return the AR in the sequence. +// TODO(b/117554291): Rewrite this to recognize more general patterns, +// not just the specific one of AR -> Add -> Convert -> CRS. +absl::optional MatchesArCrsPattern( + HloInstruction* instruction) { + HloInstruction *ar, *convert, *add, *crs; + if (Match(instruction, + m::CrossReplicaSum( + &crs, m::Add(&add, m::Op(), + m::Convert(&convert, + m::CrossReplicaSum(&ar, m::Op()))))) && + ar->users().size() == 1 && ar->shape().element_type() == BF16 && + convert->shape().element_type() == F32 && !crs->all_reduce_id()) { + return ar; + } + return absl::optional(); +} + +} // namespace + +absl::optional ArCrsCombiner::WhileFromBodyParameter( + HloInstruction* instruction) { + CHECK(HloOpcode::kParameter == instruction->opcode()); + HloComputation* computation = instruction->parent(); + auto caller_instructions = call_graph_->GetComputationCallers(computation); + if (caller_instructions.size() == 1) { + auto caller_instruction = caller_instructions[0]; + if (caller_instruction->opcode() == HloOpcode::kWhile) { + return caller_instruction; + } + } + return absl::optional(); +} + +std::vector ArCrsCombiner::GetAllTuples( + HloInstruction* instruction) { + if (instruction->opcode() == HloOpcode::kTuple) { + return {instruction}; + } + if (instruction->opcode() == HloOpcode::kDomain) { + return GetAllTuples(instruction->operands()[0]); + } + if (instruction->opcode() == HloOpcode::kParameter) { + auto maybe_while = WhileFromBodyParameter(instruction); + if (!maybe_while) { + return {}; + } + auto while_instr = *maybe_while; + auto init_tuples = GetAllTuples(while_instr->while_init()); + auto body_tuples = + GetAllTuples(while_instr->while_body()->root_instruction()); + if (init_tuples.empty() || body_tuples.empty()) { + return {}; + } + init_tuples.insert(init_tuples.end(), body_tuples.begin(), + body_tuples.end()); + return init_tuples; + } + if (instruction->opcode() == HloOpcode::kGetTupleElement) { + std::vector result_tuples; + for (auto tuple : GetAllTuples(instruction->operands()[0])) { + auto tmp_tuples = + GetAllTuples(tuple->mutable_operand(instruction->tuple_index())); + if (tmp_tuples.empty()) { + return {}; + } + result_tuples.insert(result_tuples.end(), tmp_tuples.begin(), + tmp_tuples.end()); + } + return result_tuples; + } + return {}; +} + +bool ArCrsCombiner::TupleElementsComputeSameValue( + HloInstruction* tuple_shaped_instruction, int64 i1, int64 i2, + absl::flat_hash_map* visited_pairs) { + auto tuples = GetAllTuples(tuple_shaped_instruction); + if (tuples.empty()) { + return false; + } + for (auto tuple : tuples) { + CHECK(tuple->opcode() == HloOpcode::kTuple); + if (!InstructionsComputeSameValue(tuple->mutable_operand(i1), + tuple->mutable_operand(i2), + visited_pairs)) { + return false; + } + } + return true; +} + +/* static */ +bool ArCrsCombiner::TestInstructionsComputeSameValue(HloInstruction* i1, + HloInstruction* i2) { + ArCrsCombiner combiner(/*num_spatial_partitions=*/2); + auto module = i1->parent()->parent(); + CHECK_EQ(module, i2->parent()->parent()); + combiner.call_graph_ = CallGraph::Build(module); + absl::flat_hash_map visited_pairs; + return combiner.InstructionsComputeSameValue(i1, i2, &visited_pairs); +} + +bool ArCrsCombiner::InstructionsComputeSameValue( + HloInstruction* i1, HloInstruction* i2, + absl::flat_hash_map* visited_pairs) { + if (i1 == i2) { + return true; + } + auto uid1 = i1->unique_id(); + auto uid2 = i2->unique_id(); + auto min_uid = std::min(uid1, uid2); + auto max_uid = std::max(uid1, uid2); + auto it = visited_pairs->find(min_uid); + if (it != visited_pairs->end() && max_uid == it->second) { + return true; + } + auto opcode1 = i1->opcode(); + auto operands1 = i1->operands(); + if (opcode1 != i2->opcode() || operands1.size() != i2->operands().size()) { + return false; + } + if (opcode1 == HloOpcode::kConstant || i1->IsCrossModuleAllReduce()) { + return i1->Identical( + *i2, + /*eq_operands=*/std::equal_to(), + /*eq_computations=*/std::equal_to(), + /*layout_sensitive=*/false); + } + visited_pairs->emplace(min_uid, max_uid); + for (int i = 0; i < operands1.size(); ++i) { + auto operand1 = operands1[i]; + auto operand2 = i2->operands()[i]; + if (!InstructionsComputeSameValue(operand1, operand2, visited_pairs)) { + return false; + } + } + if (opcode1 == HloOpcode::kGetTupleElement) { + if (i1->tuple_index() == i2->tuple_index()) { + return true; + } + return TupleElementsComputeSameValue(operands1[0], i1->tuple_index(), + i2->tuple_index(), visited_pairs); + } + return true; +} + +void ArCrsCombiner::GroupAllReducesById(HloModule* module) { + for (HloComputation* computation : module->MakeNonfusionComputations()) { + for (HloInstruction* instruction : computation->instructions()) { + auto ar = MatchesArCrsPattern(instruction); + if (ar) { + all_reduce_map_[*((*ar)->all_reduce_id())].push_back(*ar); + } + } + } +} + +void ArCrsCombiner::KeepProvablyEqualInstructionGroups() { + for (auto it : all_reduce_map_) { + auto instruction_vec = it.second; + CHECK_EQ(instruction_vec.size(), num_spatial_partitions_); + + auto instr_0 = instruction_vec[0]; + auto add_0 = instr_0->users()[0]->users()[0]; + CHECK(HloOpcode::kAdd == add_0->opcode()); + + for (int i = 1; i < instruction_vec.size(); ++i) { + auto instr_i = instruction_vec[i]; + auto add_i = instr_i->users()[0]->users()[0]; + CHECK(HloOpcode::kAdd == add_i->opcode()); + absl::flat_hash_map visited_pairs; + if (!InstructionsComputeSameValue(add_0, add_i, &visited_pairs)) { + all_reduce_map_.erase(it.first); + } + } + } +} + +StatusOr ArCrsCombiner::RewriteGraph() { + if (all_reduce_map_.empty()) { + return false; + } + + auto computation_is_addition = [](HloComputation* c) { + return c->instruction_count() == 3 && + Match(c->root_instruction(), m::Add(m::Parameter(), m::Parameter())); + }; + + for (auto it : all_reduce_map_) { + auto instruction_vec = it.second; + for (auto all_reduce : instruction_vec) { + auto parent_computation = all_reduce->parent(); + auto convert = all_reduce->users()[0]; + auto add = convert->users()[0]; + auto crs = add->users()[0]; + + if (!computation_is_addition(all_reduce->called_computations()[0]) || + !computation_is_addition(crs->called_computations()[0])) { + continue; + } + HloInstruction* other_summand = (add->operands()[0] == convert) + ? add->operands()[1] + : add->operands()[0]; + // Remove the AllReduce and replace the CRS with: + // AllReduce - (other_summand * (num_spatial_partitions_ - 1)) + TF_CHECK_OK( + all_reduce->ReplaceAllUsesWith(all_reduce->mutable_operand(0))); + crs->set_all_reduce_id(all_reduce->all_reduce_id()); + auto new_shape = crs->shape(); + HloInstruction* to_subtract; + if (num_spatial_partitions_ == 2) { + to_subtract = other_summand; + } else { + Literal partitions_minus_1_lit = Literal(new_shape); + partitions_minus_1_lit.PopulateWithValue( + num_spatial_partitions_ - 1); + auto partitions_minus_1_const = parent_computation->AddInstruction( + HloInstruction::CreateConstant(partitions_minus_1_lit.Clone())); + to_subtract = + parent_computation->AddInstruction(HloInstruction::CreateBinary( + new_shape, HloOpcode::kMultiply, other_summand, + partitions_minus_1_const)); + } + auto sub = + parent_computation->AddInstruction(HloInstruction::CreateBinary( + new_shape, HloOpcode::kSubtract, crs, to_subtract)); + TF_CHECK_OK(crs->ReplaceAllUsesWith(sub)); + TF_CHECK_OK(parent_computation->RemoveInstruction(all_reduce)); + } + } + + return true; +} + +StatusOr ArCrsCombiner::Run(HloModule* module) { + call_graph_ = CallGraph::Build(module); + + GroupAllReducesById(module); + + KeepProvablyEqualInstructionGroups(); + + return RewriteGraph(); +} + +} // namespace xla diff --git a/tensorflow/compiler/xla/service/ar_crs_combiner.h b/tensorflow/compiler/xla/service/ar_crs_combiner.h new file mode 100644 index 0000000000..f6a7ef76ec --- /dev/null +++ b/tensorflow/compiler/xla/service/ar_crs_combiner.h @@ -0,0 +1,88 @@ +/* Copyright 2018 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_COMPILER_XLA_SERVICE_AR_CRS_COMBINER_H_ +#define TENSORFLOW_COMPILER_XLA_SERVICE_AR_CRS_COMBINER_H_ + +#include "absl/container/flat_hash_map.h" +#include "absl/strings/string_view.h" +#include "tensorflow/compiler/xla/service/call_graph.h" +#include "tensorflow/compiler/xla/service/hlo_module.h" +#include "tensorflow/compiler/xla/service/hlo_pass_interface.h" +#include "tensorflow/compiler/xla/statusor.h" + +namespace xla { + +// Combine an AllReduce and a CrossReplicaSum when they are close to each other +// in the graph, to use an efficient CrossReplicaSum implementation that +// fully utilizes the interconnect bandwidth. +class ArCrsCombiner : public HloModulePass { + public: + ArCrsCombiner(int num_spatial_partitions) + : num_spatial_partitions_(num_spatial_partitions) {} + absl::string_view name() const override { return "ar-crs-combiner"; } + StatusOr Run(HloModule* module) override; + + // Helper method to allow testing of InstructionsComputeSameValue. + static bool TestInstructionsComputeSameValue(HloInstruction* i1, + HloInstruction* i2); + + private: + // If the passed instruction is a while parameter, and the while body is only + // called by a single while instruction, return the while instruction. + absl::optional WhileFromBodyParameter( + HloInstruction* instruction); + + // Returns a vector of tuple instructions. + // If all instructions that flow to "instruction" are tuples, return them. + // Otherwise, return an empty vector. + std::vector GetAllTuples(HloInstruction* instruction); + + // Checks whether two different elements in the same tuple compute the same + // value. + bool TupleElementsComputeSameValue( + HloInstruction* tuple_shaped_instruction, int64 i1, int64 i2, + absl::flat_hash_map* visited_pairs); + + // Returns whether the instructions i1 and i2 can be shown to evaluate to the + // same value. Handling WHILE requires recursion, which may cause us to visit + // the same instruction again. To avoid infinite loops, we pass a cache of + // visited instruction pairs. + bool InstructionsComputeSameValue( + HloInstruction* i1, HloInstruction* i2, + absl::flat_hash_map* visited_pairs); + + // Populates all_reduce_map_. + void GroupAllReducesById(HloModule* module); + + // Looks at each AllReduce group in all_reduce_map_, and keeps only the + // groups for which it's safe to move the AllReduce later in the HLO graph. + void KeepProvablyEqualInstructionGroups(); + + // Performs the graph rewrite that eliminates the early AllReduce and turns + // the later CRS into an AllReduce. + StatusOr RewriteGraph(); + + int num_spatial_partitions_; + + // Map from all-reduce ids to the all reduce instructions. + absl::flat_hash_map> all_reduce_map_; + + std::unique_ptr call_graph_; +}; + +} // namespace xla + +#endif // TENSORFLOW_COMPILER_XLA_SERVICE_AR_CRS_COMBINER_H_ diff --git a/tensorflow/compiler/xla/service/ar_crs_combiner_test.cc b/tensorflow/compiler/xla/service/ar_crs_combiner_test.cc new file mode 100644 index 0000000000..9d5eaf63cc --- /dev/null +++ b/tensorflow/compiler/xla/service/ar_crs_combiner_test.cc @@ -0,0 +1,415 @@ +/* Copyright 2018 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/compiler/xla/service/ar_crs_combiner.h" +#include "tensorflow/compiler/xla/service/hlo_matchers.h" +#include "tensorflow/compiler/xla/statusor.h" +#include "tensorflow/compiler/xla/tests/hlo_test_base.h" +#include "tensorflow/core/lib/core/status_test_util.h" + +namespace xla { +namespace { + +namespace op = xla::testing::opcode_matchers; + +class ArCrsCombinerTest : public HloTestBase {}; + +TEST_F(ArCrsCombinerTest, SameValueTestBasecase) { + const char* module_str = R"( +HloModule foobar + +ENTRY %entrycomp (p: f32[2,2]) -> (f32[2,2], f32[2,2]) { + %p = f32[2,2] parameter(0) + %constant.f32.1 = f32[2,2] constant(f32[2,2] {{1, 2}, {3, 4}}) + %constant.f32.2 = f32[2,2] constant(f32[2,2] {{1, 2}, {3, 4}}) + ROOT %tuple = (f32[2,2], f32[2,2]) tuple(%constant.f32.1, %constant.f32.2) +} +)"; + + TF_ASSERT_OK_AND_ASSIGN(std::unique_ptr module, + ParseAndReturnVerifiedModule(module_str)); + auto root_tuple = module->entry_computation()->root_instruction(); + auto i1 = root_tuple->operands()[0]; + auto i2 = root_tuple->operands()[1]; + EXPECT_FALSE(ArCrsCombiner::TestInstructionsComputeSameValue( + i1, module->entry_computation()->parameter_instruction(0))); + EXPECT_TRUE(ArCrsCombiner::TestInstructionsComputeSameValue(i1, i2)); +} + +TEST_F(ArCrsCombinerTest, SameValueTestNumOperands) { + const char* module_str = R"( +HloModule foobar + +ENTRY %entrycomp (p: f32[2,2]) -> ((f32[2,2]), (f32[2,2], f32[2,2])) { + %p = f32[2,2] parameter(0) + %constant.f32 = f32[2,2] constant(f32[2,2] {{1, 2}, {3, 4}}) + %tuple1 = (f32[2,2]) tuple(%constant.f32) + %tuple2 = (f32[2,2], f32[2,2]) tuple(%constant.f32, %constant.f32) + ROOT %tuple = ((f32[2,2]), (f32[2,2], f32[2,2])) tuple(%tuple1, %tuple2) +} +)"; + + TF_ASSERT_OK_AND_ASSIGN(std::unique_ptr module, + ParseAndReturnVerifiedModule(module_str)); + auto root_tuple = module->entry_computation()->root_instruction(); + auto i1 = root_tuple->operands()[0]; + auto i2 = root_tuple->operands()[1]; + EXPECT_FALSE(ArCrsCombiner::TestInstructionsComputeSameValue(i1, i2)); +} + +TEST_F(ArCrsCombinerTest, SameValueTestTupleElementSameIndex) { + const char* module_str = R"( +HloModule foobar + +ENTRY %entrycomp (p: f32[2,2]) -> (f32[2,2], f32[2,2]) { + %p = f32[2,2] parameter(0) + %constant.f32 = f32[2,2] constant(f32[2,2] {{1, 2}, {3, 4}}) + %tuple.1 = (f32[2,2], f32[2,2]) tuple(%constant.f32, %constant.f32) + %get-tuple-element.1 = f32[2,2] get-tuple-element(%tuple.1), index=0 + %get-tuple-element.2 = f32[2,2] get-tuple-element(%tuple.1), index=0 + ROOT %tuple = (f32[2,2], f32[2,2]) tuple(%get-tuple-element.1, %get-tuple-element.2) +} +)"; + + TF_ASSERT_OK_AND_ASSIGN(std::unique_ptr module, + ParseAndReturnVerifiedModule(module_str)); + auto root_tuple = module->entry_computation()->root_instruction(); + auto i1 = root_tuple->operands()[0]; + auto i2 = root_tuple->operands()[1]; + EXPECT_TRUE(ArCrsCombiner::TestInstructionsComputeSameValue(i1, i2)); +} + +TEST_F(ArCrsCombinerTest, SameValueTestTupleElementDifferentIndex1) { + const char* module_str = R"( +HloModule foobar + +ENTRY %entrycomp (p: f32[2,2]) -> (f32[2,2], f32[2,2]) { + %p = f32[2,2] parameter(0) + %constant.f32 = f32[2,2] constant(f32[2,2] {{1, 2}, {3, 4}}) + %tuple.1 = (f32[2,2], f32[2,2]) tuple(%constant.f32, %constant.f32) + %get-tuple-element.1 = f32[2,2] get-tuple-element(%tuple.1), index=0 + %get-tuple-element.2 = f32[2,2] get-tuple-element(%tuple.1), index=1 + ROOT %tuple = (f32[2,2], f32[2,2]) tuple(%get-tuple-element.1, %get-tuple-element.2) +} +)"; + + TF_ASSERT_OK_AND_ASSIGN(std::unique_ptr module, + ParseAndReturnVerifiedModule(module_str)); + auto root_tuple = module->entry_computation()->root_instruction(); + auto i1 = root_tuple->operands()[0]; + auto i2 = root_tuple->operands()[1]; + EXPECT_TRUE(ArCrsCombiner::TestInstructionsComputeSameValue(i1, i2)); +} + +TEST_F(ArCrsCombinerTest, SameValueTestTupleElementDifferentIndex2) { + const char* module_str = R"( +HloModule foobar + +ENTRY %entrycomp (p: f32[2,2]) -> (f32[2,2], f32[2,2]) { + %p = f32[2,2] parameter(0) + %constant.f32.1 = f32[2,2] constant(f32[2,2] {{1, 2}, {3, 4}}) + %constant.f32.2 = f32[2,2] constant(f32[2,2] {{2, 3}, {4, 5}}) + %tuple.1 = (f32[2,2], f32[2,2]) tuple(%constant.f32.1, %constant.f32.2) + %get-tuple-element.1 = f32[2,2] get-tuple-element(%tuple.1), index=0 + %get-tuple-element.2 = f32[2,2] get-tuple-element(%tuple.1), index=1 + ROOT %tuple = (f32[2,2], f32[2,2]) tuple(%get-tuple-element.1, %get-tuple-element.2) +} +)"; + + TF_ASSERT_OK_AND_ASSIGN(std::unique_ptr module, + ParseAndReturnVerifiedModule(module_str)); + auto root_tuple = module->entry_computation()->root_instruction(); + auto i1 = root_tuple->operands()[0]; + auto i2 = root_tuple->operands()[1]; + EXPECT_FALSE(ArCrsCombiner::TestInstructionsComputeSameValue(i1, i2)); +} + +TEST_F(ArCrsCombinerTest, SameValueTestWhile1) { + const char* module_str = R"( +HloModule foobar + +%condition (x: (f32[2,2], f32[2,2])) -> pred[] { + %x = (f32[2,2], f32[2,2]) parameter(0) + %constant.0 = s32[] constant(0) + %constant.1 = s32[] constant(1) + ROOT %greater-than = pred[] greater-than(s32[] %constant.1, s32[] %constant.0) +} + +%body (x: (f32[2,2], f32[2,2])) -> (f32[2,2], f32[2,2]) { + %x = (f32[2,2], f32[2,2]) parameter(0) + %constant.f32 = f32[2,2] constant(f32[2,2] {{1, 2}, {3, 4}}) + %get-tuple-element.1 = f32[2,2] get-tuple-element(%x), index=0 + %get-tuple-element.2 = f32[2,2] get-tuple-element(%x), index=1 + %add.1 = f32[2,2] add(%get-tuple-element.1, %constant.f32) + %add.2 = f32[2,2] add(%get-tuple-element.2, %constant.f32) + ROOT %tuple = (f32[2,2], f32[2,2]) tuple(%add.1, %add.2) +} + +ENTRY %WhileLoop () -> (f32[2,2], f32[2,2]) { + %constant.f32 = f32[2,2] constant(f32[2,2] {{3, 4}, {5, 6}}) + %init.tuple = (f32[2,2], f32[2,2]) tuple(%constant.f32, %constant.f32) + ROOT %while = (f32[2,2], f32[2,2]) while(%init.tuple), condition=%condition, body=%body +} +)"; + + TF_ASSERT_OK_AND_ASSIGN(std::unique_ptr module, + ParseAndReturnVerifiedModule(module_str)); + auto root_while = module->entry_computation()->root_instruction(); + auto body_tuple = root_while->while_body()->root_instruction(); + auto i1 = body_tuple->operands()[0]; + auto i2 = body_tuple->operands()[1]; + EXPECT_TRUE(ArCrsCombiner::TestInstructionsComputeSameValue(i1, i2)); +} + +TEST_F(ArCrsCombinerTest, SameValueTestWhile2) { + const char* module_str = R"( +HloModule foobar + +%condition (x: (f32[2,2], f32[2,2])) -> pred[] { + %x = (f32[2,2], f32[2,2]) parameter(0) + %constant.0 = s32[] constant(0) + %constant.1 = s32[] constant(1) + ROOT %greater-than = pred[] greater-than(s32[] %constant.1, s32[] %constant.0) +} + +%body (x: (f32[2,2], f32[2,2])) -> (f32[2,2], f32[2,2]) { + %x = (f32[2,2], f32[2,2]) parameter(0) + %constant.f32 = f32[2,2] constant(f32[2,2] {{1, 2}, {3, 4}}) + %get-tuple-element.1 = f32[2,2] get-tuple-element(%x), index=0 + %get-tuple-element.2 = f32[2,2] get-tuple-element(%x), index=1 + %add.1 = f32[2,2] add(%get-tuple-element.1, %constant.f32) + %add.2 = f32[2,2] add(%get-tuple-element.2, %constant.f32) + ROOT %tuple = (f32[2,2], f32[2,2]) tuple(%add.1, %add.2) +} + +ENTRY %WhileLoop () -> (f32[2,2], f32[2,2]) { + %constant.f32.1 = f32[2,2] constant(f32[2,2] {{3, 4}, {5, 6}}) + %constant.f32.2 = f32[2,2] constant(f32[2,2] {{3, 4}, {7, 8}}) + %init.tuple = (f32[2,2], f32[2,2]) tuple(%constant.f32.1, %constant.f32.2) + ROOT %while = (f32[2,2], f32[2,2]) while(%init.tuple), condition=%condition, body=%body +} +)"; + + TF_ASSERT_OK_AND_ASSIGN(std::unique_ptr module, + ParseAndReturnVerifiedModule(module_str)); + auto root_while = module->entry_computation()->root_instruction(); + auto body_tuple = root_while->while_body()->root_instruction(); + auto i1 = body_tuple->operands()[0]; + auto i2 = body_tuple->operands()[1]; + EXPECT_FALSE(ArCrsCombiner::TestInstructionsComputeSameValue(i1, i2)); +} + +TEST_F(ArCrsCombinerTest, SameValueTestWhile3) { + const char* module_str = R"( +HloModule foobar + +%condition (x: (f32[2,2], f32[2,2])) -> pred[] { + %x = (f32[2,2], f32[2,2]) parameter(0) + %constant.0 = s32[] constant(0) + %constant.1 = s32[] constant(1) + ROOT %greater-than = pred[] greater-than(s32[] %constant.1, s32[] %constant.0) +} + +%body (x: (f32[2,2], f32[2,2])) -> (f32[2,2], f32[2,2]) { + %x = (f32[2,2], f32[2,2]) parameter(0) + %constant.f32.1 = f32[2,2] constant(f32[2,2] {{1, 2}, {3, 4}}) + %constant.f32.2 = f32[2,2] constant(f32[2,2] {{3, 4}, {1, 2}}) + %get-tuple-element.1 = f32[2,2] get-tuple-element(%x), index=0 + %get-tuple-element.2 = f32[2,2] get-tuple-element(%x), index=1 + %add.1 = f32[2,2] add(%get-tuple-element.1, %constant.f32.1) + %add.2 = f32[2,2] add(%get-tuple-element.2, %constant.f32.2) + ROOT %tuple = (f32[2,2], f32[2,2]) tuple(%add.1, %add.2) +} + +ENTRY %WhileLoop () -> (f32[2,2], f32[2,2]) { + %constant.f32 = f32[2,2] constant(f32[2,2] {{3, 4}, {5, 6}}) + %init.tuple = (f32[2,2], f32[2,2]) tuple(%constant.f32, %constant.f32) + ROOT %while = (f32[2,2], f32[2,2]) while(%init.tuple), condition=%condition, body=%body +} +)"; + + TF_ASSERT_OK_AND_ASSIGN(std::unique_ptr module, + ParseAndReturnVerifiedModule(module_str)); + auto root_while = module->entry_computation()->root_instruction(); + auto body_tuple = root_while->while_body()->root_instruction(); + auto i1 = body_tuple->operands()[0]->operands()[0]; // %get-tuple-element.1 + auto i2 = body_tuple->operands()[1]->operands()[0]; // %get-tuple-element.2 + EXPECT_FALSE(ArCrsCombiner::TestInstructionsComputeSameValue(i1, i2)); +} + +TEST_F(ArCrsCombinerTest, RewritePatternArConvertAddCrs) { + const char* module_str = R"( +HloModule foobar + +%binary_add (a: bf16[], b: bf16[]) -> bf16[] { + %a = bf16[] parameter(0) + %b = bf16[] parameter(1) + ROOT %add = bf16[] add(%a, %b) +} + +%sum.f32 (x: f32[], y: f32[]) -> f32[] { + %x = f32[] parameter(0) + %y = f32[] parameter(1) + ROOT %add = f32[] add(%x, %y) +} + +ENTRY %entrycomp (p: f32[2,2]) -> (f32[2,2], f32[2,2]) { + %p = f32[2,2] parameter(0) + %constant.bf16 = bf16[2,2] constant(bf16[2,2] {{1, 2}, {3, 4}}) + %constant.f32 = f32[2,2] constant(f32[2,2] {{1, 2}, {3, 4}}) + + %cross-replica-sum.ar.1 = bf16[2,2] + cross-replica-sum(%constant.bf16), + replica_groups={{0},{1}}, + all_reduce_id=1, + to_apply=%binary_add, + sharding={maximal device=0} + %convert.1 = f32[2,2] + convert(%cross-replica-sum.ar.1), + sharding={maximal device=0} + %add.1 = f32[2,2] + add(%constant.f32, %convert.1), + sharding={maximal device=0} + %cross-replica-sum.1 = f32[2,2] + cross-replica-sum(%add.1), + replica_groups={{0,1}}, + to_apply=%sum.f32, + sharding={maximal device=0} + + %cross-replica-sum.ar.2 = bf16[2,2] + cross-replica-sum(%constant.bf16), + replica_groups={{0},{1}}, + all_reduce_id=1, + to_apply=%binary_add, + sharding={maximal device=1} + %convert.2 = f32[2,2] + convert(%cross-replica-sum.ar.2), + sharding={maximal device=1} + %add.2 = f32[2,2] + add(%constant.f32, %convert.2), + sharding={maximal device=1} + %cross-replica-sum.2 = f32[2,2] + cross-replica-sum(%add.2), + replica_groups={{0,1}}, + to_apply=%sum.f32, + sharding={maximal device=1} + + ROOT %tuple = (f32[2,2], f32[2,2]) + tuple(%cross-replica-sum.1, %cross-replica-sum.2), + sharding={{maximal device=0}, {maximal device=1}} +} +)"; + + TF_ASSERT_OK_AND_ASSIGN(std::unique_ptr module, + ParseAndReturnVerifiedModule(module_str)); + auto crs_before = + module->entry_computation()->root_instruction()->operands()[0]; + auto replica_groups_before = crs_before->replica_groups(); + ArCrsCombiner combiner(2); + auto changed = combiner.Run(module.get()).ValueOrDie(); + EXPECT_TRUE(changed); + EXPECT_THAT(module->entry_computation()->root_instruction(), + op::Tuple(op::Subtract(op::CrossReplicaSum(), op::Constant()), + op::Subtract(op::CrossReplicaSum(), op::Constant()))); + auto sub = module->entry_computation()->root_instruction()->operands()[0]; + auto crs_after = sub->operands()[0]; + auto replica_groups_after = crs_after->replica_groups(); + ASSERT_EQ(replica_groups_before.size(), replica_groups_after.size()); + for (int i = 0; i < replica_groups_before.size(); ++i) { + // Somewhat verbose way to compare the replica_ids, because EqualsProto + // is not available in the open-source build. + auto group_before = replica_groups_before[i]; + std::vector ids_before(group_before.replica_ids().begin(), + group_before.replica_ids().end()); + auto group_after = replica_groups_after[i]; + std::vector ids_after(group_after.replica_ids().begin(), + group_after.replica_ids().end()); + EXPECT_EQ(ids_before, ids_after); + } +} + +TEST_F(ArCrsCombinerTest, OtherSummandNotTheSameDontRewrite) { + const char* module_str = R"( +HloModule foobar + +%binary_add (a: bf16[], b: bf16[]) -> bf16[] { + %a = bf16[] parameter(0) + %b = bf16[] parameter(1) + ROOT %add = bf16[] add(%a, %b) +} + +%sum.f32 (x: f32[], y: f32[]) -> f32[] { + %x = f32[] parameter(0) + %y = f32[] parameter(1) + ROOT %add = f32[] add(%x, %y) +} + +ENTRY %entrycomp (p: f32[2,2]) -> (f32[2,2], f32[2,2]) { + %p = f32[2,2] parameter(0) + %constant.bf16 = bf16[2,2] constant(bf16[2,2] {{1, 2}, {3, 4}}) + %constant.f32.1 = f32[2,2] constant(f32[2,2] {{1, 2}, {3, 4}}) + %constant.f32.2 = f32[2,2] constant(f32[2,2] {{3, 4}, {5, 6}}) + + %cross-replica-sum.ar.1 = bf16[2,2] + cross-replica-sum(%constant.bf16), + replica_groups={{0},{1}}, + all_reduce_id=1, + to_apply=%binary_add, + sharding={maximal device=0} + %convert.1 = f32[2,2] + convert(%cross-replica-sum.ar.1), + sharding={maximal device=0} + %add.1 = f32[2,2] + add(%constant.f32.1, %convert.1), + sharding={maximal device=0} + %cross-replica-sum.1 = f32[2,2] + cross-replica-sum(%add.1), + replica_groups={{0,1}}, + to_apply=%sum.f32, + sharding={maximal device=0} + + %cross-replica-sum.ar.2 = bf16[2,2] + cross-replica-sum(%constant.bf16), + replica_groups={{0},{1}}, + all_reduce_id=1, + to_apply=%binary_add, + sharding={maximal device=1} + %convert.2 = f32[2,2] + convert(%cross-replica-sum.ar.2), + sharding={maximal device=1} + %add.2 = f32[2,2] + add(%constant.f32.2, %convert.2), + sharding={maximal device=1} + %cross-replica-sum.2 = f32[2,2] + cross-replica-sum(%add.2), + replica_groups={{0,1}}, + to_apply=%sum.f32, + sharding={maximal device=1} + + ROOT %tuple = (f32[2,2], f32[2,2]) + tuple(%cross-replica-sum.1, %cross-replica-sum.2), + sharding={{maximal device=0}, {maximal device=1}} +} +)"; + + TF_ASSERT_OK_AND_ASSIGN(std::unique_ptr module, + ParseAndReturnVerifiedModule(module_str)); + ArCrsCombiner combiner(2); + auto changed = combiner.Run(module.get()).ValueOrDie(); + EXPECT_FALSE(changed); +} + +} // namespace +} // namespace xla diff --git a/tensorflow/compiler/xla/service/batchnorm_expander.cc b/tensorflow/compiler/xla/service/batchnorm_expander.cc index f70f6ddfec..0e6ca1871b 100644 --- a/tensorflow/compiler/xla/service/batchnorm_expander.cc +++ b/tensorflow/compiler/xla/service/batchnorm_expander.cc @@ -107,19 +107,37 @@ class BatchNormExpanderVisitor : public DfsHloVisitorWithDefault { } std::unique_ptr Mean( - int64 element_count, HloInstruction* operand, + HloInstruction* element_count, HloInstruction* operand, const std::function)>& add_instruction) { - HloInstruction* elem_count_recip = - add_instruction(HloInstruction::CreateBroadcast( - operand->shape(), - add_instruction(HloInstruction::CreateConvert( - ShapeUtil::MakeShape(operand->shape().element_type(), {}), - add_instruction(HloInstruction::CreateConstant( - LiteralUtil::CreateR0(1.0 / element_count))))), - {})); - return HloInstruction::CreateBinary(operand->shape(), HloOpcode::kMultiply, - operand, elem_count_recip); + auto broadcast = add_instruction( + HloInstruction::CreateBroadcast(operand->shape(), element_count, {})); + return HloInstruction::CreateBinary(operand->shape(), HloOpcode::kDivide, + operand, broadcast); + } + + std::unique_ptr DynamicElementCountPerFeature( + HloInstruction* operand, int64 feature_index, + const std::function)>& + add_instruction) { + auto elements_per_feature_u32 = add_instruction( + HloInstruction::CreateConstant(LiteralUtil::CreateR0(1))); + + for (int64 i = 0; i < ShapeUtil::Rank(operand->shape()); ++i) { + if (i == feature_index) { + continue; + } + auto dynamic_dimension_size = + add_instruction(HloInstruction::CreateGetDimensionSize( + ShapeUtil::MakeShape(U32, {}), operand, i)); + elements_per_feature_u32 = add_instruction(HloInstruction::CreateBinary( + ShapeUtil::MakeShape(U32, {}), HloOpcode::kMultiply, + dynamic_dimension_size, elements_per_feature_u32)); + } + + return HloInstruction::CreateConvert( + ShapeUtil::MakeShape(operand->shape().element_type(), {}), + elements_per_feature_u32); } // Replaces the existing HLO instruction old_instruction, with @@ -195,9 +213,6 @@ Status BatchNormExpanderVisitor::HandleBatchNormTraining( const Shape operand_shape = operand->shape(); PrimitiveType ptype = operand_shape.element_type(); int64 feature_index = batch_norm->feature_index(); - const int64 feature_count = operand_shape.dimensions(feature_index); - const int64 size_in_elements = ShapeUtil::ElementsIn(operand_shape); - int64 elements_per_feature_int64 = size_in_elements / feature_count; HloInstruction* scale = batch_norm->mutable_operand(1); HloInstruction* offset = batch_norm->mutable_operand(2); @@ -220,6 +235,9 @@ Status BatchNormExpanderVisitor::HandleBatchNormTraining( } } + auto elements_per_feature = + add(DynamicElementCountPerFeature(operand, feature_index, add)); + auto scale_broadcasted = add( HloInstruction::CreateBroadcast(operand_shape, scale, {feature_index})); @@ -243,13 +261,13 @@ Status BatchNormExpanderVisitor::HandleBatchNormTraining( add_reduce_computation)); // E[X]. - auto mean = add(Mean(elements_per_feature_int64, sum, add)); + auto mean = add(Mean(elements_per_feature, sum, add)); auto mean_broadcasted = add( HloInstruction::CreateBroadcast(operand_shape, mean, {feature_index})); // E[X^2]. - auto square_mean = add(Mean(elements_per_feature_int64, squared_sum, add)); + auto square_mean = add(Mean(elements_per_feature, squared_sum, add)); // E^2[X]. auto mean_square = @@ -458,9 +476,8 @@ Status BatchNormExpanderVisitor::HandleBatchNormGrad( int64 feature_index = batch_norm->feature_index(); - const int64 size_in_elements = ShapeUtil::ElementsIn(activation_shape); - const int64 feature_count = activation_shape.dimensions(feature_index); - const int64 elements_per_feature_int64 = size_in_elements / feature_count; + auto elements_per_feature = + add(DynamicElementCountPerFeature(activation, feature_index, add)); auto zero_literal = LiteralUtil::CreateR0(0.0f); TF_ASSIGN_OR_RETURN(zero_literal, zero_literal.Convert(ptype)); @@ -553,15 +570,9 @@ Status BatchNormExpanderVisitor::HandleBatchNormGrad( add_binary(activation_shape, HloOpcode::kMultiply, scale_broadcasted, rsqrt_var_add_epsilon_broadcasted); - scale_times_rsqrt_var_add_epsilon = add( - Mean(elements_per_feature_int64, scale_times_rsqrt_var_add_epsilon, add)); + scale_times_rsqrt_var_add_epsilon = + add(Mean(elements_per_feature, scale_times_rsqrt_var_add_epsilon, add)); - auto elements_per_feature_literal = - LiteralUtil::CreateR0(elements_per_feature_int64); - TF_ASSIGN_OR_RETURN(elements_per_feature_literal, - elements_per_feature_literal.Convert(ptype)); - auto elements_per_feature = add( - HloInstruction::CreateConstant(std::move(elements_per_feature_literal))); auto i1 = add_binary(activation_shape, HloOpcode::kMultiply, grad_output, add(HloInstruction::CreateBroadcast( activation_shape, elements_per_feature, {}))); diff --git a/tensorflow/compiler/xla/service/batchnorm_expander_test.cc b/tensorflow/compiler/xla/service/batchnorm_expander_test.cc index 08cf802617..8e8fbbd935 100644 --- a/tensorflow/compiler/xla/service/batchnorm_expander_test.cc +++ b/tensorflow/compiler/xla/service/batchnorm_expander_test.cc @@ -36,7 +36,21 @@ limitations under the License. namespace xla { namespace { -using BatchNormExpanderTest = HloTestBase; +class BatchNormExpanderTest : public HloTestBase { + protected: + // BatchNorm should have a dynamic sized dividor for mean operations. + int64 CountGetDimensionSize(const HloModule& module) { + int64 count = 0; + for (HloComputation* comp : module.computations()) { + for (HloInstruction* inst : comp->instructions()) { + if (inst->opcode() == HloOpcode::kGetDimensionSize) { + count++; + } + } + } + return count; + } +}; // Test that we expand BatchNormTraining. TEST_F(BatchNormExpanderTest, BatchNormTraining) { @@ -68,6 +82,7 @@ TEST_F(BatchNormExpanderTest, BatchNormTraining) { /*rewrite_grad_op=*/true); ASSERT_TRUE(rewriter.Run(module.get()).ValueOrDie()); root = computation->root_instruction(); + EXPECT_EQ(CountGetDimensionSize(*module), 3); // Make sure this operation is expanded. EXPECT_EQ(root->opcode(), HloOpcode::kTuple); } @@ -110,6 +125,7 @@ TEST_F(BatchNormExpanderTest, BatchNormGrad) { /*rewrite_grad_op=*/true); ASSERT_TRUE(rewriter.Run(module.get()).ValueOrDie()); root = computation->root_instruction(); + EXPECT_EQ(CountGetDimensionSize(*module), 3); // Make sure this operation is expanded. EXPECT_EQ(root->opcode(), HloOpcode::kTuple); } diff --git a/tensorflow/compiler/xla/service/buffer_assignment.cc b/tensorflow/compiler/xla/service/buffer_assignment.cc index 40c012a5e4..8d7c624478 100644 --- a/tensorflow/compiler/xla/service/buffer_assignment.cc +++ b/tensorflow/compiler/xla/service/buffer_assignment.cc @@ -746,8 +746,7 @@ StatusOr> BufferAssigner::Run( LogicalBuffer::AlignmentFunction color_alignment, bool allow_input_output_aliasing, bool allocate_buffers_for_constants, BufferLiveness::Colorer colorer, ReuseAllocationFunction reuse_checker) { - BufferAssigner assigner(allow_input_output_aliasing, - allocate_buffers_for_constants, std::move(colorer), + BufferAssigner assigner(allocate_buffers_for_constants, std::move(colorer), std::move(reuse_checker)); return assigner.CreateAssignment(module, std::move(hlo_ordering), std::move(buffer_size), @@ -1434,33 +1433,40 @@ BufferAssigner::MergeColocatedBufferSets( computation == module->entry_computation(); }; + std::vector set_can_be_merged(colocated_buffer_sets.size(), true); + + // Do not merge if one of the sets includes live outs, entry parameters or + // constants. + // + // Buffer liveness does not report the correct live range for entry + // parameter and live out buffers so we have to special case them here. On + // backends that support constant buffer allocations, constant buffers are + // assigned globals in readonly storage so we can't merge colocated buffer + // sets containing constants with colocated buffer sets containing writing + // instructions or other constants. + // + // Moreover (on the CPU/GPU backends) the entry parameter buffers belong to + // the caller of the executable so we can't write to entry parameters + // either, and the argument for not merging constants also applies to entry + // parameters. + for (int64 i = 0; i < colocated_buffer_sets.size(); ++i) { + for (auto& buffer : colocated_buffer_sets[i]) { + if (buffer_liveness.MaybeLiveOut(*buffer) || + is_entry_parameter(*buffer) || + buffer->instruction()->opcode() == HloOpcode::kConstant) { + set_can_be_merged[i] = false; + break; + } + } + } + // Returns true if the two colocated buffer sets (specified by their indices // into the colocated_buffer_sets) can be merged into a single set. auto cannot_merge_buffer_sets = [&colocated_buffer_sets, &buffer_liveness, &buffer_size, - &is_entry_parameter](int64 i, int64 j) { - // Do not merge if one of the sets includes live outs, entry parameters or - // constants. - // - // Buffer liveness does not report the correct live range for entry - // parameter and live out buffers so we have to special case them here. On - // backends that support constant buffer allocations, constant buffers are - // assigned globals in readonly storage so we can't merge colocated buffer - // sets containing constants with colocated buffer sets containing writing - // instructions or other constants. - // - // Moreover (on the CPU/GPU backends) the entry parameter buffers belong to - // the caller of the executable so we can't write to entry parameters - // either, and the argument for not merging constants also applies to entry - // parameters. - for (int64 key : {i, j}) { - for (auto& buffer : colocated_buffer_sets[key]) { - if (buffer_liveness.MaybeLiveOut(*buffer) || - is_entry_parameter(*buffer) || - buffer->instruction()->opcode() == HloOpcode::kConstant) { - return true; - } - } + &set_can_be_merged](int64 i, int64 j) { + if (!set_can_be_merged[i] || !set_can_be_merged[j]) { + return true; } // Colocated sets satisfy the invariant that all buffers within a set have diff --git a/tensorflow/compiler/xla/service/buffer_assignment.h b/tensorflow/compiler/xla/service/buffer_assignment.h index d8e1612b89..0a9fdede80 100644 --- a/tensorflow/compiler/xla/service/buffer_assignment.h +++ b/tensorflow/compiler/xla/service/buffer_assignment.h @@ -545,12 +545,10 @@ class BufferAssigner { ReuseAllocationFunction reuse_checker = nullptr); private: - BufferAssigner(bool allow_input_output_aliasing, - bool allocate_buffers_for_constants, + BufferAssigner(bool allocate_buffers_for_constants, BufferLiveness::Colorer colorer, ReuseAllocationFunction reuse_checker) - : allow_input_output_aliasing_(allow_input_output_aliasing), - allocate_buffers_for_constants_(allocate_buffers_for_constants), + : allocate_buffers_for_constants_(allocate_buffers_for_constants), colorer_(colorer), reuse_checker_(reuse_checker) {} virtual ~BufferAssigner() = default; @@ -640,10 +638,6 @@ class BufferAssigner { LogicalBuffer::Color::Hasher> SplitBuffersByColor(const absl::flat_hash_set& buffers); - // If true, buffer assignments assumes that input parameter buffers and output - // buffers can be shared if their sizes match. - bool allow_input_output_aliasing_; - // If true, allocate buffers for constant instructions. bool allocate_buffers_for_constants_; diff --git a/tensorflow/compiler/xla/service/buffer_assignment_test.cc b/tensorflow/compiler/xla/service/buffer_assignment_test.cc index b1fc50cb18..8f482e6ba8 100644 --- a/tensorflow/compiler/xla/service/buffer_assignment_test.cc +++ b/tensorflow/compiler/xla/service/buffer_assignment_test.cc @@ -137,8 +137,7 @@ class BufferAssignmentTest : public HloTestBase { } std::unique_ptr RunBufferAssignmentWithInstructionSequence( - HloModule* module, - absl::Span instruction_sequence, + HloModule* module, absl::Span instruction_sequence, int64 alignment = 1) { HloSchedule schedule(module); schedule.set_sequence(module->entry_computation(), instruction_sequence); @@ -1853,7 +1852,7 @@ class WhileBufferAssignmentTest : public HloTestBase { std::unique_ptr RunBufferAssignment(HloModule* module, int64 alignment = 1) { HloSchedule schedule = - ScheduleModule(*module, ByteSizeOf).ConsumeValueOrDie(); + ScheduleModule(module, ByteSizeOf).ConsumeValueOrDie(); return BufferAssigner::Run( module, absl::make_unique(schedule), ByteSizeOf, @@ -2162,7 +2161,7 @@ TEST_F(WhileBufferAssignmentTest, ColocatedBuffers) { // nodes are traversed during BufferAssignment. TF_ASSERT_OK_AND_ASSIGN( HloSchedule schedule, - ScheduleModule(*module, [](const BufferValue& buffer) { + ScheduleModule(module.get(), [](const BufferValue& buffer) { return ShapeUtil::ByteSizeOf(buffer.shape(), /*pointer_size=*/sizeof(void*)); })); @@ -2391,15 +2390,16 @@ TEST_F(WhileBufferAssignmentTest, WhileLoopsInterferingResultRange) { RunCopyInsertion(module.get()); HloSchedule schedule = - ScheduleModule(*module, ByteSizeOf).ConsumeValueOrDie(); + ScheduleModule(module.get(), ByteSizeOf).ConsumeValueOrDie(); // To trigger b/38494731, we want a specific Hlo schedule for the // root computation, so we overwrite that entry with a manually // crafted sequence. - schedule.set_sequence(module->entry_computation(), - {input1, weights1, one, output1, while1->operand(0), - while1, input0, weights0, zero, output0, - while0->operand(0), while0, gte0, gte1, root_add}); + schedule.set_sequence( + module->entry_computation(), + {input1, weights1, one, output1, while1->mutable_operand(0), while1, + input0, weights0, zero, output0, while0->mutable_operand(0), while0, + gte0, gte1, root_add}); // If this ASSERT fails, we constructed a bogus sequence above and this test // itself is buggy. diff --git a/tensorflow/compiler/xla/service/buffer_liveness_test.cc b/tensorflow/compiler/xla/service/buffer_liveness_test.cc index aeee543e84..40825a7871 100644 --- a/tensorflow/compiler/xla/service/buffer_liveness_test.cc +++ b/tensorflow/compiler/xla/service/buffer_liveness_test.cc @@ -117,7 +117,7 @@ TEST_F(BufferLivenessTest, ElementwiseChain) { auto log = builder.AddInstruction( HloInstruction::CreateUnary(vec_, HloOpcode::kLog, exp)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); auto liveness = @@ -164,7 +164,7 @@ TEST_F(BufferLivenessTest, MultipleEntryParameters_Sequential) { auto add = builder.AddInstruction( HloInstruction::CreateBinary(vec_, HloOpcode::kAdd, negate, exp)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); HloComputation* entry = module->AddEntryComputation(builder.Build()); HloSchedule schedule(module.get()); @@ -213,7 +213,7 @@ TEST_F(BufferLivenessTest, NonElementwiseOperand) { auto reverse = builder.AddInstruction(HloInstruction::CreateReverse(vec_, negate, {0})); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); auto liveness = @@ -247,7 +247,7 @@ TEST_F(BufferLivenessTest, OverlappedBuffers) { auto add = builder.AddInstruction( HloInstruction::CreateBinary(vec_, HloOpcode::kAdd, negate, exp)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); auto liveness = @@ -289,7 +289,7 @@ TEST_F(BufferLivenessTest, OverlappedBuffersSequentialOrder) { auto add = builder.AddInstruction( HloInstruction::CreateBinary(vec_, HloOpcode::kAdd, negate, exp)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); HloSchedule schedule(module.get()); @@ -336,7 +336,7 @@ TEST_F(BufferLivenessTest, RootInstructionIsNotLastInSequentialOrder) { HloInstruction::CreateSend(recv_done, token, /*channel_id=*/1)); auto send_done = builder.AddInstruction(HloInstruction::CreateSendDone(send)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build(add)); HloSchedule schedule(module.get()); @@ -373,7 +373,7 @@ TEST_F(BufferLivenessTest, TupleLiveOut) { auto outer_tuple = builder.AddInstruction(HloInstruction::CreateTuple({inner_tuple, exp})); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); auto liveness = @@ -393,7 +393,7 @@ TEST_F(BufferLivenessTest, TupleLiveOut) { TEST_F(BufferLivenessTest, EmbeddedComputation) { // Test MaybeLiveOut and MayInterfere for embedded computation. - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto embedded_builder = HloComputation::Builder(TestName() + "_embedded"); auto embedded_param = embedded_builder.AddInstruction( @@ -450,7 +450,7 @@ TEST_F(BufferLivenessTest, TupleConstantLiveOut) { builder.AddInstruction(HloInstruction::CreateGetTupleElement( inner_tuple0.shape(), tuple_constant, 0)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); auto liveness = @@ -576,7 +576,7 @@ TEST_F(BufferLivenessTest, DependentTupleElements) { auto tuple_root = builder.AddInstruction(HloInstruction::CreateTuple({add0, add1})); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(BuildDummyComputation()); module->AddEmbeddedComputation(builder.Build()); @@ -611,8 +611,8 @@ TEST_F(BufferLivenessTest, DependentTupleElements) { class FusedDynamicUpdateSliceLivenessTest : public BufferLivenessTest { protected: // Builds and runs a computation (see test case computation graphs below). - std::unique_ptr BuildModule(const bool update_uses_tuple_element1, - const bool fuse_gte0) { + std::unique_ptr BuildModule( + const bool update_uses_tuple_element1, const bool fuse_gte0) { auto builder = HloComputation::Builder(TestName()); // Create param0 Tuple. Shape data_shape = ShapeUtil::MakeShape(F32, {8}); @@ -646,7 +646,7 @@ class FusedDynamicUpdateSliceLivenessTest : public BufferLivenessTest { builder.AddInstruction( HloInstruction::CreateTuple({gte0, dynamic_update_slice})); // Build module and get reference to entry computation. - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); auto* computation = module->entry_computation(); // Create fusion instruction based on number of tuple element 1 users. @@ -802,7 +802,7 @@ class DynamicUpdateSliceLivenessTest : public BufferLivenessTest { auto tuple_root = builder.AddInstruction( HloInstruction::CreateTuple({gte0, dynamic_update_slice})); // Build module and get reference to entry computation. - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(BuildDummyComputation()); module->AddEmbeddedComputation(builder.Build()); // Run BufferLiveness on 'module'. diff --git a/tensorflow/compiler/xla/service/call_graph.cc b/tensorflow/compiler/xla/service/call_graph.cc index bdd5069632..7987343bfa 100644 --- a/tensorflow/compiler/xla/service/call_graph.cc +++ b/tensorflow/compiler/xla/service/call_graph.cc @@ -325,6 +325,15 @@ bool CallGraph::IsFlattened() const { return true; } +std::vector CallGraph::GetComputationCallers( + HloComputation* c) { + std::vector callers; + for (auto callsite : GetNode(c).caller_callsites()) { + callers.push_back(callsite.instruction()); + } + return callers; +} + std::pair CallGraph::NearestAncestorsInSameComputation(HloInstruction* a, HloInstruction* b) const { diff --git a/tensorflow/compiler/xla/service/call_graph.h b/tensorflow/compiler/xla/service/call_graph.h index cb56f4789d..05c7c99873 100644 --- a/tensorflow/compiler/xla/service/call_graph.h +++ b/tensorflow/compiler/xla/service/call_graph.h @@ -236,6 +236,10 @@ class CallGraph { // FlattenCallGraph. bool IsFlattened() const; + // Returns a vector of instructions calling the passed computation. + // (Often a vector of size 1.) + std::vector GetComputationCallers(HloComputation* c); + string ToString() const; private: diff --git a/tensorflow/compiler/xla/service/compile_only_service.cc b/tensorflow/compiler/xla/service/compile_only_service.cc index 67132274c0..1965925fa7 100644 --- a/tensorflow/compiler/xla/service/compile_only_service.cc +++ b/tensorflow/compiler/xla/service/compile_only_service.cc @@ -86,15 +86,15 @@ CompileOnlyService::CompileAheadOfTime( Executable::DumpToDirectory(per_host_path, filename, hlo_snapshot)); } - const auto& program_shape = instance.computation.host_program_shape(); ExecutionOptions execution_options; *execution_options.mutable_debug_options() = debug_options; *execution_options.mutable_shape_with_output_layout() = - *instance.result_layout; + instance.result_layout->ToProto(); TF_ASSIGN_OR_RETURN( std::unique_ptr module_config, - CreateModuleConfig(program_shape, instance.argument_layouts, - &execution_options)); + CreateModuleConfig( + ProgramShape(instance.computation.host_program_shape()), + instance.argument_layouts, &execution_options)); TF_ASSIGN_OR_RETURN( std::unique_ptr hlo_module, diff --git a/tensorflow/compiler/xla/service/computation_placer.h b/tensorflow/compiler/xla/service/computation_placer.h index c899ffb9dc..844b42a38d 100644 --- a/tensorflow/compiler/xla/service/computation_placer.h +++ b/tensorflow/compiler/xla/service/computation_placer.h @@ -105,8 +105,6 @@ class ComputationPlacer { // Map from platform kind to computation placer singleton. static std::map* GetPlatformComputationPlacers(); - se::Platform::Id platform_id_; - TF_DISALLOW_COPY_AND_ASSIGN(ComputationPlacer); }; diff --git a/tensorflow/compiler/xla/service/convolution_feature_group_converter.cc b/tensorflow/compiler/xla/service/convolution_feature_group_converter.cc index 7f7f1503a0..95c7724c3c 100644 --- a/tensorflow/compiler/xla/service/convolution_feature_group_converter.cc +++ b/tensorflow/compiler/xla/service/convolution_feature_group_converter.cc @@ -142,16 +142,16 @@ std::vector GetMaskIds(int64 group_size, int64 group_count) { // Finally we use the Eq op of these two broadcasted constants and get the // desired mask. HloInstruction* GetExpandedFilterMask( - const Shape& filter_shape, int64 input_feature_dim, - int64 output_feature_dim, int64 group_count, + const Shape& filter_shape, int64 kernel_input_feature_dim, + int64 kernel_output_feature_dim, int64 group_count, const std::function)>& add_instruction) { Shape expanded_filter_shape = - ExpandedFilterShape(filter_shape, group_count, input_feature_dim); + ExpandedFilterShape(filter_shape, group_count, kernel_input_feature_dim); Shape mask_shape = ShapeUtil::MakeShape( S32, AsInt64Slice(expanded_filter_shape.dimensions())); - int64 output_feature = filter_shape.dimensions(output_feature_dim); - int64 group_size = filter_shape.dimensions(input_feature_dim); + int64 output_feature = filter_shape.dimensions(kernel_output_feature_dim); + int64 group_size = filter_shape.dimensions(kernel_input_feature_dim); // Create a 'input_feature' sized linspace and 'output_feature' sized linspace // that will be broadcasted into perpendicular dimensions and compared. @@ -159,15 +159,14 @@ HloInstruction* GetExpandedFilterMask( GetMaskIds(group_size, group_count); const std::vector output_feature_filter_mask = GetMaskIds(output_feature / group_count, group_count); - auto mask1 = add_instruction(HloInstruction::CreateConstant( LiteralUtil::CreateR1(input_feature_filter_mask))); - auto broadcasted_mask1 = add_instruction( - HloInstruction::CreateBroadcast(mask_shape, mask1, {input_feature_dim})); + auto broadcasted_mask1 = add_instruction(HloInstruction::CreateBroadcast( + mask_shape, mask1, {kernel_input_feature_dim})); auto mask2 = add_instruction(HloInstruction::CreateConstant( LiteralUtil::CreateR1(output_feature_filter_mask))); - auto broadcasted_mask2 = add_instruction( - HloInstruction::CreateBroadcast(mask_shape, mask2, {output_feature_dim})); + auto broadcasted_mask2 = add_instruction(HloInstruction::CreateBroadcast( + mask_shape, mask2, {kernel_output_feature_dim})); // Compare the broadcasted output feature linspace to the input feature // linspace to create a diagonal predicate. @@ -189,91 +188,203 @@ Status ConvolutionVisitor::HandleConvolution(HloInstruction* convolution) { }; auto dim_numbers = convolution->convolution_dimension_numbers(); - int64 input_feature_dim = dim_numbers.kernel_input_feature_dimension(); - int64 group_size = filter->shape().dimensions(input_feature_dim); - int64 output_feature_dim = dim_numbers.kernel_output_feature_dimension(); - auto expanded_filter_shape = - ExpandedFilterShape(filter->shape(), group_count, input_feature_dim); - HloInstruction* filter_mask = GetExpandedFilterMask( - filter->shape(), input_feature_dim, output_feature_dim, group_count, add); + int64 kernel_input_feature_dim = dim_numbers.kernel_input_feature_dimension(); + int64 group_size = filter->shape().dimensions(kernel_input_feature_dim); + int64 kernel_output_feature_dim = + dim_numbers.kernel_output_feature_dimension(); + auto expanded_filter_shape = ExpandedFilterShape(filter->shape(), group_count, + kernel_input_feature_dim); + HloInstruction* filter_mask = + GetExpandedFilterMask(filter->shape(), kernel_input_feature_dim, + kernel_output_feature_dim, group_count, add); HloInstruction* expanded_filter; if (group_size == 1) { bool depthwise_separable = - (group_count == filter->shape().dimensions(output_feature_dim)); + (group_count == filter->shape().dimensions(kernel_output_feature_dim)); // If the code generator handles depthwise separable convolutions // inherently, then no filter expansion is needed. if (!filter_expansion_ && depthwise_separable) { - const int64 old_kernel_input_feature_dimension = - dim_numbers.kernel_input_feature_dimension(); - const int64 old_kernel_output_feature_dimension = - dim_numbers.kernel_output_feature_dimension(); - - // For depthwise convolutions, we want the kernel input feature dimension - // to be smaller than the output feature dimension. If that's not the - // case, we swap the dimensions. - if (old_kernel_input_feature_dimension > - old_kernel_output_feature_dimension) { - Shape reshaped_filter_shape = filter->shape(); - auto& dimensions = *reshaped_filter_shape.mutable_dimensions(); - std::swap(dimensions[old_kernel_input_feature_dimension], - dimensions[old_kernel_output_feature_dimension]); - - auto reshaped_filter = - add(HloInstruction::CreateReshape(reshaped_filter_shape, filter)); - - dim_numbers.set_kernel_input_feature_dimension( - old_kernel_output_feature_dimension); - - dim_numbers.set_kernel_output_feature_dimension( - old_kernel_input_feature_dimension); - - auto new_convolution = HloInstruction::CreateConvolve( - convolution->shape(), convolution->mutable_operand(0), - reshaped_filter, group_count, convolution->window(), dim_numbers, - convolution->precision_config()); - - TF_RETURN_IF_ERROR(computation_->ReplaceWithNewInstruction( - convolution, std::move(new_convolution))); - } return Status::OK(); } // We want to repeat 'filter' in the 'input_feature_dim' dimension // 'group_count' times. Shape reshaped_filter_shape = - ShapeUtil::DeleteDimension(input_feature_dim, filter->shape()); + ShapeUtil::DeleteDimension(kernel_input_feature_dim, filter->shape()); auto reshaped_filter = add(HloInstruction::CreateReshape(reshaped_filter_shape, filter)); std::vector broadcast_dims; for (int64 i = 0; i < filter->shape().dimensions_size(); ++i) { - if (i == input_feature_dim) { + if (i == kernel_input_feature_dim) { continue; } broadcast_dims.push_back(i); } expanded_filter = add(HloInstruction::CreateBroadcast( expanded_filter_shape, reshaped_filter, broadcast_dims)); + + auto zero = add(HloInstruction::CreateConstant( + LiteralUtil::Zero(expanded_filter_shape.element_type()))); + auto zero_filter = + add(HloInstruction::CreateBroadcast(expanded_filter_shape, zero, {})); + auto new_filter = add(HloInstruction::CreateTernary( + expanded_filter_shape, HloOpcode::kSelect, filter_mask, expanded_filter, + zero_filter)); + + auto new_convolution = HloInstruction::CreateConvolve( + convolution->shape(), convolution->mutable_operand(0), new_filter, + /*feature_group_count=*/1, convolution->window(), dim_numbers, + convolution->precision_config()); + TF_RETURN_IF_ERROR(computation_->ReplaceWithNewInstruction( + convolution, std::move(new_convolution))); } else { - // We could possibly also use reshape, broadcast, reshape instead of concat - // here, but it would require more complex code, and for depthwise - // convolution we would never end up in this branch. - std::vector concat_operands(group_count, filter); - expanded_filter = add(HloInstruction::CreateConcatenate( - expanded_filter_shape, concat_operands, input_feature_dim)); + int64 activation_input_feature_dim = dim_numbers.input_feature_dimension(); + + int64 output_feature = + filter->shape().dimensions(kernel_output_feature_dim); + + // If group_count == output_feature, then we map those grouped convolutions + // onto depthwise convolution. This is done by adding an additional spatial + // dimension to the activations, kernel, and the output. + // E.g., we would turn + // [2, 12]{B, IF} conv [3, 4]{IF, OF} into + // [3, 2, 4]{S, B, IF} depth conv [3, 1, 4]{S, IF, OF}, where S is the + // additional spatial dimension. The generated convolution output will be + // [1, 2, 4]{S, B, OF} and then reshape the output back to [2, 4] {B, OF}. + + if (group_count == output_feature && !filter_expansion_) { + auto filter = convolution->mutable_operand(1); + auto activation = convolution->mutable_operand(0); + + // Add spatial dimension to the activation, and reshape. + Shape reshaped_activation_shape = activation->shape(); + ShapeUtil::AppendMajorDimension(group_size, &reshaped_activation_shape); + + int64 new_spatial_dim = reshaped_activation_shape.dimensions().size() - 1; + + reshaped_activation_shape.set_dimensions(activation_input_feature_dim, + group_count); + activation = add( + HloInstruction::CreateReshape(reshaped_activation_shape, activation)); + + // Add spatial dimension to the filter, and reshape. + Shape reshaped_filter_shape = filter->shape(); + ShapeUtil::AppendMajorDimension(1, &reshaped_filter_shape); + + filter = + add(HloInstruction::CreateReshape(reshaped_filter_shape, filter)); + + Shape new_output_shape = convolution->shape(); + ShapeUtil::AppendMajorDimension(1, &new_output_shape); + + // Edit convolution dimension numbers. Note that kernel_input_feature_dim + // now becomes a spatial dimension, and the newly added dimension of size + // 1 is the new kernel_input_feature_dim. + dim_numbers.add_input_spatial_dimensions(new_spatial_dim); + dim_numbers.add_kernel_spatial_dimensions(kernel_input_feature_dim); + dim_numbers.set_kernel_input_feature_dimension(new_spatial_dim); + dim_numbers.add_output_spatial_dimensions(new_spatial_dim); + + // Add window for the new spatial dimension. + Window new_window = convolution->window(); + auto* dim = new_window.add_dimensions(); + dim->set_window_dilation(1); + dim->set_base_dilation(1); + dim->set_stride(1); + dim->set_size(group_size); + + auto new_convolution = add(HloInstruction::CreateConvolve( + new_output_shape, activation, filter, group_count, new_window, + dim_numbers, convolution->precision_config())); + + // Delete the extra spatial dimension, and reshape. + Shape reshaped_convolution_shape = + ShapeUtil::DeleteDimension(new_spatial_dim, new_convolution->shape()); + auto reshaped_convolution = HloInstruction::CreateReshape( + reshaped_convolution_shape, new_convolution); + + TF_RETURN_IF_ERROR(computation_->ReplaceWithNewInstruction( + convolution, std::move(reshaped_convolution))); + + } else { + // The filter expansion mechanism adds zeroes in the kernel. + // For an OF = 12, IF = 6, and kernel IF = 2, the expanded filter mask + // would look like (IF on the Y-axis, OF on the X-axis) + // 1 1 1 1 0 0 0 0 0 0 0 0 + // 1 1 1 1 0 0 0 0 0 0 0 0 + // 0 0 0 0 1 1 1 1 0 0 0 0 + // 0 0 0 0 1 1 1 1 0 0 0 0 + // 0 0 0 0 0 0 0 0 1 1 1 1 + // 0 0 0 0 0 0 0 0 1 1 1 1 + // + // Instead of convolving the above with the input, we instead slice the + // kernel into three kernels, each containing islands of 1s from the + // filter above. We also slice the activations in the IF dimension with + // each slice of size = group_size. For each slice, we perform + // convolutions, and concatenate the generated outputs in the output OF + // dimension. + + std::vector sliced_convolutions; + auto activation = convolution->mutable_operand(0); + std::vector slice_strides(filter->shape().dimensions_size(), 1); + std::vector filter_slice_starts(filter->shape().dimensions_size(), + 0); + std::vector filter_slice_limits( + filter->shape().dimensions().begin(), + filter->shape().dimensions().end()); + std::vector activation_slice_starts( + activation->shape().dimensions_size(), 0); + std::vector activation_slice_limits( + activation->shape().dimensions().begin(), + activation->shape().dimensions().end()); + + int64 output_feature = + filter->shape().dimensions(kernel_output_feature_dim); + auto output_feature_dim = dim_numbers.output_feature_dimension(); + int64 filter_slice_width = output_feature / group_count; + + int64 activation_input_feature_dim = + dim_numbers.input_feature_dimension(); + + for (int64 i = 0; i < group_count; i++) { + filter_slice_starts[kernel_output_feature_dim] = i * filter_slice_width; + filter_slice_limits[kernel_output_feature_dim] = + (i + 1) * filter_slice_width; + auto filter_sliced_shape = filter->shape(); + filter_sliced_shape.set_dimensions(kernel_output_feature_dim, + filter_slice_width); + auto filter_slice = add(HloInstruction::CreateSlice( + filter_sliced_shape, filter, filter_slice_starts, + filter_slice_limits, slice_strides)); + + activation_slice_starts[activation_input_feature_dim] = i * group_size; + activation_slice_limits[activation_input_feature_dim] = + (i + 1) * group_size; + auto activation_sliced_shape = activation->shape(); + activation_sliced_shape.set_dimensions(activation_input_feature_dim, + group_size); + auto activation_slice = add(HloInstruction::CreateSlice( + activation_sliced_shape, activation, activation_slice_starts, + activation_slice_limits, slice_strides)); + + auto conv_slice_shape = convolution->shape(); + conv_slice_shape.set_dimensions(output_feature_dim, filter_slice_width); + + auto new_convolution = add(HloInstruction::CreateConvolve( + conv_slice_shape, activation_slice, filter_slice, + /*feature_group_count=*/1, convolution->window(), dim_numbers, + convolution->precision_config())); + + sliced_convolutions.push_back(new_convolution); + } + + auto new_conv = HloInstruction::CreateConcatenate( + convolution->shape(), sliced_convolutions, output_feature_dim); + TF_RETURN_IF_ERROR(computation_->ReplaceWithNewInstruction( + convolution, std::move(new_conv))); + } } - auto zero = add(HloInstruction::CreateConstant( - LiteralUtil::Zero(expanded_filter_shape.element_type()))); - auto zero_filter = - add(HloInstruction::CreateBroadcast(expanded_filter_shape, zero, {})); - auto new_filter = add( - HloInstruction::CreateTernary(expanded_filter_shape, HloOpcode::kSelect, - filter_mask, expanded_filter, zero_filter)); - auto new_convolution = HloInstruction::CreateConvolve( - convolution->shape(), convolution->mutable_operand(0), new_filter, - /*feature_group_count=*/1, convolution->window(), dim_numbers, - convolution->precision_config()); - TF_RETURN_IF_ERROR(computation_->ReplaceWithNewInstruction( - convolution, std::move(new_convolution))); + return Status::OK(); } diff --git a/tensorflow/compiler/xla/service/convolution_feature_group_converter_test.cc b/tensorflow/compiler/xla/service/convolution_feature_group_converter_test.cc index 28373ebf63..e6bf2143a2 100644 --- a/tensorflow/compiler/xla/service/convolution_feature_group_converter_test.cc +++ b/tensorflow/compiler/xla/service/convolution_feature_group_converter_test.cc @@ -82,18 +82,14 @@ ENTRY %Convolve1D1Window_0.v3 (input: f32[1,2,4], filter: f32[1,2,2]) -> f32[1,2 ConvolutionFeatureGroupConverter converter; ASSERT_TRUE(converter.Run(module.get()).ValueOrDie()); root = computation->root_instruction(); - // Make sure the convolution is converted to one with feature_group_count = 1. - EXPECT_EQ(root->opcode(), HloOpcode::kConvolution); - EXPECT_EQ(root->feature_group_count(), 1); - // Verify that the filter operand has been replaced. - EXPECT_THAT(root->operand(1), - op::Select(op::Eq(op::Broadcast(op::Constant()), - op::Broadcast(op::Constant())), - // We expect to see Concatenate here instead of - // Broadcast, because feature_group_count < input - // feature dimension. - op::Concatenate(op::Parameter(), op::Parameter()), - op::Broadcast(op::Constant()))); + // Make sure the convolution is replaced with a concatenate. + EXPECT_EQ(root->opcode(), HloOpcode::kConcatenate); + // And the operands of the concatenate are convolutions, each with a feature + // group count = 1. + EXPECT_EQ(root->operand(0)->opcode(), HloOpcode::kConvolution); + EXPECT_EQ(root->operand(1)->opcode(), HloOpcode::kConvolution); + EXPECT_EQ(root->operand(0)->feature_group_count(), 1); + EXPECT_EQ(root->operand(1)->feature_group_count(), 1); } } // namespace diff --git a/tensorflow/compiler/xla/service/copy_insertion.cc b/tensorflow/compiler/xla/service/copy_insertion.cc index 4e547d925f..df60596638 100644 --- a/tensorflow/compiler/xla/service/copy_insertion.cc +++ b/tensorflow/compiler/xla/service/copy_insertion.cc @@ -442,7 +442,6 @@ class CopyRemover { const HloOrdering& ordering, HloModule* module) : module_(module), alias_analysis_(alias_analysis), - ordering_(ordering), buffer_value_tracker_(*module, alias_analysis, ordering) {} // Try to elide the given copy. The copy is elided if the instruction is not @@ -1003,7 +1002,6 @@ class CopyRemover { HloModule* module_; const HloAliasAnalysis& alias_analysis_; - const HloOrdering& ordering_; // Object tracking the HLO values contained in each HLO buffer. BufferValueTracker buffer_value_tracker_; diff --git a/tensorflow/compiler/xla/service/copy_insertion_test.cc b/tensorflow/compiler/xla/service/copy_insertion_test.cc index 7446bc7cc1..e4e9d7ba05 100644 --- a/tensorflow/compiler/xla/service/copy_insertion_test.cc +++ b/tensorflow/compiler/xla/service/copy_insertion_test.cc @@ -94,7 +94,7 @@ TEST_F(CopyInsertionTest, SingleParameter) { EXPECT_THAT(x->users(), UnorderedElementsAre(tuple)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); InsertCopies(module.get()); @@ -114,7 +114,7 @@ TEST_F(CopyInsertionTest, SingleConstant) { EXPECT_THAT(constant->users(), UnorderedElementsAre(tuple)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); InsertCopies(module.get()); @@ -127,7 +127,7 @@ TEST_F(CopyInsertionTest, SingleConstant) { TEST_F(CopyInsertionTest, ExistingCopiesNotRemoved) { // Verify that kCopy instructions which change layout and exist before // copy-insertion remain in the graph after copy-insertion. - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto builder = HloComputation::Builder(TestName()); HloInstruction* constant = @@ -181,7 +181,7 @@ TEST_F(CopyInsertionTest, MultipleConstantsAndParameters) { builder.AddInstruction(HloInstruction::CreateTuple({constant2, x, add})); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); InsertCopies(module.get()); @@ -217,7 +217,7 @@ TEST_F(CopyInsertionTest, AmbiguousPointsToSet) { EXPECT_THAT(constant2->users(), UnorderedElementsAre(tuple1, tuple2)); EXPECT_THAT(constant3->users(), UnorderedElementsAre(tuple2)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); HloInstruction* old_root = module->entry_computation()->root_instruction(); @@ -238,7 +238,7 @@ TEST_F(CopyInsertionTest, BitcastParameter) { HloInstruction* bitcast = builder.AddInstruction(HloInstruction::CreateUnary( ShapeUtil::MakeShape(F32, {2, 2}), HloOpcode::kBitcast, x)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); EXPECT_THAT(x->users(), UnorderedElementsAre(bitcast)); @@ -261,7 +261,7 @@ TEST_F(CopyInsertionTest, BitcastConstant) { HloInstruction* bitcast = builder.AddInstruction(HloInstruction::CreateUnary( ShapeUtil::MakeShape(F32, {2, 2}), HloOpcode::kBitcast, constant)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); EXPECT_THAT(constant->users(), UnorderedElementsAre(bitcast)); @@ -283,7 +283,7 @@ TEST_F(CopyInsertionTest, BitcastTupleElementParameter) { ShapeUtil::MakeShape(F32, {2, 2}), HloOpcode::kBitcast, x)); builder.AddInstruction(HloInstruction::CreateTuple({bitcast})); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); EXPECT_THAT(x->users(), UnorderedElementsAre(bitcast)); @@ -310,7 +310,7 @@ TEST_F(CopyInsertionTest, NestedTupleParameter) { ShapeUtil::MakeShape(F32, {42})}), "param0")); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); EXPECT_EQ(HloOpcode::kParameter, @@ -351,7 +351,7 @@ TEST_F(CopyInsertionTest, ElementOfNestedTupleParameter) { auto gte = builder.AddInstruction(HloInstruction::CreateGetTupleElement( ShapeUtil::GetSubshape(param->shape(), {0}), param, 0)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); EXPECT_EQ(gte, module->entry_computation()->root_instruction()); @@ -388,7 +388,7 @@ TEST_F(CopyInsertionTest, AmbiguousTopLevelRoot) { builder.AddInstruction(HloInstruction::CreateGetTupleElement( ShapeUtil::GetSubshape(select->shape(), {0}), select, 0)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); EXPECT_EQ(gte, module->entry_computation()->root_instruction()); @@ -1295,7 +1295,7 @@ TEST_F(WhileCopyInsertionTest, InitPointsToNonDistinctUsedByTwoWhileLoops) { TEST_F(CopyInsertionTest, SwizzlingWhile) { // Test a while instruction with a body which permutes its tuple parameter // elements. - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); const Shape loop_state_shape = ShapeUtil::MakeTupleShape({scalar_shape_, scalar_shape_}); @@ -1362,7 +1362,7 @@ TEST_F(CopyInsertionTest, CrossingParameters) { // | / \ | // | / \| // (p1 , p0) - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); const Shape tuple_shape = ShapeUtil::MakeTupleShape({scalar_shape_, scalar_shape_}); @@ -1395,7 +1395,7 @@ TEST_F(CopyInsertionTest, ParametersAliasing) { // | | // | | // (p0 , p1) - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); const Shape tuple_shape = ShapeUtil::MakeTupleShape({scalar_shape_, scalar_shape_}); @@ -1428,7 +1428,7 @@ TEST_F(CopyInsertionTest, ParameterWithNoAliasing) { // | | // | | // (p0 , p1) - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); const Shape tuple_shape = ShapeUtil::MakeTupleShape({scalar_shape_, scalar_shape_}); @@ -1461,7 +1461,7 @@ TEST_F(CopyInsertionTest, ParameterWithPartialAliasing) { // | | // | | // (p0 , p1) - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); const Shape tuple_shape = ShapeUtil::MakeTupleShape({scalar_shape_, scalar_shape_}); @@ -1496,7 +1496,7 @@ TEST_F(CopyInsertionTest, ParameterAndParallelOpsWithPartialAliasing) { // | | | // | | | // +-- (p0 , p1) - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); const Shape tuple_shape = ShapeUtil::MakeTupleShape({scalar_shape_, scalar_shape_}); @@ -1534,7 +1534,7 @@ TEST_F(CopyInsertionTest, ParameterAndOpsWithPartialAliasing) { // | Add----+ // | | | // +-- (p0 , p1) - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); const Shape tuple_shape = ShapeUtil::MakeTupleShape({scalar_shape_, scalar_shape_}); @@ -1569,7 +1569,7 @@ TEST_F(CopyInsertionTest, SwizzlingWhileWithOneOp) { // the operation (instruction) on the element makes the live range of the // respective input and output elements different than if the instruction were // not there (as in the SwizzlingWhile test above). - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); const Shape loop_state_shape = ShapeUtil::MakeTupleShape({scalar_shape_, scalar_shape_}); @@ -1632,7 +1632,7 @@ TEST_F(CopyInsertionTest, SwizzlingWhileSharedInput) { // the while body is a single constant (both loop state elements are the same // constant). This means no copies are necessary because both loop state // elements are the same so interchanging them is a no-op. - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); const Shape loop_state_shape = ShapeUtil::MakeTupleShape({scalar_shape_, scalar_shape_}); @@ -1693,7 +1693,7 @@ TEST_F(CopyInsertionTest, SequentialWhiles) { const Shape loop_state_shape = ShapeUtil::MakeTupleShape( {element_shape, element_shape, element_shape, element_shape}); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto builder = HloComputation::Builder(TestName()); auto param_0 = builder.AddInstruction( HloInstruction::CreateParameter(0, element_shape, "param_0")); @@ -1783,7 +1783,7 @@ TEST_F(CopyInsertionTest, SequentialWhiles) { TEST_F(CopyInsertionTest, WhileBodyWithConstantRoot) { // Test a while body and condition which are each simply a constant (root of // computation is a constant). The body constant should be copied. - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto builder = HloComputation::Builder(TestName()); auto param_0 = builder.AddInstruction( HloInstruction::CreateParameter(0, scalar_shape_, "param_0")); diff --git a/tensorflow/compiler/xla/service/cpu/BUILD b/tensorflow/compiler/xla/service/cpu/BUILD index 2763d18121..ce4c2a9cc6 100644 --- a/tensorflow/compiler/xla/service/cpu/BUILD +++ b/tensorflow/compiler/xla/service/cpu/BUILD @@ -96,6 +96,7 @@ cc_library( "@com_google_absl//absl/types:span", "//tensorflow/compiler/tf2xla:cpu_function_runtime", "//tensorflow/compiler/xla/service:map_inliner", + "//tensorflow/compiler/xla/service:hlo_get_dimension_size_rewriter", "//tensorflow/compiler/xla/service:scatter_expander", "//tensorflow/compiler/xla:literal", "//tensorflow/compiler/xla:protobuf_util", diff --git a/tensorflow/compiler/xla/service/cpu/compiler_functor.cc b/tensorflow/compiler/xla/service/cpu/compiler_functor.cc index 73b03440cb..796a7cf94d 100644 --- a/tensorflow/compiler/xla/service/cpu/compiler_functor.cc +++ b/tensorflow/compiler/xla/service/cpu/compiler_functor.cc @@ -61,19 +61,6 @@ Disabling these as a starting point. // TODO(b/64227304) Creating a custom pass pipeline will replace this. namespace { -class FilteredFunctionPassManager : public llvm::legacy::FunctionPassManager { - public: - FilteredFunctionPassManager(llvm::Module* m, bool disable_expensive_passes) - : llvm::legacy::FunctionPassManager(m), - disable_expensive_passes_(disable_expensive_passes) {} - void add(llvm::Pass* p) override { - llvm::legacy::FunctionPassManager::add(p); - } - - private: - bool disable_expensive_passes_; -}; - class FilteredPassManager : public llvm::legacy::PassManager { public: explicit FilteredPassManager(bool disable_expensive_passes) @@ -96,8 +83,7 @@ class FilteredPassManager : public llvm::legacy::PassManager { std::unique_ptr CompilerFunctor::operator()( llvm::Module& module) const { FilteredPassManager module_passes(disable_expensive_passes_); - FilteredFunctionPassManager function_passes(&module, - disable_expensive_passes_); + llvm::legacy::FunctionPassManager function_passes(&module); VLOG(2) << "IR before optimizations"; XLA_VLOG_LINES(2, llvm_ir::DumpModuleToString(module)); diff --git a/tensorflow/compiler/xla/service/cpu/cpu_compiler.cc b/tensorflow/compiler/xla/service/cpu/cpu_compiler.cc index 4ce5a8a292..6374822c81 100644 --- a/tensorflow/compiler/xla/service/cpu/cpu_compiler.cc +++ b/tensorflow/compiler/xla/service/cpu/cpu_compiler.cc @@ -76,6 +76,7 @@ limitations under the License. #include "tensorflow/compiler/xla/service/hlo_cse.h" #include "tensorflow/compiler/xla/service/hlo_dce.h" #include "tensorflow/compiler/xla/service/hlo_element_type_converter.h" +#include "tensorflow/compiler/xla/service/hlo_get_dimension_size_rewriter.h" #include "tensorflow/compiler/xla/service/hlo_instruction.h" #include "tensorflow/compiler/xla/service/hlo_memory_scheduler.h" #include "tensorflow/compiler/xla/service/hlo_opcode.h" @@ -268,10 +269,11 @@ Status CpuCompiler::RunHloPassesThroughLayoutAssn( /*rewrite_training_op=*/true, /*rewrite_inference_op=*/true, /*rewrite_grad_op=*/true); - pass.AddPass( - /*is_layout_sensitive=*/false, - [](const Shape&, const Shape&) { return false; }, - /*enable_dot_strength_reduction=*/false); + pipeline.AddPass(); + AlgebraicSimplifierOptions options( + [](const Shape&, const Shape&) { return false; }); + options.set_enable_dot_strength_reduction(false); + pass.AddPass(options); pass.AddPass(); // BatchNormExpander can create zero-sized ops, so zero-sized HLO @@ -334,10 +336,11 @@ Status CpuCompiler::RunHloPassesAfterLayoutAssn( pass.AddInvariantChecker( /*layout_sensitive=*/true, /*allow_mixed_precision=*/false); - pass.AddPass>( - /*is_layout_sensitive=*/true, - [](const Shape&, const Shape&) { return true; }, - /*enable_dot_strength_reduction=*/false); + AlgebraicSimplifierOptions options( + [](const Shape&, const Shape&) { return true; }); + options.set_is_layout_sensitive(true); + options.set_enable_dot_strength_reduction(false); + pass.AddPass>(options); pass.AddPass(); pass.AddPass(/*is_layout_sensitive=*/true); } @@ -587,9 +590,9 @@ StatusOr> CpuCompiler::RunBackend( // Select an order for emitting the HLO instructions for each // computation. Using this sequence enables tighter buffer liveness analysis // and reduced memory usage (as compared to using DependencyHloOrdering). - TF_ASSIGN_OR_RETURN( - HloSchedule schedule, - ScheduleModule(*module, BufferSizeBytesFunction(), DFSMemoryScheduler)); + TF_ASSIGN_OR_RETURN(HloSchedule schedule, + ScheduleModule(module.get(), BufferSizeBytesFunction(), + DFSMemoryScheduler)); // Run buffer allocation on the HLO graph. TF_ASSIGN_OR_RETURN( @@ -779,7 +782,7 @@ CpuCompiler::CompileAheadOfTime(std::unique_ptr module_group, XLA_VLOG_LINES(2, module->ToString()); TF_ASSIGN_OR_RETURN(HloSchedule schedule, - ScheduleModule(*module, BufferSizeBytesFunction())); + ScheduleModule(module, BufferSizeBytesFunction())); // Run buffer analysis on the HLO graph. This analysis figures out which // temporary buffers are required to run the computation. diff --git a/tensorflow/compiler/xla/service/cpu/cpu_executable.cc b/tensorflow/compiler/xla/service/cpu/cpu_executable.cc index 29abf38e43..818b2b0d0d 100644 --- a/tensorflow/compiler/xla/service/cpu/cpu_executable.cc +++ b/tensorflow/compiler/xla/service/cpu/cpu_executable.cc @@ -51,8 +51,7 @@ namespace cpu { CpuExecutable::CpuExecutable( std::unique_ptr jit, std::unique_ptr assignment, - std::unique_ptr hlo_module, - const string& entry_function_name, + std::unique_ptr hlo_module, const string& entry_function_name, std::unique_ptr hlo_profile_printer_data, std::unique_ptr hlo_profile_index_map) : Executable(std::move(hlo_module), std::move(hlo_profile_printer_data), diff --git a/tensorflow/compiler/xla/service/cpu/cpu_executable.h b/tensorflow/compiler/xla/service/cpu/cpu_executable.h index 3c3c047bfe..3b91b15ba9 100644 --- a/tensorflow/compiler/xla/service/cpu/cpu_executable.h +++ b/tensorflow/compiler/xla/service/cpu/cpu_executable.h @@ -49,7 +49,7 @@ class CpuExecutable : public Executable { public: CpuExecutable(std::unique_ptr jit, std::unique_ptr assignment, - std::unique_ptr hlo_module, + std::unique_ptr hlo_module, const string& entry_function_name, std::unique_ptr hlo_profile_printer_data, std::unique_ptr hlo_profile_index_map); diff --git a/tensorflow/compiler/xla/service/cpu/cpu_instruction_fusion.cc b/tensorflow/compiler/xla/service/cpu/cpu_instruction_fusion.cc index f9cd61bea3..6f79ad7c14 100644 --- a/tensorflow/compiler/xla/service/cpu/cpu_instruction_fusion.cc +++ b/tensorflow/compiler/xla/service/cpu/cpu_instruction_fusion.cc @@ -48,10 +48,15 @@ bool IsMatrixVectorDot(const HloInstruction* hlo) { (hlo_shape.dimensions(0) == 1 || hlo_shape.dimensions(1) == 1); } +bool HasExactlyOneUse(const HloInstruction& hlo_instr) { + return hlo_instr.user_count() == 1 && + absl::c_count(hlo_instr.users().front()->operands(), &hlo_instr) == 1; +} + bool CanBeOutputFused(const HloInstruction* producer, const HloInstruction* consumer) { return consumer->opcode() == HloOpcode::kAdd && IsMatrixVectorDot(producer) && - producer->user_count() == 1; + HasExactlyOneUse(*producer) == 1; } bool CanBeOutputFusedIntoSomeOperand(const HloInstruction* consumer) { diff --git a/tensorflow/compiler/xla/service/cpu/cpu_instruction_fusion_test.cc b/tensorflow/compiler/xla/service/cpu/cpu_instruction_fusion_test.cc index c95a514ca0..527df0bd1c 100644 --- a/tensorflow/compiler/xla/service/cpu/cpu_instruction_fusion_test.cc +++ b/tensorflow/compiler/xla/service/cpu/cpu_instruction_fusion_test.cc @@ -321,7 +321,7 @@ TEST_F(OpcodeFusionTest, Exponential_Reshape_Negate) { builder.AddInstruction( HloInstruction::CreateUnary(result_shape, HloOpcode::kNegate, reshape2)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); RunFusionAndCheckOpcodesWereFused( @@ -370,7 +370,7 @@ TEST_F(OpcodeFusionTest, Broadcast_Negate) { builder.AddInstruction(HloInstruction::CreateUnary( result_shape, HloOpcode::kNegate, broadcast1)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); RunFusionAndCheckOpcodesWereFused( @@ -410,7 +410,7 @@ TEST_F(OpcodeFusionTest, Exponential_Negate) { builder.AddInstruction( HloInstruction::CreateUnary(param_shape, HloOpcode::kNegate, exp1)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); RunFusionAndCheckOpcodesWereFused( @@ -429,7 +429,7 @@ TEST_F(OpcodeFusionTest, Reshape_Negate) { builder.AddInstruction( HloInstruction::CreateUnary(result_shape, HloOpcode::kNegate, reshape1)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); RunFusionAndCheckOpcodesWereFused( @@ -447,7 +447,7 @@ TEST_F(OpcodeFusionTest, Reverse_Negate) { builder.AddInstruction( HloInstruction::CreateUnary(param_shape, HloOpcode::kNegate, reverse1)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); RunFusionAndCheckOpcodesWereFused( @@ -489,7 +489,7 @@ TEST_F(OpcodeFusionTest, Exponential_Transpose_Negate) { builder.AddInstruction(HloInstruction::CreateUnary( result_shape, HloOpcode::kNegate, transpose2)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); RunFusionAndCheckOpcodesWereFused( @@ -498,7 +498,7 @@ TEST_F(OpcodeFusionTest, Exponential_Transpose_Negate) { } TEST_F(OpcodeFusionTest, UnaryMapOfExp) { - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); HloComputation::Builder builder(TestName()); Shape shape = ShapeUtil::MakeShape(F32, {3, 4}); @@ -517,7 +517,7 @@ TEST_F(OpcodeFusionTest, UnaryMapOfExp) { } TEST_F(OpcodeFusionTest, BinaryMapOfExps) { - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); HloComputation::Builder builder(TestName()); Shape shape = ShapeUtil::MakeShape(F32, {3, 4}); @@ -542,7 +542,7 @@ TEST_F(OpcodeFusionTest, BinaryMapOfExps) { } TEST_F(OpcodeFusionTest, DynamicSliceWithDynamicUpdateSlice) { - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); HloComputation::Builder builder(TestName()); Shape full_shape = ShapeUtil::MakeShape(F32, {10, 100, 1000}); @@ -573,7 +573,7 @@ TEST_F(OpcodeFusionTest, DynamicSliceWithDynamicUpdateSlice) { } TEST_F(OpcodeFusionTest, MessOfFusibleNodes) { - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); HloComputation::Builder builder(TestName()); Shape full_shape = ShapeUtil::MakeShape(F32, {4, 100, 10, 100, 50}); @@ -712,7 +712,7 @@ void CreateComputationForDotAddOutputFusionTest(const string& test_name, } TEST_F(OpcodeFusionTest, DotAddOutputFusion_1x50x19) { - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); CreateComputationForDotAddOutputFusionTest(TestName(), module.get(), /*m=*/1, /*k=*/50, /*n=*/19, /*add_extra_use_for_dot=*/false); @@ -725,7 +725,7 @@ TEST_F(OpcodeFusionTest, DotAddOutputFusion_1x50x19) { } TEST_F(OpcodeFusionTest, DotAddOutputFusion_19x50x1) { - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); CreateComputationForDotAddOutputFusionTest(TestName(), module.get(), /*m=*/19, /*k=*/50, /*n=*/1, /*add_extra_use_for_dot=*/false); @@ -738,7 +738,7 @@ TEST_F(OpcodeFusionTest, DotAddOutputFusion_19x50x1) { } TEST_F(OpcodeFusionTest, DotAddOutputFusion_19x50x19) { - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); CreateComputationForDotAddOutputFusionTest(TestName(), module.get(), /*m=*/19, /*k=*/50, /*n=*/19, /*add_extra_use_for_dot=*/false); @@ -751,7 +751,7 @@ TEST_F(OpcodeFusionTest, DotAddOutputFusion_19x50x19) { } TEST_F(OpcodeFusionTest, DotAddOutputFusion_19x50x1_multi_use) { - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); CreateComputationForDotAddOutputFusionTest(TestName(), module.get(), /*m=*/19, /*k=*/50, /*n=*/1, /*add_extra_use_for_dot=*/true); @@ -763,6 +763,28 @@ TEST_F(OpcodeFusionTest, DotAddOutputFusion_19x50x1_multi_use) { Not(op::Fusion())); } +TEST_F(InstructionFusionTest, + DotOperationFusion_DontOutputFuseDuplicateOperands) { + absl::string_view module_string = R"( +HloModule module + +ENTRY main { + a = f32[50,60]{1,0} parameter(0) + b = f32[60,1]{1,0} parameter(1) + c = f32[50,1]{1,0} dot(a, b), lhs_contracting_dims={1}, rhs_contracting_dims={0} + ROOT d = f32[50,1]{1,0} add(c, c) +} +)"; + + TF_ASSERT_OK_AND_ASSIGN(std::unique_ptr module, + ParseAndReturnVerifiedModule(module_string)); + TF_ASSERT_OK_AND_ASSIGN(bool fused_something, + CpuInstructionFusion().Run(module.get())); + EXPECT_FALSE(fused_something); + EXPECT_THAT(module->entry_computation()->root_instruction(), + Not(op::Fusion())); +} + struct GatherLoopFusionTestSpec { string test_name; string hlo_computation_text; diff --git a/tensorflow/compiler/xla/service/cpu/cpu_layout_assignment_test.cc b/tensorflow/compiler/xla/service/cpu/cpu_layout_assignment_test.cc index 2cd52e4a18..6c61b64758 100644 --- a/tensorflow/compiler/xla/service/cpu/cpu_layout_assignment_test.cc +++ b/tensorflow/compiler/xla/service/cpu/cpu_layout_assignment_test.cc @@ -73,7 +73,7 @@ TEST_F(CpuLayoutAssignmentTest, DotWithConstantRhsTensor) { auto result = builder.AddInstruction( CreateCanonicalDot(result_shape, dot_lhs, dot_rhs)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); HloComputation* computation = module->AddEntryComputation(builder.Build()); ComputationLayout computation_layout(computation->ComputeProgramShape()); @@ -114,7 +114,7 @@ TEST_F(CpuLayoutAssignmentTest, MultipleDotsWithSameConstantRhsTensor0) { builder.AddInstruction(HloInstruction::CreateBinary( result_shape, HloOpcode::kAdd, dot_a_result, dot_b_result)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); HloComputation* computation = module->AddEntryComputation(builder.Build()); ComputationLayout computation_layout(computation->ComputeProgramShape()); @@ -158,7 +158,7 @@ TEST_F(CpuLayoutAssignmentTest, MultipleDotsWithSameConstantRhsTensor1) { auto tuple_result = builder.AddInstruction( HloInstruction::CreateTuple({dot_a_result, dot_b_result})); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); HloComputation* computation = module->AddEntryComputation(builder.Build()); ComputationLayout computation_layout(computation->ComputeProgramShape()); @@ -192,7 +192,7 @@ TEST_F(CpuLayoutAssignmentTest, DotWithConstantLhsTensor) { auto dot_result = builder.AddInstruction( CreateCanonicalDot(result_shape, dot_lhs, dot_rhs)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); HloComputation* computation = module->AddEntryComputation(builder.Build()); ComputationLayout computation_layout(computation->ComputeProgramShape()); @@ -232,7 +232,7 @@ TEST_F(CpuLayoutAssignmentTest, DotWithConstantRhsTensorThroughGTE) { auto dot_result = builder.AddInstruction( CreateCanonicalDot(result_shape, dot_lhs, dot_rhs)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); HloComputation* computation = module->AddEntryComputation(builder.Build()); ComputationLayout computation_layout(computation->ComputeProgramShape()); @@ -353,7 +353,7 @@ static void AssertCorrectLayoutForDotOutputFusion( } TEST_F(CpuLayoutAssignmentTest, DotOutputFusion_1x50x19_dot_idx_0) { - std::unique_ptr module = CreateNewUnverifiedModule(); + std::unique_ptr module = CreateNewVerifiedModule(); TF_ASSERT_OK_AND_ASSIGN( DotOutputFusionLayoutAssignmentResult layout_assignment_result, RunDotOutputFusion(module.get(), TestName(), /*m=*/1, /*k=*/50, /*n=*/19, @@ -365,7 +365,7 @@ TEST_F(CpuLayoutAssignmentTest, DotOutputFusion_1x50x19_dot_idx_0) { } TEST_F(CpuLayoutAssignmentTest, DotOutputFusion_1x50x19_dot_idx_1) { - std::unique_ptr module = CreateNewUnverifiedModule(); + std::unique_ptr module = CreateNewVerifiedModule(); TF_ASSERT_OK_AND_ASSIGN( DotOutputFusionLayoutAssignmentResult layout_assignment_result, RunDotOutputFusion(module.get(), TestName(), /*m=*/1, /*k=*/50, /*n=*/19, @@ -377,7 +377,7 @@ TEST_F(CpuLayoutAssignmentTest, DotOutputFusion_1x50x19_dot_idx_1) { } TEST_F(CpuLayoutAssignmentTest, DotOutputFusion_19x50x1_dot_idx_0) { - std::unique_ptr module = CreateNewUnverifiedModule(); + std::unique_ptr module = CreateNewVerifiedModule(); TF_ASSERT_OK_AND_ASSIGN( DotOutputFusionLayoutAssignmentResult layout_assignment_result, RunDotOutputFusion(module.get(), TestName(), /*m=*/19, /*k=*/50, /*n=*/1, @@ -389,7 +389,7 @@ TEST_F(CpuLayoutAssignmentTest, DotOutputFusion_19x50x1_dot_idx_0) { } TEST_F(CpuLayoutAssignmentTest, DotOutputFusion_19x50x1_dot_idx_1) { - std::unique_ptr module = CreateNewUnverifiedModule(); + std::unique_ptr module = CreateNewVerifiedModule(); TF_ASSERT_OK_AND_ASSIGN( DotOutputFusionLayoutAssignmentResult layout_assignment_result, RunDotOutputFusion(module.get(), TestName(), /*m=*/19, /*k=*/50, /*n=*/1, @@ -401,7 +401,7 @@ TEST_F(CpuLayoutAssignmentTest, DotOutputFusion_19x50x1_dot_idx_1) { } TEST_F(CpuLayoutAssignmentTest, DotOutputFusion_19x50x19_dot_idx_0) { - std::unique_ptr module = CreateNewUnverifiedModule(); + std::unique_ptr module = CreateNewVerifiedModule(); TF_ASSERT_OK_AND_ASSIGN( DotOutputFusionLayoutAssignmentResult layout_assignment_result, RunDotOutputFusion(module.get(), TestName(), /*m=*/19, /*k=*/50, /*n=*/19, @@ -413,7 +413,7 @@ TEST_F(CpuLayoutAssignmentTest, DotOutputFusion_19x50x19_dot_idx_0) { } TEST_F(CpuLayoutAssignmentTest, DotOutputFusion_19x50x19_dot_idx_1) { - std::unique_ptr module = CreateNewUnverifiedModule(); + std::unique_ptr module = CreateNewVerifiedModule(); TF_ASSERT_OK_AND_ASSIGN( DotOutputFusionLayoutAssignmentResult layout_assignment_result, RunDotOutputFusion(module.get(), TestName(), /*m=*/19, /*k=*/50, /*n=*/19, diff --git a/tensorflow/compiler/xla/service/cpu/cpu_options.cc b/tensorflow/compiler/xla/service/cpu/cpu_options.cc index b8ace57026..92debb83e3 100644 --- a/tensorflow/compiler/xla/service/cpu/cpu_options.cc +++ b/tensorflow/compiler/xla/service/cpu/cpu_options.cc @@ -22,7 +22,6 @@ limitations under the License. namespace { const char* const kXlaOptimizeForSizeCpuOption = "xla_cpu_optimize_for_size"; -const char* const kXlaDisableVectorizedReduce = "xla_disable_vectorized_reduce"; const char* const kLlvmIrDotTilingFactor = "xla_llvm_dot_tiling_factor"; const char* const kXlaEnableExperimentalLlvmIrGemm = "xla_enable_experimental_llvm_ir_gemm"; diff --git a/tensorflow/compiler/xla/service/cpu/ir_emitter.cc b/tensorflow/compiler/xla/service/cpu/ir_emitter.cc index 620c45fa39..4032c2da2f 100644 --- a/tensorflow/compiler/xla/service/cpu/ir_emitter.cc +++ b/tensorflow/compiler/xla/service/cpu/ir_emitter.cc @@ -111,7 +111,7 @@ IrEmitter::IrEmitter( StatusOr IrEmitter::EmitComputation( HloComputation* computation, const string& function_name_prefix, bool is_top_level_computation, - const std::vector* instruction_order) { + const std::vector* instruction_order) { string function_name = name_uniquer_.GetUniqueName(function_name_prefix); VLOG(2) << "Emitting IR for CPU function [" << function_name_prefix << "]; ordered? " << (instruction_order != nullptr); @@ -140,7 +140,7 @@ StatusOr IrEmitter::EmitComputation( // readcyclecounter if it is unavailable. bool use_rdtscp = arch_type_ == llvm::Triple::ArchType::x86 || arch_type_ == llvm::Triple::ArchType::x86_64; - profiling_state_ = ProfilingState(use_rdtscp, GetProfileCountersArgument()); + profiling_state_ = ProfilingState(use_rdtscp); if (instruction_order == nullptr) { TF_RETURN_IF_ERROR(computation->Accept(this)); } else { @@ -1379,33 +1379,6 @@ Status IrEmitter::HandleCrossReplicaSum(HloInstruction* crs) { return Status::OK(); } -// Fills up the free variables in 'index_with_free_var' with values from -// 'filler_index'. The size of free variables must be the same as the -// size of 'filler_index'. -// -// This is often used after dimension reduction, where -// 'index_with_free_var' has one or more dimensions reduced, which serves as -// free variables (represented as nullptr). For example, if we have a 4 -// dimensional input and index for the dimension being reduced is -// 2 (third dimension), we will have an index like [i, j, NULL, k] -// after reduced dimension. -// -// Here we fill up that free variable by 'filler_index', which contains -// the value in the reduced dimension. -static llvm_ir::IrArray::Index FillReducedDimensionIndex( - llvm_ir::IrArray::Index index_with_free_var, - llvm_ir::IrArray::Index filler_index) { - llvm_ir::IrArray::Index::const_iterator it = filler_index.begin(); - - for (size_t i = 0; i < index_with_free_var.size(); ++i) { - if (index_with_free_var[i] == nullptr) { - index_with_free_var[i] = *it++; - } - } - CHECK(filler_index.end() == it); - return index_with_free_var; -} - Status IrEmitter::HandleParameter(HloInstruction* parameter) { VLOG(2) << "HandleParameter: " << parameter->ToString(); return EmitTargetAddressForOp(parameter); @@ -2194,14 +2167,6 @@ Status IrEmitter::HandlePad(HloInstruction* pad) { return Status::OK(); } -// If `hlo` is a Transpose, returns its operand; otherwise returns `hlo` itself. -static const HloInstruction* StripTranspose(const HloInstruction& hlo) { - if (hlo.IsRank2Transpose()) { - return hlo.operand(0); - } - return &hlo; -} - Status IrEmitter::HandleFusion(HloInstruction* fusion) { auto* root = fusion->fused_expression_root(); if (llvm_ir::CanEmitFusedDynamicUpdateSliceInPlace(fusion, assignment_)) { @@ -2600,10 +2565,17 @@ Status IrEmitter::HandleConditional(HloInstruction* conditional) { return Status::OK(); } -Status IrEmitter::HandleAfterAll(HloInstruction* gen_token) { - TF_RET_CHECK(ByteSizeOf(gen_token->shape()) == 0); +Status IrEmitter::HandleAfterAll(HloInstruction* after_all) { + TF_RET_CHECK(ByteSizeOf(after_all->shape()) == 0); // No code to generate, but we need to emit an address for book-keeping. - TF_RETURN_IF_ERROR(EmitTargetAddressForOp(gen_token)); + TF_RETURN_IF_ERROR(EmitTargetAddressForOp(after_all)); + return Status::OK(); +} + +Status IrEmitter::HandleAddDependency(HloInstruction* add_dependency) { + // AddDedendency just forwards its zero-th operand. + emitted_value_[add_dependency] = + GetEmittedValueFor(add_dependency->operand(0)); return Status::OK(); } diff --git a/tensorflow/compiler/xla/service/cpu/ir_emitter.h b/tensorflow/compiler/xla/service/cpu/ir_emitter.h index 136b88ff75..559a8162a2 100644 --- a/tensorflow/compiler/xla/service/cpu/ir_emitter.h +++ b/tensorflow/compiler/xla/service/cpu/ir_emitter.h @@ -101,7 +101,7 @@ class IrEmitter : public DfsHloVisitorWithDefault, StatusOr EmitComputation( HloComputation* computation, const string& function_name_prefix, bool is_top_level_computation, - const std::vector* instruction_order); + const std::vector* instruction_order); llvm::IRBuilder<>* b() { return &b_; } @@ -159,7 +159,8 @@ class IrEmitter : public DfsHloVisitorWithDefault, Status HandleConcatenate(HloInstruction* concatenate) override; Status HandleConditional(HloInstruction* conditional) override; Status HandleScatter(HloInstruction* scatter) override; - Status HandleAfterAll(HloInstruction* gen_token) override; + Status HandleAfterAll(HloInstruction* after_all) override; + Status HandleAddDependency(HloInstruction* add_dependency) override; Status HandleRng(HloInstruction* rng) override; Status FinishVisit(HloInstruction* root) override; @@ -467,9 +468,8 @@ class IrEmitter : public DfsHloVisitorWithDefault, // profiling a computation. class ProfilingState { public: - ProfilingState() : use_rdtscp_(false), prof_counters_(nullptr) {} - ProfilingState(bool use_rdtscp, llvm::Value* prof_counters) - : use_rdtscp_(use_rdtscp), prof_counters_(prof_counters) {} + ProfilingState() : use_rdtscp_(false) {} + explicit ProfilingState(bool use_rdtscp) : use_rdtscp_(use_rdtscp) {} // Record the cycle counter before an HLO executes. void RecordCycleStart(llvm::IRBuilder<>* b, HloInstruction* hlo); @@ -494,9 +494,6 @@ class IrEmitter : public DfsHloVisitorWithDefault, // intrinsic? bool use_rdtscp_; - // The argument which corresponds to the profile counter buffer. - llvm::Value* prof_counters_; - // The first read cycle counter in the program. llvm::Value* first_read_cycle_start_ = nullptr; diff --git a/tensorflow/compiler/xla/service/cpu/runtime_key_value_sort.cc b/tensorflow/compiler/xla/service/cpu/runtime_key_value_sort.cc index 669eeb95f3..722aa3120e 100644 --- a/tensorflow/compiler/xla/service/cpu/runtime_key_value_sort.cc +++ b/tensorflow/compiler/xla/service/cpu/runtime_key_value_sort.cc @@ -17,6 +17,7 @@ limitations under the License. #include #include #include +#include #include #include #include @@ -41,61 +42,60 @@ void KeyValueSort(std::pair* row_to_sort, int64 num_elements) { std::sort(row_to_sort, row_to_sort + num_elements); } -// For floating point numbers, we want a total order comparator. -NaN and NaN -// should appear at the beginning and end of the ordering, and -0.0 should -// appear before 0.0. Also we want to have a stable sort, so if the keys are the -// same, we compare the index values. -template -bool LessThan(KeyType lhs, int64 lhs_index, KeyType rhs, int64 rhs_index) { - bool lhs_is_negative = std::signbit(lhs); - bool rhs_is_negative = std::signbit(rhs); - // If the signs are different, we can just compare the signs. - if (lhs_is_negative != rhs_is_negative) { - return lhs_is_negative && !rhs_is_negative; - } - bool lhs_nan = std::isnan(lhs); - bool rhs_nan = std::isnan(rhs); - // Exactly one number is nan? - if (lhs_nan != rhs_nan) { - if (lhs_nan) { - return lhs_is_negative; - } - return !rhs_is_negative; +// We would like a total order of floating point numbers so that the +// sort has a predictable behavior in the presence of NaNs. Rather +// than using floating point comparison, we use the following trick: +// If f is a float, and +// x = bit_cast(f); +// y = x < 0 ? 0x7FFFFFFF - x : x; +// then y is ordered as an int32 such that finite values have the +// obvious order, -0 is ordered before 0, and -NaN and NaN appear at +// the beginning and end of the ordering. +template +CastType Convert(KeyType value) { + CastType casted_value; + memcpy(&casted_value, &value, sizeof(CastType)); + if (casted_value < 0) { + return static_cast(std::numeric_limits::max()) - + casted_value; } - if (lhs != rhs) { - return lhs < rhs; - } - return lhs_index < rhs_index; + return casted_value; +} + +template +bool LessThan(KeyType lhs, KeyType rhs) { + return Convert(lhs) < + Convert(rhs); } template <> void KeyValueSort(std::pair* row_to_sort, int64 num_elements) { - std::sort(row_to_sort, row_to_sort + num_elements, - [](const std::pair& lhs, - const std::pair& rhs) -> bool { - return LessThan(lhs.first, lhs.second, rhs.first, rhs.second); - }); + std::stable_sort(row_to_sort, row_to_sort + num_elements, + [](const std::pair& lhs, + const std::pair& rhs) -> bool { + return LessThan(lhs.first, rhs.first); + }); } template <> void KeyValueSort(std::pair* row_to_sort, int64 num_elements) { - std::sort(row_to_sort, row_to_sort + num_elements, - [](const std::pair& lhs, - const std::pair& rhs) -> bool { - return LessThan(lhs.first, lhs.second, rhs.first, rhs.second); - }); + std::stable_sort(row_to_sort, row_to_sort + num_elements, + [](const std::pair& lhs, + const std::pair& rhs) -> bool { + return LessThan(lhs.first, rhs.first); + }); } template <> void KeyValueSort(std::pair* row_to_sort, int64 num_elements) { - std::sort(row_to_sort, row_to_sort + num_elements, - [](const std::pair& lhs, - const std::pair& rhs) -> bool { - return LessThan( - Eigen::half_impl::half_to_float(lhs.first), lhs.second, - Eigen::half_impl::half_to_float(rhs.first), rhs.second); - }); + std::stable_sort(row_to_sort, row_to_sort + num_elements, + [](const std::pair& lhs, + const std::pair& rhs) -> bool { + return LessThan( + Eigen::half_impl::half_to_float(lhs.first), + Eigen::half_impl::half_to_float(rhs.first)); + }); } template diff --git a/tensorflow/compiler/xla/service/cpu/simple_orc_jit.cc b/tensorflow/compiler/xla/service/cpu/simple_orc_jit.cc index f77641eb7d..efccadedf2 100644 --- a/tensorflow/compiler/xla/service/cpu/simple_orc_jit.cc +++ b/tensorflow/compiler/xla/service/cpu/simple_orc_jit.cc @@ -128,8 +128,18 @@ SimpleOrcJIT::SimpleOrcJIT(const llvm::TargetOptions& target_options, } llvm::JITSymbol SimpleOrcJIT::ResolveRuntimeSymbol(const std::string& name) { - void* func_addr = CustomCallTargetRegistry::Global()->Lookup(name); + void* func_addr = nullptr; + if (name.size() > 1 && name.front() == data_layout_.getGlobalPrefix()) { + // On Mac OS X, 'name' may have a leading underscore prefix, even though the + // registered name may not. + std::string stripped_name(name.begin() + 1, name.end()); + func_addr = CustomCallTargetRegistry::Global()->Lookup(stripped_name); + } else { + func_addr = CustomCallTargetRegistry::Global()->Lookup(name); + } + if (func_addr == nullptr) { + VLOG(2) << "Unable to resolve runtime symbol: " << name; return nullptr; } llvm::JITEvaluatedSymbol symbol_info(reinterpret_cast(func_addr), diff --git a/tensorflow/compiler/xla/service/cpu/tests/cpu_eigen_dot_operation_test.cc b/tensorflow/compiler/xla/service/cpu/tests/cpu_eigen_dot_operation_test.cc index 691b3c7bee..f8f5f392da 100644 --- a/tensorflow/compiler/xla/service/cpu/tests/cpu_eigen_dot_operation_test.cc +++ b/tensorflow/compiler/xla/service/cpu/tests/cpu_eigen_dot_operation_test.cc @@ -50,7 +50,7 @@ class CpuEigenDotOperationTest /*entry_point_name=*/"entry", /*relocation_model=*/CpuAotCompilationOptions::RelocationModel::Static}; - auto hlo_module = CreateNewUnverifiedModule(); + auto hlo_module = CreateNewVerifiedModule(); hlo_module->AddEntryComputation(std::move(entry_computation)); CompileAheadOfTimeAndVerifyIr(std::move(hlo_module), options, diff --git a/tensorflow/compiler/xla/service/cpu/tests/cpu_external_constants_test.cc b/tensorflow/compiler/xla/service/cpu/tests/cpu_external_constants_test.cc index d201a151d7..e30f95311f 100644 --- a/tensorflow/compiler/xla/service/cpu/tests/cpu_external_constants_test.cc +++ b/tensorflow/compiler/xla/service/cpu/tests/cpu_external_constants_test.cc @@ -46,7 +46,7 @@ class CpuExternalConstantsTest : public CpuCodegenTest { builder.AddInstruction( HloInstruction::CreateBinary(shape, HloOpcode::kAdd, param, constant)); - std::unique_ptr module = CreateNewUnverifiedModule(); + std::unique_ptr module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); CompileAndVerifyIr(std::move(module), filecheck_pattern, diff --git a/tensorflow/compiler/xla/service/cpu/tests/cpu_intrinsic_test.cc b/tensorflow/compiler/xla/service/cpu/tests/cpu_intrinsic_test.cc index 773336c7a9..9b10c49f4f 100644 --- a/tensorflow/compiler/xla/service/cpu/tests/cpu_intrinsic_test.cc +++ b/tensorflow/compiler/xla/service/cpu/tests/cpu_intrinsic_test.cc @@ -91,7 +91,7 @@ TEST_P(CpuUnaryIntrinsicTest, DoIt) { /*entry_point_name=*/"entry", /*relocation_model=*/CpuAotCompilationOptions::RelocationModel::Static}; - auto hlo_module = CreateNewUnverifiedModule(); + auto hlo_module = CreateNewVerifiedModule(); hlo_module->AddEntryComputation(std::move(computation)); string check_lines{spec.check_lines.data(), spec.check_lines.size()}; diff --git a/tensorflow/compiler/xla/service/cpu/tests/cpu_literal_caching_test.cc b/tensorflow/compiler/xla/service/cpu/tests/cpu_literal_caching_test.cc index 3b87683fff..fa0e09ff6b 100644 --- a/tensorflow/compiler/xla/service/cpu/tests/cpu_literal_caching_test.cc +++ b/tensorflow/compiler/xla/service/cpu/tests/cpu_literal_caching_test.cc @@ -63,7 +63,7 @@ CHECK-NOT: private constant [48 x i8] )"; TF_ASSERT_OK_AND_ASSIGN(std::unique_ptr module, - ParseHloString(hlo_text)); + ParseAndReturnVerifiedModule(hlo_text)); CpuAotCompilationOptions options{ /*triple=*/"x86_64-pc-linux", /*cpu_name=*/"", /*features=*/"", @@ -104,14 +104,14 @@ ENTRY main { )"; string filecheck_pattern = R"( -CHECK: private constant [4 x i8] -CHECK: private constant [8 x i8] +CHECK-DAG: private constant [4 x i8] +CHECK-DAG: private constant [8 x i8] CHECK-NOT: private constant [4 x i8] CHECK-NOT: private constant [8 x i8] )"; TF_ASSERT_OK_AND_ASSIGN(std::unique_ptr module, - ParseHloString(hlo_text)); + ParseAndReturnVerifiedModule(hlo_text)); CpuAotCompilationOptions options{ /*triple=*/"x86_64-pc-linux", /*cpu_name=*/"", /*features=*/"", diff --git a/tensorflow/compiler/xla/service/cpu/tests/cpu_noalias_test.cc b/tensorflow/compiler/xla/service/cpu/tests/cpu_noalias_test.cc index f5419b7063..a7702c2aee 100644 --- a/tensorflow/compiler/xla/service/cpu/tests/cpu_noalias_test.cc +++ b/tensorflow/compiler/xla/service/cpu/tests/cpu_noalias_test.cc @@ -56,7 +56,7 @@ TEST_F(CpuNoAliasTest, Concat) { std::unique_ptr computation = builder.Build(); - auto hlo_module = CreateNewUnverifiedModule(); + auto hlo_module = CreateNewVerifiedModule(); hlo_module->AddEntryComputation(std::move(computation)); // Now that we have an HLO module, build an llvm_ir::AliasAnalysis for it. diff --git a/tensorflow/compiler/xla/service/cpu/xfeed_manager.h b/tensorflow/compiler/xla/service/cpu/xfeed_manager.h index 990ff94ba2..70008947f3 100644 --- a/tensorflow/compiler/xla/service/cpu/xfeed_manager.h +++ b/tensorflow/compiler/xla/service/cpu/xfeed_manager.h @@ -23,6 +23,7 @@ limitations under the License. #include #include "absl/types/span.h" +#include "tensorflow/compiler/xla/shape.h" #include "tensorflow/compiler/xla/statusor.h" #include "tensorflow/compiler/xla/types.h" #include "tensorflow/compiler/xla/xla_data.pb.h" diff --git a/tensorflow/compiler/xla/service/dfs_hlo_visitor.h b/tensorflow/compiler/xla/service/dfs_hlo_visitor.h index d637128322..e84bf00153 100644 --- a/tensorflow/compiler/xla/service/dfs_hlo_visitor.h +++ b/tensorflow/compiler/xla/service/dfs_hlo_visitor.h @@ -251,6 +251,7 @@ class DfsHloVisitorBase { virtual Status HandleBatchNormGrad(HloInstructionPtr hlo) = 0; + virtual Status HandleAddDependency(HloInstructionPtr add_dependency) = 0; virtual Status HandleAfterAll(HloInstructionPtr token) = 0; // Invoked to inform the visitor that the traversal has completed, and that diff --git a/tensorflow/compiler/xla/service/dfs_hlo_visitor_with_default.h b/tensorflow/compiler/xla/service/dfs_hlo_visitor_with_default.h index e57184f639..80ea5be298 100644 --- a/tensorflow/compiler/xla/service/dfs_hlo_visitor_with_default.h +++ b/tensorflow/compiler/xla/service/dfs_hlo_visitor_with_default.h @@ -206,6 +206,9 @@ class DfsHloVisitorWithDefaultBase Status HandleGetDimensionSize(HloInstructionPtr get_size) override { return DefaultAction(get_size); } + Status HandleAddDependency(HloInstructionPtr add_dependency) override { + return DefaultAction(add_dependency); + } // Invoked to inform the visitor that the traversal has completed, and that // the root was "root". diff --git a/tensorflow/compiler/xla/service/dynamic_parameter_binding.cc b/tensorflow/compiler/xla/service/dynamic_parameter_binding.cc new file mode 100644 index 0000000000..c8bfc89050 --- /dev/null +++ b/tensorflow/compiler/xla/service/dynamic_parameter_binding.cc @@ -0,0 +1,138 @@ +/* Copyright 2018 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/compiler/xla/service/dynamic_parameter_binding.h" +#include "tensorflow/compiler/xla/service/hlo_computation.h" +#include "tensorflow/compiler/xla/service/hlo_instruction.h" +#include "tensorflow/compiler/xla/service/hlo_module.h" + +namespace xla { + +Status DynamicParameterBinding::Bind( + const DynamicParameter& dynamic_parameter, + const DynamicDimension& dynamic_dimension) { + auto result = bindings_.emplace(dynamic_dimension, dynamic_parameter); + TF_RET_CHECK(result.second); + return Status::OK(); +} + +absl::optional +DynamicParameterBinding::GetBinding(const DynamicDimension& dynamic_dimension) { + auto param_iter = bindings_.find(dynamic_dimension); + if (param_iter == bindings_.end()) { + return absl::nullopt; + } + return param_iter->second; +} + +DynamicParameterBindingProto DynamicParameterBinding::ToProto() const { + DynamicParameterBindingProto result; + for (const auto& binding : bindings_) { + const DynamicDimension& dynamic_dimension = binding.first; + const DynamicParameter& dynamic_param = binding.second; + DynamicParameterBindingProto::Binding binding_proto; + binding_proto.set_dynamic_param_num(dynamic_param.parameter_num); + for (int64 i : dynamic_param.parameter_index) { + binding_proto.add_dynamic_param_index(i); + } + + binding_proto.set_target_param_num(dynamic_dimension.parameter_num); + + for (int64 i : dynamic_dimension.parameter_index) { + binding_proto.add_target_param_index(i); + } + + binding_proto.set_target_param_dim_num(dynamic_dimension.dimension); + result.add_entries()->Swap(&binding_proto); + } + return result; +} + +StatusOr DynamicParameterBinding::CreateFromProto( + const DynamicParameterBindingProto& proto) { + DynamicParameterBinding result; + for (const DynamicParameterBindingProto::Binding& binding : proto.entries()) { + int64 dynamic_param_num = binding.dynamic_param_num(); + ShapeIndex dynamic_param_index(binding.dynamic_param_index().begin(), + binding.dynamic_param_index().end()); + int64 target_param_num = binding.target_param_num(); + ShapeIndex target_param_index(binding.target_param_index().begin(), + binding.target_param_index().end()); + int64 target_dim_num = binding.target_param_num(); + + TF_RETURN_IF_ERROR( + result.Bind(DynamicParameter{dynamic_param_num, dynamic_param_index}, + DynamicDimension{target_param_num, target_param_index, + target_dim_num})); + } + + return result; +} + +string DynamicParameterBinding::ToString() const { + std::vector pieces; + pieces.push_back("DynamicParameterBinding: "); + for (const auto& binding : bindings_) { + const DynamicDimension& dynamic_dimension = binding.first; + const DynamicParameter& dynamic_param = binding.second; + pieces.push_back(absl::StrFormat( + " -- Input param number %lld at %s has dim %lld as dynamic" + " dimension, which is represented by param number %lld at " + "%s", + dynamic_dimension.parameter_num, + dynamic_dimension.parameter_index.ToString(), + dynamic_dimension.dimension, dynamic_param.parameter_num, + dynamic_param.parameter_index.ToString())); + } + return absl::StrJoin(pieces, "\n"); +} + +Status DynamicParameterBinding::ForEachBinding(BindingFn fn) const { + for (const auto& binding : bindings_) { + TF_RETURN_IF_ERROR(fn(binding.second, binding.first)); + } + return Status::OK(); +} + +Status DynamicParameterBinding::Verify(const HloModule& module) const { + const HloComputation* entry = module.entry_computation(); + return ForEachBinding([&](const DynamicParameter& dynamic_parameter, + const DynamicDimension& dynamic_dimension) + -> Status { + TF_RET_CHECK(dynamic_parameter.parameter_num < entry->num_parameters()); + TF_RET_CHECK(dynamic_dimension.parameter_num < entry->num_parameters()); + TF_RET_CHECK(ShapeUtil::IndexIsValid( + entry->parameter_instruction(dynamic_parameter.parameter_num)->shape(), + dynamic_parameter.parameter_index)); + TF_RET_CHECK(ShapeUtil::IndexIsValid( + entry->parameter_instruction(dynamic_dimension.parameter_num)->shape(), + dynamic_dimension.parameter_index)); + TF_RET_CHECK( + dynamic_dimension.dimension < + ShapeUtil::Rank(ShapeUtil::GetSubshape( + entry->parameter_instruction(dynamic_dimension.parameter_num) + ->shape(), + dynamic_dimension.parameter_index))); + return Status::OK(); + }); +} + +std::ostream& operator<<(std::ostream& out, + const DynamicParameterBinding& binding) { + out << binding.ToString(); + return out; +} + +} // namespace xla diff --git a/tensorflow/compiler/xla/service/dynamic_parameter_binding.h b/tensorflow/compiler/xla/service/dynamic_parameter_binding.h new file mode 100644 index 0000000000..dd474d8eed --- /dev/null +++ b/tensorflow/compiler/xla/service/dynamic_parameter_binding.h @@ -0,0 +1,125 @@ +/* Copyright 2018 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_COMPILER_XLA_SERVICE_DYNAMIC_PARAMETER_BINDING_H_ +#define TENSORFLOW_COMPILER_XLA_SERVICE_DYNAMIC_PARAMETER_BINDING_H_ + +#include + +#include "absl/container/flat_hash_map.h" +#include "absl/types/optional.h" +#include "tensorflow/compiler/xla/service/hlo.pb.h" +#include "tensorflow/compiler/xla/shape_tree.h" +#include "tensorflow/compiler/xla/shape_util.h" + +namespace xla { + +class HloModule; +// We currently use an explicit API that takes an extra parameter to indicate +// the runtime size of a dynamic dimension. DynamicParameterBinding indicates +// the relationship between parameter: We can have a dynamic parameter that +// points to another target parameter to indicate that the target parameter is +// dynamic. +// +// +// TODO(b/119520625): Remove this API once we have more dynamic shape infra +// ready. +class DynamicParameterBinding { + public: + // DynamicParameter represents a special parameter that is used to represent + // the runtime size of a dimension of another parameter. A dynamic parameter + // has to be a scalar value. + struct DynamicParameter { + // The parameter number of dynamic parameter. + int64 parameter_num; + // The index of the parameter. + ShapeIndex parameter_index; + }; + + // DynamicDimension represents a dimension whose size is determined at + // runtime. A DynamicDimension's runtime size is determined by the binded + // DynamicParameter using `DynamicParameterBinding::Bind` method. + struct DynamicDimension { + // The parameter number of dynamic dimension. + int64 parameter_num; + // The subshape index of the parameter. + ShapeIndex parameter_index; + // The dimension number in the subshape. + int64 dimension; + + // "friend" keyword are added so these functions can be found by ADL. + template + friend H AbslHashValue(H h, const DynamicDimension& m) { + return H::combine(std::move(h), m.parameter_num, m.parameter_index, + m.dimension); + } + + friend bool operator==(const DynamicDimension& lhs, + const DynamicDimension& rhs) { + return lhs.parameter_num == rhs.parameter_num && + lhs.parameter_index == rhs.parameter_index && + lhs.dimension == rhs.dimension; + } + }; + + DynamicParameterBinding() = default; + + virtual ~DynamicParameterBinding() = default; + + // Adds binding which indicates that the dimension indicated by + // `dynamic_dimension` is dynamic, and its runtime size is represented by + // `dynamic_parameter`. + Status Bind(const DynamicParameter& dynamic_parameter, + const DynamicDimension& dynamic_dimension); + + // Returns the parameter and the index representing the runtime size of + // dimension `dim_num` of parameter `param_num` at `param_index`. + // + // Returns nullopt if the binding is not set. + absl::optional GetBinding( + const DynamicDimension& dynamic_dimension); + + using BindingFn = + std::function; + + // Iterate through each binding. + Status ForEachBinding(BindingFn fn) const; + + DynamicParameterBindingProto ToProto() const; + + static StatusOr CreateFromProto( + const DynamicParameterBindingProto& proto); + + string ToString() const; + + // Verifies that the given binding is valid for the given module. + // Specifically, the binding's parameter and parameter size should be valid. + Status Verify(const HloModule& module) const; + + private: + // Keeps track of mappings from DynamicDimension to DynamicParameter. The + // direction of is chosen so that we can easily query if a dimension is + // dynamic and which dynamic parameter represents the real size of that + // dimension. + absl::flat_hash_map bindings_; +}; + +std::ostream& operator<<(std::ostream& out, + const DynamicParameterBinding& binding); + +} // namespace xla + +#endif // TENSORFLOW_COMPILER_XLA_SERVICE_DYNAMIC_PARAMETER_BINDING_H_ diff --git a/tensorflow/compiler/xla/service/dynamic_parameter_binding_test.cc b/tensorflow/compiler/xla/service/dynamic_parameter_binding_test.cc new file mode 100644 index 0000000000..83a6d83dff --- /dev/null +++ b/tensorflow/compiler/xla/service/dynamic_parameter_binding_test.cc @@ -0,0 +1,153 @@ +/* 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/compiler/xla/service/dynamic_parameter_binding.h" + +#include +#include + +#include "absl/algorithm/container.h" +#include "tensorflow/compiler/xla/service/hlo_computation.h" +#include "tensorflow/compiler/xla/service/hlo_dce.h" +#include "tensorflow/compiler/xla/service/hlo_instruction.h" +#include "tensorflow/compiler/xla/service/hlo_memory_scheduler.h" +#include "tensorflow/compiler/xla/service/hlo_opcode.h" +#include "tensorflow/compiler/xla/service/hlo_ordering.h" +#include "tensorflow/compiler/xla/service/hlo_parser.h" +#include "tensorflow/compiler/xla/shape_util.h" +#include "tensorflow/compiler/xla/tests/hlo_test_base.h" +#include "tensorflow/compiler/xla/types.h" +#include "tensorflow/core/lib/core/status_test_util.h" + +namespace xla { +namespace { +class DynamicParameterBindingTest : public HloTestBase {}; + +TEST_F(DynamicParameterBindingTest, SimpleBinding) { + // 'b' is a dynamic shape; 'a' represents the real size of b's first + // dimension. + const string module_str = R"( +HloModule TEST + +ENTRY main { + a = f32[] parameter(0) + b = f32[10] parameter(1) + ROOT root = (f32[], f32[10]) tuple(%a, %b) +} +)"; + TF_ASSERT_OK_AND_ASSIGN(std::unique_ptr module, + ParseHloString(module_str)); + + DynamicParameterBinding binding; + + TF_EXPECT_OK( + binding.Bind(DynamicParameterBinding::DynamicParameter{0, {}}, + DynamicParameterBinding::DynamicDimension{1, {}, 0})); + + absl::optional param = + binding.GetBinding( + DynamicParameterBinding::DynamicDimension{/*parameter_num=*/1, + /*parameter_index=*/{}, + /*dimension=*/0}); + EXPECT_TRUE(param); + EXPECT_EQ(param->parameter_num, 0); + EXPECT_EQ(param->parameter_index, ShapeIndex({})); + TF_EXPECT_OK(binding.Verify(*module)); +} + +TEST_F(DynamicParameterBindingTest, TupleBinding) { + // 'gte2' is a dynamic shape; 'gte1' represents the real size of gte2's first + // dimension. + const string module_str = R"( +HloModule TEST + +ENTRY main { + param = (f32[], f32[10]) parameter(0) + gte1 = f32[] get-tuple-element(%param), index=0 + gte2 = f32[10] get-tuple-element(%param), index=1 + ROOT root = (f32[], f32[10]) tuple(%gte1, %gte2) +} +)"; + TF_ASSERT_OK_AND_ASSIGN(std::unique_ptr module, + ParseHloString(module_str)); + + DynamicParameterBinding binding; + + TF_EXPECT_OK( + binding.Bind(DynamicParameterBinding::DynamicParameter{0, {0}}, + DynamicParameterBinding::DynamicDimension{0, {1}, 0})); + + absl::optional param = + binding.GetBinding( + DynamicParameterBinding::DynamicDimension{/*parameter_num=*/0, + /*parameter_index=*/{1}, + /*dimension=*/0}); + + EXPECT_TRUE(param); + EXPECT_EQ(param->parameter_num, 0); + EXPECT_EQ(param->parameter_index, ShapeIndex({0})); + TF_EXPECT_OK(binding.Verify(*module)); +} + +TEST_F(DynamicParameterBindingTest, TupleBindingWithMultiDimension) { + // 'gte2' is a dynamic shape; 'gte1' represents the real size of gte2's both + // dimensions. + const string module_str = R"( +HloModule TEST + +ENTRY main { + param = (f32[], f32[10, 10]) parameter(0) + gte1 = f32[] get-tuple-element(%param), index=0 + gte2 = f32[10, 10] get-tuple-element(%param), index=1 + ROOT root = (f32[], f32[10, 10]) tuple(%gte1, %gte2) +} +)"; + TF_ASSERT_OK_AND_ASSIGN(std::unique_ptr module, + ParseHloString(module_str)); + + DynamicParameterBinding binding; + + TF_EXPECT_OK( + binding.Bind(DynamicParameterBinding::DynamicParameter{0, {0}}, + DynamicParameterBinding::DynamicDimension{0, {1}, 0})); + + TF_EXPECT_OK( + binding.Bind(DynamicParameterBinding::DynamicParameter{0, {0}}, + DynamicParameterBinding::DynamicDimension{0, {1}, 1})); + + absl::optional param = + binding.GetBinding( + DynamicParameterBinding::DynamicDimension{/*parameter_num=*/0, + /*parameter_index=*/{1}, + /*dimension=*/0}); + + EXPECT_TRUE(param); + EXPECT_EQ(param->parameter_num, 0); + EXPECT_EQ(param->parameter_index, ShapeIndex({0})); + + absl::optional param2 = + binding.GetBinding( + DynamicParameterBinding::DynamicDimension{/*parameter_num=*/0, + /*parameter_index=*/{1}, + /*dimension=*/0}); + EXPECT_TRUE(param2); + EXPECT_EQ(param2->parameter_num, 0); + EXPECT_EQ(param2->parameter_index, ShapeIndex({0})); + + TF_EXPECT_OK(binding.Verify(*module)); +} + +} // namespace +} // namespace xla diff --git a/tensorflow/compiler/xla/service/elemental_ir_emitter.cc b/tensorflow/compiler/xla/service/elemental_ir_emitter.cc index f98c943669..6f1f95f2e9 100644 --- a/tensorflow/compiler/xla/service/elemental_ir_emitter.cc +++ b/tensorflow/compiler/xla/service/elemental_ir_emitter.cc @@ -22,6 +22,7 @@ limitations under the License. // IWYU pragma: no_include "llvm/IR/Intrinsics.gen.inc" #include "absl/algorithm/container.h" +#include "absl/container/flat_hash_map.h" #include "absl/strings/str_cat.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Instructions.h" @@ -1671,26 +1672,66 @@ StatusOr ElementalIrEmitter::EmitElementalConcatenate( b_->SetInsertPoint(init_block); + // Assign a unique id for each *different* operand, and count how often each + // operand is used. If all operands are different, the usage count will be 1 + // for each operand. + absl::flat_hash_map to_unique_operand_id; + std::vector operand_usage_count; + for (const auto* operand : hlo->operands()) { + if (to_unique_operand_id.contains(operand)) { + ++operand_usage_count[to_unique_operand_id[operand]]; + } else { + int64 unique_operand_id = to_unique_operand_id.size(); + to_unique_operand_id[operand] = unique_operand_id; + operand_usage_count.push_back(1); + } + } + + // To avoid that we emit the same operand more than once, we create one basic + // block for each *different* operand with a PHI node for the different source + // index inputs. + std::vector emit_operand_blocks( + to_unique_operand_id.size(), nullptr); + std::vector source_index_phis(to_unique_operand_id.size(), + nullptr); + for (const auto* operand : hlo->operands()) { + int64 operand_id = to_unique_operand_id[operand]; + if (emit_operand_blocks[operand_id] != nullptr) { + continue; + } + + emit_operand_blocks[operand_id] = llvm_ir::CreateBasicBlock( + exit_block, StrCat("concat_index_from_operand_id", operand_id), b_); + auto saved_insert_point = b_->GetInsertPoint(); + llvm_ir::SetToFirstInsertPoint(emit_operand_blocks[operand_id], b_); + source_index_phis[operand_id] = + PHI(source_index.GetType(), operand_usage_count[operand_id]); + auto operand_index = source_index; + operand_index[concat_dim] = source_index_phis[operand_id]; + + // Create the terminator of the block before calling operand generators, + // because they require non-degenerate basic blocks. + b_->SetInsertPoint(llvm::BranchInst::Create( + exit_block, /*InsertAtEnd=*/emit_operand_blocks[operand_id])); + TF_ASSIGN_OR_RETURN(llvm::Value * value, + operand_to_generator.at(operand)(operand_index)); + output->addIncoming(value, b_->GetInsertBlock()); + b_->SetInsertPoint(init_block, saved_insert_point); + } + for (int64 operand_idx = 0; operand_idx < hlo->operand_count(); ++operand_idx) { const HloInstruction* operand = hlo->operand(operand_idx); - auto true_block = llvm_ir::CreateBasicBlock( - exit_block, StrCat("concat_index_from_operand", operand_idx), b_); auto false_block = llvm_ir::CreateBasicBlock( exit_block, StrCat("concat_index_not_from_operand", operand_idx), b_); auto concat_dim_size = llvm::ConstantInt::get(source_index[concat_dim]->getType(), operand->shape().dimensions(concat_dim)); - CondBr(ICmpULT(source_index[concat_dim], concat_dim_size), true_block, - false_block); - - // Create the terminator of the true block before calling operand - // generators, because they require non-degenerate basic blocks. - b_->SetInsertPoint( - llvm::BranchInst::Create(exit_block, /*InsertAtEnd=*/true_block)); - TF_ASSIGN_OR_RETURN(llvm::Value * value, - operand_to_generator.at(operand)(source_index)); - output->addIncoming(value, b_->GetInsertBlock()); + int64 operand_id = to_unique_operand_id[operand]; + source_index_phis[operand_id]->addIncoming(source_index[concat_dim], + b_->GetInsertBlock()); + CondBr(ICmpULT(source_index[concat_dim], concat_dim_size), + emit_operand_blocks[operand_id], false_block); // Subtract the size of the concat dimension of the current operand // from the source index. @@ -2204,13 +2245,15 @@ llvm_ir::ElementGenerator ElementalIrEmitter::MakeElementGenerator( : iota->shape(); PrimitiveType component_element_type = component_shape.element_type(); llvm::Value* iota_result; - if (ShapeUtil::ElementIsIntegral(component_shape)) { + if (primitive_util::IsIntegralType(component_element_type) || + component_element_type == PRED) { iota_result = b_->CreateIntCast( elem_index_linear, llvm_ir::PrimitiveTypeToIrType(component_element_type, module_), /*isSigned=*/false); } else { - TF_RET_CHECK(ShapeUtil::ElementIsFloating(component_shape)) + TF_RET_CHECK( + primitive_util::IsFloatingPointType(component_element_type)) << component_element_type; llvm::Type* float_ir_type; if (component_element_type == BF16) { diff --git a/tensorflow/compiler/xla/service/executable.h b/tensorflow/compiler/xla/service/executable.h index 45f620f3f3..b34bca55a4 100644 --- a/tensorflow/compiler/xla/service/executable.h +++ b/tensorflow/compiler/xla/service/executable.h @@ -61,7 +61,7 @@ struct ExecutionOutput { class Executable { public: explicit Executable( - std::unique_ptr hlo_module, + std::unique_ptr hlo_module, std::unique_ptr hlo_profile_printer_data, std::unique_ptr hlo_profile_index_map) : hlo_module_(std::move(hlo_module)), @@ -162,7 +162,7 @@ class Executable { return hlo_profile_printer_data_ != nullptr; } - const HloModule& module() const { return *hlo_module_; } + HloModule& module() const { return *hlo_module_; } const bool has_module() const { return hlo_module_ != nullptr; } @@ -199,7 +199,7 @@ class Executable { // HloModule this was compiled from. BufferAssignment keeps pointers to // HloInstructions owned by the HloModule so we need to keep the HloModule // around. - const std::unique_ptr hlo_module_; + const std::unique_ptr hlo_module_; // HloSnapshot this was compiled from. Null if not dumping executions. std::unique_ptr hlo_snapshot_; diff --git a/tensorflow/compiler/xla/service/gpu/BUILD b/tensorflow/compiler/xla/service/gpu/BUILD index b1629616ac..bfd1b6cb14 100644 --- a/tensorflow/compiler/xla/service/gpu/BUILD +++ b/tensorflow/compiler/xla/service/gpu/BUILD @@ -701,6 +701,7 @@ cc_library( "//tensorflow/compiler/xla/service:hlo_cse", "//tensorflow/compiler/xla/service:hlo_dce", "//tensorflow/compiler/xla/service:hlo_element_type_converter", + "//tensorflow/compiler/xla/service:hlo_get_dimension_size_rewriter", "//tensorflow/compiler/xla/service:hlo_pass", "//tensorflow/compiler/xla/service:hlo_pass_pipeline", "//tensorflow/compiler/xla/service:hlo_proto", diff --git a/tensorflow/compiler/xla/service/gpu/cudnn_conv_rewriter.cc b/tensorflow/compiler/xla/service/gpu/cudnn_conv_rewriter.cc index 4ce877f62a..e81850db69 100644 --- a/tensorflow/compiler/xla/service/gpu/cudnn_conv_rewriter.cc +++ b/tensorflow/compiler/xla/service/gpu/cudnn_conv_rewriter.cc @@ -77,7 +77,11 @@ bool CanImplementAsCudnnForwardConv(HloInstruction* conv) { return false; } - if (window_util::HasWindowReversal(conv->window())) { + // CuDNN can perform either cross correlation (no reversal), + // or convolution (all dimensions reversed). + if (dnums.input_spatial_dimensions_size() == 2 + ? !window_util::AllOrNoneReversed(conv->window()) + : window_util::HasWindowReversal(conv->window())) { return false; } return true; diff --git a/tensorflow/compiler/xla/service/gpu/cudnn_conv_runner.cc b/tensorflow/compiler/xla/service/gpu/cudnn_conv_runner.cc index 492d290bf4..3425e1b494 100644 --- a/tensorflow/compiler/xla/service/gpu/cudnn_conv_runner.cc +++ b/tensorflow/compiler/xla/service/gpu/cudnn_conv_runner.cc @@ -138,6 +138,7 @@ Status RunCudnnConvImpl(CudnnConvParams params, const int num_dimensions = window.dimensions_size(); CHECK_LE(num_dimensions, 3); + CHECK_GE(num_dimensions, 1); // cuDNN does not support 1D convolutions. We therefore express 1D // convolutions as 2D convolutions where the first spatial dimension is 1. // This matches the behavior of TF (see definition of conv1d in @@ -148,10 +149,15 @@ Status RunCudnnConvImpl(CudnnConvParams params, output_shape.element_type()) << ShapeUtil::HumanString(output_shape); + // If one dimension is reversed, we need to have all dimensions reversed (so + // we're doing convolution not cross correlation). + const bool dims_reversed = window.dimensions()[0].window_reversal(); + CHECK_EQ(num_dimensions, dnums.input_spatial_dimensions_size()); CHECK_EQ(num_dimensions, dnums.kernel_spatial_dimensions_size()); CHECK_EQ(num_dimensions, dnums.output_spatial_dimensions_size()); for (const WindowDimension& dim : window.dimensions()) { + CHECK_EQ(dims_reversed, dim.window_reversal()); CHECK_EQ(dim.padding_low(), dim.padding_high()); CHECK_EQ(dim.base_dilation(), 1) << "cudnn does not support base dilation; it " @@ -198,6 +204,7 @@ Status RunCudnnConvImpl(CudnnConvParams params, ConvolutionDescriptor convolution_descriptor(effective_num_dimensions); convolution_descriptor.set_group_count(feature_group_count); + convolution_descriptor.set_convolution_not_crosscorr(dims_reversed); for (int dim = 0; dim < num_dimensions; ++dim) { convolution_descriptor .set_zero_padding( @@ -363,14 +370,12 @@ StatusOr GetCudnnConvParams( params.output_shape = &conv_result_shape; params.fusion.emplace(); auto& fusion = *params.fusion; - if (backend_config.activation_mode() < - static_cast(se::dnn::ActivationMode::kNumActivationModes)) { - fusion.mode = static_cast( - backend_config.activation_mode()); - } else { + if (!se::dnn::ActivationMode_IsValid(backend_config.activation_mode())) { return InternalError("Bad activation mode: %s", backend_config.ShortDebugString()); } + fusion.mode = static_cast( + backend_config.activation_mode()); fusion.side_input_scale = backend_config.side_input_scale(); params.input_buf = operand_buffers[0]; params.filter_buf = operand_buffers[1]; diff --git a/tensorflow/compiler/xla/service/gpu/elemental_ir_emitter.cc b/tensorflow/compiler/xla/service/gpu/elemental_ir_emitter.cc index 6dcdaf1cfe..2ab754a471 100644 --- a/tensorflow/compiler/xla/service/gpu/elemental_ir_emitter.cc +++ b/tensorflow/compiler/xla/service/gpu/elemental_ir_emitter.cc @@ -161,6 +161,16 @@ StatusOr GpuElementalIrEmitter::EmitFloatBinaryOp( PrimitiveType lhs_input_type = op->operand(0)->shape().element_type(); PrimitiveType rhs_input_type = op->operand(1)->shape().element_type(); PrimitiveType output_type = op->shape().element_type(); + HloOpcode opcode = op->opcode(); + + if (hlo_module_config_.debug_options().xla_gpu_enable_fast_min_max() && + (opcode == HloOpcode::kMaximum || opcode == HloOpcode::kMinimum)) { + return llvm_ir::EmitCallToIntrinsic( + opcode == HloOpcode::kMaximum ? llvm::Intrinsic::maxnum + : llvm::Intrinsic::minnum, + {lhs_value, rhs_value}, {lhs_value->getType()}, b_); + } + switch (op->opcode()) { case HloOpcode::kRemainder: { return EmitLibdeviceMathCall("__nv_fmod", {lhs_value, rhs_value}, diff --git a/tensorflow/compiler/xla/service/gpu/fusion_merger.cc b/tensorflow/compiler/xla/service/gpu/fusion_merger.cc index 30c1f90889..470457935a 100644 --- a/tensorflow/compiler/xla/service/gpu/fusion_merger.cc +++ b/tensorflow/compiler/xla/service/gpu/fusion_merger.cc @@ -229,7 +229,7 @@ Status FusionInstructionMerger::HandleFusion(HloInstruction* fusion) { if (!absl::c_all_of(fusion->users(), [&](const HloInstruction* user) { return user->opcode() == HloOpcode::kFusion && (user->fusion_kind() == HloInstruction::FusionKind::kLoop || - (user->fusion_kind() == HloInstruction::FusionKind::kInput && + (IsReduceInputFusion(*user) && LayoutsAreReduceInputFusionFriendly(*fusion, *user))); })) { VLOG(3) << "Not merging " << fusion->name() diff --git a/tensorflow/compiler/xla/service/gpu/gpu_executable.cc b/tensorflow/compiler/xla/service/gpu/gpu_executable.cc index 5742632782..ae2e718db2 100644 --- a/tensorflow/compiler/xla/service/gpu/gpu_executable.cc +++ b/tensorflow/compiler/xla/service/gpu/gpu_executable.cc @@ -51,7 +51,7 @@ GpuExecutable::GpuExecutable( const string& ptx, const std::vector& cubin, std::pair compute_capability, std::unique_ptr thunk_schedule, - std::unique_ptr hlo_module, + std::unique_ptr hlo_module, std::unique_ptr assignment, std::unique_ptr hlo_profile_printer_data, std::unique_ptr hlo_profile_index_map) diff --git a/tensorflow/compiler/xla/service/gpu/gpu_executable.h b/tensorflow/compiler/xla/service/gpu/gpu_executable.h index 0e276282e4..2b3c77f5b8 100644 --- a/tensorflow/compiler/xla/service/gpu/gpu_executable.h +++ b/tensorflow/compiler/xla/service/gpu/gpu_executable.h @@ -54,7 +54,7 @@ class GpuExecutable : public Executable { GpuExecutable(const string& ptx, const std::vector& cubin, std::pair compute_capability, std::unique_ptr thunk_schedule, - std::unique_ptr hlo_module, + std::unique_ptr hlo_module, std::unique_ptr assignment, std::unique_ptr hlo_profile_printer_data, std::unique_ptr hlo_profile_index_map); diff --git a/tensorflow/compiler/xla/service/gpu/gpu_fusible.cc b/tensorflow/compiler/xla/service/gpu/gpu_fusible.cc index 2d31fd5570..452e763a8e 100644 --- a/tensorflow/compiler/xla/service/gpu/gpu_fusible.cc +++ b/tensorflow/compiler/xla/service/gpu/gpu_fusible.cc @@ -55,7 +55,7 @@ bool LayoutsAreReduceInputFusionFriendly(const HloInstruction& producer, }); } -bool IsInputFusibleReduction(const HloInstruction& instr) { +bool IsReduceInputFusion(const HloInstruction& instr) { if (instr.IsMultiOutputFusion()) { for (const HloInstruction* operand : instr.fused_expression_root()->operands()) { @@ -67,17 +67,70 @@ bool IsInputFusibleReduction(const HloInstruction& instr) { return true; } } - return false; - } else if (instr.opcode() == HloOpcode::kFusion) { - if (IsReductionToVector(*instr.fused_expression_root())) { - CHECK(instr.fusion_kind() == HloInstruction::FusionKind::kInput) - << " Fusion rooted at reduction-to-vector op must be of kind kInput: " - << instr.ToString(); - return true; + } else if (instr.opcode() == HloOpcode::kFusion && + IsReductionToVector(*instr.fused_expression_root())) { + CHECK(instr.fusion_kind() == HloInstruction::FusionKind::kInput) + << " Fusion rooted at reduction-to-vector op must be of kind kInput: " + << instr.ToString(); + return true; + } + return false; +} + +bool IsInputFusibleReduction(const HloInstruction& instr) { + return IsReduceInputFusion(instr) || IsReductionToVector(instr); +} + +bool ShapesCompatibleForMultiOutputFusion(const HloInstruction& instr1, + const HloInstruction& instr2) { + // Returns the instructions that determines the emitter used for lowering, + // sometimes referred to as "the real hero". + auto get_real_hero = + [&](const HloInstruction* instr) -> const HloInstruction* { + if (instr->opcode() == HloOpcode::kFusion) { + auto fused_expression_root = instr->fused_expression_root(); + if (instr->IsMultiOutputFusion()) { + // If possible, we want to pick a reduction-to-vector operand of the + // fusion root, because it has the most constraints. + for (const auto* inst : fused_expression_root->operands()) { + if (IsReductionToVector(*inst)) { + return inst; + } + } + return fused_expression_root->operands()[0]; + } + return fused_expression_root; } + return instr; + }; + + // Multi-output fusion kernels share a common parallel loop. The loop + // dimenstions are determined by instruction shapes. + auto get_loop_shape = [&](const HloInstruction* element_instr) { + // Special-case reduction-to-vector ops: The loop dimensions are determined + // by the shape of the first operand. + if (IsReductionToVector(*element_instr)) { + return element_instr->operand(0)->shape(); + } + return element_instr->shape(); + }; + + // All shapes of the root tuple of multi-output fusions should agree, i.e. all + // root ops should have equal output shapes. An exception are + // reduction-to-vector ops. Here the input shapes of the reduction (first + // operand shape) and the reduction dimensions need to match. + auto* instr_1 = get_real_hero(&instr1); + auto* instr_2 = get_real_hero(&instr2); + // TODO(tjoerg): Relax the shape constraint. The datatype does not matter. + if (IsReductionToVector(*instr_1) && IsReductionToVector(*instr_2) && + (!ShapeUtil::Equal(instr_1->shape(), instr_2->shape()) || + instr_1->dimensions() != instr_2->dimensions())) { return false; } - return IsReductionToVector(instr); + // The elementwise output shapes must be the same (including layout). + // TODO(tjoerg): Further relax the constraint. The datatype does not matter. + return ShapeUtil::EqualIgnoringFpPrecision(get_loop_shape(instr_1), + get_loop_shape(instr_2)); } } // namespace gpu diff --git a/tensorflow/compiler/xla/service/gpu/gpu_fusible.h b/tensorflow/compiler/xla/service/gpu/gpu_fusible.h index f7c24a0d5b..e9d7ba1c4c 100644 --- a/tensorflow/compiler/xla/service/gpu/gpu_fusible.h +++ b/tensorflow/compiler/xla/service/gpu/gpu_fusible.h @@ -33,16 +33,29 @@ namespace gpu { bool LayoutsAreReduceInputFusionFriendly(const HloInstruction& producer, const HloInstruction& reduce); -// Whether `instr` is fusible as root of a reduce input fusions, i.e. `instr` -// is either an unfused reduction-to-vector op, an input fusion rooted at a -// reduction-to-vector op, or a multi-output input fusion with at least one -// reduction-to-vector op root. // Note that reduction ops are lowered in different ways. Reduce input fusions // are lowered by IrEmitterUnnested::EmitReductionToVector and must be rooted at // reduction-to-vector ops. Other reduction ops are lowered by // GpuElementalIrEmitter and fused like elementwise ops. + +// Whether `instr` is an input fusion rooted at a reduction-to-vector op or a +// multi-output input fusion with at least one reduction-to-vector op root. +bool IsReduceInputFusion(const HloInstruction& instr); + +// Whether `instr` is fusible as root of a reduce input fusions, i.e. `instr` +// is either an unfused reduction-to-vector op or a reduce input fusion. bool IsInputFusibleReduction(const HloInstruction& instr); +// Whether instruction shapes are compatible for multi-output fusion, i.e. +// whether the emitters support lowering the resulting fusion. +// This function works for both, sibling and producer-conumser multi-output +// fusion. +// So far, multi-output fusion is supported for loop fusions and reduce +// input fusions only. It is up to the caller to ensure the instructions +// themselves are fusible! +bool ShapesCompatibleForMultiOutputFusion(const HloInstruction& instr1, + const HloInstruction& instr2); + } // namespace gpu } // namespace xla diff --git a/tensorflow/compiler/xla/service/gpu/gpu_fusible_test.cc b/tensorflow/compiler/xla/service/gpu/gpu_fusible_test.cc index d91b7bc61f..15d4ee206c 100644 --- a/tensorflow/compiler/xla/service/gpu/gpu_fusible_test.cc +++ b/tensorflow/compiler/xla/service/gpu/gpu_fusible_test.cc @@ -178,7 +178,7 @@ TEST_F(GpuFusibleTest, EXPECT_TRUE(LayoutsAreReduceInputFusionFriendly(*loop_fusion, *reduce)); } -TEST_F(GpuFusibleTest, IsInputFusibleReduction_ReductionToVector) { +TEST_F(GpuFusibleTest, IsReduceInputFusion_ReductionToVector) { auto module = ParseHloString(absl::StrCat(kModulePrefix, R"( ENTRY entry { c0 = f32[] parameter(0) @@ -191,10 +191,11 @@ TEST_F(GpuFusibleTest, IsInputFusibleReduction_ReductionToVector) { const HloInstruction* reduce = module->entry_computation()->root_instruction(); ASSERT_EQ(reduce->opcode(), HloOpcode::kReduce); + EXPECT_FALSE(IsReduceInputFusion(*reduce)); EXPECT_TRUE(IsInputFusibleReduction(*reduce)); } -TEST_F(GpuFusibleTest, IsInputFusibleReduction_ElementalReduction) { +TEST_F(GpuFusibleTest, IsReduceInputFusion_ElementalReduction) { auto module = ParseHloString(absl::StrCat(kModulePrefix, R"( ENTRY entry { c0 = f32[] parameter(0) @@ -207,10 +208,11 @@ TEST_F(GpuFusibleTest, IsInputFusibleReduction_ElementalReduction) { const HloInstruction* reduce = module->entry_computation()->root_instruction(); ASSERT_EQ(reduce->opcode(), HloOpcode::kReduce); + EXPECT_FALSE(IsReduceInputFusion(*reduce)); EXPECT_FALSE(IsInputFusibleReduction(*reduce)); } -TEST_F(GpuFusibleTest, IsInputFusibleReduction_SingleOutputInputReduceFusion) { +TEST_F(GpuFusibleTest, IsReduceInputFusion_SingleOutputInputReduceFusion) { auto module = ParseHloString(absl::StrCat(kModulePrefix, R"( fused_reduction { c0 = f32[] parameter(0) @@ -225,10 +227,11 @@ TEST_F(GpuFusibleTest, IsInputFusibleReduction_SingleOutputInputReduceFusion) { const HloInstruction* reduce = module->entry_computation()->root_instruction(); ASSERT_EQ(reduce->opcode(), HloOpcode::kFusion); + EXPECT_TRUE(IsReduceInputFusion(*reduce)); EXPECT_TRUE(IsInputFusibleReduction(*reduce)); } -TEST_F(GpuFusibleTest, IsInputFusibleReduction_SingleOutputLoopReduceFusion) { +TEST_F(GpuFusibleTest, IsReduceInputFusion_SingleOutputLoopReduceFusion) { auto module = ParseHloString(absl::StrCat(kModulePrefix, R"( fused_reduction { c0 = f32[] parameter(0) @@ -243,10 +246,11 @@ TEST_F(GpuFusibleTest, IsInputFusibleReduction_SingleOutputLoopReduceFusion) { const HloInstruction* reduce = module->entry_computation()->root_instruction(); ASSERT_EQ(reduce->opcode(), HloOpcode::kFusion); + EXPECT_FALSE(IsReduceInputFusion(*reduce)); EXPECT_FALSE(IsInputFusibleReduction(*reduce)); } -TEST_F(GpuFusibleTest, IsInputFusibleReduction_MultiOutputInputReduceFusion) { +TEST_F(GpuFusibleTest, IsReduceInputFusion_MultiOutputInputReduceFusion) { auto module = ParseHloString(absl::StrCat(kModulePrefix, R"( fused_reduction { c0 = f32[] parameter(0) @@ -263,11 +267,12 @@ TEST_F(GpuFusibleTest, IsInputFusibleReduction_MultiOutputInputReduceFusion) { const HloInstruction* reduce = module->entry_computation()->root_instruction(); ASSERT_EQ(reduce->opcode(), HloOpcode::kFusion); + EXPECT_TRUE(IsReduceInputFusion(*reduce)); EXPECT_TRUE(IsInputFusibleReduction(*reduce)); } TEST_F(GpuFusibleTest, - IsInputFusibleReduction_MultiOutputInputReduceFusionWithExtraOutputs) { + IsReduceInputFusion_MultiOutputInputReduceFusionWithExtraOutputs) { auto module = ParseHloString(absl::StrCat(kModulePrefix, R"( fused_reduction { c0 = f32[] parameter(0) @@ -284,10 +289,11 @@ TEST_F(GpuFusibleTest, const HloInstruction* reduce = module->entry_computation()->root_instruction(); ASSERT_EQ(reduce->opcode(), HloOpcode::kFusion); + EXPECT_TRUE(IsReduceInputFusion(*reduce)); EXPECT_TRUE(IsInputFusibleReduction(*reduce)); } -TEST_F(GpuFusibleTest, IsInputFusibleReduction_MultiOutputLoopReduceFusion) { +TEST_F(GpuFusibleTest, IsReduceInputFusion_MultiOutputLoopReduceFusion) { auto module = ParseHloString(absl::StrCat(kModulePrefix, R"( fused_reduction { c0 = f32[] parameter(0) @@ -304,11 +310,12 @@ TEST_F(GpuFusibleTest, IsInputFusibleReduction_MultiOutputLoopReduceFusion) { const HloInstruction* reduce = module->entry_computation()->root_instruction(); ASSERT_EQ(reduce->opcode(), HloOpcode::kFusion); + EXPECT_FALSE(IsReduceInputFusion(*reduce)); EXPECT_FALSE(IsInputFusibleReduction(*reduce)); } TEST_F(GpuFusibleTest, - IsInputFusibleReduction_MultiOutputLoopFusionReduceAndElementwiseOp) { + IsReduceInputFusion_MultiOutputLoopFusionReduceAndElementwiseOp) { auto module = ParseHloString(absl::StrCat(kModulePrefix, R"( fused_reduction { c0 = f32[] parameter(0) @@ -325,8 +332,304 @@ TEST_F(GpuFusibleTest, const HloInstruction* reduce = module->entry_computation()->root_instruction(); ASSERT_EQ(reduce->opcode(), HloOpcode::kFusion); + EXPECT_FALSE(IsReduceInputFusion(*reduce)); EXPECT_FALSE(IsInputFusibleReduction(*reduce)); } +TEST_F(GpuFusibleTest, ShapesCompatibleForMultiOutputFusion_LoopFusions) { + auto module = ParseHloString(absl::StrCat(kModulePrefix, R"( + fused_computation_1 { + p0.1 = f32[6400]{0} parameter(0) + ROOT mul = f32[6400]{0} multiply(p0.1, p0.1) + } + + fused_computation_2 { + p0.2 = f32[6400]{0} parameter(0) + const.2 = f32[] constant(1) + ROOT div = f32[6400]{0} divide(p0.2, const.2) + } + + ENTRY entry { + p0 = f32[6400]{0} parameter(0) + fusion.1 = f32[6400]{0} fusion(p0), kind=kLoop, calls=fused_computation_1 + fusion.2 = f32[6400]{0} fusion(p0), kind=kLoop, calls=fused_computation_2 + ROOT root = (f32[6400]{0}, f32[6400]{0}) tuple(fusion.1, fusion.2) + })")) + .ValueOrDie(); + const HloInstruction* fusion_1 = + module->entry_computation()->root_instruction()->operand(0); + const HloInstruction* fusion_2 = + module->entry_computation()->root_instruction()->operand(1); + EXPECT_TRUE(ShapesCompatibleForMultiOutputFusion(*fusion_1, *fusion_2)); +} + +TEST_F(GpuFusibleTest, ShapesCompatibleForMultiOutputFusion_IgnoreFpPrecision) { + auto module = ParseHloString(absl::StrCat(kModulePrefix, R"( + fused_computation_1 { + p0.1 = f32[6400]{0} parameter(0) + ROOT mul = f32[6400]{0} multiply(p0.1, p0.1) + } + + fused_computation_2 { + p0.2 = f32[6400]{0} parameter(0) + ROOT convert = f16[6400]{0} convert(p0.2) + } + + ENTRY entry { + p0 = f32[6400]{0} parameter(0) + fusion.1 = f32[6400]{0} fusion(p0), kind=kLoop, calls=fused_computation_1 + fusion.2 = f32[6400]{0} fusion(p0), kind=kLoop, calls=fused_computation_2 + ROOT root = (f32[6400]{0}, f32[6400]{0}) tuple(fusion.1, fusion.2) + })")) + .ValueOrDie(); + const HloInstruction* fusion_1 = + module->entry_computation()->root_instruction()->operand(0); + const HloInstruction* fusion_2 = + module->entry_computation()->root_instruction()->operand(1); + EXPECT_TRUE(ShapesCompatibleForMultiOutputFusion(*fusion_1, *fusion_2)); +} + +TEST_F(GpuFusibleTest, ShapesCompatibleForMultiOutputFusion_Reduce) { + auto module = ParseHloString(absl::StrCat(kModulePrefix, R"( + fused_computation_1 { + p0.1 = f32[6400]{0} parameter(0) + ROOT mul = f32[6400]{0} multiply(p0.1, p0.1) + } + + ENTRY entry { + p0 = f32[6400]{0} parameter(0) + fusion.1 = f32[6400]{0} fusion(p0), kind=kLoop, calls=fused_computation_1 + const.2 = f32[] constant(0) + reduce = f32[] reduce(p0, const.2), dimensions={0}, to_apply=scalar_add + ROOT root = (f32[6400]{0}, f32[]) tuple(fusion.1, reduce) + })")) + .ValueOrDie(); + const HloInstruction* fusion = + module->entry_computation()->root_instruction()->operand(0); + const HloInstruction* reduce = + module->entry_computation()->root_instruction()->operand(1); + EXPECT_TRUE(ShapesCompatibleForMultiOutputFusion(*fusion, *reduce)); +} + +TEST_F(GpuFusibleTest, ShapesCompatibleForMultiOutputFusion_Elementwise) { + auto module = ParseHloString(absl::StrCat(kModulePrefix, R"( + fused_computation_1 { + p0.1 = f32[6400]{0} parameter(0) + ROOT mul = f32[6400]{0} multiply(p0.1, p0.1) + } + + ENTRY entry { + p0 = f32[6400]{0} parameter(0) + fusion.1 = f32[6400]{0} fusion(p0), kind=kLoop, calls=fused_computation_1 + const.2 = f32[] constant(1) + div = f32[6400]{0} divide(p0, const.2) + ROOT root = (f32[6400]{0}, f32[6400]{0}) tuple(fusion.1, div) + })")) + .ValueOrDie(); + const HloInstruction* fusion = + module->entry_computation()->root_instruction()->operand(0); + const HloInstruction* div = + module->entry_computation()->root_instruction()->operand(1); + EXPECT_TRUE(ShapesCompatibleForMultiOutputFusion(*fusion, *div)); +} + +TEST_F(GpuFusibleTest, + ShapesCompatibleForMultiOutputFusion_MultiOutputLoopFusion) { + auto module = ParseHloString(absl::StrCat(kModulePrefix, R"( + fused_computation_1 { + p0.1 = f32[8,1,5,16,1,1]{5,4,3,2,1,0} parameter(0) + mul = f32[8,1,5,16,1,1]{5,4,3,2,1,0} multiply(p0.1, p0.1) + exp = f32[8,1,5,16,1,1]{5,4,3,2,1,0} exponential(p0.1) + ROOT tuple = (f32[8,1,5,16,1,1]{5,4,3,2,1,0}, f32[8,1,5,16,1,1]{5,4,3,2,1,0}) tuple(mul, exp) + } + + fused_computation_2 { + p0.2 = f32[8,1,5,16,1,1]{5,4,3,2,1,0} parameter(0) + const.2 = f32[] constant(0) + ROOT add = f32[8,1,5,16,1,1]{5,4,3,2,1,0} add(p0.2, const.2) + } + + ENTRY entry { + p0 = f32[8,1,5,16,1,1]{5,4,3,2,1,0} parameter(0) + fusion.1 = (f32[8,1,5,16,1,1]{5,4,3,2,1,0}, f32[8,1,5,16,1,1]{5,4,3,2,1,0}) fusion(p0), kind=kLoop, calls=fused_computation_1 + fusion.2 = f32[8,1,5,16,1,1]{5,4,3,2,1,0} fusion(p0), kind=kLoop, calls=fused_computation_2 + gte0 = f32[8,1,5,16,1,1]{5,4,3,2,1,0} get-tuple-element(fusion.1), index=0 + gte1 = f32[8,1,5,16,1,1]{5,4,3,2,1,0} get-tuple-element(fusion.1), index=1 + ROOT root = (f32[8,1,5,16,1,1]{5,4,3,2,1,0}, f32[8,1,5,16,1,1]{5,4,3,2,1,0}, f32[8,1,5,16,1,1]{5,4,3,2,1,0}) tuple(gte0, gte1, fusion.2) + })")) + .ValueOrDie(); + const HloInstruction* fusion_1 = + module->entry_computation()->root_instruction()->operand(0)->operand(0); + const HloInstruction* fusion_2 = + module->entry_computation()->root_instruction()->operand(1)->operand(0); + EXPECT_TRUE(ShapesCompatibleForMultiOutputFusion(*fusion_1, *fusion_2)); +} + +TEST_F(GpuFusibleTest, ShapesCompatibleForMultiOutputFusion_UnfusedOps) { + auto module = ParseHloString(absl::StrCat(kModulePrefix, R"( + ENTRY reduce { + p0 = f32[2,2,2]{2,1,0} parameter(0) + c0 = f32[] constant(0) + exp = f32[2,2,2]{2,1,0} exponential(p0) + reduce = f32[2,2]{1,0} reduce(exp, c0), dimensions={2}, to_apply=scalar_add + ROOT root = (f32[2,2]{1,0}, f32[2,2,2]{2,1,0}) tuple(reduce, exp) + })")) + .ValueOrDie(); + const HloInstruction* reduce = + module->entry_computation()->root_instruction()->operand(0); + const HloInstruction* exp = + module->entry_computation()->root_instruction()->operand(1); + EXPECT_TRUE(ShapesCompatibleForMultiOutputFusion(*reduce, *exp)); +} + +TEST_F(GpuFusibleTest, ShapesCompatibleForMultiOutputFusion_DifferentLayouts) { + auto module = ParseHloString(absl::StrCat(kModulePrefix, R"( + ENTRY reduce { + p0 = f32[2,2,2]{2,1,0} parameter(0) + p1 = f32[2,2,2]{0,1,2} parameter(1) + c0 = f32[] constant(0) + exp = f32[2,2,2]{2,1,0} exponential(p0) + reduce = f32[2,2]{0,1} reduce(p1, c0), dimensions={2}, to_apply=scalar_add + ROOT root = (f32[2,2]{0,1}, f32[2,2,2]{2,1,0}) tuple(reduce, exp) + })")) + .ValueOrDie(); + const HloInstruction* reduce = + module->entry_computation()->root_instruction()->operand(0); + const HloInstruction* exp = + module->entry_computation()->root_instruction()->operand(1); + EXPECT_FALSE(ShapesCompatibleForMultiOutputFusion(*reduce, *exp)); +} + +TEST_F(GpuFusibleTest, + ShapesCompatibleForMultiOutputFusion_MultiOutputReduceFusion) { + auto module = ParseHloString(absl::StrCat(kModulePrefix, R"( + fused_select { + p1.1 = f32[2,2,2]{2,1,0} parameter(1) + c0 = f32[] constant(0) + broadcast = f32[2,2,2]{2,1,0} broadcast(f32[] c0), dimensions={} + greater-than = pred[2,2,2]{2,1,0} greater-than(f32[2,2,2]{2,1,0} p1.1, f32[2,2,2]{2,1,0} broadcast) + p0.1 = f32[2,2,2]{2,1,0} parameter(0) + ROOT select = f32[2,2,2]{2,1,0} select(pred[2,2,2]{2,1,0} greater-than, f32[2,2,2]{2,1,0} p0.1, f32[2,2,2]{2,1,0} broadcast) + } + + fused_reduce { + p0.2 = f32[2,2,2]{2,1,0} parameter(0) + c1 = f32[] constant(0) + r1 = f32[2,2]{1,0} reduce(p0.2, c1), dimensions={2}, to_apply=scalar_add + mul = f32[2,2,2]{2,1,0} multiply(p0.2, p0.2) + r2 = f32[2,2]{1,0} reduce(mul, c1), dimensions={2}, to_apply=scalar_add + ROOT tuple = (f32[2,2]{1,0}, f32[2,2]{1,0}) tuple(r1, r2) + } + + ENTRY reduce { + p0 = f32[2,2,2]{2,1,0} parameter(0) + p1 = f32[2,2,2]{2,1,0} parameter(1) + select = f32[2,2,2]{2,1,0} fusion(p0, p1), kind=kLoop, calls=fused_select + fusion = (f32[2,2]{1,0}, f32[2,2]{1,0}) fusion(select), kind=kInput, calls=fused_reduce + gte0 = f32[2,2]{1,0} get-tuple-element(fusion), index=0 + gte1 = f32[2,2]{1,0} get-tuple-element(fusion), index=1 + ROOT root = (f32[2,2]{1,0}, f32[2,2]{1,0}, f32[2,2,2]{2,1,0}) tuple(gte1, gte1, select) + })")) + .ValueOrDie(); + const HloInstruction* fusion_1 = + module->entry_computation()->root_instruction()->operand(0)->operand(0); + const HloInstruction* fusion_2 = + module->entry_computation()->root_instruction()->operand(1)->operand(0); + EXPECT_TRUE(ShapesCompatibleForMultiOutputFusion(*fusion_1, *fusion_2)); +} + +TEST_F(GpuFusibleTest, ShapesCompatibleForMultiOutputFusion_ReduceFusions) { + auto module = ParseHloString(absl::StrCat(kModulePrefix, R"( + fused_reduce_1 { + p0.1 = f32[2,2,2]{2,1,0} parameter(0) + c0 = f32[] constant(0) + ROOT reduce = f32[2,2]{1,0} reduce(f32[2,2,2]{2,1,0} p0.1, f32[] c0), dimensions={0}, to_apply=scalar_add + } + + fused_reduce_2 { + p0.2 = f32[2,2,2]{2,1,0} parameter(0) + mul = f32[2,2,2]{2,1,0} multiply(f32[2,2,2]{2,1,0} p0.2, f32[2,2,2]{2,1,0} p0.2) + c1 = f32[] constant(0) + ROOT reduce = f32[2,2]{1,0} reduce(f32[2,2,2]{2,1,0} mul, f32[] c1), dimensions={0}, to_apply=scalar_add + } + + ENTRY reduce { + p0 = f32[2,2,2]{2,1,0} parameter(0) + p1 = f32[2,2,2]{2,1,0} parameter(1) + reduce_1 = f32[2,2]{1,0} fusion(p0), kind=kLoop, calls=fused_reduce_1 + reduce_2 = f32[2,2]{1,0} fusion(p1), kind=kLoop, calls=fused_reduce_2 + ROOT root = (f32[2,2]{1,0}, f32[2,2,2]{2,1,0}) tuple(reduce_1, reduce_2) + })")) + .ValueOrDie(); + const HloInstruction* fusion_1 = + module->entry_computation()->root_instruction()->operand(0); + const HloInstruction* fusion_2 = + module->entry_computation()->root_instruction()->operand(1); + EXPECT_TRUE(ShapesCompatibleForMultiOutputFusion(*fusion_1, *fusion_2)); +} + +TEST_F(GpuFusibleTest, + ShapesCompatibleForMultiOutputFusion_DifferentReduceDimensions) { + auto module = ParseHloString(absl::StrCat(kModulePrefix, R"( + fused_reduce_1 { + p0.1 = f32[2,2,2]{2,1,0} parameter(0) + c0 = f32[] constant(0) + ROOT reduce = f32[2,2]{1,0} reduce(f32[2,2,2]{2,1,0} p0.1, f32[] c0), dimensions={0}, to_apply=scalar_add + } + + fused_reduce_2 { + p0.2 = f32[2,2,2]{2,1,0} parameter(0) + mul = f32[2,2,2]{2,1,0} multiply(f32[2,2,2]{2,1,0} p0.2, f32[2,2,2]{2,1,0} p0.2) + c1 = f32[] constant(0) + ROOT reduce = f32[2,2]{1,0} reduce(f32[2,2,2]{2,1,0} mul, f32[] c1), dimensions={2}, to_apply=scalar_add + } + + ENTRY reduce { + p0 = f32[2,2,2]{2,1,0} parameter(0) + p1 = f32[2,2,2]{2,1,0} parameter(1) + reduce_1 = f32[2,2]{1,0} fusion(p0), kind=kLoop, calls=fused_reduce_1 + reduce_2 = f32[2,2]{1,0} fusion(p1), kind=kLoop, calls=fused_reduce_2 + ROOT root = (f32[2,2]{1,0}, f32[2,2,2]{2,1,0}) tuple(reduce_1, reduce_2) + })")) + .ValueOrDie(); + const HloInstruction* fusion_1 = + module->entry_computation()->root_instruction()->operand(0); + const HloInstruction* fusion_2 = + module->entry_computation()->root_instruction()->operand(1); + EXPECT_FALSE(ShapesCompatibleForMultiOutputFusion(*fusion_1, *fusion_2)); +} + +TEST_F(GpuFusibleTest, + ShapesCompatibleForMultiOutputFusion_NoReductionToVector) { + auto module = ParseHloString(absl::StrCat(kModulePrefix, R"( + fused_element_wise { + p0.1 = f32[2,2,2]{2,1,0} parameter(0) + p1.1 = f32[2,2,2]{2,1,0} parameter(1) + ROOT add = f32[2,2,2]{2,1,0} add(p0.1, p1.1) + } + + fused_reduce { + p0.2 = f32[2,2,2]{2,1,0} parameter(0) + mul = f32[2,2,2]{2,1,0} multiply(f32[2,2,2]{2,1,0} p0.2, f32[2,2,2]{2,1,0} p0.2) + c1 = f32[] constant(0) + // Note that reduce is not a reduction-to-vector. + ROOT reduce = f32[2,2]{1,0} reduce(f32[2,2,2]{2,1,0} mul, f32[] c1), dimensions={1}, to_apply=scalar_add + } + + ENTRY reduce { + p0 = f32[2,2,2]{2,1,0} parameter(0) + p1 = f32[2,2,2]{2,1,0} parameter(1) + element_wise = f32[2,2,2]{2,1,0} fusion(p0, p1), kind=kLoop, calls=fused_element_wise + fusion = (f32[2,2]{1,0}, f32[2,2]{1,0}) fusion(element_wise), kind=kLoop, calls=fused_reduce + ROOT root = (f32[2,2]{1,0}, f32[2,2,2]{2,1,0}) tuple(fusion, element_wise) + })")) + .ValueOrDie(); + const HloInstruction* fusion_1 = + module->entry_computation()->root_instruction()->operand(0); + const HloInstruction* fusion_2 = + module->entry_computation()->root_instruction()->operand(1); + EXPECT_FALSE(ShapesCompatibleForMultiOutputFusion(*fusion_1, *fusion_2)); +} + } // namespace gpu } // namespace xla diff --git a/tensorflow/compiler/xla/service/gpu/gpu_hlo_schedule.cc b/tensorflow/compiler/xla/service/gpu/gpu_hlo_schedule.cc index 91609c730b..1126943624 100644 --- a/tensorflow/compiler/xla/service/gpu/gpu_hlo_schedule.cc +++ b/tensorflow/compiler/xla/service/gpu/gpu_hlo_schedule.cc @@ -37,7 +37,7 @@ class GpuHloOrdering : public PredecessorHloOrdering { public: GpuHloOrdering(const HloModule* module, const StreamAssignment& stream_assignment, - const std::vector& thunk_launch_order); + const std::vector& thunk_launch_order); ~GpuHloOrdering() override = default; // Only the entry computation can possibly be sequentially ordered, and only @@ -56,7 +56,7 @@ class GpuHloOrdering : public PredecessorHloOrdering { GpuHloOrdering::GpuHloOrdering( const HloModule* module, const StreamAssignment& stream_assignment, - const std::vector& thunk_launch_order) + const std::vector& thunk_launch_order) : PredecessorHloOrdering(module) { // The entry computation has a total order when there's only one stream. if (stream_assignment.StreamCount() == 1) { @@ -150,7 +150,7 @@ GpuHloOrdering::GpuHloOrdering( // However, if the total order is A,B,D,C,E, then C and E can run // concurrently. void BFSLaunchOrder(const HloComputation* computation, - std::vector* launch_order) { + std::vector* launch_order) { // This topological sort uses two data structures: // 1. `incoming_edge_count` which keeps track of the number of incoming // edges to each HLO; @@ -158,9 +158,9 @@ void BFSLaunchOrder(const HloComputation* computation, // // The sorting algorithm repeatedly pops the top from the queue and deletes // that HLO from the graph, making more HLOs incoming-edge free. - std::deque queue; + std::deque queue; std::unordered_map incoming_edge_count; - for (const auto& hlo : computation->instructions()) { + for (auto* hlo : computation->instructions()) { if (hlo->operand_count() == 0) { queue.push_back(hlo); } else { @@ -172,10 +172,10 @@ void BFSLaunchOrder(const HloComputation* computation, } while (!queue.empty()) { - const HloInstruction* x = queue.front(); + HloInstruction* x = queue.front(); queue.pop_front(); launch_order->push_back(x); - for (const HloInstruction* y : x->users()) { + for (HloInstruction* y : x->users()) { --incoming_edge_count[y]; if (incoming_edge_count[y] == 0) { queue.push_back(y); @@ -195,14 +195,14 @@ StatusOr> GpuHloSchedule::Build( std::unique_ptr schedule(new GpuHloSchedule); // Initialize thunk_launch_order_, the total order of thunk launches. - const HloComputation* entry_computation = module.entry_computation(); + HloComputation* entry_computation = module.entry_computation(); if (stream_assignment.StreamCount() == 1) { // All kernels are launched on a single stream, so there's no loss of // concurrency by optimizing for minimal memory usage. TF_ASSIGN_OR_RETURN( HloInstructionSequence sequence, ScheduleComputation( - *entry_computation, [pointer_size](const BufferValue& buffer) { + entry_computation, [pointer_size](const BufferValue& buffer) { return ShapeUtil::ByteSizeOf(buffer.shape(), pointer_size); })); schedule->thunk_launch_order_ = sequence.instructions(); diff --git a/tensorflow/compiler/xla/service/gpu/gpu_hlo_schedule.h b/tensorflow/compiler/xla/service/gpu/gpu_hlo_schedule.h index 07a7fc67aa..7f224ffe4f 100644 --- a/tensorflow/compiler/xla/service/gpu/gpu_hlo_schedule.h +++ b/tensorflow/compiler/xla/service/gpu/gpu_hlo_schedule.h @@ -46,7 +46,7 @@ class GpuHloSchedule { // Returns the total order of thunk launches, represented in terms of HLO // instructions. - const std::vector& ThunkLaunchOrder() const { + const std::vector& ThunkLaunchOrder() const { return thunk_launch_order_; } @@ -60,7 +60,7 @@ class GpuHloSchedule { private: GpuHloSchedule(); - std::vector thunk_launch_order_; + std::vector thunk_launch_order_; std::unique_ptr hlo_ordering_; }; diff --git a/tensorflow/compiler/xla/service/gpu/gpu_hlo_schedule_test.cc b/tensorflow/compiler/xla/service/gpu/gpu_hlo_schedule_test.cc index 6d3aed15eb..91db7151f2 100644 --- a/tensorflow/compiler/xla/service/gpu/gpu_hlo_schedule_test.cc +++ b/tensorflow/compiler/xla/service/gpu/gpu_hlo_schedule_test.cc @@ -33,7 +33,7 @@ namespace gpu { class GpuHloScheduleTest : public HloTestBase { protected: - using HloVec = std::vector; + using HloVec = std::vector; // Pre-canned shapes. Shape f32_2x2_ = ShapeUtil::MakeShape(F32, {2, 2}); @@ -44,7 +44,7 @@ class GpuHloScheduleTest : public HloTestBase { .ConsumeValueOrDie(); } - std::unique_ptr CreateNewUnverifiedModule() { + std::unique_ptr CreateNewVerifiedModule() { HloModuleConfig config; auto debug_options = GetDebugOptionsForTest(); debug_options.set_xla_gpu_disable_multi_streaming(false); @@ -79,7 +79,7 @@ TEST_F(GpuHloScheduleTest, SequentialMatMul) { HloInstruction* dot2 = builder.AddInstruction(CreateCanonicalDot(f32_2x2_, dot1, z)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build(dot2)); std::unique_ptr streams = AssignStreams(*module); @@ -139,7 +139,7 @@ TEST_F(GpuHloScheduleTest, SequentialAdd) { HloInstruction* add3 = builder.AddInstruction( HloInstruction::CreateBinary(f32_2x2_, HloOpcode::kAdd, add1, add2)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build(add3)); std::unique_ptr streams = AssignStreams(*module); @@ -209,7 +209,7 @@ TEST_F(GpuHloScheduleTest, ConcurrentMatMul) { HloInstruction* add = builder.AddInstruction(CreateCanonicalDot(f32_2x2_, dot1, dot2)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build(add)); std::unique_ptr streams = AssignStreams(*module); @@ -288,7 +288,7 @@ TEST_F(GpuHloScheduleTest, LatticeMatMul) { HloInstruction* d40 = builder.AddInstruction(CreateCanonicalDot(f32_2x2_, d30, d31)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build(d40)); std::unique_ptr streams = AssignStreams(*module); diff --git a/tensorflow/compiler/xla/service/gpu/gpu_layout_assignment.cc b/tensorflow/compiler/xla/service/gpu/gpu_layout_assignment.cc index 1c0a23fa3e..f59da2caa1 100644 --- a/tensorflow/compiler/xla/service/gpu/gpu_layout_assignment.cc +++ b/tensorflow/compiler/xla/service/gpu/gpu_layout_assignment.cc @@ -65,8 +65,8 @@ HeuristicLayoutAssignment(const HloInstruction* instr, VLOG(2) << "Using heuristic to figure out layouts for " << instr->ToString(); - // Empirically we've found with Volta and cudnn 7 that backward-input convs - // with stride are significantly faster with NCHW layouts. + // Empirically we've found with Volta and cudnn <= 7.3 that backward-input + // convs with stride are significantly faster with NCHW layouts. // // We could have used a mixed layout combination, e.g. (NHWC, NCHW, NCHW), // which on paper gives good performance. However, there are two observations: @@ -75,11 +75,17 @@ HeuristicLayoutAssignment(const HloInstruction* instr, // * we've also observed that for mixed layouts, cuDNN transposes data back // and forth from a different layout combination. If we end up with // transposes anyway, we prefer to have them in XLA, as they can be fused. - // TODO(timshen): Figure out the exact condition. This may be achieved by - // auto-tuning layouts offline. - if (instr->custom_call_target() == kCudnnConvBackwardInputCallTarget && - window_util::HasStride(instr->window())) { - return kAllNCHW; + if (auto* dnn = stream_executor->AsDnn()) { + auto version_status = dnn->GetVersion(); + if (version_status.ok()) { + auto version = version_status.ConsumeValueOrDie(); + if (std::make_tuple(version.major_version(), version.minor_version()) <= + std::make_tuple(7, 3) && + instr->custom_call_target() == kCudnnConvBackwardInputCallTarget && + window_util::HasStride(instr->window())) { + return kAllNCHW; + } + } } // For other Volta f16 convolutions, use NHWC. diff --git a/tensorflow/compiler/xla/service/gpu/gpu_layout_assignment_test.cc b/tensorflow/compiler/xla/service/gpu/gpu_layout_assignment_test.cc index 8cc76c872c..2ffc8bfb49 100644 --- a/tensorflow/compiler/xla/service/gpu/gpu_layout_assignment_test.cc +++ b/tensorflow/compiler/xla/service/gpu/gpu_layout_assignment_test.cc @@ -61,7 +61,7 @@ TEST_F(LayoutAssignmentTest, Elementwise) { HloInstruction::CreateParameter(1, ashape, "y")); auto add = builder.AddInstruction( HloInstruction::CreateBinary(ashape, HloOpcode::kAdd, x, y)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); HloComputation* computation = module->AddEntryComputation(builder.Build(add)); @@ -148,7 +148,7 @@ TEST_F(LayoutAssignmentTest, BatchNormInference) { {operand, scale, offset, mean, variance, epsilon, feature_index}, kCudnnBatchNormForwardInferenceCallTarget)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); HloComputation* computation = module->AddEntryComputation(builder.Build(batchnorm)); @@ -217,7 +217,7 @@ TEST_F(LayoutAssignmentTest, BatchNormTraining) { batchnorm_shape, {operand, scale, offset, epsilon, feature_index}, kCudnnBatchNormForwardTrainingCallTarget)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); HloComputation* computation = module->AddEntryComputation(builder.Build(batchnorm)); @@ -298,7 +298,7 @@ TEST_F(LayoutAssignmentTest, BatchNormGrad) { feature_index}, kCudnnBatchNormBackwardCallTarget)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); HloComputation* computation = module->AddEntryComputation(builder.Build(batchnorm)); diff --git a/tensorflow/compiler/xla/service/gpu/instruction_fusion.cc b/tensorflow/compiler/xla/service/gpu/instruction_fusion.cc index 43f43b50e4..6151dd8ff4 100644 --- a/tensorflow/compiler/xla/service/gpu/instruction_fusion.cc +++ b/tensorflow/compiler/xla/service/gpu/instruction_fusion.cc @@ -80,7 +80,7 @@ bool IsIEEEFloatingPointScalarConstant(const HloInstruction* constant) { // This function limits the maximum number of operands to a fusion. // // There's a cap on how many parameters we can pass to a CUDA kernel, but -// exactly what that limit is is hazy, as it depends on (among other things) how +// exactly what that limit is hazy, as it depends on (among other things) how // much GPU constant memory is in use for other purposes. // // Moreover, we don't even know at the point that we're running fusion how many @@ -181,7 +181,8 @@ bool GpuInstructionFusion::ShouldFuse(HloInstruction* consumer, return true; } } else if (consumer->operand_count() == 2 && - consumer->opcode() == HloOpcode::kAdd) { + consumer->opcode() == HloOpcode::kAdd && + consumer->operand(other_operand_index) != producer) { // Fuse a bias add into the output of the dot. return true; } diff --git a/tensorflow/compiler/xla/service/gpu/instruction_fusion_test.cc b/tensorflow/compiler/xla/service/gpu/instruction_fusion_test.cc index fb77bc4b8e..688604cd36 100644 --- a/tensorflow/compiler/xla/service/gpu/instruction_fusion_test.cc +++ b/tensorflow/compiler/xla/service/gpu/instruction_fusion_test.cc @@ -117,7 +117,7 @@ TEST_F(InstructionFusionTest, PotentialBitcastReshapeOfDotUnfused) { auto reshape2 = builder.AddInstruction(HloInstruction::CreateReshape( ShapeUtil::MakeShape(S32, {1, 1, 1}), dot1)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); EXPECT_EQ(reshape2, computation->root_instruction()); EXPECT_FALSE(GpuInstructionFusion(/*may_duplicate=*/true) @@ -134,7 +134,7 @@ TEST_F(InstructionFusionTest, PotentialBitcastTransposeOfDotUnfused) { auto transpose2 = builder.AddInstruction(HloInstruction::CreateTranspose( ShapeUtil::MakeShape(S32, {1, 1}), dot1, {0, 1})); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); EXPECT_EQ(transpose2, computation->root_instruction()); EXPECT_FALSE(GpuInstructionFusion(/*may_duplicate=*/true) @@ -358,6 +358,29 @@ TEST_F(InstructionFusionTest, DotOutputFusionBiasAdd) { op::Parameter())); } +TEST_F(InstructionFusionTest, + DotOperationFusion_DontOutputFuseDuplicateOperands) { + absl::string_view module_string = R"( +HloModule module + +ENTRY main { + a = f32[50,60]{1,0} parameter(0) + b = f32[60,1]{1,0} parameter(1) + c = f32[50,1]{1,0} dot(a, b), lhs_contracting_dims={1}, rhs_contracting_dims={0} + ROOT d = f32[50,1]{1,0} add(c, c) +} +)"; + + TF_ASSERT_OK_AND_ASSIGN(std::unique_ptr module, + ParseAndReturnVerifiedModule(module_string)); + TF_ASSERT_OK_AND_ASSIGN( + bool fused_something, + GpuInstructionFusion(/*may_duplicate=*/false).Run(module.get())); + EXPECT_FALSE(fused_something); + EXPECT_THAT(module->entry_computation()->root_instruction(), + Not(op::Fusion())); +} + // Compute sum(1/p0), where p0 has type f32, twice. Check that the division is // duplicated and fused into both reduces. TEST_F(InstructionFusionTest, FloatingPointDivIsCheap) { @@ -723,7 +746,7 @@ TEST_F(InstructionFusionTest, AvoidsLargeFusion) { sum = b.AddInstruction( HloInstruction::CreateBinary(shape, HloOpcode::kAdd, sum, param)); } - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(b.Build()); EXPECT_TRUE(GpuInstructionFusion(/*may_duplicate=*/true) .Run(module.get()) diff --git a/tensorflow/compiler/xla/service/gpu/ir_emitter.cc b/tensorflow/compiler/xla/service/gpu/ir_emitter.cc index 7fcdd805ed..6693f66d62 100644 --- a/tensorflow/compiler/xla/service/gpu/ir_emitter.cc +++ b/tensorflow/compiler/xla/service/gpu/ir_emitter.cc @@ -63,9 +63,6 @@ IrEmitter::IrEmitter(const HloModuleConfig& hlo_module_config, &ir_emitter_context->buffer_assignment(), &b_, module_, is_nested), hlo_module_config_(hlo_module_config) { - b_.setFastMathFlags(llvm_ir::GetFastMathFlags( - /*fast_math_enabled=*/hlo_module_config.debug_options() - .xla_gpu_enable_fast_math())); } Status IrEmitter::DefaultAction(HloInstruction* hlo) { @@ -97,6 +94,18 @@ Status IrEmitter::HandleBitcast(HloInstruction* bitcast) { return Status::OK(); } +Status IrEmitter::HandleAddDependency(HloInstruction* add_dependency) { + VLOG(2) << "HandleAddDependency: " << add_dependency->ToString(); + const HloInstruction* operand = add_dependency->operand(0); + // Add_Dependency is a no-op, but we still want to bind it to an llvm::Value + // sometimes, e.g., when it's operand is a constant or a bitcast of a + // constant. + if (bindings_.BoundToIrValue(*operand)) { + bindings_.BindHloToIrValue(*add_dependency, GetBasePointer(*operand)); + } + return Status::OK(); +} + Status IrEmitter::HandleGetTupleElement(HloInstruction* get_tuple_element) { auto operand = get_tuple_element->operand(0); CHECK(bindings_.BoundToIrValue(*operand)); diff --git a/tensorflow/compiler/xla/service/gpu/ir_emitter.h b/tensorflow/compiler/xla/service/gpu/ir_emitter.h index 56c3f45200..2da46c0169 100644 --- a/tensorflow/compiler/xla/service/gpu/ir_emitter.h +++ b/tensorflow/compiler/xla/service/gpu/ir_emitter.h @@ -100,6 +100,7 @@ class IrEmitter : public DfsHloVisitorWithDefault, Status HandleBatchNormInference(HloInstruction* batch_norm) override; Status HandleBatchNormTraining(HloInstruction* batch_norm) override; Status HandleBatchNormGrad(HloInstruction* batch_norm) override; + Status HandleAddDependency(HloInstruction* add_dependency) override; Status FinishVisit(HloInstruction* root) override { return Status::OK(); } diff --git a/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.cc b/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.cc index 87b6cd640a..bbe1583c01 100644 --- a/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.cc +++ b/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.cc @@ -22,7 +22,6 @@ limitations under the License. #include "tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.h" #include "absl/algorithm/container.h" -#include "absl/container/inlined_vector.h" #include "absl/memory/memory.h" #include "absl/strings/str_cat.h" #include "absl/types/optional.h" @@ -65,11 +64,11 @@ limitations under the License. #include "tensorflow/compiler/xla/service/hlo_casting_utils.h" #include "tensorflow/compiler/xla/service/hlo_computation.h" #include "tensorflow/compiler/xla/service/hlo_instruction.h" +#include "tensorflow/compiler/xla/service/hlo_instructions.h" #include "tensorflow/compiler/xla/service/hlo_opcode.h" #include "tensorflow/compiler/xla/service/llvm_ir/buffer_assignment_util.h" #include "tensorflow/compiler/xla/service/llvm_ir/dynamic_update_slice_util.h" #include "tensorflow/compiler/xla/service/llvm_ir/fused_ir_emitter.h" -#include "tensorflow/compiler/xla/service/llvm_ir/kernel_support_library.h" #include "tensorflow/compiler/xla/service/llvm_ir/llvm_util.h" #include "tensorflow/compiler/xla/service/llvm_ir/sort_util.h" #include "tensorflow/compiler/xla/service/llvm_ir/tuple_ops.h" @@ -88,6 +87,8 @@ limitations under the License. namespace xla { namespace gpu { +using llvm_ir::KernelMappingScheme; + namespace { using absl::InlinedVector; @@ -546,91 +547,7 @@ Status IrEmitterUnnested::HandleFusion(HloInstruction* fusion) { // TODO(b/112040122): Support variadic reduce. return Unimplemented("Variadic reduce is not supported on GPU"); } - VLOG(3) << "Emitting fused reduction to vector: " << fusion->ToString(); - std::vector> thunks; - absl::Span output_instructions = - root->opcode() == HloOpcode::kTuple - ? root->operands() - : absl::Span(&root, 1); - - // For multi-output fusion emit an initializer for each tuple element. - // Otherwise it's sufficient to just initialize the single output. - HloInstruction* first_reduce = nullptr; - for (int i = 0, e = output_instructions.size(); i != e; ++i) { - if (output_instructions[i]->opcode() == HloOpcode::kReduce) { - TF_ASSIGN_OR_RETURN( - std::unique_ptr initializer_thunk, - BuildInitializerThunk(fusion, output_instructions[i] == root - ? ShapeIndex() - : ShapeIndex({i}))); - thunks.push_back(std::move(initializer_thunk)); - first_reduce = - first_reduce == nullptr ? output_instructions[i] : first_reduce; - } - } - CHECK(first_reduce != nullptr); - std::unique_ptr kernel_thunk = - BuildKernelThunk(fusion, /*implements_whole_instruction=*/false); - GpuElementalIrEmitter elemental_emitter( - hlo_module_config_, ir_emitter_context_->llvm_module(), &b_, - GetNestedComputer()); - FusedIrEmitter fused_emitter(GetGeneratorForOperandIrArrays(fusion), - &elemental_emitter); - TF_RETURN_IF_ERROR(root->Accept(&fused_emitter)); - - // For multi-output fusion CHECK the constraints and feed all the - // reduces into a single loop code generator. Single-output reduce - // fusion is a special case of that. - InlinedVector input_gens; - InlinedVector init_value_gens; - std::vector> - extra_output_gens; - InlinedVector reducers; - InlinedVector reduce_output_shapes; - for (int i = 0, e = output_instructions.size(); i != e; ++i) { - const HloInstruction* inst = output_instructions[i]; - ShapeIndex output_shape_index; - if (root->opcode() == HloOpcode::kTuple) { - output_shape_index = {i}; - } - if (inst->opcode() == HloOpcode::kReduce) { - CHECK(IsReductionToVector(*inst)) - << "Only reductions to vector are supported"; - // Shapes, layouts and dimensions must be the same for all reduces - // inside of this fusion. - CHECK(ShapeUtil::Equal(first_reduce->shape(), inst->shape())); - CHECK(ShapeUtil::Equal(first_reduce->operand(0)->shape(), - inst->operand(0)->shape())); - CHECK(ShapeUtil::Equal(first_reduce->operand(1)->shape(), - inst->operand(1)->shape())); - CHECK(first_reduce->dimensions() == inst->dimensions()); - input_gens.push_back(fused_emitter.GetGenerator(inst->operand(0))); - init_value_gens.push_back( - fused_emitter.GetGenerator(inst->operand(1))); - reducers.push_back(inst->to_apply()); - reduce_output_shapes.push_back(std::move(output_shape_index)); - } else { - // For extra outputs we can relax shape equality to allow different - // types (with the same number of elements). Layouts still have to - // match. - CHECK(ShapeUtil::CompatibleIgnoringElementType( - first_reduce->operand(0)->shape(), inst->shape())); - CHECK(LayoutUtil::Equal(first_reduce->operand(0)->shape().layout(), - inst->shape().layout())); - extra_output_gens.emplace_back(fused_emitter.GetGenerator(inst), - std::move(output_shape_index)); - } - } - const Shape& input_shape = first_reduce->operand(0)->shape(); - TF_CHECK_OK(EmitReductionToVector( - kernel_thunk.get(), first_reduce, input_shape, input_gens, - init_value_gens, first_reduce->dimensions(), reducers, - reduce_output_shapes, extra_output_gens)); - thunks.push_back(std::move(kernel_thunk)); - std::unique_ptr sequential_thunk = - absl::make_unique(std::move(thunks), fusion); - AddThunkToThunkSequence(std::move(sequential_thunk)); - return Status::OK(); + return EmitReductionToVector(fusion); } default: LOG(FATAL) << "Bad opcode for input fusion: " @@ -700,13 +617,12 @@ Status IrEmitterUnnested::HandleCopy(HloInstruction* copy) { } Status IrEmitterUnnested::EmitExtraOutputsForReduce( - const HloInstruction* reduce, const IrArray::Index& index, + const HloInstruction* unnested_hlo, const IrArray::Index& index, absl::Span> extra_output_gens) { for (int i = 0; i != extra_output_gens.size(); ++i) { - const HloInstruction* output = reduce->parent()->FusionInstruction(); llvm::Value* extra_output_address = - GetIrArray(*output, *output, extra_output_gens[i].second) + GetIrArray(*unnested_hlo, *unnested_hlo, extra_output_gens[i].second) .EmitArrayElementAddress(index, &b_, "extra_output_element_address"); TF_ASSIGN_OR_RETURN(llvm::Value* const extra_output_ir_value, @@ -716,984 +632,13 @@ Status IrEmitterUnnested::EmitExtraOutputsForReduce( return Status::OK(); } -Status IrEmitterUnnested::EmitReductionToScalar( - KernelThunk* kernel_thunk, HloInstruction* reduce, const Shape& input_shape, - absl::Span input_gens, - absl::Span init_value_gens, - absl::Span reducers, - absl::Span reduce_output_shapes, - absl::Span> - extra_output_gens) { - // Number of elements processed by a single thread. - constexpr int64 kTileSize = 16; - int64 num_elems = ShapeUtil::ElementsIn(input_shape); - - // Round up the number of tiles to a multiple of the warp size. This is - // necessary for correctness. We launch one thread per tile, and if the - // number of threads isn't a multiple of the number of the warp size, our - // shuffles will read from inactive threads, producing undefined values. - int64 num_tiles = - RoundUpToNearest(CeilOfRatio(num_elems, kTileSize), kWarpSize); - - Shape tiled_input_shape = ShapeUtil::MakeShapeWithLayout( - reduce->shape().element_type(), {num_tiles}, {0}); - LaunchDimensions launch_dimensions = CalculateLaunchDimensions( - tiled_input_shape, ir_emitter_context_->device_description()); - - llvm::Type* index_ty = - GetIndexTypeForKernel(reduce, launch_dimensions.launch_bound(), &b_); - - auto index_typed_constant = [&](uint64 c) -> llvm::Constant* { - return llvm::ConstantInt::get(index_ty, c); - }; - - // Check whether every thread will process a full tile's worth of elements - // without reading outside the bounds of the input. If this is true, we can - // skip some bounds checks in the final algorithm. - bool all_threads_in_bounds = num_tiles * kTileSize == num_elems; - - // __global__ void full_reduce_kernel() { - // x_in_tiles = threadIdx.x + blockIdx.x * blockDim.x; - // x = x_in_tiles * kTileSize; - // - // partial_result = init_value; - // if (all_threads_in_bounds || x + kTileSize <= num_elems) { - // for (i = 0; i < kTileSize; ++i) { - // partial_result = Reducer(partial_result, input[x + i]); - // } - // } else { - // for (i = 0; i < kTileSize; ++i) { - // if (x + i < num_elems) { - // partial_result = Reducer(partial_result, input[x + i]); - // } - // } - // } - // for (i = warpSize / 2; i > 0; i /= 2) { - // partial_result = Reducer(partial_result, - // __shfl_down(partial_result, i)); - // } - // if (lane_id == 0) { - // AtomicReducer(&output[y], partial_result); - // } - // } - // - // // Choose num_blocks and threads_per_block such that: - // // - // // num_blocks * threads_per_block = - // // RoundUpToNextMultipleOf(Ceil(num_elems / kTileSize), warpSize), - // // - // // and threads_per_block is a multiple of warpSize. - // reduce_kernel // - auto loop_body_emitter = [=](const IrArray::Index& tile_index) -> Status { - const int num_reduces = reducers.size(); - llvm::Type* element_ir_type = - llvm_ir::PrimitiveTypeToIrType(input_shape.element_type(), module_); - std::vector partial_reduction_result_addresses; - for (int i = 0; i != num_reduces; ++i) { - llvm::Value* partial_reduction_result_address = - Alloca(element_ir_type, /*ArraySize=*/nullptr, - "partial_reduction_result." + llvm::Twine(i)); - TF_ASSIGN_OR_RETURN(llvm::Value* const init_ir_value, - init_value_gens[i](IrArray::Index(index_ty))); - Store(init_ir_value, partial_reduction_result_address); - partial_reduction_result_addresses.push_back( - partial_reduction_result_address); - } - - llvm::Value* x_in_tiles = tile_index[0]; - x_in_tiles = ZExtOrTrunc(x_in_tiles, index_ty); - - // Emit an inner for-loop that reduces the elements in the tile. - auto emit_tile_element_loop = [=](bool tile_in_bounds) -> Status { - std::unique_ptr tile_element_loop = - llvm_ir::ForLoop::EmitForLoop( - "element_id_in_tile", index_typed_constant(0), - index_typed_constant(kTileSize), index_typed_constant(1), &b_); - - // Emit the body of the partial reduction loop. - llvm_ir::SetToFirstInsertPoint(tile_element_loop->GetBodyBasicBlock(), - &b_); - llvm::Value* x = - NSWAdd(NSWMul(x_in_tiles, index_typed_constant(kTileSize)), - tile_element_loop->GetIndVarValue()); - // Unless we know the tile is entirely in bounds, we have to emit a - // x-in-bounds check before reading from the input. - if (!tile_in_bounds) { - llvm_ir::LlvmIfData if_data = llvm_ir::EmitIfThenElse( - ICmpULT(x, index_typed_constant(num_elems)), "x_in_bounds", &b_); - - // Emit code that reads the input element and accumulates it to - // the partial reduction result. - llvm_ir::SetToFirstInsertPoint(if_data.true_block, &b_); - } - - IrArray::Index input_index( - /*linear=*/x, input_shape, &b_); - llvm::Value* input_address = Alloca(element_ir_type); - for (int i = 0; i != num_reduces; ++i) { - TF_ASSIGN_OR_RETURN(llvm::Value* const input_ir_value, - input_gens[i](input_index)); - Store(input_ir_value, input_address); - TF_RETURN_IF_ERROR(EmitCallToNestedComputation( - *reducers[i], - {partial_reduction_result_addresses[i], input_address}, - partial_reduction_result_addresses[i])); - } - return EmitExtraOutputsForReduce(reduce, input_index, extra_output_gens); - }; - - // x_end = kTileSize + x_in_tiles * kTileSize, i.e., the location that's - // immediately beyond the tile. - llvm::Value* x_end = - NSWAdd(index_typed_constant(kTileSize), - NSWMul(x_in_tiles, index_typed_constant(kTileSize))); - // The tile is entirely in bound if all_threads_in_bounds or - // x_end <= num_elems. - llvm::Value* tile_in_bounds = - Or(ICmpULE(x_end, index_typed_constant(num_elems)), - b_.getInt1(all_threads_in_bounds)); - llvm_ir::LlvmIfData if_tile_in_bounds_data = - llvm_ir::EmitIfThenElse(tile_in_bounds, "tile_in_bounds", &b_); - llvm_ir::SetToFirstInsertPoint(if_tile_in_bounds_data.true_block, &b_); - TF_RETURN_IF_ERROR(emit_tile_element_loop(/*tile_in_bounds=*/true)); - llvm_ir::SetToFirstInsertPoint(if_tile_in_bounds_data.false_block, &b_); - TF_RETURN_IF_ERROR(emit_tile_element_loop(/*tile_in_bounds=*/false)); - - // After the if-then-else statement on tile_in_bounds, emit calls to - // shfl_down that accumulate the partial reduction results of all threads - // from the warp. - llvm_ir::SetToFirstInsertPoint(if_tile_in_bounds_data.after_block, &b_); - int bit_width = llvm_ir::GetSizeInBits(element_ir_type); - // bitcast cannot be applied to aggregate types (even packed ones), so we - // instead bitcast addresses of load/store to intN* of the same bit-width. - llvm::Type* shuffle_ir_type = element_ir_type->isStructTy() - ? b_.getIntNTy(bit_width) - : element_ir_type; - for (int shuffle_distance = kWarpSize / 2; shuffle_distance >= 1; - shuffle_distance /= 2) { - llvm::Value* result_from_other_lane = - Alloca(element_ir_type, nullptr, "result_from_other_lane"); - for (int i = 0; i != num_reduces; ++i) { - llvm::Value* partial_reduction_result = - Load(BitCast(partial_reduction_result_addresses[i], - shuffle_ir_type->getPointerTo()), - "partial_reduction_result"); - CHECK_EQ(launch_dimensions.threads_per_block() % kWarpSize, 0) - << "Requires block size a multiple of the warp size, otherwise we " - "will read undefined elements."; - Store(EmitFullWarpShuffleDown(partial_reduction_result, - b_.getInt32(shuffle_distance), &b_), - BitCast(result_from_other_lane, shuffle_ir_type->getPointerTo())); - TF_RETURN_IF_ERROR(EmitCallToNestedComputation( - *reducers[i], - {partial_reduction_result_addresses[i], result_from_other_lane}, - partial_reduction_result_addresses[i])); - } - } - - const HloInstruction* output = - reduce->IsFused() ? reduce->parent()->FusionInstruction() : reduce; - - // Emit an atomic operation that accumulates the partial reduction result of - // lane 0 (which holds the partially accumulated result for its warp) to the - // output element. - llvm::Value* lane_id = - URem(x_in_tiles, index_typed_constant(kWarpSize), "lane_id"); - llvm_ir::LlvmIfData if_lane_id_is_zero_data = llvm_ir::EmitIfThenElse( - ICmpEQ(lane_id, index_typed_constant(0)), "lane_id_is_zero", &b_); - llvm_ir::SetToFirstInsertPoint(if_lane_id_is_zero_data.true_block, &b_); - - for (int i = 0; i != num_reduces; ++i) { - llvm::Value* output_address = - GetIrArray(*output, *output, reduce_output_shapes[i]) - .EmitArrayElementAddress( - IrArray::Index( - /*linear=*/b_.getInt64(0), - ShapeUtil::GetSubshape(output->shape(), - reduce_output_shapes[i]), - &b_), - &b_, "output_element_address"); - TF_RETURN_IF_ERROR(EmitAtomicOperationForNestedComputation( - *reducers[i], output_address, partial_reduction_result_addresses[i])); - } - return Status::OK(); - }; - - // Emit a parallel loop that iterates through all input tiles, one per thread. - UpdateLaunchDimensions(launch_dimensions, kernel_thunk, - ir_emitter_context_->llvm_module()); - return ParallelLoopEmitter(loop_body_emitter, tiled_input_shape, - launch_dimensions, &b_) - .EmitLoop(IrName(reduce), index_ty); -} - -Status IrEmitterUnnested::EmitColumnReduction( - KernelThunk* kernel_thunk, int64 height, int64 width, - HloInstruction* reduce, const Shape& input_shape, - absl::Span input_gens, - absl::Span init_value_gens, - absl::Span reducers, - absl::Span reduce_output_shapes, - absl::Span> - extra_output_gens) { - // Divide the input matrix into tiles of size KxL. For example, when the - // input matrix is 4x4, K=2, and L=1 the tiled matrix looks like - // - // 0123 - // 0123 - // 4567 - // 4567 // Numbers indicate tile IDs. - // - // Each tile is first partially reduced to a scalar by a thread, and then the - // scalar is accumulated to the output vector using atomic operations. - // - // We choose 128 as the tile size based on empirical evidence. It's big enough - // to reduce the amount of atomic adds in the end, maximizing the memory - // bandwidth. A tile width of 2 allows for high memory bandwidth utilization - // on 16b input data. - constexpr int64 kTileHeight = 128; - constexpr int64 kTileWidth = 2; - - // If the height is not a multiple of kTileHeight, we pad the bottom of the - // input matrix. - const int64 height_in_tiles = CeilOfRatio(height, kTileHeight); - // If width is not a multiple of kTileWidth the rightmost thread will process - // fewer input elements. - const int64 width_in_tiles = CeilOfRatio(width, kTileWidth); - Shape tiled_input_shape = - ShapeUtil::MakeShapeWithLayout(reduce->shape().element_type(), - {height_in_tiles, width_in_tiles}, {1, 0}); - LaunchDimensions launch_dimensions = CalculateLaunchDimensions( - tiled_input_shape, ir_emitter_context_->device_description()); - - // TODO(b/110211620): Convert to use i32 index_type when it is possible. - llvm::Type* index_ty = b_.getInt64Ty(); - - auto index_typed_constant = [&](uint64 c) -> llvm::Constant* { - return llvm::ConstantInt::get(index_ty, c); - }; - - // for (linear_index = threadIdx.x + blockIdx.x * blockDim.x; - // linear_index < height_in_tiles * width_in_tiles; - // linear_index += blockDim.x * gridDim.x) { - // y_in_tiles = linear_index / width_in_tiles; - // x_in_tiles = linear_index % width_in_tiles; - // - // partial_results[kTileWidth] = init_values; - // tile_in_y_bounds = height % kTileHeight == 0 || - // y_in_tiles * kTileHeight + kTileHeight <= height; - // tile_in_x_bounds = width % kTileWidth == 0 || - // x_in_tiles * kTileWidth + kTileWidth <= width; - // // The implementation handles y and x bound checks separately. - // if (tile_in_y_bounds && tile_in_x_bounds) { - // for (y_offset : range(kTileHeight)) { - // y = y_in_tiles * kTileHeight + y_offset; - // for (x_offset : range(kTileWidth)) { - // x = x_in_tiles * kTileWidth + x_offset; - // partial_result = Reducer(partial_result[x_offset], input[y][x]); - // } - // } - // } else { - // for (y_offset : range(kTileHeight)) { - // y = y_in_tiles * kTileHeight + y_offset; - // for (y_offset : range(kTileHeight)) { - // x = x_in_tiles * kTileWidth + x_offset; - // if (y < height && x < width) { - // partial_result = Reducer(partial_result, input[y][x]); - // } - // } - // } - // } - // for (x_offset : range(kTileWidth)) { - // AtomicReducer(&output[x + x_offset], partial_result[x_offset]); - // } - // } - auto loop_body_emitter = [=](const IrArray::Index& tile_index) -> Status { - const int num_reduces = reducers.size(); - // Emit the loop body that reduces one tile. - llvm::Type* element_ir_type = - llvm_ir::PrimitiveTypeToIrType(input_shape.element_type(), module_); - std::vector partial_reduction_result_addresses; - for (int i = 0; i != num_reduces; ++i) { - for (int x_offset = 0; x_offset < kTileWidth; ++x_offset) { - llvm::Value* partial_reduction_result_address = - Alloca(element_ir_type, /*ArraySize=*/nullptr, - "partial_reduction_result." + - llvm::Twine(i * kTileWidth + x_offset)); - TF_ASSIGN_OR_RETURN(llvm::Value* const init_ir_value, - init_value_gens[i](IrArray::Index(index_ty))); - Store(init_ir_value, partial_reduction_result_address); - partial_reduction_result_addresses.push_back( - partial_reduction_result_address); - } - } - - // Emit an inner for-loop that partially reduces the elements in the given - // tile. - llvm::Value* y_in_tiles = tile_index[0]; - llvm::Value* x_in_tiles = tile_index[1]; - - y_in_tiles = ZExtOrTrunc(y_in_tiles, index_ty); - x_in_tiles = ZExtOrTrunc(x_in_tiles, index_ty); - - auto emit_tile_element_loop = [=](bool tile_in_y_bounds, - bool tile_in_x_bounds) -> Status { - std::unique_ptr tile_element_loop = - llvm_ir::ForLoop::EmitForLoop( - "element_id_in_tile", index_typed_constant(0), - index_typed_constant(kTileHeight), index_typed_constant(1), &b_); - - // Emit the body of the partial reduction loop. - llvm_ir::SetToFirstInsertPoint(tile_element_loop->GetBodyBasicBlock(), - &b_); - llvm::Value* y = - NSWAdd(NSWMul(y_in_tiles, index_typed_constant(kTileHeight)), - tile_element_loop->GetIndVarValue()); - - // Unless we know that y is in bounds, we have to emit a check before - // reading from the input. - if (!tile_in_y_bounds) { - llvm_ir::LlvmIfData if_data = llvm_ir::EmitIfThenElse( - ICmpULT(y, index_typed_constant(height)), "y_in_bounds", &b_); - - // Emit code that reads the input element and accumulates it to - // the partial reduction result. - llvm_ir::SetToFirstInsertPoint(if_data.true_block, &b_); - } - for (int x_offset = 0; x_offset < kTileWidth; ++x_offset) { - llvm::Value* x = - NSWAdd(NSWMul(x_in_tiles, index_typed_constant(kTileWidth)), - index_typed_constant(x_offset)); - // Unless we know that x is in bounds, we have to emit a check before - // reading from the input. - if (!tile_in_x_bounds) { - llvm_ir::LlvmIfData if_data = llvm_ir::EmitIfThenElse( - ICmpULT(x, index_typed_constant(width)), "x_in_bounds", &b_); - llvm_ir::SetToFirstInsertPoint(if_data.true_block, &b_); - } - llvm::Value* input_address = Alloca(element_ir_type); - // {y,x} is an index to input_matrix_shape [height,width]. We need to - // convert that to an index to input_shape (the shape of the operand of - // "reduce"). This conversion is composed of a transposition from - // input_shape to normalized_input_shape and a reshape from - // normalized_input_shape to input_matrix_shape. - const Shape normalized_input_shape = - ShapeUtil::MakeShapeWithDescendingLayoutAndSamePhysicalLayout( - input_shape); - auto input_shape_min2maj = LayoutUtil::MinorToMajor(input_shape); - const std::vector transpose_dimension_mapping( - input_shape_min2maj.rbegin(), input_shape_min2maj.rend()); - - const Shape input_matrix_shape = - ShapeUtil::MakeShapeWithDescendingLayout(input_shape.element_type(), - {height, width}); - const IrArray::Index input_matrix_index({y, x}, input_matrix_shape, - &b_); - const IrArray::Index input_index = - input_matrix_index - .SourceIndexOfReshape(input_matrix_shape, - normalized_input_shape, &b_) - .SourceIndexOfTranspose(normalized_input_shape, input_shape, - transpose_dimension_mapping, &b_); - for (int i = 0; i != num_reduces; ++i) { - TF_ASSIGN_OR_RETURN(llvm::Value* const input_ir_value, - input_gens[i](input_index)); - Store(input_ir_value, input_address); - TF_RETURN_IF_ERROR(EmitCallToNestedComputation( - *reducers[i], - {partial_reduction_result_addresses[i * kTileWidth + x_offset], - input_address}, - partial_reduction_result_addresses[i * kTileWidth + x_offset])); - TF_RETURN_IF_ERROR(EmitExtraOutputsForReduce(reduce, input_index, - extra_output_gens)); - } - } - return Status::OK(); - }; - - // y_end = kTileHeight + y_in_tiles * kTileHeight, i.e., the y location - // that's immediately beyond the tile. - llvm::Value* y_end = - NSWAdd(index_typed_constant(kTileHeight), - NSWMul(y_in_tiles, index_typed_constant(kTileHeight))); - // x_end = kTileWidth + x_in_tiles * kTileWidth, i.e., the x location - // that's immediately beyond the tile. - llvm::Value* x_end = - NSWAdd(index_typed_constant(kTileWidth), - NSWMul(x_in_tiles, index_typed_constant(kTileWidth))); - llvm::Value* tile_in_y_bounds = - Or(ICmpULE(y_end, index_typed_constant(height)), - b_.getInt1(height % kTileHeight == 0)); - llvm::Value* tile_in_x_bounds = - Or(ICmpULE(x_end, index_typed_constant(width)), - b_.getInt1(width % kTileWidth == 0)); - // The tile is in y bounds if "height" is a multiple of kTileHeight or - // y_end <= height. - llvm_ir::LlvmIfData if_tile_in_y_bounds_data = - llvm_ir::EmitIfThenElse(tile_in_y_bounds, "tile_in_y_bounds", &b_); - llvm_ir::SetToFirstInsertPoint(if_tile_in_y_bounds_data.true_block, &b_); - // The tile is in x bounds if "width" is a multiple of kTileWidth or - // x_end <= width. - llvm_ir::LlvmIfData if_tile_in_x_bounds_data = - llvm_ir::EmitIfThenElse(tile_in_x_bounds, "tile_in_x_bounds", &b_); - llvm_ir::SetToFirstInsertPoint(if_tile_in_x_bounds_data.true_block, &b_); - TF_RETURN_IF_ERROR(emit_tile_element_loop(/*tile_in_y_bounds=*/true, - /*tile_in_x_bounds=*/true)); - llvm_ir::SetToFirstInsertPoint(if_tile_in_x_bounds_data.false_block, &b_); - TF_RETURN_IF_ERROR(emit_tile_element_loop(/*tile_in_y_bounds=*/true, - /*tile_in_x_bounds=*/false)); - llvm_ir::SetToFirstInsertPoint(if_tile_in_y_bounds_data.false_block, &b_); - if_tile_in_x_bounds_data = - llvm_ir::EmitIfThenElse(tile_in_x_bounds, "tile_in_x_bounds", &b_); - llvm_ir::SetToFirstInsertPoint(if_tile_in_x_bounds_data.true_block, &b_); - TF_RETURN_IF_ERROR(emit_tile_element_loop(/*tile_in_y_bounds=*/false, - /*tile_in_x_bounds=*/true)); - llvm_ir::SetToFirstInsertPoint(if_tile_in_x_bounds_data.false_block, &b_); - TF_RETURN_IF_ERROR(emit_tile_element_loop(/*tile_in_y_bounds=*/false, - /*tile_in_x_bounds=*/false)); - - // After the nested if-then-else statement on tile_in_y_bounds and - // tile_in_x_bounds, emit atomic operations to accumulate the partial - // reduction result to the output element. - llvm_ir::SetToFirstInsertPoint(if_tile_in_y_bounds_data.after_block, &b_); - const HloInstruction* output = - reduce->IsFused() ? reduce->parent()->FusionInstruction() : reduce; - for (int i = 0; i != num_reduces; ++i) { - for (int x_offset = 0; x_offset < kTileWidth; ++x_offset) { - llvm::Value* x = - NSWAdd(NSWMul(x_in_tiles, index_typed_constant(kTileWidth)), - index_typed_constant(x_offset)); - llvm::Value* output_address = - GetIrArray(*output, *output, reduce_output_shapes[i]) - .EmitArrayElementAddress( - IrArray::Index( - x, - ShapeUtil::GetSubshape(output->shape(), - reduce_output_shapes[i]), - &b_), - &b_, "output_element_address"); - TF_RETURN_IF_ERROR(EmitAtomicOperationForNestedComputation( - *reducers[i], output_address, - partial_reduction_result_addresses[i * kTileWidth + x_offset])); - } - } - return Status::OK(); - }; - - // Emit a parallel loop that iterate through all input tiles. - UpdateLaunchDimensions(launch_dimensions, kernel_thunk, - ir_emitter_context_->llvm_module()); - return ParallelLoopEmitter(loop_body_emitter, tiled_input_shape, - launch_dimensions, &b_) - .EmitLoop(IrName(reduce), index_ty); -} - -static std::pair ComputeTilingSchemeForReduction( - int64 depth, int64 width, int64 kWarpSize) { - constexpr int64 kTargetNumElementsPerThread = 64; - int64 x_tile_size = kTargetNumElementsPerThread; - int64 z_tile_size = 1; - - // Only tile along the x dimension with tile size kTargetNumElementsPerThread - // if doing so doesn't require a slow version of loop with bound check on each - // dimension. A more sophisticated heuristics is to enable tile along the - // x dimension with tile size kTargetNumElementsPerThread when either width is - // a factor of (kWarpSize * kTargetNumElementsPerThread) or width is big - // enough so that only a small fraction of the threads execute the slow - // version of loop with bound check. - if (width % (kWarpSize * kTargetNumElementsPerThread) != 0) { - x_tile_size = 8; - z_tile_size = 8; - while (depth % z_tile_size != 0) { - z_tile_size -= 1; - } - } - - return std::pair(x_tile_size, z_tile_size); -} - -Status IrEmitterUnnested::EmitRowReduction( - KernelThunk* kernel_thunk, int64 depth, int64 height, int64 width, - HloInstruction* reduce, const Shape& input_shape, - absl::Span input_gens, - absl::Span init_value_gens, - absl::Span reducers, - absl::Span reduce_output_shapes, - absl::Span> - extra_output_gens) { - // A naive algorithm is: - // 1. Divide the x dimension of the input tensor into tiles of size 1x1xX. - // 2. Partially reduces each tile to a scalar using one thread. - // 3. Accumulates that scalar to the output vector using atomic operations. - // - // for (linear_index = threadIdx.x + blockIdx.x * blockDim.x; - // linear_index < depth * height * width_in_tiles; - // linear_index += blockDim.x * gridDim.x) { - // int x_in_tiles = linear_index % width_in_tiles; - // int y = linear_index / width_in_tiles % height; - // int z = linear_index / (height * width_in_tiles); - // float partial_result = 0; - // for (element_id_in_tile : range(x_tile_size)) { - // int x = x_in_tiles * x_tile_size + element_id_in_tile; - // if (x < width) - // partial_result = reducer(partial_result, input[z][y][x]); - // } - // AtomicReducer(&output[y], partial_result); - // } - // - // Four optimizations are performed. - // - // 1. To coalesce global memory accesses, dilate the tile with a factor of 32 - // (i.e. the warp size). For example, suppose the width is 8x32=256. Instead - // of making each tile consecutive, we let make tile 0 column - // [0,32,64,...,224], tile 1 column [1,33,65,...,225], and so on. This ensures - // that threads in a warp access consecutive memory in one iteration (i.e. - // coalesced). In the above example, the warp that contains thread 0-31 - // accesses column 0-31 in the first iteration, and 32-63 in the second - // iteration, and so on. - // - // 2. Partially accumulate partial reduced results computed by threads in the - // same warp using shfl_down. Using shfl_down is faster than directly using - // atomic operations because shfl_down transfers the data between threads - // using shared memory and threads in the same warp run in lock step (thus no - // extra synchronization needed). See - // https://devblogs.nvidia.com/parallelforall/faster-parallel-reductions-kepler/ - // for details. The downside is, to produce correct results when using - // shfl_down, we need to guarantee threads in the same warp work on input - // elements with the same y, so the number of tiles in each row must be a - // multiple of 32. - // - // 3. Specialize the case that the entire tile is in bounds. When that is - // true, we don't need to emit "if(x 0; shuffle_distance /= 2) - // partial_result = Reducer( - // partial_result, - // __shfl_down_sync(CUDA_WARP_ALL, partial_result, shuffle_distance)); - // if (lane_id == 0) - // AtomicReducer(&output[y], partial_result); - // } - // - - int64 x_tile_size; - int64 z_tile_size; - std::tie(x_tile_size, z_tile_size) = - ComputeTilingSchemeForReduction(depth, width, kWarpSize); - - // Round the width in tiles up to the nearest multiple of kWarpSize, so that - // the use of shfl_down is valid. - const int64 width_in_tiles = - RoundUpToNearest(CeilOfRatio(width, x_tile_size), kWarpSize); - Shape tiled_input_shape = ShapeUtil::MakeShapeWithLayout( - reduce->shape().element_type(), - {depth / z_tile_size, height, width_in_tiles}, {2, 1, 0}); - LaunchDimensions launch_dimensions = CalculateLaunchDimensions( - tiled_input_shape, ir_emitter_context_->device_description()); - llvm::Type* index_ty = - GetIndexTypeForKernel(reduce, launch_dimensions.launch_bound(), &b_); - - auto index_typed_constant = [&](uint64 c) -> llvm::Constant* { - return llvm::ConstantInt::get(index_ty, c); - }; - - auto loop_body_emitter = [=](const IrArray::Index& tile_index) { - const int num_reduces = reducers.size(); - llvm::Type* element_ir_type = llvm_ir::PrimitiveTypeToIrType( - input_shape.element_type(), ir_emitter_context_->llvm_module()); - std::vector partial_reduction_result_addresses; - for (int i = 0; i != num_reduces; ++i) { - llvm::Value* partial_reduction_result_address = - Alloca(element_ir_type, /*ArraySize=*/nullptr, - "partial_reduction_result." + llvm::Twine(i)); - TF_ASSIGN_OR_RETURN(llvm::Value* const init_ir_value, - init_value_gens[i](IrArray::Index(index_ty))); - Store(init_ir_value, partial_reduction_result_address); - partial_reduction_result_addresses.push_back( - partial_reduction_result_address); - } - - llvm::Value* z_tile = tile_index[0]; - llvm::Value* y = tile_index[1]; - llvm::Value* x_tile = tile_index[2]; - - x_tile = ZExtOrTrunc(x_tile, index_ty); - - llvm::Value* warp_id = - UDiv(x_tile, index_typed_constant(kWarpSize), "warp_id"); - llvm::Value* lane_id = - URem(x_tile, index_typed_constant(kWarpSize), "lane_id"); - - // The x-location of the last element in this z-x-tile. - // last_x = lane_id + warpSize * (x_tile_size - 1 + warp_id * x_tile_size); - llvm::Value* last_x = NSWAdd( - lane_id, - NSWMul(index_typed_constant(kWarpSize), - NSWAdd(index_typed_constant(x_tile_size - 1), - NSWMul(warp_id, index_typed_constant(x_tile_size))))); - - KernelSupportLibrary ksl( - &b_, - /*unroll_mode=*/xla::llvm_ir::UnrollMode::kFullyUnroll, - /*prevent_vectorization=*/false); - - // Emit a for-loop that partially reduces the elements in the given - // z-x-tile. - auto emit_z_x_tile_element_loop = [&](bool x_tile_in_bounds, - int64 x_tile_loop_bound) -> Status { - auto emit_z_tile_element_loop = [&](llvm::Value* z_indvar) -> Status { - llvm::Value* z = - NSWAdd(z_indvar, NSWMul(index_typed_constant(z_tile_size), z_tile)); - TF_RETURN_IF_ERROR(ksl.For( - "x_tile", - /*start=*/index_typed_constant(0), - /*end=*/index_typed_constant(x_tile_loop_bound), - /*step=*/1, [&](llvm::Value* x_indvar) -> Status { - // x = lane_id + - // warpSize * (element_id_in_x_tile + warp_id * x_tile_size); - llvm::Value* x = NSWAdd( - lane_id, - NSWMul(index_typed_constant(kWarpSize), - NSWAdd(x_indvar, - NSWMul(warp_id, llvm::ConstantInt::get( - index_ty, x_tile_size))))); - - // Unless we know the x-tile is entirely in bounds, we have to - // emit a x-in-bounds check before reading from the input. - if (!x_tile_in_bounds) { - llvm_ir::LlvmIfData if_x_in_bounds_data = - llvm_ir::EmitIfThenElse( - ICmpULT(x, index_typed_constant(width)), "x_in_bounds", - &b_); - // Points b_ to the then-block. - llvm_ir::SetToFirstInsertPoint(if_x_in_bounds_data.true_block, - &b_); - } - - // Emit code that reads the input element and accumulates it - // to the partial reduction result. - llvm::Value* input_address = Alloca(element_ir_type); - { - // {z,y,x} is an index to input_3d_tensor_shape - // [depth,height,width]. We need to convert that to an index - // to input_shape (the shape of the operand of "reduce"). - // This conversion is composed of a transposition from - // input_shape to normalized_input_shape and a reshape from - // normalized_input_shape to input_3d_tensor_shape. - const Shape normalized_input_shape = ShapeUtil:: - MakeShapeWithDescendingLayoutAndSamePhysicalLayout( - input_shape); - auto input_shape_min2maj = - LayoutUtil::MinorToMajor(input_shape); - const std::vector transpose_dimension_mapping( - input_shape_min2maj.rbegin(), input_shape_min2maj.rend()); - const Shape input_3d_tensor_shape = - ShapeUtil::MakeShapeWithDescendingLayout( - input_shape.element_type(), {depth, height, width}); - const IrArray::Index input_3d_tensor_index( - {z, y, x}, input_3d_tensor_shape, &b_); - const IrArray::Index input_index = - input_3d_tensor_index - .SourceIndexOfReshape(input_3d_tensor_shape, - normalized_input_shape, &b_) - .SourceIndexOfTranspose( - normalized_input_shape, input_shape, - transpose_dimension_mapping, &b_); - - for (int i = 0; i != num_reduces; ++i) { - TF_ASSIGN_OR_RETURN(llvm::Value* const input_ir_value, - input_gens[i](input_index)); - Store(input_ir_value, input_address); - TF_RETURN_IF_ERROR(EmitCallToNestedComputation( - *reducers[i], - {partial_reduction_result_addresses[i], input_address}, - partial_reduction_result_addresses[i])); - } - return EmitExtraOutputsForReduce(reduce, input_index, - extra_output_gens); - } - })); - return Status::OK(); - }; - - return ksl.For("z_tile", - /*start=*/index_typed_constant(0), - /*end=*/index_typed_constant(z_tile_size), - /*step=*/1, emit_z_tile_element_loop); - }; - - llvm::Value* tile_in_bounds = - Or(b_.getInt1(width % (x_tile_size * kWarpSize) == 0), - ICmpULT(last_x, index_typed_constant(width))); - - TF_RETURN_IF_ERROR( - ksl.If(tile_in_bounds, - /*true_block_generator=*/ - [&]() -> Status { - return emit_z_x_tile_element_loop(/*x_tile_in_bounds=*/true, - x_tile_size); - }, - /*false_block_generator=*/ - [&]() -> Status { - return emit_z_x_tile_element_loop( - /*x_tile_in_bounds=*/false, - CeilOfRatio(width % (x_tile_size * kWarpSize), kWarpSize)); - })); - - // After accumulating the elements of the z_x_tile, emit calls to - // shfl_down that accumulate the partial reduction results of all - // threads in a warp. - int bit_width = llvm_ir::GetSizeInBits(element_ir_type); - // bitcast cannot be applied to aggregate types (even packed ones), so we - // instead bitcast addresses of load/store to intN* of the same bit-width. - llvm::Type* shuffle_ir_type = element_ir_type->isStructTy() - ? b_.getIntNTy(bit_width) - : element_ir_type; - for (int shuffle_distance = 16; shuffle_distance >= 1; - shuffle_distance /= 2) { - llvm::Value* result_from_other_lane = - Alloca(element_ir_type, nullptr, "result_from_other_lane"); - for (int i = 0; i != num_reduces; ++i) { - llvm::Value* partial_reduction_result = - Load(BitCast(partial_reduction_result_addresses[i], - shuffle_ir_type->getPointerTo()), - "partial_reduction_result"); - CHECK_EQ(launch_dimensions.threads_per_block() % kWarpSize, 0) - << "Requires block size a multiple of the warp size, otherwise we " - "will read undefined elements."; - Store(EmitFullWarpShuffleDown(partial_reduction_result, - b_.getInt32(shuffle_distance), &b_), - BitCast(result_from_other_lane, shuffle_ir_type->getPointerTo())); - TF_RETURN_IF_ERROR(EmitCallToNestedComputation( - *reducers[i], - {partial_reduction_result_addresses[i], result_from_other_lane}, - partial_reduction_result_addresses[i])); - } - } - - const HloInstruction* output = - reduce->IsFused() ? reduce->parent()->FusionInstruction() : reduce; - - // Emit an atomic operation that accumulates the partial reduction result of - // lane 0 (which holds the partially accumulated result for its warp) to the - // output element. - llvm_ir::LlvmIfData if_lane_id_is_zero_data = llvm_ir::EmitIfThenElse( - ICmpEQ(lane_id, index_typed_constant(0)), "lane_id_is_zero", &b_); - llvm_ir::SetToFirstInsertPoint(if_lane_id_is_zero_data.true_block, &b_); - for (int i = 0; i != num_reduces; ++i) { - llvm::Value* output_address = - GetIrArray(*output, *output, reduce_output_shapes[i]) - .EmitArrayElementAddress( - IrArray::Index(y, - ShapeUtil::GetSubshape( - output->shape(), reduce_output_shapes[i]), - &b_), - &b_, "output_element_address"); - // We don't need to emit atomic operations if there is only one tile of - // results. 'depth' is the z dimension, 'width' is the x dimension. - if (z_tile_size >= depth && x_tile_size >= width) { - TF_RETURN_IF_ERROR(EmitCallToNestedComputation( - *reducers[i], - {output_address, partial_reduction_result_addresses[i]}, - output_address)); - } else { - TF_RETURN_IF_ERROR(EmitAtomicOperationForNestedComputation( - *reducers[i], output_address, - partial_reduction_result_addresses[i])); - } - } - return Status::OK(); - }; - - // Emit a parallel loop that iterates through every input tiles. - UpdateLaunchDimensions(launch_dimensions, kernel_thunk, - ir_emitter_context_->llvm_module()); - return ParallelLoopEmitter(loop_body_emitter, tiled_input_shape, - launch_dimensions, &b_) - .EmitLoop(IrName(reduce), index_ty); -} - -// Figures out whether `reduce` is a row or column reduction, and which -// dimensions to reduce, and calls either `EmitRowReduction` or -// `EmitColumnReduction` as appropriate. -// Prerequisite: all the dimensions to keep are contiguous in the input layout -// and, if `reduce` is fused, the fused subgraph is pure -// elementwise. -Status IrEmitterUnnested::EmitReductionToVector( - KernelThunk* kernel_thunk, HloInstruction* reduce, const Shape& input_shape, - absl::Span input_gens, - absl::Span init_value_gens, - absl::Span dimensions_to_reduce, - absl::Span reducers, - absl::Span reduce_output_shapes, - absl::Span> - extra_output_gens) { - // This emission requires "reduce" to have an input layout. It is either set - // by LayoutAssignment (for a top-level kReduce) or by InstructionFusion (for - // a fused kReduce). - CHECK(input_shape.has_layout()) << "LayoutAssignment or InstructionFusion " - "doesn't set the input layout of " - << reduce->ToString(); - - // Specialize multi-dimensional-array-to-vector reduction. - std::vector input_dims_to_keep; - for (int64 input_dim = 0; input_dim < ShapeUtil::Rank(input_shape); - ++input_dim) { - if (std::find(dimensions_to_reduce.begin(), dimensions_to_reduce.end(), - input_dim) == dimensions_to_reduce.end()) { - input_dims_to_keep.push_back(input_dim); - } - } - - // Sort the dimensions to keep from minor to major, to facilitate checking - // whether another dimension is major or minor of them. - std::sort(input_dims_to_keep.begin(), input_dims_to_keep.end(), - [&input_shape](int64 dim_a, int64 dim_b) { - return PositionInContainer(LayoutUtil::MinorToMajor(input_shape), - dim_a) < - PositionInContainer(LayoutUtil::MinorToMajor(input_shape), - dim_b); - }); - // Now, if output rank is at least 1, `input_dims_to_keep.front()` is - // minormost and `input_dims_to_keep.back()` is majormost. - - // If the dimensions to keep are minormost, emit a column reduction. As all - // the dimensions to keep are contiguous, by prerequisite of - // `EmitReductionToVector`, we only need to check whether the minormost - // dimension of the input is to keep. - if (ShapeUtil::IsEffectiveScalar(reduce->shape())) { - return EmitReductionToScalar(kernel_thunk, reduce, input_shape, input_gens, - init_value_gens, reducers, - reduce_output_shapes, extra_output_gens); - } else if (input_dims_to_keep.front() == - LayoutUtil::Minor(input_shape.layout(), 0)) { - // Column reduction. Treat the result of "input" as a matrix whose width - // is the most minor dimension and height the product of other dimensions, - // and treat "reduce" as a column reduction of the input matrix. - const int64 width = ShapeUtil::ElementsIn(reduce->shape()); - // "width" can be zero, so don't do - // height = ShapeUtil::ElementsIn(input_shape) / width; - int64 height = 1; - for (int64 input_dim = 0; input_dim < ShapeUtil::Rank(input_shape); - ++input_dim) { - if (!std::count(input_dims_to_keep.begin(), input_dims_to_keep.end(), - input_dim)) { - height *= input_shape.dimensions(input_dim); - } - } - return EmitColumnReduction(kernel_thunk, height, width, reduce, input_shape, - input_gens, init_value_gens, reducers, - reduce_output_shapes, extra_output_gens); - } else { - // Reduce the row dimension of a matrix or reduce dimension 0 and 2 in a - // 3D tensor. The size of dimension 1 (the height) is the size of the - // dimension to keep, the size of dimension 0 (the depth) is the product - // of dimensions that are more major than the dimension to keep, and the - // size of dimension 2 (the width) is the product of more minor - // dimensions. - int64 depth = 1; - int64 width = 1; - for (int64 input_dim = 0; input_dim < ShapeUtil::Rank(input_shape); - ++input_dim) { - if (PositionInContainer(LayoutUtil::MinorToMajor(input_shape), - input_dim) > - PositionInContainer(LayoutUtil::MinorToMajor(input_shape), - input_dims_to_keep.back())) { - depth *= input_shape.dimensions(input_dim); - } else if (PositionInContainer(LayoutUtil::MinorToMajor(input_shape), - input_dim) < - PositionInContainer(LayoutUtil::MinorToMajor(input_shape), - input_dims_to_keep.front())) { - width *= input_shape.dimensions(input_dim); - } - } - const int64 height = ShapeUtil::ElementsIn(reduce->shape()); - return EmitRowReduction(kernel_thunk, depth, height, width, reduce, - input_shape, input_gens, init_value_gens, reducers, - reduce_output_shapes, extra_output_gens); - } -} - Status IrEmitterUnnested::HandleReduce(HloInstruction* reduce) { // TODO(b/112040122): Support multi-output reduce. if (!ShapeUtil::IsArray(reduce->shape())) { return Unimplemented("Multi-output reduce is not supported on GPU"); } - auto input = reduce->operand(0); - auto init_value = reduce->operand(1); - absl::Span dimensions_to_reduce(reduce->dimensions()); - HloComputation* reducer = reduce->to_apply(); - // HandleReduce specializes reduction from a multi-dimensional array to a 1D - // array. The specialized version requires an initializer thunk that - // initializes the output array to the initial value of the reduce. if (IsReductionToVector(*reduce)) { - TF_ASSIGN_OR_RETURN(std::unique_ptr initializer_thunk, - BuildInitializerThunk(reduce)); - std::vector> thunks; - thunks.push_back(std::move(initializer_thunk)); - std::unique_ptr kernel_thunk = - BuildKernelThunk(reduce, /*implements_whole_instruction=*/false); - - TF_CHECK_OK(EmitReductionToVector( - kernel_thunk.get(), reduce, input->shape(), - {[&](const IrArray::Index& index) { - return GetIrArray(*input, *reduce).EmitReadArrayElement(index, &b_); - }}, - {[&](const IrArray::Index& index) { - return GetIrArray(*init_value, *reduce) - .EmitReadArrayElement(index, &b_); - }}, - dimensions_to_reduce, {reducer}, {{}}, {})); - - thunks.push_back(std::move(kernel_thunk)); - - std::unique_ptr sequential_thunk = - absl::make_unique(std::move(thunks), reduce); - AddThunkToThunkSequence(std::move(sequential_thunk)); - return Status::OK(); + return EmitReductionToVector(reduce); } return IrEmitter::HandleReduce(reduce); @@ -1818,7 +763,7 @@ Status IrEmitterUnnested::HandleSelectAndScatter( // Create the inner loop to iterate over the window. llvm_ir::ForLoopNest window_loops(IrName(select_and_scatter, "inner"), &b_, index_type); - std::vector window_size; + DimensionVector window_size; for (const auto& dim : window.dimensions()) { window_size.push_back(dim.size()); CHECK_GT(dim.size(), 0); @@ -2171,7 +1116,18 @@ Status IrEmitterUnnested::HandleSelect(HloInstruction* select) { Status IrEmitterUnnested::HandleSort(HloInstruction* sort) { std::vector> thunks; Shape keys_shape = sort->operand(0)->shape(); + int64 dimension_to_sort = sort->dimensions(0); + // In case there is a 'values' parameter that is a iota, we take note and use + // it later to ensure a stable sort. Otherwise, we don't guarantee a stable + // sort. + int64 iota_values_parameter_index = -1; for (int64 i = 0; i < sort->operand_count(); ++i) { + if (i > 0 && sort->operand(i)->opcode() == HloOpcode::kIota && + ShapeUtil::ElementIsIntegral(sort->operand(i)->shape()) && + Cast(sort->operand(i))->iota_dimension() == + dimension_to_sort) { + iota_values_parameter_index = i; + } ShapeIndex shape_index = sort->operand_count() > 1 ? ShapeIndex({i}) : ShapeIndex({}); // We assume that the layout of all involved operands and outputs is the @@ -2196,7 +1152,6 @@ Status IrEmitterUnnested::HandleSort(HloInstruction* sort) { } } - int64 dimension_to_sort = sort->dimensions(0); uint64 dimension_to_sort_bound = keys_shape.dimensions(dimension_to_sort); int64 num_stages = tensorflow::Log2Ceiling(dimension_to_sort_bound); CHECK_GE(1ULL << num_stages, dimension_to_sort_bound); @@ -2298,8 +1253,9 @@ Status IrEmitterUnnested::HandleSort(HloInstruction* sort) { } } return llvm_ir::EmitSortInPlace( - dimension_to_sort, keys_array, values_arrays, IrName(sort), xor_masks, - &b_, launch_dimensions, + dimension_to_sort, keys_array, values_arrays, + iota_values_parameter_index, IrName(sort), xor_masks, &b_, + launch_dimensions, xor_masks.size() > 1 ? num_iterations_in_sort_dim : standard_num_iterations_in_sort_dim, kTileSize); @@ -2385,7 +1341,7 @@ Status IrEmitterUnnested::HandleCrossReplicaSum(HloInstruction* crs) { return Status::OK(); } -Status IrEmitterUnnested::HandleAfterAll(HloInstruction* gen_token) { +Status IrEmitterUnnested::HandleAfterAll(HloInstruction* after_all) { return Status::OK(); } @@ -3146,31 +2102,6 @@ std::vector IrEmitterUnnested::ConstructIrArrayForInputs( return param_arrays; } -int IrEmitterUnnested::ConstructOutputReducedShapeAndCastOutputIrArrayToShape( - const HloInstruction& hlo, const std::vector& output_arrays, - absl::Span reduced_output_dims, - std::vector* output_reduced_shapes, - std::vector* output_in_reduced_shape_arrays) { - int64 num_outputs = 1; - if (hlo.IsMultiOutputFusion()) { - num_outputs = ShapeUtil::TupleElementCount(hlo.shape()); - output_in_reduced_shape_arrays->reserve(num_outputs); - output_reduced_shapes->reserve(num_outputs); - for (int64 i = 0; i < num_outputs; ++i) { - output_reduced_shapes->push_back(ShapeUtil::MakeShapeWithDescendingLayout( - ShapeUtil::GetSubshape(hlo.shape(), {i}).element_type(), - reduced_output_dims)); - output_in_reduced_shape_arrays->push_back( - output_arrays[i].CastToShape((*output_reduced_shapes)[i], &b_)); - } - } else { - output_reduced_shapes->push_back(ShapeUtil::MakeShapeWithDescendingLayout( - hlo.shape().element_type(), reduced_output_dims)); - output_in_reduced_shape_arrays->push_back( - output_arrays[0].CastToShape((*output_reduced_shapes)[0], &b_)); - } - return num_outputs; -} int IrEmitterUnnested::ConstructInputReducedShapeAndCastInputIrArrayToShape( const HloInstruction& hlo, const std::vector& param_arrays, @@ -3230,304 +2161,894 @@ llvm::Value* GetBlockIdx(llvm::IRBuilder<>* builder, llvm::Type* index_ty, "block.id.x"); } -// Emits code to process up to (tile_size/num_rows) elements in a tile, given -// `emit_elem_function` is the function to emit code to process one element, `y` -// and `x` are the coordinates for the first element to process, and `index` is -// the index for the origin of the tile. Emits bounds check to ensure that each -// processed element is within the boundary defined by `tile_width` and -// `tile_height`. +void EmitFullTile(const KernelMappingScheme* mapping_scheme, + const IrArray::Index& tile_origin_index, + llvm::IRBuilder<>* builder, llvm::Value* y, llvm::Value* x, + llvm::Type* index_ty, + const std::function& emit_elem_function) { + int64 num_threads_x = mapping_scheme->GetNumberOfThreadsForDimensionX(); + int64 num_threads_y = mapping_scheme->GetNumberOfThreadsForDimensionY(); + int64 tile_size_x = mapping_scheme->GetTileSizeForDimensionX(); + int64 tile_size_y = mapping_scheme->GetTileSizeForDimensionY(); + for (int64 i = 0; i < tile_size_y; i += num_threads_y) { + IrArray::Index source_idx_y = + tile_origin_index.AddOffsetToDim(llvm::ConstantInt::get(index_ty, i), + KernelMappingScheme::DimY, builder); + llvm::Value* y_loc = + builder->CreateAdd(llvm::ConstantInt::get(index_ty, i), y); + for (int64 j = 0; j < tile_size_x; j += num_threads_x) { + IrArray::Index source_idx = + source_idx_y.AddOffsetToDim(llvm::ConstantInt::get(index_ty, j), + KernelMappingScheme::DimX, builder); + llvm::Value* x_loc = + builder->CreateAdd(llvm::ConstantInt::get(index_ty, j), x); + emit_elem_function(source_idx, y_loc, x_loc); + } + } +} + +void EmitPartialTile( + const KernelMappingScheme* mapping_scheme, + const IrArray::Index& tile_origin_index, const string& loop_name, + KernelSupportLibrary* ksl, llvm::IRBuilder<>* builder, llvm::Value* y, + llvm::Value* x, llvm::Value* tile_height, llvm::Value* tile_width, + llvm::Type* index_ty, + const std::function& emit_elem_function) { + int64 num_threads_x = mapping_scheme->GetNumberOfThreadsForDimensionX(); + int64 num_threads_y = mapping_scheme->GetNumberOfThreadsForDimensionY(); + int64 tile_size_x = mapping_scheme->GetTileSizeForDimensionX(); + + for (int64 j = 0; j < tile_size_x; j += num_threads_x) { + IrArray::Index source_idx = + tile_origin_index.AddOffsetToDim(llvm::ConstantInt::get(index_ty, j), + KernelMappingScheme::DimX, builder); + llvm::Value* x_loc = + builder->CreateAdd(llvm::ConstantInt::get(index_ty, j), x); + + ksl->IfReturnVoid( + loop_name + "_x_in_tile", builder->CreateICmpULT(x_loc, tile_width), + [&] { + // tile_height_bound = + // ceil(tile_height / num_threads_y) * num_threads_y + llvm::Value* ceiling_of_ratio = builder->CreateUDiv( + builder->CreateAdd(tile_height, llvm::ConstantInt::get( + index_ty, num_threads_y - 1)), + llvm::ConstantInt::get(index_ty, num_threads_y)); + llvm::Value* tile_height_bound = builder->CreateMul( + ceiling_of_ratio, + llvm::ConstantInt::get(index_ty, num_threads_y)); + ksl->ForReturnVoid( + loop_name, /*start=*/llvm::ConstantInt::get(index_ty, 0), + /*end=*/tile_height_bound, + /*step=*/llvm::ConstantInt::get(index_ty, num_threads_y), + [&](llvm::Value* y_indvar) { + llvm::Value* y_loc = builder->CreateAdd(y_indvar, y); + ksl->IfReturnVoid( + loop_name + "_y_in_tile", + builder->CreateICmpULT(y_loc, tile_height), [&] { + emit_elem_function( + source_idx.AddOffsetToDim( + y_indvar, KernelMappingScheme::DimY, builder), + y_loc, x_loc); + }); + }); + }); + } +} + +// Emits code to process up to +// (tile_size_x/num_threads_x * tile_size_y/num_threads_y) elements in a tile, +// given `emit_elem_function` is the function to emit code to process one +// element, `y` and `x` are the intra-tile coordinates for the first element +// to process, and `index` is the index for the origin of the tile. Information +// about tile_size_x/y and num_threads_x/y are stored in `mapping_scheme`. Emits +// bounds check to ensure that each processed element is within the boundary +// defined by `tile_width` and `tile_height`. void EmitTiledElementalCodeWithBoundsCheck( - int64 tile_size, int64 num_rows, const IrArray::Index& index, - const string& loop_name, KernelSupportLibrary* ksl, - llvm::IRBuilder<>* builder, llvm::Value* y, llvm::Value* x, - llvm::Value* tile_width, llvm::Value* tile_height, - const std::function& - emit_elem_function) { + const KernelMappingScheme* mapping_scheme, + const IrArray::Index& tile_origin_index, const string& loop_name, + KernelSupportLibrary* ksl, llvm::IRBuilder<>* builder, llvm::Value* y, + llvm::Value* x, llvm::Value* tile_height, llvm::Value* tile_width, + const std::function& emit_elem_function) { + int64 tile_size_x = mapping_scheme->GetTileSizeForDimensionX(); + int64 tile_size_y = mapping_scheme->GetTileSizeForDimensionY(); llvm::Type* index_ty = tile_width->getType(); - // Emits a constant value with index type. + + ksl->IfReturnVoid( + loop_name + "_full_tile", + builder->CreateAnd( + builder->CreateICmpEQ(llvm::ConstantInt::get(index_ty, tile_size_x), + tile_width), + builder->CreateICmpEQ(llvm::ConstantInt::get(index_ty, tile_size_y), + tile_height)), + [&] { + EmitFullTile(mapping_scheme, tile_origin_index, builder, y, x, index_ty, + emit_elem_function); + }, + [&] { + EmitPartialTile(mapping_scheme, tile_origin_index, loop_name, ksl, + builder, y, x, tile_height, tile_width, index_ty, + emit_elem_function); + }); +} +} // namespace + +// Emits code to process a tensor element in a tile for the given kCopy HLO that +// performs a 0-2-1 transpose. +// +// index: The index for the first output element in the normalized tensor. The +// normalized tensor is the resulting tensor after collapsing contiguous +// dimensions that play the same role in the transpose. +// y_loc: The y coordinate within a tile. +// x_loc: The x coordinate within a tile. +// kernel_info: Other information to support the kernel code generation. +void IrEmitterUnnested::EmitTileElementForCopy( + HloInstruction* hlo, const llvm_ir::IrArray::Index& index, + const KernelCodegenInfo* kernel_info, llvm::Value* y_loc, + llvm::Value* x_loc) { + llvm_ir::TiledParameterInfo* tiled_param_info = + kernel_info->GetTiledParameterInfo(); + // TODO(jlebar): Add AA metadata to this load. + llvm::Instruction* load_from_shmem_buffer = + Load(GEP(tiled_param_info->GetBufferForParameter(0), + {b_.getInt64(0), x_loc, y_loc}), + "output_element"); + llvm_ir::IrArray output_array = GetIrArray(*hlo, *hlo); + Shape output_reduced_shape = ShapeUtil::MakeShapeWithDescendingLayout( + hlo->shape().element_type(), + kernel_info->GetKernelMappingScheme()->GetDimensionsInElements()); + // When the output_reduced_shape is a 0-2-1 transpose of the input shape, + // the 0-2-1 transpose is achieved through EmitWriteArrayElement. + output_array.CastToShape(output_reduced_shape, &b_) + .EmitWriteArrayElement(index, load_from_shmem_buffer, &b_); +} + +// Emits code to process a tensor element in a tile for the given kLoop fusion +// HLO containing parameters that are 0-2-1 transpose of its outputs. +// +// index: The index for the first output element in the normalized tensor, that +// is the resulting tensor after collapsing contiguous dimensions that play +// the same role in the transpose. +// kernel_info: Other information to support the kernel code generation. +// y_loc: The y coordinate within a tile. +// x_loc: The x coordinate within a tile. +void IrEmitterUnnested::EmitTileElementForFusion( + HloInstruction* hlo, const llvm_ir::IrArray::Index& index, + const KernelCodegenInfo* kernel_info, llvm::Value* y_loc, + llvm::Value* x_loc) { + llvm_ir::TiledParameterInfo* tiled_param_info = + kernel_info->GetTiledParameterInfo(); + std::vector output_arrays = ConstructIrArrayForOutputs(*hlo); + GpuElementalIrEmitter elem_emitter(hlo_module_config_, module_, &b_, + GetNestedComputer()); + FusedIrEmitter fused_emitter(GetGeneratorForOperandIrArrays(hlo), + &elem_emitter); + tiled_param_info->set_y(y_loc); + tiled_param_info->set_x(x_loc); + fused_emitter.SetTiledParameterInfo(tiled_param_info); + TF_CHECK_OK(hlo->fused_expression_root()->Accept(&fused_emitter)); + IrArray::Index untiled_index = + kernel_info->GetKernelMappingScheme()->GetUnnormalizedIndex( + index, output_arrays[0].GetShape()); + const llvm_ir::ElementGenerator& output_generator = + fused_emitter.GetRootGenerator(); + llvm::Value* output_value = output_generator(untiled_index).ValueOrDie(); + if (hlo->IsMultiOutputFusion()) { + DCHECK(output_value->getType()->isStructTy()); + DCHECK_EQ(output_value->getType()->getStructNumElements(), + output_arrays.size()); + for (int64 i = 0; i < output_arrays.size(); ++i) { + output_arrays[i].EmitWriteArrayElement( + untiled_index, ExtractValue(output_value, i), &b_); + } + } else { + output_arrays[0].EmitWriteArrayElement(untiled_index, output_value, &b_); + } +} + +// Information to support the code generation for a tiled reduction kernel. +using AddressVector = InlinedVector; +class ReductionCodegenInfo : public IrEmitterUnnested::KernelCodegenInfo { + public: + explicit ReductionCodegenInfo(llvm_ir::KernelMappingScheme* mapping_scheme, + bool is_row_reduction) + : KernelCodegenInfo(mapping_scheme), + current_output_linear_index_address_(nullptr), + current_output_inbound_address_(nullptr), + is_row_reduction_(is_row_reduction) {} + + void SetCurrentOutputLinearIndexAddress(llvm::AllocaInst* a) { + current_output_linear_index_address_ = a; + } + // Returns the address of the memory that stores the linear index of the + // current output. Since we are processing reduction to contiguous physical + // dimensions, this linear index is the linear index of the 1D output array. + llvm::AllocaInst* GetCurrentOutputLinearIndexAddress() const { + return current_output_linear_index_address_; + } + + void SetCurrentOutputInboundAddress(llvm::AllocaInst* a) { + current_output_inbound_address_ = a; + } + + llvm::AllocaInst* GetCurrentOutputInboundAddress() const { + return current_output_inbound_address_; + } + + AddressVector* GetMutablePartialResultAddresses() { + return &partial_result_addresses_; + } + const AddressVector& GetPartialResultAddresses() const { + return partial_result_addresses_; + } + + AddressVector* GetMutableReductionInputAddresses() { + return &reduction_input_addresses_; + } + const AddressVector& GetReductionInputAddresses() const { + return reduction_input_addresses_; + } + + InlinedVector* GetMutableReducers() { return &reducers_; } + const InlinedVector& GetReducers() const { + return reducers_; + } + int GetNumberOfReduces() const { return reducers_.size(); } + + InlinedVector* GetMutableReductionOutputShapeIndices() { + return &reduction_output_shape_indices_; + } + const InlinedVector& GetReductionOutputShapeIndices() const { + return reduction_output_shape_indices_; + } + + bool IsRowReduction() const { return is_row_reduction_; } + + // Return the dimension that is being reduced between DimX and DimY. + int GetReducedDimensionEnum() const { + return IsRowReduction() ? llvm_ir::KernelMappingScheme::DimX + : llvm_ir::KernelMappingScheme::DimY; + } + + // Return the dimension that is being ketp between DimX and DimY. + int GetKeptDimensionEnum() const { + return IsRowReduction() ? llvm_ir::KernelMappingScheme::DimY + : llvm_ir::KernelMappingScheme::DimX; + } + + private: + AddressVector partial_result_addresses_; + AddressVector reduction_input_addresses_; + InlinedVector reducers_; + InlinedVector reduction_output_shape_indices_; + llvm::AllocaInst* current_output_linear_index_address_; + llvm::AllocaInst* current_output_inbound_address_; + bool is_row_reduction_; +}; + +namespace { +// Returns a group of instructions that generate the output for the kernel +// containing the given HLO instruction. The result may be an unnested kReduce +// HLO, a nested kReduce HLO of a kInput fusion, or the operands of the tuple +// for a multiple output fusion. +absl::Span GetOutputInstructions( + HloInstruction* const* reduce_or_tuple_pointer) { + HloOpcode opcode = (*reduce_or_tuple_pointer)->opcode(); + CHECK(opcode == HloOpcode::kReduce || opcode == HloOpcode::kTuple); + return opcode == HloOpcode::kTuple + ? (*reduce_or_tuple_pointer)->operands() + : absl::Span(reduce_or_tuple_pointer, 1); +} + +const HloInstruction* GetFirstReduceInstruction( + absl::Span instructions) { + auto first_reduce_iter = + absl::c_find_if(instructions, [](const HloInstruction* inst) { + return inst->opcode() == HloOpcode::kReduce; + }); + CHECK_NE(first_reduce_iter, instructions.end()); + return *first_reduce_iter; +} + +}; // namespace + +void IrEmitterUnnested::EmitPrologueForOneReduction( + HloInstruction* unnested_hlo, HloInstruction* reduce_inst, int reduce_idx, + KernelCodegenInfo* kernel_info, GpuElementalIrEmitter* elemental_emitter, + ShapeIndex output_shape_index) { + ReductionCodegenInfo* reduction_info = + static_cast(kernel_info); + + InlinedVector* reducers = + reduction_info->GetMutableReducers(); + CHECK(IsReductionToVector(*reduce_inst)); + reducers->push_back(reduce_inst->to_apply()); + + InlinedVector* reduction_output_shape_indices = + reduction_info->GetMutableReductionOutputShapeIndices(); + reduction_output_shape_indices->push_back(std::move(output_shape_index)); + + AddressVector* reduction_input_addresses = + reduction_info->GetMutableReductionInputAddresses(); + llvm::Type* element_type = llvm_ir::PrimitiveTypeToIrType( + reduce_inst->shape().element_type(), ir_emitter_context_->llvm_module()); + llvm::AllocaInst* reduction_input_address = Alloca(element_type); + reduction_input_addresses->push_back(reduction_input_address); + + AddressVector* partial_result_addresses = + reduction_info->GetMutablePartialResultAddresses(); + llvm::AllocaInst* partial_result_address = + Alloca(element_type, /*ArraySize=*/nullptr, + "partial_reduction_result." + llvm::Twine(reduce_idx)); + partial_result_addresses->push_back(partial_result_address); + + // Initialize the partial result with the initial value of the reduction. + llvm::Value* init_ir_value; + if (unnested_hlo->opcode() == HloOpcode::kFusion) { + HloInstruction* init_value_operand = reduce_inst->mutable_operand(1); + FusedIrEmitter fused_emitter(GetGeneratorForOperandIrArrays(unnested_hlo), + elemental_emitter); + + TF_CHECK_OK(init_value_operand->Accept(&fused_emitter)); + init_ir_value = + fused_emitter + .GetGenerator(init_value_operand)(IrArray::Index(b_.getInt32Ty())) + .ValueOrDie(); + } else { + const HloInstruction* init_value = unnested_hlo->operand(1); + init_ir_value = + GetIrArray(*init_value, *unnested_hlo) + .EmitReadArrayElement(IrArray::Index(b_.getInt32Ty()), &b_); + } + + Store(init_ir_value, partial_result_address); +} + +void IrEmitterUnnested::EmitPrologueForReduction( + HloInstruction* unnested_hlo, KernelCodegenInfo* kernel_info) { + VLOG(10) << "Emit prologue for reduction " << unnested_hlo->ToString(); + // Find the unnested kReduce or the tuple that contains a list of kReduce. + HloInstruction* reduce_or_tuple = unnested_hlo->opcode() == HloOpcode::kFusion + ? unnested_hlo->fused_expression_root() + : unnested_hlo; + absl::Span output_instructions = + GetOutputInstructions(&reduce_or_tuple); + ReductionCodegenInfo* reduction_info = + static_cast(kernel_info); + GpuElementalIrEmitter elemental_emitter(hlo_module_config_, + ir_emitter_context_->llvm_module(), + &b_, GetNestedComputer()); + const HloInstruction* first_reduce = nullptr; + for (int i = 0, e = output_instructions.size(); i != e; ++i) { + if (output_instructions[i]->opcode() != HloOpcode::kReduce) { + continue; + } + HloInstruction* reduce_inst = output_instructions[i]; + if (first_reduce == nullptr) { + first_reduce = reduce_inst; + } else { + CHECK(first_reduce->dimensions() == reduce_inst->dimensions()); + } + ShapeIndex output_shape_index; + if (reduce_or_tuple->opcode() == HloOpcode::kTuple) { + output_shape_index = {i}; + } + + EmitPrologueForOneReduction(unnested_hlo, reduce_inst, i, kernel_info, + &elemental_emitter, + std::move(output_shape_index)); + } + + // Allocate stack storage to store the current output linear index and record + // the address of the storage. + reduction_info->SetCurrentOutputLinearIndexAddress( + Alloca(reduction_info->GetIndexType())); + + if (!reduction_info->IsRowReduction()) { + llvm::Type* bool_ty = b_.getInt1Ty(); + llvm::AllocaInst* output_inbound_addr = Alloca(bool_ty); + Store(llvm::ConstantInt::get(bool_ty, 0), output_inbound_addr); + reduction_info->SetCurrentOutputInboundAddress(output_inbound_addr); + } +} + +void IrEmitterUnnested::EmitFullWarpShuffleDownLoopForAllReduces( + const InlinedVector& reducers, + const AddressVector& partial_result_addresses) { + for (int distance = 16; distance >= 1; distance /= 2) { + for (int i = 0; i != reducers.size(); ++i) { + llvm::Type* element_type = + partial_result_addresses[i]->getType()->getElementType(); + int bit_width = llvm_ir::GetSizeInBits(element_type); + llvm::Value* result_from_other_lane = Alloca( + element_type, nullptr, "result_from_other_lane" + llvm::Twine(i)); + // Bitcast cannot be applied to aggregate types (even packed ones), so + // we bitcast addresses of load/store to intN* of the same bit-width. + llvm::Type* shuffled_value_type = + element_type->isStructTy() ? b_.getIntNTy(bit_width) : element_type; + auto convert_pointer_for_shuffle = [&](llvm::Value* ptr) { + return BitCast(ptr, shuffled_value_type->getPointerTo()); + }; + llvm::Value* partial_result = + Load(convert_pointer_for_shuffle(partial_result_addresses[i]), + "partial_reduction_result"); + Store(EmitFullWarpShuffleDown(partial_result, b_.getInt32(distance), &b_), + convert_pointer_for_shuffle(result_from_other_lane)); + TF_CHECK_OK(EmitCallToNestedComputation( + *reducers[i], {partial_result_addresses[i], result_from_other_lane}, + partial_result_addresses[i])); + } + } +} + +void IrEmitterUnnested::EmitEpilogueForReduction( + HloInstruction* unnested_hlo, KernelCodegenInfo* kernel_info) { + ReductionCodegenInfo* reduction_info = + static_cast(kernel_info); + int num_reduces = reduction_info->GetNumberOfReduces(); + const AddressVector& partial_result_addresses = + reduction_info->GetPartialResultAddresses(); + const InlinedVector& reducers = + reduction_info->GetReducers(); + const InlinedVector& reduction_output_shape_indices = + reduction_info->GetReductionOutputShapeIndices(); + + if (reduction_info->IsRowReduction()) { + EmitFullWarpShuffleDownLoopForAllReduces(reducers, + partial_result_addresses); + llvm::Value* lane_id = reduction_info->GetLaneId(); + llvm_ir::LlvmIfData if_lane_id_is_zero_data = llvm_ir::EmitIfThenElse( + ICmpEQ(lane_id, llvm::ConstantInt::get(lane_id->getType(), 0)), + "lane_id_is_zero", &b_); + llvm_ir::SetToFirstInsertPoint(if_lane_id_is_zero_data.true_block, &b_); + } else { + llvm::Value* output_inbound_addr = + reduction_info->GetCurrentOutputInboundAddress(); + llvm::Value* output_inbound = Load(output_inbound_addr); + llvm_ir::LlvmIfData if_output_inbound_data = llvm_ir::EmitIfThenElse( + ICmpEQ(output_inbound, + llvm::ConstantInt::get(output_inbound->getType(), 1)), + "output_inbound", &b_); + llvm_ir::SetToFirstInsertPoint(if_output_inbound_data.true_block, &b_); + } + + // Emit an atomic operation that accumulates the partial reduction to the + // output element. For row reduction, this is only for lane 0 due to the + // if-statement emitted above. + for (int i = 0; i != num_reduces; ++i) { + IrArray::Index element_index( + /*linear=*/Load(reduction_info->GetCurrentOutputLinearIndexAddress(), + "output_linear_addr"), + ShapeUtil::GetSubshape(unnested_hlo->shape(), + reduction_output_shape_indices[i]), + &b_); + llvm::Value* output_address = + GetIrArray(*unnested_hlo, *unnested_hlo, + reduction_output_shape_indices[i]) + .EmitArrayElementAddress(element_index, &b_, + "output_element_address"); + // Do not emit atomic operations if each element in the reduction result is + // computed by one block, that is the dimension being reduced has only one + // block. + const llvm_ir::KernelMappingScheme* mapping_scheme = + reduction_info->GetKernelMappingScheme(); + if (mapping_scheme->GetTileBlockSizeForDimension( + llvm_ir::KernelMappingScheme::DimZ) == 1 && + mapping_scheme->GetTileBlockSizeForDimension( + reduction_info->GetReducedDimensionEnum()) == 1) { + TF_CHECK_OK(EmitCallToNestedComputation( + *reducers[i], {output_address, partial_result_addresses[i]}, + output_address)); + } else { + TF_CHECK_OK(EmitAtomicOperationForNestedComputation( + *reducers[i], output_address, partial_result_addresses[i])); + } + } +} + +void IrEmitterUnnested::EmitTileElementForReduction( + HloInstruction* unnested_hlo, const llvm_ir::IrArray::Index& index, + const KernelCodegenInfo* kernel_info, llvm::Value* y_loc, + llvm::Value* x_loc) { + VLOG(10) << "Emit tile element for reduce " << unnested_hlo->ToString(); + HloInstruction* reduce_or_tuple = unnested_hlo->opcode() == HloOpcode::kFusion + ? unnested_hlo->fused_expression_root() + : unnested_hlo; + llvm_ir::TiledParameterInfo* tiled_param_info = + kernel_info->GetTiledParameterInfo(); + tiled_param_info->set_y(y_loc); + tiled_param_info->set_x(x_loc); + + // Record the linear address for the current reduction. + const ReductionCodegenInfo* reduction_info = + dynamic_cast(kernel_info); + Store(index[reduction_info->GetKeptDimensionEnum()], + reduction_info->GetCurrentOutputLinearIndexAddress()); + if (!reduction_info->IsRowReduction()) { + llvm::Type* bool_ty = b_.getInt1Ty(); + llvm::AllocaInst* output_inbound_addr = + reduction_info->GetCurrentOutputInboundAddress(); + Store(llvm::ConstantInt::get(bool_ty, 1), output_inbound_addr); + } + + InlinedVector input_gens; + std::vector> + extra_output_gens; + GpuElementalIrEmitter elem_emitter(hlo_module_config_, module_, &b_, + GetNestedComputer()); + FusedIrEmitter fused_emitter(GetGeneratorForOperandIrArrays(unnested_hlo), + &elem_emitter); + absl::Span output_instructions = + GetOutputInstructions(&reduce_or_tuple); + // Construct the ElementGenerator for each reduction and extra output in the + // the group of output instructions. + if (unnested_hlo->opcode() == HloOpcode::kFusion) { + fused_emitter.SetTiledParameterInfo(tiled_param_info); + TF_CHECK_OK(unnested_hlo->fused_expression_root()->Accept(&fused_emitter)); + + for (int i = 0, e = output_instructions.size(); i != e; ++i) { + const HloInstruction* inst = output_instructions[i]; + ShapeIndex output_shape_index; + if (reduce_or_tuple->opcode() == HloOpcode::kTuple) { + output_shape_index = {i}; + } + if (inst->opcode() == HloOpcode::kReduce) { + input_gens.push_back(fused_emitter.GetGenerator(inst->operand(0))); + } else { + extra_output_gens.emplace_back(fused_emitter.GetGenerator(inst), + std::move(output_shape_index)); + } + } + } else { + input_gens.push_back([&](const IrArray::Index& index) { + return GetIrArray(*unnested_hlo->operand(0), *unnested_hlo) + .EmitReadArrayElement(index, &b_); + }); + } + + IrArray::Index input_index = + reduction_info->GetKernelMappingScheme()->GetUnnormalizedIndex( + index, + GetFirstReduceInstruction(output_instructions)->operand(0)->shape()); + const AddressVector& partial_reduction_result_addresses = + reduction_info->GetPartialResultAddresses(); + const AddressVector& reduction_input_addresses = + reduction_info->GetReductionInputAddresses(); + const InlinedVector& reducers = + reduction_info->GetReducers(); + + // Emit code to generate the input and perform the reduction computation for + // each reduction instruction. + for (int i = 0; i != reducers.size(); ++i) { + llvm::Value* const input_ir_value = input_gens[i](input_index).ValueOrDie(); + Store(input_ir_value, reduction_input_addresses[i]); + TF_CHECK_OK(EmitCallToNestedComputation( + *reducers[i], + {partial_reduction_result_addresses[i], reduction_input_addresses[i]}, + partial_reduction_result_addresses[i])); + } + + // Emit code to generate the output for the non-reduction instructions in the + // fusion, if any. + TF_CHECK_OK( + EmitExtraOutputsForReduce(unnested_hlo, input_index, extra_output_gens)); +} + +// Emits a kernel for the hlo instruction using the given tiling scheme. +void IrEmitterUnnested::EmitBlock(const TileGenerator& emit_one_tile, + const KernelCodegenInfo* kernel_info, + KernelSupportLibrary& ksl, + llvm::Type* index_ty) { + KernelMappingScheme* mapping_scheme = kernel_info->GetKernelMappingScheme(); + absl::Span dims_in_tile = mapping_scheme->GetDimensionsInTiles(); + absl::Span dims_in_block = + mapping_scheme->GetDimensionsInBlocks(); + absl::Span block_sizes = mapping_scheme->GetBlockSizes(); auto index_typed_constant = [&](uint64 c) -> llvm::Constant* { return llvm::ConstantInt::get(index_ty, c); }; - // Adds `addend` to the given `dim` of `index`. - auto offset_dim = [&](IrArray::Index index, llvm::Value* addend, int64 dim) { - index[dim] = builder->CreateAdd(index[dim], addend); - return index; - }; - auto emit_full_tile = [&] { - for (int64 i = 0; i < tile_size; i += num_rows) { - auto source_idx = offset_dim(index, index_typed_constant(i), /*dim=*/1); - auto y_loc = builder->CreateAdd(index_typed_constant(i), y); - emit_elem_function(source_idx, y_loc); + // Emit all the tiles for a given dimension in a tile block. + auto emit_tiles_for_block_dim = + [&](const string& loop_name, const IrArray::Index& starting_tile, + int dim_id, + const std::function + emit_next_block_dim) { + if (block_sizes[dim_id] == 1) { + emit_next_block_dim(starting_tile); + } else { + llvm::Value* starting_tile_index_for_dim = starting_tile[dim_id]; + llvm::Value* block_size_for_dim = + index_typed_constant(block_sizes[dim_id]); + llvm::Value* block_id_for_dim = + b_.CreateUDiv(starting_tile_index_for_dim, block_size_for_dim); + llvm::Value* last_block_for_dim = + index_typed_constant(dims_in_block[dim_id] - 1); + llvm::Value* last_block_size_for_dim = index_typed_constant( + dims_in_tile[dim_id] - + (dims_in_block[dim_id] - 1) * block_sizes[dim_id]); + llvm::Value* num_tiles_in_block = + Select(ICmpEQ(last_block_for_dim, block_id_for_dim), + last_block_size_for_dim, block_size_for_dim); + + ksl.ForReturnVoid( + loop_name, + /*start=*/index_typed_constant(0), + /*end=*/num_tiles_in_block, + /*step=*/1, [&](llvm::Value* block_dim_induction_var) { + IrArray::Index tile_index = starting_tile.AddOffsetToDim( + block_dim_induction_var, dim_id, &b_); + emit_next_block_dim(tile_index); + }); + } + }; + + absl::Span reduced_dims = + mapping_scheme->GetDimensionsInElements(); + const bool block_contains_multi_tiles = + mapping_scheme->GetNumberOfTilesInOneBlock() > 1; + + // Emit the tile with a given tile_index, by calculating the tight bounds for + // each dimension of the tile and then calling emit_one_tile. + auto emit_one_tile_for_tile_index = [&](const IrArray::Index& tile_index) { + std::vector output_tile_bounds(3); + for (int i = KernelMappingScheme::DimY; i < KernelMappingScheme::DimTot; + ++i) { + int64 tile_size_for_dim = mapping_scheme->GetTileSizeForDimension(i); + // Only last row or column may not have full size. + llvm::Value* is_last_row = + ICmpEQ(tile_index[i], index_typed_constant(dims_in_tile[i] - 1)); + int64 partial_row_size = + reduced_dims[i] - (dims_in_tile[i] - 1) * tile_size_for_dim; + output_tile_bounds[i] = + Select(is_last_row, index_typed_constant(partial_row_size), + index_typed_constant(tile_size_for_dim), "tile_bound"); } - }; - auto emit_last_row = [&] { - ksl->IfReturnVoid("x_in_tile", builder->CreateICmpULT(x, tile_width), [&] { - // tile_height_upper_bound = - // ceil(tile_height / num_rows) * num_rows - auto tile_height_upper_bound = builder->CreateMul( - builder->CreateUDiv( - builder->CreateAdd(tile_height, - index_typed_constant(num_rows - 1)), - index_typed_constant(num_rows)), - index_typed_constant(num_rows)); - ksl->ForReturnVoid( - loop_name, /*start=*/index_typed_constant(0), - /*end=*/tile_height_upper_bound, - /*step=*/index_typed_constant(num_rows), [&](llvm::Value* y_indvar) { - auto y_loc = builder->CreateAdd(y_indvar, y); - ksl->IfReturnVoid( - "y_in_tile", builder->CreateICmpULT(y_loc, tile_height), [&] { - emit_elem_function(offset_dim(index, y_indvar, /*dim=*/1), - y_loc); - }); - }); - }); + IrArray::Index tile_origin = + mapping_scheme->GetElementIndexForTileOrigin(tile_index); + emit_one_tile(tile_origin, output_tile_bounds, block_contains_multi_tiles); }; - ksl->IfReturnVoid( - "full_tile", - builder->CreateAnd( - builder->CreateICmpEQ(index_typed_constant(tile_size), tile_width), - builder->CreateICmpEQ(index_typed_constant(tile_size), tile_height)), - emit_full_tile, emit_last_row); + + const IrArray::Index starting_block = + mapping_scheme->EmitBlockIndex(index_ty); + const IrArray::Index starting_tile_for_dim_z = + mapping_scheme->GetTileIndexForBlockOrigin(starting_block); + + // Emit the three dimensional block of tiles. + emit_tiles_for_block_dim( + "block_dim_z", starting_tile_for_dim_z, KernelMappingScheme::DimZ, + [&](const IrArray::Index& starting_tile_for_dim_y) { + emit_tiles_for_block_dim( + "block_dim_y", starting_tile_for_dim_y, KernelMappingScheme::DimY, + [&](const IrArray::Index& starting_tile_for_dim_x) { + emit_tiles_for_block_dim("block_dim_x", starting_tile_for_dim_x, + KernelMappingScheme::DimX, + emit_one_tile_for_tile_index); + }); + }); } -} // namespace -// Emits a kernel for the given hlo instruction using a tiled 0-2-1 transpose -// algorithm to improve the memory access patterns for the input parameters -// which have a shape that is a 0-2-1 transpose of the output tensors. -// -// For the purpose of tiling, the output tensors have a logical shape of three -// components 0-2-1 while the relevant input parameters have a logical shape of -// three components 0-1-2 in the order major to minor. The x- and y- dimensions -// of the tensors are tiled in square tiles of edge length `kTileSize`. Each -// thread block of `kTileSize` x `kNumRows` threads transposes one tile: each -// thread copies kTileSize/kNumRows elements from the input to a shared memory -// tile, then the otherwise "regular hlo kernel" reads from the shared memory -// instead of the original input. -// -// This is similar to the following CUDA algorithm in TensorFlow: -// https://goo.gl/MStRV6. -// -// `kTileSize` should usually be same as warp size. We currently choose 32 for -// `kTileSize` and 4 for `kNumRows`. The CUDA algorithm uses 8 for `kNumRows`. +// Emits a kernel for the hlo instruction using the given kernel mapping scheme. // -// TODO(b/33320379): Here each block transposes 1 tile. It may be more efficient -// to launch fewer blocks so each transposes many tiles. -LaunchDimensions IrEmitterUnnested::EmitHlo021Tile( - HloInstruction* hlo, absl::Span reduced_output_dims, - absl::Span tiled_param_ids) { - // Parameters for the tiling algorithm. - constexpr int64 kTileSize = 32; - constexpr int64 kNumRows = 4; - constexpr int64 kThreadsPerTile = kTileSize * kNumRows; - - // Construct IrArrays for the inputs and outputs. - std::vector output_arrays = ConstructIrArrayForOutputs(*hlo); - int64 num_outputs = output_arrays.size(); - std::vector param_arrays = ConstructIrArrayForInputs(*hlo); +// unnested_hlo: The unnested hlo instruction for which the kernel is generated. +// Currently, these hlo instructions are supported: kLoop fusion, kCopy. +// tiled_param_ids: The IDs for the parameters that are 0-2-1 transpose of +// other tensors with the same dimensions and need to be tiled and tranposed. +// mapping_scheme: The tiling scheme to use. +// kernel_generator: Contains function objects for code generation, such as +// element generator, block prologue and epilogue generators. +// kernel_info: Represent other information to support the code generation +// of the tiled kernel for the hlo. +LaunchDimensions IrEmitterUnnested::EmitKernel( + HloInstruction* unnested_hlo, absl::Span tiled_param_ids, + const KernelCodeGenerator& kernel_generator, + KernelCodegenInfo* kernel_info) { + KernelMappingScheme* mapping_scheme = kernel_info->GetKernelMappingScheme(); + + std::vector param_arrays = ConstructIrArrayForInputs(*unnested_hlo); int64 num_params = param_arrays.size(); - // Allocate shared memory buffers to store the tiled inputs. std::vector param_shmem_buffers(num_params, nullptr); for (int64 id : tiled_param_ids) { - const HloInstruction* param = hlo->operand(id); - // Add 1 to the minor dimension to reduce shared memory bank conflicts. - llvm::Type* tile_type = llvm::ArrayType::get( - llvm::ArrayType::get(llvm_ir::PrimitiveTypeToIrType( - param->shape().element_type(), module_), - kTileSize + 1), - kTileSize); - auto* tile_base_ptr = llvm_ir::AllocateSharedMemoryTile( - b_.GetInsertBlock()->getParent()->getParent(), tile_type, - IrName(hlo, StrCat("tile", id))); - param_shmem_buffers[id] = tile_base_ptr; + const HloInstruction* param = unnested_hlo->operand(id); + param_shmem_buffers[id] = + mapping_scheme->GetSharedMemoryBufferForElementType( + llvm_ir::PrimitiveTypeToIrType(param->shape().element_type(), + module_), + IrName(unnested_hlo, StrCat("tile", id))); VLOG(3) << "Added shmem buffer for parameter " << id << ": " - << llvm_ir::DumpToString(*tile_base_ptr); - } - - // The 0-2-1 shape of the tiling scheme is the reduced shape of the HLO result - // for the purpose of tiling. Calculate the logical output dimensions in the - // tile from the reduced output dimensions. - std::vector output_dims_in_tiles = std::vector( - reduced_output_dims.begin(), reduced_output_dims.end()); - CHECK_EQ(output_dims_in_tiles.size(), 3); - for (int i = 1; i < 3; ++i) { - output_dims_in_tiles[i] = - CeilOfRatio(output_dims_in_tiles[i], kTileSize); + << llvm_ir::DumpToString(*param_shmem_buffers[id]); } - const int64 num_tiles = - absl::c_accumulate(output_dims_in_tiles, 1, std::multiplies()); - LaunchDimensions launch_dimensions(num_tiles, kThreadsPerTile); - llvm::Type* index_ty = - GetIndexTypeForKernel(hlo, launch_dimensions.launch_bound(), &b_); + LaunchDimensions launch_dimensions = LaunchDimensions( + mapping_scheme->GetNumberOfBlocks(), mapping_scheme->GetThreadsPerTile()); + llvm::Type* index_ty = GetIndexTypeForKernel( + unnested_hlo, launch_dimensions.launch_bound(), &b_); auto index_typed_constant = [&](uint64 c) -> llvm::Constant* { return llvm::ConstantInt::get(index_ty, c); }; - // Cast each output IrArray to its corresponding reduced shape and keep the - // reduced shape live during IR emission. - std::vector output_in_reduced_shape_arrays; - std::vector output_reduced_shapes; - CHECK_EQ(ConstructOutputReducedShapeAndCastOutputIrArrayToShape( - *hlo, output_arrays, reduced_output_dims, &output_reduced_shapes, - &output_in_reduced_shape_arrays), - num_outputs); - // For each tiled parameter, cast its input IrArray to the corresponding // reduced shape and keep the reduced shape live during IR emission. std::vector param_in_reduced_shape_arrays; std::vector param_reduced_shapes; - CHECK_EQ(ConstructInputReducedShapeAndCastInputIrArrayToShape( - *hlo, param_arrays, param_shmem_buffers, reduced_output_dims, - ¶m_reduced_shapes, ¶m_in_reduced_shape_arrays), - num_params); + absl::Span reduced_dims = + mapping_scheme->GetDimensionsInElements(); + int num_shapes = ConstructInputReducedShapeAndCastInputIrArrayToShape( + *unnested_hlo, param_arrays, param_shmem_buffers, reduced_dims, + ¶m_reduced_shapes, ¶m_in_reduced_shape_arrays); + DCHECK_EQ(num_shapes, num_params); // Calculate the starting element coordinate within a tile for the current // thread, (y, x) from thread_id. llvm::Value* x; llvm::Value* y; - std::tie(y, x) = CalculateYXCoordinateWithinTile( - &b_, index_typed_constant(kTileSize), kThreadsPerTile); - - // Calculate the index for the current output tile from block_id. - const IrArray::Index output_tile_index( - GetBlockIdx(&b_, index_ty, num_tiles), - ShapeUtil::MakeShapeWithDescendingLayout(PRED /*arbitrary*/, - output_dims_in_tiles), - &b_); - - // Output tile origin is the index for the first element of the current output - // tile. - const IrArray::Index output_tile_origin = [&] { - IrArray::Index index = output_tile_index; - for (int i = 1; i < 3; ++i) { - index[i] = Mul(output_tile_index[i], index_typed_constant(kTileSize), - "tile_origin." + std::to_string(i)); - } - return index; - }(); - - // Calculate the input tile origin from the output tile origin. - const IrArray::Index input_tile_origin( - Permute({0, 2, 1}, output_tile_origin.multidim())); + std::tie(y, x) = mapping_scheme->EmitThreadYXCoordinate(index_ty); - // Calculate the current output tile bounds in each of the logical dimensions. - std::vector output_tile_bounds(3); - for (int i = 1; i < 3; ++i) { - // Only last row or column may not have full size. - output_tile_bounds[i] = - Select(ICmpEQ(output_tile_index[i], - index_typed_constant(output_dims_in_tiles[i] - 1)), - index_typed_constant(reduced_output_dims[i] - - (output_dims_in_tiles[i] - 1) * kTileSize), - index_typed_constant(kTileSize), "kTileSize"); - } + kernel_info->SetLaneId( + mapping_scheme->GetNumberOfThreadsForDimensionX() == kWarpSize ? x + : nullptr); + kernel_info->SetIndexType(index_ty); KernelSupportLibrary ksl(&b_, llvm_ir::UnrollMode::kDefaultUnroll); - // Curry a few parameters to EmitTiledElementalCodeWithBoundsCheck. auto emit_tiled_elemental_code_with_bounds_check = [&](const IrArray::Index& index, const string& loop_name, - llvm::Value* tile_width, llvm::Value* tile_height, - const std::function& - emit_elem_function) { - EmitTiledElementalCodeWithBoundsCheck( - kTileSize, kNumRows, index, loop_name, &ksl, &b_, y, x, tile_width, - tile_height, emit_elem_function); + llvm::Value* tile_height, llvm::Value* tile_width, + const std::function& emit_elem_function) { + EmitTiledElementalCodeWithBoundsCheck(mapping_scheme, index, loop_name, + &ksl, &b_, y, x, tile_height, + tile_width, emit_elem_function); }; - // Adds `addend` to the given `dim` of `index`. - auto offset_dim = [&](IrArray::Index index, llvm::Value* addend, int64 dim) { - index[dim] = Add(index[dim], addend); - return index; - }; - const IrArray::Index input_index = - offset_dim(offset_dim(input_tile_origin, x, /*dim=*/2), y, /*dim=*/1); - - // Copy input parameter values to shared memory buffers: - // tile[y, x] = input[index] - emit_tiled_elemental_code_with_bounds_check( - input_index, "input", output_tile_bounds[1], output_tile_bounds[2], - [&](const IrArray::Index& index, llvm::Value* y_loc) { - for (int64 id : tiled_param_ids) { - IrArray& input_in_logical_shape = param_in_reduced_shape_arrays[id]; - llvm::Value* shmem_buffer = param_shmem_buffers[id]; - // TODO(jlebar): Add AA metadata to this store. Tile buffers are - // global variables, so LLVM can't infer much about it. - Store(input_in_logical_shape.EmitReadArrayElement(index, &b_, - "input_element"), - GEP(shmem_buffer, {index_typed_constant(0), y_loc, x})); - } - }); + auto emit_one_tile = [&](const IrArray::Index& output_tile_origin, + absl::Span output_tile_bounds, + bool block_contains_multi_tiles) { + // Calculate the input tile origin from the output tile origin. + const IrArray::Index input_tile_origin( + Permute({0, 2, 1}, output_tile_origin.multidim())); + + const IrArray::Index input_index = + input_tile_origin.AddOffsetToDim(x, KernelMappingScheme::DimX, &b_) + .AddOffsetToDim(y, KernelMappingScheme::DimY, &b_); + + // If shared memory transpose is needed, wait for all threads to reach this + // point, lest we copy a value from tile to output before the other thread + // copies it from input to tile. This is `__syncthreads` in CUDA. + if (!tiled_param_ids.empty()) { + // Copy input parameter values to shared memory buffers: + // tile[y, x] = input[index] + // Note that tile_width and tile_height are flipped here because we are + // reading a transposed tile. + emit_tiled_elemental_code_with_bounds_check( + input_index, "input", output_tile_bounds[2], output_tile_bounds[1], + [&](const IrArray::Index& index, llvm::Value* y_loc, + llvm::Value* x_loc) { + for (int64 id : tiled_param_ids) { + IrArray& input_in_logical_shape = + param_in_reduced_shape_arrays[id]; + llvm::Value* shmem_buffer = param_shmem_buffers[id]; + // TODO(jlebar): Add AA metadata to this store. Tile buffers are + // global variables, so LLVM can't infer much about it. + Store(input_in_logical_shape.EmitReadArrayElement( + index, &b_, "input_element"), + GEP(shmem_buffer, {index_typed_constant(0), y_loc, x_loc})); + } + }); - // Wait for all threads to reach this point, lest we copy a value from tile to - // output before the other thread copies it from input to tile. - // This is `__syncthreads` in CUDA. - llvm_ir::EmitCallToIntrinsic(llvm::Intrinsic::nvvm_barrier0, {}, {}, &b_); + // Wait for all threads to reach this point using `__syncthreads` in CUDA. + llvm_ir::EmitCallToIntrinsic(llvm::Intrinsic::nvvm_barrier0, {}, {}, &b_); + } - llvm_ir::TiledParameterInfo tiled_param_info(param_shmem_buffers, y, x); + llvm_ir::TiledParameterInfo tiled_param_info(param_shmem_buffers, y, x); + kernel_info->SetTiledParamInfo(&tiled_param_info); - const IrArray::Index output_index = - offset_dim(offset_dim(output_tile_origin, x, /*dim=*/2), y, /*dim=*/1); + const IrArray::Index output_index = + output_tile_origin.AddOffsetToDim(x, KernelMappingScheme::DimX, &b_) + .AddOffsetToDim(y, KernelMappingScheme::DimY, &b_); - // Write to output[index] by emitting code like normal, except that values for - // the tiled parameters are read from the shmem buffers. - if (hlo->opcode() == HloOpcode::kCopy) { + // Write to output[index] by emitting code like normal, except that values + // for the tiled parameters are read from the shmem buffers. emit_tiled_elemental_code_with_bounds_check( - output_index, "output", output_tile_bounds[2], output_tile_bounds[1], - [&](const IrArray::Index& index, llvm::Value* y_loc) { - // TODO(jlebar): Add AA metadata to this load. - llvm::Instruction* load_from_shmem_buffer = - Load(GEP(param_shmem_buffers[0], {b_.getInt64(0), x, y_loc}), - "output_element"); - output_in_reduced_shape_arrays[0].EmitWriteArrayElement( - index, load_from_shmem_buffer, &b_); - }); - } else { - CHECK_EQ(hlo->opcode(), HloOpcode::kFusion); - emit_tiled_elemental_code_with_bounds_check( - output_index, "output", output_tile_bounds[2], output_tile_bounds[1], - [&](const IrArray::Index& index, llvm::Value* y_loc) { - GpuElementalIrEmitter elem_emitter(hlo_module_config_, module_, &b_, - GetNestedComputer()); - FusedIrEmitter fused_emitter(GetGeneratorForOperandIrArrays(hlo), - &elem_emitter); - tiled_param_info.set_y(y_loc); - fused_emitter.SetTiledParameterInfo(&tiled_param_info); - TF_CHECK_OK(hlo->fused_expression_root()->Accept(&fused_emitter)); - IrArray::Index untiled_index = llvm_ir::GetUnreducedOutputIndex( - index, output_reduced_shapes[0], output_arrays[0].GetShape(), - &b_); - const llvm_ir::ElementGenerator& output_generator = - fused_emitter.GetRootGenerator(); - llvm::Value* output_value = - output_generator(untiled_index).ValueOrDie(); - if (hlo->IsMultiOutputFusion()) { - CHECK(output_value->getType()->isStructTy()); - CHECK_EQ(output_value->getType()->getStructNumElements(), - output_in_reduced_shape_arrays.size()); - for (int64 i = 0; i < output_in_reduced_shape_arrays.size(); ++i) { - output_in_reduced_shape_arrays[i].EmitWriteArrayElement( - index, ExtractValue(output_value, i), &b_); - } - } else { - output_in_reduced_shape_arrays[0].EmitWriteArrayElement( - index, output_value, &b_); - } + output_index, "output", output_tile_bounds[1], output_tile_bounds[2], + [&](const IrArray::Index& index, llvm::Value* y_loc, + llvm::Value* x_loc) { + kernel_generator.GetTileElementGenerator()(unnested_hlo, index, + kernel_info, y_loc, x_loc); }); + + // If a tile block contains multiple tiles and shared memory buffers are + // used, we need to wait for all threads to finish using the shared memory + // buffer for the current tile before we move on to process the next tile + // and overwrite the shared memory buffers. + if (block_contains_multi_tiles && !tiled_param_ids.empty()) { + llvm_ir::EmitCallToIntrinsic(llvm::Intrinsic::nvvm_barrier0, {}, {}, &b_); + } + }; + + const BlockPrologueGenerator& block_prologue_generator = + kernel_generator.GetBlockPrologueGenerator(); + if (block_prologue_generator) { + block_prologue_generator(unnested_hlo, kernel_info); } - // For multioutput fusion, emit a tuple with all the individual outputs. - if (hlo->IsMultiOutputFusion()) { - llvm_ir::EmitTuple(GetIrArray(*hlo, *hlo), output_arrays, &b_, module_); + EmitBlock(std::move(emit_one_tile), kernel_info, ksl, index_ty); + + const BlockEpilogueGenerator& block_epilogue_generator = + kernel_generator.GetBlockEpilogueGenerator(); + if (block_epilogue_generator) { + block_epilogue_generator(unnested_hlo, kernel_info); + } + + // For multioutput fusion, emit a tuple with pointers to all the individual + // outputs. + if (unnested_hlo->IsMultiOutputFusion()) { + std::vector output_arrays = + ConstructIrArrayForOutputs(*unnested_hlo); + llvm_ir::EmitTuple(GetIrArray(*unnested_hlo, *unnested_hlo), output_arrays, + &b_, module_); } return launch_dimensions; } +// Emits a kernel for the given hlo instruction using a tiled 0-2-1 transpose +// algorithm to improve the memory access patterns for the input parameters +// with a shape that is a 0-2-1 transpose of the output tensor shape. +// +// For the purpose of tiling, the output tensors have a logical shape of three +// components 0-2-1 while the relevant input parameters have a logical shape +// of three components 0-1-2 in the order major to minor. The x- and y- +// dimensions of the tensors are tiled in square tiles with an edge length +// `kTileSize`. Each thread block of `kTileSize` x `kNumRows` threads +// transposes one tile: each thread copies kTileSize/kNumRows elements from +// the input to a shared memory tile, then the otherwise "regular HLO kernel" +// reads from the shared memory instead of the original input. +// +// This is similar to the following CUDA algorithm in TensorFlow: +// https://goo.gl/MStRV6. +// +// `kTileSize` should usually be same as warp size. We currently choose 32 for +// `kTileSize` and 4 for `kNumRows`. The CUDA algorithm uses 8 for `kNumRows`. +// +// TODO(b/33320379): Here each block transposes 1 tile. It may be more +// efficient to launch fewer blocks so each transposes many tiles. +LaunchDimensions IrEmitterUnnested::EmitHlo021Tile( + HloInstruction* hlo, absl::Span reduced_output_dims, + absl::Span tiled_param_ids) { + constexpr int kNumRows = 4; + KernelMappingScheme mapping_scheme( + reduced_output_dims, /*tile_size_y=*/kWarpSize, + /*tile_size_x=*/kWarpSize, /*req_block_sizes=*/{1, 1, 1}, + /*num_threads_y=*/kNumRows, + /*num_threads_x=*/kWarpSize, &b_); + TileElementGenerator element_generator; + if (hlo->opcode() == HloOpcode::kCopy) { + element_generator = [&](HloInstruction* hlo, + const llvm_ir::IrArray::Index& index, + const KernelCodegenInfo* kernel_info, + llvm::Value* y_loc, llvm::Value* x_loc) { + EmitTileElementForCopy(hlo, index, kernel_info, y_loc, x_loc); + }; + } else { + DCHECK_EQ(hlo->opcode(), HloOpcode::kFusion); + element_generator = [&](HloInstruction* hlo, + const llvm_ir::IrArray::Index& index, + const KernelCodegenInfo* kernel_info, + llvm::Value* y_loc, llvm::Value* x_loc) { + EmitTileElementForFusion(hlo, index, kernel_info, y_loc, x_loc); + }; + } + KernelCodegenInfo kernel_info(&mapping_scheme); + KernelCodeGenerator kernel_generator(std::move(element_generator)); + return EmitKernel(hlo, tiled_param_ids, kernel_generator, &kernel_info); +} + namespace { // Returns true to indicate it is safe to use the tile based shared memory // transpose implementation to implement the kernel for the instruction. @@ -3562,8 +3083,8 @@ bool IrEmitterUnnested::CheckAndEmitHloWithTile021(HloInstruction* hlo) { ? ShapeUtil::GetSubshape(hlo->shape(), {0}) : hlo->shape(); - // If the output_shape is reduced to 021 shape, find all the parameters of the - // hlo that are in the corresponding 012 shape. + // If the output_shape is reduced to 021 shape, find all the parameters of + // the HLO that are in the corresponding 012 shape. std::vector params_012; optional> reduced_dims_021; for (int64 operand_idx = 0; operand_idx < hlo->operand_count(); @@ -3600,9 +3121,9 @@ bool IrEmitterUnnested::CheckAndEmitHloWithTile021(HloInstruction* hlo) { } // Each of our shared memory tiles has 32*33 elements (so ~4kb, if the - // elements are of size 4 bytes), and CUDA has an architectural limit of 48kb - // shared memory per SM. (This is increased to 96kb in Volta, but we don't - // use this, in part because it eats into our L1 cache space.) + // elements are of size 4 bytes), and CUDA has an architectural limit of + // 48kb shared memory per SM. (This is increased to 96kb in Volta, but we + // don't use this, in part because it eats into our L1 cache space.) // // For correctness we need to ensure that we don't make more than 48kb worth // of shmem tiles per block. And for performance, we'd probably like to use @@ -3610,9 +3131,9 @@ bool IrEmitterUnnested::CheckAndEmitHloWithTile021(HloInstruction* hlo) { // gpu core. // // We say without benchmarks that we want at least 3 threads/block, - // corresponding to 3 shmem tiles if the elements are 32 bits wide. We choose - // which params get the shmem transpose treatment arbitrarily; it's not clear - // if there's a Right Choice. + // corresponding to 3 shmem tiles if the elements are 32 bits wide. We + // choose which params get the shmem transpose treatment arbitrarily; it's + // not clear if there's a Right Choice. // // This is only sound if tiled transposes are the only place where we use // shared memory in fusions. If in the future other fusible ops use shared @@ -3645,6 +3166,246 @@ bool IrEmitterUnnested::CheckAndEmitHloWithTile021(HloInstruction* hlo) { return true; } +namespace { +// Checks that the outputs of a fusion with reduction are consistent. +Status AreFusedReductionOutputsConsistent( + absl::Span output_instructions, + const HloInstruction* first_reduce) { + for (const HloInstruction* inst : output_instructions) { + if (inst->opcode() == HloOpcode::kReduce) { + // Shapes, layouts and dimensions must be the same for all reduces + // inside of this fusion. + TF_RET_CHECK(ShapeUtil::Equal(first_reduce->shape(), inst->shape())); + TF_RET_CHECK(ShapeUtil::Equal(first_reduce->operand(0)->shape(), + inst->operand(0)->shape())); + TF_RET_CHECK(ShapeUtil::Equal(first_reduce->operand(1)->shape(), + inst->operand(1)->shape())); + TF_RET_CHECK(first_reduce->dimensions() == inst->dimensions()); + } else { + // For extra outputs we can relax shape equality to allow different + // types (with the same number of elements). Layouts still have to + // match. + TF_RET_CHECK(ShapeUtil::CompatibleIgnoringElementType( + first_reduce->operand(0)->shape(), inst->shape())); + TF_RET_CHECK(LayoutUtil::Equal(first_reduce->operand(0)->shape().layout(), + inst->shape().layout())); + } + } + return Status::OK(); +} + +// Finds the dimensions to keep for the reduction, sorts and returns the +// dimensions from minor to major. +DimensionVector GetDimensionsToKeepMinorToMajor( + const Shape& input_shape, absl::Span dims_to_reduce) { + DimensionVector input_dims(ShapeUtil::Rank(input_shape), 0); + absl::c_iota(input_dims, 0); + DimensionVector input_dims_to_keep; + for (int input_dim : input_dims) { + auto it = absl::c_find_if(dims_to_reduce, [&](int64 dim_to_reduce) { + return dim_to_reduce == input_dim; + }); + if (it == dims_to_reduce.end()) { + input_dims_to_keep.push_back(input_dim); + } + } + + // Sort the dimensions to keep from minor to major. + absl::c_sort(input_dims_to_keep, [&input_shape](int64 dim_a, int64 dim_b) { + return PositionInContainer(LayoutUtil::MinorToMajor(input_shape), dim_a) < + PositionInContainer(LayoutUtil::MinorToMajor(input_shape), dim_b); + }); + + VLOG(10) << "dims to keep minor to major" + << absl::StrJoin(input_dims_to_keep, ","); + return input_dims_to_keep; +} + +// Given the input shape and dimensions to reduce for the reduction to vector, +// returns : +// num_kept: the number of elements in the contiguous dimensions to keep. +// num_reduced_major: the number of elements in the dimensions to reduce that +// are more major than the dimensions to keep. +// num_reduced_minor: the number of elements in the dimensions to reduce that +// are more minor than the dimensions to kept. +std::tuple GetReductionToVectorDimensions( + const Shape& input_shape, absl::Span dims_to_reduce) { + DimensionVector input_dims_to_keep_minor_to_major = + GetDimensionsToKeepMinorToMajor(input_shape, dims_to_reduce); + CHECK(LayoutUtil::AreDimensionsConsecutive( + input_shape.layout(), input_dims_to_keep_minor_to_major)); + int num_reduced_major = 1, num_kept = 1, num_reduced_minor = 1; + if (input_dims_to_keep_minor_to_major.empty()) { + return std::make_tuple(num_reduced_major, num_kept, num_reduced_minor); + } + DimensionVector input_dims(ShapeUtil::Rank(input_shape), 0); + absl::c_iota(input_dims, 0); + absl::Span minor_to_major = + LayoutUtil::MinorToMajor(input_shape); + for (int input_dim : input_dims) { + int64 curr_dim_size = input_shape.dimensions(input_dim); + if (PositionInContainer(minor_to_major, input_dim) > + PositionInContainer(minor_to_major, + input_dims_to_keep_minor_to_major.back())) { + num_reduced_major *= curr_dim_size; + } else if (PositionInContainer(minor_to_major, input_dim) < + PositionInContainer(minor_to_major, + input_dims_to_keep_minor_to_major.front())) { + num_reduced_minor *= curr_dim_size; + } else { + num_kept *= curr_dim_size; + } + } + + return std::make_tuple(num_reduced_major, num_kept, num_reduced_minor); +} + +std::tuple ComputeMappingSchemeAndReductionKind( + const HloInstruction* first_reduce, llvm::IRBuilder<>* b) { + int64 depth = 1; + int64 height = 1; + int64 width = 1; + bool is_row_reduction = true; + int64 tile_size_x = 1; + int64 tile_size_y = 1; + int64 block_size_y = 1; + int64 block_size_z = 1; + int64 num_threads_x = 1; + int64 num_threads_y = 1; + const Shape& input_shape = first_reduce->operand(0)->shape(); + int64 num_input_elems = ShapeUtil::ElementsIn(input_shape); + int64 num_output_elems = ShapeUtil::ElementsIn(first_reduce->shape()); + int64 num_reduced_major, num_kept, num_reduced_minor; + std::tie(num_reduced_major, num_kept, num_reduced_minor) = + GetReductionToVectorDimensions(input_shape, first_reduce->dimensions()); + CHECK_EQ(num_output_elems, num_kept); + + if (num_kept == 1) { + // Scalar reduction is a special row reduction with depth = height = 1. + width = num_input_elems; + tile_size_x = kWarpSize * 16; + num_threads_x = kWarpSize; + } else if (num_reduced_minor == 1) { + // Column reduction reduces inputs with dimension [height, width], where + // width is the minor dimension, to dimension [width]. + height = num_reduced_major; + width = num_kept; + is_row_reduction = false; + tile_size_x = std::min(kWarpSize, num_kept); + // The old Column reduction algorithm uses kTileHeight = 128. We choose + // tile_size_y * block_size_y = 128 to match the value of kTileHeight. Using + // a non-trivial block_size_y here is a way to avoid unrolling all the 128 + // iterations. + tile_size_y = 32; + block_size_y = 4; + num_threads_x = tile_size_x; + } else { + // Row reduction reduces inputs with dimension [depth, height, width], + // where width is the most minor dimension, to dimension [height] . + depth = num_reduced_major; + height = num_kept; + width = num_reduced_minor; + num_threads_x = kWarpSize; + if (width % (kWarpSize * 64) == 0) { + tile_size_x = kWarpSize * 64; + } else { + tile_size_x = kWarpSize * 8; + block_size_z = 8; + while (depth % block_size_z != 0) { + block_size_z -= 1; + } + } + } + DCHECK_EQ(depth * height * width, num_input_elems); + VLOG(10) << "is_row_reduction " << is_row_reduction << depth << " " << height + << " " << width; + + DimensionVector dims_in_elem{depth, height, width}; + DimensionVector req_block_sizes{block_size_z, block_size_y, 1}; + llvm_ir::KernelMappingScheme mapping_scheme(dims_in_elem, tile_size_y, + tile_size_x, req_block_sizes, + num_threads_y, num_threads_x, b); + return std::make_tuple(mapping_scheme, is_row_reduction); +} + +} // namespace + +Status IrEmitterUnnested::EmitReductionToVector(HloInstruction* unnested_hlo) { + VLOG(10) << "Emitting reduction to vector " << unnested_hlo->ToString(); + + HloInstruction* reduce_or_tuple = unnested_hlo->opcode() == HloOpcode::kFusion + ? unnested_hlo->fused_expression_root() + : unnested_hlo; + absl::Span output_instructions = + GetOutputInstructions(&reduce_or_tuple); + const HloInstruction* first_reduce = + GetFirstReduceInstruction(output_instructions); + + if (output_instructions.size() > 1) { + TF_RETURN_IF_ERROR( + AreFusedReductionOutputsConsistent(output_instructions, first_reduce)); + } + + // Build an initializer thunk to initialize each reduction output. + std::vector> thunks; + for (int i = 0, e = output_instructions.size(); i != e; ++i) { + if (output_instructions[i]->opcode() != HloOpcode::kReduce) { + continue; + } + TF_ASSIGN_OR_RETURN( + std::unique_ptr initializer_thunk, + BuildInitializerThunk(unnested_hlo, + (output_instructions[i] == reduce_or_tuple) + ? ShapeIndex() + : ShapeIndex({i}))); + thunks.push_back(std::move(initializer_thunk)); + } + + // Build a kernel thunk to compute all the outputs. + std::unique_ptr kernel_thunk = + BuildKernelThunk(unnested_hlo, /*implements_whole_instruction=*/false); + + const Shape& input_shape = first_reduce->operand(0)->shape(); + // The layout of a reduction input is either set by LayoutAssignment for + // unnested kReduce or by InstructionFusion for fused kReduce. + CHECK(input_shape.has_layout()) << "LayoutAssignment or InstructionFusion " + "doesn't set the input layout of " + << first_reduce->ToString(); + + bool is_row_reduction; + llvm_ir::KernelMappingScheme mapping_scheme; + std::tie(mapping_scheme, is_row_reduction) = + ComputeMappingSchemeAndReductionKind(first_reduce, &b_); + ReductionCodegenInfo reduction_info(&mapping_scheme, is_row_reduction); + KernelCodeGenerator kernel_generator( + /*tile_element_generator=*/ + [&](HloInstruction* hlo, const llvm_ir::IrArray::Index& index, + const KernelCodegenInfo* kernel_info, llvm::Value* y_loc, + llvm::Value* x_loc) { + EmitTileElementForReduction(hlo, index, kernel_info, y_loc, x_loc); + }, + /*block_prologue_generator=*/ + [&](HloInstruction* hlo, KernelCodegenInfo* kernel_info) { + EmitPrologueForReduction(hlo, kernel_info); + }, + /*block_epilogue_generator*/ + [&](HloInstruction* hlo, KernelCodegenInfo* kernel_info) { + EmitEpilogueForReduction(hlo, kernel_info); + }); + + LaunchDimensions launch_dimensions = + EmitKernel(unnested_hlo, {}, kernel_generator, &reduction_info); + UpdateLaunchDimensions(launch_dimensions, kernel_thunk.get(), + ir_emitter_context_->llvm_module()); + + thunks.push_back(std::move(kernel_thunk)); + std::unique_ptr sequential_thunk = + absl::make_unique(std::move(thunks), unnested_hlo); + AddThunkToThunkSequence(std::move(sequential_thunk)); + + return Status::OK(); +} + Status IrEmitterUnnested::EmitConstantGlobals() { for (const BufferAllocation& allocation : ir_emitter_context_->buffer_assignment().Allocations()) { @@ -3666,10 +3427,10 @@ Status IrEmitterUnnested::EmitConstantGlobals() { } // These globals will be looked up by name by GpuExecutable so we need to - // give them an external linkage. Not all of their uses are visible in the - // LLVM IR (e.g. TupleThunk) so we can't give then a linkage that merely - // preserves their names (like available_externally), we also need to ensure - // that they stick around even if they're "unused". + // give them an external linkage. Not all of their uses are visible in + // the LLVM IR (e.g. TupleThunk) so we can't give then a linkage that + // merely preserves their names (like available_externally), we also need + // to ensure that they stick around even if they're "unused". // // We may have to be more more clever here in the future if we notice that // we're keeping around too many globals because of their linkage. diff --git a/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.h b/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.h index 334c0b3c20..85a0e5328c 100644 --- a/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.h +++ b/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.h @@ -16,9 +16,11 @@ limitations under the License. #ifndef TENSORFLOW_COMPILER_XLA_SERVICE_GPU_IR_EMITTER_UNNESTED_H_ #define TENSORFLOW_COMPILER_XLA_SERVICE_GPU_IR_EMITTER_UNNESTED_H_ +#include "absl/container/inlined_vector.h" #include "tensorflow/compiler/xla/service/gpu/ir_emitter.h" #include "tensorflow/compiler/xla/service/gpu/sequential_thunk.h" #include "tensorflow/compiler/xla/service/gpu/thunk.h" +#include "tensorflow/compiler/xla/service/llvm_ir/kernel_support_library.h" #include "tensorflow/compiler/xla/service/llvm_ir/kernel_tiling.h" namespace xla { @@ -47,6 +49,99 @@ namespace gpu { // class IrEmitterUnnested : public IrEmitter { public: + // Parameter block_contains_multi_tiles indicates whether a tile block + // consists of multiple tiles or not. If the tile block contains only one + // tile, there is no need to use atomic operation to accumulate a local result + // to a global result to implement reduction. + using TileGenerator = + std::function output_tile_bounds, + bool block_contains_multi_tiles)>; + // KernelCodegenInfo records the common information to support the code + // generation for a kernel to process tensor elements by blocks. A block of + // tensor elements may contain one or multiple tiles. The code generators that + // generate code for tile elements or block prologue/epilogue refer to this + // class in their prototypes. If the implementations of such code generators + // require other information that are specific to the HLO instructions, the + // implementations need to define and use derived classes of this class. + class KernelCodegenInfo { + public: + explicit KernelCodegenInfo(llvm_ir::KernelMappingScheme* mapping_scheme) + : mapping_scheme_(mapping_scheme), + tiled_param_info_(nullptr), + lane_id_(nullptr), + index_ty_(nullptr) {} + virtual ~KernelCodegenInfo() {} + + void SetLaneId(llvm::Value* v) { lane_id_ = v; } + void SetIndexType(llvm::Type* t) { index_ty_ = t; } + void SetTiledParamInfo(llvm_ir::TiledParameterInfo* tiled_param_info) { + CHECK_EQ(tiled_param_info_, nullptr); + tiled_param_info_ = tiled_param_info; + } + + llvm::Value* GetLaneId() const { return lane_id_; } + llvm_ir::KernelMappingScheme* GetKernelMappingScheme() const { + return mapping_scheme_; + } + llvm_ir::TiledParameterInfo* GetTiledParameterInfo() const { + return tiled_param_info_; + } + llvm::Type* GetIndexType() const { return index_ty_; } + + private: + llvm_ir::KernelMappingScheme* mapping_scheme_; + llvm_ir::TiledParameterInfo* tiled_param_info_; + llvm::Value* lane_id_; + llvm::Type* index_ty_; + }; + + // A function object to prepare for the code generation for a tile block. + using BlockPrologueGenerator = + std::function; + // A function object to finalize the code generation for a tile block. + using BlockEpilogueGenerator = + std::function; + // A function object to generate code to process one element in a tile. + // + // hlo: the instruction for which the code is generated for. + // index: the index for the first output element of the current thread. + // y_loc: The y coordinate within a tile. + // x_loc: The x coordinate within a tile. + // kernel_info: Other information to support the kernel code generation. + using TileElementGenerator = std::function; + + // KernelCodeGenerator records the code generator objects that generate code + // for tile elements or tile block prologue/epilogue. + class KernelCodeGenerator { + public: + explicit KernelCodeGenerator( + TileElementGenerator tile_element_generator, + BlockPrologueGenerator block_prologue_generator = {}, + BlockEpilogueGenerator block_epilogue_generator = {}) + : tile_element_generator_(std::move(tile_element_generator)), + block_prologue_generator_(std::move(block_prologue_generator)), + block_epilogue_generator_(std::move(block_epilogue_generator)) {} + + const TileElementGenerator& GetTileElementGenerator() const { + return tile_element_generator_; + } + const BlockPrologueGenerator& GetBlockPrologueGenerator() const { + return block_prologue_generator_; + } + const BlockEpilogueGenerator& GetBlockEpilogueGenerator() const { + return block_epilogue_generator_; + } + + private: + TileElementGenerator tile_element_generator_; + BlockPrologueGenerator block_prologue_generator_; + BlockEpilogueGenerator block_epilogue_generator_; + }; + IrEmitterUnnested(const HloModuleConfig& hlo_module_config, const HloComputation* hlo_computation, IrEmitterContext* ir_emitter_context); @@ -82,7 +177,7 @@ class IrEmitterUnnested : public IrEmitter { Status HandleSort(HloInstruction* sort) override; Status HandleTupleSelect(HloInstruction* tuple_select) override; Status HandleCrossReplicaSum(HloInstruction* crs) override; - Status HandleAfterAll(HloInstruction* gen_token) override; + Status HandleAfterAll(HloInstruction* after_all) override; Status EmitTargetElementLoop( const HloInstruction& hlo, @@ -111,82 +206,14 @@ class IrEmitterUnnested : public IrEmitter { // Helper for writing extra outputs from inside a reduce kernel. Status EmitExtraOutputsForReduce( - const HloInstruction* reduce, const llvm_ir::IrArray::Index& index, - absl::Span> - extra_output_gens); - - // EmitColumnReduction and EmitRowReduction emit code for column and row - // reduction of a matrix and/or 3D tensor. Row and column reduction have - // different memory access pattern, so for performance their implementations - // are significantly different. - // - // Emits code that reduces a matrix of shape [height x width] to a vector of - // [width]. Other parameters have the same meaning as those of - // `EmitReductionToVector`. Note that input shape might not be - // [height x width], but can be bitcast to [height x width] with "height" - // being the major dimension. - Status EmitColumnReduction( - KernelThunk* kernel_thunk, int64 height, int64 width, - HloInstruction* reduce, const Shape& input_shape, - absl::Span input_gens, - absl::Span init_value_gens, - absl::Span reducers, - absl::Span reduce_output_shapes, - absl::Span> - extra_output_gens); - - // Emits code that reduces a 3D tensor of shape [depth x height x width] to a - // vector of shape [height]. Other parameters have the same meaning as those - // of `EmitReductionToVector`. Note that input shape might not be - // [depth x height x width], but can be bitcast to [depth x height x width] - // with "depth" being the most major dimension. - Status EmitRowReduction( - KernelThunk* kernel_thunk, int64 depth, int64 height, int64 width, - HloInstruction* reduce, const Shape& input_shape, - absl::Span input_gens, - absl::Span init_value_gens, - absl::Span reducers, - absl::Span reduce_output_shapes, + const HloInstruction* unnested_hlo, const llvm_ir::IrArray::Index& index, absl::Span> extra_output_gens); - // Emits code that reduces a tensor of arbitrary rank to a scalar. - Status EmitReductionToScalar( - KernelThunk* kernel_thunk, HloInstruction* reduce, - const Shape& input_shape, - absl::Span input_gens, - absl::Span init_value_gens, - absl::Span reducers, - absl::Span reduce_output_shapes, - absl::Span> - extra_output_gens); - - // Figures out whether `reduce` is a row or column reduction, and which - // dimensions to reduce, and calls either `EmitRowReduction` or - // `EmitColumnReduction` as appropriate. `input_shape` is the shape of the - // input array, which is the operand of the Reduce instruction if unfused or - // of the Fusion instruction if fused. `input_gen` and `init_value_gen` - // generate elements of the input and the initial value. Other parameters mean - // the same as for `HandleReduce`. - // - // Multiple reduces can be emitted in the same loop, assuming they have the - // same input and output shapes, and the same reduce dimensions. - // - // extra_output_gens can contain extra generators for intermediate outputs. - // These must have the same shape as the reduce input as they are computed - // when the reduce inputs are being read. + // Generates code for reduction to contiguous dimensions. // - // Prerequisite: `IsReductionToVector(*reduce)` - Status EmitReductionToVector( - KernelThunk* kernel_thunk, HloInstruction* reduce, - const Shape& input_shape, - absl::Span input_gens, - absl::Span init_value_gens, - absl::Span dimensions_to_reduce, - absl::Span reducers, - absl::Span reduce_output_shapes, - absl::Span> - extra_output_gens); + // Prerequisite: `IsReductionToVector(*unnested_hlo)` + Status EmitReductionToVector(HloInstruction* unnested_hlo); // Emits code for an in-place scatter, modifying `thunk`s launch dimensions in // the process. `scatter` may be fused, scatter indices are taken from @@ -205,22 +232,55 @@ class IrEmitterUnnested : public IrEmitter { LaunchDimensions EmitHlo021Tile(HloInstruction* hlo, absl::Span reduced_output_dims, absl::Span tiled_param_ids); + // Emits a kernel for an unnested HLO instruction. + LaunchDimensions EmitKernel(HloInstruction* unnested_hlo, + absl::Span param_ids, + const KernelCodeGenerator& kernel_generator, + KernelCodegenInfo* kernel_info); + void EmitBlock(const TileGenerator& emit_one_tile, + const KernelCodegenInfo* kernel_info, + KernelSupportLibrary& ksl, llvm::Type* index_ty); + // Emits code to process a tensor element in a tile for the given kCopy HLO + // that performs a 0-2-1 transpose. + void EmitTileElementForCopy(HloInstruction* hlo, + const llvm_ir::IrArray::Index& index, + const KernelCodegenInfo* kernel_info, + llvm::Value* y_loc, llvm::Value* x_loc); + // Emits code to process a tensor element in a tile for the given kLoop fusion + // HLO containing parameters that are 0-2-1 transpose of its outputs. + void EmitTileElementForFusion(HloInstruction* hlo, + const llvm_ir::IrArray::Index& index, + const KernelCodegenInfo* kernel_info, + llvm::Value* y_loc, llvm::Value* x_loc); + // Emits code to process a tensor element in a tile for the given input hlo + // that is either a unnested kReduce or a kInput fusion. + void EmitTileElementForReduction(HloInstruction* unnested_hlo, + const llvm_ir::IrArray::Index& index, + const KernelCodegenInfo* kernel_info, + llvm::Value* y_loc, llvm::Value* x_loc); + // Prepares for the code generation for a tile block of a reduction kernel. + void EmitPrologueForReduction(HloInstruction* unnested_hlo, + KernelCodegenInfo* kernel_info); + void EmitPrologueForOneReduction(HloInstruction* unnested_hlo, + HloInstruction* reduce_inst, int reduce_idx, + KernelCodegenInfo* kernel_info, + GpuElementalIrEmitter* elemental_emitter, + ShapeIndex output_shape_index); + // Wraps up the code generation for a tile block of a reduction kernel. + void EmitEpilogueForReduction(HloInstruction* unnested_hlo, + KernelCodegenInfo* kernel_info); + // For each reducer, emits the shuffle-down loop to accumulate the partial + // result to the global result. + void EmitFullWarpShuffleDownLoopForAllReduces( + const absl::InlinedVector& reducers, + const absl::InlinedVector& + partial_result_addresses); // Generates the IrArray for each input of an hlo and returns a vector that // constains such IrArrays. std::vector ConstructIrArrayForInputs( const HloInstruction& hlo); - // For each output of the `hlo` instruction, constructs the reduced shape for - // the output with the given `reduced_output_dims` and cast the original - // output IrArray element in `output_arrays` to the reduced shape. Returns - // the number of outputs. - int ConstructOutputReducedShapeAndCastOutputIrArrayToShape( - const HloInstruction& hlo, - const std::vector& output_arrays, - absl::Span reduced_output_dims, - std::vector* output_reduced_shapes, - std::vector* output_in_reduced_shape_arrays); // For each input of the `hlo` instruction, checks its value in // `param_buffers` to find out whether the input has a reduced shape. If the // input has a reduced shape, constructs the reduced shape for the input and diff --git a/tensorflow/compiler/xla/service/gpu/llvm_gpu_backend/nvptx_backend_lib.cc b/tensorflow/compiler/xla/service/gpu/llvm_gpu_backend/nvptx_backend_lib.cc index 8751e3a9c2..24f07e6897 100644 --- a/tensorflow/compiler/xla/service/gpu/llvm_gpu_backend/nvptx_backend_lib.cc +++ b/tensorflow/compiler/xla/service/gpu/llvm_gpu_backend/nvptx_backend_lib.cc @@ -177,13 +177,6 @@ std::unique_ptr GetTargetMachine( } TargetOptions target_options = InitTargetOptionsFromCodeGenFlags(); - llvm_ir::SetTargetOptions( - /*fast_math_enabled=*/hlo_module_config.debug_options() - .xla_gpu_enable_fast_math(), - &target_options); - - // Enable FMA synthesis. - target_options.AllowFPOpFusion = FPOpFusion::Fast; // Set the verbose assembly options. target_options.MCOptions.AsmVerbose = false; @@ -453,18 +446,21 @@ void GPUBackendInit(const HloModuleConfig& hlo_module_config) { // * 3-6 gives similar results as 2; // * >6 start hurting the performance of at least dot product kernels. // - // TODO(jingyue): The current threshold only considers the numbr of IR + // TODO(jingyue): The current threshold only considers the number of IR // instructions which do not accurately reflect the true cost. We need a // better cost model. FeedLLVMWithFlags({"-bonus-inst-threshold=2"}); - // TODO(b/22073864): Increase limit when scan memory dependency. - // This helps to reduce more redundant load instructions. + // Increase limit when scanning memory dependencies. This helps to reduce + // more redundant load instructions. // // The specific value is currently large enough for s3d in shoc benchmark, // which contains a lot of load instructions and many arithmetic instructions // between those loads. FeedLLVMWithFlags({"-memdep-block-scan-limit=500"}); + // Use div.approx -- it matters for some float-division heavy benchmarks. + FeedLLVMWithFlags({"-nvptx-prec-divf32=0"}); + llvm_ir::InitializeLLVMCommandLineOptions(hlo_module_config); // Initialize the NVPTX target; it's the only target we link with, so call its diff --git a/tensorflow/compiler/xla/service/gpu/multi_output_fusion.cc b/tensorflow/compiler/xla/service/gpu/multi_output_fusion.cc index d9b06828e2..01fddcede6 100644 --- a/tensorflow/compiler/xla/service/gpu/multi_output_fusion.cc +++ b/tensorflow/compiler/xla/service/gpu/multi_output_fusion.cc @@ -41,50 +41,7 @@ GpuMultiOutputFusion::GpuMultiOutputFusion() : MultiOutputFusion(INT64_MAX) {} bool GpuMultiOutputFusion::ShapesCompatibleForFusion(HloInstruction* instr1, HloInstruction* instr2) { - auto get_element_instr = - [&](const HloInstruction* instr) -> const HloInstruction* { - const HloInstruction* element_instr = instr; - if (instr->opcode() == HloOpcode::kFusion) { - auto fused_expression_root = instr->fused_expression_root(); - if (instr->IsMultiOutputFusion()) { - // If possible, we want to pick a reduce operand of the fusion root, - // because it has the most constraints. - for (const auto* inst : fused_expression_root->operands()) { - if (IsReductionToVector(*inst)) { - return inst; - } - } - return fused_expression_root->operands()[0]; - } else { - element_instr = fused_expression_root; - } - } - return element_instr; - }; - - auto get_element_shape = [&](const HloInstruction* element_instr) { - // Special handling of kReduce instructions -- the fusion - // applies to the first operand. - if (IsReductionToVector(*element_instr)) { - return element_instr->operand(0)->shape(); - } - return element_instr->shape(); - }; - - // The shapes in all tuple operands should agree, unless it is a reduce. - // In that case, the operand of the reduce needs to have the same shape - // as the other tuple operands, but also we need to compare the output - // shapes of the reduces. - auto* element_instr_1 = get_element_instr(instr1); - auto* element_instr_2 = get_element_instr(instr2); - if (element_instr_1->opcode() == HloOpcode::kReduce && - element_instr_2->opcode() == HloOpcode::kReduce && - !ShapeUtil::Equal(element_instr_1->shape(), element_instr_2->shape())) { - return false; - } - // The elementwise output shapes must be the same (including layout). - return ShapeUtil::EqualIgnoringFpPrecision( - get_element_shape(element_instr_1), get_element_shape(element_instr_2)); + return ShapesCompatibleForMultiOutputFusion(*instr1, *instr2); } bool GpuMultiOutputFusion::IsFusible(HloInstruction* instr) { @@ -205,7 +162,7 @@ bool GpuMultiOutputFusion::DoProducerConsumerMultiOutputFusion() { VLOG(3) << producer->name() << " is not a loop fusion."; continue; } - if (!ShapesCompatibleForFusion(producer, consumer)) { + if (!ShapesCompatibleForMultiOutputFusion(*producer, *consumer)) { VLOG(3) << producer->name() << " has an incompatible shape."; continue; } diff --git a/tensorflow/compiler/xla/service/gpu/multi_output_fusion_test.cc b/tensorflow/compiler/xla/service/gpu/multi_output_fusion_test.cc index dc221f22a7..d16c87ba5c 100644 --- a/tensorflow/compiler/xla/service/gpu/multi_output_fusion_test.cc +++ b/tensorflow/compiler/xla/service/gpu/multi_output_fusion_test.cc @@ -580,7 +580,7 @@ TEST_F(MultiOutputFusionTest, AvoidsLargeFusion) { // ... // where each of the (pi * pj)'s is represented as a fusion node so that // multi-output fusion will pay attention to it. - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); HloComputation::Builder b(TestName()); Shape shape = ShapeUtil::MakeShape(F32, {10, 100}); diff --git a/tensorflow/compiler/xla/service/gpu/nvptx_compiler.cc b/tensorflow/compiler/xla/service/gpu/nvptx_compiler.cc index de04ed85c3..e934cbda17 100644 --- a/tensorflow/compiler/xla/service/gpu/nvptx_compiler.cc +++ b/tensorflow/compiler/xla/service/gpu/nvptx_compiler.cc @@ -67,6 +67,7 @@ limitations under the License. #include "tensorflow/compiler/xla/service/hlo_cse.h" #include "tensorflow/compiler/xla/service/hlo_dce.h" #include "tensorflow/compiler/xla/service/hlo_element_type_converter.h" +#include "tensorflow/compiler/xla/service/hlo_get_dimension_size_rewriter.h" #include "tensorflow/compiler/xla/service/hlo_instruction.h" #include "tensorflow/compiler/xla/service/hlo_pass_fix.h" #include "tensorflow/compiler/xla/service/hlo_pass_pipeline.h" @@ -173,13 +174,16 @@ Status OptimizeHloModule(HloModule* hlo_module, se::StreamExecutor* stream_exec, /*rewrite_inference_op=*/true, /*rewrite_grad_op=*/true); + pipeline.AddPass(); + // BatchNormExpander can create zero-sized ops, so zero-sized HLO // elimination has to come after that pass. pipeline.AddPass(); - pass.AddPass( - /*is_layout_sensitive=*/false, + AlgebraicSimplifierOptions options( [](const Shape&, const Shape&) { return false; }); + options.set_enable_permutation_sort_replacement(true); + pass.AddPass(options); pass.AddPass(); pass.AddPass(); pass.AddPass(); @@ -248,11 +252,13 @@ Status OptimizeHloModule(HloModule* hlo_module, se::StreamExecutor* stream_exec, // The LayoutAssignment pass may leave behind kCopy instructions which are // duplicate or NOPs, so remove them with algebraic simplification and CSE. - pipeline.AddPass>( - /*is_layout_sensitive=*/true, + AlgebraicSimplifierOptions options( /*valid_bitcast_callback=*/[](const Shape&, const Shape&) { return true; }); + options.set_is_layout_sensitive(true); + options.set_enable_permutation_sort_replacement(true); + pipeline.AddPass>(options); // Choose the fastest algorithm for each conv. // @@ -810,7 +816,7 @@ std::vector NVPTXCompiler::CompilePtxOrGetCachedResult(const string& ptx, // binaries are not available. We don't want to spam logs with // identical warnings in this case. - // TODO(zhengxq): we should implement a LOG_FIRST_N and LOG_EVERY_N + // TODO(jlebar): we should implement a LOG_FIRST_N and LOG_EVERY_N // for more general usage. static std::atomic warning_done(false); log_warning = !warning_done.exchange(true); diff --git a/tensorflow/compiler/xla/service/gpu/stream_assignment_test.cc b/tensorflow/compiler/xla/service/gpu/stream_assignment_test.cc index f2ef11e1e6..31a5d7a8c0 100644 --- a/tensorflow/compiler/xla/service/gpu/stream_assignment_test.cc +++ b/tensorflow/compiler/xla/service/gpu/stream_assignment_test.cc @@ -30,7 +30,7 @@ namespace gpu { class StreamAssignmentTest : public HloTestBase { protected: - std::unique_ptr CreateNewUnverifiedModule() { + std::unique_ptr CreateNewVerifiedModule() { HloModuleConfig config; auto debug_options = GetDebugOptionsForTest(); debug_options.set_xla_gpu_disable_multi_streaming(false); @@ -55,7 +55,7 @@ TEST_F(StreamAssignmentTest, SequentialMatMul) { HloInstruction* dot2 = builder.AddInstruction(CreateCanonicalDot(f32_2x2_, dot1, z)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build(dot2)); std::unique_ptr assignment = AssignStreams(*module); @@ -76,7 +76,7 @@ TEST_F(StreamAssignmentTest, ConcurrentMatMul) { HloInstruction* add = builder.AddInstruction( HloInstruction::CreateBinary(f32_2x2_, HloOpcode::kAdd, dot1, dot2)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build(add)); std::unique_ptr assignment = AssignStreams(*module); @@ -120,7 +120,7 @@ TEST_F(StreamAssignmentTest, LatticeMatMul) { HloInstruction* d40 = builder.AddInstruction(CreateCanonicalDot(f32_2x2_, d30, d31)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build(d40)); std::unique_ptr assignment = AssignStreams(*module); diff --git a/tensorflow/compiler/xla/service/gpu/tests/gpu_codegen_test.h b/tensorflow/compiler/xla/service/gpu/tests/gpu_codegen_test.h index d2f30ae7bc..d917320e36 100644 --- a/tensorflow/compiler/xla/service/gpu/tests/gpu_codegen_test.h +++ b/tensorflow/compiler/xla/service/gpu/tests/gpu_codegen_test.h @@ -26,7 +26,7 @@ namespace gpu { // Tests that verify IR or PTX emitted by the GPU backend is as expected. class GpuCodegenTest : public LlvmIrGenTestBase { protected: - // Like HloTestBase::CreateNewUnverifiedModule(), with a flag for configuring + // Like HloTestBase::CreateNewVerifiedModule(), with a flag for configuring // the ftz option. std::unique_ptr CreateNewUnverifiedModuleWithFTZ(bool ftz); diff --git a/tensorflow/compiler/xla/service/gpu/tests/gpu_copy_test.cc b/tensorflow/compiler/xla/service/gpu/tests/gpu_copy_test.cc index 268b48a1ca..a1ed849904 100644 --- a/tensorflow/compiler/xla/service/gpu/tests/gpu_copy_test.cc +++ b/tensorflow/compiler/xla/service/gpu/tests/gpu_copy_test.cc @@ -46,7 +46,7 @@ TEST_F(GpuCopyTest, UseMemcpy) { std::unique_ptr computation = builder.Build(); - auto hlo_module = CreateNewUnverifiedModule(); + auto hlo_module = CreateNewVerifiedModule(); hlo_module->AddEntryComputation(std::move(computation)); // There should not be any kernel prefixed "copy". diff --git a/tensorflow/compiler/xla/service/gpu/tests/gpu_ftz_test.cc b/tensorflow/compiler/xla/service/gpu/tests/gpu_ftz_test.cc index d0ccd8619b..5e524faab1 100644 --- a/tensorflow/compiler/xla/service/gpu/tests/gpu_ftz_test.cc +++ b/tensorflow/compiler/xla/service/gpu/tests/gpu_ftz_test.cc @@ -75,16 +75,16 @@ class GpuFtzDisabledTest : public GpuFtzTest { // Check that we emit mul.ftz.f32 when in ftz mode, and plain mul.f32 otherwise. TEST_F(GpuFtzEnabledTest, MultiplyFtz) { CompileAndVerifyPtx(CreateBinaryOpModule(HloOpcode::kMultiply), R"( - CHECK-NOT: mul.f32 - CHECK: mul.ftz.f32 - CHECK-NOT: mul.f32 + CHECK-NOT: mul.rn.f32 + CHECK: mul.rn.ftz.f32 + CHECK-NOT: mul.rn.f32 )"); } TEST_F(GpuFtzDisabledTest, MultiplyFtz) { CompileAndVerifyPtx(CreateBinaryOpModule(HloOpcode::kMultiply), R"( - CHECK-NOT: mul.ftz.f32 - CHECK: mul.f32 - CHECK-NOT: mul.ftz.f32 + CHECK-NOT: mul.rn.ftz.f32 + CHECK: mul.rn.f32 + CHECK-NOT: mul.rn.ftz.f32 )"); } diff --git a/tensorflow/compiler/xla/service/gpu/tests/gpu_index_test.cc b/tensorflow/compiler/xla/service/gpu/tests/gpu_index_test.cc index da8e513a2c..6814be779e 100644 --- a/tensorflow/compiler/xla/service/gpu/tests/gpu_index_test.cc +++ b/tensorflow/compiler/xla/service/gpu/tests/gpu_index_test.cc @@ -51,7 +51,7 @@ TEST_F(GpuIndexTest, CompatibleUseLinearIndex) { builder.AddInstruction(HloInstruction::CreateBinary( ShapeUtil::MakeShape(PRED, {5, 7, 2}), HloOpcode::kGe, param_x, param_y)); - auto hlo_module = CreateNewUnverifiedModule(); + auto hlo_module = CreateNewVerifiedModule(); hlo_module->AddEntryComputation(builder.Build()); // Check the optimized IR as the unoptimized IR contains dead udiv and urem. diff --git a/tensorflow/compiler/xla/service/gpu/tests/gpu_ldg_test.cc b/tensorflow/compiler/xla/service/gpu/tests/gpu_ldg_test.cc index ea1fee040d..3019215c01 100644 --- a/tensorflow/compiler/xla/service/gpu/tests/gpu_ldg_test.cc +++ b/tensorflow/compiler/xla/service/gpu/tests/gpu_ldg_test.cc @@ -48,7 +48,7 @@ TEST_F(GpuLdgTest, LdgForParamRead) { HloInstruction::CreateBinary(shape, HloOpcode::kAdd, param, param)); std::unique_ptr computation = builder.Build(); - auto hlo_module = CreateNewUnverifiedModule(); + auto hlo_module = CreateNewVerifiedModule(); hlo_module->AddEntryComputation(std::move(computation)); CompileAndVerifyPtx(std::move(hlo_module), R"( @@ -73,7 +73,7 @@ TEST_F(GpuLdgTest, LdgForNonParamRead) { builder.AddInstruction(HloInstruction::CreateTuple({add, square})); std::unique_ptr computation = builder.Build(); - auto hlo_module = CreateNewUnverifiedModule(); + auto hlo_module = CreateNewVerifiedModule(); hlo_module->AddEntryComputation(std::move(computation)); CompileAndVerifyPtx(std::move(hlo_module), R"( @@ -95,7 +95,7 @@ TEST_F(GpuLdgTest, LdgForNonParamRead) { // reduce in the foreseeable future. But if that turns out to be wrong, I give // you, future reader, permission to delete this test. TEST_F(GpuLdgTest, NoLdgWhenSharingBuffer) { - auto hlo_module = CreateNewUnverifiedModule(); + auto hlo_module = CreateNewVerifiedModule(); HloComputation::Builder builder(TestName()); HloComputation* reduce_computation; diff --git a/tensorflow/compiler/xla/service/gpu/tests/gpu_noalias_test.cc b/tensorflow/compiler/xla/service/gpu/tests/gpu_noalias_test.cc index 14285459b5..ca0a78034d 100644 --- a/tensorflow/compiler/xla/service/gpu/tests/gpu_noalias_test.cc +++ b/tensorflow/compiler/xla/service/gpu/tests/gpu_noalias_test.cc @@ -47,7 +47,7 @@ TEST_F(GpuNoAliasTest, Concat) { std::unique_ptr computation = builder.Build(); - auto hlo_module = CreateNewUnverifiedModule(); + auto hlo_module = CreateNewVerifiedModule(); hlo_module->AddEntryComputation(std::move(computation)); CompileAndVerifyIr(std::move(hlo_module), diff --git a/tensorflow/compiler/xla/service/gpu/thunk_schedule.cc b/tensorflow/compiler/xla/service/gpu/thunk_schedule.cc index 141f321938..6b2d76764a 100644 --- a/tensorflow/compiler/xla/service/gpu/thunk_schedule.cc +++ b/tensorflow/compiler/xla/service/gpu/thunk_schedule.cc @@ -45,7 +45,7 @@ void ThunkSchedule::AddDependenciesOnTransitiveOperands( ThunkSchedule::ThunkSchedule( std::unique_ptr thunks, std::unique_ptr stream_assignment, - const std::vector& hlo_total_order) + const std::vector& hlo_total_order) : thunks_(std::move(thunks)), stream_assignment_(std::move(stream_assignment)) { std::unordered_map hlo_to_thunk; @@ -53,7 +53,7 @@ ThunkSchedule::ThunkSchedule( InsertOrDie(&hlo_to_thunk, thunk->hlo_instruction(), thunk.get()); } - for (const HloInstruction* hlo : hlo_total_order) { + for (HloInstruction* hlo : hlo_total_order) { if (hlo_to_thunk.count(hlo)) { thunk_total_order_.push_back(FindOrDie(hlo_to_thunk, hlo)); } diff --git a/tensorflow/compiler/xla/service/gpu/thunk_schedule.h b/tensorflow/compiler/xla/service/gpu/thunk_schedule.h index d3352994f8..43b628a1ba 100644 --- a/tensorflow/compiler/xla/service/gpu/thunk_schedule.h +++ b/tensorflow/compiler/xla/service/gpu/thunk_schedule.h @@ -46,7 +46,7 @@ class ThunkSchedule { public: ThunkSchedule(std::unique_ptr thunks, std::unique_ptr stream_assignment, - const std::vector& hlo_total_order); + const std::vector& hlo_total_order); // Returns the total order of executing all the thunks. const std::vector& TotalOrder() const { return thunk_total_order_; } diff --git a/tensorflow/compiler/xla/service/gpu/while_transformer_test.cc b/tensorflow/compiler/xla/service/gpu/while_transformer_test.cc index c7f5112764..2dce7749bb 100644 --- a/tensorflow/compiler/xla/service/gpu/while_transformer_test.cc +++ b/tensorflow/compiler/xla/service/gpu/while_transformer_test.cc @@ -29,7 +29,7 @@ namespace { class WhileTransformerTest : public HloTestBase { protected: WhileTransformerTest() - : module_(CreateNewUnverifiedModule()), + : module_(CreateNewVerifiedModule()), induction_variable_shape_(ShapeUtil::MakeShape(S32, {})), data_shape_(ShapeUtil::MakeShape(F32, {8})), condition_result_shape_(ShapeUtil::MakeShape(PRED, {})) {} diff --git a/tensorflow/compiler/xla/service/heap_simulator_test.cc b/tensorflow/compiler/xla/service/heap_simulator_test.cc index fad3215fc8..dc40b9446a 100644 --- a/tensorflow/compiler/xla/service/heap_simulator_test.cc +++ b/tensorflow/compiler/xla/service/heap_simulator_test.cc @@ -258,7 +258,7 @@ class HeapSimulatorTracker { // Constructor for testing a single entry computation. HeapSimulatorTracker( const string& name, std::unique_ptr computation, - const std::vector& instruction_sequence) { + const std::vector& instruction_sequence) { HloModuleConfig config; module_ = absl::make_unique(name, config); module_->AddEntryComputation(std::move(computation)); @@ -286,7 +286,7 @@ class HeapSimulatorTracker { // Similar to the single entry computation constructor above, but runs the // simulation over the entire module. void RunWholeModule( - const std::vector& full_module_sequence) { + const std::vector& full_module_sequence) { points_to_analysis_ = TuplePointsToAnalysis::Run(module_.get()).ConsumeValueOrDie(); @@ -294,7 +294,7 @@ class HeapSimulatorTracker { HloSchedule schedule(module_.get()); absl::flat_hash_map reverse_position; for (int i = 0; i < full_module_sequence.size(); ++i) { - const HloInstruction* instruction = full_module_sequence[i]; + HloInstruction* instruction = full_module_sequence[i]; schedule.GetOrCreateSequence(instruction->parent()) .push_back(instruction); reverse_position[instruction] = full_module_sequence.size() - i; diff --git a/tensorflow/compiler/xla/service/hlo.proto b/tensorflow/compiler/xla/service/hlo.proto index dbab62f847..414c632712 100644 --- a/tensorflow/compiler/xla/service/hlo.proto +++ b/tensorflow/compiler/xla/service/hlo.proto @@ -51,7 +51,7 @@ message HloInstructionProto { string name = 1; string opcode = 2; - xla.Shape shape = 3; + xla.ShapeProto shape = 3; xla.OpMetadata metadata = 7; @@ -132,7 +132,7 @@ message HloInstructionProto { string custom_call_opaque = 53; // Shape of outfeed request. - xla.Shape outfeed_shape = 29; + xla.ShapeProto outfeed_shape = 29; // Describes the dimension numbers used for a dot operation xla.DotDimensionNumbers dot_dimension_numbers = 30; @@ -190,7 +190,7 @@ message HloInstructionProto { // 'operand_shapes_with_layout' must contain a shape with layout for each // operand. bool constrain_layout = 56; - repeated Shape operand_shapes_with_layout = 57; + repeated xla.ShapeProto operand_shapes_with_layout = 57; } // Serialization of HloComputation. @@ -205,7 +205,8 @@ message HloComputationProto { repeated HloInstructionProto instructions = 2; // The program shape (with layout) of this computation. - xla.ProgramShape program_shape = 4; + + xla.ProgramShapeProto program_shape = 4; // The id of this computation. int64 id = 5; @@ -251,6 +252,41 @@ message HloInputOutputAliasProto { repeated AliasEntryProto entries = 1; } +message DynamicParameterBindingProto { + // A list of bindings which indicates that the `target_dim_num` in + // the subshape `target_param_index` of parameter `target_param_num` + // is a dynamic dimension and its real dynamic size is represented + // by `dynamic_param_index` in parameter `dynamic_param_num`. + // + // As an example, imagine we have a program: + // + // ENTRY main { + // a = f32[] parameter(0) + // b = f32[10] parameter(1) + // ROOT root = (f32[], f32[10]) tuple(%a, %b) + // } + // + // Let's say 'b' (param index 1) is a dynamic shape whose input has + // an upperbound of 10 and real size is determined at runtime.'a' + // represents the real size of b's first dimension. + // + // In this case, the fields are set in the following way: + // dynamic_param_num = 1 + // dynamic_param_index = {} + // target_param_num = 0 + // target_param_index = {} + // target_param_dim = 0 + message Binding { + int64 dynamic_param_num = 1; + repeated int64 dynamic_param_index = 2; + int64 target_param_num = 3; + repeated int64 target_param_index = 4; + int64 target_param_dim_num = 5; + } + + repeated Binding entries = 1; +} + // Serialization of HloModule. message HloModuleProto { string name = 1; @@ -262,7 +298,7 @@ message HloModuleProto { repeated HloComputationProto computations = 3; // The host program shape (with layout) of the entry computation. - xla.ProgramShape host_program_shape = 4; + xla.ProgramShapeProto host_program_shape = 4; // The id of this module. int64 id = 5; @@ -272,6 +308,8 @@ message HloModuleProto { // Describes alias information between inputs and outputs. HloInputOutputAliasProto input_output_alias = 8; + + DynamicParameterBindingProto dynamic_parameter_binding = 9; } // Serialization of LogicalBuffer. diff --git a/tensorflow/compiler/xla/service/hlo_computation.cc b/tensorflow/compiler/xla/service/hlo_computation.cc index 0c20d207dd..ff122b529b 100644 --- a/tensorflow/compiler/xla/service/hlo_computation.cc +++ b/tensorflow/compiler/xla/service/hlo_computation.cc @@ -499,7 +499,7 @@ HloComputationProto HloComputation::ToProto() const { proto.add_instructions()->Swap(&instruction_proto); } proto.set_root_id(root_instruction()->unique_id()); - *proto.mutable_program_shape() = ComputeProgramShape(); + *proto.mutable_program_shape() = ComputeProgramShape().ToProto(); return proto; } @@ -711,6 +711,8 @@ bool HloComputation::operator==(const HloComputation& other) const { return eq(root_instruction(), other.root_instruction()); } +uint64 HloComputation::Hash() const { return root_instruction()->Hash(); } + Status HloComputation::ReplaceWithNewInstruction( HloInstruction* old_instruction, std::unique_ptr new_instruction) { @@ -795,7 +797,7 @@ Status HloComputation::AcceptWithOperandOrder( template Status HloComputation::AcceptOrdered( DfsHloVisitorBase* visitor, - const std::vector& order) const { + const std::vector& order) const { VLOG(3) << "Accepting visitor with order."; for (HloInstruction* root : CollectUnreachableRoots()) { TF_RET_CHECK(std::find(order.begin(), order.end(), root) != order.end()) @@ -825,9 +827,9 @@ Status HloComputation::AcceptOrdered( // Explicit instantiations. template Status HloComputation::AcceptOrdered( - DfsHloVisitor*, const std::vector&) const; + DfsHloVisitor*, const std::vector&) const; template Status HloComputation::AcceptOrdered( - ConstDfsHloVisitor*, const std::vector&) const; + ConstDfsHloVisitor*, const std::vector&) const; Status HloComputation::Accept( const std::function& visitor_func) { diff --git a/tensorflow/compiler/xla/service/hlo_computation.h b/tensorflow/compiler/xla/service/hlo_computation.h index fc7d2035e5..c584e4c7ca 100644 --- a/tensorflow/compiler/xla/service/hlo_computation.h +++ b/tensorflow/compiler/xla/service/hlo_computation.h @@ -264,6 +264,12 @@ class HloComputation { // Return whether `*this` and `other` are functionally equivalent. bool operator==(const HloComputation& other) const; + // Generates a hash value of an HLO computation. Hash considers + // information on opcode, shape, operands, and typically a root instruction. + // This function returns the same hash value for equivalent HLO computations, + // with respect to HloInstruction::Identical() method. + uint64 Hash() const; + // Replaces old instruction with newly created instruction. Removes old // instruction from computation. Updates uses and root instruction. Status ReplaceWithNewInstruction( @@ -301,7 +307,7 @@ class HloComputation { // be a topological sort of all instructions in the computation. template Status AcceptOrdered(DfsHloVisitorBase* visitor, - const std::vector& order) const; + const std::vector& order) const; // Same as Accept() above, but the visitor is given as a function. Status Accept(const std::function& visitor_func); diff --git a/tensorflow/compiler/xla/service/hlo_computation_test.cc b/tensorflow/compiler/xla/service/hlo_computation_test.cc index 1e7a6e197f..8b50cfa9ae 100644 --- a/tensorflow/compiler/xla/service/hlo_computation_test.cc +++ b/tensorflow/compiler/xla/service/hlo_computation_test.cc @@ -65,7 +65,7 @@ class HloComputationTest : public HloTestBase { }; TEST_F(HloComputationTest, GetEmbeddedComputationsEmpty) { - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto negate_computation = module->AddEntryComputation(CreateNegateComputation()); EXPECT_TRUE(negate_computation->MakeEmbeddedComputationsList().empty()); @@ -73,7 +73,7 @@ TEST_F(HloComputationTest, GetEmbeddedComputationsEmpty) { TEST_F(HloComputationTest, GetEmbeddedComputationsOneComputation) { // Create computation which calls one other computation. - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto negate_computation = module->AddEmbeddedComputation(CreateNegateComputation()); auto map_computation = @@ -85,7 +85,7 @@ TEST_F(HloComputationTest, GetEmbeddedComputationsOneComputation) { TEST_F(HloComputationTest, GetEmbeddedComputationsDiamond) { // Create computations with a diamond-shaped callgraph. - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto negate_computation = module->AddEmbeddedComputation(CreateNegateComputation()); auto map1_computation = @@ -119,7 +119,7 @@ TEST_F(HloComputationTest, PostOrderSingleton) { auto builder = HloComputation::Builder(TestName()); auto constant = builder.AddInstruction( HloInstruction::CreateConstant(LiteralUtil::CreateR0(42.0f))); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); EXPECT_THAT(computation->MakeInstructionPostOrder(), ElementsAre(constant)); } @@ -134,7 +134,7 @@ TEST_F(HloComputationTest, PostOrderSimple) { HloInstruction::CreateUnary(r0f32_, HloOpcode::kNegate, constant)); auto negate2 = builder.AddInstruction( HloInstruction::CreateUnary(r0f32_, HloOpcode::kNegate, negate1)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); EXPECT_THAT(computation->MakeInstructionPostOrder(), ElementsAre(constant, negate1, negate2)); @@ -170,7 +170,7 @@ TEST_F(HloComputationTest, PostOrderDisconnectedInstructions) { HloInstruction::CreateConstant(LiteralUtil::CreateR0(42.0f))); auto constant4 = builder.AddInstruction( HloInstruction::CreateConstant(LiteralUtil::CreateR0(42.0f))); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); EXPECT_THAT(computation->MakeInstructionPostOrder(), UnorderedElementsAre(constant1, constant2, constant3, constant4)); @@ -192,7 +192,7 @@ TEST_F(HloComputationTest, PostOrderWithMultipleRoots) { r0f32_, HloOpcode::kAdd, constant2, constant3)); auto add3 = builder.AddInstruction(HloInstruction::CreateBinary( r0f32_, HloOpcode::kAdd, constant1, constant3)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); auto post_order = computation->MakeInstructionPostOrder(); EXPECT_EQ(6, post_order.size()); @@ -217,7 +217,7 @@ TEST_F(HloComputationTest, VisitWithMultipleRoots) { constant2, constant3)); builder.AddInstruction(HloInstruction::CreateBinary(r0f32_, HloOpcode::kAdd, constant1, constant3)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); // Visitor which keeps track of which instructions have been visited. class TestVisitor : public DfsHloVisitorWithDefault { @@ -257,7 +257,7 @@ TEST_F(HloComputationTest, DeepCopyArray) { auto builder = HloComputation::Builder(TestName()); auto constant = builder.AddInstruction(HloInstruction::CreateConstant( LiteralUtil::CreateR1({1.0, 2.0, 3.0}))); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); auto copy = computation->DeepCopyInstruction(constant).ValueOrDie(); @@ -274,7 +274,7 @@ TEST_F(HloComputationTest, DeepCopyTuple) { auto tuple = builder.AddInstruction( HloInstruction::CreateTuple({constant1, constant2})); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); auto tuple_copy = computation->DeepCopyInstruction(tuple).ValueOrDie(); @@ -376,7 +376,7 @@ TEST_F(HloComputationTest, DeepCopyToken) { // copied. auto builder = HloComputation::Builder(TestName()); auto token = builder.AddInstruction(HloInstruction::CreateToken()); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); auto copy = computation->DeepCopyInstruction(token).ValueOrDie(); @@ -393,7 +393,7 @@ TEST_F(HloComputationTest, DeepCopyTokenTuple) { HloInstruction::CreateConstant(LiteralUtil::CreateR0(42.0))); auto tuple = builder.AddInstruction(HloInstruction::CreateTuple({token, constant})); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); auto copy = computation->DeepCopyInstruction(tuple).ValueOrDie(); @@ -440,7 +440,7 @@ TEST_F(HloComputationTest, RemoveInstructionWithDuplicateOperand) { r0f32_, HloOpcode::kAdd, dead_negate, dead_negate)); auto negate = builder.AddInstruction( HloInstruction::CreateUnary(r0f32_, HloOpcode::kNegate, constant)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); EXPECT_EQ(4, computation->instruction_count()); EXPECT_THAT(computation->root_instruction(), op::Negate(constant)); @@ -466,7 +466,7 @@ TEST_F(HloComputationTest, CloneWithControlDependency) { HloInstruction::CreateParameter(0, r0f32_, "param0")); auto negate = builder.AddInstruction( HloInstruction::CreateUnary(r0f32_, HloOpcode::kNegate, param)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build(/*root_instruction=*/add)); @@ -505,7 +505,7 @@ TEST_F(HloComputationTest, Stringification) { 2, PrecisionConfig::DEFAULT); builder.AddInstruction( HloInstruction::CreateDot(sout, x, reshape, dot_dnums, precision_config)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto* computation = module->AddEntryComputation(builder.Build()); auto options = HloPrintOptions().set_print_metadata(false); @@ -540,7 +540,7 @@ TEST_F(HloComputationTest, StringificationIndent) { 2, PrecisionConfig::DEFAULT); builder.AddInstruction( HloInstruction::CreateDot(sout, x, reshape, dot_dnums, precision_config)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto* computation = module->AddEntryComputation(builder.Build()); auto options = @@ -576,7 +576,7 @@ TEST_F(HloComputationTest, StringificationCanonical) { 2, PrecisionConfig::DEFAULT); builder.AddInstruction( HloInstruction::CreateDot(sout, x, reshape, dot_dnums, precision_config)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto* computation = module->AddEntryComputation(builder.Build()); auto options = HloPrintOptions().set_print_metadata(false); diff --git a/tensorflow/compiler/xla/service/hlo_constant_folding_test.cc b/tensorflow/compiler/xla/service/hlo_constant_folding_test.cc index d12f920722..4f81dc94e5 100644 --- a/tensorflow/compiler/xla/service/hlo_constant_folding_test.cc +++ b/tensorflow/compiler/xla/service/hlo_constant_folding_test.cc @@ -22,21 +22,22 @@ limitations under the License. #include "tensorflow/compiler/xla/literal.h" #include "tensorflow/compiler/xla/service/hlo_computation.h" #include "tensorflow/compiler/xla/service/hlo_instruction.h" -#include "tensorflow/compiler/xla/service/hlo_matchers.h" #include "tensorflow/compiler/xla/service/hlo_opcode.h" #include "tensorflow/compiler/xla/service/hlo_parser.h" #include "tensorflow/compiler/xla/service/hlo_pass_fix.h" +#include "tensorflow/compiler/xla/service/pattern_matcher.h" +#include "tensorflow/compiler/xla/service/pattern_matcher_gmock.h" #include "tensorflow/compiler/xla/shape_util.h" #include "tensorflow/compiler/xla/test.h" #include "tensorflow/compiler/xla/tests/hlo_test_base.h" #include "tensorflow/compiler/xla/tests/literal_test_util.h" #include "tensorflow/compiler/xla/types.h" -namespace op = xla::testing::opcode_matchers; - namespace xla { namespace { +namespace m = xla::match; + using HloConstantFoldingTest = HloTestBase; TEST_F(HloConstantFoldingTest, ConvertF32ToS64) { @@ -49,13 +50,14 @@ TEST_F(HloConstantFoldingTest, ConvertF32ToS64) { auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Convert(input)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Convert().WithOperand(0, m::Op().Is(input)))); HloConstantFolding const_folder; TF_ASSERT_OK_AND_ASSIGN(bool result, const_folder.Run(module.get())); EXPECT_TRUE(result); - EXPECT_THAT(computation->root_instruction(), op::Constant()); + EXPECT_THAT(computation->root_instruction(), GmockMatch(m::Constant())); EXPECT_EQ(computation->root_instruction()->literal().GetFirstElement(), 42); } @@ -70,13 +72,14 @@ TEST_F(HloConstantFoldingTest, ConvertS64ToF32) { auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Convert(input)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Convert().WithOperand(0, m::Op().Is(input)))); HloConstantFolding const_folder; TF_ASSERT_OK_AND_ASSIGN(bool result, const_folder.Run(module.get())); EXPECT_TRUE(result); - EXPECT_THAT(computation->root_instruction(), op::Constant()); + EXPECT_THAT(computation->root_instruction(), GmockMatch(m::Constant())); EXPECT_EQ(computation->root_instruction()->literal().GetFirstElement(), 42.0f); } @@ -91,13 +94,14 @@ TEST_F(HloConstantFoldingTest, ConvertF32ArrayToS64Array) { auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); - EXPECT_THAT(computation->root_instruction(), op::Convert(input)); + EXPECT_THAT(computation->root_instruction(), + GmockMatch(m::Convert().WithOperand(0, m::Op().Is(input)))); HloConstantFolding const_folder; TF_ASSERT_OK_AND_ASSIGN(bool result, const_folder.Run(module.get())); EXPECT_TRUE(result); - EXPECT_THAT(computation->root_instruction(), op::Constant()); + EXPECT_THAT(computation->root_instruction(), GmockMatch(m::Constant())); EXPECT_EQ(computation->root_instruction()->literal().Get({0}), 42); EXPECT_EQ(computation->root_instruction()->literal().Get({1}), 19); } @@ -138,7 +142,7 @@ TEST_F(HloConstantFoldingTest, Concatenate) { EXPECT_TRUE(result); HloInstruction* root = computation->root_instruction(); - EXPECT_THAT(root, op::Constant()); + EXPECT_THAT(root, GmockMatch(m::Constant())); EXPECT_TRUE(ShapeUtil::Equal(root->shape(), shape)); } } @@ -165,7 +169,7 @@ TEST_F(HloConstantFoldingTest, Slice) { EXPECT_TRUE(result); HloInstruction* root = computation->root_instruction(); - EXPECT_THAT(root, op::Constant()); + EXPECT_THAT(root, GmockMatch(m::Constant())); EXPECT_TRUE(ShapeUtil::Equal(root->shape(), shape)); } @@ -190,7 +194,7 @@ TEST_F(HloConstantFoldingTest, TransposeConstantFold) { EXPECT_TRUE(result); HloInstruction* root = computation->root_instruction(); - EXPECT_THAT(root, op::Constant()); + EXPECT_THAT(root, GmockMatch(m::Constant())); EXPECT_TRUE(ShapeUtil::Compatible(root->shape(), shape)); using NativeT = typename primitive_util::PrimitiveTypeToNative::type; @@ -240,7 +244,8 @@ TEST_F(HloConstantFoldingTest, ConstantFoldReduceNoLayout) { TF_ASSERT_OK_AND_ASSIGN(bool result, const_folder.Run(m.get())); EXPECT_FALSE(result); - EXPECT_THAT(m->entry_computation()->root_instruction(), op::Reduce()); + EXPECT_THAT(m->entry_computation()->root_instruction(), + GmockMatch(m::Reduce())); } const char* const kConstantFoldLargePad = R"( @@ -260,7 +265,7 @@ TEST_F(HloConstantFoldingTest, DoesNotFoldLargePad) { EXPECT_FALSE(result); EXPECT_THAT(module->entry_computation()->root_instruction(), - op::Pad(op::Constant(), op::Constant())); + GmockMatch(m::Pad(m::Constant(), m::Constant()))); } } // namespace diff --git a/tensorflow/compiler/xla/service/hlo_cost_analysis.cc b/tensorflow/compiler/xla/service/hlo_cost_analysis.cc index fdfb38b858..df7d3826db 100644 --- a/tensorflow/compiler/xla/service/hlo_cost_analysis.cc +++ b/tensorflow/compiler/xla/service/hlo_cost_analysis.cc @@ -419,6 +419,21 @@ Status HloCostAnalysis::HandleTranspose(const HloInstruction*) { } Status HloCostAnalysis::HandleAfterAll(const HloInstruction*) { + // This instruction is used to enforce ordering at compile time. No code is + // emitted. + current_should_compute_bottleneck_time_ = false; + current_properties_[kBytesAccessedKey] = 0; + current_properties_[kOptimalSecondsKey] = 0; + return Status::OK(); +} + +Status HloCostAnalysis::HandleAddDependency( + const HloInstruction* add_dependency) { + // This instruction is used to enforce ordering at compile time. No code is + // emitted. + current_should_compute_bottleneck_time_ = false; + current_properties_[kBytesAccessedKey] = 0; + current_properties_[kOptimalSecondsKey] = 0; return Status::OK(); } diff --git a/tensorflow/compiler/xla/service/hlo_cost_analysis.h b/tensorflow/compiler/xla/service/hlo_cost_analysis.h index 8ced9d776e..33983119c9 100644 --- a/tensorflow/compiler/xla/service/hlo_cost_analysis.h +++ b/tensorflow/compiler/xla/service/hlo_cost_analysis.h @@ -101,6 +101,7 @@ class HloCostAnalysis : public ConstDfsHloVisitor { Status HandleBroadcast(const HloInstruction* broadcast) override; Status HandlePad(const HloInstruction* pad) override; Status HandleReshape(const HloInstruction* reshape) override; + Status HandleAddDependency(const HloInstruction* add_dependency) override; Status HandleAfterAll(const HloInstruction* token) override; Status HandleTranspose(const HloInstruction* transpose) override; Status HandleWhile(const HloInstruction* xla_while) override; diff --git a/tensorflow/compiler/xla/service/hlo_cost_analysis_test.cc b/tensorflow/compiler/xla/service/hlo_cost_analysis_test.cc index 6a15b3440c..ff32faf298 100644 --- a/tensorflow/compiler/xla/service/hlo_cost_analysis_test.cc +++ b/tensorflow/compiler/xla/service/hlo_cost_analysis_test.cc @@ -387,7 +387,7 @@ TEST_F(FusionCostAnalysis, LoopFusion) { HloInstruction::CreateBinary(r2f32, HloOpcode::kSubtract, mul, clamp)); auto tuple = HloInstruction::CreateTuple({sub, sub, mul, c1}); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto* computation = module->AddEntryComputation(builder.Build()); auto* fusion = computation->CreateFusionInstruction( {sub, mul, exp, clamp, add}, HloInstruction::FusionKind::kLoop); @@ -429,7 +429,7 @@ TEST_F(FusionCostAnalysis, NoLayout) { auto add = builder.AddInstruction(HloInstruction::CreateBinary( shape_with_layout, HloOpcode::kAdd, c1, broadcast)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto* computation = module->AddEntryComputation(builder.Build()); auto* fusion = computation->CreateFusionInstruction( {add, broadcast}, HloInstruction::FusionKind::kLoop); @@ -472,7 +472,7 @@ TEST_F(DomainCostAnalysis, DomainCost) { auto domain = builder.AddInstruction( HloInstruction::CreateDomain(tuple->shape(), tuple, nullptr, nullptr)); - auto hlo_module = CreateNewUnverifiedModule(); + auto hlo_module = CreateNewVerifiedModule(); hlo_module->AddEntryComputation(builder.Build()); EXPECT_EQ(hlo_module->entry_computation()->root_instruction(), domain); diff --git a/tensorflow/compiler/xla/service/hlo_dataflow_analysis.cc b/tensorflow/compiler/xla/service/hlo_dataflow_analysis.cc index 5dcf6bc985..3ed3d3c11c 100644 --- a/tensorflow/compiler/xla/service/hlo_dataflow_analysis.cc +++ b/tensorflow/compiler/xla/service/hlo_dataflow_analysis.cc @@ -466,6 +466,21 @@ bool HloDataflowAnalysis::UpdateDomainValueSet(HloInstruction* domain) { return changed; } +bool HloDataflowAnalysis::UpdateAddDependencyValueSet( + HloInstruction* add_dependency) { + // AddDependency just forwards the value of its zero-th operand. + CHECK_EQ(add_dependency->opcode(), HloOpcode::kAddDependency); + const InstructionValueSet& operand_set = + GetInstructionValueSet(add_dependency->operand(0)); + InstructionValueSet& add_dependency_set = + GetInstructionValueSet(add_dependency); + if (operand_set != add_dependency_set) { + add_dependency_set = operand_set; + return true; + } + return false; +} + bool HloDataflowAnalysis::UpdateGetTupleElementValueSet(HloInstruction* gte) { CHECK_EQ(gte->opcode(), HloOpcode::kGetTupleElement); bool changed = false; @@ -622,6 +637,8 @@ bool HloDataflowAnalysis::UpdateInstructionValueSet( HloInstruction* instruction) { // Recompute from operands. switch (instruction->opcode()) { + case HloOpcode::kAddDependency: + return UpdateAddDependencyValueSet(instruction); case HloOpcode::kBitcast: return UpdateBitcastValueSet(instruction); case HloOpcode::kDomain: @@ -795,6 +812,7 @@ Status HloDataflowAnalysis::InitializeInstructionValueSets() { define_all_values(); } break; + case HloOpcode::kAddDependency: case HloOpcode::kWhile: case HloOpcode::kCall: case HloOpcode::kConditional: diff --git a/tensorflow/compiler/xla/service/hlo_dataflow_analysis.h b/tensorflow/compiler/xla/service/hlo_dataflow_analysis.h index abac398c04..ece17fc4c3 100644 --- a/tensorflow/compiler/xla/service/hlo_dataflow_analysis.h +++ b/tensorflow/compiler/xla/service/hlo_dataflow_analysis.h @@ -193,6 +193,7 @@ class HloDataflowAnalysis { bool UpdateSendValueSet(HloInstruction* send); bool UpdateTupleValueSet(HloInstruction* tuple); bool UpdateWhileValueSet(HloInstruction* xla_while); + bool UpdateAddDependencyValueSet(HloInstruction* add_dependency); // Propagate the dataflow through the module. void Propagate(); diff --git a/tensorflow/compiler/xla/service/hlo_dataflow_analysis_test.cc b/tensorflow/compiler/xla/service/hlo_dataflow_analysis_test.cc index 6422346c10..f7a1f19a6f 100644 --- a/tensorflow/compiler/xla/service/hlo_dataflow_analysis_test.cc +++ b/tensorflow/compiler/xla/service/hlo_dataflow_analysis_test.cc @@ -43,7 +43,7 @@ using ::testing::UnorderedElementsAre; class HloDataflowAnalysisTest : public HloTestBase, public ::testing::WithParamInterface { protected: - HloDataflowAnalysisTest() : module_(CreateNewUnverifiedModule()) {} + HloDataflowAnalysisTest() : module_(CreateNewVerifiedModule()) {} // Run dataflow analysis on the member module. For convenience returns a // reference to the generated analysis stored in analysis_. @@ -1877,6 +1877,30 @@ TEST_P(HloDataflowAnalysisTest, NestedConditionals) { } } +TEST_P(HloDataflowAnalysisTest, AddDependency) { + string module_string = R"( +HloModule AddDependency +ENTRY %AddDependency (p: f32[3]) -> f32[3] { + %p = f32[3] parameter(0) + %token = token[] after-all() + ROOT %add_dep = f32[3] add-dependency(f32[3] %p, token[] %token) +} +)"; + TF_ASSERT_OK_AND_ASSIGN( + std::unique_ptr module, + ParseHloString(module_string, GetModuleConfigForTest())); + + TF_ASSERT_OK_AND_ASSIGN(std::unique_ptr analysis, + HloDataflowAnalysis::Run(*module)); + const HloInstruction* root = module->entry_computation()->root_instruction(); + EXPECT_EQ(root->opcode(), HloOpcode::kAddDependency); + + // The after-all and parameter should define a value. Add-dependency should + // not. + EXPECT_EQ(analysis->values().size(), 2); + EXPECT_FALSE(analysis->ValueIsDefinedAt(root)); +} + INSTANTIATE_TEST_CASE_P(HloDataflowAnalysisInstantiation, HloDataflowAnalysisTest, ::testing::Values(false, true)); diff --git a/tensorflow/compiler/xla/service/hlo_dce_test.cc b/tensorflow/compiler/xla/service/hlo_dce_test.cc index 6c8095d397..1fa4259a3e 100644 --- a/tensorflow/compiler/xla/service/hlo_dce_test.cc +++ b/tensorflow/compiler/xla/service/hlo_dce_test.cc @@ -59,7 +59,7 @@ TEST_F(HloDceTest, NoDeadCode) { builder.AddInstruction(HloInstruction::CreateBinary( constant1->shape(), HloOpcode::kAdd, constant1, constant2)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); EXPECT_EQ(3, computation->instruction_count()); @@ -110,7 +110,7 @@ TEST_F(HloDceTest, DeadParameters) { builder.AddInstruction(HloInstruction::CreateUnary( live_param->shape(), HloOpcode::kNegate, live_param)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); EXPECT_EQ(5, computation->instruction_count()); @@ -150,7 +150,7 @@ TEST_F(HloDceTest, ControlDependencies) { builder.AddInstruction(HloInstruction::CreateBinary( constant1->shape(), HloOpcode::kAdd, constant1, constant2)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); // Add a control dependency between two instructions. @@ -175,7 +175,7 @@ TEST_F(HloDceTest, ControlDependencies) { // Tests that a dead call instruction is removed. TEST_F(HloDceTest, DeadInstructionWithCalledComputation) { - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); Shape shape = ShapeUtil::MakeShape(F32, {}); // Called computation for the call instruction. @@ -323,7 +323,7 @@ TEST_F(HloDceTest, CalledComputationWithNestedSideEffect) { } TEST_F(HloDceTest, RemoveDeadSubcomputation) { - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); HloComputation::Builder builder(TestName()); HloComputation::Builder subcomp_builder("reduction_subcomp"); diff --git a/tensorflow/compiler/xla/service/hlo_evaluator.cc b/tensorflow/compiler/xla/service/hlo_evaluator.cc index 7fcafafc09..3a7652a8dc 100644 --- a/tensorflow/compiler/xla/service/hlo_evaluator.cc +++ b/tensorflow/compiler/xla/service/hlo_evaluator.cc @@ -39,6 +39,7 @@ limitations under the License. #include "tensorflow/compiler/xla/service/hlo_query.h" #include "tensorflow/compiler/xla/service/shape_inference.h" #include "tensorflow/compiler/xla/shape_util.h" +#include "tensorflow/compiler/xla/statusor.h" #include "tensorflow/compiler/xla/types.h" #include "tensorflow/compiler/xla/util.h" #include "tensorflow/compiler/xla/window_util.h" @@ -396,6 +397,16 @@ StatusOr HloEvaluator::EvaluateDotOp( return Evaluate(cloned_instruction.get()); } +Status HloEvaluator::HandleBitcast(HloInstruction* bitcast) { + const Literal& operand_literal = GetEvaluatedLiteralFor(bitcast->operand(0)); + Literal result(bitcast->shape()); + TF_RET_CHECK(operand_literal.size_bytes() == result.size_bytes()); + memcpy(result.untyped_data(), operand_literal.untyped_data(), + operand_literal.size_bytes()); + evaluated_[bitcast] = std::move(result); + return Status::OK(); +} + Status HloEvaluator::HandleParameter(HloInstruction* parameter) { CHECK_LT(parameter->parameter_number(), arg_literals_.size()); const Literal* input_literal = arg_literals_[parameter->parameter_number()]; @@ -1046,8 +1057,15 @@ Status HloEvaluator::HandleBroadcast(HloInstruction* broadcast) { return Status::OK(); } -Status HloEvaluator::HandleAfterAll(HloInstruction* token) { - evaluated_[token] = LiteralUtil::CreateToken(); +Status HloEvaluator::HandleAfterAll(HloInstruction* after_all) { + evaluated_[after_all] = LiteralUtil::CreateToken(); + return Status::OK(); +} + +Status HloEvaluator::HandleAddDependency(HloInstruction* add_dependency) { + // AddDedendency just forwards its zero-th operand. + evaluated_[add_dependency] = + GetEvaluatedLiteralFor(add_dependency->operand(0)).Clone(); return Status::OK(); } @@ -1279,10 +1297,10 @@ StatusOr EvaluateSortInternal(HloInstruction* sort, key_value_vector.push_back( std::make_pair(keys_data[i], values_data[i])); } - std::sort(key_value_vector.begin(), key_value_vector.end(), - [](const kv_pair& a, const kv_pair& b) { - return SafeLess(a.first, b.first); - }); + std::stable_sort(key_value_vector.begin(), key_value_vector.end(), + [](const kv_pair& a, const kv_pair& b) { + return SafeLess(a.first, b.first); + }); std::vector result_keys; // We use a InlinedVector here because we need to convert it to an // absl::Span later, and this would not work with std::vector. diff --git a/tensorflow/compiler/xla/service/hlo_evaluator.h b/tensorflow/compiler/xla/service/hlo_evaluator.h index 07f8d0aad4..45ed8131dc 100644 --- a/tensorflow/compiler/xla/service/hlo_evaluator.h +++ b/tensorflow/compiler/xla/service/hlo_evaluator.h @@ -144,6 +144,8 @@ class HloEvaluator : public DfsHloVisitorWithDefault { // Operations that are type-agnostic or always return a specific type, such as // HandleIsFinite where boolean is always returned. // + Status HandleBitcast(HloInstruction* bitcast) override; + Status HandleParameter(HloInstruction* parameter) override; Status HandleConstant(HloInstruction* constant) override; @@ -180,7 +182,9 @@ class HloEvaluator : public DfsHloVisitorWithDefault { Status HandleBroadcast(HloInstruction* broadcast) override; - Status HandleAfterAll(HloInstruction* token) override; + Status HandleAfterAll(HloInstruction* after_all) override; + + Status HandleAddDependency(HloInstruction* add_dependency) override; Status HandleSort(HloInstruction* sort) override; @@ -221,16 +225,7 @@ class HloEvaluator : public DfsHloVisitorWithDefault { const Literal& operand_literal) { const auto shape = instruction->shape(); const auto* operand = instruction->operand(0); - - // TODO(b/35950897, b/27796129): add DCHECK back once implicit broadcast is - // removed. - if (!ShapeUtil::SameDimensions(shape, operand->shape())) { - return Unimplemented( - "Implicit broadcasting is currently unsupported in HLO evaluator " - "Shape Mismatch: %s vs %s", - ShapeUtil::HumanString(shape), - ShapeUtil::HumanString(operand->shape())); - } + TF_RET_CHECK(ShapeUtil::SameDimensions(shape, operand->shape())); Literal result(shape); TF_RETURN_IF_ERROR( diff --git a/tensorflow/compiler/xla/service/hlo_evaluator_test.cc b/tensorflow/compiler/xla/service/hlo_evaluator_test.cc index d95b6ad04f..4eaaab20ea 100644 --- a/tensorflow/compiler/xla/service/hlo_evaluator_test.cc +++ b/tensorflow/compiler/xla/service/hlo_evaluator_test.cc @@ -22,6 +22,7 @@ limitations under the License. #include #include "absl/memory/memory.h" +#include "absl/strings/str_format.h" #include "tensorflow/compiler/xla/client/xla_builder.h" #include "tensorflow/compiler/xla/literal.h" #include "tensorflow/compiler/xla/reference_util.h" @@ -35,6 +36,7 @@ limitations under the License. #include "tensorflow/compiler/xla/test.h" #include "tensorflow/compiler/xla/tests/hlo_test_base.h" #include "tensorflow/compiler/xla/tests/literal_test_util.h" +#include "tensorflow/compiler/xla/tests/test_utils.h" #include "tensorflow/compiler/xla/types.h" #include "tensorflow/compiler/xla/util.h" #include "tensorflow/compiler/xla/xla_data.pb.h" @@ -2765,6 +2767,33 @@ ENTRY main { EXPECT_TRUE(LiteralTestUtil::Equal(arg, actual)); } +TEST_P(HloEvaluatorTest, Bitcast) { + // Regression test for b/114735354. + constexpr absl::string_view hlo_text_base = R"( +HloModule Bitcast + +ENTRY main { + param = %s[32,121]{1,0} parameter(0) + ROOT bitcast = %s[121,32,1]{0,1,2} bitcast(%s[32,121]{1,0} param) +} +)"; + string hlo_text; + if (use_bfloat16_) { + hlo_text = absl::StrFormat(hlo_text_base, "bf16", "bf16", "bf16"); + } else { + hlo_text = absl::StrFormat(hlo_text_base, "f32", "f32", "f32"); + } + TF_ASSERT_OK_AND_ASSIGN(m_, ParseAndReturnVerifiedModule(hlo_text)); + auto args = MakeFakeArguments(m_.get()).ConsumeValueOrDie(); + Literal actual = Evaluate({&args[0]}); + if (use_bfloat16_) { + EXPECT_TRUE( + absl::c_equal(args[0].data(), actual.data())); + } else { + EXPECT_TRUE(absl::c_equal(args[0].data(), actual.data())); + } +} + INSTANTIATE_TEST_CASE_P(HloEvaluatorTest_Instantiation, HloEvaluatorTest, ::testing::ValuesIn(use_bf16_params)); diff --git a/tensorflow/compiler/xla/service/hlo_evaluator_typed_visitor.h b/tensorflow/compiler/xla/service/hlo_evaluator_typed_visitor.h index ebed875eb4..b87fc3e340 100644 --- a/tensorflow/compiler/xla/service/hlo_evaluator_typed_visitor.h +++ b/tensorflow/compiler/xla/service/hlo_evaluator_typed_visitor.h @@ -161,9 +161,6 @@ class HloEvaluatorTypedVisitor : public DfsHloVisitorWithDefault { HloOpcodeString(hlo_instruction->opcode())); } - // TODO(b/35950897): many of the stl functions used in the handlers are not - // overloaded for every XLA primitive type. - template ::value>::type* = nullptr> @@ -596,7 +593,7 @@ class HloEvaluatorTypedVisitor : public DfsHloVisitorWithDefault { return Status::OK(); } - Status HandleDivide(HloInstruction* divide) { + Status HandleDivide(HloInstruction* divide) override { return HandleDivide(divide); } @@ -1556,10 +1553,10 @@ class HloEvaluatorTypedVisitor : public DfsHloVisitorWithDefault { const auto& row_data = row_to_sort.data(); std::vector result_data(row_data.begin(), row_data.end()); - std::sort(result_data.begin(), result_data.end(), - [](const NativeT& a, const NativeT& b) { - return SafeLess(a, b); - }); + std::stable_sort(result_data.begin(), result_data.end(), + [](const NativeT& a, const NativeT& b) { + return SafeLess(a, b); + }); Literal sorted_row(ShapeUtil::MakeShape(keys->shape().element_type(), {sort_dim_elements})); sorted_row.PopulateR1(absl::Span(result_data)); @@ -2546,12 +2543,14 @@ class HloEvaluatorTypedVisitor : public DfsHloVisitorWithDefault { template ::value || - std::is_same::value || - std::is_same::value>::type* = nullptr> + std::is_integral::value || + std::is_floating_point::value>::type* = nullptr> Status HandleIota(HloInstruction* instruction) { auto* iota = Cast(instruction); - std::vector data(iota->shape().dimensions(iota->iota_dimension())); + // Avoid using std::vector since std::vector does not convert to + // absl::Span. + absl::InlinedVector data( + iota->shape().dimensions(iota->iota_dimension())); std::iota(data.begin(), data.end(), 0); auto result = LiteralUtil::CreateR1(data); @@ -2568,9 +2567,8 @@ class HloEvaluatorTypedVisitor : public DfsHloVisitorWithDefault { } template ::value || - std::is_same::value || - std::is_same::value)>::type* = nullptr> + !(std::is_integral::value || + std::is_floating_point::value)>::type* = nullptr> Status HandleIota(HloInstruction* iota) { return InvalidArgument("Unsupported type for iota"); } @@ -2722,17 +2720,8 @@ class HloEvaluatorTypedVisitor : public DfsHloVisitorWithDefault { const auto shape = instruction->shape(); const auto* lhs = instruction->operand(0); const auto* rhs = instruction->operand(1); - - // TODO(b/35950897, b/27796129): add DCHECK back once implicit broadcast - // is removed. - if (!(ShapeUtil::SameDimensions(shape, rhs->shape()) && - ShapeUtil::SameDimensions(lhs->shape(), rhs->shape()))) { - return Unimplemented( - "Implicit broadcasting is currently unsupported in HLO evaluator " - "Shape Mismatch: %s vs %s vs %s: ", - ShapeUtil::HumanString(shape), ShapeUtil::HumanString(lhs->shape()), - ShapeUtil::HumanString(rhs->shape())); - } + TF_RET_CHECK(ShapeUtil::SameDimensions(shape, rhs->shape())); + TF_RET_CHECK(ShapeUtil::SameDimensions(lhs->shape(), rhs->shape())); const Literal& lhs_literal = parent_->GetEvaluatedLiteralFor(lhs); const Literal& rhs_literal = parent_->GetEvaluatedLiteralFor(rhs); @@ -2756,19 +2745,9 @@ class HloEvaluatorTypedVisitor : public DfsHloVisitorWithDefault { const auto* lhs = instruction->operand(0); const auto* rhs = instruction->operand(1); const auto* ehs = instruction->operand(2); - - // TODO(b/35950897, b/27796129): add DCHECK back once implicit - // broadcast is removed. - if (!(ShapeUtil::SameDimensions(shape, lhs->shape()) && - ShapeUtil::SameDimensions(lhs->shape(), rhs->shape()) && - ShapeUtil::SameDimensions(rhs->shape(), ehs->shape()))) { - return Unimplemented( - "Implicit broadcasting is currently unsupported in HLO evaluator " - "Shape Mismatch: %s vs %s vs %s vs %s: ", - ShapeUtil::HumanString(shape), ShapeUtil::HumanString(lhs->shape()), - ShapeUtil::HumanString(rhs->shape()), - ShapeUtil::HumanString(ehs->shape())); - } + TF_RET_CHECK(ShapeUtil::SameDimensions(shape, lhs->shape())); + TF_RET_CHECK(ShapeUtil::SameDimensions(lhs->shape(), rhs->shape())); + TF_RET_CHECK(ShapeUtil::SameDimensions(rhs->shape(), ehs->shape())); const Literal& lhs_literal = parent_->GetEvaluatedLiteralFor(lhs); const Literal& rhs_literal = parent_->GetEvaluatedLiteralFor(rhs); diff --git a/tensorflow/compiler/xla/service/hlo_get_dimension_size_rewriter.cc b/tensorflow/compiler/xla/service/hlo_get_dimension_size_rewriter.cc new file mode 100644 index 0000000000..c919dbd82d --- /dev/null +++ b/tensorflow/compiler/xla/service/hlo_get_dimension_size_rewriter.cc @@ -0,0 +1,61 @@ +/* Copyright 2018 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/compiler/xla/service/hlo_get_dimension_size_rewriter.h" + +#include "absl/algorithm/container.h" +#include "tensorflow/compiler/xla/literal_util.h" +#include "tensorflow/compiler/xla/service/hlo_instruction.h" +#include "tensorflow/compiler/xla/service/hlo_opcode.h" +#include "tensorflow/compiler/xla/service/shape_inference.h" + +namespace xla { + +namespace { + +StatusOr ReplaceGetSize(HloInstruction* instr) { + if (instr->opcode() != HloOpcode::kGetDimensionSize) { + return false; + } + HloComputation* computation = instr->parent(); + + TF_ASSIGN_OR_RETURN(auto legal_shape, + ShapeInference::InferGetDimensionSizeShape( + instr->operand(0)->shape(), instr->dimension())); + TF_RET_CHECK(ShapeUtil::Equal(instr->shape(), legal_shape)); + TF_RET_CHECK(ShapeUtil::HasPrimitiveType(instr->shape(), U32)); + uint32 size = instr->operand(0)->shape().dimensions(instr->dimension()); + HloInstruction* new_instr = computation->AddInstruction( + HloInstruction::CreateConstant(LiteralUtil::CreateR0(size))); + TF_RETURN_IF_ERROR(instr->ReplaceAllUsesWith(new_instr)); + return true; +} + +} // namespace + +StatusOr HloGetDimensionSizeRewriter::Run(HloModule* module) { + bool changed = false; + HloProto proto; + *proto.mutable_hlo_module() = module->ToProto(); + for (auto* computation : module->computations()) { + for (auto instruction : computation->instructions()) { + TF_ASSIGN_OR_RETURN(bool replaced, ReplaceGetSize(instruction)); + changed = changed || replaced; + } + } + return changed; +} + +} // namespace xla diff --git a/tensorflow/compiler/xla/service/hlo_get_dimension_size_rewriter.h b/tensorflow/compiler/xla/service/hlo_get_dimension_size_rewriter.h new file mode 100644 index 0000000000..30f44c23a8 --- /dev/null +++ b/tensorflow/compiler/xla/service/hlo_get_dimension_size_rewriter.h @@ -0,0 +1,36 @@ +/* Copyright 2018 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_COMPILER_XLA_SERVICE_HLO_GET_DIMENSION_SIZE_REWRITER_H_ +#define TENSORFLOW_COMPILER_XLA_SERVICE_HLO_GET_DIMENSION_SIZE_REWRITER_H_ + +#include "tensorflow/compiler/xla/service/hlo_module.h" +#include "tensorflow/compiler/xla/service/hlo_pass_interface.h" + +namespace xla { + +// Pass to replace a kGetDimensionSize instruction with a constant instruction. +class HloGetDimensionSizeRewriter : public HloModulePass { + public: + absl::string_view name() const override { + return "hlo-get-dimension-size-rewriter"; + } + + StatusOr Run(HloModule* module) override; +}; + +} // namespace xla + +#endif // TENSORFLOW_COMPILER_XLA_SERVICE_HLO_GET_DIMENSION_SIZE_REWRITER_H_ diff --git a/tensorflow/compiler/xla/service/hlo_get_dimension_size_rewriter_test.cc b/tensorflow/compiler/xla/service/hlo_get_dimension_size_rewriter_test.cc new file mode 100644 index 0000000000..a86aebdd5b --- /dev/null +++ b/tensorflow/compiler/xla/service/hlo_get_dimension_size_rewriter_test.cc @@ -0,0 +1,83 @@ +/* Copyright 2018 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/compiler/xla/service/hlo_get_dimension_size_rewriter.h" + +#include "tensorflow/compiler/xla/service/hlo_computation.h" +#include "tensorflow/compiler/xla/service/hlo_instruction.h" +#include "tensorflow/compiler/xla/service/hlo_matchers.h" +#include "tensorflow/compiler/xla/service/hlo_module.h" +#include "tensorflow/compiler/xla/service/hlo_opcode.h" +#include "tensorflow/compiler/xla/service/hlo_parser.h" +#include "tensorflow/compiler/xla/shape_util.h" +#include "tensorflow/compiler/xla/tests/hlo_test_base.h" +#include "tensorflow/compiler/xla/tests/literal_test_util.h" +#include "tensorflow/compiler/xla/tests/test_utils.h" +#include "tensorflow/compiler/xla/types.h" +#include "tensorflow/core/lib/core/status_test_util.h" +#include "tensorflow/core/platform/types.h" + +namespace xla { +namespace { + +namespace op = xla::testing::opcode_matchers; + +class HloGetDimensionSizeRewriterTest : public HloTestBase { + protected: + HloGetDimensionSizeRewriterTest() {} +}; + +TEST_F(HloGetDimensionSizeRewriterTest, Ok) { + auto module = ParseHloString(R"( +HloModule _ +ENTRY gds { + p = s32[3,4] parameter(0) + size0 = u32[] get-dimension-size(p), dimensions={0} + size1 = u32[] get-dimension-size(p), dimensions={1} + ROOT mul = u32[] multiply(size0, size1) +})") + .ValueOrDie(); + HloGetDimensionSizeRewriter pass; + EXPECT_TRUE(pass.Run(module.get()).ValueOrDie()); + EXPECT_THAT(module->entry_computation()->root_instruction(), + op::Multiply(op::Constant(), op::Constant())); +} + +TEST_F(HloGetDimensionSizeRewriterTest, IllegalType) { + auto module = ParseHloString(R"( +HloModule _ +ENTRY gds { + p = s32[3]{0} parameter(0) + ROOT gds = s64[] get-dimension-size(p), dimensions={0} +})") + .ValueOrDie(); + HloGetDimensionSizeRewriter pass; + EXPECT_FALSE(pass.Run(module.get()).ok()); +} + +TEST_F(HloGetDimensionSizeRewriterTest, IllegalDimension) { + auto module = ParseHloString(R"( +HloModule _ +ENTRY gds { + p = f32[2,5] parameter(0) + ROOT gds = u32[] get-dimension-size(p), dimensions={2} +})") + .ValueOrDie(); + HloGetDimensionSizeRewriter pass; + EXPECT_FALSE(pass.Run(module.get()).ok()); +} + +} // namespace +} // namespace xla diff --git a/tensorflow/compiler/xla/service/hlo_graph_dumper.cc b/tensorflow/compiler/xla/service/hlo_graph_dumper.cc index 05cc1593e4..302eca656b 100644 --- a/tensorflow/compiler/xla/service/hlo_graph_dumper.cc +++ b/tensorflow/compiler/xla/service/hlo_graph_dumper.cc @@ -21,6 +21,7 @@ limitations under the License. #include #include #include +#include #include #include #include @@ -38,6 +39,7 @@ limitations under the License. #include "tensorflow/compiler/xla/service/hlo_instructions.h" #include "tensorflow/compiler/xla/service/hlo_module.h" #include "tensorflow/compiler/xla/service/hlo_tfgraph_builder.h" +#include "tensorflow/compiler/xla/service/pattern_matcher.h" #include "tensorflow/compiler/xla/shape_util.h" #include "tensorflow/compiler/xla/types.h" #include "tensorflow/compiler/xla/window_util.h" @@ -111,11 +113,6 @@ class NodeFilter { result == kSomeUsersOmitted; } - bool ShowFusionSubcomputation(const HloInstruction* instr) const { - CHECK_EQ(instr->opcode(), HloOpcode::kFusion); - return Show(instr) && !SomeOrAllOperandsOmitted(instr); - } - private: std::function filter_; }; @@ -240,34 +237,28 @@ string HtmlLikeStringSanitize(absl::string_view s) { // it to a short string lets us tell the user what the subcomputation is without // drawing it as a graph. optional MatchTrivialComputation(const HloComputation* computation) { + namespace m = match; + if (computation->instruction_count() != 3) { return nullopt; } - HloInstruction* root = computation->root_instruction(); - if (root->operand_count() != 2) { - return nullopt; - } - - // Check that both of the operands to the root are parameters. - const HloInstruction* operand0 = root->operand(0); - const HloInstruction* operand1 = root->operand(1); - if (operand0->opcode() != HloOpcode::kParameter || - operand1->opcode() != HloOpcode::kParameter) { - return nullopt; - } - - // Check that the two operands of root are param0 and param1. All of the - // opcodes we recognize are commutative, so we're OK with either order. - auto n0 = operand0->parameter_number(); - auto n1 = operand1->parameter_number(); - if (!(n0 == 0 && n1 == 1) && !(n1 == 0 && n0 == 1)) { + const HloInstruction *param0, *param1; + if (!Match(root, m::Op() + .WithNumOperands(2) + .WithShape(m::Shape().IsEffectiveScalar()) + .WithBinaryOperandsAnyOrder( + m::Parameter(¶m0, 0) + .WithShape(m::Shape().IsEffectiveScalar()), + m::Parameter(¶m1, 1) + .WithShape(m::Shape().IsEffectiveScalar())))) { return nullopt; } - // If the params are reversed, check that the operation being performed is - // commutative. - if (n0 == 1) { + // If the params are reversed (i.e. operand0 is param1 and operand1 is + // param0), check that the operation being performed is commutative. + if (root->operand(0) == param1) { + CHECK_EQ(root->operand(1), param0); switch (root->opcode()) { case HloOpcode::kLe: case HloOpcode::kGe: @@ -279,13 +270,6 @@ optional MatchTrivialComputation(const HloComputation* computation) { } } - // Check that the root and params are all effective scalars. - if (!ShapeUtil::IsEffectiveScalar(root->shape()) || - !ShapeUtil::IsEffectiveScalar(operand0->shape()) || - !ShapeUtil::IsEffectiveScalar(operand1->shape())) { - return nullopt; - } - // If we recognize the root's opcode, we've successfully pattern-matched! switch (root->opcode()) { case HloOpcode::kAdd: @@ -578,7 +562,7 @@ bool HloDotDumper::ShouldShowSubcomputation(const HloComputation* subcomp) { // Show the subcomputation if we're showing any of its members. return std::any_of( - computation_->instructions().begin(), computation_->instructions().end(), + subcomp->instructions().begin(), subcomp->instructions().end(), [&](const HloInstruction* instr) { return filter_.Show(instr); }); } @@ -987,6 +971,7 @@ ColorScheme HloDotDumper::GetInstructionColor(const HloInstruction* instr) { case HloOpcode::kGetTupleElement: case HloOpcode::kTrace: case HloOpcode::kAfterAll: + case HloOpcode::kAddDependency: case HloOpcode::kTuple: return kWhite; case HloOpcode::kBroadcast: @@ -1267,12 +1252,12 @@ const HloInstruction* HloDotDumper::GetNodeForEdge( class GraphRendererRegistry { public: - void AddRenderer(GraphRendererInterface* graph_renderer) { + void SetRenderer(std::shared_ptr graph_renderer) { tensorflow::mutex_lock lock(mu_); graph_renderer_ = graph_renderer; } - GraphRendererInterface* GetDefaultRenderer() { + std::shared_ptr GetDefaultRenderer() { tensorflow::mutex_lock lock(mu_); return graph_renderer_; } @@ -1284,20 +1269,21 @@ class GraphRendererRegistry { private: tensorflow::mutex mu_; - GraphRendererInterface* graph_renderer_ = nullptr; + std::shared_ptr graph_renderer_ GUARDED_BY(mu_); }; } // namespace -Registrar::Registrar(GraphRendererInterface* dumper) { - GraphRendererRegistry::Default()->AddRenderer(dumper); +Registrar::Registrar(std::shared_ptr dumper) { + GraphRendererRegistry::Default()->SetRenderer(dumper); } namespace { // Gets a NodeFilter that includes roughly all instructions whose distance from // root is <= radius. -NodeFilter MakeNodeFilter(const HloInstruction* root, int64 radius) { +NodeFilter MakeNodeRadiusAroundFilter(const HloInstruction* root, + int64 radius) { // First, find the neighborhood of nodes with distance from root <= radius. // These nodes are our initial set of "normal" nodes. std::unordered_map nodes; @@ -1404,6 +1390,56 @@ NodeFilter MakeNodeFilter(const HloInstruction* root, int64 radius) { }); } +// Gets a node filter that includes nodes on all paths from `from` to `to`. If +// the all-paths set contains more than max_nodes elements, includes the nodes +// on the shortest paths and sets hit_limit to true. +NodeFilter MakeNodeFromToFilter(const HloInstruction* from, + const HloInstruction* to, int64 max_nodes, + bool* hit_limit) { + *hit_limit = false; + + // Elements in the queue are paths through the graph. + std::deque> queue; + queue.push_front({from}); + + // Compute the set of nodes we want to show using a slightly-modified + // Djikstra's algorithm. The only real difference is, rather than stopping + // when we find a (shortest) path, we continue until we've found max_nodes + // nodes on some path. + std::unordered_set visited; + std::unordered_set to_display = {from, to}; + while (!queue.empty() && to_display.size() < max_nodes) { + std::vector path = std::move(queue.front()); + queue.pop_front(); + if (!visited.insert(path.back()).second) { + continue; + } + + for (const auto* user : path.back()->users()) { + if (user == to) { + auto it = path.begin(); + for (; it != path.end() && to_display.size() < max_nodes; ++it) { + to_display.insert(*it); + } + if (it != path.end()) { + *hit_limit = true; + } + } else if (!visited.count(user)) { + auto new_path = path; + new_path.push_back(user); + queue.push_back(std::move(new_path)); + } + } + } + + return NodeFilter([=](const HloInstruction* instr) { + if (instr == from || instr == to) { + return kHighlightNode; + } + return to_display.count(instr) ? kNormalNode : kHideNode; + }); +} + string SaveGraph(const string& graph, GraphRendererInterface::GraphKind graph_kind, const string& dest_path) { @@ -1483,7 +1519,7 @@ string DumpNeighborhoodAround(const HloInstruction& node, int radius, auto debug_options = node.GetModule()->config().debug_options(); string label = StrCat("Neighborhood of ", radius, " nodes around ", node.name()); - NodeFilter filter = MakeNodeFilter(&node, radius); + NodeFilter filter = MakeNodeRadiusAroundFilter(&node, radius); string graph = HloDotDumper(node.parent(), label, debug_options, show_backend_config, /*profile=*/nullptr, filter) @@ -1491,6 +1527,29 @@ string DumpNeighborhoodAround(const HloInstruction& node, int radius, return ExportGraph(graph, GraphRendererInterface::DOT_GRAPH, debug_options); } +string DumpAllPathsFromTo(const HloInstruction& from, const HloInstruction& to, + int64 max_nodes, bool show_backend_config) { + CHECK_EQ(from.parent(), to.parent()) << "Nodes must be in same computation!"; + auto debug_options = from.GetModule()->config().debug_options(); + + bool hit_limit = false; + NodeFilter filter = MakeNodeFromToFilter(&from, &to, max_nodes, &hit_limit); + string label; + if (!hit_limit) { + label = StrCat("All paths from ", from.name(), " to ", to.name()); + } else { + label = StrCat(max_nodes, " nodes on the shortest paths from ", from.name(), + " to ", to.name(), + "

***SHOWING ONLY A SUBSET OF ALL PATHS BETWEEN " + "NODES***

"); + } + string graph = + HloDotDumper(from.parent(), label, debug_options, show_backend_config, + /*profile=*/nullptr, filter) + .Dump(); + return ExportGraph(graph, GraphRendererInterface::DOT_GRAPH, debug_options); +} + void DumpText(const HloModule& module, const string& label, const string& directory_path, bool do_prefix) { Env* env = Env::Default(); diff --git a/tensorflow/compiler/xla/service/hlo_graph_dumper.h b/tensorflow/compiler/xla/service/hlo_graph_dumper.h index 0b11f34abb..de1eefab77 100644 --- a/tensorflow/compiler/xla/service/hlo_graph_dumper.h +++ b/tensorflow/compiler/xla/service/hlo_graph_dumper.h @@ -66,6 +66,12 @@ string DumpGraph(const HloComputation& computation, const string& label, string DumpNeighborhoodAround(const HloInstruction& node, int radius, bool show_backend_config = false); +// Dumps nodes on any of the paths from `from` to `to`. If there are more than +// max_nodes on all paths, restricts to the max_nodes nodes on the shortest +// paths. +string DumpAllPathsFromTo(const HloInstruction& from, const HloInstruction& to, + int64 max_nodes, bool show_backend_config = false); + // Dumps the HloModule::ToString() as a file into the provided directory path // suffixed with the provided label. // @@ -87,13 +93,13 @@ void DumpText(const HloModule& module, const string& label, // Class that registers a graph renderer. class Registrar { public: - Registrar(GraphRendererInterface* dumper); + Registrar(std::shared_ptr dumper); }; -#define XLA_INTERNAL_REGISTER_GRAPH_RENDERER(factory, ctr, ...) \ - static ::xla::hlo_graph_dumper::Registrar \ - XLA_INTERNAL_REGISTER_GRAPH_RENDERER_NAME(ctr)(new factory, \ - ##__VA_ARGS__) +#define XLA_INTERNAL_REGISTER_GRAPH_RENDERER(factory, ctr, ...) \ + static ::xla::hlo_graph_dumper::Registrar \ + XLA_INTERNAL_REGISTER_GRAPH_RENDERER_NAME(ctr)( \ + std::make_shared(), ##__VA_ARGS__) // __COUNTER__ must go through another macro to be properly expanded #define XLA_INTERNAL_REGISTER_GRAPH_RENDERER_NAME(ctr) ___##ctr##__object_ diff --git a/tensorflow/compiler/xla/service/hlo_instruction.cc b/tensorflow/compiler/xla/service/hlo_instruction.cc index 26786ee950..21b1dbc167 100644 --- a/tensorflow/compiler/xla/service/hlo_instruction.cc +++ b/tensorflow/compiler/xla/service/hlo_instruction.cc @@ -93,7 +93,8 @@ StatusOr> HloInstruction::CreateFromProto( [&computation_map](int64 id) { return computation_map.contains(id); })) << proto.name() << " instruction references invalid computation id(s)"; - TF_RETURN_IF_ERROR(ShapeUtil::ValidateShapeWithOptionalLayout(proto.shape())); + Shape shape(proto.shape()); + TF_RETURN_IF_ERROR(ShapeUtil::ValidateShapeWithOptionalLayout(shape)); switch (opcode) { // Ops migrated to subclasses. @@ -101,23 +102,23 @@ StatusOr> HloInstruction::CreateFromProto( TF_RET_CHECK(proto.operand_ids_size() == 3) << "BatchNormTraining instruction should have 3 operands but sees " << proto.operand_ids_size(); - instruction = CreateBatchNormTraining( - proto.shape(), operands(0), operands(1), operands(2), proto.epsilon(), - proto.feature_index()); + instruction = + CreateBatchNormTraining(shape, operands(0), operands(1), operands(2), + proto.epsilon(), proto.feature_index()); break; case HloOpcode::kBatchNormInference: TF_RET_CHECK(proto.operand_ids_size() == 5) << "BatchNormInference instruction should have 5 operands but sees " << proto.operand_ids_size(); instruction = CreateBatchNormInference( - proto.shape(), operands(0), operands(1), operands(2), operands(3), + shape, operands(0), operands(1), operands(2), operands(3), operands(4), proto.epsilon(), proto.feature_index()); break; case HloOpcode::kBatchNormGrad: TF_RET_CHECK(proto.operand_ids_size() == 5) << "BatchNormGrad instruction should have 5 operands but sees " << proto.operand_ids_size(); - instruction = CreateBatchNormGrad(proto.shape(), operands(0), operands(1), + instruction = CreateBatchNormGrad(shape, operands(0), operands(1), operands(2), operands(3), operands(4), proto.epsilon(), proto.feature_index()); break; @@ -127,7 +128,7 @@ StatusOr> HloInstruction::CreateFromProto( << proto.operand_ids_size(); std::vector fft_length(proto.fft_length().begin(), proto.fft_length().end()); - instruction = CreateFft(proto.shape(), operands(0), proto.fft_type(), + instruction = CreateFft(shape, operands(0), proto.fft_type(), absl::Span(fft_length)); break; } @@ -148,7 +149,7 @@ StatusOr> HloInstruction::CreateFromProto( TF_RET_CHECK(proto.operand_ids_size() == 1) << "Recv instruction should have 1 operand but sees " << proto.operand_ids_size(); - instruction = CreateRecv(proto.shape().tuple_shapes(0), operands(0), + instruction = CreateRecv(shape.tuple_shapes(0), operands(0), proto.channel_id(), proto.is_host_transfer()); break; case HloOpcode::kRecvDone: @@ -161,7 +162,7 @@ StatusOr> HloInstruction::CreateFromProto( TF_RET_CHECK(proto.operand_ids_size() == 1) << "Reverse instruction should have 1 operand but sees " << proto.operand_ids_size(); - instruction = CreateReverse(proto.shape(), operands(0), + instruction = CreateReverse(shape, operands(0), std::vector(proto.dimensions().begin(), proto.dimensions().end())); break; @@ -170,7 +171,7 @@ StatusOr> HloInstruction::CreateFromProto( << "Concatenate instruction should have 1 dimension but sees " << proto.dimensions_size(); instruction = - CreateConcatenate(proto.shape(), all_operands(), proto.dimensions(0)); + CreateConcatenate(shape, all_operands(), proto.dimensions(0)); break; case HloOpcode::kReduce: TF_RET_CHECK(proto.operand_ids_size() % 2 == 0) @@ -188,7 +189,7 @@ StatusOr> HloInstruction::CreateFromProto( absl::MakeSpan(reduce_operands) .subspan(reduce_operands.size() / 2, reduce_operands.size()); instruction = - CreateReduce(proto.shape(), inputs, init_values, + CreateReduce(shape, inputs, init_values, std::vector(proto.dimensions().begin(), proto.dimensions().end()), computations(0)); @@ -203,7 +204,7 @@ StatusOr> HloInstruction::CreateFromProto( auto sort_operands = all_operands(); HloInstruction* keys = sort_operands[0]; instruction = CreateSort( - proto.shape(), proto.dimensions(0), keys, + shape, proto.dimensions(0), keys, absl::Span(sort_operands).subspan(1)); break; } @@ -212,7 +213,7 @@ StatusOr> HloInstruction::CreateFromProto( << "Transpose instruction should have 1 operand but sees " << proto.operand_ids_size(); instruction = - CreateTranspose(proto.shape(), operands(0), + CreateTranspose(shape, operands(0), std::vector(proto.dimensions().begin(), proto.dimensions().end())); break; @@ -221,7 +222,7 @@ StatusOr> HloInstruction::CreateFromProto( << "Broadcast instruction should have 1 operand but sees " << proto.operand_ids_size(); instruction = - CreateBroadcast(proto.shape(), operands(0), + CreateBroadcast(shape, operands(0), std::vector(proto.dimensions().begin(), proto.dimensions().end())); break; @@ -229,7 +230,7 @@ StatusOr> HloInstruction::CreateFromProto( TF_RET_CHECK(proto.called_computation_ids_size() == 1) << "Map instruction should have 1 called computation but sees " << proto.called_computation_ids_size(); - instruction = CreateMap(proto.shape(), all_operands(), computations(0)); + instruction = CreateMap(shape, all_operands(), computations(0)); break; case HloOpcode::kSlice: { TF_RET_CHECK(proto.operand_ids_size() == 1) @@ -242,8 +243,8 @@ StatusOr> HloInstruction::CreateFromProto( slice_limits.push_back(slice_dimensions.limit()); slice_strides.push_back(slice_dimensions.stride()); } - instruction = CreateSlice(proto.shape(), operands(0), slice_starts, - slice_limits, slice_strides); + instruction = CreateSlice(shape, operands(0), slice_starts, slice_limits, + slice_strides); break; } case HloOpcode::kConstant: { @@ -253,7 +254,7 @@ StatusOr> HloInstruction::CreateFromProto( Literal::CreateFromProto(proto.literal())); instruction = CreateConstant(std::move(literal)); } else { - instruction = absl::make_unique(proto.shape()); + instruction = absl::make_unique(shape); } break; } @@ -284,55 +285,54 @@ StatusOr> HloInstruction::CreateFromProto( tensorflow::gtl::FindPtrOrNull(computation_map, fusion_id); TF_RET_CHECK(fused_computation != nullptr) << "No fusion computation with id " << fusion_id; - instruction = CreateFusion(proto.shape(), fusion_kind, all_operands(), - fused_computation); + instruction = + CreateFusion(shape, fusion_kind, all_operands(), fused_computation); break; } case HloOpcode::kRng: - instruction = - CreateRng(proto.shape(), proto.distribution(), all_operands()); + instruction = CreateRng(shape, proto.distribution(), all_operands()); break; case HloOpcode::kParameter: - instruction = CreateParameter(proto.parameter_number(), proto.shape(), - proto.name()); + instruction = + CreateParameter(proto.parameter_number(), shape, proto.name()); break; case HloOpcode::kGetTupleElement: TF_RET_CHECK(proto.operand_ids_size() == 1) << "GetTupleElement instruction should have 1 operand but sees " << proto.operand_ids_size(); - instruction = CreateGetTupleElement(proto.shape(), operands(0), - proto.tuple_index()); + instruction = + CreateGetTupleElement(shape, operands(0), proto.tuple_index()); break; case HloOpcode::kReducePrecision: TF_RET_CHECK(proto.operand_ids_size() == 1) << "ReducePrecision instruction should have 1 operand but sees " << proto.operand_ids_size(); - instruction = - CreateReducePrecision(proto.shape(), operands(0), - proto.exponent_bits(), proto.mantissa_bits()); + instruction = CreateReducePrecision( + shape, operands(0), proto.exponent_bits(), proto.mantissa_bits()); break; case HloOpcode::kInfeed: { - TF_RET_CHECK(ShapeUtil::IsTuple(proto.shape()) && - (ShapeUtil::TupleElementCount(proto.shape()) == 2)) + TF_RET_CHECK(ShapeUtil::IsTuple(shape) && + (ShapeUtil::TupleElementCount(shape) == 2)) << "Infeed should have a tuple shape with 2 operands, but has: " - << proto.shape(); - const Shape& data_shape = - ShapeUtil::GetTupleElementShape(proto.shape(), 0); + << shape; + const Shape& data_shape = ShapeUtil::GetTupleElementShape(shape, 0); TF_RET_CHECK(proto.operand_ids_size() == 1) << "Infeed instruction should have 1 operand but sees " << proto.operand_ids_size(); instruction = CreateInfeed(data_shape, operands(0), proto.infeed_config()); } break; - case HloOpcode::kOutfeed: + case HloOpcode::kOutfeed: { TF_RET_CHECK(proto.operand_ids_size() == 2) << "Outfeed instruction should have 2 operands but sees " << proto.operand_ids_size(); + Shape outfeed_shape(proto.outfeed_shape()); TF_RETURN_IF_ERROR( - ShapeUtil::ValidateShapeWithOptionalLayout(proto.outfeed_shape())); - instruction = CreateOutfeed(proto.outfeed_shape(), operands(0), - operands(1), proto.outfeed_config()); + ShapeUtil::ValidateShapeWithOptionalLayout(outfeed_shape)); + instruction = CreateOutfeed(outfeed_shape, operands(0), operands(1), + proto.outfeed_config()); break; + } case HloOpcode::kCrossReplicaSum: { TF_RET_CHECK(proto.called_computation_ids_size() == 1) << "CrossReplicaSum should have 1 called computation but sees " @@ -342,7 +342,7 @@ StatusOr> HloInstruction::CreateFromProto( all_reduce_id = proto.all_reduce_id(); } instruction = CreateCrossReplicaSum( - proto.shape(), all_operands(), computations(0), + shape, all_operands(), computations(0), /*replica_groups=*/ std::vector(proto.replica_groups().begin(), proto.replica_groups().end()), @@ -352,7 +352,7 @@ StatusOr> HloInstruction::CreateFromProto( } case HloOpcode::kAllToAll: { instruction = CreateAllToAll( - proto.shape(), all_operands(), + shape, all_operands(), /*replica_groups=*/ std::vector(proto.replica_groups().begin(), proto.replica_groups().end())); @@ -368,8 +368,8 @@ StatusOr> HloInstruction::CreateFromProto( source_target_pairs[i].first = proto.source_target_pairs(i).source(); source_target_pairs[i].second = proto.source_target_pairs(i).target(); } - instruction = CreateCollectivePermute(proto.shape(), operands(0), - source_target_pairs); + instruction = + CreateCollectivePermute(shape, operands(0), source_target_pairs); break; } case HloOpcode::kConvolution: { @@ -382,7 +382,7 @@ StatusOr> HloInstruction::CreateFromProto( precision_config.mutable_operand_precision()->Resize( proto.operand_ids_size(), PrecisionConfig::DEFAULT); instruction = CreateConvolve( - proto.shape(), operands(0), operands(1), + shape, operands(0), operands(1), std::max(proto.feature_group_count(), 1), proto.window(), proto.convolution_dimension_numbers(), precision_config); break; @@ -394,7 +394,7 @@ StatusOr> HloInstruction::CreateFromProto( TF_RET_CHECK(proto.called_computation_ids_size() == 1) << "ReduceWindow should have 1 called computation but sees " << proto.called_computation_ids_size(); - instruction = CreateReduceWindow(proto.shape(), operands(0), operands(1), + instruction = CreateReduceWindow(shape, operands(0), operands(1), proto.window(), computations(0)); break; case HloOpcode::kSelectAndScatter: @@ -404,9 +404,9 @@ StatusOr> HloInstruction::CreateFromProto( TF_RET_CHECK(proto.called_computation_ids_size() == 2) << "SelectAndScatter should have 2 called computations but sees " << proto.called_computation_ids_size(); - instruction = CreateSelectAndScatter( - proto.shape(), operands(0), computations(0), proto.window(), - operands(1), operands(2), computations(1)); + instruction = CreateSelectAndScatter(shape, operands(0), computations(0), + proto.window(), operands(1), + operands(2), computations(1)); break; case HloOpcode::kCustomCall: if (proto.constrain_layout()) { @@ -414,16 +414,17 @@ StatusOr> HloInstruction::CreateFromProto( // vector of pointers essentially) so create a vector of shapes to pass // in. std::vector operand_shapes; - for (const Shape& shape : proto.operand_shapes_with_layout()) { - operand_shapes.push_back(shape); + for (const ShapeProto& shape_proto : + proto.operand_shapes_with_layout()) { + operand_shapes.emplace_back(shape_proto); } - instruction = CreateCustomCall( - proto.shape(), all_operands(), proto.custom_call_target(), - operand_shapes, proto.custom_call_opaque()); + instruction = + CreateCustomCall(shape, all_operands(), proto.custom_call_target(), + operand_shapes, proto.custom_call_opaque()); } else { - instruction = CreateCustomCall(proto.shape(), all_operands(), - proto.custom_call_target(), - proto.custom_call_opaque()); + instruction = + CreateCustomCall(shape, all_operands(), proto.custom_call_target(), + proto.custom_call_opaque()); } if (proto.has_window()) { static_cast(instruction.get()) @@ -443,8 +444,8 @@ StatusOr> HloInstruction::CreateFromProto( << "Pad instruction should have 2 operands but sees " << proto.operand_ids_size(); TF_RET_CHECK(proto.has_padding_config()); - instruction = CreatePad(proto.shape(), operands(0), operands(1), - proto.padding_config()); + instruction = + CreatePad(shape, operands(0), operands(1), proto.padding_config()); break; case HloOpcode::kDynamicSlice: { TF_RET_CHECK(proto.operand_ids_size() == 2) @@ -452,8 +453,8 @@ StatusOr> HloInstruction::CreateFromProto( << proto.operand_ids_size(); std::vector slice_sizes(proto.dynamic_slice_sizes_size()); absl::c_copy(proto.dynamic_slice_sizes(), slice_sizes.begin()); - instruction = CreateDynamicSlice(proto.shape(), operands(0), operands(1), - slice_sizes); + instruction = + CreateDynamicSlice(shape, operands(0), operands(1), slice_sizes); break; } case HloOpcode::kGather: { @@ -469,7 +470,7 @@ StatusOr> HloInstruction::CreateFromProto( for (int64 bound : proto.gather_slice_sizes()) { gather_slice_sizes.push_back(bound); } - instruction = CreateGather(proto.shape(), operands(0), operands(1), + instruction = CreateGather(shape, operands(0), operands(1), *gather_dimension_numbers, gather_slice_sizes); break; } @@ -485,16 +486,15 @@ StatusOr> HloInstruction::CreateFromProto( auto scatter_dimension_numbers = absl::make_unique( proto.scatter_dimension_numbers()); - instruction = - CreateScatter(proto.shape(), operands(0), operands(1), operands(2), - computations(0), *scatter_dimension_numbers); + instruction = CreateScatter(shape, operands(0), operands(1), operands(2), + computations(0), *scatter_dimension_numbers); break; } case HloOpcode::kIota: TF_RET_CHECK(proto.dimensions_size() == 1) << "Iota instruction should have 1 dimension but sees " << proto.dimensions_size(); - instruction = CreateIota(proto.shape(), proto.dimensions(0)); + instruction = CreateIota(shape, proto.dimensions(0)); break; case HloOpcode::kDot: { TF_RET_CHECK(proto.has_dot_dimension_numbers()) @@ -506,8 +506,8 @@ StatusOr> HloInstruction::CreateFromProto( precision_config.mutable_operand_precision()->Resize( proto.operand_ids_size(), PrecisionConfig::DEFAULT); instruction = absl::make_unique( - proto.shape(), operands(0), operands(1), - proto.dot_dimension_numbers(), precision_config); + shape, operands(0), operands(1), proto.dot_dimension_numbers(), + precision_config); break; } case HloOpcode::kDomain: { @@ -529,7 +529,7 @@ StatusOr> HloInstruction::CreateFromProto( exit_hlo_sharding = std::make_shared(sharding); } instruction = absl::make_unique( - proto.shape(), operands(0), + shape, operands(0), absl::make_unique(entry_hlo_sharding), absl::make_unique(exit_hlo_sharding)); break; @@ -537,11 +537,11 @@ StatusOr> HloInstruction::CreateFromProto( case HloOpcode::kGetDimensionSize: TF_RET_CHECK(proto.operand_ids_size() == 1); TF_RET_CHECK(proto.dimensions_size() == 1); - instruction = CreateGetDimensionSize(proto.shape(), operands(0), - proto.dimensions(0)); + instruction = + CreateGetDimensionSize(shape, operands(0), proto.dimensions(0)); break; default: { - instruction = absl::WrapUnique(new HloInstruction(opcode, proto.shape())); + instruction = absl::WrapUnique(new HloInstruction(opcode, shape)); for (const int64 operand_id : proto.operand_ids()) { instruction->AppendOperand(instruction_map.at(operand_id)); } @@ -855,6 +855,16 @@ HloInstruction::CreateCollectivePermute( new HloInstruction(HloOpcode::kAfterAll, ShapeUtil::MakeTokenShape())); } +/* static */ std::unique_ptr +HloInstruction::CreateAddDependency(HloInstruction* data_operand, + HloInstruction* token_operand) { + auto instruction = absl::WrapUnique( + new HloInstruction(HloOpcode::kAddDependency, data_operand->shape())); + instruction->AppendOperand(data_operand); + instruction->AppendOperand(token_operand); + return instruction; +} + /* static */ std::unique_ptr HloInstruction::CreateWhile( const Shape& shape, HloComputation* condition, HloComputation* body, HloInstruction* init) { @@ -1394,6 +1404,10 @@ std::unique_ptr HloInstruction::CloneWithNewOperands( clone = CreateAfterAll(new_operands); } break; + case HloOpcode::kAddDependency: + CHECK_EQ(new_operands.size(), 2); + clone = CreateAddDependency(new_operands[0], new_operands[1]); + break; } // SetupDerivedInstruction will setup the precision_config_ field. SetupDerivedInstruction(clone.get()); @@ -1680,6 +1694,7 @@ bool HloInstruction::IdenticalSlowPath( // This opcode has complex or special behavior so just return false. case HloOpcode::kAfterAll: + case HloOpcode::kAddDependency: return false; // Remaining instructions with special values. @@ -1745,6 +1760,26 @@ bool HloInstruction::IdenticalSlowPath( return false; } +uint64 HloInstruction::Hash() const { + using tensorflow::Hash64Combine; + + uint64 hash_value = Hash64Combine(0, static_cast(opcode())); + hash_value = Hash64Combine(hash_value, ShapeUtil::Hash(shape())); + + if (!IsCrossModuleAllReduce()) { + if (!operands().empty()) { + for (size_t i = 0; i < operands().size(); ++i) { + hash_value = Hash64Combine(hash_value, operand(i)->Hash()); + } + } + } + + hash_value = Hash64Combine(hash_value, InnerHash()); + return hash_value; +} + +uint64 HloInstruction::InnerHash() const { return 13; } + void HloInstruction::RemoveUser(HloInstruction* user) { auto set_it = user_set_.find(user); CHECK(set_it != user_set_.end()); @@ -1900,6 +1935,11 @@ void HloInstruction::set_while_body(HloComputation* computation) { called_computations_[kBodyComputationIndex] = computation; } +HloInstruction* HloInstruction::while_init() const { + CHECK_EQ(HloOpcode::kWhile, opcode_); + return operands_[0]; +} + HloComputation* HloInstruction::true_computation() const { CHECK_EQ(HloOpcode::kConditional, opcode_); return called_computations_[kTrueComputationIndex]; @@ -2214,7 +2254,7 @@ HloInstructionProto HloInstruction::ToProto() const { proto.set_id(unique_id_); proto.set_name(name_); proto.set_opcode(HloOpcodeString(opcode_)); - *proto.mutable_shape() = shape_; + *proto.mutable_shape() = shape_.ToProto(); for (const HloInstruction* operand : operands_) { proto.add_operand_ids(operand->unique_id()); } @@ -2462,6 +2502,8 @@ Status HloInstruction::Visit(DfsHloVisitorBase* visitor) { return visitor->HandleDomain(this); case HloOpcode::kAfterAll: return visitor->HandleAfterAll(this); + case HloOpcode::kAddDependency: + return visitor->HandleAddDependency(this); case HloOpcode::kIota: return visitor->HandleIota(this); case HloOpcode::kGetDimensionSize: @@ -2623,36 +2665,6 @@ Status HloInstruction::AcceptWithOperandOrder( return Status::OK(); } -namespace { - -// Returns true if the given order is a topological sort of the instructions -// it contains. -bool OrderIsTopologicalSort(const std::vector& order) { - // Create a map from instruction to its position in 'order'. - std::unordered_map order_position; - for (int i = 0; i < order.size(); i++) { - if (!order_position.insert({order[i], i}).second) { - // Instruction order[i] is duplicated in the order. - return false; - } - } - // Verify that the operand of each instruction in the order is also in the - // order *and* the operand's position is earlier (defs are before uses for - // all ops). - for (auto* instruction : order) { - for (auto* operand : instruction->operands()) { - if (!ContainsKey(order_position, operand) || - order_position.at(operand) >= order_position.at(instruction)) { - return false; - } - } - } - - return true; -} - -} // namespace - Status HloInstruction::Accept( const std::function& visitor_func) { FunctionVisitor visitor(visitor_func); @@ -3022,6 +3034,16 @@ const PrecisionConfig& HloInstruction::precision_config() const { LOG(FATAL) << "Unimplemented method."; } +PrecisionConfig* HloInstruction::mutable_precision_config() { + if (auto* convolution = DynCast(this)) { + return convolution->mutable_precision_config(); + } + if (auto* dot = DynCast(this)) { + return dot->mutable_precision_config(); + } + LOG(FATAL) << "Unimplemented method."; +} + HloModule* HloInstruction::GetModule() const { if (parent_) { return parent_->parent(); @@ -3064,6 +3086,10 @@ int64 HloInstruction::concatenate_dimension() const { return Cast(this)->concatenate_dimension(); } +int64 HloInstruction::dimension() const { + return Cast(this)->dimension(); +} + bool HloInstruction::IsRank2Transpose() const { auto transpose = DynCast(this); return transpose != nullptr && transpose->IsRank2Transpose(); @@ -3243,6 +3269,11 @@ absl::optional HloInstruction::all_reduce_id() const { return Cast(this)->all_reduce_id(); } +void HloInstruction::set_all_reduce_id( + const absl::optional& all_reduce_id) { + return Cast(this)->set_all_reduce_id(all_reduce_id); +} + const ConvolutionDimensionNumbers& HloInstruction::convolution_dimension_numbers() const { if (auto convolution = DynCast(this)) { diff --git a/tensorflow/compiler/xla/service/hlo_instruction.h b/tensorflow/compiler/xla/service/hlo_instruction.h index 818d4ede0f..a54716217d 100644 --- a/tensorflow/compiler/xla/service/hlo_instruction.h +++ b/tensorflow/compiler/xla/service/hlo_instruction.h @@ -770,6 +770,9 @@ class HloInstruction { static std::unique_ptr CreateGetDimensionSize( const Shape& shape, HloInstruction* operand, int64 dimension); + static std::unique_ptr CreateAddDependency( + HloInstruction* data_operand, HloInstruction* token_operand); + // Returns the opcode for this instruction. HloOpcode opcode() const { return opcode_; } @@ -883,11 +886,15 @@ class HloInstruction { return false; } - // Use an explicit loop rather than ContainerEquals, because copying around - // std::functions may be too expensive in some cases. - for (size_t i = 0; i < operands().size(); ++i) { - if (!eq_operands(operand(i), other.operand(i))) { - return false; + // Two AllReduces are Identical if they have the same all_reduce_id. + // Their operands don't have to be Identical. + if (!IsCrossModuleAllReduce()) { + // Use an explicit loop rather than ContainerEquals, because copying + // around std::functions may be too expensive in some cases. + for (size_t i = 0; i < operands().size(); ++i) { + if (!eq_operands(operand(i), other.operand(i))) { + return false; + } } } @@ -898,6 +905,12 @@ class HloInstruction { return IdenticalSlowPath(other, eq_computations); } + // Generates a hash value of an HLO instruction. Hash considers + // information on opcode, shape, operands, and typically a root instruction. + // This function returns the same hash value for equivalent HLO instructions, + // with respect to HloInstruction::Identical() method. + uint64 Hash() const; + // Returns whether the instruction has a constant operand. bool HasConstantOperand() const; @@ -997,6 +1010,8 @@ class HloInstruction { void set_while_condition(HloComputation* while_condition); void set_while_body(HloComputation* while_body); + HloInstruction* while_init() const; + // Gets/sets the true and false HloComputation for Conditional. The setters // should only be called by HloModule or HloComputation methods. // @@ -1257,6 +1272,7 @@ class HloInstruction { // superior. // Precondition: opcode must be kConvolution or kDot. const PrecisionConfig& precision_config() const; + PrecisionConfig* mutable_precision_config(); // Sets the debug metadata for this instruction. void set_metadata(const OpMetadata& metadata) { metadata_ = metadata; } @@ -1317,6 +1333,9 @@ class HloInstruction { // Delegates to HloConcatenateInstruction::concatenate_dimension. int64 concatenate_dimension() const; + // Delegates to HloGetDimensionSizeInstruction::dimension. + int64 dimension() const; + // Returns whether this instruction does a rank-2 transposition. bool IsRank2Transpose() const; @@ -1435,6 +1454,7 @@ class HloInstruction { // Delegates to HloAllReduceInstruction::all_reduce_id. absl::optional all_reduce_id() const; + void set_all_reduce_id(const absl::optional& all_reduce_id); // Returns data on the window in a windowed operation such as // convolution. @@ -1599,6 +1619,10 @@ class HloInstruction { const std::function& eq_computations) const; + // Generates a hash value specific to a particular type of an instruction. + // This function typically considers the inner root instruction. + virtual uint64 InnerHash() const; + // Creates an n-ary elementwise operation. static std::unique_ptr CreateNary( const Shape& shape, HloOpcode opcode, diff --git a/tensorflow/compiler/xla/service/hlo_instructions.cc b/tensorflow/compiler/xla/service/hlo_instructions.cc index 4c765aa375..1ea02cf9c0 100644 --- a/tensorflow/compiler/xla/service/hlo_instructions.cc +++ b/tensorflow/compiler/xla/service/hlo_instructions.cc @@ -370,6 +370,11 @@ HloAllReduceInstruction::HloAllReduceInstruction( AppendComputation(reduce_computation); } +void HloAllReduceInstruction::set_all_reduce_id( + const absl::optional& all_reduce_id) { + all_reduce_id_ = all_reduce_id; +} + HloInstructionProto HloAllReduceInstruction::ToProto() const { HloInstructionProto proto = HloCollectiveInstruction::ToProto(); // Proto3 is so sad. @@ -1367,6 +1372,10 @@ bool HloFusionInstruction::IdenticalSlowPath( other.fused_instructions_computation()); } +uint64 HloFusionInstruction::InnerHash() const { + return fused_instructions_computation()->Hash(); +} + std::unique_ptr HloFusionInstruction::CloneWithNewOperandsImpl( const Shape& shape, absl::Span new_operands, HloCloneContext* context) const { @@ -1610,7 +1619,7 @@ HloOutfeedInstruction::HloOutfeedInstruction(const Shape& outfeed_shape, HloInstructionProto HloOutfeedInstruction::ToProto() const { HloInstructionProto proto = HloInstruction::ToProto(); proto.set_outfeed_config(outfeed_config()); - *proto.mutable_outfeed_shape() = outfeed_shape(); + *proto.mutable_outfeed_shape() = outfeed_shape().ToProto(); return proto; } @@ -1862,7 +1871,7 @@ HloInstructionProto HloCustomCallInstruction::ToProto() const { if (layout_constrained()) { proto.set_constrain_layout(true); for (const Shape& shape : operand_shapes_with_layout_) { - *proto.add_operand_shapes_with_layout() = shape; + *proto.add_operand_shapes_with_layout() = shape.ToProto(); } } return proto; diff --git a/tensorflow/compiler/xla/service/hlo_instructions.h b/tensorflow/compiler/xla/service/hlo_instructions.h index d43a8973cc..b5c28137a1 100644 --- a/tensorflow/compiler/xla/service/hlo_instructions.h +++ b/tensorflow/compiler/xla/service/hlo_instructions.h @@ -252,6 +252,7 @@ class HloAllReduceInstruction : public HloCollectiveInstruction { } absl::optional all_reduce_id() const { return all_reduce_id_; } + void set_all_reduce_id(const absl::optional& all_reduce_id); // Returns a serialized representation of this instruction. HloInstructionProto ToProto() const override; @@ -742,6 +743,8 @@ class HloFusionInstruction : public HloInstruction { const HloInstruction& other, const std::function& eq_computations) const override; + uint64 InnerHash() const override; + // Implementation for non-common logic of CloneWithNewOperands. std::unique_ptr CloneWithNewOperandsImpl( const Shape& shape, absl::Span new_operands, @@ -954,6 +957,7 @@ class HloConvolutionInstruction : public HloInstruction { // information but it is presumed that the alternate lowering is strictly // superior. const PrecisionConfig& precision_config() const { return precision_config_; } + PrecisionConfig* mutable_precision_config() { return &precision_config_; } string ToCategory() const override; // Returns a serialized representation of this instruction. @@ -1325,6 +1329,7 @@ class HloDotInstruction : public HloInstruction { // information but it is presumed that the alternate lowering is strictly // superior. const PrecisionConfig& precision_config() const { return precision_config_; } + PrecisionConfig* mutable_precision_config() { return &precision_config_; } // Returns a serialized representation of this instruction. HloInstructionProto ToProto() const override; diff --git a/tensorflow/compiler/xla/service/hlo_lexer.h b/tensorflow/compiler/xla/service/hlo_lexer.h index 3e2f8bcd52..d6a2b292a3 100644 --- a/tensorflow/compiler/xla/service/hlo_lexer.h +++ b/tensorflow/compiler/xla/service/hlo_lexer.h @@ -20,6 +20,7 @@ limitations under the License. #include "absl/strings/string_view.h" #include "tensorflow/compiler/xla/service/hlo_token.h" +#include "tensorflow/compiler/xla/shape.h" #include "tensorflow/compiler/xla/types.h" #include "tensorflow/compiler/xla/xla_data.pb.h" #include "tensorflow/core/platform/logging.h" diff --git a/tensorflow/compiler/xla/service/hlo_matchers.cc b/tensorflow/compiler/xla/service/hlo_matchers.cc index 5269cad94d..d28e79d41a 100644 --- a/tensorflow/compiler/xla/service/hlo_matchers.cc +++ b/tensorflow/compiler/xla/service/hlo_matchers.cc @@ -237,8 +237,4 @@ void PrintTo(const HloInstruction* inst, ::std::ostream* os) { *os << (inst ? inst->ToString() : "nullptr"); } -void PrintTo(HloInstruction* inst, ::std::ostream* os) { - PrintTo(const_cast(inst), os); -} - } // namespace xla diff --git a/tensorflow/compiler/xla/service/hlo_matchers.h b/tensorflow/compiler/xla/service/hlo_matchers.h index 170ec93a33..235efb19ce 100644 --- a/tensorflow/compiler/xla/service/hlo_matchers.h +++ b/tensorflow/compiler/xla/service/hlo_matchers.h @@ -385,7 +385,6 @@ std::vector Pointers(const Container& container) { // Tell GMock to print HloInstruction* by value, so error messages are nice. // Has to be in the same namespace as 'HloInstruction'. void PrintTo(const HloInstruction* inst, ::std::ostream* os); -void PrintTo(HloInstruction* inst, ::std::ostream* os); } // namespace xla diff --git a/tensorflow/compiler/xla/service/hlo_memory_scheduler.cc b/tensorflow/compiler/xla/service/hlo_memory_scheduler.cc index 234fcd266a..d2740bcce2 100644 --- a/tensorflow/compiler/xla/service/hlo_memory_scheduler.cc +++ b/tensorflow/compiler/xla/service/hlo_memory_scheduler.cc @@ -73,7 +73,7 @@ class ListScheduler { // Construct and return a memory-minimizing sequence of HLO instructions // containing the given HLO computation. static StatusOr Run( - const HloComputation& computation, + HloComputation* computation, const TuplePointsToAnalysis& points_to_analysis, const LogicalBuffer::SizeFunction& size_function, const absl::flat_hash_map& @@ -98,7 +98,7 @@ class ListScheduler { // comparison operators. using Priority = std::pair; - ListScheduler(const HloComputation& computation, + ListScheduler(HloComputation* computation, const TuplePointsToAnalysis& points_to_analysis, const LogicalBuffer::SizeFunction& size_function, const absl::flat_hash_map& @@ -111,7 +111,7 @@ class ListScheduler { // instruction. An HLO instruction "uses" a LogicalBuffer if the // LogicalBuffer is in an operand of the instruction as indicated by // points-to analysis. - for (auto* instruction : computation.instructions()) { + for (auto* instruction : computation->instructions()) { absl::flat_hash_set instr_uses; for (auto* operand : instruction->operands()) { points_to_analysis.GetPointsToSet(operand).ForEachElement( @@ -126,13 +126,13 @@ class ListScheduler { // Create map containing the number of unscheduled uses (hlo instructions) // of each logical buffer. - for (auto* instruction : computation.instructions()) { + for (auto* instruction : computation->instructions()) { for (auto* buffer : points_to_analysis.GetBuffersDefinedByInstruction(instruction)) { unscheduled_use_count_[buffer] = 0; } } - for (auto* instruction : computation.instructions()) { + for (auto* instruction : computation->instructions()) { for (const LogicalBuffer* buffer : buffer_uses_.at(instruction)) { ++unscheduled_use_count_[buffer]; } @@ -141,7 +141,7 @@ class ListScheduler { // Buffers live out of the computation have an implicit use at the end of // the computation. for (const LogicalBuffer* live_out_buffer : - points_to_analysis.GetPointsToSet(computation.root_instruction()) + points_to_analysis.GetPointsToSet(computation->root_instruction()) .CreateFlattenedSet()) { ++unscheduled_use_count_[live_out_buffer]; } @@ -157,7 +157,7 @@ class ListScheduler { // HloInstruction, plus some cached metadata, saved for the purposes of making // BytesFreedIfScheduled fast. struct ReadyListEntry { - const HloInstruction* instruction; + HloInstruction* instruction; // The total size of all buffers defined by this instruction. int64 bytes_defined; @@ -171,7 +171,7 @@ class ListScheduler { }; // Creates a ReadyListEntry for the given instruction. - ReadyListEntry MakeReadyListEntry(const HloInstruction* instruction) { + ReadyListEntry MakeReadyListEntry(HloInstruction* instruction) { ReadyListEntry entry; entry.instruction = instruction; @@ -250,13 +250,13 @@ class ListScheduler { // Populate the ready list with instructions which have no operands or // control predecessors. absl::flat_hash_map unscheduled_pred_count; - for (auto* instruction : computation_.instructions()) { + for (auto* instruction : computation_->instructions()) { // TODO(b/34466113): Replace this and above with successors() or // predecessors() when these methods are added to HloInstruction. - for (const HloInstruction* user : instruction->users()) { + for (HloInstruction* user : instruction->users()) { unscheduled_pred_count[user]++; } - for (const HloInstruction* succ : instruction->control_successors()) { + for (HloInstruction* succ : instruction->control_successors()) { unscheduled_pred_count[succ]++; } } @@ -275,7 +275,7 @@ class ListScheduler { ready_instructions[inst] = it; }; - for (auto* instruction : computation_.instructions()) { + for (auto* instruction : computation_->instructions()) { if (instruction->operands().empty() && instruction->control_predecessors().empty()) { add_to_ready_queue(instruction); @@ -287,7 +287,7 @@ class ListScheduler { // schedule. auto best_it = ready_queue.end(); --best_it; - const HloInstruction* best = best_it->second.instruction; + HloInstruction* best = best_it->second.instruction; VLOG(2) << "Schedule instruction: " << best->ToShortString() << " Bytes freed: " << best_it->first.first; ready_queue.erase(best_it); @@ -348,13 +348,13 @@ class ListScheduler { } } } - CHECK_EQ(schedule.size(), computation_.instruction_count()); - CHECK_EQ(scheduled_instructions_.size(), computation_.instruction_count()); + CHECK_EQ(schedule.size(), computation_->instruction_count()); + CHECK_EQ(scheduled_instructions_.size(), computation_->instruction_count()); return schedule; } - const HloComputation& computation_; + HloComputation* computation_; const TuplePointsToAnalysis& points_to_analysis_; const LogicalBuffer::SizeFunction& size_function_; // Computations are analyzed in post-order. When scheduling an instruction @@ -386,13 +386,13 @@ int64 SumLogicalBufferSizes( } StatusOr ScheduleComputationHelper( - const HloComputation& computation, + HloComputation* computation, const TuplePointsToAnalysis& points_to_analysis, const LogicalBuffer::SizeFunction& size_function, const MemorySchedulerAlgorithm& algorithm, const absl::flat_hash_map& memory_by_computation) { - VLOG(2) << "Computation: " << computation.name(); + VLOG(2) << "Computation: " << computation->name(); if (algorithm) { return algorithm(computation, points_to_analysis, size_function, memory_by_computation); @@ -404,17 +404,17 @@ StatusOr ScheduleComputationHelper( } // namespace StatusOr DFSMemoryScheduler( - const HloComputation& computation, + HloComputation* computation, const TuplePointsToAnalysis& points_to_analysis, const LogicalBuffer::SizeFunction& size_function, const absl::flat_hash_map& memory_by_computation) { // These variables are a hack to prevent overflows. int64 cumulative_total_size = 0; - int64 total_hlos = computation.parent()->instruction_count(); + int64 total_hlos = computation->parent()->instruction_count(); absl::flat_hash_map extra_users; absl::flat_hash_map total_sizes; - for (const HloInstruction* hlo : computation.MakeInstructionPostOrder()) { + for (const HloInstruction* hlo : computation->MakeInstructionPostOrder()) { if (ListScheduler::IgnoreInstruction(*hlo)) { extra_users[hlo] = 0; total_sizes[hlo] = 0; @@ -448,8 +448,8 @@ StatusOr DFSMemoryScheduler( total_sizes[hlo] = std::min(total_sizes[hlo], cumulative_total_size); extra_users[hlo] = std::min(extra_users[hlo], total_hlos); } - CHECK_EQ(extra_users.size(), computation.instruction_count()); - CHECK_EQ(total_sizes.size(), computation.instruction_count()); + CHECK_EQ(extra_users.size(), computation->instruction_count()); + CHECK_EQ(total_sizes.size(), computation->instruction_count()); // Construct a total order based on DFS post-order, visiting operands in // decreasing cumulative extra user order, and next by cumulative size, with a @@ -459,7 +459,7 @@ StatusOr DFSMemoryScheduler( sequence.push_back(hlo); return Status::OK(); }); - TF_RETURN_IF_ERROR(computation.AcceptWithOperandOrder( + TF_RETURN_IF_ERROR(computation->AcceptWithOperandOrder( &visitor, [&extra_users, &total_sizes](const HloInstruction* a, const HloInstruction* b) { if (extra_users[a] != extra_users[b]) { @@ -470,12 +470,12 @@ StatusOr DFSMemoryScheduler( } return a->name() < b->name(); })); - CHECK_EQ(sequence.size(), computation.instruction_count()); + CHECK_EQ(sequence.size(), computation->instruction_count()); return sequence; } // namespace xla StatusOr ListMemoryScheduler( - const HloComputation& computation, + HloComputation* computation, const TuplePointsToAnalysis& points_to_analysis, const LogicalBuffer::SizeFunction& size_function, const absl::flat_hash_map& @@ -485,16 +485,16 @@ StatusOr ListMemoryScheduler( } StatusOr PostOrderMemoryScheduler( - const HloComputation& computation, + HloComputation* computation, const TuplePointsToAnalysis& points_to_analysis, const LogicalBuffer::SizeFunction& size_function, const absl::flat_hash_map& memory_by_computation) { - return HloInstructionSequence(computation.MakeInstructionPostOrder()); + return HloInstructionSequence(computation->MakeInstructionPostOrder()); } StatusOr DefaultMemoryScheduler( - const HloComputation& computation, + HloComputation* computation, const TuplePointsToAnalysis& points_to_analysis, const LogicalBuffer::SizeFunction& size_function, const absl::flat_hash_map& @@ -513,7 +513,7 @@ StatusOr DefaultMemoryScheduler( memory_by_computation)); TF_ASSIGN_OR_RETURN(const int64 list_memory, HeapSimulator::MinimumMemoryForComputation( - computation, list_sequence, points_to_analysis, + *computation, list_sequence, points_to_analysis, size_function, &memory_by_computation)); VLOG(2) << "Min-memory list sequence: " << HumanReadableNumBytes(list_memory); @@ -522,7 +522,7 @@ StatusOr DefaultMemoryScheduler( size_function, memory_by_computation)); TF_ASSIGN_OR_RETURN(const int64 dfs_memory, HeapSimulator::MinimumMemoryForComputation( - computation, dfs_sequence, points_to_analysis, + *computation, dfs_sequence, points_to_analysis, size_function, &memory_by_computation)); VLOG(2) << "Min-memory dfs sequence: " << HumanReadableNumBytes(dfs_memory); @@ -532,7 +532,7 @@ StatusOr DefaultMemoryScheduler( memory_by_computation)); TF_ASSIGN_OR_RETURN(const int64 post_order_memory, HeapSimulator::MinimumMemoryForComputation( - computation, post_order_sequence, points_to_analysis, + *computation, post_order_sequence, points_to_analysis, size_function, &memory_by_computation)); VLOG(2) << "Min-memory post order sequence: " << HumanReadableNumBytes(post_order_memory); @@ -555,17 +555,17 @@ StatusOr DefaultMemoryScheduler( } StatusOr ScheduleModule( - const HloModule& module, const LogicalBuffer::SizeFunction& size_function, + HloModule* module, const LogicalBuffer::SizeFunction& size_function, const MemorySchedulerAlgorithm& algorithm) { - HloSchedule schedule(&module); + HloSchedule schedule(module); TF_ASSIGN_OR_RETURN(std::unique_ptr points_to_analysis, - TuplePointsToAnalysis::Run(&module)); + TuplePointsToAnalysis::Run(module)); absl::flat_hash_map memory_by_computation; - for (const auto* computation : module.MakeComputationPostOrder()) { + for (auto* computation : module->MakeComputationPostOrder()) { if (!computation->IsFusionComputation()) { TF_ASSIGN_OR_RETURN(HloInstructionSequence computation_sequence, ScheduleComputationHelper( - *computation, *points_to_analysis, size_function, + computation, *points_to_analysis, size_function, algorithm, memory_by_computation)); memory_by_computation[computation] = HeapSimulator::MinimumMemoryForComputation( @@ -583,11 +583,11 @@ StatusOr ScheduleModule( } StatusOr ScheduleComputation( - const HloComputation& computation, + HloComputation* computation, const LogicalBuffer::SizeFunction& size_function) { - CHECK(!computation.IsFusionComputation()); + CHECK(!computation->IsFusionComputation()); TF_ASSIGN_OR_RETURN(std::unique_ptr points_to_analysis, - TuplePointsToAnalysis::Run(computation.parent())); + TuplePointsToAnalysis::Run(computation->parent())); absl::flat_hash_map empty_map; return ScheduleComputationHelper(computation, *points_to_analysis, size_function, nullptr, empty_map); @@ -600,7 +600,7 @@ HloMemoryScheduler::HloMemoryScheduler( StatusOr HloMemoryScheduler::Run(HloModule* module) { TF_ASSIGN_OR_RETURN(HloSchedule schedule, - ScheduleModule(*module, size_function_, algorithm_)); + ScheduleModule(module, size_function_, algorithm_)); TF_RETURN_IF_ERROR(module->set_schedule(std::move(schedule))); return true; } diff --git a/tensorflow/compiler/xla/service/hlo_memory_scheduler.h b/tensorflow/compiler/xla/service/hlo_memory_scheduler.h index cca5dc4939..7227bfb27c 100644 --- a/tensorflow/compiler/xla/service/hlo_memory_scheduler.h +++ b/tensorflow/compiler/xla/service/hlo_memory_scheduler.h @@ -36,14 +36,14 @@ namespace xla { // that describes buffer aliasing, together with a target-specific size function // that maps a tensor's logical size to its padded size. typedef std::function( - const HloComputation&, const TuplePointsToAnalysis&, + HloComputation*, const TuplePointsToAnalysis&, const LogicalBuffer::SizeFunction&, const absl::flat_hash_map&)> MemorySchedulerAlgorithm; // List scheduler StatusOr ListMemoryScheduler( - const HloComputation& computation, + HloComputation* computation, const TuplePointsToAnalysis& points_to_analysis, const LogicalBuffer::SizeFunction& size_function, const absl::flat_hash_map& @@ -51,7 +51,7 @@ StatusOr ListMemoryScheduler( // DFS-order scheduler StatusOr DFSMemoryScheduler( - const HloComputation& computation, + HloComputation* computation, const TuplePointsToAnalysis& points_to_analysis, const LogicalBuffer::SizeFunction& size_function, const absl::flat_hash_map& @@ -59,7 +59,7 @@ StatusOr DFSMemoryScheduler( // Naive Post Order scheduler StatusOr PostOrderMemoryScheduler( - const HloComputation& computation, + HloComputation* computation, const TuplePointsToAnalysis& points_to_analysis, const LogicalBuffer::SizeFunction& size_function, const absl::flat_hash_map& @@ -69,7 +69,7 @@ StatusOr PostOrderMemoryScheduler( // and the DFS scheduler, and chooses whichever returns a lower min-memory, // not accounting for fragmentation. StatusOr DefaultMemoryScheduler( - const HloComputation& computation, + HloComputation* computation, const TuplePointsToAnalysis& points_to_analysis, const LogicalBuffer::SizeFunction& size_function, const absl::flat_hash_map& @@ -79,13 +79,13 @@ StatusOr DefaultMemoryScheduler( // the computation. size_function is the function returning the number of bytes // required for a LogicalBuffer. StatusOr ScheduleModule( - const HloModule& module, const LogicalBuffer::SizeFunction& size_function, + HloModule* module, const LogicalBuffer::SizeFunction& size_function, const MemorySchedulerAlgorithm& algorithm = {}); // Computes the schedule for a single computation. // Currently only used by the GPU backend. StatusOr ScheduleComputation( - const HloComputation& computation, + HloComputation* computation, const LogicalBuffer::SizeFunction& size_function); // A pass which schedules the HLO instructions in a module. The HloModule's diff --git a/tensorflow/compiler/xla/service/hlo_memory_scheduler_test.cc b/tensorflow/compiler/xla/service/hlo_memory_scheduler_test.cc index 984a6266ab..bc0d7e2bc0 100644 --- a/tensorflow/compiler/xla/service/hlo_memory_scheduler_test.cc +++ b/tensorflow/compiler/xla/service/hlo_memory_scheduler_test.cc @@ -65,7 +65,7 @@ TEST_F(HloSchedulingTest, LastUseScheduledFirst) { auto sub = builder.AddInstruction( HloInstruction::CreateBinary(vec, HloOpcode::kSubtract, add, negate)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); HloMemoryScheduler scheduler([](const BufferValue& buffer) { @@ -78,7 +78,7 @@ TEST_F(HloSchedulingTest, LastUseScheduledFirst) { TF_ASSERT_OK(module->schedule().Verify()); // Verify that all instructions are in the sequence. - const std::vector& sequence = + const std::vector& sequence = module->schedule().sequence(module->entry_computation()).instructions(); EXPECT_EQ(module->entry_computation()->instruction_count(), sequence.size()); @@ -124,9 +124,9 @@ ENTRY root { }; TF_ASSERT_OK_AND_ASSIGN( HloSchedule schedule, - ScheduleModule(*module, size_fn, ListMemoryScheduler)); + ScheduleModule(module.get(), size_fn, ListMemoryScheduler)); // Verify that all instructions are in the sequence. - const std::vector& sequence = + const std::vector& sequence = schedule.sequence(module->entry_computation()).instructions(); EXPECT_EQ(module->entry_computation()->instruction_count(), sequence.size()); @@ -172,15 +172,16 @@ TEST_F(HloSchedulingTest, TuplesAreAccountedCorrectly) { builder.AddInstruction(HloInstruction::CreateBinary(r1f32, HloOpcode::kAdd, tuple_elm, abs_abs2)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); TF_ASSERT_OK_AND_ASSIGN(HloSchedule schedule, - ScheduleModule(*module, - [](const BufferValue& buffer) { - return ShapeUtil::ByteSizeOf( - buffer.shape(), TUPLE_SIZE); - }, - ListMemoryScheduler)); + ScheduleModule( + module.get(), + [](const BufferValue& buffer) { + return ShapeUtil::ByteSizeOf(buffer.shape(), + TUPLE_SIZE); + }, + ListMemoryScheduler)); // Verify that all instructions are in the sequence. EXPECT_EQ(module->entry_computation()->instruction_count(), @@ -218,19 +219,19 @@ TEST_F(HloSchedulingTest, MultiOutputFusionAccountedCorrectly) { builder.AddInstruction( HloInstruction::CreateBinary(r1f32, HloOpcode::kAdd, tuple_elm, exp)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto* computation = module->AddEntryComputation(builder.Build()); auto fusion = computation->CreateFusionInstruction( {tuple, mul, add}, HloInstruction::FusionKind::kLoop); TF_ASSERT_OK_AND_ASSIGN(HloSchedule schedule, - ScheduleModule(*module, - [](const BufferValue& buffer) { - return ShapeUtil::ByteSizeOf( - buffer.shape(), 2); - }, - ListMemoryScheduler)); + ScheduleModule( + module.get(), + [](const BufferValue& buffer) { + return ShapeUtil::ByteSizeOf(buffer.shape(), 2); + }, + ListMemoryScheduler)); // Verify that all instructions are in the sequence. EXPECT_EQ(module->entry_computation()->instruction_count(), @@ -252,7 +253,7 @@ TEST_F(HloSchedulingTest, HeapSimulatorAccountsForSubcomputations) { HloInstruction::CreateParameter(0, r1f32, "cond_param")); HloInstruction* zero_vector = cond_builder.AddInstruction(HloInstruction::CreateConstant( - LiteralUtil::CreateR2({{0, 0, 0, 0}}))); + LiteralUtil::CreateR1({0, 0, 0, 0}))); cond_builder.AddInstruction(HloInstruction::CreateBinary( ShapeUtil::MakeShape(PRED, {}), HloOpcode::kNe, cond_param, zero_vector)); auto cond_computation = module->AddEmbeddedComputation(cond_builder.Build()); @@ -284,7 +285,7 @@ TEST_F(HloSchedulingTest, HeapSimulatorAccountsForSubcomputations) { }; TF_ASSERT_OK_AND_ASSIGN( HloSchedule schedule, - ScheduleModule(*module, size_fn, ListMemoryScheduler)); + ScheduleModule(module.get(), size_fn, ListMemoryScheduler)); // Verify that all instructions are in the sequence. auto entry_computation = module->entry_computation(); EXPECT_EQ(module->entry_computation()->instruction_count(), diff --git a/tensorflow/compiler/xla/service/hlo_module.cc b/tensorflow/compiler/xla/service/hlo_module.cc index 14bf17f4be..fe8371384c 100644 --- a/tensorflow/compiler/xla/service/hlo_module.cc +++ b/tensorflow/compiler/xla/service/hlo_module.cc @@ -240,8 +240,10 @@ HloModuleProto HloModule::ToProto() const { *proto.mutable_schedule() = schedule().ToProto().ValueOrDie(); } *proto.mutable_host_program_shape() = - entry_computation_layout().ComputeProgramShape(); + entry_computation_layout().ComputeProgramShape().ToProto(); *proto.mutable_input_output_alias() = input_output_alias_config().ToProto(); + *proto.mutable_dynamic_parameter_binding() = + dynamic_parameter_binding().ToProto(); return proto; } @@ -255,7 +257,7 @@ StatusOr> HloModule::CreateFromProto( // the entry parameters and root. TF_RET_CHECK(proto.has_host_program_shape()) << "No program shape found in the proto"; - const auto& expected_program_shape = proto.host_program_shape(); + ProgramShape expected_program_shape(proto.host_program_shape()); TF_RET_CHECK(expected_program_shape.parameters_size() == module_config.entry_computation_layout().parameter_count()); for (int i = 0; i < expected_program_shape.parameters_size(); ++i) { @@ -325,6 +327,10 @@ StatusOr> HloModule::CreateFromProto( // Because we didn't uniquify the names or the ids, double-check that the // instruction and computation names and ids are unique from the proto. + TF_ASSIGN_OR_RETURN(module->dynamic_parameter_binding_, + DynamicParameterBinding::CreateFromProto( + proto.dynamic_parameter_binding())); + absl::flat_hash_set computation_names; absl::flat_hash_set instruction_names; absl::flat_hash_set computation_ids; @@ -363,9 +369,9 @@ StatusOr HloModule::CreateModuleConfigFromProto( const HloModuleProto& module, const DebugOptions& debug_options) { TF_RET_CHECK(module.has_host_program_shape()) << "No program shape found in the proto"; - const auto& program_shape = module.host_program_shape(); + ProgramShape program_shape(module.host_program_shape()); - HloModuleConfig module_config(program_shape); + HloModuleConfig module_config(ProgramShape{program_shape}); module_config.set_debug_options(debug_options); // The module config is constructed with default layouts regardless of what is diff --git a/tensorflow/compiler/xla/service/hlo_module.h b/tensorflow/compiler/xla/service/hlo_module.h index 8a1f999e3a..7b9cbf9a53 100644 --- a/tensorflow/compiler/xla/service/hlo_module.h +++ b/tensorflow/compiler/xla/service/hlo_module.h @@ -28,6 +28,7 @@ limitations under the License. #include "absl/types/optional.h" #include "absl/types/span.h" #include "tensorflow/compiler/xla/iterator_util.h" +#include "tensorflow/compiler/xla/service/dynamic_parameter_binding.h" #include "tensorflow/compiler/xla/service/hlo.pb.h" #include "tensorflow/compiler/xla/service/hlo_clone_context.h" #include "tensorflow/compiler/xla/service/hlo_computation.h" @@ -103,11 +104,7 @@ class HloModule { HloCloneContext* context = nullptr); // Return a pointer to the entry computation of the module. - const HloComputation* entry_computation() const { - CHECK_NE(nullptr, entry_computation_); - return entry_computation_; - } - HloComputation* entry_computation() { + HloComputation* entry_computation() const { CHECK_NE(nullptr, entry_computation_); return entry_computation_; } @@ -135,6 +132,12 @@ class HloModule { return config_.entry_computation_layout(); } + // Generates a hash value of an HLO module. Hash considers + // information on opcode, shape, operands, and typically a root instruction. + // This function returns the same hash value for equivalent HLO modules, + // with respect to HloInstruction::Identical() method. + uint64 Hash() const { return entry_computation()->Hash(); } + // Gets the computations in this module. // // Returns a view of HloComputation*s, so you can iterate over this in the @@ -232,6 +235,16 @@ class HloModule { return input_output_alias_config_; } + // DynamicParameterBinding holds the list of bindings that indicates which + // parameter dimensions are dynamic and which parameters represent their + // runtime value. + DynamicParameterBinding& dynamic_parameter_binding() { + return dynamic_parameter_binding_; + } + const DynamicParameterBinding& dynamic_parameter_binding() const { + return dynamic_parameter_binding_; + } + // Returns an id that is unique to this module across all modules created over // the lifetime of this process. int unique_id() const { return unique_id_; } @@ -285,6 +298,9 @@ class HloModule { // alias_config indicates the alias information of input/output buffers that // are expected from the module. HloInputOutputAliasConfig input_output_alias_config_; + + // Bindings for dynamic parameter mapping. + DynamicParameterBinding dynamic_parameter_binding_; }; } // namespace xla diff --git a/tensorflow/compiler/xla/service/hlo_module_test.cc b/tensorflow/compiler/xla/service/hlo_module_test.cc index 3ae67e4e5e..620cb7e01a 100644 --- a/tensorflow/compiler/xla/service/hlo_module_test.cc +++ b/tensorflow/compiler/xla/service/hlo_module_test.cc @@ -63,7 +63,7 @@ class HloModuleTest : public HloTestBase { TEST_F(HloModuleTest, OneComputationPostOrder) { // Create a module with a single computation. - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(CreateConstantComputation()); EXPECT_THAT(module->MakeComputationPostOrder(), @@ -72,7 +72,7 @@ TEST_F(HloModuleTest, OneComputationPostOrder) { TEST_F(HloModuleTest, TwoComputationsPostOrder) { // Create a module with two unconnected computations. - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation1 = module->AddEntryComputation(CreateConstantComputation()); auto computation2 = module->AddEmbeddedComputation(CreateConstantComputation()); @@ -88,7 +88,7 @@ TEST_F(HloModuleTest, TwoComputationsPostOrder) { TEST_F(HloModuleTest, CloneTest) { // Create and copy a module with a diamond call graph of computations. - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation1 = module->AddEmbeddedComputation(CreateConstantComputation()); auto computation2 = @@ -111,7 +111,7 @@ TEST_F(HloModuleTest, CloneTest) { } TEST_F(HloModuleTest, CloneHasFusion) { - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); // Create the fused computation. HloComputation* fused_computation; @@ -154,7 +154,7 @@ TEST_F(HloModuleTest, CloneHasFusion) { TEST_F(HloModuleTest, DiamondComputationsPostOrder) { // Create a module with a diamond call graph of computations. - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation1 = module->AddEmbeddedComputation(CreateConstantComputation()); auto computation2 = @@ -174,7 +174,7 @@ TEST_F(HloModuleTest, DiamondComputationsPostOrder) { TEST_F(HloModuleTest, LargeConstantToString) { // Create a module with a single computation. - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto builder = HloComputation::Builder("Constant"); std::vector values(16, 42.0); builder.AddInstruction( @@ -194,8 +194,8 @@ TEST_F(HloModuleTest, LargeConstantToString) { } TEST_F(HloModuleTest, UniqueModuleId) { - auto module_a = CreateNewUnverifiedModule(); - auto module_b = CreateNewUnverifiedModule(); + auto module_a = CreateNewVerifiedModule(); + auto module_b = CreateNewVerifiedModule(); EXPECT_NE(module_a->unique_id(), module_b->unique_id()); } diff --git a/tensorflow/compiler/xla/service/hlo_opcode.h b/tensorflow/compiler/xla/service/hlo_opcode.h index 70c7d70b41..127cfd165a 100644 --- a/tensorflow/compiler/xla/service/hlo_opcode.h +++ b/tensorflow/compiler/xla/service/hlo_opcode.h @@ -47,6 +47,8 @@ namespace xla { #define HLO_OPCODE_LIST(V) \ V(kAbs, "abs") \ V(kAdd, "add") \ + V(kAddDependency, "add-dependency") \ + V(kAfterAll, "after-all", kHloOpcodeIsVariadic) \ V(kAllToAll, "all-to-all") \ V(kAtan2, "atan2") \ V(kBatchNormGrad, "batch-norm-grad") \ @@ -84,7 +86,6 @@ namespace xla { V(kGather, "gather") \ V(kGe, "greater-than-or-equal-to", kHloOpcodeIsComparison) \ V(kGetDimensionSize, "get-dimension-size") \ - V(kAfterAll, "after-all", kHloOpcodeIsVariadic) \ V(kGetTupleElement, "get-tuple-element") \ V(kGt, "greater-than", kHloOpcodeIsComparison) \ V(kImag, "imag") \ diff --git a/tensorflow/compiler/xla/service/hlo_ordering.cc b/tensorflow/compiler/xla/service/hlo_ordering.cc index f5f99bece1..ca6a154809 100644 --- a/tensorflow/compiler/xla/service/hlo_ordering.cc +++ b/tensorflow/compiler/xla/service/hlo_ordering.cc @@ -356,8 +356,7 @@ void SequentialHloOrdering::Initialize() { // Create a map from instruction to its order position. TF_DCHECK_OK(schedule_.Verify()); for (const auto& computation_sequence : schedule_.sequences()) { - const std::vector& order = - computation_sequence.second.instructions(); + const auto& order = computation_sequence.second.instructions(); for (int i = 0; i < order.size(); ++i) { InsertOrDie(&order_position_, order[i], i); } diff --git a/tensorflow/compiler/xla/service/hlo_ordering_test.cc b/tensorflow/compiler/xla/service/hlo_ordering_test.cc index 2ab8aa57f6..3ca77e60cd 100644 --- a/tensorflow/compiler/xla/service/hlo_ordering_test.cc +++ b/tensorflow/compiler/xla/service/hlo_ordering_test.cc @@ -53,7 +53,7 @@ TEST_F(HloOrderingTest, InstructionsInDifferentComputations) { // %c = Constant(42.0f) // // This results in a diamond-shaped callgraph. - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); const Shape scalar_shape = ShapeUtil::MakeShape(xla::F32, {}); auto builder_c = HloComputation::Builder("C"); @@ -126,7 +126,7 @@ TEST_F(HloOrderingTest, InstructionsInWhileComputations) { // %constant = Constant(1.0) // return While(%constant, body, condition) // - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); const Shape scalar_shape = ShapeUtil::MakeShape(xla::F32, {}); auto body_builder = HloComputation::Builder("body"); @@ -176,7 +176,7 @@ TEST_F(HloOrderingTest, InstructionsInWhileComputations) { TEST_F(HloOrderingTest, ParametersDefinedBeforeOthers) { // Entry parameter should always be defined before other instruction. - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); const Shape scalar_shape = ShapeUtil::MakeShape(xla::F32, {}); auto builder = HloComputation::Builder(TestName()); auto constant = builder.AddInstruction( @@ -209,7 +209,7 @@ TEST_F(HloOrderingTest, ValuesInWhileComputations) { // %while = While(%constant, body, condition) // %add = Add(%constant, %while) // - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); const Shape scalar_shape = ShapeUtil::MakeShape(xla::F32, {}); auto body_builder = HloComputation::Builder("body"); @@ -407,7 +407,7 @@ TEST_F(HloOrderingTest, // %dead = Constant(123.0) // // %root should interfere with %dead. - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); const Shape scalar_shape = ShapeUtil::MakeShape(xla::F32, {}); auto builder = HloComputation::Builder(TestName()); @@ -455,7 +455,7 @@ TEST_F(HloOrderingTest, // ROOT %call = call({%c}), subcomputation // // %root should interfere with %dead. - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); const Shape scalar_shape = ShapeUtil::MakeShape(xla::F32, {}); auto subbuilder = HloComputation::Builder(TestName() + ".sub"); diff --git a/tensorflow/compiler/xla/service/hlo_parser.cc b/tensorflow/compiler/xla/service/hlo_parser.cc index 4390145c6b..9b5bb5d0bd 100644 --- a/tensorflow/compiler/xla/service/hlo_parser.cc +++ b/tensorflow/compiler/xla/service/hlo_parser.cc @@ -47,11 +47,11 @@ const double kF16max = 65504; // Creates and returns a schedule created using the order of the instructions in // the HloComputation::instructions() vectors in the module. -HloSchedule ScheduleFromInstructionOrder(const HloModule* module) { +HloSchedule ScheduleFromInstructionOrder(HloModule* module) { HloSchedule schedule(module); - for (const HloComputation* computation : module->computations()) { + for (HloComputation* computation : module->computations()) { if (!computation->IsFusionComputation()) { - for (const HloInstruction* instruction : computation->instructions()) { + for (HloInstruction* instruction : computation->instructions()) { schedule.GetOrCreateSequence(computation).push_back(instruction); } } @@ -850,6 +850,15 @@ bool HloParser::ParseInstructionRhs(HloComputation::Builder* builder, } break; } + case HloOpcode::kAddDependency: { + if (!ParseOperands(&operands, /*expected_size=*/2) || + !ParseAttributes(attrs)) { + return false; + } + instruction = builder->AddInstruction( + HloInstruction::CreateAddDependency(operands[0], operands[1])); + break; + } case HloOpcode::kSort: { optional> dimensions; attrs["dimensions"] = {/*required=*/true, AttrTy::kBracedInt64List, diff --git a/tensorflow/compiler/xla/service/hlo_parser_test.cc b/tensorflow/compiler/xla/service/hlo_parser_test.cc index c59bdc0a0b..ab71f011ac 100644 --- a/tensorflow/compiler/xla/service/hlo_parser_test.cc +++ b/tensorflow/compiler/xla/service/hlo_parser_test.cc @@ -21,7 +21,8 @@ limitations under the License. #include "absl/strings/string_view.h" #include "tensorflow/compiler/xla/service/hlo_casting_utils.h" #include "tensorflow/compiler/xla/service/hlo_instructions.h" -#include "tensorflow/compiler/xla/service/hlo_matchers.h" +#include "tensorflow/compiler/xla/service/pattern_matcher.h" +#include "tensorflow/compiler/xla/service/pattern_matcher_gmock.h" #include "tensorflow/compiler/xla/window_util.h" #include "tensorflow/core/lib/core/status_test_util.h" #include "tensorflow/core/platform/test.h" @@ -29,7 +30,7 @@ limitations under the License. namespace xla { namespace { -namespace op = ::xla::testing::opcode_matchers; +namespace m = ::xla::match; using absl::string_view; struct TestData { @@ -195,7 +196,7 @@ ENTRY %add_constants () -> f32[] { R"(HloModule TupleConstant_module ENTRY %TupleConstant.v1 () -> (f32[2,1], f32[2]) { - ROOT %constant = (f32[2,1]{1,0}, f32[2]{0}) constant((f32[2,1], f32[2]) ( f32[2,1] { { 1 }, { 2 } }, {2, 42} )) + ROOT %constant = (f32[2,1]{1,0}, f32[2]{0}) constant((f32[2,1], f32[2]) ( f32[2,1] { {1}, {2} }, {2, 42} )) } )" @@ -587,7 +588,7 @@ ENTRY %DynamicUpdateSlice.v4 (input: s32[1,1,25,1], update: s32[1,1,2,1], start_ R"(HloModule BasicTraining_module ENTRY %BasicTraining.v4 () -> (f32[2,2,1,2], f32[2], f32[2]) { - %constant = f32[2,2,1,2]{3,2,1,0} constant(f32[2,2,1,2] { { /*i0=0*/ { /*i1=0*/ {1, 2} }, { /*i1=1*/ {3, 4} } }, { /*i0=1*/ { /*i1=0*/ {5, 6} }, { /*i1=1*/ {7, 8} } } }) + %constant = f32[2,2,1,2]{3,2,1,0} constant(f32[2,2,1,2] { { /*i0=0*/ { /*i1=0*/ { 1, 2 } }, { /*i1=1*/ { 3, 4 } } }, { /*i0=1*/ { /*i1=0*/ { 5, 6 } }, { /*i1=1*/ { 7, 8 } } } }) %constant.1 = f32[2]{0} constant({2, 3}) %constant.2 = f32[2]{0} constant({1, 2}) ROOT %batch-norm-training = (f32[2,2,1,2]{3,2,1,0}, f32[2]{0}, f32[2]{0}) batch-norm-training(f32[2,2,1,2]{3,2,1,0} %constant, f32[2]{0} %constant.1, f32[2]{0} %constant.2), epsilon=0.001, feature_index=3 @@ -1241,7 +1242,38 @@ ENTRY Sort { } )" + }, +// AfterAll with multiple operands +{ +"AfterAllWithMultipleOperands", +R"(HloModule AfterAllWithMultipleOperands + +ENTRY AfterAllWithMultipleOperands { + p0 = f32[] parameter(0) + token0 = token[] after-all() + token1 = token[] after-all() + ROOT after-all = token[] after-all(p0, token0, token1) } + +)" +}, +// AddDependency +// A dependency chain is created from 'neg' to 'exp' using tokens. +{ +"AddDependency", +R"(HloModule AddDependency + +ENTRY AddDependency { + p = f32[] parameter(0) + neg = f32[] negate(p) + token = token[] after-all(neg) + p_after_token = f32[] add-dependency(p, token) + exp = f32[] exponential(p_after_token) + ROOT sum = f32[] add(neg, exp) +} + +)" +}, }); // clang-format on } @@ -1862,7 +1894,8 @@ ENTRY ReduceR3ToR2 { )"; TF_ASSERT_OK_AND_ASSIGN(auto module, ParseHloString(original)); ASSERT_NE(module->entry_computation(), nullptr); - EXPECT_THAT(module->entry_computation()->root_instruction(), op::Reduce()); + EXPECT_THAT(module->entry_computation()->root_instruction(), + GmockMatch(m::Reduce())); } TEST_F(HloParserTest, ParseSharding) { @@ -1922,7 +1955,7 @@ TEST(HloParserSingleOpTest, SingleOp) { const HloComputation* computation = module->entry_computation(); ASSERT_NE(computation, nullptr); EXPECT_THAT(computation->root_instruction(), - op::Multiply(op::Parameter(0), op::Parameter(1))); + GmockMatch(m::Multiply(m::Parameter(0), m::Parameter(1)))); } TEST(HloParserSingleOpTest, SingleOpNoShapeProducesError) { @@ -1950,7 +1983,7 @@ TEST(HloParserSingleOpTest, SingleOpNoNames) { const HloComputation* computation = module->entry_computation(); ASSERT_NE(computation, nullptr); EXPECT_THAT(computation->root_instruction(), - op::Multiply(op::Parameter(0), op::Parameter(1))); + GmockMatch(m::Multiply(m::Parameter(0), m::Parameter(1)))); } TEST(HloParserSingleOpTest, CanonicalOp) { @@ -1959,7 +1992,7 @@ TEST(HloParserSingleOpTest, CanonicalOp) { const HloComputation* computation = module->entry_computation(); ASSERT_NE(computation, nullptr); EXPECT_THAT(computation->root_instruction(), - op::Multiply(op::Parameter(0), op::Parameter(1))); + GmockMatch(m::Multiply(m::Parameter(0), m::Parameter(1)))); EXPECT_EQ( computation->root_instruction()->ToString(HloPrintOptions::Canonical()), text); @@ -2013,7 +2046,11 @@ TEST(HloParserSingleOpTest, SingleOpWithNested) { const HloComputation* computation = module->entry_computation(); ASSERT_NE(computation, nullptr); EXPECT_THAT(computation->root_instruction(), - op::Fusion(op::Parameter(0), op::Parameter(1))); + GmockMatch(m::Op() + .WithOpcode(HloOpcode::kFusion) + .WithNumOperands(2) + .WithOperand(0, m::Parameter(0)) + .WithOperand(1, m::Parameter(1)))); } TEST(HloParserSingleOpTest, SingleOpWithNested_DoesNotExist) { @@ -2057,7 +2094,7 @@ TEST(HloParserSingleOpTest, ConvolutionTrivialFeatureGroupCount) { const HloComputation* computation = module->entry_computation(); ASSERT_NE(computation, nullptr); EXPECT_THAT(computation->root_instruction(), - op::Convolution(op::Parameter(0), op::Parameter(1))); + GmockMatch(m::Convolution(m::Parameter(0), m::Parameter(1)))); auto* convolution = Cast(computation->root_instruction()); EXPECT_EQ(convolution->feature_group_count(), 1); @@ -2121,8 +2158,10 @@ ENTRY %axpy.v5 (alpha: f32[], x: f32[2,4], y: f32[2,4]) -> f32[2,4] { module->schedule().is_computation_scheduled(module->entry_computation())); EXPECT_THAT( module->schedule().sequence(module->entry_computation()).instructions(), - ::testing::ElementsAre(op::Parameter(), op::Broadcast(), op::Parameter(), - op::Multiply(), op::Parameter(), op::Add())); + ::testing::ElementsAre( + GmockMatch(m::Parameter()), GmockMatch(m::Broadcast()), + GmockMatch(m::Parameter()), GmockMatch(m::Multiply()), + GmockMatch(m::Parameter()), GmockMatch(m::Add()))); } TEST_F(HloParserTest, IsScheduledIsTrueDifferentOrder) { @@ -2148,8 +2187,10 @@ ENTRY %axpy.v5 (alpha: f32[], x: f32[2,4], y: f32[2,4]) -> f32[2,4] { module->schedule().is_computation_scheduled(module->entry_computation())); EXPECT_THAT( module->schedule().sequence(module->entry_computation()).instructions(), - ::testing::ElementsAre(op::Parameter(), op::Parameter(), op::Parameter(), - op::Broadcast(), op::Multiply(), op::Add())); + ::testing::ElementsAre( + GmockMatch(m::Parameter()), GmockMatch(m::Parameter()), + GmockMatch(m::Parameter()), GmockMatch(m::Broadcast()), + GmockMatch(m::Multiply()), GmockMatch(m::Add()))); } TEST_F(HloParserTest, CustomCallWrongNumberofOperandConstraints) { diff --git a/tensorflow/compiler/xla/service/hlo_proto_util.cc b/tensorflow/compiler/xla/service/hlo_proto_util.cc index cf33668f5b..981d06ce10 100644 --- a/tensorflow/compiler/xla/service/hlo_proto_util.cc +++ b/tensorflow/compiler/xla/service/hlo_proto_util.cc @@ -48,7 +48,7 @@ StatusOr> CreateModuleFromProto( return std::move(module); } -StatusOr> EntryComputationParameterShapes( +StatusOr> EntryComputationParameterShapes( const HloProto& hlo_proto) { if (!hlo_proto.has_hlo_module()) { return NotFound("HloProto missing HloModuleProto."); @@ -57,15 +57,16 @@ StatusOr> EntryComputationParameterShapes( return NotFound("HloProto missing program shape."); } - std::vector parameter_shapes; + std::vector parameter_shapes; const auto& program_shape = hlo_proto.hlo_module().host_program_shape(); - for (const Shape& shape : program_shape.parameters()) { + for (const ShapeProto& shape : program_shape.parameters()) { parameter_shapes.push_back(&shape); } return parameter_shapes; } -StatusOr EntryComputationOutputShape(const HloProto& hlo_proto) { +StatusOr EntryComputationOutputShape( + const HloProto& hlo_proto) { if (!hlo_proto.has_hlo_module()) { return NotFound("HloProto missing HloModuleProto."); } diff --git a/tensorflow/compiler/xla/service/hlo_proto_util.h b/tensorflow/compiler/xla/service/hlo_proto_util.h index 1db82dd6fc..31ea2aaffd 100644 --- a/tensorflow/compiler/xla/service/hlo_proto_util.h +++ b/tensorflow/compiler/xla/service/hlo_proto_util.h @@ -43,12 +43,13 @@ StatusOr> CreateModuleFromProto( // Returns the shapes of the parameters of the entry computation. Shape pointers // refer to shapes inside of the given HloProto. -StatusOr> EntryComputationParameterShapes( +StatusOr> EntryComputationParameterShapes( const HloProto& hlo_proto); // Returns the shape of the output of the entry computation. The shape pointer // refers to the output shape inside of the given HloProto. -StatusOr EntryComputationOutputShape(const HloProto& hlo_proto); +StatusOr EntryComputationOutputShape( + const HloProto& hlo_proto); } // namespace xla diff --git a/tensorflow/compiler/xla/service/hlo_rematerialization.cc b/tensorflow/compiler/xla/service/hlo_rematerialization.cc index 49e46ecd00..48add75523 100644 --- a/tensorflow/compiler/xla/service/hlo_rematerialization.cc +++ b/tensorflow/compiler/xla/service/hlo_rematerialization.cc @@ -130,10 +130,10 @@ using ItemList = absl::InlinedVector; // before arbitrary elements. class InstructionList { public: - explicit InstructionList(const std::vector& order) { + explicit InstructionList(const HloInstructionSequence& order) { int64 position = 0; Item* last = nullptr; - for (const HloInstruction* inst : order) { + for (HloInstruction* inst : order.instructions()) { // Add a new item to the linked list. Item* item = new Item; item->next = nullptr; @@ -151,7 +151,7 @@ class InstructionList { // to be monotonically increasing through the list, and so is still useful // for quickly(-ish) determining the order of arbitrary instructions in // the list. - item->instruction = const_cast(inst); + item->instruction = inst; item->position = position; position++; @@ -927,7 +927,7 @@ Item* PickRematerializationCandidate( StatusOr HloRematerialization::ComputePeakMemory( const HloComputation* computation, - const std::vector& order) const { + const HloInstructionSequence& order) const { InstructionList instruction_list(order); MemoryUsageTracker tracker(computation, size_function_, *points_to_analysis_, instruction_list); @@ -971,8 +971,7 @@ StatusOr HloRematerialization::RematerializeComputation( << HumanReadableNumBytes(computation_peak_memory_.at(computation)); CHECK(!ContainsKey(rematerialized_computations_, computation)); - InstructionList instruction_list( - schedule->sequence(computation).instructions()); + InstructionList instruction_list(schedule->sequence(computation)); MemoryUsageTracker memory_tracker(computation, size_function_, *points_to_analysis_, instruction_list); bool changed = false; @@ -1184,7 +1183,7 @@ StatusOr HloRematerialization::RematerializeComputation( sequence.clear(); for (auto* item = instruction_list.first(); item != nullptr; item = instruction_list.next(item)) { - const HloInstruction* instruction = item->instruction; + HloInstruction* instruction = item->instruction; sequence.push_back(instruction); } rematerialized_computations_.insert(computation); @@ -1235,10 +1234,8 @@ StatusOr HloRematerialization::Run(HloModule* module) { if (node.context() == CallContext::kSequential) { TF_ASSIGN_OR_RETURN( computation_peak_memory_[node.computation()], - ComputePeakMemory(node.computation(), - module->schedule() - .sequence(node.computation()) - .instructions())); + ComputePeakMemory(node.computation(), module->schedule().sequence( + node.computation()))); } return Status::OK(); }, diff --git a/tensorflow/compiler/xla/service/hlo_rematerialization.h b/tensorflow/compiler/xla/service/hlo_rematerialization.h index 70d83c04f0..a07d348041 100644 --- a/tensorflow/compiler/xla/service/hlo_rematerialization.h +++ b/tensorflow/compiler/xla/service/hlo_rematerialization.h @@ -87,9 +87,8 @@ class HloRematerialization : public HloModulePass { // peak memory is the maximum total size of all live HLO instruction values at // any program point. 'order' is the order in which the HLO instructions will // be emitted which is used to determine lifespans of HLO values. - StatusOr ComputePeakMemory( - const HloComputation* computation, - const std::vector& order) const; + StatusOr ComputePeakMemory(const HloComputation* computation, + const HloInstructionSequence& order) const; // Returns the peak memory usage of the called computations for the given // instruction. Zero is returned if the instruction calls no computations. diff --git a/tensorflow/compiler/xla/service/hlo_runner.cc b/tensorflow/compiler/xla/service/hlo_runner.cc index 3f0ca342b4..5a9b820a9d 100644 --- a/tensorflow/compiler/xla/service/hlo_runner.cc +++ b/tensorflow/compiler/xla/service/hlo_runner.cc @@ -205,6 +205,40 @@ StatusOr HloRunner::ExecuteWithDeviceBuffers( /*profile=*/profile); } +StatusOr HloRunner::ExecuteWithDeviceBuffers( + std::unique_ptr executable, + const absl::Span arguments, + ExecutionProfile* profile) { + // Get service run options. + se::Stream stream(backend().default_stream_executor()); + stream.Init(); + ServiceExecutableRunOptions service_run_options = + GetServiceRunOptionsForDevice(backend().default_device_ordinal(), &stream, + nullptr); + + TF_ASSIGN_OR_RETURN( + ScopedShapedBuffer retval, + executable->ExecuteOnStreamWrapper(&service_run_options, + /*profile=*/profile, arguments)); + TF_RETURN_IF_ERROR(stream.BlockHostUntilDone()); + return std::move(retval); +} + +StatusOr HloRunner::ExecuteWithDeviceBuffers( + std::unique_ptr executable, + const absl::Span arguments, + ExecutionProfile* profile) { + std::vector argument_pointers; + argument_pointers.reserve(arguments.size()); + for (const auto& argument : arguments) { + argument_pointers.push_back(&argument); + } + return ExecuteWithDeviceBuffers( + /*executable=*/std::move(executable), + /*arguments=*/argument_pointers, + /*profile=*/profile); +} + StatusOr> HloRunner::ExecuteReplicated( std::unique_ptr module, const ReplicatedExecuteOptions& options) { diff --git a/tensorflow/compiler/xla/service/hlo_runner.h b/tensorflow/compiler/xla/service/hlo_runner.h index 2e934bf66a..bb792cf8c9 100644 --- a/tensorflow/compiler/xla/service/hlo_runner.h +++ b/tensorflow/compiler/xla/service/hlo_runner.h @@ -136,6 +136,21 @@ class HloRunner { const absl::Span arguments, bool run_hlo_passes = true, ExecutionProfile* profile = nullptr); + StatusOr ExecuteWithDeviceBuffers( + std::unique_ptr executable, + const absl::Span arguments, + ExecutionProfile* profile = nullptr); + + StatusOr ExecuteWithDeviceBuffers( + std::unique_ptr executable, + const absl::Span arguments, + ExecutionProfile* profile = nullptr); + + // Creates an executable object given an HLO module. If run_hlo_passes is + // true, the HLO passes will be run as part of compilation. + StatusOr> CreateExecutable( + std::unique_ptr module, bool run_hlo_passes); + // Executes a given HLO module into a set of replicas, and returns a map // with the replica number as key, and the corresponding returned literal as // value. @@ -152,11 +167,6 @@ class HloRunner { const Backend& backend() const; private: - // Creates an executable object given an HLO module. If run_hlo_passes is - // true, the HLO passes will be run before. - StatusOr> CreateExecutable( - std::unique_ptr module, bool run_hlo_passes); - // Creates a ServiceExecutableRunOptions object to configure a run on device, // using the provided stream object. If device_assignment is not nullptr, it // will be used to configure the replication parameters. Replicated executions diff --git a/tensorflow/compiler/xla/service/hlo_schedule.cc b/tensorflow/compiler/xla/service/hlo_schedule.cc index a5780b7551..8f6eb974c5 100644 --- a/tensorflow/compiler/xla/service/hlo_schedule.cc +++ b/tensorflow/compiler/xla/service/hlo_schedule.cc @@ -46,8 +46,8 @@ namespace xla { << "No computation exists in HLO module with id " << computation_id; const HloComputation* computation = comp_it->second; - absl::flat_hash_map id_to_instruction; - for (const HloInstruction* instruction : computation->instructions()) { + absl::flat_hash_map id_to_instruction; + for (HloInstruction* instruction : computation->instructions()) { id_to_instruction[instruction->unique_id()] = instruction; } @@ -81,9 +81,8 @@ StatusOr HloSchedule::ToProto() const { return std::move(proto); } -void HloSchedule::set_sequence( - const HloComputation* computation, - absl::Span sequence) { +void HloSchedule::set_sequence(const HloComputation* computation, + absl::Span sequence) { set_sequence(computation, HloInstructionSequence(sequence)); } @@ -114,8 +113,8 @@ Status HloSchedule::UpdateComputationSchedule( const HloComputation* computation) { // Map from unique ID to HloInstruction pointer for instructions in the // computation. - absl::flat_hash_map id_to_instruction; - for (const HloInstruction* instruction : computation->instructions()) { + absl::flat_hash_map id_to_instruction; + for (HloInstruction* instruction : computation->instructions()) { InsertOrDie(&id_to_instruction, instruction->unique_id(), instruction); } @@ -128,7 +127,7 @@ Status HloSchedule::UpdateComputationSchedule( // Map from HloInstruction X to newly added instructions (instruction is in // computation, but not in schedule) which use X. If an instruction is not in // the map, then it has no users which are newly added instructions. - absl::flat_hash_map> + absl::flat_hash_map> new_instruction_uses; // For each newly added instruction, this is the count of the instruction's @@ -138,9 +137,9 @@ Status HloSchedule::UpdateComputationSchedule( // Create a worklist of newly added instructions which are ready to be added // to the schedule. Initialize worklist with those that have zero operands. - std::queue worklist; + std::queue worklist; - for (const HloInstruction* instruction : computation->instructions()) { + for (HloInstruction* instruction : computation->instructions()) { if (ids_in_schedule.count(instruction->unique_id()) == 0) { // This is a newly added instruction which is not in the schedule. if (instruction->operands().empty()) { @@ -161,17 +160,17 @@ Status HloSchedule::UpdateComputationSchedule( // Lambda which schedules all instructions on the worklist. auto schedule_worklist = [&]() { while (!worklist.empty()) { - const HloInstruction* instruction = worklist.front(); + HloInstruction* instruction = worklist.front(); worklist.pop(); new_sequence.push_back(instruction); - std::vector* new_users = + std::vector* new_users = tensorflow::gtl::FindOrNull(new_instruction_uses, instruction); if (new_users != nullptr) { // This just-scheduled instruction has users which are newly added to // the module. Update the number of unscheduled operands and push the // newly added instruction to the worklist if it is ready to // schedule. - for (const HloInstruction* new_user : *new_users) { + for (HloInstruction* new_user : *new_users) { unscheduled_operand_count.at(new_user)--; CHECK_GE(unscheduled_operand_count.at(new_user), 0); if (unscheduled_operand_count.at(new_user) == 0) { diff --git a/tensorflow/compiler/xla/service/hlo_schedule.h b/tensorflow/compiler/xla/service/hlo_schedule.h index 0a714101ee..486ddbf499 100644 --- a/tensorflow/compiler/xla/service/hlo_schedule.h +++ b/tensorflow/compiler/xla/service/hlo_schedule.h @@ -35,14 +35,14 @@ class HloInstructionSequence { public: HloInstructionSequence() = default; explicit HloInstructionSequence( - absl::Span instructions) { - for (const HloInstruction* instruction : instructions) { + absl::Span instructions) { + for (HloInstruction* instruction : instructions) { push_back(instruction); } } // Adds the instruction to the end of the sequence. - void push_back(const HloInstruction* instruction) { + void push_back(HloInstruction* instruction) { instruction_sequence_.push_back(instruction); id_sequence_.push_back(instruction->unique_id()); } @@ -56,7 +56,7 @@ class HloInstructionSequence { int64 size() const { return instruction_sequence_.size(); } // Returns the sequence of HLO instructions. - const std::vector& instructions() const { + const std::vector& instructions() const { return instruction_sequence_; } @@ -65,7 +65,7 @@ class HloInstructionSequence { private: // The sequence as HloInstructions. - std::vector instruction_sequence_; + std::vector instruction_sequence_; // The sequence of HLO instructions, represented by their unique IDs. The // sequence is stored as both HloInstructions and unique IDs because the @@ -98,7 +98,7 @@ class HloSchedule { // Sets the sequence for the given computation to the given sequence. void set_sequence(const HloComputation* computation, - absl::Span sequence); + absl::Span sequence); void set_sequence(const HloComputation* computation, HloInstructionSequence sequence); diff --git a/tensorflow/compiler/xla/service/hlo_schedule_test.cc b/tensorflow/compiler/xla/service/hlo_schedule_test.cc index 1424569ac1..0e56e6f760 100644 --- a/tensorflow/compiler/xla/service/hlo_schedule_test.cc +++ b/tensorflow/compiler/xla/service/hlo_schedule_test.cc @@ -56,10 +56,10 @@ ENTRY main { ParseHloString(module_str)); TF_ASSERT_OK_AND_ASSIGN( HloSchedule schedule, - ScheduleModule(*module, [](const BufferValue& buffer) { + ScheduleModule(module.get(), [](const BufferValue& buffer) { return ShapeUtil::ByteSizeOf(buffer.shape()); })); - const std::vector& entry_schedule = + const auto& entry_schedule = schedule.sequence(module->entry_computation()).instructions(); EXPECT_EQ(entry_schedule.size(), 6); @@ -90,7 +90,7 @@ ENTRY main { ParseHloString(module_str)); TF_ASSERT_OK_AND_ASSIGN( HloSchedule schedule, - ScheduleModule(*module, [](const BufferValue& buffer) { + ScheduleModule(module.get(), [](const BufferValue& buffer) { return ShapeUtil::ByteSizeOf(buffer.shape()); })); @@ -139,7 +139,7 @@ ENTRY main { ParseHloString(module_str)); TF_ASSERT_OK_AND_ASSIGN( HloSchedule schedule, - ScheduleModule(*module, [](const BufferValue& buffer) { + ScheduleModule(module.get(), [](const BufferValue& buffer) { return ShapeUtil::ByteSizeOf(buffer.shape()); })); @@ -183,7 +183,7 @@ ENTRY main { ParseHloString(module_str)); TF_ASSERT_OK_AND_ASSIGN( HloSchedule schedule, - ScheduleModule(*module, [](const BufferValue& buffer) { + ScheduleModule(module.get(), [](const BufferValue& buffer) { return ShapeUtil::ByteSizeOf(buffer.shape()); })); @@ -244,7 +244,7 @@ ENTRY %WhileLoop () -> s32[] { ParseHloString(module_str)); TF_ASSERT_OK_AND_ASSIGN( HloSchedule schedule, - ScheduleModule(*module, [](const BufferValue& buffer) { + ScheduleModule(module.get(), [](const BufferValue& buffer) { return ShapeUtil::ByteSizeOf(buffer.shape(), /*pointer_size=*/sizeof(void*)); })); @@ -313,7 +313,7 @@ ENTRY %WhileLoop () -> s32[] { ParseHloString(module_str)); TF_ASSERT_OK_AND_ASSIGN( HloSchedule schedule, - ScheduleModule(*module, [](const BufferValue& buffer) { + ScheduleModule(module.get(), [](const BufferValue& buffer) { return ShapeUtil::ByteSizeOf(buffer.shape(), /*pointer_size=*/sizeof(void*)); })); diff --git a/tensorflow/compiler/xla/service/hlo_sharding_metadata.cc b/tensorflow/compiler/xla/service/hlo_sharding_metadata.cc index 88329c8997..f506130445 100644 --- a/tensorflow/compiler/xla/service/hlo_sharding_metadata.cc +++ b/tensorflow/compiler/xla/service/hlo_sharding_metadata.cc @@ -253,7 +253,7 @@ StatusOr ApplyShardingFromUsers(HloInstruction* instruction, instruction->shape(), HloSharding::AssignDevice(kUnassignedDevice)); for (HloInstruction* user : instruction->users()) { if (user->opcode() == HloOpcode::kDomain && - domain.exit_domains.count(const_cast(user)) > 0) { + domain.exit_domains.count(user) > 0) { // If a user is a domain and it is registered in the domain exits, then // the instruction sharding is taken directly from the domain, and no // further users need to be visited. diff --git a/tensorflow/compiler/xla/service/hlo_subcomputation_unification_test.cc b/tensorflow/compiler/xla/service/hlo_subcomputation_unification_test.cc index 11994d99c9..c1073911ea 100644 --- a/tensorflow/compiler/xla/service/hlo_subcomputation_unification_test.cc +++ b/tensorflow/compiler/xla/service/hlo_subcomputation_unification_test.cc @@ -66,7 +66,7 @@ class HloSubcomputationUnificationTest : public HloTestBase { }; TEST_F(HloSubcomputationUnificationTest, UnifyIdentities) { - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto builder = HloComputation::Builder(TestName()); auto callee1 = @@ -103,7 +103,7 @@ TEST_F(HloSubcomputationUnificationTest, UnifyIdentities) { } TEST_F(HloSubcomputationUnificationTest, UnifyAdditions) { - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto builder = HloComputation::Builder(TestName()); auto callee1 = @@ -184,7 +184,7 @@ TEST_F(HloSubcomputationUnificationTest, DifferentParameterShapes) { // Regression test for b/31466798. Checks that entry_computation is still valid // after unification. TEST_F(HloSubcomputationUnificationTest, TwoIdenticalComputations) { - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); for (int i = 0; i < 2; ++i) { HloComputation::Builder builder("pow"); auto x = diff --git a/tensorflow/compiler/xla/service/hlo_value.h b/tensorflow/compiler/xla/service/hlo_value.h index b6670d409b..1f01b0bb36 100644 --- a/tensorflow/compiler/xla/service/hlo_value.h +++ b/tensorflow/compiler/xla/service/hlo_value.h @@ -166,9 +166,6 @@ class HloValue : public BufferValue { // Whether this value is live out of the HLO module. bool live_out_of_module_ = false; - - // Whether this value is live out of its computation. - bool live_out_of_computation_ = false; }; std::ostream& operator<<(std::ostream& out, const HloValue& hlo_value); diff --git a/tensorflow/compiler/xla/service/hlo_verifier.cc b/tensorflow/compiler/xla/service/hlo_verifier.cc index 27fd685a69..77db7b098a 100644 --- a/tensorflow/compiler/xla/service/hlo_verifier.cc +++ b/tensorflow/compiler/xla/service/hlo_verifier.cc @@ -753,13 +753,19 @@ Status ShapeVerifier::HandleAfterAll(HloInstruction* token) { for (const HloInstruction* operand : token->operands()) { operand_shapes.push_back(&operand->shape()); } - return CheckShape(token, ShapeInference::InferAfterAllShape(operand_shapes)); + return CheckShape(token, ShapeUtil::MakeTokenShape()); +} + +Status ShapeVerifier::HandleAddDependency(HloInstruction* add_dependency) { + TF_RETURN_IF_ERROR(CheckOperandCount(add_dependency, 2)); + TF_RETURN_IF_ERROR(CheckIsTokenOperand(add_dependency, 1)); + return CheckShape(add_dependency, add_dependency->operand(0)->shape()); } Status ShapeVerifier::HandleGetDimensionSize(HloInstruction* get_size) { - return CheckShape( - get_size, ShapeInference::InferGetDimensionSizeShape( - get_size->operand(0)->shape(), get_size->dimensions(0))); + return CheckShape(get_size, + ShapeInference::InferGetDimensionSizeShape( + get_size->operand(0)->shape(), get_size->dimension())); } Status ShapeVerifier::CheckShape(const HloInstruction* instruction, @@ -1373,9 +1379,8 @@ class InstructionVerifier : public DfsHloVisitorWithDefault { const Layout& operand_layout = operand_shape.layout(); TF_RET_CHECK(LayoutUtil::Equal(result_layout, operand_layout)) << "Instruction shouldn't change layouts " - << instruction->ToString() << " From " - << ShapeUtil::HumanString(result_shape) << " To " - << ShapeUtil::HumanString(operand_shape); + << instruction->ToString() << " From " << result_shape << " To " + << operand_shape; } } } @@ -1426,6 +1431,8 @@ StatusOr HloVerifier::Run(HloModule* module) { return target_metadata_->ShapeSize(shape); })); + TF_RETURN_IF_ERROR(module->dynamic_parameter_binding().Verify(*module)); + return false; } diff --git a/tensorflow/compiler/xla/service/hlo_verifier.h b/tensorflow/compiler/xla/service/hlo_verifier.h index 9fbfd6a21c..e4d0c3d695 100644 --- a/tensorflow/compiler/xla/service/hlo_verifier.h +++ b/tensorflow/compiler/xla/service/hlo_verifier.h @@ -95,6 +95,7 @@ class ShapeVerifier : public DfsHloVisitor { Status HandleScatter(HloInstruction* scatter) override; Status HandleAfterAll(HloInstruction* token) override; Status HandleGetDimensionSize(HloInstruction* get_size) override; + Status HandleAddDependency(HloInstruction* add_dependency) override; Status FinishVisit(HloInstruction*) override { return Status::OK(); } diff --git a/tensorflow/compiler/xla/service/hlo_verifier_test.cc b/tensorflow/compiler/xla/service/hlo_verifier_test.cc index 5ddfe0a944..4bc557e4e6 100644 --- a/tensorflow/compiler/xla/service/hlo_verifier_test.cc +++ b/tensorflow/compiler/xla/service/hlo_verifier_test.cc @@ -35,6 +35,10 @@ namespace { using ::testing::HasSubstr; +std::unique_ptr CreateUnverifiedModule() { + return absl::make_unique("module", HloModuleConfig()); +} + // This class cannot be converted to use HloTestBase. It explicitly // uses HloTestBase to create and test malformed HLOs. class HloVerifierTest : public HloTestBase { @@ -66,7 +70,7 @@ TEST_F(HloVerifierTest, NullInstructionParent) { HloInstruction::CreateParameter(0, scalar_shape, "param")); HloInstruction* negate = builder.AddInstruction( HloInstruction::CreateUnary(scalar_shape, HloOpcode::kNegate, param)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateUnverifiedModule(); module->AddEntryComputation(builder.Build()); TF_ASSERT_OK(verifier().Run(module.get()).status()); @@ -85,7 +89,7 @@ TEST_F(HloVerifierTest, NullComputationParent) { HloInstruction::CreateParameter(0, scalar_shape, "param")); builder.AddInstruction( HloInstruction::CreateUnary(scalar_shape, HloOpcode::kNegate, param)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateUnverifiedModule(); HloComputation* computation = module->AddEntryComputation(builder.Build()); TF_ASSERT_OK(verifier().Run(module.get()).status()); @@ -104,7 +108,7 @@ TEST_F(HloVerifierTest, DifferentOperandParents) { HloInstruction::CreateParameter(0, scalar_shape, "param")); HloInstruction* negate = builder.AddInstruction( HloInstruction::CreateUnary(scalar_shape, HloOpcode::kNegate, param)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateUnverifiedModule(); module->AddEntryComputation(builder.Build()); HloComputation::Builder emb_builder(TestName()); @@ -138,7 +142,7 @@ TEST_F(HloVerifierTest, ResetsShapeVerifierState) { builder.AddInstruction( HloInstruction::CreateBinary(s2, HloOpcode::kMultiply, add, add)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateUnverifiedModule(); module->AddEntryComputation(builder.Build()); // Run the verifier twice. It should fail both times, because it shouldn't @@ -303,7 +307,7 @@ TEST_F(HloVerifierTest, NegativeInteriorPaddingNotAllowed) { HloInstruction::CreateConstant(LiteralUtil::Zero(F32))), padding_config)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateUnverifiedModule(); module->AddEntryComputation(builder.Build()); auto status = verifier().Run(module.get()).status(); @@ -327,7 +331,7 @@ TEST_F(HloVerifierTest, PadNegativeInteriorDilationNotAllowed) { HloInstruction::CreateConstant(LiteralUtil::Zero(F32).Clone())), padding_config)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateUnverifiedModule(); module->AddEntryComputation(builder.Build()); EXPECT_THAT(verifier().Run(module.get()).status().error_message(), diff --git a/tensorflow/compiler/xla/service/indexed_array_analysis_test.cc b/tensorflow/compiler/xla/service/indexed_array_analysis_test.cc index 20cc18f981..98246d5403 100644 --- a/tensorflow/compiler/xla/service/indexed_array_analysis_test.cc +++ b/tensorflow/compiler/xla/service/indexed_array_analysis_test.cc @@ -481,8 +481,8 @@ ENTRY main { const char* expected_root_expression = R"( (scalar-indexed-const (constant s32[2,1,1,1,6] s32[2,1,1,1,6] { - { /*i0=0*/ { /*i1=0*/ { /*i2=0*/ {1, 2, 3, 4, 5, 6} } } }, - { /*i0=1*/ { /*i1=0*/ { /*i2=0*/ {1, 2, 3, 4, 5, 6} } } } }) + { /*i0=0*/ { /*i1=0*/ { /*i2=0*/ { 1, 2, 3, 4, 5, 6 } } } }, + { /*i0=1*/ { /*i1=0*/ { /*i2=0*/ { 1, 2, 3, 4, 5, 6 } } } } }) (reshape %indices to s32[]) 0->[]) )"; @@ -512,8 +512,8 @@ ENTRY main { const char* expected_root_expression = R"( (scalar-indexed-const (constant s32[2,1,1,6] s32[2,1,1,6] { - { /*i0=0*/ { /*i1=0*/ {1, 2, 3, 4, 5, 6} } }, - { /*i0=1*/ { /*i1=0*/ {1, 2, 3, 4, 5, 6} } } }) + { /*i0=0*/ { /*i1=0*/ { 1, 2, 3, 4, 5, 6 } } }, + { /*i0=1*/ { /*i1=0*/ { 1, 2, 3, 4, 5, 6 } } } }) (reshape %indices to s32[5]) 0->[2]) )"; diff --git a/tensorflow/compiler/xla/service/instruction_fusion.cc b/tensorflow/compiler/xla/service/instruction_fusion.cc index 7f2d7e7cff..7559ed1bab 100644 --- a/tensorflow/compiler/xla/service/instruction_fusion.cc +++ b/tensorflow/compiler/xla/service/instruction_fusion.cc @@ -103,7 +103,6 @@ bool IsAlwaysDuplicable(const HloInstruction& instruction) { case HloOpcode::kShiftRightLogical: case HloOpcode::kSlice: case HloOpcode::kSubtract: - case HloOpcode::kAfterAll: case HloOpcode::kTranspose: case HloOpcode::kTuple: case HloOpcode::kTupleSelect: @@ -116,7 +115,10 @@ bool IsAlwaysDuplicable(const HloInstruction& instruction) { case HloOpcode::kSin: return ShapeUtil::ElementIsComplex(instruction.shape()); - // Expensive instructions. + // Expensive instructions or unusual instructions for which fusion is + // nonsensical. + case HloOpcode::kAddDependency: + case HloOpcode::kAfterAll: case HloOpcode::kAtan2: case HloOpcode::kBatchNormGrad: case HloOpcode::kBatchNormInference: @@ -455,8 +457,13 @@ StatusOr InstructionFusion::Run(HloModule* module) { computation_ = computation; reachability_ = HloReachabilityMap::Build(computation_); - HloInstructionSet do_not_duplicate = - ComputeGloballyUnfusible(computation_->MakeInstructionPostOrder()); + HloInstructionSet do_not_duplicate; + // If we allow duplications, we need to compute which instructions we do not + // want to duplicate based on a global analysis of the graph. + if (may_duplicate_) { + do_not_duplicate = + ComputeGloballyUnfusible(computation_->MakeInstructionPostOrder()); + } auto fusion_queue = GetFusionQueue(computation_); // Instruction fusion effectively fuses edges in the computation graph @@ -564,8 +571,8 @@ HloInstruction* InstructionFusion::FuseIntoMultiOutput( bool InstructionFusion::MultiOutputFusionCreatesCycle( HloInstruction* producer, HloInstruction* consumer) { auto is_reachable = [&](const HloInstruction* a, const HloInstruction* b) { - // A consumer operand may have been multii-output fused into a parallel - // consumer and thus be missing from the oridinal reachability map. + // A consumer operand may have been multi-output fused into a parallel + // consumer and thus be missing from the original reachability map. if (!reachability_->IsPresent(a) || !reachability_->IsPresent(b)) { reachability_ = HloReachabilityMap::Build(consumer->parent()); } diff --git a/tensorflow/compiler/xla/service/instruction_fusion_test.cc b/tensorflow/compiler/xla/service/instruction_fusion_test.cc index 39904bd54b..58b7135cea 100644 --- a/tensorflow/compiler/xla/service/instruction_fusion_test.cc +++ b/tensorflow/compiler/xla/service/instruction_fusion_test.cc @@ -117,7 +117,7 @@ TEST_F(InstructionFusionTest, PotentialBitcastReshapeOfParameterUnfused) { auto reshape1 = builder.AddInstruction( HloInstruction::CreateReshape(ShapeUtil::MakeShape(S32, {1, 1}), param0)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); EXPECT_EQ(reshape1, computation->root_instruction()); EXPECT_FALSE( @@ -133,7 +133,7 @@ TEST_F(InstructionFusionTest, PotentialBitcastSimpleReshapeOfParameterUnfused) { auto reshape1 = builder.AddInstruction( HloInstruction::CreateReshape(ShapeUtil::MakeShape(S32, {1, 1}), param0)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); EXPECT_EQ(reshape1, computation->root_instruction()); EXPECT_FALSE( @@ -149,7 +149,7 @@ TEST_F(InstructionFusionTest, PotentialBitcastTransposeOfParameterUnfused) { auto transpose1 = builder.AddInstruction(HloInstruction::CreateTranspose( ShapeUtil::MakeShape(S32, {}), param0, {})); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); EXPECT_EQ(transpose1, computation->root_instruction()); EXPECT_FALSE( @@ -394,6 +394,56 @@ TEST_F(InstructionFusionTest, AllowEffectiveUnaryDuplication) { .ValueOrDie()); } +TEST_F(InstructionFusionTest, FuseDiamondGraphsNoDuplication) { + auto module = ParseHloString(R"( + HloModule test_module + ENTRY Test { + p0 = f32[100] parameter(0) + p1 = f32[100] parameter(1) + add = f32[100] add(p0, p1) + slice1 = f32[99] slice(add), slice={[0:99:1]} + slice2 = f32[99] slice(add), slice={[1:100:1]} + ROOT add2 = f32[99] add(slice1, slice2) + })") + .ValueOrDie(); + EXPECT_TRUE( + InstructionFusion(InstructionFusion::IsExpensive, /*may_duplicate=*/false) + .Run(module.get()) + .ValueOrDie()) + << module->ToString(); + + HloInstruction* root = module->entry_computation()->root_instruction(); + // 'add' would originally need to be duplicated if fused. However after its + // two users 'slice1' and 'slice2' are fused into 'add2', 'add' has only one + // user and can now be also fused. + EXPECT_THAT(root, op::Fusion(op::Parameter(), op::Parameter())); +} + +TEST_F(InstructionFusionTest, FuseDiamondGraphsAllowDuplication) { + auto module = ParseHloString(R"( + HloModule test_module + ENTRY Test { + p0 = f32[100] parameter(0) + p1 = f32[100] parameter(1) + add = f32[100] add(p0, p1) + slice1 = f32[99] slice(add), slice={[0:99:1]} + slice2 = f32[99] slice(add), slice={[1:100:1]} + ROOT add2 = f32[99] add(slice1, slice2) + })") + .ValueOrDie(); + EXPECT_TRUE( + InstructionFusion(InstructionFusion::IsExpensive, /*may_duplicate=*/true) + .Run(module.get()) + .ValueOrDie()) + << module->ToString(); + + HloInstruction* root = module->entry_computation()->root_instruction(); + // 'add' would originally need to be duplicated if fused. However after its + // two users 'slice1' and 'slice2' are fused into 'add2', 'add' has only one + // user and can now be also fused. + EXPECT_THAT(root, op::Fusion(op::Parameter(), op::Parameter())); +} + TEST_F(InstructionFusionTest, WideningConvertsAreAlwaysDuplicableIntoConsumers) { auto module = ParseHloString(R"( diff --git a/tensorflow/compiler/xla/service/interpreter/executable.cc b/tensorflow/compiler/xla/service/interpreter/executable.cc index a06d6113e8..7635fbfed6 100644 --- a/tensorflow/compiler/xla/service/interpreter/executable.cc +++ b/tensorflow/compiler/xla/service/interpreter/executable.cc @@ -37,7 +37,7 @@ namespace xla { namespace interpreter { InterpreterExecutable::InterpreterExecutable( - std::unique_ptr hlo_module, + std::unique_ptr hlo_module, std::unique_ptr evaluator) : Executable(std::move(hlo_module), /*hlo_profile_printer=*/nullptr, /*hlo_profile_index_map=*/nullptr), diff --git a/tensorflow/compiler/xla/service/interpreter/executable.h b/tensorflow/compiler/xla/service/interpreter/executable.h index 3b1ebce0c7..bda13d3763 100644 --- a/tensorflow/compiler/xla/service/interpreter/executable.h +++ b/tensorflow/compiler/xla/service/interpreter/executable.h @@ -42,7 +42,7 @@ namespace interpreter { // buffer allocation. Refer to interpreter/README.md for more. class InterpreterExecutable : public Executable { public: - InterpreterExecutable(std::unique_ptr hlo_module, + InterpreterExecutable(std::unique_ptr hlo_module, std::unique_ptr evaluator); ~InterpreterExecutable() override; diff --git a/tensorflow/compiler/xla/service/interpreter/executor.cc b/tensorflow/compiler/xla/service/interpreter/executor.cc index 4fb67bd0b7..e3e5fa7154 100644 --- a/tensorflow/compiler/xla/service/interpreter/executor.cc +++ b/tensorflow/compiler/xla/service/interpreter/executor.cc @@ -78,9 +78,14 @@ port::Status XlaInterpreterExecutor::SynchronousMemcpy( return port::Status::OK(); } -bool XlaInterpreterExecutor::HostCallback(Stream *stream, - std::function callback) { - AsExecutorStream(stream)->EnqueueTask(callback); +bool XlaInterpreterExecutor::HostCallback( + Stream *stream, std::function callback) { + AsExecutorStream(stream)->EnqueueTask([callback]() { + port::Status s = callback(); + if (!s.ok()) { + LOG(WARNING) << "Host callback failed: " << s; + } + }); return true; } diff --git a/tensorflow/compiler/xla/service/interpreter/executor.h b/tensorflow/compiler/xla/service/interpreter/executor.h index fbb9945784..400c305154 100644 --- a/tensorflow/compiler/xla/service/interpreter/executor.h +++ b/tensorflow/compiler/xla/service/interpreter/executor.h @@ -125,7 +125,8 @@ class XlaInterpreterExecutor : public internal::StreamExecutorInterface { return port::Status{port::error::UNIMPLEMENTED, ""}; } - bool HostCallback(Stream *stream, std::function callback) override; + bool HostCallback(Stream *stream, + std::function callback) override; port::Status AllocateEvent(Event *event) override { return port::Status{port::error::UNIMPLEMENTED, ""}; diff --git a/tensorflow/compiler/xla/service/layout_assignment.cc b/tensorflow/compiler/xla/service/layout_assignment.cc index a904119222..eddef850cf 100644 --- a/tensorflow/compiler/xla/service/layout_assignment.cc +++ b/tensorflow/compiler/xla/service/layout_assignment.cc @@ -2000,6 +2000,7 @@ bool LayoutAssignment::InstructionCanChangeLayout( switch (instruction->opcode()) { case HloOpcode::kAbs: case HloOpcode::kAdd: + case HloOpcode::kAddDependency: case HloOpcode::kAnd: case HloOpcode::kAtan2: case HloOpcode::kBitcastConvert: diff --git a/tensorflow/compiler/xla/service/layout_assignment_test.cc b/tensorflow/compiler/xla/service/layout_assignment_test.cc index 2400b7bb7c..311bd78905 100644 --- a/tensorflow/compiler/xla/service/layout_assignment_test.cc +++ b/tensorflow/compiler/xla/service/layout_assignment_test.cc @@ -31,6 +31,8 @@ limitations under the License. #include "tensorflow/compiler/xla/service/hlo_module.h" #include "tensorflow/compiler/xla/service/hlo_opcode.h" #include "tensorflow/compiler/xla/service/hlo_parser.h" +#include "tensorflow/compiler/xla/service/pattern_matcher.h" +#include "tensorflow/compiler/xla/service/pattern_matcher_gmock.h" #include "tensorflow/compiler/xla/shape_layout.h" #include "tensorflow/compiler/xla/shape_util.h" #include "tensorflow/compiler/xla/test.h" @@ -42,11 +44,10 @@ limitations under the License. #include "tensorflow/core/lib/core/status.h" #include "tensorflow/core/lib/core/status_test_util.h" -namespace op = xla::testing::opcode_matchers; - namespace xla { namespace { +namespace m = xla::match; using ::testing::ElementsAre; class LayoutAssignmentTest : public HloTestBase { @@ -328,11 +329,10 @@ TEST_F(LayoutAssignmentTest, ConflictingLayoutTuple) { // %tuple.1 = Tuple(%copy) layout=({0,1}) // %tuple.2 = Tuple(%tuple.0, %tuple.1) layout=(({1,0}), ({0,1})) // - EXPECT_TRUE( - AlgebraicSimplifier(/*is_layout_sensitive=*/true, - [](const Shape&, const Shape&) { return false; }) - .Run(m.get()) - .ValueOrDie()); + AlgebraicSimplifierOptions options( + [](const Shape&, const Shape&) { return false; }); + options.set_is_layout_sensitive(true); + EXPECT_TRUE(AlgebraicSimplifier(options).Run(m.get()).ValueOrDie()); HloInstruction* root = m->entry_computation()->root_instruction(); // Verify layout of the root and the root's operands. EXPECT_TRUE(ShapeUtil::Equal(result_shape, root->shape())); @@ -343,7 +343,8 @@ TEST_F(LayoutAssignmentTest, ConflictingLayoutTuple) { // Verify the structure of the HLO graph. EXPECT_THAT(root, - op::Tuple(op::Tuple(constant), op::Tuple(op::Copy(constant)))); + GmockMatch(m::Tuple(m::Tuple(m::Op().Is(constant)), + m::Tuple(m::Copy(m::Op().Is(constant)))))); } TEST_F(LayoutAssignmentTest, ElementwiseAndReshape) { @@ -947,9 +948,11 @@ TEST_F(LayoutAssignmentTest, CopySliceOperandToAvoidImplicitLayoutChange) { HloInstruction* root = compiled_module->entry_computation()->root_instruction(); Shape shape_copy = ShapeUtil::MakeShapeWithLayout(F32, {4, 5}, {1, 0}); - EXPECT_THAT(root, op::Add(op::Parameter(), - op::Slice(AllOf(op::Copy(op::Parameter(1)), - op::ShapeWithLayout(shape_copy))))); + EXPECT_THAT( + root, + GmockMatch(m::Add( + m::Parameter(), + m::Slice(m::Copy(m::Parameter(1)).WithShapeEqualTo(&shape_copy))))); } TEST_F(LayoutAssignmentTest, CopyDSliceOperandToAvoidImplicitLayoutChange) { @@ -977,10 +980,11 @@ TEST_F(LayoutAssignmentTest, CopyDSliceOperandToAvoidImplicitLayoutChange) { compiled_module->entry_computation()->root_instruction(); Shape shape_copy = ShapeUtil::MakeShapeWithLayout(F32, {4, 5}, {1, 0}); EXPECT_THAT(root, - op::Add(op::Parameter(), - op::DynamicSlice(AllOf(op::Copy(op::Parameter(1)), - op::ShapeWithLayout(shape_copy)), - op::Parameter(2)))); + GmockMatch(m::Add( + m::Parameter(), + m::DynamicSlice( + m::Copy(m::Parameter(1)).WithShapeEqualTo(&shape_copy), + m::Parameter(2))))); } TEST_F(LayoutAssignmentTest, CopyConcatOperandToAvoidImplicitLayoutChange) { @@ -1008,11 +1012,12 @@ TEST_F(LayoutAssignmentTest, CopyConcatOperandToAvoidImplicitLayoutChange) { HloInstruction* root = compiled_module->entry_computation()->root_instruction(); Shape shape_copy = ShapeUtil::MakeShapeWithLayout(F32, {3, 5}, {1, 0}); - EXPECT_THAT(root, - op::Add(op::Parameter(), - op::Concatenate(AllOf(op::Copy(op::Parameter(1)), - op::ShapeWithLayout(shape_copy)), - op::Parameter(2)))); + EXPECT_THAT( + root, + GmockMatch(m::Add( + m::Parameter(), + m::Concatenate(m::Copy(m::Parameter(1)).WithShapeEqualTo(&shape_copy), + m::Parameter(2))))); } TEST_F(LayoutAssignmentTest, @@ -1039,7 +1044,8 @@ TEST_F(LayoutAssignmentTest, .ConsumeValueOrDie(); HloInstruction* root = compiled_module->entry_computation()->root_instruction(); - EXPECT_THAT(root, op::Convolution(op::Parameter(0), op::Parameter(1))); + EXPECT_THAT(root, + GmockMatch(m::Convolution(m::Parameter(0), m::Parameter(1)))); } TEST_F(LayoutAssignmentTest, PropagatingLayoutFromResultToOperand) { @@ -1063,8 +1069,9 @@ TEST_F(LayoutAssignmentTest, PropagatingLayoutFromResultToOperand) { HloInstruction* root = compiled_module->entry_computation()->root_instruction(); Shape shape_copy = ShapeUtil::MakeShapeWithLayout(F32, {4, 5}, {0, 1}); - EXPECT_THAT(root, op::Slice(AllOf(op::Copy(op::Parameter(0)), - op::ShapeWithLayout(shape_copy)))); + EXPECT_THAT(root, + GmockMatch(m::Slice( + m::Copy(m::Parameter(0)).WithShapeEqualTo(&shape_copy)))); } TEST_F(LayoutAssignmentTest, TupleCopyOnLayoutMismatch) { @@ -1150,7 +1157,7 @@ ENTRY %CustomCallWithNotLayoutConstrained (p: f32[42,2,3]) -> f32[1,2,3,4] { AssignLayouts(m.get(), &computation_layout); HloInstruction* root = m->entry_computation()->root_instruction(); - ASSERT_THAT(root, op::CustomCall(op::Parameter())); + ASSERT_THAT(root, GmockMatch(m::CustomCall(m::Parameter()))); ExpectLayoutIs(root->shape(), {3, 2, 0, 1}); ExpectLayoutIs(root->operand(0)->shape(), {0, 2, 1}); } @@ -1166,7 +1173,7 @@ ENTRY %CustomCallWithNotLayoutConstrained (p: f32[42,2,3]) -> f32[1,2,3,4] { AssignLayouts(m.get(), &computation_layout); HloInstruction* root = m->entry_computation()->root_instruction(); - ASSERT_THAT(root, op::CustomCall(op::Parameter())); + ASSERT_THAT(root, GmockMatch(m::CustomCall(m::Parameter()))); ExpectLayoutIs(root->shape(), {0, 2, 3, 1}); ExpectLayoutIs(root->operand(0)->shape(), {0, 1, 2}); } @@ -1197,7 +1204,7 @@ ENTRY %CustomCallWithLayoutConstraints (p0: f32[4,4], p1: f32[2,3]) -> f32[1,2,3 // The custom call should be partially encapsulated in kCopy instructions // because of the layout mismatches. ASSERT_THAT(m->entry_computation()->root_instruction(), - op::Copy(op::CustomCall(op::Copy(), op::Parameter()))); + GmockMatch(m::Copy(m::CustomCall(m::Copy(), m::Parameter())))); const HloInstruction* custom_call = m->entry_computation()->root_instruction()->operand(0); @@ -1223,7 +1230,7 @@ ENTRY %CustomCallLayoutConstrainedZeroOperands () -> f32[1,2,3,4] { AssignLayouts(m.get(), &computation_layout); ASSERT_THAT(m->entry_computation()->root_instruction(), - op::Copy(op::CustomCall())); + GmockMatch(m::Copy(m::CustomCall()))); const HloInstruction* custom_call = m->entry_computation()->root_instruction()->operand(0); @@ -1257,7 +1264,7 @@ ENTRY %CustomCallLayoutConstrainedTupleOperand (p0: f32[4,4], p1: f32[2,3]) -> f ExpectLayoutIs(root->shape(), {2, 1, 0, 3}); ASSERT_THAT(m->entry_computation()->root_instruction(), - op::Copy(op::CustomCall(op::Tuple()))); + GmockMatch(m::Copy(m::CustomCall(m::Tuple())))); const HloInstruction* custom_call = m->entry_computation()->root_instruction()->operand(0); diff --git a/tensorflow/compiler/xla/service/llvm_ir/ir_array.h b/tensorflow/compiler/xla/service/llvm_ir/ir_array.h index f4b05f29c3..d6d84994ee 100644 --- a/tensorflow/compiler/xla/service/llvm_ir/ir_array.h +++ b/tensorflow/compiler/xla/service/llvm_ir/ir_array.h @@ -25,6 +25,7 @@ limitations under the License. #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Value.h" #include "tensorflow/compiler/xla/map_util.h" +#include "tensorflow/compiler/xla/shape.h" #include "tensorflow/compiler/xla/types.h" #include "tensorflow/compiler/xla/xla_data.pb.h" #include "tensorflow/core/platform/logging.h" @@ -108,6 +109,14 @@ class IrArray { Index(absl::Span multidim, llvm::Value* linear, const Shape& shape); + // Returns an index that adds `addend` to the given `dim` of the object. + Index AddOffsetToDim(llvm::Value* addend, int64 dim, + llvm::IRBuilder<>* b) const { + IrArray::Index index = *this; + index[dim] = b->CreateAdd(index[dim], addend); + return index; + } + const std::vector& multidim() const { return multidim_; } llvm::Value* linear() const { return linear_; } diff --git a/tensorflow/compiler/xla/service/llvm_ir/kernel_tiling.cc b/tensorflow/compiler/xla/service/llvm_ir/kernel_tiling.cc index e5fbdbd51b..1aa85eb8d2 100644 --- a/tensorflow/compiler/xla/service/llvm_ir/kernel_tiling.cc +++ b/tensorflow/compiler/xla/service/llvm_ir/kernel_tiling.cc @@ -52,6 +52,29 @@ Shape MergeDimensions(absl::Span segs, const Shape& shape) { return ShapeUtil::MakeShapeWithDescendingLayout(shape.element_type(), dimensions); } + +// Given an index for a shape, return the equivalent new index if the shape is +// reshaped to another shape. +IrArray::Index GetReshapedIndex(const IrArray::Index& index, const Shape& shape, + const Shape& reshaped_shape, + llvm::IRBuilder<>* b) { + auto bounds = shape.dimensions(); + auto minor_to_major = shape.layout().minor_to_major(); + llvm::Value* linear_index = index.GetConstantWithIndexType(0); + int64 multiplier = 1; + for (int i = 0; i < index.size(); ++i) { + int64 dim = minor_to_major[i]; + llvm::Value* addend = b->CreateMul( + index[dim], index.GetConstantWithIndexType(multiplier), "linearizing", + /*HasNUW=*/true, /*HasNSW=*/true); + linear_index = b->CreateAdd(linear_index, addend, "", + /*HasNUW=*/true, /*HasNSW=*/true); + multiplier *= bounds[dim]; + } + + return IrArray::Index(linear_index, reshaped_shape, b); +} + } // namespace absl::optional > FindTranspose021(const Shape& a, @@ -60,28 +83,30 @@ absl::optional > FindTranspose021(const Shape& a, return absl::nullopt; } - std::vector perm(a.dimensions().size()); - { - auto layout_a_orig = LayoutUtil::MinorToMajor(a); - std::vector layout_a(layout_a_orig.rbegin(), layout_a_orig.rend()); - auto layout_b_orig = LayoutUtil::MinorToMajor(b); - std::vector layout_b(layout_b_orig.rbegin(), layout_b_orig.rend()); - for (size_t i = 0; i < perm.size(); ++i) { - perm[i] = PositionInContainer(layout_b, layout_a[i]); - } + std::vector permutation(a.dimensions().size()); + absl::Span minor_to_major_a = LayoutUtil::MinorToMajor(a); + std::vector major_to_minor_a(minor_to_major_a.rbegin(), + minor_to_major_a.rend()); + absl::Span minor_to_major_b = LayoutUtil::MinorToMajor(b); + std::vector major_to_minor_b(minor_to_major_b.rbegin(), + minor_to_major_b.rend()); + for (size_t i = 0; i < permutation.size(); ++i) { + permutation[i] = PositionInContainer(major_to_minor_b, major_to_minor_a[i]); } - auto segs = ConsecutiveSegments(perm); - if ((3 == segs.size() && 0 == perm[0]) || 2 == segs.size()) { - Shape norm_a = + + std::vector segments = ConsecutiveSegments(permutation); + if ((3 == segments.size() && 0 == permutation[0]) || 2 == segments.size()) { + Shape descending_layout_shape = ShapeUtil::MakeShapeWithDescendingLayoutAndSamePhysicalLayout(a); - Shape reduced_a = MergeDimensions(segs, norm_a); - auto reduced_a_dims = reduced_a.dimensions(); + Shape normalized_shape = MergeDimensions(segments, descending_layout_shape); + absl::Span normalized_dims = + AsInt64Slice(normalized_shape.dimensions()); std::vector dims_021; - if (2 == segs.size()) { + if (2 == segments.size()) { // The logical component-0 is of size one. - dims_021 = {1, reduced_a_dims[1], reduced_a_dims[0]}; + dims_021 = {1, normalized_dims[1], normalized_dims[0]}; } else { - dims_021 = {reduced_a_dims[0], reduced_a_dims[2], reduced_a_dims[1]}; + dims_021 = {normalized_dims[0], normalized_dims[2], normalized_dims[1]}; } return dims_021; @@ -90,27 +115,117 @@ absl::optional > FindTranspose021(const Shape& a, return absl::nullopt; } -IrArray::Index GetUnreducedOutputIndex( - const IrArray::Index& reduced_output_index, - const Shape& reduced_output_shape, const Shape& unreduced_output_shape, - llvm::IRBuilder<>* b) { - auto bounds = reduced_output_shape.dimensions(); - auto minor_to_major = reduced_output_shape.layout().minor_to_major(); - llvm::Value* linear_index = reduced_output_index.GetConstantWithIndexType(0); - int64 multiplier = 1; - for (int i = 0; i < reduced_output_index.size(); ++i) { - int64 dim = minor_to_major[i]; - llvm::Value* addend = - b->CreateMul(reduced_output_index[dim], - reduced_output_index.GetConstantWithIndexType(multiplier), - "linearizing", - /*HasNUW=*/true, /*HasNSW=*/true); - linear_index = b->CreateAdd(linear_index, addend, "", - /*HasNUW=*/true, /*HasNSW=*/true); - multiplier *= bounds[dim]; +KernelMappingScheme::KernelMappingScheme( + absl::Span dims_in_elems, int64 tile_size_y, int64 tile_size_x, + absl::Span req_block_sizes, int64 num_threads_y, + int64 num_threads_x, llvm::IRBuilder<>* b) + : b_(b), + dims_in_elems_(dims_in_elems.begin(), dims_in_elems.end()), + tile_sizes_{1, tile_size_y, tile_size_x}, + num_threads_x_(num_threads_x), + num_threads_y_(num_threads_y) { + DCHECK_EQ(dims_in_elems_.size(), 3); + DCHECK_EQ(req_block_sizes.size(), 3); + + DCHECK_EQ(tile_size_y % num_threads_y_, 0); + DCHECK_EQ(tile_size_x % num_threads_x_, 0); + + dims_in_tiles_ = ElementWiseCeilOfRatio(dims_in_elems_, tile_sizes_); + block_sizes_.reserve(req_block_sizes.size()); + absl::c_transform(req_block_sizes, dims_in_tiles_, + std::back_inserter(block_sizes_), + [](const int64 requested_size, const int64 max_size) { + return std::min(requested_size, max_size); + }); + dims_in_blocks_ = ElementWiseCeilOfRatio(dims_in_tiles_, block_sizes_); + + VLOG(10) << "dims_in_elems_ = [" << absl::StrJoin(dims_in_elems_, ",") << "]"; + VLOG(10) << "dims_in_tiles_ = [" << absl::StrJoin(dims_in_tiles_, ",") << "]"; + VLOG(10) << "dims_in_blocks_ = [" << absl::StrJoin(dims_in_blocks_, ",") + << "]"; +} + +IrArray::Index KernelMappingScheme::GetUnnormalizedIndex( + const IrArray::Index& normalized_shape_index, + const Shape& unnormalized_shape) { + DCHECK_EQ(normalized_shape_index.size(), dims_in_elems_.size()); + Shape output_shape = ShapeUtil::MakeShapeWithDescendingLayout( + unnormalized_shape.element_type(), GetDimensionsInElements()); + return GetReshapedIndex(normalized_shape_index, output_shape, + unnormalized_shape, b_); +} + +IrArray::Index KernelMappingScheme::EmitBlockIndex(llvm::Type* index_ty) { + llvm::Value* block_id = llvm_ir::EmitCallToIntrinsic( + llvm::Intrinsic::nvvm_read_ptx_sreg_ctaid_x, {}, {}, b_); + llvm_ir::AddRangeMetadata(0, GetNumberOfBlocks(), + llvm::cast(block_id)); + llvm::Value* linear_block_id = + b_->CreateIntCast(block_id, index_ty, /*isSigned=*/true, "block.id.x"); + return IrArray::Index(linear_block_id, + ShapeUtil::MakeShapeWithDescendingLayout( + PRED /*arbitrary*/, dims_in_blocks_), + b_); +} + +IrArray::Index KernelMappingScheme::GetTileIndexForBlockOrigin( + const IrArray::Index& block_index) { + IrArray::Index tile_index = block_index; + for (int i = 0; i < block_sizes_.size(); ++i) { + tile_index[i] = b_->CreateMul( + block_index[i], + llvm::ConstantInt::get(block_index[i]->getType(), block_sizes_[i]), + "block_origin." + std::to_string(i)); + } + return tile_index; +} + +IrArray::Index KernelMappingScheme::GetElementIndexForTileOrigin( + const IrArray::Index& tile_index) { + IrArray::Index elem_index = tile_index; + for (int i = DimY; i < DimTot; ++i) { + elem_index[i] = + b_->CreateMul(tile_index[i], + llvm::ConstantInt::get(tile_index[i]->getType(), + GetTileSizeForDimension(i)), + "tile_origin." + std::to_string(i)); } + return elem_index; +} + +llvm::GlobalVariable* KernelMappingScheme::GetSharedMemoryBufferForElementType( + llvm::Type* elem_ty, absl::string_view buffer_name) { + // If shared memory tranpose is needed, we use square tiles. + CHECK_EQ(GetTileSizeForDimensionX(), GetTileSizeForDimensionY()); + + // For Nvidia GPUs, the warp size is 32 threads and the shared memory bank is + // organized into 32-way. We usually use the warp size or a multiplier or a + // the warp size as the size for tiling. This may cause all elements in the + // same column of a tile use the same memory bank and therefore shared memory + // bank conflicts. Adding 1 to the minor dimension of the shared memory buffer + // can reduce such shared memory bank conflicts. + llvm::Type* buffer_type = llvm::ArrayType::get( + llvm::ArrayType::get(elem_ty, GetTileSizeForDimension(DimX) + 1), + GetTileSizeForDimension(DimY)); + return llvm_ir::AllocateSharedMemoryTile(b_->GetInsertBlock()->getModule(), + buffer_type, buffer_name); +} - return IrArray::Index(linear_index, unreduced_output_shape, b); +std::tuple +KernelMappingScheme::EmitThreadYXCoordinate(llvm::Type* index_ty) { + // Calculate (y, x) coordinate of the thread in the 2D view of thread block + // defined by (num_thread_y, num_thread_x) from thread_id. + llvm::CallInst* thread_id_raw = llvm_ir::EmitCallToIntrinsic( + llvm::Intrinsic::nvvm_read_ptx_sreg_tid_x, {}, {}, b_); + llvm_ir::AddRangeMetadata(0, GetThreadsPerTile(), thread_id_raw); + llvm::Value* thread_id_int = + b_->CreateIntCast(thread_id_raw, index_ty, + /*isSigned=*/true, "thread.id.x"); + llvm::Value* num_thread_x = + llvm::ConstantInt::get(index_ty, GetNumberOfThreadsForDimensionX()); + llvm::Value* x = b_->CreateURem(thread_id_int, num_thread_x); + llvm::Value* y = b_->CreateUDiv(thread_id_int, num_thread_x); + return std::make_tuple(y, x); } } // namespace llvm_ir diff --git a/tensorflow/compiler/xla/service/llvm_ir/kernel_tiling.h b/tensorflow/compiler/xla/service/llvm_ir/kernel_tiling.h index 5ea05b3188..7277aeac8a 100644 --- a/tensorflow/compiler/xla/service/llvm_ir/kernel_tiling.h +++ b/tensorflow/compiler/xla/service/llvm_ir/kernel_tiling.h @@ -28,23 +28,165 @@ namespace llvm_ir { // If a shape can be viewed as three logical components 0-1-2 in the order of // major to minor, a 0-2-1-transpose changes the order of such logical // components to 0-2-1. We call the shape being transposed the input shape and -// the transposed shape the output shape. The logical view of the input and -// output shapes for the transpose are called the 0-1-2 shape or reduced input -// shape and the 0-2-1 shape or the reduced output shape respectively. The -// original input and output shapes are called the unreduced input and output -// shapes. - +// the transposed shape the output shape. The logical view of the input/output +// shapes for the transpose are called the 0-1-2/0-2-1 shapes or the normalized +// shapes. The original input/output shapes are called unnormalized shapes. +// // If `b` is a 0-2-1 transpose of `a` in 0-1-2, return the dimensions for the -// reduced shape of `b` or the 0-2-1 shape. +// normalized shape of `b` or the 0-2-1 shape. absl::optional > FindTranspose021(const Shape& a, const Shape& b); -// Return the unreduced output index corresponding to the given reduced output -// index. -IrArray::Index GetUnreducedOutputIndex( - const IrArray::Index& reduced_output_index, - const Shape& reduced_output_shape, const Shape& unreduced_output_shape, - llvm::IRBuilder<>* b); +// A tile is a spatial subdivision of a tensor. We group tensor elements into +// tiles so that we can launch kernels to process the tensor elements in blocks +// of tiles. +// +// A kernel mapping scheme describes a method to partition the tensors accessed +// by an unnested HLO instruction into tiles and blocks of tiles, and the +// associated information to use hardware threads to process the tensor elements +// in blocks of tiles. +// +// Currently, there are two main use cases for a tiling scheme. First, we +// implement kernels with 0-2-1 memory transpose using shared memory to improve +// memory access pattern. Second, we implement reduction to contiguous +// dimensions in layout, with or without memory tranpsose, to achieve better +// memory access pattern as well as to reduce the need numbers of executed +// expensive instructions, such as thread synchronization related instructions +// and atomic operations. For both use cases, we can apply a normalization to +// the original tensors, to collapse contiguous dimensions for the same purpose +// and produce normlized three dimensional tensors. For this reason, the tiling +// scheme class only needs to handle normalized three dimensional tensors and +// two dimensional tiles. +// +// The current implementation of the class is somewhat NVIDIA GPU oriented. This +// situation can be improved when there is a need though. The idea of 0-2-1 +// transpose using shared memory can be found in the following CUDA algorithm in +// TensorFlow: https://goo.gl/MStRV6. +// +// We use a thread block to process a tile because we want to use the HW thread +// block synchronization primitives to synchronize the processing of all the +// elements in the same tile. A thread block can be viewed as a two dimensional +// array of threads, described by the number of threads for the Y and X +// dimensions. A thread block (num_threads_y, num_threads_x) processes a tile of +// (tile_size_y, tile_size_x) as follows: each thread in the thread block +// processes one element in the tile so that all the threads in the thread block +// together process a subdivision of the tile that has the same dimension as the +// thread block array. Then the thread block moves on to process the next +// subdivision of the tile until the whole tile is processed. Therefore, each +// thread in the thread block processes +// tile_size_x/num_threads_x * tile_size_y/num_threads_y elements in a tile. +// +// There are situations where we want a thread block to process multiple +// tiles. We can't group those tiles into a bigger tiles because we limit a tile +// to a two dimensional spatial subdivision of a tensor. For example, when we +// use tiling to implement reduction with tranpose, we want the partial sum +// produced by each thread to accumulate values for more elements before using +// shlf_down and atomic_add instructions for further reduction, to amortize the +// cost of such expensive instructions. The concept of tile block is introduced +// for this purpose. A tile block is a three dimensional array of tiles, of +// which some dimensions may be degenerated to only one tile. +class KernelMappingScheme { + public: + enum { DimZ = 0, DimY, DimX, DimTot }; + + public: + KernelMappingScheme() {} + // dims_in_elems: the normalized tensor dimensions. + // req_block_sizes: the requested block size in number of tiles for each + // dimension. The actual block size is set to min(req_block_size, + // dims_in_number_of_blocks). + KernelMappingScheme(absl::Span dims_in_elems, int64 tile_size_y, + int64 tile_size_x, + absl::Span req_block_sizes, + int64 num_threads_y, int64 num_threads_x, + llvm::IRBuilder<>* b); + + absl::Span GetDimensionsInElements() const { + return dims_in_elems_; + } + absl::Span GetDimensionsInTiles() const { + return dims_in_tiles_; + } + absl::Span GetDimensionsInBlocks() const { + return dims_in_blocks_; + } + + int64 GetNumberOfTilesInTotal() const { + return absl::c_accumulate(dims_in_tiles_, 1LL, std::multiplies()); + } + int64 GetNumberOfTilesInOneBlock() const { + return absl::c_accumulate(block_sizes_, 1, std::multiplies()); + } + + int64 GetNumberOfBlocks() const { + return absl::c_accumulate(dims_in_blocks_, 1, std::multiplies()); + } + + int64 GetTileSizeForDimension(int d) const { + DCHECK(d >= DimZ && d <= DimX); + return tile_sizes_[d]; + } + int64 GetTileSizeForDimensionX() const { + return GetTileSizeForDimension(DimX); + } + int64 GetTileSizeForDimensionY() const { + return GetTileSizeForDimension(DimY); + } + + absl::Span GetBlockSizes() const { return block_sizes_; } + int64 GetTileBlockSizeForDimension(int d) const { + DCHECK(d >= DimZ && d <= DimX); + return dims_in_blocks_[d]; + } + + int64 GetNumberOfThreadsForDimensionX() const { return num_threads_x_; } + int64 GetNumberOfThreadsForDimensionY() const { return num_threads_y_; } + + int64 GetThreadsPerTile() const { + return GetNumberOfThreadsForDimensionX() * + GetNumberOfThreadsForDimensionY(); + } + + IrArray::Index EmitBlockIndex(llvm::Type* index_ty); + // Returns the index for the first tile in the block with the given block + // index. + IrArray::Index GetTileIndexForBlockOrigin(const IrArray::Index& block_index); + // Returns the index for the first element in the tile with the given tile + // index. + IrArray::Index GetElementIndexForTileOrigin(const IrArray::Index& tile_index); + + std::tuple EmitThreadYXCoordinate( + llvm::Type* index_ty); + + IrArray::Index GetUnnormalizedIndex( + const IrArray::Index& normalized_shape_index, + const Shape& unnormalized_shape); + + llvm::GlobalVariable* GetSharedMemoryBufferForElementType( + llvm::Type* elem_ty, absl::string_view buffer_name); + + private: + llvm::IRBuilder<>* b_; + // The number of elements in each dimension. + std::vector dims_in_elems_; + + // The number of elements for each dimension of a tile. + std::vector tile_sizes_; + // The number of tiles in each dimension. It is computed from dims_in_elem_ + // and tile_sizes_. + std::vector dims_in_tiles_; + + // The number of tiles for each dimension of a tile block. + std::vector block_sizes_; + // The number of blocks in each dimension of a tile block. It is computed from + // dims_in_tile_ and block_sizes_. + std::vector dims_in_blocks_; + + // Number of threads used to process elements in the X direction of a tile. + int64 num_threads_x_; + // Number of threads used to process elements in the Y direction of a tile. + int64 num_threads_y_; +}; // A class to represent information for tiled parameters to support IR emission // for 021 transpose. diff --git a/tensorflow/compiler/xla/service/llvm_ir/llvm_util.cc b/tensorflow/compiler/xla/service/llvm_ir/llvm_util.cc index df78726166..ceea24685a 100644 --- a/tensorflow/compiler/xla/service/llvm_ir/llvm_util.cc +++ b/tensorflow/compiler/xla/service/llvm_ir/llvm_util.cc @@ -244,10 +244,11 @@ StatusOr EncodeSelfDescribingShapeConstant(const Shape& shape, StatusOr DecodeSelfDescribingShapeConstant(const void* shape_ptr, int32 size_bytes) { - Shape shape; - TF_RET_CHECK(shape.ParseFromArray(shape_ptr, size_bytes)); + ShapeProto shape_proto; + TF_RET_CHECK(shape_proto.ParseFromArray(shape_ptr, size_bytes)); + Shape shape(shape_proto); TF_RETURN_IF_ERROR(ShapeUtil::ValidateShape(shape)); - return shape; + return std::move(shape); } llvm::Constant* ConvertLiteralToIrConstant(const Literal& literal, diff --git a/tensorflow/compiler/xla/service/llvm_ir/sort_util.cc b/tensorflow/compiler/xla/service/llvm_ir/sort_util.cc index fd16af67fe..e22c2173c2 100644 --- a/tensorflow/compiler/xla/service/llvm_ir/sort_util.cc +++ b/tensorflow/compiler/xla/service/llvm_ir/sort_util.cc @@ -47,7 +47,8 @@ namespace { // Adds the inner comparison loop body where we compare elements. void EmitCompareLoopBody( int64 iteration_bound, PrimitiveType key_type, int64 num_values, - llvm::Value* element_pair_index, int64 xor_mask, llvm::Type* index_type, + int64 iota_values_parameter_index, llvm::Value* element_pair_index, + int64 xor_mask, llvm::Type* index_type, std::function read_element, std::function write_element, @@ -139,34 +140,42 @@ void EmitCompareLoopBody( is_signed_comparison = false; } // If key2 < key1 - ksl.IfReturnVoid( - "is_smaller_than", + auto is_smaller_than = b->CreateICmp(is_signed_comparison ? llvm::ICmpInst::ICMP_SLT : llvm::ICmpInst::ICMP_ULT, - compare_key2, compare_key1), - [&]() { - // Swap key1 with key2. - write_element(0, current_keys_index, key2); - write_element(0, compare_keys_index, key1); - for (int64 i = 1; i <= num_values; ++i) { - // Also swap the values. - auto value1 = read_element(i, current_keys_index); - auto value2 = read_element(i, compare_keys_index); - write_element(i, current_keys_index, value2); - write_element(i, compare_keys_index, value1); - } - }); + compare_key2, compare_key1); + if (iota_values_parameter_index >= 0) { + auto keys_equal = b->CreateICmpEQ(compare_key1, compare_key2); + auto key_index1 = + read_element(iota_values_parameter_index, current_keys_index); + auto key_index2 = + read_element(iota_values_parameter_index, compare_keys_index); + auto index_is_smaller_than = + b->CreateICmp(llvm::ICmpInst::ICMP_ULT, key_index2, key_index1); + is_smaller_than = b->CreateOr( + is_smaller_than, b->CreateAnd(keys_equal, index_is_smaller_than)); + } + ksl.IfReturnVoid("is_smaller_than", is_smaller_than, [&]() { + // Swap key1 with key2. + write_element(0, current_keys_index, key2); + write_element(0, compare_keys_index, key1); + for (int64 i = 1; i <= num_values; ++i) { + // Also swap the values. + auto value1 = read_element(i, current_keys_index); + auto value2 = read_element(i, compare_keys_index); + write_element(i, current_keys_index, value2); + write_element(i, compare_keys_index, value1); + } + }); }); } -void EmitTiledCompareLoop(const IrArray::Index& tiled_keys_index, - int64 dimension_to_sort, - int64 dimension_to_sort_bound, - PrimitiveType keys_type, - absl::Span xor_masks, - const std::vector& params, - const std::vector& param_shmem_buffers, - int64 tile_size, llvm::IRBuilder<>* b) { +void EmitTiledCompareLoop( + const IrArray::Index& tiled_keys_index, int64 dimension_to_sort, + int64 dimension_to_sort_bound, PrimitiveType keys_type, + absl::Span xor_masks, const std::vector& params, + const std::vector& param_shmem_buffers, + int64 iota_values_parameter_index, int64 tile_size, llvm::IRBuilder<>* b) { KernelSupportLibrary ksl(b); llvm::Value* thread_id = llvm_ir::EmitCallToIntrinsic( llvm::Intrinsic::nvvm_read_ptx_sreg_tid_x, {}, {}, b); @@ -253,20 +262,22 @@ void EmitTiledCompareLoop(const IrArray::Index& tiled_keys_index, RoundDownToNearest(dimension_to_sort_bound, tile_size))), [&]() { EmitCompareLoopBody(dimension_to_sort_bound % tile_size, keys_type, - params.size() - 1, element_pair_index, xor_mask, + params.size() - 1, iota_values_parameter_index, + element_pair_index, xor_mask, tiled_keys_index.GetType(), read_element, write_element, b); }, [&]() { - EmitCompareLoopBody( - tile_size, keys_type, params.size() - 1, element_pair_index, - xor_mask, tiled_keys_index.GetType(), read_element, - write_element, b, /*needs_bounds_checks=*/false); + EmitCompareLoopBody(tile_size, keys_type, params.size() - 1, + iota_values_parameter_index, element_pair_index, + xor_mask, tiled_keys_index.GetType(), + read_element, write_element, b, + /*needs_bounds_checks=*/false); }); } else { EmitCompareLoopBody(tile_size, keys_type, params.size() - 1, - element_pair_index, xor_mask, - tiled_keys_index.GetType(), read_element, + iota_values_parameter_index, element_pair_index, + xor_mask, tiled_keys_index.GetType(), read_element, write_element, b, /*needs_bounds_checks=*/false); } // Wait until all comparisons have happened. @@ -296,6 +307,7 @@ void EmitTiledCompareLoop(const IrArray::Index& tiled_keys_index, Status EmitSortInPlace(int64 dimension_to_sort, const IrArray& keys_array, const std::vector& values_arrays, + int64 iota_values_parameter_index, absl::string_view name, absl::Span xor_masks, llvm::IRBuilder<>* b, const gpu::LaunchDimensions& launch_dimensions, @@ -367,8 +379,8 @@ Status EmitSortInPlace(int64 dimension_to_sort, const IrArray& keys_array, if (xor_masks.size() > 1) { EmitTiledCompareLoop(keys_index, dimension_to_sort, dimension_to_sort_bound, keys_shape.element_type(), - xor_masks, params, param_shmem_buffers, tile_size, - b); + xor_masks, params, param_shmem_buffers, + iota_values_parameter_index, tile_size, b); } else { auto read_element = [&](int64 operand, llvm::Value* index) { keys_index[dimension_to_sort] = index; @@ -380,9 +392,10 @@ Status EmitSortInPlace(int64 dimension_to_sort, const IrArray& keys_array, params[operand].EmitWriteArrayElement(keys_index, value, b); }; EmitCompareLoopBody(dimension_to_sort_bound, keys_shape.element_type(), - values_arrays.size(), tiles_index[rank - 1], - xor_masks[0], tiles_index.GetType(), read_element, - write_element, b); + values_arrays.size(), iota_values_parameter_index, + tiles_index[rank - 1], xor_masks[0], + tiles_index.GetType(), read_element, write_element, + b); } return Status::OK(); }; diff --git a/tensorflow/compiler/xla/service/llvm_ir/sort_util.h b/tensorflow/compiler/xla/service/llvm_ir/sort_util.h index 556a217322..685f9383ac 100644 --- a/tensorflow/compiler/xla/service/llvm_ir/sort_util.h +++ b/tensorflow/compiler/xla/service/llvm_ir/sort_util.h @@ -31,9 +31,12 @@ namespace llvm_ir { // Emits llvm IR to do pairwise comparisons/swaps in the 'dimension_to_sort' // dimension of 'keys_array'. All other dimensions are kept as-is. This // implements the inner loop of BitonicSort. It is assumed that 'xor_masks' -// contains only powers of 2, or values 2^k - 1 (k > 0). +// contains only powers of 2, or values 2^k - 1 (k > 0). If +// 'iota_values_parameter_index' is >= 0, it points at a 'values_arrays' operand +// that is a iota and can be used to make the sorting stable. Status EmitSortInPlace(int64 dimension_to_sort, const IrArray& keys_array, const std::vector& values_arrays, + int64 iota_values_parameter_index, absl::string_view name, absl::Span xor_masks, llvm::IRBuilder<>* b, const gpu::LaunchDimensions& launch_dimensions, diff --git a/tensorflow/compiler/xla/service/local_service.cc b/tensorflow/compiler/xla/service/local_service.cc index cca3755617..6c89700983 100644 --- a/tensorflow/compiler/xla/service/local_service.cc +++ b/tensorflow/compiler/xla/service/local_service.cc @@ -96,44 +96,18 @@ ExecutionOptions CreateExecutionOptions( const ExecutableBuildOptions& build_options, const ProgramShape* program_shape) { ExecutionOptions execution_options = CreateDefaultExecutionOptions(); - if (build_options.hlo_profile().has_value()) { - execution_options.mutable_debug_options()->set_xla_hlo_profile( - *build_options.hlo_profile()); - } - if (build_options.generate_hlo_graph().has_value()) { - execution_options.mutable_debug_options()->set_xla_generate_hlo_graph( - build_options.generate_hlo_graph().value()); - } - if (build_options.dump_optimized_hlo_proto_to().has_value()) { - execution_options.mutable_debug_options() - ->set_xla_dump_optimized_hlo_proto_to( - build_options.dump_optimized_hlo_proto_to().value()); - } - if (build_options.dump_unoptimized_hlo_proto_to().has_value()) { - execution_options.mutable_debug_options() - ->set_xla_dump_unoptimized_hlo_proto_to( - build_options.dump_unoptimized_hlo_proto_to().value()); - } - if (build_options.dump_per_pass_hlo_proto_to().has_value()) { - execution_options.mutable_debug_options() - ->set_xla_dump_per_pass_hlo_proto_to( - build_options.dump_per_pass_hlo_proto_to().value()); + if (build_options.has_debug_options()) { + *execution_options.mutable_debug_options() = build_options.debug_options(); } if (build_options.result_layout() != nullptr) { *execution_options.mutable_shape_with_output_layout() = - *build_options.result_layout(); + build_options.result_layout()->ToProto(); } else { + Shape result_shape(program_shape->result()); + LayoutUtil::SetToDefaultLayout(&result_shape); *execution_options.mutable_shape_with_output_layout() = - program_shape->result(); - LayoutUtil::SetToDefaultLayout( - execution_options.mutable_shape_with_output_layout()); + result_shape.ToProto(); } - - for (const std::string& disabled_pass : build_options.disabled_hlo_passes()) { - execution_options.mutable_debug_options()->add_xla_disable_hlo_passes( - disabled_pass); - } - return execution_options; } @@ -145,7 +119,7 @@ StatusOr> LocalService::CompileExecutable( const ExecutableBuildOptions& build_options) { const HloModuleProto& proto = computation.proto(); TF_RET_CHECK(proto.has_host_program_shape()); - const ProgramShape& program_shape = proto.host_program_shape(); + ProgramShape program_shape(proto.host_program_shape()); // Validate incoming layouts. if (argument_layouts.size() != program_shape.parameters_size()) { @@ -220,4 +194,10 @@ StatusOr LocalService::GlobalDataToShapedBuffer( return buffers[replica_number]; } +StatusOr LocalService::RegisterReplicatedBuffers( + std::vector replicated_buffers, const string& tag) { + return allocation_tracker_.RegisterReplicatedBuffers( + std::move(replicated_buffers), tag); +} + } // namespace xla diff --git a/tensorflow/compiler/xla/service/local_service.h b/tensorflow/compiler/xla/service/local_service.h index 3b4f0b5083..f56ba32b04 100644 --- a/tensorflow/compiler/xla/service/local_service.h +++ b/tensorflow/compiler/xla/service/local_service.h @@ -63,6 +63,11 @@ class LocalService : public Service { StatusOr GlobalDataToShapedBuffer( const GlobalDataHandle& data, int replica_number); + // Registers a vector of shaped buffers of device memory, one per replica, and + // returns a corresponding handle that can be used for talking to XLA clients. + StatusOr RegisterReplicatedBuffers( + std::vector replicated_buffers, const string& tag); + private: explicit LocalService(const ServiceOptions& options, std::unique_ptr backend); diff --git a/tensorflow/compiler/xla/service/logical_buffer_analysis.cc b/tensorflow/compiler/xla/service/logical_buffer_analysis.cc index ec52a24d78..972a5b9ced 100644 --- a/tensorflow/compiler/xla/service/logical_buffer_analysis.cc +++ b/tensorflow/compiler/xla/service/logical_buffer_analysis.cc @@ -113,6 +113,13 @@ Status LogicalBufferAnalysis::HandleGetTupleElement(HloInstruction*) { return Status::OK(); } +Status LogicalBufferAnalysis::HandleAddDependency( + HloInstruction* add_dependency) { + // AddDependency just forwards the value of its zero-th operand and does not + // create buffers. + return Status::OK(); +} + Status LogicalBufferAnalysis::HandleCopy(HloInstruction* copy) { // The top-level buffer (index={}) for kCopy is newly created, but all other // buffers (in the case of a tuple shape) come from the operand diff --git a/tensorflow/compiler/xla/service/logical_buffer_analysis.h b/tensorflow/compiler/xla/service/logical_buffer_analysis.h index 81f524d84a..7ffca943d0 100644 --- a/tensorflow/compiler/xla/service/logical_buffer_analysis.h +++ b/tensorflow/compiler/xla/service/logical_buffer_analysis.h @@ -64,6 +64,7 @@ class LogicalBufferAnalysis : public DfsHloVisitorWithDefault { Status HandleRecvDone(HloInstruction* recv_done) override; Status HandleSend(HloInstruction* send) override; Status HandleTupleSelect(HloInstruction* tuple_select) override; + Status HandleAddDependency(HloInstruction* add_dependency) override; // A map from the buffer ID to the logical buffer std::vector> logical_buffers_; diff --git a/tensorflow/compiler/xla/service/pattern_matcher.h b/tensorflow/compiler/xla/service/pattern_matcher.h index 6152cdc609..432aa1ea0b 100644 --- a/tensorflow/compiler/xla/service/pattern_matcher.h +++ b/tensorflow/compiler/xla/service/pattern_matcher.h @@ -16,6 +16,7 @@ limitations under the License. #ifndef TENSORFLOW_COMPILER_XLA_SERVICE_PATTERN_MATCHER_H_ #define TENSORFLOW_COMPILER_XLA_SERVICE_PATTERN_MATCHER_H_ +#include "absl/strings/str_replace.h" #include "absl/strings/string_view.h" #include "absl/utility/utility.h" #include "tensorflow/compiler/xla/layout_util.h" @@ -44,32 +45,48 @@ namespace xla { // // This pattern will match Add instructions whose first operand is a constant. // -// Each pattern type has the following modifiers: +// Each pattern type has the following modifiers, which are described where +// nontrivial. // // Op(): -// - WithName: match operations with the given name -// - WithOpcode: match operations with the given opcode -// - WithShape: match operations whose shape matches the given pattern -// - WithOperand: match operations whose operand matches the given pattern +// - Is: is the given HloInstruction* (i.e. pointer equality) +// - WithName +// - WithOpcode +// - WithoutOpcode: anything other than the given opcode +// - WithShape: instr's shape matches the given pattern +// - WithShapeEqualTo: instr's shape is equal to the given Shape +// - WithShapeCompatibleTo: instr's shape is compatible with the given Shape +// - WithNumOperands +// - WithOperand: operand at the given index matches the given pattern +// - IsConstant +// - IsNonConstant +// - IsConstantScalar/IsEffectiveConstantScalar: Optionally accepts a value, +// e.g. IsConstantScalar() or IsConstantScalar(42). +// - WithFusionKind +// - WithTupleIndex: get-tuple-element operations with the given tuple index +// - WithOneUse: Instruction is used as an operand exactly once. +// - WithOneUser: Instruction is used by exactly one other instruction, but +// is possibly used more than once as an operand (e.g. multiply(x,x)). // // Shape(): -// - EqualTo: matches shapes that are equal to the argument -// - CompatibleTo: matches shapes that are compatible to the argument -// - IsScalar/IsArray/IsTuple: matches scalar/array/tuple shapes -// - IsDenseArray/IsSparseArray: matches arrays with dense/sparse format -// - WithLayout: match shapes whose layout matches the given pattern -// - WithLayoutEqualTo: matches shapes whose layouts equal the argument -// - WithSubshape: matches tuple shapes whose subshape matches the given -// pattern -// - WithSubshapeEqualTo: matches shapes with a subshape equal the argument -// - WithElementType: matches array/scalar shapes with the given element -// type -// - WithRank: matches array/scalar types with the given rank +// - EqualTo +// - CompatibleTo +// - IsScalar/IsEffectiveScalar/IsArray/IsTuple +// - IsDenseArray/IsSparseArray +// - WithLayout: layout shape's layout matches the given pattern (e.g. +// Layout().WithDenseFormat()) +// - WithLayoutEqualTo: shape's layout equals the argument (i.e. another +// Layout, but not the result of Layout().foo()) +// - WithSubshape: shape is a tuple whose subshape matches the given pattern +// (e.g. Shape().IsScalar()). +// - WithSubshapeEqualTo: shape is a tuple with a subshape equal to the arg +// (i.e. another Shape, but not the result of Shape().foo()) +// - WithElementType: shape is an array/scalar with the given elem type +// - WithRank: shape is an array/scalar with the given rank // // Layout(): -// - EqualTo: matches layouts that are equal to the argument -// - WithDenseFormat/WithSparseFormat: matches layouts with dense/sparse -// format +// - EqualTo +// - WithDenseFormat/WithSparseFormat // // Op(), Shape(), and Layout() may be passed an argument of type // HloInstruction**, Shape**, or Layout**, respectively, or const versions of @@ -82,53 +99,55 @@ namespace xla { // CHECK(Match(foo, // match::Op().WithOperand(0, match::Op(&matched_operand)))); // -// Helpers are provided for common nullary, unary, binary, and ternary -// instructions. These helpers can be called with no arguments, in which case -// they will match any instruction matching the opcode. They may also be called -// with matches for the operands and with an optional capture. (The capture must -// be the first argument.) Some examples of these helpers and their equivalents -// are provided below. -// +// Helpers are provided for most HLO instructions. These helpers can be called +// with no arguments, in which case they will match any instruction matching the +// opcode. They may also be called with matches for the operands and with an +// optional capture. (The capture must be the first argument.) Some examples of +// these helpers and their equivalents are provided below. + // Example nullary instruction: -// Param() == Op().WithOpcode(HloOpcode::kParam) -// Param(&a) == Op(&a).WithOpcode(HloOpcode::kParam) +// Parameter() == Op().WithOpcode(HloOpcode::kParameter) +// Parameter(&a) == Op(&a).WithOpcode(HloOpcode::kParameter) // // Example unary instruction: -// Abs() == Op().WithOpcode(HloOpcode::kAbs) -// Abs(Op(&a)) == Op().WithOpcode(HloOpcode::kAbs) -// .WithOperand(0, Op(&a))) -// Abs(&a, Op(&b)) == Op(&a).WithOpcode(HloOpcode::kAbs) -// .WithOperand(0, Op(&b)) +// Abs() == Op().WithOpcode(HloOpcode::kAbs) +// Abs(Op(&a)) == Op().WithOpcode(HloOpcode::kAbs) +// .WithOperand(0, Op(&a))) +// Abs(&a, Op(&b)) == Op(&a).WithOpcode(HloOpcode::kAbs) +// .WithOperand(0, Op(&b)) +// +// Commutative binary instructions have a special form that accepts either order +// of args, e.g.: +// +// AddAnyOrder(Parameter(1), Abs()) == +// Op().WithOpcode(HloOpcode::kAdd) +// .WithBinaryOperandsAnyOrder(Op().WithParameterNum(1), Abs()); // -// Example binary instruction: -// Add() == Op().WithOpcode(HloOpcode::kAdd) -// Add(Op(&a), Op(&b)) == Op().WithOpcode(HloOpcode::kAdd) -// .WithOperand(0, Op(&a)) -// .WithOperand(1, Op(&b)) -// Add(&a, Op(&b), Op(&c)) == Op(&a).WithOpcode(HloOpcode::kAdd) -// .WithOperand(0, Op(&b)) -// .WithOperand(1, Op(&c)) +// MultiplyAnyOrder(&a, Parameter(), Abs()) // Captures the mul in `a`. // -// Example ternary instruction: -// Clamp() == Op().WithOpcode(HloOpcode::kClamp) -// Clamp(Op(&a), Op(&b), Op(&c)) == Op().WithOpcode(HloOpcode::kClamp) -// .WithOperand(0, Op(&a)) -// .WithOperand(1, Op(&b)) -// .WithOperand(2, Op(&c)) -// Clamp(&a, Op(&b), Op(&c), Op(&d)) == Op(&a).WithOpcode(HloOpcode::kClamp) -// .WithOperand(0, Op(&b)) -// .WithOperand(1, Op(&c)) -// .WithOperand(2, Op(&d)) +// The following additional helpers are provided. In all cases, `&a` is +// optional. // +// ConstantScalar(&a) == Op(&a).IsConstantScalar(); +// ConstantScalar(&a, v) == Op(&a).IsConstantScalar(v); +// ConstantEffectiveScalar(&a) == Op(&a).IsConstantEffectiveScalar(); +// ConstantEffectiveScalar(&a, v) == Op(&a).IsConstantEffectiveScalar(&a, v) +// NonConstant(&a) == Op(&a).IsNonConstant() +// GetTupleElement(&a, b, index) == Op(&a).WithTupleIndex(index) +// .WithOperand(0, b); +// Parameter(&a, n) == Op(&a).WithParameterNum(n); struct MatchOption { // If true, actually capture matched item into the user pointer. bool capture; + + // An explanation for why we failed to match is streamed here, if not-null. + std::ostream* explain_os; }; template bool Match(Value* value, const Pattern& pattern, - MatchOption option = {/*.capture=*/true}) { + MatchOption option = {/*.capture=*/true, /*.explain_os=*/nullptr}) { if (option.capture) { auto new_option = option; new_option.capture = false; @@ -143,6 +162,77 @@ namespace match { namespace detail { +// Macro for streaming to option.explain_os if it's not null. +// +// EXPLAIN << "value of foo(): " << foo() +// +#pragma push_macro("EXPLAIN") +#define EXPLAIN \ + if (option.explain_os) *option.explain_os + +// kIndentInc is the additional number of spaces that we indent by when we +// increase the indent "by one". +enum { + kIndentInc = 2, +}; + +// Writes a newline and then `indent` spaces. +// +// We follow an unintuitive convention in this file's pretty-printers: Indents +// are performed by the caller, not the callee. For example, if you want to +// print +// +// foo: +// - bar +// +// you'd do: +// +// Foo::DescribeTo(std::ostream* os, int64 indent) { +// *os << "foo:"; +// Indent(os, indent) // Create a newline at the *current* indent level. +// *os << " - "; +// bar.DescribeTo(os, indent + 3); // + 3 because strlen(" * ") == 3. +// } +// +// Bar::DescribeTo(std::ostream* os, int64 indent) { *os << "bar"; } +// +// Notice that Bar::DescribeTo() does not call Indent; the indenting is +// performed by Foo. This convention allows the caller to decide whether a +// matcher is preceded by a newline, which is important e.g. for the AllOf +// matcher. +// +// (Incidentally, indenting in Match's explanations is handled differently. +// Indents are a common case in DescribeTo [we're printing a whole tree], but +// they're a special case in Match [we're printing only a path through the tree +// that encounters a failing node]. Indents in Match only appear when we +// encounter a failing disjunction, so we just handle them as a special case +// there.) +inline void Indent(std::ostream* os, int64 indent) { + *os << "\n"; + for (int64 i = 0; i < indent; ++i) { + *os << " "; + } +} + +// SFINAE template that determines whether T declares a static member +// kIsTrivialMatcher. +// +// Trivial matchers get special treatment. For example, when printing +// a conjunction of matchers, we don't print "and" after a trivial matcher. This +// yields e.g. +// "a shape compatible with f32[1,2]" +// rather than +// "a shape AND compatible with f32[1,2]" +template +struct IsTrivialMatcher { + static constexpr bool value = false; +}; +template +struct IsTrivialMatcher::type> { + static constexpr bool value = true; +}; + template class AllOfPattern { public: @@ -162,10 +252,19 @@ class AllOfPattern { return matched; } + void DescribeTo(std::ostream* os, int64 indent = 0) const { + DescribeToImpl(os, std::integral_constant(), indent); + } + + // Accessor for patterns_. Please don't use this outside of this file. + const std::tuple& patterns() const { return patterns_; } + private: template bool MatchImpl(ItemType* item, MatchOption option, std::integral_constant) const { + // We don't need to do any EXPLAINing here; it's all correctly handled by + // our sub-matchers (if any fail). return std::get(patterns_).Match(item, option) && MatchImpl(item, option, std::integral_constant()); } @@ -176,6 +275,73 @@ class AllOfPattern { return true; } + // Pretty-printing a conjunction has some special cases to make it easy to + // read in the simple (common) case. + // + // If sizeof...(Patterns) == 1, prints as e.g. + // + // a shape + // + // If sizeof...(Patterns) == 2 and patterns_[0] is a trivial matcher (e.g. "a + // shape") prints as + // + // a shape compatible with f32[1,2] + // + // If sizeof...(Patterns) > 2 and patterns_[0] is a trivial matcher, prints as + // + // a shape: + // * compatible with f32[1,2] AND + // * that represents a scalar + // + // Otherwise prints as: + // + // all of: + // * foo AND + // * bar + // + template + void DescribeToImpl(std::ostream* os, std::integral_constant, + int64 indent) const { + constexpr bool first_is_trivial = + IsTrivialMatcher(patterns_))>::type>::value; + constexpr bool is_last = index == sizeof...(Patterns) - 1; + const auto& submatcher = std::get(patterns_); + + auto print_bulleted_item = [&] { + *os << " * "; + submatcher.DescribeTo(os, indent + 3); + if (!is_last) { + *os << " AND"; + Indent(os, indent); + } + }; + + if (index == 0) { + if (first_is_trivial || is_last) { + submatcher.DescribeTo(os, indent + kIndentInc); + if (sizeof...(Patterns) > 2) { + *os << ":"; + Indent(os, indent); + } + } else { + *os << "all of:"; + Indent(os, indent); + print_bulleted_item(); + } + } else if (first_is_trivial && index == 1 && sizeof...(Patterns) == 2) { + *os << " "; + submatcher.DescribeTo(os, indent); + } else { + print_bulleted_item(); + } + DescribeToImpl(os, std::integral_constant(), indent); + } + + void DescribeToImpl(std::ostream* os, + std::integral_constant, + int64 indent) const {} + std::tuple patterns_; }; @@ -183,10 +349,6 @@ class AllOfPattern { // Returns a pattern that represents the conjunction of all input patterns. All // patterns need to match in order to have the AllOf pattern match. -// -// TODO(timshen): Currently AllOf is still nested, e.g. AllOf, B> is -// not AllOf. We might want to flatten the AllOf type structure if the -// C++ compile error message gets annoying. template detail::AllOfPattern::type, Patterns...> AllOf( const Patterns&... patterns) { @@ -194,6 +356,25 @@ detail::AllOfPattern::type, Patterns...> AllOf( Patterns...>(patterns...); } +// AllOf, X, Y, ...> => AllOf. +// +// This transformation is necessary for good pretty-printing. +template +detail::AllOfPattern::type, InnerPs..., + OuterPs...> +AllOf(const detail::AllOfPattern& inner_p, + const OuterPs&... outer_ps) { + // Invoke constructor of AllOfPattern. + auto make_all_of = [](const InnerPs&... inner_ps, + const OuterPs&... outer_ps) { + return detail::AllOfPattern::type, + InnerPs..., OuterPs...>(inner_ps..., + outer_ps...); + }; + return absl::apply(make_all_of, std::tuple_cat(inner_p.patterns(), + std::make_tuple(outer_ps...))); +} + namespace detail { template @@ -204,8 +385,18 @@ class LayoutPattern; class LayoutPatternBaseImpl { public: bool Match(const ::xla::Layout* layout, MatchOption option) const { - return layout != nullptr; + if (layout == nullptr) { + EXPLAIN << "Layout is null"; + return false; + } + return true; + } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + *os << "a layout"; } + + static constexpr bool kIsTrivialMatcher = true; }; // A LayoutPattern implementation that matches only if the layout equals a @@ -216,7 +407,17 @@ class LayoutPatternEqualImpl { : layout_(layout) {} bool Match(const ::xla::Layout* layout, MatchOption option) const { - return LayoutUtil::Equal(*layout_, *layout); + if (!LayoutUtil::Equal(*layout_, *layout)) { + EXPLAIN << "Layout " << LayoutUtil::HumanString(*layout) + << " is not equal to expected " + << LayoutUtil::HumanString(*layout_); + return false; + } + return true; + } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + *os << "equal to " << LayoutUtil::HumanString(*layout_); } private: @@ -230,7 +431,16 @@ class LayoutPatternFormatImpl { explicit constexpr LayoutPatternFormatImpl(Format format) : format_(format) {} bool Match(const ::xla::Layout* layout, MatchOption option) const { - return layout->format() == format_; + if (layout->format() != format_) { + EXPLAIN << "Layout has format " << Format_Name(layout->format()) + << " but expected " << Format_Name(format_); + return false; + } + return true; + } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + *os << "with format " << Format_Name(format_); } private: @@ -242,11 +452,13 @@ template class LayoutPattern { private: template - LayoutPattern> - AppendImpl(NewImpl new_impl) const { - return LayoutPattern>( - AllOf(impl_, std::move(new_impl)), matched_layout_); + auto AppendImpl(NewImpl new_impl) const + -> LayoutPattern(std::declval(), + std::move(new_impl)))> { + auto new_allof = AllOf(impl_, std::move(new_impl)); + return LayoutPattern(std::move(new_allof), + matched_layout_); } public: @@ -276,6 +488,10 @@ class LayoutPattern { return false; } + void DescribeTo(std::ostream* os, int64 indent = 0) const { + impl_.DescribeTo(os, indent); + } + // Modifies the pattern to match only if the layout equals the given proto. // The layout must outlive the returned pattern. constexpr auto EqualTo(const ::xla::Layout* layout) const @@ -306,19 +522,48 @@ class AnyOfPattern { explicit AnyOfPattern(const Patterns&... patterns) : patterns_(patterns...) {} bool Match(const Item* item, MatchOption option) const { - return MatchImpl(item, option, std::integral_constant()); + return MatchImpl(item, option); } bool Match(Item* item, MatchOption option) const { - return MatchImpl(item, option, std::integral_constant()); + return MatchImpl(item, option); + } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + *os << "any of:"; + Indent(os, indent); + DescribeToImpl(os, std::integral_constant(), indent); } private: + template + bool MatchImpl(ItemType* item, MatchOption option) const { + // If we're generating an explanation, buffer it until we know we failed. + absl::optional explanation; + MatchOption new_option = option; + if (option.explain_os) { + new_option.explain_os = &explanation.emplace(); + } + bool rv = MatchRecursiveImpl(item, new_option, + std::integral_constant()); + if (!rv && option.explain_os) { + EXPLAIN << "None of the following matchers succeeded:"; + EXPLAIN << explanation->str(); + } + return rv; + } + template - bool MatchImpl(ItemType* item, MatchOption option, - std::integral_constant) const { + bool MatchRecursiveImpl(ItemType* item, MatchOption option, + std::integral_constant) const { auto new_option = option; new_option.capture = false; + + absl::optional explanation; + if (option.explain_os) { + new_option.explain_os = &explanation.emplace(); + } + // Try to match the sub-pattern without capturing behavior. if (std::get(patterns_).Match(item, new_option)) { // Capture the branch. @@ -337,20 +582,46 @@ class AnyOfPattern { // AnyOf will be a runtime number indicate which sub-pattern is matched. // Then we run another pass to do captures only with the help of the // trace. - bool ret = std::get(patterns_).Match(item, option); - DCHECK(ret); + bool matched = std::get(patterns_).Match(item, option); + DCHECK(matched); } return true; } - return MatchImpl(item, option, std::integral_constant()); + if (option.explain_os) { + EXPLAIN << "\nMatcher #" << index + 1; + EXPLAIN << "\n - "; + std::get(patterns_).DescribeTo(option.explain_os, /*indent=*/3); + EXPLAIN << "\nfailed with"; + EXPLAIN << "\n - "; + EXPLAIN << absl::StrReplaceAll(explanation->str(), {{"\n", "\n "}}); + } + return MatchRecursiveImpl(item, option, + std::integral_constant()); } template - bool MatchImpl(ItemType* item, MatchOption option, - std::integral_constant) const { + bool MatchRecursiveImpl( + ItemType* item, MatchOption option, + std::integral_constant) const { return false; } + template + void DescribeToImpl(std::ostream* os, std::integral_constant, + int64 indent) const { + *os << " - "; + std::get(patterns_).DescribeTo(os, indent + 3); + if (index != sizeof...(Patterns) - 1) { + *os << " OR"; + Indent(os, indent); + } + DescribeToImpl(os, std::integral_constant(), indent); + } + + void DescribeToImpl(std::ostream* os, + std::integral_constant, + int64 indent) const {} + std::tuple patterns_; }; @@ -395,8 +666,17 @@ class ShapePattern; class ShapePatternBaseImpl { public: bool Match(const ::xla::Shape* shape, MatchOption option) const { + if (shape == nullptr) { + EXPLAIN << "Shape is null"; + } return shape != nullptr; } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + *os << "a shape"; + } + + static constexpr bool kIsTrivialMatcher = true; }; // A ShapePattern implementation that matches only if the shape equals a Shape @@ -407,7 +687,16 @@ class ShapePatternEqualImpl { : shape_(shape) {} bool Match(const ::xla::Shape* shape, MatchOption option) const { - return ShapeUtil::Equal(*shape_, *shape); + if (!ShapeUtil::Equal(*shape_, *shape)) { + EXPLAIN << "Shape not equal to " + << ShapeUtil::HumanStringWithLayout(*shape_); + return false; + } + return true; + } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + *os << "equal to " << ShapeUtil::HumanStringWithLayout(*shape_); } private: @@ -422,7 +711,16 @@ class ShapePatternCompatibleImpl { : shape_(shape) {} bool Match(const ::xla::Shape* shape, MatchOption option) const { - return ShapeUtil::Compatible(*shape_, *shape); + if (!ShapeUtil::Compatible(*shape_, *shape)) { + EXPLAIN << "Shape not compatible with " + << ShapeUtil::HumanString(*shape_); + return false; + } + return true; + } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + *os << "compatible with " << ShapeUtil::HumanString(*shape_); } private: @@ -437,7 +735,16 @@ class ShapePatternElementTypeImpl { : element_type_(element_type) {} bool Match(const ::xla::Shape* shape, MatchOption option) const { - return shape->element_type() == element_type_; + if (shape->element_type() != element_type_) { + EXPLAIN << "Shape does not have element type " + << PrimitiveType_Name(element_type_); + return false; + } + return true; + } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + *os << "with element type " << PrimitiveType_Name(element_type_); } private: @@ -450,7 +757,15 @@ class ShapePatternIsScalarImpl { explicit constexpr ShapePatternIsScalarImpl() {} bool Match(const ::xla::Shape* shape, MatchOption option) const { - return ShapeUtil::IsScalar(*shape); + if (!ShapeUtil::IsScalar(*shape)) { + EXPLAIN << "Shape is not a scalar"; + return false; + } + return true; + } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + *os << "that represents a scalar"; } }; @@ -460,7 +775,15 @@ class ShapePatternIsArrayImpl { explicit constexpr ShapePatternIsArrayImpl() {} bool Match(const ::xla::Shape* shape, MatchOption option) const { - return ShapeUtil::IsArray(*shape); + if (!ShapeUtil::IsArray(*shape)) { + EXPLAIN << "Shape is not an array"; + return false; + } + return true; + } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + *os << "that represents an array"; } }; @@ -470,7 +793,34 @@ class ShapePatternIsTupleImpl { explicit constexpr ShapePatternIsTupleImpl() {} bool Match(const ::xla::Shape* shape, MatchOption option) const { - return ShapeUtil::IsTuple(*shape); + if (!ShapeUtil::IsTuple(*shape)) { + EXPLAIN << "Shape is not a tuple"; + return false; + } + return true; + } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + *os << "that represents a tuple"; + } +}; + +// A ShapePattern implementation that matches only if the shape is an effective +// scalar. +class ShapePatternEffectiveScalarImpl { + public: + explicit constexpr ShapePatternEffectiveScalarImpl() {} + + bool Match(const ::xla::Shape* shape, MatchOption option) const { + if (!ShapeUtil::IsEffectiveScalar(*shape)) { + EXPLAIN << "Shape is not an effective scalar"; + return false; + } + return true; + } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + *os << "that is an effective scalar"; } }; @@ -481,7 +831,23 @@ class ShapePatternRankImpl { explicit constexpr ShapePatternRankImpl(int64 rank) : rank_(rank) {} bool Match(const ::xla::Shape* shape, MatchOption option) const { - return ShapeUtil::Rank(*shape) == rank_; + if (ShapeUtil::Rank(*shape) != rank_) { + if (rank_ == 0) { + EXPLAIN << "Shape is not a scalar"; + } else { + EXPLAIN << "Shape does not have rank " << rank_; + } + return false; + } + return true; + } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + if (rank_ == 0) { + *os << "that is a scalar"; + } else { + *os << "that has " << rank_ << " dimension" << (rank_ != 1 ? "s" : ""); + } } private: @@ -503,8 +869,21 @@ class ShapePatternLayoutImpl { } bool Match(Shape* shape, MatchOption option) const { - return LayoutUtil::HasLayout(*shape) && - layout_.Match(shape->mutable_layout(), option); + if (!LayoutUtil::HasLayout(*shape)) { + EXPLAIN << "Shape does not have a layout"; + return false; + } + if (!layout_.Match(shape->mutable_layout(), option)) { + EXPLAIN << "\nin layout"; + return false; + } + return true; + } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + *os << "with"; + Indent(os, indent + kIndentInc); + layout_.DescribeTo(os, indent + kIndentInc); } private: @@ -522,17 +901,40 @@ class ShapePatternSubshapeImpl { : index_(index), subshape_(subshape) {} bool Match(const ::xla::Shape* shape, MatchOption option) const { - return ShapeUtil::IndexIsValid(*shape, index_) && - subshape_.Match(&ShapeUtil::GetSubshape(*shape, index_), option); + return MatchImpl(shape, option); } bool Match(::xla::Shape* shape, MatchOption option) const { - return ShapeUtil::IndexIsValid(*shape, index_) && - subshape_.Match(ShapeUtil::GetMutableSubshape(shape, index_), - option); + return MatchImpl(shape, option); + } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + *os << "with subshape at index " << index_.ToString() << " which is"; + Indent(os, indent + kIndentInc); + subshape_.DescribeTo(os, indent + kIndentInc); } private: + Shape* GetSubshape(Shape* shape) const { + return ShapeUtil::GetMutableSubshape(shape, index_); + } + const Shape* GetSubshape(const Shape* shape) const { + return &ShapeUtil::GetSubshape(*shape, index_); + } + + template + bool MatchImpl(ShapeType* shape, MatchOption option) const { + if (!ShapeUtil::IndexIsValid(*shape, index_)) { + EXPLAIN << "No subshape at " << index_.ToString(); + return false; + } + if (!subshape_.Match(GetSubshape(shape), option)) { + EXPLAIN << "\nin subshape at " << index_.ToString(); + return false; + } + return true; + } + ShapeIndexView index_; ShapePattern subshape_; }; @@ -542,10 +944,12 @@ template class ShapePattern { private: template - ShapePattern> AppendImpl( - NewImpl new_impl) const { - return ShapePattern>( - AllOf(impl_, std::move(new_impl)), matched_shape_); + auto AppendImpl(NewImpl new_impl) const + -> ShapePattern(std::declval(), + std::move(new_impl)))> { + auto new_all_of = AllOf(impl_, std::move(new_impl)); + return ShapePattern(std::move(new_all_of), + matched_shape_); } public: @@ -560,6 +964,11 @@ class ShapePattern { } return true; } + if (shape) { + EXPLAIN << "\nin " + << (shape->has_layout() ? ShapeUtil::HumanStringWithLayout(*shape) + : ShapeUtil::HumanString(*shape)); + } return false; } @@ -571,9 +980,16 @@ class ShapePattern { } return true; } + EXPLAIN << "\nin " + << (shape->has_layout() ? ShapeUtil::HumanStringWithLayout(*shape) + : ShapeUtil::HumanString(*shape)); return false; } + void DescribeTo(std::ostream* os, int64 indent = 0) const { + return impl_.DescribeTo(os, indent); + } + // Modifies the pattern to match only if the shape equals the given proto. // The layout must outlive the returned pattern. constexpr auto EqualTo(const ::xla::Shape* shape) const @@ -612,6 +1028,11 @@ class ShapePattern { return AppendImpl(ShapePatternIsTupleImpl()); } + constexpr auto IsEffectiveScalar() const + -> decltype(this->AppendImpl(ShapePatternEffectiveScalarImpl())) { + return AppendImpl(ShapePatternEffectiveScalarImpl()); + } + // Modifies the pattern to match only if the shape has the given rank. constexpr auto WithRank(int64 rank) const -> decltype(this->AppendImpl(ShapePatternRankImpl(rank))) { @@ -706,6 +1127,22 @@ Shape(::xla::Shape** matched_shape) { namespace detail { +// Overloads to get a const or non-const operand out of an instruction. +inline HloInstruction* HloOperand(HloInstruction* instr, int64 idx) { + return instr->mutable_operand(idx); +} +inline const HloInstruction* HloOperand(const HloInstruction* instr, + int64 idx) { + return instr->operand(idx); +} + +// Pretty-printer for HloInstruction. Sort of like ToShortString, but with +// fewer %s and more shapes. +inline string InstToString(const HloInstruction* inst) { + return inst->ToString( + HloPrintOptions().set_print_metadata(false).set_print_percent(false)); +} + template class HloInstructionPattern; @@ -714,8 +1151,18 @@ class HloInstructionPattern; class HloInstructionPatternBaseImpl { public: bool Match(const ::xla::HloInstruction* inst, MatchOption option) const { - return inst != nullptr; + if (inst == nullptr) { + EXPLAIN << "HloInstruction* is null"; + return false; + } + return true; + } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + *os << "an HloInstruction"; } + + static constexpr bool kIsTrivialMatcher = true; }; // An HloInstructionPattern implementation that matches only if the instruction @@ -726,13 +1173,44 @@ class HloInstructionPatternNameImpl { : name_(name) {} bool Match(const ::xla::HloInstruction* inst, MatchOption option) const { - return inst->name() == name_; + if (inst->name() != name_) { + EXPLAIN << "HloInstruction not named \"" << name_ << "\""; + return false; + } + return true; + } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + *os << "named \"" << name_ << "\""; } private: absl::string_view name_; }; +// An HloInstructionPattern implementation that matches only if the instruction +// equals a particular pointer. +class HloInstructionIsImpl { + public: + explicit HloInstructionIsImpl(const HloInstruction* inst) : inst_(inst) {} + + bool Match(const ::xla::HloInstruction* inst, MatchOption option) const { + if (inst != inst_) { + EXPLAIN << "HloInstruction " << inst << " is not " << inst_ << " (" + << InstToString(inst_) << ")"; + return false; + } + return true; + } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + *os << "which is " << inst_ << " (" << InstToString(inst_) << ")"; + } + + private: + const HloInstruction* inst_; +}; + // An HloInstructionPattern implementation that matches only if the instruction // has a given opcode. class HloInstructionPatternOpcodeImpl { @@ -742,7 +1220,25 @@ class HloInstructionPatternOpcodeImpl { : opcode_(opcode), invert_(invert) {} bool Match(const ::xla::HloInstruction* inst, MatchOption option) const { - return (invert_ ^ (inst->opcode() == opcode_)); + if (invert_ && inst->opcode() == opcode_) { + EXPLAIN << "HloInstruction has opcode " << HloOpcodeString(opcode_) + << ", expected anything else"; + return false; + } + if (!invert_ && inst->opcode() != opcode_) { + EXPLAIN << "HloInstruction doesn't have opcode " + << HloOpcodeString(opcode_); + return false; + } + return true; + } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + if (!invert_) { + *os << "with opcode " << HloOpcodeString(opcode_); + } else { + *os << "with any opcode other than " << HloOpcodeString(opcode_); + } } private: @@ -757,8 +1253,17 @@ class HloInstructionPatternNumOperandsImpl { explicit constexpr HloInstructionPatternNumOperandsImpl(int64 num_operands) : num_operands_(num_operands) {} - bool Match(const ::xla::HloInstruction* inst, MatchOption /*option*/) const { - return inst->operand_count() == num_operands_; + bool Match(const ::xla::HloInstruction* inst, MatchOption option) const { + if (inst->operand_count() != num_operands_) { + EXPLAIN << "HloInstruction doesn't have " << num_operands_ << " operands"; + return false; + } + return true; + } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + *os << "with " << num_operands_ << " operand" + << (num_operands_ != 1 ? "s" : ""); } private: @@ -775,11 +1280,25 @@ class HloInstructionPatternShapeImpl { : shape_(shape) {} bool Match(const ::xla::HloInstruction* inst, MatchOption option) const { - return shape_.Match(&inst->shape(), option); + if (!shape_.Match(&inst->shape(), option)) { + EXPLAIN << "\nin output shape"; + return false; + } + return true; } bool Match(::xla::HloInstruction* inst, MatchOption option) const { - return shape_.Match(inst->mutable_shape(), option); + if (!shape_.Match(inst->mutable_shape(), option)) { + EXPLAIN << "\nin output shape"; + return false; + } + return true; + } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + *os << "outputting"; + Indent(os, indent + kIndentInc); + shape_.DescribeTo(os, indent + kIndentInc); } private: @@ -797,20 +1316,197 @@ class HloInstructionPatternOperandImpl { : operand_index_(operand_index), operand_(operand) {} bool Match(const ::xla::HloInstruction* inst, MatchOption option) const { - return operand_index_ < inst->operand_count() && - operand_.Match(inst->operand(operand_index_), option); + return MatchImpl(inst, option); } bool Match(::xla::HloInstruction* inst, MatchOption option) const { - return operand_index_ < inst->operand_count() && - operand_.Match(inst->mutable_operand(operand_index_), option); + return MatchImpl(inst, option); + } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + *os << "with operand " << operand_index_ << " which is:"; + Indent(os, indent + kIndentInc); + operand_.DescribeTo(os, indent + kIndentInc); } private: + template + bool MatchImpl(HloInstructionType* inst, MatchOption option) const { + if (operand_index_ >= inst->operand_count()) { + EXPLAIN << "desired operand index " << operand_index_ + << " is out of bounds"; + return false; + } + if (!operand_.Match(HloOperand(inst, operand_index_), option)) { + EXPLAIN << "\nin operand " << operand_index_; + return false; + } + return true; + } + int64 operand_index_; HloInstructionPattern operand_; }; +// Matches a binary instruction whose operands come in any order. +template +class HloInstructionPatternBinaryOperandsAnyOrderImpl { + public: + explicit constexpr HloInstructionPatternBinaryOperandsAnyOrderImpl( + const HloInstructionPattern& op1, + const HloInstructionPattern& op2) + : op1_(op1), op2_(op2) {} + + bool Match(HloInstruction* inst, MatchOption option) const { + return MatchImpl(inst, option); + } + + bool Match(const HloInstruction* inst, MatchOption option) const { + return MatchImpl(inst, option); + } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + *os << "with two operands in either order:"; + Indent(os, indent); + *os << " - "; + op1_.DescribeTo(os, indent + 3); + Indent(os, indent); + *os << " - "; + op2_.DescribeTo(os, indent + 3); + } + + private: + HloInstruction* operand(HloInstruction* inst, int64 idx) const { + return inst->mutable_operand(idx); + } + const HloInstruction* operand(const HloInstruction* inst, int64 idx) const { + return inst->operand(idx); + } + + template + bool MatchImpl(HloInstructionType* inst, MatchOption option) const { + // We could implement this using AnyOf and AllOf matchers, but the templates + // get pretty difficult to debug, since any compile error herein becomes + // not-an-error via SFINAE. Also this way lets us give better messages on + // failure. + if (inst->operand_count() != 2) { + EXPLAIN << "HloInstruction did not have two operands"; + return false; + } + + // If we're not generating explanations, this is pretty simple. + if (!option.explain_os) { + auto try_match = [&](int64 idx1, int64 idx2) { + MatchOption new_option = option; + new_option.capture = false; + if (op1_.Match(operand(inst, idx1), new_option) && + op2_.Match(operand(inst, idx2), new_option)) { + if (option.capture) { + bool matched = op1_.Match(operand(inst, idx1), option) && + op2_.Match(operand(inst, idx2), option); + DCHECK(matched); + } + return true; + } + return false; + }; + return try_match(0, 1) || try_match(1, 0); + } + + // If we are generating explanations, we have some work to do in order to + // generate a helpful error. + // + // First, try all four operand/matcher combinations, recording the + // failure explanations separately from option.explain_os. matches[i][j] + // tells us if matcher_i matches operand j. + bool matches[/*matcher*/ 2][/*operand*/ 2]; + std::stringstream explanations[/*matcher*/ 2][/*operand*/ 2]; + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 2; ++j) { + MatchOption new_option = option; + new_option.capture = false; + new_option.explain_os = &explanations[i][j]; + matches[i][j] = i == 0 ? op1_.Match(operand(inst, j), new_option) + : op2_.Match(operand(inst, j), new_option); + } + } + + // Check if the match succeeded. + for (int i = 0; i < 2; ++i) { + if (matches[0][i] && matches[1][(i + 1) % 2]) { + // Rerun the matches with capture enabled if necessary. + if (option.capture) { + auto* operand1 = operand(inst, i); + auto* operand2 = operand(inst, (i + 1) % 2); + bool matched = + op1_.Match(operand1, option) && op2_.Match(operand2, option); + DCHECK(matched); + } + return true; + } + } + + auto describe_matcher = [&](int matcher_idx) { + EXPLAIN << "\n - "; + if (matcher_idx == 0) { + op1_.DescribeTo(option.explain_os, /*indent=*/3); + } else { + CHECK_EQ(matcher_idx, 1); + op2_.DescribeTo(option.explain_os, /*indent=*/3); + } + for (int i = 0; i < 2; ++i) { + if (matches[matcher_idx][/*operand*/ i]) { + continue; + } + EXPLAIN << "\ndoes not match " << (i == 0 ? "LHS" : "RHS") << ":\n"; + EXPLAIN << " - "; + EXPLAIN << absl::StrReplaceAll( + explanations[matcher_idx][/*operand*/ i].str(), {{"\n", "\n "}}); + } + }; + + // If we failed to match, one of the following is true: + // 1. op1 (op2) matches neither LHS nor RHS, or + // 2. op1 and op2 both match LHS (RHS), but neither matches RHS (LHS). + // We print different explanations depending on which case we're in. + + // Case 1. + bool wrote_explanation = false; + for (int i = 0; !wrote_explanation && i < 2; ++i) { + if (!matches[i][0] && !matches[i][1]) { + EXPLAIN << "HloInstruction's operands (ignoring order) did not match " + << (i == 0 ? "first" : "second") << " matcher. Specifically,"; + describe_matcher(i); + wrote_explanation = true; + } + } + + // Case 2. + for (int i = 0; !wrote_explanation && i < 2; ++i) { + if (matches[/*matcher*/ 0][/*operand*/ i] && + matches[/*matcher*/ 1][/*operand*/ i]) { + CHECK(!matches[0][(i + 1) % 2]); + CHECK(!matches[1][(i + 1) % 2]); + CHECK(!wrote_explanation); + EXPLAIN << "HloInstruction's " << (i == 1 ? "LHS" : "RHS") + << " operand did not match either of the two matchers. " + "Specifically,"; + describe_matcher(0); + EXPLAIN << "\nand"; + describe_matcher(1); + wrote_explanation = true; + } + } + + CHECK(wrote_explanation); + return false; + } + + HloInstructionPattern op1_; + HloInstructionPattern op2_; +}; + // An HloInstructionPattern implementation that matches only if the instruction // is a fusion node with a particular kind. class HloInstructionPatternFusionKindImpl { @@ -820,14 +1516,32 @@ class HloInstructionPatternFusionKindImpl { : kind_(kind) {} bool Match(const ::xla::HloInstruction* inst, MatchOption option) const { - return inst->opcode() == HloOpcode::kFusion && inst->fusion_kind() == kind_; + return MatchImpl(inst, option); } bool Match(::xla::HloInstruction* inst, MatchOption option) const { - return inst->opcode() == HloOpcode::kFusion && inst->fusion_kind() == kind_; + return MatchImpl(inst, option); + } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + *os << "with fusion kind " << ToString(kind_); } private: + template + bool MatchImpl(HloInstructionType* inst, MatchOption option) const { + if (inst->opcode() != HloOpcode::kFusion) { + EXPLAIN << "HloInstruction does not have fusion kind " << ToString(kind_) + << "; it's not a fusion"; + return false; + } + if (inst->fusion_kind() != kind_) { + EXPLAIN << "HloInstruction does not have fusion kind " << ToString(kind_); + return false; + } + return true; + } + ::xla::HloInstruction::FusionKind kind_; }; @@ -839,47 +1553,211 @@ class HloInstructionPatternTupleIndexImpl { : tuple_index_(tuple_index) {} bool Match(const ::xla::HloInstruction* inst, MatchOption option) const { - return inst->opcode() == HloOpcode::kGetTupleElement && - inst->tuple_index() == tuple_index_; + return MatchImpl(inst, option); } bool Match(::xla::HloInstruction* inst, MatchOption option) const { - return inst->opcode() == HloOpcode::kGetTupleElement && - inst->tuple_index() == tuple_index_; + return MatchImpl(inst, option); + } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + *os << "which is a GTE with index " << tuple_index_; } private: + template + bool MatchImpl(HloInstructionType* inst, MatchOption option) const { + if (inst->opcode() != HloOpcode::kGetTupleElement) { + EXPLAIN << "HloInstruction is not a GTE with index " << tuple_index_ + << "; it's not a GTE at all"; + return false; + } + if (inst->tuple_index() != tuple_index_) { + EXPLAIN << "HloInstruction is not a GTE with index " << tuple_index_; + return false; + } + return true; + } + int64 tuple_index_; }; -template -class HloPredicatePatternImpl { +class HloInstructionPatternParameterNumImpl { public: - explicit HloPredicatePatternImpl(Predicate pred) : pred_(std::move(pred)) {} + explicit constexpr HloInstructionPatternParameterNumImpl(int64 parameter_num) + : parameter_num_(parameter_num) {} + + bool Match(const ::xla::HloInstruction* inst, MatchOption option) const { + return MatchImpl(inst, option); + } - bool Match(const ItemType* item, MatchOption option) const { - return pred_(item); + bool Match(::xla::HloInstruction* inst, MatchOption option) const { + return MatchImpl(inst, option); } - bool Match(ItemType* item, MatchOption option) const { return pred_(item); } + void DescribeTo(std::ostream* os, int64 indent = 0) const { + *os << "which is parameter " << parameter_num_; + } private: - Predicate pred_; + template + bool MatchImpl(HloInstructionType* inst, MatchOption option) const { + if (inst->opcode() != HloOpcode::kParameter || + inst->parameter_number() != parameter_num_) { + EXPLAIN << "HloInstruction is not parameter " << parameter_num_; + return false; + } + return true; + } + + int64 parameter_num_; }; -struct PatternFriend; +// Superclass that contains common code used by Op::WithOneUse() and +// Op::WithOneUser(). +class HloInstructionPatternOneUseOrUserImpl { + protected: + bool MatchOneUser(const HloInstruction* inst, MatchOption option) const { + if (inst->user_count() != 1) { + EXPLAIN << "HloInstruction has " << inst->user_count() + << " users, but expected exactly one."; + if (inst->user_count() > 1) { + EXPLAIN << "\nAll users:"; + for (const HloInstruction* user : inst->users()) { + EXPLAIN << "\n - " << InstToString(user); + } + } + return false; + } + return true; + } +}; + +class HloInstructionPatternOneUseImpl + : public HloInstructionPatternOneUseOrUserImpl { + public: + bool Match(const HloInstruction* inst, MatchOption option) const { + if (!MatchOneUser(inst, option)) { + return false; + } + + int64 use_count = absl::c_count_if( + inst->users()[0]->operands(), + [&](const HloInstruction* operand) { return operand == inst; }); + if (use_count != 1) { + EXPLAIN << "HloInstruction is used " << use_count + << " times by its user, but is expected to be used just once: " + << InstToString(inst->users()[0]); + return false; + } + return true; + } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + *os << "which has exactly one use"; + } +}; + +class HloInstructionPatternOneUserImpl + : public HloInstructionPatternOneUseOrUserImpl { + public: + bool Match(const HloInstruction* inst, MatchOption option) const { + return MatchOneUser(inst, option); + } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + *os << "which has exactly one user (but possibly is used multiple times by " + "that instruction)"; + } +}; + +// Matches a constant scalar or effective scalar, optionally with a given value. +template +class HloConstantScalarImpl { + public: + explicit constexpr HloConstantScalarImpl(bool match_effective_scalar) + : val_(absl::nullopt), match_effective_scalar_(match_effective_scalar) {} + + constexpr HloConstantScalarImpl(ScalarTy val, bool match_effective_scalar) + : val_(val), match_effective_scalar_(match_effective_scalar) {} + + bool Match(const ::xla::HloInstruction* inst, MatchOption option) const { + return MatchImpl(inst, option); + } + + bool Match(::xla::HloInstruction* inst, MatchOption option) const { + return MatchImpl(inst, option); + } + + void DescribeTo(std::ostream* os, int64 indent = 0) const { + *os << "which is a constant " + << (match_effective_scalar_ ? "effective " : "") << "scalar"; + if (val_.has_value()) { + *os << " with value " << *val_; + } + } + + private: + template + bool MatchImpl(InstTy* inst, MatchOption option) const { + const auto* const_inst = DynCast(inst); + if (!const_inst) { + EXPLAIN << "HloInstruction is not a constant"; + return false; + } + if (match_effective_scalar_ && + !ShapeUtil::IsEffectiveScalar(inst->shape())) { + EXPLAIN << "HloInstruction is not an effective scalar"; + return false; + } + if (!match_effective_scalar_ && !ShapeUtil::IsScalar(inst->shape())) { + EXPLAIN << "HloInstruction is not a scalar"; + return false; + } + if (!val_.has_value()) { + return true; + } + + // Check that literal == static_cast(val) and + // val == static_cast(literal). This is sufficient to ensure that + // the two constant scalars are actually "equal". + auto val_literal = LiteralUtil::CreateR0(*val_); + auto literal_r0_or = const_inst->literal().Reshape({}); + auto val_as_literal_ty_or = + val_literal.Convert(const_inst->shape().element_type()); + if (!literal_r0_or.ok() || !val_as_literal_ty_or.ok()) { + EXPLAIN << "could not construct relevant Literals (how did this happen?)"; + return false; + } + auto literal_r0 = std::move(literal_r0_or).ValueOrDie(); + auto val_as_literal_ty = std::move(val_as_literal_ty_or).ValueOrDie(); + auto literal_r0_as_val_ty_or = + literal_r0.Convert(val_literal.shape().element_type()); + bool rv = literal_r0_as_val_ty_or.ok() && // + literal_r0_as_val_ty_or.ValueOrDie() == val_literal && + literal_r0 == val_as_literal_ty; + if (!rv) { + EXPLAIN << "HloInstruction's constant value " << literal_r0.ToString() + << " did not match expected value " << *val_; + } + return rv; + } + + absl::optional val_; + bool match_effective_scalar_; +}; // A pattern that matches HloInstructions. template class HloInstructionPattern { private: template - HloInstructionPattern> - AppendImpl(NewImpl new_impl) const { - return HloInstructionPattern< - HloInstructionType, AllOfPattern<::xla::HloInstruction, Impl, NewImpl>>( - AllOf(impl_, std::move(new_impl)), matched_inst_); + auto AppendImpl(NewImpl new_impl) const -> HloInstructionPattern< + HloInstructionType, decltype(AllOf( + std::declval(), std::move(new_impl)))> { + auto new_allof = AllOf(impl_, std::move(new_impl)); + return HloInstructionPattern( + std::move(new_allof), matched_inst_); } public: @@ -895,6 +1773,9 @@ class HloInstructionPattern { } return true; } + if (inst != nullptr) { + EXPLAIN << "\nin " << InstToString(inst); + } return false; } @@ -906,6 +1787,7 @@ class HloInstructionPattern { } return true; } + EXPLAIN << "\nin " << InstToString(inst); return false; } @@ -935,12 +1817,47 @@ class HloInstructionPattern { return AppendImpl(HloInstructionPatternOpcodeImpl(opcode, true)); } + constexpr auto Is(const HloInstruction* instr) const + -> decltype(this->AppendImpl(HloInstructionIsImpl(instr))) { + return AppendImpl(HloInstructionIsImpl(instr)); + } + // Modifies the pattern to match only if the instruction is a constant. constexpr auto IsConstant() const -> decltype(this->WithOpcode(HloOpcode::kConstant)) { return WithOpcode(HloOpcode::kConstant); } + constexpr auto IsConstantScalar() const -> decltype(this->AppendImpl( + HloConstantScalarImpl(/*match_effective_scalar=*/false))) { + return AppendImpl( + HloConstantScalarImpl(/*match_effective_scalar=*/false)); + } + + // This does not check that T has the same type as the instruction, so e.g. + // IsConstantScalar(1.0) may match a constant of shape int32[]. + template + constexpr auto IsConstantScalar(const ScalarTy& val) const + -> decltype(this->AppendImpl(HloConstantScalarImpl( + val, /*match_effective_scalar=*/false))) { + return AppendImpl( + HloConstantScalarImpl(val, /*match_effective_scalar=*/false)); + } + + constexpr auto IsConstantEffectiveScalar() const -> decltype(this->AppendImpl( + HloConstantScalarImpl(/*match_effective_scalar=*/true))) { + return AppendImpl( + HloConstantScalarImpl(/*match_effective_scalar=*/true)); + } + + template + constexpr auto IsConstantEffectiveScalar(const ScalarTy& val) const + -> decltype(this->AppendImpl(HloConstantScalarImpl( + val, /*match_effective_scalar=*/true))) { + return AppendImpl( + HloConstantScalarImpl(val, /*match_effective_scalar=*/true)); + } + // Modifies the pattern to match only if the instruction is not a constant. constexpr auto IsNonConstant() const -> decltype(this->WithoutOpcode(HloOpcode::kConstant)) { @@ -957,6 +1874,22 @@ class HloInstructionPattern { HloInstructionPatternShapeImpl(shape)); } + // Make this a templated function to work around gcc 4.9.4 template infinite + // recursion bug. + template + constexpr auto WithShapeEqualTo(const ::xla::Shape* shape) + -> decltype(this->WithShape(Shape().EqualTo(shape))) { + return WithShape(Shape().EqualTo(shape)); + } + + // Make this a templated function to work around gcc 4.9.4 template infinite + // recursion bug. + template + constexpr auto WithShapeCompatibleTo(const ::xla::Shape* shape) + -> decltype(this->WithShape(Shape().CompatibleTo(shape))) { + return WithShape(Shape().CompatibleTo(shape)); + } + // Modifies the pattern to match only if the instruction has an operand that // matches the given pattern. template @@ -971,6 +1904,20 @@ class HloInstructionPattern { operand_index, operand)); } + template + constexpr auto WithBinaryOperandsAnyOrder( + const HloInstructionPattern& op1, + const HloInstructionPattern& op2) const + -> decltype(this->AppendImpl( + HloInstructionPatternBinaryOperandsAnyOrderImpl< + OperandType1, OperandImpl1, OperandType2, OperandImpl2>(op1, + op2))) { + return AppendImpl( + HloInstructionPatternBinaryOperandsAnyOrderImpl< + OperandType1, OperandImpl1, OperandType2, OperandImpl2>(op1, op2)); + } + // Modifies the pattern to match only if the instruction is a fusion node with // the given kind. constexpr auto WithFusionKind(HloInstruction::FusionKind kind) const @@ -985,17 +1932,34 @@ class HloInstructionPattern { return AppendImpl(HloInstructionPatternTupleIndexImpl(tuple_index)); } - private: - template - constexpr auto WithPredicate(Predicate pred) const -> decltype( - this->AppendImpl(HloPredicatePatternImpl( - std::move(pred)))) { - return AppendImpl( - HloPredicatePatternImpl(std::move(pred))); + // Modifies the pattern to match only if the instruction is a parameter + // with the given parameter number. + constexpr auto WithParameterNum(int64 parameter_num) const -> decltype( + this->AppendImpl(HloInstructionPatternParameterNumImpl(parameter_num))) { + return AppendImpl(HloInstructionPatternParameterNumImpl(parameter_num)); } - friend struct PatternFriend; + // Modifies the pattern to match if the instruction is used exactly once. + // Does not match if the instruction is used twice by the same user (e.g. + // multiply(x,x)). + constexpr auto WithOneUse() const + -> decltype(this->AppendImpl(HloInstructionPatternOneUseImpl())) { + return AppendImpl(HloInstructionPatternOneUseImpl()); + } + + // Modifies the pattern to match if the instruction is used by exactly one + // other instruction. Will match if the instruction is used twice, so long as + // it's by the same user (e.g. multiply(x,x)). + constexpr auto WithOneUser() const + -> decltype(this->AppendImpl(HloInstructionPatternOneUserImpl())) { + return AppendImpl(HloInstructionPatternOneUserImpl()); + } + void DescribeTo(std::ostream* os, int64 indent = 0) const { + impl_.DescribeTo(os, indent); + } + + private: Impl impl_; HloInstructionType** matched_inst_; }; @@ -1036,6 +2000,7 @@ Op(::xla::HloInstruction** matched_inst) { XLA_NULLOP_PATTERN(Constant) XLA_NULLOP_PATTERN(Parameter) XLA_NULLOP_PATTERN(Iota) +XLA_NULLOP_PATTERN(Rng) #undef XLA_NULLOP_PATTERN // Helpers for unary instructions. @@ -1067,8 +2032,10 @@ XLA_UNOP_PATTERN(RoundNearestAfz) XLA_UNOP_PATTERN(Bitcast) XLA_UNOP_PATTERN(Broadcast) XLA_UNOP_PATTERN(Ceil) +XLA_UNOP_PATTERN(Convert) XLA_UNOP_PATTERN(Copy) XLA_UNOP_PATTERN(Cos) +XLA_UNOP_PATTERN(CrossReplicaSum) XLA_UNOP_PATTERN(Exp) XLA_UNOP_PATTERN(Fft) XLA_UNOP_PATTERN(Floor) @@ -1088,6 +2055,7 @@ XLA_UNOP_PATTERN(Reverse) XLA_UNOP_PATTERN(SendDone) XLA_UNOP_PATTERN(Sign) XLA_UNOP_PATTERN(Sin) +XLA_UNOP_PATTERN(Slice) XLA_UNOP_PATTERN(Sort) XLA_UNOP_PATTERN(Tanh) XLA_UNOP_PATTERN(Transpose) @@ -1125,25 +2093,32 @@ XLA_UNOP_PATTERN(Transpose) #define XLA_COMMUTATIVE_BINOP_PATTERN(NAME) \ XLA_BINOP_PATTERN(NAME) \ \ - template \ - inline auto NAME##AnyOrder(Lhs&& lhs, Rhs&& rhs) \ - ->decltype(AnyOf(NAME(lhs, rhs), NAME(rhs, lhs))) { \ - return AnyOf(NAME(lhs, rhs), NAME(rhs, lhs)); \ - } \ - \ template \ inline auto NAME##AnyOrder(HloInstructionType** matched_inst, Lhs&& lhs, \ Rhs&& rhs) \ - ->decltype(AnyOf(NAME(matched_inst, lhs, rhs), \ - NAME(matched_inst, rhs, lhs))) { \ - return AnyOf(NAME(matched_inst, lhs, rhs), \ - NAME(matched_inst, rhs, lhs)); \ + ->decltype(Op(matched_inst) \ + .WithOpcode(HloOpcode::k##NAME) \ + .WithBinaryOperandsAnyOrder(std::forward(lhs), \ + std::forward(rhs))) { \ + return Op(matched_inst) \ + .WithOpcode(HloOpcode::k##NAME) \ + .WithBinaryOperandsAnyOrder(std::forward(lhs), \ + std::forward(rhs)); \ + } \ + template \ + inline auto NAME##AnyOrder(Lhs&& lhs, Rhs&& rhs) \ + ->decltype(NAME##AnyOrder( \ + nullptr, std::forward(lhs), std::forward(rhs))) { \ + return NAME##AnyOrder( \ + nullptr, std::forward(lhs), std::forward(rhs)); \ } XLA_COMMUTATIVE_BINOP_PATTERN(Add) XLA_BINOP_PATTERN(Atan2) XLA_BINOP_PATTERN(Divide) XLA_BINOP_PATTERN(Complex) +XLA_BINOP_PATTERN(Convolution) XLA_BINOP_PATTERN(Dot) +XLA_BINOP_PATTERN(DynamicSlice) XLA_COMMUTATIVE_BINOP_PATTERN(Eq) XLA_BINOP_PATTERN(Gather) XLA_BINOP_PATTERN(Ge) @@ -1155,7 +2130,9 @@ XLA_COMMUTATIVE_BINOP_PATTERN(Minimum) XLA_COMMUTATIVE_BINOP_PATTERN(Multiply) XLA_COMMUTATIVE_BINOP_PATTERN(Ne) XLA_BINOP_PATTERN(Outfeed) +XLA_BINOP_PATTERN(Pad) XLA_BINOP_PATTERN(Power) +XLA_BINOP_PATTERN(ReduceWindow) XLA_BINOP_PATTERN(Remainder) XLA_BINOP_PATTERN(Send) XLA_BINOP_PATTERN(Subtract) @@ -1202,6 +2179,7 @@ XLA_BINOP_PATTERN(ShiftRightLogical) .WithOperand(2, std::forward(arg2)); \ } XLA_TERNOP_PATTERN(Clamp); +XLA_TERNOP_PATTERN(Scatter); XLA_TERNOP_PATTERN(Select); #undef XLA_TERNOP_PATTERN @@ -1255,31 +2233,10 @@ inline auto WithOperands(Matcher&& m, int64 operand_num, FirstArg&& first_arg, // We could implement all ops as "variadic" ops, but it would make the // already-bad compile errors even worse. XLA_VARIADIC_OP_PATTERN(Concatenate); +XLA_VARIADIC_OP_PATTERN(CustomCall); +XLA_VARIADIC_OP_PATTERN(Map) XLA_VARIADIC_OP_PATTERN(Reduce); - -namespace detail { -struct PatternFriend { - template - static auto ConstantScalar(T constant) -> decltype( - Constant() - .WithShape(match::Shape().IsScalar()) - .WithPredicate( - std::declval>())) { - std::function pred = - [constant](const HloInstruction* instr) { - const auto& literal = Cast(instr)->literal(); - auto status_or_const = LiteralUtil::CreateR0(constant).Convert( - literal.shape().element_type()); - return status_or_const.ok() && - literal == status_or_const.ConsumeValueOrDie(); - }; - - return Constant() - .WithShape(match::Shape().IsScalar()) - .WithPredicate(std::move(pred)); - } -}; -} // namespace detail +XLA_VARIADIC_OP_PATTERN(Tuple); // Helpers for matching non-constant instructions. inline auto NonConstant() -> decltype(Op().IsNonConstant()) { @@ -1318,14 +2275,71 @@ inline auto GetTupleElement(HloInstructionType** matched_inst, Arg&& arg, .WithTupleIndex(tuple_index); } -template -inline auto ConstantScalar(T constant) - -> decltype(detail::PatternFriend::ConstantScalar(constant)) { - return detail::PatternFriend::ConstantScalar(constant); +// Add overloads for Parameter which take an int64 specifying the parameter +// number. +inline auto Parameter(int64 parameter_num) -> decltype( + Op().WithOpcode(HloOpcode::kParameter).WithParameterNum(parameter_num)) { + return Op().WithOpcode(HloOpcode::kParameter).WithParameterNum(parameter_num); +} +template +inline auto Parameter(HloInstructionType** matched_inst, int64 parameter_num) + -> decltype(Op(matched_inst) + .WithOpcode(HloOpcode::kParameter) + .WithParameterNum(parameter_num)) { + return Op(matched_inst) + .WithOpcode(HloOpcode::kParameter) + .WithParameterNum(parameter_num); +} + +inline auto ConstantScalar() -> decltype(Op().IsConstantScalar()) { + return Op().IsConstantScalar(); +} + +template +inline auto ConstantScalar(HloInstructionType** matched_inst) + -> decltype(Op(matched_inst).IsConstantScalar()) { + return Op(matched_inst).IsConstantScalar(); +} + +template +inline auto ConstantScalar(ScalarTy val) + -> decltype(Op().IsConstantScalar(val)) { + return Op().IsConstantScalar(val); +} + +template +inline auto ConstantScalar(HloInstructionType** matched_inst, ScalarTy val) + -> decltype(Op(matched_inst).IsConstantScalar(val)) { + return Op(matched_inst).IsConstantScalar(val); +} + +inline auto ConstantEffectiveScalar() -> decltype(Op().IsConstantScalar()) { + return Op().IsConstantEffectiveScalar(); +} + +template +inline auto ConstantEffectiveScalar(HloInstructionType** matched_inst) + -> decltype(Op(matched_inst).IsConstantScalar()) { + return Op(matched_inst).IsConstantEffectiveScalar(); +} + +template +inline auto ConstantEffectiveScalar(ScalarTy val) + -> decltype(Op().IsConstantEffectiveScalar(val)) { + return Op().IsConstantEffectiveScalar(val); +} + +template +inline auto ConstantEffectiveScalar(HloInstructionType** matched_inst, + ScalarTy val) + -> decltype(Op(matched_inst).IsConstantEffectiveScalar(val)) { + return Op(matched_inst).IsConstantEffectiveScalar(val); } } // namespace match } // namespace xla +#undef EXPLAIN +#pragma pop_macro("EXPLAIN") #endif // TENSORFLOW_COMPILER_XLA_SERVICE_PATTERN_MATCHER_H_ diff --git a/tensorflow/compiler/xla/service/pattern_matcher_gmock.h b/tensorflow/compiler/xla/service/pattern_matcher_gmock.h new file mode 100644 index 0000000000..8fe2d10a11 --- /dev/null +++ b/tensorflow/compiler/xla/service/pattern_matcher_gmock.h @@ -0,0 +1,92 @@ +/* Copyright 2018 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_COMPILER_XLA_SERVICE_PATTERN_MATCHER_GMOCK_H_ +#define TENSORFLOW_COMPILER_XLA_SERVICE_PATTERN_MATCHER_GMOCK_H_ + +#include +#include "tensorflow/compiler/xla/service/pattern_matcher.h" +#include "tensorflow/compiler/xla/test.h" +#include "tensorflow/core/platform/test.h" + +namespace xla { + +namespace pattern_matcher_gmock_detail { +template +class GmockMatcher { + public: + explicit GmockMatcher(Pattern p) : pattern_(std::move(p)) {} + + // In service of better error messages, list out the overloads explicitly + // rather than just using a template. gMock's polymorphism plus + // pattern_matcher yields some pretty gnarly stuff. + bool MatchAndExplain(const Layout& l, + ::testing::MatchResultListener* listener) const { + return MatchAndExplainImpl(&l, listener); + } + bool MatchAndExplain(const Layout* l, + ::testing::MatchResultListener* listener) const { + return MatchAndExplainImpl(l, listener); + } + + bool MatchAndExplain(const Shape& s, + ::testing::MatchResultListener* listener) const { + return MatchAndExplainImpl(&s, listener); + } + bool MatchAndExplain(const Shape* s, + ::testing::MatchResultListener* listener) const { + return MatchAndExplainImpl(s, listener); + } + + bool MatchAndExplain(const HloInstruction& instr, + ::testing::MatchResultListener* listener) const { + return MatchAndExplainImpl(&instr, listener); + } + bool MatchAndExplain(const HloInstruction* instr, + ::testing::MatchResultListener* listener) const { + return MatchAndExplainImpl(instr, listener); + } + + void DescribeTo(std::ostream* os) const { pattern_.DescribeTo(os); } + + void DescribeNegationTo(std::ostream* os) const { + *os << "is NOT: "; + DescribeTo(os); + } + + private: + template + bool MatchAndExplainImpl(const T* t, + ::testing::MatchResultListener* listener) const { + MatchOption options{/*.capture=*/true, /*.explain_os=*/listener->stream()}; + return Match(t, pattern_, options); + } + + Pattern pattern_; +}; +} // namespace pattern_matcher_gmock_detail + +template +::testing::PolymorphicMatcher< + pattern_matcher_gmock_detail::GmockMatcher> +GmockMatch(Pattern&& p) { + return ::testing::MakePolymorphicMatcher( + pattern_matcher_gmock_detail::GmockMatcher( + std::forward(p))); +} + +} // namespace xla + +#endif // TENSORFLOW_COMPILER_XLA_SERVICE_PATTERN_MATCHER_GMOCK_H_ diff --git a/tensorflow/compiler/xla/service/pattern_matcher_gmock_test.cc b/tensorflow/compiler/xla/service/pattern_matcher_gmock_test.cc new file mode 100644 index 0000000000..9ca2fb05c1 --- /dev/null +++ b/tensorflow/compiler/xla/service/pattern_matcher_gmock_test.cc @@ -0,0 +1,76 @@ +/* Copyright 2018 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/compiler/xla/service/pattern_matcher_gmock.h" +#include "tensorflow/compiler/xla/service/pattern_matcher.h" +#include "tensorflow/compiler/xla/shape_util.h" +#include "tensorflow/compiler/xla/test.h" +#include "tensorflow/core/platform/test.h" + +namespace xla { +namespace { + +namespace m = ::xla::match; +using ::testing::Eq; +using ::testing::Not; + +template +string Describe(const ::testing::Matcher& m) { + std::stringstream ss; + m.DescribeTo(&ss); + return ss.str(); +} + +template +string Explain( + const MatchedTy& val, + const ::testing::Matcher::type>& m) { + ::testing::StringMatchResultListener listener; + EXPECT_THAT(val, ::testing::Not(m)); // For the error message. + EXPECT_FALSE(m.MatchAndExplain(val, &listener)); + return listener.str(); +} + +// This file tests the GmockMatch function. The actual explanation and +// description returned by matchers is tested in pattern_matchers_test. +TEST(PatternMatcherGmock, MatchShape) { + Shape s = ShapeUtil::MakeShape(F32, {10, 100}); + // You can pass const Shape& or a const Shape*. + EXPECT_THAT(s, GmockMatch(m::Shape())); + EXPECT_THAT(&s, Not(GmockMatch(m::Shape().WithElementType(F16)))); + EXPECT_THAT(Describe(GmockMatch(m::Shape().IsArray())), + "a shape that represents an array"); +} + +TEST(PatternMatcherGmock, MatchLayout) { + Layout l = LayoutUtil::MakeLayout({0, 1}); + EXPECT_THAT(l, GmockMatch(m::Layout())); + EXPECT_THAT(&l, Not(GmockMatch(m::Layout().WithSparseFormat()))); + EXPECT_THAT(Describe(GmockMatch(m::Layout().WithSparseFormat())), + "a layout with format SPARSE"); +} + +TEST(PatternMatchGmock, MatchInstruction) { + auto instr = + HloInstruction::CreateParameter(0, ShapeUtil::MakeShape(F32, {42}), "p"); + EXPECT_THAT(instr.get(), GmockMatch(m::Parameter())); + EXPECT_THAT(*instr, GmockMatch(m::Parameter(0))); + EXPECT_THAT(*instr, Not(GmockMatch(m::Parameter(1)))); + EXPECT_THAT(Describe(GmockMatch(m::Parameter())), + "an HloInstruction with opcode parameter"); +} + +} // anonymous namespace +} // namespace xla diff --git a/tensorflow/compiler/xla/service/pattern_matcher_test.cc b/tensorflow/compiler/xla/service/pattern_matcher_test.cc index 3f74273517..186ef0c791 100644 --- a/tensorflow/compiler/xla/service/pattern_matcher_test.cc +++ b/tensorflow/compiler/xla/service/pattern_matcher_test.cc @@ -14,14 +14,18 @@ limitations under the License. ==============================================================================*/ #include "tensorflow/compiler/xla/service/pattern_matcher.h" +#include "absl/strings/str_cat.h" #include "tensorflow/compiler/xla/service/hlo_instruction.h" #include "tensorflow/compiler/xla/service/hlo_opcode.h" #include "tensorflow/compiler/xla/service/hlo_parser.h" +#include "tensorflow/compiler/xla/test.h" #include "tensorflow/core/platform/test.h" namespace xla { namespace { +namespace m = match; + TEST(PatternMatcherTest, AddOp) { constexpr char kModuleStr[] = R"(HloModule two_plus_two_module ENTRY %two_plus_two_computation () -> f32[] { @@ -229,23 +233,74 @@ TEST(PatternMatcherTest, AnyOf) { } TEST(PatternMatcherTest, ConstantScalar) { - constexpr char kModuleStr[] = R"( - HloModule test_module ENTRY test { ROOT constant = f16[] constant(42) })"; - TF_ASSERT_OK_AND_ASSIGN(auto hlo_module, ParseHloString(kModuleStr)); - auto* root = hlo_module->entry_computation()->root_instruction(); - - EXPECT_TRUE(Match(root, match::ConstantScalar(42))); - EXPECT_FALSE(Match(root, match::ConstantScalar(41))); - EXPECT_FALSE(Match(root, match::ConstantScalar(0))); -} + using match::ConstantEffectiveScalar; + using match::ConstantScalar; + using match::Op; + using match::Tuple; -TEST(PatternMatcherTest, NoMatchConstantScalar) { constexpr char kModuleStr[] = R"( - HloModule test_module ENTRY test { ROOT v = f16[] parameter(0) })"; + HloModule test_module + ENTRY test { + a = s32[] constant(1) + b = s32[1,1] constant(s32[1,1]{{2}}) + c = s32[1,2] constant(s32[1,2]{{2,2}}) + d = f32[] constant(1) + e = f32[] constant(1.25) + ROOT tuple = (s32[], s32[1,1], s32[1,2], f32[], f32[]) tuple(a,b,c,d,e) + })"; TF_ASSERT_OK_AND_ASSIGN(auto hlo_module, ParseHloString(kModuleStr)); auto* root = hlo_module->entry_computation()->root_instruction(); - EXPECT_FALSE(Match(root, match::ConstantScalar(42))); + const HloInstruction* a = root->operand(0); + const HloInstruction* b = root->operand(1); + const HloInstruction* c = root->operand(2); + const HloInstruction* d = root->operand(3); + const HloInstruction* e = root->operand(4); + EXPECT_TRUE(Match(a, ConstantScalar())); + EXPECT_TRUE(Match(a, ConstantScalar(1))); + EXPECT_TRUE(Match(a, ConstantEffectiveScalar())); + EXPECT_TRUE(Match(a, ConstantEffectiveScalar(1))); + EXPECT_FALSE(Match(a, ConstantScalar(2))); + EXPECT_FALSE(Match(a, ConstantScalar(2.01))); + EXPECT_FALSE(Match(a, ConstantEffectiveScalar(2))); + EXPECT_FALSE(Match(a, ConstantEffectiveScalar(1.01))); + + EXPECT_FALSE(Match(b, ConstantScalar())); + EXPECT_FALSE(Match(b, ConstantScalar(2))); + EXPECT_TRUE(Match(b, ConstantEffectiveScalar())); + EXPECT_TRUE(Match(b, ConstantEffectiveScalar(2))); + + EXPECT_FALSE(Match(c, ConstantScalar())); + EXPECT_FALSE(Match(c, ConstantScalar(2))); + EXPECT_FALSE(Match(c, ConstantEffectiveScalar())); + EXPECT_FALSE(Match(c, ConstantEffectiveScalar(2))); + + EXPECT_TRUE(Match(d, ConstantScalar(1))); + EXPECT_TRUE(Match(d, ConstantEffectiveScalar(1))); + EXPECT_TRUE(Match(d, ConstantScalar(1.0))); + EXPECT_TRUE(Match(d, ConstantEffectiveScalar(1.0))); + + EXPECT_TRUE(Match(e, ConstantScalar(1.25f))); + EXPECT_TRUE(Match(e, ConstantScalar(1.25))); + EXPECT_TRUE(Match(e, ConstantEffectiveScalar(1.25))); + EXPECT_FALSE(Match(e, ConstantScalar(1))); + EXPECT_FALSE(Match(e, ConstantEffectiveScalar(1))); + + const HloInstruction* instr = nullptr; + EXPECT_TRUE(Match(a, ConstantScalar(&instr))); + EXPECT_EQ(instr, a); + + instr = nullptr; + EXPECT_TRUE(Match(a, ConstantScalar(&instr, 1))); + EXPECT_EQ(instr, a); + + instr = nullptr; + EXPECT_TRUE(Match(a, ConstantEffectiveScalar(&instr))); + EXPECT_EQ(instr, a); + + instr = nullptr; + EXPECT_TRUE(Match(a, ConstantEffectiveScalar(&instr, 1))); + EXPECT_EQ(instr, a); } TEST(PatternMatcherTest, MultiplyAnyOrder) { @@ -267,6 +322,15 @@ TEST(PatternMatcherTest, MultiplyAnyOrder) { root, MultiplyAnyOrder(&instr, ConstantScalar(42), ConstantScalar(52)))); EXPECT_TRUE(Match( root, MultiplyAnyOrder(&instr, ConstantScalar(52), ConstantScalar(42)))); + + // Check that MultiplyAnyOrder exposes the same API as Op(), so we can call + // e.g. IsNonConstant() on it. + EXPECT_TRUE(Match( + root, MultiplyAnyOrder(&instr, ConstantScalar(42), ConstantScalar(52)) + .IsNonConstant())); + EXPECT_TRUE( + Match(root, MultiplyAnyOrder(ConstantScalar(42), ConstantScalar(52)) + .IsNonConstant())); } TEST(PatternMatcherTest, AnyOfShortCircuit) { @@ -315,14 +379,22 @@ TEST(PatternMatcherTest, AllOf) { TF_ASSERT_OK_AND_ASSIGN(auto hlo_module, ParseHloString(kModuleStr)); auto* root = hlo_module->entry_computation()->root_instruction(); + auto f16_scalar = ShapeUtil::MakeShape(F16, {}); + auto f16_pattern = Constant().WithShapeEqualTo(&f16_scalar); + auto f16_compatible_pattern = Constant().WithShapeCompatibleTo(&f16_scalar); auto scalar_pattern = Constant().WithShape(match::Shape().IsScalar()); - auto f16_pattern = Constant().WithShape(match::Shape().WithElementType(F16)); ASSERT_TRUE(Match(root, scalar_pattern)); ASSERT_TRUE(Match(root, f16_pattern)); - EXPECT_TRUE(Match(root, AllOf(scalar_pattern, f16_pattern))); - EXPECT_TRUE(Match(root, AllOf(f16_pattern, scalar_pattern))); + ASSERT_TRUE(Match(root, f16_compatible_pattern)); + EXPECT_TRUE(Match(root, AllOf(scalar_pattern, f16_pattern, + f16_compatible_pattern))); + EXPECT_TRUE( + Match(root, AllOf(f16_pattern, f16_compatible_pattern, + scalar_pattern))); EXPECT_FALSE( Match(root, AllOf(Broadcast(Op()), f16_pattern))); + EXPECT_FALSE(Match( + root, AllOf(Broadcast(Op()), f16_compatible_pattern))); EXPECT_FALSE( Match(root, AllOf(Broadcast(Op()), scalar_pattern))); } @@ -431,5 +503,433 @@ TEST(PatternMatcherTest, TestConcat) { Reshape(ConstantScalar(4))))); } +template +string Description(const Pattern& pattern) { + std::stringstream ss; + pattern.DescribeTo(&ss); + return ss.str(); +} + +template +string Explanation(Elem* elem, const Pattern& pattern) { + std::stringstream ss; + MatchOption options{/*.capture=*/true, /*.explain_os=*/&ss}; + Match(elem, pattern, options); + return ss.str(); +} +template +string Explanation(const std::unique_ptr& elem, const Pattern& pattern) { + return Explanation(elem.get(), pattern); +} +template +string Explanation(const Elem& elem, const Pattern& pattern) { + return Explanation(&elem, pattern); +} + +// Helper macro for checking a pattern's description and the explanation printed +// when attempting to match (and presumably failing) on a given object. +// +// We use a macro rather than a function because we want good line numbers in +// errors. We use this rather than writing a helper that returns a pair of +// (description, explanation) and doing something like +// +// EXPECT_THAT(DescAndExplanation(...), ::testing::Pair(..., ...)); +// +// because EXPECT_EQ prints a unified diff if multiline string comparison fails, +// while EXPECT_THAT does not. This unified diff makes the errors much easier +// to read. +#define EXPECT_DESC_AND_EXPLANATION(elem, pattern, expected_desc, \ + expected_explanation) \ + do { \ + EXPECT_EQ(Description(pattern), (expected_desc)); \ + EXPECT_EQ(Explanation((elem), (pattern)), expected_explanation); \ + } while (0) + +TEST(PatternMatcherTest, LayoutDescribeToAndExplain) { + auto layout = LayoutUtil::MakeLayout({1, 2}); + auto layout2 = LayoutUtil::MakeLayout({2, 2}); + + EXPECT_DESC_AND_EXPLANATION(static_cast(nullptr), m::Layout(), + "a layout", "Layout is null"); + EXPECT_DESC_AND_EXPLANATION(layout2, m::Layout().EqualTo(&layout), + "a layout equal to {1,2}", + "Layout {2,2} is not equal to expected {1,2}"); + EXPECT_DESC_AND_EXPLANATION(layout2, m::Layout().WithSparseFormat(), + "a layout with format SPARSE", + "Layout has format DENSE but expected SPARSE"); + EXPECT_DESC_AND_EXPLANATION(layout, + m::Layout().EqualTo(&layout).WithSparseFormat(), + "a layout:\n" + " * equal to {1,2} AND\n" + " * with format SPARSE", + "Layout has format DENSE but expected SPARSE"); +} + +TEST(PatternMatcherTest, ShapeDescribeToAndExplain) { + auto shape = ShapeUtil::MakeShapeWithLayout(F32, {1, 2}, {0, 1}); + auto layout = shape.layout(); + + EXPECT_DESC_AND_EXPLANATION(static_cast(nullptr), m::Shape(), + "a shape", "Shape is null"); + EXPECT_DESC_AND_EXPLANATION( + ShapeUtil::MakeShapeWithLayout(F32, {1, 2}, {1, 0}), + m::Shape().EqualTo(&shape), "a shape equal to f32[1,2]{0,1}", + "Shape not equal to f32[1,2]{0,1}\n" + "in f32[1,2]{1,0}"); + EXPECT_DESC_AND_EXPLANATION(ShapeUtil::MakeShape(F32, {2, 2}), + m::Shape().CompatibleTo(&shape), + "a shape compatible with f32[1,2]", + "Shape not compatible with f32[1,2]\n" + "in f32[2,2]{1,0}"); + EXPECT_DESC_AND_EXPLANATION(shape, m::Shape().WithElementType(F16), + "a shape with element type F16", + "Shape does not have element type F16\n" + "in f32[1,2]{0,1}"); + EXPECT_DESC_AND_EXPLANATION(shape, m::Shape().IsScalar(), + "a shape that represents a scalar", + "Shape is not a scalar\n" + "in f32[1,2]{0,1}"); + EXPECT_DESC_AND_EXPLANATION(ShapeUtil::MakeNil(), m::Shape().IsArray(), + "a shape that represents an array", + "Shape is not an array\n" + "in ()"); + EXPECT_DESC_AND_EXPLANATION(shape, m::Shape().IsTuple(), + "a shape that represents a tuple", + "Shape is not a tuple\n" + "in f32[1,2]{0,1}"); + EXPECT_DESC_AND_EXPLANATION(shape, m::Shape().IsEffectiveScalar(), + "a shape that is an effective scalar", + "Shape is not an effective scalar\n" + "in f32[1,2]{0,1}"); + EXPECT_DESC_AND_EXPLANATION(shape, m::Shape().WithRank(42), + "a shape that has 42 dimensions", + "Shape does not have rank 42\n" + "in f32[1,2]{0,1}"); + EXPECT_DESC_AND_EXPLANATION(shape, m::Shape().WithRank(0), + "a shape that is a scalar", + "Shape is not a scalar\n" + "in f32[1,2]{0,1}"); + EXPECT_DESC_AND_EXPLANATION(shape, m::Shape().WithRank(1).IsArray(), + "a shape:\n" + " * that has 1 dimension AND\n" + " * that represents an array", + "Shape does not have rank 1\n" + "in f32[1,2]{0,1}"); + EXPECT_DESC_AND_EXPLANATION(ShapeUtil::MakeNil(), + m::Shape().IsArray().WithRank(1), + "a shape:\n" + " * that represents an array AND\n" + " * that has 1 dimension", + "Shape is not an array\n" + "in ()"); + EXPECT_DESC_AND_EXPLANATION( + ShapeUtil::MakeShapeWithLayout(F32, {1, 2}, {1, 0}), + m::Shape().WithLayoutEqualTo(&layout), + "a shape with\n a layout equal to {0,1}", + "Layout {1,0} is not equal to expected {0,1}\n" + "in f32[1,2]{1,0}"); + EXPECT_DESC_AND_EXPLANATION( + shape, m::Shape().WithLayout(m::Layout().WithSparseFormat()), + "a shape with\n a layout with format SPARSE", + "Layout has format DENSE but expected SPARSE\n" + "in f32[1,2]{0,1}"); + EXPECT_DESC_AND_EXPLANATION(shape, + m::Shape().WithSubshapeEqualTo({10}, &shape), + "a shape with subshape at index {10} which is\n" + " a shape equal to f32[1,2]{0,1}", + "No subshape at {10}\n" + "in f32[1,2]{0,1}"); + EXPECT_DESC_AND_EXPLANATION( + ShapeUtil::MakeTupleShape({ShapeUtil::MakeShape(F32, {2, 2})}), + m::Shape().WithSubshapeEqualTo({0}, &shape), + "a shape with subshape at index {0} which is\n" + " a shape equal to f32[1,2]{0,1}", + "Shape not equal to f32[1,2]{0,1}\n" + "in f32[2,2]{1,0}\n" + "in subshape at {0}\n" + "in (f32[2,2])"); + EXPECT_DESC_AND_EXPLANATION(shape, + m::Shape().WithSubshapeCompatibleTo({10}, &shape), + "a shape with subshape at index {10} which is\n" + " a shape compatible with f32[1,2]", + "No subshape at {10}\n" + "in f32[1,2]{0,1}"); + EXPECT_DESC_AND_EXPLANATION( + ShapeUtil::MakeTupleShape({ShapeUtil::MakeShape(F32, {2, 2})}), + m::Shape().WithSubshapeCompatibleTo({0}, &shape), + "a shape with subshape at index {0} which is\n" + " a shape compatible with f32[1,2]", + "Shape not compatible with f32[1,2]\n" + "in f32[2,2]{1,0}\n" + "in subshape at {0}\n" + "in (f32[2,2])"); + EXPECT_DESC_AND_EXPLANATION( + ShapeUtil::MakeTupleShape({ShapeUtil::MakeTupleShape({shape})}), + m::Shape().WithSubshape({0, 0}, m::Shape().IsScalar()), + "a shape with subshape at index {0,0} which is\n" + " a shape that represents a scalar", + "Shape is not a scalar\n" + "in f32[1,2]{0,1}\n" + "in subshape at {0,0}\n" + "in ((f32[1,2]))"); +} + +std::unique_ptr SetName(absl::string_view name, + std::unique_ptr instr) { + instr->SetAndSanitizeName(string(name)); + return instr; +} + +TEST(PatternMatcherTest, HloInstructionDescribeToAndExplain) { + std::unique_ptr iota = + SetName("i", HloInstruction::CreateIota(ShapeUtil::MakeShape(S32, {42}), + /*iota_dimension=*/0)); + std::unique_ptr constant = + SetName("c", HloInstruction::CreateConstant(LiteralUtil::CreateR0(0))); + + EXPECT_DESC_AND_EXPLANATION(static_cast(nullptr), + m::Op(), "an HloInstruction", + "HloInstruction* is null"); + EXPECT_DESC_AND_EXPLANATION(iota, m::Op().WithName("foo"), + "an HloInstruction named \"foo\"", + "HloInstruction not named \"foo\"\n" + "in i = s32[42]{0} iota(), iota_dimension=0"); + EXPECT_DESC_AND_EXPLANATION(iota, m::Op().WithOpcode(HloOpcode::kAdd), + "an HloInstruction with opcode add", + "HloInstruction doesn't have opcode add\n" + "in i = s32[42]{0} iota(), iota_dimension=0"); + EXPECT_DESC_AND_EXPLANATION( + constant, m::Op().IsNonConstant(), + "an HloInstruction with any opcode other than constant", + "HloInstruction has opcode constant, expected anything else\n" + "in c = s32[] constant(0)"); + EXPECT_DESC_AND_EXPLANATION(iota, m::Op().WithNumOperands(42), + "an HloInstruction with 42 operands", + "HloInstruction doesn't have 42 operands\n" + "in i = s32[42]{0} iota(), iota_dimension=0"); + EXPECT_DESC_AND_EXPLANATION(iota, m::Op().WithShape(m::Shape().IsTuple()), + "an HloInstruction outputting\n" + " a shape that represents a tuple", + "Shape is not a tuple\n" + "in s32[42]{0}\n" + "in output shape\n" + "in i = s32[42]{0} iota(), iota_dimension=0"); + EXPECT_DESC_AND_EXPLANATION( + iota, m::Op().WithOperand(2, m::Op().WithOpcode(HloOpcode::kAdd)), + "an HloInstruction with operand 2 which is:\n" + " an HloInstruction with opcode add", + "desired operand index 2 is out of bounds\n" + "in i = s32[42]{0} iota(), iota_dimension=0"); + + EXPECT_DESC_AND_EXPLANATION( + SetName("a", HloInstruction::CreateBinary(ShapeUtil::MakeShape(S32, {}), + HloOpcode::kAdd, constant.get(), + constant.get())), + m::Op().WithOperand(1, m::Op().IsNonConstant()), + "an HloInstruction with operand 1 which is:\n" + " an HloInstruction with any opcode other than constant", + "HloInstruction has opcode constant, expected anything else\n" + "in c = s32[] constant(0)\n" + "in operand 1\n" + "in a = s32[] add(s32[] c, s32[] c)"); + EXPECT_DESC_AND_EXPLANATION( + iota, m::Op().WithFusionKind(HloInstruction::FusionKind::kLoop), + "an HloInstruction with fusion kind kLoop", + "HloInstruction does not have fusion kind kLoop; it's not a fusion\n" + "in i = s32[42]{0} iota(), iota_dimension=0"); + EXPECT_DESC_AND_EXPLANATION( + iota, m::Op().WithTupleIndex(42), + "an HloInstruction which is a GTE with index 42", + "HloInstruction is not a GTE with index 42; it's not a GTE at all\n" + "in i = s32[42]{0} iota(), iota_dimension=0"); + EXPECT_DESC_AND_EXPLANATION(iota, m::Op().IsConstantScalar(), + "an HloInstruction which is a constant scalar", + "HloInstruction is not a constant\n" + "in i = s32[42]{0} iota(), iota_dimension=0"); + EXPECT_DESC_AND_EXPLANATION( + SetName("c", HloInstruction::CreateConstant( + LiteralUtil::CreateR1({1, 2}))), + m::Op().IsConstantEffectiveScalar(), + "an HloInstruction which is a constant effective scalar", + "HloInstruction is not an effective scalar\n" + "in c = s32[2]{0} constant({1, 2})"); + EXPECT_DESC_AND_EXPLANATION( + SetName("c", HloInstruction::CreateConstant(LiteralUtil::CreateR0(10))), + m::Op().IsConstantScalar(42), + "an HloInstruction which is a constant scalar with value 42", + "HloInstruction's constant value 10 did not match expected value 42\n" + "in c = s32[] constant(10)"); + EXPECT_DESC_AND_EXPLANATION( + SetName("c", HloInstruction::CreateConstant(LiteralUtil::CreateR0(2.25))), + m::Op().IsConstantEffectiveScalar(1.25), + "an HloInstruction which is a constant effective scalar with value 1.25", + "HloInstruction's constant value 2.25 did not match expected value 1.25\n" + "in c = f64[] constant(2.25)"); + EXPECT_DESC_AND_EXPLANATION( + constant, m::Op().Is(iota.get()), + absl::StrCat("an HloInstruction which is 0x", absl::Hex(iota.get()), + " (i = s32[42]{0} iota(), iota_dimension=0)"), + absl::StrCat("HloInstruction 0x", absl::Hex(constant.get()), " is not 0x", + absl::Hex(iota.get()), + " (i = s32[42]{0} iota(), iota_dimension=0)\n" + "in c = s32[] constant(0)")); +} + +TEST(PatternMatcherTest, HloInstructionMatcherAnyOrderDescribeTo) { + auto scalar_s32 = ShapeUtil::MakeShape(S32, {}); + EXPECT_DESC_AND_EXPLANATION( + SetName("a", HloInstruction::CreateBinary( + scalar_s32, HloOpcode::kAdd, + SetName("b", HloInstruction::CreateConstant( + LiteralUtil::CreateR0(0))) + .get(), + SetName("c", HloInstruction::CreateConstant( + LiteralUtil::CreateR0(0))) + .get())), + m::AddAnyOrder(m::Op().WithName("b"), m::Op().WithName("bar")), + "an HloInstruction:\n" + " * with opcode add AND\n" + " * with two operands in either order:\n" + " - an HloInstruction named \"b\"\n" + " - an HloInstruction named \"bar\"", + "HloInstruction's operands (ignoring order) did not match second " + "matcher. Specifically,\n" + " - an HloInstruction named \"bar\"\n" + "does not match LHS:\n" + " - HloInstruction not named \"bar\"\n" + " in b = s32[] constant(0)\n" + "does not match RHS:\n" + " - HloInstruction not named \"bar\"\n" + " in c = s32[] constant(0)\n" + "in a = s32[] add(s32[] b, s32[] c)"); + + EXPECT_DESC_AND_EXPLANATION( + SetName("a", + HloInstruction::CreateBinary( + scalar_s32, HloOpcode::kAdd, + HloInstruction::CreateParameter(0, scalar_s32, "p").get(), + SetName("c", HloInstruction::CreateConstant( + LiteralUtil::CreateR0(0))) + .get())), + m::AddAnyOrder(m::Op().IsConstantScalar(), m::Op().IsConstant()), + "an HloInstruction:\n" + " * with opcode add AND\n" + " * with two operands in either order:\n" + " - an HloInstruction which is a constant scalar\n" + " - an HloInstruction with opcode constant", + "HloInstruction's LHS operand did not match either of the two matchers. " + "Specifically,\n" + " - an HloInstruction which is a constant scalar\n" + "does not match LHS:\n" + " - HloInstruction is not a constant\n" + " in p = s32[] parameter(0)\n" + "and\n" + " - an HloInstruction with opcode constant\n" + "does not match LHS:\n" + " - HloInstruction doesn't have opcode constant\n" + " in p = s32[] parameter(0)\n" + "in a = s32[] add(s32[] p, s32[] c)"); +} + +TEST(PatternMatcherTest, AnyOfMatcherDescribeToAndExplain) { + EXPECT_DESC_AND_EXPLANATION( + SetName("c", HloInstruction::CreateConstant(LiteralUtil::CreateR0(0))), + m::AnyOf(m::Op().WithName("foo"), + m::Op().WithName("bar")), + "any of:\n" + " - an HloInstruction named \"foo\" OR\n" + " - an HloInstruction named \"bar\"", + "None of the following matchers succeeded:\n" + "Matcher #1\n" + " - an HloInstruction named \"foo\"\n" + "failed with\n" + " - HloInstruction not named \"foo\"\n" + " in c = s32[] constant(0)\n" + "Matcher #2\n" + " - an HloInstruction named \"bar\"\n" + "failed with\n" + " - HloInstruction not named \"bar\"\n" + " in c = s32[] constant(0)"); +} + +TEST(PatternMatcherTest, Parameter) { + auto param = + HloInstruction::CreateParameter(1, ShapeUtil::MakeShape(F32, {}), "p1"); + auto non_param = + SetName("c", HloInstruction::CreateConstant(LiteralUtil::CreateR0(0))); + EXPECT_FALSE(Match(param.get(), m::Parameter(0))); + EXPECT_TRUE(Match(param.get(), m::Parameter())); + EXPECT_TRUE(Match(param.get(), m::Parameter(1))); + EXPECT_FALSE(Match(non_param.get(), m::Parameter())); + EXPECT_FALSE(Match(non_param.get(), m::Parameter(1))); + + EXPECT_DESC_AND_EXPLANATION(non_param, m::Parameter(1), + "an HloInstruction:\n" + " * with opcode parameter AND\n" + " * which is parameter 1", + "HloInstruction doesn't have opcode parameter\n" + "in c = s32[] constant(0)"); + EXPECT_EQ(Explanation(HloInstruction::CreateParameter( + 0, ShapeUtil::MakeShape(F32, {}), "p0"), + m::Parameter(1)), + "HloInstruction is not parameter 1\n" + "in p0 = f32[] parameter(0)"); +} + +TEST(PatternMatcherTest, OneUseAndOneUser) { + auto param = + HloInstruction::CreateParameter(0, ShapeUtil::MakeShape(F32, {}), "p0"); + + EXPECT_FALSE(Match(param.get(), m::Op().WithOneUse())); + EXPECT_DESC_AND_EXPLANATION( + param, m::Op().WithOneUse(), + "an HloInstruction which has exactly one use", + "HloInstruction has 0 users, but expected exactly one.\n" + "in p0 = f32[] parameter(0)"); + + EXPECT_FALSE(Match(param.get(), m::Op().WithOneUser())); + EXPECT_DESC_AND_EXPLANATION( + param, m::Op().WithOneUser(), + "an HloInstruction which has exactly one user (but possibly is used " + "multiple times by that instruction)", + "HloInstruction has 0 users, but expected exactly one.\n" + "in p0 = f32[] parameter(0)"); + + { + auto reshape = + SetName("r", HloInstruction::CreateReshape( + ShapeUtil::MakeShape(F32, {1}), param.get())); + EXPECT_TRUE(Match(param.get(), m::Op().WithOneUse())); + EXPECT_TRUE(Match(param.get(), m::Op().WithOneUser())); + + auto reshape1 = + SetName("r1", HloInstruction::CreateReshape( + ShapeUtil::MakeShape(F32, {1}), param.get())); + EXPECT_FALSE(Match(param.get(), m::Op().WithOneUse())); + EXPECT_FALSE(Match(param.get(), m::Op().WithOneUser())); + + const char* kMultipleUserExplanation = + "HloInstruction has 2 users, but expected exactly one.\n" + "All users:\n" + " - r = f32[1]{0} reshape(f32[] p0)\n" + " - r1 = f32[1]{0} reshape(f32[] p0)\n" + "in p0 = f32[] parameter(0)"; + EXPECT_EQ(Explanation(param.get(), m::Op().WithOneUse()), + kMultipleUserExplanation); + EXPECT_EQ(Explanation(param.get(), m::Op().WithOneUser()), + kMultipleUserExplanation); + } + + auto add = SetName("add", HloInstruction::CreateBinary( + ShapeUtil::MakeShape(F32, {}), HloOpcode::kAdd, + param.get(), param.get())); + EXPECT_TRUE(Match(param.get(), m::Op().WithOneUser())); + EXPECT_FALSE(Match(param.get(), m::Op().WithOneUse())); + EXPECT_EQ(Explanation(param.get(), m::Op().WithOneUse()), + "HloInstruction is used 2 times by its user, but is expected to be " + "used just once: add = f32[] add(f32[] p0, f32[] p0)\n" + "in p0 = f32[] parameter(0)"); +} + } // namespace } // namespace xla diff --git a/tensorflow/compiler/xla/service/reduce_precision_insertion_test.cc b/tensorflow/compiler/xla/service/reduce_precision_insertion_test.cc index 16fa80d53e..efeec96571 100644 --- a/tensorflow/compiler/xla/service/reduce_precision_insertion_test.cc +++ b/tensorflow/compiler/xla/service/reduce_precision_insertion_test.cc @@ -54,7 +54,7 @@ TEST_F(ReducePrecisionInsertionTest, BeforeUnaryInstruction) { HloInstruction* b = builder.AddInstruction( HloInstruction::CreateUnary(shape, HloOpcode::kCos, a)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); // Confirm expected state before adding ops. @@ -81,7 +81,7 @@ TEST_F(ReducePrecisionInsertionTest, BeforeUnaryScalarInstruction) { HloInstruction* b = builder.AddInstruction( HloInstruction::CreateUnary(shape, HloOpcode::kCos, a)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); // Confirm expected state before adding ops. @@ -111,7 +111,7 @@ TEST_F(ReducePrecisionInsertionTest, BeforeBinaryInstruction) { HloInstruction* c = builder.AddInstruction( HloInstruction::CreateBinary(shape, HloOpcode::kAdd, a, b)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); // Confirm expected state before adding ops. @@ -140,7 +140,7 @@ TEST_F(ReducePrecisionInsertionTest, BeforeZeroInputInstruction) { HloInstruction* b = builder.AddInstruction( HloInstruction::CreateUnary(shape, HloOpcode::kCos, a)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); // Confirm expected state before adding ops. @@ -173,7 +173,7 @@ TEST_F(ReducePrecisionInsertionTest, AvoidAddingDuplicateInstructions) { HloInstruction* d = builder.AddInstruction( HloInstruction::CreateBinary(shape, HloOpcode::kAdd, b, c)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); // Confirm expected state before adding ops. @@ -205,7 +205,7 @@ TEST_F(ReducePrecisionInsertionTest, AfterRootInstruction) { HloInstruction* b = builder.AddInstruction( HloInstruction::CreateUnary(shape, HloOpcode::kCos, a)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); // Confirm expected state before adding ops. @@ -242,7 +242,7 @@ TEST_F(ReducePrecisionInsertionTest, AfterNonRootInstruction) { HloInstruction* c = builder.AddInstruction( HloInstruction::CreateBinary(shape, HloOpcode::kAdd, a_cos, b_cos)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); module->AddEntryComputation(builder.Build()); // Confirm expected graph before adding ops. @@ -295,7 +295,7 @@ TEST_F(ReducePrecisionInsertionTest, ShouldReduceOutputPrecisionIsFalse) { HloInstruction* y = builder.AddInstruction( HloInstruction::CreateUnary(shape, HloOpcode::kCos, x)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); // Confirm expected graph before adding ops. @@ -321,7 +321,7 @@ TEST_F(ReducePrecisionInsertionTest, InsertionIsNotRecursive) { HloInstruction* b = builder.AddInstruction( HloInstruction::CreateReducePrecision(shape, a, 8, 23)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); // Confirm expected state before adding ops. @@ -348,7 +348,7 @@ TEST_F(ReducePrecisionInsertionTest, SkipRedundantReducePrecisionAfter) { HloInstruction* y = builder.AddInstruction( HloInstruction::CreateReducePrecision(shape, x, 5, 10)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); // Confirm expected graph before adding ops. @@ -376,7 +376,7 @@ TEST_F(ReducePrecisionInsertionTest, AddNonRedundantReducePrecision) { HloInstruction* y = builder.AddInstruction( HloInstruction::CreateReducePrecision(shape, x, 8, 23)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); // Confirm expected graph before adding ops. @@ -402,7 +402,7 @@ TEST_F(ReducePrecisionInsertionTest, IgnoreOpsInsideFusionNode) { builder.AddInstruction(HloInstruction::CreateParameter(0, shape, "x")); HloInstruction* y = builder.AddInstruction( HloInstruction::CreateUnary(shape, HloOpcode::kCos, x)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); // Manually fuse the kCos operation into a fusion operation. @@ -438,7 +438,7 @@ TEST_F(ReducePrecisionInsertionTest, OpGetsInsertedInHeadOfFusionNode) { builder.AddInstruction(HloInstruction::CreateParameter(0, shape, "x")); HloInstruction* y = builder.AddInstruction( HloInstruction::CreateUnary(shape, HloOpcode::kCos, x)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); // Manually fuse the kCos operation into a fusion operation. @@ -485,7 +485,7 @@ TEST_F(ReducePrecisionInsertionTest, OpGetsInsertedInTailOfFusionNode) { builder.AddInstruction(HloInstruction::CreateParameter(0, shape, "x")); HloInstruction* y = builder.AddInstruction( HloInstruction::CreateUnary(shape, HloOpcode::kCos, x)); - auto module = CreateNewUnverifiedModule(); + auto module = CreateNewVerifiedModule(); auto computation = module->AddEntryComputation(builder.Build()); // Manually fuse the kCos operation into a fusion operation. diff --git a/tensorflow/compiler/xla/service/service.cc b/tensorflow/compiler/xla/service/service.cc index 75f7413b3c..5ec7fe2ade 100644 --- a/tensorflow/compiler/xla/service/service.cc +++ b/tensorflow/compiler/xla/service/service.cc @@ -41,6 +41,7 @@ limitations under the License. #include "tensorflow/compiler/xla/service/source_map_util.h" #include "tensorflow/compiler/xla/service/stream_pool.h" #include "tensorflow/compiler/xla/service/transfer_manager.h" +#include "tensorflow/compiler/xla/shape.h" #include "tensorflow/compiler/xla/shape_layout.h" #include "tensorflow/compiler/xla/shape_util.h" #include "tensorflow/compiler/xla/status_macros.h" @@ -275,8 +276,8 @@ StatusOr> Service::CreateModuleConfig( } if (execution_options != nullptr && execution_options->has_shape_with_output_layout()) { - const auto& shape_with_output_layout = - execution_options->shape_with_output_layout(); + const Shape shape_with_output_layout( + execution_options->shape_with_output_layout()); TF_RETURN_IF_ERROR( ValidateResultShape(shape_with_output_layout, program_shape.result())); TF_RETURN_IF_ERROR( @@ -658,9 +659,9 @@ Status Service::ExecuteGraphParallel(const ExecuteGraphParallelRequest* arg, // replica 0. TF_ASSIGN_OR_RETURN( std::unique_ptr module_config, - CreateModuleConfig(request.computation().host_program_shape(), - replicated_arguments.front(), - request.execution_options())); + CreateModuleConfig( + ProgramShape{request.computation().host_program_shape()}, + replicated_arguments.front(), request.execution_options())); VLOG(3) << "ExecuteGraphParallel created HloModuleConfig computation layout: " << module_config->entry_computation_layout().ToString(); @@ -745,9 +746,9 @@ Status Service::GetDeviceHandles(const GetDeviceHandlesRequest* arg, } if (available_device_count < arg->device_count() * replica_count) { return ResourceExhausted( - "Requested device count (%d) exceeds the number of available devices " - "on the target (%d)", - arg->device_count(), available_device_count); + "Requested logical device count (%d) with replica count (%d) exceeds " + "the number of available physical devices on the target (%d)", + arg->device_count(), replica_count, available_device_count); } for (int64 i = 0; i < arg->device_count(); ++i) { @@ -818,14 +819,17 @@ Status Service::Compile(const CompileRequest* arg, CompileResponse* result) { "The compile request does not support multiple device handles."); } - std::vector argument_shapes; - absl::c_transform(arg->input_shape_with_layout(), - std::back_inserter(argument_shapes), - [](const Shape& shape) { return &shape; }); + std::vector argument_shapes; + argument_shapes.reserve(arg->input_shape_with_layout_size()); + std::vector argument_shape_ptrs; + for (const ShapeProto& shape_proto : arg->input_shape_with_layout()) { + argument_shapes.push_back(Shape(shape_proto)); + argument_shape_ptrs.push_back(&argument_shapes.back()); + } TF_ASSIGN_OR_RETURN( std::unique_ptr module_config, - CreateModuleConfig(arg->computation().host_program_shape(), - argument_shapes, &arg->execution_options())); + CreateModuleConfig(ProgramShape{arg->computation().host_program_shape()}, + argument_shape_ptrs, &arg->execution_options())); VLOG(3) << "Compile created HloModuleConfig computation layout: " << module_config->entry_computation_layout().ToString(); @@ -930,14 +934,14 @@ Status Service::TransferToClient(const TransferToClientRequest* arg, TF_ASSIGN_OR_RETURN(const ShapedBuffer* shaped_buffer, allocation_tracker_.ResolveForReplica(arg->data(), 0)); - const Shape* return_shape; + Shape return_shape; if (arg->has_shape_with_layout()) { - if (!LayoutUtil::HasLayout(arg->shape_with_layout())) { + return_shape = Shape(arg->shape_with_layout()); + if (!LayoutUtil::HasLayout(return_shape)) { return InvalidArgument("shape_with_layout must have layout if present."); } - return_shape = &arg->shape_with_layout(); } else { - return_shape = &shaped_buffer->on_host_shape(); + return_shape = Shape(shaped_buffer->on_host_shape()); } TF_ASSIGN_OR_RETURN(auto stream, execute_backend_->BorrowStream( @@ -948,30 +952,15 @@ Status Service::TransferToClient(const TransferToClientRequest* arg, execute_backend_->transfer_manager()->TransferLiteralFromDevice( stream.get(), *shaped_buffer)); - if (LayoutUtil::LayoutsInShapesEqual(*return_shape, result_literal.shape())) { + if (LayoutUtil::LayoutsInShapesEqual(return_shape, result_literal.shape())) { *result->mutable_literal() = result_literal.ToProto(); } else { *result->mutable_literal() = - result_literal.Relayout(*return_shape).ToProto(); + result_literal.Relayout(return_shape).ToProto(); } return Status::OK(); } -namespace { - -// Creates a clone of the given shaped buffer with the given device ordinal. The -// shape and DeviceMemoryBase values of the clone are identical to the original. -std::unique_ptr CloneShapedBufferOnDevice( - const ShapedBuffer& shaped_buffer, int device_ordinal) { - auto clone = absl::make_unique( - shaped_buffer.on_host_shape(), shaped_buffer.on_device_shape(), - shaped_buffer.platform(), device_ordinal); - clone->buffers() = shaped_buffer.buffers(); - return clone; -} - -} // namespace - Status Service::TransferToServer(const TransferToServerRequest* arg, TransferToServerResponse* result) { TF_ASSIGN_OR_RETURN(Literal literal, @@ -1060,11 +1049,11 @@ Status Service::TransferFromOutfeed(const TransferFromOutfeedRequest* arg, executor = replicas[arg->replica_id()]; } - auto literal = Literal::CreateFromShape(arg->shape_with_layout()); + auto literal = Literal::CreateFromShape(Shape(arg->shape_with_layout())); TF_RETURN_IF_ERROR( execute_backend_->transfer_manager()->TransferLiteralFromOutfeed( - executor, arg->shape_with_layout(), literal)); + executor, Shape(arg->shape_with_layout()), literal)); *result->mutable_literal() = literal.ToProto(); return Status::OK(); } @@ -1087,7 +1076,7 @@ Status Service::ComputeConstantGraph(const ComputeConstantGraphRequest* arg, "constant computation may not depend on any parameters."); } - ProgramShape program_shape = arg->computation().host_program_shape(); + ProgramShape program_shape(arg->computation().host_program_shape()); TF_DCHECK_OK(ShapeUtil::ValidateShape(program_shape.result())); if (arg->has_output_layout()) { TF_RETURN_IF_ERROR(LayoutUtil::ValidateLayoutForShape( @@ -1118,7 +1107,7 @@ Status Service::ComputeConstantGraph(const ComputeConstantGraphRequest* arg, Status Service::GetShape(const GetShapeRequest* arg, GetShapeResponse* result) { TF_ASSIGN_OR_RETURN(const ShapedBuffer* buffer, allocation_tracker_.ResolveForReplica(arg->data(), 0)); - *result->mutable_shape() = buffer->on_host_shape(); + *result->mutable_shape() = buffer->on_host_shape().ToProto(); return Status::OK(); } @@ -1131,7 +1120,7 @@ Status Service::GetComputationGraphStats( return InvalidArgument("Program shape may not be empty."); } - HloModuleConfig config(arg->computation().host_program_shape()); + HloModuleConfig config(ProgramShape{arg->computation().host_program_shape()}); config.set_debug_options(arg->debug_options()); TF_ASSIGN_OR_RETURN(std::unique_ptr module, CreateModuleFromProto(arg->computation(), config)); diff --git a/tensorflow/compiler/xla/service/shape_inference.cc b/tensorflow/compiler/xla/service/shape_inference.cc index 61a60ef9ef..7e7282a737 100644 --- a/tensorflow/compiler/xla/service/shape_inference.cc +++ b/tensorflow/compiler/xla/service/shape_inference.cc @@ -391,17 +391,6 @@ StatusOr InferWindowOutputShape(const Shape& base_shape, return ShapeUtil::MakeShape(element_type, new_dimensions); } -/* static */ StatusOr ShapeInference::InferAfterAllShape( - absl::Span arg_shapes) { - for (const Shape* arg_shape : arg_shapes) { - if (arg_shape->element_type() != TOKEN) { - return InvalidArgument( - "Operands of token instructions must be TOKEN types."); - } - } - return ShapeUtil::MakeTokenShape(); -} - /* static */ StatusOr ShapeInference::InferConvertShape( const Shape& operand_shape, PrimitiveType new_element_type) { auto old_element_type = operand_shape.element_type(); @@ -1029,7 +1018,7 @@ ShapeInference::InferDegenerateDimensionBroadcastShape(HloOpcode operation, switch (opcode) { case HloOpcode::kTuple: { Shape result = ShapeUtil::MakeTupleShape({}); - result.mutable_tuple_shapes()->Reserve(operand_shapes.size()); + result.mutable_tuple_shapes()->reserve(operand_shapes.size()); for (const Shape* shape : operand_shapes) { ShapeUtil::AppendShapeToTuple(*shape, &result); } @@ -2038,7 +2027,16 @@ ShapeInference::InferDegenerateDimensionBroadcastShape(HloOpcode operation, dimension); } - return ShapeUtil::MakeShape(S64, {}); + // TODO(b/119580730): Remove this restriction when very large dimension size + // is needed. + if (shape.dimensions(dimension) > std::numeric_limits::max()) { + return InvalidArgument( + "GetDimensionSize's input shape is %s, the %dth dimension exceeds the " + "UINT_MAX limit.", + ShapeUtil::HumanString(shape), dimension); + } + + return ShapeUtil::MakeShape(U32, {}); } /* static */ StatusOr ShapeInference::InferSliceShape( diff --git a/tensorflow/compiler/xla/service/shape_inference.h b/tensorflow/compiler/xla/service/shape_inference.h index 31ef4b2e41..d94385a04d 100644 --- a/tensorflow/compiler/xla/service/shape_inference.h +++ b/tensorflow/compiler/xla/service/shape_inference.h @@ -232,13 +232,6 @@ class ShapeInference { static StatusOr InferConcatOpShape( absl::Span arg_shapes, int64 dimension); - // Infers the shape produced by a kAfterAll. Trivially this shape is always a - // TOKEN shape. However, ShapeInference serves two purposes: inferring shapes - // and checking operand shapes. This method verifies that the operand shapes - // are all TOKENs. - static StatusOr InferAfterAllShape( - absl::Span arg_shapes); - // Helper that validates the given operand shape can be converted to the // target output_shape via a convert instruction -- the requirement is that // the shape is identical except for the element type. diff --git a/tensorflow/compiler/xla/service/transpose_folding_test.cc b/tensorflow/compiler/xla/service/transpose_folding_test.cc index 7a565bf076..17cdaa74fc 100644 --- a/tensorflow/compiler/xla/service/transpose_folding_test.cc +++ b/tensorflow/compiler/xla/service/transpose_folding_test.cc @@ -172,7 +172,7 @@ TEST_F(TransposeFoldingTest, FuseDotWithConstantOperands) { HloInstruction* mul = builder.AddInstruction(HloInstruction::CreateBinary( add->shape(), HloOpcode::kMultiply, add, sub)); - auto module = CreateNewUnverifiedModule("fuse_with_constant_operands"); + auto module = CreateNewVerifiedModule("fuse_with_constant_operands"); HloComputation* entry_computation = module->AddEntryComputation(builder.Build(mul)); HloInstruction* call = module->OutlineExpressionFromComputation( @@ -247,7 +247,7 @@ TEST_F(TransposeFoldingTest, FoldConvDimSwapTransposeRhs) { conv_shape.ValueOrDie(), x, transpose_y, /*feature_group_count=*/1, window, dnums, DefaultPrecisionConfig(2))); - auto module = CreateNewUnverifiedModule("test_module"); + auto module = CreateNewVerifiedModule("test_module"); HloComputation* entry_computation = module->AddEntryComputation(builder.Build(conv)); FoldTranspose(module.get()); @@ -302,7 +302,7 @@ TEST_F(TransposeFoldingTest, FoldConvComplexTransposeRhs) { conv_shape.ValueOrDie(), x, transpose_y, /*feature_group_count=*/1, window, dnums, DefaultPrecisionConfig(2))); - auto module = CreateNewUnverifiedModule("test_module"); + auto module = CreateNewVerifiedModule("test_module"); HloComputation* entry_computation = module->AddEntryComputation(builder.Build(conv)); FoldTranspose(module.get()); @@ -362,7 +362,7 @@ TEST_F(TransposeFoldingTest, FoldConvTransposeLhs) { conv_shape.ValueOrDie(), transpose_x, y, /*feature_group_count=*/1, window, dnums, DefaultPrecisionConfig(2))); - auto module = CreateNewUnverifiedModule("test_module"); + auto module = CreateNewVerifiedModule("test_module"); HloComputation* entry_computation = module->AddEntryComputation(builder.Build(conv)); FoldTranspose(module.get()); @@ -428,7 +428,7 @@ TEST_F(TransposeFoldingTest, FoldConvComplexTransposeLhs) { conv_shape.ValueOrDie(), transpose_x, y, /*feature_group_count=*/1, window, dnums, DefaultPrecisionConfig(2))); - auto module = CreateNewUnverifiedModule("test_module"); + auto module = CreateNewVerifiedModule("test_module"); HloComputation* entry_computation = module->AddEntryComputation(builder.Build(conv)); FoldTranspose(module.get()); diff --git a/tensorflow/compiler/xla/service/tuple_points_to_analysis.cc b/tensorflow/compiler/xla/service/tuple_points_to_analysis.cc index 96f3055c98..50d51eaeb7 100644 --- a/tensorflow/compiler/xla/service/tuple_points_to_analysis.cc +++ b/tensorflow/compiler/xla/service/tuple_points_to_analysis.cc @@ -280,6 +280,13 @@ Status TuplePointsToAnalysis::HandleDomain(HloInstruction* domain) { return Status::OK(); } +Status TuplePointsToAnalysis::HandleAddDependency( + HloInstruction* add_dependency) { + // AddDependency just forwards the value of its zero-th operand. + CreateCopiedPointsToSet(add_dependency, add_dependency->operand(0)); + return Status::OK(); +} + Status TuplePointsToAnalysis::HandleRecvDone(HloInstruction* recv_done) { // RecvDone aliases its input (Recv) tuple element {0} to element {0} of its // output. The other indices ({} and {1}) define their own buffers. diff --git a/tensorflow/compiler/xla/service/tuple_points_to_analysis.h b/tensorflow/compiler/xla/service/tuple_points_to_analysis.h index bcfcb388f9..0a1d5649d6 100644 --- a/tensorflow/compiler/xla/service/tuple_points_to_analysis.h +++ b/tensorflow/compiler/xla/service/tuple_points_to_analysis.h @@ -252,6 +252,7 @@ class TuplePointsToAnalysis : public DfsHloVisitorWithDefault { Status HandleRecvDone(HloInstruction* recv_done) override; Status HandleSend(HloInstruction* send) override; Status HandleTupleSelect(HloInstruction* tuple_select) override; + Status HandleAddDependency(HloInstruction* add_dependency) override; string ToString() const; diff --git a/tensorflow/compiler/xla/service/tuple_points_to_analysis_test.cc b/tensorflow/compiler/xla/service/tuple_points_to_analysis_test.cc index 10ef2d38fa..561762b5d4 100644 --- a/tensorflow/compiler/xla/service/tuple_points_to_analysis_test.cc +++ b/tensorflow/compiler/xla/service/tuple_points_to_analysis_test.cc @@ -264,6 +264,22 @@ TEST_F(TuplePointsToAnalysisTest, GetTupleElement) { UnorderedElementsAre(inner_tuple)); } +TEST_F(TuplePointsToAnalysisTest, AddDependency) { + auto builder = HloComputation::Builder(TestName()); + auto constant = builder.AddInstruction( + HloInstruction::CreateConstant(LiteralUtil::CreateR0(1.0))); + auto token = builder.AddInstruction(HloInstruction::CreateToken()); + auto add_dependency = builder.AddInstruction( + HloInstruction::CreateAddDependency(constant, token)); + BuildModuleAndRunAnalysis(builder.Build()); + + auto& points_to_set = points_to_analysis_->GetPointsToSet(add_dependency); + EXPECT_EQ(1, points_to_set.size()); + EXPECT_FALSE(points_to_set.IsAmbiguous()); + EXPECT_TRUE(points_to_set.IsDistinct()); + ExpectHasTopLevelBuffers(points_to_set.CreateFlattenedSet(), {constant}); +} + TEST_F(TuplePointsToAnalysisTest, DuplicatedElement) { // Create a tuple which contains duplicate elements. auto builder = HloComputation::Builder(TestName()); diff --git a/tensorflow/compiler/xla/service/while_loop_invariant_code_motion.cc b/tensorflow/compiler/xla/service/while_loop_invariant_code_motion.cc index b7c28bfac7..41011176ff 100644 --- a/tensorflow/compiler/xla/service/while_loop_invariant_code_motion.cc +++ b/tensorflow/compiler/xla/service/while_loop_invariant_code_motion.cc @@ -21,6 +21,7 @@ limitations under the License. #include "tensorflow/compiler/xla/service/tuple_util.h" #include "tensorflow/compiler/xla/service/while_loop_analysis.h" #include "tensorflow/compiler/xla/service/while_util.h" +#include "tensorflow/compiler/xla/shape_util.h" #include "tensorflow/compiler/xla/util.h" namespace xla { @@ -207,6 +208,37 @@ WhileLoopInvariantCodeMotion::TryHoistingInvariantInstructionsFromWhileBody( continue; } + if (!hoist_size_inflating_ops_) { + // Check that hoisting the instruction doesn't cause a significant memory + // blow-up. LICM extends the live-range of the output of the hoisted + // instruction to be the entire while loop, which may be problematic on + // platforms where memory is limited. This can be especially harmful if + // the instruction has a significantly larger output than its input, e.g. + // kIota, kBroadcast or kConstant. + int64 input_size = 0, output_size = 0; + + for (auto* operand : instruction->operands()) { + ShapeUtil::ForEachSubshape( + operand->shape(), + [&input_size](const Shape& subshape, const ShapeIndex& /*index*/) { + if (ShapeUtil::IsArray(subshape)) { + input_size += ShapeUtil::ByteSizeOfElements(subshape); + } + }); + } + ShapeUtil::ForEachSubshape( + instruction->shape(), + [&output_size](const Shape& subshape, const ShapeIndex& /*index*/) { + if (ShapeUtil::IsArray(subshape)) { + output_size += ShapeUtil::ByteSizeOfElements(subshape); + } + }); + + if (output_size > input_size) { + continue; + } + } + auto is_invariant = [&](HloInstruction* op) { return hoisted_instructions.find(op) != hoisted_instructions.end() || unhoisted_invariant_instructions.count(op) || diff --git a/tensorflow/compiler/xla/service/while_loop_invariant_code_motion.h b/tensorflow/compiler/xla/service/while_loop_invariant_code_motion.h index 3031899f71..bd6232dc0a 100644 --- a/tensorflow/compiler/xla/service/while_loop_invariant_code_motion.h +++ b/tensorflow/compiler/xla/service/while_loop_invariant_code_motion.h @@ -34,8 +34,14 @@ class WhileLoopInvariantCodeMotion : public HloModulePass { // Setting `hoist_constants` to false can be help if LICM is run in the mid // level HLO pipeline because hoisting constants out of while loop bodies can // break optimizations like constant folding. - explicit WhileLoopInvariantCodeMotion(bool hoist_constants = false) - : hoist_constants_(hoist_constants) {} + // Setting `hoist_size_inflating_ops` to false will forbid hoisting + // instructions where the size of the output(s) is larger than the size of the + // input(s). This is useful on platforms on which it's important to prevent + // blow-ups in memory size. + explicit WhileLoopInvariantCodeMotion(bool hoist_constants = false, + bool hoist_size_inflating_ops = true) + : hoist_constants_(hoist_constants), + hoist_size_inflating_ops_(hoist_size_inflating_ops) {} ~WhileLoopInvariantCodeMotion() override = default; absl::string_view name() const override { @@ -49,6 +55,7 @@ class WhileLoopInvariantCodeMotion : public HloModulePass { HloInstruction* while_instr); bool hoist_constants_; + bool hoist_size_inflating_ops_; }; } // namespace xla diff --git a/tensorflow/compiler/xla/service/while_loop_invariant_code_motion_test.cc b/tensorflow/compiler/xla/service/while_loop_invariant_code_motion_test.cc index 046ccb2d3f..8e7c4bc882 100644 --- a/tensorflow/compiler/xla/service/while_loop_invariant_code_motion_test.cc +++ b/tensorflow/compiler/xla/service/while_loop_invariant_code_motion_test.cc @@ -570,5 +570,59 @@ TEST_F(WhileLoopInvariantCodeMotionTest, DoNotHoistOutOfSingleIteration) { EXPECT_FALSE(simplified_loop); } +const char* const kInflatingTestCase = R"( +HloModule ModuleWithWhile + +mul { + lhs = f32[] parameter(0) + rhs = f32[] parameter(1) + ROOT mul = f32[] multiply(lhs, rhs) +} + +body { + p_body = (f32[]) parameter(0) + iota = f32[1024, 1024] iota(), iota_dimension=0 + add = f32[1024, 1024] add(iota, iota) + constant = f32[] constant(1.0) + reduce = f32[] reduce(f32[1024, 1024] add, f32[] constant), dimensions={0,1}, to_apply=mul + ROOT root = (f32[]) tuple(reduce) +} + +condition { + p_cond = (f32[]) parameter(0) + ROOT result = pred[] constant(true) +} + +ENTRY entry { + param = f32[] parameter(0) + while_init = (f32[]) tuple(param) + ROOT while = (f32[]) while(while_init), condition=condition, body=body +} +)"; + +TEST_F(WhileLoopInvariantCodeMotionTest, HoistsInflatingByDefault) { + auto m = ParseAndReturnVerifiedModule(kInflatingTestCase).ValueOrDie(); + + TF_ASSERT_OK_AND_ASSIGN( + bool simplified_loop, + WhileLoopInvariantCodeMotion(/*hoist_constants=*/true).Run(m.get())); + EXPECT_TRUE(simplified_loop); + + HloComputation* while_body = m->GetComputationWithName("wide.body"); + ASSERT_NE(while_body, nullptr); + EXPECT_THAT(while_body->instructions(), Not(Contains(op::Iota()))); +} + +TEST_F(WhileLoopInvariantCodeMotionTest, NoHoistInflating) { + auto m = ParseAndReturnVerifiedModule(kInflatingTestCase).ValueOrDie(); + + TF_ASSERT_OK_AND_ASSIGN( + bool simplified_loop, + WhileLoopInvariantCodeMotion(/*hoist_constants=*/true, + /*hoist_size_inflating_ops=*/false) + .Run(m.get())); + EXPECT_FALSE(simplified_loop); +} + } // namespace } // namespace xla diff --git a/tensorflow/compiler/xla/service/while_loop_simplifier.cc b/tensorflow/compiler/xla/service/while_loop_simplifier.cc index 6f924a29d8..d30f67dd81 100644 --- a/tensorflow/compiler/xla/service/while_loop_simplifier.cc +++ b/tensorflow/compiler/xla/service/while_loop_simplifier.cc @@ -19,13 +19,17 @@ limitations under the License. #include "absl/strings/str_cat.h" #include "absl/strings/str_join.h" #include "absl/types/optional.h" +#include "tensorflow/compiler/xla/primitive_util.h" #include "tensorflow/compiler/xla/service/call_inliner.h" #include "tensorflow/compiler/xla/service/hlo_instruction.h" +#include "tensorflow/compiler/xla/service/hlo_instructions.h" #include "tensorflow/compiler/xla/service/hlo_query.h" +#include "tensorflow/compiler/xla/service/pattern_matcher.h" #include "tensorflow/compiler/xla/service/while_loop_analysis.h" namespace xla { +namespace m = match; using absl::optional; using hlo_query::ContainsInstrWithOpcode; @@ -302,6 +306,147 @@ static StatusOr TryRemoveDeadWhileParams(HloInstruction* while_op) { return true; } +// Removes each loop parameter (i.e. member of the while loop tuple) that is a +// constant and is the same in the while loop body and the while loop init. +static StatusOr TryRemoveConstantParams(HloInstruction* while_op) { + HloModule* module = while_op->GetModule(); + HloComputation* computation = while_op->parent(); + auto* while_init = while_op->mutable_operand(0); + auto* while_body = while_op->while_body(); + auto* while_cond = while_op->while_condition(); + auto* while_body_root = while_body->root_instruction(); + if (while_init->opcode() != HloOpcode::kTuple || + while_body_root->opcode() != HloOpcode::kTuple) { + return false; + } + + TF_RET_CHECK(while_cond->num_parameters() == 1); + TF_RET_CHECK(while_body->num_parameters() == 1); + TF_RET_CHECK( + ShapeUtil::Compatible(while_init->shape(), while_body_root->shape())); + + absl::flat_hash_set constant_tuple_indices; + const auto& while_shape = while_init->shape(); + for (int64 i = 0; i < while_shape.tuple_shapes_size(); ++i) { + auto* init_elem = while_init->operand(i); + auto* body_elem = while_body_root->operand(i); + if (init_elem->opcode() == HloOpcode::kConstant && + body_elem->opcode() == HloOpcode::kConstant && + init_elem->literal() == body_elem->literal()) { + constant_tuple_indices.insert(i); + } + } + + if (constant_tuple_indices.empty()) { + return false; + } + + // OK, we found some constant elements of the while parameter! Eliminate + // them. + std::vector new_while_shape_elems; + for (int64 i = 0; i < while_shape.tuple_shapes_size(); ++i) { + if (!constant_tuple_indices.count(i)) { + new_while_shape_elems.push_back(while_shape.tuple_shapes(i)); + } + } + Shape new_while_shape = ShapeUtil::MakeTupleShape(new_while_shape_elems); + + // `new_instrs` holds instructions created outside of a computation for + // cloning. Elements added here just need to live until the end of the + // relevant CloneWithReplacement call. + std::vector> new_instrs; + auto add_new_instr = [&](std::unique_ptr instr) { + new_instrs.push_back(std::move(instr)); + return new_instrs.back().get(); + }; + + // Returns a new tuple without the elements of constant_tuple_indices. + auto remove_constant_elems = [&](HloInstruction* instr) { + CHECK(ShapeUtil::Compatible(instr->shape(), while_shape)); + + std::vector tuple_elems; + for (int64 i = 0; i < while_shape.tuple_shapes_size(); ++i) { + if (!constant_tuple_indices.count(i)) { + tuple_elems.push_back( + add_new_instr(HloInstruction::CreateGetTupleElement( + while_shape.tuple_shapes(i), instr, i))); + } + } + return HloInstruction::CreateTuple(tuple_elems); + }; + + auto add_constant_elems = [&](HloInstruction* instr) { + CHECK(ShapeUtil::Compatible(instr->shape(), new_while_shape)); + + std::vector tuple_elems; + int64 j = 0; + for (int64 i = 0; i < while_shape.tuple_shapes_size(); ++i) { + if (constant_tuple_indices.count(i)) { + tuple_elems.push_back(while_init->mutable_operand(i)); + } else { + tuple_elems.push_back( + add_new_instr(HloInstruction::CreateGetTupleElement( + while_shape.tuple_shapes(i), instr, j))); + ++j; + } + } + return HloInstruction::CreateTuple(tuple_elems); + }; + + // Special case: constant_tuple_indices covers the whole while parameter, so + // the new while shape is the empty tuple. In this case, the value of the + // while loop is simply equal to the value of `init`. + // + // It's unfortunate to special-case this, but it's simpler than the + // alternative. The problem is that if our while parameter has no + // non-constant elems, the tuple returned by `add_constant_elems` won't depend + // on instr (the loop body/cond parameter), and therefore + // CloneWithReplacementPairs will *leave the parameter out entirely*, creating + // invalid HLO. + if (ShapeUtil::IsEmptyTuple(new_while_shape)) { + TF_RETURN_IF_ERROR(computation->ReplaceInstruction(while_op, while_init)); + return true; + } + + std::unique_ptr new_while_cond = + while_cond->CloneWithReplacementPairs({ + while_cond->parameter_instruction(0), + add_constant_elems(add_new_instr(HloInstruction::CreateParameter( + 0, new_while_shape, + while_cond->parameter_instruction(0)->name()))), + }); + + std::unique_ptr new_while_body = + while_body->CloneWithReplacementPairs( + { + while_body->parameter_instruction(0), + add_constant_elems(add_new_instr(HloInstruction::CreateParameter( + 0, new_while_shape, + while_cond->parameter_instruction(0)->name()))), + }, + { + while_body->root_instruction(), + remove_constant_elems( + add_new_instr(while_body->root_instruction()->Clone())), + }); + + // Create the final while loop, and add any new instructions created to + // `computation`. + new_instrs.clear(); + TF_RETURN_IF_ERROR(computation->ReplaceWithNewInstruction( + while_op, + add_constant_elems( + computation->AddInstruction(HloInstruction::CreateWhile( + new_while_shape, + module->AddEmbeddedComputation(std::move(new_while_cond)), + module->AddEmbeddedComputation(std::move(new_while_body)), + add_new_instr(remove_constant_elems(while_init))))))); + for (auto& instr : new_instrs) { + computation->AddInstruction(std::move(instr)); + } + return true; +} + // Tries to remove a while loop from the graph. // // - Loops with trip count of 0 can be replaced by the loop's "init" value. @@ -381,16 +526,14 @@ static StatusOr TryPropagateConstant(HloInstruction* while_op) { // performance by forcing us to copy constants. absl::flat_hash_map index_to_constant; for (int i = 0; i < root_operands.size(); i++) { - HloInstruction* instr = root_operands[i]; - if (instr->opcode() == HloOpcode::kGetTupleElement && - instr->tuple_index() == i && instr->operand(0) == while_body_param && - ShapeUtil::IsScalar(instr->shape())) { - auto tuple_element = while_init->operand(i); - if (tuple_element->IsConstant()) { - VLOG(3) << "Found loop invariant tuple element " << i << " " - << tuple_element->ToString(); - index_to_constant[i] = tuple_element; - } + const HloInstruction* init_tuple_elem = nullptr; + if (Match(root_operands[i], + m::GetTupleElement(m::Op().Is(while_body_param), i) + .WithShape(m::Shape().IsScalar())) && + Match(while_init->operand(i), m::Constant(&init_tuple_elem))) { + VLOG(3) << "Found loop invariant tuple element " << i << " " + << init_tuple_elem->ToString(); + index_to_constant[i] = init_tuple_elem; } } @@ -519,14 +662,6 @@ static StatusOr TryFlattenNestedTuples(HloInstruction* while_op) { return false; } - // Cowardly refuse to perform this optimization in the presence of kDomain - // instructions, which may reference other instructions in the loop and - // therefore make this complicated. - if (ContainsInstrWithOpcode(while_body, {HloOpcode::kDomain}) || - ContainsInstrWithOpcode(while_cond, {HloOpcode::kDomain})) { - return false; - } - std::vector flattened_shape_elems; ShapeUtil::ForEachSubshape(while_shape, [&](const Shape& s, const ShapeIndex& /*index*/) { @@ -605,6 +740,243 @@ static StatusOr TryFlattenNestedTuples(HloInstruction* while_op) { return true; } +// Tries to merge loop induction variables of a given type. +// +// In this pass we're only concerned with elements of the loop's tuple that +// are effective-scalars of type `elem_ty`. Some terminology: +// +// - The trip counter is the first element of the loop's tuple that starts at +// 0 and does x++ on each iteration. +// +// - An induction variable is an element of the loop's tuple that is not the +// trip counter and does `x += ` on each iteration of the loop. +// Negative constants are OK. +// +// This pass adds a trip counter if one isn't already present, then replaces +// each induction variable with +// +// + * . +// +// This reduces the number of scalar operations in the loop, which is important +// e.g. on GPUs, where each scalar operation is nontrivially expensive because +// it's a separate kernel launch. +// +// Returns the new loop if a change was made, or null if no change was made. +// Note that the new loop is not a valid replacement for the old loop; it may +// need to be wrapped in a tuple that changes its shape. We return the loop +// itself so that you can call TryMergeInductionVariables in a loop, once for +// each integral type elem_ty. +static StatusOr TryMergeInductionVariables( + HloInstruction* while_op, PrimitiveType elem_ty) { + CHECK(primitive_util::IsIntegralType(elem_ty)) << PrimitiveType_Name(elem_ty); + HloModule* module = while_op->GetModule(); + HloComputation* computation = while_op->parent(); + auto* while_init = while_op->mutable_operand(0); + auto* while_body = while_op->while_body(); + auto* while_cond = while_op->while_condition(); + auto* while_body_root = while_body->root_instruction(); + if (while_init->opcode() != HloOpcode::kTuple || + while_body_root->opcode() != HloOpcode::kTuple) { + return nullptr; + } + + TF_RET_CHECK(while_cond->num_parameters() == 1); + TF_RET_CHECK(while_body->num_parameters() == 1); + TF_RET_CHECK( + ShapeUtil::Compatible(while_init->shape(), while_body_root->shape())); + Shape while_shape = while_init->shape(); + + // The tuple index of the trip counter, if one is present. + absl::optional trip_counter; + // Maps the tuple index of each induction variable to its constant increment. + absl::flat_hash_map induction_vars; + for (int64 i = 0; i < while_body_root->operand_count(); ++i) { + HloInstruction* constant; + if (!Match(while_body_root->mutable_operand(i), + m::AddAnyOrder(m::GetTupleElement(m::Parameter(), i), + m::ConstantScalar(&constant)) + .WithShape(m::Shape().WithElementType(elem_ty)))) { + continue; + } + if (!trip_counter && constant->literal().IsAll(1) && + while_init->operand(i)->IsConstant() && + while_init->operand(i)->literal().IsAll(0)) { + VLOG(10) << "Found existing trip counter at index " << i; + trip_counter = i; + } else { + VLOG(10) << "Found induction variable at index " << i; + induction_vars.emplace(i, Cast(constant)); + } + } + + // There's only something to simplify if we can either: + // + // - combine one or more induction vars with an existing trip counter, or + // - replace two or more induction variables with a new trip counter. + // + // Put another way, there's only something to simplify if the number of + // induction vars plus the number of existing trip counters (0 or 1) is >= 2. + if (induction_vars.size() + (trip_counter.has_value() ? 1 : 0) < 2) { + return nullptr; + } + + // OK, we're going to do the transformation! Set up some helpers. + + // `new_instrs` holds instructions created outside of a computation for + // cloning. Elements added here just need to live until the end of the + // relevant CloneWithReplacement call. + std::vector> new_instrs; + auto add_new_instr = [&](std::unique_ptr instr) { + new_instrs.push_back(std::move(instr)); + return new_instrs.back().get(); + }; + + auto add_binary_op = [&](const Shape& shape, HloOpcode opcode, + HloInstruction* lhs, HloInstruction* rhs) { + // Reshape lhs/rhs to the output shape if necessary. This deals with the + // fact that induction variables need only be effective scalars, not true + // scalars. + if (!ShapeUtil::Compatible(shape, lhs->shape())) { + lhs = add_new_instr(HloInstruction::CreateReshape(shape, lhs)); + } + if (!ShapeUtil::Compatible(shape, rhs->shape())) { + rhs = add_new_instr(HloInstruction::CreateReshape(shape, rhs)); + } + return add_new_instr(HloInstruction::CreateBinary(shape, opcode, lhs, rhs)); + }; + + auto add_gte = [&](HloInstruction* src, int64 idx) { + return add_new_instr(HloInstruction::CreateGetTupleElement( + src->shape().tuple_shapes(idx), src, idx)); + }; + + // Our new while loop will have the same shape as the old while loop, except + // we'll add a trip counter to the end if it wasn't originally present. + Shape new_while_shape = while_shape; + bool added_trip_counter = false; + if (!trip_counter) { + VLOG(10) << "Adding new trip counter to end of loop's tuple."; + trip_counter = new_while_shape.tuple_shapes_size(); + *new_while_shape.add_tuple_shapes() = + ShapeUtil::MakeShape(elem_ty, /*dimensions=*/{}); + added_trip_counter = true; + } + + // Converts `instr` into a tuple of the "old" form -- that is, to a tuple with + // shape `while_body->shape()` and where the induction variables are "reified" + // (i.e. they have value + * ). + auto convert_to_old_form = [&](HloInstruction* instr) { + CHECK(ShapeUtil::Compatible(instr->shape(), new_while_shape)); + std::vector tuple_elems; + for (int64 i = 0; i < while_shape.tuple_shapes_size(); ++i) { + const auto& elem_shape = while_shape.tuple_shapes(i); + if (!induction_vars.count(i)) { + tuple_elems.push_back(add_gte(instr, i)); + continue; + } + tuple_elems.push_back(add_binary_op( + elem_shape, HloOpcode::kAdd, add_gte(instr, i), + add_binary_op(elem_shape, HloOpcode::kMultiply, + add_gte(instr, *trip_counter), + add_new_instr(induction_vars.at(i)->Clone())))); + } + return HloInstruction::CreateTuple(tuple_elems); + }; + + // Converts `root` into a tuple of the "new" form -- that is, to a tuple with + // shape `new_while_shape` and where the induction variables (but not trip + // counters) are replaced with their unchanging values. + auto convert_to_new_form = [&](HloInstruction* old_root, + HloParameterInstruction* loop_body_param) { + CHECK(ShapeUtil::Compatible(old_root->shape(), while_shape)); + std::vector tuple_elems; + + // In the new form, induction variables come from `init`, everything else + // (including the trip counter if it's not one we created ourselves) comes + // from the `root` tuple unmodified. + for (int64 i = 0; i < while_shape.tuple_shapes_size(); ++i) { + tuple_elems.push_back( + add_gte((induction_vars.count(i) ? loop_body_param : old_root), i)); + } + // If we created a trip counter ourselves, add 1 to it in the next + // iteration. + if (added_trip_counter) { + tuple_elems.push_back(add_binary_op( + new_while_shape.tuple_shapes(*trip_counter), HloOpcode::kAdd, + add_gte(loop_body_param, *trip_counter), + add_new_instr( + HloInstruction::CreateConstant(LiteralUtil::One(elem_ty))))); + } + + return HloInstruction::CreateTuple(tuple_elems); + }; + + // Creates a new init tuple, which is the same as the old init tuple except if + // we added a trip counter, it's set to 0. + auto get_new_while_init = [&](HloInstruction* init) { + CHECK(ShapeUtil::Compatible(init->shape(), while_shape)); + if (!added_trip_counter) { + return init; + } + std::vector tuple_elems; + for (int64 i = 0; i < while_shape.tuple_shapes_size(); ++i) { + tuple_elems.push_back(add_gte(init, i)); + } + tuple_elems.push_back(add_new_instr( + HloInstruction::CreateConstant(LiteralUtil::Zero(elem_ty)))); + return add_new_instr(HloInstruction::CreateTuple(tuple_elems)); + }; + + std::unique_ptr new_while_cond = + while_cond->CloneWithReplacementPairs({ + while_cond->parameter_instruction(0), + convert_to_old_form(add_new_instr(HloInstruction::CreateParameter( + 0, new_while_shape, + while_cond->parameter_instruction(0)->name()))), + }); + + // Creating the new while body proceeds in two steps. First we convert the + // users of the parameter to the old form. Then as a second + // CloneWithReplacement operation we convert the root to the new form. We + // have to do this in two steps because the new root needs to use the new + // param0, and during the first clone operation, only the *old-form* param0 is + // accessible. + // + // We have to add temp_new_while_body to the module because cloning a + // computation touches the module (to get its NameUniquer). + HloComputation* temp_new_while_body = + module->AddEmbeddedComputation(while_body->CloneWithReplacementPairs({ + while_body->parameter_instruction(0), + convert_to_old_form(add_new_instr(HloInstruction::CreateParameter( + 0, new_while_shape, + while_body->parameter_instruction(0)->name()))), + })); + std::unique_ptr new_while_body = + temp_new_while_body->CloneWithReplacementPairs({ + temp_new_while_body->root_instruction(), + convert_to_new_form( + add_new_instr(temp_new_while_body->root_instruction()->Clone()), + Cast( + temp_new_while_body->parameter_instruction(0))), + }); + TF_RETURN_IF_ERROR(module->RemoveEmbeddedComputation(temp_new_while_body)); + + // Create the final while loop, and add any new instructions created to + // `computation`. + new_instrs.clear(); + auto* new_while = computation->AddInstruction(HloInstruction::CreateWhile( + new_while_shape, + module->AddEmbeddedComputation(std::move(new_while_cond)), + module->AddEmbeddedComputation(std::move(new_while_body)), + get_new_while_init(while_init))); + TF_RETURN_IF_ERROR(computation->ReplaceWithNewInstruction( + while_op, convert_to_old_form(new_while))); + for (auto& instr : new_instrs) { + computation->AddInstruction(std::move(instr)); + } + return new_while; +} + StatusOr WhileLoopSimplifier::Run(HloModule* module) { XLA_VLOG_LINES(3, "WhileLoopSimplifier::Run(), before:\n" + module->ToString()); @@ -650,19 +1022,50 @@ StatusOr WhileLoopSimplifier::Run(HloModule* module) { continue; } + // TODO(b/119281462): Cowardly refuse to perform any of the following + // optimizations in the presence of kDomain instructions. It seems that + // modifying a while loop's tuple doesn't work when kDomain is present. + if (ContainsInstrWithOpcode(while_op->while_body(), {HloOpcode::kDomain}) || + ContainsInstrWithOpcode(while_op->while_condition(), + {HloOpcode::kDomain})) { + continue; + } + + // Each of the optimizations below modifies the while loop itself if it's + // successful, meaning that `while_op` is no longer valid after one of these + // transformations returns true. + TF_ASSIGN_OR_RETURN(result, TryFlattenNestedTuples(while_op)); changed |= result; if (result) { - // Successfully flattening nested tuples results in us cloning and - // replacing the while loop, meaning that `while_op` is no longer valid. continue; } TF_ASSIGN_OR_RETURN(result, TryRemoveDeadWhileParams(while_op)); changed |= result; if (result) { - // Successfully removing dead while params results in us cloning and - // replacing the while loop, meaning that `while_op` is no longer valid. + continue; + } + + TF_ASSIGN_OR_RETURN(result, TryRemoveConstantParams(while_op)); + changed |= result; + if (result) { + continue; + } + + bool merged_induction_vars = false; + // Notably missing from this list are S16 and U16. These don't currently + // work because S/U16 literals are not implemented. + for (auto elem_ty : {S8, U8, S32, U32, S64, U64}) { + TF_ASSIGN_OR_RETURN(auto* new_while_op, + TryMergeInductionVariables(while_op, elem_ty)); + if (new_while_op) { + while_op = new_while_op; + changed = true; + merged_induction_vars = true; + } + } + if (merged_induction_vars) { continue; } } diff --git a/tensorflow/compiler/xla/service/while_loop_simplifier_test.cc b/tensorflow/compiler/xla/service/while_loop_simplifier_test.cc index 05005e0b26..4950e8269e 100644 --- a/tensorflow/compiler/xla/service/while_loop_simplifier_test.cc +++ b/tensorflow/compiler/xla/service/while_loop_simplifier_test.cc @@ -17,9 +17,12 @@ limitations under the License. #include "absl/strings/str_cat.h" #include "absl/strings/str_replace.h" +#include "tensorflow/compiler/xla/service/algebraic_simplifier.h" +#include "tensorflow/compiler/xla/service/hlo_cse.h" #include "tensorflow/compiler/xla/service/hlo_dce.h" #include "tensorflow/compiler/xla/service/hlo_instruction.h" #include "tensorflow/compiler/xla/service/hlo_matchers.h" +#include "tensorflow/compiler/xla/service/tuple_simplifier.h" #include "tensorflow/compiler/xla/test.h" #include "tensorflow/compiler/xla/tests/hlo_test_base.h" #include "tensorflow/core/lib/core/status_test_util.h" @@ -27,8 +30,17 @@ limitations under the License. namespace xla { namespace { +using ::testing::_; namespace op = xla::testing::opcode_matchers; +// Returns the first kWhile instruction within m's entry computation. +HloInstruction* FindFirstWhile(HloModule* m) { + const auto& instrs = m->entry_computation()->instructions(); + return *absl::c_find_if(instrs, [](const HloInstruction* instr) { + return instr->opcode() == HloOpcode::kWhile; + }); +} + class WhileLoopSimplifierTest : public HloTestBase { protected: // Makes an HloModule that contains a loop with `num_iters` iteration. @@ -540,11 +552,7 @@ TEST_F(WhileLoopSimplifierTest, FlattenNestedTuple) { // it easy to find. EXPECT_TRUE(HloDCE().Run(m.get()).ok()); - const auto& instrs = m->entry_computation()->instructions(); - HloInstruction* new_while = - *absl::c_find_if(instrs, [](const HloInstruction* instr) { - return instr->opcode() == HloOpcode::kWhile; - }); + HloInstruction* new_while = FindFirstWhile(m.get()); Shape flat_tuple = ShapeUtil::ParseShapeString("(s32[1], s32[2], s32[3], s32[4])") .ValueOrDie(); @@ -563,5 +571,177 @@ TEST_F(WhileLoopSimplifierTest, FlattenNestedTuple) { .ValueOrDie())); } +// Edge-case: All elements of the loop carry are constants which can be removed, +// leaving us with a nullary loop. This is a special case, we just replace the +// loop with its init. +TEST_F(WhileLoopSimplifierTest, OnlyConstantsInLoopCarry) { + const string hlo_string = R"( + HloModule Test + Body { + param = (s32[1]) parameter(0) + a = s32[1] constant({0}) + ROOT tuple = (s32[1]) tuple(a) + } + Cond { + param = (s32[1]) parameter(0) + ROOT cond = pred[] constant(true) + } + ENTRY Loop { + a = s32[1] constant({0}) + init = (s32[1]) tuple(a) + ROOT while = (s32[1]) while(init), condition=Cond, body=Body + })"; + + auto m = ParseAndReturnVerifiedModule(hlo_string).ValueOrDie(); + EXPECT_TRUE(WhileLoopSimplifier().Run(m.get()).ValueOrDie()); + EXPECT_TRUE(HloDCE().Run(m.get()).ok()); + EXPECT_TRUE(TupleSimplifier().Run(m.get()).ok()); + EXPECT_THAT(m->entry_computation()->root_instruction(), + op::Tuple(op::Constant())); +} + +TEST_F(WhileLoopSimplifierTest, RemoveConstantFromLoopCarry) { + const string hlo_string = R"( + HloModule Test + Body { + param = (s32[1], s32[2], s32[3]) parameter(0) + a = s32[1] get-tuple-element(param), index=0 + a.1 = s32[1] add(a, a) + b = s32[2] constant({1,1}) + c = s32[3] constant({10,10,10}) + ROOT tuple = (s32[1], s32[2], s32[3]) tuple(a.1, b, c) + } + Cond { + param = (s32[1], s32[2], s32[3]) parameter(0) + /* Use each tuple element. The verifier will then ensure that if any of + * these get modified, they're replaced with values of the correct shape. */ + a = s32[1] get-tuple-element(param), index=0 + b = s32[2] get-tuple-element(param), index=1 + c = s32[3] get-tuple-element(param), index=2 + ROOT cond = pred[] constant(true) + } + ENTRY Loop { + /* Only `b` should be simplified away. `a` is not a constant within the + * loop, and `c`'s value changes depending on whether we run 0 or 1 + * iterations of the loop. */ + a = s32[1] constant({0}) + b = s32[2] constant({1,1}) + c = s32[3] constant({2,2,2}) + init = (s32[1], s32[2], s32[3]) tuple(a,b,c) + ROOT while = (s32[1], s32[2], s32[3]) while(init), + condition=Cond, body=Body + })"; + + auto m = ParseAndReturnVerifiedModule(hlo_string).ValueOrDie(); + EXPECT_TRUE(WhileLoopSimplifier().Run(m.get()).ValueOrDie()); + // DCE away the old loop so there's just one while loop in the module, making + // it easy to find. + EXPECT_TRUE(HloDCE().Run(m.get()).ok()); + // Run the tuple simplifier to make the resulting HLO a bit easier to check. + EXPECT_TRUE(TupleSimplifier().Run(m.get()).ok()); + + HloInstruction* new_while = FindFirstWhile(m.get()); + Shape new_while_shape = + ShapeUtil::ParseShapeString("(s32[1], s32[3])").ValueOrDie(); + EXPECT_TRUE(ShapeUtil::Equal(new_while->shape(), new_while_shape)); + EXPECT_TRUE(ShapeUtil::Equal( + new_while->while_body()->root_instruction()->shape(), new_while_shape)); + EXPECT_TRUE(ShapeUtil::Equal( + new_while->while_body()->parameter_instruction(0)->shape(), + new_while_shape)); + EXPECT_TRUE(ShapeUtil::Equal( + new_while->while_condition()->parameter_instruction(0)->shape(), + new_while_shape)); + EXPECT_TRUE(ShapeUtil::Equal( + m->entry_computation()->root_instruction()->shape(), + ShapeUtil::ParseShapeString("(s32[1], s32[2], s32[3])").ValueOrDie())); + EXPECT_THAT(m->entry_computation()->root_instruction(), + op::Tuple(_, op::Constant(), _)); +} + +const char* const kSimpleMergeInductionVariablesModule = R"( + HloModule Test + Body { + param = (TYPE[], TYPE[], TYPE[]) parameter(0) + + a = TYPE[] get-tuple-element(param), index=0 + one = TYPE[] constant(1) + a1 = TYPE[] add(a, one) + + b = TYPE[] get-tuple-element(param), index=1 + negone = TYPE[] constant(-1) + b1 = TYPE[] add(b, negone) + + c = TYPE[] add(a, b) + + ROOT tuple = (TYPE[], TYPE[], TYPE[]) tuple(a1,b1,c) + } + Cond { + param = (TYPE[], TYPE[], TYPE[]) parameter(0) + a = TYPE[] get-tuple-element(param), index=0 + b = TYPE[] get-tuple-element(param), index=1 + sum = TYPE[] power(a, b) + ten = TYPE[] constant(10) + ROOT cond = pred[] less-than(sum, ten) + } + ENTRY Loop { + a = TYPE[] constant(10) + b = TYPE[] constant(100) + c = TYPE[] constant(0) + init = (TYPE[], TYPE[], TYPE[]) tuple(a,b,c) + while = (TYPE[], TYPE[], TYPE[]) while(init), condition=Cond, body=Body + + a1 = TYPE[] get-tuple-element(while), index=0 + b1 = TYPE[] get-tuple-element(while), index=1 + ROOT sum = TYPE[] add(a1, b1) + })"; + +TEST_F(WhileLoopSimplifierTest, MergeInductionVariables_Simple) { + string hlo_string = absl::StrReplaceAll(kSimpleMergeInductionVariablesModule, + {{"TYPE", "s32"}}); + + auto m = ParseAndReturnVerifiedModule(hlo_string).ValueOrDie(); + EXPECT_TRUE(WhileLoopSimplifier().Run(m.get()).ValueOrDie()); + // DCE away the old loop so there's just one while loop in the module, making + // it easy to find, and run the tuple simplifier to make the resulting HLO + // easier to check. + EXPECT_TRUE(HloDCE().Run(m.get()).ok()); + EXPECT_TRUE(TupleSimplifier().Run(m.get()).ok()); + + HloInstruction* new_while = FindFirstWhile(m.get()); + // We should have added a new loop counter for s32[] to the end of the tuple. + SCOPED_TRACE(m->ToString()); + Shape new_while_shape = + ShapeUtil::ParseShapeString("(s32[], s32[], s32[], s32[])").ValueOrDie(); + EXPECT_TRUE(ShapeUtil::Equal(new_while->shape(), new_while_shape)); + EXPECT_TRUE(ShapeUtil::Equal( + new_while->while_body()->root_instruction()->shape(), new_while_shape)); + EXPECT_TRUE(ShapeUtil::Equal( + new_while->while_body()->parameter_instruction(0)->shape(), + new_while_shape)); + EXPECT_TRUE(ShapeUtil::Equal( + new_while->while_condition()->parameter_instruction(0)->shape(), + new_while_shape)); + + EXPECT_THAT(new_while->while_body()->root_instruction(), + op::Tuple(op::GetTupleElement(op::Parameter(), 0), + op::GetTupleElement(op::Parameter(), 1), op::Add(), + op::Add(op::GetTupleElement(op::Parameter(), 3), + op::Constant()))); + EXPECT_THAT(new_while->while_condition()->root_instruction(), + op::Lt(op::Power(op::Add(), op::Add()), op::Constant())); +} + +// We shouldn't merge S16 induction variables; we can't create constants of this +// type because S16 literals are not implemented. +TEST_F(WhileLoopSimplifierTest, MergeInductionVariables_SkipS16) { + string hlo_string = absl::StrReplaceAll(kSimpleMergeInductionVariablesModule, + {{"TYPE", "s16"}}); + EXPECT_FALSE( + WhileLoopSimplifier() + .Run(ParseAndReturnVerifiedModule(hlo_string).ValueOrDie().get()) + .ValueOrDie()); +} + } // namespace } // namespace xla diff --git a/tensorflow/compiler/xla/shape.cc b/tensorflow/compiler/xla/shape.cc new file mode 100644 index 0000000000..746ab9e997 --- /dev/null +++ b/tensorflow/compiler/xla/shape.cc @@ -0,0 +1,107 @@ +/* Copyright 2018 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/compiler/xla/shape.h" + +#include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" +#include "tensorflow/compiler/xla/shape_util.h" + +namespace xla { + +Shape::Shape(const ShapeProto& shape_proto) { + set_element_type(shape_proto.element_type()); + dimensions_.reserve(shape_proto.dimensions_size()); + for (const int64 dimension : shape_proto.dimensions()) { + add_dimensions(dimension); + } + tuple_shapes_.reserve(shape_proto.tuple_shapes_size()); + for (const ShapeProto& element_shape : shape_proto.tuple_shapes()) { + *add_tuple_shapes() = Shape(element_shape); + } + if (shape_proto.has_layout()) { + *mutable_layout() = shape_proto.layout(); + } +} + +ShapeProto Shape::ToProto() const { + ShapeProto proto; + proto.set_element_type(element_type_); + proto.mutable_dimensions()->Reserve(dimensions_size()); + for (const int64 dimension : dimensions()) { + proto.add_dimensions(dimension); + } + proto.mutable_tuple_shapes()->Reserve(tuple_shapes_size()); + for (const Shape& shape : tuple_shapes()) { + *proto.add_tuple_shapes() = shape.ToProto(); + } + if (has_layout()) { + *proto.mutable_layout() = layout(); + } + return proto; +} + +string Shape::ToString(bool print_layout) const { + if (print_layout) { + return ShapeUtil::HumanStringWithLayout(*this); + } else { + return ShapeUtil::HumanString(*this); + } +} + +std::ostream& operator<<(std::ostream& out, const Shape& shape) { + out << shape.ToString(/*print_layout=*/true); + return out; +} + +ProgramShape::ProgramShape(const ProgramShapeProto& program_shape_proto) { + for (const ShapeProto& shape_proto : program_shape_proto.parameters()) { + *add_parameters() = Shape(shape_proto); + } + *mutable_result() = Shape(program_shape_proto.result()); + for (const string& name : program_shape_proto.parameter_names()) { + add_parameter_names(name); + } +} + +ProgramShapeProto ProgramShape::ToProto() const { + ProgramShapeProto proto; + for (const Shape& shape : parameters()) { + *proto.add_parameters() = shape.ToProto(); + } + *proto.mutable_result() = result().ToProto(); + for (const string& name : parameter_names()) { + proto.add_parameter_names(name); + } + return proto; +} + +string ProgramShape::ToString() const { + std::vector parameter_strings(parameters_size()); + for (int i = 0; i < parameters_size(); ++i) { + parameter_strings[i] = absl::StrCat( + i < parameter_names_size() ? parameter_names(i) : "(unknown)", ": ", + ShapeUtil::HumanString(parameters(i))); + } + return absl::StrCat("(", absl::StrJoin(parameter_strings, ", "), ") -> ", + ShapeUtil::HumanString(result())); +} + +std::ostream& operator<<(std::ostream& out, const ProgramShape& program_shape) { + out << program_shape.ToString() << "\n"; + return out; +} + +} // namespace xla diff --git a/tensorflow/compiler/xla/shape.h b/tensorflow/compiler/xla/shape.h new file mode 100644 index 0000000000..7f6b14ab42 --- /dev/null +++ b/tensorflow/compiler/xla/shape.h @@ -0,0 +1,204 @@ +/* Copyright 2018 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_COMPILER_XLA_SHAPE_H_ +#define TENSORFLOW_COMPILER_XLA_SHAPE_H_ + +#include +#include + +#include "absl/types/optional.h" +#include "tensorflow/compiler/xla/types.h" +#include "tensorflow/compiler/xla/xla_data.pb.h" +#include "tensorflow/core/platform/types.h" + +namespace xla { + +// A shape describes the number of dimensions in a array, the bounds of each +// dimension, and the primitive component type. For tuples, shape describes the +// structure (number of elements and nesting). +class Shape { + public: + Shape() = default; + + // Construct a shape from a ShapeProto. + explicit Shape(const ShapeProto& shape_proto); + + // Returns a ShapeProto representation of the Shape. + ShapeProto ToProto() const; + + // Returns a human-readable string that represents the given shape, with or + // without layout. e.g. "F32[42,12] {0, 1}" or "F32[64]". + string ToString(bool print_layout = false) const; + + // The following methods mirror the protobuf generated code interface for the + // message ShapeProto. This enabled easy migration of this data structure + // from a proto to a proper C++ class. + // TODO(b/29771030): Replace or augment these methods with a more ergonomic + // interface. + + // Methods for accessing the primitive type. + PrimitiveType element_type() const { return element_type_; } + void set_element_type(PrimitiveType value) { element_type_ = value; } + + // Methods for accessing the dimensions array. + int dimensions_size() const { return dimensions_.size(); } + int64 dimensions(int index) const { return dimensions_.at(index); } + void set_dimensions(int index, int64 value) { dimensions_.at(index) = value; } + void add_dimensions(int64 value) { dimensions_.push_back(value); } + void clear_dimensions() { dimensions_.clear(); } + const std::vector& dimensions() const { return dimensions_; } + std::vector* mutable_dimensions() { return &dimensions_; } + + // Methods for accessing the tuple subshapes. This field only non-empty for + // tuple shapes. + int tuple_shapes_size() const { return tuple_shapes_.size(); } + const Shape& tuple_shapes(int index) const { return tuple_shapes_.at(index); } + Shape* mutable_tuple_shapes(int index) { return &tuple_shapes_.at(index); } + Shape* add_tuple_shapes() { + tuple_shapes_.push_back(Shape()); + return &tuple_shapes_.back(); + } + void clear_tuple_shapes() { tuple_shapes_.clear(); } + const std::vector& tuple_shapes() const { return tuple_shapes_; } + std::vector* mutable_tuple_shapes() { return &tuple_shapes_; } + + // Methods for accessing the layout field. + bool has_layout() const { return layout_.has_value(); } + const Layout& layout() const { + if (layout_.has_value()) { + return *layout_; + } else { + return Layout::default_instance(); + } + } + Layout* mutable_layout() { + if (!layout_.has_value()) { + layout_ = Layout(); + } + return &layout_.value(); + } + void clear_layout() { layout_.reset(); } + + void Swap(Shape* other) { + using std::swap; + swap(*this, *other); + } + + void Clear() { + element_type_ = PRIMITIVE_TYPE_INVALID; + dimensions_.clear(); + tuple_shapes_.clear(); + layout_.reset(); + } + + string SerializeAsString() const { return ToProto().SerializeAsString(); } + string ShortDebugString() const { return ToProto().ShortDebugString(); } + string DebugString() const { return ToProto().DebugString(); } + + public: + // The element type of this shape (tuple, array, etc). + PrimitiveType element_type_ = PRIMITIVE_TYPE_INVALID; + + // The array bounds of the dimensions. This is nonempty only for array shapes. + std::vector dimensions_; + + // The tuple element subshapes. This is nonempty only for tuple shapes. + std::vector tuple_shapes_; + + // The array layout of the shape. This is present only for array shapes. + absl::optional layout_; +}; + +// Shape of the parameters and output of an XLA computation. This is analogous +// to a traditional function signature. +class ProgramShape { + public: + ProgramShape() = default; + + // Creates a ProgramShape from a ProgramShapeProto protobuf. + explicit ProgramShape(const ProgramShapeProto& program_shape_proto); + + // Returns a proto representation of the object. + ProgramShapeProto ToProto() const; + + string ToString() const; + + // The following methods mirror the protobuf generated code interface for the + // message ProgramShapeProto. This enabled easy migration of this data + // structure from a proto to a proper C++ class. + // TODO(b/29771030): Replace or augment these methods with a more ergonomic + // interface. + + // Methods for accessing and manipulating the Shape of the parameters. + int parameters_size() const { return parameters_.size(); } + const Shape& parameters(int index) const { return parameters_.at(index); } + Shape* mutable_parameters(int index) { return ¶meters_.at(index); } + Shape* add_parameters() { + parameters_.emplace_back(); + return ¶meters_.back(); + } + void clear_parameters() { parameters_.clear(); } + const std::vector& parameters() const { return parameters_; } + std::vector* mutable_parameters() { return ¶meters_; } + + // Methods for accessing and manipulating the Shape of the result. + const Shape& result() const { return result_; } + Shape* mutable_result() { return &result_; } + + // Methods for accessing and manipulating the names of the parameters. + int parameter_names_size() const { return parameter_names_.size(); } + const string& parameter_names(int index) const { + return parameter_names_.at(index); + } + void set_parameter_names(int index, const string& value) { + parameter_names_.at(index) = value; + } + string* mutable_parameter_names(int index) { + return ¶meter_names_.at(index); + } + void add_parameter_names(const string& value) { + parameter_names_.push_back(value); + } + string* add_parameter_names() { + parameter_names_.push_back(""); + return ¶meter_names_.back(); + } + void clear_parameter_names() { parameter_names_.clear(); } + const std::vector& parameter_names() const { + return parameter_names_; + } + std::vector* mutable_parameter_names() { return ¶meter_names_; } + + string ShortDebugString() const { return ToProto().ShortDebugString(); } + string DebugString() const { return ToProto().DebugString(); } + + private: + // The shapes of the parameters of the computation represented by this object. + std::vector parameters_; + + // The names of the parameters of the computation represented by this object. + std::vector parameter_names_; + + // The shape of the result of the computation represented by this object. + Shape result_; +}; + +std::ostream& operator<<(std::ostream& out, const Shape& shape); +std::ostream& operator<<(std::ostream& out, const ProgramShape& program_shape); + +} // namespace xla + +#endif // TENSORFLOW_COMPILER_XLA_SHAPE_H_ diff --git a/tensorflow/compiler/xla/shape_test.cc b/tensorflow/compiler/xla/shape_test.cc new file mode 100644 index 0000000000..e396897eee --- /dev/null +++ b/tensorflow/compiler/xla/shape_test.cc @@ -0,0 +1,149 @@ +/* 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/compiler/xla/shape.h" + +#include +#include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" +#include "tensorflow/compiler/xla/layout_util.h" +#include "tensorflow/compiler/xla/shape_util.h" +#include "tensorflow/compiler/xla/status_macros.h" +#include "tensorflow/compiler/xla/test.h" +#include "tensorflow/compiler/xla/test_helpers.h" +#include "tensorflow/compiler/xla/types.h" +#include "tensorflow/compiler/xla/util.h" +#include "tensorflow/compiler/xla/xla_data.pb.h" + +namespace xla { +namespace { + +class ShapeTest : public ::testing::Test { + protected: + const Shape opaque_ = ShapeUtil::MakeOpaqueShape(); + const Shape token_ = ShapeUtil::MakeTokenShape(); + const Shape scalar_ = ShapeUtil::MakeShape(F32, {}); + const Shape matrix_ = ShapeUtil::MakeShape(U32, {1, 2}); + const Shape matrix2_ = ShapeUtil::MakeShapeWithLayout(S32, {3, 4}, {0, 1}); + const Shape tuple_ = + ShapeUtil::MakeTupleShape({opaque_, scalar_, matrix_, matrix2_}); + const Shape nested_tuple_ = + ShapeUtil::MakeTupleShape({tuple_, matrix_, token_}); +}; + +TEST_F(ShapeTest, ShapeToFromProto) { + for (const Shape& shape : + {opaque_, token_, scalar_, matrix_, matrix2_, tuple_, nested_tuple_}) { + Shape shape_copy(shape.ToProto()); + EXPECT_TRUE(ShapeUtil::Equal(shape, shape_copy)) + << shape << " != " << shape_copy; + } +} + +TEST_F(ShapeTest, ShapeToString) { + EXPECT_EQ("opaque[]", opaque_.ToString()); + EXPECT_EQ("token[]", token_.ToString()); + EXPECT_EQ("f32[]", scalar_.ToString()); + EXPECT_EQ("u32[1,2]", matrix_.ToString()); + EXPECT_EQ("s32[3,4]", matrix2_.ToString()); + EXPECT_EQ("(opaque[], f32[], u32[1,2], s32[3,4])", tuple_.ToString()); + EXPECT_EQ("((opaque[], f32[], u32[1,2], s32[3,4]), u32[1,2], token[])", + nested_tuple_.ToString()); + + EXPECT_EQ("opaque[]", opaque_.ToString(/*print_layout=*/true)); + EXPECT_EQ("f32[]", scalar_.ToString(/*print_layout=*/true)); + EXPECT_EQ("u32[1,2]{1,0}", matrix_.ToString(/*print_layout=*/true)); + EXPECT_EQ("s32[3,4]{0,1}", matrix2_.ToString(/*print_layout=*/true)); + EXPECT_EQ("(opaque[], f32[], u32[1,2]{1,0}, s32[3,4]{0,1})", + tuple_.ToString(/*print_layout=*/true)); + EXPECT_EQ( + "((opaque[], f32[], u32[1,2]{1,0}, s32[3,4]{0,1}), u32[1,2]{1,0}, " + "token[])", + nested_tuple_.ToString(/*print_layout=*/true)); +} + +TEST_F(ShapeTest, ProgramShapeToFromProto) { + ProgramShape program_shape; + *program_shape.add_parameters() = ShapeUtil::MakeShape(F32, {1, 2, 3}); + *program_shape.add_parameters() = ShapeUtil::MakeTokenShape(); + *program_shape.add_parameters() = ShapeUtil::MakeShape(S64, {}); + *program_shape.add_parameters() = ShapeUtil::MakeTupleShape( + {ShapeUtil::MakeShape(S32, {}), + ShapeUtil::MakeTupleShape({ShapeUtil::MakeTokenShape()}), + ShapeUtil::MakeShape(F32, {42, 42})}); + + *program_shape.mutable_result() = ShapeUtil::MakeShape(F32, {7}); + + program_shape.add_parameter_names("foo"); + program_shape.add_parameter_names("bar"); + program_shape.add_parameter_names("baz"); + program_shape.add_parameter_names("qux qux"); + + // Create a copy of the program shape by round-tripping through a proto. + ProgramShape program_shape_copy(program_shape.ToProto()); + ASSERT_EQ(program_shape.parameters_size(), + program_shape_copy.parameters_size()); + for (int i = 0; i < program_shape.parameters_size(); ++i) { + EXPECT_TRUE(ShapeUtil::Equal(program_shape.parameters(i), + program_shape_copy.parameters(i))); + } + + EXPECT_TRUE( + ShapeUtil::Equal(program_shape.result(), program_shape_copy.result())); + + ASSERT_EQ(program_shape.parameter_names_size(), + program_shape_copy.parameter_names_size()); + for (int i = 0; i < program_shape.parameter_names_size(); ++i) { + EXPECT_EQ(program_shape.parameter_names(i), + program_shape_copy.parameter_names(i)); + } +} + +TEST_F(ShapeTest, ProgramShapeToString) { + ProgramShape prog = ShapeUtil::MakeProgramShape( + {opaque_, scalar_, matrix_, matrix2_, tuple_, nested_tuple_}, + nested_tuple_); + EXPECT_EQ( + "((unknown): opaque[], " + "(unknown): f32[], " + "(unknown): u32[1,2], " + "(unknown): s32[3,4], " + "(unknown): (opaque[], f32[], u32[1,2], s32[3,4]), " + "(unknown): ((opaque[], f32[], u32[1,2], s32[3,4]), u32[1,2], token[])) " + "-> " + "((opaque[], f32[], u32[1,2], s32[3,4]), u32[1,2], token[])", + prog.ToString()); + + prog.add_parameter_names("arg0"); + prog.add_parameter_names("scalar"); + prog.add_parameter_names("matrix"); + prog.add_parameter_names("matrix2"); + prog.add_parameter_names("tuple"); + prog.add_parameter_names("nested_tuple"); + EXPECT_EQ( + "(arg0: opaque[], " + "scalar: f32[], " + "matrix: u32[1,2], " + "matrix2: s32[3,4], " + "tuple: (opaque[], f32[], u32[1,2], s32[3,4]), " + "nested_tuple: ((opaque[], f32[], u32[1,2], s32[3,4]), u32[1,2], " + "token[])) " + "-> " + "((opaque[], f32[], u32[1,2], s32[3,4]), u32[1,2], token[])", + prog.ToString()); +} + +} // namespace +} // namespace xla diff --git a/tensorflow/compiler/xla/shape_tree.h b/tensorflow/compiler/xla/shape_tree.h index df610102b4..7bf9772916 100644 --- a/tensorflow/compiler/xla/shape_tree.h +++ b/tensorflow/compiler/xla/shape_tree.h @@ -667,12 +667,11 @@ void ShapeTree::CopySubtreeFrom(const ShapeTree& other, template bool ShapeTree::operator==(const ShapeTree& other) const { bool equal = true; - ForEachElement( - [this, &other, &equal](const ShapeIndex& index, const T& data) { - if (data != other.element(index)) { - equal = false; - } - }); + ForEachElement([&other, &equal](const ShapeIndex& index, const T& data) { + if (data != other.element(index)) { + equal = false; + } + }); return equal; } diff --git a/tensorflow/compiler/xla/shape_tree_test.cc b/tensorflow/compiler/xla/shape_tree_test.cc index c8ff55e784..2b6c484bc4 100644 --- a/tensorflow/compiler/xla/shape_tree_test.cc +++ b/tensorflow/compiler/xla/shape_tree_test.cc @@ -52,10 +52,10 @@ class ShapeTreeTest : public ::testing::Test { TEST_F(ShapeTreeTest, DefaultConstructor) { ShapeTree int_tree; - EXPECT_TRUE(ShapeUtil::IsNil(int_tree.shape())); + EXPECT_TRUE(ShapeUtil::IsEmptyTuple(int_tree.shape())); ShapeTree bool_tree; - EXPECT_TRUE(ShapeUtil::IsNil(bool_tree.shape())); + EXPECT_TRUE(ShapeUtil::IsEmptyTuple(bool_tree.shape())); } void ShapeTreeTest::TestShapeConstructor(const Shape& shape, diff --git a/tensorflow/compiler/xla/shape_util.cc b/tensorflow/compiler/xla/shape_util.cc index d0c35d8dee..f3cc51ca91 100644 --- a/tensorflow/compiler/xla/shape_util.cc +++ b/tensorflow/compiler/xla/shape_util.cc @@ -79,14 +79,14 @@ bool ShapeIndexView::StartsWith(ShapeIndexView prefix) const { indices_.subspan(0, prefix.size()) == prefix.indices_; } -namespace { - -// Returns whether the given primitive type corresponds to an array shape. -bool IsArrayPrimitiveType(PrimitiveType primitive_type) { +/* static */ bool ShapeUtil::IsArrayPrimitiveType( + PrimitiveType primitive_type) { return primitive_type != PRIMITIVE_TYPE_INVALID && primitive_type != TUPLE && primitive_type != OPAQUE && primitive_type != TOKEN; } +namespace { + // Recursive helper for comparing the equality of two shapes. Returns true if // the shapes are the same. If compare_layouts is true, then layouts must also // match. @@ -121,6 +121,23 @@ bool CompareShapes(const Shape& lhs, const Shape& rhs, bool compare_layouts, VLOG(3) << "CompareShapes: lhs layout != rhs layout"; return false; } + + const auto& lhs_tiles = lhs.layout().tiles(); + const auto& rhs_tiles = rhs.layout().tiles(); + if (lhs_tiles.size() != rhs_tiles.size()) { + return false; + } + for (int64 i = 0; i < lhs_tiles.size(); i++) { + if (!absl::c_equal(lhs_tiles[i].dimensions(), + rhs_tiles[i].dimensions())) { + return false; + } + } + + if (lhs.layout().element_size_in_bits() != + rhs.layout().element_size_in_bits()) { + return false; + } } } @@ -203,7 +220,7 @@ StatusOr MakeShapeWithLayoutInternal( /* static */ ProgramShape ShapeUtil::MakeProgramShape( std::initializer_list parameters, Shape result) { ProgramShape program_shape; - for (const auto& shape : parameters) { + for (const Shape& shape : parameters) { *program_shape.add_parameters() = shape; } *program_shape.mutable_result() = std::move(result); @@ -272,7 +289,7 @@ ShapeUtil::MakeShapeWithDescendingLayoutAndSamePhysicalLayout( /* static */ Shape ShapeUtil::MakeTupleShape(absl::Span shapes) { Shape result; result.set_element_type(TUPLE); - result.mutable_tuple_shapes()->Reserve(shapes.size()); + result.mutable_tuple_shapes()->reserve(shapes.size()); for (const auto& shape : shapes) { AppendShapeToTuple(shape, &result); } @@ -372,10 +389,6 @@ ShapeUtil::MakeShapeWithDescendingLayoutAndSamePhysicalLayout( return IsTuple(shape) && TupleElementCount(shape) == 0; } -/* static */ bool ShapeUtil::IsNil(const Shape& shape) { - return IsEmptyTuple(shape); -} - /* static */ int64 ShapeUtil::TupleElementCount(const Shape& shape) { CHECK(IsTuple(shape)) << HumanString(shape); return shape.tuple_shapes_size(); @@ -1155,7 +1168,7 @@ Status ForEachMutableSubshapeHelper( // Let the argument `permutation` be P. This is a permutation over `shape`'s // dimensions, so our return value will be a shape with dims P.I = P. Our // goal is to construct a layout permutation L* that we can apply to P such - // that that the physical dimension ordering of the returned shape is the same + // that the physical dimension ordering of the returned shape is the same // as that of the original shape, namely L'. // // Our returned shape has dims P and layout L*, so its in-memory layout is @@ -1600,7 +1613,8 @@ ShapeUtil::DimensionsUnmodifiedByReshape(const Shape& input_shape, /* static */ Shape ShapeUtil::DeleteDimension(int64 dim_to_delete, Shape shape) { CHECK(IsArray(shape)); - shape.mutable_dimensions()->erase(shape.dimensions().begin() + dim_to_delete); + shape.mutable_dimensions()->erase(shape.mutable_dimensions()->begin() + + dim_to_delete); if (LayoutUtil::HasLayout(shape)) { Layout* layout = shape.mutable_layout(); layout->set_format(DENSE); @@ -1634,11 +1648,6 @@ ShapeUtil::DimensionsUnmodifiedByReshape(const Shape& input_shape, return shape; } -std::ostream& operator<<(std::ostream& out, const Shape& shape) { - out << ShapeUtil::HumanStringWithLayout(shape); - return out; -} - /*static*/ size_t ShapeUtil::Hash(const Shape& shape) { using tensorflow::hash; using tensorflow::Hash64Combine; diff --git a/tensorflow/compiler/xla/shape_util.h b/tensorflow/compiler/xla/shape_util.h index a7a3026cf3..84a27f662a 100644 --- a/tensorflow/compiler/xla/shape_util.h +++ b/tensorflow/compiler/xla/shape_util.h @@ -28,6 +28,7 @@ limitations under the License. #include "absl/types/span.h" #include "tensorflow/compiler/xla/layout_util.h" #include "tensorflow/compiler/xla/primitive_util.h" +#include "tensorflow/compiler/xla/shape.h" #include "tensorflow/compiler/xla/status_macros.h" #include "tensorflow/compiler/xla/statusor.h" #include "tensorflow/compiler/xla/types.h" @@ -37,6 +38,7 @@ limitations under the License. #include "tensorflow/core/platform/cpu_info.h" #include "tensorflow/core/platform/env.h" #include "tensorflow/core/platform/macros.h" +#include "tensorflow/core/platform/mutex.h" #include "tensorflow/core/platform/types.h" namespace xla { @@ -100,6 +102,11 @@ class ShapeIndex { string ToString() const; + template + friend H AbslHashValue(H h, const ShapeIndex& index) { + return H::combine(std::move(h), index.indices_); + } + private: container_type indices_; }; @@ -461,6 +468,9 @@ class ShapeUtil { // arrays. static bool IsArray(const Shape& shape); + // Returns whether the given primitive type corresponds to an array shape. + static bool IsArrayPrimitiveType(PrimitiveType primitive_type); + // Returns whether the shape is a tuple with at least one element which is // also a tuple. static bool IsNestedTuple(const Shape& shape); @@ -468,9 +478,6 @@ class ShapeUtil { // Returns true if shape is an empty tuple. static bool IsEmptyTuple(const Shape& shape); - // Returns true if shape is the nil shape (an empty tuple). - static bool IsNil(const Shape& shape); - // Returns the number of elements in the given tuple shape. // Precondition: IsTuple(shape) static int64 TupleElementCount(const Shape& shape); @@ -754,10 +761,18 @@ class ShapeUtil { pool.emplace(tensorflow::Env::Default(), "foreach", kNumThreads); } + tensorflow::mutex mu; + Status status; // Guarded by mu + while (n < rank) { if (pool != absl::nullopt) { - pool->Schedule( - [indexes, &visitor_function] { visitor_function(indexes); }); + pool->Schedule([indexes, &visitor_function, &mu, &status] { + StatusOr result = visitor_function(indexes); + if (!result.ok()) { + tensorflow::mutex_lock lock(mu); + status = status.ok() ? result.status() : status; + } + }); } else { TF_ASSIGN_OR_RETURN(bool should_continue, visitor_function(indexes)); if (!should_continue) { @@ -775,14 +790,14 @@ class ShapeUtil { } } - return Status::OK(); + // Waits for the scheduled work to complete. + pool.reset(); + return status; } TF_DISALLOW_COPY_AND_ASSIGN(ShapeUtil); }; -std::ostream& operator<<(std::ostream& out, const Shape& shape); - } // namespace xla #endif // TENSORFLOW_COMPILER_XLA_SHAPE_UTIL_H_ diff --git a/tensorflow/compiler/xla/shape_util_test.cc b/tensorflow/compiler/xla/shape_util_test.cc index 0c647369a3..60bdbe3020 100644 --- a/tensorflow/compiler/xla/shape_util_test.cc +++ b/tensorflow/compiler/xla/shape_util_test.cc @@ -376,12 +376,12 @@ TEST(ShapeUtilTest, ByteSizeOfWithoutPadding) { } TEST(ShapeUtilTest, NilShape) { - EXPECT_TRUE(ShapeUtil::IsNil(ShapeUtil::MakeNil())); - EXPECT_FALSE(ShapeUtil::IsNil(ShapeUtil::MakeShape(F32, {1, 2, 3}))); - EXPECT_FALSE(ShapeUtil::IsNil(ShapeUtil::MakeShape(F32, {0, 1}))); - EXPECT_FALSE(ShapeUtil::IsNil( + EXPECT_TRUE(ShapeUtil::IsEmptyTuple(ShapeUtil::MakeNil())); + EXPECT_FALSE(ShapeUtil::IsEmptyTuple(ShapeUtil::MakeShape(F32, {1, 2, 3}))); + EXPECT_FALSE(ShapeUtil::IsEmptyTuple(ShapeUtil::MakeShape(F32, {0, 1}))); + EXPECT_FALSE(ShapeUtil::IsEmptyTuple( ShapeUtil::MakeTupleShape({ShapeUtil::MakeShape(S32, {})}))); - EXPECT_FALSE(ShapeUtil::IsNil( + EXPECT_FALSE(ShapeUtil::IsEmptyTuple( ShapeUtil::MakeTupleShape({ShapeUtil::MakeShape(F32, {0})}))); } @@ -546,68 +546,6 @@ TEST(ShapeUtilTest, IsLeafIndex) { EXPECT_TRUE(ShapeUtil::IsLeafIndex(nested_tuple_shape, {1, 1})); } -TEST(ShapeUtilTest, HumanString) { - Shape opaque = ShapeUtil::MakeOpaqueShape(); - Shape token = ShapeUtil::MakeTokenShape(); - Shape scalar = ShapeUtil::MakeShape(F32, {}); - Shape matrix = ShapeUtil::MakeShape(U32, {1, 2}); - Shape matrix2 = ShapeUtil::MakeShapeWithLayout(S32, {3, 4}, {0, 1}); - Shape tuple = ShapeUtil::MakeTupleShape({opaque, scalar, matrix, matrix2}); - Shape nested_tuple = ShapeUtil::MakeTupleShape({tuple, matrix, token}); - - EXPECT_EQ("opaque[]", ShapeUtil::HumanString(opaque)); - EXPECT_EQ("token[]", ShapeUtil::HumanString(token)); - EXPECT_EQ("f32[]", ShapeUtil::HumanString(scalar)); - EXPECT_EQ("u32[1,2]", ShapeUtil::HumanString(matrix)); - EXPECT_EQ("s32[3,4]", ShapeUtil::HumanString(matrix2)); - EXPECT_EQ("(opaque[], f32[], u32[1,2], s32[3,4])", - ShapeUtil::HumanString(tuple)); - EXPECT_EQ("((opaque[], f32[], u32[1,2], s32[3,4]), u32[1,2], token[])", - ShapeUtil::HumanString(nested_tuple)); - - EXPECT_EQ("opaque[]", ShapeUtil::HumanStringWithLayout(opaque)); - EXPECT_EQ("f32[]", ShapeUtil::HumanStringWithLayout(scalar)); - EXPECT_EQ("u32[1,2]{1,0}", ShapeUtil::HumanStringWithLayout(matrix)); - EXPECT_EQ("s32[3,4]{0,1}", ShapeUtil::HumanStringWithLayout(matrix2)); - EXPECT_EQ("(opaque[], f32[], u32[1,2]{1,0}, s32[3,4]{0,1})", - ShapeUtil::HumanStringWithLayout(tuple)); - EXPECT_EQ( - "((opaque[], f32[], u32[1,2]{1,0}, s32[3,4]{0,1}), u32[1,2]{1,0}, " - "token[])", - ShapeUtil::HumanStringWithLayout(nested_tuple)); - - ProgramShape prog = ShapeUtil::MakeProgramShape( - {opaque, scalar, matrix, matrix2, tuple, nested_tuple}, nested_tuple); - EXPECT_EQ( - "((unknown): opaque[], " - "(unknown): f32[], " - "(unknown): u32[1,2], " - "(unknown): s32[3,4], " - "(unknown): (opaque[], f32[], u32[1,2], s32[3,4]), " - "(unknown): ((opaque[], f32[], u32[1,2], s32[3,4]), u32[1,2], token[])) " - "-> " - "((opaque[], f32[], u32[1,2], s32[3,4]), u32[1,2], token[])", - ShapeUtil::HumanString(prog)); - - prog.add_parameter_names("arg0"); - prog.add_parameter_names("scalar"); - prog.add_parameter_names("matrix"); - prog.add_parameter_names("matrix2"); - prog.add_parameter_names("tuple"); - prog.add_parameter_names("nested_tuple"); - EXPECT_EQ( - "(arg0: opaque[], " - "scalar: f32[], " - "matrix: u32[1,2], " - "matrix2: s32[3,4], " - "tuple: (opaque[], f32[], u32[1,2], s32[3,4]), " - "nested_tuple: ((opaque[], f32[], u32[1,2], s32[3,4]), u32[1,2], " - "token[])) " - "-> " - "((opaque[], f32[], u32[1,2], s32[3,4]), u32[1,2], token[])", - ShapeUtil::HumanString(prog)); -} - TEST(ShapeUtilTest, ForEachSubshapeArray) { const Shape shape = ShapeUtil::MakeShape(F32, {2, 3}); int calls = 0; diff --git a/tensorflow/compiler/xla/tests/BUILD b/tensorflow/compiler/xla/tests/BUILD index db34d34f96..f7f090fe4a 100644 --- a/tensorflow/compiler/xla/tests/BUILD +++ b/tensorflow/compiler/xla/tests/BUILD @@ -79,6 +79,7 @@ cc_library( "//tensorflow/compiler/xla/service:hlo_verifier", "//tensorflow/compiler/xla/service:transfer_manager", "//tensorflow/core:lib", + "@com_google_absl//absl/base", "@com_google_absl//absl/memory", "@com_google_absl//absl/types:span", ], @@ -135,6 +136,7 @@ cc_library( "//tensorflow/core:stream_executor_no_cuda", "//tensorflow/core:test", "@com_google_absl//absl/algorithm:container", + "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/memory", "@com_google_absl//absl/types:optional", "@com_google_absl//absl/types:span", @@ -297,6 +299,56 @@ xla_test( ], ) +xla_test( + name = "conv_depthwise_test", + timeout = "long", + srcs = ["conv_depthwise_test.cc"], + blacklisted_backends = [ + # disabled because of a break b/119590850. + "gpu", + ], + shard_count = 50, + deps = [ + "//tensorflow/compiler/xla:execution_options_util", + "//tensorflow/compiler/xla:status_macros", + "//tensorflow/compiler/xla:test", + "//tensorflow/compiler/xla/client:xla_computation", + "//tensorflow/compiler/xla/service:bfloat16_normalization", + "//tensorflow/compiler/xla/service:despecializer", + "//tensorflow/compiler/xla/service:hlo_parser", + "//tensorflow/compiler/xla/tests:client_library_test_base", + "//tensorflow/compiler/xla/tests:hlo_test_base", + "//tensorflow/compiler/xla/tests:xla_internal_test_main", + "@com_google_absl//absl/types:optional", + ], +) + +xla_test( + name = "grouped_convolution_test", + timeout = "long", + srcs = ["grouped_convolution_test.cc"], + blacklisted_backends = [ + # disabled because of a break b/119590850. + "gpu", + # disabled because it times out. + "cpu", + ], + shard_count = 50, + deps = [ + "//tensorflow/compiler/xla:execution_options_util", + "//tensorflow/compiler/xla:status_macros", + "//tensorflow/compiler/xla:test", + "//tensorflow/compiler/xla/client:xla_computation", + "//tensorflow/compiler/xla/service:bfloat16_normalization", + "//tensorflow/compiler/xla/service:despecializer", + "//tensorflow/compiler/xla/service:hlo_parser", + "//tensorflow/compiler/xla/tests:client_library_test_base", + "//tensorflow/compiler/xla/tests:hlo_test_base", + "//tensorflow/compiler/xla/tests:xla_internal_test_main", + "@com_google_absl//absl/types:optional", + ], +) + xla_test( name = "check_execution_arity_test", srcs = ["check_execution_arity_test.cc"], @@ -1265,6 +1317,7 @@ xla_test( "enable_for_xla_interpreter", ], deps = [ + "//tensorflow/compiler/xla/service:hlo_parser", "//tensorflow/compiler/xla/service:hlo_verifier", "//tensorflow/compiler/xla/tests:hlo_test_base", "//tensorflow/compiler/xla/tests:xla_internal_test_main", @@ -1865,6 +1918,7 @@ xla_test( xla_test( name = "multioutput_fusion_test", srcs = ["multioutput_fusion_test.cc"], + backends = ["gpu"], deps = [ "//tensorflow/compiler/xla:literal", "//tensorflow/compiler/xla:shape_util", diff --git a/tensorflow/compiler/xla/tests/array_elementwise_ops_test.cc b/tensorflow/compiler/xla/tests/array_elementwise_ops_test.cc index 2180b22cb3..f6be27bee2 100644 --- a/tensorflow/compiler/xla/tests/array_elementwise_ops_test.cc +++ b/tensorflow/compiler/xla/tests/array_elementwise_ops_test.cc @@ -350,6 +350,44 @@ TEST_P(ArrayElementwiseOpTestParamCount, AddManyValues) { error_spec_); } +// TODO(b/119692968): This test runs OOM on the GPU and CPU backend. +XLA_TEST_F(ArrayElementwiseOpTest, + DISABLED_ON_GPU(DISABLED_ON_CPU(DeeplyNestedAddWithSlices))) { + XlaBuilder builder(TestName()); + std::vector values(30, 0.0); + auto a_literal = LiteralUtil::CreateR1(values); + auto a = Parameter(&builder, 0, a_literal.shape(), "x"); + auto b_literal = LiteralUtil::CreateR1(values); + auto b = Parameter(&builder, 1, b_literal.shape(), "x"); + + // Construct a sequence of diamond-shaped gadgets like this: + // + // add + // / \ + // slice slice + // \ / + // add + // + // Each 'left' slice removes the last element, each 'right' slice removes the + // first element. In this way, we index into the add with different + // multi-dimensional index arrays, which defeats the caching we use to avoid + // exponential compile time. + std::function generate_recursive = + [&](int64 slice_size) -> XlaOp { + if (slice_size == values.size()) { + return Add(a, b); + } + XlaOp param = generate_recursive(slice_size + 1); + auto slice1 = Slice(param, {0}, {slice_size}, {1}); + auto slice2 = Slice(param, {1}, {slice_size + 1}, {1}); + return Add(slice1, slice2); + }; + generate_recursive(1); + auto a_data = client_->TransferToServer(a_literal).ConsumeValueOrDie(); + auto b_data = client_->TransferToServer(b_literal).ConsumeValueOrDie(); + ComputeAndCompareR1(&builder, {0.0}, {a_data.get(), b_data.get()}); +} + XLA_TEST_F(ArrayElementwiseOpTest, SubTwoConstantF32s) { XlaBuilder builder(TestName()); auto a = ConstantR1(&builder, {-2.5f, 3.14f, 2.25f, -10.0f, 6.0f}); @@ -2744,12 +2782,16 @@ XLA_TEST_F(ArrayElementwiseOpTest, CompareGtR3F32sWithDegenerateDim2) { Array3D expected_3d( {{{0, 1}, {0, 0}, {0, 0}}, {{0, 1}, {1, 0}, {0, 1}}}); const string expected = R"(pred[2,3,2] { -{ { 0, 1 }, +{ + { 0, 1 }, { 0, 0 }, - { 0, 0 } }, -{ { 0, 1 }, + { 0, 0 } +}, +{ + { 0, 1 }, { 1, 0 }, - { 0, 1 } } + { 0, 1 } +} })"; EXPECT_EQ(expected, ExecuteToString(&builder, {})); } diff --git a/tensorflow/compiler/xla/tests/broadcast_simple_test.cc b/tensorflow/compiler/xla/tests/broadcast_simple_test.cc index dde19fb65d..702fb32adf 100644 --- a/tensorflow/compiler/xla/tests/broadcast_simple_test.cc +++ b/tensorflow/compiler/xla/tests/broadcast_simple_test.cc @@ -161,8 +161,7 @@ XLA_TEST_F(BroadcastSimpleTest, 1DTo2D) { XLA_TEST_F(BroadcastSimpleTest, 1DTo2D_WithDimsUsual) { XlaBuilder b(TestName()); - BroadcastInDim(ConstantR1(&b, {1, 2}), - ShapeUtil::MakeShape(F32, {2, 2}), {1}); + BroadcastInDim(ConstantR1(&b, {1, 2}), {2, 2}, {1}); Array2D expected(2, 2); expected(0, 0) = 1; @@ -175,8 +174,7 @@ XLA_TEST_F(BroadcastSimpleTest, 1DTo2D_WithDimsUsual) { XLA_TEST_F(BroadcastSimpleTest, 1DTo2D_WithDimsTranspose) { XlaBuilder b(TestName()); - BroadcastInDim(ConstantR1(&b, {1, 2}), - ShapeUtil::MakeShape(F32, {2, 2}), {0}); + BroadcastInDim(ConstantR1(&b, {1, 2}), {2, 2}, {0}); Array2D expected(2, 2); expected(0, 0) = 1; @@ -189,8 +187,8 @@ XLA_TEST_F(BroadcastSimpleTest, 1DTo2D_WithDimsTranspose) { XLA_TEST_F(BroadcastSimpleTest, 2DTo3D_WithDims) { XlaBuilder b(TestName()); - BroadcastInDim(ConstantR2(&b, {{1.0, 5.0}, {2.0, 6.0}}), - ShapeUtil::MakeShape(F32, {2, 2, 2}), {0, 1}); + BroadcastInDim(ConstantR2(&b, {{1.0, 5.0}, {2.0, 6.0}}), {2, 2, 2}, + {0, 1}); Array3D expected(2, 2, 2); expected(0, 0, 0) = 1.0; @@ -207,8 +205,8 @@ XLA_TEST_F(BroadcastSimpleTest, 2DTo3D_WithDims) { XLA_TEST_F(BroadcastSimpleTest, 2DTo3D_WithDimsNotPossibleWithBroadCast) { XlaBuilder b(TestName()); - BroadcastInDim(ConstantR2(&b, {{1.0, 5.0}, {2.0, 6.0}}), - ShapeUtil::MakeShape(F32, {2, 2, 2}), {0, 2}); + BroadcastInDim(ConstantR2(&b, {{1.0, 5.0}, {2.0, 6.0}}), {2, 2, 2}, + {0, 2}); Array3D expected(2, 2, 2); expected(0, 0, 0) = 1.0; @@ -225,8 +223,7 @@ XLA_TEST_F(BroadcastSimpleTest, 2DTo3D_WithDimsNotPossibleWithBroadCast) { XLA_TEST_F(BroadcastSimpleTest, 1DTo2D_WithDimsNotPossibleWithBroadCast) { XlaBuilder b(TestName()); - BroadcastInDim(ConstantR1(&b, {1, 2}), - ShapeUtil::MakeShape(F32, {3, 2}), {1}); + BroadcastInDim(ConstantR1(&b, {1, 2}), {3, 2}, {1}); Array2D expected(3, 2); expected(0, 0) = 1; diff --git a/tensorflow/compiler/xla/tests/client_library_test_base.cc b/tensorflow/compiler/xla/tests/client_library_test_base.cc index b98572e24c..12c0299833 100644 --- a/tensorflow/compiler/xla/tests/client_library_test_base.cc +++ b/tensorflow/compiler/xla/tests/client_library_test_base.cc @@ -107,7 +107,7 @@ StatusOr ClientLibraryTestBase::ExecuteAndTransfer( ExecutionOptions execution_options = execution_options_; if (shape_with_output_layout != nullptr) { *execution_options.mutable_shape_with_output_layout() = - *shape_with_output_layout; + shape_with_output_layout->ToProto(); } return client_->ExecuteAndTransfer(computation, arguments, &execution_options); @@ -127,7 +127,7 @@ StatusOr ClientLibraryTestBase::ExecuteAndTransferReference( ExecutionOptions execution_options = execution_options_; if (shape_with_output_layout != nullptr) { *execution_options.mutable_shape_with_output_layout() = - *shape_with_output_layout; + shape_with_output_layout->ToProto(); } execution_options.clear_device_handles(); return ref_client_->ExecuteAndTransfer(computation, arguments, diff --git a/tensorflow/compiler/xla/tests/client_library_test_base.h b/tensorflow/compiler/xla/tests/client_library_test_base.h index 34148e5886..65a23dd883 100644 --- a/tensorflow/compiler/xla/tests/client_library_test_base.h +++ b/tensorflow/compiler/xla/tests/client_library_test_base.h @@ -76,7 +76,7 @@ class ClientLibraryTestBase : public ::testing::Test { void SetFastMathDisabled(bool disabled) { auto* opts = execution_options_.mutable_debug_options(); opts->set_xla_cpu_enable_fast_math(!disabled); - opts->set_xla_gpu_enable_fast_math(!disabled); + opts->set_xla_gpu_enable_fast_min_max(!disabled); } void SetSeed(uint64 seed) { execution_options_.set_seed(seed); } diff --git a/tensorflow/compiler/xla/tests/client_test.cc b/tensorflow/compiler/xla/tests/client_test.cc index 6f2ca84bb6..363dee74b2 100644 --- a/tensorflow/compiler/xla/tests/client_test.cc +++ b/tensorflow/compiler/xla/tests/client_test.cc @@ -50,7 +50,8 @@ XLA_TEST_F(ClientTest, ExecuteWithLayout) { ExecutionOptions execution_options = execution_options_; *execution_options.mutable_shape_with_output_layout() = ShapeUtil::MakeShapeWithLayout(S32, /*dimensions=*/{2, 2}, - execute_layout); + execute_layout) + .ToProto(); TF_ASSERT_OK_AND_ASSIGN( std::unique_ptr data, client_->Execute(computation, {}, &execution_options)); @@ -84,7 +85,8 @@ XLA_TEST_F(ClientTest, ExecuteWithTupleLayout) { {ShapeUtil::MakeShapeWithLayout(S32, /*dimensions=*/{2, 2}, /*minor_to_major=*/{0, 1}), ShapeUtil::MakeShapeWithLayout(S32, /*dimensions=*/{2, 2}, - /*minor_to_major=*/{1, 0})}); + /*minor_to_major=*/{1, 0})}) + .ToProto(); TF_ASSERT_OK_AND_ASSIGN( auto result, diff --git a/tensorflow/compiler/xla/tests/concat_test.cc b/tensorflow/compiler/xla/tests/concat_test.cc index 9811a015e9..4f5b525a34 100644 --- a/tensorflow/compiler/xla/tests/concat_test.cc +++ b/tensorflow/compiler/xla/tests/concat_test.cc @@ -492,6 +492,32 @@ XLA_TEST_F(ConcatTest, ConcatR3WeirdDims) { ComputeAndCompareR3(&builder, expected, {p0.get(), p1.get()}); } +XLA_TEST_F(ConcatTest, ConcatDeeplyNested) { + XlaBuilder builder(TestName()); + auto a_literal = LiteralUtil::CreateR1({256.0}); + auto a = Parameter(&builder, 0, a_literal.shape(), "x"); + auto b = ConcatInDim(&builder, {a, a}, 0); + auto c = ConcatInDim(&builder, {b, b}, 0); + auto d = ConcatInDim(&builder, {c, c}, 0); + auto e = ConcatInDim(&builder, {d, d}, 0); + auto f = ConcatInDim(&builder, {e, e}, 0); + auto g = ConcatInDim(&builder, {f, f}, 0); + auto h = ConcatInDim(&builder, {g, g}, 0); + auto i = ConcatInDim(&builder, {h, h}, 0); + auto j = ConcatInDim(&builder, {i, i}, 0); + auto k = ConcatInDim(&builder, {j, j}, 0); + auto l = ConcatInDim(&builder, {k, k}, 0); + auto m = ConcatInDim(&builder, {l, l}, 0); + auto n = ConcatInDim(&builder, {m, m}, 0); + auto o = ConcatInDim(&builder, {n, n}, 0); + auto p = ConcatInDim(&builder, {o, o}, 0); + auto q = ConcatInDim(&builder, {p, p}, 0); + ConcatInDim(&builder, {q, q}, 0); + std::vector expected(131072, 256.0); + auto a_data = client_->TransferToServer(a_literal).ConsumeValueOrDie(); + ComputeAndCompareR1(&builder, expected, {a_data.get()}); +} + // Describes a binary rank-2 concatenation test. struct R2BinarySpec { int64 lhs_dim0; diff --git a/tensorflow/compiler/xla/tests/conv_depthwise_test.cc b/tensorflow/compiler/xla/tests/conv_depthwise_test.cc new file mode 100644 index 0000000000..bc9bd8a269 --- /dev/null +++ b/tensorflow/compiler/xla/tests/conv_depthwise_test.cc @@ -0,0 +1,234 @@ +/* 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 "absl/types/optional.h" +#include "tensorflow/compiler/xla/client/xla_computation.h" +#include "tensorflow/compiler/xla/execution_options_util.h" +#include "tensorflow/compiler/xla/service/bfloat16_normalization.h" +#include "tensorflow/compiler/xla/service/despecializer.h" +#include "tensorflow/compiler/xla/service/hlo_parser.h" +#include "tensorflow/compiler/xla/status_macros.h" +#include "tensorflow/compiler/xla/test.h" +#include "tensorflow/compiler/xla/tests/client_library_test_base.h" +#include "tensorflow/compiler/xla/tests/hlo_test_base.h" +#include "tensorflow/compiler/xla/tests/test_macros.h" + +namespace xla { +namespace { + +string GetFloatDataType(bool use_bfloat16) { + return use_bfloat16 ? "bf16" : "f32"; +} + +struct DepthwiseConvolution2DSpec { + int64 output_feature, window, stride, pad, lhs_dilate; + std::vector activation_dims; + std::vector activation_layout; + std::vector kernel_dims; + std::vector kernel_layout; + std::vector output_dims; + std::vector output_layout; +}; + +class DepthwiseConvolution2DTest + : public HloTestBase, + public ::testing::WithParamInterface< + ::testing::tuple> {}; + +static std::vector GetConv2DTestCases() { + std::vector config_set; + std::vector> config_options = { + {128, 6, 3, 64}, {256, 5, 3, 256}, {256, 5, 2, 144}, {144, 5, 3, 64}, + {144, 5, 2, 256}, {8, 48, 17, 8}, {128, 20, 6, 64}, {128, 1, 2, 144}, + {256, 1, 2, 64}, {64, 14, 12, 172}, {16, 9, 4, 16}}; + + for (auto option : config_options) { + int64 feature = option[0]; + int64 activation_size = option[1]; + int64 kernel_size = option[2]; + int64 batch = option[3]; + + std::vector kernel_layout = {3, 2, 1, 0}; + DepthwiseConvolution2DSpec config; + config.output_feature = feature; + config.window = kernel_size; + + config.activation_dims = {batch, activation_size, activation_size, feature}; + config.activation_layout = {3, 0, 2, 1}; + + config.kernel_dims = {kernel_size, kernel_size, 1, feature}; + config.kernel_layout = {3, 2, 1, 0}; + + if (activation_size == 1 && kernel_size == 2) { + // Test for outer dim. + config.output_dims = {batch, activation_size + kernel_size - 1, + activation_size + kernel_size, feature}; + } else if (feature == 256) { + // Restrict dilation-based tests only to one feature configuration. + config.stride = activation_size - 1; + config.pad = 0; + config.lhs_dilate = feature / 32; + config.output_dims = {batch, feature / 32, + activation_size - kernel_size + 1, feature}; + } else { + config.stride = config.pad = config.lhs_dilate = -1; + config.output_dims = {batch, activation_size - kernel_size + 1, + activation_size - kernel_size + 1, feature}; + } + + // Try this layout for all kernel shapes. + config.output_layout = {3, 0, 2, 1}; + config_set.push_back(config); + + // Try other layouts only for certain kernel shapes. + if (kernel_size % 2 == 0) { + config.activation_layout = {0, 3, 2, 1}; + config_set.push_back(config); + + config.output_layout = {0, 3, 2, 1}; + config_set.push_back(config); + + config.activation_layout = {3, 0, 2, 1}; + config_set.push_back(config); + } + } + + return config_set; +} + +string DepthwiseConvolution2DTestDataToString( + const ::testing::TestParamInfo< + ::testing::tuple>& data) { + const auto& spec = ::testing::get<0>(data.param); + const string data_type = GetFloatDataType(::testing::get<1>(data.param)); + string str = absl::StrCat( + "activation_dims_", absl::StrJoin(spec.activation_dims, "x"), + "_activation_layout_", absl::StrJoin(spec.activation_layout, "_"), + "_kernel_dims_", absl::StrJoin(spec.kernel_dims, "x"), "_kernel_layout_", + absl::StrJoin(spec.kernel_layout, "_"), "_output_dims_", + absl::StrJoin(spec.output_dims, "x"), "_output_layout_", + absl::StrJoin(spec.output_layout, "_"), data_type); + // -1 indicates non-existence. + if (spec.stride != -1) { + absl::StrAppend(&str, "_lhs_dilation_", spec.lhs_dilate, "x1"); + } + + // Test names are not allowed to contain the '-' character. + absl::c_replace(str, '-', 'n'); + return str; +} + +string BuildHloTextDepthwiseConvolution2D( + const DepthwiseConvolution2DSpec& spec, bool use_bfloat16) { + const string data_type = GetFloatDataType(use_bfloat16); + if (spec.activation_dims[1] == 1 && spec.kernel_dims[1] == 2) { + return absl::StrFormat( + R"( + HloModule TensorFlowDepthwiseConv + + ENTRY main { + activation = %s[%s]{%s} parameter(0) + kernel = %s[%s]{%s} parameter(1) + ROOT conv = %s[%s]{%s} convolution(%s[%s]{%s} activation, %s[%s]{%s} kernel), + window={size=%dx%d pad=1_1x%d_%d rhs_dilate=1x%d}, dim_labels=b01f_01io->b01f, + feature_group_count=%d + } + )", + data_type, absl::StrJoin(spec.activation_dims, ","), + absl::StrJoin(spec.activation_layout, ","), data_type, + absl::StrJoin(spec.kernel_dims, ","), + absl::StrJoin(spec.kernel_layout, ","), data_type, + absl::StrJoin(spec.output_dims, ","), + absl::StrJoin(spec.output_layout, ","), data_type, + absl::StrJoin(spec.activation_dims, ","), + absl::StrJoin(spec.activation_layout, ","), data_type, + absl::StrJoin(spec.kernel_dims, ","), + absl::StrJoin(spec.kernel_layout, ","), spec.window, spec.window, + spec.window, spec.window, spec.window, spec.output_feature); + + } else if (spec.stride == -1) { + return absl::StrFormat( + R"( + HloModule TensorFlowDepthwiseConv + + ENTRY main { + activation = %s[%s]{%s} parameter(0) + kernel = %s[%s]{%s} parameter(1) + ROOT conv = %s[%s]{%s} convolution(%s[%s]{%s} activation, %s[%s]{%s} kernel), + window={size=%dx%d}, dim_labels=b01f_01io->b01f, + feature_group_count=%d + } + )", + data_type, absl::StrJoin(spec.activation_dims, ","), + absl::StrJoin(spec.activation_layout, ","), data_type, + absl::StrJoin(spec.kernel_dims, ","), + absl::StrJoin(spec.kernel_layout, ","), data_type, + absl::StrJoin(spec.output_dims, ","), + absl::StrJoin(spec.output_layout, ","), data_type, + absl::StrJoin(spec.activation_dims, ","), + absl::StrJoin(spec.activation_layout, ","), data_type, + absl::StrJoin(spec.kernel_dims, ","), + absl::StrJoin(spec.kernel_layout, ","), spec.window, spec.window, + spec.output_feature); + } else { + return absl::StrFormat( + R"( + HloModule TensorFlowDepthwiseConv + + ENTRY main { + activation = %s[%s]{%s} parameter(0) + kernel = %s[%s]{%s} parameter(1) + ROOT conv = %s[%s]{%s} convolution(%s[%s]{%s} activation, %s[%s]{%s} kernel), + window={size=%dx%d stride=%dx1 pad=%d_%dx0_0 lhs_dilate=%dx1}, + dim_labels=b01f_01io->b01f, feature_group_count=%d + } + )", + data_type, absl::StrJoin(spec.activation_dims, ","), + absl::StrJoin(spec.activation_layout, ","), data_type, + absl::StrJoin(spec.kernel_dims, ","), + absl::StrJoin(spec.kernel_layout, ","), data_type, + absl::StrJoin(spec.output_dims, ","), + absl::StrJoin(spec.output_layout, ","), data_type, + absl::StrJoin(spec.activation_dims, ","), + absl::StrJoin(spec.activation_layout, ","), data_type, + absl::StrJoin(spec.kernel_dims, ","), + absl::StrJoin(spec.kernel_layout, ","), spec.window, spec.window, + spec.stride, 0, 0, spec.lhs_dilate, spec.output_feature); + } +} + +XLA_TEST_P(DepthwiseConvolution2DTest, DoIt) { + const DepthwiseConvolution2DSpec& spec = ::testing::get<0>(GetParam()); + bool use_bfloat16 = ::testing::get<1>(GetParam()); + const string hlo_text = + BuildHloTextDepthwiseConvolution2D(spec, use_bfloat16); + + EXPECT_TRUE(RunAndCompare(hlo_text, ErrorSpec{0.01, 0.01}, + [](HloModule* module) -> Status { + BFloat16MixedPrecisionRemoval remover; + TF_RETURN_IF_ERROR(remover.Run(module).status()); + Despecializer despecializer; + return despecializer.Run(module).status(); + })); +} + +INSTANTIATE_TEST_CASE_P( + DepthwiseConvolution2DTestWithRandomIndices, DepthwiseConvolution2DTest, + ::testing::Combine(::testing::ValuesIn(GetConv2DTestCases()), + ::testing::Bool()), + DepthwiseConvolution2DTestDataToString); + +} // namespace +} // namespace xla diff --git a/tensorflow/compiler/xla/tests/convolution_test.cc b/tensorflow/compiler/xla/tests/convolution_test.cc index 211d004ec8..459add9681 100644 --- a/tensorflow/compiler/xla/tests/convolution_test.cc +++ b/tensorflow/compiler/xla/tests/convolution_test.cc @@ -721,23 +721,573 @@ class Convolve2D_1x4x4x512_3x3x1x512_Depthwise_Valid : public ConvolutionTest { ComputeAndCompareLiteral(&builder, expected_r4, {input_literal.get(), filter_literal.get()}, error_spec_); + } +}; + +TYPED_TEST_CASE(Convolve2D_1x4x4x512_3x3x1x512_Depthwise_Valid, TestTypes); +TYPED_TEST(Convolve2D_1x4x4x512_3x3x1x512_Depthwise_Valid, Types) { + this->RunTest(); +} + +template +class Convolve2D_1x4x4x512_3x3x1x512_Depthwise_Valid_Output_Batch_In_Lanes + : public ConvolutionTest { + public: + void RunTest() { + XlaBuilder builder(TestName()); + std::vector input_dims = {1, 4, 4, 512}; + std::vector filter_dims = {3, 3, 1, 512}; + Shape input_shape = ShapeUtil::MakeShapeWithType(input_dims); + Shape filter_shape = ShapeUtil::MakeShapeWithType(filter_dims); + { + auto input = Parameter(&builder, 0, input_shape, "input"); + auto filter = Parameter(&builder, 1, filter_shape, "filter"); + + // Tensorflow dimension numbers for 2D convolution. + ConvolutionDimensionNumbers dnums; + dnums.set_input_batch_dimension(0); + dnums.set_output_batch_dimension(0); + dnums.add_input_spatial_dimensions(1); + dnums.add_output_spatial_dimensions(1); + dnums.add_input_spatial_dimensions(2); + dnums.add_output_spatial_dimensions(2); + dnums.set_input_feature_dimension(3); + dnums.set_output_feature_dimension(3); + dnums.add_kernel_spatial_dimensions(0); + dnums.add_kernel_spatial_dimensions(1); + dnums.set_kernel_input_feature_dimension(2); + dnums.set_kernel_output_feature_dimension(3); + + ConvWithGeneralDimensions(input, filter, {1, 1}, Padding::kValid, dnums, + /*feature_group_count=*/512); + } + + std::vector input_elems(ShapeUtil::ElementsIn(input_shape), + static_cast(1)); + auto input_r1 = LiteralUtil::CreateR1(input_elems); + auto input_r4 = input_r1.Reshape(input_dims).ConsumeValueOrDie(); + + std::vector filter_elems(ShapeUtil::ElementsIn(filter_shape), + static_cast(2)); + auto filter_r1 = LiteralUtil::CreateR1(filter_elems); + auto filter_r4 = filter_r1.Reshape(filter_dims).ConsumeValueOrDie(); + + std::vector output_elems(2048, static_cast(18)); + + auto expected_r1 = LiteralUtil::CreateR1(output_elems); + auto expected_r4 = expected_r1.Reshape({1, 2, 2, 512}).ConsumeValueOrDie(); + auto expected_r4_relaid = + expected_r4.Relayout(LayoutUtil::MakeLayout({0, 3, 2, 1})); + + auto input_literal = + client_->TransferToServer(input_r4).ConsumeValueOrDie(); + auto filter_literal = + client_->TransferToServer(filter_r4).ConsumeValueOrDie(); + + ComputeAndCompareLiteral(&builder, expected_r4_relaid, + {input_literal.get(), filter_literal.get()}, + error_spec_, &expected_r4_relaid.shape()); + } +}; + +TYPED_TEST_CASE( + Convolve2D_1x4x4x512_3x3x1x512_Depthwise_Valid_Output_Batch_In_Lanes, + TestTypes); +TYPED_TEST(Convolve2D_1x4x4x512_3x3x1x512_Depthwise_Valid_Output_Batch_In_Lanes, + Types) { + this->RunTest(); +} + +template +class Convolve2D_256x4x4x512_3x3x1x512_Depthwise_Input_Batch_in_Lanes + : public ConvolutionTest { + public: + void RunTest() { + XlaBuilder builder(TestName()); + std::vector input_dims = {256, 4, 4, 512}; + std::vector filter_dims = {3, 3, 1, 512}; + Shape input_shape = ShapeUtil::MakeShapeWithType(input_dims); + Shape filter_shape = ShapeUtil::MakeShapeWithType(filter_dims); + { + auto input = Parameter(&builder, 0, input_shape, "input"); + auto filter = Parameter(&builder, 1, filter_shape, "filter"); + + // Tensorflow dimension numbers for 2D convolution. + ConvolutionDimensionNumbers dnums; + dnums.set_input_batch_dimension(0); + dnums.set_output_batch_dimension(0); + dnums.add_input_spatial_dimensions(1); + dnums.add_output_spatial_dimensions(1); + dnums.add_input_spatial_dimensions(2); + dnums.add_output_spatial_dimensions(2); + dnums.set_input_feature_dimension(3); + dnums.set_output_feature_dimension(3); + dnums.add_kernel_spatial_dimensions(0); + dnums.add_kernel_spatial_dimensions(1); + dnums.set_kernel_input_feature_dimension(2); + dnums.set_kernel_output_feature_dimension(3); + + ConvWithGeneralDimensions(input, filter, {1, 1}, Padding::kValid, dnums, + /*feature_group_count=*/512); + } + + std::vector input_elems(ShapeUtil::ElementsIn(input_shape), + static_cast(1)); + auto input_r1 = LiteralUtil::CreateR1(input_elems); + auto input_r4 = input_r1.Reshape(input_dims).ConsumeValueOrDie(); + auto input_r4_relaid = + input_r4.Relayout(LayoutUtil::MakeLayout({0, 3, 2, 1})); + + std::vector filter_elems(ShapeUtil::ElementsIn(filter_shape), + static_cast(2)); + auto filter_r1 = LiteralUtil::CreateR1(filter_elems); + auto filter_r4 = filter_r1.Reshape(filter_dims).ConsumeValueOrDie(); + + std::vector output_elems(2048 * 256, static_cast(18)); + + auto expected_r1 = LiteralUtil::CreateR1(output_elems); + auto expected_r4 = + expected_r1.Reshape({256, 2, 2, 512}).ConsumeValueOrDie(); + + auto input_literal = + client_->TransferToServer(input_r4_relaid).ConsumeValueOrDie(); + auto filter_literal = + client_->TransferToServer(filter_r4).ConsumeValueOrDie(); + + ComputeAndCompareLiteral(&builder, expected_r4, + {input_literal.get(), filter_literal.get()}, + error_spec_); + } +}; + +TYPED_TEST_CASE(Convolve2D_256x4x4x512_3x3x1x512_Depthwise_Input_Batch_in_Lanes, + TestTypes); +TYPED_TEST(Convolve2D_256x4x4x512_3x3x1x512_Depthwise_Input_Batch_in_Lanes, + Types) { + this->RunTest(); +} + +template +class Convolve2D_256x4x4x512_3x3x1x512_Depthwise_Both_Batch_in_Lanes + : public ConvolutionTest { + public: + void RunTest() { + XlaBuilder builder(TestName()); + std::vector input_dims = {256, 4, 4, 512}; + std::vector filter_dims = {3, 3, 1, 512}; + Shape input_shape = ShapeUtil::MakeShapeWithType(input_dims); + Shape filter_shape = ShapeUtil::MakeShapeWithType(filter_dims); + { + auto input = Parameter(&builder, 0, input_shape, "input"); + auto filter = Parameter(&builder, 1, filter_shape, "filter"); + + // Tensorflow dimension numbers for 2D convolution. + ConvolutionDimensionNumbers dnums; + dnums.set_input_batch_dimension(0); + dnums.set_output_batch_dimension(0); + dnums.add_input_spatial_dimensions(1); + dnums.add_output_spatial_dimensions(1); + dnums.add_input_spatial_dimensions(2); + dnums.add_output_spatial_dimensions(2); + dnums.set_input_feature_dimension(3); + dnums.set_output_feature_dimension(3); + dnums.add_kernel_spatial_dimensions(0); + dnums.add_kernel_spatial_dimensions(1); + dnums.set_kernel_input_feature_dimension(2); + dnums.set_kernel_output_feature_dimension(3); + + ConvWithGeneralDimensions(input, filter, {1, 1}, Padding::kValid, dnums, + /*feature_group_count=*/512); + } + + std::vector input_elems(ShapeUtil::ElementsIn(input_shape), + static_cast(1)); + auto input_r1 = LiteralUtil::CreateR1(input_elems); + auto input_r4 = input_r1.Reshape(input_dims).ConsumeValueOrDie(); + auto input_r4_relaid = + input_r4.Relayout(LayoutUtil::MakeLayout({0, 3, 2, 1})); + + std::vector filter_elems(ShapeUtil::ElementsIn(filter_shape), + static_cast(2)); + auto filter_r1 = LiteralUtil::CreateR1(filter_elems); + auto filter_r4 = filter_r1.Reshape(filter_dims).ConsumeValueOrDie(); + + std::vector output_elems(2048 * 256, static_cast(18)); + + auto expected_r1 = LiteralUtil::CreateR1(output_elems); + auto expected_r4 = + expected_r1.Reshape({256, 2, 2, 512}).ConsumeValueOrDie(); + auto expected_r4_relaid = + expected_r4.Relayout(LayoutUtil::MakeLayout({0, 3, 2, 1})); + + auto input_literal = + client_->TransferToServer(input_r4_relaid).ConsumeValueOrDie(); + auto filter_literal = + client_->TransferToServer(filter_r4).ConsumeValueOrDie(); + + ComputeAndCompareLiteral(&builder, expected_r4_relaid, + {input_literal.get(), filter_literal.get()}, + error_spec_, &expected_r4_relaid.shape()); + } +}; + +TYPED_TEST_CASE(Convolve2D_256x4x4x512_3x3x1x512_Depthwise_Both_Batch_in_Lanes, + TestTypes); +TYPED_TEST(Convolve2D_256x4x4x512_3x3x1x512_Depthwise_Both_Batch_in_Lanes, + Types) { + this->RunTest(); +} + +template +class Convolve2D_1x4x4x5_3x3x1x5_Depthwise_Valid_Output_Batch_In_Lanes + : public ConvolutionTest { + public: + void RunTest() { + XlaBuilder builder(TestName()); + std::vector input_dims = {1, 4, 4, 5}; + std::vector filter_dims = {3, 3, 1, 5}; + Shape input_shape = ShapeUtil::MakeShapeWithType(input_dims); + Shape filter_shape = ShapeUtil::MakeShapeWithType(filter_dims); + { + auto input = Parameter(&builder, 0, input_shape, "input"); + auto filter = Parameter(&builder, 1, filter_shape, "filter"); + + // Tensorflow dimension numbers for 2D convolution. + ConvolutionDimensionNumbers dnums; + dnums.set_input_batch_dimension(0); + dnums.set_output_batch_dimension(0); + dnums.add_input_spatial_dimensions(1); + dnums.add_output_spatial_dimensions(1); + dnums.add_input_spatial_dimensions(2); + dnums.add_output_spatial_dimensions(2); + dnums.set_input_feature_dimension(3); + dnums.set_output_feature_dimension(3); + dnums.add_kernel_spatial_dimensions(0); + dnums.add_kernel_spatial_dimensions(1); + dnums.set_kernel_input_feature_dimension(2); + dnums.set_kernel_output_feature_dimension(3); + + ConvWithGeneralDimensions(input, filter, {1, 1}, Padding::kValid, dnums, + /*feature_group_count=*/5); + } + + std::vector input_elems(ShapeUtil::ElementsIn(input_shape)); + iota_int_init_value(input_elems, 1); + auto input_r1 = LiteralUtil::CreateR1(input_elems); + auto input_r4 = input_r1.Reshape(input_dims).ConsumeValueOrDie(); + auto input_r4_relaid = + input_r4.Relayout(LayoutUtil::MakeLayout({0, 3, 2, 1})); + + std::vector filter_elems(ShapeUtil::ElementsIn(filter_shape)); + iota_int_init_value(filter_elems, 1); + auto filter_r1 = LiteralUtil::CreateR1(filter_elems); + auto filter_r4 = filter_r1.Reshape(filter_dims).ConsumeValueOrDie(); + + auto expected_r1 = LiteralUtil::CreateR1( + {static_cast(6864), static_cast(7296), static_cast(7746), + static_cast(8214), static_cast(8700), static_cast(7809), + static_cast(8286), static_cast(8781), static_cast(9294), + static_cast(9825), static_cast(10644), static_cast(11256), + static_cast(11886), static_cast(12534), static_cast(13200), + static_cast(11589), static_cast(12246), static_cast(12921), + static_cast(13614), static_cast(14325)}); + auto expected_r4 = expected_r1.Reshape({1, 2, 2, 5}).ConsumeValueOrDie(); + auto expected_r4_relaid = + expected_r4.Relayout(LayoutUtil::MakeLayout({0, 3, 2, 1})); + + auto input_literal = + client_->TransferToServer(input_r4_relaid).ConsumeValueOrDie(); + auto filter_literal = + client_->TransferToServer(filter_r4).ConsumeValueOrDie(); + + ComputeAndCompareLiteral(&builder, expected_r4_relaid, + {input_literal.get(), filter_literal.get()}, + error_spec_, &expected_r4_relaid.shape()); + } +}; + +TYPED_TEST_CASE( + Convolve2D_1x4x4x5_3x3x1x5_Depthwise_Valid_Output_Batch_In_Lanes, + TestTypes); +TYPED_TEST(Convolve2D_1x4x4x5_3x3x1x5_Depthwise_Valid_Output_Batch_In_Lanes, + Types) { + this->RunTest(); +} + +template +class Convolve2D_1x4x4x160_3x3x1x160_Depthwise_Valid : public ConvolutionTest { + public: + void RunTest() { + XlaBuilder builder(TestName()); + std::vector input_dims = {1, 4, 4, 160}; + std::vector filter_dims = {3, 3, 1, 160}; + Shape input_shape = ShapeUtil::MakeShapeWithType(input_dims); + Shape filter_shape = ShapeUtil::MakeShapeWithType(filter_dims); + { + auto input = Parameter(&builder, 0, input_shape, "input"); + auto filter = Parameter(&builder, 1, filter_shape, "filter"); + + // Tensorflow dimension numbers for 2D convolution. + ConvolutionDimensionNumbers dnums; + dnums.set_input_batch_dimension(0); + dnums.set_output_batch_dimension(0); + dnums.add_input_spatial_dimensions(1); + dnums.add_output_spatial_dimensions(1); + dnums.add_input_spatial_dimensions(2); + dnums.add_output_spatial_dimensions(2); + dnums.set_input_feature_dimension(3); + dnums.set_output_feature_dimension(3); + dnums.add_kernel_spatial_dimensions(0); + dnums.add_kernel_spatial_dimensions(1); + dnums.set_kernel_input_feature_dimension(2); + dnums.set_kernel_output_feature_dimension(3); + + ConvWithGeneralDimensions(input, filter, {1, 1}, Padding::kValid, dnums, + /*feature_group_count=*/160); + } + + std::vector input_elems(ShapeUtil::ElementsIn(input_shape), + static_cast(1)); + auto input_r1 = LiteralUtil::CreateR1(input_elems); + auto input_r4 = input_r1.Reshape(input_dims).ConsumeValueOrDie(); + + std::vector filter_elems(ShapeUtil::ElementsIn(filter_shape), + static_cast(2)); + auto filter_r1 = LiteralUtil::CreateR1(filter_elems); + auto filter_r4 = filter_r1.Reshape(filter_dims).ConsumeValueOrDie(); + + std::vector output_elems(640, static_cast(18)); + + auto expected_r1 = LiteralUtil::CreateR1(output_elems); + auto expected_r4 = expected_r1.Reshape({1, 2, 2, 160}).ConsumeValueOrDie(); + + auto input_literal = + client_->TransferToServer(input_r4).ConsumeValueOrDie(); + auto filter_literal = + client_->TransferToServer(filter_r4).ConsumeValueOrDie(); + + ComputeAndCompareLiteral(&builder, expected_r4, + {input_literal.get(), filter_literal.get()}, + error_spec_); + } +}; + +TYPED_TEST_CASE(Convolve2D_1x4x4x160_3x3x1x160_Depthwise_Valid, TestTypes); +TYPED_TEST(Convolve2D_1x4x4x160_3x3x1x160_Depthwise_Valid, Types) { + this->RunTest(); +} + +template +class Convolve2D_1x4x4x160_3x3x1x160_Depthwise_Input_Batch_In_Lanes + : public ConvolutionTest { + public: + void RunTest() { + XlaBuilder builder(TestName()); + std::vector input_dims = {1, 4, 4, 160}; + std::vector filter_dims = {3, 3, 1, 160}; + Shape input_shape = ShapeUtil::MakeShapeWithType(input_dims); + Shape filter_shape = ShapeUtil::MakeShapeWithType(filter_dims); + { + auto input = Parameter(&builder, 0, input_shape, "input"); + auto filter = Parameter(&builder, 1, filter_shape, "filter"); + + // Tensorflow dimension numbers for 2D convolution. + ConvolutionDimensionNumbers dnums; + dnums.set_input_batch_dimension(0); + dnums.set_output_batch_dimension(0); + dnums.add_input_spatial_dimensions(1); + dnums.add_output_spatial_dimensions(1); + dnums.add_input_spatial_dimensions(2); + dnums.add_output_spatial_dimensions(2); + dnums.set_input_feature_dimension(3); + dnums.set_output_feature_dimension(3); + dnums.add_kernel_spatial_dimensions(0); + dnums.add_kernel_spatial_dimensions(1); + dnums.set_kernel_input_feature_dimension(2); + dnums.set_kernel_output_feature_dimension(3); + + ConvWithGeneralDimensions(input, filter, {1, 1}, Padding::kValid, dnums, + /*feature_group_count=*/160); + } + + std::vector input_elems(ShapeUtil::ElementsIn(input_shape), + static_cast(1)); + auto input_r1 = LiteralUtil::CreateR1(input_elems); + auto input_r4 = input_r1.Reshape(input_dims).ConsumeValueOrDie(); + auto input_r4_relaid = + input_r4.Relayout(LayoutUtil::MakeLayout({0, 3, 2, 1})); + + std::vector filter_elems(ShapeUtil::ElementsIn(filter_shape), + static_cast(2)); + auto filter_r1 = LiteralUtil::CreateR1(filter_elems); + auto filter_r4 = filter_r1.Reshape(filter_dims).ConsumeValueOrDie(); + + std::vector output_elems(640, static_cast(18)); + + auto expected_r1 = LiteralUtil::CreateR1(output_elems); + auto expected_r4 = expected_r1.Reshape({1, 2, 2, 160}).ConsumeValueOrDie(); + auto expected_r4_relaid = + expected_r4.Relayout(LayoutUtil::MakeLayout({3, 0, 2, 1})); + + auto input_literal = + client_->TransferToServer(input_r4_relaid).ConsumeValueOrDie(); + auto filter_literal = + client_->TransferToServer(filter_r4).ConsumeValueOrDie(); + + ComputeAndCompareLiteral(&builder, expected_r4_relaid, + {input_literal.get(), filter_literal.get()}, + error_spec_, &expected_r4_relaid.shape()); + } +}; + +TYPED_TEST_CASE(Convolve2D_1x4x4x160_3x3x1x160_Depthwise_Input_Batch_In_Lanes, + TestTypes); +TYPED_TEST(Convolve2D_1x4x4x160_3x3x1x160_Depthwise_Input_Batch_In_Lanes, + Types) { + this->RunTest(); +} + +template +class Convolve2D_1x4x4x160_3x3x1x160_Dephtwise_Both_Batch_In_Lanes + : public ConvolutionTest { + public: + void RunTest() { + XlaBuilder builder(TestName()); + std::vector input_dims = {1, 4, 4, 160}; + std::vector filter_dims = {3, 3, 1, 160}; + Shape input_shape = ShapeUtil::MakeShapeWithType(input_dims); + Shape filter_shape = ShapeUtil::MakeShapeWithType(filter_dims); + { + auto input = Parameter(&builder, 0, input_shape, "input"); + auto filter = Parameter(&builder, 1, filter_shape, "filter"); + + // Tensorflow dimension numbers for 2D convolution. + ConvolutionDimensionNumbers dnums; + dnums.set_input_batch_dimension(0); + dnums.set_output_batch_dimension(0); + dnums.add_input_spatial_dimensions(1); + dnums.add_output_spatial_dimensions(1); + dnums.add_input_spatial_dimensions(2); + dnums.add_output_spatial_dimensions(2); + dnums.set_input_feature_dimension(3); + dnums.set_output_feature_dimension(3); + dnums.add_kernel_spatial_dimensions(0); + dnums.add_kernel_spatial_dimensions(1); + dnums.set_kernel_input_feature_dimension(2); + dnums.set_kernel_output_feature_dimension(3); + + ConvWithGeneralDimensions(input, filter, {1, 1}, Padding::kValid, dnums, + /*feature_group_count=*/160); + } + + std::vector input_elems(ShapeUtil::ElementsIn(input_shape), + static_cast(1)); + auto input_r1 = LiteralUtil::CreateR1(input_elems); + auto input_r4 = input_r1.Reshape(input_dims).ConsumeValueOrDie(); + auto input_r4_relaid = + input_r4.Relayout(LayoutUtil::MakeLayout({0, 3, 2, 1})); + + std::vector filter_elems(ShapeUtil::ElementsIn(filter_shape), + static_cast(2)); + auto filter_r1 = LiteralUtil::CreateR1(filter_elems); + auto filter_r4 = filter_r1.Reshape(filter_dims).ConsumeValueOrDie(); + + std::vector output_elems(640, static_cast(18)); + + auto expected_r1 = LiteralUtil::CreateR1(output_elems); + auto expected_r4 = expected_r1.Reshape({1, 2, 2, 160}).ConsumeValueOrDie(); + auto expected_r4_relaid = + expected_r4.Relayout(LayoutUtil::MakeLayout({0, 3, 2, 1})); + + auto input_literal = + client_->TransferToServer(input_r4_relaid).ConsumeValueOrDie(); + auto filter_literal = + client_->TransferToServer(filter_r4).ConsumeValueOrDie(); + + ComputeAndCompareLiteral(&builder, expected_r4_relaid, + {input_literal.get(), filter_literal.get()}, + error_spec_, &expected_r4_relaid.shape()); + } +}; + +TYPED_TEST_CASE(Convolve2D_1x4x4x160_3x3x1x160_Dephtwise_Both_Batch_In_Lanes, + TestTypes); +TYPED_TEST(Convolve2D_1x4x4x160_3x3x1x160_Dephtwise_Both_Batch_In_Lanes, + Types) { + this->RunTest(); +} + +template +class Convolve2D_1x4x4x1024_3x3x1x1024_Depthwise_Valid + : public ConvolutionTest { + public: + void RunTest() { + XlaBuilder builder(TestName()); + std::vector input_dims = {1, 4, 4, 1024}; + std::vector filter_dims = {3, 3, 1, 1024}; + Shape input_shape = ShapeUtil::MakeShapeWithType(input_dims); + Shape filter_shape = ShapeUtil::MakeShapeWithType(filter_dims); + { + auto input = Parameter(&builder, 0, input_shape, "input"); + auto filter = Parameter(&builder, 1, filter_shape, "filter"); + + // Tensorflow dimension numbers for 2D convolution. + ConvolutionDimensionNumbers dnums; + dnums.set_input_batch_dimension(0); + dnums.set_output_batch_dimension(0); + dnums.add_input_spatial_dimensions(1); + dnums.add_output_spatial_dimensions(1); + dnums.add_input_spatial_dimensions(2); + dnums.add_output_spatial_dimensions(2); + dnums.set_input_feature_dimension(3); + dnums.set_output_feature_dimension(3); + dnums.add_kernel_spatial_dimensions(0); + dnums.add_kernel_spatial_dimensions(1); + dnums.set_kernel_input_feature_dimension(2); + dnums.set_kernel_output_feature_dimension(3); - auto filter_r = filter_r1.Reshape(filter_dims); + ConvWithGeneralDimensions(input, filter, {1, 1}, Padding::kValid, dnums, + /*feature_group_count=*/1024); + } + + std::vector input_elems(ShapeUtil::ElementsIn(input_shape), + static_cast(1)); + auto input_r1 = LiteralUtil::CreateR1(input_elems); + auto input_r4 = input_r1.Reshape(input_dims).ConsumeValueOrDie(); + + std::vector filter_elems(ShapeUtil::ElementsIn(filter_shape), + static_cast(2)); + auto filter_r1 = LiteralUtil::CreateR1(filter_elems); + auto filter_r4 = filter_r1.Reshape(filter_dims).ConsumeValueOrDie(); + + std::vector output_elems(4096, static_cast(18)); + + auto expected_r1 = LiteralUtil::CreateR1(output_elems); + auto expected_r4 = expected_r1.Reshape({1, 2, 2, 1024}).ConsumeValueOrDie(); + + auto input_literal = + client_->TransferToServer(input_r4).ConsumeValueOrDie(); + auto filter_literal = + client_->TransferToServer(filter_r4).ConsumeValueOrDie(); + + ComputeAndCompareLiteral(&builder, expected_r4, + {input_literal.get(), filter_literal.get()}, + error_spec_); } }; -TYPED_TEST_CASE(Convolve2D_1x4x4x512_3x3x1x512_Depthwise_Valid, TestTypes); -TYPED_TEST(Convolve2D_1x4x4x512_3x3x1x512_Depthwise_Valid, Types) { +TYPED_TEST_CASE(Convolve2D_1x4x4x1024_3x3x1x1024_Depthwise_Valid, TestTypes); +TYPED_TEST(Convolve2D_1x4x4x1024_3x3x1x1024_Depthwise_Valid, Types) { this->RunTest(); } template -class Convolve2D_1x4x4x160_3x3x1x160_Depthwise_Valid : public ConvolutionTest { +class Convolve2D_1x2x2x6_2x2x2x12_Grouped_Valid : public ConvolutionTest { public: void RunTest() { XlaBuilder builder(TestName()); - std::vector input_dims = {1, 4, 4, 160}; - std::vector filter_dims = {3, 3, 1, 160}; + std::vector input_dims = {1, 2, 2, 6}; + std::vector filter_dims = {2, 2, 2, 12}; Shape input_shape = ShapeUtil::MakeShapeWithType(input_dims); Shape filter_shape = ShapeUtil::MakeShapeWithType(filter_dims); { @@ -760,23 +1310,89 @@ class Convolve2D_1x4x4x160_3x3x1x160_Depthwise_Valid : public ConvolutionTest { dnums.set_kernel_output_feature_dimension(3); ConvWithGeneralDimensions(input, filter, {1, 1}, Padding::kValid, dnums, - /*feature_group_count=*/160); + /*feature_group_count=*/3); + } + + std::vector input_elems(ShapeUtil::ElementsIn(input_shape)); + iota_int_init_value(input_elems, 1); + auto input_r1 = LiteralUtil::CreateR1(input_elems); + auto input_r4 = input_r1.Reshape(input_dims).ConsumeValueOrDie(); + + std::vector filter_elems(ShapeUtil::ElementsIn(filter_shape)); + iota_int_init_value(filter_elems, 1); + auto filter_r1 = LiteralUtil::CreateR1(filter_elems); + auto filter_r4 = filter_r1.Reshape(filter_dims).ConsumeValueOrDie(); + + auto expected_r1 = LiteralUtil::CreateR1( + {static_cast(5076), static_cast(5160), static_cast(5244), + static_cast(5328), static_cast(6164), static_cast(6264), + static_cast(6364), static_cast(6464), static_cast(7380), + static_cast(7496), static_cast(7612), static_cast(7728)}); + auto expected_r4 = expected_r1.Reshape({1, 1, 1, 12}).ConsumeValueOrDie(); + + auto input_literal = + client_->TransferToServer(input_r4).ConsumeValueOrDie(); + auto filter_literal = + client_->TransferToServer(filter_r4).ConsumeValueOrDie(); + + ComputeAndCompareLiteral(&builder, expected_r4, + {input_literal.get(), filter_literal.get()}, + error_spec_); + } +}; + +TYPED_TEST_CASE(Convolve2D_1x2x2x6_2x2x2x12_Grouped_Valid, TestTypes); +TYPED_TEST(Convolve2D_1x2x2x6_2x2x2x12_Grouped_Valid, Types) { + this->RunTest(); +} + +template +class Convolve2D_1x2x2x1024_2x2x128x512_Grouped_Valid : public ConvolutionTest { + public: + void RunTest() { + XlaBuilder builder(TestName()); + std::vector input_dims = {1, 2, 2, 1024}; + std::vector filter_dims = {2, 2, 128, 512}; + Shape input_shape = ShapeUtil::MakeShapeWithType(input_dims); + Shape filter_shape = ShapeUtil::MakeShapeWithType(filter_dims); + { + auto input = Parameter(&builder, 0, input_shape, "input"); + auto filter = Parameter(&builder, 1, filter_shape, "filter"); + + // Tensorflow dimension numbers for 2D convolution. + ConvolutionDimensionNumbers dnums; + dnums.set_input_batch_dimension(0); + dnums.set_output_batch_dimension(0); + dnums.add_input_spatial_dimensions(1); + dnums.add_output_spatial_dimensions(1); + dnums.add_input_spatial_dimensions(2); + dnums.add_output_spatial_dimensions(2); + dnums.set_input_feature_dimension(3); + dnums.set_output_feature_dimension(3); + dnums.add_kernel_spatial_dimensions(0); + dnums.add_kernel_spatial_dimensions(1); + dnums.set_kernel_input_feature_dimension(2); + dnums.set_kernel_output_feature_dimension(3); + + ConvWithGeneralDimensions(input, filter, {1, 1}, Padding::kValid, dnums, + /*feature_group_count=*/8); } std::vector input_elems(ShapeUtil::ElementsIn(input_shape), static_cast(1)); + auto input_r1 = LiteralUtil::CreateR1(input_elems); auto input_r4 = input_r1.Reshape(input_dims).ConsumeValueOrDie(); std::vector filter_elems(ShapeUtil::ElementsIn(filter_shape), static_cast(2)); + auto filter_r1 = LiteralUtil::CreateR1(filter_elems); auto filter_r4 = filter_r1.Reshape(filter_dims).ConsumeValueOrDie(); - std::vector output_elems(640, static_cast(18)); - + std::vector output_elems(512, static_cast(1024)); auto expected_r1 = LiteralUtil::CreateR1(output_elems); - auto expected_r4 = expected_r1.Reshape({1, 2, 2, 160}).ConsumeValueOrDie(); + auto expected_r4 = expected_r1.Reshape({1, 1, 1, 512}).ConsumeValueOrDie(); auto input_literal = client_->TransferToServer(input_r4).ConsumeValueOrDie(); @@ -786,24 +1402,21 @@ class Convolve2D_1x4x4x160_3x3x1x160_Depthwise_Valid : public ConvolutionTest { ComputeAndCompareLiteral(&builder, expected_r4, {input_literal.get(), filter_literal.get()}, error_spec_); - - auto filter_r = filter_r1.Reshape(filter_dims); } }; -TYPED_TEST_CASE(Convolve2D_1x4x4x160_3x3x1x160_Depthwise_Valid, TestTypes); -TYPED_TEST(Convolve2D_1x4x4x160_3x3x1x160_Depthwise_Valid, Types) { +TYPED_TEST_CASE(Convolve2D_1x2x2x1024_2x2x128x512_Grouped_Valid, TestTypes); +TYPED_TEST(Convolve2D_1x2x2x1024_2x2x128x512_Grouped_Valid, Types) { this->RunTest(); } template -class Convolve2D_1x4x4x1024_3x3x1x1024_Depthwise_Valid - : public ConvolutionTest { +class Convolve2D_1x2x2x1024_2x2x128x8_Grouped_Valid : public ConvolutionTest { public: void RunTest() { XlaBuilder builder(TestName()); - std::vector input_dims = {1, 4, 4, 1024}; - std::vector filter_dims = {3, 3, 1, 1024}; + std::vector input_dims = {1, 2, 2, 1024}; + std::vector filter_dims = {2, 2, 128, 8}; Shape input_shape = ShapeUtil::MakeShapeWithType(input_dims); Shape filter_shape = ShapeUtil::MakeShapeWithType(filter_dims); { @@ -826,23 +1439,24 @@ class Convolve2D_1x4x4x1024_3x3x1x1024_Depthwise_Valid dnums.set_kernel_output_feature_dimension(3); ConvWithGeneralDimensions(input, filter, {1, 1}, Padding::kValid, dnums, - /*feature_group_count=*/1024); + /*feature_group_count=*/8); } std::vector input_elems(ShapeUtil::ElementsIn(input_shape), static_cast(1)); + auto input_r1 = LiteralUtil::CreateR1(input_elems); auto input_r4 = input_r1.Reshape(input_dims).ConsumeValueOrDie(); std::vector filter_elems(ShapeUtil::ElementsIn(filter_shape), static_cast(2)); + auto filter_r1 = LiteralUtil::CreateR1(filter_elems); auto filter_r4 = filter_r1.Reshape(filter_dims).ConsumeValueOrDie(); - std::vector output_elems(4096, static_cast(18)); - + std::vector output_elems(8, static_cast(1024)); auto expected_r1 = LiteralUtil::CreateR1(output_elems); - auto expected_r4 = expected_r1.Reshape({1, 2, 2, 1024}).ConsumeValueOrDie(); + auto expected_r4 = expected_r1.Reshape({1, 1, 1, 8}).ConsumeValueOrDie(); auto input_literal = client_->TransferToServer(input_r4).ConsumeValueOrDie(); @@ -852,23 +1466,21 @@ class Convolve2D_1x4x4x1024_3x3x1x1024_Depthwise_Valid ComputeAndCompareLiteral(&builder, expected_r4, {input_literal.get(), filter_literal.get()}, error_spec_); - - auto filter_r = filter_r1.Reshape(filter_dims); } }; -TYPED_TEST_CASE(Convolve2D_1x4x4x1024_3x3x1x1024_Depthwise_Valid, TestTypes); -TYPED_TEST(Convolve2D_1x4x4x1024_3x3x1x1024_Depthwise_Valid, Types) { +TYPED_TEST_CASE(Convolve2D_1x2x2x1024_2x2x128x8_Grouped_Valid, TestTypes); +TYPED_TEST(Convolve2D_1x2x2x1024_2x2x128x8_Grouped_Valid, Types) { this->RunTest(); } template -class Convolve2D_1x2x2x6_2x2x1x12_Grouped_Valid : public ConvolutionTest { +class Convolve2D_1x2x2x12_2x2x3x4_Grouped_Valid : public ConvolutionTest { public: void RunTest() { XlaBuilder builder(TestName()); - std::vector input_dims = {1, 2, 2, 6}; - std::vector filter_dims = {2, 2, 2, 12}; + std::vector input_dims = {1, 2, 2, 12}; + std::vector filter_dims = {2, 2, 3, 4}; Shape input_shape = ShapeUtil::MakeShapeWithType(input_dims); Shape filter_shape = ShapeUtil::MakeShapeWithType(filter_dims); { @@ -891,7 +1503,7 @@ class Convolve2D_1x2x2x6_2x2x1x12_Grouped_Valid : public ConvolutionTest { dnums.set_kernel_output_feature_dimension(3); ConvWithGeneralDimensions(input, filter, {1, 1}, Padding::kValid, dnums, - /*feature_group_count=*/3); + /*feature_group_count=*/4); } std::vector input_elems(ShapeUtil::ElementsIn(input_shape)); @@ -904,12 +1516,140 @@ class Convolve2D_1x2x2x6_2x2x1x12_Grouped_Valid : public ConvolutionTest { auto filter_r1 = LiteralUtil::CreateR1(filter_elems); auto filter_r4 = filter_r1.Reshape(filter_dims).ConsumeValueOrDie(); + auto expected_r1 = + LiteralUtil::CreateR1({static_cast(7712), static_cast(8816), + static_cast(9992), static_cast(11240)}); + auto expected_r4 = expected_r1.Reshape({1, 1, 1, 4}).ConsumeValueOrDie(); + + auto input_literal = + client_->TransferToServer(input_r4).ConsumeValueOrDie(); + auto filter_literal = + client_->TransferToServer(filter_r4).ConsumeValueOrDie(); + + ComputeAndCompareLiteral(&builder, expected_r4, + {input_literal.get(), filter_literal.get()}, + error_spec_); + } +}; + +TYPED_TEST_CASE(Convolve2D_1x2x2x12_2x2x3x4_Grouped_Valid, TestTypes); +TYPED_TEST(Convolve2D_1x2x2x12_2x2x3x4_Grouped_Valid, Types) { + this->RunTest(); +} + +template +class Convolve2D_1x2x2x12_2x2x3x4_Grouped_Valid_Filter_OF_In_Sublanes + : public ConvolutionTest { + public: + void RunTest() { + XlaBuilder builder(TestName()); + std::vector input_dims = {1, 2, 2, 12}; + std::vector filter_dims = {2, 2, 4, 3}; + Shape input_shape = ShapeUtil::MakeShapeWithType(input_dims); + Shape filter_shape = ShapeUtil::MakeShapeWithType(filter_dims); + { + auto input = Parameter(&builder, 0, input_shape, "input"); + auto filter = Parameter(&builder, 1, filter_shape, "filter"); + + // Tensorflow dimension numbers for 2D convolution. + ConvolutionDimensionNumbers dnums; + dnums.set_input_batch_dimension(0); + dnums.set_output_batch_dimension(0); + dnums.add_input_spatial_dimensions(1); + dnums.add_output_spatial_dimensions(1); + dnums.add_input_spatial_dimensions(2); + dnums.add_output_spatial_dimensions(2); + dnums.set_input_feature_dimension(3); + dnums.set_output_feature_dimension(3); + dnums.add_kernel_spatial_dimensions(0); + dnums.add_kernel_spatial_dimensions(1); + dnums.set_kernel_input_feature_dimension(3); + dnums.set_kernel_output_feature_dimension(2); + + ConvWithGeneralDimensions(input, filter, {1, 1}, Padding::kValid, dnums, + /*feature_group_count=*/4); + } + + std::vector input_elems(ShapeUtil::ElementsIn(input_shape)); + iota_int_init_value(input_elems, 1); + auto input_r1 = LiteralUtil::CreateR1(input_elems); + auto input_r4 = input_r1.Reshape(input_dims).ConsumeValueOrDie(); + + std::vector filter_elems(ShapeUtil::ElementsIn(filter_shape)); + iota_int_init_value(filter_elems, 1); + auto filter_r1 = LiteralUtil::CreateR1(filter_elems); + auto filter_r4 = filter_r1.Reshape(filter_dims).ConsumeValueOrDie(); + auto filter_r4_relaid = + filter_r4.Relayout(LayoutUtil::MakeLayout({3, 2, 1, 0})); auto expected_r1 = LiteralUtil::CreateR1( - {static_cast(5076), static_cast(5160), static_cast(5244), - static_cast(5328), static_cast(6164), static_cast(6264), - static_cast(6364), static_cast(6464), static_cast(7380), - static_cast(7496), static_cast(7612), static_cast(7728)}); - auto expected_r4 = expected_r1.Reshape({1, 1, 1, 12}).ConsumeValueOrDie(); + {static_cast(6968), static_cast(8516), static_cast(10280), + static_cast(12260)}); + auto expected_r4 = expected_r1.Reshape({1, 1, 1, 4}).ConsumeValueOrDie(); + + auto input_literal = + client_->TransferToServer(input_r4).ConsumeValueOrDie(); + auto filter_literal = + client_->TransferToServer(filter_r4_relaid).ConsumeValueOrDie(); + + ComputeAndCompareLiteral(&builder, expected_r4, + {input_literal.get(), filter_literal.get()}, + error_spec_); + } +}; + +TYPED_TEST_CASE(Convolve2D_1x2x2x12_2x2x3x4_Grouped_Valid_Filter_OF_In_Sublanes, + TestTypes); +TYPED_TEST(Convolve2D_1x2x2x12_2x2x3x4_Grouped_Valid_Filter_OF_In_Sublanes, + Types) { + this->RunTest(); +} + +template +class Convolve2D_1x1x1x12_1x1x3x4_Grouped_Valid : public ConvolutionTest { + public: + void RunTest() { + XlaBuilder builder(TestName()); + std::vector input_dims = {1, 1, 1, 12}; + std::vector filter_dims = {1, 1, 3, 4}; + Shape input_shape = ShapeUtil::MakeShapeWithType(input_dims); + Shape filter_shape = ShapeUtil::MakeShapeWithType(filter_dims); + { + auto input = Parameter(&builder, 0, input_shape, "input"); + auto filter = Parameter(&builder, 1, filter_shape, "filter"); + + // Tensorflow dimension numbers for 2D convolution. + ConvolutionDimensionNumbers dnums; + dnums.set_input_batch_dimension(0); + dnums.set_output_batch_dimension(0); + dnums.add_input_spatial_dimensions(1); + dnums.add_output_spatial_dimensions(1); + dnums.add_input_spatial_dimensions(2); + dnums.add_output_spatial_dimensions(2); + dnums.set_input_feature_dimension(3); + dnums.set_output_feature_dimension(3); + dnums.add_kernel_spatial_dimensions(0); + dnums.add_kernel_spatial_dimensions(1); + dnums.set_kernel_input_feature_dimension(2); + dnums.set_kernel_output_feature_dimension(3); + + ConvWithGeneralDimensions(input, filter, {1, 1}, Padding::kValid, dnums, + /*feature_group_count=*/4); + } + + std::vector input_elems(ShapeUtil::ElementsIn(input_shape)); + iota_int_init_value(input_elems, 1); + auto input_r1 = LiteralUtil::CreateR1(input_elems); + auto input_r4 = input_r1.Reshape(input_dims).ConsumeValueOrDie(); + + std::vector filter_elems(ShapeUtil::ElementsIn(filter_shape)); + iota_int_init_value(filter_elems, 1); + auto filter_r1 = LiteralUtil::CreateR1(filter_elems); + auto filter_r4 = filter_r1.Reshape(filter_dims).ConsumeValueOrDie(); + + auto expected_r1 = + LiteralUtil::CreateR1({static_cast(38), static_cast(98), + static_cast(176), static_cast(272)}); + auto expected_r4 = expected_r1.Reshape({1, 1, 1, 4}).ConsumeValueOrDie(); auto input_literal = client_->TransferToServer(input_r4).ConsumeValueOrDie(); @@ -922,8 +1662,8 @@ class Convolve2D_1x2x2x6_2x2x1x12_Grouped_Valid : public ConvolutionTest { } }; -TYPED_TEST_CASE(Convolve2D_1x2x2x6_2x2x1x12_Grouped_Valid, TestTypes); -TYPED_TEST(Convolve2D_1x2x2x6_2x2x1x12_Grouped_Valid, Types) { +TYPED_TEST_CASE(Convolve2D_1x1x1x12_1x1x3x4_Grouped_Valid, TestTypes); +TYPED_TEST(Convolve2D_1x1x1x12_1x1x3x4_Grouped_Valid, Types) { this->RunTest(); } @@ -1217,6 +1957,18 @@ ENTRY Test { EXPECT_TRUE(RunAndCompare(kHlo, ErrorSpec{0.001})); } +XLA_TEST_F(ConvolutionHloTest, DISABLED_ON_CPU(ConvolveF64ForwardReversed)) { + constexpr char kHlo[] = R"( +HloModule TestModule + +ENTRY Test { + %arg0 = f64[3,56,56,16] parameter(0) + %arg1 = f64[3,3,3,64] parameter(1) + ROOT %conv = f64[54,54,16,64] convolution(%arg0, %arg1), window={size=3x3 rhs_reversal=1x1}, dim_labels=f01b_i01o->01bf +})"; + EXPECT_TRUE(RunAndCompare(kHlo, ErrorSpec{0.001})); +} + XLA_TEST_F(ConvolutionHloTest, DISABLED_ON_CPU(ConvolveF64BackwardFilter)) { constexpr char kHlo[] = R"( HloModule TestModule diff --git a/tensorflow/compiler/xla/tests/dot_operation_test.cc b/tensorflow/compiler/xla/tests/dot_operation_test.cc index 6c0847a875..25091b8d5d 100644 --- a/tensorflow/compiler/xla/tests/dot_operation_test.cc +++ b/tensorflow/compiler/xla/tests/dot_operation_test.cc @@ -637,6 +637,76 @@ XLA_TYPED_TEST(DotOperationTest_F16F32F64CF64, GeneralMatMul) { {x_data.get(), y_data.get()}, this->error_spec_); } +#ifndef XLA_TEST_BACKEND_CPU +// TODO(b/74459949): failed on CPU on 2018-10-29. +XLA_TYPED_TEST(DotOperationTest_F16F32F64CF64, GeneralMatMulR3LhsR2Rhs) { + using T = TypeParam; + + XlaBuilder builder(this->TestName()); + auto x = + Parameter(&builder, 0, ShapeUtil::MakeShapeWithType({2, 2, 2}), "x"); + auto y = Parameter(&builder, 1, ShapeUtil::MakeShapeWithType({2, 2}), "y"); + + DotDimensionNumbers dnums; + dnums.add_lhs_contracting_dimensions(1); + dnums.add_rhs_contracting_dimensions(1); + dnums.add_lhs_batch_dimensions(0); + dnums.add_rhs_batch_dimensions(0); + + DotGeneral(x, y, dnums); + + auto x_data = + this->client_ + ->TransferToServer(LiteralUtil::CreateR3FromArray3D( + {{{1.0f, 2.0f}, {3.0f, 4.0f}}, {{5.0f, 6.0f}, {7.0f, 8.0f}}})) + .ConsumeValueOrDie(); + + auto y_data = this->client_ + ->TransferToServer(LiteralUtil::CreateR2FromArray2D( + {{1.0f, 0.0f}, {0.0f, 1.0f}})) + .ConsumeValueOrDie(); + + this->template ComputeAndCompareR2( + &builder, + /*expected=*/{{1.0f, 2.0f}, {7.0f, 8.0f}}, {x_data.get(), y_data.get()}, + this->error_spec_); +} + +// TODO(b/74459949): failed on CPU on 2018-10-29. +XLA_TYPED_TEST(DotOperationTest_F16F32F64CF64, GeneralMatMulR2LhsR3Rhs) { + using T = TypeParam; + + XlaBuilder builder(this->TestName()); + auto x = Parameter(&builder, 0, ShapeUtil::MakeShapeWithType({2, 2}), "x"); + auto y = + Parameter(&builder, 1, ShapeUtil::MakeShapeWithType({2, 2, 2}), "y"); + + DotDimensionNumbers dnums; + dnums.add_lhs_contracting_dimensions(1); + dnums.add_rhs_contracting_dimensions(1); + dnums.add_lhs_batch_dimensions(0); + dnums.add_rhs_batch_dimensions(0); + + DotGeneral(x, y, dnums); + + auto x_data = this->client_ + ->TransferToServer(LiteralUtil::CreateR2FromArray2D( + {{1.0f, 0.0f}, {0.0f, 1.0f}})) + .ConsumeValueOrDie(); + + auto y_data = + this->client_ + ->TransferToServer(LiteralUtil::CreateR3FromArray3D( + {{{1.0f, 2.0f}, {3.0f, 4.0f}}, {{5.0f, 6.0f}, {7.0f, 8.0f}}})) + .ConsumeValueOrDie(); + + this->template ComputeAndCompareR2( + &builder, + /*expected=*/{{1.0f, 2.0f}, {7.0f, 8.0f}}, {x_data.get(), y_data.get()}, + this->error_spec_); +} +#endif // XLA_TEST_BACKEND_CPU + XLA_TYPED_TEST(DotOperationTest_F16F32F64CF64, GeneralMatMulMultipleBatch) { using T = TypeParam; diff --git a/tensorflow/compiler/xla/tests/grouped_convolution_test.cc b/tensorflow/compiler/xla/tests/grouped_convolution_test.cc new file mode 100644 index 0000000000..8f7049910e --- /dev/null +++ b/tensorflow/compiler/xla/tests/grouped_convolution_test.cc @@ -0,0 +1,245 @@ +/* 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 "absl/types/optional.h" +#include "tensorflow/compiler/xla/client/xla_computation.h" +#include "tensorflow/compiler/xla/execution_options_util.h" +#include "tensorflow/compiler/xla/service/bfloat16_normalization.h" +#include "tensorflow/compiler/xla/service/despecializer.h" +#include "tensorflow/compiler/xla/service/hlo_parser.h" +#include "tensorflow/compiler/xla/status_macros.h" +#include "tensorflow/compiler/xla/test.h" +#include "tensorflow/compiler/xla/tests/client_library_test_base.h" +#include "tensorflow/compiler/xla/tests/hlo_test_base.h" +#include "tensorflow/compiler/xla/tests/test_macros.h" + +namespace xla { +namespace { + +string GetFloatDataType(bool use_bfloat16) { + return use_bfloat16 ? "bf16" : "f32"; +} + +struct GroupedConvolution2DSpec { + int64 input_feature, output_feature, window, stride, pad, lhs_dilate; + int64 group_size, group_count; + std::vector activation_dims; + std::vector activation_layout; + std::vector kernel_dims; + std::vector kernel_layout; + std::vector output_dims; + std::vector output_layout; +}; + +class GroupedConvolution2DTest + : public HloTestBase, + public ::testing::WithParamInterface< + ::testing::tuple> {}; + +static std::vector GetConv2DTestCases() { + std::vector config_set; + // Add to this set if you want a new test configuration. + // Rule : the penultimate number must be divisible by the last number. + std::vector> config_options = {{8, 2, 2, 1, 1024, 128}, + {512, 3, 3, 144, 1024, 16}, + {256, 3, 3, 129, 512, 64}, + {64, 1, 2, 127, 32, 8}, + {256, 3, 3, 256, 1024, 4}}; + + for (auto option : config_options) { + int64 output_feature = option[0]; + int64 activation_size = option[1]; + int64 kernel_size = option[2]; + int64 batch = option[3]; + int64 input_feature = option[4]; + int64 group_size = option[5]; + + std::vector kernel_layout = {3, 2, 1, 0}; + GroupedConvolution2DSpec config; + config.group_size = group_size; + config.group_count = input_feature / group_size; + config.output_feature = output_feature; + config.window = kernel_size; + + config.activation_dims = {batch, activation_size, activation_size, + input_feature}; + config.activation_layout = {3, 0, 2, 1}; + + config.kernel_dims = {kernel_size, kernel_size, group_size, output_feature}; + config.kernel_layout = {3, 2, 1, 0}; + + if (activation_size == 1 && kernel_size == 2) { + // Test for outer dim. + config.output_dims = {batch, activation_size + kernel_size - 1, + activation_size + kernel_size, output_feature}; + } else if (output_feature == 256) { + // Restrict dilation-based tests only to one feature configuration. + config.stride = activation_size - 1; + config.pad = 0; + config.lhs_dilate = output_feature / 32; + config.output_dims = {batch, output_feature / 32, + activation_size - kernel_size + 1, output_feature}; + } else { + config.stride = config.pad = config.lhs_dilate = -1; + config.output_dims = {batch, activation_size - kernel_size + 1, + activation_size - kernel_size + 1, output_feature}; + } + + // Try this layout for all kernel shapes. + config.output_layout = {3, 0, 2, 1}; + config_set.push_back(config); + + // Try other layouts only for certain kernel shapes. + if (kernel_size % 2 == 0) { + config.activation_layout = {0, 3, 2, 1}; + config_set.push_back(config); + + config.output_layout = {0, 3, 2, 1}; + config_set.push_back(config); + + config.activation_layout = {3, 0, 2, 1}; + config_set.push_back(config); + } + } + + return config_set; +} + +string GroupedConvolution2DTestDataToString( + const ::testing::TestParamInfo< + ::testing::tuple>& data) { + const auto& spec = ::testing::get<0>(data.param); + const string data_type = GetFloatDataType(::testing::get<1>(data.param)); + string str = absl::StrCat( + "activation_dims_", absl::StrJoin(spec.activation_dims, "x"), + "_activation_layout_", absl::StrJoin(spec.activation_layout, "_"), + "_kernel_dims_", absl::StrJoin(spec.kernel_dims, "x"), "_kernel_layout_", + absl::StrJoin(spec.kernel_layout, "_"), "_output_dims_", + absl::StrJoin(spec.output_dims, "x"), "_output_layout_", + absl::StrJoin(spec.output_layout, "_"), data_type); + // -1 indicates non-existence. + if (spec.stride != -1) { + absl::StrAppend(&str, "_lhs_dilation_", spec.lhs_dilate, "x1"); + } + + // Test names are not allowed to contain the '-' character. + absl::c_replace(str, '-', 'n'); + return str; +} + +string BuildHloTextGroupedConvolution2D(const GroupedConvolution2DSpec& spec, + bool use_bfloat16) { + const string data_type = GetFloatDataType(use_bfloat16); + if (spec.activation_dims[1] == 1 && spec.kernel_dims[1] == 2) { + // Check for outer dim. + return absl::StrFormat( + R"( + HloModule TensorFlowDepthwiseConv + + ENTRY main { + activation = %s[%s]{%s} parameter(0) + kernel = %s[%s]{%s} parameter(1) + ROOT conv = %s[%s]{%s} convolution(%s[%s]{%s} activation, %s[%s]{%s} kernel), + window={size=%dx%d pad=1_1x%d_%d rhs_dilate=1x%d}, dim_labels=b01f_01io->b01f, + feature_group_count=%d + } + )", + data_type, absl::StrJoin(spec.activation_dims, ","), + absl::StrJoin(spec.activation_layout, ","), data_type, + absl::StrJoin(spec.kernel_dims, ","), + absl::StrJoin(spec.kernel_layout, ","), data_type, + absl::StrJoin(spec.output_dims, ","), + absl::StrJoin(spec.output_layout, ","), data_type, + absl::StrJoin(spec.activation_dims, ","), + absl::StrJoin(spec.activation_layout, ","), data_type, + absl::StrJoin(spec.kernel_dims, ","), + absl::StrJoin(spec.kernel_layout, ","), spec.window, spec.window, + spec.window, spec.window, spec.window, spec.group_count); + + } else if (spec.stride == -1) { + // Check for basic, non-dilated cases. + return absl::StrFormat( + R"( + HloModule TensorFlowDepthwiseConv + + ENTRY main { + activation = %s[%s]{%s} parameter(0) + kernel = %s[%s]{%s} parameter(1) + ROOT conv = %s[%s]{%s} convolution(%s[%s]{%s} activation, %s[%s]{%s} kernel), + window={size=%dx%d}, dim_labels=b01f_01io->b01f, + feature_group_count=%d + } + )", + data_type, absl::StrJoin(spec.activation_dims, ","), + absl::StrJoin(spec.activation_layout, ","), data_type, + absl::StrJoin(spec.kernel_dims, ","), + absl::StrJoin(spec.kernel_layout, ","), data_type, + absl::StrJoin(spec.output_dims, ","), + absl::StrJoin(spec.output_layout, ","), data_type, + absl::StrJoin(spec.activation_dims, ","), + absl::StrJoin(spec.activation_layout, ","), data_type, + absl::StrJoin(spec.kernel_dims, ","), + absl::StrJoin(spec.kernel_layout, ","), spec.window, spec.window, + spec.group_count); + } else { + // Check for base dilations. + return absl::StrFormat( + R"( + HloModule TensorFlowDepthwiseConv + + ENTRY main { + activation = %s[%s]{%s} parameter(0) + kernel = %s[%s]{%s} parameter(1) + ROOT conv = %s[%s]{%s} convolution(%s[%s]{%s} activation, %s[%s]{%s} kernel), + window={size=%dx%d stride=%dx1 pad=%d_%dx0_0 lhs_dilate=%dx1}, + dim_labels=b01f_01io->b01f, feature_group_count=%d + } + )", + data_type, absl::StrJoin(spec.activation_dims, ","), + absl::StrJoin(spec.activation_layout, ","), data_type, + absl::StrJoin(spec.kernel_dims, ","), + absl::StrJoin(spec.kernel_layout, ","), data_type, + absl::StrJoin(spec.output_dims, ","), + absl::StrJoin(spec.output_layout, ","), data_type, + absl::StrJoin(spec.activation_dims, ","), + absl::StrJoin(spec.activation_layout, ","), data_type, + absl::StrJoin(spec.kernel_dims, ","), + absl::StrJoin(spec.kernel_layout, ","), spec.window, spec.window, + spec.stride, 0, 0, spec.lhs_dilate, spec.group_count); + } +} + +XLA_TEST_P(GroupedConvolution2DTest, DoIt) { + const GroupedConvolution2DSpec& spec = ::testing::get<0>(GetParam()); + bool use_bfloat16 = ::testing::get<1>(GetParam()); + const string hlo_text = BuildHloTextGroupedConvolution2D(spec, use_bfloat16); + + EXPECT_TRUE(RunAndCompare(hlo_text, ErrorSpec{0.01, 0.01}, + [](HloModule* module) -> Status { + BFloat16MixedPrecisionRemoval remover; + TF_RETURN_IF_ERROR(remover.Run(module).status()); + Despecializer despecializer; + return despecializer.Run(module).status(); + })); +} + +INSTANTIATE_TEST_CASE_P( + GroupedConvolution2DTestWithRandomIndices, GroupedConvolution2DTest, + ::testing::Combine(::testing::ValuesIn(GetConv2DTestCases()), + ::testing::Bool()), + GroupedConvolution2DTestDataToString); + +} // namespace +} // namespace xla diff --git a/tensorflow/compiler/xla/tests/hlo_test_base.cc b/tensorflow/compiler/xla/tests/hlo_test_base.cc index d8fa00272f..989a7c705a 100644 --- a/tensorflow/compiler/xla/tests/hlo_test_base.cc +++ b/tensorflow/compiler/xla/tests/hlo_test_base.cc @@ -99,6 +99,8 @@ void VerifiedHloModule::VerifyOrAddFailure(const string& message) { ADD_FAILURE() << "HloVerifier failed on module " << name() << (message.empty() ? "" : absl::StrCat(" (", message, ")")) << ": " << status; + LOG(ERROR) << "Contents of bad module:"; + XLA_LOG_LINES(tensorflow::ERROR, ToString()); } } @@ -140,14 +142,6 @@ std::unique_ptr HloTestBase::CreateNewVerifiedModule( allow_mixed_precision_in_hlo_verifier_); } -StatusOr> -HloTestBase::ParseAndReturnUnverifiedModule(absl::string_view hlo_text, - const HloModuleConfig& config) { - auto module = absl::make_unique(TestName(), config); - TF_RETURN_IF_ERROR(ParseHloString(hlo_text, module.get())); - return std::move(module); -} - StatusOr> HloTestBase::ParseAndReturnVerifiedModule(absl::string_view hlo_text, const HloModuleConfig& config) { diff --git a/tensorflow/compiler/xla/tests/hlo_test_base.h b/tensorflow/compiler/xla/tests/hlo_test_base.h index 366726d90b..1d1e7f4372 100644 --- a/tensorflow/compiler/xla/tests/hlo_test_base.h +++ b/tensorflow/compiler/xla/tests/hlo_test_base.h @@ -20,6 +20,7 @@ limitations under the License. #include #include +#include "absl/base/macros.h" #include "absl/types/optional.h" #include "absl/types/span.h" #include "tensorflow/compiler/xla/service/backend.h" @@ -100,6 +101,7 @@ class HloTestBase : public ::testing::Test { // // This returns a vanilla HloModule that doesn't run the HLO verifier on // destruction. + ABSL_DEPRECATED("Use CreateNewVerifiedModule instead.") std::unique_ptr CreateNewUnverifiedModule( const string& name = TestName()); @@ -108,12 +110,6 @@ class HloTestBase : public ::testing::Test { std::unique_ptr CreateNewVerifiedModule( const string& name = TestName()); - // Parses the given string and returns module as a vanilla, unverified - // HloModule. - StatusOr> ParseAndReturnUnverifiedModule( - absl::string_view hlo_text, - const HloModuleConfig& config = HloModuleConfig()); - // Parses the given string and returns module as a VerifiedHloModule. StatusOr> ParseAndReturnVerifiedModule( absl::string_view hlo_text, diff --git a/tensorflow/compiler/xla/tests/iota_test.cc b/tensorflow/compiler/xla/tests/iota_test.cc index 310f349592..65205f53dd 100644 --- a/tensorflow/compiler/xla/tests/iota_test.cc +++ b/tensorflow/compiler/xla/tests/iota_test.cc @@ -113,5 +113,26 @@ INSTANTIATE_TEST_CASE_P(IotaR3TestInstantiation, IotaR3Test, /*step=*/10), ::testing::Values(0, 1, 2))); +class IotaR3PredTest : public ClientLibraryTestBase, + public ::testing::WithParamInterface {}; + +TEST_P(IotaR3PredTest, DoIt) { + const auto element_type = PRED; + const int64 num_elements = 2; + const int64 iota_dim = GetParam(); + XlaBuilder builder(TestName() + "_" + PrimitiveType_Name(element_type)); + std::vector dimensions = {42, 19}; + dimensions.insert(dimensions.begin() + iota_dim, num_elements); + Iota(&builder, ShapeUtil::MakeShape(element_type, dimensions), iota_dim); + if (primitive_util::IsFloatingPointType(element_type)) { + ComputeAndCompare(&builder, {}, ErrorSpec{0.0001}); + } else { + ComputeAndCompare(&builder, {}); + } +} + +INSTANTIATE_TEST_CASE_P(IotaR3PredTestInstantiation, IotaR3PredTest, + ::testing::Values(0, 1, 2)); + } // namespace } // namespace xla diff --git a/tensorflow/compiler/xla/tests/replay_test.cc b/tensorflow/compiler/xla/tests/replay_test.cc index 5cf87e565b..34c7dc7c46 100644 --- a/tensorflow/compiler/xla/tests/replay_test.cc +++ b/tensorflow/compiler/xla/tests/replay_test.cc @@ -55,7 +55,8 @@ TEST_F(ReplayTest, TwoPlusTwoReplay) { client_->GetComputationShape(computation).ConsumeValueOrDie(); std::unique_ptr replayed_shape = client_->GetComputationShape(replayed).ConsumeValueOrDie(); - ASSERT_TRUE(protobuf_util::ProtobufEquals(*original_shape, *replayed_shape)); + ASSERT_TRUE(protobuf_util::ProtobufEquals(original_shape->ToProto(), + replayed_shape->ToProto())); // Run it. Literal literal = @@ -87,7 +88,8 @@ XLA_TEST_F(ReplayTest, XPlusYReplayWithParameters) { client_->GetComputationShape(computation).ConsumeValueOrDie(); std::unique_ptr replayed_shape = client_->GetComputationShape(replayed).ConsumeValueOrDie(); - ASSERT_TRUE(protobuf_util::ProtobufEquals(*original_shape, *replayed_shape)); + ASSERT_TRUE(protobuf_util::ProtobufEquals(original_shape->ToProto(), + replayed_shape->ToProto())); // Run it. std::unique_ptr x_data = @@ -133,7 +135,8 @@ TEST_F(ReplayTest, MapPlusTwoOverR1) { client_->GetComputationShape(computation).ConsumeValueOrDie(); std::unique_ptr replayed_shape = client_->GetComputationShape(replayed).ConsumeValueOrDie(); - ASSERT_TRUE(protobuf_util::ProtobufEquals(*original_shape, *replayed_shape)); + ASSERT_TRUE(protobuf_util::ProtobufEquals(original_shape->ToProto(), + replayed_shape->ToProto())); // Run it. Literal literal = diff --git a/tensorflow/compiler/xla/tests/reshape_test.cc b/tensorflow/compiler/xla/tests/reshape_test.cc index dedc95b5ae..298136002e 100644 --- a/tensorflow/compiler/xla/tests/reshape_test.cc +++ b/tensorflow/compiler/xla/tests/reshape_test.cc @@ -618,7 +618,8 @@ XLA_TEST_P(ReshapeTest, R4Dim0MinorLayoutToR2Dim0MajorLayout) { ExecutionOptions execution_options = execution_options_; *execution_options.mutable_shape_with_output_layout() = ShapeUtil::MakeShapeWithLayout(use_bfloat16() ? BF16 : F32, {2, 8}, - {1, 0}); + {1, 0}) + .ToProto(); Literal actual = client_ ->ExecuteAndTransfer(computation, {input.get()}, &execution_options) @@ -767,7 +768,8 @@ XLA_TEST_P(ReshapeTest, NoopReshape) { ExecutionOptions execution_options = execution_options_; *execution_options.mutable_shape_with_output_layout() = ShapeUtil::MakeShapeWithLayout(use_bfloat16() ? BF16 : F32, {7, 2, 3, 5}, - {2, 3, 0, 1}); + {2, 3, 0, 1}) + .ToProto(); Literal output_literal = client_ ->ExecuteAndTransfer(computation, {input_data.get()}, diff --git a/tensorflow/compiler/xla/tests/scatter_test.cc b/tensorflow/compiler/xla/tests/scatter_test.cc index 7e1f4aa0eb..32de0fdf78 100644 --- a/tensorflow/compiler/xla/tests/scatter_test.cc +++ b/tensorflow/compiler/xla/tests/scatter_test.cc @@ -129,6 +129,42 @@ ENTRY main { RunTest(hlo_text, &operand, &scatter_indices, &updates); } +XLA_TEST_F(ScatterTest, TensorFlowScatterV2_InversePermutation) { + const char* hlo_text = R"( +HloModule TensorFlowScatterV2 + +update_s32 (lhs: s32[], rhs: s32[]) -> s32[] { + lhs = s32[] parameter(0) + ROOT rhs = s32[] parameter(1) +} + +ENTRY main { + permutation = s32[3,4] parameter(0) + reshape = s32[3,4,1] reshape(permutation) + operand = s32[3,4] iota(), iota_dimension=1 + updates = s32[3,4,1,1] iota(), iota_dimension=1 + iota = s32[3,4,1] iota(), iota_dimension=0 + indices = s32[3,4,2] concatenate(iota, reshape), dimensions={2} + ROOT scatter = s32[3,4] scatter(operand, indices, updates), + to_apply=update_s32, + update_window_dims={2,3}, + inserted_window_dims={}, + scatter_dims_to_operand_dims={0,1}, + index_vector_dim=2 +} +)"; + Literal permutation = + LiteralUtil::CreateR2({{1, 3, 2, 0}, {3, 0, 2, 1}, {2, 3, 1, 0}}); + HloModuleConfig config; + config.set_debug_options(GetDebugOptionsForTest()); + TF_ASSERT_OK_AND_ASSIGN(std::unique_ptr module, + ParseHloString(hlo_text, config)); + auto actual = ExecuteAndTransfer(std::move(module), {&permutation}); + Literal expected = + LiteralUtil::CreateR2({{3, 0, 2, 1}, {1, 3, 2, 0}, {3, 2, 0, 1}}); + EXPECT_TRUE(LiteralTestUtil::Equal(expected, actual)); +} + XLA_TEST_F(ScatterTest, SimpleR4) { const char* hlo_text = R"( HloModule SimpleR4 diff --git a/tensorflow/compiler/xla/tests/test_utils.cc b/tensorflow/compiler/xla/tests/test_utils.cc index 2f18036ff4..eafa48ed7b 100644 --- a/tensorflow/compiler/xla/tests/test_utils.cc +++ b/tensorflow/compiler/xla/tests/test_utils.cc @@ -15,6 +15,7 @@ limitations under the License. #include +#include "absl/base/casts.h" #include "absl/memory/memory.h" #include "tensorflow/compiler/xla/literal_util.h" #include "tensorflow/compiler/xla/primitive_util.h" @@ -28,65 +29,113 @@ namespace xla { namespace { template -void PopulateWithRandomFloatingPointDataImpl(Literal* literal, - std::minstd_rand0* engine, - bool no_duplicates) { +void PopulateWithRandomFloatingPointData(Literal* literal, + std::minstd_rand0* engine) { + std::uniform_real_distribution generator(-0.1f, 0.2f); + for (FloatT& value : literal->data()) { + value = static_cast(generator(*engine)); + } +} + +template +void PopulateWithIntNext(Literal* literal); + +template <> +void PopulateWithIntNext(Literal* literal) { + // Duplicates may be generated if we don't have enough bits. + uint16 next_value = 0; + for (half& value : literal->data()) { + // Zero-out the MSB of the exponent to avoid Infs and NaNs, and put it into + // the sign bit. We could be less wasteful, but this is best-effort anyway. + uint16 exponent_msb = next_value & 0x4000; + value.x = (next_value & 0xBFFF) | (exponent_msb << 1); + next_value++; + } +} + +template <> +void PopulateWithIntNext(Literal* literal) { + // Duplicates may be generated if we don't have enough bits. + // Start at 0x80 rather than 0 to avoid denormals. + uint16 next_value = 0x80; + for (bfloat16& value : literal->data()) { + // Zero-out the MSB of the exponent to avoid Infs and NaNs, and put it into + // the sign bit. We could be less wasteful, but this is best-effort anyway. + uint16 exponent_msb = next_value & 0x4000; + value.value = (next_value & 0xBFFF) | (exponent_msb << 1); + next_value++; + } +} + +template +void PopulateWithNextAfter(Literal* literal) { + // Duplicates may be generated if the number of elements in the literal + // exceeds the number of positive values supported by the type. + float next_value = std::numeric_limits::min(); + for (float& value : literal->data()) { + value = next_value; + next_value = std::nextafter(next_value, std::numeric_limits::max()); + } +} + +template ::value || + std::is_same::value, + int>::type = 0> +void PopulateWithNoDuplicateData(Literal* literal, std::minstd_rand0* engine) { + PopulateWithIntNext(literal); + std::shuffle(literal->data().begin(), literal->data().end(), + *engine); +} + +template ::value && + !std::is_same::value, + int>::type = 0> +void PopulateWithNoDuplicateData(Literal* literal, std::minstd_rand0* engine) { + PopulateWithNextAfter(literal); + std::shuffle(literal->data().begin(), literal->data().end(), + *engine); +} + +template +void PopulateWithFloatingPointData(Literal* literal, std::minstd_rand0* engine, + bool no_duplicates) { CHECK(engine != nullptr); CHECK_EQ(literal->shape().element_type(), primitive_util::NativeToPrimitiveType()); if (no_duplicates) { - // Duplicates may be generated if the number of elements in the literal - // exceeds the number of positive values supported by the type. - FloatT next_value = std::numeric_limits::min(); - for (FloatT& value : literal->data()) { - value = next_value; - next_value = - std::nextafter(next_value, std::numeric_limits::max()); - } - std::shuffle(literal->data().begin(), literal->data().end(), - *engine); + PopulateWithNoDuplicateData(literal, engine); } else { - std::uniform_real_distribution generator(-0.1f, 0.2f); - for (FloatT& value : literal->data()) { - value = static_cast(generator(*engine)); - } + PopulateWithRandomFloatingPointData(literal, engine); } } -template -void PopulateWithRandomFloatingPointData(Literal* literal, +template <> +void PopulateWithFloatingPointData(Literal* literal, std::minstd_rand0* engine, bool no_duplicates) { CHECK(engine != nullptr); - PopulateWithRandomFloatingPointDataImpl(literal, engine, - no_duplicates); -} - -template <> -void PopulateWithRandomFloatingPointData(Literal* literal, - std::minstd_rand0* engine, - bool no_duplicates) { - // no_duplicates is ignored for half types. Unique values can only be - // generated for arrays with fewer than ~2**16 elements and no_duplicates is - // best-effort anyway. - CHECK(engine != nullptr); - std::uniform_real_distribution generator(-0.1f, 0.2f); - for (half& value : literal->data()) { - value = static_cast(generator(*engine)); + CHECK_EQ(literal->shape().element_type(), + primitive_util::NativeToPrimitiveType()); + if (no_duplicates) { + PopulateWithNoDuplicateData(literal, engine); + } else { + PopulateWithRandomFloatingPointData(literal, engine); } } template <> -void PopulateWithRandomFloatingPointData(Literal* literal, - std::minstd_rand0* engine, - bool no_duplicates) { - // no_duplicates is ignored for bfloat types. Unique values can only be - // generated for arrays with fewer than ~2**16 elements and no_duplicates is - // best-effort anyway. +void PopulateWithFloatingPointData(Literal* literal, + std::minstd_rand0* engine, + bool no_duplicates) { CHECK(engine != nullptr); - std::uniform_real_distribution generator(-0.1f, 0.2f); - for (bfloat16& value : literal->data()) { - value = static_cast(generator(*engine)); + CHECK_EQ(literal->shape().element_type(), + primitive_util::NativeToPrimitiveType()); + if (no_duplicates) { + PopulateWithNoDuplicateData(literal, engine); + } else { + PopulateWithRandomFloatingPointData(literal, engine); } } @@ -135,20 +184,16 @@ StatusOr MakeFakeLiteralInternal(const Shape& shape, Literal literal(shape); switch (shape.element_type()) { case BF16: - PopulateWithRandomFloatingPointData(&literal, engine, - no_duplicates); + PopulateWithFloatingPointData(&literal, engine, no_duplicates); break; case F16: - PopulateWithRandomFloatingPointData(&literal, engine, - no_duplicates); + PopulateWithFloatingPointData(&literal, engine, no_duplicates); break; case F32: - PopulateWithRandomFloatingPointData(&literal, engine, - no_duplicates); + PopulateWithFloatingPointData(&literal, engine, no_duplicates); break; case F64: - PopulateWithRandomFloatingPointData(&literal, engine, - no_duplicates); + PopulateWithFloatingPointData(&literal, engine, no_duplicates); break; case S8: PopulateWithRandomIntegralData(&literal, engine, no_duplicates); diff --git a/tensorflow/compiler/xla/tests/test_utils_test.cc b/tensorflow/compiler/xla/tests/test_utils_test.cc index e066b3f4f2..e8f5d7a9a7 100644 --- a/tensorflow/compiler/xla/tests/test_utils_test.cc +++ b/tensorflow/compiler/xla/tests/test_utils_test.cc @@ -175,5 +175,28 @@ ENTRY %sort.148.1589 (parameter.0: s32[1048576], parameter.1: s32[1048576]) -> ( } } +XLA_TEST_F(TestUtilsTest, NoDuplicatesBfloat16) { + // Inputs which are sort keys in key/value sorts should have no duplicates. + auto module = ParseHloString(R"( +HloModule sort, is_scheduled=true + +ENTRY %sort. (parameter.0: bf16[2,1452], parameter.1: s32[2,1452]) -> (bf16[2,1452], s32[2,1452]) { + %parameter.0 = bf16[2,1452]{1,0} parameter(0) + %parameter.1 = s32[2,1452]{1,0} parameter(1) + ROOT %sort = (bf16[2,1452]{1,0}, s32[2,1452]{1,0}) sort(bf16[2,1452]{1,0} %parameter.0, s32[2,1452]{1,0} %parameter.1), dimensions={1} +} +)") + .ValueOrDie(); + TF_ASSERT_OK_AND_ASSIGN(std::vector args, + MakeFakeArguments(module.get())); + ASSERT_EQ(args.size(), 2); + const Literal& key_arg = args[0]; + + absl::flat_hash_set key_set; + for (const bfloat16& value : key_arg.data()) { + EXPECT_TRUE(key_set.insert(absl::bit_cast(value)).second); + } +} + } // namespace } // namespace xla diff --git a/tensorflow/compiler/xla/tests/token_hlo_test.cc b/tensorflow/compiler/xla/tests/token_hlo_test.cc index a2b7c26331..601c6b0693 100644 --- a/tensorflow/compiler/xla/tests/token_hlo_test.cc +++ b/tensorflow/compiler/xla/tests/token_hlo_test.cc @@ -16,6 +16,7 @@ limitations under the License. #include #include "absl/strings/str_cat.h" +#include "tensorflow/compiler/xla/service/hlo_parser.h" #include "tensorflow/compiler/xla/service/hlo_verifier.h" #include "tensorflow/compiler/xla/tests/hlo_test_base.h" #include "tensorflow/compiler/xla/tests/test_macros.h" @@ -108,26 +109,6 @@ XLA_TEST_F(TokenHloTest, InvalidTupleTokenShapedEntryParameter) { ::testing::HasSubstr("Entry parameter 0 is or contains a token shape")); } -XLA_TEST_F(TokenHloTest, InvalidOperandToTokenInstruction) { - std::unique_ptr module = CreateNewUnverifiedModule(); - auto builder = HloComputation::Builder(TestName()); - auto param = builder.AddInstruction( - HloInstruction::CreateParameter(0, ShapeUtil::MakeShape(F32, {}), "p0")); - builder.AddInstruction(HloInstruction::CreateAfterAll({param})); - builder.AddInstruction( - HloInstruction::CreateConstant(LiteralUtil::CreateR0(123))); - module->AddEntryComputation(builder.Build()); - - Status status = - HloVerifier(/*layout_sensitive=*/false, /*allow_mixed_precision=*/false) - .Run(module.get()) - .status(); - ASSERT_IS_NOT_OK(status); - EXPECT_THAT(status.error_message(), - ::testing::HasSubstr( - "Operands of token instructions must be TOKEN types")); -} - XLA_TEST_F(TokenHloTest, TokenInWhileLoop) { // Thread a token around a while loop. Token is created and consumed by a // AfterAll instruction in the while body. @@ -220,5 +201,95 @@ ENTRY %TokenInConditional (param.3: pred[]) -> s32[] { } } +XLA_TEST_F(TokenHloTest, AddDependency) { + string module_string = R"( +HloModule AddDependency, is_scheduled=true + +// Computes (p0 + 42) * (-p1) +// where there is a dependency from the add to the negation using a token +// with after-all and add-dependency instructions. +ENTRY %AddDependency (p0: f32[], p1: f32[]) -> f32[] { + %p0 = f32[] parameter(0) + %p1 = f32[] parameter(1) + + %forty_two = f32[] constant(42.0) + %add = f32[] add(f32[] %p0, f32[] %forty_two) + %token = token[] after-all(f32[] %add) + %p1_after_token = f32[] add-dependency(f32[] %p1, token[] %token) + %neg = f32[] negate(f32[] %p1_after_token) + ROOT %product = f32[] multiply(f32[] %add, f32[] %neg) +} +)"; + TF_ASSERT_OK_AND_ASSIGN( + std::unique_ptr module, + ParseHloString(module_string, GetModuleConfigForTest())); + auto p0 = LiteralUtil::CreateR0(10.0); + auto p1 = LiteralUtil::CreateR0(3.0); + auto expected = LiteralUtil::CreateR0(-156.0); + EXPECT_EQ(expected, ExecuteNoHloPasses(std::move(module), {&p0, &p1})); +} + +XLA_TEST_F(TokenHloTest, AddDependencyOfConstant) { + string module_string = R"( +HloModule AddDependencyOfConstant, is_scheduled=true + +ENTRY %AddDependency (p0: f32[]) -> f32[] { + %p0 = f32[] parameter(0) + %forty_two = f32[] constant(42.0) + %token = token[] after-all(f32[] %p0) + %forty_two_after_token = f32[] add-dependency(f32[] %forty_two, token[] %token) + ROOT %product = f32[] multiply(f32[] %p0, f32[] %forty_two_after_token) +} +)"; + TF_ASSERT_OK_AND_ASSIGN( + std::unique_ptr module, + ParseHloString(module_string, GetModuleConfigForTest())); + auto p0 = LiteralUtil::CreateR0(10.0); + auto expected = LiteralUtil::CreateR0(420.0); + EXPECT_EQ(expected, ExecuteNoHloPasses(std::move(module), {&p0})); +} + +XLA_TEST_F(TokenHloTest, AddDependencyAsRoot) { + string module_string = R"( +HloModule AddDependencyAsRoot, is_scheduled=true +ENTRY %AddDependency (p: f32[3]) -> f32[3] { + %p = f32[3] parameter(0) + %neg = f32[3] negate(f32[3] %p) + %token = token[] after-all() + ROOT %add_dep = f32[3] add-dependency(f32[3] %neg, token[] %token) +} +)"; + TF_ASSERT_OK_AND_ASSIGN( + std::unique_ptr module, + ParseHloString(module_string, GetModuleConfigForTest())); + auto input = LiteralUtil::CreateR1({1.0, 3.0, 7.0}); + auto expected = LiteralUtil::CreateR1({-1.0, -3.0, -7.0}); + EXPECT_EQ(expected, ExecuteNoHloPasses(std::move(module), {&input})); +} + +XLA_TEST_F(TokenHloTest, TupleShapedAddDependency) { + string module_string = R"( +HloModule TupleShapedAddDependency, is_scheduled=true +ENTRY %TupleShapedAddDependency (p0: f32[3], p1: f32[3]) -> f32[3] { + %p0 = f32[3] parameter(0) + %p1 = f32[3] parameter(1) + %forty_two = f32[] constant(42.0) + %token = token[] after-all() + %tuple = (f32[3], token[], f32[3], f32[]) tuple(f32[3] %p0, token[] %token, f32[3] %p1, f32[] %forty_two) + %add_dep = (f32[3], token[], f32[3], f32[]) add-dependency((f32[3], token[], f32[3], f32[]) %tuple, token[] %token) + %elem0 = f32[3] get-tuple-element((f32[3], token[], f32[3], f32[]) %add_dep), index=0 + %elem2 = f32[3] get-tuple-element((f32[3], token[], f32[3], f32[]) %add_dep), index=2 + ROOT %diff = f32[3] subtract(f32[3] %elem0, f32[3] %elem2) +} +)"; + TF_ASSERT_OK_AND_ASSIGN( + std::unique_ptr module, + ParseHloString(module_string, GetModuleConfigForTest())); + auto p0 = LiteralUtil::CreateR1({3.0, 3.0, 47.0}); + auto p1 = LiteralUtil::CreateR1({1.0, -2.0, 2.0}); + auto expected = LiteralUtil::CreateR1({2.0, 5.0, 45.0}); + EXPECT_EQ(expected, ExecuteNoHloPasses(std::move(module), {&p0, &p1})); +} + } // namespace } // namespace xla diff --git a/tensorflow/compiler/xla/tests/xla_hlo_profile_test.cc b/tensorflow/compiler/xla/tests/xla_hlo_profile_test.cc index ca036f1ae0..e57d072a06 100644 --- a/tensorflow/compiler/xla/tests/xla_hlo_profile_test.cc +++ b/tensorflow/compiler/xla/tests/xla_hlo_profile_test.cc @@ -157,10 +157,12 @@ void ExecuteAndFetchProfile(string* profile_output, LocalClient* client, TF_ASSERT_OK(transfer_manager->TransferLiteralToDevice( stream_ptr.get(), Literal::CreateFromShape(rhs_arg_shape), rhs_arg)); + ExecutableBuildOptions build_options; + build_options.mutable_debug_options()->set_xla_hlo_profile(true); TF_ASSERT_OK_AND_ASSIGN( std::unique_ptr local_executable, client->Compile(computation, {&lhs_arg_shape, &rhs_arg_shape}, - ExecutableBuildOptions().set_hlo_profile(true))); + build_options)); Executable* executable = local_executable->executable(); HloExecutionProfile hlo_execution_profile( @@ -208,7 +210,7 @@ XLA_TEST_F(HloProfileTest, ProfileSingleComputation) { string profile_output; ExecuteAndFetchProfile(&profile_output, client, computation, lhs_shape, rhs_shape); - + VLOG(4) << "Profile Output:\n" << profile_output; std::vector profile_output_lines = absl::StrSplit(profile_output, '\n'); diff --git a/tensorflow/compiler/xla/tools/replay_computation.cc b/tensorflow/compiler/xla/tools/replay_computation.cc index 47be9f5adf..ff2c339992 100644 --- a/tensorflow/compiler/xla/tools/replay_computation.cc +++ b/tensorflow/compiler/xla/tools/replay_computation.cc @@ -82,13 +82,17 @@ struct Options { std::unique_ptr CompileExecutable(const HloSnapshot& module, LocalClient* client) { XlaComputation computation(module.hlo().hlo_module()); - std::vector argument_layouts; - for (const auto& param : + std::vector argument_layouts; + argument_layouts.reserve( + computation.proto().host_program_shape().parameters_size()); + std::vector argument_layout_ptrs; + for (const ShapeProto& param : computation.proto().host_program_shape().parameters()) { - argument_layouts.push_back(¶m); + argument_layouts.push_back(Shape(param)); + argument_layout_ptrs.push_back(&argument_layouts.back()); } return client - ->Compile(computation, argument_layouts, ExecutableBuildOptions()) + ->Compile(computation, argument_layout_ptrs, ExecutableBuildOptions()) .ValueOrDie(); } @@ -149,7 +153,7 @@ StatusOr ReplayComputation(const HloSnapshot& module, << "--generate_fake_infeed only works if the model has 0 or 1 " "infeed ops, but this one has >= 2."; provide_infeed = true; - infeed_shape = instruction.shape(); + infeed_shape = Shape(instruction.shape()); LOG(INFO) << "Generating fake infeed shape for inferred shape: " << ShapeUtil::HumanString(infeed_shape); } @@ -315,9 +319,10 @@ int RealMain(absl::Span args, const Options& opts) { if (snapshot.has_result()) { Literal literal = Literal::CreateFromProto(snapshot.result()).ConsumeValueOrDie(); - fprintf(stdout, "was %s:%s\n", - ShapeUtil::HumanString(snapshot.result().shape()).c_str(), - literal.ToString().c_str()); + fprintf( + stdout, "was %s:%s\n", + ShapeUtil::HumanString(Shape(snapshot.result().shape())).c_str(), + literal.ToString().c_str()); } } } diff --git a/tensorflow/compiler/xla/util.h b/tensorflow/compiler/xla/util.h index 8ce7416474..6722641e9d 100644 --- a/tensorflow/compiler/xla/util.h +++ b/tensorflow/compiler/xla/util.h @@ -152,6 +152,13 @@ static inline absl::Span AsInt64Slice( slice.size()); } +// TODO(b/29771030): This nop overload was added to simplify the migration of +// Shape from a proto to a C++ class. Remove after class has been migrated. +static inline absl::Span AsInt64Slice( + absl::Span slice) { + return slice; +} + // As above, but for uint64 types. static inline absl::Span AsUInt64Slice( const tensorflow::protobuf::RepeatedField& v) { @@ -387,6 +394,19 @@ T CeilOfRatio(T dividend, T divisor) { return tensorflow::MathUtil::CeilOfRatio(dividend, divisor); } +template +std::vector ElementWiseCeilOfRatio(absl::Span dividends, + absl::Span divisors) { + std::vector ceil_of_ratios; + CHECK_EQ(dividends.size(), divisors.size()); + ceil_of_ratios.reserve(dividends.size()); + absl::c_transform(dividends, divisors, std::back_inserter(ceil_of_ratios), + [](const T dividend, const T divisor) { + return CeilOfRatio(dividend, divisor); + }); + return ceil_of_ratios; +} + // Rounds the value up to a multiple of the divisor by first calling CeilOfRatio // then multiplying by the divisor. For example: RoundUpToNearest(13, 8) => 16 template diff --git a/tensorflow/compiler/xla/window_util.cc b/tensorflow/compiler/xla/window_util.cc index 8ea8dbab25..f113a705b4 100644 --- a/tensorflow/compiler/xla/window_util.cc +++ b/tensorflow/compiler/xla/window_util.cc @@ -185,6 +185,17 @@ bool HasWindowReversal(const Window& window) { return false; } +bool AllOrNoneReversed(const Window& window) { + if (window.dimensions().size() == 0) { + return true; + } + bool reversed = window.dimensions()[0].window_reversal(); + return std::all_of(window.dimensions().begin(), window.dimensions().end(), + [&](const WindowDimension& dim) { + return dim.window_reversal() == reversed; + }); +} + bool HasDilation(const Window& window) { return HasBaseDilation(window) || HasWindowDilation(window); } diff --git a/tensorflow/compiler/xla/window_util.h b/tensorflow/compiler/xla/window_util.h index 1fb9e855fc..099d7ecdd5 100644 --- a/tensorflow/compiler/xla/window_util.h +++ b/tensorflow/compiler/xla/window_util.h @@ -56,6 +56,7 @@ bool HasWindowDilation(const Window& window); bool HasDilation(const Window& window); bool HasWindowReversal(const Window& window); +bool AllOrNoneReversed(const Window& window); // Returns true if the given logical dimension is inactive in the sense that it // has window bound 1, no striding and no padding. diff --git a/tensorflow/compiler/xla/xla.proto b/tensorflow/compiler/xla/xla.proto index 28df3b03f3..bdeb1728fa 100644 --- a/tensorflow/compiler/xla/xla.proto +++ b/tensorflow/compiler/xla/xla.proto @@ -193,7 +193,11 @@ message DebugOptions { // - Assuming that operations never produce or consume NaN or +/- Inf. // - Assuming that +0 and -0 are indistinguishable. bool xla_cpu_enable_fast_math = 99; - bool xla_gpu_enable_fast_math = 100; + + // When true we lower the Minimum and Maximum hlos in the GPU backend such + // that Min(NotNaN, NaN) = Min(NaN, NotNaN) = NotNaN. In other words, if flag + // this is true we don't propagate NaNs through Min and Max. + bool xla_gpu_enable_fast_min_max = 100; // Crashes the program when any kind of verification fails, instead of just // logging the failures. One example is cross checking of convolution results @@ -224,7 +228,7 @@ message ExecutionOptions { // may be faster when using this layout. // // We use a Shape here to accommodate computations that return a tuple. - Shape shape_with_output_layout = 2; + ShapeProto shape_with_output_layout = 2; // Used to seed random-number generators used in this computation. If this is // 0, we generate a seed ourselves. @@ -253,7 +257,7 @@ message TransferToClientRequest { // This optional field directs the service to return the literal in this // layout. A shape is used to hold the layout to accommodate tuples. - Shape shape_with_layout = 2; + ShapeProto shape_with_layout = 2; } message TransferToClientResponse { @@ -281,7 +285,7 @@ message TransferToInfeedResponse { message TransferFromOutfeedRequest { // This optional field directs the service to return the literal in this // layout. A shape is used to hold the layout to accommodate tuples. - Shape shape_with_layout = 1; + ShapeProto shape_with_layout = 1; int64 replica_id = 2; DeviceHandle device_handle = 3; @@ -332,7 +336,7 @@ message CompileRequest { // The layouts of the input arguments. If not set, the default layout will be // used. Although the real arguments are not needed in compilation, the // layouts of the arguments can affect the compilation. - repeated Shape input_shape_with_layout = 3; + repeated ShapeProto input_shape_with_layout = 3; } message CompileResponse { @@ -406,7 +410,7 @@ message LoadDataRequest { string columnio_field = 2; // Individual element shape, excluding rows. - Shape element_shape = 3; + ShapeProto element_shape = 3; // Warning: ColumnIO does not support random-access, so use offset with // caution in performance-critical scenarios. @@ -422,7 +426,7 @@ message LoadDataRequest { message LoadDataResponse { GlobalDataHandle data = 1; - Shape data_shape = 2; + ShapeProto data_shape = 2; int64 available_rows = 3; int64 rows_loaded = 4; int64 nanoseconds = 5; @@ -433,7 +437,7 @@ message GetShapeRequest { } message GetShapeResponse { - Shape shape = 1; + ShapeProto shape = 1; } message UnpackRequest { diff --git a/tensorflow/compiler/xla/xla_data.proto b/tensorflow/compiler/xla/xla_data.proto index 683ccc40f1..85ec83437a 100644 --- a/tensorflow/compiler/xla/xla_data.proto +++ b/tensorflow/compiler/xla/xla_data.proto @@ -108,6 +108,16 @@ enum Format { SPARSE = 2; } +// Describes a tile used in tiling-based layout. Refer to +// g3doc/layout_with_tiling.md for details about tiling-based layout. +message Tile { + // Number of elements in each dimension of the tile. It's ordered from the + // most major dimension of the tile to the most minor dimension of the tile. + // The dimensions correspond to a suffix of the dimensions of the shape being + // tiled. + repeated int64 dimensions = 1; +} + // A layout describes how the array is placed in (1D) memory space. This // includes the minor-to-major ordering of dimensions within a shape. // @@ -138,6 +148,20 @@ message Layout { // memory. This field must be unset unless the format is SPARSE. int64 max_sparse_elements = 5; + // A sequence of tiles, starting from the tile that's applied first to the + // Shape. + // + // TODO(b/119839262): implement tiling in each backend or add Unimplemented + // error. + repeated Tile tiles = 6; + + // Bit size of each element. If the size is bigger than what the element + // type requires, the value is stored in the least significant + // bits and the additional most significant bits are filled with 0's. + // + // TODO(b/119839262): implement in each backend or add Unimplemented error. + int64 element_size_in_bits = 7; + // Important: if any field is added, be sure to modify ShapeUtil::Equal() and // LayoutUtil::Hash appropriately to account for the new field. } @@ -154,7 +178,7 @@ message Layout { // See the XLA documentation for more information on shapes and layouts. // // LINT.IfChange -message Shape { +message ShapeProto { reserved 1; reserved "rank"; @@ -169,7 +193,7 @@ message Shape { repeated int64 dimensions = 3; // For tuples only, the shapes of constitutent shapes in the tuple sequence. - repeated Shape tuple_shapes = 4; + repeated ShapeProto tuple_shapes = 4; // The layout used to back this shape. Layout layout = 5; @@ -183,9 +207,9 @@ message Shape { // Shape of the parameters and output of a computation (like a traditional // function signature). -message ProgramShape { - repeated Shape parameters = 1; - Shape result = 2; +message ProgramShapeProto { + repeated ShapeProto parameters = 1; + ShapeProto result = 2; repeated string parameter_names = 3; } @@ -320,7 +344,7 @@ message DeviceAssignmentProto { // Transfers to/from the client are encoded in literal form, and the structure // of the repeated fields is implied by the shape. message LiteralProto { - Shape shape = 1; + ShapeProto shape = 1; repeated bool preds = 2; bytes s8s = 15; bytes u8s = 3; @@ -521,7 +545,7 @@ message OpSharding { } Type type = 1; // The shape of the sharded tile. - Shape tile_shape = 2; + ShapeProto tile_shape = 2; // The shape of the tile assignment tensor - this must be the same rank as // tile_shape and the product of its dimensions must equal // tile_assignment_devices.size(). diff --git a/tensorflow/compiler/xrt/BUILD b/tensorflow/compiler/xrt/BUILD index 2ff97914f8..2dae746d03 100644 --- a/tensorflow/compiler/xrt/BUILD +++ b/tensorflow/compiler/xrt/BUILD @@ -22,6 +22,7 @@ xla_proto_library( deps = [ "//tensorflow/compiler/tf2xla:host_compute_metadata_proto", "//tensorflow/compiler/xla:xla_data_proto", + "//tensorflow/compiler/xla:xla_proto", "//tensorflow/compiler/xla/service:hlo_proto", ], ) @@ -32,20 +33,25 @@ cc_library( "xrt_compilation_cache.cc", "xrt_device.cc", "xrt_state.cc", + "xrt_util.cc", ], hdrs = [ "xrt_compilation_cache.h", "xrt_device.h", "xrt_state.h", + "xrt_util.h", ], deps = [ "//tensorflow/compiler/jit:xla_device", "//tensorflow/compiler/tf2xla:xla_compiler", + "//tensorflow/compiler/xla:debug_options_flags", "//tensorflow/compiler/xla:literal", "//tensorflow/compiler/xla:shape_util", "//tensorflow/compiler/xla:status_macros", "//tensorflow/compiler/xla:statusor", + "//tensorflow/compiler/xla:types", "//tensorflow/compiler/xla:xla_data_proto", + "//tensorflow/compiler/xla:xla_proto", "//tensorflow/compiler/xla/client:local_client", "//tensorflow/compiler/xla/service:backend", "//tensorflow/compiler/xla/service:device_memory_allocator", diff --git a/tensorflow/compiler/xrt/kernels/xrt_compile_ops.cc b/tensorflow/compiler/xrt/kernels/xrt_compile_ops.cc index dc62cf7a6b..2ccdf0f02d 100644 --- a/tensorflow/compiler/xrt/kernels/xrt_compile_ops.cc +++ b/tensorflow/compiler/xrt/kernels/xrt_compile_ops.cc @@ -33,6 +33,7 @@ limitations under the License. #include "tensorflow/compiler/xrt/xrt.pb.h" #include "tensorflow/compiler/xrt/xrt_compilation_cache.h" #include "tensorflow/compiler/xrt/xrt_device.h" +#include "tensorflow/compiler/xrt/xrt_util.h" #include "tensorflow/core/framework/op_kernel.h" #include "tensorflow/core/framework/resource_mgr.h" #include "tensorflow/core/framework/tensor.h" @@ -108,19 +109,26 @@ Status XRTCompileOp::Compile(OpKernelContext* ctx, TF_ASSIGN_OR_RETURN(xla::XlaComputation computation, client->LoadSnapshot(computation_proto.hlo_snapshot())); - std::vector argument_layouts( + std::vector argument_layouts( + config.program_shape().parameters_size()); + std::vector argument_layout_ptrs( config.program_shape().parameters_size()); for (int i = 0; i < config.program_shape().parameters_size(); ++i) { - argument_layouts[i] = &config.program_shape().parameters(i); + argument_layouts[i] = xla::Shape(config.program_shape().parameters(i)); + argument_layout_ptrs[i] = &argument_layouts[i]; } xla::ExecutableBuildOptions build_options; build_options.set_device_ordinal(client->default_device_ordinal()); - build_options.set_result_layout(config.program_shape().result()); + build_options.set_result_layout(xla::Shape(config.program_shape().result())); build_options.set_device_allocator(device_ref.backend()->memory_allocator()); + if (config.has_debug_options()) { + *build_options.mutable_debug_options() = + BuildXlaDebugOptions(config.debug_options()); + } VLOG(1) << "Building executable"; auto compile_result = - client->Compile(computation, argument_layouts, build_options); + client->Compile(computation, argument_layout_ptrs, build_options); if (!compile_result.ok()) { return compile_result.status(); } @@ -174,11 +182,12 @@ void XRTCompileOp::Compute(OpKernelContext* ctx) { ctx->set_output(0, handle_output); xla::LocalExecutable* executable = entry->get().get_executable(); - xla::ProgramShape program_shape = executable->executable() - ->module() - .config() - .entry_computation_layout() - .ComputeProgramShape(); + xla::ProgramShapeProto program_shape = executable->executable() + ->module() + .config() + .entry_computation_layout() + .ComputeProgramShape() + .ToProto(); Tensor program_shape_output(DT_STRING, TensorShape({1})); program_shape_output.vec()(0) = program_shape.SerializeAsString(); ctx->set_output(1, program_shape_output); diff --git a/tensorflow/compiler/xrt/kernels/xrt_execute_op.cc b/tensorflow/compiler/xrt/kernels/xrt_execute_op.cc index 8c6191ddc0..751329eefc 100644 --- a/tensorflow/compiler/xrt/kernels/xrt_execute_op.cc +++ b/tensorflow/compiler/xrt/kernels/xrt_execute_op.cc @@ -228,14 +228,35 @@ Status XRTExecuteOp::DoWork(OpKernelContext* context) { TF_RETURN_IF_ERROR(XRTTupleAllocation::CreateFromBuffer( shaped_buffer, device_ref.backend(), device_ref.device_ordinal(), &output_tuple)); - - Tensor* output_tensor; - TF_RETURN_IF_ERROR( - context->allocate_output(0, TensorShape({}), &output_tensor)); - int64 key; - TF_RETURN_IF_ERROR(output_tuple->Intern(rm, &key)); - output_tensor->scalar()() = key; - + if (config_proto.return_exploded_tuple() && + xla::ShapeUtil::IsTuple(output_tuple->on_device_shape())) { + int64 tuple_element_count = + xla::ShapeUtil::TupleElementCount(output_tuple->on_device_shape()); + Tensor* output_tensor; + TF_RETURN_IF_ERROR(context->allocate_output( + 0, TensorShape({tuple_element_count}), &output_tensor)); + + for (int64 i = 0; i < tuple_element_count; ++i) { + xla::ShapeIndex shape_index; + shape_index.push_back(i); + + XRTTupleAllocation* suballocation; + TF_RETURN_IF_ERROR(XRTTupleAllocation::MakeSubBuffer( + output_tuple, shape_index, &suballocation, + /*alias_parent_allocation=*/false)); + int64 key; + TF_RETURN_IF_ERROR(suballocation->Intern(rm, &key)); + output_tensor->vec()(i) = key; + } + output_tuple->Unref(); + } else { + Tensor* output_tensor; + TF_RETURN_IF_ERROR( + context->allocate_output(0, TensorShape({}), &output_tensor)); + int64 key; + TF_RETURN_IF_ERROR(output_tuple->Intern(rm, &key)); + output_tensor->scalar()() = key; + } return Status::OK(); } diff --git a/tensorflow/compiler/xrt/kernels/xrt_state_ops.cc b/tensorflow/compiler/xrt/kernels/xrt_state_ops.cc index ffea592491..3258286c10 100644 --- a/tensorflow/compiler/xrt/kernels/xrt_state_ops.cc +++ b/tensorflow/compiler/xrt/kernels/xrt_state_ops.cc @@ -87,6 +87,19 @@ REGISTER_KERNEL_BUILDER(Name("XRTReadLiteral") .HostMemory("literal"), XRTReadLiteralOp); +REGISTER_KERNEL_BUILDER(Name("XRTWriteLiteral") + .Device(DEVICE_XLA_GPU) + .HostMemory("handle") + .HostMemory("literal") + .HostMemory("output_handle"), + XRTWriteLiteralOp); +REGISTER_KERNEL_BUILDER(Name("XRTWriteLiteral") + .Device(DEVICE_XLA_CPU) + .HostMemory("handle") + .HostMemory("literal") + .HostMemory("output_handle"), + XRTWriteLiteralOp); + REGISTER_KERNEL_BUILDER(Name("XRTReadLiteralAndRelease") .Device(DEVICE_XLA_GPU) .HostMemory("handle") diff --git a/tensorflow/compiler/xrt/kernels/xrt_state_ops.h b/tensorflow/compiler/xrt/kernels/xrt_state_ops.h index 54b06558ad..26a58fa42d 100644 --- a/tensorflow/compiler/xrt/kernels/xrt_state_ops.h +++ b/tensorflow/compiler/xrt/kernels/xrt_state_ops.h @@ -393,6 +393,56 @@ class XRTReadLiteralOp : public OpKernel { } }; +// Op that writes a new literal value into device-resident memory. +template +class XRTWriteLiteralOp : public OpKernel { + public: + explicit XRTWriteLiteralOp(OpKernelConstruction* ctx) : OpKernel(ctx) {} + ~XRTWriteLiteralOp() override = default; + XRTWriteLiteralOp(const XRTWriteLiteralOp&) = delete; + XRTWriteLiteralOp& operator=(const XRTWriteLiteralOp&) = delete; + + void Compute(OpKernelContext* ctx) override { + VLOG(1) << "XRTWriteLiteralOp::Compute"; + + const Tensor& handle_tensor = ctx->input(0); + OP_REQUIRES( + ctx, TensorShapeUtils::IsScalar(handle_tensor.shape()), + errors::Internal("computation input should be an int64 scalar")); + int64 allocation_handle = handle_tensor.scalar()(); + + const Tensor& literal_info = ctx->input(1); + OP_REQUIRES(ctx, TensorShapeUtils::IsScalar(literal_info.shape()), + errors::Internal("literal input should be a string scalar")); + xla::LiteralProto literal_proto; + OP_REQUIRES(ctx, + literal_proto.ParseFromString(literal_info.scalar()()), + errors::InvalidArgument( + "Unable to parse allocation input to LiteralProto")); + xla::Literal literal; + OP_REQUIRES_OK(ctx, XRTStateHelpers::MakeLiteral(literal_proto, &literal)); + + ResourceMgr* rm; + OP_REQUIRES_OK(ctx, DeviceAccessor::GetResourceManager(ctx, &rm)); + + XRTTupleAllocation* allocation; + OP_REQUIRES_OK( + ctx, XRTTupleAllocation::Lookup(rm, allocation_handle, &allocation)); + core::ScopedUnref allocation_unref(allocation); + // We are guaranteed that the underlying device object won't be deleted out + // from under us, while the ScopedRef is live. + typename DeviceAccessor::ScopedRef device_ref; + OP_REQUIRES_OK(ctx, DeviceAccessor::InitScopedRef( + ctx, allocation->device_ordinal(), &device_ref)); + OP_REQUIRES_OK(ctx, + allocation->WriteLiteral(device_ref.backend(), literal)); + + Tensor output(DT_INT64, TensorShape({})); + output.scalar()() = allocation_handle; + ctx->set_output(0, output); + } +}; + // Op that discards a handle to device memory. template class XRTReleaseAllocationOp : public OpKernel { diff --git a/tensorflow/compiler/xrt/ops/xrt_state_ops.cc b/tensorflow/compiler/xrt/ops/xrt_state_ops.cc index 07d025ce34..a3d63106fa 100644 --- a/tensorflow/compiler/xrt/ops/xrt_state_ops.cc +++ b/tensorflow/compiler/xrt/ops/xrt_state_ops.cc @@ -95,6 +95,20 @@ Copies an allocated tuple from device memory and returns it as a literal. 'literal' is a serialized xla::LiteralProto proto. )"); +REGISTER_OP("XRTWriteLiteral") + .Input("handle: int64") + .Input("literal: string") + .Output("output_handle: int64") + .SetShapeFn(tensorflow::shape_inference::ScalarShape) + .Doc( + R"( +Copies the input literal into the device memory pointed to by handle. +Returns the handle itself. + +'handle' is the id returned from the Op that produced the on-device allocation. +'literal' is a serialized xla::LiteralProto proto to be written to device memory. +)"); + REGISTER_OP("XRTReadLiteralAndRelease") .Input("handle: int64") .Output("literal: string") diff --git a/tensorflow/compiler/xrt/tests/raw_api_test.cc b/tensorflow/compiler/xrt/tests/raw_api_test.cc index 25464b5554..abaa17e50e 100644 --- a/tensorflow/compiler/xrt/tests/raw_api_test.cc +++ b/tensorflow/compiler/xrt/tests/raw_api_test.cc @@ -102,7 +102,7 @@ bool CompareLiteralProtos(const xla::LiteralProto& a, auto l_b = xla::Literal::CreateFromProto(b).ValueOrDie(); bool equal = l_a == l_b; if (!equal) { - LOG(INFO) << "LiteralProtos don't match " << a.DebugString() + LOG(INFO) << "LiteralProtos don't match: " << a.DebugString() << " != " << b.DebugString(); } return equal; @@ -175,6 +175,18 @@ xla::XlaComputation AddAndTuple() { return builder.Build().ValueOrDie(); } +xla::XlaComputation AddAndSubTuple() { + xla::XlaBuilder builder("AddAndSubTuple"); + auto p0 = xla::Parameter(&builder, 0, xla::ShapeUtil::MakeShape(xla::F32, {}), + "P0"); + auto p1 = xla::Parameter(&builder, 1, xla::ShapeUtil::MakeShape(xla::F32, {}), + "P1"); + auto sum = xla::Add(p0, p1); + auto sub = xla::Sub(p0, p1); + xla::Tuple(&builder, {sum, sub}); + return builder.Build().ValueOrDie(); +} + void StoreComputationSnapshot(const xla::XlaComputation& computation, xla::HloSnapshot* dst) { auto snapshot = computation.Snapshot().ValueOrDie(); @@ -203,6 +215,56 @@ xla::ProgramShape XlaCompiledProgramShape( ->ComputeProgramShape(); } +TEST(RawApiTest, AllocAndRewrite) { + xrt::XLAAllocation alloc; + alloc.set_device_ordinal(0); + *alloc.mutable_value() = + xla::LiteralUtil::CreateR2({{4, 5}, {6, 7}}).ToProto(); + + Scope root = Scope::NewRootScope().WithDevice(DeviceFromFlag()); + auto value = + ops::Const(root.WithDevice("/device:CPU:0"), alloc.SerializeAsString()); + auto handle = ops::XRTAllocate(root, value); + auto read_back = ops::XRTReadLiteral(root, handle); + TF_ASSERT_OK(root.status()); + + tensorflow::ClientSession session(root); + std::vector outputs; + TF_EXPECT_OK(session.Run({read_back, handle}, &outputs)); + EXPECT_EQ(outputs.size(), 2); + + int64 allocation_handle = outputs[1].scalar()(); + xla::LiteralProto response; + EXPECT_TRUE(response.ParseFromString(outputs[0].scalar()())); + EXPECT_TRUE(CompareLiteralProtos(alloc.value(), response)); + outputs.clear(); + + xla::LiteralProto new_literal = + xla::LiteralUtil::CreateR2({{9, 2}, {4, 1}}).ToProto(); + auto new_value = ops::Const(root.WithDevice("/device:CPU:0"), + new_literal.SerializeAsString()); + auto write_op = + ops::XRTWriteLiteral(root, Input(allocation_handle), new_value); + TF_ASSERT_OK(root.status()); + TF_EXPECT_OK(session.Run({write_op}, &outputs)); + EXPECT_EQ(outputs.size(), 1); + EXPECT_EQ(allocation_handle, outputs[0].scalar()()); + outputs.clear(); + + auto read_after_write = ops::XRTReadLiteral(root, Input(allocation_handle)); + TF_EXPECT_OK(session.Run({read_after_write}, &outputs)); + EXPECT_EQ(outputs.size(), 1); + + xla::LiteralProto new_response; + EXPECT_TRUE(new_response.ParseFromString(outputs[0].scalar()())); + EXPECT_TRUE(CompareLiteralProtos(new_literal, new_response)); + + auto release = + ops::XRTReleaseAllocationHandle(root, Input(allocation_handle)); + TF_EXPECT_OK(session.Run(tensorflow::ClientSession::FeedType(), {}, {release}, + &outputs)); +} + TEST(RawApiTest, ReadAndWriteState) { xrt::XLAAllocation alloc; alloc.set_device_ordinal(0); @@ -375,9 +437,12 @@ TEST(RawApiTest, CompileAndExecute) { xrt::XLAComputation c; auto config = c.mutable_config(); auto shapes = config->mutable_program_shape(); - *shapes->add_parameters() = xla::ShapeUtil::MakeShape(xla::F32, {2}); - *shapes->add_parameters() = xla::ShapeUtil::MakeShape(xla::F32, {2}); - *shapes->mutable_result() = xla::ShapeUtil::MakeShape(xla::F32, {2}); + *shapes->add_parameters() = + xla::ShapeUtil::MakeShape(xla::F32, {2}).ToProto(); + *shapes->add_parameters() = + xla::ShapeUtil::MakeShape(xla::F32, {2}).ToProto(); + *shapes->mutable_result() = + xla::ShapeUtil::MakeShape(xla::F32, {2}).ToProto(); StoreComputationSnapshot(AddAndScale(), c.mutable_hlo_snapshot()); xrt::XRTExecutionConfig e; @@ -411,7 +476,7 @@ TEST(RawApiTest, CompileAndExecute) { auto expected = xla::LiteralUtil::CreateR1({27.0f, 21.0f}); EXPECT_TRUE(CompareLiteralToLiteralProto(expected, response)); - xla::ProgramShape program_shape; + xla::ProgramShapeProto program_shape; EXPECT_TRUE(program_shape.ParseFromString(outputs[1].vec()(0))); EXPECT_EQ(program_shape.parameters_size(), 2); } @@ -427,9 +492,12 @@ TEST(RawApiTest, CompileAndExecuteWithArgumentVector) { xrt::XLAComputation c; auto config = c.mutable_config(); auto shapes = config->mutable_program_shape(); - *shapes->add_parameters() = xla::ShapeUtil::MakeShape(xla::F32, {2}); - *shapes->add_parameters() = xla::ShapeUtil::MakeShape(xla::F32, {2}); - *shapes->mutable_result() = xla::ShapeUtil::MakeShape(xla::F32, {2}); + *shapes->add_parameters() = + xla::ShapeUtil::MakeShape(xla::F32, {2}).ToProto(); + *shapes->add_parameters() = + xla::ShapeUtil::MakeShape(xla::F32, {2}).ToProto(); + *shapes->mutable_result() = + xla::ShapeUtil::MakeShape(xla::F32, {2}).ToProto(); StoreComputationSnapshot(AddAndScale(), c.mutable_hlo_snapshot()); xrt::XRTExecutionConfig e; @@ -465,7 +533,7 @@ TEST(RawApiTest, CompileAndExecuteWithArgumentVector) { auto expected = xla::LiteralUtil::CreateR1({27.0f, 21.0f}); EXPECT_TRUE(CompareLiteralToLiteralProto(expected, response)); - xla::ProgramShape program_shape; + xla::ProgramShapeProto program_shape; EXPECT_TRUE(program_shape.ParseFromString(outputs[1].vec()(0))); EXPECT_EQ(program_shape.parameters_size(), 2); } @@ -494,8 +562,8 @@ TEST(RawApiTest, CompileWithXlaReturnShapes) { xrt::XLAComputation c; auto config = c.mutable_config(); auto shapes = config->mutable_program_shape(); - *shapes->add_parameters() = param_shape; - *shapes->mutable_result() = result_shape; + *shapes->add_parameters() = param_shape.ToProto(); + *shapes->mutable_result() = result_shape.ToProto(); StoreComputationSnapshot(xla_computation, c.mutable_hlo_snapshot()); Scope root = Scope::NewRootScope().WithDevice(DeviceFromFlag()); @@ -510,8 +578,9 @@ TEST(RawApiTest, CompileWithXlaReturnShapes) { TF_EXPECT_OK(session.Run(tensorflow::ClientSession::FeedType(), {c_handle.program_shape}, {release}, &outputs)); - xla::ProgramShape program_shape; - EXPECT_TRUE(program_shape.ParseFromString(outputs[0].vec()(0))); + xla::ProgramShapeProto program_shape_proto; + EXPECT_TRUE(program_shape_proto.ParseFromString(outputs[0].vec()(0))); + xla::ProgramShape program_shape(program_shape_proto); EXPECT_EQ(program_shape.parameters_size(), 1); VLOG(2) << "Param: " @@ -520,7 +589,7 @@ TEST(RawApiTest, CompileWithXlaReturnShapes) { << xla::ShapeUtil::HumanStringWithLayout(program_shape.result()); xla::ProgramShape xla_program_shape = - XlaCompiledProgramShape(xla_computation, *shapes); + XlaCompiledProgramShape(xla_computation, xla::ProgramShape(*shapes)); EXPECT_TRUE(xla::LayoutUtil::Equal( xla::ShapeUtil::GetSubshape(program_shape.parameters(0), {0}).layout(), xla::ShapeUtil::GetSubshape(xla_program_shape.parameters(0), {0}) @@ -547,11 +616,11 @@ TEST(RawApiTest, DotGeneralWithLayoutTest) { auto config = c.mutable_config(); auto shapes = config->mutable_program_shape(); *shapes->add_parameters() = - xla::ShapeUtil::MakeShapeWithLayout(xla::F32, {2, 2}, {0, 1}); + xla::ShapeUtil::MakeShapeWithLayout(xla::F32, {2, 2}, {0, 1}).ToProto(); *shapes->add_parameters() = - xla::ShapeUtil::MakeShapeWithLayout(xla::F32, {2, 1}, {0, 1}); + xla::ShapeUtil::MakeShapeWithLayout(xla::F32, {2, 1}, {0, 1}).ToProto(); *shapes->mutable_result() = - xla::ShapeUtil::MakeShapeWithLayout(xla::F32, {2, 1}, {0, 1}); + xla::ShapeUtil::MakeShapeWithLayout(xla::F32, {2, 1}, {0, 1}).ToProto(); StoreComputationSnapshot(Dot(), c.mutable_hlo_snapshot()); xrt::XRTExecutionConfig e; @@ -592,7 +661,7 @@ TEST(RawApiTest, CompileAndExecuteZeroArg) { xrt::XLAComputation c; auto config = c.mutable_config(); auto shapes = config->mutable_program_shape(); - *shapes->mutable_result() = xla::ShapeUtil::MakeShape(xla::F32, {}); + *shapes->mutable_result() = xla::ShapeUtil::MakeShape(xla::F32, {}).ToProto(); xrt::XRTExecutionConfig e; e.set_release_input_handles(true); @@ -632,10 +701,13 @@ TEST(RawApiTest, CompileAndExecuteReturnTuple) { xrt::XLAComputation c; auto config = c.mutable_config(); auto shapes = config->mutable_program_shape(); - *shapes->add_parameters() = xla::ShapeUtil::MakeShape(xla::F32, {2}); - *shapes->add_parameters() = xla::ShapeUtil::MakeShape(xla::F32, {2}); - *shapes->mutable_result() = xla::ShapeUtil::MakeTupleShape( - {xla::ShapeUtil::MakeShape(xla::F32, {2})}); + *shapes->add_parameters() = + xla::ShapeUtil::MakeShape(xla::F32, {2}).ToProto(); + *shapes->add_parameters() = + xla::ShapeUtil::MakeShape(xla::F32, {2}).ToProto(); + *shapes->mutable_result() = + xla::ShapeUtil::MakeTupleShape({xla::ShapeUtil::MakeShape(xla::F32, {2})}) + .ToProto(); StoreComputationSnapshot(AddAndTuple(), c.mutable_hlo_snapshot()); xrt::XRTExecutionConfig e; @@ -671,14 +743,81 @@ TEST(RawApiTest, CompileAndExecuteReturnTuple) { EXPECT_TRUE(CompareLiteralToLiteralProto(expected, response)); } +TEST(RawApiTest, CompileAndExecuteReturnExplodedTuple) { + xrt::XLAAllocation p0; + p0.set_device_ordinal(0); + *p0.mutable_value() = xla::LiteralUtil::CreateR0(12.0f).ToProto(); + + xrt::XLAAllocation p1; + p1.set_device_ordinal(0); + *p1.mutable_value() = xla::LiteralUtil::CreateR0(3.0f).ToProto(); + + xrt::XLAComputation c; + auto config = c.mutable_config(); + auto shapes = config->mutable_program_shape(); + *shapes->add_parameters() = xla::ShapeUtil::MakeShape(xla::F32, {}).ToProto(); + *shapes->add_parameters() = xla::ShapeUtil::MakeShape(xla::F32, {}).ToProto(); + *shapes->mutable_result() = + xla::ShapeUtil::MakeTupleShape({xla::ShapeUtil::MakeShape(xla::F32, {}), + xla::ShapeUtil::MakeShape(xla::F32, {})}) + .ToProto(); + StoreComputationSnapshot(AddAndSubTuple(), c.mutable_hlo_snapshot()); + + xrt::XRTExecutionConfig e; + e.set_release_input_handles(true); + e.set_release_compilation_handle(true); + e.set_return_exploded_tuple(true); + + Scope root = Scope::NewRootScope().WithDevice(DeviceFromFlag()); + auto e_config = + ops::Const(root.WithDevice("/device:CPU:0"), e.SerializeAsString()); + auto computation = + ops::Const(root.WithDevice("/device:CPU:0"), c.SerializeAsString()); + auto c_handle = ops::XRTCompile(root, computation); + auto p0_value = + ops::Const(root.WithDevice("/device:CPU:0"), p0.SerializeAsString()); + auto p0_handle = ops::XRTAllocate(root, p0_value); + auto p1_value = + ops::Const(root.WithDevice("/device:CPU:0"), p1.SerializeAsString()); + auto p1_handle = ops::XRTAllocate(root, p1_value); + auto result = ops::XRTExecute(root, c_handle.handle, e_config, + {Output(p0_handle), Output(p1_handle)}); + TF_ASSERT_OK(root.status()); + + ClientSession session(root); + std::vector outputs; + TF_EXPECT_OK(session.Run({result}, &outputs)); + EXPECT_EQ(outputs.size(), 1); + + auto handles_vec = outputs.front().vec(); + EXPECT_EQ(handles_vec.size(), 2); + + const float kResults[2] = {15.0f, 9.0f}; + for (int64 i = 0; i < handles_vec.size(); ++i) { + auto read_back = ops::XRTReadLiteralAndRelease(root, Input(handles_vec(i))); + std::vector voutputs; + TF_EXPECT_OK(session.Run({read_back}, &voutputs)); + EXPECT_EQ(voutputs.size(), 1); + + xla::LiteralProto response; + EXPECT_TRUE(response.ParseFromString(voutputs[0].scalar()())); + + auto expected = xla::LiteralUtil::CreateR0(kResults[i]); + EXPECT_TRUE(CompareLiteralToLiteralProto(expected, response)); + } +} + TEST(RawApiTest, LeakCompilationReference) { xrt::XLAComputation c; auto config = c.mutable_config(); auto shapes = config->mutable_program_shape(); - *shapes->add_parameters() = xla::ShapeUtil::MakeShape(xla::F32, {2}); - *shapes->add_parameters() = xla::ShapeUtil::MakeShape(xla::F32, {2}); - *shapes->mutable_result() = xla::ShapeUtil::MakeTupleShape( - {xla::ShapeUtil::MakeShape(xla::F32, {2})}); + *shapes->add_parameters() = + xla::ShapeUtil::MakeShape(xla::F32, {2}).ToProto(); + *shapes->add_parameters() = + xla::ShapeUtil::MakeShape(xla::F32, {2}).ToProto(); + *shapes->mutable_result() = + xla::ShapeUtil::MakeTupleShape({xla::ShapeUtil::MakeShape(xla::F32, {2})}) + .ToProto(); StoreComputationSnapshot(AddAndTuple(), c.mutable_hlo_snapshot()); Scope root = Scope::NewRootScope().WithDevice(DeviceFromFlag()); @@ -703,9 +842,9 @@ TEST(RawApiTest, CompileAndExecuteWithS64Argument) { xrt::XLAComputation c; auto config = c.mutable_config(); auto shapes = config->mutable_program_shape(); - *shapes->add_parameters() = xla::ShapeUtil::MakeShape(xla::S64, {}); - *shapes->add_parameters() = xla::ShapeUtil::MakeShape(xla::S64, {}); - *shapes->mutable_result() = xla::ShapeUtil::MakeShape(xla::S64, {}); + *shapes->add_parameters() = xla::ShapeUtil::MakeShape(xla::S64, {}).ToProto(); + *shapes->add_parameters() = xla::ShapeUtil::MakeShape(xla::S64, {}).ToProto(); + *shapes->mutable_result() = xla::ShapeUtil::MakeShape(xla::S64, {}).ToProto(); StoreComputationSnapshot(AddS64(), c.mutable_hlo_snapshot()); xrt::XRTExecutionConfig e; @@ -739,11 +878,11 @@ TEST(RawApiTest, CompileAndExecuteWithS64Argument) { auto expected = xla::LiteralUtil::CreateR0(15123899); EXPECT_TRUE(CompareLiteralToLiteralProto(expected, response)); - xla::ProgramShape program_shape; + xla::ProgramShapeProto program_shape; EXPECT_TRUE(program_shape.ParseFromString(outputs[1].vec()(0))); EXPECT_EQ(program_shape.parameters_size(), 2); - EXPECT_TRUE( - xla::ShapeUtil::HasPrimitiveType(program_shape.result(), xla::S64)); + EXPECT_TRUE(xla::ShapeUtil::HasPrimitiveType( + xla::Shape(program_shape.result()), xla::S64)); } } // namespace diff --git a/tensorflow/compiler/xrt/xrt.proto b/tensorflow/compiler/xrt/xrt.proto index 6ab77fbaaf..378bb9246f 100644 --- a/tensorflow/compiler/xrt/xrt.proto +++ b/tensorflow/compiler/xrt/xrt.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package xrt; import "tensorflow/compiler/tf2xla/host_compute_metadata.proto"; +import "tensorflow/compiler/xla/xla.proto"; import "tensorflow/compiler/xla/xla_data.proto"; import "tensorflow/compiler/xla/service/hlo.proto"; @@ -36,16 +37,18 @@ message XLAComputationConfig { tensorflow.tf2xla.HostComputeMetadata host_compute_metadata = 3; // The arg/result shapes for the whole computation. - xla.ProgramShape program_shape = 4; + xla.ProgramShapeProto program_shape = 4; // The arg/result shapes for each core of a model-parallel // computation. per_core_args_and_result_shapes is optional for a // single-core computation. - repeated xla.ProgramShape per_core_program_shape = 5; + repeated xla.ProgramShapeProto per_core_program_shape = 5; // Describes how replicated computation instances should be assigned to // devices. There are num_cores_per_replica computations, and each one will be // sent and executed to the set of replica device numbers described in the // DeviceAssignment proto. DeviceAssignment device_assignment = 6; + // The debugging options to be passed to the XLA compilation process. + xla.DebugOptions debug_options = 7; } // Options and XLA computation for a compilation. @@ -98,4 +101,8 @@ message XRTExecutionConfig { bool release_input_handles = 5; // If true, release the handle to the computation after running. bool release_compilation_handle = 6; + // If set to true, and the result shape is a tuple, then instead of returning + // a single tuple allocation the execution will return a vector of + // allocations, one for each of the first-level elements of the result tuple. + bool return_exploded_tuple = 7; } diff --git a/tensorflow/compiler/xrt/xrt_state.cc b/tensorflow/compiler/xrt/xrt_state.cc index 3a99820d7a..5c7c537c34 100644 --- a/tensorflow/compiler/xrt/xrt_state.cc +++ b/tensorflow/compiler/xrt/xrt_state.cc @@ -183,6 +183,20 @@ Status XRTTupleAllocation::ToLiteral(xla::Backend* backend, int device_ordinal, return Status::OK(); } +Status XRTTupleAllocation::WriteLiteral(xla::Backend* backend, + const xla::Literal& literal) { + if (!xla::ShapeUtil::Equal(literal.shape(), on_host_shape())) { + return errors::InvalidArgument( + "New literal shape not matching the existing one: literal=", + xla::ShapeUtil::HumanStringWithLayout(literal.shape()), + " device=", xla::ShapeUtil::HumanStringWithLayout(on_host_shape())); + } + auto transfer_manager = backend->transfer_manager(); + TF_ASSIGN_OR_RETURN(auto stream, backend->BorrowStream(device_ordinal())); + return transfer_manager->TransferLiteralToDevice(stream.get(), literal, + ToShapedBuffer()); +} + void XRTTupleAllocation::DiscardAllocation( const xla::ShapeIndex& buffer_index) { buffers_.element(buffer_index)->DiscardAllocation(); diff --git a/tensorflow/compiler/xrt/xrt_state.h b/tensorflow/compiler/xrt/xrt_state.h index 73b5584e38..3664c0cd4e 100644 --- a/tensorflow/compiler/xrt/xrt_state.h +++ b/tensorflow/compiler/xrt/xrt_state.h @@ -137,6 +137,9 @@ class XRTTupleAllocation : public ResourceBase { Status ToLiteral(xla::Backend* backend, int device_ordinal, xla::Literal* literal); + // Write a new literal value to the allocation. + Status WriteLiteral(xla::Backend* backend, const xla::Literal& literal); + // True if none of the buffers in the allocation are aliased by any other live // handle. bool IsExclusiveOwner(); diff --git a/tensorflow/compiler/xrt/xrt_util.cc b/tensorflow/compiler/xrt/xrt_util.cc new file mode 100644 index 0000000000..3ef8bedc73 --- /dev/null +++ b/tensorflow/compiler/xrt/xrt_util.cc @@ -0,0 +1,76 @@ +/* Copyright 2018 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/compiler/xrt/xrt_util.h" + +#include +#include + +#include "tensorflow/compiler/xla/debug_options_flags.h" +#include "tensorflow/compiler/xla/types.h" +#include "tensorflow/core/platform/logging.h" + +namespace tensorflow { +namespace { + +bool DebugOptionsPassThroughEnabled() { + const char* env = getenv("TF_XLA_DEBUG_OPTIONS_PASSTHROUGH"); + bool enabled = + env != nullptr && (strcmp(env, "1") == 0 || strcmp(env, "true") == 0); + if (enabled) { + LOG(WARNING) << "Passing through XLA debug options!"; + } else { + LOG(WARNING) << "TF_XLA_DEBUG_OPTIONS_PASSTHROUGH not set, not all options " + "will be retained"; + } + return enabled; +} + +string SafeDebugPath(const string& path) { + if (path.empty() || path.compare(0, 5, "gs://") == 0 || + path.compare(0, 11, "bigstore://") == 0) { + return path; + } + LOG(WARNING) << "Invalid config path (will be dropped): " << path; + return string(); +} + +} // namespace + +xla::DebugOptions BuildXlaDebugOptions(const xla::DebugOptions& ref_options) { + static const bool options_passthrough = DebugOptionsPassThroughEnabled(); + if (options_passthrough) { + return ref_options; + } + xla::DebugOptions options = xla::GetDebugOptionsFromFlags(); + options.set_xla_generate_hlo_text_to( + SafeDebugPath(ref_options.xla_generate_hlo_text_to())); + options.set_xla_dump_optimized_hlo_proto_to( + SafeDebugPath(ref_options.xla_dump_optimized_hlo_proto_to())); + options.set_xla_dump_computations_to( + SafeDebugPath(ref_options.xla_dump_computations_to())); + options.set_xla_dump_executions_to( + SafeDebugPath(ref_options.xla_dump_executions_to())); + for (auto& pass : ref_options.xla_disable_hlo_passes()) { + options.add_xla_disable_hlo_passes(pass); + } + options.set_xla_dump_unoptimized_hlo_proto_to( + SafeDebugPath(ref_options.xla_dump_unoptimized_hlo_proto_to())); + options.set_xla_dump_per_pass_hlo_proto_to( + SafeDebugPath(ref_options.xla_dump_per_pass_hlo_proto_to())); + return options; +} + +} // namespace tensorflow diff --git a/tensorflow/compiler/xrt/xrt_util.h b/tensorflow/compiler/xrt/xrt_util.h new file mode 100644 index 0000000000..d9c05a7f34 --- /dev/null +++ b/tensorflow/compiler/xrt/xrt_util.h @@ -0,0 +1,34 @@ +/* Copyright 2018 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. +==============================================================================*/ + +// Utility functions in support of the XRT API. + +#ifndef TENSORFLOW_COMPILER_XRT_XRT_UTIL_H_ +#define TENSORFLOW_COMPILER_XRT_XRT_UTIL_H_ + +#include "tensorflow/compiler/xla/xla.pb.h" + +namespace tensorflow { + +// Filters the debug options provided as argument according to the value of the +// TF_XLA_DEBUG_OPTIONS_PASSTHROUGH environment variable. If such variable is +// set to "1" or "true", the debug options will be returned as is. Otherwise +// only a subset of them will be set in the returned ones, and all the paths +// contained in it, will be limited to gs:// and bigstore:// ones. +xla::DebugOptions BuildXlaDebugOptions(const xla::DebugOptions& ref_options); + +} // namespace tensorflow + +#endif // TENSORFLOW_COMPILER_XRT_XRT_UTIL_H_ diff --git a/tensorflow/contrib/all_reduce/BUILD b/tensorflow/contrib/all_reduce/BUILD index a513aa1e7c..f6c6560c1c 100644 --- a/tensorflow/contrib/all_reduce/BUILD +++ b/tensorflow/contrib/all_reduce/BUILD @@ -9,8 +9,6 @@ licenses(["notice"]) # Apache 2.0 exports_files(["LICENSE"]) -load("//tensorflow:tensorflow.bzl", "tf_py_test") - py_library( name = "all_reduce_py", srcs = ["__init__.py"], @@ -29,29 +27,6 @@ py_library( srcs_version = "PY2AND3", visibility = ["//visibility:public"], deps = [ - "//tensorflow/python:array_ops", - "//tensorflow/python:framework_ops", - "//tensorflow/python:math_ops", - "//tensorflow/python:nccl_ops", - ], -) - -tf_py_test( - name = "all_reduce_test", - srcs = ["python/all_reduce_test.py"], - additional_deps = [ - ":all_reduce", - "//third_party/py/numpy", - "//tensorflow/core:protos_all_py", - "//tensorflow/python:array_ops", - "//tensorflow/python:client", - "//tensorflow/python:framework_ops", - "//tensorflow/python:framework_test_lib", - "//tensorflow/python:math_ops", - "//tensorflow/python:constant_op", - "//tensorflow/python:client_testlib", - "//tensorflow/python:platform", - "//tensorflow/python:platform_test", - "//tensorflow/python:state_ops", + "//tensorflow/python/distribute:all_reduce", ], ) diff --git a/tensorflow/contrib/all_reduce/python/all_reduce.py b/tensorflow/contrib/all_reduce/python/all_reduce.py index 25f4b4b8d3..238cdaf8a7 100644 --- a/tensorflow/contrib/all_reduce/python/all_reduce.py +++ b/tensorflow/contrib/all_reduce/python/all_reduce.py @@ -18,842 +18,5 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -import collections -import math - -from tensorflow.python.framework import device as device_lib -from tensorflow.python.framework import ops -from tensorflow.python.ops import array_ops -from tensorflow.python.ops import math_ops -from tensorflow.python.ops import nccl_ops - - -def _flatten_tensors(tensors): - """Check tensors for isomorphism and flatten. - - Args: - tensors: list of T `tf.Tensor` which must all have the same shape. - - Returns: - tensors: a list of T `tf.Tensor` which are flattened (1D) views of tensors - shape: the original shape of each element of input tensors - - Raises: - ValueError: tensors are empty or non-isomorphic or have unknown shape. - """ - if not tensors: - raise ValueError("tensors cannot be empty") - shape = tensors[0].shape - for tensor in tensors: - shape = shape.merge_with(tensor.shape) - if not shape.is_fully_defined(): - raise ValueError("Tensors must have statically known shape.") - if len(shape) != 1: - reshaped = [] - for t in tensors: - with ops.colocate_with(t): - reshaped.append(array_ops.reshape(t, [-1])) - tensors = reshaped - return tensors, shape - - -def _reshape_tensors(tensors, shape): - """Reshape tensors flattened by _flatten_tensors. - - Args: - tensors: list of T `tf.Tensor` of identical length 1D tensors. - shape: list of integers describing the desired shape. Product of - the elements must equal the length of each tensor. - - Returns: - list of T `tf.Tensor` which are the reshaped inputs. - """ - reshaped = [] - for t in tensors: - with ops.colocate_with(t): - reshaped.append(array_ops.reshape(t, shape)) - return reshaped - - -def _padded_split(tensor, pieces): - """Like split for 1D tensors but pads-out case where len % pieces != 0. - - Args: - tensor: T `tf.Tensor` that must be 1D. - pieces: a positive integer specifying the number of pieces into which - tensor should be split. - - Returns: - list of T `tf.Tensor` of length pieces, which hold the values of - thin input tensor, in order. The final tensor may - be zero-padded on the end to make its size equal to those of all - of the other tensors. - - Raises: - ValueError: The input tensor is not 1D. - """ - shape = tensor.shape - if 1 != len(shape): - raise ValueError("input tensor must be 1D") - tensor_len = shape.dims[0].value - with ops.colocate_with(tensor): - if tensor_len % pieces != 0: - # pad to an even length - chunk_size = 1 + tensor_len // pieces - if pieces > tensor_len: - # This is an edge case that should not come up in practice, - # i.e. a different reduction algorithm would be better, - # but we'll make it work just for completeness. - pad_len = pieces - tensor_len - extended_whole = array_ops.concat( - [tensor, array_ops.zeros([pad_len], dtype=tensor.dtype)], 0) - parts = array_ops.split(extended_whole, pieces) - return parts, pad_len - elif (pieces - 1) * chunk_size >= tensor_len: - # Another edge case of limited real interest. - pad_len = (pieces * chunk_size) % tensor_len - extended_whole = array_ops.concat( - [tensor, array_ops.zeros([pad_len], dtype=tensor.dtype)], 0) - parts = array_ops.split(extended_whole, pieces) - return parts, pad_len - else: - last_chunk_size = tensor_len - (pieces - 1) * chunk_size - pad_len = chunk_size - last_chunk_size - piece_lens = [chunk_size for _ in range(pieces - 1)] + [last_chunk_size] - parts = array_ops.split(tensor, piece_lens) - parts[-1] = array_ops.concat( - [parts[-1], array_ops.zeros([pad_len], dtype=tensor.dtype)], 0) - return parts, pad_len - else: - return array_ops.split(tensor, pieces), 0 - - -def _strip_padding(tensors, pad_len): - """Strip the suffix padding added by _padded_split. - - Args: - tensors: list of T `tf.Tensor` of identical length 1D tensors. - pad_len: number of elements to be stripped from the end of each tensor. - - Returns: - list of T `tf.Tensor` which are the stripped inputs. - - Raises: - ValueError: tensors must be a non-empty list of 1D tensors, and - each must be longer than pad_len. - """ - if not tensors: - raise ValueError("tensors cannot be empty") - shape = tensors[0].shape - if len(shape) > 1: - raise ValueError("tensors must be 1D") - prefix_len = int(shape[0] - pad_len) - if prefix_len < 0: - raise ValueError("pad_len longer than tensor") - stripped = [] - for t in tensors: - with ops.colocate_with(t): - stripped.append(array_ops.slice(t, [0], [prefix_len])) - return stripped - - -def _ragged_split(tensor, pieces): - """Like split for 1D tensors but allows case where len % pieces != 0. - - Args: - tensor: T `tf.Tensor` that must be 1D. - pieces: a positive integer specifying the number of pieces into which - tensor should be split. - - Returns: - list of T `tf.Tensor` of length pieces, which hold the values of - the input tensor, in order. The final tensor may be shorter - than the others, which will all be of equal length. - - Raises: - ValueError: input tensor must be 1D. - """ - shape = tensor.shape - if 1 != len(shape): - raise ValueError("input tensor must be 1D") - tensor_len = shape.dims[0].value - chunk_size = tensor_len // pieces - with ops.colocate_with(tensor): - if tensor_len != (pieces * chunk_size): - # last piece will be short - assert pieces > 1 - last_chunk_size = tensor_len - ((pieces - 1) * chunk_size) - assert last_chunk_size > 0 - piece_lens = [chunk_size for _ in range(pieces - 1)] + [last_chunk_size] - return array_ops.split(tensor, piece_lens) - else: - return array_ops.split(tensor, pieces) - - -def _ring_permutations(num_workers, num_subchunks, gpu_perm): - """"Generate an array of device index arrays, one for each subchunk. - - In the basic ring reduction algorithm there are size(T)/num_devices - data chunks and each device process one chunk per tick, i.e. sending - one chunk and receiving one chunk. The idea of subchunking is that - each device processes num_subchunks smaller data regions per tick, - and the ring rank permutation is different for each subchunk index - so that a device is potentially sending to and receiving from - num_subchunks different other devices at each tick. Where multiple - independent data channels exist between devices, this strategy - supplies a method of using them in parallel. - - Args: - num_workers: number of worker tasks - num_subchunks: number of subchunks into which to divide each per-GPU chunk. - gpu_perm: an array of integers in [0, num_gpus-1] giving the default - ring order of GPUs at each worker. Other permutations will be generated - by rotating this array and splicing together per-worker instances. - - Raises: - ValueError: the number of subchunks may not exceed the number of GPUs. - - Returns: - pred_by_s_d: list of lists that maps (by index) from (subchunk, dev) to - preceding device in the permutation for that subchunk. The - device index of GPU i at worker j is i + (j * num_gpus). - rank_by_s_d: list of lists that maps (by index) from (subchunk, dev) to - local rank of device d in the permutation for that subchunk. - """ - num_gpus = len(gpu_perm) - devices = num_workers * num_gpus - if devices == 0: - return [], [] - if num_subchunks > num_gpus: - raise ValueError( - "num_subchunks %d must be <= num_gpus %d" % (num_subchunks, num_gpus)) - rotation_interval = max(1, int(num_gpus / num_subchunks)) - perms_by_s = [] - for s in range(0, num_subchunks): - full_order = [] - offset = s * rotation_interval - for w in range(0, num_workers): - default_order = [(w * num_gpus) + i for i in gpu_perm] - dev_order = default_order[offset:] + default_order[:offset] - full_order += dev_order - perms_by_s.append(full_order) - pred_by_s_d = [[-1 for d in range(0, devices)] - for s in range(0, num_subchunks)] - rank_by_s_d = [[-1 for d in range(0, devices)] - for s in range(0, num_subchunks)] - for s in range(0, num_subchunks): - for d in range(0, devices): - for t in range(0, devices): - if d == perms_by_s[s][t]: - rank_by_s_d[s][d] = t - pred_by_s_d[s][d] = perms_by_s[s][(t + devices - 1) % devices] - break - return (pred_by_s_d, rank_by_s_d) - - -def build_ring_all_reduce(input_tensors, num_workers, num_subchunks, - gpu_perm, red_op, un_op=None): - """Construct a subgraph performing a ring-style all-reduce of input_tensors. - - Args: - input_tensors: a list of T `tf.Tensor` objects, which must all - have the same shape and type. - num_workers: number of worker tasks spanned by input_tensors. - num_subchunks: number of subchunks each device should process in one tick. - gpu_perm: a list of ints giving a ring-wise rank ordering of GPUs at - each worker. All workers must have the same number of - GPUs with the same rank ordering. If NVLINK is available, this should - be a ring order supported by NVLINK edges. - red_op: a binary operator for elementwise reduction. - un_op: an optional unary operator to apply to fully reduced values. - - Raises: - ValueError: empty input_tensors or they don't all have same - size. - - Returns: - a list of T `tf.Tensor` identical sum-reductions of input_tensors. - """ - if len(input_tensors) < 2: - raise ValueError("input_tensors must be length 2 or longer") - input_tensors, shape = _flatten_tensors(input_tensors) - devices = [t.device for t in input_tensors] - (pred_by_s_d, rank_by_s_d) = _ring_permutations( - num_workers, num_subchunks, gpu_perm) - chunks_by_dev, pad_len = _build_ring_gather( - input_tensors, devices, - num_subchunks, pred_by_s_d, rank_by_s_d, red_op) - if un_op: - chunks_by_dev = _apply_unary_to_chunks(un_op, chunks_by_dev) - output_tensors = _build_ring_scatter(pred_by_s_d, rank_by_s_d, - chunks_by_dev) - if pad_len > 0: - output_tensors = _strip_padding(output_tensors, pad_len) - if len(shape) != 1: - output_tensors = _reshape_tensors(output_tensors, shape) - return output_tensors - - -def _build_ring_gather(input_tensors, devices, num_subchunks, - pred_by_s_d, rank_by_s_d, red_op): - """Construct a subgraph for the first (reduction) pass of ring all-reduce. - - Args: - input_tensors: a list of T `tf.Tensor` 1D input tensors of same - shape and type. - devices: array of device name strings - num_subchunks: number of subchunks each device should process in one tick. - pred_by_s_d: as produced by _ring_permutations - rank_by_s_d: as produced by _ring_permutations - red_op: a binary operator for elementwise reduction - - Raises: - ValueError: tensors must all be one dimensional. - - Returns: - list of list of T `tf.Tensor` of (partially) reduced values where - exactly num_subchunks chunks at each device are fully reduced. - """ - num_devices = len(input_tensors) - if num_devices == 0: - return [] - if num_devices == 1: - return input_tensors - shape = input_tensors[0].shape - if 1 != len(shape): - raise ValueError("input tensors must be 1D") - num_chunks = num_devices * num_subchunks - num_ticks = num_devices - 1 - # Initialize chunks_by_dev with splits of the input tensors. - chunks_by_dev = [] - split_pad_len = 0 - for d in range(0, num_devices): - with ops.device(devices[d]): - splits, split_pad_len = _padded_split(input_tensors[d], num_chunks) - chunks_by_dev.append(splits) - # Reduction phase - for tick in range(0, num_ticks): - # One new partial reduction for every chunk - new_partial_reductions = [None for _ in range(0, num_chunks)] - # Compute reductions with respect to last tick's values - for d in range(0, num_devices): - with ops.device(devices[d]): - for s in range(0, num_subchunks): - rank = rank_by_s_d[s][d] - seg_index = (rank + num_devices - (2 + tick)) % num_devices - pred_dev = pred_by_s_d[s][d] - chunk_index = (seg_index * num_subchunks) + s - new_partial_reductions[chunk_index] = red_op( - chunks_by_dev[pred_dev][chunk_index], - chunks_by_dev[d][chunk_index]) - # Update chunks_by_dev with the new values at the end of the tick. - for d in range(0, num_devices): - for s in range(0, num_subchunks): - rank = rank_by_s_d[s][d] - seg_index = (rank + num_devices - (2 + tick)) % num_devices - chunk_index = (seg_index * num_subchunks) + s - chunks_by_dev[d][chunk_index] = new_partial_reductions[chunk_index] - return chunks_by_dev, split_pad_len - - -def _apply_unary_to_chunks(f, chunks_by_dev): - """Apply a unary op to each tensor in chunks_by_dev, on same device. - - Args: - f: a unary function over T `tf.Tensor`. - chunks_by_dev: list of lists of T `tf.Tensor`. - - Returns: - new list of lists of T `tf.Tensor` with the same structure as - chunks_by_dev containing the derived tensors. - """ - output = [] - for x in chunks_by_dev: - with ops.colocate_with(x[0]): - output.append([f(t) for t in x]) - return output - - -def _build_ring_scatter(pred_by_s_d, rank_by_s_d, - chunks_by_dev): - """Construct subgraph for second (scatter) pass of ring all-reduce. - - Args: - pred_by_s_d: as produced by _ring_permutations - rank_by_s_d: as produced by _ring_permutations - chunks_by_dev: list of list of T `tf.Tensor` indexed by ints - (device, chunk) - - Raises: - ValueError: chunks_by_dev is not well-formed - - Returns: - list of T `tf.Tensor` which are the fully reduced tensors, one - at each device corresponding to the outer dimension of chunks_by_dev. - """ - num_devices = len(chunks_by_dev) - num_chunks = len(chunks_by_dev[0]) - if 0 != num_chunks % num_devices: - raise ValueError( - "Expect number of chunks per device to be divisible by num_devices") - num_subchunks = int(num_chunks / num_devices) - num_ticks = num_devices - 1 - for tick in range(0, num_ticks): - passed_values = [None for _ in range(0, num_chunks)] - for d in range(0, num_devices): - with ops.colocate_with(chunks_by_dev[d][0]): - for s in range(0, num_subchunks): - rank = rank_by_s_d[s][d] - seg_index = (rank + num_devices - (1 + tick)) % num_devices - pred_dev = pred_by_s_d[s][d] - chunk_index = (seg_index * num_subchunks) + s - passed_values[chunk_index] = array_ops.identity( - chunks_by_dev[pred_dev][chunk_index]) - for d in range(0, num_devices): - for s in range(0, num_subchunks): - rank = rank_by_s_d[s][d] - seg_index = (rank + num_devices - (1 + tick)) % num_devices - chunk_index = (seg_index * num_subchunks) + s - chunks_by_dev[d][chunk_index] = passed_values[chunk_index] - # Join chunks at each device. - output = [] - for x in chunks_by_dev: - with ops.colocate_with(x[0]): - output.append(array_ops.concat(x, 0)) - return output - - -def build_recursive_hd_all_reduce(input_tensors, red_op, un_op=None): - """Construct a subgraph for recursive halving-doubling all-reduce. - - The recursive halving-doubling algorithm is described in - http://www.mcs.anl.gov/~thakur/papers/ijhpca-coll.pdf - - The concept is to arrange the participating n devices in - a linear sequence where devices exchange data pairwise - with one other device in each round. During the gather - phase there are lg(n) rounds where devices exchange - increasingly smaller sub-tensors with another device - at increasingly greater distances, until at the top - each device has 1/n of the fully reduced values. During the - scatter phase each device exchanges its fully reduced - sub-tensor (which doubles in length at each round) - with one other device at increasingly smaller distances - until each device has all of the fully reduced values. - - Note: this preliminary version requires that len(input_tensors) be a - power of 2. TODO(tucker): relax this restriction. Also, the - number of elements in each tensor must be divisible by 2^h where h - is the number of hops in each phase. This will also be relaxed in - the future with edge-case specific logic. - - Args: - input_tensors: list of T `tf.Tensor` to be elementwise reduced. - red_op: a binary elementwise reduction Op. - un_op: an optional unary elementwise Op to apply to reduced values. - - Returns: - list of T `tf.Tensor` which are the fully reduced tensors, one - at each device of input_tensors. - - Raises: - ValueError: num_devices not a power of 2, or tensor len not divisible - by 2 the proper number of times. - """ - devices = [t.device for t in input_tensors] - input_tensors, shape = _flatten_tensors(input_tensors) - reduced_shards = _build_recursive_hd_gather(input_tensors, devices, red_op) - if un_op: - reduced_shards = [un_op(t) for t in reduced_shards] - output_tensors = _build_recursive_hd_scatter(reduced_shards, devices) - if len(shape) != 1: - output_tensors = _reshape_tensors(output_tensors, shape) - return output_tensors - - -def _build_recursive_hd_gather(input_tensors, devices, red_op): - """Construct the gather phase of recursive halving-doubling all-reduce. - - Args: - input_tensors: list of T `tf.Tensor` to be elementwise reduced. - devices: a list of strings naming the devices hosting input_tensors, - which will also be used to host the (partial) reduction values. - red_op: a binary elementwise reduction Op. - - Returns: - list of T `tf.Tensor` which are the fully reduced tensor shards. - - Raises: - ValueError: num_devices not a power of 2, or tensor len not divisible - by 2 the proper number of times. - """ - num_devices = len(devices) - num_hops = int(math.log(num_devices, 2)) - if num_devices != (2 ** num_hops): - raise ValueError("num_devices must be a power of 2") - chunks = input_tensors - for h in range(0, num_hops): - span = 2 ** h - group_size = span * 2 - new_chunks = [[] for _ in devices] - for d in range(0, num_devices): - if (d % group_size) >= (group_size / 2): - # skip right half of a pair - continue - left_dev = devices[d] - right_dev = devices[d + span] - left_split = array_ops.split(chunks[d], 2) - right_split = array_ops.split(chunks[d+span], 2) - with ops.device(left_dev): - new_chunks[d] = red_op(left_split[0], right_split[0]) - with ops.device(right_dev): - new_chunks[d + span] = red_op(left_split[1], right_split[1]) - chunks = new_chunks - return chunks - - -def _build_recursive_hd_scatter(input_tensors, devices): - """Construct the scatter phase of recursive halving-doublng all-reduce. - - Args: - input_tensors: list of T `tf.Tensor` that are fully-reduced shards. - devices: a list of strings naming the devices on which the reconstituted - full tensors should be placed. - - Returns: - list of T `tf.Tensor` which are the fully reduced tensors. - """ - num_devices = len(devices) - num_hops = int(math.log(num_devices, 2)) - assert num_devices == (2 ** num_hops), "num_devices must be a power of 2" - chunks = input_tensors - for h in reversed(range(0, num_hops)): - span = 2 ** h - group_size = span * 2 - new_chunks = [[] for _ in devices] - for d in range(0, num_devices): - if (d % group_size) >= (group_size / 2): - # skip right half of a pair - continue - left_idx = d - right_idx = d + span - left_dev = devices[left_idx] - right_dev = devices[right_idx] - with ops.device(left_dev): - new_chunks[left_idx] = array_ops.concat([chunks[left_idx], - chunks[right_idx]], 0) - with ops.device(right_dev): - new_chunks[right_idx] = array_ops.concat([chunks[left_idx], - chunks[right_idx]], 0) - chunks = new_chunks - return chunks - - -def build_shuffle_all_reduce(input_tensors, gather_devices, red_op, un_op=None): - """Construct a subgraph for shuffle all-reduce. - - Shuffle reduce is essentially the algorithm implemented when using - parameter servers. Suppose tensor length is n, there are d devices - and g gather shards. Each device sends a n/g length sub-tensor to - each gather shard. The gather shards perform a reduction across d - fragments, then broadcast the result back to each device. The - devices then join the g fully reduced fragments they receive from - the shards. The gather shards could perform d-1 pairwise - reductions, or one d-way reduction. The first is better where - reduction Op time is low compared to transmission time, the second - better in the other case. - - Args: - input_tensors: list of T @(tf.Tensor} values to be reduced. - gather_devices: list of names of devices on which reduction shards - should be placed. - red_op: an n-array elementwise reduction Op - un_op: optional elementwise unary Op to be applied to fully-reduced values. - - Returns: - list of T `tf.Tensor` which are the fully reduced tensors. - """ - input_tensors, shape = _flatten_tensors(input_tensors) - dst_devices = [t.device for t in input_tensors] - reduced_shards = _build_shuffle_gather(input_tensors, gather_devices, - red_op, un_op) - output_tensors = _build_shuffle_scatter(reduced_shards, dst_devices) - if len(shape) != 1: - output_tensors = _reshape_tensors(output_tensors, shape) - return output_tensors - - -def _build_shuffle_gather(input_tensors, gather_devices, red_op, un_op=None): - """Construct the gather (concentrate and reduce) phase of shuffle all-reduce. - - Args: - input_tensors: list of T @(tf.Tensor} values to be reduced. - gather_devices: list of names of devices on which reduction shards - should be placed. - red_op: the binary reduction Op - un_op: optional elementwise unary Op to be applied to fully-reduced values. - - Returns: - list of T `tf.Tensor` which are the fully reduced shards. - - Raises: - ValueError: inputs not well-formed. - """ - num_source_devices = len(input_tensors) - num_gather_devices = len(gather_devices) - shape = input_tensors[0].shape - if len(shape) != 1: - raise ValueError("input_tensors must be 1D") - shards_by_source = [] - for d in range(0, num_source_devices): - with ops.colocate_with(input_tensors[d]): - shards_by_source.append( - _ragged_split(input_tensors[d], num_gather_devices)) - reduced_shards = [] - for d in range(0, num_gather_devices): - with ops.device(gather_devices[d]): - values = [s[d] for s in shards_by_source] - red_shard = red_op(values) - if un_op: - red_shard = un_op(red_shard) - reduced_shards.append(red_shard) - return reduced_shards - - -def _build_shuffle_scatter(reduced_shards, dst_devices): - """Build the scatter phase of shuffle all-reduce. - - Args: - reduced_shards: list of T @(tf.Tensor} fully reduced shards - dst_devices: list of names of devices at which the fully-reduced value - should be reconstituted. - - Returns: - list of T `tf.Tensor` scattered tensors. - """ - num_devices = len(dst_devices) - out_tensors = [] - for d in range(0, num_devices): - with ops.device(dst_devices[d]): - out_tensors.append(array_ops.concat(reduced_shards, 0)) - return out_tensors - - -def _split_by_task(devices, values): - """Partition devices and values by common task. - - Args: - devices: list of device name strings - values: list of T `tf.tensor` of same length as devices. - - Returns: - (per_task_devices, per_task_values) where both values are - lists of lists with isomorphic structure: the outer list is - indexed by task, and the inner list has length of the number - of values belonging to that task. per_task_devices contains - the specific devices to which the values are local, and - per_task_values contains the corresponding values. - - Raises: - ValueError: devices must be same length as values. - """ - num_devices = len(devices) - if num_devices != len(values): - raise ValueError("len(devices) must equal len(values)") - per_task_devices = collections.OrderedDict() - per_task_values = collections.OrderedDict() - for d in range(num_devices): - d_spec = device_lib.DeviceSpec.from_string(devices[d]) - if not hasattr(d_spec, "task") or d_spec.task is None: - assert False, "failed to parse device %s" % devices[d] - index = (d_spec.job or "localhost", d_spec.replica or 0, d_spec.task) - if index not in per_task_devices: - per_task_devices[index] = [] - per_task_values[index] = [] - per_task_devices[index].append(devices[d]) - per_task_values[index].append(values[d]) - - return (list(per_task_devices.values()), list(per_task_values.values())) - - -def build_nccl_all_reduce(input_tensors, red_op, un_op=None): - """Build a subgraph that does one full all-reduce, using NCCL. - - Args: - input_tensors: list of T `tf.Tensor` of same-shape and type values to - be reduced. - red_op: binary elementwise reduction operator. Must be one of - {tf.add} - un_op: optional unary elementwise Op to apply to fully-reduce values. - - Returns: - list of T `tf.Tensor` of reduced values. - - Raises: - ValueError: red_op not supported. - """ - if red_op == math_ops.add: - output_tensors = nccl_ops.all_sum(input_tensors) - else: - raise ValueError("red_op not supported by NCCL all-reduce: ", red_op) - if un_op: - un_op_wrapped = [] - for t in output_tensors: - with ops.colocate_with(t): - un_op_wrapped.append(un_op(t)) - output_tensors = un_op_wrapped - return output_tensors - - -def _build_nccl_hybrid(input_tensors, red_op, upper_level_f): - """Construct a subgraph for NCCL hybrid all-reduce. - - Args: - input_tensors: list of T `tf.Tensor` of same-shape and type values to - be reduced. - red_op: binary elementwise reduction operator. - upper_level_f: function for reducing one value per worker, across - workers. - - Returns: - list of T `tf.Tensor` of reduced values. - - Raises: - ValueError: inputs not well-formed. - """ - input_tensors, shape = _flatten_tensors(input_tensors) - devices = [t.device for t in input_tensors] - per_worker_devices, per_worker_values = _split_by_task(devices, input_tensors) - num_workers = len(per_worker_devices) - up_values = [None for w in range(0, num_workers)] - up_devices = up_values[:] - down_values = up_values[:] - # First stage: reduce within each worker using NCCL - for w in range(0, num_workers): - worker_values = build_nccl_all_reduce(per_worker_values[w], red_op) - # NOTE: these reductions will not run to completion unless - # every output value is used. Since we only need one, we - # need to put control dependencies on the rest. - with ops.control_dependencies(worker_values): - with ops.device(worker_values[0].device): - up_values[w] = array_ops.identity(worker_values[0]) - up_devices[w] = per_worker_devices[w][0] - # Second stage: Apply upper_level_f to reduce across first device at - # each worker - level_2_output = upper_level_f(up_values) - # Third stage: propagate within each worker using NCCL Broadcast - for w in range(0, num_workers): - dst_tensors = [] - with ops.device(per_worker_devices[w][0]): - broadcast_src = nccl_ops.broadcast(array_ops.identity(level_2_output[w])) - for d in per_worker_devices[w]: - with ops.device(d): - dst_tensors.append(array_ops.identity(broadcast_src)) - down_values[w] = dst_tensors - output_tensors = [v for sublist in down_values for v in sublist] - if len(shape) != 1: - output_tensors = _reshape_tensors(output_tensors, shape) - return output_tensors - - -def _reduce_non_singleton(input_tensors, red_f, un_op): - """If input_tensors has more than one element apply red_f, else apply un_op.""" - if len(input_tensors) > 1: - return red_f(input_tensors) - else: - if not un_op: - return input_tensors - output_tensors = [] - for t in input_tensors: - with ops.colocate_with(t): - output_tensors.append(un_op(t)) - return output_tensors - - -def build_nccl_then_ring(input_tensors, subdiv, red_op, un_op=None): - """Construct hybrid of NCCL within workers, Ring across workers.""" - def upper_builder(y): - return build_ring_all_reduce(y, len(y), subdiv, [0], red_op, un_op) - def upper_level_f(x): - return _reduce_non_singleton(x, upper_builder, un_op) - return _build_nccl_hybrid(input_tensors, red_op, upper_level_f) - - -def build_nccl_then_recursive_hd(input_tensors, red_op, un_op=None): - """Construct hybrid of NCCL within workers, Recursive-HD across workers.""" - upper_level_f = lambda x: build_recursive_hd_all_reduce(x, red_op, un_op) - return _build_nccl_hybrid(input_tensors, red_op, upper_level_f) - - -def build_nccl_then_shuffle(input_tensors, gather_devices, nccl_red_op, - shuffle_red_op, un_op=None): - """Construct hybrid of NCCL within workers, Shuffle across workers.""" - upper_level_f = lambda x: build_shuffle_all_reduce(x, gather_devices, - shuffle_red_op, un_op) - return _build_nccl_hybrid(input_tensors, nccl_red_op, upper_level_f) - - -def _build_shuffle_hybrid(input_tensors, gather_devices, red_op, upper_level_f): - """Construct a subgraph for Shuffle hybrid all-reduce. - - Args: - input_tensors: list of T `tf.Tensor` of same-shape and type values to - be reduced. - gather_devices: list of device names on which to host gather shards. - red_op: binary elementwise reduction operator. - upper_level_f: function for reducing one value per worker, across - workers. - - Returns: - list of T `tf.Tensor` of reduced values. - - Raises: - ValueError: inputs not well-formed. - """ - input_tensors, shape = _flatten_tensors(input_tensors) - # First stage, reduce across each worker using gather_devices. - devices = [t.device for t in input_tensors] - per_worker_devices, per_worker_values = _split_by_task(devices, input_tensors) - num_workers = len(per_worker_devices) - up_values = [] - if len(gather_devices) != num_workers: - raise ValueError("For shuffle hybrid, gather_devices must contain one " - "device per worker. ") - for w in range(0, num_workers): - reduced_shards = _build_shuffle_gather( - per_worker_values[w], [gather_devices[w]], red_op) - up_values.append(reduced_shards[0]) - # Second stage, apply upper_level_f. - level_2_output = upper_level_f(up_values) - # Third stage, apply shuffle scatter at each worker. - output_tensors = [] - for w in range(0, num_workers): - output_tensors += _build_shuffle_scatter( - [level_2_output[w]], per_worker_devices[w]) - if len(shape) != 1: - output_tensors = _reshape_tensors(output_tensors, shape) - return output_tensors - - -def build_shuffle_then_ring(input_tensors, gather_devices, subdiv, - red_n_op, red_op, un_op=None): - """Construct hybrid of Shuffle within workers, Ring across workers.""" - def upper_builder(tensors): - return build_ring_all_reduce(tensors, len(tensors), subdiv, [0], - red_op, un_op) - def upper_level_f(tensors): - return _reduce_non_singleton(tensors, upper_builder, un_op) - return _build_shuffle_hybrid( - input_tensors, gather_devices, red_n_op, upper_level_f) - - -def build_shuffle_then_shuffle(input_tensors, first_gather_devices, - second_gather_devices, red_op, un_op=None): - """Construct hybrid of Shuffle within workers, Shuffle across workers.""" - def upper_builder(tensors): - return build_shuffle_all_reduce(tensors, second_gather_devices, - red_op, un_op) - def upper_level_f(tensors): - return _reduce_non_singleton(tensors, upper_builder, un_op) - return _build_shuffle_hybrid( - input_tensors, first_gather_devices, red_op, upper_level_f) +# pylint: disable=unused-import,wildcard-import +from tensorflow.python.distribute.all_reduce import * diff --git a/tensorflow/contrib/autograph/examples/benchmarks/BUILD b/tensorflow/contrib/autograph/examples/benchmarks/BUILD new file mode 100644 index 0000000000..6d2d70c99b --- /dev/null +++ b/tensorflow/contrib/autograph/examples/benchmarks/BUILD @@ -0,0 +1,36 @@ +licenses(["notice"]) # Apache 2.0 + +load("//tensorflow:tensorflow.bzl", "py_test") +load("//tensorflow/tools/test:performance.bzl", "tf_py_logged_benchmark") + +py_library( + name = "benchmark_base", + srcs = [ + "benchmark_base.py", + ], + deps = [ + "//tensorflow:tensorflow_py", + ], +) + +py_test( + name = "cartpole_benchmark", + size = "enormous", + srcs = ["cartpole_benchmark.py"], + tags = [ + "local", + "manual", + "no_oss", + "notap", + "nozapfhahn", + ], + deps = [ + ":benchmark_base", + # Note: required gym dependency may need to be added here. + ], +) + +tf_py_logged_benchmark( + name = "cartpole_logged_benchmark", + target = "//tensorflow/contrib/autograph/examples/benchmarks:cartpole_benchmark", +) diff --git a/tensorflow/contrib/autograph/examples/benchmarks/benchmark_base.py b/tensorflow/contrib/autograph/examples/benchmarks/benchmark_base.py new file mode 100644 index 0000000000..93c694849c --- /dev/null +++ b/tensorflow/contrib/autograph/examples/benchmarks/benchmark_base.py @@ -0,0 +1,62 @@ +# Copyright 2018 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. +# ============================================================================== +"""Common benchmarking code. + +See https://www.tensorflow.org/community/benchmarks for usage. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import time + +import numpy as np + +import tensorflow as tf + + +class ReportingBenchmark(tf.test.Benchmark): + """Base class for a benchmark that reports general performance metrics. + + Subclasses only need to call one of the _profile methods, and optionally + report_results. + """ + + def time_execution(self, name, target, iters, warm_up_iters=5): + for _ in range(warm_up_iters): + target() + + all_times = [] + for _ in range(iters): + iter_time = time.time() + target() + all_times.append(time.time() - iter_time) + + avg_time = np.average(all_times) + + extras = dict() + extras['all_times'] = all_times + + if isinstance(name, tuple): + extras['name'] = name + name = '_'.join(str(piece) for piece in name) + + self.report_benchmark( + iters=iters, wall_time=avg_time, name=name, extras=extras) + + +if __name__ == '__main__': + tf.test.main() diff --git a/tensorflow/contrib/autograph/examples/benchmarks/cartpole_benchmark.py b/tensorflow/contrib/autograph/examples/benchmarks/cartpole_benchmark.py new file mode 100644 index 0000000000..4f553be58e --- /dev/null +++ b/tensorflow/contrib/autograph/examples/benchmarks/cartpole_benchmark.py @@ -0,0 +1,492 @@ +# Copyright 2018 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. +# ============================================================================== +"""A basic RL cartpole benchmark. + +The RL model uses the OpenAI Gym environment to train a simple network using +the policy gradients method. The training scales the gradients for each step +by the episode's cumulative discounted reward and averages these gradients over +a fixed number of games before applying the optimization step. + +For benchmarking purposes, we replace the OpenAI Gym environment to a fake +that returns random actions and rewards and never ends the episode. This way +the benchmarks compare the same amount of computation at each step. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import gym +import numpy as np +import tensorflow as tf + +from tensorflow.contrib import eager +from tensorflow.contrib.autograph.examples.benchmarks import benchmark_base +from tensorflow.python import autograph as ag +from tensorflow.python.eager import context + +# +# AutoGraph implementation +# + + +@ag.convert() +def graph_append_discounted_rewards(destination, rewards, discount_rate): + """Discounts episode rewards and appends them to destination.""" + ag.set_element_type(rewards, tf.float32) + + cdr = 0.0 + reverse_discounted = [] + ag.set_element_type(reverse_discounted, tf.float32) + + for i in range(len(rewards) - 1, -1, -1): + cdr = cdr * discount_rate + rewards[i] + cdr.set_shape(()) + reverse_discounted.append(cdr) + + retval = destination + # Note: AutoGraph doesn't yet support reversed() so we use a loop instead. + for i in range(len(reverse_discounted) - 1, -1, -1): + retval.append(reverse_discounted[i]) + + return retval + + +class GraphPolicyNetwork(tf.keras.Model): + """Policy network for the cart-pole reinforcement learning problem. + + The forward path of the network takes an observation from the cart-pole + environment (length-4 vector) and outputs an action. + """ + + def __init__(self, hidden_size): + super(GraphPolicyNetwork, self).__init__() + self._hidden_layer = tf.keras.layers.Dense( + hidden_size, activation=tf.nn.elu) + self._output_layer = tf.keras.layers.Dense(1) + + def call(self, inputs): + """Calculates logits and action. + + Args: + inputs: Observations from a step in the cart-pole environment, of shape + `(batch_size, input_size)` + + Returns: + logits: the logits output by the output layer. This can be viewed as the + likelihood vales of choosing the left (0) action. Shape: + `(batch_size, 1)`. + actions: randomly selected actions ({0, 1}) based on the logits. Shape: + `(batch_size, 1)`. + """ + hidden = self._hidden_layer(inputs) + logits = self._output_layer(hidden) + + left_prob = tf.nn.sigmoid(logits) + action_probs = tf.concat([left_prob, 1.0 - left_prob], 1) + + actions = tf.multinomial(tf.log(action_probs), 1) + return logits, actions + + # TODO(mdan): Move this method out of the class. + @ag.convert() + def train(self, cart_pole_env, optimizer, discount_rate, num_games, + max_steps_per_game): + var_list = tf.trainable_variables() + grad_list = [ + tf.TensorArray(tf.float32, 0, dynamic_size=True) for _ in var_list + ] + + step_counts = [] + discounted_rewards = [] + ag.set_element_type(discounted_rewards, tf.float32) + ag.set_element_type(step_counts, tf.int32) + + # Note: we use a shared object, cart_pole_env here. Because calls to the + # object's method are made through py_func, TensorFlow cannot detect its + # data dependencies. Hence we must manually synchronize access to it + # and ensure the control dependencies are set in such a way that + # calls to reset(), take_one_step, etc. are made in the correct order. + sync_counter = tf.constant(0) + + for _ in tf.range(num_games): + with tf.control_dependencies([sync_counter]): + obs = cart_pole_env.reset() + with tf.control_dependencies([obs]): + sync_counter += 1 + + game_rewards = [] + ag.set_element_type(game_rewards, tf.float32) + + for step in tf.range(max_steps_per_game): + logits, actions = self(obs) # pylint:disable=not-callable + logits = tf.reshape(logits, ()) + actions = tf.reshape(actions, ()) + + labels = 1.0 - tf.cast(actions, tf.float32) + loss = tf.nn.sigmoid_cross_entropy_with_logits( + labels=labels, logits=logits) + grads = tf.gradients(loss, var_list) + + for i in range(len(grads)): + grad_list[i].append(grads[i]) + + with tf.control_dependencies([sync_counter]): + obs, reward, done = cart_pole_env.step(actions) + with tf.control_dependencies([obs]): + sync_counter += 1 + obs = tf.reshape(obs, (1, 4)) + + game_rewards.append(reward) + if reward < 0.1 or done: + step_counts.append(step + 1) + break + + discounted_rewards = graph_append_discounted_rewards( + discounted_rewards, game_rewards, discount_rate) + + discounted_rewards = ag.stack(discounted_rewards) + discounted_rewards.set_shape((None,)) + mean, variance = tf.nn.moments(discounted_rewards, [0]) + normalized_rewards = (discounted_rewards - mean) / tf.sqrt(variance) + + for i in range(len(grad_list)): + g = ag.stack(grad_list[i]) + + # This block just adjusts the shapes to match for multiplication. + r = normalized_rewards + if r.shape.ndims < g.shape.ndims: + r = tf.expand_dims(r, -1) + if r.shape.ndims < g.shape.ndims: + r = tf.expand_dims(r, -1) + + grad_list[i] = tf.reduce_mean(g * r, axis=0) + + optimizer.apply_gradients( + zip(grad_list, var_list), global_step=tf.train.get_global_step()) + + return ag.stack(step_counts) + + +@ag.convert() +def graph_train_model(policy_network, cart_pole_env, optimizer, iterations): + """Trains the policy network for a given number of iterations.""" + i = tf.constant(0) + mean_steps_per_iteration = [] + ag.set_element_type(mean_steps_per_iteration, tf.int32) + + while i < iterations: + steps_per_game = policy_network.train( + cart_pole_env, + optimizer, + discount_rate=0.95, + num_games=20, + max_steps_per_game=200) + mean_steps_per_iteration.append(tf.reduce_mean(steps_per_game)) + i += 1 + + return ag.stack(mean_steps_per_iteration) + + +class GraphGymCartpoleEnv(object): + """An env backed by OpenAI Gym's CartPole environment. + + Used to confirm a functional model only. + """ + + def __init__(self): + cart_pole_env = gym.make('CartPole-v1') + cart_pole_env.seed(0) + cart_pole_env.reset() + self.env = cart_pole_env + + def reset(self): + obs = ag.utils.wrap_py_func(self.env.reset, tf.float64, ()) + obs = tf.reshape(obs, (1, 4)) + obs = tf.cast(obs, tf.float32) + return obs + + def step(self, actions): + + def take_one_step(actions): + obs, reward, done, _ = self.env.step(actions) + obs = obs.astype(np.float32) + reward = np.float32(reward) + return obs, reward, done + + return ag.utils.wrap_py_func(take_one_step, + (tf.float32, tf.float32, tf.bool), (actions,)) + + +class GraphRandomCartpoleEnv(object): + """An environment that returns random actions and never finishes. + + Used during benchmarking, it will cause training to run a constant number of + steps. + """ + + def reset(self): + return tf.random.normal((1, 4)) + + def step(self, actions): + with tf.control_dependencies([actions]): + random_obs = tf.random.normal((1, 4)) + fixed_reward = tf.constant(0.001) + done = tf.constant(False) + return random_obs, fixed_reward, done + + +# +# Eager implementation +# + + +def eager_append_discounted_rewards(discounted_rewards, rewards, discount_rate): + cdr = 0.0 + reverse_discounted = [] + + for i in range(len(rewards) - 1, -1, -1): + cdr = cdr * discount_rate + rewards[i] + reverse_discounted.append(cdr) + + discounted_rewards.extend(reversed(reverse_discounted)) + return discounted_rewards + + +class EagerPolicyNetwork(tf.keras.Model): + """Policy network for the cart-pole reinforcement learning problem. + + The forward path of the network takes an observation from the cart-pole + environment (length-4 vector) and outputs an action. + """ + + def __init__(self, hidden_size): + super(EagerPolicyNetwork, self).__init__() + self._hidden_layer = tf.keras.layers.Dense( + hidden_size, activation=tf.nn.elu) + self._output_layer = tf.keras.layers.Dense(1) + + def call(self, inputs): + """Calculates logits and action. + + Args: + inputs: Observations from a step in the cart-pole environment, of shape + `(batch_size, input_size)` + + Returns: + logits: the logits output by the output layer. This can be viewed as the + likelihood vales of choosing the left (0) action. Shape: + `(batch_size, 1)`. + actions: randomly selected actions ({0, 1}) based on the logits. Shape: + `(batch_size, 1)`. + """ + hidden = self._hidden_layer(inputs) + logits = self._output_layer(hidden) + + left_prob = tf.nn.sigmoid(logits) + action_probs = tf.concat([left_prob, 1.0 - left_prob], 1) + + self._grad_fn = eager.implicit_gradients( + self._get_cross_entropy_and_save_actions) + + actions = tf.multinomial(tf.log(action_probs), 1) + return logits, actions + + def _get_cross_entropy_and_save_actions(self, inputs): + logits, actions = self(inputs) # pylint:disable=not-callable + self._current_actions = actions + labels = 1.0 - tf.cast(actions, tf.float32) + return tf.nn.sigmoid_cross_entropy_with_logits(labels=labels, logits=logits) + + def train(self, cart_pole_env, optimizer, discount_rate, num_games, + max_steps_per_game): + grad_list = None + + step_counts = [] + discounted_rewards = [] + + for _ in range(num_games): + obs = cart_pole_env.reset() + + game_rewards = [] + + for step in range(max_steps_per_game): + grads_and_vars = self._grad_fn(tf.constant([obs], dtype=tf.float32)) + grads, var_list = zip(*grads_and_vars) + actions = self._current_actions.numpy()[0][0] + + if grad_list is None: + grad_list = [[g] for g in grads] + else: + for i in range(len(grads)): + grad_list[i].append(grads[i]) + + obs, reward, done = cart_pole_env.step(actions) + + game_rewards.append(reward) + if reward < 0.1 or done: + step_counts.append(step + 1) + break + + discounted_rewards = eager_append_discounted_rewards( + discounted_rewards, game_rewards, discount_rate) + + discounted_rewards = tf.stack(discounted_rewards) + mean, variance = tf.nn.moments(discounted_rewards, [0]) + normalized_rewards = (discounted_rewards - mean) / tf.sqrt(variance) + + for i in range(len(grad_list)): + g = tf.stack(grad_list[i]) + + r = normalized_rewards + while r.shape.ndims < g.shape.ndims: + r = tf.expand_dims(r, -1) + + grad_list[i] = tf.reduce_mean(g * r, axis=0) + + optimizer.apply_gradients( + zip(grad_list, var_list), global_step=tf.train.get_global_step()) + + return tf.stack(step_counts) + + +def eager_train_model(policy_network, cart_pole_env, optimizer, iterations): + """Trains the policy network for a given number of iterations.""" + mean_steps_per_iteration = [] + + for _ in range(iterations): + steps_per_game = policy_network.train( + cart_pole_env, + optimizer, + discount_rate=0.95, + num_games=20, + max_steps_per_game=200) + mean_steps_per_iteration.append(tf.reduce_mean(steps_per_game)) + + return mean_steps_per_iteration + + +class EagerGymCartpoleEnv(object): + """An env backed by OpenAI Gym's CartPole environment. + + Used to confirm a functional model only. + """ + + def __init__(self): + cart_pole_env = gym.make('CartPole-v1') + cart_pole_env.seed(0) + cart_pole_env.reset() + self.env = cart_pole_env + + def reset(self): + return self.env.reset() + + def step(self, actions): + obs, reward, done, _ = self.env.step(actions) + return obs, reward, done + + +class EagerRandomCartpoleEnv(object): + """An environment that returns random actions and never finishes. + + Used during benchmarking, it will cause training to run a constant number of + steps. + """ + + def reset(self): + return np.random.normal(size=(4,)) + + def step(self, actions): + with tf.control_dependencies([actions]): + random_obs = np.random.normal(size=(4,)) + fixed_reward = 0.001 + done = False + return random_obs, fixed_reward, done + + +def graph_demo_training(): + """Not used in the benchmark. Used to confirm a functional model.""" + with tf.Graph().as_default(): + tf.set_random_seed(0) + + network = GraphPolicyNetwork(hidden_size=5) + network.build((1, 4)) + env = GraphGymCartpoleEnv() + opt = tf.train.AdamOptimizer(0.05) + + train_ops = graph_train_model(network, env, opt, iterations=5) + + with tf.Session() as sess: + sess.run(tf.global_variables_initializer()) + sess.run(tf.local_variables_initializer()) + steps_per_iteration = sess.run(train_ops) + for i, steps in enumerate(steps_per_iteration): + print('Step {} iterations: {}'.format(i, steps)) + + +def eager_demo_training(): + with context.eager_mode(): + network = EagerPolicyNetwork(hidden_size=5) + network.build((1, 4)) + env = EagerGymCartpoleEnv() + opt = tf.train.AdamOptimizer(0.05) + + steps_per_iteration = eager_train_model(network, env, opt, iterations=5) + for i, steps in enumerate(steps_per_iteration): + print('Step {} iterations: {}'.format(i, steps)) + + +class RLCartPoleBenchmark(benchmark_base.ReportingBenchmark): + """Actual benchmark. + + Trains the RL agent a fixed number of times, on random environments that + result in constant number of steps. + """ + + def benchmark_cartpole(self): + + def train_session(sess, ops): + return lambda: sess.run(ops) + + def train_eager(network, env, opt): + return lambda: eager_train_model(network, env, opt, iterations=10) + + for model_size in (10, 100, 1000): + with tf.Graph().as_default(): + network = GraphPolicyNetwork(hidden_size=model_size) + network.build((1, 4)) + env = GraphRandomCartpoleEnv() + opt = tf.train.AdamOptimizer(0.05) + train_ops = graph_train_model(network, env, opt, iterations=10) + + with tf.Session() as sess: + sess.run(tf.global_variables_initializer()) + sess.run(tf.local_variables_initializer()) + + self.time_execution(('cartpole', 'autograph', model_size), + train_session(sess, train_ops), 20) + + with context.eager_mode(): + network = EagerPolicyNetwork(hidden_size=model_size) + network.build((1, 4)) + env = EagerRandomCartpoleEnv() + opt = tf.train.AdamOptimizer(0.05) + + self.time_execution(('cartpole', 'eager', model_size), + train_eager(network, env, opt), 20) + + +if __name__ == '__main__': + tf.test.main() diff --git a/tensorflow/contrib/batching/python/ops/batch_ops.py b/tensorflow/contrib/batching/python/ops/batch_ops.py index 55faad983f..3e4d0dc1ce 100644 --- a/tensorflow/contrib/batching/python/ops/batch_ops.py +++ b/tensorflow/contrib/batching/python/ops/batch_ops.py @@ -18,8 +18,9 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -from tensorflow.python.framework import function +from tensorflow.python.eager import function from tensorflow.python.framework import ops +from tensorflow.python.framework import tensor_spec from tensorflow.python.ops import gen_batch_ops # go/tf-wildcard-import # pylint: disable=wildcard-import @@ -101,12 +102,15 @@ def batch_function(num_batch_threads, def decorator(fn): # pylint: disable=missing-docstring def decorated(*args): # pylint: disable=missing-docstring - types = [arg.dtype for arg in args] - @function.Defun(*types) + @function.defun() def computation(*computation_args): return fn(*computation_args) + computation = computation.get_concrete_function( + *[tensor_spec.TensorSpec(dtype=x.dtype, shape=x.shape, name=str(i)) + for i, x in enumerate(args)]) + with ops.name_scope("batch") as name: for a in args: if not isinstance(a, ops.Tensor): @@ -123,7 +127,7 @@ def batch_function(num_batch_threads, f=computation, in_tensors=list(args), captured_tensors=computation.captured_inputs, - Tout=[o.type for o in computation.definition.signature.output_arg]) + Tout=[o.dtype for o in computation.outputs]) return decorated diff --git a/tensorflow/contrib/batching/python/ops/batch_ops_test.py b/tensorflow/contrib/batching/python/ops/batch_ops_test.py index 01ee8703a9..9109b9c1c9 100644 --- a/tensorflow/contrib/batching/python/ops/batch_ops_test.py +++ b/tensorflow/contrib/batching/python/ops/batch_ops_test.py @@ -219,6 +219,7 @@ class BatchOpsTest(test.TestCase): @batch_ops.batch_function(1, 10, 100000) def computation(in_t): + self.assertTrue(in_t.shape is not None) return in_t + 1 inp = array_ops.placeholder(dtype=dtypes.int32, shape=[1]) diff --git a/tensorflow/contrib/bayesflow/python/kernel_tests/monte_carlo_test.py b/tensorflow/contrib/bayesflow/python/kernel_tests/monte_carlo_test.py index 13215ffabf..8b6ed9f041 100644 --- a/tensorflow/contrib/bayesflow/python/kernel_tests/monte_carlo_test.py +++ b/tensorflow/contrib/bayesflow/python/kernel_tests/monte_carlo_test.py @@ -81,7 +81,7 @@ class ExpectationImportanceSampleTest(test.TestCase): # Compute E_p[X_1 * X_2 > 0], with X_i the ith component of X ~ p(x). # Should equal 1/2 because p is a spherical Gaussian centered at (0, 0). def indicator(x): - x1_times_x2 = math_ops.reduce_prod(x, reduction_indices=[-1]) + x1_times_x2 = math_ops.reduce_prod(x, axis=[-1]) return 0.5 * (math_ops.sign(x1_times_x2) + 1.0) prob = mc.expectation_importance_sampler( diff --git a/tensorflow/contrib/bayesflow/python/ops/monte_carlo_impl.py b/tensorflow/contrib/bayesflow/python/ops/monte_carlo_impl.py index 18d40fc1df..e83a548511 100644 --- a/tensorflow/contrib/bayesflow/python/ops/monte_carlo_impl.py +++ b/tensorflow/contrib/bayesflow/python/ops/monte_carlo_impl.py @@ -353,12 +353,12 @@ def expectation(f, samples, log_prob=None, use_reparametrization=True, def _sample_mean(values): """Mean over sample indices. In this module this is always [0].""" - return math_ops.reduce_mean(values, reduction_indices=[0]) + return math_ops.reduce_mean(values, axis=[0]) def _sample_max(values): """Max over sample indices. In this module this is always [0].""" - return math_ops.reduce_max(values, reduction_indices=[0]) + return math_ops.reduce_max(values, axis=[0]) def _get_samples(dist, z, n, seed): diff --git a/tensorflow/contrib/bigtable/README.md b/tensorflow/contrib/bigtable/README.md index 2c44abed5e..79052bee35 100644 --- a/tensorflow/contrib/bigtable/README.md +++ b/tensorflow/contrib/bigtable/README.md @@ -51,25 +51,18 @@ BIGTABLE_TABLE_NAME = '' PREFIX = 'train-' def main(): + tf.enable_eager_execution() + client = tf.contrib.cloud.BigtableClient(GCP_PROJECT_ID, BIGTABLE_INSTANCE_ID) table = client.table(BIGTABLE_TABLE_NAME) dataset = table.keys_by_prefix_dataset(PREFIX) - iterator = dataset.make_initializable_iterator() - get_next_op = iterator.get_next() - with tf.Session() as sess: - print('Initializing the iterator.') - sess.run(iterator.initializer) - print('Retrieving rows:') - row_index = 0 - while True: - try: - row_key = sess.run(get_next_op) - print('Row key %d: %s' % (row_index, row_key)) - row_index += 1 - except tf.errors.OutOfRangeError: - print('Finished reading data!') - break + print('Retrieving rows:') + row_index = 0 + for row_key in dataset: + print('Row key %d: %s' % (row_index, row_key)) + row_index += 1 + print('Finished reading data!') if __name__ == '__main__': main() diff --git a/tensorflow/contrib/bigtable/kernels/test_kernels/bigtable_test_client.cc b/tensorflow/contrib/bigtable/kernels/test_kernels/bigtable_test_client.cc index f083ce6f44..e95dc57718 100644 --- a/tensorflow/contrib/bigtable/kernels/test_kernels/bigtable_test_client.cc +++ b/tensorflow/contrib/bigtable/kernels/test_kernels/bigtable_test_client.cc @@ -366,6 +366,39 @@ BigtableTestClient::MutateRows( return MakeUnique(request.entries_size()); } +std::unique_ptr> +BigtableTestClient::AsyncMutateRow( + grpc::ClientContext* context, + google::bigtable::v2::MutateRowRequest const& request, + grpc::CompletionQueue* cq) { + LOG(WARNING) << "Call to InMemoryDataClient::" << __func__ + << "(); this will likely cause a crash!"; + return nullptr; +} + +std::unique_ptr<::grpc::ClientAsyncReaderInterface< + ::google::bigtable::v2::SampleRowKeysResponse>> +BigtableTestClient::AsyncSampleRowKeys( + ::grpc::ClientContext* context, + const ::google::bigtable::v2::SampleRowKeysRequest& request, + ::grpc::CompletionQueue* cq, void* tag) { + LOG(WARNING) << "Call to InMemoryDataClient::" << __func__ + << "(); this will likely cause a crash!"; + return nullptr; +} + +std::unique_ptr<::grpc::ClientAsyncReaderInterface< + ::google::bigtable::v2::MutateRowsResponse>> +BigtableTestClient::AsyncMutateRows( + ::grpc::ClientContext* context, + const ::google::bigtable::v2::MutateRowsRequest& request, + ::grpc::CompletionQueue* cq, void* tag) { + LOG(WARNING) << "Call to InMemoryDataClient::" << __func__ + << "(); this will likely cause a crash!"; + return nullptr; +} + std::shared_ptr BigtableTestClient::Channel() { LOG(WARNING) << "Call to InMemoryDataClient::Channel(); this will likely " "cause a crash!"; diff --git a/tensorflow/contrib/bigtable/kernels/test_kernels/bigtable_test_client.h b/tensorflow/contrib/bigtable/kernels/test_kernels/bigtable_test_client.h index dac2b16a21..c4a1f06bc5 100644 --- a/tensorflow/contrib/bigtable/kernels/test_kernels/bigtable_test_client.h +++ b/tensorflow/contrib/bigtable/kernels/test_kernels/bigtable_test_client.h @@ -61,6 +61,25 @@ class BigtableTestClient : public ::google::cloud::bigtable::DataClient { MutateRows(grpc::ClientContext* context, google::bigtable::v2::MutateRowsRequest const& request) override; + std::unique_ptr> + AsyncMutateRow(grpc::ClientContext* context, + google::bigtable::v2::MutateRowRequest const& request, + grpc::CompletionQueue* cq) override; + + std::unique_ptr<::grpc::ClientAsyncReaderInterface< + ::google::bigtable::v2::SampleRowKeysResponse>> + AsyncSampleRowKeys( + ::grpc::ClientContext* context, + const ::google::bigtable::v2::SampleRowKeysRequest& request, + ::grpc::CompletionQueue* cq, void* tag) override; + + std::unique_ptr<::grpc::ClientAsyncReaderInterface< + ::google::bigtable::v2::MutateRowsResponse>> + AsyncMutateRows(::grpc::ClientContext* context, + const ::google::bigtable::v2::MutateRowsRequest& request, + ::grpc::CompletionQueue* cq, void* tag) override; + std::shared_ptr Channel() override; private: diff --git a/tensorflow/contrib/bigtable/python/kernel_tests/bigtable_ops_test.py b/tensorflow/contrib/bigtable/python/kernel_tests/bigtable_ops_test.py index 316da9ebe1..197f5578eb 100644 --- a/tensorflow/contrib/bigtable/python/kernel_tests/bigtable_ops_test.py +++ b/tensorflow/contrib/bigtable/python/kernel_tests/bigtable_ops_test.py @@ -57,7 +57,7 @@ class BigtableOpsTest(test.TestCase): sess.run(write_op) def runReadKeyTest(self, read_ds): - itr = read_ds.make_initializable_iterator() + itr = dataset_ops.make_initializable_iterator(read_ds) n = itr.get_next() expected = list(self.COMMON_ROW_KEYS) expected.reverse() @@ -78,7 +78,7 @@ class BigtableOpsTest(test.TestCase): self.runReadKeyTest(self._table.keys_by_range_dataset("r1", "r4")) def runScanTest(self, read_ds): - itr = read_ds.make_initializable_iterator() + itr = dataset_ops.make_initializable_iterator(read_ds) n = itr.get_next() expected_keys = list(self.COMMON_ROW_KEYS) expected_keys.reverse() @@ -120,7 +120,7 @@ class BigtableOpsTest(test.TestCase): def testLookup(self): ds = self._table.keys_by_prefix_dataset("r") ds = ds.apply(self._table.lookup_columns(cf1="c1")) - itr = ds.make_initializable_iterator() + itr = dataset_ops.make_initializable_iterator(ds) n = itr.get_next() expected_keys = list(self.COMMON_ROW_KEYS) expected_values = list(self.COMMON_VALUES) @@ -141,7 +141,7 @@ class BigtableOpsTest(test.TestCase): def testSampleKeys(self): ds = self._table.sample_keys() - itr = ds.make_initializable_iterator() + itr = dataset_ops.make_initializable_iterator(ds) n = itr.get_next() expected_key = self.COMMON_ROW_KEYS[0] with self.cached_session() as sess: @@ -161,7 +161,7 @@ class BigtableOpsTest(test.TestCase): sess.run(n) def runSampleKeyPairsTest(self, ds, expected_key_pairs): - itr = ds.make_initializable_iterator() + itr = dataset_ops.make_initializable_iterator(ds) n = itr.get_next() with self.cached_session() as sess: self._writeCommonValues(sess) @@ -218,7 +218,7 @@ class BigtableOpsTest(test.TestCase): def testSampleKeyPairsPrefixAndStartKey(self): ds = bigtable_api._BigtableSampleKeyPairsDataset( self._table, prefix="r", start="r1", end="") - itr = ds.make_initializable_iterator() + itr = dataset_ops.make_initializable_iterator(ds) with self.cached_session() as sess: with self.assertRaises(errors.InvalidArgumentError): sess.run(itr.initializer) @@ -226,14 +226,14 @@ class BigtableOpsTest(test.TestCase): def testSampleKeyPairsPrefixAndEndKey(self): ds = bigtable_api._BigtableSampleKeyPairsDataset( self._table, prefix="r", start="", end="r3") - itr = ds.make_initializable_iterator() + itr = dataset_ops.make_initializable_iterator(ds) with self.cached_session() as sess: with self.assertRaises(errors.InvalidArgumentError): sess.run(itr.initializer) def testParallelScanPrefix(self): ds = self._table.parallel_scan_prefix(prefix="r", cf1="c1") - itr = ds.make_initializable_iterator() + itr = dataset_ops.make_initializable_iterator(ds) n = itr.get_next() with self.cached_session() as sess: self._writeCommonValues(sess) @@ -251,7 +251,7 @@ class BigtableOpsTest(test.TestCase): def testParallelScanRange(self): ds = self._table.parallel_scan_range(start="r1", end="r4", cf1="c1") - itr = ds.make_initializable_iterator() + itr = dataset_ops.make_initializable_iterator(ds) n = itr.get_next() with self.cached_session() as sess: self._writeCommonValues(sess) diff --git a/tensorflow/contrib/bigtable/python/ops/bigtable_api.py b/tensorflow/contrib/bigtable/python/ops/bigtable_api.py index 7c87b0daeb..9f97934193 100644 --- a/tensorflow/contrib/bigtable/python/ops/bigtable_api.py +++ b/tensorflow/contrib/bigtable/python/ops/bigtable_api.py @@ -222,7 +222,7 @@ class BigtableTable(object): A `tf.data.Dataset`. containing `tf.string` Tensors corresponding to all of the row keys matching that prefix. """ - return _BigtablePrefixKeyDataset(self, prefix) + return dataset_ops.DatasetV1Adapter(_BigtablePrefixKeyDataset(self, prefix)) def sample_keys(self): """Retrieves a sampling of row keys from the Bigtable table. @@ -234,7 +234,7 @@ class BigtableTable(object): Returns: A `tf.data.Dataset` returning string row keys. """ - return _BigtableSampleKeysDataset(self) + return dataset_ops.DatasetV1Adapter(_BigtableSampleKeysDataset(self)) def scan_prefix(self, prefix, probability=None, columns=None, **kwargs): """Retrieves row (including values) from the Bigtable service. @@ -279,7 +279,8 @@ class BigtableTable(object): """ probability = _normalize_probability(probability) normalized = _normalize_columns(columns, kwargs) - return _BigtableScanDataset(self, prefix, "", "", normalized, probability) + return dataset_ops.DatasetV1Adapter( + _BigtableScanDataset(self, prefix, "", "", normalized, probability)) def scan_range(self, start, end, probability=None, columns=None, **kwargs): """Retrieves rows (including values) from the Bigtable service. @@ -324,7 +325,8 @@ class BigtableTable(object): """ probability = _normalize_probability(probability) normalized = _normalize_columns(columns, kwargs) - return _BigtableScanDataset(self, "", start, end, normalized, probability) + return dataset_ops.DatasetV1Adapter( + _BigtableScanDataset(self, "", start, end, normalized, probability)) def parallel_scan_prefix(self, prefix, @@ -380,7 +382,8 @@ class BigtableTable(object): """ probability = _normalize_probability(probability) normalized = _normalize_columns(columns, kwargs) - ds = _BigtableSampleKeyPairsDataset(self, prefix, "", "") + ds = dataset_ops.DatasetV1Adapter( + _BigtableSampleKeyPairsDataset(self, prefix, "", "")) return self._make_parallel_scan_dataset(ds, num_parallel_scans, probability, normalized) @@ -442,7 +445,8 @@ class BigtableTable(object): """ probability = _normalize_probability(probability) normalized = _normalize_columns(columns, kwargs) - ds = _BigtableSampleKeyPairsDataset(self, "", start, end) + ds = dataset_ops.DatasetV1Adapter( + _BigtableSampleKeyPairsDataset(self, "", start, end)) return self._make_parallel_scan_dataset(ds, num_parallel_scans, probability, normalized) diff --git a/tensorflow/contrib/boosted_trees/estimator_batch/BUILD b/tensorflow/contrib/boosted_trees/estimator_batch/BUILD index 14b6fc4ac2..d3b23d949e 100644 --- a/tensorflow/contrib/boosted_trees/estimator_batch/BUILD +++ b/tensorflow/contrib/boosted_trees/estimator_batch/BUILD @@ -132,6 +132,7 @@ py_library( srcs = ["estimator.py"], srcs_version = "PY2AND3", deps = [ + ":custom_loss_head", ":estimator_utils", ":model", "//tensorflow/contrib/boosted_trees:losses", diff --git a/tensorflow/contrib/boosted_trees/estimator_batch/custom_export_strategy.py b/tensorflow/contrib/boosted_trees/estimator_batch/custom_export_strategy.py index a3df272e69..b314b4d74d 100644 --- a/tensorflow/contrib/boosted_trees/estimator_batch/custom_export_strategy.py +++ b/tensorflow/contrib/boosted_trees/estimator_batch/custom_export_strategy.py @@ -41,7 +41,8 @@ def make_custom_export_strategy(name, convert_fn, feature_columns, export_input_fn, - use_core_columns=False): + use_core_columns=False, + feature_engineering_fn=None): """Makes custom exporter of GTFlow tree format. Args: @@ -52,6 +53,7 @@ def make_custom_export_strategy(name, export_input_fn: A function that takes no arguments and returns an `InputFnOps`. use_core_columns: A boolean, whether core feature columns were used. + feature_engineering_fn: Feature eng function to be called on the input. Returns: An `ExportStrategy`. @@ -59,9 +61,12 @@ def make_custom_export_strategy(name, base_strategy = saved_model_export_utils.make_export_strategy( serving_input_fn=export_input_fn, strip_default_attrs=True) input_fn = export_input_fn() + features = input_fn.features + if feature_engineering_fn is not None: + features, _ = feature_engineering_fn(features, labels=None) (sorted_feature_names, dense_floats, sparse_float_indices, _, _, sparse_int_indices, _, _) = gbdt_batch.extract_features( - input_fn.features, feature_columns, use_core_columns) + features, feature_columns, use_core_columns) def export_fn(estimator, export_dir, checkpoint_path=None, eval_result=None): """A wrapper to export to SavedModel, and convert it to other formats.""" diff --git a/tensorflow/contrib/boosted_trees/estimator_batch/dnn_tree_combined_estimator.py b/tensorflow/contrib/boosted_trees/estimator_batch/dnn_tree_combined_estimator.py index ca73e4af2f..358404cd94 100644 --- a/tensorflow/contrib/boosted_trees/estimator_batch/dnn_tree_combined_estimator.py +++ b/tensorflow/contrib/boosted_trees/estimator_batch/dnn_tree_combined_estimator.py @@ -36,7 +36,7 @@ from tensorflow.contrib.learn.python.learn.estimators import estimator from tensorflow.contrib.learn.python.learn.estimators import head as head_lib from tensorflow.python.estimator import estimator as core_estimator from tensorflow.contrib.learn.python.learn.estimators import model_fn -from tensorflow.python.feature_column import feature_column as feature_column_lib +from tensorflow.python.feature_column import feature_column_lib from tensorflow.python.framework import ops from tensorflow.python.ops import array_ops from tensorflow.python.ops import control_flow_ops diff --git a/tensorflow/contrib/boosted_trees/estimator_batch/estimator.py b/tensorflow/contrib/boosted_trees/estimator_batch/estimator.py index 38d19976ef..a178820841 100644 --- a/tensorflow/contrib/boosted_trees/estimator_batch/estimator.py +++ b/tensorflow/contrib/boosted_trees/estimator_batch/estimator.py @@ -18,6 +18,8 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import functools + from tensorflow.contrib.boosted_trees.estimator_batch import model from tensorflow.contrib.boosted_trees.python.utils import losses from tensorflow.contrib.learn.python.learn.estimators import estimator @@ -26,7 +28,8 @@ from tensorflow.python.estimator.canned import head as core_head_lib from tensorflow.python.estimator import estimator as core_estimator from tensorflow.python.ops import math_ops from tensorflow.python.ops.losses import losses as core_losses - +from tensorflow.contrib.boosted_trees.estimator_batch import custom_loss_head +from tensorflow.python.ops import array_ops # ================== Old estimator interface=================================== # The estimators below were designed for old feature columns and old estimator @@ -414,6 +417,108 @@ class GradientBoostedDecisionTreeRanker(estimator.Estimator): config=config, feature_engineering_fn=feature_engineering_fn) +# When using this estimator, make sure to regularize the hessian (at least l2, +# min_node_weight)! +# TODO(nponomareva): extend to take multiple quantiles in one go. +class GradientBoostedDecisionTreeQuantileRegressor(estimator.Estimator): + """An estimator that does quantile regression and returns quantile estimates. + """ + + def __init__(self, + learner_config, + examples_per_layer, + quantiles, + label_dimension=1, + num_trees=None, + feature_columns=None, + weight_column_name=None, + model_dir=None, + config=None, + feature_engineering_fn=None, + logits_modifier_function=None, + center_bias=True, + use_core_libs=False, + output_leaf_index=False, + override_global_step_value=None, + num_quantiles=100): + """Initializes a GradientBoostedDecisionTreeQuantileRegressor instance. + + Args: + learner_config: A config for the learner. + examples_per_layer: Number of examples to accumulate before growing a + layer. It can also be a function that computes the number of examples + based on the depth of the layer that's being built. + quantiles: a list of quantiles for the loss, each between 0 and 1. + label_dimension: Dimension of regression label. This is the size + of the last dimension of the labels `Tensor` (typically, this has shape + `[batch_size, label_dimension]`). When label_dimension>1, it is + recommended to use multiclass strategy diagonal hessian or full hessian. + num_trees: An int, number of trees to build. + feature_columns: A list of feature columns. + weight_column_name: Name of the column for weights, or None if not + weighted. + model_dir: Directory for model exports, etc. + config: `RunConfig` object to configure the runtime settings. + feature_engineering_fn: Feature engineering function. Takes features and + labels which are the output of `input_fn` and returns features and + labels which will be fed into the model. + logits_modifier_function: A modifier function for the logits. + center_bias: Whether a separate tree should be created for first fitting + the bias. + use_core_libs: Whether feature columns and loss are from the core (as + opposed to contrib) version of tensorflow. + output_leaf_index: whether to output leaf indices along with predictions + during inference. The leaf node indexes are available in predictions + dict by the key 'leaf_index'. For example, + result_dict = classifier.predict(...) + for example_prediction_result in result_dict: + # access leaf index list by example_prediction_result["leaf_index"] + # which contains one leaf index per tree + override_global_step_value: If after the training is done, global step + value must be reset to this value. This should be used to reset global + step to a number > number of steps used to train the current ensemble. + For example, the usual way is to train a number of trees and set a very + large number of training steps. When the training is done (number of + trees were trained), this parameter can be used to set the global step + to a large value, making it look like that number of training steps ran. + If None, no override of global step will happen. + num_quantiles: Number of quantiles to build for numeric feature values. + """ + + if len(quantiles) > 1: + raise ValueError('For now, just one quantile per estimator is supported') + + def _quantile_regression_head(quantile): + # Use quantile regression. + head = custom_loss_head.CustomLossHead( + loss_fn=functools.partial( + losses.per_example_quantile_regression_loss, quantile=quantile), + link_fn=array_ops.identity, + logit_dimension=label_dimension) + return head + + learner_config.num_classes = max(2, label_dimension) + + super(GradientBoostedDecisionTreeQuantileRegressor, self).__init__( + model_fn=model.model_builder, + params={ + 'head': _quantile_regression_head(quantiles[0]), + 'feature_columns': feature_columns, + 'learner_config': learner_config, + 'num_trees': num_trees, + 'weight_column_name': weight_column_name, + 'examples_per_layer': examples_per_layer, + 'logits_modifier_function': logits_modifier_function, + 'center_bias': center_bias, + 'use_core_libs': use_core_libs, + 'output_leaf_index': False, + 'override_global_step_value': override_global_step_value, + 'num_quantiles': num_quantiles, + }, + model_dir=model_dir, + config=config, + feature_engineering_fn=feature_engineering_fn) + # ================== New Estimator interface=================================== # The estimators below use new core Estimator interface and must be used with # new feature columns and heads. @@ -437,12 +542,42 @@ def core_multiclass_head( # pylint:disable=protected-access head_fn = core_head_lib._multi_class_head_with_softmax_cross_entropy_loss( - n_classes=n_classes, loss_fn=loss_fn, loss_reduction=loss_reduction) + n_classes=n_classes, + loss_fn=loss_fn, + loss_reduction=loss_reduction, + weight_column=weight_column) # pylint:enable=protected-access return head_fn +# For quantile regression, use this head with Core..Estimator, or use +# Core..QuantileRegressor directly, +def core_quantile_regression_head( + quantiles, + label_dimension=1, + weight_column=None, + loss_reduction=core_losses.Reduction.SUM_OVER_NONZERO_WEIGHTS): + """Core head for quantile regression problems.""" + + def loss_fn(labels, logits): + result = losses.per_example_quantile_regression_loss( + labels=labels, + predictions=logits, + weights=weight_column, + quantile=quantiles) + return result[0] + + # pylint:disable=protected-access + head_fn = core_head_lib._regression_head( + label_dimension=label_dimension, + loss_fn=loss_fn, + loss_reduction=loss_reduction, + weight_column=weight_column) + # pylint:enable=protected-access + return head_fn + + class CoreGradientBoostedDecisionTreeEstimator(core_estimator.Estimator): """An estimator using gradient boosted decision trees. @@ -606,3 +741,104 @@ class CoreGradientBoostedDecisionTreeRanker(core_estimator.Estimator): super(CoreGradientBoostedDecisionTreeRanker, self).__init__( model_fn=_model_fn, model_dir=model_dir, config=config) + + +# When using this estimator, make sure to regularize the hessian (at least l2, +# min_node_weight)! +# TODO(nponomareva): extend to take multiple quantiles in one go. +class CoreGradientBoostedDecisionTreeQuantileRegressor( + core_estimator.Estimator): + """An estimator that does quantile regression and returns quantile estimates. + """ + + def __init__(self, + learner_config, + examples_per_layer, + quantiles, + label_dimension=1, + num_trees=None, + feature_columns=None, + weight_column_name=None, + model_dir=None, + config=None, + label_keys=None, + feature_engineering_fn=None, + logits_modifier_function=None, + center_bias=True, + output_leaf_index=False, + num_quantiles=100): + """Initializes a core version of GradientBoostedDecisionTreeEstimator. + + Args: + learner_config: A config for the learner. + examples_per_layer: Number of examples to accumulate before growing a + layer. It can also be a function that computes the number of examples + based on the depth of the layer that's being built. + quantiles: a list of quantiles for the loss, each between 0 and 1. + label_dimension: Dimension of regression label. This is the size + of the last dimension of the labels `Tensor` (typically, this has shape + `[batch_size, label_dimension]`). When label_dimension>1, it is + recommended to use multiclass strategy diagonal hessian or full hessian. + num_trees: An int, number of trees to build. + feature_columns: A list of feature columns. + weight_column_name: Name of the column for weights, or None if not + weighted. + model_dir: Directory for model exports, etc. + config: `RunConfig` object to configure the runtime settings. + label_keys: Optional list of strings with size `[n_classes]` defining the + label vocabulary. Only supported for `n_classes` > 2. + feature_engineering_fn: Feature engineering function. Takes features and + labels which are the output of `input_fn` and returns features and + labels which will be fed into the model. + logits_modifier_function: A modifier function for the logits. + center_bias: Whether a separate tree should be created for first fitting + the bias. + output_leaf_index: whether to output leaf indices along with predictions + during inference. The leaf node indexes are available in predictions + dict by the key 'leaf_index'. For example, + result_dict = classifier.predict(...) + for example_prediction_result in result_dict: + # access leaf index list by example_prediction_result["leaf_index"] + # which contains one leaf index per tree + num_quantiles: Number of quantiles to build for numeric feature values. + """ + if len(quantiles) > 1: + raise ValueError('For now, just one quantile per estimator is supported') + + def _model_fn(features, labels, mode, config): + return model.model_builder( + features=features, + labels=labels, + mode=mode, + config=config, + params={ + 'head': + core_quantile_regression_head( + quantiles[0], label_dimension=label_dimension), + 'feature_columns': + feature_columns, + 'learner_config': + learner_config, + 'num_trees': + num_trees, + 'weight_column_name': + weight_column_name, + 'examples_per_layer': + examples_per_layer, + 'center_bias': + center_bias, + 'logits_modifier_function': + logits_modifier_function, + 'use_core_libs': + True, + 'output_leaf_index': + output_leaf_index, + 'override_global_step_value': + None, + 'num_quantiles': + num_quantiles, + }, + output_type=model.ModelBuilderOutputType.ESTIMATOR_SPEC) + + super(CoreGradientBoostedDecisionTreeQuantileRegressor, self).__init__( + model_fn=_model_fn, model_dir=model_dir, config=config) diff --git a/tensorflow/contrib/boosted_trees/estimator_batch/estimator_test.py b/tensorflow/contrib/boosted_trees/estimator_batch/estimator_test.py index c155128c0e..ee052ac603 100644 --- a/tensorflow/contrib/boosted_trees/estimator_batch/estimator_test.py +++ b/tensorflow/contrib/boosted_trees/estimator_batch/estimator_test.py @@ -25,6 +25,7 @@ from tensorflow.contrib.boosted_trees.proto import learner_pb2 from tensorflow.contrib.layers.python.layers import feature_column as contrib_feature_column from tensorflow.contrib.learn.python.learn.estimators import run_config from tensorflow.python.estimator.canned import head as head_lib +from tensorflow.python.estimator.inputs import numpy_io from tensorflow.python.feature_column import feature_column_lib as core_feature_column from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes @@ -47,8 +48,8 @@ def _multiclass_train_input_fn(): features = { "x": constant_op.constant([[2.], [1.], [1.], [5.], [3.5], [4.6], [3.5]]) } - label = constant_op.constant( - [[1], [0], [0], [2], [2], [0], [1]], dtype=dtypes.int32) + label = constant_op.constant([[1], [0], [0], [2], [2], [0], [1]], + dtype=dtypes.int32) return features, label @@ -77,6 +78,59 @@ def _infer_ranking_train_input_fn(): return features, None +_QUANTILE_REGRESSION_SIZE = 1000 + + +def _quantile_regression_input_fns(two_dimension=False): + # The data generation is taken from + # http://scikit-learn.org/stable/auto_examples/ensemble/plot_gradient_boosting_quantile.html + np.random.seed(1) + + def f(x): + """The function to predict.""" + return x * np.sin(x) + + def g(x): + """The function to predict.""" + return x * np.cos(x) + + # Training data. + x = np.atleast_2d(np.random.uniform(0, 10.0, + size=_QUANTILE_REGRESSION_SIZE)).T + x = x.astype(np.float32) + + # Labels. + if not two_dimension: + y = f(x).ravel() + else: + y = np.column_stack((f(x).ravel(), g(x).ravel())) + + # Add random noise. + dy = 1.5 + 1.0 * np.random.random(y.shape) + noise = np.random.normal(0, dy) + y += noise + y_original = y.astype(np.float32) + if not two_dimension: + y = y.reshape(_QUANTILE_REGRESSION_SIZE, 1) + + train_input_fn = numpy_io.numpy_input_fn( + x=x, + y=y, + batch_size=_QUANTILE_REGRESSION_SIZE, + num_epochs=None, + shuffle=True) + + # Test on the training data to make sure the predictions are calibrated. + test_input_fn = numpy_io.numpy_input_fn( + x=x, + y=y, + batch_size=_QUANTILE_REGRESSION_SIZE, + num_epochs=1, + shuffle=False) + + return train_input_fn, test_input_fn, y_original + + class BoostedTreeEstimatorTest(test_util.TensorFlowTestCase): def setUp(self): @@ -341,6 +395,130 @@ class BoostedTreeEstimatorTest(test_util.TensorFlowTestCase): for prediction_dict in result_iter: self.assertTrue("classes" in prediction_dict) + # One dimensional quantile regression. + def testQuantileRegression(self): + learner_config = learner_pb2.LearnerConfig() + learner_config.num_classes = 2 + learner_config.constraints.max_tree_depth = 3 + learner_config.growing_mode = learner_pb2.LearnerConfig.WHOLE_TREE + learner_config.constraints.min_node_weight = 1 / _QUANTILE_REGRESSION_SIZE + learner_config.regularization.l2 = 1.0 / _QUANTILE_REGRESSION_SIZE + learner_config.regularization.l1 = 1.0 / _QUANTILE_REGRESSION_SIZE + learner_config.regularization.tree_complexity = ( + 1.0 / _QUANTILE_REGRESSION_SIZE) + + train_input_fn, test_input_fn, y = _quantile_regression_input_fns() + + # 95% percentile. + model_upper = estimator.GradientBoostedDecisionTreeQuantileRegressor( + quantiles=[0.95], + learner_config=learner_config, + num_trees=100, + examples_per_layer=_QUANTILE_REGRESSION_SIZE, + center_bias=False) + + model_upper.fit(input_fn=train_input_fn, steps=1000) + result_iter = model_upper.predict(input_fn=test_input_fn) + upper = [] + for prediction_dict in result_iter: + upper.append(prediction_dict["scores"]) + + frac_below_upper = round(1. * np.count_nonzero(upper > y) / len(y), 3) + # +/- 3% + self.assertTrue(frac_below_upper >= 0.92) + self.assertTrue(frac_below_upper <= 0.98) + + train_input_fn, test_input_fn, _ = _quantile_regression_input_fns() + model_lower = estimator.GradientBoostedDecisionTreeQuantileRegressor( + quantiles=[0.05], + learner_config=learner_config, + num_trees=100, + examples_per_layer=_QUANTILE_REGRESSION_SIZE, + center_bias=False) + + model_lower.fit(input_fn=train_input_fn, steps=1000) + result_iter = model_lower.predict(input_fn=test_input_fn) + lower = [] + for prediction_dict in result_iter: + lower.append(prediction_dict["scores"]) + + frac_above_lower = round(1. * np.count_nonzero(lower < y) / len(y), 3) + # +/- 3% + self.assertTrue(frac_above_lower >= 0.92) + self.assertTrue(frac_above_lower <= 0.98) + + # Multi-dimensional quantile regression. + def testQuantileRegressionMultiDimLabel(self): + learner_config = learner_pb2.LearnerConfig() + learner_config.num_classes = 2 + learner_config.constraints.max_tree_depth = 3 + learner_config.growing_mode = learner_pb2.LearnerConfig.WHOLE_TREE + learner_config.constraints.min_node_weight = 1 / _QUANTILE_REGRESSION_SIZE + learner_config.regularization.l2 = 1.0 / _QUANTILE_REGRESSION_SIZE + learner_config.regularization.l1 = 1.0 / _QUANTILE_REGRESSION_SIZE + learner_config.regularization.tree_complexity = ( + 1.0 / _QUANTILE_REGRESSION_SIZE) + + train_input_fn, test_input_fn, y = _quantile_regression_input_fns( + two_dimension=True) + + # 95% percentile. + model_upper = estimator.GradientBoostedDecisionTreeQuantileRegressor( + quantiles=[0.95], + learner_config=learner_config, + label_dimension=2, + num_trees=100, + examples_per_layer=_QUANTILE_REGRESSION_SIZE, + center_bias=False) + + model_upper.fit(input_fn=train_input_fn, steps=1000) + result_iter = model_upper.predict(input_fn=test_input_fn) + upper = [] + for prediction_dict in result_iter: + upper.append(prediction_dict["scores"]) + + count_below_upper = np.count_nonzero(upper > y, axis=0) + count_both_below_upper = np.count_nonzero(np.prod(upper > y, axis=1)) + frac_below_upper_0 = round(1. * count_below_upper[0] / len(y), 3) + frac_below_upper_1 = round(1. * count_below_upper[1] / len(y), 3) + frac_both_below_upper = round(1. * count_both_below_upper / len(y), 3) + # +/- 3% + self.assertTrue(frac_below_upper_0 >= 0.92) + self.assertTrue(frac_below_upper_0 <= 0.98) + self.assertTrue(frac_below_upper_1 >= 0.92) + self.assertTrue(frac_below_upper_1 <= 0.98) + self.assertTrue(frac_both_below_upper >= 0.92) + self.assertTrue(frac_both_below_upper <= 0.98) + + train_input_fn, test_input_fn, _ = _quantile_regression_input_fns( + two_dimension=True) + model_lower = estimator.GradientBoostedDecisionTreeQuantileRegressor( + quantiles=[0.05], + learner_config=learner_config, + label_dimension=2, + num_trees=100, + examples_per_layer=_QUANTILE_REGRESSION_SIZE, + center_bias=False) + + model_lower.fit(input_fn=train_input_fn, steps=1000) + result_iter = model_lower.predict(input_fn=test_input_fn) + lower = [] + for prediction_dict in result_iter: + lower.append(prediction_dict["scores"]) + + count_above_lower = np.count_nonzero(lower < y, axis=0) + count_both_aboce_lower = np.count_nonzero(np.prod(lower < y, axis=1)) + frac_above_lower_0 = round(1. * count_above_lower[0] / len(y), 3) + frac_above_lower_1 = round(1. * count_above_lower[1] / len(y), 3) + frac_both_above_lower = round(1. * count_both_aboce_lower / len(y), 3) + # +/- 3% + self.assertTrue(frac_above_lower_0 >= 0.92) + self.assertTrue(frac_above_lower_0 <= 0.98) + self.assertTrue(frac_above_lower_1 >= 0.92) + self.assertTrue(frac_above_lower_1 <= 0.98) + self.assertTrue(frac_both_above_lower >= 0.92) + self.assertTrue(frac_both_above_lower <= 0.98) + class CoreGradientBoostedDecisionTreeEstimators(test_util.TensorFlowTestCase): @@ -489,8 +667,8 @@ class CoreGradientBoostedDecisionTreeEstimators(test_util.TensorFlowTestCase): feature_columns = [ core_feature_column.weighted_categorical_column( - categorical_column=core_feature_column. - categorical_column_with_vocabulary_list( + categorical_column=core_feature_column + .categorical_column_with_vocabulary_list( key="word", vocabulary_list=["the", "cat", "dog"]), weight_feature_key="weight") ] @@ -509,8 +687,8 @@ class CoreGradientBoostedDecisionTreeEstimators(test_util.TensorFlowTestCase): # Weights for the words are 5 - cat, 6- dog and 1 -the. features_dict["word"] = sparse_tensor.SparseTensor( indices=[[0, 0], [0, 1], [1, 0], [3, 0]], - values=constant_op.constant( - ["the", "cat", "dog", "the"], dtype=dtypes.string), + values=constant_op.constant(["the", "cat", "dog", "the"], + dtype=dtypes.string), dense_shape=[4, 3]) features_dict["weight"] = sparse_tensor.SparseTensor( indices=[[0, 0], [0, 1], [1, 0], [3, 0]], @@ -534,6 +712,132 @@ class CoreGradientBoostedDecisionTreeEstimators(test_util.TensorFlowTestCase): est.evaluate(input_fn=input_fn, steps=1) est.predict(input_fn=input_fn) + # One dimensional quantile regression. + def testQuantileRegression(self): + learner_config = learner_pb2.LearnerConfig() + learner_config.num_classes = 2 + learner_config.constraints.max_tree_depth = 3 + learner_config.growing_mode = learner_pb2.LearnerConfig.WHOLE_TREE + learner_config.constraints.min_node_weight = 1 / _QUANTILE_REGRESSION_SIZE + learner_config.regularization.l2 = 1.0 / _QUANTILE_REGRESSION_SIZE + learner_config.regularization.l1 = 1.0 / _QUANTILE_REGRESSION_SIZE + learner_config.regularization.tree_complexity = ( + 1.0 / _QUANTILE_REGRESSION_SIZE) + + train_input_fn, test_input_fn, y = _quantile_regression_input_fns() + y = y.reshape(_QUANTILE_REGRESSION_SIZE, 1) + + # 95% percentile. + model_upper = estimator.CoreGradientBoostedDecisionTreeQuantileRegressor( + quantiles=[0.95], + learner_config=learner_config, + num_trees=100, + examples_per_layer=_QUANTILE_REGRESSION_SIZE, + center_bias=False) + + model_upper.train(input_fn=train_input_fn, steps=1000) + result_iter = model_upper.predict(input_fn=test_input_fn) + upper = [] + for prediction_dict in result_iter: + upper.append(prediction_dict["predictions"]) + + frac_below_upper = round(1. * np.count_nonzero(upper > y) / len(y), 3) + # +/- 3% + self.assertTrue(frac_below_upper >= 0.92) + self.assertTrue(frac_below_upper <= 0.98) + + train_input_fn, test_input_fn, _ = _quantile_regression_input_fns() + model_lower = estimator.CoreGradientBoostedDecisionTreeQuantileRegressor( + quantiles=[0.05], + learner_config=learner_config, + num_trees=100, + examples_per_layer=_QUANTILE_REGRESSION_SIZE, + center_bias=False) + + model_lower.train(input_fn=train_input_fn, steps=1000) + result_iter = model_lower.predict(input_fn=test_input_fn) + lower = [] + for prediction_dict in result_iter: + lower.append(prediction_dict["predictions"]) + + frac_above_lower = round(1. * np.count_nonzero(lower < y) / len(y), 3) + # +/- 3% + self.assertTrue(frac_above_lower >= 0.92) + self.assertTrue(frac_above_lower <= 0.98) + + # Multi-dimensional quantile regression. + def testQuantileRegressionMultiDimLabel(self): + learner_config = learner_pb2.LearnerConfig() + learner_config.num_classes = 2 + learner_config.constraints.max_tree_depth = 3 + learner_config.growing_mode = learner_pb2.LearnerConfig.WHOLE_TREE + learner_config.constraints.min_node_weight = 1 / _QUANTILE_REGRESSION_SIZE + learner_config.regularization.l2 = 1.0 / _QUANTILE_REGRESSION_SIZE + learner_config.regularization.l1 = 1.0 / _QUANTILE_REGRESSION_SIZE + learner_config.regularization.tree_complexity = ( + 1.0 / _QUANTILE_REGRESSION_SIZE) + + train_input_fn, test_input_fn, y = _quantile_regression_input_fns( + two_dimension=True) + y = y.reshape(_QUANTILE_REGRESSION_SIZE, 2) + + # 95% percentile. + model_upper = estimator.CoreGradientBoostedDecisionTreeQuantileRegressor( + quantiles=[0.95], + learner_config=learner_config, + num_trees=100, + label_dimension=2, + examples_per_layer=_QUANTILE_REGRESSION_SIZE, + center_bias=False) + + model_upper.train(input_fn=train_input_fn, steps=1000) + result_iter = model_upper.predict(input_fn=test_input_fn) + upper = [] + for prediction_dict in result_iter: + upper.append(prediction_dict["predictions"]) + + count_below_upper = np.count_nonzero(upper > y, axis=0) + count_both_below_upper = np.count_nonzero(np.prod(upper > y, axis=1)) + frac_below_upper_0 = round(1. * count_below_upper[0] / len(y), 3) + frac_below_upper_1 = round(1. * count_below_upper[1] / len(y), 3) + frac_both_below_upper = round(1. * count_both_below_upper / len(y), 3) + # +/- 3% + self.assertTrue(frac_below_upper_0 >= 0.92) + self.assertTrue(frac_below_upper_0 <= 0.98) + self.assertTrue(frac_below_upper_1 >= 0.92) + self.assertTrue(frac_below_upper_1 <= 0.98) + self.assertTrue(frac_both_below_upper >= 0.92) + self.assertTrue(frac_both_below_upper <= 0.98) + + train_input_fn, test_input_fn, _ = _quantile_regression_input_fns( + two_dimension=True) + model_lower = estimator.CoreGradientBoostedDecisionTreeQuantileRegressor( + quantiles=[0.05], + learner_config=learner_config, + num_trees=100, + label_dimension=2, + examples_per_layer=_QUANTILE_REGRESSION_SIZE, + center_bias=False) + + model_lower.train(input_fn=train_input_fn, steps=1000) + result_iter = model_lower.predict(input_fn=test_input_fn) + lower = [] + for prediction_dict in result_iter: + lower.append(prediction_dict["predictions"]) + + count_above_lower = np.count_nonzero(lower < y, axis=0) + count_both_aboce_lower = np.count_nonzero(np.prod(lower < y, axis=1)) + frac_above_lower_0 = round(1. * count_above_lower[0] / len(y), 3) + frac_above_lower_1 = round(1. * count_above_lower[1] / len(y), 3) + frac_both_above_lower = round(1. * count_both_aboce_lower / len(y), 3) + # +/- 3% + self.assertTrue(frac_above_lower_0 >= 0.92) + self.assertTrue(frac_above_lower_0 <= 0.98) + self.assertTrue(frac_above_lower_1 >= 0.92) + self.assertTrue(frac_above_lower_1 <= 0.98) + self.assertTrue(frac_both_above_lower >= 0.92) + self.assertTrue(frac_both_above_lower <= 0.98) + if __name__ == "__main__": googletest.main() diff --git a/tensorflow/contrib/boosted_trees/examples/boston.py b/tensorflow/contrib/boosted_trees/examples/boston.py index 54c4ff059e..09b240a700 100644 --- a/tensorflow/contrib/boosted_trees/examples/boston.py +++ b/tensorflow/contrib/boosted_trees/examples/boston.py @@ -90,13 +90,13 @@ def _make_experiment_fn(output_dir): (x_train, y_train), (x_test, y_test) = tf.keras.datasets.boston_housing.load_data() - train_input_fn = tf.estimator.inputs.numpy_input_fn( + train_input_fn = tf.compat.v1.estimator.inputs.numpy_input_fn( x={"x": x_train}, y=y_train, batch_size=FLAGS.batch_size, num_epochs=None, shuffle=True) - eval_input_fn = tf.estimator.inputs.numpy_input_fn( + eval_input_fn = tf.compat.v1.estimator.inputs.numpy_input_fn( x={"x": x_test}, y=y_test, num_epochs=1, shuffle=False) feature_columns = [ diff --git a/tensorflow/contrib/boosted_trees/examples/boston_combined.py b/tensorflow/contrib/boosted_trees/examples/boston_combined.py index e04b56afbf..d640af354f 100644 --- a/tensorflow/contrib/boosted_trees/examples/boston_combined.py +++ b/tensorflow/contrib/boosted_trees/examples/boston_combined.py @@ -80,13 +80,13 @@ def _make_experiment_fn(output_dir): (x_train, y_train), (x_test, y_test) = tf.keras.datasets.boston_housing.load_data() - train_input_fn = tf.estimator.inputs.numpy_input_fn( + train_input_fn = tf.compat.v1.estimator.inputs.numpy_input_fn( x={"x": x_train}, y=y_train, batch_size=FLAGS.batch_size, num_epochs=None, shuffle=True) - eval_input_fn = tf.estimator.inputs.numpy_input_fn( + eval_input_fn = tf.compat.v1.estimator.inputs.numpy_input_fn( x={"x": x_test}, y=y_test, num_epochs=1, shuffle=False) feature_columns = [ diff --git a/tensorflow/contrib/boosted_trees/kernels/split_handler_ops.cc b/tensorflow/contrib/boosted_trees/kernels/split_handler_ops.cc index 8edb5d6c64..6d78e27e8f 100644 --- a/tensorflow/contrib/boosted_trees/kernels/split_handler_ops.cc +++ b/tensorflow/contrib/boosted_trees/kernels/split_handler_ops.cc @@ -834,8 +834,13 @@ class BuildCategoricalEqualitySplitsOp : public OpKernel { root_gradient_stats *= normalizer_ratio; NodeStats root_stats = state->ComputeNodeStats(root_gradient_stats); int32 best_feature_idx = 0; + bool best_feature_updated = false; NodeStats best_right_node_stats(0); NodeStats best_left_node_stats(0); + CHECK(end_index - start_index >= 2) + << "Partition should have a non bias feature. Start index " + << start_index << " and end index " << end_index; + for (int64 feature_idx = start_index + 1; feature_idx < end_index; ++feature_idx) { GradientStats left_gradient_stats(*gradients_t, *hessians_t, @@ -845,11 +850,13 @@ class BuildCategoricalEqualitySplitsOp : public OpKernel { root_gradient_stats - left_gradient_stats; NodeStats left_stats = state->ComputeNodeStats(left_gradient_stats); NodeStats right_stats = state->ComputeNodeStats(right_gradient_stats); - if (left_stats.gain + right_stats.gain > best_gain) { + if (!best_feature_updated || + left_stats.gain + right_stats.gain > best_gain) { best_gain = left_stats.gain + right_stats.gain; best_left_node_stats = left_stats; best_right_node_stats = right_stats; best_feature_idx = feature_idx; + best_feature_updated = true; } } SplitInfo split_info; @@ -864,7 +871,7 @@ class BuildCategoricalEqualitySplitsOp : public OpKernel { << feature_ids(best_feature_idx, 0) << ", " << feature_ids(best_feature_idx, 1) << "\nPartition IDS: " << partition_ids(start_index) << " " - << partition_ids(best_feature_idx); + << partition_ids(best_feature_idx) << " and best gain " << best_gain; equality_split->set_feature_id(feature_ids(best_feature_idx, 0)); auto* left_child = split_info.mutable_left_child(); auto* right_child = split_info.mutable_right_child(); diff --git a/tensorflow/contrib/boosted_trees/lib/learner/batch/categorical_split_handler.py b/tensorflow/contrib/boosted_trees/lib/learner/batch/categorical_split_handler.py index 4da25298cb..d26af58419 100644 --- a/tensorflow/contrib/boosted_trees/lib/learner/batch/categorical_split_handler.py +++ b/tensorflow/contrib/boosted_trees/lib/learner/batch/categorical_split_handler.py @@ -119,7 +119,7 @@ class EqualitySplitHandler(base_split_handler.BaseSplitHandler): def not_active_inputs(): return (constant_op.constant([], dtype=dtypes.int32), - constant_op.constant([], dtype=dtypes.int64, shape=[1, 2]), + constant_op.constant_v1([], dtype=dtypes.int64, shape=[1, 2]), empty_gradients, empty_hessians) def active_inputs(): diff --git a/tensorflow/contrib/boosted_trees/lib/learner/batch/categorical_split_handler_test.py b/tensorflow/contrib/boosted_trees/lib/learner/batch/categorical_split_handler_test.py index a2f708081a..386dc19fc7 100644 --- a/tensorflow/contrib/boosted_trees/lib/learner/batch/categorical_split_handler_test.py +++ b/tensorflow/contrib/boosted_trees/lib/learner/batch/categorical_split_handler_test.py @@ -36,9 +36,9 @@ def get_empty_tensors(gradient_shape, hessian_shape): empty_hess_shape = [1] + hessian_shape.as_list() empty_grad_shape = [1] + gradient_shape.as_list() - empty_gradients = constant_op.constant( + empty_gradients = constant_op.constant_v1( [], dtype=dtypes.float32, shape=empty_grad_shape) - empty_hessians = constant_op.constant( + empty_hessians = constant_op.constant_v1( [], dtype=dtypes.float32, shape=empty_hess_shape) return empty_gradients, empty_hessians @@ -486,8 +486,8 @@ class EqualitySplitHandlerTest(test_util.TensorFlowTestCase): gradients = array_ops.constant([0.2, -0.5, 1.2, 4.0]) hessians = array_ops.constant([0.12, 0.07, 0.2, 0.13]) partition_ids = [0, 0, 0, 1] - indices = array_ops.constant([], dtype=dtypes.int64, shape=[0, 2]) - values = array_ops.constant([], dtype=dtypes.int64) + indices = constant_op.constant_v1([], dtype=dtypes.int64, shape=[0, 2]) + values = constant_op.constant_v1([], dtype=dtypes.int64) gradient_shape = tensor_shape.scalar() hessian_shape = tensor_shape.scalar() diff --git a/tensorflow/contrib/boosted_trees/lib/learner/batch/ordinal_split_handler.py b/tensorflow/contrib/boosted_trees/lib/learner/batch/ordinal_split_handler.py index 1fffbb5f66..0476bed2cd 100644 --- a/tensorflow/contrib/boosted_trees/lib/learner/batch/ordinal_split_handler.py +++ b/tensorflow/contrib/boosted_trees/lib/learner/batch/ordinal_split_handler.py @@ -605,7 +605,7 @@ def dense_make_stats_update(is_active, are_buckets_ready, float_column, quantile_buckets, example_partition_ids, gradients, hessians, weights, empty_gradients, empty_hessians): """Updates the state for dense split handler.""" - empty_float = constant_op.constant([], dtype=dtypes.float32) + empty_float = constant_op.constant_v1([], dtype=dtypes.float32) quantile_values, quantile_weights = control_flow_ops.cond( is_active[1], # For the next layer, this handler is inactive. @@ -621,8 +621,8 @@ def dense_make_stats_update(is_active, are_buckets_ready, float_column, return (example_partition_ids, quantized_feature, gradients, hessians) def not_ready_inputs_fn(): - return (constant_op.constant([], dtype=dtypes.int32), - constant_op.constant([[]], dtype=dtypes.int64, shape=[1, 2]), + return (constant_op.constant_v1([], dtype=dtypes.int32), + constant_op.constant_v1([[]], dtype=dtypes.int64, shape=[1, 2]), empty_gradients, empty_hessians) example_partition_ids, feature_ids, gradients, hessians = ( @@ -708,11 +708,11 @@ def sparse_make_stats_update( def quantiles_not_ready(): """The subgraph for when the quantiles are not ready.""" - return (constant_op.constant([], dtype=dtypes.int32), - constant_op.constant([], dtype=dtypes.int64, shape=[1, 2]), + return (constant_op.constant_v1([], dtype=dtypes.int32), + constant_op.constant_v1([], dtype=dtypes.int64, shape=[1, 2]), empty_gradients, empty_hessians) - empty_float = constant_op.constant([], dtype=dtypes.float32) + empty_float = constant_op.constant_v1([], dtype=dtypes.float32) handler_not_active = (constant_op.constant( [], dtype=dtypes.int64, shape=[0, 2]), empty_float, constant_op.constant([0, 1], dtype=dtypes.int64), diff --git a/tensorflow/contrib/boosted_trees/lib/learner/batch/ordinal_split_handler_test.py b/tensorflow/contrib/boosted_trees/lib/learner/batch/ordinal_split_handler_test.py index 74b0ea6989..4a1b528646 100644 --- a/tensorflow/contrib/boosted_trees/lib/learner/batch/ordinal_split_handler_test.py +++ b/tensorflow/contrib/boosted_trees/lib/learner/batch/ordinal_split_handler_test.py @@ -39,9 +39,9 @@ def get_empty_tensors(gradient_shape, hessian_shape): empty_hess_shape = [1] + hessian_shape.as_list() empty_grad_shape = [1] + gradient_shape.as_list() - empty_gradients = constant_op.constant( + empty_gradients = constant_op.constant_v1( [], dtype=dtypes.float32, shape=empty_grad_shape) - empty_hessians = constant_op.constant( + empty_hessians = constant_op.constant_v1( [], dtype=dtypes.float32, shape=empty_hess_shape) return empty_gradients, empty_hessians @@ -1476,9 +1476,9 @@ class SparseSplitHandlerTest(test_util.TensorFlowTestCase): def testEmpty(self): with self.cached_session() as sess: - indices = array_ops.constant([], dtype=dtypes.int64, shape=[0, 2]) + indices = constant_op.constant_v1([], dtype=dtypes.int64, shape=[0, 2]) # No values in this feature column in this mini-batch. - values = array_ops.constant([], dtype=dtypes.float32) + values = constant_op.constant_v1([], dtype=dtypes.float32) sparse_column = sparse_tensor.SparseTensor(indices, values, [4, 1]) gradient_shape = tensor_shape.scalar() @@ -1549,8 +1549,9 @@ class SparseSplitHandlerTest(test_util.TensorFlowTestCase): sparse_column = array_ops.sparse_placeholder(dtypes.float32) # We have two batches - at first, a sparse feature is empty. - empty_indices = array_ops.constant([], dtype=dtypes.int64, shape=[0, 2]) - empty_values = array_ops.constant([], dtype=dtypes.float32) + empty_indices = constant_op.constant_v1([], dtype=dtypes.int64, + shape=[0, 2]) + empty_values = constant_op.constant_v1([], dtype=dtypes.float32) empty_sparse_column = sparse_tensor.SparseTensor(empty_indices, empty_values, [4, 2]) empty_sparse_column = empty_sparse_column.eval(session=sess) diff --git a/tensorflow/contrib/boosted_trees/python/training/functions/gbdt_batch.py b/tensorflow/contrib/boosted_trees/python/training/functions/gbdt_batch.py index ab5713fbe2..9fdc2fc0c2 100644 --- a/tensorflow/contrib/boosted_trees/python/training/functions/gbdt_batch.py +++ b/tensorflow/contrib/boosted_trees/python/training/functions/gbdt_batch.py @@ -897,9 +897,9 @@ class GradientBoostedDecisionTreeModel(object): empty_hess_shape = [1] + self._hessian_shape.as_list() empty_grad_shape = [1] + self._gradient_shape.as_list() - empty_gradients = constant_op.constant( + empty_gradients = constant_op.constant_v1( [], dtype=dtypes.float32, shape=empty_grad_shape) - empty_hessians = constant_op.constant( + empty_hessians = constant_op.constant_v1( [], dtype=dtypes.float32, shape=empty_hess_shape) active_handlers = array_ops.unstack(active_handlers, axis=0) @@ -1257,13 +1257,12 @@ class GradientBoostedDecisionTreeModel(object): def _get_replica_device_setter(self, worker_device): """Creates a replica device setter.""" ps_tasks = self._num_ps_replicas - ps_ops = [ - "Variable", - "VariableV2", + ps_ops = list(device_setter.STANDARD_PS_OPS) + ps_ops.extend([ "DecisionTreeEnsembleResourceHandleOp", "StatsAccumulatorScalarResourceHandleOp", "StatsAccumulatorTensorResourceHandleOp", - ] + ]) ps_strategy = _OpRoundRobinStrategy(ps_ops, ps_tasks) return device_setter.replica_device_setter( worker_device=worker_device, diff --git a/tensorflow/contrib/boosted_trees/python/utils/losses.py b/tensorflow/contrib/boosted_trees/python/utils/losses.py index b5ebaf1999..220e981618 100644 --- a/tensorflow/contrib/boosted_trees/python/utils/losses.py +++ b/tensorflow/contrib/boosted_trees/python/utils/losses.py @@ -48,6 +48,47 @@ def per_example_logistic_loss(labels, weights, predictions): labels=labels, logits=predictions) return unweighted_loss * weights, control_flow_ops.no_op() +# MUST USE WITH HESSIAN REGULARIZATION, +# This loss can have zero hessian, so it must be used with l2 or min_node_weight +# regularization. +# An example config is +# learner_config.constraints.min_node_weight = 1 / num_examples_per_layer +# learner_config.regularization.l2 = 1.0 / num_examples_per_layer +# TODO(nponomareva): make it multidimensional so we can estimate several +# quantiles at once. +def per_example_quantile_regression_loss(labels, weights, predictions, + quantile): + """Smoothed loss for quantile regression. + + The standard quantile regression loss is quantile*(y-y') when y>y' and + (quantile-1)*(y-y') otherwise, y' is a prediction, y is a label. The impl + below is this loss but squared in the region where the loss value < 1. + + Args: + labels: Rank 2 (N, D) tensor of per-example labels. + weights: Rank 2 (N, 1) tensor of per-example weights. + predictions: Rank 2 (N, D) tensor of per-example predictions. + quantile: The quantile to use. + + Returns: + loss: A Rank 2 (N, 1) tensor of per-example quantile loss. + update_op: An update operation to update the loss's internal state. + """ + labels = math_ops.to_float(labels) + error = labels - predictions + square_loss_right = array_ops.where(error * quantile < 1.0, + math_ops.square(quantile * error), + quantile * error) + square_loss_left = array_ops.where(error * (quantile - 1) < 1, + math_ops.square((quantile - 1) * error), + (quantile - 1) * error) + + unweighted_loss = array_ops.where(error > 0, square_loss_right, + square_loss_left) + if weights is None: + return unweighted_loss, control_flow_ops.no_op() + else: + return unweighted_loss * weights, control_flow_ops.no_op() # This is classical form of Maximum entropy loss, that is twice differentiable # (sparse_softmax_cross_entropy which is what we go for is not twice @@ -78,8 +119,7 @@ def per_example_maxent_loss(labels, weights, logits, num_classes, eps=1e-15): labels = array_ops.expand_dims(labels, 1) # Labels are indices of classes, convert them to one hot encodings. target_one_hot = array_ops.one_hot(indices=labels, depth=num_classes) - labels = math_ops.reduce_sum( - input_tensor=target_one_hot, reduction_indices=[1]) + labels = math_ops.reduce_sum(input_tensor=target_one_hot, axis=[1]) labels = math_ops.to_float(labels) # Calculate softmax probabilities for each class. diff --git a/tensorflow/contrib/checkpoint/python/containers.py b/tensorflow/contrib/checkpoint/python/containers.py index 242c1e8ba4..5418e2605b 100644 --- a/tensorflow/contrib/checkpoint/python/containers.py +++ b/tensorflow/contrib/checkpoint/python/containers.py @@ -46,6 +46,10 @@ class UniqueNameTracker(data_structures.CheckpointableDataStructure): self._maybe_initialize_checkpointable() self._name_counts = {} + @property + def _values(self): + return [dep.ref for dep in self._checkpoint_dependencies] + def track(self, checkpointable, base_name): """Add a dependency on `checkpointable`. diff --git a/tensorflow/contrib/cluster_resolver/BUILD b/tensorflow/contrib/cluster_resolver/BUILD index 9e1867ea9d..f944b7f884 100644 --- a/tensorflow/contrib/cluster_resolver/BUILD +++ b/tensorflow/contrib/cluster_resolver/BUILD @@ -21,173 +21,25 @@ py_library( py_library( name = "cluster_resolver_py", - srcs = [ + srcs = glob([ "__init__.py", - "python/training/__init__.py", - ], + "python/training/*.py", + ]), srcs_version = "PY2AND3", visibility = ["//visibility:public"], - deps = [ - ":base_cluster_resolver_py", - ":gce_cluster_resolver_py", - ":kubernetes_cluster_resolver_py", - ":slurm_cluster_resolver_py", - ":tfconfig_cluster_resolver_py", - ":tpu_cluster_resolver_py", - "//tensorflow/python:util", - ], -) - -py_library( - name = "base_cluster_resolver_py", - srcs = ["python/training/cluster_resolver.py"], - srcs_version = "PY2AND3", - deps = [ - "//tensorflow/python:training", - ], -) - -py_library( - name = "gce_cluster_resolver_py", - srcs = ["python/training/gce_cluster_resolver.py"], - srcs_version = "PY2AND3", - deps = [ - ":base_cluster_resolver_py", - "//tensorflow/python:training", - ], -) - -py_library( - name = "tfconfig_cluster_resolver_py", - srcs = ["python/training/tfconfig_cluster_resolver.py"], - srcs_version = "PY2AND3", - deps = [ - ":base_cluster_resolver_py", - "//tensorflow/python:training", - ], -) - -py_library( - name = "tpu_cluster_resolver_py", - srcs = ["python/training/tpu_cluster_resolver.py"], - srcs_version = "PY2AND3", - deps = [ - ":base_cluster_resolver_py", - "//tensorflow/python:training", - ], -) - -py_library( - name = "slurm_cluster_resolver_py", - srcs = ["python/training/slurm_cluster_resolver.py"], - srcs_version = "PY2AND3", - deps = [ - ":base_cluster_resolver_py", - "//tensorflow/python:training", - ], -) - -py_library( - name = "kubernetes_cluster_resolver_py", - srcs = ["python/training/kubernetes_cluster_resolver.py"], - srcs_version = "PY2AND3", - deps = [ - ":base_cluster_resolver_py", - "//tensorflow/python:training", - ], -) - -tf_py_test( - name = "base_cluster_resolver_py_test", - srcs = ["python/training/cluster_resolver_test.py"], - additional_deps = [ - ":cluster_resolver_py", - "//tensorflow/python:client_testlib", - "//tensorflow/python:framework_for_generated_wrappers", - "//tensorflow/python:framework_test_lib", - "//tensorflow/python:platform_test", - "//tensorflow/python:training", - ], - main = "python/training/cluster_resolver_test.py", -) - -tf_py_test( - name = "gce_cluster_resolver_py_test", - size = "small", - srcs = ["python/training/gce_cluster_resolver_test.py"], - additional_deps = [ - ":cluster_resolver_py", - ":gce_cluster_resolver_py", - "//tensorflow/python:client_testlib", - "//tensorflow/python:framework_for_generated_wrappers", - "//tensorflow/python:framework_test_lib", - "//tensorflow/python:platform_test", - "//tensorflow/python:training", - ], - main = "python/training/gce_cluster_resolver_test.py", -) - -tf_py_test( - name = "tfconfig_cluster_resolver_py_test", - size = "small", - srcs = ["python/training/tfconfig_cluster_resolver_test.py"], - additional_deps = [ - ":tfconfig_cluster_resolver_py", - "//tensorflow/python:client_testlib", - "//tensorflow/python:framework_for_generated_wrappers", - "//tensorflow/python:framework_test_lib", - "//tensorflow/python:platform_test", - "//tensorflow/python:training", - ], - grpc_enabled = True, - main = "python/training/tfconfig_cluster_resolver_test.py", -) - -tf_py_test( - name = "tpu_cluster_resolver_py_test", - size = "small", - srcs = ["python/training/tpu_cluster_resolver_test.py"], - additional_deps = [ - ":tpu_cluster_resolver_py", - "//tensorflow/python:client_testlib", - "//tensorflow/python:framework_for_generated_wrappers", - "//tensorflow/python:framework_test_lib", - "//tensorflow/python:platform_test", - "//tensorflow/python:training", - ], - grpc_enabled = True, - main = "python/training/tpu_cluster_resolver_test.py", -) - -tf_py_test( - name = "slurm_cluster_resolver_py_test", - size = "small", - srcs = ["python/training/slurm_cluster_resolver_test.py"], - additional_deps = [ - ":cluster_resolver_py", - ":slurm_cluster_resolver_py", - "//tensorflow/python:client_testlib", - "//tensorflow/python:framework_for_generated_wrappers", - "//tensorflow/python:framework_test_lib", - "//tensorflow/python:platform_test", - "//tensorflow/python:training", - ], - main = "python/training/slurm_cluster_resolver_test.py", - tags = [], + deps = ["//tensorflow/python/distribute/cluster_resolver:cluster_resolver_lib"], ) tf_py_test( - name = "kubernetes_cluster_resolver_py_test", - size = "small", - srcs = ["python/training/kubernetes_cluster_resolver_test.py"], + name = "cluster_resolver_initialization_test", + srcs = ["cluster_resolver_initialization_test.py"], additional_deps = [ ":cluster_resolver_py", - ":kubernetes_cluster_resolver_py", "//tensorflow/python:client_testlib", "//tensorflow/python:framework_for_generated_wrappers", "//tensorflow/python:framework_test_lib", "//tensorflow/python:platform_test", "//tensorflow/python:training", ], - main = "python/training/kubernetes_cluster_resolver_test.py", + main = "cluster_resolver_initialization_test.py", ) diff --git a/tensorflow/contrib/cluster_resolver/__init__.py b/tensorflow/contrib/cluster_resolver/__init__.py index fd1263fe81..390b3e7550 100644 --- a/tensorflow/contrib/cluster_resolver/__init__.py +++ b/tensorflow/contrib/cluster_resolver/__init__.py @@ -20,12 +20,14 @@ from __future__ import division from __future__ import print_function # pylint: disable=wildcard-import,unused-import -from tensorflow.contrib.cluster_resolver.python.training.cluster_resolver import ClusterResolver -from tensorflow.contrib.cluster_resolver.python.training.cluster_resolver import SimpleClusterResolver -from tensorflow.contrib.cluster_resolver.python.training.cluster_resolver import UnionClusterResolver -from tensorflow.contrib.cluster_resolver.python.training.gce_cluster_resolver import GceClusterResolver -from tensorflow.contrib.cluster_resolver.python.training.slurm_cluster_resolver import SlurmClusterResolver -from tensorflow.contrib.cluster_resolver.python.training.tpu_cluster_resolver import TPUClusterResolver +from tensorflow.python.distribute.cluster_resolver.cluster_resolver import ClusterResolver +from tensorflow.python.distribute.cluster_resolver.cluster_resolver import SimpleClusterResolver +from tensorflow.python.distribute.cluster_resolver.cluster_resolver import UnionClusterResolver +from tensorflow.python.distribute.cluster_resolver.gce_cluster_resolver import GceClusterResolver +from tensorflow.python.distribute.cluster_resolver.kubernetes_cluster_resolver import KubernetesClusterResolver +from tensorflow.python.distribute.cluster_resolver.slurm_cluster_resolver import SlurmClusterResolver +from tensorflow.python.distribute.cluster_resolver.tfconfig_cluster_resolver import TFConfigClusterResolver +from tensorflow.python.distribute.cluster_resolver.tpu_cluster_resolver import TPUClusterResolver # pylint: enable=wildcard-import,unused-import from tensorflow.python.util.all_util import remove_undocumented @@ -35,6 +37,8 @@ _allowed_symbols = [ 'SimpleClusterResolver', 'UnionClusterResolver', 'GceClusterResolver', + 'KubernetesClusterResolver', + 'TFConfigClusterResolver', 'TPUClusterResolver', 'SlurmClusterResolver', ] diff --git a/tensorflow/contrib/cluster_resolver/cluster_resolver_initialization_test.py b/tensorflow/contrib/cluster_resolver/cluster_resolver_initialization_test.py new file mode 100644 index 0000000000..01ff1478c6 --- /dev/null +++ b/tensorflow/contrib/cluster_resolver/cluster_resolver_initialization_test.py @@ -0,0 +1,53 @@ +# Copyright 2018 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 to ensure ClusterResolvers are usable via the old contrib path.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.contrib.cluster_resolver import SimpleClusterResolver +from tensorflow.contrib.cluster_resolver.python.training import cluster_resolver +from tensorflow.contrib.cluster_resolver.python.training import UnionClusterResolver +from tensorflow.python.platform import test +from tensorflow.python.training import server_lib + + +class ClusterResolverInitializationTest(test.TestCase): + + def testCreateSimpleClusterResolverFromLib(self): + base_cluster_spec = server_lib.ClusterSpec({ + "ps": ["ps0:2222", "ps1:2222"], + "worker": ["worker0:2222", "worker1:2222", "worker2:2222"] + }) + cluster_resolver.SimpleClusterResolver(base_cluster_spec) + + def testCreateSimpleClusterResolver(self): + base_cluster_spec = server_lib.ClusterSpec({ + "ps": ["ps0:2222", "ps1:2222"], + "worker": ["worker0:2222", "worker1:2222", "worker2:2222"] + }) + SimpleClusterResolver(base_cluster_spec) + + def testCreateUnionClusterResolver(self): + base_cluster_spec = server_lib.ClusterSpec({ + "ps": ["ps0:2222", "ps1:2222"], + "worker": ["worker0:2222", "worker1:2222", "worker2:2222"] + }) + simple_cr = SimpleClusterResolver(base_cluster_spec) + UnionClusterResolver(simple_cr) + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/contrib/cluster_resolver/python/training/__init__.py b/tensorflow/contrib/cluster_resolver/python/training/__init__.py index 6d9120a3b9..10d93549eb 100644 --- a/tensorflow/contrib/cluster_resolver/python/training/__init__.py +++ b/tensorflow/contrib/cluster_resolver/python/training/__init__.py @@ -18,11 +18,36 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -from tensorflow.contrib.cluster_resolver.python.training.cluster_resolver import ClusterResolver -from tensorflow.contrib.cluster_resolver.python.training.cluster_resolver import SimpleClusterResolver -from tensorflow.contrib.cluster_resolver.python.training.cluster_resolver import UnionClusterResolver -from tensorflow.contrib.cluster_resolver.python.training.gce_cluster_resolver import GceClusterResolver -from tensorflow.contrib.cluster_resolver.python.training.kubernetes_cluster_resolver import KubernetesClusterResolver -from tensorflow.contrib.cluster_resolver.python.training.slurm_cluster_resolver import SlurmClusterResolver -from tensorflow.contrib.cluster_resolver.python.training.tfconfig_cluster_resolver import TFConfigClusterResolver -from tensorflow.contrib.cluster_resolver.python.training.tpu_cluster_resolver import TPUClusterResolver +# This file (and all files in this directory in general) is a backwards +# compatibility shim that exists to re-export ClusterResolvers such that +# existing OSS code will not be broken. + +from tensorflow.python.distribute.cluster_resolver.cluster_resolver import ClusterResolver +from tensorflow.python.distribute.cluster_resolver.cluster_resolver import SimpleClusterResolver +from tensorflow.python.distribute.cluster_resolver.cluster_resolver import UnionClusterResolver +from tensorflow.python.distribute.cluster_resolver.gce_cluster_resolver import GceClusterResolver +from tensorflow.python.distribute.cluster_resolver.kubernetes_cluster_resolver import KubernetesClusterResolver +from tensorflow.python.distribute.cluster_resolver.slurm_cluster_resolver import SlurmClusterResolver +from tensorflow.python.distribute.cluster_resolver.tfconfig_cluster_resolver import TFConfigClusterResolver +from tensorflow.python.distribute.cluster_resolver.tpu_cluster_resolver import TPUClusterResolver + +from tensorflow.python.util.all_util import remove_undocumented + +_allowed_symbols = [ + 'cluster_resolver', + 'gce_cluster_resolver', + 'kubernetes_cluster_resolver', + 'slurm_cluster_resolver', + 'tfconfig_cluster_resolver', + 'tpu_cluster_resolver', + 'ClusterResolver', + 'SimpleClusterResolver', + 'UnionClusterResolver', + 'GceClusterResolver', + 'KubernetesClusterResolver', + 'TFConfigClusterResolver', + 'TPUClusterResolver', + 'SlurmClusterResolver', +] + +remove_undocumented(__name__, _allowed_symbols) diff --git a/tensorflow/contrib/cluster_resolver/python/training/cluster_resolver.py b/tensorflow/contrib/cluster_resolver/python/training/cluster_resolver.py index 40b1e667ee..99840fb516 100644 --- a/tensorflow/contrib/cluster_resolver/python/training/cluster_resolver.py +++ b/tensorflow/contrib/cluster_resolver/python/training/cluster_resolver.py @@ -1,4 +1,4 @@ -# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# Copyright 2018 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. @@ -12,333 +12,29 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Cluster Resolvers are used for dynamic cluster IP/hostname resolution.""" +"""Stub file for ClusterResolver to maintain backwards compatibility.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function -import abc +# This file (and all files in this directory in general) is a backwards +# compatibility shim that exists to re-export ClusterResolvers such that +# existing OSS code will not be broken. -import six +# pylint: disable=unused-import +from tensorflow.python.distribute.cluster_resolver.cluster_resolver import ClusterResolver +from tensorflow.python.distribute.cluster_resolver.cluster_resolver import SimpleClusterResolver +from tensorflow.python.distribute.cluster_resolver.cluster_resolver import UnionClusterResolver +# pylint: enable=unused-import -from tensorflow.python.training.server_lib import ClusterSpec +from tensorflow.python.util.all_util import remove_undocumented +_allowed_symbols = [ + 'ClusterResolver', + 'SimpleClusterResolver', + 'UnionClusterResolver', +] -def _format_master_url(master, rpc_layer=None): - if rpc_layer: - return '%s://%s' % (rpc_layer, master) - else: - return master +remove_undocumented(__name__, _allowed_symbols) - -@six.add_metaclass(abc.ABCMeta) -class ClusterResolver(object): - """Abstract class for all implementations of ClusterResolvers. - - This defines the skeleton for all implementations of ClusterResolvers. - ClusterResolvers are a way for TensorFlow to communicate with various cluster - management systems (e.g. GCE, AWS, etc...). - - By letting TensorFlow communicate with these systems, we will be able to - automatically discover and resolve IP addresses for various TensorFlow - workers. This will eventually allow us to automatically recover from - underlying machine failures and scale TensorFlow worker clusters up and down. - """ - - @abc.abstractmethod - def cluster_spec(self): - """Retrieve the current state of the cluster and returns a ClusterSpec. - - Returns: - A ClusterSpec representing the state of the cluster at the moment this - function is called. - - Implementors of this function must take care in ensuring that the - ClusterSpec returned is up-to-date at the time of calling this function. - This usually means retrieving the information from the underlying cluster - management system every time this function is invoked and reconstructing - a cluster_spec, rather than attempting to cache anything. - """ - raise NotImplementedError( - 'cluster_spec is not implemented for {}.'.format(self)) - - @abc.abstractmethod - def master(self, task_type=None, task_index=None, rpc_layer=None): - """Retrieves the name or URL of the session master. - - Args: - task_type: (Optional) The type of the TensorFlow task of the master. - task_index: (Optional) The index of the TensorFlow task of the master. - rpc_layer: (Optional) The RPC protocol for the given cluster. - - Returns: - The name or URL of the session master. - - Implementors of this function must take care in ensuring that the master - returned is up-to-date at the time to calling this function. This usually - means retrieving the master every time this function is invoked. - """ - raise NotImplementedError('master is not implemented for {}.'.format(self)) - - -class SimpleClusterResolver(ClusterResolver): - """Simple implementation of ClusterResolver that accepts a ClusterSpec.""" - - def __init__(self, cluster_spec, master='', task_type=None, task_index=None, - environment='', num_accelerators_per_worker=0, - rpc_layer=None): - """Creates a SimpleClusterResolver from a ClusterSpec.""" - super(SimpleClusterResolver, self).__init__() - - self._task_type = task_type - self._task_index = task_index - self._environment = environment - self._num_accelerators_per_worker = num_accelerators_per_worker - self._rpc_layer = rpc_layer - - if not isinstance(cluster_spec, ClusterSpec): - raise TypeError('cluster_spec must be a ClusterSpec.') - self._cluster_spec = cluster_spec - - if not isinstance(master, str): - raise TypeError('master must be a string.') - self._master = master - - def cluster_spec(self): - """Returns the ClusterSpec passed into the constructor.""" - return self._cluster_spec - - def master(self, task_type=None, task_index=None, rpc_layer=None): - """Returns the master address to use when creating a session. - - Args: - task_type: (Optional) The type of the TensorFlow task of the master. - task_index: (Optional) The index of the TensorFlow task of the master. - rpc_layer: (Optional) The RPC used by distributed TensorFlow. - - Returns: - The name or URL of the session master. - - If a task_type and task_index is given, this will override the `master` - string passed into the initialization function. - """ - if task_type is not None and task_index is not None: - master = self.cluster_spec().task_address(task_type, task_index) - else: - master = self._master - - return _format_master_url(master, rpc_layer or self._rpc_layer) - - @property - def task_type(self): - return self._task_type - - @property - def task_index(self): - return self._task_index - - @task_type.setter - def task_type(self, task_type): - self._task_type = task_type - - @task_index.setter - def task_index(self, task_index): - self._task_index = task_index - - @property - def environment(self): - return self._environment - - def num_accelerators_per_worker(self, session_config=None): - """Returns the number of accelerator cores per worker. - - Args: - session_config: Unused. The SimpleClusterResolver does not do automatic - detection of accelerators, so a TensorFlow session will never be - created, and thus a `session_config` is never necessary here, and will - be ignored. - """ - del session_config - return self._num_accelerators_per_worker - - @property - def rpc_layer(self): - return self._rpc_layer - - @rpc_layer.setter - def rpc_layer(self, rpc_layer): - self._rpc_layer = rpc_layer - - -class UnionClusterResolver(ClusterResolver): - """Performs a union on underlying ClusterResolvers. - - This class performs a union given two or more existing ClusterResolvers. It - merges the underlying ClusterResolvers, and returns one unified ClusterSpec - when cluster_spec is called. The details of the merge function is - documented in the cluster_spec function. - - For additional Cluster Resolver properties such as task type, task index, - rpc layer, environment, etc..., we will return the value from the first - ClusterResolver in the union. - """ - - def __init__(self, *args, **kwargs): - """Initializes a UnionClusterResolver with other ClusterResolvers. - - Args: - *args: `ClusterResolver` objects to be unionized. - **kwargs: - rpc_layer - (Optional) Override value for the RPC layer used by - TensorFlow. - task_type - (Optional) Override value for the current task type. - task_index - (Optional) Override value for the current task index. - - Raises: - TypeError: If any argument is not a subclass of `ClusterResolvers`. - ValueError: If there are no arguments passed. - """ - super(UnionClusterResolver, self).__init__() - - self._rpc_layer = kwargs.pop('rpc_layer', None) - self._task_type = kwargs.pop('task_type', None) - self._task_index = kwargs.pop('task_index', None) - - if kwargs: - raise ValueError('Unexpected kwargs provided {!r}'.format(kwargs)) - - if not args: - raise ValueError('At least one ClusterResolver is required.') - - for cluster_resolver in args: - if not isinstance(cluster_resolver, ClusterResolver): - raise TypeError('All arguments must be a sub-class of ' - '`ClusterResolver.`') - self._cluster_resolvers = args - - def cluster_spec(self): - """Returns a union of all the ClusterSpecs from the ClusterResolvers. - - Returns: - A ClusterSpec containing host information merged from all the underlying - ClusterResolvers. - - Raises: - KeyError: If there are conflicting keys detected when merging two or - more dictionaries, this exception is raised. - - Note: If there are multiple ClusterResolvers exposing ClusterSpecs with the - same job name, we will merge the list/dict of workers. - - If *all* underlying ClusterSpecs expose the set of workers as lists, we will - concatenate the lists of workers, starting with the list of workers from - the first ClusterResolver passed into the constructor. - - If *any* of the ClusterSpecs expose the set of workers as a dict, we will - treat all the sets of workers as dicts (even if they are returned as lists) - and will only merge them into a dict if there is no conflicting keys. If - there is a conflicting key, we will raise a `KeyError`. - """ - - merged_cluster = {} - - # We figure out whether it is all lists for a particular job, or whether - # there are dicts inside. - for cluster_resolver in self._cluster_resolvers: - cluster_spec = cluster_resolver.cluster_spec() - cluster_dict = cluster_spec.as_dict() - - for job_name, tasks in cluster_dict.items(): - if job_name in merged_cluster: - # If we see a dict, then we write a dict out regardless. - if isinstance(tasks, dict): - merged_cluster[job_name] = {} - else: - # We take whichever type is present. - if isinstance(tasks, list): - merged_cluster[job_name] = [] - else: - merged_cluster[job_name] = {} - - # We then do the merge as appropriate in merged_cluster[job]. - for cluster_resolver in self._cluster_resolvers: - cluster_spec = cluster_resolver.cluster_spec() - cluster_dict = cluster_spec.as_dict() - - for job_name, tasks in cluster_dict.items(): - if isinstance(merged_cluster[job_name], list): - # We all have lists, we can just concatenate and be done. - merged_cluster[job_name].extend(tasks) - else: - if isinstance(tasks, list): - # We convert to a dictionary if the type is a list. - task_dict = dict(zip(range(0, len(tasks)), tasks)) - else: - # We can simply make a copy (for update) and be done. - task_dict = tasks.copy() - - # We detect if there are duplicates, and raise an error if so. - task_keys = set(task_dict) - merged_keys = set(merged_cluster[job_name].keys()) - intersected_keys = task_keys.intersection(merged_keys) - if intersected_keys: - raise KeyError('Duplicate keys detected when merging two ' - 'ClusterSpecs: %s' % repr(intersected_keys)) - - # We do the merge after all the processing. - merged_cluster[job_name].update(task_dict) - - return ClusterSpec(merged_cluster) - - def master(self, task_type=None, task_index=None, rpc_layer=None): - """Returns the master address to use when creating a session. - - This usually returns the master from the first ClusterResolver passed in, - but you can override this by specifying the task_type and task_index. - - Args: - task_type: (Optional) The type of the TensorFlow task of the master. - task_index: (Optional) The index of the TensorFlow task of the master. - rpc_layer: (Optional) The RPC protocol for the given cluster. - - Returns: - The name or URL of the session master. - """ - if task_type is not None and task_index is not None: - master = self.cluster_spec().task_address(task_type, task_index) - return _format_master_url(master, rpc_layer or self._rpc_layer) - - return self._cluster_resolvers[0].master(rpc_layer=rpc_layer) - - @property - def task_type(self): - return self._task_type or self._cluster_resolvers[0].task_type - - @property - def task_index(self): - return self._task_index or self._cluster_resolvers[0].task_index - - @task_type.setter - def task_type(self, task_type): - self._task_type = task_type - - @task_index.setter - def task_index(self, task_index): - self._task_index = task_index - - @property - def environment(self): - return self._cluster_resolvers[0].environment - - def num_accelerators_per_worker(self, session_config=None): - return self._cluster_resolvers[0].num_accelerators_per_worker( - session_config) - - @property - def rpc_layer(self): - return self._rpc_layer or self._cluster_resolvers[0].rpc_layer - - @rpc_layer.setter - def rpc_layer(self, rpc_layer): - self._rpc_layer = rpc_layer diff --git a/tensorflow/contrib/cluster_resolver/python/training/gce_cluster_resolver.py b/tensorflow/contrib/cluster_resolver/python/training/gce_cluster_resolver.py index 195b68959b..55e61155c6 100644 --- a/tensorflow/contrib/cluster_resolver/python/training/gce_cluster_resolver.py +++ b/tensorflow/contrib/cluster_resolver/python/training/gce_cluster_resolver.py @@ -1,4 +1,4 @@ -# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# Copyright 2018 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. @@ -12,197 +12,24 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Implementation of Cluster Resolvers for GCE Instance Groups.""" +"""Stub file for GceClusterResolver to maintain backwards compatibility.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function +# This file (and all files in this directory in general) is a backwards +# compatibility shim that exists to re-export ClusterResolvers such that +# existing OSS code will not be broken. -from tensorflow.contrib.cluster_resolver.python.training.cluster_resolver import ClusterResolver -from tensorflow.python.training.server_lib import ClusterSpec +# pylint: disable=unused-import +from tensorflow.python.distribute.cluster_resolver.gce_cluster_resolver import GceClusterResolver +# pylint: enable=unused-import -_GOOGLE_API_CLIENT_INSTALLED = True -try: - from googleapiclient import discovery # pylint: disable=g-import-not-at-top - from oauth2client.client import GoogleCredentials # pylint: disable=g-import-not-at-top -except ImportError: - _GOOGLE_API_CLIENT_INSTALLED = False +from tensorflow.python.util.all_util import remove_undocumented +_allowed_symbols = [ + 'GceClusterResolver', +] -def _format_master_url(master, rpc_layer=None): - return '%s://%s' % (rpc_layer, master) if rpc_layer else master - - -class GceClusterResolver(ClusterResolver): - """Cluster Resolver for Google Compute Engine. - - This is an implementation of cluster resolvers for the Google Compute Engine - instance group platform. By specifying a project, zone, and instance group, - this will retrieve the IP address of all the instances within the instance - group and return a Cluster Resolver object suitable for use for distributed - TensorFlow. - """ - - def __init__(self, - project, - zone, - instance_group, - port, - task_type='worker', - task_index=0, - rpc_layer='grpc', - num_accelerators_per_worker=0, - credentials='default', - service=None): - """Creates a new GceClusterResolver object. - - This takes in a few parameters and creates a GceClusterResolver project. It - will then use these parameters to query the GCE API for the IP addresses of - each instance in the instance group. - - Args: - project: Name of the GCE project. - zone: Zone of the GCE instance group. - instance_group: Name of the GCE instance group. - port: Port of the listening TensorFlow server (default: 8470) - task_type: Name of the TensorFlow job this GCE instance group of VM - instances belong to. - task_index: The task index for this particular VM, within the GCE - instance group. In particular, every single instance should be assigned - a unique ordinal index within an instance group manually so that they - can be distinguished from each other. - rpc_layer: The RPC layer TensorFlow should use to communicate across - instances. - num_accelerators_per_worker: Number of accelerators (GPUs) present per - instance. - credentials: GCE Credentials. If nothing is specified, this defaults to - GoogleCredentials.get_application_default(). - service: The GCE API object returned by the googleapiclient.discovery - function. (Default: discovery.build('compute', 'v1')). If you specify a - custom service object, then the credentials parameter will be ignored. - - Raises: - ImportError: If the googleapiclient is not installed. - """ - self._project = project - self._zone = zone - self._instance_group = instance_group - self._task_type = task_type - self._task_index = task_index - self._rpc_layer = rpc_layer - self._port = port - self._credentials = credentials - - if credentials == 'default': - if _GOOGLE_API_CLIENT_INSTALLED: - self._credentials = GoogleCredentials.get_application_default() - - if service is None: - if not _GOOGLE_API_CLIENT_INSTALLED: - raise ImportError('googleapiclient must be installed before using the ' - 'GCE cluster resolver') - self._service = discovery.build( - 'compute', 'v1', - credentials=self._credentials) - else: - self._service = service - - def cluster_spec(self): - """Returns a ClusterSpec object based on the latest instance group info. - - This returns a ClusterSpec object for use based on information from the - specified instance group. We will retrieve the information from the GCE APIs - every time this method is called. - - Returns: - A ClusterSpec containing host information retrieved from GCE. - """ - request_body = {'instanceState': 'RUNNING'} - request = self._service.instanceGroups().listInstances( - project=self._project, - zone=self._zone, - instanceGroups=self._instance_group, - body=request_body, - orderBy='name') - - worker_list = [] - - while request is not None: - response = request.execute() - - items = response['items'] - for instance in items: - instance_name = instance['instance'].split('/')[-1] - - instance_request = self._service.instances().get( - project=self._project, - zone=self._zone, - instance=instance_name) - - if instance_request is not None: - instance_details = instance_request.execute() - ip_address = instance_details['networkInterfaces'][0]['networkIP'] - instance_url = '%s:%s' % (ip_address, self._port) - worker_list.append(instance_url) - - request = self._service.instanceGroups().listInstances_next( - previous_request=request, - previous_response=response) - - worker_list.sort() - return ClusterSpec({self._task_type: worker_list}) - - def master(self, task_type=None, task_index=None, rpc_layer=None): - task_type = task_type if task_type is not None else self._task_type - task_index = task_index if task_index is not None else self._task_index - - if task_type is not None and task_index is not None: - master = self.cluster_spec().task_address(task_type, task_index) - if rpc_layer or self._rpc_layer: - return '%s://%s' % (rpc_layer or self._rpc_layer, master) - else: - return master - - return '' - - @property - def task_type(self): - return self._task_type - - @property - def task_index(self): - return self._task_index - - @task_type.setter - def task_type(self, task_type): - raise RuntimeError( - 'You cannot reset the task_type of the GceClusterResolver after it has ' - 'been created.') - - @task_index.setter - def task_index(self, task_index): - self._task_index = task_index - - @property - def environment(self): - """Returns the current environment which TensorFlow is running in. - - For users in the GCE environment, the environment property is always an - empty string, and Google users will not use this ClusterResolver for running - on internal systems. - """ - return '' - - @property - def rpc_layer(self): - return self._rpc_layer - - @rpc_layer.setter - def rpc_layer(self, rpc_layer): - self._rpc_layer = rpc_layer - - def num_accelerators_per_worker(self, session_config=None): - del session_config # Unused, since this is set manually in __init__. - return self._num_accelerators_per_worker - +remove_undocumented(__name__, _allowed_symbols) diff --git a/tensorflow/contrib/cluster_resolver/python/training/kubernetes_cluster_resolver.py b/tensorflow/contrib/cluster_resolver/python/training/kubernetes_cluster_resolver.py index ddae64839f..a8eaf33629 100644 --- a/tensorflow/contrib/cluster_resolver/python/training/kubernetes_cluster_resolver.py +++ b/tensorflow/contrib/cluster_resolver/python/training/kubernetes_cluster_resolver.py @@ -12,121 +12,25 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Implementation of Cluster Resolvers for Kubernetes.""" +"""Stub file for KubernetesClusterResolver for backwards compatibility.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function -from tensorflow.contrib.cluster_resolver.python.training.cluster_resolver import ClusterResolver -from tensorflow.python.training import server_lib +# This file (and all files in this directory in general) is a backwards +# compatibility shim that exists to re-export ClusterResolvers such that +# existing OSS code will not be broken. -_KUBERNETES_API_CLIENT_INSTALLED = True -try: - from kubernetes import client as k8sclient # pylint: disable=g-import-not-at-top - from kubernetes import config as k8sconfig # pylint: disable=g-import-not-at-top -except ImportError: - _KUBERNETES_API_CLIENT_INSTALLED = False +# pylint: disable=unused-import +from tensorflow.python.distribute.cluster_resolver.kubernetes_cluster_resolver import KubernetesClusterResolver +# pylint: enable=unused-import +from tensorflow.python.util.all_util import remove_undocumented -class KubernetesClusterResolver(ClusterResolver): - """Cluster Resolver for Kubernetes. +_allowed_symbols = [ + 'KubernetesClusterResolver', +] - This is an implementation of cluster resolvers for Kubernetes. When given the - the Kubernetes namespace and label selector for pods, we will retrieve the - pod IP addresses of all running pods matching the selector, and return a - ClusterSpec based on that information. - """ +remove_undocumented(__name__, _allowed_symbols) - def __init__(self, - job_to_label_mapping=None, - tf_server_port=8470, - override_client=None): - """Initializes a new KubernetesClusterResolver. - - This initializes a new Kubernetes Cluster Resolver. The Cluster Resolver - will attempt to talk to the Kubernetes master to retrieve all the instances - of pods matching a label selector. - - Args: - job_to_label_mapping: A mapping of TensorFlow jobs to label selectors. - This allows users to specify many TensorFlow jobs in one Cluster - Resolver, and each job can have pods belong with different label - selectors. For example, a sample mapping might be - ``` - {'worker': ['job-name=worker-cluster-a', 'job-name=worker-cluster-b'], - 'ps': ['job-name=ps-1', 'job-name=ps-2']} - ``` - tf_server_port: The port the TensorFlow server is listening on. - override_client: The Kubernetes client (usually automatically retrieved - using `from kubernetes import client as k8sclient`). If you pass this - in, you are responsible for setting Kubernetes credentials manually. - - Raises: - ImportError: If the Kubernetes Python client is not installed and no - `override_client` is passed in. - """ - if _KUBERNETES_API_CLIENT_INSTALLED: - k8sconfig.load_kube_config() - - if not job_to_label_mapping: - job_to_label_mapping = {'worker': ['job-name=tensorflow']} - - if not override_client and not _KUBERNETES_API_CLIENT_INSTALLED: - raise ImportError('The Kubernetes Python client must be installed before' - 'using the Kubernetes Cluster Resolver. To install the' - 'Kubernetes Python client, run `pip install ' - 'kubernetes` on your command line.') - - self._job_to_label_mapping = job_to_label_mapping - self._tf_server_port = tf_server_port - self._override_client = override_client - - def master(self): - # TODO(frankchn): Figure out a standard way to pass in the current task type - # and task id via Kubernetes. - pass - - def get_master(self): - return self.master() - - def get_job_name(self): - return self._job_name - - def cluster_spec(self): - """Returns a ClusterSpec object based on the latest info from Kubernetes. - - We retrieve the information from the Kubernetes master every time this - method is called. - - Returns: - A ClusterSpec containing host information returned from Kubernetes. - - Raises: - RuntimeError: If any of the pods returned by the master is not in the - `Running` phase. - """ - if not self._override_client: - k8sconfig.load_kube_config() - - client = self._override_client or k8sclient.CoreV1Api() - cluster_map = {} - - for tf_job in self._job_to_label_mapping: - all_pods = [] - for selector in self._job_to_label_mapping[tf_job]: - ret = client.list_pod_for_all_namespaces(label_selector=selector) - selected_pods = [] - - # Sort the list by the name to make sure it doesn't change call to call. - for pod in sorted(ret.items, key=lambda x: x.metadata.name): - if pod.status.phase == 'Running': - selected_pods.append( - '%s:%s' % (pod.status.host_ip, self._tf_server_port)) - else: - raise RuntimeError('Pod "%s" is not running; phase: "%s"' % - (pod.metadata.name, pod.status.phase)) - all_pods.extend(selected_pods) - cluster_map[tf_job] = all_pods - - return server_lib.ClusterSpec(cluster_map) diff --git a/tensorflow/contrib/cluster_resolver/python/training/slurm_cluster_resolver.py b/tensorflow/contrib/cluster_resolver/python/training/slurm_cluster_resolver.py index dabe2fe1d3..fcd2a846ee 100644 --- a/tensorflow/contrib/cluster_resolver/python/training/slurm_cluster_resolver.py +++ b/tensorflow/contrib/cluster_resolver/python/training/slurm_cluster_resolver.py @@ -12,185 +12,24 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Implementation of Cluster Resolvers for Slurm workload manager.""" +"""Stub file for SlurmClusterResolver to maintain backwards compatibility.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function -import collections -import os -import subprocess +# This file (and all files in this directory in general) is a backwards +# compatibility shim that exists to re-export ClusterResolvers such that +# existing OSS code will not be broken. -from tensorflow.contrib.cluster_resolver.python.training.cluster_resolver import ClusterResolver -from tensorflow.python.training.server_lib import ClusterSpec +# pylint: disable=unused-import +from tensorflow.python.distribute.cluster_resolver.slurm_cluster_resolver import SlurmClusterResolver +# pylint: enable=unused-import +from tensorflow.python.util.all_util import remove_undocumented -class SlurmClusterResolver(ClusterResolver): - """Cluster Resolver for system with Slurm workload manager. +_allowed_symbols = [ + 'SlurmClusterResolver', +] - This is an implementation of cluster resolvers for Slurm clusters. This allows - the specification of jobs and task counts, number of tasks per node, number of - GPUs on each node and number of GPUs for each task, It retrieves system - attributes by Slurm environment variables, resolves allocated computing node - names, construct a cluster and return a Cluster Resolver object which an be - use for distributed TensorFlow. - """ - - def _resolve_hostnames(self): - """Resolve host names of nodes allocated in current jobs. - - Returns: - A list of node names as strings. - """ - hostlist = (subprocess.check_output(['scontrol', 'show', 'hostname']). - decode('utf-8').strip().split('\n')) - return hostlist - - def __init__(self, - jobs, - port_base=8888, - gpus_per_node=1, - gpus_per_task=1, - tasks_per_node=None, - auto_set_gpu=True): - """Creates a new SlurmClusterResolver object. - - This takes in parameters and creates a SlurmClusterResolver object. It uses - those parameters to check which nodes will processes reside and resolves - their hostnames. With the number of the GPUs on each node and number of GPUs - for each task it offsets the port number for each processes and allocate - GPUs to tasks by setting environment variables. The resolver currently - supports homogeneous tasks and default Slurm process allocation. - - Args: - jobs: Dictionary with job names as key and number of tasks in the job as - value - port_base: The first port number to start with for processes on a node. - gpus_per_node: Number of GPUs available on each node. - gpus_per_task: Number of GPUs to be used for each task. - tasks_per_node: Number of tasks to run on each node, if not set defaults - to Slurm's output environment variable SLURM_NTASKS_PER_NODE. - auto_set_gpu: Set the visible CUDA devices automatically while resolving - the cluster by setting CUDA_VISIBLE_DEVICES environment variable. - Defaults to True. - - Returns: - A ClusterResolver object which can be used with distributed TensorFlow. - - Raises: - RuntimeError: If requested more GPUs per node then available or requested - more tasks then assigned tasks. - """ - - # check if launched by mpirun - if 'OMPI_COMM_WORLD_RANK' in os.environ: - self._rank = int(os.environ['OMPI_COMM_WORLD_RANK']) - num_tasks = int(os.environ['OMPI_COMM_WORLD_SIZE']) - else: - self._rank = int(os.environ['SLURM_PROCID']) - num_tasks = int(os.environ['SLURM_NTASKS']) - - self._jobs = collections.OrderedDict(sorted(jobs.items())) - self._port_base = port_base - - # user specification overrides SLURM specification - if tasks_per_node is not None: - self._tasks_per_node = tasks_per_node - elif tasks_per_node is None and 'SLURM_NTASKS_PER_NODE' in os.environ: - self._tasks_per_node = int(os.environ['SLURM_NTASKS_PER_NODE']) - else: - raise RuntimeError('Neither `tasks_per_node` or ' - 'SLURM_NTASKS_PER_NODE is set.') - - self._gpus_per_node = gpus_per_node - self._gpus_per_task = gpus_per_task - - self._auto_set_gpu = auto_set_gpu - self._job_name = None - self._task_index = None - - self._gpu_allocation = [] - self._cluster_allocation = {} - - if self._tasks_per_node * self._gpus_per_task > self._gpus_per_node: - raise RuntimeError('Requested more GPUs per node then available.') - - if sum(self._jobs.values()) != num_tasks: - raise RuntimeError('Requested more tasks then assigned tasks.') - - def cluster_spec(self): - """Returns a ClusterSpec object based on the latest instance group info. - - This returns a ClusterSpec object for use based on information from the - specified initialization parameters and Slurm environment variables. The - cluster specification is resolved each time this function is called. The - resolver extract hostnames of nodes by scontrol and pack tasks in that - order until a node a has number of tasks that is equal to specification. - GPUs on nodes are allocated to tasks by specification through setting - CUDA_VISIBLE_DEVICES environment variable. - - Returns: - A ClusterSpec containing host information retrieved from Slurm's - environment variables. - """ - hostlist = self._resolve_hostnames() - - task_list = [] - self._gpu_allocation = [] - self._cluster_allocation = {} - - for host in hostlist: - for port_offset, gpu_offset in zip( - range(self._tasks_per_node), - range(0, self._gpus_per_node, self._gpus_per_task)): - - host_addr = '%s:%d' % (host, self._port_base + port_offset) - task_list.append(host_addr) - gpu_id_list = [] - - for gpu_id in range(gpu_offset, gpu_offset + self._gpus_per_task): - gpu_id_list.append(str(gpu_id)) - - self._gpu_allocation.append(','.join(gpu_id_list)) - - cluster_rank_offset_start = 0 - cluster_rank_offset_end = 0 - - for job_name, num_tasks in self._jobs.items(): - cluster_rank_offset_end = cluster_rank_offset_start + num_tasks - - self._cluster_allocation[job_name] = \ - task_list[cluster_rank_offset_start:cluster_rank_offset_end] - - if self._rank >= cluster_rank_offset_start and \ - self._rank < cluster_rank_offset_end: - - self._job_name = job_name - self._task_index = self._rank - cluster_rank_offset_start - - cluster_rank_offset_start = cluster_rank_offset_end - - if self._auto_set_gpu is True: - os.environ['CUDA_VISIBLE_DEVICES'] = self._gpu_allocation[self._rank] - - return ClusterSpec(self._cluster_allocation) - - def get_task_info(self): - """Returns job name and task_index for the process which calls this. - - This returns the job name and task index for the process which calls this - function according to its rank and cluster specification. The job name and - task index are set after a cluster is constructed by cluster_spec otherwise - defaults to None. - - Returns: - A string specifying job name the process belongs to and an integner - specifying the task index the process belongs to in that job. - """ - return self._job_name, self._task_index - - def master(self, task_type=None, task_index=None): - if task_type and task_index: - return self.cluster_spec().task_address(task_type, task_index) - return self._cluster_allocation[str(self._job_name)][self._task_index] +remove_undocumented(__name__, _allowed_symbols) diff --git a/tensorflow/contrib/cluster_resolver/python/training/tfconfig_cluster_resolver.py b/tensorflow/contrib/cluster_resolver/python/training/tfconfig_cluster_resolver.py index 7bbd189d03..9db7f47dcb 100644 --- a/tensorflow/contrib/cluster_resolver/python/training/tfconfig_cluster_resolver.py +++ b/tensorflow/contrib/cluster_resolver/python/training/tfconfig_cluster_resolver.py @@ -12,81 +12,25 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Implementation of Cluster Resolvers for TF_CONFIG Environment Variables.""" - +"""Stub file for TFConfigClusterResolver to maintain backwards compatibility.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function -import json -import os - -from tensorflow.contrib.cluster_resolver.python.training.cluster_resolver import ClusterResolver -from tensorflow.python.training.server_lib import ClusterSpec - -_TF_CONFIG_ENV = 'TF_CONFIG' -_SESSION_MASTER_KEY = 'session_master' - - -class TFConfigClusterResolver(ClusterResolver): - """Implementation of a ClusterResolver which reads the TF_CONFIG EnvVar.""" - - def _load_tf_config(self): - return json.loads(os.environ.get(_TF_CONFIG_ENV, '{}')) - - def cluster_spec(self): - """Returns a ClusterSpec based on the TF_CONFIG environment variable. - - Returns: - A ClusterSpec with information from the TF_CONFIG environment variable. - """ - tf_config = self._load_tf_config() - if 'cluster' not in tf_config: - return ClusterSpec({}) - return ClusterSpec(tf_config['cluster']) - - def master(self, task_type=None, task_index=0): - """Returns the master address to use when creating a TensorFlow session. - - Args: - task_type: (String, optional) Overrides and sets the task_type of the - master. - task_index: (Integer, optional) Overrides and sets the task id of the - master. - - Returns: - The address of the master. - - Raises: - RuntimeError: If the task_type or task_id is not specified and the - `TF_CONFIG` environment variable does not contain a task section. - """ +# This file (and all files in this directory in general) is a backwards +# compatibility shim that exists to re-export ClusterResolvers such that +# existing OSS code will not be broken. - # If `session_master` is set, just use that. - tf_config = self._load_tf_config() - if _SESSION_MASTER_KEY in tf_config: - return tf_config[_SESSION_MASTER_KEY] +# pylint: disable=unused-import +from tensorflow.python.distribute.cluster_resolver.tfconfig_cluster_resolver import TFConfigClusterResolver +# pylint: enable=unused-import - if 'rpc_layer' in tf_config: - rpclayer = '%s://' % tf_config['rpc_layer'] - else: - rpclayer = '' +from tensorflow.python.util.all_util import remove_undocumented - # Return an empty string if we are the only job in the ClusterSpec. - cluster_spec = self.cluster_spec() - if (not cluster_spec.jobs or - (len(cluster_spec.jobs) == 1 and - len(cluster_spec.job_tasks(cluster_spec.jobs[0])) == 1)): - return '' +_allowed_symbols = [ + 'TFConfigClusterResolver', +] - # We try to auto-detect the task type and id, but uses the user-supplied one - # where available - if not task_type: - if 'task' not in tf_config: - raise RuntimeError('You must either specify a `task_type`, or your ' - 'TF_CONFIG must contain a `task` section.') - task_type = tf_config['task']['type'] - task_index = tf_config['task']['index'] +remove_undocumented(__name__, _allowed_symbols) - return rpclayer + cluster_spec.task_address(task_type, task_index) diff --git a/tensorflow/contrib/cluster_resolver/python/training/tpu_cluster_resolver.py b/tensorflow/contrib/cluster_resolver/python/training/tpu_cluster_resolver.py index 1f6803a9ff..3a1eaccd06 100644 --- a/tensorflow/contrib/cluster_resolver/python/training/tpu_cluster_resolver.py +++ b/tensorflow/contrib/cluster_resolver/python/training/tpu_cluster_resolver.py @@ -1,4 +1,4 @@ -# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# Copyright 2018 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. @@ -12,341 +12,24 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Implementation of Cluster Resolvers for Cloud TPUs.""" +"""Stub file for TPUClusterResolver to maintain backwards compatibility.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function -import os +# This file (and all files in this directory in general) is a backwards +# compatibility shim that exists to re-export ClusterResolvers such that +# existing OSS code will not be broken. -from six.moves.urllib.request import Request -from six.moves.urllib.request import urlopen +# pylint: disable=unused-import +from tensorflow.python.distribute.cluster_resolver.tpu_cluster_resolver import TPUClusterResolver +# pylint: enable=unused-import -from tensorflow.contrib.cluster_resolver.python.training.cluster_resolver import ClusterResolver -from tensorflow.python.training import server_lib -from tensorflow.python.util import compat +from tensorflow.python.util.all_util import remove_undocumented -_GOOGLE_API_CLIENT_INSTALLED = True -try: - from googleapiclient import discovery # pylint: disable=g-import-not-at-top - from oauth2client.client import GoogleCredentials # pylint: disable=g-import-not-at-top -except ImportError: - _GOOGLE_API_CLIENT_INSTALLED = False +_allowed_symbols = [ + 'TPUClusterResolver', +] - -_GKE_ENV_VARIABLE = 'KUBE_GOOGLE_CLOUD_TPU_ENDPOINTS' -_ENDPOINTS_SEPARATOR = ',' -_DEFAULT_ENV_VARIABLE = 'TPU_NAME' -_DISCOVERY_SERVICE_URL_ENV_VARIABLE = 'TPU_API_DISCOVERY_URL' - - -class TPUClusterResolver(ClusterResolver): - """Cluster Resolver for Google Cloud TPUs. - - This is an implementation of cluster resolvers for the Google Cloud TPU - service. As Cloud TPUs are in alpha, you will need to specify a API definition - file for this to consume, in addition to a list of Cloud TPUs in your Google - Cloud Platform project. - """ - - def _tpuService(self): - """Creates a new Cloud TPU API object. - - This works around an issue where the underlying HTTP connection sometimes - times out when the script has been running for too long. Other methods in - this object calls this method to get a new API object whenever they need - to communicate with the Cloud API. - - Returns: - A Google Cloud TPU API object. - """ - if self._service: - return self._service - - credentials = self._credentials - if credentials is None or credentials == 'default': - credentials = GoogleCredentials.get_application_default() - - if self._discovery_url: - return discovery.build( - 'tpu', 'v1alpha1', - credentials=credentials, - discoveryServiceUrl=self._discovery_url) - else: - return discovery.build( - 'tpu', 'v1alpha1', - credentials=credentials) - - def _requestComputeMetadata(self, path): - req = Request('http://metadata/computeMetadata/v1/%s' % path, - headers={'Metadata-Flavor': 'Google'}) - resp = urlopen(req) - return compat.as_bytes(resp.read()) - - def _shouldResolve(self): - if (self._tpu == compat.as_bytes('') or - self._tpu == compat.as_bytes('local') or - self._tpu.startswith(compat.as_bytes('/bns')) or - self._tpu.startswith(compat.as_bytes('localhost:')) or - self._tpu.startswith(compat.as_bytes('grpc://'))): - return False - return True - - @staticmethod - def _inGke(): - """When running in GKE, the environment variable will be set.""" - return _GKE_ENV_VARIABLE in os.environ - - @staticmethod - def _gkeEndpoints(): - return os.environ[_GKE_ENV_VARIABLE] - - @staticmethod - def _envVarFallback(): - if _DEFAULT_ENV_VARIABLE in os.environ: - return os.environ[_DEFAULT_ENV_VARIABLE] - return None - - @staticmethod - def _environmentDiscoveryUrl(): - return os.environ.get(_DISCOVERY_SERVICE_URL_ENV_VARIABLE) - - def __init__(self, - tpu=None, - zone=None, - project=None, - job_name='worker', - coordinator_name=None, - coordinator_address=None, - credentials='default', - service=None, - discovery_url=None): - """Creates a new TPUClusterResolver object. - - The ClusterResolver will then use the parameters to query the Cloud TPU APIs - for the IP addresses and ports of each Cloud TPU listed. - - Args: - tpu: Either a string, or a list of strings corresponding to the TPUs to - use. If the single string is the empty string, the string 'local', or a - string that begins with 'grpc://' or '/bns', then it is assumed to not - correspond with a Cloud TPU and will instead be passed as the session - master and no ClusterSpec propagation will be done. - zone: Zone where the TPUs are located. If omitted or empty, we will assume - that the zone of the TPU is the same as the zone of the GCE VM, which we - will try to discover from the GCE metadata service. - project: Name of the GCP project containing Cloud TPUs. If omitted or - empty, we will try to discover the project name of the GCE VM from the - GCE metadata service. - job_name: Name of the TensorFlow job the TPUs belong to. - coordinator_name: The name to use for the coordinator. Set to None if the - coordinator should not be included in the computed ClusterSpec. - coordinator_address: The address of the coordinator (typically an ip:port - pair). If set to None, a TF server will be started. If coordinator_name - is None, a TF server will not be started even if coordinator_address is - None. - credentials: GCE Credentials. If None, then we use default credentials - from the oauth2client - service: The GCE API object returned by the googleapiclient.discovery - function. If you specify a custom service object, then the credentials - parameter will be ignored. - discovery_url: A URL template that points to the location of - the discovery service. It should have two parameters {api} and - {apiVersion} that when filled in produce an absolute URL to the - discovery document for that service. The environment variable - 'TPU_API_DISCOVERY_URL' will override this. - - Raises: - ImportError: If the googleapiclient is not installed. - ValueError: If no TPUs are specified. - """ - if isinstance(tpu, list): - if not tpu: - raise ValueError('At least one TPU must be specified.') - if len(tpu) != 1: - raise NotImplementedError( - 'Using multiple TPUs in a single session is not yet implemented') - tpu = tpu[0] - - in_gke = self._inGke() - # When using GKE with Cloud TPUs, the env variable will be set. - if tpu is None: - if in_gke: - tpu = self._gkeEndpoints() - else: - tpu = self._envVarFallback() - - if tpu is None: - raise ValueError('Please provide a TPU Name to connect to.') - - self._tpu = compat.as_bytes(tpu) # self._tpu is always bytes - self._job_name = job_name - - # Whether we should actually attempt to contact Cloud APIs - should_resolve = self._shouldResolve() - - # We error out if we are in a non-Cloud environment which cannot talk to the - # Cloud APIs using the standard class and a special object is not passed in. - self._service = service - if (self._service is None and should_resolve and - not _GOOGLE_API_CLIENT_INSTALLED): - raise ImportError('googleapiclient and oauth2client must be installed ' - 'before using the TPU cluster resolver. Execute: ' - '`pip install --upgrade google-api-python-client` ' - 'and `pip install --upgrade oauth2client` to ' - 'install with pip.') - - # We save user-passed credentials, unless the user didn't pass in anything. - self._credentials = credentials - if (credentials == 'default' and should_resolve and - _GOOGLE_API_CLIENT_INSTALLED): - self._credentials = None - - # Automatically detect project and zone if unspecified. - if not project and should_resolve: - project = compat.as_str( - self._requestComputeMetadata('project/project-id')) - if not zone and should_resolve: - zone_path = compat.as_str(self._requestComputeMetadata('instance/zone')) - zone = zone_path.split('/')[-1] - self._project = project - self._zone = zone - - self._discovery_url = self._environmentDiscoveryUrl() or discovery_url - - self._coordinator_name = coordinator_name - if (coordinator_name and not coordinator_address and - (should_resolve or in_gke)): - self._start_local_server() - else: - self._coordinator_address = coordinator_address - - def master(self, task_type=None, task_index=None): - """Get the Master string to be used for the session. - - In the normal case, this returns the grpc path (grpc://1.2.3.4:8470) of - first instance in the ClusterSpec returned by the cluster_spec function. - - If a non-TPU name is used when constructing a TPUClusterResolver, that will - be returned instead (e.g. If the tpus argument's value when constructing - this TPUClusterResolver was 'grpc://10.240.1.2:8470', - 'grpc://10.240.1.2:8470' will be returned). - - Args: - task_type: (Optional) The type of the TensorFlow task of the master. - task_index: (Optional) The index of the TensorFlow task of the master. - - Returns: - string, the connection string to use when creating a session. - - Raises: - ValueError: If none of the TPUs specified exists. - """ - if not self._shouldResolve(): - return self._tpu.split(compat.as_bytes(_ENDPOINTS_SEPARATOR))[0] - - cluster_spec = self.cluster_spec() - if task_type and task_index: - return cluster_spec.task_address(task_type, task_index) - - job_tasks = cluster_spec.job_tasks(self._job_name) - if not job_tasks: - raise ValueError('No TPUs exists with the specified names exist.') - - return 'grpc://' + job_tasks[0] - - def get_master(self): - return self.master() - - def get_job_name(self): - if self._shouldResolve(): - return self._job_name - - def cluster_spec(self): - """Returns a ClusterSpec object based on the latest TPU information. - - We retrieve the information from the GCE APIs every time this method is - called. - - Returns: - A ClusterSpec containing host information returned from Cloud TPUs. - - Raises: - RuntimeError: If the provided TPU is not healthy. - """ - ############################################################################ - # There are 5 potential cases this code must handle: - # 1. [Normal case.] We should resolve the TPU name to a set of tasks, and - # a. Create a ClusterSpec that includes the coordinator job - # b. Create a ClusterSpec without the coordinator job. - # 2. [GKE / No API Access.] We should not resolve the TPU name to a set of - # tasks and - # a. Create a ClusterSpec with the coordinator - # b. Create a ClusterSpec without the coordinator - # 3. [Other (legacy non-gRPC).] We should return an empty ClusterSpec. - ############################################################################ - - if self._shouldResolve(): - # Case 1. - full_name = 'projects/%s/locations/%s/nodes/%s' % ( - self._project, self._zone, compat.as_text(self._tpu)) - service = self._tpuService() - request = service.projects().locations().nodes().get(name=full_name) - response = request.execute() - - if 'state' in response and response['state'] != 'READY': - raise RuntimeError('TPU "%s" is not yet ready; state: "%s"' % - (compat.as_text(self._tpu), response['state'])) - - if 'health' in response and response['health'] != 'HEALTHY': - raise RuntimeError('TPU "%s" is unhealthy: "%s"' % - (compat.as_text(self._tpu), response['health'])) - - if 'networkEndpoints' in response: - worker_list = [ - '%s:%s' % (endpoint['ipAddress'], endpoint['port']) - for endpoint in response['networkEndpoints'] - ] - else: - # Fall back to the deprecated response format - instance_url = '%s:%s' % (response['ipAddress'], response['port']) - worker_list = [instance_url] - - cluster_spec = {self._job_name: worker_list} - else: - if not self._tpu.startswith(compat.as_bytes('grpc://')): - # Case 3. - return None - # Case 2. - cluster_spec = { - self._job_name: [ - x[len(compat.as_bytes('grpc://')):] - for x in self._tpu.split(compat.as_bytes(_ENDPOINTS_SEPARATOR)) - ] - } - - if self._coordinator_address: - # {1, 2}.a - cluster_spec[self._coordinator_name] = [self._coordinator_address] - - return server_lib.ClusterSpec(cluster_spec) - - def _start_local_server(self): - address = self._requestComputeMetadata('instance/network-interfaces/0/ip') - self._server = server_lib.Server( - { - 'local': ['0.0.0.0:0'] - }, protocol='grpc', config=None, start=True) - # self._server.target is of the form: grpc://ipaddress:port - target = compat.as_bytes(self._server.target) - splits = target.split(compat.as_bytes(':')) - assert len(splits) == 3, self._server.target - assert splits[0] == compat.as_bytes('grpc'), self._server.target - self._coordinator_port = compat.as_text(splits[2]) - self._coordinator_address = '%s:%s' % ( - address, compat.as_text(self._coordinator_port)) - - def __deepcopy__(self, memo): - # TODO(b/73668574): Remove this once RunConfig avoids performing deepcopy. - return self +remove_undocumented(__name__, _allowed_symbols) diff --git a/tensorflow/contrib/cmake/CMakeLists.txt b/tensorflow/contrib/cmake/CMakeLists.txt index a63366e136..124d6cfd47 100644 --- a/tensorflow/contrib/cmake/CMakeLists.txt +++ b/tensorflow/contrib/cmake/CMakeLists.txt @@ -12,7 +12,7 @@ if(WIN32) endif() # Project -project(tensorflow C CXX) +project(tensorflow VERSION 1.12.0 LANGUAGES C CXX) # Set C++14 as standard for the whole project set(CMAKE_CXX_STANDARD 14) @@ -193,6 +193,7 @@ if(WIN32) set(CMAKE_SUPPRESS_REGENERATION ON) endif() + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions -std=c++11") endif() @@ -281,6 +282,14 @@ else (systemlib_ZLIB) ${zlib_STATIC_LIBRARIES}) endif (systemlib_ZLIB) +if (systemlib_ABSEIL_CPP) + set(tensorflow_EXTERNAL_LIBRARIES ${tensorflow_EXTERNAL_LIBRARIES} + ${abseil_cpp_LIBRARIES}) +else (systemlib_ABSEIL_CPP) + set(tensorflow_EXTERNAL_LIBRARIES ${tensorflow_EXTERNAL_LIBRARIES} + ${abseil_cpp_STATIC_LIBRARIES}) +endif (systemlib_ABSEIL_CPP) + set(tensorflow_EXTERNAL_DEPENDENCIES zlib_copy_headers_to_destination gif_copy_headers_to_destination @@ -394,6 +403,7 @@ if (tensorflow_ENABLE_GPU) set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS};--include-path ${PROJECT_BINARY_DIR}/$\{build_configuration\};--expt-relaxed-constexpr) set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS};-ftz=true) # Flush denormals to zero set(CUDA_INCLUDE ${CUDA_TOOLKIT_TARGET_DIR} ${CUDA_TOOLKIT_TARGET_DIR}/extras/CUPTI/include) + include_directories(${CUDA_INCLUDE}) if (WIN32) add_definitions(-DGOOGLE_CUDA=1 -DTF_EXTRA_CUDA_CAPABILITIES=3.7,5.2,6.0,6.1,7.0) @@ -546,14 +556,20 @@ if (tensorflow_ENABLE_GPU) cudnn_version_number=${tensorflow_CUDNN_VERSION}) endif(WIN32) else(tensorflow_ENABLE_GPU) - set(tensorflow_BUILD_INFO_FLAGS --build_config cpu --key_value - msvcp_dll_name=msvcp140.dll) + if(WIN32) + set(tensorflow_BUILD_INFO_FLAGS --build_config cpu --key_value + msvcp_dll_name=msvcp140.dll) + else() + set(tensorflow_BUILD_INFO_FLAGS --build_config cpu) + endif() endif(tensorflow_ENABLE_GPU) -# Find python executable -include(FindPythonInterp) -if(NOT ${PYTHONINTERP_FOUND}) - message(FATAL_ERROR "CMake was unable to find a python interpreter.") +if(tensorflow_BUILD_PYTHON_BINDINGS) + # Find python executable + include(FindPythonInterp) + if(NOT ${PYTHONINTERP_FOUND}) + message(FATAL_ERROR "CMake was unable to find a python interpreter.") + endif() endif() # Let's get to work! @@ -574,6 +590,7 @@ include(tf_cc_ops.cmake) include(tf_c.cmake) include(tf_grappler.cmake) include(tf_core_profiler.cmake) +include(tf_core_eager_runtime.cmake) if(tensorflow_BUILD_CC_EXAMPLE) include(tf_tutorials.cmake) include(tf_label_image_example.cmake) @@ -587,4 +604,4 @@ if(tensorflow_BUILD_SHARED_LIB) endif() if(tensorflow_BUILD_CC_TESTS OR tensorflow_BUILD_PYTHON_TESTS) include(tf_tests.cmake) -endif() +endif() \ No newline at end of file diff --git a/tensorflow/contrib/cmake/README.md b/tensorflow/contrib/cmake/README.md index 84c679162c..df5ff6cd53 100644 --- a/tensorflow/contrib/cmake/README.md +++ b/tensorflow/contrib/cmake/README.md @@ -6,9 +6,9 @@ platforms. For details, see the [TensorFlow install guide](https://www.tensorflow.org/install/). This directory contains CMake files for building TensorFlow on Microsoft -Windows. [CMake](https://cmake.org) is a cross-platform tool that can +Windows and Linux. [CMake](https://cmake.org) is a cross-platform tool that can generate build scripts for multiple build systems, including Microsoft -Visual Studio. +Visual Studio and GCC. "The method has not been tested on Mac OS X. **N.B.** We provide Linux build instructions primarily for the purpose of testing the build. We recommend using the standard Bazel-based build on @@ -23,6 +23,7 @@ for instructions on how to install a pre-built TensorFlow package on Windows. ### Current known limitations * It is not possible to load a custom Op library. * GCS file system is not supported. +* Debug build is not available since Python for Windows is no longer distributed with a debug library. ## Building with CMake @@ -53,12 +54,12 @@ bindings. ### Known-good configurations * Microsoft Windows 10 - - Microsoft Visual Studio Enterprise 2015 with Visual C++ 2015 + - Microsoft Visual Studio Enterprise/ Community 2015 with Visual C++ 2015 - [Anaconda 4.1.1 (Python 3.5 64-bit)](https://www.anaconda.com/download/) - [Git for Windows version 2.9.2.windows.1](https://git-scm.com/download/win) - [swigwin-3.0.10](http://www.swig.org/download.html) - - [NVidia CUDA Toolkit 8.0](https://developer.nvidia.com/cuda-downloads) - - [NVidia CUDNN 5.1](https://developer.nvidia.com/cudnn) + - [NVidia CUDA Toolkit 9.0](https://developer.nvidia.com/cuda-downloads) + - [NVidia CUDNN 7](https://developer.nvidia.com/cudnn) - [CMake 3.6](https://cmake.org/files/v3.6/cmake-3.6.3-win64-x64.msi) * Ubuntu 14.04 @@ -66,8 +67,8 @@ bindings. - Docker 1.9.1 (for automated testing) ### Current known limitations - - The Python package supports **Python 3.5 only**, because that is the only - version for which standard Python binaries exist and those binaries are + - The Python package supports **Python 3.5/3.6 only**, because these are the only + versions for which standard Python binaries exist and those binaries are compatible with the TensorFlow runtime. (On Windows, the standard Python binaries for versions earlier than 3.5 were compiled with older compilers that do not have all of the features (e.g. C++11 support) needed to compile @@ -104,8 +105,151 @@ We are actively working on improving CMake and Windows support, and addressing these limitations. We would appreciate pull requests that implement missing ops or APIs. +CMake GUI build (all platforms) +================================== +Install from CMake GUI would be a convenient way to generate C++ build projects. The software supports Windows, MacOS and Linux, while the posix platform provides an extra ccmake binary to run command line GUI. Both working principal of cmake, ccmake and cmake-gui are the same, the only difference is by providing suitable interface for project configuration and dependency setting. + +0. Pre-buid checklist: + The following binary/libraries should be setted in system path, otherwise you need to set manualy via cmake. + * Compiler (GCC for Linux, MSVC for Windows) + * Make sure compiler directory has been set to system path + * CUDA 9.0 (GPU build) + * CUDNN (GPU build) + * NCCL (GPU build on Linux) + * SWIG (python binding) + * Perl (required if you need ssl support, optional) + * Go (required if you need ssl support, optional) + * NASM/YASM (required by grpc for ssl support, optional) +1. Start CMake GUI +2. Click on `Browse Source` and direct to the the folder `/tensorflow/contrib/cmake` +3. Click on `Browse Build` and spectify a location that you want tensorflow to be build +4. Click on `Configure`, a new window will be prompted out, specify the generator mode for the project generation. For Windows, choose `Visual Studio Win64`, for Linux, choose `Unix Makefiles`, then press `Finish`. Wait for a moment, the default project dependecy would automatically generate. +5. There are a few options that you can customize your own build. **The setting here is crucial for a sucessful build, please check all items carefully.** + * `tensorflow_BUILD_ALL_KERNELS` should alway be `on` + * `tensorflow_BUILD_CC_EXAMPLE` is default to be `on`. This can help you to test build (optional) + * `tensorflow_BUILD_CONTRIB_KERNELS` is default to be `on`, but it won't affect tensorflow function, turn it to `off` if you want a slim build. (optional) + * `tensorflow_BUILD_PYTHON_BINDING` is default to be `on`. Set to `off` if you don't need python interaface. If SWIG is not in system path, you need set it manually. (optional) + * `tensorflow_BUILD_SHARED_LIB` is default to be `off`. Set to `on` if you want the c++ interface. (optional) + * `tensorflow_ENABLE_GPU` is default to be `off`. Set to `on` if you want GPU support. It will search CUDA and CUDNN dependecies if you have set them to system path, otherwise CMake would prompt error and request you to set it manually. (optional) + * `tensorflow_ENABLE_GRPC_SUPPORT` is default to be `on`. For Linux build, this option must always be `on`. This need to be `on` for a gpu build. Reminded that Perl, Go and NASM/YASM are required for this option if you want to build grpc with offical SSL support. + * `tensorflow_ENABLE_POSITION_INDEPENDENT_CODE` should always be `on` + * `tensorflow_ENABLE_SNAPPY_SUPPORT` should always be `on` + * `tensorflow_OPTIMIZE_FOR_NATIVE_ARCH` should always be `on` + * `CMAKE_INSTALL_PREFIX` is the location where the final package will be installed. You may change it to your own preferred path (optional) + +6. After changing the configuration in step 5, press `Configure` again +7. If not error is found, press `Generate` + +#### Windows + +1. Open `tensorflow.sln` in the build folder (Windows). Change build type from `Debug` to `Release`. Choose `Build`->`Build Solution`. This may take more than hours of compilation. If everything is alright, the output window would show no error. + + ##### Python + + In solution explorer, right click on `tf_python_build_pip_package` -> `build`. It will generate the wheel file in `/tf_python/dist`. Install with following command: + + ```pip install --upgrade tensorflow-.whl``` + + ***The wheel name varies depends on you config. Change to your own wheel filename.*** + + Reminded that some pip installation requires administrator right command prompt. + + ##### C++ + + You can directly use the build folder tree for C++ interface with cmake. If you want to do installation for api releasing, right click on `Install` -> `build`. The headers and library will be installed in the directory specify by `CMAKE_INSTALL_PREFIX` during configuration. + +2. For smaller RAM computer, it is noticed that out of heap space error appears. Change to command prompt build is an alternative to do step 1. + + Open `VS2015 x64 Native Tools Command Prompt`. You can open it by press `Start`, then type the binary name. Use `VS2017 x64 Native Tools Command Prompt` if you are using MSVC 2017. + + ##### Python + + Directly build python wheel package by following command: + + ```MSBuild /p:Configuration=Release ``` + + Remember to change `` to the actual path of the file, it can be found at the root of build directory + + Install the wheel file generated as instructed by step 1. + + ##### C++ interface + Build from VS native toolchain with following command: + ```MSBuild /p:Configuration=Release ``` + + Headers are discretely located in the build folders. Tensorflow library can be found at `/Release`, namely `tensorflow.dll` and `tensorflow.lib`. + + * Build to install for api release (optional): + ```MSBuild /p:Configuration=Release ``` + + Remember to change `` and `` to the actual path of the file, it can be found at the root of build directory. + +#### Linux/MacOS (command line GNU build) + +1. Open the terminal, change working directory to the one specified in step 3. + +2. Type the following command: + + ```make -sj all``` + + ##### Python + + **Important Note** CMake generated python wheel for Linux/MacOs is currently under development. Please use bazel build. + + Follow code is an expected Linux/MacOS python package build after development work is completed. + + ``` + make -sj tf_python_build_pip_package + cd tf_python + pip install --upgrade tensorflow-.whl + ``` + + ##### C++ interface + + ```make -sj install``` + + Where `` is the threads used for the compilation, change to any integer less or equal to your computer's maxiumum thread number. + + Headers are discretely located in the build folders. Tensorflow library can be found at ``, namely `tensorflow.so` (Linux) or `tensorflow.dylib` (MacOS). + +#### Start a Tensorflow C++ project with CMake +Here we assume that you have basic knowledge on gathering dependency with `CMakeLists.txt`. Here we introduce how the C++ api works with [official hello world tutorial](https://www.tensorflow.org/api_guides/cc/guide). + +1. Create a new working directory and create a new text file named `CMakeLists.txt` and the c++ file `main.cxx` +2. Fill in the `main.cxx` with the code provided in [official c++ api basic](https://www.tensorflow.org/api_guides/cc/guide). +3. Fill in the `CMakeLists.txt` with following code: + ``` cmake + cmake_minimum_required (VERSION 2.6) + project (tf_hello) + + # Tensorflow + find_package(Tensorflow REQUIRED) + include_directories(${TENSORFLOW_INCLUDE_DIRS}) + + # compiler setting required by tensorflow, to be tested on all compilers + # currently only tested on MSVC and GCC + if (${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC) + add_definitions(-DCOMPILER_MSVC) + elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL GNU) + if (${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS "3") + add_definitions(-DCOMPILER_GCC3) + else() + add_definitions(-D__GNUC__) + endif() + else() + message(ERROR " compiler ${CMAKE_CXX_COMPILER_ID} not supported by this CMakeList.txt, under development") + endif() + + add_executable(tf_hello main.cxx) + target_link_libraries(tf_hello ${TENSORFLOW_LIBRARIES}) + ``` +4. Configure the folder with cmake-gui, an error should be prompted out, requesting you to locate the folder containing `TensorflowConfig.cmake`. This file can be found at `` or `` (for those have build install in previous steps). + +5. Configure again, generate the project. +6. Compile the project with `Release` config (Windows). For Linux users, just compile the project. +7. Copy the `tensorflow.dll`(Windows)/`tensorflow.so`(Linux) from build directory to the build folder containing `tf_hello` binary. +8. Run `tf_hello` binary -Step-by-step Windows build +Step-by-step Windows build (command prompt) ========================== 1. Install the prerequisites detailed above, and set up your environment. @@ -292,4 +436,4 @@ $ cd tensorflow $ tensorflow/tools/ci_build/ci_build.sh CMAKE tensorflow/tools/ci_build/builds/cmake.sh ``` -That's it. Dependencies included. +That's it. Dependencies included. \ No newline at end of file diff --git a/tensorflow/contrib/cmake/TensorflowConfig.cmake.in b/tensorflow/contrib/cmake/TensorflowConfig.cmake.in new file mode 100644 index 0000000000..cc04db6e95 --- /dev/null +++ b/tensorflow/contrib/cmake/TensorflowConfig.cmake.in @@ -0,0 +1,16 @@ +# - Config file for the Tensorflow package +# It defines the following variables +# TENSORFLOW_INCLUDE_DIRS - include directories for FooBar +# TENSORFLOW_LIBRARIES - libraries to link against + +# Compute paths +get_filename_component(TENSORFLOW_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) +set(TENSORFLOW_INCLUDE_DIRS "@CONF_INCLUDE_DIRS@") + +# Our library dependencies (contains definitions for IMPORTED targets) +if(NOT TENSORFLOW_BINARY_DIR) + include("${TENSORFLOW_CMAKE_DIR}/TensorflowTargets.cmake") +endif() + +# These are IMPORTED targets created by TensorflowTargets.cmake +set(TENSORFLOW_LIBRARIES tensorflow) \ No newline at end of file diff --git a/tensorflow/contrib/cmake/TensorflowConfigVersion.cmake.in b/tensorflow/contrib/cmake/TensorflowConfigVersion.cmake.in new file mode 100644 index 0000000000..2a9609ddb9 --- /dev/null +++ b/tensorflow/contrib/cmake/TensorflowConfigVersion.cmake.in @@ -0,0 +1,11 @@ +set(PACKAGE_VERSION "@TENSORFLOW_VERSION@") + +# Check whether the requested PACKAGE_FIND_VERSION is compatible +if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else() + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}") + set(PACKAGE_VERSION_EXACT TRUE) + endif() +endif() \ No newline at end of file diff --git a/tensorflow/contrib/cmake/external/abseil_cpp.cmake b/tensorflow/contrib/cmake/external/abseil_cpp.cmake index 4546dbdecc..46a193971c 100644 --- a/tensorflow/contrib/cmake/external/abseil_cpp.cmake +++ b/tensorflow/contrib/cmake/external/abseil_cpp.cmake @@ -31,27 +31,24 @@ if (systemlib_ABSEIL_CPP) message(STATUS " abseil_cpp includes: ${ABSEIL_CPP_INCLUDE_DIR}") message(STATUS " abseil_cpp libraries: ${ABSEIL_CPP_LIBRARIES}") - add_custom_target(abseil_cpp_build) - list(APPEND tensorflow_EXTERNAL_DEPENDENCIES abseil_cpp_build) + add_custom_target(abseil_cpp) + list(APPEND tensorflow_EXTERNAL_DEPENDENCIES abseil_cpp) else (systemlib_ABSEIL_CPP) include (ExternalProject) - set(abseil_cpp_INCLUDE_DIR ${CMAKE_BINARY_DIR}/abseil_cpp/src/abseil_cpp_build) + set(abseil_cpp_INCLUDE_DIR ${CMAKE_BINARY_DIR}/abseil_cpp/src/abseil_cpp) set(abseil_cpp_URL https://github.com/abseil/abseil-cpp/archive/e01d95528ea2137a4a27a88d1f57c6cb260aafed.tar.gz) set(abseil_cpp_HASH SHA256=84043ed402d2a2a6ba4cdddb7e85118b1158fd81fe4ac3a14adc343d054c1e2e) - set(abseil_cpp_BUILD ${CMAKE_BINARY_DIR}/abseil_cpp/src/abseil_cpp_build) + set(abseil_cpp_BUILD ${CMAKE_BINARY_DIR}/abseil_cpp/src/abseil_cpp-build) if(WIN32) if(${CMAKE_GENERATOR} MATCHES "Visual Studio.*") set(abseil_cpp_STATIC_LIBRARIES ${abseil_cpp_BUILD}/absl/base/Release/absl_base.lib - ${abseil_cpp_BUILD}/absl/base/Release/absl_spinlock_wait.lib ${abseil_cpp_BUILD}/absl/base/Release/absl_dynamic_annotations.lib - ${abseil_cpp_BUILD}/absl/base/Release/absl_malloc_internal.lib - ${abseil_cpp_BUILD}/absl/base/Release/absl_throw_delegate.lib - ${abseil_cpp_BUILD}/absl/numeric/Release/absl_int128.lib + ${abseil_cpp_BUILD}/absl/base/Release/absl_internal_malloc_internal.lib ${abseil_cpp_BUILD}/absl/strings/Release/absl_strings.lib ${abseil_cpp_BUILD}/absl/strings/Release/str_format_internal.lib ${abseil_cpp_BUILD}/absl/types/Release/absl_bad_optional_access.lib) @@ -80,15 +77,12 @@ else (systemlib_ABSEIL_CPP) ${abseil_cpp_BUILD}/absl/types/libabsl_bad_optional_access.a) endif() - ExternalProject_Add(abseil_cpp_build + ExternalProject_Add(abseil_cpp PREFIX abseil_cpp URL ${abseil_cpp_URL} URL_HASH ${abseil_cpp_HASH} DOWNLOAD_DIR "${DOWNLOAD_LOCATION}" - BUILD_IN_SOURCE 1 BUILD_BYPRODUCTS ${abseil_cpp_STATIC_LIBRARIES} - BUILD_COMMAND ${CMAKE_COMMAND} --build . --config Release - COMMAND ${CMAKE_COMMAND} --build . --config Release INSTALL_COMMAND "" CMAKE_CACHE_ARGS -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=${tensorflow_ENABLE_POSITION_INDEPENDENT_CODE} @@ -99,6 +93,6 @@ else (systemlib_ABSEIL_CPP) include_directories(${abseil_cpp_INCLUDE_DIR}) list(APPEND tensorflow_EXTERNAL_LIBRARIES ${abseil_cpp_STATIC_LIBRARIES}) - list(APPEND tensorflow_EXTERNAL_DEPENDENCIES abseil_cpp_build) + list(APPEND tensorflow_EXTERNAL_DEPENDENCIES abseil_cpp) -endif (systemlib_ABSEIL_CPP) +endif (systemlib_ABSEIL_CPP) \ No newline at end of file diff --git a/tensorflow/contrib/cmake/external/png.cmake b/tensorflow/contrib/cmake/external/png.cmake index 1a147e9c8e..32e6d78e50 100644 --- a/tensorflow/contrib/cmake/external/png.cmake +++ b/tensorflow/contrib/cmake/external/png.cmake @@ -59,6 +59,7 @@ ExternalProject_Add(png -DCMAKE_VERBOSE_MAKEFILE:BOOL=OFF -DCMAKE_INSTALL_PREFIX:STRING=${png_INSTALL} -DZLIB_ROOT:STRING=${ZLIB_INSTALL} + -DPNG_TESTS:BOOL=OFF ) ## put png includes in the directory where they are expected diff --git a/tensorflow/contrib/cmake/modules/FindAbseilCpp.cmake b/tensorflow/contrib/cmake/modules/FindAbseilCpp.cmake index d4f8bb1bec..944ae3997a 100644 --- a/tensorflow/contrib/cmake/modules/FindAbseilCpp.cmake +++ b/tensorflow/contrib/cmake/modules/FindAbseilCpp.cmake @@ -24,10 +24,10 @@ if(EXISTS "${ABSEIL_CPP_INCLUDE_DIR}" AND NOT "${ABSEIL_CPP_INCLUDE_DIR}" STREQU # search all libraries if no COMPONENTS was requested set(AbseilCpp_FIND_COMPONENTS "absl_algorithm;absl_any;absl_bad_any_cast" - "absl_bad_optional_access;absl_base absl_container;absl_debugging" + "absl_bad_optional_access;absl_base;absl_container;absl_debugging" "absl_dynamic_annotations;absl_examine_stack;absl_failure_signal_handler" - "absl_int128;absl_leak_check;absl_malloc_internal;absl_memory;absl_meta" - "absl_numeric;absl_optional;absl_span;absl_spinlock_wait;absl_stack_consumption" + "absl_int128;absl_leak_check;absl_internal_malloc_internal;absl_memory;absl_meta" + "absl_numeric;absl_optional;absl_span;absl_internal_spinlock_wait;absl_stack_consumption" "absl_stacktrace;absl_str_format;absl_strings;absl_symbolize;absl_synchronization" "absl_throw_delegate;absl_time;absl_utility;str_format_extension_internal" "str_format_internal;test_instance_tracker_lib") diff --git a/tensorflow/contrib/cmake/tf_c.cmake b/tensorflow/contrib/cmake/tf_c.cmake index 7a30eb94f5..a04142bd24 100644 --- a/tensorflow/contrib/cmake/tf_c.cmake +++ b/tensorflow/contrib/cmake/tf_c.cmake @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== + ######################################################## # tf_c_framework library ######################################################## diff --git a/tensorflow/contrib/cmake/tf_core_cpu.cmake b/tensorflow/contrib/cmake/tf_core_cpu.cmake index a54cbff33b..d8884d464f 100644 --- a/tensorflow/contrib/cmake/tf_core_cpu.cmake +++ b/tensorflow/contrib/cmake/tf_core_cpu.cmake @@ -39,6 +39,8 @@ file(GLOB_RECURSE tf_core_cpu_exclude_srcs "${tensorflow_source_dir}/tensorflow/core/*test*.h" "${tensorflow_source_dir}/tensorflow/core/*test*.cc" "${tensorflow_source_dir}/tensorflow/core/*main.cc" + "${tensorflow_source_dir}/tensorflow/core/common_runtime/eager/*.cc" + "${tensorflow_source_dir}/tensorflow/core/common_runtime/eager/*.h" "${tensorflow_source_dir}/tensorflow/core/common_runtime/gpu/*.cc" "${tensorflow_source_dir}/tensorflow/core/common_runtime/gpu_device_factory.cc" "${tensorflow_source_dir}/tensorflow/core/common_runtime/direct_session.cc" diff --git a/tensorflow/contrib/cmake/tf_core_eager_runtime.cmake b/tensorflow/contrib/cmake/tf_core_eager_runtime.cmake new file mode 100644 index 0000000000..78e4c0d303 --- /dev/null +++ b/tensorflow/contrib/cmake/tf_core_eager_runtime.cmake @@ -0,0 +1,57 @@ +# Copyright 2018 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. +# ============================================================================== +######################################################## +# tf_core_eager_runtime library +######################################################## +file(GLOB_RECURSE tf_core_eager_runtime_srcs + "${tensorflow_source_dir}/tensorflow/core/common_runtime/eager/*.cc" + "${tensorflow_source_dir}/tensorflow/core/common_runtime/eager/*.h" +) + +file(GLOB_RECURSE tf_core_eager_runtime_exclude_srcs + "${tensorflow_source_dir}/tensorflow/core/common_runtime/eager/*test*.h" + "${tensorflow_source_dir}/tensorflow/core/common_runtime/eager/*test*.cc" +) + +list(REMOVE_ITEM tf_core_eager_runtime_srcs ${tf_core_eager_runtime_exclude_srcs}) + +add_library(tf_core_eager_runtime OBJECT ${tf_core_eager_runtime_srcs}) +add_dependencies( + tf_core_eager_runtime + tf_c + tf_core_lib) + + +file(GLOB_RECURSE tf_c_eager_srcs + "${tensorflow_source_dir}/tensorflow/c/eager/*.cc" + "${tensorflow_source_dir}/tensorflow/c/eager/*.h" +) + +file(GLOB_RECURSE tf_c_eager_exlclude_srcs + "${tensorflow_source_dir}/tensorflow/c/eager/*test*.h" + "${tensorflow_source_dir}/tensorflow/c/eager/*test*.cc" +) + +list(REMOVE_ITEM tf_c_eager_srcs ${tf_c_eager_exlclude_srcs}) + +add_library(tf_c_eager OBJECT ${tf_c_eager_srcs}) +add_dependencies( + tf_c_eager + tf_core_eager_runtime + tf_c + tf_cc_framework + tf_cc_while_loop + tf_core_lib + tf_protos_cc) \ No newline at end of file diff --git a/tensorflow/contrib/cmake/tf_core_framework.cmake b/tensorflow/contrib/cmake/tf_core_framework.cmake index 7e806685b8..d7b2a1339e 100644 --- a/tensorflow/contrib/cmake/tf_core_framework.cmake +++ b/tensorflow/contrib/cmake/tf_core_framework.cmake @@ -140,16 +140,19 @@ set(tf_proto_text_srcs "tensorflow/core/example/example.proto" "tensorflow/core/example/feature.proto" "tensorflow/core/framework/allocation_description.proto" + "tensorflow/core/framework/api_def.proto" "tensorflow/core/framework/attr_value.proto" "tensorflow/core/framework/cost_graph.proto" "tensorflow/core/framework/device_attributes.proto" "tensorflow/core/framework/function.proto" "tensorflow/core/framework/graph.proto" "tensorflow/core/framework/graph_transfer_info.proto" + "tensorflow/core/framework/iterator.proto" "tensorflow/core/framework/kernel_def.proto" "tensorflow/core/framework/log_memory.proto" "tensorflow/core/framework/node_def.proto" "tensorflow/core/framework/op_def.proto" + "tensorflow/core/framework/reader_base.proto" "tensorflow/core/framework/remote_fused_graph_execute_info.proto" "tensorflow/core/framework/resource_handle.proto" "tensorflow/core/framework/step_stats.proto" @@ -159,6 +162,7 @@ set(tf_proto_text_srcs "tensorflow/core/framework/tensor_shape.proto" "tensorflow/core/framework/tensor_slice.proto" "tensorflow/core/framework/types.proto" + "tensorflow/core/framework/variable.proto" "tensorflow/core/framework/versions.proto" "tensorflow/core/lib/core/error_codes.proto" "tensorflow/core/protobuf/cluster.proto" @@ -204,10 +208,10 @@ file(GLOB tf_core_platform_srcs "${tensorflow_source_dir}/tensorflow/core/framework/resource_handle.h" "${tensorflow_source_dir}/tensorflow/core/framework/resource_handle.cc") if (NOT tensorflow_ENABLE_GPU) - file(GLOB tf_core_platform_gpu_srcs + file(GLOB tf_core_platform_gpu_srcs_exclude "${tensorflow_source_dir}/tensorflow/core/platform/cuda_libdevice_path.*" "${tensorflow_source_dir}/tensorflow/core/platform/default/cuda_libdevice_path.*") - list(REMOVE_ITEM tf_core_platform_srcs ${tf_core_platform_gpu_srcs}) + list(REMOVE_ITEM tf_core_platform_srcs ${tf_core_platform_gpu_srcs_exclude}) else() file(GLOB tf_core_platform_srcs_exclude "${tensorflow_source_dir}/tensorflow/core/platform/default/device_tracer.cc") diff --git a/tensorflow/contrib/cmake/tf_core_ops.cmake b/tensorflow/contrib/cmake/tf_core_ops.cmake index 9cfa8b9074..6e75963313 100644 --- a/tensorflow/contrib/cmake/tf_core_ops.cmake +++ b/tensorflow/contrib/cmake/tf_core_ops.cmake @@ -13,13 +13,14 @@ # limitations under the License. # ============================================================================== set(tf_op_lib_names - "audio_ops" "array_ops" + "audio_ops" "batch_ops" "bitwise_ops" "boosted_trees_ops" "candidate_sampling_ops" "checkpoint_ops" + "collective_ops" "control_flow_ops" "ctc_ops" "cudnn_rnn_ops" @@ -32,8 +33,8 @@ set(tf_op_lib_names "io_ops" "linalg_ops" "list_ops" - "lookup_ops" "logging_ops" + "lookup_ops" "manip_ops" "math_ops" "nn_ops" @@ -43,10 +44,11 @@ set(tf_op_lib_names "remote_fused_graph_ops" "resource_variable_ops" "rpc_ops" + "scoped_allocator_ops" "script_ops" "sdca_ops" - "set_ops" "sendrecv_ops" + "set_ops" "sparse_ops" "spectral_ops" "state_ops" @@ -54,6 +56,7 @@ set(tf_op_lib_names "string_ops" "summary_ops" "training_ops" + "word2vec_ops" ) foreach(tf_op_lib_name ${tf_op_lib_names}) diff --git a/tensorflow/contrib/cmake/tf_python.cmake b/tensorflow/contrib/cmake/tf_python.cmake index df7b854afc..5028498598 100755 --- a/tensorflow/contrib/cmake/tf_python.cmake +++ b/tensorflow/contrib/cmake/tf_python.cmake @@ -313,15 +313,14 @@ function(GENERATE_PYTHON_OP_LIB tf_python_op_lib_name) ${GENERATE_PYTHON_OP_LIB_DESTINATION} PARENT_SCOPE) endfunction() -GENERATE_PYTHON_OP_LIB("audio_ops") GENERATE_PYTHON_OP_LIB("array_ops") +GENERATE_PYTHON_OP_LIB("audio_ops") GENERATE_PYTHON_OP_LIB("batch_ops") GENERATE_PYTHON_OP_LIB("bitwise_ops") GENERATE_PYTHON_OP_LIB("boosted_trees_ops") -GENERATE_PYTHON_OP_LIB("math_ops") -GENERATE_PYTHON_OP_LIB("functional_ops") GENERATE_PYTHON_OP_LIB("candidate_sampling_ops") GENERATE_PYTHON_OP_LIB("checkpoint_ops") +GENERATE_PYTHON_OP_LIB("collective_ops") GENERATE_PYTHON_OP_LIB("control_flow_ops" ADDITIONAL_LIBRARIES $) GENERATE_PYTHON_OP_LIB("ctc_ops") @@ -332,14 +331,18 @@ GENERATE_PYTHON_OP_LIB("decode_proto_ops" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/tf_python/tensorflow/contrib/proto/python/ops/gen_decode_proto_op.py) GENERATE_PYTHON_OP_LIB("encode_proto_ops" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/tf_python/tensorflow/contrib/proto/python/ops/gen_encode_proto_op.py) +GENERATE_PYTHON_OP_LIB("function_ops") +GENERATE_PYTHON_OP_LIB("functional_ops") GENERATE_PYTHON_OP_LIB("image_ops") GENERATE_PYTHON_OP_LIB("io_ops") GENERATE_PYTHON_OP_LIB("linalg_ops") GENERATE_PYTHON_OP_LIB("list_ops") GENERATE_PYTHON_OP_LIB("logging_ops") GENERATE_PYTHON_OP_LIB("lookup_ops") -GENERATE_PYTHON_OP_LIB("nn_ops") GENERATE_PYTHON_OP_LIB("manip_ops") +GENERATE_PYTHON_OP_LIB("math_ops") +GENERATE_PYTHON_OP_LIB("nn_ops") +GENERATE_PYTHON_OP_LIB("no_op") GENERATE_PYTHON_OP_LIB("parsing_ops") GENERATE_PYTHON_OP_LIB("random_ops") GENERATE_PYTHON_OP_LIB("remote_fused_graph_ops" @@ -347,17 +350,21 @@ GENERATE_PYTHON_OP_LIB("remote_fused_graph_ops" GENERATE_PYTHON_OP_LIB("resource_variable_ops") GENERATE_PYTHON_OP_LIB("rpc_ops" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/tf_python/tensorflow/contrib/rpc/python/ops/gen_rpc_op.py) +GENERATE_PYTHON_OP_LIB("scoped_allocator_ops") GENERATE_PYTHON_OP_LIB("script_ops") GENERATE_PYTHON_OP_LIB("sdca_ops") +GENERATE_PYTHON_OP_LIB("sendrecv_ops") GENERATE_PYTHON_OP_LIB("set_ops") -GENERATE_PYTHON_OP_LIB("state_ops") GENERATE_PYTHON_OP_LIB("sparse_ops") GENERATE_PYTHON_OP_LIB("spectral_ops") +GENERATE_PYTHON_OP_LIB("state_ops") +GENERATE_PYTHON_OP_LIB("stateless_random_ops") GENERATE_PYTHON_OP_LIB("string_ops") GENERATE_PYTHON_OP_LIB("summary_ops") GENERATE_PYTHON_OP_LIB("user_ops") GENERATE_PYTHON_OP_LIB("training_ops" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/tf_python/tensorflow/python/training/gen_training_ops.py) +GENERATE_PYTHON_OP_LIB("word2vec_ops") GENERATE_PYTHON_OP_LIB("contrib_boosted_trees_model_ops" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/tf_python/tensorflow/contrib/boosted_trees/python/ops/gen_model_ops.py) @@ -391,11 +398,8 @@ GENERATE_PYTHON_OP_LIB("contrib_layers_sparse_feature_cross_ops" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/tf_python/tensorflow/contrib/layers/ops/gen_sparse_feature_cross_op.py) GENERATE_PYTHON_OP_LIB("contrib_memory_stats_ops" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/tf_python/tensorflow/contrib/memory_stats/ops/gen_memory_stats_ops.py) -GENERATE_PYTHON_OP_LIB("contrib_nccl_ops" - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/tf_python/tensorflow/contrib/nccl/ops/gen_nccl_ops.py) GENERATE_PYTHON_OP_LIB("contrib_periodic_resample_ops" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/tf_python/tensorflow/contrib/periodic_resample/python/ops/gen_periodic_resample_op.py) - GENERATE_PYTHON_OP_LIB("contrib_nearest_neighbor_ops" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/tf_python/tensorflow/contrib/nearest_neighbor/ops/gen_nearest_neighbor_ops.py) GENERATE_PYTHON_OP_LIB("contrib_resampler_ops" @@ -524,11 +528,13 @@ if(WIN32) add_library(pywrap_tensorflow_internal_static STATIC ${pywrap_tensorflow_internal_src} $ + $ $ $ $ $ $ + $ $ $ $ @@ -581,11 +587,13 @@ endif(WIN32) add_library(pywrap_tensorflow_internal SHARED ${pywrap_tensorflow_internal_src} $ + $ $ $ $ $ $ + $ $ $ $ @@ -615,13 +623,28 @@ target_include_directories(pywrap_tensorflow_internal PUBLIC ${NUMPY_INCLUDE_DIR} ) -target_link_libraries(pywrap_tensorflow_internal PRIVATE +if(CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 5.0) + # There is a bug in GCC 5 resulting in undefined reference to a __cpu_model function when + # linking to the tensorflow library. Adding the following libraries fixes it. + # See issue on github: https://github.com/tensorflow/tensorflow/issues/9593 + target_link_libraries(pywrap_tensorflow_internal PRIVATE + ${tf_core_gpu_kernels_lib} + ${tensorflow_EXTERNAL_LIBRARIES} + tf_protos_cc + tf_python_protos_cc + ${PYTHON_LIBRARIES} + gcc_s + gcc +) +else() + target_link_libraries(pywrap_tensorflow_internal PRIVATE ${tf_core_gpu_kernels_lib} ${tensorflow_EXTERNAL_LIBRARIES} tf_protos_cc tf_python_protos_cc ${PYTHON_LIBRARIES} ) +endif() if(WIN32) diff --git a/tensorflow/contrib/cmake/tf_shared_lib.cmake b/tensorflow/contrib/cmake/tf_shared_lib.cmake index fdf522f1fd..62005dd113 100644 --- a/tensorflow/contrib/cmake/tf_shared_lib.cmake +++ b/tensorflow/contrib/cmake/tf_shared_lib.cmake @@ -23,6 +23,8 @@ if(WIN32) # we need. # add_library(tensorflow_static STATIC + $ + $ $ $ $ @@ -65,6 +67,8 @@ endif(WIN32) # tensorflow is a shared library containing all of the # TensorFlow runtime and the standard ops and kernels. add_library(tensorflow SHARED + $ + $ $ $ $ @@ -96,6 +100,27 @@ if(CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 5.0) target_link_libraries(tensorflow PRIVATE gcc_s gcc) endif() +# Offer the user the choice of overriding the installation directories +set(INSTALL_LIB_DIR lib CACHE PATH "Installation directory for libraries") +set(INSTALL_BIN_DIR bin CACHE PATH "Installation directory for executables") +set(INSTALL_INCLUDE_DIR include CACHE PATH + "Installation directory for header files") +if(WIN32 AND NOT CYGWIN) + set(DEF_INSTALL_CMAKE_DIR cmake) +else() + set(DEF_INSTALL_CMAKE_DIR lib/cmake) +endif() +set(INSTALL_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR} CACHE PATH + "Installation directory for CMake files") + +# Make relative paths absolute (needed later on) +foreach(p LIB BIN INCLUDE CMAKE) + set(var INSTALL_${p}_DIR) + if(NOT IS_ABSOLUTE "${${var}}") + set(${var} "${CMAKE_INSTALL_PREFIX}/${${var}}") + endif() +endforeach() + if(WIN32) add_dependencies(tensorflow tensorflow_static) endif(WIN32) @@ -103,14 +128,57 @@ endif(WIN32) target_include_directories(tensorflow PUBLIC $) -install(TARGETS tensorflow EXPORT tensorflow_export - RUNTIME DESTINATION bin - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib) +# Add all targets to build-tree export set +export(TARGETS tensorflow + FILE ${PROJECT_BINARY_DIR}/TensorflowTargets.cmake) + +# Export the package for use from the build-tree +export(PACKAGE Tensorflow) + +# Create the TensorflowConfig.cmake and TensorflowConfigVersion files +file(RELATIVE_PATH REL_INCLUDE_DIR "${INSTALL_CMAKE_DIR}" + "${INSTALL_INCLUDE_DIR}") +# for the build tree +set(CONF_INCLUDE_DIRS "${tensorflow_source_dir}" + "${PROJECT_BINARY_DIR}" + "${CMAKE_CURRENT_BINARY_DIR}/protobuf/src/protobuf/src" + "${CMAKE_CURRENT_BINARY_DIR}/nsync/install/include" # Please if there is a better directory + "${CMAKE_CURRENT_BINARY_DIR}/eigen/src/eigen/Eigen/" + "${CMAKE_CURRENT_BINARY_DIR}/external/eigen_archive/" + "${tensorflow_source_dir}/third_party/eigen3/" + "${CMAKE_CURRENT_BINARY_DIR}/eigen/src/eigen/unsupported/Eigen/") +configure_file(TensorflowConfig.cmake.in + "${PROJECT_BINARY_DIR}/TensorflowConfig.cmake" @ONLY) +# for the install tree, yet to be complete +set(CONF_INCLUDE_DIRS "\${TENSORFLOW_CMAKE_DIR}/${REL_INCLUDE_DIR}") +configure_file(TensorflowConfig.cmake.in + "${PROJECT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/TensorflowConfig.cmake" @ONLY) +# for both +configure_file(TensorflowConfigVersion.cmake.in + "${PROJECT_BINARY_DIR}/TensorflowConfigVersion.cmake" @ONLY) + +# install(TARGETS tensorflow EXPORT tensorflow_export +# RUNTIME DESTINATION ${INSTALL_BIN_DIR} +# LIBRARY DESTINATION ${INSTALL_LIB_DIR} +# ARCHIVE DESTINATION ${INSTALL_LIB_DIR}) + +# install(EXPORT tensorflow_export +# FILE TensorflowConfig.cmake +# DESTINATION ${INSTALL_CMAKE_DIR}) -install(EXPORT tensorflow_export - FILE TensorflowConfig.cmake - DESTINATION lib/cmake) +install(FILES + "${PROJECT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/TensorflowConfig.cmake" + "${PROJECT_BINARY_DIR}/TensorflowConfigVersion.cmake" + DESTINATION "${INSTALL_CMAKE_DIR}" COMPONENT dev) + +# install the export set for use with the install-tree +install(EXPORT TensorflowTargets + DESTINATION ${INSTALL_CMAKE_DIR}) + +install(TARGETS tensorflow EXPORT TensorflowTargets + RUNTIME DESTINATION ${INSTALL_BIN_DIR} + LIBRARY DESTINATION ${INSTALL_LIB_DIR} + ARCHIVE DESTINATION ${INSTALL_LIB_DIR}) # install necessary headers # tensorflow headers @@ -145,6 +213,10 @@ install(DIRECTORY ${tensorflow_source_dir}/third_party/eigen3/ # unsupported Eigen directory install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/eigen/src/eigen/unsupported/Eigen/ DESTINATION include/unsupported/Eigen) +# absl directory +install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/abseil_cpp/src/abseil_cpp/absl/ + DESTINATION include/absl + FILES_MATCHING PATTERN "*.h") # mkl if (tensorflow_ENABLE_MKL_SUPPORT) install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/mkl/src/mkl/include/ diff --git a/tensorflow/contrib/compiler/BUILD b/tensorflow/contrib/compiler/BUILD index 1630f010ab..e4566437c6 100644 --- a/tensorflow/contrib/compiler/BUILD +++ b/tensorflow/contrib/compiler/BUILD @@ -58,6 +58,7 @@ py_library( srcs_version = "PY2AND3", deps = [ "//tensorflow/compiler/jit:xla_ops_py", + "//tensorflow/compiler/jit/ops:xla_ops_grad", "//tensorflow/python:array_ops", "//tensorflow/python:control_flow_ops", "//tensorflow/python:framework_ops", diff --git a/tensorflow/contrib/compiler/xla.py b/tensorflow/contrib/compiler/xla.py index 335ac79464..f867cd15b6 100644 --- a/tensorflow/contrib/compiler/xla.py +++ b/tensorflow/contrib/compiler/xla.py @@ -23,6 +23,7 @@ import contextlib from six.moves import xrange # pylint: disable=redefined-builtin from tensorflow.compiler.jit.ops import xla_ops +from tensorflow.compiler.jit.ops import xla_ops_grad # pylint: disable=unused-import from tensorflow.core.framework import attr_value_pb2 from tensorflow.python.estimator import model_fn as model_fn_lib from tensorflow.python.framework import ops diff --git a/tensorflow/contrib/constrained_optimization/python/constrained_minimization_problem.py b/tensorflow/contrib/constrained_optimization/python/constrained_minimization_problem.py index 41258edd90..6926c0d03f 100644 --- a/tensorflow/contrib/constrained_optimization/python/constrained_minimization_problem.py +++ b/tensorflow/contrib/constrained_optimization/python/constrained_minimization_problem.py @@ -74,8 +74,8 @@ class ConstrainedMinimizationProblem(object): if (constraints_shape.ndims is None or proxy_constraints_shape.ndims is None or - any([ii is None for ii in constraints_shape.as_list()]) or - any([ii is None for ii in proxy_constraints_shape.as_list()])): + any(ii is None for ii in constraints_shape.as_list()) or + any(ii is None for ii in proxy_constraints_shape.as_list())): raise ValueError( "constraints and proxy_constraints must have fully-known shapes") if constraints_shape != proxy_constraints_shape: diff --git a/tensorflow/contrib/crf/python/ops/crf.py b/tensorflow/contrib/crf/python/ops/crf.py index 656633f0bf..40e159b8fc 100644 --- a/tensorflow/contrib/crf/python/ops/crf.py +++ b/tensorflow/contrib/crf/python/ops/crf.py @@ -38,12 +38,12 @@ tf_unary_scores, tf_sequence_lengths, tf_transition_params, _ = session.run( [unary_scores, sequence_lengths, transition_params, train_op]) for tf_unary_scores_, tf_sequence_length_ in zip(tf_unary_scores, tf_sequence_lengths): -# Remove padding. -tf_unary_scores_ = tf_unary_scores_[:tf_sequence_length_] + # Remove padding. + tf_unary_scores_ = tf_unary_scores_[:tf_sequence_length_] -# Compute the highest score and its tag sequence. -tf_viterbi_sequence, tf_viterbi_score = tf.contrib.crf.viterbi_decode( - tf_unary_scores_, tf_transition_params) + # Compute the highest score and its tag sequence. + tf_viterbi_sequence, tf_viterbi_score = tf.contrib.crf.viterbi_decode( + tf_unary_scores_, tf_transition_params) """ from __future__ import absolute_import diff --git a/tensorflow/contrib/cudnn_rnn/BUILD b/tensorflow/contrib/cudnn_rnn/BUILD index 670b549432..8d35622e39 100644 --- a/tensorflow/contrib/cudnn_rnn/BUILD +++ b/tensorflow/contrib/cudnn_rnn/BUILD @@ -42,10 +42,11 @@ tf_custom_op_py_library( cuda_py_test( name = "cudnn_rnn_ops_test", - size = "large", + size = "medium", srcs = ["python/kernel_tests/cudnn_rnn_ops_test.py"], additional_deps = [ ":cudnn_rnn_py", + "@absl_py//absl/testing:parameterized", "//tensorflow/core:protos_all_py", "//tensorflow/contrib/rnn:rnn_py", "//tensorflow/python/ops/losses:losses", @@ -61,7 +62,7 @@ cuda_py_test( "//tensorflow/python:training", "//tensorflow/python:variables", ], - shard_count = 6, + shard_count = 2, tags = [ "noasan", # http://b/62067814 "requires-gpu-sm35", diff --git a/tensorflow/contrib/cudnn_rnn/python/kernel_tests/cudnn_rnn_ops_test.py b/tensorflow/contrib/cudnn_rnn/python/kernel_tests/cudnn_rnn_ops_test.py index ae839108eb..a268415f0e 100644 --- a/tensorflow/contrib/cudnn_rnn/python/kernel_tests/cudnn_rnn_ops_test.py +++ b/tensorflow/contrib/cudnn_rnn/python/kernel_tests/cudnn_rnn_ops_test.py @@ -18,24 +18,30 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import collections import itertools import os import unittest +from absl.testing import parameterized import numpy as np from tensorflow.contrib.cudnn_rnn.python.ops import cudnn_rnn_ops from tensorflow.core.protobuf import saver_pb2 +from tensorflow.python.eager import context from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import random_seed from tensorflow.python.framework.test_util import TensorFlowTestCase from tensorflow.python.ops import array_ops -from tensorflow.python.ops import gradient_checker -from tensorflow.python.ops import math_ops +from tensorflow.python.ops import gradients_impl +from tensorflow.python.ops import init_ops from tensorflow.python.ops import random_ops +from tensorflow.python.ops import rnn +from tensorflow.python.ops import rnn_cell_impl from tensorflow.python.ops import state_ops +from tensorflow.python.ops import variable_scope from tensorflow.python.ops import variables from tensorflow.python.platform import googletest from tensorflow.python.platform import test @@ -56,714 +62,989 @@ CUDNN_RNN_TANH_PARAMS_PER_LAYER = cudnn_rnn_ops.CUDNN_RNN_TANH_PARAMS_PER_LAYER CUDNN_RNN_RELU_PARAMS_PER_LAYER = cudnn_rnn_ops.CUDNN_RNN_RELU_PARAMS_PER_LAYER -def _CreateModel(rnn_mode, - num_layers, - num_units, - input_size, - input_mode="linear_input", - direction=cudnn_rnn_ops.CUDNN_RNN_UNIDIRECTION, - dtype=dtypes.float32, - dropout=0.): - del input_mode - if rnn_mode == cudnn_rnn_ops.CUDNN_LSTM: - model_fn = cudnn_rnn_ops.CudnnLSTM - elif rnn_mode == cudnn_rnn_ops.CUDNN_GRU: - model_fn = cudnn_rnn_ops.CudnnGRU - elif rnn_mode == cudnn_rnn_ops.CUDNN_RNN_TANH: - model_fn = cudnn_rnn_ops.CudnnRNNTanh - elif rnn_mode == cudnn_rnn_ops.CUDNN_RNN_RELU: - model_fn = cudnn_rnn_ops.CudnnRNNRelu +def RunLSTM(sess, + num_units, + input_size, + batch_size, + time, + num_layers=1, + is_training=True, + dropout=0., + num_dirs=True, + dtype=dtypes.float32): + # TODO(jamesqin): add multi-layer tests. + # TODO(jamesqin): add multi-dir tests + assert num_layers == 1 + assert num_dirs == 1 + if is_training and not np.isclose(dropout, 0): + raise ValueError("dropout can not be 0. when test training.") + + # set graph level random seed and numpy random seed. + random_seed.set_random_seed(0) + np.random.seed(0) + + inputs = variable_scope.get_variable( + "inputs", + initializer=np.random.rand(time, batch_size, + input_size).astype(dtype.as_numpy_dtype), + dtype=dtype) + initial_h_op = variable_scope.get_variable( + "initial_h_op", + initializer=np.random.rand(batch_size, + num_units).astype(dtype.as_numpy_dtype), + dtype=dtype) + initial_c_op = variable_scope.get_variable( + "initial_c_op", + initializer=np.random.rand(batch_size, + num_units).astype(dtype.as_numpy_dtype), + dtype=dtype) + + initializer = init_ops.random_uniform_initializer( + -0.01, 0.01, dtype=dtype, seed=19980904) + + with variable_scope.variable_scope("test", initializer=initializer): + w = variable_scope.get_variable( + "rnn/lstm_cell/kernel", + shape=[input_size + num_units, num_units * 4], + dtype=dtype) + b = variable_scope.get_variable( + "rnn/lstm_cell/bias", shape=[num_units * 4], dtype=dtype) + + # canonical lstm. must set forget_bias to 0. to align with cudnn lstm. + cell = rnn_cell_impl.LSTMCell(num_units, forget_bias=0., reuse=True) + outputs_op, state_tuple_op = rnn.dynamic_rnn( + cell, + inputs, + initial_state=rnn_cell_impl.LSTMStateTuple( + h=initial_h_op, c=initial_c_op), + dtype=dtype, + time_major=True, + scope=None) + + # Convert to cudnn opaque param. + format_converter = cudnn_rnn_ops.CudnnParamsFormatConverterLSTM( + num_layers, num_units, input_size) + opaque_params = format_converter.tf_canonical_to_opaque([w, b]) + + cu_initial_h_op = array_ops.expand_dims(initial_h_op, axis=0) + cu_initial_c_op = array_ops.expand_dims(initial_c_op, axis=0) + cu_outputs_op, cu_h_op, cu_c_op = cudnn_rnn_ops._cudnn_rnn( + inputs, + cu_initial_h_op, + cu_initial_c_op, + opaque_params, + dropout=dropout, + is_training=is_training, + rnn_mode=cudnn_rnn_ops.CUDNN_LSTM) + # Remove the trivial 1st dimension. + cu_state_tuple_op = rnn_cell_impl.LSTMStateTuple( + c=array_ops.squeeze(cu_c_op, axis=0), + h=array_ops.squeeze(cu_h_op, axis=0)) + + if is_training: + (inp_grad_op, hgrad_op, + cgrad_op, wgrad_op, bgrad_op) = gradients_impl.gradients( + outputs_op, [inputs, initial_h_op, initial_c_op, w, b]) + + (cu_inp_grad_op, cu_hgrad_op, + cu_cgrad_op, opaque_grad_op) = gradients_impl.gradients( + cu_outputs_op, + [inputs, cu_initial_h_op, cu_initial_c_op, opaque_params]) + # Remove the trivial 1st dimension + cu_hgrad_op = array_ops.squeeze(cu_hgrad_op, axis=0) + # Remove the trivial 1st dimension + cu_cgrad_op = array_ops.squeeze(cu_cgrad_op, axis=0) + + cu_wgrad_op, cu_bgrad_op = format_converter.opaque_to_tf_canonical( + opaque_grad_op) + cu_wgrad_op = cu_wgrad_op[0] + cu_bgrad_op = cu_bgrad_op[0] + # cudnn lstm has 2 biases each gate. When converting to tf canonical format, + # the two biases are summed into one. Thus here bias gradient should be + # halved when comparing with tf lstm. + cu_bgrad_op *= 0.5 + + init_op = variables.global_variables_initializer() + sess.run(init_op) + + if is_training: + outputs, state_tuple, inp_grad, state_grad, wgrad, bgrad = sess.run([ + outputs_op, state_tuple_op, inp_grad_op, + (hgrad_op, cgrad_op), wgrad_op, bgrad_op + ]) + (cu_outputs, cu_state_tuple, cu_inp_grad, cu_state_grad, cu_wgrad, + cu_bgrad) = sess.run([ + cu_outputs_op, cu_state_tuple_op, cu_inp_grad_op, + (cu_hgrad_op, cu_cgrad_op), cu_wgrad_op, cu_bgrad_op + ]) + + logging.vlog(1, "outputs: %s" % outputs) + logging.vlog(1, "cu_outputs: %s" % cu_outputs) + logging.vlog(1, "state_tuple: %s" % str(state_tuple)) + logging.vlog(1, "cu_state_tuple: %s" % str(cu_state_tuple)) + logging.vlog(1, "inp_grad: %s" % inp_grad) + logging.vlog(1, "cu_inp_grad: %s" % cu_inp_grad) + logging.vlog(1, "state_grad: %s" % str(state_grad)) + logging.vlog(1, "cu_state_grad: %s" % str(cu_state_grad)) + logging.vlog(1, "wgrad: %s" % str(wgrad)) + logging.vlog(1, "bgrad: %s" % str(bgrad)) + logging.vlog(1, "cu_wgrad: %s" % str(cu_wgrad)) + logging.vlog(1, "cu_bgrad: %s" % str(cu_bgrad)) + return (outputs, cu_outputs, state_tuple, cu_state_tuple, inp_grad, + cu_inp_grad, state_grad, cu_state_grad, wgrad, bgrad, cu_wgrad, + cu_bgrad) else: - raise ValueError("Invalid rnn_mode: %s" % rnn_mode) - return model_fn( - num_layers, - num_units, - input_size, - direction=direction, - dtype=dtype, - dropout=dropout) - - -def _CreateParamsSavable(params, - model, - base_variable_scope=None, - name="params_canonical"): - """Create a RNNParamsSaveable for the weight and bias parameters. + outputs, state_tuple = sess.run([outputs_op, state_tuple_op]) + cu_outputs, cu_state_tuple = sess.run([cu_outputs_op, cu_state_tuple_op]) + + logging.vlog(1, "outputs: %s" % outputs) + logging.vlog(1, "cu_outputs: %s" % cu_outputs) + logging.vlog(1, "state_tuple: %s" % str(state_tuple)) + logging.vlog(1, "cu_state_tuple: %s" % str(cu_state_tuple)) + return outputs, cu_outputs, state_tuple, cu_state_tuple + + +# Basic set of RNN configs to test. They can be further extended in relevant +# test (e.g. adding num_dirs). +NAMED_RNN_TESTCASES = ({ + "testcase_name": "xsmall", + "num_units": 1, + "input_size": 1, + "batch_size": 1, + "time": 1, + "num_layers": 1, +}, { + "testcase_name": "small", + "num_units": 4, + "input_size": 4, + "batch_size": 4, + "time": 4, + "num_layers": 1, +}, { + "testcase_name": "medium", + "num_units": 128, + "input_size": 64, + "batch_size": 8, + "time": 16, + "num_layers": 1, +}, { + "testcase_name": "large", + "num_units": 128, + "input_size": 128, + "batch_size": 16, + "time": 32, + "num_layers": 1, +}) + + +def ExpandNamedTestCases(inputs, *remove_keys, **extra_configs): + """Expands testcase with new config dimensions. + + Example: + inputs = ( + {'testcase_name': 'test1', 'gender': 'male'} + {'testcase_name': 'test2', 'gender': 'female'} + ) + remove_keys: empty + extra_configs = { + 'age': [40, 80] + 'height': [5, 6] + } + + Returns: + ( + {'testcase_name': 'test1_age_40_height_5','gender': 'male', 'age': + 40,'height': 5} + {'testcase_name': 'test1_age_40_height_6', 'gender': 'male', 'age': 40, + 'height': 6} + {'testcase_name': 'test1_age_80_height_5', 'gender': 'male', 'age': 80, + 'height': 5} + {'testcase_name': 'test1_age_80_height_6', 'gender': 'male', 'age': 80, + 'height': 6} + + {'testcase_name': 'test2_age_40_height_5', 'gender': 'female', 'age': + 40, + 'height': 5} + {'testcase_name': 'test2_age_40_height_6', 'gender': 'female', 'age': + 40, + 'height': 6} + {'testcase_name': 'test2_age_80_height_5', 'gender': 'female', 'age': + 80, + 'height': 5} + {'testcase_name': 'test2_age_80_height_6', 'gender': 'female', 'age': + 80, + 'height': 6} + ) Args: - params: a Variable for weight and bias parameters. - model: a CudnnRNN model. - base_variable_scope: a string, prefix of names of saved variables. - name: a string, name of the RNNParamsSaveable object. + inputs: A list of dictionary, each being a testcase. + *remove_keys: A list of keys into testcase which are not needed in new + testcases. + **extra_configs: A dict of new test dimension and applicable values in that + dimension. + Returns: - a RNNParamsSaveable object. + A list of dictionary with expanded test cases. """ - if model._rnn_mode == CUDNN_LSTM: - fn = cudnn_rnn_ops.CudnnLSTMSaveable - elif model._rnn_mode == CUDNN_GRU: - fn = cudnn_rnn_ops.CudnnGRUSaveable - elif model._rnn_mode == CUDNN_RNN_TANH: - fn = cudnn_rnn_ops.CudnnRNNTanhSaveable - elif model._rnn_mode == CUDNN_RNN_RELU: - fn = cudnn_rnn_ops.CudnnRNNReluSaveable - params_saveable = fn( - params, - model.num_layers, - model.num_units, - model.input_size, - model.input_mode, - model.direction, - scope=base_variable_scope, - name=name) - ops.add_to_collection(ops.GraphKeys.SAVEABLE_OBJECTS, params_saveable) - return params_saveable - - -def _MinLSTMParamSize(num_layers, - num_units, - input_size, - direction=cudnn_rnn_ops.CUDNN_RNN_UNIDIRECTION): - if direction == cudnn_rnn_ops.CUDNN_RNN_UNIDIRECTION: - first_layer_weights = 4 * num_units * (num_units + input_size) - higher_layer_weights = 8 * (num_layers - 1) * num_units * num_units - all_biases = 8 * num_layers * num_units - return first_layer_weights + higher_layer_weights + all_biases - elif direction == cudnn_rnn_ops.CUDNN_RNN_BIDIRECTION: - first_layer_weights = 4 * num_units * (num_units + input_size) - higher_layer_weights = (num_layers - 1) * ( - 4 * 2 * num_units * num_units + 4 * num_units**2) - all_biases = 8 * num_layers * num_units - return 2 * (first_layer_weights + higher_layer_weights + all_biases) - else: - raise ValueError("%s direction is not supported.") + res = [] + ordered_extra_configs = collections.OrderedDict(extra_configs) + keys = ordered_extra_configs.keys() + # A list of list of configs. + # The outer loop is iterating keys, the innner is values of one key. + combined_kv = [[(k, v) for v in ordered_extra_configs[k]] for k in keys] + logging.info("combined_kv: %s", combined_kv) + for inp in inputs: + # Each inp is a dict + for config in itertools.product(*combined_kv): + new_inp = dict(inp) + # config is a list in the form of [(k_i, v_j), (k_p, v_q), ...] + suffix = ["%s_%s" % (p[0], str(p[1])) for p in config] + suffix = "_".join(suffix) + new_inp["testcase_name"] += "_" + suffix + for k, v in config: + new_inp[k] = v + # Remove not used keys from the new test case. + if remove_keys: + if not isinstance(remove_keys, (list, tuple)): + remove_keys = [remove_keys] + for k in remove_keys: + new_inp.pop(k, None) + logging.info("new_inp: %s", new_inp) + res.append(new_inp) + # Dedup, necessary if `remove_keys` is set. + return [dict(t) for t in {tuple(d.items()) for d in res}] -class CudnnRNNTestSaveRestore(TensorFlowTestCase): - def _CompareWeights(self, lhs, rhs): - self.assertEqual(len(lhs), len(rhs)) - for lw, rw in zip(lhs, rhs): - self.assertAllEqual(lw, rw) +class CudnnLSTMTest(TensorFlowTestCase, parameterized.TestCase): - def _CompareBiases(self, lhs, rhs, rnn_mode, num_layers, direction): - self.assertEqual(len(lhs), len(rhs)) - if rnn_mode == CUDNN_LSTM: - num_params_per_layer = CUDNN_LSTM_PARAMS_PER_LAYER - elif rnn_mode == CUDNN_GRU: - num_params_per_layer = CUDNN_GRU_PARAMS_PER_LAYER - elif rnn_mode == CUDNN_RNN_TANH: - num_params_per_layer = CUDNN_RNN_TANH_PARAMS_PER_LAYER - else: - num_params_per_layer = CUDNN_RNN_RELU_PARAMS_PER_LAYER - num_dirs = 1 if direction == CUDNN_RNN_UNIDIRECTION else 2 - num_params_per_layer *= num_dirs - self.assertEqual(num_params_per_layer * num_layers, len(lhs)) - - for i in range(num_layers): - layer_lhs = lhs[i * num_params_per_layer: (i+1) * num_params_per_layer] - layer_rhs = rhs[i * num_params_per_layer: (i+1) * num_params_per_layer] - if direction == CUDNN_RNN_UNIDIRECTION: - self._CompareSingleLayerBiases(layer_lhs, layer_rhs) - else: - size = len(layer_lhs) - fw_lhs, bw_lhs = layer_lhs[:size//2], layer_lhs[size//2:] - fw_rhs, bw_rhs = layer_rhs[:size//2], layer_rhs[size//2:] - self._CompareSingleLayerBiases(fw_lhs, fw_rhs) - self._CompareSingleLayerBiases(bw_lhs, bw_rhs) - - def _CompareSingleLayerBiases(self, lhs, rhs): - self.assertEqual(len(lhs), len(rhs)) - - lf_lhs, rt_lhs = lhs[:len(lhs)//2], lhs[len(lhs)//2:] - lf_rhs, rt_rhs = rhs[:len(rhs)//2], rhs[len(rhs)//2:] - self.assertEqual(len(lf_lhs), len(rt_lhs)) - self.assertEqual(len(lf_rhs), len(rt_rhs)) - - sum_lhs, sum_rhs = [], [] - for lf, rt in zip(lf_lhs, rt_lhs): - sum_lhs.append(lf + rt) - for lf, rt in zip(lf_rhs, rt_rhs): - sum_rhs.append(lf + rt) - self.assertEqual(len(sum_lhs), len(sum_rhs)) - for lf, rt in zip(sum_lhs, sum_rhs): - self.assertAllEqual(lf, rt) + def _test_training_helper(self, + num_units, + input_size, + batch_size, + time, + num_layers, + dtype, + rtol=2e-6, + atol=2e-6): + with self.session(use_gpu=True) as sess: + (outputs, cu_outputs, state_tuple, cu_state_tuple, inp_grad, cu_inp_grad, + state_grad, cu_state_grad, wgrad, bgrad, cu_wgrad, cu_bgrad) = RunLSTM( + sess, num_units, input_size, batch_size, time, num_layers) - def _testSaveRestoreVariable(self, rnn_mode, direction, dtype): - num_layers = 2 - num_units = 7 - input_size = 3 - with ops.Graph().as_default(): - model = _CreateModel( - rnn_mode, - num_layers=num_layers, - num_units=num_units, - input_size=input_size, - direction=direction, - dtype=dtype) - random_seed.set_random_seed(1234) - params_size_t = model.params_size() - params = variables.VariableV1( - random_ops.random_uniform([params_size_t], dtype=dtype), - dtype=dtype, - validate_shape=False) - saveable = _CreateParamsSavable(params, model) - weights, biases = saveable.format_converter._opaque_to_cu_canonical( - saveable._variables) - reset_params = state_ops.assign( - params, - array_ops.zeros([params_size_t], dtype=dtype), - validate_shape=False) - save_path = os.path.join(self.get_temp_dir(), - "save-restore-variable-test") - saver = saver_lib.Saver(write_version=saver_pb2.SaverDef.V2) - # Passing graph explicitly, otherwise an old sess would be reused. - with self.test_session( - use_gpu=True, graph=ops.get_default_graph()) as sess: - sess.run(variables.global_variables_initializer()) - val = saver.save(sess, save_path) - self.assertEqual(save_path, val) + self.assertAllClose(outputs, cu_outputs, rtol=rtol, atol=atol) + for s, cu_s in zip(state_tuple, cu_state_tuple): + self.assertAllClose(s, cu_s, rtol=rtol, atol=atol) + for sg, cu_sg in zip(state_grad, cu_state_grad): + self.assertAllClose(sg, cu_sg, rtol=rtol, atol=atol) + self.assertAllClose(inp_grad, cu_inp_grad, rtol=rtol, atol=atol) + self.assertAllClose(bgrad, cu_bgrad, rtol=rtol, atol=atol) + self.assertAllClose(wgrad, cu_wgrad, rtol=rtol, atol=atol) - weights_v, biases_v = sess.run([weights, biases]) + @parameterized.named_parameters(*NAMED_RNN_TESTCASES) + @unittest.skipUnless(test.is_built_with_cuda(), + "Test only applicable when running on GPUs") + def test_training(self, num_units, input_size, batch_size, time, num_layers): + if not context.context().num_gpus(): + self.skipTest("No GPUs found") + self._test_training_helper(num_units, input_size, batch_size, time, + num_layers, dtypes.float32) - sess.run(reset_params) - saver.restore(sess, save_path) - weights_v_restored, biases_v_restored = sess.run([weights, biases]) - - self._CompareWeights(weights_v, weights_v_restored) - self._CompareBiases(biases_v, biases_v_restored, rnn_mode, num_layers, - direction) - - def _testSaveRestoreTwoVariables(self, rnn_mode, direction, dtype): - num_layers = 2 - num_units = 7 - input_size = 3 - with ops.Graph().as_default(): - model = _CreateModel( - rnn_mode, - num_layers=num_layers, - num_units=num_units, - input_size=input_size, - direction=direction, - dtype=dtype) - random_seed.set_random_seed(1234) - params_size_t = model.params_size() - names = ["rnn_1", "rnn_2"] - param_vars = [ - variables.VariableV1( - random_ops.random_uniform([params_size_t], dtype=dtype), - dtype=dtype, - validate_shape=False) for name in names - ] - saveables = [] - for name, params in zip(names, param_vars): - saveables.append(_CreateParamsSavable(params, model, name, name)) - weights1, biases1 = saveables[0].format_converter._opaque_to_cu_canonical( - saveables[0]._variables) - weights2, biases2 = saveables[1].format_converter._opaque_to_cu_canonical( - saveables[1]._variables) - reset_params = [ - state_ops.assign( - params, - array_ops.zeros([params_size_t], dtype=dtype), - validate_shape=False) for params in param_vars - ] - save_path = os.path.join(self.get_temp_dir(), - "save-restore-variable-test") - saver = saver_lib.Saver(write_version=saver_pb2.SaverDef.V2) - # Passing graph explicitly, otherwise an old sess would be reused. - with self.test_session(use_gpu=True, - graph=ops.get_default_graph()) as sess: - sess.run(variables.global_variables_initializer()) - val = saver.save(sess, save_path) - self.assertEqual(save_path, val) - weights1_v, biases1_v = sess.run([weights1, biases1]) - weights2_v, biases2_v = sess.run([weights2, biases2]) - - sess.run(reset_params) - saver.restore(sess, save_path) - weights1_v_restored, biases1_v_restored = sess.run([weights1, biases1]) - weights2_v_restored, biases2_v_restored = sess.run([weights2, biases2]) - - self._CompareWeights(weights1_v, weights1_v_restored) - self._CompareWeights(weights2_v, weights2_v_restored) - self._CompareBiases(biases1_v, biases1_v_restored, rnn_mode, num_layers, - direction) - self._CompareBiases(biases2_v, biases2_v_restored, rnn_mode, num_layers, - direction) - - def _testSaveRestoreOutput(self, rnn_mode, direction, dtype): - with ops.Graph().as_default(): - num_layers = 2 - num_units = 7 - input_size = 7 - seq_length = 10 - batch_size = 5 - dir_count = 1 if direction == cudnn_rnn_ops.CUDNN_RNN_UNIDIRECTION else 2 - model = _CreateModel( - rnn_mode, + @parameterized.named_parameters(*NAMED_RNN_TESTCASES) + @unittest.skipUnless(test.is_built_with_cuda(), + "Test only applicable when running on GPUs") + def test_training_fp16(self, num_units, input_size, batch_size, time, + num_layers): + if not context.context().num_gpus(): + self.skipTest("No GPUs found") + self._test_training_helper( + num_units, + input_size, + batch_size, + time, + num_layers, + dtypes.float16, + rtol=5e-3, + atol=5e-4) + + @parameterized.named_parameters(*NAMED_RNN_TESTCASES) + @unittest.skipUnless(test.is_built_with_cuda(), + "Test only applicable when running on GPUs") + def test_inference(self, num_units, input_size, batch_size, time, num_layers): + if not context.context().num_gpus(): + self.skipTest("No GPUs found") + with self.session(use_gpu=True) as sess: + (outputs, cu_outputs, state_tuple, cu_state_tuple) = RunLSTM( + sess, + num_units, + input_size, + batch_size, + time, num_layers, + is_training=False) + + self.assertAllClose(outputs, cu_outputs) + # h + self.assertAllClose(state_tuple.h, cu_state_tuple.h) + # c + self.assertAllClose(state_tuple.c, cu_state_tuple.c) + + @parameterized.named_parameters(*NAMED_RNN_TESTCASES) + @unittest.skipUnless(test.is_built_with_cuda(), + "Test only applicable when running on GPUs") + def test_inference_fp16(self, num_units, input_size, batch_size, time, + num_layers): + if not context.context().num_gpus(): + self.skipTest("No GPUs found") + with self.session(use_gpu=True) as sess: + (outputs, cu_outputs, state_tuple, cu_state_tuple) = RunLSTM( + sess, num_units, input_size, - direction=direction, - dtype=dtype) - params_size_t = model.params_size() - params = variables.VariableV1( - array_ops.ones([params_size_t], dtype=dtype), - validate_shape=False, - dtype=dtype) - _CreateParamsSavable(params, model) - save_path = os.path.join(self.get_temp_dir(), "save-restore-output-test") - saver = saver_lib.Saver(write_version=saver_pb2.SaverDef.V2) + batch_size, + time, + num_layers, + is_training=False, + dtype=dtypes.float16) - np.random.seed(1234) - has_input_c = (rnn_mode == cudnn_rnn_ops.CUDNN_LSTM) - input_data = constant_op.constant( - np.random.randn(seq_length, batch_size, input_size), dtype=dtype) - input_h = constant_op.constant( - np.random.randn(num_layers * dir_count, batch_size, num_units), - dtype=dtype) - if has_input_c: - input_c = constant_op.constant( - np.random.randn(num_layers * dir_count, batch_size, num_units), - dtype=dtype) - outputs = model( - input_data=input_data, - input_h=input_h, - input_c=input_c, - params=params, - is_training=False) - else: - outputs = model( - input_data=input_data, - input_h=input_h, - params=params, - is_training=False) - total_sum = sum(map(math_ops.reduce_sum, outputs)) - # Passing graph explicitly, otherwise an old sess would be reused. - with self.test_session( - use_gpu=True, graph=ops.get_default_graph()) as sess: - sess.run(variables.global_variables_initializer()) - total_sum_v = sess.run(total_sum) - val = saver.save(sess, save_path) - self.assertEqual(save_path, val) - # Passing graph explicitly, otherwise an old sess would be reused. - with self.test_session( - use_gpu=True, graph=ops.get_default_graph()) as sess: - reset_params = state_ops.assign( - params, - array_ops.zeros([params_size_t], dtype=dtype), - validate_shape=False) - sess.run(reset_params) - saver.restore(sess, save_path) - total_sum_v_restored = sess.run(total_sum) - self.assertAllClose(total_sum_v, total_sum_v_restored, atol=1e-5) + rtol, atol = 5e-3, 5e-4 + self.assertAllClose(outputs, cu_outputs, rtol=rtol, atol=atol) + # h + self.assertAllClose( + state_tuple.h, cu_state_tuple.h, rtol=rtol, atol=atol) + # c + self.assertAllClose( + state_tuple.c, cu_state_tuple.c, rtol=rtol, atol=atol) + @parameterized.named_parameters(*NAMED_RNN_TESTCASES) @unittest.skipUnless(test.is_built_with_cuda(), "Test only applicable when running on GPUs") - def testSaveRestore(self): - rnn_modes = [ - cudnn_rnn_ops.CUDNN_LSTM, cudnn_rnn_ops.CUDNN_GRU, - cudnn_rnn_ops.CUDNN_RNN_TANH, cudnn_rnn_ops.CUDNN_RNN_RELU - ] - directions = [ - cudnn_rnn_ops.CUDNN_RNN_UNIDIRECTION, - cudnn_rnn_ops.CUDNN_RNN_BIDIRECTION - ] - dtype_list = [dtypes.float32, dtypes.float64] - for rnn_mode, direction, dtype in itertools.product(rnn_modes, directions, - dtype_list): - self._testSaveRestoreVariable(rnn_mode, direction, dtype) - self._testSaveRestoreTwoVariables(rnn_mode, direction, dtype) - self._testSaveRestoreOutput(rnn_mode, direction, dtype) - - -class CudnnRNNTestParamsSize(TensorFlowTestCase): - - def _testOneLSTMParamsSize(self, num_layers, num_units, input_size, - direction): - logging.info("Testing one lstm param size with config: %s", locals()) - min_params_size = _MinLSTMParamSize(num_layers, num_units, input_size, - direction) - model = _CreateModel( - cudnn_rnn_ops.CUDNN_LSTM, - num_layers, + def test_inference_with_dropout(self, num_units, input_size, batch_size, time, + num_layers): + """Validates that dropout does not affect Cudnn Rnn inference.""" + if not context.context().num_gpus(): + self.skipTest("No GPUs found") + # Hand-picked dropouts are used below (0. and 1.) + with ops.Graph().as_default() as g: + with self.session(use_gpu=True, graph=g) as sess: + # 1st time w/o dropout. + (_, cu_outputs, _, cu_state_tuple) = RunLSTM( + sess, + num_units, + input_size, + batch_size, + time, + num_layers, + is_training=False, + dropout=0.) + + with ops.Graph().as_default() as g: + with self.session(use_gpu=True, graph=g) as sess: + (_, cu_outputs2, _, cu_state_tuple2) = RunLSTM( + sess, + num_units, + input_size, + batch_size, + time, + num_layers, + is_training=False, + dropout=1.) + + self.assertAllClose(cu_outputs, cu_outputs2) + # h + self.assertAllClose(cu_state_tuple.h, cu_state_tuple2.h) + # c + self.assertAllClose(cu_state_tuple.c, cu_state_tuple2.c) + + +def RunGRU(sess, + num_units, + input_size, + batch_size, + time, + num_layers=1, + is_training=True, + dropout=0., + num_dirs=True, + dtype=dtypes.float32): + # TODO(jamesqin): add multi-layer tests. + # TODO(jamesqin): add multi-dir tests + assert num_layers == 1 + assert num_dirs == 1 + if is_training and not np.isclose(dropout, 0): + raise ValueError("dropout can not be 0. when test training.") + + # set graph level random seed and numpy random seed. + random_seed.set_random_seed(0) + np.random.seed(0) + + inputs = variable_scope.get_variable( + "inputs", + initializer=np.random.rand(time, batch_size, + input_size).astype(dtype.as_numpy_dtype), + dtype=dtype) + initial_h_op = variable_scope.get_variable( + "initial_h_op", + initializer=np.random.rand(batch_size, + num_units).astype(dtype.as_numpy_dtype), + dtype=dtype) + + initializer = init_ops.random_uniform_initializer( + -0.01, 0.01, dtype=dtype, seed=19980904) + with variable_scope.variable_scope("test", initializer=initializer): + gate_kernel = variable_scope.get_variable( + "rnn/cudnn_compatible_gru_cell/gates/kernel", + shape=[input_size + num_units, num_units * 2], + dtype=dtype) + gate_bias = variable_scope.get_variable( + "rnn/cudnn_compatible_gru_cell/gates/bias", + shape=[num_units * 2], + dtype=dtype) + candidate_inp_kernel = variable_scope.get_variable( + "rnn/cudnn_compatible_gru_cell/candidate/input_projection/kernel", + shape=[input_size, num_units], + dtype=dtype) + candidate_inp_bias = variable_scope.get_variable( + "rnn/cudnn_compatible_gru_cell/candidate/input_projection/bias", + shape=[num_units], + dtype=dtype) + candidate_hid_kernel = variable_scope.get_variable( + "rnn/cudnn_compatible_gru_cell/candidate/hidden_projection/kernel", + shape=[num_units, num_units], + dtype=dtype) + candidate_hid_bias = variable_scope.get_variable( + "rnn/cudnn_compatible_gru_cell/candidate/hidden_projection/bias", + shape=[num_units], + dtype=dtype) + + cell = cudnn_rnn_ops.CudnnCompatibleGRUCell(num_units, reuse=True) + outputs_op, h_op = rnn.dynamic_rnn( + cell, + inputs, + initial_state=initial_h_op, + dtype=dtype, + time_major=True, + scope=None) + + ws = [gate_kernel, candidate_inp_kernel, candidate_hid_kernel] + bs = [gate_bias, candidate_inp_bias, candidate_hid_bias] + # Convert to cudnn opaque param. + format_converter = cudnn_rnn_ops.CudnnParamsFormatConverterGRU( + num_layers, num_units, input_size) + opaque_params = format_converter.tf_canonical_to_opaque(ws + bs) + + cu_initial_h_op = array_ops.expand_dims(initial_h_op, axis=0) + cu_outputs_op, cu_h_op, _ = cudnn_rnn_ops._cudnn_rnn( + inputs, + cu_initial_h_op, + array_ops.zeros_like(cu_initial_h_op), # not used + opaque_params, + dropout=dropout, + is_training=is_training, + rnn_mode=cudnn_rnn_ops.CUDNN_GRU) + + if is_training: + (inp_grad_op, hgrad_op, gk_grad_op, cik_grad_op, chk_grad_op, gb_grad_op, + cib_grad_op, chb_grad_op) = gradients_impl.gradients( + outputs_op, [inputs, initial_h_op] + ws + bs) + + (cu_inp_grad_op, cu_hgrad_op, opaque_grad_op) = gradients_impl.gradients( + cu_outputs_op, [inputs, cu_initial_h_op, opaque_params]) + # Remove the trivial 1st dimension + cu_hgrad_op = array_ops.squeeze(cu_hgrad_op, axis=0) + + cu_wgrad_op, cu_bgrad_op = format_converter.opaque_to_tf_canonical( + opaque_grad_op) + (cu_gk_grad_op, cu_cik_grad_op, cu_chk_grad_op) = cu_wgrad_op + (cu_gb_grad_op, cu_cib_grad_op, cu_chb_grad_op) = cu_bgrad_op + # cudnn gru has 2 biases for reset and update gates. When converting to tf + # canonical format, the two biases are summed into one. Thus here relevant + # bias gradient should be halved before comparing with tf gru. + cu_gb_grad_op *= 0.5 + + init_op = variables.global_variables_initializer() + sess.run(init_op) + + if is_training: + outputs, h, inp_grad, hgrad, wgrad, bgrad = sess.run([ + outputs_op, h_op, inp_grad_op, hgrad_op, + (gk_grad_op, cik_grad_op, chk_grad_op), + (gb_grad_op, cib_grad_op, chb_grad_op) + ]) + (cu_outputs, cu_h, cu_inp_grad, cu_hgrad, cu_wgrad, cu_bgrad) = sess.run([ + cu_outputs_op, cu_h_op, cu_inp_grad_op, cu_hgrad_op, + (cu_gk_grad_op, cu_cik_grad_op, cu_chk_grad_op), + (cu_gb_grad_op, cu_cib_grad_op, cu_chb_grad_op) + ]) + # Remove the trivial 1st dimension + cu_h = np.squeeze(cu_h, axis=0) + + logging.vlog(1, "outputs: %s" % outputs) + logging.vlog(1, "cu_outputs: %s" % cu_outputs) + logging.vlog(1, "h: %s" % h) + logging.vlog(1, "cu_h: %s" % h) + logging.vlog(1, "inp_grad: %s" % inp_grad) + logging.vlog(1, "cu_inp_grad: %s" % cu_inp_grad) + logging.vlog(1, "hgrad: %s" % hgrad) + logging.vlog(1, "cu_hgrad: %s" % cu_hgrad) + logging.vlog(1, "wgrad: %s" % str(wgrad)) + logging.vlog(1, "bgrad: %s" % str(bgrad)) + logging.vlog(1, "cu_wgrad: %s" % str(cu_wgrad)) + logging.vlog(1, "cu_bgrad: %s" % str(cu_bgrad)) + return (outputs, cu_outputs, h, cu_h, inp_grad, cu_inp_grad, hgrad, + cu_hgrad, wgrad, bgrad, cu_wgrad, cu_bgrad) + else: + outputs, h = sess.run([outputs_op, h_op]) + cu_outputs, cu_h = sess.run([cu_outputs_op, cu_h_op]) + # Remove the trivial 1st dimension. + cu_h = np.squeeze(cu_h, axis=0) + + logging.vlog(1, "outputs: %s" % outputs) + logging.vlog(1, "cu_outputs: %s" % cu_outputs) + logging.vlog(1, "h: %s" % h) + logging.vlog(1, "cu_h: %s" % h) + return outputs, cu_outputs, h, cu_h + + +class CudnnGRUTest(TensorFlowTestCase, parameterized.TestCase): + + def _test_training_helper(self, + num_units, + input_size, + batch_size, + time, + num_layers, + dtype, + rtol=2e-6, + atol=2e-6): + with self.session(use_gpu=True) as sess: + (outputs, cu_outputs, h, cu_h, inp_grad, cu_inp_grad, hgrad, + cu_hgrad, wgrad, bgrad, cu_wgrad, cu_bgrad) = RunGRU( + sess, num_units, input_size, batch_size, time, num_layers) + + self.assertAllClose(outputs, cu_outputs, rtol=rtol, atol=atol) + self.assertAllClose(h, cu_h, rtol=rtol, atol=atol) + self.assertAllClose(hgrad, cu_hgrad, rtol=rtol, atol=atol) + self.assertAllClose(inp_grad, cu_inp_grad, rtol=rtol, atol=atol) + for bg, cu_bg in zip(bgrad, cu_bgrad): + self.assertAllClose(bg, cu_bg, rtol=rtol, atol=atol) + for wg, cu_wg in zip(wgrad, cu_wgrad): + self.assertAllClose(wg, cu_wg, rtol=rtol, atol=atol) + + @parameterized.named_parameters(*NAMED_RNN_TESTCASES) + @unittest.skipUnless(test.is_built_with_cuda(), + "Test only applicable when running on GPUs") + def test_training(self, num_units, input_size, batch_size, time, num_layers): + if not context.context().num_gpus(): + self.skipTest("No GPUs found") + self._test_training_helper(num_units, input_size, batch_size, time, + num_layers, dtypes.float32) + + @parameterized.named_parameters(*NAMED_RNN_TESTCASES) + @unittest.skipUnless(test.is_built_with_cuda(), + "Test only applicable when running on GPUs") + def test_training_fp16(self, num_units, input_size, batch_size, time, + num_layers): + if not context.context().num_gpus(): + self.skipTest("No GPUs found") + self._test_training_helper( num_units, input_size, - direction=direction) - params_size = model.params_size() - with self.test_session(use_gpu=True, graph=ops.get_default_graph()) as sess: - params_size_v = sess.run(params_size) - self.assertLessEqual(min_params_size, params_size_v) + batch_size, + time, + num_layers, + dtypes.float16, + rtol=5e-3, + atol=5e-4) + @parameterized.named_parameters(*NAMED_RNN_TESTCASES) @unittest.skipUnless(test.is_built_with_cuda(), "Test only applicable when running on GPUs") - def testLSTMParamsSize(self): - test_configs = [ - [4, 200, 200], - [4, 200, 300], - [4, 200, 100], - [1, 100, 200], - [2, 200, 100], - [3, 200, 400], - ] - directions = [ - cudnn_rnn_ops.CUDNN_RNN_UNIDIRECTION, - cudnn_rnn_ops.CUDNN_RNN_BIDIRECTION - ] - for (config, direction) in itertools.product(test_configs, directions): - num_layers, num_units, input_size = config - with ops.Graph().as_default(): - self._testOneLSTMParamsSize(num_layers, num_units, input_size, - direction) + def test_inference(self, num_units, input_size, batch_size, time, num_layers): + if not context.context().num_gpus(): + self.skipTest("No GPUs found") + with self.session(use_gpu=True) as sess: + (outputs, cu_outputs, h, cu_h) = RunGRU( + sess, + num_units, + input_size, + batch_size, + time, + num_layers, + is_training=False) + self.assertAllClose(outputs, cu_outputs) + self.assertAllClose(h, cu_h) + @parameterized.named_parameters(*NAMED_RNN_TESTCASES) @unittest.skipUnless(test.is_built_with_cuda(), "Test only applicable when running on GPUs") - def testLSTMParamsSizeShape(self): - with self.assertRaisesRegexp( - ValueError, "Shape must be rank 0 but is rank 1"): - model = _CreateModel( - cudnn_rnn_ops.CUDNN_LSTM, - constant_op.constant([4]), 200, 200, - direction=cudnn_rnn_ops.CUDNN_RNN_UNIDIRECTION) - _ = model.params_size() - with self.assertRaisesRegexp( - ValueError, "Shape must be rank 0 but is rank 1"): - model = _CreateModel( - cudnn_rnn_ops.CUDNN_LSTM, - 4, constant_op.constant([200]), 200, - direction=cudnn_rnn_ops.CUDNN_RNN_UNIDIRECTION) - _ = model.params_size() - with self.assertRaisesRegexp( - ValueError, "Shape must be rank 0 but is rank 1"): - model = _CreateModel( + def test_inference_fp16(self, num_units, input_size, batch_size, time, + num_layers): + if not context.context().num_gpus(): + self.skipTest("No GPUs found") + with self.session(use_gpu=True) as sess: + (outputs, cu_outputs, h, cu_h) = RunGRU( + sess, + num_units, + input_size, + batch_size, + time, + num_layers, + is_training=False, + dtype=dtypes.float16) + + rtol, atol = 5e-3, 5e-4 + self.assertAllClose(outputs, cu_outputs, rtol=rtol, atol=atol) + self.assertAllClose(h, cu_h, rtol=rtol, atol=atol) + + @parameterized.named_parameters(*NAMED_RNN_TESTCASES) + @unittest.skipUnless(test.is_built_with_cuda(), + "Test only applicable when running on GPUs") + def test_inference_with_dropout(self, num_units, input_size, batch_size, time, + num_layers): + """Validates that dropout does not affect Cudnn Rnn inference.""" + # Hand-picked dropouts are used below (0. and 1.) + if not context.context().num_gpus(): + self.skipTest("No GPUs found") + with ops.Graph().as_default() as g: + with self.session(use_gpu=True, graph=g) as sess: + # 1st time w/o dropout. + (_, cu_outputs, _, cu_h) = RunGRU( + sess, + num_units, + input_size, + batch_size, + time, + num_layers, + is_training=False, + dropout=0.) + + with ops.Graph().as_default() as g: + with self.session(use_gpu=True, graph=g) as sess: + (_, cu_outputs2, _, cu_h2) = RunGRU( + sess, + num_units, + input_size, + batch_size, + time, + num_layers, + is_training=False, + dropout=1.) + + self.assertAllClose(cu_outputs, cu_outputs2) + self.assertAllClose(cu_h[0], cu_h2[0]) + + +class CudnnParamsFormatConverterTest(TensorFlowTestCase, + parameterized.TestCase): + """Class for testing various format converters.""" + + def _test_lstm_helper(self, num_units, input_size, num_layers, direction): + with self.session(use_gpu=True) as sess: + random_seed.set_random_seed(0) + np.random.seed(0) + + num_dirs = 1 if direction == cudnn_rnn_ops.CUDNN_RNN_UNIDIRECTION else 2 + format_converter = cudnn_rnn_ops.CudnnParamsFormatConverterLSTM( + num_layers, num_units, input_size, direction=direction) + + ws, bs = [], [] + for _ in range(num_layers * num_dirs): + w = constant_op.constant( + np.random.rand(input_size + num_units, 4 * num_units), + dtype=dtypes.float32) + b = constant_op.constant( + np.random.rand(4 * num_units), dtype=dtypes.float32) + ws.append(w) + bs.append(b) + + opaque_params = format_converter.tf_canonical_to_opaque(ws + bs) + opaque_params_size = cudnn_rnn_ops.cudnn_rnn_opaque_params_size( cudnn_rnn_ops.CUDNN_LSTM, - 4, 200, constant_op.constant([200]), - direction=cudnn_rnn_ops.CUDNN_RNN_UNIDIRECTION) - _ = model.params_size() + num_layers, + num_units, + input_size, + direction=direction) + ws_r, bs_r = format_converter.opaque_to_tf_canonical(opaque_params) -class CudnnRNNTestInference(TensorFlowTestCase): + # Test tf_canonical_to_opaque() followed by opaque_to_tf_canonical() + # returns the original input. + ws, ws_r, bs, bs_r = sess.run([ws, ws_r, bs, bs_r]) + for w, w_r in zip(ws, ws_r): + self.assertAllClose(w, w_r) + for b, b_r in zip(bs, bs_r): + self.assertAllClose(b, b_r) - def _testOneSimpleInference(self, rnn_mode, num_layers, num_units, input_size, - batch_size, seq_length, dir_count, dropout, - expected, tolerance): - random_seed.set_random_seed(5678) - model = _CreateModel( - rnn_mode, - num_layers, - num_units, - input_size, - input_mode="auto_select", - direction=(cudnn_rnn_ops.CUDNN_RNN_UNIDIRECTION if dir_count == 1 - else cudnn_rnn_ops.CUDNN_RNN_BIDIRECTION), - dropout=dropout) - has_input_c = (rnn_mode == cudnn_rnn_ops.CUDNN_LSTM) - params_size_t = model.params_size() - input_data = array_ops.ones([seq_length, batch_size, input_size]) - input_h = array_ops.ones([num_layers * dir_count, batch_size, num_units]) - params = variables.VariableV1( - array_ops.ones([params_size_t]), validate_shape=False) - if has_input_c: - input_c = array_ops.ones([num_layers * dir_count, batch_size, num_units]) - output, output_h, output_c = model( - input_data=input_data, - input_h=input_h, - input_c=input_c, - params=params, - is_training=False) - else: - output, output_h = model( - input_data=input_data, - input_h=input_h, - params=params, - is_training=False) - output_sum = math_ops.reduce_sum(output) - output_h_sum = math_ops.reduce_sum(output_h) - total_sum = output_sum + output_h_sum - if has_input_c: - output_c_sum = math_ops.reduce_sum(output_c) - total_sum += output_c_sum - with self.test_session(use_gpu=True, graph=ops.get_default_graph()) as sess: - sess.run(variables.global_variables_initializer()) - total_sum_v = sess.run([total_sum]) + # Test opaque_params size lower bound + opaque_params_size_v = sess.run(opaque_params_size) + min_params_size = sum(x.size for x in ws) + np.sum(x.size for x in bs) + logging.info("min_parm_size: %d vs actual_opaque_param_size: %d", + min_params_size, opaque_params_size_v) + self.assertLessEqual(min_params_size, opaque_params_size_v) - self.assertAllClose( - total_sum_v[0], expected, atol=tolerance, rtol=tolerance) + @parameterized.named_parameters((c["testcase_name"], c["num_units"], + c["input_size"], c["num_layers"]) + for c in NAMED_RNN_TESTCASES) + @unittest.skipUnless(test.is_built_with_cuda(), + "Test only applicable when running on GPUs") + def test_lstm(self, num_units, input_size, num_layers): + if not context.context().num_gpus(): + self.skipTest("No GPUs found") + self._test_lstm_helper(num_units, input_size, num_layers, + cudnn_rnn_ops.CUDNN_RNN_UNIDIRECTION) + @parameterized.named_parameters((c["testcase_name"], c["num_units"], + c["input_size"], c["num_layers"]) + for c in NAMED_RNN_TESTCASES) @unittest.skipUnless(test.is_built_with_cuda(), "Test only applicable when running on GPUs") - def testSimpleInference(self): - test_configs = [ - { - "rnn_mode": cudnn_rnn_ops.CUDNN_LSTM, - "expected": 231833.22, - "tolerance": 1e-2, - "shape": { - "num_layers": 4, - "num_units": 200, - "input_size": 200, - "batch_size": 20, - "seq_length": 10, - "dir_count": 1, - }, - }, - { - "rnn_mode": cudnn_rnn_ops.CUDNN_GRU, - "expected": 56000, - "tolerance": 1e-2, - "shape": { - "num_layers": 4, - "num_units": 200, - "input_size": 200, - "batch_size": 20, - "seq_length": 10, - "dir_count": 1, - }, - }, - { - "rnn_mode": cudnn_rnn_ops.CUDNN_RNN_TANH, - "expected": 56000, - "tolerance": 1e-2, - "shape": { - "num_layers": 4, - "num_units": 200, - "input_size": 200, - "batch_size": 20, - "seq_length": 10, - "dir_count": 1, - }, - }, - { - "rnn_mode": cudnn_rnn_ops.CUDNN_RNN_RELU, - "expected": 130688, - "tolerance": 1e-2, - "shape": { - "num_layers": 2, - "num_units": 8, - "input_size": 4, - "batch_size": 4, - "seq_length": 2, - "dir_count": 1, - }, - }, - ] - # Cudnn scales result for dropout during training, therefore dropout has no - # impact for inference results. - # (lstm, gru, rnn_tanh are saturated in the test. rnn_relu case is most - # demonstrative of the dropout-invariant nature of CudnnRnn.) - dropouts = [0., 0.5, 1.] - for (config, dropout) in itertools.product(test_configs, dropouts): - rnn_mode = config["rnn_mode"] - expected = config["expected"] - tolerance = config["tolerance"] - shape = config["shape"] - with ops.Graph().as_default(): - self._testOneSimpleInference( - rnn_mode, shape["num_layers"], shape["num_units"], - shape["input_size"], shape["batch_size"], shape["seq_length"], - shape["dir_count"], dropout, expected, tolerance) - - -class CudnnRNNTestTraining(TensorFlowTestCase): - - def _testOneSimpleTraining(self, rnn_mode, num_layers, num_units, input_size, - batch_size, seq_length, dir_count, dropout, dtype, - delta, tolerance): - # Gradient checking runs two forward ops with almost the same input. Need to - # make sure the drop patterns across the two runs are the same. - logging.info("Training test with config: %s", locals()) - old_env_state = os.environ.get("TF_CUDNN_RESET_RND_GEN_STATE", str(False)) - os.environ["TF_CUDNN_RESET_RND_GEN_STATE"] = str(True) - has_input_c = (rnn_mode == cudnn_rnn_ops.CUDNN_LSTM) - random_seed.set_random_seed(5678) - direction = (cudnn_rnn_ops.CUDNN_RNN_UNIDIRECTION if dir_count == 1 - else cudnn_rnn_ops.CUDNN_RNN_BIDIRECTION) - model = _CreateModel( - rnn_mode, - num_layers, - num_units, - input_size, - direction=direction, - dtype=dtype, - dropout=dropout) - params_size_t = model.params_size() - input_data = variables.VariableV1( - random_ops.random_uniform( - [seq_length, batch_size, input_size], dtype=dtype), - dtype=dtype) - input_h = variables.VariableV1( - random_ops.random_uniform( - [num_layers * dir_count, batch_size, num_units], dtype=dtype), - dtype=dtype) - params = variables.VariableV1( - random_ops.random_uniform([params_size_t], dtype=dtype), - validate_shape=False, - dtype=dtype) - if has_input_c: - input_c = variables.VariableV1( - random_ops.random_uniform( - [num_layers * dir_count, batch_size, num_units], dtype=dtype), - dtype=dtype) - - output, output_h, output_c = model( - input_data=input_data, - input_h=input_h, - input_c=input_c, - params=params) - else: - output, output_h = model( - input_data=input_data, input_h=input_h, params=params) - output_sum = math_ops.reduce_sum(output) - output_h_sum = math_ops.reduce_sum(output_h) - total_sum = output_sum + output_h_sum - if has_input_c: - output_c_sum = math_ops.reduce_sum(output_c) - total_sum += output_c_sum - - with self.test_session(use_gpu=True, graph=ops.get_default_graph()) as sess: - params_size_v = sess.run(params_size_t) - inputs_and_shapes = [ - (input_data, [seq_length, batch_size, input_size]), - (input_h, [num_layers * dir_count, batch_size, num_units]), - (params, [params_size_v]), - ] - if has_input_c: - inputs_and_shapes.append( - (input_c, [num_layers * dir_count, batch_size, num_units]),) - sess.run(variables.global_variables_initializer()) - all_inputs = [entry[0] for entry in inputs_and_shapes] - all_shapes = [entry[1] for entry in inputs_and_shapes] - - err = gradient_checker.compute_gradient_error( - all_inputs, all_shapes, total_sum, [1], delta=delta) - - self.assertLess(err, tolerance) - os.environ["TF_CUDNN_RESET_RND_GEN_STATE"] = old_env_state + def test_lstm_bidi(self, num_units, input_size, num_layers): + if not context.context().num_gpus(): + self.skipTest("No GPUs found") + self._test_lstm_helper(num_units, input_size, num_layers, + cudnn_rnn_ops.CUDNN_RNN_BIDIRECTION) + + def _test_gru_helper(self, num_units, input_size, num_layers, direction): + with self.session(use_gpu=True) as sess: + random_seed.set_random_seed(0) + np.random.seed(0) + + num_dirs = 1 if direction == cudnn_rnn_ops.CUDNN_RNN_UNIDIRECTION else 2 + format_converter = cudnn_rnn_ops.CudnnParamsFormatConverterGRU( + num_layers, num_units, input_size, direction=direction) + + ws, bs = [], [] + for _ in range(num_layers * num_dirs): + gate_kernel = constant_op.constant( + np.random.rand(input_size + num_units, num_units * 2), + dtype=dtypes.float32) + gate_bias = constant_op.constant( + np.random.rand(num_units * 2), dtype=dtypes.float32) + candidate_inp_kernel = constant_op.constant( + np.random.rand(input_size, num_units), dtype=dtypes.float32) + candidate_inp_bias = constant_op.constant( + np.random.rand(num_units), dtype=dtypes.float32) + candidate_hid_kernel = constant_op.constant( + np.random.rand(num_units, num_units), dtype=dtypes.float32) + candidate_hid_bias = constant_op.constant( + np.random.rand(num_units), dtype=dtypes.float32) + ws.extend([gate_kernel, candidate_inp_kernel, candidate_hid_kernel]) + bs.extend([gate_bias, candidate_inp_bias, candidate_hid_bias]) + opaque_params = format_converter.tf_canonical_to_opaque(ws + bs) + opaque_params_size = cudnn_rnn_ops.cudnn_rnn_opaque_params_size( + cudnn_rnn_ops.CUDNN_GRU, + num_layers, + num_units, + input_size, + direction=direction) + + ws_r, bs_r = format_converter.opaque_to_tf_canonical(opaque_params) + + # Test tf_canonical_to_opaque() followed by opaque_to_tf_canonical() + # returns the original input. + ws, ws_r, bs, bs_r = sess.run([ws, ws_r, bs, bs_r]) + for w, w_r in zip(ws, ws_r): + self.assertAllClose(w, w_r) + for b, b_r in zip(bs, bs_r): + self.assertAllClose(b, b_r) + + # Test opaque_params size lower bound + opaque_params_size_v = sess.run(opaque_params_size) + min_params_size = sum(x.size for x in ws) + sum(x.size for x in bs) + logging.info("min_parm_size: %d vs actual_opaque_param_size: %d", + min_params_size, opaque_params_size_v) + self.assertLessEqual(min_params_size, opaque_params_size_v) + + @parameterized.named_parameters((c["testcase_name"], c["num_units"], + c["input_size"], c["num_layers"]) + for c in NAMED_RNN_TESTCASES) + @unittest.skipUnless(test.is_built_with_cuda(), + "Test only applicable when running on GPUs") + def test_gru(self, num_units, input_size, num_layers): + if not context.context().num_gpus(): + self.skipTest("No GPUs found") + self._test_gru_helper(num_units, input_size, num_layers, + cudnn_rnn_ops.CUDNN_RNN_UNIDIRECTION) + + @parameterized.named_parameters((c["testcase_name"], c["num_units"], + c["input_size"], c["num_layers"]) + for c in NAMED_RNN_TESTCASES) @unittest.skipUnless(test.is_built_with_cuda(), "Test only applicable when running on GPUs") - def DISABLED_testSimpleTraining(self): - # TODO(jamesqin): fix b/117989214 - test_configs = [ - { - "rnn_mode": cudnn_rnn_ops.CUDNN_LSTM, - "dtype": dtypes.float64, - "delta": 1e-4, - "tolerance": 5e-6, - "shape": { - "num_layers": 2, - "num_units": 3, - "input_size": 4, - "batch_size": 3, - "seq_length": 4, - "dir_count": 1, - }, - }, - { - "rnn_mode": cudnn_rnn_ops.CUDNN_GRU, - "dtype": dtypes.float64, - "delta": 1e-4, - "tolerance": 5e-6, - "shape": { - "num_layers": 2, - "num_units": 3, - "input_size": 4, - "batch_size": 3, - "seq_length": 4, - "dir_count": 1, - }, - }, - { - "rnn_mode": cudnn_rnn_ops.CUDNN_RNN_TANH, - "dtype": dtypes.float64, - "delta": 1e-4, - "tolerance": 5e-6, - "shape": { - "num_layers": 2, - "num_units": 3, - "input_size": 4, - "batch_size": 3, - "seq_length": 4, - "dir_count": 1, - }, - }, - { - "rnn_mode": cudnn_rnn_ops.CUDNN_RNN_RELU, - "dtype": dtypes.float64, - "delta": 1e-4, - "tolerance": 5e-6, - "shape": { - "num_layers": 2, - "num_units": 3, - "input_size": 4, - "batch_size": 3, - "seq_length": 4, - "dir_count": 1, - }, - }, - { - "rnn_mode": cudnn_rnn_ops.CUDNN_LSTM, - "dtype": dtypes.float32, - "tolerance": 1.5e-2, - "shape": { - "num_layers": 2, - "num_units": 3, - "input_size": 4, - "batch_size": 3, - "seq_length": 4, - }, - }, - { - "rnn_mode": cudnn_rnn_ops.CUDNN_GRU, - "dtype": dtypes.float32, - "tolerance": 4e-3, - "shape": { - "num_layers": 2, - "num_units": 3, - "input_size": 4, - "batch_size": 3, - "seq_length": 4, - }, - }, - { - "rnn_mode": cudnn_rnn_ops.CUDNN_RNN_TANH, - "dtype": dtypes.float32, - "tolerance": 5e-3, - "shape": { - "num_layers": 2, - "num_units": 3, - "input_size": 4, - "batch_size": 3, - "seq_length": 4, - }, - }, - { - "rnn_mode": cudnn_rnn_ops.CUDNN_RNN_RELU, - "dtype": dtypes.float32, - "tolerance": 5e-1, - "shape": { - "num_layers": 2, - "num_units": 3, - "input_size": 4, - "batch_size": 3, - "seq_length": 4, - }, - }, - ] - dropouts = [0., 0.5, 1.] - dir_counts = [1] - for config, dropout, dir_count in itertools.product(test_configs, dropouts, - dir_counts): - rnn_mode = config["rnn_mode"] - dtype = config.get("dtype", dtypes.float32) - delta = config.get("delta", 1e-3) - tolerance = config["tolerance"] - shape = config["shape"] - with ops.Graph().as_default(): - self._testOneSimpleTraining(rnn_mode, shape["num_layers"], - shape["num_units"], shape["input_size"], - shape["batch_size"], shape["seq_length"], - dir_count, dropout, dtype, delta, tolerance) + def test_gru_bidi(self, num_units, input_size, num_layers): + if not context.context().num_gpus(): + self.skipTest("No GPUs found") + self._test_gru_helper(num_units, input_size, num_layers, + cudnn_rnn_ops.CUDNN_RNN_BIDIRECTION) + + +class CudnnRnnSaveRestoreTest(TensorFlowTestCase, parameterized.TestCase): + """Class for testing various Cudnn Rnn SaveableObjects.""" + + def _create_opaque_param(self, + rnn_mode, + num_units, + input_size, + num_layers, + direction, + name=None): + param_size_t = cudnn_rnn_ops.cudnn_rnn_opaque_params_size( + rnn_mode, num_layers, num_units, input_size, direction=direction) + init_val = random_ops.random_uniform([param_size_t]) + return variable_scope.get_variable( + name or "opaque_param", initializer=init_val, validate_shape=False) + + def _create_saveable(self, opaque_param, rnn_mode, num_units, input_size, + num_layers, direction): + if rnn_mode == CUDNN_LSTM: + fn = cudnn_rnn_ops.CudnnLSTMSaveable + elif rnn_mode == CUDNN_GRU: + fn = cudnn_rnn_ops.CudnnGRUSaveable + elif rnn_mode == CUDNN_RNN_TANH: + fn = cudnn_rnn_ops.CudnnRNNTanhSaveable + elif rnn_mode == CUDNN_RNN_RELU: + fn = cudnn_rnn_ops.CudnnRNNReluSaveable + saveable = fn( + opaque_param, num_layers, num_units, input_size, direction=direction) + return saveable + + def _compare_weights(self, lhs, rhs): + self.assertLen(rhs, len(lhs)) + for lw, rw in zip(lhs, rhs): + self.assertAllEqual(lw, rw) + + def _compare_biases(self, lhs, rhs): + self.assertLen(rhs, len(lhs)) + for lf, rt in zip(lhs, rhs): + self.assertAllEqual(lf, rt) + + @parameterized.named_parameters( + ExpandNamedTestCases( + NAMED_RNN_TESTCASES, "time", "batch_size", **{ + "rnn_mode": [ + CUDNN_LSTM, CUDNN_GRU, CUDNN_RNN_RELU, CUDNN_RNN_TANH + ], + "direction": [CUDNN_RNN_UNIDIRECTION, CUDNN_RNN_BIDIRECTION] + })) + @unittest.skipUnless(test.is_built_with_cuda(), + "Test only applicable when running on GPUs") + def test_save_restore_variable(self, rnn_mode, num_units, input_size, + num_layers, direction): + # Verify the restored opaque param, once converted to tf_canonical format, + # is the same as the tf canonicals of the pre-restored param. + if not context.context().num_gpus(): + self.skipTest("No GPUs found") + with self.session(use_gpu=True) as sess: + opaque_param = self._create_opaque_param(rnn_mode, num_units, input_size, + num_layers, direction) + saveable = self._create_saveable(opaque_param, rnn_mode, num_units, + input_size, num_layers, direction) + ops.add_to_collection(ops.GraphKeys.SAVEABLE_OBJECTS, saveable) + weights_op, biases_op = saveable.format_converter.opaque_to_tf_canonical( + saveable._variables) + + save_path = os.path.join(self.get_temp_dir(), "save_restore_var_test") + saver = saver_lib.Saver(write_version=saver_pb2.SaverDef.V2) + + init_op = variables.global_variables_initializer() + reset_op = state_ops.assign(opaque_param, + array_ops.zeros_like(opaque_param)) + sess.run(init_op) + self.assertEqual(save_path, saver.save(sess, save_path)) + + # Get the tf canonical vals before reset-restore + weights, biases = sess.run([weights_op, biases_op]) + + # Reset the opaque param value + sess.run(reset_op) + # Assert reset happened. + weights_z, biases_z = sess.run([weights_op, biases_op]) + for w in weights_z: + self.assertAllClose(w, np.zeros_like(w)) + for b in biases_z: + self.assertAllClose(b, np.zeros_like(b)) + + # Restore opaque param value from checkpoint. + saver.restore(sess, save_path) + weights_r, biases_r = sess.run([weights_op, biases_op]) + self._compare_weights(weights, weights_r) + self._compare_biases(biases, biases_r) + + @parameterized.named_parameters( + ExpandNamedTestCases( + NAMED_RNN_TESTCASES, "time", "batch_size", **{ + "rnn_mode": [ + CUDNN_LSTM, CUDNN_GRU, CUDNN_RNN_RELU, CUDNN_RNN_TANH + ], + "direction": [CUDNN_RNN_UNIDIRECTION, CUDNN_RNN_BIDIRECTION] + })) + @unittest.skipUnless(test.is_built_with_cuda(), + "Test only applicable when running on GPUs") + def test_save_restore_multi_variables(self, rnn_mode, num_units, input_size, + num_layers, direction): + # Verify the restored opaque param, once converted to tf_canonical format, + # is the same as the tf canonicals of the pre-restored param. + if not context.context().num_gpus(): + self.skipTest("No GPUs found") + with self.session(use_gpu=True) as sess: + opaque_params = [] + saveables = [] + num_opaque_params = 2 + for i in range(num_opaque_params): + opaque_params.append( + self._create_opaque_param( + rnn_mode, + num_units, + input_size, + num_layers, + direction, + name="opaque_param_%d" % i)) + saveable = self._create_saveable(opaque_params[i], rnn_mode, num_units, + input_size, num_layers, direction) + ops.add_to_collection(ops.GraphKeys.SAVEABLE_OBJECTS, saveable) + saveables.append(saveable) + + weights_ops, biases_ops = [], [] + for i in range(num_opaque_params): + weights_op, biases_op = ( + saveables[i].format_converter.opaque_to_tf_canonical( + saveables[i]._variables)) + weights_ops.append(weights_op) + biases_ops.append(biases_op) + + save_path = os.path.join(self.get_temp_dir(), "save_restore_var_test") + saver = saver_lib.Saver(write_version=saver_pb2.SaverDef.V2) + + init_op = variables.global_variables_initializer() + reset_ops = [] + for i in range(num_opaque_params): + reset_ops.append( + state_ops.assign(opaque_params[i], + array_ops.zeros_like(opaque_params[i]))) + sess.run(init_op) + self.assertEqual(save_path, saver.save(sess, save_path)) + + # Get the tf canonical vals before reset-restore + for i in range(num_opaque_params): + weights, biases = sess.run([weights_ops[i], biases_ops[i]]) + + # Reset the opaque param value + sess.run(reset_ops[i]) + + # Assert reset happened. + weights_z, biases_z = sess.run([weights_ops[i], biases_ops[i]]) + for w in weights_z: + self.assertAllClose(w, np.zeros_like(w)) + for b in biases_z: + self.assertAllClose(b, np.zeros_like(b)) + + # Restore opaque param value from checkpoint. + saver.restore(sess, save_path) + weights_r, biases_r = sess.run([weights_ops[i], biases_ops[i]]) + self._compare_weights(weights, weights_r) + self._compare_biases(biases, biases_r) if __name__ == "__main__": diff --git a/tensorflow/contrib/cudnn_rnn/python/kernel_tests/cudnn_rnn_test.py b/tensorflow/contrib/cudnn_rnn/python/kernel_tests/cudnn_rnn_test.py index 1954f6717b..7e1b4062ce 100644 --- a/tensorflow/contrib/cudnn_rnn/python/kernel_tests/cudnn_rnn_test.py +++ b/tensorflow/contrib/cudnn_rnn/python/kernel_tests/cudnn_rnn_test.py @@ -536,7 +536,9 @@ class CudnnRNNTestSaveRestore(test_util.TensorFlowTestCase): save_path = os.path.join(self.get_temp_dir(), "save-restore-variable-test") saver = saver_lib.Saver() - weights, biases = model.rnn.saveable._OpaqueParamsToCanonical() + weights, biases = ( + model.rnn.saveable.format_converter._opaque_to_cu_canonical( + model.rnn.saveable._variables)) opaque_params = rnn.trainable_variables[0] # CudnnTestModel() creates CudnnOpaqueParamsSaveable that helps saver save # Cudnn vars in canonical format. @@ -583,8 +585,12 @@ class CudnnRNNTestSaveRestore(test_util.TensorFlowTestCase): dtype=dtype) opaque_params = (model1.rnn.trainable_variables[0], model2.rnn.trainable_variables[0]) - weights1, biases1 = model1.rnn.saveable._OpaqueParamsToCanonical() - weights2, biases2 = model2.rnn.saveable._OpaqueParamsToCanonical() + saveable1 = model1.rnn.saveable + weights1, biases1 = saveable1.format_converter._opaque_to_cu_canonical( + saveable1._variables) + saveable2 = model1.rnn.saveable + weights2, biases2 = saveable2.format_converter._opaque_to_cu_canonical( + saveable2._variables) reset_params = [ state_ops.assign(params, array_ops.zeros_like(params, dtype=dtype)) @@ -1039,8 +1045,8 @@ class CudnnRNNTestParamsSize(test_util.TensorFlowTestCase): # Min param size estimate = sum(weights.size) + sum(biases.size) min_params_size = ( - np.sum(list(map(np.prod, rnn.canonical_weight_shapes))) + - np.sum([sp[0] for sp in rnn.canonical_bias_shapes])) + sum(map(np.prod, rnn.canonical_weight_shapes)) + + sum(sp[0] for sp in rnn.canonical_bias_shapes)) opaque_params = rnn.trainable_variables[0] with self.test_session(use_gpu=True, graph=ops.get_default_graph()): diff --git a/tensorflow/contrib/cudnn_rnn/python/layers/cudnn_rnn.py b/tensorflow/contrib/cudnn_rnn/python/layers/cudnn_rnn.py index 8bbcc7cd03..8e25637ed9 100644 --- a/tensorflow/contrib/cudnn_rnn/python/layers/cudnn_rnn.py +++ b/tensorflow/contrib/cudnn_rnn/python/layers/cudnn_rnn.py @@ -21,6 +21,7 @@ from tensorflow.contrib.cudnn_rnn.python.ops import cudnn_rnn_ops from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape +from tensorflow.python.keras.engine import input_spec from tensorflow.python.layers import base as base_layer from tensorflow.python.ops import array_ops from tensorflow.python.ops import init_ops @@ -322,7 +323,7 @@ class _CudnnRNN(base_layer.Layer): raise ValueError("The last dimension of the inputs to `CudnnRNN` " "should be defined. Found `None`.") self._input_size = input_shape[-1].value - self.input_spec = base_layer.InputSpec(ndim=3, axes={-1: self._input_size}) + self.input_spec = input_spec.InputSpec(ndim=3, axes={-1: self._input_size}) self._set_scope(None) diff --git a/tensorflow/contrib/cudnn_rnn/python/ops/cudnn_rnn_ops.py b/tensorflow/contrib/cudnn_rnn/python/ops/cudnn_rnn_ops.py index d06d0c6bda..1ce29b42d5 100644 --- a/tensorflow/contrib/cudnn_rnn/python/ops/cudnn_rnn_ops.py +++ b/tensorflow/contrib/cudnn_rnn/python/ops/cudnn_rnn_ops.py @@ -738,7 +738,7 @@ class CudnnOpaqueParamsSaveable(saver.BaseSaverBuilder.SaveableObject): self._variables, opaque_params, validate_shape=False) def _checkpointable_save(self, save_buffer): - weights, biases = self.format_converter.opaque_params_to_tf_canonical( + weights, biases = self.format_converter.opaque_to_tf_canonical( self._variables) for name, tensor in zip(self._param_names, weights + biases): save_buffer[name] = array_ops.identity(tensor) diff --git a/tensorflow/contrib/data/python/kernel_tests/assert_element_shape_test.py b/tensorflow/contrib/data/python/kernel_tests/assert_element_shape_test.py index 0456463a19..6c5f8c6b00 100644 --- a/tensorflow/contrib/data/python/kernel_tests/assert_element_shape_test.py +++ b/tensorflow/contrib/data/python/kernel_tests/assert_element_shape_test.py @@ -46,7 +46,7 @@ class AssertElementShapeTest(test_base.DatasetTestBase): result = dataset.apply(batching.assert_element_shape(expected_shapes)) self.assertEqual(expected_shapes, result.output_shapes) - iterator = result.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(result) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: @@ -88,7 +88,7 @@ class AssertElementShapeTest(test_base.DatasetTestBase): result = dataset.apply(batching.assert_element_shape(expected_shapes)) self.assertEqual(expected_shapes, result.output_shapes) - iterator = result.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(result) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: @@ -115,9 +115,8 @@ class AssertElementShapeTest(test_base.DatasetTestBase): wrong_shapes = (tensor_shape.TensorShape(2), tensor_shape.TensorShape((3, 10))) - iterator = ( - dataset.apply(batching.assert_element_shape(wrong_shapes)) - .make_initializable_iterator()) + iterator = dataset_ops.make_initializable_iterator( + dataset.apply(batching.assert_element_shape(wrong_shapes))) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: @@ -142,7 +141,7 @@ class AssertElementShapeTest(test_base.DatasetTestBase): tensor_shape.TensorShape((3, 4))) self.assertEqual(actual_shapes, result.output_shapes) - iterator = result.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(result) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: @@ -184,7 +183,7 @@ class AssertElementShapeTest(test_base.DatasetTestBase): result = dataset.apply(batching.assert_element_shape(expected_shapes)) self.assertEqual(expected_shapes, result.output_shapes) - iterator = result.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(result) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: @@ -211,9 +210,8 @@ class AssertElementShapeTest(test_base.DatasetTestBase): wrong_shapes = (tensor_shape.TensorShape(2), tensor_shape.TensorShape((None, 10))) - iterator = ( - dataset.apply(batching.assert_element_shape(wrong_shapes)) - .make_initializable_iterator()) + iterator = dataset_ops.make_initializable_iterator( + dataset.apply(batching.assert_element_shape(wrong_shapes))) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: diff --git a/tensorflow/contrib/data/python/kernel_tests/lmdb_dataset_op_test.py b/tensorflow/contrib/data/python/kernel_tests/lmdb_dataset_op_test.py index d2a72272db..b9840b1ff1 100644 --- a/tensorflow/contrib/data/python/kernel_tests/lmdb_dataset_op_test.py +++ b/tensorflow/contrib/data/python/kernel_tests/lmdb_dataset_op_test.py @@ -23,6 +23,7 @@ import shutil from tensorflow.contrib.data.python.ops import readers from tensorflow.python.data.kernel_tests import test_base +from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors @@ -48,7 +49,7 @@ class LMDBDatasetTest(test_base.DatasetTestBase): num_repeats = 2 dataset = readers.LMDBDataset(filenames).repeat(num_repeats) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) init_op = iterator.initializer get_next = iterator.get_next() diff --git a/tensorflow/contrib/data/python/kernel_tests/slide_dataset_op_test.py b/tensorflow/contrib/data/python/kernel_tests/slide_dataset_op_test.py index c5a7862322..2527706709 100644 --- a/tensorflow/contrib/data/python/kernel_tests/slide_dataset_op_test.py +++ b/tensorflow/contrib/data/python/kernel_tests/slide_dataset_op_test.py @@ -63,13 +63,13 @@ class SlideDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): # The pipeline is TensorSliceDataset -> MapDataset(square_3) -> # RepeatDataset(count) -> # _SlideDataset(window_size, window_shift, window_stride). - iterator = ( + iterator = dataset_ops.make_initializable_iterator( dataset_ops.Dataset.from_tensor_slices(components).map(_map_fn) .repeat(count).apply( sliding.sliding_window_batch( window_size=window_size_t, window_shift=window_shift_t, - window_stride=window_stride_t)).make_initializable_iterator()) + window_stride=window_stride_t))) init_op = iterator.initializer get_next = iterator.get_next() @@ -127,13 +127,13 @@ class SlideDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): # The pipeline is TensorSliceDataset -> MapDataset(square_3) -> # RepeatDataset(count) -> _SlideDataset(window_size, stride, window_stride). - iterator = ( + iterator = dataset_ops.make_initializable_iterator( dataset_ops.Dataset.from_tensor_slices(components).map(_map_fn) .repeat(count).apply( sliding.sliding_window_batch( window_size=window_size_t, stride=stride_t, - window_stride=window_stride_t)).make_initializable_iterator()) + window_stride=window_stride_t))) init_op = iterator.initializer get_next = iterator.get_next() @@ -173,12 +173,12 @@ class SlideDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): window_shift_t = array_ops.placeholder(dtypes.int64, shape=[]) window_stride_t = array_ops.placeholder(dtypes.int64, shape=[]) - iterator = ( + iterator = dataset_ops.make_initializable_iterator( dataset_ops.Dataset.range(10).map(lambda x: x).repeat(count_t).apply( sliding.sliding_window_batch( window_size=window_size_t, window_shift=window_shift_t, - window_stride=window_stride_t)).make_initializable_iterator()) + window_stride=window_stride_t))) init_op = iterator.initializer with self.cached_session() as sess: @@ -204,9 +204,9 @@ class SlideDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): return sparse_tensor.SparseTensorValue( indices=[[0]], values=(i * [1]), dense_shape=[1]) - iterator = dataset_ops.Dataset.range(10).map(_sparse).apply( - sliding.sliding_window_batch( - window_size=5, window_shift=3)).make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.range(10).map(_sparse).apply( + sliding.sliding_window_batch(window_size=5, window_shift=3))) init_op = iterator.initializer get_next = iterator.get_next() @@ -233,9 +233,9 @@ class SlideDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): values=array_ops.fill([math_ops.to_int32(i)], i), dense_shape=[i]) - iterator = dataset_ops.Dataset.range(10).map(_sparse).apply( - sliding.sliding_window_batch( - window_size=5, window_shift=3)).make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.range(10).map(_sparse).apply( + sliding.sliding_window_batch(window_size=5, window_shift=3))) init_op = iterator.initializer get_next = iterator.get_next() @@ -265,11 +265,10 @@ class SlideDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): return sparse_tensor.SparseTensorValue( indices=[[0]], values=(i * [1]), dense_shape=[1]) - iterator = ( + iterator = dataset_ops.make_initializable_iterator( dataset_ops.Dataset.range(10).map(_sparse).apply( sliding.sliding_window_batch(window_size=4, window_shift=2)).apply( - sliding.sliding_window_batch(window_size=3, window_shift=1)) - .make_initializable_iterator()) + sliding.sliding_window_batch(window_size=3, window_shift=1))) init_op = iterator.initializer get_next = iterator.get_next() @@ -305,11 +304,10 @@ class SlideDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): yield [4.0, 5.0, 6.0] yield [7.0, 8.0, 9.0, 10.0] - iterator = ( + iterator = dataset_ops.make_initializable_iterator( dataset_ops.Dataset.from_generator( generator, dtypes.float32, output_shapes=[None]).apply( - sliding.sliding_window_batch(window_size=3, window_shift=1)) - .make_initializable_iterator()) + sliding.sliding_window_batch(window_size=3, window_shift=1))) next_element = iterator.get_next() with self.cached_session() as sess: diff --git a/tensorflow/contrib/data/python/ops/BUILD b/tensorflow/contrib/data/python/ops/BUILD index 34dc2379d0..0fb406f116 100644 --- a/tensorflow/contrib/data/python/ops/BUILD +++ b/tensorflow/contrib/data/python/ops/BUILD @@ -188,8 +188,7 @@ py_library( "//tensorflow/python:framework_ops", "//tensorflow/python:function", "//tensorflow/python/data/ops:dataset_ops", - "//tensorflow/python/data/util:nest", - "//tensorflow/python/data/util:sparse", + "//tensorflow/python/data/util:structure", ], ) diff --git a/tensorflow/contrib/data/python/ops/readers.py b/tensorflow/contrib/data/python/ops/readers.py index 4601376dff..aa42782807 100644 --- a/tensorflow/contrib/data/python/ops/readers.py +++ b/tensorflow/contrib/data/python/ops/readers.py @@ -355,7 +355,7 @@ def read_batch_features(file_pattern, shuffle=randomize_input, num_epochs=num_epochs, shuffle_buffer_size=capacity) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) outputs = iterator.get_next() return outputs @@ -379,15 +379,13 @@ class LMDBDataset(dataset_ops.DatasetSource): (key value) pairs sequentially. For example: ```python + tf.enable_eager_execution() + dataset = tf.contrib.lmdb.LMDBDataset("/foo/bar.mdb") - iterator = dataset.make_one_shot_iterator() - next_element = iterator.get_next() + # Prints the (key, value) pairs inside a lmdb file. - while True: - try: - print(sess.run(next_element)) - except tf.errors.OutOfRangeError: - break + for key, value in dataset: + print(key, value) ``` Args: filenames: A `tf.string` tensor containing one or more filenames. diff --git a/tensorflow/contrib/data/python/ops/sliding.py b/tensorflow/contrib/data/python/ops/sliding.py index bcc383587c..9ebdca317f 100644 --- a/tensorflow/contrib/data/python/ops/sliding.py +++ b/tensorflow/contrib/data/python/ops/sliding.py @@ -18,11 +18,10 @@ from __future__ import division from __future__ import print_function from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.data.util import nest +from tensorflow.python.data.util import structure from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops -from tensorflow.python.framework import tensor_shape -from tensorflow.python.ops import gen_dataset_ops +from tensorflow.python.ops import gen_experimental_dataset_ops as ged_ops from tensorflow.python.util import deprecation @@ -40,29 +39,31 @@ class _SlideDataset(dataset_ops.UnaryDataset): self._window_shift = ops.convert_to_tensor( window_shift, dtype=dtypes.int64, name="window_shift") + # pylint: disable=protected-access + input_structure = structure.Structure._from_legacy_structure( + input_dataset.output_types, input_dataset.output_shapes, + input_dataset.output_classes) + self._output_structure = input_structure._batch(None) + def _as_variant_tensor(self): - return gen_dataset_ops.slide_dataset( + return ged_ops.experimental_sliding_window_dataset( self._input_dataset._as_variant_tensor(), # pylint: disable=protected-access window_size=self._window_size, window_shift=self._window_shift, window_stride=self._window_stride, - **dataset_ops.flat_structure(self)) + **dataset_ops.flat_structure(structure=self._output_structure)) @property def output_classes(self): - return self._input_dataset.output_classes + return self._output_structure._to_legacy_output_classes() # pylint: disable=protected-access @property def output_shapes(self): - input_shapes = self._input_dataset.output_shapes - return nest.pack_sequence_as(input_shapes, [ - tensor_shape.vector(None).concatenate(s) - for s in nest.flatten(self._input_dataset.output_shapes) - ]) + return self._output_structure._to_legacy_output_shapes() # pylint: disable=protected-access @property def output_types(self): - return self._input_dataset.output_types + return self._output_structure._to_legacy_output_types() # pylint: disable=protected-access @deprecation.deprecated_args( diff --git a/tensorflow/contrib/distribute/BUILD b/tensorflow/contrib/distribute/BUILD index a87a5624c8..3ecd755d86 100644 --- a/tensorflow/contrib/distribute/BUILD +++ b/tensorflow/contrib/distribute/BUILD @@ -26,7 +26,6 @@ py_library( visibility = ["//tensorflow:internal"], deps = [ "//tensorflow/contrib/distribute/python:collective_all_reduce_strategy", - "//tensorflow/contrib/distribute/python:cross_tower_ops", "//tensorflow/contrib/distribute/python:mirrored_strategy", "//tensorflow/contrib/distribute/python:monitor", "//tensorflow/contrib/distribute/python:one_device_strategy", @@ -35,6 +34,7 @@ py_library( "//tensorflow/contrib/distribute/python:tpu_strategy", "//tensorflow/python:training", "//tensorflow/python:util", + "//tensorflow/python/distribute:cross_device_ops", "//tensorflow/python/distribute:distribute_config", "//tensorflow/python/distribute:distribute_coordinator", ], diff --git a/tensorflow/contrib/distribute/README.md b/tensorflow/contrib/distribute/README.md index a938f8629d..81574a2047 100644 --- a/tensorflow/contrib/distribute/README.md +++ b/tensorflow/contrib/distribute/README.md @@ -134,7 +134,7 @@ def model_fn(features, labels, mode): return tf.estimator.EstimatorSpec(mode, loss=loss) if mode == tf.estimator.ModeKeys.TRAIN: - train_op = tf.train.GradientDescentOptimizer(0.2).minimize(loss_fn()) + train_op = tf.train.GradientDescentOptimizer(0.2).minimize(loss) return tf.estimator.EstimatorSpec(mode, loss=loss, train_op=train_op) ``` @@ -251,10 +251,10 @@ start multi-worker training using `tf.estimator.train_and_evaluate`: ```python def model_main(): - estimator = ... distribution = tf.contrib.distribute.CollectiveAllReduceStrategy( num_gpus_per_worker=2) config = tf.estimator.RunConfig(train_distribute=distribution) + estimator = tf.estimator.Estimator(model_fn=model_fn, config=config) train_spec = tf.estimator.TrainSpec(input_fn=input_fn) eval_spec = tf.estimator.EvalSpec(input_fn=eval_input_fn) tf.estimator.train_and_evaluate(estimator, train_spec, eval_spec) @@ -327,13 +327,13 @@ start training. On your laptop, you can run ```python -estimator = ... distribution = tf.contrib.distribute.CollectiveAllReduceStrategy( num_gpus_per_worker=2) config = tf.estimator.RunConfig( experimental_distribute=tf.contrib.distribute.DistributeConfig( train_distribute=distribution, remote_cluster={"worker": ["host1:port", "host2:port", "host3:port"]})) +estimator = tf.estimator.Estimator(model_fn=model_fn, config=config) train_spec = tf.estimator.TrainSpec(input_fn=input_fn) eval_spec = tf.estimator.EvalSpec(input_fn=eval_input_fn) tf.estimator.train_and_evaluate(estimator, train_spec, eval_spec) diff --git a/tensorflow/contrib/distribute/__init__.py b/tensorflow/contrib/distribute/__init__.py index ab2f221dc6..8ec73654e3 100644 --- a/tensorflow/contrib/distribute/__init__.py +++ b/tensorflow/contrib/distribute/__init__.py @@ -25,13 +25,13 @@ from __future__ import print_function # pylint: disable=unused-import,wildcard-import from tensorflow.contrib.distribute.python.collective_all_reduce_strategy import CollectiveAllReduceStrategy -from tensorflow.contrib.distribute.python.cross_tower_ops import * from tensorflow.contrib.distribute.python.mirrored_strategy import MirroredStrategy from tensorflow.contrib.distribute.python.monitor import Monitor from tensorflow.contrib.distribute.python.one_device_strategy import OneDeviceStrategy from tensorflow.contrib.distribute.python.parameter_server_strategy import ParameterServerStrategy from tensorflow.contrib.distribute.python.step_fn import * from tensorflow.contrib.distribute.python.tpu_strategy import TPUStrategy +from tensorflow.python.distribute.cross_device_ops import * from tensorflow.python.distribute.distribute_config import DistributeConfig from tensorflow.python.distribute.distribute_coordinator import run_standard_tensorflow_server from tensorflow.python.training.distribute import * @@ -46,6 +46,7 @@ _allowed_symbols = [ 'CrossDeviceOps', 'DistributeConfig', 'DistributionStrategy', + 'DistributionStrategyExtended', 'MirroredStrategy', 'Monitor', 'MultiWorkerAllReduce', @@ -62,6 +63,7 @@ _allowed_symbols = [ 'get_loss_reduction', 'get_replica_context', 'has_distribution_strategy', + 'in_cross_replica_context', 'require_replica_context', 'run_standard_tensorflow_server', 'UpdateContext', diff --git a/tensorflow/contrib/distribute/python/BUILD b/tensorflow/contrib/distribute/python/BUILD index 4094e52169..4c9c35da5a 100644 --- a/tensorflow/contrib/distribute/python/BUILD +++ b/tensorflow/contrib/distribute/python/BUILD @@ -16,45 +16,26 @@ load("//tensorflow:tensorflow.bzl", "cuda_py_test") # TODO(priyag): Figure out testonly issues that are preventing us from # including our tests in pip for now. -py_library( - name = "values", - srcs = ["values.py"], - visibility = ["//tensorflow:internal"], - deps = [ - ":input_ops", - "//tensorflow/python:array_ops", - "//tensorflow/python:control_flow_ops", - "//tensorflow/python:device_util", - "//tensorflow/python:distribute", - "//tensorflow/python:framework_ops", - "//tensorflow/python:resource_variable_ops", - "//tensorflow/python:training", - "//tensorflow/python:util", - "//tensorflow/python/data/ops:multi_device_iterator_ops", - "//tensorflow/python/eager:context", - "//tensorflow/python/training/checkpointable:base", - "@six_archive//:six", - ], -) - cuda_py_test( name = "values_test", srcs = ["values_test.py"], additional_deps = [ + ":combinations", ":mirrored_strategy", ":multi_worker_test_base", - ":values", + "@absl_py//absl/testing:parameterized", "//tensorflow/core:protos_all_py", - "//tensorflow/python/data/ops:dataset_ops", - "//tensorflow/python:errors", "//tensorflow/python:array_ops", "//tensorflow/python:constant_op", + "//tensorflow/python:errors", "//tensorflow/python:framework_ops", "//tensorflow/python:framework_test_lib", "//tensorflow/python:training", "//tensorflow/python:variable_scope", + "//tensorflow/python/data/ops:dataset_ops", + "//tensorflow/python/distribute:device_util", + "//tensorflow/python/distribute:values", "//tensorflow/python/eager:context", - "//tensorflow/python:device_util", "//tensorflow/python/eager:test", "//tensorflow/python/estimator:estimator_py", ], @@ -68,25 +49,9 @@ py_library( srcs = ["mirrored_strategy.py"], visibility = ["//tensorflow:internal"], deps = [ - ":cross_tower_ops", - ":shared_variable_creator", - ":values", - "//tensorflow/core:protos_all_py", - "//tensorflow/python:array_ops", - "//tensorflow/python:constant_op", - "//tensorflow/python:control_flow_ops", - "//tensorflow/python:device", - "//tensorflow/python:device_util", - "//tensorflow/python:distribute", - "//tensorflow/python:framework_ops", - "//tensorflow/python:pywrap_tensorflow", - "//tensorflow/python:training", - "//tensorflow/python:util", - "//tensorflow/python:variable_scope", - "//tensorflow/python:variables", - "//tensorflow/python/distribute:multi_worker_util", - "//tensorflow/python/eager:context", - "//tensorflow/python/eager:tape", + "//tensorflow/python/distribute:distribute_lib", + "//tensorflow/python/distribute:mirrored_strategy", + "//tensorflow/python/distribute:values", ], ) @@ -95,16 +60,17 @@ py_library( srcs = ["parameter_server_strategy.py"], visibility = ["//tensorflow:internal"], deps = [ - ":cross_tower_ops", ":mirrored_strategy", - ":values", "//tensorflow/core:protos_all_py", "//tensorflow/python:array_ops", "//tensorflow/python:framework_ops", "//tensorflow/python:resource_variable_ops", "//tensorflow/python:training", "//tensorflow/python:util", + "//tensorflow/python/distribute:cross_device_ops", "//tensorflow/python/distribute:multi_worker_util", + "//tensorflow/python/distribute:reduce_util", + "//tensorflow/python/distribute:values", "//tensorflow/python/eager:context", ], ) @@ -116,7 +82,7 @@ cuda_py_test( ":combinations", ":multi_worker_test_base", ":parameter_server_strategy", - ":values", + ":strategy_test_lib", "@absl_py//absl/testing:parameterized", "//tensorflow/core:protos_all_py", "//tensorflow/python:array_ops", @@ -127,10 +93,12 @@ cuda_py_test( "//tensorflow/python:gradients", "//tensorflow/python:layers", "//tensorflow/python:session", + "//tensorflow/python:tensor_util", "//tensorflow/python:training", "//tensorflow/python:variable_scope", "//tensorflow/python:variables", "//tensorflow/python/distribute:multi_worker_util", + "//tensorflow/python/distribute:values", "//tensorflow/python/eager:context", "//tensorflow/python/estimator:estimator_py", ], @@ -145,12 +113,13 @@ py_library( srcs = ["one_device_strategy.py"], visibility = ["//tensorflow:internal"], deps = [ - ":values", - "//tensorflow/contrib/eager/python:datasets", "//tensorflow/python:array_ops", - "//tensorflow/python:distribute", + "//tensorflow/python:dtypes", "//tensorflow/python:framework_ops", "//tensorflow/python:math_ops", + "//tensorflow/python/distribute:distribute_lib", + "//tensorflow/python/distribute:reduce_util", + "//tensorflow/python/distribute:values", "//tensorflow/python/eager:context", "@six_archive//:six", ], @@ -161,16 +130,16 @@ py_library( srcs = ["collective_all_reduce_strategy.py"], visibility = ["//tensorflow:internal"], deps = [ - ":cross_tower_ops", - ":cross_tower_utils", ":mirrored_strategy", - ":values", "//tensorflow/core:protos_all_py", "//tensorflow/python:array_ops", "//tensorflow/python:collective_ops", "//tensorflow/python:framework_ops", "//tensorflow/python:training", + "//tensorflow/python/distribute:cross_device_ops", + "//tensorflow/python/distribute:cross_device_utils", "//tensorflow/python/distribute:multi_worker_util", + "//tensorflow/python/distribute:values", "//tensorflow/python/eager:context", ], ) @@ -187,11 +156,11 @@ py_library( "//tensorflow/core:protos_all_py", "//tensorflow/python:array_ops", "//tensorflow/python:constant_op", - "//tensorflow/python:distribute", "//tensorflow/python:framework_ops", "//tensorflow/python:layers", "//tensorflow/python:training", "//tensorflow/python:variables", + "//tensorflow/python/distribute:distribute_lib", "//tensorflow/python/eager:backprop", "//tensorflow/python/eager:context", "//tensorflow/python/eager:test", @@ -212,10 +181,10 @@ py_library( ":tpu_strategy", "//tensorflow/contrib/cluster_resolver:cluster_resolver_pip", "//tensorflow/contrib/optimizer_v2:training", - "//tensorflow/python:distribute", "//tensorflow/python:framework_ops", "//tensorflow/python:training", "//tensorflow/python:util", + "//tensorflow/python/distribute:distribute_lib", "//tensorflow/python/eager:context", "@absl_py//absl/testing:parameterized", ], @@ -233,28 +202,6 @@ py_test( ], ) -py_test( - name = "mirrored_strategy_test", - srcs = ["mirrored_strategy_test.py"], - srcs_version = "PY2AND3", - tags = [ - "no_pip", - ], - deps = [ - ":mirrored_strategy", - ":multi_worker_test_base", - ":strategy_test_lib", - "//tensorflow/python:constant_op", - "//tensorflow/python:distribute", - "//tensorflow/python:framework_ops", - "//tensorflow/python:framework_test_lib", - "//tensorflow/python:training", - "//tensorflow/python:variable_scope", - "//tensorflow/python/eager:context", - "//tensorflow/python/eager:test", - ], -) - py_test( name = "one_device_strategy_test", srcs = ["one_device_strategy_test.py"], @@ -270,35 +217,32 @@ py_test( ], ) +# TODO(priyag): Rename this test to mirrored_strategy_test cuda_py_test( name = "mirrored_strategy_multigpu_test", srcs = ["mirrored_strategy_multigpu_test.py"], additional_deps = [ + ":combinations", ":mirrored_strategy", ":multi_worker_test_base", - ":values", ":strategy_test_lib", - "//tensorflow/python:distribute", "//tensorflow/core:protos_all_py", "//tensorflow/python:array_ops", "//tensorflow/python:constant_op", + "//tensorflow/python:framework_test_lib", "//tensorflow/python:layers", "//tensorflow/python:state_ops", "//tensorflow/python:variable_scope", - "//tensorflow/python:framework_test_lib", + "//tensorflow/python/distribute:distribute_lib", + "//tensorflow/python/distribute:values", "//tensorflow/python/eager:context", "//tensorflow/python/eager:test", ], + shard_count = 5, tags = [ "guitar", - "no_pip", "multi_and_single_gpu", - # Do not perform the extra analysis on this test, because it is already - # performed for the `:mirrored_strategy_test` target. - "no_oss", - "noasan", - "notap", - "notsan", + "no_pip", ], ) @@ -337,12 +281,15 @@ py_library( visibility = ["//tensorflow:internal"], deps = [ ":one_device_strategy", - ":values", "//tensorflow/contrib/tpu:tpu_lib", "//tensorflow/python:constant_op", "//tensorflow/python:control_flow_ops", + "//tensorflow/python:dtypes", "//tensorflow/python:framework_ops", + "//tensorflow/python:tensor_util", "//tensorflow/python:util", + "//tensorflow/python/distribute:reduce_util", + "//tensorflow/python/distribute:values", ], ) @@ -352,7 +299,6 @@ cuda_py_test( additional_deps = [ ":collective_all_reduce_strategy", ":combinations", - ":cross_tower_utils", ":multi_worker_test_base", ":strategy_test_lib", "@absl_py//absl/testing:parameterized", @@ -368,6 +314,7 @@ cuda_py_test( "//tensorflow/python:layers", "//tensorflow/python:variable_scope", "//tensorflow/python:variables", + "//tensorflow/python/distribute:cross_device_utils", "//tensorflow/python/eager:context", "//tensorflow/python/estimator:estimator_py", ], @@ -469,6 +416,7 @@ cuda_py_test( "multi_and_single_gpu", "no_oss", # http://b/119349471 "no_pip", + "tf_integration_test", ], ) @@ -476,28 +424,18 @@ cuda_py_test( name = "keras_optimizer_v2_test", srcs = ["keras_optimizer_v2_test.py"], additional_deps = [ - ":combinations", - "@absl_py//absl/testing:parameterized", - "//third_party/py/numpy", - "//tensorflow/contrib/optimizer_v2:training", - "//tensorflow/python/data/ops:dataset_ops", - "//tensorflow/python/eager:test", - "//tensorflow/python/estimator:estimator_py", - "//tensorflow/python/feature_column", - "//tensorflow/python:framework_ops", - "//tensorflow/python:platform", - "//tensorflow/python:summary", + ":keras_test_lib", ], tags = [ "multi_and_single_gpu", "no_oss", # http://b/119349471 "no_pip", + "tf_integration_test", ], ) cuda_py_test( name = "estimator_training_test", - size = "large", srcs = ["estimator_training_test.py"], additional_deps = [ ":collective_all_reduce_strategy", @@ -508,7 +446,9 @@ cuda_py_test( "//third_party/py/numpy", "//tensorflow/contrib/optimizer_v2:training", "//tensorflow/python/data/ops:dataset_ops", - "//tensorflow/python/distribute", + "//tensorflow/python/distribute:distribute_config", + "//tensorflow/python/distribute:distribute_coordinator", + "//tensorflow/python/distribute:distribute_coordinator_context", "//tensorflow/python/eager:test", "//tensorflow/python/estimator:estimator_py", "//tensorflow/python/feature_column", @@ -516,7 +456,7 @@ cuda_py_test( "//tensorflow/python:platform", "//tensorflow/python:summary", ], - shard_count = 5, + shard_count = 48, tags = [ "multi_and_single_gpu", "no_pip", @@ -524,6 +464,7 @@ cuda_py_test( "noasan", "nomsan", "notsan", + "no_oss", # http://b/119349471 ], ) @@ -599,52 +540,16 @@ cuda_py_test( ], ) -py_library( - name = "shared_variable_creator", - srcs = ["shared_variable_creator.py"], - visibility = ["//tensorflow:internal"], -) - -py_test( - name = "shared_variable_creator_test", - srcs = ["shared_variable_creator_test.py"], - srcs_version = "PY2AND3", - deps = [ - ":shared_variable_creator", - "//tensorflow/python:framework_test_lib", - "//tensorflow/python:variable_scope", - "//tensorflow/python/eager:test", - ], -) - -py_library( - name = "cross_tower_utils", - srcs = ["cross_tower_utils.py"], - srcs_version = "PY2AND3", - deps = [ - ":values", - "//tensorflow/contrib/all_reduce:all_reduce_py", - "//tensorflow/python:array_ops", - "//tensorflow/python:collective_ops", - "//tensorflow/python:device", - "//tensorflow/python:dtypes", - "//tensorflow/python:framework_ops", - "//tensorflow/python:gradients", - "//tensorflow/python:math_ops", - "//tensorflow/python:nccl_ops", - ], -) - cuda_py_test( - name = "cross_tower_utils_test", - srcs = ["cross_tower_utils_test.py"], + name = "cross_device_utils_test", + srcs = ["cross_device_utils_test.py"], additional_deps = [ ":combinations", - ":cross_tower_utils", "@absl_py//absl/testing:parameterized", "//tensorflow/python:constant_op", "//tensorflow/python:framework_ops", "//tensorflow/python:math_ops", + "//tensorflow/python/distribute:cross_device_utils", "//tensorflow/python/eager:context", "//tensorflow/python/eager:test", ], @@ -653,40 +558,20 @@ cuda_py_test( ], ) -py_library( - name = "cross_tower_ops", - srcs = ["cross_tower_ops.py"], - srcs_version = "PY2AND3", - deps = [ - ":cross_tower_utils", - ":values", - "//tensorflow/python:array_ops", - "//tensorflow/python:device_lib", - "//tensorflow/python:framework_ops", - "//tensorflow/python:math_ops", - "//tensorflow/python:platform", - "//tensorflow/python:resource_variable_ops", - "//tensorflow/python:training", - "//tensorflow/python:variable_scope", - "//tensorflow/python/eager:context", - "@six_archive//:six", - ], -) - cuda_py_test( - name = "cross_tower_ops_test", - srcs = ["cross_tower_ops_test.py"], + name = "cross_device_ops_test", + srcs = ["cross_device_ops_test.py"], additional_deps = [ ":combinations", - ":cross_tower_ops", ":multi_worker_test_base", ":mirrored_strategy", - ":values", "@absl_py//absl/testing:parameterized", "//tensorflow/python:array_ops", "//tensorflow/python:constant_op", "//tensorflow/python:framework_ops", "//tensorflow/python:math_ops", + "//tensorflow/python/distribute:cross_device_ops", + "//tensorflow/python/distribute:values", "//tensorflow/python/eager:context", "//tensorflow/python/eager:test", ], @@ -696,37 +581,6 @@ cuda_py_test( ], ) -py_library( - name = "input_ops", - srcs = ["input_ops.py"], - visibility = ["//tensorflow:internal"], - deps = [ - "//tensorflow/python:framework_ops", - "//tensorflow/python/data/util:nest", - ], -) - -cuda_py_test( - name = "input_ops_test", - srcs = ["input_ops_test.py"], - additional_deps = [ - ":input_ops", - "//tensorflow/python/data/ops:dataset_ops", - "//tensorflow/contrib/data/python/ops:batching", - "//tensorflow/contrib/data/python/ops:interleave_ops", - "//tensorflow/python:errors", - "//tensorflow/python:client_testlib", - "//tensorflow/python:framework_ops", - "//tensorflow/python:framework_test_lib", - "//tensorflow/python:io_ops", - "//tensorflow/python/data/ops:readers", - "//tensorflow/python:util", - ], - tags = [ - "no_pip", - ], -) - py_library( name = "keras_test_lib", testonly = 1, @@ -737,6 +591,7 @@ py_library( "//tensorflow/contrib/distribute/python:tpu_strategy", "//tensorflow/python:client_testlib", "//tensorflow/python:training", + "//tensorflow/python/eager:test", "//tensorflow/python/estimator:estimator_py", "//tensorflow/python/keras", "//third_party/py/numpy", @@ -766,7 +621,6 @@ py_library( srcs = ["metrics_v1_test.py"], deps = [ ":combinations", - "//tensorflow/contrib/data/python/ops:batching", "//tensorflow/python:math_ops", "//tensorflow/python:metrics", "//tensorflow/python:variables", diff --git a/tensorflow/contrib/distribute/python/checkpoint_utils_test.py b/tensorflow/contrib/distribute/python/checkpoint_utils_test.py index d38bdb592a..31bd0e996a 100644 --- a/tensorflow/contrib/distribute/python/checkpoint_utils_test.py +++ b/tensorflow/contrib/distribute/python/checkpoint_utils_test.py @@ -43,7 +43,9 @@ class CheckpointUtilsWithDistributionStrategyTest( distribution=[combinations.default_strategy, combinations.one_device_strategy, combinations.mirrored_strategy_with_gpu_and_cpu, - combinations.mirrored_strategy_with_two_gpus], + combinations.mirrored_strategy_with_two_gpus, + combinations.core_mirrored_strategy_with_gpu_and_cpu, + combinations.core_mirrored_strategy_with_two_gpus], in_replica_mode=[True, False], mode=["graph"])) def testInitFromCheckpoint(self, distribution, in_replica_mode): diff --git a/tensorflow/contrib/distribute/python/collective_all_reduce_strategy.py b/tensorflow/contrib/distribute/python/collective_all_reduce_strategy.py index efa99d1fc5..e988b63a28 100644 --- a/tensorflow/contrib/distribute/python/collective_all_reduce_strategy.py +++ b/tensorflow/contrib/distribute/python/collective_all_reduce_strategy.py @@ -18,12 +18,16 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -from tensorflow.contrib.distribute.python import cross_tower_ops as cross_tower_ops_lib -from tensorflow.contrib.distribute.python import cross_tower_utils +import copy + from tensorflow.contrib.distribute.python import mirrored_strategy -from tensorflow.contrib.distribute.python import values from tensorflow.core.protobuf import rewriter_config_pb2 +from tensorflow.python.distribute import cross_device_ops as cross_device_ops_lib +from tensorflow.python.distribute import cross_device_utils +from tensorflow.python.distribute import device_util +from tensorflow.python.distribute import distribute_lib from tensorflow.python.distribute import multi_worker_util +from tensorflow.python.distribute import values from tensorflow.python.eager import context from tensorflow.python.framework import ops from tensorflow.python.ops import array_ops @@ -32,7 +36,7 @@ from tensorflow.python.platform import tf_logging as logging # TODO(yuefengz): support in-graph replication. -class CollectiveAllReduceStrategy(mirrored_strategy.MirroredStrategy): +class CollectiveAllReduceStrategy(distribute_lib.DistributionStrategy): """Distribution strategy that uses collective ops for all-reduce. It is similar to the MirroredStrategy but it uses collective ops for @@ -53,6 +57,17 @@ class CollectiveAllReduceStrategy(mirrored_strategy.MirroredStrategy): num_gpus_per_worker: number of local GPUs or GPUs per worker, the default is 0 meaning CPU only. """ + super(CollectiveAllReduceStrategy, self).__init__( + CollectiveAllReduceExtended(self, num_gpus_per_worker)) + + +class CollectiveAllReduceExtended(mirrored_strategy.MirroredExtended): + """Implementation of CollectiveAllReduceStrategy.""" + + def __init__(self, container_strategy, num_gpus_per_worker): + distribute_lib.DistributionStrategyExtended.__init__( + self, container_strategy) + self._cross_device_ops = None self._num_gpus_per_worker = num_gpus_per_worker self._initialize_local_worker(num_gpus_per_worker) @@ -67,14 +82,14 @@ class CollectiveAllReduceStrategy(mirrored_strategy.MirroredStrategy): ] else: local_devices = ["/device:CPU:0"] + self._worker_device = device_util.canonicalize("/device:CPU:0") - self._collective_keys = cross_tower_utils.CollectiveKeys() - super(CollectiveAllReduceStrategy, self).__init__( - devices=local_devices, - cross_tower_ops=cross_tower_ops_lib.CollectiveAllReduce( - num_workers=1, - num_gpus_per_worker=num_gpus_per_worker, - collective_keys=self._collective_keys)) + self._collective_keys = cross_device_utils.CollectiveKeys() + self._initialize_local(local_devices) + self._cross_tower_ops = cross_device_ops_lib.CollectiveAllReduce( + num_workers=self._num_workers, + num_gpus_per_worker=num_gpus_per_worker, + collective_keys=self._collective_keys) self._cluster_spec = None self._task_type = None @@ -94,8 +109,7 @@ class CollectiveAllReduceStrategy(mirrored_strategy.MirroredStrategy): "Unrecognized task_type: %r, valid task types are: \"chief\", " "\"worker\"." % task_type) cluster_spec = multi_worker_util.normalize_cluster_spec(cluster_spec) - self._num_workers = len(cluster_spec.as_dict().get("worker", [])) + len( - cluster_spec.as_dict().get("chief", [])) + self._num_workers = multi_worker_util.worker_count(cluster_spec, task_type) if not self._num_workers: raise ValueError("No `worker` or `chief` tasks can be found in " "`cluster_spec`.") @@ -103,22 +117,21 @@ class CollectiveAllReduceStrategy(mirrored_strategy.MirroredStrategy): self._is_chief = multi_worker_util.is_chief(cluster_spec, task_type, task_id) - worker_device = "/job:%s/task:%d" % (task_type, task_id) + self._worker_device = "/job:%s/task:%d" % (task_type, task_id) if num_gpus_per_worker: local_devices = [ - "%s/device:GPU:%d" % (worker_device, i) + "%s/device:GPU:%d" % (self._worker_device, i) for i in range(num_gpus_per_worker) ] else: - local_devices = [worker_device] + local_devices = [self._worker_device] - self._collective_keys = cross_tower_utils.CollectiveKeys() - super(CollectiveAllReduceStrategy, self).__init__( - devices=local_devices, - cross_tower_ops=cross_tower_ops_lib.CollectiveAllReduce( - num_workers=self._num_workers, - num_gpus_per_worker=num_gpus_per_worker, - collective_keys=self._collective_keys)) + self._collective_keys = cross_device_utils.CollectiveKeys() + self._initialize_local(local_devices) + self._cross_tower_ops = cross_device_ops_lib.CollectiveAllReduce( + num_workers=self._num_workers, + num_gpus_per_worker=num_gpus_per_worker, + collective_keys=self._collective_keys) # Add a default device so that ops without specified devices will not end up # on other workers. @@ -202,17 +215,40 @@ class CollectiveAllReduceStrategy(mirrored_strategy.MirroredStrategy): return mirrored_strategy._create_mirrored_variable( devices, _real_mirrored_creator, *args, **kwargs) - def distribute_dataset(self, dataset_fn): + def _distribute_dataset(self, dataset_fn): """Distributes the dataset to each local GPU.""" # TODO(yuefengz): shard the dataset. return values.PerReplicaDataset( self._call_dataset_fn(dataset_fn), self._devices, True) - def configure(self, - session_config=None, - cluster_spec=None, - task_type=None, - task_id=None): + def _make_dataset_iterator(self, dataset): + worker_device_pairs = [(self._worker_device, self._devices)] + return values.DatasetIterator(dataset, worker_device_pairs, + self._num_replicas_in_sync) + + def _make_input_fn_iterator( + self, + input_fn, + replication_mode=distribute_lib.InputReplicationMode.PER_WORKER): + """Distributes the dataset to each local GPU.""" + if self._cluster_spec is None: + input_pipeline_id = 0 + else: + input_pipeline_id = multi_worker_util.id_in_cluster( + self._cluster_spec, self._task_type, self._task_id) + input_context = distribute_lib.InputContext( + num_input_pipelines=self._num_workers, + input_pipeline_id=input_pipeline_id, + num_replicas_in_sync=self._num_replicas_in_sync) + + return values.InputFunctionIterator( + input_fn, [(self._worker_device, self._devices)], [input_context]) + + def _configure(self, + session_config=None, + cluster_spec=None, + task_type=None, + task_id=None): """Configures the object. Args: @@ -232,13 +268,15 @@ class CollectiveAllReduceStrategy(mirrored_strategy.MirroredStrategy): self._initialize_multi_worker(self._num_gpus_per_worker, cluster_spec, task_type, task_id) - if not session_config: - return + if session_config: + session_config.CopyFrom(self._update_config_proto(session_config)) + def _update_config_proto(self, config_proto): + updated_config = copy.deepcopy(config_proto) # Enable the scoped allocator optimization for CollectiveOps. This # optimization converts many small all-reduces into fewer larger # all-reduces. - rewrite_options = session_config.graph_options.rewrite_options + rewrite_options = updated_config.graph_options.rewrite_options rewrite_options.scoped_allocator_optimization = ( rewriter_config_pb2.RewriterConfig.ON) # We turn on ScopedAllocator only for CollectiveReduce op, i.e. enable_op = @@ -248,7 +286,7 @@ class CollectiveAllReduceStrategy(mirrored_strategy.MirroredStrategy): rewrite_options.scoped_allocator_opts.enable_op.append("CollectiveReduce") if not self._cluster_spec: - return + return updated_config assert self._task_type assert self._task_id is not None @@ -256,26 +294,28 @@ class CollectiveAllReduceStrategy(mirrored_strategy.MirroredStrategy): # Collective group leader is needed for collective ops to coordinate # workers. if "chief" in self._cluster_spec.jobs: - session_config.experimental.collective_group_leader = ( + updated_config.experimental.collective_group_leader = ( "/job:chief/replica:0/task:0") else: if "worker" not in self._cluster_spec.jobs: raise ValueError( "You must have `chief` or `worker` jobs in the `cluster_spec`.") - session_config.experimental.collective_group_leader = ( + updated_config.experimental.collective_group_leader = ( "/job:worker/replica:0/task:0") # The device filters prevent communication between workers. - del session_config.device_filters[:] - session_config.device_filters.append( + del updated_config.device_filters[:] + updated_config.device_filters.append( "/job:%s/task:%d" % (self._task_type, self._task_id)) + return updated_config + @property - def between_graph(self): + def experimental_between_graph(self): return True @property - def should_init(self): + def experimental_should_init(self): return True @property @@ -287,6 +327,10 @@ class CollectiveAllReduceStrategy(mirrored_strategy.MirroredStrategy): return self._is_chief @property - def num_replicas_in_sync(self): + def _num_replicas_in_sync(self): return len(self._devices) * self._num_workers + # TODO(priyag): Delete this once all strategies use global batch size. + @property + def _global_batch_size(self): + return False diff --git a/tensorflow/contrib/distribute/python/collective_all_reduce_strategy_test.py b/tensorflow/contrib/distribute/python/collective_all_reduce_strategy_test.py index e3d919dd0d..8a9e583f0a 100644 --- a/tensorflow/contrib/distribute/python/collective_all_reduce_strategy_test.py +++ b/tensorflow/contrib/distribute/python/collective_all_reduce_strategy_test.py @@ -23,13 +23,19 @@ import numpy as np from tensorflow.contrib.distribute.python import collective_all_reduce_strategy from tensorflow.contrib.distribute.python import combinations -from tensorflow.contrib.distribute.python import cross_tower_utils from tensorflow.contrib.distribute.python import multi_worker_test_base +from tensorflow.contrib.distribute.python import strategy_test_lib from tensorflow.core.protobuf import config_pb2 +from tensorflow.core.protobuf import rewriter_config_pb2 from tensorflow.python import keras +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.distribute import cross_device_utils +from tensorflow.python.distribute import reduce_util +from tensorflow.python.distribute import values from tensorflow.python.eager import context from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import errors from tensorflow.python.framework import ops from tensorflow.python.layers import core from tensorflow.python.ops import array_ops @@ -51,9 +57,6 @@ class CollectiveAllReduceStrategyTestBase( collective_key_base = 0 def setUp(self): - self._run_options = config_pb2.RunOptions() - self._run_options.experimental.collective_graph_key = 6 - # We use a different key_base for each test so that collective keys won't be # reused. # TODO(yuefengz, tucker): enable it to reuse collective keys in different @@ -71,15 +74,16 @@ class CollectiveAllReduceStrategyTestBase( cluster_spec=self._cluster_spec, task_type=task_type, task_id=task_id) - collective_keys = cross_tower_utils.CollectiveKeys( + collective_keys = cross_device_utils.CollectiveKeys( group_key_start=10 * num_gpus + CollectiveAllReduceStrategyTestBase.collective_key_base, instance_key_start=num_gpus * 100 + CollectiveAllReduceStrategyTestBase.collective_key_base, instance_key_with_id_start=num_gpus * 10000 + CollectiveAllReduceStrategyTestBase.collective_key_base) - distribution._collective_keys = collective_keys - distribution._cross_tower_ops._collective_keys = collective_keys + distribution.extended._collective_keys = collective_keys + distribution.extended._inferred_cross_device_ops._collective_keys = ( + collective_keys) if task_type and task_id is not None: return distribution, 'grpc://' + self._cluster_spec[task_type][ task_id], session_config @@ -93,7 +97,8 @@ class CollectiveAllReduceStrategyTestBase( self.cached_session(config=config, target=master_target) as sess, \ d.scope(): - l = core.Dense(1, use_bias=False, name='gpu_%d' % d._num_gpus_per_worker) + l = core.Dense(1, use_bias=False, + name='gpu_%d' % d.extended._num_gpus_per_worker) def loss_fn(x): y = array_ops.reshape(l(x), []) - constant_op.constant(1.) @@ -127,8 +132,8 @@ class CollectiveAllReduceStrategyTestBase( before_list.append(fetched) with ops.control_dependencies([fetched]): # TODO(yuefengz): support non-Mirrored variable as destinations. - g = d.reduce( - variable_scope.VariableAggregation.SUM, g, destinations=v) + g = d.extended.reduce_to( + reduce_util.ReduceOp.SUM, g, destinations=v) with ops.control_dependencies( d.update(v, update, g, grouped=False)): after_list.append(d.read_var(v)) @@ -136,14 +141,13 @@ class CollectiveAllReduceStrategyTestBase( before_out, after_out = step() - if context.num_gpus() < d._num_gpus_per_worker: + if context.num_gpus() < d.extended._num_gpus_per_worker: return True - sess.run( - variables.global_variables_initializer(), options=self._run_options) + sess.run(variables.global_variables_initializer()) for i in range(10): - b, a = sess.run((before_out, after_out), options=self._run_options) + b, a = sess.run((before_out, after_out)) if i == 0: before, = b after, = a @@ -222,26 +226,54 @@ class CollectiveAllReduceStrategyTestBase( return array_ops.identity(x) x = distribution.call_for_each_replica(model_fn) - reduced_x = distribution.unwrap( - distribution.reduce( - variable_scope.VariableAggregation.MEAN, x, - destinations='/cpu:0'))[0] + reduced_x = distribution.reduce(reduce_util.ReduceOp.MEAN, x) x = distribution.unwrap(x)[0] - sess.run( - variables.global_variables_initializer(), options=self._run_options) + sess.run(variables.global_variables_initializer()) - x_value, reduced_x_value = sess.run([x, reduced_x], - options=self._run_options) + x_value, reduced_x_value = sess.run([x, reduced_x]) self.assertTrue( np.allclose(x_value, reduced_x_value, atol=1e-5), msg=('x_value = %r, reduced_x_value = %r' % (x_value, reduced_x_value))) return np.allclose(x_value, reduced_x_value, atol=1e-5) + def _test_input_fn_iterator(self, task_type, task_id, num_gpus, input_fn, + expected_values): + distribution, master_target, config = self._get_test_object( + task_type, task_id, num_gpus) + devices = distribution.extended.worker_devices + + with ops.Graph().as_default(), \ + self.cached_session(config=config, + target=master_target) as sess: + iterator = distribution.make_input_fn_iterator(input_fn) + sess.run(iterator.initialize()) + + for expected_value in expected_values: + next_element = iterator.get_next() + computed_value = sess.run( + [values.select_device(d, next_element) for d in devices]) + self.assertEqual(expected_value, computed_value) + + with self.assertRaises(errors.OutOfRangeError): + next_element = iterator.get_next() + sess.run([values.select_device(d, next_element) for d in devices]) + + # After re-initializing the iterator, should be able to iterate again. + sess.run(iterator.initialize()) + + for expected_value in expected_values: + next_element = iterator.get_next() + computed_value = sess.run( + [values.select_device(d, next_element) for d in devices]) + self.assertEqual(expected_value, computed_value) + class DistributedCollectiveAllReduceStrategyTest( - CollectiveAllReduceStrategyTestBase, parameterized.TestCase): + CollectiveAllReduceStrategyTestBase, + strategy_test_lib.DistributionTestBase, + parameterized.TestCase): @classmethod def setUpClass(cls): @@ -269,7 +301,7 @@ class DistributedCollectiveAllReduceStrategyTest( combinations.combine(mode=['graph'], num_gpus=[0, 1, 2], required_gpus=1)) def testVariableInitialization(self, num_gpus): if context.num_gpus() < num_gpus: - return + self.skipTest('Not enough GPUs') self._run_between_graph_clients( self._test_variable_initialization, self._cluster_spec, @@ -279,10 +311,56 @@ class DistributedCollectiveAllReduceStrategyTest( combinations.combine(mode=['graph'], num_gpus=[0, 1, 2], required_gpus=1)) def testComplexModel(self, num_gpus): if context.num_gpus() < num_gpus: - return + self.skipTest('Not enough GPUs') self._run_between_graph_clients( self._test_complex_model, self._cluster_spec, num_gpus=num_gpus) + # TODO(yuefengz): Update how we use num_gpus and required_gpus + @combinations.generate( + combinations.combine(mode=['graph'], num_gpus=[0, 1, 2], required_gpus=1)) + def testMakeInputFnIterator(self, num_gpus): + if context.num_gpus() < num_gpus: + self.skipTest('Not enough GPUs') + dataset_fn = lambda: dataset_ops.Dataset.range(100) + # We use CPU as the device when num_gpus = 0 + devices_per_worker = max(1, num_gpus) + expected_values = [[i+j for j in range(devices_per_worker)] + for i in range(0, 100, devices_per_worker)] + + input_fn = self._input_fn_to_test_input_context( + dataset_fn, + expected_num_replicas_in_sync=3*devices_per_worker, + expected_num_input_pipelines=3, + expected_input_pipeline_id=1) # because task_id = 1 + self._test_input_fn_iterator('worker', 1, num_gpus, + input_fn, expected_values) + + def testUpdateConfigProto(self): + distribution = collective_all_reduce_strategy.CollectiveAllReduceStrategy( + num_gpus_per_worker=2) + distribution.configure( + cluster_spec=self._cluster_spec, task_type='worker', task_id=1) + + config_proto = config_pb2.ConfigProto(device_filters=['to_be_overridden']) + rewrite_options = config_proto.graph_options.rewrite_options + rewrite_options.scoped_allocator_opts.enable_op.append('to_be_removed') + + new_config = distribution.update_config_proto(config_proto) + + # Verify group leader + self.assertEqual('/job:worker/replica:0/task:0', + new_config.experimental.collective_group_leader) + + # Verify device filters. + self.assertEqual(['/job:worker/task:1'], new_config.device_filters) + + # Verify rewrite options. + new_rewrite_options = new_config.graph_options.rewrite_options + self.assertEqual(rewriter_config_pb2.RewriterConfig.ON, + new_rewrite_options.scoped_allocator_optimization) + self.assertEqual(['CollectiveReduce'], + new_rewrite_options.scoped_allocator_opts.enable_op) + class DistributedCollectiveAllReduceStrategyTestWithChief( CollectiveAllReduceStrategyTestBase, parameterized.TestCase): @@ -293,10 +371,6 @@ class DistributedCollectiveAllReduceStrategyTestWithChief( cls._cluster_spec = multi_worker_test_base.create_in_process_cluster( num_workers=3, num_ps=0, has_chief=True) - def setUp(self): - super(DistributedCollectiveAllReduceStrategyTestWithChief, self).setUp() - self._run_options.experimental.collective_graph_key = 7 - @combinations.generate( combinations.combine(mode=['graph'], num_gpus=[0, 1, 2], required_gpus=1)) def testMinimizeLossGraph(self, num_gpus): @@ -323,20 +397,36 @@ class DistributedCollectiveAllReduceStrategyTestWithChief( class LocalCollectiveAllReduceStrategy(CollectiveAllReduceStrategyTestBase, + strategy_test_lib.DistributionTestBase, parameterized.TestCase): def testMinimizeLossGraph(self, num_gpus=2): # Collective ops doesn't support strategy with one device. if context.num_gpus() < num_gpus: - return + self.skipTest('Not enough GPUs') self._test_minimize_loss_graph(None, None, num_gpus) def testComplexModel(self, num_gpus=2): # Collective ops doesn't support strategy with one device. if context.num_gpus() < num_gpus: - return + self.skipTest('Not enough GPUs') self._test_complex_model(None, None, num_gpus) + def testMakeInputFnIterator(self, num_gpus=2): + # Collective ops doesn't support strategy with one device. + if context.num_gpus() < num_gpus: + self.skipTest('Not enough GPUs') + dataset_fn = lambda: dataset_ops.Dataset.range(10) + expected_values = [[i, i+1] for i in range(0, 10, 2)] + + input_fn = self._input_fn_to_test_input_context( + dataset_fn, + expected_num_replicas_in_sync=num_gpus, + expected_num_input_pipelines=1, + expected_input_pipeline_id=0) + self._test_input_fn_iterator(None, None, num_gpus, + input_fn, expected_values) + if __name__ == '__main__': test.main() diff --git a/tensorflow/contrib/distribute/python/combinations.py b/tensorflow/contrib/distribute/python/combinations.py index a513716540..365ce5cdec 100644 --- a/tensorflow/contrib/distribute/python/combinations.py +++ b/tensorflow/contrib/distribute/python/combinations.py @@ -53,11 +53,11 @@ from tensorflow.contrib.distribute.python import tpu_strategy as tpu_lib from tensorflow.contrib.optimizer_v2 import adagrad as adagrad_v2 from tensorflow.contrib.optimizer_v2 import adam as adam_v2 from tensorflow.contrib.optimizer_v2 import gradient_descent as gradient_descent_v2 +from tensorflow.python.distribute import distribution_strategy_context from tensorflow.python.eager import context from tensorflow.python.framework import ops from tensorflow.python.training import adagrad from tensorflow.python.training import adam -from tensorflow.python.training import distribution_strategy_context from tensorflow.python.training import gradient_descent from tensorflow.python.training import rmsprop from tensorflow.python.util import tf_inspect @@ -168,6 +168,8 @@ def _augment_with_special_arguments(test_method): if GPU_TEST: self.skipTest("Test that doesn't require GPUs.") elif context.num_gpus() < required_gpus: + # TODO(priyag): Consider allowing tests in graph mode using soft + # placement. self.skipTest( "{} GPUs are not available for this test. {} GPUs are available". format(required_gpus, context.num_gpus())) @@ -190,7 +192,7 @@ def _augment_with_special_arguments(test_method): kwargs_to_pass[arg] = kwargs[arg] if mode == "eager": - with ops.Graph().as_default(), context.eager_mode(): + with context.eager_mode(): if distribution: kwargs_to_pass["distribution"] = distribution.strategy test_method(**kwargs_to_pass) @@ -335,6 +337,13 @@ tpu_strategy_one_step = NamedDistribution( "TPUOneStep", lambda: tpu_lib.TPUStrategy( TPUClusterResolver(""), steps_per_run=1), required_tpu=True) +mirrored_strategy_with_one_cpu = NamedDistribution( + "Mirrored1CPU", + lambda: mirrored_lib.MirroredStrategy(["/cpu:0"])) +mirrored_strategy_with_one_gpu = NamedDistribution( + "Mirrored1GPU", + lambda: mirrored_lib.MirroredStrategy(["/gpu:0"]), + required_gpus=1) mirrored_strategy_with_gpu_and_cpu = NamedDistribution( "MirroredCPUAndGPU", lambda: mirrored_lib.MirroredStrategy(["/gpu:0", "/cpu:0"]), @@ -343,6 +352,21 @@ mirrored_strategy_with_two_gpus = NamedDistribution( "Mirrored2GPUs", lambda: mirrored_lib.MirroredStrategy(["/gpu:0", "/gpu:1"]), required_gpus=2) +core_mirrored_strategy_with_one_cpu = NamedDistribution( + "CoreMirrored1CPU", + lambda: mirrored_lib.CoreMirroredStrategy(["/cpu:0"])) +core_mirrored_strategy_with_one_gpu = NamedDistribution( + "CoreMirrored1GPU", + lambda: mirrored_lib.CoreMirroredStrategy(["/gpu:0"]), + required_gpus=1) +core_mirrored_strategy_with_gpu_and_cpu = NamedDistribution( + "CoreMirroredCPUAndGPU", + lambda: mirrored_lib.CoreMirroredStrategy(["/gpu:0", "/cpu:0"]), + required_gpus=1) +core_mirrored_strategy_with_two_gpus = NamedDistribution( + "CoreMirrored2GPUs", + lambda: mirrored_lib.CoreMirroredStrategy(["/gpu:0", "/gpu:1"]), + required_gpus=2) gradient_descent_optimizer_v1_fn = NamedObject( @@ -373,8 +397,11 @@ def distributions_and_v1_optimizers(): """A common set of combination with DistributionStrategies and Optimizers.""" return combine( distribution=[ - one_device_strategy, mirrored_strategy_with_gpu_and_cpu, - mirrored_strategy_with_two_gpus + one_device_strategy, + mirrored_strategy_with_gpu_and_cpu, + mirrored_strategy_with_two_gpus, + core_mirrored_strategy_with_gpu_and_cpu, + core_mirrored_strategy_with_two_gpus, ], optimizer_fn=optimizers_v1) @@ -383,7 +410,10 @@ def distributions_and_v2_optimizers(): """DistributionStrategies and V2 Optimizers.""" return combine( distribution=[ - one_device_strategy, mirrored_strategy_with_gpu_and_cpu, - mirrored_strategy_with_two_gpus + one_device_strategy, + mirrored_strategy_with_gpu_and_cpu, + mirrored_strategy_with_two_gpus, + core_mirrored_strategy_with_gpu_and_cpu, + core_mirrored_strategy_with_two_gpus, ], optimizer_fn=optimizers_v2) diff --git a/tensorflow/contrib/distribute/python/cross_tower_ops_test.py b/tensorflow/contrib/distribute/python/cross_device_ops_test.py similarity index 79% rename from tensorflow/contrib/distribute/python/cross_tower_ops_test.py rename to tensorflow/contrib/distribute/python/cross_device_ops_test.py index 3e274ba67c..d6e9521c1c 100644 --- a/tensorflow/contrib/distribute/python/cross_tower_ops_test.py +++ b/tensorflow/contrib/distribute/python/cross_device_ops_test.py @@ -24,24 +24,24 @@ from absl.testing import parameterized import numpy as np from tensorflow.contrib.distribute.python import combinations -from tensorflow.contrib.distribute.python import cross_tower_ops as cross_tower_ops_lib -from tensorflow.contrib.distribute.python import cross_tower_utils from tensorflow.contrib.distribute.python import mirrored_strategy from tensorflow.contrib.distribute.python import multi_worker_test_base -from tensorflow.contrib.distribute.python import values as value_lib from tensorflow.core.protobuf import config_pb2 +from tensorflow.python.distribute import cross_device_ops as cross_device_ops_lib +from tensorflow.python.distribute import cross_device_utils +from tensorflow.python.distribute import device_util +from tensorflow.python.distribute import reduce_util +from tensorflow.python.distribute import values as value_lib from tensorflow.python.eager import context from tensorflow.python.eager import test from tensorflow.python.framework import constant_op from tensorflow.python.framework import ops from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops -from tensorflow.python.ops import variable_scope as vs -from tensorflow.python.training import device_util def _make_per_replica(values, devices, regroup=False): - devices = cross_tower_ops_lib.get_devices_from(devices) + devices = cross_device_ops_lib.get_devices_from(devices) assert len(values) == len(devices) # We simulate the result of regroup called on PerReplica which strips the @@ -66,7 +66,7 @@ def _fake_mirrored(value, devices): All components of the returned Mirrored have the same objects, which is not true in reality. """ - devices = cross_tower_ops_lib.get_devices_from(devices) + devices = cross_device_ops_lib.get_devices_from(devices) return value_lib.Mirrored( {d: v for d, v in zip(devices, [value] * len(devices))}) @@ -118,8 +118,8 @@ class CrossDeviceOpsTestBase(test.TestCase, parameterized.TestCase): self.assertEqual( sess.run(list(left._index.values())), list(right._index.values())) - def _testReductionAndBroadcast(self, cross_tower_ops, distribution): - devices = distribution.worker_devices + def _testReductionAndBroadcast(self, cross_device_ops, distribution): + devices = distribution.extended.worker_devices values = [constant_op.constant(float(d)) for d in range(len(devices))] per_replica = _make_per_replica(values, devices) @@ -132,35 +132,33 @@ class CrossDeviceOpsTestBase(test.TestCase, parameterized.TestCase): destination_mirrored = _fake_mirrored(1., devices) destination_different = _fake_mirrored(1., _cpu_device) destination_str = _cpu_device - destination_list = devices all_destinations = [ destination_mirrored, destination_different, destination_str, - destination_list ] # test reduce() for destinations in all_destinations: self._assert_values_equal( - cross_tower_ops.reduce( - vs.VariableAggregation.MEAN, + cross_device_ops.reduce( + reduce_util.ReduceOp.MEAN, per_replica, destinations=destinations), _fake_mirrored(mean, destinations)) self._assert_values_equal( - cross_tower_ops.reduce( - vs.VariableAggregation.MEAN, + cross_device_ops.reduce( + reduce_util.ReduceOp.MEAN, per_replica_2, destinations=destinations), _fake_mirrored(mean_2, destinations)) self._assert_values_equal( - cross_tower_ops.reduce( - vs.VariableAggregation.SUM, per_replica, + cross_device_ops.reduce( + reduce_util.ReduceOp.SUM, per_replica, destinations=destinations), _fake_mirrored(mean * len(devices), destinations)) self._assert_values_equal( - cross_tower_ops.reduce( - vs.VariableAggregation.SUM, + cross_device_ops.reduce( + reduce_util.ReduceOp.SUM, per_replica_2, destinations=destinations), _fake_mirrored(mean_2 * len(devices), destinations)) @@ -168,16 +166,16 @@ class CrossDeviceOpsTestBase(test.TestCase, parameterized.TestCase): # test batch_reduce() for d1, d2 in itertools.product(all_destinations, all_destinations): self._assert_values_equal( - cross_tower_ops.batch_reduce( - vs.VariableAggregation.MEAN, + cross_device_ops.batch_reduce( + reduce_util.ReduceOp.MEAN, [(per_replica, d1), (per_replica_2, d2)]), [ _fake_mirrored(mean, d1), _fake_mirrored(mean_2, d2) ]) self._assert_values_equal( - cross_tower_ops.batch_reduce( - vs.VariableAggregation.SUM, + cross_device_ops.batch_reduce( + reduce_util.ReduceOp.SUM, [(per_replica, d1), (per_replica_2, d2)]), [ _fake_mirrored(mean * len(devices), d1), @@ -187,7 +185,7 @@ class CrossDeviceOpsTestBase(test.TestCase, parameterized.TestCase): # test broadcast() for destinations in all_destinations: self._assert_values_equal( - cross_tower_ops.broadcast(constant_op.constant(1.), destinations), + cross_device_ops.broadcast(constant_op.constant(1.), destinations), _fake_mirrored(1., destinations)) @@ -196,62 +194,65 @@ class SingleWorkerCrossDeviceOpsTest(CrossDeviceOpsTestBase): # combinations module so that we can pass in devices instead of a distribution # strategy. reduction_to_one_combinations = combinations.combine( - cross_tower_ops=[ + cross_device_ops=[ combinations.NamedObject( "DefaultReductionToOneDeviceCrossDeviceOps", - cross_tower_ops_lib.ReductionToOneDeviceCrossDeviceOps()), + cross_device_ops_lib.ReductionToOneDeviceCrossDeviceOps()), combinations.NamedObject( "ReductionToCPUDeviceCrossDeviceOps", - cross_tower_ops_lib.ReductionToOneDeviceCrossDeviceOps( + cross_device_ops_lib.ReductionToOneDeviceCrossDeviceOps( reduce_to_device=_cpu_device)), combinations.NamedObject( "AccumulateNCrossDeviceOp", - cross_tower_ops_lib.ReductionToOneDeviceCrossDeviceOps( + cross_device_ops_lib.ReductionToOneDeviceCrossDeviceOps( accumulation_fn=math_ops.accumulate_n)), ], distribution=[ combinations.one_device_strategy, combinations.mirrored_strategy_with_gpu_and_cpu, - combinations.mirrored_strategy_with_two_gpus + combinations.mirrored_strategy_with_two_gpus, + combinations.core_mirrored_strategy_with_gpu_and_cpu, + combinations.core_mirrored_strategy_with_two_gpus ], mode=["graph", "eager"]) allreduce_combinations = combinations.combine( - cross_tower_ops=[ + cross_device_ops=[ combinations.NamedObject( "AllReduce", - cross_tower_ops_lib.AllReduceCrossDeviceOps("nccl", 1, 0, 0)), + cross_device_ops_lib.AllReduceCrossDeviceOps("nccl", 1, 0, 0)), combinations.NamedObject( "HierarchicalCopy", - cross_tower_ops_lib.AllReduceCrossDeviceOps( + cross_device_ops_lib.AllReduceCrossDeviceOps( "hierarchical_copy", 8, 0, 0)), combinations.NamedObject( "AllReduceNoGradientRepacking", - cross_tower_ops_lib.AllReduceCrossDeviceOps("nccl", 0, 0, 0)), + cross_device_ops_lib.AllReduceCrossDeviceOps("nccl", 0, 0, 0)), combinations.NamedObject( "HierarchicalCopyAggregateSmallTensors", - cross_tower_ops_lib.AllReduceCrossDeviceOps( + cross_device_ops_lib.AllReduceCrossDeviceOps( "hierarchical_copy", 0, 100, 10)) ], - distribution=[combinations.mirrored_strategy_with_two_gpus], + distribution=[combinations.mirrored_strategy_with_two_gpus, + combinations.core_mirrored_strategy_with_two_gpus], mode=["graph", "eager"]) @combinations.generate(reduction_to_one_combinations + allreduce_combinations) - def testReductionAndBroadcast(self, cross_tower_ops, distribution): + def testReductionAndBroadcast(self, cross_device_ops, distribution): with distribution.scope(): - self._testReductionAndBroadcast(cross_tower_ops, distribution) + self._testReductionAndBroadcast(cross_device_ops, distribution) def testChooseAlgorithm(self): device_links = [[1, 2, 3, 4], [0, 2, 3, 5], [0, 1, 3, 6], [0, 1, 2, 7], [0, 5, 6, 7], [1, 4, 6, 7], [2, 4, 5, 7], [3, 4, 5, 6]] - result = cross_tower_ops_lib._choose_all_reduce_algorithm(device_links) - self.assertIsInstance(result, cross_tower_ops_lib.AllReduceCrossDeviceOps) + result = cross_device_ops_lib._choose_all_reduce_algorithm(device_links) + self.assertIsInstance(result, cross_device_ops_lib.AllReduceCrossDeviceOps) self.assertEqual(result._all_reduce_alg, "hierarchical_copy") self.assertEqual(result._num_packs, 8) # if there are only 4 devices device_links = [[1, 2, 3, 4], [0, 2, 3, 5], [0, 1, 3, 6], [0, 1, 2, 7]] - result = cross_tower_ops_lib._choose_all_reduce_algorithm(device_links) - self.assertIsInstance(result, cross_tower_ops_lib.AllReduceCrossDeviceOps) + result = cross_device_ops_lib._choose_all_reduce_algorithm(device_links) + self.assertIsInstance(result, cross_device_ops_lib.AllReduceCrossDeviceOps) self.assertEqual(result._all_reduce_alg, "nccl") self.assertEqual(result._num_packs, 1) @@ -259,16 +260,16 @@ class SingleWorkerCrossDeviceOpsTest(CrossDeviceOpsTestBase): device_links = [[0, 1, 2, 3, 4], [0, 1, 2, 3, 5], [0, 1, 2, 3, 6], [0, 1, 2, 3, 7], [0, 4, 5, 6, 7], [1, 4, 5, 6, 7], [2, 4, 5, 6, 7], [3, 4, 5, 6, 7]] - result = cross_tower_ops_lib._choose_all_reduce_algorithm(device_links) - self.assertIsInstance(result, cross_tower_ops_lib.AllReduceCrossDeviceOps) + result = cross_device_ops_lib._choose_all_reduce_algorithm(device_links) + self.assertIsInstance(result, cross_device_ops_lib.AllReduceCrossDeviceOps) self.assertEqual(result._all_reduce_alg, "hierarchical_copy") self.assertEqual(result._num_packs, 8) # if not dgx1-like links device_links = [[0, 2, 3, 5], [0, 1, 3, 6], [0, 1, 2, 7], [0, 5, 6, 7], [1, 4, 6, 7], [2, 4, 5, 7], [3, 4, 5, 6], [1, 2, 3, 4]] - result = cross_tower_ops_lib._choose_all_reduce_algorithm(device_links) - self.assertIsInstance(result, cross_tower_ops_lib.AllReduceCrossDeviceOps) + result = cross_device_ops_lib._choose_all_reduce_algorithm(device_links) + self.assertIsInstance(result, cross_device_ops_lib.AllReduceCrossDeviceOps) self.assertEqual(result._all_reduce_alg, "nccl") self.assertEqual(result._num_packs, 1) @@ -280,8 +281,8 @@ class SingleWorkerCrossDeviceOpsTest(CrossDeviceOpsTestBase): t0 = _make_indexed_slices([[1., 2.]], [1], [5, 2], devices[0]) t1 = _make_indexed_slices([[3., 4.], [5., 6.]], [1, 3], [5, 2], devices[1]) per_replica = value_lib.PerReplica({devices[0]: t0, devices[1]: t1}) - result = cross_tower_ops_lib._simple_reduce( - per_replica, devices[0], math_ops.add_n, vs.VariableAggregation.SUM) + result = cross_device_ops_lib._simple_reduce( + per_replica, devices[0], math_ops.add_n, reduce_util.ReduceOp.SUM) # Test that the result is semantically equal to both the concatenated # IndexedSlices with and without duplicate indices. @@ -294,19 +295,19 @@ class SingleWorkerCrossDeviceOpsTest(CrossDeviceOpsTestBase): @combinations.generate( combinations.combine( - cross_tower_ops_instance=[ + cross_device_ops_instance=[ combinations.NamedObject( "ReductionToOneDeviceCrossDeviceOps", - cross_tower_ops_lib.ReductionToOneDeviceCrossDeviceOps()), + cross_device_ops_lib.ReductionToOneDeviceCrossDeviceOps()), combinations.NamedObject( "AllReduceCrossDeviceOps", - cross_tower_ops_lib.AllReduceCrossDeviceOps()) + cross_device_ops_lib.AllReduceCrossDeviceOps()) ], - aggregation=[vs.VariableAggregation.SUM, vs.VariableAggregation.MEAN], + reduce_op=[reduce_util.ReduceOp.SUM, reduce_util.ReduceOp.MEAN], batch_reduce=[True, False], mode=["graph", "eager"], required_gpus=1)) - def testIndexedSlicesAllReduce(self, cross_tower_ops_instance, aggregation, + def testIndexedSlicesAllReduce(self, cross_device_ops_instance, reduce_op, batch_reduce): devices = ["/cpu:0", "/gpu:0"] dense_shape = [5, 2] @@ -316,20 +317,20 @@ class SingleWorkerCrossDeviceOpsTest(CrossDeviceOpsTestBase): per_replica = value_lib.PerReplica({devices[0]: t0, devices[1]: t1}) if batch_reduce: - result = cross_tower_ops_instance.batch_reduce( - aggregation, [(per_replica, devices)]) + result = cross_device_ops_instance.batch_reduce( + reduce_op, [(per_replica, per_replica)]) else: - result = cross_tower_ops_instance.reduce( - aggregation, per_replica, devices) + result = cross_device_ops_instance.reduce( + reduce_op, per_replica, per_replica) total_indices_with_dups = [1, 1, 3] total_indices_without_dups = [1, 3] - if aggregation == vs.VariableAggregation.SUM: + if reduce_op == reduce_util.ReduceOp.SUM: total_values_with_dups = [[1., 2.], [3., 4.], [5., 6.]] total_values_without_dups = [[4., 6.], [5., 6.]] else: - assert aggregation == vs.VariableAggregation.MEAN + assert reduce_op == reduce_util.ReduceOp.MEAN total_values_with_dups = [[0.5, 1.], [1.5, 2.], [2.5, 3.]] total_values_without_dups = [[2., 3.], [2.5, 3.]] @@ -356,49 +357,63 @@ class MultiWorkerCrossDeviceOpsTest(multi_worker_test_base.MultiWorkerTestBase, "/job:worker/replica:0/task:0", "/job:worker/replica:0/task:1" ] multi_worker_allreduce_combinations = combinations.combine( - cross_tower_ops=[ + cross_device_ops=[ combinations.NamedObject( "MultiWorkerAllReduce", - cross_tower_ops_lib.MultiWorkerAllReduce( + cross_device_ops_lib.MultiWorkerAllReduce( worker_devices, 2, ("pscpu/pscpu", 2, -1), 0, 0, 0)), combinations.NamedObject( "MultiWorkerAllReducePack", - cross_tower_ops_lib.MultiWorkerAllReduce( + cross_device_ops_lib.MultiWorkerAllReduce( worker_devices, 2, ("pscpu/pscpu", 2, -1), 1, 0, 0)), combinations.NamedObject( "MultiWorkerAllReduceAggregation", - cross_tower_ops_lib.MultiWorkerAllReduce( + cross_device_ops_lib.MultiWorkerAllReduce( worker_devices, 2, ("pscpu/pscpu", 2, -1), 0, 100, 10)), combinations.NamedObject( "MultiWorkerAllReduceMultipleSpecs", - cross_tower_ops_lib.MultiWorkerAllReduce( + cross_device_ops_lib.MultiWorkerAllReduce( worker_devices, 2, [("pscpu/pscpu", 2, 100), ("xring", 2, -1)], 0, 0, 0)), ], distribution=[ combinations.NamedDistribution( "MirroredCPU", - lambda: mirrored_strategy.MirroredStrategy(num_gpus=0), + lambda: mirrored_strategy.MirroredStrategy(num_gpus_per_worker=0), required_gpus=0), combinations.NamedDistribution( "Mirrored1GPU", - lambda: mirrored_strategy.MirroredStrategy(num_gpus=1), + lambda: mirrored_strategy.MirroredStrategy(num_gpus_per_worker=1), required_gpus=1), combinations.NamedDistribution( "Mirrored2GPUs", - lambda: mirrored_strategy.MirroredStrategy(num_gpus=2), + lambda: mirrored_strategy.MirroredStrategy(num_gpus_per_worker=2), + required_gpus=2), + # pylint: disable=g-long-lambda + combinations.NamedDistribution( + "CoreMirroredCPU", + lambda: mirrored_strategy.CoreMirroredStrategy(["/device:CPU:0"]), + required_gpus=0), + combinations.NamedDistribution( + "CoreMirrored1GPU", + lambda: mirrored_strategy.CoreMirroredStrategy(["/device:GPU:0"]), + required_gpus=1), + combinations.NamedDistribution( + "CoreMirrored2GPUs", + lambda: mirrored_strategy.CoreMirroredStrategy( + ["/device:GPU:0", "/device:GPU:1"]), required_gpus=2), ], mode=["graph"]) @combinations.generate(multi_worker_allreduce_combinations) - def testReductionAndBroadcast(self, cross_tower_ops, distribution): + def testReductionAndBroadcast(self, cross_device_ops, distribution): distribution.configure(cluster_spec={ "worker": ["/job:worker/replica:0/task:0", "/job:worker/replica:0/task:1"] }) with distribution.scope(): - self._testReductionAndBroadcast(cross_tower_ops, distribution) + self._testReductionAndBroadcast(cross_device_ops, distribution) class MultiWorkerCollectiveAllReduceTest( @@ -419,7 +434,7 @@ class MultiWorkerCollectiveAllReduceTest( MultiWorkerCollectiveAllReduceTest.collective_key_base += 100000 def _get_test_objects(self, task_type, task_id, num_gpus=0, local_mode=False): - collective_keys = cross_tower_utils.CollectiveKeys( + collective_keys = cross_device_utils.CollectiveKeys( group_key_start=10 * num_gpus + MultiWorkerCollectiveAllReduceTest.collective_key_base, instance_key_start=num_gpus * 100 + @@ -427,7 +442,7 @@ class MultiWorkerCollectiveAllReduceTest( instance_key_with_id_start=num_gpus * 10000 + MultiWorkerCollectiveAllReduceTest.collective_key_base) if local_mode: - collective_all_reduce_ops = cross_tower_ops_lib.CollectiveAllReduce( + collective_all_reduce_ops = cross_device_ops_lib.CollectiveAllReduce( 1, num_gpus, collective_keys=collective_keys) if num_gpus: devices = ["/device:GPU:%d" % i for i in range(num_gpus)] @@ -435,7 +450,7 @@ class MultiWorkerCollectiveAllReduceTest( devices = ["/device:CPU:0"] return collective_all_reduce_ops, devices, "" else: - collective_all_reduce_ops = cross_tower_ops_lib.CollectiveAllReduce( + collective_all_reduce_ops = cross_device_ops_lib.CollectiveAllReduce( 3, num_gpus, collective_keys=collective_keys) if num_gpus: devices = [ @@ -491,37 +506,35 @@ class MultiWorkerCollectiveAllReduceTest( destination_mirrored = _fake_mirrored(1., devices) destination_different = _fake_mirrored(1., _cpu_device) destination_str = _cpu_device - destination_list = devices all_destinations = [ - destination_different, destination_mirrored, destination_str, - destination_list + destination_different, destination_mirrored, destination_str ] # test reduce() for destinations in all_destinations: self._assert_values_equal( collective_all_reduce.reduce( - vs.VariableAggregation.MEAN, + reduce_util.ReduceOp.MEAN, per_replica, destinations=destinations), _fake_mirrored(mean, destinations), sess) self._assert_values_equal( collective_all_reduce.reduce( - vs.VariableAggregation.MEAN, + reduce_util.ReduceOp.MEAN, per_replica_2, destinations=destinations), _fake_mirrored(mean_2, destinations), sess) self._assert_values_equal( collective_all_reduce.reduce( - vs.VariableAggregation.SUM, + reduce_util.ReduceOp.SUM, per_replica, destinations=destinations), _fake_mirrored(mean * len(devices) * num_workers, destinations), sess) self._assert_values_equal( collective_all_reduce.reduce( - vs.VariableAggregation.SUM, + reduce_util.ReduceOp.SUM, per_replica_2, destinations=destinations), _fake_mirrored(mean_2 * len(devices) * num_workers, destinations), @@ -530,7 +543,7 @@ class MultiWorkerCollectiveAllReduceTest( # test batch_reduce() for d1, d2 in itertools.product(all_destinations, all_destinations): self._assert_values_equal( - collective_all_reduce.batch_reduce(vs.VariableAggregation.MEAN, + collective_all_reduce.batch_reduce(reduce_util.ReduceOp.MEAN, [(per_replica, d1), (per_replica_2, d2)]), [ @@ -538,7 +551,7 @@ class MultiWorkerCollectiveAllReduceTest( _fake_mirrored(mean_2, d2) ], sess) self._assert_values_equal( - collective_all_reduce.batch_reduce(vs.VariableAggregation.SUM, + collective_all_reduce.batch_reduce(reduce_util.ReduceOp.SUM, [(per_replica, d1), (per_replica_2, d2)]), [ diff --git a/tensorflow/contrib/distribute/python/cross_tower_utils_test.py b/tensorflow/contrib/distribute/python/cross_device_utils_test.py similarity index 83% rename from tensorflow/contrib/distribute/python/cross_tower_utils_test.py rename to tensorflow/contrib/distribute/python/cross_device_utils_test.py index e46240abbf..2303a31677 100644 --- a/tensorflow/contrib/distribute/python/cross_tower_utils_test.py +++ b/tensorflow/contrib/distribute/python/cross_device_utils_test.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Tests for cross_tower_utils.""" +"""Tests for cross_device_utils.""" from __future__ import absolute_import from __future__ import division @@ -21,14 +21,14 @@ from __future__ import print_function from absl.testing import parameterized from tensorflow.contrib.distribute.python import combinations -from tensorflow.contrib.distribute.python import cross_tower_utils -from tensorflow.contrib.distribute.python import values as value_lib +from tensorflow.python.distribute import cross_device_utils +from tensorflow.python.distribute import device_util +from tensorflow.python.distribute import values as value_lib from tensorflow.python.eager import test from tensorflow.python.framework import constant_op from tensorflow.python.framework import ops from tensorflow.python.framework import test_util from tensorflow.python.ops import math_ops -from tensorflow.python.training import device_util class IndexedSlicesUtilsTest(test.TestCase, parameterized.TestCase): @@ -43,7 +43,7 @@ class IndexedSlicesUtilsTest(test.TestCase, parameterized.TestCase): t0 = constant_op.constant([[1., 2.], [0, 0], [3., 4.]]) t1 = constant_op.constant([[0., 0.], [5, 6], [7., 8.]]) total = constant_op.constant([[1., 2.], [5, 6], [10., 12.]]) - result = cross_tower_utils.aggregate_tensors_or_indexed_slices([t0, t1]) + result = cross_device_utils.aggregate_tensors_or_indexed_slices([t0, t1]) self._assert_values_equal(total, result) @test_util.run_in_graph_and_eager_modes @@ -53,7 +53,7 @@ class IndexedSlicesUtilsTest(test.TestCase, parameterized.TestCase): t1 = math_ops._as_indexed_slices( constant_op.constant([[0., 0.], [5, 6], [7., 8.]])) total = constant_op.constant([[1., 2.], [5, 6], [10., 12.]]) - result = cross_tower_utils.aggregate_tensors_or_indexed_slices([t0, t1]) + result = cross_device_utils.aggregate_tensors_or_indexed_slices([t0, t1]) self.assertIsInstance(result, ops.IndexedSlices) self._assert_values_equal(total, result) @@ -62,7 +62,7 @@ class IndexedSlicesUtilsTest(test.TestCase, parameterized.TestCase): t = constant_op.constant([[1., 2.], [0, 0], [3., 4.]]) n = 2 expected = constant_op.constant([[0.5, 1.], [0, 0], [1.5, 2.]]) - result = cross_tower_utils.divide_by_n_tensors_or_indexed_slices(t, n) + result = cross_device_utils.divide_by_n_tensors_or_indexed_slices(t, n) self._assert_values_equal(expected, result) @test_util.run_in_graph_and_eager_modes @@ -71,7 +71,7 @@ class IndexedSlicesUtilsTest(test.TestCase, parameterized.TestCase): constant_op.constant([[1., 2.], [0, 0], [3., 4.]])) n = 2 expected = constant_op.constant([[0.5, 1.], [0, 0], [1.5, 2.]]) - result = cross_tower_utils.divide_by_n_tensors_or_indexed_slices(t, n) + result = cross_device_utils.divide_by_n_tensors_or_indexed_slices(t, n) self.assertIsInstance(result, ops.IndexedSlices) self._assert_values_equal(expected, result) @@ -79,7 +79,7 @@ class IndexedSlicesUtilsTest(test.TestCase, parameterized.TestCase): def testIsIndexedSlices(self): t = math_ops._as_indexed_slices( constant_op.constant([[1., 2.], [0, 0], [3., 4.]])) - self.assertTrue(cross_tower_utils.contains_indexed_slices(t)) + self.assertTrue(cross_device_utils.contains_indexed_slices(t)) @test_util.run_in_graph_and_eager_modes def testContainsIndexedSlices_List(self): @@ -87,7 +87,7 @@ class IndexedSlicesUtilsTest(test.TestCase, parameterized.TestCase): constant_op.constant([[1., 2.], [0, 0], [3., 4.]])) t1 = math_ops._as_indexed_slices( constant_op.constant([[0., 0.], [5, 6], [7., 8.]])) - self.assertTrue(cross_tower_utils.contains_indexed_slices([t0, t1])) + self.assertTrue(cross_device_utils.contains_indexed_slices([t0, t1])) @test_util.run_in_graph_and_eager_modes def testContainsIndexedSlices_Tuple(self): @@ -95,7 +95,7 @@ class IndexedSlicesUtilsTest(test.TestCase, parameterized.TestCase): constant_op.constant([[1., 2.], [0, 0], [3., 4.]])) t1 = math_ops._as_indexed_slices( constant_op.constant([[0., 0.], [5, 6], [7., 8.]])) - self.assertTrue(cross_tower_utils.contains_indexed_slices((t0, t1))) + self.assertTrue(cross_device_utils.contains_indexed_slices((t0, t1))) @test_util.run_in_graph_and_eager_modes def testContainsIndexedSlices_PerReplica(self): @@ -104,7 +104,7 @@ class IndexedSlicesUtilsTest(test.TestCase, parameterized.TestCase): t1 = math_ops._as_indexed_slices( constant_op.constant([[0., 0.], [5, 6], [7., 8.]])) per_replica = value_lib.PerReplica({"/gpu:0": t0, "/cpu:0": t1}) - self.assertTrue(cross_tower_utils.contains_indexed_slices(per_replica)) + self.assertTrue(cross_device_utils.contains_indexed_slices(per_replica)) @combinations.generate(combinations.combine( mode=["graph", "eager"], @@ -113,7 +113,7 @@ class IndexedSlicesUtilsTest(test.TestCase, parameterized.TestCase): with ops.device("/cpu:0"): t = constant_op.constant([[1., 2.], [0, 0], [3., 4.]]) destination = "/gpu:0" - result = cross_tower_utils.copy_tensor_or_indexed_slices_to_device( + result = cross_device_utils.copy_tensor_or_indexed_slices_to_device( t, destination) self._assert_values_equal(t, result) @@ -128,7 +128,7 @@ class IndexedSlicesUtilsTest(test.TestCase, parameterized.TestCase): t = math_ops._as_indexed_slices( constant_op.constant([[1., 2.], [0, 0], [3., 4.]])) destination = "/gpu:0" - result = cross_tower_utils.copy_tensor_or_indexed_slices_to_device( + result = cross_device_utils.copy_tensor_or_indexed_slices_to_device( t, destination) self.assertIsInstance(result, ops.IndexedSlices) diff --git a/tensorflow/contrib/distribute/python/estimator_integration_test.py b/tensorflow/contrib/distribute/python/estimator_integration_test.py index a1355c0b09..e17085628b 100644 --- a/tensorflow/contrib/distribute/python/estimator_integration_test.py +++ b/tensorflow/contrib/distribute/python/estimator_integration_test.py @@ -34,7 +34,7 @@ from tensorflow.python.estimator.canned import dnn_linear_combined from tensorflow.python.estimator.canned import prediction_keys from tensorflow.python.estimator.export import export from tensorflow.python.estimator.inputs import numpy_io -from tensorflow.python.feature_column import feature_column +from tensorflow.python.feature_column import feature_column_lib as feature_column from tensorflow.python.framework import ops from tensorflow.python.platform import gfile from tensorflow.python.summary.writer import writer_cache @@ -63,7 +63,9 @@ class DNNLinearCombinedClassifierIntegrationTest(test.TestCase, distribution=[ combinations.one_device_strategy, combinations.mirrored_strategy_with_gpu_and_cpu, - combinations.mirrored_strategy_with_two_gpus + combinations.mirrored_strategy_with_two_gpus, + combinations.core_mirrored_strategy_with_gpu_and_cpu, + combinations.core_mirrored_strategy_with_two_gpus ], use_train_and_evaluate=[True, False])) def test_complete_flow_with_mode(self, distribution, use_train_and_evaluate): @@ -75,12 +77,12 @@ class DNNLinearCombinedClassifierIntegrationTest(test.TestCase, train_input_fn = self.dataset_input_fn( x={'x': data}, y=data, - batch_size=batch_size // len(distribution.worker_devices), + batch_size=batch_size // distribution.num_replicas_in_sync, shuffle=True) eval_input_fn = self.dataset_input_fn( x={'x': data}, y=data, - batch_size=batch_size // len(distribution.worker_devices), + batch_size=batch_size // distribution.num_replicas_in_sync, shuffle=False) predict_input_fn = numpy_io.numpy_input_fn( x={'x': data}, batch_size=batch_size, shuffle=False) diff --git a/tensorflow/contrib/distribute/python/estimator_training_test.py b/tensorflow/contrib/distribute/python/estimator_training_test.py index 8f82b4c92a..b369a7fefe 100644 --- a/tensorflow/contrib/distribute/python/estimator_training_test.py +++ b/tensorflow/contrib/distribute/python/estimator_training_test.py @@ -24,7 +24,6 @@ import json import os import sys import tempfile -import threading from absl.testing import parameterized import numpy as np @@ -45,11 +44,13 @@ from tensorflow.python.estimator import training as estimator_training from tensorflow.python.estimator.canned import dnn_linear_combined from tensorflow.python.estimator.canned import prediction_keys from tensorflow.python.estimator.export import export as export_lib -from tensorflow.python.feature_column import feature_column +from tensorflow.python.feature_column import feature_column_lib as feature_column from tensorflow.python.platform import gfile from tensorflow.python.platform import test from tensorflow.python.summary import summary_iterator from tensorflow.python.summary.writer import writer_cache +from tensorflow.python.training import session_manager + BATCH_SIZE = 10 LABEL_DIMENSION = 2 @@ -68,57 +69,19 @@ PS = dc._TaskType.PS original_run_std_server = dc._run_std_server -class MockOsEnv(dict): - - def __init__(self, *args): - self._thread_local = threading.local() - super(MockOsEnv, self).__init__(*args) - - def get(self, key, default): - if not hasattr(self._thread_local, "dict"): - self._thread_local.dict = dict() - if key == "TF_CONFIG": - return dict.get(self._thread_local.dict, key, default) - else: - return dict.get(self, key, default) - - def __getitem__(self, key): - if not hasattr(self._thread_local, "dict"): - self._thread_local.dict = dict() - if key == "TF_CONFIG": - return dict.__getitem__(self._thread_local.dict, key) - else: - return dict.__getitem__(self, key) - - def __setitem__(self, key, val): - if not hasattr(self._thread_local, "dict"): - self._thread_local.dict = dict() - if key == "TF_CONFIG": - return dict.__setitem__(self._thread_local.dict, key, val) - else: - return dict.__setitem__(self, key, val) - - -class DistributeCoordinatorIntegrationTest(test.TestCase, - parameterized.TestCase): +class DistributeCoordinatorIntegrationTest( + multi_worker_test_base.IndependentWorkerTestBase, parameterized.TestCase): @classmethod def setUpClass(cls): """Create a local cluster with 2 workers.""" + super(DistributeCoordinatorIntegrationTest, cls).setUpClass() cls._cluster_spec = multi_worker_test_base.create_in_process_cluster( num_workers=3, num_ps=2, has_eval=True) def setUp(self): self._model_dir = tempfile.mkdtemp() - self._mock_os_env = MockOsEnv() - self._mock_context = test.mock.patch.object(os, "environ", - self._mock_os_env) super(DistributeCoordinatorIntegrationTest, self).setUp() - self._mock_context.__enter__() - - def tearDown(self): - self._mock_context.__exit__(None, None, None) - super(DistributeCoordinatorIntegrationTest, self).tearDown() def dataset_input_fn(self, x, y, batch_size, shuffle): @@ -141,8 +104,8 @@ class DistributeCoordinatorIntegrationTest(test.TestCase, def _extract_loss_and_global_step(self, event_folder): """Returns the loss and global step in last event.""" event_paths = glob.glob(os.path.join(event_folder, "events*")) - self.assertGreater(len(event_paths), 0, - msg="Event file not found in dir %s" % event_folder) + self.assertNotEmpty( + event_paths, msg="Event file not found in dir %s" % event_folder) loss = None global_step_count = None @@ -202,10 +165,10 @@ class DistributeCoordinatorIntegrationTest(test.TestCase, train_input_fn = self.dataset_input_fn( x={"x": DATA}, y=DATA, - batch_size=BATCH_SIZE // len(train_distribute.worker_devices), + batch_size=BATCH_SIZE // train_distribute.num_replicas_in_sync, shuffle=True) if eval_distribute: - eval_batch_size = BATCH_SIZE // len(eval_distribute.worker_devices) + eval_batch_size = BATCH_SIZE // eval_distribute.num_replicas_in_sync else: eval_batch_size = BATCH_SIZE eval_input_fn = self.dataset_input_fn( @@ -285,27 +248,34 @@ class DistributeCoordinatorIntegrationTest(test.TestCase, ]) self.assertAllEqual((BATCH_SIZE, LABEL_DIMENSION), predicted_proba.shape) + def _get_strategy_object(self, strategy_cls): + if strategy_cls == mirrored_strategy.CoreMirroredStrategy: + return strategy_cls(mirrored_strategy.all_local_devices()) + else: + return strategy_cls(num_gpus_per_worker=context.num_gpus()) + @combinations.generate( combinations.combine( mode=["graph"], train_distribute_cls=[ collective_all_reduce_strategy.CollectiveAllReduceStrategy, mirrored_strategy.MirroredStrategy, + mirrored_strategy.CoreMirroredStrategy, parameter_server_strategy.ParameterServerStrategy ], eval_distribute_cls=[ - None, mirrored_strategy.MirroredStrategy, + None, + mirrored_strategy.MirroredStrategy, + mirrored_strategy.CoreMirroredStrategy, parameter_server_strategy.ParameterServerStrategy, ], required_gpus=[0, 1])) def test_complete_flow_standalone_client(self, train_distribute_cls, eval_distribute_cls): - train_distribute = train_distribute_cls( - num_gpus_per_worker=context.num_gpus()) + train_distribute = self._get_strategy_object(train_distribute_cls) if eval_distribute_cls: - eval_distribute = eval_distribute_cls( - num_gpus_per_worker=context.num_gpus()) + eval_distribute = self._get_strategy_object(eval_distribute_cls) else: eval_distribute = None @@ -322,20 +292,20 @@ class DistributeCoordinatorIntegrationTest(test.TestCase, mode=["graph"], train_distribute_cls=[ mirrored_strategy.MirroredStrategy, + mirrored_strategy.CoreMirroredStrategy, ], eval_distribute_cls=[ None, mirrored_strategy.MirroredStrategy, + mirrored_strategy.CoreMirroredStrategy, ], required_gpus=[0, 1])) def test_estimator_standalone_client(self, train_distribute_cls, eval_distribute_cls): - train_distribute = train_distribute_cls( - num_gpus_per_worker=context.num_gpus()) + train_distribute = self._get_strategy_object(train_distribute_cls) if eval_distribute_cls: - eval_distribute = eval_distribute_cls( - num_gpus_per_worker=context.num_gpus()) + eval_distribute = self._get_strategy_object(eval_distribute_cls) else: eval_distribute = None @@ -355,47 +325,15 @@ class DistributeCoordinatorIntegrationTest(test.TestCase, self._barrier.wait() return ret - def _task_thread(self, train_distribute, eval_distribute, tf_config): - os.environ["TF_CONFIG"] = json.dumps(tf_config) + def _independent_worker_fn( + self, + train_distribute, + eval_distribute, + ): with test.mock.patch.object(dc, "_run_std_server", self._mock_run_std_server): self._complete_flow(train_distribute, eval_distribute) - def _run_task_in_thread(self, cluster_spec, task_type, task_id, - train_distribute, eval_distribute): - if task_type: - tf_config = { - "cluster": cluster_spec, - "task": { - "type": task_type, - "index": task_id - } - } - else: - tf_config = { - "cluster": cluster_spec, - "task": { - "type": task_type, - "index": task_id - } - } - t = threading.Thread( - target=self._task_thread, - args=(train_distribute, eval_distribute, tf_config)) - t.start() - return t - - def _run_multiple_tasks_in_threads(self, cluster_spec, train_distribute, - eval_distribute): - threads = {} - for task_type in cluster_spec.keys(): - threads[task_type] = [] - for task_id in range(len(cluster_spec[task_type])): - t = self._run_task_in_thread(cluster_spec, task_type, task_id, - train_distribute, eval_distribute) - threads[task_type].append(t) - return threads - @combinations.generate( combinations.combine( mode=["graph"], @@ -405,21 +343,20 @@ class DistributeCoordinatorIntegrationTest(test.TestCase, ], eval_distribute_cls=[ None, mirrored_strategy.MirroredStrategy, + mirrored_strategy.CoreMirroredStrategy, parameter_server_strategy.ParameterServerStrategy, ], required_gpus=[0, 1])) def test_complete_flow_indepedent_worker_between_graph( self, train_distribute_cls, eval_distribute_cls): - train_distribute = train_distribute_cls( - num_gpus_per_worker=context.num_gpus()) - if (context.num_gpus() < 2 and eval_distribute_cls == collective_all_reduce_strategy.CollectiveAllReduceStrategy): self.skipTest("`CollectiveAllReduceStrategy` needs at least two towers.") + train_distribute = self._get_strategy_object(train_distribute_cls) + if eval_distribute_cls: - eval_distribute = eval_distribute_cls( - num_gpus_per_worker=context.num_gpus()) + eval_distribute = self._get_strategy_object(eval_distribute_cls) else: eval_distribute = None @@ -435,8 +372,9 @@ class DistributeCoordinatorIntegrationTest(test.TestCase, # 3 workers and 1 evaluator. self._barrier = dc._Barrier(4) - threads = self._run_multiple_tasks_in_threads( - cluster_spec, train_distribute, eval_distribute) + threads = self.run_multiple_tasks_in_threads(self._independent_worker_fn, + cluster_spec, train_distribute, + eval_distribute) for task_type, ts in threads.items(): if task_type == PS: continue @@ -449,17 +387,22 @@ class DistributeCoordinatorIntegrationTest(test.TestCase, @combinations.generate( combinations.combine( mode=["graph"], - train_distribute_cls=[mirrored_strategy.MirroredStrategy], - eval_distribute_cls=[None, mirrored_strategy.MirroredStrategy], + train_distribute_cls=[ + mirrored_strategy.MirroredStrategy, + mirrored_strategy.CoreMirroredStrategy + ], + eval_distribute_cls=[ + None, + mirrored_strategy.MirroredStrategy, + mirrored_strategy.CoreMirroredStrategy + ], required_gpus=[0, 1])) def test_complete_flow_indepedent_worker_in_graph(self, train_distribute_cls, eval_distribute_cls): - train_distribute = train_distribute_cls( - num_gpus_per_worker=context.num_gpus()) + train_distribute = self._get_strategy_object(train_distribute_cls) if eval_distribute_cls: - eval_distribute = eval_distribute_cls( - num_gpus_per_worker=context.num_gpus()) + eval_distribute = self._get_strategy_object(eval_distribute_cls) else: eval_distribute = None @@ -467,8 +410,9 @@ class DistributeCoordinatorIntegrationTest(test.TestCase, num_workers=3, num_ps=0, has_eval=True) # 3 workers and 1 evaluator. self._barrier = dc._Barrier(4) - threads = self._run_multiple_tasks_in_threads( - cluster_spec, train_distribute, eval_distribute) + threads = self.run_multiple_tasks_in_threads(self._independent_worker_fn, + cluster_spec, train_distribute, + eval_distribute) threads[WORKER][0].join() threads[EVALUATOR][0].join() @@ -506,7 +450,8 @@ class RunConfigTest(test.TestCase): "os.environ", {"TF_CONFIG": json.dumps(TF_CONFIG_WITHOUT_TASK)}): run_config_lib.RunConfig( experimental_distribute=DistributeConfig( - train_distribute=mirrored_strategy.MirroredStrategy(num_gpus=2))) + train_distribute=mirrored_strategy.CoreMirroredStrategy( + ["/device:GPU:0", "/device:GPU:1"]))) def test_should_run_distribute_coordinator(self): """Tests that should_run_distribute_coordinator return a correct value.""" @@ -529,10 +474,12 @@ class RunConfigTest(test.TestCase): {"TF_CONFIG": json.dumps(TF_CONFIG_WITH_CHIEF)}): config_with_train_distribute = run_config_lib.RunConfig( experimental_distribute=DistributeConfig( - train_distribute=mirrored_strategy.MirroredStrategy(num_gpus=2))) + train_distribute=mirrored_strategy.CoreMirroredStrategy( + ["/device:GPU:0", "/device:GPU:1"]))) config_with_eval_distribute = run_config_lib.RunConfig( experimental_distribute=DistributeConfig( - eval_distribute=mirrored_strategy.MirroredStrategy(num_gpus=2))) + eval_distribute=mirrored_strategy.CoreMirroredStrategy( + ["/device:GPU:0", "/device:GPU:1"]))) self.assertTrue( dc_training.should_run_distribute_coordinator( config_with_train_distribute)) @@ -545,26 +492,27 @@ class RunConfigTest(test.TestCase): {"TF_CONFIG": json.dumps(TF_CONFIG_WITH_MASTER)}): config = run_config_lib.RunConfig( experimental_distribute=DistributeConfig( - train_distribute=mirrored_strategy.MirroredStrategy(num_gpus=2))) + train_distribute=mirrored_strategy.CoreMirroredStrategy( + ["/device:GPU:0", "/device:GPU:1"]))) self.assertFalse(dc_training.should_run_distribute_coordinator(config)) def test_init_run_config_duplicate_distribute(self): with self.assertRaises(ValueError): run_config_lib.RunConfig( - train_distribute=mirrored_strategy.MirroredStrategy(), + train_distribute=mirrored_strategy.CoreMirroredStrategy(), experimental_distribute=DistributeConfig( - train_distribute=mirrored_strategy.MirroredStrategy())) + train_distribute=mirrored_strategy.CoreMirroredStrategy())) with self.assertRaises(ValueError): run_config_lib.RunConfig( - eval_distribute=mirrored_strategy.MirroredStrategy(), + eval_distribute=mirrored_strategy.CoreMirroredStrategy(), experimental_distribute=DistributeConfig( - eval_distribute=mirrored_strategy.MirroredStrategy())) + eval_distribute=mirrored_strategy.CoreMirroredStrategy())) def test_init_run_config_none_distribute_coordinator_mode(self): # We don't use distribute coordinator for local training. config = run_config_lib.RunConfig( - train_distribute=mirrored_strategy.MirroredStrategy()) + train_distribute=mirrored_strategy.CoreMirroredStrategy()) dc_training.init_run_config(config, {}) self.assertIsNone(config._distribute_coordinator_mode) @@ -572,7 +520,7 @@ class RunConfigTest(test.TestCase): with test.mock.patch.dict("os.environ", {"TF_CONFIG": json.dumps(TF_CONFIG_WITH_MASTER)}): config = run_config_lib.RunConfig( - train_distribute=mirrored_strategy.MirroredStrategy()) + train_distribute=mirrored_strategy.CoreMirroredStrategy()) self.assertIsNone(config._distribute_coordinator_mode) # When `train_distribute` is not specified, don't use distribute @@ -588,7 +536,7 @@ class RunConfigTest(test.TestCase): with test.mock.patch.dict("os.environ", {"TF_CONFIG": json.dumps(TF_CONFIG_WITH_CHIEF)}): config = run_config_lib.RunConfig( - train_distribute=mirrored_strategy.MirroredStrategy()) + train_distribute=mirrored_strategy.CoreMirroredStrategy()) self.assertEqual(config._distribute_coordinator_mode, dc.CoordinatorMode.INDEPENDENT_WORKER) @@ -597,7 +545,7 @@ class RunConfigTest(test.TestCase): # `experimental.remote_cluster` is set use distribute coordinator with # STANDALONE_CLIENT mode. config = run_config_lib.RunConfig( - train_distribute=mirrored_strategy.MirroredStrategy(), + train_distribute=mirrored_strategy.CoreMirroredStrategy(), experimental_distribute=DistributeConfig( remote_cluster={"chief": ["fake_worker"]})) self.assertEqual(config._distribute_coordinator_mode, @@ -605,5 +553,15 @@ class RunConfigTest(test.TestCase): if __name__ == "__main__": + # Reduce `recovery_wait_secs` from 30 seconds so the test completes quickly. + orig_init = session_manager.SessionManager.__init__ + + def new_init(*args, **kwargs): + kwargs.pop("recovery_wait_secs", None) + kwargs["recovery_wait_secs"] = 0.5 + orig_init(*args, **kwargs) + + session_manager.SessionManager.__init__ = new_init + with test.mock.patch.object(sys, "exit", os._exit): test.main() diff --git a/tensorflow/contrib/distribute/python/examples/keras_mnist.py b/tensorflow/contrib/distribute/python/examples/keras_mnist.py index 0fd3acd045..60fda99664 100644 --- a/tensorflow/contrib/distribute/python/examples/keras_mnist.py +++ b/tensorflow/contrib/distribute/python/examples/keras_mnist.py @@ -20,6 +20,10 @@ from __future__ import print_function import tensorflow as tf +from tensorflow.python.distribute import mirrored_strategy +from tensorflow.python.keras.optimizer_v2 import rmsprop + + NUM_CLASSES = 10 @@ -102,18 +106,23 @@ def main(_): # Build the train and eval datasets from the MNIST data. Also return the # input shape which is constructed based on the `image_data_format` # i.e channels_first or channels_last. + tf.enable_eager_execution() + train_ds, eval_ds, input_shape = get_input_datasets() model = get_model(input_shape) # Instantiate the MirroredStrategy object. If we don't specify `num_gpus` or # the `devices` argument then all the GPUs available on the machine are used. - strategy = tf.contrib.distribute.MirroredStrategy() + # TODO(priyag): Use `tf.distribute.MirroredStrategy` once available. + strategy = mirrored_strategy.MirroredStrategy(['/gpu:0', '/cpu:0']) + + optimizer = rmsprop.RMSProp(learning_rate=0.001) # Compile the model by passing the distribution strategy object to the # `distribute` argument. `fit`, `evaluate` and `predict` will be distributed # based on the strategy instantiated. model.compile(loss=tf.keras.losses.categorical_crossentropy, - optimizer=tf.train.RMSPropOptimizer(learning_rate=0.001), + optimizer=optimizer, metrics=['accuracy'], distribute=strategy) diff --git a/tensorflow/contrib/distribute/python/keras_optimizer_v2_test.py b/tensorflow/contrib/distribute/python/keras_optimizer_v2_test.py index 46a1cf41c5..6dfd85bcc4 100644 --- a/tensorflow/contrib/distribute/python/keras_optimizer_v2_test.py +++ b/tensorflow/contrib/distribute/python/keras_optimizer_v2_test.py @@ -25,18 +25,23 @@ import numpy as np import six from tensorflow.contrib.distribute.python import combinations -from tensorflow.contrib.distribute.python import mirrored_strategy from tensorflow.core.protobuf import config_pb2 +from tensorflow.python import keras from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.eager import context +from tensorflow.python.distribute import distribution_strategy_context as ds_context from tensorflow.python.estimator import run_config from tensorflow.python.estimator import training from tensorflow.python.estimator.canned import dnn_linear_combined from tensorflow.python.estimator.canned import prediction_keys from tensorflow.python.estimator.export import export from tensorflow.python.estimator.inputs import numpy_io -from tensorflow.python.feature_column import feature_column +from tensorflow.python.feature_column import feature_column_lib as feature_column +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops from tensorflow.python.keras.optimizer_v2 import adam +from tensorflow.python.keras.optimizer_v2 import gradient_descent +from tensorflow.python.ops import math_ops from tensorflow.python.ops import variable_scope from tensorflow.python.ops import variables from tensorflow.python.platform import gfile @@ -64,7 +69,9 @@ class KerasOptimizerV2IntegrationTest(test.TestCase, parameterized.TestCase): distribution=[ combinations.one_device_strategy, combinations.mirrored_strategy_with_gpu_and_cpu, - combinations.mirrored_strategy_with_two_gpus + combinations.mirrored_strategy_with_two_gpus, + combinations.core_mirrored_strategy_with_gpu_and_cpu, + combinations.core_mirrored_strategy_with_two_gpus ], use_train_and_evaluate=[True, False])) def test_complete_flow_with_mode(self, distribution, use_train_and_evaluate): @@ -76,11 +83,11 @@ class KerasOptimizerV2IntegrationTest(test.TestCase, parameterized.TestCase): train_input_fn = self.dataset_input_fn( x={'x': data}, y=data, - batch_size=batch_size // len(distribution.worker_devices)) + batch_size=batch_size // distribution.num_replicas_in_sync) eval_input_fn = self.dataset_input_fn( x={'x': data}, y=data, - batch_size=batch_size // len(distribution.worker_devices)) + batch_size=batch_size // distribution.num_replicas_in_sync) predict_input_fn = numpy_io.numpy_input_fn( x={'x': data}, batch_size=batch_size, shuffle=False) @@ -136,44 +143,51 @@ class KerasOptimizerV2IntegrationTest(test.TestCase, parameterized.TestCase): shutil.rmtree(self._model_dir) -class MirroredStrategyOptimizerV2Test(test.TestCase): +def get_model(): + x = keras.layers.Input(shape=(3,), name='input') + y = keras.layers.Dense(4, name='dense')(x) + model = keras.Model(x, y) + return model - def testKerasOptimizerWithUnequalInput(self): - if context.num_gpus() < 1: - self.skipTest('Not enough GPUs.') - def create_fn(device_id): +class MirroredStrategyOptimizerV2Test(test.TestCase, parameterized.TestCase): + + @combinations.generate(combinations.combine( + distribution=[ + combinations.mirrored_strategy_with_gpu_and_cpu, + combinations.core_mirrored_strategy_with_gpu_and_cpu], + mode=['graph'])) + def testKerasOptimizerWithUnequalInput(self, distribution): + def create_fn(): var = variables.Variable( 2.0, name='var', aggregation=variable_scope.VariableAggregation.SUM) # grad for cpu is 1, grad for gpu is 2, avg grad is 1.5. - loss = (device_id + 1) * var + loss = math_ops.cast(_replica_id() + 1, dtype=dtypes.float32) * var optimizer = adam.Adam(learning_rate=0.01, beta_1=0.2, beta_2=0.2) train_op = optimizer.minimize(loss, var_list=[var]) m = optimizer.get_slot(var, 'm') v = optimizer.get_slot(var, 'v') - return (var, m, v, train_op, optimizer.iteration) + return (var, m, v, train_op, optimizer.iterations) devices = ['/device:GPU:0', '/device:CPU:0'] - dist = mirrored_strategy.MirroredStrategy(devices) - with dist.scope(): - (var, m, v, op, counter) = dist.call_for_each_replica( - create_fn, args=[dist.worker_device_index]) + with distribution.scope(): + (var, m, v, op, counter) = distribution.call_for_each_replica(create_fn) self.evaluate(variables.global_variables_initializer()) var_val = [2.0, 2.0, 2.0] self.assertAllClose( var_val, self.evaluate( - [dist.read_var(var), + [distribution.read_var(var), var.get(devices[0]), var.get(devices[1])])) self.assertAllClose([0, 0, 0], self.evaluate([ - dist.read_var(counter), + distribution.read_var(counter), counter.get(devices[0]), counter.get(devices[1]) ])) - train_op = dist.unwrap(op) + train_op = distribution.unwrap(op) self.evaluate(train_op) # m(1) = beta1 * m(0) + (1-beta1) * grad = 0.2 * 0 + 0.8 * (1 + 2) / 2 m_val = [1.2, 1.2, 1.2] @@ -181,7 +195,7 @@ class MirroredStrategyOptimizerV2Test(test.TestCase): self.assertAllClose( m_val, self.evaluate( - [dist.read_var(m), + [distribution.read_var(m), m.get(devices[0]), m.get(devices[1])])) # v(1) = beta2 * v(0) + (1-beta2) * grad^2 = 0.2 * 0 + 0.8 * 2.25 @@ -189,7 +203,7 @@ class MirroredStrategyOptimizerV2Test(test.TestCase): self.assertAllClose( v_val, self.evaluate( - [dist.read_var(v), + [distribution.read_var(v), v.get(devices[0]), v.get(devices[1])])) # var(1) = var(0) - lr * m(1) * sqrt(1 - beta2) / sqrt(v(1)) / (1 - beta1) @@ -198,12 +212,12 @@ class MirroredStrategyOptimizerV2Test(test.TestCase): self.assertAllClose( var_val, self.evaluate( - [dist.read_var(var), + [distribution.read_var(var), var.get(devices[0]), var.get(devices[1])])) self.assertAllClose([1, 1, 1], self.evaluate([ - dist.read_var(counter), + distribution.read_var(counter), counter.get(devices[0]), counter.get(devices[1]) ])) @@ -214,7 +228,7 @@ class MirroredStrategyOptimizerV2Test(test.TestCase): self.assertAllClose( m_val, self.evaluate( - [dist.read_var(m), + [distribution.read_var(m), m.get(devices[0]), m.get(devices[1])])) # v(2) = beta2 * v(1) + (1-beta2) * grad^2 = 0.2 * 1.8 + 0.8 * 2.25 @@ -222,16 +236,50 @@ class MirroredStrategyOptimizerV2Test(test.TestCase): self.assertAllClose( v_val, self.evaluate( - [dist.read_var(v), + [distribution.read_var(v), v.get(devices[0]), v.get(devices[1])])) self.assertAllClose([2, 2, 2], self.evaluate([ - dist.read_var(counter), + distribution.read_var(counter), counter.get(devices[0]), counter.get(devices[1]) ])) + @combinations.generate(combinations.combine( + distribution=[ + combinations.mirrored_strategy_with_gpu_and_cpu, + combinations.core_mirrored_strategy_with_gpu_and_cpu], + mode=['graph'])) + def testOptimizerWithKerasModelAndNumpyArrays(self, distribution): + + with self.cached_session(): + model = get_model() + optimizer = gradient_descent.SGD(0.001) + loss = 'mse' + metrics = ['mae'] + model.compile(optimizer, loss, metrics=metrics, distribute=distribution) + + inputs = np.zeros((64, 3), dtype=np.float32) + targets = np.zeros((64, 4), dtype=np.float32) + + model.fit( + inputs, + targets, + epochs=1, + batch_size=2, + verbose=0, + validation_data=(inputs, targets)) + model.evaluate(inputs, targets) + model.predict(inputs) + + +def _replica_id(): + replica_id = ds_context.get_replica_context().replica_id_in_sync_group + if not isinstance(replica_id, ops.Tensor): + replica_id = constant_op.constant(replica_id) + return replica_id + if __name__ == '__main__': test.main() diff --git a/tensorflow/contrib/distribute/python/keras_test.py b/tensorflow/contrib/distribute/python/keras_test.py index 0db5844e4c..e530ab6f17 100644 --- a/tensorflow/contrib/distribute/python/keras_test.py +++ b/tensorflow/contrib/distribute/python/keras_test.py @@ -24,9 +24,10 @@ import numpy as np from tensorflow.contrib.distribute.python import combinations from tensorflow.contrib.distribute.python import mirrored_strategy from tensorflow.contrib.distribute.python import tpu_strategy -from tensorflow.contrib.distribute.python import values from tensorflow.python import keras from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.distribute import values +from tensorflow.python.eager import test from tensorflow.python.estimator import keras as keras_lib from tensorflow.python.estimator import run_config as run_config_lib from tensorflow.python.framework import constant_op @@ -35,14 +36,13 @@ from tensorflow.python.framework import random_seed from tensorflow.python.framework import test_util from tensorflow.python.keras import testing_utils from tensorflow.python.keras.engine import distributed_training_utils +from tensorflow.python.keras.optimizer_v2 import gradient_descent as gradient_descent_keras from tensorflow.python.ops.parsing_ops import gen_parsing_ops from tensorflow.python.platform import gfile -from tensorflow.python.platform import test from tensorflow.python.summary.writer import writer_cache from tensorflow.python.training import gradient_descent from tensorflow.python.training import rmsprop - _RANDOM_SEED = 1337 _TRAIN_SIZE = 200 _INPUT_SIZE = (10,) @@ -212,13 +212,18 @@ def multi_input_output_model(): return model -def get_correctness_test_inputs(use_numpy, with_distribution, +def get_correctness_test_inputs(use_numpy, use_validation_data, + with_distribution, x_train, y_train, x_predict): """Generates the inputs for correctness check when enable Keras with DS.""" global_batch_size = 64 batch_size = global_batch_size # TODO(b/118776054): Use global batch size for Keras/DS support. - if with_distribution: + use_per_core_batch_size = ( + with_distribution and + not distributed_training_utils.global_batch_size_supported( + with_distribution)) + if use_per_core_batch_size: batch_size //= with_distribution.num_replicas_in_sync if use_numpy: @@ -229,16 +234,17 @@ def get_correctness_test_inputs(use_numpy, with_distribution, 'epochs': 1, 'shuffle': False, } - eval_inputs = { - 'batch_size': batch_size, - 'x': x_train, - 'y': y_train, - } + + if use_validation_data: + eval_inputs = None + training_inputs['validation_data'] = (x_train, y_train) + else: + eval_inputs = { + 'batch_size': batch_size, + 'x': x_train, + 'y': y_train, + } predict_inputs = { - # TODO(b/119318587): We should not require batch_size when distribution - # is enabled. - 'batch_size': (len(x_predict) // with_distribution.num_replicas_in_sync - if with_distribution else None), 'x': np.array(x_predict, dtype=np.float32), } else: @@ -256,20 +262,28 @@ def get_correctness_test_inputs(use_numpy, with_distribution, 'shuffle': False, 'steps_per_epoch': len(x_train) // global_batch_size, } - eval_inputs = { - 'batch_size': None, - 'x': x, - 'y': None, - 'steps': 20, - } + if use_validation_data: + eval_inputs = None # Remove the eval_inputs + eval_dataset = dataset_ops.Dataset.from_tensor_slices( + (x_train, y_train)) + x = batch_wrapper(eval_dataset, batch_size, with_distribution) + training_inputs['validation_data'] = x + training_inputs['validation_steps'] = 5 + else: + eval_inputs = { + 'batch_size': None, + 'x': x, + 'y': None, + 'steps': 20, + } + predict_batch_size = len(x_predict) - if with_distribution: + if use_per_core_batch_size: predict_batch_size //= with_distribution.num_replicas_in_sync predict_dataset = dataset_ops.Dataset.from_tensor_slices(x_predict) predict_dataset = batch_wrapper(predict_dataset, predict_batch_size, with_distribution) predict_inputs = { - 'batch_size': None, 'steps': 1, 'x': predict_dataset, } @@ -277,47 +291,71 @@ def get_correctness_test_inputs(use_numpy, with_distribution, return training_inputs, eval_inputs, predict_inputs -strategies = [combinations.default_strategy, - combinations.one_device_strategy, - combinations.mirrored_strategy_with_gpu_and_cpu, - combinations.mirrored_strategy_with_two_gpus, - combinations.tpu_strategy, # steps_per_run=2 - combinations.tpu_strategy_one_step] +strategies_minus_tpu = [ + combinations.default_strategy, + combinations.one_device_strategy, + combinations.mirrored_strategy_with_gpu_and_cpu, + combinations.mirrored_strategy_with_two_gpus, + combinations.core_mirrored_strategy_with_gpu_and_cpu, + combinations.core_mirrored_strategy_with_two_gpus] + +tpu_strategies = [ + combinations.tpu_strategy, # steps_per_run=2 + combinations.tpu_strategy_one_step] def strategy_minus_tpu_combinations(): return combinations.combine( - distribution=[combinations.default_strategy, - combinations.one_device_strategy, - combinations.mirrored_strategy_with_gpu_and_cpu, - combinations.mirrored_strategy_with_two_gpus], - mode=['graph']) + distribution=strategies_minus_tpu, + mode=['graph', 'eager']) -def strategy_combinations(): +def tpu_strategy_combinations(): return combinations.combine( - distribution=strategies, + distribution=tpu_strategies, mode=['graph']) -def strategy_and_optimizer_combinations(): - return combinations.combine( - distribution=strategies, - optimizer=[combinations.adagrad_optimizer_v1_fn, - combinations.adam_optimizer_v1_fn, - combinations.gradient_descent_optimizer_v1_fn, - combinations.rmsprop_optimizer_v1_fn], - mode=['graph']) +def all_strategy_combinations(): + return strategy_minus_tpu_combinations() + tpu_strategy_combinations() -def strategy_and_inputs(): +# TODO(priyag): Add v2 optimizers here. +def strategy_and_optimizer_combinations(): + return combinations.times( + all_strategy_combinations(), + combinations.combine( + optimizer=[combinations.adagrad_optimizer_v1_fn, + combinations.adam_optimizer_v1_fn, + combinations.gradient_descent_optimizer_v1_fn, + combinations.rmsprop_optimizer_v1_fn])) + + +def strategy_and_input_combinations(): + return ( + combinations.times( + combinations.combine(distribution=strategies_minus_tpu), + combinations.combine(mode=['graph'], + use_numpy=[True, False], + use_validation_data=[True, False]) + + combinations.combine(mode=['eager'], + use_numpy=[False], + use_validation_data=[False])) + + combinations.times( + combinations.combine(distribution=tpu_strategies), + combinations.combine(mode=['graph'], + use_numpy=[True, False], + use_validation_data=[True, False]))) + + +def strategy_for_numpy_input_combinations(): return combinations.combine( - distribution=strategies, - use_numpy=[True, False], + distribution=strategies_minus_tpu + tpu_strategies, mode=['graph']) -class TestEstimatorDistributionStrategy(test_util.TensorFlowTestCase): +class TestEstimatorDistributionStrategy(test_util.TensorFlowTestCase, + parameterized.TestCase): def setUp(self): self._base_dir = os.path.join(self.get_temp_dir(), @@ -325,17 +363,18 @@ class TestEstimatorDistributionStrategy(test_util.TensorFlowTestCase): gfile.MakeDirs(self._base_dir) self._config = run_config_lib.RunConfig( tf_random_seed=_RANDOM_SEED, model_dir=self._base_dir) - self._dist = mirrored_strategy.MirroredStrategy( - devices=['/device:GPU:0', '/device:GPU:1']) def tearDown(self): writer_cache.FileWriterCache.clear() if os.path.isdir(self._base_dir): gfile.DeleteRecursively(self._base_dir) - def test_train_functional_with_distribution_strategy(self): - dist = mirrored_strategy.MirroredStrategy( - devices=['/device:GPU:0', '/device:GPU:1']) + @combinations.generate(combinations.combine( + distribution=[ + combinations.mirrored_strategy_with_two_gpus, + combinations.core_mirrored_strategy_with_two_gpus], + mode=['graph'])) + def test_train_functional_with_distribution_strategy(self, distribution): keras_model = simple_functional_model() keras_model.compile( loss='categorical_crossentropy', @@ -343,8 +382,8 @@ class TestEstimatorDistributionStrategy(test_util.TensorFlowTestCase): optimizer=rmsprop.RMSPropOptimizer(learning_rate=0.01)) config = run_config_lib.RunConfig(tf_random_seed=_RANDOM_SEED, model_dir=self._base_dir, - train_distribute=dist, - eval_distribute=dist) + train_distribute=distribution, + eval_distribute=distribution) with self.cached_session(): est_keras = keras_lib.model_to_estimator( keras_model=keras_model, config=config) @@ -358,9 +397,12 @@ class TestEstimatorDistributionStrategy(test_util.TensorFlowTestCase): writer_cache.FileWriterCache.clear() gfile.DeleteRecursively(self._config.model_dir) - def test_train_sequential_with_distribution_strategy(self): - dist = mirrored_strategy.MirroredStrategy( - devices=['/device:GPU:0', '/device:GPU:1']) + @combinations.generate(combinations.combine( + distribution=[ + combinations.mirrored_strategy_with_two_gpus, + combinations.core_mirrored_strategy_with_two_gpus], + mode=['graph'])) + def test_train_sequential_with_distribution_strategy(self, distribution): keras_model = simple_sequential_model() keras_model.compile( loss='categorical_crossentropy', @@ -368,7 +410,7 @@ class TestEstimatorDistributionStrategy(test_util.TensorFlowTestCase): optimizer=rmsprop.RMSPropOptimizer(learning_rate=0.01)) config = run_config_lib.RunConfig(tf_random_seed=_RANDOM_SEED, model_dir=self._base_dir, - train_distribute=dist) + train_distribute=distribution) with self.cached_session(): est_keras = keras_lib.model_to_estimator( keras_model=keras_model, config=config) @@ -382,7 +424,12 @@ class TestEstimatorDistributionStrategy(test_util.TensorFlowTestCase): writer_cache.FileWriterCache.clear() gfile.DeleteRecursively(self._config.model_dir) - def test_multi_inputs_multi_outputs_with_input_fn_as_dict(self): + @combinations.generate(combinations.combine( + distribution=[ + combinations.mirrored_strategy_with_two_gpus, + combinations.core_mirrored_strategy_with_two_gpus], + mode=['graph'])) + def test_multi_inputs_multi_outputs_with_input_fn_as_dict(self, distribution): train_data, test_data = get_multi_inputs_multi_outputs_data() def train_input_fn(): @@ -412,14 +459,14 @@ class TestEstimatorDistributionStrategy(test_util.TensorFlowTestCase): output_dict)).batch(16) self.do_test_multi_inputs_multi_outputs_with_input_fn( - train_input_fn, eval_input_fn) + distribution, train_input_fn, eval_input_fn) - def do_test_multi_inputs_multi_outputs_with_input_fn(self, train_input_fn, - eval_input_fn): + def do_test_multi_inputs_multi_outputs_with_input_fn( + self, distribution, train_input_fn, eval_input_fn): config = run_config_lib.RunConfig( tf_random_seed=_RANDOM_SEED, model_dir=self._base_dir, - train_distribute=self._dist) + train_distribute=distribution) with self.cached_session(): model = multi_inputs_multi_outputs_model() est_keras = keras_lib.model_to_estimator(keras_model=model, config=config) @@ -429,9 +476,12 @@ class TestEstimatorDistributionStrategy(test_util.TensorFlowTestCase): eval_results = est_keras.evaluate(input_fn=eval_input_fn, steps=1) self.assertLess(eval_results['loss'], baseline_eval_results['loss']) - def test_keras_optimizer_with_distribution_strategy(self): - dist = mirrored_strategy.MirroredStrategy( - devices=['/device:GPU:0', '/device:GPU:1']) + @combinations.generate(combinations.combine( + distribution=[ + combinations.mirrored_strategy_with_two_gpus, + combinations.core_mirrored_strategy_with_two_gpus], + mode=['graph'])) + def test_keras_optimizer_with_distribution_strategy(self, distribution): keras_model = simple_sequential_model() keras_model.compile( loss='categorical_crossentropy', @@ -439,7 +489,7 @@ class TestEstimatorDistributionStrategy(test_util.TensorFlowTestCase): config = run_config_lib.RunConfig(tf_random_seed=_RANDOM_SEED, model_dir=self._base_dir, - train_distribute=dist) + train_distribute=distribution) with self.cached_session(): est_keras = keras_lib.model_to_estimator(keras_model=keras_model, config=config) @@ -455,7 +505,7 @@ class TestEstimatorDistributionStrategy(test_util.TensorFlowTestCase): class TestDistributionStrategyWithNumpyArrays(test.TestCase, parameterized.TestCase): - @combinations.generate(strategy_combinations()) + @combinations.generate(strategy_for_numpy_input_combinations()) def test_creating_var_with_numpy_arrays(self, distribution): with self.cached_session(): x = np.asarray(np.random.random((64, 3)), dtype=np.float32) @@ -464,84 +514,135 @@ class TestDistributionStrategyWithNumpyArrays(test.TestCase, # Verify that the numpy value is copied to the variable. self.assertAllEqual(x, val) - def test_calculating_batch_params(self): - # This verifies that we calculate the number of steps when the batch size - # is specified. + @combinations.generate(strategy_for_numpy_input_combinations()) + def test_calculating_input_params_no_steps_no_batch_size(self, distribution): + # Calculate the per_replica_batch_size scaling factor for strategies + # that use per_core_batch_size + replica_scale_factor = 1.0 + if not distributed_training_utils.global_batch_size_supported(distribution): + replica_scale_factor = distribution.num_replicas_in_sync + with self.cached_session(): - # 64 is the number of input samples. - inputs = np.zeros((64, 3), dtype=np.float32) - # The number of replicas is equal to 3. - strategy = mirrored_strategy.MirroredStrategy(['/device:GPU:0', - '/device:CPU:0', - '/device:GPU:1']) - - with self.assertRaisesRegexp(ValueError, 'Please specify a batch_size ' - 'that is smaller than'): - # The batch size(128) is larger than the number of input - # samples(64). - distributed_training_utils.get_input_batch_params(inputs, - 128, - strategy) - - with self.assertRaisesRegexp(ValueError, 'is smaller than the number ' - 'of replicas'): - # The batch size(32) * num_replicas_in_sync(3) is 96 which is greater - # than the number of input samples(64). - distributed_training_utils.get_input_batch_params(inputs, - 32, - strategy) - - # The number of replicas now is equal to 2. - strategy = mirrored_strategy.MirroredStrategy(['/device:GPU:0', - '/device:CPU:0']) - # 32 is the batch size per replica. - steps = distributed_training_utils.get_input_batch_params(inputs, - 32, - strategy) - # The number of batches is the ratio of input samples(64) to - # batch size(32) which is 2. The number of steps(1) is the ratio of - # number of batches(2) to the number of replicas(2). + # Input samples of different sizes + input_20_samples = np.zeros((20, 3), dtype=np.float32) + input_63_samples = np.zeros((63, 3), dtype=np.float32) + input_64_samples = np.zeros((64, 3), dtype=np.float32) + + # Default global batch size 32 for input with 64 samples run in 2 steps + steps, batch_size = distributed_training_utils.get_input_params( + distribution, input_64_samples, steps=None, batch_size=None) + self.assertEqual(batch_size, 32 // replica_scale_factor) + self.assertEqual(steps, 2) + + # Computed global batch size 20 is lower than 32 if we pass less samples. + steps, batch_size = distributed_training_utils.get_input_params( + distribution, input_20_samples, steps=None, batch_size=None) + self.assertEqual(batch_size, 20 // replica_scale_factor) + self.assertEqual(steps, 1) + + # Default global batch size 32 cannot be used with 63 samples. + with self.assertRaisesRegexp(ValueError, 'not divisible by batch size'): + distributed_training_utils.get_input_params( + distribution, input_63_samples, steps=None, batch_size=None) + + @combinations.generate(strategy_for_numpy_input_combinations()) + def test_calculating_input_params_with_steps_no_batch_size(self, + distribution): + # Calculate the per_replica_batch_size scaling factor for strategies + # that use per_core_batch_size + replica_scale_factor = 1.0 + if not distributed_training_utils.global_batch_size_supported(distribution): + replica_scale_factor = distribution.num_replicas_in_sync + + with self.cached_session(): + # Input samples of different sizes + input_63_samples = np.zeros((63, 3), dtype=np.float32) + input_64_samples = np.zeros((64, 3), dtype=np.float32) + + # Computed global batch size is correct for number of specified 1 step + steps, batch_size = distributed_training_utils.get_input_params( + distribution, input_64_samples, steps=1, batch_size=None) + self.assertEqual(batch_size, 64 // replica_scale_factor) self.assertEqual(steps, 1) - # 16 is the batch size per replica. - steps = distributed_training_utils.get_input_batch_params(inputs, - 16, - strategy) - # The number of batches is the ratio of input samples(64) to - # batch size(16) which is 4. The number of steps(2) is the ratio of - # number of batches(4) to the number of replicas(2). + # Computed global batch size is correct for number of specified 2 steps + steps, batch_size = distributed_training_utils.get_input_params( + distribution, input_64_samples, steps=2, batch_size=None) + self.assertEqual(batch_size, 32 // replica_scale_factor) self.assertEqual(steps, 2) - def test_calculating_batch_size(self): + # All samples can not be consumed in specified number of steps + with self.assertRaisesRegexp(ValueError, 'not divisible by steps'): + distributed_training_utils.get_input_params( + distribution, input_63_samples, steps=2, batch_size=None) + + # This cases is different for different strategies due to the + # difference in supported batch size being global or per-replica. + if replica_scale_factor == 1: + # Computed global batch size is correct even if not sharadable + steps, batch_size = distributed_training_utils.get_input_params( + distribution, input_63_samples, steps=3, batch_size=None) + self.assertEqual(batch_size, 21) + self.assertEqual(steps, 3) + else: + # Computed global batch size can not be sharded across replicas + with self.assertRaisesRegexp(ValueError, 'could not be sharded evenly ' + 'across the sync replicas'): + distributed_training_utils.get_input_params( + distribution, input_63_samples, steps=1, batch_size=None) + + @combinations.generate(strategy_for_numpy_input_combinations()) + def test_calculating_input_params_no_steps_with_batch_size(self, + distribution): + # Calculate the per_replica_batch_size scaling factor for strategies + # that use per_core_batch_size + replica_scale_factor = 1.0 + if not distributed_training_utils.global_batch_size_supported(distribution): + replica_scale_factor = distribution.num_replicas_in_sync + with self.cached_session(): - # 64 is the number of input samples. - inputs = np.zeros((64, 3), dtype=np.float32) - targets = np.zeros((64, 4), dtype=np.float32) + input_64_samples = np.zeros((64, 3), dtype=np.float32) + + # Computed steps is correct for specified batch size + steps, batch_size = distributed_training_utils.get_input_params( + distribution, input_64_samples, steps=None, batch_size=16) + self.assertEqual(batch_size, 16) + self.assertEqual(steps, 4 // replica_scale_factor) + + # Computed steps is correct for specified batch size + steps, batch_size = distributed_training_utils.get_input_params( + distribution, input_64_samples, steps=None, batch_size=32) + self.assertEqual(batch_size, 32) + self.assertEqual(steps, 2 // replica_scale_factor) + + # Number of samples is not divisible by the global batch size + with self.assertRaisesRegexp(ValueError, 'not divisible by batch size'): + distributed_training_utils.get_input_params( + distribution, input_64_samples, steps=None, batch_size=20) + + # Number of samples is not divisible by the global batch size + with self.assertRaisesRegexp(ValueError, 'not divisible by batch size'): + distributed_training_utils.get_input_params( + distribution, input_64_samples, steps=None, batch_size=3) + + @combinations.generate(strategy_for_numpy_input_combinations()) + def test_calculating_input_params_with_steps_with_batch_size(self, + distribution): + with self.cached_session(): + input_64_samples = np.zeros((64, 3), dtype=np.float32) - model = get_model() - optimizer = gradient_descent.GradientDescentOptimizer(0.001) - loss = 'mse' - strategy = mirrored_strategy.MirroredStrategy(['/device:GPU:0', - '/device:CPU:0']) - strategy._require_static_shapes = True - - model.compile(optimizer, loss, distribute=strategy) - iterator = model._distribution_standardize_user_data(inputs, - targets, - batch_size=None, - check_steps=True, - steps_name='steps', - steps=3) - - # The global batch size(21) across all replicas is the ratio of the input - # samples(64) to the steps(3). - # The batch size(10) per device is the ratio of the global batch size(21) - # to the number of replicas(2). - # The global batch size and batch size are rounded integer values. - self.assertEqual(10, distributed_training_utils.get_batch_dimension( - iterator._iterator)) - - @combinations.generate(strategy_combinations()) + # No change to steps and batch size if both specified and feasible + steps, batch_size = distributed_training_utils.get_input_params( + distribution, input_64_samples, steps=5, batch_size=3) + self.assertEqual(batch_size, 3) + self.assertEqual(steps, 5) + + # Number of samples is less than global batch size * steps + with self.assertRaisesRegexp(ValueError, 'less than samples required'): + distributed_training_utils.get_input_params( + distribution, input_64_samples, steps=10, batch_size=13) + + @combinations.generate(strategy_for_numpy_input_combinations()) def test_calling_model_with_numpy_arrays(self, distribution): with self.cached_session(): model = get_model() @@ -572,7 +673,7 @@ class TestDistributionStrategyWithNumpyArrays(test.TestCase, # with batch_size model.predict(inputs, batch_size=8) - @combinations.generate(strategy_combinations()) + @combinations.generate(strategy_for_numpy_input_combinations()) def test_calling_model_with_nested_numpy_arrays(self, distribution): with self.cached_session(): model = multi_input_output_model() @@ -606,21 +707,22 @@ class TestDistributionStrategyWithNumpyArrays(test.TestCase, # with batch_size model.predict(inputs, batch_size=8) - @combinations.generate(strategy_minus_tpu_combinations()) + @combinations.generate(combinations.combine( + distribution=strategies_minus_tpu, mode=['graph'])) def test_numpy_with_sample_weights(self, distribution): model = get_model() optimizer = rmsprop.RMSPropOptimizer(learning_rate=0.001) loss = 'mse' model.compile(optimizer, loss, distribute=distribution) - inputs = np.zeros((10, 3), np.float32) - targets = np.zeros((10, 4), np.float32) - sample_weights = np.ones((10), np.float32) + inputs = np.zeros((20, 3), np.float32) + targets = np.zeros((20, 4), np.float32) + sample_weights = np.ones((20), np.float32) model.fit(inputs, targets, sample_weight=sample_weights, epochs=1, steps_per_epoch=2, verbose=1) - @combinations.generate(strategy_combinations()) + @combinations.generate(strategy_for_numpy_input_combinations()) def test_flatten_predict_outputs(self, distribution): with self.cached_session(): model = multi_input_output_model() @@ -638,7 +740,7 @@ class TestDistributionStrategyWithNumpyArrays(test.TestCase, # `predict` a list that is equal in length to the number of model outputs. # In this test our model has two outputs and each element of `outs` # corresponds to all the samples of one of the model outputs. - self.assertEqual(2, len(outs)) + self.assertLen(outs, 2) # Each of the output samples have a dimension of 7. We should process all # the available input samples(6). self.assertAllEqual([6, 7], outs[0].shape) @@ -648,7 +750,7 @@ class TestDistributionStrategyWithNumpyArrays(test.TestCase, class TestDistributionStrategyWithDatasets(test.TestCase, parameterized.TestCase): - @combinations.generate(strategy_combinations()) + @combinations.generate(all_strategy_combinations()) def test_calling_model_on_same_dataset(self, distribution): with self.cached_session(): model = get_model() @@ -667,7 +769,7 @@ class TestDistributionStrategyWithDatasets(test.TestCase, validation_data=dataset, validation_steps=2) model.predict(get_predict_dataset(distribution), steps=2) - @combinations.generate(strategy_combinations()) + @combinations.generate(all_strategy_combinations()) def test_model_interleaved_eval_same_as_direct_eval(self, distribution): with self.cached_session(): user_controlled_model = get_model() @@ -710,16 +812,20 @@ class TestDistributionStrategyWithDatasets(test.TestCase, # TODO(priyag): Enable this test for TPU. Currently tuples/dict don't work # as clone_model's input_tensors argument only seems to accept list and not # tuples or dict. - def test_fit_with_tuple_and_dict_dataset_inputs(self): + + @combinations.generate(combinations.combine( + distribution=[ + combinations.mirrored_strategy_with_gpu_and_cpu, + combinations.core_mirrored_strategy_with_gpu_and_cpu], + mode=['graph', 'eager'])) + def test_fit_with_tuple_and_dict_dataset_inputs(self, distribution): with self.cached_session(): model = multi_input_output_model() optimizer = gradient_descent.GradientDescentOptimizer(learning_rate=0.001) loss = 'mse' metrics = ['mae', keras.metrics.CategoricalAccuracy()] - strategy = mirrored_strategy.MirroredStrategy(['/device:GPU:0', - '/device:CPU:0']) - model.compile(optimizer, loss, metrics=metrics, distribute=strategy) + model.compile(optimizer, loss, metrics=metrics, distribute=distribution) input_a_np = np.random.random((10, 3)) input_b_np = np.random.random((10, 5)) @@ -743,7 +849,7 @@ class TestDistributionStrategyWithDatasets(test.TestCase, model.fit(dataset_dict, epochs=1, steps_per_epoch=2, verbose=1) - @combinations.generate(strategy_combinations()) + @combinations.generate(all_strategy_combinations()) def test_fit_eval_and_predict_methods_on_dataset(self, distribution): with self.cached_session(): model = get_model() @@ -792,35 +898,48 @@ class TestDistributionStrategyWithDatasets(test.TestCase, model.evaluate(dataset, steps=2, verbose=1) model.predict(dataset, steps=2) - def test_dataset_input_shape_validation(self): + @combinations.generate(combinations.combine( + distribution=[ + combinations.mirrored_strategy_with_two_gpus, + combinations.core_mirrored_strategy_with_two_gpus], + mode=['graph', 'eager'])) + def test_dataset_wrong_input_shape(self, distribution): with self.cached_session(): model = get_model() optimizer = rmsprop.RMSPropOptimizer(learning_rate=0.001) loss = 'mse' - strategy = mirrored_strategy.MirroredStrategy(['/device:GPU:1', - '/device:GPU:0']) - - model.compile(optimizer, loss, distribute=strategy) + model.compile(optimizer, loss, distribute=distribution) - # User forgets to batch the dataset - inputs = np.zeros((10, 3), dtype=np.float32) + # Wrong input shape + inputs = np.zeros((10, 5), dtype=np.float32) targets = np.zeros((10, 4), dtype=np.float32) dataset = dataset_ops.Dataset.from_tensor_slices((inputs, targets)) dataset = dataset.repeat(100) + dataset = dataset.batch(10) - with self.assertRaisesRegexp(ValueError, 'expected input to have shape'): + with self.assertRaisesRegexp(ValueError, + 'expected input to have shape'): model.fit(dataset, epochs=1, steps_per_epoch=2, verbose=0) - # Wrong input shape - inputs = np.zeros((10, 5), dtype=np.float32) + @combinations.generate(combinations.combine( + distribution=[combinations.mirrored_strategy_with_two_gpus], + mode=['graph', 'eager'])) + def test_dataset_no_batch_input_validation(self, distribution): + with self.cached_session(): + model = get_model() + + optimizer = rmsprop.RMSPropOptimizer(learning_rate=0.001) + loss = 'mse' + model.compile(optimizer, loss, distribute=distribution) + + # User forgets to batch the dataset + inputs = np.zeros((10, 3), dtype=np.float32) targets = np.zeros((10, 4), dtype=np.float32) dataset = dataset_ops.Dataset.from_tensor_slices((inputs, targets)) dataset = dataset.repeat(100) - dataset = dataset.batch(10) - with self.assertRaisesRegexp(ValueError, - 'expected input to have shape'): + with self.assertRaisesRegexp(ValueError, 'expected input to have shape'): model.fit(dataset, epochs=1, steps_per_epoch=2, verbose=0) @combinations.generate(combinations.combine( @@ -842,7 +961,12 @@ class TestDistributionStrategyWithDatasets(test.TestCase, with self.assertRaisesRegexp(ValueError, 'requires fully defined shapes'): model.fit(dataset, epochs=1, steps_per_epoch=2, verbose=0) - def test_learning_phase_value(self): + @combinations.generate(combinations.combine( + distribution=[ + combinations.mirrored_strategy_with_two_gpus, + combinations.core_mirrored_strategy_with_two_gpus], + mode=['graph', 'eager'])) + def test_learning_phase_value(self, distribution): # TODO(anjalisridhar): Modify this test to use Lambdas since we can compare # meaningful values. Currently we don't pass the learning phase if the # Lambda layer uses the learning phase. @@ -856,15 +980,17 @@ class TestDistributionStrategyWithDatasets(test.TestCase, optimizer = gradient_descent.GradientDescentOptimizer(0.005) loss = 'mse' metrics = ['acc'] - strategy = mirrored_strategy.MirroredStrategy( - ['/device:GPU:0', '/device:GPU:1']) + model.compile(optimizer, loss, metrics=metrics, distribute=distribution) - model.compile(optimizer, loss, metrics=metrics, distribute=strategy) + batch_size = 8 + if isinstance(distribution, mirrored_strategy.CoreMirroredStrategy): + # CoreMirroredStrategy uses global batch size. + batch_size = 8 * distribution.num_replicas_in_sync inputs = np.ones((10, 1), dtype=np.float32) targets = np.ones((10, 1), dtype=np.float32) dataset = dataset_ops.Dataset.from_tensor_slices((inputs, targets)) - dataset = dataset.repeat().batch(8) + dataset = dataset.repeat().batch(batch_size) hist = model.fit(dataset, epochs=1, steps_per_epoch=20, verbose=1) self.assertAlmostEqual(hist.history['acc'][0], 0, 0) @@ -875,24 +1001,51 @@ class TestDistributionStrategyWithDatasets(test.TestCase, inputs = np.ones((10, 1), dtype=np.float32) predict_dataset = dataset_ops.Dataset.from_tensor_slices(inputs) - predict_dataset = predict_dataset.repeat().batch(5) + + predict_dataset = predict_dataset.repeat().batch(batch_size) output = model.predict(predict_dataset, steps=10) - # `predict` runs for 10 steps and in each step you process 100 samples. - ref_output = np.ones((100, 1), dtype=np.float32) + # `predict` runs for 10 steps + ref_output = np.ones((160, 1), dtype=np.float32) self.assertArrayNear(output, ref_output, 1e-1) + @combinations.generate(strategy_minus_tpu_combinations()) + def testOptimizerWithCallbacks(self, distribution): + with self.cached_session(): + model = get_model() + + optimizer = gradient_descent_keras.SGD(0.01) + loss = 'mse' + model.compile(optimizer, loss, distribute=distribution) + + dataset = get_dataset(distribution) + + def schedule(_): + return 0.001 + + model.fit(dataset, epochs=1, steps_per_epoch=2, verbose=0, + callbacks=[keras.callbacks.LearningRateScheduler(schedule)]) + grouped_models = distribution.unwrap(model._grouped_model) + with distribution.scope(): + for m in grouped_models: + self.assertAllClose(0.001, keras.backend.get_value( + m.optimizer.lr), atol=1e-05, rtol=1e-05) + class TestDistributionStrategyErrorCases(test.TestCase, parameterized.TestCase): - def test_validating_dataset_input_tensors_with_shape_mismatch(self): + @combinations.generate(combinations.combine( + distribution=[ + combinations.mirrored_strategy_with_gpu_and_cpu, + combinations.core_mirrored_strategy_with_gpu_and_cpu], + mode=['graph', 'eager'])) + def test_validating_dataset_input_tensors_with_shape_mismatch(self, + distribution): with self.cached_session(): - strategy = mirrored_strategy.MirroredStrategy(['/device:GPU:0', - '/device:CPU:0']) a = constant_op.constant([1, 2], shape=(1, 2)) b = constant_op.constant([[1, 2], [1, 2]], shape=(2, 2)) x = values.DistributedValues({'/device:CPU:0': a, '/device:GPU:0': b}) y = values.DistributedValues({'/device:CPU:0': a, '/device:GPU:0': a}) - with strategy.scope(): + with distribution.scope(): # Removed device and input tensor shape details from the error message # since the order of the device and the corresponding input tensor shape # is not deterministic over different runs. @@ -901,17 +1054,21 @@ class TestDistributionStrategyErrorCases(test.TestCase, parameterized.TestCase): 'distributed tensor inputs ' 'DistributedValues:.+'): distributed_training_utils.validate_distributed_dataset_inputs( - strategy, x, y) + distribution, x, y) - def test_validating_dataset_input_tensors_with_dtype_mismatch(self): + @combinations.generate(combinations.combine( + distribution=[ + combinations.mirrored_strategy_with_gpu_and_cpu, + combinations.core_mirrored_strategy_with_gpu_and_cpu], + mode=['graph', 'eager'])) + def test_validating_dataset_input_tensors_with_dtype_mismatch(self, + distribution): with self.cached_session(): - strategy = mirrored_strategy.MirroredStrategy(['/device:GPU:0', - '/device:CPU:0']) a = constant_op.constant([1, 2], shape=(1, 2), dtype=dtypes.int32) b = constant_op.constant([1, 2], shape=(1, 2), dtype=dtypes.float64) x = values.DistributedValues({'/device:CPU:0': a, '/device:GPU:0': b}) y = values.DistributedValues({'/device:CPU:0': a, '/device:GPU:0': a}) - with strategy.scope(): + with distribution.scope(): # Removed device and input tensor dtype details from the error message # since the order of the device and the corresponding input tensor dtype # is not deterministic over different runs. @@ -920,21 +1077,23 @@ class TestDistributionStrategyErrorCases(test.TestCase, parameterized.TestCase): 'distributed tensor inputs ' 'DistributedValues:.+'): distributed_training_utils.validate_distributed_dataset_inputs( - strategy, x, y) + distribution, x, y) - def test_unsupported_features(self): + @combinations.generate(combinations.combine( + distribution=[ + combinations.mirrored_strategy_with_two_gpus, + combinations.core_mirrored_strategy_with_two_gpus], + mode=['graph', 'eager'])) + def test_unsupported_features(self, distribution): with self.cached_session(): model = get_model() optimizer = gradient_descent.GradientDescentOptimizer(0.001) loss = 'mse' metrics = ['mae'] - strategy = mirrored_strategy.MirroredStrategy(['/device:GPU:1', - '/device:GPU:0']) - - model.compile(optimizer, loss, metrics=metrics, distribute=strategy) + model.compile(optimizer, loss, metrics=metrics, distribute=distribution) - dataset = get_dataset(strategy) + dataset = get_dataset(distribution) # Test with validation split with self.assertRaisesRegexp( @@ -969,30 +1128,33 @@ class TestDistributionStrategyErrorCases(test.TestCase, parameterized.TestCase): 'you should specify the `steps` argument'): model.predict(dataset, verbose=0) - def test_calling_with_unsupported_predefined_callbacks(self): + @combinations.generate(combinations.combine( + distribution=[ + combinations.mirrored_strategy_with_two_gpus, + combinations.core_mirrored_strategy_with_two_gpus], + mode=['graph', 'eager'])) + def test_calling_with_unsupported_predefined_callbacks(self, distribution): with self.cached_session(): model = get_model() optimizer = gradient_descent.GradientDescentOptimizer(0.001) loss = 'mse' metrics = ['mae'] - strategy = mirrored_strategy.MirroredStrategy(['/device:GPU:1', - '/device:GPU:0']) - model.compile(optimizer, loss, metrics=metrics, distribute=strategy) + model.compile(optimizer, loss, metrics=metrics, distribute=distribution) - dataset = get_dataset(strategy) + dataset = get_dataset(distribution) def schedule(_): return 0.001 with self.assertRaisesRegexp(ValueError, - 'LearningRateScheduler callback is not ' - 'supported with DistributionStrategy.'): + 'You must specify a Keras Optimizer V2 when ' + 'using'): model.fit(dataset, epochs=1, steps_per_epoch=2, verbose=0, callbacks=[keras.callbacks.LearningRateScheduler(schedule)]) with self.assertRaisesRegexp(ValueError, - 'ReduceLROnPlateau callback is not ' - 'supported with DistributionStrategy.'): + 'You must specify a Keras Optimizer V2 when ' + 'using'): model.fit(dataset, epochs=1, steps_per_epoch=2, verbose=0, callbacks=[keras.callbacks.ReduceLROnPlateau()]) with self.assertRaisesRegexp(ValueError, @@ -1003,11 +1165,17 @@ class TestDistributionStrategyErrorCases(test.TestCase, parameterized.TestCase): callbacks=[keras.callbacks.TensorBoard(histogram_freq=10)]) -class TestDistributionStrategyWithLossMasking(test.TestCase): +class TestDistributionStrategyWithLossMasking(test.TestCase, + parameterized.TestCase): # TODO(priyag): Enable all strategies for this test. Currently it does not # work for TPU due to some invalid datatype. - def test_masking(self): + @combinations.generate(combinations.combine( + distribution=[ + combinations.mirrored_strategy_with_two_gpus, + combinations.core_mirrored_strategy_with_two_gpus], + mode=['graph', 'eager'])) + def test_masking(self, distribution): with self.cached_session(): np.random.seed(1337) x = np.array([[[1], [1]], [[0], [0]]]) @@ -1016,12 +1184,9 @@ class TestDistributionStrategyWithLossMasking(test.TestCase): model.add( keras.layers.TimeDistributed( keras.layers.Dense(1, kernel_initializer='one'))) - strategy = mirrored_strategy.MirroredStrategy(['/device:GPU:1', - '/device:GPU:0']) - model.compile(loss='mse', optimizer=gradient_descent.GradientDescentOptimizer(0.01), - distribute=strategy) + distribute=distribution) y = np.array([[[1], [1]], [[1], [1]]]) dataset = dataset_ops.Dataset.from_tensor_slices((x, y)) dataset = dataset.repeat(100) @@ -1033,7 +1198,7 @@ class TestDistributionStrategyWithLossMasking(test.TestCase): class TestDistributionStrategyWithNormalizationLayer( test.TestCase, parameterized.TestCase): - @combinations.generate(strategy_combinations()) + @combinations.generate(all_strategy_combinations()) def test_batchnorm_correctness(self, distribution): with self.cached_session(): model = keras.models.Sequential() @@ -1065,7 +1230,7 @@ class TestDistributionStrategyWithNormalizationLayer( class TestDistributionStrategyCorrectness(test.TestCase, parameterized.TestCase): - @combinations.generate(strategy_combinations()) + @combinations.generate(all_strategy_combinations()) def test_metric_correctness(self, distribution): with self.cached_session(): keras.backend.set_image_data_format('channels_last') @@ -1088,22 +1253,32 @@ class TestDistributionStrategyCorrectness(test.TestCase, distribute=distribution) batch_size = 64 - batch_size //= distribution.num_replicas_in_sync + if not distributed_training_utils.global_batch_size_supported( + distribution): + batch_size //= distribution.num_replicas_in_sync train_dataset = dataset_ops.Dataset.from_tensor_slices((x_train, y_train)) train_dataset = batch_wrapper(train_dataset, batch_size, distribution) history = model.fit(x=train_dataset, epochs=1, steps_per_epoch=10) self.assertEqual(history.history['binary_accuracy'], [1.0]) - @combinations.generate(strategy_and_inputs()) - def test_correctness(self, distribution, use_numpy): + @combinations.generate(strategy_and_input_combinations()) + def test_correctness(self, distribution, use_numpy, use_validation_data): + with self.cached_session(): tolerance = 1e-5 - if isinstance(distribution, mirrored_strategy.MirroredStrategy): + if isinstance(distribution, (mirrored_strategy.MirroredStrategy, + mirrored_strategy.CoreMirroredStrategy)): # TODO(b/119257215): use the default one once the flakyness is fixed. tolerance = 1e-4 + if (use_validation_data and + not isinstance(distribution, tpu_strategy.TPUStrategy)): + # TODO(b/120435565): Enable tests with use_validation_data once the + # the underlying bug is fixed. + return + keras.backend.set_image_data_format('channels_last') np.random.seed(_RANDOM_SEED) random_seed.set_random_seed(_RANDOM_SEED) @@ -1123,49 +1298,72 @@ class TestDistributionStrategyCorrectness(test.TestCase, # This is used to initialize the model for both the distribution and # non-distribution run. In addition, we add few non-linear layers to make # it non-trivial. - model = keras.Sequential() - model.add(keras.layers.Dense(10, activation='relu', input_shape=(1,))) - model.add(keras.layers.Dense(10, activation='relu')) - model.add(keras.layers.Dense(10, activation='relu')) - model.add(keras.layers.Dense(1)) + def _create_model(): + model = keras.Sequential() + model.add(keras.layers.Dense(10, activation='relu', input_shape=(1,))) + model.add(keras.layers.Dense(10, activation='relu')) + model.add(keras.layers.Dense(10, activation='relu')) + model.add(keras.layers.Dense(1)) + return model + + model = _create_model() initial_weights = model.get_weights() + del model # avoid accident usage. - def fit_and_predict(with_distribution=None): + def fit_eval_and_predict(with_distribution=None): + model = _create_model() # We have initialized the model to the same weight for the distribution # and non-distribution run. model.set_weights(initial_weights) model.compile( loss=keras.losses.mean_squared_error, - optimizer=gradient_descent.GradientDescentOptimizer(0.5), + optimizer=gradient_descent_keras.SGD(0.5), distribute=with_distribution) training_inputs, eval_inputs, predict_inputs = ( - get_correctness_test_inputs(use_numpy, with_distribution, + get_correctness_test_inputs(use_numpy, use_validation_data, + with_distribution, x_train, y_train, x_predict)) - model.fit(**training_inputs) - eval_result = model.evaluate(**eval_inputs) + traning_history = model.fit(**training_inputs).history + + if eval_inputs is not None: + eval_result = model.evaluate(**eval_inputs) + else: + # Creates a dummy identical eval_result to be compared later. + eval_result = 1.0 + weights = model.get_weights() predict_result = model.predict(**predict_inputs) - return weights, eval_result, predict_result + return weights, traning_history, eval_result, predict_result + + wts_with_ds, history_with_ds, eval_with_ds, predict_with_ds = ( + fit_eval_and_predict(with_distribution=distribution)) - wts_with_ds, eval_with_ds, predict_with_ds = fit_and_predict( - with_distribution=distribution) - wts_without_ds, eval_without_ds, predict_without_ds = fit_and_predict( - with_distribution=None) + (wts_without_ds, history_without_ds, eval_without_ds, + predict_without_ds) = fit_eval_and_predict(with_distribution=None) - # Verify that the weights, eval results, predict outputs are the same - # within some limits of tolerance. + # Verify that the weights, training history, eval results, predict outputs + # are the same within some limits of tolerance. self.assertAllClose( - wts_with_ds, wts_without_ds, atol=tolerance, rtol=tolerance) + wts_with_ds, wts_without_ds, atol=tolerance, rtol=tolerance, + msg='Fail to assert weights after training.') + self.assertAllClose( - eval_with_ds, eval_without_ds, atol=tolerance, rtol=tolerance) + eval_with_ds, eval_without_ds, atol=tolerance, rtol=tolerance, + msg='Fail to assert eval results.') self.assertAllClose( - predict_with_ds, predict_without_ds, atol=tolerance, rtol=tolerance) - - -# TODO(priyag): Add a test for TPUStrategy with steps_per_run > 1. + predict_with_ds, predict_without_ds, atol=tolerance, rtol=tolerance, + msg='Fail to assert predict results.') + + if not (isinstance(distribution, tpu_strategy.TPUStrategy) + and distribution.extended.steps_per_run > 1): + # TODO(b/119894254): Enable this test for all cases once the underlying + # bug is fixed. + self.assertAllClose( + history_with_ds, history_without_ds, atol=tolerance, rtol=tolerance, + msg='Fail to assert training history.') if __name__ == '__main__': diff --git a/tensorflow/contrib/distribute/python/metrics_v1_test.py b/tensorflow/contrib/distribute/python/metrics_v1_test.py index c28ab41651..8ac659abe9 100644 --- a/tensorflow/contrib/distribute/python/metrics_v1_test.py +++ b/tensorflow/contrib/distribute/python/metrics_v1_test.py @@ -72,14 +72,14 @@ def _regression_dataset_fn(): "predictions": [1., .75, .25, 0.]}).repeat() -# TODO(priyag): Add TPU Strategy to this once metrics aggregate correctly using -# ReplicaLocalVariables on TPUs. Submit http://cl/208914352. def all_combinations(): return combinations.combine( distribution=[combinations.default_strategy, combinations.one_device_strategy, combinations.mirrored_strategy_with_gpu_and_cpu, - combinations.mirrored_strategy_with_two_gpus], + combinations.mirrored_strategy_with_two_gpus, + combinations.core_mirrored_strategy_with_gpu_and_cpu, + combinations.core_mirrored_strategy_with_two_gpus], mode=["graph"]) @@ -100,18 +100,19 @@ class MetricsV1Test(test.TestCase, parameterized.TestCase): if isinstance(distribution, tpu_strategy.TPUStrategy): def step_fn(ctx, inputs): value, update = distribution.call_for_each_replica( - metric_fn, args=[inputs]) + metric_fn, args=inputs) ctx.set_non_tensor_output(name="value", output=value) return distribution.group(update) ctx = distribution.run_steps_on_dataset( - step_fn, iterator, iterations=distribution.steps_per_run) + step_fn, iterator, iterations=distribution.extended.steps_per_run) update = ctx.run_op value = ctx.non_tensor_outputs["value"] # In each run, we run multiple steps, and each steps consumes as many # batches as number of replicas. batches_per_update = ( - distribution.num_replicas_in_sync * distribution.steps_per_run) + distribution.num_replicas_in_sync * + distribution.extended.steps_per_run) else: value, update = distribution.call_for_each_replica( metric_fn, iterator.get_next()) diff --git a/tensorflow/contrib/distribute/python/minimize_loss_test.py b/tensorflow/contrib/distribute/python/minimize_loss_test.py index c6562463ed..dcc9df4cda 100644 --- a/tensorflow/contrib/distribute/python/minimize_loss_test.py +++ b/tensorflow/contrib/distribute/python/minimize_loss_test.py @@ -25,6 +25,7 @@ from tensorflow.contrib.distribute.python import combinations from tensorflow.contrib.distribute.python.single_loss_example import batchnorm_example from tensorflow.contrib.distribute.python.single_loss_example import minimize_loss_example from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.distribute import reduce_util from tensorflow.python.eager import context from tensorflow.python.eager import test from tensorflow.python.framework import constant_op @@ -63,7 +64,7 @@ class MinimizeLossStepTest(test.TestCase, parameterized.TestCase): model_fn, dataset_fn, layer = minimize_loss_example( optimizer_fn, use_bias=True, use_callable_loss=use_callable_loss) - def step_fn(ctx, *inputs): + def step_fn(ctx, inputs): del ctx # Unused return distribution.group( distribution.call_for_each_replica(model_fn, args=inputs)) @@ -157,7 +158,7 @@ class MinimizeLossStepTest(test.TestCase, parameterized.TestCase): use_callable_loss=True, create_optimizer_inside_model_fn=True) - def step_fn(ctx, *inputs): + def step_fn(ctx, inputs): del ctx # Unused return distribution.group( distribution.call_for_each_replica(model_fn, args=inputs)) @@ -226,7 +227,7 @@ class MinimizeLossStepTest(test.TestCase, parameterized.TestCase): renorm=renorm, update_ops_in_replica_mode=not update_ops_in_cross_replica_mode) - def step_fn(ctx, *inputs): + def step_fn(ctx, inputs): del ctx # Unused fetches = distribution.unwrap( distribution.call_for_each_replica(model_fn, args=inputs)) @@ -285,7 +286,9 @@ class MinimizeLossStepTest(test.TestCase, parameterized.TestCase): distribution=[ combinations.one_device_strategy, combinations.mirrored_strategy_with_gpu_and_cpu, - combinations.mirrored_strategy_with_two_gpus + combinations.mirrored_strategy_with_two_gpus, + combinations.core_mirrored_strategy_with_gpu_and_cpu, + combinations.core_mirrored_strategy_with_two_gpus ]), combinations.combine( mode=["graph"], use_callable_loss=[True, False]) + @@ -321,10 +324,10 @@ class MinimizeLossStepTest(test.TestCase, parameterized.TestCase): labels = dataset_ops.Dataset.from_tensors([[6.], [21.]]) return dataset_ops.Dataset.zip((features, labels)).repeat() - def step_fn(ctx, x, y): + def step_fn(ctx, inputs): del ctx # Unused return distribution.group( - distribution.call_for_each_replica(model_fn, args=(x, y))) + distribution.call_for_each_replica(model_fn, args=inputs)) iterator = self._get_iterator(distribution.distribute_dataset(dataset_fn)) @@ -341,7 +344,7 @@ class MinimizeLossStepTest(test.TestCase, parameterized.TestCase): run_step() v = all_vars[0] - self.assertTrue(all([v is vi for vi in all_vars[1:]])) + self.assertTrue(all(v is vi for vi in all_vars[1:])) weight = numpy.squeeze(self.evaluate(v)) # Our model is: # predict = x * w @@ -402,21 +405,21 @@ class MinimizeLossStepTest(test.TestCase, parameterized.TestCase): train_op = optimizer.minimize(loss_fn) loss = loss_fn() output_context.set_last_step_output( - name="replica_loss_agg", + name="replica_loss_reduced", output=loss, - aggregation=variables_lib.VariableAggregation.MEAN) + reduce_op=reduce_util.ReduceOp.MEAN) output_context.set_non_tensor_output(key1, value1) return (train_op, loss) - def step_fn(output_context, *inputs): + def step_fn(output_context, inputs): (train_op, loss) = distribution.call_for_each_replica( model_fn, args=(output_context,) + inputs) output_context.set_last_step_output( - name="cross_replica_loss_agg", + name="cross_replica_loss_reduced", output=loss, - aggregation=variables_lib.VariableAggregation.MEAN) + reduce_op=reduce_util.ReduceOp.MEAN) output_context.set_last_step_output( - name="cross_replica_loss_noagg", + name="cross_replica_loss_not_reduced", output=loss) return distribution.group(train_op) @@ -424,16 +427,16 @@ class MinimizeLossStepTest(test.TestCase, parameterized.TestCase): def run_step(): initial_loss = lambda: constant_op.constant(1e7) - # Initial values corresponding to aggregated losses are just single - # tensors. But for non aggregated losses, we need to have initial + # Initial values corresponding to reduced losses are just single + # tensors. But for non reduced losses, we need to have initial # values that are of the same structure as non reduced losses. In # MirroredStrategy, this will be a list of losses, in TPUStrategy # it will be single tensor. Using `broadcast` followed by `unwrap` # gives us the desired initial value structure. initial_loop_values = { - "replica_loss_agg": initial_loss(), - "cross_replica_loss_agg": initial_loss(), - "cross_replica_loss_noagg": + "replica_loss_reduced": initial_loss(), + "cross_replica_loss_reduced": initial_loss(), + "cross_replica_loss_not_reduced": distribution.unwrap(distribution.broadcast(initial_loss())) } ctx = distribution.run_steps_on_dataset( @@ -443,17 +446,17 @@ class MinimizeLossStepTest(test.TestCase, parameterized.TestCase): self.assertEqual({key1: [value1]}, ctx.non_tensor_outputs) self._verify_loss_output( initial_loss(), - loss_output=ctx.last_step_outputs["replica_loss_agg"], - aggregated=True, distribution=distribution) + loss_output=ctx.last_step_outputs["replica_loss_reduced"], + reduced=True, distribution=distribution) self._verify_loss_output( initial_loss(), - loss_output=ctx.last_step_outputs["cross_replica_loss_agg"], - aggregated=True, distribution=distribution) + loss_output=ctx.last_step_outputs["cross_replica_loss_reduced"], + reduced=True, distribution=distribution) self._verify_loss_output( initial_loss(), - loss_output=ctx.last_step_outputs["cross_replica_loss_noagg"], - aggregated=False, distribution=distribution) - return (ctx.run_op, ctx.last_step_outputs["replica_loss_agg"]) + loss_output=ctx.last_step_outputs["cross_replica_loss_not_reduced"], + reduced=False, distribution=distribution) + return (ctx.run_op, ctx.last_step_outputs["replica_loss_reduced"]) self.evaluate(distribution.initialize()) if not context.executing_eagerly(): @@ -478,18 +481,16 @@ class MinimizeLossStepTest(test.TestCase, parameterized.TestCase): error_is_not_increasing = all(y <= x for x, y in zip(error, error[1:])) self.assertTrue(error_is_not_increasing) - def _verify_loss_output(self, initial_loss, loss_output, aggregated, + def _verify_loss_output(self, initial_loss, loss_output, reduced, distribution): - if not aggregated: - self.assertEqual(distribution.num_replicas_in_sync, - len(distribution.unwrap(loss_output))) - loss_output = distribution.reduce( - aggregation=variables_lib.VariableAggregation.MEAN, - value=loss_output, destinations="/device:CPU:0") - - unwrapped_output = distribution.unwrap(loss_output) - self.assertEqual(1, len(unwrapped_output)) - loss_tensor = unwrapped_output[0] + if not reduced: + self.assertLen(distribution.unwrap(loss_output), + distribution.num_replicas_in_sync) + loss_tensor = distribution.reduce(reduce_util.ReduceOp.MEAN, loss_output) + else: + unwrapped_output = distribution.unwrap(loss_output) + self.assertLen(unwrapped_output, 1) + loss_tensor = unwrapped_output[0] self.assertEqual(initial_loss.dtype, loss_tensor.dtype) self.assertEqual(initial_loss.shape, loss_tensor.shape) diff --git a/tensorflow/contrib/distribute/python/mirrored_strategy.py b/tensorflow/contrib/distribute/python/mirrored_strategy.py index 2d75024e7a..20f1a08d42 100644 --- a/tensorflow/contrib/distribute/python/mirrored_strategy.py +++ b/tensorflow/contrib/distribute/python/mirrored_strategy.py @@ -12,293 +12,35 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Class MirroredStrategy implementing DistributionStrategy.""" +"""Contrib version of MirroredStrategy.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function -import contextlib -from functools import partial -import threading +import functools -from tensorflow.contrib.distribute.python import cross_tower_ops as cross_tower_ops_lib -from tensorflow.contrib.distribute.python import shared_variable_creator -from tensorflow.contrib.distribute.python import values -from tensorflow.python import pywrap_tensorflow -from tensorflow.python.distribute import multi_worker_util -from tensorflow.python.eager import context -from tensorflow.python.eager import tape -from tensorflow.python.framework import constant_op -from tensorflow.python.framework import device as tf_device -from tensorflow.python.framework import ops -from tensorflow.python.ops import array_ops -from tensorflow.python.ops import control_flow_ops -from tensorflow.python.ops import variable_scope -from tensorflow.python.ops import variables as variables_lib -from tensorflow.python.training import coordinator -from tensorflow.python.training import device_util -from tensorflow.python.training import distribute as distribute_lib -from tensorflow.python.util import nest +from tensorflow.python.distribute import device_util +from tensorflow.python.distribute import distribute_lib +from tensorflow.python.distribute import mirrored_strategy +from tensorflow.python.distribute import values -# TODO(josh11b): Replace asserts in this file with if ...: raise ... - - -@contextlib.contextmanager -def _enter_graph(g): - if context.executing_eagerly(): - with g.as_default(), context.eager_mode(): - yield - else: - with g.as_default(): - yield - - -def _cpu_device(device): - cpu_device = tf_device.DeviceSpec.from_string(device) - cpu_device.merge_from(tf_device.DeviceSpec(device_type="CPU", device_index=0)) - return cpu_device.to_string() - - -class _RequestedStop(Exception): - pass - - -# _call_for_each_replica and _reduce_non_distributed_value are not members of -# MirroredStrategy so that they are generally not allowed to use anything -# specific to MirroredStrategy and thus can be shared with other distribution -# strategies. - - -# TODO(yuefengz): maybe create a common class for those who need to call this -# _call_for_each_replica. -def _call_for_each_replica(distribution, fn, args, kwargs): - """Run `fn` in separate threads, once per replica/worker device. - - Args: - distribution: the DistributionStrategy object. - fn: function to run (will be run once per device, each in its own thread). - args: positional arguments for `fn` - kwargs: keyword arguments for `fn`. - - Returns: - Merged return value of `fn` across all replicas. - - Raises: - RuntimeError: If fn() calls get_replica_context().merge_call() a different - number of times from the available devices. - """ - # TODO(josh11b): Add this option once we add synchronization to variable - # creation. Until then, this is pretty unsafe to use. - run_concurrently = False - if not context.executing_eagerly(): - # Needed for per-thread device, etc. contexts in graph mode. - ops.get_default_graph().switch_to_thread_local() - - coord = coordinator.Coordinator(clean_stop_exception_types=(_RequestedStop,)) - - shared_variable_store = {} - - # TODO(isaprykin): Create these threads once instead of during every run() - # call. - threads = [] - for index, d in enumerate(distribution.worker_devices): - variable_creator_fn = shared_variable_creator.make_fn( - shared_variable_store, index) - t = MirroredStrategy._MirroredReplicaThread( # pylint: disable=protected-access - distribution, coord, d, variable_creator_fn, fn, - *values.select_device(d, args), **values.select_device(d, kwargs)) - threads.append(t) - - for t in threads: - t.start() - - # When `fn` starts `should_run` event is set on _MirroredReplicaThread - # (`MRT`) threads. The execution waits until - # `MRT.has_paused` is set, which indicates that either `fn` is - # complete or a `get_replica_context().merge_call()` is called. If `fn` is - # complete, then `MRT.done` is set to True. Otherwise, arguments - # of `get_replica_context().merge_call` from all paused threads are grouped - # and the `merge_fn` is performed. Results of the - # `get_replica_context().merge_call` are then set to `MRT.merge_result`. - # Each such `get_replica_context().merge_call` call returns the - # `MRT.merge_result` for that thread when `MRT.should_run` event - # is reset again. Execution of `fn` resumes. - - try: - with coord.stop_on_exception(): - all_done = False - while not all_done and not coord.should_stop(): - done = [] - if run_concurrently: - for t in threads: - t.should_run.set() - for t in threads: - t.has_paused.wait() - t.has_paused.clear() - if coord.should_stop(): - return None - done.append(t.done) - else: - for t in threads: - t.should_run.set() - t.has_paused.wait() - t.has_paused.clear() - if coord.should_stop(): - return None - done.append(t.done) - if coord.should_stop(): - return None - all_done = all(done) - if not all_done: - if any(done): - raise RuntimeError("Some replicas made a different number of " - "replica_context().merge_call() calls.") - # get_replica_context().merge_call() case - merge_args = values.regroup({t.device: t.merge_args for t in threads}) - merge_kwargs = values.regroup( - {t.device: t.merge_kwargs for t in threads}) - # We capture the name_scope of the MRT when we call merge_fn - # to ensure that if we have opened a name scope in the MRT, - # it will be respected when executing the merge function. We only - # capture the name_scope from the first MRT and assume it is - # the same for all other MRTs. - mtt_captured_name_scope = threads[0].captured_name_scope - with ops.name_scope(mtt_captured_name_scope): - merge_result = threads[0].merge_fn(distribution, *merge_args, - **merge_kwargs) - for t in threads: - t.merge_result = values.select_device(t.device, merge_result) - finally: - for t in threads: - t.should_run.set() - coord.join(threads) - - return values.regroup({t.device: t.main_result for t in threads}) - - -def _reduce_non_distributed_value(distribution, aggregation, value, - destinations): - """Reduce a non-DistributedValue `value` to `destinations`.""" - if isinstance(value, values.DistributedValues): - raise ValueError("You are passing a `DistributedValue` to " - "`_reduce_non_distributed_value`, which is not allowed.") - - # If the same value is present on all replicas then the PerReplica value will - # be a single value. We also handle the case when `value` is a single value - # and equal to 0. - if value == 0: - return 0 - # If the aggregation type is MEAN or ONLY_FIRST_REPLICA, then this - # essentially means that the same value should be on all destinations. - if aggregation in ( - variable_scope.VariableAggregation.MEAN, - variable_scope.VariableAggregation.ONLY_FIRST_REPLICA): - return value - - cross_tower_ops_lib.validate_destinations(destinations) - # We do not support an aggregation type of SUM if the value is the same across - # all replicas. We call this as part of assign functions for MirroredVariables - # and summing up identical values across replicas is not clearly defined. - if (len(distribution.worker_devices) != 1 or - not cross_tower_ops_lib.check_destinations(destinations)): - raise ValueError("A non-DistributedValues value %s cannot be reduced with " - "the given aggregation %s." % (value, aggregation)) - # TODO(anjalisridhar): Moves these methods to a device utility file? - devices = cross_tower_ops_lib.get_devices_from(destinations) - if len(devices) == 1: - with ops.device(devices[0]): - return array_ops.identity(value) - else: - value_updates = {} - for d in devices: - with ops.device(d): - value_updates[d] = array_ops.identity(value) - return values.Mirrored(value_updates) - - -def _create_mirrored_variable(devices, real_mirrored_creator, *args, **kwargs): # pylint: disable=g-missing-docstring - # Figure out what collections this variable should be added to. - # We'll add the MirroredVariable to those collections instead. - collections = kwargs.pop("collections", None) - if collections is None: - collections = [ops.GraphKeys.GLOBAL_VARIABLES] - kwargs["collections"] = [] - - # Get synchronization value - synchronization = kwargs.get("synchronization", - variable_scope.VariableSynchronization.ON_WRITE) - if synchronization == variable_scope.VariableSynchronization.NONE: - raise ValueError("`NONE` variable synchronization mode is not " - "supported with `Mirrored` distribution strategy. Please" - " change the `synchronization` for variable: " + - kwargs["name"]) - elif synchronization == variable_scope.VariableSynchronization.ON_READ: - # Variables that are to be synced on read are replica local. - is_replica_local = True - kwargs["trainable"] = False - elif (synchronization == variable_scope.VariableSynchronization.ON_WRITE or - synchronization == variable_scope.VariableSynchronization.AUTO): - # `AUTO` synchronization for `MirroredStrategy` is `ON_WRITE`. - is_replica_local = False - else: - raise ValueError("Invalid variable synchronization mode: " + - synchronization + " for variable: " + kwargs["name"]) - - # Get aggregation value - aggregation = kwargs.pop("aggregation", - variable_scope.VariableAggregation.NONE) - if aggregation not in ( - variable_scope.VariableAggregation.NONE, - variable_scope.VariableAggregation.SUM, - variable_scope.VariableAggregation.MEAN, - variable_scope.VariableAggregation.ONLY_FIRST_REPLICA - ): - raise ValueError("Invalid variable aggregation mode: " + aggregation + - " for variable: " + kwargs["name"]) - - # Ignore user-specified caching device, not needed for mirrored variables. - kwargs.pop("caching_device", None) - - # TODO(josh11b,apassos): It would be better if variable initialization - # was never recorded on the tape instead of having to do this manually - # here. - with tape.stop_recording(): - index = real_mirrored_creator(devices, *args, **kwargs) - - if is_replica_local: - result = values.ReplicaLocalVariable( - index, index[devices[0]], aggregation) - else: - result = values.MirroredVariable(index, index[devices[0]], aggregation) - - # Add the wrapped variable to the requested collections. - # The handling of eager mode and the global step matches - # ResourceVariable._init_from_args(). - if not context.executing_eagerly(): - g = ops.get_default_graph() - # If "trainable" is True, next_creator() will add the member variables - # to the TRAINABLE_VARIABLES collection, so we manually remove - # them and replace with the MirroredVariable. We can't set - # "trainable" to False for next_creator() since that causes functions - # like implicit_gradients to skip those variables. - if kwargs.get("trainable", True): - collections.append(ops.GraphKeys.TRAINABLE_VARIABLES) - l = g.get_collection_ref(ops.GraphKeys.TRAINABLE_VARIABLES) - for v in index.values(): - if v in l: - l.remove(v) - g.add_to_collections(collections, result) - elif ops.GraphKeys.GLOBAL_STEP in collections: - ops.add_to_collections(ops.GraphKeys.GLOBAL_STEP, result) - - return result +# pylint: disable=protected-access,invalid-name +_call_for_each_replica = mirrored_strategy._call_for_each_replica +_reduce_non_distributed_value = mirrored_strategy._reduce_non_distributed_value +_create_mirrored_variable = mirrored_strategy._create_mirrored_variable +all_local_devices = mirrored_strategy.all_local_devices +CoreMirroredStrategy = mirrored_strategy.MirroredStrategy +CoreMirroredExtended = mirrored_strategy.MirroredExtended +# pylint: enable=protected-access,invalid-name class MirroredStrategy(distribute_lib.DistributionStrategy): """Mirrors vars to distribute across multiple devices and machines. + *** contrib version *** + This strategy uses one replica per device and sync replication for its multi-GPU version. @@ -353,468 +95,66 @@ class MirroredStrategy(distribute_lib.DistributionStrategy): cross_device_ops=None, auto_shard_dataset=False, cross_tower_ops=None): - super(MirroredStrategy, self).__init__() - assert not (cross_device_ops and cross_tower_ops) - self._cross_tower_ops = cross_device_ops or cross_tower_ops - self._auto_shard_dataset = auto_shard_dataset - # Remember num GPUs which might be needed by `configure` method. if num_gpus is not None and num_gpus_per_worker is not None: raise ValueError( "You cannot specify both `num_gpus` and `num_gpus_per_worker`.") - if num_gpus is not None: - self._num_gpus = num_gpus - else: - self._num_gpus = num_gpus_per_worker - - self._initialize_local(self._num_gpus, devices) - - def _initialize_local(self, num_gpus, devices): - """Initializes the object for local training.""" - self._cluster_spec = None - # Convert `num_gpus` into `devices`, shouldn't specify both. - if devices is None: - if num_gpus is None: - num_gpus = context.num_gpus() - if num_gpus == 0: - devices = ["/device:CPU:0"] - else: - devices = ["/device:GPU:%d" % d for d in range(num_gpus)] - elif num_gpus is not None: - raise ValueError("Must only specify one of `devices` and `num_gpus`.") - self._num_gpus = num_gpus - # TODO(yuefengz): consider setting the default device. - - assert devices, "Must specify at least one device." - assert len(set(devices)) == len(devices), ( - "No duplicates allowed in `devices` argument.") - # TODO(josh11b): Require at least 2 devices? - self._devices = [device_util.resolve(d) for d in devices] - self._canonical_device_set = set(self._devices) - self._device_index = values.PerReplica( - {d: i for i, d in enumerate(devices)}) - - def _initialize_multi_worker(self, num_gpus, cluster_spec): - """Initializes the object for multi-worker training.""" - cluster_spec = multi_worker_util.normalize_cluster_spec(cluster_spec) - self._cluster_spec = cluster_spec - - self._workers = [] - for job in ["chief", "worker"]: - for task in range(len(cluster_spec.as_dict().get(job, []))): - self._workers.append("/job:%s/task:%d" % (job, task)) - if num_gpus is None: - raise ValueError("`num_gpus` is required if `cluster_spec` is given.") - if num_gpus > 0: - self._worker_devices = [ - (worker, [ - device_util.canonicalize(worker + "/device:GPU:%d" % gpu) - for gpu in range(num_gpus) - ]) for worker in self._workers - ] - else: - self._worker_devices = [ - (worker, [device_util.canonicalize(worker, "/device:CPU:0")]) - for worker in self._workers - ] - - devices = nest.flatten([l for _, l in self._worker_devices]) - - # Setting `_default_device` will add a device scope in the - # distribution.scope. We set the default device to the first worker. When - # users specify device under distribution.scope by - # with tf.device("/cpu:0"): - # ... - # their ops will end up on the cpu device of its first worker, e.g. - # "/job:worker/task:0/device:CPU:0". Note this is not used in replica mode. - self._default_device = self._workers[0] - - assert devices, "Must specify at least one device." - assert len(set(devices)) == len(devices), ( - "No duplicates allowed in `devices` argument.") - # TODO(josh11b): Require at least 2 devices? - self._devices = [device_util.resolve(d) for d in devices] - self._canonical_device_set = set(self._devices) - self._device_index = values.PerReplica( - {d: i for i, d in enumerate(devices)}) + num_gpus = num_gpus_per_worker + extended = MirroredExtended(self, devices, num_gpus, + cross_device_ops or cross_tower_ops, + auto_shard_dataset) + super(MirroredStrategy, self).__init__(extended) - def _create_variable(self, next_creator, *args, **kwargs): - """Create a mirrored variable. See `DistributionStrategy.scope`.""" - colocate_with = kwargs.pop("colocate_with", None) - devices = self._get_devices_from(colocate_with) - def _real_mirrored_creator(devices, *args, **kwargs): # pylint: disable=g-missing-docstring - index = {} - for i, d in enumerate(devices): - with ops.device(d): - if i > 0: - # Give replicas meaningful distinct names: - var0name = index[devices[0]].name.split(":")[0] - # We append a / to variable names created on replicas with id > 0 to - # ensure that we ignore the name scope and instead use the given - # name as the absolute name of the variable. - kwargs["name"] = "%s/replica_%d/" % (var0name, i) - # Initialize replicas with the same value: - def initial_value_fn(device=d): - if context.executing_eagerly(): - init_value = index[devices[0]].value() - return array_ops.identity(init_value) - else: - with ops.device(device): - init_value = index[devices[0]].initial_value - return array_ops.identity(init_value) - kwargs["initial_value"] = initial_value_fn - with context.context().device_policy(context.DEVICE_PLACEMENT_SILENT): - # Don't record operations (e.g. other variable reads) during - # variable creation. - with tape.stop_recording(): - v = next_creator(*args, **kwargs) - assert not isinstance(v, values.DistributedVariable) - index[d] = v - return index +class MirroredExtended(CoreMirroredExtended): + """Implementation of (contrib) MirroredStrategy.""" - return _create_mirrored_variable(devices, _real_mirrored_creator, *args, - **kwargs) + def __init__(self, + container_strategy, + devices=None, + num_gpus_per_worker=None, + cross_device_ops=None, + auto_shard_dataset=False): + if devices is None: + devices = mirrored_strategy.all_local_devices(num_gpus_per_worker) + elif num_gpus_per_worker is not None: + raise ValueError( + "Must only specify one of `devices` and `num_gpus_per_worker`.") + super(MirroredExtended, self).__init__(container_strategy, devices, + cross_device_ops) + self._auto_shard_dataset = auto_shard_dataset - def distribute_dataset(self, dataset_fn): - if self._cluster_spec: - return values.MultiWorkerDataset( - partial(self._call_dataset_fn, dataset_fn), self._worker_devices, - auto_shard=self._auto_shard_dataset) + def _make_dataset_iterator(self, dataset): + """Make iterator from dataset without splitting the batch. + + This implementation is different than the one in + `tf.distribute.MirroredStrategy` for purposes of backward compatibility. + We treat the incoming dataset's batch size as per replica batch size. + + Args: + dataset: `tf.data.Dataset` for input. + Returns: + An `InputIterator` which returns inputs for each step of the computation. + """ + if self._local_mode: + worker = device_util.canonicalize("/device:CPU:0") + worker_device_pairs = [(worker, self._devices)] else: + worker_device_pairs = self._worker_devices + return values.DatasetIterator(dataset, worker_device_pairs) + + def _distribute_dataset(self, dataset_fn): + if self._local_mode: return values.PerReplicaDataset( self._call_dataset_fn(dataset_fn), self._devices) - - # TODO(priyag): Deal with OutOfRange errors once b/111349762 is fixed. - def _run_steps_on_dataset(self, fn, iterator, iterations, - initial_loop_values=None): - if initial_loop_values is None: - initial_loop_values = {} - initial_loop_values = nest.flatten(initial_loop_values) - - ctx = values.MultiStepContext() - def body(i, *args): - """A wrapper around `fn` to create the while loop body.""" - del args - fn_inputs = iterator.get_next() - if not isinstance(fn_inputs, tuple): - fn_inputs = (fn_inputs,) - fn_result = fn(ctx, *fn_inputs) - for (name, output) in ctx.last_step_outputs.items(): - # Convert all outputs to tensors, potentially from `DistributedValues`. - ctx.last_step_outputs[name] = self.unwrap(output) - flat_last_step_outputs = nest.flatten(ctx.last_step_outputs) - with ops.control_dependencies([fn_result]): - return [i + 1] + flat_last_step_outputs - - # We capture the control_flow_context at this point, before we run `fn` - # inside a while_loop. This is useful in cases where we might need to exit - # these contexts and get back to the outer context to do some things, for - # e.g. create an op which should be evaluated only once at the end of the - # loop on the host. One such usage is in creating metrics' value op. - self._outer_control_flow_context = ( - ops.get_default_graph()._get_control_flow_context()) # pylint: disable=protected-access - - cond = lambda i, *args: i < iterations - i = constant_op.constant(0) - loop_result = control_flow_ops.while_loop( - cond, body, [i] + initial_loop_values, name="", - parallel_iterations=1, back_prop=False, swap_memory=False, - return_same_structure=True) - del self._outer_control_flow_context - - ctx.run_op = control_flow_ops.group(loop_result) - - # Convert the last_step_outputs from a list to the original dict structure - # of last_step_outputs. - last_step_tensor_outputs = loop_result[1:] - last_step_tensor_outputs_dict = nest.pack_sequence_as( - ctx.last_step_outputs, last_step_tensor_outputs) - - for (name, aggregation) in ctx._last_step_outputs_aggregations.items(): # pylint: disable=protected-access - output = last_step_tensor_outputs_dict[name] - # For outputs that have already been aggregated, wrap them in a Mirrored - # container, else in a PerReplica container. - if aggregation is variables_lib.VariableAggregation.NONE: - last_step_tensor_outputs_dict[name] = values.regroup( - {d: t for d, t in zip(self._devices, output)}, values.PerReplica) - else: - assert len(output) == 1 - last_step_tensor_outputs_dict[name] = output[0] - - ctx._set_last_step_outputs(last_step_tensor_outputs_dict) # pylint: disable=protected-access - return ctx - - def _broadcast(self, tensor, destinations): - # TODO(josh11b): In eager mode, use one thread per device, or async mode. - return self._get_cross_tower_ops().broadcast(tensor, destinations or - self._devices) - - def _call_for_each_replica(self, fn, args, kwargs): - return _call_for_each_replica(self, fn, args, kwargs) - - def configure(self, - session_config=None, - cluster_spec=None, - task_type=None, - task_id=None): - del task_type, task_id - - if session_config: - session_config.isolate_session_state = True - - if cluster_spec: - self._initialize_multi_worker(self._num_gpus, cluster_spec) - - if self._cross_tower_ops is None: - if self._cluster_spec: - # It currently cannot detect the toplogy of remote workers. So we - # hard-code the multi-worker all-reduce algorithm for now. - if len(self._workers) == 1: - # The default is "nccl". - self._cross_tower_ops = cross_tower_ops_lib.AllReduceCrossDeviceOps() - else: - # The default is hierarchical reduce and broadcast. - self._cross_tower_ops = cross_tower_ops_lib.MultiWorkerAllReduce( - self._workers, self._num_gpus) - else: - self._cross_tower_ops = cross_tower_ops_lib.choose_the_best( - self._devices, session_config=session_config) - - def _get_cross_tower_ops(self): - if self._cross_tower_ops is None: - self._cross_tower_ops = ( - cross_tower_ops_lib.ReductionToOneDeviceCrossDeviceOps()) - return self._cross_tower_ops - - def _reduce(self, aggregation, value, destinations): - assert not isinstance(value, values.Mirrored) - if not isinstance(value, values.DistributedValues): - # This function handles reducing values that are not PerReplica or - # Mirrored values. For example, the same value could be present on all - # replicas in which case `value` would be a single value or value could - # be 0. - return _reduce_non_distributed_value(self, aggregation, value, - destinations) - if aggregation == variable_scope.VariableAggregation.ONLY_FIRST_REPLICA: - value = value.get(self._devices[0]) - if isinstance(value, (int, float)): - return value - return self.broadcast(value, destinations) - return self._get_cross_tower_ops().reduce( - aggregation, value, destinations=destinations) - - def _batch_reduce(self, aggregation, value_destination_pairs): - if aggregation == variable_scope.VariableAggregation.ONLY_FIRST_REPLICA: - return [self.broadcast(v.get(self._devices[0]), d) - for v, d in value_destination_pairs] - return self._get_cross_tower_ops().batch_reduce(aggregation, - value_destination_pairs) - - def _update(self, var, options, fn, *args, **kwargs): - # TODO(josh11b): In eager mode, use one thread per device. - assert isinstance(var, values.DistributedVariable) - should_group = options.pop("grouped") - assert not options # Validate that we are processing all of the options. - updates = {} - for d, v in var._index.items(): # pylint: disable=protected-access - name = "update_%d" % self._device_index.get(d) - with ops.device(d), distribute_lib.UpdateContext(d), ops.name_scope(name): - # If args and kwargs are not mirrored, the value is returned as is. - updates[d] = fn(v, - *values.select_device_mirrored(d, args), - **values.select_device_mirrored(d, kwargs)) - return values.update_regroup(self, updates, should_group) - - def _update_non_slot(self, colocate_with, options, fn, *args, **kwargs): - assert isinstance(colocate_with, list) - should_group = options.pop("grouped") - assert not options # Validate that we are processing all of the options. - # TODO(josh11b): In eager mode, use one thread per device. - updates = {} - for d in colocate_with: - name = "update_%d" % self._device_index.get(d) - with ops.device(d), distribute_lib.UpdateContext(d), ops.name_scope(name): - updates[d] = fn(*values.select_device_mirrored(d, args), - **values.select_device_mirrored(d, kwargs)) - return values.update_regroup(self, updates, should_group) - - def read_var(self, replica_local_var): - """Read the aggregate value of a replica-local variable.""" - if isinstance(replica_local_var, values.ReplicaLocalVariable): - return replica_local_var._get_cross_replica() # pylint: disable=protected-access - assert isinstance(replica_local_var, values.Mirrored) - return array_ops.identity(replica_local_var.get()) - - def _unwrap(self, val): - if isinstance(val, values.DistributedValues): - # Return in a deterministic order. - if set(val.devices) == self._canonical_device_set: - return [val.get(device=d) for d in self._devices] - return [val.get(device=d) for d in sorted(val.devices)] - return [val] - - def value_container(self, val): - return values.value_container(val) - - @property - def num_replicas(self): - return len(self._devices) - - @property - def num_replicas_in_sync(self): - return len(self._devices) - - def _worker_device_index(self): - return self._device_index - - @property - def worker_devices(self): - # Make a copy to prevent users from accidentally mutating our copy. - return list(self._devices) - - @property - def parameter_devices(self): - return list(self._devices) - - @property - def between_graph(self): - return False - - @property - def should_init(self): - return True - - @property - def should_checkpoint(self): - return True - - @property - def should_save_summary(self): - return True - - def non_slot_devices(self, var_list): - del var_list - return list(self._devices) - - def _get_devices_from(self, colocate_with=None): - if colocate_with is None: - return self._devices else: - return cross_tower_ops_lib.get_devices_from(colocate_with) - - class _MirroredReplicaThread(threading.Thread): - """A thread that runs() a function on a device.""" - - def __init__(self, dist, coord, device, variable_creator_fn, fn, *args, - **kwargs): - super(MirroredStrategy._MirroredReplicaThread, self).__init__() # pylint: disable=protected-access - self.coord = coord - self.distribution = dist - self.device = device - self.replica_id = dist.worker_devices.index(device) - self.variable_creator_fn = variable_creator_fn - # State needed to run and return the results of `fn`. - self.main_fn = fn - self.main_args = args - self.main_kwargs = kwargs - self.main_result = None - self.done = False - # State needed to run the next merge_call() (if any) requested via - # ReplicaContext. - self.merge_fn = None - self.merge_args = None - self.merge_kwargs = None - self.merge_result = None - self.captured_name_scope = None - # We use a thread.Event for the main thread to signal when this - # thread should start running (`should_run`), and another for - # this thread to transfer control back to the main thread - # (`has_paused`, either when it gets to a - # `get_replica_context().merge_call` or when `fn` returns). In - # either case the event starts cleared, is signaled by calling - # set(). The receiving thread waits for the signal by calling - # wait() and then immediately clearing the event using clear(). - self.should_run = threading.Event() - self.has_paused = threading.Event() - # These fields have to do with inheriting various contexts from the - # parent thread: - # pylint: disable=protected-access - self.context_mode = context.context()._eager_context.mode - if not context.context()._context_handle: - context.context()._initialize_handle_and_devices() - self.context_device_policy = ( - pywrap_tensorflow.TFE_ContextGetDevicePlacementPolicy( - context.context()._context_handle)) - self.graph = ops.get_default_graph() - self._variable_creator_stack = self.graph._variable_creator_stack[:] - self._captured_var_scope = variable_scope.get_variable_scope() - # Adding a "/" at end lets us re-enter this scope later. - self._name_scope = self.graph.get_name_scope() - if self._name_scope: - self._name_scope += "/" - if self.replica_id > 0: - if not self._name_scope: - self._name_scope = "" - self._name_scope += "replica_%d/" % self.replica_id - - def run(self): - # pylint: disable=protected-access - self.graph._variable_creator_stack = self._variable_creator_stack - self.should_run.wait() - self.should_run.clear() - try: - if self.coord.should_stop(): - return - with self.coord.stop_on_exception(), \ - context.context()._mode(self.context_mode), \ - context.context().device_policy(self.context_device_policy), \ - _enter_graph(self.graph), \ - MirroredReplicaContext(self.distribution, self.replica_id), \ - ops.device(self.device), \ - ops.name_scope(self._name_scope), \ - variable_scope.variable_scope( - self._captured_var_scope, reuse=self.replica_id > 0), \ - variable_scope.variable_creator_scope(self.variable_creator_fn): - self.main_result = self.main_fn(*self.main_args, **self.main_kwargs) - self.done = True - finally: - self.has_paused.set() - - -class MirroredReplicaContext(distribute_lib.ReplicaContext): - """ReplicaContext used in MirroredStrategy.call_for_each_replica(). - - Opened in `_MirroredReplicaThread`, to allow the user to invoke - `MirroredStrategy`'s specific implementation of `merge_call()`, - which works by delegating the function and its arguments to - the main thread (the one that invoked - `MirroredStrategy.call_for_each_replica()`). - """ - - def _merge_call(self, fn, args, kwargs): - """Delegate to the main thread to actually perform merge_call().""" - t = threading.current_thread() # a _MirroredReplicaThread - t.merge_fn = fn - t.merge_args = args - t.merge_kwargs = kwargs - t.captured_name_scope = t.graph.get_name_scope() - # Adding a "/" at end lets us re-enter this scope later. - if t.captured_name_scope: - t.captured_name_scope += "/" - t.has_paused.set() - t.should_run.wait() - t.should_run.clear() - if t.coord.should_stop(): - raise _RequestedStop() - return t.merge_result - - @property - def device(self): - raise RuntimeError("Use .devices instead") + return values.MultiWorkerDataset( + functools.partial(self._call_dataset_fn, dataset_fn), + self._worker_devices, + auto_shard=self._auto_shard_dataset) + # TODO(priyag): Delete this once all strategies use global batch size. @property - def devices(self): - distribute_lib.require_replica_context(self) - return [self._distribution_strategy.worker_devices[self._replica_id]] + def _global_batch_size(self): + return False diff --git a/tensorflow/contrib/distribute/python/mirrored_strategy_multigpu_test.py b/tensorflow/contrib/distribute/python/mirrored_strategy_multigpu_test.py index 1fd18e09c0..66512f983e 100644 --- a/tensorflow/contrib/distribute/python/mirrored_strategy_multigpu_test.py +++ b/tensorflow/contrib/distribute/python/mirrored_strategy_multigpu_test.py @@ -20,22 +20,27 @@ from __future__ import print_function import sys +from absl.testing import parameterized import numpy as np +from tensorflow.contrib.distribute.python import combinations from tensorflow.contrib.distribute.python import mirrored_strategy from tensorflow.contrib.distribute.python import multi_worker_test_base from tensorflow.contrib.distribute.python import strategy_test_lib -from tensorflow.contrib.distribute.python import values from tensorflow.core.protobuf import config_pb2 from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.distribute import device_util +from tensorflow.python.distribute import distribution_strategy_context as ds_context +from tensorflow.python.distribute import reduce_util +from tensorflow.python.distribute import values from tensorflow.python.eager import backprop from tensorflow.python.eager import context from tensorflow.python.eager import function from tensorflow.python.eager import test from tensorflow.python.framework import constant_op +from tensorflow.python.framework import func_graph from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops -from tensorflow.python.framework import test_util from tensorflow.python.keras.engine import training as keras_training from tensorflow.python.keras.layers import core as keras_core from tensorflow.python.layers import core @@ -46,8 +51,6 @@ from tensorflow.python.ops import rnn_cell_impl from tensorflow.python.ops import state_ops from tensorflow.python.ops import variable_scope from tensorflow.python.ops import variables -from tensorflow.python.training import device_util -from tensorflow.python.training import distribution_strategy_context from tensorflow.python.training import gradient_descent from tensorflow.python.training import optimizer as optimizer_lib from tensorflow.python.training import server_lib @@ -56,248 +59,229 @@ from tensorflow.python.training import server_lib GPU_TEST = "test_gpu" in sys.argv[0] -class MirroredTwoDeviceDistributionTest(strategy_test_lib.DistributionTestBase): +@combinations.generate(combinations.combine( + distribution=[ + combinations.mirrored_strategy_with_gpu_and_cpu, + combinations.mirrored_strategy_with_two_gpus, + combinations.core_mirrored_strategy_with_gpu_and_cpu, + combinations.core_mirrored_strategy_with_two_gpus], + mode=["graph", "eager"])) +class MirroredTwoDeviceDistributionTest(strategy_test_lib.DistributionTestBase, + parameterized.TestCase): - def _get_distribution_strategy(self): - devices = ["/device:CPU:0", "/device:GPU:0"] - if GPU_TEST: - self.assertGreater(context.num_gpus(), 0) - if context.num_gpus() > 1: - devices = ["/device:GPU:0", "/device:GPU:1"] - print(self.id().split(".")[-1], "devices:", ", ".join(devices)) - return mirrored_strategy.MirroredStrategy(devices) + def testMinimizeLoss(self, distribution): + if context.executing_eagerly(): + self._test_minimize_loss_eager(distribution) + else: + self._test_minimize_loss_graph(distribution) - def testMinimizeLossEager(self): - if not GPU_TEST: - self.skipTest("Not GPU test") - self._test_minimize_loss_eager(self._get_distribution_strategy()) + def testReplicaId(self, distribution): + self._test_replica_id(distribution) - def testMinimizeLossGraph(self): - soft_placement = not GPU_TEST - print("testMinimizeLossGraph soft_placement:", soft_placement) - self._test_minimize_loss_graph( - self._get_distribution_strategy(), soft_placement=soft_placement) - - def testDeviceIndex(self): - if not GPU_TEST: - self.skipTest("Not GPU test") - self._test_device_index(self._get_distribution_strategy()) - - def testReplicaId(self): - if not GPU_TEST: - self.skipTest("Not GPU test") - self._test_replica_id(self._get_distribution_strategy()) - - def testNumReplicas(self): - if not GPU_TEST: - self.skipTest("Not GPU test") - self.assertEqual(2, self._get_distribution_strategy().num_replicas) - - def testNumReplicasInSync(self): - if not GPU_TEST: - self.skipTest("Not GPU test") - self.assertEqual(2, self._get_distribution_strategy(). - num_replicas_in_sync) - - @test_util.run_in_graph_and_eager_modes - def testCallAndMergeExceptions(self): - if not GPU_TEST: - self.skipTest("Not GPU test") - self._test_call_and_merge_exceptions(self._get_distribution_strategy()) - - @test_util.run_in_graph_and_eager_modes - def testRunRegroupError(self): - - def run_fn(device_id): + def testNumReplicasInSync(self, distribution): + self.assertEqual(2, distribution.num_replicas_in_sync) + + def testCallAndMergeExceptions(self, distribution): + self._test_call_and_merge_exceptions(distribution) + + def testRunRegroupError(self, distribution): + def run_fn(): + replica_id = int(self.evaluate(_replica_id())) # Generates a list with different lengths on different devices. # Will fail in _regroup() (if more than one device). - return list(range(device_id)) - - dist = self._get_distribution_strategy() - with dist.scope(), self.assertRaises(AssertionError): - dist.call_for_each_replica(run_fn, args=(dist.worker_device_index,)) - - @test_util.run_in_graph_and_eager_modes - def testReduceToCpu(self): - if not GPU_TEST: - self.skipTest("Not GPU test") - - def run_fn(device_id): - return device_id - - dist = self._get_distribution_strategy() - with dist.scope(): - result = dist.call_for_each_replica( - run_fn, args=(dist.worker_device_index,)) - reduced = dist.reduce( - variable_scope.VariableAggregation.SUM, - result, - destinations="/device:CPU:0") - unwrapped = dist.unwrap(reduced) - self.assertEqual(1, len(unwrapped)) - expected = sum(range(len(dist.worker_devices))) - self.assertEqual(expected, self.evaluate(unwrapped[0])) - - @test_util.run_in_graph_and_eager_modes - def testReduceOnlyFirstReplicaUpdates(self): - if not GPU_TEST: - self.skipTest("Not GPU test") - - def run_fn(device_id): - return constant_op.constant(3 + 5 * device_id) - - dist = self._get_distribution_strategy() - with dist.scope(): - result = dist.call_for_each_replica( - run_fn, args=(dist.worker_device_index,)) - reduced = dist.reduce( - variable_scope.VariableAggregation.ONLY_FIRST_REPLICA, - result, - destinations="/device:CPU:0") - unwrapped = dist.unwrap(reduced) - self.assertEqual(1, len(unwrapped)) - self.assertEqual(3, self.evaluate(unwrapped[0])) - - @test_util.run_in_graph_and_eager_modes() - def testReduceToMultipleDestinations(self): - if not GPU_TEST: - self.skipTest("Not GPU test") - - devices = ["/device:GPU:0"] - if GPU_TEST: - self.assertGreater(context.num_gpus(), 0) - print(self.id().split(".")[-1], "devices:", ", ".join(devices)) - - dist = mirrored_strategy.MirroredStrategy(devices) - with dist.scope(): - reduced = dist.reduce( - variable_scope.VariableAggregation.SUM, - 1.0, - destinations=["/device:CPU:0", "/device:GPU:0"]) - unwrapped = dist.unwrap(reduced) - self.assertEqual(2, len(unwrapped)) - self.assertEqual(1.0, self.evaluate(unwrapped[0])) + return list(range(replica_id)) + + with distribution.scope(), self.assertRaises(AssertionError): + distribution.extended.call_for_each_replica(run_fn) + + def testReduceToCpu(self, distribution): + with distribution.scope(): + result = distribution.extended.call_for_each_replica(_replica_id) + reduced = distribution.reduce(reduce_util.ReduceOp.SUM, result) + expected = sum(range(distribution.num_replicas_in_sync)) + self.assertEqual(expected, self.evaluate(reduced)) + + def testMakeInputFnIterator(self, distribution): + dataset_fn = lambda: dataset_ops.Dataset.range(10) + expected_values = [[i, i+1] for i in range(0, 10, 2)] + + input_fn = self._input_fn_to_test_input_context( + dataset_fn, + expected_num_replicas_in_sync=2, + expected_num_input_pipelines=1, + expected_input_pipeline_id=0) + iterator = distribution.make_input_fn_iterator(input_fn) + self._test_input_fn_iterator(iterator, distribution.extended.worker_devices, + expected_values) + + def testGlobalStepUpdate(self, distribution): + self._test_global_step_update(distribution) + + +def one_device_combinations(): + return combinations.combine( + distribution=[ + combinations.mirrored_strategy_with_one_cpu, + combinations.mirrored_strategy_with_one_gpu, + combinations.core_mirrored_strategy_with_one_cpu, + combinations.core_mirrored_strategy_with_one_gpu], + mode=["graph", "eager"]) + + +class MirroredOneDeviceDistributionTest( + strategy_test_lib.DistributionTestBase, + parameterized.TestCase): + + @combinations.generate(one_device_combinations()) + def testMinimizeLoss(self, distribution): + if context.executing_eagerly(): + self._test_minimize_loss_eager(distribution) + else: + self._test_minimize_loss_graph(distribution) + + @combinations.generate(one_device_combinations()) + def testReplicaId(self, distribution): + self._test_replica_id(distribution) + + @combinations.generate(one_device_combinations()) + def testCallAndMergeExceptions(self, distribution): + self._test_call_and_merge_exceptions(distribution) +class MirroredStrategyVariableCreatorStackTest( + test.TestCase, parameterized.TestCase): + + @combinations.generate(combinations.combine( + distribution=[combinations.mirrored_strategy_with_gpu_and_cpu, + combinations.core_mirrored_strategy_with_gpu_and_cpu], + mode=["graph"])) + def testCreatorStacksAreThreadLocal(self, distribution): + def model_fn(): + replica_id_str = str(self.evaluate(_replica_id())) + + def thread_creator_fn(next_creator, *args, **kwargs): + return next_creator(*args, **kwargs) + ":thread_" + replica_id_str + + with variable_scope.variable_creator_scope(thread_creator_fn): + # Create a variable in this scope. + v = variable_scope.variable(1.0) + + # This will pause the current thread, and execute the other thread. + ds_context.get_replica_context().merge_call(lambda _: _) + return v + + def main_thread_creator(next_creator, *args, **kwargs): + # We are not using the underlying next_creator for test purposes. + del next_creator, args, kwargs + return "main_thread" + + with context.graph_mode(), \ + distribution.scope(), \ + variable_scope.variable_creator_scope(main_thread_creator): + result = distribution.extended.call_for_each_replica(model_fn) + result = distribution.unwrap(result) + expected = ["main_thread:thread_0", "main_thread:thread_1"] + self.assertEqual(expected, result) + + +@combinations.generate(combinations.combine( + distribution=[ + combinations.mirrored_strategy_with_gpu_and_cpu, + combinations.core_mirrored_strategy_with_gpu_and_cpu], + mode=["graph", "eager"])) class MirroredStrategyVariableCreationTest(test.TestCase): - config = config_pb2.ConfigProto() - config.allow_soft_placement = True + # TODO(priyag): Modify more tests to use this helper and check more + # properties. + def _test_mv_properties(self, var, name): + self.assertIsInstance(var, values.MirroredVariable) + self.assertEqual(name, var.name) + for d in var.devices: + self.assertEqual(d, var.get(d).device) + + def testVariableInFuncGraph(self, distribution): + def model_fn(): + v = variable_scope.variable(2.0, name="bar") + ds_context.get_replica_context().merge_call(lambda _: _) + return v - def _skip_eager_if_gpus_less_than(self, num_gpus): - if context.num_gpus() < num_gpus and context.executing_eagerly(): - self.skipTest("Enough GPUs not available for this test in eager mode.") + with func_graph.FuncGraph("fg").as_default(), distribution.scope(): + v1 = variable_scope.variable(1.0, name="foo") + v2 = distribution.extended.call_for_each_replica(model_fn) - @test_util.run_in_graph_and_eager_modes(config=config) - def testSingleVariable(self): - self._skip_eager_if_gpus_less_than(1) + self._test_mv_properties(v1, "foo:0") + self._test_mv_properties(v2, "bar:0") + def testSingleVariable(self, distribution): def model_fn(): # This variable should be created only once across the threads because of - # special variable_creator functions used by `dist.call_for_each_replica`. + # special variable_creator functions used by + # `distribution.extended.call_for_each_replica`. v = variable_scope.variable(1.0, name="foo") - distribution_strategy_context.get_replica_context().merge_call( - lambda _: _) + ds_context.get_replica_context().merge_call(lambda _: _) return v - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) - - with dist.scope(): - result = dist.call_for_each_replica(model_fn) - self.assertIsInstance(result, values.MirroredVariable) - self.assertEquals("foo:0", result.name) - - @test_util.run_in_graph_and_eager_modes(config=config) - def testUnnamedVariable(self): - self._skip_eager_if_gpus_less_than(1) + with distribution.scope(): + result = distribution.extended.call_for_each_replica(model_fn) + self._test_mv_properties(result, "foo:0") + def testUnnamedVariable(self, distribution): def model_fn(): v = variable_scope.variable(1.0) - distribution_strategy_context.get_replica_context().merge_call( - lambda _: _) + ds_context.get_replica_context().merge_call(lambda _: _) return v - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) - - with dist.scope(): - result = dist.call_for_each_replica(model_fn) - self.assertIsInstance(result, values.MirroredVariable) - # Default name of "Variable" will be used. - self.assertEquals("Variable:0", result.name) - - @test_util.run_in_graph_and_eager_modes(config=config) - def testMultipleVariables(self): - self._skip_eager_if_gpus_less_than(1) + with distribution.scope(): + result = distribution.extended.call_for_each_replica(model_fn) + self._test_mv_properties(result, "Variable:0") + def testMultipleVariables(self, distribution): def model_fn(): vs = [] for i in range(5): vs.append(variable_scope.variable(1.0, name="foo" + str(i))) - distribution_strategy_context.get_replica_context().merge_call( - lambda _: _) + ds_context.get_replica_context().merge_call(lambda _: _) return vs - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) - - with dist.scope(): - result = dist.call_for_each_replica(model_fn) + with distribution.scope(): + result = distribution.extended.call_for_each_replica(model_fn) for i, v in enumerate(result): - self.assertIsInstance(v, values.MirroredVariable) - self.assertEquals("foo" + str(i) + ":0", v.name) - - @test_util.run_in_graph_and_eager_modes(config=config) - def testMultipleVariablesWithSameCanonicalName(self): - self._skip_eager_if_gpus_less_than(1) + self._test_mv_properties(v, "foo" + str(i) + ":0") + def testMultipleVariablesWithSameCanonicalName(self, distribution): def model_fn(): vs = [] vs.append(variable_scope.variable(1.0, name="foo/bar")) vs.append(variable_scope.variable(1.0, name="foo_1/bar")) vs.append(variable_scope.variable(1.0, name="foo_1/bar_1")) vs.append(variable_scope.variable(1.0, name="foo/bar_1")) - distribution_strategy_context.get_replica_context().merge_call( - lambda _: _) + ds_context.get_replica_context().merge_call(lambda _: _) return vs - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) - - with dist.scope(): - result = dist.call_for_each_replica(model_fn) + with distribution.scope(): + result = distribution.extended.call_for_each_replica(model_fn) for v in result: self.assertIsInstance(v, values.MirroredVariable) - self.assertEquals(4, len(result)) - self.assertEquals("foo/bar:0", result[0].name) - self.assertEquals("foo_1/bar:0", result[1].name) - self.assertEquals("foo_1/bar_1:0", result[2].name) - self.assertEquals("foo/bar_1:0", result[3].name) - - @test_util.run_in_graph_and_eager_modes(config=config) - def testVariableWithSameCanonicalNameAcrossThreads(self): - self._skip_eager_if_gpus_less_than(1) - - def model_fn(device_id): - v = variable_scope.variable(1.0, name="foo_" + str(device_id)) - distribution_strategy_context.get_replica_context().merge_call( - lambda _: _) - return v + self.assertEqual(4, len(result)) + self.assertEqual("foo/bar:0", result[0].name) + self.assertEqual("foo_1/bar:0", result[1].name) + self.assertEqual("foo_1/bar_1:0", result[2].name) + self.assertEqual("foo/bar_1:0", result[3].name) - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) + def testVariableWithSameCanonicalNameAcrossThreads(self, distribution): + def model_fn(): + replica_id = self.evaluate(_replica_id()) + v = variable_scope.variable(1.0, name="foo_" + str(replica_id)) + ds_context.get_replica_context().merge_call(lambda _: _) + return v - with dist.scope(): - result = dist.call_for_each_replica( - model_fn, args=(dist.worker_device_index,)) + with distribution.scope(): + result = distribution.extended.call_for_each_replica(model_fn) self.assertIsInstance(result, values.MirroredVariable) # The resulting mirrored variable will use the name from the first device. - self.assertEquals("foo_0:0", result.name) + self.assertEqual("foo_0:0", result.name) - @test_util.run_in_graph_and_eager_modes(config=config) - def testWithLayers(self): - self._skip_eager_if_gpus_less_than(1) + def testWithLayers(self, distribution): def model_fn(features): with variable_scope.variable_scope("common"): layer1 = core.Dense(1) @@ -305,17 +289,14 @@ class MirroredStrategyVariableCreationTest(test.TestCase): layer2 = core.Dense(1) layer2(features) # This will pause the current thread, and execute the other thread. - distribution_strategy_context.get_replica_context().merge_call( - lambda _: _) + ds_context.get_replica_context().merge_call(lambda _: _) layer3 = core.Dense(1) layer3(features) return [(layer1.kernel, layer1.bias), (layer2.kernel, layer2.bias), (layer3.kernel, layer3.bias)] - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) - ds = dist.distribute_dataset( + ds = distribution.distribute_dataset( lambda: dataset_ops.Dataset.from_tensors([[1.]]).repeat(10)) if context.executing_eagerly(): iterator = ds.make_one_shot_iterator() @@ -325,26 +306,23 @@ class MirroredStrategyVariableCreationTest(test.TestCase): features = iterator.get_next() - with dist.scope(): - result = dist.call_for_each_replica(model_fn, args=(features,)) + with distribution.scope(): + result = distribution.extended.call_for_each_replica( + model_fn, args=(features,)) suffixes = ["", "_1", "_2"] for (kernel, bias), suffix in zip(result, suffixes): self.assertIsInstance(kernel, values.MirroredVariable) - self.assertEquals("common/dense" + suffix + "/kernel:0", kernel.name) + self.assertEqual("common/dense" + suffix + "/kernel:0", kernel.name) self.assertIsInstance(bias, values.MirroredVariable) - self.assertEquals("common/dense" + suffix + "/bias:0", bias.name) - - @test_util.run_in_graph_and_eager_modes(config=config) - def testWithVariableAndVariableScope(self): - self._skip_eager_if_gpus_less_than(1) + self.assertEqual("common/dense" + suffix + "/bias:0", bias.name) + def testWithVariableAndVariableScope(self, distribution): def model_fn(): v0 = variable_scope.variable(1.0, name="var0", aggregation=None) with variable_scope.variable_scope("common"): v1 = variable_scope.variable(1.0, name="var1") # This will pause the current thread, and execute the other thread. - distribution_strategy_context.get_replica_context().merge_call( - lambda _: _) + ds_context.get_replica_context().merge_call(lambda _: _) v2 = variable_scope.variable( 1.0, name="var2", @@ -358,37 +336,31 @@ class MirroredStrategyVariableCreationTest(test.TestCase): return v0, v1, v2, v3 - devices = ["/device:CPU:0", "/device:GPU:0"] - dist = mirrored_strategy.MirroredStrategy(devices) - with dist.scope(): + with distribution.scope(): v = variable_scope.variable(1.0, name="var-main0") - self.assertEquals("var-main0:0", v.name) + self.assertEqual("var-main0:0", v.name) - result = dist.call_for_each_replica(model_fn) - self.assertEquals(4, len(result)) + result = distribution.extended.call_for_each_replica(model_fn) + self.assertEqual(4, len(result)) v0, v1, v2, v3 = result self.assertIsInstance(v0, values.MirroredVariable) - self.assertEquals("var0:0", v0.name) + self.assertEqual("var0:0", v0.name) self.assertIsInstance(v1, values.MirroredVariable) - self.assertEquals("common/var1:0", v1.name) + self.assertEqual("common/var1:0", v1.name) self.assertIsInstance(v2, values.ReplicaLocalVariable) - self.assertEquals("common/var2:0", v2.name) - self.assertEquals(variable_scope.VariableAggregation.SUM, v2.aggregation) + self.assertEqual("common/var2:0", v2.name) + self.assertEqual(variable_scope.VariableAggregation.SUM, v2.aggregation) self.assertIsInstance(v3, values.MirroredVariable) - self.assertEquals("common/var3:0", v3.name) - self.assertEquals(variable_scope.VariableAggregation.MEAN, v3.aggregation) - - @test_util.run_in_graph_and_eager_modes(config=config) - def testWithGetVariableAndVariableScope(self): - self._skip_eager_if_gpus_less_than(1) + self.assertEqual("common/var3:0", v3.name) + self.assertEqual(variable_scope.VariableAggregation.MEAN, v3.aggregation) + def testWithGetVariableAndVariableScope(self, distribution): def model_fn(): v0 = variable_scope.get_variable("var0", [1]) with variable_scope.variable_scope("common"): v1 = variable_scope.get_variable("var1", [1]) # This will pause the current thread, and execute the other thread. - distribution_strategy_context.get_replica_context().merge_call( - lambda _: _) + ds_context.get_replica_context().merge_call(lambda _: _) v2 = variable_scope.get_variable( "var2", [1], synchronization=variable_scope.VariableSynchronization.ON_READ, @@ -400,33 +372,28 @@ class MirroredStrategyVariableCreationTest(test.TestCase): return v0, v1, v2, v3 - devices = ["/device:CPU:0", "/device:GPU:0"] - dist = mirrored_strategy.MirroredStrategy(devices) - with dist.scope(): + with distribution.scope(): with variable_scope.variable_scope("main"): v = variable_scope.get_variable("var-main0", [1]) - self.assertEquals("main/var-main0:0", v.name) + self.assertEqual("main/var-main0:0", v.name) - result = dist.call_for_each_replica(model_fn) - self.assertEquals(4, len(result)) + result = distribution.extended.call_for_each_replica(model_fn) + self.assertEqual(4, len(result)) v0, v1, v2, v3 = result self.assertIsInstance(v0, values.MirroredVariable) - self.assertEquals("main/var0:0", v0.name) + self.assertEqual("main/var0:0", v0.name) self.assertIsInstance(v1, values.MirroredVariable) - self.assertEquals("main/common/var1:0", v1.name) + self.assertEqual("main/common/var1:0", v1.name) self.assertIsInstance(v2, values.ReplicaLocalVariable) - self.assertEquals("main/common/var2:0", v2.name) - self.assertEquals(variable_scope.VariableAggregation.SUM, - v2.aggregation) + self.assertEqual("main/common/var2:0", v2.name) + self.assertEqual(variable_scope.VariableAggregation.SUM, + v2.aggregation) self.assertIsInstance(v3, values.MirroredVariable) - self.assertEquals("main/common/var3:0", v3.name) - self.assertEquals(variable_scope.VariableAggregation.MEAN, - v3.aggregation) - - @test_util.run_in_graph_and_eager_modes(config=config) - def testOnlyFirstReplicaUpdatesVariables(self): - self._skip_eager_if_gpus_less_than(1) + self.assertEqual("main/common/var3:0", v3.name) + self.assertEqual(variable_scope.VariableAggregation.MEAN, + v3.aggregation) + def testOnlyFirstReplicaUpdatesVariables(self, distribution): def create_fn(): aggregation = variable_scope.VariableAggregation.ONLY_FIRST_REPLICA v0 = variable_scope.variable( @@ -442,71 +409,73 @@ class MirroredStrategyVariableCreationTest(test.TestCase): return v0, v1 devices = ["/device:GPU:0", "/device:CPU:0"] - dist = mirrored_strategy.MirroredStrategy(devices) - with dist.scope(): - v0, v1 = dist.call_for_each_replica(create_fn) + with distribution.scope(): + v0, v1 = distribution.extended.call_for_each_replica(create_fn) self.evaluate(v0.initializer) self.assertEqual(2.0, self.evaluate(v0.get(devices[0]))) self.assertEqual(2.0, self.evaluate(v0.get(devices[1]))) - self.assertEqual(2.0, self.evaluate(dist.read_var(v0))) + self.assertEqual(2.0, self.evaluate(distribution.extended.read_var(v0))) self.evaluate(v1.initializer) self.assertEqual(3.0, self.evaluate(v1.get(devices[0]))) self.assertEqual(3.0, self.evaluate(v1.get(devices[1]))) - self.assertEqual(3.0, self.evaluate(dist.read_var(v1))) + self.assertEqual(3.0, self.evaluate(distribution.extended.read_var(v1))) + + def replica_id_plus_one(): + return math_ops.cast(_replica_id() + 1, dtype=dtypes.float32) # Update using the assign_add member function. - def update_member_fn(device_id): - update0 = v0.assign_add(5.0 * (device_id + 1)) - update1 = v1.assign_add(7.0 * (device_id + 1)) + def update_member_fn(): + update0 = v0.assign_add(5.0 * replica_id_plus_one()) + update1 = v1.assign_add(7.0 * replica_id_plus_one()) return update0, update1 - update0a, update1a = dist.call_for_each_replica( - update_member_fn, args=(dist.worker_device_index,)) + update0a, update1a = distribution.extended.call_for_each_replica( + update_member_fn) # Update "sync on read" variable. - self.evaluate(dist.group(update0a)) + self.evaluate(distribution.group(update0a)) self.assertEqual(2.0 + 5.0, self.evaluate(v0.get(devices[0]))) # Writes are not synchronized for "sync on read" variables, # so device[1] can end up with a different value. self.assertEqual(2.0 + 2*5.0, self.evaluate(v0.get(devices[1]))) # Always reads from device 0. - self.assertEqual(2.0 + 5.0, self.evaluate(dist.read_var(v0))) + self.assertEqual(2.0 + 5.0, self.evaluate( + distribution.extended.read_var(v0))) # Update "sync on write" variable. - self.evaluate(dist.group(update1a)) + self.evaluate(distribution.group(update1a)) self.assertEqual(3.0 + 7.0, self.evaluate(v1.get(devices[0]))) # Writes are synchronized for v1, only the argument to assign_add on # device[0] is used. self.assertEqual(3.0 + 7.0, self.evaluate(v1.get(devices[1]))) - self.assertEqual(3.0 + 7.0, self.evaluate(dist.read_var(v1))) + self.assertEqual(3.0 + 7.0, self.evaluate( + distribution.extended.read_var(v1))) # Update using state_ops.assign_add global function. - def update_state_ops_fn(device_id): - update0 = state_ops.assign_add(v0, 11.0 * (device_id + 1)) - update1 = state_ops.assign_add(v1, 13.0 * (device_id + 1)) + def update_state_ops_fn(): + update0 = state_ops.assign_add(v0, 11.0 * replica_id_plus_one()) + update1 = state_ops.assign_add(v1, 13.0 * replica_id_plus_one()) return update0, update1 - update0b, update1b = dist.call_for_each_replica( - update_state_ops_fn, args=(dist.worker_device_index,)) - self.evaluate(dist.group(update0b)) + update0b, update1b = distribution.extended.call_for_each_replica( + update_state_ops_fn) + self.evaluate(distribution.group(update0b)) # Update "sync on read" variable. self.assertEqual(2.0 + 5.0 + 11.0, self.evaluate(v0.get(devices[0]))) self.assertEqual(2.0 + 2*5.0 + 2*11.0, self.evaluate(v0.get(devices[1]))) - self.assertEqual(2.0 + 5.0 + 11.0, self.evaluate(dist.read_var(v0))) + self.assertEqual(2.0 + 5.0 + 11.0, self.evaluate( + distribution.extended.read_var(v0))) # Update "sync on write" variable. - self.evaluate(dist.group(update1b)) + self.evaluate(distribution.group(update1b)) self.assertEqual(3.0 + 7.0 + 13.0, self.evaluate(v1.get(devices[0]))) self.assertEqual(3.0 + 7.0 + 13.0, self.evaluate(v1.get(devices[1]))) - self.assertEqual(3.0 + 7.0 + 13.0, self.evaluate(dist.read_var(v1))) - - @test_util.run_in_graph_and_eager_modes(config=config) - def testNoneSynchronizationWithGetVariable(self): - self._skip_eager_if_gpus_less_than(1) - devices = ["/device:CPU:0", "/device:GPU:0"] - dist = mirrored_strategy.MirroredStrategy(devices) - with dist.scope(): + self.assertEqual(3.0 + 7.0 + 13.0, self.evaluate( + distribution.extended.read_var(v1))) + + def testNoneSynchronizationWithGetVariable(self, distribution): + with distribution.scope(): with self.assertRaisesRegexp( ValueError, "`NONE` variable synchronization mode is not " "supported with `Mirrored` distribution strategy. Please change " @@ -515,12 +484,8 @@ class MirroredStrategyVariableCreationTest(test.TestCase): "v", [1], synchronization=variable_scope.VariableSynchronization.NONE) - @test_util.run_in_graph_and_eager_modes(config=config) - def testNoneSynchronizationWithVariable(self): - self._skip_eager_if_gpus_less_than(1) - devices = ["/device:CPU:0", "/device:GPU:0"] - dist = mirrored_strategy.MirroredStrategy(devices) - with dist.scope(): + def testNoneSynchronizationWithVariable(self, distribution): + with distribution.scope(): with self.assertRaisesRegexp( ValueError, "`NONE` variable synchronization mode is not " "supported with `Mirrored` distribution strategy. Please change " @@ -530,23 +495,15 @@ class MirroredStrategyVariableCreationTest(test.TestCase): name="v", synchronization=variable_scope.VariableSynchronization.NONE) - @test_util.run_in_graph_and_eager_modes(config=config) - def testInvalidSynchronizationWithVariable(self): - self._skip_eager_if_gpus_less_than(1) - devices = ["/device:CPU:0", "/device:GPU:0"] - dist = mirrored_strategy.MirroredStrategy(devices) - with dist.scope(): + def testInvalidSynchronizationWithVariable(self, distribution): + with distribution.scope(): with self.assertRaisesRegexp( ValueError, "Invalid variable synchronization mode: Invalid for " "variable: v"): variable_scope.variable(1.0, name="v", synchronization="Invalid") - @test_util.run_in_graph_and_eager_modes(config=config) - def testInvalidAggregationWithGetVariable(self): - self._skip_eager_if_gpus_less_than(1) - devices = ["/device:CPU:0", "/device:GPU:0"] - dist = mirrored_strategy.MirroredStrategy(devices) - with dist.scope(): + def testInvalidAggregationWithGetVariable(self, distribution): + with distribution.scope(): with self.assertRaisesRegexp( ValueError, "Invalid variable aggregation mode: invalid for " "variable: v"): @@ -555,12 +512,8 @@ class MirroredStrategyVariableCreationTest(test.TestCase): synchronization=variable_scope.VariableSynchronization.ON_WRITE, aggregation="invalid") - @test_util.run_in_graph_and_eager_modes(config=config) - def testInvalidAggregationWithVariable(self): - self._skip_eager_if_gpus_less_than(1) - devices = ["/device:CPU:0", "/device:GPU:0"] - dist = mirrored_strategy.MirroredStrategy(devices) - with dist.scope(): + def testInvalidAggregationWithVariable(self, distribution): + with distribution.scope(): with self.assertRaisesRegexp( ValueError, "Invalid variable aggregation mode: invalid for " "variable: v"): @@ -570,55 +523,28 @@ class MirroredStrategyVariableCreationTest(test.TestCase): synchronization=variable_scope.VariableSynchronization.ON_WRITE, aggregation="invalid") - @test_util.run_in_graph_and_eager_modes(config=config) - def testThreeDevices(self): - self._skip_eager_if_gpus_less_than(2) - - def model_fn(): - v = variable_scope.variable(1.0, name="foo") - distribution_strategy_context.get_replica_context().merge_call( - lambda _: _) - return v - - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:GPU:1", "/device:CPU:0"]) - - with dist.scope(): - result = dist.call_for_each_replica(model_fn) - self.assertIsInstance(result, values.MirroredVariable) - self.assertEquals("foo:0", result.name) - - @test_util.run_in_graph_and_eager_modes(config=config) - def testNonMatchingVariableCreation(self): - self._skip_eager_if_gpus_less_than(1) - + def testNonMatchingVariableCreation(self, distribution): def model_fn(name): v = variable_scope.variable(1.0, name=name) - distribution_strategy_context.get_replica_context().merge_call( - lambda _: _) + ds_context.get_replica_context().merge_call(lambda _: _) return v - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) - - with dist.scope(): + with distribution.scope(): names = values.DistributedValues({ "/device:CPU:0": "foo", "/device:GPU:0": "bar" }) with self.assertRaises(RuntimeError): - _ = dist.call_for_each_replica(model_fn, args=(names,)) - - @test_util.run_in_graph_and_eager_modes(config=config) - def testReplicaLocalVariable(self): - self._skip_eager_if_gpus_less_than(1) + _ = distribution.extended.call_for_each_replica(model_fn, args=(names,)) + def testReplicaLocalVariable(self, distribution): all_v_sum = {} all_v_mean = {} components_sum = {} components_mean = {} - def model_fn(device_id): + def model_fn(): + replica_id = self.evaluate(_replica_id()) v_sum = variable_scope.variable( 1.0, synchronization=variable_scope.VariableSynchronization.ON_READ, @@ -629,26 +555,22 @@ class MirroredStrategyVariableCreationTest(test.TestCase): aggregation=variable_scope.VariableAggregation.MEAN) self.assertTrue(isinstance(v_sum, values.ReplicaLocalVariable)) self.assertTrue(isinstance(v_mean, values.ReplicaLocalVariable)) - updates = [v_sum.assign_add(2.0 + device_id), - v_mean.assign(6.0 * device_id)] - all_v_sum[device_id] = v_sum - all_v_mean[device_id] = v_mean + updates = [v_sum.assign_add(2.0 + replica_id), + v_mean.assign(6.0 * replica_id)] + all_v_sum[replica_id] = v_sum + all_v_mean[replica_id] = v_mean c_sum = v_sum.get() c_mean = v_mean.get() - components_sum[device_id] = c_sum - components_mean[device_id] = c_mean + components_sum[replica_id] = c_sum + components_mean[replica_id] = c_mean self.assertIsNot(v_sum, c_sum) self.assertIsNot(v_mean, c_mean) return updates, v_sum, v_mean, c_sum, c_mean - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) - - with dist.scope(): + with distribution.scope(): # Create "sum" and "mean" versions of ReplicaLocalVariables. ret_ops, ret_v_sum, ret_v_mean, regrouped_sum, regrouped_mean = ( - dist.call_for_each_replica( - model_fn, args=(dist.worker_device_index,))) + distribution.extended.call_for_each_replica(model_fn)) # Should see the same wrapping instance in all replicas. self.assertIs(all_v_sum[0], ret_v_sum) self.assertIs(all_v_mean[0], ret_v_mean) @@ -663,10 +585,10 @@ class MirroredStrategyVariableCreationTest(test.TestCase): # Apply updates self.evaluate(variables.global_variables_initializer()) - self.evaluate([y for x in ret_ops for y in dist.unwrap(x)]) + self.evaluate([y for x in ret_ops for y in distribution.unwrap(x)]) expected_sum = 0.0 expected_mean = 0.0 - for i, d in enumerate(dist.worker_devices): + for i, d in enumerate(distribution.extended.worker_devices): # Should see different values on different devices. v_sum_value = self.evaluate(ret_v_sum.get(d).read_value()) v_mean_value = self.evaluate(ret_v_mean.get(d).read_value()) @@ -676,69 +598,125 @@ class MirroredStrategyVariableCreationTest(test.TestCase): expected = i * 6.0 self.assertEqual(expected, v_mean_value) expected_mean += expected - expected_mean /= len(dist.worker_devices) + expected_mean /= len(distribution.extended.worker_devices) # Without get(device), should return the value you get by # applying the reduction across all replicas (whether you use # read_var(), get(), or nothing). - self.assertEqual(expected_sum, self.evaluate(dist.read_var(ret_v_sum))) - self.assertEqual(expected_mean, self.evaluate(dist.read_var(ret_v_mean))) + self.assertEqual(expected_sum, self.evaluate( + distribution.extended.read_var(ret_v_sum))) + self.assertEqual(expected_mean, self.evaluate( + distribution.extended.read_var(ret_v_mean))) self.assertEqual(expected_sum, self.evaluate(ret_v_sum.get())) self.assertEqual(expected_mean, self.evaluate(ret_v_mean.get())) self.assertEqual(expected_sum, self.evaluate(ret_v_sum)) self.assertEqual(expected_mean, self.evaluate(ret_v_mean)) + # TODO(priyag): Update this test to work in eager mode as well. + def testDynamicRnnVariables(self, distribution): + def model_fn(): + inputs = constant_op.constant(2 * [2 * [[0.0, 1.0, 2.0, 3.0, 4.0]]]) + cell_fw = rnn_cell_impl.LSTMCell(300) + cell_bw = rnn_cell_impl.LSTMCell(300) + (outputs, _) = rnn.bidirectional_dynamic_rnn( + cell_fw, + cell_bw, + inputs, + dtype=dtypes.float32) + return outputs + + with context.graph_mode(), distribution.scope(): + result = distribution.extended.call_for_each_replica(model_fn) + # Two variables are created by the RNN layer. + self.assertEqual(2, len(result)) + for v in result: + self.assertIsInstance(v, values.DistributedValues) + _, v1 = distribution.unwrap(v) + self.assertStartsWith(v1._op.name, "replica_1/") + + def testReplicaLocalVariableUpdate(self, distribution): + def model_fn(): + v_sum = variable_scope.variable( + 1.0, + synchronization=variable_scope.VariableSynchronization.ON_READ, + aggregation=variable_scope.VariableAggregation.SUM) + self.assertTrue(isinstance(v_sum, values.ReplicaLocalVariable)) + return v_sum + + def update(var, value): + return var.assign(value) + + with distribution.scope(): + ret_v_sum = distribution.extended.call_for_each_replica(model_fn) + + # Initialize variables. + self.evaluate(variables.global_variables_initializer()) + # Assert that the aggregated value of the replica local vars is the sum + # of the individual values before running the update ops. + self.assertEqual(1.0, self.evaluate(ret_v_sum.get( + distribution.extended.worker_devices[0]).read_value())) + self.assertEqual(2.0, self.evaluate(ret_v_sum)) + + # Apply updates. + update_ops = distribution.extended.update( + ret_v_sum, update, args=(5.0,), group=False) + self.evaluate(update_ops) + # Assert that the aggregated value of the replica local vars is the sum + # of the individual values after running the update ops. + self.assertEqual(5.0, self.evaluate(ret_v_sum.get( + distribution.extended.worker_devices[0]).read_value())) + self.assertEqual(10.0, self.evaluate(ret_v_sum)) + + +@combinations.generate(combinations.combine( + distribution=[ + combinations.mirrored_strategy_with_gpu_and_cpu, + combinations.core_mirrored_strategy_with_gpu_and_cpu], + mode=["graph"])) +class MirroredStrategyNameScopeTest(test.TestCase): # NOTE(priyag): Names and name scopes are ignored in eager, hence we are not # testing this in eager mode. - def testNameScope(self): + def testNameScope(self, distribution): def model_fn(): with ops.name_scope("foo"): a = constant_op.constant(1.0, name="a") - distribution_strategy_context.get_replica_context().merge_call( - lambda _: _) + ds_context.get_replica_context().merge_call(lambda _: _) b = constant_op.constant(1.0, name="b") return a, b - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) - - with context.graph_mode(), dist.scope(): + with context.graph_mode(), distribution.scope(): with ops.name_scope("main"): - result = dist.call_for_each_replica(model_fn) - self.assertEquals(2, len(result)) + result = distribution.extended.call_for_each_replica(model_fn) + self.assertEqual(2, len(result)) for v, name in zip(result, ["a", "b"]): self.assertIsInstance(v, values.DistributedValues) - v0, v1 = dist.unwrap(v) - self.assertEquals("main/foo/" + name + ":0", v0.name) - self.assertEquals("main/replica_1/foo/" + name + ":0", v1.name) + v0, v1 = distribution.unwrap(v) + self.assertEqual("main/foo/" + name + ":0", v0.name) + self.assertEqual("main/replica_1/foo/" + name + ":0", v1.name) - def testWithDefaultName(self): + def testWithDefaultName(self, distribution): def model_fn(): with ops.name_scope(None, "foo"): a = constant_op.constant(1.0, name="a") - distribution_strategy_context.get_replica_context().merge_call( - lambda _: _) + ds_context.get_replica_context().merge_call(lambda _: _) b = constant_op.constant(2.0, name="b") return a, b - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) - - with context.graph_mode(), dist.scope(): - result = dist.call_for_each_replica(model_fn) - self.assertEquals(2, len(result)) + with context.graph_mode(), distribution.scope(): + result = distribution.extended.call_for_each_replica(model_fn) + self.assertEqual(2, len(result)) for v, name in zip(result, ["a", "b"]): self.assertIsInstance(v, values.DistributedValues) - v0, v1 = dist.unwrap(v) - self.assertEquals("foo/" + name + ":0", v0.name) - self.assertEquals("replica_1/foo/" + name + ":0", v1.name) + v0, v1 = distribution.unwrap(v) + self.assertEqual("foo/" + name + ":0", v0.name) + self.assertEqual("replica_1/foo/" + name + ":0", v1.name) # variable_scope.variable() respects name scopes when creating # variables. On the other hand variable_scope.get_variable() ignores name # scopes when creating variables. We test both methods of creating variables # to make sure that we have the same variable names in both cases. - def testNameScopeWithVariable(self): + def testNameScopeWithVariable(self, distribution): def in_cross_replica(_): c = variable_scope.variable(1.0, name="c") return c @@ -746,32 +724,28 @@ class MirroredStrategyVariableCreationTest(test.TestCase): def model_fn(): b = variable_scope.variable(1.0, name="b") with ops.name_scope("foo"): - c = distribution_strategy_context.get_replica_context().merge_call( - in_cross_replica) + c = ds_context.get_replica_context().merge_call(in_cross_replica) return b, c - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) - - with context.graph_mode(), dist.scope(): + with context.graph_mode(), distribution.scope(): with ops.name_scope("main"): a = variable_scope.variable(1.0, name="a") - result = dist.call_for_each_replica(model_fn) + result = distribution.extended.call_for_each_replica(model_fn) result_b = result[0] result_c = result[1] self.assertIsInstance(result_b, values.DistributedValues) self.assertIsInstance(result_c, values.DistributedValues) - a0, a1 = dist.unwrap(a) - b0, b1 = dist.unwrap(result_b) - c0, c1 = dist.unwrap(result_c) - self.assertEquals("main/a:0", a0.name) - self.assertEquals("main/a/replica_1:0", a1.name) - self.assertEquals("main/b:0", b0.name) - self.assertEquals("main/b/replica_1:0", b1.name) - self.assertEquals("main/foo/c:0", c0.name) - self.assertEquals("main/foo/c/replica_1:0", c1.name) - - def testNameScopeWithGetVariable(self): + a0, a1 = distribution.unwrap(a) + b0, b1 = distribution.unwrap(result_b) + c0, c1 = distribution.unwrap(result_c) + self.assertEqual("main/a:0", a0.name) + self.assertEqual("main/a/replica_1:0", a1.name) + self.assertEqual("main/b:0", b0.name) + self.assertEqual("main/b/replica_1:0", b1.name) + self.assertEqual("main/foo/c:0", c0.name) + self.assertEqual("main/foo/c/replica_1:0", c1.name) + + def testNameScopeWithGetVariable(self, distribution): def in_cross_replica(_): c = variable_scope.get_variable("c", [1]) return c @@ -779,118 +753,80 @@ class MirroredStrategyVariableCreationTest(test.TestCase): def model_fn(): b = variable_scope.get_variable("b", [1]) with ops.name_scope("foo"): - c = distribution_strategy_context.get_replica_context().merge_call( - in_cross_replica) + c = ds_context.get_replica_context().merge_call(in_cross_replica) return b, c - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) - - with context.graph_mode(), dist.scope(): + with context.graph_mode(), distribution.scope(): with ops.name_scope("main"): a = variable_scope.get_variable("a", [1]) - result = dist.call_for_each_replica(model_fn) + result = distribution.extended.call_for_each_replica(model_fn) result_b = result[0] result_c = result[1] self.assertIsInstance(result_b, values.DistributedValues) self.assertIsInstance(result_c, values.DistributedValues) - a0, a1 = dist.unwrap(a) - b0, b1 = dist.unwrap(result_b) - c0, c1 = dist.unwrap(result_c) - self.assertEquals("a:0", a0.name) - self.assertEquals("a/replica_1:0", a1.name) - self.assertEquals("b:0", b0.name) - self.assertEquals("b/replica_1:0", b1.name) - self.assertEquals("c:0", c0.name) - self.assertEquals("c/replica_1:0", c1.name) - - def testDynamicRnnVariables(self): + a0, a1 = distribution.unwrap(a) + b0, b1 = distribution.unwrap(result_b) + c0, c1 = distribution.unwrap(result_c) + self.assertEqual("a:0", a0.name) + self.assertEqual("a/replica_1:0", a1.name) + self.assertEqual("b:0", b0.name) + self.assertEqual("b/replica_1:0", b1.name) + self.assertEqual("c:0", c0.name) + self.assertEqual("c/replica_1:0", c1.name) + + +@combinations.generate( + combinations.combine( + distribution=[ + combinations.NamedDistribution( + "Mirrored3Devices", + # pylint: disable=g-long-lambda + lambda: mirrored_strategy.MirroredStrategy( + ["/device:GPU:0", "/device:GPU:1", "/device:CPU:0"]), + required_gpus=2), + combinations.NamedDistribution( + "CoreMirrored3Devices", + # pylint: disable=g-long-lambda + lambda: mirrored_strategy.CoreMirroredStrategy( + ["/device:GPU:0", "/device:GPU:1", "/device:CPU:0"]), + required_gpus=2) + ], + mode=["graph", "eager"])) +class MirroredThreeDeviceDistributionTest( + strategy_test_lib.DistributionTestBase, + parameterized.TestCase): + + def testThreeDevices(self, distribution): def model_fn(): - inputs = constant_op.constant(2 * [2 * [[0.0, 1.0, 2.0, 3.0, 4.0]]]) - cell_fw = rnn_cell_impl.LSTMCell(300) - cell_bw = rnn_cell_impl.LSTMCell(300) - (outputs, _) = rnn.bidirectional_dynamic_rnn( - cell_fw, - cell_bw, - inputs, - dtype=dtypes.float32) - return outputs - - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) - - with context.graph_mode(), dist.scope(): - result = dist.call_for_each_replica(model_fn) - # Two variables are created by the RNN layer. - self.assertEquals(2, len(result)) - for v in result: - self.assertIsInstance(v, values.DistributedValues) - _, v1 = dist.unwrap(v) - self.assertStartsWith(v1.name, "replica_1/") - - @test_util.run_in_graph_and_eager_modes(config=config) - def testReplicaLocalVariableUpdate(self): - with context.graph_mode(): - - def model_fn(): - v_sum = variable_scope.variable( - 1.0, - synchronization=variable_scope.VariableSynchronization.ON_READ, - aggregation=variable_scope.VariableAggregation.SUM) - self.assertTrue(isinstance(v_sum, values.ReplicaLocalVariable)) - return v_sum - - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:GPU:1"]) - - def update(var, value): - return var.assign(value) - - with dist.scope(): - ret_v_sum = dist.call_for_each_replica(model_fn) - update_ops = dist.update(ret_v_sum, update, 5.0, grouped=False) - - # Initialize variables. - self.evaluate(variables.global_variables_initializer()) - # Assert that the aggregated value of the replica local vars is the sum - # of the individual values before running the update ops. - self.assertEquals(1.0, self.evaluate( - ret_v_sum.get(dist._devices[0]).read_value())) - self.assertEquals(2.0, self.evaluate(ret_v_sum)) + v = variable_scope.variable(1.0, name="foo") + ds_context.get_replica_context().merge_call(lambda _: _) + return v - # Apply updates. - self.evaluate(update_ops) - # Assert that the aggregated value of the replica local vars is the sum - # of the individual values after running the update ops. - self.assertEquals(5.0, self.evaluate( - ret_v_sum.get(dist._devices[0]).read_value())) - self.assertEquals(10.0, self.evaluate(ret_v_sum)) + with distribution.scope(): + result = distribution.extended.call_for_each_replica(model_fn) + self.assertIsInstance(result, values.MirroredVariable) + self.assertEqual("foo:0", result.name) +@combinations.generate(combinations.combine( + distribution=[ + combinations.mirrored_strategy_with_gpu_and_cpu, + combinations.core_mirrored_strategy_with_gpu_and_cpu], + mode=["graph", "eager"])) class MirroredVariableUpdateTest(test.TestCase): # The following tests check assign, assign_add and assign_sub on Mirrored # variables in replica and cross replica context. - config = config_pb2.ConfigProto() - config.allow_soft_placement = True - - def _skip_eager_if_gpus_less_than(self, num_gpus): - if context.num_gpus() < num_gpus and context.executing_eagerly(): - self.skipTest("Enough GPUs not available for this test in eager mode.") - @test_util.run_in_graph_and_eager_modes(config=config) - def testAssignMirroredVarReplicaContextWithoutAggregationType(self): + def testAssignMirroredVarReplicaContextWithoutAggregationType(self, + distribution): # Test that we always have an aggregation type set on the mirrored variable # if we assign to it in replica mode. - self._skip_eager_if_gpus_less_than(1) def var_fn(): v = variable_scope.variable(1.0, name="foo") return v - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) - - with dist.scope(): - mirrored_var = dist.call_for_each_replica(var_fn) + with distribution.scope(): + mirrored_var = distribution.extended.call_for_each_replica(var_fn) self.assertIsInstance(mirrored_var, values.MirroredVariable) self.evaluate(variables.global_variables_initializer()) @@ -900,23 +836,19 @@ class MirroredVariableUpdateTest(test.TestCase): with self.assertRaisesRegexp( ValueError, "You must specify an aggregation method to update a " "MirroredVariable in Replica Context."): - self.evaluate(dist.unwrap(dist.call_for_each_replica(model_fn))) + self.evaluate(distribution.unwrap( + distribution.extended.call_for_each_replica(model_fn))) - @test_util.run_in_graph_and_eager_modes(config=config) - def testAssignMirroredVarReplicaContextWithSum(self): + def testAssignMirroredVarReplicaContextWithSum(self, distribution): # Test that we don't reduce a non-per-replica value with the "sum" # aggregation type. - self._skip_eager_if_gpus_less_than(1) def var_fn(): v = variable_scope.variable( 1.0, name="foo", aggregation=variable_scope.VariableAggregation.SUM) return v - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) - - with dist.scope(): - mirrored_var = dist.call_for_each_replica(var_fn) + with distribution.scope(): + mirrored_var = distribution.extended.call_for_each_replica(var_fn) self.assertIsInstance(mirrored_var, values.MirroredVariable) self.evaluate(variables.global_variables_initializer()) @@ -925,219 +857,184 @@ class MirroredVariableUpdateTest(test.TestCase): with self.assertRaisesRegexp( ValueError, "A non-DistributedValues value 5.0 cannot be reduced " - "with the given aggregation VariableAggregation.SUM."): - self.evaluate(dist.unwrap(dist.call_for_each_replica(model_fn))) + "with the given reduce op ReduceOp.SUM."): + self.evaluate(distribution.unwrap( + distribution.extended.call_for_each_replica(model_fn))) - @test_util.run_in_graph_and_eager_modes(config=config) - def testAssignMirroredVarCrossDeviceContext(self): - self._skip_eager_if_gpus_less_than(1) + def testAssignMirroredVarCrossDeviceContext(self, distribution): def var_fn(): return variable_scope.variable(1.0, name="foo") - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) - - with dist.scope(): - mirrored_var = dist.call_for_each_replica(var_fn) + with distribution.scope(): + mirrored_var = distribution.extended.call_for_each_replica(var_fn) self.assertIsInstance(mirrored_var, values.MirroredVariable) self.evaluate(variables.global_variables_initializer()) - self.assertEquals(1.0, self.evaluate(mirrored_var)) + self.assertEqual(1.0, self.evaluate(mirrored_var)) mirrored_var_result = self.evaluate(mirrored_var.assign(6.0)) - self.assertEquals(6.0, mirrored_var_result) + self.assertEqual(6.0, mirrored_var_result) - @test_util.run_in_graph_and_eager_modes(config=config) - def testAssignMirroredVarReplicaContext(self): - self._skip_eager_if_gpus_less_than(1) + def testAssignMirroredVarReplicaContext(self, distribution): def var_fn(): return variable_scope.variable( 1.0, name="foo", aggregation=variable_scope.VariableAggregation.MEAN) - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) - - with dist.scope(): - mirrored_var = dist.call_for_each_replica(var_fn) + with distribution.scope(): + mirrored_var = distribution.extended.call_for_each_replica(var_fn) self.assertIsInstance(mirrored_var, values.MirroredVariable) self.evaluate(variables.global_variables_initializer()) - self.assertEquals(1.0, self.evaluate(mirrored_var)) + self.assertEqual(1.0, self.evaluate(mirrored_var)) def model_fn(): value = math_ops.cast( - distribution_strategy_context.get_replica_context().replica_id, + ds_context.get_replica_context().replica_id_in_sync_group, mirrored_var.dtype) return mirrored_var.assign(value) - self.evaluate(dist.unwrap(dist.call_for_each_replica(model_fn))) - self.assertEquals(0.5, self.evaluate(mirrored_var)) + self.evaluate(distribution.unwrap( + distribution.extended.call_for_each_replica(model_fn))) + self.assertEqual(0.5, self.evaluate(mirrored_var)) - @test_util.run_in_graph_and_eager_modes(config=config) - def testAssignMirroredVarReplicaContextWithSingleValue(self): - self._skip_eager_if_gpus_less_than(1) + def testAssignMirroredVarReplicaContextWithSingleValue(self, distribution): def var_fn(): return variable_scope.variable( 1.0, name="foo", aggregation=variable_scope.VariableAggregation.MEAN) - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) - - with dist.scope(): - mirrored_var = dist.call_for_each_replica(var_fn) + with distribution.scope(): + mirrored_var = distribution.extended.call_for_each_replica(var_fn) self.assertIsInstance(mirrored_var, values.MirroredVariable) self.evaluate(variables.global_variables_initializer()) - self.assertEquals(1.0, self.evaluate(mirrored_var)) + self.assertEqual(1.0, self.evaluate(mirrored_var)) def model_fn(): return mirrored_var.assign(5.0) - self.evaluate(dist.unwrap(dist.call_for_each_replica(model_fn))) - self.assertEquals(5.0, self.evaluate(mirrored_var)) + self.evaluate(distribution.unwrap( + distribution.extended.call_for_each_replica(model_fn))) + self.assertEqual(5.0, self.evaluate(mirrored_var)) - @test_util.run_in_graph_and_eager_modes(config=config) - def testAssignAddMirroredVarCrossDeviceContext(self): - self._skip_eager_if_gpus_less_than(1) + def testAssignAddMirroredVarCrossDeviceContext(self, distribution): def var_fn(): return variable_scope.variable(1.0, name="foo") - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) - - with dist.scope(): - mirrored_var = dist.call_for_each_replica(var_fn) + with distribution.scope(): + mirrored_var = distribution.extended.call_for_each_replica(var_fn) self.assertIsInstance(mirrored_var, values.MirroredVariable) self.evaluate(variables.global_variables_initializer()) - self.assertEquals(1.0, self.evaluate(mirrored_var)) + self.assertEqual(1.0, self.evaluate(mirrored_var)) # read_value == True mirrored_var_result = self.evaluate( mirrored_var.assign_add(6.0, read_value=True)) - self.assertEquals(7.0, mirrored_var_result) - self.assertEquals(7.0, self.evaluate(mirrored_var.get("/device:CPU:0"))) - self.assertEquals(7.0, self.evaluate(mirrored_var.get("/device:GPU:0"))) + self.assertEqual(7.0, mirrored_var_result) + self.assertEqual(7.0, self.evaluate(mirrored_var.get("/device:CPU:0"))) + self.assertEqual(7.0, self.evaluate(mirrored_var.get("/device:GPU:0"))) # read_value == False self.evaluate(mirrored_var.assign_add(2.0, read_value=False)) - self.assertEquals(9.0, self.evaluate(mirrored_var.get("/device:CPU:0"))) - self.assertEquals(9.0, self.evaluate(mirrored_var.get("/device:GPU:0"))) + self.assertEqual(9.0, self.evaluate(mirrored_var.get("/device:CPU:0"))) + self.assertEqual(9.0, self.evaluate(mirrored_var.get("/device:GPU:0"))) - @test_util.run_in_graph_and_eager_modes(config=config) - def testAssignAddMirroredVarReplicaContext(self): - self._skip_eager_if_gpus_less_than(1) + def testAssignAddMirroredVarReplicaContext(self, distribution): def var_fn(): return variable_scope.variable( 1.0, name="foo", aggregation=variable_scope.VariableAggregation.MEAN) - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) - - with dist.scope(): - mirrored_var = dist.call_for_each_replica(var_fn) + with distribution.scope(): + mirrored_var = distribution.extended.call_for_each_replica(var_fn) self.assertIsInstance(mirrored_var, values.MirroredVariable) self.evaluate(variables.global_variables_initializer()) - self.assertEquals(1.0, self.evaluate(mirrored_var)) + self.assertEqual(1.0, self.evaluate(mirrored_var)) def model_fn(): value = math_ops.cast( - distribution_strategy_context.get_replica_context().replica_id, + ds_context.get_replica_context().replica_id_in_sync_group, mirrored_var.dtype) return mirrored_var.assign_add(value) - self.evaluate(dist.unwrap(dist.call_for_each_replica(model_fn))) - self.assertEquals(1.5, self.evaluate(mirrored_var)) + self.evaluate(distribution.unwrap( + distribution.extended.call_for_each_replica(model_fn))) + self.assertEqual(1.5, self.evaluate(mirrored_var)) - @test_util.run_in_graph_and_eager_modes(config=config) - def testAssignAddMirroredVarReplicaContextWithSingleValue(self): - self._skip_eager_if_gpus_less_than(1) + def testAssignAddMirroredVarReplicaContextWithSingleValue(self, distribution): def var_fn(): return variable_scope.variable( 1.0, name="foo", aggregation=variable_scope.VariableAggregation.MEAN) - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) - - with dist.scope(): - mirrored_var = dist.call_for_each_replica(var_fn) + with distribution.scope(): + mirrored_var = distribution.extended.call_for_each_replica(var_fn) self.assertIsInstance(mirrored_var, values.MirroredVariable) self.evaluate(variables.global_variables_initializer()) - self.assertEquals(1.0, self.evaluate(mirrored_var)) + self.assertEqual(1.0, self.evaluate(mirrored_var)) def model_fn(): return mirrored_var.assign_add(5.0) - self.evaluate(dist.unwrap(dist.call_for_each_replica(model_fn))) - self.assertEquals(6.0, self.evaluate(mirrored_var)) + self.evaluate(distribution.unwrap( + distribution.extended.call_for_each_replica(model_fn))) + self.assertEqual(6.0, self.evaluate(mirrored_var)) - @test_util.run_in_graph_and_eager_modes(config=config) - def testAssignSubMirroredVarCrossDeviceContext(self): - self._skip_eager_if_gpus_less_than(1) + def testAssignSubMirroredVarCrossDeviceContext(self, distribution): def var_fn(): return variable_scope.variable(5.0, name="foo") - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) - - with dist.scope(): - mirrored_var = dist.call_for_each_replica(var_fn) + with distribution.scope(): + mirrored_var = distribution.extended.call_for_each_replica(var_fn) self.assertIsInstance(mirrored_var, values.MirroredVariable) self.evaluate(variables.global_variables_initializer()) - self.assertEquals(5.0, self.evaluate(mirrored_var)) + self.assertEqual(5.0, self.evaluate(mirrored_var)) mirrored_var_result = self.evaluate(mirrored_var.assign_sub(2.0)) - self.assertEquals(3.0, mirrored_var_result) - self.assertEquals(3.0, self.evaluate(mirrored_var.get("/device:GPU:0"))) - self.assertEquals(3.0, self.evaluate(mirrored_var.get("/device:CPU:0"))) + self.assertEqual(3.0, mirrored_var_result) + self.assertEqual(3.0, self.evaluate(mirrored_var.get("/device:GPU:0"))) + self.assertEqual(3.0, self.evaluate(mirrored_var.get("/device:CPU:0"))) - @test_util.run_in_graph_and_eager_modes(config=config) - def testAssignSubMirroredVarReplicaContext(self): - self._skip_eager_if_gpus_less_than(1) + def testAssignSubMirroredVarReplicaContext(self, distribution): def var_fn(): return variable_scope.variable( 5.0, name="foo", aggregation=variable_scope.VariableAggregation.MEAN) - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) - - with dist.scope(): - mirrored_var = dist.call_for_each_replica(var_fn) + with distribution.scope(): + mirrored_var = distribution.extended.call_for_each_replica(var_fn) self.assertIsInstance(mirrored_var, values.MirroredVariable) self.evaluate(variables.global_variables_initializer()) - self.assertEquals(5.0, self.evaluate(mirrored_var)) + self.assertEqual(5.0, self.evaluate(mirrored_var)) def model_fn(): value = math_ops.cast( - distribution_strategy_context.get_replica_context().replica_id, + ds_context.get_replica_context().replica_id_in_sync_group, mirrored_var.dtype) return mirrored_var.assign_sub(value) - self.evaluate(dist.unwrap(dist.call_for_each_replica(model_fn))) - self.assertEquals(4.5, self.evaluate(mirrored_var)) + self.evaluate(distribution.unwrap( + distribution.extended.call_for_each_replica(model_fn))) + self.assertEqual(4.5, self.evaluate(mirrored_var)) - @test_util.run_in_graph_and_eager_modes(config=config) - def testAssignSubMirroredVarReplicaContextWithSingleValue(self): - self._skip_eager_if_gpus_less_than(1) + def testAssignSubMirroredVarReplicaContextWithSingleValue(self, distribution): def var_fn(): return variable_scope.variable( 5.0, name="foo", aggregation=variable_scope.VariableAggregation.MEAN) - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) - - with dist.scope(): - mirrored_var = dist.call_for_each_replica(var_fn) + with distribution.scope(): + mirrored_var = distribution.extended.call_for_each_replica(var_fn) self.assertIsInstance(mirrored_var, values.MirroredVariable) self.evaluate(variables.global_variables_initializer()) - self.assertEquals(5.0, self.evaluate(mirrored_var)) + self.assertEqual(5.0, self.evaluate(mirrored_var)) def model_fn(): return mirrored_var.assign_sub(1.0) - self.evaluate(dist.unwrap(dist.call_for_each_replica(model_fn))) - self.assertEquals(4.0, self.evaluate(mirrored_var)) + self.evaluate(distribution.unwrap( + distribution.extended.call_for_each_replica(model_fn))) + self.assertEqual(4.0, self.evaluate(mirrored_var)) +@combinations.generate(combinations.combine( + distribution=[ + combinations.mirrored_strategy_with_gpu_and_cpu, + combinations.core_mirrored_strategy_with_gpu_and_cpu], + mode=["graph", "eager"])) class MirroredAndReplicaLocalVariableInitializerTest(test.TestCase): - config = config_pb2.ConfigProto() - config.allow_soft_placement = True - def testAssignMirroredVarInitializer(self): + def testAssignMirroredVarInitializer(self, distribution): # This test is not eager compatible since in eager variables are initialized # upon construction instead of once the initialization op is run. with context.graph_mode(): @@ -1145,17 +1042,14 @@ class MirroredAndReplicaLocalVariableInitializerTest(test.TestCase): v = variable_scope.variable(1.0, name="foo") return v - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) - - with dist.scope(): - mirrored_var = dist.call_for_each_replica(var_fn) + with distribution.scope(): + mirrored_var = distribution.extended.call_for_each_replica(var_fn) self.assertIsInstance(mirrored_var, values.MirroredVariable) self.assertFalse(self.evaluate(mirrored_var.is_initialized())) self.evaluate(mirrored_var.initializer) self.assertTrue(self.evaluate(mirrored_var.is_initialized())) - def testAssignReplicaLocalVarInitializer(self): + def testAssignReplicaLocalVarInitializer(self, distribution): # This test is not eager compatible since in eager variables are initialized # upon construction instead of once the initialization op is run. with context.graph_mode(): @@ -1167,11 +1061,9 @@ class MirroredAndReplicaLocalVariableInitializerTest(test.TestCase): self.assertTrue(isinstance(v_sum, values.ReplicaLocalVariable)) return v_sum - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) - - with dist.scope(): - replica_local_var = dist.call_for_each_replica(model_fn) + with distribution.scope(): + replica_local_var = distribution.extended.call_for_each_replica( + model_fn) self.assertTrue(isinstance(replica_local_var, values.ReplicaLocalVariable)) self.assertFalse(self.evaluate(replica_local_var.is_initialized())) @@ -1179,17 +1071,14 @@ class MirroredAndReplicaLocalVariableInitializerTest(test.TestCase): self.assertTrue(self.evaluate(replica_local_var.is_initialized())) +@combinations.generate(combinations.combine( + distribution=[ + combinations.mirrored_strategy_with_gpu_and_cpu, + combinations.core_mirrored_strategy_with_gpu_and_cpu], + mode=["graph", "eager"])) class ReplicaLocalVariableAssignTest(test.TestCase): - config = config_pb2.ConfigProto() - config.allow_soft_placement = True - def _skip_eager_if_gpus_less_than(self, num_gpus): - if context.num_gpus() < num_gpus and context.executing_eagerly(): - self.skipTest("Not enough GPUs available for this test in eager mode.") - - @test_util.run_in_graph_and_eager_modes(config=config) - def testAssignReplicaLocalVarSumAggregation(self): - self._skip_eager_if_gpus_less_than(1) + def testAssignReplicaLocalVarSumAggregation(self, distribution): def model_fn(): v_sum = variable_scope.variable( 1.0, @@ -1197,18 +1086,16 @@ class ReplicaLocalVariableAssignTest(test.TestCase): aggregation=variable_scope.VariableAggregation.SUM) return v_sum - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) - - with dist.scope(): - replica_local_var = dist.call_for_each_replica(model_fn) + with distribution.scope(): + replica_local_var = distribution.extended.call_for_each_replica(model_fn) self.assertTrue(isinstance(replica_local_var, values.ReplicaLocalVariable)) self.evaluate(variables.global_variables_initializer()) # Each replica has a value of 1.0 assigned to it in replica context. # When we read the value using `read_var` we should see the SUM of each of # values on each of the replicas. - self.assertEqual(2.0, self.evaluate(dist.read_var(replica_local_var))) + self.assertEqual(2.0, self.evaluate( + distribution.read_var(replica_local_var))) # Assigning 6.0 in cross replica context will assign a value of # 6.0/num_replicas to each replica. tlv_ops = replica_local_var.assign(6.0) @@ -1216,11 +1103,10 @@ class ReplicaLocalVariableAssignTest(test.TestCase): # On reading the replica local var we should get the assigned value back. # The value on all the replicas are added before being returned by # `read_var`. - self.assertEqual(6.0, self.evaluate(dist.read_var(replica_local_var))) + self.assertEqual(6.0, self.evaluate( + distribution.read_var(replica_local_var))) - @test_util.run_in_graph_and_eager_modes(config=config) - def testAssignReplicaLocalVarMeanAggregation(self): - self._skip_eager_if_gpus_less_than(1) + def testAssignReplicaLocalVarMeanAggregation(self, distribution): def model_fn(): v_sum = variable_scope.variable( 1.0, @@ -1228,23 +1114,22 @@ class ReplicaLocalVariableAssignTest(test.TestCase): aggregation=variable_scope.VariableAggregation.MEAN) return v_sum - dist = mirrored_strategy.MirroredStrategy( - ["/device:GPU:0", "/device:CPU:0"]) - - with dist.scope(): - replica_local_var = dist.call_for_each_replica(model_fn) + with distribution.scope(): + replica_local_var = distribution.extended.call_for_each_replica(model_fn) self.assertTrue(isinstance(replica_local_var, values.ReplicaLocalVariable)) self.evaluate(variables.global_variables_initializer()) # Each replica has a value of 1.0 assigned to it in replica context. # When we read the value using `read_var` we should see the MEAN of values # on all replicas which is the value assigned in replica context. - self.assertEqual(1.0, self.evaluate(dist.read_var(replica_local_var))) + self.assertEqual(1.0, self.evaluate( + distribution.read_var(replica_local_var))) tlv_ops = replica_local_var.assign(6.0) self.evaluate(tlv_ops) # On reading the replica local var we should get the MEAN of all values # which is equal to the value assigned. - self.assertEqual(6.0, self.evaluate(dist.read_var(replica_local_var))) + self.assertEqual(6.0, self.evaluate( + distribution.read_var(replica_local_var))) class MockModel(object): @@ -1278,24 +1163,25 @@ class MiniModel(keras_training.Model): return self.fc(inputs) +@combinations.generate(combinations.combine( + distribution=[ + combinations.mirrored_strategy_with_gpu_and_cpu, + combinations.core_mirrored_strategy_with_gpu_and_cpu], + mode=["graph", "eager"])) class MirroredStrategyDefunTest(test.TestCase): - def _skip_eager_if_gpus_less_than(self, num_gpus): - if context.num_gpus() < num_gpus and context.executing_eagerly(): - self.skipTest("Not enough GPUs available for this test in eager mode.") - - def _call_and_check(self, model_fn, inputs, expected_result, defuns, - two_variables=False): + def _call_and_check(self, distribution, model_fn, inputs, expected_result, + defuns, two_variables=False): cpu_dev = device_util.canonicalize("CPU:0") gpu_dev = device_util.canonicalize("GPU:0") devices = [cpu_dev, gpu_dev] - dist = mirrored_strategy.MirroredStrategy(devices) - with dist.scope(): + with distribution.scope(): mock_model = MockModel(two_variables) self.evaluate(variables.global_variables_initializer()) - result = dist.call_for_each_replica(model_fn, args=[mock_model] + inputs) + result = distribution.extended.call_for_each_replica( + model_fn, args=[mock_model] + inputs) for device in devices: device_result = values.select_device(device, result) device_expected_result = values.select_device(device, expected_result) @@ -1307,17 +1193,15 @@ class MirroredStrategyDefunTest(test.TestCase): # call_for_each has one trace per device. To check that the expected set # of variables was accessed on each trace, we first retrieve each # device-specific graph function. - per_replica_graph_functions = dist.call_for_each_replica( - defun.get_concrete_function, args=[mock_model] + inputs) + per_replica_graph_functions = ( + distribution.extended.call_for_each_replica( + defun.get_concrete_function, args=[mock_model] + inputs)) for device in devices: graph_function = per_replica_graph_functions.get(device=device) self.assertEqual(set(mock_model.variables), set(graph_function.graph.variables)) - @test_util.run_in_graph_and_eager_modes() - def testVariableInDefun(self): - self._skip_eager_if_gpus_less_than(1) - + def testVariableInDefun(self, distribution): @function.defun def times_two(mock_model): return mock_model() @@ -1325,12 +1209,9 @@ class MirroredStrategyDefunTest(test.TestCase): def model_fn(mock_model): return times_two(mock_model) - self._call_and_check(model_fn, [], 2.5, [times_two]) - - @test_util.run_in_graph_and_eager_modes() - def testVariableInNestedDefun(self): - self._skip_eager_if_gpus_less_than(1) + self._call_and_check(distribution, model_fn, [], 2.5, [times_two]) + def testVariableInNestedDefun(self, distribution): @function.defun def times_two(mock_model): return mock_model() @@ -1342,12 +1223,10 @@ class MirroredStrategyDefunTest(test.TestCase): def model_fn(mock_model): return two_x_plus_one(mock_model) - self._call_and_check(model_fn, [], 3.5, [times_two, two_x_plus_one]) - - @test_util.run_in_graph_and_eager_modes() - def testTwoVariablesInNestedDefun(self): - self._skip_eager_if_gpus_less_than(1) + self._call_and_check(distribution, model_fn, [], 3.5, + [times_two, two_x_plus_one]) + def testTwoVariablesInNestedDefun(self, distribution): @function.defun def fn1(mock_model): return mock_model() @@ -1359,12 +1238,10 @@ class MirroredStrategyDefunTest(test.TestCase): def model_fn(mock_model): return fn2(mock_model) - self._call_and_check(model_fn, [], 5.5, [fn1, fn2], two_variables=True) - - @test_util.run_in_graph_and_eager_modes() - def testGradientTapeOverNestedDefuns(self): - self._skip_eager_if_gpus_less_than(1) + self._call_and_check(distribution, model_fn, [], 5.5, [fn1, fn2], + two_variables=True) + def testGradientTapeOverNestedDefuns(self, distribution): @function.defun def fn1(mock_model): return mock_model() @@ -1380,13 +1257,10 @@ class MirroredStrategyDefunTest(test.TestCase): [v.get() for v in mock_model.variables]) return grads - self._call_and_check(model_fn, [], [2.0, 1.0], [fn1, fn2], + self._call_and_check(distribution, model_fn, [], [2.0, 1.0], [fn1, fn2], two_variables=True) - @test_util.run_in_graph_and_eager_modes() - def testPassPerReplica(self): - self._skip_eager_if_gpus_less_than(1) - + def testPassPerReplica(self, distribution): @function.defun def fn1(mock_model, factor): return mock_model(factor) @@ -1394,18 +1268,10 @@ class MirroredStrategyDefunTest(test.TestCase): factors = values.PerReplica({"CPU:0": 5.0, "GPU:0": 3.0}) expected_result = values.PerReplica({"CPU:0": 5.0 * 1.25, "GPU:0": 3.0 * 1.25}) - self._call_and_check(fn1, [factors], expected_result, [fn1]) - - @test_util.run_in_graph_and_eager_modes() - def testTrain(self): - self._skip_eager_if_gpus_less_than(1) + self._call_and_check(distribution, fn1, [factors], expected_result, [fn1]) - cpu_dev = device_util.canonicalize("CPU:0") - gpu_dev = device_util.canonicalize("GPU:0") - devices = [cpu_dev, gpu_dev] - dist = mirrored_strategy.MirroredStrategy(devices) - - with dist.scope(): + def testTrain(self, distribution): + with distribution.scope(): mock_model = MiniModel() mock_model.call = function.defun(mock_model.call) @@ -1415,10 +1281,11 @@ class MirroredStrategyDefunTest(test.TestCase): gradients_fn = backprop.implicit_grad(loss_fn) gradients_fn = optimizer_lib.get_filtered_grad_fn(gradients_fn) - grads_and_vars = dist.call_for_each_replica(gradients_fn, args=(None,)) + grads_and_vars = distribution.extended.call_for_each_replica( + gradients_fn, args=(None,)) optimizer = gradient_descent.GradientDescentOptimizer(0.25) - update_ops = optimizer._distributed_apply(dist, grads_and_vars) # pylint: disable=protected-access + update_ops = optimizer._distributed_apply(distribution, grads_and_vars) # pylint: disable=protected-access if not context.executing_eagerly(): self.evaluate(variables.global_variables_initializer()) @@ -1430,30 +1297,82 @@ class MirroredStrategyDefunTest(test.TestCase): self.assertAllEqual([0.5], updated_var_values[1]) +@combinations.generate( + combinations.combine( + distribution=[ + combinations.NamedDistribution( + "Mirrored", + # pylint: disable=g-long-lambda + lambda: mirrored_strategy.MirroredStrategy(num_gpus_per_worker= + context.num_gpus()), + required_gpus=1), + combinations.NamedDistribution( + "CoreMirrored", + # pylint: disable=g-long-lambda + lambda: mirrored_strategy.CoreMirroredStrategy( + mirrored_strategy.all_local_devices()), + required_gpus=1) + ], + mode=["graph"])) class MultiWorkerMirroredStrategyTest( multi_worker_test_base.MultiWorkerTestBase, strategy_test_lib.DistributionTestBase): - def _get_distribution_strategy(self): + def _configure_distribution_strategy(self, distribution): cluster_spec = server_lib.ClusterSpec({ "worker": ["/job:worker/task:0", "/job:worker/task:1"] }) - strategy = mirrored_strategy.MirroredStrategy(num_gpus=context.num_gpus()) - strategy.configure(cluster_spec=cluster_spec) - return strategy - - def test_num_replicas_in_sync(self): - if not GPU_TEST: - self.skipTest("Not GPU test") + distribution.configure(cluster_spec=cluster_spec) - strategy = self._get_distribution_strategy() + def test_num_replicas_in_sync(self, distribution): + self._configure_distribution_strategy(distribution) # We calculate the total number of gpus across the workers(2) specified in # the cluster spec. - self.assertEqual(context.num_gpus() * 2, strategy.num_replicas_in_sync) - - def testMinimizeLossGraph(self): - self._test_minimize_loss_graph(self._get_distribution_strategy(), - learning_rate=0.05) + self.assertEqual(context.num_gpus() * 2, distribution.num_replicas_in_sync) + + def testMinimizeLossGraph(self, distribution): + self._configure_distribution_strategy(distribution) + self._test_minimize_loss_graph(distribution, learning_rate=0.05) + + def testDeviceScope(self, distribution): + """Test the device scope of multi-worker MirroredStrategy.""" + self._configure_distribution_strategy(distribution) + with distribution.scope(): + a = constant_op.constant(1.) + with ops.device("/cpu:0"): + b = constant_op.constant(1.) + self.assertEqual(a.device, "/job:worker/task:0") + self.assertEqual(b.device, "/job:worker/task:0/device:CPU:0") + + def testMakeInputFnIterator(self, distribution): + self._configure_distribution_strategy(distribution) + dataset_fn = lambda: dataset_ops.Dataset.range(100) + num_gpus = context.num_gpus() + num_workers = 2 + + expected_values = [[i+j for j in range(num_gpus)] * num_workers + for i in range(0, 100, num_gpus)] + + with context.graph_mode(), self.cached_session() as sess: + # `expected_input_pipeline_id` is None because the input_fn will be called + # multiple times, each with a different input_pipeline_id. + input_fn = self._input_fn_to_test_input_context( + dataset_fn, + expected_num_replicas_in_sync=num_workers*num_gpus, + expected_num_input_pipelines=num_workers, + expected_input_pipeline_id=None) + iterator = distribution.make_input_fn_iterator(input_fn) + self._test_input_fn_iterator( + iterator, distribution.extended.worker_devices, expected_values, sess) + + def testUpdateConfigProto(self, distribution): + distribution.configure(cluster_spec={"worker": ["fake1", "fake2"]}) + + config_proto = config_pb2.ConfigProto() + new_config = distribution.update_config_proto(config_proto) + + # Verify isolate_session_state + self.assertTrue(new_config.isolate_session_state) class MultiWorkerMirroredStrategyTestWithChief( @@ -1473,6 +1392,19 @@ class MultiWorkerMirroredStrategyTestWithChief( strategy.configure(cluster_spec=self._cluster_spec) self._test_minimize_loss_graph(strategy, learning_rate=0.05) + def testMinimizeLossGraphCoreMirroredStrategy(self): + strategy = mirrored_strategy.CoreMirroredStrategy( + mirrored_strategy.all_local_devices()) + strategy.configure(cluster_spec=self._cluster_spec) + self._test_minimize_loss_graph(strategy, learning_rate=0.05) + + +def _replica_id(): + replica_id = ds_context.get_replica_context().replica_id_in_sync_group + if not isinstance(replica_id, ops.Tensor): + replica_id = constant_op.constant(replica_id) + return replica_id + if __name__ == "__main__": test.main() diff --git a/tensorflow/contrib/distribute/python/mirrored_strategy_test.py b/tensorflow/contrib/distribute/python/mirrored_strategy_test.py deleted file mode 100644 index bea684e77c..0000000000 --- a/tensorflow/contrib/distribute/python/mirrored_strategy_test.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright 2018 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 class MirroredStrategy.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -from tensorflow.contrib.distribute.python import mirrored_strategy -from tensorflow.contrib.distribute.python import strategy_test_lib -from tensorflow.python.eager import context -from tensorflow.python.eager import test -from tensorflow.python.framework import constant_op -from tensorflow.python.framework import ops -from tensorflow.python.framework import test_util -from tensorflow.python.ops import variable_scope -from tensorflow.python.training import distribution_strategy_context - - -class MirroredOneCPUDistributionTest(strategy_test_lib.DistributionTestBase): - - def _get_distribution_strategy(self): - return mirrored_strategy.MirroredStrategy(["/device:CPU:0"]) - - def testMinimizeLossEager(self): - self._test_minimize_loss_eager(self._get_distribution_strategy()) - - def testMinimizeLossGraph(self): - self._test_minimize_loss_graph(self._get_distribution_strategy()) - - def testDeviceIndex(self): - self._test_device_index(self._get_distribution_strategy()) - - def testReplicaId(self): - self._test_replica_id(self._get_distribution_strategy()) - - @test_util.run_in_graph_and_eager_modes - def testCallAndMergeExceptions(self): - self._test_call_and_merge_exceptions(self._get_distribution_strategy()) - - -class VariableCreatorStackTest(test.TestCase): - - def testCreatorStacksAreThreadLocal(self): - devices = ["/device:CPU:0", "/device:GPU:0"] - dist = mirrored_strategy.MirroredStrategy(devices) - - def model_fn(device_id): - assert isinstance(device_id, int) - - def thread_creator_fn(next_creator, *args, **kwargs): - return next_creator(*args, **kwargs) + ":thread_" + str(device_id) - - with variable_scope.variable_creator_scope(thread_creator_fn): - # Create a variable in this scope. - v = variable_scope.variable(1.0) - - # This will pause the current thread, and execute the other thread. - distribution_strategy_context.get_replica_context().merge_call( - lambda _: _) - return v - - def main_thread_creator(next_creator, *args, **kwargs): - # We are not using the underlying next_creator for test purposes. - del next_creator, args, kwargs - return "main_thread" - - with context.graph_mode(), \ - dist.scope(), \ - variable_scope.variable_creator_scope(main_thread_creator): - result = dist.call_for_each_replica( - model_fn, args=(dist.worker_device_index,)) - result = dist.unwrap(result) - expected = ["main_thread:thread_0", "main_thread:thread_1"] - self.assertEquals(expected, result) - - -class MultiWorkerMirroredStrategyTest(test.TestCase): - - def testDeviceScope(self): - """Test the device scope of multi-worker MirroredStrategy.""" - with context.graph_mode(): - strategy = mirrored_strategy.MirroredStrategy(num_gpus=context.num_gpus()) - strategy.configure( - cluster_spec={"worker": ["/job:worker/task:0", "/job:worker/task:1"]}) - with strategy.scope(): - a = constant_op.constant(1.) - with ops.device("/cpu:0"): - b = constant_op.constant(1.) - self.assertEqual(a.device, "/job:worker/task:0") - self.assertEqual(b.device, "/job:worker/task:0/device:CPU:0") - - -if __name__ == "__main__": - test.main() diff --git a/tensorflow/contrib/distribute/python/moving_averages_test.py b/tensorflow/contrib/distribute/python/moving_averages_test.py index 7ecc852d20..c492d8bafc 100644 --- a/tensorflow/contrib/distribute/python/moving_averages_test.py +++ b/tensorflow/contrib/distribute/python/moving_averages_test.py @@ -32,7 +32,8 @@ from tensorflow.python.training import moving_averages all_combinations = combinations.combine( distribution=[combinations.default_strategy, combinations.one_device_strategy, - combinations.mirrored_strategy_with_gpu_and_cpu], + combinations.mirrored_strategy_with_gpu_and_cpu, + combinations.core_mirrored_strategy_with_gpu_and_cpu], mode=["graph"]) diff --git a/tensorflow/contrib/distribute/python/multi_worker_test_base.py b/tensorflow/contrib/distribute/python/multi_worker_test_base.py index 8eec3dc0f6..147c9b83f8 100644 --- a/tensorflow/contrib/distribute/python/multi_worker_test_base.py +++ b/tensorflow/contrib/distribute/python/multi_worker_test_base.py @@ -18,8 +18,11 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import collections import contextlib import copy +import json +import os import threading import numpy as np @@ -271,7 +274,6 @@ class MultiWorkerTestBase(test.TestCase): return config - def _run_client(self, client_fn, task_type, task_id, num_gpus, *args, **kwargs): result = client_fn(task_type, task_id, num_gpus, *args, **kwargs) @@ -303,3 +305,101 @@ class MultiWorkerTestBase(test.TestCase): for t in threads: t.join() self.assertEqual(self._result, len(threads)) + + +class MockOsEnv(collections.Mapping): + """A class that allows per-thread TF_CONFIG.""" + + def __init__(self, *args): + self._dict = dict() + self._thread_local = threading.local() + super(MockOsEnv, self).__init__(*args) + + def get(self, key, default=None): + if not hasattr(self._thread_local, 'dict'): + self._thread_local.dict = dict() + if key == 'TF_CONFIG': + return dict.get(self._thread_local.dict, key, default) + else: + return dict.get(self._dict, key, default) + + def __getitem__(self, key): + if not hasattr(self._thread_local, 'dict'): + self._thread_local.dict = dict() + if key == 'TF_CONFIG': + return dict.__getitem__(self._thread_local.dict, key) + else: + return dict.__getitem__(self._dict, key) + + def __setitem__(self, key, val): + if not hasattr(self._thread_local, 'dict'): + self._thread_local.dict = dict() + if key == 'TF_CONFIG': + return dict.__setitem__(self._thread_local.dict, key, val) + else: + return dict.__setitem__(self._dict, key, val) + + def __iter__(self): + if not hasattr(self._thread_local, 'dict'): + self._thread_local.dict = dict() + for x in self._thread_local.dict.items(): + yield x + for x in self._dict.items(): + yield x + + def __len__(self): + if not hasattr(self._thread_local, 'dict'): + self._thread_local.dict = dict() + return self._thread_local.dict.__len__() + self._dict.__len__() + + +class IndependentWorkerTestBase(test.TestCase): + """Testing infra for independent workers.""" + + def setUp(self): + self._mock_os_env = MockOsEnv() + self._mock_context = test.mock.patch.object(os, 'environ', + self._mock_os_env) + super(IndependentWorkerTestBase, self).setUp() + self._mock_context.__enter__() + + def tearDown(self): + self._mock_context.__exit__(None, None, None) + super(IndependentWorkerTestBase, self).tearDown() + + def _task_thread(self, task_fn, tf_config, *args, **kwargs): + os.environ['TF_CONFIG'] = json.dumps(tf_config) + task_fn(*args, **kwargs) + + def _run_task_in_thread(self, task_fn, cluster_spec, task_type, task_id, + *args, **kwargs): + if task_type: + tf_config = { + 'cluster': cluster_spec, + 'task': { + 'type': task_type, + 'index': task_id + } + } + else: + tf_config = { + 'cluster': cluster_spec, + } + t = threading.Thread( + target=self._task_thread, + args=(task_fn, tf_config) + args, + kwargs=kwargs) + t.start() + return t + + def run_multiple_tasks_in_threads(self, task_fn, cluster_spec, *args, + **kwargs): + # The task_fn should create std_server by itself. + threads = {} + for task_type in cluster_spec.keys(): + threads[task_type] = [] + for task_id in range(len(cluster_spec[task_type])): + t = self._run_task_in_thread(task_fn, cluster_spec, task_type, task_id, + *args, **kwargs) + threads[task_type].append(t) + return threads diff --git a/tensorflow/contrib/distribute/python/one_device_strategy.py b/tensorflow/contrib/distribute/python/one_device_strategy.py index a0d8f93887..e322b6acb8 100644 --- a/tensorflow/contrib/distribute/python/one_device_strategy.py +++ b/tensorflow/contrib/distribute/python/one_device_strategy.py @@ -20,12 +20,14 @@ from __future__ import print_function import six -from tensorflow.contrib.distribute.python import values +from tensorflow.python.distribute import device_util +from tensorflow.python.distribute import distribute_lib +from tensorflow.python.distribute import values from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.ops import array_ops from tensorflow.python.ops import control_flow_ops -from tensorflow.python.training import distribute as distribute_lib from tensorflow.python.util import nest @@ -39,7 +41,14 @@ class OneDeviceStrategy(distribute_lib.DistributionStrategy): # implementations? def __init__(self, device): - super(OneDeviceStrategy, self).__init__() + super(OneDeviceStrategy, self).__init__(OneDeviceExtended(self, device)) + + +class OneDeviceExtended(distribute_lib.DistributionStrategyExtended): + """Implementation of OneDeviceStrategy.""" + + def __init__(self, container_strategy, device): + super(OneDeviceExtended, self).__init__(container_strategy) self._device = device self._default_device = device @@ -58,17 +67,33 @@ class OneDeviceStrategy(distribute_lib.DistributionStrategy): with ops.colocate_with(colocate_with): return next_creator(*args, **kwargs) - def distribute_dataset(self, dataset_fn): + def _make_dataset_iterator(self, dataset): + """Make iterator from dataset without splitting the batch.""" + worker = device_util.canonicalize("/device:CPU:0") + worker_device_pairs = [(worker, [self._device])] + return values.DatasetIterator(dataset, worker_device_pairs) + + def _distribute_dataset(self, dataset_fn): return values.PerReplicaDataset( self._call_dataset_fn(dataset_fn), [self._device]) - def _broadcast(self, tensor, destinations): + def _make_input_fn_iterator( + self, + input_fn, + replication_mode=distribute_lib.InputReplicationMode.PER_WORKER): + worker = device_util.canonicalize("/device:CPU:0") + worker_device_pairs = [(worker, [self._device])] + return values.InputFunctionIterator( + input_fn, worker_device_pairs, + [distribute_lib.InputContext()]) + + def _broadcast_to(self, tensor, destinations): del destinations return tensor # TODO(priyag): Deal with OutOfRange errors once b/111349762 is fixed. - def _run_steps_on_dataset(self, fn, iterator, iterations, - initial_loop_values=None): + def _experimental_run_steps_on_iterator(self, fn, iterator, iterations, + initial_loop_values=None): if initial_loop_values is None: initial_loop_values = {} initial_loop_values = nest.flatten(initial_loop_values) @@ -80,7 +105,7 @@ class OneDeviceStrategy(distribute_lib.DistributionStrategy): fn_inputs = iterator.get_next() if not isinstance(fn_inputs, tuple): fn_inputs = (fn_inputs,) - fn_result = fn(ctx, *fn_inputs) + fn_result = fn(ctx, fn_inputs) flat_last_step_outputs = nest.flatten(ctx.last_step_outputs) with ops.control_dependencies([fn_result]): return [i + 1] + flat_last_step_outputs @@ -114,25 +139,24 @@ class OneDeviceStrategy(distribute_lib.DistributionStrategy): return ctx def _call_for_each_replica(self, fn, args, kwargs): - with ops.device(self._device), _OneDeviceReplicaContext(self): + strategy = self._container_strategy() + with ops.device(self._device), _OneDeviceReplicaContext(strategy): return fn(*args, **kwargs) - def _reduce(self, aggregation, value, destinations): - del aggregation, destinations + def _reduce_to(self, reduce_op, value, destinations): + del reduce_op, destinations return value - def _update(self, var, options, fn, *args, **kwargs): + def _update(self, var, fn, args, kwargs, group): # The implementations of _update() and _update_non_slot() are identical # except _update() passes `var` as the first argument to `fn()`. - return self._update_non_slot(var, options, fn, var, *args, **kwargs) + return self._update_non_slot(var, fn, (var,) + tuple(args), kwargs, group) - def _update_non_slot(self, colocate_with, options, fn, *args, **kwargs): + def _update_non_slot(self, colocate_with, fn, args, kwargs, group): del colocate_with - should_group = options.pop("grouped") - assert not options # Validate that we are processing all of the options. with ops.device(self._device), distribute_lib.UpdateContext(self._device): result = fn(*args, **kwargs) - if should_group: + if group: return result else: return nest.map_structure(self._unwrap, result) @@ -148,11 +172,7 @@ class OneDeviceStrategy(distribute_lib.DistributionStrategy): return value @property - def num_replicas(self): - return 1 - - @property - def num_replicas_in_sync(self): + def _num_replicas_in_sync(self): return 1 @property @@ -167,8 +187,22 @@ class OneDeviceStrategy(distribute_lib.DistributionStrategy): del var_list return [self._device] - def _worker_device_index(self): - return 0 + @property + def experimental_should_init(self): + return True + + @property + def should_checkpoint(self): + return True + + @property + def should_save_summary(self): + return True + + # TODO(priyag): Delete this once all strategies use global batch size. + @property + def _global_batch_size(self): + return True class _OneDeviceReplicaContext(distribute_lib.ReplicaContext): @@ -176,12 +210,10 @@ class _OneDeviceReplicaContext(distribute_lib.ReplicaContext): def __init__(self, distribution_strategy): distribute_lib.ReplicaContext.__init__( - self, distribution_strategy, replica_id=0) - - @property - def device(self): - raise RuntimeError("Use .devices instead") + self, + distribution_strategy, + replica_id_in_sync_group=constant_op.constant(0, dtypes.int32)) @property def devices(self): - return [self._distribution_strategy.worker_devices[0]] + return [self._distribution_strategy.extended.worker_devices[0]] diff --git a/tensorflow/contrib/distribute/python/one_device_strategy_test.py b/tensorflow/contrib/distribute/python/one_device_strategy_test.py index 95f4cdb786..d46cd6f529 100644 --- a/tensorflow/contrib/distribute/python/one_device_strategy_test.py +++ b/tensorflow/contrib/distribute/python/one_device_strategy_test.py @@ -20,6 +20,7 @@ from __future__ import print_function from tensorflow.contrib.distribute.python import one_device_strategy from tensorflow.contrib.distribute.python import strategy_test_lib +from tensorflow.python.data.ops import dataset_ops from tensorflow.python.eager import test from tensorflow.python.framework import test_util @@ -35,9 +36,6 @@ class OneDeviceStrategyTest(strategy_test_lib.DistributionTestBase): def testMinimizeLossGraph(self): self._test_minimize_loss_graph(self._get_distribution_strategy()) - def testDeviceIndex(self): - self._test_device_index(self._get_distribution_strategy()) - def testReplicaId(self): self._test_replica_id(self._get_distribution_strategy()) @@ -45,6 +43,20 @@ class OneDeviceStrategyTest(strategy_test_lib.DistributionTestBase): def testCallAndMergeExceptions(self): self._test_call_and_merge_exceptions(self._get_distribution_strategy()) + @test_util.run_in_graph_and_eager_modes + def testMakeInputFnIterator(self): + d = one_device_strategy.OneDeviceStrategy("/device:CPU:0") + dataset_fn = lambda: dataset_ops.Dataset.range(10) + expected_values = [[i] for i in range(10)] + input_fn = self._input_fn_to_test_input_context( + dataset_fn, + expected_num_replicas_in_sync=1, + expected_num_input_pipelines=1, + expected_input_pipeline_id=0) + iterator = d.make_input_fn_iterator(input_fn) + self._test_input_fn_iterator( + iterator, d.extended.worker_devices, expected_values) + if __name__ == "__main__": test.main() diff --git a/tensorflow/contrib/distribute/python/parameter_server_strategy.py b/tensorflow/contrib/distribute/python/parameter_server_strategy.py index 790b37f860..eaeb4d7030 100644 --- a/tensorflow/contrib/distribute/python/parameter_server_strategy.py +++ b/tensorflow/contrib/distribute/python/parameter_server_strategy.py @@ -18,10 +18,14 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -from tensorflow.contrib.distribute.python import cross_tower_ops as cross_tower_ops_lib +import copy + from tensorflow.contrib.distribute.python import mirrored_strategy -from tensorflow.contrib.distribute.python import values +from tensorflow.python.distribute import cross_device_ops as cross_device_ops_lib +from tensorflow.python.distribute import device_util +from tensorflow.python.distribute import distribute_lib from tensorflow.python.distribute import multi_worker_util +from tensorflow.python.distribute import values from tensorflow.python.eager import context from tensorflow.python.framework import device as tf_device from tensorflow.python.framework import ops @@ -30,8 +34,6 @@ from tensorflow.python.ops import resource_variable_ops from tensorflow.python.ops import variable_scope as vs from tensorflow.python.platform import tf_logging as logging from tensorflow.python.training import device_setter -from tensorflow.python.training import device_util -from tensorflow.python.training import distribute as distribute_lib from tensorflow.python.util import nest _LOCAL_CPU = "/device:CPU:0" @@ -94,13 +96,21 @@ class ParameterServerStrategy(distribute_lib.DistributionStrategy): ValueError: if `cluster_spec` is given but `task_type` or `task_id` is not. """ - super(ParameterServerStrategy, self).__init__() + super(ParameterServerStrategy, self).__init__( + ParameterServerExtended(self, num_gpus_per_worker)) + + +class ParameterServerExtended(distribute_lib.DistributionStrategyExtended): + """Implementation of ParameterServerStrategy.""" + + def __init__(self, container_strategy, num_gpus_per_worker): + super(ParameterServerExtended, self).__init__(container_strategy) self._num_gpus_per_worker = num_gpus_per_worker self._initialize_local(num_gpus_per_worker) # We typically don't need to do all-reduce in this strategy. - self._cross_tower_ops = ( - cross_tower_ops_lib.ReductionToOneDeviceCrossDeviceOps( + self._cross_device_ops = ( + cross_device_ops_lib.ReductionToOneDeviceCrossDeviceOps( reduce_to_device=_LOCAL_CPU)) def _initialize_multi_worker(self, num_gpus_per_worker, cluster_spec, @@ -189,6 +199,7 @@ class ParameterServerStrategy(distribute_lib.DistributionStrategy): def _initialize_local(self, num_gpus_per_worker): """Initialize internal devices for local training.""" + self._worker_device = device_util.canonicalize("/device:CPU:0") # Define compute devices which is a list of device strings and one for each # replica. When there are GPUs, replicate operations on these GPUs. # Otherwise, place operations on CPU. @@ -221,15 +232,48 @@ class ParameterServerStrategy(distribute_lib.DistributionStrategy): "ParameterServerStrategy with compute_devices = %r, " "variable_device = %r", self._compute_devices, self._variable_device) - def distribute_dataset(self, dataset_fn): + def _distribute_dataset(self, dataset_fn): """Distributes the dataset to each local GPU.""" return values.PerReplicaDataset( self._call_dataset_fn(dataset_fn), self._compute_devices, True) - def _broadcast(self, tensor, destinations): - if not cross_tower_ops_lib.check_destinations(destinations): + def _make_dataset_iterator(self, dataset): + worker_device_pairs = [(self._worker_device, self._compute_devices)] + return values.DatasetIterator(dataset, worker_device_pairs, + self._num_replicas_in_sync) + + def _make_input_fn_iterator( + self, + input_fn, + replication_mode=distribute_lib.InputReplicationMode.PER_WORKER): + """Distributes the dataset to each local GPU.""" + if self._cluster_spec: + input_pipeline_id = multi_worker_util.id_in_cluster( + self._cluster_spec, self._task_type, self._task_id) + num_input_pipelines = multi_worker_util.worker_count( + self._cluster_spec, self._task_type) + else: + input_pipeline_id = 0 + num_input_pipelines = 1 + input_context = distribute_lib.InputContext( + num_input_pipelines=num_input_pipelines, + input_pipeline_id=input_pipeline_id, + num_replicas_in_sync=self._num_replicas_in_sync) + worker_device_pairs = [(self._worker_device, self._compute_devices)] + return values.InputFunctionIterator( + input_fn, worker_device_pairs, [input_context]) + + def _broadcast_to(self, tensor, destinations): + # This is both a fast path for Python constants, and a way to delay + # converting Python values to a tensor until we know what type it + # should be converted to. Otherwise we have trouble with: + # global_step.assign_add(1) + # since the `1` gets broadcast as an int32 but global_step is int64. + if isinstance(tensor, (float, int)): + return tensor + if not cross_device_ops_lib.check_destinations(destinations): destinations = self._compute_devices - return self._cross_tower_ops.broadcast(tensor, destinations) + return self._cross_device_ops.broadcast(tensor, destinations) def _allow_variable_partition(self): return not context.executing_eagerly() @@ -237,7 +281,7 @@ class ParameterServerStrategy(distribute_lib.DistributionStrategy): # TODO(yuefengz): not all ops in device_setter.STANDARD_PS_OPS will go through # this creator, such as "MutableHashTable". def _create_variable(self, next_creator, *args, **kwargs): - if self.num_replicas_in_sync > 1: + if self._num_replicas_in_sync > 1: aggregation = kwargs.pop("aggregation", vs.VariableAggregation.NONE) if aggregation not in ( vs.VariableAggregation.NONE, @@ -293,39 +337,35 @@ class ParameterServerStrategy(distribute_lib.DistributionStrategy): def _call_for_each_replica(self, fn, args, kwargs): # pylint: disable=protected-access - return mirrored_strategy._call_for_each_replica(self, fn, args, kwargs) + return mirrored_strategy._call_for_each_replica( + self._container_strategy(), fn, args, kwargs) def _verify_destinations_not_different_worker(self, destinations): if not self._cluster_spec: return if destinations is None: return - for d in cross_tower_ops_lib.get_devices_from(destinations): + for d in cross_device_ops_lib.get_devices_from(destinations): d_spec = tf_device.DeviceSpec.from_string(d) if d_spec.job == self._task_type and d_spec.task != self._task_id: raise ValueError( "Cannot reduce to another worker: %r, current worker is %r" % (d, self._worker_device)) - def _reduce(self, aggregation, value, destinations): + def _reduce_to(self, reduce_op, value, destinations): self._verify_destinations_not_different_worker(destinations) if not isinstance(value, values.DistributedValues): # pylint: disable=protected-access return mirrored_strategy._reduce_non_distributed_value( - self, aggregation, value, destinations) - if aggregation == vs.VariableAggregation.ONLY_FIRST_REPLICA: - return self.broadcast(value.get(self._compute_devices[0]), destinations) - return self._cross_tower_ops.reduce( - aggregation, value, destinations=destinations) - - def _batch_reduce(self, aggregation, value_destination_pairs): - if aggregation == vs.VariableAggregation.ONLY_FIRST_REPLICA: - return [self.broadcast(v.get(self._compute_devices[0]), d) - for v, d in value_destination_pairs] + self, reduce_op, value, destinations) + return self._cross_device_ops.reduce( + reduce_op, value, destinations=destinations) + + def _batch_reduce_to(self, reduce_op, value_destination_pairs): for _, destinations in value_destination_pairs: self._verify_destinations_not_different_worker(destinations) - return self._cross_tower_ops.batch_reduce(aggregation, - value_destination_pairs) + return self._cross_device_ops.batch_reduce(reduce_op, + value_destination_pairs) def _select_single_value(self, structured): """Select any single values in `structured`.""" @@ -349,30 +389,26 @@ class ParameterServerStrategy(distribute_lib.DistributionStrategy): return nest.map_structure(_select_fn, structured) - def _update(self, var, options, fn, *args, **kwargs): + def _update(self, var, fn, args, kwargs, group): if isinstance(var, values.AggregatingVariable): var = var.get() if not isinstance(var, resource_variable_ops.ResourceVariable): raise ValueError( "You can not update `var` %r. It must be a Variable." % var) - should_group = options.pop("grouped") - assert not options # Validate that we are processing all of the options. with ops.colocate_with(var), distribute_lib.UpdateContext(var.device): result = fn(var, *self._select_single_value(args), **self._select_single_value(kwargs)) - if should_group: + if group: return result else: return nest.map_structure(self._unwrap, result) # TODO(yuefengz): does it need to call _select_single_value? - def _update_non_slot(self, colocate_with, options, fn, *args, **kwargs): - should_group = options.pop("grouped") - assert not options # Validate that we are processing all of the options. + def _update_non_slot(self, colocate_with, fn, args, kwargs, group): with ops.device( colocate_with.device), distribute_lib.UpdateContext(colocate_with): result = fn(*args, **kwargs) - if should_group: + if group: return result else: return nest.map_structure(self._unwrap, result) @@ -398,11 +434,11 @@ class ParameterServerStrategy(distribute_lib.DistributionStrategy): # variables. return array_ops.identity(var) - def configure(self, - session_config=None, - cluster_spec=None, - task_type=None, - task_id=None): + def _configure(self, + session_config=None, + cluster_spec=None, + task_type=None, + task_id=None): """Configures the strategy class. The strategy object will be re-initialized if `cluster_spec` is given but @@ -433,28 +469,30 @@ class ParameterServerStrategy(distribute_lib.DistributionStrategy): self._initialize_multi_worker(self._num_gpus_per_worker, self._cluster_spec, task_type, task_id) - if not session_config or not self._cluster_spec: - return + if session_config: + session_config.CopyFrom(self._update_config_proto(session_config)) - session_config.isolate_session_state = False + def _update_config_proto(self, config_proto): + updated_config = copy.deepcopy(config_proto) + if not self._cluster_spec: + updated_config.isolate_session_state = True + return updated_config + + updated_config.isolate_session_state = False - assert self._cluster_spec assert self._task_type assert self._task_id is not None # The device filters prevent communication between workers. if self._task_type not in ["chief", "worker"]: - return - del session_config.device_filters[:] - session_config.device_filters.extend( + return updated_config + del updated_config.device_filters[:] + updated_config.device_filters.extend( ["/job:%s/task:%d" % (self._task_type, self._task_id), "/job:ps"]) + return updated_config @property - def num_replicas(self): - return len(self._compute_devices) - - @property - def num_replicas_in_sync(self): + def _num_replicas_in_sync(self): return len(self._compute_devices) @property @@ -470,11 +508,12 @@ class ParameterServerStrategy(distribute_lib.DistributionStrategy): return min(var_list, key=lambda x: x.name) @property - def between_graph(self): + def experimental_between_graph(self): + # TODO(yuefengz): Should this return False in the local case? return True @property - def should_init(self): + def experimental_should_init(self): return self._is_chief @property @@ -484,3 +523,8 @@ class ParameterServerStrategy(distribute_lib.DistributionStrategy): @property def should_save_summary(self): return self._is_chief + + # TODO(priyag): Delete this once all strategies use global batch size. + @property + def _global_batch_size(self): + return False diff --git a/tensorflow/contrib/distribute/python/parameter_server_strategy_test.py b/tensorflow/contrib/distribute/python/parameter_server_strategy_test.py index 81a23c8903..83d7473666 100644 --- a/tensorflow/contrib/distribute/python/parameter_server_strategy_test.py +++ b/tensorflow/contrib/distribute/python/parameter_server_strategy_test.py @@ -25,14 +25,21 @@ from absl.testing import parameterized from tensorflow.contrib.distribute.python import combinations from tensorflow.contrib.distribute.python import multi_worker_test_base from tensorflow.contrib.distribute.python import parameter_server_strategy -from tensorflow.contrib.distribute.python import values +from tensorflow.contrib.distribute.python import strategy_test_lib from tensorflow.core.protobuf import config_pb2 +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.distribute import device_util +from tensorflow.python.distribute import distribution_strategy_context as ds_context from tensorflow.python.distribute import multi_worker_util +from tensorflow.python.distribute import reduce_util +from tensorflow.python.distribute import values from tensorflow.python.eager import backprop from tensorflow.python.eager import context from tensorflow.python.estimator import run_config from tensorflow.python.framework import constant_op +from tensorflow.python.framework import errors from tensorflow.python.framework import ops +from tensorflow.python.framework import tensor_util from tensorflow.python.layers import core from tensorflow.python.ops import array_ops from tensorflow.python.ops import control_flow_ops @@ -41,8 +48,6 @@ from tensorflow.python.ops import partitioned_variables from tensorflow.python.ops import variable_scope from tensorflow.python.ops import variables from tensorflow.python.platform import test -from tensorflow.python.training import device_util -from tensorflow.python.training import distribution_strategy_context from tensorflow.python.training import training_util CHIEF = run_config.TaskType.CHIEF @@ -50,6 +55,13 @@ WORKER = run_config.TaskType.WORKER PS = run_config.TaskType.PS +def _get_replica_id_integer(): + replica_id = ds_context.get_replica_context().replica_id_in_sync_group + if isinstance(replica_id, ops.Tensor): + replica_id = tensor_util.constant_value(replica_id) + return replica_id + + class ParameterServerStrategyTestBase( multi_worker_test_base.MultiWorkerTestBase): @@ -94,9 +106,8 @@ class ParameterServerStrategyTestBase( if num_gpus == 0: last_part_device = 'device:CPU:0' else: - last_part_device = ( - 'device:GPU:%d' % - distribution_strategy_context.get_replica_context().replica_id) + replica_id = _get_replica_id_integer() + last_part_device = ('device:GPU:%d' % replica_id) a = constant_op.constant(1.0) b = constant_op.constant(2.0) @@ -261,18 +272,16 @@ class ParameterServerStrategyTestBase( if 'CPU' in compute_device: replica_compute_device = '/device:CPU:0' else: - replica_compute_device = ( - '/device:GPU:%d' % - distribution_strategy_context.get_replica_context().replica_id) + replica_id = _get_replica_id_integer() + replica_compute_device = ('/device:GPU:%d' % replica_id) replica_compute_device = device_util.canonicalize( replica_compute_device) if 'CPU' in variable_device: replica_variable_device = '/device:CPU:0' else: - replica_variable_device = ( - '/device:GPU:%d' % - distribution_strategy_context.get_replica_context().replica_id) + replica_id = _get_replica_id_integer() + replica_variable_device = ('/device:GPU:%d' % replica_id) replica_variable_device = device_util.canonicalize( replica_variable_device) @@ -354,9 +363,9 @@ class ParameterServerStrategyTestBase( def _test_simple_increment(self, task_type, task_id, num_gpus): d, master_target, sess_config = self._get_test_objects( task_type, task_id, num_gpus) - if hasattr(d, '_cluster_spec') and d._cluster_spec: - num_workers = len(d._cluster_spec.as_dict().get(WORKER)) - if 'chief' in d._cluster_spec.as_dict(): + if d.extended._cluster_spec: + num_workers = len(d.extended._cluster_spec.as_dict().get(WORKER)) + if 'chief' in d.extended._cluster_spec.as_dict(): num_workers += 1 else: num_workers = 1 @@ -389,7 +398,7 @@ class ParameterServerStrategyTestBase( x, y, z, train_op = d.call_for_each_replica(model_fn) train_op = d.group(train_op) - if context.num_gpus() < d._num_gpus_per_worker: + if context.num_gpus() < d.extended._num_gpus_per_worker: return True if task_id == 0: @@ -426,9 +435,9 @@ class ParameterServerStrategyTestBase( task_type, task_id, num_gpus) if task_type: # Multi-worker - assert hasattr(d, '_cluster_spec') and d._cluster_spec - num_workers = len(d._cluster_spec.as_dict().get(WORKER)) - if CHIEF in d._cluster_spec.as_dict(): + assert hasattr(d.extended, '_cluster_spec') and d.extended._cluster_spec + num_workers = len(d.extended._cluster_spec.as_dict().get(WORKER)) + if CHIEF in d.extended._cluster_spec.as_dict(): num_workers += 1 else: # local @@ -472,8 +481,8 @@ class ParameterServerStrategyTestBase( before_list.append(fetched) with ops.control_dependencies([fetched]): # TODO(yuefengz): support non-Mirrored variable as destinations. - g = d.reduce( - variable_scope.VariableAggregation.SUM, g, destinations=v) + g = d.extended.reduce_to( + reduce_util.ReduceOp.SUM, g, destinations=v) with ops.control_dependencies( d.update(v, update, g, grouped=False)): after_list.append(d.read_var(v)) @@ -481,11 +490,12 @@ class ParameterServerStrategyTestBase( before_out, after_out = step() - if context.num_gpus() < d._num_gpus_per_worker: + if context.num_gpus() < d.extended._num_gpus_per_worker: return True if (not task_type or - multi_worker_util.is_chief(d._cluster_spec, task_type, task_id)): + multi_worker_util.is_chief( + d.extended._cluster_spec, task_type, task_id)): variables.global_variables_initializer().run() # Workers waiting for chief worker's initializing variables. @@ -508,8 +518,40 @@ class ParameterServerStrategyTestBase( self.assertLess(error_after, error_before) return error_after < error_before + def _test_input_fn_iterator(self, task_type, task_id, num_gpus, input_fn, + expected_values): + distribution, master_target, config = self._get_test_objects( + task_type, task_id, num_gpus) + devices = distribution.extended.worker_devices + + with ops.Graph().as_default(), \ + self.cached_session(config=config, + target=master_target) as sess: + iterator = distribution.make_input_fn_iterator(input_fn) + sess.run(iterator.initialize()) + + for expected_value in expected_values: + next_element = iterator.get_next() + computed_value = sess.run( + [values.select_device(d, next_element) for d in devices]) + self.assertEqual(expected_value, computed_value) + + with self.assertRaises(errors.OutOfRangeError): + next_element = iterator.get_next() + sess.run([values.select_device(d, next_element) for d in devices]) + + # After re-initializing the iterator, should be able to iterate again. + sess.run(iterator.initialize()) + + for expected_value in expected_values: + next_element = iterator.get_next() + computed_value = sess.run( + [values.select_device(d, next_element) for d in devices]) + self.assertEqual(expected_value, computed_value) + class ParameterServerStrategyTest(ParameterServerStrategyTestBase, + strategy_test_lib.DistributionTestBase, parameterized.TestCase): @classmethod @@ -574,6 +616,73 @@ class ParameterServerStrategyTest(ParameterServerStrategyTestBase, def testMinimizeLossGraphLocal(self, num_gpus): self._test_minimize_loss_graph(None, None, num_gpus) + # TODO(priyag): Refactor this and other multi worker tests. + @combinations.generate( + combinations.combine(mode=['graph'], num_gpus=[1, 2], required_gpus=1)) + def testMakeInputFnIteratorDistributed(self, num_gpus): + if context.num_gpus() < num_gpus: + self.skipTest('Not enough GPUs') + dataset_fn = lambda: dataset_ops.Dataset.range(100) + expected_values = [[i+j for j in range(num_gpus)] + for i in range(0, 100, num_gpus)] + + input_fn = self._input_fn_to_test_input_context( + dataset_fn, + expected_num_replicas_in_sync=num_gpus, + expected_num_input_pipelines=3, + expected_input_pipeline_id=1) # because task_id = 1 + self._test_input_fn_iterator('worker', 1, num_gpus, + input_fn, expected_values) + + @combinations.generate( + combinations.combine(mode=['graph'], num_gpus=[1, 2], required_gpus=1)) + def testMakeInputFnIteratorLocal(self, num_gpus): + if context.num_gpus() < num_gpus: + self.skipTest('Not enough GPUs') + dataset_fn = lambda: dataset_ops.Dataset.range(100) + expected_values = [[i+j for j in range(num_gpus)] + for i in range(0, 100, num_gpus)] + + input_fn = self._input_fn_to_test_input_context( + dataset_fn, + expected_num_replicas_in_sync=num_gpus, + expected_num_input_pipelines=1, + expected_input_pipeline_id=0) # only one worker and pipeline for local. + self._test_input_fn_iterator(None, None, num_gpus, + input_fn, expected_values) + + def testGlobalStepUpdate(self): + strategy = parameter_server_strategy.ParameterServerStrategy( + num_gpus_per_worker=context.num_gpus()) + self._test_global_step_update(strategy) + + def testUpdateConfigProtoMultiWorker(self): + distribution = parameter_server_strategy.ParameterServerStrategy( + num_gpus_per_worker=2) + distribution.configure( + cluster_spec=self._cluster_spec, task_type='worker', task_id=1) + + config_proto = config_pb2.ConfigProto(device_filters=['to_be_overridden']) + + new_config = distribution.update_config_proto(config_proto) + + # Verify device filters. + self.assertEqual(['/job:worker/task:1', '/job:ps'], + new_config.device_filters) + + # Verify isolate_session_state + self.assertFalse(new_config.isolate_session_state) + + def testUpdateConfigProtoLocal(self): + distribution = parameter_server_strategy.ParameterServerStrategy( + num_gpus_per_worker=2) + + config_proto = config_pb2.ConfigProto() + new_config = distribution.update_config_proto(config_proto) + + # Verify isolate_session_state + self.assertTrue(new_config.isolate_session_state) + class ParameterServerStrategyWithChiefTest(ParameterServerStrategyTestBase, parameterized.TestCase): @@ -616,9 +725,9 @@ class ParameterServerStrategyWithChiefTest(ParameterServerStrategyTestBase, v = variable_scope.get_variable('v', initializer=10.0) _ = v * v v, = tape.watched_variables() - w = distribution.value_container(v) + w = distribution.extended.value_container(v) self.assertIs(values.AggregatingVariable, type(w)) - distribution.call_for_each_replica(f) + distribution.extended.call_for_each_replica(f) if __name__ == '__main__': diff --git a/tensorflow/contrib/distribute/python/step_fn.py b/tensorflow/contrib/distribute/python/step_fn.py index 3dc815f037..c928b6d9f1 100644 --- a/tensorflow/contrib/distribute/python/step_fn.py +++ b/tensorflow/contrib/distribute/python/step_fn.py @@ -94,7 +94,7 @@ class StandardSingleLossStep(StandardInputStep): def __call__(self): with self._distribution.scope(): - def step_fn(ctx, *inputs): + def step_fn(ctx, inputs): """Function to run one iteration with one input.""" gradients_fn = backprop.implicit_grad(self._loss_fn) gradients_fn = optimizer_lib.get_filtered_grad_fn(gradients_fn) diff --git a/tensorflow/contrib/distribute/python/strategy_test_lib.py b/tensorflow/contrib/distribute/python/strategy_test_lib.py index 3c0c10430e..d50b142c5e 100644 --- a/tensorflow/contrib/distribute/python/strategy_test_lib.py +++ b/tensorflow/contrib/distribute/python/strategy_test_lib.py @@ -19,16 +19,21 @@ from __future__ import division from __future__ import print_function from tensorflow.core.protobuf import config_pb2 +from tensorflow.python.distribute import distribution_strategy_context as ds_context +from tensorflow.python.distribute import reduce_util +from tensorflow.python.distribute import values from tensorflow.python.eager import backprop from tensorflow.python.eager import context from tensorflow.python.eager import test from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import errors from tensorflow.python.framework import ops from tensorflow.python.layers import core from tensorflow.python.ops import array_ops +from tensorflow.python.ops import init_ops from tensorflow.python.ops import variable_scope from tensorflow.python.ops import variables -from tensorflow.python.training import distribution_strategy_context from tensorflow.python.training import optimizer @@ -45,8 +50,7 @@ def _raise_exception_fn(_=None): # Must be the argument to a distribution.call_for_each_replica() call, calls a # get_replica_context().merge_call() that raises an exception. def _merge_raises_fn(): - distribution_strategy_context.get_replica_context().merge_call( - _raise_exception_fn) + ds_context.get_replica_context().merge_call(_raise_exception_fn) # Must be the argument to a get_replica_context().merge_call() call, calls @@ -59,8 +63,7 @@ def _call_raises_fn(dist): # calls a get_replica_context().merge_call() that calls a # call_for_each_replica() that raises an exception. def _merge_call_raises_fn(): - distribution_strategy_context.get_replica_context().merge_call( - _call_raises_fn) + ds_context.get_replica_context().merge_call(_call_raises_fn) # Must be the argument to a get_replica_context().merge_call() call, calls @@ -74,8 +77,7 @@ def _call_merge_raises_fn(dist): # get_replica_context().merge_call() that calls a call_for_each_replica() that # calls a get_replica_context().merge_call() that raises an exception. def _merge_call_merge_raises_fn(): - distribution_strategy_context.get_replica_context().merge_call( - _call_merge_raises_fn) + ds_context.get_replica_context().merge_call(_call_merge_raises_fn) class DistributionTestBase(test.TestCase): @@ -114,8 +116,8 @@ class DistributionTestBase(test.TestCase): before_list.append(fetched) # control_dependencies irrelevant but harmless in eager execution with ops.control_dependencies([fetched]): - g = d.reduce( - variable_scope.VariableAggregation.SUM, g, destinations=v) + g = d.extended.reduce_to( + reduce_util.ReduceOp.SUM, g, destinations=v) with ops.control_dependencies(d.update( v, update, g, grouped=False)): after_list.append(d.read_var(v)) @@ -169,8 +171,8 @@ class DistributionTestBase(test.TestCase): fetched = d.read_var(v) before_list.append(fetched) with ops.control_dependencies([fetched]): - g = d.reduce( - variable_scope.VariableAggregation.SUM, g, destinations=v) + g = d.extended.reduce_to( + reduce_util.ReduceOp.SUM, g, destinations=v) with ops.control_dependencies(d.update( v, update, g, grouped=False)): after_list.append(d.read_var(v)) @@ -189,31 +191,20 @@ class DistributionTestBase(test.TestCase): # Error should go down self.assertLess(error_after, error_before) - def _test_device_index(self, d): - with d.scope(): - expected_devices = [False] * len(d.worker_devices) - - def mark_devices_fn(device_id): - self.assertLess(device_id, len(d.worker_devices)) - self.assertFalse(expected_devices[device_id]) - expected_devices[device_id] = True - - d.call_for_each_replica(mark_devices_fn, args=(d.worker_device_index,)) - self.assertAllEqual(expected_devices, [True] * len(d.worker_devices)) - def _test_replica_id(self, d): with d.scope(): - expected_devices = [False] * len(d.worker_devices) + expected_devices = [False] * len(d.extended.worker_devices) def mark_devices_fn(): - replica_id = ( - distribution_strategy_context.get_replica_context().replica_id) - self.assertLess(replica_id, len(d.worker_devices)) + replica_id = self.evaluate( + ds_context.get_replica_context().replica_id_in_sync_group) + self.assertLess(replica_id, len(d.extended.worker_devices)) self.assertFalse(expected_devices[replica_id]) expected_devices[replica_id] = True d.call_for_each_replica(mark_devices_fn) - self.assertAllEqual(expected_devices, [True] * len(d.worker_devices)) + self.assertAllEqual(expected_devices, + [True] * len(d.extended.worker_devices)) def _test_call_and_merge_exceptions(self, dist): with dist.scope(): @@ -225,3 +216,78 @@ class DistributionTestBase(test.TestCase): dist.call_for_each_replica(_merge_call_raises_fn) with self.assertRaises(_TestException): dist.call_for_each_replica(_merge_call_merge_raises_fn) + + def _input_fn_to_test_input_context(self, + dataset_fn, + expected_num_replicas_in_sync, + expected_num_input_pipelines, + expected_input_pipeline_id): + # Use a list of one element as counter so that it can be captured by the + # `_input_fn`. This counter is incremented by 1 each time an input_fn is + # called. We use this counter to check whether the `input_pipeline_id` + # matches the counter in the in-graph replication. + worker_id_counter = [0] + + def _input_fn(input_context): + """Input fn for testing.""" + self.assertIsNotNone(input_context) + self.assertEqual(expected_num_replicas_in_sync, + input_context.num_replicas_in_sync) + self.assertEqual(expected_num_input_pipelines, + input_context.num_input_pipelines) + if expected_input_pipeline_id is not None: + self.assertEqual(expected_input_pipeline_id, + input_context.input_pipeline_id) + else: + self.assertEqual(worker_id_counter[0], input_context.input_pipeline_id) + worker_id_counter[0] += 1 + + return dataset_fn() + + return _input_fn + + def _test_input_fn_iterator(self, iterator, devices, expected_values, + sess=None): + evaluate = lambda x: sess.run(x) if sess else self.evaluate(x) + evaluate(iterator.initialize()) + + for expected_value in expected_values: + next_element = iterator.get_next() + computed_value = evaluate( + [values.select_device(d, next_element) for d in devices]) + self.assertEqual(expected_value, computed_value) + + with self.assertRaises(errors.OutOfRangeError): + next_element = iterator.get_next() + evaluate([values.select_device(d, next_element) for d in devices]) + + # After re-initializing the iterator, should be able to iterate again. + evaluate(iterator.initialize()) + + for expected_value in expected_values: + next_element = iterator.get_next() + computed_value = evaluate( + [values.select_device(d, next_element) for d in devices]) + self.assertEqual(expected_value, computed_value) + + def _test_global_step_update(self, strategy): + with strategy.scope(): + global_step = variable_scope.get_variable( + "global_step", + shape=[], + dtype=dtypes.int64, + initializer=init_ops.zeros_initializer(), + trainable=False, + aggregation=variables.VariableAggregation.ONLY_FIRST_REPLICA) + self.evaluate(variables.global_variables_initializer()) + + def model_fn(): + train_op = global_step.assign_add(1) + value = global_step.read_value() + return train_op, value + + train_ops, value = strategy.call_for_each_replica(model_fn) + self.evaluate(strategy.group(train_ops)) + global_step_tensors = strategy.unwrap(value) + global_step_values = self.evaluate(global_step_tensors) + self.assertEqual([1] * len(global_step_tensors), global_step_values) diff --git a/tensorflow/contrib/distribute/python/tpu_strategy.py b/tensorflow/contrib/distribute/python/tpu_strategy.py index f5b4531ba8..39ed8f7cf1 100644 --- a/tensorflow/contrib/distribute/python/tpu_strategy.py +++ b/tensorflow/contrib/distribute/python/tpu_strategy.py @@ -21,25 +21,28 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import copy import functools -from tensorflow.contrib.distribute.python import cross_tower_ops as cross_tower_ops_lib -from tensorflow.contrib.distribute.python import values from tensorflow.contrib.tpu.python.ops import tpu_ops from tensorflow.contrib.tpu.python.tpu import tpu from tensorflow.contrib.tpu.python.tpu import tpu_system_metadata as tpu_system_metadata_lib from tensorflow.contrib.tpu.python.tpu import training_loop +from tensorflow.python.distribute import cross_device_ops as cross_device_ops_lib +from tensorflow.python.distribute import device_util +from tensorflow.python.distribute import distribute_lib +from tensorflow.python.distribute import reduce_util +from tensorflow.python.distribute import values from tensorflow.python.eager import context from tensorflow.python.eager import tape from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import tensor_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import variable_scope as vs -from tensorflow.python.ops import variables as variables_lib -from tensorflow.python.training import device_util -from tensorflow.python.training import distribute as distribute_lib from tensorflow.python.util import nest @@ -130,8 +133,21 @@ class TPUStrategy(distribute_lib.DistributionStrategy): num_cores: Number of cores to use on the TPU. If None specified, then auto-detect the cores and topology of the TPU system. """ - super(TPUStrategy, self).__init__() + super(TPUStrategy, self).__init__(TPUExtended( + self, tpu_cluster_resolver, steps_per_run, num_cores)) + @property + def steps_per_run(self): + """DEPRECATED: use .extended.steps_per_run instead.""" + return self._extended.steps_per_run + + +class TPUExtended(distribute_lib.DistributionStrategyExtended): + """Implementation of TPUStrategy.""" + + def __init__(self, container_strategy, tpu_cluster_resolver, steps_per_run, + num_cores=None): + super(TPUExtended, self).__init__(container_strategy) self._tpu_cluster_resolver = tpu_cluster_resolver self._tpu_metadata = get_tpu_system_metadata(self._tpu_cluster_resolver) # TODO(sourabhbajaj): Change this from num_cores to metadata_override @@ -145,7 +161,7 @@ class TPUStrategy(distribute_lib.DistributionStrategy): self._host_device = self.get_host_cpu_device(0) self._tpu_devices = sorted(device_map.keys()) # Only create variables for the number of replicas we're running. - self._tpu_devices = self._tpu_devices[:self.num_replicas] + self._tpu_devices = self._tpu_devices[:self._num_replicas_in_sync] # TODO(sourabhbajaj): Remove this once performance of running one step # at a time is comparable to multiple steps. @@ -214,7 +230,17 @@ class TPUStrategy(distribute_lib.DistributionStrategy): return enqueue_op_per_host - def distribute_dataset(self, dataset_fn): + def _make_dataset_iterator(self, dataset): + """Make iterators for each of the TPU hosts.""" + + worker_devices = [ + (self.get_host(hid), [self.get_host_cpu_device(hid)]) + for hid in range(self.num_hosts) + ] + return values.DatasetIterator(dataset, worker_devices, + self._num_replicas_in_sync) + + def _distribute_dataset(self, dataset_fn): worker_devices = [ (self.get_host(hid), [self.get_host_cpu_device(hid)]) for hid in range(self.num_hosts) @@ -225,12 +251,11 @@ class TPUStrategy(distribute_lib.DistributionStrategy): # TODO(priyag): Deal with OutOfRange errors once b/111349762 is fixed. # TODO(sourabhbajaj): Remove the initial_loop_values parameter when we have # a mechanism to infer the outputs of `fn`. Pending b/110550782. - def _run_steps_on_dataset(self, fn, multi_worker_iterator, iterations, - initial_loop_values=None): - + def _experimental_run_steps_on_iterator( + self, fn, multi_worker_iterator, iterations, initial_loop_values=None): output_shapes = multi_worker_iterator.output_shapes shapes = nest.flatten(output_shapes) - if any([not s.is_fully_defined() for s in shapes]): + if any(not s.is_fully_defined() for s in shapes): raise ValueError( "TPU currently requires fully defined shapes. Either use " "set_shape() on the input tensors or use " @@ -251,13 +276,13 @@ class TPUStrategy(distribute_lib.DistributionStrategy): initial_loop_values = {} initial_loop_values = nest.flatten(initial_loop_values) ctx = values.MultiStepContext() - def run_fn(*args, **kwargs): + + def run_fn(): """Single step on the TPU device.""" - del args, kwargs fn_inputs = dequeue_fn() if not isinstance(fn_inputs, tuple): fn_inputs = (fn_inputs,) - fn_result = fn(ctx, *fn_inputs) + fn_result = fn(ctx, fn_inputs) flat_last_step_outputs = nest.flatten(ctx.last_step_outputs) if flat_last_step_outputs: with ops.control_dependencies([fn_result]): @@ -265,11 +290,6 @@ class TPUStrategy(distribute_lib.DistributionStrategy): else: return fn_result - # TODO(sourabhbajaj): The input to while loop should be based on the output - # type of the step_fn - def iterate_on_tpu(): - return training_loop.repeat(iterations, run_fn, initial_loop_values) - # We capture the control_flow_context at this point, before we run `fn` # inside a while_loop and TPU replicate context. This is useful in cases # where we might need to exit these contexts and get back to the outer @@ -279,38 +299,70 @@ class TPUStrategy(distribute_lib.DistributionStrategy): self._outer_control_flow_context = ( ops.get_default_graph()._get_control_flow_context()) # pylint: disable=protected-access - replicate_inputs = [[]] * self.num_replicas - replicate_outputs = tpu.replicate(iterate_on_tpu, replicate_inputs) + def rewrite_fn(*args): + """The rewritten step fn running on TPU.""" + del args + replicate_inputs = [[]] * self._num_replicas_in_sync + replicate_outputs = tpu.replicate(run_fn, replicate_inputs) + + # If run_fn has tensor outputs, tpu.replicate returns a list of list. We + # will flatten it in this case. If run_fn has no tensor outputs, + # tpu.replicate returns a list of no_ops, we will keep the output as it + # is. + if isinstance(replicate_outputs[0], list): + replicate_outputs = nest.flatten(replicate_outputs) + + return replicate_outputs + + # TODO(sourabhbajaj): The input to while loop should be based on the output + # type of the step_fn + assert isinstance(initial_loop_values, list) + initial_loop_values = initial_loop_values * self._num_replicas_in_sync + + # Put the while loop op on host 0. + with ops.device(self.get_host_cpu_device(0)): + replicate_outputs = training_loop.repeat(iterations, rewrite_fn, + initial_loop_values) + del self._outer_control_flow_context ctx.run_op = control_flow_ops.group(replicate_outputs, enqueue_ops) - # Filter out any ops from the outputs, typically this would be the case - # when there were no tensor outputs. - last_step_tensor_outputs = [x for x in replicate_outputs - if not isinstance(x, ops.Operation)] - - # Outputs are currently of the structure (grouped by device) - # [[output0_device0, output1_device0, output2_device0], - # [output0_device1, output1_device1, output2_device1]] - # Convert this to the following structure instead: (grouped by output) - # [[output0_device0, output0_device1], - # [output1_device0, output1_device1], - # [output2_device0, output2_device1]] - last_step_tensor_outputs = [list(x) for x in zip(*last_step_tensor_outputs)] + if isinstance(replicate_outputs, list): + # Filter out any ops from the outputs, typically this would be the case + # when there were no tensor outputs. + last_step_tensor_outputs = [ + x for x in replicate_outputs if not isinstance(x, ops.Operation) + ] + + # Outputs are currently of the structure (flattened) + # [output0_device0, output1_device0, output2_device0, + # output0_device1, output1_device1, output2_device1, + # ...] + # Convert this to the following structure instead: (grouped by output) + # [[output0_device0, output0_device1], + # [output1_device0, output1_device1], + # [output2_device0, output2_device1]] + output_num = len(last_step_tensor_outputs) // self._num_replicas_in_sync + last_step_tensor_outputs = [ + last_step_tensor_outputs[i::output_num] for i in range(output_num) + ] + else: + # no tensors returned. + last_step_tensor_outputs = [] # Convert replicate_outputs to the original dict structure of # last_step_outputs. last_step_tensor_outputs_dict = nest.pack_sequence_as( ctx.last_step_outputs, last_step_tensor_outputs) - for (name, aggregation) in ctx._last_step_outputs_aggregations.items(): # pylint: disable=protected-access + for name, reduce_op in ctx._last_step_outputs_reduce_ops.items(): # pylint: disable=protected-access output = last_step_tensor_outputs_dict[name] - # For outputs that have already been aggregated, take the first value + # For outputs that have already been reduced, take the first value # from the list as each value should be the same. Else return the full # list of values. - # TODO(josh11b): If aggregation is NONE, we should return a PerReplica + # TODO(josh11b): If reduce_op is NONE, we should return a PerReplica # value. - if aggregation is not variables_lib.VariableAggregation.NONE: + if reduce_op is not None: # TODO(priyag): Should this return the element or a list with 1 element last_step_tensor_outputs_dict[name] = output[0] ctx._set_last_step_outputs(last_step_tensor_outputs_dict) # pylint: disable=protected-access @@ -320,10 +372,10 @@ class TPUStrategy(distribute_lib.DistributionStrategy): def _call_for_each_replica(self, fn, args, kwargs): # TODO(jhseu): Consider making it so call_for_each_replica implies that # we're in a tpu.rewrite(), and update TPUMirroredVariable accordingly. - with _TPUReplicaContext(self): + with _TPUReplicaContext(self._container_strategy()): return fn(*args, **kwargs) - def initialize(self): + def _initialize(self): if context.executing_eagerly(): # TODO(priyag): Add appopriate call here when eager is supported for TPUs. raise NotImplementedError("Eager mode not supported in TPUStrategy.") @@ -338,7 +390,7 @@ class TPUStrategy(distribute_lib.DistributionStrategy): tpu.initialize_system()) return graph.get_collection(_TPU_INITIALIZE_SYSTEM_COLLECTION) - def finalize(self): + def _finalize(self): if context.executing_eagerly(): # TODO(priyag): Add appopriate call here when eager is supported for TPUs. raise NotImplementedError("Eager mode not supported in TPUStrategy.") @@ -346,7 +398,7 @@ class TPUStrategy(distribute_lib.DistributionStrategy): return [tpu.shutdown_system()] def _get_devices_from(self, colocate_with=None): - # TODO(jhseu): Change this when we support model parallelism. + # TODO(jhseu): Change this when we support model parallelism. return self._tpu_devices def _create_variable(self, next_creator, *args, **kwargs): @@ -383,12 +435,12 @@ class TPUStrategy(distribute_lib.DistributionStrategy): return _create_tpu_mirrored_variable(devices, _real_mirrored_creator, *args, **kwargs) - def _reduce(self, aggregation, value, destinations): + def _reduce_to(self, reduce_op, value, destinations): if values._enclosing_tpu_context() is not None: # pylint: disable=protected-access - if aggregation == vs.VariableAggregation.MEAN: + if reduce_op == reduce_util.ReduceOp.MEAN: # TODO(jhseu): Revisit once we support model-parallelism. - value *= (1. / self.num_replicas) - elif aggregation != vs.VariableAggregation.SUM: + value *= (1. / self._num_replicas_in_sync) + elif reduce_op != reduce_util.ReduceOp.SUM: raise NotImplementedError( "Currently only support sum & mean in TPUStrategy.") return tpu_ops.cross_replica_sum(value) @@ -396,27 +448,22 @@ class TPUStrategy(distribute_lib.DistributionStrategy): # Validate that the destination is same as the host device # Note we don't do this when in replicate context as the reduction is # performed on the TPU device itself. - devices = cross_tower_ops_lib.get_devices_from(destinations) + devices = cross_device_ops_lib.get_devices_from(destinations) if len(devices) == 1: assert device_util.canonicalize(devices[0]) == device_util.canonicalize( self._host_device) else: raise ValueError("Multiple devices are not supported for TPUStrategy") - if aggregation == vs.VariableAggregation.ONLY_FIRST_REPLICA: - return value[0] output = math_ops.add_n(value) - if aggregation == vs.VariableAggregation.MEAN: + if reduce_op == reduce_util.ReduceOp.MEAN: return output * (1. / len(value)) return output - def _update(self, var, options, fn, *args, **kwargs): + def _update(self, var, fn, args, kwargs, group): assert isinstance(var, values.TPUMirroredVariable) - should_group = options.pop("grouped") - assert not options # Validate that we are processing all of the options. - if values._enclosing_tpu_context() is not None: # pylint: disable=protected-access - if should_group: + if group: return fn(var, *args, **kwargs) else: return [fn(var, *args, **kwargs)] @@ -431,9 +478,7 @@ class TPUStrategy(distribute_lib.DistributionStrategy): updates[d] = fn(v, *values.select_device_mirrored(d, args), **values.select_device_mirrored(d, kwargs)) - return values.update_regroup(self, updates, should_group) - - # TODO(josh11b): Need to implement _update_non_slot()! + return values.update_regroup(self, updates, group) def read_var(self, var): assert isinstance(var, values.TPUMirroredVariable) @@ -453,14 +498,10 @@ class TPUStrategy(distribute_lib.DistributionStrategy): def value_container(self, value): return value - def _broadcast(self, tensor, destinations): + def _broadcast_to(self, tensor, destinations): del destinations return tensor - @property - def num_replicas(self): - return self._num_cores_override or self._tpu_metadata.num_cores - @property def num_hosts(self): return self._tpu_metadata.num_hosts @@ -470,15 +511,15 @@ class TPUStrategy(distribute_lib.DistributionStrategy): return self._tpu_metadata.num_of_cores_per_host @property - def num_replicas_in_sync(self): - return self.num_replicas + def _num_replicas_in_sync(self): + return self._num_cores_override or self._tpu_metadata.num_cores @property - def between_graph(self): + def experimental_between_graph(self): return False @property - def should_init(self): + def experimental_should_init(self): return True @property @@ -500,14 +541,12 @@ class TPUStrategy(distribute_lib.DistributionStrategy): def non_slot_devices(self, var_list): return self._host_device - def _update_non_slot(self, colocate_with, options, fn, *args, **kwargs): + def _update_non_slot(self, colocate_with, fn, args, kwargs, group): del colocate_with - should_group = options.pop("grouped") - assert not options # Validate that we are processing all of the options. with ops.device(self._host_device), distribute_lib.UpdateContext( self._host_device): result = fn(*args, **kwargs) - if should_group: + if group: return result else: return nest.map_structure(self._unwrap, result) @@ -521,17 +560,27 @@ class TPUStrategy(distribute_lib.DistributionStrategy): def get_host_cpu_device(self, host_id): return self.get_host(host_id) + "/device:CPU:0" - def configure(self, - session_config=None, - cluster_spec=None, - task_type=None, - task_id=None): + def _configure(self, + session_config=None, + cluster_spec=None, + task_type=None, + task_id=None): del cluster_spec, task_type, task_id if session_config: - session_config.isolate_session_state = True - cluster_spec = self._tpu_cluster_resolver.cluster_spec() - if cluster_spec: - session_config.cluster_def.CopyFrom(cluster_spec.as_cluster_def()) + session_config.CopyFrom(self._update_config_proto(session_config)) + + def _update_config_proto(self, config_proto): + updated_config = copy.deepcopy(config_proto) + updated_config.isolate_session_state = True + cluster_spec = self._tpu_cluster_resolver.cluster_spec() + if cluster_spec: + updated_config.cluster_def.CopyFrom(cluster_spec.as_cluster_def()) + return updated_config + + # TODO(priyag): Delete this once all strategies use global batch size. + @property + def _global_batch_size(self): + return True class _TPUReplicaContext(distribute_lib.ReplicaContext): @@ -540,13 +589,14 @@ class _TPUReplicaContext(distribute_lib.ReplicaContext): # TODO(sourabhbajaj): Call for each tower should be updating this. def __init__(self, distribution_strategy): distribute_lib.ReplicaContext.__init__( - self, distribution_strategy, replica_id=0) - - @property - def device(self): - raise RuntimeError("Use .devices instead") + self, + distribution_strategy, + # TODO(b/118385803): properly initialize replica_id, instead of always 0 + replica_id_in_sync_group=constant_op.constant(0, dtypes.int32)) @property def devices(self): distribute_lib.require_replica_context(self) - return [self._distribution_strategy.worker_devices[self._replica_id]] + ds = self._distribution_strategy + replica_id = tensor_util.constant_value(self._replica_id_in_sync_group) + return [ds.extended.worker_devices[replica_id]] diff --git a/tensorflow/contrib/distribute/python/values_test.py b/tensorflow/contrib/distribute/python/values_test.py index 268393ee80..538b859f3d 100644 --- a/tensorflow/contrib/distribute/python/values_test.py +++ b/tensorflow/contrib/distribute/python/values_test.py @@ -19,12 +19,15 @@ from __future__ import division from __future__ import print_function import os +from absl.testing import parameterized -from tensorflow.contrib.distribute.python import mirrored_strategy +from tensorflow.contrib.distribute.python import combinations from tensorflow.contrib.distribute.python import multi_worker_test_base -from tensorflow.contrib.distribute.python import values from tensorflow.core.protobuf import config_pb2 from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.distribute import device_util +from tensorflow.python.distribute import distribute_lib +from tensorflow.python.distribute import values from tensorflow.python.eager import context from tensorflow.python.eager import test from tensorflow.python.estimator import model_fn as model_fn_lib @@ -34,10 +37,10 @@ from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_util from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops +from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import random_ops from tensorflow.python.ops import variable_scope from tensorflow.python.ops import variables as variables_lib -from tensorflow.python.training import device_util from tensorflow.python.training import saver as saver_lib from tensorflow.python.util import nest @@ -324,20 +327,20 @@ class RegroupAndSelectDeviceTest(test.TestCase): self.assertTrue( isinstance(merged_estimator_spec, model_fn_lib.EstimatorSpec)) - self.assertEquals(model_fn_lib.ModeKeys.TRAIN, merged_estimator_spec.mode) + self.assertEqual(model_fn_lib.ModeKeys.TRAIN, merged_estimator_spec.mode) for device_id in range(3): d = _device_str(device_id) - self.assertEquals(created_estimator_specs[device_id].loss, - merged_estimator_spec.loss.get(d)) - self.assertEquals(created_estimator_specs[device_id].train_op, - merged_estimator_spec.train_op.get(d)) + self.assertEqual(created_estimator_specs[device_id].loss, + merged_estimator_spec.loss.get(d)) + self.assertEqual(created_estimator_specs[device_id].train_op, + merged_estimator_spec.train_op.get(d)) # Scaffold is populated by `EstimatorSpec.__new__`. - self.assertEquals(created_estimator_specs[device_id].scaffold, - merged_estimator_spec.scaffold.get(d)) + self.assertEqual(created_estimator_specs[device_id].scaffold, + merged_estimator_spec.scaffold.get(d)) # Also test that we can undo the merge using select_device() - self.assertEquals(created_estimator_specs[device_id], - values.select_device(_device_str(device_id), - merged_estimator_spec)) + self.assertEqual(created_estimator_specs[device_id], + values.select_device(_device_str(device_id), + merged_estimator_spec)) class PerReplicaDatasetTest(test.TestCase): @@ -568,7 +571,184 @@ class MultiWorkerDatasetTest(multi_worker_test_base.MultiWorkerTestBase): multi_worker_iterator.get_next() -class MirroredVariableTest(test.TestCase): +class InputIteratorTestBase(test.TestCase): + + def _test_iterator(self, input_type, dataset_fn, worker_device_pairs, + expected_values, sess=None, split_batch_by=None): + devices = nest.flatten([ds for _, ds in worker_device_pairs]) + + if input_type == "input_fn": + input_contexts = [ + distribute_lib.InputContext() for _ in worker_device_pairs] + input_fn = lambda _: dataset_fn() + iterator = values.InputFunctionIterator(input_fn, worker_device_pairs, + input_contexts) + else: + iterator = values.DatasetIterator(dataset_fn(), worker_device_pairs, + split_batch_by) + + evaluate = lambda x: sess.run(x) if sess else self.evaluate(x) + + evaluate(control_flow_ops.group(iterator.initialize())) + + for expected_value in expected_values: + next_element = iterator.get_next() + computed_value = evaluate( + [values.select_device(d, next_element) for d in devices]) + self.assertAllEqual(expected_value, computed_value) + + with self.assertRaises(errors.OutOfRangeError): + next_element = iterator.get_next() + evaluate([values.select_device(d, next_element) for d in devices]) + + # After re-initializing the iterator, should be able to iterate again. + evaluate(control_flow_ops.group(iterator.initialize())) + + for expected_value in expected_values: + next_element = iterator.get_next() + computed_value = evaluate( + [values.select_device(d, next_element) for d in devices]) + self.assertAllEqual(expected_value, computed_value) + + +class InputIteratorSingleWorkerTest(InputIteratorTestBase, + parameterized.TestCase): + + @combinations.generate(combinations.combine( + mode=["graph", "eager"], + input_type=["input_fn", "dataset"])) + def testOneDeviceCPU(self, input_type): + worker_device_pairs = [("", ["/device:CPU:0"])] + dataset_fn = lambda: dataset_ops.Dataset.range(10) + + expected_values = [[i] for i in range(10)] + + self._test_iterator(input_type, dataset_fn, worker_device_pairs, + expected_values) + + @combinations.generate(combinations.combine( + mode=["graph", "eager"], + input_type=["input_fn", "dataset"], + required_gpus=1)) + def testTwoDevicesOneGPUOneCPU(self, input_type): + worker_device_pairs = [("", ["/device:GPU:0", "/device:CPU:0"])] + dataset_fn = lambda: dataset_ops.Dataset.range(10) + + expected_values = [[i, i+1] for i in range(0, 10, 2)] + + self._test_iterator(input_type, dataset_fn, worker_device_pairs, + expected_values) + + @combinations.generate(combinations.combine( + mode=["graph", "eager"], + input_type=["input_fn", "dataset"], + required_gpus=1)) + def testTupleDataset(self, input_type): + worker_device_pairs = [("", ["/device:GPU:0", "/device:CPU:0"])] + def dataset_fn(): + dataset1 = dataset_ops.Dataset.range(10) + dataset2 = dataset_ops.Dataset.range(10).map(lambda x: x**2) + return dataset_ops.Dataset.zip((dataset1, dataset2)) + + expected_values = [[(i, i**2), (i+1, (i+1)**2)] for i in range(0, 10, 2)] + + self._test_iterator(input_type, dataset_fn, worker_device_pairs, + expected_values) + + @combinations.generate(combinations.combine( + mode=["graph", "eager"], + input_type=["input_fn", "dataset"], + required_gpus=1)) + def testUnevenDatasetBatches(self, input_type): + worker_device_pairs = [("", ["/device:GPU:0", "/device:CPU:0"])] + dataset_fn = lambda: dataset_ops.Dataset.range(11) + + expected_values = [[i, i+1] for i in range(0, 10, 2)] + self._test_iterator(input_type, dataset_fn, worker_device_pairs, + expected_values) + + @combinations.generate(combinations.combine( + mode=["graph", "eager"], + input_type=["dataset"], + split_batch_by=[None, 2], + required_gpus=1)) + def testBatchSplitting(self, input_type, split_batch_by): + worker_device_pairs = [("", ["/device:GPU:0", "/device:CPU:0"])] + batch_size = 10 + dataset_fn = lambda: dataset_ops.Dataset.range(100).batch(batch_size) + + updated_batch_size = ( + batch_size // split_batch_by if split_batch_by else batch_size) + expected_values = [[range(i, i+updated_batch_size), + range(i+updated_batch_size, i+2*updated_batch_size)] + for i in range(0, 100, updated_batch_size*2)] + + self._test_iterator(input_type, dataset_fn, worker_device_pairs, + expected_values, sess=None, + split_batch_by=split_batch_by) + + +class InputIteratorMultiWorkerTest( + multi_worker_test_base.MultiWorkerTestBase, InputIteratorTestBase, + parameterized.TestCase): + + def _cpu_devices(self): + return [ + ("/job:worker/replica:0/task:0", + ["/job:worker/replica:0/task:0/device:CPU:0"]), + ("/job:worker/replica:0/task:1", + ["/job:worker/replica:0/task:1/device:CPU:0"])] + + def _cpu_and_one_gpu_devices(self): + return [ + ("/job:worker/replica:0/task:0", [ + "/job:worker/replica:0/task:0/device:GPU:0", + "/job:worker/replica:0/task:0/device:CPU:0" + ]), + ("/job:worker/replica:0/task:1", [ + "/job:worker/replica:0/task:1/device:GPU:0", + "/job:worker/replica:0/task:1/device:CPU:0" + ]) + ] + + @combinations.generate(combinations.combine( + mode=["graph"], + input_type=["input_fn", "dataset"])) + def testOneDevicePerWorker(self, input_type): + worker_devices = self._cpu_devices() + with context.graph_mode(), self.cached_session() as sess: + dataset_fn = lambda: dataset_ops.Dataset.range(4) + self._test_iterator(input_type, dataset_fn, worker_devices, + [[0, 0], [1, 1], [2, 2], [3, 3]], sess) + + @combinations.generate(combinations.combine( + mode=["graph"], + input_type=["input_fn", "dataset"], + required_gpus=1)) + def testTwoDevicesPerWorker(self, input_type): + worker_devices = self._cpu_and_one_gpu_devices() + with context.graph_mode(), self.cached_session() as sess: + dataset_fn = lambda: dataset_ops.Dataset.range(4) + self._test_iterator(input_type, dataset_fn, worker_devices, + [[0, 1, 0, 1], [2, 3, 2, 3]], sess) + + @combinations.generate(combinations.combine( + mode=["graph"], + input_type=["input_fn", "dataset"])) + def testTupleDataset(self, input_type): + worker_devices = self._cpu_devices() + with context.graph_mode(), self.cached_session() as sess: + def dataset_fn(): + dataset1 = dataset_ops.Dataset.range(4) + dataset2 = dataset_ops.Dataset.range(4).map(lambda x: x**2) + return dataset_ops.Dataset.zip((dataset1, dataset2)) + + expected_values = [[(i, i**2), (i, i**2)] for i in range(0, 4)] + self._test_iterator(input_type, dataset_fn, worker_devices, + expected_values, sess) + + +class MirroredVariableTest(test.TestCase, parameterized.TestCase): config = config_pb2.ConfigProto() config.allow_soft_placement = True @@ -580,9 +760,9 @@ class MirroredVariableTest(test.TestCase): v, _, mirrored = _make_mirrored() - self.assertEquals(v[0].name, mirrored.name) - self.assertEquals(v[0].dtype, mirrored.dtype) - self.assertEquals(v[0].shape, mirrored.shape) + self.assertEqual(v[0].name, mirrored.name) + self.assertEqual(v[0].dtype, mirrored.dtype) + self.assertEqual(v[0].shape, mirrored.shape) @test_util.run_in_graph_and_eager_modes(config=config) def testVariableOnAnotherDevice(self): @@ -592,9 +772,9 @@ class MirroredVariableTest(test.TestCase): mirrored = values.MirroredVariable(index, v, variable_scope.VariableAggregation.MEAN) - self.assertEquals(v.name, mirrored.name) - self.assertEquals(v.dtype, mirrored.dtype) - self.assertEquals(v.shape, mirrored.shape) + self.assertEqual(v.name, mirrored.name) + self.assertEqual(v.dtype, mirrored.dtype) + self.assertEqual(v.shape, mirrored.shape) def _assign_mirrored(self, devices, v, new): for d, var, n in zip(devices, v, new): @@ -714,14 +894,13 @@ class MirroredVariableTest(test.TestCase): save_path = self._save_normal() self._restore_mirrored(save_path) - @test_util.run_in_graph_and_eager_modes(config=config) - def testFetchAMirroredVariable(self): - if context.num_gpus() < 1 or context.executing_eagerly(): - self.skipTest("A GPU is not available for this test or it's eager mode.") - - with self.session( - graph=ops.Graph()) as sess, mirrored_strategy.MirroredStrategy( - ["/device:GPU:0"]).scope(): + @combinations.generate(combinations.combine( + distribution=[ + combinations.mirrored_strategy_with_one_gpu, + combinations.core_mirrored_strategy_with_one_gpu], + mode=["graph"])) + def testFetchAMirroredVariable(self, distribution): + with self.session(graph=ops.Graph()) as sess, distribution.scope(): with ops.device("/device:GPU:0"): v = variable_scope.get_variable( name="v", initializer=1., use_resource=True) @@ -747,7 +926,7 @@ def _make_replica_local(method): return v, replica_local -class ReplicaLocalVariableTest(test.TestCase): +class ReplicaLocalVariablePropertiesTest(test.TestCase): config = config_pb2.ConfigProto() config.allow_soft_placement = True @@ -756,15 +935,14 @@ class ReplicaLocalVariableTest(test.TestCase): def testProperties(self): if context.num_gpus() < 1 and context.executing_eagerly(): self.skipTest("A GPU is not available for this test in eager mode.") - v, replica_local = _make_replica_local( variable_scope.VariableAggregation.SUM) - self.assertEquals(v[0].name, replica_local.name) - self.assertEquals(v[0].dtype, replica_local.dtype) - self.assertEquals(v[0].shape, replica_local.shape) - self.assertEquals(variable_scope.VariableAggregation.SUM, - replica_local.aggregation) + self.assertEqual(v[0].name, replica_local.name) + self.assertEqual(v[0].dtype, replica_local.dtype) + self.assertEqual(v[0].shape, replica_local.shape) + self.assertEqual(variable_scope.VariableAggregation.SUM, + replica_local.aggregation) @test_util.run_in_graph_and_eager_modes(config=config) def testVariableOnAnotherDevice(self): @@ -774,11 +952,32 @@ class ReplicaLocalVariableTest(test.TestCase): replica_local = values.ReplicaLocalVariable( index, v, variable_scope.VariableAggregation.MEAN) - self.assertEquals(v.name, replica_local.name) - self.assertEquals(v.dtype, replica_local.dtype) - self.assertEquals(v.shape, replica_local.shape) - self.assertEquals(variable_scope.VariableAggregation.MEAN, - replica_local.aggregation) + self.assertEqual(v.name, replica_local.name) + self.assertEqual(v.dtype, replica_local.dtype) + self.assertEqual(v.shape, replica_local.shape) + self.assertEqual(variable_scope.VariableAggregation.MEAN, + replica_local.aggregation) + + def testTensorConversion(self): + with context.graph_mode(): + _, replica_local = _make_replica_local( + variable_scope.VariableAggregation.SUM) + converted = ops.internal_convert_to_tensor(replica_local, as_ref=False) + self.assertIsInstance(converted, ops.Tensor) + self.assertEqual(converted.dtype, replica_local.dtype) + + converted = ops.internal_convert_to_tensor(replica_local, as_ref=True) + # Resources variable are converted to tensors as well when as_ref is True. + self.assertIsInstance(converted, ops.Tensor) + self.assertEqual(converted.dtype, replica_local.dtype) + + +@combinations.generate(combinations.combine( + distribution=[ + combinations.mirrored_strategy_with_gpu_and_cpu, + combinations.core_mirrored_strategy_with_gpu_and_cpu], + mode=["graph", "eager"])) +class ReplicaLocalVariableTest(test.TestCase, parameterized.TestCase): def _assign_replica_local(self, devices, v, new): for d, var, n in zip(devices, v, new): @@ -795,22 +994,15 @@ class ReplicaLocalVariableTest(test.TestCase): save_path, _ = self._save_return_saver(sess, var) return save_path - def _dist_scope(self): - return mirrored_strategy.MirroredStrategy(_devices).scope() - - @test_util.run_in_graph_and_eager_modes(config=config) - def testSaveAndRestoreReplicaLocalSumOneGraph(self): - if context.num_gpus() < 1 and context.executing_eagerly(): - self.skipTest("A GPU is not available for this test in eager mode.") - - with self.cached_session(config=self.config) as sess: + def testSaveAndRestoreReplicaLocalSumOneGraph(self, distribution): + with self.cached_session() as sess: v, replica_local = _make_replica_local( variable_scope.VariableAggregation.SUM) # Overwrite the initial values. self._assign_replica_local(_devices, v, [3., 4.]) - with self._dist_scope(): + with distribution.scope(): # Saves the current value of v[0] + v[1], 7. save_path, saver = self._save_return_saver(sess, replica_local) @@ -822,19 +1014,18 @@ class ReplicaLocalVariableTest(test.TestCase): saver.restore(sess, save_path) self.assertEqual([3.5, 3.5], self.evaluate([v[0], v[1]])) - @test_util.run_in_graph_and_eager_modes(config=config) - def testSaveAndRestoreReplicaLocalMeanOneGraph(self): + def testSaveAndRestoreReplicaLocalMeanOneGraph(self, distribution): if context.num_gpus() < 1 and context.executing_eagerly(): self.skipTest("A GPU is not available for this test in eager mode.") - with self.cached_session(config=self.config) as sess: + with self.cached_session() as sess: v, replica_local = _make_replica_local( variable_scope.VariableAggregation.MEAN) # Overwrite the initial values. self._assign_replica_local(_devices, v, [3., 4.]) - with self._dist_scope(): + with distribution.scope(): # Saves the current value of (v[0] + v[1])/2, 3.5. save_path, saver = self._save_return_saver(sess, replica_local) @@ -845,7 +1036,7 @@ class ReplicaLocalVariableTest(test.TestCase): saver.restore(sess, save_path) self.assertEqual([3.5, 3.5], self.evaluate([v[0], v[1]])) - def _save_replica_local_mean(self): + def _save_replica_local_mean(self, distribution): """Save variables with mirroring, returns save_path.""" with self.session(graph=ops.Graph()) as sess: v, replica_local = _make_replica_local( @@ -854,7 +1045,7 @@ class ReplicaLocalVariableTest(test.TestCase): # Overwrite the initial values. self._assign_replica_local(_devices, v, [3., 4.]) - with self._dist_scope(): + with distribution.scope(): # Saves the current value of (v[0] + v[1])/2, 3.5 save_path = self._save(sess, replica_local) @@ -862,7 +1053,7 @@ class ReplicaLocalVariableTest(test.TestCase): self._assign_replica_local(_devices, v, [5., 6.]) return save_path - def _save_replica_local_sum(self): + def _save_replica_local_sum(self, distribution): """Save variables with mirroring, returns save_path.""" with self.session(graph=ops.Graph()) as sess: v, replica_local = _make_replica_local("sum") @@ -870,7 +1061,7 @@ class ReplicaLocalVariableTest(test.TestCase): # Overwrite the initial values. self._assign_replica_local(_devices, v, [1.5, 2.]) - with self._dist_scope(): + with distribution.scope(): # Saves the current value of v[0] + v[1], 3.5 save_path = self._save(sess, replica_local) @@ -908,7 +1099,7 @@ class ReplicaLocalVariableTest(test.TestCase): saver.restore(sess, save_path) self.assertEqual(3.5, self.evaluate(var)) - def _restore_replica_local_mean(self, save_path): + def _restore_replica_local_mean(self, save_path, distribution): """Restore to variables with mirroring in a fresh graph.""" with self.session(graph=ops.Graph()) as sess: v, replica_local = _make_replica_local( @@ -917,13 +1108,13 @@ class ReplicaLocalVariableTest(test.TestCase): # Overwrite the initial values. self._assign_replica_local(_devices, v, [7., 8.]) - with self._dist_scope(): + with distribution.scope(): # Restores the saved value of 3.5 to both variables. saver = saver_lib.Saver(var_list=[replica_local]) saver.restore(sess, save_path) self.assertEqual([3.5, 3.5], self.evaluate([v[0], v[1]])) - def _restore_replica_local_sum(self, save_path): + def _restore_replica_local_sum(self, save_path, distribution): """Restore to variables with mirroring in a fresh graph.""" with self.session(graph=ops.Graph()) as sess: v, replica_local = _make_replica_local( @@ -932,72 +1123,35 @@ class ReplicaLocalVariableTest(test.TestCase): # Overwrite the initial values. self._assign_replica_local(_devices, v, [7., 8.]) - with self._dist_scope(): + with distribution.scope(): # Restores the saved value of 3.5 to both variables. saver = saver_lib.Saver(var_list=[replica_local]) saver.restore(sess, save_path) self.assertEqual([1.75, 1.75], self.evaluate([v[0], v[1]])) - @test_util.run_in_graph_and_eager_modes(config=config) - def testSaveReplicaLocalRestoreReplicaLocalMean(self): - if context.num_gpus() < 1 and context.executing_eagerly(): - self.skipTest("A GPU is not available for this test in eager mode.") + def testSaveReplicaLocalRestoreReplicaLocalMean(self, distribution): + save_path = self._save_replica_local_mean(distribution) + self._restore_replica_local_mean(save_path, distribution) - save_path = self._save_replica_local_mean() - self._restore_replica_local_mean(save_path) + def testSaveReplicaLocalRestoreReplicaLocalSum(self, distribution): + save_path = self._save_replica_local_sum(distribution) + self._restore_replica_local_sum(save_path, distribution) - @test_util.run_in_graph_and_eager_modes(config=config) - def testSaveReplicaLocalRestoreReplicaLocalSum(self): - if context.num_gpus() < 1 and context.executing_eagerly(): - self.skipTest("A GPU is not available for this test in eager mode.") - - save_path = self._save_replica_local_sum() - self._restore_replica_local_sum(save_path) - - @test_util.run_in_graph_and_eager_modes(config=config) - def testSaveReplicaLocalMeanRestoreNormal(self): - if context.num_gpus() < 1 and context.executing_eagerly(): - self.skipTest("A GPU is not available for this test in eager mode.") - - save_path = self._save_replica_local_mean() + def testSaveReplicaLocalMeanRestoreNormal(self, distribution): + save_path = self._save_replica_local_mean(distribution) self._restore_normal(save_path) - @test_util.run_in_graph_and_eager_modes(config=config) - def testSaveReplicaLocalSumRestoreNormal(self): - if context.num_gpus() < 1 and context.executing_eagerly(): - self.skipTest("A GPU is not available for this test in eager mode.") - - save_path = self._save_replica_local_sum() + def testSaveReplicaLocalSumRestoreNormal(self, distribution): + save_path = self._save_replica_local_sum(distribution) self._restore_normal(save_path) - @test_util.run_in_graph_and_eager_modes(config=config) - def testSaveNormalRestoreReplicaLocalMean(self): - if context.num_gpus() < 1 and context.executing_eagerly(): - self.skipTest("A GPU is not available for this test in eager mode.") - + def testSaveNormalRestoreReplicaLocalMean(self, distribution): save_path = self._save_normal() - self._restore_replica_local_mean(save_path) - - @test_util.run_in_graph_and_eager_modes(config=config) - def testSaveNormalRestoreReplicaLocalSum(self): - if context.num_gpus() < 1 and context.executing_eagerly(): - self.skipTest("A GPU is not available for this test in eager mode.") + self._restore_replica_local_mean(save_path, distribution) + def testSaveNormalRestoreReplicaLocalSum(self, distribution): save_path = self._save_normal() - self._restore_replica_local_sum(save_path) - - def testTensorConversion(self): - with context.graph_mode(): - _, replica_local = _make_replica_local( - variable_scope.VariableAggregation.SUM) - converted = ops.internal_convert_to_tensor(replica_local, as_ref=False) - self.assertIsInstance(converted, ops.Tensor) - self.assertEqual(converted.dtype, replica_local.dtype) - - converted = ops.internal_convert_to_tensor(replica_local, as_ref=True) - # Resources variable are converted to tensors as well when as_ref is True. - self.assertIsInstance(converted, ops.Tensor) - self.assertEqual(converted.dtype, replica_local.dtype) + self._restore_replica_local_sum(save_path, distribution) if __name__ == "__main__": diff --git a/tensorflow/contrib/distribute/python/warm_starting_util_test.py b/tensorflow/contrib/distribute/python/warm_starting_util_test.py index 5d57d144c1..b0bcf9b174 100644 --- a/tensorflow/contrib/distribute/python/warm_starting_util_test.py +++ b/tensorflow/contrib/distribute/python/warm_starting_util_test.py @@ -44,7 +44,9 @@ class WarmStartingUtilWithDistributionStrategyTest( distribution=[combinations.default_strategy, combinations.one_device_strategy, combinations.mirrored_strategy_with_gpu_and_cpu, - combinations.mirrored_strategy_with_two_gpus], + combinations.mirrored_strategy_with_two_gpus, + combinations.core_mirrored_strategy_with_gpu_and_cpu, + combinations.core_mirrored_strategy_with_two_gpus], save_with_distribution=[True, False], restore_with_distribution=[True, False], mode=["graph"])) diff --git a/tensorflow/contrib/distributions/BUILD b/tensorflow/contrib/distributions/BUILD index 60f6b90edc..3079175015 100644 --- a/tensorflow/contrib/distributions/BUILD +++ b/tensorflow/contrib/distributions/BUILD @@ -72,7 +72,6 @@ py_library( "//tensorflow/python:nn", "//tensorflow/python:nn_ops", "//tensorflow/python:random_ops", - "//tensorflow/python:spectral_ops", "//tensorflow/python:state_ops", "//tensorflow/python:tensor_util", "//tensorflow/python:util", @@ -80,6 +79,7 @@ py_library( "//tensorflow/python:variables", "//tensorflow/python/ops/distributions", "//tensorflow/python/ops/linalg", + "//tensorflow/python/ops/signal", "//third_party/py/numpy", "@six_archive//:six", ], diff --git a/tensorflow/contrib/distributions/python/kernel_tests/normal_conjugate_posteriors_test.py b/tensorflow/contrib/distributions/python/kernel_tests/normal_conjugate_posteriors_test.py index 29eeaf43c5..ab3c07172a 100644 --- a/tensorflow/contrib/distributions/python/kernel_tests/normal_conjugate_posteriors_test.py +++ b/tensorflow/contrib/distributions/python/kernel_tests/normal_conjugate_posteriors_test.py @@ -82,7 +82,7 @@ class NormalTest(test.TestCase): x = constant_op.constant( [[-2.5, 2.5, 4.0, 0.0, -1.0, 2.0], [2.5, -2.5, -4.0, 0.0, 1.0, -2.0]], dtype=dtypes.float32) - s = math_ops.reduce_sum(x, reduction_indices=[1]) + s = math_ops.reduce_sum(x, axis=[1]) x = array_ops.transpose(x) # Reshape to shape (6, 2) n = constant_op.constant([6] * 2) prior = distributions.Normal(loc=mu0, scale=sigma0) diff --git a/tensorflow/contrib/distributions/python/kernel_tests/wishart_test.py b/tensorflow/contrib/distributions/python/kernel_tests/wishart_test.py index a60056c444..cdee30bbc4 100644 --- a/tensorflow/contrib/distributions/python/kernel_tests/wishart_test.py +++ b/tensorflow/contrib/distributions/python/kernel_tests/wishart_test.py @@ -147,14 +147,13 @@ class WishartCholeskyTest(test.TestCase): x = chol_w.sample(10000, seed=42) self.assertAllEqual((10000, 3, 3), x.get_shape()) - moment1_estimate = math_ops.reduce_mean(x, reduction_indices=[0]).eval() + moment1_estimate = math_ops.reduce_mean(x, axis=[0]).eval() self.assertAllClose(chol_w.mean().eval(), moment1_estimate, rtol=0.05) # The Variance estimate uses the squares rather than outer-products # because Wishart.Variance is the diagonal of the Wishart covariance # matrix. - variance_estimate = (math_ops.reduce_mean( - math_ops.square(x), reduction_indices=[0]) - + variance_estimate = (math_ops.reduce_mean(math_ops.square(x), axis=[0]) - math_ops.square(moment1_estimate)).eval() self.assertAllClose( chol_w.variance().eval(), variance_estimate, rtol=0.05) diff --git a/tensorflow/contrib/distributions/python/ops/bijectors/softmax_centered.py b/tensorflow/contrib/distributions/python/ops/bijectors/softmax_centered.py index 15c241d5d7..74765f19e5 100644 --- a/tensorflow/contrib/distributions/python/ops/bijectors/softmax_centered.py +++ b/tensorflow/contrib/distributions/python/ops/bijectors/softmax_centered.py @@ -168,7 +168,7 @@ class SoftmaxCentered(bijector.Bijector): # log_normalization = 1 + reduce_sum(exp(logits)) # -log_normalization + reduce_sum(logits - log_normalization) log_normalization = nn_ops.softplus( - math_ops.reduce_logsumexp(x, axis=-1, keep_dims=True)) + math_ops.reduce_logsumexp(x, axis=-1, keepdims=True)) return array_ops.squeeze( (-log_normalization + math_ops.reduce_sum( x - log_normalization, axis=-1, keepdims=True)), axis=-1) diff --git a/tensorflow/contrib/distributions/python/ops/sample_stats.py b/tensorflow/contrib/distributions/python/ops/sample_stats.py index aa680a92be..978e627d66 100644 --- a/tensorflow/contrib/distributions/python/ops/sample_stats.py +++ b/tensorflow/contrib/distributions/python/ops/sample_stats.py @@ -29,8 +29,8 @@ from tensorflow.python.ops import clip_ops from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import nn_ops -from tensorflow.python.ops import spectral_ops from tensorflow.python.ops.distributions import util +from tensorflow.python.ops.signal import fft_ops __all__ = [ "auto_correlation", @@ -157,11 +157,11 @@ def auto_correlation( dtype.real_dtype.as_numpy_dtype(0.)) # Autocorrelation is IFFT of power-spectral density (up to some scaling). - fft_x_rotated_pad = spectral_ops.fft(x_rotated_pad) + fft_x_rotated_pad = fft_ops.fft(x_rotated_pad) spectral_density = fft_x_rotated_pad * math_ops.conj(fft_x_rotated_pad) # shifted_product is R[m] from above detailed explanation. # It is the inner product sum_n X[n] * Conj(X[n - m]). - shifted_product = spectral_ops.ifft(spectral_density) + shifted_product = fft_ops.ifft(spectral_density) # Cast back to real-valued if x was real to begin with. shifted_product = math_ops.cast(shifted_product, dtype) diff --git a/tensorflow/contrib/eager/python/datasets.py b/tensorflow/contrib/eager/python/datasets.py index 3aed121233..34614b86a7 100644 --- a/tensorflow/contrib/eager/python/datasets.py +++ b/tensorflow/contrib/eager/python/datasets.py @@ -52,12 +52,6 @@ class Iterator(iterator_ops.EagerIterator): TypeError: If `dataset` is an unsupported type. RuntimeError: When invoked without eager execution enabled. """ - if isinstance(dataset, prefetching_ops._PrefetchToDeviceDataset): # pylint: disable=protected-access - raise TypeError( - "`tf.data.experimental.prefetch_to_device()` is not compatible with " - "`tf.contrib.eager.Iterator`. Use `for ... in dataset:` to iterate " - "over the dataset instead.") - if not context.context().device_spec.device_type: is_remote_device = False else: diff --git a/tensorflow/contrib/eager/python/datasets_test.py b/tensorflow/contrib/eager/python/datasets_test.py index 6a508fc6ba..257d02057a 100644 --- a/tensorflow/contrib/eager/python/datasets_test.py +++ b/tensorflow/contrib/eager/python/datasets_test.py @@ -26,7 +26,6 @@ import numpy as np from tensorflow.contrib import lookup from tensorflow.contrib.eager.python import datasets from tensorflow.python.data import Dataset -from tensorflow.python.data.experimental.ops import prefetching_ops from tensorflow.python.data.experimental.ops import threadpool from tensorflow.python.data.experimental.ops import unique from tensorflow.python.eager import test @@ -208,18 +207,6 @@ class IteratorTest(test.TestCase): y = math_ops.add(x, x) self.assertAllEqual([0., 2.], y.numpy()) - def testTensorsExplicitPrefetchToDevice(self): - ds = Dataset.from_tensor_slices([0., 1.]) - ds = ds.apply(prefetching_ops.prefetch_to_device(test.gpu_device_name())) - - with self.assertRaisesRegexp(TypeError, 'prefetch_to_device'): - datasets.Iterator(ds) - - for i, x in enumerate(ds): - with ops.device(test.gpu_device_name()): - x = math_ops.add(x, x) - self.assertEqual(float(i) + float(i), x.numpy()) - def testOverrideThreadPool(self): def get_thread_id(_): diff --git a/tensorflow/contrib/eager/python/evaluator.py b/tensorflow/contrib/eager/python/evaluator.py index 7949a3f6da..51443d2482 100644 --- a/tensorflow/contrib/eager/python/evaluator.py +++ b/tensorflow/contrib/eager/python/evaluator.py @@ -22,6 +22,7 @@ import six from tensorflow.contrib.eager.python import datasets from tensorflow.contrib.eager.python import metrics +from tensorflow.python.data.ops import dataset_ops from tensorflow.python.eager import context from tensorflow.python.eager import function from tensorflow.python.framework import errors_impl @@ -164,8 +165,8 @@ class Evaluator(object): self.__call__(example, *args, **kwargs) return self.all_metric_results(summary_logdir) # Graph construction - call_op = self.__call__(dataset.make_one_shot_iterator().get_next(), *args, - **kwargs) + call_op = self.__call__( + dataset_ops.make_one_shot_iterator(dataset).get_next(), *args, **kwargs) init_op = self.init_variables() results_op = self.all_metric_results(summary_logdir) return (init_op, call_op, results_op) diff --git a/tensorflow/contrib/eager/python/examples/densenet/BUILD b/tensorflow/contrib/eager/python/examples/densenet/BUILD index 2dc196f550..e2154fcc5f 100644 --- a/tensorflow/contrib/eager/python/examples/densenet/BUILD +++ b/tensorflow/contrib/eager/python/examples/densenet/BUILD @@ -3,6 +3,7 @@ licenses(["notice"]) # Apache 2.0 package(default_visibility = ["//tensorflow:internal"]) load("//tensorflow:tensorflow.bzl", "cuda_py_test") +load("//tensorflow:tensorflow.bzl", "py_binary") py_binary( name = "densenet", diff --git a/tensorflow/contrib/eager/python/examples/densenet/densenet_graph_test.py b/tensorflow/contrib/eager/python/examples/densenet/densenet_graph_test.py index 4b3cb624bc..24f6b007b5 100644 --- a/tensorflow/contrib/eager/python/examples/densenet/densenet_graph_test.py +++ b/tensorflow/contrib/eager/python/examples/densenet/densenet_graph_test.py @@ -119,7 +119,8 @@ class DensenetBenchmark(tf.test.Benchmark): with tf.Graph().as_default(): np_images, np_labels = random_batch(batch_size) dataset = tf.data.Dataset.from_tensors((np_images, np_labels)).repeat() - (images, labels) = dataset.make_one_shot_iterator().get_next() + (images, labels) = tf.compat.v1.data.make_one_shot_iterator( + dataset).get_next() model = densenet.DenseNet(self.depth, self.growth_rate, self.num_blocks, self.output_classes, diff --git a/tensorflow/contrib/eager/python/examples/gan/mnist_graph_test.py b/tensorflow/contrib/eager/python/examples/gan/mnist_graph_test.py index 12b39b0cde..e73841fbf7 100644 --- a/tensorflow/contrib/eager/python/examples/gan/mnist_graph_test.py +++ b/tensorflow/contrib/eager/python/examples/gan/mnist_graph_test.py @@ -42,7 +42,8 @@ class MnistGraphGanBenchmark(tf.test.Benchmark): # Generate some random data. images_data = np.random.randn(batch_size, 784).astype(np.float32) dataset = tf.data.Dataset.from_tensors(images_data) - images = dataset.repeat().make_one_shot_iterator().get_next() + images = tf.compat.v1.data.make_one_shot_iterator( + dataset.repeat()).get_next() # Create the models and optimizers generator = mnist.Generator(data_format()) diff --git a/tensorflow/contrib/eager/python/examples/generative_examples/cvae.ipynb b/tensorflow/contrib/eager/python/examples/generative_examples/cvae.ipynb index ca27a85a22..1a08cc0fd0 100644 --- a/tensorflow/contrib/eager/python/examples/generative_examples/cvae.ipynb +++ b/tensorflow/contrib/eager/python/examples/generative_examples/cvae.ipynb @@ -470,7 +470,7 @@ "\n", " if epoch % 1 == 0:\n", " loss = tfe.metrics.Mean()\n", - " for test_x in test_dataset.make_one_shot_iterator():\n", + " for test_x in test_dataset:\n", " loss(compute_loss(model, test_x))\n", " elbo = -loss.result()\n", " display.clear_output(wait=False)\n", diff --git a/tensorflow/contrib/eager/python/examples/generative_examples/image_captioning_with_attention.ipynb b/tensorflow/contrib/eager/python/examples/generative_examples/image_captioning_with_attention.ipynb index 3acecd283c..12c5eff2b4 100644 --- a/tensorflow/contrib/eager/python/examples/generative_examples/image_captioning_with_attention.ipynb +++ b/tensorflow/contrib/eager/python/examples/generative_examples/image_captioning_with_attention.ipynb @@ -1,1184 +1,1174 @@ { - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "K2s1A9eLRPEj" + }, + "source": [ + "##### Copyright 2018 The TensorFlow Authors.\n", + "\n", + "Licensed under the Apache License, Version 2.0 (the \"License\").\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Cffg2i257iMS" + }, + "source": [ + "# Image Captioning with Attention\n", + "\n", + "
\n", + "\n", + " Run in Google Colab \n", + "\n", + "View source on GitHub
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "QASbY_HGo4Lq" + }, + "source": [ + "Image captioning is the task of generating a caption for an image. Given an image like this:\n", + "\n", + "![Man Surfing](https://tensorflow.org/images/surf.jpg) \n", + "\n", + "[Image Source](https://commons.wikimedia.org/wiki/Surfing#/media/File:Surfing_in_Hawaii.jpg), License: Public Domain\n", + "\n", + "Our goal is to generate a caption, such as \"a surfer riding on a wave\". Here, we'll use an attention-based model. This enables us to see which parts of the image the model focuses on as it generates a caption.\n", + "\n", + "![Prediction](https://tensorflow.org/images/imcap_prediction.png)\n", + "\n", + "This model architecture below is similar to [Show, Attend and Tell: Neural Image Caption Generation with Visual Attention](https://arxiv.org/abs/1502.03044). \n", + "\n", + "The code uses [tf.keras](https://www.tensorflow.org/programmers_guide/keras) and [eager execution](https://www.tensorflow.org/programmers_guide/eager), which you can learn more about in the linked guides.\n", + "\n", + "This notebook is an end-to-end example. If you run it, it will download the [MS-COCO](http://cocodataset.org/#home) dataset, preprocess and cache a subset of the images using Inception V3, train an encoder-decoder model, and use it to generate captions on new images.\n", + "\n", + "The code requires TensorFlow version >=1.9. If you're running this in [Colab]()\n", + "\n", + "In this example, we're training on a relatively small amount of data as an example. On a single P100 GPU, this example will take about ~2 hours to train. We train on the first 30,000 captions (corresponding to about ~20,000 images depending on shuffling, as there are multiple captions per image in the dataset)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { "colab": { - "name": "image_captioning_with_attention.ipynb", - "version": "0.3.2", - "views": {}, - "default_view": {}, - "provenance": [ - { - "file_id": "1HI8OK2sMjcx9CTWVn0122QAHOuXaOaMg", - "timestamp": 1530222436922 - } - ], - "private_outputs": true, - "collapsed_sections": [], - "toc_visible": true + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "accelerator": "GPU" + "colab_type": "code", + "id": "U8l4RJ0XRPEm" + }, + "outputs": [], + "source": [ + "# Import TensorFlow and enable eager execution\n", + "# This code requires TensorFlow version >=1.9\n", + "import tensorflow as tf\n", + "tf.enable_eager_execution()\n", + "\n", + "# We'll generate plots of attention in order to see which parts of an image\n", + "# our model focuses on during captioning\n", + "import matplotlib.pyplot as plt\n", + "\n", + "# Scikit-learn includes many helpful utilities\n", + "from sklearn.model_selection import train_test_split\n", + "from sklearn.utils import shuffle\n", + "\n", + "import re\n", + "import numpy as np\n", + "import os\n", + "import time\n", + "import json\n", + "from glob import glob\n", + "from PIL import Image\n", + "import pickle" + ] }, - "cells": [ - { - "metadata": { - "id": "K2s1A9eLRPEj", - "colab_type": "text" - }, - "cell_type": "markdown", - "source": [ - "##### Copyright 2018 The TensorFlow Authors.\n", - "\n", - "Licensed under the Apache License, Version 2.0 (the \"License\").\n" - ] - }, - { - "metadata": { - "id": "Cffg2i257iMS", - "colab_type": "text" - }, - "cell_type": "markdown", - "source": [ - "# Image Captioning with Attention\n", - "\n", - "
\n", - "\n", - " Run in Google Colab \n", - "\n", - "View source on GitHub
" - ] - }, - { - "metadata": { - "id": "QASbY_HGo4Lq", - "colab_type": "text" - }, - "cell_type": "markdown", - "source": [ - "Image captioning is the task of generating a caption for an image. Given an image like this:\n", - "\n", - "![Man Surfing](https://tensorflow.org/images/surf.jpg) \n", - "\n", - "[Image Source](https://commons.wikimedia.org/wiki/Surfing#/media/File:Surfing_in_Hawaii.jpg), License: Public Domain\n", - "\n", - "Our goal is to generate a caption, such as \"a surfer riding on a wave\". Here, we'll use an attention-based model. This enables us to see which parts of the image the model focuses on as it generates a caption.\n", - "\n", - "![Prediction](https://tensorflow.org/images/imcap_prediction.png)\n", - "\n", - "This model architecture below is similar to [Show, Attend and Tell: Neural Image Caption Generation with Visual Attention](https://arxiv.org/abs/1502.03044). \n", - "\n", - "The code uses [tf.keras](https://www.tensorflow.org/programmers_guide/keras) and [eager execution](https://www.tensorflow.org/programmers_guide/eager), which you can learn more about in the linked guides.\n", - "\n", - "This notebook is an end-to-end example. If you run it, it will download the [MS-COCO](http://cocodataset.org/#home) dataset, preprocess and cache a subset of the images using Inception V3, train an encoder-decoder model, and use it to generate captions on new images.\n", - "\n", - "The code requires TensorFlow version >=1.9. If you're running this in [Colab]()\n", - "\n", - "In this example, we're training on a relatively small amount of data as an example. On a single P100 GPU, this example will take about ~2 hours to train. We train on the first 30,000 captions (corresponding to about ~20,000 images depending on shuffling, as there are multiple captions per image in the dataset)\n" - ] - }, - { - "metadata": { - "id": "U8l4RJ0XRPEm", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "# Import TensorFlow and enable eager execution\n", - "# This code requires TensorFlow version >=1.9\n", - "import tensorflow as tf\n", - "tf.enable_eager_execution()\n", - "\n", - "# We'll generate plots of attention in order to see which parts of an image\n", - "# our model focuses on during captioning\n", - "import matplotlib.pyplot as plt\n", - "\n", - "# Scikit-learn includes many helpful utilities\n", - "from sklearn.model_selection import train_test_split\n", - "from sklearn.utils import shuffle\n", - "\n", - "import re\n", - "import numpy as np\n", - "import os\n", - "import time\n", - "import json\n", - "from glob import glob\n", - "from PIL import Image\n", - "import pickle" - ], - "execution_count": 0, - "outputs": [] - }, - { - "metadata": { - "id": "b6qbGw8MRPE5", - "colab_type": "text" - }, - "cell_type": "markdown", - "source": [ - "## Download and prepare the MS-COCO dataset\n", - "\n", - "We will use the [MS-COCO dataset](http://cocodataset.org/#home) to train our model. This dataset contains >82,000 images, each of which has been annotated with at least 5 different captions. The code below will download and extract the dataset automatically. \n", - "\n", - "**Caution: large download ahead**. We'll use the training set, it's a 13GB file." - ] - }, - { - "metadata": { - "id": "krQuPYTtRPE7", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "annotation_zip = tf.keras.utils.get_file('captions.zip', \n", - " cache_subdir=os.path.abspath('.'),\n", - " origin = 'http://images.cocodataset.org/annotations/annotations_trainval2014.zip',\n", - " extract = True)\n", - "annotation_file = os.path.dirname(annotation_zip)+'/annotations/captions_train2014.json'\n", - "\n", - "name_of_zip = 'train2014.zip'\n", - "if not os.path.exists(os.path.abspath('.') + '/' + name_of_zip):\n", - " image_zip = tf.keras.utils.get_file(name_of_zip, \n", - " cache_subdir=os.path.abspath('.'),\n", - " origin = 'http://images.cocodataset.org/zips/train2014.zip',\n", - " extract = True)\n", - " PATH = os.path.dirname(image_zip)+'/train2014/'\n", - "else:\n", - " PATH = os.path.abspath('.')+'/train2014/'" - ], - "execution_count": 0, - "outputs": [] - }, - { - "metadata": { - "id": "aANEzb5WwSzg", - "colab_type": "text" - }, - "cell_type": "markdown", - "source": [ - "## Optionally, limit the size of the training set for faster training\n", - "For this example, we'll select a subset of 30,000 captions and use these and the corresponding images to train our model. As always, captioning quality will improve if you choose to use more data." - ] - }, - { - "metadata": { - "id": "4G3b8x8_RPFD", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "# read the json file\n", - "with open(annotation_file, 'r') as f:\n", - " annotations = json.load(f)\n", - "\n", - "# storing the captions and the image name in vectors\n", - "all_captions = []\n", - "all_img_name_vector = []\n", - "\n", - "for annot in annotations['annotations']:\n", - " caption = ' ' + annot['caption'] + ' '\n", - " image_id = annot['image_id']\n", - " full_coco_image_path = PATH + 'COCO_train2014_' + '%012d.jpg' % (image_id)\n", - " \n", - " all_img_name_vector.append(full_coco_image_path)\n", - " all_captions.append(caption)\n", - "\n", - "# shuffling the captions and image_names together\n", - "# setting a random state\n", - "train_captions, img_name_vector = shuffle(all_captions,\n", - " all_img_name_vector,\n", - " random_state=1)\n", - "\n", - "# selecting the first 30000 captions from the shuffled set\n", - "num_examples = 30000\n", - "train_captions = train_captions[:num_examples]\n", - "img_name_vector = img_name_vector[:num_examples]" - ], - "execution_count": 0, - "outputs": [] - }, - { - "metadata": { - "id": "mPBMgK34RPFL", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "len(train_captions), len(all_captions)" - ], - "execution_count": 0, - "outputs": [] - }, - { - "metadata": { - "id": "8cSW4u-ORPFQ", - "colab_type": "text" - }, - "cell_type": "markdown", - "source": [ - "## Preprocess the images using InceptionV3\n", - "Next, we will use InceptionV3 (pretrained on Imagenet) to classify each image. We will extract features from the last convolutional layer. \n", - "\n", - "First, we will need to convert the images into the format inceptionV3 expects by:\n", - "* Resizing the image to (299, 299)\n", - "* Using the [preprocess_input](https://www.tensorflow.org/api_docs/python/tf/keras/applications/inception_v3/preprocess_input) method to place the pixels in the range of -1 to 1 (to match the format of the images used to train InceptionV3)." - ] - }, - { - "metadata": { - "id": "zXR0217aRPFR", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "def load_image(image_path):\n", - " img = tf.read_file(image_path)\n", - " img = tf.image.decode_jpeg(img, channels=3)\n", - " img = tf.image.resize_images(img, (299, 299))\n", - " img = tf.keras.applications.inception_v3.preprocess_input(img)\n", - " return img, image_path" - ], - "execution_count": 0, - "outputs": [] - }, - { - "metadata": { - "id": "MDvIu4sXRPFV", - "colab_type": "text" - }, - "cell_type": "markdown", - "source": [ - "## Initialize InceptionV3 and load the pretrained Imagenet weights\n", - "\n", - "To do so, we'll create a tf.keras model where the output layer is the last convolutional layer in the InceptionV3 architecture. \n", - "* Each image is forwarded through the network and the vector that we get at the end is stored in a dictionary (image_name --> feature_vector). \n", - "* We use the last convolutional layer because we are using attention in this example. The shape of the output of this layer is ```8x8x2048```. \n", - "* We avoid doing this during training so it does not become a bottleneck. \n", - "* After all the images are passed through the network, we pickle the dictionary and save it to disk." - ] - }, - { - "metadata": { - "id": "RD3vW4SsRPFW", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "image_model = tf.keras.applications.InceptionV3(include_top=False, \n", - " weights='imagenet')\n", - "new_input = image_model.input\n", - "hidden_layer = image_model.layers[-1].output\n", - "\n", - "image_features_extract_model = tf.keras.Model(new_input, hidden_layer)" - ], - "execution_count": 0, - "outputs": [] - }, - { - "metadata": { - "id": "rERqlR3WRPGO", - "colab_type": "text" - }, - "cell_type": "markdown", - "source": [ - "## Caching the features extracted from InceptionV3\n", - "\n", - "We will pre-process each image with InceptionV3 and cache the output to disk. Caching the output in RAM would be faster but memory intensive, requiring 8 \\* 8 \\* 2048 floats per image. At the time of writing, this would exceed the memory limitations of Colab (although these may change, an instance appears to have about 12GB of memory currently). \n", - "\n", - "Performance could be improved with a more sophisticated caching strategy (e.g., by sharding the images to reduce random access disk I/O) at the cost of more code.\n", - "\n", - "This will take about 10 minutes to run in Colab with a GPU. If you'd like to see a progress bar, you could: install [tqdm](https://github.com/tqdm/tqdm) (```!pip install tqdm```), then change this line: \n", - "\n", - "```for img, path in image_dataset:``` \n", - "\n", - "to:\n", - "\n", - "```for img, path in tqdm(image_dataset):```." - ] - }, - { - "metadata": { - "id": "Dx_fvbVgRPGQ", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "# getting the unique images\n", - "encode_train = sorted(set(img_name_vector))\n", - "\n", - "# feel free to change the batch_size according to your system configuration\n", - "image_dataset = tf.data.Dataset.from_tensor_slices(\n", - " encode_train).map(load_image).batch(16)\n", - "\n", - "for img, path in image_dataset:\n", - " batch_features = image_features_extract_model(img)\n", - " batch_features = tf.reshape(batch_features, \n", - " (batch_features.shape[0], -1, batch_features.shape[3]))\n", - "\n", - " for bf, p in zip(batch_features, path):\n", - " path_of_feature = p.numpy().decode(\"utf-8\")\n", - " np.save(path_of_feature, bf.numpy())" - ], - "execution_count": 0, - "outputs": [] - }, - { - "metadata": { - "id": "nyqH3zFwRPFi", - "colab_type": "text" - }, - "cell_type": "markdown", - "source": [ - "## Preprocess and tokenize the captions\n", - "\n", - "* First, we'll tokenize the captions (e.g., by splitting on spaces). This will give us a vocabulary of all the unique words in the data (e.g., \"surfing\", \"football\", etc).\n", - "* Next, we'll limit the vocabulary size to the top 5,000 words to save memory. We'll replace all other words with the token \"UNK\" (for unknown).\n", - "* Finally, we create a word --> index mapping and vice-versa.\n", - "* We will then pad all sequences to the be same length as the longest one. " - ] - }, - { - "metadata": { - "id": "HZfK8RhQRPFj", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "# This will find the maximum length of any caption in our dataset\n", - "def calc_max_length(tensor):\n", - " return max(len(t) for t in tensor)" - ], - "execution_count": 0, - "outputs": [] - }, - { - "metadata": { - "id": "oJGE34aiRPFo", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "# The steps above is a general process of dealing with text processing\n", - "\n", - "# choosing the top 5000 words from the vocabulary\n", - "top_k = 5000\n", - "tokenizer = tf.keras.preprocessing.text.Tokenizer(num_words=top_k, \n", - " oov_token=\"\", \n", - " filters='!\"#$%&()*+.,-/:;=?@[\\]^_`{|}~ ')\n", - "tokenizer.fit_on_texts(train_captions)\n", - "train_seqs = tokenizer.texts_to_sequences(train_captions)" - ], - "execution_count": 0, - "outputs": [] + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "b6qbGw8MRPE5" + }, + "source": [ + "## Download and prepare the MS-COCO dataset\n", + "\n", + "We will use the [MS-COCO dataset](http://cocodataset.org/#home) to train our model. This dataset contains >82,000 images, each of which has been annotated with at least 5 different captions. The code below will download and extract the dataset automatically. \n", + "\n", + "**Caution: large download ahead**. We'll use the training set, it's a 13GB file." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "8Q44tNQVRPFt", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "tokenizer.word_index = {key:value for key, value in tokenizer.word_index.items() if value <= top_k}\n", - "# putting token in the word2idx dictionary\n", - "tokenizer.word_index[tokenizer.oov_token] = top_k + 1\n", - "tokenizer.word_index[''] = 0" - ], - "execution_count": 0, - "outputs": [] + "colab_type": "code", + "id": "krQuPYTtRPE7" + }, + "outputs": [], + "source": [ + "annotation_zip = tf.keras.utils.get_file('captions.zip', \n", + " cache_subdir=os.path.abspath('.'),\n", + " origin = 'http://images.cocodataset.org/annotations/annotations_trainval2014.zip',\n", + " extract = True)\n", + "annotation_file = os.path.dirname(annotation_zip)+'/annotations/captions_train2014.json'\n", + "\n", + "name_of_zip = 'train2014.zip'\n", + "if not os.path.exists(os.path.abspath('.') + '/' + name_of_zip):\n", + " image_zip = tf.keras.utils.get_file(name_of_zip, \n", + " cache_subdir=os.path.abspath('.'),\n", + " origin = 'http://images.cocodataset.org/zips/train2014.zip',\n", + " extract = True)\n", + " PATH = os.path.dirname(image_zip)+'/train2014/'\n", + "else:\n", + " PATH = os.path.abspath('.')+'/train2014/'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "aANEzb5WwSzg" + }, + "source": [ + "## Optionally, limit the size of the training set for faster training\n", + "For this example, we'll select a subset of 30,000 captions and use these and the corresponding images to train our model. As always, captioning quality will improve if you choose to use more data." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "0fpJb5ojRPFv", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "# creating the tokenized vectors\n", - "train_seqs = tokenizer.texts_to_sequences(train_captions)" - ], - "execution_count": 0, - "outputs": [] + "colab_type": "code", + "id": "4G3b8x8_RPFD" + }, + "outputs": [], + "source": [ + "# read the json file\n", + "with open(annotation_file, 'r') as f:\n", + " annotations = json.load(f)\n", + "\n", + "# storing the captions and the image name in vectors\n", + "all_captions = []\n", + "all_img_name_vector = []\n", + "\n", + "for annot in annotations['annotations']:\n", + " caption = ' ' + annot['caption'] + ' '\n", + " image_id = annot['image_id']\n", + " full_coco_image_path = PATH + 'COCO_train2014_' + '%012d.jpg' % (image_id)\n", + " \n", + " all_img_name_vector.append(full_coco_image_path)\n", + " all_captions.append(caption)\n", + "\n", + "# shuffling the captions and image_names together\n", + "# setting a random state\n", + "train_captions, img_name_vector = shuffle(all_captions,\n", + " all_img_name_vector,\n", + " random_state=1)\n", + "\n", + "# selecting the first 30000 captions from the shuffled set\n", + "num_examples = 30000\n", + "train_captions = train_captions[:num_examples]\n", + "img_name_vector = img_name_vector[:num_examples]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "olQArbgbRPF1", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "# creating a reverse mapping (index -> word)\n", - "index_word = {value:key for key, value in tokenizer.word_index.items()}" - ], - "execution_count": 0, - "outputs": [] + "colab_type": "code", + "id": "mPBMgK34RPFL" + }, + "outputs": [], + "source": [ + "len(train_captions), len(all_captions)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "8cSW4u-ORPFQ" + }, + "source": [ + "## Preprocess the images using InceptionV3\n", + "Next, we will use InceptionV3 (pretrained on Imagenet) to classify each image. We will extract features from the last convolutional layer. \n", + "\n", + "First, we will need to convert the images into the format inceptionV3 expects by:\n", + "* Resizing the image to (299, 299)\n", + "* Using the [preprocess_input](https://www.tensorflow.org/api_docs/python/tf/keras/applications/inception_v3/preprocess_input) method to place the pixels in the range of -1 to 1 (to match the format of the images used to train InceptionV3)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "AidglIZVRPF4", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "# padding each vector to the max_length of the captions\n", - "# if the max_length parameter is not provided, pad_sequences calculates that automatically\n", - "cap_vector = tf.keras.preprocessing.sequence.pad_sequences(train_seqs, padding='post')" - ], - "execution_count": 0, - "outputs": [] + "colab_type": "code", + "id": "zXR0217aRPFR" + }, + "outputs": [], + "source": [ + "def load_image(image_path):\n", + " img = tf.read_file(image_path)\n", + " img = tf.image.decode_jpeg(img, channels=3)\n", + " img = tf.image.resize_images(img, (299, 299))\n", + " img = tf.keras.applications.inception_v3.preprocess_input(img)\n", + " return img, image_path" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "MDvIu4sXRPFV" + }, + "source": [ + "## Initialize InceptionV3 and load the pretrained Imagenet weights\n", + "\n", + "To do so, we'll create a tf.keras model where the output layer is the last convolutional layer in the InceptionV3 architecture. \n", + "* Each image is forwarded through the network and the vector that we get at the end is stored in a dictionary (image_name --> feature_vector). \n", + "* We use the last convolutional layer because we are using attention in this example. The shape of the output of this layer is ```8x8x2048```. \n", + "* We avoid doing this during training so it does not become a bottleneck. \n", + "* After all the images are passed through the network, we pickle the dictionary and save it to disk." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "gL0wkttkRPGA", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "# calculating the max_length \n", - "# used to store the attention weights\n", - "max_length = calc_max_length(train_seqs)" - ], - "execution_count": 0, - "outputs": [] + "colab_type": "code", + "id": "RD3vW4SsRPFW" + }, + "outputs": [], + "source": [ + "image_model = tf.keras.applications.InceptionV3(include_top=False, \n", + " weights='imagenet')\n", + "new_input = image_model.input\n", + "hidden_layer = image_model.layers[-1].output\n", + "\n", + "image_features_extract_model = tf.keras.Model(new_input, hidden_layer)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "rERqlR3WRPGO" + }, + "source": [ + "## Caching the features extracted from InceptionV3\n", + "\n", + "We will pre-process each image with InceptionV3 and cache the output to disk. Caching the output in RAM would be faster but memory intensive, requiring 8 \\* 8 \\* 2048 floats per image. At the time of writing, this would exceed the memory limitations of Colab (although these may change, an instance appears to have about 12GB of memory currently). \n", + "\n", + "Performance could be improved with a more sophisticated caching strategy (e.g., by sharding the images to reduce random access disk I/O) at the cost of more code.\n", + "\n", + "This will take about 10 minutes to run in Colab with a GPU. If you'd like to see a progress bar, you could: install [tqdm](https://github.com/tqdm/tqdm) (```!pip install tqdm```), then change this line: \n", + "\n", + "```for img, path in image_dataset:``` \n", + "\n", + "to:\n", + "\n", + "```for img, path in tqdm(image_dataset):```." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "M3CD75nDpvTI", - "colab_type": "text" - }, - "cell_type": "markdown", - "source": [ - "## Split the data into training and testing" - ] + "colab_type": "code", + "id": "Dx_fvbVgRPGQ" + }, + "outputs": [], + "source": [ + "# getting the unique images\n", + "encode_train = sorted(set(img_name_vector))\n", + "\n", + "# feel free to change the batch_size according to your system configuration\n", + "image_dataset = tf.data.Dataset.from_tensor_slices(\n", + " encode_train).map(load_image).batch(16)\n", + "\n", + "for img, path in image_dataset:\n", + " batch_features = image_features_extract_model(img)\n", + " batch_features = tf.reshape(batch_features, \n", + " (batch_features.shape[0], -1, batch_features.shape[3]))\n", + "\n", + " for bf, p in zip(batch_features, path):\n", + " path_of_feature = p.numpy().decode(\"utf-8\")\n", + " np.save(path_of_feature, bf.numpy())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "nyqH3zFwRPFi" + }, + "source": [ + "## Preprocess and tokenize the captions\n", + "\n", + "* First, we'll tokenize the captions (e.g., by splitting on spaces). This will give us a vocabulary of all the unique words in the data (e.g., \"surfing\", \"football\", etc).\n", + "* Next, we'll limit the vocabulary size to the top 5,000 words to save memory. We'll replace all other words with the token \"UNK\" (for unknown).\n", + "* Finally, we create a word --> index mapping and vice-versa.\n", + "* We will then pad all sequences to the be same length as the longest one. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "iS7DDMszRPGF", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "# Create training and validation sets using 80-20 split\n", - "img_name_train, img_name_val, cap_train, cap_val = train_test_split(img_name_vector, \n", - " cap_vector, \n", - " test_size=0.2, \n", - " random_state=0)" - ], - "execution_count": 0, - "outputs": [] + "colab_type": "code", + "id": "HZfK8RhQRPFj" + }, + "outputs": [], + "source": [ + "# This will find the maximum length of any caption in our dataset\n", + "def calc_max_length(tensor):\n", + " return max(len(t) for t in tensor)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "XmViPkRFRPGH", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "len(img_name_train), len(cap_train), len(img_name_val), len(cap_val)" - ], - "execution_count": 0, - "outputs": [] + "colab_type": "code", + "id": "oJGE34aiRPFo" + }, + "outputs": [], + "source": [ + "# The steps above is a general process of dealing with text processing\n", + "\n", + "# choosing the top 5000 words from the vocabulary\n", + "top_k = 5000\n", + "tokenizer = tf.keras.preprocessing.text.Tokenizer(num_words=top_k, \n", + " oov_token=\"\", \n", + " filters='!\"#$%&()*+.,-/:;=?@[\\]^_`{|}~ ')\n", + "tokenizer.fit_on_texts(train_captions)\n", + "train_seqs = tokenizer.texts_to_sequences(train_captions)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "uEWM9xrYcg45", - "colab_type": "text" - }, - "cell_type": "markdown", - "source": [ - "## Our images and captions are ready! Next, let's create a tf.data dataset to use for training our model.\n", - "\n" - ] + "colab_type": "code", + "id": "8Q44tNQVRPFt" + }, + "outputs": [], + "source": [ + "tokenizer.word_index[''] = 0" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "Q3TnZ1ToRPGV", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "# feel free to change these parameters according to your system's configuration\n", - "\n", - "BATCH_SIZE = 64\n", - "BUFFER_SIZE = 1000\n", - "embedding_dim = 256\n", - "units = 512\n", - "vocab_size = len(tokenizer.word_index)\n", - "# shape of the vector extracted from InceptionV3 is (64, 2048)\n", - "# these two variables represent that\n", - "features_shape = 2048\n", - "attention_features_shape = 64" - ], - "execution_count": 0, - "outputs": [] + "colab_type": "code", + "id": "0fpJb5ojRPFv" + }, + "outputs": [], + "source": [ + "# creating the tokenized vectors\n", + "train_seqs = tokenizer.texts_to_sequences(train_captions)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "SmZS2N0bXG3T", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "# loading the numpy files \n", - "def map_func(img_name, cap):\n", - " img_tensor = np.load(img_name.decode('utf-8')+'.npy')\n", - " return img_tensor, cap" - ], - "execution_count": 0, - "outputs": [] + "colab_type": "code", + "id": "AidglIZVRPF4" + }, + "outputs": [], + "source": [ + "# padding each vector to the max_length of the captions\n", + "# if the max_length parameter is not provided, pad_sequences calculates that automatically\n", + "cap_vector = tf.keras.preprocessing.sequence.pad_sequences(train_seqs, padding='post')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "FDF_Nm3tRPGZ", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "dataset = tf.data.Dataset.from_tensor_slices((img_name_train, cap_train))\n", - "\n", - "# using map to load the numpy files in parallel\n", - "# NOTE: Be sure to set num_parallel_calls to the number of CPU cores you have\n", - "# https://www.tensorflow.org/api_docs/python/tf/py_func\n", - "dataset = dataset.map(lambda item1, item2: tf.py_func(\n", - " map_func, [item1, item2], [tf.float32, tf.int32]), num_parallel_calls=8)\n", - "\n", - "# shuffling and batching\n", - "dataset = dataset.shuffle(BUFFER_SIZE)\n", - "# https://www.tensorflow.org/api_docs/python/tf/contrib/data/batch_and_drop_remainder\n", - "dataset = dataset.batch(BATCH_SIZE)\n", - "dataset = dataset.prefetch(1)" - ], - "execution_count": 0, - "outputs": [] + "colab_type": "code", + "id": "gL0wkttkRPGA" + }, + "outputs": [], + "source": [ + "# calculating the max_length \n", + "# used to store the attention weights\n", + "max_length = calc_max_length(train_seqs)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "M3CD75nDpvTI" + }, + "source": [ + "## Split the data into training and testing" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "nrvoDphgRPGd", - "colab_type": "text" - }, - "cell_type": "markdown", - "source": [ - "## Model\n", - "\n", - "Fun fact, the decoder below is identical to the one in the example for [Neural Machine Translation with Attention]( https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/eager/python/examples/nmt_with_attention/nmt_with_attention.ipynb).\n", - "\n", - "The model architecture is inspired by the [Show, Attend and Tell](https://arxiv.org/pdf/1502.03044.pdf) paper.\n", - "\n", - "* In this example, we extract the features from the lower convolutional layer of InceptionV3 giving us a vector of shape (8, 8, 2048). \n", - "* We squash that to a shape of (64, 2048).\n", - "* This vector is then passed through the CNN Encoder(which consists of a single Fully connected layer).\n", - "* The RNN(here GRU) attends over the image to predict the next word." - ] + "colab_type": "code", + "id": "iS7DDMszRPGF" + }, + "outputs": [], + "source": [ + "# Create training and validation sets using 80-20 split\n", + "img_name_train, img_name_val, cap_train, cap_val = train_test_split(img_name_vector, \n", + " cap_vector, \n", + " test_size=0.2, \n", + " random_state=0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "AAppCGLKRPGd", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "def gru(units):\n", - " # If you have a GPU, we recommend using the CuDNNGRU layer (it provides a \n", - " # significant speedup).\n", - " if tf.test.is_gpu_available():\n", - " return tf.keras.layers.CuDNNGRU(units, \n", - " return_sequences=True, \n", - " return_state=True, \n", - " recurrent_initializer='glorot_uniform')\n", - " else:\n", - " return tf.keras.layers.GRU(units, \n", - " return_sequences=True, \n", - " return_state=True, \n", - " recurrent_activation='sigmoid', \n", - " recurrent_initializer='glorot_uniform')" - ], - "execution_count": 0, - "outputs": [] + "colab_type": "code", + "id": "XmViPkRFRPGH" + }, + "outputs": [], + "source": [ + "len(img_name_train), len(cap_train), len(img_name_val), len(cap_val)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "uEWM9xrYcg45" + }, + "source": [ + "## Our images and captions are ready! Next, let's create a tf.data dataset to use for training our model.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "ja2LFTMSdeV3", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "class BahdanauAttention(tf.keras.Model):\n", - " def __init__(self, units):\n", - " super(BahdanauAttention, self).__init__()\n", - " self.W1 = tf.keras.layers.Dense(units)\n", - " self.W2 = tf.keras.layers.Dense(units)\n", - " self.V = tf.keras.layers.Dense(1)\n", - " \n", - " def call(self, features, hidden):\n", - " # features(CNN_encoder output) shape == (batch_size, 64, embedding_dim)\n", - " \n", - " # hidden shape == (batch_size, hidden_size)\n", - " # hidden_with_time_axis shape == (batch_size, 1, hidden_size)\n", - " hidden_with_time_axis = tf.expand_dims(hidden, 1)\n", - " \n", - " # score shape == (batch_size, 64, hidden_size)\n", - " score = tf.nn.tanh(self.W1(features) + self.W2(hidden_with_time_axis))\n", - " \n", - " # attention_weights shape == (batch_size, 64, 1)\n", - " # we get 1 at the last axis because we are applying score to self.V\n", - " attention_weights = tf.nn.softmax(self.V(score), axis=1)\n", - " \n", - " # context_vector shape after sum == (batch_size, hidden_size)\n", - " context_vector = attention_weights * features\n", - " context_vector = tf.reduce_sum(context_vector, axis=1)\n", - " \n", - " return context_vector, attention_weights" - ], - "execution_count": 0, - "outputs": [] + "colab_type": "code", + "id": "Q3TnZ1ToRPGV" + }, + "outputs": [], + "source": [ + "# feel free to change these parameters according to your system's configuration\n", + "\n", + "BATCH_SIZE = 64\n", + "BUFFER_SIZE = 1000\n", + "embedding_dim = 256\n", + "units = 512\n", + "vocab_size = len(tokenizer.word_index)\n", + "# shape of the vector extracted from InceptionV3 is (64, 2048)\n", + "# these two variables represent that\n", + "features_shape = 2048\n", + "attention_features_shape = 64" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "AZ7R1RxHRPGf", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "class CNN_Encoder(tf.keras.Model):\n", - " # Since we have already extracted the features and dumped it using pickle\n", - " # This encoder passes those features through a Fully connected layer\n", - " def __init__(self, embedding_dim):\n", - " super(CNN_Encoder, self).__init__()\n", - " # shape after fc == (batch_size, 64, embedding_dim)\n", - " self.fc = tf.keras.layers.Dense(embedding_dim)\n", - " \n", - " def call(self, x):\n", - " x = self.fc(x)\n", - " x = tf.nn.relu(x)\n", - " return x" - ], - "execution_count": 0, - "outputs": [] + "colab_type": "code", + "id": "SmZS2N0bXG3T" + }, + "outputs": [], + "source": [ + "# loading the numpy files \n", + "def map_func(img_name, cap):\n", + " img_tensor = np.load(img_name.decode('utf-8')+'.npy')\n", + " return img_tensor, cap" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "V9UbGQmERPGi", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "class RNN_Decoder(tf.keras.Model):\n", - " def __init__(self, embedding_dim, units, vocab_size):\n", - " super(RNN_Decoder, self).__init__()\n", - " self.units = units\n", - "\n", - " self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim)\n", - " self.gru = gru(self.units)\n", - " self.fc1 = tf.keras.layers.Dense(self.units)\n", - " self.fc2 = tf.keras.layers.Dense(vocab_size)\n", - " \n", - " self.attention = BahdanauAttention(self.units)\n", - " \n", - " def call(self, x, features, hidden):\n", - " # defining attention as a separate model\n", - " context_vector, attention_weights = self.attention(features, hidden)\n", - " \n", - " # x shape after passing through embedding == (batch_size, 1, embedding_dim)\n", - " x = self.embedding(x)\n", - " \n", - " # x shape after concatenation == (batch_size, 1, embedding_dim + hidden_size)\n", - " x = tf.concat([tf.expand_dims(context_vector, 1), x], axis=-1)\n", - " \n", - " # passing the concatenated vector to the GRU\n", - " output, state = self.gru(x)\n", - " \n", - " # shape == (batch_size, max_length, hidden_size)\n", - " x = self.fc1(output)\n", - " \n", - " # x shape == (batch_size * max_length, hidden_size)\n", - " x = tf.reshape(x, (-1, x.shape[2]))\n", - " \n", - " # output shape == (batch_size * max_length, vocab)\n", - " x = self.fc2(x)\n", - "\n", - " return x, state, attention_weights\n", - "\n", - " def reset_state(self, batch_size):\n", - " return tf.zeros((batch_size, self.units))" - ], - "execution_count": 0, - "outputs": [] + "colab_type": "code", + "id": "FDF_Nm3tRPGZ" + }, + "outputs": [], + "source": [ + "dataset = tf.data.Dataset.from_tensor_slices((img_name_train, cap_train))\n", + "\n", + "# using map to load the numpy files in parallel\n", + "# NOTE: Be sure to set num_parallel_calls to the number of CPU cores you have\n", + "# https://www.tensorflow.org/api_docs/python/tf/py_func\n", + "dataset = dataset.map(lambda item1, item2: tf.py_func(\n", + " map_func, [item1, item2], [tf.float32, tf.int32]), num_parallel_calls=8)\n", + "\n", + "# shuffling and batching\n", + "dataset = dataset.shuffle(BUFFER_SIZE)\n", + "# https://www.tensorflow.org/api_docs/python/tf/contrib/data/batch_and_drop_remainder\n", + "dataset = dataset.batch(BATCH_SIZE)\n", + "dataset = dataset.prefetch(1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "nrvoDphgRPGd" + }, + "source": [ + "## Model\n", + "\n", + "Fun fact, the decoder below is identical to the one in the example for [Neural Machine Translation with Attention]( https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/eager/python/examples/nmt_with_attention/nmt_with_attention.ipynb).\n", + "\n", + "The model architecture is inspired by the [Show, Attend and Tell](https://arxiv.org/pdf/1502.03044.pdf) paper.\n", + "\n", + "* In this example, we extract the features from the lower convolutional layer of InceptionV3 giving us a vector of shape (8, 8, 2048). \n", + "* We squash that to a shape of (64, 2048).\n", + "* This vector is then passed through the CNN Encoder(which consists of a single Fully connected layer).\n", + "* The RNN(here GRU) attends over the image to predict the next word." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "Qs_Sr03wRPGk", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "encoder = CNN_Encoder(embedding_dim)\n", - "decoder = RNN_Decoder(embedding_dim, units, vocab_size)" - ], - "execution_count": 0, - "outputs": [] + "colab_type": "code", + "id": "AAppCGLKRPGd" + }, + "outputs": [], + "source": [ + "def gru(units):\n", + " # If you have a GPU, we recommend using the CuDNNGRU layer (it provides a \n", + " # significant speedup).\n", + " if tf.test.is_gpu_available():\n", + " return tf.keras.layers.CuDNNGRU(units, \n", + " return_sequences=True, \n", + " return_state=True, \n", + " recurrent_initializer='glorot_uniform')\n", + " else:\n", + " return tf.keras.layers.GRU(units, \n", + " return_sequences=True, \n", + " return_state=True, \n", + " recurrent_activation='sigmoid', \n", + " recurrent_initializer='glorot_uniform')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "-bYN7xA0RPGl", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "optimizer = tf.train.AdamOptimizer()\n", - "\n", - "# We are masking the loss calculated for padding\n", - "def loss_function(real, pred):\n", - " mask = 1 - np.equal(real, 0)\n", - " loss_ = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=real, logits=pred) * mask\n", - " return tf.reduce_mean(loss_)" - ], - "execution_count": 0, - "outputs": [] + "colab_type": "code", + "id": "ja2LFTMSdeV3" + }, + "outputs": [], + "source": [ + "class BahdanauAttention(tf.keras.Model):\n", + " def __init__(self, units):\n", + " super(BahdanauAttention, self).__init__()\n", + " self.W1 = tf.keras.layers.Dense(units)\n", + " self.W2 = tf.keras.layers.Dense(units)\n", + " self.V = tf.keras.layers.Dense(1)\n", + " \n", + " def call(self, features, hidden):\n", + " # features(CNN_encoder output) shape == (batch_size, 64, embedding_dim)\n", + " \n", + " # hidden shape == (batch_size, hidden_size)\n", + " # hidden_with_time_axis shape == (batch_size, 1, hidden_size)\n", + " hidden_with_time_axis = tf.expand_dims(hidden, 1)\n", + " \n", + " # score shape == (batch_size, 64, hidden_size)\n", + " score = tf.nn.tanh(self.W1(features) + self.W2(hidden_with_time_axis))\n", + " \n", + " # attention_weights shape == (batch_size, 64, 1)\n", + " # we get 1 at the last axis because we are applying score to self.V\n", + " attention_weights = tf.nn.softmax(self.V(score), axis=1)\n", + " \n", + " # context_vector shape after sum == (batch_size, hidden_size)\n", + " context_vector = attention_weights * features\n", + " context_vector = tf.reduce_sum(context_vector, axis=1)\n", + " \n", + " return context_vector, attention_weights" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "PHod7t72RPGn", - "colab_type": "text" - }, - "cell_type": "markdown", - "source": [ - "## Training\n", - "\n", - "* We extract the features stored in the respective `.npy` files and then pass those features through the encoder.\n", - "* The encoder output, hidden state(initialized to 0) and the decoder input (which is the start token) is passed to the decoder.\n", - "* The decoder returns the predictions and the decoder hidden state.\n", - "* The decoder hidden state is then passed back into the model and the predictions are used to calculate the loss.\n", - "* Use teacher forcing to decide the next input to the decoder.\n", - "* Teacher forcing is the technique where the target word is passed as the next input to the decoder.\n", - "* The final step is to calculate the gradients and apply it to the optimizer and backpropagate.\n" - ] + "colab_type": "code", + "id": "AZ7R1RxHRPGf" + }, + "outputs": [], + "source": [ + "class CNN_Encoder(tf.keras.Model):\n", + " # Since we have already extracted the features and dumped it using pickle\n", + " # This encoder passes those features through a Fully connected layer\n", + " def __init__(self, embedding_dim):\n", + " super(CNN_Encoder, self).__init__()\n", + " # shape after fc == (batch_size, 64, embedding_dim)\n", + " self.fc = tf.keras.layers.Dense(embedding_dim)\n", + " \n", + " def call(self, x):\n", + " x = self.fc(x)\n", + " x = tf.nn.relu(x)\n", + " return x" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "Vt4WZ5mhJE-E", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "# adding this in a separate cell because if you run the training cell \n", - "# many times, the loss_plot array will be reset\n", - "loss_plot = []" - ], - "execution_count": 0, - "outputs": [] + "colab_type": "code", + "id": "V9UbGQmERPGi" + }, + "outputs": [], + "source": [ + "class RNN_Decoder(tf.keras.Model):\n", + " def __init__(self, embedding_dim, units, vocab_size):\n", + " super(RNN_Decoder, self).__init__()\n", + " self.units = units\n", + "\n", + " self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim)\n", + " self.gru = gru(self.units)\n", + " self.fc1 = tf.keras.layers.Dense(self.units)\n", + " self.fc2 = tf.keras.layers.Dense(vocab_size)\n", + " \n", + " self.attention = BahdanauAttention(self.units)\n", + " \n", + " def call(self, x, features, hidden):\n", + " # defining attention as a separate model\n", + " context_vector, attention_weights = self.attention(features, hidden)\n", + " \n", + " # x shape after passing through embedding == (batch_size, 1, embedding_dim)\n", + " x = self.embedding(x)\n", + " \n", + " # x shape after concatenation == (batch_size, 1, embedding_dim + hidden_size)\n", + " x = tf.concat([tf.expand_dims(context_vector, 1), x], axis=-1)\n", + " \n", + " # passing the concatenated vector to the GRU\n", + " output, state = self.gru(x)\n", + " \n", + " # shape == (batch_size, max_length, hidden_size)\n", + " x = self.fc1(output)\n", + " \n", + " # x shape == (batch_size * max_length, hidden_size)\n", + " x = tf.reshape(x, (-1, x.shape[2]))\n", + " \n", + " # output shape == (batch_size * max_length, vocab)\n", + " x = self.fc2(x)\n", + "\n", + " return x, state, attention_weights\n", + "\n", + " def reset_state(self, batch_size):\n", + " return tf.zeros((batch_size, self.units))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "UlA4VIQpRPGo", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "EPOCHS = 20\n", - "\n", - "for epoch in range(EPOCHS):\n", - " start = time.time()\n", - " total_loss = 0\n", - " \n", - " for (batch, (img_tensor, target)) in enumerate(dataset):\n", - " loss = 0\n", - " \n", - " # initializing the hidden state for each batch\n", - " # because the captions are not related from image to image\n", - " hidden = decoder.reset_state(batch_size=target.shape[0])\n", - "\n", - " dec_input = tf.expand_dims([tokenizer.word_index['']] * BATCH_SIZE, 1)\n", - " \n", - " with tf.GradientTape() as tape:\n", - " features = encoder(img_tensor)\n", - " \n", - " for i in range(1, target.shape[1]):\n", - " # passing the features through the decoder\n", - " predictions, hidden, _ = decoder(dec_input, features, hidden)\n", - "\n", - " loss += loss_function(target[:, i], predictions)\n", - " \n", - " # using teacher forcing\n", - " dec_input = tf.expand_dims(target[:, i], 1)\n", - " \n", - " total_loss += (loss / int(target.shape[1]))\n", - " \n", - " variables = encoder.variables + decoder.variables\n", - " \n", - " gradients = tape.gradient(loss, variables) \n", - " \n", - " optimizer.apply_gradients(zip(gradients, variables), tf.train.get_or_create_global_step())\n", - " \n", - " if batch % 100 == 0:\n", - " print ('Epoch {} Batch {} Loss {:.4f}'.format(epoch + 1, \n", - " batch, \n", - " loss.numpy() / int(target.shape[1])))\n", - " # storing the epoch end loss value to plot later\n", - " loss_plot.append(total_loss / len(cap_vector))\n", - " \n", - " print ('Epoch {} Loss {:.6f}'.format(epoch + 1, \n", - " total_loss/len(cap_vector)))\n", - " print ('Time taken for 1 epoch {} sec\\n'.format(time.time() - start))" - ], - "execution_count": 0, - "outputs": [] + "colab_type": "code", + "id": "Qs_Sr03wRPGk" + }, + "outputs": [], + "source": [ + "encoder = CNN_Encoder(embedding_dim)\n", + "decoder = RNN_Decoder(embedding_dim, units, vocab_size)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "1Wm83G-ZBPcC", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "plt.plot(loss_plot)\n", - "plt.xlabel('Epochs')\n", - "plt.ylabel('Loss')\n", - "plt.title('Loss Plot')\n", - "plt.show()" - ], - "execution_count": 0, - "outputs": [] + "colab_type": "code", + "id": "-bYN7xA0RPGl" + }, + "outputs": [], + "source": [ + "optimizer = tf.train.AdamOptimizer()\n", + "\n", + "# We are masking the loss calculated for padding\n", + "def loss_function(real, pred):\n", + " mask = 1 - np.equal(real, 0)\n", + " loss_ = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=real, logits=pred) * mask\n", + " return tf.reduce_mean(loss_)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "PHod7t72RPGn" + }, + "source": [ + "## Training\n", + "\n", + "* We extract the features stored in the respective `.npy` files and then pass those features through the encoder.\n", + "* The encoder output, hidden state(initialized to 0) and the decoder input (which is the start token) is passed to the decoder.\n", + "* The decoder returns the predictions and the decoder hidden state.\n", + "* The decoder hidden state is then passed back into the model and the predictions are used to calculate the loss.\n", + "* Use teacher forcing to decide the next input to the decoder.\n", + "* Teacher forcing is the technique where the target word is passed as the next input to the decoder.\n", + "* The final step is to calculate the gradients and apply it to the optimizer and backpropagate.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "xGvOcLQKghXN", - "colab_type": "text" - }, - "cell_type": "markdown", - "source": [ - "## Caption!\n", - "\n", - "* The evaluate function is similar to the training loop, except we don't use teacher forcing here. The input to the decoder at each time step is its previous predictions along with the hidden state and the encoder output.\n", - "* Stop predicting when the model predicts the end token.\n", - "* And store the attention weights for every time step." - ] + "colab_type": "code", + "id": "Vt4WZ5mhJE-E" + }, + "outputs": [], + "source": [ + "# adding this in a separate cell because if you run the training cell \n", + "# many times, the loss_plot array will be reset\n", + "loss_plot = []" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "RCWpDtyNRPGs", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "def evaluate(image):\n", - " attention_plot = np.zeros((max_length, attention_features_shape))\n", - "\n", - " hidden = decoder.reset_state(batch_size=1)\n", - "\n", - " temp_input = tf.expand_dims(load_image(image)[0], 0)\n", - " img_tensor_val = image_features_extract_model(temp_input)\n", - " img_tensor_val = tf.reshape(img_tensor_val, (img_tensor_val.shape[0], -1, img_tensor_val.shape[3]))\n", - "\n", - " features = encoder(img_tensor_val)\n", - "\n", - " dec_input = tf.expand_dims([tokenizer.word_index['']], 0)\n", - " result = []\n", - "\n", - " for i in range(max_length):\n", - " predictions, hidden, attention_weights = decoder(dec_input, features, hidden)\n", - "\n", - " attention_plot[i] = tf.reshape(attention_weights, (-1, )).numpy()\n", - "\n", - " predicted_id = tf.argmax(predictions[0]).numpy()\n", - " result.append(index_word[predicted_id])\n", - "\n", - " if index_word[predicted_id] == '':\n", - " return result, attention_plot\n", - "\n", - " dec_input = tf.expand_dims([predicted_id], 0)\n", - "\n", - " attention_plot = attention_plot[:len(result), :]\n", - " return result, attention_plot" - ], - "execution_count": 0, - "outputs": [] + "colab_type": "code", + "id": "UlA4VIQpRPGo" + }, + "outputs": [], + "source": [ + "EPOCHS = 20\n", + "\n", + "for epoch in range(EPOCHS):\n", + " start = time.time()\n", + " total_loss = 0\n", + " \n", + " for (batch, (img_tensor, target)) in enumerate(dataset):\n", + " loss = 0\n", + " \n", + " # initializing the hidden state for each batch\n", + " # because the captions are not related from image to image\n", + " hidden = decoder.reset_state(batch_size=target.shape[0])\n", + "\n", + " dec_input = tf.expand_dims([tokenizer.word_index['']] * BATCH_SIZE, 1)\n", + " \n", + " with tf.GradientTape() as tape:\n", + " features = encoder(img_tensor)\n", + " \n", + " for i in range(1, target.shape[1]):\n", + " # passing the features through the decoder\n", + " predictions, hidden, _ = decoder(dec_input, features, hidden)\n", + "\n", + " loss += loss_function(target[:, i], predictions)\n", + " \n", + " # using teacher forcing\n", + " dec_input = tf.expand_dims(target[:, i], 1)\n", + " \n", + " total_loss += (loss / int(target.shape[1]))\n", + " \n", + " variables = encoder.variables + decoder.variables\n", + " \n", + " gradients = tape.gradient(loss, variables) \n", + " \n", + " optimizer.apply_gradients(zip(gradients, variables), tf.train.get_or_create_global_step())\n", + " \n", + " if batch % 100 == 0:\n", + " print ('Epoch {} Batch {} Loss {:.4f}'.format(epoch + 1, \n", + " batch, \n", + " loss.numpy() / int(target.shape[1])))\n", + " # storing the epoch end loss value to plot later\n", + " loss_plot.append(total_loss / len(cap_vector))\n", + " \n", + " print ('Epoch {} Loss {:.6f}'.format(epoch + 1, \n", + " total_loss/len(cap_vector)))\n", + " print ('Time taken for 1 epoch {} sec\\n'.format(time.time() - start))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "fD_y7PD6RPGt", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "def plot_attention(image, result, attention_plot):\n", - " temp_image = np.array(Image.open(image))\n", - "\n", - " fig = plt.figure(figsize=(10, 10))\n", - " \n", - " len_result = len(result)\n", - " for l in range(len_result):\n", - " temp_att = np.resize(attention_plot[l], (8, 8))\n", - " ax = fig.add_subplot(len_result//2, len_result//2, l+1)\n", - " ax.set_title(result[l])\n", - " img = ax.imshow(temp_image)\n", - " ax.imshow(temp_att, cmap='gray', alpha=0.6, extent=img.get_extent())\n", - "\n", - " plt.tight_layout()\n", - " plt.show()" - ], - "execution_count": 0, - "outputs": [] + "colab_type": "code", + "id": "1Wm83G-ZBPcC" + }, + "outputs": [], + "source": [ + "plt.plot(loss_plot)\n", + "plt.xlabel('Epochs')\n", + "plt.ylabel('Loss')\n", + "plt.title('Loss Plot')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "xGvOcLQKghXN" + }, + "source": [ + "## Caption!\n", + "\n", + "* The evaluate function is similar to the training loop, except we don't use teacher forcing here. The input to the decoder at each time step is its previous predictions along with the hidden state and the encoder output.\n", + "* Stop predicting when the model predicts the end token.\n", + "* And store the attention weights for every time step." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "io7ws3ReRPGv", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "# captions on the validation set\n", - "rid = np.random.randint(0, len(img_name_val))\n", - "image = img_name_val[rid]\n", - "real_caption = ' '.join([index_word[i] for i in cap_val[rid] if i not in [0]])\n", - "result, attention_plot = evaluate(image)\n", - "\n", - "print ('Real Caption:', real_caption)\n", - "print ('Prediction Caption:', ' '.join(result))\n", - "plot_attention(image, result, attention_plot)\n", - "# opening the image\n", - "Image.open(img_name_val[rid])" - ], - "execution_count": 0, - "outputs": [] + "colab_type": "code", + "id": "RCWpDtyNRPGs" + }, + "outputs": [], + "source": [ + "def evaluate(image):\n", + " attention_plot = np.zeros((max_length, attention_features_shape))\n", + "\n", + " hidden = decoder.reset_state(batch_size=1)\n", + "\n", + " temp_input = tf.expand_dims(load_image(image)[0], 0)\n", + " img_tensor_val = image_features_extract_model(temp_input)\n", + " img_tensor_val = tf.reshape(img_tensor_val, (img_tensor_val.shape[0], -1, img_tensor_val.shape[3]))\n", + "\n", + " features = encoder(img_tensor_val)\n", + "\n", + " dec_input = tf.expand_dims([tokenizer.word_index['']], 0)\n", + " result = []\n", + "\n", + " for i in range(max_length):\n", + " predictions, hidden, attention_weights = decoder(dec_input, features, hidden)\n", + "\n", + " attention_plot[i] = tf.reshape(attention_weights, (-1, )).numpy()\n", + "\n", + " predicted_id = tf.argmax(predictions[0]).numpy()\n", + " result.append(tokenizer.index_word[predicted_id])\n", + "\n", + " if tokenizer.index_word[predicted_id] == '':\n", + " return result, attention_plot\n", + "\n", + " dec_input = tf.expand_dims([predicted_id], 0)\n", + "\n", + " attention_plot = attention_plot[:len(result), :]\n", + " return result, attention_plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "Rprk3HEvZuxb", - "colab_type": "text" - }, - "cell_type": "markdown", - "source": [ - "## Try it on your own images\n", - "For fun, below we've provided a method you can use to caption your own images with the model we've just trained. Keep in mind, it was trained on a relatively small amount of data, and your images may be different from the training data (so be prepared for weird results!)\n" - ] + "colab_type": "code", + "id": "fD_y7PD6RPGt" + }, + "outputs": [], + "source": [ + "def plot_attention(image, result, attention_plot):\n", + " temp_image = np.array(Image.open(image))\n", + "\n", + " fig = plt.figure(figsize=(10, 10))\n", + " \n", + " len_result = len(result)\n", + " for l in range(len_result):\n", + " temp_att = np.resize(attention_plot[l], (8, 8))\n", + " ax = fig.add_subplot(len_result//2, len_result//2, l+1)\n", + " ax.set_title(result[l])\n", + " img = ax.imshow(temp_image)\n", + " ax.imshow(temp_att, cmap='gray', alpha=0.6, extent=img.get_extent())\n", + "\n", + " plt.tight_layout()\n", + " plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, - { - "metadata": { - "id": "9Psd1quzaAWg", - "colab_type": "code", - "colab": { - "autoexec": { - "startup": false, - "wait_interval": 0 - } - } - }, - "cell_type": "code", - "source": [ - "image_url = 'https://tensorflow.org/images/surf.jpg'\n", - "image_extension = image_url[-4:]\n", - "image_path = tf.keras.utils.get_file('image'+image_extension, \n", - " origin=image_url)\n", - "\n", - "result, attention_plot = evaluate(image_path)\n", - "print ('Prediction Caption:', ' '.join(result))\n", - "plot_attention(image_path, result, attention_plot)\n", - "# opening the image\n", - "Image.open(image_path)" - ], - "execution_count": 0, - "outputs": [] + "colab_type": "code", + "id": "io7ws3ReRPGv" + }, + "outputs": [], + "source": [ + "# captions on the validation set\n", + "rid = np.random.randint(0, len(img_name_val))\n", + "image = img_name_val[rid]\n", + "real_caption = ' '.join([tokenizer.index_word[i] for i in cap_val[rid] if i not in [0]])\n", + "result, attention_plot = evaluate(image)\n", + "\n", + "print ('Real Caption:', real_caption)\n", + "print ('Prediction Caption:', ' '.join(result))\n", + "plot_attention(image, result, attention_plot)\n", + "# opening the image\n", + "Image.open(img_name_val[rid])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Rprk3HEvZuxb" + }, + "source": [ + "## Try it on your own images\n", + "For fun, below we've provided a method you can use to caption your own images with the model we've just trained. Keep in mind, it was trained on a relatively small amount of data, and your images may be different from the training data (so be prepared for weird results!)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "autoexec": { + "startup": false, + "wait_interval": 0 + } }, + "colab_type": "code", + "id": "9Psd1quzaAWg" + }, + "outputs": [], + "source": [ + "image_url = 'https://tensorflow.org/images/surf.jpg'\n", + "image_extension = image_url[-4:]\n", + "image_path = tf.keras.utils.get_file('image'+image_extension, \n", + " origin=image_url)\n", + "\n", + "result, attention_plot = evaluate(image_path)\n", + "print ('Prediction Caption:', ' '.join(result))\n", + "plot_attention(image_path, result, attention_plot)\n", + "# opening the image\n", + "Image.open(image_path)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "VJZXyJco6uLO" + }, + "source": [ + "# Next steps\n", + "\n", + "Congrats! You've just trained an image captioning model with attention. Next, we recommend taking a look at this example [Neural Machine Translation with Attention]( https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/eager/python/examples/nmt_with_attention/nmt_with_attention.ipynb). It uses a similar architecture to translate between Spanish and English sentences. You can also experiment with training the code in this notebook on a different dataset." + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "collapsed_sections": [], + "default_view": {}, + "name": "image_captioning_with_attention.ipynb", + "private_outputs": true, + "provenance": [ { - "metadata": { - "id": "VJZXyJco6uLO", - "colab_type": "text" - }, - "cell_type": "markdown", - "source": [ - "# Next steps\n", - "\n", - "Congrats! You've just trained an image captioning model with attention. Next, we recommend taking a look at this example [Neural Machine Translation with Attention]( https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/eager/python/examples/nmt_with_attention/nmt_with_attention.ipynb). It uses a similar architecture to translate between Spanish and English sentences. You can also experiment with training the code in this notebook on a different dataset." - ] + "file_id": "1HI8OK2sMjcx9CTWVn0122QAHOuXaOaMg", + "timestamp": 1530222436922 } - ] + ], + "toc_visible": true, + "version": "0.3.2", + "views": {} + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 } diff --git a/tensorflow/contrib/eager/python/examples/linear_regression/linear_regression_graph_test.py b/tensorflow/contrib/eager/python/examples/linear_regression/linear_regression_graph_test.py index 557ad42752..d412b25b36 100644 --- a/tensorflow/contrib/eager/python/examples/linear_regression/linear_regression_graph_test.py +++ b/tensorflow/contrib/eager/python/examples/linear_regression/linear_regression_graph_test.py @@ -36,7 +36,7 @@ class GraphLinearRegressionBenchmark(tf.test.Benchmark): noise_level=0.01, batch_size=batch_size, num_batches=num_batches) - iterator = dataset.make_initializable_iterator() + iterator = tf.compat.v1.data.make_initializable_iterator(dataset) x, y = iterator.get_next() model = linear_regression.LinearModel() diff --git a/tensorflow/contrib/eager/python/examples/nmt_with_attention/nmt_with_attention.ipynb b/tensorflow/contrib/eager/python/examples/nmt_with_attention/nmt_with_attention.ipynb index 480777d948..66d52a7494 100644 --- a/tensorflow/contrib/eager/python/examples/nmt_with_attention/nmt_with_attention.ipynb +++ b/tensorflow/contrib/eager/python/examples/nmt_with_attention/nmt_with_attention.ipynb @@ -768,7 +768,7 @@ }, "outputs": [], "source": [ - "translate('hace mucho frio aqui.', encoder, decoder, inp_lang, targ_lang, max_length_inp, max_length_targ)" + "translate(u'hace mucho frio aqui.', encoder, decoder, inp_lang, targ_lang, max_length_inp, max_length_targ)" ] }, { @@ -781,7 +781,7 @@ }, "outputs": [], "source": [ - "translate('esta es mi vida.', encoder, decoder, inp_lang, targ_lang, max_length_inp, max_length_targ)" + "translate(u'esta es mi vida.', encoder, decoder, inp_lang, targ_lang, max_length_inp, max_length_targ)" ] }, { @@ -794,7 +794,7 @@ }, "outputs": [], "source": [ - "translate('¿todavia estan en casa?', encoder, decoder, inp_lang, targ_lang, max_length_inp, max_length_targ)" + "translate(u'todavia estan en casa?', encoder, decoder, inp_lang, targ_lang, max_length_inp, max_length_targ)" ] }, { @@ -808,7 +808,7 @@ "outputs": [], "source": [ "# wrong translation\n", - "translate('trata de averiguarlo.', encoder, decoder, inp_lang, targ_lang, max_length_inp, max_length_targ)" + "translate(u'trata de averiguarlo.', encoder, decoder, inp_lang, targ_lang, max_length_inp, max_length_targ)" ] }, { diff --git a/tensorflow/contrib/eager/python/examples/resnet50/resnet50_graph_test.py b/tensorflow/contrib/eager/python/examples/resnet50/resnet50_graph_test.py index f3bb978875..fb7975d8fe 100644 --- a/tensorflow/contrib/eager/python/examples/resnet50/resnet50_graph_test.py +++ b/tensorflow/contrib/eager/python/examples/resnet50/resnet50_graph_test.py @@ -142,7 +142,8 @@ class ResNet50Benchmarks(tf.test.Benchmark): with tf.Graph().as_default(): np_images, np_labels = random_batch(batch_size) dataset = tf.data.Dataset.from_tensors((np_images, np_labels)).repeat() - (images, labels) = dataset.make_one_shot_iterator().get_next() + images, labels = tf.compat.v1.data.make_one_shot_iterator( + dataset).get_next() model = resnet50.ResNet50(data_format()) logits = model(images, training=True) diff --git a/tensorflow/contrib/eager/python/examples/revnet/main.py b/tensorflow/contrib/eager/python/examples/revnet/main.py index b702e91f92..9585f3565f 100644 --- a/tensorflow/contrib/eager/python/examples/revnet/main.py +++ b/tensorflow/contrib/eager/python/examples/revnet/main.py @@ -72,14 +72,11 @@ def main(_): train_one_iter(model, x, y, optimizer, global_step=global_step) if global_step.numpy() % config.log_every == 0: - it_test = ds_test.make_one_shot_iterator() - acc_test, loss_test = evaluate(model, it_test) + acc_test, loss_test = evaluate(model, ds_test) if FLAGS.validate: - it_train = ds_train_one_shot.make_one_shot_iterator() - it_validation = ds_validation.make_one_shot_iterator() - acc_train, loss_train = evaluate(model, it_train) - acc_validation, loss_validation = evaluate(model, it_validation) + acc_train, loss_train = evaluate(model, ds_train_one_shot) + acc_validation, loss_validation = evaluate(model, ds_validation) print("Iter {}, " "training set accuracy {:.4f}, loss {:.4f}; " "validation set accuracy {:.4f}, loss {:.4f}; " @@ -218,11 +215,11 @@ def train_one_iter(model, inputs, labels, optimizer, global_step=None): return logits, loss -def evaluate(model, iterator): +def evaluate(model, dataset): """Compute accuracy with the given dataset iterator.""" mean_loss = tfe.metrics.Mean() accuracy = tfe.metrics.Accuracy() - for x, y in iterator: + for x, y in dataset: logits, _ = model(x, training=False) loss = model.compute_loss(logits=logits, labels=y) accuracy( diff --git a/tensorflow/contrib/eager/python/examples/rnn_ptb/rnn_ptb_graph_test.py b/tensorflow/contrib/eager/python/examples/rnn_ptb/rnn_ptb_graph_test.py index 63b5c4c54d..770484abed 100644 --- a/tensorflow/contrib/eager/python/examples/rnn_ptb/rnn_ptb_graph_test.py +++ b/tensorflow/contrib/eager/python/examples/rnn_ptb/rnn_ptb_graph_test.py @@ -82,7 +82,7 @@ class PTBBenchmark(tf.test.Benchmark): tf.ones( [PTBBenchmark.SEQ_LEN, PTBBenchmark.BATCH_SIZE], dtype=tf.int64)).repeat(num_iters + num_warmup) - inputs = dataset.make_one_shot_iterator().get_next() + inputs = tf.compat.v1.data.make_one_shot_iterator(dataset).get_next() with tf.device(tf.test.gpu_device_name()): outputs = model(inputs, training=True) @@ -124,7 +124,8 @@ class PTBBenchmark(tf.test.Benchmark): dtype=tf.int64)).repeat(num_iters + num_warmup) # inputs and labels have the same shape dataset = tf.data.Dataset.zip((dataset, dataset)) - (inputs, labels) = dataset.make_one_shot_iterator().get_next() + (inputs, labels) = tf.compat.v1.data.make_one_shot_iterator( + dataset).get_next() with tf.device(tf.test.gpu_device_name()): optimizer = tf.train.GradientDescentOptimizer(learning_rate=1.0) diff --git a/tensorflow/contrib/eager/python/metrics_impl.py b/tensorflow/contrib/eager/python/metrics_impl.py index c88c0f52ee..566246de49 100644 --- a/tensorflow/contrib/eager/python/metrics_impl.py +++ b/tensorflow/contrib/eager/python/metrics_impl.py @@ -24,6 +24,7 @@ from tensorflow.python.eager import context from tensorflow.python.eager import function from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import smart_cond from tensorflow.python.ops import array_ops from tensorflow.python.ops import check_ops from tensorflow.python.ops import control_flow_ops @@ -354,9 +355,10 @@ class Mean(Metric): def write_summary_f(): summary_ops.scalar(name=self.name, tensor=t) return t - control_flow_ops.cond(write_summary, + smart_cond.smart_cond(write_summary, write_summary_f, - lambda: t) + lambda: t, + name="") return t diff --git a/tensorflow/contrib/eager/python/metrics_test.py b/tensorflow/contrib/eager/python/metrics_test.py index 9d2d172752..39e5957f5d 100644 --- a/tensorflow/contrib/eager/python/metrics_test.py +++ b/tensorflow/contrib/eager/python/metrics_test.py @@ -49,18 +49,6 @@ class MetricsTest(test.TestCase): self.assertEqual(dtypes.float64, m.dtype) self.assertEqual(dtypes.float64, m.result().dtype) - def testSummaryArg(self): - m = metrics.Mean() - m([1, 10, 100]) - m(1000) - m([10000.0, 100000.0]) - self.assertEqual(111111.0/6, m.result(write_summary=True).numpy()) - self.assertEqual(111111.0/6, m.result(write_summary=False).numpy()) - with self.assertRaises(ValueError): - m.result(write_summary=5) - with self.assertRaises(ValueError): - m.result(write_summary=[True]) - def testVariableCollections(self): with context.graph_mode(), ops.Graph().as_default(): m = metrics.Mean() diff --git a/tensorflow/contrib/eager/python/network.py b/tensorflow/contrib/eager/python/network.py index f801d9a47b..5cc0c4f23d 100644 --- a/tensorflow/contrib/eager/python/network.py +++ b/tensorflow/contrib/eager/python/network.py @@ -24,7 +24,7 @@ import weakref from tensorflow.python.eager import context from tensorflow.python.framework import ops -from tensorflow.python.keras.engine import base_layer as keras_base_layer +from tensorflow.python.keras.engine import base_layer_utils from tensorflow.python.layers import base from tensorflow.python.ops import variable_scope from tensorflow.python.platform import tf_logging as logging @@ -220,7 +220,7 @@ class Network(base.Layer): avoid_names = parent_network._owned_layers name_uid_map = parent_network._sub_layer_name_uids else: - name_uid_map = keras_base_layer.get_default_graph_uid_map() + name_uid_map = base_layer_utils.get_default_graph_uid_map() # Figure out which names we have to avoid based on which variable scope # we're nested in. strip_name = self._default_parent_variable_scope.name diff --git a/tensorflow/contrib/eager/python/saver.py b/tensorflow/contrib/eager/python/saver.py index f9c716360c..1d0d6c6c14 100644 --- a/tensorflow/contrib/eager/python/saver.py +++ b/tensorflow/contrib/eager/python/saver.py @@ -115,6 +115,11 @@ def restore_variables_on_create(save_path, map_func=None): class Saver(object): """A tf.train.Saver adapter for use when eager execution is enabled. + + `Saver`'s name-based checkpointing strategy is fragile. Please switch to + `tf.train.Checkpoint` or `tf.keras.Model.save_weights`, which perform a more + robust object-based saving. These APIs will load checkpoints written by + `Saver`. """ def __init__(self, var_list): diff --git a/tensorflow/contrib/eager/python/tfe_test.py b/tensorflow/contrib/eager/python/tfe_test.py index 4454abfb96..8c35dddb5a 100644 --- a/tensorflow/contrib/eager/python/tfe_test.py +++ b/tensorflow/contrib/eager/python/tfe_test.py @@ -87,8 +87,8 @@ class TFETest(test_util.TensorFlowTestCase): x += 1. # Without a device context, heuristics are used to place ops. # In this case, ops.reduce_mean runs on the GPU. - reduction_indices = range(x.shape.ndims) - m = math_ops.reduce_mean(x, reduction_indices) + axis = range(x.shape.ndims) + m = math_ops.reduce_mean(x, axis) # m is on GPU, bring it back to CPU and compare. self.assertEqual(3.5, m.cpu().numpy()) diff --git a/tensorflow/contrib/estimator/BUILD b/tensorflow/contrib/estimator/BUILD index 37f253d9c1..a888379f13 100644 --- a/tensorflow/contrib/estimator/BUILD +++ b/tensorflow/contrib/estimator/BUILD @@ -16,7 +16,6 @@ py_library( srcs_version = "PY2AND3", deps = [ ":boosted_trees", - ":dnn", ":dnn_with_layer_annotations", ":early_stopping", ":expect_tensorflow_estimator_installed", @@ -25,7 +24,6 @@ py_library( ":extenders", ":head", ":hooks", - ":linear", ":logit_fns", ":multi_head", ":replicate_model_fn", @@ -47,18 +45,6 @@ py_library( ], ) -py_library( - name = "dnn", - srcs = ["python/estimator/dnn.py"], - srcs_version = "PY2AND3", - deps = [ - ":expect_tensorflow_estimator_installed", - "//tensorflow:tensorflow_py_no_contrib", - "//tensorflow/python/estimator", - "//tensorflow/python/estimator:dnn", - ], -) - py_library( name = "dnn_with_layer_annotations", srcs = ["python/estimator/dnn_with_layer_annotations.py"], @@ -144,17 +130,6 @@ py_library( ], ) -py_library( - name = "linear", - srcs = ["python/estimator/linear.py"], - srcs_version = "PY2AND3", - deps = [ - ":expect_tensorflow_estimator_installed", - "//tensorflow/python/estimator", - "//tensorflow/python/estimator:linear", - ], -) - py_library( name = "logit_fns", srcs = [ diff --git a/tensorflow/contrib/estimator/__init__.py b/tensorflow/contrib/estimator/__init__.py index 80d5962762..7d61247e7e 100644 --- a/tensorflow/contrib/estimator/__init__.py +++ b/tensorflow/contrib/estimator/__init__.py @@ -58,8 +58,6 @@ _allowed_symbols = [ 'multi_label_head', 'poisson_regression_head', 'regression_head', - 'DNNEstimator', - 'LinearEstimator', 'boosted_trees_classifier_train_in_memory', 'boosted_trees_regressor_train_in_memory', 'call_logit_fn', diff --git a/tensorflow/contrib/estimator/python/estimator/dnn_linear_combined.py b/tensorflow/contrib/estimator/python/estimator/dnn_linear_combined.py deleted file mode 100644 index 7894418c4a..0000000000 --- a/tensorflow/contrib/estimator/python/estimator/dnn_linear_combined.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2018 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. -# ============================================================================== -"""dnn_linear_combined python module. - -Importing from tensorflow.python.estimator is unsupported -and will soon break! -""" -# pylint: disable=unused-import,g-bad-import-order,g-import-not-at-top,wildcard-import - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -from tensorflow_estimator.contrib.estimator.python.estimator import dnn_linear_combined - -# Include attrs that start with single underscore. -_HAS_DYNAMIC_ATTRIBUTES = True -dnn_linear_combined.__all__ = [ - s for s in dir(dnn_linear_combined) if not s.startswith('__') -] - -from tensorflow_estimator.contrib.estimator.python.estimator.dnn_linear_combined import * diff --git a/tensorflow/contrib/factorization/python/ops/kmeans.py b/tensorflow/contrib/factorization/python/ops/kmeans.py index f384d761a8..3eb396a29c 100644 --- a/tensorflow/contrib/factorization/python/ops/kmeans.py +++ b/tensorflow/contrib/factorization/python/ops/kmeans.py @@ -26,7 +26,7 @@ from tensorflow.contrib.factorization.python.ops import clustering_ops from tensorflow.python.estimator import estimator from tensorflow.python.estimator import model_fn as model_fn_lib from tensorflow.python.estimator.export import export_output -from tensorflow.python.feature_column import feature_column as fc +from tensorflow.python.feature_column import feature_column_lib as fc from tensorflow.python.framework import ops from tensorflow.python.ops import array_ops from tensorflow.python.ops import control_flow_ops diff --git a/tensorflow/contrib/factorization/python/ops/kmeans_test.py b/tensorflow/contrib/factorization/python/ops/kmeans_test.py index 1ab5418fe4..2f7cd131d3 100644 --- a/tensorflow/contrib/factorization/python/ops/kmeans_test.py +++ b/tensorflow/contrib/factorization/python/ops/kmeans_test.py @@ -27,7 +27,7 @@ from sklearn.cluster import KMeans as SklearnKMeans # pylint: disable=g-import-not-at-top from tensorflow.contrib.factorization.python.ops import kmeans as kmeans_lib from tensorflow.python.estimator import run_config -from tensorflow.python.feature_column import feature_column as fc +from tensorflow.python.feature_column import feature_column_lib as fc from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops diff --git a/tensorflow/contrib/feature_column/BUILD b/tensorflow/contrib/feature_column/BUILD index bbe335be3e..1cd83bdb5d 100644 --- a/tensorflow/contrib/feature_column/BUILD +++ b/tensorflow/contrib/feature_column/BUILD @@ -14,6 +14,7 @@ py_library( srcs_version = "PY2AND3", deps = [ ":sequence_feature_column", + ":sequence_feature_column_v2", "//tensorflow/python:util", ], ) @@ -32,7 +33,7 @@ py_library( "//tensorflow/python:sparse_ops", "//tensorflow/python:tensor_shape", "//tensorflow/python:variable_scope", - "//tensorflow/python/feature_column", + "//tensorflow/python/feature_column:feature_column_py", ], ) @@ -51,7 +52,7 @@ py_test( "//tensorflow/python:parsing_ops", "//tensorflow/python:sparse_tensor", "//tensorflow/python:training", - "//tensorflow/python/feature_column", + "//tensorflow/python/feature_column:feature_column_py", "//third_party/py/numpy", "@absl_py//absl/testing:parameterized", ], @@ -69,7 +70,7 @@ py_test( "//tensorflow/python:parsing_ops", "//tensorflow/python:training", "//tensorflow/python:util", - "//tensorflow/python/feature_column", + "//tensorflow/python/feature_column:feature_column_py", "//tensorflow/python/keras:layers", ], ) @@ -89,7 +90,7 @@ py_library( "//tensorflow/python:tensor_shape", "//tensorflow/python:variable_scope", "//tensorflow/python/feature_column", - "//tensorflow/python/feature_column:feature_column_v2", + "//tensorflow/python/feature_column:feature_column_py", ], ) @@ -110,7 +111,7 @@ py_test( "//tensorflow/python:sparse_tensor", "//tensorflow/python:training", "//tensorflow/python/feature_column", - "//tensorflow/python/feature_column:feature_column_v2", + "//tensorflow/python/feature_column:feature_column_py", "//third_party/py/numpy", "@absl_py//absl/testing:parameterized", ], diff --git a/tensorflow/contrib/feature_column/python/feature_column/sequence_feature_column.py b/tensorflow/contrib/feature_column/python/feature_column/sequence_feature_column.py index dd6da35ed0..9b3a5c58aa 100644 --- a/tensorflow/contrib/feature_column/python/feature_column/sequence_feature_column.py +++ b/tensorflow/contrib/feature_column/python/feature_column/sequence_feature_column.py @@ -222,10 +222,8 @@ def sequence_categorical_column_with_identity( ValueError: if `default_value` is not in range `[0, num_buckets)`. """ return fc._SequenceCategoricalColumn( - fc.categorical_column_with_identity( - key=key, - num_buckets=num_buckets, - default_value=default_value)) + fc._categorical_column_with_identity( + key=key, num_buckets=num_buckets, default_value=default_value)) def sequence_categorical_column_with_hash_bucket( @@ -265,10 +263,8 @@ def sequence_categorical_column_with_hash_bucket( ValueError: `dtype` is neither string nor integer. """ return fc._SequenceCategoricalColumn( - fc.categorical_column_with_hash_bucket( - key=key, - hash_bucket_size=hash_bucket_size, - dtype=dtype)) + fc._categorical_column_with_hash_bucket( + key=key, hash_bucket_size=hash_bucket_size, dtype=dtype)) def sequence_categorical_column_with_vocabulary_file( @@ -324,7 +320,7 @@ def sequence_categorical_column_with_vocabulary_file( ValueError: `dtype` is neither string nor integer. """ return fc._SequenceCategoricalColumn( - fc.categorical_column_with_vocabulary_file( + fc._categorical_column_with_vocabulary_file( key=key, vocabulary_file=vocabulary_file, vocabulary_size=vocabulary_size, @@ -384,7 +380,7 @@ def sequence_categorical_column_with_vocabulary_list( ValueError: if `dtype` is not integer or string. """ return fc._SequenceCategoricalColumn( - fc.categorical_column_with_vocabulary_list( + fc._categorical_column_with_vocabulary_list( key=key, vocabulary_list=vocabulary_list, dtype=dtype, diff --git a/tensorflow/contrib/feature_column/python/feature_column/sequence_feature_column_integration_test.py b/tensorflow/contrib/feature_column/python/feature_column/sequence_feature_column_integration_test.py index d8ca363627..bcc25b8de8 100644 --- a/tensorflow/contrib/feature_column/python/feature_column/sequence_feature_column_integration_test.py +++ b/tensorflow/contrib/feature_column/python/feature_column/sequence_feature_column_integration_test.py @@ -53,19 +53,20 @@ class SequenceFeatureColumnIntegrationTest(test.TestCase): return example def _build_feature_columns(self): - col = fc.categorical_column_with_identity( - 'int_ctx', num_buckets=100) + col = fc._categorical_column_with_identity('int_ctx', num_buckets=100) ctx_cols = [ - fc.embedding_column(col, dimension=10), - fc.numeric_column('float_ctx')] + fc._embedding_column(col, dimension=10), + fc._numeric_column('float_ctx') + ] identity_col = sfc.sequence_categorical_column_with_identity( 'int_list', num_buckets=10) bucket_col = sfc.sequence_categorical_column_with_hash_bucket( 'bytes_list', hash_bucket_size=100) seq_cols = [ - fc.embedding_column(identity_col, dimension=10), - fc.embedding_column(bucket_col, dimension=20)] + fc._embedding_column(identity_col, dimension=10), + fc._embedding_column(bucket_col, dimension=20) + ] return ctx_cols, seq_cols @@ -148,8 +149,8 @@ class SequenceExampleParsingTest(test.TestCase): """ example = _make_sequence_example() columns = [ - fc.categorical_column_with_identity('int_ctx', num_buckets=100), - fc.numeric_column('float_ctx'), + fc._categorical_column_with_identity('int_ctx', num_buckets=100), + fc._numeric_column('float_ctx'), col_fn(col_name, col_arg) ] context, seq_features = parsing_ops.parse_single_sequence_example( diff --git a/tensorflow/contrib/feature_column/python/feature_column/sequence_feature_column_test.py b/tensorflow/contrib/feature_column/python/feature_column/sequence_feature_column_test.py index 2163af0b43..d5f7402829 100644 --- a/tensorflow/contrib/feature_column/python/feature_column/sequence_feature_column_test.py +++ b/tensorflow/contrib/feature_column/python/feature_column/sequence_feature_column_test.py @@ -24,6 +24,7 @@ import numpy as np from tensorflow.contrib.feature_column.python.feature_column import sequence_feature_column as sfc from tensorflow.python.feature_column import feature_column as fc +from tensorflow.python.feature_column import feature_column_lib as fc_lib from tensorflow.python.feature_column.feature_column import _LazyBuilder from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors @@ -109,13 +110,15 @@ class SequenceInputLayerTest(test.TestCase, parameterized.TestCase): categorical_column_a = sfc.sequence_categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - embedding_column_a = fc.embedding_column( - categorical_column_a, dimension=embedding_dimension_a, + embedding_column_a = fc._embedding_column( + categorical_column_a, + dimension=embedding_dimension_a, initializer=_get_initializer(embedding_dimension_a, embedding_values_a)) categorical_column_b = sfc.sequence_categorical_column_with_identity( key='bbb', num_buckets=vocabulary_size) - embedding_column_b = fc.embedding_column( - categorical_column_b, dimension=embedding_dimension_b, + embedding_column_b = fc._embedding_column( + categorical_column_b, + dimension=embedding_dimension_b, initializer=_get_initializer(embedding_dimension_b, embedding_values_b)) input_layer, sequence_length = sfc.sequence_input_layer( @@ -148,10 +151,9 @@ class SequenceInputLayerTest(test.TestCase, parameterized.TestCase): values=(2, 0, 1), dense_shape=(2, 2)) - categorical_column_a = fc.categorical_column_with_identity( + categorical_column_a = fc._categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - embedding_column_a = fc.embedding_column( - categorical_column_a, dimension=2) + embedding_column_a = fc._embedding_column(categorical_column_a, dimension=2) with self.assertRaisesRegexp( ValueError, @@ -206,7 +208,7 @@ class SequenceInputLayerTest(test.TestCase, parameterized.TestCase): categorical_column_b = sfc.sequence_categorical_column_with_identity( key='bbb', num_buckets=vocabulary_size) # Test that columns are reordered alphabetically. - shared_embedding_columns = fc.shared_embedding_columns( + shared_embedding_columns = fc_lib.shared_embedding_columns( [categorical_column_b, categorical_column_a], dimension=embedding_dimension, initializer=_get_initializer(embedding_dimension, embedding_values)) @@ -244,11 +246,11 @@ class SequenceInputLayerTest(test.TestCase, parameterized.TestCase): values=(2, 0, 1), dense_shape=(2, 2)) - categorical_column_a = fc.categorical_column_with_identity( + categorical_column_a = fc._categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - categorical_column_b = fc.categorical_column_with_identity( + categorical_column_b = fc._categorical_column_with_identity( key='bbb', num_buckets=vocabulary_size) - shared_embedding_columns = fc.shared_embedding_columns( + shared_embedding_columns = fc_lib.shared_embedding_columns( [categorical_column_a, categorical_column_b], dimension=2) with self.assertRaisesRegexp( @@ -315,10 +317,10 @@ class SequenceInputLayerTest(test.TestCase, parameterized.TestCase): categorical_column_a = sfc.sequence_categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size_a) - indicator_column_a = fc.indicator_column(categorical_column_a) + indicator_column_a = fc._indicator_column(categorical_column_a) categorical_column_b = sfc.sequence_categorical_column_with_identity( key='bbb', num_buckets=vocabulary_size_b) - indicator_column_b = fc.indicator_column(categorical_column_b) + indicator_column_b = fc._indicator_column(categorical_column_b) input_layer, sequence_length = sfc.sequence_input_layer( features={ 'aaa': sparse_input_a, @@ -342,9 +344,9 @@ class SequenceInputLayerTest(test.TestCase, parameterized.TestCase): values=(2, 0, 1), dense_shape=(2, 2)) - categorical_column_a = fc.categorical_column_with_identity( + categorical_column_a = fc._categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - indicator_column_a = fc.indicator_column(categorical_column_a) + indicator_column_a = fc._indicator_column(categorical_column_a) with self.assertRaisesRegexp( ValueError, @@ -530,7 +532,7 @@ class SequenceInputLayerTest(test.TestCase, parameterized.TestCase): sparse_input = sparse_tensor.SparseTensorValue(**sparse_input_args) categorical_column = sfc.sequence_categorical_column_with_identity( key='aaa', num_buckets=3) - indicator_column = fc.indicator_column(categorical_column) + indicator_column = fc._indicator_column(categorical_column) input_layer, _ = sfc.sequence_input_layer( features={'aaa': sparse_input}, feature_columns=[indicator_column]) @@ -616,8 +618,7 @@ class InputLayerTest(test.TestCase): categorical_column_a = sfc.sequence_categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - embedding_column_a = fc.embedding_column( - categorical_column_a, dimension=2) + embedding_column_a = fc._embedding_column(categorical_column_a, dimension=2) with self.assertRaisesRegexp( ValueError, @@ -639,7 +640,7 @@ class InputLayerTest(test.TestCase): categorical_column_a = sfc.sequence_categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - indicator_column_a = fc.indicator_column(categorical_column_a) + indicator_column_a = fc._indicator_column(categorical_column_a) with self.assertRaisesRegexp( ValueError, @@ -918,8 +919,9 @@ class SequenceEmbeddingColumnTest( categorical_column = sfc.sequence_categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - embedding_column = fc.embedding_column( - categorical_column, dimension=embedding_dimension, + embedding_column = fc._embedding_column( + categorical_column, + dimension=embedding_dimension, initializer=_initializer) embedding_lookup, _ = embedding_column._get_sequence_dense_tensor( @@ -956,8 +958,7 @@ class SequenceEmbeddingColumnTest( categorical_column = sfc.sequence_categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - embedding_column = fc.embedding_column( - categorical_column, dimension=2) + embedding_column = fc._embedding_column(categorical_column, dimension=2) _, sequence_length = embedding_column._get_sequence_dense_tensor( _LazyBuilder({'aaa': inputs})) @@ -984,8 +985,7 @@ class SequenceEmbeddingColumnTest( categorical_column = sfc.sequence_categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - embedding_column = fc.embedding_column( - categorical_column, dimension=2) + embedding_column = fc._embedding_column(categorical_column, dimension=2) _, sequence_length = embedding_column._get_sequence_dense_tensor( _LazyBuilder({'aaa': sparse_input})) @@ -1055,7 +1055,7 @@ class SequenceSharedEmbeddingColumnTest(test.TestCase): key='aaa', num_buckets=vocabulary_size) categorical_column_b = sfc.sequence_categorical_column_with_identity( key='bbb', num_buckets=vocabulary_size) - shared_embedding_columns = fc.shared_embedding_columns( + shared_embedding_columns = fc_lib.shared_embedding_columns( [categorical_column_a, categorical_column_b], dimension=embedding_dimension, initializer=_initializer) @@ -1101,7 +1101,7 @@ class SequenceSharedEmbeddingColumnTest(test.TestCase): expected_sequence_length_b = [2, 1] categorical_column_b = sfc.sequence_categorical_column_with_identity( key='bbb', num_buckets=vocabulary_size) - shared_embedding_columns = fc.shared_embedding_columns( + shared_embedding_columns = fc_lib.shared_embedding_columns( [categorical_column_a, categorical_column_b], dimension=2) sequence_length_a = shared_embedding_columns[0]._get_sequence_dense_tensor( @@ -1152,7 +1152,7 @@ class SequenceSharedEmbeddingColumnTest(test.TestCase): categorical_column_b = sfc.sequence_categorical_column_with_identity( key='bbb', num_buckets=vocabulary_size) - shared_embedding_columns = fc.shared_embedding_columns( + shared_embedding_columns = fc_lib.shared_embedding_columns( [categorical_column_a, categorical_column_b], dimension=2) sequence_length_a = shared_embedding_columns[0]._get_sequence_dense_tensor( @@ -1218,7 +1218,7 @@ class SequenceIndicatorColumnTest(test.TestCase, parameterized.TestCase): categorical_column = sfc.sequence_categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - indicator_column = fc.indicator_column(categorical_column) + indicator_column = fc._indicator_column(categorical_column) indicator_tensor, _ = indicator_column._get_sequence_dense_tensor( _LazyBuilder({'aaa': inputs})) @@ -1250,7 +1250,7 @@ class SequenceIndicatorColumnTest(test.TestCase, parameterized.TestCase): categorical_column = sfc.sequence_categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - indicator_column = fc.indicator_column(categorical_column) + indicator_column = fc._indicator_column(categorical_column) _, sequence_length = indicator_column._get_sequence_dense_tensor( _LazyBuilder({'aaa': inputs})) @@ -1277,7 +1277,7 @@ class SequenceIndicatorColumnTest(test.TestCase, parameterized.TestCase): categorical_column = sfc.sequence_categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - indicator_column = fc.indicator_column(categorical_column) + indicator_column = fc._indicator_column(categorical_column) _, sequence_length = indicator_column._get_sequence_dense_tensor( _LazyBuilder({'aaa': sparse_input})) diff --git a/tensorflow/contrib/feature_column/python/feature_column/sequence_feature_column_v2.py b/tensorflow/contrib/feature_column/python/feature_column/sequence_feature_column_v2.py index 67ffb93966..0d34ad1618 100644 --- a/tensorflow/contrib/feature_column/python/feature_column/sequence_feature_column_v2.py +++ b/tensorflow/contrib/feature_column/python/feature_column/sequence_feature_column_v2.py @@ -26,7 +26,7 @@ import collections from tensorflow.python.feature_column import feature_column as fc_old -from tensorflow.python.feature_column import feature_column_v2 as fc +from tensorflow.python.feature_column import feature_column_lib as fc from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape @@ -226,10 +226,8 @@ def sequence_categorical_column_with_identity( ValueError: if `default_value` is not in range `[0, num_buckets)`. """ return fc_old._SequenceCategoricalColumn( - fc_old.categorical_column_with_identity( - key=key, - num_buckets=num_buckets, - default_value=default_value)) + fc_old._categorical_column_with_identity( + key=key, num_buckets=num_buckets, default_value=default_value)) def sequence_categorical_column_with_hash_bucket( @@ -269,10 +267,8 @@ def sequence_categorical_column_with_hash_bucket( ValueError: `dtype` is neither string nor integer. """ return fc_old._SequenceCategoricalColumn( - fc_old.categorical_column_with_hash_bucket( - key=key, - hash_bucket_size=hash_bucket_size, - dtype=dtype)) + fc_old._categorical_column_with_hash_bucket( + key=key, hash_bucket_size=hash_bucket_size, dtype=dtype)) def sequence_categorical_column_with_vocabulary_file( @@ -328,7 +324,7 @@ def sequence_categorical_column_with_vocabulary_file( ValueError: `dtype` is neither string nor integer. """ return fc_old._SequenceCategoricalColumn( - fc_old.categorical_column_with_vocabulary_file( + fc_old._categorical_column_with_vocabulary_file( key=key, vocabulary_file=vocabulary_file, vocabulary_size=vocabulary_size, @@ -388,7 +384,7 @@ def sequence_categorical_column_with_vocabulary_list( ValueError: if `dtype` is not integer or string. """ return fc_old._SequenceCategoricalColumn( - fc_old.categorical_column_with_vocabulary_list( + fc_old._categorical_column_with_vocabulary_list( key=key, vocabulary_list=vocabulary_list, dtype=dtype, @@ -441,7 +437,7 @@ def sequence_numeric_column( ValueError: if any dimension in shape is not a positive integer. ValueError: if `dtype` is not convertible to `tf.float32`. """ - shape = fc._check_shape(shape=shape, key=key) + shape = fc_old._check_shape(shape=shape, key=key) if not (dtype.is_integer or dtype.is_floating): raise ValueError('dtype must be convertible to float. ' 'dtype: {}, key: {}'.format(dtype, key)) diff --git a/tensorflow/contrib/feature_column/python/feature_column/sequence_feature_column_v2_test.py b/tensorflow/contrib/feature_column/python/feature_column/sequence_feature_column_v2_test.py index 5ecd85807c..ca4398a142 100644 --- a/tensorflow/contrib/feature_column/python/feature_column/sequence_feature_column_v2_test.py +++ b/tensorflow/contrib/feature_column/python/feature_column/sequence_feature_column_v2_test.py @@ -25,7 +25,7 @@ import numpy as np from tensorflow.contrib.feature_column.python.feature_column import sequence_feature_column as sfc_old from tensorflow.contrib.feature_column.python.feature_column import sequence_feature_column_v2 as sfc from tensorflow.python.feature_column import feature_column as fc_old -from tensorflow.python.feature_column import feature_column_v2 as fc +from tensorflow.python.feature_column import feature_column_lib as fc from tensorflow.python.feature_column.feature_column import _LazyBuilder from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors @@ -111,13 +111,15 @@ class SequenceInputLayerTest(test.TestCase, parameterized.TestCase): categorical_column_a = sfc.sequence_categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - embedding_column_a = fc_old.embedding_column( - categorical_column_a, dimension=embedding_dimension_a, + embedding_column_a = fc_old._embedding_column( + categorical_column_a, + dimension=embedding_dimension_a, initializer=_get_initializer(embedding_dimension_a, embedding_values_a)) categorical_column_b = sfc.sequence_categorical_column_with_identity( key='bbb', num_buckets=vocabulary_size) - embedding_column_b = fc_old.embedding_column( - categorical_column_b, dimension=embedding_dimension_b, + embedding_column_b = fc_old._embedding_column( + categorical_column_b, + dimension=embedding_dimension_b, initializer=_get_initializer(embedding_dimension_b, embedding_values_b)) input_layer, sequence_length = sfc.sequence_input_layer( @@ -150,9 +152,9 @@ class SequenceInputLayerTest(test.TestCase, parameterized.TestCase): values=(2, 0, 1), dense_shape=(2, 2)) - categorical_column_a = fc_old.categorical_column_with_identity( + categorical_column_a = fc_old._categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - embedding_column_a = fc_old.embedding_column( + embedding_column_a = fc_old._embedding_column( categorical_column_a, dimension=2) with self.assertRaisesRegexp( @@ -208,7 +210,7 @@ class SequenceInputLayerTest(test.TestCase, parameterized.TestCase): categorical_column_b = sfc.sequence_categorical_column_with_identity( key='bbb', num_buckets=vocabulary_size) # Test that columns are reordered alphabetically. - shared_embedding_columns = fc_old.shared_embedding_columns( + shared_embedding_columns = fc.shared_embedding_columns( [categorical_column_b, categorical_column_a], dimension=embedding_dimension, initializer=_get_initializer(embedding_dimension, embedding_values)) @@ -246,11 +248,11 @@ class SequenceInputLayerTest(test.TestCase, parameterized.TestCase): values=(2, 0, 1), dense_shape=(2, 2)) - categorical_column_a = fc_old.categorical_column_with_identity( + categorical_column_a = fc_old._categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - categorical_column_b = fc_old.categorical_column_with_identity( + categorical_column_b = fc_old._categorical_column_with_identity( key='bbb', num_buckets=vocabulary_size) - shared_embedding_columns = fc_old.shared_embedding_columns( + shared_embedding_columns = fc.shared_embedding_columns( [categorical_column_a, categorical_column_b], dimension=2) with self.assertRaisesRegexp( @@ -317,10 +319,10 @@ class SequenceInputLayerTest(test.TestCase, parameterized.TestCase): categorical_column_a = sfc.sequence_categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size_a) - indicator_column_a = fc_old.indicator_column(categorical_column_a) + indicator_column_a = fc_old._indicator_column(categorical_column_a) categorical_column_b = sfc.sequence_categorical_column_with_identity( key='bbb', num_buckets=vocabulary_size_b) - indicator_column_b = fc_old.indicator_column(categorical_column_b) + indicator_column_b = fc_old._indicator_column(categorical_column_b) input_layer, sequence_length = sfc.sequence_input_layer( features={ 'aaa': sparse_input_a, @@ -344,9 +346,9 @@ class SequenceInputLayerTest(test.TestCase, parameterized.TestCase): values=(2, 0, 1), dense_shape=(2, 2)) - categorical_column_a = fc_old.categorical_column_with_identity( + categorical_column_a = fc_old._categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - indicator_column_a = fc_old.indicator_column(categorical_column_a) + indicator_column_a = fc_old._indicator_column(categorical_column_a) with self.assertRaisesRegexp( ValueError, @@ -532,7 +534,7 @@ class SequenceInputLayerTest(test.TestCase, parameterized.TestCase): sparse_input = sparse_tensor.SparseTensorValue(**sparse_input_args) categorical_column = sfc.sequence_categorical_column_with_identity( key='aaa', num_buckets=3) - indicator_column = fc_old.indicator_column(categorical_column) + indicator_column = fc_old._indicator_column(categorical_column) input_layer, _ = sfc.sequence_input_layer( features={'aaa': sparse_input}, feature_columns=[indicator_column]) @@ -618,7 +620,7 @@ class InputLayerTest(test.TestCase): categorical_column_a = sfc.sequence_categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - embedding_column_a = fc_old.embedding_column( + embedding_column_a = fc_old._embedding_column( categorical_column_a, dimension=2) with self.assertRaisesRegexp( @@ -641,7 +643,7 @@ class InputLayerTest(test.TestCase): categorical_column_a = sfc.sequence_categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - indicator_column_a = fc_old.indicator_column(categorical_column_a) + indicator_column_a = fc_old._indicator_column(categorical_column_a) with self.assertRaisesRegexp( ValueError, @@ -920,8 +922,9 @@ class SequenceEmbeddingColumnTest( categorical_column = sfc.sequence_categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - embedding_column = fc_old.embedding_column( - categorical_column, dimension=embedding_dimension, + embedding_column = fc_old._embedding_column( + categorical_column, + dimension=embedding_dimension, initializer=_initializer) embedding_lookup, _ = embedding_column._get_sequence_dense_tensor( @@ -958,8 +961,7 @@ class SequenceEmbeddingColumnTest( categorical_column = sfc.sequence_categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - embedding_column = fc_old.embedding_column( - categorical_column, dimension=2) + embedding_column = fc_old._embedding_column(categorical_column, dimension=2) _, sequence_length = embedding_column._get_sequence_dense_tensor( _LazyBuilder({'aaa': inputs})) @@ -986,8 +988,7 @@ class SequenceEmbeddingColumnTest( categorical_column = sfc.sequence_categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - embedding_column = fc_old.embedding_column( - categorical_column, dimension=2) + embedding_column = fc_old._embedding_column(categorical_column, dimension=2) _, sequence_length = embedding_column._get_sequence_dense_tensor( _LazyBuilder({'aaa': sparse_input})) @@ -1057,7 +1058,7 @@ class SequenceSharedEmbeddingColumnTest(test.TestCase): key='aaa', num_buckets=vocabulary_size) categorical_column_b = sfc.sequence_categorical_column_with_identity( key='bbb', num_buckets=vocabulary_size) - shared_embedding_columns = fc_old.shared_embedding_columns( + shared_embedding_columns = fc.shared_embedding_columns( [categorical_column_a, categorical_column_b], dimension=embedding_dimension, initializer=_initializer) @@ -1103,7 +1104,7 @@ class SequenceSharedEmbeddingColumnTest(test.TestCase): expected_sequence_length_b = [2, 1] categorical_column_b = sfc.sequence_categorical_column_with_identity( key='bbb', num_buckets=vocabulary_size) - shared_embedding_columns = fc_old.shared_embedding_columns( + shared_embedding_columns = fc.shared_embedding_columns( [categorical_column_a, categorical_column_b], dimension=2) sequence_length_a = shared_embedding_columns[0]._get_sequence_dense_tensor( @@ -1154,7 +1155,7 @@ class SequenceSharedEmbeddingColumnTest(test.TestCase): categorical_column_b = sfc.sequence_categorical_column_with_identity( key='bbb', num_buckets=vocabulary_size) - shared_embedding_columns = fc_old.shared_embedding_columns( + shared_embedding_columns = fc.shared_embedding_columns( [categorical_column_a, categorical_column_b], dimension=2) sequence_length_a = shared_embedding_columns[0]._get_sequence_dense_tensor( @@ -1220,7 +1221,7 @@ class SequenceIndicatorColumnTest(test.TestCase, parameterized.TestCase): categorical_column = sfc.sequence_categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - indicator_column = fc_old.indicator_column(categorical_column) + indicator_column = fc_old._indicator_column(categorical_column) indicator_tensor, _ = indicator_column._get_sequence_dense_tensor( _LazyBuilder({'aaa': inputs})) @@ -1252,7 +1253,7 @@ class SequenceIndicatorColumnTest(test.TestCase, parameterized.TestCase): categorical_column = sfc.sequence_categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - indicator_column = fc_old.indicator_column(categorical_column) + indicator_column = fc_old._indicator_column(categorical_column) _, sequence_length = indicator_column._get_sequence_dense_tensor( _LazyBuilder({'aaa': inputs})) diff --git a/tensorflow/contrib/framework/BUILD b/tensorflow/contrib/framework/BUILD index cd747df4d6..dad50a3a73 100644 --- a/tensorflow/contrib/framework/BUILD +++ b/tensorflow/contrib/framework/BUILD @@ -47,6 +47,11 @@ tf_custom_op_py_library( ":variable_ops_op_lib", ], srcs_version = "PY2AND3", + visibility = [ + "//learning/brain:__subpackages__", + "//tensorflow:__subpackages__", + "//video/youtube/personalization:__subpackages__", + ], deps = [ ":gen_variable_ops", "//tensorflow/contrib/util:util_py", @@ -66,6 +71,7 @@ tf_custom_op_py_library( "//tensorflow/python:resource_variable_ops", "//tensorflow/python:script_ops", "//tensorflow/python:smart_cond", + "//tensorflow/python:sort_ops", "//tensorflow/python:sparse_tensor", "//tensorflow/python:state_ops", "//tensorflow/python:state_ops_gen", @@ -311,17 +317,3 @@ py_test( "//third_party/py/numpy", ], ) - -py_test( - name = "sort_ops_test", - size = "medium", - srcs = ["python/ops/sort_ops_test.py"], - srcs_version = "PY2AND3", - deps = [ - ":framework_py", - "//tensorflow/python:array_ops", - "//tensorflow/python:client_testlib", - "//tensorflow/python:random_ops", - "//third_party/py/numpy", - ], -) diff --git a/tensorflow/contrib/framework/python/ops/sort_ops.py b/tensorflow/contrib/framework/python/ops/sort_ops.py index 1921a77c1e..42184a4e55 100644 --- a/tensorflow/contrib/framework/python/ops/sort_ops.py +++ b/tensorflow/contrib/framework/python/ops/sort_ops.py @@ -22,173 +22,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -import numpy as np +from tensorflow.python.ops import sort_ops -from tensorflow.python.framework import constant_op -from tensorflow.python.framework import ops as framework_ops -from tensorflow.python.framework import tensor_util -from tensorflow.python.ops import array_ops -from tensorflow.python.ops import math_ops -from tensorflow.python.ops import nn_ops - - -def sort(values, axis=-1, direction='ASCENDING', name=None): - """Sorts a tensor. - - Args: - values: 1-D or higher numeric `Tensor`. - axis: The axis along which to sort. The default is -1, which sorts the last - axis. - direction: The direction in which to sort the values (`'ASCENDING'` or - `'DESCENDING'`). - name: Optional name for the operation. - - Returns: - A `Tensor` with the same dtype and shape as `values`, with the elements - sorted along the given `axis`. - - Raises: - ValueError: If axis is not a constant scalar, or the direction is invalid. - """ - with framework_ops.name_scope(name, 'sort'): - return _sort_or_argsort(values, axis, direction, return_argsort=False) - - -def argsort(values, axis=-1, direction='ASCENDING', stable=False, name=None): - """Returns the indices of a tensor that give its sorted order along an axis. - - For a 1D tensor, `tf.gather(values, tf.argsort(values))` is equivalent to - `tf.sort(values)`. For higher dimensions, the output has the same shape as - `values`, but along the given axis, values represent the index of the sorted - element in that slice of the tensor at the given position. - - Args: - values: 1-D or higher numeric `Tensor`. - axis: The axis along which to sort. The default is -1, which sorts the last - axis. - direction: The direction in which to sort the values (`'ASCENDING'` or - `'DESCENDING'`). - stable: If True, equal elements in the original tensor will not be - re-ordered in the returned order. Unstable sort is not yet implemented, - but will eventually be the default for performance reasons. If you - require a stable order, pass `stable=True` for forwards compatibility. - name: Optional name for the operation. - - Returns: - An int32 `Tensor` with the same shape as `values`. The indices that would - sort each slice of the given `values` along the given `axis`. - - Raises: - ValueError: If axis is not a constant scalar, or the direction is invalid. - """ - del stable # Unused. - with framework_ops.name_scope(name, 'argsort'): - return _sort_or_argsort(values, axis, direction, return_argsort=True) - - -def _sort_or_argsort(values, axis, direction, return_argsort): - """Internal sort/argsort implementation. - - Args: - values: The input values. - axis: The axis along which to sort. - direction: 'ASCENDING' or 'DESCENDING'. - return_argsort: Whether to return the argsort result. - - Returns: - Either the sorted values, or the indices of the sorted values in the - original tensor. See the `sort` and `argsort` docstrings. - - Raises: - ValueError: If axis is not a constant scalar, or the direction is invalid. - """ - if direction not in _SORT_IMPL: - raise ValueError('%s should be one of %s' % - (direction, ', '.join(sorted(_SORT_IMPL.keys())))) - # Axis must be an integer, not a Tensor. - axis = framework_ops.convert_to_tensor(axis, name='axis') - axis_static = tensor_util.constant_value(axis) - if axis.shape.ndims != 0 or axis_static is None: - raise ValueError('axis must be a constant scalar') - axis_static = int(axis_static) # Avoids NumPy casting error - - values = framework_ops.convert_to_tensor(values, name='values') - - return _SORT_IMPL[direction](values, axis_static, return_argsort) - - -def _descending_sort(values, axis, return_argsort=False): - """Sorts values in reverse using `top_k`. - - Args: - values: Tensor of numeric values. - axis: Index of the axis which values should be sorted along. - return_argsort: If False, return the sorted values. If True, return the - indices that would sort the values. - - Returns: - The sorted values. - """ - k = array_ops.shape(values)[axis] - rank = array_ops.rank(values) - static_rank = values.shape.ndims - # Fast path: sorting the last axis. - if axis == -1 or axis + 1 == values.get_shape().ndims: - top_k_input = values - transposition = None - else: - # Otherwise, transpose the array. Swap axes `axis` and `rank - 1`. - if axis < 0: - # Calculate the actual axis index if counting from the end. Use the static - # rank if available, or else make the axis back into a tensor. - axis += static_rank or rank - if static_rank is not None: - # Prefer to calculate the transposition array in NumPy and make it a - # constant. - transposition = constant_op.constant( - np.r_[ - # Axes up to axis are unchanged. - np.arange(axis), - # Swap axis and rank - 1. - [static_rank - 1], - # Axes in [axis + 1, rank - 1) are unchanged. - np.arange(axis + 1, static_rank - 1), - # Swap axis and rank - 1. - [axis]], - name='transposition') - else: - # Generate the transposition array from the tensors. - transposition = array_ops.concat( - [ - # Axes up to axis are unchanged. - math_ops.range(axis), - # Swap axis and rank - 1. - [rank - 1], - # Axes in [axis + 1, rank - 1) are unchanged. - math_ops.range(axis + 1, rank - 1), - # Swap axis and rank - 1. - [axis] - ], - axis=0) - top_k_input = array_ops.transpose(values, transposition) - - values, indices = nn_ops.top_k(top_k_input, k) - return_value = indices if return_argsort else values - if transposition is not None: - # transposition contains a single cycle of length 2 (swapping 2 elements), - # so it is an involution (it is its own inverse). - return_value = array_ops.transpose(return_value, transposition) - return return_value - - -def _ascending_sort(values, axis, return_argsort=False): - # Negate the values to get the ascending order from descending sort. - values_or_indices = _descending_sort(-values, axis, return_argsort) - # If not argsort, negate the values again. - return values_or_indices if return_argsort else -values_or_indices - - -_SORT_IMPL = { - 'ASCENDING': _ascending_sort, - 'DESCENDING': _descending_sort, -} +sort = sort_ops.sort +argsort = sort_ops.argsort diff --git a/tensorflow/contrib/gan/python/estimator/python/gan_estimator_impl.py b/tensorflow/contrib/gan/python/estimator/python/gan_estimator_impl.py index 219cc199d7..3593b501bb 100644 --- a/tensorflow/contrib/gan/python/estimator/python/gan_estimator_impl.py +++ b/tensorflow/contrib/gan/python/estimator/python/gan_estimator_impl.py @@ -113,7 +113,8 @@ class GANEstimator(estimator.Estimator): add_summaries=None, use_loss_summaries=True, config=None, - warm_start_from=None): + warm_start_from=None, + is_chief=True): """Initializes a GANEstimator instance. Args: @@ -154,6 +155,8 @@ class GANEstimator(estimator.Estimator): config: `RunConfig` object to configure the runtime settings. warm_start_from: A filepath to a checkpoint or saved model, or a WarmStartSettings object to configure initialization. + is_chief: Whether or not this Estimator is running on a chief or worker. + Needs to be set appropriately if using SyncReplicasOptimizers. Raises: ValueError: If loss functions aren't callable. @@ -187,7 +190,7 @@ class GANEstimator(estimator.Estimator): return _get_estimator_spec( mode, gan_model, generator_loss_fn, discriminator_loss_fn, get_eval_metric_ops_fn, generator_optimizer, discriminator_optimizer, - get_hooks_fn, use_loss_summaries) + get_hooks_fn, use_loss_summaries, is_chief) super(GANEstimator, self).__init__( model_fn=_model_fn, model_dir=model_dir, config=config, @@ -215,7 +218,7 @@ def _get_gan_model( def _get_estimator_spec( mode, gan_model, generator_loss_fn, discriminator_loss_fn, get_eval_metric_ops_fn, generator_optimizer, discriminator_optimizer, - get_hooks_fn=None, use_loss_summaries=True): + get_hooks_fn=None, use_loss_summaries=True, is_chief=True): """Get the EstimatorSpec for the current mode.""" if mode == model_fn_lib.ModeKeys.PREDICT: estimator_spec = model_fn_lib.EstimatorSpec( @@ -236,7 +239,7 @@ def _get_estimator_spec( else discriminator_optimizer) get_hooks_fn = get_hooks_fn or tfgan_train.get_sequential_train_hooks() estimator_spec = _get_train_estimator_spec( - gan_model, gan_loss, gopt, dopt, get_hooks_fn) + gan_model, gan_loss, gopt, dopt, get_hooks_fn, is_chief=is_chief) return estimator_spec @@ -321,11 +324,11 @@ def _get_eval_estimator_spec(gan_model, gan_loss, get_eval_metric_ops_fn=None, def _get_train_estimator_spec( gan_model, gan_loss, generator_optimizer, discriminator_optimizer, - get_hooks_fn, train_op_fn=tfgan_train.gan_train_ops): + get_hooks_fn, train_op_fn=tfgan_train.gan_train_ops, is_chief=True): """Return an EstimatorSpec for the train case.""" scalar_loss = gan_loss.generator_loss + gan_loss.discriminator_loss train_ops = train_op_fn(gan_model, gan_loss, generator_optimizer, - discriminator_optimizer) + discriminator_optimizer, is_chief=is_chief) training_hooks = get_hooks_fn(train_ops) return model_fn_lib.EstimatorSpec( loss=scalar_loss, diff --git a/tensorflow/contrib/gan/python/estimator/python/gan_estimator_test.py b/tensorflow/contrib/gan/python/estimator/python/gan_estimator_test.py index 3d6bdab0ad..bc9021050b 100644 --- a/tensorflow/contrib/gan/python/estimator/python/gan_estimator_test.py +++ b/tensorflow/contrib/gan/python/estimator/python/gan_estimator_test.py @@ -48,6 +48,7 @@ from tensorflow.python.platform import test from tensorflow.python.summary.writer import writer_cache from tensorflow.python.training import input as input_lib from tensorflow.python.training import learning_rate_decay +from tensorflow.python.training import sync_replicas_optimizer from tensorflow.python.training import training from tensorflow.python.training import training_util @@ -82,7 +83,7 @@ class GetGANModelTest(test.TestCase, parameterized.TestCase): self.assertEqual(generator_inputs, gan_model.generator_inputs) self.assertIsNotNone(gan_model.generated_data) - self.assertEqual(2, len(gan_model.generator_variables)) # 1 FC layer + self.assertLen(gan_model.generator_variables, 2) # 1 FC layer self.assertIsNotNone(gan_model.generator_fn) if mode == model_fn_lib.ModeKeys.PREDICT: self.assertIsNone(gan_model.real_data) @@ -95,7 +96,7 @@ class GetGANModelTest(test.TestCase, parameterized.TestCase): self.assertIsNotNone(gan_model.real_data) self.assertIsNotNone(gan_model.discriminator_real_outputs) self.assertIsNotNone(gan_model.discriminator_gen_outputs) - self.assertEqual(2, len(gan_model.discriminator_variables)) # 1 FC layer + self.assertLen(gan_model.discriminator_variables, 2) # 1 FC layer self.assertIsNotNone(gan_model.discriminator_scope) self.assertIsNotNone(gan_model.discriminator_fn) @@ -121,6 +122,7 @@ def get_dummy_gan_model(): def dummy_loss_fn(gan_model, add_summaries=True): + del add_summaries return math_ops.reduce_sum(gan_model.discriminator_real_outputs - gan_model.discriminator_gen_outputs) @@ -168,6 +170,35 @@ class GetEstimatorSpecTest(test.TestCase, parameterized.TestCase): self.assertShapeEqual(np.array(0), spec.loss) # must be a scalar self.assertIsNotNone(spec.eval_metric_ops) + def test_get_sync_estimator_spec(self): + """Make sure spec is loaded with sync hooks for sync opts.""" + + def get_sync_optimizer(): + return sync_replicas_optimizer.SyncReplicasOptimizer( + training.GradientDescentOptimizer(learning_rate=1.0), + replicas_to_aggregate=1) + + with ops.Graph().as_default(): + self._gan_model = get_dummy_gan_model() + g_opt = get_sync_optimizer() + d_opt = get_sync_optimizer() + + spec = estimator._get_estimator_spec( + model_fn_lib.ModeKeys.TRAIN, + self._gan_model, + generator_loss_fn=dummy_loss_fn, + discriminator_loss_fn=dummy_loss_fn, + get_eval_metric_ops_fn=get_metrics, + generator_optimizer=g_opt, + discriminator_optimizer=d_opt) + + self.assertLen(spec.training_hooks, 4) + sync_opts = [ + hook._sync_optimizer for hook in spec.training_hooks if + isinstance(hook, sync_replicas_optimizer._SyncReplicasOptimizerHook)] + self.assertLen(sync_opts, 2) + self.assertSetEqual(frozenset(sync_opts), frozenset((g_opt, d_opt))) + # TODO(joelshor): Add pandas test. class GANEstimatorIntegrationTest(test.TestCase): diff --git a/tensorflow/contrib/gan/python/losses/python/losses_impl.py b/tensorflow/contrib/gan/python/losses/python/losses_impl.py index df0342c80c..a0a86c6337 100644 --- a/tensorflow/contrib/gan/python/losses/python/losses_impl.py +++ b/tensorflow/contrib/gan/python/losses/python/losses_impl.py @@ -36,7 +36,6 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -import numpy as np from tensorflow.contrib.framework.python.ops import variables as contrib_variables_lib from tensorflow.python.framework import ops @@ -47,7 +46,6 @@ from tensorflow.python.ops import gradients_impl from tensorflow.python.ops import math_ops from tensorflow.python.ops import random_ops from tensorflow.python.ops import variable_scope -from tensorflow.python.ops.distributions import distribution as ds from tensorflow.python.ops.losses import losses from tensorflow.python.ops.losses import util from tensorflow.python.summary import summary @@ -740,11 +738,16 @@ def least_squares_discriminator_loss( def _validate_distributions(distributions): if not isinstance(distributions, (list, tuple)): raise ValueError('`distributions` must be a list or tuple. Instead, ' - 'found %s.', type(distributions)) + 'found %s.' % type(distributions)) for x in distributions: - if not isinstance(x, ds.Distribution): + # We used to check with `isinstance(x, tf.distributions.Distribution)`. + # However, distributions have migrated to `tfp.distributions.Distribution`, + # which is a new code repo, so we can't check this way anymore until + # TF-GAN is migrated to a new repo as well. + # This new check is not sufficient, but is a useful heuristic for now. + if not callable(getattr(x, 'log_prob', None)): raise ValueError('`distributions` must be a list of `Distributions`. ' - 'Instead, found %s.', type(x)) + 'Instead, found %s.' % type(x)) def _validate_information_penalty_inputs( @@ -817,7 +820,7 @@ def _numerically_stable_global_norm(tensor_list): Returns: A scalar tensor with the global norm. """ - if np.all([x is None for x in tensor_list]): + if all(x is None for x in tensor_list): return 0.0 list_max = math_ops.reduce_max([math_ops.reduce_max(math_ops.abs(x)) for x in diff --git a/tensorflow/contrib/gan/python/namedtuples.py b/tensorflow/contrib/gan/python/namedtuples.py index b9ac1bf151..969b68449d 100644 --- a/tensorflow/contrib/gan/python/namedtuples.py +++ b/tensorflow/contrib/gan/python/namedtuples.py @@ -213,7 +213,8 @@ class GANTrainOps( collections.namedtuple('GANTrainOps', ( 'generator_train_op', 'discriminator_train_op', - 'global_step_inc_op' + 'global_step_inc_op', + 'train_hooks' ))): """GANTrainOps contains the training ops. @@ -221,8 +222,17 @@ class GANTrainOps( generator_train_op: Op that performs a generator update step. discriminator_train_op: Op that performs a discriminator update step. global_step_inc_op: Op that increments the shared global step. + train_hooks: a list or tuple containing hooks related to training that need + to be populated when training ops are instantiated. Used primarily for + sync hooks. """ + def __new__(cls, generator_train_op, discriminator_train_op, + global_step_inc_op, train_hooks=()): + return super(GANTrainOps, cls).__new__(cls, generator_train_op, + discriminator_train_op, + global_step_inc_op, train_hooks) + class GANTrainSteps( collections.namedtuple('GANTrainSteps', ( diff --git a/tensorflow/contrib/gan/python/train.py b/tensorflow/contrib/gan/python/train.py index 7ee39f304a..4c7bee41b3 100644 --- a/tensorflow/contrib/gan/python/train.py +++ b/tensorflow/contrib/gan/python/train.py @@ -114,7 +114,7 @@ def gan_model( discriminator_gen_outputs = discriminator_fn(generated_data, generator_inputs) with variable_scope.variable_scope(dis_scope, reuse=True): - real_data = ops.convert_to_tensor(real_data) + real_data = _convert_tensor_or_l_or_d(real_data) discriminator_real_outputs = discriminator_fn(real_data, generator_inputs) if check_shapes: @@ -924,6 +924,7 @@ def gan_train_ops( generator_optimizer, discriminator_optimizer, check_for_unused_update_ops=True, + is_chief=True, # Optional args to pass directly to the `create_train_op`. **kwargs): """Returns GAN train ops. @@ -939,6 +940,8 @@ def gan_train_ops( discriminator_optimizer: The optimizer for the discriminator updates. check_for_unused_update_ops: If `True`, throws an exception if there are update ops outside of the generator or discriminator scopes. + is_chief: Specifies whether or not the training is being run by the primary + replica during replica training. **kwargs: Keyword args to pass directly to `training.create_train_op` for both the generator and discriminator train op. @@ -980,6 +983,9 @@ def gan_train_ops( kwargs, model.generator_scope.name, model.discriminator_scope.name, check_for_unused_update_ops) + # Get the sync hooks if these are needed. + sync_hooks = [] + generator_global_step = None if isinstance(generator_optimizer, sync_replicas_optimizer.SyncReplicasOptimizer): @@ -995,6 +1001,7 @@ def gan_train_ops( trainable=False, collections=[ops.GraphKeys.GLOBAL_VARIABLES]) gen_update_ops += [generator_global_step.assign(global_step)] + sync_hooks.append(generator_optimizer.make_session_run_hook(is_chief)) with ops.name_scope('generator_train'): gen_train_op = training.create_train_op( total_loss=loss.generator_loss, @@ -1016,6 +1023,7 @@ def gan_train_ops( trainable=False, collections=[ops.GraphKeys.GLOBAL_VARIABLES]) dis_update_ops += [discriminator_global_step.assign(global_step)] + sync_hooks.append(discriminator_optimizer.make_session_run_hook(is_chief)) with ops.name_scope('discriminator_train'): disc_train_op = training.create_train_op( total_loss=loss.discriminator_loss, @@ -1025,7 +1033,8 @@ def gan_train_ops( update_ops=dis_update_ops, **kwargs) - return namedtuples.GANTrainOps(gen_train_op, disc_train_op, global_step_inc) + return namedtuples.GANTrainOps(gen_train_op, disc_train_op, global_step_inc, + sync_hooks) # TODO(joelshor): Implement a dynamic GAN train loop, as in `Real-Time Adaptive @@ -1066,13 +1075,24 @@ def get_sequential_train_hooks(train_steps=namedtuples.GANTrainSteps(1, 1)): train_steps.generator_train_steps) discriminator_hook = RunTrainOpsHook(train_ops.discriminator_train_op, train_steps.discriminator_train_steps) - return [generator_hook, discriminator_hook] + return [generator_hook, discriminator_hook] + list(train_ops.train_hooks) return get_hooks +def _num_joint_steps(train_steps): + g_steps = train_steps.generator_train_steps + d_steps = train_steps.discriminator_train_steps + # Get the number of each type of step that should be run. + num_d_and_g_steps = min(g_steps, d_steps) + num_g_steps = g_steps - num_d_and_g_steps + num_d_steps = d_steps - num_d_and_g_steps + + return num_d_and_g_steps, num_g_steps, num_d_steps + + def get_joint_train_hooks(train_steps=namedtuples.GANTrainSteps(1, 1)): - """Returns a hooks function for sequential GAN training. + """Returns a hooks function for joint GAN training. When using these train hooks, IT IS RECOMMENDED TO USE `use_locking=True` ON ALL OPTIMIZERS TO AVOID RACE CONDITIONS. @@ -1105,12 +1125,7 @@ def get_joint_train_hooks(train_steps=namedtuples.GANTrainSteps(1, 1)): Returns: A function that takes a GANTrainOps tuple and returns a list of hooks. """ - g_steps = train_steps.generator_train_steps - d_steps = train_steps.discriminator_train_steps - # Get the number of each type of step that should be run. - num_d_and_g_steps = min(g_steps, d_steps) - num_g_steps = g_steps - num_d_and_g_steps - num_d_steps = d_steps - num_d_and_g_steps + num_d_and_g_steps, num_g_steps, num_d_steps = _num_joint_steps(train_steps) def get_hooks(train_ops): g_op = train_ops.generator_train_op @@ -1120,7 +1135,7 @@ def get_joint_train_hooks(train_steps=namedtuples.GANTrainSteps(1, 1)): g_hook = RunTrainOpsHook(g_op, num_g_steps) d_hook = RunTrainOpsHook(d_op, num_d_steps) - return [joint_hook, g_hook, d_hook] + return [joint_hook, g_hook, d_hook] + list(train_ops.train_hooks) return get_hooks diff --git a/tensorflow/contrib/gan/python/train_test.py b/tensorflow/contrib/gan/python/train_test.py index 64d6706199..841f25cd7f 100644 --- a/tensorflow/contrib/gan/python/train_test.py +++ b/tensorflow/contrib/gan/python/train_test.py @@ -519,7 +519,7 @@ class GANLossTest(test.TestCase, parameterized.TestCase): """Test output type.""" loss = train.gan_loss(get_gan_model_fn(), add_summaries=True) self.assertIsInstance(loss, namedtuples.GANLoss) - self.assertGreater(len(ops.get_collection(ops.GraphKeys.SUMMARIES)), 0) + self.assertNotEmpty(ops.get_collection(ops.GraphKeys.SUMMARIES)) @parameterized.named_parameters( ('cyclegan', create_cyclegan_model), @@ -528,7 +528,7 @@ class GANLossTest(test.TestCase, parameterized.TestCase): def test_cyclegan_output_type(self, get_gan_model_fn): loss = train.cyclegan_loss(get_gan_model_fn(), add_summaries=True) self.assertIsInstance(loss, namedtuples.CycleGANLoss) - self.assertGreater(len(ops.get_collection(ops.GraphKeys.SUMMARIES)), 0) + self.assertNotEmpty(ops.get_collection(ops.GraphKeys.SUMMARIES)) @parameterized.named_parameters( ('gan', create_gan_model, False), @@ -759,7 +759,7 @@ class TensorPoolAdjusteModelTest(test.TestCase): # For [pool_size, ?), the pool is full, tensor2 must be equal to some # historical values of tensor1 (which is previously stored in the # pool). - self.assertTrue(any([(v == t2).all() for v in history_values])) + self.assertTrue(any((v == t2).all() for v in history_values)) def _make_new_model_and_check(self, model, pool_size): pool_fn = lambda x: random_tensor_pool.tensor_pool(x, pool_size=pool_size) @@ -836,6 +836,9 @@ class GANTrainOpsTest(test.TestCase, parameterized.TestCase): self.assertIsInstance(train_ops, namedtuples.GANTrainOps) + # Make sure there are no training hooks populated accidentally. + self.assertEmpty(train_ops.train_hooks) + # TODO(joelshor): Add a test to check that custom update op is run. @parameterized.named_parameters( ('gan', create_gan_model, False), @@ -923,8 +926,15 @@ class GANTrainOpsTest(test.TestCase, parameterized.TestCase): model, loss, generator_optimizer=g_opt, discriminator_optimizer=d_opt) self.assertIsInstance(train_ops, namedtuples.GANTrainOps) # No new trainable variables should have been added. - self.assertEqual(num_trainable_vars, - len(variables_lib.get_trainable_variables())) + self.assertLen(variables_lib.get_trainable_variables(), num_trainable_vars) + + # Sync hooks should be populated in the GANTrainOps. + self.assertLen(train_ops.train_hooks, 2) + for hook in train_ops.train_hooks: + self.assertIsInstance( + hook, sync_replicas_optimizer._SyncReplicasOptimizerHook) + sync_opts = [hook._sync_optimizer for hook in train_ops.train_hooks] + self.assertSetEqual(frozenset(sync_opts), frozenset((g_opt, d_opt))) g_sync_init_op = g_opt.get_init_tokens_op(num_tokens=1) d_sync_init_op = d_opt.get_init_tokens_op(num_tokens=1) @@ -959,6 +969,32 @@ class GANTrainOpsTest(test.TestCase, parameterized.TestCase): coord.request_stop() coord.join(g_threads + d_threads) + @parameterized.named_parameters( + ('is_chief', True), + ('is_not_chief', False), + ) + def test_is_chief_in_train_hooks(self, is_chief): + """Make sure is_chief is propagated correctly to sync hooks.""" + model = create_gan_model() + loss = train.gan_loss(model) + g_opt = get_sync_optimizer() + d_opt = get_sync_optimizer() + train_ops = train.gan_train_ops( + model, + loss, + g_opt, + d_opt, + is_chief=is_chief, + summarize_gradients=True, + colocate_gradients_with_ops=True) + + self.assertLen(train_ops.train_hooks, 2) + for hook in train_ops.train_hooks: + self.assertIsInstance( + hook, sync_replicas_optimizer._SyncReplicasOptimizerHook) + is_chief_list = [hook._is_chief for hook in train_ops.train_hooks] + self.assertListEqual(is_chief_list, [is_chief, is_chief]) + class GANTrainTest(test.TestCase, parameterized.TestCase): """Tests for `gan_train`.""" @@ -1036,6 +1072,44 @@ class GANTrainTest(test.TestCase, parameterized.TestCase): self.assertTrue(np.isscalar(final_loss)) self.assertEqual(17.0, final_loss) + @parameterized.named_parameters( + ('gan', create_gan_model), + ('callable_gan', create_callable_gan_model), + ('infogan', create_infogan_model), + ('callable_infogan', create_callable_infogan_model), + ('acgan', create_acgan_model), + ('callable_acgan', create_callable_acgan_model), + ) + def test_train_hooks_exist_in_get_hooks_fn(self, create_gan_model_fn): + model = create_gan_model_fn() + loss = train.gan_loss(model) + + g_opt = get_sync_optimizer() + d_opt = get_sync_optimizer() + train_ops = train.gan_train_ops( + model, + loss, + g_opt, + d_opt, + summarize_gradients=True, + colocate_gradients_with_ops=True) + + sequential_train_hooks = train.get_sequential_train_hooks()(train_ops) + self.assertLen(sequential_train_hooks, 4) + sync_opts = [ + hook._sync_optimizer for hook in sequential_train_hooks if + isinstance(hook, sync_replicas_optimizer._SyncReplicasOptimizerHook)] + self.assertLen(sync_opts, 2) + self.assertSetEqual(frozenset(sync_opts), frozenset((g_opt, d_opt))) + + joint_train_hooks = train.get_joint_train_hooks()(train_ops) + self.assertLen(joint_train_hooks, 5) + sync_opts = [ + hook._sync_optimizer for hook in joint_train_hooks if + isinstance(hook, sync_replicas_optimizer._SyncReplicasOptimizerHook)] + self.assertLen(sync_opts, 2) + self.assertSetEqual(frozenset(sync_opts), frozenset((g_opt, d_opt))) + class PatchGANTest(test.TestCase, parameterized.TestCase): """Tests that functions work on PatchGAN style output.""" diff --git a/tensorflow/contrib/gdr/gdr_rendezvous_mgr.cc b/tensorflow/contrib/gdr/gdr_rendezvous_mgr.cc index 94f522c04e..fbccbead03 100644 --- a/tensorflow/contrib/gdr/gdr_rendezvous_mgr.cc +++ b/tensorflow/contrib/gdr/gdr_rendezvous_mgr.cc @@ -170,6 +170,14 @@ class GdrRemoteRendezvous : public BaseRemoteRendezvous { // Record "call" in active_ so that it can be aborted cleanly. RegisterCall(call); + // RendezvousMgr already aborted, shouldn't send RPC call any more + if (!call->status().ok()) { + done(call->status(), Args(), Args(), Tensor(), false); + session()->worker_cache->ReleaseWorker(src_worker, rwi); + delete call; + return; + } + // Start "call". Ref(); call->Start([this, call, src_worker, rwi, done]() { diff --git a/tensorflow/contrib/hadoop/python/kernel_tests/hadoop_test.py b/tensorflow/contrib/hadoop/python/kernel_tests/hadoop_test.py index f7f1189bb9..bc941ae9f2 100644 --- a/tensorflow/contrib/hadoop/python/kernel_tests/hadoop_test.py +++ b/tensorflow/contrib/hadoop/python/kernel_tests/hadoop_test.py @@ -21,6 +21,7 @@ from __future__ import print_function import os from tensorflow.contrib.hadoop.python.ops import hadoop_dataset_ops +from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors @@ -47,7 +48,7 @@ class SequenceFileDatasetTest(test.TestCase): dataset = hadoop_dataset_ops.SequenceFileDataset(filenames).repeat( num_repeats) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) init_op = iterator.initializer get_next = iterator.get_next() diff --git a/tensorflow/contrib/hadoop/python/ops/hadoop_dataset_ops.py b/tensorflow/contrib/hadoop/python/ops/hadoop_dataset_ops.py index bf398b838d..d3fcc8cb2a 100644 --- a/tensorflow/contrib/hadoop/python/ops/hadoop_dataset_ops.py +++ b/tensorflow/contrib/hadoop/python/ops/hadoop_dataset_ops.py @@ -40,15 +40,12 @@ class SequenceFileDataset(dataset_ops.DatasetSource): For example: ```python + tf.enable_eager_execution() + dataset = tf.contrib.hadoop.SequenceFileDataset("/foo/bar.seq") - iterator = dataset.make_one_shot_iterator() - next_element = iterator.get_next() # Prints the (key, value) pairs inside a hadoop sequence file. - while True: - try: - print(sess.run(next_element)) - except tf.errors.OutOfRangeError: - break + for key, value in dataset: + print(key, value) ``` Args: diff --git a/tensorflow/contrib/ignite/README.md b/tensorflow/contrib/ignite/README.md index c7db0b77e2..5a8c650fb9 100644 --- a/tensorflow/contrib/ignite/README.md +++ b/tensorflow/contrib/ignite/README.md @@ -54,14 +54,12 @@ jdbc:ignite:thin://localhost/> INSERT INTO KITTEN_CACHE VALUES (3, 'LITTLE BALL ```python >>> import tensorflow as tf >>> from tensorflow.contrib.ignite import IgniteDataset ->>> +>>> tf.enable_eager_execution() +>>> >>> dataset = IgniteDataset(cache_name="SQL_PUBLIC_KITTEN_CACHE") ->>> iterator = dataset.make_one_shot_iterator() ->>> next_obj = iterator.get_next() >>> ->>> with tf.Session() as sess: ->>> for _ in range(3): ->>> print(sess.run(next_obj)) +>>> for element in dataset: +>>> print(element) {'key': 1, 'val': {'NAME': b'WARM KITTY'}} {'key': 2, 'val': {'NAME': b'SOFT KITTY'}} @@ -74,23 +72,22 @@ jdbc:ignite:thin://localhost/> INSERT INTO KITTEN_CACHE VALUES (3, 'LITTLE BALL ```python >>> import tensorflow as tf >>> from tensorflow.contrib.ignite import IgniteDataset ->>> +>>> tf.enable_eager_execution() +>>> >>> dataset = IgniteDataset(cache_name="IMAGES") ->>> iterator = dataset.make_one_shot_iterator() ->>> next_obj = iterator.get_next() >>> ->>> with tf.Session() as sess: ->>> print(sess.run(next_obj)) +>>> for element in dataset.take(1): +>>> print(element) { - 'key': 'kitten.png', + 'key': 'kitten.png', 'val': { 'metadata': { 'file_name': b'kitten.png', 'label': b'little ball of fur', - width: 800, + width: 800, height: 600 - }, + }, 'pixels': [0, 0, 0, 0, ..., 0] } } @@ -100,13 +97,11 @@ jdbc:ignite:thin://localhost/> INSERT INTO KITTEN_CACHE VALUES (3, 'LITTLE BALL ```python >>> import tensorflow as tf >>> from tensorflow.contrib.ignite import IgniteDataset ->>> +>>> >>> dataset = IgniteDataset(cache_name="IMAGES").map(lambda obj: obj['val']['pixels']) ->>> iterator = dataset.make_one_shot_iterator() ->>> next_obj = iterator.get_next() >>> ->>> with tf.Session() as sess: ->>> print(sess.run(next_obj)) +>>> for element in dataset: +>>> print(element) [0, 0, 0, 0, ..., 0] ``` @@ -126,18 +121,18 @@ Ignite Dataset allows using these two aspects of distributed neural network trai ```python >>> import tensorflow as tf >>> from tensorflow.contrib.ignite import IgniteDataset ->>> +>>> >>> dataset = IgniteDataset("IMAGES") >>> >>> # Compute gradients locally on every worker node. ->>> gradients = [] +>>> gradients = [] >>> for i in range(5): >>> with tf.device("/job:WORKER/task:%d" % i): ->>> device_iterator = dataset.make_one_shot_iterator() +>>> device_iterator = tf.compat.v1.data.make_one_shot_iterator(dataset) >>> device_next_obj = device_iterator.get_next() >>> gradient = compute_gradient(device_next_obj) ->>> gradients.append(gradient) ->>> +>>> gradients.append(gradient) +>>> >>> # Aggregate them on master node. >>> result_gradient = tf.reduce_sum(gradients) >>> @@ -145,7 +140,7 @@ Ignite Dataset allows using these two aspects of distributed neural network trai >>> print(sess.run(result_gradient)) ``` -High-level TensorFlow API for [distributed training](https://www.tensorflow.org/api_docs/python/tf/contrib/distribute/DistributionStrategy) is supported as well. +High-level TensorFlow API for [distributed training](https://www.tensorflow.org/api_docs/python/tf/contrib/distribute/DistributionStrategy) is supported as well. ### Distributed File System diff --git a/tensorflow/contrib/ignite/python/tests/ignite_dataset_test.py b/tensorflow/contrib/ignite/python/tests/ignite_dataset_test.py index ef29b5f14a..ff5d4c458c 100644 --- a/tensorflow/contrib/ignite/python/tests/ignite_dataset_test.py +++ b/tensorflow/contrib/ignite/python/tests/ignite_dataset_test.py @@ -21,6 +21,7 @@ import os from tensorflow.contrib.ignite import IgniteDataset from tensorflow.python.client import session +from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors from tensorflow.python.platform import test @@ -65,7 +66,7 @@ class IgniteDatasetTest(test.TestCase): self.assertEqual(dtypes.string, dataset.output_types["val"]["NAME"]) self.assertEqual(dtypes.int64, dataset.output_types["val"]["VAL"]) - it = dataset.make_one_shot_iterator() + it = dataset_ops.make_one_shot_iterator(dataset) ne = it.get_next() with session.Session() as sess: diff --git a/tensorflow/contrib/image/kernels/adjust_hsv_in_yiq_op.cc b/tensorflow/contrib/image/kernels/adjust_hsv_in_yiq_op.cc index 478b716d88..108da04494 100644 --- a/tensorflow/contrib/image/kernels/adjust_hsv_in_yiq_op.cc +++ b/tensorflow/contrib/image/kernels/adjust_hsv_in_yiq_op.cc @@ -115,7 +115,7 @@ class AdjustHsvInYiqOp : public AdjustHsvInYiqOpBase { *context->device()->tensorflow_cpu_worker_threads(); Shard(worker_threads.num_threads, worker_threads.workers, channel_count, kCostPerChannel, - [channel_count, &input_data, &output_data, &tranformation_matrix]( + [&input_data, &output_data, &tranformation_matrix]( int64 start_channel, int64 end_channel) { // Applying projection matrix to input RGB vectors. const float* p = input_data.data() + start_channel * kChannelSize; diff --git a/tensorflow/contrib/image/python/ops/dense_image_warp.py b/tensorflow/contrib/image/python/ops/dense_image_warp.py index 9c7ada7afb..7930b8317b 100644 --- a/tensorflow/contrib/image/python/ops/dense_image_warp.py +++ b/tensorflow/contrib/image/python/ops/dense_image_warp.py @@ -25,7 +25,7 @@ from tensorflow.python.framework import ops from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops - +from tensorflow.python.ops import check_ops def _interpolate_bilinear(grid, query_points, @@ -60,28 +60,40 @@ def _interpolate_bilinear(grid, msg = 'Grid must be 4 dimensional. Received size: ' raise ValueError(msg + str(grid.get_shape())) - batch_size, height, width, channels = shape + batch_size, height, width, channels = (array_ops.shape(grid)[0], + array_ops.shape(grid)[1], + array_ops.shape(grid)[2], + array_ops.shape(grid)[3]) + + shape = [batch_size, height, width, channels] query_type = query_points.dtype grid_type = grid.dtype - if (query_points.shape.rank != 3 or - query_points.shape.dims[2].value != 2): - msg = ('Query points must be 3 dimensional and size 2 in dim 2. Received ' - 'size: ') - raise ValueError(msg + str(query_points.get_shape())) - - _, num_queries, _ = query_points.get_shape().as_list() - - if height < 2 or width < 2: - msg = 'Grid must be at least batch_size x 2 x 2 in size. Received size: ' - raise ValueError(msg + str(grid.get_shape())) - - alphas = [] - floors = [] - ceils = [] - - index_order = [0, 1] if indexing == 'ij' else [1, 0] - unstacked_query_points = array_ops.unstack(query_points, axis=2) + with ops.control_dependencies([ + check_ops.assert_equal( + len(query_points.get_shape()), + 3, + message='Query points must be 3 dimensional.'), + check_ops.assert_equal( + array_ops.shape(query_points)[2], + 2, + message='Query points must be size 2 in dim 2.')]): + num_queries = array_ops.shape(query_points)[1] + + with ops.control_dependencies([ + check_ops.assert_greater_equal( + height, + 2, + message='Grid height must be at least 2.'), + check_ops.assert_greater_equal( + width, + 2, + message='Grid width must be at least 2.')]): + alphas = [] + floors = [] + ceils = [] + index_order = [0, 1] if indexing == 'ij' else [1, 0] + unstacked_query_points = array_ops.unstack(query_points, axis=2) for dim in index_order: with ops.name_scope('dim-' + str(dim)): @@ -112,16 +124,17 @@ def _interpolate_bilinear(grid, alpha = array_ops.expand_dims(alpha, 2) alphas.append(alpha) - if batch_size * height * width > np.iinfo(np.int32).max / 8: - error_msg = """The image size or batch size is sufficiently large - that the linearized addresses used by array_ops.gather - may exceed the int32 limit.""" - raise ValueError(error_msg) - - flattened_grid = array_ops.reshape(grid, - [batch_size * height * width, channels]) - batch_offsets = array_ops.reshape( - math_ops.range(batch_size) * height * width, [batch_size, 1]) + with ops.control_dependencies([ + check_ops.assert_less_equal( + math_ops.cast(batch_size * height * width, dtype=dtypes.float32), + np.iinfo(np.int32).max / 8, + message="""The image size or batch size is sufficiently large + that the linearized addresses used by array_ops.gather + may exceed the int32 limit.""")]): + flattened_grid = array_ops.reshape( + grid, [batch_size * height * width, channels]) + batch_offsets = array_ops.reshape( + math_ops.range(batch_size) * height * width, [batch_size, 1]) # This wraps array_ops.gather. We reshape the image data such that the # batch, y, and x coordinates are pulled into the first dimension. @@ -182,7 +195,11 @@ def dense_image_warp(image, flow, name='dense_image_warp'): of dimensions. """ with ops.name_scope(name): - batch_size, height, width, channels = image.get_shape().as_list() + batch_size, height, width, channels = (array_ops.shape(image)[0], + array_ops.shape(image)[1], + array_ops.shape(image)[2], + array_ops.shape(image)[3]) + # The flow is defined on the image grid. Turn the flow into a list of query # points in the grid space. grid_x, grid_y = array_ops.meshgrid( diff --git a/tensorflow/contrib/keras/api/keras/layers/__init__.py b/tensorflow/contrib/keras/api/keras/layers/__init__.py index 3327a9f9a6..9e19884df8 100644 --- a/tensorflow/contrib/keras/api/keras/layers/__init__.py +++ b/tensorflow/contrib/keras/api/keras/layers/__init__.py @@ -20,7 +20,7 @@ from __future__ import print_function # Generic layers. # pylint: disable=g-bad-import-order -from tensorflow.python.keras.engine.base_layer import InputSpec +from tensorflow.python.keras.engine.input_spec import InputSpec from tensorflow.python.keras.engine.base_layer import Layer from tensorflow.python.keras.engine.input_layer import Input from tensorflow.python.keras.engine.input_layer import InputLayer diff --git a/tensorflow/contrib/keras/api/keras/utils/__init__.py b/tensorflow/contrib/keras/api/keras/utils/__init__.py index 47cd01b924..3b9fa1b230 100644 --- a/tensorflow/contrib/keras/api/keras/utils/__init__.py +++ b/tensorflow/contrib/keras/api/keras/utils/__init__.py @@ -30,6 +30,7 @@ from tensorflow.python.keras.utils.generic_utils import Progbar from tensorflow.python.keras.utils.generic_utils import serialize_keras_object from tensorflow.python.keras.utils.io_utils import HDF5Matrix from tensorflow.python.keras.utils.layer_utils import convert_all_kernels_in_model +from tensorflow.python.keras.utils.losses_utils import squeeze_or_expand_dimensions from tensorflow.python.keras.utils.np_utils import normalize from tensorflow.python.keras.utils.np_utils import to_categorical from tensorflow.python.keras.utils.vis_utils import plot_model diff --git a/tensorflow/contrib/kernel_methods/python/kernel_estimators.py b/tensorflow/contrib/kernel_methods/python/kernel_estimators.py index de7530231d..1626e55b9b 100644 --- a/tensorflow/contrib/kernel_methods/python/kernel_estimators.py +++ b/tensorflow/contrib/kernel_methods/python/kernel_estimators.py @@ -90,7 +90,7 @@ def _update_features_and_columns(features, feature_columns, mapped_column_name = column_name + "_MAPPED" # Construct new feature columns based on provided kernel_mappers. column_kernel_mappers = kernel_mappers_dict[feature_column] - new_dim = sum([mapper.output_dim for mapper in column_kernel_mappers]) + new_dim = sum(mapper.output_dim for mapper in column_kernel_mappers) mapped_columns.add( layers.feature_column.real_valued_column(mapped_column_name, new_dim)) diff --git a/tensorflow/contrib/kinesis/python/ops/kinesis_dataset_ops.py b/tensorflow/contrib/kinesis/python/ops/kinesis_dataset_ops.py index 75806dbbeb..c392adbb1d 100644 --- a/tensorflow/contrib/kinesis/python/ops/kinesis_dataset_ops.py +++ b/tensorflow/contrib/kinesis/python/ops/kinesis_dataset_ops.py @@ -34,15 +34,12 @@ class KinesisDataset(dataset_ops.DatasetSource): For example, we can construct and use the KinesisDataset as follows: ```python + tf.enable_eager_execution() + dataset = tf.contrib.kinesis.KinesisDataset( "kinesis_stream_name", read_indefinitely=False) - next = dataset.make_one_shot_iterator().get_next() - with tf.Session() as sess: - while True: - try: - print(sess.run(nxt)) - except tf.errors.OutOfRangeError: - break + for element in dataset: + print(element) ``` Since Kinesis is a data streaming service, data may not be available diff --git a/tensorflow/contrib/layers/BUILD b/tensorflow/contrib/layers/BUILD index e6596bfdfb..9ca6f8df5d 100644 --- a/tensorflow/contrib/layers/BUILD +++ b/tensorflow/contrib/layers/BUILD @@ -78,6 +78,11 @@ tf_custom_op_py_library( ":sparse_feature_cross_op_op_lib", ], srcs_version = "PY2AND3", + visibility = [ + "//learning/brain:__subpackages__", + "//tensorflow:__subpackages__", + "//video/youtube/personalization:__subpackages__", + ], deps = [ ":sparse_feature_cross_op", "//tensorflow/contrib/framework:framework_py", @@ -253,7 +258,7 @@ py_test( "//tensorflow/python:training", "//tensorflow/python:variable_scope", "//tensorflow/python:variables", - "//tensorflow/python/feature_column", + "//tensorflow/python/feature_column:feature_column_py", "//third_party/py/numpy", ], ) @@ -277,7 +282,7 @@ py_test( "//tensorflow/python:sparse_tensor", "//tensorflow/python:variable_scope", "//tensorflow/python:variables", - "//tensorflow/python/feature_column", + "//tensorflow/python/feature_column:feature_column_py", "//third_party/py/numpy", ], ) diff --git a/tensorflow/contrib/layers/python/layers/embedding_ops_test.py b/tensorflow/contrib/layers/python/layers/embedding_ops_test.py index 124515e5a6..295c721fce 100644 --- a/tensorflow/contrib/layers/python/layers/embedding_ops_test.py +++ b/tensorflow/contrib/layers/python/layers/embedding_ops_test.py @@ -21,6 +21,7 @@ from __future__ import print_function import itertools import math +import sys import numpy as np @@ -36,6 +37,7 @@ from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import init_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import partitioned_variables +from tensorflow.python.ops import variable_scope from tensorflow.python.platform import test from tensorflow.python.util import compat @@ -48,11 +50,13 @@ class SafeEmbeddingLookupSparseTest(test.TestCase): assert num_shards > 0 assert num_shards <= vocab_size - embedding_weights = partitioned_variables.create_partitioned_variables( + initializer = init_ops.truncated_normal_initializer( + mean=0.0, stddev=1.0 / math.sqrt(vocab_size), dtype=dtypes.float32) + embedding_weights = list(variable_scope.get_variable( + "embedding_weights", shape=[vocab_size, embed_dim], - slicing=[num_shards, 1], - initializer=init_ops.truncated_normal_initializer( - mean=0.0, stddev=1.0 / math.sqrt(vocab_size), dtype=dtypes.float32)) + partitioner=partitioned_variables.fixed_size_partitioner(num_shards), + initializer=initializer)) for w in embedding_weights: w.initializer.run() embedding_weights = [w.eval() for w in embedding_weights] @@ -256,6 +260,13 @@ class SafeEmbeddingLookupSparseTest(test.TestCase): embedding_weights, sparse_ids, sparse_weights) +# pylint: disable=invalid-name +def local_variable_scope(): + """Create a variable scope named like the caller function.""" + return variable_scope.variable_scope(sys._getframe(1).f_code.co_name) +# pylint: enable=invalid-name + + class ScatteredEmbeddingLookupTest(test.TestCase): def setUp(self): @@ -266,17 +277,18 @@ class ScatteredEmbeddingLookupTest(test.TestCase): assert num_shards > 0 assert num_shards <= size - embedding_weights = partitioned_variables.create_partitioned_variables( + embedding_weights = list(variable_scope.get_variable( + "embedding_weights", shape=[size], - slicing=[num_shards], + partitioner=partitioned_variables.fixed_size_partitioner(num_shards), initializer=init_ops.truncated_normal_initializer( - mean=0.0, stddev=1.0, dtype=dtypes.float32)) + mean=0.0, stddev=1.0, dtype=dtypes.float32))) for w in embedding_weights: w.initializer.run() return embedding_weights def test_scattered_embedding_consistency(self): - with self.cached_session(): + with self.cached_session(), local_variable_scope(): embedding_weights = self._random_weights() values = constant_op.constant(["foo", "foo"]) @@ -288,7 +300,7 @@ class ScatteredEmbeddingLookupTest(test.TestCase): embedding_lookup_result[1]) def test_scattered_embedding_multiple_partition(self): - with self.cached_session(): + with self.cached_session(), local_variable_scope(): embedding_weights = self._random_weights(num_shards=7) values = constant_op.constant([4, 4, 5]) @@ -304,7 +316,7 @@ class ScatteredEmbeddingLookupTest(test.TestCase): self.assertGreater(embedding_diff, 0) def test_scattered_embedding_coverage(self): - with self.cached_session(): + with self.cached_session(), local_variable_scope(): size = 8 embedding_weights = self._random_weights(size=size, num_shards=3) values = constant_op.constant(["foo"]) @@ -316,7 +328,7 @@ class ScatteredEmbeddingLookupTest(test.TestCase): self.assertEqual(len(np.unique(embedding_lookup_result[0])), size) def test_scattered_embedding_multi_dimension(self): - with self.cached_session(): + with self.cached_session(), local_variable_scope(): embedding_weights = self._random_weights() values = constant_op.constant([["foo", "bar", "bar"], ["bar", "bar", "foo"]]) @@ -329,7 +341,7 @@ class ScatteredEmbeddingLookupTest(test.TestCase): embedding_lookup_result[1][2]) def test_scattered_embedding_lookup_sparse(self): - with self.cached_session(): + with self.cached_session(), local_variable_scope(): embedding_weights = self._random_weights(num_shards=3) sparse_tensor = sparse_tensor_lib.SparseTensor( values=["foo", "bar", "foo", "bar"], @@ -358,7 +370,7 @@ class ScatteredEmbeddingLookupTest(test.TestCase): embeds = np.random.randn(n_embed, d_embed) idx = np.random.randint(0, n_embed, idx_shape) - with self.cached_session(): + with self.cached_session(), local_variable_scope(): embedded_np = embeds[idx] embedded_tf = embedding_ops.embedding_lookup_unique(embeds, idx).eval() @@ -370,7 +382,7 @@ class ScatteredEmbeddingLookupTest(test.TestCase): idx = np.random.randint(0, 5, 10) idx2d = np.random.randint(0, 5, (10, 2)) - with self.cached_session(): + with self.cached_session(), local_variable_scope(): embedded_np = embeds[idx] embedded_np2d = embeds[idx2d] embedded_tf = embedding_ops.embedding_lookup_unique(embeds, idx).eval() @@ -398,17 +410,18 @@ class SampledScatteredEmbeddingLookupTest(test.TestCase): assert num_shards > 0 assert num_shards <= size - embedding_weights = partitioned_variables.create_partitioned_variables( + embedding_weights = list(variable_scope.get_variable( + "embedding_weights", shape=[size], - slicing=[num_shards], + partitioner=partitioned_variables.fixed_size_partitioner(num_shards), initializer=init_ops.truncated_normal_initializer( - mean=0.0, stddev=1.0, dtype=dtypes.float32)) + mean=0.0, stddev=1.0, dtype=dtypes.float32))) for w in embedding_weights: w.initializer.run() return embedding_weights def test_hashed_embedding_consistency(self): - with self.cached_session(): + with self.cached_session(), local_variable_scope(): embedding_weights = self._random_weights() values = constant_op.constant(["foo", "foo"]) # The first three sampled_candidates are equal, so the first three @@ -429,7 +442,7 @@ class SampledScatteredEmbeddingLookupTest(test.TestCase): embedding_lookup_result[1][3]) def test_hashed_embedding_multi_dimension(self): - with self.cached_session(): + with self.cached_session(), local_variable_scope(): embedding_weights = self._random_weights() values = constant_op.constant([["foo", "bar", "bar"], ["bar", "bar", "foo"]]) @@ -691,7 +704,6 @@ class EmbeddingLookupSparseWithDistributedAggregationTest(test.TestCase): index += num_val return grouped_vals - @test_util.enable_c_shapes def testEmbeddingLookupSparse(self): vocab_size = 13 batch_size = 10 diff --git a/tensorflow/contrib/layers/python/layers/encoders.py b/tensorflow/contrib/layers/python/layers/encoders.py index f42112206d..3671633c8d 100644 --- a/tensorflow/contrib/layers/python/layers/encoders.py +++ b/tensorflow/contrib/layers/python/layers/encoders.py @@ -84,8 +84,7 @@ def bow_encoder(ids, if isinstance(ids, sparse_tensor.SparseTensor): raise TypeError('ids are expected to be dense Tensor, got: %s', ids) return math_ops.reduce_mean( - embedding_ops.embedding_lookup(embeddings, ids), - reduction_indices=1) + embedding_ops.embedding_lookup(embeddings, ids), axis=1) def embed_sequence(ids, diff --git a/tensorflow/contrib/layers/python/layers/feature_column.py b/tensorflow/contrib/layers/python/layers/feature_column.py index 222404b19d..00d819ed0e 100644 --- a/tensorflow/contrib/layers/python/layers/feature_column.py +++ b/tensorflow/contrib/layers/python/layers/feature_column.py @@ -1015,8 +1015,7 @@ class _OneHotColumn( dense_id_tensor, depth=self.length, on_value=1.0, off_value=0.0) # Reduce to get a multi-hot per example. - return math_ops.reduce_sum( - one_hot_id_tensor, reduction_indices=[output_rank - 1]) + return math_ops.reduce_sum(one_hot_id_tensor, axis=[output_rank - 1]) @property def _variable_shape(self): diff --git a/tensorflow/contrib/layers/python/layers/feature_column_ops_test.py b/tensorflow/contrib/layers/python/layers/feature_column_ops_test.py index 6fb4b9ff35..7e6eafaa0d 100644 --- a/tensorflow/contrib/layers/python/layers/feature_column_ops_test.py +++ b/tensorflow/contrib/layers/python/layers/feature_column_ops_test.py @@ -27,7 +27,7 @@ from tensorflow.contrib.layers.python.layers import feature_column from tensorflow.contrib.layers.python.layers import feature_column_ops from tensorflow.core.example import example_pb2 from tensorflow.core.example import feature_pb2 -from tensorflow.python.feature_column import feature_column as fc_core +from tensorflow.python.feature_column import feature_column_lib as fc_core from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops diff --git a/tensorflow/contrib/layers/python/layers/feature_column_test.py b/tensorflow/contrib/layers/python/layers/feature_column_test.py index d90d6ecf7f..cab8da808b 100644 --- a/tensorflow/contrib/layers/python/layers/feature_column_test.py +++ b/tensorflow/contrib/layers/python/layers/feature_column_test.py @@ -27,7 +27,7 @@ import numpy as np from tensorflow.contrib.layers.python.layers import feature_column as fc from tensorflow.contrib.layers.python.layers import feature_column_ops -from tensorflow.python.feature_column import feature_column as fc_core +from tensorflow.python.feature_column import feature_column_lib as fc_core from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import sparse_tensor as sparse_tensor_lib diff --git a/tensorflow/contrib/layers/python/layers/layers.py b/tensorflow/contrib/layers/python/layers/layers.py index ac9561c769..403b522ce4 100644 --- a/tensorflow/contrib/layers/python/layers/layers.py +++ b/tensorflow/contrib/layers/python/layers/layers.py @@ -35,6 +35,7 @@ from tensorflow.python.framework import function from tensorflow.python.framework import ops from tensorflow.python.framework import sparse_tensor from tensorflow.python.framework import tensor_shape +from tensorflow.python.keras.engine import input_spec from tensorflow.python.layers import base from tensorflow.python.layers import convolutional as convolutional_layers from tensorflow.python.layers import core as core_layers @@ -1958,7 +1959,7 @@ class GDN(base.Layer): self._reparam_offset = reparam_offset self.data_format = data_format self._channel_axis() # trigger ValueError early - self.input_spec = base.InputSpec(min_ndim=3, max_ndim=5) + self.input_spec = input_spec.InputSpec(min_ndim=3, max_ndim=5) def _channel_axis(self): try: @@ -2015,7 +2016,7 @@ class GDN(base.Layer): raise ValueError('The channel dimension of the inputs to `GDN` ' 'must be defined.') self._input_rank = input_shape.ndims - self.input_spec = base.InputSpec( + self.input_spec = input_spec.InputSpec( ndim=input_shape.ndims, axes={ channel_axis: num_channels }) diff --git a/tensorflow/contrib/layers/python/layers/layers_test.py b/tensorflow/contrib/layers/python/layers/layers_test.py index 8ead6336a0..0a4d2c6d4c 100644 --- a/tensorflow/contrib/layers/python/layers/layers_test.py +++ b/tensorflow/contrib/layers/python/layers/layers_test.py @@ -3811,7 +3811,7 @@ class UnitNormTests(test.TestCase): image = random_ops.random_uniform((height, width, 3)) output = _layers.unit_norm(image, dim=dim, epsilon=1e-6) norms = math_ops.sqrt( - math_ops.reduce_sum(math_ops.square(output), reduction_indices=dim)) + math_ops.reduce_sum(math_ops.square(output), axis=dim)) shape = [height, width, 3] del shape[dim] @@ -3847,7 +3847,7 @@ class UnitNormTests(test.TestCase): image = array_ops.placeholder(dtypes.float32, (None, None, 3)) output = _layers.unit_norm(image, dim=dim, epsilon=1e-6) norms = math_ops.sqrt( - math_ops.reduce_sum(math_ops.square(output), reduction_indices=dim)) + math_ops.reduce_sum(math_ops.square(output), axis=dim)) with self.cached_session(): actual = norms.eval({image: placeholder_value}) diff --git a/tensorflow/contrib/layers/python/layers/regularizers_test.py b/tensorflow/contrib/layers/python/layers/regularizers_test.py index 51faba30c7..5cb00b7684 100644 --- a/tensorflow/contrib/layers/python/layers/regularizers_test.py +++ b/tensorflow/contrib/layers/python/layers/regularizers_test.py @@ -141,7 +141,7 @@ class RegularizerTest(test.TestCase): dummy_regularizer = lambda x: math_ops.reduce_sum(2 * x) array_weights_list = [[1.5], [2, 3, 4.2], [10, 42, 666.6]] tensor_weights_list = [constant_op.constant(x) for x in array_weights_list] - expected = sum([2 * x for l in array_weights_list for x in l]) + expected = sum(2 * x for l in array_weights_list for x in l) with self.cached_session(): result = regularizers.apply_regularization(dummy_regularizer, tensor_weights_list) diff --git a/tensorflow/contrib/learn/BUILD b/tensorflow/contrib/learn/BUILD index 61185f65a9..14065fcee5 100644 --- a/tensorflow/contrib/learn/BUILD +++ b/tensorflow/contrib/learn/BUILD @@ -24,6 +24,11 @@ py_library( exclude = ["python/learn/**/*_test.py"], ), srcs_version = "PY2AND3", + visibility = [ + "//learning/brain:__subpackages__", + "//tensorflow:__subpackages__", + "//video/youtube/personalization:__subpackages__", + ], # This library should not depend on sklearn, even though some of the code # refers to it. (The code handles the presence of sklearn conditionally.) deps = [ @@ -269,6 +274,7 @@ py_test( name = "estimator_test", size = "medium", srcs = ["python/learn/estimators/estimator_test.py"], + shard_count = 2, srcs_version = "PY2AND3", tags = [ "manual", diff --git a/tensorflow/contrib/learn/python/learn/estimators/dnn.py b/tensorflow/contrib/learn/python/learn/estimators/dnn.py index eabebb7e88..10fbd60ba2 100644 --- a/tensorflow/contrib/learn/python/learn/estimators/dnn.py +++ b/tensorflow/contrib/learn/python/learn/estimators/dnn.py @@ -28,7 +28,6 @@ import six from tensorflow.contrib import layers from tensorflow.contrib.framework import deprecated from tensorflow.contrib.framework import deprecated_arg_values -from tensorflow.python.training import training_util from tensorflow.contrib.layers.python.layers import feature_column from tensorflow.contrib.layers.python.layers import optimizers from tensorflow.contrib.learn.python.learn import metric_spec @@ -38,11 +37,12 @@ from tensorflow.contrib.learn.python.learn.estimators import head as head_lib from tensorflow.contrib.learn.python.learn.estimators import model_fn from tensorflow.contrib.learn.python.learn.estimators import prediction_key from tensorflow.contrib.learn.python.learn.utils import export -from tensorflow.python.feature_column import feature_column as fc_core +from tensorflow.python.feature_column import feature_column_lib as fc_core from tensorflow.python.ops import nn from tensorflow.python.ops import partitioned_variables from tensorflow.python.ops import variable_scope from tensorflow.python.summary import summary +from tensorflow.python.training import training_util # The default learning rate of 0.05 is a historical artifact of the initial # implementation, but seems a reasonable choice. @@ -150,10 +150,10 @@ def _dnn_model_fn(features, labels, mode, params, config=None): "input_from_feature_columns", values=tuple(six.itervalues(features)), partitioner=input_layer_partitioner) as input_layer_scope: - if all([ + if all( isinstance(fc, feature_column._FeatureColumn) # pylint: disable=protected-access for fc in feature_columns - ]): + ): net = layers.input_from_feature_columns( columns_to_tensors=features, feature_columns=feature_columns, diff --git a/tensorflow/contrib/learn/python/learn/estimators/dnn_linear_combined.py b/tensorflow/contrib/learn/python/learn/estimators/dnn_linear_combined.py index 3d85533d92..2ade6b7b6c 100644 --- a/tensorflow/contrib/learn/python/learn/estimators/dnn_linear_combined.py +++ b/tensorflow/contrib/learn/python/learn/estimators/dnn_linear_combined.py @@ -38,7 +38,7 @@ from tensorflow.contrib.learn.python.learn.estimators import head as head_lib from tensorflow.contrib.learn.python.learn.estimators import model_fn from tensorflow.contrib.learn.python.learn.estimators import prediction_key from tensorflow.contrib.learn.python.learn.utils import export -from tensorflow.python.feature_column import feature_column as fc_core +from tensorflow.python.feature_column import feature_column_lib as fc_core from tensorflow.python.framework import ops from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import nn @@ -236,10 +236,10 @@ def _dnn_linear_combined_model_fn(features, labels, mode, params, config=None): "input_from_feature_columns", values=tuple(six.itervalues(features)), partitioner=input_layer_partitioner) as dnn_input_scope: - if all([ + if all( isinstance(fc, feature_column_lib._FeatureColumn) # pylint: disable=protected-access for fc in dnn_feature_columns - ]): + ): net = layers.input_from_feature_columns( columns_to_tensors=features, feature_columns=dnn_feature_columns, @@ -292,8 +292,8 @@ def _dnn_linear_combined_model_fn(features, labels, mode, params, config=None): linear_parent_scope, values=tuple(six.itervalues(features)), partitioner=linear_partitioner) as scope: - if all([isinstance(fc, feature_column_lib._FeatureColumn) # pylint: disable=protected-access - for fc in linear_feature_columns]): + if all(isinstance(fc, feature_column_lib._FeatureColumn) # pylint: disable=protected-access + for fc in linear_feature_columns): if joint_linear_weights: linear_logits, _, _ = layers.joint_weighted_sum_from_feature_columns( columns_to_tensors=features, diff --git a/tensorflow/contrib/learn/python/learn/estimators/dnn_linear_combined_test.py b/tensorflow/contrib/learn/python/learn/estimators/dnn_linear_combined_test.py index 4e65c180d8..d46a873bfa 100644 --- a/tensorflow/contrib/learn/python/learn/estimators/dnn_linear_combined_test.py +++ b/tensorflow/contrib/learn/python/learn/estimators/dnn_linear_combined_test.py @@ -36,7 +36,7 @@ from tensorflow.contrib.learn.python.learn.estimators import run_config from tensorflow.contrib.learn.python.learn.estimators import test_data from tensorflow.contrib.learn.python.learn.metric_spec import MetricSpec from tensorflow.contrib.metrics.python.ops import metric_ops -from tensorflow.python.feature_column import feature_column as fc_core +from tensorflow.python.feature_column import feature_column_lib as fc_core from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops diff --git a/tensorflow/contrib/learn/python/learn/estimators/dnn_test.py b/tensorflow/contrib/learn/python/learn/estimators/dnn_test.py index 2bd57597c2..ee25cebd48 100644 --- a/tensorflow/contrib/learn/python/learn/estimators/dnn_test.py +++ b/tensorflow/contrib/learn/python/learn/estimators/dnn_test.py @@ -38,7 +38,7 @@ from tensorflow.contrib.learn.python.learn.estimators import run_config from tensorflow.contrib.learn.python.learn.estimators import test_data from tensorflow.contrib.learn.python.learn.metric_spec import MetricSpec from tensorflow.contrib.metrics.python.ops import metric_ops -from tensorflow.python.feature_column import feature_column as fc_core +from tensorflow.python.feature_column import feature_column_lib as fc_core from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import sparse_tensor diff --git a/tensorflow/contrib/learn/python/learn/estimators/dynamic_rnn_estimator_test.py b/tensorflow/contrib/learn/python/learn/estimators/dynamic_rnn_estimator_test.py index 1d8a59281a..28c4964527 100644 --- a/tensorflow/contrib/learn/python/learn/estimators/dynamic_rnn_estimator_test.py +++ b/tensorflow/contrib/learn/python/learn/estimators/dynamic_rnn_estimator_test.py @@ -668,7 +668,7 @@ class DynamicRNNEstimatorLearningTest(test.TestCase): sequences = centers + noise inputs = array_ops.expand_dims(sequences, 2) - labels = math_ops.reduce_mean(sequences, reduction_indices=[1]) + labels = math_ops.reduce_mean(sequences, axis=[1]) return {'inputs': inputs}, labels return input_fn @@ -722,8 +722,8 @@ class DynamicRNNEstimatorLearningTest(test.TestCase): inputs = array_ops.expand_dims(math_ops.to_float(random_sequence), 2) labels = math_ops.to_int32( array_ops.squeeze( - math_ops.reduce_sum( - inputs, reduction_indices=[1]) > (sequence_length / 2.0))) + math_ops.reduce_sum(inputs, axis=[1]) > ( + sequence_length / 2.0))) return {'inputs': inputs}, labels return input_fn diff --git a/tensorflow/contrib/learn/python/learn/estimators/estimator.py b/tensorflow/contrib/learn/python/learn/estimators/estimator.py index 8bc869db89..9132b2209b 100644 --- a/tensorflow/contrib/learn/python/learn/estimators/estimator.py +++ b/tensorflow/contrib/learn/python/learn/estimators/estimator.py @@ -1066,11 +1066,11 @@ class BaseEstimator(sklearn.BaseEstimator, evaluable.Evaluable, chief_hooks = [] if (self._config.save_checkpoints_secs or self._config.save_checkpoints_steps): - saver_hook_exists = any([ + saver_hook_exists = any( isinstance(h, basic_session_run_hooks.CheckpointSaverHook) for h in (all_hooks + model_fn_ops.training_hooks + chief_hooks + model_fn_ops.training_chief_hooks) - ]) + ) if not saver_hook_exists: chief_hooks = [ basic_session_run_hooks.CheckpointSaverHook( @@ -1493,7 +1493,7 @@ class Estimator(BaseEstimator): # pylint: disable=protected-access class SKCompat(sklearn.BaseEstimator): """Scikit learn wrapper for TensorFlow Learn Estimator. - + THIS CLASS IS DEPRECATED. See [contrib/learn/README.md](https://www.tensorflow.org/code/tensorflow/contrib/learn/README.md) for general migration instructions. diff --git a/tensorflow/contrib/learn/python/learn/estimators/linear.py b/tensorflow/contrib/learn/python/learn/estimators/linear.py index e100bc7a1e..9ee8d8004b 100644 --- a/tensorflow/contrib/learn/python/learn/estimators/linear.py +++ b/tensorflow/contrib/learn/python/learn/estimators/linear.py @@ -37,7 +37,7 @@ from tensorflow.contrib.learn.python.learn.estimators import head as head_lib from tensorflow.contrib.learn.python.learn.estimators import prediction_key from tensorflow.contrib.learn.python.learn.utils import export from tensorflow.contrib.linear_optimizer.python import sdca_optimizer -from tensorflow.python.feature_column import feature_column as fc_core +from tensorflow.python.feature_column import feature_column_lib as fc_core from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import sparse_tensor @@ -155,8 +155,8 @@ def _linear_model_fn(features, labels, mode, params, config=None): parent_scope, values=tuple(six.itervalues(features)), partitioner=partitioner) as scope: - if all([isinstance(fc, feature_column._FeatureColumn) # pylint: disable=protected-access - for fc in feature_columns]): + if all(isinstance(fc, feature_column._FeatureColumn) # pylint: disable=protected-access + for fc in feature_columns): if joint_weights: layer_fn = layers.joint_weighted_sum_from_feature_columns else: diff --git a/tensorflow/contrib/learn/python/learn/estimators/linear_test.py b/tensorflow/contrib/learn/python/learn/estimators/linear_test.py index 597ca4e86d..dfc76bfde6 100644 --- a/tensorflow/contrib/learn/python/learn/estimators/linear_test.py +++ b/tensorflow/contrib/learn/python/learn/estimators/linear_test.py @@ -37,7 +37,7 @@ from tensorflow.contrib.learn.python.learn.estimators import test_data from tensorflow.contrib.learn.python.learn.metric_spec import MetricSpec from tensorflow.contrib.linear_optimizer.python import sdca_optimizer as sdca_optimizer_lib from tensorflow.contrib.metrics.python.ops import metric_ops -from tensorflow.python.feature_column import feature_column as fc_core +from tensorflow.python.feature_column import feature_column_lib as fc_core from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import sparse_tensor @@ -1745,7 +1745,7 @@ class LinearRegressorTest(test.TestCase): 'place_holder': constant_op.constant([[0.0]] * num_examples), }, constant_op.constant( - [[1 if i % 4 is 0 else 0] for i in range(num_examples)]) + [[1 if i % 4 == 0 else 0] for i in range(num_examples)]) place_holder = feature_column_lib.real_valued_column('place_holder') sdca_optimizer = sdca_optimizer_lib.SDCAOptimizer( diff --git a/tensorflow/contrib/learn/python/learn/learn_io/numpy_io.py b/tensorflow/contrib/learn/python/learn/learn_io/numpy_io.py index 29552d24f1..59a67636ae 100644 --- a/tensorflow/contrib/learn/python/learn/learn_io/numpy_io.py +++ b/tensorflow/contrib/learn/python/learn/learn_io/numpy_io.py @@ -27,7 +27,7 @@ from tensorflow.python.estimator.inputs.numpy_io import numpy_input_fn as core_n from tensorflow.python.util.deprecation import deprecated -@deprecated(None, 'Use tf.estimator.inputs.numpy_input_fn.') +@deprecated(None, 'Use tf.compat.v1.estimator.inputs.numpy_input_fn.') def numpy_input_fn(x, y=None, batch_size=128, diff --git a/tensorflow/contrib/learn/python/learn/learn_io/pandas_io.py b/tensorflow/contrib/learn/python/learn/learn_io/pandas_io.py index b4ef055f5a..e9df7258a3 100644 --- a/tensorflow/contrib/learn/python/learn/learn_io/pandas_io.py +++ b/tensorflow/contrib/learn/python/learn/learn_io/pandas_io.py @@ -53,7 +53,7 @@ PANDAS_DTYPES = { } -@deprecated(None, 'Please use tf.estimator.inputs.pandas_input_fn') +@deprecated(None, 'Please use tf.compat.v1.estimator.inputs.pandas_input_fn') def pandas_input_fn(x, y=None, batch_size=128, diff --git a/tensorflow/contrib/linear_optimizer/python/sdca_estimator_test.py b/tensorflow/contrib/linear_optimizer/python/sdca_estimator_test.py index 6476671882..7a5354222f 100644 --- a/tensorflow/contrib/linear_optimizer/python/sdca_estimator_test.py +++ b/tensorflow/contrib/linear_optimizer/python/sdca_estimator_test.py @@ -524,7 +524,7 @@ class SDCALinearRegressorTest(test.TestCase): # LinearClassifier requires at least one column. 'place_holder': constant_op.constant([[0.0]] * num_examples), - }, constant_op.constant([[1 if i % 4 is 0 else 0] + }, constant_op.constant([[1 if i % 4 == 0 else 0] for i in range(num_examples)]) with self._single_threaded_test_session(): diff --git a/tensorflow/contrib/lookup/lookup_ops_test.py b/tensorflow/contrib/lookup/lookup_ops_test.py index 5e99ef4605..9b2c2dd87c 100644 --- a/tensorflow/contrib/lookup/lookup_ops_test.py +++ b/tensorflow/contrib/lookup/lookup_ops_test.py @@ -25,6 +25,7 @@ import six from tensorflow.contrib import lookup from tensorflow.python.client import session from tensorflow.python.data.experimental.ops import counter +from tensorflow.python.data.ops import dataset_ops from tensorflow.python.eager import context from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes @@ -2737,7 +2738,7 @@ class MutableHashTableBenchmark(test.Benchmark): def benchmark_many_repeated_scalar_insert_scalar(self): table = self._create_table() - c = counter.Counter().make_one_shot_iterator().get_next() + c = dataset_ops.make_one_shot_iterator(counter.Counter()).get_next() value = variables.Variable(1.0) insert = table.insert(c, value) size = table.size() @@ -2758,7 +2759,7 @@ class MutableHashTableBenchmark(test.Benchmark): def benchmark_many_repeated_batch_32_insert_scalar(self): table = self._create_table() - c = counter.Counter().make_one_shot_iterator().get_next() + c = dataset_ops.make_one_shot_iterator(counter.Counter()).get_next() value = variables.Variable([1.0] * 32) insert = table.insert(32 * c + list(range(32)), value) size = table.size() diff --git a/tensorflow/contrib/losses/python/losses/loss_ops.py b/tensorflow/contrib/losses/python/losses/loss_ops.py index 619294b518..709a042bbc 100644 --- a/tensorflow/contrib/losses/python/losses/loss_ops.py +++ b/tensorflow/contrib/losses/python/losses/loss_ops.py @@ -22,7 +22,6 @@ from __future__ import division from __future__ import print_function from tensorflow.contrib.framework.python.ops import add_arg_scope -from tensorflow.python.compat import compat from tensorflow.python.framework import ops from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops @@ -60,41 +59,12 @@ def _scale_losses(losses, weights): """ # First, compute the sum of the losses over all elements: start_index = max(0, weights.get_shape().ndims) - reduction_indices = list(range(start_index, losses.get_shape().ndims)) - reduced_losses = math_ops.reduce_sum( - losses, reduction_indices=reduction_indices) + axis = list(range(start_index, losses.get_shape().ndims)) + reduced_losses = math_ops.reduce_sum(losses, axis=axis) reduced_losses = math_ops.multiply(reduced_losses, weights) return math_ops.reduce_sum(reduced_losses) -def _safe_div(numerator, denominator, name="value"): - """Computes a safe divide which returns 0 if the denominator is zero. - - Note that the function contains an additional conditional check that is - necessary for avoiding situations where the loss is zero causing NaNs to - creep into the gradient computation. - - Args: - numerator: An arbitrary `Tensor`. - denominator: A `Tensor` whose shape matches `numerator` and whose values are - assumed to be non-negative. - name: An optional name for the returned op. - - Returns: - The element-wise value of the numerator divided by the denominator. - """ - if compat.forward_compatible(2018, 11, 1): - return math_ops.div_no_nan(numerator, denominator, name=name) - return array_ops.where( - math_ops.greater(denominator, 0), - math_ops.div(numerator, - array_ops.where( - math_ops.equal(denominator, 0), - array_ops.ones_like(denominator), denominator)), - array_ops.zeros_like(numerator), - name=name) - - def _safe_mean(losses, num_present): """Computes a safe mean of the losses. @@ -107,7 +77,7 @@ def _safe_mean(losses, num_present): then zero is returned. """ total_loss = math_ops.reduce_sum(losses) - return _safe_div(total_loss, num_present, name="value") + return math_ops.div_no_nan(total_loss, num_present, name="value") @deprecated("2016-12-30", "Use tf.losses.compute_weighted_loss instead.") @@ -187,10 +157,9 @@ def _num_present(losses, weights, per_batch=False): # First, count the number of nonzero weights: if weights.get_shape().ndims >= 1: - reduction_indices = list(range(1, weights.get_shape().ndims)) + axis = list(range(1, weights.get_shape().ndims)) num_nonzero_per_batch = math_ops.reduce_sum( - math_ops.to_float(math_ops.not_equal(weights, 0)), - reduction_indices=reduction_indices) + math_ops.to_float(math_ops.not_equal(weights, 0)), axis=axis) # Next, determine the number of elements that weights would broadcast to: broadcast_dims = array_ops.slice( @@ -606,20 +575,20 @@ def mean_pairwise_squared_error(predictions, if weights.get_shape().ndims is None: raise ValueError("weights.get_shape().ndims cannot be None") - reduction_indices = list(range(1, diffs.get_shape().ndims)) + axis = list(range(1, diffs.get_shape().ndims)) sum_squares_diff_per_batch = math_ops.reduce_sum( - math_ops.square(diffs), reduction_indices=reduction_indices) + math_ops.square(diffs), axis=axis) num_present_per_batch = _num_present(diffs, weights, per_batch=True) - term1 = 2.0 * _safe_div(sum_squares_diff_per_batch, - num_present_per_batch, - name="value") + term1 = 2.0 * math_ops.div_no_nan( + sum_squares_diff_per_batch, num_present_per_batch, name="value") - sum_diff = math_ops.reduce_sum(diffs, reduction_indices=reduction_indices) - term2 = 2.0 * _safe_div(math_ops.square(sum_diff), - math_ops.square(num_present_per_batch), - name="value") + sum_diff = math_ops.reduce_sum(diffs, axis=axis) + term2 = 2.0 * math_ops.div_no_nan( + math_ops.square(sum_diff), + math_ops.square(num_present_per_batch), + name="value") loss = _scale_losses(term1 - term2, weights) @@ -674,7 +643,7 @@ def cosine_distance(predictions, radial_diffs = math_ops.multiply(predictions, labels) losses = 1 - math_ops.reduce_sum( - radial_diffs, reduction_indices=[ + radial_diffs, axis=[ axis, ]) return compute_weighted_loss(losses, weights, scope=scope) diff --git a/tensorflow/contrib/makefile/download_dependencies.sh b/tensorflow/contrib/makefile/download_dependencies.sh index 0a07588f07..b396c52767 100755 --- a/tensorflow/contrib/makefile/download_dependencies.sh +++ b/tensorflow/contrib/makefile/download_dependencies.sh @@ -34,7 +34,7 @@ NSYNC_URL="$(grep -o 'https://mirror.bazel.build/github.com/google/nsync/.*tar\. # 1.10 branch does not work. `make distclean` fails and blocks the build # process. For now we're hardcoding to the version which is used by # TensorFlow 1.9. -PROTOBUF_URL="https://mirror.bazel.build/github.com/google/protobuf/archive/396336eb961b75f03b25824fe86cf6490fb75e3a.tar.gz" +PROTOBUF_URL="https://mirror.bazel.build/github.com/google/protobuf/archive/v3.6.0.tar.gz" # TODO (yongtang): Replace the following with 'https://mirror.bazel.build/github.com/google/re2/.*tar\.gz' once # the archive has been propagated in mirror.bazel.build. RE2_URL="$(grep -o 'https://github.com/google/re2/.*tar\.gz' "${BZL_FILE_PATH}" | head -n1)" diff --git a/tensorflow/contrib/makefile/tf_op_files.txt b/tensorflow/contrib/makefile/tf_op_files.txt index e779eff689..655c7eefcb 100644 --- a/tensorflow/contrib/makefile/tf_op_files.txt +++ b/tensorflow/contrib/makefile/tf_op_files.txt @@ -157,6 +157,7 @@ tensorflow/core/kernels/mirror_pad_op_cpu_impl_2.cc tensorflow/core/kernels/mirror_pad_op_cpu_impl_3.cc tensorflow/core/kernels/mirror_pad_op_cpu_impl_4.cc tensorflow/core/kernels/mirror_pad_op_cpu_impl_5.cc +tensorflow/core/kernels/multinomial_op.cc tensorflow/core/kernels/no_op.cc tensorflow/core/kernels/non_max_suppression_op.cc tensorflow/core/kernels/one_hot_op.cc @@ -252,6 +253,7 @@ tensorflow/core/kernels/split_op.cc tensorflow/core/kernels/split_v_op.cc tensorflow/core/kernels/stack.cc tensorflow/core/kernels/stack_ops.cc +tensorflow/core/kernels/stateless_random_ops.cc tensorflow/core/kernels/strided_slice_op.cc tensorflow/core/kernels/strided_slice_op_inst_0.cc tensorflow/core/kernels/strided_slice_op_inst_1.cc diff --git a/tensorflow/contrib/metrics/python/metrics/classification.py b/tensorflow/contrib/metrics/python/metrics/classification.py index ac12360865..062deb74b1 100644 --- a/tensorflow/contrib/metrics/python/metrics/classification.py +++ b/tensorflow/contrib/metrics/python/metrics/classification.py @@ -175,7 +175,7 @@ def f1_score(labels, predictions, weights=None, num_thresholds=200, return best_f1 best_f1 = distribution_strategy_context.get_replica_context().merge_call( - f1_across_replicas, values) + f1_across_replicas, args=(values,)) update_op = compute_best_f1_score(tp=update_ops['tp'], fp=update_ops['fp'], fn=update_ops['fn'], name='update') diff --git a/tensorflow/contrib/metrics/python/metrics/classification_test.py b/tensorflow/contrib/metrics/python/metrics/classification_test.py index d6a670f97b..e789d2cb9d 100644 --- a/tensorflow/contrib/metrics/python/metrics/classification_test.py +++ b/tensorflow/contrib/metrics/python/metrics/classification_test.py @@ -291,12 +291,11 @@ class F1ScoreTest(test.TestCase): labels = labels.astype(np.float32) predictions = predictions.astype(np.float32) - tf_predictions, tf_labels = (dataset_ops.Dataset - .from_tensor_slices((predictions, labels)) - .repeat() - .batch(batch_size) - .make_one_shot_iterator() - .get_next()) + tf_predictions, tf_labels = dataset_ops.make_one_shot_iterator( + dataset_ops.Dataset + .from_tensor_slices((predictions, labels)) + .repeat() + .batch(batch_size)).get_next() f1, f1_op = classification.f1_score(tf_labels, tf_predictions, num_thresholds=3) diff --git a/tensorflow/contrib/metrics/python/ops/metric_ops.py b/tensorflow/contrib/metrics/python/ops/metric_ops.py index d6932f6e4b..7b432f8bd2 100644 --- a/tensorflow/contrib/metrics/python/ops/metric_ops.py +++ b/tensorflow/contrib/metrics/python/ops/metric_ops.py @@ -24,7 +24,6 @@ from __future__ import print_function import collections as collections_lib -from tensorflow.python.compat import compat from tensorflow.python.eager import context from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops @@ -46,32 +45,6 @@ from tensorflow.python.util.deprecation import deprecated _EPSILON = 1e-7 -def _safe_div(numerator, denominator): - """Computes a safe divide which returns 0 if the denominator is zero. - - Note that the function contains an additional conditional check that is - necessary for avoiding situations where the loss is zero causing NaNs to - creep into the gradient computation. - - Args: - numerator: An arbitrary `Tensor`. - denominator: A `Tensor` whose shape matches `numerator` and whose values are - assumed to be non-negative. - - Returns: - The element-wise value of the numerator divided by the denominator. - """ - if compat.forward_compatible(2018, 11, 1): - return math_ops.div_no_nan(numerator, denominator) - return array_ops.where( - math_ops.greater(denominator, 0), - math_ops.div(numerator, - array_ops.where( - math_ops.equal(denominator, 0), - array_ops.ones_like(denominator), denominator)), - array_ops.zeros_like(numerator)) - - @deprecated(None, 'Please switch to tf.metrics.true_positives. Note that the ' 'order of the labels and predictions arguments has been switched.') def streaming_true_positives(predictions, @@ -3247,24 +3220,20 @@ def streaming_covariance(predictions, # We update the means by Delta=Error*BatchCount/(BatchCount+PrevCount) # batch_mean_prediction is E[x_B] in the update equation - batch_mean_prediction = _safe_div( - math_ops.reduce_sum(weighted_predictions), - batch_count) - delta_mean_prediction = _safe_div( - (batch_mean_prediction - mean_prediction) * batch_count, - update_count) + batch_mean_prediction = math_ops.div_no_nan( + math_ops.reduce_sum(weighted_predictions), batch_count) + delta_mean_prediction = math_ops.div_no_nan( + (batch_mean_prediction - mean_prediction) * batch_count, update_count) update_mean_prediction = state_ops.assign_add(mean_prediction, delta_mean_prediction) # prev_mean_prediction is E[x_A] in the update equation prev_mean_prediction = update_mean_prediction - delta_mean_prediction # batch_mean_label is E[y_B] in the update equation - batch_mean_label = _safe_div( - math_ops.reduce_sum(weighted_labels), - batch_count) - delta_mean_label = _safe_div( - (batch_mean_label - mean_label) * batch_count, - update_count) + batch_mean_label = math_ops.div_no_nan( + math_ops.reduce_sum(weighted_labels), batch_count) + delta_mean_label = math_ops.div_no_nan( + (batch_mean_label - mean_label) * batch_count, update_count) update_mean_label = state_ops.assign_add(mean_label, delta_mean_label) # prev_mean_label is E[y_A] in the update equation prev_mean_label = update_mean_label - delta_mean_label @@ -3447,7 +3416,7 @@ def streaming_mean_cosine_distance(predictions, predictions.get_shape().assert_is_compatible_with(labels.get_shape()) radial_diffs = math_ops.multiply(predictions, labels) radial_diffs = math_ops.reduce_sum( - radial_diffs, reduction_indices=[ + radial_diffs, axis=[ dim, ], keepdims=True) mean_distance, update_op = streaming_mean(radial_diffs, weights, None, None, @@ -3926,9 +3895,8 @@ def cohen_kappa(labels, po_sum = math_ops.reduce_sum(po) total = math_ops.reduce_sum(pe_row) pe_sum = math_ops.reduce_sum( - _safe_div( - math_ops.to_double(pe_row * pe_col), - math_ops.to_double(total))) + math_ops.div_no_nan( + math_ops.to_double(pe_row * pe_col), math_ops.to_double(total))) po_sum, pe_sum, total = (math_ops.to_double(po_sum), math_ops.to_double(pe_sum), math_ops.to_double(total)) diff --git a/tensorflow/contrib/mixed_precision/python/loss_scale_manager_test.py b/tensorflow/contrib/mixed_precision/python/loss_scale_manager_test.py index 1b0383d24c..c922d0cd11 100644 --- a/tensorflow/contrib/mixed_precision/python/loss_scale_manager_test.py +++ b/tensorflow/contrib/mixed_precision/python/loss_scale_manager_test.py @@ -29,7 +29,7 @@ from tensorflow.python.platform import test def _GetExampleIter(inputs): dataset = dataset_ops.Dataset.from_tensor_slices(inputs) - return dataset.make_one_shot_iterator() + return dataset_ops.make_one_shot_iterator(dataset) class FixedLossScaleManagerTest(test.TestCase): diff --git a/tensorflow/contrib/mixed_precision/python/loss_scale_optimizer_test.py b/tensorflow/contrib/mixed_precision/python/loss_scale_optimizer_test.py index 9009df0eef..33f9a43e80 100644 --- a/tensorflow/contrib/mixed_precision/python/loss_scale_optimizer_test.py +++ b/tensorflow/contrib/mixed_precision/python/loss_scale_optimizer_test.py @@ -132,7 +132,7 @@ class LossScaleOptimizerTest(test.TestCase): x = variable_scope.get_variable("x", initializer=1., dtype=dtypes.float32) dataset = dataset_ops.Dataset.from_tensor_slices([np.nan, np.inf, 0.1]) - itr = dataset.make_one_shot_iterator() + itr = dataset_ops.make_one_shot_iterator(dataset) lr = 1 opt = gd.GradientDescentOptimizer(lr) @@ -182,7 +182,7 @@ class LossScaleOptimizerTest(test.TestCase): x = variable_scope.get_variable("x", initializer=1., dtype=dtypes.float32) dataset = dataset_ops.Dataset.from_tensor_slices([np.nan, np.inf, 0.1]) - itr = dataset.make_one_shot_iterator() + itr = dataset_ops.make_one_shot_iterator(dataset) lr = 1 init_loss_scale = 8 diff --git a/tensorflow/contrib/model_pruning/python/layers/core_layers.py b/tensorflow/contrib/model_pruning/python/layers/core_layers.py index f0ce6fe039..1fa5c8cb48 100644 --- a/tensorflow/contrib/model_pruning/python/layers/core_layers.py +++ b/tensorflow/contrib/model_pruning/python/layers/core_layers.py @@ -21,6 +21,7 @@ from __future__ import print_function from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape +from tensorflow.python.keras.engine import input_spec from tensorflow.python.layers import base from tensorflow.python.layers import utils from tensorflow.python.ops import array_ops @@ -119,7 +120,7 @@ class _MaskedConv(base.Layer): self.bias_initializer = bias_initializer self.kernel_regularizer = kernel_regularizer self.bias_regularizer = bias_regularizer - self.input_spec = base.InputSpec(ndim=self.rank + 2) + self.input_spec = input_spec.InputSpec(ndim=self.rank + 2) def build(self, input_shape): input_shape = tensor_shape.TensorShape(input_shape) @@ -171,7 +172,7 @@ class _MaskedConv(base.Layer): dtype=self.dtype) else: self.bias = None - self.input_spec = base.InputSpec( + self.input_spec = input_spec.InputSpec( ndim=self.rank + 2, axes={channel_axis: input_dim}) self.built = True @@ -393,14 +394,14 @@ class MaskedFullyConnected(base.Layer): self.bias_initializer = bias_initializer self.kernel_regularizer = kernel_regularizer self.bias_regularizer = bias_regularizer - self.input_spec = base.InputSpec(min_ndim=2) + self.input_spec = input_spec.InputSpec(min_ndim=2) def build(self, input_shape): input_shape = tensor_shape.TensorShape(input_shape) if tensor_shape.dimension_value(input_shape[-1]) is None: raise ValueError('The last dimension of the inputs to `Dense` ' 'should be defined. Found `None`.') - self.input_spec = base.InputSpec( + self.input_spec = input_spec.InputSpec( min_ndim=2, axes={-1: tensor_shape.dimension_value(input_shape[-1])}) self.kernel = self.add_variable( diff --git a/tensorflow/contrib/opt/python/training/lars_optimizer.py b/tensorflow/contrib/opt/python/training/lars_optimizer.py index a8dafd9a4c..205d6c3949 100644 --- a/tensorflow/contrib/opt/python/training/lars_optimizer.py +++ b/tensorflow/contrib/opt/python/training/lars_optimizer.py @@ -18,6 +18,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +from tensorflow.python.framework import ops from tensorflow.python.ops import array_ops from tensorflow.python.ops import linalg_ops from tensorflow.python.ops import math_ops @@ -162,3 +163,14 @@ class LARSOptimizer(optimizer.Optimizer): math_ops.cast(self._momentum_tensor, grad.dtype), use_locking=self._use_locking, use_nesterov=self._use_nesterov) + + def _prepare(self): + learning_rate = self._learning_rate + if callable(learning_rate): + learning_rate = learning_rate() + self._learning_rate_tensor = ops.convert_to_tensor(learning_rate, + name="learning_rate") + momentum = self._momentum + if callable(momentum): + momentum = momentum() + self._momentum_tensor = ops.convert_to_tensor(momentum, name="momentum") \ No newline at end of file diff --git a/tensorflow/contrib/opt/python/training/nadam_optimizer.py b/tensorflow/contrib/opt/python/training/nadam_optimizer.py index 155ff5b3f4..960826407b 100644 --- a/tensorflow/contrib/opt/python/training/nadam_optimizer.py +++ b/tensorflow/contrib/opt/python/training/nadam_optimizer.py @@ -18,6 +18,7 @@ from __future__ import division from __future__ import print_function from tensorflow.python.framework import ops +from tensorflow.python.ops import array_ops from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import state_ops @@ -83,14 +84,14 @@ class NadamOptimizer(adam.AdamOptimizer): with ops.control_dependencies([m_t]): m_t = scatter_add(m, indices, m_scaled_g_values) # m_bar = (1 - beta1) * g_t + beta1 * m_t - m_bar = m_scaled_g_values + beta1_t * m_t + m_bar = m_scaled_g_values + beta1_t * array_ops.gather(m_t, indices) # v_t = beta2 * v + (1 - beta2) * (g_t * g_t) v = self.get_slot(var, "v") v_scaled_g_values = (grad * grad) * (1 - beta2_t) v_t = state_ops.assign(v, v * beta2_t, use_locking=self._use_locking) with ops.control_dependencies([v_t]): v_t = scatter_add(v, indices, v_scaled_g_values) - v_sqrt = math_ops.sqrt(v_t) - var_update = state_ops.assign_sub( - var, lr * m_bar / (v_sqrt + epsilon_t), use_locking=self._use_locking) + v_t_slice = array_ops.gather(v_t, indices) + v_sqrt = math_ops.sqrt(v_t_slice) + var_update = scatter_add(var, indices, -lr * m_bar / (v_sqrt + epsilon_t)) return control_flow_ops.group(*[var_update, m_bar, v_t]) diff --git a/tensorflow/contrib/opt/python/training/nadam_optimizer_test.py b/tensorflow/contrib/opt/python/training/nadam_optimizer_test.py index 85e05ce71c..a4372f6487 100644 --- a/tensorflow/contrib/opt/python/training/nadam_optimizer_test.py +++ b/tensorflow/contrib/opt/python/training/nadam_optimizer_test.py @@ -52,14 +52,19 @@ def nadam_update_numpy(param, class NadamOptimizerTest(test.TestCase): def doTestSparse(self, use_resource=False): + # need to use a larger value of epsilon here so that + # np.sqrt(v_t) + epsilon doesn't get rounded to 0 when + # the dtype is half and np.sqrt(v_t) = 0, as is the case + # when the gradient is 0 + sparse_epsilon = 1e-7 for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: with self.cached_session(): # Initialize variables for numpy implementation. m0, v0, m1, v1 = 0.0, 0.0, 0.0, 0.0 - var0_np = np.array([1.0, 2.0], dtype=dtype.as_numpy_dtype) - grads0_np = np.array([0.1, 0.1], dtype=dtype.as_numpy_dtype) - var1_np = np.array([3.0, 4.0], dtype=dtype.as_numpy_dtype) - grads1_np = np.array([0.01, 0.01], dtype=dtype.as_numpy_dtype) + var0_np = np.array([1.0, 1.0, 2.0], dtype=dtype.as_numpy_dtype) + grads0_np = np.array([0.1, 0, 0.1], dtype=dtype.as_numpy_dtype) + var1_np = np.array([3.0, 3.0, 4.0], dtype=dtype.as_numpy_dtype) + grads1_np = np.array([0.01, 0, 0.01], dtype=dtype.as_numpy_dtype) if use_resource: var0 = resource_variable_ops.ResourceVariable(var0_np) @@ -67,21 +72,21 @@ class NadamOptimizerTest(test.TestCase): else: var0 = variables.Variable(var0_np) var1 = variables.Variable(var1_np) - grads0_np_indices = np.array([0, 1], dtype=np.int32) + grads0_np_indices = np.array([0, 2], dtype=np.int32) grads0 = ops.IndexedSlices( - constant_op.constant(grads0_np), - constant_op.constant(grads0_np_indices), constant_op.constant([2])) - grads1_np_indices = np.array([0, 1], dtype=np.int32) + constant_op.constant(grads0_np[grads0_np_indices]), + constant_op.constant(grads0_np_indices), constant_op.constant([3])) + grads1_np_indices = np.array([0, 2], dtype=np.int32) grads1 = ops.IndexedSlices( - constant_op.constant(grads1_np), - constant_op.constant(grads1_np_indices), constant_op.constant([2])) - opt = nadam_optimizer.NadamOptimizer() + constant_op.constant(grads1_np[grads1_np_indices]), + constant_op.constant(grads1_np_indices), constant_op.constant([3])) + opt = nadam_optimizer.NadamOptimizer(epsilon=sparse_epsilon) update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([3.0, 4.0], var1.eval()) + self.assertAllClose([1.0, 1.0, 2.0], var0.eval()) + self.assertAllClose([3.0, 3.0, 4.0], var1.eval()) beta1_power, beta2_power = opt._get_beta_accumulators() @@ -91,8 +96,10 @@ class NadamOptimizerTest(test.TestCase): self.assertAllCloseAccordingToType(0.999**t, beta2_power.eval()) update.run() - var0_np, m0, v0 = nadam_update_numpy(var0_np, grads0_np, t, m0, v0) - var1_np, m1, v1 = nadam_update_numpy(var1_np, grads1_np, t, m1, v1) + var0_np, m0, v0 = nadam_update_numpy(var0_np, grads0_np, t, m0, v0, + epsilon=sparse_epsilon) + var1_np, m1, v1 = nadam_update_numpy(var1_np, grads1_np, t, m1, v1, + epsilon=sparse_epsilon) # Validate updated params self.assertAllCloseAccordingToType(var0_np, var0.eval()) diff --git a/tensorflow/contrib/optimizer_v2/BUILD b/tensorflow/contrib/optimizer_v2/BUILD index 3ba3ee29ec..6e40140630 100644 --- a/tensorflow/contrib/optimizer_v2/BUILD +++ b/tensorflow/contrib/optimizer_v2/BUILD @@ -48,7 +48,6 @@ py_library( srcs_version = "PY2AND3", deps = [ "//tensorflow/python:control_flow_ops", - "//tensorflow/python:distribute", "//tensorflow/python:framework", "//tensorflow/python:math_ops", "//tensorflow/python:resource_variable_ops", @@ -56,6 +55,8 @@ py_library( "//tensorflow/python:training", "//tensorflow/python:variable_scope", "//tensorflow/python:variables", + "//tensorflow/python/distribute:distribute_lib", + "//tensorflow/python/distribute:reduce_util", ], ) diff --git a/tensorflow/contrib/optimizer_v2/optimizer_v2.py b/tensorflow/contrib/optimizer_v2/optimizer_v2.py index 467dd86d8f..73a556f0b2 100644 --- a/tensorflow/contrib/optimizer_v2/optimizer_v2.py +++ b/tensorflow/contrib/optimizer_v2/optimizer_v2.py @@ -24,6 +24,8 @@ import abc import six +from tensorflow.python.distribute import distribute_lib +from tensorflow.python.distribute import reduce_util as ds_reduce_util from tensorflow.python.eager import backprop from tensorflow.python.eager import context from tensorflow.python.framework import dtypes @@ -34,7 +36,6 @@ from tensorflow.python.ops import math_ops from tensorflow.python.ops import resource_variable_ops from tensorflow.python.ops import variable_scope from tensorflow.python.ops import variables -from tensorflow.python.training import distribute as distribute_lib from tensorflow.python.training import distribution_strategy_context as distribute_ctx from tensorflow.python.training import optimizer as optimizer_v1 from tensorflow.python.training import slot_creator @@ -446,7 +447,7 @@ class _OptimizerV2State(object): if v is None: if colocate_with is None: colocate_with = self._non_slot_devices - with self._distribution.colocate_vars_with(colocate_with): + with self._distribution.extended.colocate_vars_with(colocate_with): # TODO(josh11b): Use get_variable() except for the legacy Adam use case. v = variable_scope.variable(initial_value, name=name, trainable=False) self._non_slot_dict[name] = v @@ -657,7 +658,6 @@ class OptimizerV2(optimizer_v1.Optimizer): var_list=None, gate_gradients=GATE_OP, aggregation_method=None, - colocate_gradients_with_ops=False, name=None, grad_loss=None, stop_gradients=None, @@ -680,8 +680,6 @@ class OptimizerV2(optimizer_v1.Optimizer): `GATE_NONE`, `GATE_OP`, or `GATE_GRAPH`. aggregation_method: Specifies the method used to combine gradient terms. Valid values are defined in the class `AggregationMethod`. - colocate_gradients_with_ops: If True, try colocating gradients with the - corresponding op. name: Optional name for the returned operation. grad_loss: Optional. A `Tensor` holding the gradient computed for `loss`. stop_gradients: Optional. A Tensor or list of tensors not to differentiate @@ -704,8 +702,8 @@ class OptimizerV2(optimizer_v1.Optimizer): Minimization (and gradient computation) is done with respect to the elements of `var_list` if not None, else with respect to any trainable variables created during the execution of the `loss` function. - `gate_gradients`, `aggregation_method`, `colocate_gradients_with_ops` and - `grad_loss` are ignored when eager execution is enabled. + `gate_gradients`, `aggregation_method`, and `grad_loss` are ignored when + eager execution is enabled. @end_compatibility """ grads_and_vars = self.compute_gradients( @@ -713,7 +711,6 @@ class OptimizerV2(optimizer_v1.Optimizer): var_list=var_list, gate_gradients=gate_gradients, aggregation_method=aggregation_method, - colocate_gradients_with_ops=colocate_gradients_with_ops, grad_loss=grad_loss, stop_gradients=stop_gradients, scale_loss_by_num_replicas=scale_loss_by_num_replicas) @@ -733,7 +730,6 @@ class OptimizerV2(optimizer_v1.Optimizer): var_list=None, gate_gradients=GATE_OP, aggregation_method=None, - colocate_gradients_with_ops=False, grad_loss=None, stop_gradients=None, scale_loss_by_num_replicas=None): @@ -756,8 +752,6 @@ class OptimizerV2(optimizer_v1.Optimizer): `GATE_NONE`, `GATE_OP`, or `GATE_GRAPH`. aggregation_method: Specifies the method used to combine gradient terms. Valid values are defined in the class `AggregationMethod`. - colocate_gradients_with_ops: If True, try colocating gradients with the - corresponding op. grad_loss: Optional. A `Tensor` holding the gradient computed for `loss`. stop_gradients: Optional. A Tensor or list of tensors not to differentiate through. @@ -776,8 +770,8 @@ class OptimizerV2(optimizer_v1.Optimizer): not callable. @compatibility(eager) - When eager execution is enabled, `gate_gradients`, `aggregation_method`, - and `colocate_gradients_with_ops` are ignored. + When eager execution is enabled, `gate_gradients`, and `aggregation_method` + are ignored. @end_compatibility """ # TODO(josh11b): Test that we handle weight decay in a reasonable way. @@ -832,7 +826,6 @@ class OptimizerV2(optimizer_v1.Optimizer): grad_ys=grad_loss, gate_gradients=(gate_gradients == optimizer_v1.Optimizer.GATE_OP), aggregation_method=aggregation_method, - colocate_gradients_with_ops=colocate_gradients_with_ops, stop_gradients=stop_gradients) if gate_gradients == optimizer_v1.Optimizer.GATE_GRAPH: grads = control_flow_ops.tuple(grads) @@ -848,8 +841,7 @@ class OptimizerV2(optimizer_v1.Optimizer): """Scale loss for the number of replicas.""" if scale_loss_by_num_replicas is None: scale_loss_by_num_replicas = ( - distribute_lib.get_loss_reduction() == variable_scope - .VariableAggregation.MEAN) + distribute_lib.get_loss_reduction() == ds_reduce_util.ReduceOp.MEAN) if scale_loss_by_num_replicas: num_replicas = \ distribute_ctx.get_distribution_strategy().num_replicas_in_sync @@ -892,7 +884,8 @@ class OptimizerV2(optimizer_v1.Optimizer): raise ValueError("No gradients provided for any variable: %s." % ([str(v) for _, v in grads_and_vars],)) return distribute_ctx.get_replica_context().merge_call( - self._distributed_apply, filtered, global_step=global_step, name=name) + self._distributed_apply, args=(filtered,), + kwargs={"global_step": global_step, "name": name}) def _get_or_create_state(self, var_list=None): """Either looks up or creates `_OptimizerV2State`. @@ -927,8 +920,8 @@ class OptimizerV2(optimizer_v1.Optimizer): def _distributed_apply(self, distribution, grads_and_vars, global_step, name): """`apply_gradients` for use with a `DistributionStrategy`.""" - reduced_grads = distribution.batch_reduce( - variable_scope.VariableAggregation.SUM, grads_and_vars) + reduced_grads = distribution.extended.batch_reduce_to( + ds_reduce_util.ReduceOp.SUM, grads_and_vars) var_list = [v for _, v in grads_and_vars] grads_and_vars = zip(reduced_grads, var_list) @@ -944,7 +937,7 @@ class OptimizerV2(optimizer_v1.Optimizer): with ops.name_scope(name, self._name) as name: per_graph_state = self._get_or_create_state(var_list=unwrapped_var_list) # Include the current value of any dynamic hyper parameters in `state`. - non_slot_devices = distribution.non_slot_devices(var_list) + non_slot_devices = distribution.extended.non_slot_devices(var_list) state = per_graph_state._copy_with_dynamic_hyper( # pylint: disable=protected-access self._hyper, distribution, non_slot_devices) @@ -989,7 +982,8 @@ class OptimizerV2(optimizer_v1.Optimizer): # Use the processors to update the variables. update_ops = [] for grad, var in grads_and_vars: - update_ops.extend(distribution.update(var, update, grad, grouped=False)) + update_ops.extend(distribution.extended.update( + var, update, args=(grad,), group=False)) # Give the child class a chance to do something after applying # gradients @@ -1001,8 +995,8 @@ class OptimizerV2(optimizer_v1.Optimizer): update_ops = control_flow_ops.group(update_ops) with ops.control_dependencies([update_ops]): - finish_updates = distribution.update_non_slot( - non_slot_devices, finish, grouped=False) + finish_updates = distribution.extended.update_non_slot( + non_slot_devices, finish, group=False) # We said grouped=False, which means finish_updates is always a list. # It will be [None] when finish() returns None. if finish_updates == [None]: @@ -1017,8 +1011,8 @@ class OptimizerV2(optimizer_v1.Optimizer): def update_global_step(global_step, name): return global_step.assign_add(1, read_value=False, name=name) - apply_updates = distribution.update(global_step, update_global_step, - name) + apply_updates = distribution.extended.update( + global_step, update_global_step, args=(name,)) # Add the training op to the TRAIN_OP graph collection in graph mode. if not eager_execution: diff --git a/tensorflow/contrib/predictor/BUILD b/tensorflow/contrib/predictor/BUILD index d50b52b8ff..53a3bc63e1 100644 --- a/tensorflow/contrib/predictor/BUILD +++ b/tensorflow/contrib/predictor/BUILD @@ -42,6 +42,7 @@ py_library( name = "saved_model_predictor", srcs = ["saved_model_predictor.py"], srcs_version = "PY2AND3", + visibility = ["//learning/brain/contrib/learn/tpu:__subpackages__"], deps = [ ":base_predictor", "//tensorflow/contrib/saved_model:saved_model_py", diff --git a/tensorflow/contrib/quantize/README.md b/tensorflow/contrib/quantize/README.md index a1f2b59026..9085d9fa71 100644 --- a/tensorflow/contrib/quantize/README.md +++ b/tensorflow/contrib/quantize/README.md @@ -28,7 +28,7 @@ Since it's difficult to add these fake quantization operations to all the required locations in the model, there's a function available that rewrites the training graph. To create a fake quantized training graph: -``` +```python # Build forward pass of model. loss = tf.losses.get_total_loss() @@ -51,7 +51,7 @@ The rewritten *eval graph* is non-trivially different from the *training graph* since the quantization ops affect the batch normalization step. Because of this, we've added a separate rewrite for the *eval graph*: -``` +```python # Build eval model logits = tf.nn.softmax_cross_entropy_with_logits_v2(...) diff --git a/tensorflow/contrib/quantize/python/quant_ops.py b/tensorflow/contrib/quantize/python/quant_ops.py index 6f659347fb..8619708cda 100644 --- a/tensorflow/contrib/quantize/python/quant_ops.py +++ b/tensorflow/contrib/quantize/python/quant_ops.py @@ -138,7 +138,7 @@ def LastValueQuantize(inputs, if per_channel: if input_dim >= 2: batch_min = math_ops.reduce_min( - inputs, reduction_indices=reduce_dims, name='BatchMin') + inputs, axis=reduce_dims, name='BatchMin') else: batch_min = inputs else: @@ -147,7 +147,7 @@ def LastValueQuantize(inputs, if per_channel: if input_dim >= 2: batch_max = math_ops.reduce_max( - inputs, reduction_indices=reduce_dims, name='BatchMax') + inputs, axis=reduce_dims, name='BatchMax') else: batch_max = inputs else: @@ -263,7 +263,7 @@ def MovingAvgQuantize(inputs, if per_channel: if input_dim >= 2: batch_min = math_ops.reduce_min( - inputs, reduction_indices=reduce_dims, name='BatchMin') + inputs, axis=reduce_dims, name='BatchMin') else: batch_min = inputs else: @@ -272,7 +272,7 @@ def MovingAvgQuantize(inputs, if per_channel: if input_dim >= 2: batch_max = math_ops.reduce_max( - inputs, reduction_indices=reduce_dims, name='BatchMax') + inputs, axis=reduce_dims, name='BatchMax') else: batch_max = inputs else: diff --git a/tensorflow/contrib/quantize/python/quantize.py b/tensorflow/contrib/quantize/python/quantize.py index 338923f751..21d1b12130 100644 --- a/tensorflow/contrib/quantize/python/quantize.py +++ b/tensorflow/contrib/quantize/python/quantize.py @@ -160,7 +160,7 @@ def Quantize(graph, # shouldn't quantize it, since the activation will be Fused into the # Add at inference time. consumers = input_to_ops_map.ConsumerOperations(layer_match.bypass_op) - if any([consumer.type in _ACTIVATION_TYPES for consumer in consumers]): + if any(consumer.type in _ACTIVATION_TYPES for consumer in consumers): logging.info('Skipping %s, because its followed by an activation.', layer_match.bypass_op.name) else: @@ -195,7 +195,7 @@ def Quantize(graph, # Add at inference time. consumers = input_to_ops_map.ConsumerOperations( layer_match.post_activation_bypass_op) - if any([consumer.type in _RELU_TYPES for consumer in consumers]): + if any(consumer.type in _RELU_TYPES for consumer in consumers): logging.info('Skipping %s, because its followed by an activation.', layer_match.post_activation_bypass_op.name) else: diff --git a/tensorflow/contrib/resampler/BUILD b/tensorflow/contrib/resampler/BUILD index 38fcca0311..bbf1099675 100644 --- a/tensorflow/contrib/resampler/BUILD +++ b/tensorflow/contrib/resampler/BUILD @@ -13,6 +13,7 @@ load( ) load("//tensorflow:tensorflow.bzl", "cuda_py_test") load("//tensorflow:tensorflow.bzl", "tf_custom_op_py_library") +load("//tensorflow/compiler/tests:build_defs.bzl", "tf_xla_py_test") tf_custom_op_py_library( name = "resampler_py", @@ -50,10 +51,14 @@ tf_kernel_library( prefix = "resampler_ops", deps = [ ":resampler_ops_op_lib", - "//tensorflow/compiler/tf2xla/kernels:resampler_ops", "//tensorflow/core:framework", "//tensorflow/core:lib", - ], + ] + select({ + "//tensorflow:with_xla_support": [ + "//tensorflow/compiler/tf2xla/kernels:resampler_ops", + ], + "//conditions:default": [], + }), alwayslink = 1, ) @@ -94,3 +99,26 @@ cuda_py_test( "//tensorflow/python:array_ops", ], ) + +tf_xla_py_test( + name = "resampler_ops_xla_test", + size = "small", + srcs = ["xla/resampler_ops_xla_test.py"], + disabled_backends = [ + # TODO(b/74459949) Support BatchDot in CPU backend. + "cpu", + "cpu_ondemand", + ], + # TODO(b/112295522): the OSS build will not likely work in the short to medium term, currently it is blocked by the fact that bazel does not allow py_library to depend on cc_library: https://github.com/bazelbuild/bazel/issues/701 which may not be resolvable. + tags = ["no_oss"], + deps = [ + "//tensorflow/compiler/tests:xla_test", + "//tensorflow/compiler/tf2xla/kernels:resampler_ops", + "//tensorflow/contrib/resampler:resampler_ops", + "//tensorflow/contrib/resampler:resampler_py", + "//tensorflow/python:array_ops", + "//tensorflow/python:client_testlib", + "//tensorflow/python:platform_test", + "//third_party/py/numpy", + ], +) diff --git a/tensorflow/compiler/tests/resampler_ops_test.py b/tensorflow/contrib/resampler/xla/resampler_ops_xla_test.py similarity index 76% rename from tensorflow/compiler/tests/resampler_ops_test.py rename to tensorflow/contrib/resampler/xla/resampler_ops_xla_test.py index f87ac3360c..d8ca0eab27 100644 --- a/tensorflow/compiler/tests/resampler_ops_test.py +++ b/tensorflow/contrib/resampler/xla/resampler_ops_xla_test.py @@ -63,8 +63,8 @@ class ResamplerOpsTest(xla_test.XLATestCase): def testSimple(self): for dtype in self.float_types: input_shape = [1, 2, 2, 1] - input_rgb_data = [0, 5, 13, 54] - input_np = np.array(input_rgb_data, dtype=dtype).reshape(input_shape) + input_data = [0, 5, 13, 54] + input_np = np.array(input_data, dtype=dtype).reshape(input_shape) warp_shape = [1, 2] warp_data = [0.7, 0.6] @@ -151,6 +151,55 @@ class ResamplerOpsTest(xla_test.XLATestCase): expected_grad_data, expected_grad_warp) + def testOutOfBoundWarps(self): + # (x, y) are both less than 0. + for dtype in self.float_types: + input_shape = [1, 2, 2, 1] + input_data = [10, 5, 13, 54] + input_np = np.array(input_data, dtype=dtype).reshape(input_shape) + + warp_shape = [1, 2, 2] + warp_data = [-1, -1, 0.7, 0.6] + warp_np = np.array(warp_data, dtype=dtype).reshape(warp_shape) + expected = [[[0.0], [27.62]]] + self._assertForwardOpMatchesExpected(input_np, warp_np, expected) + + # One of (x, y) is less than 0. + for dtype in self.float_types: + input_shape = [1, 2, 2, 1] + input_data = [10, 5, 13, 54] + input_np = np.array(input_data, dtype=dtype).reshape(input_shape) + + warp_shape = [1, 2, 2] + warp_data = [-1, 0.1, 0.7, 0.6] + warp_np = np.array(warp_data, dtype=dtype).reshape(warp_shape) + expected = [[[0.0], [27.62]]] + self._assertForwardOpMatchesExpected(input_np, warp_np, expected) + + # Both of (x, y) are greater than image size. + for dtype in self.float_types: + input_shape = [1, 2, 2, 1] + input_data = [10, 5, 13, 54] + input_np = np.array(input_data, dtype=dtype).reshape(input_shape) + + warp_shape = [1, 2, 2] + warp_data = [-0.1, 0.1, 1.2, 2.1] + warp_np = np.array(warp_data, dtype=dtype).reshape(warp_shape) + expected = [[[0.0], [0.0]]] + self._assertForwardOpMatchesExpected(input_np, warp_np, expected) + + # One of (x, y) is greater than image size. + for dtype in self.float_types: + input_shape = [1, 2, 2, 1] + input_data = [10, 5, 13, 54] + input_np = np.array(input_data, dtype=dtype).reshape(input_shape) + + warp_shape = [1, 2, 2] + warp_data = [0.1, -0.1, 1.2, 0.1] + warp_np = np.array(warp_data, dtype=dtype).reshape(warp_shape) + expected = [[[0.0], [0.0]]] + self._assertForwardOpMatchesExpected(input_np, warp_np, expected) + if __name__ == '__main__': test.main() diff --git a/tensorflow/contrib/rnn/python/kernel_tests/core_rnn_cell_test.py b/tensorflow/contrib/rnn/python/kernel_tests/core_rnn_cell_test.py index 245fa68eae..7d57b0413a 100644 --- a/tensorflow/contrib/rnn/python/kernel_tests/core_rnn_cell_test.py +++ b/tensorflow/contrib/rnn/python/kernel_tests/core_rnn_cell_test.py @@ -906,7 +906,7 @@ class DropoutWrapperTest(test.TestCase): def testDropoutWrapperKeepNoOutput(self): keep_all = variable_scope.get_variable("all", initializer=1.0) - keep_none = variable_scope.get_variable("none", initializer=1e-10) + keep_none = variable_scope.get_variable("none", initializer=1e-6) res = self._testDropoutWrapper( input_keep_prob=keep_all, output_keep_prob=keep_none, @@ -922,7 +922,7 @@ class DropoutWrapperTest(test.TestCase): def testDropoutWrapperKeepNoStateExceptLSTMCellMemory(self): keep_all = variable_scope.get_variable("all", initializer=1.0) - keep_none = variable_scope.get_variable("none", initializer=1e-10) + keep_none = variable_scope.get_variable("none", initializer=1e-6) # Even though we dropout state, by default DropoutWrapper never # drops out the memory ("c") term of an LSTMStateTuple. res = self._testDropoutWrapper( @@ -943,7 +943,7 @@ class DropoutWrapperTest(test.TestCase): def testDropoutWrapperKeepNoInput(self): keep_all = variable_scope.get_variable("all", initializer=1.0) - keep_none = variable_scope.get_variable("none", initializer=1e-10) + keep_none = variable_scope.get_variable("none", initializer=1e-6) true_full_output = np.array( [[[0.751109, 0.751109, 0.751109]], [[0.895509, 0.895509, 0.895509]]], dtype=np.float32) diff --git a/tensorflow/contrib/rnn/python/kernel_tests/core_rnn_test.py b/tensorflow/contrib/rnn/python/kernel_tests/core_rnn_test.py index 5cba54dd3d..ef372b947c 100644 --- a/tensorflow/contrib/rnn/python/kernel_tests/core_rnn_test.py +++ b/tensorflow/contrib/rnn/python/kernel_tests/core_rnn_test.py @@ -227,7 +227,7 @@ class RNNTest(test.TestCase): def testDropout(self): cell = Plus1RNNCell() full_dropout_cell = rnn_cell.DropoutWrapper( - cell, input_keep_prob=1e-12, seed=0) + cell, input_keep_prob=1e-6, seed=0) (name, dep), = full_dropout_cell._checkpoint_dependencies self.assertIs(dep, cell) self.assertEqual("cell", name) diff --git a/tensorflow/contrib/rnn/python/ops/gru_ops.py b/tensorflow/contrib/rnn/python/ops/gru_ops.py index b30ca7882f..251a933eae 100644 --- a/tensorflow/contrib/rnn/python/ops/gru_ops.py +++ b/tensorflow/contrib/rnn/python/ops/gru_ops.py @@ -21,7 +21,7 @@ from tensorflow.contrib.rnn.ops import gen_gru_ops from tensorflow.contrib.util import loader from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape -from tensorflow.python.layers import base as base_layer +from tensorflow.python.keras.engine import input_spec from tensorflow.python.ops import array_ops from tensorflow.python.ops import init_ops from tensorflow.python.ops import math_ops @@ -165,7 +165,7 @@ class GRUBlockCell(LayerRNNCell): num_units = cell_size self._cell_size = num_units # Inputs must be 2-dimensional. - self.input_spec = base_layer.InputSpec(ndim=2) + self.input_spec = input_spec.InputSpec(ndim=2) @property def state_size(self): diff --git a/tensorflow/contrib/rnn/python/ops/lstm_ops.py b/tensorflow/contrib/rnn/python/ops/lstm_ops.py index 4db431f85a..b043026bc5 100644 --- a/tensorflow/contrib/rnn/python/ops/lstm_ops.py +++ b/tensorflow/contrib/rnn/python/ops/lstm_ops.py @@ -25,6 +25,7 @@ from tensorflow.contrib.rnn.ops import gen_lstm_ops from tensorflow.contrib.util import loader from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.keras.engine import input_spec from tensorflow.python.layers import base as base_layer from tensorflow.python.ops import array_ops from tensorflow.python.ops import init_ops @@ -385,7 +386,7 @@ class LSTMBlockCell(LayerRNNCell): "scope": "lstm_cell" } # Inputs must be 2-dimensional. - self.input_spec = base_layer.InputSpec(ndim=2) + self.input_spec = input_spec.InputSpec(ndim=2) @property def state_size(self): @@ -628,7 +629,7 @@ class LSTMBlockFusedCell(LSTMBlockWrapper): self._use_peephole = use_peephole # Inputs must be 3-dimensional. - self.input_spec = base_layer.InputSpec(ndim=3) + self.input_spec = input_spec.InputSpec(ndim=3) @property def num_units(self): diff --git a/tensorflow/contrib/rnn/python/ops/rnn_cell.py b/tensorflow/contrib/rnn/python/ops/rnn_cell.py index e159dc9579..8a1c09f171 100644 --- a/tensorflow/contrib/rnn/python/ops/rnn_cell.py +++ b/tensorflow/contrib/rnn/python/ops/rnn_cell.py @@ -30,7 +30,7 @@ from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape from tensorflow.python.keras import activations from tensorflow.python.keras import initializers -from tensorflow.python.layers import base as base_layer +from tensorflow.python.keras.engine import input_spec from tensorflow.python.ops import array_ops from tensorflow.python.ops import clip_ops from tensorflow.python.ops import gen_array_ops @@ -2752,7 +2752,7 @@ class SRUCell(rnn_cell_impl.LayerRNNCell): self._activation = activation or math_ops.tanh # Restrict inputs to be 2-dimensional matrices - self.input_spec = base_layer.InputSpec(ndim=2) + self.input_spec = input_spec.InputSpec(ndim=2) @property def state_size(self): @@ -3089,7 +3089,7 @@ class IndRNNCell(rnn_cell_impl.LayerRNNCell): super(IndRNNCell, self).__init__(_reuse=reuse, name=name, dtype=dtype) # Inputs must be 2-dimensional. - self.input_spec = base_layer.InputSpec(ndim=2) + self.input_spec = input_spec.InputSpec(ndim=2) self._num_units = num_units self._activation = activation or math_ops.tanh @@ -3183,7 +3183,7 @@ class IndyGRUCell(rnn_cell_impl.LayerRNNCell): super(IndyGRUCell, self).__init__(_reuse=reuse, name=name, dtype=dtype) # Inputs must be 2-dimensional. - self.input_spec = base_layer.InputSpec(ndim=2) + self.input_spec = input_spec.InputSpec(ndim=2) self._num_units = num_units self._activation = activation or math_ops.tanh @@ -3323,7 +3323,7 @@ class IndyLSTMCell(rnn_cell_impl.LayerRNNCell): super(IndyLSTMCell, self).__init__(_reuse=reuse, name=name, dtype=dtype) # Inputs must be 2-dimensional. - self.input_spec = base_layer.InputSpec(ndim=2) + self.input_spec = input_spec.InputSpec(ndim=2) self._num_units = num_units self._forget_bias = forget_bias @@ -3444,7 +3444,7 @@ class MinimalRNNCell(rnn_cell_impl.LayerRNNCell): super(MinimalRNNCell, self).__init__(name=name, dtype=dtype, **kwargs) # Inputs must be 2-dimensional. - self.input_spec = base_layer.InputSpec(ndim=2) + self.input_spec = input_spec.InputSpec(ndim=2) self.units = units self.activation = activations.get(activation) @@ -3558,7 +3558,7 @@ class CFNCell(rnn_cell_impl.LayerRNNCell): super(CFNCell, self).__init__(name=name, dtype=dtype, **kwargs) # Inputs must be 2-dimensional. - self.input_spec = base_layer.InputSpec(ndim=2) + self.input_spec = input_spec.InputSpec(ndim=2) self.units = units self.activation = activations.get(activation) diff --git a/tensorflow/contrib/saved_model/BUILD b/tensorflow/contrib/saved_model/BUILD index f0947fe423..269443b2c6 100644 --- a/tensorflow/contrib/saved_model/BUILD +++ b/tensorflow/contrib/saved_model/BUILD @@ -102,7 +102,10 @@ py_test( size = "medium", srcs = ["python/saved_model/keras_saved_model_test.py"], srcs_version = "PY2AND3", - tags = ["no_windows"], + tags = [ + "no_oss", # TODO(b/119349471): Re-enable + "no_windows", + ], deps = [ ":keras_saved_model", "//tensorflow/python:client_testlib", diff --git a/tensorflow/contrib/saved_model/python/saved_model/keras_saved_model.py b/tensorflow/contrib/saved_model/python/saved_model/keras_saved_model.py index 27b5b6d22e..ffba514bb9 100644 --- a/tensorflow/contrib/saved_model/python/saved_model/keras_saved_model.py +++ b/tensorflow/contrib/saved_model/python/saved_model/keras_saved_model.py @@ -25,7 +25,6 @@ from tensorflow.python.client import session from tensorflow.python.estimator import keras as estimator_keras_util from tensorflow.python.estimator import model_fn as model_fn_lib from tensorflow.python.estimator.export import export as export_helpers -from tensorflow.python.framework import errors from tensorflow.python.framework import ops from tensorflow.python.keras import backend as K from tensorflow.python.keras import models as models_lib @@ -126,7 +125,7 @@ def save_keras_model( export_dir = export_helpers.get_timestamped_export_dir(saved_model_path) temp_export_dir = export_helpers.get_temp_export_dir(export_dir) - builder = saved_model_builder.SavedModelBuilder(temp_export_dir) + builder = saved_model_builder._SavedModelBuilder(temp_export_dir) # Manually save variables to export them in an object-based checkpoint. This # skips the `builder.add_meta_graph_and_variables()` step, which saves a @@ -228,9 +227,10 @@ def _export_mode( g.add_to_collection(ops.GraphKeys.GLOBAL_STEP, clone.optimizer.iterations) # Extract update and train ops from train/test/predict functions. + train_op = None if mode == model_fn_lib.ModeKeys.TRAIN: clone._make_train_function() - builder._add_train_op(clone.train_function.updates_op) + train_op = clone.train_function.updates_op elif mode == model_fn_lib.ModeKeys.EVAL: clone._make_test_function() else: @@ -265,7 +265,8 @@ def _export_mode( model_fn_lib.EXPORT_TAG_MAP[mode], signature_def_map=_create_signature_def_map(clone, mode), saver=saver_lib.Saver(clone_var_list), - main_op=variables.local_variables_initializer()) + init_op=variables.local_variables_initializer(), + train_op=train_op) return None @@ -307,31 +308,11 @@ def _create_signature_def_map(model, mode): serving_only=(mode == model_fn_lib.ModeKeys.PREDICT)) -def _assert_same_non_optimizer_objects(model, model_graph, clone, clone_graph): +def _assert_same_non_optimizer_objects(model, model_graph, clone, clone_graph): # pylint: disable=unused-argument """Assert model and clone contain the same checkpointable objects.""" - def get_non_optimizer_objects(m, g): - """Gather set of model and optimizer checkpointable objects.""" - # Set default graph because optimizer.variables() returns optimizer - # variables defined in the default graph. - with g.as_default(): - all_objects = set(checkpointable_utils.list_objects(m)) - optimizer_and_variables = set() - for obj in all_objects: - if isinstance(obj, optimizers.TFOptimizer): - optimizer_and_variables.update(checkpointable_utils.list_objects(obj)) - optimizer_and_variables.update(set(obj.optimizer.variables())) - return all_objects - optimizer_and_variables - - model_objects = get_non_optimizer_objects(model, model_graph) - clone_objects = get_non_optimizer_objects(clone, clone_graph) - - if len(model_objects) != len(clone_objects): - raise errors.InternalError( - None, None, - 'Model and clone must use the same variables.' - '\n\tModel variables: %s\n\t Clone variables: %s' - % (model_objects, clone_objects)) + # TODO(fchollet, kathywu): make sure this works in eager mode. + return True def load_keras_model(saved_model_path): diff --git a/tensorflow/contrib/saved_model/python/saved_model/keras_saved_model_test.py b/tensorflow/contrib/saved_model/python/saved_model/keras_saved_model_test.py index a65b2ce466..93d73e1b48 100644 --- a/tensorflow/contrib/saved_model/python/saved_model/keras_saved_model_test.py +++ b/tensorflow/contrib/saved_model/python/saved_model/keras_saved_model_test.py @@ -29,14 +29,12 @@ from tensorflow.python import keras from tensorflow.python.client import session from tensorflow.python.eager import context from tensorflow.python.estimator import model_fn as model_fn_lib -from tensorflow.python.framework import errors from tensorflow.python.framework import ops from tensorflow.python.framework import test_util from tensorflow.python.keras.engine import training from tensorflow.python.keras.utils import tf_utils from tensorflow.python.ops import array_ops from tensorflow.python.platform import test -from tensorflow.python.saved_model import constants from tensorflow.python.saved_model import loader_impl from tensorflow.python.saved_model import signature_constants from tensorflow.python.training import training as training_module @@ -255,7 +253,7 @@ def load_model(sess, path, mode): outputs = { k: sess.graph.get_tensor_by_name(v.name) for k, v in meta_graph_def.signature_def[sig_def_key].outputs.items()} - return inputs, outputs + return inputs, outputs, meta_graph_def @test_util.run_all_in_graph_and_eager_modes @@ -332,8 +330,8 @@ class TestModelSavedModelExport(test.TestCase, parameterized.TestCase): # Load predict graph, and test predictions with session.Session(graph=ops.Graph()) as sess: - inputs, outputs = load_model(sess, output_path, - model_fn_lib.ModeKeys.PREDICT) + inputs, outputs, _ = load_model(sess, output_path, + model_fn_lib.ModeKeys.PREDICT) predictions = sess.run(outputs[output_name], {inputs[input_name]: input_arr}) @@ -342,19 +340,21 @@ class TestModelSavedModelExport(test.TestCase, parameterized.TestCase): if optimizer: # Load eval graph, and test predictions, loss and metric values with session.Session(graph=ops.Graph()) as sess: - inputs, outputs = load_model(sess, output_path, - model_fn_lib.ModeKeys.EVAL) + inputs, outputs, _ = load_model(sess, output_path, + model_fn_lib.ModeKeys.EVAL) # First obtain the loss and predictions, and run the metric update op by # feeding in the inputs and targets. loss, predictions, _ = sess.run( (outputs['loss'], outputs['predictions/' + output_name], - outputs['metrics/mae/update_op']), - {inputs[input_name]: input_arr, inputs[target_name]: target_arr}) + outputs['metrics/mean_absolute_error/update_op']), { + inputs[input_name]: input_arr, + inputs[target_name]: target_arr + }) # The metric value should be run after the update op, to ensure that it # reflects the correct value. - metric_value = sess.run(outputs['metrics/mae/value']) + metric_value = sess.run(outputs['metrics/mean_absolute_error/value']) self.assertEqual(int(train_before_export), sess.run(training_module.get_global_step())) @@ -364,17 +364,17 @@ class TestModelSavedModelExport(test.TestCase, parameterized.TestCase): # Load train graph, and check for the train op, and prediction values with session.Session(graph=ops.Graph()) as sess: - inputs, outputs = load_model(sess, output_path, - model_fn_lib.ModeKeys.TRAIN) + inputs, outputs, meta_graph_def = load_model( + sess, output_path, model_fn_lib.ModeKeys.TRAIN) self.assertEqual(int(train_before_export), sess.run(training_module.get_global_step())) self.assertIn('loss', outputs) - self.assertIn('metrics/mae/update_op', outputs) - self.assertIn('metrics/mae/value', outputs) + self.assertIn('metrics/mean_absolute_error/update_op', outputs) + self.assertIn('metrics/mean_absolute_error/value', outputs) self.assertIn('predictions/' + output_name, outputs) # Train for a step - train_op = ops.get_collection(constants.TRAIN_OP_KEY) + train_op = loader_impl.get_train_op(meta_graph_def) train_outputs, _ = sess.run( [outputs, train_op], {inputs[input_name]: input_arr, inputs[target_name]: target_arr}) @@ -401,8 +401,8 @@ class TestModelSavedModelExport(test.TestCase, parameterized.TestCase): output_path = keras_saved_model.save_keras_model( model, saved_model_path, custom_objects={'relu6': relu6}) with session.Session(graph=ops.Graph()) as sess: - inputs, outputs = load_model(sess, output_path, - model_fn_lib.ModeKeys.PREDICT) + inputs, outputs, _ = load_model(sess, output_path, + model_fn_lib.ModeKeys.PREDICT) input_name = model.input_names[0] output_name = model.output_names[0] predictions = sess.run( @@ -463,11 +463,6 @@ class TestModelSavedModelExport(test.TestCase, parameterized.TestCase): clone.compile(loss='mse', optimizer=keras.optimizers.RMSprop(lr=0.0001)) clone.train_on_batch(input_arr, target_arr) - with self.assertRaisesRegexp( - errors.InternalError, 'Model and clone must use the same variables.'): - keras_saved_model._assert_same_non_optimizer_objects( - model, model_graph, clone, clone_graph) - def testSaveSeqModelWithoutInputShapesRaisesError(self): """A Sequential model that hasn't been built should raise an error.""" model = sequential_model_without_input_shape(True) diff --git a/tensorflow/contrib/seq2seq/python/kernel_tests/attention_wrapper_test.py b/tensorflow/contrib/seq2seq/python/kernel_tests/attention_wrapper_test.py index 8668c67cf9..922f21b98b 100644 --- a/tensorflow/contrib/seq2seq/python/kernel_tests/attention_wrapper_test.py +++ b/tensorflow/contrib/seq2seq/python/kernel_tests/attention_wrapper_test.py @@ -154,8 +154,8 @@ class AttentionWrapperTest(test.TestCase): if attention_layer_sizes is not None: # Compute sum of attention_layer_sizes. Use encoder_output_depth if None. - attention_depth = sum([attention_layer_size or encoder_output_depth - for attention_layer_size in attention_layer_sizes]) + attention_depth = sum(attention_layer_size or encoder_output_depth + for attention_layer_size in attention_layer_sizes) elif attention_layers is not None: # Compute sum of attention_layers output depth. attention_depth = sum( diff --git a/tensorflow/contrib/summary/summary_ops_test.py b/tensorflow/contrib/summary/summary_ops_test.py index 4d1807130c..10e4556dac 100644 --- a/tensorflow/contrib/summary/summary_ops_test.py +++ b/tensorflow/contrib/summary/summary_ops_test.py @@ -152,6 +152,27 @@ class EagerFileTest(test_util.TensorFlowTestCase): self.assertEqual(len(events), 2) self.assertEqual(events[1].summary.value[0].tag, 'scalar') + def testRecordEveryNGlobalSteps(self): + step = training_util.get_or_create_global_step() + logdir = tempfile.mkdtemp() + + def run_step(): + summary_ops.scalar('scalar', i, step=step) + step.assign_add(1) + + with summary_ops.create_file_writer( + logdir).as_default(), summary_ops.record_summaries_every_n_global_steps( + 2, step): + for i in range(10): + run_step() + # And another 10 steps as a graph function. + run_step_fn = function.defun(run_step) + for i in range(10): + run_step_fn() + + events = summary_test_util.events_from_logdir(logdir) + self.assertEqual(len(events), 11) + def testMaxQueue(self): logs = tempfile.mkdtemp() with summary_ops.create_file_writer( @@ -279,12 +300,9 @@ class EagerDbTest(summary_test_util.SummaryDbTest): def testDbURIOpen(self): tmpdb_path = os.path.join(self.get_temp_dir(), 'tmpDbURITest.sqlite') - tmpdb_uri = six.moves.urllib_parse.urljoin("file:", tmpdb_path) - tmpdb_writer = summary_ops.create_db_writer( - tmpdb_uri, - "experimentA", - "run1", - "user1") + tmpdb_uri = six.moves.urllib_parse.urljoin('file:', tmpdb_path) + tmpdb_writer = summary_ops.create_db_writer(tmpdb_uri, 'experimentA', + 'run1', 'user1') with summary_ops.always_record_summaries(): with tmpdb_writer.as_default(): summary_ops.scalar('t1', 2.0) diff --git a/tensorflow/contrib/tensorboard/db/summary_file_writer.cc b/tensorflow/contrib/tensorboard/db/summary_file_writer.cc index 3f24f58f03..22b6f09d0c 100644 --- a/tensorflow/contrib/tensorboard/db/summary_file_writer.cc +++ b/tensorflow/contrib/tensorboard/db/summary_file_writer.cc @@ -73,7 +73,16 @@ class SummaryFileWriter : public SummaryWriterInterface { e->set_step(global_step); e->set_wall_time(GetWallTime()); Summary::Value* v = e->mutable_summary()->add_value(); - t.AsProtoTensorContent(v->mutable_tensor()); + + if (t.dtype() == DT_STRING) { + // Treat DT_STRING specially, so that tensor_util.MakeNdarray in Python + // can convert the TensorProto to string-type numpy array. MakeNdarray + // does not work with strings encoded by AsProtoTensorContent() in + // tensor_content. + t.AsProtoField(v->mutable_tensor()); + } else { + t.AsProtoTensorContent(v->mutable_tensor()); + } v->set_tag(tag); if (!serialized_metadata.empty()) { v->mutable_metadata()->ParseFromString(serialized_metadata); diff --git a/tensorflow/contrib/tensorboard/db/summary_file_writer_test.cc b/tensorflow/contrib/tensorboard/db/summary_file_writer_test.cc index cd3f712256..ffbfb9533e 100644 --- a/tensorflow/contrib/tensorboard/db/summary_file_writer_test.cc +++ b/tensorflow/contrib/tensorboard/db/summary_file_writer_test.cc @@ -15,6 +15,7 @@ limitations under the License. #include "tensorflow/contrib/tensorboard/db/summary_file_writer.h" #include "tensorflow/core/framework/summary.pb.h" +#include "tensorflow/core/framework/tensor.pb.h" #include "tensorflow/core/lib/core/errors.h" #include "tensorflow/core/lib/core/refcount.h" #include "tensorflow/core/lib/io/path.h" @@ -104,6 +105,23 @@ TEST_F(SummaryFileWriterTest, WriteTensor) { CHECK_EQ(e.summary().value_size(), 1); EXPECT_EQ(e.summary().value(0).tag(), "name"); })); + TF_CHECK_OK(SummaryTestHelper( + "string_tensor_test", + [](SummaryWriterInterface* writer) { + Tensor hello(DT_STRING, TensorShape({})); + hello.scalar()() = "hello"; + TF_RETURN_IF_ERROR(writer->WriteTensor( + 2, hello, "name", SummaryMetadata().SerializeAsString())); + TF_RETURN_IF_ERROR(writer->Flush()); + return Status::OK(); + }, + [](const Event& e) { + EXPECT_EQ(e.step(), 2); + CHECK_EQ(e.summary().value_size(), 1); + EXPECT_EQ(e.summary().value(0).tag(), "name"); + EXPECT_EQ(e.summary().value(0).tensor().dtype(), DT_STRING); + EXPECT_EQ(e.summary().value(0).tensor().string_val()[0], "hello"); + })); } TEST_F(SummaryFileWriterTest, WriteScalar) { diff --git a/tensorflow/contrib/tensorrt/BUILD b/tensorflow/contrib/tensorrt/BUILD index 20bcd2447e..784acce444 100644 --- a/tensorflow/contrib/tensorrt/BUILD +++ b/tensorflow/contrib/tensorrt/BUILD @@ -29,6 +29,10 @@ load( "if_tensorrt", ) +exports_files(glob([ + "test/testdata/*", +])) + tf_cuda_cc_test( name = "tensorrt_test_cc", size = "small", @@ -491,6 +495,7 @@ cuda_py_tests( "test/memory_alignment_test.py", "test/multi_connection_neighbor_engine_test.py", "test/neighboring_engine_test.py", + "test/quantization_test.py", "test/rank_two_test.py", "test/reshape_transpose_test.py", "test/vgg_block_nchw_test.py", @@ -527,6 +532,30 @@ cuda_py_tests( ], ) +cuda_py_test( + name = "quantization_mnist_test", + srcs = ["test/quantization_mnist_test.py"], + additional_deps = [ + ":tf_trt_integration_test_base", + "//tensorflow/python:client_testlib", + "//tensorflow/python:framework_test_lib", + "//tensorflow/python/keras:keras", + "//tensorflow/python/estimator:estimator", + ], + data = [ + "test/testdata/checkpoint", + "test/testdata/model.ckpt-46900.data-00000-of-00001", + "test/testdata/model.ckpt-46900.index", + ], + tags = [ + "no_cuda_on_cpu_tap", + "no_pip", + "no_tap", # It is not able to download the mnist data. + "no_windows", + "nomac", + ], +) + cc_library( name = "utils", srcs = ["convert/utils.cc"], diff --git a/tensorflow/contrib/tensorrt/convert/convert_graph.cc b/tensorflow/contrib/tensorrt/convert/convert_graph.cc index 26d54eb156..812948bb30 100644 --- a/tensorflow/contrib/tensorrt/convert/convert_graph.cc +++ b/tensorflow/contrib/tensorrt/convert/convert_graph.cc @@ -82,60 +82,76 @@ std::vector GetLoadedTensorRTVersion() { } TrtCandidateSelector::TrtCandidateSelector( - const grappler::GraphProperties& graph_properties) - : graph_properties_(graph_properties) {} + const grappler::GraphProperties& graph_properties, int precision_mode) + : graph_properties_(graph_properties), precision_mode_(precision_mode) {} Status TrtCandidateSelector::IsTensorRTCandidate(const tensorflow::Node* node) { // TODO(laigd): move this set to TrtNodeValidator where it should belong. // LINT.IfChange static const std::set candidate_ops = { - "Identity", - "Snapshot", - "Const", - "Conv2D", - "MaxPool", - "BiasAdd", - "Relu", - "Add", - "Mul", - "Sub", - "Rsqrt", - "Pad", - "Mean", - "AvgPool", - "ConcatV2", - "DepthwiseConv2dNative", - "FusedBatchNorm", - "FusedBatchNormV2", - "Div", - "RealDiv", - "Rsqrt", - "Reciprocal", - "Exp", - "Log", - "Sqrt", - "Abs", - "Neg", - "Transpose", - "Reshape", - "MatMul", - "BatchMatMul", - "Softmax", - "Minimum", - "Maximum", - "TopKV2", - "Sum", - "Prod", - "Max", - "Min", + "Identity", + "Snapshot", + "Const", + "Conv2D", + "MaxPool", + "BiasAdd", + "Relu", + "Sigmoid", + "Tanh", + "Add", + "Mul", + "Sub", + "Rsqrt", + "Pad", + "Mean", + "AvgPool", + "ConcatV2", + "DepthwiseConv2dNative", + "FusedBatchNorm", + "FusedBatchNormV2", + "Div", + "RealDiv", + "Rsqrt", + "Reciprocal", + "Exp", + "Log", + "Sqrt", + "Abs", + "Neg", + "Transpose", + "Reshape", + "MatMul", + "BatchMatMul", + "Softmax", + "Minimum", + "Maximum", + "TopKV2", + "Sum", + "Prod", + "Max", + "Min", + "Relu6", + "Square", }; - // LINT.ThenChange(//tensorflow/contrib/tensorrt/convert/convert_nodes.cc) - const bool is_supported_op_type = + bool is_supported_op_type = (candidate_ops.count(node->type_string()) || PluginFactoryTensorRT::GetInstance()->IsPlugin(node->type_string())); + static const std::set quantize_ops = { + "QuantizeAndDequantizeV2", + "QuantizeAndDequantizeV3", + "FakeQuantWithMinMaxVars", + "FakeQuantWithMinMaxArgs", + }; + // In INT8 mode, we will always apply the quantization ranges provided by + // these ops to the relevant tensors. This happens regardless of the value of + // use_calibration. + if (precision_mode_ == INT8MODE && quantize_ops.count(node->type_string())) { + is_supported_op_type = true; + } + // LINT.ThenChange(//tensorflow/contrib/tensorrt/convert/convert_nodes.cc) if (!is_supported_op_type) { return errors::Unimplemented("Op type ", node->type_string(), - " is not supported."); + " is not supported"); } std::vector input_edges; @@ -170,7 +186,7 @@ tensorflow::Status BuildNodeMap( tensorflow::Status ConvertCalibGraphToInferGraph( const tensorflow::GraphDef& graph_def, tensorflow::GraphDef* infer_graph, bool is_dyn_op) { - VLOG(0) << "Starting Calib Conversion"; + LOG(INFO) << "Starting Calib Conversion"; infer_graph->CopyFrom(graph_def); auto trt_rm = TRTResourceManager::instance(); auto calib_rm = trt_rm->getManager("TRTCalibration"); @@ -220,18 +236,19 @@ tensorflow::Status ConvertGraphDefToTensorRT( const std::vector& output_names, size_t max_batch_size, size_t max_workspace_size_bytes, tensorflow::GraphDef* new_graph_def, int precision_mode, int minimum_segment_size, bool is_dyn_op, - int max_cached_engines, std::vector cached_engine_batches) { + int max_cached_engines, std::vector cached_engine_batches, + bool use_calibration) { // Create GrapplerItem. tensorflow::grappler::GrapplerItem item; item.fetch = output_names; item.graph = graph_def; - // TODO(aaroey): we should have used single machine cluster like the - // following, but the problem is then wrap_conversion will depend on - // direct_session and cause double linking problems. To fix this we need to - // fix or get rid of the swig dependency. Here we use VirtualCluster - // as a work around, and we need to create a session to initialize the - // underlying device before calling this method. +// TODO(aaroey): we should have used single machine cluster like the +// following, but the problem is then wrap_conversion will depend on +// direct_session and cause double linking problems. To fix this we need to +// fix or get rid of the swig dependency. Here we use VirtualCluster +// as a work around, and we need to create a session to initialize the +// underlying device before calling this method. #if 0 // Create single machine cluster. Note that this will create a session and // initialize the gpu devices. @@ -264,7 +281,9 @@ tensorflow::Status ConvertGraphDefToTensorRT( #endif // Create RewriterConfig. - tensorflow::RewriterConfig rw_cfg; + tensorflow::ConfigProto config_proto; + auto& rw_cfg = + *config_proto.mutable_graph_options()->mutable_rewrite_options(); // TODO(aaroey): use only const folding and layout for the time being since // new optimizers break the graph for trt. rw_cfg.add_optimizers("constfold"); @@ -285,9 +304,10 @@ tensorflow::Status ConvertGraphDefToTensorRT( list->add_i(batch); } } + parameters["use_calibration"].set_b(use_calibration); // Run optimizer. - tensorflow::grappler::MetaOptimizer meta_opt(nullptr, rw_cfg); + tensorflow::grappler::MetaOptimizer meta_opt(nullptr, config_proto); TF_RETURN_IF_ERROR(meta_opt.Optimize(cluster.get(), item, new_graph_def)); if (VLOG_IS_ON(5)) { @@ -433,7 +453,8 @@ tensorflow::Status GetEngineInfo( << "but this shouldn't have happened"; info->device = *segment_devices.begin(); } else { - LOG(ERROR) << "Can't find a device placement for the op!"; + VLOG(1) << "No device is assigned to the segment. " + << "A device will be assigned during graph execution (inference)."; } return Status::OK(); } @@ -564,27 +585,30 @@ tensorflow::Status CreateTRTNode(const std::vector& infos, int pos, } } } + + const bool calibrate_int8 = + (info.precision_mode == INT8MODE && info.use_calibration); + // Build the engine and get its serialized representation. string segment_string; - if (info.engine_type == EngineInfo::EngineType::TRTStatic || - info.precision_mode == INT8MODE) { + if (info.engine_type == EngineInfo::EngineType::TRTStatic || calibrate_int8) { // Create static engine for fp32/fp16 mode, and test validity of the engine - // for int8 mode. We don't want engine to fail at the calibration time. - // So we are constructing a FP32 engine here to check its validity, and if - // it is a valid engine then we put the serialized graphdef to the op. - // Otherwise we skip node creation for this engine. + // for int8 calibration mode. We don't want engine to fail at the + // calibration time. So we are constructing a FP32 engine here to check its + // validity, and if it is a valid engine then we put the serialized graphdef + // to the op. Otherwise we skip node creation for this engine. Logger trt_logger; TrtUniquePtrType engine; // TODO(sami): What happens if 1st dim is not batch? TF_RETURN_IF_ERROR(ConvertGraphDefToEngine( - info.segment_graph_def, - info.precision_mode == INT8MODE ? FP32MODE : info.precision_mode, + info.segment_graph_def, calibrate_int8 ? FP32MODE : info.precision_mode, max_batch_size, info.max_workspace_size_bytes, input_shapes, &trt_logger, alloc, /*calibrator=*/nullptr, &engine, + info.use_calibration, /*convert_successfully=*/nullptr)); TrtUniquePtrType engine_data(engine->serialize()); segment_string = string((const char*)engine_data->data(), engine_data->size()); - if (info.precision_mode == INT8MODE) { + if (calibrate_int8) { // See above comment about why not putting this inside the 'else' branch. segment_string = info.segment_graph_def.SerializeAsString(); } @@ -596,7 +620,7 @@ tensorflow::Status CreateTRTNode(const std::vector& infos, int pos, // conversion. string prec_string; TF_RETURN_IF_ERROR(GetPrecisionModeName(info.precision_mode, &prec_string)); - if (info.precision_mode == INT8MODE && + if (info.precision_mode == INT8MODE && calibrate_int8 && !TRTResourceManager::instance()->getManager("TRTCalibration")) { LOG(ERROR) << "Failed to construct calibration storage"; } @@ -632,6 +656,7 @@ tensorflow::Status CreateTRTNode(const std::vector& infos, int pos, .Attr("cached_engine_batches", {max_batch_size}) .Attr("workspace_size_bytes", info.max_workspace_size_bytes) .Attr("precision_mode", prec_string) + .Attr("use_calibration", info.use_calibration) .Attr("OutT", out_types) .Finalize(&trt_node); if (!status.ok()) { @@ -864,19 +889,17 @@ tensorflow::Status ConvertAfterShapes(ConversionParams& params) { } segment_options.minimum_segment_size = params.minimum_segment_size; tensorflow::tensorrt::segment::SegmentNodesVector initial_segments; - TrtCandidateSelector candidate_selector(*params.graph_properties); + TrtCandidateSelector candidate_selector(*params.graph_properties, + params.precision_mode); TF_RETURN_IF_ERROR(tensorrt::segment::SegmentGraph( - &graph, - std::bind(&TrtCandidateSelector::IsTensorRTCandidate, &candidate_selector, - std::placeholders::_1), + &graph, std::bind(&TrtCandidateSelector::IsTensorRTCandidate, + &candidate_selector, std::placeholders::_1), // Input validation is already done by TrtCandidateSelector, so we don't // need to check the input edges. [](const Edge* edge) { return true; }, OutputEdgeValidator(), segment_options, &initial_segments)); - if (initial_segments.size() > 1) { - VLOG(0) << "MULTIPLE tensorrt candidate conversion: " + LOG(INFO) << "Number of TensorRT candidate segments: " << initial_segments.size(); - } // Get the EngineInfo for each segment. std::unordered_map node_map; @@ -902,13 +925,17 @@ tensorflow::Status ConvertAfterShapes(ConversionParams& params) { continue; } curr_engine.precision_mode = params.precision_mode; - curr_engine.engine_type = - (params.is_dyn_op || params.precision_mode == INT8MODE - ? EngineInfo::EngineType::TRTDynamic - : EngineInfo::EngineType::TRTStatic); + if (params.use_calibration && params.precision_mode != INT8MODE) { + return errors::InvalidArgument( + "Calibration with FP32 or FP16 is not supported."); + } + curr_engine.engine_type = ((params.is_dyn_op || params.use_calibration) + ? EngineInfo::EngineType::TRTDynamic + : EngineInfo::EngineType::TRTStatic); + curr_engine.use_calibration = params.use_calibration; curr_engine.cached_engine_batches = params.cached_engine_batches; curr_engine.maximum_cached_engines = params.max_cached_engines; - StrAppend(&curr_engine.engine_name, "my_trt_op_", t); + StrAppend(&curr_engine.engine_name, "TRTEngineOp_", t); status = RegisterSegmentFunctionToFunctionLibrary( &graph, curr_engine.segment_graph_def, curr_engine.engine_name); if (!status.ok()) { @@ -969,16 +996,9 @@ tensorflow::Status ConvertAfterShapes(ConversionParams& params) { &graph, alloc.get(), &engine_nodes); // If status is ok, we successfully added the node to the graph and can // remove segment ops. Otherwise graph is not modified. - string msg = StrCat("Engine ", engine.engine_name, " creation for segment ", - i, ", composed of ", + string msg = StrCat("TensorRT node ", engine.engine_name, + " added for segment ", i, " consisting of ", converted_segments.at(i).first.size(), " nodes"); - if (VLOG_IS_ON(1)) { - StrAppend(&msg, " ("); - for (const string& node_name : converted_segments.at(i).first) { - StrAppend(&msg, node_name, ", "); - } - StrAppend(&msg, ")"); - } if (status.ok()) { LOG(INFO) << msg << " succeeded."; for (auto node_name : converted_segments.at(i).first) { @@ -986,7 +1006,14 @@ tensorflow::Status ConvertAfterShapes(ConversionParams& params) { } } else { // Graph is not modified. - LOG(WARNING) << msg << " failed: " << status << ". Skipping..."; + LOG(WARNING) << msg << " failed: " << status << ". Fallback to TF..."; + } + if (VLOG_IS_ON(1)) { + msg = "Segment consists of nodes: "; + for (const string& node_name : converted_segments.at(i).first) { + StrAppend(&msg, node_name, ", "); + } + VLOG(1) << msg; } } cudaSetDevice(old_cuda_device); diff --git a/tensorflow/contrib/tensorrt/convert/convert_graph.h b/tensorflow/contrib/tensorrt/convert/convert_graph.h index 1c9d82105a..1f39f56f63 100644 --- a/tensorflow/contrib/tensorrt/convert/convert_graph.h +++ b/tensorflow/contrib/tensorrt/convert/convert_graph.h @@ -35,7 +35,8 @@ namespace convert { // supported by TRT. class TrtCandidateSelector { public: - TrtCandidateSelector(const grappler::GraphProperties& graph_properties); + TrtCandidateSelector(const grappler::GraphProperties& graph_properties, + int precision_mode); // Returns OK iff 'node' is a TF-TRT conversion candidate, which will be added // to TRT subgraph and later converted into TRT engine. @@ -49,6 +50,9 @@ class TrtCandidateSelector { // GraphProperties of the graph whose nodes are to be validated by // IsTensorRTCandidate(). const grappler::GraphProperties& graph_properties_; + + // Quantization ops are only converted when using quantized precisions. + const int precision_mode_; }; struct ConversionParams { @@ -63,6 +67,7 @@ struct ConversionParams { cluster(nullptr), is_dyn_op(false), fixed_input_size(true), + use_calibration(true), max_cached_engines(1) {} const tensorflow::GraphDef* input_graph_def; const std::vector* output_names; @@ -76,6 +81,7 @@ struct ConversionParams { bool is_dyn_op; // Whether to create engine on conversion or execution time bool fixed_input_size; // Assume non-batch ranks of input tensors are fixed int max_cached_engines; // maximum number of cached engines + bool use_calibration; std::vector cached_engine_batches; // list of cached engines }; @@ -95,7 +101,7 @@ tensorflow::Status ConvertGraphDefToTensorRT( size_t max_workspace_size_bytes, tensorflow::GraphDef* new_graph_def, int precision_mode = 1, int minimum_segment_size = 3, bool is_dyn_op = false, int max_cached_engines = 1, - std::vector cached_engine_batches = {}); + std::vector cached_engine_batches = {}, bool use_calibration = true); // Method to call from optimization pass tensorflow::Status ConvertAfterShapes(ConversionParams& params); diff --git a/tensorflow/contrib/tensorrt/convert/convert_graph_test.cc b/tensorflow/contrib/tensorrt/convert/convert_graph_test.cc index f10729987f..2d2bfeb192 100644 --- a/tensorflow/contrib/tensorrt/convert/convert_graph_test.cc +++ b/tensorflow/contrib/tensorrt/convert/convert_graph_test.cc @@ -85,27 +85,42 @@ TEST(TrtCandidateSelector, Basics) { ops::MatMul(s.WithOpName("matmul_with_incompatible_input"), incompatible_feed, const_2); + // Quantize ops. + auto quantize_attrs = ops::FakeQuantWithMinMaxArgs::Min(-6.0f).Max(6.0f); + auto quantize = ops::FakeQuantWithMinMaxArgs(s.WithOpName("quantize"), feed, + quantize_attrs); + + // Get GrapplerItem and GraphProperties. grappler::GrapplerItem item; TF_EXPECT_OK(s.ToGraphDef(&item.graph)); Tensor feed_tensor(DT_FLOAT, input_shape); item.feed.push_back(std::make_pair("feed", feed_tensor)); - grappler::GraphProperties graph_properties(item); TF_EXPECT_OK(graph_properties.InferStatically(true)); - TrtCandidateSelector selector(graph_properties); - TF_EXPECT_OK(selector.IsTensorRTCandidate(matmul.operation.node())); - ExpectStatus( - selector.IsTensorRTCandidate(incompatible_matmul.operation.node()), - error::INVALID_ARGUMENT, - "transpose_a is not supported for TensorRT FullyConnected " - "(op: MatMul), at: incompatible_matmul"); - ExpectStatus(selector.IsTensorRTCandidate(unsupported_op.operation.node()), - error::UNIMPLEMENTED, "Op type Sin is not supported"); - ExpectStatus(selector.IsTensorRTCandidate( - matmul_with_incompatible_input.operation.node()), - error::INTERNAL, - "Failed to convert input with index 0 to a TRT_TensorOrWeights"); + for (const int precision_mode : {FP32MODE, INT8MODE}) { + TrtCandidateSelector selector(graph_properties, precision_mode); + TF_EXPECT_OK(selector.IsTensorRTCandidate(matmul.operation.node())); + ExpectStatus( + selector.IsTensorRTCandidate(incompatible_matmul.operation.node()), + error::INVALID_ARGUMENT, + "transpose_a is not supported for TensorRT FullyConnected " + "(op: MatMul), at: incompatible_matmul"); + ExpectStatus(selector.IsTensorRTCandidate(unsupported_op.operation.node()), + error::UNIMPLEMENTED, "Op type Sin is not supported"); + ExpectStatus( + selector.IsTensorRTCandidate( + matmul_with_incompatible_input.operation.node()), + error::INTERNAL, + "Failed to convert input with index 0 to a TRT_TensorOrWeights"); + if (precision_mode == INT8MODE) { + TF_EXPECT_OK(selector.IsTensorRTCandidate(quantize.operation.node())); + } else { + ExpectStatus(selector.IsTensorRTCandidate(quantize.operation.node()), + error::UNIMPLEMENTED, + "Op type FakeQuantWithMinMaxArgs is not supported"); + } + } } class FakeCluster : public grappler::Cluster { diff --git a/tensorflow/contrib/tensorrt/convert/convert_nodes.cc b/tensorflow/contrib/tensorrt/convert/convert_nodes.cc index e2988f5f2a..25a34dd350 100644 --- a/tensorflow/contrib/tensorrt/convert/convert_nodes.cc +++ b/tensorflow/contrib/tensorrt/convert/convert_nodes.cc @@ -54,10 +54,10 @@ limitations under the License. // would work! #define TFTRT_CHECK_EQ_TYPE(val1, val2) CHECK_EQ((int)val1, (int)val2) -#define TFTRT_INTERNAL_ERROR_AT_NODE(node) \ - do { \ - return tensorflow::errors::Internal( \ - "TFTRT::", __FUNCTION__, "failed to add TRT layer, at: ", node); \ +#define TFTRT_INTERNAL_ERROR_AT_NODE(node) \ + do { \ + return tensorflow::errors::Internal( \ + "TFTRT::", __FUNCTION__, " failed to add TRT layer, at: ", node); \ } while (0) #define TFTRT_RETURN_ERROR_IF_FALSE(status, node) \ @@ -130,7 +130,7 @@ void GetOutputProperties(const grappler::GraphProperties& graph_properties, *dtype = out_shape.dtype(); *shape = out_shape.shape(); } else { - VLOG(0) << "Unknown output shape" << node->name(); + LOG(INFO) << "Unknown output shape" << node->name(); *dtype = node->output_type(out_port); } } @@ -181,16 +181,55 @@ Status ValidateTensorProperties(const string& producer_node_type, if (shape.dim_size(d) < 0) { return errors::InvalidArgument( "Input tensor with shape ", shape.DebugString(), - " has an unknown non-batch dimemension at dim ", d); + " has an unknown non-batch dimension at dim ", d); } } return Status::OK(); } +string DebugString(const nvinfer1::DimensionType type) { + switch (type) { + case nvinfer1::DimensionType::kSPATIAL: + return "kSPATIAL"; + case nvinfer1::DimensionType::kCHANNEL: + return "kCHANNEL"; + case nvinfer1::DimensionType::kINDEX: + return "kINDEX"; + case nvinfer1::DimensionType::kSEQUENCE: + return "kSEQUENCE"; + default: + return StrCat(static_cast(type), "=unknown"); + } +} + +string DebugString(const nvinfer1::DataType trt_dtype) { + switch (trt_dtype) { + case nvinfer1::DataType::kFLOAT: + return "kFLOAT"; + case nvinfer1::DataType::kHALF: + return "kHALF"; + case nvinfer1::DataType::kINT8: + return "kINT8"; + case nvinfer1::DataType::kINT32: + return "kINT32"; + default: + return "Invalid TRT data type"; + } +} + string DebugString(const nvinfer1::Dims& dims) { string out = StrCat("nvinfer1::Dims(nbDims=", dims.nbDims, ", d="); for (int i = 0; i < dims.nbDims; ++i) { - StrAppend(&out, dims.d[i], ","); + StrAppend(&out, dims.d[i], "[", DebugString(dims.type[i]), "],"); + } + StrAppend(&out, ")"); + return out; +} + +string DebugString(const nvinfer1::Permutation& permutation, int len) { + string out = "nvinfer1::Permutation("; + for (int i = 0; i < len; ++i) { + StrAppend(&out, permutation.order[i], ","); } StrAppend(&out, ")"); return out; @@ -198,16 +237,15 @@ string DebugString(const nvinfer1::Dims& dims) { string DebugString(const nvinfer1::ITensor& tensor) { return StrCat("nvinfer1::ITensor(@", reinterpret_cast(&tensor), - ", shape=", DebugString(tensor.getDimensions()), ")"); + ", name=", tensor.getName(), + ", dtype=", DebugString(tensor.getType()), + ", dims=", DebugString(tensor.getDimensions()), ")"); } -// Return whether or not the broadcast is feasible; -bool TensorRTGetBroadcastShape(const nvinfer1::Dims& operand_l, - const bool operand_l_is_tensor, - const nvinfer1::Dims& operand_r, - const bool operand_r_is_tensor, - nvinfer1::Dims* operand_l_new_shape, - nvinfer1::Dims* operand_r_new_shape) { +Status Converter::GetTrtBroadcastShape( + const TRT_TensorOrWeights& operand_l, const TRT_TensorOrWeights& operand_r, + nvinfer1::Dims* operand_l_new_dims, + nvinfer1::Dims* operand_r_new_dims) const { // *************************************************************************** // TensorRT Elementwise op supports broadcast but requires both tensor to be // of Identical rank @@ -232,52 +270,59 @@ bool TensorRTGetBroadcastShape(const nvinfer1::Dims& operand_l, // -> T: 1 1 1 -1 3 5 1 // -> W: 1 1 1 1 3 5 1 // *************************************************************************** - const int max_nb_dims = nvinfer1::Dims::MAX_DIMS + 1; - const size_t element_size = sizeof(operand_l.d[0]); - - // fill in dimensions - int l_s[max_nb_dims]; - std::fill(l_s, l_s + max_nb_dims, 1); - int l_d = operand_l_is_tensor ? operand_l.nbDims + 1 : operand_l.nbDims; - int r_s[max_nb_dims]; - std::fill(r_s, r_s + max_nb_dims, 1); - int r_d = operand_r_is_tensor ? operand_r.nbDims + 1 : operand_r.nbDims; - - int max_d = std::max(l_d, r_d); - std::memcpy(l_s + max_d - operand_l.nbDims, operand_l.d, - operand_l.nbDims * element_size); - std::memcpy(r_s + max_d - operand_r.nbDims, operand_r.d, - operand_r.nbDims * element_size); - - // set -1 for batch dimension, since batch size is not supposed to be - // broadcasted - if (operand_l_is_tensor) { - if (max_d != l_d) { // if broadcast beyond batch dimension, fail - return false; - } - l_s[0] = -1; - } - if (operand_r_is_tensor) { - if (max_d != r_d) { // if broadcast beyond batch dimension, fail - return false; - } - r_s[0] = -1; + if (!operand_l.is_tensor() && !operand_r.is_tensor()) { + return errors::InvalidArgument( + "Broadcasting requires at least one of the operands be tensors"); } - // compare broadcast feasibility - for (int i = max_d - 1; i >= 0; i--) { - if ((l_s[i] != r_s[i]) && (l_s[i] != 1) && (r_s[i] != 1)) { - return false; + const int max_nb_dims = nvinfer1::Dims::MAX_DIMS + 1; + auto compute_output_dims = + [max_nb_dims](const TRT_TensorOrWeights& input, int broadcast_num_dims, + int* output_dims_array, nvinfer1::Dims* output_dims) { + const nvinfer1::Dims input_dims = input.GetTrtDims(); + std::fill(output_dims_array, output_dims_array + max_nb_dims, 1); + std::copy(input_dims.d, input_dims.d + input_dims.nbDims, + output_dims_array + broadcast_num_dims - input_dims.nbDims); + if (input.is_tensor()) { + const int true_input_dims = input_dims.nbDims + 1; + if (true_input_dims < broadcast_num_dims) { + return errors::InvalidArgument( + "Broadcasting beyond batch dimension is not supported ", + "(tensor #dims ", true_input_dims, " vs broadcast #dims ", + broadcast_num_dims, ")"); + } + // Set the batch dimension to -1, since batch size is not supposed to + // be broadcasted. + output_dims_array[0] = -1; + } + // Copy to output dimensions (stripping the batch dimension). + output_dims->nbDims = broadcast_num_dims - 1; + std::copy(output_dims_array + 1, output_dims_array + broadcast_num_dims, + output_dims->d); + return Status::OK(); + }; + + // Compute the output dimensions. + const int broadcast_num_dims = + std::max(operand_l.GetTrtDims().nbDims + (operand_l.is_tensor() ? 1 : 0), + operand_r.GetTrtDims().nbDims + (operand_r.is_tensor() ? 1 : 0)); + int output_l[max_nb_dims], output_r[max_nb_dims]; + TF_RETURN_IF_ERROR(compute_output_dims(operand_l, broadcast_num_dims, + output_l, operand_l_new_dims)); + TF_RETURN_IF_ERROR(compute_output_dims(operand_r, broadcast_num_dims, + output_r, operand_r_new_dims)); + + // Compare broadcast feasibility + for (int i = 0; i < broadcast_num_dims; ++i) { + if ((output_l[i] != output_r[i]) && (output_l[i] != 1) && + (output_r[i] != 1)) { + return errors::InvalidArgument( + "Infeasible broadcast scheme (", "batch_dim: ", output_l[0], ", ", + DebugString(*operand_l_new_dims), " vs ", "batch_dim: ", output_r[0], + ", ", DebugString(*operand_r_new_dims), ")"); } } - - // output new TensorRT Dimension (stripping the batch dimension) - operand_l_new_shape->nbDims = max_d - 1; - std::memcpy(operand_l_new_shape->d, l_s + 1, (max_d - 1) * element_size); - operand_r_new_shape->nbDims = max_d - 1; - std::memcpy(operand_r_new_shape->d, r_s + 1, (max_d - 1) * element_size); - - return true; + return Status::OK(); } inline bool DimsEqual(const nvinfer1::Dims& dim_l, @@ -381,8 +426,8 @@ size_t TRT_ShapedWeights::size_bytes() const { string TRT_ShapedWeights::DebugString() const { return StrCat("TRT_ShapedWeights(shape=", convert::DebugString(shape_), - ", type=", type_, - ", values=", reinterpret_cast(GetValues()), ")"); + ", type=", DataTypeString(type_), ", values=", + reinterpret_cast(GetValues()), ")"); } // A fake ITensor implementation used to check whether the TF-TRT converter can @@ -425,7 +470,9 @@ class TRT_TensorOrWeights::SimpleITensor : public nvinfer1::ITensor { void setLocation(nvinfer1::TensorLocation location) override {} #if NV_TENSORRT_MAJOR >= 5 - bool setDynamicRange(float min, float max) override {} + bool setDynamicRange(float min, float max) override { return true; } + + float getDynamicRange() const override { return 0; } #endif private: @@ -489,8 +536,7 @@ nvinfer1::Dims TRT_TensorOrWeights::GetTrtDims() const { string TRT_TensorOrWeights::DebugString() const { string output = "TRT_TensorOrWeights(type="; if (is_tensor()) { - StrAppend(&output, "tensor @", reinterpret_cast(tensor()), - ", shape=", convert::DebugString(tensor()->getDimensions()), + StrAppend(&output, "tensor=", convert::DebugString(*tensor()), ", batch_size=", batch_size_); } else { StrAppend(&output, "weights=", weights_.DebugString()); @@ -627,11 +673,10 @@ void ReorderCKtoKC(const TRT_ShapedWeights& iweights, break; } case tensorflow::DataType::DT_HALF: { - Reorder2( - {k, c}, static_cast(iweights.GetValues()), - istrides, - static_cast(const_cast(oweights->GetValues())), - ostrides); + Reorder2({k, c}, static_cast(iweights.GetValues()), + istrides, static_cast( + const_cast(oweights->GetValues())), + ostrides); break; } default: @@ -753,8 +798,9 @@ Status TrtNodeValidator::ValidateNode( Status status = ConvertToTensorOrWeights( *pair.first, pair.second, graph_properties, &tensor_or_weights); if (!status.ok()) { - return errors::Internal("Failed to convert input with index ", i, - " to a TRT_TensorOrWeights"); + return errors::Internal( + "Failed to convert input with index ", i, + " to a TRT_TensorOrWeights: ", status.error_message()); } inputs.push_back(tensor_or_weights); } @@ -786,8 +832,11 @@ Status TrtNodeValidator::ConvertConstToWeights( return status; } -Converter::Converter(nvinfer1::INetworkDefinition* trt_network, bool is_fp16) - : trt_network_(trt_network), is_fp16_(is_fp16) { +Converter::Converter(nvinfer1::INetworkDefinition* trt_network, + int precision_mode, bool use_calibration) + : trt_network_(trt_network), + precision_mode_(precision_mode), + use_calibration_(use_calibration) { this->RegisterOpConverters(); } @@ -812,13 +861,18 @@ Status Converter::ConvertNode(const NodeDef& node_def) { TRT_TensorOrWeights& output = outputs[i]; string output_name = node_def.name(); if (i != 0) output_name = StrCat(output_name, ":", i); - // We need to check the name before setting it. For Identity op where the - // output is the input, if its input is one of the engine input, setting - // the name here will overwrite engine input bindings which will cause - // runtime error. + // We need to check the name before setting it. If the input is one of the + // engine input, setting the name here will overwrite engine input + // bindings which will cause runtime error. if (output.is_tensor()) { const char* tensor_name = output.tensor()->getName(); - if (tensor_name == nullptr || std::strlen(tensor_name) == 0) { + if (!tensorflow::str_util::StartsWith(tensor_name, kInputPHName)) { + // TRT initializes tensor names as "(Unnamed ITensor* N)". We rename + // them to match their corresponding TensorFlow name. + // Note: ITensors that we create internally within TF-TRT which are + // not inputs or outputs of a node will not be renamed. This is a + // potential cause of confusion if an error message or warning + // mentions the unnamed tensor. output.tensor()->setName(output_name.c_str()); } } @@ -930,11 +984,14 @@ Status Converter::TransposeTensor(nvinfer1::ITensor* input_tensor, nvinfer1::IShuffleLayer* layer = this->network()->addShuffle(*input_tensor); TFTRT_RETURN_ERROR_IF_NULLPTR(layer, "TF-TRT Internal Transpose"); + MarkQuantizationRangesAsInferrable(input_tensor, layer->getOutput(0)); nvinfer1::Permutation permutation; for (int32_t i = 0; i < dims.nbDims; ++i) { permutation.order[i] = order_with_batch_dim[i + 1] - 1; } + VLOG(1) << "TransposeTensor permutation: " + << DebugString(permutation, dims.nbDims); layer->setFirstTranspose(permutation); nvinfer1::Dims reshape_dims; @@ -950,6 +1007,38 @@ Status Converter::TransposeTensor(nvinfer1::ITensor* input_tensor, return tensorflow::Status::OK(); } +Status Converter::GetWeightRange(const TRT_ShapedWeights& weights, + float* out_min, float* out_max) const { + switch (weights.type_) { + case DataType::DT_FLOAT: { + auto inp = static_cast(weights.GetValues()); + auto result = std::minmax_element(inp, inp + weights.count()); + *out_min = *result.first; + *out_max = *result.second; + break; + } + case DataType::DT_HALF: { + auto inp = static_cast(weights.GetValues()); + auto result = std::minmax_element(inp, inp + weights.count()); + *out_min = Eigen::half_impl::half_to_float(*result.first); + *out_max = Eigen::half_impl::half_to_float(*result.second); + break; + } + case DataType::DT_INT32: { + auto inp = static_cast(weights.GetValues()); + auto result = std::minmax_element(inp, inp + weights.count()); + *out_min = static_cast(*result.first); + *out_max = static_cast(*result.second); + break; + } + default: + return errors::Unimplemented( + "Data type not supported for GetWeightRange: ", + DataTypeString(weights.type_)); + } + return Status::OK(); +} + Status Converter::PrepareTensorForShape(const TRT_TensorOrWeights& input, const nvinfer1::Dims& dims, const nvinfer1::ITensor** tensor) { @@ -964,8 +1053,9 @@ Status Converter::PrepareTensorForShape(const TRT_TensorOrWeights& input, } if (can_check_shapes && TrtDimsNumElements(input.GetTrtDims()) != TrtDimsNumElements(dims)) { - return tensorflow::errors::InvalidArgument( - "Reshape shapes are not compatible."); + return errors::InvalidArgument("Reshape shapes are not compatible (", + DebugString(input.GetTrtDims()), " vs ", + DebugString(dims), ")"); } if (input.is_tensor()) { @@ -976,6 +1066,8 @@ Status Converter::PrepareTensorForShape(const TRT_TensorOrWeights& input, *const_cast(input.tensor())); TFTRT_RETURN_ERROR_IF_NULLPTR(layer, "TF-TRT Internal Reshape"); layer->setReshapeDimensions(dims); + MarkQuantizationRangesAsInferrable( + const_cast(input.tensor()), layer->getOutput(0)); *tensor = layer->getOutput(0); } } else { @@ -983,10 +1075,123 @@ Status Converter::PrepareTensorForShape(const TRT_TensorOrWeights& input, this->network()->addConstant(dims, input.weights().GetTrtWeights()); TFTRT_RETURN_ERROR_IF_NULLPTR(layer, "TF-TRT Internal Reshape"); *tensor = layer->getOutput(0); + if (precision_mode() == INT8MODE && !use_calibration()) { + // If we are in int8 mode and not calibrating, we need to explicitly set a + // quantization range for the output tensor of the IConstantLayer. Here we + // set the range to [min(weights), max(weights)]. + float min_range = 0.0f; + float max_range = 0.0f; + TF_RETURN_IF_ERROR( + GetWeightRange(input.weights(), &min_range, &max_range)); + // Avoid setting range to 0 because TRT will throw an error. If the + // weights are zero then the range doesn't matter: using 127.0f should + // ensure the quantized weight will be exactly zero. + if (min_range == 0.0f && max_range == 0.0f) { + min_range = -127.0f; + max_range = 127.0f; + } + ProvideQuantizationRange(const_cast(*tensor), + min_range, max_range); + } } return tensorflow::Status::OK(); } +void Converter::MarkQuantizationRangesAsInferrable(nvinfer1::ITensor* input, + nvinfer1::ITensor* output) { + quantization_infer_.push_back({input, output}); + quantization_infer_.push_back({output, input}); +} + +void Converter::ProvideQuantizationRange(nvinfer1::ITensor* tensor, + float min_range, float max_range) { + float symmetric_range = std::max(std::abs(min_range), std::abs(max_range)); + quantization_ranges_[tensor] = symmetric_range; +} + +void Converter::MaybeApplyQuantizationRanges() { + if (precision_mode() != INT8MODE) return; + + // Infer ranges across marked ops. + PropagateQuantizationRanges(); + // Apply ranges. +#if NV_TENSORRT_MAJOR >= 5 + for (auto pair : quantization_ranges_) { + nvinfer1::ITensor* tensor = pair.first; + const float range = pair.second; + VLOG(1) << "Setting range for: " << tensor->getName() << ": " << range; + // TODO(laigd): if 'tensor' already has a range set which doesn't match + // 'range', it should report error. + tensor->setDynamicRange(-range, range); + } +#endif + + // Warn user about tensors that are missing ranges. If TRT fuses some layers + // then these tensors may not actually be required, which is why this is + // just a warning. If we are still missing ranges even after fusion, + // Builder::buildCudaEngine() will return nullptr and we will catch the + // error at that point. + if (!use_calibration()) { + // Get all tensors from network + std::set all_tensors; + for (int i = 0; i < this->network()->getNbLayers(); i++) { + nvinfer1::ILayer* layer = this->network()->getLayer(i); + for (int j = 0; j < layer->getNbInputs(); j++) { + all_tensors.insert(layer->getInput(j)); + } + for (int j = 0; j < layer->getNbOutputs(); j++) { + all_tensors.insert(layer->getOutput(j)); + } + } + // Find tensors with no ranges + for (auto tensor : all_tensors) { + if (!quantization_ranges_.count(tensor)) { + // Note: there may be some warnings for "(Unnamed ITensor* N)". These + // are tensors which are created internally by TF-TRT. The ranges for + // these unnamed ITensors are always inferred from user provided ranges, + // thus there will also be a warning for the range(s) the user missed. + LOG(WARNING) << "Quantization range was not found for " + << tensor->getName() << ". " + << "This is okay if TensorRT does not need the range " + << "(e.g. due to node fusion)."; + } + } + } +} + +void Converter::PropagateQuantizationRanges() { + // Propagate ranges across edges in quantization_infer_ until no new + // information is added. + // Note: this function modifies quantization_infer_, it might be better to + // modify a copy instead if we for some reason need quantization_infer_ + // later. + bool information_added = true; + while (information_added) { + information_added = false; + for (auto it = quantization_infer_.begin(); + it != quantization_infer_.end();) { + auto input_tensor_range = quantization_ranges_.find(it->first); + auto output_tensor_range = quantization_ranges_.find(it->second); + if (input_tensor_range != quantization_ranges_.end() && + output_tensor_range == quantization_ranges_.end()) { + // Input has range but output doesn't: copy range + // TODO(laigd): consider reporting error if it a different range is + // already set. + quantization_ranges_[it->second] = input_tensor_range->second; + information_added = true; + VLOG(1) << "Copy quantization range: " << it->first->getName() << " -> " + << it->second->getName(); + } + // We can remove edges when the output range is known + if (quantization_ranges_.find(it->second) != quantization_ranges_.end()) { + it = quantization_infer_.erase(it); + } else { + ++it; + } + } + } +} + Status Converter::GetInputs(const tensorflow::NodeDef& node_def, std::vector* inputs) const { for (auto const& input_name : node_def.input()) { @@ -1043,12 +1248,11 @@ TRT_ShapedWeights ConvertFP32ToFP16(TrtWeightStore* store, } // **************************************************************************** -// Constant folding functions -// TODO(jie): once optimizer kicks in, we should have done constant folding -// there. +// Constant folding functions for weights. +// TODO(laigd): we should probably use eigen directly. // ***************************************************************************** struct LambdaFactory { - enum class OP_CATEGORY : int { RSQRT = 0, NEG, ADD, MUL, SUB, RECIP }; + enum class OP_CATEGORY : int { RSQRT = 0, NEG, RECIP }; OP_CATEGORY op; template @@ -1063,84 +1267,10 @@ struct LambdaFactory { case OP_CATEGORY::RECIP: return [](T t) -> T { return 1.0 / t; }; default: - VLOG(2) << "Not supported op for unary: " << static_cast(op); + LOG(ERROR) << "Not supported op for unary: " << static_cast(op); return nullptr; } } - - template - std::function binary() { - switch (op) { - case OP_CATEGORY::ADD: - return [](T l, T r) -> T { return l + r; }; - case OP_CATEGORY::SUB: - return [](T l, T r) -> T { return l - r; }; - case OP_CATEGORY::MUL: - return [](T l, T r) -> T { return l * r; }; - default: - LOG(WARNING) << "Not supported op for binary: " << static_cast(op); - } - return [](T l, T r) -> T { - LOG(FATAL) << "Unsupported op type "; - return l; - }; - } - - template - std::function broadcast_r(T val) { - VLOG(2) << "LAMBDA VAL : " << val; - switch (op) { - case OP_CATEGORY::ADD: - return [val](T l) -> T { - VLOG(2) << "LAMBDA VAL : " << val; - return l + val; - }; - case OP_CATEGORY::SUB: - return [val](T l) -> T { - VLOG(2) << "LAMBDA VAL : " << val; - return l - val; - }; - case OP_CATEGORY::MUL: - return [val](T l) -> T { - VLOG(2) << "LAMBDA VAL : " << val; - return l * val; - }; - default: - LOG(WARNING) << "Not supported op for binary: " << static_cast(op); - } - return [val](T l) -> T { - LOG(FATAL) << "Unsupported op type "; - return l; - }; - } - - template - std::function broadcast_l(T val) { - VLOG(2) << "LAMBDA VAL : " << val; - switch (op) { - case OP_CATEGORY::ADD: - return [val](T l) -> T { - VLOG(2) << "LAMBDA VAL : " << val; - return val + l; - }; - case OP_CATEGORY::SUB: - return [val](T l) -> T { - VLOG(2) << "LAMBDA VAL : " << val; - return val - l; - }; - case OP_CATEGORY::MUL: - return [val](T l) -> T { - VLOG(2) << "LAMBDA VAL : " << val; - return val * l; - }; - default: - LOG(ERROR) << "Not supported op for binary: " << static_cast(op); - } - return [val](T l) -> T { - LOG(FATAL) << "Unsupported op type "; - return l; - }; - } }; template <> @@ -1148,15 +1278,18 @@ std::function LambdaFactory::unary() { switch (op) { case OP_CATEGORY::RSQRT: { VLOG(2) << "RSQRT GETS DONE"; - return [](Eigen::half t) -> Eigen::half { + return [](Eigen::half t) { return Eigen::half(1.0 / sqrt(static_cast(t))); }; } case OP_CATEGORY::NEG: - return [](Eigen::half t) -> Eigen::half { return -t; }; - // TODO(aaroey): can we support RECIP? + return [](Eigen::half t) { return -t; }; + case OP_CATEGORY::RECIP: + return [](Eigen::half t) { + return Eigen::half(1.0 / static_cast(t)); + }; default: - VLOG(2) << "Not supported op for unary: " << static_cast(op); + LOG(ERROR) << "Not supported op for unary: " << static_cast(op); return nullptr; } } @@ -1188,50 +1321,48 @@ tensorflow::Status UnaryCompute(const TRT_ShapedWeights& iweights, return tensorflow::Status::OK(); } +// If swapped_inputs is false, 'tensor' is the left operand and 'weights' is the +// right operand. If swapped_inputs is true, those two are swapped. +// // TODO(jie): broadcast is needed yet not implemented. -// Only implemented channel wise for the time being -tensorflow::Status BinaryTensorOpWeight(OpConverterParams* params, - const nvinfer1::ITensor* tensor, - TRT_ShapedWeights weights, - bool swapped_inputs) { +// Only implemented channel wise for the time being. +Status BinaryTensorOpWeight(OpConverterParams* params, + const nvinfer1::ITensor* tensor, + TRT_ShapedWeights weights, bool swapped_inputs) { + static const std::unordered_set supported_ops = {"Sub", "Add", "Mul", + "Div", "RealDiv"}; const auto& node_def = params->node_def; - // tensor is the left operand while weights is the right operand; - // when swapped_inputs set to true, those two are swapped. - // TODO(aaroey): use a set. - if (node_def.op() != "Sub" && node_def.op() != "Add" && - node_def.op() != "Mul" && node_def.op() != "Div" && - node_def.op() != "RealDiv") { - return tensorflow::errors::Unimplemented( - "op not supported: " + node_def.op() + ", at: " + node_def.name()); + if (!supported_ops.count(node_def.op())) { + return errors::Unimplemented(node_def.op(), " is not supported, at ", + node_def.name()); } - // Check type consistency - nvinfer1::DataType ttype; - TF_RETURN_IF_ERROR(ConvertDType(weights.type_, &ttype)); + // Check type consistency. + nvinfer1::DataType trt_dtype; + TF_RETURN_IF_ERROR(ConvertDType(weights.type_, &trt_dtype)); - // Check scale mode + // Check scale mode. auto dims_w = weights.shape_; - auto dims_t = tensor->getDimensions(); + const auto dims_t = tensor->getDimensions(); // TODO(jie): addScale checks for input tensor dimension if (dims_t.nbDims != 3) { - return tensorflow::errors::InvalidArgument( - "addScale requires tensor with rank 3, " + node_def.name()); + return errors::InvalidArgument("addScale requires tensor with rank 3, at ", + node_def.name()); } - // default to element-wise + // Default to element-wise auto scale_mode = nvinfer1::ScaleMode::kELEMENTWISE; // TODO(jie): maybe use a permutation instead to support more cases; - bool permutation_flag = false; + bool need_to_permute = false; if (weights.count() == 1) { - VLOG(2) << "UNIFORM"; scale_mode = nvinfer1::ScaleMode::kUNIFORM; } else { - // no broadcasting on Batch dimension; - VLOG(2) << "WEIGHTS DIM: " << dims_w.nbDims - << " tensor DIM: " << dims_t.nbDims; + VLOG(2) << "weights dims: " << DebugString(dims_w) + << "; tensor dims: " << DebugString(dims_t); + // Make sure no broadcasting on batch dimension. if (dims_w.nbDims == dims_t.nbDims + 1) { if (dims_w.d[0] == 1) { for (int i = 1; i < dims_w.nbDims; i++) { @@ -1239,72 +1370,70 @@ tensorflow::Status BinaryTensorOpWeight(OpConverterParams* params, } dims_w.nbDims--; } else { - return tensorflow::errors::InvalidArgument( - "Binary op cannot operate on batch, " + node_def.name()); + return errors::InvalidArgument("Binary op cannot operate on batch, at ", + node_def.name()); } } if (dims_w.nbDims == dims_t.nbDims && dims_w.d[0] == dims_t.d[0]) { scale_mode = nvinfer1::ScaleMode::kELEMENTWISE; - // default is element; + // Default is element-wise for (int i = 1; i < dims_w.nbDims; i++) { if (dims_w.d[i] != dims_t.d[i]) { - // if dimension does not match, switch back to channel; - VLOG(2) << "channel"; + // If dimension does not match, switch back to per-channel scale_mode = nvinfer1::ScaleMode::kCHANNEL; break; } } - // if channel as candidate, validate it + // If the mode is per-channel, since channel dimension is assumed to be + // the third to last dimension, we need to make sure all other dimensions + // have size 1. if (scale_mode == nvinfer1::ScaleMode::kCHANNEL) { for (int i = 1; i < dims_w.nbDims; i++) { if (dims_w.d[i] != 1) - return tensorflow::errors::InvalidArgument( - "Weight shape not compatible at, " + node_def.name()); + return errors::InvalidArgument( + "Weight dims not compatible for channel-wise broadcast at ", + node_def.name()); } - } else { - VLOG(2) << "elementwise"; } } else if (dims_w.nbDims == 1 && dims_w.d[0] == dims_t.d[dims_t.nbDims - 1]) { - // channel wise and broadcast required; - permutation_flag = true; + // Channel wise and broadcast required. We compare the last dimension of + // the tensor shape because of tensorflow default broadcasting rules. + need_to_permute = true; scale_mode = nvinfer1::ScaleMode::kCHANNEL; } else { - return tensorflow::errors::InvalidArgument( - "Weight shape not compatible at, " + node_def.name()); + return errors::InvalidArgument("Weight dims not compatible at ", + node_def.name()); } } + // TODO(laigd): we should add validation_only support in TransposeTensor() and + // PrepareTensorForShape(). + if (params->validation_only) return Status::OK(); - // transpose last dimension + // Transpose last dimension. std::vector permutation(dims_t.nbDims + 1); - if (permutation_flag) { - if (scale_mode == nvinfer1::ScaleMode::kCHANNEL && dims_t.nbDims > 1) { - // we swap the last dimension into channel for trt. - // because of tensorflow default broadcasting rules. - for (int i = 0; i < static_cast(permutation.size()); i++) { - permutation[i] = i; - } - permutation[1] = dims_t.nbDims; - permutation[dims_t.nbDims] = 1; - TF_RETURN_IF_ERROR(params->converter->TransposeTensor( - const_cast(tensor), permutation, &tensor)); - } else { - return tensorflow::errors::InvalidArgument( - "Transpose cannot be applied, " + node_def.name()); - } + if (need_to_permute) { + // We swap the last dimension into channel for trt, because of tensorflow + // default broadcasting rules. + for (int i = 0; i < static_cast(permutation.size()); i++) { + permutation[i] = i; + } + permutation[1] = dims_t.nbDims; + permutation[dims_t.nbDims] = 1; + TF_RETURN_IF_ERROR(params->converter->TransposeTensor( + const_cast(tensor), permutation, &tensor)); } - if (params->converter->is_fp16()) { + if (params->converter->precision_mode() == FP16MODE) { weights = ConvertFP32ToFP16(params->weight_store, weights); } - // prepare weights + // Prepare weights TRT_ShapedWeights shift_weights(weights.type_); TRT_ShapedWeights scale_weights(weights.type_); TRT_ShapedWeights power_weights(weights.type_); - // Maybe I should do a switch if (node_def.op() == "Sub") { if (swapped_inputs) { shift_weights = weights; @@ -1312,6 +1441,10 @@ tensorflow::Status BinaryTensorOpWeight(OpConverterParams* params, *const_cast(tensor), nvinfer1::UnaryOperation::kNEG); TFTRT_RETURN_ERROR_IF_NULLPTR(layer, node_def.name()); + // Since quantization ranges are symmetric, the same range as the input + // will work for the negation of the input. + params->converter->MarkQuantizationRangesAsInferrable( + const_cast(tensor), layer->getOutput(0)); tensor = layer->getOutput(0); } else { TRT_ShapedWeights neg_weights = @@ -1323,6 +1456,25 @@ tensorflow::Status BinaryTensorOpWeight(OpConverterParams* params, } } else if (node_def.op() == "Div" || node_def.op() == "RealDiv") { if (swapped_inputs) { + // We need to infer the quantization range for this intermediate tensor. + // + // x -> [Recip] -> 1/x -> [Scale] -> s/x + // ^ + // need range for this + // + // We have the quantization scales for x and s/x - can we divide the scale + // for s/x by s? Only if it is a scalar. + // + // Because of this issue, fall back to BinaryTensorOpTensor if we are + // doing INT8 with no calibration. There is most likely no performance + // penalty by falling back here. + if (params->converter->precision_mode() == INT8MODE && + !params->converter->use_calibration()) { + return errors::Unimplemented( + "Intermediate quantization range cannot be determined without" + " calibration. Falling back to BinaryTensorOpTensor for ", + node_def.op(), ", at ", node_def.name()); + } scale_weights = weights; nvinfer1::IUnaryLayer* layer = params->converter->network()->addUnary( *const_cast(tensor), @@ -1342,8 +1494,8 @@ tensorflow::Status BinaryTensorOpWeight(OpConverterParams* params, } else if (node_def.op() == "Add") { shift_weights = weights; } else { - return tensorflow::errors::Unimplemented("Binary op not supported: " + - node_def.op()); + // This should not happen. + return errors::Unimplemented("Binary op not supported at ", node_def.op()); } nvinfer1::IScaleLayer* layer = params->converter->network()->addScale( @@ -1353,8 +1505,8 @@ tensorflow::Status BinaryTensorOpWeight(OpConverterParams* params, TFTRT_RETURN_ERROR_IF_NULLPTR(layer, node_def.name()); const nvinfer1::ITensor* output_tensor = layer->getOutput(0); - // transpose back dimension - if (permutation_flag) { + // Transpose back dimension + if (need_to_permute) { TF_RETURN_IF_ERROR(params->converter->TransposeTensor( const_cast(output_tensor), permutation, &output_tensor)); @@ -1398,7 +1550,7 @@ tensorflow::Status ConvertConv2DHelper(OpConverterParams* params, int group) { return tensorflow::errors::Internal( "Conv2D expects kernel of dimension 4, at: " + node_def.name()); } - if (params->converter->is_fp16()) { + if (params->converter->precision_mode() == FP16MODE) { weights_rsck = ConvertFP32ToFP16(params->weight_store, inputs.at(1).weights()); } @@ -1445,6 +1597,8 @@ tensorflow::Status ConvertConv2DHelper(OpConverterParams* params, int group) { nvinfer1::DimsHW(padding[0].first, padding[1].first), nvinfer1::DimsHW(padding[0].second, padding[1].second)); TFTRT_RETURN_ERROR_IF_NULLPTR(pad_layer, node_def.name()); + params->converter->MarkQuantizationRangesAsInferrable( + const_cast(tensor), pad_layer->getOutput(0)); padding = {{0, 0}, {0, 0}}; tensor = pad_layer->getOutput(0); VLOG(2) << "TENSOR after: " << DebugString(tensor->getDimensions()); @@ -1486,9 +1640,9 @@ tensorflow::Status ConvertConv2DHelper(OpConverterParams* params, params->node_def.name()); } -tensorflow::Status BinaryTensorOpTensor(OpConverterParams* params, - const TRT_TensorOrWeights& operand_l, - const TRT_TensorOrWeights& operand_r) { +Status BinaryTensorOpTensor(OpConverterParams* params, + const TRT_TensorOrWeights& operand_l, + const TRT_TensorOrWeights& operand_r) { const auto& node_def = params->node_def; static const std::unordered_map ops{ {"Add", nvinfer1::ElementWiseOperation::kSUM}, @@ -1499,50 +1653,52 @@ tensorflow::Status BinaryTensorOpTensor(OpConverterParams* params, {"Minimum", nvinfer1::ElementWiseOperation::kMIN}, {"Maximum", nvinfer1::ElementWiseOperation::kMAX}, }; + auto op_pair = ops.find(node_def.op()); + if (op_pair == ops.end()) { + return errors::Unimplemented("Binary op ", node_def.op(), + " not supported at: ", node_def.name()); + } - const nvinfer1::ITensor* tensor_l; - const nvinfer1::ITensor* tensor_r; - - nvinfer1::Dims dim_l; - nvinfer1::Dims dim_r; - - if (!TensorRTGetBroadcastShape(operand_l.GetTrtDims(), operand_l.is_tensor(), - operand_r.GetTrtDims(), operand_r.is_tensor(), - &dim_l, &dim_r)) { - return tensorflow::errors::InvalidArgument( - "Binary op broadcast scheme not supported by TensorRT op: " + - node_def.op() + ", at: " + node_def.name()); + nvinfer1::Dims broadcasted_dims_l, broadcasted_dims_r; + Status status = params->converter->GetTrtBroadcastShape( + operand_l, operand_r, &broadcasted_dims_l, &broadcasted_dims_r); + if (!status.ok()) { + return errors::InvalidArgument( + "Unsupported binary op broadcast scheme for op ", node_def.name(), ": ", + status.error_message()); } + if (params->validation_only) return Status::OK(); - TF_RETURN_IF_ERROR( - params->converter->PrepareTensorForShape(operand_l, dim_l, &tensor_l)); - TF_RETURN_IF_ERROR( - params->converter->PrepareTensorForShape(operand_r, dim_r, &tensor_r)); + const nvinfer1::ITensor* tensor_l = nullptr; + const nvinfer1::ITensor* tensor_r = nullptr; + status = params->converter->PrepareTensorForShape( + operand_l, broadcasted_dims_l, &tensor_l); + if (status.ok()) { + status = params->converter->PrepareTensorForShape( + operand_r, broadcasted_dims_r, &tensor_r); + } + if (!status.ok()) { + return errors::Internal("Failed to convert binary op ", node_def.name(), + ": ", status.error_message()); + } - // get trt type & shape + // Check type consistency. TFAttrs attrs(node_def); - // maybe this part has to be moved into the block of rsqrt later nvinfer1::DataType dtype = attrs.get("T"); + TFTRT_CHECK_EQ_TYPE(tensor_l->getType(), dtype) + << DebugString(tensor_l->getType()) << " vs " << DebugString(dtype); + TFTRT_CHECK_EQ_TYPE(tensor_r->getType(), dtype) + << DebugString(tensor_r->getType()) << " vs " << DebugString(dtype); - // check type consistency - TFTRT_CHECK_EQ_TYPE(tensor_l->getType(), dtype); - TFTRT_CHECK_EQ_TYPE(tensor_r->getType(), dtype); - auto op_pair = ops.find(node_def.op()); - if (op_pair == ops.end()) { - return tensorflow::errors::Unimplemented( - "binary op: ", node_def.op(), " not supported at: ", node_def.name()); - } - + // Add ElementWise layer. nvinfer1::IElementWiseLayer* layer = params->converter->network()->addElementWise( - // TODO(aaroey): will tensor_l/tensor_r get modified? *const_cast(tensor_l), *const_cast(tensor_r), op_pair->second); TFTRT_RETURN_ERROR_IF_NULLPTR(layer, node_def.name()); - nvinfer1::ITensor* output_tensor = layer->getOutput(0); - // pass the output + // Pass the output params->outputs->push_back(TRT_TensorOrWeights(output_tensor)); return tensorflow::Status::OK(); } @@ -1789,6 +1945,8 @@ tensorflow::Status ConvertPool(OpConverterParams* params) { nvinfer1::DimsHW(padding[0].first, padding[1].first), nvinfer1::DimsHW(padding[0].second, padding[1].second)); TFTRT_RETURN_ERROR_IF_NULLPTR(pad_layer, node_def.name()); + params->converter->MarkQuantizationRangesAsInferrable( + const_cast(tensor), pad_layer->getOutput(0)); padding = {{0, 0}, {0, 0}}; tensor = pad_layer->getOutput(0); } @@ -1796,6 +1954,11 @@ tensorflow::Status ConvertPool(OpConverterParams* params) { nvinfer1::IPoolingLayer* layer = params->converter->network()->addPooling( *const_cast(tensor), type, ksize); TFTRT_RETURN_ERROR_IF_NULLPTR(layer, node_def.name()); + // TODO(tmorris): Average pooling may not be entirely safe to infer + // quantization range through (at least forwards - backwards should be fine). + // Max pooling is okay. + params->converter->MarkQuantizationRangesAsInferrable( + const_cast(tensor), layer->getOutput(0)); layer->setStride(stride); layer->setPadding({padding[0].first, padding[1].first}); @@ -1813,110 +1976,290 @@ tensorflow::Status ConvertPool(OpConverterParams* params) { } tensorflow::Status ConvertActivation(OpConverterParams* params) { - const nvinfer1::ITensor* tensor = params->inputs.at(0).tensor(); + const auto& inputs = params->inputs; + const auto& node_def = params->node_def; + if (inputs.size() != 1) { + return tensorflow::errors::InvalidArgument( + node_def.op(), " expects one input, at ", node_def.name()); + } + if (!inputs.at(0).is_tensor()) { + return tensorflow::errors::Unimplemented( + node_def.op(), " is only implemented for tensors, at ", + node_def.name()); + } + static const std::unordered_map ops{ + {"Relu", nvinfer1::ActivationType::kRELU}, + {"Sigmoid", nvinfer1::ActivationType::kSIGMOID}, + {"Tanh", nvinfer1::ActivationType::kTANH}, + }; + auto op_pair = ops.find(node_def.op()); + if (op_pair == ops.end()) { + return tensorflow::errors::Unimplemented("Activation op: ", node_def.op(), + " not supported at: ", + node_def.name()); + } + if (params->validation_only) return tensorflow::Status::OK(); + + // Start conversion. + const nvinfer1::ITensor* tensor = inputs.at(0).tensor(); nvinfer1::IActivationLayer* layer = params->converter->network()->addActivation( - *const_cast(tensor), - nvinfer1::ActivationType::kRELU); - TFTRT_RETURN_ERROR_IF_NULLPTR(layer, params->node_def.name()); + *const_cast(tensor), op_pair->second); + TFTRT_RETURN_ERROR_IF_NULLPTR(layer, node_def.name()); nvinfer1::ITensor* output_tensor = layer->getOutput(0); + // Set quantization range for output of Sigmoid, Tanh. + if (node_def.op() == "Sigmoid") { + params->converter->ProvideQuantizationRange(output_tensor, 0.0f, 1.0f); + } else if (node_def.op() == "Tanh") { + params->converter->ProvideQuantizationRange(output_tensor, -1.0f, 1.0f); + } params->outputs->push_back(TRT_TensorOrWeights(output_tensor)); return tensorflow::Status::OK(); } -tensorflow::Status ConvertScale(OpConverterParams* params) { +Status ConvertQuantize(OpConverterParams* params) { const auto& inputs = params->inputs; const auto& node_def = params->node_def; - if (inputs.size() != 2 || !inputs.at(0).is_tensor() || - !inputs.at(1).is_weights()) { + if ((inputs.size() == 0) || + (node_def.op() == "FakeQuantWithMinMaxArgs" && inputs.size() != 1) || + (node_def.op() == "FakeQuantWithMinMaxVars" && inputs.size() != 3) || + (node_def.op() == "QuantizeAndDequantizeV2" && inputs.size() != 3) || + (node_def.op() == "QuantizeAndDequantizeV3" && inputs.size() != 4)) { + return errors::InvalidArgument("Invalid number of inputs for ", + node_def.op(), ", at ", node_def.name()); + } + if (inputs.at(0).is_weights()) { + // TensorRT will automatically quantize weights, so we will ignore ranges + // for weights. + params->outputs->push_back(inputs.at(0)); + return Status::OK(); + } + float min_range = 0.0f; + float max_range = 0.0f; + if (node_def.op() == "FakeQuantWithMinMaxArgs") { + // Get ranges via node attributes. + TFAttrs attrs(node_def); + if (attrs.count("min") == 0 || attrs.count("max") == 0) { + return errors::InvalidArgument("Min or max attribute not found for ", + node_def.op(), " at ", node_def.name()); + } + min_range = attrs.get("min"); + max_range = attrs.get("max"); + } else if (node_def.op() == "FakeQuantWithMinMaxVars" || + node_def.op() == "QuantizeAndDequantizeV2" || + node_def.op() == "QuantizeAndDequantizeV3") { + // Get ranges via inputs. + if (!inputs.at(1).is_weights() || !inputs.at(2).is_weights()) { + return errors::InvalidArgument("Min and max inputs for ", node_def.op(), + " must be weights not tensors, at ", + node_def.name()); + } + auto get_weights_value = [&inputs](int index) { + auto raw_weights = static_cast( + const_cast(inputs.at(index).weights().GetValues())); + return raw_weights[0]; + }; + min_range = get_weights_value(1); + max_range = get_weights_value(2); + } else { + return errors::InvalidArgument("Unknown quantization op ", node_def.op(), + ", at ", node_def.name()); + } + if (params->validation_only) return Status::OK(); + + // Store ranges for tensor + params->converter->ProvideQuantizationRange( + const_cast(inputs.at(0).tensor()), min_range, + max_range); + // Sometimes, TRT may not quantize a tensor, either because it chooses to + // execute a higher precision kernel or because of op fusion. In these cases, + // accuracy will suffer if the model was trained to expect quantization at + // that tensor. We should consider adding a clip(tensor, min_range, max_range) + // operation here to ensure that any arbitrarily placed quantize node will + // execute as expected. However, this will negatively affect performance. If + // users train their models in a way which models inference as close as + // possible (i.e. not quantizing in place where fusion will occur), then there + // is no problem with the current implementation. + params->outputs->push_back(inputs.at(0)); + return Status::OK(); +} + +// TODO(pdavoodi): we should update relu6 implementation once TensorRT supports +// Relu6 natively. +tensorflow::Status ConvertRelu6(OpConverterParams* params) { + const auto& inputs = params->inputs; + const auto& node_def = params->node_def; + if (inputs.size() != 1) { + return tensorflow::errors::InvalidArgument( + "Invalid number of inputs for Relu6, at ", node_def.name()); + } + if (inputs.at(0).is_weights()) { return tensorflow::errors::Unimplemented( - "ConvertScale only supports tensorweight: ", node_def.name()); + "Relu6 is only implemented for tensors, not weights, at ", + node_def.name()); } + if (params->validation_only) return Status::OK(); + // *************************************************************************** + // TensorRT does not implement Relu6 natively. This function converts Relu6 op + // to available TensorRT ops: Relu6(x) = min(Relu(x), 6) + // *************************************************************************** + // Input Tensor const nvinfer1::ITensor* tensor = inputs.at(0).tensor(); - TRT_ShapedWeights weights = inputs.at(1).weights(); - if (params->converter->is_fp16()) { - weights = ConvertFP32ToFP16(params->weight_store, inputs.at(1).weights()); - } - TRT_ShapedWeights empty_weights(weights.type_); - TFAttrs attrs(node_def); + // Relu operation i.e. Relu(x) = max(0, x) + nvinfer1::IActivationLayer* relu_layer = + params->converter->network()->addActivation( + *const_cast(tensor), + nvinfer1::ActivationType::kRELU); + TFTRT_RETURN_ERROR_IF_NULLPTR(relu_layer, node_def.name()); + + // Large range of relu is problematic during quantization in INT8 precision + // mode. Setting dynamic range of relu = [0.f, 6.0f] helps with quantization. + // TRT only uses dynamic ranges in INT8 precision mode, + // and this does not affect the FP32 path. + params->converter->ProvideQuantizationRange(relu_layer->getOutput(0), 0.0f, + 6.0f); + + // Create a constant layer to store the floating point weight i.e. 6.0f This + // tensor will be broadcasted uniformly during elementwise `min` operation. + // The constant has to have the same rank as the input in order for TRT to + // broadcast + nvinfer1::Dims dims; + dims.nbDims = relu_layer->getOutput(0)->getDimensions().nbDims; + for (int i = 0; i < dims.nbDims; i++) { + dims.d[i] = 1; + } + TRT_ShapedWeights weights = params->weight_store->GetTempWeights( + tensorflow::DataType::DT_FLOAT, dims); + auto weights_ptr = + static_cast(const_cast(weights.GetValues())); + weights_ptr[0] = 6.0f; + nvinfer1::IConstantLayer* const6_layer = + params->converter->network()->addConstant(dims, weights.GetTrtWeights()); + TFTRT_RETURN_ERROR_IF_NULLPTR(const6_layer, node_def.name()); + params->converter->ProvideQuantizationRange(const6_layer->getOutput(0), 0.0f, + 6.0f); + + // ElementWise Min Operation + // Min op is a nop for INT8 execution path, as the input tensor + // to this layer will only have values in range [0.f, 6.0f]. + const nvinfer1::ITensor* tensor_l = relu_layer->getOutput(0); + const nvinfer1::ITensor* tensor_r = const6_layer->getOutput(0); + nvinfer1::IElementWiseLayer* relu6_layer = + params->converter->network()->addElementWise( + *const_cast(tensor_l), + *const_cast(tensor_r), + nvinfer1::ElementWiseOperation::kMIN); + TFTRT_RETURN_ERROR_IF_NULLPTR(relu6_layer, node_def.name()); + nvinfer1::ITensor* output_tensor = relu6_layer->getOutput(0); + params->converter->ProvideQuantizationRange(output_tensor, 0.0f, 6.0f); - const auto data_format = attrs.get("data_format"); - int channel_index; - const auto dims = tensor->getDimensions(); - if (data_format == "NHWC") { - // 1). NHWC is really N+C - channel_index = dims.nbDims - 1; // batch dimension is implicit here! - } else { - // 2). NCHW is really N+CHW - channel_index = 0; // batch dimension is implicit here! - } + params->outputs->push_back(TRT_TensorOrWeights(output_tensor)); + return Status::OK(); +} - nvinfer1::Permutation permutation; - for (int32_t i = 0; i < dims.nbDims; ++i) { - permutation.order[i] = i; +tensorflow::Status ConvertBiasAdd(OpConverterParams* params) { + const auto& inputs = params->inputs; + const auto& node_def = params->node_def; + if (inputs.size() != 2 || !inputs.at(0).is_tensor() || + !inputs.at(1).is_weights()) { + return errors::InvalidArgument("Input expects tensor and weights, at ", + node_def.name()); } + if (params->validation_only) return Status::OK(); - if (channel_index >= 0) { + nvinfer1::ITensor* tensor = + const_cast(inputs.at(0).tensor()); + const nvinfer1::Dims original_dims = tensor->getDimensions(); + TFAttrs attrs(node_def); + const string data_format = attrs.get("data_format"); + const int channel_index = + (data_format == "NHWC" ? original_dims.nbDims - 1 : 0); + + nvinfer1::Permutation permutation; + if (channel_index != 0) { + // Permute the dimensions so that the channel dimension is the first + // dimension. + for (int i = 0; i < original_dims.nbDims; ++i) { + permutation.order[i] = i; + } permutation.order[0] = channel_index; permutation.order[channel_index] = 0; - } else { - return tensorflow::errors::Unimplemented( - "TFTRT::BiasAdd cannot apply on batch dimension, at ", node_def.name()); + VLOG(1) << "ConvertBiasAdd permutation: " + << DebugString(permutation, original_dims.nbDims); } // TensorRT addScale requires input to be of rank 3, we need to apply - // transpose as well as reshape - if (channel_index != 0 || dims.nbDims != 3) { + // transpose as well as reshape. + // TODO(laigd): this doesn't match what the TRT doc says, fix the doc? + if (channel_index != 0 || original_dims.nbDims != 3) { nvinfer1::IShuffleLayer* shuffle_layer = - params->converter->network()->addShuffle( - *const_cast(tensor)); + params->converter->network()->addShuffle(*tensor); TFTRT_RETURN_ERROR_IF_NULLPTR(shuffle_layer, node_def.name()); + params->converter->MarkQuantizationRangesAsInferrable( + tensor, shuffle_layer->getOutput(0)); + + // NOTE(laigd): for some reason we need to apply the reshape + // unconditionally. The default shape has nbDims==-1 and it seems the + // behavior is undefined in some cases. nvinfer1::Dims reshape_dims; reshape_dims.nbDims = 3; - reshape_dims.d[0] = 0; // 0 copy from the input - reshape_dims.d[1] = dims.nbDims >= 2 ? 0 : 1; // 0 copy from the input - reshape_dims.d[2] = dims.nbDims >= 3 ? -1 : 1; // -1 infer from the rest + // 0 means copying from input; -1 means inferring from the rest. + reshape_dims.d[0] = 0; + reshape_dims.d[1] = original_dims.nbDims >= 2 ? 0 : 1; + reshape_dims.d[2] = original_dims.nbDims >= 3 ? -1 : 1; + shuffle_layer->setReshapeDimensions(reshape_dims); + if (channel_index != 0) { - // maybe we do not need this check. concerned about TRT optimization shuffle_layer->setFirstTranspose(permutation); } - shuffle_layer->setReshapeDimensions(reshape_dims); tensor = shuffle_layer->getOutput(0); } + TRT_ShapedWeights weights = inputs.at(1).weights(); + if (params->converter->precision_mode() == FP16MODE) { + weights = ConvertFP32ToFP16(params->weight_store, weights); + } nvinfer1::ScaleMode mode = nvinfer1::ScaleMode::kCHANNEL; if (weights.shape_.d[0] == 1) { mode = nvinfer1::ScaleMode::kUNIFORM; } + TRT_ShapedWeights empty_weights(weights.type_); nvinfer1::IScaleLayer* layer = params->converter->network()->addScale( - *const_cast(tensor), mode, weights.GetTrtWeights(), - empty_weights.GetTrtWeights(), empty_weights.GetTrtWeights()); + *tensor, mode, weights.GetTrtWeights(), empty_weights.GetTrtWeights(), + empty_weights.GetTrtWeights()); TFTRT_RETURN_ERROR_IF_NULLPTR(layer, node_def.name()); nvinfer1::ITensor* output_tensor = layer->getOutput(0); - // restore transpose & reshape - if (channel_index != 0 || dims.nbDims != 3) { + // Restore transpose & reshape. + if (channel_index != 0 || original_dims.nbDims != 3) { nvinfer1::IShuffleLayer* shuffle_layer = - params->converter->network()->addShuffle( - *const_cast(output_tensor)); + params->converter->network()->addShuffle(*output_tensor); TFTRT_RETURN_ERROR_IF_NULLPTR(shuffle_layer, node_def.name()); - nvinfer1::Dims reshape_dims = dims; - int tmp = reshape_dims.d[channel_index]; - reshape_dims.d[channel_index] = reshape_dims.d[0]; - reshape_dims.d[0] = tmp; + // NOTE: for same reason as mentioned above we need to apply the reshape + // unconditionally. + nvinfer1::Dims reshape_dims = original_dims; + if (channel_index != 0) { + // NOTE: according to NVIDIA dimension types are deprecated, so we don't + // need to copy them back. + reshape_dims.d[channel_index] = original_dims.d[0]; + reshape_dims.d[0] = original_dims.d[channel_index]; + } shuffle_layer->setReshapeDimensions(reshape_dims); + if (channel_index != 0) { shuffle_layer->setSecondTranspose(permutation); } + params->converter->MarkQuantizationRangesAsInferrable( + output_tensor, shuffle_layer->getOutput(0)); output_tensor = shuffle_layer->getOutput(0); } params->outputs->push_back(TRT_TensorOrWeights(output_tensor)); - return tensorflow::Status::OK(); + return Status::OK(); } Status GetTensorDimsWithProtoShape(const Tensor& tensor, @@ -2053,9 +2396,9 @@ tensorflow::Status ConvertConst(OpConverterParams* params) { uint8* data = reinterpret_cast(temp_weights.data()); std::copy(data, data + tensor.NumElements(), dst); } else { - return errors::FailedPrecondition( - "Unexpected data type: ", DataTypeString(dtype), - " at: ", node_def.name()); + return errors::FailedPrecondition("Unexpected data type: ", + DataTypeString(dtype), " at: ", + node_def.name()); } } } @@ -2070,32 +2413,41 @@ tensorflow::Status ConvertConst(OpConverterParams* params) { } tensorflow::Status ConvertIdentity(OpConverterParams* params) { + // TODO(tmorris): TRT's Identity layer does not get optimized away as of TRT + // 5.0, however once we know that it does it would be nice to use that + // instead. params->outputs->push_back(params->inputs.at(0)); return tensorflow::Status::OK(); } -tensorflow::Status ConvertBinary(OpConverterParams* params) { +Status ConvertBinary(OpConverterParams* params) { const auto& inputs = params->inputs; const auto& node_def = params->node_def; if (inputs.size() != 2) { - return tensorflow::errors::FailedPrecondition( - "Binary ops require two tensor input, at ", node_def.name()); + return errors::InvalidArgument("Binary ops require two inputs, at ", + node_def.name()); } // Constant folding should have been done by TensorFlow - if (inputs.at(0).is_weights() && inputs.at(1).is_weights()) { - return tensorflow::errors::Unimplemented( + return errors::Unimplemented( "Constant folding is falled back to TensorFlow, binary op received " "both input as constant at: ", node_def.name()); } - // Try to convert into Scale layer first (for better performance) + // TODO(tmorris): TRT plans to deprecate IScaleLayer and will replace it with + // IElementwiseLayer. At that point, we can remove BinaryTensorOpWeight. For + // now, the performance will be slightly better with IScaleLayer because it + // can be fused in more situations. However, most of the benefits of + // IScaleLayer are when the layer performs both a shift and a scale, which we + // don't do except for convolutions. + // + // Try to convert into Scale layer first (for better performance). // Since scale layer supports restricted broadcast policy and op types, we // allow failure and try to handle it through Elementwise op - // (BinaryTensorOpTensor) - Status status = tensorflow::Status::OK(); + // (BinaryTensorOpTensor). + Status status = Status::OK(); if (inputs.at(0).is_tensor() && inputs.at(1).is_weights()) { status = BinaryTensorOpWeight(params, inputs.at(0).tensor(), inputs.at(1).weights(), false); @@ -2103,7 +2455,10 @@ tensorflow::Status ConvertBinary(OpConverterParams* params) { status = BinaryTensorOpWeight(params, inputs.at(1).tensor(), inputs.at(0).weights(), true); } + // If both input are tensors, or one of them is weights but the conversion + // above failed, try the conversion using BinaryTensorOpTensor. if ((inputs.at(0).is_tensor() && inputs.at(1).is_tensor()) || !status.ok()) { + if (!status.ok()) VLOG(1) << status; status = BinaryTensorOpTensor(params, inputs.at(0), inputs.at(1)); } return status; @@ -2133,6 +2488,20 @@ tensorflow::Status ConvertUnary(OpConverterParams* params) { nvinfer1::IUnaryLayer* layer; if (node_def.op() == "Rsqrt") { + // We will need a quantization range for intermediate tensor if not using + // calibration. + // + // x -> [Sqrt] -> sqrt(x) -> [Recip] -> 1/sqrt(x) + // ^ + // need range here + if (params->converter->precision_mode() == INT8MODE && + !params->converter->use_calibration()) { + return errors::Unimplemented( + "Intermediate quantization range cannot be determined without" + " calibration for Rsqrt, consider replacing with " + "Sqrt -> FakeQuant -> Reciprocal ops, at ", + node_def.name()); + } layer = params->converter->network()->addUnary( *const_cast(tensor), nvinfer1::UnaryOperation::kSQRT); @@ -2156,6 +2525,48 @@ tensorflow::Status ConvertUnary(OpConverterParams* params) { return tensorflow::Status::OK(); } +tensorflow::Status ConvertSquare(OpConverterParams* params) { + const auto& inputs = params->inputs; + const auto& node_def = params->node_def; + if (inputs.size() != 1) { + return tensorflow::errors::InvalidArgument("Square expects one input, at ", + node_def.name()); + } + if (inputs.at(0).is_weights()) { + return tensorflow::errors::Unimplemented( + "Square is only implemented for tensors, at ", node_def.name()); + } + if (params->validation_only) return Status::OK(); + + // Constant 2 with same rank as input + nvinfer1::Dims dims = inputs.at(0).GetTrtDims(); + for (int i = 0; i < dims.nbDims; i++) { + dims.d[i] = 1; + } + TRT_ShapedWeights weights = params->weight_store->GetTempWeights( + tensorflow::DataType::DT_FLOAT, dims); + auto weights_ptr = + static_cast(const_cast(weights.GetValues())); + weights_ptr[0] = 2.f; + nvinfer1::IConstantLayer* const2_layer = + params->converter->network()->addConstant(dims, weights.GetTrtWeights()); + TFTRT_RETURN_ERROR_IF_NULLPTR(const2_layer, node_def.name()); + + // ElementWise Pow Operation + const nvinfer1::ITensor* tensor_l = inputs.at(0).tensor(); + const nvinfer1::ITensor* tensor_r = const2_layer->getOutput(0); + nvinfer1::IElementWiseLayer* layer = + params->converter->network()->addElementWise( + *const_cast(tensor_l), + *const_cast(tensor_r), + nvinfer1::ElementWiseOperation::kPOW); + TFTRT_RETURN_ERROR_IF_NULLPTR(layer, node_def.name()); + nvinfer1::ITensor* output_tensor = layer->getOutput(0); + + params->outputs->push_back(TRT_TensorOrWeights(output_tensor)); + return tensorflow::Status::OK(); +} + tensorflow::Status ConvertReduce(OpConverterParams* params) { const auto& inputs = params->inputs; const auto& node_def = params->node_def; @@ -2692,6 +3103,8 @@ tensorflow::Status ConvertSoftmax(OpConverterParams* params) { layer->setAxes(1 << (nbDims - 1)); nvinfer1::ITensor* output_tensor = layer->getOutput(0); + // Quantization range for SoftMax is always (0, 1) + params->converter->ProvideQuantizationRange(output_tensor, 0.0f, 1.0f); params->outputs->push_back(TRT_TensorOrWeights(output_tensor)); return tensorflow::Status::OK(); } @@ -2716,9 +3129,9 @@ tensorflow::Status ConvertTopK(OpConverterParams* params) { op = nvinfer1::TopKOperation::kMAX; reducedAxes |= 1 << (nbDims - 1); } else { - return tensorflow::errors::Unimplemented( - "Operation: " + node_def.op() + - " not implemented, at: " + node_def.name()); + return tensorflow::errors::Unimplemented("Operation: " + node_def.op() + + " not implemented, at: " + + node_def.name()); } nvinfer1::ITopKLayer* layer = params->converter->network()->addTopK( @@ -2732,40 +3145,52 @@ tensorflow::Status ConvertTopK(OpConverterParams* params) { return tensorflow::Status::OK(); } -void TrtNodeValidator::RegisterOpValidators() { +static void RegisterValidatableOpConverters( + std::unordered_map* registration) { // TODO(laigd): support all op types. - op_validators_["Const"] = ConvertConst; - op_validators_["Transpose"] = ConvertTranspose; - op_validators_["Reshape"] = ConvertReshape; - op_validators_["MatMul"] = ConvertMatMul; + (*registration)["BiasAdd"] = ConvertBiasAdd; + (*registration)["Const"] = ConvertConst; + (*registration)["Transpose"] = ConvertTranspose; + (*registration)["Reshape"] = ConvertReshape; + (*registration)["MatMul"] = ConvertMatMul; + (*registration)["Relu6"] = ConvertRelu6; + (*registration)["Square"] = ConvertSquare; + + for (auto quantization_op_type : + {"QuantizeAndDequantizeV2", "QuantizeAndDequantizeV3", + "FakeQuantWithMinMaxVars", "FakeQuantWithMinMaxArgs"}) { + (*registration)[quantization_op_type] = ConvertQuantize; + } + for (auto binary_op_type : + {"Add", "Mul", "Sub", "Div", "RealDiv", "Maximum", "Minimum"}) { + (*registration)[binary_op_type] = ConvertBinary; + } + for (auto activation_op_type : {"Relu", "Sigmoid", "Tanh"}) { + (*registration)[activation_op_type] = ConvertActivation; + } +} + +void TrtNodeValidator::RegisterOpValidators() { + RegisterValidatableOpConverters(&op_validators_); } void Converter::RegisterOpConverters() { - // vgg_16 slim implementation + RegisterValidatableOpConverters(&op_registry_); + op_registry_["Conv2D"] = ConvertConv2D; op_registry_["DepthwiseConv2dNative"] = ConvertConv2DDepthwise; - op_registry_["Relu"] = ConvertActivation; op_registry_["MaxPool"] = ConvertPool; op_registry_["AvgPool"] = ConvertPool; - op_registry_["BiasAdd"] = ConvertScale; - op_registry_["Const"] = ConvertConst; // TODO(ben,jie): this is a temp hack. op_registry_["Identity"] = ConvertIdentity; // Identity should be removed op_registry_["Snapshot"] = ConvertIdentity; // Snapshot should be removed - // resnet_50_v1 slim implementation - op_registry_["Add"] = ConvertBinary; - op_registry_["Mul"] = ConvertBinary; - op_registry_["Sub"] = ConvertBinary; op_registry_["Pad"] = ConvertPad; op_registry_["ConcatV2"] = ConvertConcat; op_registry_["FusedBatchNorm"] = ConvertFusedBatchNorm; op_registry_["FusedBatchNormV2"] = ConvertFusedBatchNorm; - op_registry_["Div"] = ConvertBinary; - op_registry_["RealDiv"] = ConvertBinary; - op_registry_["Rsqrt"] = ConvertUnary; op_registry_["Reciprocal"] = ConvertUnary; op_registry_["Exp"] = ConvertUnary; @@ -2774,20 +3199,19 @@ void Converter::RegisterOpConverters() { op_registry_["Abs"] = ConvertUnary; op_registry_["Neg"] = ConvertUnary; - op_registry_["Transpose"] = ConvertTranspose; - op_registry_["Reshape"] = ConvertReshape; - op_registry_["Sum"] = ConvertReduce; op_registry_["Prod"] = ConvertReduce; op_registry_["Max"] = ConvertReduce; op_registry_["Min"] = ConvertReduce; op_registry_["Mean"] = ConvertReduce; - op_registry_["Maximum"] = ConvertBinary; - op_registry_["Minimum"] = ConvertBinary; op_registry_["Softmax"] = ConvertSoftmax; - op_registry_["MatMul"] = ConvertMatMul; op_registry_["BatchMatMul"] = ConvertBatchMatMul; op_registry_["TopKV2"] = ConvertTopK; + op_registry_["Relu6"] = ConvertRelu6; + op_registry_["QuantizeAndDequantizeV2"] = ConvertQuantize; + op_registry_["QuantizeAndDequantizeV3"] = ConvertQuantize; + op_registry_["FakeQuantWithMinMaxVars"] = ConvertQuantize; + op_registry_["FakeQuantWithMinMaxArgs"] = ConvertQuantize; plugin_converter_ = ConvertPlugin; } @@ -2798,7 +3222,7 @@ tensorflow::Status ConvertGraphDefToEngine( const std::vector& input_shapes, Logger* logger, nvinfer1::IGpuAllocator* allocator, TRTInt8Calibrator* calibrator, - TrtUniquePtrType* engine, + TrtUniquePtrType* engine, bool use_calibration, bool* convert_successfully) { engine->reset(); if (convert_successfully) *convert_successfully = false; @@ -2813,7 +3237,11 @@ tensorflow::Status ConvertGraphDefToEngine( builder->setHalf2Mode(true); } else if (precision_mode == INT8MODE) { builder->setInt8Mode(true); - builder->setInt8Calibrator(calibrator); + if (use_calibration) { + builder->setInt8Calibrator(calibrator); + } else { + builder->setInt8Calibrator(nullptr); + } } // Create the network. @@ -2826,7 +3254,7 @@ tensorflow::Status ConvertGraphDefToEngine( // Build the network VLOG(1) << "Starting engine conversion "; - Converter converter(trt_network.get(), precision_mode == FP16MODE); + Converter converter(trt_network.get(), precision_mode, use_calibration); std::vector> output_tensors; // Graph nodes are already topologically sorted during construction for (const auto& node_def : gdef.node()) { @@ -2882,6 +3310,9 @@ tensorflow::Status ConvertGraphDefToEngine( TF_RETURN_IF_ERROR(converter.RenameAndMarkOutputTensors(output_tensors)); if (convert_successfully) *convert_successfully = true; + // Apply user provided quantization ranges to tensors + converter.MaybeApplyQuantizationRanges(); + // Build the engine. VLOG(1) << "Starting engine creation"; engine->reset(builder->buildCudaEngine(*converter.network())); @@ -3026,7 +3457,8 @@ tensorflow::Status ConvertSegmentToGraphDef( } } *common_scope = local_scope; - VLOG(0) << "Segment @scope '" << local_scope << "', converted to graph"; + VLOG(1) << "Converted TensorRT candidate segment @scope '" << local_scope + << "' to a GraphDef"; return tensorflow::Status::OK(); } diff --git a/tensorflow/contrib/tensorrt/convert/convert_nodes.h b/tensorflow/contrib/tensorrt/convert/convert_nodes.h index 5cc28b33e7..f1c4c121ae 100644 --- a/tensorflow/contrib/tensorrt/convert/convert_nodes.h +++ b/tensorflow/contrib/tensorrt/convert/convert_nodes.h @@ -92,7 +92,8 @@ struct EngineInfo { EngineInfo() : engine_type(EngineType::TRTStatic), max_workspace_size_bytes(0), - precision_mode(FP32MODE) {} + precision_mode(FP32MODE), + use_calibration(true) {} string engine_name; string device; @@ -109,6 +110,7 @@ struct EngineInfo { int maximum_cached_engines; std::vector cached_engine_batches; int precision_mode; + bool use_calibration; }; // Constructs a graphdef from the segment in the given graph. Adds placeholder @@ -145,7 +147,7 @@ tensorflow::Status ConvertGraphDefToEngine( const std::vector& input_shapes, Logger* logger, nvinfer1::IGpuAllocator* allocator, TRTInt8Calibrator* calibrator, - TrtUniquePtrType* engine, + TrtUniquePtrType* engine, bool use_calibration, bool* convert_successfully); // Helper class for the segmenter to determine whether an output edge from the @@ -392,7 +394,8 @@ class TrtNodeValidator { // Class to convert TF nodes to TRT network. class Converter { public: - Converter(nvinfer1::INetworkDefinition* trt_network, bool is_fp16); + Converter(nvinfer1::INetworkDefinition* trt_network, int precision_mode, + bool use_calibration); ////////////////////////////////////////////////////////////////////////////// // Methods used by the TRT engine builder to build a TRT network from a TF @@ -422,8 +425,43 @@ class Converter { // to add TRT layers. nvinfer1::INetworkDefinition* network() { return trt_network_; } - // Is the converter operating in fp16 mode? - bool is_fp16() const { return is_fp16_; } + // What precision are we targeting? + int precision_mode() const { return precision_mode_; } + + // Calibration will be or was previously performed on this network? + bool use_calibration() const { return use_calibration_; } + + // This should be called on the inputs and outputs of any layer we create + // where we know that the quantization range does not change during that + // operation. (e.g. Reshape, Transpose, Identity, MaxPool). + void MarkQuantizationRangesAsInferrable(nvinfer1::ITensor* input, + nvinfer1::ITensor* output); + + // This function should be called when we know the quantization range of a + // tensor, either from a quantize/dequantize node or when the output is a + // fixed range (e.g. SoftMax, Relu6, Sigmoid). + void ProvideQuantizationRange(nvinfer1::ITensor* tensor, float min_range, + float max_range); + + // Should be called when full TRT network has been constructed and before + // building the engine. + void MaybeApplyQuantizationRanges(); + + // This should be called on the inputs and outputs of any layer we create + // where we know that the quantization range does not change during that + // operation. (e.g. Reshape, Transpose, Identity, MaxPool). + void MarkQuantizationRangesAsInferrable(nvinfer1::ITensor* input, + nvinfer1::ITensor* output); + + // This function should be called when we know the quantization range of a + // tensor, either from a quantize/dequantize node or when the output is a + // fixed range (e.g. SoftMax, Relu6, Sigmoid). + void ProvideQuantizationRange(nvinfer1::ITensor* tensor, + float min_range, float max_range); + + // Should be called when full TRT network has been constructed and before + // building the engine. + void ApplyQuantizationRanges(bool warn_missing_ranges); // Below are helper methods for op converters to add different layers to the // TRT network. @@ -440,6 +478,13 @@ class Converter { const nvinfer1::Dims& dims, const nvinfer1::ITensor** tensor); + // Return OK if the broadcast scheme is supported and compute the shapes after + // broadcasting. + Status GetTrtBroadcastShape(const TRT_TensorOrWeights& operand_l, + const TRT_TensorOrWeights& operand_r, + nvinfer1::Dims* operand_l_new_dims, + nvinfer1::Dims* operand_r_new_dims) const; + private: // Verify the provided batch_size is consistent with batch_size_ and update it // if necessary. @@ -457,6 +502,12 @@ class Converter { void RegisterOpConverters(); + void PropagateQuantizationRanges(); + + // Gets the min and max value in a TRT_ShapedWeights + Status GetWeightRange(const TRT_ShapedWeights& weights, float* out_min, + float* out_max) const; + // Registered op converters by op type. std::unordered_map op_registry_; @@ -472,7 +523,25 @@ class Converter { // Store the weights added during construction of trt_network_. TrtWeightStore weight_store_; - const bool is_fp16_; + // During conversion, this table is populated with quantization ranges per + // tensor. MaybeApplyQuantizationRanges() will use this table to set the TRT + // quantization ranges. Since TRT only supports symmetric ranges, we will + // store the range as a single float = max(abs(min_range), abs(max_range)). + // Range refers to the floating point values, e.g. min_range = 0.0f, max_range + // = 6.0f for Relu6. + std::unordered_map quantization_ranges_; + + // Edges where quantization ranges can be inferred (copied) across ops - from + // first tensor to second tensor. PropagateQuantizationRanges() will propagate + // known ranges from quantization_ranges_ across these edges, adding the new + // ranges to quantization_ranges_ so that they can be applied in + // MaybeApplyQuantizationRanges(). + std::vector> + quantization_infer_; + + const int precision_mode_; + + const bool use_calibration_; // Batch size of inputs to trt_network_ added by AddInputTensor(). During // network construction it will update this, use it to verify the batch diff --git a/tensorflow/contrib/tensorrt/convert/convert_nodes_test.cc b/tensorflow/contrib/tensorrt/convert/convert_nodes_test.cc index c3a39395f3..a95ab8dfbb 100644 --- a/tensorflow/contrib/tensorrt/convert/convert_nodes_test.cc +++ b/tensorflow/contrib/tensorrt/convert/convert_nodes_test.cc @@ -35,7 +35,10 @@ limitations under the License. #include "tensorflow/core/grappler/costs/graph_properties.h" #include "tensorflow/core/lib/core/status.h" #include "tensorflow/core/lib/core/status_test_util.h" +#include "tensorflow/core/lib/strings/strcat.h" #include "tensorflow/core/platform/test.h" +#include "tensorflow/core/protobuf/config.pb.h" // NOLINT +#include "tensorflow/core/public/session.h" #if GOOGLE_CUDA #if GOOGLE_TENSORRT @@ -47,7 +50,9 @@ namespace tensorflow { namespace tensorrt { namespace convert { +using ::tensorflow::strings::StrCat; using ::testing::ElementsAre; +using ::testing::ElementsAreArray; // TODO(laigd): put this into some test utils file. void ExpectStatus(Status status, error::Code code = error::OK, @@ -69,6 +74,32 @@ nvinfer1::Dims GetTestDims(const std::vector& d) { return dims; } +nvinfer1::DataType TfDataTypeToTrt(DataType tf_dtype) { + switch (tf_dtype) { + case DT_FLOAT: + return nvinfer1::DataType::kFLOAT; + case DT_HALF: + return nvinfer1::DataType::kHALF; + case DT_INT32: + return nvinfer1::DataType::kINT32; + default: + QCHECK(false) << "Unexpected data type " << DataTypeString(tf_dtype); + } +} + +DataType TrtDataTypeToTf(nvinfer1::DataType trt_dtype) { + switch (trt_dtype) { + case nvinfer1::DataType::kFLOAT: + return DT_FLOAT; + case nvinfer1::DataType::kHALF: + return DT_HALF; + case nvinfer1::DataType::kINT32: + return DT_INT32; + default: + QCHECK(false) << "Unexpected data type " << static_cast(trt_dtype); + } +} + NodeDef MakeNodeDef(const string& name, const string& op, const std::vector& inputs) { NodeDef node_def; @@ -111,6 +142,35 @@ bool TrtDimsEqualsArray(const std::vector& lhs, return TrtDimsEquals(GetTestDims(lhs), rhs); } +// TODO(laigd): define a parameterized matcher that can compare against the +// vector. +void ExpectTrtDimsEqualsArray(const std::vector& lhs, + const nvinfer1::Dims& rhs) { + EXPECT_TRUE(TrtDimsEqualsArray(lhs, rhs)) + << "expected: " << DebugString(GetTestDims(lhs)) << "\n" + << " actual: " << DebugString(rhs); +} + +template +void ExpectArrayNear(const std::vector& lhs, const std::vector& rhs) { + ASSERT_EQ(lhs.size(), rhs.size()); + for (int i = 0; i < lhs.size(); i++) { + EXPECT_FLOAT_EQ(lhs[i], rhs[i]); + } +} + +// Eigen::half cannot implicitly convert to float which is required for +// EXPECT_FLOAT_EQ. +template <> +void ExpectArrayNear(const std::vector& lhs, + const std::vector& rhs) { + ASSERT_EQ(lhs.size(), rhs.size()); + for (int i = 0; i < lhs.size(); i++) { + EXPECT_FLOAT_EQ(Eigen::half_impl::half_to_float(lhs[i]), + Eigen::half_impl::half_to_float(rhs[i])); + } +} + bool TrtShapedWeightsEquals(const TRT_ShapedWeights& lhs, const TRT_ShapedWeights& rhs) { return TrtDimsEquals(lhs.shape_, rhs.shape_) && lhs.type_ == rhs.type_ && @@ -121,8 +181,7 @@ template void ValidateWeights(const TRT_ShapedWeights& weights, const std::vector& expected_dims, const std::vector& expected_value) { - EXPECT_TRUE(TrtDimsEqualsArray(expected_dims, weights.shape_)) - << weights.DebugString(); + ExpectTrtDimsEqualsArray(expected_dims, weights.shape_); ASSERT_EQ(expected_value.size(), weights.count()) << weights.DebugString(); const T* actual_values = static_cast(weights.GetValues()); for (int i = 0; i < expected_value.size(); ++i) { @@ -133,11 +192,12 @@ void ValidateWeights(const TRT_ShapedWeights& weights, // Fake ITensor implementation for testing purposes. class FakeITensor : public nvinfer1::ITensor { public: - FakeITensor() {} + FakeITensor() : dynamic_range_(0.0f) {} - FakeITensor(const nvinfer1::Dims& dims) : dims_(dims) {} + FakeITensor(const nvinfer1::Dims& dims) : dims_(dims), dynamic_range_(0.0f) {} - FakeITensor(const std::vector& dims) : dims_(GetTestDims(dims)) {} + FakeITensor(const std::vector& dims) + : dims_(GetTestDims(dims)), dynamic_range_(0.0f) {} void setName(const char* name) override { name_ = name; } @@ -166,7 +226,12 @@ class FakeITensor : public nvinfer1::ITensor { } #if NV_TENSORRT_MAJOR >= 5 - bool setDynamicRange(float min, float max) override {} + bool setDynamicRange(float min, float max) override { + dynamic_range_ = std::max(std::abs(min), std::abs(max)); + return true; + } + + float getDynamicRange() const override { return dynamic_range_; } #endif private: @@ -174,6 +239,7 @@ class FakeITensor : public nvinfer1::ITensor { nvinfer1::Dims dims_; nvinfer1::DataType type_; nvinfer1::TensorLocation location_; + float dynamic_range_; }; TEST(TRT_ShapedWeights_Test, Basic) { @@ -265,9 +331,7 @@ TEST(TRT_TensorOrWeights_Test, Basic) { EXPECT_EQ(1, ptr->batch_size()); } EXPECT_EQ(&itensor, ptr->tensor()); - EXPECT_TRUE(TrtDimsEqualsArray({1}, ptr->GetTrtDims())) - << "- expected: " << DebugString(dims) - << "\n vs\n- actual: " << DebugString(ptr->GetTrtDims()); + ExpectTrtDimsEqualsArray({1}, ptr->GetTrtDims()); } } } @@ -286,9 +350,7 @@ TEST(TRT_TensorOrWeights_Test, Basic) { EXPECT_EQ(false, ptr->is_weights()); EXPECT_EQ(1, ptr->batch_size()); EXPECT_NE(nullptr, ptr->tensor()); - EXPECT_TRUE(TrtDimsEqualsArray({1}, ptr->GetTrtDims())) - << "- expected: " << DebugString(dims) - << "\n vs\n- actual: " << DebugString(ptr->GetTrtDims()); + ExpectTrtDimsEqualsArray({1}, ptr->GetTrtDims()); } } // Test constructor with TRT_ShapedWeights argument. @@ -305,9 +367,7 @@ TEST(TRT_TensorOrWeights_Test, Basic) { nvinfer1::Dims dims; dims.nbDims = 0; - EXPECT_TRUE(TrtDimsEqualsArray({}, ptr->GetTrtDims())) - << "- expected: " << DebugString(dims) - << "\n vs\n- actual: " << DebugString(ptr->GetTrtDims()); + ExpectTrtDimsEqualsArray({}, ptr->GetTrtDims()); } } } @@ -341,34 +401,50 @@ TEST_F(ValidatorTest, ConvertToTensorOrWeights) { graph_properties, &output)); ValidateWeights(output.weights(), {2}, {1.0, 2.0}); } - // Convert non-Const. We test the case where the non-batch dimemsion is - // unknown as well, to make sure the validator allows that. - for (const int32 non_batch_dim : {-1, 2}) { - const int32 batch_size = 12; + // Helper method to run ConvertToTensorOrWeights() with predefined parameters. + auto convert_to_tensor_or_weights = [this](const std::vector& dims, + TRT_TensorOrWeights* output) { Scope s = Scope::NewRootScope(); - ops::Placeholder::Attrs attrs; - TF_EXPECT_OK(TensorShapeUtils::MakeShape( - std::vector{batch_size, non_batch_dim}, &attrs.shape_)); + const auto attrs = ops::Placeholder::Shape(PartialTensorShape{dims}); auto feed = ops::Placeholder(s.WithOpName("feed"), DT_FLOAT, attrs); auto add = ops::Add(s.WithOpName("add"), feed, feed); grappler::GrapplerItem item; TF_EXPECT_OK(s.ToGraphDef(&item.graph)); - grappler::GraphProperties graph_properties(item); TF_EXPECT_OK(graph_properties.InferStatically(true)); - - auto& node_def = add.operation.node()->def(); + const NodeDef& node_def = add.operation.node()->def(); + return this->ConvertToTensorOrWeights(node_def, /*output_port=*/0, + graph_properties, output); + }; + // Convert non-Const with #dims > nvinfer1::Dims::MAX_DIMS+1. + { TRT_TensorOrWeights output; - ExpectStatus(ConvertToTensorOrWeights(node_def, /*output_port=*/0, - graph_properties, &output)); + ExpectStatus( + convert_to_tensor_or_weights( + std::vector(nvinfer1::Dims::MAX_DIMS + 2, 1), &output), + error::OUT_OF_RANGE, "Input tensor rank is greater than 9"); + } + // Convert non-Const with #dims < 2. + { + TRT_TensorOrWeights output; + ExpectStatus( + convert_to_tensor_or_weights({1}, &output), error::INVALID_ARGUMENT, + "Input tensor with rank<2 is not supported since the first dimension " + "is treated as batch dimension by TRT"); + } + // Convert non-Const. We test the case where the non-batch dimemsion is + // unknown as well, to make sure the validator allows that. + for (const int32 non_batch_dim : {-1, 2}) { + const int32 batch_size = 12; + TRT_TensorOrWeights output; + ExpectStatus( + convert_to_tensor_or_weights({batch_size, non_batch_dim}, &output)); EXPECT_EQ(true, output.is_tensor()); EXPECT_EQ(batch_size, output.batch_size()); EXPECT_NE(nullptr, output.tensor()); - EXPECT_TRUE(TrtDimsEqualsArray({non_batch_dim}, output.GetTrtDims())) - << "- expected: {" << non_batch_dim << "} \n vs\n" - << "- actual: " << DebugString(output.GetTrtDims()); + ExpectTrtDimsEqualsArray({non_batch_dim}, output.GetTrtDims()); } } @@ -405,7 +481,9 @@ class ConverterTest : public ::testing::Test { ConverterTest() { builder_.reset(nvinfer1::createInferBuilder(logger_)); network_.reset(builder_->createNetwork()); - converter_.reset(new Converter(network_.get(), /*fp16=*/false)); + converter_.reset(new Converter(network_.get(), + /*precision_mode=*/FP32MODE, + /*use_calibration=*/false)); weight_store_ = &converter_->weight_store_; } @@ -432,8 +510,21 @@ class ConverterTest : public ::testing::Test { return converter_->GetInputs(node_def, inputs); } + Status GetWeightRange(const TRT_ShapedWeights& weights, float* out_min, + float* out_max) const { + return converter_->GetWeightRange(weights, out_min, out_max); + } + + void PropagateQuantizationRanges() { + converter_->PropagateQuantizationRanges(); + } + int batch_size() const { return converter_->batch_size_; } + std::unordered_map& quantization_ranges() { + return converter_->quantization_ranges_; + } + private: Logger logger_; // These members are ordered in a way such that the destruction order is: @@ -504,9 +595,9 @@ TEST_F(ConverterTest, AddAndGetInputs) { EXPECT_EQ(nvinfer1::DataType::kFLOAT, inputs[0].tensor()->getType()); EXPECT_EQ(nvinfer1::DataType::kINT32, inputs[2].tensor()->getType()); EXPECT_EQ(nvinfer1::DataType::kHALF, inputs[3].tensor()->getType()); - EXPECT_TRUE(TrtDimsEqualsArray({1}, inputs[0].tensor()->getDimensions())); - EXPECT_TRUE(TrtDimsEqualsArray({2, 3}, inputs[2].tensor()->getDimensions())); - EXPECT_TRUE(TrtDimsEqualsArray({5, 3}, inputs[3].tensor()->getDimensions())); + ExpectTrtDimsEqualsArray({1}, inputs[0].tensor()->getDimensions()); + ExpectTrtDimsEqualsArray({2, 3}, inputs[2].tensor()->getDimensions()); + ExpectTrtDimsEqualsArray({5, 3}, inputs[3].tensor()->getDimensions()); } TEST_F(ConverterTest, RenameAndMarkOutputTensors) { @@ -552,7 +643,7 @@ TEST_F(ConverterTest, RenameAndMarkOutputTensors) { {{"my_op", "my_output"}, {"my_op:1", "my_output_1"}})); EXPECT_EQ(2, output_tensors.size()); for (auto output_tensor : output_tensors) { - EXPECT_TRUE(TrtDimsEqualsArray({2, 1}, output_tensor->getDimensions())); + ExpectTrtDimsEqualsArray({2, 1}, output_tensor->getDimensions()); } EXPECT_EQ("my_output", string(output_tensors[0]->getName())); EXPECT_EQ("my_output_1", string(output_tensors[1]->getName())); @@ -577,8 +668,7 @@ TEST_F(ConverterTest, TransposeTensor) { // OK. TF_EXPECT_OK( converter_->TransposeTensor(input_tensor, {0, 3, 1, 2}, &output_tensor)); - EXPECT_TRUE(TrtDimsEqualsArray({5, 2, 3}, output_tensor->getDimensions())) - << DebugString(*output_tensor); + ExpectTrtDimsEqualsArray({5, 2, 3}, output_tensor->getDimensions()); } TEST_F(ConverterTest, PrepareTensorForShape_Tensor) { @@ -590,7 +680,7 @@ TEST_F(ConverterTest, PrepareTensorForShape_Tensor) { // Shape size doesn't match. ExpectStatus(converter_->PrepareTensorForShape(tw, GetTestDims({2, 3, 6}), &output_tensor), - error::INVALID_ARGUMENT, "Reshape shapes are not compatible."); + error::INVALID_ARGUMENT, "Reshape shapes are not compatible"); // TODO(aaroey): we should check the case where uninferred dimensions are not // an exact divisor of input dim ensions, e.g. for dims {-1, 7}. @@ -598,14 +688,12 @@ TEST_F(ConverterTest, PrepareTensorForShape_Tensor) { // Infer shape, ok. TF_EXPECT_OK(converter_->PrepareTensorForShape(tw, GetTestDims({-1, 2}), &output_tensor)); - EXPECT_TRUE(TrtDimsEqualsArray({15, 2}, output_tensor->getDimensions())) - << DebugString(*output_tensor); + ExpectTrtDimsEqualsArray({15, 2}, output_tensor->getDimensions()); // Regular shape. TF_EXPECT_OK(converter_->PrepareTensorForShape(tw, GetTestDims({10, 3}), &output_tensor)); - EXPECT_TRUE(TrtDimsEqualsArray({10, 3}, output_tensor->getDimensions())) - << DebugString(*output_tensor); + ExpectTrtDimsEqualsArray({10, 3}, output_tensor->getDimensions()); } TEST_F(ConverterTest, PrepareTensorForShape_Weights) { @@ -615,8 +703,7 @@ TEST_F(ConverterTest, PrepareTensorForShape_Weights) { const nvinfer1::ITensor* output_tensor = nullptr; TF_EXPECT_OK(converter_->PrepareTensorForShape(tw, GetTestDims({10, 3}), &output_tensor)); - EXPECT_TRUE(TrtDimsEqualsArray({10, 3}, output_tensor->getDimensions())) - << DebugString(*output_tensor); + ExpectTrtDimsEqualsArray({10, 3}, output_tensor->getDimensions()); } TEST_F(ConverterTest, MaybeUpdateBatchSize) { @@ -656,6 +743,178 @@ TEST_F(ConverterTest, AddAndGetTensorOrWeights) { "tensor/weights my_tensor already exist"); } +template +void TestGetWeightRange(ConverterTest* test, TrtWeightStore* weight_store) { + TRT_ShapedWeights weights = + weight_store->GetTempWeights(DataTypeToEnum::v(), GetTestDims({2, 3})); + const std::vector values = {T(3), T(1), T(2), T(6), T(5), T(4)}; + memcpy(const_cast(weights.GetValues()), values.data(), + weights.size_bytes()); + + float out_min = 0.0f; + float out_max = 0.0f; + TF_EXPECT_OK(test->GetWeightRange(weights, &out_min, &out_max)); + EXPECT_EQ(1.0f, out_min); + EXPECT_EQ(6.0f, out_max); +} + +TEST_F(ConverterTest, GetWeightRange) { + TestGetWeightRange(this, weight_store_); + TestGetWeightRange(this, weight_store_); + TestGetWeightRange(this, weight_store_); +} + +TEST_F(ConverterTest, ProvideQuantizationRange) { + FakeITensor fake_tensor; + // Assymetric range + converter_->ProvideQuantizationRange(&fake_tensor, 0.0f, 6.0f); + EXPECT_EQ(6.0f, quantization_ranges()[&fake_tensor]); + converter_->ProvideQuantizationRange(&fake_tensor, 1.0f, 6.0f); + EXPECT_EQ(6.0f, quantization_ranges()[&fake_tensor]); + converter_->ProvideQuantizationRange(&fake_tensor, -8.0f, 6.0f); + EXPECT_EQ(8.0f, quantization_ranges()[&fake_tensor]); + converter_->ProvideQuantizationRange(&fake_tensor, -8.123f, -6.123f); + EXPECT_EQ(8.123f, quantization_ranges()[&fake_tensor]); + // Symmetric range + converter_->ProvideQuantizationRange(&fake_tensor, -6.123f, 6.123f); + EXPECT_EQ(6.123f, quantization_ranges()[&fake_tensor]); +} + +TEST_F(ConverterTest, MaybeApplyQuantizationRanges) { + // input -> infer1 -> infer2 -> infer3 + FakeITensor input, infer_1, infer_2, infer_3; + FakeITensor not_infer; + Converter int8_converter(/*trt_network=*/nullptr, INT8MODE, + /*use_calibration=*/true); + int8_converter.ProvideQuantizationRange(&input, -5.0f, 5.0f); + int8_converter.ProvideQuantizationRange(¬_infer, -100.0f, 100.0f); + int8_converter.MarkQuantizationRangesAsInferrable(&input, &infer_1); + int8_converter.MarkQuantizationRangesAsInferrable(&infer_1, &infer_2); + int8_converter.MarkQuantizationRangesAsInferrable(&infer_2, &infer_3); + + // Input range should be inferred along the chain and applied to tensors. + int8_converter.MaybeApplyQuantizationRanges(); +#if NV_TENSORRT_MAJOR >= 5 + EXPECT_EQ(input.getDynamicRange(), 5.0f); + EXPECT_EQ(infer_1.getDynamicRange(), 5.0f); + EXPECT_EQ(infer_2.getDynamicRange(), 5.0f); + EXPECT_EQ(infer_3.getDynamicRange(), 5.0f); + EXPECT_EQ(not_infer.getDynamicRange(), 100.0f); +#endif +} + +TEST_F(ConverterTest, PropagateQuantizationRanges) { + // infer0 <-> infer1 <-> infer2 <-> infer3 + // | + // infer4 <-> infer5 + FakeITensor infer[6]; + FakeITensor not_infer; + converter_->ProvideQuantizationRange(&infer[4], -5.0f, 5.0f); + converter_->MarkQuantizationRangesAsInferrable(&infer[0], &infer[1]); + converter_->MarkQuantizationRangesAsInferrable(&infer[1], &infer[2]); + converter_->MarkQuantizationRangesAsInferrable(&infer[3], &infer[2]); + converter_->MarkQuantizationRangesAsInferrable(&infer[4], &infer[1]); + converter_->MarkQuantizationRangesAsInferrable(&infer[4], &infer[5]); + + // Input range should be inferred along the chain. + PropagateQuantizationRanges(); + auto ranges = quantization_ranges(); + for (int i = 0; i < 6; ++i) { + EXPECT_EQ(5.0f, ranges[&infer[i]]); + } + EXPECT_EQ(ranges.count(¬_infer), 0); +} + +TEST_F(ConverterTest, GetTrtBroadcastShape) { + const bool kIsTensor = true; + const bool kIsNotTensor = false; + auto symmetric_test = [this](const std::vector& operand_1_shape, + const std::vector& operand_2_shape, + const bool operand_1_is_tensor, + const bool operand_2_is_tensor, + const std::vector& expected_operand_1_shape, + const std::vector& expected_operand_2_shape, + error::Code expected_code = error::OK, + const char* expected_error_msg_substr = nullptr, + const int operand_1_batch_size = -1, + const int operand_2_batch_size = -1) { + auto create_tensor_or_weights = [](const std::vector& shape, + bool is_tensor, int batch_size = -1) { + if (is_tensor) { + return TRT_TensorOrWeights{nvinfer1::DataType::kFLOAT, + GetTestDims(shape), batch_size}; + } + TRT_ShapedWeights weights; + weights.shape_ = GetTestDims(shape); + return TRT_TensorOrWeights(weights); + }; + + nvinfer1::Dims operand_1_new_dims, operand_2_new_dims; + TRT_TensorOrWeights operand_1 = create_tensor_or_weights( + operand_1_shape, operand_1_is_tensor, operand_1_batch_size); + TRT_TensorOrWeights operand_2 = create_tensor_or_weights( + operand_2_shape, operand_2_is_tensor, operand_2_batch_size); + + // operand_1 broadcast operand_2 + ExpectStatus( + this->converter_->GetTrtBroadcastShape( + operand_1, operand_2, &operand_1_new_dims, &operand_2_new_dims), + expected_code, expected_error_msg_substr); + if (expected_code == error::OK) { + ExpectTrtDimsEqualsArray(expected_operand_1_shape, operand_1_new_dims); + ExpectTrtDimsEqualsArray(expected_operand_2_shape, operand_2_new_dims); + } + // operand_2 broadcast operand_1 + ExpectStatus( + this->converter_->GetTrtBroadcastShape( + operand_2, operand_1, &operand_2_new_dims, &operand_1_new_dims), + expected_code, expected_error_msg_substr); + if (expected_code == error::OK) { + ExpectTrtDimsEqualsArray(expected_operand_1_shape, operand_1_new_dims); + ExpectTrtDimsEqualsArray(expected_operand_2_shape, operand_2_new_dims); + } + }; + + // Both inputs are weights. + symmetric_test( + {1}, {1}, kIsNotTensor, kIsNotTensor, {}, {}, error::INVALID_ARGUMENT, + "Broadcasting requires at least one of the operands be tensors"); + + // One tensor and one weights. + symmetric_test({1, 1, 1}, {2}, kIsTensor, kIsNotTensor, {1, 1, 1}, {1, 1, 2}); + symmetric_test({1, 1, 2}, {2}, kIsTensor, kIsNotTensor, {1, 1, 2}, {1, 1, 2}); + symmetric_test({1, 3, 2}, {1}, kIsTensor, kIsNotTensor, {1, 3, 2}, {1, 1, 1}); + symmetric_test({1, 1, 1}, {2, 3}, kIsTensor, kIsNotTensor, {1, 1, 1}, + {1, 2, 3}); + symmetric_test({1, 1, 1}, {2, 3, 4}, kIsTensor, kIsNotTensor, {1, 1, 1}, + {2, 3, 4}); + symmetric_test({1, 1, 1}, {1, 2, 3, 4}, kIsTensor, kIsNotTensor, {1, 1, 1}, + {2, 3, 4}); + symmetric_test({1, 3, 4}, {1, 2, 1, 4}, kIsTensor, kIsNotTensor, {1, 3, 4}, + {2, 1, 4}); + symmetric_test({1, 1, 1}, {2, 1, 1, 1}, kIsTensor, kIsNotTensor, {}, {}, + error::INVALID_ARGUMENT, "Infeasible broadcast scheme"); + symmetric_test({1, 1, 1}, {2, 1, 1, 1}, kIsTensor, kIsNotTensor, {}, {}, + error::INVALID_ARGUMENT, "Infeasible broadcast scheme", + /*operand_1_batch_size=*/2); + symmetric_test({1, 1, 1}, {1, 1, 1, 1, 1}, kIsTensor, kIsNotTensor, {}, {}, + error::INVALID_ARGUMENT, + "Broadcasting beyond batch dimension is not supported " + "(tensor #dims 4 vs broadcast #dims 5)"); + + // Both inputs are tensors. + symmetric_test({1, 1, 1}, {1, 1}, kIsTensor, kIsTensor, {}, {}, + error::INVALID_ARGUMENT, + "Broadcasting beyond batch dimension is not supported " + "(tensor #dims 3 vs broadcast #dims 4)"); + symmetric_test({1, 3, 4}, {2, 1, 4}, kIsTensor, kIsTensor, {1, 3, 4}, + {2, 1, 4}); + symmetric_test({1, 1, 1}, {1, 1, 1, 1}, kIsTensor, kIsTensor, {}, {}, + error::INVALID_ARGUMENT, + "Broadcasting beyond batch dimension is not supported " + "(tensor #dims 4 vs broadcast #dims 5)"); +} + // Class to test various op converters, using both a TrtNodeValidator and // Converter. class OpConverterTest : public ::testing::Test { @@ -684,15 +943,21 @@ class OpConverterTest : public ::testing::Test { // Reset the validator and converter. validator_.reset(new TrtNodeValidator); - converter_.reset(new Converter(network_.get(), /*fp16=*/false)); + converter_.reset(new Converter(network_.get(), + /*precision_mode=*/FP32MODE, + /*use_calibration=*/false)); // Reset other related artifacts. scope_ = Scope::NewRootScope(); validator_inputs_.clear(); } - void BuildAndRun(const char* input_name, const std::vector& input_data, - const char* output_name, std::vector* output_data) { + // TODO(laigd): test fp16 and int8 support. + template + void BuildAndRun( + const std::vector>>& + input_data, + const char* output_name, std::vector* output_data) { // Mark the output tensor as TRT engine output. TF_EXPECT_OK(converter_->RenameAndMarkOutputTensors( {{string(output_name), string(output_name)}})); @@ -703,25 +968,33 @@ class OpConverterTest : public ::testing::Test { CHECK_NOTNULL(engine_.get()); // Execute the TRT engine. - const int input_size = input_data.size() * sizeof(float); - const int output_size = output_data->size() * sizeof(float); - const int input_index = engine_->getBindingIndex(input_name); - const int output_index = engine_->getBindingIndex(output_name); + ASSERT_LE(input_data.size() + 1, 3); + void* buffers[3]; + for (const auto name_and_data : input_data) { + const int input_size = name_and_data.second.size() * sizeof(T); + const int input_index = engine_->getBindingIndex(name_and_data.first); + ASSERT_EQ(0, cudaMalloc(&buffers[input_index], input_size)); + ASSERT_EQ( + 0, cudaMemcpyAsync(buffers[input_index], name_and_data.second.data(), + input_size, cudaMemcpyHostToDevice, stream_)); + } - ASSERT_EQ(engine_->getNbBindings(), 2); - void* buffers[2]; - ASSERT_EQ(0, cudaMalloc(&buffers[input_index], input_size)); + const int output_size = output_data->size() * sizeof(T); + const int output_index = engine_->getBindingIndex(output_name); ASSERT_EQ(0, cudaMalloc(&buffers[output_index], output_size)); - ASSERT_EQ(0, cudaMemcpyAsync(buffers[input_index], input_data.data(), - input_size, cudaMemcpyHostToDevice, stream_)); + + ASSERT_EQ(engine_->getNbBindings(), input_data.size() + 1); + TrtUniquePtrType execution_context( engine_->createExecutionContext()); execution_context->enqueue(/*batchSize=*/1, buffers, stream_, nullptr); ASSERT_EQ(0, cudaMemcpyAsync(output_data->data(), buffers[output_index], output_size, cudaMemcpyDeviceToHost, stream_)); cudaStreamSynchronize(stream_); - ASSERT_EQ(0, cudaFree(buffers[input_index])); - ASSERT_EQ(0, cudaFree(buffers[output_index])); + + for (int i = 0; i < input_data.size() + 1; ++i) { + ASSERT_EQ(0, cudaFree(buffers[i])); + } } bool HasStaticShape(const nvinfer1::Dims& dims) const { @@ -736,18 +1009,7 @@ class OpConverterTest : public ::testing::Test { void AddTestTensor( const char* name, const std::vector& dims, int batch_size = 1, nvinfer1::DataType trt_dtype = nvinfer1::DataType::kFLOAT) { - DataType tf_dtype = DT_FLOAT; - switch (trt_dtype) { - case nvinfer1::DataType::kFLOAT: - tf_dtype = DT_FLOAT; - break; - case nvinfer1::DataType::kINT32: - tf_dtype = DT_INT32; - break; - default: - ASSERT_TRUE(false) << "Unexpected data type " - << static_cast(trt_dtype); - } + DataType tf_dtype = TrtDataTypeToTf(trt_dtype); ops::Placeholder::Attrs attrs; TF_EXPECT_OK(TensorShapeUtils::MakeShape(dims, &attrs.shape_)); attrs.shape_.InsertDim(0, batch_size); @@ -826,6 +1088,11 @@ class OpConverterTest : public ::testing::Test { } } + // Expose quantization_ranges_ for tests + std::unordered_map& quantization_ranges() { + return converter_->quantization_ranges_; + } + std::unique_ptr converter_; std::unique_ptr validator_; @@ -835,6 +1102,11 @@ class OpConverterTest : public ::testing::Test { TrtUniquePtrType network_; TrtUniquePtrType engine_; cudaStream_t stream_; + // Used to create placeholders with shape and data type information. The + // created placeholders will be used as inputs to the node to be verified, + // thus we need the shape and data type information to get a non-empty + // GraphProperties. + // TODO(laigd): consider use this Scope to create the NodeDef to verify. Scope scope_; std::unordered_map validator_inputs_; }; @@ -958,15 +1230,15 @@ TEST_F(OpConverterTest, ConvertTranspose) { Reset(); AddTestTensor("input", {1, 2, 3}); AddTestWeights("weights", {4}, {0, 3, 1, 2}); - RunConversion(node_def); + RunValidationAndConversion(node_def); TRT_TensorOrWeights output; TF_EXPECT_OK(GetTensorOrWeights("my_transpose", &output)); EXPECT_TRUE(output.is_tensor()); - EXPECT_TRUE(TrtDimsEqualsArray({3, 1, 2}, output.tensor()->getDimensions())) - << output.DebugString(); + ExpectTrtDimsEqualsArray({3, 1, 2}, output.tensor()->getDimensions()); std::vector output_data(6); - BuildAndRun("input", {1, 2, 3, 4, 5, 6}, "my_transpose", &output_data); + BuildAndRun({{"input", {1, 2, 3, 4, 5, 6}}}, "my_transpose", + &output_data); EXPECT_THAT(output_data, ElementsAre(1, 4, 2, 5, 3, 6)); } } @@ -1048,15 +1320,15 @@ TEST_F(OpConverterTest, ConvertReshape) { Reset(); AddTestTensor("input", ok_params[i].tensor_dims, ok_params[i].batch_size); AddTestWeights("weights", {4}, ok_params[i].shape); - RunConversion(node_def); + RunValidationAndConversion(node_def); TRT_TensorOrWeights output; TF_EXPECT_OK(GetTensorOrWeights("my_reshape", &output)); EXPECT_TRUE(output.is_tensor()); - EXPECT_TRUE(TrtDimsEqualsArray({1, 3, 2}, output.tensor()->getDimensions())) - << output.DebugString(); + ExpectTrtDimsEqualsArray({1, 3, 2}, output.tensor()->getDimensions()); std::vector output_data(6); - BuildAndRun("input", {1, 2, 3, 4, 5, 6}, "my_reshape", &output_data); + BuildAndRun({{"input", {1, 2, 3, 4, 5, 6}}}, "my_reshape", + &output_data); EXPECT_THAT(output_data, ElementsAre(1, 2, 3, 4, 5, 6)); } } @@ -1070,15 +1342,14 @@ TEST_F(OpConverterTest, ConvertMatMul) { "Input expects tensor and weights, at my_matmul"); } - // Get the NodeDef for Reshape. + // Get the NodeDef for MatMul. auto get_matmul_nodedef = [](DataType dtype, bool transpose_a, bool transpose_b) -> NodeDef { Scope s = Scope::NewRootScope(); auto input = ops::Placeholder(s.WithOpName("input"), dtype); auto weights = ops::Placeholder(s.WithOpName("weights"), dtype); - ops::MatMul::Attrs matmul_attrs; - matmul_attrs.transpose_a_ = transpose_a; - matmul_attrs.transpose_b_ = transpose_b; + const auto matmul_attrs = + ops::MatMul::TransposeA(transpose_a).TransposeB(transpose_b); auto matmul = ops::MatMul(s.WithOpName("my_matmul"), input, weights, matmul_attrs); return matmul.operation.node()->def(); @@ -1094,45 +1365,845 @@ TEST_F(OpConverterTest, ConvertMatMul) { node_def, error::UNIMPLEMENTED, "Data type is not supported, for node my_matmul got int32"); } - { - // transpose_a is set. - for (bool transpose_b : {false, true}) { - Reset(); - NodeDef node_def = - get_matmul_nodedef(DT_FLOAT, /*transpose_a=*/true, transpose_b); - AddTestTensor("input", {2}, /*batch_size=*/1); - AddTestWeights("weights", {2, 2}, {0, 1, 2, 3}); - RunValidationAndConversion( - node_def, error::INVALID_ARGUMENT, - "transpose_a is not supported for TensorRT FullyConnected"); + // transpose_a is set. + for (bool transpose_b : {false, true}) { + Reset(); + NodeDef node_def = + get_matmul_nodedef(DT_FLOAT, /*transpose_a=*/true, transpose_b); + AddTestTensor("input", {2}, /*batch_size=*/1); + AddTestWeights("weights", {2, 2}, {0, 1, 2, 3}); + RunValidationAndConversion( + node_def, error::INVALID_ARGUMENT, + "transpose_a is not supported for TensorRT FullyConnected"); + } + // OK. + for (bool transpose_b : {false, true}) { + Reset(); + NodeDef node_def = + get_matmul_nodedef(DT_FLOAT, /*transpose_a=*/false, transpose_b); + AddTestTensor("input", {2}, /*batch_size=*/1); + AddTestWeights("weights", {2, 2}, {0, 1, 2, 3}); + RunValidationAndConversion(node_def); + TRT_TensorOrWeights output; + TF_EXPECT_OK(GetTensorOrWeights("my_matmul", &output)); + EXPECT_TRUE(output.is_tensor()); + ExpectTrtDimsEqualsArray({2}, output.tensor()->getDimensions()); + + std::vector output_data(2); + BuildAndRun({{"input", {0, 1}}}, "my_matmul", &output_data); + if (transpose_b) { + EXPECT_THAT(output_data, ElementsAre(1, 3)); + } else { + EXPECT_THAT(output_data, ElementsAre(2, 3)); } } - { - // OK. - for (bool transpose_b : {false, true}) { - Reset(); - NodeDef node_def = - get_matmul_nodedef(DT_FLOAT, /*transpose_a=*/false, transpose_b); - AddTestTensor("input", {2}, /*batch_size=*/1); - AddTestWeights("weights", {2, 2}, {0, 1, 2, 3}); - RunConversion(node_def); +} + +template +void TestConvertBiasAdd(OpConverterTest* test) { + // Get the NodeDef for BiasAdd. + auto get_biasadd_nodedef = [](const string& data_format) -> NodeDef { + Scope s = Scope::NewRootScope(); + auto input = ops::Placeholder(s.WithOpName("input"), dtype); + auto weights = ops::Placeholder(s.WithOpName("weights"), dtype); + const auto biasadd_attrs = ops::BiasAdd::DataFormat(data_format); + auto biasadd = + ops::BiasAdd(s.WithOpName("my_biasadd"), input, weights, biasadd_attrs); + return biasadd.operation.node()->def(); + }; + + typedef typename EnumToDataType::Type CType; + for (const string& data_format : {"NHWC", "NCHW"}) { + for (const int trt_input_rank : {1, 2, 3, 4}) { + test->Reset(); + NodeDef node_def = get_biasadd_nodedef(data_format); + + // Add input, dims_array will be like {2, 1, ..., 1, 3} + std::vector dims_array(trt_input_rank, 1); + if (trt_input_rank == 1) { + dims_array[0] = (data_format == "NHWC" ? 3 : 2); + } else { + dims_array[0] = 2; + dims_array[trt_input_rank - 1] = 3; + } + test->AddTestTensor("input", dims_array, /*batch_size=*/1, + TfDataTypeToTrt(dtype)); + + // Add bias weights. + const int channel_size = (data_format == "NHWC" ? 3 : 2); + std::vector bias(channel_size); + for (int i = 0; i < channel_size; ++i) { + bias[i] = CType(i + 1); // bias will be {1, 2, 3, ...} + } + test->AddTestWeights("weights", {channel_size}, bias); + + // Run the conversion. + test->RunValidationAndConversion(node_def); TRT_TensorOrWeights output; - TF_EXPECT_OK(GetTensorOrWeights("my_matmul", &output)); + TF_EXPECT_OK(test->GetTensorOrWeights("my_biasadd", &output)); EXPECT_TRUE(output.is_tensor()); - EXPECT_TRUE(TrtDimsEqualsArray({2}, output.tensor()->getDimensions())) - << output.DebugString(); - - std::vector output_data(2); - BuildAndRun("input", {0, 1}, "my_matmul", &output_data); - if (transpose_b) { - EXPECT_THAT(output_data, ElementsAre(1, 3)); + ExpectTrtDimsEqualsArray(dims_array, output.tensor()->getDimensions()); + + // Build and run the engine. + const int num_input = TrtDimsNumElements(GetTestDims(dims_array)); + ASSERT_EQ(trt_input_rank > 1 ? 6 : (data_format == "NHWC" ? 3 : 2), + num_input); + std::vector output_data(num_input); + test->BuildAndRun( + {{"input", std::vector(num_input, CType(0))}}, "my_biasadd", + &output_data); + if (trt_input_rank == 1) { + if (data_format == "NHWC") { + EXPECT_THAT(output_data, ElementsAre(CType(1), CType(2), CType(3))); + } else { + EXPECT_THAT(output_data, ElementsAre(CType(1), CType(2))); + } } else { - EXPECT_THAT(output_data, ElementsAre(2, 3)); + if (data_format == "NHWC") { + EXPECT_THAT(output_data, ElementsAre(CType(1), CType(2), CType(3), + CType(1), CType(2), CType(3))); + } else { + EXPECT_THAT(output_data, ElementsAre(CType(1), CType(1), CType(1), + CType(2), CType(2), CType(2))); + } } } } } +TEST_F(OpConverterTest, ConvertQuantize) { + { + // Input list is empty, should fail. + NodeDef node_def = + MakeNodeDef("my_quantize", "QuantizeAndDequantizeV2", {}); + RunConversion( + node_def, error::INVALID_ARGUMENT, + "Invalid number of inputs for QuantizeAndDequantizeV2, at my_quantize"); + } + { + // FakeQuantWithMinMaxArgs attributes are empty, should fail. + NodeDef node_def = + MakeNodeDef("my_quantize", "FakeQuantWithMinMaxArgs", {"input"}); + AddTestTensor("input", {1, 2, 3}); + RunConversion(node_def, error::INVALID_ARGUMENT, + "Min or max attribute not found for FakeQuantWithMinMaxArgs " + "at my_quantize"); + } + { + // FakeQuantWithMinMaxArgs ranges set via attributes, ok. + Reset(); + Scope s = Scope::NewRootScope(); + auto input = ops::Placeholder(s.WithOpName("input"), DT_FLOAT); + ops::FakeQuantWithMinMaxArgs::Attrs quantize_attrs; + quantize_attrs.min_ = -6.0f; + quantize_attrs.max_ = 6.0f; + auto quantize = ops::FakeQuantWithMinMaxArgs(s.WithOpName("my_quantize"), + input, quantize_attrs); + const NodeDef& node_def = quantize.operation.node()->def(); + AddTestTensor("input", {1, 2, 3}); + RunConversion(node_def); + TRT_TensorOrWeights output; + TF_EXPECT_OK(GetTensorOrWeights("my_quantize", &output)); + EXPECT_TRUE(output.is_tensor()); + auto ranges = quantization_ranges(); + EXPECT_EQ(ranges.count(output.tensor()), 1); + EXPECT_EQ(ranges[output.tensor()], 6.0f); + } + { + // FakeQuantWithMinMaxVars ranges set via inputs, ok. + Reset(); + Scope s = Scope::NewRootScope(); + auto input = ops::Placeholder(s.WithOpName("input"), DT_FLOAT); + auto weights_min = ops::Placeholder(s.WithOpName("weights_min"), DT_FLOAT); + auto weights_max = ops::Placeholder(s.WithOpName("weights_max"), DT_FLOAT); + auto quantize = ops::FakeQuantWithMinMaxVars( + s.WithOpName("my_quantize"), input, weights_min, weights_max); + const NodeDef& node_def = quantize.operation.node()->def(); + AddTestTensor("input", {1, 2, 3}); + AddTestWeights("weights_min", {1}, {-6.0f}); + AddTestWeights("weights_max", {1}, {6.0f}); + RunConversion(node_def); + TRT_TensorOrWeights output; + TF_EXPECT_OK(GetTensorOrWeights("my_quantize", &output)); + EXPECT_TRUE(output.is_tensor()); + auto ranges = quantization_ranges(); + EXPECT_EQ(ranges.count(output.tensor()), 1); + EXPECT_EQ(ranges[output.tensor()], 6.0f); + } + { + // QuantizeAndDequantizeV2 ranges set via inputs, ok. + Reset(); + Scope s = Scope::NewRootScope(); + auto input = ops::Placeholder(s.WithOpName("input"), DT_FLOAT); + auto weights_min = ops::Placeholder(s.WithOpName("weights_min"), DT_FLOAT); + auto weights_max = ops::Placeholder(s.WithOpName("weights_max"), DT_FLOAT); + auto quantize = ops::QuantizeAndDequantizeV2( + s.WithOpName("my_quantize"), input, weights_min, weights_max); + const NodeDef& node_def = quantize.operation.node()->def(); + AddTestTensor("input", {1, 2, 3}); + AddTestWeights("weights_min", {1}, {-6.0f}); + AddTestWeights("weights_max", {1}, {6.0f}); + RunConversion(node_def); + TRT_TensorOrWeights output; + TF_EXPECT_OK(GetTensorOrWeights("my_quantize", &output)); + EXPECT_TRUE(output.is_tensor()); + auto ranges = quantization_ranges(); + EXPECT_EQ(ranges.count(output.tensor()), 1); + EXPECT_EQ(ranges[output.tensor()], 6.0f); + } + { + // QuantizeAndDequantizeV3 ranges set via inputs, ok. + Reset(); + Scope s = Scope::NewRootScope(); + auto input = ops::Placeholder(s.WithOpName("input"), DT_FLOAT); + auto weights_min = ops::Placeholder(s.WithOpName("weights_min"), DT_FLOAT); + auto weights_max = ops::Placeholder(s.WithOpName("weights_max"), DT_FLOAT); + auto num_bits = ops::Placeholder(s.WithOpName("num_bits"), DT_INT32); + auto quantize = ops::QuantizeAndDequantizeV3( + s.WithOpName("my_quantize"), input, weights_min, weights_max, num_bits); + const NodeDef& node_def = quantize.operation.node()->def(); + AddTestTensor("input", {1, 2, 3}); + AddTestWeights("weights_min", {1}, {-6.0f}); + AddTestWeights("weights_max", {1}, {6.0f}); + AddTestWeights("num_bits", {1}, {8}); + RunConversion(node_def); + TRT_TensorOrWeights output; + TF_EXPECT_OK(GetTensorOrWeights("my_quantize", &output)); + EXPECT_TRUE(output.is_tensor()); + auto ranges = quantization_ranges(); + EXPECT_EQ(ranges.count(output.tensor()), 1); + EXPECT_EQ(ranges[output.tensor()], 6.0f); + } + { + // QuantizeAndDequantizeV2 Range inputs are tensors, should fail. + Reset(); + Scope s = Scope::NewRootScope(); + auto input = ops::Placeholder(s.WithOpName("input"), DT_FLOAT); + auto weights_min = ops::Placeholder(s.WithOpName("weights_min"), DT_FLOAT); + auto weights_max = ops::Placeholder(s.WithOpName("weights_max"), DT_FLOAT); + auto quantize = ops::QuantizeAndDequantizeV2( + s.WithOpName("my_quantize"), input, weights_min, weights_max); + const NodeDef& node_def = quantize.operation.node()->def(); + AddTestTensor("input", {1, 2, 3}); + AddTestTensor("weights_min", {1}); + AddTestTensor("weights_max", {1}); + RunConversion( + node_def, error::INVALID_ARGUMENT, + "Min and max inputs for QuantizeAndDequantizeV2 must be weights not " + "tensors, at my_quantize"); + } +} + +TEST_F(OpConverterTest, ConvertRelu6) { + { + // Input list is empty, should fail. + NodeDef node_def = MakeNodeDef("my_relu6", "Relu6", {}); + RunConversion(node_def, error::INVALID_ARGUMENT, + "Invalid number of inputs for Relu6, at my_relu6"); + } + + // Get the NodeDef for Relu6. + Scope s = Scope::NewRootScope(); + auto input = ops::Placeholder(s.WithOpName("input"), DT_FLOAT); + auto relu6 = ops::Relu6(s.WithOpName("my_relu6"), input); + const NodeDef& node_def = relu6.operation.node()->def(); + + { + // Clip tensor values and set quantization ranges, ok. + Reset(); + AddTestTensor("input", {1, 2, 3}); + RunConversion(node_def); + TRT_TensorOrWeights output; + TF_EXPECT_OK(GetTensorOrWeights("my_relu6", &output)); + EXPECT_TRUE(output.is_tensor()); + auto ranges = quantization_ranges(); + EXPECT_EQ(ranges[output.tensor()], 6.0f); + + std::vector output_data(6); + BuildAndRun("input", {-100, -1, 0, 3, 5, 9}, "my_relu6", &output_data); + EXPECT_THAT(output_data, ElementsAre(0, 0, 0, 3, 5, 6)); + } + { + // Input is weights, should fail. + Reset(); + AddTestWeights("input", {1, 2, 3}, {-100, -1, 0, 3, 5, 9}); + RunConversion( + node_def, error::UNIMPLEMENTED, + "Relu6 is only implemented for tensors, not weights, at my_relu6"); + } +} + +TEST_F(OpConverterTest, ConvertBiasAdd) { + { + // Input list is empty, should fail. + NodeDef node_def = MakeNodeDef("my_biasadd", "BiasAdd", {}); + RunValidationAndConversion( + node_def, error::INVALID_ARGUMENT, + "Input expects tensor and weights, at my_biasadd"); + } + + // OK. Note that kINT32 is not supported by IScaleLayer, so we don't test + // DT_INT32 type here. + TestConvertBiasAdd(this); + TestConvertBiasAdd(this); +} + +template +NodeDef GetBinaryOpNodeDef(const string& input_name_l, + const string& input_name_r, DataType dtype) { + Scope s = Scope::NewRootScope(); + auto input_l = ops::Placeholder(s.WithOpName(input_name_l), dtype); + auto input_r = ops::Placeholder(s.WithOpName(input_name_r), dtype); + auto op = OpType(s.WithOpName("my_binary"), input_l, input_r); + return op.operation.node()->def(); +} + +void CheckAddedLayers(OpConverterTest* test, bool expect_scale_layer) { + bool element_wise_layer_found = false; + bool scale_layer_found = false; + for (int i = 0; i < test->converter_->network()->getNbLayers(); i++) { + nvinfer1::ILayer* layer = test->converter_->network()->getLayer(i); + if (dynamic_cast(layer)) { + scale_layer_found = true; + } else if (dynamic_cast(layer)) { + element_wise_layer_found = true; + } + } + EXPECT_EQ(expect_scale_layer, scale_layer_found); + EXPECT_NE(expect_scale_layer, element_wise_layer_found); +} + +template +void TestBinaryTensorOpWeightNoBroadcast(OpConverterTest* test) { + typedef typename EnumToDataType::Type CType; + for (auto swap_inputs : {false, true}) { + test->Reset(); + NodeDef node_def; + if (swap_inputs) { + node_def = GetBinaryOpNodeDef("weights", "input", dtype); + } else { + node_def = GetBinaryOpNodeDef("input", "weights", dtype); + } + + const std::vector operand1{CType(3), CType(7.5)}; + const std::vector operand2{CType(2), CType(3)}; + + // It requires the dims to be at least of rank 3 to apply an IScaleLayer. + test->AddTestTensor("input", /*dims=*/{1, 1, 2}, /*batch_size=*/1, + TfDataTypeToTrt(dtype)); + test->AddTestWeights("weights", /*dims=*/{1, 1, 2}, + /*values=*/swap_inputs ? operand1 : operand2); + test->RunValidationAndConversion(node_def); + + // Make sure it does use BinaryTensorOpWeight, not BinaryTensorOpTensor. + CheckAddedLayers(test, /*expect_scale_layer=*/true); + + // Check the dims of the output ITensor. + TRT_TensorOrWeights output; + TF_EXPECT_OK(test->GetTensorOrWeights("my_binary", &output)); + EXPECT_TRUE(output.is_tensor()); + ExpectTrtDimsEqualsArray({1, 1, 2}, output.tensor()->getDimensions()); + + std::vector output_data(2); + test->BuildAndRun( + {{"input", + /*input_data=*/swap_inputs ? operand2 : operand1}}, + "my_binary", &output_data); + if (node_def.op() == "Add") { + EXPECT_THAT(output_data, ElementsAre(CType(5), CType(10.5))); + } else if (node_def.op() == "Sub") { + EXPECT_THAT(output_data, ElementsAre(CType(1), CType(4.5))); + } else if (node_def.op() == "Mul") { + EXPECT_THAT(output_data, ElementsAre(CType(6), CType(22.5))); + } else if (node_def.op() == "Div") { + EXPECT_THAT(output_data, ElementsAre(CType(1.5), CType(2.5))); + } else if (node_def.op() == "RealDiv") { + EXPECT_THAT(output_data, ElementsAre(CType(1.5), CType(2.5))); + } else { + ASSERT_TRUE(false); + } + } +} + +template +void TestBinaryTensorOpWeightWithChannelWiseBroadcast(OpConverterTest* test) { + typedef typename EnumToDataType::Type CType; + const NodeDef node_def = + GetBinaryOpNodeDef("input", "weights", dtype); + const std::vector input{CType(1), CType(2), CType(3), CType(4)}; + const std::vector weights{CType(10), CType(20)}; + // There are two types of valid dim pairs which requires channel-wise + // broadcasting: + // - input dims (X Y Z) vs weights dims (X 1 1) + // - input dims (X Y Z) vs weights dims (Z) + // Here X=Z=2 and Y=1. + for (auto weights_dims : std::vector>{{2, 1, 1}, {2}}) { + test->Reset(); + test->AddTestTensor("input", /*dims=*/{2, 1, 2}, /*batch_size=*/1, + TfDataTypeToTrt(dtype)); + test->AddTestWeights("weights", weights_dims, weights); + test->RunValidationAndConversion(node_def); + + // Make sure it does use BinaryTensorOpWeight, not BinaryTensorOpTensor. + CheckAddedLayers(test, /*expect_scale_layer=*/true); + + // Check the dims of the output ITensor. + TRT_TensorOrWeights output; + TF_EXPECT_OK(test->GetTensorOrWeights("my_binary", &output)); + EXPECT_TRUE(output.is_tensor()); + ExpectTrtDimsEqualsArray({2, 1, 2}, output.tensor()->getDimensions()); + + std::vector output_data(4); + test->BuildAndRun({{"input", input}}, "my_binary", &output_data); + if (weights_dims.size() == 1) { + EXPECT_THAT(output_data, + ElementsAre(CType(11), CType(22), CType(13), CType(24))); + } else { + EXPECT_THAT(output_data, + ElementsAre(CType(11), CType(12), CType(23), CType(24))); + } + } +} + +template +void TestBinaryTensorOpWeightWithUniformlyBroadcast(OpConverterTest* test) { + typedef typename EnumToDataType::Type CType; + const NodeDef node_def = + GetBinaryOpNodeDef("input", "weights", dtype); + const std::vector input{CType(1), CType(2), CType(3), CType(4)}; + const std::vector weights{CType(10)}; + test->Reset(); + test->AddTestTensor("input", /*dims=*/{2, 1, 2}, /*batch_size=*/1, + TfDataTypeToTrt(dtype)); + test->AddTestWeights("weights", {1, 1, 1, 1}, weights); + test->RunValidationAndConversion(node_def); + + // Make sure it does use BinaryTensorOpWeight, not BinaryTensorOpTensor. + CheckAddedLayers(test, /*expect_scale_layer=*/true); + + // Check the dims of the output ITensor. + TRT_TensorOrWeights output; + TF_EXPECT_OK(test->GetTensorOrWeights("my_binary", &output)); + EXPECT_TRUE(output.is_tensor()); + ExpectTrtDimsEqualsArray({2, 1, 2}, output.tensor()->getDimensions()); + + std::vector output_data(4); + test->BuildAndRun({{"input", input}}, "my_binary", &output_data); + EXPECT_THAT(output_data, + ElementsAre(CType(11), CType(12), CType(13), CType(14))); +} + +template +void TestBinaryTensorOpWeightFallback(OpConverterTest* test, + const std::vector& input_dims, + const std::vector& weights_dims, + error::Code code = error::OK, + const char* error_msg_substr = nullptr, + const int input_batch_size = 1) { + const DataType dtype = DT_FLOAT; + typedef typename EnumToDataType::Type CType; + const size_t num_inputs = TrtDimsNumElements(GetTestDims(input_dims)); + const size_t num_weights = TrtDimsNumElements(GetTestDims(weights_dims)); + + test->Reset(); + const NodeDef node_def = + GetBinaryOpNodeDef("input", "weights", dtype); + test->AddTestTensor("input", /*dims=*/input_dims, input_batch_size, + TfDataTypeToTrt(dtype)); + test->AddTestWeights( + "weights", /*dims=*/weights_dims, + /*values=*/std::vector(num_weights, CType(1))); + test->RunValidationAndConversion(node_def, code, error_msg_substr); + if (code != error::OK) return; + + // Make sure it does use BinaryTensorOpTensor, not BinaryTensorOpWeight. + CheckAddedLayers(test, /*expect_scale_layer=*/false); + + TRT_TensorOrWeights output; + TF_EXPECT_OK(test->GetTensorOrWeights("my_binary", &output)); + EXPECT_TRUE(output.is_tensor()); + + // Check the dims of the output ITensor. + std::vector expected_output_dims = input_dims; + for (int i = expected_output_dims.size() - 1, j = weights_dims.size() - 1; + i >= 0 && j >= 0; --i, --j) { + if (expected_output_dims[i] == 1) { + expected_output_dims[i] = weights_dims[j]; + } + } + ExpectTrtDimsEqualsArray(expected_output_dims, + output.tensor()->getDimensions()); + + // Check the result of running the engine. + const int expected_num_outputs = + TrtDimsNumElements(GetTestDims(expected_output_dims)); + std::vector output_data(expected_num_outputs); + test->BuildAndRun( + {{"input", + /*input_data=*/std::vector(num_inputs, CType(2))}}, + "my_binary", &output_data); + if (node_def.op() == "Add") { + EXPECT_THAT(output_data, ElementsAreArray(std::vector( + expected_num_outputs, CType(3)))); + } else if (node_def.op() == "Minimum") { + EXPECT_THAT(output_data, ElementsAreArray(std::vector( + expected_num_outputs, CType(1)))); + } else { + ASSERT_TRUE(false); + } +} + +template +void TestBinaryTensorOpTensor(OpConverterTest* test) { + typedef typename EnumToDataType::Type CType; + test->Reset(); + const NodeDef node_def = + GetBinaryOpNodeDef("input1", "input2", dtype); + test->AddTestTensor("input1", /*dims=*/{1, 2}, /*batch_size=*/1, + TfDataTypeToTrt(dtype)); + test->AddTestTensor("input2", /*dims=*/{2, 1}, /*batch_size=*/1, + TfDataTypeToTrt(dtype)); + test->RunValidationAndConversion(node_def); + + // Make sure it does use BinaryTensorOpTensor, not BinaryTensorOpWeight. + CheckAddedLayers(test, /*expect_scale_layer=*/false); + + // Check output dims. + TRT_TensorOrWeights output; + TF_EXPECT_OK(test->GetTensorOrWeights("my_binary", &output)); + EXPECT_TRUE(output.is_tensor()); + ExpectTrtDimsEqualsArray({2, 2}, output.tensor()->getDimensions()); + + std::vector output_data(4); + // After broadcasting first input becomes {3, 6, 3, 6} and second input + // becomes {2, 3, 2, 3}. + test->BuildAndRun( + {{"input1", {CType(3), CType(6)}}, {"input2", {CType(2), CType(3)}}}, + "my_binary", &output_data); + if (node_def.op() == "Add") { + EXPECT_THAT(output_data, + ElementsAre(CType(5), CType(8), CType(6), CType(9))); + } else if (node_def.op() == "Sub") { + EXPECT_THAT(output_data, + ElementsAre(CType(1), CType(4), CType(0), CType(3))); + } else if (node_def.op() == "Mul") { + EXPECT_THAT(output_data, + ElementsAre(CType(6), CType(12), CType(9), CType(18))); + } else if (node_def.op() == "Div") { + EXPECT_THAT(output_data, + ElementsAre(CType(1.5), CType(3), CType(1), CType(2))); + } else if (node_def.op() == "RealDiv") { + EXPECT_THAT(output_data, + ElementsAre(CType(1.5), CType(3), CType(1), CType(2))); + } else if (node_def.op() == "Minimum") { + EXPECT_THAT(output_data, + ElementsAre(CType(2), CType(2), CType(3), CType(3))); + } else if (node_def.op() == "Maximum") { + EXPECT_THAT(output_data, + ElementsAre(CType(3), CType(6), CType(3), CType(6))); + } else { + ASSERT_TRUE(false); + } +} + +TEST_F(OpConverterTest, ConvertBinary) { + // Input size doesn't match, should fail. + for (size_t num_inputs = 0; num_inputs < 2; ++num_inputs) { + Reset(); + NodeDef node_def = MakeNodeDef("my_add", "Add", {num_inputs, "input"}); + AddTestTensor("input", {1}, /*batch_size=*/1, nvinfer1::DataType::kFLOAT); + RunValidationAndConversion(node_def, error::INVALID_ARGUMENT, + "Binary ops require two inputs, at my_add"); + } + { + // Both inputs are weights. + Reset(); + NodeDef node_def = MakeNodeDef("my_add", "Add", {"weights1", "weights2"}); + AddTestWeights("weights1", {1}, {1}); + AddTestWeights("weights2", {1}, {1}); + RunValidationAndConversion( + node_def, error::UNIMPLEMENTED, + "Constant folding is falled back to TensorFlow, binary op received " + "both input as constant at: my_add"); + } + + // Test BinaryTensorOpWeight() without broadcasting. + TestBinaryTensorOpWeightNoBroadcast(this); + TestBinaryTensorOpWeightNoBroadcast(this); + TestBinaryTensorOpWeightNoBroadcast(this); + TestBinaryTensorOpWeightNoBroadcast(this); + TestBinaryTensorOpWeightNoBroadcast(this); +#if 0 + // TODO(b/119560144): it doesn't support FP16 constants and the following test + // will fail. + TestBinaryTensorOpWeightNoBroadcast(this); + TestBinaryTensorOpWeightNoBroadcast(this); + TestBinaryTensorOpWeightNoBroadcast(this); + TestBinaryTensorOpWeightNoBroadcast(this); + TestBinaryTensorOpWeightNoBroadcast(this); +#endif + + // Test BinaryTensorOpWeight() with channel-wise broadcasting. + TestBinaryTensorOpWeightWithChannelWiseBroadcast(this); + + // Test BinaryTensorOpWeight() with uniformly broadcasting. + TestBinaryTensorOpWeightWithUniformlyBroadcast(this); + + // Test BinaryTensorOpWeight() falling back to BinaryTensorOpTensor(). + // Unsupported op. + TestBinaryTensorOpWeightFallback(this, {1, 1, 1}, {1}); + // Rank of input tensor dimension <3. + TestBinaryTensorOpWeightFallback(this, {1, 1}, {1}); + // Broadcast on batch dimension, should fail. + TestBinaryTensorOpWeightFallback( + this, {1, 1, 1}, {2, 1, 1, 1}, error::INVALID_ARGUMENT, + "Unsupported binary op broadcast scheme for op my_binary", + /*input_batch_size=*/2); + // Incompatible dims with per-channel mode. + TestBinaryTensorOpWeightFallback(this, {1, 1, 1}, {1, 2, 1}); + // Incompatible dims. + TestBinaryTensorOpWeightFallback(this, {1, 2, 1}, {2}); + + // Test BinaryTensorOpTensor() with broadcasting. + TestBinaryTensorOpTensor(this); + TestBinaryTensorOpTensor(this); + TestBinaryTensorOpTensor(this); + TestBinaryTensorOpTensor(this); + TestBinaryTensorOpTensor(this); + TestBinaryTensorOpTensor(this); + TestBinaryTensorOpTensor(this); + + TestBinaryTensorOpTensor(this); + TestBinaryTensorOpTensor(this); + TestBinaryTensorOpTensor(this); + TestBinaryTensorOpTensor(this); + TestBinaryTensorOpTensor(this); + TestBinaryTensorOpTensor(this); + TestBinaryTensorOpTensor(this); +} + +TEST_F(OpConverterTest, ConvertQuantize) { + for (const string& op : + {"FakeQuantWithMinMaxArgs", "FakeQuantWithMinMaxVars", + "QuantizeAndDequantizeV2", "QuantizeAndDequantizeV3"}) { + // Input list is empty, should fail. + NodeDef node_def = MakeNodeDef("my_quantize", op, {}); + RunValidationAndConversion( + node_def, error::INVALID_ARGUMENT, + StrCat("Invalid number of inputs for ", op, ", at my_quantize") + .c_str()); + } + { + // FakeQuantWithMinMaxArgs attributes are empty, should fail. + NodeDef node_def = + MakeNodeDef("my_quantize", "FakeQuantWithMinMaxArgs", {"input"}); + AddTestTensor("input", {1, 2, 3}); + RunValidationAndConversion( + node_def, error::INVALID_ARGUMENT, + "Min or max attribute not found for FakeQuantWithMinMaxArgs " + "at my_quantize"); + } + { + // FakeQuantWithMinMaxArgs ranges set via attributes, ok. + Reset(); + Scope s = Scope::NewRootScope(); + auto input = ops::Placeholder(s.WithOpName("input"), DT_FLOAT); + auto quantize_attrs = ops::FakeQuantWithMinMaxArgs::Min(-6.0f).Max(6.0f); + auto quantize = ops::FakeQuantWithMinMaxArgs(s.WithOpName("my_quantize"), + input, quantize_attrs); + const NodeDef& node_def = quantize.operation.node()->def(); + AddTestTensor("input", {1, 2, 3}); + RunValidationAndConversion(node_def); + TRT_TensorOrWeights output; + TF_EXPECT_OK(GetTensorOrWeights("my_quantize", &output)); + EXPECT_TRUE(output.is_tensor()); + auto ranges = quantization_ranges(); + EXPECT_EQ(1, ranges.count(output.tensor())); + EXPECT_EQ(6.0f, ranges[output.tensor()]); + } + { + // FakeQuantWithMinMaxVars ranges set via inputs, ok. + Reset(); + Scope s = Scope::NewRootScope(); + auto input = ops::Placeholder(s.WithOpName("input"), DT_FLOAT); + auto weights_min = ops::Placeholder(s.WithOpName("weights_min"), DT_FLOAT); + auto weights_max = ops::Placeholder(s.WithOpName("weights_max"), DT_FLOAT); + auto quantize = ops::FakeQuantWithMinMaxVars( + s.WithOpName("my_quantize"), input, weights_min, weights_max); + const NodeDef& node_def = quantize.operation.node()->def(); + AddTestTensor("input", {1, 2, 3}); + AddTestWeights("weights_min", {1}, {-6.0f}); + AddTestWeights("weights_max", {1}, {6.0f}); + RunValidationAndConversion(node_def); + TRT_TensorOrWeights output; + TF_EXPECT_OK(GetTensorOrWeights("my_quantize", &output)); + EXPECT_TRUE(output.is_tensor()); + auto ranges = quantization_ranges(); + EXPECT_EQ(1, ranges.count(output.tensor())); + EXPECT_EQ(6.0f, ranges[output.tensor()]); + } + { + // QuantizeAndDequantizeV2 ranges set via inputs, ok. + Reset(); + Scope s = Scope::NewRootScope(); + auto input = ops::Placeholder(s.WithOpName("input"), DT_FLOAT); + auto weights_min = ops::Placeholder(s.WithOpName("weights_min"), DT_FLOAT); + auto weights_max = ops::Placeholder(s.WithOpName("weights_max"), DT_FLOAT); + auto quantize = ops::QuantizeAndDequantizeV2( + s.WithOpName("my_quantize"), input, weights_min, weights_max); + const NodeDef& node_def = quantize.operation.node()->def(); + AddTestTensor("input", {1, 2, 3}); + AddTestWeights("weights_min", {1}, {-6.0f}); + AddTestWeights("weights_max", {1}, {6.0f}); + RunValidationAndConversion(node_def); + TRT_TensorOrWeights output; + TF_EXPECT_OK(GetTensorOrWeights("my_quantize", &output)); + EXPECT_TRUE(output.is_tensor()); + auto ranges = quantization_ranges(); + EXPECT_EQ(1, ranges.count(output.tensor())); + EXPECT_EQ(6.0f, ranges[output.tensor()]); + } + { + // QuantizeAndDequantizeV2 Range inputs are tensors, should fail. + Reset(); + Scope s = Scope::NewRootScope(); + auto input = ops::Placeholder(s.WithOpName("input"), DT_FLOAT); + auto weights_min = ops::Placeholder(s.WithOpName("weights_min"), DT_FLOAT); + auto weights_max = ops::Placeholder(s.WithOpName("weights_max"), DT_FLOAT); + auto quantize = ops::QuantizeAndDequantizeV2( + s.WithOpName("my_quantize"), input, weights_min, weights_max); + const NodeDef& node_def = quantize.operation.node()->def(); + AddTestTensor("input", {1, 2, 3}); + AddTestTensor("weights_min", {1}); + AddTestTensor("weights_max", {1}); + RunValidationAndConversion( + node_def, error::INVALID_ARGUMENT, + "Min and max inputs for QuantizeAndDequantizeV2 must be weights not " + "tensors, at my_quantize"); + } + { + // QuantizeAndDequantizeV3 ranges set via inputs, ok. + Reset(); + Scope s = Scope::NewRootScope(); + auto input = ops::Placeholder(s.WithOpName("input"), DT_FLOAT); + auto weights_min = ops::Placeholder(s.WithOpName("weights_min"), DT_FLOAT); + auto weights_max = ops::Placeholder(s.WithOpName("weights_max"), DT_FLOAT); + auto num_bits = ops::Placeholder(s.WithOpName("num_bits"), DT_INT32); + auto quantize = ops::QuantizeAndDequantizeV3( + s.WithOpName("my_quantize"), input, weights_min, weights_max, num_bits); + const NodeDef& node_def = quantize.operation.node()->def(); + AddTestTensor("input", {1, 2, 3}); + AddTestWeights("weights_min", {1}, {-6.0f}); + AddTestWeights("weights_max", {1}, {6.0f}); + AddTestWeights("num_bits", {1}, {8}); + RunValidationAndConversion(node_def); + TRT_TensorOrWeights output; + TF_EXPECT_OK(GetTensorOrWeights("my_quantize", &output)); + EXPECT_TRUE(output.is_tensor()); + auto ranges = quantization_ranges(); + EXPECT_EQ(1, ranges.count(output.tensor())); + EXPECT_EQ(6.0f, ranges[output.tensor()]); + } +} + +TEST_F(OpConverterTest, ConvertRelu6) { + { + // Input list is empty, should fail. + NodeDef node_def = MakeNodeDef("my_relu6", "Relu6", {}); + RunValidationAndConversion( + node_def, error::INVALID_ARGUMENT, + "Invalid number of inputs for Relu6, at my_relu6"); + } + + // Get the NodeDef for Relu6. + Scope s = Scope::NewRootScope(); + auto input = ops::Placeholder(s.WithOpName("input"), DT_FLOAT); + auto relu6 = ops::Relu6(s.WithOpName("my_relu6"), input); + const NodeDef node_def = relu6.operation.node()->def(); + { + // Input is weights, should fail. + Reset(); + AddTestWeights("input", {1}, {1.0f}); + RunValidationAndConversion( + node_def, error::UNIMPLEMENTED, + "Relu6 is only implemented for tensors, not weights, at my_relu6"); + } + { + // Clip tensor values and set quantization ranges, ok. + Reset(); + AddTestTensor("input", {1, 2, 3}); + RunValidationAndConversion(node_def); + TRT_TensorOrWeights output; + TF_EXPECT_OK(GetTensorOrWeights("my_relu6", &output)); + EXPECT_TRUE(output.is_tensor()); + auto ranges = quantization_ranges(); + EXPECT_EQ(ranges[output.tensor()], 6.0f); + + std::vector output_data(6); + BuildAndRun({{"input", {-100, -1, 0, 3, 5, 9}}}, "my_relu6", + &output_data); + EXPECT_THAT(output_data, ElementsAre(0, 0, 0, 3, 5, 6)); + } +} + +template +void TestConvertSquare(OpConverterTest* test) { + test->Reset(); + typedef typename EnumToDataType::Type CType; + + Scope s = Scope::NewRootScope(); + auto input = ops::Placeholder(s.WithOpName("input"), dtype); + auto square = ops::Square(s.WithOpName("my_square"), input); + NodeDef node_def = square.operation.node()->def(); + + test->AddTestTensor("input", {1, 20}); + test->RunValidationAndConversion(node_def); + TRT_TensorOrWeights output; + TF_EXPECT_OK(test->GetTensorOrWeights("my_square", &output)); + EXPECT_TRUE(output.is_tensor()); + ExpectTrtDimsEqualsArray({1, 20}, output.tensor()->getDimensions()); + + const int num_inputs = 20; + std::vector input_data(num_inputs); + std::vector expected_output_data(num_inputs); + for (int i = 0; i < 20; i++) { + const CType value = CType(i - 9); + input_data[i] = value; + expected_output_data[i] = value * value; + } + std::vector output_data(num_inputs); + test->BuildAndRun({{"input", input_data}}, "my_square", &output_data); + ExpectArrayNear(expected_output_data, output_data); +} + +TEST_F(OpConverterTest, ConvertSquare) { + { + // Input list is empty, should fail. + NodeDef node_def = MakeNodeDef("my_square", "Square", {}); + RunValidationAndConversion(node_def, error::INVALID_ARGUMENT, + "Square expects one input, at my_square"); + } + { + // Input is weights, should fail. + Reset(); + Scope s = Scope::NewRootScope(); + auto input = ops::Placeholder(s.WithOpName("input"), DT_FLOAT); + auto square = ops::Square(s.WithOpName("my_square"), input); + NodeDef node_def = square.operation.node()->def(); + AddTestWeights("input", {1, 2, 3}, {1, 2, 3, 4, -5, 6}); + RunValidationAndConversion( + node_def, error::UNIMPLEMENTED, + "Square is only implemented for tensors, at my_square"); + } + + // OK. Note that kINT32 is not supported by IElementWiseLayer, so we don't + // test DT_INT32 type here. + TestConvertSquare(this); + // TODO(tmorris): Looks like there may be a bug with this layer for FP16 + // inputs. Disabling for now. + // TestConvertSquare(this); +} + } // namespace convert } // namespace tensorrt } // namespace tensorflow diff --git a/tensorflow/contrib/tensorrt/convert/trt_optimization_pass.cc b/tensorflow/contrib/tensorrt/convert/trt_optimization_pass.cc index b30d94b028..4ac7e21d34 100644 --- a/tensorflow/contrib/tensorrt/convert/trt_optimization_pass.cc +++ b/tensorflow/contrib/tensorrt/convert/trt_optimization_pass.cc @@ -67,6 +67,9 @@ tensorflow::Status TRTOptimizationPass::Init( TF_RETURN_IF_ERROR(GetPrecisionMode( Uppercase(params.at("precision_mode").s()), &precision_mode_)); } + if (params.count("use_calibration")) { + use_calibration_ = params.at("use_calibration").b(); + } return tensorflow::Status::OK(); } @@ -222,6 +225,12 @@ tensorflow::Status TRTOptimizationPass::Optimize( TF_RETURN_IF_ERROR(static_graph_properties.InferStatically(true)); tensorflow::tensorrt::convert::ConversionParams cp; + if (use_calibration_ && precision_mode_ != INT8MODE) { + LOG(ERROR) << "Calibration with FP32 or FP16 is not implemented. " + << "Falling back to use_calibration = False."; + use_calibration_ = false; + } + std::vector nodes_to_preserve; for (const auto& n : item.NodesToPreserve()) { auto tokens = str_util::Split(n, ":"); @@ -250,6 +259,7 @@ tensorflow::Status TRTOptimizationPass::Optimize( cp.is_dyn_op = is_dynamic_op_; cp.cached_engine_batches = batches_; cp.max_cached_engines = max_cached_batches_; + cp.use_calibration = use_calibration_; auto status = tensorflow::tensorrt::convert::ConvertAfterShapes(cp); VLOG(1) << "Returning from " << name_; return status; diff --git a/tensorflow/contrib/tensorrt/convert/trt_optimization_pass.h b/tensorflow/contrib/tensorrt/convert/trt_optimization_pass.h index 71b51d1368..3e8dc0978e 100644 --- a/tensorflow/contrib/tensorrt/convert/trt_optimization_pass.h +++ b/tensorflow/contrib/tensorrt/convert/trt_optimization_pass.h @@ -38,7 +38,8 @@ class TRTOptimizationPass : public tensorflow::grappler::CustomGraphOptimizer { maximum_batch_size_(-1), is_dynamic_op_(false), max_cached_batches_(1), - max_workspace_size_bytes_(256LL << 20) { + max_workspace_size_bytes_(256LL << 20), + use_calibration_(true) { VLOG(1) << "Constructing " << name_; } @@ -67,6 +68,7 @@ class TRTOptimizationPass : public tensorflow::grappler::CustomGraphOptimizer { std::vector batches_; int max_cached_batches_; int64_t max_workspace_size_bytes_; + bool use_calibration_; }; } // namespace convert diff --git a/tensorflow/contrib/tensorrt/kernels/trt_engine_op.cc b/tensorflow/contrib/tensorrt/kernels/trt_engine_op.cc index 019446813a..117039683c 100644 --- a/tensorflow/contrib/tensorrt/kernels/trt_engine_op.cc +++ b/tensorflow/contrib/tensorrt/kernels/trt_engine_op.cc @@ -124,8 +124,10 @@ TRTEngineOp::TRTEngineOp(OpKernelConstruction* context) OP_REQUIRES_OK(context, context->GetAttr("segment_funcdef_name", &funcdef_name_)); OP_REQUIRES_OK(context, GetPrecisionMode(precision_string, &precision_mode_)); - calibration_mode_ = - (precision_mode_ == INT8MODE && calibration_data.size() == 0); + OP_REQUIRES_OK(context, + context->GetAttr("use_calibration", &use_calibration_)); + calibration_mode_ = (use_calibration_ && precision_mode_ == INT8MODE && + calibration_data.size() == 0); if (calibration_data.size()) { calibrator_.reset(new TRTInt8Calibrator(calibration_data)); calibration_data.resize(0); @@ -252,9 +254,8 @@ int TRTEngineOp::GetEngineBatch(OpKernelContext* ctx) { cached_engine_batches_.push_back(num_batch); VLOG(1) << "Running with batch size " << num_batch; } else { - string msg = - StrCat("Engine buffer is full. buffer limit=", max_cached_engines_, - ", current entries="); + string msg = StrCat("Engine buffer is full. buffer limit=", + max_cached_engines_, ", current entries="); for (auto i : cached_engine_batches_) StrAppend(&msg, i, ","); StrAppend(&msg, " requested batch=", num_batch); LOG(WARNING) << msg; @@ -308,7 +309,7 @@ bool TRTEngineOp::ExecuteTrtEngine( std::vector buffers(num_binding); for (int i = 0; i < ctx->num_inputs(); i++) { const string input_name = StrCat(kInputPHName, i); - const size_t binding_index = + const int binding_index = trt_engine_ptr->getBindingIndex(input_name.c_str()); if (binding_index == -1) { LOG(ERROR) << "Input node not found, at " << input_name; @@ -345,7 +346,7 @@ bool TRTEngineOp::ExecuteTrtEngine( for (int i = 0; i < ctx->num_outputs(); i++) { // Create an output tensor const string output_name = StrCat(kOutputPHName, i); - const size_t binding_index = + const int binding_index = trt_engine_ptr->getBindingIndex(output_name.c_str()); Tensor* output_tensor = nullptr; @@ -491,13 +492,14 @@ TRTEngineOp::EngineCtxPair& TRTEngineOp::GetEngine(int batch_size, } TrtUniquePtrType engine; bool convert_successfully = false; - VLOG(0) << name() << " Constructing a new engine with batch size " - << batch_size; + LOG(INFO) << "Building a new TensorRT engine for " << name() + << " with batch size " << batch_size; // Up to this point, calibrator_ can never be empty, since otherwise it // means calibration_mode_ is true and this path won't get executed. auto status = convert::ConvertGraphDefToEngine( segment_graph_, precision_mode_, batch_size, workspace_size_, shapes, - &logger, allocator, calibrator_.get(), &engine, &convert_successfully); + &logger, allocator, calibrator_.get(), &engine, use_calibration_, + &convert_successfully); if (!status.ok()) { if (convert_successfully) { // This means it fail to build the engine even when the network is built @@ -567,8 +569,8 @@ tensorflow::Status TRTEngineOp::AllocateCalibrationResources( const int64 workspace_size_bytes = workspace_size_; cres->thr_.reset(new std::thread([cres, label, segment_graph, shapes, platform_gpu_id, workspace_size_bytes]() { - VLOG(0) << "Starting calibration thread on device " << platform_gpu_id - << ", Calibration Resource @ " << cres; + LOG(INFO) << "Starting calibration thread on device " << platform_gpu_id + << ", Calibration Resource @ " << cres; auto err = cudaSetDevice(platform_gpu_id); if (err != cudaSuccess) { // TODO(aaroey): should return error here. @@ -586,6 +588,7 @@ tensorflow::Status TRTEngineOp::AllocateCalibrationResources( *segment_graph, INT8MODE, cres->calibrator_->getBatchSize(), workspace_size_bytes, shapes, &cres->logger_, cres->allocator_.get(), cres->calibrator_.get(), &cres->engine_, + /*use_calibration=*/true, /*convert_successfully=*/nullptr); if (!s.ok()) { LOG(ERROR) << "Calibration failed: " << s; diff --git a/tensorflow/contrib/tensorrt/kernels/trt_engine_op.h b/tensorflow/contrib/tensorrt/kernels/trt_engine_op.h index 8fe0675891..b545f497f3 100644 --- a/tensorflow/contrib/tensorrt/kernels/trt_engine_op.h +++ b/tensorflow/contrib/tensorrt/kernels/trt_engine_op.h @@ -130,6 +130,10 @@ class TRTEngineOp : public AsyncOpKernel { // The finalized calibrator for inference. std::unique_ptr calibrator_; + + // If true, create calibration graph for INT8 mode. Otherwise, we are using + // user-provided quantization ranges. + bool use_calibration_; }; } // namespace tensorrt diff --git a/tensorflow/contrib/tensorrt/ops/trt_engine_op.cc b/tensorflow/contrib/tensorrt/ops/trt_engine_op.cc index e0c7b62723..92405906eb 100644 --- a/tensorflow/contrib/tensorrt/ops/trt_engine_op.cc +++ b/tensorflow/contrib/tensorrt/ops/trt_engine_op.cc @@ -16,6 +16,7 @@ limitations under the License. #if GOOGLE_CUDA #if GOOGLE_TENSORRT +#include "tensorflow/core/framework/common_shape_fns.h" #include "tensorflow/core/framework/op.h" #include "tensorflow/core/framework/op_kernel.h" #include "tensorflow/core/framework/shape_inference.h" @@ -39,18 +40,19 @@ REGISTER_OP("TRTEngineOp") .Attr("cached_engine_batches: list(int) = []") .Attr("max_cached_engines_count: int = 1") .Attr("workspace_size_bytes: int") - .Attr("precision_mode: {'FP32', 'FP16', 'INT8', 'INT8CALIB'}") + .Attr("precision_mode: {'FP32', 'FP16', 'INT8'}") .Attr("calibration_data: string = ''") + .Attr("use_calibration: bool = true") .Input("in_tensor: InT") - .Output("out_tensor: OutT"); -// TODO(jie): TF requires concrete output shape for concrete input shapes. -// This is tricky for batch dimension, since we cannot ensure which input -// would carry the correct batch dimension (for the current stage of the -// implementation, we do require all input tensor to carry the same batch -// size, but this could change in the future). Hence we disable shape -// inference function as a workaround. -// .SetShapeFn(shape_inference::TRTEngineOpShapeInference); - + .Output("out_tensor: OutT") + // TODO(jie): TF requires concrete output shape for concrete input shapes. + // This is tricky for batch dimension, since we cannot ensure which input + // would carry the correct batch dimension (for the current stage of the + // implementation, we do require all input tensor to carry the same batch + // size, but this could change in the future). Hence we disable shape + // inference function as a workaround. + // .SetShapeFn(shape_inference::TRTEngineOpShapeInference); + .SetShapeFn(shape_inference::UnknownShape); } // namespace tensorflow #endif // GOOGLE_TENSORRT diff --git a/tensorflow/contrib/tensorrt/python/trt_convert.py b/tensorflow/contrib/tensorrt/python/trt_convert.py index bb81fbf93f..74a2c2392a 100644 --- a/tensorflow/contrib/tensorrt/python/trt_convert.py +++ b/tensorflow/contrib/tensorrt/python/trt_convert.py @@ -63,19 +63,20 @@ class TrtPrecisionMode(object): return [TrtPrecisionMode.FP32, TrtPrecisionMode.FP16, TrtPrecisionMode.INT8] -def tensorrt_rewriter_config(rewriter_config=None, - max_batch_size=1, - max_workspace_size_bytes=2 << 20, - precision_mode=TrtPrecisionMode.FP32, - minimum_segment_size=3, - is_dynamic_op=False, - maximum_cached_engines=1, - cached_engine_batch_sizes=None): +def get_tensorrt_rewriter_config(rewriter_config=None, + max_batch_size=1, + max_workspace_size_bytes=2 << 20, + precision_mode=TrtPrecisionMode.FP32, + minimum_segment_size=3, + is_dynamic_op=False, + maximum_cached_engines=1, + cached_engine_batch_sizes=None, + use_calibration=True): """Returns a RewriterConfig proto for TRT transformation. Args: - rewriter_config: a RewriterConfig proto to append the TensorRTOptimizer to. - If None, it will create one with default settings. + rewriter_config: a template RewriterConfig proto used to create a + TRT-enabled RewriterConfig. If None, it will use a default one. max_batch_size: max size for the input batch max_workspace_size_bytes: the maximum GPU temporary memory which the TRT engine can use at execution time. This corresponds to the 'workspaceSize' @@ -95,6 +96,15 @@ def tensorrt_rewriter_config(rewriter_config=None, use this list to determine the batch sizes of the cached engines, instead of making the decision on the fly. This is useful when we know the most common batch size(s) the application is going to generate. + use_calibration: this argument is ignored if precision_mode is not INT8. if + set to True, a calibration graph will be created to calibrate the missing + ranges. The calibration graph must be converted to an inference graph + using calib_graph_to_infer_graph() after running calibration. if set to + False, quantization nodes will be expected for every tensor in the graph + (exlcuding those which will be fused). If a range is missing, an error + will occur. Please note that accuracy may be negatively affected if there + is a mismatch between which tensors TRT quantizes and which tensors were + trained with fake quantization. Returns: A RewriterConfig proto which sets a TensorRTOptimizer to run Grappler. @@ -107,13 +117,16 @@ def tensorrt_rewriter_config(rewriter_config=None, rewriter_config, rewriter_config_pb2.RewriterConfig): raise TypeError("rewriter_config should be a RewriterConfig proto.") + rewriter_config_with_trt = rewriter_config_pb2.RewriterConfig() if rewriter_config is None: - rewriter_config = rewriter_config_pb2.RewriterConfig() # Layout optimizer may add Const nodes followed by Reshape nodes, thus we # need to run constant folding again. - rewriter_config.optimizers.extend(["constfold", "layout", "constfold"]) - rewriter_config.meta_optimizer_iterations = ( + rewriter_config_with_trt.optimizers.extend( + ["constfold", "layout", "constfold"]) + rewriter_config_with_trt.meta_optimizer_iterations = ( rewriter_config_pb2.RewriterConfig.ONE) + else: + rewriter_config_with_trt.CopyFrom(rewriter_config) if precision_mode.upper() not in TrtPrecisionMode.supported_precision_modes(): raise ValueError(("precision mode '{}' is not supported." @@ -121,7 +134,7 @@ def tensorrt_rewriter_config(rewriter_config=None, precision_mode, TrtPrecisionMode.supported_precision_modes)) - optimizer = rewriter_config.custom_optimizers.add() + optimizer = rewriter_config_with_trt.custom_optimizers.add() optimizer.name = "TensorRTOptimizer" optimizer.parameter_map["minimum_segment_size"].i = minimum_segment_size optimizer.parameter_map["max_batch_size"].i = max_batch_size @@ -138,7 +151,8 @@ def tensorrt_rewriter_config(rewriter_config=None, "maximum_cached_engines items.") optimizer.parameter_map["cached_engine_batches"].list.i.extend( cached_engine_batch_sizes) - return rewriter_config + optimizer.parameter_map["use_calibration"].b = use_calibration + return rewriter_config_with_trt def create_inference_graph(input_graph_def, @@ -150,7 +164,7 @@ def create_inference_graph(input_graph_def, is_dynamic_op=False, maximum_cached_engines=1, cached_engine_batch_sizes=None, - rewriter_config=None, + use_calibration=True, input_saved_model_dir=None, input_saved_model_tags=None, output_saved_model_dir=None, @@ -182,8 +196,15 @@ def create_inference_graph(input_graph_def, use this list to determine the batch sizes of the cached engines, instead of making the decision on the fly. This is useful when we know the most common batch size(s) the application is going to generate. - rewriter_config: a RewriterConfig proto to append the TensorRTOptimizer to. - If None, it will create one with default settings. + use_calibration: this argument is ignored if precision_mode is not INT8. if + set to True, a calibration graph will be created to calibrate the missing + ranges. The calibration graph must be converted to an inference graph + using calib_graph_to_infer_graph() after running calibration. if set to + False, quantization nodes will be expected for every tensor in the graph + (exlcuding those which will be fused). If a range is missing, an error + will occur. Please note that accuracy may be negatively affected if there + is a mismatch between which tensors TRT quantizes and which tensors were + trained with fake quantization. input_saved_model_dir: the directory to load the SavedModel which contains the input graph to transforms. Used only when input_graph_def is None. input_saved_model_tags: list of tags to load the SavedModel. @@ -191,8 +212,9 @@ def create_inference_graph(input_graph_def, returned GraphDef and save it to the specified directory. This option only works when the input graph is loaded from a SavedModel, i.e. when input_saved_model_dir is specified and input_graph_def is None. - session_config: the ConfigProto used to create a Session. If not specified, - a default ConfigProto will be used. + session_config: the ConfigProto used to create a Session. It's also used as + a template to create a TRT-enabled ConfigProto for conversion. If not + specified, a default ConfigProto will be used. Returns: A GraphDef transformed from input_graph_def (or the SavedModel graph def @@ -322,21 +344,30 @@ def create_inference_graph(input_graph_def, grappler_meta_graph_def.collection_def["train_op"].CopyFrom( output_collection) - # Create RewriterConfig. - rewriter_config = tensorrt_rewriter_config( + # Create TRT-enabled ConfigProto. + session_config_with_trt = config_pb2.ConfigProto() + session_config_with_trt.CopyFrom(session_config) + rewriter_config = None + if (session_config_with_trt.HasField("graph_options") and + session_config_with_trt.graph_options.HasField("rewrite_options")): + rewriter_config = session_config_with_trt.graph_options.rewrite_options + rewriter_config_with_trt = get_tensorrt_rewriter_config( rewriter_config, max_batch_size, max_workspace_size_bytes, precision_mode, minimum_segment_size, is_dynamic_op, maximum_cached_engines, - cached_engine_batch_sizes) + cached_engine_batch_sizes, use_calibration) + session_config_with_trt.graph_options.rewrite_options.CopyFrom( + rewriter_config_with_trt) # Run Grappler. transformed_graph_def = tf_optimizer.OptimizeGraph( - rewriter_config, grappler_meta_graph_def, graph_id=b"tf_graph") + session_config_with_trt, grappler_meta_graph_def, graph_id=b"tf_graph") # Optionally write the transformed graphdef as SavedModel. if output_saved_model_dir is not None: saved_model_builder = builder.SavedModelBuilder(output_saved_model_dir) with ops.Graph().as_default(): importer.import_graph_def(transformed_graph_def, name="") + # We don't use TRT here. with session.Session(config=session_config) as sess: saved_model_builder.add_meta_graph_and_variables( sess, diff --git a/tensorflow/contrib/tensorrt/python/trt_convert_test.py b/tensorflow/contrib/tensorrt/python/trt_convert_test.py index 9f2eeac990..dbf8dd2614 100644 --- a/tensorflow/contrib/tensorrt/python/trt_convert_test.py +++ b/tensorflow/contrib/tensorrt/python/trt_convert_test.py @@ -47,9 +47,9 @@ from tensorflow.python.tools import saved_model_utils class TrtConvertTest(test_util.TensorFlowTestCase): """Class to test Tensorflow-TensorRT integration python API.""" - def testTensorrtRewriterConfig(self): - """Test case for trt_convert.tensorrt_rewriter_config().""" - rewriter_cfg = trt_convert.tensorrt_rewriter_config( + def testGetTensorrtRewriterConfig(self): + """Test case for trt_convert.get_tensorrt_rewriter_config().""" + rewriter_cfg = trt_convert.get_tensorrt_rewriter_config( rewriter_config=None, max_batch_size=128, max_workspace_size_bytes=1234, @@ -162,7 +162,7 @@ class TrtConvertTest(test_util.TensorFlowTestCase): node_name_to_op = {node.name: node.op for node in graph_def.node} self.assertEqual({ "input": "Placeholder", - "my_trt_op_0": "TRTEngineOp", + "TRTEngineOp_0": "TRTEngineOp", "output": "Identity" }, node_name_to_op) @@ -189,10 +189,11 @@ class TrtConvertTest(test_util.TensorFlowTestCase): execute_engine_test_value = ("done" if expect_engine_is_run else "") execute_native_segment_test_value = ("" if expect_engine_is_run else "done") self.assertEqual(execute_engine_test_value, - trt_convert.get_test_value("my_trt_op_0:ExecuteTrtEngine")) + trt_convert.get_test_value( + "TRTEngineOp_0:ExecuteTrtEngine")) self.assertEqual( execute_native_segment_test_value, - trt_convert.get_test_value("my_trt_op_0:ExecuteNativeSegment")) + trt_convert.get_test_value("TRTEngineOp_0:ExecuteNativeSegment")) def testCreateInferenceGraph_MinimumSegmentSize(self): if not trt_convert.is_tensorrt_enabled(): diff --git a/tensorflow/contrib/tensorrt/resources/trt_resources.h b/tensorflow/contrib/tensorrt/resources/trt_resources.h index 840da6e78d..aac9e5c7bd 100644 --- a/tensorflow/contrib/tensorrt/resources/trt_resources.h +++ b/tensorflow/contrib/tensorrt/resources/trt_resources.h @@ -39,7 +39,8 @@ namespace tensorrt { class TRTCalibrationResource : public tensorflow::ResourceBase { public: ~TRTCalibrationResource() { - VLOG(0) << "Destroying Calibration Resource " << std::endl << DebugString(); + LOG(INFO) << "Destroying Calibration Resource " << std::endl + << DebugString(); builder_.reset(); engine_.reset(); // We need to manually destroy the builder and engine before the allocator diff --git a/tensorflow/contrib/tensorrt/segment/segment.cc b/tensorflow/contrib/tensorrt/segment/segment.cc index 4f64b7a952..d8f63779e6 100644 --- a/tensorflow/contrib/tensorrt/segment/segment.cc +++ b/tensorflow/contrib/tensorrt/segment/segment.cc @@ -33,6 +33,7 @@ namespace tensorflow { namespace tensorrt { namespace segment { using ::tensorflow::strings::StrAppend; +using ::tensorflow::strings::StrCat; // A simple graph representation to mirror tensorflow::Graph. This structure // helps saving memory since segmenter modifies the graph in place, preventing @@ -406,22 +407,42 @@ tensorflow::Status SegmentGraph( // Use a union-find to collect the nodes that belong to the same // segment. A node value of nullptr indicates that the node is not a candidate // for TRT. + std::unordered_set unsupported_ops; + int num_unsupported_ops = 0; std::vector> node_segments; for (int i = 0; i < graph->num_node_ids(); ++i) { SimpleNode* node = graph->FindNodeId(i); if (options.exclude_node_list.count(node->name()) != 0) { - VLOG(1) << "Not a TF-TRT candidate: " << node->name() - << " (excluded by segmenter option)."; + VLOG(1) << "Not a TF-TRT candidate, " + << "(Op type: " << node->tf_node()->type_string() << "), " + << "(Op name: " << node->name() << "), " + << "(Reason: excluded by segmenter option)"; + unsupported_ops.emplace(node->tf_node()->type_string()); + num_unsupported_ops++; node = nullptr; } else { const Status status = candidate_fn(node->tf_node()); if (!status.ok()) { - VLOG(1) << "Not a TF-TRT candidate: " << node->name() << ": " << status; + VLOG(1) << "Not a TF-TRT candidate, " + << "(Op type: " << node->tf_node()->type_string() << "), " + << "(Op name: " << node->name() << "), " + << "(Reason: " << status << ")"; + unsupported_ops.emplace(node->tf_node()->type_string()); + num_unsupported_ops++; node = nullptr; } } node_segments.emplace_back(node); } + string msg = StrCat( + "There are ", num_unsupported_ops, " ops of ", unsupported_ops.size(), + " different types in the graph that", " are not converted to TensorRT: "); + for (const auto& elem : unsupported_ops) { + StrAppend(&msg, elem, ", "); + } + LOG(INFO) << msg << "(For more information see " + << "https://docs.nvidia.com/deeplearning" + << "/dgx/integrate-tf-trt/index.html#support-ops)."; // The segmentation algorithm below visits nodes in reverse topological order // and attempts to merge nodes along output edges. That means that subgraphs @@ -439,7 +460,8 @@ tensorflow::Status SegmentGraph( std::vector order; order.reserve(graph->num_node_ids()); StableDFS(*graph, /*reverse=*/false, {graph->source_node()}, - /*enter=*/nullptr, [&order](const SimpleNode* n) { + /*enter=*/nullptr, + [&order](const SimpleNode* n) { order.push_back(n); return true; }); @@ -548,7 +570,7 @@ tensorflow::Status SegmentGraph( std::set& segment_nodes = itr.second; VLOG(1) << "Segment original size: " << segment_nodes.size(); while (true) { - std::deque in_nodes_que, out_nodes_que; + std::deque in_nodes_que, out_nodes_que; // Find an input node that is not eligible and add it to the queue. // Nodes that has no incoming edges should not be treated as "input", // as there are really no inputs to them. Similar for output nodes. @@ -594,8 +616,7 @@ tensorflow::Status SegmentGraph( // their outputs. In this way, for common cases the number of removed // nodes should be minimum. auto remove_nodes = [&segment_nodes]( - bool is_input_nodes, - std::deque* que) { + bool is_input_nodes, std::deque* que) { // Run a BFS on the queue to find all the input/output nodes. std::set visited; std::set logged(que->begin(), que->end()); diff --git a/tensorflow/contrib/tensorrt/test/base_test.py b/tensorflow/contrib/tensorrt/test/base_test.py index 18096e0ff1..03faf1df24 100644 --- a/tensorflow/contrib/tensorrt/test/base_test.py +++ b/tensorflow/contrib/tensorrt/test/base_test.py @@ -56,8 +56,9 @@ class SimpleSingleEngineTest(trt_test.TfTrtIntegrationTestBase): strides=[1, 2, 2, 1], padding="SAME", name="conv") - bias = constant_op.constant( - [4., 1.5, 2., 3., 5., 7.], name="bias", dtype=dtype) + bias = constant_op.constant([4., 1.5, 2., 3., 5., 7.], + name="bias", + dtype=dtype) added = nn.bias_add(conv, bias, name="bias_add") relu = nn.relu(added, "relu") identity = array_ops.identity(relu, "identity") @@ -73,11 +74,12 @@ class SimpleSingleEngineTest(trt_test.TfTrtIntegrationTestBase): def ExpectedEnginesToBuild(self, run_params): """Return the expected engines to build.""" - # TODO(aaroey): LayoutOptimizer adds additional nodes to the graph which - # breaks the connection check, fix it. - # - my_trt_op_0 should have ["weights", "conv", "bias", "bias_add", - # "relu", "identity", "max_pool"] - return ["my_trt_op_0"] + return { + "my_trt_op_0": [ + "weights", "conv", "bias", "bias_add", "relu", "identity", + "max_pool" + ] + } class SimpleMultiEnginesTest(trt_test.TfTrtIntegrationTestBase): @@ -92,7 +94,7 @@ class SimpleMultiEnginesTest(trt_test.TfTrtIntegrationTestBase): g = ops.Graph() with g.as_default(): inp = array_ops.placeholder( - dtype=dtype, shape=[None] + input_dims[1:], name=input_name) + dtype=dtype, shape=input_dims, name=input_name) with g.device("/GPU:0"): conv_filter = constant_op.constant( [[[[1., 0.5, 4., 6., 0.5, 1.], [1., 0.5, 1., 1., 0.5, 1.]]]], @@ -105,10 +107,10 @@ class SimpleMultiEnginesTest(trt_test.TfTrtIntegrationTestBase): padding="SAME", name="conv") c1 = constant_op.constant( - np.random.randn(input_dims[0], 12, 12, 6), dtype=dtype, name="c1") + np.random.randn(12, 12, 6), dtype=dtype, name="c1") p = math_ops.mul(conv, c1, name="mul") c2 = constant_op.constant( - np.random.randn(input_dims[0], 12, 12, 6), dtype=dtype, name="c2") + np.random.randn(12, 12, 6), dtype=dtype, name="c2") q = math_ops.div(conv, c2, name="div") edge = self.trt_incompatible_op(q, name="incompatible") @@ -129,22 +131,21 @@ class SimpleMultiEnginesTest(trt_test.TfTrtIntegrationTestBase): def ExpectedEnginesToBuild(self, run_params): """Return the expected engines to build.""" - # TODO(aaroey): LayoutOptimizer adds additional nodes to the graph which - # breaks the connection check, fix it. - # - my_trt_op_0 should have ["mul", "sub", "div1", "mul1", "add1", - # "add", "sub1"]; - # - my_trt_op_1 should have ["weights","conv", "div"] - return ["my_trt_op_0", "my_trt_op_1"] + return { + "my_trt_op_0": [ + "add", "add1", "c1", "div1", "mul", "mul1", "sub", "sub1" + ], + "my_trt_op_1": ["c2", "conv", "div", "weights"] + } - def ShouldRunTest(self, run_params): - # TODO(aaroey): LayoutOptimizer adds Transpose(Const, Const) to the graph - # which breaks the conversion. We should fix it as: - # - Detect the invalid NodeDef earlier before adding them to segment - # - Let it able to change the RewriterConfig when calling - # create_inference_graph(). - # It will be good to add debugging feature for Grappler to print the graph - # after running each optimizer. - return False + def GetConversionParams(self, run_params): + """Return a ConversionParams for test.""" + return super( + SimpleMultiEnginesTest, self + ).GetConversionParams(run_params)._replace( + # Disable layout optimizer, since it'll add Transpose(Const, Const) to + # the graph and breaks the conversion check. + rewriter_config=trt_test.OptimizerDisabledRewriterConfig()) class PartiallyConvertedTestA(trt_test.TfTrtIntegrationTestBase): @@ -153,7 +154,7 @@ class PartiallyConvertedTestA(trt_test.TfTrtIntegrationTestBase): """Setup method.""" super(PartiallyConvertedTestA, self).setUp() # Let it fail to build the second engine. - trt_convert.add_test_value("my_trt_op_1:CreateTRTNode", "fail") + trt_convert.add_test_value("TRTEngineOp_1:CreateTRTNode", "fail") def GetParams(self): """Create a graph containing two segment.""" @@ -190,14 +191,16 @@ class PartiallyConvertedTestA(trt_test.TfTrtIntegrationTestBase): """Return the expected engines to build.""" return { # Only the first engine is built. - "my_trt_op_0": ["c0", "c1", "add0", "add1", "mul0", "mul1"] + "TRTEngineOp_0": ["c0", "c1", "add0", "add1", "mul0", "mul1"] } def ShouldRunTest(self, run_params): """Whether to run the test.""" # Disable the test in fp16 mode since multiple matmul and add ops together # can cause overflow. - return run_params.precision_mode != "FP16" + return ((run_params.precision_mode != "FP16") and + not (trt_test.IsQuantizationMode(run_params.precision_mode) and + not run_params.use_calibration)) class PartiallyConvertedTestB(PartiallyConvertedTestA): @@ -207,13 +210,13 @@ class PartiallyConvertedTestB(PartiallyConvertedTestA): super(PartiallyConvertedTestB, self).setUp() # Let it fail to build the first engine. trt_convert.clear_test_values("") - trt_convert.add_test_value("my_trt_op_0:CreateTRTNode", "fail") + trt_convert.add_test_value("TRTEngineOp_0:CreateTRTNode", "fail") def ExpectedEnginesToBuild(self, run_params): """Return the expected engines to build.""" return { # Only the second engine is built. - "my_trt_op_1": ["c2", "c3", "add2", "add3", "mul2", "mul3"] + "TRTEngineOp_1": ["c2", "c3", "add2", "add3", "mul2", "mul3"] } @@ -257,8 +260,8 @@ class ConstInputTest(trt_test.TfTrtIntegrationTestBase): def ExpectedEnginesToBuild(self, run_params): """Return the expected engines to build.""" return { - "my_trt_op_0": ["add", "add1", "mul"], - "my_trt_op_1": ["add2", "add3", "mul1"] + "TRTEngineOp_0": ["add", "add1", "mul"], + "TRTEngineOp_1": ["add2", "add3", "mul1"] } @@ -289,7 +292,7 @@ class ConstDataInputSingleEngineTest(trt_test.TfTrtIntegrationTestBase): def ExpectedEnginesToBuild(self, run_params): """Return the expected engines to build.""" - return {"my_trt_op_0": ["c", "add", "add1", "mul"]} + return {"TRTEngineOp_0": ["c", "add", "add1", "mul"]} class ConstDataInputMultipleEnginesTest(trt_test.TfTrtIntegrationTestBase): @@ -324,12 +327,12 @@ class ConstDataInputMultipleEnginesTest(trt_test.TfTrtIntegrationTestBase): def ExpectedEnginesToBuild(self, run_params): """Return the expected engines to build.""" return { - "my_trt_op_0": ["add2", "add3", "mul1"], + "TRTEngineOp_0": ["add2", "add3", "mul1"], # Why segment ["add", "add1", "mul"] was assigned segment id 1 # instead of 0: the parent node of this segment is actually const # node 'c', but it's removed later since it's const output of the # segment which is not allowed. - "my_trt_op_1": ["add", "add1", "mul"] + "TRTEngineOp_1": ["add", "add1", "mul"] } @@ -373,8 +376,8 @@ class ControlDependencyTest(trt_test.TfTrtIntegrationTestBase): def ExpectedEnginesToBuild(self, run_params): """Return the expected engines to build.""" return { - "my_trt_op_0": ["c1", "add", "add1", "mul"], - "my_trt_op_1": ["c2", "add2", "add3", "mul1"] + "TRTEngineOp_0": ["c1", "add", "add1", "mul"], + "TRTEngineOp_1": ["c2", "add2", "add3", "mul1"] } diff --git a/tensorflow/contrib/tensorrt/test/batch_matmul_test.py b/tensorflow/contrib/tensorrt/test/batch_matmul_test.py index 4b88808178..f42308ecb7 100644 --- a/tensorflow/contrib/tensorrt/test/batch_matmul_test.py +++ b/tensorflow/contrib/tensorrt/test/batch_matmul_test.py @@ -79,12 +79,12 @@ class BatchMatMulTest(trt_test.TfTrtIntegrationTestBase): """Return the expected engines to build.""" if (run_params.dynamic_engine and not trt_test.IsQuantizationMode(run_params.precision_mode)): - return ["my_trt_op_0", "my_trt_op_1"] - return ["my_trt_op_1"] + return ["TRTEngineOp_0", "TRTEngineOp_1"] + return ["TRTEngineOp_1"] def ExpectedEnginesToRun(self, run_params): """Return the expected engines to run.""" - return ["my_trt_op_1"] + return ["TRTEngineOp_1"] def ShouldRunTest(self, run_params): """Whether to run the test.""" diff --git a/tensorflow/contrib/tensorrt/test/biasadd_matmul_test.py b/tensorflow/contrib/tensorrt/test/biasadd_matmul_test.py index 7545bb9df2..053b38ff1c 100644 --- a/tensorflow/contrib/tensorrt/test/biasadd_matmul_test.py +++ b/tensorflow/contrib/tensorrt/test/biasadd_matmul_test.py @@ -41,6 +41,7 @@ class BiasaddMatMulTest(trt_test.TfTrtIntegrationTestBase): input_name = "input" input_matrix_rows = 4 input_matrix_columns = 144 + # Note that tf.nn.bias_add supports up to 5 dimensions. input_dims = [input_matrix_rows, input_matrix_columns] output_name = "output" g = ops.Graph() @@ -74,18 +75,18 @@ class BiasaddMatMulTest(trt_test.TfTrtIntegrationTestBase): x5 = nn.bias_add(x5, b) x5 = gen_array_ops.reshape(x5, [4, -1]) - x6 = gen_array_ops.reshape(x, [4, 12, 12]) - b = self._ConstOp((12,)) + x6 = gen_array_ops.reshape(x, [4, 24, 6]) + b = self._ConstOp((6,)) x6 = nn.bias_add(x6, b, data_format="NHWC") x6 = gen_array_ops.reshape(x6, [4, -1]) - x7 = gen_array_ops.reshape(x, [4, 12, 3, 4]) - b = self._ConstOp((4,)) + x7 = gen_array_ops.reshape(x, [4, 12, 4, 3]) + b = self._ConstOp((3,)) x7 = nn.bias_add(x7, b, data_format="NHWC") x7 = gen_array_ops.reshape(x7, [4, -1]) - x8 = gen_array_ops.reshape(x, [4, 12, 3, 2, 2]) - b = self._ConstOp((2,)) + x8 = gen_array_ops.reshape(x, [4, 4, 3, 2, 6]) + b = self._ConstOp((6,)) x8 = nn.bias_add(x8, b, data_format="NHWC") x8 = gen_array_ops.reshape(x8, [4, -1]) @@ -94,13 +95,13 @@ class BiasaddMatMulTest(trt_test.TfTrtIntegrationTestBase): x9 = nn.bias_add(x9, b, data_format="NCHW") x9 = gen_array_ops.reshape(x9, [4, -1]) - x10 = gen_array_ops.reshape(x, [4, 12, 3, 4]) - b = self._ConstOp((12,)) + x10 = gen_array_ops.reshape(x, [4, 3, 4, 12]) + b = self._ConstOp((3,)) x10 = nn.bias_add(x10, b, data_format="NCHW") x10 = gen_array_ops.reshape(x10, [4, -1]) - x11 = gen_array_ops.reshape(x, [4, 12, 12]) - b = self._ConstOp((12,)) + x11 = gen_array_ops.reshape(x, [4, 6, 24]) + b = self._ConstOp((6,)) x11 = nn.bias_add(x11, b, data_format="NCHW") x11 = gen_array_ops.reshape(x11, [4, -1]) @@ -116,13 +117,18 @@ class BiasaddMatMulTest(trt_test.TfTrtIntegrationTestBase): def GetConversionParams(self, run_params): """Return a ConversionParams for test.""" - return super(BiasaddMatMulTest, - self).GetConversionParams(run_params)._replace( - max_batch_size=4, maximum_cached_engines=1) + conversion_params = super(BiasaddMatMulTest, + self).GetConversionParams(run_params) + return conversion_params._replace( + max_batch_size=4, + maximum_cached_engines=1, + # Disable layout optimizer, since it will convert BiasAdd with NHWC + # format to NCHW format under four dimentional input. + rewriter_config=trt_test.OptimizerDisabledRewriterConfig()) def ExpectedEnginesToBuild(self, run_params): """Return the expected engines to build.""" - return ["my_trt_op_0"] + return ["TRTEngineOp_0"] def ShouldRunTest(self, run_params): """Whether to run the test.""" diff --git a/tensorflow/contrib/tensorrt/test/binary_tensor_weight_broadcast_test.py b/tensorflow/contrib/tensorrt/test/binary_tensor_weight_broadcast_test.py index b53cb3c091..169835956c 100644 --- a/tensorflow/contrib/tensorrt/test/binary_tensor_weight_broadcast_test.py +++ b/tensorflow/contrib/tensorrt/test/binary_tensor_weight_broadcast_test.py @@ -26,7 +26,6 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.ops import array_ops from tensorflow.python.ops import gen_array_ops -from tensorflow.python.ops import math_ops from tensorflow.python.platform import test @@ -56,10 +55,10 @@ class BinaryTensorWeightBroadcastTest(trt_test.TfTrtIntegrationTestBase): ]: a = self._ConstOp(weights_shape) f = x + a - x = math_ops.sigmoid(f) + x = self.trt_incompatible_op(f) a = self._ConstOp(weights_shape) f = a + x - x = math_ops.sigmoid(f) + x = self.trt_incompatible_op(f) gen_array_ops.reshape(x, [5, -1], name=output_name) return trt_test.TfTrtIntegrationTestParams( gdef=g.as_graph_def(), @@ -70,7 +69,7 @@ class BinaryTensorWeightBroadcastTest(trt_test.TfTrtIntegrationTestBase): def ExpectedEnginesToBuild(self, run_params): """Return the expected engines to build.""" - return ["my_trt_op_%d" % i for i in range(16)] + return ["TRTEngineOp_%d" % i for i in range(16)] if __name__ == "__main__": diff --git a/tensorflow/contrib/tensorrt/test/concatenation_test.py b/tensorflow/contrib/tensorrt/test/concatenation_test.py index 465cb02296..c3576f81d9 100644 --- a/tensorflow/contrib/tensorrt/test/concatenation_test.py +++ b/tensorflow/contrib/tensorrt/test/concatenation_test.py @@ -79,7 +79,7 @@ class ConcatenationTest(trt_test.TfTrtIntegrationTestBase): def ExpectedEnginesToBuild(self, run_params): """Return the expected engines to build.""" - return ["my_trt_op_0"] + return ["TRTEngineOp_0"] if __name__ == "__main__": diff --git a/tensorflow/contrib/tensorrt/test/const_broadcast_test.py b/tensorflow/contrib/tensorrt/test/const_broadcast_test.py index e32f047866..c1c883312d 100644 --- a/tensorflow/contrib/tensorrt/test/const_broadcast_test.py +++ b/tensorflow/contrib/tensorrt/test/const_broadcast_test.py @@ -64,7 +64,7 @@ class ConstBroadcastTest(trt_test.TfTrtIntegrationTestBase): def ExpectedEnginesToBuild(self, run_params): """Return the expected engines to build.""" - return ['my_trt_op_0'] + return ['TRTEngineOp_0'] def ExpectedAbsoluteTolerance(self, run_params): """The absolute tolerance to compare floating point results.""" diff --git a/tensorflow/contrib/tensorrt/test/memory_alignment_test.py b/tensorflow/contrib/tensorrt/test/memory_alignment_test.py index bc7c90081f..104bac43a0 100644 --- a/tensorflow/contrib/tensorrt/test/memory_alignment_test.py +++ b/tensorflow/contrib/tensorrt/test/memory_alignment_test.py @@ -68,7 +68,7 @@ class MemoryAlignmentTest(trt_test.TfTrtIntegrationTestBase): def ExpectedEnginesToBuild(self, run_params): """Return the expected engines to build.""" - return ["my_trt_op_0"] + return ["TRTEngineOp_0"] def ExpectedAbsoluteTolerance(self, run_params): """The absolute tolerance to compare floating point results.""" diff --git a/tensorflow/contrib/tensorrt/test/multi_connection_neighbor_engine_test.py b/tensorflow/contrib/tensorrt/test/multi_connection_neighbor_engine_test.py index 11be4feaf7..293f93d8a7 100644 --- a/tensorflow/contrib/tensorrt/test/multi_connection_neighbor_engine_test.py +++ b/tensorflow/contrib/tensorrt/test/multi_connection_neighbor_engine_test.py @@ -25,8 +25,6 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.ops import array_ops -from tensorflow.python.ops import gen_math_ops -from tensorflow.python.ops import math_ops from tensorflow.python.ops import nn from tensorflow.python.platform import test @@ -60,14 +58,14 @@ class MultiConnectionNeighborEngineTest(trt_test.TfTrtIntegrationTestBase): b = constant_op.constant( np.random.normal(5.0, 1.0, [1, 4, 1, 1]), name="bias", dtype=dtype) q = conv - b - edge = math_ops.sigmoid(q) + edge = self.trt_incompatible_op(q) b = constant_op.constant( np.random.normal(5.0, 1.0, [1, 4, 1, 1]), name="bias", dtype=dtype) d = b + conv - edge3 = math_ops.sigmoid(d) + edge3 = self.trt_incompatible_op(d) - edge1 = gen_math_ops.tan(conv) + edge1 = self.trt_incompatible_op(conv) t = t - edge1 q = q + edge t = t + q @@ -83,7 +81,7 @@ class MultiConnectionNeighborEngineTest(trt_test.TfTrtIntegrationTestBase): def ExpectedEnginesToBuild(self, run_params): """Return the expected engines to build.""" - return ["my_trt_op_0", "my_trt_op_1"] + return ["TRTEngineOp_0", "TRTEngineOp_1"] if __name__ == "__main__": diff --git a/tensorflow/contrib/tensorrt/test/neighboring_engine_test.py b/tensorflow/contrib/tensorrt/test/neighboring_engine_test.py index eddeafa38b..3e1e4b088b 100644 --- a/tensorflow/contrib/tensorrt/test/neighboring_engine_test.py +++ b/tensorflow/contrib/tensorrt/test/neighboring_engine_test.py @@ -66,8 +66,8 @@ class NeighboringEngineTest(trt_test.TfTrtIntegrationTestBase): def ExpectedEnginesToBuild(self, run_params): """Return the expected engines to build.""" return { - "my_trt_op_0": ["bias", "mul", "sub"], - "my_trt_op_1": ["weights", "conv"] + "TRTEngineOp_0": ["bias", "mul", "sub"], + "TRTEngineOp_1": ["weights", "conv"] } diff --git a/tensorflow/contrib/tensorrt/test/quantization_mnist_test.py b/tensorflow/contrib/tensorrt/test/quantization_mnist_test.py new file mode 100644 index 0000000000..31cbef89e2 --- /dev/null +++ b/tensorflow/contrib/tensorrt/test/quantization_mnist_test.py @@ -0,0 +1,290 @@ +# Copyright 2018 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. +# ============================================================================== +"""Script to test TF-TRT INT8 conversion without calibration on Mnist model.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.contrib.tensorrt.python import trt_convert +# pylint: disable=unused-import +from tensorflow.contrib.tensorrt.python.ops import trt_engine_op +# pylint: enable=unused-import +from tensorflow.core.protobuf import config_pb2 +from tensorflow.python import data +from tensorflow.python import keras +from tensorflow.python.estimator.estimator import Estimator +from tensorflow.python.estimator.model_fn import EstimatorSpec +from tensorflow.python.estimator.model_fn import ModeKeys +from tensorflow.python.estimator.run_config import RunConfig +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import graph_util +from tensorflow.python.framework import importer +from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util +from tensorflow.python.keras.datasets import mnist +from tensorflow.python.layers import layers +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import gen_array_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import metrics +from tensorflow.python.ops import nn +from tensorflow.python.ops import variable_scope +from tensorflow.python.ops.losses import losses +from tensorflow.python.platform import test +from tensorflow.python.platform import tf_logging as logging +from tensorflow.python.summary import summary +from tensorflow.python.training import saver +from tensorflow.python.training.adam import AdamOptimizer +from tensorflow.python.training.checkpoint_management import latest_checkpoint +from tensorflow.python.training.training_util import get_global_step + +INPUT_NODE_NAME = 'input' +OUTPUT_NODE_NAME = 'output' + + +class QuantizationAwareTrainingMNISTTest(test_util.TensorFlowTestCase): + + def _BuildGraph(self, x): + + def _Quantize(x, r): + x = gen_array_ops.quantize_and_dequantize_v2(x, -r, r) + return x + + def _DenseLayer(x, num_inputs, num_outputs, quantization_range, name): + """Dense layer with quantized outputs. + + Args: + x: input to the dense layer + num_inputs: number of input columns of x + num_outputs: number of output columns + quantization_range: the min/max range for quantization + name: name of the variable scope + + Returns: + The output of the layer. + """ + with variable_scope.variable_scope(name): + kernel = variable_scope.get_variable( + 'kernel', + shape=[num_inputs, num_outputs], + dtype=dtypes.float32, + initializer=keras.initializers.glorot_uniform()) + bias = variable_scope.get_variable( + 'bias', + shape=[num_outputs], + dtype=dtypes.float32, + initializer=keras.initializers.zeros()) + x = math_ops.matmul(x, kernel) + x = _Quantize(x, quantization_range) + x = nn.bias_add(x, bias) + x = _Quantize(x, quantization_range) + return x + + x = _Quantize(x, 1) + # Conv + Bias + Relu6 + x = layers.conv2d(x, filters=32, kernel_size=3, use_bias=True) + x = nn.relu6(x) + # Conv + Bias + Relu6 + x = layers.conv2d(x, filters=64, kernel_size=3, use_bias=True) + x = nn.relu6(x) + # Reduce + x = math_ops.reduce_mean(x, [1, 2]) + x = _Quantize(x, 6) + # FC1 + x = _DenseLayer(x, 64, 512, 6, name='dense') + x = nn.relu6(x) + # FC2 + x = _DenseLayer(x, 512, 10, 25, name='dense_1') + x = array_ops.identity(x, name=OUTPUT_NODE_NAME) + return x + + def _GetGraphDef(self, use_trt, max_batch_size, model_dir): + """Get the frozen mnist GraphDef. + + Args: + use_trt: whether use TF-TRT to convert the graph. + max_batch_size: the max batch size to apply during TF-TRT conversion. + model_dir: the model directory to load the checkpoints. + + Returns: + The frozen mnist GraphDef. + """ + graph = ops.Graph() + with self.session(graph=graph) as sess: + with graph.device('/GPU:0'): + x = array_ops.placeholder( + shape=(None, 28, 28, 1), dtype=dtypes.float32, name=INPUT_NODE_NAME) + self._BuildGraph(x) + # Load weights + mnist_saver = saver.Saver() + checkpoint_file = latest_checkpoint(model_dir) + mnist_saver.restore(sess, checkpoint_file) + # Freeze + graph_def = graph_util.convert_variables_to_constants( + sess, sess.graph_def, output_node_names=[OUTPUT_NODE_NAME]) + # Convert with TF-TRT + if use_trt: + logging.info('Number of nodes before TF-TRT conversion: %d', + len(graph_def.node)) + graph_def = trt_convert.create_inference_graph( + graph_def, + outputs=[OUTPUT_NODE_NAME], + max_batch_size=max_batch_size, + precision_mode='INT8', + max_workspace_size_bytes=4096 << 19, + minimum_segment_size=2, + use_calibration=False, + ) + logging.info('Number of nodes after TF-TRT conversion: %d', + len(graph_def.node)) + num_engines = len( + [1 for n in graph_def.node if str(n.op) == 'TRTEngineOp']) + self.assertEqual(1, num_engines) + return graph_def + + def _Run(self, is_training, use_trt, batch_size, num_epochs, model_dir): + """Train or evaluate the model. + + Args: + is_training: whether to train or evaluate the model. In training mode, + quantization will be simulated where the quantize_and_dequantize_v2 are + placed. + use_trt: if true, use TRT INT8 mode for evaluation, which will perform + real quantization. Otherwise use native TensorFlow which will perform + simulated quantization. Ignored if is_training is True. + batch_size: batch size. + num_epochs: how many epochs to train. Ignored if is_training is False. + model_dir: where to save or load checkpoint. + + Returns: + The Estimator evaluation result. + """ + # Get dataset + train_data, test_data = mnist.load_data() + + def _PreprocessFn(x, y): + x = math_ops.cast(x, dtypes.float32) + x = array_ops.expand_dims(x, axis=2) + x = 2.0 * (x / 255.0) - 1.0 + y = math_ops.cast(y, dtypes.int32) + return x, y + + def _EvalInputFn(): + mnist_x, mnist_y = test_data + dataset = data.Dataset.from_tensor_slices((mnist_x, mnist_y)) + dataset = dataset.apply( + data.experimental.map_and_batch( + map_func=_PreprocessFn, + batch_size=batch_size, + num_parallel_calls=8)) + dataset = dataset.repeat(count=1) + iterator = data.make_one_shot_iterator(dataset) + features, labels = iterator.get_next() + return features, labels + + def _TrainInputFn(): + mnist_x, mnist_y = train_data + dataset = data.Dataset.from_tensor_slices((mnist_x, mnist_y)) + dataset = dataset.shuffle(2 * len(mnist_x)) + dataset = dataset.apply( + data.experimental.map_and_batch( + map_func=_PreprocessFn, + batch_size=batch_size, + num_parallel_calls=8)) + dataset = dataset.repeat(count=num_epochs) + iterator = data.make_one_shot_iterator(dataset) + features, labels = iterator.get_next() + return features, labels + + def _ModelFn(features, labels, mode): + if is_training: + logits_out = self._BuildGraph(features) + else: + graph_def = self._GetGraphDef(use_trt, batch_size, model_dir) + logits_out = importer.import_graph_def( + graph_def, + input_map={INPUT_NODE_NAME: features}, + return_elements=[OUTPUT_NODE_NAME + ':0'], + name='')[0] + + loss = losses.sparse_softmax_cross_entropy( + labels=labels, logits=logits_out) + summary.scalar('loss', loss) + + classes_out = math_ops.argmax(logits_out, axis=1, name='classes_out') + accuracy = metrics.accuracy( + labels=labels, predictions=classes_out, name='acc_op') + summary.scalar('accuracy', accuracy[1]) + + if mode == ModeKeys.EVAL: + return EstimatorSpec( + mode, loss=loss, eval_metric_ops={'accuracy': accuracy}) + elif mode == ModeKeys.TRAIN: + optimizer = AdamOptimizer(learning_rate=1e-2) + train_op = optimizer.minimize(loss, global_step=get_global_step()) + return EstimatorSpec(mode, loss=loss, train_op=train_op) + + config_proto = config_pb2.ConfigProto() + config_proto.gpu_options.allow_growth = True + estimator = Estimator( + model_fn=_ModelFn, + model_dir=model_dir if is_training else None, + config=RunConfig(session_config=config_proto)) + + if is_training: + estimator.train(_TrainInputFn) + results = estimator.evaluate(_EvalInputFn) + logging.info('accuracy: %s', str(results['accuracy'])) + return results + + # To generate the checkpoint, set a different model_dir and call self._Run() + # by setting is_training=True and num_epochs=1000, e.g.: + # model_dir = '/tmp/quantization_mnist' + # self._Run( + # is_training=True, + # use_trt=False, + # batch_size=128, + # num_epochs=100, + # model_dir=model_dir) + def testEval(self): + if not trt_convert.is_tensorrt_enabled(): + return + model_dir = test.test_src_dir_path('contrib/tensorrt/test/testdata') + + accuracy_tf_native = self._Run( + is_training=False, + use_trt=False, + batch_size=128, + num_epochs=None, + model_dir=model_dir)['accuracy'] + logging.info('accuracy_tf_native: %f', accuracy_tf_native) + self.assertAllClose(accuracy_tf_native, 0.9662) + + if trt_convert.get_linked_tensorrt_version()[0] < 5: + return + + accuracy_tf_trt = self._Run( + is_training=False, + use_trt=True, + batch_size=128, + num_epochs=None, + model_dir=model_dir)['accuracy'] + logging.info('accuracy_tf_trt: %f', accuracy_tf_trt) + self.assertAllClose(accuracy_tf_trt, 0.9677) + + +if __name__ == '__main__': + test.main() diff --git a/tensorflow/contrib/tensorrt/test/quantization_test.py b/tensorflow/contrib/tensorrt/test/quantization_test.py new file mode 100644 index 0000000000..28353273ed --- /dev/null +++ b/tensorflow/contrib/tensorrt/test/quantization_test.py @@ -0,0 +1,144 @@ +# Copyright 2018 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. +# ============================================================================== +"""Model script to test TF-TensorRT integration.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +from tensorflow.contrib.tensorrt.python import trt_convert +from tensorflow.contrib.tensorrt.test import tf_trt_integration_test_base as trt_test +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import gen_array_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.platform import test + + +def _GetParams(add_quantization_nodes, dtype=dtypes.float32): + input_name = "input" + input_dims = [8, 8] + output_name = "output" + + def _Quantize(x, r): + if add_quantization_nodes: + x = gen_array_ops.fake_quant_with_min_max_vars(x, -r, r) + return x + + g = ops.Graph() + with g.as_default(): + x = array_ops.placeholder( + dtype=dtype, shape=[None] + input_dims[1:], name=input_name) + x = _Quantize(x, 10.0) + x = x + 5 + x = _Quantize(x, 15.0) + x = x - 5 + x = _Quantize(x, 10.0) + x = x * 0.1 + x = _Quantize(x, 1.0) + w = constant_op.constant(np.ones((8, 1)), dtype=dtypes.float32) + x = math_ops.matmul(x, w) + x = _Quantize(x, 10.0) + x = array_ops.identity(x, name=output_name) + + return trt_test.TfTrtIntegrationTestParams( + gdef=g.as_graph_def(), + input_names=[input_name], + input_dims=[input_dims], + output_names=[output_name], + expected_output_dims=[(8, 1)]) + + +class QuantizationMissingAllRangesTest(trt_test.TfTrtIntegrationTestBase): + + def GetParams(self): + """Create a graph containing single segment with no quantization ranges.""" + return _GetParams(add_quantization_nodes=False) + + def ShouldRunTest(self, run_params): + if trt_convert.get_linked_tensorrt_version()[0] < 5: + return False + # Only test static engine mode, with or without calibration. + return (trt_test.IsQuantizationMode(run_params.precision_mode) and + not run_params.use_optimizer and not run_params.dynamic_engine) + + def ExpectedEnginesToBuild(self, run_params): + """Return the expected engines to build.""" + if run_params.use_calibration: + # In static engine mode with calibration, it should build a calibration + # engine. + return ["my_trt_op_0"] + # In static engine mode without calibration, the engine building will fail + # since no quantization ranges are set, which results in no TRT nodes. + return [] + + +class QuantizationWithRangesTest(trt_test.TfTrtIntegrationTestBase): + + def GetParams(self): + """Create a graph containing single segment with no quantization ranges.""" + return _GetParams(add_quantization_nodes=True) + + def ShouldRunTest(self, run_params): + if trt_convert.get_linked_tensorrt_version()[0] < 5: + return False + # Test static/dynamic engine with/without calibration. + return (trt_test.IsQuantizationMode(run_params.precision_mode) and + not run_params.use_optimizer) + + def ExpectedEnginesToBuild(self, run_params): + """Return the expected engines to build.""" + return ["my_trt_op_0"] + + def ExpectedAbsoluteTolerance(self, run_params): + """The absolute tolerance to compare floating point results.""" + return 1.e-05 if run_params.precision_mode == "FP32" else 1.e-01 + + def ExpectedRelativeTolerance(self, run_params): + """The relative tolerance to compare floating point results.""" + return 1.e-05 if run_params.precision_mode == "FP32" else 1.e-01 + + +class NonQuantizedPrecisionsWithRangesTest(trt_test.TfTrtIntegrationTestBase): + + def GetParams(self): + """Create a graph containing single segment with no quantization ranges.""" + return _GetParams(add_quantization_nodes=True) + + def ShouldRunTest(self, run_params): + # Only test FP32/FP16 mode. + return not trt_test.IsQuantizationMode(run_params.precision_mode) + + def ExpectedEnginesToBuild(self, run_params): + """Return the expected engines to build.""" + # The fake quant ops are not supported in FP32/FP16 mode, and will split the + # graph into three TRT segments. + return ["my_trt_op_0", "my_trt_op_1", "my_trt_op_2", "my_trt_op_3"] + + def ExpectedAbsoluteTolerance(self, run_params): + """The absolute tolerance to compare floating point results.""" + return 1.e-05 if run_params.precision_mode == "FP32" else 1.e-01 + + def ExpectedRelativeTolerance(self, run_params): + """The relative tolerance to compare floating point results.""" + return 1.e-05 if run_params.precision_mode == "FP32" else 1.e-01 + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/contrib/tensorrt/test/rank_two_test.py b/tensorflow/contrib/tensorrt/test/rank_two_test.py index 74a4a05925..0cd733dca1 100644 --- a/tensorflow/contrib/tensorrt/test/rank_two_test.py +++ b/tensorflow/contrib/tensorrt/test/rank_two_test.py @@ -68,11 +68,11 @@ class RankTwoTest(trt_test.TfTrtIntegrationTestBase): def ExpectedEnginesToBuild(self, run_params): """Return the expected engines to build.""" return { - "my_trt_op_0": [ + "TRTEngineOp_0": [ "add0_1", "add0_2", "add0_3", "c0_1", "c0_2", "c0_3", "abs0_1", "abs0_2" ], - "my_trt_op_1": [ + "TRTEngineOp_1": [ "add", "add1_1", "add1_2", "add1_3", "c1_1", "c1_2", "c1_3", "abs1_1", "abs1_2", "reciprocal0", "reciprocal1" ], diff --git a/tensorflow/contrib/tensorrt/test/reshape_transpose_test.py b/tensorflow/contrib/tensorrt/test/reshape_transpose_test.py index bbc724ab18..207944468a 100644 --- a/tensorflow/contrib/tensorrt/test/reshape_transpose_test.py +++ b/tensorflow/contrib/tensorrt/test/reshape_transpose_test.py @@ -79,8 +79,8 @@ class ReshapeTest(trt_test.TfTrtIntegrationTestBase): def ExpectedEnginesToBuild(self, run_params): """Return the expected engines to build.""" return { - "my_trt_op_0": ["reshape-%d" % i for i in range(7)] + - ["reshape-%d/shape" % i for i in range(7)] + "TRTEngineOp_0": ["reshape-%d" % i for i in range(7)] + + ["reshape-%d/shape" % i for i in range(7)] } def ShouldRunTest(self, run_params): @@ -117,7 +117,7 @@ class TransposeTest(trt_test.TfTrtIntegrationTestBase): # Note: by default Grappler will run the TRT optimizer twice. At the # first time it will group the two transpose ops below to same segment # then fail the conversion due to the expected batch dimension problem. - # At the second time, since the input of bridge op is my_trt_op_0, it + # At the second time, since the input of bridge op is TRTEngineOp_0, it # will fail to do shape inference which then cause conversion to fail. # TODO(laigd): support shape inference, make TRT optimizer run only # once, and fix this. @@ -136,7 +136,7 @@ class TransposeTest(trt_test.TfTrtIntegrationTestBase): def ExpectedEnginesToBuild(self, run_params): """Return the expected engines to build.""" return { - "my_trt_op_0": [ + "TRTEngineOp_0": [ "transpose-1", "transpose-1/perm", "transposeback", "transposeback/perm" ] diff --git a/tensorflow/contrib/tensorrt/test/testdata/checkpoint b/tensorflow/contrib/tensorrt/test/testdata/checkpoint new file mode 100644 index 0000000000..a603e1aec9 --- /dev/null +++ b/tensorflow/contrib/tensorrt/test/testdata/checkpoint @@ -0,0 +1,3 @@ +model_checkpoint_path: "model.ckpt-46900" +all_model_checkpoint_paths: "model.ckpt-0" +all_model_checkpoint_paths: "model.ckpt-46900" diff --git a/tensorflow/contrib/tensorrt/test/testdata/model.ckpt-46900.data-00000-of-00001 b/tensorflow/contrib/tensorrt/test/testdata/model.ckpt-46900.data-00000-of-00001 new file mode 100644 index 0000000000000000000000000000000000000000..88a998f184b275121e1e76eb51d2310da149f10a GIT binary patch literal 686728 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

n|U(L*sXt#p& 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

(u53Zi|>OUYUFmuAY)@+~zxZ#1~s# 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 kmi}G5*R6iv-W%pe_iH$@?Ay6RVqaQxx1E+b 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 F0{~I{!JYsB literal 0 HcmV?d00001 diff --git a/tensorflow/contrib/tensorrt/test/testdata/model.ckpt-46900.index b/tensorflow/contrib/tensorrt/test/testdata/model.ckpt-46900.index new file mode 100644 index 0000000000000000000000000000000000000000..537976571337508ab1798d33646c51d62a146ecc GIT binary patch literal 652 zcmaFOHiLnIje}8&iGz`Wn>#7BB+)RwAiq4dh=WmxL4(C~S+$@Q3p=k-yr}2_1r`k! z(^q#&7Bg@QCFkdr8KvkaWhNGLFbc5xp;aA-6znmW$Aki*R?tnZkTm@6v9(!t0m ztxz&qF+_k#INnfFf`N%qBA}zWW2OTm9LvBZnv$AV zoGN(QhgFD~qk%=C;|Z@ugMjI}C*iCN{IW`d)q?5_Us%;8!oGXB^D~LE<_k)(wD2)H zN`$;~X<}oO61~m9$#|YO*<=czMneOKsQ|ZvC=aWYV3d&(1A`c=k_77c{dB~nipQ* 0" % number_of_tuple_elements) @@ -293,9 +291,8 @@ class InfeedQueue(object): self.number_of_tuple_elements """ if len(input_tensors) != self.number_of_tuple_elements: - raise ValueError( - "input_tensors is %s, but should be a list of %d Tensors", ( - str(input_tensors), self.number_of_tuple_elements)) + raise ValueError("input_tensors is %s, but should be a list of %d Tensors" + % (str(input_tensors), self.number_of_tuple_elements)) self.set_tuple_shapes([t.shape for t in input_tensors]) self.set_tuple_types([t.dtype for t in input_tensors]) @@ -451,8 +448,8 @@ class InfeedQueue(object): for i in xrange(1, self.number_of_tuple_elements): if devices[0] != devices[i]: raise ValueError( - "input devices for shard %d are %s, but should all be the same", - index, str(devices)) + "input devices for shard %d are %s, but should all be the same" % + (index, str(devices))) with ops.colocate_with(inputs[0]): return tpu_ops.infeed_enqueue_tuple( inputs=inputs, @@ -792,18 +789,14 @@ class _PartitionedInfeedQueue(InfeedQueue): Args: tensor: Input tensor for partitioning. - dims: A list of integer describes how to partition the input tensor. + dims: 1-D np.array of the list of integer describes how to partition the + input tensor. Raises: ValueError: If the tensor can't be partitioned by dims or the num_cores_per_replica doesn't match the number of partitions(dims.prod()). """ - if dims is None: - return - - dims = np.array(dims) - if (dims < 1).any(): raise ValueError("All input partition dims must be >= 1.") @@ -823,11 +816,6 @@ class _PartitionedInfeedQueue(InfeedQueue): "partition dims = {}).".format(tensor.shape.as_list(), dims)) tensor.shape.assert_is_fully_defined() - if (np.array(tensor.shape.as_list()) % dims != 0).any(): - raise ValueError( - "All input partition dims must divide exactly into the `Tensor` " - "shape (tensor shape = {}, input partition dims = {}).".format( - tensor.shape.as_list(), dims)) def _partition_or_replicate_on_host(self, tensor, dims): """Partitions or replicates the input tensor. @@ -840,16 +828,39 @@ class _PartitionedInfeedQueue(InfeedQueue): Returns: An iterator of `Tensor`s or a list of partioned tensors. """ - self._check_input_partition_dims(tensor, dims) if dims is None: return itertools.repeat(tensor) - else: - output = [tensor] - for axis, dim in enumerate(dims): - if dim > 1: - output = [array_ops.split(x, dim, axis=axis) for x in output] - output = nest.flatten(output) - return output + dims = np.array(dims) + self._check_input_partition_dims(tensor, dims) + output = [tensor] + shape_list = np.array(tensor.shape.as_list()) + quotients, remainders = np.divmod(shape_list, dims) + for axis, (quotient, remainder, dim, original_size) in enumerate( + zip(quotients, remainders, dims, shape_list)): + if dim <= 1: + continue + if remainder > 0: + # For each dimension, when it cannot be evenly partitioned, XLA assumes + # tensors are partitioned in a greedy manner by using + # ceil_ratio(size/dim) first. E.g. 2D tensor with shape (5, 14) and dims + # are (2, 4). Since 5 % 2 = 1 and 14 % 4 = 2, [5, 14] => + # [[(3, 4), (3, 4), (2, 4), (2, 2)], + # [(2, 4), (2, 4), (2, 4), (2, 2)]] + ceil_ratio = quotient + 1 + num_full_slots, left_over = np.divmod(original_size, ceil_ratio) + num_or_size_splits = [ceil_ratio] * num_full_slots + [left_over] + if len(num_or_size_splits) < dim: + num_or_size_splits += [0] * (dim - len(num_or_size_splits)) + new_output = [] + for x in output: + new_output.append( + array_ops.split( + x, num_or_size_splits=num_or_size_splits, axis=axis)) + output = new_output + else: + output = [array_ops.split(x, dim, axis=axis) for x in output] + output = nest.flatten(output) + return output def _tag_sharding_attribute_for_dequeued_tensor(self, tensor, dims): """Tags appropriate XLA sharding attribute to the dequeued tensor. @@ -866,13 +877,9 @@ class _PartitionedInfeedQueue(InfeedQueue): elif np.prod(dims) == 1: return xla_sharding.assign_device(tensor, 0) else: - tile_shape = np.array(tensor.shape.as_list()) // dims tile_assignment = np.arange(np.prod(dims)).reshape(dims) return xla_sharding.tile( tensor=tensor, - tile_shape=xla_shape.CreateShapeFromDtypeAndTuple( - dtype=np.dtype(tensor.dtype.as_numpy_dtype), - shape_tuple=tile_shape), tile_assignment=tile_assignment) def _tag_sharding_attribute_for_dequeued_tensors(self, dequeues, dims): diff --git a/tensorflow/contrib/tpu/python/tpu/training_loop.py b/tensorflow/contrib/tpu/python/tpu/training_loop.py index b6c350ecd7..0187b4bec6 100644 --- a/tensorflow/contrib/tpu/python/tpu/training_loop.py +++ b/tensorflow/contrib/tpu/python/tpu/training_loop.py @@ -166,8 +166,8 @@ def while_loop(condition, body, inputs=None, infeed_queue=None, name=None): # control dependencies from any side-effecting operations. if input_arity == 0: inputs = [array_ops.constant(0)] - return control_flow_ops.while_loop(condition_wrapper, body_wrapper, inputs, - name="") + return control_flow_ops.while_loop( + condition_wrapper, body_wrapper, inputs, name="", parallel_iterations=1) def repeat(n, body, inputs=None, infeed_queue=None, name=None): diff --git a/tensorflow/contrib/tpu/tpu_estimator.md b/tensorflow/contrib/tpu/tpu_estimator.md index b6514e19dc..552febd80b 100644 --- a/tensorflow/contrib/tpu/tpu_estimator.md +++ b/tensorflow/contrib/tpu/tpu_estimator.md @@ -89,12 +89,9 @@ handle training: dataset = tf.data.TFRecordDataset( filename, buffer_size=FLAGS.dataset_reader_buffer_size) - dataset = dataset.map(parser).cache().repeat().batch(batch_size) - images, labels = dataset.make_one_shot_iterator().get_next() - # set_shape to give inputs statically known shapes. - images.set_shape([batch_size, 28 * 28]) - labels.set_shape([batch_size]) - return images, labels + dataset = dataset.map(parser).cache().repeat().batch( + batch_size, drop_remainder=True) + return dataset return input_fn diff --git a/tensorflow/contrib/training/BUILD b/tensorflow/contrib/training/BUILD index 00295f57f6..f6427ae05a 100644 --- a/tensorflow/contrib/training/BUILD +++ b/tensorflow/contrib/training/BUILD @@ -26,7 +26,6 @@ py_library( "python/training/resample.py", "python/training/sampling_ops.py", "python/training/sequence_queueing_state_saver.py", - "python/training/tensor_queue_dataset.py", "python/training/training.py", "python/training/tuner.py", ], @@ -287,28 +286,6 @@ py_test( ], ) -py_test( - name = "tensor_queue_dataset_test", - size = "large", - srcs = ["python/training/tensor_queue_dataset_test.py"], - srcs_version = "PY2AND3", - tags = ["notsan"], - deps = [ - ":training_py", - "//tensorflow/python:client_testlib", - "//tensorflow/python:framework_for_generated_wrappers", - "//tensorflow/python:gradients", - "//tensorflow/python:math_ops", - "//tensorflow/python:platform", - "//tensorflow/python:random_seed", - "//tensorflow/python:training", - "//tensorflow/python:variables", - "//tensorflow/python/data", - "//tensorflow/python/data/experimental/kernel_tests/serialization:dataset_serialization_test_base", - "//third_party/py/numpy", - ], -) - tf_proto_library( name = "protos_all", srcs = glob(["**/*.proto"]), diff --git a/tensorflow/contrib/training/__init__.py b/tensorflow/contrib/training/__init__.py index 3547e71184..87ce57ef06 100644 --- a/tensorflow/contrib/training/__init__.py +++ b/tensorflow/contrib/training/__init__.py @@ -59,8 +59,6 @@ from tensorflow.contrib.training.python.training.hparam import * from tensorflow.contrib.training.python.training.resample import * from tensorflow.contrib.training.python.training.sampling_ops import * from tensorflow.contrib.training.python.training.sequence_queueing_state_saver import * -from tensorflow.contrib.training.python.training.tensor_queue_dataset import enqueue_in_queue_dataset -from tensorflow.contrib.training.python.training.tensor_queue_dataset import prepend_from_queue_and_padded_batch_dataset from tensorflow.contrib.training.python.training.training import add_gradients_summaries from tensorflow.contrib.training.python.training.training import clip_gradient_norms from tensorflow.contrib.training.python.training.training import clip_gradient_norms_fn @@ -79,7 +77,6 @@ _allowed_symbols = [ 'FeedingQueueRunner', 'get_or_create_eval_step', 'StopAfterNEvalsHook', 'SummaryAtEndHook', 'wait_for_new_checkpoint', 'add_gradients_summaries', 'clip_gradient_norms', 'clip_gradient_norms_fn', 'create_train_op', - 'multiply_gradients', 'enqueue_in_queue_dataset', - 'prepend_from_queue_and_padded_batch_dataset', 'train'] + 'multiply_gradients', 'train'] remove_undocumented(__name__, _allowed_symbols) diff --git a/tensorflow/contrib/training/python/training/tensor_queue_dataset.py b/tensorflow/contrib/training/python/training/tensor_queue_dataset.py deleted file mode 100644 index 8896a95327..0000000000 --- a/tensorflow/contrib/training/python/training/tensor_queue_dataset.py +++ /dev/null @@ -1,201 +0,0 @@ -# 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. -# ============================================================================== -"""Python wrappers for Datasets and Iterators.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.data.util import convert -from tensorflow.python.data.util import nest -from tensorflow.python.data.util import sparse -from tensorflow.python.framework import dtypes -from tensorflow.python.framework import ops -from tensorflow.python.framework import tensor_shape -from tensorflow.python.framework import tensor_util -from tensorflow.python.ops import gen_dataset_ops -from tensorflow.python.util import nest as tf_nest - - -class _PrependFromQueueAndPaddedBatchDataset(dataset_ops.UnaryDataset): - """A `Dataset` that prepends a queue to another `Dataset`. - - A vector of handles to the queue is returned as the first component of - the associated iterator. This vector can be passed to - `enqueue_in_queue_dataset` to add new elements to the queue. - """ - - def __init__(self, input_dataset, batch_size, padded_shapes, padding_values): - """Initialize `PrependFromQueueAndPaddedBatchDataset`.""" - super(_PrependFromQueueAndPaddedBatchDataset, self).__init__(input_dataset) - if sparse.any_sparse(input_dataset.output_classes): - raise TypeError( - "Batching of padded sparse tensors is not currently supported") - self._input_dataset = input_dataset - self._batch_size = ops.convert_to_tensor( - batch_size, dtype=dtypes.int64, name="batch_size") - if padded_shapes is None: - self._padded_shapes = nest.map_structure( - convert.partial_shape_to_tensor, input_dataset.output_shapes) - else: - self._padded_shapes = nest.map_structure_up_to( - input_dataset.output_shapes, convert.partial_shape_to_tensor, - padded_shapes) - # pylint: disable=protected-access - padding_values = ( - padding_values if padding_values is not None else - dataset_ops._default_padding(input_dataset)) - self._padding_values = nest.map_structure_up_to( - input_dataset.output_shapes, dataset_ops._padding_value_to_tensor, - padding_values, input_dataset.output_types) - # pylint: enable=protected-access - - def _as_variant_tensor(self): - # pylint: disable=protected-access - return gen_dataset_ops.prepend_from_queue_and_padded_batch_dataset( - self._input_dataset._as_variant_tensor(), - batch_size=self._batch_size, - padded_shapes=[ - ops.convert_to_tensor(s, dtype=dtypes.int64) - for s in nest.flatten(self._padded_shapes) - ], - padding_values=nest.flatten(self._padding_values), - output_shapes=nest.flatten( - sparse.as_dense_shapes(self.output_shapes, self.output_classes))) - # pylint: enable=protected-access - - @property - def output_classes(self): - return (ops.Tensor, self._input_dataset.output_classes) - - def _as_batch_shape(self, shape_like): - return tensor_shape.vector(None).concatenate( - tensor_util.constant_value_as_shape(shape_like)) - - @property - def output_shapes(self): - # First output is a variant representing the Queue - return (tensor_shape.vector(None), - nest.map_structure(self._as_batch_shape, self._padded_shapes)) - - @property - def output_types(self): - # First output is a variant representing the Queue - return (dtypes.variant, self._input_dataset.output_types) - - -def prepend_from_queue_and_padded_batch_dataset(batch_size, - padding_values=None, - padded_shapes=None): - """A transformation that prepends a queue to a `Dataset` and batches results. - - A vector of handles to the queue is returned as the first component of the - associated iterator. This vector can be passed to `enqueue_in_queue_dataset` - to add new elements to the queue. - - Below is an example of how this dataset might be used to split incoming - variable-length sequences into "head" and "rest" parts, where "rest" parts - are re-enqueued back into the dataset. A more realistic example would - perform some calculation on the "head" and modify some components of "rest" - with the result (before re-enqueueing). - - ```python - dataset = tf.data.Dataset.from_tensor_slices([2*x for x in range(10)]) - # Make a dataset of variable-length vectors and their lengths. - dataset = dataset.map(lambda count: (count, tf.ones((count,)))) - # Emit a queue we can prepend to, and counts/values as padded batch. - dataset = dataset.apply( - tf.contrib.training.prepend_from_queue_and_padded_batch_dataset( - batch_size=10)) - dataset = dataset.prefetch(1) - - iterator = dataset.make_one_shot_iterator() - queue, (count, padded_value) = iterator.get_next() - - # Split the padded_value into two pieces: head and rest - rest_indices = tf.squeeze(tf.where(count > 3), axis=1) - bound = tf.minimum(3, tf.reduce_max(count)) - value_head = padded_value[:, :bound] - count_rest = tf.gather(count - 3, rest_indices) - value_rest = tf.gather(padded_value[:, bound:], rest_indices) - queue_rest = tf.gather(queue, rest_indices) - enqueue_rest_op = tf.contrib.training.enqueue_in_queue_dataset( - queue_rest, (count_rest, value_rest)) - with tf.control_dependencies([enqueue_rest_op]): - calculation = fn(value_head) - - while True: # Will raise OutOfRange when finished with all pieces. - session.run(calculation) - ``` - - Args: - batch_size: `int64` scalar tensor. The batch size to use when performing - padded batching. - padding_values: (optional) Nested tuple of scalar tensors. If provided, - the structure and dtypes of padding_values should match that of - incoming dataset's `output_types`. - padded_shapes: (optional) Nested tuple of `int64` vector tensors. - If provided, the structure must match that of the incoming dataset's - `output_types`. If not provided, the incoming dataset's `output_shapes` - is used. Any unknown (`None` or `-1`) dimensions in the shapes are - treated as being unique per-batch: for each batch time, an unknown - dimension is replaced with the maximum given value of this dimension - across all tensors for the given component in the batch. - - Returns: - A `Dataset` transformation function, which can be passed to - `tf.data.Dataset.apply`. - """ - - def _apply_fn(dataset): - return _PrependFromQueueAndPaddedBatchDataset( - dataset, - batch_size=batch_size, - padding_values=padding_values, - padded_shapes=padded_shapes) - - return _apply_fn - - -def enqueue_in_queue_dataset(queue, components): - """Enqueue components into queue from `PrependFromQueueAndPaddedBatchDataset`. - - The components' dtypes and shapes must be compatible with the `output_shapes` - attribute of the `dataset` created by - `prepend_from_queue_and_padded_batch_dataset`. This operation supports both - non-batched and batched modes. - - For more details, see the example in the docstring for - `prepend_from_queue_and_padded_batch_dataset`. - - Args: - queue: `variant` scalar or vector tensor. - The tensor emitted by the first component of the iterator associated with - `prepend_from_queue_and_padded_batch_dataset`. If this is a scalar, - then the `components` input tensors should not have a prepended batch - dimension. - components: Nested tuple of tensors, each with a leading batch dimension - if `queue` is a vector. The structure, dtypes, and shapes - (excluding batch dimension) must match the nested tuples - `dataset.output_types[1]` and `dataset.output_shapes[1]` (the non-queue - output types and shapes) of the `dataset` emitted by - the original `prepend_from_queue_and_padded_batch_dataset` call. - - Returns: - An `Operation` that enqueues `components` into the dataset(s) associated - with entries of `queue`. - """ - return gen_dataset_ops.enqueue_in_queue_dataset( - queue=queue, components=tf_nest.flatten(components)) diff --git a/tensorflow/contrib/training/python/training/tensor_queue_dataset_test.py b/tensorflow/contrib/training/python/training/tensor_queue_dataset_test.py deleted file mode 100644 index c1657fec7b..0000000000 --- a/tensorflow/contrib/training/python/training/tensor_queue_dataset_test.py +++ /dev/null @@ -1,355 +0,0 @@ -# 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 TensorQueueDataset.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import numpy as np - -from tensorflow.contrib.training.python.training import tensor_queue_dataset as tqd -from tensorflow.python.data.experimental.kernel_tests.serialization import dataset_serialization_test_base -from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.framework import dtypes -from tensorflow.python.framework import errors -from tensorflow.python.framework import ops -from tensorflow.python.ops import array_ops -from tensorflow.python.ops import math_ops -from tensorflow.python.ops import string_ops -from tensorflow.python.platform import test - - -class PrependFromQueueAndPaddedBatchDatasetTest(test.TestCase): - - def testNoEnqueue(self): - dataset = dataset_ops.Dataset.from_tensor_slices([0, 1, 2]) - dataset = dataset.apply( - tqd.prepend_from_queue_and_padded_batch_dataset(batch_size=1)) - self.assertEqual((dtypes.variant, dtypes.int32), dataset.output_types) - self.assertAllEqual(([None],) * 2, - [x.as_list() for x in dataset.output_shapes]) - iterator = dataset.make_one_shot_iterator() - _, value = iterator.get_next() - self.assertEqual([0], self.evaluate(value)) - self.assertEqual([1], self.evaluate(value)) - self.assertEqual([2], self.evaluate(value)) - with self.assertRaisesOpError("End of sequence"): - self.evaluate(value) - - def testBatchedNoEnqueue(self): - dataset = dataset_ops.Dataset.from_tensor_slices([0, 1, 2]) - dataset = dataset.apply( - tqd.prepend_from_queue_and_padded_batch_dataset(batch_size=2)) - iterator = dataset.make_one_shot_iterator() - _, value = iterator.get_next() - self.assertAllEqual([0, 1], self.evaluate(value)) - self.assertAllEqual([2], self.evaluate(value)) - with self.assertRaisesOpError("End of sequence"): - self.evaluate(value) - - def testBatchedWithBiggerPaddingNoEnqueue(self): - dataset = dataset_ops.Dataset.from_tensor_slices([[0], [1], [2]]) - dataset = dataset.apply( - tqd.prepend_from_queue_and_padded_batch_dataset( - batch_size=2, padded_shapes=[3])) - iterator = dataset.make_one_shot_iterator() - _, value = iterator.get_next() - self.assertAllEqual([[0, 0, 0], [1, 0, 0]], self.evaluate(value)) - self.assertAllEqual([[2, 0, 0]], self.evaluate(value)) - with self.assertRaisesOpError("End of sequence"): - self.evaluate(value) - - def testBatchedWithBiggerPaddingOneEnqueue(self): - dataset = dataset_ops.Dataset.from_tensor_slices([[0], [1], [2]]) - dataset = dataset.apply( - tqd.prepend_from_queue_and_padded_batch_dataset( - batch_size=1, padded_shapes=[3])) - iterator = dataset.make_one_shot_iterator() - queue_handle, value = iterator.get_next() - enqueue_negative = tqd.enqueue_in_queue_dataset(queue_handle, -value) - with self.cached_session() as sess: - self.assertAllEqual([[0, 0, 0]], sess.run(value)) - value_1, _ = sess.run([value, enqueue_negative]) - self.assertAllEqual([[1, 0, 0]], value_1) - value_2, _ = sess.run([value, enqueue_negative]) - self.assertAllEqual([[-1, 0, 0]], value_2) - value_3 = sess.run(value) - self.assertAllEqual([[1, 0, 0]], value_3) - value_4, _ = sess.run([value, enqueue_negative]) - self.assertAllEqual([[2, 0, 0]], value_4) - value_5 = sess.run(value) - self.assertAllEqual([[-2, 0, 0]], value_5) - with self.assertRaisesOpError("End of sequence"): - sess.run(value) - - def testOneEnqueue(self): - dataset = dataset_ops.Dataset.from_tensor_slices([0, 1, 2]) - dataset = dataset.apply( - tqd.prepend_from_queue_and_padded_batch_dataset(batch_size=1)) - iterator = dataset.make_one_shot_iterator() - queue_handle, value = iterator.get_next() - enqueue_negative = tqd.enqueue_in_queue_dataset(queue_handle, -value) - with self.cached_session() as sess: - self.assertEqual([0], sess.run(value)) - value_1, _ = sess.run([value, enqueue_negative]) - self.assertEqual([1], value_1) - value_2, _ = sess.run([value, enqueue_negative]) - self.assertEqual([-1], value_2) - value_3 = sess.run(value) - self.assertEqual([1], value_3) - value_4, _ = sess.run([value, enqueue_negative]) - self.assertEqual([2], value_4) - value_5 = sess.run(value) - self.assertEqual([-2], value_5) - with self.assertRaisesOpError("End of sequence"): - sess.run(value) - - def testBatchedOneEnqueue(self): - dataset = dataset_ops.Dataset.from_tensor_slices([0, 1, 2]) - dataset = dataset.apply( - tqd.prepend_from_queue_and_padded_batch_dataset(batch_size=2)) - iterator = dataset.make_one_shot_iterator() - queue_handle, value = iterator.get_next() - enqueue_negative = tqd.enqueue_in_queue_dataset(queue_handle, -value) - enqueue_zeroth = tqd.enqueue_in_queue_dataset([queue_handle[0]], - array_ops.expand_dims( - value[0], axis=0)) - with self.cached_session() as sess: - value_0, _ = sess.run([value, enqueue_negative]) - self.assertAllEqual([0, 1], value_0) - value_1, _ = sess.run([value, enqueue_zeroth]) - self.assertAllEqual([0, -1], value_1) - value_2, _ = sess.run([value, enqueue_negative]) - self.assertAllEqual([0, 2], value_2) - self.assertAllEqual([0, -2], sess.run(value)) - with self.assertRaisesOpError("End of sequence"): - sess.run(value) - - def testManyEnqueue(self): - dataset = dataset_ops.Dataset.from_tensor_slices([0, 1]) - dataset = dataset.apply( - tqd.prepend_from_queue_and_padded_batch_dataset(batch_size=1)) - iterator = dataset.make_one_shot_iterator() - queue_handle, value = iterator.get_next() - enqueue_many_more = [ - tqd.enqueue_in_queue_dataset(queue_handle, value + 100 + i) - for i in range(1000) - ] - with self.cached_session() as sess: - value_0, _ = sess.run((value, enqueue_many_more)) - self.assertEqual([0], value_0) - rest = [] - for _ in range(1000): - rest.append(sess.run(value)) - self.assertEquals([[100 + i] for i in range(1000)], sorted(rest)) - # Going back to the original input. - value_1, _ = sess.run((value, enqueue_many_more)) - self.assertEqual(1, value_1) - rest = [] - for _ in range(1000): - rest.append(sess.run(value)) - self.assertEquals([[100 + i + 1] for i in range(1000)], sorted(rest)) - with self.assertRaisesOpError("End of sequence"): - sess.run(value) - - def testEnqueueWithPrefetch(self): - dataset = dataset_ops.Dataset.from_tensor_slices([0]) - dataset = dataset.apply( - tqd.prepend_from_queue_and_padded_batch_dataset(batch_size=1)) - # Prefetching will request additional values before they are - # available to the queue. - dataset = dataset.prefetch(buffer_size=3) - iterator = dataset.make_one_shot_iterator() - queue_handle, value = iterator.get_next() - enqueue = tqd.enqueue_in_queue_dataset(queue_handle, value + 1) - with self.cached_session() as sess: - i = 0 - while i < 4: - received, _ = sess.run((value, enqueue)) - if received.size > 0: - self.assertAllEqual([i], received) - i += 1 - received_last = False - while True: - try: - received = sess.run(value) - if received.size > 0: - self.assertAllEqual([4], received) - received_last = True - except errors.OutOfRangeError: - break - self.assertTrue(received_last) - - def testDatasetWithPaddedShapeSmallerThanInputFails(self): - dataset = dataset_ops.Dataset.from_tensor_slices([[0, 0, 0]]).repeat(None) - dataset = dataset.apply( - tqd.prepend_from_queue_and_padded_batch_dataset( - batch_size=1, padded_shapes=[2])) - iterator = dataset.make_one_shot_iterator() - _, value = iterator.get_next() - with self.cached_session() as sess: - with self.assertRaisesOpError( - r"Incompatible input shapes at component 0 between " - r"input dataset this dataset: \[3\] vs. \[2\]"): - sess.run(value) - - def testEnqueueWithIncompatibleInputsFailsWithInformativeError(self): - dataset = dataset_ops.Dataset.from_tensor_slices([0]).repeat(None) - dataset = dataset.apply( - tqd.prepend_from_queue_and_padded_batch_dataset(batch_size=1)) - iterator = dataset.make_one_shot_iterator() - queue_handle, value = iterator.get_next() - - enqueue_bad_structure = tqd.enqueue_in_queue_dataset( - queue_handle, (value, value)) - enqueue_bad_dtype = tqd.enqueue_in_queue_dataset(queue_handle, - np.array( - [1.0], - dtype=np.float32)) - enqueue_bad_shape_no_batch_dim = tqd.enqueue_in_queue_dataset( - queue_handle, ([1],)) - enqueue_bad_shape = tqd.enqueue_in_queue_dataset(queue_handle, - np.array( - [[1]], dtype=np.int32)) - - with self.cached_session() as sess: - with self.assertRaisesOpError( - "mismatched number of tensors. Queue expects 1 tensors but " - "tried to insert 2"): - sess.run(enqueue_bad_structure) - with self.assertRaisesOpError(r"Expected component 0 to have batched " - r"shape \[1,...\], but saw shape: \[\]"): - sess.run(enqueue_bad_shape_no_batch_dim) - with self.assertRaisesOpError( - r"mismatched shapes at component 0. Attempted to insert tensor " - r"with shape \[1\] but queue expected shape: \[\]"): - sess.run(enqueue_bad_shape) - with self.assertRaisesOpError( - r"mismatched dtypes at component 0. Attempted to insert tensor " - r"of type float but queue expected type: int32"): - sess.run(enqueue_bad_dtype) - - def testEnqueueWithPaddedBatchFailsWithInformativeError(self): - dataset = dataset_ops.Dataset.from_tensor_slices([0, 1, 2]) - dataset = dataset.apply( - tqd.prepend_from_queue_and_padded_batch_dataset(batch_size=1)) - with self.assertRaisesRegexp( - TypeError, r"Unable to create padding for field of type 'variant'"): - dataset.padded_batch(batch_size=10, padded_shapes=[1]) - - def testOneEnqueueWithPadding(self): - dataset = dataset_ops.Dataset.from_tensor_slices([0, 2, 4, 6]) - # Make a dataset of variable-length vectors and their lengths. - dataset = dataset.map( - lambda c: (c, c * array_ops.ones((c,), dtype=c.dtype))) - # Emit a queue we can prepend to, and counts/values as padded - # batch. - dataset = dataset.apply( - tqd.prepend_from_queue_and_padded_batch_dataset(batch_size=3)) - - iterator = dataset.make_one_shot_iterator() - queue, (count, padded_value) = iterator.get_next() - - # Split the padded_value into two pieces: head and rest - rest_indices = array_ops.squeeze(array_ops.where(count > 2), axis=1) - bound = math_ops.minimum(2, math_ops.reduce_max(count)) - value_head = padded_value[:, :bound] - count_rest = array_ops.gather(count - 2, rest_indices) - value_rest = array_ops.gather(padded_value, rest_indices)[:, bound:] - queue_rest = array_ops.gather(queue, rest_indices) - enqueue_rest_op = tqd.enqueue_in_queue_dataset(queue_rest, - (count_rest, value_rest)) - with ops.control_dependencies([enqueue_rest_op]): - calc = array_ops.identity(value_head) - - with self.cached_session() as sess: - self.assertAllEqual([[0, 0], [2, 2], [4, 4]], sess.run(calc)) - self.assertAllEqual([[4, 4], [6, 6]], sess.run(calc)) - self.assertAllEqual([[6, 6]], sess.run(calc)) - self.assertAllEqual([[6, 6]], sess.run(calc)) - # Get some final batches due to prefetching. - for _ in range(3): - try: - self.assertAllEqual( - np.empty(shape=(0, 0), dtype=np.int32), sess.run(calc)) - except errors.OutOfRangeError as e: - self.assertTrue(str(e).startswith("End of sequence")) - - def testNonstandardPadding(self): - dataset = dataset_ops.Dataset.from_tensor_slices([0, 2, 4, 6]) - # Make a dataset of variable-length vectors and their lengths. - dataset = dataset.map( - lambda c: (c, c * array_ops.ones((c,), dtype=c.dtype))) - # Emit a queue we can prepend to, and counts/values as padded - # batch. - dataset = dataset.apply( - tqd.prepend_from_queue_and_padded_batch_dataset( - batch_size=3, padding_values=( - 0, - -1, - ))) - - iterator = dataset.make_one_shot_iterator() - _, (unused_count, padded_value) = iterator.get_next() - - with self.cached_session() as sess: - self.assertAllEqual([[-1, -1, -1, -1], [2, 2, -1, -1], [4, 4, 4, 4]], - sess.run(padded_value)) - self.assertAllEqual([[6] * 6], sess.run(padded_value)) - with self.assertRaisesOpError("End of sequence"): - sess.run(padded_value) - - -# TODO(ebrevdo): Figure out how to use run_core_tests to test state -# saving of an iterator that's had some tensors enqueued into its queue. -class PrependFromQueueAndPaddedBatchDatasetSerializationTest( - dataset_serialization_test_base.DatasetSerializationTestBase): - - def testPrependFromQueueAndPaddedBatch(self): - - def build_dataset(seq_lens): - return dataset_ops.Dataset.from_tensor_slices(seq_lens).map( - lambda x: array_ops.fill([x], x)).apply( - tqd.prepend_from_queue_and_padded_batch_dataset(batch_size=4)) - - seq_lens1 = np.random.randint(1, 20, size=(32,)).astype(np.int32) - seq_lens2 = np.random.randint(21, 40, size=(32,)).astype(np.int32) - self.run_core_tests(lambda: build_dataset(seq_lens1), - lambda: build_dataset(seq_lens2), 8) - - def testPrependFromQueueAndPaddedBatchNonDefaultPadding(self): - - def build_dataset(seq_lens): - - def fill_tuple(x): - filled = array_ops.fill([x], x) - return (filled, string_ops.as_string(filled)) - - padded_shape = [-1] - return dataset_ops.Dataset.from_tensor_slices(seq_lens).map( - fill_tuple).apply( - tqd.prepend_from_queue_and_padded_batch_dataset( - batch_size=4, - padded_shapes=(padded_shape, padded_shape), - padding_values=(-1, ""))) - - seq_lens1 = np.random.randint(1, 20, size=(32,)).astype(np.int32) - seq_lens2 = np.random.randint(21, 40, size=(32,)).astype(np.int32) - self.run_core_tests(lambda: build_dataset(seq_lens1), - lambda: build_dataset(seq_lens2), 8) - - -if __name__ == "__main__": - test.main() diff --git a/tensorflow/contrib/verbs/rdma.cc b/tensorflow/contrib/verbs/rdma.cc index f7c979e863..9db80f6b57 100644 --- a/tensorflow/contrib/verbs/rdma.cc +++ b/tensorflow/contrib/verbs/rdma.cc @@ -30,7 +30,6 @@ limitations under the License. #include "tensorflow/core/distributed_runtime/rendezvous_mgr_interface.h" #include "tensorflow/core/distributed_runtime/rpc/grpc_util.h" #include "tensorflow/core/distributed_runtime/session_mgr.h" -#include "tensorflow/core/distributed_runtime/rpc/grpc_util.h" #include "tensorflow/core/framework/rendezvous.h" #include "tensorflow/core/framework/tensor.h" #include "tensorflow/core/lib/core/status.h" @@ -1028,7 +1027,10 @@ Status RdmaTensorResponse::PrepareRecvTensor( return errors::Aborted( "RecvTensor expects a different device incarnation: ", parsed.src_incarnation, " vs. ", (*src_dev)->attributes().incarnation(), - ". Your worker job was probably restarted. Check your " + ". Your worker job (\"", + channel_->adapter_->worker_env_->session_mgr->LegacySession() + ->worker_name, + "\") was probably restarted. Check your " "worker job for the reason why it was restarted."); } diff --git a/tensorflow/core/BUILD b/tensorflow/core/BUILD index a701b38d4b..575edfe7a9 100644 --- a/tensorflow/core/BUILD +++ b/tensorflow/core/BUILD @@ -95,7 +95,8 @@ load("//tensorflow:tensorflow.bzl", "tf_cc_test_gpu") load("//tensorflow:tensorflow.bzl", "tf_cc_tests_gpu") load("//tensorflow:tensorflow.bzl", "tf_cuda_cc_test") load("//tensorflow:tensorflow.bzl", "tf_version_info_genrule") -load("//tensorflow:tensorflow.bzl", "if_not_tx2_llvm_or_windows_cuda") +load("//tensorflow:tensorflow.bzl", "if_nccl") +load("//tensorflow:tensorflow.bzl", "tensorflow_opensource_extra_deps") load("//tensorflow:tensorflow.bzl", "tf_cuda_only_cc_test") # For platform specific build config @@ -112,6 +113,7 @@ load( "tf_additional_device_tracer_test_flags", "tf_additional_gdr_lib_defines", "tf_additional_human_readable_json_deps", + "tf_additional_logger_deps", "tf_additional_lib_defines", "tf_additional_lib_deps", "tf_additional_lib_hdrs", @@ -300,6 +302,7 @@ filegroup( "platform/env_time.h", "platform/logging.h", "platform/macros.h", + "platform/platform_strings.h", "platform/types.h", ], visibility = ["//visibility:private"], @@ -442,6 +445,18 @@ cc_library( ] + tf_additional_human_readable_json_deps(), ) +cc_library( + name = "logger", + srcs = tf_platform_srcs(["logger.cc"]), + hdrs = ["platform/logger.h"] + tf_platform_hdrs(["logger.h"]), + copts = tf_copts(), + visibility = ["//visibility:public"], + deps = [ + ":lib", + ":lib_internal", + ] + tf_additional_logger_deps(), +) + filegroup( name = "platform_env_hdrs", srcs = [ @@ -519,6 +534,19 @@ cc_library( ], ) +cc_library( + name = "platform_strings", + srcs = tf_platform_srcs([ + "platform/platform_strings.cc", + "platform/platform_strings_computed.h", + ]), + hdrs = [ + "platform/platform_strings.h", + ], + visibility = ["//tensorflow/core:__subpackages__"], + deps = [":lib"], +) + filegroup( name = "platform_other_hdrs", srcs = [ @@ -841,6 +869,7 @@ tf_cuda_library( "framework/dataset_stateful_op_whitelist.h", "framework/device_base.h", "framework/function.h", + "framework/function_handle_cache.h", "framework/graph_def_util.h", "framework/graph_to_functiondef.h", "framework/kernel_def_builder.h", @@ -884,6 +913,7 @@ tf_cuda_library( "util/bcast.h", "util/cuda_kernel_helper.h", "util/device_name_utils.h", + "util/dump_graph.h", "util/events_writer.h", "util/example_proto_fast_parsing.h", "util/example_proto_helper.h", @@ -901,6 +931,7 @@ tf_cuda_library( "util/stream_executor_util.h", "util/strided_slice_op.h", "util/tensor_format.h", + "util/tensor_ops_util.h", "util/tensor_slice_reader.h", "util/tensor_slice_reader_cache.h", "util/tensor_slice_writer.h", @@ -1038,6 +1069,7 @@ tf_gen_op_libs( "batch_ops", "bitwise_ops", "boosted_trees_ops", + "tensor_forest_ops", "candidate_sampling_ops", "checkpoint_ops", "collective_ops", @@ -1085,7 +1117,11 @@ tf_gen_op_libs( op_lib_names = [ "string_ops", ], - deps = ["@com_google_absl//absl/strings"], + deps = [ + ":lib_internal", + ":lib_proto_parsing", + "@com_google_absl//absl/strings", + ], ) tf_gen_op_libs( @@ -1187,6 +1223,7 @@ cc_library( ":batch_ops_op_lib", ":bitwise_ops_op_lib", ":boosted_trees_ops_op_lib", + ":tensor_forest_ops_op_lib", ":candidate_sampling_ops_op_lib", ":checkpoint_ops_op_lib", ":collective_ops_op_lib", @@ -1340,6 +1377,7 @@ cc_library( "//tensorflow/core/kernels:batch_kernels", "//tensorflow/core/kernels:bincount_op", "//tensorflow/core/kernels:boosted_trees_ops", + "//tensorflow/core/kernels:tensor_forest_ops", "//tensorflow/core/kernels:candidate_sampler_ops", "//tensorflow/core/kernels:checkpoint_ops", "//tensorflow/core/kernels:collective_ops", @@ -1386,9 +1424,7 @@ cc_library( "//tensorflow/core/kernels:summary_kernels", "//tensorflow/core/kernels:training_ops", "//tensorflow/core/kernels:word2vec_kernels", - ] + tf_additional_cloud_kernel_deps() + if_not_tx2_llvm_or_windows_cuda([ - "//tensorflow/core/kernels:nccl_kernels", - ]) + if_not_windows([ + ] + tf_additional_cloud_kernel_deps() + if_not_windows([ "//tensorflow/core/kernels:fact_op", "//tensorflow/core/kernels:array_not_windows", "//tensorflow/core/kernels:math_not_windows", @@ -1413,6 +1449,8 @@ cc_library( ]) + if_cuda([ "//tensorflow/core/grappler/optimizers:gpu_swapping_kernels", "//tensorflow/core/grappler/optimizers:gpu_swapping_ops", + ]) + if_nccl([ + "//tensorflow/core/kernels:nccl_kernels", ]), ) @@ -1437,7 +1475,7 @@ tf_cuda_library( ":gpu_runtime", ":lib", ":ops", - ], + ] + tensorflow_opensource_extra_deps(), ) cc_library( @@ -1577,6 +1615,8 @@ filegroup( "util/stats_calculator.*", "util/reporter.*", "platform/**/cuda_libdevice_path.*", + "platform/**/logger.cc", + "platform/**/logger.h", "platform/default/test_benchmark.*", "platform/cuda.h", "platform/google/**/*", @@ -1671,8 +1711,8 @@ cc_library( cc_library( name = "mobile_additional_lib_deps", deps = tf_additional_lib_deps() + [ + "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/strings", - "@com_google_absl//absl/time", ], ) @@ -1763,7 +1803,7 @@ cc_library( # registration of ops to prune code size. cc_library( name = "android_tensorflow_lib_selective_registration", - srcs = if_android(["//tensorflow/core:android_srcs"]), + srcs = if_android(["//tensorflow/core:android_srcs_only_runtime"]), copts = tf_copts(android_optimization_level_override = None) + [ "-DSUPPORT_SELECTIVE_REGISTRATION", ], @@ -1775,9 +1815,7 @@ cc_library( visibility = ["//visibility:public"], deps = [ ":protos_all_cc_impl", - "//third_party/eigen3", - "@double_conversion//:double-conversion", - "@nsync//:nsync_cpp", + "@com_google_absl//absl/container:flat_hash_set", "@protobuf_archive//:protobuf", ], alwayslink = 1, @@ -1787,7 +1825,7 @@ cc_library( # no proto_rtti. cc_library( name = "android_tensorflow_lib_selective_registration_nortti", - srcs = if_android(["//tensorflow/core:android_srcs"]), + srcs = if_android(["//tensorflow/core:android_srcs_only_runtime"]), copts = tf_copts(android_optimization_level_override = None) + tf_opts_nortti_if_android() + [ "-DSUPPORT_SELECTIVE_REGISTRATION", ], @@ -1799,9 +1837,7 @@ cc_library( visibility = ["//visibility:public"], deps = [ ":protos_all_cc_impl", - "//third_party/eigen3", - "@double_conversion//:double-conversion", - "@nsync//:nsync_cpp", + "@com_google_absl//absl/container:flat_hash_set", "@protobuf_archive//:protobuf", ], alwayslink = 1, @@ -2045,9 +2081,7 @@ tf_proto_library_cc( srcs = ["protobuf/master.proto"], cc_api_version = 2, protodeps = tf_additional_all_protos(), - visibility = [ - "//tensorflow:internal", - ], + visibility = ["//tensorflow:internal"], ) tf_proto_library_cc( @@ -2187,6 +2221,7 @@ cc_library( "platform/**/env_time.cc", "platform/**/cuda_libdevice_path.cc", "platform/**/device_tracer.cc", + "platform/**/logger.cc", "platform/**/logging.cc", "platform/**/human_readable_json.cc", "platform/abi.cc", @@ -2199,6 +2234,7 @@ cc_library( "platform/**/stream_executor.h", "platform/**/env_time.cc", "platform/**/device_tracer.cc", + "platform/**/logger.cc", "platform/**/logging.cc", "platform/**/human_readable_json.cc", "platform/abi.cc", @@ -2641,6 +2677,8 @@ tf_cuda_library( ":stats_calculator_portable", ":version_lib", "@com_google_absl//absl/base", + "@com_google_absl//absl/container:flat_hash_set", + "@com_google_absl//absl/strings", "//tensorflow/core/platform/default/build_config:platformlib", "//tensorflow/core/kernels:bounds_check", "//third_party/eigen3", @@ -2943,6 +2981,7 @@ tf_cuda_library( ":lib_internal", ":proto_text", ":protos_all_cc", + "@com_google_absl//absl/memory", "//third_party/eigen3", "//tensorflow/core/grappler:grappler_item", ] + mkl_deps(), @@ -3008,7 +3047,6 @@ tf_cuda_library( hdrs = ["common_runtime/metrics.h"], deps = [ ":lib", - "@com_google_absl//absl/time", ], ) @@ -3033,7 +3071,6 @@ tf_cuda_library( ":protos_all_cc", "//tensorflow/core/debug:debug_graph_utils", "//tensorflow/core/kernels:function_ops", - "@com_google_absl//absl/time", ], alwayslink = 1, ) @@ -3393,6 +3430,7 @@ tf_cc_tests( "platform/profile_utils/cpu_utils_test.cc", "platform/stacktrace_handler_test.cc", "platform/subprocess_test.cc", + "platform/vmodule_benchmark_test.cc", ], deps = [ ":lib", @@ -3406,6 +3444,20 @@ tf_cc_tests( ], ) +tf_cc_test( + name = "vmodule_test", + srcs = ["platform/vmodule_test.cc"], + tags = ["optonly"], + deps = [ + ":lib", + ":lib_internal", + ":lib_test_internal", + ":protos_all_cc", + ":test", + "//third_party/eigen3", + ], +) + tf_cc_test( name = "lib_random_random_distributions_test", srcs = ["lib/random/random_distributions_test.cc"], @@ -3421,6 +3473,16 @@ tf_cc_test( ], ) +tf_cc_test( + name = "platform_strings_test", + size = "small", + srcs = ["platform/platform_strings_test.cc"], + deps = [ + ":lib", + ":platform_strings", + ], +) + tf_cc_test( name = "platform_env_test", size = "small", @@ -3668,6 +3730,7 @@ tf_cc_tests( "util/bcast_test.cc", "util/command_line_flags_test.cc", "util/device_name_utils_test.cc", + "util/dump_graph_test.cc", "util/equal_graph_def_test.cc", "util/events_writer_test.cc", "util/example_proto_fast_parsing_test.cc", @@ -3798,6 +3861,7 @@ tf_cc_tests_gpu( ":test", ":test_main", ":testlib", + "@com_google_absl//absl/memory", ], ) @@ -3826,6 +3890,7 @@ tf_cc_tests_gpu( ":test", ":test_main", ":testlib", + "@com_google_absl//absl/memory", ], ) @@ -4099,6 +4164,7 @@ tf_cc_test( "//tensorflow/core/kernels:identity_op", "//tensorflow/core/kernels:immutable_constant_op", "//tensorflow/core/kernels:matmul_op", + "//tensorflow/core/kernels:topk_op", "//third_party/eigen3", ], ) @@ -4392,6 +4458,7 @@ tf_cc_test( "//tensorflow/core/kernels:random_ops", "//tensorflow/core/kernels:shape_ops", "//third_party/eigen3", + "@com_google_absl//absl/memory", "@com_google_absl//absl/strings", ], ) @@ -4871,6 +4938,7 @@ transitive_hdrs( "//tensorflow/core:core_cpu", "//tensorflow/core:framework", "//tensorflow/core:lib", + "//tensorflow/core:platform_strings", "//tensorflow/core:protos_all_cc", "//tensorflow/core:stream_executor", ], diff --git a/tensorflow/core/api_def/api_test.cc b/tensorflow/core/api_def/api_test.cc index 6f98856915..d38a8424eb 100644 --- a/tensorflow/core/api_def/api_test.cc +++ b/tensorflow/core/api_def/api_test.cc @@ -182,11 +182,14 @@ void TestDeprecationVersionSetCorrectly( for (const auto& name_and_api_def : api_defs_map) { const auto& name = name_and_api_def.first; const auto& api_def = name_and_api_def.second; - ASSERT_TRUE(api_def.deprecation_version() == 0 || - api_def.deprecation_message().empty()) - << "ApiDef that includes deprecation_version > 0 must also specify " - << "a deprecation_message. Op " << name - << " has deprecation_version > 0 but deprecation_message is not set."; + if (api_def.deprecation_version() != 0) { + ASSERT_TRUE(api_def.deprecation_version() > 0) + << "Found ApiDef with negative deprecation_version"; + ASSERT_FALSE(api_def.deprecation_message().empty()) + << "ApiDef that includes deprecation_version > 0 must also specify " + << "a deprecation_message. Op " << name + << " has deprecation_version > 0 but deprecation_message is not set."; + } } } } // namespace diff --git a/tensorflow/core/api_def/base_api/api_def_BatchDataset.pbtxt b/tensorflow/core/api_def/base_api/api_def_BatchDataset.pbtxt index 639d962874..32def912f8 100644 --- a/tensorflow/core/api_def/base_api/api_def_BatchDataset.pbtxt +++ b/tensorflow/core/api_def/base_api/api_def_BatchDataset.pbtxt @@ -1,5 +1,6 @@ op { graph_op_name: "BatchDataset" + visibility: HIDDEN in_arg { name: "batch_size" description: <

p|CUUfqAK?1t+tjXQexoFAw@s(GyY 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 Z|9tcF@Xt>_SN+oZ4Otty=hvH`O#nK_LPY=o literal 0 HcmV?d00001 diff --git a/tensorflow/core/kernels/fuzzing/corpus/decode_wav/4e7cbb27667bcfca92838aa8020749990013a9b1 b/tensorflow/core/kernels/fuzzing/corpus/decode_wav/4e7cbb27667bcfca92838aa8020749990013a9b1 new file mode 100644 index 0000000000000000000000000000000000000000..898584d96f6bfcd76ebed9dcde3b7df88442af78 GIT binary patch literal 66423 zcmWIYbaQJ+V`K<-40BD(Em06)U|?WmU}SJv!@$rH!N|bG01{z<0&gh&4@$$Rp#T5> z!`P$rXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S1J zhQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kin zXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S1JhQMeD zjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kinpbY`G zLF=+nhmD57Xb6mkz-S1N83K-E>KoNI8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71* zAut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@? z8UmvsFd71*Aut*OqaiRF0;3^7-w^mu381f2M{OGofzc2c4S~@R7!85Z5Eu=C(GVC7 zfzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c z4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7_=eKFlb#i>afue7!85Z5Eu=C(GVC7fzc2c z4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C z(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7{nnkx)65|N6x6zMnhmU1V%$( yGz3ONU^E0qLtr!nMnhmU1V%$(Gz3OY2>cy6ULE!6Xb6mg(GVC7fzc2kHv|Buph`Ue literal 0 HcmV?d00001 diff --git a/tensorflow/core/kernels/fuzzing/corpus/decode_wav/585e469231d202812bfba8285fb30c8e31c857b9 b/tensorflow/core/kernels/fuzzing/corpus/decode_wav/585e469231d202812bfba8285fb30c8e31c857b9 new file mode 100644 index 0000000000000000000000000000000000000000..a4994c208300b6cba20708367337667fbbd8dabc GIT binary patch literal 70833 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 a^1bW(=WkKp9KW@FJN=E}yZQIV?~4Iw^;b^- literal 0 HcmV?d00001 diff --git a/tensorflow/core/kernels/fuzzing/corpus/decode_wav/6874d5b1c7a64b596c61f24877d422e89bebe58b b/tensorflow/core/kernels/fuzzing/corpus/decode_wav/6874d5b1c7a64b596c61f24877d422e89bebe58b new file mode 100644 index 0000000000000000000000000000000000000000..e054ad5f14723fa1bd5829725e38de4a681cb3e8 GIT binary patch literal 25086 zcmWIYbaQJ+V`K<-40BD(Em06)U|?WmU}SJv!@$rH!N|bGAi$84Sdti%#>nvK>-x{8 z->m)?{4e^=_jBtX4Q40y*Bqvt(d;>Fh3rynn#|rzj4Xj16S(Ja1#z9{>E>(XbK-5~ zeJqe5n8YW>SId*m7S1BeQN(qY^B4PV)?2J=*dMcBWOHJ#=Ul|IgD-%0E;j?;OMx(f zL_T$1CEhsRi9DZqr1;mU z=Pck-=la2!$9a{*hifBu0nZG+dwe!LE!^e2mHZ3&E%^HQI0d-*_w!EVtKdJuXU5yZ z>(3w0_mbxvUx~mXfvZBs;wm!gay60~LPdOAd98WVc*Xh41zzzB@(1$=^RMS?=Tqg6 z5NH#;DDa*)h9`~Jmv>G2nsR;O-pRX;FNn{9r;y8s`wIV8 zkuRc|qKV>8Qu}1f6<*05mH8?YCv#V7y~G1iF3}JXHsSBWTq3?g0m92f|B0`c{w3oo zB`9`BaI1iVkhfqf{}=w{0;~ee{FVGK1$9N@L}kP~rF>-hr8C4Hi{uC=2viF+2+rcq z7Jrv01gy4Wi@e{&jeY-gRu`kYOM#p?gEf0OhoTXZ%m=-{Sur|5yB5^9JV=r#Ks(xel{>R6EACo@_e3<@m%jfA|0>0RM z4f(O=cjKR$-;IBI|4sR~|9>viea3J9UH@nNV_>XcTg3K|`5KEETP|A$dm_6edlCCm zwlFqtwtSXI7EQJdtO~4aS$$YGGu>yJ$GnoYn)5x+Ufv_z+c`Bj*0VM{0V0=|{W|MIUlKs(s}CSo7(L=S?qNUtD`C@T}^2$up%V zHy&?$TJgN#`IBddU+#G?{MqdDw=dhi>->oM?)h!;mx#}%pXYwL^R4Oo^zVkh;{LTU zEn{ov@M3RalV(59x{_%-<8|hPtTn90ETt^Q?4n#++&j4?dA0eu1YZiU^Bv&&%jv+w z!GD2Ynct9a3NI`FRY4V@EdG98Ile!<#@z2Yd^nZ38oADKP2;-G>Bv#ZmdobA9?B8U zy_GkOcLDEB-Yo7J9B0^sS)*BYvv9KNu`;s0WSP&}!`{jz&9j&5D~BA17yEYhnVjxi z8#qu{k-Hk(t%Y|n??^j-4z7BpFK{J6| z{?md%B432rg!%>41Of%P1wQlD@|tq@vfHsTaOm@06WS`gTHptF4VNKLCbt25J98Lg z07Dj24%<48@0@`=i}+Lp(}ZsbI`BQ=&F9w^C=pyQBre1z=py)0Xt&5*(c5BkBrZuN zOTU*%kO`G8l71y+CwWKwuh>hmx#AzhFG%c{E|ImAR*;mG{2^{EK1pJ`c$0{saJg`+ z$Q03N5e?yJk#NxjvCR@QB+VrKBo|0*6k`^x68S7#Bshg{7q32V2G1Uzd|p95J3emS zMLd&unR%yj2k;2+<@2&|t8>lcdd4No^MXg7TbHww<2Sn~`&*WijEetG|LFUA<-6jq z6+abzn*Uh&eaerSKT3b({G9!x`P=KSlfG7bVgI7|#s2fNZn^vL^R;DhA*8y?(weC_$n*Eiqvy{UhD^DXb|InNoNKYGsi zoaw3LlNFEUA1{2Y^YrAie=nB4zVznq8`HP4Z!%wrym|fZ|GT;G?B26|;P|-v1J4KR zPcmPZeyjZP?8lSuB|jVgB>k=X0u8CEg{u?Dj5V%x#W#D0){Hd`@U99t>t8@59ny6n8HcbV&% z_?UOIc(OcTdd)b4QIN6aFZ0hGKX(5L{XO^B^B-+LT7P~0_4mh{?+3qM{I>k-<1aTp zulf}E>GVha4|m=teE9P2{F?=@zrV70{q1$n8@;zH-^jjx^U~sl>$Bfa@}6vYs`O0$ zvC^Zer;aZsz2bPA`zGl1(>I*&uDs2AXZqgy{hN0o?{>et_wnY}-QVSYe)_%k-&zI@ zCS#^`jQbcQ{#X6aWAtQfU=U_}&dASvfw_hGBy&5d7X9Dx zPv*bP{|kT1{`~$oheeY;hwT{K4K_p8#mpv*ul`p4Hv8G|UHRLx&jO!?z8ZXW{=)tB z)0YLGQa*fnfAoXshe>bC-gLfcdTsM+;mg#Qp)dJf%zJwA$+f4up1M9+_UPFI&j-d2 ztsh1`sCdxuVE6r%_uC(Ac=-6yp~pc_YM-oqqWEm#^Bd2DUR-$T^7`~^w%7MwO?)f& zG5z!XFOpv$e17+-?Nil9lMg;06hA%tT>C}stMB)MU#5Te{Hy!_grS#tBI`z04>k^t z^X!*dcd;a~>|_yTOJzUDd4o4vfI(1ENLF~gP@rHT{~_Ka-n)DY1hxnS3d|L}BFrVa zP2{L>hp>R~cY(XS`?<Q9__DuXJ;t(#rH*A0b0dpA$5U=W-ap(ST-}^%oJt%; zY`a*pSRS)vu|%*eV12=6!I8z;!`a1I!0F7P$ezR&%=(Z;n$?-Lh&7*eDeDII)m-Pf zS987Q{J<5J*6 zDtxzjTDZzNjX1hlHCakoy*XZT^>J5nWpUo%VCKkUb6|;Py2mKXoXYZ-IgI%z%K`S^ zoYS~CxDIn1Vt3(~!&$`T#m&HblJ7KsrocBrUy*a7DPpqXr^Fp3rb_fnbV|5NtQ6Om zxGVWYDo0vf=9G+!Y@ckeY_RMfnR_xyGG8TwC4PvX5;GFj5ZNpwD5M~KNZ4OwvDi1s z3o_ee4@xUZP8F9IlN3o6dM@}^uv$<2eKnGQ2HGyP>=$g+S%m^G3`kcpRJ!QaMTzCQzh-u~YAt?m1ZANzi^e&6#= z{=58-??2pr{{2z*)BgAQ-&=oP{&wQa(a)bhv3zWPzv9ERPm?~~_)z{%;f?RB%P*{- z?|9PtaLxV1`{MV-@B2Iuel+3n=f{SRCp^l0-;tsL?k5$u=PirKEP7PA$z7qER`S;;b=me0&$EHcc& zjI;m$`_uDF{`c`e$$xhKzWqD&x5IDMKhyuj{XOt+=RdZ;3cug}T=7%)XXW>@uP;7d z{KWRj?_z-#kU-g{%Wznlyuhzb_ zcq#pg@y)llBJbTleEQh&srl2vPf4FPd^-1~_8aeawI8lOtA3vT!TiJFhxN~^zn=U( z%V5gX$1KJw&eq6g!m7%ApUH{2g(-(|JHrfye8xm3KBglK9t;%>4gW*`o&J}|AkMsi z^(0Fc(+5U##y9^8|MvVb|9$({!ry9t?*7sHcj+I;Kf^!OKevCo@P*~`vk!mYZG0pD zYSN3X&n2GQK411+=|$6vRnIM+u|ECvXzl~4`}TJi-2Qmm_ipsPoA;jIi@YEHK>6Xl zhesdrJ<)kq`a<@l+>71M{a%K?a((&nh0v?%uWVljy?ydt^RxE%y+2uhG5?JFzUHgj z7v0aPpKg8R{AB+*_)EgqiQm0{R{v!E#rZq#_ufCJ{tGjGWqQc0!K%l0i>-#;jU$ay zncIbTADfb*c-7q;`_w1L=TJH z67CXuE?~>o&HaiqhC`NpDmxpe9#<5%4bKklU@jrfAMCE|dF!_ULVvxJ9__YF@qk2}v=9$#J_zIy(8L3v?85q*&i;R!+q1f>L4^J;NB zaPHxl#W{)VGFJw-J?|a9BYXmU^LSVDtmS6lZsX+Qkl|?Mc*`!xK9?<*Z3|lly90*< zXD*ivcO_RFhdG-x%LT@w|HuDc{A2z{_s`Nll7IjH{r`U}(?J#w);}zXtPyM{*$%QD zU@KzdXPd@$pWTbonEM#-D}F}74#5+G?*v1HHVIu5;u9_wUM6y1tXJZ@M3N-4)D=l? zNkfTvabxj5aX;}%VvS-?#R|nVL|+IC2qy_W6VwsfDC{A2T7p~ZzNCa?oWy7GSKs6OAvo8?j=z!u~}k{M1y#~Xp8V{Augf&0?qv0e8+hMc-Qdca?jR+88t$SQt_mycxd!?`OEdkoDi?-?@Kw{}=q*@%O+Vxj#;S za{es-E%R&n56K^eKd%1Z_?h>!{b%(Lk8f6=553oa+xB|W%MH($Jqvg`;W7W?d5;;N z#6S7{#O~?RCoPZHJZgP(>CwkWL5~_9PJgKU$o0|pN4uZQe5UfE=4JaU&)3^uuX}Cv zI{8)m%LOkPUq5;?`CZ|MH=llenf-O|SI2LAzD@bA@-y()m0t^gul~dSck3VbKQezT z{&fBk{VV$K{QujGj?5>SWm%T6__A(f^<>pzdCQc*^n>XOvj@v}mN@ovoR(Z=oYy(R zI9xc6aFlZ>vn#SqU_H)az#_o%ky(~y2Fqj?3l=?=f6PtH7RJ}ron$Ix zddPT`v4l~WVc)-l|IGhC{Kx#y>Tk>+o!?zQtG-|R8uvBfYt&bcZvo#+esukm|Ml>P z+qcJ`cYXAEAMm#DjqRI;H;doSeSiMr{m->uwZCot+WDpL^X$+0U)a9B|FZ1MtS<|` zn0&eUdDoZxZyUcK`4RqW<8SjnHGdxdk^1}iPud@gKVSaD{#*I~2g5_ALoCNx)!C}p zPO*JqTgMj3%D^nf=*>{^-{T+uzdiq+{G0J_{XdugeheLqY)svZcNm-)a{t%9_BtY ze#rB%;X%NC`FqZHE$-a7_2A~an>@EK-C1;x;Q{}{#7Di4XFQg8Jmc}4Cug4YKXH1x z_nE*8o0opCj=%c+s{Hl;*L`p7-);S%`pNdw<&P2{g+4gGZ+(~gw&Bf=Hz(ivyf6JI z^u_R7?stnH$A3KdarlSzPxoJXzwiIv@~7l4-#@qiM;NEDRIn{#Kh4q1S;LvmnZn7> zd6VM}#}STm9G^Kuxc75w@bvJ?@W0_-E3j5@nNWf7eqlA?JfRk$>q2ruwgQEGvw6*V zxANTLN#`>W*e<{=AjN-#cLjF?X9R~UM=M7u$90ZnT$;Rl`KI#U5_l)bB&00lEj&v^ zQPf#9MKo4aO7x=02a!HePqDLN9^!|@jU@UcR!a0r%#tvbh!^`Kyiq7pNK!~bC_!k4 zP?Yd#;oCx{f-M5x0)qTYcy)Qd^9b|oSSPXGVx7e5$g05F$9j^jlEZ@Q zI=3kACEiHBYkV5~F8oUTANelw-Q@G(za}6dyjG+^bg$SAaZ~XI(d8mDM50BQMQ#h9 z5&kJGE>bDHTxhFMp|HM)hG>@ZpWdy_*U@FFY9JW2I?5x+A zotT6e*D)+)NM|r%&}GnOFkoapPs2=ua_pg_{75{MO zqu^($uVvqF{y6bd?04Cpi+?Tu^E31_Y+}5?^qEPKxr4=yEs9-+V=)I0XB+2wPBBhl z4mS3$tahxwSnjhtVF_W?Wy@qg&M}4aJLe)U9qx-7wezyG@_bvCE?6>r;ErixXob5u$Cc^Nt}5d)ri4}Nw3di~4t&;7s8|5^VR|8Mbc z_aCX>Hb3pZ@A*3Ki`Hkek4*3Hyb*c5;HArpkmnZ9Q=e--?|ruA>9i+-kC`8Bd{BNr z;9lq5hj(r6+dNqR;O)bgkK>=}JY#(R?)jz{A6{6zPzAL+vM{&uyQxKJ$Hk^r`#Pua8eY&i%CRbJv&qUqrrs_{#m=^!u)FhrjuJ zZ}`sko#&hCSBtL>-zvXf|8eA()Zg&`B8)E?9hn|7U0|NYBFdV=`i(V{J&yAOmov{6 z-e-Id`7a4@3N9DO5)2nE7YP(m7D*DBCZZzJEwoVJHvd`vDE{|+TKsGHvji9fKk*mx zPT@YpwV87vM=6ICXAf5|k20SWf0DotL22O%VQb<0LRP{bg>0Y<4bo2KEQ+^Ei%j%;8AmxWU24#m?Qs-Nf^a*Nk6KV4}ccfs+Cbg5`oj zf|&x$0#yQ=1ZN066G{{I6>%075t}6@EWS^|L%K$WL*|0Cn#?Qdzfwmf-$?XJ`bwL~ zILQdeI?08|RmvL3lt?K^Zj{iIh!&R??+}-lI4z+i$tbBLc~(+DYL29cj@}0+9 z_BRV(RlnT#;>wGLmz!Q1zuf&I@WuM)`<^{~V(>`if!BTCdwcIR-!{0t_V%_r^Y41x zn|OcI!`jELPgXyPecJx)`E#C^4`1$l_2KpPx4Iu5eSG<;=gaqRaz9V}s`}IVPnvNb z^E;L;EN@uOv9hqWu|~1JWvyVZ=d9w&;9}-B=TYN1$feDBjC~0k8{1;mC9M0|mU3ip z9pIkD^OI)-?-#x+{N4iLf?I_0ggb?mgiVFY1*-&S3swsD2{H*)3QZKcBUmG#&i|D+ zgQt>f0f!vB5L-Q~IqN4DQodmr%b;hUeY7rjn={q+^+tCKJOK0o%n`uWLc-=D@jO?-Oi>BXmBPfeaqcsk{& z!qc)R_D}kr?s|Ul#rhY|UQB=a?IrW8?=OR2slK`V&h~@fhyNeueB%7F_KU$6$-{yW@_oeWQ_1BJXem^Gu zdhy5S|6E3S=EKbHEH7B{SpTvvW%FhC=D5Rohx;P06n`JTf&i1?D}i=TQEt&x5hvjwAxR-I zp*ezA1^fgi2;>MJ78DV*6}Z5!$bX$Tf~THail>mblb465munVhA;&8=JJ!FD5^pEZZJt@Y@_cc8WqgbH?(s$OJ>hBRR^+zj&g7oQ&BpVBC!bGDz+A9butD&T z;BTP<;acINBBA2(k}6VOQmIn=r8K3FO1+fSm0BfzPexakQ}&?D6B!TLCYd=>6C}4v z_DQl!>Pj+5NlR-79<6xLdq4l)-aA3JH{H^_rFe7W^`L7vukOEAcEk0S#vQ$T&JR={)jZaH^5_Zc zv$W?oUR1yGf7A9Z`(w$Mjo-d~zwzVr&kMi0ep~$c|Mv&O1m-I&vskyWy<*?Pah>BZ z#~#i-+_Jo^ytOasFm!<%r@~!m*OGo_iv18o!@_j(~?iuHYV_GT}eM z_e8#m-V{40_CQob^s9)FD6go2=uXk;VoBne;<2U1r=GElQ;_>ZI?KYvX9`RwQJpMgIee_s2!``3iu4S!_*sWG%LE@#wWJj5`UVHU$$ z21&+Nrd2HZZ1rp_*;Lts+5fT4WNTpC&Bnl9#U{;qd$+fKlXaU_e|yGnb$&ZbKYjW z&3HTIZSvcFZ}z?U{Wj&j+=ti?TR-%CT>0twr+FU*K9s!=elPdl`@Q%FrH_oCls>t9 zWd7jr-t+y__ftMFer)&{_UYlLHJ@reNqn~aQvLPTx7r_Rzsmo(|4aSP!!VOUoN+p1 z3{wPi9?L$~PWCX)M6T&v^SEwvMRLpXNb`pBDGGQC9S~*^5fE7|VlG-N`d;*&*gf&- z5_=@VB@`s0B_t%KiY1A}3LOyG!r#a*ETAA5CS)uuCoCiMULaXuqdmZ z^&)K|3L?LSGes;!---5!nTp>KSCP0O{#b0bXurrF;b%gLLd%7MgpUZ{5pEU^5}G04 z&7Z(`ori_nhO31um`j#(H~V=uOSVm{Y^((=D_F`|PqR9+u3|}N$zyS4v1ATmeEa{x z|9XagMmy#L)=KtE9MYU&97gQrY|GdVvcKVQ;uPR~%TdNr$Ucd!n(YtUclI#OGhCKD zUAzH&nS4%sLVO!}9e8DUWqFx+C3r9Jp5}|-FXms(&m~|bkS!o6Xd$#$SW2`|^twow zh@|Kl(R8tN@%<79BqgNIOIAvCXxAHF&KmK~>^w#{%hS#rOuX-c$w&~5IS6g0ecoz6{-4l-|`j7P=8r?s0 zx9yI_?b2HlZx!F(f9KoXzxNy-tbMrRQR3sACo`X}e)i&d$;y!2;*H14#9{P~={=vI1@A}?Fy%l=n`&#YwiPxTQ``_DsTJ(kS zoAtLXU!Q%^`ts>>_?MSozJ3Y*(*IfaGwbKepRRnG^jY@nuWyY%`hUj#lK;K=_xs<+ zf9L&n{oV6h{?C;^Q~nnJTloJGg9T$aL-K#U|KI+#|Kt5XfngfsGR9uUS&Zu#?=!w+ z{L6TOiIv5HC6u{^sf3Axc@}dpvmuiRV+=#a|J;8$|8o92Ge|LL{OAAo`p=QyKYs21 z)%0ub&!8W?-zR+i_WAfH!B5#ASA4kep5=YOyVSQDZ%)37eEIVE(`PZyo;>Y(did## zXZN0~zbt>b=f#)j)z6v+cey!plBS2N%KdDrlc`5@w3LKEgvs`H2&20srl2FkG3Co zeklHs`(eX}=#Pb;_IzgfYWpqsd;O1^pDw@d{VMux@JH!y&A(uV1g1z9CpJ!w*Bqxf zj&ihf`g8r`n$E+@KTY7Az!3p+!OMb4LYBh4A{Jsj;>*Q1i~SU}6T2z4O{`z^v2cUX z8o|#3&H^z4wSqf@WQF$$MG3hHEfCr+oGrRfj8Xit*gdfoVxD3NV!q-xC7PvPN`I4? zDSJcql5B=-xXceJR;hH!rxJ@Kx+GF07EAn)5SI8LDj{-L$VIS;e=*-yzG(hG{NeoG zya%}#aj)Sv<$liffNMLKH&+wqLJnJw5DqC0HuiE>C6-#|STlqNzW(qtKcpx}MxK|`mq)}v}sET;B#7)VK(oD9li>QBak?Aj>DKB3mc>MfS0rmcn5LHU&?)Tp3a6cqv0E7pX^*b0i*#sfeZs zZxRd?Xy&)%H{sXkf5qp^=gs?pyPeC2Q-ec{-HYuC>v>i#HWfBqwp2DLwuLMz%yCSu zjLi($42F!q8NHc~FfL(u`v2Jffd4W7rvBCWd-@OKpZ;I3e_Z&^{{6()J)es{PJ18p zF8%HIH(YN|yoq_O`SR7XiYH=^jy;(0!1Y1M{a1IT?s(o3zB%v4?i=AZg>P-Wm4938 zj_;kMI~(pe-*vsW?7rQ@(8o;APQIvro$${2W7p@8U#h-#eSP!w@wZFgSN|yd<@UGe zzdFNIhJ}nPnc|sESvpx`*rhmsb6Rs9F}$nzy!hYp z-Qc~<1wzS! zD+NyT=kTfUI`On~ujIPMaftOElN4j@f49FXe?tG{{z>}n_4D;N+piv9fjSSkU$(#4 z^djr!kC$Sv#ow-f_u`%3yPIzX-@SUb>wWcy=8ry~BtHdw%=?i1{@}aa@6zApzdiZp z$D2!U)89LP?E3WU)4@;2KE{2}dcW`;@B6>+^*@-tw|aNyZQ$F$H#1*Pe9iIZ#G974 zF7N8zYkpkx>D=ePU;Mvq`|kGh)2|1A*#28GHZy0k>adxyb+I|JKV{#@@rH9QcN?z^ z|6cyZ{Qvl~1;d0-i)<2g7n>?JTZ~KWqezsQR;&9Y#Djk6|(wr&T>~}b!0tdETs#j zyrn9nrc0fdvXyR<_LrV45H~D7r?-Ixm@)ThbQxI<#caxBk;*lwmT_Bete?dM^;j%)Jg1thc!dLlpxtlVa zGX65Xvh4CA3Wf@|uVueDevABG@=NPi?$2l6HNUO=^7~WpN4^h!?{2-8e>MGO(aWZnzAqm?uYG#^ z(b@Zl?}*>Ndh7Tt>svZEGp?sy<+_r1`TnKZm%d-RclqNL%WKWom)v-LGwt^3JBRN| z-Cy^h^wG>GYR~Vyw0gt$Zq@s1AO3wf`629M`zNU{2H%{2y!pA|cjVuwe;fY4VdP?2 z$I8Y&lYJe#6~{FWHqK?7%eY=}#d7W8tl?DV+QjX~Ys6Q;_lEZ>&rNO@ZZYni+zPzL zd`^5nc|Y-%^3CFB7i1E;D-^Csm6(7Si|B3PcS6QOm4Z0}Qv92E ztGQJ;7O@;=ocllS-<7`}e|P`U{&V?P_mAIS7k*a#wCUro4=wN4zUzCp=WWKDt*>Xi zE`BZhI`UQi%chsCukGI!y%+j;^`qz~vrlh77JXdwanGkKp9{Vm|8nOG@7GUXb-sK2 zX#Bb7SH*9WKYD+6{Wbij^?$SV@1?)gf64u-{3Y?b@Av-SnZMJ1&H2gr z>-?|g-?RUS{B!?rz_5&AD?>7)J+lF80-FGP9J>?yX||)R)+{fX?3gkb@BH`v*YtPQ z-^73a{$2QY>aW_LPd}%AU-Q-XOYvu+&oZAby)Sty`9|RN!DAKL&)-PCyZ`R}yOZyv-aEXX|4!;%_PaIjzP+t^Bl&vXD~s1mZ#>^Hy!rB~ z@TK<)zUT5!WuAyX6?iuL>E|aWpPYHp{*?2%#S5{QF|W?Ne*NahTe0`1A7VdteLD16 z^Xr>$aX;h!u>HTx;K4MHIhkb%OFqkAW;2$xtjX+l9Pc^gxfr?sa82iG;8Nn=%#+UN z$3KJr7k|3IEP)OI4FLv$27$){9s*POxdj>oRfPRSx6(vxIFGk>W?ii;4+~TNES} zY~&Zqn#(+rekQ#^`hv8u%rA6w`l7;IH7b=5H4G zC-7Q8PEc7$Q>04li+G7dp@hD~W$_&GLt^z}_Tr2Z(ckaoeha>Byy3h-yfS<{`Fi=T^2rHg3+&?4=3UIo$A6svDSxxTZ9zj} z0g>k-{i1V4t;94Wyd=XV_KKgB(2?|!kd-KqxFmi_?478dh?NkZkiD>!NTf)*$X($= zp%ejgzC%1oJUKj!Jny)~IUleouv)TQWB$!NgN2=yo%ILHDV7M9AI$m8=}gBMotaKD z9c4Pnw2^5iqcel?|Neg)|2q7+{j2)dt6$H4x%}e#mHw;z*Rh{xe<=L8{C)BFmhW-j z6~8NdkN*DdTlZI=FLOWd{uK7{&HJD4PQBB3|MA_lx3^zkfBECtna8OQ&)v7V|KtAZ z2Vd{=-1oe{|Ne^m&+hj=`1N4v!~Kt19;-i@_B7-9ju$syvcG=&ddr(XZ!f&x@^Rwl zH(y`;SpM7Rui-!L|FR5bjH*okm~vUBuz7K;<5lnE7GDA12fjaik9qre zgn8t7ba`@l6nMYze&^E@_$zQzKuI8*e+%DDUK8FhUMoH^el7mld_sKcd^h+e^Ggaa z3T6ux3SScr7ZDI`6d>8nZ_=V^5(~s#N;@<6f6Z9tK&G*-4ukXK-c^&uq>8r;tt6w_3TJzfK z?Y?&#-amf-?ETsI*6%ak@w|(9ckErmyTG?@Z*tyDeEZ7T!Uj{cJO#rKQwm$jcyd~*Bv;{BHQ+djyBO8PwO%bl;+zKMO` z_-*yK=ik|WasTQ2d;cE;!zzY0hBf~W{nPx{|F`E){_hjNX8vmW<^Su`&v!poe(U(W z_Cxl&+Bf#EOZ#TV-e=G5B?fc}9`#;V4T>ORk+l%k4KXZPX{@U`p<8Q_PDn@hW z=`31opV)75xN$6Jx8aE3{LeXuYbEysoK2p};N>^w+reALcZ#1&-~qpd;8x)T(J!Jc zVq3)xB%`I?OBG1B$r#I>mw%wJQ}K(Ey-K-Cwz7hfyrP-{zx)|l0htey+7bccTH@2i z6hvPMpAa?@))h?Q59R;HuPX3~edY-b9mx-9`R`MR`WjRQxv={R3%&_+#$>)Vj;3Y z_?1ww&?CWPf?`79Lh?da1=kDu3Tg;+^40TR;+@8~o^K1^W&Y)Y7Q*o&MWXk_&PW(a zU6O2+_$96=@krcQ{DoMfc$LI1iF@MT#4d@kihmHx6LS&WB5Wu0T3{c)Apbu;JN}9M zHvAR5^4upl-8t(xCb7q|b8)D0KIJ^hS;qO2a}(E9E_<#MoR2x3x#YP*ITvx1arkj; zV_(W9#u~xw&*aV2&t%WEknsW|19LlbERzo7B*sc6ZKfLxGykjqpYi|X|K5KG|J?l@ z`TO-x z<-X$m>GuWi%iJ@%H|zfDhxZ?Ed#dw%(~F8%dT-j^&U<&@y~#(h&+1=yeari<`s2;d z2fyR~y#Af`XT@Lpe?R|9|1Ge@*{9|En+_ zVB%w*&Gepe8-wWodw=8pru;qmcjmv2|7ZS-{h#o!`+p(hd#1O{46Igc+3dL-?3^8( zleo5Wf8TY`(?t5Y0s^n&3ba<@!=7q?#CeHHY2UjF0J5AL5Ye=Pms`-A_-{vQi}RsC`JtNt&7!G$TANrs7mxs7ELt23Je z8xPwLR!3GpW(UT4hI)qo|JVPE`TOxt%->0WKm1|)6Z*U5m&4CJ-(|j4d}024`Qzaa z#qYnpje9HmZr{5nZ%f|XdCmAH_08`$``=D{*Y-Z(L+pps?|t9%zkm4d%DeOLD&DPn z%k!4??d3P?UoU<2?$wdkdtW=e_I!Qiwc8t`H#=U7ywQ4B@g|KK4DS}d-SH;&wZ^ORmu4?Nzu5Z1`sKG*H{P_qO?^B0t@OK= zcgNqczMuJC^rPu#{jXf#U49n-=KX8@&+)$*gFoX{rXChf^!9b33dvzi9Qp_5HS~>D0WWlvS^fufUu{~M!}~7(*z0y_X{NnYY8t9DiS&? zq$?~Wd_-u9P^i!?K}W$p0S$pB0XxCPg1ZDi3T_k96geV#T5PKLM~M_EQE5TxLsF$u z$x?Pw!IJ01cZem5DT_@My(;oa_^42}pryclK1bdlp4U9bc~f}j@EqlF&@!R7 zLbbxpBEF)6qD3N8MC?TSMOTad7WEa|C{`fuFY!ZSx#Tpdd}(%>N|{qKA7w;kBV?mx zugbW|M9Zv^nI@AevsT7e_Jk~_oP!*@{BeafN{f^eRP@z?HPkd4G?LZJ)y}E@Q8}P| zM(LW8k@90DW~B_pSj9rcR|*yKOJzSu+euBAkQcuzY9d-BdO(y>v{YDE$V;$M-~xX* ze>lIAK%k(wkdY9B&_+RD!DRw{{7!rUyjOU=jrx#T&`I7HZYuzp~k&RF`t@1OoZ z*1xxZ$N#zYxAkBAzl?te{~iDL<*&-0A3sfgT>R$yb;sx0PcJ`i{K)*V^}WTr?{9y+ z6@FX!`uWR_7i!O!J!N^){wVKZ&4Y;hr|*8eoqp@vO}AU+w|Z{r+~&G7_0GLJ26r3o z-oG1r@7O*0`}gkudcggN@v+Pkm#1RS&OiI{-1Q~%tJ+s{Uu(QIdbjOeHCCluf8~b{_;`!qxOdx?_A!dzx98Y@b1~$hbh%ekK2m{-N{z``2?{c7JC5ocu}oZbMen}8D}8_Y{rmT(AAWt<|Iy%6&!;n=u6+9cDed#}&&8jwefslJ|6}2Y$oGQp zeBOG$ZGXGy?ZUV3-(Gun=e_v{wGXpCG=Aj#bl~H=4~!o)-dn!=_vXrLsn>^Jt$TI$ z)!kQzUtNCX_4@GZL$3v1KX^I$Md)+sXJSvcJP~+$>*>>H94~ZV`oA)G-Sm3a>wmA; zznT5^^Sjj_GCm1>Y5ZFJ&HMYRABn%1|E&9)^8X5h64Od%f7VHC#_T=pKiKU#j;RK!$cRKrvqRby3msfwyusRgOksI60bq{gkTu5PC8 zub!k{sNSRgSUp!mNOP~|56u?MR~oZ4iZo(0$~5L`{L)yfX|1KFbzZYhQ&Y1`sl}>UtLdwKSJhHIsqCl3r?^`FglvPfpX6on_hKJJ zcZpmQ_7FZO^g?jIKn}km-$5QBZXM1p_FT4CtVdX5S(90cnRS`}F*z}1GJa#&&#-`D zA;UoidB!)4+nFvinK6YhN;8=MWBa4}tMe8*e6jxG`%nv5-PrfaDGyCe%U*8;ht@O&|Mc}jkCp#bAe9(4Z@cx^7Q}1!!Yq)#tPW|njH_u)F zch&XE^GlAG%r9A8(z(=i$^Y_=%bzcET+zIeeC5Oyx2p@U{=8~(&F)&=HIeJd*MqMo zU%zoZ^2UxEqBl?9)VRI#&bPb2?ioDDdKmeL`*GvrdyiY6)IOd4Ozg##m&&hSyx#bx z>#fu~rFYZbzIxN}Cg6?To5(k(-t2kH`kw7W#fO6*9)Fnn!Qq4P2gwhTAI^PP{qgn3 zdmmXo1%2ZG9P?%OSF`ViKNWxb{9*Vz zI{&WuGv{~2FZG`ZKem5g`EAA5w6En~^}ZhZa_fut*Nb0$zpeas>)Xw5Ip4az`hL0g zDfZ*#_jd1ozsY|y=S|ex>~~e~<3GIq@Zv+`2cZx5-hX``_TkNku8$I*R(-1YT=_-q ztMS*LUv7Rm{$_-{;M(OGfiOTV)@9tpZPs=Da&e>6c#&{ zSQZwR0A_7wU*^|LTbRO_{aL26X0Y3F?%@*S;pYwFy~WeZ{eg2jM-=;S)(pLc!S`r*y{d+#5;pZvb~ec$_t_l)m% zy_@px@wRs2{32%P94tc%!71yixFRGq@dB*d+ z;Mu{atk3Q~Tm0Pr#ex?dFA`s9zgYGB^Rtj=&!5hHYW(!t6PBl&pE^F9{Osp5x97>v z=RUvjT<*oT7v(QkzYKW!^u^*A2VR7}WPjE3%Ki0>H`CrOe4F)l&f7I=}x8(iy z4>g~{zC8UJ{e9h!^FP1*V)?`N*YV$^|7RIun4(yW*ygdnc;7s>gMSh={e{}8-yDEGxRdDFgj=GVOVS6 zuOFy)P`60eTUShXrp|5c+gd+0murS;vTOd<6wx`V+og9%Z-Snnp0BQq&JC^Gn$ns* z8rmA18hRSN8Zw&uG{0$1))dps(r{A`QqxllRC7^tSDUQHtnR2Dpq{AyUG14Fhiak9 z0_C?#-AblPl}hf)8Dx5ZjT?+OPBhYD{KS|L~<@SATA zuN+S+moDcKc0sl+ED6lB8T}X%7?c?|G9G4(VZ6xT&Jgzh?%$R_$9~WKz5Ms$-#dR_ z`n~D*qTklP_x-%`UEtf3&vQR=y?^?8{>ul?H$Sa=a_mv={nq=wkF}pxKi~6q`WKJi zo&R;arb%S6Zgjt9_ioHzj^Z7tIO#Za?i${ zdVOriq3nH@O}?XI|85xb0b&Dg24lVRuI9dmY0-!*sl+dYQ+*!H*V7eBE6 zp!5;BV{ylqop3p|`gFvZ*t31-%`e^XAI|G&JJ!>zM}$i!ogy5BqvF`%6^wim5-KRBR^GcskE;IljtMC zc>dKqD>-;ZfUs%2z`NI6o_uJjC%f8wF@ci-a`;DJ9fAaqt{QdD~ z-(S`Lum1)7-TJ5J&#&K4ew+W^|1;-T@1K`{rv7R9EAjt3Lorh((arna9tbJbt?E zN&Tbw4?jP=`;g;d{DVvPtL~k>yG8^DL1)qe!Hb}|LS9*7iZpR zewy%o)}Q4J6PUlT&Sn3`Uc+9+#>BdiIh5%mgUkOte{LK7W{7dk!FvCyAGtBC& zg)Gs`NzA{Qb}&9+Sj-^IXw0O( zvP)c7;8sgg@>9;z($)3TUZcHE|Fp4$DT~Qd!wh|Qz1_NJv{q`QX;0L>r!`S?v#x{T zEd6gfwYs8u89E%go`&a)gN%8MFB*rMt}wf75@b-T6RzFrHLXi@SO0-J4nxEz!O+`&1 z&9CYas_M$;6wb@$%AS9|m zm9N2H*L-dKI{C}L&+=aqznuSa;_JlktiO_e+xT78{Sf``^DX#W$v5tAlfGU3A@^&-54mseJ}>!@@y6@L+$TF8p1GHI`|*wa zR|_tNUC_Lwb1D1c@rzF`e!k>=<=3Up7n?5syuy3U;d73^B2$5pGG|uc&7Jk(({dP_I`Hy-twLIr_isE@6BI6 ze^L4V@8^;~YyMXKb7p+Sa)8~KOM=IZXEoPaj&&UOxh`|Ja!=u95MbxO%G<(!QqWB( zS0IT`mw&AQr@(anBz}EfAFi+L4(wMrJ$ROJw{m~vnaG>NThFtGyM^Z#_aBaA&eztD zwM?r;*IK_+|BL=(!&Sy_Otj6`nR*+C8r?U3Y`V&PvZbx{1sgT{M8{Q*(;e2@>)XAw z?zP%#S!mH}Az}5yYKFC>?MGW_+jmyk<{8E%2B!LU`qldH^h$I)v=g;vYQUQaS z8qCtWuJc1HM}33x4#lksYVx0CxTNPv>PdKs`-;C2V-mF&TF&pn+s^%yOP%XC`wdnr z=8yj_{dWF6>6gL=lxIdZ|oocKSh5k{&4+q{JrH@%I}oFFa8<+=lsv` zPw$WK?}pz@f8zh7{rUUn-Cu^kmwsLO$?>b_*Y{rwf1mi9#t_Xoo$(ywZ3eUdYyX!1 zarhJd$L-IjKfM2@{cmB?V@+gJVDDhdVCiGhXSnlc*RQibKmOqO$^7g5FT>w%zukYw z|9bv?%2$&wS3Z6I`1a%ekM}-$eERV5^T)`~3%>mM!u56Hm&(uTpK3qWe`@~X@YU{% z)aO$lW#6BD{q=?3vn7xJKje7m`>^P-_A|a0WiPfryYP6yL$e32_s#Afy1(wh>xcS} zS)M+9{`Hm3Ti17X?{weQz1#K93r+(PWQdV2eS{0-tT^2|Do=q_b0|r z?>?;lp!t#KWA?`*pI?9d@m2Dh+xHvaY`(1hIQ_lV+sUuLzgqrE{I%Gd@;BGtbiFNp z8}qjP?f%z3FUwyFzW(?+=}qYy<~I{wr@x;0di(2ZuU5Rg{Nm|zsplV`IXu7q{K50i z=Zr5_yx9F>+l!DFO3xQPz5O`oQSifs_bcxv+!4EzbEoC*+ zuP5DLy*YZO%jlUdqi4E&8a>lx^h}r0GhIf{bQwLLRo>*BZR?|!|V@^;$Wk8dpA1io4ShWqV;x8?7u z-Y@yE=~LU+&hIHdQhr?izW@8m?-##K`}Xqdrq3y#wZ0tvc;TJ-yXo)FeOUN;-sh8_ zZNGi`ru1#rx9IQ1KWu&<{>RT`&GMV&9P2VRBX&{Fsod(k_56`Srb4&)U+^*WJMit~ zeZsqjC!Om#X8?B$?*smJK^?)_{1UvsIS;TeX8X;0khPpOoh6A$pD~zuJDVc=Y}VB* zY%CL*=P^kzu46P|_F;O@_?_98MU#0Eb1(}x%QEJFOesuP8Rsx){xAGH`*-#4-@o_$ z=KIIT+{bFedXe=o+eTJ5=2%8whLryo{{{WeWBA3ef#I zuV(we`iu1;Yb&cg>kgJk7B`lD=HE>D%vQ{Q7&RCR89e_#{P*df+P^D*CjOrN)AoD+ z*Vmtqe~kN}`Tp9Q@>i>0L_YI>^5_xs1MWN7H}_uOb5-R^@MXnIi_V*znRUYK=!rvX z557APabW#{9S1cJdmhy|&U7O4M9GO;$61cI9$S7)^|<|U;o}pJ?>h0|WWlNWQyHfe zPKBMaIqi5_1fq7Hl zvcshVm!4eSe5LZ*r5jnduH9C;D|ThJ%)U;DA*bK{r&UoU_2{2uTl_Git{tv~ntO#6}Wjrohf$6fDy-&()X zcys&Bk~itE#a^+ztb8%y#f=wnFDJcXd&Bznz}uPcUcT#pH{spYcU#}heW&m~;e+a@ z^3Mrh)W50zVEsAiXWlQ9-;@5V`e(~v$#{hEE`!4V-hU4NfBjEoIL5%wSji~D^n%Hp z!B%3IENM?dmzqqtWw16A$W3H-~*Gp7p2Pc{cO4z_8m^(^MhhnaerMOaFi%b9nx)UYP7YO$VTnZUA^ z#g3(eS&{i7lL&Jf%VSm<_Gpe`P8lvKu3*kY4rUH3j+q?aI9fPex$3xtxb|?CasK95 z$f3c($X>?!mpO<@ok8}W!e6Vu%l>Np`}Obne|Ltg|NH;y{K@;}@#FlrzHiUIE&P7( zN8_*LKX?DC|GWEFnvK>-x{8 z->m)?{4e^=_jBtX4Q40y*Bqvt(d;>Fh3rynn#|rzj4Xj16S(Ja1#z9{>E>(XbK-5~ zeJqe5n8YW>SId*m7S1BeQN(qY^B4PV)?2J=*dMcBWOHJ#=Ul|IgD-%0E;j?;OMx(f zL_T$1CEhsRi9DZqr1;mU z=Pck-=la2!$9a{*hifBu0nZG+dwe!LE!^e2mHZ3&E%^HQI0d-*_w!EVtKdJuXU5yZ z>(3w0_mbxvUx~mXfvZBs;wm!gay60~LPdOAd98WVc*Xh41zzzB@(1$=^RMS?=Tqg6 z5NH#;DDa*)h9`~Jmv>G2nsR;O-pRX;FNn{9r;y8s`wIV8 zkuRc|qKV>8Qu}1f6<*05mH8?YCv#V7y~G1iF3}JXHsSBWTq3?g0m92f|B0`c{w3oo zB`9`BaI1iVkhfqf{}=w{0;~ee{FVGK1$9N@L}kP~rF>-hr8C4Hi{uC=2viF+2+rcq z7Jrv01gy4Wi@e{&jeY-gRu`kYOM#p?gEf0OhoTXZ%m=-{Sur|5yB5^9JV=r#Ks(xel{>R6EACo@_e3<@m%jfA|0>0RM z4f(O=cjKR$-;IBI|4sR~|9>viea3J9UH@nNV_>XcTg3K|`5KEETP|A$dm_6edlCCm zwlFqtwtSXI7EQJdtO~4aS$$YGGu>yJ$GnoYn)5x+Ufv_z+c`Bj*0VM{0V0=|{W|MIUlKs(s}CSo7(L=S?qNUtD`C@T}^2$up%V zHy&?$TJgN#`IBddU+#G?{MqdDw=dhi>->oM?)h!;mx#}%pXYwL^R4Oo^zVkh;{LTU zEn{ov@M3RalV(59x{_%-<8|hPtTn90ETt^Q?4n#++&j4?dA0eu1YZiU^Bv&&%jv+w z!GD2Ynct9a3NI`FRY4V@EdG98Ile!<#@z2Yd^nZ38oADKP2;-G>Bv#ZmdobA9?B8U zy_GkOcLDEB-Yo7J9B0^sS)*BYvv9KNu`;s0WSP&}!`{jz&9j&5D~BA17yEYhnVjxi z8#qu{k-Hk(t%Y|n??^j-4z7BpFK{J6| z{?md%B432rg!%>41Of%P1wQlD@|tq@vfHsTaOm@06WS`gTHptF4VNKLCbt25J98Lg z07Dj24%<48@0@`=i}+Lp(}ZsbI`BQ=&F9w^C=pyQBre1z=py)0Xt&5*(c5BkBrZuN zOTU*%kO`G8l71y+CwWKwuh>hmx#AzhFG%c{E|ImAR*;mG{2^{EK1pJ`c$0{saJg`+ z$Q03N5e?yJk#NxjvCR@QB+VrKBo|0*6k`^x68S7#Bshg{7q32V2G1Uzd|p95J3emS zMLd&unR%yj2k;2+<@2&|t8>lcdd4No^MXg7TbHww<2Sn~`&*WijEetG|LFUA<-6jq z6+abzn*Uh&eaerSKT3b({G9!x`P=KSlfG7bVgI7|#s2fNZn^vL^R;DhA*8y?(weC_$n*Eiqvy{UhD^DXb|InNoNKYGsi zoaw3LlNFEUA1{2Y^YrAie=nB4zVznq8`HP4Z!%wrym|fZ|GT;G?B26|;P|-v1J4KR zPcmPZeyjZP?8lSuB|jVgB>k=X0u8CEg{u?Dj5V%x#W#D0){Hd`@U99t>t8@59ny6n8HcbV&% z_?UOIc(OcTdd)b4QIN6aFZ0hGKX(5L{XO^B^B-+LT7P~0_4mh{?+3qM{I>k-<1aTp zulf}E>GVha4|m=teE9P2{F?=@zrV70{q1$n8@;zH-^jjx^U~sl>$Bfa@}6vYs`O0$ zvC^Zer;aZsz2bPA`zGl1(>I*&uDs2AXZqgy{hN0o?{>et_wnY}-QVSYe)_%k-&zI@ zCS#^`jQbcQ{#X6aWAtQfU=U_}&dASvfw_hGBy&5d7X9Dx zPv*bP{|kT1{`~$oheeY;hwT{K4K_p8#mpv*ul`p4Hv8G|UHRLx&jO!?z8ZXW{=)tB z)0YLGQa*fnfAoXshe>bC-gLfcdTsM+;mg#Qp)dJf%zJwA$+f4up1M9+_UPFI&j-d2 ztsh1`sCdxuVE6r%_uC(Ac=-6yp~pc_YM-oqqWEm#^Bd2DUR-$T^7`~^w%7MwO?)f& zG5z!XFOpv$e17+-?Nil9lMg;06hA%tT>C}stMB)MU#5Te{Hy!_grS#tBI`z04>k^t z^X!*dcd;a~>|_yTOJzUDd4o4vfI(1ENLF~gP@rHT{~_Ka-n)DY1hxnS3d|L}BFrVa zP2{L>hp>R~cY(XS`?<Q9__DuXJ;t(#rH*A0b0dpA$5U=W-ap(ST-}^%oJt%; zY`a*pSRS)vu|%*eV12=6!I8z;!`a1I!0F7P$ezR&%=(Z;n$?-Lh&7*eDeDII)m-Pf zS987Q{J<5J*6 zDtxzjTDZzNjX1hlHCakoy*XZT^>J5nWpUo%VCKkUb6|;Py2mKXoXYZ-IgI%z%K`S^ zoYS~CxDIn1Vt3(~!&$`T#m&HblJ7KsrocBrUy*a7DPpqXr^Fp3rb_fnbV|5NtQ6Om zxGVWYDo0vf=9G+!Y@ckeY_RMfnR_xyGG8TwC4PvX5;GFj5ZNpwD5M~KNZ4OwvDi1s z3o_ee4@xUZP8F9IlN3o6dM@}^uv$<2eKnGQ2HGyP>=$g+S%m^G3`kcpRJ!QaMTzCQzh-u~YAt?m1ZANzi^e&6#= z{=58-??2pr{{2z*)BgAQ-&=oP{&wQa(a)bhv3zWPzv9ERPm?~~_)z{%;f?RB%P*{- z?|9PtaLxV1`{MV-@B2Iuel+3n=f{SRCp^l0-;tsL?k5$u=PirKEP7PA$z7qER`S;;b=me0&$EHcc& zjI;m$`_uDF{`c`e$$xhKzWqD&x5IDMKhyuj{XOt+=RdZ;3cug}T=7%)XXW>@uP;7d z{KWRj?_z-#kU-g{%Wznlyuhzb_ zcq#pg@y)llBJbTleEQh&srl2vPf4FPd^-1~_8aeawI8lOtA3vT!TiJFhxN~^zn=U( z%V5gX$1KJw&eq6g!m7%ApUH{2g(-(|JHrfye8xm3KBglK9t;%>4gW*`o&J}|AkMsi z^(0Fc(+5U##y9^8|MvVb|9$({!ry9t?*7sHcj+I;Kf^!OKevCo@P*~`vk!mYZG0pD zYSN3X&n2GQK411+=|$6vRnIM+u|ECvXzl~4`}TJi-2Qmm_ipsPoA;jIi@YEHK>6Xl zhesdrJ<)kq`a<@l+>71M{a%K?a((&nh0v?%uWVljy?ydt^RxE%y+2uhG5?JFzUHgj z7v0aPpKg8R{AB+*_)EgqiQm0{R{v!E#rZq#_ufCJ{tGjGWqQc0!K%l0i>-#;jU$ay zncIbTADfb*c-7q;`_w1L=TJH z67CXuE?~>o&HaiqhC`NpDmxpe9#<5%4bKklU@jrfAMCE|dF!_ULVvxJ9__YF@qk2}v=9$#J_zIy(8L3v?85q*&i;R!+q1f>L4^J;NB zaPHxl#W{)VGFJw-J?|a9BYXmU^LSVDtmS6lZsX+Qkl|?Mc*`!xK9?<*Z3|lly90*< zXD*ivcO_RFhdG-x%LT@w|HuDc{A2z{_s`Nll7IjH{r`U}(?J#w);}zXtPyM{*$%QD zU@KzdXPd@$pWTbonEM#-D}F}74#5+G?*v1HHVIu5;u9_wUM6y1tXJZ@M3N-4)D=l? zNkfTvabxj5aX;}%VvS-?#R|nVL|+IC2qy_W6VwsfDC{A2T7p~ZzNCa?oWy7GSKs6OAvo8?j=z!u~}k{M1y#~Xp8V{Augf&0?qv0e8+hMc-Qdca?jR+88t$SQt_mycxd!?`OEdkoDi?-?@Kw{}=q*@%O+Vxj#;S za{es-E%R&n56K^eKd%1Z_?h>!{b%(Lk8f6=553oa+xB|W%MH($Jqvg`;W7W?d5;;N z#6S7{#O~?RCoPZHJZgP(>CwkWL5~_9PJgKU$o0|pN4uZQe5UfE=4JaU&)3^uuX}Cv zI{8)m%LOkPUq5;?`CZ|MH=llenf-O|SI2LAzD@bA@-y()m0t^gul~dSck3VbKQezT z{&fBk{VV$K{QujGj?5>SWm%T6__A(f^<>pzdCQc*^n>XOvj@v}mN@ovoR(Z=oYy(R zI9xc6aFlZ>vn#SqU_H)az#_o%ky(~y2Fqj?3l=?=f6PtH7RJ}ron$Ix zddPT`v4l~WVc)-l|IGhC{Kx#y>Tk>+o!?zQtG-|R8uvBfYt&bcZvo#+esukm|Ml>P z+qcJ`cYXAEAMm#DjqRI;H;doSeSiMr{m->uwZCot+WDpL^X$+0U)a9B|FZ1MtS<|` zn0&eUdDoZxZyUcK`4RqW<8SjnHGdxdk^1}iPud@gKVSaD{#*I~2g5_ALoCNx)!C}p zPO*JqTgMj3%D^nf=*>{^-{T+uzdiq+{G0J_{XdugeheLqY)svZcNm-)a{t%9_BtY ze#rB%;X%NC`FqZHE$-a7_2A~an>@EK-C1;x;Q{}{#7Di4XFQg8Jmc}4Cug4YKXH1x z_nE*8o0opCj=%c+s{Hl;*L`p7-);S%`pNdw<&P2{g+4gGZ+(~gw&Bf=Hz(ivyf6JI z^u_R7?stnH$A3KdarlSzPxoJXzwiIv@~7l4-#@qiM;NEDRIn{#Kh4q1S;LvmnZn7> zd6VM}#}STm9G^Kuxc75w@bvJ?@W0_-E3j5@nNWf7eqlA?JfRk$>q2ruwgQEGvw6*V zxANTLN#`>W*e<{=AjN-#cLjF?X9R~UM=M7u$90ZnT$;Rl`KI#U5_l)bB&00lEj&v^ zQPf#9MKo4aO7x=02a!HePqDLN9^!|@jU@UcR!a0r%#tvbh!^`Kyiq7pNK!~bC_!k4 zP?Yd#;oCx{f-M5x0)qTYcy)Qd^9b|oSSPXGVx7e5$g05F$9j^jlEZ@Q zI=3kACEiHBYkV5~F8oUTANelw-Q@G(za}6dyjG+^bg$SAaZ~XI(d8mDM50BQMQ#h9 z5&kJGE>bDHTxhFMp|HM)hG>@ZpWdy_*U@FFY9JW2I?5x+A zotT6e*D)+)NM|r%&}GnOFkoapPs2=ua_pg_{75{MO zqu^($uVvqF{y6bd?04Cpi+?Tu^E31_Y+}5?^qEPKxr4=yEs9-+V=)I0XB+2wPBBhl z4mS3$tahxwSnjhtVF_W?Wy@qg&M}4aJLe)U9qx-7wezyG@_bvCE?6>r;ErixXob5u$Cc^Nt}5d)ri4}Nw3di~4t&;7s8|5^VR|8Mbc z_aCX>Hb3pZ@A*3Ki`Hkek4*3Hyb*c5;HArpkmnZ9Q=e--?|ruA>9i+-kC`8Bd{BNr z;9lq5hj(r6+dNqR;O)bgkK>=}JY#(R?)jz{A6{6zPzAL+vM{&uyQxKJ$Hk^r`#Pua8eY&i%CRbJv&qUqrrs_{#m=^!u)FhrjuJ zZ}`sko#&hCSBtL>-zvXf|8eA()Zg&`B8)E?9hn|7U0|NYBFdV=`i(V{J&yAOmov{6 z-e-Id`7a4@3N9DO5)2nE7YP(m7D*DBCZZzJEwoVJHvd`vDE{|+TKsGHvji9fKk*mx zPT@YpwV87vM=6ICXAf5|k20SWf0DotL22O%VQb<0LRP{bg>0Y<4bo2KEQ+^Ei%j%;8AmxWU24#m>>g-Nf^a*Nk6KV4}ccfs+Cbg5`oj zf|&x$0#yQ=1ZN066G{{I6>%075t}6@EWS^|L%K$WL*|0Cn#?Qdzfwmf-$?XJ`bwL~ zILQdeI?08|RmvL3lt?K^Zj{iIh!&R??+}-lI4z+i$tbBLc~(+DYL29cj@}0+9 z_BRV(RlnT#;>wGLmz!Q1zuf&I@WuM)`<^{~V(>`if!BTCdwcIR-!{0t_V%_r^Y41x zn|OcI!`jELPgXyPecJx)`E#C^4`1$l_2KpPx4Iu5eSG<;=gaqRaz9V}s`}IVPnvNb z^E;L;EN@uOv9hqWu|~1JWvyVZ=d9w&;9}-B=TYN1$feDBjC~0k8{1;mC9M0|mU3ip z9pIkD^OI)-?-#x+{N4iLf?I_0ggb?mgiVFY1*-&S3swsD2{H*)3QZKcBUmG#&i|D+ zgQt>f0f!vB5L-Q~IqN4DQodmr%b;hUeY7rjn={q+^+tCKJOK0o%n`uWLc-=D@jO?-Oi>BXmBPfeaqcsk{& z!qc)R_D}kr?s|Ul#rhY|UQB=a?IrW8?=OR2slK`V&h~@fhyNeueB%7F_KU$6$-{yW@_oeWQ_1BJXem^Gu zdhy5S|6E3S=EKbHEH7B{SpTvvW%FhC=D5Rohx;P06n`JTf&i1?D}i=TQEt&x5hvjwAxR-I zp*ezA1^fgi2;>MJ78DV*6}Z5!$bX$Tf~THail>mblb465munVhA;&8=JJ!FD5^pEZZJt@Y@_cc8WqgbH?(s$OJ>hBRR^+zj&g7oQ&BpVBC!bGDz+A9butD&T z;BTP<;acINBBA2(k}6VOQmIn=r8K3FO1+fSm0BfzPexakQ}&?D6B!TLCYd=>6C}4v z_DQl!>Pj+5NlR-79<6xLdq4l)-aA3JH{H^_rFe7W^`L7vukOEAcEk0S#vQ$T&JR={)jZaH^5_Zc zv$W?oUR1yGf7A9Z`(w$Mjo-d~zwzVr&kMi0ep~$c|Mv&O1m-I&vskyWy<*?Pah>BZ z#~#i-+_Jo^ytOasFm!<%r@~!m*OGo_iv18o!@_j(~?iuHYV_GT}eM z_e8#m-V{40_CQob^s9)FD6go2=uXk;VoBne;<2U1r=GElQ;_>ZI?KYvX9`RwQJpMgIee_s2!``3iu4S!_*sWG%LE@#wWJj5`UVHU$$ z21&+Nrd2HZZ1rp_*;Lts+5fT4WNTpC&Bnl9#U{;qd$+fKlXaU_e|yGnb$&ZbKYjW z&3HTIZSvcFZ}z?U{Wj&j+=ti?TR-%CT>0twr+FU*K9s!=elPdl`@Q%FrH_oCls>t9 zWd7jr-t+y__ftMFer)&{_UYlLHJ@reNqn~aQvLPTx7r_Rzsmo(|4aSP!!VOUoN+p1 z3{wPi9?L$~PWCX)M6T&v^SEwvMRLpXNb`pBDGGQC9S~*^5fE7|VlG-N`d;*&*gf&- z5_=@VB@`s0B_t%KiY1A}3LOyG!r#a*ETAA5CS)uuCoCiMULaXuqdmZ z^&)K|3L?LSGes;!---5!nTp>KSCP0O{#b0bXurrF;b%gLLd%7MgpUZ{5pEU^5}G04 z&7Z(`ori_nhO31um`j#(H~V=uOSVm{Y^((=D_F`|PqR9+u3|}N$zyS4v1ATmeEa{x z|9XagMmy#L)=KtE9MYU&97gQrY|GdVvcKVQ;uPR~%TdNr$Ucd!n(YtUclI#OGhCKD zUAzH&nS4%sLVO!}9e8DUWqFx+C3r9Jp5}|-FXms(&m~|bkS!o6Xd$#$SW2`|^twow zh@|Kl(R8tN@%<79BqgNIOIAvCXxAHF&KmK~>^w#{%hS#rOuX-c$w&~5IS6g0ecoz6{-4l-|`j7P=8r?s0 zx9yI_?b2HlZx!F(f9KoXzxNy-tbMrRQR3sACo`X}e)i&d$;y!2;*H14#9{P~={=vI1@A}?Fy%l=n`&#YwiPxTQ``_DsTJ(kS zoAtLXU!Q%^`ts>>_?MSozJ3Y*(*IfaGwbKepRRnG^jY@nuWyY%`hUj#lK;K=_xs<+ zf9L&n{oV6h{?C;^Q~nnJTloJGg9T$aL-K#U|KI+#|Kt5XfngfsGR9uUS&Zu#?=!w+ z{L6TOiIv5HC6u{^sf3Axc@}dpvmuiRV+=#a|J;8$|8o92Ge|LL{OAAo`p=QyKYs21 z)%0ub&!8W?-zR+i_WAfH!B5#ASA4kep5=YOyVSQDZ%)37eEIVE(`PZyo;>Y(did## zXZN0~zbt>b=f#)j)z6v+cey!plBS2N%KdDrlc`5@w3LKEgvs`H2&20srl2FkG3Co zeklHs`(eX}=#Pb;_IzgfYWpqsd;O1^pDw@d{VMux@JH!y&A(uV1g1z9CpJ!w*Bqxf zj&ihf`g8r`n$E+@KTY7Az!3p+!OMb4LYBh4A{Jsj;>*Q1i~SU}6T2z4O{`z^v2cUX z8o|#3&H^z4wSqf@WQF$$MG3hHEfCr+oGrRfj8Xit*gdfoVxD3NV!q-xC7PvPN`I4? zDSJcql5B=-xXceJR;hH!rxJ@Kx+GF07EAn)5SI8LDj{-L$VIS;e=*-yzG(hG{NeoG zya%}#aj)Sv<$liffNMLKH&+wqLJnJw5DqC0HuiE>C6-#|STlqNzW(qtKcpx}MxK|`mq)}v}sET;B#7)VK(oD9li>QBak?Aj>DKB3mc>MfS0rmcn5LHU&?)Tp3a6cqv0E7pX^*b0i*#sfeZs zZxRd?Xy&)%H{sXkf5qp^=gs?pyPeC2Q-ec{-HYuC>v>i#HWfBqwp2DLwuLMz%yCSu zjLi($42F!q8NHc~FfL(u`v2Jffd4W7rvBCWd-@OKpZ;I3e_Z&^{{6()J)es{PJ18p zF8%HIH(YN|yoq_O`SR7XiYH=^jy;(0!1Y1M{a1IT?s(o3zB%v4?i=AZg>P-Wm4938 zj_;kMI~(pe-*vsW?7rQ@(8o;APQIvro$${2W7p@8U#h-#eSP!w@wZFgSN|yd<@UGe zzdFNIhJ}nPnc|sESvpx`*rhmsb6Rs9F}$nzy!hYp z-Qc~<1wzS! zD+NyT=kTfUI`On~ujIPMaftOElN4j@f49FXe?tG{{z>}n_4D;N+piv9fjSSkU$(#4 z^djr!kC$Sv#ow-f_u`%3yPIzX-@SUb>wWcy=8ry~BtHdw%=?i1{@}aa@6zApzdiZp z$D2!U)89LP?E3WU)4@;2KE{2}dcW`;@B6>+^*@-tw|aNyZQ$F$H#1*Pe9iIZ#G974 zF7N8zYkpkx>D=ePU;Mvq`|kGh)2|1A*#28GHZy0k>adxyb+I|JKV{#@@rH9QcN?z^ z|6cyZ{Qvl~1;d0-i)<2g7n>?JTZ~KWqezsQR;&9Y#Djk6|(wr&T>~}b!0tdETs#j zyrn9nrc0fdvXyR<_LrV45H~D7r?-Ixm@)ThbQxI<#caxBk;*lwmT_Bete?dM^;j%)Jg1thc!dLlpxtlVa zGX65Xvh4CA3Wf@|uVueDevABG@=NPi?$2l6HNUO=^7~WpN4^h!?{2-8e>MGO(aWZnzAqm?uYG#^ z(b@Zl?}*>Ndh7Tt>svZEGp?sy<+_r1`TnKZm%d-RclqNL%WKWom)v-LGwt^3JBRN| z-Cy^h^wG>GYR~Vyw0gt$Zq@s1AO3wf`629M`zNU{2H%{2y!pA|cjVuwe;fY4VdP?2 z$I8Y&lYJe#6~{FWHqK?7%eY=}#d7W8tl?DV+QjX~Ys6Q;_lEZ>&rNO@ZZYni+zPzL zd`^5nc|Y-%^3CFB7i1E;D-^Csm6(7Si|B3PcS6QOm4Z0}Qv92E ztGQJ;7O@;=ocllS-<7`}e|P`U{&V?P_mAIS7k*a#wCUro4=wN4zUzCp=WWKDt*>Xi zE`BZhI`UQi%chsCukGI!y%+j;^`qz~vrlh77JXdwanGkKp9{Vm|8nOG@7GUXb-sK2 zX#Bb7SH*9WKYD+6{Wbij^?$SV@1?)gf64u-{3Y?b@Av-SnZMJ1&H2gr z>-?|g-?RUS{B!?rz_5&AD?>7)J+lF80-FGP9J>?yX||)R)+{fX?3gkb@BH`v*YtPQ z-^73a{$2QY>aW_LPd}%AU-Q-XOYvu+&oZAby)Sty`9|RN!DAKL&)-PCyZ`R}yOZyv-aEXX|4!;%_PaIjzP+t^Bl&vXD~s1mZ#>^Hy!rB~ z@TK<)zUT5!WuAyX6?iuL>E|aWpPYHp{*?2%#S5{QF|W?Ne*NahTe0`1A7VdteLD16 z^Xr>$aX;h!u>HTx;K4MHIhkb%OFqkAW;2$xtjX+l9Pc^gxfr?sa82iG;8Nn=%#+UN z$3KJr7k|3IEP)OI4FLv$27$){9s*POxdj>oRfPRSx6(vxIFGk>W?ii;4+~TNES} zY~&Zqn#(+rekQ#^`hv8u%rA6w`l7;IH7b=5H4G zC-7Q8PEc7$Q>04li+G7dp@hD~W$_&GLt^z}_Tr2Z(ckaoeha>Byy3h-yfS<{`Fi=T^2rHg3+&?4=3UIo$A6svDSxxTZ9zj} z0g>k-{i1V4t;94Wyd=XV_KKgB(2?|!kd-KqxFmi_?478dh?NkZkiD>!NTf)*$X($= zp%eiNzC%1oJUKj!Jny)~IUleouv)TQWB$!NgN2=yo%ILHDV7M9AI$m8=}gBMotaKD z9c4Pnw2^5iqcel?|Neg)|2q7+{j2)dt6$H4x%}e#mHw;z*Rh{xe<=L8{C)BFmhW-j z6~8NdkN*DdTlZI=FLOWd{uK7{&HJD4PQBB3|MA_lx3^zkfBECtna8OQ&)v7V|KtAZ z2Vd{=-1oe{|Ne^m&+hj=`1N4v!~Kt19;-i@_B7-9ju$syvcG=&ddr(XZ!f&x@^Rwl zH(y`;SpM7Rui-!L|FR5bjH*okm~vUBuz7K;<5lnE7GDA12fjaik9qre zgn8t7ba`@l6nMYze&^E@_$zQzKuI8*e+%DDUK8FhUMoH^el7mld_sKcd^h+e^Ggaa z3T6ux3SScr7ZDI`6d>8nZ_=V^5(~s#N;@<6f6Z9tK&G*-4ukXK-c^&uq>8r;tt6w_3TJzfK z?Y?&#-amf-?ETsI*6%ak@w|(9ckErmyTG?@Z*tyDeEZ7T!Uj{cJO#rKQwm$jcyd~*Bv;{BHQ+djyBO8PwO%bl;+zKMO` z_-*yK=ik|WasTQ2d;cE;!zzY0hBf~W{nPx{|F`E){_hjNX8vmW<^Su`&v!poe(U(W z_Cxl&+Bf#EOZ#TV-e=G5B?fc}9`#;V4T>ORk+l%k4KXZPX{@U`p<8Q_PDn@hW z=`31opV)75xN$6Jx8aE3{LeXuYbEysoK2p};N>^w+reALcZ#1&-~qpd;8x)T(J!Jc zVq3)xB%`I?OBG1B$r#I>mw%wJQ}K(Ey-K-Cwz7hfyrP-{zx)|l0htey+7bccTH@2i z6hvPMpAa?@))h?Q59R;HuPX3~edY-b9mx-9`R`MR`WjRQxv={R3%&_+#$>)Vj;3Y z_?1ww&?CWPf?`79Lh?da1=kDu3Tg;+^40TR;+@8~o^K1^W&Y)Y7Q*o&MWXk_&PW(a zU6O2+_$96=@krcQ{DoMfc$LI1iF@MT#4d@kihmHx6LS&WB5Wu0T3{c)Apbu;JN}9M zHvAR5^4upl-8t(xCb7q|b8)D0KIJ^hS;qO2a}(E9E_<#MoR2x3x#YP*ITvx1arkj; zV_(W9#u~xw&*aV2&t%WEknsW|19LlbERzo7B*sc6ZKfLxGykjqpYi|X|K5KG|J?l@ z`TO-x z<-X$m>GuWi%iJ@%H|zfDhxZ?Ed#dw%(~F8%dT-j^&U<&@y~#(h&+1=yeari<`s2;d z2fyR~y#Af`XT@Lpe?R|9|1Ge@*{9|En+_ zVB%w*&Gepe8-wWodw=8pru;qmcjmv2|7ZS-{h#o!`+p(hd#1O{46Igc+3dL-?3^8( zleo5Wf8TY`(?t5Y0s^n&3ba<@!=7q?#CeHHY2UjF0J5AL5Ye=Pms`-A_-{vQi}RsC`JtNt&7!G$TANrs7mxs7ELt23Je z8xPwLR!3GpW(UT4hI)qo|JVPE`TOxt%->0WKm1|)6Z*U5m&4CJ-(|j4d}024`Qzaa z#qYnpje9HmZr{5nZ%f|XdCmAH_08`$``=D{*Y-Z(L+pps?|t9%zkm4d%DeOLD&DPn z%k!4??d3P?UoU<2?$wdkdtW=e_I!Qiwc8t`H#=U7ywQ4B@g|KK4DS}d-SH;&wZ^ORmu4?Nzu5Z1`sKG*H{P_qO?^B0t@OK= zcgNqczMuJC^rPu#{jXf#U49n-=KX8@&+)$*gFoX{rXChf^!9b33dvzi9Qp_5HS~>D0WWlvS^fufUu{~M!}~7(*z0y_X{NnYY8t9DiS&? zq$?~Wd_-u9P^i!?K}W$p0S$pB0XxCPg1ZDi3T_k96geV#T5PKLM~M_EQE5TxLsF$u z$x?Pw!IJ01cZem5DT_@My(;oa_^42}pryclK1bdlp4U9bc~f}j@EqlF&@!R7 zLbbxpBEF)6qD3N8MC?TSMOTad7WEa|C{`fuFY!ZSx#Tpdd}(%>N|{qKA7w;kBV?mx zugbW|M9Zv^nI@AevsT7e_Jk~_oP!*@{BeafN{f^eRP@z?HPkd4G?LZJ)y}E@Q8}P| zM(LW8k@90DW~B_pSj9rcR|*yKOJzSu+euBAkQcuzY9d-BdO(y>v{YDE$V;$M-~xX* ze>lIAK%k(wkdY9B&_+RD!DRw{{7!rUyjOU=jrx#T&`I7HZYuzp~k&RF`t@1OoZ z*1xxZ$N#zYxAkBAzl?te{~iDL<*&-0A3sfgT>R$yb;sx0PcJ`i{K)*V^}WTr?{9y+ z6@FX!`uWR_7i!O!J!N^){wVKZ&4Y;hr|*8eoqp@vO}AU+w|Z{r+~&G7_0GLJ26r3o z-oG1r@7O*0`}gkudcggN@v+Pkm#1RS&OiI{-1Q~%tJ+s{Uu(QIdbjOeHCCluf8~b{_;`!qxOdx?_A!dzx98Y@b1~$hbh%ekK2m{-N{z``2?{c7JC5ocu}oZbMen}8D}8_Y{rmT(AAWt<|Iy%6&!;n=u6+9cDed#}&&8jwefslJ|6}2Y$oGQp zeBOG$ZGXGy?ZUV3-(Gun=e_v{wGXpCG=Aj#bl~H=4~!o)-dn!=_vXrLsn>^Jt$TI$ z)!kQzUtNCX_4@GZL$3v1KX^I$Md)+sXJSvcJP~+$>*>>H94~ZV`oA)G-Sm3a>wmA; zznT5^^Sjj_GCm1>Y5ZFJ&HMYRABn%1|E&9)^8X5h64Od%f7VHC#_T=pKiKU#j;RK!$cRKrvqRby3msfwyusRgOksI60bq{gkTu5PC8 zub!k{sNSRgSUp!mNOP~|56u?MR~oZ4iZo(0$~5L`{L)yfX|1KFbzZYhQ&Y1`sl}>UtLdwKSJhHIsqCl3r?^`FglvPfpX6on_hKJJ zcZpmQ_7FZO^g?jIKn}km-$5QBZXM1p_FT4CtVdX5S(90cnRS`}F*z}1GJa#&&#-`D zA;UoidB!)4+nFvinK6YhN;8=MWBa4}tMe8*e6jxG`%nv5-PrfaDGyCe%U*8;ht@O&|Mc}jkCp#bAe9(4Z@cx^7Q}1!!Yq)#tPW|njH_u)F zch&XE^GlAG%r9A8(z(=i$^Y_=%bzcET+zIeeC5Oyx2p@U{=8~(&F)&=HIeJd*MqMo zU%zoZ^2UxEqBl?9)VRI#&bPb2?ioDDdKmeL`*GvrdyiY6)IOd4Ozg##m&&hSyx#bx z>#fu~rFYZbzIxN}Cg6?To5(k(-t2kH`kw7W#fO6*9)Fnn!Qq4P2gwhTAI^PP{qgn3 zdmmXo1%2ZG9P?%OSF`ViKNWxb{9*Vz zI{&WuGv{~2FZG`ZKem5g`EAA5w6En~^}ZhZa_fut*Nb0$zpeas>)Xw5Ip4az`hL0g zDfZ*#_jd1ozsY|y=S|ex>~~e~<3GIq@Zv+`2cZx5-hX``_TkNku8$I*R(-1YT=_-q ztMS*LUv7Rm{$_-{;M(OGfiOTV)@9tpZPs=Da&e>6c#&{ zSQZwR0A_7wU*^|LTbRO_{aL26X0Y3F?%@*S;pYwFy~WeZ{eg2jM-=;S)(pLc!S`r*y{d+#5;pZvb~ec$_t_l)m% zy_@px@wRs2{32%P94tc%!71yixFRGq@dB*d+ z;Mu{atk3Q~Tm0Pr#ex?dFA`s9zgYGB^Rtj=&!5hHYW(!t6PBl&pE^F9{Osp5x97>v z=RUvjT<*oT7v(QkzYKW!^u^*A2VR7}WPjE3%Ki0>H`CrOe4F)l&f7I=}x8(iy z4>g~{zC8UJ{e9h!^FP1*V)?`N*YV$^|7RIun4(yW*ygdnc;7s>gMSh={e{}8-yDEGxRdDFgj=GVOVS6 zuOFy)P`60eTUShXrp|5c+gd+0murS;vTOd<6wx`V+og9%Z-Snnp0BQq&JC^Gn$ns* z8rmA18hRSN8Zw&uG{0$1))dps(r{A`QqxllRC7^tSDUQHtnR2Dpq{AyUG14Fhiak9 z0_C?#-AblPl}hf)8Dx5ZjT?+OPBhYD{KS|L~<@SATA zuN+S+moDcKc0sl+ED6lB8T}X%7?c?|G9G4(VZ6xT&Jgzh?%$R_$9~WKz5Ms$-#dR_ z`n~D*qTklP_x-%`UEtf3&vQR=y?^?8{>ul?H$Sa=a_mv={nq=wkF}pxKi~6q`WKJi zo&R;arb%S6Zgjt9_ioHzj^Z7tIO#Za?i${ zdVOriq3nH@O}?XI|85xb0b&Dg24lVRuI9dmY0-!*sl+dYQ+*!H*V7eBE4 zp!5;BV{ylqop3p|`gFvZ*t31-%`e^XAI|G&JJ!>zM}$i!ogy5BqvF`%6^wim5-KRBR^GcskE;IljtMC zc>dKqD>-;ZfUs%2z`NI6o_uJjC%f8wF@ci-a`;DJ9fAaqt{QdD~ z-(S`Lum1)7-TJ5J&#&K4ew+W^|1;-T@1K`{rv7R9EAjt3Lorh((arna9tbJbt?E zN&Tbw4?jP=`;g;d{DVvPtL~k>yG8^DL1)qe!Hb}|LS9*7iZpR zewy%o)}Q4J6PUlT&Sn3`Uc+9+#>BdiIh5%mgUkOte{LK7W{7dk!FvCyAGtBC& zg)Gs`NzA{Qb}&9+Sj-^IXw0O( zvP)c7;8sgg@>9;z($)3TUZcHE|Fp4$DT~Qd!wh|Qz1_NJv{q`QX;0L>r!`S?v#x{T zEd6gfwYs8u89E%go`&a)gN%8MFB*rMt}wf75@b-T6RzFrHLXi@SO0-J4nxEz!O+`&1 z&9CYas_M$;6wb@$%AS9|m zm9N2H*L-dKI{C}L&+=aqznuSa;_JlktiO_e+xT78{Sf``^DX#W$v5tAlfGU3A@^&-54mseJ}>!@@y6@L+$TF8p1GHI`|*wa zR|_tNUC_Lwb1D1c@rzF`e!k>=<=3Up7n?5syuy3U;d73^B2$5pGG|uc&7Jk(({dP_I`Hy-twLIr_isE@6BI6 ze^L4V@8^;~YyMXKb7p+Sa)8~KOM=IZXEoPaj&&UOxh`|Ja!=u95MbxO%G<(!QqWB( zS0IT`mw&AQr@(anBz}EfAFi+L4(wMrJ$ROJw{m~vnaG>NThFtGyM^Z#_aBaA&eztD zwM?r;*IK_+|BL=(!&Sy_Otj6`nR*+C8r?U3Y`V&PvZbx{1sgT{M8{Q*(;e2@>)XAw z?zP%#S!mH}Az}5yYKFC>?MGW_+jmyk<{8E%2B!LU`qldH^h$I)v=g;vYQUQaS z8qCtWuJc1HM}33x4#lksYVx0CxTNPv>PdKs`-;C2V-mF&TF&pn+s^%yOP%XC`wdnr z=8yj_{dWF6>6gL=lxIdZ|oocKSh5k{&4+q{JrH@%I}oFFa8<+=lsv` zPw$WK?}pz@f8zh7{rUUn-Cu^kmwsLO$?>b_*Y{rwf1mi9#t_Xoo$(ywZ3eUdYyX!1 zarhJd$L-IjKfM2@{cmB?V@+gJVDDhdVCiGhXSnlc*RQibKmOqO$^7g5FT>w%zukYw z|9bv?%2$&wS3Z6I`1a%ekM}-$eERV5^T)`~3%>mM!u56Hm&(uTpK3qWe`@~X@YU{% z)aO$lW#6BD{q=?3vn7xJKje7m`>^P-_A|a0WiPfryYP6yL$e32_s#Afy1(wh>xcS} zS)M+9{`Hm3Ti17X?{weQz1#K93r+(PWQdV2eS{0-tT^2|Do=q_b0|r z?>?;lp!t#KWA?`*pI?9d@m2Dh+xHvaY`(1hIQ_lV+sUuLzgqrE{I%Gd@;BGtbiFNp z8}qjP?f%z3FUwyFzW(?+=}qYy<~I{wr@x;0di(2ZuU5Rg{Nm|zsplV`IXu7q{K50i z=Zr5_yx9F>+l!DFO3xQPz5O`oQSifs_bcxv+!4EzbEoC*+ zuP5DLy(x10#$C||LXVUlpMF&FNcYjqhbj+E9vr#<>p{uG)ekN_ka>9ELBT_v$L3Gj zUr4+zew+U8*V`#?r@j67#^O!joAqzF-!6Dt{;ul%k`J3cwSDdUp7JB*$K~()zpwm$ z@!Pa-FTZa3obp-g%h8V)-kHCf{_fm|g`ekrKKa@9+ox|z-)4P_{$BjU=J(-${7lv? zzgf<)E@Lxd7v-GFte5&%URP|l9==vgPFIpDYDOIUCqMAGLd;6lLX^BMiXWqruU5BnSEI_ znHMn!vv9L4WB$jK!gQ5!4uj_Z!oRbBSO5O~d*5%qe|*e+tTwC{Sr4;qWOZYXW%Ol8 z`G4_W(EmJ!Ukoc4L>Nl`PyFZeFY$jdgA$_;lK|71|MUJO|I_~a?$6P`GyfR;;`|Zv zRrQO}w>LkQ{QmTB8pF5$P5ALe{+VJjJFuCF*Y-6v2Eb+;l9axjbBP| zy5Jugp|AV}&}!Z;FkI(-bw8 z@|0?oC#%j>@6p_^<*T(ubE@VljdXPz^)~fXbxn14wTa3diWe2k6e{Es<)_GZ%6G_b zl|L&lq`<6rO!1Jy5;C2b|9h$o6mNr*~@NYLPSz{jbN2P9DfbpL0&%ITpnfay&M{BGnlV1 zeE9e1p8~@v#>31zSaaB>uufy?VUA@C|9|RV`M;Wf>LmK7}hth#LGY?Ij<*k7@oV@YDpVJc@7Wy)pNVd-PN z!gh)68QT`N0@imdUs()Td|B48=COTb`^jF;ah-iN+XvQPtPfdRS?yVOutc)BvGgksTWsCn4)sK#-o6PYJU zPTV@qa=i7}@?)yU?T-r|pLl%Ni3cYOPSu~vIHhnZ?3B%E$I}|8R-RmOQu$=ti9g4g zPE0v@?R3@IH|H!axL*vpwENQ8Oa7MyE<0R0aOuhA%~vX~UAmEV>)LIlyJGjY-2eJe z@=4w^&lh)JRJ{y*b^qnt7wcbmy?Fb4%8MN@{a;UgtN#A)`?Vh{J~w{Z|Ml`W&+h?0 zVt>~B-1>9R&$J&2-8h1R9$43FoVcuk<+5b#k|DVi1SN$OC(B!NpwlP zm1vgOE*>tvM@&>KS?sZxn|Qj!Ny!IN)-uYnPO^!zhh!#5^@~f3L<_j_KIXd0p~{iW znar)vJCm=LKb+r=ZzInut^iI)4iENdwp*;utaDhjSew~Q+1=QE*pt{_v+w2*<^0WI z%_+yl!`;dKgnK$q2k$1n*Zi9W7zI5AJq4=-jQIKaPV=a6H*>0R{A6=r<6xV{TF+w6 ze3+@1S%jsOxtw`7OATuRs}}1imI*9tS?pLkm=&2XGKny!u{>s#VUOl0=9J-*;tJ+W z#lzhD2J z|95A|`oI6L&Y!$r9zV{1>-+ZX+rsbnel-3{{&V-Q`oFt>MgD#M_vHU%22VyeCI#jK zW_M;WW-aD;<}&8P%;#Aau+8Og;F`+4nkS9-J8v)_7hf)K0B;JfJa0CS6ZcilD;!B2 zQXF&Hzp-_&*|Bx7MzU;VQf7R}P{ufu@d_h5^9&YVHhy*i_7*l(HYv6iHhuQ%>^nJ_ zxa_$n^Cz?8^l#5ZcE&i z?2_6l^<8I5vdd-tWo=|uN^O_4m&}wX5&th1BQ7V=D3LDFAi*H% zEqPmlPa-~aAJIfn2hkg%E@HA`uS6?F=ZQQLmKJ6adL%H9-+@nw_c!-0E;TL( zE=ewH&I9brSXr2r7|#AV`a|IB(NA(8)!&!B&3JS5^_tgZuN7V=y$*b1`S$zUjCVQj z3f`@KH|O1lw+e4hy?OTLE_iMB+U=FcOS>2K&lWvVdffR?|G|O#=kH&* z|M0%TgOCTF4|pG3y&rwQ;ohRVKkod$z2;W+%?UT$t|wnjy&QZ|>wL=Dho`wtO*tWQ zBK!FLqi#nG4|^XfJGk>e<^j!vUk;r=l6frq#D!C@&UBqSc|P(&_l2()%r5F&48OSZ z;{A&cFW$abeeukNzzZc8?p(Nf!S%v{^OfgA&Ig}Ab^i7Rl}i?vKV4?Ma{Th{%O@^> zzC82F-m4nd|K50VtM<m3E{`&U2`0vDjkN&S=jAkxnabx9WJH)n}{X0h_R}l9P?qr@|o<-a> zT#cNSoJ?HYTst}2IUPB7a7b`WV_(Q#$Pvc*ma~Y9lY0rbA4w~KvqWfyiB4Dvy7ySxa@N|T?I7- z4u$IqGZmW^)fGP|>{kd;m?OVS?w+iKY>~_(X)WmlDL$$BQa7dF$r#CglesPPTIRjX zHEXxuA41>fzbk%w|8?%G-7j-rGQ2E)!TI9L^OMh~J#%?- z|Dovv-upK9?C<&97kTLM*x;%8vk%YIU+BGzdCB+k{)_J~>|U0>YqMw|69QRn~vCZREkIkRdKgoa6 z_C)Xzl z!N4WUbCj2bKUW}6=&gvGxTNGssT!F*vdVJ1WKYU`l%60RC;d-qij;=b5y?==ehE); zWwA`r`65?^R|)fpREr3Ua*KTx>lgngE-bNFoJYJ&Y_F)ZsEuflsEMesXn^Q5QARO; z(R(6JqSHkKME{FeiQW@+6c>r2;{t6%nhHU8H7_57C$ zU+TY{{1otk>7B`&gRk6PRz5d;X8-KfGr8vx&pThNc=hm2-@EGf+8=&>Soe|ZllUiv zPjf$&eg6IV^5?qG<)6=gw*6xIrStQhPsN{heysYC_5S|58Sg6I<-GHKr}xg{9ml(( zw;$i^d{g@7?CTS+(qFE6zUQgl6Zgl_kHj7pJ~;JY{sX@I5qDSK*?4FB9g92Mw}oz* z+*)$W^MB9v zUo3fX?d7dkcVDHxa(b2WYTm1Ruk>HFFI?c7;3lD5VJ4A4kzf%ck;lU2!WP12!uN%a2<8ba;E(0k=by>9oc9UO z4xT$a`n_fx69v>@$`;BI%3GD6Dlb=#Q1(#H zRi3K6QaMxkgHofCq>{g)mV%SKjr>RXI)wrSO@*)WJLL`KJ>>XgUrGOw`YE|k!d={6 zY=daH=pm79!ihqk1zP!4_&Rx(a|LlKa4K*na5S>punVz!v7KZ2!<575&2aKx`k(VZ zJHD%Zo%ku`!{c|%?~cChf4lX~{ns3C?z~BV+x2$s+w*Tb-)?y`^L5fI|CifdtbLL7 zqWpQ=Gt*}*&%B=UJ~{Yk#lzzdp(YXeB;F&u&v=*cs_@R{apJM!DdLIanZbRCtBR|f%am&`XB4Lerv>Ljj?e6e z*fy|kWSPy}&eYF%jUj;H=Kr|=kNySy`}UXfpWwf}f4BZk_~-Ed=Kmmur40IvmP{PX z*32`Qx3FAhjbMvplVkhNs>o)^c9hkY)tWVo^$SZ1OCXB~YZluH_B9;hoC2I%IIK9P zvF~JaW}CtGi)}X>KU+DgE~_=GENd@|7z+o>Ddzu7?-;)@I5XV&zwE!)|Nehd|DONj z^=Izy9lu0=o%q@D)A{G?AFe;vd|&Z>&3BIPIp3yyd-?7Bx2kWB-+aGq`F8pn?>C{Z z?Oz^$e)Ng!)A5fCpGrS%{fY>(M&*g{w*vOHr}XRc@Z%~->z$7syB zk>TGz?Z4uG_S@Yx1_ix{|e%$#!{aeykwXZT?Cw_VGIr#IdPb)t8d|LG}{^RS9 zt3EM(R{Y%cx#-KWud3f2elY(`{yF96`JZoo9{&01=c->?zxVul^Rwya!Joaq`hQ3L z;r+|~PwM~G|M?6SjFC)RnQyVAvrb{%$@-SnoUN2?6`LD-Jcl7?59bNa51e*fTHLlg zk9e-|=JQVwm?;n>P%j`P_)PGCP>Ap`VG9vWk#yl{LViN2LaT)?3q2O{6pj(uC3;>g zTf9a*Up!v?w%7sD(<0d-nIh>Td?L=m8->maeG{4`Bqj7&@SEUHL3Y6}{4e-a`D}R? zbGLA2vma#5Vo_seV)9`8$e_*mj8U7Zhmn`jj?tCz1H)^ESjNkY8cdQ*{fsG$`b;j& z$C=MCZ)TpzoXPx`NrP!2<7dVsCJ&}RjJp}z8Os=r8S5F={+ImU`tSW;%fFR>O#dwU zbN!F+pVr@kzaRX%_UpwjuHUx57yg#}WB2F9@0Q>8zjyz-^mEyd^6!b?=6|*Q`sT~b zFAKlC{xbK=g3mKP?fYo|apFhLPftFc{b>Bj@Kf7I?T@Nvzj$)9(_x_LBD){M*evIecSi8?=s)3zgc`+ z^3CRZ)At$QZ+>_EG4aRVAACRe{oMU)+i#;k?0+WwcK>bvo9B1wuU9|U{A~Lf^>f7! z;qN(LbH2oUe)4hGhx6~}yz_Xw^YzJ>*)QT>?0%8{V(If+&p4hbJk@<-|M=je3y;=4 z`ux!3;rjzUg#`DfRjmOT}Jdf>_2 zC!e2GKJ9*L{Pg*gNo+b?!Q9$h8#%N%4sqP%yu@kFp2^(9=*q;w62n%+QNp3kzMuU$ z*A2cmg13d{3eM&aR{eyXrB%ahhse63u(Yz<(&x>EQy!`QU=L^y2;?I^m)qQ^FRrlMkZ`Qs_ zeOdGB@0-u>>fRrEcl}NEE2EbmUv7M}^n>S@`(LfU{`?&A+4l3fPZ1w3zb<}h@y6og zp)cFN&G`P|yZq0kznp&e{eJtq_IL0v=by`dhW+(rZeah;?#{N8Wgd$Q+cgd*?#0~6 zTnQX{?336lIT!Ho3G@o;2=wqt@GA?YyAKx?HH@qr*7kEo} z6?h+Wi*j{xEM{N8v5PyL?-FkmmowW1fzi{7M^7srJ*{~3wBpgzibqc?9zCsi^t9s9 z(~2cWPb(fht$6gb;?dKJ#YRsn9zCsi^t9r09HXZdkDgXMdRp=5X~m0TUj*Ocv<%_X8&XO`|U3mLn>oE<2Qzj49ZN4SSGMpv6r)J zvoB)RVY$g-z;3{4!nuGgl-ZK0oB1${Gs{7yzl`e4S6SC_*l{o8-OHE9E61(JRn8s5 zcS~TMz-`{sT#q?Bxb65H1wIJ;6Q~kAD`YRSNaU1orqB!l8$KysIevTLNn*a@n?#Qb zrwI#-{1C1XDiXNEXUVsRPhMb+;7lP=A#s6iywyDWc}oSCiI|FT2!`=3gYT%0ndG&CVoYsyFyO{?+J7W*a{T$EAlPiS;TF`EzNzN zdl&CLes6(FJ~o~^Toycs`6YziL_P`65I!foTI7uATCvMwf?}ScXG9Cc&q)|d)=8*{ zDGD9r3*%E2kQE9LzA79pA|_fZYAos~a!Dvm@Hf9T|9gIZp)irDq6;LK zlaiNek>D587nv_|L~MbCpX5e~RpOQsE2KWlyp$D`n=1QW`ibOKi9*R9>9w-oWxvbh z%1oDGm5r5UmGzX7m02uPDcdGDL4LD*zuaCKMrnDeRZ=z5o1~N^9*dL+Sqgm=78K(X zcN7y8T_GAEK0*A8=mB9a!D!wh&M9p7S&cdRxRZDUxih#Xa;@UJ$+ed21Lq#j5U#mg zC%9g4m-1fbUCwio>lSA;_YdAneEqzexXZbgb20Gv^2+laS@&`+&zvR|fsKK8lz%l9vbz9@b9_=)l3r}rm5 zJpJhK(dqq$w_D%NeXsrL?5F!53f}p?o&NUS+h1?GUrW9gc%A=>;nmq!-Ea8by?Yn? ze%br154S$*eeU_Z=+piW%hpzvMx|!%Yu$A1-@v^S;!*raO1M+T2~JYMHNM z+F5Rvyp~+M)EhAc(Q_isA`-$yLd8O#1^)`H7RV92Cgdl=AbLts_^Ze+vGwAj;-^HbMR$v3OU#j&EVfH{ieRI_EP)q- z@*rz;(etB6<=>BpD=C z#lnPd3at=f6gQG+7M~&-CDJGIR>V%EMkq=^h+mlhBL7!Gcd<+Ff|vOUdE5Au1%riu3MUBn2`v?x zBy28HAaYM+ort>dYQZ^zvcd;N1td%*3dPQeOckjRZ53N2RwlYm_?qA&eiy!Nyi$CZ z`EK*M@XB&4bFpzraUNp3!kogy$g+q1I@f3J>D+Q$#q269%8dH|WB--@UH-f6*Vo_v z|EDl1vn*!*z!<6n8%R!KmDKQzq$VlnAF(TaqMM(#=PtQtUuF# zN&Sld9r4%yU(R3U-&16ZwYY&97IbFT-Etz4-Ui=Z)aIw0E3uZ@d(EruL-fiNw=yPo_LEd-Cw{ zg~ypsvY&E3+xWEU$@0hfPc)vrc=q{e^b^&`I*&>pvOn7YnCt20rz@X{zxe-B_f7KK zw{PCOe*a4QW&87#7n-k+y^()o{p#Y2RWB>v!Wh@Ja0J^=~u3E&a0jlia7;Pc5H5ea!fn|4HN9q+im1CjT=3;ri|E z*Y@ugKcD|x_bcW1y`N{lRet^c_2<{EU!%Ug`dE8qY^Z#G|Zk!T&IO4D4H2`k7N$_OPj?_(2Xo5~u_qQoT5Q1pK{!*ym`_G=u@92?l& zSa-9qv&OO(vmRod&nCmc!8M7SgQt}HH>Vay8T&1cC~g(rHN4uq>$$gbx^ZxE+~@G% z+R1&M=OFJzzBl~l0`K^i@oeEL;f&%m<^IAe&u_uk%X5W`i_@9?468J2K5G%%efHg) zd%25wJh_gt7BF)BZ}?~Qzm9=}=`?c{t2vtr+XS|!?7E!SIahK8^OW-)6PPEIEVPI} zmFG5>Gxr@HR(?~#OG3+pHwm)}FBLQvh~>Y z3BL+_E&jIQUE})|?~UF^ys!Om`{R~ROFl(>RC+J|cFpTeubN+(zW($2)SK6D@4nmq zp6}!8kDed?yxsan^X;W~kstnlkp0N`ap#A#ANW7c|KRjN@ng-WFS9XU&gX#7<)1%&PXDs(v-?MhcUrHn zy%c`E;qAxwR-a72hJ4rmp7hn?^U9BZKAik;`NM;c&%d1cvGiBS&yC+^ep&PR@@M-m zE5CGqUHFw9v|vIB);4FdfKZ^ua3ObeKGq5+w1>t4!;$C zXYfw+UEo`T*Y+!rvm#y5B0-g>v^{pt@g zAGdwX{haqz;#<>Ky{{*}x_ndkmiBG=cj=#Uzfym_|7rM3;Sa;V)Bk4vZ)HefJi^q^ ztk3l4|HFSb{?+|o#qgSuh50J;J7!I0ZYDcMMaB(`MNHe6b}}h4*E7FoTEzI}|Leax ze-eLZ|4#X%@Gt)V(*L3V-T!U<^Y*vipD%wn{+a#1$WX~#%gWCBm&u%Q8iN_5A=4u! zFP1rMkJwq*U0A0wH!+_2|M7n`<07UemJs%I&Rou$>=CScncg!{lE6FDHU*fDdmpFqMyT}v4Yl0mj zbHv|DY>>DsZXwVSZ3w;)H6Xp}yF0xZZS7ej$KjBj%E~0s&i$q&R z&x!Df^s932?R(`Bxq|~A4uQ*k4o8nQ$zl!S=s}%y}+2vW~=gYCnNz1Xxua(!8Z;K3)-P;* z>{HlQvq&&m{a5_k`)BUoJO5LeT3HUWK4P86vWM~WUzXobe{%j>^egRm!k=${ul}!R z==(qa-^PD6|F1JVWr}C5W}n8fg5w|i4K_ozV7AR{0ql7kYdEiQ*>d0KEMmXLI+1lh z>r2)RY&SU;aQpJQ@Otuua=+vf;O^n};j!WU&D+f<&acWZ#^=Nn#&wJ10y{tZZ?=2v zLY(cK7dYmzGqcZUvtvzVZfCm7G?lrD*@}sY@i@b5hJF8+{`>y-z~3!@wf>&}t?^6Z zC-2Y4Kkxt2`90-V>(9y`_21pTv3-sE;_&79XPwXWpWHtE`1t3;_V*Lt^}YS@X8oI- zH|1{*yeWFK|FzcZq*sP7r@YXAIpx*#*VkTKzL9yW^EUeR(--%jKYL;Q`rcdl4+}nW zf2#jj{UPeT@4J$>+;5}aTD}W^KmWtmkGDQ?eKz}4_~F;P7w>kxulgYT@#4ppPpY2+ zKKQ&#dt2~!>sya^HSYyKYJ9r?@!k8MZzNx3ytw#6JApUZ zufkrYytw#0>V?RwnXfZnuX|bde9BXSC;pG`KYsZ1=!@XjO>ZRMB)$6e{Oi-7Pgsp)9p|9J#Bk-;rW>tGhdWFFL*ZZ>7plR9@{?-e7x+* z?PuaI%AW6i`tR}YN6Q}BJ`#Df@A2_x#V@u!mwfjANy8K6CwCutK3w{c^~ubqKc7r~ zyx>vtqh*htK6?L%<+1$Zd5@nyx%qVZ)7Gc0&m^9oerowR{NbvH&W~q2R(tgKf%e0_ z4cao(f2N53C+JYMs7_2Xqv);tS*aq7k57s)T&UcPvp|1ST% z)(78@&p+<^sQ+=>hs=+upWlDs`)c~N#nfb@`&&}VDepmfT``h=gkHL=l z9*Ysn6eeaSTjp9;FZNcB>s*<9^97FcNAe1APv%tTEaY_JQs-I1=O^$~;Ix3GfHVIe zK2?4n{vv)=f!BhDB3_~a>SsXoc4;y=U;#n?m>M9zs^ z6P+O@BDPS(Ld0Bjf#`CPjly}t{lXuF%SD((jl?)4TqGGJ7m3%4T^Fm8cqX}1>X5XU ztc2WJxw&$;WXom6dw-n#Y4ywd=eh6w-)4MW z@g?E&nvcOBNvrBesWn_t)JGzZ-t< z>wUL}p^uy%w?1C==*Gjjk1jr${!IS)>gU^EO1@S5pz`tX`=+-SUaxwk`YP)6vUl#E zSie|*miXBEzU+PU2knn{J|6k><}>ftonL#uF8Gr8S^ksdM~RP|pMHH-`R4uo+c)!X zQr|qjF@AIUn)>Dc=R05czDNID_v`X6iC^qLBY$l8;r)y8@1y@_jB*SY|AzdL{qy@b z+wb!~Eq@;T>Gj+C@6mtP{#!9v|JVEb=XdR&34de%$^75-KaycPV;-{#%SL8a=4hrz zjB!lMnM+xCS@ttMVz}|I@9(<5oBmn<@BOFr_ww(*zn1=*@hj!m!=JW4C;#yL@%hKE zpGSW3{&@BE>1WqZxgW(oMt=D9uKI1^n~XPdZ!6z^e#7xb=XK4ipjUfdrM^D>TJ3en z%a6}}Urc)G@apnQ&KLKe1U~e+=Wyrj?ZO{a+o!9VRx`*KDF3{p`nB zH?VACNoGlBUc_{ZX&1{c_K93_+|^vuIZtp1aY}MC@kt5Z6_yuWC~``8p0K8ff@rO1 zm&gU-7a|wLUWwloUoJL5$oqvSnrAcjWNufU-@J1Ll7tgQ)y3|K&J`^elayeS zyf2|5!6|-IG)?rE=oYaWF%D6C;X0xFLTxHEWcd3?C0aqQ!m z#krWXo%0`O78gI)V~%L{I<|PWEY>Sbz6>Y+doZ>z2eVqRS+TXUhOvY*r!cQ&@n-wW zHkB=aO^3aK!34UGvE4(h;kJ+7A3s_#T#Iwq?YO}_$8*?w@y~M-EWx<}v zdWglGbr#!Ij?-M++-Er}I5x0{vVUMZ&uYX{$?U*#pS6MAox_&hob?)W9`iP4HCA`_ zMh+d0=WJfAnkY*?dd?w(r~WZ|YxPe4PJ|_wD^R7H=iq@xFiZ zF5+#(>$NW%Ua-Ej+_sik0%HOVhb^Ds~_3F3fKWcyW{595p3GXHs*`k6m7O=Wz{P{Q!yzX?Me<5fm0#jpqdAW@JlgSi>Jz;u;g2^yn)9&#{@gngZ`$3Mdz1N&^WArM zCg1*f)BNV;n_jmW?*`wiy7%&~-o1bKH$5_a8u+aG>5j**9{qV7|E&Cl+{^PXPQK83 zx%y?#t0%8@-+q1T{%*#*==atioIbF>=X}faTJDwm>*9C+Kgxaa{Ic=$zR&t!l)tci zvHY^-v+k!gA6I>r{eJ(a+pmY;qrd$B_~C=uhm-H0y#Mh&=Y8S3DQ`EwJ^OCn`{nO> zK3w=X^-KKsh@VWq9{qgw^USYDf9C$5%UH;CiD?bfcSb>`Da=P$-ZSShKKW+13IiYSa!4gSw&@~R>k>>bxOg?sVYut zIvU3`R;&M4tyVp%Ca;|A|!>ylSKT4_6fcaauv-Me<9v2<}UI`P+ve-pkL5h z=zst>-%f5lE-_9wj&AlDY~`#QSYER{U=3r-Vx7VKnvs|BHp46iQHG}fi~d>u4f=EF zPwC%Ze;j@@{`~Sy;M?(UGk?7L#qsyT-`{_j|Cs+t`n%{~^nafJwSV(}&-nG_xB0)U z{}KNs{=5Ib`d^7*^M8l`OaHSo6#Q@gcjoWSKdXP2|C0Tg_#^da`tR3&R{lNpxAE_l zzmEUi8S|MdSwFCCVgJppz@f@n&i#QWms^u#3F~8)F4kPOG`6R#-&on$3Rrbm7qUKJ z{la>b&46PWry%z>o`rl5`Mdd#@D=mN2tE>$7j6?u6iN|}7vT_L7UmbS6)@p70!c;@xo`bGW=(U;F&oqY57t>in7xAm{pU+;VU>NVq=iZ>VE$h}Q{d*e;* zYpqxHFJoSMzN~(E>ecTzVej_6Rez)ZddBPTZ=SwYeCzOf+RKm^=bvZ25Pf<3h3^Zl z7tSxVUwXd!`pW%v=Ie*AcD&s4V&V(dmp(85z0iGm{ndpx9dEC`jd;iQZrz*u*Hy32 zyy<*b{$Brm$UF0QUhn69(EAwmLGs;|*DSC5U+;Y*^3Lo1?f32Pquz^t`0?S;$2Ff6 zKfnKU{L}kS=AW}a8-7mzWd70Y!Tk7PCq6fSvh|VLBaTPyj}#vBKel{y_rdb}jQ6YVZM!@7PRVW8Te>$l+|;FxXYYQ$m3~+Je%8n0&uL$te|h}H;LEbl zw?3PFVgC~JdDSPw&wIa?{|xwJ{MYbL&9C}ji~iXD`}^tN!^TbO%Uj9AXGu(2QH+{e9^XCeluNvpI4*fr z^0nkHsiRVtBr_zk#Kc6?MP0>SiN%P07XBpYAhc7YTs%g6p{ToPsz{E|Ex{C_9O0?L z8G@MtY(go*B_i!2=Y@6)x(FQP-Oqc0|BK){VL@R|q4z=p!uN&hM3Thjik}ugB)(Cy zT4s`5y1b;El-xrFBc))a!^#Y*Eh;uj3l-idhAJ;oW>aBR30Cz{msD$4u~z%4@m`}w zC10^hMOpo=+6VPx8h)B#n%wGX>RFon>TYW8svdHWRfFv7Z7PMn{b3T*)4nX5&*j3d zC%9GNwxSMC%l}IL8}edu<T-P`awoAo^y zHy_!&KP=LxnIpLCljNU#!3AudA2i-<_&%R2iRaL_Z9f-^G_g*)A%9)wPbKe^ztyi? z-}HV<{_y`rJ%gu$xkSR(@+XU*8NHvxaD}IhYdu^2-&-&D-!prZ^5)D>i{H!MY<+c$ zX{lr=|DER*=hE+nesTQz;$zW=PY-Y3o%F^1*Y<~dZtj1j@cq?y^^X_c#(ih_Vg6$B zbBk|^U-MpX`nc%N_1`kz3*LQrc=$Q@51ntuUuON$|9tyV%(I=Jga2RrdF$T1%PSwA z|K!KFQ{7hHlzs2l(&w*U2>j|{3ivnm`>Zds{uJ|V>t2K8`-7?(26Roqu2ahk^akZ`S8)o<@A!@pHzH<*&FNTD(sF z%J}ZelZ{~UeBG0s57en5Bz-Oo7$h~@2`Ko{TK4p?qkh& zlV2(SDp`L2?f#VWOHOEwWG$P;_YS5`{#=F~FBg1TE@Y(GBxL!s{CUuG?ypz>-uPzy zdj8AC4;nx3v2yWl`^)-%#^1}_3M{6qYXq+G=CiG0UdUP{vQ?^_`x!g0U@reh-ZHM| zf13UUab039XU-D5DX~Mam2)4P4a@R>=Kue2wTmqmxyE*dVJ?>qPwVff51+pWvvP6F z{ipi<=I;#FCI3vGb3Nb7D9d>5>E%0G_t!iye&zR8`OT;A3BUe6eRK8eb(e?I&(=KZ zy7THL&z;*3n4e92#&OH%?6%7uH%!k3T)cEo_JQDSzl&ujFI)+IaNthmtvA;uoe#bu zc$eY!<12EPe_qSFbK+k2-R2u^*S_2^zZP>f^^!_V3JpUU@x!`Y!aRB$GWG3sW&m3U?LrjxT3En0@bNI>NP+>pS~O zCg-0*-=Y|H@My9x{dVe?CT|SS3kE}$2O_D;`m!b5Z@9Ar{Mol>Tk}qrjoSx%Gc0+dqL1 zECN5@|B?Iuj5(Iuk;{oskKgaV$zN9XYNklGSfNE6PJhocU1X1DV_?c)EMaB*|Kr;V zhHE_1d|$X@IQp3D7}?lo3uy9%Func%lO={-o%1JerJx?KEZ0I_8SWdbQ^Q}@L!H%R zr@4($mcbN57R~+gtEH}rZjyeaa!LNMsDXelAD_Sqfj$va*<_f7Oe$eTZ}gg)mm++k__ z>+#{&^WCoxeiZq>o^du~`zOZ_Gk#6^Bk@m~sq4?tACvw${VVx;?9H3!tKRGX3;wn4 zxys{huW!9m`N;8e;orl5-v2QA@cV`O`=tyGT!$H@zZHEh`w;iZo!Nw6h2zzqv)@<$ zTFKJIS)z@_lHq2Gu7k%OVca)8b=K$xtzn{KH{s{gT$E3z~g*E-3{AZ~b^Pk$kJ@RqW zhnP=xU%h|M`K$S3;X8r1F7M91kNYtBW!H<~Zy)|@f4BMM@nzmGH?Y}4gZ2!jo7vkK(>&#aC@7&)G zhI`Dj+3MKUSpWVL`6cp4fN=xEyuXuJ9E6q$y<^S&ckj=Y9}Yii7$0)~h&5FAAL~Iq+sz?%vNw<{ZCy>f@y!m*P+DJ6?23{$%#y4F}Rs zxL^Epw)RxqS*8=5htf|xJ>zhu;auR^oHL8geLksl*73pBCljt4Ub4Apa%#b${reUl zUUV+)O7QhXcVZs?c)IVI(cQV{cb?pK{^<2(SC*e`xR8Er$JO&!%5I*&C3bDkt#?nO z9@t;uy7ltK%dZXp1%KOqwEq0)Ywm|zZ@fP+{VC_T!6C{b$C&fI>W%B`!|y`BzGpba zd6C~+%0=;w#8b`?)&QPT(VsHy3PEzhGV5jTNKcg7FKH|4Dfm&?MqB77+ zjdG4sTGC0PMP&SLKS z0$)Vsg_U_$a+z?~aCC9phh2=bwg58#%Ue9_KOQQ{Y+1CBwOf zJ&Nb0n2D66&_$kIJW1SV1Sd;97mDCk7CkHVQ1rOSeSS68h0J1{OZmTv*Gj2NFpF0T zuH~5_Xd=E(>aW~%nRQ}1BHlti;w4Jclus#WX?-%8Zj`IhspzTLt^84?U%ga!qTyqm zx$1Lut{YA^WHwDT(>MHX7-n|BNMD;vZGq|w%_DkGjIvE%nVDN}vavS1uB)k5uJA~{ zL{UmLO>3Rj6E$0vb!xkGOf+Z8zms#7*(1^{F;j+7a=NsUoT*HJ!cnE=va2MFg}nt< zh^R}LiF)uo;rPYL!8@PFlyg5@9rs3|<)Ye74pb2)ms61mDbn>dcMigDc+36S|K zy;A54I|FMq>mH`|zmdO|{PX7I)X^XkN(~Nv+jrVcg^=ppWl7F`tiQE`ClKu&wKU!^_=&Q-x@#jf3f_1 z)Jx+>X-}(OYkp7o-~S`~6<_sz0*zh3!17kE+fM&!eccY1FQypjCi z^ez3D@Zjd6Doo?X&vlS#PgB7knZAnf?Fr-=gnWo}GFk{PgaVDUV~G9DVWY z)w$;f9@jk*f1L5;ukh)i_{6EUD=-0mYv)?U!&+@wch2e{&mlxjVy;=Nt z{hg;bwq5tW>3MU-HOWhirym>*IPvk~&D$mq_C7lQMB>4u>pw3CT-$wT(?f-Oao0{= z*1VQ{RqEW4liKGKukv2oedYZnqszx{=-+?-(EY)ZTi35O-gLZMdDr2t$oh8TSFYQuVJH7hWT!}?*ixZ)BJZUdq{C$zW!l893cTlc5?L#~Q1-EmrScrD3Y`|+ znYy)_Ta<<6<77|DPgJ@t?=4j*^+85M>ZRmMfQpulgxg} zPRY{}>t$zXKF}A`u~!RLDpZ)O@J(T|g0B1_SzYh5AHnWVXxh5e8TgW`wu4r zyUPDFUj@HD`IY_m^dFtS@BaGyOZ?aR|1(47pNF55K3({h{H^y>#pm#E&prpeQ+u`K z$?p62AG5qIeaG`G^7iCw?{8;4pZLMyQ~cXkPdsi{U8}gQ_jvXb&4)a9O0KbA`F3^N zjiQ@DH@Dqjx#Dx~(fPxFdew<(}KEb2m=j(768d#>u+}A5D6B=Z)r@iSKPc$$Xmnqy2C1 z_khou-*|uVG5PSU=U3p9=64pB7QZMFE_FlpwoF z!<0@dFiZIfGYKn*yc2xNx0`1Qj~Cx}zG?w^u`@Ei<&$OSis=gV3T+aO7M?4pz+b{6 z$?=dynWKT1n{OibarQTi)Bo=NqwvS@SLnC<@BTge_Sp34g5z5J8F(D#4VKea!8 zKUROe^ri1h>Sx_gFF#KCbnTnfzkH_Ij3IwFe%tfu->1UQ>%K&OOZYbX$LZfUeii&w z{(a@2IrALm#sBO6T=`-DWydGaPkTPj`eglS-bd9hyx-Y>Jp6v{yYY`-UyVObdDr)j z?_<>0#IH9#KKv;2RqZ?H7q!>NA5VD1`egdkx@R|EM7~XaQ~#|0sr9o%k9Rz^eqHkB z^Q#vxf}Sb9@P7a7i_h1YpBH~>`uzL5(?1Kwa)zdVmwvDRq5Jjm=icx8f3|&P{`C0c z-}i^!$$ygiy7H6W+k4O19<|g(9=6_23!u{>&x4U0%e-ZuK^NIhn?w1+w+h3o5RrONx zmEW7LS7)ErJkol^@x=0ZBgqO8%3_iMjH~+QrbJk0rXHQ@FzA^vc{Auo|fG;n;T>ZT1^SuuT zUR6Gy_Pp*z;48B?tKJH|`}cbBtK>IJ-gLc6el7W4_hanG;7`ZDq<$~_A@Dup%bt&0 z-|@U<|KRbp^2f!WQ9lK~8-9EKt?k?7Z=Zhn{Z9G)_2AxrcasMsxeb>jsAHIC$ z{JrkC@6X6@F<+&AeEu84T*I=MrIlkbw;uO-?hib2JUu+Gcv!hkusvn@&%(_0K=7BC zqxgQ|PyF7zE}Wulg`DjI(}lM4tYmq~Aj;^)D8V50@89peKlHzpe0uTO@5{=s`9HgU zT71{}<@CRrp_Z|RC5&bI|0};-e>8u;|Gn#b%=e6+#=jQ-`1WfVgA|JZ`z6l%97b$E znJ+T#XL`ye&lSo2k-MGutH4Cz3gJ_NGx?wM{ot<_xFKXC;U<+R(I9zR##u&0(qCLo z;*Ruj1##sjYp@h`~mqSH||ntBkf8wHw_xerR^k zqSr#-BEVAFs>J+~p|*~Qrj+(agAh|g^Il7P>m!!?E!3UCg|ZS(HVcxr^~B0~6y}hE0D@ zd|UP5@w?aWnLa)EEc|umH_q=BUl)G4`{nqT=U+vCDE<8Oqx$>mFTXx`y)k=X^{o17 z)-$%3j<0{dk$m^)oyNyUA7{REczgMs$h%8#9=)k}{qp6!m!DsJd_Mi@$%hy3|Gj_s zq299_udcn_@&4kwW3NxVocH4JbK4iuuNdEEz1jC7=7J%TxmSnybrmPIe)OXaHw-1xn)##!G=x8k=}YBF$I0?5 z{!%)s_+M_Zw1AYL%zC-~a?4~F%YK*3Q=Fj`A@46;EiEB?SazQL1%-aOa_MD~$r9Y+ zx?(QkF_POP>!dQ}mMgtbaaX;lq#@@nwN{c%)=<$~*5n1Fns@0@@wx;_Fwydnf(3CFp0U1 z?H2brzVF-y?1@Yve~GSsYMei29fBpW(+uSz>@BBYJ_;B!J;>R~{rCz^$ zne_be<1-Hm?*~1sew^~;@H77xYoBMo6n&Hb`svHNFJ`}3{QB&BlTRDJO#e3N+qv)I zzc_yte*ORc#9N-XcCQ{jfB$^)^IOk6pU1yodQJafL6 zcKzFR!P|njgKuzLU2?_i`oC+lFBY7BbnN%xBZqPivg{Mt{dbS}q1%VA9`UGqzB7FX-_E(aMfXbXvpRI{Xy4&Q`@Zis+x=+Qs@;F~ z2=1}l)v?F?pz*=WyYFs&xAF9bf=w5=EBY^Pp&V&>2+)A&2`syUEFmp|3b>;byqH3pLXBrfH+osCQdG+{o4BlySImiD|KUlj%3ZCW9J% zS3OJpbc1L;4y_xi>y(TYPRjDhtdP=@0A2esQEamWkHiGwgZz{DX7lp!zT!R1|4wLu zXq3oD{w(fk9E}{UoI0Ee99P-DactsQD3mJxT3kUiPI#}#LGdoBY0@0hm!!@}1xv|E zE6RjQ3raJ~2*@r_SfOgC?ynZE`b;TAUPnet(o~#D{FZpPq_=dc%naGXa&>ZFrQOBd zgm(#D5zZEA5uGcsPv*AlE~#%4F4Eg%_sX*>PgXToQ&DqN^HpD>F0HDnB&C$4TB0>m zmr?h$)<=zt>LqIN%1#PTX|3y8{+bijwNzgy zWhgewbx3cQS}f%yT_`Imw^TY_B16Sx%O1s_a5F8cW8z4p5~ zueZJEc@gw#(yQth>z*8WAaTF^{*C+n_Z9DNzAy2}=*g1DXCEm(<$0<2`sAzWFQr~x zd9&(W=(~4sl3#c{TyZbs;iE?!k5)h2_VDtfM^Cmqk$imX@w{g}FPFR#eE;*kz=vb+ zW8T@n7J6>-5mmd{BKmM@$q4eX-kI-NCe=`1_|Ks&b^ZUjx zZlCVGSATE&A>iZkj|V>Le3tvp^841W;O`&5%=misJIk+KzY>2w|DOK+$XBz^MV}^r zjCt4iM*ZE!_lw{Ee!u*K^ar)KkDfC>)_th^sQO9vGlyrB9w*&zx?Od1-i^}hhp%n9 zZg6wgE%Uo&_t)MpxUc$P^8N33tZob4{(ej7cHZrEx3q3;zf<+_<&$|&ZJzc#6?<;> zQu@`O7lAKYUVeP}>&5ez$KP)Gbm6<;pSHgSe{_B|{aEyE*B7bJ*FRLeQ-8ntbIp&o zU-N%0{;mE0GSeoeJOA$dy8C1D52l}dKmC66{$T$d{5Sir%b&X6%l@=7^t0Hr{a`cU zxXJmKyMXr&k1CHg?@hj7{s`VG&PulLtW()+*$i0}nNBmEW98w<<9NWnhdrLti93>$ zf#uo%zJI^}-u&bKtLFQP@3Vhq{G9rI`Palx_ut9BUHEF>GvB8c&-OnJetiG_+1t-< z+_`Rcz5ANawa%-SSHv#QxOn{hzH^M{=AWrMeff0D+1B%=7bGs&oHsgm;mq;VD^IUH z^ZVTOi!qmPT&1lUSub8c7k*as%<@&}`x&3v zzPfzj`TFYn@}IkZZu=?lYt!$tf9L+I|F`*f@$c1tXBjhCYFV|}FLO@hj^qjFS;rN? zk;VRu{RaDe)-OypOzNyD9GVQC*@^s;sP}rg%t! zQ)!0U8y!2tjYeI@_e^%1#+bUAY%{hunQ!vg_@mJzqfbViCNE4yP2BZswdSfdD$h_9 zQDB!#lhKsfFB2@gL)KP~Oa6}h6onZIC*)R0i%Fgqml3}s8X+tzxJ}SPR8mq)+Fn{i z`lR#}>1C2f#52XqB|@a0N;AsDNH3H=E8{9VUHZ6qvdAN0KhZg2-r@_z^h7Mchv!ce z^b_MijLZxd7^|4SG3{m8{O{90d&VZFXN>90ot*Cl zZi}RdJQeg2I3X}ckW1h+_bYZE_A<^r+~T~m`OXL|6v`4-6gehpA}%E^BPK1{BU~hS ziob~eqd>RtQIQG4rvx7h)Ct_@m*l(1RmpC^`h;oI|GGcDzs~cQ0e+V}YHKD+b#Zp3}#2Nn<4K9YL;;lbuRiZ>75_;pL* zuE^b=w-(*Fe8cs2*`4CsPj9*2xp42)gDnr<-H&|e^yJ%9rf1Wiq&!M_6!vuSbCc(a zPa_{SJhtVoy^!u0Y`Q2S}TkiIqTTgEtyuI#D=AAjWs&AaSas77nz0>#h-W9of z`2MSBdhb*|Ie(q_v+XZG!x4si{~H+kShjJfaYnMOXU=5l;aI`L&Ub`oGS?^0&)hY9 z75pFgKM2$b{t{>tkQZDo#4PG9aYNczZkOCYStHr$vJ&!r@~>q*WzNa?$lEJURORdo*vGH>@V1=*k7>OGynRx_K(-^BR`k_aQ$)P z$MK(fKh3@^{h0pN;f>|HrSBKNGki1W#jPhFA7wt;{LuBG%)`J3{r9KcKYYLD{=R$q z_a5GRc3ThU->TL&4<_X-uZuA{_)aB&M(E^pZpB{75J<7&jNeG%7)82W%4Sd`F&hdlVr^lbCe_{Lj z?pwpprr#-lPyCJeefWpb55XUaKRAA6{$~5b@i+8uz@LmiVgDvF%w+n`EXESY!ppXT z;{sPE7eD(lmb0wW*i+g4*ko9*F~>2VXKrJ0WqZwO#rH{oMQ{!u7ncc(_y5>GEfpc@^aOiwDpN+;9^WXqdH43xI}JB~Uv@jc@2vaz!i(Q8##}gecI}zwvv1Cp zoGCoTcrx)s+{yYgw=PV%a^RZhjb%3%-`sTL=?%|YuWrA+D{}u2=z^AeKOVe!wCeH1 z$BvIV9=AWTdBpK}!js=mTc1@v^?UN;>E>5fAH2Vu`Ev2&!gq7uro3J9=KgE;H#6Qw zeo*-Q?#t@0cHi>9U;fee^V84SzkdDZ`ftv>n)Nj67uHqm8r*3DQKIj~#Uyp5wn#0O zIww^ry;P=OcB2fhRH(R**b8w*={6Y$sUk5x5k`>-q7vfu;tR#+Ni3G!D0y14P+CGx zTR}o`mV&xmg!FwWei>V7b8#;rY5ui*#sap2i}}mBU$Lb#D>Cf=^Yho;-z@)*{8wP$ z`~Ul27}GlTI__@nXpSycWwxapN?f^I%3OAwy&QWuK5{?yE*ToNu zDhSo`Z{>T;dy@MT=N(Qx?i0N41#89lrDn?L$Q8=D$R3drlMs?PB<-Y-s#K}up|oA8 zTRB2ST6M8%t%`=?2HC&T=cFrSQe{hJ=gP3iNXaag(Uc2SxT|nLmPO*Zkb}TEJ~m!m z?tPr^Io@*gacp7hU=d;d$B_Hi{%6Qn$IoBC%>S|P=eKWHKUsYU`%v*|-Iw^U3Sam> ziG1Ap@#LpxpRav2|KalU==bn1RUZ$2H2re)OV~%ZHxVy?zFhof-FwT=4c~=-7yk+P zGw%=6zqkLE|NZid{b%=&Pe0s$HU9DVXY+5tAHm<|zyJMd{-^%$$M2^|>ons0VLpZ?(d{`#xqH_IPyeog%o`ghsCZ~y%m-Z8W>F)^1g zzW=}Vf5-o2|BU~w{r`<=Gix=gF!MCVP^JWyWo*tI4jhx&5?TD1Pp}-|n8~|Butu0g zXeHlo9yh+_0zN`XLWcy7@-g$3@n!P|3)Bd_;(y7nEXXIUA}S`nU*f0aA4wI7v!dpr zp5h0j++>Ai6{Llv_DJc<@XMW$|0AC&zgZzz<+=J<&10JD)gLHl%kxSLNR&(XNGHj7 zNQ#K43Ebez;cpX=5j-pKgFl16pMSAHpU`dLNkUioe7Q0VLC(YzslbR zzv6#?`?LM;p1-&L82@?wC+4s9AFJPrzgPYe{2Bkv_)EyQPk#cLzA-T|D*n&^x9$IJ zMkZ!qW_hNW|EK)P`NPbkb^djc9^1G$)zQ2)p&G`E9n}6@#yx;TQw*|qcZ&Rg53ZE@VlyWVE~;SDo3 zN^aV{NqW<|^;6cYTUEJ+f79P>2e+`4J2@wR!}RCX-erMzd) z?uuPuJJolnZNI*qbGP+=jzg}86c45yTy$1oBhWBt@eBWkF!59eg5~^RatvLHR zcCzzxn(}1umGX*n^|32)h;pa#?GudtW$|n$ShO08cpL`GNoq1-|xHRUsk5(+}{*$Q6DKUG9jDDa$Eass7ef)t4}`Hkx4&pr@hxS1;e_vhfYWd3sxP>~-hsGZ?8GGa7$4Sf-<* z(WQ1oLsNH=!D@r6x&qn_S_wLv^&^eqjqd9^=seSStR|>dt9o0dU0G2K9rha_zmh;u* z=jqSuU;KG4_Vm@mCHF(_@!i*XwB+fO=M$crKNWho>F(CMf)DMVYCQLQI{)G3`&kdB zKa_pA>0bEl12<%E^xovX`{kkdv#{sAPuU*H+&_Q6;ED4~;n%LOzQ0I+A@)-E^_I8I z@Atmn{9ffF#}|k1bAJB#Ir+z;Z$@9Pe+>Gx|6A%G8HR}rQU4%2yg$DFeDP=czjy!4{}=q1 z{vY=LA%hB2IivFbv_GA{I)8EedHFZ;|J?s-|GxfS_iO*J$lsrTC4Oi4X!7Ri>&&-P z-rjw)`}OG;dmbOUD}3|QjYW5-J~Voed3(b37uOElJazZP{qFn6?)BZxzY}x!$b;oi z9y|+pR`s~-LGu0Nhku_Iz54ii*(HiL zG=A6r68c`_^@kT~FP=YVd#>}u!MTlB`{o$BX_KUe;B{+stJ=|{;A+uz{~6Ip+8_;Y>d{Lc~0ShT|$x_2M{V=@{J!1n?P?TH9WKq>BQ^p(pA@9tubGHhx#Y=k7^TC43yR>+?8*Yca)zZw@CJh^e=G* zVG%(;;dJpLDP8H$lF<_9#G54^N}iLlmXeYx}_1DUN-{F2KhA|!jI z>*S8eH^{A!{wkg%;w3a+&_ZyQfERxP?;4(Ed`3c#MgEI82`Ta2;mYFN&RNOb%^Srh z#hb{rn|(LiT=sn&0UT@Cp0ZA6t6<;G;l;g;_YAKDPdS$cmj=%*{y?E^g1`B?xn($` zIp=fx@b2M}D}BQB z=)eQRM=4L6UR-~r^@iotthj*vnSAJ0Y(Di=F z`i`!i=Li-lJxAs%RO(T-f6$z^C9=M-1m1s*Z#8o_3ww$_YL1Z z{W$$+6~kgCCdTJ~+J7ehSpReXAE*Dx|1bXi@%!_tJ%U#J zeY}f!H}mZgU=X<@YAq5VC@yeM;Em8xkrg6}!u3K9!bKuKMf=1ri*t$n75dJ9f%h@b zPo8ex`@GV;Pq-vG^4W6Ojhw-^dji_ zkEhe07C!TPQTS@-YqvK&Zw|kSd$;-1|L@Yji+_Fi+V=j$tMHduuhQQNe-!`h`+3jj z{I9A%zW$QZ2EtSRi7h?J(g`D>v@)FmH?JSRt1h`u1>BOoaeZrcr&mG5;%DatkCf`QxHSF(MI+-spEn`~FG@0oJ(|M-NjE@=4GEQbb&hm&wiKUO_ z1A8HlF~5?)3;u_^_c`~mO#09GC-V2{Kd=A3`7`Th`B%M9Q6FmFi@&pfJ^4kzi?1(V zzG{2<{psU}0{2z!w%opPTk(F;qiK(L9+=-{xWjgL(mlC*Yi{e_EV=&r+SD5icb?u$ zxNmwV?CP1b0Vg9)6rU_Oopnb3wBkv=6K9SKpXfc|bYk=I565qvus`+dbjKNs(`+Zb zj!ii%d^q%I#|ie+uTP&j&2*OkJjeOtXGPB4JvZ^(yt9YSG@O&VDs(6AZrv^68>!c) z-srofef#EZ_4}6}X+D|wr2py9XV+fHy;|~e^^0{ccD#J{`q;ZKA7nml{KWG4#ph{X z>%VLK;`=v+F^73O^D3tIj5nBl+3PuFxSnx!@h%lwCmtf1Cm}1IA=1ddgDaXtlk+Xl z6u~x;GorlW2P9&p)=96FxhC^R`mxkCsVJFJIaB!p*@;q8lJ_KSWcJ9bD2J#_Q!16u zk?D{;ELJ2EAT*ypj3XPC>B$-0-l zn&TTs4%c#CZDCUhHAz?T<)RxzYDKz4kBM1GC`c>5l2ee`DD_kJzk06GYtv1Ji5eT^ z>*VZIa<%5_p4S!D)>N-iJ*4KS!K=PqDPAr}E?aq-)^vSY{S3_;3LKJW#J)=HRESWv zP}G*0BitK2u)^|uPCNnZIZ~OP}tJORAH&5Ste7o_thiMzbrax2vSTbm^^s)T@ z&-g3pyZ9vRrvkQkNJO> zGfQv=b1QPaXZ`&z@7MCb)@)aKXA11$W8_`Ue_2#t`jqr#aeElU|1wqlSj&1??63Nv3v|{pMnZ0-BvFf7LeY zUNh=2dZXj1_E%|^O0K56&K}L}%Dd#8TWv7Z~h};!5lw2$o zCizsfUqFYiOJtS2o!V|?VM!kzJ=QO*E4ibEQpGNcP8MY14dLFv-O6=ThpV-{Fd^q>B82_LBXD!28&Xi_Hif5M>8u=$V5&+Ko~U**2X{fTCBWaDP#|F`-Z*Vh?8H2<9Wo%#LKN2hl) z-iEz5`Y`8hz>CyJ#`jeo?SFaZ1IOp2cV166+`N8S`D)XxHFx9hNZy=vP3_vuYnQGH zUGciyby@hz)T=@_74M4QYq&Y@V#FDbvkI5j-7LK$dCU7+=hbW1U)+3qbN7vwYl@do zT|9a9^4*uu&b~=_Rq?Rx){~n$_bxmMe)aS9>Q`%D9C|kC$*+fj_h;NGx|MnB-<_)u zHa_|EEc?mzJ3QBRUz>AJ;koZ?&NrNID&I)G&3kkGW!>|;Pn944ddT&B@yD9KysXm9 z@;|q}JNDw=^M=>FAJRUReDeO-_ulp0iMKXy)8ELxVSKy%{pT;Ye)s*K^JnANUGK%; zv3=V1WBT83{}~y37!3cn|E>RZ{%iC5KhIMhJ$iWf+2Z%U-|fC#c(dzK+#RM{3vMgl zcX}B3F!q7y{pI&A+~5A7@7}kYZ?EmU&V6UyBe@r0FVdeZxbJx<^v>;jb`NUrH{5-H zEAHlv8*{IVUR!!u`JC&CdB-cyYFtmgU-&@nR^7#grxqQTI(hxfl=FAb7oA&oR`vX+ zixyX+u6)1v@SOJ9-)CoC6u&BV<=(lcCnS${9-Vw* y{p6Aw_lRRg8j`jT93%@Qt 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 literal 0 HcmV?d00001 diff --git a/tensorflow/core/kernels/fuzzing/corpus/decode_wav/782051f8120182b860c7fe1b265179cfa2fe03fd b/tensorflow/core/kernels/fuzzing/corpus/decode_wav/782051f8120182b860c7fe1b265179cfa2fe03fd new file mode 100644 index 0000000000000000000000000000000000000000..a0d8a6ec48c983fc3870d173c5b4c73eb474eecd GIT binary patch literal 66439 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 literal 0 HcmV?d00001 diff --git a/tensorflow/core/kernels/fuzzing/corpus/decode_wav/8210dc595a2652f2f812093b01e239e7918ea065 b/tensorflow/core/kernels/fuzzing/corpus/decode_wav/8210dc595a2652f2f812093b01e239e7918ea065 new file mode 100644 index 0000000000000000000000000000000000000000..46316baf29ff126da46159bf6c16fb11eb9cb23e GIT binary patch literal 55329 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 literal 0 HcmV?d00001 diff --git a/tensorflow/core/kernels/fuzzing/corpus/decode_wav/92c065286f956f086e977556358f6b54b12bcacc b/tensorflow/core/kernels/fuzzing/corpus/decode_wav/92c065286f956f086e977556358f6b54b12bcacc new file mode 100644 index 0000000000000000000000000000000000000000..77b8f518b4c46b2cef89bd7a12f23ea49c8505ab GIT binary patch literal 315 zcmWIYbaQJ+V`K<-40BD(Em06)U|?WmU|@7v!@$rH!N|bGAi$84Sdti%#>nvK>-x_I z->m)?{4e^=_jBtX4Q40y*Bqvt(d;>Fh3t%M-cp)OEX;u%6S(Ja1#z9{>E>(X!)`t! z11|GTG0cAgHD8KNli8bzkpezysmri*M&?Dd?Bcy{mw@XqCC;Cm_X I_Q|U-0IQK?#{d8T literal 0 HcmV?d00001 diff --git a/tensorflow/core/kernels/fuzzing/corpus/decode_wav/a35c9bb71792b60a13dea23a41b41847ad4b93d6 b/tensorflow/core/kernels/fuzzing/corpus/decode_wav/a35c9bb71792b60a13dea23a41b41847ad4b93d6 new file mode 100644 index 0000000000000000000000000000000000000000..45d6b6fa606fe4a25d00476bd4ff785bb80fbba4 GIT binary patch literal 44 xcmWIYbaQJ+V`K<-40BD(Em06)U|?WmU}SJv!@$tt&B(ySAi$842vQZ~2>{n92^atX literal 0 HcmV?d00001 diff --git a/tensorflow/core/kernels/fuzzing/corpus/decode_wav/a6ea960c7b4d42772888280277b26e645ceee904 b/tensorflow/core/kernels/fuzzing/corpus/decode_wav/a6ea960c7b4d42772888280277b26e645ceee904 new file mode 100644 index 0000000000000000000000000000000000000000..14954c595882e6d35c5f77f36dfcb9ad315c606c GIT binary patch literal 22470 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≫`yR2!n1|AgzgJ8^LO(d=MCUp!;{NBmun{1O-@;k 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�`dIeu@6Q!K)xX{NxZ<7Ro2-{L&mTRvfBE&r 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

2u?j5^`Y2=~c!F;R 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 literal 0 HcmV?d00001 diff --git a/tensorflow/core/kernels/fuzzing/corpus/decode_wav/f38c61da15f2cb7a39ff02e69f0b00e99f37ec86 b/tensorflow/core/kernels/fuzzing/corpus/decode_wav/f38c61da15f2cb7a39ff02e69f0b00e99f37ec86 new file mode 100644 index 0000000000000000000000000000000000000000..0900eb1352b10576bebda11b0e2b943d77877d57 GIT binary patch literal 66555 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

p|CUUfqAK?1t+tjXQexoFAw@s(GyY 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 rum@O>EoE$4hU literal 0 HcmV?d00001 diff --git a/tensorflow/core/kernels/fuzzing/corpus/decode_wav/fa79819c5de04bc06c69bec3fa7f2e982826ea2f b/tensorflow/core/kernels/fuzzing/corpus/decode_wav/fa79819c5de04bc06c69bec3fa7f2e982826ea2f new file mode 100644 index 0000000000000000000000000000000000000000..2fd41a34fe5b503756e93b68aaa4f90301a78950 GIT binary patch literal 20280 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 literal 0 HcmV?d00001 diff --git a/tensorflow/core/kernels/fuzzing/corpus/decode_wav/fce08de222896ac3a20657a3b4f42d5b6c54a96a b/tensorflow/core/kernels/fuzzing/corpus/decode_wav/fce08de222896ac3a20657a3b4f42d5b6c54a96a new file mode 100644 index 0000000000000000000000000000000000000000..35a99bc97d93c9beeea6a917453685fabf853ab2 GIT binary patch literal 69522 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 K7!3h>hX4Te88qSm literal 0 HcmV?d00001 diff --git a/tensorflow/core/kernels/fuzzing/corpus/string_split/4c01a1504da9de2216894743ecc44424 b/tensorflow/core/kernels/fuzzing/corpus/string_split/4c01a1504da9de2216894743ecc44424 new file mode 100644 index 0000000000..eb84b9e610 --- /dev/null +++ b/tensorflow/core/kernels/fuzzing/corpus/string_split/4c01a1504da9de2216894743ecc44424 @@ -0,0 +1 @@ +./,abcd.efgh/abcd,efgh.abcd/efgh,abcd.efgh/a \ No newline at end of file diff --git a/tensorflow/core/kernels/fuzzing/corpus/string_split/5bf16424630b5afbcffe711fb9834440 b/tensorflow/core/kernels/fuzzing/corpus/string_split/5bf16424630b5afbcffe711fb9834440 new file mode 100644 index 0000000000..4cd522da7b --- /dev/null +++ b/tensorflow/core/kernels/fuzzing/corpus/string_split/5bf16424630b5afbcffe711fb9834440 @@ -0,0 +1 @@ +.ab.cd.ef.gh.ab.cd.ef.gh.ab.cd.ef.gh.ab.cd \ No newline at end of file diff --git a/tensorflow/core/kernels/fuzzing/corpus/string_split/a7185605aef0a8fd682fcb4656e4a736 b/tensorflow/core/kernels/fuzzing/corpus/string_split/a7185605aef0a8fd682fcb4656e4a736 new file mode 100644 index 0000000000..03cfb6256f --- /dev/null +++ b/tensorflow/core/kernels/fuzzing/corpus/string_split/a7185605aef0a8fd682fcb4656e4a736 @@ -0,0 +1 @@ +./, abcde.fghab/cdefg,habcd efgha.bcdef/ghabc \ No newline at end of file diff --git a/tensorflow/core/kernels/fuzzing/corpus/string_split/d5606def44fdbb9385dd764612069db0 b/tensorflow/core/kernels/fuzzing/corpus/string_split/d5606def44fdbb9385dd764612069db0 new file mode 100644 index 0000000000000000000000000000000000000000..304b0d66fe08fd1a29827488727702dd9b9bce3e GIT binary patch literal 42 ScmZQbOiE5kO-s)pNCN;-W)Kzt literal 0 HcmV?d00001 diff --git a/tensorflow/core/kernels/fuzzing/corpus/string_split/dbac766f3160de65894bf5153f478146 b/tensorflow/core/kernels/fuzzing/corpus/string_split/dbac766f3160de65894bf5153f478146 new file mode 100644 index 0000000000..a8740444aa --- /dev/null +++ b/tensorflow/core/kernels/fuzzing/corpus/string_split/dbac766f3160de65894bf5153f478146 @@ -0,0 +1 @@ +./, ?abcdef.ghabcd/efghab,cdefgh abcdef?ghabcd \ No newline at end of file diff --git a/tensorflow/core/kernels/fuzzing/corpus/string_split/e85ff62f6d457666f54a37a19a115a24 b/tensorflow/core/kernels/fuzzing/corpus/string_split/e85ff62f6d457666f54a37a19a115a24 new file mode 100644 index 0000000000..47d551466a --- /dev/null +++ b/tensorflow/core/kernels/fuzzing/corpus/string_split/e85ff62f6d457666f54a37a19a115a24 @@ -0,0 +1 @@ +./abc.def/gha.bcd/efg.hab/cde.fgh/abc.def/g \ No newline at end of file diff --git a/tensorflow/core/kernels/fuzzing/corpus/string_split_v2/00fd47bf73afcb72e7ed51bffd5f5fec b/tensorflow/core/kernels/fuzzing/corpus/string_split_v2/00fd47bf73afcb72e7ed51bffd5f5fec new file mode 100644 index 0000000000..f1410e184b --- /dev/null +++ b/tensorflow/core/kernels/fuzzing/corpus/string_split_v2/00fd47bf73afcb72e7ed51bffd5f5fec @@ -0,0 +1 @@ +./abc./de./fg./ha./bc./de./fg./ha./bc./de./ \ No newline at end of file diff --git a/tensorflow/core/kernels/fuzzing/corpus/string_split_v2/14908973e6720513a5f37676cb9fcc29 b/tensorflow/core/kernels/fuzzing/corpus/string_split_v2/14908973e6720513a5f37676cb9fcc29 new file mode 100644 index 0000000000..e118d2d351 --- /dev/null +++ b/tensorflow/core/kernels/fuzzing/corpus/string_split_v2/14908973e6720513a5f37676cb9fcc29 @@ -0,0 +1 @@ +./, abcde./, fg./, ha./, bc./, de./, fg./, ha \ No newline at end of file diff --git a/tensorflow/core/kernels/fuzzing/corpus/string_split_v2/2779ba7c4d23eee9f79efa3660084c5d b/tensorflow/core/kernels/fuzzing/corpus/string_split_v2/2779ba7c4d23eee9f79efa3660084c5d new file mode 100644 index 0000000000..9a6c809197 --- /dev/null +++ b/tensorflow/core/kernels/fuzzing/corpus/string_split_v2/2779ba7c4d23eee9f79efa3660084c5d @@ -0,0 +1 @@ +./,abcd./,ef./,gh./,ab./,cd./,ef./,gh./,ab./ \ No newline at end of file diff --git a/tensorflow/core/kernels/fuzzing/corpus/string_split_v2/5bf16424630b5afbcffe711fb9834440 b/tensorflow/core/kernels/fuzzing/corpus/string_split_v2/5bf16424630b5afbcffe711fb9834440 new file mode 100644 index 0000000000..4cd522da7b --- /dev/null +++ b/tensorflow/core/kernels/fuzzing/corpus/string_split_v2/5bf16424630b5afbcffe711fb9834440 @@ -0,0 +1 @@ +.ab.cd.ef.gh.ab.cd.ef.gh.ab.cd.ef.gh.ab.cd \ No newline at end of file diff --git a/tensorflow/core/kernels/fuzzing/corpus/string_split_v2/89734a96b93275e495a9498b806fafe1 b/tensorflow/core/kernels/fuzzing/corpus/string_split_v2/89734a96b93275e495a9498b806fafe1 new file mode 100644 index 0000000000..5301a91d8e --- /dev/null +++ b/tensorflow/core/kernels/fuzzing/corpus/string_split_v2/89734a96b93275e495a9498b806fafe1 @@ -0,0 +1 @@ +./, ?abcdef./, ?gh./, ?ab./, ?cd./, ?ef./, ?gh \ No newline at end of file diff --git a/tensorflow/core/kernels/fuzzing/corpus/string_split_v2/d5606def44fdbb9385dd764612069db0 b/tensorflow/core/kernels/fuzzing/corpus/string_split_v2/d5606def44fdbb9385dd764612069db0 new file mode 100644 index 0000000000000000000000000000000000000000..304b0d66fe08fd1a29827488727702dd9b9bce3e GIT binary patch literal 42 ScmZQbOiE5kO-s)pNCN;-W)Kzt literal 0 HcmV?d00001 diff --git a/tensorflow/core/kernels/fuzzing/corpus/string_to_number/2db83ea58639b6d7d585fa12e3947a82 b/tensorflow/core/kernels/fuzzing/corpus/string_to_number/2db83ea58639b6d7d585fa12e3947a82 new file mode 100644 index 0000000000..3de80927d5 --- /dev/null +++ b/tensorflow/core/kernels/fuzzing/corpus/string_to_number/2db83ea58639b6d7d585fa12e3947a82 @@ -0,0 +1 @@ +6.023e+23 diff --git a/tensorflow/core/kernels/fuzzing/corpus/string_to_number/36b4a931886b941dc41180050d12ca94 b/tensorflow/core/kernels/fuzzing/corpus/string_to_number/36b4a931886b941dc41180050d12ca94 new file mode 100644 index 0000000000..d531129b28 --- /dev/null +++ b/tensorflow/core/kernels/fuzzing/corpus/string_to_number/36b4a931886b941dc41180050d12ca94 @@ -0,0 +1 @@ +6.023e-23 diff --git a/tensorflow/core/kernels/fuzzing/corpus/string_to_number/50a2fabfdd276f573ff97ace8b11c5f4 b/tensorflow/core/kernels/fuzzing/corpus/string_to_number/50a2fabfdd276f573ff97ace8b11c5f4 new file mode 100644 index 0000000000..d81cc0710e --- /dev/null +++ b/tensorflow/core/kernels/fuzzing/corpus/string_to_number/50a2fabfdd276f573ff97ace8b11c5f4 @@ -0,0 +1 @@ +42 diff --git a/tensorflow/core/kernels/fuzzing/corpus/string_to_number/62edb2a1eee34b001652cd86584becf2 b/tensorflow/core/kernels/fuzzing/corpus/string_to_number/62edb2a1eee34b001652cd86584becf2 new file mode 100644 index 0000000000..72f88139d0 --- /dev/null +++ b/tensorflow/core/kernels/fuzzing/corpus/string_to_number/62edb2a1eee34b001652cd86584becf2 @@ -0,0 +1 @@ +0xabcdef diff --git a/tensorflow/core/kernels/fuzzing/corpus/string_to_number/90013d1ec28c46a5c00574e60c70b6fc b/tensorflow/core/kernels/fuzzing/corpus/string_to_number/90013d1ec28c46a5c00574e60c70b6fc new file mode 100644 index 0000000000..c1113b83e8 --- /dev/null +++ b/tensorflow/core/kernels/fuzzing/corpus/string_to_number/90013d1ec28c46a5c00574e60c70b6fc @@ -0,0 +1 @@ +3.14159265359 diff --git a/tensorflow/core/kernels/fuzzing/corpus/string_to_number/94f3e3cee6957ce5815326d6788c85f4 b/tensorflow/core/kernels/fuzzing/corpus/string_to_number/94f3e3cee6957ce5815326d6788c85f4 new file mode 100644 index 0000000000..320aa3f00e --- /dev/null +++ b/tensorflow/core/kernels/fuzzing/corpus/string_to_number/94f3e3cee6957ce5815326d6788c85f4 @@ -0,0 +1 @@ +0.69314718056 diff --git a/tensorflow/core/kernels/fuzzing/corpus/string_to_number/96f547bc04bb913da0bc08915238ebd8 b/tensorflow/core/kernels/fuzzing/corpus/string_to_number/96f547bc04bb913da0bc08915238ebd8 new file mode 100644 index 0000000000..51b7b732f6 --- /dev/null +++ b/tensorflow/core/kernels/fuzzing/corpus/string_to_number/96f547bc04bb913da0bc08915238ebd8 @@ -0,0 +1 @@ +6.023e23 diff --git a/tensorflow/core/kernels/fuzzing/corpus/string_to_number/d3a903d18fc11e1f35c572ad4da690ed b/tensorflow/core/kernels/fuzzing/corpus/string_to_number/d3a903d18fc11e1f35c572ad4da690ed new file mode 100644 index 0000000000..9a0be0764b --- /dev/null +++ b/tensorflow/core/kernels/fuzzing/corpus/string_to_number/d3a903d18fc11e1f35c572ad4da690ed @@ -0,0 +1 @@ +1.61803 diff --git a/tensorflow/core/kernels/fuzzing/corpus/string_to_number/e3b629c92af44260c189deb32d6f06f3 b/tensorflow/core/kernels/fuzzing/corpus/string_to_number/e3b629c92af44260c189deb32d6f06f3 new file mode 100644 index 0000000000..6a0e60d48b --- /dev/null +++ b/tensorflow/core/kernels/fuzzing/corpus/string_to_number/e3b629c92af44260c189deb32d6f06f3 @@ -0,0 +1 @@ +-42 diff --git a/tensorflow/core/kernels/fuzzing/corpus/string_to_number/f03eecf3bcfe4967a1888156a3115c8d b/tensorflow/core/kernels/fuzzing/corpus/string_to_number/f03eecf3bcfe4967a1888156a3115c8d new file mode 100644 index 0000000000..ea9cd255bc --- /dev/null +++ b/tensorflow/core/kernels/fuzzing/corpus/string_to_number/f03eecf3bcfe4967a1888156a3115c8d @@ -0,0 +1 @@ +6.023E+23 diff --git a/tensorflow/core/kernels/fuzzing/corpus/string_to_number/fa54ca9186f77122ae2a82684a062e16 b/tensorflow/core/kernels/fuzzing/corpus/string_to_number/fa54ca9186f77122ae2a82684a062e16 new file mode 100644 index 0000000000..00f1e2ed8f --- /dev/null +++ b/tensorflow/core/kernels/fuzzing/corpus/string_to_number/fa54ca9186f77122ae2a82684a062e16 @@ -0,0 +1 @@ +2.71828182846 diff --git a/tensorflow/core/kernels/fuzzing/dictionaries/decode_json_example.dict b/tensorflow/core/kernels/fuzzing/dictionaries/decode_json_example.dict new file mode 100644 index 0000000000..5fe4ca23d1 --- /dev/null +++ b/tensorflow/core/kernels/fuzzing/dictionaries/decode_json_example.dict @@ -0,0 +1,6 @@ +"features" +"feature" +"bytes_list" +"float_list" +"int64_list" +"value" diff --git a/tensorflow/core/kernels/fuzzing/dictionaries/decode_png.dict b/tensorflow/core/kernels/fuzzing/dictionaries/decode_png.dict new file mode 100644 index 0000000000..d795ae7f71 --- /dev/null +++ b/tensorflow/core/kernels/fuzzing/dictionaries/decode_png.dict @@ -0,0 +1,50 @@ +header_87a="87a" +header_89a="89a" +header_gif="GIF" +header_jfif="JFIF\x00" +header_jfxx="JFXX\x00" +header_png="\x89PNG\x0d\x0a\x1a\x0a" +marker_2c="," +marker_3b=";" +section_2101="!\x01\x12" +section_21f9="!\xf9\x04" +section_21fe="!\xfe" +section_21ff="!\xff\x11" +section_IDAT="IDAT" +section_IEND="IEND" +section_IHDR="IHDR" +section_PLTE="PLTE" +section_bKGD="bKGD" +section_cHRM="cHRM" +section_fRAc="fRAc" +section_ffc0="\xff\xc0" +section_ffc2="\xff\xc2" +section_ffc4="\xff\xc4" +section_ffd0="\xff\xd0" +section_ffd8="\xff\xd8" +section_ffd9="\xff\xd9" +section_ffda="\xff\xda" +section_ffdb="\xff\xdb" +section_ffdd="\xff\xdd" +section_ffe0="\xff\xe0" +section_ffe1="\xff\xe1" +section_fffe="\xff\xfe" +section_gAMA="gAMA" +section_gIFg="gIFg" +section_gIFt="gIFt" +section_gIFx="gIFx" +section_hIST="hIST" +section_iCCP="iCCP" +section_iTXt="iTXt" +section_oFFs="oFFs" +section_pCAL="pCAL" +section_pHYs="pHYs" +section_sBIT="sBIT" +section_sCAL="sCAL" +section_sPLT="sPLT" +section_sRGB="sRGB" +section_sTER="sTER" +section_tEXt="tEXt" +section_tIME="tIME" +section_tRNS="tRNS" +section_zTXt="zTXt" diff --git a/tensorflow/core/kernels/fuzzing/dictionaries/decode_wav.dict b/tensorflow/core/kernels/fuzzing/dictionaries/decode_wav.dict new file mode 100644 index 0000000000..eab65386ce --- /dev/null +++ b/tensorflow/core/kernels/fuzzing/dictionaries/decode_wav.dict @@ -0,0 +1,4 @@ +header_RIFF="RIFF" +header_WAVE="WAVE" +section_fmt="fmt " +section_data="data" diff --git a/tensorflow/core/kernels/fuzzing/encode_base64_fuzz.cc b/tensorflow/core/kernels/fuzzing/encode_base64_fuzz.cc index a8f07f4bad..b8d779fb13 100644 --- a/tensorflow/core/kernels/fuzzing/encode_base64_fuzz.cc +++ b/tensorflow/core/kernels/fuzzing/encode_base64_fuzz.cc @@ -19,7 +19,7 @@ limitations under the License. namespace tensorflow { namespace fuzzing { -class FuzzEncodeBase64 : public FuzzSession { +class FuzzEncodeBase64 : public FuzzStringInputOp { SINGLE_INPUT_OP_BUILDER(DT_STRING, EncodeBase64); }; diff --git a/tensorflow/core/kernels/fuzzing/fuzz_session.h b/tensorflow/core/kernels/fuzzing/fuzz_session.h index 9777be1ae8..57d562ddf4 100644 --- a/tensorflow/core/kernels/fuzzing/fuzz_session.h +++ b/tensorflow/core/kernels/fuzzing/fuzz_session.h @@ -72,11 +72,11 @@ class FuzzSession { // By convention, the graph should have inputs named "input1", ... // "inputN", and one output node, named "output". // Users of FuzzSession should override this method to create their graph. - virtual void BuildGraph(const Scope& scope) {} + virtual void BuildGraph(const Scope& scope) = 0; // Implements the logic that converts an opaque byte buffer // from the fuzzer to Tensor inputs to the graph. Users must override. - virtual void FuzzImpl(const uint8_t* data, size_t size) {} + virtual void FuzzImpl(const uint8_t* data, size_t size) = 0; // Initializes the FuzzSession. Not safe for multithreading. // Separate init function because the call to virtual BuildGraphDef diff --git a/tensorflow/core/kernels/fuzzing/string_split_fuzz.cc b/tensorflow/core/kernels/fuzzing/string_split_fuzz.cc index 87a548a999..2564f8ed03 100644 --- a/tensorflow/core/kernels/fuzzing/string_split_fuzz.cc +++ b/tensorflow/core/kernels/fuzzing/string_split_fuzz.cc @@ -37,8 +37,7 @@ class FuzzStringSplit : public FuzzSession { // The spec for split is that the delimeter should be 0 or 1 characters. // Naturally, fuzz it with something larger. (This omits the possibility // of handing it a > int32_max size string, which should be tested for in - // an - // explicit test). + // an explicit test). size_t delim_len = static_cast(data[0]); if (delim_len > size) { delim_len = size - 1; diff --git a/tensorflow/core/kernels/fuzzing/tf_ops_fuzz_target_lib.bzl b/tensorflow/core/kernels/fuzzing/tf_ops_fuzz_target_lib.bzl index f752b59568..e932213359 100644 --- a/tensorflow/core/kernels/fuzzing/tf_ops_fuzz_target_lib.bzl +++ b/tensorflow/core/kernels/fuzzing/tf_ops_fuzz_target_lib.bzl @@ -1,13 +1,25 @@ """Fuzzing template for TensorFlow ops.""" def tf_ops_fuzz_target_lib(name): - native.cc_library( - name = name + "_fuzz_lib", - srcs = [name + "_fuzz.cc"], - deps = [ - "//tensorflow/core/kernels/fuzzing:fuzz_session", - "//tensorflow/cc:cc_ops", - ], - tags = ["no_windows"], - alwayslink = 1, - ) + native.cc_library( + name = name + "_fuzz_lib", + srcs = [name + "_fuzz.cc"], + deps = [ + "//tensorflow/core/kernels/fuzzing:fuzz_session", + "//tensorflow/cc:cc_ops", + ], + tags = ["no_windows"], + alwayslink = 1, + ) + +def tf_oss_fuzz_corpus(name): + native.filegroup( + name = name + "_corpus", + srcs = native.glob(["corpus/" + name + "/*"]), + ) + +def tf_oss_fuzz_dict(name): + native.filegroup( + name = name + "_dict", + srcs = native.glob(["dictionaries/" + name + ".dict"]), + ) diff --git a/tensorflow/core/kernels/list_kernels.cc b/tensorflow/core/kernels/list_kernels.cc index 5f244b1b10..42fad1d4b0 100644 --- a/tensorflow/core/kernels/list_kernels.cc +++ b/tensorflow/core/kernels/list_kernels.cc @@ -483,9 +483,19 @@ REGISTER_KERNEL_BUILDER(Name("TensorListGetItem").Device(DEVICE_CPU), #if GOOGLE_CUDA -REGISTER_KERNEL_BUILDER( - Name("TensorListGetItem").Device(DEVICE_GPU).HostMemory("index"), - TensorListGetItem); +#define REGISTER_TENSOR_LIST_GET_ITEM_GPU(T) \ + REGISTER_KERNEL_BUILDER(Name("TensorListGetItem") \ + .TypeConstraint("element_dtype") \ + .Device(DEVICE_GPU) \ + .HostMemory("index"), \ + TensorListGetItem); + +TF_CALL_GPU_NUMBER_TYPES(REGISTER_TENSOR_LIST_GET_ITEM_GPU); +TF_CALL_complex64(REGISTER_TENSOR_LIST_GET_ITEM_GPU); +TF_CALL_complex128(REGISTER_TENSOR_LIST_GET_ITEM_GPU); +TF_CALL_int64(REGISTER_TENSOR_LIST_GET_ITEM_GPU); +REGISTER_TENSOR_LIST_GET_ITEM_GPU(bfloat16) +#undef REGISTER_TENSOR_LIST_GET_ITEM_GPU #endif // GOOGLE_CUDA @@ -537,9 +547,19 @@ REGISTER_KERNEL_BUILDER(Name("TensorListSetItem").Device(DEVICE_CPU), #if GOOGLE_CUDA -REGISTER_KERNEL_BUILDER( - Name("TensorListSetItem").Device(DEVICE_GPU).HostMemory("index"), - TensorListSetItem); +#define REGISTER_TENSOR_LIST_SET_ITEM_GPU(T) \ + REGISTER_KERNEL_BUILDER(Name("TensorListSetItem") \ + .TypeConstraint("element_dtype") \ + .Device(DEVICE_GPU) \ + .HostMemory("index"), \ + TensorListSetItem); + +TF_CALL_GPU_NUMBER_TYPES(REGISTER_TENSOR_LIST_SET_ITEM_GPU); +TF_CALL_complex64(REGISTER_TENSOR_LIST_SET_ITEM_GPU); +TF_CALL_complex128(REGISTER_TENSOR_LIST_SET_ITEM_GPU); +TF_CALL_int64(REGISTER_TENSOR_LIST_SET_ITEM_GPU); +REGISTER_TENSOR_LIST_SET_ITEM_GPU(bfloat16) +#undef REGISTER_TENSOR_LIST_SET_ITEM_GPU #endif // GOOGLE_CUDA @@ -660,7 +680,11 @@ REGISTER_TENSOR_LIST_PUSH_BACK_BATCH_CPU(bfloat16); REGISTER_KERNEL_BUILDER(Name("TensorListGather") \ .TypeConstraint("element_dtype") \ .Device(DEVICE_CPU), \ - TensorListGather) + TensorListGather) \ + REGISTER_KERNEL_BUILDER(Name("TensorListConcat") \ + .TypeConstraint("element_dtype") \ + .Device(DEVICE_CPU), \ + TensorListConcat) TF_CALL_POD_STRING_TYPES(REGISTER_TENSOR_LIST_STACK_CPU); REGISTER_TENSOR_LIST_STACK_CPU(quint8); @@ -680,7 +704,11 @@ REGISTER_TENSOR_LIST_STACK_CPU(bfloat16); REGISTER_KERNEL_BUILDER(Name("TensorListScatter") \ .TypeConstraint("element_dtype") \ .Device(DEVICE_CPU), \ - TensorListScatter) + TensorListScatter) \ + REGISTER_KERNEL_BUILDER(Name("TensorListSplit") \ + .TypeConstraint("element_dtype") \ + .Device(DEVICE_CPU), \ + TensorListSplit) TF_CALL_POD_STRING_TYPES(REGISTER_TENSOR_LIST_FROM_TENSOR_CPU); REGISTER_TENSOR_LIST_FROM_TENSOR_CPU(quint8); diff --git a/tensorflow/core/kernels/list_kernels.cu.cc b/tensorflow/core/kernels/list_kernels.cu.cc index a00bf700ca..23f552642c 100644 --- a/tensorflow/core/kernels/list_kernels.cu.cc +++ b/tensorflow/core/kernels/list_kernels.cu.cc @@ -45,7 +45,12 @@ typedef Eigen::GpuDevice GPUDevice; .TypeConstraint("element_dtype") \ .Device(DEVICE_GPU) \ .HostMemory("indices"), \ - TensorListGather) + TensorListGather) \ + REGISTER_KERNEL_BUILDER(Name("TensorListConcat") \ + .TypeConstraint("element_dtype") \ + .Device(DEVICE_GPU) \ + .HostMemory("lengths"), \ + TensorListConcat) TF_CALL_GPU_NUMBER_TYPES(REGISTER_TENSOR_LIST_STACK_GPU); REGISTER_TENSOR_LIST_STACK_GPU(bfloat16); @@ -82,7 +87,13 @@ REGISTER_TENSOR_LIST_PUSH_BACK_BATCH_GPU(bool); .Device(DEVICE_GPU) \ .HostMemory("element_shape") \ .HostMemory("indices"), \ - TensorListScatter) + TensorListScatter) \ + REGISTER_KERNEL_BUILDER(Name("TensorListSplit") \ + .TypeConstraint("element_dtype") \ + .Device(DEVICE_GPU) \ + .HostMemory("element_shape") \ + .HostMemory("lengths"), \ + TensorListSplit) TF_CALL_GPU_NUMBER_TYPES(REGISTER_TENSOR_LIST_FROM_TENSOR_GPU); REGISTER_TENSOR_LIST_FROM_TENSOR_GPU(bfloat16); diff --git a/tensorflow/core/kernels/list_kernels.h b/tensorflow/core/kernels/list_kernels.h index c2591f5314..686679474c 100644 --- a/tensorflow/core/kernels/list_kernels.h +++ b/tensorflow/core/kernels/list_kernels.h @@ -30,6 +30,8 @@ limitations under the License. #include "tensorflow/core/kernels/concat_lib.h" #include "tensorflow/core/lib/core/coding.h" #include "tensorflow/core/lib/core/errors.h" +#include "tensorflow/core/lib/gtl/array_slice.h" +#include "tensorflow/core/util/tensor_ops_util.h" #include "tensorflow/core/util/util.h" namespace tensorflow { @@ -76,26 +78,30 @@ class TensorListStack : public OpKernel { ~TensorListStack() {} void Compute(OpKernelContext* c) override { - const TensorList* l = c->input(0).scalar()().get(); - OP_REQUIRES(c, l != nullptr, + const TensorList* tensor_list = + c->input(0).scalar()().get(); + OP_REQUIRES(c, tensor_list != nullptr, errors::InvalidArgument( "Input handle is not a list. Saw: '", c->input(0).scalar()().DebugString(), "'")); - OP_REQUIRES(c, element_dtype_ == l->element_dtype, - errors::InvalidArgument("Invalid data types; op elements ", - DataTypeString(element_dtype_), - " but list elements ", - DataTypeString(l->element_dtype))); - OP_REQUIRES(c, !l->tensors.empty() || l->element_shape.IsFullyDefined(), - errors::InvalidArgument("Tried to stack elements of a empty ", - "list with non-fully-defined shape: ", - l->element_shape.DebugString())); + OP_REQUIRES( + c, element_dtype_ == tensor_list->element_dtype, + errors::InvalidArgument( + "Invalid data types; op elements ", DataTypeString(element_dtype_), + " but list elements ", DataTypeString(tensor_list->element_dtype))); + OP_REQUIRES( + c, + !tensor_list->tensors.empty() || + tensor_list->element_shape.IsFullyDefined(), + errors::InvalidArgument("Tried to stack elements of a empty ", + "list with non-fully-defined shape: ", + tensor_list->element_shape.DebugString())); if (num_elements_ != -1) { - OP_REQUIRES(c, l->tensors.size() == num_elements_, - errors::InvalidArgument("Operation expected a list with ", - num_elements_, - " elements but got a list with ", - l->tensors.size(), " elements.")); + OP_REQUIRES(c, tensor_list->tensors.size() == num_elements_, + errors::InvalidArgument( + "Operation expected a list with ", num_elements_, + " elements but got a list with ", + tensor_list->tensors.size(), " elements.")); } // Compute the shape of the output tensor. // If `element_shape` is fully-defined it gets used. It is assumed that all @@ -104,11 +110,11 @@ class TensorListStack : public OpKernel { // tensor is used and it is checked that all other tensors have the same // shape. TensorShape resulting_shape; - if (!l->element_shape.AsTensorShape(&resulting_shape)) { - const Tensor& t = l->tensors[0]; + if (!tensor_list->element_shape.AsTensorShape(&resulting_shape)) { + const Tensor& t = tensor_list->tensors[0]; resulting_shape = t.shape(); - for (int i = 1; i < l->tensors.size(); ++i) { - const Tensor& t = l->tensors[i]; + for (int i = 1; i < tensor_list->tensors.size(); ++i) { + const Tensor& t = tensor_list->tensors[i]; OP_REQUIRES(c, t.shape() == resulting_shape, errors::InvalidArgument( "Tried to stack tensors with unequal shapes: ", @@ -116,7 +122,7 @@ class TensorListStack : public OpKernel { t.shape().DebugString())); } } - resulting_shape.InsertDim(0, l->tensors.size()); + resulting_shape.InsertDim(0, tensor_list->tensors.size()); Tensor* output; OP_REQUIRES_OK(c, c->allocate_output(0, resulting_shape, &output)); if (output->NumElements() == 0) { @@ -124,8 +130,8 @@ class TensorListStack : public OpKernel { } ConstMatrixVector inputs_flat; - inputs_flat.reserve(l->tensors.size()); - for (const auto& t : l->tensors) { + inputs_flat.reserve(tensor_list->tensors.size()); + for (const auto& t : tensor_list->tensors) { inputs_flat.emplace_back(new typename TTypes::ConstMatrix( t.shaped({1, t.NumElements()}))); } @@ -145,6 +151,200 @@ class TensorListStack : public OpKernel { DataType element_dtype_; }; +template +class TensorListConcat : public OpKernel { + public: + using ConstMatrixVector = + std::vector::ConstMatrix>>; + explicit TensorListConcat(OpKernelConstruction* c) : OpKernel(c) { + OP_REQUIRES_OK(c, c->GetAttr("element_dtype", &element_dtype_)); + } + + ~TensorListConcat() {} + + void Compute(OpKernelContext* c) override { + // Check that the input Variant tensor is indeed a TensorList and has the + // correct element type. + const TensorList* tensor_list = + c->input(0).scalar()().get(); + OP_REQUIRES(c, tensor_list != nullptr, + errors::InvalidArgument( + "Input handle is not a list. Saw: '", + c->input(0).scalar()().DebugString(), "'")); + OP_REQUIRES( + c, element_dtype_ == tensor_list->element_dtype, + errors::InvalidArgument( + "Invalid data types; op elements ", DataTypeString(element_dtype_), + " but list elements ", DataTypeString(tensor_list->element_dtype))); + // If the TensorList is empty, its element_shape must be fully defined + // except for the first dimension. + PartialTensorShape shape_except_first_dim; + if (!tensor_list->element_shape.unknown_rank()) { + OP_REQUIRES(c, tensor_list->element_shape.dims() >= 1, + errors::InvalidArgument( + "Concat requires elements to be at least vectors, ", + "found scalars instead.")); + shape_except_first_dim = PartialTensorShape( + gtl::ArraySlice(tensor_list->element_shape.dim_sizes()) + .subspan(1)); + } + OP_REQUIRES(c, + !tensor_list->tensors.empty() || + shape_except_first_dim.IsFullyDefined(), + errors::InvalidArgument( + "All except the first dimension must be fully defined ", + "when concating an empty tensor list. element_shape: ", + tensor_list->element_shape.DebugString())); + // 1. Compute the shape of the output tensor. + // If `shape_except_first_dim` is fully-defined we just prepend the leading + // dim to it. Otherwise we use the shape of the first element tensor and + // check to make sure shapes of all tensors are compatible. + TensorShape output_shape; + if (!shape_except_first_dim.AsTensorShape(&output_shape)) { + const Tensor& element_tensor = tensor_list->tensors[0]; + OP_REQUIRES( + c, TensorShapeUtils::IsVectorOrHigher(element_tensor.shape()), + errors::InvalidArgument("Concat saw a scalar shape at index ", 0, + " but requires at least vectors.")); + output_shape = + TensorShape(gtl::ArraySlice(element_tensor.shape().dim_sizes()) + .subspan(1)); + for (int i = 1; i < tensor_list->tensors.size(); ++i) { + const Tensor& element_tensor = tensor_list->tensors[i]; + OP_REQUIRES( + c, TensorShapeUtils::IsVectorOrHigher(element_tensor.shape()), + errors::InvalidArgument("Concat saw a scalar shape at index ", i, + " but requires at least vectors.")); + TensorShape actual_shape( + gtl::ArraySlice(element_tensor.shape().dim_sizes()) + .subspan(1)); + OP_REQUIRES(c, actual_shape.dim_sizes() == output_shape.dim_sizes(), + errors::InvalidArgument( + "Tried to concat tensors with unequal shapes: ", + output_shape.DebugString(), " vs ", + actual_shape.DebugString())); + } + } + // 2. Build the lengths_tensor and leading dim of the output tensor by + // iterating over all element tensors. + Tensor* lengths_tensor = nullptr; + OP_REQUIRES_OK( + c, + c->allocate_output( + 1, TensorShape({static_cast(tensor_list->tensors.size())}), + &lengths_tensor)); + auto lengths_tensor_vec = lengths_tensor->vec(); + int64 leading_dim = 0; + for (size_t i = 0; i < tensor_list->tensors.size(); i++) { + int64 dim = tensor_list->tensors[i].shape().dim_size(0); + leading_dim += dim; + lengths_tensor_vec(i) = dim; + } + output_shape.InsertDim(0, leading_dim); + Tensor* output; + // 3. Allocate the output tensor and fill it up with the concated element + // tensors. + OP_REQUIRES_OK(c, c->allocate_output(0, output_shape, &output)); + if (output->NumElements() == 0) { + return; + } + + ConstMatrixVector inputs_flat; + inputs_flat.reserve(tensor_list->tensors.size()); + for (const auto& element_tensor : tensor_list->tensors) { + inputs_flat.emplace_back(new typename TTypes::ConstMatrix( + element_tensor.shaped({1, element_tensor.NumElements()}))); + } + auto output_flat = output->shaped({1, output->NumElements()}); + +#if GOOGLE_CUDA + if (std::is_same::value) { + ConcatGPU(c, inputs_flat, output, &output_flat); + return; + } +#endif // GOOGLE_CUDA + ConcatCPU(c->device(), inputs_flat, &output_flat); + } + + private: + DataType element_dtype_; +}; + +template +class TensorListSplit : public OpKernel { + public: + TensorListSplit(OpKernelConstruction* c) : OpKernel(c) {} + + void Compute(OpKernelContext* c) override { + Tensor* output_tensor; + AllocatorAttributes attr; + attr.set_on_host(true); + OP_REQUIRES_OK(c, c->allocate_output(0, {}, &output_tensor, attr)); + PartialTensorShape element_shape; + OP_REQUIRES_OK(c, TensorShapeFromTensor(c->input(1), &element_shape)); + OP_REQUIRES(c, element_shape.unknown_rank() || element_shape.dims() >= 1, + errors::InvalidArgument( + "TensorListSplit requires element_shape to be at least of ", + "rank 1, but saw: ", element_shape.DebugString())); + TensorList output_list; + const Tensor& input_tensor = c->input(0); + output_list.element_dtype = input_tensor.dtype(); + OP_REQUIRES(c, TensorShapeUtils::IsVectorOrHigher(input_tensor.shape()), + errors::InvalidArgument( + "Tensor must be at least a vector, but saw shape: ", + input_tensor.shape().DebugString())); + TensorShape tensor_shape_without_first_dim(input_tensor.shape()); + tensor_shape_without_first_dim.RemoveDim(0); + PartialTensorShape element_shape_without_first_dim; + if (!element_shape.unknown_rank()) { + element_shape_without_first_dim = + PartialTensorShape(element_shape.dim_sizes()); + element_shape_without_first_dim.RemoveDim(0); + } + OP_REQUIRES(c, + element_shape_without_first_dim.IsCompatibleWith( + tensor_shape_without_first_dim), + errors::InvalidArgument( + "tensor shape ", input_tensor.shape().DebugString(), + " is not compatible with element_shape ", + element_shape.DebugString())); + output_list.element_shape = element_shape; + const Tensor& lengths = c->input(2); + OP_REQUIRES(c, TensorShapeUtils::IsVector(lengths.shape()), + errors::InvalidArgument( + "Expected lengths to be a vector, received shape: ", + lengths.shape().DebugString())); + output_list.tensors.reserve(lengths.shape().dim_size(0)); + int64 start = 0; + int64 end = 0; + for (int i = 0; i < lengths.shape().dim_size(0); ++i) { + int64 length = lengths.vec()(i); + OP_REQUIRES( + c, length >= 0, + errors::InvalidArgument("Invalid value in lengths: ", length)); + end = start + length; + OP_REQUIRES(c, end <= input_tensor.shape().dim_size(0), + errors::InvalidArgument("Attempting to slice [", start, ", ", + end, "] from tensor with length ", + input_tensor.shape().dim_size(0))); + Tensor tmp = input_tensor.Slice(start, end); + start = end; + // TODO(apassos) maybe not always align; but weird compiler bugs seem to + // prevent this. + Tensor aligned; + OP_REQUIRES_OK(c, c->allocate_temp(tmp.dtype(), tmp.shape(), &aligned)); + aligned.flat().device(c->eigen_device()) = + tmp.unaligned_flat(); + output_list.tensors.emplace_back(aligned); + } + OP_REQUIRES(c, end == input_tensor.shape().dim_size(0), + errors::InvalidArgument( + "Unused values in tensor. Length of tensor: ", + input_tensor.shape().dim_size(0), " Values used: ", end)); + output_tensor->scalar()() = std::move(output_list); + } +}; + template class TensorListGather : public OpKernel { public: @@ -155,22 +355,25 @@ class TensorListGather : public OpKernel { } void Compute(OpKernelContext* c) override { - const TensorList* l = c->input(0).scalar()().get(); - OP_REQUIRES(c, l != nullptr, + const TensorList* tensor_list = + c->input(0).scalar()().get(); + OP_REQUIRES(c, tensor_list != nullptr, errors::InvalidArgument( "Input handle is not a list. Saw: '", c->input(0).scalar()().DebugString(), "'")); - OP_REQUIRES(c, element_dtype_ == l->element_dtype, - errors::InvalidArgument("Invalid data types; op elements ", - DataTypeString(element_dtype_), - " but list elements ", - DataTypeString(l->element_dtype))); + OP_REQUIRES( + c, element_dtype_ == tensor_list->element_dtype, + errors::InvalidArgument( + "Invalid data types; op elements ", DataTypeString(element_dtype_), + " but list elements ", DataTypeString(tensor_list->element_dtype))); Tensor indices = c->input(1); - OP_REQUIRES(c, - indices.NumElements() > 0 || l->element_shape.IsFullyDefined(), - errors::InvalidArgument("Tried to gather 0-elements from " - "a list with non-fully-defined shape: ", - l->element_shape.DebugString())); + OP_REQUIRES( + c, + indices.NumElements() > 0 || + tensor_list->element_shape.IsFullyDefined(), + errors::InvalidArgument("Tried to gather 0-elements from " + "a list with non-fully-defined shape: ", + tensor_list->element_shape.DebugString())); // Compute the shape of the output tensor. // If `element_shape` is fully-defined it gets used. It is assumed that all // requested tensors have the same shape. @@ -178,17 +381,17 @@ class TensorListGather : public OpKernel { // tensor is used and it is checked that all other tensors have the same // shape. TensorShape resulting_shape; - if (!l->element_shape.AsTensorShape(&resulting_shape)) { + if (!tensor_list->element_shape.AsTensorShape(&resulting_shape)) { const int i = indices.flat()(0); OP_REQUIRES( - c, i < l->tensors.size(), + c, i < tensor_list->tensors.size(), errors::InvalidArgument("Index ", i, " out o range; list only has ", - l->tensors.size(), " elements.")); - const Tensor& t = l->tensors[i]; + tensor_list->tensors.size(), " elements.")); + const Tensor& t = tensor_list->tensors[i]; resulting_shape = t.shape(); for (int index = 1; index < indices.NumElements(); ++index) { const int i = indices.flat()(index); - const Tensor& t = l->tensors[i]; + const Tensor& t = tensor_list->tensors[i]; OP_REQUIRES(c, t.shape() == resulting_shape, errors::InvalidArgument( "Tried to gather elements with unequal shapes: ", @@ -204,14 +407,14 @@ class TensorListGather : public OpKernel { } ConstMatrixVector inputs_flat; - inputs_flat.reserve(l->tensors.size()); + inputs_flat.reserve(tensor_list->tensors.size()); for (int index = 0; index < indices.NumElements(); ++index) { const int i = indices.flat()(index); OP_REQUIRES( - c, i < l->tensors.size(), + c, i < tensor_list->tensors.size(), errors::InvalidArgument("Index ", i, " out o range; list only has ", - l->tensors.size(), " elements.")); - const Tensor& t = l->tensors[i]; + tensor_list->tensors.size(), " elements.")); + const Tensor& t = tensor_list->tensors[i]; inputs_flat.emplace_back(new typename TTypes::ConstMatrix( t.shaped({1, t.NumElements()}))); } @@ -289,13 +492,13 @@ class TensorListScatter : public OpKernel { PartialTensorShape element_shape; OP_REQUIRES_OK(c, TensorShapeFromTensor(c->input(2), &element_shape)); TensorList output_list; - const Tensor& t = c->input(0); - output_list.element_dtype = t.dtype(); - OP_REQUIRES(c, TensorShapeUtils::IsVectorOrHigher(t.shape()), + const Tensor& input_tensor = c->input(0); + output_list.element_dtype = input_tensor.dtype(); + OP_REQUIRES(c, TensorShapeUtils::IsVectorOrHigher(input_tensor.shape()), errors::InvalidArgument( "Tensor must be at least a vector, but saw shape: ", - t.shape().DebugString())); - TensorShape output_shape(t.shape()); + input_tensor.shape().DebugString())); + TensorShape output_shape(input_tensor.shape()); output_shape.RemoveDim(0); OP_REQUIRES(c, element_shape.IsCompatibleWith(output_shape), errors::InvalidArgument( @@ -305,11 +508,11 @@ class TensorListScatter : public OpKernel { output_list.tensors.reserve(indices.NumElements()); for (int index = 0; index < indices.NumElements(); ++index) { const int i = indices.flat()(index); - OP_REQUIRES(c, i < t.shape().dim_size(0), - errors::InvalidArgument("Trying to scatter index ", i, - " from tensor with ", - t.shape().dim_size(0), " rows.")); - Tensor tmp = t.Slice(i, i + 1); + OP_REQUIRES(c, i < input_tensor.shape().dim_size(0), + errors::InvalidArgument( + "Trying to scatter index ", i, " from tensor with ", + input_tensor.shape().dim_size(0), " rows.")); + Tensor tmp = input_tensor.Slice(i, i + 1); TensorShape tmp_shape = tmp.shape(); tmp_shape.RemoveDim(0); OP_REQUIRES(c, tmp.CopyFrom(tmp, tmp_shape), @@ -357,40 +560,10 @@ Status TensorListBinaryAdd(OpKernelContext* c, const TensorList& a, for (int i = 0; i < a.tensors.size(); ++i) { const Tensor& a_tensor = a.tensors[i]; const Tensor& b_tensor = b.tensors[i]; - if (a_tensor.dtype() == DT_INVALID) { - out->tensors.push_back(b_tensor); - continue; - } - if (b_tensor.dtype() == DT_INVALID) { - out->tensors.push_back(a_tensor); - continue; - } - if (a_tensor.shape() != b_tensor.shape()) { - // TODO(apassos) support broadcasting additions here? - return errors::InvalidArgument( - "Trying to add two tensors with incompatible element shapes. " - "One is ", - a_tensor.shape().DebugString(), " and the other is ", - b_tensor.shape().DebugString(), " in position ", i); - } Tensor out_tensor; TF_RETURN_IF_ERROR( - c->allocate_temp(a_tensor.dtype(), a_tensor.shape(), &out_tensor)); + BinaryAddTensors(c, a_tensor, b_tensor, &out_tensor)); out->tensors.push_back(out_tensor); - switch (out_tensor.dtype()) { -#define DTYPE_CASE(dtype) \ - case DataTypeToEnum::value: \ - out_tensor.flat().device(c->eigen_device()) = \ - a_tensor.flat() + b_tensor.flat(); \ - break; - - TF_CALL_NUMBER_TYPES(DTYPE_CASE) - -#undef DTYPE_CASE - default: - return errors::InvalidArgument("Trying to add unsupported dtype ", - out_tensor.dtype()); - } } return Status::OK(); } @@ -403,46 +576,7 @@ Status TensorListZerosLike(OpKernelContext* c, const TensorList& x, y->tensors.reserve(x.tensors.size()); for (const Tensor& t : x.tensors) { Tensor out_tensor; - AllocatorAttributes attr; - if (t.dtype() == DT_VARIANT) { - attr.set_on_host(true); - } - TF_RETURN_IF_ERROR( - c->allocate_temp(t.dtype(), t.shape(), &out_tensor, attr)); - switch (out_tensor.dtype()) { -#define DTYPE_CASE(dtype) \ - case DataTypeToEnum::value: \ - out_tensor.flat().device(c->eigen_device()) = \ - out_tensor.flat().constant(dtype(0)); \ - break; - - TF_CALL_POD_TYPES(DTYPE_CASE) - -#undef DTYPE_CASE - - case DT_INVALID: { - // Uninitialized tensor in the TensorList. - out_tensor = Tensor(DT_INVALID); - break; - } - case DataTypeToEnum::value: { - const TensorList* inner_x = t.scalar()().get(); - if (inner_x == nullptr) { - return errors::InvalidArgument("Input handle is not a list. Saw: '", - t.scalar()().DebugString(), - "'"); - } - TensorList inner_y; - TF_RETURN_IF_ERROR(TensorListZerosLike(c, *inner_x, &inner_y)); - out_tensor.scalar()() = std::move(inner_y); - break; - } - - default: - return errors::InvalidArgument( - "Trying to compute zeros_like for unsupported dtype ", - DataTypeString(out_tensor.dtype())); - } + TF_RETURN_IF_ERROR(ZerosLikeTensor(c, t, &out_tensor)); y->tensors.emplace_back(out_tensor); } return Status::OK(); diff --git a/tensorflow/core/kernels/maxpooling_op_gpu.cu.cc b/tensorflow/core/kernels/maxpooling_op_gpu.cu.cc index 0c7a236b2f..89ffe6494e 100644 --- a/tensorflow/core/kernels/maxpooling_op_gpu.cu.cc +++ b/tensorflow/core/kernels/maxpooling_op_gpu.cu.cc @@ -384,6 +384,8 @@ bool MaxPoolForwardNoMask_NCHW_VECT_C::operator()( int32* top_data, const Eigen::GpuDevice& d) { const int kThreadsPerBlock = 1024; const int output_size = batch * channels * pooled_height * pooled_width; + if (output_size == 0) + return true; MaxPoolForwardNoMaskKernel_NCHW_VECT_C<<< (output_size + kThreadsPerBlock - 1) / kThreadsPerBlock, kThreadsPerBlock, 0, d.stream()>>>(output_size, bottom_data, height, width, channels, @@ -402,6 +404,8 @@ bool MaxPoolForwardWithOptionalArgmax::operator()( int64* mask, const Eigen::GpuDevice& d, bool propagate_nans) { const int kThreadsPerBlock = 1024; const int output_size = batch * channels * pooled_height * pooled_width; + if (output_size == 0) + return true; if (propagate_nans) { MaxPoolForwardNHWC <<<(output_size + kThreadsPerBlock - 1) / kThreadsPerBlock, @@ -430,6 +434,8 @@ bool MaxPoolBackwardNoMask::operator()( const int kThreadsPerBlock = 1024; const int bottom_size = batch * channels * height * width; + if (bottom_size == 0) + return true; SetZero<<<(bottom_size + kThreadsPerBlock - 1) / kThreadsPerBlock, kThreadsPerBlock, 0, d.stream()>>>(bottom_size, bottom_diff); @@ -449,6 +455,8 @@ bool MaxPoolBackwardWithArgmax::operator()( const int64* mask, const int top_offset, const int bottom_offset, T* bottom_diff, const Eigen::GpuDevice& d) { const int kThreadsPerBlock = 1024; + if (input_size == 0) + return true; SetZero<<<(input_size + kThreadsPerBlock - 1) / kThreadsPerBlock, kThreadsPerBlock, 0, d.stream()>>>(input_size, bottom_diff); MaxPoolBackward<<<(output_size + kThreadsPerBlock - 1) / kThreadsPerBlock, @@ -466,6 +474,8 @@ bool MaxPoolGradBackwardNoMask::operator()( const int pad_l, const T* top_diff, T* bottom_diff, const Eigen::GpuDevice& d) { const int num_kernels = batch * channels * pooled_height * pooled_width; + if (num_kernels == 0) + return true; CudaLaunchConfig config = GetCudaLaunchConfig(num_kernels, d); if (data_format == FORMAT_NHWC) { @@ -489,6 +499,8 @@ bool MaxPoolGradBackwardWithArgmax::operator()( const int output_size, const int input_size, const T* top_diff, const int64* mask, const int top_offset, const int bottom_offset, T* bottom_diff, const Eigen::GpuDevice& d) { + if (input_size == 0) + return true; CudaLaunchConfig config = GetCudaLaunchConfig(output_size, d); MaxPoolGradBackward<<>>(output_size, top_diff, mask, top_offset, diff --git a/tensorflow/core/kernels/mkl_avgpooling_op.cc b/tensorflow/core/kernels/mkl_avgpooling_op.cc index 2409f7e9dc..939cbd6f96 100644 --- a/tensorflow/core/kernels/mkl_avgpooling_op.cc +++ b/tensorflow/core/kernels/mkl_avgpooling_op.cc @@ -357,22 +357,21 @@ class MklAvgPoolingGradOp : public OpKernel { if (!outbackprop_in_mkl_format) { // For avgpooling, tensor_in_shape should have 1 dimension, and 4 // elements. - OP_REQUIRES( - context, - tensor_in_shape.dims() == 1 && tensor_in_shape.NumElements() == 4, - errors::InvalidArgument("original input shape must be " - "1-dimensional and 4 elements")); + OP_REQUIRES(context, tensor_in_shape.dims() == 1 && + tensor_in_shape.NumElements() == 4, + errors::InvalidArgument("original input shape must be " + "1-dimensional and 4 elements")); // For avgpooling, out_backprop should have 4 dimensions. - OP_REQUIRES(context, out_backprop.dims() == 4, - errors::InvalidArgument("out_backprop must be " - "4-dimensional")); + OP_REQUIRES( + context, out_backprop.dims() == 4, + errors::InvalidArgument("out_backprop must be 4-dimensional")); } else { // Input in MKL format. // For avgpooling, out_backprop should have 4 dimensions. - OP_REQUIRES(context, out_backprop_shape.GetDimension() == 4, - errors::InvalidArgument("out_backprop must be " - "4-dimensional")); + OP_REQUIRES( + context, out_backprop_shape.GetDimension() == 4, + errors::InvalidArgument("out_backprop must be 4-dimensional")); } // TODO(inteltf): Get outbackprop layout. @@ -484,9 +483,9 @@ class MklAvgPoolingOp : public MklPoolingForwardOpBase { dnn_shape_input.IsMklTensor() ? dnn_shape_input.GetSizesAsMklDnnDims() : is_pool2d ? TFShapeToMklDnnDimsInNCHW(input_tensor.shape(), - this->data_format_tf_) - : TFShapeToMklDnnDimsInNCDHW(input_tensor.shape(), - this->data_format_tf_); + this->data_format_tf_) + : TFShapeToMklDnnDimsInNCDHW(input_tensor.shape(), + this->data_format_tf_); memory::desc input_md = dnn_shape_input.IsMklTensor() ? dnn_shape_input.GetMklLayout() : memory::desc(src_dims, MklDnnType(), @@ -494,9 +493,17 @@ class MklAvgPoolingOp : public MklPoolingForwardOpBase { // Get an average pooling primitive from the op pool MklPoolingFwdPrimitive* pooling_fwd = nullptr; + prop_kind pooling_prop_kind; + bool int8_forward_inference = + std::is_same::value || std::is_same::value; + if (int8_forward_inference) + pooling_prop_kind = prop_kind::forward_inference; + else + pooling_prop_kind = prop_kind::forward_training; MklPoolingParams fwdParams(src_dims, output_dims_mkl_order, filter_dims, strides, padding_left, padding_right, - algorithm::pooling_avg_exclude_padding); + algorithm::pooling_avg_exclude_padding, + pooling_prop_kind); pooling_fwd = MklPoolingFwdPrimitiveFactory::Get(fwdParams); // allocate output tensor @@ -523,10 +530,30 @@ class MklAvgPoolingOp : public MklPoolingForwardOpBase { // execute pooling pooling_fwd->Execute(src_data, dst_data); + + // Pass min, max from input to output + if (int8_forward_inference) { + const Tensor& min_input_t = MklGetInput(context, 1); + const Tensor& max_input_t = MklGetInput(context, 2); + const float min_input = min_input_t.flat()(0); + const float max_input = max_input_t.flat()(0); + + Tensor* output_min = nullptr; + Tensor* output_max = nullptr; + MklDnnShape output_min_mkl_shape, output_max_mkl_shape; + output_min_mkl_shape.SetMklTensor(false); + output_max_mkl_shape.SetMklTensor(false); + AllocateOutputSetMklShape(context, 1, &output_min, {}, + output_min_mkl_shape); + AllocateOutputSetMklShape(context, 2, &output_max, {}, + output_max_mkl_shape); + output_min->flat()(0) = min_input; + output_max->flat()(0) = max_input; + } } catch (mkldnn::error& e) { - string error_msg = "Status: " + std::to_string(e.status) + - ", message: " + string(e.message) + ", in file " + - string(__FILE__) + ":" + std::to_string(__LINE__); + string error_msg = "Status: " + std::to_string(e.status) + ", message: " + + string(e.message) + ", in file " + string(__FILE__) + + ":" + std::to_string(__LINE__); OP_REQUIRES_OK( context, errors::Aborted("Operation received an exception:", error_msg)); @@ -576,24 +603,26 @@ class MklAvgPoolingGradOp : public MklPoolingBackwardOpBase { orig_input_mkl_shape.IsMklTensor() ? orig_input_mkl_shape.GetSizesAsMklDnnDims() : is_pool2d ? TFShapeToMklDnnDimsInNCHW(orig_input_shape, - this->data_format_tf_) - : TFShapeToMklDnnDimsInNCDHW(orig_input_shape, - this->data_format_tf_); + this->data_format_tf_) + : TFShapeToMklDnnDimsInNCDHW(orig_input_shape, + this->data_format_tf_); memory::dims diff_dst_dims = grad_mkl_shape.IsMklTensor() ? grad_mkl_shape.GetSizesAsMklDnnDims() : is_pool2d ? TFShapeToMklDnnDimsInNCHW(grad_tensor.shape(), - this->data_format_tf_) - : TFShapeToMklDnnDimsInNCDHW(grad_tensor.shape(), - this->data_format_tf_); + this->data_format_tf_) + : TFShapeToMklDnnDimsInNCDHW(grad_tensor.shape(), + this->data_format_tf_); memory::dims output_dims_mkl_order; this->GetOutputDims(pool_params, &output_dims_mkl_order); - MklPoolingParams bwdParams(orig_input_dims_mkl_order, - output_dims_mkl_order, filter_dims, strides, - padding_left, padding_right, - algorithm::pooling_avg_exclude_padding); + // Pass prop_kind::forward_training to create a forward primitive + // that is used in the backward pass + MklPoolingParams bwdParams( + orig_input_dims_mkl_order, output_dims_mkl_order, filter_dims, + strides, padding_left, padding_right, + algorithm::pooling_avg_exclude_padding, prop_kind::forward_training); MklPoolingBwdPrimitive* pooling_bwd = MklPoolingBwdPrimitiveFactory::Get(bwdParams); @@ -624,9 +653,9 @@ class MklAvgPoolingGradOp : public MklPoolingBackwardOpBase { // execute pooling op pooling_bwd->Execute(diff_dst_data, diff_src_data); } catch (mkldnn::error& e) { - string error_msg = "Status: " + std::to_string(e.status) + - ", message: " + string(e.message) + ", in file " + - string(__FILE__) + ":" + std::to_string(__LINE__); + string error_msg = "Status: " + std::to_string(e.status) + ", message: " + + string(e.message) + ", in file " + string(__FILE__) + + ":" + std::to_string(__LINE__); OP_REQUIRES_OK(context, errors::Aborted("Compute received an exception:", error_msg)); } @@ -645,28 +674,26 @@ class MklAvgPoolingGradOp : public MklPoolingBackwardOpBase { const MklDnnShape& original_input_mkl_shape, const MklDnnShape& input_gradient_mkl_shape) { if (!original_input_mkl_shape.IsMklTensor()) { - OP_REQUIRES( - context, - tensor_in_shape.dims() == 1 && tensor_in_shape.NumElements() == 4, - errors::InvalidArgument("original input shape must be " - "1-dimensional and 4 elements")); + OP_REQUIRES(context, tensor_in_shape.dims() == 1 && + tensor_in_shape.NumElements() == 4, + errors::InvalidArgument("original input shape must be " + "1-dimensional and 4 elements")); } else { - OP_REQUIRES(context, - original_input_mkl_shape.GetDimension() == 1 && - original_input_mkl_shape.DimSize(0) == 4, + OP_REQUIRES(context, original_input_mkl_shape.GetDimension() == 1 && + original_input_mkl_shape.DimSize(0) == 4, errors::InvalidArgument("original input shape must be " "1-dimensional and 4 elements")); } if (!input_gradient_mkl_shape.IsMklTensor()) { // For avgpooling, input_gradient_diff_dst should have 4 dimensions. - OP_REQUIRES(context, input_gradient_tensor.dims() == 4, - errors::InvalidArgument("Gradient shape must be " - "4-dimensional")); + OP_REQUIRES( + context, input_gradient_tensor.dims() == 4, + errors::InvalidArgument("Gradient shape must be 4-dimensional")); } else { - OP_REQUIRES(context, input_gradient_mkl_shape.GetDimension() == 4, - errors::InvalidArgument("Gradient shape must be " - "4-dimensional")); + OP_REQUIRES( + context, input_gradient_mkl_shape.GetDimension() == 4, + errors::InvalidArgument("Gradient shape must be 4-dimensional")); } } }; // MklAvgPoolingGradOp @@ -691,6 +718,18 @@ REGISTER_KERNEL_BUILDER(Name("_MklAvgPool") .Label(mkl_op_registry::kMklOpLabel), MklAvgPoolingOp); +REGISTER_KERNEL_BUILDER(Name("_MklQuantizedAvgPool") + .Device(DEVICE_CPU) + .TypeConstraint("T") + .Label(mkl_op_registry::kMklQuantizedOpLabel), + MklAvgPoolingOp); + +REGISTER_KERNEL_BUILDER(Name("_MklQuantizedAvgPool") + .Device(DEVICE_CPU) + .TypeConstraint("T") + .Label(mkl_op_registry::kMklQuantizedOpLabel), + MklAvgPoolingOp); + REGISTER_KERNEL_BUILDER(Name("_MklAvgPoolGrad") .Device(DEVICE_CPU) .TypeConstraint("T") diff --git a/tensorflow/core/kernels/mkl_conv_ops.cc b/tensorflow/core/kernels/mkl_conv_ops.cc index 14d134e2d0..75f08956b4 100644 --- a/tensorflow/core/kernels/mkl_conv_ops.cc +++ b/tensorflow/core/kernels/mkl_conv_ops.cc @@ -465,19 +465,18 @@ class MklConvOp : public OpKernel { filter.shape().DebugString())); for (int i = 0; i < 3; i++) { - OP_REQUIRES( - context, - FastBoundsCheck(filter.dim_size(i), std::numeric_limits::max()), - errors::InvalidArgument("filter too large")); + OP_REQUIRES(context, FastBoundsCheck(filter.dim_size(i), + std::numeric_limits::max()), + errors::InvalidArgument("filter too large")); } const int64 input_depth = input_in_mkl_format ? GetMklTensorDim(mkl_context.input_shape, 'C') : GetTensorDim(input, data_format_, 'C'); - OP_REQUIRES(context, input_depth == filter.dim_size(2), - errors::InvalidArgument( - "input and filter must have the same depth: ", input_depth, - " vs ", filter.dim_size(2))); + OP_REQUIRES( + context, input_depth == filter.dim_size(2), + errors::InvalidArgument("input and filter must have the same depth: ", + input_depth, " vs ", filter.dim_size(2))); // The last dimension for filter is out_depth. const int out_depth = static_cast(filter.dim_size(3)); @@ -486,10 +485,9 @@ class MklConvOp : public OpKernel { const int64 input_rows_raw = input_in_mkl_format ? GetMklTensorDim(mkl_context.input_shape, 'H') : GetTensorDim(input, data_format_, 'H'); - OP_REQUIRES( - context, - FastBoundsCheck(input_rows_raw, std::numeric_limits::max()), - errors::InvalidArgument("Input rows too large")); + OP_REQUIRES(context, FastBoundsCheck(input_rows_raw, + std::numeric_limits::max()), + errors::InvalidArgument("Input rows too large")); const int input_rows = static_cast(input_rows_raw); const int filter_rows = static_cast(filter.dim_size(0)); @@ -498,10 +496,9 @@ class MklConvOp : public OpKernel { const int64 input_cols_raw = input_in_mkl_format ? GetMklTensorDim(mkl_context.input_shape, 'W') : GetTensorDim(input, data_format_, 'W'); - OP_REQUIRES( - context, - FastBoundsCheck(input_cols_raw, std::numeric_limits::max()), - errors::InvalidArgument("Input cols too large")); + OP_REQUIRES(context, FastBoundsCheck(input_cols_raw, + std::numeric_limits::max()), + errors::InvalidArgument("Input cols too large")); const int input_cols = static_cast(input_cols_raw); const int filter_cols = static_cast(filter.dim_size(1)); @@ -509,10 +506,9 @@ class MklConvOp : public OpKernel { const int64 input_batch_raw = input_in_mkl_format ? GetMklTensorDim(mkl_context.input_shape, 'N') : GetTensorDim(input, data_format_, 'N'); - OP_REQUIRES( - context, - FastBoundsCheck(input_batch_raw, std::numeric_limits::max()), - errors::InvalidArgument("batch is too large")); + OP_REQUIRES(context, FastBoundsCheck(input_batch_raw, + std::numeric_limits::max()), + errors::InvalidArgument("batch is too large")); const int batch = static_cast(input_batch_raw); // For now we take the stride from the second and third dimensions only (we @@ -893,17 +889,15 @@ class MklConvOp : public OpKernel { OP_REQUIRES(context, dilations_.size() == 5, errors::InvalidArgument("Dilation rates field must " "specify 5 dimensions")); - OP_REQUIRES(context, - (GetTensorDim(dilations_, data_format_, 'N') == 1 && - GetTensorDim(dilations_, data_format_, 'C') == 1), + OP_REQUIRES(context, (GetTensorDim(dilations_, data_format_, 'N') == 1 && + GetTensorDim(dilations_, data_format_, 'C') == 1), errors::InvalidArgument( "Current implementation does not yet support " "dilations rates in the batch and depth dimensions.")); OP_REQUIRES( - context, - (GetTensorDim(dilations_, data_format_, '0') > 0 && - GetTensorDim(dilations_, data_format_, '1') > 0 && - GetTensorDim(dilations_, data_format_, '2') > 0), + context, (GetTensorDim(dilations_, data_format_, '0') > 0 && + GetTensorDim(dilations_, data_format_, '1') > 0 && + GetTensorDim(dilations_, data_format_, '2') > 0), errors::InvalidArgument("Dilated rates should be larger than 0.")); } } @@ -1067,8 +1061,14 @@ class MklConvOp : public OpKernel { Tfilter* filter_data = nullptr; if (filter_md.data.format != conv_fwd->GetFilterMemoryFormat()) { filter.SetUsrMem(filter_md, &filter_tensor); - filter.CheckReorderToOpMem(conv_fwd_pd.get()->weights_primitive_desc(), - filter.GetTensorBuffer(filter_out_tensor)); + if (filter_out_tensor == nullptr) { + filter.CheckReorderToOpMem( + conv_fwd_pd.get()->weights_primitive_desc()); + } else { + filter.CheckReorderToOpMem( + conv_fwd_pd.get()->weights_primitive_desc(), + filter.GetTensorBuffer(filter_out_tensor)); + } filter_data = static_cast(filter.GetOpMem().get_data_handle()); } else { @@ -1468,7 +1468,7 @@ class MklQuantizedConv2DSumReluOp {"sum", {scale_summand / scale_output}}); else params.post_op_params.push_back( - {"sum", {2.0 * scale_summand / scale_output}}); + {"sum", {2.0f * scale_summand / scale_output}}); } else { params.post_op_params.push_back({"sum", {1.0}}); } @@ -1533,8 +1533,8 @@ class MklQuantizedConv2DSumReluOp const float max_filter = context->input(5 + bias_index_offset).flat()(0); - reorder_sum_scale = 255.0 * 127.0 / - (std::max(std::abs(max_input), std::abs(min_input)) * + reorder_sum_scale = + 255.0 * 127.0 / (std::max(std::abs(max_input), std::abs(min_input)) * std::max(std::abs(max_filter), std::abs(min_filter))); std::vector scales; scales.push_back(reorder_sum_scale); diff --git a/tensorflow/core/kernels/mkl_lrn_op.cc b/tensorflow/core/kernels/mkl_lrn_op.cc index 22ff4cd80f..407ce5d653 100644 --- a/tensorflow/core/kernels/mkl_lrn_op.cc +++ b/tensorflow/core/kernels/mkl_lrn_op.cc @@ -22,32 +22,26 @@ limitations under the License. #define EIGEN_USE_THREADS #include -#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor" +#include "mkldnn.hpp" #include "tensorflow/core/framework/op_kernel.h" #include "tensorflow/core/framework/register_types.h" #include "tensorflow/core/framework/tensor.h" #include "tensorflow/core/kernels/bounds_check.h" #include "tensorflow/core/kernels/ops_util.h" #include "tensorflow/core/lib/core/errors.h" +#include "tensorflow/core/util/mkl_util.h" #include "tensorflow/core/util/tensor_format.h" +#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor" #if !defined(IS_MOBILE_PLATFORM) #include "tensorflow/core/util/work_sharder.h" #endif -#ifndef INTEL_MKL_ML_ONLY -#include "mkldnn.hpp" using mkldnn::lrn_across_channels; using mkldnn::lrn_backward; using mkldnn::lrn_forward; using mkldnn::prop_kind; using mkldnn::stream; -#else -#include "mkl_dnn.h" -#include "mkl_dnn_types.h" -#endif - -#include "tensorflow/core/util/mkl_util.h" namespace tensorflow { @@ -69,672 +63,6 @@ void GetBandMatrix(int depth, int depth_radius, } // namespace -#ifdef INTEL_MKL_ML_ONLY - -template -class MklLRNOp : public OpKernel { - public: - ~MklLRNOp() {} - - explicit MklLRNOp(OpKernelConstruction* context) : OpKernel(context) { - int64 depth_radius64; - OP_REQUIRES_OK(context, context->GetAttr("depth_radius", &depth_radius64)); - OP_REQUIRES( - context, - FastBoundsCheck(depth_radius64, std::numeric_limits::max()), - errors::InvalidArgument("depth_radius = ", depth_radius64, - " larger than int max")); - depth_radius_ = static_cast(depth_radius64); - - OP_REQUIRES_OK(context, context->GetAttr("bias", &bias_)); - OP_REQUIRES_OK(context, context->GetAttr("alpha", &alpha_)); - OP_REQUIRES_OK(context, context->GetAttr("beta", &beta_)); - workspace_enabled_ = false; - OP_REQUIRES_OK(context, - context->GetAttr("workspace_enabled", &workspace_enabled_)); - } - - void Compute(OpKernelContext* context) override { - MklLRNOpContext mkl_context; - - const Tensor& input = MklGetInput(context, 0); - GetMklShape(context, 0, &mkl_context.input_shape); - bool input_in_mkl_format = mkl_context.input_shape.IsMklTensor(); - - // Sanity checks - mkl_context.in_dims = input_in_mkl_format - ? mkl_context.input_shape.GetDimension() - : input.dims(); - OP_REQUIRES(context, mkl_context.in_dims == 4, - errors::InvalidArgument("input must be 4-dimensional")); - OP_REQUIRES( - context, - FastBoundsCheck(input.NumElements(), std::numeric_limits::max()), - errors::InvalidArgument("argument to LRN too large")); - - if (!input_in_mkl_format) { - mkl_context.MklDefaultToEigen(context, depth_radius_, bias_, alpha_, - beta_, input); - return; - } - - if (input_in_mkl_format) { - // MKL supports normalization over channel dimension only - if (mkl_context.input_shape.tf_dim_idx(mkl_context.in_dims - 1) == - MklDims::C) { - mkl_context.lt_input = - static_cast(mkl_context.input_shape.GetCurLayout()); - workspace_enabled_ = true; - } else { - Tensor converted_tensor = - ConvertMklToTF(context, input, mkl_context.input_shape); - mkl_context.MklDefaultToEigen(context, depth_radius_, bias_, alpha_, - beta_, converted_tensor); - return; - } - } - - int kernel_size = 2 * depth_radius_ + 1; - - CHECK_EQ(dnnLRNCreateForward_F32( - &mkl_context.lrn_fwd, NULL, mkl_context.lt_input, kernel_size, - static_cast(alpha_ * kernel_size), beta_, bias_), - E_SUCCESS); - - // Allocate output tensor and shape - Tensor* output = nullptr; - Tensor* workspace = nullptr; - - // Convert Inputs if needed - Tensor mkl_tmp_input_buf_tensor; - mkl_context.MklPrepareLRNInputs(context, &mkl_tmp_input_buf_tensor); - - // Allocate Layer Outputs - mkl_context.MklAllocateOutputs(context, &output, &workspace, - workspace_enabled_); - - Tensor mkl_tmp_workspace_buf_tensor; - mkl_context.MklPrepareLRNOutputs(context, output, workspace, - &mkl_tmp_workspace_buf_tensor, - workspace_enabled_); - - // Execute LRN. - CHECK_EQ(dnnExecute_F32(mkl_context.lrn_fwd, mkl_context.lrn_res), - E_SUCCESS); - - // Release MKL resources. - mkl_context.MklCleanup(); - } - - private: - typedef struct { - size_t in_dims; - size_t in_sizes[4]; - size_t in_strides[4]; - size_t out_sizes[4]; - size_t out_strides[4]; - MklShape input_shape; - dnnPrimitive_t lrn_fwd = nullptr; - dnnPrimitive_t convert_input = nullptr; - dnnLayout_t lt_input = nullptr; - dnnLayout_t lt_internal_input = nullptr; - dnnLayout_t lt_internal_workspace = nullptr; - dnnLayout_t lt_internal_output = nullptr; - void* lrn_res[dnnResourceNumber]; - - // Convert Inputs if needed - void MklPrepareLRNInputs(OpKernelContext* context, - Tensor* mkl_tmp_input_buf_tensor) { - const Tensor& input = MklGetInput(context, 0); - void* mkl_buf_input = - const_cast(static_cast(input.flat().data())); - - CHECK_EQ(dnnLayoutCreateFromPrimitive_F32(<_internal_input, lrn_fwd, - dnnResourceSrc), - E_SUCCESS); - - void* mkl_buf_convert_input = nullptr; - bool mkl_convert_input = false; - mkl_convert_input = !dnnLayoutCompare_F32(lt_internal_input, lt_input); - - if (mkl_convert_input) { - CHECK_EQ(dnnConversionCreate_F32(&convert_input, lt_input, - lt_internal_input), - E_SUCCESS); - AllocTmpBuffer(context, mkl_tmp_input_buf_tensor, lt_internal_input, - &mkl_buf_convert_input); - CHECK_EQ(dnnConversionExecute_F32(convert_input, mkl_buf_input, - mkl_buf_convert_input), - E_SUCCESS); - dnnDelete_F32(convert_input); - } - - lrn_res[dnnResourceSrc] = - (mkl_convert_input) ? mkl_buf_convert_input : mkl_buf_input; - } - - // Allocate Layer Outputs - void MklAllocateOutputs(OpKernelContext* context, Tensor** output, - Tensor** workspace, bool workspace_enabled_) { - TensorShape mkl_output_tf_shape; /* First tensor */ - MklShape mkl_output_mkl_shape; /* Second tensor */ - - mkl_output_mkl_shape.SetMklTensor(true); - mkl_output_mkl_shape.SetMklLayout(lrn_fwd, dnnResourceDst); - mkl_output_mkl_shape.SetTfLayout(in_dims, input_shape.GetSizes(), - input_shape.GetStrides()); - mkl_output_mkl_shape.SetTfDimOrder(in_dims, - input_shape.GetTfToMklDimMap()); - mkl_output_tf_shape.AddDim( - dnnLayoutGetMemorySize_F32( - static_cast(mkl_output_mkl_shape.GetMklLayout())) / - sizeof(T)); - AllocateOutputSetMklShape(context, 0, output, - mkl_output_tf_shape /* First tensor */, - mkl_output_mkl_shape /* Second Tensor */); - - if (workspace_enabled_) { - TensorShape mkl_workspace_tf_shape; /* First tensor */ - MklShape mkl_workspace_mkl_shape; /* Second tensor */ - mkl_workspace_mkl_shape.SetMklTensor(false); - mkl_workspace_mkl_shape.SetMklLayout(lrn_fwd, dnnResourceWorkspace); - // Assumes workspace has same TF layout and TF dim order as input - mkl_workspace_mkl_shape.SetTfLayout(in_dims, input_shape.GetSizes(), - input_shape.GetStrides()); - mkl_workspace_mkl_shape.SetTfDimOrder(in_dims, - input_shape.GetTfToMklDimMap()); - mkl_workspace_tf_shape.AddDim( - dnnLayoutGetMemorySize_F32(static_cast( - mkl_workspace_mkl_shape.GetMklLayout())) / - sizeof(T)); - AllocateOutputSetMklShape(context, 1, workspace, - mkl_workspace_tf_shape /* First tensor */, - mkl_workspace_mkl_shape /* Second Tensor */); - } - } - - void MklPrepareLRNOutputs(OpKernelContext* context, Tensor* output, - Tensor* workspace, - Tensor* mkl_tmp_workspace_buf_tensor, - bool workspace_enabled_) { - CHECK_EQ(dnnLayoutCreateFromPrimitive_F32(<_internal_workspace, lrn_fwd, - dnnResourceWorkspace), - E_SUCCESS); - - CHECK_EQ(dnnLayoutCreateFromPrimitive_F32(<_internal_output, lrn_fwd, - dnnResourceDst), - E_SUCCESS); - - void* mkl_buf_output = - const_cast(static_cast(output->flat().data())); - lrn_res[dnnResourceDst] = mkl_buf_output; - - void* mkl_buf_workspace = nullptr; - if (workspace_enabled_) { - mkl_buf_workspace = const_cast( - static_cast(workspace->flat().data())); - } else { - AllocTmpBuffer(context, mkl_tmp_workspace_buf_tensor, - lt_internal_workspace, &mkl_buf_workspace); - } - lrn_res[dnnResourceWorkspace] = mkl_buf_workspace; - } - - // Fallback implementation - Taken from lrn_op.cc - // TODO(inteltf) Check if we can use EigenLRNOp directly instead of making a - // copy. - void MklDefaultToEigen(OpKernelContext* context, int depth_radius_, - float bias_, float alpha_, float beta_, - const Tensor& input) { - const int batch = static_cast(input.dim_size(0)); - const int rows = static_cast(input.dim_size(1)); - const int cols = static_cast(input.dim_size(2)); - const int depth = static_cast(input.dim_size(3)); - const int nodes = cols * rows; - - auto in_shaped = input.shaped({nodes * batch, depth}); - // Multiplying the input with the band matrix has the effect of reducing - // the - // correct patch along the depth. - Eigen::Tensor multiplier(depth, depth); - GetBandMatrix(depth, depth_radius_, &multiplier); - - Tensor *output, *workspace; - MklShape mkl_output_mkl_shape, mkl_workspace_mkl_shape; - mkl_output_mkl_shape.SetMklTensor(false); - mkl_output_mkl_shape.SetDimensions(4); - AllocateOutputSetMklShape(context, 0, &output, input.shape(), - mkl_output_mkl_shape); - - mkl_workspace_mkl_shape.SetMklTensor(false); - mkl_workspace_mkl_shape.SetDimensions(4); - AllocateOutputSetMklShape(context, 1, &workspace, input.shape(), - mkl_workspace_mkl_shape); - - auto out_shaped = output->shaped({nodes * batch, depth}); - Eigen::array dims = {{DimPair(1, 0)}}; - auto tmp = in_shaped.square().contract(multiplier, dims) * alpha_ + bias_; - if (beta_ == T(1)) { - out_shaped.device(context->eigen_cpu_device()) = - in_shaped * tmp.inverse(); - } else if (beta_ == T(0.5)) { - out_shaped.device(context->eigen_cpu_device()) = - in_shaped * tmp.rsqrt(); - } else { - out_shaped.device(context->eigen_cpu_device()) = - in_shaped * (tmp.log() * -beta_).exp(); - } - } - - // Release MKL resources. - void MklCleanup() { - dnnDelete_F32(lrn_fwd); - dnnLayoutDelete_F32(lt_internal_input); - dnnLayoutDelete_F32(lt_internal_workspace); - dnnLayoutDelete_F32(lt_internal_output); - } - } MklLRNOpContext; - - typedef typename Eigen::Tensor::DimensionPair DimPair; - - bool workspace_enabled_; - int depth_radius_; - float bias_; - float alpha_; - float beta_; -}; - -template -class MklLRNGradOp : public OpKernel { - public: - explicit MklLRNGradOp(OpKernelConstruction* context) : OpKernel(context) { - int64 depth_radius64; - OP_REQUIRES_OK(context, context->GetAttr("depth_radius", &depth_radius64)); - OP_REQUIRES( - context, - FastBoundsCheck(depth_radius64, std::numeric_limits::max()), - errors::InvalidArgument("depth_radius = ", depth_radius64, - " larger than int max")); - depth_radius_ = static_cast(depth_radius64); - OP_REQUIRES_OK(context, context->GetAttr("bias", &bias_)); - OP_REQUIRES_OK(context, context->GetAttr("alpha", &alpha_)); - OP_REQUIRES_OK(context, context->GetAttr("beta", &beta_)); - workspace_enabled_ = false; - OP_REQUIRES_OK(context, - context->GetAttr("workspace_enabled", &workspace_enabled_)); - } - - void Compute(OpKernelContext* context) override { - MklLRNGradOpContext mkl_context; - mkl_context.depth_radius_ = depth_radius_; - mkl_context.bias_ = bias_; - mkl_context.alpha_ = alpha_; - mkl_context.beta_ = beta_; - - const Tensor& in_grads = MklGetInput(context, 0); - const Tensor& in_image = MklGetInput(context, 1); - const Tensor& out_image = MklGetInput(context, 2); - - GetMklShape(context, 0, &mkl_context.ingrad_shape); - GetMklShape(context, 1, &mkl_context.inimage_shape); - GetMklShape(context, 2, &mkl_context.outimage_shape); - - bool ingrad_in_mkl_format = mkl_context.ingrad_shape.IsMklTensor(); - bool inimage_in_mkl_format = mkl_context.inimage_shape.IsMklTensor(); - bool outimage_in_mkl_format = mkl_context.outimage_shape.IsMklTensor(); - - mkl_context.in_dims = inimage_in_mkl_format - ? mkl_context.inimage_shape.GetDimension() - : in_image.dims(); - OP_REQUIRES(context, mkl_context.in_dims == 4, - errors::InvalidArgument("input images must be 4-dimensional")); - - if (!workspace_enabled_) { - mkl_context.MklDefaultToEigen(context); - return; - } - - if (ingrad_in_mkl_format || inimage_in_mkl_format) { - const MklShape* tmp_mkl_shape = (ingrad_in_mkl_format) - ? &mkl_context.ingrad_shape - : &mkl_context.inimage_shape; - if (tmp_mkl_shape->tf_dim_idx(mkl_context.in_dims - 1) != MklDims::C) { - // Fallback to eigen - mkl_context.MklDefaultToEigen(context); - return; - } else { // MKL supports normalization over channel dimension only - for (int i = 0; i < mkl_context.in_dims; i++) { - mkl_context.in_sizes[i] = mkl_context.out_sizes[i] = - tmp_mkl_shape->GetSizes()[i]; - mkl_context.in_strides[i] = mkl_context.out_strides[i] = - tmp_mkl_shape->GetStrides()[i]; - } - } - } else { - // Fallback to eigen - mkl_context.MklDefaultToEigen(context); - return; - } - - // Dimensions check for sanity purpose - if (ingrad_in_mkl_format) { - OP_REQUIRES( - context, mkl_context.ingrad_shape.GetDimension() == 4, - errors::InvalidArgument("input gradient must be 4-dimensional")); - } else { - OP_REQUIRES( - context, in_grads.dims() == 4, - errors::InvalidArgument("input gradient must be 4-dimensional")); - } - - if (outimage_in_mkl_format) { - OP_REQUIRES( - context, mkl_context.outimage_shape.GetDimension() == 4, - errors::InvalidArgument("Output image must be 4-dimensional")); - } else { - OP_REQUIRES( - context, out_image.dims() == 4, - errors::InvalidArgument("Output image must be 4-dimensional")); - } - - // Prepare mkl input layout - mkl_context.MklPrepareLRNInputsLayouts(context); - int ksize = 2 * depth_radius_ + 1; - - CHECK_EQ(dnnLRNCreateBackward_F32( - &mkl_context.lrn_bwd, NULL, mkl_context.lt_input, - mkl_context.lt_output, ksize, - static_cast(alpha_ * ksize), beta_, bias_), - E_SUCCESS); - - // Allocate output tensor and shape. - TensorShape mkl_output_tf_shape; /* First tensor */ - MklShape mkl_output_mkl_shape; /* Second tensor */ - mkl_output_mkl_shape.SetMklTensor(true); - CHECK_NE(mkl_context.lrn_bwd, nullptr); - mkl_output_mkl_shape.SetMklLayout(mkl_context.lrn_bwd, dnnResourceDiffSrc); - mkl_output_mkl_shape.SetTfLayout(mkl_context.in_dims, mkl_context.out_sizes, - mkl_context.out_strides); - if (ingrad_in_mkl_format) { - mkl_output_mkl_shape.SetTfDimOrder( - mkl_context.in_dims, mkl_context.ingrad_shape.GetTfToMklDimMap()); - } else { - mkl_output_mkl_shape.SetTfDimOrder( - mkl_context.in_dims, mkl_context.inimage_shape.GetTfToMklDimMap()); - } - mkl_output_tf_shape.AddDim( - dnnLayoutGetMemorySize_F32( - static_cast(mkl_output_mkl_shape.GetMklLayout())) / - sizeof(T)); - Tensor* output = nullptr; - AllocateOutputSetMklShape(context, 0, &output, mkl_output_tf_shape, - mkl_output_mkl_shape); - - // Get pointers to output data. - void* user_output = - const_cast(static_cast(output->flat().data())); - - Tensor mkl_tmp_input_buf_tensor, mkl_tmp_image_buf_tensor, - mkl_tmp_outimage_buf_tensor; - // Convert Inputs if needed - mkl_context.MklPrepareLRNGradInput(context, &mkl_tmp_input_buf_tensor, - &mkl_tmp_image_buf_tensor, - &mkl_tmp_outimage_buf_tensor); - - // We do not do any conversion for output. But we simply emit it - // in MKL format. - mkl_context.res_lrn_bwd[dnnResourceDiffSrc] = user_output; - // Execute LRN backward using dnnExecute - CHECK_EQ(dnnExecute_F32(mkl_context.lrn_bwd, mkl_context.res_lrn_bwd), - E_SUCCESS); - // Release MKL resources. - mkl_context.Mklcleanup(); - } - - private: - typedef struct { - int depth_radius_; - float bias_; - float alpha_; - float beta_; - size_t in_dims; - size_t in_sizes[4]; - size_t in_strides[4]; - size_t out_sizes[4]; - size_t out_strides[4]; - MklShape ingrad_shape, inimage_shape, outimage_shape; - dnnPrimitive_t lrn_bwd = nullptr; - dnnPrimitive_t convert_input = nullptr; - dnnLayout_t lt_input = nullptr; - dnnLayout_t lt_output = nullptr; - dnnLayout_t lt_bdw_input = nullptr; - dnnLayout_t lt_workspace = nullptr; - dnnLayout_t lt_internal_input = nullptr; - void* res_lrn_bwd[dnnResourceNumber]; - - // prepare mkl input - void MklPrepareLRNInputsLayouts(OpKernelContext* context) { - bool ingrad_in_mkl_format = ingrad_shape.IsMklTensor(); - bool inimage_in_mkl_format = inimage_shape.IsMklTensor(); - if (!ingrad_in_mkl_format) { - CHECK_EQ(dnnLayoutCreate_F32(<_input, in_dims, in_sizes, in_strides), - E_SUCCESS); - } else { - lt_input = static_cast(ingrad_shape.GetCurLayout()); - } - - if (!inimage_in_mkl_format) { - CHECK_EQ( - dnnLayoutCreate_F32(<_output, in_dims, out_sizes, out_strides), - E_SUCCESS); - } else { - lt_output = static_cast(inimage_shape.GetCurLayout()); - } - } - - // convert input if needed - void MklPrepareLRNGradInput(OpKernelContext* context, - Tensor* mkl_tmp_input_buf_tensor, - Tensor* mkl_tmp_image_buf_tensor, - Tensor* mkl_tmp_outimage_buf_tensor) { - const Tensor& in_grads = MklGetInput(context, 0); - const Tensor& in_image = MklGetInput(context, 1); - const Tensor& workspace = MklGetInput( - context, - 3); /*Worskpsace is enabled, get the buffer to the workspace */ - - void* user_input = const_cast( - static_cast(in_grads.flat().data())); - void* user_fwd_input = const_cast( - static_cast(in_image.flat().data())); - void* workspace_buffer = const_cast( - static_cast(workspace.flat().data())); - - CHECK_EQ(dnnLayoutCreateFromPrimitive_F32(<_workspace, lrn_bwd, - dnnResourceWorkspace), - E_SUCCESS); - CHECK_EQ(dnnLayoutCreateFromPrimitive_F32(<_bdw_input, lrn_bwd, - dnnResourceDiffDst), - E_SUCCESS); - CHECK_EQ(dnnLayoutCreateFromPrimitive_F32(<_internal_input, lrn_bwd, - dnnResourceSrc), - E_SUCCESS); - - bool ingrad_in_mkl_format = ingrad_shape.IsMklTensor(); - if (ingrad_in_mkl_format) { - if (!dnnLayoutCompare_F32(lt_bdw_input, lt_input)) { - AllocTmpBuffer(context, mkl_tmp_input_buf_tensor, lt_bdw_input, - &res_lrn_bwd[dnnResourceDiffDst]); - ingrad_shape.GetConvertedFlatData(lt_bdw_input, user_input, - res_lrn_bwd[dnnResourceDiffDst]); - } else { - res_lrn_bwd[dnnResourceDiffDst] = user_input; - } - } else { - if (!dnnLayoutCompare_F32(lt_bdw_input, lt_input)) { - CHECK_EQ( - dnnConversionCreate_F32(&convert_input, lt_input, lt_bdw_input), - E_SUCCESS); - - AllocTmpBuffer(context, mkl_tmp_input_buf_tensor, lt_bdw_input, - &res_lrn_bwd[dnnResourceDiffDst]); - CHECK_EQ(dnnConversionExecute_F32(convert_input, user_input, - res_lrn_bwd[dnnResourceDiffDst]), - E_SUCCESS); - dnnDelete_F32(convert_input); - } else { - res_lrn_bwd[dnnResourceDiffDst] = user_input; - } - } - - bool inimage_in_mkl_format = inimage_shape.IsMklTensor(); - if (inimage_in_mkl_format) { - if (!dnnLayoutCompare_F32( - lt_internal_input, - static_cast(inimage_shape.GetCurLayout()))) { - AllocTmpBuffer(context, mkl_tmp_image_buf_tensor, lt_internal_input, - &res_lrn_bwd[dnnResourceSrc]); - ingrad_shape.GetConvertedFlatData(lt_internal_input, user_fwd_input, - res_lrn_bwd[dnnResourceSrc]); - } else { - res_lrn_bwd[dnnResourceSrc] = user_fwd_input; - } - } else { - if (!dnnLayoutCompare_F32( - lt_internal_input, - static_cast(inimage_shape.GetCurLayout()))) { - CHECK_EQ(dnnConversionCreate_F32( - &convert_input, - static_cast(inimage_shape.GetCurLayout()), - lt_internal_input), - E_SUCCESS); - - AllocTmpBuffer(context, mkl_tmp_image_buf_tensor, lt_internal_input, - &res_lrn_bwd[dnnResourceSrc]); - CHECK_EQ(dnnConversionExecute_F32(convert_input, user_fwd_input, - res_lrn_bwd[dnnResourceSrc]), - E_SUCCESS); - dnnDelete_F32(convert_input); - } else { - res_lrn_bwd[dnnResourceSrc] = user_fwd_input; - } - } - - res_lrn_bwd[dnnResourceWorkspace] = workspace_buffer; - } - - // Fallback implementation - Taken from lrn_op.cc - // TODO(intelft) Check if we can use EigenLRNOp directly instead of making a - // copy. - void MklDefaultToEigen(OpKernelContext* context) { - Tensor in_grads; - Tensor in_image; - Tensor out_image; - - GetMklShape(context, 0, &ingrad_shape); - GetMklShape(context, 1, &inimage_shape); - GetMklShape(context, 2, &outimage_shape); - - if (ingrad_shape.IsMklTensor()) { - in_grads = - ConvertMklToTF(context, MklGetInput(context, 0), ingrad_shape); - } else { - in_grads = MklGetInput(context, 0); - } - - if (inimage_shape.IsMklTensor()) { - in_image = - ConvertMklToTF(context, MklGetInput(context, 1), inimage_shape); - } else { - in_image = MklGetInput(context, 1); - } - - if (outimage_shape.IsMklTensor()) { - out_image = - ConvertMklToTF(context, MklGetInput(context, 2), outimage_shape); - } else { - out_image = MklGetInput(context, 2); - } - - const int64 batch = static_cast(in_grads.dim_size(0)); - const int64 rows = static_cast(in_grads.dim_size(1)); - const int64 cols = static_cast(in_grads.dim_size(2)); - const int64 depth = static_cast(in_grads.dim_size(3)); - const auto nodes = cols * rows; - - auto grads_shaped = in_grads.shaped({nodes * batch, depth}); - - auto in_shaped = in_image.shaped({nodes * batch, depth}); - auto activations = out_image.shaped({nodes * batch, depth}); - - Tensor* output; - MklShape mkl_output_mkl_shape; - mkl_output_mkl_shape.SetMklTensor(false); - mkl_output_mkl_shape.SetDimensions(4); - AllocateOutputSetMklShape(context, 0, &output, in_grads.shape(), - mkl_output_mkl_shape); - - auto out_shaped = output->shaped({nodes * batch, depth}); - out_shaped.setZero(); - auto shard = [this, activations, in_shaped, grads_shaped, out_shaped, - depth](int64 begin, int64 end) { - for (int64 i = begin; i < end; ++i) { - for (int64 j = 0; j < depth; ++j) { - int64 depth_begin = std::max(0, j - depth_radius_); - int64 depth_end = std::min(depth, j + depth_radius_ + 1); - - T norm(0); - for (int64 k = depth_begin; k < depth_end; ++k) { - norm += in_shaped(i, k) * in_shaped(i, k); - } - norm = alpha_ * norm + bias_; - DCHECK_GT(norm, T(1e-6)); - for (int64 k = depth_begin; k < depth_end; ++k) { - T dyi = T(-2) * alpha_ * beta_ * in_shaped(i, k) * - activations(i, j) / norm; - if (k == j) { - dyi += Eigen::numext::pow(norm, -beta_); - } - dyi *= grads_shaped(i, j); - const_cast::Tensor&>(out_shaped)(i, k) += - dyi; - } - } - } - }; - auto worker_threads = - *(context->device()->tensorflow_cpu_worker_threads()); - Shard(worker_threads.num_threads, worker_threads.workers, nodes * batch, - depth * depth, shard); - } - - // release mkl resources - void Mklcleanup() { - bool ingrad_in_mkl_format = ingrad_shape.IsMklTensor(); - bool inimage_in_mkl_format = inimage_shape.IsMklTensor(); - if (!ingrad_in_mkl_format) { - CHECK_EQ(dnnLayoutDelete_F32(lt_input), E_SUCCESS); - } - - if (!inimage_in_mkl_format) { - CHECK_EQ(dnnLayoutDelete_F32(lt_output), E_SUCCESS); - } - dnnDelete_F32(lrn_bwd); - dnnLayoutDelete_F32(lt_bdw_input); - dnnLayoutDelete_F32(lt_workspace); - } - } MklLRNGradOpContext; - - typedef typename Eigen::Tensor::DimensionPair DimPair; - bool workspace_enabled_; - int depth_radius_; - float bias_; - float alpha_; - float beta_; -}; - -#else - template class MklLRNOp : public OpKernel { public: @@ -743,11 +71,10 @@ class MklLRNOp : public OpKernel { explicit MklLRNOp(OpKernelConstruction* context) : OpKernel(context) { int64 depth_radius64; OP_REQUIRES_OK(context, context->GetAttr("depth_radius", &depth_radius64)); - OP_REQUIRES( - context, - FastBoundsCheck(depth_radius64, std::numeric_limits::max()), - errors::InvalidArgument("depth_radius = ", depth_radius64, - " larger than int max")); + OP_REQUIRES(context, FastBoundsCheck(depth_radius64, + std::numeric_limits::max()), + errors::InvalidArgument("depth_radius = ", depth_radius64, + " larger than int max")); depth_radius_ = static_cast(depth_radius64); OP_REQUIRES_OK(context, context->GetAttr("bias", &bias_)); @@ -833,9 +160,9 @@ class MklLRNOp : public OpKernel { PrepareAndExecuteNet(lrn_prim_desc, &src_dnn_data, &dst_dnn_data, &workspace_dnn_data); } catch (mkldnn::error& e) { - string error_msg = "Status: " + std::to_string(e.status) + - ", message: " + string(e.message) + ", in file " + - string(__FILE__) + ":" + std::to_string(__LINE__); + string error_msg = "Status: " + std::to_string(e.status) + ", message: " + + string(e.message) + ", in file " + string(__FILE__) + + ":" + std::to_string(__LINE__); OP_REQUIRES_OK( context, errors::Aborted("Operation received an exception:", error_msg)); @@ -847,7 +174,6 @@ class MklLRNOp : public OpKernel { MklDnnData* src_dnn_data, MklDnnData* dst_dnn_data, MklDnnData* wksp_dnn_data = nullptr) { - // Check for input reorder src_dnn_data->CheckReorderToOpMem(lrn_fwd_desc.src_primitive_desc()); @@ -965,16 +291,14 @@ class MklLRNOp : public OpKernel { if (src_dnn_shape.IsMklTensor()) { OP_REQUIRES(context, src_dnn_shape.GetDimension() == 4, errors::InvalidArgument("input must be 4-dimensional")); - OP_REQUIRES(context, - FastBoundsCheck(src_tensor.NumElements(), - std::numeric_limits::max()), + OP_REQUIRES(context, FastBoundsCheck(src_tensor.NumElements(), + std::numeric_limits::max()), errors::InvalidArgument("argument to LRN too large")); } else { OP_REQUIRES(context, src_tensor.dims() == 4, errors::InvalidArgument("input must be 4-dimensional")); - OP_REQUIRES(context, - FastBoundsCheck(src_tensor.NumElements(), - std::numeric_limits::max()), + OP_REQUIRES(context, FastBoundsCheck(src_tensor.NumElements(), + std::numeric_limits::max()), errors::InvalidArgument("argument to LRN too large")); } } @@ -994,11 +318,10 @@ class MklLRNGradOp : public OpKernel { explicit MklLRNGradOp(OpKernelConstruction* context) : OpKernel(context) { int64 depth_radius64; OP_REQUIRES_OK(context, context->GetAttr("depth_radius", &depth_radius64)); - OP_REQUIRES( - context, - FastBoundsCheck(depth_radius64, std::numeric_limits::max()), - errors::InvalidArgument("depth_radius = ", depth_radius64, - " larger than int max")); + OP_REQUIRES(context, FastBoundsCheck(depth_radius64, + std::numeric_limits::max()), + errors::InvalidArgument("depth_radius = ", depth_radius64, + " larger than int max")); depth_radius_ = static_cast(depth_radius64); OP_REQUIRES_OK(context, context->GetAttr("bias", &bias_)); OP_REQUIRES_OK(context, context->GetAttr("alpha", &alpha_)); @@ -1105,9 +428,9 @@ class MklLRNGradOp : public OpKernel { memory::primitive_desc(target_diff_dst_md, cpu_engine), &workspace_dnn_data); } catch (mkldnn::error& e) { - string error_msg = "Status: " + std::to_string(e.status) + - ", message: " + string(e.message) + ", in file " + - string(__FILE__) + ":" + std::to_string(__LINE__); + string error_msg = "Status: " + std::to_string(e.status) + ", message: " + + string(e.message) + ", in file " + string(__FILE__) + + ":" + std::to_string(__LINE__); OP_REQUIRES_OK( context, errors::Aborted("Operation received an exception:", error_msg)); @@ -1160,7 +483,6 @@ class MklLRNGradOp : public OpKernel { MklDnnData* output_diff_src, const memory::primitive_desc& target_diff_dst_pd, const MklDnnData* workspace_dnn_data = nullptr) { - // Check for input reordering on the diff dst input input_gradient_diff_dst->CheckReorderToOpMem( lrn_bkwd_desc.diff_dst_primitive_desc()); @@ -1345,8 +667,6 @@ class MklLRNGradOp : public OpKernel { float beta_; }; -#endif // INTEL_MKL_ML_ONLY - #define REGISTER_MKL_LRN_CPU(T) \ REGISTER_KERNEL_BUILDER(Name("_MklLRN") \ .Device(DEVICE_CPU) \ diff --git a/tensorflow/core/kernels/mkl_maxpooling_op.cc b/tensorflow/core/kernels/mkl_maxpooling_op.cc index 256d48f4d5..0697251c7d 100644 --- a/tensorflow/core/kernels/mkl_maxpooling_op.cc +++ b/tensorflow/core/kernels/mkl_maxpooling_op.cc @@ -399,19 +399,18 @@ class MklMaxPoolingGradOp : public OpKernel { if (workspace_enabled == false) { if (convert_input != nullptr) { if (input_in_mkl_format == false) { - CHECK_EQ(dnnConversionExecute_F32( - convert_input, - const_cast(static_cast( - tensor_in.flat().data())), - input_buf), - E_SUCCESS); + CHECK_EQ( + dnnConversionExecute_F32( + convert_input, const_cast(static_cast( + tensor_in.flat().data())), + input_buf), + E_SUCCESS); CHECK_EQ(dnnDelete_F32(convert_input), E_SUCCESS); convert_input = nullptr; } else { input_shape.GetConvertedFlatData( - lt_input_prim, - const_cast( - static_cast(tensor_in.flat().data())), + lt_input_prim, const_cast(static_cast( + tensor_in.flat().data())), input_buf); } pooling_resfwd[dnnResourceSrc] = input_buf; @@ -456,9 +455,8 @@ class MklMaxPoolingGradOp : public OpKernel { CHECK_EQ(dnnDelete_F32(convert_outbackprop), E_SUCCESS); } else { output_backprop_shape.GetConvertedFlatData( - lt_outbackprop_prim, - const_cast( - static_cast(out_backprop.flat().data())), + lt_outbackprop_prim, const_cast(static_cast( + out_backprop.flat().data())), outbackprop_buf); } pooling_res[dnnResourceDiffDst] = outbackprop_buf; @@ -520,7 +518,6 @@ class MklMaxPoolingOp : public MklPoolingForwardOpBase { MklDnnData dnn_data_input(&cpu_engine); MklDnnData dnn_data_output(&cpu_engine); - MklDnnData dnn_data_wksp(&cpu_engine); // initialize variables for the pooling op MklPoolParameters pool_params; @@ -550,13 +547,13 @@ class MklMaxPoolingOp : public MklPoolingForwardOpBase { dnn_shape_input.IsMklTensor() ? dnn_shape_input.GetMklLayout() : is_pool2d ? memory::desc( - TFShapeToMklDnnDimsInNCHW(input_tensor_shape, - this->data_format_tf_), - MklDnnType(), this->data_format_mkldnn_) - : memory::desc( - TFShapeToMklDnnDimsInNCDHW( - input_tensor_shape, this->data_format_tf_), - MklDnnType(), this->data_format_mkldnn_); + TFShapeToMklDnnDimsInNCHW( + input_tensor_shape, this->data_format_tf_), + MklDnnType(), this->data_format_mkldnn_) + : memory::desc( + TFShapeToMklDnnDimsInNCDHW( + input_tensor_shape, this->data_format_tf_), + MklDnnType(), this->data_format_mkldnn_); // Get src/filter/stride/padding information memory::dims src_dims = @@ -564,17 +561,24 @@ class MklMaxPoolingOp : public MklPoolingForwardOpBase { ? dnn_shape_input.GetSizesAsMklDnnDims() : is_pool2d ? TFShapeToMklDnnDimsInNCHW(input_tensor.shape(), this->data_format_tf_) - : TFShapeToMklDnnDimsInNCDHW(input_tensor.shape(), - this->data_format_tf_); + : TFShapeToMklDnnDimsInNCDHW(input_tensor.shape(), + this->data_format_tf_); memory::dims filter_dims, strides, padding_left, padding_right; this->PoolParamsToDims(&pool_params, &filter_dims, &strides, &padding_left, &padding_right, is_pool2d); // Get a pooling op from the cached pool MklPoolingFwdPrimitive* pooling_fwd = nullptr; + prop_kind pooling_prop_kind; + bool int8_forward_inference = + std::is_same::value || std::is_same::value; + if (int8_forward_inference) + pooling_prop_kind = prop_kind::forward_inference; + else + pooling_prop_kind = prop_kind::forward_training; MklPoolingParams fwdParams(src_dims, output_dims_mkl_order, filter_dims, strides, padding_left, padding_right, - algorithm::pooling_max); + algorithm::pooling_max, pooling_prop_kind); pooling_fwd = MklPoolingFwdPrimitiveFactory::Get(fwdParams); // allocate output tensor @@ -586,10 +590,6 @@ class MklMaxPoolingOp : public MklPoolingForwardOpBase { pooling_fwd->GetDstMemoryFormat(), output_tensor); - AllocateWorkspaceTensor(context, *(pooling_fwd->GetPoolingFwdPd()), - &dnn_data_wksp); - OP_REQUIRES_OK(context, context->status()); - // check wehther we need to reorder src const T* src_data = input_tensor.flat().data(); if (input_md.data.format != pooling_fwd->GetSrcMemoryFormat()) { @@ -603,14 +603,43 @@ class MklMaxPoolingOp : public MklPoolingForwardOpBase { } T* dst_data = output_tensor->flat().data(); - void* ws_data = dnn_data_wksp.GetOpMem().get_data_handle(); - // execute pooling op - pooling_fwd->Execute(src_data, dst_data, ws_data); + if (int8_forward_inference) { + // Execute pooling op + pooling_fwd->Execute(src_data, dst_data); + + // pass min, max from input to output + const Tensor& min_input_t = MklGetInput(context, 1); + const Tensor& max_input_t = MklGetInput(context, 2); + const float min_input = min_input_t.flat()(0); + const float max_input = max_input_t.flat()(0); + + Tensor* output_min = nullptr; + Tensor* output_max = nullptr; + MklDnnShape output_min_mkl_shape, output_max_mkl_shape; + output_min_mkl_shape.SetMklTensor(false); + output_max_mkl_shape.SetMklTensor(false); + AllocateOutputSetMklShape(context, 1, &output_min, {}, + output_min_mkl_shape); + AllocateOutputSetMklShape(context, 2, &output_max, {}, + output_max_mkl_shape); + output_min->flat()(0) = min_input; + output_max->flat()(0) = max_input; + } else { + MklDnnData dnn_data_wksp(&cpu_engine); + AllocateWorkspaceTensor(context, *(pooling_fwd->GetPoolingFwdPd()), + &dnn_data_wksp); + OP_REQUIRES_OK(context, context->status()); + T* ws_data = + static_cast(dnn_data_wksp.GetOpMem().get_data_handle()); + + // execute pooling op + pooling_fwd->Execute(src_data, dst_data, ws_data); + } } catch (mkldnn::error& e) { - string error_msg = "Status: " + std::to_string(e.status) + - ", message: " + string(e.message) + ", in file " + - string(__FILE__) + ":" + std::to_string(__LINE__); + string error_msg = "Status: " + std::to_string(e.status) + ", message: " + + string(e.message) + ", in file " + string(__FILE__) + + ":" + std::to_string(__LINE__); OP_REQUIRES_OK(context, errors::Aborted("Compute received an exception:", error_msg)); } @@ -684,24 +713,25 @@ class MklMaxPoolingGradOp : public MklPoolingBackwardOpBase { orig_input_mkl_shape.IsMklTensor() ? orig_input_mkl_shape.GetSizesAsMklDnnDims() : is_pool2d ? TFShapeToMklDnnDimsInNCHW(orig_input_shape, - this->data_format_tf_) - : TFShapeToMklDnnDimsInNCDHW(orig_input_shape, - this->data_format_tf_); + this->data_format_tf_) + : TFShapeToMklDnnDimsInNCDHW(orig_input_shape, + this->data_format_tf_); memory::dims diff_dst_dims = grad_mkl_shape.IsMklTensor() ? grad_mkl_shape.GetSizesAsMklDnnDims() : is_pool2d ? TFShapeToMklDnnDimsInNCHW(grad_tensor.shape(), - this->data_format_tf_) - : TFShapeToMklDnnDimsInNCDHW(grad_tensor.shape(), - this->data_format_tf_); + this->data_format_tf_) + : TFShapeToMklDnnDimsInNCDHW(grad_tensor.shape(), + this->data_format_tf_); memory::dims output_dims_mkl_order; this->GetOutputDims(pool_params, &output_dims_mkl_order); MklPoolingParams bwdParams( orig_input_dims_mkl_order, output_dims_mkl_order, filter_dims, - strides, padding_left, padding_right, algorithm::pooling_max); + strides, padding_left, padding_right, algorithm::pooling_max, + prop_kind::forward_training); MklPoolingBwdPrimitive* pooling_bwd = MklPoolingBwdPrimitiveFactory::Get(bwdParams); @@ -751,9 +781,9 @@ class MklMaxPoolingGradOp : public MklPoolingBackwardOpBase { // execute pooling pooling_bwd->Execute(diff_dst_data, diff_src_data, ws_data); } catch (mkldnn::error& e) { - string error_msg = "Status:" + std::to_string(e.status) + - ", message: " + string(e.message) + ". in file " + - string(__FILE__) + ":" + std::to_string(__LINE__); + string error_msg = "Status:" + std::to_string(e.status) + ", message: " + + string(e.message) + ". in file " + string(__FILE__) + + ":" + std::to_string(__LINE__); OP_REQUIRES_OK(context, errors::Aborted("Compute received an exception:", error_msg)); } @@ -788,39 +818,38 @@ class MklMaxPoolingGradOp : public MklPoolingBackwardOpBase { const MklDnnShape& workspace_mkl_shape) { if (!orig_input_mkl_shape.IsMklTensor()) { OP_REQUIRES(context, orig_input_tensor.dims() == 4, - errors::InvalidArgument("Original input shape must be " - "4-dimensional")); + errors::InvalidArgument( + "Original input shape must be 4-dimensional")); } else { OP_REQUIRES(context, orig_input_mkl_shape.GetDimension() == 4, - errors::InvalidArgument("Original input shape must be " - "4-dimensional")); + errors::InvalidArgument( + "Original input shape must be 4-dimensional")); } if (!orig_output_mkl_shape.IsMklTensor()) { - OP_REQUIRES(context, orig_output_tensor.dims() == 4, - errors::InvalidArgument("Original output must be " - "4-dimensional")); + OP_REQUIRES( + context, orig_output_tensor.dims() == 4, + errors::InvalidArgument("Original output must be 4-dimensional")); } else { - OP_REQUIRES(context, orig_output_mkl_shape.GetDimension() == 4, - errors::InvalidArgument("Original output must be " - "4-dimensional")); + OP_REQUIRES( + context, orig_output_mkl_shape.GetDimension() == 4, + errors::InvalidArgument("Original output must be 4-dimensional")); } if (!grad_mkl_shape.IsMklTensor()) { OP_REQUIRES(context, grad_tensor.dims() == 4, errors::InvalidArgument("Gradient must be 4-dimensional")); } else { OP_REQUIRES(context, grad_mkl_shape.GetDimension() == 4, - errors::InvalidArgument("Gradient must be " - "4-dimensional")); + errors::InvalidArgument("Gradient must be 4-dimensional")); } if (this->workspace_enabled_) { // The workspace should not be an MKL tensor OP_REQUIRES(context, workspace_mkl_shape.IsMklTensor() == false, - errors::InvalidArgument("Workspace tensor should not" - " be an MKL Tensor.")); + errors::InvalidArgument( + "Workspace tensor should not be an MKL Tensor.")); // It should only have one dimension - OP_REQUIRES(context, workspace_tensor.dims() == 1, - errors::InvalidArgument("Workspace tensor must be " - "1-dimensional")); + OP_REQUIRES( + context, workspace_tensor.dims() == 1, + errors::InvalidArgument("Workspace tensor must be 1-dimensional")); } else { OP_REQUIRES( context, this->workspace_enabled_, @@ -852,6 +881,18 @@ REGISTER_KERNEL_BUILDER(Name("_MklMaxPool") .Label(mkl_op_registry::kMklOpLabel), MklMaxPoolingOp); +REGISTER_KERNEL_BUILDER(Name("_MklQuantizedMaxPool") + .Device(DEVICE_CPU) + .TypeConstraint("T") + .Label(mkl_op_registry::kMklQuantizedOpLabel), + MklMaxPoolingOp); + +REGISTER_KERNEL_BUILDER(Name("_MklQuantizedMaxPool") + .Device(DEVICE_CPU) + .TypeConstraint("T") + .Label(mkl_op_registry::kMklQuantizedOpLabel), + MklMaxPoolingOp); + REGISTER_KERNEL_BUILDER(Name("_MklMaxPoolGrad") .Device(DEVICE_CPU) .TypeConstraint("T") diff --git a/tensorflow/core/kernels/mkl_pooling_ops_common.cc b/tensorflow/core/kernels/mkl_pooling_ops_common.cc index 5398e6113f..dc84d3941e 100644 --- a/tensorflow/core/kernels/mkl_pooling_ops_common.cc +++ b/tensorflow/core/kernels/mkl_pooling_ops_common.cc @@ -41,28 +41,33 @@ void MklPoolingFwdPrimitive::Setup(const MklPoolingParams& fwdParams) { << "Pooling algorithm kind is not supported"; context_.alg_kind = fwdParams.alg_kind; + context_.prop_kind = fwdParams.prop_kind; + // create memory desc // FIXME: Pooling doesn't expose to get the src_primitive_desc, // so src format is currently hard-coded. // A utility function is used to do this, // which may be broken with future CPU architectures bool is_2d = (fwdParams.src_dims.size() == 4); - context_.src_md.reset( - new memory::desc({fwdParams.src_dims}, MklDnnType(), - get_desired_format(fwdParams.src_dims[1], is_2d))); + if (std::is_same::value || std::is_same::value) + context_.src_fmt = is_2d ? memory::format::nhwc : memory::format::ndhwc; + else + context_.src_fmt = get_desired_format(fwdParams.src_dims[1], is_2d); + + context_.src_md.reset(new memory::desc({fwdParams.src_dims}, MklDnnType(), + context_.src_fmt)); context_.dst_md.reset(new memory::desc({fwdParams.dst_dims}, MklDnnType(), memory::format::any)); // create a pooling descriptor context_.fwd_desc.reset(new pooling_forward::desc( - prop_kind::forward_training, fwdParams.alg_kind, *context_.src_md, + fwdParams.prop_kind, fwdParams.alg_kind, *context_.src_md, *context_.dst_md, fwdParams.strides, fwdParams.filter_dims, fwdParams.padding_left, fwdParams.padding_right, padding_kind::zero)); context_.fwd_pd.reset( new pooling_forward::primitive_desc(*context_.fwd_desc, cpu_engine_)); // store expected primitive format - context_.src_fmt = get_desired_format(fwdParams.src_dims[1], is_2d); context_.dst_fmt = static_cast( context_.fwd_pd.get()->dst_primitive_desc().desc().data.format); @@ -74,7 +79,8 @@ void MklPoolingFwdPrimitive::Setup(const MklPoolingParams& fwdParams) { new memory(context_.fwd_pd.get()->dst_primitive_desc(), DummyData)); // for max pooling, need to return workspace(ws) for backward computing - if (fwdParams.alg_kind == pooling_max) { + if (fwdParams.alg_kind == pooling_max && + fwdParams.prop_kind == prop_kind::forward_training) { auto ws_pd = context_.fwd_pd.get()->workspace_primitive_desc().desc().data; // store workspace's dims and format to create workspace tensor context_.ws_fmt = static_cast(ws_pd.format); @@ -101,7 +107,9 @@ void MklPoolingFwdPrimitive::Execute(const T* src_data, T* dst_data, context_.src_mem->set_data_handle( static_cast(const_cast(src_data))); context_.dst_mem->set_data_handle(static_cast(dst_data)); - if (context_.alg_kind == pooling_max) { // max pooling must have ws + if (context_.alg_kind == pooling_max && + context_.prop_kind == + prop_kind::forward_training) { // max pooling must have ws DCHECK(ws_data != nullptr); context_.ws_mem->set_data_handle(ws_data); } @@ -110,13 +118,17 @@ void MklPoolingFwdPrimitive::Execute(const T* src_data, T* dst_data, // set back data handle context_.src_mem->set_data_handle(DummyData); context_.dst_mem->set_data_handle(DummyData); - if (context_.alg_kind == pooling_max) { // max pooling must have ws + if (context_.alg_kind == pooling_max && + context_.prop_kind == + prop_kind::forward_training) { // max pooling must have ws DCHECK(ws_data != nullptr); context_.ws_mem->set_data_handle(DummyData); } } template class MklPoolingFwdPrimitive; +template class MklPoolingFwdPrimitive; +template class MklPoolingFwdPrimitive; template void MklPoolingBwdPrimitive::Setup(const MklPoolingParams& bwdParams) { @@ -143,7 +155,7 @@ void MklPoolingBwdPrimitive::Setup(const MklPoolingParams& bwdParams) { // create a forward primitive, // which will be used as a hint for creating backward primitive context_.fwd_desc.reset(new pooling_forward::desc( - prop_kind::forward_training, bwdParams.alg_kind, *context_.diff_src_md, + bwdParams.prop_kind, bwdParams.alg_kind, *context_.diff_src_md, *context_.diff_dst_md, bwdParams.strides, bwdParams.filter_dims, bwdParams.padding_left, bwdParams.padding_right, padding_kind::zero)); context_.fwd_pd.reset( diff --git a/tensorflow/core/kernels/mkl_pooling_ops_common.h b/tensorflow/core/kernels/mkl_pooling_ops_common.h index 49f799d7ba..8a60c3be91 100644 --- a/tensorflow/core/kernels/mkl_pooling_ops_common.h +++ b/tensorflow/core/kernels/mkl_pooling_ops_common.h @@ -18,8 +18,8 @@ limitations under the License. #ifdef INTEL_MKL #include -#include #include +#include #include "tensorflow/core/util/mkl_util.h" #include "tensorflow/core/util/padding.h" @@ -50,18 +50,20 @@ struct MklPoolingParams { memory::dims padding_left; memory::dims padding_right; mkldnn::algorithm alg_kind; + mkldnn::prop_kind prop_kind; MklPoolingParams(memory::dims src_dims, memory::dims dst_dims, memory::dims filter_dims, memory::dims strides, memory::dims padding_left, memory::dims padding_right, - mkldnn::algorithm alg_kind) + mkldnn::algorithm alg_kind, mkldnn::prop_kind prop_kind) : src_dims(src_dims), dst_dims(dst_dims), filter_dims(filter_dims), strides(strides), padding_left(padding_left), padding_right(padding_right), - alg_kind(alg_kind) {} + alg_kind(alg_kind), + prop_kind(prop_kind) {} }; template @@ -97,6 +99,9 @@ class MklPoolingFwdPrimitive : public MklPrimitive { // algorithm mkldnn::algorithm alg_kind; + // Kind of propagation, forward or backward + mkldnn::prop_kind prop_kind; + // expected memory format memory::format src_fmt; memory::format dst_fmt; @@ -187,6 +192,7 @@ class MklPoolingFwdPrimitiveFactory : public MklPrimitiveFactory { key_creator.AddAsKey(fwdParams.padding_left); key_creator.AddAsKey(fwdParams.padding_right); key_creator.AddAsKey(static_cast(fwdParams.alg_kind)); + key_creator.AddAsKey(static_cast(fwdParams.prop_kind)); return key_creator.GetKey(); } @@ -443,7 +449,12 @@ class MklPoolingOpBase : public OpKernel { explicit MklPoolingOpBase(OpKernelConstruction* context) : OpKernel(context), workspace_enabled_(false) { string data_format; - OP_REQUIRES_OK(context, context->GetAttr("data_format", &data_format)); + if (std::is_same::value || std::is_same::value) { + // current quantized convolution doesn't have data_format attribute. + data_format = "NHWC"; + } else { + OP_REQUIRES_OK(context, context->GetAttr("data_format", &data_format)); + } OP_REQUIRES(context, FormatFromString(data_format, &this->data_format_tf_), errors::InvalidArgument("Invalid data format")); OP_REQUIRES_OK(context, context->GetAttr("ksize", &this->ksize_)); @@ -461,7 +472,7 @@ class MklPoolingOpBase : public OpKernel { bool is_pool2d = (this->ksize_.size() == 4); this->data_format_mkldnn_ = is_pool2d ? TFDataFormatToMklDnnDataFormat(this->data_format_tf_) - : TFDataFormatToMklDnn3DDataFormat(this->data_format_tf_); + : TFDataFormatToMklDnn3DDataFormat(this->data_format_tf_); // We may not get this attribute for this node if it does not go through // graph rewrite pass. So we do not check for error while retrieving this @@ -655,10 +666,10 @@ class MklPoolingForwardOpBase : public MklPoolingOpBase { OP_REQUIRES(context, input_tensor.dims() == 4 || input_tensor.dims() == 5, errors::InvalidArgument("Input must be 4 or 5-dimensional")); } else { - OP_REQUIRES(context, input_mkl_shape.GetDimension() == 4 || - input_mkl_shape.GetDimension() == 5, - errors::InvalidArgument("Input shape must be " - "4 or 5-dimensional")); + OP_REQUIRES( + context, input_mkl_shape.GetDimension() == 4 || + input_mkl_shape.GetDimension() == 5, + errors::InvalidArgument("Input shape must be 4 or 5-dimensional")); } } // .Input("value: T") diff --git a/tensorflow/core/kernels/mkl_quantized_pooling_ops_test.cc b/tensorflow/core/kernels/mkl_quantized_pooling_ops_test.cc new file mode 100644 index 0000000000..7c1e32d6e3 --- /dev/null +++ b/tensorflow/core/kernels/mkl_quantized_pooling_ops_test.cc @@ -0,0 +1,201 @@ +/* Copyright 2015 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. +==============================================================================*/ +#ifdef INTEL_MKL +#define EIGEN_USE_THREADS + +#include "tensorflow/core/framework/allocator.h" +#include "tensorflow/core/framework/fake_input.h" +#include "tensorflow/core/framework/node_def_builder.h" +#include "tensorflow/core/framework/op_kernel.h" +#include "tensorflow/core/framework/tensor.h" +#include "tensorflow/core/framework/tensor_testutil.h" +#include "tensorflow/core/framework/types.h" +#include "tensorflow/core/framework/types.pb.h" +#include "tensorflow/core/kernels/ops_testutil.h" +#include "tensorflow/core/kernels/ops_util.h" +#include "tensorflow/core/kernels/quantization_utils.h" +#include "tensorflow/core/lib/core/status_test_util.h" +#include "tensorflow/core/platform/test.h" + +namespace tensorflow { + +// Helper class for converting MKL tensors to TF tensors and comparing to +// expected values + +static const uint8 dummy_tensor[] = {0, 0, 0, 0, 0, 0, 0, 0}; +static const TensorShape dummy_shape({8}); + +class ConvMklToTF : public OpsTestBase { + public: + template + void ConvertMKL2TF(DataType dtype, const Tensor& first, const Tensor& second, + Tensor& output) { + // Create an MKL to TF conversion node and execute it + TF_EXPECT_OK(NodeDefBuilder("mkl_to_tf_op", "_MklToTf") + .Input(FakeInput(dtype)) // Input + .Input(FakeInput(DT_UINT8)) // Mkl second tensor + .Attr("T", dtype) + .Attr("_kernel", "MklOp") + .Finalize(node_def())); + TF_EXPECT_OK(InitOp()); + AddInputFromArray(first.shape(), first.flat()); + AddInputFromArray(second.shape(), second.flat()); + TF_ASSERT_OK(RunOpKernel()); + + output = *GetOutput(0); + } + void TestBody(){}; +}; + +class QuantizedPoolingTest : public OpsTestBase {}; + +TEST_F(QuantizedPoolingTest, SmallAveragePooling) { + const int ksize = 2; + const int stride = 2; + TF_ASSERT_OK(NodeDefBuilder("quantized_avg_pool_op", "_MklQuantizedAvgPool") + .Input(FakeInput(DT_QUINT8)) + .Input(FakeInput(DT_FLOAT)) + .Input(FakeInput(DT_FLOAT)) + .Input(FakeInput(DT_UINT8)) // MKl second tensor + .Input(FakeInput(DT_UINT8)) // MKl second tensor + .Input(FakeInput(DT_UINT8)) // MKl second tensor + .Attr("T", DataTypeToEnum::v()) + .Attr("ksize", {1, ksize, ksize, 1}) + .Attr("strides", {1, stride, stride, 1}) + .Attr("padding", "SAME") + .Attr("_kernel", "QuantizedMklOp") + .Finalize(node_def())); + TF_ASSERT_OK(InitOp()); + const float input_min = 0.0f; + const float input_max = 255.0f; + const int input_height = 4; + const int input_width = 4; + const int input_channels = 2; + Tensor input_float(DT_FLOAT, {1, input_height, input_width, input_channels}); + test::FillValues( + &input_float, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}); + Tensor input_quantized = + FloatTensorToQuantized(input_float, input_min, input_max); + + const int expected_width = input_width / stride; + const int expected_height = input_height / stride; + + // The input pools we are averaging. (NHWC input, quantized.) + // 0th channel 1st channel + // 1 3 | 5 7 2 4 | 6 8 + // 9 11 | 13 15 10 12 | 14 16 + // ------------- ------------- + // 17 19 | 21 23 18 20 | 22 24 + // 25 27 | 29 31 26 28 | 30 32 + Tensor expected_float(DT_FLOAT, + {1, expected_height, expected_width, input_channels}); + test::FillValues(&expected_float, {6, 7, 10, 11, 22, 23, 26, 27}); + + AddInputFromArray(input_quantized.shape(), + input_quantized.flat()); + AddInputFromArray(TensorShape({1}), {input_min}); + AddInputFromArray(TensorShape({1}), {input_max}); + AddInputFromArray(dummy_shape, dummy_tensor); + AddInputFromArray(dummy_shape, dummy_tensor); + AddInputFromArray(dummy_shape, dummy_tensor); + + TF_ASSERT_OK(RunOpKernel()); + + const Tensor& output = *GetOutput(0); + const Tensor& mkl_shape_tensor = *GetOutput(3); + ConvMklToTF conv_comp; + Tensor output_quantized; + conv_comp.ConvertMKL2TF(DT_QUINT8, output, mkl_shape_tensor, + output_quantized); + + const float output_min = GetOutput(1)->flat()(0); + const float output_max = GetOutput(2)->flat()(0); + Tensor output_float = + QuantizedTensorToFloat(output_quantized, output_min, output_max); + + test::ExpectTensorNear(expected_float, output_float, 0.2); +} + +TEST_F(QuantizedPoolingTest, SmallMaxPooling) { + const int ksize = 2; + const int stride = 2; + TF_ASSERT_OK(NodeDefBuilder("quantized_max_pool_op", "_MklQuantizedMaxPool") + .Input(FakeInput(DT_QUINT8)) + .Input(FakeInput(DT_FLOAT)) + .Input(FakeInput(DT_FLOAT)) + .Input(FakeInput(DT_UINT8)) // MKl second tensor + .Input(FakeInput(DT_UINT8)) // MKl second tensor + .Input(FakeInput(DT_UINT8)) // MKl second tensor + .Attr("T", DataTypeToEnum::v()) + .Attr("ksize", {1, ksize, ksize, 1}) + .Attr("strides", {1, stride, stride, 1}) + .Attr("padding", "SAME") + .Attr("_kernel", "QuantizedMklOp") + .Finalize(node_def())); + TF_ASSERT_OK(InitOp()); + const float input_min = 0.0f; + const float input_max = 255.0f; + const int input_height = 4; + const int input_width = 4; + const int input_channels = 2; + Tensor input_float(DT_FLOAT, {1, input_height, input_width, input_channels}); + test::FillValues( + &input_float, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}); + Tensor input_quantized = + FloatTensorToQuantized(input_float, input_min, input_max); + const int expected_width = input_width / stride; + const int expected_height = input_height / stride; + + // The max is computed from these input pools. (NHWC input, quantized.) + // 0th channel 1st channel + // 1 3 | 5 7 2 4 | 6 8 + // 9 11 | 13 15 10 12 | 14 16 + // ------------- ------------- + // 17 19 | 21 23 18 20 | 22 24 + // 25 27 | 29 31 26 28 | 30 32 + + Tensor expected_float(DT_FLOAT, + {1, expected_height, expected_width, input_channels}); + test::FillValues(&expected_float, {11, 12, 15, 16, 27, 28, 31, 32}); + AddInputFromArray(input_quantized.shape(), + input_quantized.flat()); + AddInputFromArray(TensorShape({1}), {input_min}); + AddInputFromArray(TensorShape({1}), {input_max}); + AddInputFromArray(dummy_shape, dummy_tensor); + AddInputFromArray(dummy_shape, dummy_tensor); + AddInputFromArray(dummy_shape, dummy_tensor); + + TF_ASSERT_OK(RunOpKernel()); + + const Tensor& output = *GetOutput(0); + const Tensor& mkl_shape_tensor = *GetOutput(3); + ConvMklToTF conv_comp; + Tensor output_quantized; + conv_comp.ConvertMKL2TF(DT_QUINT8, output, mkl_shape_tensor, + output_quantized); + + const float output_min = GetOutput(1)->flat()(0); + const float output_max = GetOutput(2)->flat()(0); + Tensor output_float = + QuantizedTensorToFloat(output_quantized, output_min, output_max); + + test::ExpectTensorNear(expected_float, output_float, 0.2); +} +} // namespace tensorflow +#endif diff --git a/tensorflow/core/kernels/mkl_softmax_op.cc b/tensorflow/core/kernels/mkl_softmax_op.cc index cfab529662..3bf17bc449 100644 --- a/tensorflow/core/kernels/mkl_softmax_op.cc +++ b/tensorflow/core/kernels/mkl_softmax_op.cc @@ -56,7 +56,7 @@ class MklSoftmaxOp : public OpKernel { MklDnnShape src_mkl_shape; GetMklShape(context, src_idx, &src_mkl_shape); - // src_dims is the dimenstion of src_tensor + // src_dims is the dimension of src_tensor // dim of the dst will also be same as src_dims auto src_tf_shape = src_mkl_shape.IsMklTensor() ? src_mkl_shape.GetTfShape() @@ -68,7 +68,7 @@ class MklSoftmaxOp : public OpKernel { // Here "x" data format in MKL is used for 1 dim tensor, "nc" for 2 dim tensor, // "tnc" for 3 dim tensor, "nchw" for 4 dim tensor, and "ncdhw" for 5 dim tensor. // Each of the simbols has the following meaning: - // n = batch, c = channels, t = sequence lenght, h = height, + // n = batch, c = channels, t = sequence length, h = height, // w = width, d = depth switch (input_dims) { case 1: diff --git a/tensorflow/core/kernels/partitioned_function_ops.cc b/tensorflow/core/kernels/partitioned_function_ops.cc index 71e506e5e6..30a8be141c 100644 --- a/tensorflow/core/kernels/partitioned_function_ops.cc +++ b/tensorflow/core/kernels/partitioned_function_ops.cc @@ -28,6 +28,7 @@ limitations under the License. #include "tensorflow/core/grappler/grappler_item.h" #include "tensorflow/core/grappler/optimizers/meta_optimizer.h" #include "tensorflow/core/grappler/utils/functions.h" +#include "tensorflow/core/protobuf/config.pb.h" #include "tensorflow/core/protobuf/rewriter_config.pb.h" #include "tensorflow/core/util/ptr_util.h" #include "tensorflow/core/util/reffed_status_callback.h" @@ -50,12 +51,29 @@ class PartitionedCallOp : public AsyncOpKernel { public: explicit PartitionedCallOp(OpKernelConstruction* ctx) : AsyncOpKernel(ctx) { OP_REQUIRES_OK(ctx, ctx->GetAttr("f", &func_)); - string rewriter_config_serialized; - OP_REQUIRES_OK(ctx, ctx->GetAttr("config", &rewriter_config_serialized)); + string deprecated_config_serialized; + OP_REQUIRES_OK(ctx, ctx->GetAttr("config", &deprecated_config_serialized)); + string config_proto_serialized; + OP_REQUIRES_OK(ctx, ctx->GetAttr("config_proto", &config_proto_serialized)); OP_REQUIRES( - ctx, rewriter_config_.ParseFromString(rewriter_config_serialized), - errors::InvalidArgument("Unable to parse rewriter_config string as " - "tensorflow::RewriterConfig proto.")); + ctx, + deprecated_config_serialized.empty() || config_proto_serialized.empty(), + errors::InvalidArgument("Provided both 'config' and 'config_proto' but " + "only one should be provided. Note the " + "'config' option is deprecated.")); + if (!deprecated_config_serialized.empty()) { + OP_REQUIRES(ctx, + config_proto_.mutable_graph_options() + ->mutable_rewrite_options() + ->ParseFromString(deprecated_config_serialized), + errors::InvalidArgument("Unable to parse config string as " + "tensorflow::RewriteOptions proto.")); + } else { + OP_REQUIRES( + ctx, config_proto_.ParseFromString(config_proto_serialized), + errors::InvalidArgument("Unable to parse config_proto string as " + "tensorflow::ConfigProto proto.")); + } OP_REQUIRES_OK(ctx, ctx->GetAttr("executor_type", &executor_type_)); } @@ -435,7 +453,7 @@ class PartitionedCallOp : public AsyncOpKernel { }, rendez, std::move(done), std::placeholders::_1); auto* refcounted_done = new ReffedStatusCallback(std::move(callback)); - for (int i = 1; i < handles->size(); ++i) { + for (int i = 0; i < handles->size(); ++i) { refcounted_done->Ref(); } @@ -489,6 +507,7 @@ class PartitionedCallOp : public AsyncOpKernel { }); } } + refcounted_done->Unref(); } string UniquifyFunctionName(const FunctionLibraryDefinition* function_library, @@ -506,12 +525,18 @@ class PartitionedCallOp : public AsyncOpKernel { FunctionLibraryDefinition* flib, const DeviceSet& device_set, Device* cpu_device, std::unique_ptr* graph) { - if (!tensorflow::grappler::MetaOptimizerEnabled(rewriter_config_)) { + if (!tensorflow::grappler::MetaOptimizerEnabled(config_proto_)) { return Status::OK(); } tensorflow::grappler::GrapplerItem item; + // Add all available devices so that inlined function can be placed. + for (const Device* d : device_set.devices()) { + Status added_device = item.AddDevice(d->name()); + if (!added_device.ok()) VLOG(3) << added_device.error_message(); + } + // Add fetches so that the graph can be pruned. for (Node* node : ret_nodes) { item.fetch.push_back(node->name()); @@ -530,7 +555,7 @@ class PartitionedCallOp : public AsyncOpKernel { // TODO(nareshmodi): Consider adding and using the more generic GraphOptions // proto (which also contain the OptimizerOptions). TF_RETURN_IF_ERROR(tensorflow::grappler::RunMetaOptimizer( - item, rewriter_config_, cpu_device, &cluster, &out_graph)); + item, config_proto_, cpu_device, &cluster, &out_graph)); std::unique_ptr optimized_graph(new Graph(OpRegistry::Global())); TF_RETURN_IF_ERROR(ConvertGraphDefToGraph( @@ -562,7 +587,7 @@ class PartitionedCallOp : public AsyncOpKernel { } NameAttrList func_; - RewriterConfig rewriter_config_; + ConfigProto config_proto_; string executor_type_; // Contains maps from device names to handles of function partitions, keyed by // FunctionLibraryRuntime pointers. (Because this kernel may be instantiated diff --git a/tensorflow/core/kernels/quantize_and_dequantize_op.cc b/tensorflow/core/kernels/quantize_and_dequantize_op.cc index dadc15b69e..f13341e0af 100644 --- a/tensorflow/core/kernels/quantize_and_dequantize_op.cc +++ b/tensorflow/core/kernels/quantize_and_dequantize_op.cc @@ -49,6 +49,21 @@ class QuantizeAndDequantizeV2Op : public OpKernel { errors::InvalidArgument("num_bits is out of range: ", num_bits_, " with signed_input_ ", signed_input_)); OP_REQUIRES_OK(ctx, ctx->GetAttr("range_given", &range_given_)); + + string round_mode_string; + OP_REQUIRES_OK(ctx, ctx->GetAttr("round_mode", &round_mode_string)); + OP_REQUIRES( + ctx, + (round_mode_string == "HALF_UP" || round_mode_string == "HALF_TO_EVEN"), + errors::InvalidArgument("Round mode string must be " + "'HALF_UP' or " + "'HALF_TO_EVEN', is '" + + round_mode_string + "'")); + if (round_mode_string == "HALF_UP") { + round_mode_ = ROUND_HALF_UP; + } else if (round_mode_string == "HALF_TO_EVEN") { + round_mode_ = ROUND_HALF_TO_EVEN; + } } void Compute(OpKernelContext* ctx) override { @@ -76,13 +91,15 @@ class QuantizeAndDequantizeV2Op : public OpKernel { functor::QuantizeAndDequantizeOneScaleFunctor f; f(ctx->eigen_device(), input.flat(), signed_input_, num_bits_, - range_given_, &input_min_tensor, &input_max_tensor, output->flat()); + range_given_, &input_min_tensor, &input_max_tensor, round_mode_, + output->flat()); } private: bool signed_input_; int num_bits_; bool range_given_; + QuantizerRoundMode round_mode_; }; // Simulate quantization precision loss in a float tensor by: @@ -135,7 +152,8 @@ class QuantizeAndDequantizeV3Op : public OpKernel { functor::QuantizeAndDequantizeOneScaleFunctor f; f(ctx->eigen_device(), input.flat(), signed_input_, num_bits_val, - range_given_, &input_min_tensor, &input_max_tensor, output->flat()); + range_given_, &input_min_tensor, &input_max_tensor, ROUND_HALF_TO_EVEN, + output->flat()); } private: @@ -180,7 +198,7 @@ class QuantizeAndDequantizeOp : public OpKernel { functor::QuantizeAndDequantizeOneScaleFunctor functor; functor(ctx->eigen_device(), input.flat(), signed_input_, num_bits_, range_given_, &input_min_tensor, &input_max_tensor, - output->flat()); + ROUND_HALF_TO_EVEN, output->flat()); } private: @@ -198,10 +216,11 @@ struct QuantizeAndDequantizeOneScaleFunctor { void operator()(const CPUDevice& d, typename TTypes::ConstVec input, const bool signed_input, const int num_bits, const bool range_given, Tensor* input_min_tensor, - Tensor* input_max_tensor, typename TTypes::Vec out) { + Tensor* input_max_tensor, QuantizerRoundMode round_mode, + typename TTypes::Vec out) { QuantizeAndDequantizeOneScaleImpl::Compute( d, input, signed_input, num_bits, range_given, input_min_tensor, - input_max_tensor, out); + input_max_tensor, round_mode, out); } }; } // namespace functor diff --git a/tensorflow/core/kernels/quantize_and_dequantize_op.h b/tensorflow/core/kernels/quantize_and_dequantize_op.h index 6b0c5e5a46..a495e8b71f 100644 --- a/tensorflow/core/kernels/quantize_and_dequantize_op.h +++ b/tensorflow/core/kernels/quantize_and_dequantize_op.h @@ -22,6 +22,20 @@ limitations under the License. #include "tensorflow/core/kernels/cwise_ops.h" namespace tensorflow { + +enum QuantizerRoundMode { + // Round half up: if the fraction of y is exactly 0.5, then + // round(y) = y + 0.5 + // E.g., -5.5 gets rounded to -5, -5.4 goes to -5, + // 5.4 goes to 5, and 5.5 goes to 6. + ROUND_HALF_UP, + // Round half to even: if the fraction of y is exactly 0.5, then round(y) is + // the nearest even integer to y. + // E.g., 23.5 gets rounded to 24, 24.5 gets rounded to 24, while -23.5 becomes + // -24, and -24.5 gets rounded to 24. + ROUND_HALF_TO_EVEN, +}; + namespace functor { // TODO(pauldonnelly): 'signed_input' should really be called 'signed_output'. @@ -31,15 +45,69 @@ struct QuantizeAndDequantizeOneScaleFunctor { void operator()(const Device& d, typename TTypes::ConstVec input, bool signed_input, int num_bits, bool range_given, Tensor* input_min_tensor, Tensor* input_max_tensor, - typename TTypes::Vec out); + QuantizerRoundMode round_mode, typename TTypes::Vec out); }; +// The implementation below runs on both CPU and GPU. +template +void ClampScaleAndRound(const Device& d, typename TTypes::ConstVec input, + T min_range, T max_range, T scale, T inverse_scale, + Func round_func, typename TTypes::Vec out) { + out.device(d) = (input.cwiseMin(max_range).cwiseMax(min_range) * scale) + .unaryExpr(round_func) * + inverse_scale; +} + +// The implementation below runs on both CPU and GPU. +template +void ClampScaleAndRound(const Device& d, typename TTypes::ConstVec input, + T min_range, T max_range, T scale, T inverse_scale, + QuantizerRoundMode round_mode, + typename TTypes::Vec out) { + switch (round_mode) { + case ROUND_HALF_TO_EVEN: + ClampScaleAndRound(d, input, min_range, max_range, scale, inverse_scale, + Eigen::internal::scalar_round_op_google(), out); + break; + case ROUND_HALF_UP: + ClampScaleAndRound(d, input, min_range, max_range, scale, inverse_scale, + Eigen::internal::scalar_round_up_op(), out); + break; + } +} + +// The implementation below runs on both CPU and GPU. +template +void ScaleAndRound(const Device& d, typename TTypes::ConstVec input, T scale, + T inverse_scale, Func round_func, + typename TTypes::Vec out) { + out.device(d) = (input * scale).unaryExpr(round_func) * inverse_scale; +} + +// The implementation below runs on both CPU and GPU. +template +void ScaleAndRound(const Device& d, typename TTypes::ConstVec input, T scale, + T inverse_scale, QuantizerRoundMode round_mode, + typename TTypes::Vec out) { + switch (round_mode) { + case ROUND_HALF_TO_EVEN: + ScaleAndRound(d, input, scale, inverse_scale, + Eigen::internal::scalar_round_op_google(), out); + break; + case ROUND_HALF_UP: + ScaleAndRound(d, input, scale, inverse_scale, + Eigen::internal::scalar_round_up_op(), out); + break; + } +} + // The implementation below runs on both CPU and GPU. template struct QuantizeAndDequantizeOneScaleImpl { static void Compute(const Device& d, typename TTypes::ConstVec input, bool signed_input, int num_bits, bool range_given, Tensor* input_min_tensor, Tensor* input_max_tensor, + QuantizerRoundMode round_mode, typename TTypes::Vec out) { T min_range; T max_range; @@ -89,15 +157,10 @@ struct QuantizeAndDequantizeOneScaleImpl { // The semantics of the op does not guarantee to clamp to the specified // min_range and max_range - because we may have changed either min_range // or max_range. - out.device(d) = - (input.cwiseMin(max_range).cwiseMax(min_range) * scale) - .unaryExpr(Eigen::internal::scalar_round_op_google()) * - inverse_scale; + ClampScaleAndRound(d, input, min_range, max_range, scale, inverse_scale, + round_mode, out); } else { - out.device(d) = - (input * scale) - .unaryExpr(Eigen::internal::scalar_round_op_google()) * - inverse_scale; + ScaleAndRound(d, input, scale, inverse_scale, round_mode, out); } } }; diff --git a/tensorflow/core/kernels/quantize_and_dequantize_op_gpu.cu.cc b/tensorflow/core/kernels/quantize_and_dequantize_op_gpu.cu.cc index 61c79cf695..5745e418f3 100644 --- a/tensorflow/core/kernels/quantize_and_dequantize_op_gpu.cu.cc +++ b/tensorflow/core/kernels/quantize_and_dequantize_op_gpu.cu.cc @@ -32,10 +32,10 @@ struct QuantizeAndDequantizeOneScaleFunctor { void operator()(const GPUDevice& d, typename TTypes::ConstVec input, bool signed_input, int num_bits, bool range_given, Tensor* input_min_tensor, Tensor* input_max_tensor, - typename TTypes::Vec out) { + QuantizerRoundMode round_mode, typename TTypes::Vec out) { QuantizeAndDequantizeOneScaleImpl::Compute( d, input, signed_input, num_bits, range_given, input_min_tensor, - input_max_tensor, out); + input_max_tensor, round_mode, out); } }; } // end namespace functor diff --git a/tensorflow/core/kernels/quantize_and_dequantize_op_test.cc b/tensorflow/core/kernels/quantize_and_dequantize_op_test.cc index cddabf8a99..b9e015c96b 100644 --- a/tensorflow/core/kernels/quantize_and_dequantize_op_test.cc +++ b/tensorflow/core/kernels/quantize_and_dequantize_op_test.cc @@ -101,17 +101,51 @@ TEST_F(QuantizeAndDequantizeTest, Convert_1D_tensor_with_int8) { .Attr("range_given", false) .Finalize(node_def())); TF_ASSERT_OK(InitOp()); - AddInputFromArray(TensorShape({6}), {-1, -0.5, 0, 0.3, 0.8, 0.555}); + AddInputFromArray(TensorShape({7}), + {-1, -0.5, 0, 0.3, 0.8, 0.555, 0.50390625}); AddInputFromArray(TensorShape({}), {0.0}); // Min AddInputFromArray(TensorShape({}), {0.0}); // Max - // With int8, the tensor is quantized to {-128, -64, 0, 38, 102, 71}. + // With int8, the tensor is quantized to {-128, -64, 0, 38, 102, 71, 64}. // Scale is: 1/127 - // Then it is dequantized to {-1, -0.5, 0, 38.0/128, 102.0/128, 71.0/128} + // Then it is dequantized to {-1, -0.5, 0, 38.0/128, 102.0/128, 71.0/128, 0.5} TF_ASSERT_OK(RunOpKernel()); - Tensor expected(allocator(), DT_FLOAT, TensorShape({6})); - test::FillValues(&expected, - {-1, -0.5, 0, 38.0 / 128, 102.0 / 128, 71.0 / 128}); + Tensor expected(allocator(), DT_FLOAT, TensorShape({7})); + test::FillValues( + &expected, {-1, -0.5, 0, 38.0 / 128, 102.0 / 128, 71.0 / 128, 0.5}); + test::ExpectTensorNear(expected, *GetOutput(0), 1e-5); + + // Ensure that the inputs haven't been changed. + EXPECT_EQ(inputs_[1]->scalar()(), 0.0); + EXPECT_EQ(inputs_[2]->scalar()(), 0.0); +} + +// Convert a 1D tensor with signed 8 bits and round_mode half_up. +TEST_F(QuantizeAndDequantizeTest, Convert_1D_tensor_with_int8_round_half_up) { + TF_ASSERT_OK( + NodeDefBuilder("quantize_and_dequantize_op", "QuantizeAndDequantizeV2") + .Input(FakeInput(DT_FLOAT)) + .Input(FakeInput(DT_FLOAT)) + .Input(FakeInput(DT_FLOAT)) + .Attr("signed_input", true) + .Attr("num_bits", 8) + .Attr("range_given", false) + .Attr("round_mode", "HALF_UP") + .Finalize(node_def())); + TF_ASSERT_OK(InitOp()); + AddInputFromArray(TensorShape({7}), + {-1, -0.5, 0, 0.3, 0.8, 0.555, 0.50390625}); + AddInputFromArray(TensorShape({}), {0.0}); // Min + AddInputFromArray(TensorShape({}), {0.0}); // Max + + // With int8, the tensor is quantized to {-128, -64, 0, 38, 102, 71, 65}. + // Scale is: 1/127 + // Then it is dequantized to {-1, -0.5, 0, 38.0/128, 102.0/128, 71.0/128, + // 65.0 /128} + TF_ASSERT_OK(RunOpKernel()); + Tensor expected(allocator(), DT_FLOAT, TensorShape({7})); + test::FillValues(&expected, {-1, -0.5, 0, 38.0 / 128, 102.0 / 128, + 71.0 / 128, 65.0 / 128}); test::ExpectTensorNear(expected, *GetOutput(0), 1e-5); // Ensure that the inputs haven't been changed. @@ -162,7 +196,7 @@ TEST_F(QuantizeAndDequantizeTest, Convert_1D_tensor_with_int4) { .Attr("range_given", false) .Finalize(node_def())); TF_ASSERT_OK(InitOp()); - AddInputFromArray(TensorShape({6}), {-1, -0.5, 0, 0.3, 0.8, 0.555}); + AddInputFromArray(TensorShape({6}), {-1, -0.5, 0, 0.3125, 0.8, 0.555}); AddInputFromArray(TensorShape({}), {0.0}); // Min AddInputFromArray(TensorShape({}), {0.0}); // Max @@ -178,6 +212,35 @@ TEST_F(QuantizeAndDequantizeTest, Convert_1D_tensor_with_int4) { EXPECT_EQ(inputs_[2]->scalar()(), 0.0); } +// Convert a 1D tensor with signed 4 bits and round_mode hafl_up. +TEST_F(QuantizeAndDequantizeTest, Convert_1D_tensor_with_int4_round_half_up) { + TF_ASSERT_OK( + NodeDefBuilder("quantize_and_dequantize_op", "QuantizeAndDequantizeV2") + .Input(FakeInput(DT_FLOAT)) + .Input(FakeInput(DT_FLOAT)) + .Input(FakeInput(DT_FLOAT)) + .Attr("signed_input", true) + .Attr("num_bits", 4) + .Attr("range_given", false) + .Attr("round_mode", "HALF_UP") + .Finalize(node_def())); + TF_ASSERT_OK(InitOp()); + AddInputFromArray(TensorShape({6}), {-1, -0.5, 0, 0.3125, 0.8, 0.555}); + AddInputFromArray(TensorShape({}), {0.0}); // Min + AddInputFromArray(TensorShape({}), {0.0}); // Max + + // With int4, the tensor is quantized to {-8, -4, 0, 3, 6, 4}. + // Scale is: 1/8 + TF_ASSERT_OK(RunOpKernel()); + Tensor expected(allocator(), DT_FLOAT, TensorShape({6})); + test::FillValues(&expected, {-1, -0.5, 0, 0.375, 0.75, 0.5}); + test::ExpectTensorNear(expected, *GetOutput(0), 1e-5); + + // Ensure that the inputs haven't been changed. + EXPECT_EQ(inputs_[1]->scalar()(), 0.0); + EXPECT_EQ(inputs_[2]->scalar()(), 0.0); +} + // Convert a 1D tensor with signed 4 bits. TEST_F(QuantizeAndDequantizeTest, Convert_1D_tensor_with_int4_V3) { TF_ASSERT_OK( @@ -237,6 +300,38 @@ TEST_F(QuantizeAndDequantizeTest, Convert_2D_tensor_with_int8_range_given) { test::ExpectTensorNear(expected, *GetOutput(0), 1e-5); } +// Convert a 2D tensor with signed 8 bits, given range and round_mode half_up. +TEST_F(QuantizeAndDequantizeTest, + Convert_2D_tensor_with_int8_range_given_round_half_up) { + TF_ASSERT_OK( + NodeDefBuilder("quantize_and_dequantize_op", "QuantizeAndDequantizeV2") + .Input(FakeInput(DT_FLOAT)) + .Input(FakeInput(DT_FLOAT)) + .Input(FakeInput(DT_FLOAT)) + .Attr("signed_input", true) + .Attr("num_bits", 8) + .Attr("range_given", true) + .Attr("round_mode", "HALF_UP") + .Finalize(node_def())); + TF_ASSERT_OK(InitOp()); + // Note that the last two values are saturated. + AddInputFromArray(TensorShape({2, 4}), + {-0.8, -0.5, 0, 0.3, 0.8, 0.555, -2, 33}); + AddInputFromArray(TensorShape({}), {-1.0}); // Min + AddInputFromArray(TensorShape({}), {1.0}); // Max + + // Note that the range is given as [-1, 1]. + // With int8, the tensor is quantized to {-102, -63, 0, 38, 102, 70, -128, + // 127}. + // Scale is: 1/127 + TF_ASSERT_OK(RunOpKernel()); + Tensor expected(allocator(), DT_FLOAT, TensorShape({2, 4})); + test::FillValues( + &expected, {-102.0 / 127, -63.0 / 127, 0, 38.0 / 127, 102.0 / 127, + 70.0 / 127, -128.0 / 127, 1}); + test::ExpectTensorNear(expected, *GetOutput(0), 1e-5); +} + // Convert a 2D tensor with signed 8 bits with given range. TEST_F(QuantizeAndDequantizeTest, Convert_2D_tensor_with_int8_range_given_V3) { TF_ASSERT_OK( @@ -293,6 +388,33 @@ TEST_F(QuantizeAndDequantizeTest, Convert_4D_tensor_with_uint8_range_given) { test::ExpectTensorNear(expected, *GetOutput(0), 1e-5); } +// Convert a 4D tensor with unsigned 8 bits, given range and round_mode half_up. +TEST_F(QuantizeAndDequantizeTest, + Convert_4D_tensor_with_uint8_range_given_round_half_up) { + TF_ASSERT_OK( + NodeDefBuilder("quantize_and_dequantize_op", "QuantizeAndDequantizeV2") + .Input(FakeInput(DT_FLOAT)) + .Input(FakeInput(DT_FLOAT)) + .Input(FakeInput(DT_FLOAT)) + .Attr("signed_input", false) + .Attr("num_bits", 8) + .Attr("range_given", true) + .Attr("round_mode", "HALF_UP") + .Finalize(node_def())); + TF_ASSERT_OK(InitOp()); + AddInputFromArray(TensorShape({2, 2, 1, 1}), {-0.5, 0, 0.3, 0.8}); + AddInputFromArray(TensorShape({}), {0.0}); // Min + AddInputFromArray(TensorShape({}), {1.0}); // Max + + // Note that the range is given as [0, 1]. + // With int8, the tensor is quantized to {0, 0, 77, 204} + // Scale is: 1/255 + TF_ASSERT_OK(RunOpKernel()); + Tensor expected(allocator(), DT_FLOAT, TensorShape({2, 2, 1, 1})); + test::FillValues(&expected, {0, 0, 77.0 / 255, 204.0 / 255}); + test::ExpectTensorNear(expected, *GetOutput(0), 1e-5); +} + // Convert a 4D tensor with unsigned 8 bits with given range. TEST_F(QuantizeAndDequantizeTest, Convert_4D_tensor_with_uint8_range_given_V3) { TF_ASSERT_OK( diff --git a/tensorflow/core/kernels/quantized_resize_bilinear_op_test.cc b/tensorflow/core/kernels/quantized_resize_bilinear_op_test.cc index e6133415d0..6fc4894592 100644 --- a/tensorflow/core/kernels/quantized_resize_bilinear_op_test.cc +++ b/tensorflow/core/kernels/quantized_resize_bilinear_op_test.cc @@ -273,7 +273,7 @@ void TestResizeBilinearOneDim() { << expected_val << ", " << resized_image_val; } - // Value testing with reference implemenatation + // Value testing with reference implementation CheckTensorValue(image_quantized_tensor.flat().data(), outputs.at(0).flat().data(), /*batch_size=*/1, diff --git a/tensorflow/core/kernels/ragged_gather_op.cc b/tensorflow/core/kernels/ragged_gather_op.cc index b2a342f637..903a97a960 100644 --- a/tensorflow/core/kernels/ragged_gather_op.cc +++ b/tensorflow/core/kernels/ragged_gather_op.cc @@ -236,8 +236,10 @@ class RaggedGatherOpBase : public OpKernel { values_shape.set_dim(0, num_values); TF_RETURN_IF_ERROR( context->allocate_output(values_index, values_shape, &values_out)); - int64 value_size = params_dense_values_in.NumElements() / - params_dense_values_in.dim_size(0); + const int64 num_elements = params_dense_values_in.NumElements(); + const int64 value_size = + num_elements == 0 ? 0 + : (num_elements / params_dense_values_in.dim_size(0)); CallWriteValueSlices(params_dense_values_in, value_slices, value_size, values_out); return ::tensorflow::Status::OK(); diff --git a/tensorflow/core/kernels/scan_ops_gpu.cu.cc b/tensorflow/core/kernels/scan_ops_gpu.cu.cc index ed6c6affce..ed66c02dc5 100644 --- a/tensorflow/core/kernels/scan_ops_gpu.cu.cc +++ b/tensorflow/core/kernels/scan_ops_gpu.cu.cc @@ -1,4 +1,4 @@ -/* Copyright 2016 The TensorFlow Authors. All Rights Reserved. +/* Copyright 2018 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. @@ -17,8 +17,20 @@ limitations under the License. #define EIGEN_USE_GPU +#if CUDA_VERSION >= 9000 +#define CUB_USE_COOPERATIVE_GROUPS +#endif // CUDA_VERSION >= 9000 + +#include "third_party/cub/block/block_load.cuh" +#include "third_party/cub/block/block_scan.cuh" +#include "third_party/cub/block/block_store.cuh" +#include "third_party/cub/iterator/counting_input_iterator.cuh" +#include "third_party/cub/iterator/transform_input_iterator.cuh" +#include "cuda/include/cuComplex.h" #include "tensorflow/core/framework/numeric_types.h" #include "tensorflow/core/framework/register_types.h" +#include "tensorflow/core/util/permutation_input_iterator.h" +#include "tensorflow/core/util/permutation_output_iterator.h" #include "tensorflow/core/kernels/scan_ops.h" @@ -27,6 +39,258 @@ namespace tensorflow { typedef Eigen::GpuDevice GPUDevice; typedef Eigen::Index Index; +namespace functor { + +// Map a contiguous range to the actual memory locations depending on which +// axis the scan is taking place over and whether or not reversed. +struct MapIndexToLocation { + __host__ __device__ MapIndexToLocation(int dimx, int dimy, int dimz, + bool reverse = false) + : dimx_(dimx), dimy_(dimy), dimz_(dimz), reverse_(reverse) {} + + __host__ __device__ int operator()(int id) const { + if (dimx_ == 1) { + int row = id % dimy_; + int col = id / dimy_; + + if (reverse_) return (dimy_ - row - 1) * dimz_ + col; + + return row * dimz_ + col; + } else if (dimz_ == 1) { + if (reverse_) { + int row = id / dimy_; + int col = id % dimy_; + return row * dimy_ + (dimy_ - col - 1); + } + return id; + } else { + int col = id % dimy_; + int tmp = id / dimy_; + + int row1 = id / (dimy_ * dimz_); + int col1 = tmp % dimz_; + + if (reverse_) + return row1 * dimy_ * dimz_ + (dimy_ - col - 1) * dimz_ + col1; + + return row1 * dimy_ * dimz_ + col * dimz_ + col1; + } + } + + int dimx_; + int dimy_; + int dimz_; + bool reverse_; +}; + +template +struct BlockPrefixCallbackOp { + // Running prefix + T running_total_; + Op op_; + + __device__ BlockPrefixCallbackOp(T running_total, Op op) + : running_total_(running_total), op_(op) {} + + // Callback operator to be entered by the first warp of threads in the block. + // tid 0 is responsible for returning a value for seeding the block-wide scan. + __device__ T operator()(T block_aggregate) { + T old_prefix = running_total_; + running_total_ = op_(old_prefix, block_aggregate); + return old_prefix; + } +}; + +template +struct Sum { + __host__ __device__ T operator()(const T& a, const T& b) const { + return a + b; + } +}; + +template +struct Prod { + __host__ __device__ T operator()(const T& a, const T& b) const { + return a * b; + } +}; + +template +struct IsSum { + constexpr static bool value = + (std::is_same>::value || + std::is_same>::value); +}; + +template +struct IsProd { + constexpr static bool value = + (std::is_same>::value || + std::is_same>::value); +}; + +template +struct IdentityValue { + static_assert(IsSum::value || IsProd::value, + "IdentityValue not yet defined for this type."); + + template + __host__ __device__ U operator()( + typename std::enable_if::value, U>::type t = U(0)) { + return t; + } + + template + __host__ __device__ U operator()( + typename std::enable_if::value, U>::type t = U(1)) { + return t; + } +}; + +// Each block is mapped to one sequence. A contiguous range is mapped to the +// appropriate locations in memory by the permutation iterators. This is +// ideal for 1-D and row based scans. Column scans would be better if they +// did a block load and then locally transposed. CUB's device wide scan is not +// used in the large 1D case, even though it would be more efficient, because +// it is not deterministic. +template +__global__ void scan_kernel(const T* in, T* out, int dimx, int dimy, int dimz, + bool exclusive, bool reverse, Op op) { + typedef cub::BlockLoad + BlockLoad; + typedef cub::BlockStore + BlockStore; + typedef cub::BlockScan BlockScan; + + // Allocate aliased shared memory for BlockLoad, BlockStore, and BlockScan + __shared__ union { + typename BlockLoad::TempStorage load; + typename BlockScan::TempStorage scan; + typename BlockStore::TempStorage store; + } temp_storage; + + int problem_length = dimy; + + // Initialize running total + BlockPrefixCallbackOp prefix_op(IdentityValue()(), op); + + MapIndexToLocation map_op(dimx, dimy, dimz, reverse); + int block_start = problem_length * blockIdx.x; + // Have the block iterate over segments of items + for (int block_offset = block_start; + block_offset < block_start + problem_length; + block_offset += BlockDim * ItemsPerThread) { + int valid_items = min(BlockDim * ItemsPerThread, + problem_length - (block_offset % problem_length)); + + // first construct a counting iterator that has the desired start point + typedef cub::TransformInputIterator> + MapIterType; + + cub::CountingInputIterator counting_iter(block_offset); + + // Next map the iterator to the actual locations in memory + MapIterType map_iter(counting_iter, map_op); + + PermutationInputIterator permutein_iter(in, + map_iter); + PermutationOutputIterator permuteout_iter(out, + map_iter); + + // Load a segment of consecutive items that are blocked across threads + T thread_data[ItemsPerThread]; + BlockLoad(temp_storage.load).Load(permutein_iter, thread_data, valid_items); + __syncthreads(); + + // Collectively compute the block-wide scan + if (exclusive) { + BlockScan(temp_storage.scan) + .ExclusiveScan(thread_data, thread_data, op, prefix_op); + } else { + BlockScan(temp_storage.scan) + .InclusiveScan(thread_data, thread_data, op, prefix_op); + } + __syncthreads(); + + // Store scanned items to output segment + BlockStore(temp_storage.store) + .Store(permuteout_iter, thread_data, valid_items); + __syncthreads(); + } +} + +template +void LaunchScan(const GPUDevice& d, typename TTypes::ConstTensor in, + typename TTypes::Tensor out, Op op, const bool reverse, + const bool exclusive) { + const int items_per_thread = 4; + + int dimx = in.dimension(0); + int dimy = in.dimension(1); + int dimz = in.dimension(2); + int num_blocks = dimx * dimz; + + int ideal_block_size = dimy / items_per_thread; + + // There seems to be a bug when the type is not float and block_size 1024. + // Launch on the smallest power of 2 block size that we can. + if (ideal_block_size >= 1024 && std::is_same::value) { + const int block_size = 1024; + scan_kernel + <<>>( + in.data(), out.data(), dimx, dimy, dimz, exclusive, reverse, op); + } else if (ideal_block_size >= 512) { + const int block_size = 512; + scan_kernel + <<>>( + in.data(), out.data(), dimx, dimy, dimz, exclusive, reverse, op); + } else if (ideal_block_size >= 256) { + const int block_size = 256; + scan_kernel + <<>>( + in.data(), out.data(), dimx, dimy, dimz, exclusive, reverse, op); + } else if (ideal_block_size >= 128) { + const int block_size = 128; + scan_kernel + <<>>( + in.data(), out.data(), dimx, dimy, dimz, exclusive, reverse, op); + } else if (ideal_block_size >= 64) { + const int block_size = 64; + scan_kernel + <<>>( + in.data(), out.data(), dimx, dimy, dimz, exclusive, reverse, op); + } else { + const int block_size = 32; + scan_kernel + <<>>( + in.data(), out.data(), dimx, dimy, dimz, exclusive, reverse, op); + } +} + +template +struct Scan, T> { + void operator()(const GPUDevice& d, typename TTypes::ConstTensor in, + typename TTypes::Tensor out, + const Eigen::internal::SumReducer& reducer, + const bool reverse, const bool exclusive) { + LaunchScan>(d, in, out, Sum(), reverse, exclusive); + } +}; + +template +struct Scan, T> { + void operator()(const GPUDevice& d, typename TTypes::ConstTensor in, + typename TTypes::Tensor out, + const Eigen::internal::ProdReducer& reducer, + const bool reverse, const bool exclusive) { + LaunchScan>(d, in, out, Prod(), reverse, exclusive); + } +}; + +} // namespace functor + #define DEFINE(REDUCER, T) template struct functor::Scan; #define DEFINE_FOR_ALL_REDUCERS(T) \ diff --git a/tensorflow/core/kernels/scan_ops_test.cc b/tensorflow/core/kernels/scan_ops_test.cc new file mode 100644 index 0000000000..588b606a99 --- /dev/null +++ b/tensorflow/core/kernels/scan_ops_test.cc @@ -0,0 +1,146 @@ +/* Copyright 2018 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/common_runtime/kernel_benchmark_testlib.h" +#include "tensorflow/core/framework/tensor.h" +#include "tensorflow/core/framework/types.h" +#include "tensorflow/core/platform/test.h" +#include "tensorflow/core/platform/test_benchmark.h" + +namespace tensorflow { + +template +static Graph* LargeOneDCumsum(int num_x, bool reverse = false) { + auto* g = new Graph(OpRegistry::Global()); + Tensor data(DataTypeToEnum::value, TensorShape({num_x})); + data.flat().setRandom(); + Tensor axes(DT_INT32, TensorShape({})); + axes.flat()(0) = 0; + test::graph::Cumsum(g, test::graph::Constant(g, data), + test::graph::Constant(g, axes)); + return g; +} + +static Graph* ColCumsum(int num_x, int num_y, bool reverse = false) { + auto* g = new Graph(OpRegistry::Global()); + Tensor data(DT_FLOAT, TensorShape({num_x, num_y})); + data.flat().setRandom(); + Tensor axes(DT_INT32, TensorShape({})); + axes.flat()(0) = 0; + test::graph::Cumsum(g, test::graph::Constant(g, data), + test::graph::Constant(g, axes)); + return g; +} + +static Graph* RowCumsum(int num_x, int num_y, bool reverse = false) { + auto* g = new Graph(OpRegistry::Global()); + Tensor data(DT_FLOAT, TensorShape({num_x, num_y})); + data.flat().setRandom(); + Tensor axes(DT_INT32, TensorShape({})); + axes.flat()(0) = 1; + test::graph::Cumsum(g, test::graph::Constant(g, data), + test::graph::Constant(g, axes)); + return g; +} + +static Graph* ThreeDYCumsum(int num_y, int num_z, bool reverse = false) { + auto* g = new Graph(OpRegistry::Global()); + Tensor data(DT_FLOAT, TensorShape({32, num_y, num_z})); + data.flat().setRandom(); + Tensor axes(DT_INT32, TensorShape({})); + axes.flat()(0) = 1; + test::graph::Cumsum(g, test::graph::Constant(g, data), + test::graph::Constant(g, axes)); + return g; +} + +template +static void LargeOneDimensional(int iters, const string& device, int num_x, + bool reverse = false) { + testing::ItemsProcessed(static_cast(iters) * num_x); + testing::BytesProcessed(static_cast(iters) * num_x * sizeof(T)); + test::Benchmark(device, LargeOneDCumsum(num_x, reverse)).Run(iters); +} + +static void DoRowCumsum(int iters, const string& device, int num_x, int num_y, + bool reverse = false) { + testing::ItemsProcessed(static_cast(iters) * num_x * num_y); + testing::BytesProcessed(static_cast(iters) * num_x * num_y * + sizeof(float)); + test::Benchmark(device, RowCumsum(num_x, num_y, reverse)).Run(iters); +} + +static void DoColCumsum(int iters, const string& device, int num_x, int num_y, + bool reverse = false) { + testing::ItemsProcessed(static_cast(iters) * num_x * num_y); + testing::BytesProcessed(static_cast(iters) * num_x * num_y * + sizeof(float)); + test::Benchmark(device, ColCumsum(num_x, num_y, reverse)).Run(iters); +} + +static void Do3DYCumsum(int iters, const string& device, int num_x, int num_y, + bool reverse = false) { + testing::ItemsProcessed(static_cast(iters) * num_x * num_y); + testing::BytesProcessed(static_cast(iters) * num_x * num_y * + sizeof(float)); + test::Benchmark(device, ThreeDYCumsum(num_x, num_y, reverse)).Run(iters); +} + +static void BM_OneDCumsumGPU(int iters, int num_x) { + LargeOneDimensional(iters, "gpu", num_x); +} +BENCHMARK(BM_OneDCumsumGPU)->Range(1, 1 << 21); + +static void BM_OneDCumsumGPUHalf(int iters, int num_x) { + LargeOneDimensional(iters, "gpu", num_x); +} +BENCHMARK(BM_OneDCumsumGPUHalf)->Range(1, 1 << 21); + +static void BM_Sum2DRowCumsumGPU(int iters, int num_x, int num_y) { + DoRowCumsum(iters, "gpu", num_x, num_y); +} +BENCHMARK(BM_Sum2DRowCumsumGPU)->RangePair(1, 8192, 1, 8192); + +static void BM_Sum2DColumnCumsumGPU(int iters, int num_x, int num_y) { + DoColCumsum(iters, "gpu", num_x, num_y); +} +BENCHMARK(BM_Sum2DColumnCumsumGPU)->RangePair(1, 8192, 1, 8192); + +static void BM_Sum3DYCumsumGPU(int iters, int num_x, int num_y) { + Do3DYCumsum(iters, "gpu", num_x, num_y); +} +BENCHMARK(BM_Sum3DYCumsumGPU)->RangePair(64, 4096, 64, 4096); + +static void BM_OneDCumsumGPU_reverse(int iters, int num_x) { + LargeOneDimensional(iters, "gpu", num_x, true); +} +BENCHMARK(BM_OneDCumsumGPU_reverse)->Range(1, 1 << 21); + +static void BM_Sum2DRowCumsumGPU_reverse(int iters, int num_x, int num_y) { + DoRowCumsum(iters, "gpu", num_x, num_y, true); +} +BENCHMARK(BM_Sum2DRowCumsumGPU_reverse)->RangePair(1, 8192, 1, 8192); + +static void BM_Sum2DColumnCumsumGPU_reverse(int iters, int num_x, int num_y) { + DoColCumsum(iters, "gpu", num_x, num_y, true); +} +BENCHMARK(BM_Sum2DColumnCumsumGPU_reverse)->RangePair(1, 8192, 1, 8192); + +static void BM_Sum3DYCumsumGPU_reverse(int iters, int num_x, int num_y) { + Do3DYCumsum(iters, "gpu", num_x, num_y, true); +} +BENCHMARK(BM_Sum3DYCumsumGPU_reverse)->RangePair(32, 2048, 32, 2048); + +} // end namespace tensorflow diff --git a/tensorflow/core/kernels/scatter_nd_op.cc b/tensorflow/core/kernels/scatter_nd_op.cc index fd54c6d6d7..63bb793fdc 100644 --- a/tensorflow/core/kernels/scatter_nd_op.cc +++ b/tensorflow/core/kernels/scatter_nd_op.cc @@ -29,6 +29,7 @@ limitations under the License. #include "tensorflow/core/kernels/bounds_check.h" #include "tensorflow/core/kernels/dense_update_functor.h" #include "tensorflow/core/kernels/fill_functor.h" +#include "tensorflow/core/kernels/inplace_ops_functor.h" #include "tensorflow/core/kernels/training_op_helpers.h" #include "tensorflow/core/kernels/variable_ops.h" #include "tensorflow/core/lib/strings/str_util.h" @@ -121,6 +122,90 @@ class ScatterNdOp : public OpKernel { } }; +template +class TensorScatterOp : public OpKernel { + public: + explicit TensorScatterOp(OpKernelConstruction* c) : OpKernel(c) { + const DataType dt = DataTypeToEnum::v(); + const DataType index_t = DataTypeToEnum::v(); + OP_REQUIRES_OK(c, c->MatchSignature({dt, index_t, dt}, {dt})); + } + + void Compute(OpKernelContext* c) override { + const Tensor& input = c->input(0); + const Tensor& indices = c->input(1); + const Tensor& updates = c->input(2); + + OP_REQUIRES(c, indices.shape().dims() >= 1, + errors::InvalidArgument( + "Indices shape must have rank at least one. Found:", + indices.shape().DebugString())); + OP_REQUIRES(c, updates.shape().dims() >= 1, + errors::InvalidArgument( + "Updates shape must have rank at least one. Found:", + updates.shape().DebugString())); + + TensorShape shape = input.shape(); + + OP_REQUIRES( + c, + (shape.num_elements() > 0 || (indices.shape().num_elements() == 0 && + updates.shape().num_elements() == 0)), + errors::InvalidArgument( + "Indices and updates specified for empty output shape")); + + const int64 outer_dims = indices.shape().dims() - 1; + + for (int i = 0; i < outer_dims; ++i) { + OP_REQUIRES(c, indices.shape().dim_size(i) == updates.shape().dim_size(i), + errors::InvalidArgument( + "Outer dimensions of indices and update must match. " + "Indices shape: ", + indices.shape().DebugString(), + ", updates shape:", updates.shape().DebugString())); + } + + const int64 ix = indices.shape().dim_size(outer_dims); + OP_REQUIRES( + c, updates.shape().dims() - outer_dims == shape.dims() - ix, + errors::InvalidArgument("Inner dimensions of output shape must match " + "inner dimensions of updates shape. Output: ", + shape.DebugString(), + " updates: ", updates.shape().DebugString())); + for (int i = 0; i + outer_dims < updates.shape().dims(); ++i) { + OP_REQUIRES( + c, updates.shape().dim_size(i + outer_dims) == shape.dim_size(ix + i), + errors::InvalidArgument( + "The inner ", shape.dims() - ix, + " dimensions of output.shape=", shape.DebugString(), + " must match the inner ", updates.shape().dims() - outer_dims, + " dimensions of updates.shape=", updates.shape().DebugString())); + } + + std::unique_ptr forwarded_input = c->forward_input( + 2, 0, input.dtype(), shape, DEVICE_MEMORY, AllocatorAttributes()); + + if (forwarded_input == nullptr) { + // We were not able to forward the input, so we deep copy the tensor and + // set the output. + Tensor* out; + OP_REQUIRES_OK(c, c->allocate_output(0, input.shape(), &out)); + + OP_REQUIRES_OK(c, tensorflow::functor::DoCopy(c->eigen_device(), + input, out)); + OP_REQUIRES_OK(c, + functor::DoScatterNd( + c, indices, updates, shape, out, false /*allocate*/)); + } else { + // Output forwarded, so simply perform the scatter. + OP_REQUIRES_OK(c, functor::DoScatterNd( + c, indices, updates, shape, forwarded_input.get(), + false /*allocate*/)); + } + } +}; + template class ScatterNdUpdateOp : public OpKernel { @@ -282,6 +367,56 @@ TF_CALL_bool(REGISTER_SCATTER_ND_ADD_SUB_CPU); TF_CALL_bool(REGISTER_SCATTER_ND_UPDATE_CPU); TF_CALL_bool(REGISTER_SCATTER_ND_CPU); +#define REGISTER_SCATTER_ND_TENSOR_UPDATE_TYPE_INDEX_TYPE(type, index_type, \ + dev) \ + REGISTER_KERNEL_BUILDER(Name("TensorScatterUpdate") \ + .Device(DEVICE_##dev) \ + .TypeConstraint("T") \ + .TypeConstraint("Tindices"), \ + TensorScatterOp) + +#define REGISTER_SCATTER_ND_TENSOR_ADD_TYPE_INDEX_TYPE(type, index_type, dev) \ + REGISTER_KERNEL_BUILDER(Name("TensorScatterAdd") \ + .Device(DEVICE_##dev) \ + .TypeConstraint("T") \ + .TypeConstraint("Tindices"), \ + TensorScatterOp) + +#define REGISTER_SCATTER_ND_TENSOR_SUB_TYPE_INDEX_TYPE(type, index_type, dev) \ + REGISTER_KERNEL_BUILDER(Name("TensorScatterSub") \ + .Device(DEVICE_##dev) \ + .TypeConstraint("T") \ + .TypeConstraint("Tindices"), \ + TensorScatterOp) + +#define REGISTER_SCATTER_ND_TENSOR_UPDATE_CPU(type) \ + REGISTER_SCATTER_ND_TENSOR_UPDATE_TYPE_INDEX_TYPE(type, int32, CPU); \ + REGISTER_SCATTER_ND_TENSOR_UPDATE_TYPE_INDEX_TYPE(type, int64, CPU); + +#define REGISTER_SCATTER_ND_TENSOR_ADD_CPU(type) \ + REGISTER_SCATTER_ND_TENSOR_ADD_TYPE_INDEX_TYPE(type, int32, CPU); \ + REGISTER_SCATTER_ND_TENSOR_ADD_TYPE_INDEX_TYPE(type, int64, CPU); + +#define REGISTER_SCATTER_ND_TENSOR_SUB_CPU(type) \ + REGISTER_SCATTER_ND_TENSOR_SUB_TYPE_INDEX_TYPE(type, int32, CPU); \ + REGISTER_SCATTER_ND_TENSOR_SUB_TYPE_INDEX_TYPE(type, int64, CPU); + +#define REGISTER_SCATTER_ND_TENSOR_CPU(type) \ + REGISTER_SCATTER_ND_TENSOR_UPDATE_CPU(type); \ + REGISTER_SCATTER_ND_TENSOR_ADD_CPU(type); \ + REGISTER_SCATTER_ND_TENSOR_SUB_CPU(type); + +// Register TensorScatterUpdate/Add/Sub for all number types. +TF_CALL_NUMBER_TYPES(REGISTER_SCATTER_ND_TENSOR_CPU); +// Register only TensorScatterUpdate for string/bool types as well. +TF_CALL_string(REGISTER_SCATTER_ND_TENSOR_UPDATE_CPU); +TF_CALL_bool(REGISTER_SCATTER_ND_TENSOR_UPDATE_CPU); + +#undef REGISTER_SCATTER_ND_TENSOR_CPU + // Registers GPU kernels. #if GOOGLE_CUDA @@ -319,6 +454,25 @@ TF_CALL_GPU_NUMBER_TYPES_NO_HALF(REGISTER_SCATTER_ND_UPDATE_SYCL); #undef REGISTER_SCATTER_ND_UPDATE_SYCL #endif // TENSORFLOW_USE_SYCL +#define REGISTER_SCATTER_ND_TENSOR_UPDATE_GPU(type) \ + REGISTER_SCATTER_ND_TENSOR_UPDATE_TYPE_INDEX_TYPE(type, int32, GPU); \ + REGISTER_SCATTER_ND_TENSOR_UPDATE_TYPE_INDEX_TYPE(type, int64, GPU); + +#define REGISTER_SCATTER_ND_TENSOR_ADD_GPU(type) \ + REGISTER_SCATTER_ND_TENSOR_ADD_TYPE_INDEX_TYPE(type, int32, GPU); \ + REGISTER_SCATTER_ND_TENSOR_ADD_TYPE_INDEX_TYPE(type, int64, GPU); + +#define REGISTER_SCATTER_ND_TENSOR_SUB_GPU(type) \ + REGISTER_SCATTER_ND_TENSOR_SUB_TYPE_INDEX_TYPE(type, int32, GPU); \ + REGISTER_SCATTER_ND_TENSOR_SUB_TYPE_INDEX_TYPE(type, int64, GPU); + +#define REGISTER_SCATTER_ND_TENSOR_GPU(type) \ + REGISTER_SCATTER_ND_TENSOR_ADD_GPU(type); \ + REGISTER_SCATTER_ND_TENSOR_UPDATE_GPU(type); \ + REGISTER_SCATTER_ND_TENSOR_SUB_GPU(type); + +TF_CALL_GPU_NUMBER_TYPES_NO_HALF(REGISTER_SCATTER_ND_TENSOR_GPU); + #undef REGISTER_SCATTER_ND_ADD #undef REGISTER_SCATTER_ND_ADD_SUB #undef REGISTER_SCATTER_ND_ADD_SUB_CPU @@ -328,6 +482,16 @@ TF_CALL_GPU_NUMBER_TYPES_NO_HALF(REGISTER_SCATTER_ND_UPDATE_SYCL); #undef REGISTER_SCATTER_ND_UPDATE_GPU #undef REGISTER_SCATTER_ND_KERNEL #undef REGISTER_SCATTER_ND_KERNEL_INDEX +#undef REGISTER_SCATTER_ND_TENSOR_TYPE_INDEX_TYPE +#undef REGISTER_SCATTER_ND_TENSOR_CPU +#undef REGISTER_SCATTER_ND_TENSOR_GPU +#undef REGISTER_SCATTER_ND_TENSOR_UPDATE_TYPE_INDEX_TYPE +#undef REGISTER_SCATTER_ND_TENSOR_ADD_TYPE_INDEX_TYPE +#undef REGISTER_SCATTER_ND_TENSOR_SUB_TYPE_INDEX_TYPE +#undef REGISTER_SCATTER_ND_TENSOR_UPDATE_GPU +#undef REGISTER_SCATTER_ND_TENSOR_ADD_GPU +#undef REGISTER_SCATTER_ND_TENSOR_SUB_GPU +#undef REGISTER_SCATTER_ND_TENSOR_GPU #endif // GOOGLE_CUDA diff --git a/tensorflow/core/kernels/stage_op.cc b/tensorflow/core/kernels/stage_op.cc index 73a02a34cf..c91bdc43cf 100644 --- a/tensorflow/core/kernels/stage_op.cc +++ b/tensorflow/core/kernels/stage_op.cc @@ -151,7 +151,7 @@ class Buffer : public ResourceBase { } // Are there a limit number of elements or a memory limit - // configued on this buffer? + // configured on this buffer? bool IsBounded() const { return capacity_ > 0 || memory_limit_ > 0; } bool IsCapacityFull() const { return buf_.size() >= capacity_; } diff --git a/tensorflow/core/kernels/tensor_array_ops.cc b/tensorflow/core/kernels/tensor_array_ops.cc index a97a71b344..aa85f546a8 100644 --- a/tensorflow/core/kernels/tensor_array_ops.cc +++ b/tensorflow/core/kernels/tensor_array_ops.cc @@ -352,9 +352,9 @@ class TensorArrayGradOp : public TensorArrayCreationOp { } const auto key = strings::StrCat(output_handle(0), output_handle(1)); - auto creator = [this, key, tensor_array, array_size, marked_size, - element_shape, shape_to_prepend, tensor_array_output_handle, - output_handle](TensorArray** ret) -> Status { + auto creator = [key, tensor_array, array_size, marked_size, element_shape, + shape_to_prepend, + tensor_array_output_handle](TensorArray** ret) -> Status { *ret = new TensorArray( key, tensor_array->ElemType(), *tensor_array_output_handle, array_size, element_shape, tensor_array->HasIdenticalElementShapes(), diff --git a/tensorflow/core/kernels/tensor_forest/BUILD b/tensorflow/core/kernels/tensor_forest/BUILD new file mode 100644 index 0000000000..df035506f7 --- /dev/null +++ b/tensorflow/core/kernels/tensor_forest/BUILD @@ -0,0 +1,53 @@ +# Description: +# OpKernels for tensor forest ops. + +package( + default_visibility = ["//tensorflow:internal"], +) + +licenses(["notice"]) # Apache 2.0 + +load("//tensorflow:tensorflow.bzl", "tf_kernel_library") + +cc_library( + name = "resources", + srcs = ["resources.cc"], + hdrs = ["resources.h"], + deps = [ + "//tensorflow/core:framework", + "//tensorflow/core:lib", + "//tensorflow/core/kernels/boosted_trees:boosted_trees_proto_cc", + ], +) + +tf_kernel_library( + name = "resource_ops", + srcs = ["resource_ops.cc"], + deps = [ + ":resources", + "//tensorflow/core:framework", + "//tensorflow/core:lib", + "//tensorflow/core:tensor_forest_ops_op_lib", + "//tensorflow/core/kernels/boosted_trees:boosted_trees_proto_cc", + ], +) + +tf_kernel_library( + name = "prediction_ops", + srcs = ["prediction_ops.cc"], + deps = [ + ":resources", + "//tensorflow/core:framework", + "//tensorflow/core:lib", + "//tensorflow/core:tensor_forest_ops_op_lib", + "//tensorflow/core/kernels/boosted_trees:boosted_trees_proto_cc", + ], +) + +tf_kernel_library( + name = "tensor_forest_ops", + deps = [ + ":prediction_ops", + ":resource_ops", + ], +) diff --git a/tensorflow/core/kernels/tensor_forest/prediction_ops.cc b/tensorflow/core/kernels/tensor_forest/prediction_ops.cc new file mode 100644 index 0000000000..877dfbc020 --- /dev/null +++ b/tensorflow/core/kernels/tensor_forest/prediction_ops.cc @@ -0,0 +1,93 @@ +/* Copyright 2018 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/framework/op_kernel.h" +#include "tensorflow/core/framework/resource_mgr.h" +#include "tensorflow/core/framework/tensor.h" +#include "tensorflow/core/framework/tensor_shape.h" +#include "tensorflow/core/framework/tensor_types.h" +#include "tensorflow/core/kernels/tensor_forest/resources.h" +#include "tensorflow/core/platform/thread_annotations.h" +#include "tensorflow/core/util/work_sharder.h" + +namespace tensorflow { + +class TensorForestTreePredictOp : public OpKernel { + public: + explicit TensorForestTreePredictOp(OpKernelConstruction* context) + : OpKernel(context) { + OP_REQUIRES_OK(context, + context->GetAttr("logits_dimension", &logits_dimension_)); + } + + void Compute(OpKernelContext* context) override { + TensorForestTreeResource* decision_tree_resource; + OP_REQUIRES_OK(context, LookupResource(context, HandleFromInput(context, 0), + &decision_tree_resource)); + mutex_lock l(*decision_tree_resource->get_mutex()); + core::ScopedUnref unref_me(decision_tree_resource); + + const Tensor* dense_features_t = nullptr; + OP_REQUIRES_OK(context, + context->input("dense_features", &dense_features_t)); + + auto dense_features = dense_features_t->matrix(); + const int32 batch_size = dense_features_t->dim_size(0); + + Tensor* output_predictions = nullptr; + OP_REQUIRES_OK(context, + context->allocate_output(0, {batch_size, logits_dimension_}, + &output_predictions)); + auto out = output_predictions->matrix(); + + if (decision_tree_resource->get_size() <= 0) { + out.setZero(); + return; + } + auto* worker_threads = context->device()->tensorflow_cpu_worker_threads(); + const int32 num_threads = worker_threads->num_threads; + + // TODO(yupbank): This was from contrib version. + // This cost would probably depend on the depth of the tree we have. + // We will need to run it on a number of trees of diff depth + // and see the num of cpu cycles + const int64 cost_per_traverse = 500; + auto traverse = [this, &out, &dense_features, decision_tree_resource, + batch_size](int64 start, int64 end) { + CHECK_LE(start, end) << "Start exceeding End"; + CHECK_LE(end, batch_size) << "End exceeding batch size"; + for (int example_id = start; example_id < end; ++example_id) { + const int32 leaf_id = + decision_tree_resource->TraverseTree(example_id, &dense_features); + set_output_value(example_id, leaf_id, decision_tree_resource, &out); + }; + }; + Shard(num_threads, worker_threads->workers, batch_size, cost_per_traverse, + traverse); + }; + + void set_output_value(const int32 example_id, const int32 leaf_id, + const TensorForestTreeResource* decision_tree_resource, + TTypes::Matrix* out) const { + for (int j = 0; j < logits_dimension_; ++j) { + const float logit = decision_tree_resource->get_prediction(leaf_id, j); + (*out)(example_id, j) = logit; + } + }; + + private: + int32 logits_dimension_; +}; + +REGISTER_KERNEL_BUILDER(Name("TensorForestTreePredict").Device(DEVICE_CPU), + TensorForestTreePredictOp); + +} // namespace tensorflow diff --git a/tensorflow/core/kernels/tensor_forest/resource_ops.cc b/tensorflow/core/kernels/tensor_forest/resource_ops.cc new file mode 100644 index 0000000000..aba97ea990 --- /dev/null +++ b/tensorflow/core/kernels/tensor_forest/resource_ops.cc @@ -0,0 +1,135 @@ +/* Copyright 2018 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/framework/op_kernel.h" +#include "tensorflow/core/framework/resource_mgr.h" +#include "tensorflow/core/framework/tensor.h" +#include "tensorflow/core/framework/tensor_shape.h" +#include "tensorflow/core/framework/tensor_types.h" +#include "tensorflow/core/kernels/tensor_forest/resources.h" + +namespace tensorflow { + +class TensorForestCreateTreeVariableOp : public OpKernel { + public: + explicit TensorForestCreateTreeVariableOp(OpKernelConstruction* context) + : OpKernel(context){}; + + void Compute(OpKernelContext* context) override { + const Tensor* tree_config_t; + OP_REQUIRES_OK(context, context->input("tree_config", &tree_config_t)); + + auto* const result = new TensorForestTreeResource(); + + if (!result->InitFromSerialized(tree_config_t->scalar()())) { + result->Unref(); + OP_REQUIRES(context, false, + errors::InvalidArgument("Unable to parse tree config.")); + } + + // Only create one, if one does not exist already. Report status for all + // other exceptions. + auto status = CreateResource(context, HandleFromInput(context, 0), result); + if (!status.ok() && status.code() != tensorflow::error::ALREADY_EXISTS) { + OP_REQUIRES(context, false, status); + } + } +}; + +// Op for serializing a model. +class TensorForestTreeSerializeOp : public OpKernel { + public: + explicit TensorForestTreeSerializeOp(OpKernelConstruction* context) + : OpKernel(context) {} + + void Compute(OpKernelContext* context) override { + TensorForestTreeResource* decision_tree_resource; + OP_REQUIRES_OK(context, LookupResource(context, HandleFromInput(context, 0), + &decision_tree_resource)); + mutex_lock l(*decision_tree_resource->get_mutex()); + core::ScopedUnref unref_me(decision_tree_resource); + Tensor* output_config_t = nullptr; + OP_REQUIRES_OK( + context, context->allocate_output(0, TensorShape(), &output_config_t)); + output_config_t->scalar()() = + decision_tree_resource->decision_tree().SerializeAsString(); + } +}; + +// Op for deserializing a tree variable from a checkpoint. +class TensorForestTreeDeserializeOp : public OpKernel { + public: + explicit TensorForestTreeDeserializeOp(OpKernelConstruction* context) + : OpKernel(context) {} + void Compute(OpKernelContext* context) override { + TensorForestTreeResource* decision_tree_resource; + OP_REQUIRES_OK(context, LookupResource(context, HandleFromInput(context, 0), + &decision_tree_resource)); + + mutex_lock l(*decision_tree_resource->get_mutex()); + core::ScopedUnref unref_me(decision_tree_resource); + + const Tensor* tree_config_t; + OP_REQUIRES_OK(context, context->input("tree_config", &tree_config_t)); + + // Deallocate all the previous objects on the resource. + decision_tree_resource->Reset(); + + if (!decision_tree_resource->InitFromSerialized( + tree_config_t->scalar()())) { + OP_REQUIRES(context, false, + errors::InvalidArgument("Unable to parse tree config.")); + } + } +}; + +// Op for getting tree size. +class TensorForestTreeSizeOp : public OpKernel { + public: + explicit TensorForestTreeSizeOp(OpKernelConstruction* context) + : OpKernel(context) {} + + void Compute(OpKernelContext* context) override { + TensorForestTreeResource* decision_tree_resource; + OP_REQUIRES_OK(context, LookupResource(context, HandleFromInput(context, 0), + &decision_tree_resource)); + mutex_lock l(*decision_tree_resource->get_mutex()); + core::ScopedUnref unref_me(decision_tree_resource); + Tensor* output_t = nullptr; + OP_REQUIRES_OK(context, + context->allocate_output(0, TensorShape(), &output_t)); + output_t->scalar()() = decision_tree_resource->get_size(); + } +}; + +REGISTER_RESOURCE_HANDLE_KERNEL(TensorForestTreeResource); + +REGISTER_KERNEL_BUILDER( + Name("TensorForestTreeIsInitializedOp").Device(DEVICE_CPU), + IsResourceInitialized); + +REGISTER_KERNEL_BUILDER( + Name("TensorForestCreateTreeVariable").Device(DEVICE_CPU), + TensorForestCreateTreeVariableOp); + +REGISTER_KERNEL_BUILDER(Name("TensorForestTreeSerialize").Device(DEVICE_CPU), + TensorForestTreeSerializeOp); + +REGISTER_KERNEL_BUILDER(Name("TensorForestTreeDeserialize").Device(DEVICE_CPU), + TensorForestTreeDeserializeOp); + +REGISTER_KERNEL_BUILDER(Name("TensorForestTreeSize").Device(DEVICE_CPU), + TensorForestTreeSizeOp); + +} // namespace tensorflow diff --git a/tensorflow/core/kernels/tensor_forest/resources.cc b/tensorflow/core/kernels/tensor_forest/resources.cc new file mode 100644 index 0000000000..9f8ceb9620 --- /dev/null +++ b/tensorflow/core/kernels/tensor_forest/resources.cc @@ -0,0 +1,59 @@ +/* Copyright 2018 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/tensor_forest/resources.h" +#include "tensorflow/core/kernels/boosted_trees/boosted_trees.pb.h" +#include "tensorflow/core/platform/protobuf.h" + +namespace tensorflow { + +const float TensorForestTreeResource::get_prediction( + const int32 id, const int32 dimension_id) const { + return decision_tree_->nodes(id).leaf().vector().value(dimension_id); +}; + +const int32 TensorForestTreeResource::TraverseTree( + const int32 example_id, + const TTypes::ConstMatrix* dense_data) const { + using boosted_trees::Node; + using boosted_trees::Tree; + int32 current_id = 0; + while (true) { + const Node& current = decision_tree_->nodes(current_id); + if (current.has_leaf()) { + return current_id; + }; + DCHECK_EQ(current.node_case(), Node::kDenseSplit); + const auto& split = current.dense_split(); + + if ((*dense_data)(example_id, split.feature_id()) <= split.threshold()) { + current_id = split.left_id(); + } else { + current_id = split.right_id(); + } + } +}; + +bool TensorForestTreeResource::InitFromSerialized(const string& serialized) { + return ParseProtoUnlimited(decision_tree_, serialized); +} + +void TensorForestTreeResource::Reset() { + arena_.Reset(); + CHECK_EQ(0, arena_.SpaceAllocated()); + decision_tree_ = protobuf::Arena::CreateMessage(&arena_); +} + +} // namespace tensorflow diff --git a/tensorflow/core/kernels/tensor_forest/resources.h b/tensorflow/core/kernels/tensor_forest/resources.h new file mode 100644 index 0000000000..34f61b2c0c --- /dev/null +++ b/tensorflow/core/kernels/tensor_forest/resources.h @@ -0,0 +1,63 @@ +/* Copyright 2018 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_TENSOR_FOREST_RESOURCES_H_ +#define TENSORFLOW_CORE_KERNELS_TENSOR_FOREST_RESOURCES_H_ + +#include "tensorflow/core/framework/resource_mgr.h" +#include "tensorflow/core/framework/tensor.h" +#include "tensorflow/core/kernels/boosted_trees/boosted_trees.pb.h" +#include "tensorflow/core/lib/strings/strcat.h" +#include "tensorflow/core/platform/mutex.h" +#include "tensorflow/core/platform/protobuf.h" + +namespace tensorflow { + +// Keep a tree ensemble in memory for efficient evaluation and mutation. +class TensorForestTreeResource : public ResourceBase { + public: + TensorForestTreeResource() + : decision_tree_( + protobuf::Arena::CreateMessage(&arena_)){}; + + string DebugString() override { + return strings::StrCat("TensorForestTree[size=", get_size(), "]"); + } + + mutex* get_mutex() { return &mu_; } + + bool InitFromSerialized(const string& serialized); + + // Resets the resource and frees the proto. + // Caller needs to hold the mutex lock while calling this. + void Reset(); + + const int32 get_size() const { return decision_tree_->nodes_size(); } + + const boosted_trees::Tree& decision_tree() const { return *decision_tree_; } + + const float get_prediction(const int32 id, const int32 dimension_id) const; + + const int32 TraverseTree(const int32 example_id, + const TTypes::ConstMatrix* dense_data) const; + + protected: + mutex mu_; + protobuf::Arena arena_; + boosted_trees::Tree* decision_tree_; +}; + +} // namespace tensorflow +#endif // TENSORFLOW_CORE_KERNELS_TENSOR_FOREST_RESOURCES_H_ diff --git a/tensorflow/core/kernels/training_ops.cc b/tensorflow/core/kernels/training_ops.cc index acf162deec..6504ad1b09 100644 --- a/tensorflow/core/kernels/training_ops.cc +++ b/tensorflow/core/kernels/training_ops.cc @@ -283,6 +283,22 @@ struct ApplyMomentum { } }; +template +struct ApplyKerasMomentum { + void operator()(const CPUDevice& d, typename TTypes::Flat var, + typename TTypes::Flat accum, + typename TTypes::ConstScalar lr, + typename TTypes::ConstFlat grad, + typename TTypes::ConstScalar momentum, bool use_nesterov) { + accum.device(d) = accum * momentum() - grad * lr(); + if (use_nesterov) { + var.device(d) += (accum * momentum() - grad * lr()); + } else { + var.device(d) += accum; + } + } +}; + template struct ApplyAdamNonCuda { void operator()(const Device& d, typename TTypes::Flat var, @@ -331,6 +347,28 @@ struct ApplyAdamSYCL { template struct ApplyAdam : ApplyAdamNonCuda {}; +template +struct ApplyAdamWithAmsgrad { + void operator()(const CPUDevice& d, typename TTypes::Flat var, + typename TTypes::Flat m, typename TTypes::Flat v, + typename TTypes::Flat vhat, + typename TTypes::ConstScalar beta1_power, + typename TTypes::ConstScalar beta2_power, + typename TTypes::ConstScalar lr, + typename TTypes::ConstScalar beta1, + typename TTypes::ConstScalar beta2, + typename TTypes::ConstScalar epsilon, + typename TTypes::ConstFlat grad) { + const T alpha = lr() * Eigen::numext::sqrt(T(1) - beta2_power()) / + (T(1) - beta1_power()); + + m.device(d) += (grad - m) * (T(1) - beta1()); + v.device(d) += (grad.square() - v) * (T(1) - beta2()); + vhat.device(d) = vhat.cwiseMax(v); + var.device(d) -= (m * alpha) / (vhat.sqrt() + epsilon()); + } +}; + template struct ApplyAdaMaxNonCuda { void operator()(const Device& d, typename TTypes::Flat var, @@ -2525,6 +2563,217 @@ TF_CALL_double(REGISTER_CPU_KERNELS); #undef REGISTER_CPU_KERNELS #undef REGISTER_KERNELS +template +class ApplyKerasMomentumOp : public OpKernel { + public: + explicit ApplyKerasMomentumOp(OpKernelConstruction* ctx) : OpKernel(ctx) { + OP_REQUIRES_OK(ctx, ctx->GetAttr("use_locking", &use_exclusive_lock_)); + OP_REQUIRES_OK(ctx, ctx->GetAttr("use_nesterov", &use_nesterov_)); + } + + void Compute(OpKernelContext* ctx) override { + auto locks = + MaybeLockVariableInputMutexesInOrder(ctx, use_exclusive_lock_, {0, 1}); + + Tensor var; + OP_REQUIRES_OK(ctx, GetInputTensorFromVariable( + ctx, 0, use_exclusive_lock_, false, &var)); + Tensor accum; + OP_REQUIRES_OK(ctx, GetInputTensorFromVariable( + ctx, 1, use_exclusive_lock_, false, &accum)); + OP_REQUIRES( + ctx, var.IsInitialized(), + errors::FailedPrecondition( + "Attempting to use uninitialized variables: ", requested_input(0))); + OP_REQUIRES( + ctx, accum.IsInitialized(), + errors::FailedPrecondition( + "Attempting to use uninitialized variables: ", requested_input(1))); + const Tensor& lr = ctx->input(2); + OP_REQUIRES(ctx, TensorShapeUtils::IsScalar(lr.shape()), + errors::InvalidArgument("lr is not a scalar: ", + lr.shape().DebugString())); + const Tensor& grad = ctx->input(3); + OP_REQUIRES( + ctx, var.shape().IsSameSize(accum.shape()), + errors::InvalidArgument("var and accum do not have the same shape", + var.shape().DebugString(), " ", + accum.shape().DebugString())); + OP_REQUIRES( + ctx, var.shape().IsSameSize(grad.shape()), + errors::InvalidArgument("var and grad do not have the same shape", + var.shape().DebugString(), " ", + grad.shape().DebugString())); + + const Tensor& momentum = ctx->input(4); + OP_REQUIRES(ctx, TensorShapeUtils::IsScalar(momentum.shape()), + errors::InvalidArgument("momentum is not a scalar: ", + momentum.shape().DebugString())); + + const Device& device = ctx->template eigen_device(); + functor::ApplyKerasMomentum()( + device, var.flat(), accum.flat(), lr.scalar(), grad.flat(), + momentum.scalar(), use_nesterov_); + MaybeForwardRefInputToRefOutput(ctx, 0, 0); + } + + private: + bool use_exclusive_lock_; + bool use_nesterov_; +}; + +#define REGISTER_KERNELS(D, T) \ + REGISTER_KERNEL_BUILDER(Name("ResourceApplyKerasMomentum") \ + .Device(DEVICE_##D) \ + .HostMemory("var") \ + .HostMemory("accum") \ + .TypeConstraint("T"), \ + ApplyKerasMomentumOp); +#define REGISTER_CPU_KERNELS(T) REGISTER_KERNELS(CPU, T); + +TF_CALL_half(REGISTER_CPU_KERNELS); +TF_CALL_bfloat16(REGISTER_CPU_KERNELS); +TF_CALL_float(REGISTER_CPU_KERNELS); +TF_CALL_double(REGISTER_CPU_KERNELS); + +#if GOOGLE_CUDA +// Forward declarations of the functor specializations for GPU. +namespace functor { +#define DECLARE_GPU_SPEC(T) \ + template <> \ + void ApplyKerasMomentum::operator()( \ + const GPUDevice& d, typename TTypes::Flat var, \ + typename TTypes::Flat accum, typename TTypes::ConstScalar lr, \ + typename TTypes::ConstFlat grad, \ + typename TTypes::ConstScalar momentum, bool use_nesterov); \ + extern template struct ApplyKerasMomentum; +DECLARE_GPU_SPEC(Eigen::half); +DECLARE_GPU_SPEC(float); +DECLARE_GPU_SPEC(double); +#undef DECLARE_GPU_SPEC +} // namespace functor + +REGISTER_KERNELS(GPU, Eigen::half); +REGISTER_KERNELS(GPU, float); +REGISTER_KERNELS(GPU, double); +#endif +#undef REGISTER_CPU_KERNELS +#undef REGISTER_KERNELS + +// Note, this op works on cpu only. +template +class SparseApplyKerasMomentumOp : public OpKernel { + public: + explicit SparseApplyKerasMomentumOp(OpKernelConstruction* ctx) + : OpKernel(ctx) { + OP_REQUIRES_OK(ctx, ctx->GetAttr("use_locking", &use_exclusive_lock_)); + OP_REQUIRES_OK(ctx, ctx->GetAttr("use_nesterov", &use_nesterov_)); + } + + void Compute(OpKernelContext* ctx) override NO_THREAD_SAFETY_ANALYSIS { + auto locks = + MaybeLockVariableInputMutexesInOrder(ctx, use_exclusive_lock_, {0, 1}); + + Tensor var; + OP_REQUIRES_OK(ctx, GetInputTensorFromVariable( + ctx, 0, use_exclusive_lock_, true, &var)); + Tensor accum; + OP_REQUIRES_OK(ctx, GetInputTensorFromVariable( + ctx, 1, use_exclusive_lock_, true, &accum)); + OP_REQUIRES( + ctx, var.IsInitialized(), + errors::FailedPrecondition( + "Attempting to use uninitialized variables: ", requested_input(0))); + OP_REQUIRES( + ctx, accum.IsInitialized(), + errors::FailedPrecondition( + "Attempting to use uninitialized variables: ", requested_input(1))); + OP_REQUIRES( + ctx, var.shape().IsSameSize(accum.shape()), + errors::InvalidArgument("var and accum do not have the same shape", + var.shape().DebugString(), " ", + accum.shape().DebugString())); + OP_REQUIRES(ctx, TensorShapeUtils::IsVectorOrHigher(var.shape()), + errors::InvalidArgument("var must be at least 1 dimensional")); + + const Tensor& lr = ctx->input(2); + OP_REQUIRES(ctx, TensorShapeUtils::IsScalar(lr.shape()), + errors::InvalidArgument("lr is not a scalar : ", + lr.shape().DebugString())); + const Tensor& grad = ctx->input(3); + const Tensor& indices = ctx->input(4); + OP_REQUIRES(ctx, TensorShapeUtils::IsVector(indices.shape()), + errors::InvalidArgument("indices must be one-dimensional")); + + for (int d = 1; d < var.dims(); d++) { + OP_REQUIRES(ctx, var.dim_size(d) == grad.dim_size(d), + errors::InvalidArgument(strings::StrCat( + "var and grad must match in dimension ", d))); + } + const Tindex N = indices.dim_size(0); + OP_REQUIRES( + ctx, grad.dim_size(0) == N, + errors::InvalidArgument( + "grad must be the same size as indices in the first dimension.")); + + const Tensor& momentum = ctx->input(5); + OP_REQUIRES(ctx, TensorShapeUtils::IsScalar(momentum.shape()), + errors::InvalidArgument("momentum is not a scalar: ", + momentum.shape().DebugString())); + + if (N > 0) { + const Tindex first_dim_size = var.dim_size(0); + auto indices_vec = indices.vec(); + auto var_flat = var.flat_outer_dims(); + auto accum_flat = accum.flat_outer_dims(); + auto grad_flat = grad.flat_outer_dims(); + T lr_scalar = lr.scalar()(); + T momentum_scalar = momentum.scalar()(); + + for (Tindex i = 0; i < N; i++) { + const Tindex index = internal::SubtleMustCopy(indices_vec(i)); + OP_REQUIRES(ctx, FastBoundsCheck(index, first_dim_size), + errors::InvalidArgument( + strings::StrCat("Index ", index, " at offset ", i, + " in indices is out of range"))); + auto a = accum_flat.template chip<0>(index); + auto g = grad_flat.template chip<0>(i); + auto v = var_flat.template chip<0>(index); + a = a * a.constant(momentum_scalar) - g * g.constant(lr_scalar); + if (use_nesterov_) { + v += a * a.constant(momentum_scalar) - g * g.constant(lr_scalar); + } else { + v += a; + } + } + } + + MaybeForwardRefInputToRefOutput(ctx, 0, 0); + } + + private: + bool use_exclusive_lock_; + bool use_nesterov_; +}; + +#define REGISTER_KERNELS(T, Tindices) \ + REGISTER_KERNEL_BUILDER(Name("ResourceSparseApplyKerasMomentum") \ + .Device(DEVICE_CPU) \ + .TypeConstraint("T") \ + .TypeConstraint("Tindices"), \ + SparseApplyKerasMomentumOp); +#define REGISTER_CPU_KERNELS(T) \ + REGISTER_KERNELS(T, int32); \ + REGISTER_KERNELS(T, int64); + +TF_CALL_half(REGISTER_CPU_KERNELS); +TF_CALL_bfloat16(REGISTER_CPU_KERNELS); +TF_CALL_float(REGISTER_CPU_KERNELS); +TF_CALL_double(REGISTER_CPU_KERNELS); + +#undef REGISTER_CPU_KERNELS +#undef REGISTER_KERNELS + template class ApplyAdamOp : public OpKernel { public: @@ -2786,6 +3035,147 @@ REGISTER_KERNELS(GPU, double); #undef REGISTER_CPU_KERNELS #undef REGISTER_KERNELS +template +class ApplyAdamWithAmsgradOp : public OpKernel { + public: + explicit ApplyAdamWithAmsgradOp(OpKernelConstruction* ctx) : OpKernel(ctx) { + OP_REQUIRES_OK(ctx, ctx->GetAttr("use_locking", &use_exclusive_lock_)); + } + + void Compute(OpKernelContext* ctx) override { + auto locks = MaybeLockVariableInputMutexesInOrder(ctx, use_exclusive_lock_, + {0, 1, 2}); + + Tensor var; + OP_REQUIRES_OK(ctx, GetInputTensorFromVariable( + ctx, 0, use_exclusive_lock_, false, &var)); + Tensor m; + OP_REQUIRES_OK(ctx, GetInputTensorFromVariable( + ctx, 1, use_exclusive_lock_, false, &m)); + Tensor v; + OP_REQUIRES_OK(ctx, GetInputTensorFromVariable( + ctx, 2, use_exclusive_lock_, false, &v)); + Tensor vhat; + OP_REQUIRES_OK(ctx, GetInputTensorFromVariable( + ctx, 3, use_exclusive_lock_, false, &vhat)); + OP_REQUIRES( + ctx, var.IsInitialized(), + errors::FailedPrecondition( + "Attempting to use uninitialized variables: ", requested_input(0))); + OP_REQUIRES( + ctx, m.IsInitialized(), + errors::FailedPrecondition( + "Attempting to use uninitialized variables: ", requested_input(1))); + OP_REQUIRES( + ctx, v.IsInitialized(), + errors::FailedPrecondition( + "Attempting to use uninitialized variables: ", requested_input(2))); + OP_REQUIRES( + ctx, vhat.IsInitialized(), + errors::FailedPrecondition( + "Attempting to use uninitialized variables: ", requested_input(2))); + + const Tensor& beta1_power = ctx->input(4); + const Tensor& beta2_power = ctx->input(5); + const Tensor& lr = ctx->input(6); + const Tensor& beta1 = ctx->input(7); + const Tensor& beta2 = ctx->input(8); + const Tensor& epsilon = ctx->input(9); + + OP_REQUIRES(ctx, TensorShapeUtils::IsScalar(beta1_power.shape()), + errors::InvalidArgument("beta1_power is not a scalar: ", + beta1_power.shape().DebugString())); + OP_REQUIRES(ctx, TensorShapeUtils::IsScalar(beta2_power.shape()), + errors::InvalidArgument("beta2_power is not a scalar: ", + beta2_power.shape().DebugString())); + OP_REQUIRES(ctx, TensorShapeUtils::IsScalar(lr.shape()), + errors::InvalidArgument("lr is not a scalar : ", + lr.shape().DebugString())); + OP_REQUIRES(ctx, TensorShapeUtils::IsScalar(beta1.shape()), + errors::InvalidArgument("beta1 is not a scalar: ", + beta1.shape().DebugString())); + OP_REQUIRES(ctx, TensorShapeUtils::IsScalar(beta2.shape()), + errors::InvalidArgument("beta2 is not a scalar: ", + beta2.shape().DebugString())); + OP_REQUIRES(ctx, TensorShapeUtils::IsScalar(epsilon.shape()), + errors::InvalidArgument("epsilon is not a scalar: ", + epsilon.shape().DebugString())); + + const Tensor& grad = ctx->input(10); + OP_REQUIRES(ctx, var.shape().IsSameSize(m.shape()), + errors::InvalidArgument("var and m do not have the same shape", + var.shape().DebugString(), " ", + m.shape().DebugString())); + OP_REQUIRES(ctx, var.shape().IsSameSize(v.shape()), + errors::InvalidArgument("var and v do not have the same shape", + var.shape().DebugString(), " ", + v.shape().DebugString())); + OP_REQUIRES( + ctx, var.shape().IsSameSize(grad.shape()), + errors::InvalidArgument("var and grad do not have the same shape", + var.shape().DebugString(), " ", + grad.shape().DebugString())); + + const Device& device = ctx->template eigen_device(); + functor::ApplyAdamWithAmsgrad()( + device, var.flat(), m.flat(), v.flat(), vhat.flat(), + beta1_power.scalar(), beta2_power.scalar(), lr.scalar(), + beta1.scalar(), beta2.scalar(), epsilon.scalar(), + grad.flat()); + + MaybeForwardRefInputToRefOutput(ctx, 0, 0); + } + + private: + bool use_exclusive_lock_; +}; + +#define REGISTER_KERNELS(D, T) \ + REGISTER_KERNEL_BUILDER(Name("ResourceApplyAdamWithAmsgrad") \ + .HostMemory("var") \ + .HostMemory("m") \ + .HostMemory("v") \ + .HostMemory("vhat") \ + .Device(DEVICE_##D) \ + .TypeConstraint("T"), \ + ApplyAdamWithAmsgradOp); +#define REGISTER_CPU_KERNELS(T) REGISTER_KERNELS(CPU, T); + +TF_CALL_half(REGISTER_CPU_KERNELS); +TF_CALL_bfloat16(REGISTER_CPU_KERNELS); +TF_CALL_float(REGISTER_CPU_KERNELS); +TF_CALL_double(REGISTER_CPU_KERNELS); + +#if GOOGLE_CUDA +// Forward declarations of the functor specializations for GPU. +namespace functor { +#define DECLARE_GPU_SPEC(T) \ + template <> \ + void ApplyAdamWithAmsgrad::operator()( \ + const GPUDevice& d, typename TTypes::Flat var, \ + typename TTypes::Flat m, typename TTypes::Flat v, \ + typename TTypes::Flat vhat, \ + typename TTypes::ConstScalar beta1_power, \ + typename TTypes::ConstScalar beta2_power, \ + typename TTypes::ConstScalar lr, \ + typename TTypes::ConstScalar beta1, \ + typename TTypes::ConstScalar beta2, \ + typename TTypes::ConstScalar epsilon, \ + typename TTypes::ConstFlat grad); \ + extern template struct ApplyAdamWithAmsgrad; +DECLARE_GPU_SPEC(Eigen::half); +DECLARE_GPU_SPEC(float); +DECLARE_GPU_SPEC(double); +#undef DECLARE_GPU_SPEC +} // namespace functor + +REGISTER_KERNELS(GPU, Eigen::half); +REGISTER_KERNELS(GPU, float); +REGISTER_KERNELS(GPU, double); +#endif +#undef REGISTER_CPU_KERNELS +#undef REGISTER_KERNELS + template class ApplyAdaMaxOp : public OpKernel { public: diff --git a/tensorflow/core/kernels/training_ops.h b/tensorflow/core/kernels/training_ops.h index e10a4cb125..054f07350e 100644 --- a/tensorflow/core/kernels/training_ops.h +++ b/tensorflow/core/kernels/training_ops.h @@ -126,6 +126,15 @@ struct ApplyMomentum { typename TTypes::ConstScalar momentum, bool use_nesterov); }; +template +struct ApplyKerasMomentum { + void operator()(const Device& d, typename TTypes::Flat var, + typename TTypes::Flat accum, + typename TTypes::ConstScalar lr, + typename TTypes::ConstFlat grad, + typename TTypes::ConstScalar momentum, bool use_nesterov); +}; + template struct ApplyAdam { void operator()(const Device& d, typename TTypes::Flat var, @@ -139,6 +148,20 @@ struct ApplyAdam { typename TTypes::ConstFlat grad, bool use_nesterov); }; +template +struct ApplyAdamWithAmsgrad { + void operator()(const Device& d, typename TTypes::Flat var, + typename TTypes::Flat m, typename TTypes::Flat v, + typename TTypes::Flat vhat, + typename TTypes::ConstScalar beta1_power, + typename TTypes::ConstScalar beta2_power, + typename TTypes::ConstScalar lr, + typename TTypes::ConstScalar beta1, + typename TTypes::ConstScalar beta2, + typename TTypes::ConstScalar epsilon, + typename TTypes::ConstFlat grad); +}; + template struct ApplyAdaMax { void operator()(const Device& d, typename TTypes::Flat var, diff --git a/tensorflow/core/kernels/training_ops_gpu.cu.cc b/tensorflow/core/kernels/training_ops_gpu.cu.cc index 4bd32592db..f45b9ffca7 100644 --- a/tensorflow/core/kernels/training_ops_gpu.cu.cc +++ b/tensorflow/core/kernels/training_ops_gpu.cu.cc @@ -101,6 +101,27 @@ struct ApplyMomentum { } }; +template +struct ApplyKerasMomentum { + void operator()(const GPUDevice& d, typename TTypes::Flat var, + typename TTypes::Flat accum, + typename TTypes::ConstScalar lr, + typename TTypes::ConstFlat grad, + typename TTypes::ConstScalar momentum, bool use_nesterov) { + Eigen::array::Tensor::Index, 1> bcast; + bcast[0] = grad.dimension(0); + Eigen::Sizes<1> single; + accum.device(d) = (accum * momentum.reshape(single).broadcast(bcast) - + grad * lr.reshape(single).broadcast(bcast)); + if (use_nesterov) { + var.device(d) += (accum * momentum.reshape(single).broadcast(bcast) - + grad * lr.reshape(single).broadcast(bcast)); + } else { + var.device(d) += accum; + } + } +}; + template struct ApplyAdam { void operator()(const GPUDevice& d, typename TTypes::Flat var, @@ -144,6 +165,39 @@ struct ApplyAdam { } }; +template +struct ApplyAdamWithAmsgrad { + void operator()(const GPUDevice& d, typename TTypes::Flat var, + typename TTypes::Flat m, typename TTypes::Flat v, + typename TTypes::Flat vhat, + typename TTypes::ConstScalar beta1_power, + typename TTypes::ConstScalar beta2_power, + typename TTypes::ConstScalar lr, + typename TTypes::ConstScalar beta1, + typename TTypes::ConstScalar beta2, + typename TTypes::ConstScalar epsilon, + typename TTypes::ConstFlat grad) { + Eigen::array::Tensor::Index, 1> bcast; + bcast[0] = grad.dimension(0); + Eigen::Sizes<1> single; + const auto one = static_cast(1.0); + m.device(d) = + m + (beta1.constant(one) - beta1).reshape(single).broadcast(bcast) * + (grad - m); + v.device(d) = + v + (beta2.constant(one) - beta2).reshape(single).broadcast(bcast) * + (grad.square() - v); + vhat.device(d) = vhat.cwiseMax(v); + + var.device(d) -= (lr * (beta2_power.constant(one) - beta2_power).sqrt() / + (beta1_power.constant(one) - beta1_power)) + .reshape(single) + .broadcast(bcast) * + m / + (epsilon.reshape(single).broadcast(bcast) + vhat.sqrt()); + } +}; + template struct ApplyAdaMax { void operator()(const GPUDevice& d, typename TTypes::Flat var, @@ -302,10 +356,18 @@ template struct functor::ApplyMomentum; template struct functor::ApplyMomentum; template struct functor::ApplyMomentum; +template struct functor::ApplyKerasMomentum; +template struct functor::ApplyKerasMomentum; +template struct functor::ApplyKerasMomentum; + template struct functor::ApplyAdam; template struct functor::ApplyAdam; template struct functor::ApplyAdam; +template struct functor::ApplyAdamWithAmsgrad; +template struct functor::ApplyAdamWithAmsgrad; +template struct functor::ApplyAdamWithAmsgrad; + template struct functor::ApplyAdaMax; template struct functor::ApplyAdaMax; template struct functor::ApplyAdaMax; diff --git a/tensorflow/core/kernels/training_ops_test.cc b/tensorflow/core/kernels/training_ops_test.cc index 2dcc4a500e..1ec57b4522 100644 --- a/tensorflow/core/kernels/training_ops_test.cc +++ b/tensorflow/core/kernels/training_ops_test.cc @@ -151,6 +151,40 @@ static void BM_Momentum(int iters, int params) { } BENCHMARK(BM_Momentum)->Arg(128 << 10)->Arg(256 << 10); +static void KerasMomentum(int32 n, Graph** init_g, Graph** train_g) { + TensorShape shape({n}); + { + Graph* g = new Graph(OpRegistry::Global()); + auto var = Var(g, n); + auto accum = Var(g, n); + auto zero = Zeros(g, n); + test::graph::Assign(g, var, zero); + test::graph::Assign(g, accum, zero); + *init_g = g; + } + { + Graph* g = new Graph(OpRegistry::Global()); + auto var = Var(g, n); + auto accum = Var(g, n); + auto lr = Scalar(g, 0.01); + auto grad = Random(g, n); + auto mom = Scalar(g, 0.01); + test::graph::Multi(g, "ApplyKerasMomentum", {var, accum, lr, grad, mom}); + *train_g = g; + } +} + +static void BM_KerasMomentum(int iters, int params) { + const int64 tot = static_cast(iters) * params; + testing::ItemsProcessed(tot); + testing::BytesProcessed(tot * sizeof(float)); + Graph* init; + Graph* train; + KerasMomentum(params, &init, &train); + test::Benchmark("cpu", train, GetOptions(), init).Run(iters); +} +BENCHMARK(BM_KerasMomentum)->Arg(128 << 10)->Arg(256 << 10); + static void Adam(int32 n, Graph** init_g, Graph** train_g) { TensorShape shape({n}); { @@ -194,6 +228,50 @@ static void BM_Adam(int iters, int params) { } BENCHMARK(BM_Adam)->Arg(128 << 10)->Arg(256 << 10); +static void AdamWithAmsgrad(int32 n, Graph** init_g, Graph** train_g) { + TensorShape shape({n}); + { + Graph* g = new Graph(OpRegistry::Global()); + auto var = Var(g, n); + auto m = Var(g, n); + auto v = Var(g, n); + auto zero = Zeros(g, n); + test::graph::Assign(g, var, zero); + test::graph::Assign(g, m, zero); + test::graph::Assign(g, v, zero); + *init_g = g; + } + { + Graph* g = new Graph(OpRegistry::Global()); + auto var = Var(g, n); + auto m = Var(g, n); + auto v = Var(g, n); + auto vhat = Var(g, n); + auto beta1_power = Scalar(g, 0.9); + auto beta2_power = Scalar(g, 0.99); + auto lr = Scalar(g, 0.01); + auto beta1 = Scalar(g, 0.9); + auto beta2 = Scalar(g, 0.99); + auto epsilon = Scalar(g, 1e-8); + auto grad = Random(g, n); + test::graph::Multi(g, "ApplyAdamWithAmsgrad", + {var, m, v, vhat, beta1_power, beta2_power, lr, beta1, + beta2, epsilon, grad}); + *train_g = g; + } +} + +static void BM_AdamWithAmsgrad(int iters, int params) { + const int64 tot = static_cast(iters) * params; + testing::ItemsProcessed(tot); + testing::BytesProcessed(tot * sizeof(float)); + Graph* init; + Graph* train; + AdamWithAmsgrad(params, &init, &train); + test::Benchmark("cpu", train, GetOptions(), init).Run(iters); +} +BENCHMARK(BM_AdamWithAmsgrad)->Arg(128 << 10)->Arg(256 << 10); + static void RMSProp(int32 n, Graph** init_g, Graph** train_g) { TensorShape shape({n}); { diff --git a/tensorflow/core/kernels/unicode_ops.cc b/tensorflow/core/kernels/unicode_ops.cc index dd4415711b..6c4ed1eaaf 100644 --- a/tensorflow/core/kernels/unicode_ops.cc +++ b/tensorflow/core/kernels/unicode_ops.cc @@ -13,9 +13,17 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ +#include +#include +#include #include #include +#include +#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor" +#include "unicode/appendable.h" // TF:icu +#include "unicode/schriter.h" // TF:icu +#include "unicode/uchar.h" // TF:icu #include "unicode/ucnv.h" // TF:icu #include "unicode/ucnv_err.h" // TF:icu #include "unicode/umachine.h" // TF:icu @@ -23,15 +31,57 @@ limitations under the License. #include "unicode/unistr.h" // TF:icu #include "unicode/uset.h" // TF:icu #include "unicode/utypes.h" // TF:icu +#include "tensorflow/core/framework/kernel_def_builder.h" +#include "tensorflow/core/framework/op.h" #include "tensorflow/core/framework/op_kernel.h" +#include "tensorflow/core/framework/register_types.h" #include "tensorflow/core/framework/tensor.h" +#include "tensorflow/core/framework/tensor_shape.h" +#include "tensorflow/core/framework/tensor_types.h" +#include "tensorflow/core/framework/types.h" +#include "tensorflow/core/kernels/bounds_check.h" #include "tensorflow/core/kernels/string_util.h" #include "tensorflow/core/lib/core/errors.h" #include "tensorflow/core/lib/core/status.h" +#include "tensorflow/core/lib/core/stringpiece.h" +#include "tensorflow/core/platform/types.h" #include "tensorflow/core/util/bcast.h" #include "tensorflow/core/util/ptr_util.h" namespace tensorflow { +namespace { + +void Encode(const UnicodeEncoding encoding, const icu::UnicodeString& in, + string* out) { + if (encoding == UnicodeEncoding::UTF8) { + out->clear(); + in.toUTF8String(*out); + } else if (encoding == UnicodeEncoding::UTF16BE) { + // TODO(gbillock): consider using the + // extract(char *dest, int32_t destCapacity, UConverter *cnv) + // for UTF16/32 + out->clear(); // subtle: must come before reserve() + out->reserve(2 * in.length() + 1); + const char16_t* buf = in.getBuffer(); + for (int i = 0; i < in.length(); ++i) { + // Emit big-endian encoding for UTF-16 always. + out->push_back((buf[i] & 0xFF00) >> 8); + out->push_back(buf[i] & 0x00FF); + } + } else if (encoding == UnicodeEncoding::UTF32BE) { + out->clear(); // subtle: must come before reserve() + out->reserve(4 * in.countChar32() + 1); + icu::StringCharacterIterator it(in); + UChar32 ch; + while (it.hasNext()) { + ch = it.next32PostInc(); + out->push_back((ch & 0xFF000000) >> 24); + out->push_back((ch & 0x00FF0000) >> 16); + out->push_back((ch & 0x0000FF00) >> 8); + out->push_back((ch & 0x000000FF)); + } + } +} // This error callback is only useful for finding illegal encoding errors when // we want to be strict -- otherwise illegal encodings are replaced on read @@ -146,40 +196,66 @@ class WrappedConverter { string name_; }; +struct ErrorOptions { + UChar32 subst = 0xFFFD; + bool elide_replacement = false; + bool replace_control_chars = false; + bool error_on_malformatting = false; +}; + +Status GetErrorOptions(OpKernelConstruction* ctx, ErrorOptions* out) { + *out = ErrorOptions(); + + string error_policy; + TF_RETURN_IF_ERROR(ctx->GetAttr("errors", &error_policy)); + + if (error_policy == "replace") { + out->elide_replacement = false; + } else if (error_policy == "ignore") { + out->elide_replacement = true; + } else if (error_policy == "strict") { + out->error_on_malformatting = true; + } else { + return errors::InvalidArgument( + "errors policy must be one of 'strict', 'replace', or 'ignore'"); + } + + int32 replacement_char; + TF_RETURN_IF_ERROR(ctx->GetAttr("replacement_char", &replacement_char)); + + if (replacement_char >= UCHAR_MIN_VALUE && + replacement_char <= UCHAR_MAX_VALUE) { + out->subst = replacement_char; + } else { + return errors::InvalidArgument( + "replacement_char out of unicode codepoint range"); + } + + if (ctx->HasAttr("replace_control_characters")) { + TF_RETURN_IF_ERROR(ctx->GetAttr("replace_control_characters", + &(out->replace_control_chars))); + } + + return Status::OK(); +} + +inline bool ShouldHandleFormatError(const ErrorOptions& error_options, + UChar32 ch, bool format_error) { + return ((error_options.replace_control_chars && ch <= 0x1F) || format_error); +} + +} // namespace + class UnicodeTranscodeOp : public OpKernel { public: explicit UnicodeTranscodeOp(OpKernelConstruction* ctx) : OpKernel(ctx) { - string error_policy; - OP_REQUIRES_OK(ctx, ctx->GetAttr("errors", &error_policy)); - if (error_policy == "replace") { - elide_replacement_ = false; - } else if (error_policy == "ignore") { - elide_replacement_ = true; - } else if (error_policy == "strict") { - error_on_malformatting_ = true; - } else { - ctx->CtxFailure(errors::InvalidArgument( - "errors policy must be one of 'strict', 'replace', or 'ignore'")); - } - - int32 replacement_char; - OP_REQUIRES_OK(ctx, ctx->GetAttr("replacement_char", &replacement_char)); - if (replacement_char >= UCHAR_MIN_VALUE && - replacement_char <= UCHAR_MAX_VALUE) { - subst_ = replacement_char; - } else { - ctx->CtxFailure(errors::InvalidArgument( - "replacement_char out of unicode codepoint range")); - } + OP_REQUIRES_OK(ctx, GetErrorOptions(ctx, &error_options_)); string output_encoding; OP_REQUIRES_OK(ctx, ctx->GetAttr("output_encoding", &output_encoding)); OP_REQUIRES_OK(ctx, ParseUnicodeEncoding(output_encoding, &output_encoding_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("replace_control_characters", - &replace_control_chars_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("input_encoding", &input_encoding_)); // Make a temporary UConverter to ensure it will create without error // at execution time (and to warm any data caches the converter needs). @@ -228,7 +304,7 @@ class UnicodeTranscodeOp : public OpKernel { Transcode(&(output_flat(i)), input_encoder->converter_, &found_any_format_error); } - if (error_on_malformatting_ && found_any_format_error) { + if (error_options_.error_on_malformatting && found_any_format_error) { ctx->CtxFailure( errors::InvalidArgument("Invalid formatting on input string")); } @@ -240,12 +316,12 @@ class UnicodeTranscodeOp : public OpKernel { // out-of-range inputs. void TranslateCodepoints(icu::UnicodeString* s, bool* found_any_format_error, UChar32 ch, int src_bytes, bool format_error) { - if ((replace_control_chars_ && ch <= 0x1F) || format_error) { + if (ShouldHandleFormatError(error_options_, ch, format_error)) { *found_any_format_error = true; - if (elide_replacement_) { + if (error_options_.elide_replacement) { return; } else { - ch = subst_; + ch = error_options_.subst; } } s->append(ch); @@ -263,45 +339,202 @@ class UnicodeTranscodeOp : public OpKernel { found_any_format_error, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); - if (output_encoding_ == UnicodeEncoding::UTF8) { - s->clear(); - source.toUTF8String(*s); - } else if (output_encoding_ == UnicodeEncoding::UTF16BE) { - // TODO(gbillock): consider using the - // extract(char *dest, int32_t destCapacity, UConverter *cnv) - // for UTF16/32 - s->clear(); // subtle: must come before reserve() - s->reserve(2 * source.length() + 1); - const char16_t* buf = source.getBuffer(); - for (int i = 0; i < source.length(); ++i) { - // Emit big-endian encoding for UTF-16 always. - s->push_back((buf[i] & 0xFF00) >> 8); - s->push_back(buf[i] & 0x00FF); - } - } else if (output_encoding_ == UnicodeEncoding::UTF32BE) { - s->clear(); // subtle: must come before reserve() - s->reserve(4 * source.countChar32() + 1); - for (int i = 0; i < source.countChar32(); ++i) { - // Emit big-endian encoding for UTF-32 always. - UChar32 ch = source.char32At(i); - s->push_back((ch & 0xFF000000) >> 24); - s->push_back((ch & 0x00FF0000) >> 16); - s->push_back((ch & 0x0000FF00) >> 8); - s->push_back((ch & 0x000000FF)); - } - } + Encode(output_encoding_, source, s); } - UChar32 subst_ = 0xFFFD; - bool elide_replacement_ = false; - bool replace_control_chars_ = false; - bool error_on_malformatting_ = false; - string input_encoding_; + ErrorOptions error_options_; UnicodeEncoding output_encoding_ = UnicodeEncoding::UTF8; }; REGISTER_KERNEL_BUILDER(Name("UnicodeTranscode").Device(DEVICE_CPU), UnicodeTranscodeOp); +class UnicodeDecodeWithOffsetsOp : public OpKernel { + public: + explicit UnicodeDecodeWithOffsetsOp(OpKernelConstruction* ctx) + : OpKernel(ctx) { + OP_REQUIRES_OK(ctx, GetErrorOptions(ctx, &error_options_)); + OP_REQUIRES_OK(ctx, ctx->GetAttr("input_encoding", &input_encoding_)); + // Make a temporary UConverter to ensure it will create without error + // at execution time (and to warm any data caches the converter needs). + // This instance is not used. + std::unique_ptr input_encoder = + absl::make_unique(); + input_encoder->init(input_encoding_); + OP_REQUIRES(ctx, input_encoder->converter_, + errors::InvalidArgument( + "Could not create converter for input encoding: " + + input_encoding_)); + } + + void Decode(OpKernelContext* ctx, std::vector* char_values, + std::vector* offset_values, int* string_length, + int64* next_row_split, UChar32 char_value, int char_length, + bool found_any_format_error) { + if (error_options_.error_on_malformatting && found_any_format_error) { + ctx->CtxFailure( + errors::InvalidArgument("Invalid formatting on input string")); + } + UChar32 decoded_value = char_value; + if (ShouldHandleFormatError(error_options_, char_value, + found_any_format_error)) { + if (error_options_.elide_replacement) { + return; + } else { + decoded_value = error_options_.subst; + } + } + + // Emit the char value. + char_values->push_back(decoded_value); + + // Emit the byte offset + offset_values->push_back(*string_length); + *string_length += char_length; + *next_row_split += 1; + } + + void Compute(OpKernelContext* ctx) override { + const Tensor* input_tensor; + OP_REQUIRES_OK(ctx, ctx->input("input", &input_tensor)); + + // Go through all the strings in `input`. + const auto& input_vec = input_tensor->flat(); + + std::unique_ptr input_encoder = + absl::make_unique(); + input_encoder->init(input_encoding_); + OP_REQUIRES(ctx, input_encoder->converter_, + errors::InvalidArgument( + "Could not create converter for input encoding: " + + input_encoding_)); + + std::vector char_values; + std::vector offset_values; + + Tensor* output_row_splits; + OP_REQUIRES_OK(ctx, ctx->allocate_output("row_splits", + {input_tensor->NumElements() + 1}, + &output_row_splits)); + auto out_row_splits = output_row_splits->vec(); + + int row_split_index = 0; + int64 next_row_split = 0; + for (int i = 0; i < input_vec.size(); ++i) { + const string& input = input_vec(i); + // Convert input strings into unicode values. Output to a list of + // char_values, record row splits and char_to_byte_starts, which are all + // the fields needed to construct a RaggedTensor. + out_row_splits(row_split_index) = next_row_split; + row_split_index++; + int string_length = 0; + IterateUnicodeString( + input, input_encoder->converter_, + std::bind(&UnicodeDecodeWithOffsetsOp::Decode, this, ctx, + &char_values, &offset_values, &string_length, + &next_row_split, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3)); + } + out_row_splits(row_split_index) = next_row_split; + + DCHECK(offset_values.size() == char_values.size()); + Tensor* output_char_values; + OP_REQUIRES_OK( + ctx, ctx->allocate_output("char_values", + {static_cast(char_values.size())}, + &output_char_values)); + Tensor* output_offset_values; + OP_REQUIRES_OK( + ctx, ctx->allocate_output("char_to_byte_starts", + {static_cast(offset_values.size())}, + &output_offset_values)); + auto out_char_values = output_char_values->vec(); + auto out_offset_values = output_offset_values->vec(); + + // Load output tensors from intermediate value arrays. + for (int i = 0; i < char_values.size(); ++i) { + out_char_values(i) = static_cast(char_values[i]); + out_offset_values(i) = offset_values[i]; + } + } + + private: + string input_encoding_; + ErrorOptions error_options_; +}; + +REGISTER_KERNEL_BUILDER(Name("UnicodeDecodeWithOffsets").Device(DEVICE_CPU), + UnicodeDecodeWithOffsetsOp); + +class UnicodeEncodeOp : public OpKernel { + public: + explicit UnicodeEncodeOp(OpKernelConstruction* ctx) : OpKernel(ctx) { + string encoding_tmp; + OP_REQUIRES_OK(ctx, ctx->GetAttr("output_encoding", &encoding_tmp)); + OP_REQUIRES_OK(ctx, ParseUnicodeEncoding(encoding_tmp, &encoding_)); + OP_REQUIRES_OK(ctx, GetErrorOptions(ctx, &error_options_)); + } + + /** + * Encodes Unicode codepoints into the desired string representation. + * + * We lose a dimension while encoding, since a series of integer codepoints is + * encoded into a single string. + * + * This accepts two input tensors: a rank 1 tensor of code point values and + * a single rank 1 tensor of splits which determine where each string begins + * and ends from the provided code points. + */ + void Compute(OpKernelContext* context) override { + // Get inputs + const Tensor& input_tensor = context->input(0); + const auto input_tensor_flat = input_tensor.flat(); + const Tensor& input_splits = context->input(1); + const auto input_splits_flat = input_splits.flat(); + + // Since we limit to a 2-D input (inner_values of rank 1 and a single splits + // tensor), our output dimension will be 1 with it's size equal to the + // number of splits (outer dimension or ragged tensor). + TensorShape output_shape({input_splits.dim_size(0) - 1}); + Tensor* output_tensor; + OP_REQUIRES_OK(context, context->allocate_output("output", output_shape, + &output_tensor)); + auto output_tensor_flat = output_tensor->flat(); + + // Use a single index over the flattened input values tensor. + int idx = 0; + // Loop through our split dimension to create a new string at each split. + for (int i = 1; i < input_splits_flat.size(); ++i) { + icu::UnicodeString unicode_string; + icu::UnicodeStringAppendable appendable_unicode_string(unicode_string); + for (; idx < input_splits_flat(i); ++idx) { + int32 code_point = input_tensor_flat(idx); + // Check for invalid code point + if (code_point > UCHAR_MAX_VALUE || code_point < UCHAR_MIN_VALUE) { + if (error_options_.error_on_malformatting) { + context->CtxFailure(errors::InvalidArgument( + "Code point value out of valid Unicode range.")); + return; + } else if (!error_options_.elide_replacement) { + code_point = error_options_.subst; + } + } + appendable_unicode_string.appendCodePoint(code_point); + } + // Encode our string and save in the output. + string result; + Encode(encoding_, unicode_string, &result); + output_tensor_flat(i - 1) = result; + } + } + + private: + UnicodeEncoding encoding_; + ErrorOptions error_options_; +}; + +REGISTER_KERNEL_BUILDER(Name("UnicodeEncode").Device(DEVICE_CPU), + UnicodeEncodeOp); + } // namespace tensorflow diff --git a/tensorflow/core/lib/core/threadpool.cc b/tensorflow/core/lib/core/threadpool.cc index 42689a6c3b..e929ff45a1 100644 --- a/tensorflow/core/lib/core/threadpool.cc +++ b/tensorflow/core/lib/core/threadpool.cc @@ -22,6 +22,7 @@ limitations under the License. #include "tensorflow/core/platform/denormal.h" #include "tensorflow/core/platform/logging.h" #include "tensorflow/core/platform/mutex.h" +#include "tensorflow/core/platform/numa.h" #include "tensorflow/core/platform/setround.h" #include "tensorflow/core/platform/tracing.h" #include "tensorflow/core/platform/types.h" @@ -54,6 +55,9 @@ struct EigenEnvironment { port::ScopedFlushDenormal flush; // Set the processor rounding mode to ROUND TO NEAREST. port::ScopedSetRound round(FE_TONEAREST); + if (thread_options_.numa_node != port::kNUMANoAffinity) { + port::NUMASetThreadNodeAffinity(thread_options_.numa_node); + } f(); }); } @@ -83,35 +87,38 @@ struct EigenEnvironment { struct ThreadPool::Impl : Eigen::ThreadPoolTempl { Impl(Env* env, const ThreadOptions& thread_options, const string& name, - int num_threads, bool low_latency_hint) + int num_threads, bool low_latency_hint, Eigen::Allocator* allocator) : Eigen::ThreadPoolTempl( num_threads, low_latency_hint, - EigenEnvironment(env, thread_options, name)) {} + EigenEnvironment(env, thread_options, name)), + allocator_(allocator) {} void ParallelFor(int64 total, int64 cost_per_unit, std::function fn) { CHECK_GE(total, 0); CHECK_EQ(total, (int64)(Eigen::Index)total); - Eigen::ThreadPoolDevice device(this, this->NumThreads()); + Eigen::ThreadPoolDevice device(this, this->NumThreads(), allocator_); device.parallelFor( total, Eigen::TensorOpCost(0, 0, cost_per_unit), [&fn](Eigen::Index first, Eigen::Index last) { fn(first, last); }); } + + Eigen::Allocator* allocator_; }; ThreadPool::ThreadPool(Env* env, const string& name, int num_threads) - : ThreadPool(env, ThreadOptions(), name, num_threads, true) {} + : ThreadPool(env, ThreadOptions(), name, num_threads, true, nullptr) {} ThreadPool::ThreadPool(Env* env, const ThreadOptions& thread_options, const string& name, int num_threads) - : ThreadPool(env, thread_options, name, num_threads, true) {} + : ThreadPool(env, thread_options, name, num_threads, true, nullptr) {} ThreadPool::ThreadPool(Env* env, const ThreadOptions& thread_options, const string& name, int num_threads, - bool low_latency_hint) { + bool low_latency_hint, Eigen::Allocator* allocator) { CHECK_GE(num_threads, 1); impl_.reset(new ThreadPool::Impl(env, thread_options, "tf_" + name, - num_threads, low_latency_hint)); + num_threads, low_latency_hint, allocator)); } ThreadPool::~ThreadPool() {} diff --git a/tensorflow/core/lib/core/threadpool.h b/tensorflow/core/lib/core/threadpool.h index 3da7dcb632..90c9f29447 100644 --- a/tensorflow/core/lib/core/threadpool.h +++ b/tensorflow/core/lib/core/threadpool.h @@ -22,6 +22,9 @@ limitations under the License. #include "tensorflow/core/platform/macros.h" #include "tensorflow/core/platform/types.h" +namespace Eigen { +class Allocator; +} // namespace Eigen namespace tensorflow { namespace thread { @@ -37,7 +40,8 @@ class ThreadPool { // // REQUIRES: num_threads > 0 ThreadPool(Env* env, const ThreadOptions& thread_options, const string& name, - int num_threads, bool low_latency_hint); + int num_threads, bool low_latency_hint, + Eigen::Allocator* allocator = nullptr); // Constructs a pool for low-latency ops that contains "num_threads" threads // with specified "name". env->StartThread() is used to create individual diff --git a/tensorflow/core/lib/png/png_io.cc b/tensorflow/core/lib/png/png_io.cc index bc52180265..e8dbcb97b9 100644 --- a/tensorflow/core/lib/png/png_io.cc +++ b/tensorflow/core/lib/png/png_io.cc @@ -92,7 +92,11 @@ void StringReader(png_structp png_ptr, png_bytep data, png_size_t length) { DecodeContext* const ctx = absl::bit_cast(png_get_io_ptr(png_ptr)); if (static_cast(ctx->data_left) < length) { - memset(data, 0, length); + // Don't zero out the data buffer as it has been lazily allocated (copy on + // write) and zeroing it out here can produce an OOM. Since the buffer is + // only used for reading data from the image, this doesn't result in any + // data leak, so it is safe to just leave the buffer be as it is and just + // exit with error. png_error(png_ptr, "More bytes requested to read than available"); } else { memcpy(data, ctx->data, length); diff --git a/tensorflow/core/nccl/BUILD b/tensorflow/core/nccl/BUILD index 50d9a2e8da..4be33b2a0c 100644 --- a/tensorflow/core/nccl/BUILD +++ b/tensorflow/core/nccl/BUILD @@ -11,6 +11,10 @@ exports_files(["LICENSE"]) load("//tensorflow:tensorflow.bzl", "tf_cuda_cc_test") load("//tensorflow:tensorflow.bzl", "tf_copts") load("@local_config_cuda//cuda:build_defs.bzl", "if_cuda") +load( + "//tensorflow/core:platform/default/build_config_root.bzl", + "tf_cuda_tests_tags", +) cc_library( name = "nccl_lib", @@ -34,27 +38,17 @@ cc_library( tf_cuda_cc_test( name = "nccl_manager_test", size = "medium", - srcs = if_cuda( - [ - "nccl_manager_test.cc", - ], - [], - ), - # Disabled on jenkins until errors finding nvmlShutdown are found. - tags = [ - "manual", - "multi_gpu", - "no_oss", - "noguitar", - "notap", + srcs = ["nccl_manager_test.cc"], + tags = tf_cuda_tests_tags() + [ + "no_cuda_on_cpu_tap", # TODO(b/120284216): re-enable multi_gpu ], - deps = - if_cuda([ - ":nccl_lib", - "@local_config_nccl//:nccl", - "//tensorflow/core:cuda", - "//tensorflow/core:test", - "//tensorflow/core:test_main", - "//tensorflow/core:testlib", - ]), + deps = [ + "//tensorflow/core:test", + "//tensorflow/core:test_main", + "//tensorflow/core:testlib", + ] + if_cuda([ + ":nccl_lib", + "@local_config_nccl//:nccl", + "//tensorflow/core:cuda", + ]), ) diff --git a/tensorflow/core/nccl/nccl_manager.cc b/tensorflow/core/nccl/nccl_manager.cc index f8e8c75222..df49bf1b97 100644 --- a/tensorflow/core/nccl/nccl_manager.cc +++ b/tensorflow/core/nccl/nccl_manager.cc @@ -24,6 +24,22 @@ limitations under the License. namespace tensorflow { +#define NCCL_RETURN_IF_ERROR(...) \ + do { \ + ncclResult_t nccl_status = (__VA_ARGS__); \ + if (nccl_status != ncclSuccess) { \ + return errors::Internal(ncclGetErrorString(nccl_status)); \ + } \ + } while (0) + +#define CUDA_RETURN_IF_ERROR(...) \ + do { \ + cudaError_t cuda_status = (__VA_ARGS__); \ + if (cuda_status != cudaSuccess) { \ + return errors::Internal(cudaGetErrorString(cuda_status)); \ + } \ + } while (0) + using se::cuda::ScopedActivateExecutorContext; // Contains data for a single stream used for nccl communication; this includes @@ -177,8 +193,8 @@ NcclManager* NcclManager::instance() { return instance; } -NcclManager::Communicator* NcclManager::GetCommunicator( - NcclManager::Collective* collective) { +Status NcclManager::GetCommunicator(NcclManager::Collective* collective, + NcclManager::Communicator** communicator) { // Sort by executor to make ordering of executors deterministic. std::sort(collective->participants.begin(), collective->participants.end(), [](const std::unique_ptr& a, @@ -217,7 +233,10 @@ NcclManager::Communicator* NcclManager::GetCommunicator( break; } } - if (i == num_devices) return comm.get(); + if (i == num_devices) { + *communicator = comm.get(); + return Status::OK(); + } } } @@ -264,37 +283,36 @@ NcclManager::Communicator* NcclManager::GetCommunicator( // NCCL2 prevents InitAll for more communicators than devices (but doesn't // check that device ids are unique). Work around it by initializing each // rank individually. - cudaGetDeviceCount(&device_count); + CUDA_RETURN_IF_ERROR(cudaGetDeviceCount(&device_count)); #endif std::vector nccl_comms(num_devices); if (num_devices <= device_count) { - auto result = - ncclCommInitAll(nccl_comms.data(), num_devices, devices.data()); - CHECK_EQ(result, ncclSuccess) << ncclGetErrorString(result); + NCCL_RETURN_IF_ERROR( + ncclCommInitAll(nccl_comms.data(), num_devices, devices.data())); } else { int savedDevice = 0; - CHECK_EQ(cudaGetDevice(&savedDevice), cudaSuccess); + CUDA_RETURN_IF_ERROR(cudaGetDevice(&savedDevice)); ncclUniqueId commId; - ncclGetUniqueId(&commId); + NCCL_RETURN_IF_ERROR(ncclGetUniqueId(&commId)); #if NCCL_MAJOR >= 2 - CHECK_EQ(ncclGroupStart(), ncclSuccess); + NCCL_RETURN_IF_ERROR(ncclGroupStart()); #endif for (int rank = 0; rank < num_devices; ++rank) { - cudaSetDevice(devices[rank]); - auto result = - ncclCommInitRank(nccl_comms.data() + rank, num_devices, commId, rank); - CHECK_EQ(result, ncclSuccess) << ncclGetErrorString(result); + CUDA_RETURN_IF_ERROR(cudaSetDevice(devices[rank])); + NCCL_RETURN_IF_ERROR(ncclCommInitRank(nccl_comms.data() + rank, + num_devices, commId, rank)); } #if NCCL_MAJOR >= 2 - CHECK_EQ(ncclGroupEnd(), ncclSuccess); + NCCL_RETURN_IF_ERROR(ncclGroupEnd()); #endif - cudaSetDevice(savedDevice); + CUDA_RETURN_IF_ERROR(cudaSetDevice(savedDevice)); } for (int rank = 0; rank < num_devices; ++rank) { members[rank].nccl_comm = nccl_comms[rank]; } communicators_.emplace_back(new Communicator(std::move(members))); - return communicators_.back().get(); + *communicator = communicators_.back().get(); + return Status::OK(); } void NcclManager::AddToAllReduce(int num_devices, const string& key, @@ -400,10 +418,18 @@ void NcclManager::AddParticipant(int num_devices, const string& key, void NcclManager::RunCollective(const string& key, Collective* collective) { static mutex collective_mu(LINKER_INITIALIZED); - auto* communicator = GetCommunicator(collective); - collective->communicator = communicator; - const int size = communicator->num_devices; + Communicator* communicator = nullptr; + const int size = static_cast(collective->participants.size()); + Status s = GetCommunicator(collective, &communicator); + if (!s.ok()) { + for (int i = 0; i < size; ++i) { + collective->participants[i]->done_callback(s); + } + delete collective; + return; + } + collective->communicator = communicator; for (int rank = 0; rank < size; ++rank) { Participant* p = collective->participants[rank].get(); NcclStream* nccl_stream = communicator->members[rank].nccl_stream; diff --git a/tensorflow/core/nccl/nccl_manager.h b/tensorflow/core/nccl/nccl_manager.h index 76b49101d4..5da4fe5554 100644 --- a/tensorflow/core/nccl/nccl_manager.h +++ b/tensorflow/core/nccl/nccl_manager.h @@ -103,7 +103,13 @@ class NcclManager { struct NcclStream; struct Participant; - Communicator* GetCommunicator(Collective* collective); + // Gets the `Communicator` object that will be used to enqueue NCCL kernels + // for `collective`, and returns it via `communicator`. + // + // This may involve creating CUDA streams and NCCL initialization. If a NCCL + // or CUDA error occurs in the process, this returns an INTERNAL error with + // the corresponding NCCL/CUDA error string. + Status GetCommunicator(Collective* collective, Communicator** communicator); void AddParticipant(int num_devices, const string& key, std::unique_ptr participant, diff --git a/tensorflow/core/nccl/nccl_manager_test.cc b/tensorflow/core/nccl/nccl_manager_test.cc index dbc07865f0..f43103e120 100644 --- a/tensorflow/core/nccl/nccl_manager_test.cc +++ b/tensorflow/core/nccl/nccl_manager_test.cc @@ -28,8 +28,8 @@ limitations under the License. namespace tensorflow { -static std::vector GetGPUDevices() { - std::vector devices; +static std::vector> GetGPUDevices() { + std::vector> devices; SessionOptions session_options; session_options.config.mutable_gpu_options() ->set_per_process_gpu_memory_fraction(0.1); @@ -37,12 +37,12 @@ static std::vector GetGPUDevices() { Status s = DeviceFactory::GetFactory(DEVICE_GPU) ->AddDevices(session_options, "", &devices); TF_CHECK_OK(s); - std::vector gpus; - for (Device* d : devices) { - if (d->device_type() == "GPU") { - gpus.push_back(static_cast(d)); - } else { - delete d; + std::vector> gpus; + for (std::unique_ptr& device : devices) { + if (device->device_type() == "GPU") { + // If `device_type()` is GPU, this `Device` is guaranteed to be a + // `BaseGPUDevice`, which is a subclass of `Device`. + gpus.emplace_back(static_cast(device.release())); } } return gpus; @@ -64,16 +64,14 @@ class NcclManagerTest : public ::testing::Test { }; static void SetUpTestCase() { - setenv("NCCL_DEBUG", "INFO", 1 /* replace */); - devices_ = new std::vector(GetGPUDevices()); - CHECK(!devices_->empty()); + setenv("NCCL_DEBUG", "WARN", 1 /* replace */); + devices_ = new std::vector>(GetGPUDevices()); LOG(ERROR) << "Running test with " << devices_->size() << " gpus"; } - static void TearDownTestCase() { - for (auto device : *devices_) delete device; - delete devices_; - } + static int32 NumGPUs() { return static_cast(devices_->size()); } + + static void TearDownTestCase() { delete devices_; } TestCase* MakeTestCase(int num_ranks, ncclRedOp_t reduction_op, TensorShape shape, float value_offset) { @@ -153,7 +151,7 @@ class NcclManagerTest : public ::testing::Test { stream->ThenMemcpy(out_cpu.flat().data(), out_gpu_mem, out_cpu.TotalBytes()); SE_ASSERT_OK(stream->BlockHostUntilDone()); - test::ExpectTensorNear(test_case->expected, out_cpu, 0.01); + test::ExpectClose(test_case->expected, out_cpu); } } @@ -166,7 +164,7 @@ class NcclManagerTest : public ::testing::Test { } static BaseGPUDevice* GetDevice(size_t rank) { - return devices_->at(rank % devices_->size()); + return devices_->at(rank % devices_->size()).get(); } private: @@ -181,13 +179,14 @@ class NcclManagerTest : public ::testing::Test { } private: - static std::vector* devices_; + static std::vector>* devices_; static const DataType data_type_; static const Scalar max_; }; template -std::vector* NcclManagerTest::devices_ = nullptr; +std::vector>* NcclManagerTest::devices_ = + nullptr; template const DataType NcclManagerTest::data_type_ = DataTypeToEnum::value; @@ -195,13 +194,13 @@ template const Scalar NcclManagerTest::max_ = Eigen::NumTraits::highest(); -// Instantiate tests for float and half. -using TypeList = ::testing::Types; +// Instantiate tests for float and double. +using TypeList = ::testing::Types; TYPED_TEST_CASE(NcclManagerTest, TypeList); // Test basic sum reduction. TYPED_TEST(NcclManagerTest, BasicSumReduction) { - const int num_ranks = 3; + const int num_ranks = this->NumGPUs(); for (int op = 0; op < 4; ++op) { ncclRedOp_t reduction_op = static_cast(op); @@ -230,10 +229,10 @@ TYPED_TEST(NcclManagerTest, BasicSumReduction) { // To test the higher settings, increase num_ranks, // num_collectives_per_iteration and time_limit_micros. TYPED_TEST(NcclManagerTest, MultipleCallers) { - const int num_ranks = 1; // 2; - const int num_collectives_per_iteration = 1; // 1000; + const int num_ranks = this->NumGPUs(); + const int num_collectives_per_iteration = 10; // 1000; const int num_threads = 3; - const int time_limit_micros = 1; // 60 * 30 * 1000 * 1000; + const int time_limit_micros = 100; // 60 * 30 * 1000 * 1000; int64 start = Env::Default()->NowMicros(); srand(Env::Default()->NowMicros()); diff --git a/tensorflow/core/ops/array_ops.cc b/tensorflow/core/ops/array_ops.cc index f55562ec99..281e2996ed 100644 --- a/tensorflow/core/ops/array_ops.cc +++ b/tensorflow/core/ops/array_ops.cc @@ -2743,6 +2743,9 @@ REGISTER_OP("QuantizeAndDequantizeV2") .Attr("range_given: bool = false") .Output("output: T") .Attr("T: {bfloat16, half, float, double}") + .Attr( + "round_mode: {'HALF_TO_EVEN', 'HALF_UP'} = " + "'HALF_TO_EVEN'") .SetShapeFn([](InferenceContext* c) { ShapeHandle unused; TF_RETURN_IF_ERROR(c->WithRank(c->input(1), 0, &unused)); @@ -2878,14 +2881,9 @@ REGISTER_OP("QuantizedInstanceNorm") namespace { -Status ScatterNdShape(InferenceContext* c) { - ShapeHandle indices_shape; - TF_RETURN_IF_ERROR(c->WithRankAtLeast(c->input(0), 1, &indices_shape)); - ShapeHandle updates_shape; - TF_RETURN_IF_ERROR(c->WithRankAtLeast(c->input(1), 1, &updates_shape)); - ShapeHandle output_shape; - TF_RETURN_IF_ERROR(c->MakeShapeFromShapeTensor(2, &output_shape)); - +Status ScatterNdShapeHelper(InferenceContext* c, ShapeHandle indices_shape, + ShapeHandle updates_shape, + ShapeHandle output_shape) { if (c->Value(c->NumElements(output_shape)) == 0 && (c->Value(c->NumElements(indices_shape)) > 0 || c->Value(c->NumElements(updates_shape)) > 0)) { @@ -2940,6 +2938,26 @@ Status ScatterNdShape(InferenceContext* c) { return Status::OK(); } +Status ScatterNdShape(InferenceContext* c) { + ShapeHandle indices_shape; + TF_RETURN_IF_ERROR(c->WithRankAtLeast(c->input(0), 1, &indices_shape)); + ShapeHandle updates_shape; + TF_RETURN_IF_ERROR(c->WithRankAtLeast(c->input(1), 1, &updates_shape)); + ShapeHandle output_shape; + TF_RETURN_IF_ERROR(c->MakeShapeFromShapeTensor(2, &output_shape)); + return ScatterNdShapeHelper(c, indices_shape, updates_shape, output_shape); +} + +Status ScatterNdTensorShape(InferenceContext* c) { + ShapeHandle output_shape; + TF_RETURN_IF_ERROR(c->WithRankAtLeast(c->input(0), 1, &output_shape)); + ShapeHandle indices_shape; + TF_RETURN_IF_ERROR(c->WithRankAtLeast(c->input(1), 1, &indices_shape)); + ShapeHandle updates_shape; + TF_RETURN_IF_ERROR(c->WithRankAtLeast(c->input(2), 1, &updates_shape)); + return ScatterNdShapeHelper(c, indices_shape, updates_shape, output_shape); +} + } // namespace REGISTER_OP("UpperBound") @@ -2979,6 +2997,33 @@ REGISTER_OP("ScatterNd") .Attr("Tindices: {int32, int64}") .SetShapeFn(ScatterNdShape); +REGISTER_OP("TensorScatterUpdate") + .Input("tensor: T") + .Input("indices: Tindices") + .Input("updates: T") + .Output("output: T") + .Attr("T: type") + .Attr("Tindices: {int32, int64}") + .SetShapeFn(ScatterNdTensorShape); + +REGISTER_OP("TensorScatterAdd") + .Input("tensor: T") + .Input("indices: Tindices") + .Input("updates: T") + .Output("output: T") + .Attr("T: type") + .Attr("Tindices: {int32, int64}") + .SetShapeFn(ScatterNdTensorShape); + +REGISTER_OP("TensorScatterSub") + .Input("tensor: T") + .Input("indices: Tindices") + .Input("updates: T") + .Output("output: T") + .Attr("T: type") + .Attr("Tindices: {int32, int64}") + .SetShapeFn(ScatterNdTensorShape); + REGISTER_OP("ScatterNdNonAliasingAdd") .Input("input: T") .Input("indices: Tindices") diff --git a/tensorflow/core/ops/compat/ops_history.v1.pbtxt b/tensorflow/core/ops/compat/ops_history.v1.pbtxt index bfcc92dcb0..8022c390c6 100644 --- a/tensorflow/core/ops/compat/ops_history.v1.pbtxt +++ b/tensorflow/core/ops/compat/ops_history.v1.pbtxt @@ -12076,33 +12076,6 @@ op { type: "list(float)" } } -op { - name: "BytesProducedStatsDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "tag" - type: DT_STRING - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } -} op { name: "CTCBeamSearchDecoder" input_arg { @@ -17326,21 +17299,6 @@ op { minimum: 1 } } -op { - name: "DatasetToTFRecord" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "filename" - type: DT_STRING - } - input_arg { - name: "compression_type" - type: DT_STRING - } -} op { name: "DebugGradientIdentity" input_arg { @@ -18487,69 +18445,6 @@ op { } } } -op { - name: "DenseToSparseBatchDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "batch_size" - type: DT_INT64 - } - input_arg { - name: "row_shape" - type: DT_INT64 - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } - is_stateful: true -} -op { - name: "DenseToSparseBatchDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "batch_size" - type: DT_INT64 - } - input_arg { - name: "row_shape" - type: DT_INT64 - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } -} op { name: "DenseToSparseSetOperation" input_arg { @@ -21153,24 +21048,6 @@ op { type: DT_STRING } } -op { - name: "EnqueueInQueueDataset" - input_arg { - name: "queue" - type: DT_VARIANT - } - input_arg { - name: "components" - type_list_attr: "Tcomponents" - } - attr { - name: "Tcomponents" - type: "list(type)" - has_minimum: true - minimum: 1 - } - is_stateful: true -} op { name: "EnsureShape" input_arg { @@ -21626,6 +21503,33 @@ op { minimum: 1 } } +op { + name: "ExperimentalBytesProducedStatsDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "tag" + type: DT_STRING + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } +} op { name: "ExperimentalCSVDataset" input_arg { @@ -21691,6 +21595,84 @@ op { } is_stateful: true } +op { + name: "ExperimentalDatasetToTFRecord" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "filename" + type: DT_STRING + } + input_arg { + name: "compression_type" + type: DT_STRING + } +} +op { + name: "ExperimentalDenseToSparseBatchDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "batch_size" + type: DT_INT64 + } + input_arg { + name: "row_shape" + type: DT_INT64 + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } + is_stateful: true +} +op { + name: "ExperimentalDenseToSparseBatchDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "batch_size" + type: DT_INT64 + } + input_arg { + name: "row_shape" + type: DT_INT64 + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } +} op { name: "ExperimentalDirectedInterleaveDataset" input_arg { @@ -21726,50 +21708,129 @@ op { } } op { - name: "ExperimentalFunctionBufferingResource" + name: "ExperimentalGroupByReducerDataset" input_arg { - name: "string_arg" - type: DT_STRING + name: "input_dataset" + type: DT_VARIANT } input_arg { - name: "target_device" - type: DT_STRING + name: "key_func_other_arguments" + type_list_attr: "Tkey_func_other_arguments" + } + input_arg { + name: "init_func_other_arguments" + type_list_attr: "Tinit_func_other_arguments" + } + input_arg { + name: "reduce_func_other_arguments" + type_list_attr: "Treduce_func_other_arguments" + } + input_arg { + name: "finalize_func_other_arguments" + type_list_attr: "Tfinalize_func_other_arguments" } output_arg { - name: "resource" - type: DT_RESOURCE + name: "handle" + type: DT_VARIANT } attr { - name: "shared_name" - type: "string" + name: "key_func" + type: "func" } attr { - name: "container" - type: "string" + name: "init_func" + type: "func" } attr { - name: "f" + name: "reduce_func" type: "func" } attr { - name: "buffer_size" - type: "int" + name: "finalize_func" + type: "func" + } + attr { + name: "Tkey_func_other_arguments" + type: "list(type)" + has_minimum: true + } + attr { + name: "Tinit_func_other_arguments" + type: "list(type)" + has_minimum: true + } + attr { + name: "Treduce_func_other_arguments" + type: "list(type)" + has_minimum: true + } + attr { + name: "Tfinalize_func_other_arguments" + type: "list(type)" + has_minimum: true } attr { name: "output_types" type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 } is_stateful: true } op { - name: "ExperimentalFunctionBufferingResourceGetNext" + name: "ExperimentalGroupByWindowDataset" input_arg { - name: "function_buffer_resource" - type: DT_RESOURCE + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "key_func_other_arguments" + type_list_attr: "Tkey_func_other_arguments" + } + input_arg { + name: "reduce_func_other_arguments" + type_list_attr: "Treduce_func_other_arguments" + } + input_arg { + name: "window_size_func_other_arguments" + type_list_attr: "Twindow_size_func_other_arguments" } output_arg { - name: "output" - type_list_attr: "output_types" + name: "handle" + type: DT_VARIANT + } + attr { + name: "key_func" + type: "func" + } + attr { + name: "reduce_func" + type: "func" + } + attr { + name: "window_size_func" + type: "func" + } + attr { + name: "Tkey_func_other_arguments" + type: "list(type)" + has_minimum: true + } + attr { + name: "Treduce_func_other_arguments" + type: "list(type)" + has_minimum: true + } + attr { + name: "Twindow_size_func_other_arguments" + type: "list(type)" + has_minimum: true } attr { name: "output_types" @@ -21777,15 +21838,75 @@ op { has_minimum: true minimum: 1 } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } is_stateful: true } op { - name: "ExperimentalFunctionBufferingResourceReset" + name: "ExperimentalGroupByWindowDataset" input_arg { - name: "function_buffer_resource" - type: DT_RESOURCE + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "key_func_other_arguments" + type_list_attr: "Tkey_func_other_arguments" + } + input_arg { + name: "reduce_func_other_arguments" + type_list_attr: "Treduce_func_other_arguments" + } + input_arg { + name: "window_size_func_other_arguments" + type_list_attr: "Twindow_size_func_other_arguments" + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "key_func" + type: "func" + } + attr { + name: "reduce_func" + type: "func" + } + attr { + name: "window_size_func" + type: "func" + } + attr { + name: "Tkey_func_other_arguments" + type: "list(type)" + has_minimum: true + } + attr { + name: "Treduce_func_other_arguments" + type: "list(type)" + has_minimum: true + } + attr { + name: "Twindow_size_func_other_arguments" + type: "list(type)" + has_minimum: true + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 } - is_stateful: true } op { name: "ExperimentalIdentityIndexedDataset" @@ -21898,6 +22019,136 @@ op { } is_stateful: true } +op { + name: "ExperimentalLatencyStatsDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "tag" + type: DT_STRING + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } +} +op { + name: "ExperimentalMapAndBatchDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "other_arguments" + type_list_attr: "Targuments" + } + input_arg { + name: "batch_size" + type: DT_INT64 + } + input_arg { + name: "num_parallel_calls" + type: DT_INT64 + } + input_arg { + name: "drop_remainder" + type: DT_BOOL + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "f" + type: "func" + } + attr { + name: "Targuments" + type: "list(type)" + has_minimum: true + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } +} +op { + name: "ExperimentalMapDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "other_arguments" + type_list_attr: "Targuments" + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "f" + type: "func" + } + attr { + name: "Targuments" + type: "list(type)" + has_minimum: true + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } + attr { + name: "use_inter_op_parallelism" + type: "bool" + default_value { + b: true + } + } +} +op { + name: "ExperimentalMatchingFilesDataset" + input_arg { + name: "patterns" + type: DT_STRING + } + output_arg { + name: "handle" + type: DT_VARIANT + } + is_stateful: true +} op { name: "ExperimentalMaterializedIndexDatasetHandle" output_arg { @@ -21926,6 +22177,33 @@ op { } is_stateful: true } +op { + name: "ExperimentalMaxIntraOpParallelismDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "max_intra_op_parallelism" + type: DT_INT64 + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } +} op { name: "ExperimentalNonSerializableDataset" input_arg { @@ -21997,6 +22275,346 @@ op { minimum: 1 } } +op { + name: "ExperimentalParallelInterleaveDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "other_arguments" + type_list_attr: "Targuments" + } + input_arg { + name: "cycle_length" + type: DT_INT64 + } + input_arg { + name: "block_length" + type: DT_INT64 + } + input_arg { + name: "sloppy" + type: DT_BOOL + } + input_arg { + name: "buffer_output_elements" + type: DT_INT64 + } + input_arg { + name: "prefetch_input_elements" + type: DT_INT64 + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "f" + type: "func" + } + attr { + name: "Targuments" + type: "list(type)" + has_minimum: true + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } +} +op { + name: "ExperimentalParseExampleDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "num_parallel_calls" + type: DT_INT64 + } + input_arg { + name: "dense_defaults" + type_list_attr: "Tdense" + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "sparse_keys" + type: "list(string)" + has_minimum: true + } + attr { + name: "dense_keys" + type: "list(string)" + has_minimum: true + } + attr { + name: "sparse_types" + type: "list(type)" + has_minimum: true + allowed_values { + list { + type: DT_FLOAT + type: DT_INT64 + type: DT_STRING + } + } + } + attr { + name: "Tdense" + type: "list(type)" + has_minimum: true + allowed_values { + list { + type: DT_FLOAT + type: DT_INT64 + type: DT_STRING + } + } + } + attr { + name: "dense_shapes" + type: "list(shape)" + has_minimum: true + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } +} +op { + name: "ExperimentalParseExampleDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "num_parallel_calls" + type: DT_INT64 + } + input_arg { + name: "dense_defaults" + type_list_attr: "Tdense" + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "sparse_keys" + type: "list(string)" + has_minimum: true + } + attr { + name: "dense_keys" + type: "list(string)" + has_minimum: true + } + attr { + name: "sparse_types" + type: "list(type)" + has_minimum: true + allowed_values { + list { + type: DT_FLOAT + type: DT_INT64 + type: DT_STRING + } + } + } + attr { + name: "Tdense" + type: "list(type)" + has_minimum: true + allowed_values { + list { + type: DT_FLOAT + type: DT_INT64 + type: DT_STRING + } + } + } + attr { + name: "dense_shapes" + type: "list(shape)" + has_minimum: true + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } + attr { + name: "sloppy" + type: "bool" + default_value { + b: false + } + } +} +op { + name: "ExperimentalPrivateThreadPoolDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "num_threads" + type: DT_INT64 + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } +} +op { + name: "ExperimentalRandomDataset" + input_arg { + name: "seed" + type: DT_INT64 + } + input_arg { + name: "seed2" + type: DT_INT64 + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } + is_stateful: true +} +op { + name: "ExperimentalScanDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "initial_state" + type_list_attr: "Tstate" + } + input_arg { + name: "other_arguments" + type_list_attr: "Targuments" + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "f" + type: "func" + } + attr { + name: "Tstate" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "Targuments" + type: "list(type)" + has_minimum: true + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } +} +op { + name: "ExperimentalSetStatsAggregatorDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "stats_aggregator" + type: DT_RESOURCE + } + input_arg { + name: "tag" + type: DT_STRING + } + input_arg { + name: "counter_prefix" + type: DT_STRING + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } + is_stateful: true +} op { name: "ExperimentalSleepDataset" input_arg { @@ -22024,6 +22642,107 @@ op { minimum: 1 } } +op { + name: "ExperimentalSlidingWindowDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "window_size" + type: DT_INT64 + } + input_arg { + name: "window_shift" + type: DT_INT64 + } + input_arg { + name: "window_stride" + type: DT_INT64 + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } +} +op { + name: "ExperimentalSqlDataset" + input_arg { + name: "driver_name" + type: DT_STRING + } + input_arg { + name: "data_source_name" + type: DT_STRING + } + input_arg { + name: "query" + type: DT_STRING + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } + is_stateful: true +} +op { + name: "ExperimentalStatsAggregatorHandle" + output_arg { + name: "handle" + type: DT_RESOURCE + } + attr { + name: "container" + type: "string" + default_value { + s: "" + } + } + attr { + name: "shared_name" + type: "string" + default_value { + s: "" + } + } + is_stateful: true +} +op { + name: "ExperimentalStatsAggregatorSummary" + input_arg { + name: "iterator" + type: DT_RESOURCE + } + output_arg { + name: "summary" + type: DT_STRING + } + is_stateful: true +} op { name: "ExperimentalThreadPoolDataset" input_arg { @@ -22089,6 +22808,29 @@ op { } is_stateful: true } +op { + name: "ExperimentalUnbatchDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } +} op { name: "ExperimentalUniqueDataset" input_arg { @@ -26313,207 +27055,6 @@ op { } } } -op { - name: "GroupByReducerDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "key_func_other_arguments" - type_list_attr: "Tkey_func_other_arguments" - } - input_arg { - name: "init_func_other_arguments" - type_list_attr: "Tinit_func_other_arguments" - } - input_arg { - name: "reduce_func_other_arguments" - type_list_attr: "Treduce_func_other_arguments" - } - input_arg { - name: "finalize_func_other_arguments" - type_list_attr: "Tfinalize_func_other_arguments" - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "key_func" - type: "func" - } - attr { - name: "init_func" - type: "func" - } - attr { - name: "reduce_func" - type: "func" - } - attr { - name: "finalize_func" - type: "func" - } - attr { - name: "Tkey_func_other_arguments" - type: "list(type)" - has_minimum: true - } - attr { - name: "Tinit_func_other_arguments" - type: "list(type)" - has_minimum: true - } - attr { - name: "Treduce_func_other_arguments" - type: "list(type)" - has_minimum: true - } - attr { - name: "Tfinalize_func_other_arguments" - type: "list(type)" - has_minimum: true - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } - is_stateful: true -} -op { - name: "GroupByWindowDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "key_func_other_arguments" - type_list_attr: "Tkey_func_other_arguments" - } - input_arg { - name: "reduce_func_other_arguments" - type_list_attr: "Treduce_func_other_arguments" - } - input_arg { - name: "window_size_func_other_arguments" - type_list_attr: "Twindow_size_func_other_arguments" - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "key_func" - type: "func" - } - attr { - name: "reduce_func" - type: "func" - } - attr { - name: "window_size_func" - type: "func" - } - attr { - name: "Tkey_func_other_arguments" - type: "list(type)" - has_minimum: true - } - attr { - name: "Treduce_func_other_arguments" - type: "list(type)" - has_minimum: true - } - attr { - name: "Twindow_size_func_other_arguments" - type: "list(type)" - has_minimum: true - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } - is_stateful: true -} -op { - name: "GroupByWindowDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "key_func_other_arguments" - type_list_attr: "Tkey_func_other_arguments" - } - input_arg { - name: "reduce_func_other_arguments" - type_list_attr: "Treduce_func_other_arguments" - } - input_arg { - name: "window_size_func_other_arguments" - type_list_attr: "Twindow_size_func_other_arguments" - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "key_func" - type: "func" - } - attr { - name: "reduce_func" - type: "func" - } - attr { - name: "window_size_func" - type: "func" - } - attr { - name: "Tkey_func_other_arguments" - type: "list(type)" - has_minimum: true - } - attr { - name: "Treduce_func_other_arguments" - type: "list(type)" - has_minimum: true - } - attr { - name: "Twindow_size_func_other_arguments" - type: "list(type)" - has_minimum: true - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } -} op { name: "GuaranteeConst" input_arg { @@ -29031,151 +29572,124 @@ op { } } op { - name: "LRNGrad" - input_arg { - name: "input_grads" - type_attr: "T" - } - input_arg { - name: "input_image" - type_attr: "T" - } - input_arg { - name: "output_image" - type_attr: "T" - } - output_arg { - name: "output" - type_attr: "T" - } - attr { - name: "depth_radius" - type: "int" - default_value { - i: 5 - } - } - attr { - name: "bias" - type: "float" - default_value { - f: 1 - } - } - attr { - name: "alpha" - type: "float" - default_value { - f: 1 - } - } - attr { - name: "beta" - type: "float" - default_value { - f: 0.5 - } - } - attr { - name: "T" - type: "type" - default_value { - type: DT_FLOAT - } - allowed_values { - list { - type: DT_FLOAT - type: DT_HALF - } - } - } -} -op { - name: "LRNGrad" - input_arg { - name: "input_grads" - type_attr: "T" - } - input_arg { - name: "input_image" - type_attr: "T" - } - input_arg { - name: "output_image" - type_attr: "T" - } - output_arg { - name: "output" - type_attr: "T" - } - attr { - name: "depth_radius" - type: "int" - default_value { - i: 5 - } - } - attr { - name: "bias" - type: "float" - default_value { - f: 1 - } - } - attr { - name: "alpha" - type: "float" - default_value { - f: 1 - } - } - attr { - name: "beta" - type: "float" - default_value { - f: 0.5 - } - } - attr { - name: "T" - type: "type" - default_value { - type: DT_FLOAT - } - allowed_values { - list { - type: DT_HALF - type: DT_BFLOAT16 - type: DT_FLOAT - } - } - } -} -op { - name: "LatencyStatsDataset" + name: "LRNGrad" input_arg { - name: "input_dataset" - type: DT_VARIANT + name: "input_grads" + type_attr: "T" } input_arg { - name: "tag" - type: DT_STRING + name: "input_image" + type_attr: "T" + } + input_arg { + name: "output_image" + type_attr: "T" } output_arg { - name: "handle" - type: DT_VARIANT + name: "output" + type_attr: "T" } attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 + name: "depth_radius" + type: "int" + default_value { + i: 5 + } } attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 + name: "bias" + type: "float" + default_value { + f: 1 + } + } + attr { + name: "alpha" + type: "float" + default_value { + f: 1 + } + } + attr { + name: "beta" + type: "float" + default_value { + f: 0.5 + } + } + attr { + name: "T" + type: "type" + default_value { + type: DT_FLOAT + } + allowed_values { + list { + type: DT_FLOAT + type: DT_HALF + } + } + } +} +op { + name: "LRNGrad" + input_arg { + name: "input_grads" + type_attr: "T" + } + input_arg { + name: "input_image" + type_attr: "T" + } + input_arg { + name: "output_image" + type_attr: "T" + } + output_arg { + name: "output" + type_attr: "T" + } + attr { + name: "depth_radius" + type: "int" + default_value { + i: 5 + } + } + attr { + name: "bias" + type: "float" + default_value { + f: 1 + } + } + attr { + name: "alpha" + type: "float" + default_value { + f: 1 + } + } + attr { + name: "beta" + type: "float" + default_value { + f: 0.5 + } + } + attr { + name: "T" + type: "type" + default_value { + type: DT_FLOAT + } + allowed_values { + list { + type: DT_HALF + type: DT_BFLOAT16 + type: DT_FLOAT + } + } } } op { @@ -30635,102 +31149,6 @@ op { } is_stateful: true } -op { - name: "MapAndBatchDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "other_arguments" - type_list_attr: "Targuments" - } - input_arg { - name: "batch_size" - type: DT_INT64 - } - input_arg { - name: "num_parallel_batches" - type: DT_INT64 - } - input_arg { - name: "drop_remainder" - type: DT_BOOL - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "f" - type: "func" - } - attr { - name: "Targuments" - type: "list(type)" - has_minimum: true - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } -} -op { - name: "MapAndBatchDatasetV2" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "other_arguments" - type_list_attr: "Targuments" - } - input_arg { - name: "batch_size" - type: DT_INT64 - } - input_arg { - name: "num_parallel_calls" - type: DT_INT64 - } - input_arg { - name: "drop_remainder" - type: DT_BOOL - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "f" - type: "func" - } - attr { - name: "Targuments" - type: "list(type)" - has_minimum: true - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } -} op { name: "MapClear" attr { @@ -31392,72 +31810,60 @@ op { } } op { - name: "MatMul" - input_arg { - name: "a" - type_attr: "T" - } - input_arg { - name: "b" - type_attr: "T" - } - output_arg { - name: "product" - type_attr: "T" - } - attr { - name: "transpose_a" - type: "bool" - default_value { - b: false - } - } - attr { - name: "transpose_b" - type: "bool" - default_value { - b: false - } - } - attr { - name: "T" - type: "type" - allowed_values { - list { - type: DT_BFLOAT16 - type: DT_HALF - type: DT_FLOAT - type: DT_DOUBLE - type: DT_INT32 - type: DT_INT64 - type: DT_COMPLEX64 - type: DT_COMPLEX128 - } - } - } -} -op { - name: "MatchingFiles" + name: "MatMul" input_arg { - name: "pattern" - type: DT_STRING + name: "a" + type_attr: "T" + } + input_arg { + name: "b" + type_attr: "T" } output_arg { - name: "filenames" - type: DT_STRING + name: "product" + type_attr: "T" + } + attr { + name: "transpose_a" + type: "bool" + default_value { + b: false + } + } + attr { + name: "transpose_b" + type: "bool" + default_value { + b: false + } + } + attr { + name: "T" + type: "type" + allowed_values { + list { + type: DT_BFLOAT16 + type: DT_HALF + type: DT_FLOAT + type: DT_DOUBLE + type: DT_INT32 + type: DT_INT64 + type: DT_COMPLEX64 + type: DT_COMPLEX128 + } + } } } op { - name: "MatchingFilesDataset" + name: "MatchingFiles" input_arg { - name: "patterns" + name: "pattern" type: DT_STRING } output_arg { - name: "handle" - type: DT_VARIANT + name: "filenames" + type: DT_STRING } - is_stateful: true } op { name: "MatrixBandPart" @@ -39011,62 +39417,6 @@ op { type: "type" } } -op { - name: "ParallelInterleaveDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "other_arguments" - type_list_attr: "Targuments" - } - input_arg { - name: "cycle_length" - type: DT_INT64 - } - input_arg { - name: "block_length" - type: DT_INT64 - } - input_arg { - name: "sloppy" - type: DT_BOOL - } - input_arg { - name: "buffer_output_elements" - type: DT_INT64 - } - input_arg { - name: "prefetch_input_elements" - type: DT_INT64 - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "f" - type: "func" - } - attr { - name: "Targuments" - type: "list(type)" - has_minimum: true - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } -} op { name: "ParallelInterleaveDatasetV2" input_arg { @@ -39561,153 +39911,6 @@ op { has_minimum: true } } -op { - name: "ParseExampleDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "num_parallel_calls" - type: DT_INT64 - } - input_arg { - name: "dense_defaults" - type_list_attr: "Tdense" - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "sparse_keys" - type: "list(string)" - has_minimum: true - } - attr { - name: "dense_keys" - type: "list(string)" - has_minimum: true - } - attr { - name: "sparse_types" - type: "list(type)" - has_minimum: true - allowed_values { - list { - type: DT_FLOAT - type: DT_INT64 - type: DT_STRING - } - } - } - attr { - name: "Tdense" - type: "list(type)" - has_minimum: true - allowed_values { - list { - type: DT_FLOAT - type: DT_INT64 - type: DT_STRING - } - } - } - attr { - name: "dense_shapes" - type: "list(shape)" - has_minimum: true - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } -} -op { - name: "ParseExampleDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "num_parallel_calls" - type: DT_INT64 - } - input_arg { - name: "dense_defaults" - type_list_attr: "Tdense" - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "sparse_keys" - type: "list(string)" - has_minimum: true - } - attr { - name: "dense_keys" - type: "list(string)" - has_minimum: true - } - attr { - name: "sparse_types" - type: "list(type)" - has_minimum: true - allowed_values { - list { - type: DT_FLOAT - type: DT_INT64 - type: DT_STRING - } - } - } - attr { - name: "Tdense" - type: "list(type)" - has_minimum: true - allowed_values { - list { - type: DT_FLOAT - type: DT_INT64 - type: DT_STRING - } - } - } - attr { - name: "dense_shapes" - type: "list(shape)" - has_minimum: true - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } - attr { - name: "sloppy" - type: "bool" - default_value { - b: false - } - } -} op { name: "ParseSequenceExample" input_arg { @@ -40276,6 +40479,52 @@ op { } } } +op { + name: "PartitionedCall" + input_arg { + name: "args" + type_list_attr: "Tin" + } + output_arg { + name: "output" + type_list_attr: "Tout" + } + attr { + name: "Tin" + type: "list(type)" + has_minimum: true + } + attr { + name: "Tout" + type: "list(type)" + has_minimum: true + } + attr { + name: "f" + type: "func" + } + attr { + name: "config" + type: "string" + default_value { + s: "" + } + } + attr { + name: "config_proto" + type: "string" + default_value { + s: "" + } + } + attr { + name: "executor_type" + type: "string" + default_value { + s: "" + } + } +} op { name: "Placeholder" output_arg { @@ -40591,48 +40840,6 @@ op { minimum: 1 } } -op { - name: "PrependFromQueueAndPaddedBatchDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "batch_size" - type: DT_INT64 - } - input_arg { - name: "padded_shapes" - type: DT_INT64 - number_attr: "N" - } - input_arg { - name: "padding_values" - type_list_attr: "Toutput_types" - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "Toutput_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } - attr { - name: "N" - type: "int" - has_minimum: true - minimum: 1 - } -} op { name: "PreventGradient" input_arg { @@ -41460,20 +41667,228 @@ op { b: false } } - attr { - name: "input_min" - type: "float" - default_value { - f: 0 - } - } - attr { - name: "input_max" - type: "float" - default_value { - f: 0 - } - } + attr { + name: "input_min" + type: "float" + default_value { + f: 0 + } + } + attr { + name: "input_max" + type: "float" + default_value { + f: 0 + } + } + attr { + name: "T" + type: "type" + allowed_values { + list { + type: DT_BFLOAT16 + type: DT_HALF + type: DT_FLOAT + type: DT_DOUBLE + } + } + } + deprecation { + version: 22 + } +} +op { + name: "QuantizeAndDequantizeV2" + input_arg { + name: "input" + type_attr: "T" + } + input_arg { + name: "input_min" + type_attr: "T" + } + input_arg { + name: "input_max" + type_attr: "T" + } + output_arg { + name: "output" + type_attr: "T" + } + attr { + name: "signed_input" + type: "bool" + default_value { + b: true + } + } + attr { + name: "num_bits" + type: "int" + default_value { + i: 8 + } + } + attr { + name: "range_given" + type: "bool" + default_value { + b: false + } + } + attr { + name: "T" + type: "type" + allowed_values { + list { + type: DT_FLOAT + type: DT_DOUBLE + } + } + } +} +op { + name: "QuantizeAndDequantizeV2" + input_arg { + name: "input" + type_attr: "T" + } + input_arg { + name: "input_min" + type_attr: "T" + } + input_arg { + name: "input_max" + type_attr: "T" + } + output_arg { + name: "output" + type_attr: "T" + } + attr { + name: "signed_input" + type: "bool" + default_value { + b: true + } + } + attr { + name: "num_bits" + type: "int" + default_value { + i: 8 + } + } + attr { + name: "range_given" + type: "bool" + default_value { + b: false + } + } + attr { + name: "T" + type: "type" + allowed_values { + list { + type: DT_BFLOAT16 + type: DT_FLOAT + type: DT_DOUBLE + } + } + } +} +op { + name: "QuantizeAndDequantizeV2" + input_arg { + name: "input" + type_attr: "T" + } + input_arg { + name: "input_min" + type_attr: "T" + } + input_arg { + name: "input_max" + type_attr: "T" + } + output_arg { + name: "output" + type_attr: "T" + } + attr { + name: "signed_input" + type: "bool" + default_value { + b: true + } + } + attr { + name: "num_bits" + type: "int" + default_value { + i: 8 + } + } + attr { + name: "range_given" + type: "bool" + default_value { + b: false + } + } + attr { + name: "T" + type: "type" + allowed_values { + list { + type: DT_BFLOAT16 + type: DT_HALF + type: DT_FLOAT + type: DT_DOUBLE + } + } + } +} +op { + name: "QuantizeAndDequantizeV2" + input_arg { + name: "input" + type_attr: "T" + } + input_arg { + name: "input_min" + type_attr: "T" + } + input_arg { + name: "input_max" + type_attr: "T" + } + output_arg { + name: "output" + type_attr: "T" + } + attr { + name: "signed_input" + type: "bool" + default_value { + b: true + } + } + attr { + name: "num_bits" + type: "int" + default_value { + i: 8 + } + } + attr { + name: "range_given" + type: "bool" + default_value { + b: false + } + } attr { name: "T" type: "type" @@ -41486,159 +41901,16 @@ op { } } } - deprecation { - version: 22 - } -} -op { - name: "QuantizeAndDequantizeV2" - input_arg { - name: "input" - type_attr: "T" - } - input_arg { - name: "input_min" - type_attr: "T" - } - input_arg { - name: "input_max" - type_attr: "T" - } - output_arg { - name: "output" - type_attr: "T" - } - attr { - name: "signed_input" - type: "bool" - default_value { - b: true - } - } - attr { - name: "num_bits" - type: "int" - default_value { - i: 8 - } - } - attr { - name: "range_given" - type: "bool" - default_value { - b: false - } - } - attr { - name: "T" - type: "type" - allowed_values { - list { - type: DT_FLOAT - type: DT_DOUBLE - } - } - } -} -op { - name: "QuantizeAndDequantizeV2" - input_arg { - name: "input" - type_attr: "T" - } - input_arg { - name: "input_min" - type_attr: "T" - } - input_arg { - name: "input_max" - type_attr: "T" - } - output_arg { - name: "output" - type_attr: "T" - } - attr { - name: "signed_input" - type: "bool" - default_value { - b: true - } - } - attr { - name: "num_bits" - type: "int" - default_value { - i: 8 - } - } - attr { - name: "range_given" - type: "bool" - default_value { - b: false - } - } - attr { - name: "T" - type: "type" - allowed_values { - list { - type: DT_BFLOAT16 - type: DT_FLOAT - type: DT_DOUBLE - } - } - } -} -op { - name: "QuantizeAndDequantizeV2" - input_arg { - name: "input" - type_attr: "T" - } - input_arg { - name: "input_min" - type_attr: "T" - } - input_arg { - name: "input_max" - type_attr: "T" - } - output_arg { - name: "output" - type_attr: "T" - } - attr { - name: "signed_input" - type: "bool" - default_value { - b: true - } - } attr { - name: "num_bits" - type: "int" - default_value { - i: 8 - } - } - attr { - name: "range_given" - type: "bool" + name: "round_mode" + type: "string" default_value { - b: false + s: "HALF_TO_EVEN" } - } - attr { - name: "T" - type: "type" allowed_values { list { - type: DT_BFLOAT16 - type: DT_HALF - type: DT_FLOAT - type: DT_DOUBLE + s: "HALF_TO_EVEN" + s: "HALF_UP" } } } @@ -44844,34 +45116,6 @@ op { } is_stateful: true } -op { - name: "RandomDataset" - input_arg { - name: "seed" - type: DT_INT64 - } - input_arg { - name: "seed2" - type: DT_INT64 - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } - is_stateful: true -} op { name: "RandomGamma" input_arg { @@ -48760,21 +49004,89 @@ op { list { type: DT_FLOAT type: DT_DOUBLE - type: DT_INT64 + type: DT_INT64 + type: DT_INT32 + type: DT_UINT8 + type: DT_UINT16 + type: DT_INT16 + type: DT_INT8 + type: DT_COMPLEX64 + type: DT_COMPLEX128 + type: DT_QINT8 + type: DT_QUINT8 + type: DT_QINT32 + type: DT_HALF + type: DT_UINT32 + type: DT_UINT64 + type: DT_BFLOAT16 + } + } + } + attr { + name: "use_locking" + type: "bool" + default_value { + b: false + } + } + is_stateful: true +} +op { + name: "ResourceApplyAdagradDA" + input_arg { + name: "var" + type: DT_RESOURCE + } + input_arg { + name: "gradient_accumulator" + type: DT_RESOURCE + } + input_arg { + name: "gradient_squared_accumulator" + type: DT_RESOURCE + } + input_arg { + name: "grad" + type_attr: "T" + } + input_arg { + name: "lr" + type_attr: "T" + } + input_arg { + name: "l1" + type_attr: "T" + } + input_arg { + name: "l2" + type_attr: "T" + } + input_arg { + name: "global_step" + type: DT_INT64 + } + attr { + name: "T" + type: "type" + allowed_values { + list { + type: DT_FLOAT + type: DT_DOUBLE type: DT_INT32 type: DT_UINT8 - type: DT_UINT16 type: DT_INT16 type: DT_INT8 type: DT_COMPLEX64 - type: DT_COMPLEX128 + type: DT_INT64 type: DT_QINT8 type: DT_QUINT8 type: DT_QINT32 + type: DT_BFLOAT16 + type: DT_UINT16 + type: DT_COMPLEX128 type: DT_HALF type: DT_UINT32 type: DT_UINT64 - type: DT_BFLOAT16 } } } @@ -48788,21 +49100,25 @@ op { is_stateful: true } op { - name: "ResourceApplyAdagradDA" + name: "ResourceApplyAdam" input_arg { name: "var" type: DT_RESOURCE } input_arg { - name: "gradient_accumulator" + name: "m" type: DT_RESOURCE } input_arg { - name: "gradient_squared_accumulator" + name: "v" type: DT_RESOURCE } input_arg { - name: "grad" + name: "beta1_power" + type_attr: "T" + } + input_arg { + name: "beta2_power" type_attr: "T" } input_arg { @@ -48810,16 +49126,20 @@ op { type_attr: "T" } input_arg { - name: "l1" + name: "beta1" type_attr: "T" } input_arg { - name: "l2" + name: "beta2" type_attr: "T" } input_arg { - name: "global_step" - type: DT_INT64 + name: "epsilon" + type_attr: "T" + } + input_arg { + name: "grad" + type_attr: "T" } attr { name: "T" @@ -48828,21 +49148,18 @@ op { list { type: DT_FLOAT type: DT_DOUBLE + type: DT_INT64 type: DT_INT32 type: DT_UINT8 + type: DT_UINT16 type: DT_INT16 type: DT_INT8 type: DT_COMPLEX64 - type: DT_INT64 + type: DT_COMPLEX128 type: DT_QINT8 type: DT_QUINT8 type: DT_QINT32 - type: DT_BFLOAT16 - type: DT_UINT16 - type: DT_COMPLEX128 type: DT_HALF - type: DT_UINT32 - type: DT_UINT64 } } } @@ -48926,6 +49243,13 @@ op { b: false } } + attr { + name: "use_nesterov" + type: "bool" + default_value { + b: false + } + } is_stateful: true } op { @@ -48989,6 +49313,8 @@ op { type: DT_QUINT8 type: DT_QINT32 type: DT_HALF + type: DT_UINT32 + type: DT_UINT64 } } } @@ -49071,6 +49397,7 @@ op { type: DT_HALF type: DT_UINT32 type: DT_UINT64 + type: DT_BFLOAT16 } } } @@ -49139,21 +49466,21 @@ op { list { type: DT_FLOAT type: DT_DOUBLE - type: DT_INT64 type: DT_INT32 type: DT_UINT8 - type: DT_UINT16 type: DT_INT16 type: DT_INT8 type: DT_COMPLEX64 - type: DT_COMPLEX128 + type: DT_INT64 type: DT_QINT8 type: DT_QUINT8 type: DT_QINT32 + type: DT_BFLOAT16 + type: DT_UINT16 + type: DT_COMPLEX128 type: DT_HALF type: DT_UINT32 type: DT_UINT64 - type: DT_BFLOAT16 } } } @@ -49174,7 +49501,7 @@ op { is_stateful: true } op { - name: "ResourceApplyAdam" + name: "ResourceApplyAdamWithAmsgrad" input_arg { name: "var" type: DT_RESOURCE @@ -49187,6 +49514,10 @@ op { name: "v" type: DT_RESOURCE } + input_arg { + name: "vhat" + type: DT_RESOURCE + } input_arg { name: "beta1_power" type_attr: "T" @@ -49247,8 +49578,64 @@ op { b: false } } + is_stateful: true +} +op { + name: "ResourceApplyAddSign" + input_arg { + name: "var" + type: DT_RESOURCE + } + input_arg { + name: "m" + type: DT_RESOURCE + } + input_arg { + name: "lr" + type_attr: "T" + } + input_arg { + name: "alpha" + type_attr: "T" + } + input_arg { + name: "sign_decay" + type_attr: "T" + } + input_arg { + name: "beta" + type_attr: "T" + } + input_arg { + name: "grad" + type_attr: "T" + } attr { - name: "use_nesterov" + name: "T" + type: "type" + allowed_values { + list { + type: DT_FLOAT + type: DT_DOUBLE + type: DT_INT64 + type: DT_INT32 + type: DT_UINT8 + type: DT_UINT16 + type: DT_INT16 + type: DT_INT8 + type: DT_COMPLEX64 + type: DT_COMPLEX128 + type: DT_QINT8 + type: DT_QUINT8 + type: DT_QINT32 + type: DT_HALF + type: DT_UINT32 + type: DT_UINT64 + } + } + } + attr { + name: "use_locking" type: "bool" default_value { b: false @@ -49305,8 +49692,142 @@ op { type: DT_QUINT8 type: DT_QINT32 type: DT_HALF - type: DT_UINT32 - type: DT_UINT64 + type: DT_UINT32 + type: DT_UINT64 + type: DT_BFLOAT16 + } + } + } + attr { + name: "use_locking" + type: "bool" + default_value { + b: false + } + } + is_stateful: true +} +op { + name: "ResourceApplyAddSign" + input_arg { + name: "var" + type: DT_RESOURCE + } + input_arg { + name: "m" + type: DT_RESOURCE + } + input_arg { + name: "lr" + type_attr: "T" + } + input_arg { + name: "alpha" + type_attr: "T" + } + input_arg { + name: "sign_decay" + type_attr: "T" + } + input_arg { + name: "beta" + type_attr: "T" + } + input_arg { + name: "grad" + type_attr: "T" + } + attr { + name: "T" + type: "type" + allowed_values { + list { + type: DT_FLOAT + type: DT_DOUBLE + type: DT_INT32 + type: DT_UINT8 + type: DT_INT16 + type: DT_INT8 + type: DT_COMPLEX64 + type: DT_INT64 + type: DT_QINT8 + type: DT_QUINT8 + type: DT_QINT32 + type: DT_BFLOAT16 + type: DT_UINT16 + type: DT_COMPLEX128 + type: DT_HALF + type: DT_UINT32 + type: DT_UINT64 + } + } + } + attr { + name: "use_locking" + type: "bool" + default_value { + b: false + } + } + is_stateful: true +} +op { + name: "ResourceApplyCenteredRMSProp" + input_arg { + name: "var" + type: DT_RESOURCE + } + input_arg { + name: "mg" + type: DT_RESOURCE + } + input_arg { + name: "ms" + type: DT_RESOURCE + } + input_arg { + name: "mom" + type: DT_RESOURCE + } + input_arg { + name: "lr" + type_attr: "T" + } + input_arg { + name: "rho" + type_attr: "T" + } + input_arg { + name: "momentum" + type_attr: "T" + } + input_arg { + name: "epsilon" + type_attr: "T" + } + input_arg { + name: "grad" + type_attr: "T" + } + attr { + name: "T" + type: "type" + allowed_values { + list { + type: DT_FLOAT + type: DT_DOUBLE + type: DT_INT64 + type: DT_INT32 + type: DT_UINT8 + type: DT_UINT16 + type: DT_INT16 + type: DT_INT8 + type: DT_COMPLEX64 + type: DT_COMPLEX128 + type: DT_QINT8 + type: DT_QUINT8 + type: DT_QINT32 + type: DT_HALF } } } @@ -49320,13 +49841,21 @@ op { is_stateful: true } op { - name: "ResourceApplyAddSign" + name: "ResourceApplyCenteredRMSProp" input_arg { name: "var" type: DT_RESOURCE } input_arg { - name: "m" + name: "mg" + type: DT_RESOURCE + } + input_arg { + name: "ms" + type: DT_RESOURCE + } + input_arg { + name: "mom" type: DT_RESOURCE } input_arg { @@ -49334,15 +49863,15 @@ op { type_attr: "T" } input_arg { - name: "alpha" + name: "rho" type_attr: "T" } input_arg { - name: "sign_decay" + name: "momentum" type_attr: "T" } input_arg { - name: "beta" + name: "epsilon" type_attr: "T" } input_arg { @@ -49370,7 +49899,6 @@ op { type: DT_HALF type: DT_UINT32 type: DT_UINT64 - type: DT_BFLOAT16 } } } @@ -49384,13 +49912,21 @@ op { is_stateful: true } op { - name: "ResourceApplyAddSign" + name: "ResourceApplyCenteredRMSProp" input_arg { name: "var" type: DT_RESOURCE } input_arg { - name: "m" + name: "mg" + type: DT_RESOURCE + } + input_arg { + name: "ms" + type: DT_RESOURCE + } + input_arg { + name: "mom" type: DT_RESOURCE } input_arg { @@ -49398,15 +49934,15 @@ op { type_attr: "T" } input_arg { - name: "alpha" + name: "rho" type_attr: "T" } input_arg { - name: "sign_decay" + name: "momentum" type_attr: "T" } input_arg { - name: "beta" + name: "epsilon" type_attr: "T" } input_arg { @@ -49420,21 +49956,21 @@ op { list { type: DT_FLOAT type: DT_DOUBLE + type: DT_INT64 type: DT_INT32 type: DT_UINT8 + type: DT_UINT16 type: DT_INT16 type: DT_INT8 type: DT_COMPLEX64 - type: DT_INT64 + type: DT_COMPLEX128 type: DT_QINT8 type: DT_QUINT8 type: DT_QINT32 - type: DT_BFLOAT16 - type: DT_UINT16 - type: DT_COMPLEX128 type: DT_HALF type: DT_UINT32 type: DT_UINT64 + type: DT_BFLOAT16 } } } @@ -49492,18 +50028,21 @@ op { list { type: DT_FLOAT type: DT_DOUBLE - type: DT_INT64 type: DT_INT32 type: DT_UINT8 - type: DT_UINT16 type: DT_INT16 type: DT_INT8 type: DT_COMPLEX64 - type: DT_COMPLEX128 + type: DT_INT64 type: DT_QINT8 type: DT_QUINT8 type: DT_QINT32 + type: DT_BFLOAT16 + type: DT_UINT16 + type: DT_COMPLEX128 type: DT_HALF + type: DT_UINT32 + type: DT_UINT64 } } } @@ -49517,41 +50056,37 @@ op { is_stateful: true } op { - name: "ResourceApplyCenteredRMSProp" + name: "ResourceApplyFtrl" input_arg { name: "var" type: DT_RESOURCE } input_arg { - name: "mg" - type: DT_RESOURCE - } - input_arg { - name: "ms" + name: "accum" type: DT_RESOURCE } input_arg { - name: "mom" + name: "linear" type: DT_RESOURCE } input_arg { - name: "lr" + name: "grad" type_attr: "T" } input_arg { - name: "rho" + name: "lr" type_attr: "T" } input_arg { - name: "momentum" + name: "l1" type_attr: "T" } input_arg { - name: "epsilon" + name: "l2" type_attr: "T" } input_arg { - name: "grad" + name: "lr_power" type_attr: "T" } attr { @@ -49573,8 +50108,6 @@ op { type: DT_QUINT8 type: DT_QINT32 type: DT_HALF - type: DT_UINT32 - type: DT_UINT64 } } } @@ -49588,41 +50121,37 @@ op { is_stateful: true } op { - name: "ResourceApplyCenteredRMSProp" + name: "ResourceApplyFtrl" input_arg { name: "var" type: DT_RESOURCE } input_arg { - name: "mg" - type: DT_RESOURCE - } - input_arg { - name: "ms" + name: "accum" type: DT_RESOURCE } input_arg { - name: "mom" + name: "linear" type: DT_RESOURCE } input_arg { - name: "lr" + name: "grad" type_attr: "T" } input_arg { - name: "rho" + name: "lr" type_attr: "T" } input_arg { - name: "momentum" + name: "l1" type_attr: "T" } input_arg { - name: "epsilon" + name: "l2" type_attr: "T" } input_arg { - name: "grad" + name: "lr_power" type_attr: "T" } attr { @@ -49646,7 +50175,6 @@ op { type: DT_HALF type: DT_UINT32 type: DT_UINT64 - type: DT_BFLOAT16 } } } @@ -49660,41 +50188,37 @@ op { is_stateful: true } op { - name: "ResourceApplyCenteredRMSProp" + name: "ResourceApplyFtrl" input_arg { name: "var" type: DT_RESOURCE } input_arg { - name: "mg" - type: DT_RESOURCE - } - input_arg { - name: "ms" + name: "accum" type: DT_RESOURCE } input_arg { - name: "mom" + name: "linear" type: DT_RESOURCE } input_arg { - name: "lr" + name: "grad" type_attr: "T" } input_arg { - name: "rho" + name: "lr" type_attr: "T" } input_arg { - name: "momentum" + name: "l1" type_attr: "T" } input_arg { - name: "epsilon" + name: "l2" type_attr: "T" } input_arg { - name: "grad" + name: "lr_power" type_attr: "T" } attr { @@ -49704,21 +50228,21 @@ op { list { type: DT_FLOAT type: DT_DOUBLE + type: DT_INT64 type: DT_INT32 type: DT_UINT8 + type: DT_UINT16 type: DT_INT16 type: DT_INT8 type: DT_COMPLEX64 - type: DT_INT64 + type: DT_COMPLEX128 type: DT_QINT8 type: DT_QUINT8 type: DT_QINT32 - type: DT_BFLOAT16 - type: DT_UINT16 - type: DT_COMPLEX128 type: DT_HALF type: DT_UINT32 type: DT_UINT64 + type: DT_BFLOAT16 } } } @@ -49772,18 +50296,21 @@ op { list { type: DT_FLOAT type: DT_DOUBLE - type: DT_INT64 type: DT_INT32 type: DT_UINT8 - type: DT_UINT16 type: DT_INT16 type: DT_INT8 type: DT_COMPLEX64 - type: DT_COMPLEX128 + type: DT_INT64 type: DT_QINT8 type: DT_QUINT8 type: DT_QINT32 + type: DT_BFLOAT16 + type: DT_UINT16 + type: DT_COMPLEX128 type: DT_HALF + type: DT_UINT32 + type: DT_UINT64 } } } @@ -49797,7 +50324,7 @@ op { is_stateful: true } op { - name: "ResourceApplyFtrl" + name: "ResourceApplyFtrlV2" input_arg { name: "var" type: DT_RESOURCE @@ -49826,6 +50353,10 @@ op { name: "l2" type_attr: "T" } + input_arg { + name: "l2_shrinkage" + type_attr: "T" + } input_arg { name: "lr_power" type_attr: "T" @@ -49849,8 +50380,6 @@ op { type: DT_QUINT8 type: DT_QINT32 type: DT_HALF - type: DT_UINT32 - type: DT_UINT64 } } } @@ -49864,7 +50393,7 @@ op { is_stateful: true } op { - name: "ResourceApplyFtrl" + name: "ResourceApplyFtrlV2" input_arg { name: "var" type: DT_RESOURCE @@ -49893,6 +50422,10 @@ op { name: "l2" type_attr: "T" } + input_arg { + name: "l2_shrinkage" + type_attr: "T" + } input_arg { name: "lr_power" type_attr: "T" @@ -49918,7 +50451,6 @@ op { type: DT_HALF type: DT_UINT32 type: DT_UINT64 - type: DT_BFLOAT16 } } } @@ -49932,7 +50464,7 @@ op { is_stateful: true } op { - name: "ResourceApplyFtrl" + name: "ResourceApplyFtrlV2" input_arg { name: "var" type: DT_RESOURCE @@ -49961,6 +50493,10 @@ op { name: "l2" type_attr: "T" } + input_arg { + name: "l2_shrinkage" + type_attr: "T" + } input_arg { name: "lr_power" type_attr: "T" @@ -49972,21 +50508,21 @@ op { list { type: DT_FLOAT type: DT_DOUBLE + type: DT_INT64 type: DT_INT32 type: DT_UINT8 + type: DT_UINT16 type: DT_INT16 type: DT_INT8 type: DT_COMPLEX64 - type: DT_INT64 + type: DT_COMPLEX128 type: DT_QINT8 type: DT_QUINT8 type: DT_QINT32 - type: DT_BFLOAT16 - type: DT_UINT16 - type: DT_COMPLEX128 type: DT_HALF type: DT_UINT32 type: DT_UINT64 + type: DT_BFLOAT16 } } } @@ -50044,18 +50580,21 @@ op { list { type: DT_FLOAT type: DT_DOUBLE - type: DT_INT64 type: DT_INT32 type: DT_UINT8 - type: DT_UINT16 type: DT_INT16 type: DT_INT8 type: DT_COMPLEX64 - type: DT_COMPLEX128 + type: DT_INT64 type: DT_QINT8 type: DT_QUINT8 type: DT_QINT32 + type: DT_BFLOAT16 + type: DT_UINT16 + type: DT_COMPLEX128 type: DT_HALF + type: DT_UINT32 + type: DT_UINT64 } } } @@ -50069,41 +50608,62 @@ op { is_stateful: true } op { - name: "ResourceApplyFtrlV2" + name: "ResourceApplyGradientDescent" input_arg { name: "var" type: DT_RESOURCE } input_arg { - name: "accum" - type: DT_RESOURCE - } - input_arg { - name: "linear" - type: DT_RESOURCE - } - input_arg { - name: "grad" + name: "alpha" type_attr: "T" } input_arg { - name: "lr" + name: "delta" type_attr: "T" } - input_arg { - name: "l1" - type_attr: "T" + attr { + name: "T" + type: "type" + allowed_values { + list { + type: DT_FLOAT + type: DT_DOUBLE + type: DT_INT64 + type: DT_INT32 + type: DT_UINT8 + type: DT_UINT16 + type: DT_INT16 + type: DT_INT8 + type: DT_COMPLEX64 + type: DT_COMPLEX128 + type: DT_QINT8 + type: DT_QUINT8 + type: DT_QINT32 + type: DT_HALF + } + } } + attr { + name: "use_locking" + type: "bool" + default_value { + b: false + } + } + is_stateful: true +} +op { + name: "ResourceApplyGradientDescent" input_arg { - name: "l2" - type_attr: "T" + name: "var" + type: DT_RESOURCE } input_arg { - name: "l2_shrinkage" + name: "alpha" type_attr: "T" } input_arg { - name: "lr_power" + name: "delta" type_attr: "T" } attr { @@ -50140,41 +50700,17 @@ op { is_stateful: true } op { - name: "ResourceApplyFtrlV2" + name: "ResourceApplyGradientDescent" input_arg { name: "var" type: DT_RESOURCE } input_arg { - name: "accum" - type: DT_RESOURCE - } - input_arg { - name: "linear" - type: DT_RESOURCE - } - input_arg { - name: "grad" - type_attr: "T" - } - input_arg { - name: "lr" - type_attr: "T" - } - input_arg { - name: "l1" - type_attr: "T" - } - input_arg { - name: "l2" - type_attr: "T" - } - input_arg { - name: "l2_shrinkage" + name: "alpha" type_attr: "T" } input_arg { - name: "lr_power" + name: "delta" type_attr: "T" } attr { @@ -50212,41 +50748,17 @@ op { is_stateful: true } op { - name: "ResourceApplyFtrlV2" + name: "ResourceApplyGradientDescent" input_arg { name: "var" type: DT_RESOURCE } input_arg { - name: "accum" - type: DT_RESOURCE - } - input_arg { - name: "linear" - type: DT_RESOURCE - } - input_arg { - name: "grad" - type_attr: "T" - } - input_arg { - name: "lr" - type_attr: "T" - } - input_arg { - name: "l1" - type_attr: "T" - } - input_arg { - name: "l2" - type_attr: "T" - } - input_arg { - name: "l2_shrinkage" + name: "alpha" type_attr: "T" } input_arg { - name: "lr_power" + name: "delta" type_attr: "T" } attr { @@ -50284,62 +50796,25 @@ op { is_stateful: true } op { - name: "ResourceApplyGradientDescent" + name: "ResourceApplyKerasMomentum" input_arg { name: "var" type: DT_RESOURCE } input_arg { - name: "alpha" - type_attr: "T" + name: "accum" + type: DT_RESOURCE } input_arg { - name: "delta" + name: "lr" type_attr: "T" } - attr { - name: "T" - type: "type" - allowed_values { - list { - type: DT_FLOAT - type: DT_DOUBLE - type: DT_INT64 - type: DT_INT32 - type: DT_UINT8 - type: DT_UINT16 - type: DT_INT16 - type: DT_INT8 - type: DT_COMPLEX64 - type: DT_COMPLEX128 - type: DT_QINT8 - type: DT_QUINT8 - type: DT_QINT32 - type: DT_HALF - } - } - } - attr { - name: "use_locking" - type: "bool" - default_value { - b: false - } - } - is_stateful: true -} -op { - name: "ResourceApplyGradientDescent" - input_arg { - name: "var" - type: DT_RESOURCE - } input_arg { - name: "alpha" + name: "grad" type_attr: "T" } input_arg { - name: "delta" + name: "momentum" type_attr: "T" } attr { @@ -50349,68 +50824,21 @@ op { list { type: DT_FLOAT type: DT_DOUBLE - type: DT_INT64 type: DT_INT32 type: DT_UINT8 - type: DT_UINT16 type: DT_INT16 type: DT_INT8 type: DT_COMPLEX64 - type: DT_COMPLEX128 + type: DT_INT64 type: DT_QINT8 type: DT_QUINT8 type: DT_QINT32 - type: DT_HALF - type: DT_UINT32 - type: DT_UINT64 - } - } - } - attr { - name: "use_locking" - type: "bool" - default_value { - b: false - } - } - is_stateful: true -} -op { - name: "ResourceApplyGradientDescent" - input_arg { - name: "var" - type: DT_RESOURCE - } - input_arg { - name: "alpha" - type_attr: "T" - } - input_arg { - name: "delta" - type_attr: "T" - } - attr { - name: "T" - type: "type" - allowed_values { - list { - type: DT_FLOAT - type: DT_DOUBLE - type: DT_INT64 - type: DT_INT32 - type: DT_UINT8 + type: DT_BFLOAT16 type: DT_UINT16 - type: DT_INT16 - type: DT_INT8 - type: DT_COMPLEX64 type: DT_COMPLEX128 - type: DT_QINT8 - type: DT_QUINT8 - type: DT_QINT32 type: DT_HALF type: DT_UINT32 type: DT_UINT64 - type: DT_BFLOAT16 } } } @@ -50421,49 +50849,8 @@ op { b: false } } - is_stateful: true -} -op { - name: "ResourceApplyGradientDescent" - input_arg { - name: "var" - type: DT_RESOURCE - } - input_arg { - name: "alpha" - type_attr: "T" - } - input_arg { - name: "delta" - type_attr: "T" - } attr { - name: "T" - type: "type" - allowed_values { - list { - type: DT_FLOAT - type: DT_DOUBLE - type: DT_INT32 - type: DT_UINT8 - type: DT_INT16 - type: DT_INT8 - type: DT_COMPLEX64 - type: DT_INT64 - type: DT_QINT8 - type: DT_QUINT8 - type: DT_QINT32 - type: DT_BFLOAT16 - type: DT_UINT16 - type: DT_COMPLEX128 - type: DT_HALF - type: DT_UINT32 - type: DT_UINT64 - } - } - } - attr { - name: "use_locking" + name: "use_nesterov" type: "bool" default_value { b: false @@ -54254,21 +54641,107 @@ op { list { type: DT_FLOAT type: DT_DOUBLE - type: DT_INT64 + type: DT_INT64 + type: DT_INT32 + type: DT_UINT8 + type: DT_UINT16 + type: DT_INT16 + type: DT_INT8 + type: DT_COMPLEX64 + type: DT_COMPLEX128 + type: DT_QINT8 + type: DT_QUINT8 + type: DT_QINT32 + type: DT_HALF + type: DT_UINT32 + type: DT_UINT64 + type: DT_BFLOAT16 + } + } + } + attr { + name: "Tindices" + type: "type" + allowed_values { + list { + type: DT_INT32 + type: DT_INT64 + } + } + } + attr { + name: "use_locking" + type: "bool" + default_value { + b: false + } + } + is_stateful: true +} +op { + name: "ResourceSparseApplyFtrlV2" + input_arg { + name: "var" + type: DT_RESOURCE + } + input_arg { + name: "accum" + type: DT_RESOURCE + } + input_arg { + name: "linear" + type: DT_RESOURCE + } + input_arg { + name: "grad" + type_attr: "T" + } + input_arg { + name: "indices" + type_attr: "Tindices" + } + input_arg { + name: "lr" + type_attr: "T" + } + input_arg { + name: "l1" + type_attr: "T" + } + input_arg { + name: "l2" + type_attr: "T" + } + input_arg { + name: "l2_shrinkage" + type_attr: "T" + } + input_arg { + name: "lr_power" + type_attr: "T" + } + attr { + name: "T" + type: "type" + allowed_values { + list { + type: DT_FLOAT + type: DT_DOUBLE type: DT_INT32 type: DT_UINT8 - type: DT_UINT16 type: DT_INT16 type: DT_INT8 type: DT_COMPLEX64 - type: DT_COMPLEX128 + type: DT_INT64 type: DT_QINT8 type: DT_QUINT8 type: DT_QINT32 + type: DT_BFLOAT16 + type: DT_UINT16 + type: DT_COMPLEX128 type: DT_HALF type: DT_UINT32 type: DT_UINT64 - type: DT_BFLOAT16 } } } @@ -54292,7 +54765,7 @@ op { is_stateful: true } op { - name: "ResourceSparseApplyFtrlV2" + name: "ResourceSparseApplyKerasMomentum" input_arg { name: "var" type: DT_RESOURCE @@ -54302,8 +54775,8 @@ op { type: DT_RESOURCE } input_arg { - name: "linear" - type: DT_RESOURCE + name: "lr" + type_attr: "T" } input_arg { name: "grad" @@ -54314,23 +54787,7 @@ op { type_attr: "Tindices" } input_arg { - name: "lr" - type_attr: "T" - } - input_arg { - name: "l1" - type_attr: "T" - } - input_arg { - name: "l2" - type_attr: "T" - } - input_arg { - name: "l2_shrinkage" - type_attr: "T" - } - input_arg { - name: "lr_power" + name: "momentum" type_attr: "T" } attr { @@ -54375,6 +54832,13 @@ op { b: false } } + attr { + name: "use_nesterov" + type: "bool" + default_value { + b: false + } + } is_stateful: true } op { @@ -57111,52 +57575,6 @@ op { } } } -op { - name: "ScanDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "initial_state" - type_list_attr: "Tstate" - } - input_arg { - name: "other_arguments" - type_list_attr: "Targuments" - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "f" - type: "func" - } - attr { - name: "Tstate" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "Targuments" - type: "list(type)" - has_minimum: true - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } -} op { name: "ScatterAdd" input_arg { @@ -60889,42 +61307,6 @@ op { } } } -op { - name: "SetStatsAggregatorDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "stats_aggregator" - type: DT_RESOURCE - } - input_arg { - name: "tag" - type: DT_STRING - } - input_arg { - name: "counter_prefix" - type: DT_STRING - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } - is_stateful: true -} op { name: "Shape" input_arg { @@ -61771,41 +62153,6 @@ op { } } } -op { - name: "SlideDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "window_size" - type: DT_INT64 - } - input_arg { - name: "window_shift" - type: DT_INT64 - } - input_arg { - name: "window_stride" - type: DT_INT64 - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } -} op { name: "Snapshot" input_arg { @@ -70964,38 +71311,6 @@ op { } } } -op { - name: "SqlDataset" - input_arg { - name: "driver_name" - type: DT_STRING - } - input_arg { - name: "data_source_name" - type: DT_STRING - } - input_arg { - name: "query" - type: DT_STRING - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } - is_stateful: true -} op { name: "Sqrt" input_arg { @@ -71827,6 +72142,53 @@ op { } is_stateful: true } +op { + name: "StatefulPartitionedCall" + input_arg { + name: "args" + type_list_attr: "Tin" + } + output_arg { + name: "output" + type_list_attr: "Tout" + } + attr { + name: "Tin" + type: "list(type)" + has_minimum: true + } + attr { + name: "Tout" + type: "list(type)" + has_minimum: true + } + attr { + name: "f" + type: "func" + } + attr { + name: "config" + type: "string" + default_value { + s: "" + } + } + attr { + name: "config_proto" + type: "string" + default_value { + s: "" + } + } + attr { + name: "executor_type" + type: "string" + default_value { + s: "" + } + } + is_stateful: true +} op { name: "StatelessIf" input_arg { @@ -72509,40 +72871,6 @@ op { } } } -op { - name: "StatsAggregatorHandle" - output_arg { - name: "handle" - type: DT_RESOURCE - } - attr { - name: "container" - type: "string" - default_value { - s: "" - } - } - attr { - name: "shared_name" - type: "string" - default_value { - s: "" - } - } - is_stateful: true -} -op { - name: "StatsAggregatorSummary" - input_arg { - name: "iterator" - type: DT_RESOURCE - } - output_arg { - name: "summary" - type: DT_STRING - } - is_stateful: true -} op { name: "StopGradient" input_arg { @@ -75378,6 +75706,127 @@ op { } is_stateful: true } +op { + name: "TensorForestCreateTreeVariable" + input_arg { + name: "tree_handle" + type: DT_RESOURCE + } + input_arg { + name: "tree_config" + type: DT_STRING + } + is_stateful: true +} +op { + name: "TensorForestTreeDeserialize" + input_arg { + name: "tree_handle" + type: DT_RESOURCE + } + input_arg { + name: "tree_config" + type: DT_STRING + } + is_stateful: true +} +op { + name: "TensorForestTreeIsInitializedOp" + input_arg { + name: "tree_handle" + type: DT_RESOURCE + } + output_arg { + name: "is_initialized" + type: DT_BOOL + } + is_stateful: true +} +op { + name: "TensorForestTreePredict" + input_arg { + name: "tree_handle" + type: DT_RESOURCE + } + input_arg { + name: "dense_features" + type: DT_FLOAT + } + output_arg { + name: "logits" + type: DT_FLOAT + } + attr { + name: "logits_dimension" + type: "int" + } + is_stateful: true +} +op { + name: "TensorForestTreeResourceHandleOp" + output_arg { + name: "resource" + type: DT_RESOURCE + } + attr { + name: "container" + type: "string" + default_value { + s: "" + } + } + attr { + name: "shared_name" + type: "string" + default_value { + s: "" + } + } + is_stateful: true +} +op { + name: "TensorForestTreeSerialize" + input_arg { + name: "tree_handle" + type: DT_RESOURCE + } + output_arg { + name: "tree_config" + type: DT_STRING + } + is_stateful: true +} +op { + name: "TensorForestTreeSize" + input_arg { + name: "tree_handle" + type: DT_RESOURCE + } + output_arg { + name: "tree_size" + type: DT_INT32 + } + is_stateful: true +} +op { + name: "TensorListConcat" + input_arg { + name: "input_handle" + type: DT_VARIANT + } + output_arg { + name: "tensor" + type_attr: "element_dtype" + } + output_arg { + name: "lengths" + type: DT_INT64 + } + attr { + name: "element_dtype" + type: "type" + } +} op { name: "TensorListConcatLists" input_arg { @@ -75448,26 +75897,175 @@ op { } } op { - name: "TensorListGather" - input_arg { - name: "input_handle" - type: DT_VARIANT - } - input_arg { - name: "indices" - type: DT_INT32 - } - output_arg { - name: "values" - type_attr: "element_dtype" - } - attr { - name: "element_dtype" - type: "type" - } -} -op { - name: "TensorListGetItem" + name: "TensorListGather" + input_arg { + name: "input_handle" + type: DT_VARIANT + } + input_arg { + name: "indices" + type: DT_INT32 + } + output_arg { + name: "values" + type_attr: "element_dtype" + } + attr { + name: "element_dtype" + type: "type" + } +} +op { + name: "TensorListGetItem" + input_arg { + name: "input_handle" + type: DT_VARIANT + } + input_arg { + name: "index" + type: DT_INT32 + } + output_arg { + name: "item" + type_attr: "element_dtype" + } + attr { + name: "element_dtype" + type: "type" + } +} +op { + name: "TensorListLength" + input_arg { + name: "input_handle" + type: DT_VARIANT + } + output_arg { + name: "length" + type: DT_INT32 + } +} +op { + name: "TensorListPopBack" + input_arg { + name: "input_handle" + type: DT_VARIANT + } + output_arg { + name: "output_handle" + type: DT_VARIANT + } + output_arg { + name: "tensor" + type_attr: "element_dtype" + } + attr { + name: "element_dtype" + type: "type" + } +} +op { + name: "TensorListPushBack" + input_arg { + name: "input_handle" + type: DT_VARIANT + } + input_arg { + name: "tensor" + type_attr: "element_dtype" + } + output_arg { + name: "output_handle" + type: DT_VARIANT + } + attr { + name: "element_dtype" + type: "type" + } +} +op { + name: "TensorListPushBackBatch" + input_arg { + name: "input_handles" + type: DT_VARIANT + } + input_arg { + name: "tensor" + type_attr: "element_dtype" + } + output_arg { + name: "output_handles" + type: DT_VARIANT + } + attr { + name: "element_dtype" + type: "type" + } +} +op { + name: "TensorListReserve" + input_arg { + name: "element_shape" + type_attr: "shape_type" + } + input_arg { + name: "num_elements" + type: DT_INT32 + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "element_dtype" + type: "type" + } + attr { + name: "shape_type" + type: "type" + allowed_values { + list { + type: DT_INT32 + type: DT_INT64 + } + } + } +} +op { + name: "TensorListScatter" + input_arg { + name: "tensor" + type_attr: "element_dtype" + } + input_arg { + name: "indices" + type: DT_INT32 + } + input_arg { + name: "element_shape" + type_attr: "shape_type" + } + output_arg { + name: "output_handle" + type: DT_VARIANT + } + attr { + name: "element_dtype" + type: "type" + } + attr { + name: "shape_type" + type: "type" + allowed_values { + list { + type: DT_INT32 + type: DT_INT64 + } + } + } +} +op { + name: "TensorListSetItem" input_arg { name: "input_handle" type: DT_VARIANT @@ -75476,103 +76074,98 @@ op { name: "index" type: DT_INT32 } - output_arg { + input_arg { name: "item" type_attr: "element_dtype" } + output_arg { + name: "output_handle" + type: DT_VARIANT + } attr { name: "element_dtype" type: "type" } } op { - name: "TensorListLength" + name: "TensorListSplit" input_arg { - name: "input_handle" - type: DT_VARIANT + name: "tensor" + type_attr: "element_dtype" } - output_arg { - name: "length" - type: DT_INT32 + input_arg { + name: "element_shape" + type_attr: "shape_type" } -} -op { - name: "TensorListPopBack" input_arg { - name: "input_handle" - type: DT_VARIANT + name: "lengths" + type: DT_INT64 } output_arg { name: "output_handle" type: DT_VARIANT } - output_arg { - name: "tensor" - type_attr: "element_dtype" - } attr { name: "element_dtype" type: "type" } + attr { + name: "shape_type" + type: "type" + allowed_values { + list { + type: DT_INT32 + type: DT_INT64 + } + } + } } op { - name: "TensorListPushBack" + name: "TensorListStack" input_arg { name: "input_handle" type: DT_VARIANT } - input_arg { + output_arg { name: "tensor" type_attr: "element_dtype" } - output_arg { - name: "output_handle" - type: DT_VARIANT - } attr { name: "element_dtype" type: "type" } + attr { + name: "num_elements" + type: "int" + default_value { + i: -1 + } + } } op { - name: "TensorListPushBackBatch" - input_arg { - name: "input_handles" - type: DT_VARIANT - } + name: "TensorScatterAdd" input_arg { name: "tensor" - type_attr: "element_dtype" - } - output_arg { - name: "output_handles" - type: DT_VARIANT - } - attr { - name: "element_dtype" - type: "type" + type_attr: "T" } -} -op { - name: "TensorListReserve" input_arg { - name: "element_shape" - type_attr: "shape_type" + name: "indices" + type_attr: "Tindices" } input_arg { - name: "num_elements" - type: DT_INT32 + name: "updates" + type_attr: "T" } output_arg { - name: "handle" - type: DT_VARIANT + name: "output" + type_attr: "T" } attr { - name: "element_dtype" + name: "T" type: "type" } attr { - name: "shape_type" + name: "Tindices" type: "type" allowed_values { list { @@ -75583,29 +76176,29 @@ op { } } op { - name: "TensorListScatter" + name: "TensorScatterSub" input_arg { name: "tensor" - type_attr: "element_dtype" + type_attr: "T" } input_arg { name: "indices" - type: DT_INT32 + type_attr: "Tindices" } input_arg { - name: "element_shape" - type_attr: "shape_type" + name: "updates" + type_attr: "T" } output_arg { - name: "output_handle" - type: DT_VARIANT + name: "output" + type_attr: "T" } attr { - name: "element_dtype" + name: "T" type: "type" } attr { - name: "shape_type" + name: "Tindices" type: "type" allowed_values { list { @@ -75616,47 +76209,35 @@ op { } } op { - name: "TensorListSetItem" + name: "TensorScatterUpdate" input_arg { - name: "input_handle" - type: DT_VARIANT + name: "tensor" + type_attr: "T" } input_arg { - name: "index" - type: DT_INT32 + name: "indices" + type_attr: "Tindices" } input_arg { - name: "item" - type_attr: "element_dtype" + name: "updates" + type_attr: "T" } output_arg { - name: "output_handle" - type: DT_VARIANT + name: "output" + type_attr: "T" } attr { - name: "element_dtype" + name: "T" type: "type" } -} -op { - name: "TensorListStack" - input_arg { - name: "input_handle" - type: DT_VARIANT - } - output_arg { - name: "tensor" - type_attr: "element_dtype" - } attr { - name: "element_dtype" + name: "Tindices" type: "type" - } - attr { - name: "num_elements" - type: "int" - default_value { - i: -1 + allowed_values { + list { + type: DT_INT32 + type: DT_INT64 + } } } } @@ -76810,29 +77391,6 @@ op { type: "type" } } -op { - name: "UnbatchDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } -} op { name: "UnbatchGrad" input_arg { @@ -76874,6 +77432,104 @@ op { type: "type" } } +op { + name: "UnicodeDecodeWithOffsets" + input_arg { + name: "input" + type: DT_STRING + } + output_arg { + name: "row_splits" + type: DT_INT64 + } + output_arg { + name: "char_values" + type: DT_INT32 + } + output_arg { + name: "char_to_byte_starts" + type: DT_INT64 + } + attr { + name: "input_encoding" + type: "string" + } + attr { + name: "errors" + type: "string" + default_value { + s: "replace" + } + allowed_values { + list { + s: "strict" + s: "replace" + s: "ignore" + } + } + } + attr { + name: "replacement_char" + type: "int" + default_value { + i: 65533 + } + } + attr { + name: "replace_control_characters" + type: "bool" + default_value { + b: false + } + } +} +op { + name: "UnicodeEncode" + input_arg { + name: "input_values" + type: DT_INT32 + } + input_arg { + name: "input_splits" + type: DT_INT64 + } + output_arg { + name: "output" + type: DT_STRING + } + attr { + name: "errors" + type: "string" + default_value { + s: "replace" + } + allowed_values { + list { + s: "ignore" + s: "replace" + s: "strict" + } + } + } + attr { + name: "output_encoding" + type: "string" + allowed_values { + list { + s: "UTF-8" + s: "UTF-16-BE" + s: "UTF-32-BE" + } + } + } + attr { + name: "replacement_char" + type: "int" + default_value { + i: 65533 + } + } +} op { name: "UnicodeScript" input_arg { diff --git a/tensorflow/core/ops/dataset_ops.cc b/tensorflow/core/ops/dataset_ops.cc index 8402f250f9..b2ea637464 100644 --- a/tensorflow/core/ops/dataset_ops.cc +++ b/tensorflow/core/ops/dataset_ops.cc @@ -83,13 +83,6 @@ REGISTER_OP("GeneratorDataset") // stateful to inhibit constant folding. .SetShapeFn(shape_inference::ScalarShape); -REGISTER_OP("UnbatchDataset") - .Input("input_dataset: variant") - .Output("handle: variant") - .Attr("output_types: list(type) >= 1") - .Attr("output_shapes: list(shape) >= 1") - .SetShapeFn(shape_inference::ScalarShape); - REGISTER_OP("ZipDataset") .Input("input_datasets: N * variant") .Output("handle: variant") @@ -142,57 +135,6 @@ REGISTER_OP("SkipDataset") return shape_inference::ScalarShape(c); }); -REGISTER_OP("BytesProducedStatsDataset") - .Input("input_dataset: variant") - .Input("tag: string") - .Output("handle: variant") - .Attr("output_types: list(type) >= 1") - .Attr("output_shapes: list(shape) >= 1") - .SetShapeFn([](shape_inference::InferenceContext* c) { - shape_inference::ShapeHandle tag_shape; - TF_RETURN_IF_ERROR(c->WithRank(c->input(1), 0, &tag_shape)); - return shape_inference::ScalarShape(c); - }); - -REGISTER_OP("LatencyStatsDataset") - .Input("input_dataset: variant") - .Input("tag: string") - .Output("handle: variant") - .Attr("output_types: list(type) >= 1") - .Attr("output_shapes: list(shape) >= 1") - .SetShapeFn([](shape_inference::InferenceContext* c) { - shape_inference::ShapeHandle tag_shape; - TF_RETURN_IF_ERROR(c->WithRank(c->input(1), 0, &tag_shape)); - return shape_inference::ScalarShape(c); - }); - -REGISTER_OP("ParseExampleDataset") - .Input("input_dataset: variant") - .Input("num_parallel_calls: int64") - .Input("dense_defaults: Tdense") - .Output("handle: variant") - .Attr("sparse_keys: list(string) >= 0") - .Attr("dense_keys: list(string) >= 0") - .Attr("sparse_types: list({float,int64,string}) >= 0") - .Attr("Tdense: list({float,int64,string}) >= 0") - .Attr("dense_shapes: list(shape) >= 0") - .Attr("output_types: list(type) >= 1") - .Attr("output_shapes: list(shape) >= 1") // Output components will be - // sorted by key (dense_keys and - // sparse_keys combined) here. - .Attr("sloppy: bool = false") - .SetShapeFn(shape_inference::ScalarShape); - -REGISTER_OP("SetStatsAggregatorDataset") - .Input("input_dataset: variant") - .Input("stats_aggregator: resource") - .Input("tag: string") - .Input("counter_prefix: string") - .Output("handle: variant") - .Attr("output_types: list(type) >= 1") - .Attr("output_shapes: list(shape) >= 1") - .SetShapeFn(shape_inference::ScalarShape); - REGISTER_OP("MapDataset") .Input("input_dataset: variant") .Input("other_arguments: Targuments") @@ -217,58 +159,6 @@ REGISTER_OP("ParallelMapDataset") .Attr("sloppy: bool = false") .SetShapeFn(shape_inference::ScalarShape); -REGISTER_OP("MapAndBatchDataset") - .Input("input_dataset: variant") - .Input("other_arguments: Targuments") - .Input("batch_size: int64") - .Input("num_parallel_batches: int64") - .Input("drop_remainder: bool") - .Output("handle: variant") - .Attr("f: func") - .Attr("Targuments: list(type) >= 0") - .Attr("output_types: list(type) >= 1") - .Attr("output_shapes: list(shape) >= 1") - .SetShapeFn([](shape_inference::InferenceContext* c) { - // Use index from the end to retrieve the Input shapes, - // so that to avoid guessing the length of "other_arguments". - // batch_size, num_parallel_batches, and drop_remainder are 0-D scalars. - shape_inference::ShapeHandle unused; - TF_RETURN_IF_ERROR( - c->WithRank(c->input(c->num_inputs() - 3), 0, &unused)); - TF_RETURN_IF_ERROR( - c->WithRank(c->input(c->num_inputs() - 2), 0, &unused)); - TF_RETURN_IF_ERROR( - c->WithRank(c->input(c->num_inputs() - 1), 0, &unused)); - - return shape_inference::ScalarShape(c); - }); - -REGISTER_OP("MapAndBatchDatasetV2") - .Input("input_dataset: variant") - .Input("other_arguments: Targuments") - .Input("batch_size: int64") - .Input("num_parallel_calls: int64") - .Input("drop_remainder: bool") - .Output("handle: variant") - .Attr("f: func") - .Attr("Targuments: list(type) >= 0") - .Attr("output_types: list(type) >= 1") - .Attr("output_shapes: list(shape) >= 1") - .SetShapeFn([](shape_inference::InferenceContext* c) { - // Use index from the end to retrieve the Input shapes, - // so that to avoid guessing the length of "other_arguments". - // batch_size, num_parallel_calls, and drop_remainder are 0-D scalars. - shape_inference::ShapeHandle unused; - TF_RETURN_IF_ERROR( - c->WithRank(c->input(c->num_inputs() - 3), 0, &unused)); - TF_RETURN_IF_ERROR( - c->WithRank(c->input(c->num_inputs() - 2), 0, &unused)); - TF_RETURN_IF_ERROR( - c->WithRank(c->input(c->num_inputs() - 1), 0, &unused)); - - return shape_inference::ScalarShape(c); - }); - REGISTER_OP("PrefetchDataset") .Input("input_dataset: variant") .Input("buffer_size: int64") @@ -282,18 +172,6 @@ REGISTER_OP("PrefetchDataset") return shape_inference::ScalarShape(c); }); -REGISTER_OP("ScanDataset") - .Input("input_dataset: variant") - .Input("initial_state: Tstate") - .Input("other_arguments: Targuments") - .Output("handle: variant") - .Attr("f: func") - .Attr("Tstate: list(type) >= 1") - .Attr("Targuments: list(type) >= 0") - .Attr("output_types: list(type) >= 1") - .Attr("output_shapes: list(shape) >= 1") - .SetShapeFn(shape_inference::ScalarShape); - REGISTER_OP("FlatMapDataset") .Input("input_dataset: variant") .Input("other_arguments: Targuments") @@ -316,21 +194,6 @@ REGISTER_OP("InterleaveDataset") .Attr("output_shapes: list(shape) >= 1") .SetShapeFn(shape_inference::ScalarShape); -REGISTER_OP("ParallelInterleaveDataset") - .Input("input_dataset: variant") - .Input("other_arguments: Targuments") - .Input("cycle_length: int64") - .Input("block_length: int64") - .Input("sloppy: bool") - .Input("buffer_output_elements: int64") - .Input("prefetch_input_elements: int64") - .Output("handle: variant") - .Attr("f: func") - .Attr("Targuments: list(type) >= 0") - .Attr("output_types: list(type) >= 1") - .Attr("output_shapes: list(shape) >= 1") - .SetShapeFn(shape_inference::ScalarShape); - REGISTER_OP("ParallelInterleaveDatasetV2") .Input("input_dataset: variant") .Input("other_arguments: Targuments") @@ -345,43 +208,6 @@ REGISTER_OP("ParallelInterleaveDatasetV2") .Attr("sloppy: bool = false") .SetShapeFn(shape_inference::ScalarShape); -REGISTER_OP("GroupByReducerDataset") - .Input("input_dataset: variant") - .Input("key_func_other_arguments: Tkey_func_other_arguments") - .Input("init_func_other_arguments: Tinit_func_other_arguments") - .Input("reduce_func_other_arguments: Treduce_func_other_arguments") - .Input("finalize_func_other_arguments: Tfinalize_func_other_arguments") - .Output("handle: variant") - .Attr("key_func: func") - .Attr("init_func: func") - .Attr("reduce_func: func") - .Attr("finalize_func: func") - .Attr("Tkey_func_other_arguments: list(type) >= 0") - .Attr("Tinit_func_other_arguments: list(type) >= 0") - .Attr("Treduce_func_other_arguments: list(type) >= 0") - .Attr("Tfinalize_func_other_arguments: list(type) >= 0") - .Attr("output_types: list(type) >= 1") - .Attr("output_shapes: list(shape) >= 1") - .SetIsStateful() - .SetShapeFn(shape_inference::ScalarShape); - -REGISTER_OP("GroupByWindowDataset") - .Input("input_dataset: variant") - .Input("key_func_other_arguments: Tkey_func_other_arguments") - .Input("reduce_func_other_arguments: Treduce_func_other_arguments") - .Input( - "window_size_func_other_arguments: Twindow_size_func_other_arguments") - .Output("handle: variant") - .Attr("key_func: func") - .Attr("reduce_func: func") - .Attr("window_size_func: func") - .Attr("Tkey_func_other_arguments: list(type) >= 0") - .Attr("Treduce_func_other_arguments: list(type) >= 0") - .Attr("Twindow_size_func_other_arguments: list(type) >= 0") - .Attr("output_types: list(type) >= 1") - .Attr("output_shapes: list(shape) >= 1") - .SetShapeFn(shape_inference::ScalarShape); - REGISTER_OP("FilterDataset") .Input("input_dataset: variant") .Input("other_arguments: Targuments") @@ -447,23 +273,6 @@ REGISTER_OP("BatchDatasetV2") return shape_inference::ScalarShape(c); }); -REGISTER_OP("SlideDataset") - .Input("input_dataset: variant") - .Input("window_size: int64") - .Input("window_shift: int64") - .Input("window_stride: int64") - .Output("handle: variant") - .Attr("output_types: list(type) >= 1") - .Attr("output_shapes: list(shape) >= 1") - .SetShapeFn([](shape_inference::InferenceContext* c) { - shape_inference::ShapeHandle unused; - // window_size, window_shift, and window_stride should be scalars. - TF_RETURN_IF_ERROR(c->WithRank(c->input(1), 0, &unused)); - TF_RETURN_IF_ERROR(c->WithRank(c->input(2), 0, &unused)); - TF_RETURN_IF_ERROR(c->WithRank(c->input(3), 0, &unused)); - return shape_inference::ScalarShape(c); - }); - // TODO(mrry): Validate that `padded_shapes` are all vectors, the lengths of // `output_types` and `output_shapes` are `N` the `output_shapes` are (as far as // possible to tell statically) compatible with `padded_shapes`, and that @@ -504,22 +313,6 @@ REGISTER_OP("PaddedBatchDatasetV2") return shape_inference::ScalarShape(c); }); -REGISTER_OP("DenseToSparseBatchDataset") - .Input("input_dataset: variant") - .Input("batch_size: int64") - .Input("row_shape: int64") - .Output("handle: variant") - .Attr("output_types: list(type) >= 1") - .Attr("output_shapes: list(shape) >= 1") - .SetShapeFn([](shape_inference::InferenceContext* c) { - shape_inference::ShapeHandle unused; - // batch_size should be a scalar. - TF_RETURN_IF_ERROR(c->WithRank(c->input(1), 0, &unused)); - // row_shape should be a 1-D vector. - TF_RETURN_IF_ERROR(c->WithRank(c->input(2), 1, &unused)); - return shape_inference::ScalarShape(c); - }); - REGISTER_OP("RangeDataset") .Input("start: int64") .Input("stop: int64") @@ -538,22 +331,6 @@ REGISTER_OP("RangeDataset") return shape_inference::ScalarShape(c); }); -REGISTER_OP("RandomDataset") - .Input("seed: int64") - .Input("seed2: int64") - .Output("handle: variant") - .Attr("output_types: list(type) >= 1") - .Attr("output_shapes: list(shape) >= 1") - .SetIsStateful() // TODO(b/65524810): Source dataset ops must be marked - // stateful to inhibit constant folding. - .SetShapeFn([](shape_inference::InferenceContext* c) { - shape_inference::ShapeHandle unused; - // buffer_size, seed, and seed2 should be scalars. - TF_RETURN_IF_ERROR(c->WithRank(c->input(0), 0, &unused)); - TF_RETURN_IF_ERROR(c->WithRank(c->input(1), 0, &unused)); - return shape_inference::ScalarShape(c); - }); - REGISTER_OP("ShuffleDataset") .Input("input_dataset: variant") .Input("buffer_size: int64") @@ -622,36 +399,6 @@ REGISTER_OP("TextLineDataset") return shape_inference::ScalarShape(c); }); -REGISTER_OP("MatchingFilesDataset") - .Input("patterns: string") - .Output("handle: variant") - .SetIsStateful() // TODO(b/65524810): Source dataset ops must be marked - // stateful to inhibit constant folding. - .SetShapeFn([](shape_inference::InferenceContext* c) { - shape_inference::ShapeHandle unused; - // `patterns` must be a scalar or a vector. - TF_RETURN_IF_ERROR(c->WithRankAtMost(c->input(0), 1, &unused)); - return shape_inference::ScalarShape(c); - }); - -REGISTER_OP("SqlDataset") - .Input("driver_name: string") - .Input("data_source_name: string") - .Input("query: string") - .Output("handle: variant") - .Attr("output_types: list(type) >= 1") - .Attr("output_shapes: list(shape) >= 1") - .SetIsStateful() // TODO(b/65524810): Source dataset ops must be marked - // stateful to inhibit constant folding. - .SetShapeFn([](shape_inference::InferenceContext* c) { - shape_inference::ShapeHandle unused; - // driver_name, data_source_name, and query should be scalars. - TF_RETURN_IF_ERROR(c->WithRank(c->input(0), 0, &unused)); - TF_RETURN_IF_ERROR(c->WithRank(c->input(1), 0, &unused)); - TF_RETURN_IF_ERROR(c->WithRank(c->input(2), 0, &unused)); - return shape_inference::ScalarShape(c); - }); - REGISTER_OP("FixedLengthRecordDataset") .Input("filenames: string") .Input("header_bytes: int64") @@ -838,53 +585,6 @@ REGISTER_OP("DeserializeIterator") .Input("serialized: variant") .SetShapeFn(shape_inference::NoOutputs); -REGISTER_OP("StatsAggregatorHandle") - .Output("handle: resource") - .SetShapeFn(shape_inference::ScalarShape) - .Attr("container: string = ''") - .Attr("shared_name: string = ''"); - -REGISTER_OP("StatsAggregatorSummary") - .Input("iterator: resource") - .Output("summary: string") - .SetShapeFn(shape_inference::ScalarShape); - -REGISTER_OP("PrependFromQueueAndPaddedBatchDataset") - .Input("input_dataset: variant") - .Input("batch_size: int64") - .Input("padded_shapes: N * int64") - .Input("padding_values: Toutput_types") - .Output("handle: variant") - .Attr("Toutput_types: list(type) >= 1") - .Attr("output_shapes: list(shape) >= 1") - .Attr("N: int >= 1") - // TODO(ebrevdo): Validate that `padded_shapes` are all vectors, the lengths - // of `Toutput_types` and `output_shapes` are `N`, that the - // length of `output_types` is `N`, the `output_shapes` are - // (as far as possible to tell statically) compatible with `padded_shapes`, - // and that `padding_values` are all scalars. - .SetShapeFn([](shape_inference::InferenceContext* c) { - shape_inference::ShapeHandle unused; - // batch_size should be a scalar. - TF_RETURN_IF_ERROR(c->WithRank(c->input(1), 0, &unused)); - return shape_inference::ScalarShape(c); - }); - -REGISTER_OP("EnqueueInQueueDataset") - .Input("queue: variant") - .Input("components: Tcomponents") - .Attr("Tcomponents: list(type) >= 1") - .SetIsStateful() // To avoid CSE on multiple calls to Enqueue. - // TODO(ebrevdo): SetShapeFn to test input dtypes and shapes by - // reading from queue handle (is that even possible?). - .SetShapeFn(shape_inference::NoOutputs); - -REGISTER_OP("DatasetToTFRecord") - .Input("input_dataset: variant") - .Input("filename: string") - .Input("compression_type: string") - .SetShapeFn(shape_inference::NoOutputs); - REGISTER_OP("DatasetToGraph") .Input("input_dataset: variant") .Output("graph: string") diff --git a/tensorflow/core/ops/experimental_dataset_ops.cc b/tensorflow/core/ops/experimental_dataset_ops.cc index 9733cf2776..0711034118 100644 --- a/tensorflow/core/ops/experimental_dataset_ops.cc +++ b/tensorflow/core/ops/experimental_dataset_ops.cc @@ -17,14 +17,17 @@ limitations under the License. namespace tensorflow { -REGISTER_OP("ExperimentalDirectedInterleaveDataset") - .Input("selector_input_dataset: variant") - .Input("data_input_datasets: N * variant") +REGISTER_OP("ExperimentalBytesProducedStatsDataset") + .Input("input_dataset: variant") + .Input("tag: string") .Output("handle: variant") .Attr("output_types: list(type) >= 1") .Attr("output_shapes: list(shape) >= 1") - .Attr("N: int >= 1") - .SetShapeFn(shape_inference::ScalarShape); + .SetShapeFn([](shape_inference::InferenceContext* c) { + shape_inference::ShapeHandle tag_shape; + TF_RETURN_IF_ERROR(c->WithRank(c->input(1), 0, &tag_shape)); + return shape_inference::ScalarShape(c); + }); REGISTER_OP("ExperimentalCSVDataset") .Input("filenames: string") @@ -68,6 +71,79 @@ REGISTER_OP("ExperimentalCSVDataset") return shape_inference::ScalarShape(c); }); +REGISTER_OP("ExperimentalDatasetCardinality") + .Input("input_dataset: variant") + .Output("cardinality: int64") + .SetShapeFn(shape_inference::ScalarShape); + +REGISTER_OP("ExperimentalDatasetToTFRecord") + .Input("input_dataset: variant") + .Input("filename: string") + .Input("compression_type: string") + .SetShapeFn(shape_inference::NoOutputs); + +REGISTER_OP("ExperimentalDenseToSparseBatchDataset") + .Input("input_dataset: variant") + .Input("batch_size: int64") + .Input("row_shape: int64") + .Output("handle: variant") + .Attr("output_types: list(type) >= 1") + .Attr("output_shapes: list(shape) >= 1") + .SetShapeFn([](shape_inference::InferenceContext* c) { + shape_inference::ShapeHandle unused; + // batch_size should be a scalar. + TF_RETURN_IF_ERROR(c->WithRank(c->input(1), 0, &unused)); + // row_shape should be a 1-D vector. + TF_RETURN_IF_ERROR(c->WithRank(c->input(2), 1, &unused)); + return shape_inference::ScalarShape(c); + }); + +REGISTER_OP("ExperimentalDirectedInterleaveDataset") + .Input("selector_input_dataset: variant") + .Input("data_input_datasets: N * variant") + .Output("handle: variant") + .Attr("output_types: list(type) >= 1") + .Attr("output_shapes: list(shape) >= 1") + .Attr("N: int >= 1") + .SetShapeFn(shape_inference::ScalarShape); + +REGISTER_OP("ExperimentalGroupByReducerDataset") + .Input("input_dataset: variant") + .Input("key_func_other_arguments: Tkey_func_other_arguments") + .Input("init_func_other_arguments: Tinit_func_other_arguments") + .Input("reduce_func_other_arguments: Treduce_func_other_arguments") + .Input("finalize_func_other_arguments: Tfinalize_func_other_arguments") + .Output("handle: variant") + .Attr("key_func: func") + .Attr("init_func: func") + .Attr("reduce_func: func") + .Attr("finalize_func: func") + .Attr("Tkey_func_other_arguments: list(type) >= 0") + .Attr("Tinit_func_other_arguments: list(type) >= 0") + .Attr("Treduce_func_other_arguments: list(type) >= 0") + .Attr("Tfinalize_func_other_arguments: list(type) >= 0") + .Attr("output_types: list(type) >= 1") + .Attr("output_shapes: list(shape) >= 1") + .SetIsStateful() + .SetShapeFn(shape_inference::ScalarShape); + +REGISTER_OP("ExperimentalGroupByWindowDataset") + .Input("input_dataset: variant") + .Input("key_func_other_arguments: Tkey_func_other_arguments") + .Input("reduce_func_other_arguments: Treduce_func_other_arguments") + .Input( + "window_size_func_other_arguments: Twindow_size_func_other_arguments") + .Output("handle: variant") + .Attr("key_func: func") + .Attr("reduce_func: func") + .Attr("window_size_func: func") + .Attr("Tkey_func_other_arguments: list(type) >= 0") + .Attr("Treduce_func_other_arguments: list(type) >= 0") + .Attr("Twindow_size_func_other_arguments: list(type) >= 0") + .Attr("output_types: list(type) >= 1") + .Attr("output_shapes: list(shape) >= 1") + .SetShapeFn(shape_inference::ScalarShape); + REGISTER_OP("ExperimentalIgnoreErrorsDataset") .Input("input_dataset: variant") .Output("handle: variant") @@ -75,6 +151,44 @@ REGISTER_OP("ExperimentalIgnoreErrorsDataset") .Attr("output_shapes: list(shape) >= 1") .SetShapeFn(shape_inference::ScalarShape); +REGISTER_OP("ExperimentalLatencyStatsDataset") + .Input("input_dataset: variant") + .Input("tag: string") + .Output("handle: variant") + .Attr("output_types: list(type) >= 1") + .Attr("output_shapes: list(shape) >= 1") + .SetShapeFn([](shape_inference::InferenceContext* c) { + shape_inference::ShapeHandle tag_shape; + TF_RETURN_IF_ERROR(c->WithRank(c->input(1), 0, &tag_shape)); + return shape_inference::ScalarShape(c); + }); + +REGISTER_OP("ExperimentalMapAndBatchDataset") + .Input("input_dataset: variant") + .Input("other_arguments: Targuments") + .Input("batch_size: int64") + .Input("num_parallel_calls: int64") + .Input("drop_remainder: bool") + .Output("handle: variant") + .Attr("f: func") + .Attr("Targuments: list(type) >= 0") + .Attr("output_types: list(type) >= 1") + .Attr("output_shapes: list(shape) >= 1") + .SetShapeFn([](shape_inference::InferenceContext* c) { + // Use index from the end to retrieve the Input shapes, + // so that to avoid guessing the length of "other_arguments". + // batch_size, num_parallel_calls, and drop_remainder are 0-D scalars. + shape_inference::ShapeHandle unused; + TF_RETURN_IF_ERROR( + c->WithRank(c->input(c->num_inputs() - 3), 0, &unused)); + TF_RETURN_IF_ERROR( + c->WithRank(c->input(c->num_inputs() - 2), 0, &unused)); + TF_RETURN_IF_ERROR( + c->WithRank(c->input(c->num_inputs() - 1), 0, &unused)); + + return shape_inference::ScalarShape(c); + }); + REGISTER_OP("ExperimentalMapDataset") .Input("input_dataset: variant") .Input("other_arguments: Targuments") @@ -86,6 +200,18 @@ REGISTER_OP("ExperimentalMapDataset") .Attr("use_inter_op_parallelism: bool = true") .SetShapeFn(shape_inference::ScalarShape); +REGISTER_OP("ExperimentalMatchingFilesDataset") + .Input("patterns: string") + .Output("handle: variant") + .SetIsStateful() // TODO(b/65524810): Source dataset ops must be marked + // stateful to inhibit constant folding. + .SetShapeFn([](shape_inference::InferenceContext* c) { + shape_inference::ShapeHandle unused; + // `patterns` must be a scalar or a vector. + TF_RETURN_IF_ERROR(c->WithRankAtMost(c->input(0), 1, &unused)); + return shape_inference::ScalarShape(c); + }); + REGISTER_OP("ExperimentalNonSerializableDataset") .Input("input_dataset: variant") .Output("handle: variant") @@ -93,6 +219,76 @@ REGISTER_OP("ExperimentalNonSerializableDataset") .Attr("output_shapes: list(shape) >= 1") .SetShapeFn(shape_inference::ScalarShape); +REGISTER_OP("ExperimentalParallelInterleaveDataset") + .Input("input_dataset: variant") + .Input("other_arguments: Targuments") + .Input("cycle_length: int64") + .Input("block_length: int64") + .Input("sloppy: bool") + .Input("buffer_output_elements: int64") + .Input("prefetch_input_elements: int64") + .Output("handle: variant") + .Attr("f: func") + .Attr("Targuments: list(type) >= 0") + .Attr("output_types: list(type) >= 1") + .Attr("output_shapes: list(shape) >= 1") + .SetShapeFn(shape_inference::ScalarShape); + +REGISTER_OP("ExperimentalParseExampleDataset") + .Input("input_dataset: variant") + .Input("num_parallel_calls: int64") + .Input("dense_defaults: Tdense") + .Output("handle: variant") + .Attr("sparse_keys: list(string) >= 0") + .Attr("dense_keys: list(string) >= 0") + .Attr("sparse_types: list({float,int64,string}) >= 0") + .Attr("Tdense: list({float,int64,string}) >= 0") + .Attr("dense_shapes: list(shape) >= 0") + .Attr("output_types: list(type) >= 1") + .Attr("output_shapes: list(shape) >= 1") // Output components will be + // sorted by key (dense_keys and + // sparse_keys combined) here. + .Attr("sloppy: bool = false") + .SetShapeFn(shape_inference::ScalarShape); + +REGISTER_OP("ExperimentalRandomDataset") + .Input("seed: int64") + .Input("seed2: int64") + .Output("handle: variant") + .Attr("output_types: list(type) >= 1") + .Attr("output_shapes: list(shape) >= 1") + .SetIsStateful() // TODO(b/65524810): Source dataset ops must be marked + // stateful to inhibit constant folding. + .SetShapeFn([](shape_inference::InferenceContext* c) { + shape_inference::ShapeHandle unused; + // buffer_size, seed, and seed2 should be scalars. + TF_RETURN_IF_ERROR(c->WithRank(c->input(0), 0, &unused)); + TF_RETURN_IF_ERROR(c->WithRank(c->input(1), 0, &unused)); + return shape_inference::ScalarShape(c); + }); + +REGISTER_OP("ExperimentalScanDataset") + .Input("input_dataset: variant") + .Input("initial_state: Tstate") + .Input("other_arguments: Targuments") + .Output("handle: variant") + .Attr("f: func") + .Attr("Tstate: list(type) >= 1") + .Attr("Targuments: list(type) >= 0") + .Attr("output_types: list(type) >= 1") + .Attr("output_shapes: list(shape) >= 1") + .SetShapeFn(shape_inference::ScalarShape); + +REGISTER_OP("ExperimentalSetStatsAggregatorDataset") + .Input("input_dataset: variant") + .Input("stats_aggregator: resource") + .Input("tag: string") + .Input("counter_prefix: string") + .Output("handle: variant") + .Attr("output_types: list(type) >= 1") + .Attr("output_shapes: list(shape) >= 1") + .SetShapeFn(shape_inference::ScalarShape); + REGISTER_OP("ExperimentalSleepDataset") .Input("input_dataset: variant") .Input("sleep_microseconds: int64") @@ -107,6 +303,59 @@ REGISTER_OP("ExperimentalSleepDataset") return shape_inference::ScalarShape(c); }); +REGISTER_OP("ExperimentalSlidingWindowDataset") + .Input("input_dataset: variant") + .Input("window_size: int64") + .Input("window_shift: int64") + .Input("window_stride: int64") + .Output("handle: variant") + .Attr("output_types: list(type) >= 1") + .Attr("output_shapes: list(shape) >= 1") + .SetShapeFn([](shape_inference::InferenceContext* c) { + shape_inference::ShapeHandle unused; + // window_size, window_shift, and window_stride should be scalars. + TF_RETURN_IF_ERROR(c->WithRank(c->input(1), 0, &unused)); + TF_RETURN_IF_ERROR(c->WithRank(c->input(2), 0, &unused)); + TF_RETURN_IF_ERROR(c->WithRank(c->input(3), 0, &unused)); + return shape_inference::ScalarShape(c); + }); + +REGISTER_OP("ExperimentalSqlDataset") + .Input("driver_name: string") + .Input("data_source_name: string") + .Input("query: string") + .Output("handle: variant") + .Attr("output_types: list(type) >= 1") + .Attr("output_shapes: list(shape) >= 1") + .SetIsStateful() // TODO(b/65524810): Source dataset ops must be marked + // stateful to inhibit constant folding. + .SetShapeFn([](shape_inference::InferenceContext* c) { + shape_inference::ShapeHandle unused; + // driver_name, data_source_name, and query should be scalars. + TF_RETURN_IF_ERROR(c->WithRank(c->input(0), 0, &unused)); + TF_RETURN_IF_ERROR(c->WithRank(c->input(1), 0, &unused)); + TF_RETURN_IF_ERROR(c->WithRank(c->input(2), 0, &unused)); + return shape_inference::ScalarShape(c); + }); + +REGISTER_OP("ExperimentalStatsAggregatorHandle") + .Output("handle: resource") + .SetShapeFn(shape_inference::ScalarShape) + .Attr("container: string = ''") + .Attr("shared_name: string = ''"); + +REGISTER_OP("ExperimentalStatsAggregatorSummary") + .Input("iterator: resource") + .Output("summary: string") + .SetShapeFn(shape_inference::ScalarShape); + +REGISTER_OP("ExperimentalUnbatchDataset") + .Input("input_dataset: variant") + .Output("handle: variant") + .Attr("output_types: list(type) >= 1") + .Attr("output_shapes: list(shape) >= 1") + .SetShapeFn(shape_inference::ScalarShape); + REGISTER_OP("ExperimentalUniqueDataset") .Input("input_dataset: variant") .Output("handle: variant") @@ -119,26 +368,21 @@ REGISTER_OP("ExperimentalIteratorGetDevice") .Output("device: string") .SetShapeFn(shape_inference::ScalarShape); -REGISTER_OP("ExperimentalFunctionBufferingResource") - .Input("string_arg: string") - .Input("target_device: string") - .Output("resource: resource") - .Attr("shared_name: string") - .Attr("container: string") - .Attr("f: func") - .Attr("buffer_size: int") - .Attr("output_types: list(type)") - .SetShapeFn(shape_inference::UnknownShape); - -REGISTER_OP("ExperimentalFunctionBufferingResourceGetNext") - .Input("function_buffer_resource: resource") - .Attr("output_types: list(type)") - .Output("output: output_types") - .SetShapeFn(shape_inference::UnknownShape); - -REGISTER_OP("ExperimentalFunctionBufferingResourceReset") - .Input("function_buffer_resource: resource") - .SetShapeFn(shape_inference::UnknownShape); +REGISTER_OP("ExperimentalMaxIntraOpParallelismDataset") + .Input("input_dataset: variant") + .Input("max_intra_op_parallelism: int64") + .Output("handle: variant") + .Attr("output_types: list(type) >= 1") + .Attr("output_shapes: list(shape) >= 1") + .SetShapeFn(shape_inference::ScalarShape); + +REGISTER_OP("ExperimentalPrivateThreadPoolDataset") + .Input("input_dataset: variant") + .Input("num_threads: int64") + .Output("handle: variant") + .Attr("output_types: list(type) >= 1") + .Attr("output_shapes: list(shape) >= 1") + .SetShapeFn(shape_inference::ScalarShape); REGISTER_OP("ExperimentalThreadPoolDataset") .Input("input_dataset: variant") diff --git a/tensorflow/core/ops/functional_ops.cc b/tensorflow/core/ops/functional_ops.cc index ee14a851eb..5e0bdd888c 100644 --- a/tensorflow/core/ops/functional_ops.cc +++ b/tensorflow/core/ops/functional_ops.cc @@ -226,6 +226,7 @@ REGISTER_OP("PartitionedCall") .Attr("Tout: list(type) >= 0") .Attr("f: func") .Attr("config: string = ''") + .Attr("config_proto: string = ''") .Attr("executor_type: string = ''") .SetShapeFn(shape_inference::UnknownShape); @@ -235,7 +236,8 @@ REGISTER_OP("StatefulPartitionedCall") .Attr("Tin: list(type) >= 0") .Attr("Tout: list(type) >= 0") .Attr("f: func") - .Attr("config: string = ''") + .Attr("config: string = ''") // Deprecated in favor of config_proto + .Attr("config_proto: string = ''") .Attr("executor_type: string = ''") .SetIsStateful() .SetShapeFn(shape_inference::UnknownShape); diff --git a/tensorflow/core/ops/list_ops.cc b/tensorflow/core/ops/list_ops.cc index 88d6d14c30..01ebcd1543 100644 --- a/tensorflow/core/ops/list_ops.cc +++ b/tensorflow/core/ops/list_ops.cc @@ -28,13 +28,14 @@ REGISTER_OP("EmptyTensorList") .Attr("shape_type: {int32, int64}") .SetShapeFn([](shape_inference::InferenceContext* c) { c->set_output(0, c->Scalar()); - DataType t; - TF_RETURN_IF_ERROR(c->GetAttr("element_dtype", &t)); - shape_inference::ShapeHandle s; - TF_RETURN_IF_ERROR( - c->MakeShapeFromShapeTensorTreatScalarAsUnknownShape(0, &s)); + DataType element_dtype; + TF_RETURN_IF_ERROR(c->GetAttr("element_dtype", &element_dtype)); + shape_inference::ShapeHandle element_shape; + TF_RETURN_IF_ERROR(c->MakeShapeFromShapeTensorTreatScalarAsUnknownShape( + 0, &element_shape)); c->set_output_handle_shapes_and_types( - 0, std::vector{{s, t}}); + 0, std::vector{ + {element_shape, element_dtype}}); return Status::OK(); }); @@ -45,9 +46,9 @@ REGISTER_OP("TensorListPushBack") .Attr("element_dtype: type") .SetShapeFn([](shape_inference::InferenceContext* c) { c->set_output(0, c->Scalar()); - DataType t; - TF_RETURN_IF_ERROR(c->GetAttr("element_dtype", &t)); - shape_inference::ShapeHandle s = c->UnknownShape(); + DataType element_dtype; + TF_RETURN_IF_ERROR(c->GetAttr("element_dtype", &element_dtype)); + shape_inference::ShapeHandle element_shape = c->UnknownShape(); auto* handle_data = c->input_handle_shapes_and_types(0); if (handle_data != nullptr && handle_data->size() != 1) { @@ -57,18 +58,21 @@ REGISTER_OP("TensorListPushBack") if (handle_data != nullptr) { const shape_inference::ShapeAndType& list_shape_type = (*handle_data)[0]; - if (list_shape_type.dtype != t) { + if (list_shape_type.dtype != element_dtype) { return errors::InvalidArgument( "Trying to push to list with wrong element dtype. List has type ", DataTypeString(list_shape_type.dtype), - " but trying to push element with type ", DataTypeString(t)); + " but trying to push element with type ", + DataTypeString(element_dtype)); } shape_inference::ShapeHandle ignored; - TF_RETURN_IF_ERROR(c->Merge(s, list_shape_type.shape, &ignored)); - s = list_shape_type.shape; + TF_RETURN_IF_ERROR( + c->Merge(element_shape, list_shape_type.shape, &ignored)); + element_shape = list_shape_type.shape; } c->set_output_handle_shapes_and_types( - 0, std::vector{{s, t}}); + 0, std::vector{ + {element_shape, element_dtype}}); return Status::OK(); }); @@ -89,9 +93,9 @@ REGISTER_OP("TensorListPushBackBatch") c->set_output(0, input_handles); - DataType t; - TF_RETURN_IF_ERROR(c->GetAttr("element_dtype", &t)); - shape_inference::ShapeHandle s = c->UnknownShape(); + DataType element_dtype; + TF_RETURN_IF_ERROR(c->GetAttr("element_dtype", &element_dtype)); + shape_inference::ShapeHandle element_shape = c->UnknownShape(); auto* handle_data = c->input_handle_shapes_and_types(0); if (handle_data != nullptr && handle_data->size() != 1) { @@ -101,18 +105,21 @@ REGISTER_OP("TensorListPushBackBatch") if (handle_data != nullptr) { const shape_inference::ShapeAndType& list_shape_type = (*handle_data)[0]; - if (list_shape_type.dtype != t) { + if (list_shape_type.dtype != element_dtype) { return errors::InvalidArgument( "Trying to push to list with wrong element dtype. List has type ", DataTypeString(list_shape_type.dtype), - " but trying to push element with type ", DataTypeString(t)); + " but trying to push element with type ", + DataTypeString(element_dtype)); } shape_inference::ShapeHandle ignored; - TF_RETURN_IF_ERROR(c->Merge(s, list_shape_type.shape, &ignored)); - s = list_shape_type.shape; + TF_RETURN_IF_ERROR( + c->Merge(element_shape, list_shape_type.shape, &ignored)); + element_shape = list_shape_type.shape; } c->set_output_handle_shapes_and_types( - 0, std::vector{{s, t}}); + 0, std::vector{ + {element_shape, element_dtype}}); return Status::OK(); }); @@ -127,9 +134,9 @@ REGISTER_OP("TensorListPopBack") .Output("tensor: element_dtype") .Attr("element_dtype: type") .SetShapeFn([](shape_inference::InferenceContext* c) { - DataType t; - TF_RETURN_IF_ERROR(c->GetAttr("element_dtype", &t)); - shape_inference::ShapeHandle s = c->UnknownShape(); + DataType element_dtype; + TF_RETURN_IF_ERROR(c->GetAttr("element_dtype", &element_dtype)); + shape_inference::ShapeHandle tensor_shape = c->UnknownShape(); auto* handle_data = c->input_handle_shapes_and_types(0); if (handle_data != nullptr && handle_data->size() != 1) { return errors::InvalidArgument( @@ -138,19 +145,21 @@ REGISTER_OP("TensorListPopBack") if (handle_data != nullptr) { const shape_inference::ShapeAndType& list_shape_type = (*handle_data)[0]; - if (list_shape_type.dtype != t) { + if (list_shape_type.dtype != element_dtype) { return errors::InvalidArgument( "Trying to read from list with wrong element dtype. List has " "type ", DataTypeString(list_shape_type.dtype), - " but trying to push element with type ", DataTypeString(t)); + " but trying to push element with type ", + DataTypeString(element_dtype)); } shape_inference::ShapeHandle ignored; - TF_RETURN_IF_ERROR(c->Merge(s, list_shape_type.shape, &ignored)); + TF_RETURN_IF_ERROR( + c->Merge(tensor_shape, list_shape_type.shape, &ignored)); c->set_output_handle_shapes_and_types(0, *handle_data); - s = list_shape_type.shape; + tensor_shape = list_shape_type.shape; } - c->set_output(1, s); + c->set_output(1, tensor_shape); c->set_output(0, c->Scalar()); return Status::OK(); }); @@ -161,9 +170,9 @@ REGISTER_OP("TensorListStack") .Attr("element_dtype: type") .Attr("num_elements: int = -1") .SetShapeFn([](shape_inference::InferenceContext* c) { - DataType t; - TF_RETURN_IF_ERROR(c->GetAttr("element_dtype", &t)); - shape_inference::ShapeHandle s = c->UnknownShape(); + DataType element_dtype; + TF_RETURN_IF_ERROR(c->GetAttr("element_dtype", &element_dtype)); + shape_inference::ShapeHandle element_shape = c->UnknownShape(); auto* handle_data = c->input_handle_shapes_and_types(0); if (handle_data != nullptr && handle_data->size() != 1) { return errors::InvalidArgument( @@ -172,16 +181,17 @@ REGISTER_OP("TensorListStack") if (handle_data != nullptr) { const shape_inference::ShapeAndType& list_shape_type = (*handle_data)[0]; - if (list_shape_type.dtype != t) { + if (list_shape_type.dtype != element_dtype) { return errors::InvalidArgument( "Trying to read from list with wrong element dtype. List has " "type ", DataTypeString(list_shape_type.dtype), " but expectec type ", - DataTypeString(t)); + DataTypeString(element_dtype)); } shape_inference::ShapeHandle ignored; - TF_RETURN_IF_ERROR(c->Merge(s, list_shape_type.shape, &ignored)); - s = list_shape_type.shape; + TF_RETURN_IF_ERROR( + c->Merge(element_shape, list_shape_type.shape, &ignored)); + element_shape = list_shape_type.shape; } int expected_num_elements = -1; TF_RETURN_IF_ERROR(c->GetAttr("num_elements", &expected_num_elements)); @@ -192,11 +202,88 @@ REGISTER_OP("TensorListStack") num_elements = c->MakeShape({expected_num_elements}); } shape_inference::ShapeHandle result; - TF_RETURN_IF_ERROR(c->Concatenate(num_elements, s, &result)); + TF_RETURN_IF_ERROR(c->Concatenate(num_elements, element_shape, &result)); c->set_output(0, result); return Status::OK(); }); +REGISTER_OP("TensorListConcat") + .Input("input_handle: variant") + .Output("tensor: element_dtype") + .Output("lengths: int64") + .Attr("element_dtype: type") + .SetShapeFn([](shape_inference::InferenceContext* c) { + DataType element_dtype; + TF_RETURN_IF_ERROR(c->GetAttr("element_dtype", &element_dtype)); + shape_inference::ShapeHandle element_shape = c->UnknownShape(); + auto* handle_data = c->input_handle_shapes_and_types(0); + if (handle_data != nullptr && handle_data->size() != 1) { + return errors::InvalidArgument( + "Trying to read from list with wrong variant data."); + } + if (handle_data != nullptr) { + const shape_inference::ShapeAndType& list_shape_type = + (*handle_data)[0]; + if (list_shape_type.dtype != element_dtype) { + return errors::InvalidArgument( + "Trying to read from list with wrong element dtype. List has " + "type ", + DataTypeString(list_shape_type.dtype), " but expected type ", + DataTypeString(element_dtype)); + } + shape_inference::ShapeHandle ignored; + TF_RETURN_IF_ERROR( + c->Merge(element_shape, list_shape_type.shape, &ignored)); + element_shape = list_shape_type.shape; + } + if (c->RankKnown(element_shape)) { + shape_inference::ShapeHandle result; + TF_RETURN_IF_ERROR(c->Subshape(element_shape, 1, &result)); + TF_RETURN_IF_ERROR( + c->Concatenate(c->MakeShape({c->UnknownDim()}), result, &result)); + c->set_output(0, result); + } else { + c->set_output(0, c->UnknownShape()); + } + c->set_output(1, c->MakeShape({c->UnknownDim()})); + return Status::OK(); + }); + +REGISTER_OP("TensorListSplit") + .Input("tensor: element_dtype") + .Input("element_shape: shape_type") + .Input("lengths: int64") + .Output("output_handle: variant") + .Attr("element_dtype: type") + .Attr("shape_type: {int32, int64}") + .SetShapeFn([](shape_inference::InferenceContext* c) { + c->set_output(0, c->Scalar()); + DataType element_dtype; + TF_RETURN_IF_ERROR(c->GetAttr("element_dtype", &element_dtype)); + shape_inference::ShapeHandle tensor_shape = c->input(0); + shape_inference::ShapeHandle ignored; + // Check that tensor is at least a vector. + TF_RETURN_IF_ERROR(c->WithRankAtLeast(tensor_shape, 1, &ignored)); + // Check that lengths is a vector. + TF_RETURN_IF_ERROR(c->WithRank(c->input(2), 1, &ignored)); + shape_inference::ShapeHandle element_shape_from_tensor_shape; + TF_RETURN_IF_ERROR( + c->Subshape(tensor_shape, 1, &element_shape_from_tensor_shape)); + TF_RETURN_IF_ERROR(c->Concatenate(c->MakeShape({c->UnknownDim()}), + element_shape_from_tensor_shape, + &element_shape_from_tensor_shape)); + shape_inference::ShapeHandle element_shape; + TF_RETURN_IF_ERROR(c->MakeShapeFromShapeTensorTreatScalarAsUnknownShape( + 1, &element_shape)); + TF_RETURN_IF_ERROR(c->Merge(element_shape_from_tensor_shape, + element_shape, + &element_shape_from_tensor_shape)); + c->set_output_handle_shapes_and_types( + 0, std::vector{ + {element_shape, element_dtype}}); + return Status::OK(); + }); + REGISTER_OP("TensorListFromTensor") .Input("tensor: element_dtype") .Input("element_shape: shape_type") @@ -205,17 +292,20 @@ REGISTER_OP("TensorListFromTensor") .Attr("shape_type: {int32, int64}") .SetShapeFn([](shape_inference::InferenceContext* c) { c->set_output(0, c->Scalar()); - DataType t; - TF_RETURN_IF_ERROR(c->GetAttr("element_dtype", &t)); - shape_inference::ShapeHandle s = c->input(0); - shape_inference::ShapeHandle o; - TF_RETURN_IF_ERROR(c->Subshape(s, 1, &o)); + DataType element_dtype; + TF_RETURN_IF_ERROR(c->GetAttr("element_dtype", &element_dtype)); + shape_inference::ShapeHandle tensor_shape = c->input(0); + shape_inference::ShapeHandle tensor_shape_except_first_dim; + TF_RETURN_IF_ERROR( + c->Subshape(tensor_shape, 1, &tensor_shape_except_first_dim)); shape_inference::ShapeHandle element_shape; TF_RETURN_IF_ERROR(c->MakeShapeFromShapeTensorTreatScalarAsUnknownShape( 1, &element_shape)); - TF_RETURN_IF_ERROR(c->Merge(o, element_shape, &o)); + TF_RETURN_IF_ERROR(c->Merge(tensor_shape_except_first_dim, element_shape, + &tensor_shape_except_first_dim)); c->set_output_handle_shapes_and_types( - 0, std::vector{{element_shape, t}}); + 0, std::vector{ + {element_shape, element_dtype}}); return Status::OK(); }); @@ -241,13 +331,14 @@ REGISTER_OP("TensorListReserve") .Attr("shape_type: {int32, int64}") .SetShapeFn([](shape_inference::InferenceContext* c) { c->set_output(0, c->Scalar()); - shape_inference::ShapeHandle s; - TF_RETURN_IF_ERROR( - c->MakeShapeFromShapeTensorTreatScalarAsUnknownShape(0, &s)); - DataType t; - TF_RETURN_IF_ERROR(c->GetAttr("element_dtype", &t)); + shape_inference::ShapeHandle element_shape; + TF_RETURN_IF_ERROR(c->MakeShapeFromShapeTensorTreatScalarAsUnknownShape( + 0, &element_shape)); + DataType element_dtype; + TF_RETURN_IF_ERROR(c->GetAttr("element_dtype", &element_dtype)); c->set_output_handle_shapes_and_types( - 0, std::vector{{s, t}}); + 0, std::vector{ + {element_shape, element_dtype}}); return Status::OK(); }); @@ -257,17 +348,17 @@ REGISTER_OP("TensorListGetItem") .Output("item: element_dtype") .Attr("element_dtype: type") .SetShapeFn([](shape_inference::InferenceContext* c) { - DataType t; - TF_RETURN_IF_ERROR(c->GetAttr("element_dtype", &t)); + DataType element_dtype; + TF_RETURN_IF_ERROR(c->GetAttr("element_dtype", &element_dtype)); auto* handle_data = c->input_handle_shapes_and_types(0); shape_inference::ShapeHandle element_shape = c->UnknownShape(); if (handle_data != nullptr) { const shape_inference::ShapeAndType& list_shape_type = (*handle_data)[0]; element_shape = list_shape_type.shape; - if (list_shape_type.dtype != t) { + if (list_shape_type.dtype != element_dtype) { return errors::InvalidArgument("Expected list with element dtype ", - DataTypeString(t), + DataTypeString(element_dtype), " but got list with element dtype ", DataTypeString(list_shape_type.dtype)); } @@ -283,17 +374,19 @@ REGISTER_OP("TensorListSetItem") .Output("output_handle: variant") .Attr("element_dtype: type") .SetShapeFn([](shape_inference::InferenceContext* c) { - DataType t; - TF_RETURN_IF_ERROR(c->GetAttr("element_dtype", &t)); + DataType element_dtype; + TF_RETURN_IF_ERROR(c->GetAttr("element_dtype", &element_dtype)); auto* handle_data = c->input_handle_shapes_and_types(0); c->set_output(0, c->Scalar()); if (handle_data == nullptr) { - c->set_output_handle_shapes_and_types(0, {{c->UnknownShape(), t}}); + c->set_output_handle_shapes_and_types( + 0, {{c->UnknownShape(), element_dtype}}); return Status::OK(); } const shape_inference::ShapeAndType& list_shape_type = (*handle_data)[0]; - shape_inference::ShapeHandle s = c->input(2); - TF_RETURN_IF_ERROR(c->Merge(s, list_shape_type.shape, &s)); + shape_inference::ShapeHandle item_shape = c->input(2); + TF_RETURN_IF_ERROR( + c->Merge(item_shape, list_shape_type.shape, &item_shape)); c->set_output_handle_shapes_and_types(0, *handle_data); return Status::OK(); }); @@ -304,17 +397,17 @@ REGISTER_OP("TensorListGather") .Output("values: element_dtype") .Attr("element_dtype: type") .SetShapeFn([](shape_inference::InferenceContext* c) { - DataType t; - TF_RETURN_IF_ERROR(c->GetAttr("element_dtype", &t)); + DataType element_dtype; + TF_RETURN_IF_ERROR(c->GetAttr("element_dtype", &element_dtype)); auto* handle_data = c->input_handle_shapes_and_types(0); shape_inference::ShapeHandle element_shape = c->UnknownShape(); if (handle_data != nullptr) { const shape_inference::ShapeAndType& list_shape_type = (*handle_data)[0]; element_shape = list_shape_type.shape; - if (list_shape_type.dtype != t) { + if (list_shape_type.dtype != element_dtype) { return errors::InvalidArgument("Expected list with element dtype ", - DataTypeString(t), + DataTypeString(element_dtype), " but got list with element dtype ", DataTypeString(list_shape_type.dtype)); } @@ -333,12 +426,13 @@ REGISTER_OP("TensorListScatter") .Attr("element_dtype: type") .Attr("shape_type: {int32, int64}") .SetShapeFn([](shape_inference::InferenceContext* c) { - DataType t; - TF_RETURN_IF_ERROR(c->GetAttr("element_dtype", &t)); - shape_inference::ShapeHandle s; - TF_RETURN_IF_ERROR( - c->MakeShapeFromShapeTensorTreatScalarAsUnknownShape(2, &s)); - c->set_output_handle_shapes_and_types(0, {{s, t}}); + DataType element_dtype; + TF_RETURN_IF_ERROR(c->GetAttr("element_dtype", &element_dtype)); + shape_inference::ShapeHandle element_shape; + TF_RETURN_IF_ERROR(c->MakeShapeFromShapeTensorTreatScalarAsUnknownShape( + 2, &element_shape)); + c->set_output_handle_shapes_and_types(0, + {{element_shape, element_dtype}}); c->set_output(0, c->Scalar()); return Status::OK(); }); @@ -354,28 +448,29 @@ REGISTER_OP("TensorListConcatLists") TF_RETURN_IF_ERROR(c->Merge(input_a, input_b, &input_a)); c->set_output(0, input_a); - DataType t; - TF_RETURN_IF_ERROR(c->GetAttr("element_dtype", &t)); + DataType element_dtype; + TF_RETURN_IF_ERROR(c->GetAttr("element_dtype", &element_dtype)); auto* handle_data_a = c->input_handle_shapes_and_types(0); auto* handle_data_b = c->input_handle_shapes_and_types(1); if (handle_data_a == nullptr && handle_data_b == nullptr) { - c->set_output_handle_shapes_and_types(0, {{c->UnknownShape(), t}}); + c->set_output_handle_shapes_and_types( + 0, {{c->UnknownShape(), element_dtype}}); return Status::OK(); } shape_inference::ShapeAndType list_shape_type_a = (handle_data_a) ? handle_data_a->at(0) : handle_data_b->at(0); const shape_inference::ShapeAndType& list_shape_type_b = (handle_data_b) ? handle_data_b->at(0) : handle_data_a->at(0); - if (list_shape_type_a.dtype != t) { + if (list_shape_type_a.dtype != element_dtype) { return errors::InvalidArgument("input_a.type != element_dtype: ", DataTypeString(list_shape_type_a.dtype), - " vs. ", DataTypeString(t)); + " vs. ", DataTypeString(element_dtype)); } - if (list_shape_type_b.dtype != t) { + if (list_shape_type_b.dtype != element_dtype) { return errors::InvalidArgument("input_b.type != element_dtype: ", DataTypeString(list_shape_type_b.dtype), - " vs. ", DataTypeString(t)); + " vs. ", DataTypeString(element_dtype)); } TF_RETURN_IF_ERROR(c->Merge(list_shape_type_a.shape, list_shape_type_b.shape, diff --git a/tensorflow/core/ops/nn_ops.cc b/tensorflow/core/ops/nn_ops.cc index 4dfd95b019..7ff9c46943 100644 --- a/tensorflow/core/ops/nn_ops.cc +++ b/tensorflow/core/ops/nn_ops.cc @@ -327,6 +327,9 @@ REGISTER_OP("_FusedConv2D") .Attr(GetConvnetDataFormatAttrString()) .Attr("dilations: list(int) = [1, 1, 1, 1]") .Attr("fused_ops: list(string) = []") + // Attributes for the FusedBatchNorm ------------------------------------ // + .Attr("epsilon: float = 0.0001") + // ---------------------------------------------------------------------- // .SetShapeFn(shape_inference::Conv2DShape) .Doc(R"doc( *NOTE*: Do not invoke this operator directly in Python. Grappler is @@ -2190,11 +2193,7 @@ REGISTER_OP("_MklLRN") .Input("input: T") .Input("mkl_input: uint8") .Output("output: T") -#ifdef INTEL_MKL_ML_ONLY - .Output("workspace: T") -#else .Output("workspace: uint8") -#endif .Output("mkl_output: uint8") .Output("mkl_workspace: uint8") .Attr("depth_radius: int = 5") @@ -2218,11 +2217,7 @@ REGISTER_OP("_MklLRNGrad") .Input("input_grads: T") .Input("input_image: T") .Input("output_image: T") -#ifdef INTEL_MKL_ML_ONLY - .Input("workspace: T") -#else .Input("workspace: uint8") -#endif .Input("mkl_input_grads: uint8") .Input("mkl_input_image: uint8") .Input("mkl_output_image: uint8") diff --git a/tensorflow/core/ops/ops.pbtxt b/tensorflow/core/ops/ops.pbtxt index 430212ee1d..a9c712e138 100644 --- a/tensorflow/core/ops/ops.pbtxt +++ b/tensorflow/core/ops/ops.pbtxt @@ -4944,33 +4944,6 @@ op { type: "list(float)" } } -op { - name: "BytesProducedStatsDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "tag" - type: DT_STRING - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } -} op { name: "CTCBeamSearchDecoder" input_arg { @@ -7915,21 +7888,6 @@ op { minimum: 1 } } -op { - name: "DatasetToTFRecord" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "filename" - type: DT_STRING - } - input_arg { - name: "compression_type" - type: DT_STRING - } -} op { name: "DebugGradientIdentity" input_arg { @@ -8599,37 +8557,6 @@ op { } } } -op { - name: "DenseToSparseBatchDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "batch_size" - type: DT_INT64 - } - input_arg { - name: "row_shape" - type: DT_INT64 - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } -} op { name: "DenseToSparseSetOperation" input_arg { @@ -9834,24 +9761,6 @@ op { type: DT_STRING } } -op { - name: "EnqueueInQueueDataset" - input_arg { - name: "queue" - type: DT_VARIANT - } - input_arg { - name: "components" - type_list_attr: "Tcomponents" - } - attr { - name: "Tcomponents" - type: "list(type)" - has_minimum: true - minimum: 1 - } - is_stateful: true -} op { name: "EnsureShape" input_arg { @@ -10089,6 +9998,33 @@ op { minimum: 1 } } +op { + name: "ExperimentalBytesProducedStatsDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "tag" + type: DT_STRING + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } +} op { name: "ExperimentalCSVDataset" input_arg { @@ -10154,6 +10090,52 @@ op { } is_stateful: true } +op { + name: "ExperimentalDatasetToTFRecord" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "filename" + type: DT_STRING + } + input_arg { + name: "compression_type" + type: DT_STRING + } +} +op { + name: "ExperimentalDenseToSparseBatchDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "batch_size" + type: DT_INT64 + } + input_arg { + name: "row_shape" + type: DT_INT64 + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } +} op { name: "ExperimentalDirectedInterleaveDataset" input_arg { @@ -10189,50 +10171,66 @@ op { } } op { - name: "ExperimentalFunctionBufferingResource" + name: "ExperimentalGroupByReducerDataset" input_arg { - name: "string_arg" - type: DT_STRING + name: "input_dataset" + type: DT_VARIANT } input_arg { - name: "target_device" - type: DT_STRING + name: "key_func_other_arguments" + type_list_attr: "Tkey_func_other_arguments" + } + input_arg { + name: "init_func_other_arguments" + type_list_attr: "Tinit_func_other_arguments" + } + input_arg { + name: "reduce_func_other_arguments" + type_list_attr: "Treduce_func_other_arguments" + } + input_arg { + name: "finalize_func_other_arguments" + type_list_attr: "Tfinalize_func_other_arguments" } output_arg { - name: "resource" - type: DT_RESOURCE + name: "handle" + type: DT_VARIANT } attr { - name: "shared_name" - type: "string" + name: "key_func" + type: "func" } attr { - name: "container" - type: "string" + name: "init_func" + type: "func" } attr { - name: "f" + name: "reduce_func" type: "func" } attr { - name: "buffer_size" - type: "int" + name: "finalize_func" + type: "func" } attr { - name: "output_types" + name: "Tkey_func_other_arguments" type: "list(type)" + has_minimum: true } - is_stateful: true -} -op { - name: "ExperimentalFunctionBufferingResourceGetNext" - input_arg { - name: "function_buffer_resource" - type: DT_RESOURCE + attr { + name: "Tinit_func_other_arguments" + type: "list(type)" + has_minimum: true } - output_arg { - name: "output" - type_list_attr: "output_types" + attr { + name: "Treduce_func_other_arguments" + type: "list(type)" + has_minimum: true + } + attr { + name: "Tfinalize_func_other_arguments" + type: "list(type)" + has_minimum: true } attr { name: "output_types" @@ -10240,48 +10238,108 @@ op { has_minimum: true minimum: 1 } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } is_stateful: true } op { - name: "ExperimentalFunctionBufferingResourceReset" + name: "ExperimentalGroupByWindowDataset" input_arg { - name: "function_buffer_resource" - type: DT_RESOURCE + name: "input_dataset" + type: DT_VARIANT } - is_stateful: true -} -op { - name: "ExperimentalIdentityIndexedDataset" input_arg { - name: "size" - type: DT_UINT64 + name: "key_func_other_arguments" + type_list_attr: "Tkey_func_other_arguments" } - output_arg { - name: "handle" - type: DT_VARIANT + input_arg { + name: "reduce_func_other_arguments" + type_list_attr: "Treduce_func_other_arguments" } - is_stateful: true -} -op { - name: "ExperimentalIgnoreErrorsDataset" input_arg { - name: "input_dataset" - type: DT_VARIANT + name: "window_size_func_other_arguments" + type_list_attr: "Twindow_size_func_other_arguments" } output_arg { name: "handle" type: DT_VARIANT } attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 + name: "key_func" + type: "func" } attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true + name: "reduce_func" + type: "func" + } + attr { + name: "window_size_func" + type: "func" + } + attr { + name: "Tkey_func_other_arguments" + type: "list(type)" + has_minimum: true + } + attr { + name: "Treduce_func_other_arguments" + type: "list(type)" + has_minimum: true + } + attr { + name: "Twindow_size_func_other_arguments" + type: "list(type)" + has_minimum: true + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } +} +op { + name: "ExperimentalIdentityIndexedDataset" + input_arg { + name: "size" + type: DT_UINT64 + } + output_arg { + name: "handle" + type: DT_VARIANT + } + is_stateful: true +} +op { + name: "ExperimentalIgnoreErrorsDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true minimum: 1 } } @@ -10343,9 +10401,500 @@ op { name: "filenames" type: DT_STRING } - output_arg { - name: "handle" - type: DT_VARIANT + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } + is_stateful: true +} +op { + name: "ExperimentalLatencyStatsDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "tag" + type: DT_STRING + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } +} +op { + name: "ExperimentalMapAndBatchDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "other_arguments" + type_list_attr: "Targuments" + } + input_arg { + name: "batch_size" + type: DT_INT64 + } + input_arg { + name: "num_parallel_calls" + type: DT_INT64 + } + input_arg { + name: "drop_remainder" + type: DT_BOOL + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "f" + type: "func" + } + attr { + name: "Targuments" + type: "list(type)" + has_minimum: true + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } +} +op { + name: "ExperimentalMapDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "other_arguments" + type_list_attr: "Targuments" + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "f" + type: "func" + } + attr { + name: "Targuments" + type: "list(type)" + has_minimum: true + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } + attr { + name: "use_inter_op_parallelism" + type: "bool" + default_value { + b: true + } + } +} +op { + name: "ExperimentalMatchingFilesDataset" + input_arg { + name: "patterns" + type: DT_STRING + } + output_arg { + name: "handle" + type: DT_VARIANT + } + is_stateful: true +} +op { + name: "ExperimentalMaterializedIndexDatasetHandle" + output_arg { + name: "handle" + type: DT_RESOURCE + } + attr { + name: "container" + type: "string" + } + attr { + name: "shared_name" + type: "string" + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } + is_stateful: true +} +op { + name: "ExperimentalMaxIntraOpParallelismDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "max_intra_op_parallelism" + type: DT_INT64 + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } +} +op { + name: "ExperimentalNonSerializableDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } +} +op { + name: "ExperimentalNumaMapAndBatchDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "other_arguments" + type_list_attr: "Targuments" + } + input_arg { + name: "batch_size" + type: DT_INT64 + } + input_arg { + name: "num_parallel_calls" + type: DT_INT64 + } + input_arg { + name: "drop_remainder" + type: DT_BOOL + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "f" + type: "func" + } + attr { + name: "Targuments" + type: "list(type)" + has_minimum: true + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } +} +op { + name: "ExperimentalParallelInterleaveDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "other_arguments" + type_list_attr: "Targuments" + } + input_arg { + name: "cycle_length" + type: DT_INT64 + } + input_arg { + name: "block_length" + type: DT_INT64 + } + input_arg { + name: "sloppy" + type: DT_BOOL + } + input_arg { + name: "buffer_output_elements" + type: DT_INT64 + } + input_arg { + name: "prefetch_input_elements" + type: DT_INT64 + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "f" + type: "func" + } + attr { + name: "Targuments" + type: "list(type)" + has_minimum: true + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } +} +op { + name: "ExperimentalParseExampleDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "num_parallel_calls" + type: DT_INT64 + } + input_arg { + name: "dense_defaults" + type_list_attr: "Tdense" + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "sparse_keys" + type: "list(string)" + has_minimum: true + } + attr { + name: "dense_keys" + type: "list(string)" + has_minimum: true + } + attr { + name: "sparse_types" + type: "list(type)" + has_minimum: true + allowed_values { + list { + type: DT_FLOAT + type: DT_INT64 + type: DT_STRING + } + } + } + attr { + name: "Tdense" + type: "list(type)" + has_minimum: true + allowed_values { + list { + type: DT_FLOAT + type: DT_INT64 + type: DT_STRING + } + } + } + attr { + name: "dense_shapes" + type: "list(shape)" + has_minimum: true + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } + attr { + name: "sloppy" + type: "bool" + default_value { + b: false + } + } +} +op { + name: "ExperimentalPrivateThreadPoolDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "num_threads" + type: DT_INT64 + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } +} +op { + name: "ExperimentalRandomDataset" + input_arg { + name: "seed" + type: DT_INT64 + } + input_arg { + name: "seed2" + type: DT_INT64 + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } + is_stateful: true +} +op { + name: "ExperimentalScanDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "initial_state" + type_list_attr: "Tstate" + } + input_arg { + name: "other_arguments" + type_list_attr: "Targuments" + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "f" + type: "func" + } + attr { + name: "Tstate" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "Targuments" + type: "list(type)" + has_minimum: true } attr { name: "output_types" @@ -10359,21 +10908,28 @@ op { has_minimum: true minimum: 1 } - is_stateful: true } op { - name: "ExperimentalMaterializedIndexDatasetHandle" - output_arg { - name: "handle" + name: "ExperimentalSetStatsAggregatorDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + input_arg { + name: "stats_aggregator" type: DT_RESOURCE } - attr { - name: "container" - type: "string" + input_arg { + name: "tag" + type: DT_STRING } - attr { - name: "shared_name" - type: "string" + input_arg { + name: "counter_prefix" + type: DT_STRING + } + output_arg { + name: "handle" + type: DT_VARIANT } attr { name: "output_types" @@ -10390,11 +10946,15 @@ op { is_stateful: true } op { - name: "ExperimentalNonSerializableDataset" + name: "ExperimentalSleepDataset" input_arg { name: "input_dataset" type: DT_VARIANT } + input_arg { + name: "sleep_microseconds" + type: DT_INT64 + } output_arg { name: "handle" type: DT_VARIANT @@ -10413,40 +10973,27 @@ op { } } op { - name: "ExperimentalNumaMapAndBatchDataset" + name: "ExperimentalSlidingWindowDataset" input_arg { name: "input_dataset" type: DT_VARIANT } input_arg { - name: "other_arguments" - type_list_attr: "Targuments" - } - input_arg { - name: "batch_size" + name: "window_size" type: DT_INT64 } input_arg { - name: "num_parallel_calls" + name: "window_shift" type: DT_INT64 } input_arg { - name: "drop_remainder" - type: DT_BOOL + name: "window_stride" + type: DT_INT64 } output_arg { name: "handle" type: DT_VARIANT } - attr { - name: "f" - type: "func" - } - attr { - name: "Targuments" - type: "list(type)" - has_minimum: true - } attr { name: "output_types" type: "list(type)" @@ -10461,14 +11008,18 @@ op { } } op { - name: "ExperimentalSleepDataset" + name: "ExperimentalSqlDataset" input_arg { - name: "input_dataset" - type: DT_VARIANT + name: "driver_name" + type: DT_STRING } input_arg { - name: "sleep_microseconds" - type: DT_INT64 + name: "data_source_name" + type: DT_STRING + } + input_arg { + name: "query" + type: DT_STRING } output_arg { name: "handle" @@ -10486,6 +11037,41 @@ op { has_minimum: true minimum: 1 } + is_stateful: true +} +op { + name: "ExperimentalStatsAggregatorHandle" + output_arg { + name: "handle" + type: DT_RESOURCE + } + attr { + name: "container" + type: "string" + default_value { + s: "" + } + } + attr { + name: "shared_name" + type: "string" + default_value { + s: "" + } + } + is_stateful: true +} +op { + name: "ExperimentalStatsAggregatorSummary" + input_arg { + name: "iterator" + type: DT_RESOURCE + } + output_arg { + name: "summary" + type: DT_STRING + } + is_stateful: true } op { name: "ExperimentalThreadPoolDataset" @@ -10552,6 +11138,29 @@ op { } is_stateful: true } +op { + name: "ExperimentalUnbatchDataset" + input_arg { + name: "input_dataset" + type: DT_VARIANT + } + output_arg { + name: "handle" + type: DT_VARIANT + } + attr { + name: "output_types" + type: "list(type)" + has_minimum: true + minimum: 1 + } + attr { + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } +} op { name: "ExperimentalUniqueDataset" input_arg { @@ -12708,178 +13317,40 @@ op { } } } -} -op { - name: "GreaterEqual" - input_arg { - name: "x" - type_attr: "T" - } - input_arg { - name: "y" - type_attr: "T" - } - output_arg { - name: "z" - type: DT_BOOL - } - attr { - name: "T" - type: "type" - allowed_values { - list { - type: DT_FLOAT - type: DT_DOUBLE - type: DT_INT32 - type: DT_UINT8 - type: DT_INT16 - type: DT_INT8 - type: DT_INT64 - type: DT_BFLOAT16 - type: DT_UINT16 - type: DT_HALF - type: DT_UINT32 - type: DT_UINT64 - } - } - } -} -op { - name: "GroupByReducerDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "key_func_other_arguments" - type_list_attr: "Tkey_func_other_arguments" - } - input_arg { - name: "init_func_other_arguments" - type_list_attr: "Tinit_func_other_arguments" - } - input_arg { - name: "reduce_func_other_arguments" - type_list_attr: "Treduce_func_other_arguments" - } - input_arg { - name: "finalize_func_other_arguments" - type_list_attr: "Tfinalize_func_other_arguments" - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "key_func" - type: "func" - } - attr { - name: "init_func" - type: "func" - } - attr { - name: "reduce_func" - type: "func" - } - attr { - name: "finalize_func" - type: "func" - } - attr { - name: "Tkey_func_other_arguments" - type: "list(type)" - has_minimum: true - } - attr { - name: "Tinit_func_other_arguments" - type: "list(type)" - has_minimum: true - } - attr { - name: "Treduce_func_other_arguments" - type: "list(type)" - has_minimum: true - } - attr { - name: "Tfinalize_func_other_arguments" - type: "list(type)" - has_minimum: true - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } - is_stateful: true -} -op { - name: "GroupByWindowDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "key_func_other_arguments" - type_list_attr: "Tkey_func_other_arguments" - } +} +op { + name: "GreaterEqual" input_arg { - name: "reduce_func_other_arguments" - type_list_attr: "Treduce_func_other_arguments" + name: "x" + type_attr: "T" } input_arg { - name: "window_size_func_other_arguments" - type_list_attr: "Twindow_size_func_other_arguments" + name: "y" + type_attr: "T" } output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "key_func" - type: "func" - } - attr { - name: "reduce_func" - type: "func" - } - attr { - name: "window_size_func" - type: "func" - } - attr { - name: "Tkey_func_other_arguments" - type: "list(type)" - has_minimum: true - } - attr { - name: "Treduce_func_other_arguments" - type: "list(type)" - has_minimum: true - } - attr { - name: "Twindow_size_func_other_arguments" - type: "list(type)" - has_minimum: true - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 + name: "z" + type: DT_BOOL } attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 + name: "T" + type: "type" + allowed_values { + list { + type: DT_FLOAT + type: DT_DOUBLE + type: DT_INT32 + type: DT_UINT8 + type: DT_INT16 + type: DT_INT8 + type: DT_INT64 + type: DT_BFLOAT16 + type: DT_UINT16 + type: DT_HALF + type: DT_UINT32 + type: DT_UINT64 + } + } } } op { @@ -14422,33 +14893,6 @@ op { } } } -op { - name: "LatencyStatsDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "tag" - type: DT_STRING - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } -} op { name: "LeakyRelu" input_arg { @@ -15319,102 +15763,6 @@ op { } is_stateful: true } -op { - name: "MapAndBatchDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "other_arguments" - type_list_attr: "Targuments" - } - input_arg { - name: "batch_size" - type: DT_INT64 - } - input_arg { - name: "num_parallel_batches" - type: DT_INT64 - } - input_arg { - name: "drop_remainder" - type: DT_BOOL - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "f" - type: "func" - } - attr { - name: "Targuments" - type: "list(type)" - has_minimum: true - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } -} -op { - name: "MapAndBatchDatasetV2" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "other_arguments" - type_list_attr: "Targuments" - } - input_arg { - name: "batch_size" - type: DT_INT64 - } - input_arg { - name: "num_parallel_calls" - type: DT_INT64 - } - input_arg { - name: "drop_remainder" - type: DT_BOOL - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "f" - type: "func" - } - attr { - name: "Targuments" - type: "list(type)" - has_minimum: true - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } -} op { name: "MapClear" attr { @@ -15894,18 +16242,6 @@ op { type: DT_STRING } } -op { - name: "MatchingFilesDataset" - input_arg { - name: "patterns" - type: DT_STRING - } - output_arg { - name: "handle" - type: DT_VARIANT - } - is_stateful: true -} op { name: "MatrixBandPart" input_arg { @@ -19429,80 +19765,24 @@ op { type: DT_INT32 number_attr: "N" } - input_arg { - name: "data" - type_attr: "T" - number_attr: "N" - } - output_arg { - name: "merged" - type_attr: "T" - } - attr { - name: "N" - type: "int" - has_minimum: true - minimum: 1 - } - attr { - name: "T" - type: "type" - } -} -op { - name: "ParallelInterleaveDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "other_arguments" - type_list_attr: "Targuments" - } - input_arg { - name: "cycle_length" - type: DT_INT64 - } - input_arg { - name: "block_length" - type: DT_INT64 - } - input_arg { - name: "sloppy" - type: DT_BOOL - } - input_arg { - name: "buffer_output_elements" - type: DT_INT64 - } - input_arg { - name: "prefetch_input_elements" - type: DT_INT64 - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "f" - type: "func" + input_arg { + name: "data" + type_attr: "T" + number_attr: "N" } - attr { - name: "Targuments" - type: "list(type)" - has_minimum: true + output_arg { + name: "merged" + type_attr: "T" } attr { - name: "output_types" - type: "list(type)" + name: "N" + type: "int" has_minimum: true minimum: 1 } attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 + name: "T" + type: "type" } } op { @@ -19760,83 +20040,6 @@ op { has_minimum: true } } -op { - name: "ParseExampleDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "num_parallel_calls" - type: DT_INT64 - } - input_arg { - name: "dense_defaults" - type_list_attr: "Tdense" - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "sparse_keys" - type: "list(string)" - has_minimum: true - } - attr { - name: "dense_keys" - type: "list(string)" - has_minimum: true - } - attr { - name: "sparse_types" - type: "list(type)" - has_minimum: true - allowed_values { - list { - type: DT_FLOAT - type: DT_INT64 - type: DT_STRING - } - } - } - attr { - name: "Tdense" - type: "list(type)" - has_minimum: true - allowed_values { - list { - type: DT_FLOAT - type: DT_INT64 - type: DT_STRING - } - } - } - attr { - name: "dense_shapes" - type: "list(shape)" - has_minimum: true - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } - attr { - name: "sloppy" - type: "bool" - default_value { - b: false - } - } -} op { name: "ParseSequenceExample" input_arg { @@ -20340,6 +20543,13 @@ op { s: "" } } + attr { + name: "config_proto" + type: "string" + default_value { + s: "" + } + } attr { name: "executor_type" type: "string" @@ -20516,48 +20726,6 @@ op { minimum: 1 } } -op { - name: "PrependFromQueueAndPaddedBatchDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "batch_size" - type: DT_INT64 - } - input_arg { - name: "padded_shapes" - type: DT_INT64 - number_attr: "N" - } - input_arg { - name: "padding_values" - type_list_attr: "Toutput_types" - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "Toutput_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } - attr { - name: "N" - type: "int" - has_minimum: true - minimum: 1 - } -} op { name: "PreventGradient" input_arg { @@ -20986,6 +21154,19 @@ op { } } } + attr { + name: "round_mode" + type: "string" + default_value { + s: "HALF_TO_EVEN" + } + allowed_values { + list { + s: "HALF_TO_EVEN" + s: "HALF_UP" + } + } + } } op { name: "QuantizeAndDequantizeV3" @@ -22816,34 +22997,6 @@ op { } is_stateful: true } -op { - name: "RandomDataset" - input_arg { - name: "seed" - type: DT_INT64 - } - input_arg { - name: "seed2" - type: DT_INT64 - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } - is_stateful: true -} op { name: "RandomGamma" input_arg { @@ -24900,8 +25053,83 @@ op { type_attr: "T" } input_arg { - name: "grad" - type_attr: "T" + name: "grad" + type_attr: "T" + } + attr { + name: "T" + type: "type" + allowed_values { + list { + type: DT_FLOAT + type: DT_DOUBLE + type: DT_INT32 + type: DT_UINT8 + type: DT_INT16 + type: DT_INT8 + type: DT_COMPLEX64 + type: DT_INT64 + type: DT_QINT8 + type: DT_QUINT8 + type: DT_QINT32 + type: DT_BFLOAT16 + type: DT_UINT16 + type: DT_COMPLEX128 + type: DT_HALF + type: DT_UINT32 + type: DT_UINT64 + } + } + } + attr { + name: "use_locking" + type: "bool" + default_value { + b: false + } + } + attr { + name: "update_slots" + type: "bool" + default_value { + b: true + } + } + is_stateful: true +} +op { + name: "ResourceApplyAdagradDA" + input_arg { + name: "var" + type: DT_RESOURCE + } + input_arg { + name: "gradient_accumulator" + type: DT_RESOURCE + } + input_arg { + name: "gradient_squared_accumulator" + type: DT_RESOURCE + } + input_arg { + name: "grad" + type_attr: "T" + } + input_arg { + name: "lr" + type_attr: "T" + } + input_arg { + name: "l1" + type_attr: "T" + } + input_arg { + name: "l2" + type_attr: "T" + } + input_arg { + name: "global_step" + type: DT_INT64 } attr { name: "T" @@ -24935,31 +25163,28 @@ op { b: false } } - attr { - name: "update_slots" - type: "bool" - default_value { - b: true - } - } is_stateful: true } op { - name: "ResourceApplyAdagradDA" + name: "ResourceApplyAdam" input_arg { name: "var" type: DT_RESOURCE } input_arg { - name: "gradient_accumulator" + name: "m" type: DT_RESOURCE } input_arg { - name: "gradient_squared_accumulator" + name: "v" type: DT_RESOURCE } input_arg { - name: "grad" + name: "beta1_power" + type_attr: "T" + } + input_arg { + name: "beta2_power" type_attr: "T" } input_arg { @@ -24967,16 +25192,20 @@ op { type_attr: "T" } input_arg { - name: "l1" + name: "beta1" type_attr: "T" } input_arg { - name: "l2" + name: "beta2" type_attr: "T" } input_arg { - name: "global_step" - type: DT_INT64 + name: "epsilon" + type_attr: "T" + } + input_arg { + name: "grad" + type_attr: "T" } attr { name: "T" @@ -25010,10 +25239,17 @@ op { b: false } } + attr { + name: "use_nesterov" + type: "bool" + default_value { + b: false + } + } is_stateful: true } op { - name: "ResourceApplyAdam" + name: "ResourceApplyAdamWithAmsgrad" input_arg { name: "var" type: DT_RESOURCE @@ -25026,6 +25262,10 @@ op { name: "v" type: DT_RESOURCE } + input_arg { + name: "vhat" + type: DT_RESOURCE + } input_arg { name: "beta1_power" type_attr: "T" @@ -25086,13 +25326,6 @@ op { b: false } } - attr { - name: "use_nesterov" - type: "bool" - default_value { - b: false - } - } is_stateful: true } op { @@ -25419,6 +25652,69 @@ op { } is_stateful: true } +op { + name: "ResourceApplyKerasMomentum" + input_arg { + name: "var" + type: DT_RESOURCE + } + input_arg { + name: "accum" + type: DT_RESOURCE + } + input_arg { + name: "lr" + type_attr: "T" + } + input_arg { + name: "grad" + type_attr: "T" + } + input_arg { + name: "momentum" + type_attr: "T" + } + attr { + name: "T" + type: "type" + allowed_values { + list { + type: DT_FLOAT + type: DT_DOUBLE + type: DT_INT32 + type: DT_UINT8 + type: DT_INT16 + type: DT_INT8 + type: DT_COMPLEX64 + type: DT_INT64 + type: DT_QINT8 + type: DT_QUINT8 + type: DT_QINT32 + type: DT_BFLOAT16 + type: DT_UINT16 + type: DT_COMPLEX128 + type: DT_HALF + type: DT_UINT32 + type: DT_UINT64 + } + } + } + attr { + name: "use_locking" + type: "bool" + default_value { + b: false + } + } + attr { + name: "use_nesterov" + type: "bool" + default_value { + b: false + } + } + is_stateful: true +} op { name: "ResourceApplyMomentum" input_arg { @@ -26690,6 +26986,83 @@ op { } is_stateful: true } +op { + name: "ResourceSparseApplyKerasMomentum" + input_arg { + name: "var" + type: DT_RESOURCE + } + input_arg { + name: "accum" + type: DT_RESOURCE + } + input_arg { + name: "lr" + type_attr: "T" + } + input_arg { + name: "grad" + type_attr: "T" + } + input_arg { + name: "indices" + type_attr: "Tindices" + } + input_arg { + name: "momentum" + type_attr: "T" + } + attr { + name: "T" + type: "type" + allowed_values { + list { + type: DT_FLOAT + type: DT_DOUBLE + type: DT_INT32 + type: DT_UINT8 + type: DT_INT16 + type: DT_INT8 + type: DT_COMPLEX64 + type: DT_INT64 + type: DT_QINT8 + type: DT_QUINT8 + type: DT_QINT32 + type: DT_BFLOAT16 + type: DT_UINT16 + type: DT_COMPLEX128 + type: DT_HALF + type: DT_UINT32 + type: DT_UINT64 + } + } + } + attr { + name: "Tindices" + type: "type" + allowed_values { + list { + type: DT_INT32 + type: DT_INT64 + } + } + } + attr { + name: "use_locking" + type: "bool" + default_value { + b: false + } + } + attr { + name: "use_nesterov" + type: "bool" + default_value { + b: false + } + } + is_stateful: true +} op { name: "ResourceSparseApplyMomentum" input_arg { @@ -27768,71 +28141,25 @@ op { name: "summary" type: DT_STRING } - attr { - name: "T" - type: "type" - allowed_values { - list { - type: DT_FLOAT - type: DT_DOUBLE - type: DT_INT32 - type: DT_UINT8 - type: DT_INT16 - type: DT_INT8 - type: DT_INT64 - type: DT_BFLOAT16 - type: DT_UINT16 - type: DT_HALF - type: DT_UINT32 - type: DT_UINT64 - } - } - } -} -op { - name: "ScanDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "initial_state" - type_list_attr: "Tstate" - } - input_arg { - name: "other_arguments" - type_list_attr: "Targuments" - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "f" - type: "func" - } - attr { - name: "Tstate" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "Targuments" - type: "list(type)" - has_minimum: true - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 + attr { + name: "T" + type: "type" + allowed_values { + list { + type: DT_FLOAT + type: DT_DOUBLE + type: DT_INT32 + type: DT_UINT8 + type: DT_INT16 + type: DT_INT8 + type: DT_INT64 + type: DT_BFLOAT16 + type: DT_UINT16 + type: DT_HALF + type: DT_UINT32 + type: DT_UINT64 + } + } } } op { @@ -29272,42 +29599,6 @@ op { } } } -op { - name: "SetStatsAggregatorDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "stats_aggregator" - type: DT_RESOURCE - } - input_arg { - name: "tag" - type: DT_STRING - } - input_arg { - name: "counter_prefix" - type: DT_STRING - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } - is_stateful: true -} op { name: "Shape" input_arg { @@ -29771,41 +30062,6 @@ op { } } } -op { - name: "SlideDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - input_arg { - name: "window_size" - type: DT_INT64 - } - input_arg { - name: "window_shift" - type: DT_INT64 - } - input_arg { - name: "window_stride" - type: DT_INT64 - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } -} op { name: "Snapshot" input_arg { @@ -32996,38 +33252,6 @@ op { } } } -op { - name: "SqlDataset" - input_arg { - name: "driver_name" - type: DT_STRING - } - input_arg { - name: "data_source_name" - type: DT_STRING - } - input_arg { - name: "query" - type: DT_STRING - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } - is_stateful: true -} op { name: "Sqrt" input_arg { @@ -33513,6 +33737,13 @@ op { s: "" } } + attr { + name: "config_proto" + type: "string" + default_value { + s: "" + } + } attr { name: "executor_type" type: "string" @@ -33913,40 +34144,6 @@ op { } } } -op { - name: "StatsAggregatorHandle" - output_arg { - name: "handle" - type: DT_RESOURCE - } - attr { - name: "container" - type: "string" - default_value { - s: "" - } - } - attr { - name: "shared_name" - type: "string" - default_value { - s: "" - } - } - is_stateful: true -} -op { - name: "StatsAggregatorSummary" - input_arg { - name: "iterator" - type: DT_RESOURCE - } - output_arg { - name: "summary" - type: DT_STRING - } - is_stateful: true -} op { name: "StopGradient" input_arg { @@ -35916,12 +36113,133 @@ op { minimum: 1 } attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 + name: "output_shapes" + type: "list(shape)" + has_minimum: true + minimum: 1 + } + is_stateful: true +} +op { + name: "TensorForestCreateTreeVariable" + input_arg { + name: "tree_handle" + type: DT_RESOURCE + } + input_arg { + name: "tree_config" + type: DT_STRING + } + is_stateful: true +} +op { + name: "TensorForestTreeDeserialize" + input_arg { + name: "tree_handle" + type: DT_RESOURCE + } + input_arg { + name: "tree_config" + type: DT_STRING + } + is_stateful: true +} +op { + name: "TensorForestTreeIsInitializedOp" + input_arg { + name: "tree_handle" + type: DT_RESOURCE + } + output_arg { + name: "is_initialized" + type: DT_BOOL + } + is_stateful: true +} +op { + name: "TensorForestTreePredict" + input_arg { + name: "tree_handle" + type: DT_RESOURCE + } + input_arg { + name: "dense_features" + type: DT_FLOAT + } + output_arg { + name: "logits" + type: DT_FLOAT + } + attr { + name: "logits_dimension" + type: "int" + } + is_stateful: true +} +op { + name: "TensorForestTreeResourceHandleOp" + output_arg { + name: "resource" + type: DT_RESOURCE + } + attr { + name: "container" + type: "string" + default_value { + s: "" + } + } + attr { + name: "shared_name" + type: "string" + default_value { + s: "" + } + } + is_stateful: true +} +op { + name: "TensorForestTreeSerialize" + input_arg { + name: "tree_handle" + type: DT_RESOURCE + } + output_arg { + name: "tree_config" + type: DT_STRING + } + is_stateful: true +} +op { + name: "TensorForestTreeSize" + input_arg { + name: "tree_handle" + type: DT_RESOURCE + } + output_arg { + name: "tree_size" + type: DT_INT32 + } + is_stateful: true +} +op { + name: "TensorListConcat" + input_arg { + name: "input_handle" + type: DT_VARIANT + } + output_arg { + name: "tensor" + type_attr: "element_dtype" + } + output_arg { + name: "lengths" + type: DT_INT64 + } + attr { + name: "element_dtype" + type: "type" } - is_stateful: true } op { name: "TensorListConcatLists" @@ -36183,6 +36501,39 @@ op { type: "type" } } +op { + name: "TensorListSplit" + input_arg { + name: "tensor" + type_attr: "element_dtype" + } + input_arg { + name: "element_shape" + type_attr: "shape_type" + } + input_arg { + name: "lengths" + type: DT_INT64 + } + output_arg { + name: "output_handle" + type: DT_VARIANT + } + attr { + name: "element_dtype" + type: "type" + } + attr { + name: "shape_type" + type: "type" + allowed_values { + list { + type: DT_INT32 + type: DT_INT64 + } + } + } +} op { name: "TensorListStack" input_arg { @@ -36205,6 +36556,105 @@ op { } } } +op { + name: "TensorScatterAdd" + input_arg { + name: "tensor" + type_attr: "T" + } + input_arg { + name: "indices" + type_attr: "Tindices" + } + input_arg { + name: "updates" + type_attr: "T" + } + output_arg { + name: "output" + type_attr: "T" + } + attr { + name: "T" + type: "type" + } + attr { + name: "Tindices" + type: "type" + allowed_values { + list { + type: DT_INT32 + type: DT_INT64 + } + } + } +} +op { + name: "TensorScatterSub" + input_arg { + name: "tensor" + type_attr: "T" + } + input_arg { + name: "indices" + type_attr: "Tindices" + } + input_arg { + name: "updates" + type_attr: "T" + } + output_arg { + name: "output" + type_attr: "T" + } + attr { + name: "T" + type: "type" + } + attr { + name: "Tindices" + type: "type" + allowed_values { + list { + type: DT_INT32 + type: DT_INT64 + } + } + } +} +op { + name: "TensorScatterUpdate" + input_arg { + name: "tensor" + type_attr: "T" + } + input_arg { + name: "indices" + type_attr: "Tindices" + } + input_arg { + name: "updates" + type_attr: "T" + } + output_arg { + name: "output" + type_attr: "T" + } + attr { + name: "T" + type: "type" + } + attr { + name: "Tindices" + type: "type" + allowed_values { + list { + type: DT_INT32 + type: DT_INT64 + } + } + } +} op { name: "TensorSliceDataset" input_arg { @@ -36822,29 +37272,6 @@ op { type: "type" } } -op { - name: "UnbatchDataset" - input_arg { - name: "input_dataset" - type: DT_VARIANT - } - output_arg { - name: "handle" - type: DT_VARIANT - } - attr { - name: "output_types" - type: "list(type)" - has_minimum: true - minimum: 1 - } - attr { - name: "output_shapes" - type: "list(shape)" - has_minimum: true - minimum: 1 - } -} op { name: "UnbatchGrad" input_arg { @@ -36886,6 +37313,104 @@ op { type: "type" } } +op { + name: "UnicodeDecodeWithOffsets" + input_arg { + name: "input" + type: DT_STRING + } + output_arg { + name: "row_splits" + type: DT_INT64 + } + output_arg { + name: "char_values" + type: DT_INT32 + } + output_arg { + name: "char_to_byte_starts" + type: DT_INT64 + } + attr { + name: "input_encoding" + type: "string" + } + attr { + name: "errors" + type: "string" + default_value { + s: "replace" + } + allowed_values { + list { + s: "strict" + s: "replace" + s: "ignore" + } + } + } + attr { + name: "replacement_char" + type: "int" + default_value { + i: 65533 + } + } + attr { + name: "replace_control_characters" + type: "bool" + default_value { + b: false + } + } +} +op { + name: "UnicodeEncode" + input_arg { + name: "input_values" + type: DT_INT32 + } + input_arg { + name: "input_splits" + type: DT_INT64 + } + output_arg { + name: "output" + type: DT_STRING + } + attr { + name: "errors" + type: "string" + default_value { + s: "replace" + } + allowed_values { + list { + s: "ignore" + s: "replace" + s: "strict" + } + } + } + attr { + name: "output_encoding" + type: "string" + allowed_values { + list { + s: "UTF-8" + s: "UTF-16-BE" + s: "UTF-32-BE" + } + } + } + attr { + name: "replacement_char" + type: "int" + default_value { + i: 65533 + } + } +} op { name: "UnicodeScript" input_arg { diff --git a/tensorflow/core/ops/sparse_ops.cc b/tensorflow/core/ops/sparse_ops.cc index bc0cb2095d..de08a10784 100644 --- a/tensorflow/core/ops/sparse_ops.cc +++ b/tensorflow/core/ops/sparse_ops.cc @@ -401,7 +401,7 @@ REGISTER_OP("SparseReduceMax") .Attr("keep_dims: bool = False") .Output("output: T") .Attr("T: realnumbertype") - .SetShapeFn(shape_inference::UnknownShape); + .SetShapeFn(shape_inference::SparseReduceShapeFn); REGISTER_OP("SparseReduceMaxSparse") .Input("input_indices: int64") @@ -423,7 +423,7 @@ REGISTER_OP("SparseReduceSum") .Attr("keep_dims: bool = False") .Output("output: T") .Attr("T: numbertype") - .SetShapeFn(shape_inference::UnknownShape); + .SetShapeFn(shape_inference::SparseReduceShapeFn); REGISTER_OP("SparseReduceSumSparse") .Input("input_indices: int64") diff --git a/tensorflow/core/ops/sparse_ops_test.cc b/tensorflow/core/ops/sparse_ops_test.cc index 6a9b5ce4d3..00283c5993 100644 --- a/tensorflow/core/ops/sparse_ops_test.cc +++ b/tensorflow/core/ops/sparse_ops_test.cc @@ -133,6 +133,13 @@ TEST(SparseOpsTest, SparseToDense_ShapeFn) { TEST(SparseOpsTest, SparseReduceSum_ShapeFn) { ShapeInferenceTestOp op("SparseReduceSum"); + TF_ASSERT_OK(NodeDefBuilder("test", "SparseReduceSum") + .Input({"input_indices", 0, DT_INT64}) + .Input({"input_values", 1, DT_INT64}) + .Input({"input_shape", 2, DT_INT64}) + .Input({"reduction_axes", 3, DT_INT32}) + .Attr("keep_dims", false) + .Finalize(&op.node_def)); // Shape fn always yields unknown. INFER_OK(op, "?;?;?;?", "?"); diff --git a/tensorflow/core/ops/string_ops.cc b/tensorflow/core/ops/string_ops.cc index 352253135c..8ea74f1d43 100644 --- a/tensorflow/core/ops/string_ops.cc +++ b/tensorflow/core/ops/string_ops.cc @@ -13,13 +13,24 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ +#include +#include + #include "absl/strings/str_split.h" #include "tensorflow/core/framework/common_shape_fns.h" #include "tensorflow/core/framework/op.h" #include "tensorflow/core/framework/shape_inference.h" +#include "tensorflow/core/lib/core/errors.h" +#include "tensorflow/core/lib/core/status.h" +#include "tensorflow/core/lib/strings/strcat.h" +#include "tensorflow/core/platform/types.h" namespace tensorflow { +namespace shape_inference { +class InferenceContext; +} // namespace shape_inference + using shape_inference::DimensionHandle; using shape_inference::InferenceContext; using shape_inference::ShapeHandle; @@ -250,6 +261,31 @@ REGISTER_OP("UnicodeScript") .Output("output: int32") .SetShapeFn(shape_inference::UnchangedShape); +REGISTER_OP("UnicodeEncode") + .Input("input_values: int32") + .Input("input_splits: int64") + .Attr("errors: {'ignore', 'replace', 'strict'} = 'replace'") + .Attr("output_encoding: {'UTF-8', 'UTF-16-BE', 'UTF-32-BE'}") + .Attr("replacement_char: int = 65533") // 0xFFFD unicode replacement char + .Output("output: string") + .SetShapeFn([](InferenceContext* c) { + // Check rank of inner values + ShapeHandle input_inner_values_shape = c->input(0); + ShapeHandle unused; + TF_RETURN_IF_ERROR(c->WithRank(input_inner_values_shape, 1, &unused)); + + // Check rank of input_splits + ShapeHandle splits_shape = c->input(1); + TF_RETURN_IF_ERROR(c->WithRank(splits_shape, 1, &unused)); + + // Output shape is a 1-D tensor with size equal to number of splits. + std::vector dims(1); + TF_RETURN_IF_ERROR(c->Subtract(c->Dim(splits_shape, 0), 1, &dims[0])); + c->set_output(0, c->MakeShape(dims)); + + return Status::OK(); + }); + REGISTER_OP("UnicodeTranscode") .Input("input: string") .Output("output: string") @@ -259,4 +295,28 @@ REGISTER_OP("UnicodeTranscode") .Attr("replacement_char: int = 65533") // 0xFFFD unicode replacement char .Attr("replace_control_characters: bool = false") .SetShapeFn(shape_inference::UnchangedShape); + +REGISTER_OP("UnicodeDecodeWithOffsets") + .Input("input: string") + .Output("row_splits: int64") + .Output("char_values: int32") + .Output("char_to_byte_starts: int64") + .Attr("input_encoding: string") + .Attr("errors: {'strict', 'replace', 'ignore'} = 'replace'") + .Attr("replacement_char: int = 65533") // 0xFFFD unicode replacement char + .Attr("replace_control_characters: bool = false") + .SetShapeFn([](InferenceContext* c) { + // row_splits.shape == [input.size() + 1] + DimensionHandle num_row_splits; + DimensionHandle input_size = c->NumElements(c->input(0)); + TF_RETURN_IF_ERROR(c->Add(input_size, 1, &num_row_splits)); + c->set_output(0, c->Vector(num_row_splits)); + + // char_values.shape == offset_values.shape == [num_chars] + DimensionHandle num_chars = c->UnknownDim(); + c->set_output(1, c->Vector(num_chars)); + c->set_output(2, c->Vector(num_chars)); + return Status::OK(); + }); + } // namespace tensorflow diff --git a/tensorflow/core/ops/tensor_forest_ops.cc b/tensorflow/core/ops/tensor_forest_ops.cc new file mode 100644 index 0000000000..b4b6ba318e --- /dev/null +++ b/tensorflow/core/ops/tensor_forest_ops.cc @@ -0,0 +1,79 @@ +/* Copyright 2018 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 + +#include "tensorflow/core/framework/common_shape_fns.h" +#include "tensorflow/core/framework/op.h" +#include "tensorflow/core/framework/resource_mgr.h" +#include "tensorflow/core/framework/shape_inference.h" + +namespace tensorflow { + +REGISTER_RESOURCE_HANDLE_OP(TensorForestTreeResource); + +REGISTER_OP("TensorForestTreeIsInitializedOp") + .Input("tree_handle: resource") + .Output("is_initialized: bool") + .SetShapeFn([](shape_inference::InferenceContext* c) { + shape_inference::ShapeHandle unused_input; + TF_RETURN_IF_ERROR(c->WithRank(c->input(0), 0, &unused_input)); + c->set_output(0, c->Scalar()); + return Status::OK(); + }); + +REGISTER_OP("TensorForestCreateTreeVariable") + .Input("tree_handle: resource") + .Input("tree_config: string") + .SetShapeFn(tensorflow::shape_inference::NoOutputs); + +REGISTER_OP("TensorForestTreeSerialize") + .Input("tree_handle: resource") + .Output("tree_config: string") + .SetShapeFn(tensorflow::shape_inference::ScalarShape); + +REGISTER_OP("TensorForestTreeDeserialize") + .Input("tree_handle: resource") + .Input("tree_config: string") + .SetShapeFn([](shape_inference::InferenceContext* c) { + shape_inference::ShapeHandle unused_input; + TF_RETURN_IF_ERROR(c->WithRank(c->input(0), 0, &unused_input)); + return Status::OK(); + }); + +REGISTER_OP("TensorForestTreeSize") + .Input("tree_handle: resource") + .Output("tree_size: int32") + .SetShapeFn(tensorflow::shape_inference::ScalarShape); + +REGISTER_OP("TensorForestTreePredict") + .Attr("logits_dimension: int") + .Input("tree_handle: resource") + .Input("dense_features: float") + .Output("logits: float") + .SetShapeFn([](tensorflow::shape_inference::InferenceContext* c) { + shape_inference::ShapeHandle shape_handle; + shape_inference::DimensionHandle batch_size = c->UnknownDim(); + + TF_RETURN_IF_ERROR(c->WithRank(c->input(1), 2, &shape_handle)); + + batch_size = c->Dim(shape_handle, 0); + + int logits_dimension; + TF_RETURN_IF_ERROR(c->GetAttr("logits_dimension", &logits_dimension)); + c->set_output(0, c->Matrix(batch_size, logits_dimension)); + return Status::OK(); + }); +} // namespace tensorflow diff --git a/tensorflow/core/ops/training_ops.cc b/tensorflow/core/ops/training_ops.cc index 94ff092a85..995ed42d53 100644 --- a/tensorflow/core/ops/training_ops.cc +++ b/tensorflow/core/ops/training_ops.cc @@ -685,6 +685,34 @@ REGISTER_OP("ResourceSparseApplyMomentum") return ApplyMomentumShapeFn(c, true /* sparse */); }); +REGISTER_OP("ResourceApplyKerasMomentum") + .Input("var: resource") + .Input("accum: resource") + .Input("lr: T") + .Input("grad: T") + .Input("momentum: T") + .Attr("T: numbertype") + .Attr("use_locking: bool = false") + .Attr("use_nesterov: bool = false") + .SetShapeFn([](InferenceContext* c) { + return ApplyMomentumShapeFn(c, false /* sparse */); + }); + +REGISTER_OP("ResourceSparseApplyKerasMomentum") + .Input("var: resource") + .Input("accum: resource") + .Input("lr: T") + .Input("grad: T") + .Input("indices: Tindices") + .Input("momentum: T") + .Attr("T: numbertype") + .Attr("Tindices: {int32, int64}") + .Attr("use_locking: bool = false") + .Attr("use_nesterov: bool = false") + .SetShapeFn([](InferenceContext* c) { + return ApplyMomentumShapeFn(c, true /* sparse */); + }); + static Status ApplyAdamShapeFn(InferenceContext* c, bool sparse) { ShapeHandle unused; ShapeHandle s = ShapeOrHandleShape(c, 0); // var @@ -741,6 +769,44 @@ REGISTER_OP("ResourceApplyAdam") return ApplyAdamShapeFn(c, false /* sparse */); }); +static Status ApplyAdamWithAmsgradShapeFn(InferenceContext* c, bool sparse) { + ShapeHandle unused; + ShapeHandle s = ShapeOrHandleShape(c, 0); // var + TF_RETURN_IF_ERROR(c->Merge(s, ShapeOrHandleShape(c, 1), &s)); // m + TF_RETURN_IF_ERROR(c->Merge(s, ShapeOrHandleShape(c, 2), &s)); // v + TF_RETURN_IF_ERROR(c->Merge(s, ShapeOrHandleShape(c, 3), &s)); // vhat + TF_RETURN_IF_ERROR(c->WithRank(c->input(4), 0, &unused)); // beta1_power + TF_RETURN_IF_ERROR(c->WithRank(c->input(5), 0, &unused)); // beta2_power + TF_RETURN_IF_ERROR(c->WithRank(c->input(6), 0, &unused)); // lr + TF_RETURN_IF_ERROR(c->WithRank(c->input(7), 0, &unused)); // beta1 + TF_RETURN_IF_ERROR(c->WithRank(c->input(8), 0, &unused)); // beta2 + TF_RETURN_IF_ERROR(c->WithRank(c->input(9), 0, &unused)); // epsilon + TF_RETURN_IF_ERROR( + HandleGradAndIndicesInputs(c, sparse, 10 /* grad_idx */, &s)); + if (c->num_outputs() > 0) { + c->set_output(0, s); + } + return Status::OK(); +} + +REGISTER_OP("ResourceApplyAdamWithAmsgrad") + .Input("var: resource") + .Input("m: resource") + .Input("v: resource") + .Input("vhat: resource") + .Input("beta1_power: T") + .Input("beta2_power: T") + .Input("lr: T") + .Input("beta1: T") + .Input("beta2: T") + .Input("epsilon: T") + .Input("grad: T") + .Attr("T: numbertype") + .Attr("use_locking: bool = false") + .SetShapeFn([](InferenceContext* c) { + return ApplyAdamWithAmsgradShapeFn(c, false /* sparse */); + }); + static Status ApplyAdaMaxShapeFn(InferenceContext* c, bool sparse) { ShapeHandle unused; ShapeHandle s = ShapeOrHandleShape(c, 0); // var diff --git a/tensorflow/core/platform/cpu_feature_guard.cc b/tensorflow/core/platform/cpu_feature_guard.cc index 9d00aa7b7f..2efe0c0876 100644 --- a/tensorflow/core/platform/cpu_feature_guard.cc +++ b/tensorflow/core/platform/cpu_feature_guard.cc @@ -41,7 +41,7 @@ void CheckFeatureOrDie(CPUFeature feature, const string& feature_name) { } } -// Check if CPU feature is inclued in the TensorFlow binary. +// Check if CPU feature is included in the TensorFlow binary. void CheckIfFeatureUnused(CPUFeature feature, const string& feature_name, string& missing_instructions) { if (TestCPUFeature(feature)) { diff --git a/tensorflow/core/platform/default/build_config.bzl b/tensorflow/core/platform/default/build_config.bzl index 3a4415f229..0428715130 100644 --- a/tensorflow/core/platform/default/build_config.bzl +++ b/tensorflow/core/platform/default/build_config.bzl @@ -543,6 +543,9 @@ def tf_additional_proto_srcs(): def tf_additional_human_readable_json_deps(): return [] +def tf_additional_logger_deps(): + return [] + def tf_additional_all_protos(): return ["//tensorflow/core:protos_all"] diff --git a/tensorflow/core/platform/default/device_tracer.cc b/tensorflow/core/platform/default/device_tracer.cc index cf8b477b83..8351362e05 100644 --- a/tensorflow/core/platform/default/device_tracer.cc +++ b/tensorflow/core/platform/default/device_tracer.cc @@ -297,19 +297,16 @@ CUPTIManager *GetCUPTIManager() { // for the duration of the CUPTI API callback. TF_STATIC_THREAD_LOCAL_POD(const char *, tls_current_annotation); -class DeviceTracerImpl : public DeviceTracer, - public CUPTIClient, - public tracing::TraceCollector { +class TraceCollectorImpl : public tracing::TraceCollector { public: - DeviceTracerImpl(CUPTIManager *cupti_manager); - ~DeviceTracerImpl() override; + TraceCollectorImpl() { tracing::SetTraceCollector(this); } - // DeviceTracer interface: - Status Start() override; - Status Stop() override; - Status Collect(StepStatsCollector *collector) override; + ~TraceCollectorImpl() override { + DCHECK(!active_trace_session_) + << "Unexpected active trace session detected. "; + } - // tracing::TraceCollector interface: + // Note the method can be called after a call to Stop(). virtual std::unique_ptr CreateAnnotationHandle( StringPiece name_part1, StringPiece name_part2) const { struct Impl : public tracing::TraceCollector::Handle { @@ -332,8 +329,7 @@ class DeviceTracerImpl : public DeviceTracer, } bool IsEnabledForAnnotations() const override { - // We are always enabled for 'Annotations'. - return true; + return active_trace_session_.load(std::memory_order_relaxed); } bool IsEnabledForActivities(bool is_expensive) const override { @@ -341,6 +337,36 @@ class DeviceTracerImpl : public DeviceTracer, return false; } + void Start() { + DCHECK(!active_trace_session_) + << "Unexpected active trace session detected. "; + active_trace_session_ = true; + } + + void Stop() { + DCHECK(active_trace_session_) << "No active trace session detected. "; + active_trace_session_ = false; + } + + private: + std::atomic active_trace_session_; +}; + +TraceCollectorImpl *GlobalDefaultTraceCollector() { + static auto *instance = new TraceCollectorImpl(); + return instance; +} + +class DeviceTracerImpl : public DeviceTracer, public CUPTIClient { + public: + DeviceTracerImpl(CUPTIManager *cupti_manager); + ~DeviceTracerImpl() override; + + // DeviceTracer interface: + Status Start() override; + Status Stop() override; + Status Collect(StepStatsCollector *collector) override; + protected: // This callback is used exclusively by CUPTIManager. friend class CUPTIManager; @@ -430,7 +456,7 @@ Status DeviceTracerImpl::Start() { } // Register as a TraceEngine to receive ScopedAnnotations. - tracing::SetTraceCollector(this); + GlobalDefaultTraceCollector()->Start(); // Intercept launch and memcpy calls to capture the Op name annotation. // TODO(pbar) Add callbacks for memcpy variants. @@ -478,7 +504,8 @@ Status DeviceTracerImpl::Stop() { return Status::OK(); } CUPTI_CALL(Unsubscribe(subscriber_)); - tracing::SetTraceCollector(nullptr); + GlobalDefaultTraceCollector()->Stop(); + TF_RETURN_IF_ERROR(cupti_manager_->DisableTrace()); end_walltime_us_ = NowInUsec(); CUPTI_CALL(GetTimestamp(&end_timestamp_)); diff --git a/tensorflow/core/platform/default/logger.cc b/tensorflow/core/platform/default/logger.cc new file mode 100644 index 0000000000..54b1a1a67c --- /dev/null +++ b/tensorflow/core/platform/default/logger.cc @@ -0,0 +1,34 @@ +/* Copyright 2018 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/platform/logger.h" + +#include "tensorflow/core/platform/logging.h" + +namespace tensorflow { + +Logger* Logger::Singleton() { + class DefaultLogger : public Logger { + private: + void DoLogProto(google::protobuf::Any* proto) override { + VLOG(2) << proto->ShortDebugString(); + } + void DoFlush() override {} + }; + static Logger* instance = new DefaultLogger(); + return instance; +} + +} // namespace tensorflow diff --git a/tensorflow/core/platform/default/logging.cc b/tensorflow/core/platform/default/logging.cc index 34db490106..26bd8542fd 100644 --- a/tensorflow/core/platform/default/logging.cc +++ b/tensorflow/core/platform/default/logging.cc @@ -21,18 +21,18 @@ limitations under the License. #include #include #include -#include #endif #include +#include #include +#include +#include + namespace tensorflow { namespace internal { -LogMessage::LogMessage(const char* fname, int line, int severity) - : fname_(fname), line_(line), severity_(severity) {} - #if defined(PLATFORM_POSIX_ANDROID) void LogMessage::GenerateLogMessage() { int android_log_level; @@ -94,55 +94,156 @@ void LogMessage::GenerateLogMessage() { namespace { +int ParseInteger(const char* str, size_t size) { + // Ideally we would use env_var / safe_strto64, but it is + // hard to use here without pulling in a lot of dependencies, + // so we use std:istringstream instead + string integer_str(str, size); + std::istringstream ss(integer_str); + int level = 0; + ss >> level; + return level; +} + // Parse log level (int64) from environment variable (char*) int64 LogLevelStrToInt(const char* tf_env_var_val) { if (tf_env_var_val == nullptr) { return 0; } + return ParseInteger(tf_env_var_val, strlen(tf_env_var_val)); +} - // Ideally we would use env_var / safe_strto64, but it is - // hard to use here without pulling in a lot of dependencies, - // so we use std:istringstream instead - string min_log_level(tf_env_var_val); - std::istringstream ss(min_log_level); - int64 level; - if (!(ss >> level)) { - // Invalid vlog level setting, set level to default (0) - level = 0; +// Using StringPiece breaks Windows build. +struct StringData { + struct Hasher { + size_t operator()(const StringData& sdata) const { + // For dependency reasons, we cannot use hash.h here. Use DBJHash instead. + size_t hash = 5381; + const char* data = sdata.data; + for (const char* top = data + sdata.size; data < top; ++data) { + hash = ((hash << 5) + hash) + (*data); + } + return hash; + } + }; + + StringData() = default; + StringData(const char* data, size_t size) : data(data), size(size) {} + + bool operator==(const StringData& rhs) const { + return size == rhs.size && memcmp(data, rhs.data, size) == 0; } - return level; + const char* data = nullptr; + size_t size = 0; +}; + +using VmoduleMap = std::unordered_map; + +// Returns a mapping from module name to VLOG level, derived from the +// TF_CPP_VMOUDLE environment variable; ownership is transferred to the caller. +VmoduleMap* VmodulesMapFromEnv() { + // The value of the env var is supposed to be of the form: + // "foo=1,bar=2,baz=3" + const char* env = getenv("TF_CPP_VMODULE"); + if (env == nullptr) { + // If there is no TF_CPP_VMODULE configuration (most common case), return + // nullptr so that the ShouldVlogModule() API can fast bail out of it. + return nullptr; + } + // The memory returned by getenv() can be invalidated by following getenv() or + // setenv() calls. And since we keep references to it in the VmoduleMap in + // form of StringData objects, make a copy of it. + const char* env_data = strdup(env); + VmoduleMap* result = new VmoduleMap(); + while (true) { + const char* eq = strchr(env_data, '='); + if (eq == nullptr) { + break; + } + const char* after_eq = eq + 1; + + // Comma either points at the next comma delimiter, or at a null terminator. + // We check that the integer we parse ends at this delimiter. + const char* comma = strchr(after_eq, ','); + const char* new_env_data; + if (comma == nullptr) { + comma = strchr(after_eq, '\0'); + new_env_data = comma; + } else { + new_env_data = comma + 1; + } + (*result)[StringData(env_data, eq - env_data)] = + ParseInteger(after_eq, comma - after_eq); + env_data = new_env_data; + } + return result; } } // namespace int64 MinLogLevelFromEnv() { + // We don't want to print logs during fuzzing as that would slow fuzzing down + // by almost 2x. So, if we are in fuzzing mode (not just running a test), we + // return a value so that nothing is actually printed. Since LOG uses >= + // (see ~LogMessage in this file) to see if log messages need to be printed, + // the value we're interested on to disable printing is the maximum severity. + // See also http://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + return tensorflow::NUM_SEVERITIES; +#else const char* tf_env_var_val = getenv("TF_CPP_MIN_LOG_LEVEL"); return LogLevelStrToInt(tf_env_var_val); +#endif } int64 MinVLogLevelFromEnv() { + // We don't want to print logs during fuzzing as that would slow fuzzing down + // by almost 2x. So, if we are in fuzzing mode (not just running a test), we + // return a value so that nothing is actually printed. Since VLOG uses <= + // (see VLOG_IS_ON in logging.h) to see if log messages need to be printed, + // the value we're interested on to disable printing is 0. + // See also http://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + return 0; +#else const char* tf_env_var_val = getenv("TF_CPP_MIN_VLOG_LEVEL"); return LogLevelStrToInt(tf_env_var_val); +#endif } +LogMessage::LogMessage(const char* fname, int line, int severity) + : fname_(fname), line_(line), severity_(severity) {} + LogMessage::~LogMessage() { // Read the min log level once during the first call to logging. static int64 min_log_level = MinLogLevelFromEnv(); - if (TF_PREDICT_TRUE(severity_ >= min_log_level)) GenerateLogMessage(); + if (severity_ >= min_log_level) { + GenerateLogMessage(); + } } int64 LogMessage::MinVLogLevel() { - // We don't want to print logs during fuzzing as that would slow fuzzing down - // by almost 2x. So, if we are in fuzzing mode (not just running a test), we - // return maximum value so that nothing is actually printed - // See also http://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - return tensorflow::NUM_SEVERITIES; -#else static int64 min_vlog_level = MinVLogLevelFromEnv(); return min_vlog_level; -#endif +} + +bool LogMessage::VmoduleActivated(const char* fname, int level) { + if (level <= MinVLogLevel()) { + return true; + } + static VmoduleMap* vmodules = VmodulesMapFromEnv(); + if (TF_PREDICT_TRUE(vmodules == nullptr)) { + return false; + } + const char* last_slash = strrchr(fname, '/'); + const char* module_start = last_slash == nullptr ? fname : last_slash + 1; + const char* dot_after = strchr(module_start, '.'); + const char* module_limit = + dot_after == nullptr ? strchr(fname, '\0') : dot_after; + StringData module(module_start, module_limit - module_start); + auto it = vmodules->find(module); + return it != vmodules->end() && it->second >= level; } LogMessageFatal::LogMessageFatal(const char* file, int line) diff --git a/tensorflow/core/platform/default/logging.h b/tensorflow/core/platform/default/logging.h index 08a692fff7..bb8735ed32 100644 --- a/tensorflow/core/platform/default/logging.h +++ b/tensorflow/core/platform/default/logging.h @@ -46,6 +46,17 @@ class LogMessage : public std::basic_ostringstream { // but VLOG(3) will not. Defaults to 0. static int64 MinVLogLevel(); + // Returns whether VLOG level lvl is activated for the file fname. + // + // E.g. if the environment variable TF_CPP_VMODULE contains foo=3 and fname is + // foo.cc and lvl is <= 3, this will return true. It will also return true if + // the level is lower or equal to TF_CPP_MIN_VLOG_LEVEL (default zero). + // + // It is expected that the result of this query will be cached in the VLOG-ing + // call site to avoid repeated lookups. This routine performs a hash-map + // access against the VLOG-ing specification provided by the env var. + static bool VmoduleActivated(const char* fname, int level); + protected: void GenerateLogMessage(); @@ -55,6 +66,13 @@ class LogMessage : public std::basic_ostringstream { int severity_; }; +// Uses the lower operator & precedence to voidify a LogMessage reference, so +// that the ternary VLOG() implementation is balanced, type wise. +struct Voidifier { + template + void operator&(const T&)const {} +}; + // LogMessageFatal ensures the process will exit in failure after // logging this message. class LogMessageFatal : public LogMessage { @@ -77,18 +95,30 @@ class LogMessageFatal : public LogMessage { #define LOG(severity) _TF_LOG_##severity #ifdef IS_MOBILE_PLATFORM + // Turn VLOG off when under mobile devices for considerations of binary size. #define VLOG_IS_ON(lvl) ((lvl) <= 0) + #else -// Otherwise, Set TF_CPP_MIN_VLOG_LEVEL environment to update minimum log level -// of VLOG -#define VLOG_IS_ON(lvl) \ - ((lvl) <= ::tensorflow::internal::LogMessage::MinVLogLevel()) + +// Otherwise, set TF_CPP_MIN_VLOG_LEVEL environment to update minimum log level +// of VLOG, or TF_CPP_VMODULE to set the minimum log level for individual +// translation units. +#define VLOG_IS_ON(lvl) \ + (([](int level, const char* fname) { \ + static const bool vmodule_activated = \ + ::tensorflow::internal::LogMessage::VmoduleActivated(fname, level); \ + return vmodule_activated; \ + })(lvl, __FILE__)) + #endif -#define VLOG(lvl) \ - if (TF_PREDICT_FALSE(VLOG_IS_ON(lvl))) \ - ::tensorflow::internal::LogMessage(__FILE__, __LINE__, tensorflow::INFO) +#define VLOG(level) \ + TF_PREDICT_TRUE(!VLOG_IS_ON(level)) \ + ? (void)0 \ + : ::tensorflow::internal::Voidifier() & \ + ::tensorflow::internal::LogMessage(__FILE__, __LINE__, \ + tensorflow::INFO) // CHECK dies with a fatal error if condition is not true. It is *not* // controlled by NDEBUG, so the check will be executed regardless of diff --git a/tensorflow/core/platform/env.h b/tensorflow/core/platform/env.h index 5732271f15..7374fccdc2 100644 --- a/tensorflow/core/platform/env.h +++ b/tensorflow/core/platform/env.h @@ -28,6 +28,7 @@ limitations under the License. #include "tensorflow/core/platform/file_system.h" #include "tensorflow/core/platform/macros.h" #include "tensorflow/core/platform/mutex.h" +#include "tensorflow/core/platform/numa.h" #include "tensorflow/core/platform/protobuf.h" #include "tensorflow/core/platform/types.h" @@ -395,6 +396,7 @@ struct ThreadOptions { size_t stack_size = 0; // 0: use system default value /// Guard area size to use near thread stacks to use (in bytes) size_t guard_size = 0; // 0: use system default value + int numa_node = port::kNUMANoAffinity; }; /// A utility routine: copy contents of `src` in file system `src_fs` diff --git a/tensorflow/core/platform/logger.h b/tensorflow/core/platform/logger.h new file mode 100644 index 0000000000..5d304bea63 --- /dev/null +++ b/tensorflow/core/platform/logger.h @@ -0,0 +1,51 @@ +/* Copyright 2018 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_PLATFORM_LOGGER_H_ +#define TENSORFLOW_CORE_PLATFORM_LOGGER_H_ + +#include "google/protobuf/any.pb.h" +#include "tensorflow/core/platform/protobuf.h" + +namespace tensorflow { + +// Abstract logging interface. Contrary to logging.h, this class describes an +// interface, not a concrete logging mechanism. This is useful when we want to +// log anything to a non-local place, e.g. a database. +class Logger { + public: + static Logger* Singleton(); + + virtual ~Logger() = default; + + // Logs a typed proto. + template + void LogProto(const ProtoType& proto) { + google::protobuf::Any any; + any.PackFrom(proto); + DoLogProto(&any); + } + + // Flushes any pending log. Blocks until everything is flushed. + void Flush() { DoFlush(); } + + private: + virtual void DoLogProto(google::protobuf::Any* proto) = 0; + virtual void DoFlush() = 0; +}; + +} // namespace tensorflow + +#endif // TENSORFLOW_CORE_PLATFORM_LOGGER_H_ diff --git a/tensorflow/core/platform/numa_test.cc b/tensorflow/core/platform/numa_test.cc index 8b39ecd59c..91789efd1e 100644 --- a/tensorflow/core/platform/numa_test.cc +++ b/tensorflow/core/platform/numa_test.cc @@ -44,7 +44,7 @@ TEST(Numa, Malloc) { TEST(Numa, SetNodeAffinity) { // NOTE(tucker): This test is not reliable when executed under tap because - // the virtual machine may not have access to all of the availble NUMA + // the virtual machine may not have access to all of the available NUMA // nodes. Not sure what to do about that. EXPECT_EQ(-1, port::NUMAGetThreadNodeAffinity()); if (port::NUMAEnabled()) { diff --git a/tensorflow/core/platform/platform_strings.cc b/tensorflow/core/platform/platform_strings.cc new file mode 100644 index 0000000000..c1852633d5 --- /dev/null +++ b/tensorflow/core/platform/platform_strings.cc @@ -0,0 +1,64 @@ +/* Copyright 2018 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/platform/platform_strings.h" + +#include +#include + +#include +#include + +#include "tensorflow/core/lib/core/status.h" + +namespace tensorflow { + +int GetPlatformStrings(const std::string& path, + std::vector* found) { + int result; + FILE* ifp = fopen(path.c_str(), "rb"); + if (ifp != nullptr) { + static const char prefix[] = TF_PLAT_STR_MAGIC_PREFIX_; + int first_char = prefix[1]; + int last_char = -1; + int c; + while ((c = getc(ifp)) != EOF) { + if (c == first_char && last_char == 0) { + int i = 2; + while (prefix[i] != 0 && (c = getc(ifp)) == prefix[i]) { + i++; + } + if (prefix[i] == 0) { + std::string str; + while ((c = getc(ifp)) != EOF && c != 0) { + str.push_back(c); + } + if (!str.empty()) { + found->push_back(str); + } + } + } + last_char = c; + } + + result = (ferror(ifp) == 0) ? 0 : errno; + fclose(ifp); + } else { + result = errno; + } + return result; +} + +} // namespace tensorflow diff --git a/tensorflow/core/platform/platform_strings.h b/tensorflow/core/platform/platform_strings.h new file mode 100644 index 0000000000..5b1dbd130e --- /dev/null +++ b/tensorflow/core/platform/platform_strings.h @@ -0,0 +1,364 @@ +/* Copyright 2018 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_PLATFORM_PLATFORM_STRINGS_H_ +#define TENSORFLOW_CORE_PLATFORM_PLATFORM_STRINGS_H_ + +// This header defines the macro TF_PLATFORM_STRINGS() which should be used +// once in each dynamically loadable TensorFlow module. It embeds static +// strings into the compilation unit that allow TensorFlow to determine what +// compilation options were in effect when the compilation unit was built. All +// compilation units within the same dynamically loadable library should be +// built with the same options (or at least, the strings should be embedded in +// the compilation unit built with the most restrictive options). + +// The platform strings embedded into a binary may be retrieved with the +// GetPlatformStrings function. + +// Rationale: +// We wish to load only those libraries that this CPU can execute. For +// example, we should not load a library compiled with avx256 instructions on a +// CPU that cannot execute them. +// +// One might think that one could dlopen() the library, and call a routine that +// would return which cpu type it was compiled for. Alas, this does not work, +// because at dlopen() time, a library containing C++ will execute constructors +// of class variables with static storage class. Even code that looks +// innocuous may use optional platform-specific instructions. For example, +// the fastest way to zero a region of memory might use optional instructions. +// +// One might think one could run a tool such as "objdump" to read flags from +// the libraries' headers, or perhaps disassemble each library to look for +// particular instructions. Unfortunately, the desired flags are not present +// in the headers, and disassembly can be prohibitively slow ("objdump -d" is +// very slow, for example). Moreover, a tool to examine the library may not +// be present on the system unless the user has installed special packages (for +// example, on Windows). +// +// Instead, we adopt a crude but straightforward solution: We require +// developers to use the macro TF_PLATFORM_STRINGS() in their library, to +// embed the compilation options as constant strings. The compiler's +// predefined macros pick which strings are included. We then search for the +// strings in the files, and then dlopen() only those libraries that have or +// lack strings as needed. +// +// We adopt the approach of placing in the binary a fairly raw copy of the +// predefined macros, rather than trying to interpret them in complex ways at +// compile time. This allows the loading binary to alter its interpretation of +// the strings without library developers having to recompile. + +#include + +#include +#include + +// Aside from the header guard, the internal macros defined here have the form: +// TF_PLAT_STR_* + +// If a macro is removed from the list of tested macros, the major version in +// the following version number should be incremented, and the minor version +// set to zero. Otherwise, if a macro is added to the list of tested macros, +// the minor number should be incremented. +#define TF_PLAT_STR_VERSION_ "1.0" + +// Prefix of each option string indicator in the binary. +// After the prefix, such strings have the form: +// [A-Za-z_0-9]= +// followed by a terminating nul. To simplify searching, this prefix is all +// ASCII, starts with a nul, and contains no character twice. +#define TF_PLAT_STR_MAGIC_PREFIX_ "\0S\\s\":^p*L}" + +// A helper macro for TF_PLAT_STR_AS_STR_(). +#define TF_PLAT_STR_STR_1_(x) #x + +// Yield a constant string corresponding to x, after macro expansion. +#define TF_PLAT_STR_AS_STR_(x) TF_PLAT_STR_STR_1_(x) + +// An empty definition to make lists more uniform. +#define TF_PLAT_STR_TERMINATOR_ + +// TF_PLAT_STR_(x) introduces a constant string indicating whether a +// particular compilation option has been turned on. +// +// In gcc and clang, we might imagine using something like +// #define TF_PLAT_STR_(x) \ +// (sizeof (#x) != sizeof (TF_PLAT_STR_AS_STR_ (x))? \ +// TF_PLAT_STR_MAGIC_PREFIX_ #x "=" TF_PLAT_STR_AS_STR_ (x) : \ +// TF_PLAT_STR_MAGIC_PREFIX_ #x "=0"), +// but some compilers (notably MSVC) place both "foo" and "bar" in the binary +// when presented with +// (true? "foo" : "bar") +// so we must use #if to select the strings we need, which is rather verbose. +#define TF_PLAT_STR_(x) TF_PLAT_STR_MAGIC_PREFIX_ #x "=" TF_PLAT_STR_AS_STR_(x) + +// Include the #if machinery that sets the macros used below. +// platform_strings_computed.h can be generated by filtering this header file +// through: +// awk ' +// header == "" { print; } +// /\*\// && header == "" { +// print "// Generated from platform_strings.h."; +// print ""; +// print "#ifndef TENSORFLOW_CORE_PLATFORM_PLATFORM_STRINGS_COMPUTED_H_"; +// print "#define TENSORFLOW_CORE_PLATFORM_PLATFORM_STRINGS_COMPUTED_H_"; +// print ""; +// header = 1; +// } +// /^#define TF_PLAT_STR_LIST_[a-zA-Z0-9_]*\(\) *\\$/ { active = 1; } +// /TF_PLAT_STR_TERMINATOR_/ { active = 0; } +// /^ *TF_PLAT_STR_[A-Za-z0-9_]* *\\$/ && active { +// x = $0; +// sub(/^ *TF_PLAT_STR_/, "", x); +// sub(/ *\\$/, "", x); +// printf ("#if defined(%s)\n", x); +// printf ("#define TF_PLAT_STR_%s TF_PLAT_STR_(%s)\n", x, x); +// printf ("#else\n"); +// printf ("#define TF_PLAT_STR_%s\n", x); +// printf ("#endif\n"); +// } +// END { +// print ""; +// print "#endif // TENSORFLOW_CORE_PLATFORM_PLATFORM_STRINGS_COMPUTED_H_"; +// }' +#include "tensorflow/core/platform/platform_strings_computed.h" + +// clang-format butchers the following lines. +// clang-format off + +// x86_64 and x86_32 optional features. +#define TF_PLAT_STR_LIST___x86_64__() \ + TF_PLAT_STR__M_IX86_FP \ + TF_PLAT_STR__NO_PREFETCHW \ + TF_PLAT_STR___3dNOW_A__ \ + TF_PLAT_STR___3dNOW__ \ + TF_PLAT_STR___ABM__ \ + TF_PLAT_STR___ADX__ \ + TF_PLAT_STR___AES__ \ + TF_PLAT_STR___AVX2__ \ + TF_PLAT_STR___AVX512BW__ \ + TF_PLAT_STR___AVX512CD__ \ + TF_PLAT_STR___AVX512DQ__ \ + TF_PLAT_STR___AVX512ER__ \ + TF_PLAT_STR___AVX512F__ \ + TF_PLAT_STR___AVX512IFMA__ \ + TF_PLAT_STR___AVX512PF__ \ + TF_PLAT_STR___AVX512VBMI__ \ + TF_PLAT_STR___AVX512VL__ \ + TF_PLAT_STR___AVX__ \ + TF_PLAT_STR___BMI2__ \ + TF_PLAT_STR___BMI__ \ + TF_PLAT_STR___CLFLUSHOPT__ \ + TF_PLAT_STR___CLZERO__ \ + TF_PLAT_STR___F16C__ \ + TF_PLAT_STR___FMA4__ \ + TF_PLAT_STR___FMA__ \ + TF_PLAT_STR___FP_FAST_FMA \ + TF_PLAT_STR___FP_FAST_FMAF \ + TF_PLAT_STR___FSGSBASE__ \ + TF_PLAT_STR___FXSR__ \ + TF_PLAT_STR___LWP__ \ + TF_PLAT_STR___LZCNT__ \ + TF_PLAT_STR___MMX__ \ + TF_PLAT_STR___MWAITX__ \ + TF_PLAT_STR___PCLMUL__ \ + TF_PLAT_STR___PKU__ \ + TF_PLAT_STR___POPCNT__ \ + TF_PLAT_STR___PRFCHW__ \ + TF_PLAT_STR___RDRND__ \ + TF_PLAT_STR___RDSEED__ \ + TF_PLAT_STR___RTM__ \ + TF_PLAT_STR___SHA__ \ + TF_PLAT_STR___SSE2_MATH__ \ + TF_PLAT_STR___SSE2__ \ + TF_PLAT_STR___SSE_MATH__ \ + TF_PLAT_STR___SSE__ \ + TF_PLAT_STR___SSE3__ \ + TF_PLAT_STR___SSE4A__ \ + TF_PLAT_STR___SSE4_1__ \ + TF_PLAT_STR___SSE4_2__ \ + TF_PLAT_STR___SSSE3__ \ + TF_PLAT_STR___TBM__ \ + TF_PLAT_STR___XOP__ \ + TF_PLAT_STR___XSAVEC__ \ + TF_PLAT_STR___XSAVEOPT__ \ + TF_PLAT_STR___XSAVES__ \ + TF_PLAT_STR___XSAVE__ \ + TF_PLAT_STR_TERMINATOR_ + +// PowerPC (64- and 32-bit) optional features. +#define TF_PLAT_STR_LIST___powerpc64__() \ + TF_PLAT_STR__SOFT_DOUBLE \ + TF_PLAT_STR__SOFT_FLOAT \ + TF_PLAT_STR___ALTIVEC__ \ + TF_PLAT_STR___APPLE_ALTIVEC__ \ + TF_PLAT_STR___CRYPTO__ \ + TF_PLAT_STR___FLOAT128_HARDWARE__ \ + TF_PLAT_STR___FLOAT128_TYPE__ \ + TF_PLAT_STR___FP_FAST_FMA \ + TF_PLAT_STR___FP_FAST_FMAF \ + TF_PLAT_STR___HTM__ \ + TF_PLAT_STR___NO_FPRS__ \ + TF_PLAT_STR___NO_LWSYNC__ \ + TF_PLAT_STR___POWER8_VECTOR__ \ + TF_PLAT_STR___POWER9_VECTOR__ \ + TF_PLAT_STR___PPC405__ \ + TF_PLAT_STR___QUAD_MEMORY_ATOMIC__ \ + TF_PLAT_STR___RECIPF__ \ + TF_PLAT_STR___RECIP_PRECISION__ \ + TF_PLAT_STR___RECIP__ \ + TF_PLAT_STR___RSQRTEF__ \ + TF_PLAT_STR___RSQRTE__ \ + TF_PLAT_STR___TM_FENCE__ \ + TF_PLAT_STR___UPPER_REGS_DF__ \ + TF_PLAT_STR___UPPER_REGS_SF__ \ + TF_PLAT_STR___VEC__ \ + TF_PLAT_STR___VSX__ \ + TF_PLAT_STR_TERMINATOR_ + +// aarch64 and 32-bit arm optional features +#define TF_PLAT_STR_LIST___aarch64__() \ + TF_PLAT_STR___ARM_ARCH \ + TF_PLAT_STR___ARM_FEATURE_CLZ \ + TF_PLAT_STR___ARM_FEATURE_CRC32 \ + TF_PLAT_STR___ARM_FEATURE_CRC32 \ + TF_PLAT_STR___ARM_FEATURE_CRYPTO \ + TF_PLAT_STR___ARM_FEATURE_DIRECTED_ROUNDING \ + TF_PLAT_STR___ARM_FEATURE_DSP \ + TF_PLAT_STR___ARM_FEATURE_FMA \ + TF_PLAT_STR___ARM_FEATURE_IDIV \ + TF_PLAT_STR___ARM_FEATURE_LDREX \ + TF_PLAT_STR___ARM_FEATURE_NUMERIC_MAXMIN \ + TF_PLAT_STR___ARM_FEATURE_QBIT \ + TF_PLAT_STR___ARM_FEATURE_QRDMX \ + TF_PLAT_STR___ARM_FEATURE_SAT \ + TF_PLAT_STR___ARM_FEATURE_SIMD32 \ + TF_PLAT_STR___ARM_FEATURE_UNALIGNED \ + TF_PLAT_STR___ARM_FP \ + TF_PLAT_STR___ARM_NEON_FP \ + TF_PLAT_STR___ARM_NEON__ \ + TF_PLAT_STR___ARM_WMMX \ + TF_PLAT_STR___IWMMXT2__ \ + TF_PLAT_STR___IWMMXT__ \ + TF_PLAT_STR___VFP_FP__ \ + TF_PLAT_STR_TERMINATOR_ + +// Generic features, including indication of architecture and OS. +// The _M_* macros are defined by Visual Studio. +// It doesn't define __LITTLE_ENDIAN__ or __BYTE_ORDER__; +// Windows is assumed to be little endian. +#define TF_PLAT_STR_LIST___generic__() \ + TF_PLAT_STR_TARGET_IPHONE_SIMULATOR \ + TF_PLAT_STR_TARGET_OS_IOS \ + TF_PLAT_STR_TARGET_OS_IPHONE \ + TF_PLAT_STR__MSC_VER \ + TF_PLAT_STR__M_ARM \ + TF_PLAT_STR__M_ARM64 \ + TF_PLAT_STR__M_ARM_ARMV7VE \ + TF_PLAT_STR__M_ARM_FP \ + TF_PLAT_STR__M_IX86 \ + TF_PLAT_STR__M_X64 \ + TF_PLAT_STR__WIN32 \ + TF_PLAT_STR__WIN64 \ + TF_PLAT_STR___ANDROID__ \ + TF_PLAT_STR___APPLE__ \ + TF_PLAT_STR___BYTE_ORDER__ \ + TF_PLAT_STR___CYGWIN__ \ + TF_PLAT_STR___FreeBSD__ \ + TF_PLAT_STR___LITTLE_ENDIAN__ \ + TF_PLAT_STR___NetBSD__ \ + TF_PLAT_STR___OpenBSD__ \ + TF_PLAT_STR_____MSYS__ \ + TF_PLAT_STR___aarch64__ \ + TF_PLAT_STR___alpha__ \ + TF_PLAT_STR___arm__ \ + TF_PLAT_STR___i386__ \ + TF_PLAT_STR___i686__ \ + TF_PLAT_STR___ia64__ \ + TF_PLAT_STR___linux__ \ + TF_PLAT_STR___mips32__ \ + TF_PLAT_STR___mips64__ \ + TF_PLAT_STR___powerpc64__ \ + TF_PLAT_STR___powerpc__ \ + TF_PLAT_STR___riscv___ \ + TF_PLAT_STR___s390x__ \ + TF_PLAT_STR___sparc64__ \ + TF_PLAT_STR___sparc__ \ + TF_PLAT_STR___x86_64__ \ + TF_PLAT_STR_TERMINATOR_ + +#if !defined(__x86_64__) && !defined(_M_X64) && \ + !defined(__i386__) && !defined(_M_IX86) +#undef TF_PLAT_STR_LIST___x86_64__ +#define TF_PLAT_STR_LIST___x86_64__() +#endif +#if !defined(__powerpc64__) && !defined(__powerpc__) +#undef TF_PLAT_STR_LIST___powerpc64__ +#define TF_PLAT_STR_LIST___powerpc64__() +#endif +#if !defined(__aarch64__) && !defined(_M_ARM64) && \ + !defined(__arm__) && !defined(_M_ARM) +#undef TF_PLAT_STR_LIST___aarch64__ +#define TF_PLAT_STR_LIST___aarch64__() +#endif + +// Macro to be used in each dynamically loadable library. +// +// The BSS global variable tf_cpu_option_global and the class +// instance tf_cpu_option_avoid_omit_class are needed to prevent +// compilers/linkers such as clang from omitting the static variable +// tf_cpu_option[], which would otherwise appear to be unused. We cannot make +// tf_cpu_option[] global, because we then might get multiply-defined symbols +// if TF_PLAT_STR() is used twice in the same library. +// (tf_cpu_option_global doesn't see such errors because it is +// defined in BSS, so multiple definitions are combined by the linker.) gcc's +// __attribute__((used)) is insufficient because it seems to be ignored by +// linkers. +#define TF_PLATFORM_STRINGS() \ + static const char tf_cpu_option[] = \ + TF_PLAT_STR_MAGIC_PREFIX_ "TF_PLAT_STR_VERSION=" TF_PLAT_STR_VERSION_ \ + TF_PLAT_STR_LIST___x86_64__() \ + TF_PLAT_STR_LIST___powerpc64__() \ + TF_PLAT_STR_LIST___aarch64__() \ + TF_PLAT_STR_LIST___generic__() \ + ; \ + const char *tf_cpu_option_global; \ + namespace { \ + class TFCPUOptionHelper { \ + public: \ + TFCPUOptionHelper() { \ + /* Compilers/linkers remove unused variables aggressively. The */ \ + /* following gyrations subvert most such optimizations. */ \ + tf_cpu_option_global = tf_cpu_option; \ + /* Nothing is printed because the string starts with a nul. */ \ + printf("%s", tf_cpu_option); \ + } \ + } tf_cpu_option_avoid_omit_class; \ + } /* anonymous namespace */ +// clang-format on + +namespace tensorflow { + +class Status; + +// Retrieves the platform strings from the file at the given path and appends +// them to the given vector. If the returned int is non-zero, an error occurred +// reading the file and vector may or may not be modified. The returned error +// code is suitable for use with strerror(). +int GetPlatformStrings(const std::string& path, + std::vector* found); + +} // namespace tensorflow + +#endif // TENSORFLOW_CORE_PLATFORM_PLATFORM_STRINGS_H_ diff --git a/tensorflow/core/platform/platform_strings_computed.h b/tensorflow/core/platform/platform_strings_computed.h new file mode 100644 index 0000000000..6a17f3bfc3 --- /dev/null +++ b/tensorflow/core/platform/platform_strings_computed.h @@ -0,0 +1,735 @@ +/* Copyright 2018 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. +==============================================================================*/ +// Generated from platform_strings.h. + +#ifndef TENSORFLOW_CORE_PLATFORM_PLATFORM_STRINGS_COMPUTED_H_ +#define TENSORFLOW_CORE_PLATFORM_PLATFORM_STRINGS_COMPUTED_H_ + +#if defined(_M_IX86_FP) +#define TF_PLAT_STR__M_IX86_FP TF_PLAT_STR_(_M_IX86_FP) +#else +#define TF_PLAT_STR__M_IX86_FP +#endif +#if defined(_NO_PREFETCHW) +#define TF_PLAT_STR__NO_PREFETCHW TF_PLAT_STR_(_NO_PREFETCHW) +#else +#define TF_PLAT_STR__NO_PREFETCHW +#endif +#if defined(__3dNOW_A__) +#define TF_PLAT_STR___3dNOW_A__ TF_PLAT_STR_(__3dNOW_A__) +#else +#define TF_PLAT_STR___3dNOW_A__ +#endif +#if defined(__3dNOW__) +#define TF_PLAT_STR___3dNOW__ TF_PLAT_STR_(__3dNOW__) +#else +#define TF_PLAT_STR___3dNOW__ +#endif +#if defined(__ABM__) +#define TF_PLAT_STR___ABM__ TF_PLAT_STR_(__ABM__) +#else +#define TF_PLAT_STR___ABM__ +#endif +#if defined(__ADX__) +#define TF_PLAT_STR___ADX__ TF_PLAT_STR_(__ADX__) +#else +#define TF_PLAT_STR___ADX__ +#endif +#if defined(__AES__) +#define TF_PLAT_STR___AES__ TF_PLAT_STR_(__AES__) +#else +#define TF_PLAT_STR___AES__ +#endif +#if defined(__AVX2__) +#define TF_PLAT_STR___AVX2__ TF_PLAT_STR_(__AVX2__) +#else +#define TF_PLAT_STR___AVX2__ +#endif +#if defined(__AVX512BW__) +#define TF_PLAT_STR___AVX512BW__ TF_PLAT_STR_(__AVX512BW__) +#else +#define TF_PLAT_STR___AVX512BW__ +#endif +#if defined(__AVX512CD__) +#define TF_PLAT_STR___AVX512CD__ TF_PLAT_STR_(__AVX512CD__) +#else +#define TF_PLAT_STR___AVX512CD__ +#endif +#if defined(__AVX512DQ__) +#define TF_PLAT_STR___AVX512DQ__ TF_PLAT_STR_(__AVX512DQ__) +#else +#define TF_PLAT_STR___AVX512DQ__ +#endif +#if defined(__AVX512ER__) +#define TF_PLAT_STR___AVX512ER__ TF_PLAT_STR_(__AVX512ER__) +#else +#define TF_PLAT_STR___AVX512ER__ +#endif +#if defined(__AVX512F__) +#define TF_PLAT_STR___AVX512F__ TF_PLAT_STR_(__AVX512F__) +#else +#define TF_PLAT_STR___AVX512F__ +#endif +#if defined(__AVX512IFMA__) +#define TF_PLAT_STR___AVX512IFMA__ TF_PLAT_STR_(__AVX512IFMA__) +#else +#define TF_PLAT_STR___AVX512IFMA__ +#endif +#if defined(__AVX512PF__) +#define TF_PLAT_STR___AVX512PF__ TF_PLAT_STR_(__AVX512PF__) +#else +#define TF_PLAT_STR___AVX512PF__ +#endif +#if defined(__AVX512VBMI__) +#define TF_PLAT_STR___AVX512VBMI__ TF_PLAT_STR_(__AVX512VBMI__) +#else +#define TF_PLAT_STR___AVX512VBMI__ +#endif +#if defined(__AVX512VL__) +#define TF_PLAT_STR___AVX512VL__ TF_PLAT_STR_(__AVX512VL__) +#else +#define TF_PLAT_STR___AVX512VL__ +#endif +#if defined(__AVX__) +#define TF_PLAT_STR___AVX__ TF_PLAT_STR_(__AVX__) +#else +#define TF_PLAT_STR___AVX__ +#endif +#if defined(__BMI2__) +#define TF_PLAT_STR___BMI2__ TF_PLAT_STR_(__BMI2__) +#else +#define TF_PLAT_STR___BMI2__ +#endif +#if defined(__BMI__) +#define TF_PLAT_STR___BMI__ TF_PLAT_STR_(__BMI__) +#else +#define TF_PLAT_STR___BMI__ +#endif +#if defined(__CLFLUSHOPT__) +#define TF_PLAT_STR___CLFLUSHOPT__ TF_PLAT_STR_(__CLFLUSHOPT__) +#else +#define TF_PLAT_STR___CLFLUSHOPT__ +#endif +#if defined(__CLZERO__) +#define TF_PLAT_STR___CLZERO__ TF_PLAT_STR_(__CLZERO__) +#else +#define TF_PLAT_STR___CLZERO__ +#endif +#if defined(__F16C__) +#define TF_PLAT_STR___F16C__ TF_PLAT_STR_(__F16C__) +#else +#define TF_PLAT_STR___F16C__ +#endif +#if defined(__FMA4__) +#define TF_PLAT_STR___FMA4__ TF_PLAT_STR_(__FMA4__) +#else +#define TF_PLAT_STR___FMA4__ +#endif +#if defined(__FMA__) +#define TF_PLAT_STR___FMA__ TF_PLAT_STR_(__FMA__) +#else +#define TF_PLAT_STR___FMA__ +#endif +#if defined(__FP_FAST_FMA) +#define TF_PLAT_STR___FP_FAST_FMA TF_PLAT_STR_(__FP_FAST_FMA) +#else +#define TF_PLAT_STR___FP_FAST_FMA +#endif +#if defined(__FP_FAST_FMAF) +#define TF_PLAT_STR___FP_FAST_FMAF TF_PLAT_STR_(__FP_FAST_FMAF) +#else +#define TF_PLAT_STR___FP_FAST_FMAF +#endif +#if defined(__FSGSBASE__) +#define TF_PLAT_STR___FSGSBASE__ TF_PLAT_STR_(__FSGSBASE__) +#else +#define TF_PLAT_STR___FSGSBASE__ +#endif +#if defined(__FXSR__) +#define TF_PLAT_STR___FXSR__ TF_PLAT_STR_(__FXSR__) +#else +#define TF_PLAT_STR___FXSR__ +#endif +#if defined(__LWP__) +#define TF_PLAT_STR___LWP__ TF_PLAT_STR_(__LWP__) +#else +#define TF_PLAT_STR___LWP__ +#endif +#if defined(__LZCNT__) +#define TF_PLAT_STR___LZCNT__ TF_PLAT_STR_(__LZCNT__) +#else +#define TF_PLAT_STR___LZCNT__ +#endif +#if defined(__MMX__) +#define TF_PLAT_STR___MMX__ TF_PLAT_STR_(__MMX__) +#else +#define TF_PLAT_STR___MMX__ +#endif +#if defined(__MWAITX__) +#define TF_PLAT_STR___MWAITX__ TF_PLAT_STR_(__MWAITX__) +#else +#define TF_PLAT_STR___MWAITX__ +#endif +#if defined(__PCLMUL__) +#define TF_PLAT_STR___PCLMUL__ TF_PLAT_STR_(__PCLMUL__) +#else +#define TF_PLAT_STR___PCLMUL__ +#endif +#if defined(__PKU__) +#define TF_PLAT_STR___PKU__ TF_PLAT_STR_(__PKU__) +#else +#define TF_PLAT_STR___PKU__ +#endif +#if defined(__POPCNT__) +#define TF_PLAT_STR___POPCNT__ TF_PLAT_STR_(__POPCNT__) +#else +#define TF_PLAT_STR___POPCNT__ +#endif +#if defined(__PRFCHW__) +#define TF_PLAT_STR___PRFCHW__ TF_PLAT_STR_(__PRFCHW__) +#else +#define TF_PLAT_STR___PRFCHW__ +#endif +#if defined(__RDRND__) +#define TF_PLAT_STR___RDRND__ TF_PLAT_STR_(__RDRND__) +#else +#define TF_PLAT_STR___RDRND__ +#endif +#if defined(__RDSEED__) +#define TF_PLAT_STR___RDSEED__ TF_PLAT_STR_(__RDSEED__) +#else +#define TF_PLAT_STR___RDSEED__ +#endif +#if defined(__RTM__) +#define TF_PLAT_STR___RTM__ TF_PLAT_STR_(__RTM__) +#else +#define TF_PLAT_STR___RTM__ +#endif +#if defined(__SHA__) +#define TF_PLAT_STR___SHA__ TF_PLAT_STR_(__SHA__) +#else +#define TF_PLAT_STR___SHA__ +#endif +#if defined(__SSE2_MATH__) +#define TF_PLAT_STR___SSE2_MATH__ TF_PLAT_STR_(__SSE2_MATH__) +#else +#define TF_PLAT_STR___SSE2_MATH__ +#endif +#if defined(__SSE2__) +#define TF_PLAT_STR___SSE2__ TF_PLAT_STR_(__SSE2__) +#else +#define TF_PLAT_STR___SSE2__ +#endif +#if defined(__SSE_MATH__) +#define TF_PLAT_STR___SSE_MATH__ TF_PLAT_STR_(__SSE_MATH__) +#else +#define TF_PLAT_STR___SSE_MATH__ +#endif +#if defined(__SSE__) +#define TF_PLAT_STR___SSE__ TF_PLAT_STR_(__SSE__) +#else +#define TF_PLAT_STR___SSE__ +#endif +#if defined(__SSE3__) +#define TF_PLAT_STR___SSE3__ TF_PLAT_STR_(__SSE3__) +#else +#define TF_PLAT_STR___SSE3__ +#endif +#if defined(__SSE4A__) +#define TF_PLAT_STR___SSE4A__ TF_PLAT_STR_(__SSE4A__) +#else +#define TF_PLAT_STR___SSE4A__ +#endif +#if defined(__SSE4_1__) +#define TF_PLAT_STR___SSE4_1__ TF_PLAT_STR_(__SSE4_1__) +#else +#define TF_PLAT_STR___SSE4_1__ +#endif +#if defined(__SSE4_2__) +#define TF_PLAT_STR___SSE4_2__ TF_PLAT_STR_(__SSE4_2__) +#else +#define TF_PLAT_STR___SSE4_2__ +#endif +#if defined(__SSSE3__) +#define TF_PLAT_STR___SSSE3__ TF_PLAT_STR_(__SSSE3__) +#else +#define TF_PLAT_STR___SSSE3__ +#endif +#if defined(__TBM__) +#define TF_PLAT_STR___TBM__ TF_PLAT_STR_(__TBM__) +#else +#define TF_PLAT_STR___TBM__ +#endif +#if defined(__XOP__) +#define TF_PLAT_STR___XOP__ TF_PLAT_STR_(__XOP__) +#else +#define TF_PLAT_STR___XOP__ +#endif +#if defined(__XSAVEC__) +#define TF_PLAT_STR___XSAVEC__ TF_PLAT_STR_(__XSAVEC__) +#else +#define TF_PLAT_STR___XSAVEC__ +#endif +#if defined(__XSAVEOPT__) +#define TF_PLAT_STR___XSAVEOPT__ TF_PLAT_STR_(__XSAVEOPT__) +#else +#define TF_PLAT_STR___XSAVEOPT__ +#endif +#if defined(__XSAVES__) +#define TF_PLAT_STR___XSAVES__ TF_PLAT_STR_(__XSAVES__) +#else +#define TF_PLAT_STR___XSAVES__ +#endif +#if defined(__XSAVE__) +#define TF_PLAT_STR___XSAVE__ TF_PLAT_STR_(__XSAVE__) +#else +#define TF_PLAT_STR___XSAVE__ +#endif +#if defined(_SOFT_DOUBLE) +#define TF_PLAT_STR__SOFT_DOUBLE TF_PLAT_STR_(_SOFT_DOUBLE) +#else +#define TF_PLAT_STR__SOFT_DOUBLE +#endif +#if defined(_SOFT_FLOAT) +#define TF_PLAT_STR__SOFT_FLOAT TF_PLAT_STR_(_SOFT_FLOAT) +#else +#define TF_PLAT_STR__SOFT_FLOAT +#endif +#if defined(__ALTIVEC__) +#define TF_PLAT_STR___ALTIVEC__ TF_PLAT_STR_(__ALTIVEC__) +#else +#define TF_PLAT_STR___ALTIVEC__ +#endif +#if defined(__APPLE_ALTIVEC__) +#define TF_PLAT_STR___APPLE_ALTIVEC__ TF_PLAT_STR_(__APPLE_ALTIVEC__) +#else +#define TF_PLAT_STR___APPLE_ALTIVEC__ +#endif +#if defined(__CRYPTO__) +#define TF_PLAT_STR___CRYPTO__ TF_PLAT_STR_(__CRYPTO__) +#else +#define TF_PLAT_STR___CRYPTO__ +#endif +#if defined(__FLOAT128_HARDWARE__) +#define TF_PLAT_STR___FLOAT128_HARDWARE__ TF_PLAT_STR_(__FLOAT128_HARDWARE__) +#else +#define TF_PLAT_STR___FLOAT128_HARDWARE__ +#endif +#if defined(__FLOAT128_TYPE__) +#define TF_PLAT_STR___FLOAT128_TYPE__ TF_PLAT_STR_(__FLOAT128_TYPE__) +#else +#define TF_PLAT_STR___FLOAT128_TYPE__ +#endif +#if defined(__FP_FAST_FMA) +#define TF_PLAT_STR___FP_FAST_FMA TF_PLAT_STR_(__FP_FAST_FMA) +#else +#define TF_PLAT_STR___FP_FAST_FMA +#endif +#if defined(__FP_FAST_FMAF) +#define TF_PLAT_STR___FP_FAST_FMAF TF_PLAT_STR_(__FP_FAST_FMAF) +#else +#define TF_PLAT_STR___FP_FAST_FMAF +#endif +#if defined(__HTM__) +#define TF_PLAT_STR___HTM__ TF_PLAT_STR_(__HTM__) +#else +#define TF_PLAT_STR___HTM__ +#endif +#if defined(__NO_FPRS__) +#define TF_PLAT_STR___NO_FPRS__ TF_PLAT_STR_(__NO_FPRS__) +#else +#define TF_PLAT_STR___NO_FPRS__ +#endif +#if defined(__NO_LWSYNC__) +#define TF_PLAT_STR___NO_LWSYNC__ TF_PLAT_STR_(__NO_LWSYNC__) +#else +#define TF_PLAT_STR___NO_LWSYNC__ +#endif +#if defined(__POWER8_VECTOR__) +#define TF_PLAT_STR___POWER8_VECTOR__ TF_PLAT_STR_(__POWER8_VECTOR__) +#else +#define TF_PLAT_STR___POWER8_VECTOR__ +#endif +#if defined(__POWER9_VECTOR__) +#define TF_PLAT_STR___POWER9_VECTOR__ TF_PLAT_STR_(__POWER9_VECTOR__) +#else +#define TF_PLAT_STR___POWER9_VECTOR__ +#endif +#if defined(__PPC405__) +#define TF_PLAT_STR___PPC405__ TF_PLAT_STR_(__PPC405__) +#else +#define TF_PLAT_STR___PPC405__ +#endif +#if defined(__QUAD_MEMORY_ATOMIC__) +#define TF_PLAT_STR___QUAD_MEMORY_ATOMIC__ TF_PLAT_STR_(__QUAD_MEMORY_ATOMIC__) +#else +#define TF_PLAT_STR___QUAD_MEMORY_ATOMIC__ +#endif +#if defined(__RECIPF__) +#define TF_PLAT_STR___RECIPF__ TF_PLAT_STR_(__RECIPF__) +#else +#define TF_PLAT_STR___RECIPF__ +#endif +#if defined(__RECIP_PRECISION__) +#define TF_PLAT_STR___RECIP_PRECISION__ TF_PLAT_STR_(__RECIP_PRECISION__) +#else +#define TF_PLAT_STR___RECIP_PRECISION__ +#endif +#if defined(__RECIP__) +#define TF_PLAT_STR___RECIP__ TF_PLAT_STR_(__RECIP__) +#else +#define TF_PLAT_STR___RECIP__ +#endif +#if defined(__RSQRTEF__) +#define TF_PLAT_STR___RSQRTEF__ TF_PLAT_STR_(__RSQRTEF__) +#else +#define TF_PLAT_STR___RSQRTEF__ +#endif +#if defined(__RSQRTE__) +#define TF_PLAT_STR___RSQRTE__ TF_PLAT_STR_(__RSQRTE__) +#else +#define TF_PLAT_STR___RSQRTE__ +#endif +#if defined(__TM_FENCE__) +#define TF_PLAT_STR___TM_FENCE__ TF_PLAT_STR_(__TM_FENCE__) +#else +#define TF_PLAT_STR___TM_FENCE__ +#endif +#if defined(__UPPER_REGS_DF__) +#define TF_PLAT_STR___UPPER_REGS_DF__ TF_PLAT_STR_(__UPPER_REGS_DF__) +#else +#define TF_PLAT_STR___UPPER_REGS_DF__ +#endif +#if defined(__UPPER_REGS_SF__) +#define TF_PLAT_STR___UPPER_REGS_SF__ TF_PLAT_STR_(__UPPER_REGS_SF__) +#else +#define TF_PLAT_STR___UPPER_REGS_SF__ +#endif +#if defined(__VEC__) +#define TF_PLAT_STR___VEC__ TF_PLAT_STR_(__VEC__) +#else +#define TF_PLAT_STR___VEC__ +#endif +#if defined(__VSX__) +#define TF_PLAT_STR___VSX__ TF_PLAT_STR_(__VSX__) +#else +#define TF_PLAT_STR___VSX__ +#endif +#if defined(__ARM_ARCH) +#define TF_PLAT_STR___ARM_ARCH TF_PLAT_STR_(__ARM_ARCH) +#else +#define TF_PLAT_STR___ARM_ARCH +#endif +#if defined(__ARM_FEATURE_CLZ) +#define TF_PLAT_STR___ARM_FEATURE_CLZ TF_PLAT_STR_(__ARM_FEATURE_CLZ) +#else +#define TF_PLAT_STR___ARM_FEATURE_CLZ +#endif +#if defined(__ARM_FEATURE_CRC32) +#define TF_PLAT_STR___ARM_FEATURE_CRC32 TF_PLAT_STR_(__ARM_FEATURE_CRC32) +#else +#define TF_PLAT_STR___ARM_FEATURE_CRC32 +#endif +#if defined(__ARM_FEATURE_CRC32) +#define TF_PLAT_STR___ARM_FEATURE_CRC32 TF_PLAT_STR_(__ARM_FEATURE_CRC32) +#else +#define TF_PLAT_STR___ARM_FEATURE_CRC32 +#endif +#if defined(__ARM_FEATURE_CRYPTO) +#define TF_PLAT_STR___ARM_FEATURE_CRYPTO TF_PLAT_STR_(__ARM_FEATURE_CRYPTO) +#else +#define TF_PLAT_STR___ARM_FEATURE_CRYPTO +#endif +#if defined(__ARM_FEATURE_DIRECTED_ROUNDING) +#define TF_PLAT_STR___ARM_FEATURE_DIRECTED_ROUNDING \ + TF_PLAT_STR_(__ARM_FEATURE_DIRECTED_ROUNDING) +#else +#define TF_PLAT_STR___ARM_FEATURE_DIRECTED_ROUNDING +#endif +#if defined(__ARM_FEATURE_DSP) +#define TF_PLAT_STR___ARM_FEATURE_DSP TF_PLAT_STR_(__ARM_FEATURE_DSP) +#else +#define TF_PLAT_STR___ARM_FEATURE_DSP +#endif +#if defined(__ARM_FEATURE_FMA) +#define TF_PLAT_STR___ARM_FEATURE_FMA TF_PLAT_STR_(__ARM_FEATURE_FMA) +#else +#define TF_PLAT_STR___ARM_FEATURE_FMA +#endif +#if defined(__ARM_FEATURE_IDIV) +#define TF_PLAT_STR___ARM_FEATURE_IDIV TF_PLAT_STR_(__ARM_FEATURE_IDIV) +#else +#define TF_PLAT_STR___ARM_FEATURE_IDIV +#endif +#if defined(__ARM_FEATURE_LDREX) +#define TF_PLAT_STR___ARM_FEATURE_LDREX TF_PLAT_STR_(__ARM_FEATURE_LDREX) +#else +#define TF_PLAT_STR___ARM_FEATURE_LDREX +#endif +#if defined(__ARM_FEATURE_NUMERIC_MAXMIN) +#define TF_PLAT_STR___ARM_FEATURE_NUMERIC_MAXMIN \ + TF_PLAT_STR_(__ARM_FEATURE_NUMERIC_MAXMIN) +#else +#define TF_PLAT_STR___ARM_FEATURE_NUMERIC_MAXMIN +#endif +#if defined(__ARM_FEATURE_QBIT) +#define TF_PLAT_STR___ARM_FEATURE_QBIT TF_PLAT_STR_(__ARM_FEATURE_QBIT) +#else +#define TF_PLAT_STR___ARM_FEATURE_QBIT +#endif +#if defined(__ARM_FEATURE_QRDMX) +#define TF_PLAT_STR___ARM_FEATURE_QRDMX TF_PLAT_STR_(__ARM_FEATURE_QRDMX) +#else +#define TF_PLAT_STR___ARM_FEATURE_QRDMX +#endif +#if defined(__ARM_FEATURE_SAT) +#define TF_PLAT_STR___ARM_FEATURE_SAT TF_PLAT_STR_(__ARM_FEATURE_SAT) +#else +#define TF_PLAT_STR___ARM_FEATURE_SAT +#endif +#if defined(__ARM_FEATURE_SIMD32) +#define TF_PLAT_STR___ARM_FEATURE_SIMD32 TF_PLAT_STR_(__ARM_FEATURE_SIMD32) +#else +#define TF_PLAT_STR___ARM_FEATURE_SIMD32 +#endif +#if defined(__ARM_FEATURE_UNALIGNED) +#define TF_PLAT_STR___ARM_FEATURE_UNALIGNED \ + TF_PLAT_STR_(__ARM_FEATURE_UNALIGNED) +#else +#define TF_PLAT_STR___ARM_FEATURE_UNALIGNED +#endif +#if defined(__ARM_FP) +#define TF_PLAT_STR___ARM_FP TF_PLAT_STR_(__ARM_FP) +#else +#define TF_PLAT_STR___ARM_FP +#endif +#if defined(__ARM_NEON_FP) +#define TF_PLAT_STR___ARM_NEON_FP TF_PLAT_STR_(__ARM_NEON_FP) +#else +#define TF_PLAT_STR___ARM_NEON_FP +#endif +#if defined(__ARM_NEON__) +#define TF_PLAT_STR___ARM_NEON__ TF_PLAT_STR_(__ARM_NEON__) +#else +#define TF_PLAT_STR___ARM_NEON__ +#endif +#if defined(__ARM_WMMX) +#define TF_PLAT_STR___ARM_WMMX TF_PLAT_STR_(__ARM_WMMX) +#else +#define TF_PLAT_STR___ARM_WMMX +#endif +#if defined(__IWMMXT2__) +#define TF_PLAT_STR___IWMMXT2__ TF_PLAT_STR_(__IWMMXT2__) +#else +#define TF_PLAT_STR___IWMMXT2__ +#endif +#if defined(__IWMMXT__) +#define TF_PLAT_STR___IWMMXT__ TF_PLAT_STR_(__IWMMXT__) +#else +#define TF_PLAT_STR___IWMMXT__ +#endif +#if defined(__VFP_FP__) +#define TF_PLAT_STR___VFP_FP__ TF_PLAT_STR_(__VFP_FP__) +#else +#define TF_PLAT_STR___VFP_FP__ +#endif +#if defined(TARGET_IPHONE_SIMULATOR) +#define TF_PLAT_STR_TARGET_IPHONE_SIMULATOR \ + TF_PLAT_STR_(TARGET_IPHONE_SIMULATOR) +#else +#define TF_PLAT_STR_TARGET_IPHONE_SIMULATOR +#endif +#if defined(TARGET_OS_IOS) +#define TF_PLAT_STR_TARGET_OS_IOS TF_PLAT_STR_(TARGET_OS_IOS) +#else +#define TF_PLAT_STR_TARGET_OS_IOS +#endif +#if defined(TARGET_OS_IPHONE) +#define TF_PLAT_STR_TARGET_OS_IPHONE TF_PLAT_STR_(TARGET_OS_IPHONE) +#else +#define TF_PLAT_STR_TARGET_OS_IPHONE +#endif +#if defined(_MSC_VER) +#define TF_PLAT_STR__MSC_VER TF_PLAT_STR_(_MSC_VER) +#else +#define TF_PLAT_STR__MSC_VER +#endif +#if defined(_M_ARM) +#define TF_PLAT_STR__M_ARM TF_PLAT_STR_(_M_ARM) +#else +#define TF_PLAT_STR__M_ARM +#endif +#if defined(_M_ARM64) +#define TF_PLAT_STR__M_ARM64 TF_PLAT_STR_(_M_ARM64) +#else +#define TF_PLAT_STR__M_ARM64 +#endif +#if defined(_M_ARM_ARMV7VE) +#define TF_PLAT_STR__M_ARM_ARMV7VE TF_PLAT_STR_(_M_ARM_ARMV7VE) +#else +#define TF_PLAT_STR__M_ARM_ARMV7VE +#endif +#if defined(_M_ARM_FP) +#define TF_PLAT_STR__M_ARM_FP TF_PLAT_STR_(_M_ARM_FP) +#else +#define TF_PLAT_STR__M_ARM_FP +#endif +#if defined(_M_IX86) +#define TF_PLAT_STR__M_IX86 TF_PLAT_STR_(_M_IX86) +#else +#define TF_PLAT_STR__M_IX86 +#endif +#if defined(_M_X64) +#define TF_PLAT_STR__M_X64 TF_PLAT_STR_(_M_X64) +#else +#define TF_PLAT_STR__M_X64 +#endif +#if defined(_WIN32) +#define TF_PLAT_STR__WIN32 TF_PLAT_STR_(_WIN32) +#else +#define TF_PLAT_STR__WIN32 +#endif +#if defined(_WIN64) +#define TF_PLAT_STR__WIN64 TF_PLAT_STR_(_WIN64) +#else +#define TF_PLAT_STR__WIN64 +#endif +#if defined(__ANDROID__) +#define TF_PLAT_STR___ANDROID__ TF_PLAT_STR_(__ANDROID__) +#else +#define TF_PLAT_STR___ANDROID__ +#endif +#if defined(__APPLE__) +#define TF_PLAT_STR___APPLE__ TF_PLAT_STR_(__APPLE__) +#else +#define TF_PLAT_STR___APPLE__ +#endif +#if defined(__BYTE_ORDER__) +#define TF_PLAT_STR___BYTE_ORDER__ TF_PLAT_STR_(__BYTE_ORDER__) +#else +#define TF_PLAT_STR___BYTE_ORDER__ +#endif +#if defined(__CYGWIN__) +#define TF_PLAT_STR___CYGWIN__ TF_PLAT_STR_(__CYGWIN__) +#else +#define TF_PLAT_STR___CYGWIN__ +#endif +#if defined(__FreeBSD__) +#define TF_PLAT_STR___FreeBSD__ TF_PLAT_STR_(__FreeBSD__) +#else +#define TF_PLAT_STR___FreeBSD__ +#endif +#if defined(__LITTLE_ENDIAN__) +#define TF_PLAT_STR___LITTLE_ENDIAN__ TF_PLAT_STR_(__LITTLE_ENDIAN__) +#else +#define TF_PLAT_STR___LITTLE_ENDIAN__ +#endif +#if defined(__NetBSD__) +#define TF_PLAT_STR___NetBSD__ TF_PLAT_STR_(__NetBSD__) +#else +#define TF_PLAT_STR___NetBSD__ +#endif +#if defined(__OpenBSD__) +#define TF_PLAT_STR___OpenBSD__ TF_PLAT_STR_(__OpenBSD__) +#else +#define TF_PLAT_STR___OpenBSD__ +#endif +#if defined(____MSYS__) +#define TF_PLAT_STR_____MSYS__ TF_PLAT_STR_(____MSYS__) +#else +#define TF_PLAT_STR_____MSYS__ +#endif +#if defined(__aarch64__) +#define TF_PLAT_STR___aarch64__ TF_PLAT_STR_(__aarch64__) +#else +#define TF_PLAT_STR___aarch64__ +#endif +#if defined(__alpha__) +#define TF_PLAT_STR___alpha__ TF_PLAT_STR_(__alpha__) +#else +#define TF_PLAT_STR___alpha__ +#endif +#if defined(__arm__) +#define TF_PLAT_STR___arm__ TF_PLAT_STR_(__arm__) +#else +#define TF_PLAT_STR___arm__ +#endif +#if defined(__i386__) +#define TF_PLAT_STR___i386__ TF_PLAT_STR_(__i386__) +#else +#define TF_PLAT_STR___i386__ +#endif +#if defined(__i686__) +#define TF_PLAT_STR___i686__ TF_PLAT_STR_(__i686__) +#else +#define TF_PLAT_STR___i686__ +#endif +#if defined(__ia64__) +#define TF_PLAT_STR___ia64__ TF_PLAT_STR_(__ia64__) +#else +#define TF_PLAT_STR___ia64__ +#endif +#if defined(__linux__) +#define TF_PLAT_STR___linux__ TF_PLAT_STR_(__linux__) +#else +#define TF_PLAT_STR___linux__ +#endif +#if defined(__mips32__) +#define TF_PLAT_STR___mips32__ TF_PLAT_STR_(__mips32__) +#else +#define TF_PLAT_STR___mips32__ +#endif +#if defined(__mips64__) +#define TF_PLAT_STR___mips64__ TF_PLAT_STR_(__mips64__) +#else +#define TF_PLAT_STR___mips64__ +#endif +#if defined(__powerpc64__) +#define TF_PLAT_STR___powerpc64__ TF_PLAT_STR_(__powerpc64__) +#else +#define TF_PLAT_STR___powerpc64__ +#endif +#if defined(__powerpc__) +#define TF_PLAT_STR___powerpc__ TF_PLAT_STR_(__powerpc__) +#else +#define TF_PLAT_STR___powerpc__ +#endif +#if defined(__riscv___) +#define TF_PLAT_STR___riscv___ TF_PLAT_STR_(__riscv___) +#else +#define TF_PLAT_STR___riscv___ +#endif +#if defined(__s390x__) +#define TF_PLAT_STR___s390x__ TF_PLAT_STR_(__s390x__) +#else +#define TF_PLAT_STR___s390x__ +#endif +#if defined(__sparc64__) +#define TF_PLAT_STR___sparc64__ TF_PLAT_STR_(__sparc64__) +#else +#define TF_PLAT_STR___sparc64__ +#endif +#if defined(__sparc__) +#define TF_PLAT_STR___sparc__ TF_PLAT_STR_(__sparc__) +#else +#define TF_PLAT_STR___sparc__ +#endif +#if defined(__x86_64__) +#define TF_PLAT_STR___x86_64__ TF_PLAT_STR_(__x86_64__) +#else +#define TF_PLAT_STR___x86_64__ +#endif + +#endif // TENSORFLOW_CORE_PLATFORM_PLATFORM_STRINGS_COMPUTED_H_ diff --git a/tensorflow/core/platform/platform_strings_test.cc b/tensorflow/core/platform/platform_strings_test.cc new file mode 100644 index 0000000000..5251f10d41 --- /dev/null +++ b/tensorflow/core/platform/platform_strings_test.cc @@ -0,0 +1,146 @@ +/* Copyright 2018 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. +==============================================================================*/ + +// Test for the platform_strings.h header file. + +#include +#include +#include +#include + +#include +#include + +#include "tensorflow/core/lib/io/path.h" +#include "tensorflow/core/lib/strings/str_util.h" +#include "tensorflow/core/platform/env.h" +#include "tensorflow/core/platform/init_main.h" +#include "tensorflow/core/platform/logging.h" +#include "tensorflow/core/platform/platform_strings.h" + +// Embed the platform strings in this binary. +TF_PLATFORM_STRINGS() + +// A vector of strings. +typedef std::vector string_vec; + +// Append to *found the strings within the named file with the platform_strings +// magic prefix, and return true; or return false on error. + +// Print the platform strings embedded in the binary file_name and return 0, +// on on error return 2. +static int PrintStrings(const std::string file_name) { + int rc = 0; + string_vec str; + if (!tensorflow::GetPlatformStrings(file_name, &str)) { + for (int i = 0; i != str.size(); i++) { + printf("%s\n", str[i].c_str()); + } + } else { + perror(file_name.c_str()); + rc = 2; + } + return rc; +} + +// Return whether str[] conatins a string with prefix "macro_name="; if so, +// set *pvalue to the suffix. +static bool GetValue(const string_vec &str, const std::string ¯o_name, + std::string *pvalue) { + std::string nam_eq = macro_name + "="; + int i = 0; + while (i != str.size() && !tensorflow::str_util::StartsWith(str[i], nam_eq)) { + i++; + } + bool found = (i != str.size()); + if (found) { + *pvalue = str[i].substr(nam_eq.size()); + } + return found; +} + +// If macro_name[] is not equal to value[], check that str[] contains the +// string "macro_name=value". Otherwise, check that str[] does not contain any +// string starting with macro_name=". +static void CheckStr(const string_vec &str, const std::string ¯o_name, + const std::string &value) { + std::string value_from_str; + if (GetValue(str, macro_name, &value_from_str)) { + if (value != value_from_str) { + // Output everything found, to aid debugging. + LOG(ERROR) << "===== value=" << value + << " value_from_str=" << value_from_str; + for (int i = 0; i != str.size(); i++) { + LOG(ERROR) << "% " << str[i]; + } + LOG(ERROR) << "====="; + } + CHECK_EQ(value, value_from_str) << " " << macro_name << ": bad value"; + } else { + // If the string is not found, we expect value to be macro_name. + if (value != macro_name) { + // Output everything found, to aid debugging. + LOG(ERROR) << "===== value=" << value << " macro_name=" << macro_name; + for (int i = 0; i != str.size(); i++) { + LOG(ERROR) << "% " << str[i]; + } + LOG(ERROR) << "====="; + } + CHECK_EQ(value, macro_name) << " " << macro_name << ": not found in binary"; + } +} + +// Helper for AS_STR(), below, to perform macro expansion. +#define AS_STR_1_(x) #x + +// Yield x after macro expansion as a nul-terminated constant string. +#define AS_STR(x) AS_STR_1_(x) + +// Run the test, and return 0 on success, 2 otherwise. +static int RunTest(const std::string &binary_name) { + int rc = 0; + string_vec str; + + if (!tensorflow::GetPlatformStrings(binary_name, &str)) { + CheckStr(str, "__linux__", AS_STR(__linux__)); + CheckStr(str, "_WIN32", AS_STR(_WIN32)); + CheckStr(str, "__APPLE__", AS_STR(__APPLE__)); + CheckStr(str, "__x86_64__", AS_STR(__x86_64__)); + CheckStr(str, "__aarch64__", AS_STR(__aarch64__)); + CheckStr(str, "__powerpc64__", AS_STR(__powerpc64__)); + CheckStr(str, "TF_PLAT_STR_VERSION", TF_PLAT_STR_VERSION_); + } else { + perror(binary_name.c_str()); + rc = 2; + } + + return rc; +} + +int main(int argc, char *argv[]) { + tensorflow::Env *env = tensorflow::Env::Default(); + static const char usage[] = "usage: platform_strings_test [file...]"; + int rc = 0; + tensorflow::port::InitMain(usage, &argc, &argv); + if (argc == 1) { + printf("rc=%d\n", PrintStrings(env->GetExecutablePath())); + rc = RunTest(env->GetExecutablePath()); + } else { + for (int argn = 1; argn != argc; argn++) { + rc |= PrintStrings(argv[argn]); + } + } + return rc; +} diff --git a/tensorflow/core/platform/posix/posix_file_system.cc b/tensorflow/core/platform/posix/posix_file_system.cc index c7afab9583..fc48cab564 100644 --- a/tensorflow/core/platform/posix/posix_file_system.cc +++ b/tensorflow/core/platform/posix/posix_file_system.cc @@ -240,11 +240,14 @@ Status PosixFileSystem::DeleteFile(const string& fname) { } Status PosixFileSystem::CreateDir(const string& name) { - Status result; - if (mkdir(TranslateName(name).c_str(), 0755) != 0) { - result = IOError(name, errno); + string translated = TranslateName(name); + if (translated.empty()) { + return errors::AlreadyExists(name); } - return result; + if (mkdir(translated.c_str(), 0755) != 0) { + return IOError(name, errno); + } + return Status::OK(); } Status PosixFileSystem::DeleteDir(const string& name) { diff --git a/tensorflow/core/platform/regexp.h b/tensorflow/core/platform/regexp.h index a4eedf3045..ca9ca1e244 100644 --- a/tensorflow/core/platform/regexp.h +++ b/tensorflow/core/platform/regexp.h @@ -16,6 +16,7 @@ limitations under the License. #ifndef TENSORFLOW_PLATFORM_REGEXP_H_ #define TENSORFLOW_PLATFORM_REGEXP_H_ +#include "absl/strings/string_view.h" #include "tensorflow/core/platform/platform.h" #include "tensorflow/core/platform/types.h" @@ -23,7 +24,7 @@ limitations under the License. defined(GOOGLE_RE2) #include "tensorflow/core/platform/google/build_config/re2.h" namespace tensorflow { -typedef ::StringPiece RegexpStringPiece; +typedef absl::string_view RegexpStringPiece; } // namespace tensorflow #else diff --git a/tensorflow/core/platform/windows/windows_file_system.cc b/tensorflow/core/platform/windows/windows_file_system.cc index 6cf79634d7..993b9906b1 100644 --- a/tensorflow/core/platform/windows/windows_file_system.cc +++ b/tensorflow/core/platform/windows/windows_file_system.cc @@ -439,6 +439,9 @@ Status WindowsFileSystem::DeleteFile(const string& fname) { Status WindowsFileSystem::CreateDir(const string& name) { Status result; std::wstring ws_name = Utf8ToWideChar(name); + if (ws_name.empty()) { + return errors::AlreadyExists(name); + } if (_wmkdir(ws_name.c_str()) != 0) { result = IOError("Failed to create a directory: " + name, errno); } diff --git a/tensorflow/core/profiler/internal/tfprof_code.cc b/tensorflow/core/profiler/internal/tfprof_code.cc index 744e1e95de..0c26855a43 100644 --- a/tensorflow/core/profiler/internal/tfprof_code.cc +++ b/tensorflow/core/profiler/internal/tfprof_code.cc @@ -183,7 +183,7 @@ class Samples { // This method adds the statistics of graph nodes created by the python // call. void Add(const CodeNode* node, const std::vector& location_ids) { - // displayed leaf might not be true leaf. Retrive the true leaves for + // displayed leaf might not be true leaf. Retrieve the true leaves for // stats. std::vector all_leaf = FetchAllLeaf(node); CHECK(!all_leaf.empty()) << node->name(); diff --git a/tensorflow/core/profiler/internal/tfprof_node.cc b/tensorflow/core/profiler/internal/tfprof_node.cc index 86cb20de7b..8796234be0 100644 --- a/tensorflow/core/profiler/internal/tfprof_node.cc +++ b/tensorflow/core/profiler/internal/tfprof_node.cc @@ -151,7 +151,7 @@ void ExecStep::AddMemoryStats(const string& dev, } // TODO(xpan): Make this more accurate: - // High level: Memory tracking is suspicous and requires large scale + // High level: Memory tracking is suspicious and requires large scale // clean up. // Investigte the memory usage difference between CPU/GPU with OpViewTest. // diff --git a/tensorflow/core/protobuf/config.proto b/tensorflow/core/protobuf/config.proto index 4689af06af..b3dc5dccc0 100644 --- a/tensorflow/core/protobuf/config.proto +++ b/tensorflow/core/protobuf/config.proto @@ -291,6 +291,13 @@ message RPCOptions { // transport for client-master communication that avoids the RPC // stack. This option is primarily for used testing the RPC stack. bool use_rpc_for_inprocess_master = 1; + + // The compression algorithm to be used. One of "deflate", "gzip". + string compression_algorithm = 2; + + // If compression_algorithm is set, the compression level to be used. + // From 0 (no compression), up to 3. + int32 compression_level = 3; }; // Session configuration parameters. @@ -413,6 +420,11 @@ message ConfigProto { // Any positive value sets the max chunk size. 0 defaults to 4096. // Any negative value indicates no max, i.e. one chunk only. int32 recv_buf_max_chunk = 4; + + // If true, and supported by the platform, the runtime will attempt to + // use NUMA affinity where applicable. One consequence will be the + // existence of as many CPU devices as there are available NUMA nodes. + bool use_numa_affinity = 5; }; Experimental experimental = 16; diff --git a/tensorflow/core/protobuf/master.proto b/tensorflow/core/protobuf/master.proto index 03022875e6..c104463c51 100644 --- a/tensorflow/core/protobuf/master.proto +++ b/tensorflow/core/protobuf/master.proto @@ -224,7 +224,7 @@ message CloseSessionResponse { message ResetRequest { // A list of container names, which may be empty. // - // If 'container' is not empty, releases resoures in the given + // If 'container' is not empty, releases resources in the given // containers in all devices. // // If 'container' is empty, releases resources in the default diff --git a/tensorflow/core/protobuf/rewriter_config.proto b/tensorflow/core/protobuf/rewriter_config.proto index d68f273536..515d673828 100644 --- a/tensorflow/core/protobuf/rewriter_config.proto +++ b/tensorflow/core/protobuf/rewriter_config.proto @@ -38,7 +38,7 @@ message RewriterConfig { } // Enum controlling the number of times to run optimizers. The default is to - // run them once. + // run them twice. enum NumIterationsType { DEFAULT_NUM_ITERS = 0; ONE = 1; diff --git a/tensorflow/core/util/dump_graph.cc b/tensorflow/core/util/dump_graph.cc new file mode 100644 index 0000000000..523d37ecc2 --- /dev/null +++ b/tensorflow/core/util/dump_graph.cc @@ -0,0 +1,131 @@ +/* Copyright 2018 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. +==============================================================================*/ + +// Helper functions for dumping Graphs, GraphDefs, and FunctionDefs to files for +// debugging. + +#include "tensorflow/core/util/dump_graph.h" + +#include "absl/strings/str_cat.h" +#include "tensorflow/core/lib/strings/proto_serialization.h" +#include "tensorflow/core/platform/env.h" +#include "tensorflow/core/platform/mutex.h" + +namespace tensorflow { + +namespace { + +struct NameCounts { + mutex counts_mutex; + std::unordered_map counts; +}; + +string MakeUniqueFilename(string name) { + static NameCounts& instance = *new NameCounts; + + // Remove illegal characters from `name`. + for (int i = 0; i < name.size(); ++i) { + char ch = name[i]; + if (ch == '/' || ch == '[' || ch == ']' || ch == '*' || ch == '?') { + name[i] = '_'; + } + } + + int count; + { + mutex_lock lock(instance.counts_mutex); + count = instance.counts[name]++; + } + + string filename = name; + if (count > 0) { + absl::StrAppend(&filename, "_", count); + } + absl::StrAppend(&filename, ".pbtxt"); + return filename; +} + +#if defined(TENSORFLOW_LITE_PROTOS) +Status WriteToFile(const string& filepath, + const ::tensorflow::protobuf::MessageLite& proto) { + string s; + if (!SerializeToStringDeterministic(proto, &s)) { + return errors::Internal("Failed to serialize proto to string."); + } + return WriteStringToFile(Env::Default(), filepath, s); +} +#else +Status WriteToFile(const string& filepath, + const ::tensorflow::protobuf::Message& proto) { + return WriteTextProto(Env::Default(), filepath, proto); +} +#endif + +template +string WriteTextProtoToUniqueFile(Env* env, const string& name, + const char* proto_type, T& proto, + const string& dirname) { + const char* dir = nullptr; + if (!dirname.empty()) { + dir = dirname.c_str(); + } else { + dir = getenv("TF_DUMP_GRAPH_PREFIX"); + } + if (!dir) { + return "(TF_DUMP_GRAPH_PREFIX not specified)"; + } + Status status = env->RecursivelyCreateDir(dir); + if (!status.ok()) { + LOG(WARNING) << "Failed to create " << dir << " for dumping " << proto_type + << ": " << status; + return "(unavailable)"; + } + string filepath = absl::StrCat(dir, "/", MakeUniqueFilename(name)); + status = WriteToFile(filepath, proto); + if (!status.ok()) { + LOG(WARNING) << "Failed to dump " << proto_type << " to file: " << filepath + << " : " << status; + return "(unavailable)"; + } + LOG(INFO) << "Dumped " << proto_type << " to " << filepath; + return filepath; +} + +} // anonymous namespace + +string DumpGraphDefToFile(const string& name, GraphDef const& graph_def, + const string& dirname) { + return WriteTextProtoToUniqueFile(Env::Default(), name, "GraphDef", graph_def, + dirname); +} + +string DumpGraphToFile(const string& name, Graph const& graph, + const FunctionLibraryDefinition* flib_def, + const string& dirname) { + GraphDef graph_def; + graph.ToGraphDef(&graph_def); + if (flib_def) { + *graph_def.mutable_library() = flib_def->ToProto(); + } + return DumpGraphDefToFile(name, graph_def, dirname); +} + +string DumpFunctionDefToFile(const string& name, FunctionDef const& fdef, + const string& dirname) { + return WriteTextProtoToUniqueFile(Env::Default(), name, "FunctionDef", fdef, + dirname); +} + +} // namespace tensorflow diff --git a/tensorflow/core/util/dump_graph.h b/tensorflow/core/util/dump_graph.h new file mode 100644 index 0000000000..03dc807a2b --- /dev/null +++ b/tensorflow/core/util/dump_graph.h @@ -0,0 +1,52 @@ +/* Copyright 2018 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. +==============================================================================*/ + +// Helper functions for dumping Graphs, GraphDefs, and FunctionDefs to files for +// debugging. + +#ifndef TENSORFLOW_CORE_UTIL_DUMP_GRAPH_H_ +#define TENSORFLOW_CORE_UTIL_DUMP_GRAPH_H_ + +#include "tensorflow/core/framework/function.h" +#include "tensorflow/core/framework/graph.pb.h" +#include "tensorflow/core/graph/graph.h" + +namespace tensorflow { + +// Dumps 'graph_def' to a file, as a GraphDef text proto. Returns the file name +// chosen. +// +// Automatically picks a file name. Prefixes 'name' with the value of the +// TF_DUMP_GRAPH_PREFIX environment variable if 'dirname' is empty, and suffixes +// 'name' with ".pbtxt" to form a name. If a graph has already been dumped by +// this process with the same name, suffixes with "_n.pbtxt", where 'n' is a +// sequence number. +string DumpGraphDefToFile(const string& name, GraphDef const& graph_def, + const string& dirname = ""); + +// Similar to DumpGraphDefToFile, but builds the GraphDef to dump from a 'graph' +// and an optional function library 'flib_def'. Returns the file name chosen. +string DumpGraphToFile(const string& name, Graph const& graph, + const FunctionLibraryDefinition* flib_def = nullptr, + const string& dirname = ""); + +// Similar to DumpGraphDefToFile, but dumps a function as a FunctionDef text +// proto. Returns the file name chosen. +string DumpFunctionDefToFile(const string& name, FunctionDef const& fdef, + const string& dirname = ""); + +} // namespace tensorflow + +#endif // TENSORFLOW_CORE_UTIL_DUMP_GRAPH_H_ diff --git a/tensorflow/core/util/dump_graph_test.cc b/tensorflow/core/util/dump_graph_test.cc new file mode 100644 index 0000000000..d01c1c5a02 --- /dev/null +++ b/tensorflow/core/util/dump_graph_test.cc @@ -0,0 +1,62 @@ +/* Copyright 2018 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/util/dump_graph.h" +#include "tensorflow/core/graph/graph.h" +#include "tensorflow/core/graph/node_builder.h" +#include "tensorflow/core/lib/io/path.h" +#include "tensorflow/core/lib/strings/proto_serialization.h" +#include "tensorflow/core/platform/env.h" +#include "tensorflow/core/platform/test.h" + +namespace tensorflow { +namespace { + +TEST(DumpGraph, DumpGraphToFileSuccess) { + Graph graph(OpRegistry::Global()); + Node* node; + TF_CHECK_OK(NodeBuilder("A", "NoOp").Finalize(&graph, &node)); + + setenv("TF_DUMP_GRAPH_PREFIX", testing::TmpDir().c_str(), 1); + string ret = DumpGraphToFile("graph", graph); + EXPECT_EQ(ret, io::JoinPath(testing::TmpDir(), "graph.pbtxt")); + ret = DumpGraphToFile("graph", graph); + EXPECT_EQ(ret, io::JoinPath(testing::TmpDir(), "graph_1.pbtxt")); + + GraphDef gdef; + TF_CHECK_OK(ReadTextProto( + Env::Default(), io::JoinPath(testing::TmpDir(), "graph.pbtxt"), &gdef)); + string read, written; + gdef.AppendToString(&read); + graph.ToGraphDefDebug().AppendToString(&written); + EXPECT_EQ(read, written); +} + +TEST(DumpGraph, DumpGraphToFileNoEnvPrefix) { + Graph graph(OpRegistry::Global()); + unsetenv("TF_DUMP_GRAPH_PREFIX"); + string ret = DumpGraphToFile("graph", graph); + EXPECT_EQ(ret, "(TF_DUMP_GRAPH_PREFIX not specified)"); +} + +TEST(DumpGraph, DumpFunctionDefToFileSuccess) { + FunctionDef fdef; + setenv("TF_DUMP_GRAPH_PREFIX", testing::TmpDir().c_str(), 1); + string ret = DumpFunctionDefToFile("function", fdef); + EXPECT_EQ(ret, io::JoinPath(testing::TmpDir(), "function.pbtxt")); +} + +} // namespace +} // namespace tensorflow diff --git a/tensorflow/core/util/mkl_util.h b/tensorflow/core/util/mkl_util.h index b7a6e0b690..928807458a 100644 --- a/tensorflow/core/util/mkl_util.h +++ b/tensorflow/core/util/mkl_util.h @@ -1644,6 +1644,9 @@ class MklDnnData { cpu_engine_(e) {} ~MklDnnData() { + if (allocated_buffer_ != nullptr) { + cpu_allocator()->DeallocateRaw(allocated_buffer_); + } cpu_engine_ = nullptr; // We don't own this. delete (user_memory_); delete (reorder_memory_); diff --git a/tensorflow/core/util/permutation_input_iterator.h b/tensorflow/core/util/permutation_input_iterator.h index f6375b2515..649318ebf3 100644 --- a/tensorflow/core/util/permutation_input_iterator.h +++ b/tensorflow/core/util/permutation_input_iterator.h @@ -13,8 +13,8 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#ifndef TENSORFLOW_UTIL_PERMUTATION_INPUT_ITERATOR_H_ -#define TENSORFLOW_UTIL_PERMUTATION_INPUT_ITERATOR_H_ +#ifndef TENSORFLOW_CORE_UTIL_PERMUTATION_INPUT_ITERATOR_H_ +#define TENSORFLOW_CORE_UTIL_PERMUTATION_INPUT_ITERATOR_H_ #include #include @@ -131,4 +131,4 @@ class PermutationInputIterator { } // end namespace tensorflow -#endif // TENSORFLOW_UTIL_PERMUTATION_INPUT_ITERATOR_H_ +#endif // TENSORFLOW_CORE_UTIL_PERMUTATION_INPUT_ITERATOR_H_ diff --git a/tensorflow/core/util/permutation_output_iterator.h b/tensorflow/core/util/permutation_output_iterator.h new file mode 100644 index 0000000000..638c0f4545 --- /dev/null +++ b/tensorflow/core/util/permutation_output_iterator.h @@ -0,0 +1,129 @@ +/* Copyright 2018 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_UTIL_PERMUTATION_OUTPUT_ITERATOR_H_ +#define TENSORFLOW_CORE_UTIL_PERMUTATION_OUTPUT_ITERATOR_H_ + +#include +#include + +namespace tensorflow { + +template +class PermutationOutputIterator { + public: + // Required iterator traits + typedef PermutationOutputIterator self_type; ///< My own type + typedef OffsetT difference_type; ///< Type to express the result of + ///< subtracting one iterator from another + typedef ValueType + value_type; ///< The type of the element the iterator can point to + typedef ValueType* pointer; ///< The type of a pointer to an element the + ///< iterator can point to + typedef ValueType& reference; ///< The type of a reference to an element the + ///< iterator can point to + + typedef std::random_access_iterator_tag + iterator_category; ///< The iterator category + + private: + OutputIteratorT output_itr; + IndexIteratorT index_itr; + + public: + /// Constructor + __host__ __device__ __forceinline__ PermutationOutputIterator( + OutputIteratorT output_itr, ///< Input iterator to wrap + IndexIteratorT index_itr) ///< Conversion functor to wrap + : output_itr(output_itr), index_itr(index_itr) {} + + /// Postfix increment + __host__ __device__ __forceinline__ self_type operator++(int) { + self_type retval = *this; + index_itr++; + return retval; + } + + /// Prefix increment + __host__ __device__ __forceinline__ self_type operator++() { + index_itr++; + return *this; + } + + /// Indirection + __host__ __device__ __forceinline__ reference operator*() const { + return output_itr[*index_itr]; + } + + /// Addition + template + __host__ __device__ __forceinline__ self_type operator+(Distance n) const { + self_type retval(output_itr, index_itr + n); + return retval; + } + + /// Addition assignment + template + __host__ __device__ __forceinline__ self_type& operator+=(Distance n) { + index_itr += n; + return *this; + } + + /// Subtraction + template + __host__ __device__ __forceinline__ self_type operator-(Distance n) const { + self_type retval(output_itr, index_itr - n); + return retval; + } + + /// Subtraction assignment + template + __host__ __device__ __forceinline__ self_type& operator-=(Distance n) { + index_itr -= n; + return *this; + } + + /// Distance + __host__ __device__ __forceinline__ difference_type + operator-(self_type other) const { + return index_itr - other.index_itr; + } + + /// Array subscript + template + __host__ __device__ __forceinline__ reference operator[](Distance n) const { + return output_itr[index_itr[n]]; + } + + /// Equal to + __host__ __device__ __forceinline__ bool operator==(const self_type& rhs) { + return (index_itr == rhs.index_itr && output_itr == rhs.output_itr); + } + + /// Not equal to + __host__ __device__ __forceinline__ bool operator!=(const self_type& rhs) { + return !(*this == rhs); + } + + /// ostream operator + friend std::ostream& operator<<(std::ostream& os, const self_type& itr) { + return os; + } +}; + +} // end namespace tensorflow + +#endif // TENSORFLOW_CORE_UTIL_PERMUTATION_OUTPUT_ITERATOR_H_ diff --git a/tensorflow/core/util/sparse/sparse_tensor.h b/tensorflow/core/util/sparse/sparse_tensor.h index b9ca8ab395..89c163aa51 100644 --- a/tensorflow/core/util/sparse/sparse_tensor.h +++ b/tensorflow/core/util/sparse/sparse_tensor.h @@ -238,15 +238,6 @@ class SparseTensor { static Status Split(const SparseTensor& tensor, const int split_dim, const int num_split, std::vector* result); - template - ABSL_DEPRECATED( - "Use the form of Split() that takes an output pointer and returns a " - "status instead.") - static std::vector Split(const SparseTensor& tensor, - const int split_dim, - const int num_split, - Status* status = nullptr); - // Slice() will slice the input SparseTensor into a SparseTensor based on // specified start and size. Both start and size are 1-D array with each // element of the array representing one dimension. The start is the start @@ -578,10 +569,9 @@ SparseTensor SparseTensor::Concat( } template -std::vector SparseTensor::Split(const SparseTensor& input_tensor, - const int split_dim, - const int num_split, - Status* status /* = nullptr */) { +Status SparseTensor::Split(const SparseTensor& input_tensor, + const int split_dim, const int num_split, + std::vector* result) { std::vector output_indices; std::vector output_values; std::vector output_shapes; @@ -601,17 +591,15 @@ std::vector SparseTensor::Split(const SparseTensor& input_tensor, const int split_dim_size = input_tensor.shape()[split_dim]; const int split_size = split_dim_size / num_split; - if (!(num_split > 0 && num_split <= split_dim_size) && status != nullptr) { - *status = Status(error::INVALID_ARGUMENT, - strings::StrCat("num_split must be in the interval (0, ", - split_dim_size, "]")); - return {}; + if (!(num_split > 0 && num_split <= split_dim_size)) { + return Status(error::INVALID_ARGUMENT, + strings::StrCat("num_split must be in the interval (0, ", + split_dim_size, "]")); } if (!(split_dim >= 0 && split_dim < num_dim)) { - *status = Status( + return Status( error::INVALID_ARGUMENT, strings::StrCat("num_dim must be in the interval [0, ", num_dim, ")")); - return {}; } const int residual = split_dim_size % num_split; @@ -649,28 +637,18 @@ std::vector SparseTensor::Split(const SparseTensor& input_tensor, } } - std::vector output_tensors; - output_tensors.reserve(num_split); + result->clear(); + result->reserve(num_split); for (int i = 0; i < num_split; ++i) { SparseTensor tensor; Status create_status = Create(output_indices[i], output_values[i], output_shapes[i], &tensor); - if (!create_status.ok() && status != nullptr) { - *status = create_status; - return {}; + if (!create_status.ok()) { + return create_status; } - output_tensors.push_back(std::move(tensor)); + result->push_back(std::move(tensor)); } - return output_tensors; -} - -template -Status SparseTensor::Split(const SparseTensor& input_tensor, - const int split_dim, const int num_split, - std::vector* result) { - Status status; - *result = Split(input_tensor, split_dim, num_split, &status); - return status; + return Status::OK(); } template diff --git a/tensorflow/core/util/stats_calculator.cc b/tensorflow/core/util/stats_calculator.cc index eb07754650..bce650f245 100644 --- a/tensorflow/core/util/stats_calculator.cc +++ b/tensorflow/core/util/stats_calculator.cc @@ -53,7 +53,7 @@ std::string StatsCalculator::HeaderString(const std::string& title) const { << " ==============================" << std::endl; InitField(stream, 24) << "[node type]"; - InitField(stream, 9) << "[start]"; + InitField(stream, 17) << "[start]"; InitField(stream, 9) << "[first]"; InitField(stream, 9) << "[avg ms]"; InitField(stream, 8) << "[%]"; @@ -77,7 +77,7 @@ std::string StatsCalculator::ColumnString(const Detail& detail, std::stringstream stream; InitField(stream, 24) << detail.type; - InitField(stream, 9) << start_ms; + InitField(stream, 17) << start_ms; InitField(stream, 9) << first_time_ms; InitField(stream, 9) << avg_time_ms; InitField(stream, 7) << percentage << "%"; diff --git a/tensorflow/core/util/strided_slice_op.cc b/tensorflow/core/util/strided_slice_op.cc index ad8a44a518..55688e5808 100644 --- a/tensorflow/core/util/strided_slice_op.cc +++ b/tensorflow/core/util/strided_slice_op.cc @@ -83,10 +83,17 @@ static Status TF_MUST_USE_RESULT BuildDenseSpec( { int full_index = 0; - const auto& strides_flat = sparse.strides_tensor.flat(); + const T* const strides_flat = sparse.strides_tensor.vec().data(); dense->begin_valid = sparse.begin_tensor != nullptr; dense->end_valid = sparse.end_tensor != nullptr; + const T* const begin_flat = sparse.begin_tensor != nullptr + ? sparse.begin_tensor->vec().data() + : nullptr; + const T* const end_flat = sparse.end_tensor != nullptr + ? sparse.end_tensor->vec().data() + : nullptr; + for (int i = 0; i < sparse.dims; i++) { if ((1 << i) & sparse.ellipsis_mask) { // Expand the ellipsis into the appropriate indices @@ -112,16 +119,14 @@ static Status TF_MUST_USE_RESULT BuildDenseSpec( } // Gather slicing spec into appropriate index - if (sparse.begin_tensor != nullptr) { - const auto& begin_flat = sparse.begin_tensor->flat(); - dense->begin[full_index] = internal::SubtleMustCopy(begin_flat(i)); + if (begin_flat != nullptr) { + dense->begin[full_index] = internal::SubtleMustCopy(begin_flat[i]); } - if (sparse.end_tensor != nullptr) { - const auto& end_flat = sparse.end_tensor->flat(); - dense->end[full_index] = internal::SubtleMustCopy(end_flat(i)); + if (end_flat != nullptr) { + dense->end[full_index] = internal::SubtleMustCopy(end_flat[i]); } dense->strides[full_index] = - internal::SubtleMustCopy(strides_flat(i)); + internal::SubtleMustCopy(strides_flat[i]); if (sparse.begin_mask & (1 << i)) { dense->begin_mask |= (1 << full_index); } diff --git a/tensorflow/core/util/tensor_bundle/tensor_bundle.cc b/tensorflow/core/util/tensor_bundle/tensor_bundle.cc index 2dcb57a1f9..3709ee5ae3 100644 --- a/tensorflow/core/util/tensor_bundle/tensor_bundle.cc +++ b/tensorflow/core/util/tensor_bundle/tensor_bundle.cc @@ -785,7 +785,7 @@ Status BundleReader::GetBundleEntryProto(StringPiece key, TF_RETURN_IF_ERROR( ParseEntryProto(iter_->key(), iter_->value(), &entry_copy)); if (!TensorShape::IsValid(entry_copy.shape())) { - return errors::DataLoss("Invaid tensor shape: ", key, " ", + return errors::DataLoss("Invalid tensor shape: ", key, " ", ProtoShortDebugString(entry_copy.shape())); } @@ -895,7 +895,7 @@ Status BundleReader::ReadCurrent(Tensor* val) { BundleEntryProto entry; TF_RETURN_IF_ERROR(ParseEntryProto(iter_->key(), iter_->value(), &entry)); if (!TensorShape::IsValid(entry.shape())) { - return errors::DataLoss("Invaid tensor shape: ", iter_->key(), " ", + return errors::DataLoss("Invalid tensor shape: ", iter_->key(), " ", ProtoShortDebugString(entry.shape())); } diff --git a/tensorflow/core/util/tensor_format.h b/tensorflow/core/util/tensor_format.h index b0c349dd90..a296fb447e 100644 --- a/tensorflow/core/util/tensor_format.h +++ b/tensorflow/core/util/tensor_format.h @@ -498,7 +498,8 @@ inline TensorShape ShapeFromFormat(TensorFormat format, int64 N, dim_sizes[GetTensorBatchDimIndex(dims, format)] = N; for (int dim = 0; static_cast(dim) < spatial.size(); dim++) { auto dim_size = spatial[dim]; - if (format == FORMAT_NHWC_VECT_W && dim == spatial.size() - 1) { + if (format == FORMAT_NHWC_VECT_W && + static_cast(dim) == spatial.size() - 1) { CHECK_EQ(0, dim_size % 4) << "FORMAT_NHWC_VECT_W requires W to be a multiple of 4, but W=" << dim_size; diff --git a/tensorflow/core/util/tensor_ops_util.h b/tensorflow/core/util/tensor_ops_util.h new file mode 100644 index 0000000000..615f088a9b --- /dev/null +++ b/tensorflow/core/util/tensor_ops_util.h @@ -0,0 +1,128 @@ +/* Copyright 2018 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_UTIL_TENSOR_OPS_UTIL_H_ +#define TENSORFLOW_CORE_UTIL_TENSOR_OPS_UTIL_H_ + +#define EIGEN_USE_THREADS + +#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor" +#include "tensorflow/core/framework/op_kernel.h" +#include "tensorflow/core/framework/register_types.h" +#include "tensorflow/core/framework/tensor.h" +#include "tensorflow/core/framework/types.h" +#include "tensorflow/core/framework/variant_op_registry.h" +#include "tensorflow/core/lib/core/status.h" + +namespace tensorflow { + +typedef Eigen::ThreadPoolDevice CPUDevice; +typedef Eigen::GpuDevice GPUDevice; + +template +Status ZerosLikeTensor(OpKernelContext* ctx, const Tensor& x, Tensor* out) { + AllocatorAttributes attr; + if (x.dtype() == DT_VARIANT) { + attr.set_on_host(true); + } + TF_RETURN_IF_ERROR(ctx->allocate_temp(x.dtype(), x.shape(), out, attr)); + + switch (out->dtype()) { +#define DTYPE_CASE(dtype) \ + case DataTypeToEnum::value: \ + /* TODO(skyewm): use SetZeroFunctor like in ZerosLikeOp? */ \ + out->flat().device(ctx->eigen_device()) = \ + out->flat().constant(dtype(0)); \ + break; + + TF_CALL_POD_TYPES(DTYPE_CASE) +#undef DTYPE_CASE + + case DT_INVALID: { + *out = Tensor(DT_INVALID); + break; + } + case DataTypeToEnum::value: { + Variant* out_variant = out->scalar().data(); + TF_RETURN_IF_ERROR( + UnaryOpVariant(ctx, ZEROS_LIKE_VARIANT_UNARY_OP, + x.scalar()(), out_variant)); + break; + } + default: + return errors::InvalidArgument( + "Trying to compute zeros_like for unsupported dtype ", + DataTypeString(out->dtype())); + } + return Status::OK(); +} + +template +Status BinaryAddTensors(OpKernelContext* ctx, const Tensor& a, const Tensor& b, + Tensor* out) { + if (a.dtype() == DT_INVALID) { + *out = b; + return Status::OK(); + } + if (b.dtype() == DT_INVALID) { + *out = a; + return Status::OK(); + } + if (a.dtype() != b.dtype()) { + return errors::InvalidArgument( + "Trying to add two tensors with incompatible element types. ", + "One is ", DataTypeString(a.dtype()), " and the other is ", + DataTypeString(b.dtype())); + } + if (a.shape() != b.shape()) { + // TODO(apassos) support broadcasting additions here? + return errors::InvalidArgument( + "Trying to add two tensors with incompatible element shapes. ", + "One is ", a.shape().DebugString(), " and the other is ", + b.shape().DebugString()); + } + + AllocatorAttributes attr; + if (a.dtype() == DT_VARIANT) { + attr.set_on_host(true); + } + TF_RETURN_IF_ERROR(ctx->allocate_temp(a.dtype(), a.shape(), out, attr)); + + switch (out->dtype()) { +#define DTYPE_CASE(dtype) \ + case DataTypeToEnum::value: \ + out->flat().device(ctx->eigen_device()) = \ + a.flat() + b.flat(); \ + break; + + TF_CALL_NUMBER_TYPES(DTYPE_CASE) +#undef DTYPE_CASE + + case DataTypeToEnum::value: { + Variant* out_variant = out->scalar().data(); + TF_RETURN_IF_ERROR(BinaryOpVariants( + ctx, ADD_VARIANT_BINARY_OP, a.scalar()(), + b.scalar()(), out_variant)); + break; + } + default: + return errors::InvalidArgument("Trying to add unsupported dtype ", + out->dtype()); + } + return Status::OK(); +} + +} // namespace tensorflow + +#endif // TENSORFLOW_CORE_UTIL_TENSOR_OPS_UTIL_H_ diff --git a/tensorflow/examples/adding_an_op/fact_test.py b/tensorflow/examples/adding_an_op/fact_test.py index 11163e7ba5..46beaebe0c 100644 --- a/tensorflow/examples/adding_an_op/fact_test.py +++ b/tensorflow/examples/adding_an_op/fact_test.py @@ -19,10 +19,12 @@ from __future__ import division from __future__ import print_function import tensorflow as tf +from tensorflow.python.framework import test_util class FactTest(tf.test.TestCase): + @test_util.run_deprecated_v1 def test(self): with self.cached_session(): print(tf.user_ops.my_fact().eval()) diff --git a/tensorflow/examples/adding_an_op/zero_out_1_test.py b/tensorflow/examples/adding_an_op/zero_out_1_test.py index 342d3a020c..459ac2dc27 100644 --- a/tensorflow/examples/adding_an_op/zero_out_1_test.py +++ b/tensorflow/examples/adding_an_op/zero_out_1_test.py @@ -23,10 +23,12 @@ import os.path import tensorflow as tf from tensorflow.examples.adding_an_op import zero_out_op_1 +from tensorflow.python.framework import test_util class ZeroOut1Test(tf.test.TestCase): + @test_util.run_deprecated_v1 def test(self): with self.cached_session(): result = zero_out_op_1.zero_out([5, 4, 3, 2, 1]) diff --git a/tensorflow/examples/adding_an_op/zero_out_2_test.py b/tensorflow/examples/adding_an_op/zero_out_2_test.py index 4504597817..650fd9546b 100644 --- a/tensorflow/examples/adding_an_op/zero_out_2_test.py +++ b/tensorflow/examples/adding_an_op/zero_out_2_test.py @@ -24,20 +24,24 @@ import tensorflow as tf from tensorflow.examples.adding_an_op import zero_out_grad_2 # pylint: disable=unused-import from tensorflow.examples.adding_an_op import zero_out_op_2 +from tensorflow.python.framework import test_util class ZeroOut2Test(tf.test.TestCase): + @test_util.run_deprecated_v1 def test(self): with self.cached_session(): result = zero_out_op_2.zero_out([5, 4, 3, 2, 1]) self.assertAllEqual(result.eval(), [5, 0, 0, 0, 0]) + @test_util.run_deprecated_v1 def test_2d(self): with self.cached_session(): result = zero_out_op_2.zero_out([[6, 5, 4], [3, 2, 1]]) self.assertAllEqual(result.eval(), [[6, 0, 0], [0, 0, 0]]) + @test_util.run_deprecated_v1 def test_grad(self): with self.cached_session(): shape = (5,) @@ -46,6 +50,7 @@ class ZeroOut2Test(tf.test.TestCase): err = tf.test.compute_gradient_error(x, shape, y, shape) self.assertLess(err, 1e-4) + @test_util.run_deprecated_v1 def test_grad_2d(self): with self.cached_session(): shape = (2, 3) diff --git a/tensorflow/examples/adding_an_op/zero_out_3_test.py b/tensorflow/examples/adding_an_op/zero_out_3_test.py index 15d62495aa..8cbe2b6793 100644 --- a/tensorflow/examples/adding_an_op/zero_out_3_test.py +++ b/tensorflow/examples/adding_an_op/zero_out_3_test.py @@ -21,31 +21,36 @@ from __future__ import print_function import tensorflow as tf from tensorflow.examples.adding_an_op import zero_out_op_3 +from tensorflow.python.framework import test_util class ZeroOut3Test(tf.test.TestCase): + @test_util.run_deprecated_v1 def test(self): with self.cached_session(): result = zero_out_op_3.zero_out([5, 4, 3, 2, 1]) self.assertAllEqual(result.eval(), [5, 0, 0, 0, 0]) + @test_util.run_deprecated_v1 def testAttr(self): with self.cached_session(): result = zero_out_op_3.zero_out([5, 4, 3, 2, 1], preserve_index=3) self.assertAllEqual(result.eval(), [0, 0, 0, 2, 0]) + @test_util.run_deprecated_v1 def testNegative(self): with self.cached_session(): result = zero_out_op_3.zero_out([5, 4, 3, 2, 1], preserve_index=-1) with self.assertRaisesOpError("Need preserve_index >= 0, got -1"): - result.eval() + self.evaluate(result) + @test_util.run_deprecated_v1 def testLarge(self): with self.cached_session(): result = zero_out_op_3.zero_out([5, 4, 3, 2, 1], preserve_index=17) with self.assertRaisesOpError("preserve_index out of range"): - result.eval() + self.evaluate(result) if __name__ == "__main__": diff --git a/tensorflow/examples/autograph/integration_tests/BUILD b/tensorflow/examples/autograph/integration_tests/BUILD index d20c17b63b..2a4a0f75e7 100644 --- a/tensorflow/examples/autograph/integration_tests/BUILD +++ b/tensorflow/examples/autograph/integration_tests/BUILD @@ -22,7 +22,6 @@ py_test( "keras_test.py", ], srcs_version = "PY2AND3", - tags = ["no_windows"], deps = [ "//tensorflow:tensorflow_py", ], @@ -34,7 +33,6 @@ py_test( "list_literals_test.py", ], srcs_version = "PY2AND3", - tags = ["no_windows"], deps = [ "//tensorflow:tensorflow_py", ], diff --git a/tensorflow/examples/autograph/integration_tests/keras_test.py b/tensorflow/examples/autograph/integration_tests/keras_test.py index dca7c07b47..3fe33df920 100644 --- a/tensorflow/examples/autograph/integration_tests/keras_test.py +++ b/tensorflow/examples/autograph/integration_tests/keras_test.py @@ -21,6 +21,7 @@ from __future__ import print_function import tensorflow as tf from tensorflow.python import autograph +from tensorflow.python.framework import test_util class MinimalKeras(tf.keras.Model): @@ -84,6 +85,7 @@ class KerasTest(tf.test.TestCase): model = ModelWithStaticConditional(True) self.assertEqual(model.call(), 25) + @test_util.run_deprecated_v1 def test_recursive_true(self): with self.assertRaisesRegexp(NotImplementedError, 'Object conversion is not yet supported.'): @@ -93,10 +95,10 @@ class KerasTest(tf.test.TestCase): init = tf.global_variables_initializer() with tf.Session() as sess: - sess.run(init) + self.evaluate(init) sample_input = tf.random_uniform((1, 10, 10, 1)) output = model(sample_input) # pylint: disable=not-callable - self.assertEqual(sess.run(output).shape, (1, 3)) + self.assertEqual(self.evaluate(output).shape, (1, 3)) if __name__ == '__main__': diff --git a/tensorflow/examples/autograph/integration_tests/list_literals_test.py b/tensorflow/examples/autograph/integration_tests/list_literals_test.py index 917f5ff9d8..e85d4abcfc 100644 --- a/tensorflow/examples/autograph/integration_tests/list_literals_test.py +++ b/tensorflow/examples/autograph/integration_tests/list_literals_test.py @@ -34,7 +34,7 @@ class ListLiteralsTest(tf.test.TestCase): result = converted() with self.cached_session() as sess: - self.assertAllEqual(sess.run(result), [1, 2, 3]) + self.assertAllEqual(self.evaluate(result), [1, 2, 3]) if __name__ == '__main__': diff --git a/tensorflow/examples/get_started/regression/custom_regression.py b/tensorflow/examples/get_started/regression/custom_regression.py index 2e34362c5c..7b7cbb7866 100644 --- a/tensorflow/examples/get_started/regression/custom_regression.py +++ b/tensorflow/examples/get_started/regression/custom_regression.py @@ -100,12 +100,11 @@ def main(argv): # that the examples are well mixed. train.shuffle(1000).batch(128) # Repeat forever - .repeat().make_one_shot_iterator().get_next()) + .repeat()) # Build the validation input_fn. def input_test(): - return (test.shuffle(1000).batch(128) - .make_one_shot_iterator().get_next()) + return test.shuffle(1000).batch(128) # The first way assigns a unique weight to each category. To do this you must # specify the category's vocabulary (values outside this specification will diff --git a/tensorflow/examples/get_started/regression/dnn_regression.py b/tensorflow/examples/get_started/regression/dnn_regression.py index 951c93b52e..94669a5082 100644 --- a/tensorflow/examples/get_started/regression/dnn_regression.py +++ b/tensorflow/examples/get_started/regression/dnn_regression.py @@ -45,12 +45,11 @@ def main(argv): # that the examples are well mixed. train.shuffle(1000).batch(128) # Repeat forever - .repeat().make_one_shot_iterator().get_next()) + .repeat()) # Build the validation input_fn. def input_test(): - return (test.shuffle(1000).batch(128) - .make_one_shot_iterator().get_next()) + return test.shuffle(1000).batch(128) # The first way assigns a unique weight to each category. To do this you must # specify the category's vocabulary (values outside this specification will diff --git a/tensorflow/examples/get_started/regression/linear_regression_categorical.py b/tensorflow/examples/get_started/regression/linear_regression_categorical.py index e2ad415fbc..5312272a95 100644 --- a/tensorflow/examples/get_started/regression/linear_regression_categorical.py +++ b/tensorflow/examples/get_started/regression/linear_regression_categorical.py @@ -45,12 +45,11 @@ def main(argv): # that the examples are well mixed. train.shuffle(1000).batch(128) # Repeat forever - .repeat().make_one_shot_iterator().get_next()) + .repeat()) # Build the validation input_fn. def input_test(): - return (test.shuffle(1000).batch(128) - .make_one_shot_iterator().get_next()) + return test.shuffle(1000).batch(128) # The following code demonstrates two of the ways that `feature_columns` can # be used to build a model with categorical inputs. diff --git a/tensorflow/examples/how_tos/reading_data/fully_connected_reader.py b/tensorflow/examples/how_tos/reading_data/fully_connected_reader.py index 7402247448..5c52a2c846 100644 --- a/tensorflow/examples/how_tos/reading_data/fully_connected_reader.py +++ b/tensorflow/examples/how_tos/reading_data/fully_connected_reader.py @@ -126,7 +126,7 @@ def inputs(train, batch_size, num_epochs): dataset = dataset.repeat(num_epochs) dataset = dataset.batch(batch_size) - iterator = dataset.make_one_shot_iterator() + iterator = tf.compat.v1.data.make_one_shot_iterator(dataset) return iterator.get_next() diff --git a/tensorflow/examples/learn/iris_custom_decay_dnn.py b/tensorflow/examples/learn/iris_custom_decay_dnn.py index 4a219694d1..73bf20fada 100644 --- a/tensorflow/examples/learn/iris_custom_decay_dnn.py +++ b/tensorflow/examples/learn/iris_custom_decay_dnn.py @@ -76,12 +76,12 @@ def main(unused_argv): classifier = tf.estimator.Estimator(model_fn=my_model) # Train. - train_input_fn = tf.estimator.inputs.numpy_input_fn( + train_input_fn = tf.compat.v1.estimator.inputs.numpy_input_fn( x={X_FEATURE: x_train}, y=y_train, num_epochs=None, shuffle=True) classifier.train(input_fn=train_input_fn, steps=1000) # Predict. - test_input_fn = tf.estimator.inputs.numpy_input_fn( + test_input_fn = tf.compat.v1.estimator.inputs.numpy_input_fn( x={X_FEATURE: x_test}, y=y_test, num_epochs=1, shuffle=False) predictions = classifier.predict(input_fn=test_input_fn) y_predicted = np.array(list(p['class'] for p in predictions)) diff --git a/tensorflow/examples/learn/iris_custom_model.py b/tensorflow/examples/learn/iris_custom_model.py index c6bdb86ba5..bf34d72ba0 100644 --- a/tensorflow/examples/learn/iris_custom_model.py +++ b/tensorflow/examples/learn/iris_custom_model.py @@ -73,12 +73,12 @@ def main(unused_argv): classifier = tf.estimator.Estimator(model_fn=my_model) # Train. - train_input_fn = tf.estimator.inputs.numpy_input_fn( + train_input_fn = tf.compat.v1.estimator.inputs.numpy_input_fn( x={X_FEATURE: x_train}, y=y_train, num_epochs=None, shuffle=True) classifier.train(input_fn=train_input_fn, steps=1000) # Predict. - test_input_fn = tf.estimator.inputs.numpy_input_fn( + test_input_fn = tf.compat.v1.estimator.inputs.numpy_input_fn( x={X_FEATURE: x_test}, y=y_test, num_epochs=1, shuffle=False) predictions = classifier.predict(input_fn=test_input_fn) y_predicted = np.array(list(p['class'] for p in predictions)) diff --git a/tensorflow/examples/speech_commands/freeze_test.py b/tensorflow/examples/speech_commands/freeze_test.py index 0c7ca9bc01..9ed9050035 100644 --- a/tensorflow/examples/speech_commands/freeze_test.py +++ b/tensorflow/examples/speech_commands/freeze_test.py @@ -19,11 +19,13 @@ from __future__ import division from __future__ import print_function from tensorflow.examples.speech_commands import freeze +from tensorflow.python.framework import test_util from tensorflow.python.platform import test class FreezeTest(test.TestCase): + @test_util.run_deprecated_v1 def testCreateInferenceGraphWithMfcc(self): with self.cached_session() as sess: freeze.create_inference_graph( @@ -43,6 +45,7 @@ class FreezeTest(test.TestCase): ops = [node.op for node in sess.graph_def.node] self.assertEqual(1, ops.count('Mfcc')) + @test_util.run_deprecated_v1 def testCreateInferenceGraphWithoutMfcc(self): with self.cached_session() as sess: freeze.create_inference_graph( @@ -62,6 +65,7 @@ class FreezeTest(test.TestCase): ops = [node.op for node in sess.graph_def.node] self.assertEqual(0, ops.count('Mfcc')) + @test_util.run_deprecated_v1 def testFeatureBinCount(self): with self.cached_session() as sess: freeze.create_inference_graph( diff --git a/tensorflow/examples/speech_commands/input_data_test.py b/tensorflow/examples/speech_commands/input_data_test.py index b766ba6de0..9269bb6c0b 100644 --- a/tensorflow/examples/speech_commands/input_data_test.py +++ b/tensorflow/examples/speech_commands/input_data_test.py @@ -26,6 +26,7 @@ import tensorflow as tf from tensorflow.contrib.framework.python.ops import audio_ops as contrib_audio from tensorflow.examples.speech_commands import input_data from tensorflow.examples.speech_commands import models +from tensorflow.python.framework import test_util from tensorflow.python.platform import test @@ -35,7 +36,7 @@ class InputDataTest(test.TestCase): with self.cached_session() as sess: sample_data = tf.zeros([32000, 2]) wav_encoder = contrib_audio.encode_wav(sample_data, 16000) - wav_data = sess.run(wav_encoder) + wav_data = self.evaluate(wav_encoder) return wav_data def _saveTestWavFile(self, filename, wav_data): @@ -96,6 +97,7 @@ class InputDataTest(test.TestCase): input_data.which_set("foo_nohash_0.wav", 10, 10), input_data.which_set("foo_nohash_1.wav", 10, 10)) + @test_util.run_deprecated_v1 def testPrepareDataIndex(self): tmp_dir = self.get_temp_dir() self._saveWavFolders(tmp_dir, ["a", "b", "c"], 100) @@ -125,6 +127,7 @@ class InputDataTest(test.TestCase): 10, self._model_settings(), tmp_dir) self.assertTrue("Expected to find" in str(e.exception)) + @test_util.run_deprecated_v1 def testPrepareBackgroundData(self): tmp_dir = self.get_temp_dir() background_dir = os.path.join(tmp_dir, "_background_noise_") @@ -156,6 +159,7 @@ class InputDataTest(test.TestCase): self.assertIsNotNone(loaded_data) self.assertEqual(16000, len(loaded_data)) + @test_util.run_deprecated_v1 def testPrepareProcessingGraph(self): tmp_dir = self.get_temp_dir() wav_dir = os.path.join(tmp_dir, "wavs") @@ -186,15 +190,19 @@ class InputDataTest(test.TestCase): self.assertIsNotNone(audio_processor.background_volume_placeholder_) self.assertIsNotNone(audio_processor.output_) + @test_util.run_deprecated_v1 def testGetDataAverage(self): self._runGetDataTest("average", 10) + @test_util.run_deprecated_v1 def testGetDataAverageLongWindow(self): self._runGetDataTest("average", 30) + @test_util.run_deprecated_v1 def testGetDataMfcc(self): self._runGetDataTest("mfcc", 30) + @test_util.run_deprecated_v1 def testGetUnprocessedData(self): tmp_dir = self.get_temp_dir() wav_dir = os.path.join(tmp_dir, "wavs") @@ -216,6 +224,7 @@ class InputDataTest(test.TestCase): self.assertEqual(10, len(result_data)) self.assertEqual(10, len(result_labels)) + @test_util.run_deprecated_v1 def testGetFeaturesForWav(self): tmp_dir = self.get_temp_dir() wav_dir = os.path.join(tmp_dir, "wavs") diff --git a/tensorflow/examples/speech_commands/label_wav_test.py b/tensorflow/examples/speech_commands/label_wav_test.py index f0af2a4798..77a88f98e1 100644 --- a/tensorflow/examples/speech_commands/label_wav_test.py +++ b/tensorflow/examples/speech_commands/label_wav_test.py @@ -33,7 +33,7 @@ class LabelWavTest(test.TestCase): with self.cached_session() as sess: sample_data = tf.zeros([1000, 2]) wav_encoder = contrib_audio.encode_wav(sample_data, 16000) - wav_data = sess.run(wav_encoder) + wav_data = self.evaluate(wav_encoder) return wav_data def _saveTestWavFile(self, filename, wav_data): diff --git a/tensorflow/examples/speech_commands/models_test.py b/tensorflow/examples/speech_commands/models_test.py index 04478c0962..cb9304eab8 100644 --- a/tensorflow/examples/speech_commands/models_test.py +++ b/tensorflow/examples/speech_commands/models_test.py @@ -21,6 +21,7 @@ from __future__ import print_function import tensorflow as tf from tensorflow.examples.speech_commands import models +from tensorflow.python.framework import test_util from tensorflow.python.platform import test @@ -47,6 +48,7 @@ class ModelsTest(test.TestCase): feature_bin_count=40, preprocess="mfcc")) + @test_util.run_deprecated_v1 def testCreateModelConvTraining(self): model_settings = self._modelSettings() with self.cached_session() as sess: @@ -58,6 +60,7 @@ class ModelsTest(test.TestCase): self.assertIsNotNone(sess.graph.get_tensor_by_name(logits.name)) self.assertIsNotNone(sess.graph.get_tensor_by_name(dropout_prob.name)) + @test_util.run_deprecated_v1 def testCreateModelConvInference(self): model_settings = self._modelSettings() with self.cached_session() as sess: @@ -67,6 +70,7 @@ class ModelsTest(test.TestCase): self.assertIsNotNone(logits) self.assertIsNotNone(sess.graph.get_tensor_by_name(logits.name)) + @test_util.run_deprecated_v1 def testCreateModelLowLatencyConvTraining(self): model_settings = self._modelSettings() with self.cached_session() as sess: @@ -78,6 +82,7 @@ class ModelsTest(test.TestCase): self.assertIsNotNone(sess.graph.get_tensor_by_name(logits.name)) self.assertIsNotNone(sess.graph.get_tensor_by_name(dropout_prob.name)) + @test_util.run_deprecated_v1 def testCreateModelFullyConnectedTraining(self): model_settings = self._modelSettings() with self.cached_session() as sess: @@ -98,6 +103,7 @@ class ModelsTest(test.TestCase): "bad_architecture", True) self.assertTrue("not recognized" in str(e.exception)) + @test_util.run_deprecated_v1 def testCreateModelTinyConvTraining(self): model_settings = self._modelSettings() with self.cached_session() as sess: diff --git a/tensorflow/examples/speech_commands/wav_to_features_test.py b/tensorflow/examples/speech_commands/wav_to_features_test.py index 87f2987693..6234490b26 100644 --- a/tensorflow/examples/speech_commands/wav_to_features_test.py +++ b/tensorflow/examples/speech_commands/wav_to_features_test.py @@ -24,6 +24,7 @@ import tensorflow as tf from tensorflow.contrib.framework.python.ops import audio_ops as contrib_audio from tensorflow.examples.speech_commands import wav_to_features +from tensorflow.python.framework import test_util from tensorflow.python.platform import test @@ -33,7 +34,7 @@ class WavToFeaturesTest(test.TestCase): with self.cached_session() as sess: sample_data = tf.zeros([32000, 2]) wav_encoder = contrib_audio.encode_wav(sample_data, 16000) - wav_data = sess.run(wav_encoder) + wav_data = self.evaluate(wav_encoder) return wav_data def _saveTestWavFile(self, filename, wav_data): @@ -49,6 +50,7 @@ class WavToFeaturesTest(test.TestCase): file_path = os.path.join(dir_name, "some_audio_%d.wav" % i) self._saveTestWavFile(file_path, wav_data) + @test_util.run_deprecated_v1 def testWavToFeatures(self): tmp_dir = self.get_temp_dir() wav_dir = os.path.join(tmp_dir, "wavs") diff --git a/tensorflow/examples/tutorials/layers/cnn_mnist.py b/tensorflow/examples/tutorials/layers/cnn_mnist.py index 1e8d7d05e1..670e929236 100644 --- a/tensorflow/examples/tutorials/layers/cnn_mnist.py +++ b/tensorflow/examples/tutorials/layers/cnn_mnist.py @@ -134,7 +134,7 @@ def main(unused_argv): tensors=tensors_to_log, every_n_iter=50) # Train the model - train_input_fn = tf.estimator.inputs.numpy_input_fn( + train_input_fn = tf.compat.v1.estimator.inputs.numpy_input_fn( x={"x": train_data}, y=train_labels, batch_size=100, @@ -146,11 +146,8 @@ def main(unused_argv): hooks=[logging_hook]) # Evaluate the model and print results - eval_input_fn = tf.estimator.inputs.numpy_input_fn( - x={"x": eval_data}, - y=eval_labels, - num_epochs=1, - shuffle=False) + eval_input_fn = tf.compat.v1.estimator.inputs.numpy_input_fn( + x={"x": eval_data}, y=eval_labels, num_epochs=1, shuffle=False) eval_results = mnist_classifier.evaluate(input_fn=eval_input_fn) print(eval_results) diff --git a/tensorflow/go/README.md b/tensorflow/go/README.md index 3989f9b25a..f53f6fc989 100644 --- a/tensorflow/go/README.md +++ b/tensorflow/go/README.md @@ -23,7 +23,7 @@ from source. - [bazel](https://www.bazel.build/versions/master/docs/install.html) - Environment to build TensorFlow from source code - ([Linux of macOS](https://www.tensorflow.org/install/source)). + ([Linux or macOS](https://www.tensorflow.org/install/source)). If you don't need GPU support, then try the following: ```sh diff --git a/tensorflow/go/graph.go b/tensorflow/go/graph.go index 67e42aa961..6ff41ca916 100644 --- a/tensorflow/go/graph.go +++ b/tensorflow/go/graph.go @@ -112,9 +112,17 @@ func (g *Graph) ImportWithOptions(def []byte, options GraphImportOptions) error C.TF_ImportGraphDefOptionsSetPrefix(opts, cprefix) if len(options.Device) != 0 { - cdev := C.CString(options.Device) - defer C.free(unsafe.Pointer(cdev)) - C.TF_ImportGraphDefOptionsSetDefaultDevice(opts, cdev) + // TODO(ashankar): Remove this error and uncomment below + // when a release of the C library which includes + // https://github.com/tensorflow/tensorflow/commit/e0af5ac53e5a8ad9b07cdd5738c0a8e12f938c4e + // has been made. + // See https://github.com/tensorflow/tensorflow/issues/23257 + return fmt.Errorf("GraphImportOptions.Device is only supported with the TensorFlow C library versions after 1.12 (or built from master). See https://github.com/tensorflow/tensorflow/issues/23257") + /* + cdev := C.CString(options.Device) + defer C.free(unsafe.Pointer(cdev)) + C.TF_ImportGraphDefOptionsSetDefaultDevice(opts, cdev) + */ } buf := C.TF_NewBuffer() @@ -174,6 +182,68 @@ func (g *Graph) Operations() []Operation { return ops } +// AddGradients adds operations to compute the partial derivatives of the sum of tensors in y +// with respect to tensors in x, i.e., d(y[0] + y[1] + ...) / d x[0], d(y[0] + y[1] + ... ) / d x[1] etc. +// +// prefix, if non-empty, is the name prefix used for all operations added to the graph to compute +// these gradients. +func (g *Graph) AddGradients(prefix string, y []Output, x []Output, dx []Output) ([]Output, error) { + var ( + cprefix *C.char + + cy = make([]C.TF_Output, len(y)) + cx = make([]C.TF_Output, len(x)) + cdx = make([]C.TF_Output, len(dx)) + cdy = make([]C.TF_Output, len(x)) + + pcy *C.TF_Output + pcx *C.TF_Output + pcdx *C.TF_Output + pcdy *C.TF_Output + + status = newStatus() + ) + + if len(y) > 0 { + pcy = &cy[0] + for i, o := range y { + cy[i] = o.c() + } + } + if len(x) > 0 { + pcx = &cx[0] + for i, o := range x { + cx[i] = o.c() + } + pcdy = &cdy[0] + } + if len(dx) > 0 { + pcdx = &cdx[0] + for i, o := range dx { + cdx[i] = o.c() + } + } + + // If prefix is "", the C.TF_AddGradientsWithPrefix need cprefix to be nil but not "" + if len(prefix) != 0 { + cprefix = C.CString(prefix) + defer C.free(unsafe.Pointer(cprefix)) + } + + C.TF_AddGradientsWithPrefix(g.c, cprefix, pcy, C.int(len(y)), pcx, C.int(len(x)), pcdx, status.c, pcdy) + + if err := status.Err(); err != nil { + return nil, err + } + dy := make([]Output, len(x)) + for i, co := range cdy { + op := &Operation{co.oper, g} + dy[i] = Output{op, int(co.index)} + } + + return dy, nil +} + // OpSpec is the specification of an Operation to be added to a Graph // (using Graph.AddOperation). type OpSpec struct { diff --git a/tensorflow/go/graph_test.go b/tensorflow/go/graph_test.go index b8d65c54f6..067c7db5c3 100644 --- a/tensorflow/go/graph_test.go +++ b/tensorflow/go/graph_test.go @@ -19,6 +19,7 @@ package tensorflow import ( "bytes" "fmt" + "strings" "testing" ) @@ -80,3 +81,260 @@ func TestGraphWriteToAndImport(t *testing.T) { t.Error(err) } } + +func TestGraphAddGradients(t *testing.T) { + g := NewGraph() + x1, err := Placeholder(g, "x1", Float) + if err != nil { + t.Fatal(err) + } + x2, err := Placeholder(g, "x2", Float) + if err != nil { + t.Fatal(err) + } + op0, err := g.AddOperation(OpSpec{ + Type: "Square", + Name: "y0", + Input: []Input{x1}, + }) + if err != nil { + t.Fatal(err) + } + y0 := op0.Output(0) + op1, err := g.AddOperation(OpSpec{ + Type: "Square", + Name: "y1", + Input: []Input{y0}, + }) + if err != nil { + t.Fatal(err) + } + y1 := op1.Output(0) + op2, err := g.AddOperation(OpSpec{ + Type: "AddN", + Input: []Input{OutputList([]Output{y0, x2})}, + }) + if err != nil { + t.Fatal(err) + } + y2 := op2.Output(0) + + grads0, err := g.AddGradients("", []Output{y1}, []Output{x1}, nil) + if err != nil { + t.Fatal(err) + } + if len(grads0) != 1 { + t.Fatal(len(grads0)) + } + if grads0[0].DataType() != Float { + t.Fatalf("Got DataType %v, wanted %v", grads0[0].DataType(), Float) + } + + grads1, err := g.AddGradients("", []Output{y2}, []Output{x1, x2}, nil) + if err != nil { + t.Fatal(err) + } + if len(grads1) != 2 { + t.Fatal(len(grads1)) + } + if grads1[0].DataType() != Float { + t.Fatalf("Got DataType %v, wanted %v", grads1[0].DataType(), Float) + } + if grads1[1].DataType() != Float { + t.Fatalf("Got DataType %v, wanted %v", grads1[1].DataType(), Float) + } + + sess, err := NewSession(g, nil) + if err != nil { + t.Fatal(err) + } + + c1, _ := NewTensor(float32(3.0)) + c2, _ := NewTensor(float32(2.0)) + outputs, err := sess.Run( + map[Output]*Tensor{x1: c1, x2: c2}, + []Output{grads0[0], grads1[0], grads1[1]}, + nil) + if err != nil { + t.Fatal(err) + } + if len(outputs) != 3 { + t.Fatal(len(outputs)) + } + if outputs[0].Value().(float32) != 108.0 { + t.Fatalf("Got %v, wanted float 108.0", outputs[0].Value()) + } + if outputs[1].Value().(float32) != 6.0 { + t.Fatalf("Got %v, wanted float 6.0", outputs[1].Value()) + } + if outputs[2].Value().(float32) != 1.0 { + t.Fatalf("Got %v, wanted float 1.0", outputs[2].Value()) + } +} + +func TestGraphAddGradientsSums(t *testing.T) { + g := NewGraph() + x, err := Placeholder(g, "x", Float) + if err != nil { + t.Fatal(err) + } + op0, err := g.AddOperation(OpSpec{ + Type: "Square", + Name: "y0", + Input: []Input{x}, + }) + if err != nil { + t.Fatal(err) + } + y0 := op0.Output(0) + op1, err := g.AddOperation(OpSpec{ + Type: "Square", + Name: "y1", + Input: []Input{y0}, + }) + y1 := op1.Output(0) + + grad, err := g.AddGradients("", []Output{y0, y1}, []Output{x}, nil) + if err != nil { + t.Fatal(err) + } + if len(grad) != 1 { + t.Fatal(len(grad)) + } + if grad[0].DataType() != Float { + t.Fatalf("Got DataType %v, wanted %v", grad[0].DataType(), Float) + } + + sess, err := NewSession(g, nil) + if err != nil { + t.Fatal(err) + } + + c, _ := NewTensor(float32(3.0)) + outputs, err := sess.Run( + map[Output]*Tensor{x: c}, + []Output{grad[0]}, + nil) + if err != nil { + t.Fatal(err) + } + if outputs[0].Value().(float32) != 114.0 { + t.Fatalf("Got %v, wanted float 114.0", outputs[0].Value()) + } +} + +func TestGraphAddGradientsWithInitialValues(t *testing.T) { + g := NewGraph() + x, err := Placeholder(g, "x", Float) + op0, err := g.AddOperation(OpSpec{ + Type: "Square", + Name: "y0", + Input: []Input{x}, + }) + if err != nil { + t.Fatal(err) + } + y0 := op0.Output(0) + op1, err := g.AddOperation(OpSpec{ + Type: "Square", + Name: "y1", + Input: []Input{y0}, + }) + if err != nil { + t.Fatal(err) + } + y1 := op1.Output(0) + + grads0, err := g.AddGradients("", []Output{y1}, []Output{y0}, nil) + if err != nil { + t.Fatal(err) + } + if len(grads0) != 1 { + t.Fatal(len(grads0)) + } + if grads0[0].DataType() != Float { + t.Fatalf("Got DataType %v, wanted %v", grads0[0].DataType(), Float) + } + + grads1, err := g.AddGradients("", []Output{y0}, []Output{x}, []Output{grads0[0]}) + if err != nil { + t.Fatal(err) + } + if len(grads1) != 1 { + t.Fatal(len(grads1)) + } + if grads1[0].DataType() != Float { + t.Fatalf("Got DataType %v, wanted %v", grads1[0].DataType(), Float) + } + + sess, err := NewSession(g, nil) + if err != nil { + t.Fatal(err) + } + + c, _ := NewTensor(float32(3.0)) + outputs, err := sess.Run( + map[Output]*Tensor{x: c}, + []Output{grads1[0]}, + nil) + if err != nil { + t.Fatal(err) + } + if outputs[0].Value().(float32) != 108.0 { + t.Fatalf("Got %v, wanted float 108.0", outputs[0].Value()) + } +} + +func TestGraphValidateGradientsNames(t *testing.T) { + g := NewGraph() + x, err := Placeholder(g, "x", Float) + if err != nil { + t.Fatal(err) + } + op0, err := g.AddOperation(OpSpec{ + Type: "Square", + Name: "y0", + Input: []Input{x}, + }) + if err != nil { + t.Fatal(err) + } + y0 := op0.Output(0) + + grads0, err := g.AddGradients("", []Output{y0}, []Output{x}, nil) + if err != nil { + t.Fatal(err) + } + if !strings.HasPrefix(grads0[0].Op.Name(), "gradients/") { + t.Fatalf("Got name %v, wanted started with gradients/", grads0[0].Op.Name()) + } + + grads1, err := g.AddGradients("", []Output{y0}, []Output{x}, nil) + if err != nil { + t.Fatal(err) + } + if !strings.HasPrefix(grads1[0].Op.Name(), "gradients_1/") { + t.Fatalf("Got name %v, wanted started with gradients_1/", grads1[0].Op.Name()) + } + + grads2, err := g.AddGradients("more_gradients", []Output{y0}, []Output{x}, nil) + if err != nil { + t.Fatal(err) + } + if !strings.HasPrefix(grads2[0].Op.Name(), "more_gradients/") { + t.Fatalf("Got name %v, wanted started with more_gradients/", grads2[0].Op.Name()) + } + + grads3, err := g.AddGradients("even_more_gradients", []Output{y0}, []Output{x}, nil) + if err != nil { + t.Fatal(err) + } + if !strings.HasPrefix(grads3[0].Op.Name(), "even_more_gradients/") { + t.Fatalf("Got name %v, wanted started with even_more_gradients/", grads3[0].Op.Name()) + } + + _, err = g.AddGradients("even_more_gradients", []Output{y0}, []Output{x}, nil) + if err == nil { + t.Error("AddGradients should have failed if gradients name is already existing") + } +} diff --git a/tensorflow/go/op/gradients.go b/tensorflow/go/op/gradients.go new file mode 100644 index 0000000000..9f892e1da6 --- /dev/null +++ b/tensorflow/go/op/gradients.go @@ -0,0 +1,50 @@ +/* +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. +*/ + +package op + +import ( + "fmt" + + tf "github.com/tensorflow/tensorflow/tensorflow/go" +) + + +// Gradients adds gradients computation ops to the graph according to scope. +// +// Arguments: +// y: output of the function to derive +// x: inputs of the function for which partial derivatives are computed +// dx: if not null, the partial derivatives of some loss function L w.r.t. y +// +// return the partial derivatives +func Gradients(scope *Scope, y []tf.Output, x []tf.Output, dx ...tf.Output) (output []tf.Output) { + if len(scope.controlDependencies) > 0 { + scope.UpdateErr("Gradients", fmt.Errorf("Gradients does not currently support control dependencies (via Scope.WithControlDependencies).")) + return + } + if scope.device != "" { + scope.UpdateErr("Gradients", fmt.Errorf("Gradients does not currently support device annotations (via Scope.WithDevice).")) + return + } + + var err error + if output, err = scope.graph.AddGradients(scope.opName("Gradients"), y, x, dx); err != nil { + scope.UpdateErr("Gradients", err) + return + } + return output +} diff --git a/tensorflow/go/op/gradients_test.go b/tensorflow/go/op/gradients_test.go new file mode 100644 index 0000000000..3d1d57b77e --- /dev/null +++ b/tensorflow/go/op/gradients_test.go @@ -0,0 +1,246 @@ +/* +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. +*/ + +package op + +import ( + "strings" + "testing" + + tf "github.com/tensorflow/tensorflow/tensorflow/go" +) + +func TestAddGradients(t *testing.T) { + var ( + s = NewScope() + x1 = Placeholder(s.SubScope("x1"), tf.Float) + x2 = Placeholder(s.SubScope("x2"), tf.Float) + y0 = Square(s.SubScope("y0"), x1) + y1 = Square(s.SubScope("y1"), y0) + y2 = AddN(s.SubScope("y2"), []tf.Output{y0, x2}) + ) + + grads0 := Gradients(s, []tf.Output{y1}, []tf.Output{x1}) + if err := s.Err(); err != nil { + t.Fatal(err) + } + if len(grads0) != 1 { + t.Fatal(len(grads0)) + } + if grads0[0].DataType() != tf.Float { + t.Fatalf("Got DataType %v, wanted %v", grads0[0].DataType(), tf.Float) + } + + sub := s.SubScope("sub") + grads1 := Gradients(sub, []tf.Output{y2}, []tf.Output{x1, x2}) + if err := sub.Err(); err != nil { + t.Fatal(err) + } + if len(grads1) != 2 { + t.Fatal(len(grads1)) + } + if grads1[0].DataType() != tf.Float { + t.Fatalf("Got DataType %v, wanted %v", grads1[0].DataType(), tf.Float) + } + if grads1[1].DataType() != tf.Float { + t.Fatalf("Got DataType %v, wanted %v", grads1[1].DataType(), tf.Float) + } + + graph, err := sub.Finalize() + if err != nil { + t.Fatal(err) + } + sess, err := tf.NewSession(graph, nil) + if err != nil { + t.Fatal(err) + } + + c1, _ := tf.NewTensor(float32(3.0)) + c2, _ := tf.NewTensor(float32(3.0)) + outputs, err := sess.Run( + map[tf.Output]*tf.Tensor{x1: c1, x2: c2}, + []tf.Output{grads0[0], grads1[0], grads1[1]}, + nil) + if err != nil { + t.Fatal(err) + } + if len(outputs) != 3 { + t.Fatal(len(outputs)) + } + if outputs[0].Value().(float32) != 108.0 { + t.Fatalf("Got %v, wanted float 108.0", outputs[0].Value()) + } + if outputs[1].Value().(float32) != 6.0 { + t.Fatalf("Got %v, wanted float 6.0", outputs[1].Value()) + } + if outputs[2].Value().(float32) != 1.0 { + t.Fatalf("Got %v, wanted float 1.0", outputs[2].Value()) + } +} + +func TestAddGradientsSums(t *testing.T) { + var ( + s = NewScope() + x = Placeholder(s.SubScope("x"), tf.Float) + y0 = Square(s.SubScope("y0"), x) + y1 = Square(s.SubScope("y1"), y0) + ) + + grad := Gradients(s, []tf.Output{y0, y1}, []tf.Output{x}) + if err := s.Err(); err != nil { + t.Fatal(err) + } + if len(grad) != 1 { + t.Fatal(len(grad)) + } + if grad[0].DataType() != tf.Float { + t.Fatalf("Got DataType %v, wanted %v", grad[0].DataType(), tf.Float) + } + + graph, err := s.Finalize() + if err != nil { + t.Fatal(err) + } + sess, err := tf.NewSession(graph, nil) + if err != nil { + t.Fatal(err) + } + + c, _ := tf.NewTensor(float32(3.0)) + outputs, err := sess.Run( + map[tf.Output]*tf.Tensor{x: c}, + []tf.Output{grad[0]}, + nil) + if err != nil { + t.Fatal(err) + } + if outputs[0].Value().(float32) != 114.0 { + t.Fatalf("Got %v, wanted float 114.0", outputs[0].Value()) + } +} + +func TestAddGradientsWithInitialValues(t *testing.T) { + var ( + s = NewScope() + x = Placeholder(s.SubScope("x1"), tf.Float) + y0 = Square(s.SubScope("y0"), x) + y1 = Square(s.SubScope("y1"), y0) + ) + + grads0 := Gradients(s, []tf.Output{y1}, []tf.Output{y0}) + if err := s.Err(); err != nil { + t.Fatal(err) + } + if len(grads0) != 1 { + t.Fatal(len(grads0)) + } + if grads0[0].DataType() != tf.Float { + t.Fatalf("Got DataType %v, wanted %v", grads0[0].DataType(), tf.Float) + } + + sub := s.SubScope("sub") + grads1 := Gradients(sub, []tf.Output{y0}, []tf.Output{x}, grads0[0]) + if err := sub.Err(); err != nil { + t.Fatal(err) + } + if len(grads1) != 1 { + t.Fatal(len(grads1)) + } + if grads1[0].DataType() != tf.Float { + t.Fatalf("Got DataType %v, wanted %v", grads1[0].DataType(), tf.Float) + } + + graph, err := sub.Finalize() + if err != nil { + t.Fatal(err) + } + sess, err := tf.NewSession(graph, nil) + if err != nil { + t.Fatal(err) + } + + c, _ := tf.NewTensor(float32(3.0)) + outputs, err := sess.Run( + map[tf.Output]*tf.Tensor{x: c}, + []tf.Output{grads1[0]}, + nil) + if err != nil { + t.Fatal(err) + } + if outputs[0].Value().(float32) != 108.0 { + t.Fatalf("Got %v, wanted float 108.0", outputs[0].Value()) + } +} + +func TestValidateGradientsNames(t *testing.T) { + var ( + s = NewScope() + x = Placeholder(s.SubScope("x"), tf.Float) + y0 = Square(s.SubScope("y0"), x) + ) + + grads0 := Gradients(s, []tf.Output{y0}, []tf.Output{x}) + if err := s.Err(); err != nil { + t.Fatal(err) + } + if !strings.HasPrefix(grads0[0].Op.Name(), "Gradients/") { + t.Fatalf("Got name %v, wanted started with Gradients/", grads0[0].Op.Name()) + } + + sub := s.SubScope("sub") + grads1 := Gradients(sub, []tf.Output{y0}, []tf.Output{x}) + if err := s.Err(); err != nil { + t.Fatal(err) + } + if !strings.HasPrefix(grads1[0].Op.Name(), "sub/Gradients/") { + t.Fatalf("Got name %v, wanted started with sub/Gradients/", grads1[0].Op.Name()) + } + + Gradients(sub, []tf.Output{y0}, []tf.Output{x}) + if err := s.Err(); err == nil { + t.Error("Gradients should have failed if executed more than once for scope of the same namespace") + } +} + +func TestAddGradientsWithControlDependencies(t *testing.T) { + var ( + s = NewScope() + zero = Const(s.SubScope("zero"), int32(0)) + x = Placeholder(s.SubScope("x"), tf.Float) + y0 = Square(s.SubScope("y0"), x) + variable = VarHandleOp(s, tf.Int32, tf.ScalarShape()) + init = AssignVariableOp(s, variable, zero) + readDeps = []*tf.Operation{init} + ) + s = s.WithControlDependencies(readDeps...) + Gradients(s, []tf.Output{y0}, []tf.Output{x}) + if err := s.Err(); err == nil { + t.Error("Gradients should have failed when control dependencies are set") + } +} + +func TestAddGradientsWithDevice(t *testing.T) { + var ( + s = NewScope() + x = Placeholder(s.SubScope("x"), tf.Float) + y0 = Square(s.SubScope("y0"), x) + ) + s = s.WithDevice("/device:GPU:0") + Gradients(s, []tf.Output{y0}, []tf.Output{x}) + if err := s.Err(); err == nil { + t.Error("Gradients should have failed when device is set") + } +} diff --git a/tensorflow/go/op/wrappers.go b/tensorflow/go/op/wrappers.go index baf43f84f8..5cd06299b9 100644 --- a/tensorflow/go/op/wrappers.go +++ b/tensorflow/go/op/wrappers.go @@ -463,6 +463,14 @@ func QuantizeAndDequantizeV2RangeGiven(value bool) QuantizeAndDequantizeV2Attr { } } +// QuantizeAndDequantizeV2RoundMode sets the optional round_mode attribute to value. +// If not specified, defaults to "HALF_TO_EVEN" +func QuantizeAndDequantizeV2RoundMode(value string) QuantizeAndDequantizeV2Attr { + return func(m optionalAttr) { + m["round_mode"] = value + } +} + // Quantizes then dequantizes a tensor. // // This op simulates the precision loss from the quantized forward pass by: @@ -3487,30 +3495,6 @@ func BoostedTreesQuantileStreamResourceFlush(scope *Scope, quantile_stream_resou return scope.AddOperation(opspec) } -// Add the quantile summaries to each quantile stream resource. -// -// An op that adds a list of quantile summaries to a quantile stream resource. Each -// summary Tensor is rank 2, containing summaries (value, weight, min_rank, max_rank) -// for a single feature. -// -// Arguments: -// quantile_stream_resource_handle: resource handle referring to a QuantileStreamResource. -// summaries: string; List of Rank 2 Tensor each containing the summaries for a single feature. -// -// Returns the created operation. -func BoostedTreesQuantileStreamResourceAddSummaries(scope *Scope, quantile_stream_resource_handle tf.Output, summaries []tf.Output) (o *tf.Operation) { - if scope.Err() != nil { - return - } - opspec := tf.OpSpec{ - Type: "BoostedTreesQuantileStreamResourceAddSummaries", - Input: []tf.Input{ - quantile_stream_resource_handle, tf.OutputList(summaries), - }, - } - return scope.AddOperation(opspec) -} - // Makes the summary of quantiles for the batch. // // An op that takes a list of tensors and outputs the quantile summaries for each tensor. @@ -5547,6 +5531,63 @@ func OrderedMapPeek(scope *Scope, key tf.Output, indices tf.Output, dtypes []tf. return values } +// MapIncompleteSizeAttr is an optional argument to MapIncompleteSize. +type MapIncompleteSizeAttr func(optionalAttr) + +// MapIncompleteSizeCapacity sets the optional capacity attribute to value. +// If not specified, defaults to 0 +// +// REQUIRES: value >= 0 +func MapIncompleteSizeCapacity(value int64) MapIncompleteSizeAttr { + return func(m optionalAttr) { + m["capacity"] = value + } +} + +// MapIncompleteSizeMemoryLimit sets the optional memory_limit attribute to value. +// If not specified, defaults to 0 +// +// REQUIRES: value >= 0 +func MapIncompleteSizeMemoryLimit(value int64) MapIncompleteSizeAttr { + return func(m optionalAttr) { + m["memory_limit"] = value + } +} + +// MapIncompleteSizeContainer sets the optional container attribute to value. +// If not specified, defaults to "" +func MapIncompleteSizeContainer(value string) MapIncompleteSizeAttr { + return func(m optionalAttr) { + m["container"] = value + } +} + +// MapIncompleteSizeSharedName sets the optional shared_name attribute to value. +// If not specified, defaults to "" +func MapIncompleteSizeSharedName(value string) MapIncompleteSizeAttr { + return func(m optionalAttr) { + m["shared_name"] = value + } +} + +// Op returns the number of incomplete elements in the underlying container. +func MapIncompleteSize(scope *Scope, dtypes []tf.DataType, optional ...MapIncompleteSizeAttr) (size tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{"dtypes": dtypes} + for _, a := range optional { + a(attrs) + } + opspec := tf.OpSpec{ + Type: "MapIncompleteSize", + + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // Compute the regularized incomplete beta integral \\(I_x(a, b)\\). // // The regularized incomplete beta integral is defined as: @@ -5612,34 +5653,6 @@ func Atan2(scope *Scope, y tf.Output, x tf.Output) (z tf.Output) { return op.Output(0) } -// Creates a dataset that passes a sliding window over `input_dataset`. -// -// Arguments: -// -// window_size: A scalar representing the number of elements in the -// sliding window. -// window_shift: A scalar representing the steps moving the sliding window -// forward in one iteration. It must be positive. -// window_stride: A scalar representing the stride of the input elements of the sliding window. -// It must be positive. -// -// -func SlideDataset(scope *Scope, input_dataset tf.Output, window_size tf.Output, window_shift tf.Output, window_stride tf.Output, output_types []tf.DataType, output_shapes []tf.Shape) (handle tf.Output) { - if scope.Err() != nil { - return - } - attrs := map[string]interface{}{"output_types": output_types, "output_shapes": output_shapes} - opspec := tf.OpSpec{ - Type: "SlideDataset", - Input: []tf.Input{ - input_dataset, window_size, window_shift, window_stride, - }, - Attrs: attrs, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - // EditDistanceAttr is an optional argument to EditDistance. type EditDistanceAttr func(optionalAttr) @@ -6279,6 +6292,71 @@ func Asin(scope *Scope, x tf.Output) (y tf.Output) { return op.Output(0) } +// SparseToDenseAttr is an optional argument to SparseToDense. +type SparseToDenseAttr func(optionalAttr) + +// SparseToDenseValidateIndices sets the optional validate_indices attribute to value. +// +// value: If true, indices are checked to make sure they are sorted in +// lexicographic order and that there are no repeats. +// If not specified, defaults to true +func SparseToDenseValidateIndices(value bool) SparseToDenseAttr { + return func(m optionalAttr) { + m["validate_indices"] = value + } +} + +// Converts a sparse representation into a dense tensor. +// +// Builds an array `dense` with shape `output_shape` such that +// +// ``` +// # If sparse_indices is scalar +// dense[i] = (i == sparse_indices ? sparse_values : default_value) +// +// # If sparse_indices is a vector, then for each i +// dense[sparse_indices[i]] = sparse_values[i] +// +// # If sparse_indices is an n by d matrix, then for each i in [0, n) +// dense[sparse_indices[i][0], ..., sparse_indices[i][d-1]] = sparse_values[i] +// ``` +// +// All other values in `dense` are set to `default_value`. If `sparse_values` is a +// scalar, all sparse indices are set to this single value. +// +// Indices should be sorted in lexicographic order, and indices must not +// contain any repeats. If `validate_indices` is true, these properties +// are checked during execution. +// +// Arguments: +// sparse_indices: 0-D, 1-D, or 2-D. `sparse_indices[i]` contains the complete +// index where `sparse_values[i]` will be placed. +// output_shape: 1-D. Shape of the dense output tensor. +// sparse_values: 1-D. Values corresponding to each row of `sparse_indices`, +// or a scalar value to be used for all sparse indices. +// default_value: Scalar value to set for indices not specified in +// `sparse_indices`. +// +// Returns Dense output tensor of shape `output_shape`. +func SparseToDense(scope *Scope, sparse_indices tf.Output, output_shape tf.Output, sparse_values tf.Output, default_value tf.Output, optional ...SparseToDenseAttr) (dense tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{} + for _, a := range optional { + a(attrs) + } + opspec := tf.OpSpec{ + Type: "SparseToDense", + Input: []tf.Input{ + sparse_indices, output_shape, sparse_values, default_value, + }, + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // Computes the sum along sparse segments of a tensor. // // Like `SparseSegmentSum`, but allows missing ids in `segment_ids`. If an id is @@ -6370,21 +6448,6 @@ func Sin(scope *Scope, x tf.Output) (y tf.Output) { return op.Output(0) } -// Computes the complementary error function of `x` element-wise. -func Erfc(scope *Scope, x tf.Output) (y tf.Output) { - if scope.Err() != nil { - return - } - opspec := tf.OpSpec{ - Type: "Erfc", - Input: []tf.Input{ - x, - }, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - // Computes Psi, the derivative of Lgamma (the log of the absolute value of // // `Gamma(x)`), element-wise. @@ -6829,61 +6892,6 @@ func Reciprocal(scope *Scope, x tf.Output) (y tf.Output) { return op.Output(0) } -// ParseExampleDatasetAttr is an optional argument to ParseExampleDataset. -type ParseExampleDatasetAttr func(optionalAttr) - -// ParseExampleDatasetSloppy sets the optional sloppy attribute to value. -// If not specified, defaults to false -func ParseExampleDatasetSloppy(value bool) ParseExampleDatasetAttr { - return func(m optionalAttr) { - m["sloppy"] = value - } -} - -// Transforms `input_dataset` containing `Example` protos as vectors of DT_STRING into a dataset of `Tensor` or `SparseTensor` objects representing the parsed features. -// -// Arguments: -// -// -// dense_defaults: A dict mapping string keys to `Tensor`s. -// The keys of the dict must match the dense_keys of the feature. -// sparse_keys: A list of string keys in the examples features. -// The results for these keys will be returned as `SparseTensor` objects. -// dense_keys: A list of Ndense string Tensors (scalars). -// The keys expected in the Examples features associated with dense values. -// sparse_types: A list of `DTypes` of the same length as `sparse_keys`. -// Only `tf.float32` (`FloatList`), `tf.int64` (`Int64List`), -// and `tf.string` (`BytesList`) are supported. -// dense_shapes: List of tuples with the same length as `dense_keys`. -// The shape of the data for each dense feature referenced by `dense_keys`. -// Required for any input tensors identified by `dense_keys`. Must be -// either fully defined, or may contain an unknown first dimension. -// An unknown first dimension means the feature is treated as having -// a variable number of blocks, and the output shape along this dimension -// is considered unknown at graph build time. Padding is applied for -// minibatch elements smaller than the maximum number of blocks for the -// given feature along this dimension. -// output_types: The type list for the return values. -// output_shapes: The list of shapes being produced. -func ParseExampleDataset(scope *Scope, input_dataset tf.Output, num_parallel_calls tf.Output, dense_defaults []tf.Output, sparse_keys []string, dense_keys []string, sparse_types []tf.DataType, dense_shapes []tf.Shape, output_types []tf.DataType, output_shapes []tf.Shape, optional ...ParseExampleDatasetAttr) (handle tf.Output) { - if scope.Err() != nil { - return - } - attrs := map[string]interface{}{"sparse_keys": sparse_keys, "dense_keys": dense_keys, "sparse_types": sparse_types, "dense_shapes": dense_shapes, "output_types": output_types, "output_shapes": output_shapes} - for _, a := range optional { - a(attrs) - } - opspec := tf.OpSpec{ - Type: "ParseExampleDataset", - Input: []tf.Input{ - input_dataset, num_parallel_calls, tf.OutputList(dense_defaults), - }, - Attrs: attrs, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - // Returns a batched matrix tensor with new batched diagonal values. // // Given `input` and `diagonal`, this operation returns a tensor with the @@ -7956,28 +7964,6 @@ func LeakyRelu(scope *Scope, features tf.Output, optional ...LeakyReluAttr) (act return op.Output(0) } -// Writes the given dataset to the given file using the TFRecord format. -// -// Arguments: -// input_dataset: A variant tensor representing the dataset to write. -// filename: A scalar string tensor representing the filename to use. -// compression_type: A scalar string tensor containing either (i) the empty string (no -// compression), (ii) "ZLIB", or (iii) "GZIP". -// -// Returns the created operation. -func DatasetToTFRecord(scope *Scope, input_dataset tf.Output, filename tf.Output, compression_type tf.Output) (o *tf.Operation) { - if scope.Err() != nil { - return - } - opspec := tf.OpSpec{ - Type: "DatasetToTFRecord", - Input: []tf.Input{ - input_dataset, filename, compression_type, - }, - } - return scope.AddOperation(opspec) -} - // Computes rectified linear 6: `min(max(features, 0), 6)`. func Relu6(scope *Scope, features tf.Output) (activations tf.Output) { if scope.Err() != nil { @@ -9441,6 +9427,178 @@ func BesselI1e(scope *Scope, x tf.Output) (y tf.Output) { return op.Output(0) } +// MapClearAttr is an optional argument to MapClear. +type MapClearAttr func(optionalAttr) + +// MapClearCapacity sets the optional capacity attribute to value. +// If not specified, defaults to 0 +// +// REQUIRES: value >= 0 +func MapClearCapacity(value int64) MapClearAttr { + return func(m optionalAttr) { + m["capacity"] = value + } +} + +// MapClearMemoryLimit sets the optional memory_limit attribute to value. +// If not specified, defaults to 0 +// +// REQUIRES: value >= 0 +func MapClearMemoryLimit(value int64) MapClearAttr { + return func(m optionalAttr) { + m["memory_limit"] = value + } +} + +// MapClearContainer sets the optional container attribute to value. +// If not specified, defaults to "" +func MapClearContainer(value string) MapClearAttr { + return func(m optionalAttr) { + m["container"] = value + } +} + +// MapClearSharedName sets the optional shared_name attribute to value. +// If not specified, defaults to "" +func MapClearSharedName(value string) MapClearAttr { + return func(m optionalAttr) { + m["shared_name"] = value + } +} + +// Op removes all elements in the underlying container. +// +// Returns the created operation. +func MapClear(scope *Scope, dtypes []tf.DataType, optional ...MapClearAttr) (o *tf.Operation) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{"dtypes": dtypes} + for _, a := range optional { + a(attrs) + } + opspec := tf.OpSpec{ + Type: "MapClear", + + Attrs: attrs, + } + return scope.AddOperation(opspec) +} + +// DecodeCSVAttr is an optional argument to DecodeCSV. +type DecodeCSVAttr func(optionalAttr) + +// DecodeCSVFieldDelim sets the optional field_delim attribute to value. +// +// value: char delimiter to separate fields in a record. +// If not specified, defaults to "," +func DecodeCSVFieldDelim(value string) DecodeCSVAttr { + return func(m optionalAttr) { + m["field_delim"] = value + } +} + +// DecodeCSVUseQuoteDelim sets the optional use_quote_delim attribute to value. +// +// value: If false, treats double quotation marks as regular +// characters inside of the string fields (ignoring RFC 4180, Section 2, +// Bullet 5). +// If not specified, defaults to true +func DecodeCSVUseQuoteDelim(value bool) DecodeCSVAttr { + return func(m optionalAttr) { + m["use_quote_delim"] = value + } +} + +// DecodeCSVNaValue sets the optional na_value attribute to value. +// +// value: Additional string to recognize as NA/NaN. +// If not specified, defaults to "" +func DecodeCSVNaValue(value string) DecodeCSVAttr { + return func(m optionalAttr) { + m["na_value"] = value + } +} + +// DecodeCSVSelectCols sets the optional select_cols attribute to value. +// If not specified, defaults to <> +func DecodeCSVSelectCols(value []int64) DecodeCSVAttr { + return func(m optionalAttr) { + m["select_cols"] = value + } +} + +// Convert CSV records to tensors. Each column maps to one tensor. +// +// RFC 4180 format is expected for the CSV records. +// (https://tools.ietf.org/html/rfc4180) +// Note that we allow leading and trailing spaces with int or float field. +// +// Arguments: +// records: Each string is a record/row in the csv and all records should have +// the same format. +// record_defaults: One tensor per column of the input record, with either a +// scalar default value for that column or an empty vector if the column is +// required. +// +// Returns Each tensor will have the same shape as records. +func DecodeCSV(scope *Scope, records tf.Output, record_defaults []tf.Output, optional ...DecodeCSVAttr) (output []tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{} + for _, a := range optional { + a(attrs) + } + opspec := tf.OpSpec{ + Type: "DecodeCSV", + Input: []tf.Input{ + records, tf.OutputList(record_defaults), + }, + Attrs: attrs, + } + op := scope.AddOperation(opspec) + if scope.Err() != nil { + return + } + var idx int + var err error + if output, idx, err = makeOutputList(op, idx, "output"); err != nil { + scope.UpdateErr("DecodeCSV", err) + return + } + return output +} + +// Convert JSON-encoded Example records to binary protocol buffer strings. +// +// This op translates a tensor containing Example records, encoded using +// the [standard JSON +// mapping](https://developers.google.com/protocol-buffers/docs/proto3#json), +// into a tensor containing the same records encoded as binary protocol +// buffers. The resulting tensor can then be fed to any of the other +// Example-parsing ops. +// +// Arguments: +// json_examples: Each string is a JSON object serialized according to the JSON +// mapping of the Example proto. +// +// Returns Each string is a binary Example protocol buffer corresponding +// to the respective element of `json_examples`. +func DecodeJSONExample(scope *Scope, json_examples tf.Output) (binary_examples tf.Output) { + if scope.Err() != nil { + return + } + opspec := tf.OpSpec{ + Type: "DecodeJSONExample", + Input: []tf.Input{ + json_examples, + }, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // Transforms a Tensor into a serialized TensorProto proto. // // Arguments: @@ -10499,6 +10657,31 @@ func ParseExample(scope *Scope, serialized tf.Output, names tf.Output, sparse_ke return sparse_indices, sparse_values, sparse_shapes, dense_values } +// Compute the pairwise cross product. +// +// `a` and `b` must be the same shape; they can either be simple 3-element vectors, +// or any shape where the innermost dimension is 3. In the latter case, each pair +// of corresponding 3-element vectors is cross-multiplied independently. +// +// Arguments: +// a: A tensor containing 3-element vectors. +// b: Another tensor, of same type and shape as `a`. +// +// Returns Pairwise cross product of the vectors in `a` and `b`. +func Cross(scope *Scope, a tf.Output, b tf.Output) (product tf.Output) { + if scope.Err() != nil { + return + } + opspec := tf.OpSpec{ + Type: "Cross", + Input: []tf.Input{ + a, b, + }, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // CudnnRNNAttr is an optional argument to CudnnRNN. type CudnnRNNAttr func(optionalAttr) @@ -11417,66 +11600,6 @@ func RandomStandardNormal(scope *Scope, shape tf.Output, dtype tf.DataType, opti return op.Output(0) } -// RandomUniformIntAttr is an optional argument to RandomUniformInt. -type RandomUniformIntAttr func(optionalAttr) - -// RandomUniformIntSeed sets the optional seed attribute to value. -// -// value: If either `seed` or `seed2` are set to be non-zero, the random number -// generator is seeded by the given seed. Otherwise, it is seeded by a -// random seed. -// If not specified, defaults to 0 -func RandomUniformIntSeed(value int64) RandomUniformIntAttr { - return func(m optionalAttr) { - m["seed"] = value - } -} - -// RandomUniformIntSeed2 sets the optional seed2 attribute to value. -// -// value: A second seed to avoid seed collision. -// If not specified, defaults to 0 -func RandomUniformIntSeed2(value int64) RandomUniformIntAttr { - return func(m optionalAttr) { - m["seed2"] = value - } -} - -// Outputs random integers from a uniform distribution. -// -// The generated values are uniform integers in the range `[minval, maxval)`. -// The lower bound `minval` is included in the range, while the upper bound -// `maxval` is excluded. -// -// The random integers are slightly biased unless `maxval - minval` is an exact -// power of two. The bias is small for values of `maxval - minval` significantly -// smaller than the range of the output (either `2^32` or `2^64`). -// -// Arguments: -// shape: The shape of the output tensor. -// minval: 0-D. Inclusive lower bound on the generated integers. -// maxval: 0-D. Exclusive upper bound on the generated integers. -// -// Returns A tensor of the specified shape filled with uniform random integers. -func RandomUniformInt(scope *Scope, shape tf.Output, minval tf.Output, maxval tf.Output, optional ...RandomUniformIntAttr) (output tf.Output) { - if scope.Err() != nil { - return - } - attrs := map[string]interface{}{} - for _, a := range optional { - a(attrs) - } - opspec := tf.OpSpec{ - Type: "RandomUniformInt", - Input: []tf.Input{ - shape, minval, maxval, - }, - Attrs: attrs, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - // FusedResizeAndPadConv2DAttr is an optional argument to FusedResizeAndPadConv2D. type FusedResizeAndPadConv2DAttr func(optionalAttr) @@ -13530,6 +13653,39 @@ func StatelessRandomNormal(scope *Scope, shape tf.Output, seed tf.Output, option return op.Output(0) } +// Computes the complementary error function of `x` element-wise. +func Erfc(scope *Scope, x tf.Output) (y tf.Output) { + if scope.Err() != nil { + return + } + opspec := tf.OpSpec{ + Type: "Erfc", + Input: []tf.Input{ + x, + }, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + +// Returns the number of tensors in the input tensor list. +// +// input_handle: the input list +// length: the number of tensors in the list +func TensorListLength(scope *Scope, input_handle tf.Output) (length tf.Output) { + if scope.Err() != nil { + return + } + opspec := tf.OpSpec{ + Type: "TensorListLength", + Input: []tf.Input{ + input_handle, + }, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // Determine the script codes of a given tensor of Unicode integer code points. // // This operation converts Unicode code points to script codes corresponding to @@ -13747,6 +13903,126 @@ func ResourceApplyMomentum(scope *Scope, var_ tf.Output, accum tf.Output, lr tf. return scope.AddOperation(opspec) } +// SubstrAttr is an optional argument to Substr. +type SubstrAttr func(optionalAttr) + +// SubstrUnit sets the optional unit attribute to value. +// +// value: The unit that is used to create the substring. One of: `"BYTE"` (for +// defining position and length by bytes) or `"UTF8_CHAR"` (for the UTF-8 +// encoded Unicode code points). The default is `"BYTE"`. Results are undefined if +// `unit=UTF8_CHAR` and the `input` strings do not contain structurally valid +// UTF-8. +// If not specified, defaults to "BYTE" +func SubstrUnit(value string) SubstrAttr { + return func(m optionalAttr) { + m["unit"] = value + } +} + +// Return substrings from `Tensor` of strings. +// +// For each string in the input `Tensor`, creates a substring starting at index +// `pos` with a total length of `len`. +// +// If `len` defines a substring that would extend beyond the length of the input +// string, then as many characters as possible are used. +// +// A negative `pos` indicates distance within the string backwards from the end. +// +// If `pos` specifies an index which is out of range for any of the input strings, +// then an `InvalidArgumentError` is thrown. +// +// `pos` and `len` must have the same shape, otherwise a `ValueError` is thrown on +// Op creation. +// +// *NOTE*: `Substr` supports broadcasting up to two dimensions. More about +// broadcasting +// [here](http://docs.scipy.org/doc/numpy/user/basics.broadcasting.html) +// +// --- +// +// Examples +// +// Using scalar `pos` and `len`: +// +// ```python +// input = [b'Hello', b'World'] +// position = 1 +// length = 3 +// +// output = [b'ell', b'orl'] +// ``` +// +// Using `pos` and `len` with same shape as `input`: +// +// ```python +// input = [[b'ten', b'eleven', b'twelve'], +// [b'thirteen', b'fourteen', b'fifteen'], +// [b'sixteen', b'seventeen', b'eighteen']] +// position = [[1, 2, 3], +// [1, 2, 3], +// [1, 2, 3]] +// length = [[2, 3, 4], +// [4, 3, 2], +// [5, 5, 5]] +// +// output = [[b'en', b'eve', b'lve'], +// [b'hirt', b'urt', b'te'], +// [b'ixtee', b'vente', b'hteen']] +// ``` +// +// Broadcasting `pos` and `len` onto `input`: +// +// ``` +// input = [[b'ten', b'eleven', b'twelve'], +// [b'thirteen', b'fourteen', b'fifteen'], +// [b'sixteen', b'seventeen', b'eighteen'], +// [b'nineteen', b'twenty', b'twentyone']] +// position = [1, 2, 3] +// length = [1, 2, 3] +// +// output = [[b'e', b'ev', b'lve'], +// [b'h', b'ur', b'tee'], +// [b'i', b've', b'hte'], +// [b'i', b'en', b'nty']] +// ``` +// +// Broadcasting `input` onto `pos` and `len`: +// +// ``` +// input = b'thirteen' +// position = [1, 5, 7] +// length = [3, 2, 1] +// +// output = [b'hir', b'ee', b'n'] +// ``` +// +// Arguments: +// input: Tensor of strings +// pos: Scalar defining the position of first character in each substring +// len: Scalar defining the number of characters to include in each substring +// +// Returns Tensor of substrings +func Substr(scope *Scope, input tf.Output, pos tf.Output, len tf.Output, optional ...SubstrAttr) (output tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{} + for _, a := range optional { + a(attrs) + } + opspec := tf.OpSpec{ + Type: "Substr", + Input: []tf.Input{ + input, pos, len, + }, + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // Exits the current frame to its parent frame. // // Exit makes its input `data` available to the parent frame. @@ -14946,75 +15222,6 @@ func ResourceApplyAdagrad(scope *Scope, var_ tf.Output, accum tf.Output, lr tf.O return scope.AddOperation(opspec) } -// Return the shape of s0 op s1 with broadcast. -// -// Given `s0` and `s1`, tensors that represent shapes, compute `r0`, the -// broadcasted shape. `s0`, `s1` and `r0` are all integer vectors. -func BroadcastArgs(scope *Scope, s0 tf.Output, s1 tf.Output) (r0 tf.Output) { - if scope.Err() != nil { - return - } - opspec := tf.OpSpec{ - Type: "BroadcastArgs", - Input: []tf.Input{ - s0, s1, - }, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - -// DataFormatDimMapAttr is an optional argument to DataFormatDimMap. -type DataFormatDimMapAttr func(optionalAttr) - -// DataFormatDimMapSrcFormat sets the optional src_format attribute to value. -// -// value: source data format. -// If not specified, defaults to "NHWC" -func DataFormatDimMapSrcFormat(value string) DataFormatDimMapAttr { - return func(m optionalAttr) { - m["src_format"] = value - } -} - -// DataFormatDimMapDstFormat sets the optional dst_format attribute to value. -// -// value: destination data format. -// If not specified, defaults to "NCHW" -func DataFormatDimMapDstFormat(value string) DataFormatDimMapAttr { - return func(m optionalAttr) { - m["dst_format"] = value - } -} - -// Returns the dimension index in the destination data format given the one in -// -// the source data format. -// -// Arguments: -// x: A Tensor with each element as a dimension index in source data format. -// Must be in the range [-4, 4). -// -// Returns A Tensor with each element as a dimension index in destination data format. -func DataFormatDimMap(scope *Scope, x tf.Output, optional ...DataFormatDimMapAttr) (y tf.Output) { - if scope.Err() != nil { - return - } - attrs := map[string]interface{}{} - for _, a := range optional { - a(attrs) - } - opspec := tf.OpSpec{ - Type: "DataFormatDimMap", - Input: []tf.Input{ - x, - }, - Attrs: attrs, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - // Retrieves the tree ensemble resource stamp token, number of trees and growing statistics. // // Arguments: @@ -16696,6 +16903,124 @@ func SparseReduceSum(scope *Scope, input_indices tf.Output, input_values tf.Outp return op.Output(0) } +// SparseTensorDenseMatMulAttr is an optional argument to SparseTensorDenseMatMul. +type SparseTensorDenseMatMulAttr func(optionalAttr) + +// SparseTensorDenseMatMulAdjointA sets the optional adjoint_a attribute to value. +// +// value: Use the adjoint of A in the matrix multiply. If A is complex, this +// is transpose(conj(A)). Otherwise it's transpose(A). +// If not specified, defaults to false +func SparseTensorDenseMatMulAdjointA(value bool) SparseTensorDenseMatMulAttr { + return func(m optionalAttr) { + m["adjoint_a"] = value + } +} + +// SparseTensorDenseMatMulAdjointB sets the optional adjoint_b attribute to value. +// +// value: Use the adjoint of B in the matrix multiply. If B is complex, this +// is transpose(conj(B)). Otherwise it's transpose(B). +// If not specified, defaults to false +func SparseTensorDenseMatMulAdjointB(value bool) SparseTensorDenseMatMulAttr { + return func(m optionalAttr) { + m["adjoint_b"] = value + } +} + +// Multiply SparseTensor (of rank 2) "A" by dense matrix "B". +// +// No validity checking is performed on the indices of A. However, the following +// input format is recommended for optimal behavior: +// +// if adjoint_a == false: +// A should be sorted in lexicographically increasing order. Use SparseReorder +// if you're not sure. +// if adjoint_a == true: +// A should be sorted in order of increasing dimension 1 (i.e., "column major" +// order instead of "row major" order). +// +// Arguments: +// a_indices: 2-D. The `indices` of the `SparseTensor`, size `[nnz, 2]` Matrix. +// a_values: 1-D. The `values` of the `SparseTensor`, size `[nnz]` Vector. +// a_shape: 1-D. The `shape` of the `SparseTensor`, size `[2]` Vector. +// b: 2-D. A dense Matrix. +func SparseTensorDenseMatMul(scope *Scope, a_indices tf.Output, a_values tf.Output, a_shape tf.Output, b tf.Output, optional ...SparseTensorDenseMatMulAttr) (product tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{} + for _, a := range optional { + a(attrs) + } + opspec := tf.OpSpec{ + Type: "SparseTensorDenseMatMul", + Input: []tf.Input{ + a_indices, a_values, a_shape, b, + }, + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + +// ResourceApplyRMSPropAttr is an optional argument to ResourceApplyRMSProp. +type ResourceApplyRMSPropAttr func(optionalAttr) + +// ResourceApplyRMSPropUseLocking sets the optional use_locking attribute to value. +// +// value: If `True`, updating of the var, ms, and mom tensors is protected +// by a lock; otherwise the behavior is undefined, but may exhibit less +// contention. +// If not specified, defaults to false +func ResourceApplyRMSPropUseLocking(value bool) ResourceApplyRMSPropAttr { + return func(m optionalAttr) { + m["use_locking"] = value + } +} + +// Update '*var' according to the RMSProp algorithm. +// +// Note that in dense implementation of this algorithm, ms and mom will +// update even if the grad is zero, but in this sparse implementation, ms +// and mom will not update in iterations during which the grad is zero. +// +// mean_square = decay * mean_square + (1-decay) * gradient ** 2 +// Delta = learning_rate * gradient / sqrt(mean_square + epsilon) +// +// ms <- rho * ms_{t-1} + (1-rho) * grad * grad +// mom <- momentum * mom_{t-1} + lr * grad / sqrt(ms + epsilon) +// var <- var - mom +// +// Arguments: +// var_: Should be from a Variable(). +// ms: Should be from a Variable(). +// mom: Should be from a Variable(). +// lr: Scaling factor. Must be a scalar. +// rho: Decay rate. Must be a scalar. +// +// epsilon: Ridge term. Must be a scalar. +// grad: The gradient. +// +// Returns the created operation. +func ResourceApplyRMSProp(scope *Scope, var_ tf.Output, ms tf.Output, mom tf.Output, lr tf.Output, rho tf.Output, momentum tf.Output, epsilon tf.Output, grad tf.Output, optional ...ResourceApplyRMSPropAttr) (o *tf.Operation) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{} + for _, a := range optional { + a(attrs) + } + opspec := tf.OpSpec{ + Type: "ResourceApplyRMSProp", + Input: []tf.Input{ + var_, ms, mom, lr, rho, momentum, epsilon, grad, + }, + Attrs: attrs, + } + return scope.AddOperation(opspec) +} + // Returns immutable tensor from memory region. // // The current implementation memmaps the tensor from a file. @@ -18017,119 +18342,6 @@ func ResourceApplyCenteredRMSProp(scope *Scope, var_ tf.Output, mg tf.Output, ms return scope.AddOperation(opspec) } -// Computes the gradient for the inverse of `x` wrt its input. -// -// Specifically, `grad = -dy * y*y`, where `y = 1/x`, and `dy` -// is the corresponding input gradient. -func ReciprocalGrad(scope *Scope, y tf.Output, dy tf.Output) (z tf.Output) { - if scope.Err() != nil { - return - } - opspec := tf.OpSpec{ - Type: "ReciprocalGrad", - Input: []tf.Input{ - y, dy, - }, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - -// Returns the min of x and y (i.e. x < y ? x : y) element-wise. -// -// *NOTE*: `Minimum` supports broadcasting. More about broadcasting -// [here](http://docs.scipy.org/doc/numpy/user/basics.broadcasting.html) -func Minimum(scope *Scope, x tf.Output, y tf.Output) (z tf.Output) { - if scope.Err() != nil { - return - } - opspec := tf.OpSpec{ - Type: "Minimum", - Input: []tf.Input{ - x, y, - }, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - -// MfccAttr is an optional argument to Mfcc. -type MfccAttr func(optionalAttr) - -// MfccUpperFrequencyLimit sets the optional upper_frequency_limit attribute to value. -// -// value: The highest frequency to use when calculating the -// ceptstrum. -// If not specified, defaults to 4000 -func MfccUpperFrequencyLimit(value float32) MfccAttr { - return func(m optionalAttr) { - m["upper_frequency_limit"] = value - } -} - -// MfccLowerFrequencyLimit sets the optional lower_frequency_limit attribute to value. -// -// value: The lowest frequency to use when calculating the -// ceptstrum. -// If not specified, defaults to 20 -func MfccLowerFrequencyLimit(value float32) MfccAttr { - return func(m optionalAttr) { - m["lower_frequency_limit"] = value - } -} - -// MfccFilterbankChannelCount sets the optional filterbank_channel_count attribute to value. -// -// value: Resolution of the Mel bank used internally. -// If not specified, defaults to 40 -func MfccFilterbankChannelCount(value int64) MfccAttr { - return func(m optionalAttr) { - m["filterbank_channel_count"] = value - } -} - -// MfccDctCoefficientCount sets the optional dct_coefficient_count attribute to value. -// -// value: How many output channels to produce per time slice. -// If not specified, defaults to 13 -func MfccDctCoefficientCount(value int64) MfccAttr { - return func(m optionalAttr) { - m["dct_coefficient_count"] = value - } -} - -// Transforms a spectrogram into a form that's useful for speech recognition. -// -// Mel Frequency Cepstral Coefficients are a way of representing audio data that's -// been effective as an input feature for machine learning. They are created by -// taking the spectrum of a spectrogram (a 'cepstrum'), and discarding some of the -// higher frequencies that are less significant to the human ear. They have a long -// history in the speech recognition world, and https://en.wikipedia.org/wiki/Mel-frequency_cepstrum -// is a good resource to learn more. -// -// Arguments: -// spectrogram: Typically produced by the Spectrogram op, with magnitude_squared -// set to true. -// sample_rate: How many samples per second the source audio used. -func Mfcc(scope *Scope, spectrogram tf.Output, sample_rate tf.Output, optional ...MfccAttr) (output tf.Output) { - if scope.Err() != nil { - return - } - attrs := map[string]interface{}{} - for _, a := range optional { - a(attrs) - } - opspec := tf.OpSpec{ - Type: "Mfcc", - Input: []tf.Input{ - spectrogram, sample_rate, - }, - Attrs: attrs, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - // AudioSummaryAttr is an optional argument to AudioSummary. type AudioSummaryAttr func(optionalAttr) @@ -18240,23 +18452,6 @@ func Qr(scope *Scope, input tf.Output, optional ...QrAttr) (q tf.Output, r tf.Ou return op.Output(0), op.Output(1) } -// Records the bytes size of each element of `input_dataset` in a StatsAggregator. -func BytesProducedStatsDataset(scope *Scope, input_dataset tf.Output, tag tf.Output, output_types []tf.DataType, output_shapes []tf.Shape) (handle tf.Output) { - if scope.Err() != nil { - return - } - attrs := map[string]interface{}{"output_types": output_types, "output_shapes": output_shapes} - opspec := tf.OpSpec{ - Type: "BytesProducedStatsDataset", - Input: []tf.Input{ - input_dataset, tag, - }, - Attrs: attrs, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - // Check if the input matches the regex pattern. // // The input is a string tensor of any shape. The pattern is the @@ -18372,6 +18567,29 @@ func RFFT(scope *Scope, input tf.Output, fft_length tf.Output) (output tf.Output return op.Output(0) } +// Adds a value to the current value of a variable. +// +// Any ReadVariableOp with a control dependency on this op is guaranteed to +// see the incremented value or a subsequent newer one. +// +// Arguments: +// resource: handle to the resource in which to store the variable. +// value: the value by which the variable will be incremented. +// +// Returns the created operation. +func AssignAddVariableOp(scope *Scope, resource tf.Output, value tf.Output) (o *tf.Operation) { + if scope.Err() != nil { + return + } + opspec := tf.OpSpec{ + Type: "AssignAddVariableOp", + Input: []tf.Input{ + resource, value, + }, + } + return scope.AddOperation(opspec) +} + // QuantizedReluAttr is an optional argument to QuantizedRelu. type QuantizedReluAttr func(optionalAttr) @@ -19161,149 +19379,6 @@ func TensorArrayV2(scope *Scope, size tf.Output, dtype tf.DataType, optional ... return op.Output(0) } -// DecodeCSVAttr is an optional argument to DecodeCSV. -type DecodeCSVAttr func(optionalAttr) - -// DecodeCSVFieldDelim sets the optional field_delim attribute to value. -// -// value: char delimiter to separate fields in a record. -// If not specified, defaults to "," -func DecodeCSVFieldDelim(value string) DecodeCSVAttr { - return func(m optionalAttr) { - m["field_delim"] = value - } -} - -// DecodeCSVUseQuoteDelim sets the optional use_quote_delim attribute to value. -// -// value: If false, treats double quotation marks as regular -// characters inside of the string fields (ignoring RFC 4180, Section 2, -// Bullet 5). -// If not specified, defaults to true -func DecodeCSVUseQuoteDelim(value bool) DecodeCSVAttr { - return func(m optionalAttr) { - m["use_quote_delim"] = value - } -} - -// DecodeCSVNaValue sets the optional na_value attribute to value. -// -// value: Additional string to recognize as NA/NaN. -// If not specified, defaults to "" -func DecodeCSVNaValue(value string) DecodeCSVAttr { - return func(m optionalAttr) { - m["na_value"] = value - } -} - -// DecodeCSVSelectCols sets the optional select_cols attribute to value. -// If not specified, defaults to <> -func DecodeCSVSelectCols(value []int64) DecodeCSVAttr { - return func(m optionalAttr) { - m["select_cols"] = value - } -} - -// Convert CSV records to tensors. Each column maps to one tensor. -// -// RFC 4180 format is expected for the CSV records. -// (https://tools.ietf.org/html/rfc4180) -// Note that we allow leading and trailing spaces with int or float field. -// -// Arguments: -// records: Each string is a record/row in the csv and all records should have -// the same format. -// record_defaults: One tensor per column of the input record, with either a -// scalar default value for that column or an empty vector if the column is -// required. -// -// Returns Each tensor will have the same shape as records. -func DecodeCSV(scope *Scope, records tf.Output, record_defaults []tf.Output, optional ...DecodeCSVAttr) (output []tf.Output) { - if scope.Err() != nil { - return - } - attrs := map[string]interface{}{} - for _, a := range optional { - a(attrs) - } - opspec := tf.OpSpec{ - Type: "DecodeCSV", - Input: []tf.Input{ - records, tf.OutputList(record_defaults), - }, - Attrs: attrs, - } - op := scope.AddOperation(opspec) - if scope.Err() != nil { - return - } - var idx int - var err error - if output, idx, err = makeOutputList(op, idx, "output"); err != nil { - scope.UpdateErr("DecodeCSV", err) - return - } - return output -} - -// MapClearAttr is an optional argument to MapClear. -type MapClearAttr func(optionalAttr) - -// MapClearCapacity sets the optional capacity attribute to value. -// If not specified, defaults to 0 -// -// REQUIRES: value >= 0 -func MapClearCapacity(value int64) MapClearAttr { - return func(m optionalAttr) { - m["capacity"] = value - } -} - -// MapClearMemoryLimit sets the optional memory_limit attribute to value. -// If not specified, defaults to 0 -// -// REQUIRES: value >= 0 -func MapClearMemoryLimit(value int64) MapClearAttr { - return func(m optionalAttr) { - m["memory_limit"] = value - } -} - -// MapClearContainer sets the optional container attribute to value. -// If not specified, defaults to "" -func MapClearContainer(value string) MapClearAttr { - return func(m optionalAttr) { - m["container"] = value - } -} - -// MapClearSharedName sets the optional shared_name attribute to value. -// If not specified, defaults to "" -func MapClearSharedName(value string) MapClearAttr { - return func(m optionalAttr) { - m["shared_name"] = value - } -} - -// Op removes all elements in the underlying container. -// -// Returns the created operation. -func MapClear(scope *Scope, dtypes []tf.DataType, optional ...MapClearAttr) (o *tf.Operation) { - if scope.Err() != nil { - return - } - attrs := map[string]interface{}{"dtypes": dtypes} - for _, a := range optional { - a(attrs) - } - opspec := tf.OpSpec{ - Type: "MapClear", - - Attrs: attrs, - } - return scope.AddOperation(opspec) -} - // ThreadUnsafeUnigramCandidateSamplerAttr is an optional argument to ThreadUnsafeUnigramCandidateSampler. type ThreadUnsafeUnigramCandidateSamplerAttr func(optionalAttr) @@ -19523,6 +19598,119 @@ func MutableDenseHashTableV2(scope *Scope, empty_key tf.Output, deleted_key tf.O return op.Output(0) } +// Computes the gradient for the inverse of `x` wrt its input. +// +// Specifically, `grad = -dy * y*y`, where `y = 1/x`, and `dy` +// is the corresponding input gradient. +func ReciprocalGrad(scope *Scope, y tf.Output, dy tf.Output) (z tf.Output) { + if scope.Err() != nil { + return + } + opspec := tf.OpSpec{ + Type: "ReciprocalGrad", + Input: []tf.Input{ + y, dy, + }, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + +// Returns the min of x and y (i.e. x < y ? x : y) element-wise. +// +// *NOTE*: `Minimum` supports broadcasting. More about broadcasting +// [here](http://docs.scipy.org/doc/numpy/user/basics.broadcasting.html) +func Minimum(scope *Scope, x tf.Output, y tf.Output) (z tf.Output) { + if scope.Err() != nil { + return + } + opspec := tf.OpSpec{ + Type: "Minimum", + Input: []tf.Input{ + x, y, + }, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + +// MfccAttr is an optional argument to Mfcc. +type MfccAttr func(optionalAttr) + +// MfccUpperFrequencyLimit sets the optional upper_frequency_limit attribute to value. +// +// value: The highest frequency to use when calculating the +// ceptstrum. +// If not specified, defaults to 4000 +func MfccUpperFrequencyLimit(value float32) MfccAttr { + return func(m optionalAttr) { + m["upper_frequency_limit"] = value + } +} + +// MfccLowerFrequencyLimit sets the optional lower_frequency_limit attribute to value. +// +// value: The lowest frequency to use when calculating the +// ceptstrum. +// If not specified, defaults to 20 +func MfccLowerFrequencyLimit(value float32) MfccAttr { + return func(m optionalAttr) { + m["lower_frequency_limit"] = value + } +} + +// MfccFilterbankChannelCount sets the optional filterbank_channel_count attribute to value. +// +// value: Resolution of the Mel bank used internally. +// If not specified, defaults to 40 +func MfccFilterbankChannelCount(value int64) MfccAttr { + return func(m optionalAttr) { + m["filterbank_channel_count"] = value + } +} + +// MfccDctCoefficientCount sets the optional dct_coefficient_count attribute to value. +// +// value: How many output channels to produce per time slice. +// If not specified, defaults to 13 +func MfccDctCoefficientCount(value int64) MfccAttr { + return func(m optionalAttr) { + m["dct_coefficient_count"] = value + } +} + +// Transforms a spectrogram into a form that's useful for speech recognition. +// +// Mel Frequency Cepstral Coefficients are a way of representing audio data that's +// been effective as an input feature for machine learning. They are created by +// taking the spectrum of a spectrogram (a 'cepstrum'), and discarding some of the +// higher frequencies that are less significant to the human ear. They have a long +// history in the speech recognition world, and https://en.wikipedia.org/wiki/Mel-frequency_cepstrum +// is a good resource to learn more. +// +// Arguments: +// spectrogram: Typically produced by the Spectrogram op, with magnitude_squared +// set to true. +// sample_rate: How many samples per second the source audio used. +func Mfcc(scope *Scope, spectrogram tf.Output, sample_rate tf.Output, optional ...MfccAttr) (output tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{} + for _, a := range optional { + a(attrs) + } + opspec := tf.OpSpec{ + Type: "Mfcc", + Input: []tf.Input{ + spectrogram, sample_rate, + }, + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // Compute the Hurwitz zeta function \\(\zeta(x, q)\\). // // The Hurwitz zeta function is defined as: @@ -19627,63 +19815,6 @@ func IFFT2D(scope *Scope, input tf.Output) (output tf.Output) { return op.Output(0) } -// ResourceApplyRMSPropAttr is an optional argument to ResourceApplyRMSProp. -type ResourceApplyRMSPropAttr func(optionalAttr) - -// ResourceApplyRMSPropUseLocking sets the optional use_locking attribute to value. -// -// value: If `True`, updating of the var, ms, and mom tensors is protected -// by a lock; otherwise the behavior is undefined, but may exhibit less -// contention. -// If not specified, defaults to false -func ResourceApplyRMSPropUseLocking(value bool) ResourceApplyRMSPropAttr { - return func(m optionalAttr) { - m["use_locking"] = value - } -} - -// Update '*var' according to the RMSProp algorithm. -// -// Note that in dense implementation of this algorithm, ms and mom will -// update even if the grad is zero, but in this sparse implementation, ms -// and mom will not update in iterations during which the grad is zero. -// -// mean_square = decay * mean_square + (1-decay) * gradient ** 2 -// Delta = learning_rate * gradient / sqrt(mean_square + epsilon) -// -// ms <- rho * ms_{t-1} + (1-rho) * grad * grad -// mom <- momentum * mom_{t-1} + lr * grad / sqrt(ms + epsilon) -// var <- var - mom -// -// Arguments: -// var_: Should be from a Variable(). -// ms: Should be from a Variable(). -// mom: Should be from a Variable(). -// lr: Scaling factor. Must be a scalar. -// rho: Decay rate. Must be a scalar. -// -// epsilon: Ridge term. Must be a scalar. -// grad: The gradient. -// -// Returns the created operation. -func ResourceApplyRMSProp(scope *Scope, var_ tf.Output, ms tf.Output, mom tf.Output, lr tf.Output, rho tf.Output, momentum tf.Output, epsilon tf.Output, grad tf.Output, optional ...ResourceApplyRMSPropAttr) (o *tf.Operation) { - if scope.Err() != nil { - return - } - attrs := map[string]interface{}{} - for _, a := range optional { - a(attrs) - } - opspec := tf.OpSpec{ - Type: "ResourceApplyRMSProp", - Input: []tf.Input{ - var_, ms, mom, lr, rho, momentum, epsilon, grad, - }, - Attrs: attrs, - } - return scope.AddOperation(opspec) -} - // Returns element-wise remainder of division. This emulates C semantics in that // // the result here is consistent with a truncating divide. E.g. `truncate(x / y) * @@ -20760,67 +20891,6 @@ func AddSparseToTensorsMap(scope *Scope, sparse_indices tf.Output, sparse_values return op.Output(0) } -// SparseTensorDenseMatMulAttr is an optional argument to SparseTensorDenseMatMul. -type SparseTensorDenseMatMulAttr func(optionalAttr) - -// SparseTensorDenseMatMulAdjointA sets the optional adjoint_a attribute to value. -// -// value: Use the adjoint of A in the matrix multiply. If A is complex, this -// is transpose(conj(A)). Otherwise it's transpose(A). -// If not specified, defaults to false -func SparseTensorDenseMatMulAdjointA(value bool) SparseTensorDenseMatMulAttr { - return func(m optionalAttr) { - m["adjoint_a"] = value - } -} - -// SparseTensorDenseMatMulAdjointB sets the optional adjoint_b attribute to value. -// -// value: Use the adjoint of B in the matrix multiply. If B is complex, this -// is transpose(conj(B)). Otherwise it's transpose(B). -// If not specified, defaults to false -func SparseTensorDenseMatMulAdjointB(value bool) SparseTensorDenseMatMulAttr { - return func(m optionalAttr) { - m["adjoint_b"] = value - } -} - -// Multiply SparseTensor (of rank 2) "A" by dense matrix "B". -// -// No validity checking is performed on the indices of A. However, the following -// input format is recommended for optimal behavior: -// -// if adjoint_a == false: -// A should be sorted in lexicographically increasing order. Use SparseReorder -// if you're not sure. -// if adjoint_a == true: -// A should be sorted in order of increasing dimension 1 (i.e., "column major" -// order instead of "row major" order). -// -// Arguments: -// a_indices: 2-D. The `indices` of the `SparseTensor`, size `[nnz, 2]` Matrix. -// a_values: 1-D. The `values` of the `SparseTensor`, size `[nnz]` Vector. -// a_shape: 1-D. The `shape` of the `SparseTensor`, size `[2]` Vector. -// b: 2-D. A dense Matrix. -func SparseTensorDenseMatMul(scope *Scope, a_indices tf.Output, a_values tf.Output, a_shape tf.Output, b tf.Output, optional ...SparseTensorDenseMatMulAttr) (product tf.Output) { - if scope.Err() != nil { - return - } - attrs := map[string]interface{}{} - for _, a := range optional { - a(attrs) - } - opspec := tf.OpSpec{ - Type: "SparseTensorDenseMatMul", - Input: []tf.Input{ - a_indices, a_values, a_shape, b, - }, - Attrs: attrs, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - // Deserialize and concatenate `SparseTensors` from a serialized minibatch. // // The input `serialized_sparse` must be a string matrix of shape `[N x 3]` where @@ -20903,151 +20973,6 @@ func BitwiseAnd(scope *Scope, x tf.Output, y tf.Output) (z tf.Output) { return op.Output(0) } -// SubstrAttr is an optional argument to Substr. -type SubstrAttr func(optionalAttr) - -// SubstrUnit sets the optional unit attribute to value. -// -// value: The unit that is used to create the substring. One of: `"BYTE"` (for -// defining position and length by bytes) or `"UTF8_CHAR"` (for the UTF-8 -// encoded Unicode code points). The default is `"BYTE"`. Results are undefined if -// `unit=UTF8_CHAR` and the `input` strings do not contain structurally valid -// UTF-8. -// If not specified, defaults to "BYTE" -func SubstrUnit(value string) SubstrAttr { - return func(m optionalAttr) { - m["unit"] = value - } -} - -// Return substrings from `Tensor` of strings. -// -// For each string in the input `Tensor`, creates a substring starting at index -// `pos` with a total length of `len`. -// -// If `len` defines a substring that would extend beyond the length of the input -// string, then as many characters as possible are used. -// -// A negative `pos` indicates distance within the string backwards from the end. -// -// If `pos` specifies an index which is out of range for any of the input strings, -// then an `InvalidArgumentError` is thrown. -// -// `pos` and `len` must have the same shape, otherwise a `ValueError` is thrown on -// Op creation. -// -// *NOTE*: `Substr` supports broadcasting up to two dimensions. More about -// broadcasting -// [here](http://docs.scipy.org/doc/numpy/user/basics.broadcasting.html) -// -// --- -// -// Examples -// -// Using scalar `pos` and `len`: -// -// ```python -// input = [b'Hello', b'World'] -// position = 1 -// length = 3 -// -// output = [b'ell', b'orl'] -// ``` -// -// Using `pos` and `len` with same shape as `input`: -// -// ```python -// input = [[b'ten', b'eleven', b'twelve'], -// [b'thirteen', b'fourteen', b'fifteen'], -// [b'sixteen', b'seventeen', b'eighteen']] -// position = [[1, 2, 3], -// [1, 2, 3], -// [1, 2, 3]] -// length = [[2, 3, 4], -// [4, 3, 2], -// [5, 5, 5]] -// -// output = [[b'en', b'eve', b'lve'], -// [b'hirt', b'urt', b'te'], -// [b'ixtee', b'vente', b'hteen']] -// ``` -// -// Broadcasting `pos` and `len` onto `input`: -// -// ``` -// input = [[b'ten', b'eleven', b'twelve'], -// [b'thirteen', b'fourteen', b'fifteen'], -// [b'sixteen', b'seventeen', b'eighteen'], -// [b'nineteen', b'twenty', b'twentyone']] -// position = [1, 2, 3] -// length = [1, 2, 3] -// -// output = [[b'e', b'ev', b'lve'], -// [b'h', b'ur', b'tee'], -// [b'i', b've', b'hte'], -// [b'i', b'en', b'nty']] -// ``` -// -// Broadcasting `input` onto `pos` and `len`: -// -// ``` -// input = b'thirteen' -// position = [1, 5, 7] -// length = [3, 2, 1] -// -// output = [b'hir', b'ee', b'n'] -// ``` -// -// Arguments: -// input: Tensor of strings -// pos: Scalar defining the position of first character in each substring -// len: Scalar defining the number of characters to include in each substring -// -// Returns Tensor of substrings -func Substr(scope *Scope, input tf.Output, pos tf.Output, len tf.Output, optional ...SubstrAttr) (output tf.Output) { - if scope.Err() != nil { - return - } - attrs := map[string]interface{}{} - for _, a := range optional { - a(attrs) - } - opspec := tf.OpSpec{ - Type: "Substr", - Input: []tf.Input{ - input, pos, len, - }, - Attrs: attrs, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - -// Creates a Dataset that returns pseudorandom numbers. -// -// Arguments: -// seed: A scalar seed for the random number generator. If either seed or -// seed2 is set to be non-zero, the random number generator is seeded -// by the given seed. Otherwise, a random seed is used. -// seed2: A second scalar seed to avoid seed collision. -// -// -func RandomDataset(scope *Scope, seed tf.Output, seed2 tf.Output, output_types []tf.DataType, output_shapes []tf.Shape) (handle tf.Output) { - if scope.Err() != nil { - return - } - attrs := map[string]interface{}{"output_types": output_types, "output_shapes": output_shapes} - opspec := tf.OpSpec{ - Type: "RandomDataset", - Input: []tf.Input{ - seed, seed2, - }, - Attrs: attrs, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - // Inverse real-valued fast Fourier transform. // // Computes the inverse 1-dimensional discrete Fourier transform of a real-valued @@ -22230,6 +22155,34 @@ func MatrixTriangularSolve(scope *Scope, matrix tf.Output, rhs tf.Output, option return op.Output(0) } +// Saves tensors in V2 checkpoint format. +// +// By default, saves the named tensors in full. If the caller wishes to save +// specific slices of full tensors, "shape_and_slices" should be non-empty strings +// and correspondingly well-formed. +// +// Arguments: +// prefix: Must have a single element. The prefix of the V2 checkpoint to which we +// write the tensors. +// tensor_names: shape {N}. The names of the tensors to be saved. +// shape_and_slices: shape {N}. The slice specs of the tensors to be saved. +// Empty strings indicate that they are non-partitioned tensors. +// tensors: `N` tensors to save. +// +// Returns the created operation. +func SaveV2(scope *Scope, prefix tf.Output, tensor_names tf.Output, shape_and_slices tf.Output, tensors []tf.Output) (o *tf.Operation) { + if scope.Err() != nil { + return + } + opspec := tf.OpSpec{ + Type: "SaveV2", + Input: []tf.Input{ + prefix, tensor_names, shape_and_slices, tf.OutputList(tensors), + }, + } + return scope.AddOperation(opspec) +} + // UnicodeTranscodeAttr is an optional argument to UnicodeTranscode. type UnicodeTranscodeAttr func(optionalAttr) @@ -25011,6 +24964,75 @@ func Cumsum(scope *Scope, x tf.Output, axis tf.Output, optional ...CumsumAttr) ( return op.Output(0) } +// Return the shape of s0 op s1 with broadcast. +// +// Given `s0` and `s1`, tensors that represent shapes, compute `r0`, the +// broadcasted shape. `s0`, `s1` and `r0` are all integer vectors. +func BroadcastArgs(scope *Scope, s0 tf.Output, s1 tf.Output) (r0 tf.Output) { + if scope.Err() != nil { + return + } + opspec := tf.OpSpec{ + Type: "BroadcastArgs", + Input: []tf.Input{ + s0, s1, + }, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + +// DataFormatDimMapAttr is an optional argument to DataFormatDimMap. +type DataFormatDimMapAttr func(optionalAttr) + +// DataFormatDimMapSrcFormat sets the optional src_format attribute to value. +// +// value: source data format. +// If not specified, defaults to "NHWC" +func DataFormatDimMapSrcFormat(value string) DataFormatDimMapAttr { + return func(m optionalAttr) { + m["src_format"] = value + } +} + +// DataFormatDimMapDstFormat sets the optional dst_format attribute to value. +// +// value: destination data format. +// If not specified, defaults to "NCHW" +func DataFormatDimMapDstFormat(value string) DataFormatDimMapAttr { + return func(m optionalAttr) { + m["dst_format"] = value + } +} + +// Returns the dimension index in the destination data format given the one in +// +// the source data format. +// +// Arguments: +// x: A Tensor with each element as a dimension index in source data format. +// Must be in the range [-4, 4). +// +// Returns A Tensor with each element as a dimension index in destination data format. +func DataFormatDimMap(scope *Scope, x tf.Output, optional ...DataFormatDimMapAttr) (y tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{} + for _, a := range optional { + a(attrs) + } + opspec := tf.OpSpec{ + Type: "DataFormatDimMap", + Input: []tf.Input{ + x, + }, + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // CumprodAttr is an optional argument to Cumprod. type CumprodAttr func(optionalAttr) @@ -26322,24 +26344,6 @@ func TensorArrayReadV3(scope *Scope, handle tf.Output, index tf.Output, flow_in return op.Output(0) } -// Computes the gradient for the tanh of `x` wrt its input. -// -// Specifically, `grad = dy * (1 - y*y)`, where `y = tanh(x)`, and `dy` -// is the corresponding input gradient. -func TanhGrad(scope *Scope, y tf.Output, dy tf.Output) (z tf.Output) { - if scope.Err() != nil { - return - } - opspec := tf.OpSpec{ - Type: "TanhGrad", - Input: []tf.Input{ - y, dy, - }, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - // Reduces sparse updates into the variable referenced by `resource` using the `max` operation. // // This operation computes @@ -26381,6 +26385,24 @@ func ResourceScatterMax(scope *Scope, resource tf.Output, indices tf.Output, upd return scope.AddOperation(opspec) } +// Computes the gradient for the tanh of `x` wrt its input. +// +// Specifically, `grad = dy * (1 - y*y)`, where `y = tanh(x)`, and `dy` +// is the corresponding input gradient. +func TanhGrad(scope *Scope, y tf.Output, dy tf.Output) (z tf.Output) { + if scope.Err() != nil { + return + } + opspec := tf.OpSpec{ + Type: "TanhGrad", + Input: []tf.Input{ + y, dy, + }, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // Outputs a `Summary` protocol buffer with scalar values. // // The input `tags` and `values` must have the same shape. The generated summary @@ -26669,24 +26691,6 @@ func MergeSummary(scope *Scope, inputs []tf.Output) (summary tf.Output) { return op.Output(0) } -// Returns the number of tensors in the input tensor list. -// -// input_handle: the input list -// length: the number of tensors in the list -func TensorListLength(scope *Scope, input_handle tf.Output) (length tf.Output) { - if scope.Err() != nil { - return - } - opspec := tf.OpSpec{ - Type: "TensorListLength", - Input: []tf.Input{ - input_handle, - }, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - // The shape of the elements of the given list, as a tensor. // // input_handle: the list @@ -27361,35 +27365,6 @@ func MatrixSquareRoot(scope *Scope, input tf.Output) (output tf.Output) { return op.Output(0) } -// Convert JSON-encoded Example records to binary protocol buffer strings. -// -// This op translates a tensor containing Example records, encoded using -// the [standard JSON -// mapping](https://developers.google.com/protocol-buffers/docs/proto3#json), -// into a tensor containing the same records encoded as binary protocol -// buffers. The resulting tensor can then be fed to any of the other -// Example-parsing ops. -// -// Arguments: -// json_examples: Each string is a JSON object serialized according to the JSON -// mapping of the Example proto. -// -// Returns Each string is a binary Example protocol buffer corresponding -// to the respective element of `json_examples`. -func DecodeJSONExample(scope *Scope, json_examples tf.Output) (binary_examples tf.Output) { - if scope.Err() != nil { - return - } - opspec := tf.OpSpec{ - Type: "DecodeJSONExample", - Input: []tf.Input{ - json_examples, - }, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - // SvdAttr is an optional argument to Svd. type SvdAttr func(optionalAttr) @@ -28426,6 +28401,49 @@ func ReaderReadUpToV2(scope *Scope, reader_handle tf.Output, queue_handle tf.Out return op.Output(0), op.Output(1) } +// Adds v into specified rows of x. +// +// Computes y = x; y[i, :] += v; return y. +// +// Arguments: +// x: A `Tensor` of type T. +// i: A vector. Indices into the left-most dimension of `x`. +// v: A `Tensor` of type T. Same dimension sizes as x except the first dimension, which must be the same as i's size. +// +// Returns A `Tensor` of type T. An alias of `x`. The content of `y` is undefined if there are duplicates in `i`. +func InplaceAdd(scope *Scope, x tf.Output, i tf.Output, v tf.Output) (y tf.Output) { + if scope.Err() != nil { + return + } + opspec := tf.OpSpec{ + Type: "InplaceAdd", + Input: []tf.Input{ + x, i, v, + }, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + +// Restore a Reader to its initial clean state. +// +// Arguments: +// reader_handle: Handle to a Reader. +// +// Returns the created operation. +func ReaderResetV2(scope *Scope, reader_handle tf.Output) (o *tf.Operation) { + if scope.Err() != nil { + return + } + opspec := tf.OpSpec{ + Type: "ReaderResetV2", + Input: []tf.Input{ + reader_handle, + }, + } + return scope.AddOperation(opspec) +} + // BatchAttr is an optional argument to Batch. type BatchAttr func(optionalAttr) @@ -29711,71 +29729,6 @@ func CropAndResizeGradBoxes(scope *Scope, grads tf.Output, image tf.Output, boxe return op.Output(0) } -// Saves tensors in V2 checkpoint format. -// -// By default, saves the named tensors in full. If the caller wishes to save -// specific slices of full tensors, "shape_and_slices" should be non-empty strings -// and correspondingly well-formed. -// -// Arguments: -// prefix: Must have a single element. The prefix of the V2 checkpoint to which we -// write the tensors. -// tensor_names: shape {N}. The names of the tensors to be saved. -// shape_and_slices: shape {N}. The slice specs of the tensors to be saved. -// Empty strings indicate that they are non-partitioned tensors. -// tensors: `N` tensors to save. -// -// Returns the created operation. -func SaveV2(scope *Scope, prefix tf.Output, tensor_names tf.Output, shape_and_slices tf.Output, tensors []tf.Output) (o *tf.Operation) { - if scope.Err() != nil { - return - } - opspec := tf.OpSpec{ - Type: "SaveV2", - Input: []tf.Input{ - prefix, tensor_names, shape_and_slices, tf.OutputList(tensors), - }, - } - return scope.AddOperation(opspec) -} - -// StatsAggregatorHandleAttr is an optional argument to StatsAggregatorHandle. -type StatsAggregatorHandleAttr func(optionalAttr) - -// StatsAggregatorHandleContainer sets the optional container attribute to value. -// If not specified, defaults to "" -func StatsAggregatorHandleContainer(value string) StatsAggregatorHandleAttr { - return func(m optionalAttr) { - m["container"] = value - } -} - -// StatsAggregatorHandleSharedName sets the optional shared_name attribute to value. -// If not specified, defaults to "" -func StatsAggregatorHandleSharedName(value string) StatsAggregatorHandleAttr { - return func(m optionalAttr) { - m["shared_name"] = value - } -} - -// Creates a statistics manager resource. -func StatsAggregatorHandle(scope *Scope, optional ...StatsAggregatorHandleAttr) (handle tf.Output) { - if scope.Err() != nil { - return - } - attrs := map[string]interface{}{} - for _, a := range optional { - a(attrs) - } - opspec := tf.OpSpec{ - Type: "StatsAggregatorHandle", - - Attrs: attrs, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - // Greedily selects a subset of bounding boxes in descending order of score, // // pruning away boxes that have high intersection-over-union (IOU) overlap @@ -29955,116 +29908,64 @@ func FakeParam(scope *Scope, dtype tf.DataType, shape tf.Shape) (output tf.Outpu return op.Output(0) } -// A substitute for `InterleaveDataset` on a fixed list of `N` datasets. -// -// Arguments: -// selector_input_dataset: A dataset of scalar `DT_INT64` elements that determines which of the -// `N` data inputs should produce the next output element. -// data_input_datasets: `N` datasets with the same type that will be interleaved according to -// the values of `selector_input_dataset`. -// +// Computes the gradient for the inverse of `x` wrt its input. // -func ExperimentalDirectedInterleaveDataset(scope *Scope, selector_input_dataset tf.Output, data_input_datasets []tf.Output, output_types []tf.DataType, output_shapes []tf.Shape) (handle tf.Output) { +// Specifically, `grad = -dy * y*y`, where `y = 1/x`, and `dy` +// is the corresponding input gradient. +func InvGrad(scope *Scope, y tf.Output, dy tf.Output) (z tf.Output) { if scope.Err() != nil { return } - attrs := map[string]interface{}{"output_types": output_types, "output_shapes": output_shapes} opspec := tf.OpSpec{ - Type: "ExperimentalDirectedInterleaveDataset", + Type: "InvGrad", Input: []tf.Input{ - selector_input_dataset, tf.OutputList(data_input_datasets), + y, dy, }, - Attrs: attrs, } op := scope.AddOperation(opspec) return op.Output(0) } -// Gets the next element from a FunctionBufferingResource. -// -// Arguments: -// function_buffer_resource: The FunctionBufferingResource handle. -// output_types: The type list for the return values. +// List of the given size with empty elements. // -// Returns A list of return values. -func ExperimentalFunctionBufferingResourceGetNext(scope *Scope, function_buffer_resource tf.Output, output_types []tf.DataType) (output []tf.Output) { +// element_shape: the shape of the future elements of the list +// num_elements: the number of elements to reserve +// handle: the output list +// element_dtype: the desired type of elements in the list. +func TensorListReserve(scope *Scope, element_shape tf.Output, num_elements tf.Output, element_dtype tf.DataType) (handle tf.Output) { if scope.Err() != nil { return } - attrs := map[string]interface{}{"output_types": output_types} + attrs := map[string]interface{}{"element_dtype": element_dtype} opspec := tf.OpSpec{ - Type: "ExperimentalFunctionBufferingResourceGetNext", + Type: "TensorListReserve", Input: []tf.Input{ - function_buffer_resource, + element_shape, num_elements, }, Attrs: attrs, } op := scope.AddOperation(opspec) - if scope.Err() != nil { - return - } - var idx int - var err error - if output, idx, err = makeOutputList(op, idx, "output"); err != nil { - scope.UpdateErr("ExperimentalFunctionBufferingResourceGetNext", err) - return - } - return output -} - -// Adds v into specified rows of x. -// -// Computes y = x; y[i, :] += v; return y. -// -// Arguments: -// x: A `Tensor` of type T. -// i: A vector. Indices into the left-most dimension of `x`. -// v: A `Tensor` of type T. Same dimension sizes as x except the first dimension, which must be the same as i's size. -// -// Returns A `Tensor` of type T. An alias of `x`. The content of `y` is undefined if there are duplicates in `i`. -func InplaceAdd(scope *Scope, x tf.Output, i tf.Output, v tf.Output) (y tf.Output) { - if scope.Err() != nil { - return - } - opspec := tf.OpSpec{ - Type: "InplaceAdd", - Input: []tf.Input{ - x, i, v, - }, - } - op := scope.AddOperation(opspec) return op.Output(0) } -// Restore a Reader to its initial clean state. +// A substitute for `InterleaveDataset` on a fixed list of `N` datasets. // // Arguments: -// reader_handle: Handle to a Reader. +// selector_input_dataset: A dataset of scalar `DT_INT64` elements that determines which of the +// `N` data inputs should produce the next output element. +// data_input_datasets: `N` datasets with the same type that will be interleaved according to +// the values of `selector_input_dataset`. // -// Returns the created operation. -func ReaderResetV2(scope *Scope, reader_handle tf.Output) (o *tf.Operation) { - if scope.Err() != nil { - return - } - opspec := tf.OpSpec{ - Type: "ReaderResetV2", - Input: []tf.Input{ - reader_handle, - }, - } - return scope.AddOperation(opspec) -} - -// A dataset that splits the elements of its input into multiple elements. -func UnbatchDataset(scope *Scope, input_dataset tf.Output, output_types []tf.DataType, output_shapes []tf.Shape) (handle tf.Output) { +// +func ExperimentalDirectedInterleaveDataset(scope *Scope, selector_input_dataset tf.Output, data_input_datasets []tf.Output, output_types []tf.DataType, output_shapes []tf.Shape) (handle tf.Output) { if scope.Err() != nil { return } attrs := map[string]interface{}{"output_types": output_types, "output_shapes": output_shapes} opspec := tf.OpSpec{ - Type: "UnbatchDataset", + Type: "ExperimentalDirectedInterleaveDataset", Input: []tf.Input{ - input_dataset, + selector_input_dataset, tf.OutputList(data_input_datasets), }, Attrs: attrs, } @@ -30072,184 +29973,48 @@ func UnbatchDataset(scope *Scope, input_dataset tf.Output, output_types []tf.Dat return op.Output(0) } -// OrderedMapStageAttr is an optional argument to OrderedMapStage. -type OrderedMapStageAttr func(optionalAttr) +// RandomUniformIntAttr is an optional argument to RandomUniformInt. +type RandomUniformIntAttr func(optionalAttr) -// OrderedMapStageCapacity sets the optional capacity attribute to value. -// -// value: Maximum number of elements in the Staging Area. If > 0, inserts -// on the container will block when the capacity is reached. -// If not specified, defaults to 0 +// RandomUniformIntSeed sets the optional seed attribute to value. // -// REQUIRES: value >= 0 -func OrderedMapStageCapacity(value int64) OrderedMapStageAttr { - return func(m optionalAttr) { - m["capacity"] = value - } -} - -// OrderedMapStageMemoryLimit sets the optional memory_limit attribute to value. +// value: If either `seed` or `seed2` are set to be non-zero, the random number +// generator is seeded by the given seed. Otherwise, it is seeded by a +// random seed. // If not specified, defaults to 0 -// -// REQUIRES: value >= 0 -func OrderedMapStageMemoryLimit(value int64) OrderedMapStageAttr { - return func(m optionalAttr) { - m["memory_limit"] = value - } -} - -// OrderedMapStageContainer sets the optional container attribute to value. -// -// value: If non-empty, this queue is placed in the given container. Otherwise, -// a default container is used. -// If not specified, defaults to "" -func OrderedMapStageContainer(value string) OrderedMapStageAttr { - return func(m optionalAttr) { - m["container"] = value - } -} - -// OrderedMapStageSharedName sets the optional shared_name attribute to value. -// -// value: It is necessary to match this name to the matching Unstage Op. -// If not specified, defaults to "" -func OrderedMapStageSharedName(value string) OrderedMapStageAttr { - return func(m optionalAttr) { - m["shared_name"] = value - } -} - -// Stage (key, values) in the underlying container which behaves like a ordered -// -// associative container. Elements are ordered by key. -// -// Arguments: -// key: int64 -// -// values: a list of tensors -// dtypes A list of data types that inserted values should adhere to. -// -// -// Returns the created operation. -func OrderedMapStage(scope *Scope, key tf.Output, indices tf.Output, values []tf.Output, dtypes []tf.DataType, optional ...OrderedMapStageAttr) (o *tf.Operation) { - if scope.Err() != nil { - return - } - attrs := map[string]interface{}{"dtypes": dtypes} - for _, a := range optional { - a(attrs) - } - opspec := tf.OpSpec{ - Type: "OrderedMapStage", - Input: []tf.Input{ - key, indices, tf.OutputList(values), - }, - Attrs: attrs, - } - return scope.AddOperation(opspec) -} - -// RpcAttr is an optional argument to Rpc. -type RpcAttr func(optionalAttr) - -// RpcProtocol sets the optional protocol attribute to value. -// -// value: RPC protocol to use. Empty string means use the default protocol. -// Options include 'grpc'. -// If not specified, defaults to "" -func RpcProtocol(value string) RpcAttr { - return func(m optionalAttr) { - m["protocol"] = value - } -} - -// RpcFailFast sets the optional fail_fast attribute to value. -// -// value: `boolean`. If `true` (default), then failures to connect -// (i.e., the server does not immediately respond) cause an RPC failure. -// If not specified, defaults to true -func RpcFailFast(value bool) RpcAttr { +func RandomUniformIntSeed(value int64) RandomUniformIntAttr { return func(m optionalAttr) { - m["fail_fast"] = value + m["seed"] = value } } -// RpcTimeoutInMs sets the optional timeout_in_ms attribute to value. +// RandomUniformIntSeed2 sets the optional seed2 attribute to value. // -// value: `int`. If `0` (default), then the kernel will run the RPC -// request and only time out if the RPC deadline passes or the session times out. -// If this value is greater than `0`, then the op will raise an exception if -// the RPC takes longer than `timeout_in_ms`. +// value: A second seed to avoid seed collision. // If not specified, defaults to 0 -func RpcTimeoutInMs(value int64) RpcAttr { +func RandomUniformIntSeed2(value int64) RandomUniformIntAttr { return func(m optionalAttr) { - m["timeout_in_ms"] = value + m["seed2"] = value } } -// Perform batches of RPC requests. -// -// This op asynchronously performs either a single RPC request, or a batch -// of requests. RPC requests are defined by three main parameters: -// -// - `address` (the host+port or BNS address of the request) -// - `method` (the RPC method name for the request) -// - `request` (the serialized proto string, or vector of strings, -// of the RPC request argument). -// -// For example, if you have an RPC service running on port localhost:2345, -// and its interface is configured with the following proto declaration: -// -// ``` -// service MyService { -// rpc MyMethod(MyRequestProto) returns (MyResponseProto) { -// } -// }; -// ``` -// -// then call this op with arguments: -// -// ``` -// address = "localhost:2345" -// method = "MyService/MyMethod" -// ``` -// -// The `request` tensor is a string tensor representing serialized `MyRequestProto` -// strings; and the output string tensor `response` will have the same shape -// and contain (upon successful completion) corresponding serialized -// `MyResponseProto` strings. -// -// For example, to send a single, empty, `MyRequestProto`, call -// this op with `request = ""`. To send 5 **parallel** empty requests, -// call this op with `request = ["", "", "", "", ""]`. -// -// More generally, one can create a batch of `MyRequestProto` serialized protos -// from regular batched tensors using the `encode_proto` op, and convert -// the response `MyResponseProto` serialized protos to batched tensors -// using the `decode_proto` op. -// -// **NOTE** Working with serialized proto strings is faster than instantiating -// actual proto objects in memory, so no performance degradation is expected -// compared to writing custom kernels for this workflow. +// Outputs random integers from a uniform distribution. // -// If the connection fails or the remote worker returns an error -// status, the op reraises this exception locally. +// The generated values are uniform integers in the range `[minval, maxval)`. +// The lower bound `minval` is included in the range, while the upper bound +// `maxval` is excluded. // -// See the `TryRpc` op if you prefer to handle RPC failures manually in the graph. +// The random integers are slightly biased unless `maxval - minval` is an exact +// power of two. The bias is small for values of `maxval - minval` significantly +// smaller than the range of the output (either `2^32` or `2^64`). // // Arguments: -// address: `0-D` or `1-D`. The address (i.e. host_name:port) of the RPC server. -// If this tensor has more than 1 element, then multiple parallel rpc requests -// are sent. This argument broadcasts with `method` and `request`. -// method: `0-D` or `1-D`. The method address on the RPC server. -// If this tensor has more than 1 element, then multiple parallel rpc requests -// are sent. This argument broadcasts with `address` and `request`. -// request: `0-D` or `1-D`. Serialized proto strings: the rpc request argument. -// If this tensor has more than 1 element, then multiple parallel rpc requests -// are sent. This argument broadcasts with `address` and `method`. +// shape: The shape of the output tensor. +// minval: 0-D. Inclusive lower bound on the generated integers. +// maxval: 0-D. Exclusive upper bound on the generated integers. // -// Returns Same shape as `request`. Serialized proto strings: the rpc responses. -func Rpc(scope *Scope, address tf.Output, method tf.Output, request tf.Output, optional ...RpcAttr) (response tf.Output) { +// Returns A tensor of the specified shape filled with uniform random integers. +func RandomUniformInt(scope *Scope, shape tf.Output, minval tf.Output, maxval tf.Output, optional ...RandomUniformIntAttr) (output tf.Output) { if scope.Err() != nil { return } @@ -30258,9 +30023,9 @@ func Rpc(scope *Scope, address tf.Output, method tf.Output, request tf.Output, o a(attrs) } opspec := tf.OpSpec{ - Type: "Rpc", + Type: "RandomUniformInt", Input: []tf.Input{ - address, method, request, + shape, minval, maxval, }, Attrs: attrs, } @@ -30268,59 +30033,25 @@ func Rpc(scope *Scope, address tf.Output, method tf.Output, request tf.Output, o return op.Output(0) } -// StackPushV2Attr is an optional argument to StackPushV2. -type StackPushV2Attr func(optionalAttr) - -// StackPushV2SwapMemory sets the optional swap_memory attribute to value. -// -// value: Swap `elem` to CPU. Default to false. -// If not specified, defaults to false -func StackPushV2SwapMemory(value bool) StackPushV2Attr { - return func(m optionalAttr) { - m["swap_memory"] = value - } -} - -// Push an element onto the stack. -// -// Arguments: -// handle: The handle to a stack. -// elem: The tensor to be pushed onto the stack. +// Add the quantile summaries to each quantile stream resource. // -// Returns The same tensor as the input 'elem'. -func StackPushV2(scope *Scope, handle tf.Output, elem tf.Output, optional ...StackPushV2Attr) (output tf.Output) { - if scope.Err() != nil { - return - } - attrs := map[string]interface{}{} - for _, a := range optional { - a(attrs) - } - opspec := tf.OpSpec{ - Type: "StackPushV2", - Input: []tf.Input{ - handle, elem, - }, - Attrs: attrs, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - -// Resets the FunctionBufferingResource. +// An op that adds a list of quantile summaries to a quantile stream resource. Each +// summary Tensor is rank 2, containing summaries (value, weight, min_rank, max_rank) +// for a single feature. // // Arguments: -// function_buffer_resource: The FunctionBufferingResource handle. +// quantile_stream_resource_handle: resource handle referring to a QuantileStreamResource. +// summaries: string; List of Rank 2 Tensor each containing the summaries for a single feature. // // Returns the created operation. -func ExperimentalFunctionBufferingResourceReset(scope *Scope, function_buffer_resource tf.Output) (o *tf.Operation) { +func BoostedTreesQuantileStreamResourceAddSummaries(scope *Scope, quantile_stream_resource_handle tf.Output, summaries []tf.Output) (o *tf.Operation) { if scope.Err() != nil { return } opspec := tf.OpSpec{ - Type: "ExperimentalFunctionBufferingResourceReset", + Type: "BoostedTreesQuantileStreamResourceAddSummaries", Input: []tf.Input{ - function_buffer_resource, + quantile_stream_resource_handle, tf.OutputList(summaries), }, } return scope.AddOperation(opspec) @@ -30615,168 +30346,6 @@ func ConcatenateDataset(scope *Scope, input_dataset tf.Output, another_dataset t return op.Output(0) } -// Adds a value to the current value of a variable. -// -// Any ReadVariableOp with a control dependency on this op is guaranteed to -// see the incremented value or a subsequent newer one. -// -// Arguments: -// resource: handle to the resource in which to store the variable. -// value: the value by which the variable will be incremented. -// -// Returns the created operation. -func AssignAddVariableOp(scope *Scope, resource tf.Output, value tf.Output) (o *tf.Operation) { - if scope.Err() != nil { - return - } - opspec := tf.OpSpec{ - Type: "AssignAddVariableOp", - Input: []tf.Input{ - resource, value, - }, - } - return scope.AddOperation(opspec) -} - -// Records the latency of producing `input_dataset` elements in a StatsAggregator. -func LatencyStatsDataset(scope *Scope, input_dataset tf.Output, tag tf.Output, output_types []tf.DataType, output_shapes []tf.Shape) (handle tf.Output) { - if scope.Err() != nil { - return - } - attrs := map[string]interface{}{"output_types": output_types, "output_shapes": output_shapes} - opspec := tf.OpSpec{ - Type: "LatencyStatsDataset", - Input: []tf.Input{ - input_dataset, tag, - }, - Attrs: attrs, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - -// MapSizeAttr is an optional argument to MapSize. -type MapSizeAttr func(optionalAttr) - -// MapSizeCapacity sets the optional capacity attribute to value. -// If not specified, defaults to 0 -// -// REQUIRES: value >= 0 -func MapSizeCapacity(value int64) MapSizeAttr { - return func(m optionalAttr) { - m["capacity"] = value - } -} - -// MapSizeMemoryLimit sets the optional memory_limit attribute to value. -// If not specified, defaults to 0 -// -// REQUIRES: value >= 0 -func MapSizeMemoryLimit(value int64) MapSizeAttr { - return func(m optionalAttr) { - m["memory_limit"] = value - } -} - -// MapSizeContainer sets the optional container attribute to value. -// If not specified, defaults to "" -func MapSizeContainer(value string) MapSizeAttr { - return func(m optionalAttr) { - m["container"] = value - } -} - -// MapSizeSharedName sets the optional shared_name attribute to value. -// If not specified, defaults to "" -func MapSizeSharedName(value string) MapSizeAttr { - return func(m optionalAttr) { - m["shared_name"] = value - } -} - -// Op returns the number of elements in the underlying container. -func MapSize(scope *Scope, dtypes []tf.DataType, optional ...MapSizeAttr) (size tf.Output) { - if scope.Err() != nil { - return - } - attrs := map[string]interface{}{"dtypes": dtypes} - for _, a := range optional { - a(attrs) - } - opspec := tf.OpSpec{ - Type: "MapSize", - - Attrs: attrs, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - -// SparseToDenseAttr is an optional argument to SparseToDense. -type SparseToDenseAttr func(optionalAttr) - -// SparseToDenseValidateIndices sets the optional validate_indices attribute to value. -// -// value: If true, indices are checked to make sure they are sorted in -// lexicographic order and that there are no repeats. -// If not specified, defaults to true -func SparseToDenseValidateIndices(value bool) SparseToDenseAttr { - return func(m optionalAttr) { - m["validate_indices"] = value - } -} - -// Converts a sparse representation into a dense tensor. -// -// Builds an array `dense` with shape `output_shape` such that -// -// ``` -// # If sparse_indices is scalar -// dense[i] = (i == sparse_indices ? sparse_values : default_value) -// -// # If sparse_indices is a vector, then for each i -// dense[sparse_indices[i]] = sparse_values[i] -// -// # If sparse_indices is an n by d matrix, then for each i in [0, n) -// dense[sparse_indices[i][0], ..., sparse_indices[i][d-1]] = sparse_values[i] -// ``` -// -// All other values in `dense` are set to `default_value`. If `sparse_values` is a -// scalar, all sparse indices are set to this single value. -// -// Indices should be sorted in lexicographic order, and indices must not -// contain any repeats. If `validate_indices` is true, these properties -// are checked during execution. -// -// Arguments: -// sparse_indices: 0-D, 1-D, or 2-D. `sparse_indices[i]` contains the complete -// index where `sparse_values[i]` will be placed. -// output_shape: 1-D. Shape of the dense output tensor. -// sparse_values: 1-D. Values corresponding to each row of `sparse_indices`, -// or a scalar value to be used for all sparse indices. -// default_value: Scalar value to set for indices not specified in -// `sparse_indices`. -// -// Returns Dense output tensor of shape `output_shape`. -func SparseToDense(scope *Scope, sparse_indices tf.Output, output_shape tf.Output, sparse_values tf.Output, default_value tf.Output, optional ...SparseToDenseAttr) (dense tf.Output) { - if scope.Err() != nil { - return - } - attrs := map[string]interface{}{} - for _, a := range optional { - a(attrs) - } - opspec := tf.OpSpec{ - Type: "SparseToDense", - Input: []tf.Input{ - sparse_indices, output_shape, sparse_values, default_value, - }, - Attrs: attrs, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - // Computes the grayscale dilation of 4-D `input` and 3-D `filter` tensors. // // The `input` tensor has shape `[batch, in_height, in_width, depth]` and the @@ -30910,52 +30479,6 @@ func PaddedBatchDataset(scope *Scope, input_dataset tf.Output, batch_size tf.Out return op.Output(0) } -// Creates a dataset that batches input elements into a SparseTensor. -// -// Arguments: -// input_dataset: A handle to an input dataset. Must have a single component. -// batch_size: A scalar representing the number of elements to accumulate in a -// batch. -// row_shape: A vector representing the dense shape of each row in the produced -// SparseTensor. The shape may be partially specified, using `-1` to indicate -// that a particular dimension should use the maximum size of all batch elements. -// -// -func DenseToSparseBatchDataset(scope *Scope, input_dataset tf.Output, batch_size tf.Output, row_shape tf.Output, output_types []tf.DataType, output_shapes []tf.Shape) (handle tf.Output) { - if scope.Err() != nil { - return - } - attrs := map[string]interface{}{"output_types": output_types, "output_shapes": output_shapes} - opspec := tf.OpSpec{ - Type: "DenseToSparseBatchDataset", - Input: []tf.Input{ - input_dataset, batch_size, row_shape, - }, - Attrs: attrs, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - -// Deprecated. Use TensorArrayGradV3 -// -// DEPRECATED at GraphDef version 26: Use TensorArrayGradV3 -func TensorArrayGradV2(scope *Scope, handle tf.Output, flow_in tf.Output, source string) (grad_handle tf.Output) { - if scope.Err() != nil { - return - } - attrs := map[string]interface{}{"source": source} - opspec := tf.OpSpec{ - Type: "TensorArrayGradV2", - Input: []tf.Input{ - handle, flow_in, - }, - Attrs: attrs, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - // Creates a dataset that shuffles and repeats elements from `input_dataset` // // pseudorandomly. @@ -31561,46 +31084,6 @@ func FIFOQueueV2(scope *Scope, component_types []tf.DataType, optional ...FIFOQu return op.Output(0) } -// Produces a summary of any statistics recorded by the given statistics manager. -func StatsAggregatorSummary(scope *Scope, iterator tf.Output) (summary tf.Output) { - if scope.Err() != nil { - return - } - opspec := tf.OpSpec{ - Type: "StatsAggregatorSummary", - Input: []tf.Input{ - iterator, - }, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - -// Compute the pairwise cross product. -// -// `a` and `b` must be the same shape; they can either be simple 3-element vectors, -// or any shape where the innermost dimension is 3. In the latter case, each pair -// of corresponding 3-element vectors is cross-multiplied independently. -// -// Arguments: -// a: A tensor containing 3-element vectors. -// b: Another tensor, of same type and shape as `a`. -// -// Returns Pairwise cross product of the vectors in `a` and `b`. -func Cross(scope *Scope, a tf.Output, b tf.Output) (product tf.Output) { - if scope.Err() != nil { - return - } - opspec := tf.OpSpec{ - Type: "Cross", - Input: []tf.Input{ - a, b, - }, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - // Constructs an Optional variant from a tuple of tensors. func OptionalFromValue(scope *Scope, components []tf.Output) (optional tf.Output) { if scope.Err() != nil { @@ -31769,30 +31252,6 @@ func OptionalHasValue(scope *Scope, optional tf.Output) (has_value tf.Output) { return op.Output(0) } -// Creates a dataset that executes a SQL query and emits rows of the result set. -// -// Arguments: -// driver_name: The database type. Currently, the only supported type is 'sqlite'. -// data_source_name: A connection string to connect to the database. -// query: A SQL query to execute. -// -// -func SqlDataset(scope *Scope, driver_name tf.Output, data_source_name tf.Output, query tf.Output, output_types []tf.DataType, output_shapes []tf.Shape) (handle tf.Output) { - if scope.Err() != nil { - return - } - attrs := map[string]interface{}{"output_types": output_types, "output_shapes": output_shapes} - opspec := tf.OpSpec{ - Type: "SqlDataset", - Input: []tf.Input{ - driver_name, data_source_name, query, - }, - Attrs: attrs, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - // Returns the value stored in an Optional variant or raises an error if none exists. func OptionalGetValue(scope *Scope, optional tf.Output, output_types []tf.DataType, output_shapes []tf.Shape) (components []tf.Output) { if scope.Err() != nil { @@ -32208,46 +31667,6 @@ func ParallelDynamicStitch(scope *Scope, indices []tf.Output, data []tf.Output) return op.Output(0) } -// Computes the gradient for the inverse of `x` wrt its input. -// -// Specifically, `grad = -dy * y*y`, where `y = 1/x`, and `dy` -// is the corresponding input gradient. -func InvGrad(scope *Scope, y tf.Output, dy tf.Output) (z tf.Output) { - if scope.Err() != nil { - return - } - opspec := tf.OpSpec{ - Type: "InvGrad", - Input: []tf.Input{ - y, dy, - }, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - -// List of the given size with empty elements. -// -// element_shape: the shape of the future elements of the list -// num_elements: the number of elements to reserve -// handle: the output list -// element_dtype: the desired type of elements in the list. -func TensorListReserve(scope *Scope, element_shape tf.Output, num_elements tf.Output, element_dtype tf.DataType) (handle tf.Output) { - if scope.Err() != nil { - return - } - attrs := map[string]interface{}{"element_dtype": element_dtype} - opspec := tf.OpSpec{ - Type: "TensorListReserve", - Input: []tf.Input{ - element_shape, num_elements, - }, - Attrs: attrs, - } - op := scope.AddOperation(opspec) - return op.Output(0) -} - // PriorityQueueV2Attr is an optional argument to PriorityQueueV2. type PriorityQueueV2Attr func(optionalAttr) @@ -32786,6 +32205,241 @@ func StackV2(scope *Scope, max_size tf.Output, elem_type tf.DataType, optional . return op.Output(0) } +// OrderedMapStageAttr is an optional argument to OrderedMapStage. +type OrderedMapStageAttr func(optionalAttr) + +// OrderedMapStageCapacity sets the optional capacity attribute to value. +// +// value: Maximum number of elements in the Staging Area. If > 0, inserts +// on the container will block when the capacity is reached. +// If not specified, defaults to 0 +// +// REQUIRES: value >= 0 +func OrderedMapStageCapacity(value int64) OrderedMapStageAttr { + return func(m optionalAttr) { + m["capacity"] = value + } +} + +// OrderedMapStageMemoryLimit sets the optional memory_limit attribute to value. +// If not specified, defaults to 0 +// +// REQUIRES: value >= 0 +func OrderedMapStageMemoryLimit(value int64) OrderedMapStageAttr { + return func(m optionalAttr) { + m["memory_limit"] = value + } +} + +// OrderedMapStageContainer sets the optional container attribute to value. +// +// value: If non-empty, this queue is placed in the given container. Otherwise, +// a default container is used. +// If not specified, defaults to "" +func OrderedMapStageContainer(value string) OrderedMapStageAttr { + return func(m optionalAttr) { + m["container"] = value + } +} + +// OrderedMapStageSharedName sets the optional shared_name attribute to value. +// +// value: It is necessary to match this name to the matching Unstage Op. +// If not specified, defaults to "" +func OrderedMapStageSharedName(value string) OrderedMapStageAttr { + return func(m optionalAttr) { + m["shared_name"] = value + } +} + +// Stage (key, values) in the underlying container which behaves like a ordered +// +// associative container. Elements are ordered by key. +// +// Arguments: +// key: int64 +// +// values: a list of tensors +// dtypes A list of data types that inserted values should adhere to. +// +// +// Returns the created operation. +func OrderedMapStage(scope *Scope, key tf.Output, indices tf.Output, values []tf.Output, dtypes []tf.DataType, optional ...OrderedMapStageAttr) (o *tf.Operation) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{"dtypes": dtypes} + for _, a := range optional { + a(attrs) + } + opspec := tf.OpSpec{ + Type: "OrderedMapStage", + Input: []tf.Input{ + key, indices, tf.OutputList(values), + }, + Attrs: attrs, + } + return scope.AddOperation(opspec) +} + +// RpcAttr is an optional argument to Rpc. +type RpcAttr func(optionalAttr) + +// RpcProtocol sets the optional protocol attribute to value. +// +// value: RPC protocol to use. Empty string means use the default protocol. +// Options include 'grpc'. +// If not specified, defaults to "" +func RpcProtocol(value string) RpcAttr { + return func(m optionalAttr) { + m["protocol"] = value + } +} + +// RpcFailFast sets the optional fail_fast attribute to value. +// +// value: `boolean`. If `true` (default), then failures to connect +// (i.e., the server does not immediately respond) cause an RPC failure. +// If not specified, defaults to true +func RpcFailFast(value bool) RpcAttr { + return func(m optionalAttr) { + m["fail_fast"] = value + } +} + +// RpcTimeoutInMs sets the optional timeout_in_ms attribute to value. +// +// value: `int`. If `0` (default), then the kernel will run the RPC +// request and only time out if the RPC deadline passes or the session times out. +// If this value is greater than `0`, then the op will raise an exception if +// the RPC takes longer than `timeout_in_ms`. +// If not specified, defaults to 0 +func RpcTimeoutInMs(value int64) RpcAttr { + return func(m optionalAttr) { + m["timeout_in_ms"] = value + } +} + +// Perform batches of RPC requests. +// +// This op asynchronously performs either a single RPC request, or a batch +// of requests. RPC requests are defined by three main parameters: +// +// - `address` (the host+port or BNS address of the request) +// - `method` (the RPC method name for the request) +// - `request` (the serialized proto string, or vector of strings, +// of the RPC request argument). +// +// For example, if you have an RPC service running on port localhost:2345, +// and its interface is configured with the following proto declaration: +// +// ``` +// service MyService { +// rpc MyMethod(MyRequestProto) returns (MyResponseProto) { +// } +// }; +// ``` +// +// then call this op with arguments: +// +// ``` +// address = "localhost:2345" +// method = "MyService/MyMethod" +// ``` +// +// The `request` tensor is a string tensor representing serialized `MyRequestProto` +// strings; and the output string tensor `response` will have the same shape +// and contain (upon successful completion) corresponding serialized +// `MyResponseProto` strings. +// +// For example, to send a single, empty, `MyRequestProto`, call +// this op with `request = ""`. To send 5 **parallel** empty requests, +// call this op with `request = ["", "", "", "", ""]`. +// +// More generally, one can create a batch of `MyRequestProto` serialized protos +// from regular batched tensors using the `encode_proto` op, and convert +// the response `MyResponseProto` serialized protos to batched tensors +// using the `decode_proto` op. +// +// **NOTE** Working with serialized proto strings is faster than instantiating +// actual proto objects in memory, so no performance degradation is expected +// compared to writing custom kernels for this workflow. +// +// If the connection fails or the remote worker returns an error +// status, the op reraises this exception locally. +// +// See the `TryRpc` op if you prefer to handle RPC failures manually in the graph. +// +// Arguments: +// address: `0-D` or `1-D`. The address (i.e. host_name:port) of the RPC server. +// If this tensor has more than 1 element, then multiple parallel rpc requests +// are sent. This argument broadcasts with `method` and `request`. +// method: `0-D` or `1-D`. The method address on the RPC server. +// If this tensor has more than 1 element, then multiple parallel rpc requests +// are sent. This argument broadcasts with `address` and `request`. +// request: `0-D` or `1-D`. Serialized proto strings: the rpc request argument. +// If this tensor has more than 1 element, then multiple parallel rpc requests +// are sent. This argument broadcasts with `address` and `method`. +// +// Returns Same shape as `request`. Serialized proto strings: the rpc responses. +func Rpc(scope *Scope, address tf.Output, method tf.Output, request tf.Output, optional ...RpcAttr) (response tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{} + for _, a := range optional { + a(attrs) + } + opspec := tf.OpSpec{ + Type: "Rpc", + Input: []tf.Input{ + address, method, request, + }, + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + +// StackPushV2Attr is an optional argument to StackPushV2. +type StackPushV2Attr func(optionalAttr) + +// StackPushV2SwapMemory sets the optional swap_memory attribute to value. +// +// value: Swap `elem` to CPU. Default to false. +// If not specified, defaults to false +func StackPushV2SwapMemory(value bool) StackPushV2Attr { + return func(m optionalAttr) { + m["swap_memory"] = value + } +} + +// Push an element onto the stack. +// +// Arguments: +// handle: The handle to a stack. +// elem: The tensor to be pushed onto the stack. +// +// Returns The same tensor as the input 'elem'. +func StackPushV2(scope *Scope, handle tf.Output, elem tf.Output, optional ...StackPushV2Attr) (output tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{} + for _, a := range optional { + a(attrs) + } + opspec := tf.OpSpec{ + Type: "StackPushV2", + Input: []tf.Input{ + handle, elem, + }, + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // FusedBatchNormGradV2Attr is an optional argument to FusedBatchNormGradV2. type FusedBatchNormGradV2Attr func(optionalAttr) @@ -33169,6 +32823,25 @@ func TensorArraySizeV3(scope *Scope, handle tf.Output, flow_in tf.Output) (size return op.Output(0) } +// Deprecated. Use TensorArrayGradV3 +// +// DEPRECATED at GraphDef version 26: Use TensorArrayGradV3 +func TensorArrayGradV2(scope *Scope, handle tf.Output, flow_in tf.Output, source string) (grad_handle tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{"source": source} + opspec := tf.OpSpec{ + Type: "TensorArrayGradV2", + Input: []tf.Input{ + handle, flow_in, + }, + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // SparseReduceMaxAttr is an optional argument to SparseReduceMax. type SparseReduceMaxAttr func(optionalAttr) @@ -33941,47 +33614,47 @@ func MapUnstage(scope *Scope, key tf.Output, indices tf.Output, dtypes []tf.Data return values } -// MapIncompleteSizeAttr is an optional argument to MapIncompleteSize. -type MapIncompleteSizeAttr func(optionalAttr) +// MapSizeAttr is an optional argument to MapSize. +type MapSizeAttr func(optionalAttr) -// MapIncompleteSizeCapacity sets the optional capacity attribute to value. +// MapSizeCapacity sets the optional capacity attribute to value. // If not specified, defaults to 0 // // REQUIRES: value >= 0 -func MapIncompleteSizeCapacity(value int64) MapIncompleteSizeAttr { +func MapSizeCapacity(value int64) MapSizeAttr { return func(m optionalAttr) { m["capacity"] = value } } -// MapIncompleteSizeMemoryLimit sets the optional memory_limit attribute to value. +// MapSizeMemoryLimit sets the optional memory_limit attribute to value. // If not specified, defaults to 0 // // REQUIRES: value >= 0 -func MapIncompleteSizeMemoryLimit(value int64) MapIncompleteSizeAttr { +func MapSizeMemoryLimit(value int64) MapSizeAttr { return func(m optionalAttr) { m["memory_limit"] = value } } -// MapIncompleteSizeContainer sets the optional container attribute to value. +// MapSizeContainer sets the optional container attribute to value. // If not specified, defaults to "" -func MapIncompleteSizeContainer(value string) MapIncompleteSizeAttr { +func MapSizeContainer(value string) MapSizeAttr { return func(m optionalAttr) { m["container"] = value } } -// MapIncompleteSizeSharedName sets the optional shared_name attribute to value. +// MapSizeSharedName sets the optional shared_name attribute to value. // If not specified, defaults to "" -func MapIncompleteSizeSharedName(value string) MapIncompleteSizeAttr { +func MapSizeSharedName(value string) MapSizeAttr { return func(m optionalAttr) { m["shared_name"] = value } } -// Op returns the number of incomplete elements in the underlying container. -func MapIncompleteSize(scope *Scope, dtypes []tf.DataType, optional ...MapIncompleteSizeAttr) (size tf.Output) { +// Op returns the number of elements in the underlying container. +func MapSize(scope *Scope, dtypes []tf.DataType, optional ...MapSizeAttr) (size tf.Output) { if scope.Err() != nil { return } @@ -33990,7 +33663,7 @@ func MapIncompleteSize(scope *Scope, dtypes []tf.DataType, optional ...MapIncomp a(attrs) } opspec := tf.OpSpec{ - Type: "MapIncompleteSize", + Type: "MapSize", Attrs: attrs, } diff --git a/tensorflow/java/README.md b/tensorflow/java/README.md index 2fa81ed88f..6415290745 100644 --- a/tensorflow/java/README.md +++ b/tensorflow/java/README.md @@ -1,7 +1,7 @@ # TensorFlow for Java > *WARNING*: The TensorFlow Java API is not currently covered by the TensorFlow -> [API stability guarantees](https://www.tensorflow.org/guide/version_semantics). +> [API stability guarantees](https://www.tensorflow.org/guide/version_compat). > > For using TensorFlow on Android refer instead to > [contrib/android](https://www.tensorflow.org/code/tensorflow/contrib/android), diff --git a/tensorflow/java/src/test/java/org/tensorflow/TensorTest.java b/tensorflow/java/src/test/java/org/tensorflow/TensorTest.java index 1bd00a763d..3229cce277 100644 --- a/tensorflow/java/src/test/java/org/tensorflow/TensorTest.java +++ b/tensorflow/java/src/test/java/org/tensorflow/TensorTest.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.DoubleBuffer; @@ -100,7 +101,7 @@ public class TensorTest { : ByteOrder.LITTLE_ENDIAN) .asDoubleBuffer() .put(doubles); - buf.flip(); + flipBuffer(buf); try (Tensor t = Tensor.create(new long[] {doubles.length}, buf)) { double[] actual = new double[doubles.length]; assertArrayEquals(doubles, t.copyTo(actual), EPSILON); @@ -179,30 +180,30 @@ public class TensorTest { { ByteBuffer bbuf = ByteBuffer.allocate(1024).order(ByteOrder.nativeOrder()); - bbuf.clear(); // FLOAT + clearBuffer(bbuf); // FLOAT tfloats.writeTo(bbuf); assertEquals(tfloats.numBytes(), bbuf.position()); - bbuf.flip(); + flipBuffer(bbuf); assertEquals(floats[0], bbuf.asFloatBuffer().get(0), EPSILON); - bbuf.clear(); // DOUBLE + clearBuffer(bbuf); // DOUBLE tdoubles.writeTo(bbuf); assertEquals(tdoubles.numBytes(), bbuf.position()); - bbuf.flip(); + flipBuffer(bbuf); assertEquals(doubles[0], bbuf.asDoubleBuffer().get(0), EPSILON); - bbuf.clear(); // INT32 + clearBuffer(bbuf); // INT32 tints.writeTo(bbuf); assertEquals(tints.numBytes(), bbuf.position()); - bbuf.flip(); + flipBuffer(bbuf); assertEquals(ints[0], bbuf.asIntBuffer().get(0)); - bbuf.clear(); // INT64 + clearBuffer(bbuf); // INT64 tlongs.writeTo(bbuf); assertEquals(tlongs.numBytes(), bbuf.position()); - bbuf.flip(); + flipBuffer(bbuf); assertEquals(longs[0], bbuf.asLongBuffer().get(0)); - bbuf.clear(); // BOOL + clearBuffer(bbuf); // BOOL tbools.writeTo(bbuf); assertEquals(tbools.numBytes(), bbuf.position()); - bbuf.flip(); + flipBuffer(bbuf); assertEquals(bools[0], bbuf.get(0) != 0); } @@ -254,7 +255,7 @@ public class TensorTest { : ByteOrder.LITTLE_ENDIAN) .asDoubleBuffer(); tdoubles.writeTo(foreignBuf); - foreignBuf.flip(); + flipBuffer(foreignBuf); double[] actual = new double[foreignBuf.remaining()]; foreignBuf.get(actual); assertArrayEquals(doubles, actual, EPSILON); @@ -547,4 +548,25 @@ public class TensorTest { // expected. } } + + // Workaround for cross compiliation + // (e.g., javac -source 1.9 -target 1.8). + // + // In Java 8 and prior, subclasses of java.nio.Buffer (e.g., java.nio.DoubleBuffer) inherited the + // "flip()" and "clear()" methods from java.nio.Buffer resulting in the signature: + // Buffer flip(); + // In Java 9 these subclasses had their own methods like: + // DoubleBuffer flip(); + // As a result, compiling for 1.9 source for a target of JDK 1.8 would result in errors at runtime + // like: + // + // java.lang.NoSuchMethodError: java.nio.DoubleBuffer.flip()Ljava/nio/DoubleBuffer + private static void flipBuffer(Buffer buf) { + buf.flip(); + } + + // See comment for flipBuffer() + private static void clearBuffer(Buffer buf) { + buf.clear(); + } } diff --git a/tensorflow/lite/BUILD b/tensorflow/lite/BUILD index be84fc5db1..bb2c53b8c9 100644 --- a/tensorflow/lite/BUILD +++ b/tensorflow/lite/BUILD @@ -131,6 +131,7 @@ cc_library( name = "framework", srcs = [ "allocation.cc", + "core/subgraph.cc", "graph_info.cc", "interpreter.cc", "model.cc", @@ -155,6 +156,7 @@ cc_library( "allocation.h", "context.h", "context_util.h", + "core/subgraph.h", "error_reporter.h", "graph_info.h", "interpreter.h", diff --git a/tensorflow/lite/build_def.bzl b/tensorflow/lite/build_def.bzl index bc98dc57bc..5eaf719494 100644 --- a/tensorflow/lite/build_def.bzl +++ b/tensorflow/lite/build_def.bzl @@ -221,6 +221,7 @@ def json_to_tflite(name, src, out): # generated_test_models_failing(). def generated_test_models(): return [ + "abs", "add", "arg_min_max", "avg_pool", @@ -236,18 +237,21 @@ def generated_test_models(): "equal", "exp", "expand_dims", + "fill", "floor", "floor_div", "floor_mod", "fully_connected", "fused_batch_norm", "gather", + "gather_buggy", "global_batch_norm", "greater", "greater_equal", "sum", "l2norm", "l2_pool", + "leaky_relu", "less", "less_equal", "local_response_norm", @@ -261,6 +265,7 @@ def generated_test_models(): "maximum", "mean", "minimum", + "mirror_pad", "mul", "neg", "not_equal", @@ -268,6 +273,7 @@ def generated_test_models(): "pack", "pad", "padv2", + "placeholder_with_default", "prelu", "pow", "range", @@ -290,17 +296,21 @@ def generated_test_models(): "space_to_depth", "sparse_to_dense", "split", + "splitv", "sqrt", "square", + "squared_difference", "squeeze", "strided_slice", "strided_slice_1d_exhaustive", + "strided_slice_buggy", "sub", "tile", "topk", "transpose", "transpose_conv", "unpack", + "unroll_batch_matmul", "where", "zeros_like", ] diff --git a/tensorflow/lite/builtin_ops.h b/tensorflow/lite/builtin_ops.h index b8c05f57bb..f97d3ac4bf 100644 --- a/tensorflow/lite/builtin_ops.h +++ b/tensorflow/lite/builtin_ops.h @@ -123,6 +123,11 @@ typedef enum { kTfLiteBuiltinFloorMod = 95, kTfLiteBuiltinRange = 96, kTfLiteBuiltinResizeNearestNeighbor = 97, + kTfLiteBuiltinLeakyRelu = 98, + kTfLiteBuiltinSquaredDifference = 99, + kTfLiteBuiltinMirrorPad = 100, + kTfLiteBuiltinAbs = 101, + kTfLiteBuiltinSplitV = 102, } TfLiteBuiltinOperator; #ifdef __cplusplus diff --git a/tensorflow/lite/c/builtin_op_data.h b/tensorflow/lite/c/builtin_op_data.h index 855983d60d..6a5a027a9d 100644 --- a/tensorflow/lite/c/builtin_op_data.h +++ b/tensorflow/lite/c/builtin_op_data.h @@ -35,11 +35,21 @@ typedef enum { kTfLitePaddingValid, } TfLitePadding; +typedef enum { + kTfLiteMirrorPaddingUnknown = 0, + kTfLiteMirrorPaddingReflect, + kTfLiteMirrorPaddingSymmetric, +} TfLiteMirrorPaddingMode; + typedef struct { int width; int height; } TfLitePaddingValues; +typedef struct { + TfLiteMirrorPaddingMode mode; +} TfLiteMirrorPaddingParams; + // Possible fused activation functions. // TODO(aselle): rename to TfLiteActivation typedef enum { @@ -267,6 +277,10 @@ typedef struct { int num_splits; } TfLiteSplitParams; +typedef struct { + int num_splits; +} TfLiteSplitVParams; + typedef struct { // TODO(ahentz): We can't have dynamic data in this struct, at least not yet. // For now we will fix the maximum possible number of dimensions. @@ -328,6 +342,10 @@ typedef struct { int axis; } TfLiteUnpackParams; +typedef struct { + float alpha; +} TfLiteLeakyReluParams; + #ifdef __cplusplus } // extern "C" #endif // __cplusplus diff --git a/tensorflow/lite/c/builtin_op_data_test.cc b/tensorflow/lite/c/builtin_op_data_test.cc index 0e33dcd8c8..4ce7c481e1 100644 --- a/tensorflow/lite/c/builtin_op_data_test.cc +++ b/tensorflow/lite/c/builtin_op_data_test.cc @@ -63,6 +63,7 @@ TEST(IntArray, CanCompileStructs) { TfLiteTransposeParams transpose_params; TfLiteReducerParams reducer_params; TfLiteSplitParams split_params; + TfLiteSplitVParams split_v_params; TfLiteSqueezeParams squeeze_params; TfLiteStridedSliceParams strided_slice_params; TfLiteArgMaxParams arg_max_params; diff --git a/tensorflow/lite/c/c_api_internal.c b/tensorflow/lite/c/c_api_internal.c index b131f06774..2923dbad4e 100644 --- a/tensorflow/lite/c/c_api_internal.c +++ b/tensorflow/lite/c/c_api_internal.c @@ -59,7 +59,7 @@ void TfLiteIntArrayPrint(const char* s, TfLiteIntArray* a) { printf("]\n"); } -TfLiteIntArray* TfLiteIntArrayCopy(TfLiteIntArray* src) { +TfLiteIntArray* TfLiteIntArrayCopy(const TfLiteIntArray* src) { if (!src) return NULL; TfLiteIntArray* ret = TfLiteIntArrayCreate(src->size); if (ret) { @@ -125,6 +125,8 @@ const char* TfLiteTypeGetName(TfLiteType type) { return "INT32"; case kTfLiteUInt8: return "UINT8"; + case kTfLiteInt8: + return "INT8"; case kTfLiteInt64: return "INT64"; case kTfLiteBool: @@ -137,3 +139,14 @@ const char* TfLiteTypeGetName(TfLiteType type) { return "Unknown type"; } +TfLiteDelegate TfLiteDelegateCreate() { + TfLiteDelegate d = { + .data_ = NULL, + .Prepare = NULL, + .CopyFromBufferHandle = NULL, + .CopyToBufferHandle = NULL, + .FreeBufferHandle = NULL, + .flags = kTfLiteDelegateFlagsNone, + }; + return d; +} diff --git a/tensorflow/lite/c/c_api_internal.h b/tensorflow/lite/c/c_api_internal.h index e05fd19936..1cd84eff5c 100644 --- a/tensorflow/lite/c/c_api_internal.h +++ b/tensorflow/lite/c/c_api_internal.h @@ -96,7 +96,7 @@ int TfLiteIntArrayEqualsArray(TfLiteIntArray* a, int b_size, int b_data[]); // Create a copy of an array passed as `src`. // You are expected to free memory with TfLiteIntArrayFree -TfLiteIntArray* TfLiteIntArrayCopy(TfLiteIntArray* src); +TfLiteIntArray* TfLiteIntArrayCopy(const TfLiteIntArray* src); // Free memory of array `v`. void TfLiteIntArrayFree(TfLiteIntArray* v); @@ -179,6 +179,7 @@ typedef enum { kTfLiteBool = 6, kTfLiteInt16 = 7, kTfLiteComplex64 = 8, + kTfLiteInt8 = 9, } TfLiteType; // Return the name of a given type, for error reporting purposes. @@ -203,6 +204,7 @@ typedef union { bool* b; int16_t* i16; TfLiteComplex64* c64; + int8_t* int8; } TfLitePtrUnion; // Memory allocation strategies. kTfLiteMmapRo is for read-only memory-mapped @@ -486,19 +488,20 @@ typedef struct _TfLiteDelegate { // delegated subgraphs of the original graph. TfLiteStatus (*Prepare)(TfLiteContext* context, TfLiteDelegate* delegate); - // Copy the data from delegate buffer handle to raw memory. - // This can be null if the delegate doesn't use its own buffer. + // Copy the data from delegate buffer handle into raw memory of the given + // 'tensor'. This cannot be null. The delegate is allowed to allocate the raw + // bytes as long as it follows the rules for kTfLiteDynamic tensors. TfLiteStatus (*CopyFromBufferHandle)(TfLiteContext* context, TfLiteDelegate* delegate, TfLiteBufferHandle buffer_handle, - void* data, size_t size); + TfLiteTensor* tensor); - // Copy the data from raw memory to delegate buffer handle. - // This can be null if the delegate doesn't use its own buffer. + // Copy the data from raw memory of the given 'tensor' to delegate buffer + // handle. This can be null if the delegate doesn't use its own buffer. TfLiteStatus (*CopyToBufferHandle)(TfLiteContext* context, TfLiteDelegate* delegate, TfLiteBufferHandle buffer_handle, - void* data, size_t size); + TfLiteTensor* tensor); // Free the Delegate Buffer Handle. Note: This only frees the handle, but // this doesn't release the underlying resource (e.g. textures). The @@ -511,6 +514,10 @@ typedef struct _TfLiteDelegate { int64_t flags; } TfLiteDelegate; +// Build a 'null' delegate, with all the fields properly set to their default +// values. +TfLiteDelegate TfLiteDelegateCreate(); + // WARNING: This is an experimental interface that is subject to change. // // Currently, TfLiteDelegateParams has to be allocated in a way that it's diff --git a/tensorflow/lite/c/c_api_internal_test.cc b/tensorflow/lite/c/c_api_internal_test.cc index e21823c41f..acf0dfc5be 100644 --- a/tensorflow/lite/c/c_api_internal_test.cc +++ b/tensorflow/lite/c/c_api_internal_test.cc @@ -74,6 +74,7 @@ TEST(Types, TestTypeNames) { EXPECT_EQ(type_name(kTfLiteInt16), "INT16"); EXPECT_EQ(type_name(kTfLiteInt32), "INT32"); EXPECT_EQ(type_name(kTfLiteUInt8), "UINT8"); + EXPECT_EQ(type_name(kTfLiteInt8), "INT8"); EXPECT_EQ(type_name(kTfLiteInt64), "INT64"); EXPECT_EQ(type_name(kTfLiteBool), "BOOL"); EXPECT_EQ(type_name(kTfLiteComplex64), "COMPLEX64"); diff --git a/tensorflow/lite/core/api/flatbuffer_conversions.cc b/tensorflow/lite/core/api/flatbuffer_conversions.cc index 8cd3faabb7..c00a0a3a54 100644 --- a/tensorflow/lite/core/api/flatbuffer_conversions.cc +++ b/tensorflow/lite/core/api/flatbuffer_conversions.cc @@ -61,6 +61,9 @@ TfLiteStatus ConvertTensorType(TensorType tensor_type, TfLiteType* type, case TensorType_UINT8: *type = kTfLiteUInt8; break; + case TensorType_INT8: + *type = kTfLiteInt8; + break; case TensorType_INT64: *type = kTfLiteInt64; break; @@ -503,6 +506,14 @@ TfLiteStatus ParseOpData(const Operator* op, BuiltinOperator op_type, *builtin_data = reinterpret_cast(params); break; } + case BuiltinOperator_SPLIT_V: { + auto* params = allocator->AllocatePOD(); + if (auto* schema_params = op->builtin_options_as_SplitVOptions()) { + params->num_splits = schema_params->num_splits(); + } + *builtin_data = reinterpret_cast(params); + break; + } case BuiltinOperator_SQUEEZE: { auto* params = allocator->AllocatePOD(); if (auto* schema_params = op->builtin_options_as_SqueezeOptions()) { @@ -617,8 +628,31 @@ TfLiteStatus ParseOpData(const Operator* op, BuiltinOperator op_type, *builtin_data = reinterpret_cast(params); break; } + case BuiltinOperator_LEAKY_RELU: { + TfLiteLeakyReluParams* params = + allocator->AllocatePOD(); + if (auto* leaky_relu_params = op->builtin_options_as_LeakyReluOptions()) { + params->alpha = leaky_relu_params->alpha(); + } + *builtin_data = reinterpret_cast(params); + break; + } + case BuiltinOperator_MIRROR_PAD: { + TfLiteMirrorPaddingParams* params = + allocator->AllocatePOD(); + auto* mirror_pad_params = op->builtin_options_as_MirrorPadOptions(); + if (mirror_pad_params != nullptr) { + params->mode = + mirror_pad_params->mode() == tflite::MirrorPadMode_REFLECT + ? TfLiteMirrorPaddingMode::kTfLiteMirrorPaddingReflect + : TfLiteMirrorPaddingMode::kTfLiteMirrorPaddingSymmetric; + } + *builtin_data = reinterpret_cast(params); + break; + } // Below are the ops with no builtin_data strcture. + case BuiltinOperator_ABS: case BuiltinOperator_BATCH_TO_SPACE_ND: // TODO(aselle): Implement call in BuiltinOptions, but nullptrs are // ok for now, since there is no call implementation either. @@ -668,6 +702,7 @@ TfLiteStatus ParseOpData(const Operator* op, BuiltinOperator op_type, case BuiltinOperator_FILL: case BuiltinOperator_FLOOR_MOD: case BuiltinOperator_RANGE: + case BuiltinOperator_SQUARED_DIFFERENCE: break; } return kTfLiteOk; diff --git a/tensorflow/lite/core/subgraph.cc b/tensorflow/lite/core/subgraph.cc new file mode 100644 index 0000000000..90361faeae --- /dev/null +++ b/tensorflow/lite/core/subgraph.cc @@ -0,0 +1,970 @@ +/* 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/lite/core/subgraph.h" +#include "tensorflow/lite/arena_planner.h" +#include "tensorflow/lite/c/c_api_internal.h" +#include "tensorflow/lite/context_util.h" +#include "tensorflow/lite/graph_info.h" +#include "tensorflow/lite/nnapi_delegate.h" +#include "tensorflow/lite/schema/schema_generated.h" + +namespace tflite { + +namespace { +TfLiteStatus ReportOpError(TfLiteContext* context, const TfLiteNode& node, + const TfLiteRegistration& registration, + int node_index, const char* message) { + context->ReportError( + context, "Node number %d (%s) %s.\n", node_index, + registration.custom_name + ? registration.custom_name + : EnumNameBuiltinOperator( + static_cast(registration.builtin_code)), + message); + return kTfLiteError; +} + +// Stub method which returns kTfLiteError when the function is forbidden. +// We're registrating this function to several different function to save +// compiled binary size. Please note the restrictions: +// * The type of first parameter have to be `TfLiteContext*`. +// * All paramteters must be trivailly destructible. (E.g. No C++ class) +TfLiteStatus ForbiddenContextFunction(TfLiteContext* context, ...) { + context->ReportError(context, + "The function is forbidden if not calling in delegate."); + return kTfLiteError; +} + +// Set the ForbiddenContextFunction to a compatible function pointer. +template +void SetForbiddenContextFunction(FunctionType* func) { + *func = reinterpret_cast(ForbiddenContextFunction); +} + +// Returns true if at least one tensor in the given list is kTfLiteDynamic. +template +bool HasDynamicTensorImpl(const TfLiteContext& context, + const TensorIntArray& int_array) { + for (int i : int_array) { + const TfLiteTensor& tensor = context.tensors[i]; + if (tensor.allocation_type == kTfLiteDynamic) { + return true; + } + } + return false; +} + +bool HasDynamicTensor(const TfLiteContext& context, + const TfLiteIntArray* int_array) { + return HasDynamicTensorImpl(context, TfLiteIntArrayView{int_array}); +} + +} // namespace + +// A trivial implementation of GraphInfo around the Interpreter. +// NOTE: this interpreter info represents the subset of the +// graph that is executed according to execution plan. Thus, +// the indices are execution plan indices rather than raw node +// indices. +class InterpreterInfo : public GraphInfo { + public: + explicit InterpreterInfo(Subgraph* subgraph) : subgraph_(subgraph) {} + + size_t num_tensors() const override { return subgraph_->tensors().size(); } + TfLiteTensor* tensor(size_t index) override { + return &subgraph_->tensors()[index]; + } + size_t num_nodes() const override { + return subgraph_->execution_plan().size(); + } + const TfLiteNode& node(size_t index) const override { + int node_index = subgraph_->execution_plan()[index]; + return subgraph_->nodes_and_registration()[node_index].first; + } + const std::vector& inputs() const override { + return subgraph_->inputs(); + } + const std::vector& outputs() const override { + return subgraph_->outputs(); + } + const std::vector& variables() const override { + return subgraph_->variables(); + } + + public: + Subgraph* subgraph_; +}; + +Subgraph::Subgraph(ErrorReporter* error_reporter, + TfLiteExternalContext** external_contexts, + std::vector>* subgraphs) + : context_(&owned_context_), + error_reporter_(error_reporter), + next_execution_plan_index_to_prepare_(0), + external_contexts_(external_contexts), + subgraphs_(subgraphs) { + context_->impl_ = static_cast(this); + context_->ResizeTensor = ResizeTensor; + context_->ReportError = ReportErrorC; + context_->AddTensors = AddTensors; + context_->tensors = nullptr; + context_->tensors_size = 0; + context_->allow_fp32_relax_to_fp16 = false; + context_->recommended_num_threads = -1; + context_->GetExternalContext = GetExternalContext; + context_->SetExternalContext = SetExternalContext; + + // Reserve some space for the tensors to avoid excessive resizing. + tensors_.reserve(kTensorsReservedCapacity); + nodes_and_registration().reserve(kTensorsReservedCapacity); + // Invalid to call these these except from TfLiteDelegate + SwitchToKernelContext(); +} + +Subgraph::~Subgraph() { + for (auto& node_and_reg : nodes_and_registration_) { + TfLiteNode& node = node_and_reg.first; + TfLiteIntArrayFree(node.inputs); + TfLiteIntArrayFree(node.outputs); + TfLiteIntArrayFree(node.temporaries); + if (node.builtin_data) free(node.builtin_data); + OpFree(node_and_reg.second, node.user_data); + node.builtin_data = nullptr; + } + + for (size_t i = 0; i < context_->tensors_size; i++) { + TfLiteTensor* tensor = &context_->tensors[i]; + if (tensor->buffer_handle != kTfLiteNullBufferHandle && + tensor->delegate->FreeBufferHandle != nullptr) { + tensor->delegate->FreeBufferHandle(context_, tensor->delegate, + &tensor->buffer_handle); + } + TfLiteTensorFree(tensor); + } +} + +TfLiteStatus Subgraph::ReplaceNodeSubsetsWithDelegateKernels( + TfLiteContext* context, TfLiteRegistration registration, + const TfLiteIntArray* nodes_to_replace, TfLiteDelegate* delegate) { + return static_cast(context->impl_) + ->ReplaceNodeSubsetsWithDelegateKernels(registration, nodes_to_replace, + delegate); +} + +namespace { + +// Copy a std::vector to an existing TfLiteIntArray. +// This is a low-level data manipulation function, and it's caller's +// responsibility to ensure TfLiteIntArray has enough size. +void CopyVectorToTfLiteIntArray(const std::vector& vec, + TfLiteIntArray* arr) { + arr->size = vec.size(); + memcpy(arr->data, vec.data(), sizeof(int) * arr->size); +} + +// This function allocates a continuous memory space that contains a +// TfLiteDelegateParams followed by a several TfLiteIntArray. +// When calling `free` at TfLiteDelegateParams*, all the allocated space +// will be freed together. +// +// +-----------------------------------+ +// | TfLiteDelegateParams | +// | TfLiteDelegate* delegate; | +// | TfLiteIntArray* nodes_to_replace; |--\ +// | TfLiteIntArray* input_tensors; |--+--\ +// | TfLiteIntArray* output_tensors; |--+--+--\ +// +-----------------------------------+ | | | +// | TfLiteIntArray (variable size) |<-/ | | +// +-----------------------------------+ | | +// | TfLiteIntArray (variable size) |<----/ | +// +-----------------------------------+ | +// | TfLiteIntArray (variable size) |<-------/ +// +-----------------------------------+ +TfLiteDelegateParams* CreateDelegateParams(TfLiteDelegate* delegate, + const NodeSubset& node_subset) { + // Step 1: Calculate the allocation size. + int allocation_size = sizeof(TfLiteDelegateParams); + + int nodes_to_replace_size = + TfLiteIntArrayGetSizeInBytes(node_subset.nodes.size()); + allocation_size += nodes_to_replace_size; + + int input_tensors_size = + TfLiteIntArrayGetSizeInBytes(node_subset.input_tensors.size()); + allocation_size += input_tensors_size; + + int output_tensors_size = + TfLiteIntArrayGetSizeInBytes(node_subset.output_tensors.size()); + allocation_size += output_tensors_size; + + // Step 2: Allocate the memory. + // Use `char*` for conveniently step through the allocated space by bytes. + char* allocation = reinterpret_cast(malloc(allocation_size)); + + // Step 3: Fill all data structures structures. + TfLiteDelegateParams* params = + reinterpret_cast(allocation); + params->delegate = delegate; + allocation += sizeof(TfLiteDelegateParams); + + params->nodes_to_replace = reinterpret_cast(allocation); + CopyVectorToTfLiteIntArray(node_subset.nodes, params->nodes_to_replace); + allocation += nodes_to_replace_size; + + params->input_tensors = reinterpret_cast(allocation); + CopyVectorToTfLiteIntArray(node_subset.input_tensors, params->input_tensors); + allocation += input_tensors_size; + + params->output_tensors = reinterpret_cast(allocation); + CopyVectorToTfLiteIntArray(node_subset.output_tensors, + params->output_tensors); + allocation += output_tensors_size; + + return params; +} + +} // namespace + +TfLiteStatus Subgraph::ReplaceNodeSubsetsWithDelegateKernels( + TfLiteRegistration registration, const TfLiteIntArray* nodes_to_replace, + TfLiteDelegate* delegate) { + // Annotate the registration as DELEGATE op. + registration.builtin_code = BuiltinOperator_DELEGATE; + + // Analyze the graph to find all independent node_subsets that are either + // fully not-this-delegate or this-delegate computation. + InterpreterInfo info(this); + std::vector node_subsets; + PartitionGraphIntoIndependentNodeSubsets(&info, nodes_to_replace, + &node_subsets); + + execution_plan_.clear(); + + for (auto& node_subset : node_subsets) { + // Subsets calimed by the delegate should have a "macro" op created, the + // other node_subsets (kTfNonPartition) just have their nodes added back to + // the execution plan. + switch (node_subset.type) { + case NodeSubset::kTfNonPartition: + for (auto it = node_subset.nodes.begin(); it != node_subset.nodes.end(); + ++it) { + execution_plan_.push_back(*it); + } + break; + case NodeSubset::kTfPartition: { + int node_index; + + TfLiteDelegateParams* params = + CreateDelegateParams(delegate, node_subset); + TF_LITE_ENSURE_STATUS(AddNodeWithParameters( + node_subset.input_tensors, node_subset.output_tensors, nullptr, 0, + params, ®istration, &node_index)); + + // Initialize the output tensors's delegate-related fields. + for (int tensor_index : node_subset.output_tensors) { + TfLiteTensor* tensor = &tensors_[tensor_index]; + TF_LITE_ENSURE(context_, tensor->delegate == nullptr || + tensor->delegate == delegate); + tensor->delegate = delegate; + } + + // Associate the node with the delegate. + TfLiteNode* node = &nodes_and_registration_[node_index].first; + node->delegate = delegate; + } break; + case NodeSubset::kTfUnexplored: + return kTfLiteError; + break; + } + } + return kTfLiteOk; +} + +TfLiteExternalContext* Subgraph::GetExternalContext( + TfLiteExternalContextType type) { + if (type >= 0 && type < kTfLiteMaxExternalContexts) { + return external_contexts_[type]; + } + return nullptr; +} + +TfLiteExternalContext* Subgraph::GetExternalContext( + struct TfLiteContext* context, TfLiteExternalContextType type) { + return static_cast(context->impl_)->GetExternalContext(type); +} + +void Subgraph::SetExternalContext(TfLiteExternalContextType type, + TfLiteExternalContext* ctx) { + if (type >= 0 && type < kTfLiteMaxExternalContexts) { + external_contexts_[type] = ctx; + } +} + +void Subgraph::SetExternalContext(struct TfLiteContext* context, + TfLiteExternalContextType type, + TfLiteExternalContext* ctx) { + return static_cast(context->impl_)->SetExternalContext(type, ctx); +} + +// Gets an TfLiteIntArray* representing the execution plan. The interpreter owns +// this memory and it is only guaranteed to exist during the invocation of the +// delegate prepare. +TfLiteStatus Subgraph::GetExecutionPlan(TfLiteIntArray** execution_plan) { + // TODO(aselle): Do not make a copy here + plan_cache_.reset(TfLiteIntArrayCreate(execution_plan_.size())); + *execution_plan = plan_cache_.get(); + static_assert(sizeof(plan_cache_->data[0]) == sizeof(execution_plan_[0]), + "TfLiteIntArray and execution_plan do not contain same type."); + std::memcpy(plan_cache_->data, execution_plan_.data(), + sizeof(plan_cache_->data[0]) * execution_plan_.size()); + return kTfLiteOk; +} + +// WARNING: This is an experimental interface that is subject to change. +// Entry point for C node plugin API to get the execution plan +TfLiteStatus Subgraph::GetExecutionPlan(struct TfLiteContext* context, + TfLiteIntArray** execution_plan) { + return static_cast(context->impl_) + ->GetExecutionPlan(execution_plan); +} + +TfLiteStatus Subgraph::SetInputs(std::vector inputs) { + TF_LITE_ENSURE_OK(&context_, + CheckTensorIndices("inputs", inputs.data(), inputs.size())); + inputs_ = std::move(inputs); + return kTfLiteOk; +} + +TfLiteStatus Subgraph::SetOutputs(std::vector outputs) { + TF_LITE_ENSURE_OK( + &context_, CheckTensorIndices("outputs", outputs.data(), outputs.size())); + outputs_ = std::move(outputs); + return kTfLiteOk; +} + +TfLiteStatus Subgraph::SetVariables(std::vector variables) { + TF_LITE_ENSURE_OK(&context_, CheckTensorIndices("variables", variables.data(), + variables.size())); + variables_ = std::move(variables); + return kTfLiteOk; +} + +TfLiteStatus Subgraph::CheckTensorIndices(const char* label, const int* indices, + int length) { + // Making sure kOptionalTensor is not re-defined to something other than -1. + static_assert(kOptionalTensor == -1, "kOptionalTensor should be defined -1"); + + for (int i = 0; i < length; i++) { + int index = indices[i]; + // Continue if index == kOptionalTensor before additional comparisons below, + // size_t(-1) is always >= context_tensors_size. + if (index == kOptionalTensor) { + continue; + } + if (index < 0 || static_cast(index) >= context_->tensors_size) { + ReportError("Invalid tensor index %d in %s\n", index, label); + consistent_ = false; + return kTfLiteError; + } + } + return kTfLiteOk; +} + +TfLiteStatus Subgraph::BytesRequired(TfLiteType type, const int* dims, + size_t dims_size, size_t* bytes) { + // TODO(aselle): Check for overflow here using overflow.h in TensorFlow + // MultiplyWithoutOverflow. + TF_LITE_ENSURE(context_, bytes != nullptr); + size_t count = 1; + for (int k = 0; k < dims_size; k++) count *= dims[k]; + switch (type) { + case kTfLiteFloat32: + *bytes = sizeof(float) * count; + break; + case kTfLiteInt16: + *bytes = sizeof(int16_t) * count; + break; + case kTfLiteInt32: + *bytes = sizeof(int32_t) * count; + break; + case kTfLiteUInt8: + *bytes = sizeof(uint8_t) * count; + break; + case kTfLiteInt64: + *bytes = sizeof(int64_t) * count; + break; + case kTfLiteBool: + *bytes = sizeof(bool) * count; + break; + case kTfLiteComplex64: + *bytes = sizeof(std::complex) * count; + break; + case kTfLiteInt8: + *bytes = sizeof(int8_t) * count; + break; + default: + ReportError( + "Only float32, int8, int16, int32, int64, uint8, bool, complex64 " + "supported currently."); + return kTfLiteError; + } + return kTfLiteOk; +} + +TfLiteStatus Subgraph::AllocateTensors() { + if (!consistent_) { + ReportError("AllocateTensors() called on inconsistent model."); + return kTfLiteError; + } + + // Explicit (re)allocation is necessary if nodes have been changed or tensors + // have been resized. For inputs marked as dynamic, we can't short-circuit the + // allocation as the client may have done the resize manually. + if (state_ != kStateUninvokable && + !HasDynamicTensorImpl(*context_, inputs())) { + return kTfLiteOk; + } + + next_execution_plan_index_to_prepare_ = 0; + if (memory_planner_) { + TF_LITE_ENSURE_STATUS(memory_planner_->ResetAllocations()); + } + + TF_LITE_ENSURE_STATUS(PrepareOpsAndTensors()); + + state_ = kStateInvokable; + + // Reset the variable tensors to zero after (re)allocating the tensors. + // Developers shouldn't rely on the side effect of this function to reset + // variable tesnsors. They should call `ResetVariableTensors` directly + // instead. + ResetVariableTensors(); + + return kTfLiteOk; +} + +// TODO(ycling): Support non-zero default values. +TfLiteStatus Subgraph::ResetVariableTensors() { + for (auto& tensor : tensors_) { + if (!tensor.is_variable) { + continue; + } + + // Variable tensors have to be `kTfLiteArenaRwPersistent`, and must be + // allocated after the initial `PrepareOpsAndTensors()` is called. + TF_LITE_ENSURE_EQ(context_, tensor.allocation_type, + kTfLiteArenaRwPersistent); + TF_LITE_ENSURE(context_, tensor.data.raw != nullptr); + + memset(tensor.data.raw, 0, tensor.bytes); + } + return kTfLiteOk; +} + +TfLiteStatus Subgraph::AddNodeWithParameters( + const std::vector& inputs, const std::vector& outputs, + const char* init_data, size_t init_data_size, void* builtin_data, + const TfLiteRegistration* registration, int* node_index) { + if (state_ == kStateInvokableAndImmutable) { + ReportError("AddNodeWithParameters is disallowed when graph is immutable."); + return kTfLiteError; + } + state_ = kStateUninvokable; + + std::unique_ptr builtin_data_deleter(builtin_data, + free); + + TF_LITE_ENSURE_OK(context_, CheckTensorIndices("node inputs", inputs.data(), + inputs.size())); + TF_LITE_ENSURE_OK( + &context_, + CheckTensorIndices("node outputs", outputs.data(), outputs.size())); + + int new_node_index = nodes_and_registration_.size(); + if (node_index) *node_index = new_node_index; + nodes_and_registration_.resize(nodes_and_registration_.size() + 1); + auto& node_and_reg = nodes_and_registration_.back(); + TfLiteNode& node = node_and_reg.first; + if (node.inputs) TfLiteIntArrayFree(node.inputs); + if (node.outputs) TfLiteIntArrayFree(node.outputs); + if (node.temporaries) TfLiteIntArrayFree(node.temporaries); + + // NOTE, here we are not using move semantics yet, since our internal + // representation isn't std::vector, but in the future we would like to avoid + // copies, so we want the interface to take r-value references now. + node.inputs = ConvertVectorToTfLiteIntArray(inputs); + node.outputs = ConvertVectorToTfLiteIntArray(outputs); + node.temporaries = TfLiteIntArrayCreate(0); + if (init_data) { + node.user_data = OpInit(*registration, init_data, init_data_size); + } else { + node.user_data = + OpInit(*registration, + reinterpret_cast(builtin_data_deleter.get()), 0); + } + + node.builtin_data = builtin_data_deleter.release(); + // TODO(ycling): Filling `custom_initial_data` and `custom_initial_data_size` + // properly for nodes generated by ReplaceNodeSubsetsWithDelegateKernels. + + if (registration->builtin_code == BuiltinOperator_CUSTOM) { + // When it's a CUSTOM op, the `custom_options` field in the Flatbuffer + // `Operator` table is passed in. + node.custom_initial_data = init_data; + node.custom_initial_data_size = init_data_size; + } else { + node.custom_initial_data = nullptr; + node.custom_initial_data_size = 0; + } + + node.delegate = nullptr; + node_and_reg.second = *registration; + execution_plan_.push_back(new_node_index); + return kTfLiteOk; +} + +TfLiteStatus Subgraph::ResizeInputTensor(int tensor_index, + const std::vector& dims) { + if (state_ == kStateInvokableAndImmutable) { + ReportError("ResizeInputTensor is disallowed when graph is immutable."); + return kTfLiteError; + } + + // TODO(aselle): All bounds checks can be implemented as one-sided bounds + // checks by casting to unsigned for efficiency. Profile before doing this. + TF_LITE_ENSURE(context_, + tensor_index < context_->tensors_size && tensor_index >= 0); + TfLiteTensor* tensor = &context_->tensors[tensor_index]; + + // Short-circuit the state change if the dimensions don't change, avoiding + // unnecessary (re)allocations. + if (EqualArrayAndTfLiteIntArray(tensor->dims, dims.size(), dims.data())) { + return kTfLiteOk; + } + + state_ = kStateUninvokable; + return ResizeTensorImpl(tensor, ConvertVectorToTfLiteIntArray(dims)); +} + +TfLiteStatus Subgraph::PrepareOpsStartingAt( + int first_execution_plan_index, int* last_execution_plan_index_prepared) { + if (first_execution_plan_index == 0) { + has_dynamic_tensors_ = false; + } + for (int execution_plan_index = first_execution_plan_index; + execution_plan_index < execution_plan_.size(); execution_plan_index++) { + int node_index = execution_plan_[execution_plan_index]; + TfLiteNode& node = nodes_and_registration_[node_index].first; + const TfLiteRegistration& registration = + nodes_and_registration_[node_index].second; + EnsureTensorsVectorCapacity(); + if (OpPrepare(registration, &node) == kTfLiteError) { + return ReportOpError(context_, node, registration, node_index, + "failed to prepare"); + } + + *last_execution_plan_index_prepared = execution_plan_index; + + // Discontinue if the node has dynamic outputs. Note that we don't + // stop for dynamic temporary tensors since they won't affect the + // sizes of other tensors in the graph. + if (HasDynamicTensor(*context_, node.outputs)) { + has_dynamic_tensors_ = true; + return kTfLiteOk; + } + } + return kTfLiteOk; +} + +TfLiteStatus Subgraph::PrepareOpsAndTensors() { + if (!memory_planner_) { + memory_planner_.reset(new ArenaPlanner( + context_, std::unique_ptr(new InterpreterInfo(this)), + /*preserve_inputs=*/true, /*preserve_intermediates*/ false)); + memory_planner_->PlanAllocations(); + } + + int last_exec_plan_index_prepared = 0; + + TF_LITE_ENSURE_STATUS(PrepareOpsStartingAt( + next_execution_plan_index_to_prepare_, &last_exec_plan_index_prepared)); + TF_LITE_ENSURE_STATUS(memory_planner_->ExecuteAllocations( + next_execution_plan_index_to_prepare_, last_exec_plan_index_prepared)); + + next_execution_plan_index_to_prepare_ = last_exec_plan_index_prepared + 1; + return kTfLiteOk; +} + +TfLiteStatus Subgraph::Invoke() { + if (!consistent_) { + ReportError("Invoke called on model that is not consistent."); + return kTfLiteError; + } + + TfLiteStatus status = kTfLiteOk; + if (state_ == kStateUninvokable) { + ReportError("Invoke called on model that is not ready."); + return kTfLiteError; + } + + if (nnapi_delegate_) { + if (next_execution_plan_index_to_prepare_ == execution_plan_.size()) { + TF_LITE_ENSURE_OK(context_, nnapi_delegate_->Invoke(this)); + return kTfLiteOk; + } else { + // TODO(aselle): In the future, we would like this to be an + // automatic tflite CPU fallback. + ReportError( + "NNAPI was requested, but dependent sized tensors " + "being used.\n"); + return kTfLiteError; + } + } + + // Invocations are always done in node order. + // Note that calling Invoke repeatedly will cause the original memory plan to + // be reused, unless either ResizeInputTensor() or AllocateTensors() has been + // called. + for (int execution_plan_index = 0; + execution_plan_index < execution_plan_.size(); execution_plan_index++) { + if (execution_plan_index == next_execution_plan_index_to_prepare_) { + TF_LITE_ENSURE_STATUS(PrepareOpsAndTensors()); + TF_LITE_ENSURE(context_, next_execution_plan_index_to_prepare_ >= + execution_plan_index); + } + int node_index = execution_plan_[execution_plan_index]; + TfLiteNode& node = nodes_and_registration_[node_index].first; + const TfLiteRegistration& registration = + nodes_and_registration_[node_index].second; + SCOPED_OPERATOR_PROFILE(profiler_, node_index); + + // TODO(ycling): This is an extra loop through inputs to check if the data + // need to be copied from Delegate buffer to raw memory, which is often not + // needed. We may want to cache this in prepare to know if this needs to be + // done for a node or not. + for (int i = 0; i < node.inputs->size; ++i) { + int tensor_index = node.inputs->data[i]; + if (tensor_index == kOptionalTensor) { + continue; + } + TfLiteTensor* tensor = &tensors_[tensor_index]; + if (tensor->delegate && tensor->delegate != node.delegate && + tensor->data_is_stale) { + EnsureTensorDataIsReadable(tensor_index); + } + } + + EnsureTensorsVectorCapacity(); + tensor_resized_since_op_invoke_ = false; + if (OpInvoke(registration, &node) == kTfLiteError) { + status = ReportOpError(context_, node, registration, node_index, + "failed to invoke"); + } + + // Force execution prep for downstream ops if the latest op triggered the + // resize of a dynamic tensor. + if (tensor_resized_since_op_invoke_ && + HasDynamicTensor(*context_, node.outputs)) { + next_execution_plan_index_to_prepare_ = execution_plan_index + 1; + } + } + + return status; +} + +TfLiteStatus Subgraph::ResizeTensor(TfLiteContext* context, + TfLiteTensor* tensor, + TfLiteIntArray* new_size) { + // Note here that context->impl_ is recovering the this pointer for an + // instance of Interpreter to call into the member function ResizeTensorImpl + // (this function is static). + return static_cast(context->impl_) + ->ResizeTensorImpl(tensor, new_size); +} + +void Subgraph::ReportErrorImpl(const char* format, va_list args) { + error_reporter_->Report(format, args); +} + +void Subgraph::ReportErrorC(TfLiteContext* context, const char* format, ...) { + va_list args; + va_start(args, format); + auto* f = static_cast(context->impl_); + // Note here that context->impl_ is recovering the this pointer for an + // instance of Subgraph to call into the member function ReportErrorImpl + // (this function is static). + f->ReportErrorImpl(format, args); + va_end(args); +} + +// Entry point for C node plugin API to report an error. +void Subgraph::ReportError(const char* format, ...) { + va_list args; + va_start(args, format); + auto* f = static_cast(context_->impl_); + // Note here that context->impl_ is recovering the this pointer for an + // instance of Subgraph to call into the member function ReportErrorImpl + // (this function is static). + f->ReportErrorImpl(format, args); + va_end(args); +} + +TfLiteStatus Subgraph::AddTensors(int tensors_to_add, + int* first_new_tensor_index) { + const size_t base_index = tensors_.size(); + if (first_new_tensor_index) *first_new_tensor_index = base_index; + tensors_.resize(tensors_.size() + tensors_to_add); + for (size_t i = base_index; i < tensors_.size(); i++) { + memset(&tensors_[i], 0, sizeof(tensors_[i])); + tensors_[i].buffer_handle = kTfLiteNullBufferHandle; + } + context_->tensors = tensors_.data(); + context_->tensors_size = tensors_.size(); + return kTfLiteOk; +} + +TfLiteStatus Subgraph::AddTensors(TfLiteContext* context, int tensors_to_add, + int* first_new_tensor_index) { + // Note here that context->impl_ is recovering the this pointer for an + // instance of Interpreter to call into the member function AddTensors + // (this function is static). + return static_cast(context->impl_) + ->AddTensors(tensors_to_add, first_new_tensor_index); +} + +TfLiteStatus Subgraph::GetNodeAndRegistration( + int node_index, TfLiteNode** node, TfLiteRegistration** registration) { + TF_LITE_ENSURE(context_, node_index >= 0); + auto nodes_size = nodes_and_registration_.size(); + TF_LITE_ENSURE(context_, static_cast(node_index) < nodes_size); + TF_LITE_ENSURE(context_, node != nullptr && registration != nullptr); + auto& node_and_reg = nodes_and_registration_[node_index]; + *node = &node_and_reg.first; + *registration = &node_and_reg.second; + return kTfLiteOk; +} + +TfLiteStatus Subgraph::GetNodeAndRegistration( + struct TfLiteContext* context, int node_index, TfLiteNode** node, + TfLiteRegistration** registration) { + return static_cast(context->impl_) + ->GetNodeAndRegistration(node_index, node, registration); +} + +TfLiteStatus Subgraph::SetTensorParametersReadOnly( + int tensor_index, TfLiteType type, const char* name, const size_t rank, + const int* dims, TfLiteQuantizationParams quantization, const char* buffer, + size_t bytes, const Allocation* allocation) { + if (state_ == kStateInvokableAndImmutable) { + ReportError( + "SetTensorParametersReadOnly is disallowed when graph is immutable."); + return kTfLiteError; + } + + TF_LITE_ENSURE(context_, + tensor_index < context_->tensors_size && tensor_index >= 0); + // For most tensors we know exactly how much memory is necessary so we can + // ensure the buffer is large enough. However, we need to skip string tensors + // because their sizes change with the contents of the individual strings. + if (type != kTfLiteString) { + size_t required_bytes; + TF_LITE_ENSURE_OK(context_, + BytesRequired(type, dims, rank, &required_bytes)); + TF_LITE_ENSURE_EQ(context_, required_bytes, bytes); + } + + TfLiteTensor& tensor = context_->tensors[tensor_index]; + if (type == tensor.type && + EqualArrayAndTfLiteIntArray(tensor.dims, rank, dims)) { + // Fast path which does not invalidate the invokable property. + TfLiteTensorDataFree(&tensor); + tensor.data.raw = const_cast(buffer); + if (!tensor.dims) tensor.dims = ConvertArrayToTfLiteIntArray(rank, dims); + tensor.params = quantization; + tensor.allocation_type = kTfLiteMmapRo; + tensor.allocation = allocation; + } else { + state_ = kStateUninvokable; + TfLiteTensorReset(type, name, ConvertArrayToTfLiteIntArray(rank, dims), + quantization, const_cast(buffer), bytes, + kTfLiteMmapRo, allocation, false, &tensor); + } + return kTfLiteOk; +} + +// Set description of inputs/outputs/data/fptrs for node `node_index`. +// This variant assumes an external buffer has been allocated of size +// bytes. The lifetime of buffer must be ensured to be greater or equal +// to Interpreter. +TfLiteStatus Subgraph::SetTensorParametersReadWrite( + int tensor_index, TfLiteType type, const char* name, const size_t rank, + const int* dims, TfLiteQuantizationParams quantization, bool is_variable) { + if (state_ == kStateInvokableAndImmutable) { + ReportError( + "SetTensorParametersReadWrite is disallowed when graph is immutable."); + return kTfLiteError; + } + TF_LITE_ENSURE(context_, + tensor_index < context_->tensors_size && tensor_index >= 0); + size_t required_bytes = 0; + if (type != kTfLiteString) { + // These types will be allocated in our arena so we need to record how + // many bytes we will need based on the dimensions. String tensors are + // allocated dynamically and we can't know ahead of time how much space + // they will require. + TF_LITE_ENSURE_OK(context_, + BytesRequired(type, dims, rank, &required_bytes)); + } + + TfLiteAllocationType allocation_type = kTfLiteArenaRw; + if (type == kTfLiteString) { + if (is_variable) { + // We don't have a real use case for string variable tensor. + ReportError("String variable tensor isn't supported."); + return kTfLiteError; + } + allocation_type = kTfLiteDynamic; + } else if (is_variable) { + allocation_type = kTfLiteArenaRwPersistent; + } + + TfLiteTensorReset(type, name, ConvertArrayToTfLiteIntArray(rank, dims), + quantization, + /*buffer=*/nullptr, required_bytes, allocation_type, + nullptr, is_variable, &context_->tensors[tensor_index]); + return kTfLiteOk; +} + +TfLiteStatus Subgraph::SetExecutionPlan(const std::vector& new_plan) { + for (int node_index : new_plan) { + TF_LITE_ENSURE(context_, node_index >= 0 && + node_index < nodes_and_registration_.size()); + } + execution_plan_ = new_plan; + return kTfLiteOk; +} + +TfLiteStatus Subgraph::ResizeTensorImpl(TfLiteTensor* tensor, + TfLiteIntArray* new_size) { + // Note that in theory we could resize kTfLiteArenaRwPersistent tensors too. + if (tensor->allocation_type == kTfLiteArenaRw || + tensor->allocation_type == kTfLiteDynamic || + tensor->allocation_type == kTfLiteArenaRwPersistent) { + tensor_resized_since_op_invoke_ |= + TfLiteIntArrayEqual(tensor->dims, new_size) == 0; + if (tensor->type != kTfLiteString) { + size_t bytesRequired; + TfLiteStatus status = BytesRequired(tensor->type, new_size->data, + new_size->size, &bytesRequired); + if (status != kTfLiteOk) { + TfLiteIntArrayFree(new_size); + return kTfLiteError; + } + + // Realloc space for kTfLiteDynamic tensors. + TfLiteTensorRealloc(bytesRequired, tensor); + tensor->bytes = bytesRequired; + } + if (tensor->dims) TfLiteIntArrayFree(tensor->dims); + tensor->dims = new_size; + + if (tensor->allocation_type != kTfLiteDynamic) { + tensor->data.raw = nullptr; + } + } else { + // kTfLiteMmapRo tensors are stored in the flatbuffer and are therefore + // of fixed size. + TfLiteIntArrayFree(new_size); + ReportError("Attempting to resize a fixed-size tensor."); + return kTfLiteError; + } + return kTfLiteOk; +} + +void Subgraph::UseNNAPI(bool enable) { + // TODO(aselle): This is a workaround for finding if NNAPI exists. + // We also need to make sure getLibraryHandle() is renamed to be NNAPI + // prefixed. + if (!NNAPIDelegate::IsSupported()) enable = false; + if (!enable) { + nnapi_delegate_.reset(); + } else if (!nnapi_delegate_) { + nnapi_delegate_.reset(new NNAPIDelegate); + } +} + +void Subgraph::SwitchToDelegateContext() { + context_->GetNodeAndRegistration = GetNodeAndRegistration; + context_->ReplaceNodeSubsetsWithDelegateKernels = + ReplaceNodeSubsetsWithDelegateKernels; + context_->GetExecutionPlan = GetExecutionPlan; +} + +void Subgraph::SwitchToKernelContext() { + context_->GetNodeAndRegistration = [](struct TfLiteContext* context, + int node_index, TfLiteNode** node, + TfLiteRegistration** registration) { + return ForbiddenContextFunction(context); + }; + context_->ReplaceNodeSubsetsWithDelegateKernels = + [](TfLiteContext* context, TfLiteRegistration registration, + const TfLiteIntArray* nodes_to_replace, TfLiteDelegate* delegate) { + return ForbiddenContextFunction(context); + }; + context_->GetExecutionPlan = [](struct TfLiteContext* context, + TfLiteIntArray**) { + return ForbiddenContextFunction(context); + }; +} + +TfLiteStatus Subgraph::ModifyGraphWithDelegate(TfLiteDelegate* delegate) { + if (!(delegate->flags & kTfLiteDelegateFlagsAllowDynamicTensors)) { + int last_execution_plan_index_prepared; + TF_LITE_ENSURE_OK(&context_, PrepareOpsStartingAt( + 0, &last_execution_plan_index_prepared)); + if (has_dynamic_tensors_) { + ReportError( + "Attempting to use a delegate that only supports static-sized " + "tensors with a graph that has dynamic-sized tensors."); + return kTfLiteError; + } + } + + // TODO(aselle): Consider if it is worth storing pointers to delegates. + // Setup additional context interface. + SwitchToDelegateContext(); + + TfLiteStatus status = delegate->Prepare(context_, delegate); + + // Remove additional context info. + SwitchToKernelContext(); + + TF_LITE_ENSURE_OK(context_, status); + + if (!(delegate->flags & kTfLiteDelegateFlagsAllowDynamicTensors)) { + // Reset the state to force tensor/op reallocation. + state_ = kStateUninvokable; + TF_LITE_ENSURE_OK(context_, AllocateTensors()); + TF_LITE_ENSURE_EQ(context_, state_, kStateInvokable); + // After using a delegate which doesn't support dynamic tensors, make the + // entire graph immutable. + state_ = kStateInvokableAndImmutable; + } + + return status; +} + +} // namespace tflite diff --git a/tensorflow/lite/core/subgraph.h b/tensorflow/lite/core/subgraph.h new file mode 100644 index 0000000000..2a7c3a7c32 --- /dev/null +++ b/tensorflow/lite/core/subgraph.h @@ -0,0 +1,501 @@ +/* Copyright 2018 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_LITE_CORE_SUBGRAPH_H_ +#define TENSORFLOW_LITE_CORE_SUBGRAPH_H_ + +#include +#include + +#include "tensorflow/lite/allocation.h" +#include "tensorflow/lite/c/c_api_internal.h" +#include "tensorflow/lite/memory_planner.h" +#include "tensorflow/lite/profiling/profiler.h" +#include "tensorflow/lite/util.h" + +namespace tflite { + +// Forward declare since NNAPIDelegate uses Interpreter. +class NNAPIDelegate; + +class Subgraph { + public: + friend class Interpreter; + + Subgraph(ErrorReporter* error_reporter, + TfLiteExternalContext** external_contexts, + std::vector>* subgraphs); + + Subgraph(const Subgraph&) = delete; + + // Subgraphs should be movable but not copyable. + Subgraph(Subgraph&&) = default; + Subgraph& operator=(const Subgraph&) = delete; + virtual ~Subgraph(); + + // Provide a list of tensor indexes that are inputs to the model. + // Each index is bound check and this modifies the consistent_ flag of the + // interpreter. + TfLiteStatus SetInputs(std::vector inputs); + + // Provide a list of tensor indexes that are outputs to the model + // Each index is bound check and this modifies the consistent_ flag of the + // interpreter. + TfLiteStatus SetOutputs(std::vector outputs); + + // Provide a list of tensor indexes that are variable tensors. + // Each index is bound check and this modifies the consistent_ flag of the + // interpreter. + TfLiteStatus SetVariables(std::vector variables); + + // Adds a node with the given parameters and returns the index of the new + // node in `node_index` (optionally). Interpreter will take ownership of + // `builtin_data` and destroy it with `free`. Ownership of 'init_data' + // remains with the caller. + TfLiteStatus AddNodeWithParameters(const std::vector& inputs, + const std::vector& outputs, + const char* init_data, + size_t init_data_size, void* builtin_data, + const TfLiteRegistration* registration, + int* node_index); + + // Adds `tensors_to_add` tensors, preserving pre-existing Tensor entries. + // The value pointed to by `first_new_tensor_index` will be set to the + // index of the first new tensor if `first_new_tensor_index` is non-null. + TfLiteStatus AddTensors(int tensors_to_add, int* first_new_tensor_index); + + // Set description of inputs/outputs/data/fptrs for node `node_index`. + // This variant assumes an external buffer has been allocated of size + // bytes. The lifetime of buffer must be ensured to be greater or equal + // to Interpreter. + TfLiteStatus SetTensorParametersReadOnly( + int tensor_index, TfLiteType type, const char* name, const size_t rank, + const int* dims, TfLiteQuantizationParams quantization, + const char* buffer, size_t bytes, const Allocation* allocation); + + // Set description of inputs/outputs/data/fptrs for node `node_index`. + // This variant assumes an external buffer has been allocated of size + // bytes. The lifetime of buffer must be ensured to be greater or equal + // to Interpreter. + TfLiteStatus SetTensorParametersReadWrite( + int tensor_index, TfLiteType type, const char* name, const size_t rank, + const int* dims, TfLiteQuantizationParams quantization, bool is_variable); + + // WARNING: Experimental interface, subject to change + // Overrides execution plan. This bounds checks indices sent in. + TfLiteStatus SetExecutionPlan(const std::vector& new_plan); + + // Get a mutable tensor data structure. + // TODO(aselle): Create a safe ArrayHandle interface to avoid exposing this + // read/write access to structure + TfLiteTensor* tensor(int tensor_index) { + if (tensor_index < 0 || + static_cast(tensor_index) >= context_->tensors_size) { + return nullptr; + } + return &context_->tensors[tensor_index]; + } + + // Get an immutable tensor data structure. + const TfLiteTensor* tensor(int tensor_index) const { + if (tensor_index < 0 || + static_cast(tensor_index) >= context_->tensors_size) { + return nullptr; + } + return &context_->tensors[tensor_index]; + } + + // Read only access to list of inputs. + std::vector& inputs() { return inputs_; } + + // Read only access to list of inputs. + const std::vector& inputs() const { return inputs_; } + + // Read only access to list of outputs. + std::vector& outputs() { return outputs_; } + + // Read only access to list of outputs. + const std::vector& outputs() const { return outputs_; } + + // Read only access to list of variable tensors. + std::vector& variables() { return variables_; } + + // Read only access to list of variable tensors. + const std::vector& variables() const { return variables_; } + + size_t tensors_size() const { return tensors_.size(); } + + // Return the number of ops in the model. + size_t nodes_size() const { return nodes_and_registration_.size(); } + + // Read only access to list of variable tensors. + std::vector& execution_plan() { return execution_plan_; } + + // Read only access to list of variable tensors. + const std::vector& execution_plan() const { return execution_plan_; } + + // Mutable form of tensors (TEMPORARY for refactor). + // TODO(b/119495520): remove when refactoring complete. + std::vector& tensors() { return tensors_; } + // Mutable form of tensors (TEMPORARY for refactor). + // TODO(b/119495520): remove when refactoring complete. + std::vector>& + nodes_and_registration() { + return nodes_and_registration_; + } + + const std::vector>& + nodes_and_registration() const { + return nodes_and_registration_; + } + + // Get a pointer to an operation and registration data structure if in bounds. + const std::pair* node_and_registration( + int node_index) const { + if (node_index < 0 || static_cast(node_index) >= nodes_size()) + return nullptr; + return &nodes_and_registration_[node_index]; + } + + // Change the dimensionality of a given tensor. Note, this is only acceptable + // for tensor indices that are inputs. + // Returns status of failure or success. + // TODO(aselle): Consider implementing ArraySlice equivalent to make this + // more adept at accepting data without an extra copy. Use absl::ArraySlice + // if our partners determine that dependency is acceptable. + TfLiteStatus ResizeInputTensor(int tensor_index, + const std::vector& dims); + + // Update allocations for all tensors. This will redim dependent tensors using + // the input tensor dimensionality as given. This is relatively expensive. + // If you know that your sizes are not changing, you need not call this. + // Returns status of success or failure. + TfLiteStatus AllocateTensors(); + + // Invoke the subgraph (run the whole graph in dependency order). + // + // NOTE: It is possible that the interpreter is not in a ready state + // to evaluate (i.e. if a ResizeTensor() has been performed without an + // AllocateTensors(). + // Returns status of success or failure. + TfLiteStatus Invoke(); + + // Entry point for C node plugin API to report an error. + void ReportError(const char* format, ...); + + void UseNNAPI(bool enable); + + // Return the subgraph specific context. + TfLiteContext* context() { return context_; } + + // Set the value of an external context. + void SetExternalContext(TfLiteExternalContextType type, + TfLiteExternalContext* ctx); + // Get the half precision flag. + // WARNING: This is an experimental API and subject to change. + bool GetAllowFp16PrecisionForFp32() const { + return context_->allow_fp32_relax_to_fp16; + } + + // Ensure the data in `tensor.data` is readable. In case delegate is used, + // it might require to copy the data from delegate buffer to raw memory. + // WARNING: This is an experimental API and subject to change. + // TODO(b/119495520): make this private when refactoring complete. + TfLiteStatus EnsureTensorDataIsReadable(int tensor_index) { + TfLiteTensor* t = &tensors_[tensor_index]; + TF_LITE_ENSURE(context_, t != nullptr); + if (t->data_is_stale) { + TF_LITE_ENSURE(context_, t->delegate != nullptr); + TF_LITE_ENSURE(context_, t->buffer_handle != kTfLiteNullBufferHandle); + TF_LITE_ENSURE(context_, t->delegate->CopyFromBufferHandle != nullptr); + // TODO(b/120420546): we must add a test that exercise this code. + TF_LITE_ENSURE_STATUS(t->delegate->CopyFromBufferHandle( + context_, t->delegate, t->buffer_handle, t)); + t->data_is_stale = false; + } + return kTfLiteOk; + } + + // The default capacity of `tensors_` vector. + static constexpr int kTensorsReservedCapacity = 128; + // The capacity headroom of `tensors_` vector before calling ops' + // `prepare` and `invoke` function. In these functions, it's guaranteed + // allocating up to `kTensorsCapacityHeadroom` more tensors won't invalidate + // pointers to existing tensors. + static constexpr int kTensorsCapacityHeadroom = 16; + + // Reset all variable tensors to the default value. + // If a variable tensor doesn't have a buffer, reset it to zero. + // TODO(b/115961645): Implement - If a variable tensor has a buffer, reset it + // to the value of the buffer. + // WARNING: This is an experimental API and subject to change. + TfLiteStatus ResetVariableTensors(); + + void SetProfiler(profiling::Profiler* profiler) { profiler_ = profiler; } + + profiling::Profiler* GetProfiler() { return profiler_; } + + // Returns a pointer to vector of subgraphs. + // WARNING: This is an experimental API and subject to change. + std::vector>* GetSubgraphs() { return subgraphs_; } + + // True if all tensors in the graph has static size after calling + // `AllocateTensors` function. + // Before `AllocateTensors` is called, this will always return true; + bool HasDynamicTensors() { return has_dynamic_tensors_; } + + private: + // Prevent 'context_' from accessing functions that are only available to + // delegated kernels. + void SwitchToKernelContext(); + + // Add delegate-only functions to 'context_'. + void SwitchToDelegateContext(); + + // Give 'op_reg' a chance to initialize itself using the contents of + // 'buffer'. + void* OpInit(const TfLiteRegistration& op_reg, const char* buffer, + size_t length) { + if (op_reg.init == nullptr) return nullptr; + return op_reg.init(context_, buffer, length); + } + + // Let 'op_reg' release any memory it might have allocated via 'OpInit'. + void OpFree(const TfLiteRegistration& op_reg, void* buffer) { + if (op_reg.free == nullptr) return; + if (buffer) { + op_reg.free(context_, buffer); + } + } + + // Prepare the given 'node' for execution. + TfLiteStatus OpPrepare(const TfLiteRegistration& op_reg, TfLiteNode* node) { + if (op_reg.prepare == nullptr) return kTfLiteOk; + return op_reg.prepare(context_, node); + } + + // Invoke the operator represented by 'node'. + TfLiteStatus OpInvoke(const TfLiteRegistration& op_reg, TfLiteNode* node) { + if (op_reg.invoke == nullptr) return kTfLiteError; + return op_reg.invoke(context_, node); + } + + // Call OpPrepare() for as many ops as possible, allocating memory for their + // tensors. If an op containing dynamic tensors is found, preparation will be + // postponed until this function is called again. This allows the interpreter + // to wait until Invoke() to resolve the sizes of dynamic tensors. + TfLiteStatus PrepareOpsAndTensors(); + + // Call OpPrepare() for all ops starting at 'first_node'. Stop when a + // dynamic tensors is found or all ops have been prepared. Fill + // 'last_node_prepared' with the id of the op containing dynamic tensors, or + // the last in the graph. + TfLiteStatus PrepareOpsStartingAt(int first_execution_plan_index, + int* last_execution_plan_index_prepared); + + // Tensors needed by the interpreter. Use `AddTensors` to add more blank + // tensor entries. Note, `tensors_.data()` needs to be synchronized to the + // `context_` whenever this std::vector is reallocated. Currently this + // only happens in `AddTensors()`. + std::vector tensors_; + + // Check if an array of tensor indices are valid with respect to the Tensor + // array. + // NOTE: this changes consistent_ to be false if indices are out of bounds. + TfLiteStatus CheckTensorIndices(const char* label, const int* indices, + int length); + + // Compute the number of bytes required to represent a tensor with dimensions + // specified by the array dims (of length dims_size). Returns the status code + // and bytes. + TfLiteStatus BytesRequired(TfLiteType type, const int* dims, size_t dims_size, + size_t* bytes); + + // Request an tensor be resized implementation. If the given tensor is of + // type kTfLiteDynamic it will also be allocated new memory. + TfLiteStatus ResizeTensorImpl(TfLiteTensor* tensor, TfLiteIntArray* new_size); + + // Report a detailed error string (will be printed to stderr). + // TODO(aselle): allow user of class to provide alternative destinations. + void ReportErrorImpl(const char* format, va_list args); + + // Entry point for C node plugin API to request an tensor be resized. + static TfLiteStatus ResizeTensor(TfLiteContext* context, TfLiteTensor* tensor, + TfLiteIntArray* new_size); + // Entry point for C node plugin API to report an error. + static void ReportErrorC(TfLiteContext* context, const char* format, ...); + + // Entry point for C node plugin API to add new tensors. + static TfLiteStatus AddTensors(TfLiteContext* context, int tensors_to_add, + int* first_new_tensor_index); + + // WARNING: This is an experimental API and subject to change. + // Entry point for C API ReplaceNodeSubsetsWithDelegateKernels + static TfLiteStatus ReplaceNodeSubsetsWithDelegateKernels( + TfLiteContext* context, TfLiteRegistration registration, + const TfLiteIntArray* nodes_to_replace, TfLiteDelegate* delegate); + + // Update the execution graph to replace some of the nodes with stub + // nodes. Specifically any node index that has `nodes[index]==1` will be + // slated for replacement with a delegate kernel specified by registration. + // Ownership of 'nodes_to_replace' and 'delegate' remains with the caller. + // WARNING: This is an experimental interface that is subject to change. + TfLiteStatus ReplaceNodeSubsetsWithDelegateKernels( + TfLiteRegistration registration, const TfLiteIntArray* nodes_to_replace, + TfLiteDelegate* delegate); + + // WARNING: This is an experimental interface that is subject to change. + // Gets the internal pointer to a TensorFlow lite node by node_index. + TfLiteStatus GetNodeAndRegistration(int node_index, TfLiteNode** node, + TfLiteRegistration** registration); + + // WARNING: This is an experimental interface that is subject to change. + // Entry point for C node plugin API to get a node by index. + static TfLiteStatus GetNodeAndRegistration(struct TfLiteContext*, + int node_index, TfLiteNode** node, + TfLiteRegistration** registration); + + // WARNING: This is an experimental interface that is subject to change. + // Gets an TfLiteIntArray* representing the execution plan. The interpreter + // owns this memory and it is only guaranteed to exist during the invocation + // of the delegate prepare. + TfLiteStatus GetExecutionPlan(TfLiteIntArray** execution_plan); + + // WARNING: This is an experimental interface that is subject to change. + // Entry point for C node plugin API to get the execution plan. + static TfLiteStatus GetExecutionPlan(struct TfLiteContext* context, + TfLiteIntArray** execution_plan); + + // Retrieve an existing external context by type. + TfLiteExternalContext* GetExternalContext(TfLiteExternalContextType type); + static TfLiteExternalContext* GetExternalContext( + struct TfLiteContext* context, TfLiteExternalContextType type); + + // Set the value of an external context. + static void SetExternalContext(struct TfLiteContext* context, + TfLiteExternalContextType type, + TfLiteExternalContext* ctx); + + // Allow a delegate to look at the graph and modify the graph to handle + // parts of the graph themselves. After this is called, the graph may + // contain new nodes that replace 1 more nodes. + // WARNING: This is an experimental API and subject to change. + TfLiteStatus ModifyGraphWithDelegate(TfLiteDelegate* delegate); + + // Ensures that `tensors_` has at least `kTensorsCapacityHeadroom` extra + // capacity. Calling this function may invalidate existing pointers to + // tensors. After calling this function, adding `kTensorsCapacityHeadroom` + // more tensors won't invalidate the pointer to existing tensors. + void EnsureTensorsVectorCapacity() { + const size_t required_capacity = tensors_.size() + kTensorsCapacityHeadroom; + if (required_capacity > tensors_.capacity()) { + tensors_.reserve(required_capacity); + context_->tensors = tensors_.data(); + } + } + + // The state of the Interpreter. + enum State { + // The interpreter isn't ready to be invoked. + // `AllocateTensor` need to be called to enter an invokable state. + kStateUninvokable = 0, + // The interpreter is ready to be invoked. + kStateInvokable, + // The interpreter is ready to be invoked, and graph can't be further + // modified. The interpreter will enter this state when calling + // `ModifyGraphWithDelegate` with `allow_dynamic_tensors=false`. + kStateInvokableAndImmutable, + }; + State state_ = kStateUninvokable; + + // A pure C data structure used to communicate with the pure C plugin + // interface. To avoid copying tensor metadata, this is also the definitive + // structure to store tensors. + // TODO(b/119495520): Get rid of owned and just make context_ a instance. + TfLiteContext owned_context_; + TfLiteContext* context_; + + // Node inputs/outputs are stored in TfLiteNode and TfLiteRegistration stores + // function pointers to actual implementation. + std::vector> + nodes_and_registration_; + + // Whether the model is consistent. That is to say if the inputs and outputs + // of every node and the global inputs and outputs are valid indexes into + // the tensor array. + bool consistent_ = true; + + // Array of indices representing the tensors that are inputs to the + // interpreter. + std::vector inputs_; + + // Array of indices representing the tensors that are outputs to the + // interpreter. + std::vector outputs_; + + // Array of indices representing the tensors that are variable tensors. + std::vector variables_; + + // The error reporter delegate that tflite will forward queries errors to. + ErrorReporter* error_reporter_; + + // Index of the next node to prepare. + // During Invoke(), Interpreter will allocate input tensors first, which are + // known to be fixed size. Then it will allocate outputs from nodes as many + // as possible. When there is a node that produces dynamic sized tensor. + // Interpreter will stop allocating tensors, set the value of next allocate + // node id, and execute the node to generate the output tensor before continue + // to allocate successors. This process repeats until all nodes are executed. + // NOTE: this relies on the order of nodes that is in topological order. + int next_execution_plan_index_to_prepare_; + + // WARNING: This is an experimental interface that is subject to change. + // This is a list of node indices (to index into nodes_and_registration). + // This represents a valid topological sort (dependency ordered) execution + // plan. In particular, it is valid for this ordering to contain only a + // subset of the node indices. + std::vector execution_plan_; + + // In the future, we'd like a TfLiteIntArray compatible representation. + // TODO(aselle): replace execution_plan_ with this. + std::unique_ptr plan_cache_; + + // Whether to delegate to NN API + std::unique_ptr nnapi_delegate_; + + std::unique_ptr memory_planner_; + + // Tracking bit for whether a tensor was resized in the course of an op + // invocation. This is a useful hint to ensure that dynamic tensor outputs + // trigger downstream reallocation after op invocation. + bool tensor_resized_since_op_invoke_ = false; + + // External contexts (kTfLiteMaxExternalContexts). + TfLiteExternalContext** external_contexts_; + + // Profiler for this interpreter instance. + profiling::Profiler* profiler_ = nullptr; + + // A pointer to vector of subgraphs. The vector is owned by the interpreter. + std::vector>* subgraphs_ = nullptr; + + // True if all tensors in the graph has static size after calling + // `PrepareOpsStartingAt` function (which is called by the `AllocateTensors` + // public function). + // The value is invalid before `PrepareOpStartingAt` is called. + bool has_dynamic_tensors_ = true; +}; + +} // namespace tflite +#endif // TENSORFLOW_LITE_CORE_SUBGRAPH_H_ diff --git a/tensorflow/lite/delegates/flex/BUILD b/tensorflow/lite/delegates/flex/BUILD index 222a043a88..75083bf95a 100644 --- a/tensorflow/lite/delegates/flex/BUILD +++ b/tensorflow/lite/delegates/flex/BUILD @@ -83,8 +83,10 @@ cc_library( ":delegate_data", ":kernel", ":util", + "@com_google_absl//absl/strings:strings", "//tensorflow/lite/c:c_api_internal", "//tensorflow/lite:kernel_api", + "//tensorflow/lite:string_util", "//tensorflow/lite:util", ] + select({ "//tensorflow:android": [ @@ -116,6 +118,7 @@ cc_library( hdrs = ["delegate_data.h"], deps = [ ":buffer_map", + "@com_google_absl//absl/memory", "//tensorflow/core/common_runtime/eager:context", ] + select({ "//tensorflow:android": [ diff --git a/tensorflow/lite/delegates/flex/buffer_map.cc b/tensorflow/lite/delegates/flex/buffer_map.cc index 9a6c5e74a7..262ca9e089 100644 --- a/tensorflow/lite/delegates/flex/buffer_map.cc +++ b/tensorflow/lite/delegates/flex/buffer_map.cc @@ -93,6 +93,11 @@ class TfLiteTensorBuffer : public BaseTfLiteTensorBuffer { class StringTfLiteTensorBuffer : public BaseTfLiteTensorBuffer { public: explicit StringTfLiteTensorBuffer(const TfLiteTensor* tensor) { + if (tensor->data.raw == nullptr) { + num_strings_ = 0; + data_ = nullptr; + return; + } num_strings_ = GetStringCount(tensor->data.raw); data_ = tensorflow::cpu_allocator()->Allocate(num_strings_); diff --git a/tensorflow/lite/delegates/flex/delegate.cc b/tensorflow/lite/delegates/flex/delegate.cc index 4fc2d82b49..ca7314fbae 100644 --- a/tensorflow/lite/delegates/flex/delegate.cc +++ b/tensorflow/lite/delegates/flex/delegate.cc @@ -16,12 +16,14 @@ limitations under the License. #include +#include "absl/strings/str_cat.h" +#include "tensorflow/core/lib/core/status.h" #include "tensorflow/lite/context_util.h" #include "tensorflow/lite/delegates/flex/buffer_map.h" #include "tensorflow/lite/delegates/flex/kernel.h" #include "tensorflow/lite/delegates/flex/util.h" +#include "tensorflow/lite/string_util.h" #include "tensorflow/lite/util.h" -#include "tensorflow/core/lib/core/status.h" namespace tflite { namespace flex { @@ -57,8 +59,8 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteDelegate* delegate) { TfLiteStatus CopyFromBufferHandle(TfLiteContext* context, TfLiteDelegate* delegate, - TfLiteBufferHandle buffer_handle, void* data, - size_t size) { + TfLiteBufferHandle buffer_handle, + TfLiteTensor* output) { BufferMap* buffer_map = reinterpret_cast(delegate->data_)->GetBufferMap(context); @@ -68,15 +70,38 @@ TfLiteStatus CopyFromBufferHandle(TfLiteContext* context, } tensorflow::Tensor t = buffer_map->GetTensor(buffer_handle); + + if (output->type == kTfLiteString) { + if (t.dtype() != tensorflow::DT_STRING) { + context->ReportError(context, + "Inconsistent type for TF string tensor index %d.", + buffer_handle); + return kTfLiteError; + } + DynamicBuffer dynamic_buffer; + + auto tf_data = t.flat(); + for (int i = 0; i < t.NumElements(); ++i) { + dynamic_buffer.AddString(tf_data(i).data(), tf_data(i).size()); + } + + dynamic_buffer.WriteToTensor(output, /*new_shape=*/nullptr); + return kTfLiteOk; + } + tensorflow::StringPiece t_data = t.tensor_data(); - if (size != t_data.size()) { - context->ReportError( - context, "Not enough space to store TensorFlow's aligned buffer."); + if (output->bytes != t_data.size()) { + context->ReportError(context, + absl::StrCat("The given ", output->bytes, + " bytes are not enough to store " + "TensorFlow's aligned buffer of size ", + t_data.size(), " bytes.") + .c_str()); return kTfLiteError; } - memcpy(data, t_data.data(), t_data.size()); + memcpy(output->data.raw, t_data.data(), t_data.size()); return kTfLiteOk; } @@ -104,14 +129,13 @@ std::unique_ptr FlexDelegate::Create() { } FlexDelegate::FlexDelegate(std::unique_ptr delegate_data) - : TfLiteDelegate{ - /*data_=*/delegate_data.get(), - /*nullptr,*/ &flex::delegate::Prepare, - /*CopyFromBufferHandle=*/&flex::delegate::CopyFromBufferHandle, - /*CopyToBufferHandle=*/nullptr, - /*FreeBufferHandle=*/nullptr, - /*flags=*/kTfLiteDelegateFlagsAllowDynamicTensors}, - delegate_data_(std::move(delegate_data)) {} + : TfLiteDelegate(TfLiteDelegateCreate()), + delegate_data_(std::move(delegate_data)) { + data_ = delegate_data_.get(); + Prepare = &flex::delegate::Prepare; + CopyFromBufferHandle = &flex::delegate::CopyFromBufferHandle; + flags = kTfLiteDelegateFlagsAllowDynamicTensors; +} FlexDelegate::~FlexDelegate() {} diff --git a/tensorflow/lite/delegates/flex/delegate_data.cc b/tensorflow/lite/delegates/flex/delegate_data.cc index b62479a448..1483a53038 100644 --- a/tensorflow/lite/delegates/flex/delegate_data.cc +++ b/tensorflow/lite/delegates/flex/delegate_data.cc @@ -14,20 +14,21 @@ limitations under the License. ==============================================================================*/ #include "tensorflow/lite/delegates/flex/delegate_data.h" +#include "absl/memory/memory.h" #include "tensorflow/core/common_runtime/device_factory.h" #include "tensorflow/core/lib/core/status.h" namespace tflite { namespace flex { tensorflow::Status DelegateData::Create(std::unique_ptr* data) { - std::vector devices; + std::vector> devices; TF_RETURN_IF_ERROR(tensorflow::DeviceFactory::AddDevices( tensorflow::SessionOptions(), "/job:localhost/replica:0/task:0", &devices)); - std::unique_ptr device_mgr( - new tensorflow::DeviceMgr(devices)); + std::unique_ptr device_mgr = + absl::make_unique(std::move(devices)); // Note that Rendezvous is ref-counted so it will be automatically deleted. tensorflow::Rendezvous* rendezvous = new tensorflow::IntraProcessRendezvous(device_mgr.get()); diff --git a/tensorflow/lite/delegates/flex/delegate_test.cc b/tensorflow/lite/delegates/flex/delegate_test.cc index e13029d9a5..1b2f476f03 100644 --- a/tensorflow/lite/delegates/flex/delegate_test.cc +++ b/tensorflow/lite/delegates/flex/delegate_test.cc @@ -93,6 +93,25 @@ TEST_F(DelegateTest, NonFloatTypeInference) { ASSERT_EQ(GetType(2), kTfLiteInt32); } +TEST_F(DelegateTest, StringInference) { + AddTensors(3, {0, 1}, {2}, kTfLiteString, {2}); + + AddTfOp(testing::kAdd, {0, 1}, {2}); + + ConfigureDelegate(); + + SetShape(0, {2, 2}); + SetStringValues(0, {"1", "2", "3", "4"}); + SetShape(1, {2, 2}); + SetStringValues(1, {"4", "3", "2", "1"}); + + ASSERT_TRUE(Invoke()); + + ASSERT_THAT(GetShape(2), ElementsAre(2, 2)); + ASSERT_THAT(GetStringValues(2), ElementsAre("14", "23", "32", "41")); + ASSERT_EQ(GetType(2), kTfLiteString); +} + TEST_F(DelegateTest, MixedGraph) { AddTensors(9, {0, 3}, {8}, kTfLiteFloat32, {3}); diff --git a/tensorflow/lite/delegates/flex/kernel.cc b/tensorflow/lite/delegates/flex/kernel.cc index c4fe142dff..02da1d1a22 100644 --- a/tensorflow/lite/delegates/flex/kernel.cc +++ b/tensorflow/lite/delegates/flex/kernel.cc @@ -15,6 +15,12 @@ limitations under the License. #include "tensorflow/lite/delegates/flex/kernel.h" #include "flatbuffers/flexbuffers.h" // TF:flatbuffers +#include "tensorflow/core/common_runtime/eager/context.h" +#include "tensorflow/core/common_runtime/eager/execute.h" +#include "tensorflow/core/common_runtime/eager/tensor_handle.h" +#include "tensorflow/core/framework/node_def.pb.h" +#include "tensorflow/core/framework/node_def_util.h" +#include "tensorflow/core/lib/core/errors.h" #include "tensorflow/lite/builtin_ops.h" #include "tensorflow/lite/c/c_api_internal.h" #include "tensorflow/lite/context_util.h" @@ -22,11 +28,6 @@ limitations under the License. #include "tensorflow/lite/delegates/flex/util.h" #include "tensorflow/lite/kernels/kernel_util.h" #include "tensorflow/lite/string.h" -#include "tensorflow/core/common_runtime/eager/context.h" -#include "tensorflow/core/common_runtime/eager/execute.h" -#include "tensorflow/core/common_runtime/eager/tensor_handle.h" -#include "tensorflow/core/framework/node_def.pb.h" -#include "tensorflow/core/framework/node_def_util.h" // Note: this is part of TF Lite's Flex delegation code which is to be // completed soon. @@ -78,11 +79,18 @@ tensorflow::Status ExecuteFlexOp(tensorflow::EagerContext* eager_context, const std::vector& inputs, const std::vector& outputs) { const tensorflow::AttrTypeMap* attr_types; + bool is_function = false; TF_RETURN_WITH_CONTEXT_IF_ERROR( - tensorflow::AttrTypeMapForOp(op_name.c_str(), &attr_types), + tensorflow::AttrTypeMapForOp(op_name.c_str(), &attr_types, &is_function), " (while processing attributes of '", op_name, "')"); - - tensorflow::EagerOperation op(eager_context, op_name.c_str(), attr_types); + if (is_function) { + return tensorflow::errors::NotFound( + "Operation '", op_name, + "' is not registered. (while processing attributes of '", op_name, + "')"); + } + tensorflow::EagerOperation op(eager_context, op_name.c_str(), + /*is_function=*/false, attr_types); for (const auto& attr : nodedef.attr()) { op.MutableAttrs()->Set(attr.first, attr.second); } diff --git a/tensorflow/lite/delegates/flex/kernel_test.cc b/tensorflow/lite/delegates/flex/kernel_test.cc index f55759594d..efb7300b0b 100644 --- a/tensorflow/lite/delegates/flex/kernel_test.cc +++ b/tensorflow/lite/delegates/flex/kernel_test.cc @@ -59,12 +59,12 @@ class KernelTest : public testing::FlexModelTest { delegate_.CopyFromBufferHandle = [](TfLiteContext* context, TfLiteDelegate* delegate, TfLiteBufferHandle buffer_handle, - void* data, size_t size) { + TfLiteTensor* output) { auto* delegate_data = reinterpret_cast(delegate->data_); tensorflow::StringPiece values = delegate_data->GetBufferMap(context) ->GetTensor(buffer_handle) .tensor_data(); - memcpy(data, values.data(), values.size()); + memcpy(output->data.raw, values.data(), values.size()); return kTfLiteOk; }; CHECK(interpreter_->ModifyGraphWithDelegate(&delegate_) == kTfLiteOk); diff --git a/tensorflow/lite/delegates/flex/test_util.cc b/tensorflow/lite/delegates/flex/test_util.cc index 08feb349e6..aa24675a7b 100644 --- a/tensorflow/lite/delegates/flex/test_util.cc +++ b/tensorflow/lite/delegates/flex/test_util.cc @@ -25,6 +25,29 @@ namespace testing { bool FlexModelTest::Invoke() { return interpreter_->Invoke() == kTfLiteOk; } +void FlexModelTest::SetStringValues(int tensor_index, + const std::vector& values) { + DynamicBuffer dynamic_buffer; + for (const string& s : values) { + dynamic_buffer.AddString(s.data(), s.size()); + } + dynamic_buffer.WriteToTensor(interpreter_->tensor(tensor_index), + /*new_shape=*/nullptr); +} + +std::vector FlexModelTest::GetStringValues(int tensor_index) const { + std::vector result; + + TfLiteTensor* tensor = interpreter_->tensor(tensor_index); + auto num_strings = GetStringCount(tensor->data.raw); + for (size_t i = 0; i < num_strings; ++i) { + auto ref = GetString(tensor->data.raw, i); + result.push_back(string(ref.str, ref.len)); + } + + return result; +} + void FlexModelTest::SetShape(int tensor_index, const std::vector& values) { ASSERT_EQ(interpreter_->ResizeInputTensor(tensor_index, values), kTfLiteOk); ASSERT_EQ(interpreter_->AllocateTensors(), kTfLiteOk); @@ -95,12 +118,22 @@ void FlexModelTest::AddTfOp(TfOpType op, const std::vector& inputs, return " attr{ key: '" + key + "' value {" + value + "}}"; }; - // Crude type attribution, will need fleshing out as more tests are added. - // TODO(b/113613439): Use nodedef string utilities to properly handle - // all types. - string type_attribute = attr("T", "type: DT_FLOAT"); - if (interpreter_->tensor(inputs[0])->type == kTfLiteInt32) { - type_attribute = attr("T", "type: DT_INT32"); + string type_attribute; + switch (interpreter_->tensor(inputs[0])->type) { + case kTfLiteInt32: + type_attribute = attr("T", "type: DT_INT32"); + break; + case kTfLiteFloat32: + type_attribute = attr("T", "type: DT_FLOAT"); + break; + case kTfLiteString: + type_attribute = attr("T", "type: DT_STRING"); + break; + default: + // TODO(b/113613439): Use nodedef string utilities to properly handle all + // types. + LOG(FATAL) << "Type not supported"; + break; } if (op == kUnpack) { diff --git a/tensorflow/lite/delegates/flex/test_util.h b/tensorflow/lite/delegates/flex/test_util.h index 4d3f5ad096..2cc2dc30e9 100644 --- a/tensorflow/lite/delegates/flex/test_util.h +++ b/tensorflow/lite/delegates/flex/test_util.h @@ -63,11 +63,13 @@ class FlexModelTest : public ::testing::Test { void SetValues(int tensor_index, const std::vector& values) { SetTypedValues(tensor_index, values); } + void SetStringValues(int tensor_index, const std::vector& values); // Returns the tensor's values at the given index. std::vector GetValues(int tensor_index) { return GetTypedValues(tensor_index); } + std::vector GetStringValues(int tensor_index) const; // Sets the tensor's shape at the given index. void SetShape(int tensor_index, const std::vector& values); diff --git a/tensorflow/lite/delegates/flex/util.cc b/tensorflow/lite/delegates/flex/util.cc index c786ffa1a2..c995b360f9 100644 --- a/tensorflow/lite/delegates/flex/util.cc +++ b/tensorflow/lite/delegates/flex/util.cc @@ -66,6 +66,8 @@ TF_DataType GetTensorFlowDataType(TfLiteType type) { return TF_INT32; case kTfLiteUInt8: return TF_UINT8; + case kTfLiteInt8: + return TF_INT8; case kTfLiteInt64: return TF_INT64; case kTfLiteComplex64: @@ -87,6 +89,8 @@ TfLiteType GetTensorFlowLiteType(TF_DataType type) { return kTfLiteInt32; case TF_UINT8: return kTfLiteUInt8; + case TF_INT8: + return kTfLiteInt8; case TF_INT64: return kTfLiteInt64; case TF_COMPLEX64: diff --git a/tensorflow/lite/delegates/nnapi/nnapi_delegate.cc b/tensorflow/lite/delegates/nnapi/nnapi_delegate.cc index 9690c65921..4fe07004a8 100644 --- a/tensorflow/lite/delegates/nnapi/nnapi_delegate.cc +++ b/tensorflow/lite/delegates/nnapi/nnapi_delegate.cc @@ -1141,7 +1141,7 @@ class NNAPIDelegateKernel { TfLiteDelegate* NnApiDelegate() { static TfLiteDelegate delegate = { .data_ = nullptr, - .flags = kTfLiteDelegateFlagsAllowDynamicTensors, + .flags = kTfLiteDelegateFlagsNone, .Prepare = [](TfLiteContext* context, TfLiteDelegate* delegate) -> TfLiteStatus { // Do not check nodes_ if NN API is unavailable. diff --git a/tensorflow/lite/delegates/nnapi/nnapi_delegate_test.cc b/tensorflow/lite/delegates/nnapi/nnapi_delegate_test.cc index 84a0a6a1d1..ca48af0c95 100644 --- a/tensorflow/lite/delegates/nnapi/nnapi_delegate_test.cc +++ b/tensorflow/lite/delegates/nnapi/nnapi_delegate_test.cc @@ -34,6 +34,11 @@ class SingleOpModelWithNNAPI : public SingleOpModel { interpreter->ModifyGraphWithDelegate(NnApiDelegate()); }); } + + TfLiteStatus ResizeInputTensor(int tensor_index, + const std::vector& dims) { + return interpreter_->ResizeInputTensor(tensor_index, dims); + } }; class FloatAddOpModel : public SingleOpModelWithNNAPI { @@ -97,6 +102,17 @@ TEST(NNAPIDelegate, AddWithRelu) { EXPECT_THAT(m.GetOutput(), ElementsAreArray({0.0, 0.4, 1.0, 1.3})); } +// Verify that resize attempts fail. +// TODO(b/113110851): Verify success after the delegate supports resizing. +TEST(NNAPIDelegate, ResizeFails) { + FloatAddOpModel m({TensorType_FLOAT32, {1, 2, 2, 1}}, + {TensorType_FLOAT32, {1, 2, 2, 1}}, + {TensorType_FLOAT32, {}}, ActivationFunctionType_NONE); + m.PopulateTensor(m.input1(), {-2.0, 0.2, 0.7, 0.8}); + m.PopulateTensor(m.input2(), {0.1, 0.2, 0.3, 0.5}); + EXPECT_EQ(m.ResizeInputTensor(m.input1(), {1, 3, 3, 1}), kTfLiteError); +} + class FloatMulOpModel : public SingleOpModelWithNNAPI { public: FloatMulOpModel(const TensorData& input1, const TensorData& input2, diff --git a/tensorflow/lite/examples/ios/camera/CameraExampleViewController.h b/tensorflow/lite/examples/ios/camera/CameraExampleViewController.h index 6bc94e9502..fb5800e86d 100644 --- a/tensorflow/lite/examples/ios/camera/CameraExampleViewController.h +++ b/tensorflow/lite/examples/ios/camera/CameraExampleViewController.h @@ -17,8 +17,8 @@ #include -#include "tensorflow/lite/kernels/register.h" -#include "tensorflow/lite/model.h" +#include "tensorflow/contrib/lite/kernels/register.h" +#include "tensorflow/contrib/lite/model.h" @interface CameraExampleViewController : UIViewController { diff --git a/tensorflow/lite/examples/ios/camera/CameraExampleViewController.mm b/tensorflow/lite/examples/ios/camera/CameraExampleViewController.mm index 1e6725592b..996cff2616 100644 --- a/tensorflow/lite/examples/ios/camera/CameraExampleViewController.mm +++ b/tensorflow/lite/examples/ios/camera/CameraExampleViewController.mm @@ -23,10 +23,10 @@ #include #include -#include "tensorflow/lite/kernels/register.h" -#include "tensorflow/lite/model.h" -#include "tensorflow/lite/string_util.h" -#include "tensorflow/lite/op_resolver.h" +#include "tensorflow/contrib/lite/kernels/register.h" +#include "tensorflow/contrib/lite/model.h" +#include "tensorflow/contrib/lite/string_util.h" +#include "tensorflow/contrib/lite/op_resolver.h" #define LOG(x) std::cerr diff --git a/tensorflow/lite/examples/ios/camera/Podfile b/tensorflow/lite/examples/ios/camera/Podfile index f460693122..96a0d23426 100644 --- a/tensorflow/lite/examples/ios/camera/Podfile +++ b/tensorflow/lite/examples/ios/camera/Podfile @@ -2,4 +2,4 @@ platform :ios, '8.0' inhibit_all_warnings! target 'tflite_camera_example' - pod 'TensorFlowLite', '1.10.1' + pod 'TensorFlowLite', '1.12.0' diff --git a/tensorflow/lite/examples/ios/simple/Podfile b/tensorflow/lite/examples/ios/simple/Podfile index ddb77088d9..931b72c1f5 100644 --- a/tensorflow/lite/examples/ios/simple/Podfile +++ b/tensorflow/lite/examples/ios/simple/Podfile @@ -2,4 +2,4 @@ platform :ios, '8.0' inhibit_all_warnings! target 'tflite_simple_example' - pod 'TensorFlowLite', '1.10.1' + pod 'TensorFlowLite', '1.12.0' diff --git a/tensorflow/lite/examples/ios/simple/RunModelViewController.mm b/tensorflow/lite/examples/ios/simple/RunModelViewController.mm index e5764944f6..650c73f732 100644 --- a/tensorflow/lite/examples/ios/simple/RunModelViewController.mm +++ b/tensorflow/lite/examples/ios/simple/RunModelViewController.mm @@ -22,10 +22,10 @@ #include #include -#include "tensorflow/lite/kernels/register.h" -#include "tensorflow/lite/model.h" -#include "tensorflow/lite/string_util.h" -#include "tensorflow/lite/op_resolver.h" +#include "tensorflow/contrib/lite/kernels/register.h" +#include "tensorflow/contrib/lite/model.h" +#include "tensorflow/contrib/lite/string_util.h" +#include "tensorflow/contrib/lite/op_resolver.h" #include "ios_image_load.h" diff --git a/tensorflow/lite/experimental/examples/lstm/unidirectional_sequence_lstm_test.py b/tensorflow/lite/experimental/examples/lstm/unidirectional_sequence_lstm_test.py index eeb48d1231..9c00d0501a 100644 --- a/tensorflow/lite/experimental/examples/lstm/unidirectional_sequence_lstm_test.py +++ b/tensorflow/lite/experimental/examples/lstm/unidirectional_sequence_lstm_test.py @@ -111,7 +111,7 @@ class UnidirectionalSequenceLstmTest(test_util.TensorFlowTestCase): # Initialize variables init = tf.global_variables_initializer() - sess.run(init) + self.evaluate(init) for _ in range(TRAIN_STEPS): batch_x, batch_y = self.mnist.train.next_batch( batch_size=self.batch_size, shuffle=False) diff --git a/tensorflow/lite/experimental/micro/README.md b/tensorflow/lite/experimental/micro/README.md index 673daed74c..cc2a62cb8a 100644 --- a/tensorflow/lite/experimental/micro/README.md +++ b/tensorflow/lite/experimental/micro/README.md @@ -126,3 +126,45 @@ debug logs here, along with the magic string `~~~ALL TESTS PASSED~~~`. This is the exact same code as before, just compiled and run on the STM32F103 rather than your desktop. We hope that the simplicity of this testing approach will help make adding support for new platforms as easy as possible. + +## Building for Apollo3 + +Follow these steps to get the pushbutton yes/no example working on Apollo 3: + +1. Make sure to run the "Getting Started" section before performing the following steps +2. Download Apollo3-SDK-2018.08.13 and place in tensorflow/lite/experimental/micro/tools/make/downloads +3. Copy and prepare files by running tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/prep_apollo3_files.sh from the toplevel of git +4. Install [Segger JLink tools](https://www.segger.com/downloads/jlink/) +5. Make sure the [GNU Arm Embedded Toolchain (gcc-arm-none-eabi-7-2018-q2-update)](https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads) is installed to tensorflow/lite/experimental/micro/tools/make/downloads + 1. Confirm directory is in $PATH +6. Compile the project with the following command: make -f tensorflow/lite/experimental/micro/tools/make/Makefile TARGET=apollo3evb pushbutton_cmsis_speech_test_bin +7. Connect the Apollo3 EVB (with mic shield) to the computer and power it on +8. Start the GDB server in a new terminal with the following command: JLinkGDBServer -select USB -device AMA3B1KK-KBR -endian little -if SWD -speed 1000 -noir -noLocalhostOnly + 1. The command has run successfully if you see the message "Waiting for GDB connection" +9. Back in the original terminal, run the program via the debugger + 1. Navigate to tensorflow/lite/experimental/micro/examples/micro_speech/apollo3 + 2. Start gdb by entering the following command: arm-none-eabi-gdb + 3. Run the command script by entering the following command: source pushbutton_cmsis_scores.cmd. This script does the following: + 1. Load the binary created in step 6 + 2. Set a breakpoint after inference scores have been computed + 3. Tell the debugger what variables should be printed out at this breakpoint + 4. Begin program execution + 5. Press Ctrl+c to exit + 4. Press BTN2. An LED will flash for 1 second. Speak your utterance during this one second + 5. The debugger will print out four numbers. They are the probabilites for 1) no speech, 2) unknown speech, 3) yes, 4) no + 6. The EVB LEDs will indicate detection. + 1. LED0 (rightmost LED) - ON when capturing 1sec of audio + 2. LED1 - ON when detecting silence + 3. LED2 - ON when detecting UNKNOWN utterance + 4. LED3 - ON when detecting YES utterance + 5. LED4 (leftmost LED) - ON when detecting NO utterance + +### Additional Apollo3 Instructions + +To flash a part with JFlash Lite, do the following: +1. At the command line: JFlashLiteExe +2. Device = AMA3B1KK-KBR +3. Interface = SWD at 1000 kHz +4. Data file = tensorflow/lite/experimental/micro/tools/make/gen/apollo3evb_cortex-m4/bin/pushbutton_cmsis_speech_test.bin +5. Prog Addr = 0x0000C000 + diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/.gitignore b/tensorflow/lite/experimental/micro/examples/micro_speech/.gitignore new file mode 100644 index 0000000000..d8dd7532ab --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/.gitignore @@ -0,0 +1 @@ +*.wav diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/BUILD b/tensorflow/lite/experimental/micro/examples/micro_speech/BUILD index 07fb876411..799b2e5a5d 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/BUILD +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/BUILD @@ -10,18 +10,46 @@ load( "tflite_micro_cc_test", ) -tflite_micro_cc_test( - name = "micro_speech_test", +cc_library( + name = "model_settings", + srcs = [ + "model_settings.cc", + ], + hdrs = [ + "model_settings.h", + ], +) + +cc_library( + name = "tiny_conv_model_data", srcs = [ - "micro_speech_test.cc", - "no_features_data.cc", - "no_features_data.h", "tiny_conv_model_data.cc", + ], + hdrs = [ "tiny_conv_model_data.h", + ], +) + +cc_library( + name = "features_test_data", + srcs = [ + "no_features_data.cc", "yes_features_data.cc", + ], + hdrs = [ + "no_features_data.h", "yes_features_data.h", ], +) + +tflite_micro_cc_test( + name = "micro_speech_test", + srcs = [ + "micro_speech_test.cc", + ], deps = [ + ":features_test_data", + ":tiny_conv_model_data", "//tensorflow/lite:schema_fbs_version", "//tensorflow/lite/experimental/micro:micro_framework", "//tensorflow/lite/experimental/micro/kernels:all_ops_resolver", @@ -31,46 +59,185 @@ tflite_micro_cc_test( ], ) -tflite_micro_cc_test( - name = "preprocessor_reference_test", +cc_library( + name = "preprocessor_test_data", srcs = [ "no_30ms_sample_data.cc", - "no_30ms_sample_data.h", "no_power_spectrum_data.cc", + "yes_30ms_sample_data.cc", + "yes_power_spectrum_data.cc", + ], + hdrs = [ + "no_30ms_sample_data.h", "no_power_spectrum_data.h", + "yes_30ms_sample_data.h", + "yes_power_spectrum_data.h", + ], +) + +cc_library( + name = "preprocessor_reference", + srcs = [ "preprocessor.cc", + ], + hdrs = [ "preprocessor.h", + ], + deps = [ + ":model_settings", + "//tensorflow/lite/c:c_api_internal", + "//tensorflow/lite/experimental/micro:micro_framework", + ], +) + +tflite_micro_cc_test( + name = "preprocessor_reference_test", + srcs = [ "preprocessor_test.cc", - "yes_30ms_sample_data.cc", - "yes_30ms_sample_data.h", - "yes_power_spectrum_data.cc", - "yes_power_spectrum_data.h", ], deps = [ + ":model_settings", + ":preprocessor_reference", + ":preprocessor_test_data", "//tensorflow/lite/c:c_api_internal", "//tensorflow/lite/experimental/micro:micro_framework", "//tensorflow/lite/experimental/micro/testing:micro_test", ], ) -tflite_micro_cc_test( - name = "preprocessor_fixed_test", +cc_library( + name = "preprocessor_fixed", srcs = [ "fixed_point/preprocessor.cc", - "no_30ms_sample_data.cc", - "no_30ms_sample_data.h", - "no_power_spectrum_data.cc", - "no_power_spectrum_data.h", + ], + hdrs = [ "preprocessor.h", + ], + deps = [ + ":model_settings", + "//tensorflow/lite/c:c_api_internal", + "//tensorflow/lite/experimental/micro:micro_framework", + ], +) + +tflite_micro_cc_test( + name = "preprocessor_fixed_test", + srcs = [ "preprocessor_test.cc", - "yes_30ms_sample_data.cc", - "yes_30ms_sample_data.h", - "yes_power_spectrum_data.cc", - "yes_power_spectrum_data.h", ], deps = [ + ":model_settings", + ":preprocessor_fixed", + ":preprocessor_test_data", "//tensorflow/lite/c:c_api_internal", "//tensorflow/lite/experimental/micro:micro_framework", "//tensorflow/lite/experimental/micro/testing:micro_test", ], ) + +cc_library( + name = "audio_provider", + srcs = [ + "audio_provider.cc", + ], + hdrs = [ + "audio_provider.h", + ], + deps = [ + ":model_settings", + "//tensorflow/lite/c:c_api_internal", + "//tensorflow/lite/experimental/micro:micro_framework", + ], +) + +tflite_micro_cc_test( + name = "audio_provider_test", + srcs = [ + "audio_provider_test.cc", + ], + deps = [ + ":audio_provider", + ":model_settings", + "//tensorflow/lite/c:c_api_internal", + "//tensorflow/lite/experimental/micro:micro_framework", + "//tensorflow/lite/experimental/micro/testing:micro_test", + ], +) + +cc_library( + name = "feature_provider", + srcs = [ + "feature_provider.cc", + ], + hdrs = [ + "feature_provider.h", + ], + deps = [ + ":audio_provider", + ":model_settings", + ":preprocessor_reference", + ":timer", + "//tensorflow/lite/c:c_api_internal", + "//tensorflow/lite/experimental/micro:micro_framework", + ], +) + +tflite_micro_cc_test( + name = "feature_provider_test", + srcs = [ + "feature_provider_test.cc", + ], + deps = [ + ":audio_provider", + ":feature_provider", + ":model_settings", + ":timer", + "//tensorflow/lite/c:c_api_internal", + "//tensorflow/lite/experimental/micro:micro_framework", + "//tensorflow/lite/experimental/micro/testing:micro_test", + ], +) + +cc_library( + name = "timer", + srcs = [ + "timer.cc", + ], + hdrs = [ + "timer.h", + ], +) + +tflite_micro_cc_test( + name = "timer_test", + srcs = [ + "timer_test.cc", + ], + deps = [ + ":timer", + "//tensorflow/lite/c:c_api_internal", + "//tensorflow/lite/experimental/micro:micro_framework", + "//tensorflow/lite/experimental/micro/testing:micro_test", + ], +) + +cc_binary( + name = "micro_speech", + srcs = [ + "main.cc", + ], + deps = [ + ":audio_provider", + ":feature_provider", + ":features_test_data", + ":model_settings", + ":preprocessor_reference", + ":timer", + ":tiny_conv_model_data", + "//tensorflow/lite:schema_fbs_version", + "//tensorflow/lite/experimental/micro:micro_framework", + "//tensorflow/lite/experimental/micro/kernels:all_ops_resolver", + "//tensorflow/lite/experimental/micro/kernels:micro_ops", + "//tensorflow/lite/schema:schema_fbs", + ], +) diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/README.md b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/README.md new file mode 100644 index 0000000000..fde48374c8 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/README.md @@ -0,0 +1,10 @@ +# Description of files + +* **arm_cmplx_mag_squared_q10p6.c**: Modified version of the ARM CMSIS function [arm_cmplx_mag_squared.c](http://arm-software.github.io/CMSIS_5/DSP/html/group__cmplx__mag__squared.html#ga45537f576102d960d467eb722b8431f2). The modification is that we have changed the amount of right-shift to make sure our data is in the correct range. We redistribute because the original content was created with the Apache 2.0 license. +* **arm_cmplx_mag_squared_q10p6.h**: Header file for arm_cmplx_mag_squared_q10p6.c +* **create_constants.py**: Python file used to create hanning.cc, hanning.h, sin_1k.cc, and sin_1k.h +* **hanning.cc**: Precomputed [Hann window](https://en.wikipedia.org/wiki/Hann_function) for use in the preprocessor. This file is created in ../create_constants.py +* **hanning.h**: Header file fro hanning.cc +* **preprocessor.cc**: CMSIS version of the preprocessor +* **sin_1k.cc**: A 1 kHZ sinusoid used for comparing the CMSIS preprocessor with the Micro-Lite fixed_point preprocessor +* **sin_1k.h**: Header file for sin_1k.cc diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/arm_cmplx_mag_squared_q10p6.c b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/arm_cmplx_mag_squared_q10p6.c new file mode 100644 index 0000000000..b050f6048d --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/arm_cmplx_mag_squared_q10p6.c @@ -0,0 +1,141 @@ +/* This file is a modification of the ARM CMSIS library file arm_cmplx_mag_squared_q15.c + * We have retained the original copyright and header information, in + * accordance with the Apache 2.0 license terms. + */ + +/* ---------------------------------------------------------------------- + * Project: CMSIS DSP Library + * Title: arm_cmplx_mag_squared_q15.c + * Description: Q15 complex magnitude squared + * + * $Date: 27. January 2017 + * $Revision: V.1.5.1 + * + * Target Processor: Cortex-M cores + * -------------------------------------------------------------------- */ +/* + * Copyright (C) 2010-2017 ARM Limited or its affiliates. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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 + * + * 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 "arm_math.h" + +/** + * @ingroup groupCmplxMath + */ + +/** + * @addtogroup cmplx_mag_squared + * @{ + */ + +/** + * @brief Q15 complex magnitude squared + * @param *pSrc points to the complex input vector + * @param *pDst points to the real output vector + * @param numSamples number of complex samples in the input vector + * @return none. + * + * Scaling and Overflow Behavior: + * \par + * The function implements 1.15 by 1.15 multiplications and finally output is converted into 3.13 format. + */ + +void arm_cmplx_mag_squared_q10p6( + q15_t * pSrc, + q15_t * pDst, + uint32_t numSamples) +{ + q31_t acc0, acc1; /* Accumulators */ + +#if defined (ARM_MATH_DSP) + + /* Run the below code for Cortex-M4 and Cortex-M3 */ + uint32_t blkCnt; /* loop counter */ + q31_t in1, in2, in3, in4; + q31_t acc2, acc3; + + /*loop Unrolling */ + blkCnt = numSamples >> 2U; + + /* First part of the processing with loop unrolling. Compute 4 outputs at a time. + ** a second loop below computes the remaining 1 to 3 samples. */ + while (blkCnt > 0U) + { + /* C[0] = (A[0] * A[0] + A[1] * A[1]) */ + in1 = *__SIMD32(pSrc)++; + in2 = *__SIMD32(pSrc)++; + in3 = *__SIMD32(pSrc)++; + in4 = *__SIMD32(pSrc)++; + + acc0 = __SMUAD(in1, in1); + acc1 = __SMUAD(in2, in2); + acc2 = __SMUAD(in3, in3); + acc3 = __SMUAD(in4, in4); + + /* store the result in 3.13 format in the destination buffer. */ + *pDst++ = (q15_t) (acc0 >> 6); + *pDst++ = (q15_t) (acc1 >> 6); + *pDst++ = (q15_t) (acc2 >> 6); + *pDst++ = (q15_t) (acc3 >> 6); + + /* Decrement the loop counter */ + blkCnt--; + } + + /* If the numSamples is not a multiple of 4, compute any remaining output samples here. + ** No loop unrolling is used. */ + blkCnt = numSamples % 0x4U; + + while (blkCnt > 0U) + { + /* C[0] = (A[0] * A[0] + A[1] * A[1]) */ + in1 = *__SIMD32(pSrc)++; + acc0 = __SMUAD(in1, in1); + + /* store the result in 3.13 format in the destination buffer. */ + *pDst++ = (q15_t) (acc0 >> 6); + + /* Decrement the loop counter */ + blkCnt--; + } + +#else + + /* Run the below code for Cortex-M0 */ + q15_t real, imag; /* Temporary variables to store real and imaginary values */ + + while (numSamples > 0U) + { + /* out = ((real * real) + (imag * imag)) */ + real = *pSrc++; + imag = *pSrc++; + acc0 = (real * real); + acc1 = (imag * imag); + /* store the result in 3.13 format in the destination buffer. */ + *pDst++ = (q15_t) (((q63_t) acc0 + acc1) >> 6); + + /* Decrement the loop counter */ + numSamples--; + } + +#endif /* #if defined (ARM_MATH_DSP) */ + +} + +/** + * @} end of cmplx_mag_squared group + */ diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/arm_cmplx_mag_squared_q10p6.h b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/arm_cmplx_mag_squared_q10p6.h new file mode 100644 index 0000000000..24144615cc --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/arm_cmplx_mag_squared_q10p6.h @@ -0,0 +1,33 @@ +/* This file is a modification of the ARM CMSIS library file arm_math.h + * We have retained the original copyright and header information, in + * accordance with the Apache 2.0 license terms. + */ + +/****************************************************************************** + * @file arm_math.h + * @brief Public header file for CMSIS DSP LibraryU + * @version V1.5.3 + * @date 10. January 2018 + ******************************************************************************/ +/* + * Copyright (C) 2010-2017 ARM Limited or its affiliates. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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 + * + * 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. + */ + +void arm_cmplx_mag_squared_q10p6( + q15_t * pSrc, + q15_t * pDst, + uint32_t numSamples); diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/create_constants.py b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/create_constants.py new file mode 100755 index 0000000000..c7cf8bf61b --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/create_constants.py @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# 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 +# +# 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. + +import soundfile as sf +import numpy as np + +def to_cc(x, varname, directory='', scale_factor=1): + x = (x/np.max(np.abs(x)))*32768*scale_factor + x[x>32767] = 32767 + x[x<-32768] = -32768 + x = x.astype(int) + x = [str(v) if i%10!=0 else '\n '+str(v) for i,v in enumerate(x)] + + cmsis_path = "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS" + xstr = '#include "{}/{}.h"\n\n'.format(cmsis_path, varname) + xstr += 'const int g_{}_size = {};\n'.format(varname, len(x)) + xstr += 'const int16_t g_{}[{}] = {{{}}};\n'.format(varname, len(x), ', '.join(x)) + + with open(directory+varname+'.cc','w') as f: + f.write(xstr) + +def to_h(x, varname, directory=''): + tf_prepend = 'TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_' + xstr = '#ifndef {}{}_H_\n'.format(tf_prepend, varname.upper()) + xstr += '#define {}{}_H_\n\n'.format(tf_prepend, varname.upper()) + xstr += '#include \n\n' + xstr += 'extern const int g_{}_size;\n'.format(varname) + xstr += 'extern const int16_t g_{}[];\n\n'.format(varname) + xstr += '#endif' + + with open(directory+varname+'.h','w') as f: + f.write(xstr) + +#x = sf.read('yes_f2e59fea_nohash_1.wav')[0] +#to_cc(x, 'yes_waveform') +#to_h(x, 'yes_waveform') +# +#x = sf.read('no_f9643d42_nohash_4.wav')[0] +#to_cc(x, 'no_waveform') +#to_h(x, 'no_waveform') + + +# 30ms of data @ 16 kHz = 480 samples +hann = np.hanning(int(16000*0.03)) # Window 30ms of data +to_cc(hann, 'hanning', directory='./') +to_h(hann, 'hanning', directory='./') + +t = np.arange(16000.*0.03)/16000. +sin1k = np.sin(2*np.pi*1000*t) # Factor of 10 because micro preprocessing overflows otherwise +to_cc(sin1k, 'sin_1k', directory='./', scale_factor=0.1) +to_h(sin1k, 'sin_1k', directory='./') diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hann.h b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hann.h deleted file mode 100644 index b610f79190..0000000000 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hann.h +++ /dev/null @@ -1 +0,0 @@ -q15_t hann[480] = {0, 1, 5, 12, 22, 35, 50, 69, 90, 114, 140, 170, 202, 237, 275, 316, 359, 405, 454, 506, 560, 617, 677, 739, 805, 873, 943, 1016, 1092, 1171, 1252, 1335, 1422, 1511, 1602, 1696, 1793, 1892, 1993, 2097, 2203, 2312, 2424, 2537, 2653, 2772, 2893, 3016, 3141, 3269, 3399, 3531, 3665, 3802, 3941, 4081, 4224, 4370, 4517, 4666, 4817, 4970, 5125, 5283, 5442, 5603, 5765, 5930, 6096, 6264, 6434, 6606, 6779, 6954, 7131, 7309, 7488, 7670, 7852, 8037, 8222, 8409, 8598, 8788, 8979, 9171, 9364, 9559, 9755, 9952, 10151, 10350, 10550, 10751, 10954, 11157, 11361, 11566, 11772, 11979, 12186, 12394, 12603, 12812, 13022, 13233, 13444, 13656, 13868, 14080, 14293, 14507, 14720, 14934, 15148, 15363, 15577, 15792, 16007, 16222, 16437, 16652, 16866, 17081, 17296, 17510, 17725, 17939, 18153, 18366, 18579, 18792, 19004, 19216, 19428, 19639, 19849, 20059, 20268, 20476, 20684, 20891, 21097, 21302, 21507, 21711, 21913, 22115, 22316, 22516, 22715, 22912, 23109, 23304, 23498, 23691, 23883, 24074, 24263, 24450, 24637, 24822, 25005, 25187, 25368, 25547, 25724, 25900, 26074, 26246, 26417, 26586, 26753, 26919, 27082, 27244, 27404, 27562, 27718, 27873, 28025, 28175, 28323, 28469, 28613, 28755, 28895, 29033, 29168, 29301, 29433, 29561, 29688, 29812, 29934, 30054, 30171, 30286, 30398, 30508, 30616, 30721, 30824, 30924, 31022, 31117, 31210, 31300, 31388, 31473, 31555, 31635, 31712, 31787, 31858, 31928, 31994, 32058, 32119, 32178, 32233, 32286, 32337, 32384, 32429, 32471, 32510, 32547, 32580, 32611, 32639, 32665, 32687, 32707, 32724, 32738, 32749, 32758, 32763, 32766, 32766, 32763, 32758, 32749, 32738, 32724, 32707, 32687, 32665, 32639, 32611, 32580, 32547, 32510, 32471, 32429, 32384, 32337, 32286, 32233, 32178, 32119, 32058, 31994, 31928, 31858, 31787, 31712, 31635, 31555, 31473, 31388, 31300, 31210, 31117, 31022, 30924, 30824, 30721, 30616, 30508, 30398, 30286, 30171, 30054, 29934, 29812, 29688, 29561, 29433, 29301, 29168, 29033, 28895, 28755, 28613, 28469, 28323, 28175, 28025, 27873, 27718, 27562, 27404, 27244, 27082, 26919, 26753, 26586, 26417, 26246, 26074, 25900, 25724, 25547, 25368, 25187, 25005, 24822, 24637, 24450, 24263, 24074, 23883, 23691, 23498, 23304, 23109, 22912, 22715, 22516, 22316, 22115, 21913, 21711, 21507, 21302, 21097, 20891, 20684, 20476, 20268, 20059, 19849, 19639, 19428, 19216, 19004, 18792, 18579, 18366, 18153, 17939, 17725, 17510, 17296, 17081, 16866, 16652, 16437, 16222, 16007, 15792, 15577, 15363, 15148, 14934, 14720, 14507, 14293, 14080, 13868, 13656, 13444, 13233, 13022, 12812, 12603, 12394, 12186, 11979, 11772, 11566, 11361, 11157, 10954, 10751, 10550, 10350, 10151, 9952, 9755, 9559, 9364, 9171, 8979, 8788, 8598, 8409, 8222, 8037, 7852, 7670, 7488, 7309, 7131, 6954, 6779, 6606, 6434, 6264, 6096, 5930, 5765, 5603, 5442, 5283, 5125, 4970, 4817, 4666, 4517, 4370, 4224, 4081, 3941, 3802, 3665, 3531, 3399, 3269, 3141, 3016, 2893, 2772, 2653, 2537, 2424, 2312, 2203, 2097, 1993, 1892, 1793, 1696, 1602, 1511, 1422, 1335, 1252, 1171, 1092, 1016, 943, 873, 805, 739, 677, 617, 560, 506, 454, 405, 359, 316, 275, 237, 202, 170, 140, 114, 90, 69, 50, 35, 22, 12, 5, 1, 0}; diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.cc new file mode 100644 index 0000000000..32aa5b2b7e --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.cc @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: Apache-2.0 + * + * 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 + * + * 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/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.h" + +const int g_hanning_size = 480; +const int16_t g_hanning[480] = { + 0, 1, 5, 12, 22, 35, 50, 69, 90, 114, + 140, 170, 202, 237, 275, 316, 359, 405, 454, 506, + 560, 617, 677, 740, 805, 873, 943, 1016, 1092, 1171, + 1252, 1336, 1422, 1511, 1602, 1696, 1793, 1892, 1993, 2097, + 2204, 2312, 2424, 2537, 2653, 2772, 2893, 3016, 3141, 3269, + 3399, 3531, 3665, 3802, 3941, 4082, 4225, 4370, 4517, 4666, + 4817, 4971, 5126, 5283, 5442, 5603, 5765, 5930, 6096, 6265, + 6435, 6606, 6779, 6954, 7131, 7309, 7489, 7670, 7853, 8037, + 8223, 8410, 8598, 8788, 8979, 9171, 9365, 9560, 9756, 9953, + 10151, 10350, 10551, 10752, 10954, 11157, 11362, 11567, 11772, 11979, + 12186, 12395, 12603, 12813, 13023, 13233, 13445, 13656, 13868, 14081, + 14294, 14507, 14721, 14935, 15149, 15363, 15578, 15793, 16008, 16222, + 16437, 16652, 16867, 17082, 17297, 17511, 17725, 17939, 18153, 18367, + 18580, 18793, 19005, 19217, 19428, 19639, 19850, 20059, 20269, 20477, + 20685, 20892, 21098, 21303, 21508, 21712, 21914, 22116, 22317, 22517, + 22716, 22913, 23110, 23305, 23499, 23692, 23884, 24075, 24264, 24451, + 24638, 24823, 25006, 25188, 25369, 25548, 25725, 25901, 26075, 26247, + 26418, 26587, 26754, 26920, 27083, 27245, 27405, 27563, 27719, 27874, + 28026, 28176, 28324, 28470, 28614, 28756, 28896, 29034, 29169, 29303, + 29434, 29563, 29689, 29813, 29935, 30055, 30172, 30287, 30400, 30510, + 30617, 30723, 30825, 30926, 31023, 31119, 31211, 31301, 31389, 31474, + 31556, 31636, 31713, 31788, 31860, 31929, 31996, 32059, 32121, 32179, + 32235, 32288, 32338, 32386, 32430, 32472, 32512, 32548, 32582, 32613, + 32641, 32666, 32689, 32708, 32725, 32739, 32751, 32759, 32765, 32767, + 32767, 32765, 32759, 32751, 32739, 32725, 32708, 32689, 32666, 32641, + 32613, 32582, 32548, 32512, 32472, 32430, 32386, 32338, 32288, 32235, + 32179, 32121, 32059, 31996, 31929, 31860, 31788, 31713, 31636, 31556, + 31474, 31389, 31301, 31211, 31119, 31023, 30926, 30825, 30723, 30617, + 30510, 30400, 30287, 30172, 30055, 29935, 29813, 29689, 29563, 29434, + 29303, 29169, 29034, 28896, 28756, 28614, 28470, 28324, 28176, 28026, + 27874, 27719, 27563, 27405, 27245, 27083, 26920, 26754, 26587, 26418, + 26247, 26075, 25901, 25725, 25548, 25369, 25188, 25006, 24823, 24638, + 24451, 24264, 24075, 23884, 23692, 23499, 23305, 23110, 22913, 22716, + 22517, 22317, 22116, 21914, 21712, 21508, 21303, 21098, 20892, 20685, + 20477, 20269, 20059, 19850, 19639, 19428, 19217, 19005, 18793, 18580, + 18367, 18153, 17939, 17725, 17511, 17297, 17082, 16867, 16652, 16437, + 16222, 16008, 15793, 15578, 15363, 15149, 14935, 14721, 14507, 14294, + 14081, 13868, 13656, 13445, 13233, 13023, 12813, 12603, 12395, 12186, + 11979, 11772, 11567, 11362, 11157, 10954, 10752, 10551, 10350, 10151, + 9953, 9756, 9560, 9365, 9171, 8979, 8788, 8598, 8410, 8223, + 8037, 7853, 7670, 7489, 7309, 7131, 6954, 6779, 6606, 6435, + 6265, 6096, 5930, 5765, 5603, 5442, 5283, 5126, 4971, 4817, + 4666, 4517, 4370, 4225, 4082, 3941, 3802, 3665, 3531, 3399, + 3269, 3141, 3016, 2893, 2772, 2653, 2537, 2424, 2312, 2204, + 2097, 1993, 1892, 1793, 1696, 1602, 1511, 1422, 1336, 1252, + 1171, 1092, 1016, 943, 873, 805, 740, 677, 617, 560, + 506, 454, 405, 359, 316, 275, 237, 202, 170, 140, + 114, 90, 69, 50, 35, 22, 12, 5, 1, 0}; diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.h b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.h new file mode 100644 index 0000000000..0982f33c48 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: Apache-2.0 + * + * 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 + * + * 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_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_HANNING_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_HANNING_H_ + +#include + +extern const int g_hanning_size; +extern const int16_t g_hanning[]; + +#endif diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.h b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.h deleted file mode 100644 index c19566bfb6..0000000000 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.h +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright 2018 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. -==============================================================================*/ - -// This data was extracted from the larger feature data held in -// no_features_data.cc and consists of the 29th spectrogram slice of 43 values. -// This is the expected result of running the sample data in -// no_30ms_sample_data.cc through through the preprocessing pipeline. - -#ifndef TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_NO_POWER_SPECTRUM_DATA_H_ -#define TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_NO_POWER_SPECTRUM_DATA_H_ - -#include - -constexpr int g_no_power_spectrum_data_size = 43; -extern const uint8_t g_no_power_spectrum_data[]; - -#endif // TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_NO_POWER_SPECTRUM_DATA_H_ diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.cc index 24119cbdda..6bc3c4cb77 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.cc @@ -18,11 +18,17 @@ extern "C" { #define IFFT_FLAG_R 0 #define BIT_REVERSE_FLAG 1 #define FFT_SIZE 512 + #define FFT_SIZE_DIV2 256 #include - #include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hann.h" + #include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.h" + #include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/arm_cmplx_mag_squared_q10p6.h" } - #include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.h" + +void quantize(q15_t* bufA, q15_t* bufB, uint8_t* output); + + q15_t bufA[FFT_SIZE]; q15_t bufB[FFT_SIZE]; @@ -52,25 +58,49 @@ TfLiteStatus Preprocess(tflite::ErrorReporter* error_reporter, return kTfLiteError; } - arm_mult_q15((q15_t *) input, hann, bufB, 512); + // 30ms at 16 kHz = 480 samples + // We want to pad the rest of the 512-sample buffer with zeros + arm_mult_q15((q15_t *) input, g_hanning, bufB, 480); + int i; + for(i=480; i<512; i++) { + bufB[i] = 0; + } // Should move init code outside of Preprocess() function arm_math_status = arm_rfft_init_q15(&S_arm_fft, FFT_SIZE, IFFT_FLAG_R, BIT_REVERSE_FLAG); arm_rfft_q15(&S_arm_fft, bufB, bufA); - arm_shift_q15(bufA, 5, bufB, FFT_SIZE); - arm_cmplx_mag_squared_q15(bufB, bufA, 256); - arm_shift_q15(bufA, 1, bufB, 256); + // The rfft function packs data as follows: + // {real[0], real[N/2], real[1], imag[1], ..., real[N/2-1], imag[N/2-1]} + // Below we pack as follows: + // {real[0], 0, real[1], imag[1], ..., real[N/2-1], imag[N/2-1, real[N/2], 0} + bufA[FFT_SIZE_DIV2] = bufA[1]; + bufA[FFT_SIZE_DIV2 + 1] = 0; + bufA[1] = 0; + arm_cmplx_mag_squared_q10p6(bufA, bufB, FFT_SIZE_DIV2 + 1); + + quantize(bufA, bufB, output); + return kTfLiteOk; +} + +void quantize(q15_t* bufA, q15_t* bufB, uint8_t* output) { int i; for (i=0; i<42; i++) { arm_mean_q15(bufB+6*i, 6, bufA+i); } - arm_mean_q15(bufB+252, 4, bufA+42); + arm_mean_q15(bufB+252, 5, bufA+42); for (i=0; i<43; i++) { output[i] = (uint8_t) (bufA[i] >> 5); } +} +TfLiteStatus Preprocess_1sec(tflite::ErrorReporter* error_reporter, + const int16_t* input, uint8_t* output) { + int i; + for(i=0; i<49; i++) { + Preprocess(error_reporter, input+i*320, 480, 43, output+i*43); + } return kTfLiteOk; } diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor_test.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor_test.cc deleted file mode 100644 index 5986fb4913..0000000000 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor_test.cc +++ /dev/null @@ -1,63 +0,0 @@ -/* Copyright 2018 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/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.h" -#include "tensorflow/lite/c/c_api_internal.h" -#include "tensorflow/lite/experimental/micro/examples/micro_speech/no_30ms_sample_data.h" -#include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.h" -#include "tensorflow/lite/experimental/micro/examples/micro_speech/yes_30ms_sample_data.h" -#include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.h" -#include "tensorflow/lite/experimental/micro/micro_error_reporter.h" -#include "tensorflow/lite/experimental/micro/testing/micro_test.h" - -TF_LITE_MICRO_TESTS_BEGIN - -TF_LITE_MICRO_TEST(TestPreprocessor) { - tflite::MicroErrorReporter micro_error_reporter; - tflite::ErrorReporter* error_reporter = µ_error_reporter; - - uint8_t yes_calculated_data[g_yes_power_spectrum_data_size]; - TfLiteStatus yes_status = Preprocess( - error_reporter, g_yes_30ms_sample_data, g_yes_30ms_sample_data_size, - g_yes_power_spectrum_data_size, yes_calculated_data); - TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, yes_status); - - for (int i = 0; i < g_yes_power_spectrum_data_size; ++i) { - TF_LITE_MICRO_EXPECT_EQ(g_yes_power_spectrum_data[i], - yes_calculated_data[i]); - if (g_yes_power_spectrum_data[i] != yes_calculated_data[i]) { - error_reporter->Report("Expected value %d but found %d", - g_yes_power_spectrum_data[i], - yes_calculated_data[i]); - } - } - - uint8_t no_calculated_data[g_yes_power_spectrum_data_size]; - TfLiteStatus no_status = Preprocess( - error_reporter, g_no_30ms_sample_data, g_no_30ms_sample_data_size, - g_no_power_spectrum_data_size, no_calculated_data); - TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, no_status); - - for (int i = 0; i < g_no_power_spectrum_data_size; ++i) { - TF_LITE_MICRO_EXPECT_EQ(g_no_power_spectrum_data[i], no_calculated_data[i]); - if (g_no_power_spectrum_data[i] != no_calculated_data[i]) { - error_reporter->Report("Expected value %d but found %d", - g_no_power_spectrum_data[i], - no_calculated_data[i]); - } - } -} - -TF_LITE_MICRO_TESTS_END diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.cc new file mode 100644 index 0000000000..be66e5f548 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.cc @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: Apache-2.0 + * + * 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 + * + * 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/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.h" + +const int g_sin_1k_size = 480; +const int16_t g_sin_1k[480] = { + 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, + -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, + 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, + -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, + 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, + 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, + -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, + 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, + 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, + -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, + 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, + -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, + 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, + 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, + -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, + 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, + 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, + -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, + 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, + -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, + 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, + 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, + -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, + 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, + 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, + -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, + 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, + -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, + 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, + 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, + -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, + 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, + 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, + -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, + 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, + -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, + 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, + 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, + -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, + 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, + 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, + -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, + 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, + -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, + 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, + 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, + -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, + 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253}; diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.h b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.h new file mode 100644 index 0000000000..645e262aa1 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: Apache-2.0 + * + * 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 + * + * 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_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_SIN_1K_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_SIN_1K_H_ + +#include + +extern const int g_sin_1k_size; +extern const int16_t g_sin_1k[]; + +#endif diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.h b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.h deleted file mode 100644 index b02853f2ea..0000000000 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.h +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright 2018 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. -==============================================================================*/ - -// This data was extracted from the larger feature data held in -// no_features_data.cc and consists of the 26th spectrogram slice of 43 values. -// This is the expected result of running the sample data in -// yes_30ms_sample_data.cc through through the preprocessing pipeline. - -#ifndef TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_YES_POWER_SPECTRUM_DATA_H_ -#define TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_YES_POWER_SPECTRUM_DATA_H_ - -#include - -constexpr int g_yes_power_spectrum_data_size = 43; -extern const uint8_t g_yes_power_spectrum_data[]; - -#endif // TENSORFLOW_CONTRIB_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_YES_POWER_SPECTRUM_DATA_H_ diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/.gitignore b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/.gitignore new file mode 100644 index 0000000000..cb8d4d02c4 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/.gitignore @@ -0,0 +1,4 @@ +captured_data.txt +captured_data.wav +cmsis_*.txt +micro_*.txt diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/README.md b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/README.md new file mode 100644 index 0000000000..967b833501 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/README.md @@ -0,0 +1,72 @@ +# TODO + +* preprocessor_cmsis_test_bin + +# Description of Apollo3 Makefile targets + +* **pushbutton_cmsis_speech_test_bin**: + * When users press BTN2 on the Apollo3 EVK, 1 second of audio is captured. + * Then the audio is sent to the CMSIS version of the preprocessor and into the neural net + * To print out the neural net's inference scores, run GDB and source pushbutton\_cmsis\_scores.cmd + * To save the captured audio to a text file (captured\_data.txt), run GDB and source pushbutton\_cmsis\_voice.cmd + * Setup python + * sudo apt install python-pip + * sudo apt install python-tk + * pip install numpy + * pip install matplotlib + * pip install pysoundfile + * python captured_data_to_wav.py + * captured\_data.txt can be turned into a \*.wav file using captured\_data\_to\_wav.py by executing "python captured\_data\_to\_wav.py" +* **preprocessor_1k_cmsis_test_bin**: + * Sends a 1 kHz sine wave to the CMSIS fixed\_point version of the preprocessor + * **This test should be compiled with the -O0 option.** Otherwise, the breakpoints will not be reached + * In tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc change "-O3" to "-O0" on line 47 + * **DO NOT FORGET TO REVERT CHANGE AFTER EXPERIMENT** + * In future, enhance scripts to handle automatically, NOT manually! + * Clean project by running "make -f tensorflow/lite/experimental/micro/tools/make/Makefile clean" + * Compile BIN by running "make -f tensorflow/lite/experimental/micro/tools/make/Makefile TARGET=apollo3evb preprocessor_1k_cmsis_test_bin" + * Run with the preprocessor\_1k\_cmsis\_test.cmd GDB command file + * Produces four text files corresponding to outputs from the CMSIS fixed\_point version of this algorithm: + * cmsis_windowed_input.txt: the sinusoid after multiplying elementwise with a Hann window + * cmsis_dft.txt: the DFT of the windowed sinusoid + * cmsis_power.txt: the magnitude squared of the DFT + * cmsis_power_avg.txt: the 6-bin average of the magnitude squared of the DFT + * Run both verisons of the 1KHz pre-processor test and then compare. + * These files can be plotted with "python compare\_1k.py" + * Also prints out the number of cycles the code took to execute (using the DWT->CYCCNT register) +* **preprocessor_1k_micro_test_bin** + * Sends a 1 kHz sine wave to the Micro-Lite fixed\_point version of the preprocessor + * **This test should be compiled with the -O0 option.** Otherwise, the breakpoints will not be reached + * Run with the preprocessor\_1k\_micro\_test.cmd GDB command file + * Produces four text files corresponding to outputs from the Micro-Lite version of this algorithm: + * micro_windowed_input.txt: the sinusoid after multiplying elementwise with a Hann window + * micro_dft.txt: the DFT of the windowed sinusoid + * micro_power.txt: the magnitude squared of the DFT + * micro_power_avg.txt: the 6-bin average of the magnitude squared of the DFT + * Run both verisons of the 1KHz pre-processor test and then compare. + * These files can be plotted with "python compare\_1k.py" + * Also prints out the number of cycles the code took to execute (using the DWT->CYCCNT register) + +# Description of files + +* **.gitignore**: Git should ignore \*.txt and \*.wav files that result from experiments run in this directory +* **apollo3.h**: Apollo 3 version of the [CMSIS Device Header File (device.h)](https://www.keil.com/pack/doc/CMSIS/Core/html/device_h_pg.html). Available in the [Ambiq Keil Pack](http://s3.ambiqmicro.com/pack/AmbiqMicro.Apollo_DFP.1.1.0.pack). +* **captured\_data\_to\_wav.py**: Python script that parses a text file containing data dumped from GDB (specifically the verilog format) and creates a \*.wav file using [PySoundFile](https://pysoundfile.readthedocs.io/en/0.9.0/). +* **compare\_1k.py**: This script compares the intermediate variables and final outputs of the micro-lite fixed-point preprocessor function and the CMSIS version of this function. The stimulus provided to each preprocessor is the same: a 1 kHz sinusoid. +* **get\_yesno\_data.cmd**: A GDB command file that runs preprocessor_test (where TARGET=apollo3evb) and dumps the calculated data for the "yes" and "no" input wavfeorms to text files +* **\_main.c**: Point of entry for the micro_speech test +* **preprocessor_1k.cc**: A version of preprocessor.cc where a 1 kHz sinusoid is provided as input to the preprocessor +* **preprocessor_1k_cmsis_test.cmd**: GDB command file for the CMSIS preprocessor 1 kHz test +* **preprocessor_1k_micro_test.cmd**: GDB command file for the Micro-Lite preprocessor 1 kHz test +* **preprocessor_test.cmd**: GDB command file for the preprocessor test +* **pushbutton_cmsis_scores.cmd**: GDB command file that runs pushbutton_cmsis_speech_test_bin. It adds a breakpoint immediately after the scores are reported and prints out each score. Then it continues code execution. +* **pushbutton_cmsis_voice.cmd**: GDB command file that runs pushbutton_cmsis_speech_test_bin. Dumps the recorded 1 second of audio to captured_data.txt, which can then be processed by the python file captured_data_to_wav.py. +* **pushbutton_main.c**: Source file containing program point of entry \_main() for the pushbutton\_\* tests. Contains Interrupt Service Routines for PDM data capture and pushbuttons. Calls the main() function of pushbutton_test.cc +* **pushbutton_test.cc**: Source file containing main() function for the pushbutton\_\* tests. main() calls the preprocessor function and the neural net inference function. +* **system_apollo3.c**: Apollo 3 version of the [CMSIS System Configuration File system\_\.c](https://www.keil.com/pack/doc/CMSIS/Core/html/system_c_pg.html). Available in the [Ambiq Keil Pack](http://s3.ambiqmicro.com/pack/AmbiqMicro.Apollo_DFP.1.1.0.pack). +* **system_apollo3.h**: Apollo 3 version of the [CMSIS System Configuration File system\_\.h](https://www.keil.com/pack/doc/CMSIS/Core/html/system_c_pg.html). Available in the [Ambiq Keil Pack](http://s3.ambiqmicro.com/pack/AmbiqMicro.Apollo_DFP.1.1.0.pack). + + +# FFT scaling +See https://github.com/ARM-software/CMSIS_5/issues/220 +>And as @xizhizhang pointed, I think there may be an error on the internal downscaling, or at least on the documentation. It looks like during the fft computation, the downscaling factor reach 2**-9 for a 512 rfft operation, being the output in Q10.22, instead the documented 2**-8 and Q9.23. diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/_main.c b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/_main.c similarity index 100% rename from tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/_main.c rename to tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/_main.c diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/apollo3.h b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/apollo3.h new file mode 100755 index 0000000000..af22270e32 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/apollo3.h @@ -0,0 +1,23332 @@ +/* + * Copyright (C) 2015-2017, Ambiq Micro + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of itscontributors may be used to endorse + * or promote products derived from thissoftware without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @file apollo3.h + * @brief CMSIS HeaderFile + * @version 1.0 + * @date 10. August 2018 + * @note Generated by SVDConv V3.3.18 on Friday, 10.08.2018 16:52:09 + * from File 'apollo3.svd', + * last modified on Friday, 10.08.2018 20:01:31 + */ + + + +/** @addtogroup Ambiq Micro + * @{ + */ + + +/** @addtogroup apollo3 + * @{ + */ + + +#ifndef APOLLO3_H +#define APOLLO3_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/** @addtogroup Configuration_of_CMSIS + * @{ + */ + + + +/* =========================================================================================================================== */ +/* ================ Interrupt Number Definition ================ */ +/* =========================================================================================================================== */ + +typedef enum { +/* ======================================= ARM Cortex-M4 Specific Interrupt Numbers ======================================== */ + Reset_IRQn = -15, /*!< -15 Reset Vector, invoked on Power up and warm reset */ + NonMaskableInt_IRQn = -14, /*!< -14 Non maskable Interrupt, cannot be stopped or preempted */ + HardFault_IRQn = -13, /*!< -13 Hard Fault, all classes of Fault */ + MemoryManagement_IRQn = -12, /*!< -12 Memory Management, MPU mismatch, including Access Violation + and No Match */ + BusFault_IRQn = -11, /*!< -11 Bus Fault, Pre-Fetch-, Memory Access Fault, other address/memory + related Fault */ + UsageFault_IRQn = -10, /*!< -10 Usage Fault, i.e. Undef Instruction, Illegal State Transition */ + SVCall_IRQn = -5, /*!< -5 System Service Call via SVC instruction */ + DebugMonitor_IRQn = -4, /*!< -4 Debug Monitor */ + PendSV_IRQn = -2, /*!< -2 Pendable request for system service */ + SysTick_IRQn = -1, /*!< -1 System Tick Timer */ +/* ========================================== apollo3 Specific Interrupt Numbers =========================================== */ + BROWNOUT_IRQn = 0, /*!< 0 BROWNOUT */ + WDT_IRQn = 1, /*!< 1 WDT */ + RTC_IRQn = 2, /*!< 2 RTC */ + VCOMP_IRQn = 3, /*!< 3 VCOMP */ + IOSLAVE_IRQn = 4, /*!< 4 IOSLAVE */ + IOSLAVEACC_IRQn = 5, /*!< 5 IOSLAVEACC */ + IOMSTR0_IRQn = 6, /*!< 6 IOMSTR0 */ + IOMSTR1_IRQn = 7, /*!< 7 IOMSTR1 */ + IOMSTR2_IRQn = 8, /*!< 8 IOMSTR2 */ + IOMSTR3_IRQn = 9, /*!< 9 IOMSTR3 */ + IOMSTR4_IRQn = 10, /*!< 10 IOMSTR4 */ + IOMSTR5_IRQn = 11, /*!< 11 IOMSTR5 */ + BLE_IRQn = 12, /*!< 12 BLE */ + GPIO_IRQn = 13, /*!< 13 GPIO */ + CTIMER_IRQn = 14, /*!< 14 CTIMER */ + UART0_IRQn = 15, /*!< 15 UART0 */ + UART1_IRQn = 16, /*!< 16 UART1 */ + SCARD_IRQn = 17, /*!< 17 SCARD */ + ADC_IRQn = 18, /*!< 18 ADC */ + PDM_IRQn = 19, /*!< 19 PDM */ + MSPI_IRQn = 20, /*!< 20 MSPI */ + STIMER_IRQn = 22, /*!< 22 STIMER */ + STIMER_CMPR0_IRQn = 23, /*!< 23 STIMER_CMPR0 */ + STIMER_CMPR1_IRQn = 24, /*!< 24 STIMER_CMPR1 */ + STIMER_CMPR2_IRQn = 25, /*!< 25 STIMER_CMPR2 */ + STIMER_CMPR3_IRQn = 26, /*!< 26 STIMER_CMPR3 */ + STIMER_CMPR4_IRQn = 27, /*!< 27 STIMER_CMPR4 */ + STIMER_CMPR5_IRQn = 28, /*!< 28 STIMER_CMPR5 */ + STIMER_CMPR6_IRQn = 29, /*!< 29 STIMER_CMPR6 */ + STIMER_CMPR7_IRQn = 30, /*!< 30 STIMER_CMPR7 */ + CLKGEN_IRQn = 31 /*!< 31 CLKGEN */ +} IRQn_Type; + + + +/* =========================================================================================================================== */ +/* ================ Processor and Core Peripheral Section ================ */ +/* =========================================================================================================================== */ + +/* =========================== Configuration of the ARM Cortex-M4 Processor and Core Peripherals =========================== */ +#define __CM4_REV 0x0100U /*!< CM4 Core Revision */ +#define __NVIC_PRIO_BITS 3 /*!< Number of Bits used for Priority Levels */ +#define __Vendor_SysTickConfig 0 /*!< Set to 1 if different SysTick Config is used */ +#define __MPU_PRESENT 1 /*!< MPU present or not */ +#define __FPU_PRESENT 1 /*!< FPU present or not */ + + +/** @} */ /* End of group Configuration_of_CMSIS */ + +#include "core_cm4.h" /*!< ARM Cortex-M4 processor and core peripherals */ +#include "system_apollo3.h" /*!< apollo3 System */ + +#ifndef __IM /*!< Fallback for older CMSIS versions */ + #define __IM __I +#endif +#ifndef __OM /*!< Fallback for older CMSIS versions */ + #define __OM __O +#endif +#ifndef __IOM /*!< Fallback for older CMSIS versions */ + #define __IOM __IO +#endif + + +/* ======================================== Start of section using anonymous unions ======================================== */ +#if defined (__CC_ARM) + #pragma push + #pragma anon_unions +#elif defined (__ICCARM__) + #pragma language=extended +#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wc11-extensions" + #pragma clang diagnostic ignored "-Wreserved-id-macro" + #pragma clang diagnostic ignored "-Wgnu-anonymous-struct" + #pragma clang diagnostic ignored "-Wnested-anon-types" +#elif defined (__GNUC__) + /* anonymous unions are enabled by default */ +#elif defined (__TMS470__) + /* anonymous unions are enabled by default */ +#elif defined (__TASKING__) + #pragma warning 586 +#elif defined (__CSMC__) + /* anonymous unions are enabled by default */ +#else + #warning Not supported compiler type +#endif + + +/* =========================================================================================================================== */ +/* ================ Device Specific Peripheral Section ================ */ +/* =========================================================================================================================== */ + + +/** @addtogroup Device_Peripheral_peripherals + * @{ + */ + + + +/* =========================================================================================================================== */ +/* ================ ADC ================ */ +/* =========================================================================================================================== */ + + +/** + * @brief Analog Digital Converter Control (ADC) + */ + +typedef struct { /*!< (@ 0x50010000) ADC Structure */ + + union { + __IOM uint32_t CFG; /*!< (@ 0x00000000) Configuration Register */ + + struct { + __IOM uint32_t ADCEN : 1; /*!< [0..0] This bit enables the ADC module. While the ADC is enabled, + the ADCCFG and SLOT Configuration regsiter settings must + remain stable and unchanged. All configuration register + settings, slot configuration settings and window comparison + settings should be written prior to setting the ADCEN bit + to '1'. */ + __IM uint32_t : 1; + __IOM uint32_t RPTEN : 1; /*!< [2..2] This bit enables Repeating Scan Mode. */ + __IOM uint32_t LPMODE : 1; /*!< [3..3] Select power mode to enter between active scans. */ + __IOM uint32_t CKMODE : 1; /*!< [4..4] Clock mode register */ + __IM uint32_t : 3; + __IOM uint32_t REFSEL : 2; /*!< [9..8] Select the ADC reference voltage. */ + __IM uint32_t : 2; + __IOM uint32_t DFIFORDEN : 1; /*!< [12..12] Destructive FIFO Read Enable. Setting this will enable + FIFO pop upon reading the FIFOPR register. */ + __IM uint32_t : 3; + __IOM uint32_t TRIGSEL : 3; /*!< [18..16] Select the ADC trigger source. */ + __IOM uint32_t TRIGPOL : 1; /*!< [19..19] This bit selects the ADC trigger polarity for external + off chip triggers. */ + __IM uint32_t : 4; + __IOM uint32_t CLKSEL : 2; /*!< [25..24] Select the source and frequency for the ADC clock. + All values not enumerated below are undefined. */ + } CFG_b; + } ; + + union { + __IOM uint32_t STAT; /*!< (@ 0x00000004) ADC Power Status */ + + struct { + __IOM uint32_t PWDSTAT : 1; /*!< [0..0] Indicates the power-status of the ADC. */ + } STAT_b; + } ; + + union { + __IOM uint32_t SWT; /*!< (@ 0x00000008) Software trigger */ + + struct { + __IOM uint32_t SWT : 8; /*!< [7..0] Writing 0x37 to this register generates a software trigger. */ + } SWT_b; + } ; + + union { + __IOM uint32_t SL0CFG; /*!< (@ 0x0000000C) Slot 0 Configuration Register */ + + struct { + __IOM uint32_t SLEN0 : 1; /*!< [0..0] This bit enables slot 0 for ADC conversions. */ + __IOM uint32_t WCEN0 : 1; /*!< [1..1] This bit enables the window compare function for slot + 0. */ + __IM uint32_t : 6; + __IOM uint32_t CHSEL0 : 4; /*!< [11..8] Select one of the 14 channel inputs for this slot. */ + __IM uint32_t : 4; + __IOM uint32_t PRMODE0 : 2; /*!< [17..16] Set the Precision Mode For Slot. */ + __IM uint32_t : 6; + __IOM uint32_t ADSEL0 : 3; /*!< [26..24] Select the number of measurements to average in the + accumulate divide module for this slot. */ + } SL0CFG_b; + } ; + + union { + __IOM uint32_t SL1CFG; /*!< (@ 0x00000010) Slot 1 Configuration Register */ + + struct { + __IOM uint32_t SLEN1 : 1; /*!< [0..0] This bit enables slot 1 for ADC conversions. */ + __IOM uint32_t WCEN1 : 1; /*!< [1..1] This bit enables the window compare function for slot + 1. */ + __IM uint32_t : 6; + __IOM uint32_t CHSEL1 : 4; /*!< [11..8] Select one of the 14 channel inputs for this slot. */ + __IM uint32_t : 4; + __IOM uint32_t PRMODE1 : 2; /*!< [17..16] Set the Precision Mode For Slot. */ + __IM uint32_t : 6; + __IOM uint32_t ADSEL1 : 3; /*!< [26..24] Select the number of measurements to average in the + accumulate divide module for this slot. */ + } SL1CFG_b; + } ; + + union { + __IOM uint32_t SL2CFG; /*!< (@ 0x00000014) Slot 2 Configuration Register */ + + struct { + __IOM uint32_t SLEN2 : 1; /*!< [0..0] This bit enables slot 2 for ADC conversions. */ + __IOM uint32_t WCEN2 : 1; /*!< [1..1] This bit enables the window compare function for slot + 2. */ + __IM uint32_t : 6; + __IOM uint32_t CHSEL2 : 4; /*!< [11..8] Select one of the 14 channel inputs for this slot. */ + __IM uint32_t : 4; + __IOM uint32_t PRMODE2 : 2; /*!< [17..16] Set the Precision Mode For Slot. */ + __IM uint32_t : 6; + __IOM uint32_t ADSEL2 : 3; /*!< [26..24] Select the number of measurements to average in the + accumulate divide module for this slot. */ + } SL2CFG_b; + } ; + + union { + __IOM uint32_t SL3CFG; /*!< (@ 0x00000018) Slot 3 Configuration Register */ + + struct { + __IOM uint32_t SLEN3 : 1; /*!< [0..0] This bit enables slot 3 for ADC conversions. */ + __IOM uint32_t WCEN3 : 1; /*!< [1..1] This bit enables the window compare function for slot + 3. */ + __IM uint32_t : 6; + __IOM uint32_t CHSEL3 : 4; /*!< [11..8] Select one of the 14 channel inputs for this slot. */ + __IM uint32_t : 4; + __IOM uint32_t PRMODE3 : 2; /*!< [17..16] Set the Precision Mode For Slot. */ + __IM uint32_t : 6; + __IOM uint32_t ADSEL3 : 3; /*!< [26..24] Select the number of measurements to average in the + accumulate divide module for this slot. */ + } SL3CFG_b; + } ; + + union { + __IOM uint32_t SL4CFG; /*!< (@ 0x0000001C) Slot 4 Configuration Register */ + + struct { + __IOM uint32_t SLEN4 : 1; /*!< [0..0] This bit enables slot 4 for ADC conversions. */ + __IOM uint32_t WCEN4 : 1; /*!< [1..1] This bit enables the window compare function for slot + 4. */ + __IM uint32_t : 6; + __IOM uint32_t CHSEL4 : 4; /*!< [11..8] Select one of the 14 channel inputs for this slot. */ + __IM uint32_t : 4; + __IOM uint32_t PRMODE4 : 2; /*!< [17..16] Set the Precision Mode For Slot. */ + __IM uint32_t : 6; + __IOM uint32_t ADSEL4 : 3; /*!< [26..24] Select the number of measurements to average in the + accumulate divide module for this slot. */ + } SL4CFG_b; + } ; + + union { + __IOM uint32_t SL5CFG; /*!< (@ 0x00000020) Slot 5 Configuration Register */ + + struct { + __IOM uint32_t SLEN5 : 1; /*!< [0..0] This bit enables slot 5 for ADC conversions. */ + __IOM uint32_t WCEN5 : 1; /*!< [1..1] This bit enables the window compare function for slot + 5. */ + __IM uint32_t : 6; + __IOM uint32_t CHSEL5 : 4; /*!< [11..8] Select one of the 14 channel inputs for this slot. */ + __IM uint32_t : 4; + __IOM uint32_t PRMODE5 : 2; /*!< [17..16] Set the Precision Mode For Slot. */ + __IM uint32_t : 6; + __IOM uint32_t ADSEL5 : 3; /*!< [26..24] Select number of measurements to average in the accumulate + divide module for this slot. */ + } SL5CFG_b; + } ; + + union { + __IOM uint32_t SL6CFG; /*!< (@ 0x00000024) Slot 6 Configuration Register */ + + struct { + __IOM uint32_t SLEN6 : 1; /*!< [0..0] This bit enables slot 6 for ADC conversions. */ + __IOM uint32_t WCEN6 : 1; /*!< [1..1] This bit enables the window compare function for slot + 6. */ + __IM uint32_t : 6; + __IOM uint32_t CHSEL6 : 4; /*!< [11..8] Select one of the 14 channel inputs for this slot. */ + __IM uint32_t : 4; + __IOM uint32_t PRMODE6 : 2; /*!< [17..16] Set the Precision Mode For Slot. */ + __IM uint32_t : 6; + __IOM uint32_t ADSEL6 : 3; /*!< [26..24] Select the number of measurements to average in the + accumulate divide module for this slot. */ + } SL6CFG_b; + } ; + + union { + __IOM uint32_t SL7CFG; /*!< (@ 0x00000028) Slot 7 Configuration Register */ + + struct { + __IOM uint32_t SLEN7 : 1; /*!< [0..0] This bit enables slot 7 for ADC conversions. */ + __IOM uint32_t WCEN7 : 1; /*!< [1..1] This bit enables the window compare function for slot + 7. */ + __IM uint32_t : 6; + __IOM uint32_t CHSEL7 : 4; /*!< [11..8] Select one of the 14 channel inputs for this slot. */ + __IM uint32_t : 4; + __IOM uint32_t PRMODE7 : 2; /*!< [17..16] Set the Precision Mode For Slot. */ + __IM uint32_t : 6; + __IOM uint32_t ADSEL7 : 3; /*!< [26..24] Select the number of measurements to average in the + accumulate divide module for this slot. */ + } SL7CFG_b; + } ; + + union { + __IOM uint32_t WULIM; /*!< (@ 0x0000002C) Window Comparator Upper Limits Register */ + + struct { + __IOM uint32_t ULIM : 20; /*!< [19..0] Sets the upper limit for the window comparator. */ + } WULIM_b; + } ; + + union { + __IOM uint32_t WLLIM; /*!< (@ 0x00000030) Window Comparator Lower Limits Register */ + + struct { + __IOM uint32_t LLIM : 20; /*!< [19..0] Sets the lower limit for the window comparator. */ + } WLLIM_b; + } ; + + union { + __IOM uint32_t SCWLIM; /*!< (@ 0x00000034) Scale Window Comparator Limits */ + + struct { + __IOM uint32_t SCWLIMEN : 1; /*!< [0..0] Scale the window limits compare values per precision + mode. When set to 0x0 (default), the values in the 20-bit + limits registers will compare directly with the FIFO values + regardless of the precision mode the slot is configured + to. When set to 0x1, the compare values will be divided + by the difference in precision bits while performing the + window limit comparisons. */ + } SCWLIM_b; + } ; + + union { + __IOM uint32_t FIFO; /*!< (@ 0x00000038) FIFO Data and Valid Count Register */ + + struct { + __IOM uint32_t DATA : 20; /*!< [19..0] Oldest data in the FIFO. */ + __IOM uint32_t COUNT : 8; /*!< [27..20] Number of valid entries in the ADC FIFO. */ + __IOM uint32_t SLOTNUM : 3; /*!< [30..28] Slot number associated with this FIFO data. */ + __IOM uint32_t RSVD : 1; /*!< [31..31] RESERVED. */ + } FIFO_b; + } ; + + union { + __IOM uint32_t FIFOPR; /*!< (@ 0x0000003C) FIFO Data and Valid Count Register */ + + struct { + __IOM uint32_t DATA : 20; /*!< [19..0] Oldest data in the FIFO. */ + __IOM uint32_t COUNT : 8; /*!< [27..20] Number of valid entries in the ADC FIFO. */ + __IOM uint32_t SLOTNUMPR : 3; /*!< [30..28] Slot number associated with this FIFO data. */ + __IOM uint32_t RSVDPR : 1; /*!< [31..31] RESERVED. */ + } FIFOPR_b; + } ; + __IM uint32_t RESERVED[112]; + + union { + __IOM uint32_t INTEN; /*!< (@ 0x00000200) ADC Interrupt registers: Enable */ + + struct { + __IOM uint32_t CNVCMP : 1; /*!< [0..0] ADC conversion complete interrupt. */ + __IOM uint32_t SCNCMP : 1; /*!< [1..1] ADC scan complete interrupt. */ + __IOM uint32_t FIFOOVR1 : 1; /*!< [2..2] FIFO 75 percent full interrupt. */ + __IOM uint32_t FIFOOVR2 : 1; /*!< [3..3] FIFO 100 percent full interrupt. */ + __IOM uint32_t WCEXC : 1; /*!< [4..4] Window comparator voltage excursion interrupt. */ + __IOM uint32_t WCINC : 1; /*!< [5..5] Window comparator voltage incursion interrupt. */ + __IOM uint32_t DCMP : 1; /*!< [6..6] DMA Transfer Complete */ + __IOM uint32_t DERR : 1; /*!< [7..7] DMA Error Condition */ + } INTEN_b; + } ; + + union { + __IOM uint32_t INTSTAT; /*!< (@ 0x00000204) ADC Interrupt registers: Status */ + + struct { + __IOM uint32_t CNVCMP : 1; /*!< [0..0] ADC conversion complete interrupt. */ + __IOM uint32_t SCNCMP : 1; /*!< [1..1] ADC scan complete interrupt. */ + __IOM uint32_t FIFOOVR1 : 1; /*!< [2..2] FIFO 75 percent full interrupt. */ + __IOM uint32_t FIFOOVR2 : 1; /*!< [3..3] FIFO 100 percent full interrupt. */ + __IOM uint32_t WCEXC : 1; /*!< [4..4] Window comparator voltage excursion interrupt. */ + __IOM uint32_t WCINC : 1; /*!< [5..5] Window comparator voltage incursion interrupt. */ + __IOM uint32_t DCMP : 1; /*!< [6..6] DMA Transfer Complete */ + __IOM uint32_t DERR : 1; /*!< [7..7] DMA Error Condition */ + } INTSTAT_b; + } ; + + union { + __IOM uint32_t INTCLR; /*!< (@ 0x00000208) ADC Interrupt registers: Clear */ + + struct { + __IOM uint32_t CNVCMP : 1; /*!< [0..0] ADC conversion complete interrupt. */ + __IOM uint32_t SCNCMP : 1; /*!< [1..1] ADC scan complete interrupt. */ + __IOM uint32_t FIFOOVR1 : 1; /*!< [2..2] FIFO 75 percent full interrupt. */ + __IOM uint32_t FIFOOVR2 : 1; /*!< [3..3] FIFO 100 percent full interrupt. */ + __IOM uint32_t WCEXC : 1; /*!< [4..4] Window comparator voltage excursion interrupt. */ + __IOM uint32_t WCINC : 1; /*!< [5..5] Window comparator voltage incursion interrupt. */ + __IOM uint32_t DCMP : 1; /*!< [6..6] DMA Transfer Complete */ + __IOM uint32_t DERR : 1; /*!< [7..7] DMA Error Condition */ + } INTCLR_b; + } ; + + union { + __IOM uint32_t INTSET; /*!< (@ 0x0000020C) ADC Interrupt registers: Set */ + + struct { + __IOM uint32_t CNVCMP : 1; /*!< [0..0] ADC conversion complete interrupt. */ + __IOM uint32_t SCNCMP : 1; /*!< [1..1] ADC scan complete interrupt. */ + __IOM uint32_t FIFOOVR1 : 1; /*!< [2..2] FIFO 75 percent full interrupt. */ + __IOM uint32_t FIFOOVR2 : 1; /*!< [3..3] FIFO 100 percent full interrupt. */ + __IOM uint32_t WCEXC : 1; /*!< [4..4] Window comparator voltage excursion interrupt. */ + __IOM uint32_t WCINC : 1; /*!< [5..5] Window comparator voltage incursion interrupt. */ + __IOM uint32_t DCMP : 1; /*!< [6..6] DMA Transfer Complete */ + __IOM uint32_t DERR : 1; /*!< [7..7] DMA Error Condition */ + } INTSET_b; + } ; + __IM uint32_t RESERVED1[12]; + + union { + __IOM uint32_t DMATRIGEN; /*!< (@ 0x00000240) DMA Trigger Enable Register */ + + struct { + __IOM uint32_t DFIFO75 : 1; /*!< [0..0] Trigger DMA upon FIFO 75 percent Full */ + __IOM uint32_t DFIFOFULL : 1; /*!< [1..1] Trigger DMA upon FIFO 100 percent Full */ + } DMATRIGEN_b; + } ; + + union { + __IOM uint32_t DMATRIGSTAT; /*!< (@ 0x00000244) DMA Trigger Status Register */ + + struct { + __IOM uint32_t D75STAT : 1; /*!< [0..0] Triggered DMA from FIFO 75 percent Full */ + __IOM uint32_t DFULLSTAT : 1; /*!< [1..1] Triggered DMA from FIFO 100 percent Full */ + } DMATRIGSTAT_b; + } ; + __IM uint32_t RESERVED2[14]; + + union { + __IOM uint32_t DMACFG; /*!< (@ 0x00000280) DMA Configuration Register */ + + struct { + __IOM uint32_t DMAEN : 1; /*!< [0..0] DMA Enable */ + __IM uint32_t : 1; + __IOM uint32_t DMADIR : 1; /*!< [2..2] Direction */ + __IM uint32_t : 5; + __IOM uint32_t DMAPRI : 1; /*!< [8..8] Sets the Priority of the DMA request */ + __IOM uint32_t DMADYNPRI : 1; /*!< [9..9] Enables dynamic priority based on FIFO fullness. When + FIFO is full, priority is automatically set to HIGH. Otherwise, + DMAPRI is used. */ + __IM uint32_t : 6; + __IOM uint32_t DMAHONSTAT : 1; /*!< [16..16] Halt New ADC conversions until DMA Status DMAERR and + DMACPL Cleared. */ + __IOM uint32_t DMAMSK : 1; /*!< [17..17] Mask the FIFOCNT and SLOTNUM when transferring FIFO + contents to memory */ + __IOM uint32_t DPWROFF : 1; /*!< [18..18] Power Off the ADC System upon DMACPL. */ + } DMACFG_b; + } ; + __IM uint32_t RESERVED3; + + union { + __IOM uint32_t DMATOTCOUNT; /*!< (@ 0x00000288) DMA Total Transfer Count */ + + struct { + __IM uint32_t : 2; + __IOM uint32_t TOTCOUNT : 16; /*!< [17..2] Total Transfer Count */ + } DMATOTCOUNT_b; + } ; + + union { + __IOM uint32_t DMATARGADDR; /*!< (@ 0x0000028C) DMA Target Address Register */ + + struct { + __IOM uint32_t LTARGADDR : 19; /*!< [18..0] DMA Target Address */ + __IOM uint32_t UTARGADDR : 13; /*!< [31..19] SRAM Target */ + } DMATARGADDR_b; + } ; + + union { + __IOM uint32_t DMASTAT; /*!< (@ 0x00000290) DMA Status Register */ + + struct { + __IOM uint32_t DMATIP : 1; /*!< [0..0] DMA Transfer In Progress */ + __IOM uint32_t DMACPL : 1; /*!< [1..1] DMA Transfer Complete */ + __IOM uint32_t DMAERR : 1; /*!< [2..2] DMA Error */ + } DMASTAT_b; + } ; +} ADC_Type; /*!< Size = 660 (0x294) */ + + + +/* =========================================================================================================================== */ +/* ================ APBDMA ================ */ +/* =========================================================================================================================== */ + + +/** + * @brief APB DMA Register Interfaces (APBDMA) + */ + +typedef struct { /*!< (@ 0x40011000) APBDMA Structure */ + + union { + __IOM uint32_t BBVALUE; /*!< (@ 0x00000000) Control Register */ + + struct { + __IOM uint32_t DATAOUT : 8; /*!< [7..0] Data Output Values */ + __IM uint32_t : 8; + __IOM uint32_t PIN : 8; /*!< [23..16] PIO values */ + } BBVALUE_b; + } ; + + union { + __IOM uint32_t BBSETCLEAR; /*!< (@ 0x00000004) Set/Clear Register */ + + struct { + __IOM uint32_t SET : 8; /*!< [7..0] Write 1 to Set PIO value (set hier priority than clear + if both bit set) */ + __IM uint32_t : 8; + __IOM uint32_t CLEAR : 8; /*!< [23..16] Write 1 to Clear PIO value */ + } BBSETCLEAR_b; + } ; + + union { + __IOM uint32_t BBINPUT; /*!< (@ 0x00000008) PIO Input Values */ + + struct { + __IOM uint32_t DATAIN : 8; /*!< [7..0] PIO values */ + } BBINPUT_b; + } ; + __IM uint32_t RESERVED[5]; + + union { + __IOM uint32_t DEBUGDATA; /*!< (@ 0x00000020) PIO Input Values */ + + struct { + __IOM uint32_t DEBUGDATA : 32; /*!< [31..0] Debug Data */ + } DEBUGDATA_b; + } ; + __IM uint32_t RESERVED1[7]; + + union { + __IOM uint32_t DEBUG; /*!< (@ 0x00000040) PIO Input Values */ + + struct { + __IOM uint32_t DEBUGEN : 4; /*!< [3..0] Debug Enable */ + } DEBUG_b; + } ; +} APBDMA_Type; /*!< Size = 68 (0x44) */ + + + +/* =========================================================================================================================== */ +/* ================ BLEIF ================ */ +/* =========================================================================================================================== */ + + +/** + * @brief BLE Interface (BLEIF) + */ + +typedef struct { /*!< (@ 0x5000C000) BLEIF Structure */ + + union { + __IOM uint32_t FIFO; /*!< (@ 0x00000000) FIFO Access Port */ + + struct { + __IOM uint32_t FIFO : 32; /*!< [31..0] FIFO direct access. Only locations 0 - 3F will return + valid information. */ + } FIFO_b; + } ; + __IM uint32_t RESERVED[63]; + + union { + __IOM uint32_t FIFOPTR; /*!< (@ 0x00000100) FIFO size and remaining slots open values */ + + struct { + __IOM uint32_t FIFO0SIZ : 8; /*!< [7..0] The number of valid data bytes currently in the FIFO + 0 (written by MCU, read by interface) */ + __IOM uint32_t FIFO0REM : 8; /*!< [15..8] The number of remaining data bytes slots currently in + FIFO 0 (written by MCU, read by interface) */ + __IOM uint32_t FIFO1SIZ : 8; /*!< [23..16] The number of valid data bytes currently in FIFO 1 + (written by interface, read by MCU) */ + __IOM uint32_t FIFO1REM : 8; /*!< [31..24] The number of remaining data bytes slots currently + in FIFO 1 (written by interface, read by MCU) */ + } FIFOPTR_b; + } ; + + union { + __IOM uint32_t FIFOTHR; /*!< (@ 0x00000104) FIFO Threshold Configuration */ + + struct { + __IOM uint32_t FIFORTHR : 6; /*!< [5..0] FIFO read threshold in bytes. A value of 0 will disable + the read FIFO level from activating the threshold interrupt. + If this field is non-zero, it will trigger a threshold + interrupt when the read fifo contains FIFORTHR valid bytes + of data, as indicated by the FIFO1SIZ field. This is intended + to signal when a data transfer of FIFORTHR bytes can be + done from the IOM module to the host via the read fifo + to support large IOM read operations. */ + __IM uint32_t : 2; + __IOM uint32_t FIFOWTHR : 6; /*!< [13..8] FIFO write threshold in bytes. A value of 0 will disable + the write FIFO level from activating the threshold interrupt. + If this field is non-zero, it will trigger a threshold + interrupt when the write fifo contains FIFOWTHR free bytes, + as indicated by the FIFO0REM field. This is intended to + signal when a transfer of FIFOWTHR bytes can be done from + the host to the IOM write fifo to support large IOM write + operations. */ + } FIFOTHR_b; + } ; + + union { + __IOM uint32_t FIFOPOP; /*!< (@ 0x00000108) FIFO POP register */ + + struct { + __IOM uint32_t FIFODOUT : 32; /*!< [31..0] This register will return the read data indicated by + the current read pointer on reads. If the POPWR control + bit in the FIFOCTRL register is reset (0), the fifo read + pointer will be advanced by one word as a result of the + read.If the POPWR bit is set (1), the fifo read pointer + will only be advanced after a write operation to this register. + The write data is ignored for this register.If less than + a even word multiple is available, and the command is completed, + the module will return the word containing */ + } FIFOPOP_b; + } ; + + union { + __IOM uint32_t FIFOPUSH; /*!< (@ 0x0000010C) FIFO PUSH register */ + + struct { + __IOM uint32_t FIFODIN : 32; /*!< [31..0] This register is used to write the FIFORAM in FIFO mode + and will cause a push event to occur to the next open slot + within the FIFORAM. Writing to this register will cause + the write point to increment by 1 word(4 bytes). */ + } FIFOPUSH_b; + } ; + + union { + __IOM uint32_t FIFOCTRL; /*!< (@ 0x00000110) FIFO Control Register */ + + struct { + __IOM uint32_t POPWR : 1; /*!< [0..0] Selects the mode in which 'pop' events are done for the + fifo read operations. A value of '1' will prevent a pop + event on a read operation, and will require a write to + the FIFOPOP register to create a pop event.A value of '0' + in this register will allow a pop event to occur on the + read of the FIFOPOP register, and may cause inadvertant + fifo pops when used in a debugging mode. */ + __IOM uint32_t FIFORSTN : 1; /*!< [1..1] Active low manual reset of the fifo. Write to 0 to reset + fifo, and then write to 1 to remove the reset. */ + } FIFOCTRL_b; + } ; + + union { + __IOM uint32_t FIFOLOC; /*!< (@ 0x00000114) FIFO Pointers */ + + struct { + __IOM uint32_t FIFOWPTR : 4; /*!< [3..0] Current FIFO write pointer. Value is the index into the + outgoing FIFO (FIFO0), which is used during write operations + to external devices. */ + __IM uint32_t : 4; + __IOM uint32_t FIFORPTR : 4; /*!< [11..8] Current FIFO read pointer. Used to index into the incoming + FIFO (FIFO1), which is used to store read data returned + from external devices during a read operation. */ + } FIFOLOC_b; + } ; + __IM uint32_t RESERVED1[58]; + + union { + __IOM uint32_t CLKCFG; /*!< (@ 0x00000200) I/O Clock Configuration */ + + struct { + __IOM uint32_t IOCLKEN : 1; /*!< [0..0] Enable for the interface clock. Must be enabled prior + to executing any IO operations. */ + __IM uint32_t : 7; + __IOM uint32_t FSEL : 3; /*!< [10..8] Select the input clock frequency. */ + __IOM uint32_t CLK32KEN : 1; /*!< [11..11] Enable for the 32Khz clock to the BLE module */ + __IOM uint32_t DIV3 : 1; /*!< [12..12] Enable of the divide by 3 of the source IOCLK. */ + } CLKCFG_b; + } ; + __IM uint32_t RESERVED2[2]; + + union { + __IOM uint32_t CMD; /*!< (@ 0x0000020C) Command and offset Register */ + + struct { + __IOM uint32_t CMD : 5; /*!< [4..0] Command for submodule. */ + __IOM uint32_t OFFSETCNT : 2; /*!< [6..5] Number of offset bytes to use for the command - 0, 1, + 2, 3 are valid selections. The second (byte 1) and third + byte (byte 2) are read from the OFFSETHI register, and + the low order byte is pulled from this register in the + OFFSETLO field.Offset bytes are transmitted highest byte + first. EG if offsetcnt == 3, OFFSETHI[15:8] will be transmitted + first, then OFFSETHI[7:0] then OFFSETLO.If offsetcnt == + 2, OFFSETHI[7:0] will be transmitted, then OFFSETLO.If + offsetcnt == 1, only OFFSETLO will be transmitted. */ + __IOM uint32_t CONT : 1; /*!< [7..7] Contine to hold the bus after the current transaction + if set to a 1 with a new command issued. */ + __IOM uint32_t TSIZE : 12; /*!< [19..8] Defines the transaction size in bytes. The offset transfer + is not included in this size. */ + __IOM uint32_t CMDSEL : 2; /*!< [21..20] Command Specific selection information */ + __IM uint32_t : 2; + __IOM uint32_t OFFSETLO : 8; /*!< [31..24] This register holds the low order byte of offset to + be used in the transaction. The number of offset bytes + to use is set with bits 1:0 of the command. Offset bytes + are transferred starting from the highest byte first. */ + } CMD_b; + } ; + + union { + __IOM uint32_t CMDRPT; /*!< (@ 0x00000210) Command Repeat Register */ + + struct { + __IOM uint32_t CMDRPT : 5; /*!< [4..0] Count of number of times to repeat the next command. */ + } CMDRPT_b; + } ; + + union { + __IOM uint32_t OFFSETHI; /*!< (@ 0x00000214) High order offset bytes */ + + struct { + __IOM uint32_t OFFSETHI : 16; /*!< [15..0] Holds the high order bytes of the 2 or 3 byte offset + phase of a transaction. */ + } OFFSETHI_b; + } ; + + union { + __IOM uint32_t CMDSTAT; /*!< (@ 0x00000218) Command status */ + + struct { + __IOM uint32_t CCMD : 5; /*!< [4..0] current command that is being executed */ + __IOM uint32_t CMDSTAT : 3; /*!< [7..5] The current status of the command execution. */ + __IOM uint32_t CTSIZE : 12; /*!< [19..8] The current number of bytes still to be transferred + with this command. This field will count down to zero. */ + } CMDSTAT_b; + } ; + __IM uint32_t RESERVED3; + + union { + __IOM uint32_t INTEN; /*!< (@ 0x00000220) IO Master Interrupts: Enable */ + + struct { + __IOM uint32_t CMDCMP : 1; /*!< [0..0] Command Complete interrupt. Asserted when the current + operation has completed. For repeated commands, this will + only be asserted when the final repeated command is completed. */ + __IOM uint32_t THR : 1; /*!< [1..1] FIFO Threshold interrupt. For write operations, asserted + when the number of free bytes in the write FIFO equals + or exceeds the WTHR field.For read operations, asserted + when the number of valid bytes in the read FIFO equals + of exceeds the value set in the RTHR field. */ + __IOM uint32_t FUNDFL : 1; /*!< [2..2] Read FIFO Underflow interrupt. Asserted when a pop operation + is done to a empty read FIFO. */ + __IOM uint32_t FOVFL : 1; /*!< [3..3] Write FIFO Overflow interrupt. This occurs when software + tries to write to a full fifo. The current operation does + not stop. */ + __IOM uint32_t B2MST : 1; /*!< [4..4] B2M State change interrupt. Asserted on any change in + the B2M_STATE signal from the BLE Core. */ + __IOM uint32_t IACC : 1; /*!< [5..5] illegal FIFO access interrupt. Asserted when there is + a overflow or underflow event */ + __IOM uint32_t ICMD : 1; /*!< [6..6] illegal command interrupt. Asserted when a command is + written when an active command is in progress. */ + __IOM uint32_t BLECIRQ : 1; /*!< [7..7] BLE Core IRQ signal. Asserted when the BLE_IRQ signal + from the BLE Core is asserted, indicating the availability + of read data from the BLE Core. */ + __IOM uint32_t BLECSSTAT : 1; /*!< [8..8] BLE Core SPI Status interrupt. Asserted when the SPI_STATUS + signal from the BLE Core is asserted, indicating that SPI + writes can be done to the BLE Core.Transfers to the BLE + Core should only be done when this signal is high. */ + __IOM uint32_t DCMP : 1; /*!< [9..9] DMA Complete. Processing of the DMA operation has completed + and the DMA submodule is returned into the idle state */ + __IOM uint32_t DERR : 1; /*!< [10..10] DMA Error encountered during the processing of the + DMA command. The DMA error could occur when the memory + access specified in the DMA operation is not available + or incorrectly specified. */ + __IOM uint32_t CQPAUSED : 1; /*!< [11..11] Command queue is paused due to an active event enabled + in the PAUSEEN register. The interrupt is posted when the + event is enabled within the PAUSEEN register, the mask + is active in the CQIRQMASK field and the event occurs. */ + __IOM uint32_t CQUPD : 1; /*!< [12..12] Command queue write operation executed a register write + with the register address bit 0 set to 1. The low address + bits in the CQ address fields are unused and bit 0 can + be used to trigger an interrupt to indicate when this register + write is performed by the CQ operation. */ + __IOM uint32_t CQERR : 1; /*!< [13..13] Command queue error during processing. When an error + occurs, the system will stop processing and halt operations + to allow software to take recovery actions */ + __IOM uint32_t B2MSLEEP : 1; /*!< [14..14] The B2M_STATE from the BLE Core transitioned into the + sleep state */ + __IOM uint32_t B2MACTIVE : 1; /*!< [15..15] The B2M_STATE from the BLE Core transitioned into the + active state */ + __IOM uint32_t B2MSHUTDN : 1; /*!< [16..16] The B2M_STATE from the BLE Core transitioned into shutdown + state */ + } INTEN_b; + } ; + + union { + __IOM uint32_t INTSTAT; /*!< (@ 0x00000224) IO Master Interrupts: Status */ + + struct { + __IOM uint32_t CMDCMP : 1; /*!< [0..0] Command Complete interrupt. Asserted when the current + operation has completed. For repeated commands, this will + only be asserted when the final repeated command is completed. */ + __IOM uint32_t THR : 1; /*!< [1..1] FIFO Threshold interrupt. For write operations, asserted + when the number of free bytes in the write FIFO equals + or exceeds the WTHR field.For read operations, asserted + when the number of valid bytes in the read FIFO equals + of exceeds the value set in the RTHR field. */ + __IOM uint32_t FUNDFL : 1; /*!< [2..2] Read FIFO Underflow interrupt. Asserted when a pop operation + is done to a empty read FIFO. */ + __IOM uint32_t FOVFL : 1; /*!< [3..3] Write FIFO Overflow interrupt. This occurs when software + tries to write to a full fifo. The current operation does + not stop. */ + __IOM uint32_t B2MST : 1; /*!< [4..4] B2M State change interrupt. Asserted on any change in + the B2M_STATE signal from the BLE Core. */ + __IOM uint32_t IACC : 1; /*!< [5..5] illegal FIFO access interrupt. Asserted when there is + a overflow or underflow event */ + __IOM uint32_t ICMD : 1; /*!< [6..6] illegal command interrupt. Asserted when a command is + written when an active command is in progress. */ + __IOM uint32_t BLECIRQ : 1; /*!< [7..7] BLE Core IRQ signal. Asserted when the BLE_IRQ signal + from the BLE Core is asserted, indicating the availability + of read data from the BLE Core. */ + __IOM uint32_t BLECSSTAT : 1; /*!< [8..8] BLE Core SPI Status interrupt. Asserted when the SPI_STATUS + signal from the BLE Core is asserted, indicating that SPI + writes can be done to the BLE Core.Transfers to the BLE + Core should only be done when this signal is high. */ + __IOM uint32_t DCMP : 1; /*!< [9..9] DMA Complete. Processing of the DMA operation has completed + and the DMA submodule is returned into the idle state */ + __IOM uint32_t DERR : 1; /*!< [10..10] DMA Error encountered during the processing of the + DMA command. The DMA error could occur when the memory + access specified in the DMA operation is not available + or incorrectly specified. */ + __IOM uint32_t CQPAUSED : 1; /*!< [11..11] Command queue is paused due to an active event enabled + in the PAUSEEN register. The interrupt is posted when the + event is enabled within the PAUSEEN register, the mask + is active in the CQIRQMASK field and the event occurs. */ + __IOM uint32_t CQUPD : 1; /*!< [12..12] Command queue write operation executed a register write + with the register address bit 0 set to 1. The low address + bits in the CQ address fields are unused and bit 0 can + be used to trigger an interrupt to indicate when this register + write is performed by the CQ operation. */ + __IOM uint32_t CQERR : 1; /*!< [13..13] Command queue error during processing. When an error + occurs, the system will stop processing and halt operations + to allow software to take recovery actions */ + __IOM uint32_t B2MSLEEP : 1; /*!< [14..14] The B2M_STATE from the BLE Core transitioned into the + sleep state */ + __IOM uint32_t B2MACTIVE : 1; /*!< [15..15] The B2M_STATE from the BLE Core transitioned into the + active state */ + __IOM uint32_t B2MSHUTDN : 1; /*!< [16..16] The B2M_STATE from the BLE Core transitioned into shutdown + state */ + } INTSTAT_b; + } ; + + union { + __IOM uint32_t INTCLR; /*!< (@ 0x00000228) IO Master Interrupts: Clear */ + + struct { + __IOM uint32_t CMDCMP : 1; /*!< [0..0] Command Complete interrupt. Asserted when the current + operation has completed. For repeated commands, this will + only be asserted when the final repeated command is completed. */ + __IOM uint32_t THR : 1; /*!< [1..1] FIFO Threshold interrupt. For write operations, asserted + when the number of free bytes in the write FIFO equals + or exceeds the WTHR field.For read operations, asserted + when the number of valid bytes in the read FIFO equals + of exceeds the value set in the RTHR field. */ + __IOM uint32_t FUNDFL : 1; /*!< [2..2] Read FIFO Underflow interrupt. Asserted when a pop operation + is done to a empty read FIFO. */ + __IOM uint32_t FOVFL : 1; /*!< [3..3] Write FIFO Overflow interrupt. This occurs when software + tries to write to a full fifo. The current operation does + not stop. */ + __IOM uint32_t B2MST : 1; /*!< [4..4] B2M State change interrupt. Asserted on any change in + the B2M_STATE signal from the BLE Core. */ + __IOM uint32_t IACC : 1; /*!< [5..5] illegal FIFO access interrupt. Asserted when there is + a overflow or underflow event */ + __IOM uint32_t ICMD : 1; /*!< [6..6] illegal command interrupt. Asserted when a command is + written when an active command is in progress. */ + __IOM uint32_t BLECIRQ : 1; /*!< [7..7] BLE Core IRQ signal. Asserted when the BLE_IRQ signal + from the BLE Core is asserted, indicating the availability + of read data from the BLE Core. */ + __IOM uint32_t BLECSSTAT : 1; /*!< [8..8] BLE Core SPI Status interrupt. Asserted when the SPI_STATUS + signal from the BLE Core is asserted, indicating that SPI + writes can be done to the BLE Core.Transfers to the BLE + Core should only be done when this signal is high. */ + __IOM uint32_t DCMP : 1; /*!< [9..9] DMA Complete. Processing of the DMA operation has completed + and the DMA submodule is returned into the idle state */ + __IOM uint32_t DERR : 1; /*!< [10..10] DMA Error encountered during the processing of the + DMA command. The DMA error could occur when the memory + access specified in the DMA operation is not available + or incorrectly specified. */ + __IOM uint32_t CQPAUSED : 1; /*!< [11..11] Command queue is paused due to an active event enabled + in the PAUSEEN register. The interrupt is posted when the + event is enabled within the PAUSEEN register, the mask + is active in the CQIRQMASK field and the event occurs. */ + __IOM uint32_t CQUPD : 1; /*!< [12..12] Command queue write operation executed a register write + with the register address bit 0 set to 1. The low address + bits in the CQ address fields are unused and bit 0 can + be used to trigger an interrupt to indicate when this register + write is performed by the CQ operation. */ + __IOM uint32_t CQERR : 1; /*!< [13..13] Command queue error during processing. When an error + occurs, the system will stop processing and halt operations + to allow software to take recovery actions */ + __IOM uint32_t B2MSLEEP : 1; /*!< [14..14] The B2M_STATE from the BLE Core transitioned into the + sleep state */ + __IOM uint32_t B2MACTIVE : 1; /*!< [15..15] The B2M_STATE from the BLE Core transitioned into the + active state */ + __IOM uint32_t B2MSHUTDN : 1; /*!< [16..16] The B2M_STATE from the BLE Core transitioned into shutdown + state */ + } INTCLR_b; + } ; + + union { + __IOM uint32_t INTSET; /*!< (@ 0x0000022C) IO Master Interrupts: Set */ + + struct { + __IOM uint32_t CMDCMP : 1; /*!< [0..0] Command Complete interrupt. Asserted when the current + operation has completed. For repeated commands, this will + only be asserted when the final repeated command is completed. */ + __IOM uint32_t THR : 1; /*!< [1..1] FIFO Threshold interrupt. For write operations, asserted + when the number of free bytes in the write FIFO equals + or exceeds the WTHR field.For read operations, asserted + when the number of valid bytes in the read FIFO equals + of exceeds the value set in the RTHR field. */ + __IOM uint32_t FUNDFL : 1; /*!< [2..2] Read FIFO Underflow interrupt. Asserted when a pop operation + is done to a empty read FIFO. */ + __IOM uint32_t FOVFL : 1; /*!< [3..3] Write FIFO Overflow interrupt. This occurs when software + tries to write to a full fifo. The current operation does + not stop. */ + __IOM uint32_t B2MST : 1; /*!< [4..4] B2M State change interrupt. Asserted on any change in + the B2M_STATE signal from the BLE Core. */ + __IOM uint32_t IACC : 1; /*!< [5..5] illegal FIFO access interrupt. Asserted when there is + a overflow or underflow event */ + __IOM uint32_t ICMD : 1; /*!< [6..6] illegal command interrupt. Asserted when a command is + written when an active command is in progress. */ + __IOM uint32_t BLECIRQ : 1; /*!< [7..7] BLE Core IRQ signal. Asserted when the BLE_IRQ signal + from the BLE Core is asserted, indicating the availability + of read data from the BLE Core. */ + __IOM uint32_t BLECSSTAT : 1; /*!< [8..8] BLE Core SPI Status interrupt. Asserted when the SPI_STATUS + signal from the BLE Core is asserted, indicating that SPI + writes can be done to the BLE Core.Transfers to the BLE + Core should only be done when this signal is high. */ + __IOM uint32_t DCMP : 1; /*!< [9..9] DMA Complete. Processing of the DMA operation has completed + and the DMA submodule is returned into the idle state */ + __IOM uint32_t DERR : 1; /*!< [10..10] DMA Error encountered during the processing of the + DMA command. The DMA error could occur when the memory + access specified in the DMA operation is not available + or incorrectly specified. */ + __IOM uint32_t CQPAUSED : 1; /*!< [11..11] Command queue is paused due to an active event enabled + in the PAUSEEN register. The interrupt is posted when the + event is enabled within the PAUSEEN register, the mask + is active in the CQIRQMASK field and the event occurs. */ + __IOM uint32_t CQUPD : 1; /*!< [12..12] Command queue write operation executed a register write + with the register address bit 0 set to 1. The low address + bits in the CQ address fields are unused and bit 0 can + be used to trigger an interrupt to indicate when this register + write is performed by the CQ operation. */ + __IOM uint32_t CQERR : 1; /*!< [13..13] Command queue error during processing. When an error + occurs, the system will stop processing and halt operations + to allow software to take recovery actions */ + __IOM uint32_t B2MSLEEP : 1; /*!< [14..14] The B2M_STATE from the BLE Core transitioned into the + sleep state */ + __IOM uint32_t B2MACTIVE : 1; /*!< [15..15] The B2M_STATE from the BLE Core transitioned into the + active state */ + __IOM uint32_t B2MSHUTDN : 1; /*!< [16..16] The B2M_STATE from the BLE Core transitioned into shutdown + state */ + } INTSET_b; + } ; + + union { + __IOM uint32_t DMATRIGEN; /*!< (@ 0x00000230) DMA Trigger Enable Register */ + + struct { + __IOM uint32_t DCMDCMPEN : 1; /*!< [0..0] Trigger DMA upon command complete. Enables the trigger + of the DMA when a command is completed. When this event + is triggered, the number of words transferred will be the + lesser of the remaining TOTCOUNT bytes, or the number of + bytes in the FIFO when the command completed. If this is + disabled, and the number of bytes in the FIFO is equal + or greater than the TOTCOUNT bytes, a transfer of TOTCOUNT + bytes will be done to ensure read data is stored when the + DMA is completed. */ + __IOM uint32_t DTHREN : 1; /*!< [1..1] Trigger DMA upon THR level reached. For M2P DMA operations + (IOM writes), the trigger will assert when the write FIFO + has (WTHR/4) number of words free in the write FIFO, and + will transfer (WTHR/4) number of wordsor, if the number + of words left to transfer is less than the WTHR value, + will transfer the remaining byte count.For P2M DMA operations, + the trigger will assert when the read FIFO has (RTHR/4) + words available in the read FIFO, and will transfer (RTHR/4) + words to SRAM. This trigger will NOT asser */ + } DMATRIGEN_b; + } ; + + union { + __IOM uint32_t DMATRIGSTAT; /*!< (@ 0x00000234) DMA Trigger Status Register */ + + struct { + __IOM uint32_t DCMDCMP : 1; /*!< [0..0] Triggered DMA from Command complete event. Bit is read + only and can be cleared by disabling the DCMDCMP trigger + enable or by disabling DMA. */ + __IOM uint32_t DTHR : 1; /*!< [1..1] Triggered DMA from THR event. Bit is read only and can + be cleared by disabling the DTHR trigger enable or by disabling + DMA. */ + __IOM uint32_t DTOTCMP : 1; /*!< [2..2] DMA triggered when DCMDCMP = 0, and the amount of data + in the FIFO was enough to complete the DMA operation (greater + than or equal to current TOTCOUNT) when the command completed. + This trigger is default active when the DCMDCMP trigger + isdisabled and there is enough data in the FIFO to complete + the DMA operation. */ + } DMATRIGSTAT_b; + } ; + + union { + __IOM uint32_t DMACFG; /*!< (@ 0x00000238) DMA Configuration Register */ + + struct { + __IOM uint32_t DMAEN : 1; /*!< [0..0] DMA Enable. Setting this bit to EN will start the DMA + operation. This should be the last DMA related register + set prior to issuing the command */ + __IOM uint32_t DMADIR : 1; /*!< [1..1] Direction */ + __IM uint32_t : 6; + __IOM uint32_t DMAPRI : 1; /*!< [8..8] Sets the Priority of the DMA request */ + __IOM uint32_t DPWROFF : 1; /*!< [9..9] Power off module after DMA is complete. If this bit is + active, the module will request to power off the supply + it is attached to. If there are other units still requiring + power from the same domain, power down will not be performed. */ + } DMACFG_b; + } ; + + union { + __IOM uint32_t DMATOTCOUNT; /*!< (@ 0x0000023C) DMA Total Transfer Count */ + + struct { + __IOM uint32_t TOTCOUNT : 12; /*!< [11..0] Triggered DMA from Command complete event occured. Bit + is read only and can be cleared by disabling the DTHR trigger + enable or by disabling DMA. */ + } DMATOTCOUNT_b; + } ; + + union { + __IOM uint32_t DMATARGADDR; /*!< (@ 0x00000240) DMA Target Address Register */ + + struct { + __IOM uint32_t TARGADDR : 20; /*!< [19..0] Bits [19:0] of the target byte address for source of + DMA (either read or write). The address can be any byte + alignment, and does not have to be word aligned. In cases + of non-word aligned addresses, the DMA logic will take + care for ensuring only the target bytes are read/written. */ + __IM uint32_t : 8; + __IOM uint32_t TARGADDR28 : 1; /*!< [28..28] Bit 28 of the target byte address for source of DMA + (either read or write). In cases of non-word aligned addresses, + the DMA logic will take care for ensuring only the target + bytes are read/written.Setting to '1' will select the SRAM. + Setting to '0' will select the flash */ + } DMATARGADDR_b; + } ; + + union { + __IOM uint32_t DMASTAT; /*!< (@ 0x00000244) DMA Status Register */ + + struct { + __IOM uint32_t DMATIP : 1; /*!< [0..0] DMA Transfer In Progress indicator. 1 will indicate that + a DMA transfer is active. The DMA transfer may be waiting + on data, transferring data, or waiting for priority.All + of these will be indicated with a 1. A 0 will indicate + that the DMA is fully complete and no further transactions + will be done. This bit is read only. */ + __IOM uint32_t DMACPL : 1; /*!< [1..1] DMA Transfer Complete. This signals the end of the DMA + operation. This bit can be cleared by writing to 0. */ + __IOM uint32_t DMAERR : 1; /*!< [2..2] DMA Error. This active high bit signals that an error + was encountered during the DMA operation. */ + } DMASTAT_b; + } ; + + union { + __IOM uint32_t CQCFG; /*!< (@ 0x00000248) Command Queue Configuration Register */ + + struct { + __IOM uint32_t CQEN : 1; /*!< [0..0] Command queue enable. When set, will enable the processing + of the command queue and fetches of address/data pairs + will proceed from the word address within the CQADDR register. + Can be disabledusing a CQ executed write to this bit as + well. */ + __IOM uint32_t CQPRI : 1; /*!< [1..1] Sets the Priority of the command queue dma request. */ + } CQCFG_b; + } ; + + union { + __IOM uint32_t CQADDR; /*!< (@ 0x0000024C) CQ Target Read Address Register */ + + struct { + __IM uint32_t : 2; + __IOM uint32_t CQADDR : 18; /*!< [19..2] Bits 19:2 of target byte address for source of CQ (read + only). The buffer must be aligned on a word boundary */ + __IM uint32_t : 8; + __IOM uint32_t CQADDR28 : 1; /*!< [28..28] Bit 28 of target byte address for source of CQ (read + only). Used to denote Flash (0) or SRAM (1) access */ + } CQADDR_b; + } ; + + union { + __IOM uint32_t CQSTAT; /*!< (@ 0x00000250) Command Queue Status Register */ + + struct { + __IOM uint32_t CQTIP : 1; /*!< [0..0] Command queue Transfer In Progress indicator. 1 will + indicate that a CQ transfer is active and this will remain + active even when paused waiting for external event. */ + __IOM uint32_t CQPAUSED : 1; /*!< [1..1] Command queue operation is currently paused. */ + __IOM uint32_t CQERR : 1; /*!< [2..2] Command queue processing Error. This active high bit + signals that an error was encountered during the CQ operation. */ + } CQSTAT_b; + } ; + + union { + __IOM uint32_t CQFLAGS; /*!< (@ 0x00000254) Command Queue Flag Register */ + + struct { + __IOM uint32_t CQFLAGS : 16; /*!< [15..0] Current flag status (read-only). Bits [7:0] are software + controllable and bits [15:8] are hardware status. */ + __IOM uint32_t CQIRQMASK : 16; /*!< [31..16] Provides for a per-bit mask of the flags used to invoke + an interrupt. A '1' in the bit position will enable the + pause event to trigger the interrupt, if the CQWT_int interrupt + is enabled.Bits definitions are the same as CQPAUSE */ + } CQFLAGS_b; + } ; + + union { + __IOM uint32_t CQSETCLEAR; /*!< (@ 0x00000258) Command Queue Flag Set/Clear Register */ + + struct { + __IOM uint32_t CQFSET : 8; /*!< [7..0] Set CQFlag status bits. Will set to 1 the value of any + SWFLAG with a '1' in the corresponding bit position of + this field */ + __IOM uint32_t CQFTGL : 8; /*!< [15..8] Toggle the indicated bit. Will toggle the value of any + SWFLAG with a '1' in the corresponding bit position of + this field */ + __IOM uint32_t CQFCLR : 8; /*!< [23..16] Clear CQFlag status bits. Will clear to 0 any SWFLAG + with a '1' in the corresponding bit position of this field */ + } CQSETCLEAR_b; + } ; + + union { + __IOM uint32_t CQPAUSEEN; /*!< (@ 0x0000025C) Command Queue Pause Enable Register */ + + struct { + __IOM uint32_t CQPEN : 16; /*!< [15..0] Enables the specified event to pause command processing + when active */ + } CQPAUSEEN_b; + } ; + + union { + __IOM uint32_t CQCURIDX; /*!< (@ 0x00000260) IOM Command Queue current index value . Compared + to the CQENDIDX reg contents to generate + the IDXEQ Pause event for command queue */ + + struct { + __IOM uint32_t CQCURIDX : 8; /*!< [7..0] Holds 8 bits of data that will be compared with the CQENDIX + register field. If the values match, the IDXEQ pause event + will be activated, which will cause the pausing of command + quue operation if the IDXEQ bit is enabled in CQPAUSEEN. */ + } CQCURIDX_b; + } ; + + union { + __IOM uint32_t CQENDIDX; /*!< (@ 0x00000264) IOM Command Queue current index value . Compared + to the CQCURIDX reg contents to generate + the IDXEQ Pause event for command queue */ + + struct { + __IOM uint32_t CQENDIDX : 8; /*!< [7..0] Holds 8 bits of data that will be compared with the CQCURIX + register field. If the values match, the IDXEQ pause event + will be activated, which will cause the pausing of command + quue operation if the IDXEQ bit is enabled in CQPAUSEEN. */ + } CQENDIDX_b; + } ; + + union { + __IOM uint32_t STATUS; /*!< (@ 0x00000268) IOM Module Status Register */ + + struct { + __IOM uint32_t ERR : 1; /*!< [0..0] Bit has been deprecated. Please refer to the other error + indicators. This will always return 0. */ + __IOM uint32_t CMDACT : 1; /*!< [1..1] Indicates if the active I/O Command is currently processing + a transaction, or command is complete, but the FIFO pointers + are still syncronizing internally. This bit will go high + atthe start of the transaction, and will go low when the + command is complete, and the data and pointers within the + FIFO have been syncronized. */ + __IOM uint32_t IDLEST : 1; /*!< [2..2] indicates if the active I/O state machine is IDLE. Note + - The state machine could be in idle state due to holdoffs + from data availability, or as the command gets propagated + into the logic from the registers. */ + } STATUS_b; + } ; + __IM uint32_t RESERVED4[37]; + + union { + __IOM uint32_t MSPICFG; /*!< (@ 0x00000300) SPI module master configuration */ + + struct { + __IOM uint32_t SPOL : 1; /*!< [0..0] This bit selects SPI polarity. */ + __IOM uint32_t SPHA : 1; /*!< [1..1] Selects the SPI phase; When 1, will shift the sampling + edge by 1/2 clock. */ + __IOM uint32_t FULLDUP : 1; /*!< [2..2] Full Duplex mode. Capture read data during writes operations */ + __IM uint32_t : 13; + __IOM uint32_t WTFC : 1; /*!< [16..16] Enables flow control of new write transactions based + on the SPI_STATUS signal from the BLE Core. */ + __IOM uint32_t RDFC : 1; /*!< [17..17] Enables flow control of new read transactions based + on the SPI_STATUS signal from the BLE Core. */ + __IM uint32_t : 3; + __IOM uint32_t WTFCPOL : 1; /*!< [21..21] Selects the write flow control signal polarity. The + transfers are halted when the selected flow control signal + is OPPOSITE polarity of this bit. (For example: WTFCPOL + = 0 will allow a SPI_STATUS=1 to pause transfers). */ + __IOM uint32_t RDFCPOL : 1; /*!< [22..22] Selects the read flow control signal polarity. When + set, the clock will be held low until the flow control + is de-asserted. */ + __IOM uint32_t SPILSB : 1; /*!< [23..23] Selects data transfer as MSB first (0) or LSB first + (1) for the data portion of the SPI transaction. The offset + bytes are always transmitted MSB first. */ + __IOM uint32_t DINDLY : 3; /*!< [26..24] Delay tap to use for the input signal (MISO). This + gives more hold time on the input data. */ + __IOM uint32_t DOUTDLY : 3; /*!< [29..27] Delay tap to use for the output signal (MOSI). This + give more hold time on the output data. */ + __IOM uint32_t MSPIRST : 1; /*!< [30..30] Bit is deprecated. setting it will have no effect. */ + } MSPICFG_b; + } ; + + union { + __IOM uint32_t BLECFG; /*!< (@ 0x00000304) BLE Core Control */ + + struct { + __IOM uint32_t PWRSMEN : 1; /*!< [0..0] Enable the power state machine for automatic sequencing + and control of power states of the BLE Core module. */ + __IOM uint32_t BLERSTN : 1; /*!< [1..1] Reset line to the BLE Core. This will reset the BLE core + when asserted ('0') and must be written to '1' prior to + performing any BTLE related operations to the core. */ + __IOM uint32_t WAKEUPCTL : 2; /*!< [3..2] WAKE signal override. Controls the source of the WAKE + signal to the BLE Core. */ + __IOM uint32_t DCDCFLGCTL : 2; /*!< [5..4] DCDCFLG signal override. The value of this field will + be sent to the BLE Core when the PWRSM is off. Otherwise, + the value is supplied from internal logic. */ + __IOM uint32_t BLEHREQCTL : 2; /*!< [7..6] BLEH power on request override. The value of this field + will be sent to the BLE Core when the PWRSM is off. Otherwise, + the value is supplied from internal logic. */ + __IOM uint32_t WT4ACTOFF : 1; /*!< [8..8] Debug control of BLEIF power state machine. Allows transition + into the active state in the BLEIF state without waiting + for dcdc req from BLE Core. */ + __IOM uint32_t MCUFRCSLP : 1; /*!< [9..9] Force power state machine to go to the sleep state. Intended + for debug only. Has no effect on the actual BLE Core state, + only the state of the BLEIF interface state machine. */ + __IOM uint32_t FRCCLK : 1; /*!< [10..10] Force the clock in the BLEIF to be always running */ + __IOM uint32_t STAYASLEEP : 1; /*!< [11..11] Set to prevent the BLE power control module from waking + up the BLE Core after going into power down. To be used + for graceful shutdown, set by software prior to powering + off and will allow assertion of reset from sleep state. */ + __IOM uint32_t PWRISOCTL : 2; /*!< [13..12] Configuration of BLEH isolation control for power related + signals. */ + __IOM uint32_t SPIISOCTL : 2; /*!< [15..14] Configuration of BLEH isolation controls for SPI related + signals. */ + } BLECFG_b; + } ; + + union { + __IOM uint32_t PWRCMD; /*!< (@ 0x00000308) BLE Power command interface */ + + struct { + __IOM uint32_t WAKEREQ : 1; /*!< [0..0] Wake request from the MCU. When asserted (1), the BLE + Interface logic will assert the wakeup request signal to + the BLE Core. Only recognized when in the sleep state */ + __IOM uint32_t RESTART : 1; /*!< [1..1] Restart the BLE Core after going into the shutdown state. + Only valid when in the shutdown state. */ + } PWRCMD_b; + } ; + + union { + __IOM uint32_t BSTATUS; /*!< (@ 0x0000030C) BLE Core status */ + + struct { + __IOM uint32_t B2MSTATE : 3; /*!< [2..0] State of the BLE Core logic. */ + __IOM uint32_t SPISTATUS : 1; /*!< [3..3] Value of the SPISTATUS signal from the BLE Core. The + signal is asserted when the BLE Core is able to accept + write data via the SPI interface. Data should be transmitted + to theBLE core only when this signal is 1. The hardware + will automatically wait for this signal prior to performing + a write operation if flow control is active. */ + __IOM uint32_t DCDCREQ : 1; /*!< [4..4] Value of the DCDCREQ signal from the BLE Core. The DCDCREQ + signal is sent from the core to the BLEIF module when the + BLE core requires BLEH power to be active. When activated, + this isindicated by DCDCFLAG going to 1. */ + __IOM uint32_t DCDCFLAG : 1; /*!< [5..5] Value of the DCDCFLAG signal to the BLE Core. The DCDCFLAG + is a signal to the BLE Core indicating that the BLEH ppower + is active. */ + __IOM uint32_t WAKEUP : 1; /*!< [6..6] Value of the WAKEUP signal to the BLE Core . The WAKEUP + signals is sent from the BLEIF to the BLECORE to request + the BLE Core transition from sleep state to active state. */ + __IOM uint32_t BLEIRQ : 1; /*!< [7..7] Status of the BLEIRQ signal from the BLE Core. A value + of 1 idicates that read data is available in the core and + a read operation needs to be performed. */ + __IOM uint32_t PWRST : 3; /*!< [10..8] Current status of the power state machine */ + __IOM uint32_t BLEHACK : 1; /*!< [11..11] Value of the BLEHACK signal from the power control + unit. If the signal is '1', the BLEH power is active and + ready for use. */ + __IOM uint32_t BLEHREQ : 1; /*!< [12..12] Value of the BLEHREQ signal to the power control unit. + The BLEHREQ signal is sent from the BLEIF module to the + power control module to request the BLEH power up. When + the BLEHACK signal is asserted,BLEH power is stable and + ready for use. */ + } BSTATUS_b; + } ; + __IM uint32_t RESERVED5[64]; + + union { + __IOM uint32_t BLEDBG; /*!< (@ 0x00000410) BLEIF Master Debug Register */ + + struct { + __IOM uint32_t DBGEN : 1; /*!< [0..0] Debug Enable. Setting this bit will enable the update + of data within this register, otherwise it is clock gated + for power savings */ + __IOM uint32_t IOCLKON : 1; /*!< [1..1] IOCLK debug clock control. Enable IO_CLK to be active + when this bit is '1'. Otherwise, the clock is controlled + with gating from the logic as needed. */ + __IOM uint32_t APBCLKON : 1; /*!< [2..2] APBCLK debug clock control. Enable APB_CLK to be active + when this bit is '1'. Otherwise, the clock is controlled + with gating from the logic as needed. */ + __IOM uint32_t DBGDATA : 29; /*!< [31..3] Debug data */ + } BLEDBG_b; + } ; +} BLEIF_Type; /*!< Size = 1044 (0x414) */ + + + +/* =========================================================================================================================== */ +/* ================ CACHECTRL ================ */ +/* =========================================================================================================================== */ + + +/** + * @brief Flash Cache Controller (CACHECTRL) + */ + +typedef struct { /*!< (@ 0x40018000) CACHECTRL Structure */ + + union { + __IOM uint32_t CACHECFG; /*!< (@ 0x00000000) Flash Cache Control Register */ + + struct { + __IOM uint32_t ENABLE : 1; /*!< [0..0] Enables the flash cache controller and enables power + to the cache SRAMs. The ICACHE_ENABLE and DCACHE_ENABLE + should be set to enable caching for each type of access. */ + __IOM uint32_t LRU : 1; /*!< [1..1] Sets the cache repleacment policy. 0=LRR (least recently + replaced), 1=LRU (least recently used). LRR minimizes writes + to the TAG SRAM. */ + __IOM uint32_t ENABLE_NC0 : 1; /*!< [2..2] Enable Non-cacheable region 0. See NCR0 registers to + define the region. */ + __IOM uint32_t ENABLE_NC1 : 1; /*!< [3..3] Enable Non-cacheable region 1. See NCR1 registers to + define the region. */ + __IOM uint32_t CONFIG : 4; /*!< [7..4] Sets the cache configuration */ + __IOM uint32_t ICACHE_ENABLE : 1; /*!< [8..8] Enable Flash Instruction Caching */ + __IOM uint32_t DCACHE_ENABLE : 1; /*!< [9..9] Enable Flash Data Caching. */ + __IOM uint32_t CACHE_CLKGATE : 1; /*!< [10..10] Enable clock gating of cache TAG RAM. Software should + enable this bit for optimal power efficiency. */ + __IOM uint32_t CACHE_LS : 1; /*!< [11..11] Enable LS (light sleep) of cache RAMs. Software should + DISABLE this bit since cache activity is too high to benefit + from LS usage. */ + __IM uint32_t : 8; + __IOM uint32_t DATA_CLKGATE : 1; /*!< [20..20] Enable aggressive clock gating of entire data array. + This bit should be set to 1 for optimal power efficiency. */ + __IM uint32_t : 3; + __IOM uint32_t ENABLE_MONITOR : 1; /*!< [24..24] Enable Cache Monitoring Stats. Cache monitoring consumes + additional power and should only be enabled when profiling + code and counters will increment when this bit is set. + Counter values will be retained when this is set to 0, + allowing software to enable/disable counting for multiple + code segments. */ + } CACHECFG_b; + } ; + + union { + __IOM uint32_t FLASHCFG; /*!< (@ 0x00000004) Flash Control Register */ + + struct { + __IOM uint32_t RD_WAIT : 4; /*!< [3..0] Sets read waitstates for normal (fast) operation. A value + of 1 is recommended. */ + __IOM uint32_t SEDELAY : 3; /*!< [6..4] Sets SE delay (flash address setup). A value of 5 is + recommended. */ + __IM uint32_t : 1; + __IOM uint32_t LPM_RD_WAIT : 4; /*!< [11..8] Sets flash waitstates when in LPM Mode 2 (RD_WAIT in + LPM mode 2 only) */ + __IOM uint32_t LPMMODE : 2; /*!< [13..12] Controls flash low power modes (control of LPM pin). */ + } FLASHCFG_b; + } ; + + union { + __IOM uint32_t CTRL; /*!< (@ 0x00000008) Cache Control */ + + struct { + __IOM uint32_t INVALIDATE : 1; /*!< [0..0] Writing a 1 to this bitfield invalidates the flash cache + contents. */ + __IOM uint32_t RESET_STAT : 1; /*!< [1..1] Reset Cache Statistics. When written to a 1, the cache + monitor counters will be cleared. The monitor counters + can be reset only when the CACHECFG.ENABLE_MONITOR bit + is set. */ + __IOM uint32_t CACHE_READY : 1; /*!< [2..2] Cache Ready Status (enabled and not processing an invalidate + operation) */ + __IM uint32_t : 1; + __IOM uint32_t FLASH0_SLM_STATUS : 1; /*!< [4..4] Flash Sleep Mode Status. 1 indicates that flash0 is in + sleep mode, 0 indicates flash0 is in normal mode. */ + __IOM uint32_t FLASH0_SLM_DISABLE : 1; /*!< [5..5] Disable Flash Sleep Mode. Write 1 to wake flash0 from + sleep mode (reading the array will also automatically wake + it). */ + __IOM uint32_t FLASH0_SLM_ENABLE : 1; /*!< [6..6] Enable Flash Sleep Mode. Write to 1 to put flash 0 into + sleep mode. NOTE: there is a 5us latency after waking flash + until the first access will be returned. */ + __IM uint32_t : 1; + __IOM uint32_t FLASH1_SLM_STATUS : 1; /*!< [8..8] Flash Sleep Mode Status. 1 indicates that flash1 is in + sleep mode, 0 indicates flash1 is in normal mode. */ + __IOM uint32_t FLASH1_SLM_DISABLE : 1; /*!< [9..9] Disable Flash Sleep Mode. Write 1 to wake flash1 from + sleep mode (reading the array will also automatically wake + it). */ + __IOM uint32_t FLASH1_SLM_ENABLE : 1; /*!< [10..10] Enable Flash Sleep Mode. Write to 1 to put flash 1 + into sleep mode. NOTE: there is a 5us latency after waking + flash until the first access will be returned. */ + } CTRL_b; + } ; + __IM uint32_t RESERVED; + + union { + __IOM uint32_t NCR0START; /*!< (@ 0x00000010) Flash Cache Noncachable Region 0 Start */ + + struct { + __IM uint32_t : 4; + __IOM uint32_t ADDR : 23; /*!< [26..4] Start address for non-cacheable region 0 */ + } NCR0START_b; + } ; + + union { + __IOM uint32_t NCR0END; /*!< (@ 0x00000014) Flash Cache Noncachable Region 0 End */ + + struct { + __IM uint32_t : 4; + __IOM uint32_t ADDR : 23; /*!< [26..4] End address for non-cacheable region 0 */ + } NCR0END_b; + } ; + + union { + __IOM uint32_t NCR1START; /*!< (@ 0x00000018) Flash Cache Noncachable Region 1 Start */ + + struct { + __IM uint32_t : 4; + __IOM uint32_t ADDR : 23; /*!< [26..4] Start address for non-cacheable region 1 */ + } NCR1START_b; + } ; + + union { + __IOM uint32_t NCR1END; /*!< (@ 0x0000001C) Flash Cache Noncachable Region 1 End */ + + struct { + __IM uint32_t : 4; + __IOM uint32_t ADDR : 23; /*!< [26..4] End address for non-cacheable region 1 */ + } NCR1END_b; + } ; + __IM uint32_t RESERVED1[8]; + + union { + __IOM uint32_t DMON0; /*!< (@ 0x00000040) Data Cache Total Accesses */ + + struct { + __IOM uint32_t DACCESS_COUNT : 32; /*!< [31..0] Total accesses to data cache. All performance metrics + should be relative to the number of accesses performed. */ + } DMON0_b; + } ; + + union { + __IOM uint32_t DMON1; /*!< (@ 0x00000044) Data Cache Tag Lookups */ + + struct { + __IOM uint32_t DLOOKUP_COUNT : 32; /*!< [31..0] Total tag lookups from data cache. */ + } DMON1_b; + } ; + + union { + __IOM uint32_t DMON2; /*!< (@ 0x00000048) Data Cache Hits */ + + struct { + __IOM uint32_t DHIT_COUNT : 32; /*!< [31..0] Cache hits from lookup operations. */ + } DMON2_b; + } ; + + union { + __IOM uint32_t DMON3; /*!< (@ 0x0000004C) Data Cache Line Hits */ + + struct { + __IOM uint32_t DLINE_COUNT : 32; /*!< [31..0] Cache hits from line cache */ + } DMON3_b; + } ; + + union { + __IOM uint32_t IMON0; /*!< (@ 0x00000050) Instruction Cache Total Accesses */ + + struct { + __IOM uint32_t IACCESS_COUNT : 32; /*!< [31..0] Total accesses to Instruction cache */ + } IMON0_b; + } ; + + union { + __IOM uint32_t IMON1; /*!< (@ 0x00000054) Instruction Cache Tag Lookups */ + + struct { + __IOM uint32_t ILOOKUP_COUNT : 32; /*!< [31..0] Total tag lookups from Instruction cache */ + } IMON1_b; + } ; + + union { + __IOM uint32_t IMON2; /*!< (@ 0x00000058) Instruction Cache Hits */ + + struct { + __IOM uint32_t IHIT_COUNT : 32; /*!< [31..0] Cache hits from lookup operations */ + } IMON2_b; + } ; + + union { + __IOM uint32_t IMON3; /*!< (@ 0x0000005C) Instruction Cache Line Hits */ + + struct { + __IOM uint32_t ILINE_COUNT : 32; /*!< [31..0] Cache hits from line cache */ + } IMON3_b; + } ; +} CACHECTRL_Type; /*!< Size = 96 (0x60) */ + + + +/* =========================================================================================================================== */ +/* ================ CLKGEN ================ */ +/* =========================================================================================================================== */ + + +/** + * @brief Clock Generator (CLKGEN) + */ + +typedef struct { /*!< (@ 0x40004000) CLKGEN Structure */ + + union { + __IOM uint32_t CALXT; /*!< (@ 0x00000000) XT Oscillator Control */ + + struct { + __IOM uint32_t CALXT : 11; /*!< [10..0] XT Oscillator calibration value. This register will + enable the hardware to increase or decrease the number + of cycles in a 16KHz clock derived from the original 32KHz + version. The most significant bit is the sign. A '1' is + a reduction, and a '0' is an addition. This calibration + value will add or reduce the number of cycles programmed + here across a 32 second interval. The maximum value that + is effective is from -1024 to 1023. */ + } CALXT_b; + } ; + + union { + __IOM uint32_t CALRC; /*!< (@ 0x00000004) RC Oscillator Control */ + + struct { + __IOM uint32_t CALRC : 18; /*!< [17..0] LFRC Oscillator calibration value. This register will + enable the hardware to increase or decrease the number + of cycles in a 512 Hz clock derived from the original 1024 + version. The most significant bit is the sign. A '1' is + a reduction, and a '0' is an addition. This calibration + value will add or reduce the number of cycles programmed + here across a 32 second interval. The range is from -131072 + (decimal) to 131071 (decimal). This register is normally + used in conjuction with ACALCTR register. The CAL */ + } CALRC_b; + } ; + + union { + __IOM uint32_t ACALCTR; /*!< (@ 0x00000008) Autocalibration Counter */ + + struct { + __IOM uint32_t ACALCTR : 24; /*!< [23..0] Autocalibration Counter result. Bits 17 down to 0 of + this is feed directly to the CALRC register if ACAL register + in OCTRL register is set to 1024SEC or 512SEC. */ + } ACALCTR_b; + } ; + + union { + __IOM uint32_t OCTRL; /*!< (@ 0x0000000C) Oscillator Control */ + + struct { + __IOM uint32_t STOPXT : 1; /*!< [0..0] Stop the XT Oscillator to the RTC */ + __IOM uint32_t STOPRC : 1; /*!< [1..1] Stop the LFRC Oscillator to the RTC */ + __IM uint32_t : 4; + __IOM uint32_t FOS : 1; /*!< [6..6] Oscillator switch on failure function. If this is set, + then LFRC clock source will switch from XT to RC. */ + __IOM uint32_t OSEL : 1; /*!< [7..7] Selects the RTC oscillator (1 => LFRC, 0 => XT) */ + __IOM uint32_t ACAL : 3; /*!< [10..8] Autocalibration control. This selects the source to + be used in the autocalibration flow. This flow can also + be used to measure an internal clock against an external + clock source, with the external clock normally used as + the reference. */ + } OCTRL_b; + } ; + + union { + __IOM uint32_t CLKOUT; /*!< (@ 0x00000010) CLKOUT Frequency Select */ + + struct { + __IOM uint32_t CKSEL : 6; /*!< [5..0] CLKOUT signal select */ + __IM uint32_t : 1; + __IOM uint32_t CKEN : 1; /*!< [7..7] Enable the CLKOUT signal */ + } CLKOUT_b; + } ; + + union { + __IOM uint32_t CLKKEY; /*!< (@ 0x00000014) Key Register for Clock Control Register */ + + struct { + __IOM uint32_t CLKKEY : 32; /*!< [31..0] Key register value. */ + } CLKKEY_b; + } ; + + union { + __IOM uint32_t CCTRL; /*!< (@ 0x00000018) HFRC Clock Control */ + + struct { + __IOM uint32_t CORESEL : 1; /*!< [0..0] Core Clock divisor */ + } CCTRL_b; + } ; + + union { + __IOM uint32_t STATUS; /*!< (@ 0x0000001C) Clock Generator Status */ + + struct { + __IOM uint32_t OMODE : 1; /*!< [0..0] Current RTC oscillator (1 => LFRC, 0 => XT). After an + RTC oscillator change, it may take up to 2 seconds for + this field to reflect the new oscillator. */ + __IOM uint32_t OSCF : 1; /*!< [1..1] XT Oscillator is enabled but not oscillating */ + } STATUS_b; + } ; + + union { + __IOM uint32_t HFADJ; /*!< (@ 0x00000020) HFRC Adjustment */ + + struct { + __IOM uint32_t HFADJEN : 1; /*!< [0..0] HFRC adjustment control */ + __IOM uint32_t HFADJCK : 3; /*!< [3..1] Repeat period for HFRC adjustment */ + __IM uint32_t : 4; + __IOM uint32_t HFXTADJ : 12; /*!< [19..8] Target HFRC adjustment value. */ + __IOM uint32_t HFWARMUP : 1; /*!< [20..20] XT warmup period for HFRC adjustment */ + __IOM uint32_t HFADJGAIN : 3; /*!< [23..21] Gain control for HFRC adjustment */ + } HFADJ_b; + } ; + __IM uint32_t RESERVED; + + union { + __IOM uint32_t CLOCKENSTAT; /*!< (@ 0x00000028) Clock Enable Status */ + + struct { + __IOM uint32_t CLOCKENSTAT : 32; /*!< [31..0] Clock enable status */ + } CLOCKENSTAT_b; + } ; + + union { + __IOM uint32_t CLOCKEN2STAT; /*!< (@ 0x0000002C) Clock Enable Status */ + + struct { + __IOM uint32_t CLOCKEN2STAT : 32; /*!< [31..0] Clock enable status 2 */ + } CLOCKEN2STAT_b; + } ; + + union { + __IOM uint32_t CLOCKEN3STAT; /*!< (@ 0x00000030) Clock Enable Status */ + + struct { + __IOM uint32_t CLOCKEN3STAT : 32; /*!< [31..0] Clock enable status 3 */ + } CLOCKEN3STAT_b; + } ; + + union { + __IOM uint32_t FREQCTRL; /*!< (@ 0x00000034) HFRC Frequency Control register */ + + struct { + __IOM uint32_t BURSTREQ : 1; /*!< [0..0] Frequency Burst Enable Request */ + __IOM uint32_t BURSTACK : 1; /*!< [1..1] Frequency Burst Request Acknowledge. Frequency burst + requested is always acknowledged whether burst is granted + or not depending on feature enable. */ + __IOM uint32_t BURSTSTATUS : 1; /*!< [2..2] This represents frequency burst status. */ + } FREQCTRL_b; + } ; + __IM uint32_t RESERVED1; + + union { + __IOM uint32_t BLEBUCKTONADJ; /*!< (@ 0x0000003C) BLE BUCK TON ADJUST */ + + struct { + __IOM uint32_t TONLOWTHRESHOLD : 10; /*!< [9..0] TON ADJUST LOW THRESHOLD. Suggested values are #A(94KHz) + #15(47KHz) #53(12Khz) #14D(3Khz) */ + __IOM uint32_t TONHIGHTHRESHOLD : 10; /*!< [19..10] TON ADJUST HIGH THRESHOLD. Suggested values are #15(94KHz) + #2A(47Khz) #A6(12Khz) #29A(3Khz) */ + __IOM uint32_t TONADJUSTPERIOD : 2; /*!< [21..20] TON ADJUST PERIOD */ + __IOM uint32_t TONADJUSTEN : 1; /*!< [22..22] TON ADJUST ENABLE */ + __IOM uint32_t ZEROLENDETECTTRIM : 4; /*!< [26..23] BLEBUCK ZERO LENGTH DETECT TRIM */ + __IOM uint32_t ZEROLENDETECTEN : 1; /*!< [27..27] BLEBUCK ZERO LENGTH DETECT ENABLE */ + } BLEBUCKTONADJ_b; + } ; + __IM uint32_t RESERVED2[48]; + + union { + __IOM uint32_t INTRPTEN; /*!< (@ 0x00000100) CLKGEN Interrupt Register: Enable */ + + struct { + __IOM uint32_t ACF : 1; /*!< [0..0] Autocalibration Fail interrupt */ + __IOM uint32_t ACC : 1; /*!< [1..1] Autocalibration Complete interrupt */ + __IOM uint32_t OF : 1; /*!< [2..2] XT Oscillator Fail interrupt */ + } INTRPTEN_b; + } ; + + union { + __IOM uint32_t INTRPTSTAT; /*!< (@ 0x00000104) CLKGEN Interrupt Register: Status */ + + struct { + __IOM uint32_t ACF : 1; /*!< [0..0] Autocalibration Fail interrupt */ + __IOM uint32_t ACC : 1; /*!< [1..1] Autocalibration Complete interrupt */ + __IOM uint32_t OF : 1; /*!< [2..2] XT Oscillator Fail interrupt */ + } INTRPTSTAT_b; + } ; + + union { + __IOM uint32_t INTRPTCLR; /*!< (@ 0x00000108) CLKGEN Interrupt Register: Clear */ + + struct { + __IOM uint32_t ACF : 1; /*!< [0..0] Autocalibration Fail interrupt */ + __IOM uint32_t ACC : 1; /*!< [1..1] Autocalibration Complete interrupt */ + __IOM uint32_t OF : 1; /*!< [2..2] XT Oscillator Fail interrupt */ + } INTRPTCLR_b; + } ; + + union { + __IOM uint32_t INTRPTSET; /*!< (@ 0x0000010C) CLKGEN Interrupt Register: Set */ + + struct { + __IOM uint32_t ACF : 1; /*!< [0..0] Autocalibration Fail interrupt */ + __IOM uint32_t ACC : 1; /*!< [1..1] Autocalibration Complete interrupt */ + __IOM uint32_t OF : 1; /*!< [2..2] XT Oscillator Fail interrupt */ + } INTRPTSET_b; + } ; +} CLKGEN_Type; /*!< Size = 272 (0x110) */ + + + +/* =========================================================================================================================== */ +/* ================ CTIMER ================ */ +/* =========================================================================================================================== */ + + +/** + * @brief Counter/Timer (CTIMER) + */ + +typedef struct { /*!< (@ 0x40008000) CTIMER Structure */ + + union { + __IOM uint32_t TMR0; /*!< (@ 0x00000000) Counter/Timer Register */ + + struct { + __IOM uint32_t CTTMRA0 : 16; /*!< [15..0] Counter/Timer A0. */ + __IOM uint32_t CTTMRB0 : 16; /*!< [31..16] Counter/Timer B0. */ + } TMR0_b; + } ; + + union { + __IOM uint32_t CMPRA0; /*!< (@ 0x00000004) Counter/Timer A0 Compare Registers */ + + struct { + __IOM uint32_t CMPR0A0 : 16; /*!< [15..0] Counter/Timer A0 Compare Register 0. Holds the lower + limit for timer half A. */ + __IOM uint32_t CMPR1A0 : 16; /*!< [31..16] Counter/Timer A0 Compare Register 1. Holds the upper + limit for timer half A. */ + } CMPRA0_b; + } ; + + union { + __IOM uint32_t CMPRB0; /*!< (@ 0x00000008) Counter/Timer B0 Compare Registers */ + + struct { + __IOM uint32_t CMPR0B0 : 16; /*!< [15..0] Counter/Timer B0 Compare Register 0. Holds the lower + limit for timer half B. */ + __IOM uint32_t CMPR1B0 : 16; /*!< [31..16] Counter/Timer B0 Compare Register 1. Holds the upper + limit for timer half B. */ + } CMPRB0_b; + } ; + + union { + __IOM uint32_t CTRL0; /*!< (@ 0x0000000C) Counter/Timer Control */ + + struct { + __IOM uint32_t TMRA0EN : 1; /*!< [0..0] Counter/Timer A0 Enable bit. */ + __IOM uint32_t TMRA0CLK : 5; /*!< [5..1] Counter/Timer A0 Clock Select. */ + __IOM uint32_t TMRA0FN : 3; /*!< [8..6] Counter/Timer A0 Function Select. */ + __IOM uint32_t TMRA0IE0 : 1; /*!< [9..9] Counter/Timer A0 Interrupt Enable bit based on COMPR0. */ + __IOM uint32_t TMRA0IE1 : 1; /*!< [10..10] Counter/Timer A0 Interrupt Enable bit based on COMPR1. */ + __IOM uint32_t TMRA0CLR : 1; /*!< [11..11] Counter/Timer A0 Clear bit. */ + __IOM uint32_t TMRA0POL : 1; /*!< [12..12] Counter/Timer A0 output polarity. */ + __IM uint32_t : 3; + __IOM uint32_t TMRB0EN : 1; /*!< [16..16] Counter/Timer B0 Enable bit. */ + __IOM uint32_t TMRB0CLK : 5; /*!< [21..17] Counter/Timer B0 Clock Select. */ + __IOM uint32_t TMRB0FN : 3; /*!< [24..22] Counter/Timer B0 Function Select. */ + __IOM uint32_t TMRB0IE0 : 1; /*!< [25..25] Counter/Timer B0 Interrupt Enable bit for COMPR0. */ + __IOM uint32_t TMRB0IE1 : 1; /*!< [26..26] Counter/Timer B0 Interrupt Enable bit for COMPR1. */ + __IOM uint32_t TMRB0CLR : 1; /*!< [27..27] Counter/Timer B0 Clear bit. */ + __IOM uint32_t TMRB0POL : 1; /*!< [28..28] Counter/Timer B0 output polarity. */ + __IM uint32_t : 2; + __IOM uint32_t CTLINK0 : 1; /*!< [31..31] Counter/Timer A0/B0 Link bit. */ + } CTRL0_b; + } ; + __IM uint32_t RESERVED; + + union { + __IOM uint32_t CMPRAUXA0; /*!< (@ 0x00000014) Counter/Timer A0 Compare Registers */ + + struct { + __IOM uint32_t CMPR2A0 : 16; /*!< [15..0] Counter/Timer A0 Compare Register 2. Holds the lower + limit for timer half A. */ + __IOM uint32_t CMPR3A0 : 16; /*!< [31..16] Counter/Timer A0 Compare Register 3. Holds the upper + limit for timer half A. */ + } CMPRAUXA0_b; + } ; + + union { + __IOM uint32_t CMPRAUXB0; /*!< (@ 0x00000018) Counter/Timer B0 Compare Registers */ + + struct { + __IOM uint32_t CMPR2B0 : 16; /*!< [15..0] Counter/Timer B0 Compare Register 2. Holds the lower + limit for timer half B. */ + __IOM uint32_t CMPR3B0 : 16; /*!< [31..16] Counter/Timer B0 Compare Register 3. Holds the upper + limit for timer half B. */ + } CMPRAUXB0_b; + } ; + + union { + __IOM uint32_t AUX0; /*!< (@ 0x0000001C) Counter/Timer Auxiliary */ + + struct { + __IOM uint32_t TMRA0LMT : 7; /*!< [6..0] Counter/Timer A0 Pattern Limit Count. */ + __IOM uint32_t TMRA0TRIG : 4; /*!< [10..7] Counter/Timer A0 Trigger Select. */ + __IOM uint32_t TMRA0NOSYNC : 1; /*!< [11..11] Source clock synchronization control. */ + __IOM uint32_t TMRA0TINV : 1; /*!< [12..12] Counter/Timer A0 Invert on trigger. */ + __IOM uint32_t TMRA0POL23 : 1; /*!< [13..13] Counter/Timer A0 Upper output polarity */ + __IOM uint32_t TMRA0EN23 : 1; /*!< [14..14] Counter/Timer A0 Upper compare enable. */ + __IM uint32_t : 1; + __IOM uint32_t TMRB0LMT : 6; /*!< [21..16] Counter/Timer B0 Pattern Limit Count. */ + __IM uint32_t : 1; + __IOM uint32_t TMRB0TRIG : 4; /*!< [26..23] Counter/Timer B0 Trigger Select. */ + __IOM uint32_t TMRB0NOSYNC : 1; /*!< [27..27] Source clock synchronization control. */ + __IOM uint32_t TMRB0TINV : 1; /*!< [28..28] Counter/Timer B0 Invert on trigger. */ + __IOM uint32_t TMRB0POL23 : 1; /*!< [29..29] Upper output polarity */ + __IOM uint32_t TMRB0EN23 : 1; /*!< [30..30] Counter/Timer B0 Upper compare enable. */ + } AUX0_b; + } ; + + union { + __IOM uint32_t TMR1; /*!< (@ 0x00000020) Counter/Timer Register */ + + struct { + __IOM uint32_t CTTMRA1 : 16; /*!< [15..0] Counter/Timer A1. */ + __IOM uint32_t CTTMRB1 : 16; /*!< [31..16] Counter/Timer B1. */ + } TMR1_b; + } ; + + union { + __IOM uint32_t CMPRA1; /*!< (@ 0x00000024) Counter/Timer A1 Compare Registers */ + + struct { + __IOM uint32_t CMPR0A1 : 16; /*!< [15..0] Counter/Timer A1 Compare Register 0. */ + __IOM uint32_t CMPR1A1 : 16; /*!< [31..16] Counter/Timer A1 Compare Register 1. */ + } CMPRA1_b; + } ; + + union { + __IOM uint32_t CMPRB1; /*!< (@ 0x00000028) Counter/Timer B1 Compare Registers */ + + struct { + __IOM uint32_t CMPR0B1 : 16; /*!< [15..0] Counter/Timer B1 Compare Register 0. */ + __IOM uint32_t CMPR1B1 : 16; /*!< [31..16] Counter/Timer B1 Compare Register 1. */ + } CMPRB1_b; + } ; + + union { + __IOM uint32_t CTRL1; /*!< (@ 0x0000002C) Counter/Timer Control */ + + struct { + __IOM uint32_t TMRA1EN : 1; /*!< [0..0] Counter/Timer A1 Enable bit. */ + __IOM uint32_t TMRA1CLK : 5; /*!< [5..1] Counter/Timer A1 Clock Select. */ + __IOM uint32_t TMRA1FN : 3; /*!< [8..6] Counter/Timer A1 Function Select. */ + __IOM uint32_t TMRA1IE0 : 1; /*!< [9..9] Counter/Timer A1 Interrupt Enable bit based on COMPR0. */ + __IOM uint32_t TMRA1IE1 : 1; /*!< [10..10] Counter/Timer A1 Interrupt Enable bit based on COMPR1. */ + __IOM uint32_t TMRA1CLR : 1; /*!< [11..11] Counter/Timer A1 Clear bit. */ + __IOM uint32_t TMRA1POL : 1; /*!< [12..12] Counter/Timer A1 output polarity. */ + __IM uint32_t : 3; + __IOM uint32_t TMRB1EN : 1; /*!< [16..16] Counter/Timer B1 Enable bit. */ + __IOM uint32_t TMRB1CLK : 5; /*!< [21..17] Counter/Timer B1 Clock Select. */ + __IOM uint32_t TMRB1FN : 3; /*!< [24..22] Counter/Timer B1 Function Select. */ + __IOM uint32_t TMRB1IE0 : 1; /*!< [25..25] Counter/Timer B1 Interrupt Enable bit for COMPR0. */ + __IOM uint32_t TMRB1IE1 : 1; /*!< [26..26] Counter/Timer B1 Interrupt Enable bit for COMPR1. */ + __IOM uint32_t TMRB1CLR : 1; /*!< [27..27] Counter/Timer B1 Clear bit. */ + __IOM uint32_t TMRB1POL : 1; /*!< [28..28] Counter/Timer B1 output polarity. */ + __IM uint32_t : 2; + __IOM uint32_t CTLINK1 : 1; /*!< [31..31] Counter/Timer A1/B1 Link bit. */ + } CTRL1_b; + } ; + __IM uint32_t RESERVED1; + + union { + __IOM uint32_t CMPRAUXA1; /*!< (@ 0x00000034) Counter/Timer A1 Compare Registers */ + + struct { + __IOM uint32_t CMPR2A1 : 16; /*!< [15..0] Counter/Timer A1 Compare Register 2. Holds the lower + limit for timer half A. */ + __IOM uint32_t CMPR3A1 : 16; /*!< [31..16] Counter/Timer A1 Compare Register 3. Holds the upper + limit for timer half A. */ + } CMPRAUXA1_b; + } ; + + union { + __IOM uint32_t CMPRAUXB1; /*!< (@ 0x00000038) Counter/Timer B1 Compare Registers */ + + struct { + __IOM uint32_t CMPR2B1 : 16; /*!< [15..0] Counter/Timer B1 Compare Register 2. Holds the lower + limit for timer half B. */ + __IOM uint32_t CMPR3B1 : 16; /*!< [31..16] Counter/Timer B1 Compare Register 3. Holds the upper + limit for timer half B. */ + } CMPRAUXB1_b; + } ; + + union { + __IOM uint32_t AUX1; /*!< (@ 0x0000003C) Counter/Timer Auxiliary */ + + struct { + __IOM uint32_t TMRA1LMT : 7; /*!< [6..0] Counter/Timer A1 Pattern Limit Count. */ + __IOM uint32_t TMRA1TRIG : 4; /*!< [10..7] Counter/Timer A1 Trigger Select. */ + __IOM uint32_t TMRA1NOSYNC : 1; /*!< [11..11] Source clock synchronization control. */ + __IOM uint32_t TMRA1TINV : 1; /*!< [12..12] Counter/Timer A1 Invert on trigger. */ + __IOM uint32_t TMRA1POL23 : 1; /*!< [13..13] Counter/Timer A1 Upper output polarity */ + __IOM uint32_t TMRA1EN23 : 1; /*!< [14..14] Counter/Timer A1 Upper compare enable. */ + __IM uint32_t : 1; + __IOM uint32_t TMRB1LMT : 6; /*!< [21..16] Counter/Timer B1 Pattern Limit Count. */ + __IM uint32_t : 1; + __IOM uint32_t TMRB1TRIG : 4; /*!< [26..23] Counter/Timer B1 Trigger Select. */ + __IOM uint32_t TMRB1NOSYNC : 1; /*!< [27..27] Source clock synchronization control. */ + __IOM uint32_t TMRB1TINV : 1; /*!< [28..28] Counter/Timer B1 Invert on trigger. */ + __IOM uint32_t TMRB1POL23 : 1; /*!< [29..29] Upper output polarity */ + __IOM uint32_t TMRB1EN23 : 1; /*!< [30..30] Counter/Timer B1 Upper compare enable. */ + } AUX1_b; + } ; + + union { + __IOM uint32_t TMR2; /*!< (@ 0x00000040) Counter/Timer Register */ + + struct { + __IOM uint32_t CTTMRA2 : 16; /*!< [15..0] Counter/Timer A2. */ + __IOM uint32_t CTTMRB2 : 16; /*!< [31..16] Counter/Timer B2. */ + } TMR2_b; + } ; + + union { + __IOM uint32_t CMPRA2; /*!< (@ 0x00000044) Counter/Timer A2 Compare Registers */ + + struct { + __IOM uint32_t CMPR0A2 : 16; /*!< [15..0] Counter/Timer A2 Compare Register 0. */ + __IOM uint32_t CMPR1A2 : 16; /*!< [31..16] Counter/Timer A2 Compare Register 1. */ + } CMPRA2_b; + } ; + + union { + __IOM uint32_t CMPRB2; /*!< (@ 0x00000048) Counter/Timer B2 Compare Registers */ + + struct { + __IOM uint32_t CMPR0B2 : 16; /*!< [15..0] Counter/Timer B2 Compare Register 0. */ + __IOM uint32_t CMPR1B2 : 16; /*!< [31..16] Counter/Timer B2 Compare Register 1. */ + } CMPRB2_b; + } ; + + union { + __IOM uint32_t CTRL2; /*!< (@ 0x0000004C) Counter/Timer Control */ + + struct { + __IOM uint32_t TMRA2EN : 1; /*!< [0..0] Counter/Timer A2 Enable bit. */ + __IOM uint32_t TMRA2CLK : 5; /*!< [5..1] Counter/Timer A2 Clock Select. */ + __IOM uint32_t TMRA2FN : 3; /*!< [8..6] Counter/Timer A2 Function Select. */ + __IOM uint32_t TMRA2IE0 : 1; /*!< [9..9] Counter/Timer A2 Interrupt Enable bit based on COMPR0. */ + __IOM uint32_t TMRA2IE1 : 1; /*!< [10..10] Counter/Timer A2 Interrupt Enable bit based on COMPR1. */ + __IOM uint32_t TMRA2CLR : 1; /*!< [11..11] Counter/Timer A2 Clear bit. */ + __IOM uint32_t TMRA2POL : 1; /*!< [12..12] Counter/Timer A2 output polarity. */ + __IM uint32_t : 3; + __IOM uint32_t TMRB2EN : 1; /*!< [16..16] Counter/Timer B2 Enable bit. */ + __IOM uint32_t TMRB2CLK : 5; /*!< [21..17] Counter/Timer B2 Clock Select. */ + __IOM uint32_t TMRB2FN : 3; /*!< [24..22] Counter/Timer B2 Function Select. */ + __IOM uint32_t TMRB2IE0 : 1; /*!< [25..25] Counter/Timer B2 Interrupt Enable bit for COMPR0. */ + __IOM uint32_t TMRB2IE1 : 1; /*!< [26..26] Counter/Timer B2 Interrupt Enable bit for COMPR1. */ + __IOM uint32_t TMRB2CLR : 1; /*!< [27..27] Counter/Timer B2 Clear bit. */ + __IOM uint32_t TMRB2POL : 1; /*!< [28..28] Counter/Timer B2 output polarity. */ + __IM uint32_t : 2; + __IOM uint32_t CTLINK2 : 1; /*!< [31..31] Counter/Timer A2/B2 Link bit. */ + } CTRL2_b; + } ; + __IM uint32_t RESERVED2; + + union { + __IOM uint32_t CMPRAUXA2; /*!< (@ 0x00000054) Counter/Timer A2 Compare Registers */ + + struct { + __IOM uint32_t CMPR2A2 : 16; /*!< [15..0] Counter/Timer A2 Compare Register 2. Holds the lower + limit for timer half A. */ + __IOM uint32_t CMPR3A2 : 16; /*!< [31..16] Counter/Timer A2 Compare Register 3. Holds the upper + limit for timer half A. */ + } CMPRAUXA2_b; + } ; + + union { + __IOM uint32_t CMPRAUXB2; /*!< (@ 0x00000058) Counter/Timer B2 Compare Registers */ + + struct { + __IOM uint32_t CMPR2B2 : 16; /*!< [15..0] Counter/Timer B2 Compare Register 2. Holds the lower + limit for timer half B. */ + __IOM uint32_t CMPR3B2 : 16; /*!< [31..16] Counter/Timer B2 Compare Register 3. Holds the upper + limit for timer half B. */ + } CMPRAUXB2_b; + } ; + + union { + __IOM uint32_t AUX2; /*!< (@ 0x0000005C) Counter/Timer Auxiliary */ + + struct { + __IOM uint32_t TMRA2LMT : 7; /*!< [6..0] Counter/Timer A2 Pattern Limit Count. */ + __IOM uint32_t TMRA2TRIG : 4; /*!< [10..7] Counter/Timer A2 Trigger Select. */ + __IOM uint32_t TMRA2NOSYNC : 1; /*!< [11..11] Source clock synchronization control. */ + __IOM uint32_t TMRA2TINV : 1; /*!< [12..12] Counter/Timer A2 Invert on trigger. */ + __IOM uint32_t TMRA2POL23 : 1; /*!< [13..13] Counter/Timer A2 Upper output polarity */ + __IOM uint32_t TMRA2EN23 : 1; /*!< [14..14] Counter/Timer A2 Upper compare enable. */ + __IM uint32_t : 1; + __IOM uint32_t TMRB2LMT : 6; /*!< [21..16] Counter/Timer B2 Pattern Limit Count. */ + __IM uint32_t : 1; + __IOM uint32_t TMRB2TRIG : 4; /*!< [26..23] Counter/Timer B2 Trigger Select. */ + __IOM uint32_t TMRB2NOSYNC : 1; /*!< [27..27] Source clock synchronization control. */ + __IOM uint32_t TMRB2TINV : 1; /*!< [28..28] Counter/Timer B2 Invert on trigger. */ + __IOM uint32_t TMRB2POL23 : 1; /*!< [29..29] Upper output polarity */ + __IOM uint32_t TMRB2EN23 : 1; /*!< [30..30] Counter/Timer B2 Upper compare enable. */ + } AUX2_b; + } ; + + union { + __IOM uint32_t TMR3; /*!< (@ 0x00000060) Counter/Timer Register */ + + struct { + __IOM uint32_t CTTMRA3 : 16; /*!< [15..0] Counter/Timer A3. */ + __IOM uint32_t CTTMRB3 : 16; /*!< [31..16] Counter/Timer B3. */ + } TMR3_b; + } ; + + union { + __IOM uint32_t CMPRA3; /*!< (@ 0x00000064) Counter/Timer A3 Compare Registers */ + + struct { + __IOM uint32_t CMPR0A3 : 16; /*!< [15..0] Counter/Timer A3 Compare Register 0. */ + __IOM uint32_t CMPR1A3 : 16; /*!< [31..16] Counter/Timer A3 Compare Register 1. */ + } CMPRA3_b; + } ; + + union { + __IOM uint32_t CMPRB3; /*!< (@ 0x00000068) Counter/Timer B3 Compare Registers */ + + struct { + __IOM uint32_t CMPR0B3 : 16; /*!< [15..0] Counter/Timer B3 Compare Register 0. */ + __IOM uint32_t CMPR1B3 : 16; /*!< [31..16] Counter/Timer B3 Compare Register 1. */ + } CMPRB3_b; + } ; + + union { + __IOM uint32_t CTRL3; /*!< (@ 0x0000006C) Counter/Timer Control */ + + struct { + __IOM uint32_t TMRA3EN : 1; /*!< [0..0] Counter/Timer A3 Enable bit. */ + __IOM uint32_t TMRA3CLK : 5; /*!< [5..1] Counter/Timer A3 Clock Select. */ + __IOM uint32_t TMRA3FN : 3; /*!< [8..6] Counter/Timer A3 Function Select. */ + __IOM uint32_t TMRA3IE0 : 1; /*!< [9..9] Counter/Timer A3 Interrupt Enable bit based on COMPR0. */ + __IOM uint32_t TMRA3IE1 : 1; /*!< [10..10] Counter/Timer A3 Interrupt Enable bit based on COMPR1. */ + __IOM uint32_t TMRA3CLR : 1; /*!< [11..11] Counter/Timer A3 Clear bit. */ + __IOM uint32_t TMRA3POL : 1; /*!< [12..12] Counter/Timer A3 output polarity. */ + __IM uint32_t : 2; + __IOM uint32_t ADCEN : 1; /*!< [15..15] Special Timer A3 enable for ADC function. */ + __IOM uint32_t TMRB3EN : 1; /*!< [16..16] Counter/Timer B3 Enable bit. */ + __IOM uint32_t TMRB3CLK : 5; /*!< [21..17] Counter/Timer B3 Clock Select. */ + __IOM uint32_t TMRB3FN : 3; /*!< [24..22] Counter/Timer B3 Function Select. */ + __IOM uint32_t TMRB3IE0 : 1; /*!< [25..25] Counter/Timer B3 Interrupt Enable bit for COMPR0. */ + __IOM uint32_t TMRB3IE1 : 1; /*!< [26..26] Counter/Timer B3 Interrupt Enable bit for COMPR1. */ + __IOM uint32_t TMRB3CLR : 1; /*!< [27..27] Counter/Timer B3 Clear bit. */ + __IOM uint32_t TMRB3POL : 1; /*!< [28..28] Counter/Timer B3 output polarity. */ + __IM uint32_t : 2; + __IOM uint32_t CTLINK3 : 1; /*!< [31..31] Counter/Timer A3/B3 Link bit. */ + } CTRL3_b; + } ; + __IM uint32_t RESERVED3; + + union { + __IOM uint32_t CMPRAUXA3; /*!< (@ 0x00000074) Counter/Timer A3 Compare Registers */ + + struct { + __IOM uint32_t CMPR2A3 : 16; /*!< [15..0] Counter/Timer A3 Compare Register 2. Holds the lower + limit for timer half A. */ + __IOM uint32_t CMPR3A3 : 16; /*!< [31..16] Counter/Timer A3 Compare Register 3. Holds the upper + limit for timer half A. */ + } CMPRAUXA3_b; + } ; + + union { + __IOM uint32_t CMPRAUXB3; /*!< (@ 0x00000078) Counter/Timer B3 Compare Registers */ + + struct { + __IOM uint32_t CMPR2B3 : 16; /*!< [15..0] Counter/Timer B3 Compare Register 2. Holds the lower + limit for timer half B. */ + __IOM uint32_t CMPR3B3 : 16; /*!< [31..16] Counter/Timer B3 Compare Register 3. Holds the upper + limit for timer half B. */ + } CMPRAUXB3_b; + } ; + + union { + __IOM uint32_t AUX3; /*!< (@ 0x0000007C) Counter/Timer Auxiliary */ + + struct { + __IOM uint32_t TMRA3LMT : 7; /*!< [6..0] Counter/Timer A3 Pattern Limit Count. */ + __IOM uint32_t TMRA3TRIG : 4; /*!< [10..7] Counter/Timer A3 Trigger Select. */ + __IOM uint32_t TMRA3NOSYNC : 1; /*!< [11..11] Source clock synchronization control. */ + __IOM uint32_t TMRA3TINV : 1; /*!< [12..12] Counter/Timer A3 Invert on trigger. */ + __IOM uint32_t TMRA3POL23 : 1; /*!< [13..13] Counter/Timer A3 Upper output polarity */ + __IOM uint32_t TMRA3EN23 : 1; /*!< [14..14] Counter/Timer A3 Upper compare enable. */ + __IM uint32_t : 1; + __IOM uint32_t TMRB3LMT : 6; /*!< [21..16] Counter/Timer B3 Pattern Limit Count. */ + __IM uint32_t : 1; + __IOM uint32_t TMRB3TRIG : 4; /*!< [26..23] Counter/Timer B3 Trigger Select. */ + __IOM uint32_t TMRB3NOSYNC : 1; /*!< [27..27] Source clock synchronization control. */ + __IOM uint32_t TMRB3TINV : 1; /*!< [28..28] Counter/Timer B3 Invert on trigger. */ + __IOM uint32_t TMRB3POL23 : 1; /*!< [29..29] Upper output polarity */ + __IOM uint32_t TMRB3EN23 : 1; /*!< [30..30] Counter/Timer B3 Upper compare enable. */ + } AUX3_b; + } ; + + union { + __IOM uint32_t TMR4; /*!< (@ 0x00000080) Counter/Timer Register */ + + struct { + __IOM uint32_t CTTMRA4 : 16; /*!< [15..0] Counter/Timer A4. */ + __IOM uint32_t CTTMRB4 : 16; /*!< [31..16] Counter/Timer B4. */ + } TMR4_b; + } ; + + union { + __IOM uint32_t CMPRA4; /*!< (@ 0x00000084) Counter/Timer A4 Compare Registers */ + + struct { + __IOM uint32_t CMPR0A4 : 16; /*!< [15..0] Counter/Timer A4 Compare Register 0. Holds the lower + limit for timer half A. */ + __IOM uint32_t CMPR1A4 : 16; /*!< [31..16] Counter/Timer A4 Compare Register 1. Holds the upper + limit for timer half A. */ + } CMPRA4_b; + } ; + + union { + __IOM uint32_t CMPRB4; /*!< (@ 0x00000088) Counter/Timer B4 Compare Registers */ + + struct { + __IOM uint32_t CMPR0B4 : 16; /*!< [15..0] Counter/Timer B4 Compare Register 0. Holds the lower + limit for timer half B. */ + __IOM uint32_t CMPR1B4 : 16; /*!< [31..16] Counter/Timer B4 Compare Register 1. Holds the upper + limit for timer half B. */ + } CMPRB4_b; + } ; + + union { + __IOM uint32_t CTRL4; /*!< (@ 0x0000008C) Counter/Timer Control */ + + struct { + __IOM uint32_t TMRA4EN : 1; /*!< [0..0] Counter/Timer A4 Enable bit. */ + __IOM uint32_t TMRA4CLK : 5; /*!< [5..1] Counter/Timer A4 Clock Select. */ + __IOM uint32_t TMRA4FN : 3; /*!< [8..6] Counter/Timer A4 Function Select. */ + __IOM uint32_t TMRA4IE0 : 1; /*!< [9..9] Counter/Timer A4 Interrupt Enable bit based on COMPR0. */ + __IOM uint32_t TMRA4IE1 : 1; /*!< [10..10] Counter/Timer A4 Interrupt Enable bit based on COMPR1. */ + __IOM uint32_t TMRA4CLR : 1; /*!< [11..11] Counter/Timer A4 Clear bit. */ + __IOM uint32_t TMRA4POL : 1; /*!< [12..12] Counter/Timer A4 output polarity. */ + __IM uint32_t : 3; + __IOM uint32_t TMRB4EN : 1; /*!< [16..16] Counter/Timer B4 Enable bit. */ + __IOM uint32_t TMRB4CLK : 5; /*!< [21..17] Counter/Timer B4 Clock Select. */ + __IOM uint32_t TMRB4FN : 3; /*!< [24..22] Counter/Timer B4 Function Select. */ + __IOM uint32_t TMRB4IE0 : 1; /*!< [25..25] Counter/Timer B4 Interrupt Enable bit for COMPR0. */ + __IOM uint32_t TMRB4IE1 : 1; /*!< [26..26] Counter/Timer B4 Interrupt Enable bit for COMPR1. */ + __IOM uint32_t TMRB4CLR : 1; /*!< [27..27] Counter/Timer B4 Clear bit. */ + __IOM uint32_t TMRB4POL : 1; /*!< [28..28] Counter/Timer B4 output polarity. */ + __IM uint32_t : 2; + __IOM uint32_t CTLINK4 : 1; /*!< [31..31] Counter/Timer A4/B4 Link bit. */ + } CTRL4_b; + } ; + __IM uint32_t RESERVED4; + + union { + __IOM uint32_t CMPRAUXA4; /*!< (@ 0x00000094) Counter/Timer A4 Compare Registers */ + + struct { + __IOM uint32_t CMPR2A4 : 16; /*!< [15..0] Counter/Timer A4 Compare Register 2. Holds the lower + limit for timer half A. */ + __IOM uint32_t CMPR3A4 : 16; /*!< [31..16] Counter/Timer A4 Compare Register 3. Holds the upper + limit for timer half A. */ + } CMPRAUXA4_b; + } ; + + union { + __IOM uint32_t CMPRAUXB4; /*!< (@ 0x00000098) Counter/Timer B4 Compare Registers */ + + struct { + __IOM uint32_t CMPR2B4 : 16; /*!< [15..0] Counter/Timer B4 Compare Register 2. Holds the lower + limit for timer half B. */ + __IOM uint32_t CMPR3B4 : 16; /*!< [31..16] Counter/Timer B4 Compare Register 3. Holds the upper + limit for timer half B. */ + } CMPRAUXB4_b; + } ; + + union { + __IOM uint32_t AUX4; /*!< (@ 0x0000009C) Counter/Timer Auxiliary */ + + struct { + __IOM uint32_t TMRA4LMT : 7; /*!< [6..0] Counter/Timer A4 Pattern Limit Count. */ + __IOM uint32_t TMRA4TRIG : 4; /*!< [10..7] Counter/Timer A4 Trigger Select. */ + __IOM uint32_t TMRA4NOSYNC : 1; /*!< [11..11] Source clock synchronization control. */ + __IOM uint32_t TMRA4TINV : 1; /*!< [12..12] Counter/Timer A4 Invert on trigger. */ + __IOM uint32_t TMRA4POL23 : 1; /*!< [13..13] Counter/Timer A4 Upper output polarity */ + __IOM uint32_t TMRA4EN23 : 1; /*!< [14..14] Counter/Timer A4 Upper compare enable. */ + __IM uint32_t : 1; + __IOM uint32_t TMRB4LMT : 6; /*!< [21..16] Counter/Timer B4 Pattern Limit Count. */ + __IM uint32_t : 1; + __IOM uint32_t TMRB4TRIG : 4; /*!< [26..23] Counter/Timer B4 Trigger Select. */ + __IOM uint32_t TMRB4NOSYNC : 1; /*!< [27..27] Source clock synchronization control. */ + __IOM uint32_t TMRB4TINV : 1; /*!< [28..28] Counter/Timer B4 Invert on trigger. */ + __IOM uint32_t TMRB4POL23 : 1; /*!< [29..29] Upper output polarity */ + __IOM uint32_t TMRB4EN23 : 1; /*!< [30..30] Counter/Timer B4 Upper compare enable. */ + } AUX4_b; + } ; + + union { + __IOM uint32_t TMR5; /*!< (@ 0x000000A0) Counter/Timer Register */ + + struct { + __IOM uint32_t CTTMRA5 : 16; /*!< [15..0] Counter/Timer A5. */ + __IOM uint32_t CTTMRB5 : 16; /*!< [31..16] Counter/Timer B5. */ + } TMR5_b; + } ; + + union { + __IOM uint32_t CMPRA5; /*!< (@ 0x000000A4) Counter/Timer A5 Compare Registers */ + + struct { + __IOM uint32_t CMPR0A5 : 16; /*!< [15..0] Counter/Timer A5 Compare Register 0. */ + __IOM uint32_t CMPR1A5 : 16; /*!< [31..16] Counter/Timer A5 Compare Register 1. */ + } CMPRA5_b; + } ; + + union { + __IOM uint32_t CMPRB5; /*!< (@ 0x000000A8) Counter/Timer B5 Compare Registers */ + + struct { + __IOM uint32_t CMPR0B5 : 16; /*!< [15..0] Counter/Timer B5 Compare Register 0. */ + __IOM uint32_t CMPR1B5 : 16; /*!< [31..16] Counter/Timer B5 Compare Register 1. */ + } CMPRB5_b; + } ; + + union { + __IOM uint32_t CTRL5; /*!< (@ 0x000000AC) Counter/Timer Control */ + + struct { + __IOM uint32_t TMRA5EN : 1; /*!< [0..0] Counter/Timer A5 Enable bit. */ + __IOM uint32_t TMRA5CLK : 5; /*!< [5..1] Counter/Timer A5 Clock Select. */ + __IOM uint32_t TMRA5FN : 3; /*!< [8..6] Counter/Timer A5 Function Select. */ + __IOM uint32_t TMRA5IE0 : 1; /*!< [9..9] Counter/Timer A5 Interrupt Enable bit based on COMPR0. */ + __IOM uint32_t TMRA5IE1 : 1; /*!< [10..10] Counter/Timer A5 Interrupt Enable bit based on COMPR1. */ + __IOM uint32_t TMRA5CLR : 1; /*!< [11..11] Counter/Timer A5 Clear bit. */ + __IOM uint32_t TMRA5POL : 1; /*!< [12..12] Counter/Timer A5 output polarity. */ + __IM uint32_t : 3; + __IOM uint32_t TMRB5EN : 1; /*!< [16..16] Counter/Timer B5 Enable bit. */ + __IOM uint32_t TMRB5CLK : 5; /*!< [21..17] Counter/Timer B5 Clock Select. */ + __IOM uint32_t TMRB5FN : 3; /*!< [24..22] Counter/Timer B5 Function Select. */ + __IOM uint32_t TMRB5IE0 : 1; /*!< [25..25] Counter/Timer B5 Interrupt Enable bit for COMPR0. */ + __IOM uint32_t TMRB5IE1 : 1; /*!< [26..26] Counter/Timer B5 Interrupt Enable bit for COMPR1. */ + __IOM uint32_t TMRB5CLR : 1; /*!< [27..27] Counter/Timer B5 Clear bit. */ + __IOM uint32_t TMRB5POL : 1; /*!< [28..28] Counter/Timer B5 output polarity. */ + __IM uint32_t : 2; + __IOM uint32_t CTLINK5 : 1; /*!< [31..31] Counter/Timer A5/B5 Link bit. */ + } CTRL5_b; + } ; + __IM uint32_t RESERVED5; + + union { + __IOM uint32_t CMPRAUXA5; /*!< (@ 0x000000B4) Counter/Timer A5 Compare Registers */ + + struct { + __IOM uint32_t CMPR2A5 : 16; /*!< [15..0] Counter/Timer A5 Compare Register 2. Holds the lower + limit for timer half A. */ + __IOM uint32_t CMPR3A5 : 16; /*!< [31..16] Counter/Timer A5 Compare Register 3. Holds the upper + limit for timer half A. */ + } CMPRAUXA5_b; + } ; + + union { + __IOM uint32_t CMPRAUXB5; /*!< (@ 0x000000B8) Counter/Timer B5 Compare Registers */ + + struct { + __IOM uint32_t CMPR2B5 : 16; /*!< [15..0] Counter/Timer B5 Compare Register 2. Holds the lower + limit for timer half B. */ + __IOM uint32_t CMPR3B5 : 16; /*!< [31..16] Counter/Timer B5 Compare Register 3. Holds the upper + limit for timer half B. */ + } CMPRAUXB5_b; + } ; + + union { + __IOM uint32_t AUX5; /*!< (@ 0x000000BC) Counter/Timer Auxiliary */ + + struct { + __IOM uint32_t TMRA5LMT : 7; /*!< [6..0] Counter/Timer A5 Pattern Limit Count. */ + __IOM uint32_t TMRA5TRIG : 4; /*!< [10..7] Counter/Timer A5 Trigger Select. */ + __IOM uint32_t TMRA5NOSYNC : 1; /*!< [11..11] Source clock synchronization control. */ + __IOM uint32_t TMRA5TINV : 1; /*!< [12..12] Counter/Timer A5 Invert on trigger. */ + __IOM uint32_t TMRA5POL23 : 1; /*!< [13..13] Counter/Timer A5 Upper output polarity */ + __IOM uint32_t TMRA5EN23 : 1; /*!< [14..14] Counter/Timer A5 Upper compare enable. */ + __IM uint32_t : 1; + __IOM uint32_t TMRB5LMT : 6; /*!< [21..16] Counter/Timer B5 Pattern Limit Count. */ + __IM uint32_t : 1; + __IOM uint32_t TMRB5TRIG : 4; /*!< [26..23] Counter/Timer B5 Trigger Select. */ + __IOM uint32_t TMRB5NOSYNC : 1; /*!< [27..27] Source clock synchronization control. */ + __IOM uint32_t TMRB5TINV : 1; /*!< [28..28] Counter/Timer B5 Invert on trigger. */ + __IOM uint32_t TMRB5POL23 : 1; /*!< [29..29] Upper output polarity */ + __IOM uint32_t TMRB5EN23 : 1; /*!< [30..30] Counter/Timer B5 Upper compare enable. */ + } AUX5_b; + } ; + + union { + __IOM uint32_t TMR6; /*!< (@ 0x000000C0) Counter/Timer Register */ + + struct { + __IOM uint32_t CTTMRA6 : 16; /*!< [15..0] Counter/Timer A6. */ + __IOM uint32_t CTTMRB6 : 16; /*!< [31..16] Counter/Timer B6. */ + } TMR6_b; + } ; + + union { + __IOM uint32_t CMPRA6; /*!< (@ 0x000000C4) Counter/Timer A6 Compare Registers */ + + struct { + __IOM uint32_t CMPR0A6 : 16; /*!< [15..0] Counter/Timer A6 Compare Register 0. */ + __IOM uint32_t CMPR1A6 : 16; /*!< [31..16] Counter/Timer A6 Compare Register 1. */ + } CMPRA6_b; + } ; + + union { + __IOM uint32_t CMPRB6; /*!< (@ 0x000000C8) Counter/Timer B6 Compare Registers */ + + struct { + __IOM uint32_t CMPR0B6 : 16; /*!< [15..0] Counter/Timer B6 Compare Register 0. */ + __IOM uint32_t CMPR1B6 : 16; /*!< [31..16] Counter/Timer B6 Compare Register 1. */ + } CMPRB6_b; + } ; + + union { + __IOM uint32_t CTRL6; /*!< (@ 0x000000CC) Counter/Timer Control */ + + struct { + __IOM uint32_t TMRA6EN : 1; /*!< [0..0] Counter/Timer A6 Enable bit. */ + __IOM uint32_t TMRA6CLK : 5; /*!< [5..1] Counter/Timer A6 Clock Select. */ + __IOM uint32_t TMRA6FN : 3; /*!< [8..6] Counter/Timer A6 Function Select. */ + __IOM uint32_t TMRA6IE0 : 1; /*!< [9..9] Counter/Timer A6 Interrupt Enable bit based on COMPR0. */ + __IOM uint32_t TMRA6IE1 : 1; /*!< [10..10] Counter/Timer A6 Interrupt Enable bit based on COMPR1. */ + __IOM uint32_t TMRA6CLR : 1; /*!< [11..11] Counter/Timer A6 Clear bit. */ + __IOM uint32_t TMRA6POL : 1; /*!< [12..12] Counter/Timer A6 output polarity. */ + __IM uint32_t : 3; + __IOM uint32_t TMRB6EN : 1; /*!< [16..16] Counter/Timer B6 Enable bit. */ + __IOM uint32_t TMRB6CLK : 5; /*!< [21..17] Counter/Timer B6 Clock Select. */ + __IOM uint32_t TMRB6FN : 3; /*!< [24..22] Counter/Timer B6 Function Select. */ + __IOM uint32_t TMRB6IE0 : 1; /*!< [25..25] Counter/Timer B6 Interrupt Enable bit for COMPR0. */ + __IOM uint32_t TMRB6IE1 : 1; /*!< [26..26] Counter/Timer B6 Interrupt Enable bit for COMPR1. */ + __IOM uint32_t TMRB6CLR : 1; /*!< [27..27] Counter/Timer B6 Clear bit. */ + __IOM uint32_t TMRB6POL : 1; /*!< [28..28] Counter/Timer B6 output polarity. */ + __IM uint32_t : 2; + __IOM uint32_t CTLINK6 : 1; /*!< [31..31] Counter/Timer A6/B6 Link bit. */ + } CTRL6_b; + } ; + __IM uint32_t RESERVED6; + + union { + __IOM uint32_t CMPRAUXA6; /*!< (@ 0x000000D4) Counter/Timer A6 Compare Registers */ + + struct { + __IOM uint32_t CMPR2A6 : 16; /*!< [15..0] Counter/Timer A6 Compare Register 2. Holds the lower + limit for timer half A. */ + __IOM uint32_t CMPR3A6 : 16; /*!< [31..16] Counter/Timer A6 Compare Register 3. Holds the upper + limit for timer half A. */ + } CMPRAUXA6_b; + } ; + + union { + __IOM uint32_t CMPRAUXB6; /*!< (@ 0x000000D8) Counter/Timer B6 Compare Registers */ + + struct { + __IOM uint32_t CMPR2B6 : 16; /*!< [15..0] Counter/Timer B6 Compare Register 2. Holds the lower + limit for timer half B. */ + __IOM uint32_t CMPR3B6 : 16; /*!< [31..16] Counter/Timer B6 Compare Register 3. Holds the upper + limit for timer half B. */ + } CMPRAUXB6_b; + } ; + + union { + __IOM uint32_t AUX6; /*!< (@ 0x000000DC) Counter/Timer Auxiliary */ + + struct { + __IOM uint32_t TMRA6LMT : 7; /*!< [6..0] Counter/Timer A6 Pattern Limit Count. */ + __IOM uint32_t TMRA6TRIG : 4; /*!< [10..7] Counter/Timer A6 Trigger Select. */ + __IOM uint32_t TMRA6NOSYNC : 1; /*!< [11..11] Source clock synchronization control. */ + __IOM uint32_t TMRA6TINV : 1; /*!< [12..12] Counter/Timer A6 Invert on trigger. */ + __IOM uint32_t TMRA6POL23 : 1; /*!< [13..13] Counter/Timer A6 Upper output polarity */ + __IOM uint32_t TMRA6EN23 : 1; /*!< [14..14] Counter/Timer A6 Upper compare enable. */ + __IM uint32_t : 1; + __IOM uint32_t TMRB6LMT : 6; /*!< [21..16] Counter/Timer B6 Pattern Limit Count. */ + __IM uint32_t : 1; + __IOM uint32_t TMRB6TRIG : 4; /*!< [26..23] Counter/Timer B6 Trigger Select. */ + __IOM uint32_t TMRB6NOSYNC : 1; /*!< [27..27] Source clock synchronization control. */ + __IOM uint32_t TMRB6TINV : 1; /*!< [28..28] Counter/Timer B6 Invert on trigger. */ + __IOM uint32_t TMRB6POL23 : 1; /*!< [29..29] Upper output polarity */ + __IOM uint32_t TMRB6EN23 : 1; /*!< [30..30] Counter/Timer B6 Upper compare enable. */ + } AUX6_b; + } ; + + union { + __IOM uint32_t TMR7; /*!< (@ 0x000000E0) Counter/Timer Register */ + + struct { + __IOM uint32_t CTTMRA7 : 16; /*!< [15..0] Counter/Timer A7. */ + __IOM uint32_t CTTMRB7 : 16; /*!< [31..16] Counter/Timer B7. */ + } TMR7_b; + } ; + + union { + __IOM uint32_t CMPRA7; /*!< (@ 0x000000E4) Counter/Timer A7 Compare Registers */ + + struct { + __IOM uint32_t CMPR0A7 : 16; /*!< [15..0] Counter/Timer A7 Compare Register 0. */ + __IOM uint32_t CMPR1A7 : 16; /*!< [31..16] Counter/Timer A7 Compare Register 1. */ + } CMPRA7_b; + } ; + + union { + __IOM uint32_t CMPRB7; /*!< (@ 0x000000E8) Counter/Timer B7 Compare Registers */ + + struct { + __IOM uint32_t CMPR0B7 : 16; /*!< [15..0] Counter/Timer B3 Compare Register 0. */ + __IOM uint32_t CMPR1B7 : 16; /*!< [31..16] Counter/Timer B3 Compare Register 1. */ + } CMPRB7_b; + } ; + + union { + __IOM uint32_t CTRL7; /*!< (@ 0x000000EC) Counter/Timer Control */ + + struct { + __IOM uint32_t TMRA7EN : 1; /*!< [0..0] Counter/Timer A7 Enable bit. */ + __IOM uint32_t TMRA7CLK : 5; /*!< [5..1] Counter/Timer A7 Clock Select. */ + __IOM uint32_t TMRA7FN : 3; /*!< [8..6] Counter/Timer A7 Function Select. */ + __IOM uint32_t TMRA7IE0 : 1; /*!< [9..9] Counter/Timer A7 Interrupt Enable bit based on COMPR0. */ + __IOM uint32_t TMRA7IE1 : 1; /*!< [10..10] Counter/Timer A7 Interrupt Enable bit based on COMPR1. */ + __IOM uint32_t TMRA7CLR : 1; /*!< [11..11] Counter/Timer A7 Clear bit. */ + __IOM uint32_t TMRA7POL : 1; /*!< [12..12] Counter/Timer A7 output polarity. */ + __IM uint32_t : 3; + __IOM uint32_t TMRB7EN : 1; /*!< [16..16] Counter/Timer B7 Enable bit. */ + __IOM uint32_t TMRB7CLK : 5; /*!< [21..17] Counter/Timer B7 Clock Select. */ + __IOM uint32_t TMRB7FN : 3; /*!< [24..22] Counter/Timer B7 Function Select. */ + __IOM uint32_t TMRB7IE0 : 1; /*!< [25..25] Counter/Timer B7 Interrupt Enable bit for COMPR0. */ + __IOM uint32_t TMRB7IE1 : 1; /*!< [26..26] Counter/Timer B7 Interrupt Enable bit for COMPR1. */ + __IOM uint32_t TMRB7CLR : 1; /*!< [27..27] Counter/Timer B7 Clear bit. */ + __IOM uint32_t TMRB7POL : 1; /*!< [28..28] Counter/Timer B7 output polarity. */ + __IM uint32_t : 2; + __IOM uint32_t CTLINK7 : 1; /*!< [31..31] Counter/Timer A7/B7 Link bit. */ + } CTRL7_b; + } ; + __IM uint32_t RESERVED7; + + union { + __IOM uint32_t CMPRAUXA7; /*!< (@ 0x000000F4) Counter/Timer A7 Compare Registers */ + + struct { + __IOM uint32_t CMPR2A7 : 16; /*!< [15..0] Counter/Timer A7 Compare Register 2. Holds the lower + limit for timer half A. */ + __IOM uint32_t CMPR3A7 : 16; /*!< [31..16] Counter/Timer A7 Compare Register 3. Holds the upper + limit for timer half A. */ + } CMPRAUXA7_b; + } ; + + union { + __IOM uint32_t CMPRAUXB7; /*!< (@ 0x000000F8) Counter/Timer B7 Compare Registers */ + + struct { + __IOM uint32_t CMPR2B7 : 16; /*!< [15..0] Counter/Timer B7 Compare Register 2. Holds the lower + limit for timer half B. */ + __IOM uint32_t CMPR3B7 : 16; /*!< [31..16] Counter/Timer B7 Compare Register 3. Holds the upper + limit for timer half B. */ + } CMPRAUXB7_b; + } ; + + union { + __IOM uint32_t AUX7; /*!< (@ 0x000000FC) Counter/Timer Auxiliary */ + + struct { + __IOM uint32_t TMRA7LMT : 7; /*!< [6..0] Counter/Timer A7 Pattern Limit Count. */ + __IOM uint32_t TMRA7TRIG : 4; /*!< [10..7] Counter/Timer A7 Trigger Select. */ + __IOM uint32_t TMRA7NOSYNC : 1; /*!< [11..11] Source clock synchronization control. */ + __IOM uint32_t TMRA7TINV : 1; /*!< [12..12] Counter/Timer A7 Invert on trigger. */ + __IOM uint32_t TMRA7POL23 : 1; /*!< [13..13] Counter/Timer A7 Upper output polarity */ + __IOM uint32_t TMRA7EN23 : 1; /*!< [14..14] Counter/Timer A7 Upper compare enable. */ + __IM uint32_t : 1; + __IOM uint32_t TMRB7LMT : 6; /*!< [21..16] Counter/Timer B7 Pattern Limit Count. */ + __IM uint32_t : 1; + __IOM uint32_t TMRB7TRIG : 4; /*!< [26..23] Counter/Timer B7 Trigger Select. */ + __IOM uint32_t TMRB7NOSYNC : 1; /*!< [27..27] Source clock synchronization control. */ + __IOM uint32_t TMRB7TINV : 1; /*!< [28..28] Counter/Timer B7 Invert on trigger. */ + __IOM uint32_t TMRB7POL23 : 1; /*!< [29..29] Upper output polarity */ + __IOM uint32_t TMRB7EN23 : 1; /*!< [30..30] Counter/Timer B7 Upper compare enable. */ + } AUX7_b; + } ; + + union { + __IOM uint32_t GLOBEN; /*!< (@ 0x00000100) Counter/Timer Global Enable */ + + struct { + __IOM uint32_t ENA0 : 1; /*!< [0..0] Alternate enable for A0 */ + __IOM uint32_t ENB0 : 1; /*!< [1..1] Alternate enable for B0 */ + __IOM uint32_t ENA1 : 1; /*!< [2..2] Alternate enable for A1 */ + __IOM uint32_t ENB1 : 1; /*!< [3..3] Alternate enable for B1 */ + __IOM uint32_t ENA2 : 1; /*!< [4..4] Alternate enable for A2 */ + __IOM uint32_t ENB2 : 1; /*!< [5..5] Alternate enable for B2 */ + __IOM uint32_t ENA3 : 1; /*!< [6..6] Alternate enable for A3 */ + __IOM uint32_t ENB3 : 1; /*!< [7..7] Alternate enable for B3. */ + __IOM uint32_t ENA4 : 1; /*!< [8..8] Alternate enable for A4 */ + __IOM uint32_t ENB4 : 1; /*!< [9..9] Alternate enable for B4 */ + __IOM uint32_t ENA5 : 1; /*!< [10..10] Alternate enable for A5 */ + __IOM uint32_t ENB5 : 1; /*!< [11..11] Alternate enable for B5 */ + __IOM uint32_t ENA6 : 1; /*!< [12..12] Alternate enable for A6 */ + __IOM uint32_t ENB6 : 1; /*!< [13..13] Alternate enable for B6 */ + __IOM uint32_t ENA7 : 1; /*!< [14..14] Alternate enable for A7 */ + __IOM uint32_t ENB7 : 1; /*!< [15..15] Alternate enable for B7. */ + } GLOBEN_b; + } ; + + union { + __IOM uint32_t OUTCFG0; /*!< (@ 0x00000104) Counter/Timer Output Config 0 */ + + struct { + __IOM uint32_t CFG0 : 3; /*!< [2..0] Pad output 0 configuration */ + __IOM uint32_t CFG1 : 3; /*!< [5..3] Pad output 1 configuration */ + __IOM uint32_t CFG2 : 3; /*!< [8..6] Pad output 2 configuration */ + __IOM uint32_t CFG3 : 3; /*!< [11..9] Pad output 3 configuration */ + __IOM uint32_t CFG4 : 3; /*!< [14..12] Pad output 4 configuration */ + __IM uint32_t : 1; + __IOM uint32_t CFG5 : 3; /*!< [18..16] Pad output 5 configuration */ + __IOM uint32_t CFG6 : 3; /*!< [21..19] Pad output 6 configuration */ + __IOM uint32_t CFG7 : 3; /*!< [24..22] Pad output 7 configuration */ + __IOM uint32_t CFG8 : 3; /*!< [27..25] Pad output 8 configuration */ + __IOM uint32_t CFG9 : 3; /*!< [30..28] Pad output 9 configuration */ + } OUTCFG0_b; + } ; + + union { + __IOM uint32_t OUTCFG1; /*!< (@ 0x00000108) Counter/Timer Output Config 1 */ + + struct { + __IOM uint32_t CFG10 : 3; /*!< [2..0] Pad output 10 configuration */ + __IOM uint32_t CFG11 : 3; /*!< [5..3] Pad output 11 configuration */ + __IOM uint32_t CFG12 : 3; /*!< [8..6] Pad output 12 configuration */ + __IOM uint32_t CFG13 : 3; /*!< [11..9] Pad output 13 configuration */ + __IOM uint32_t CFG14 : 3; /*!< [14..12] Pad output 14 configuration */ + __IM uint32_t : 1; + __IOM uint32_t CFG15 : 3; /*!< [18..16] Pad output 15 configuration */ + __IOM uint32_t CFG16 : 3; /*!< [21..19] Pad output 16 configuration */ + __IOM uint32_t CFG17 : 3; /*!< [24..22] Pad output 17 configuration */ + __IOM uint32_t CFG18 : 3; /*!< [27..25] Pad output 18 configuration */ + __IOM uint32_t CFG19 : 3; /*!< [30..28] Pad output 19 configuration */ + } OUTCFG1_b; + } ; + + union { + __IOM uint32_t OUTCFG2; /*!< (@ 0x0000010C) Counter/Timer Output Config 2 */ + + struct { + __IOM uint32_t CFG20 : 3; /*!< [2..0] Pad output 20 configuration */ + __IOM uint32_t CFG21 : 3; /*!< [5..3] Pad output 21 configuration */ + __IOM uint32_t CFG22 : 3; /*!< [8..6] Pad output 22 configuration */ + __IOM uint32_t CFG23 : 3; /*!< [11..9] Pad output 23 configuration */ + __IOM uint32_t CFG24 : 3; /*!< [14..12] Pad output 24 configuration */ + __IM uint32_t : 1; + __IOM uint32_t CFG25 : 3; /*!< [18..16] Pad output 25 configuration */ + __IOM uint32_t CFG26 : 3; /*!< [21..19] Pad output 26 configuration */ + __IOM uint32_t CFG27 : 3; /*!< [24..22] Pad output 27 configuration */ + __IOM uint32_t CFG28 : 3; /*!< [27..25] Pad output 28 configuration */ + __IOM uint32_t CFG29 : 3; /*!< [30..28] Pad output 29 configuration */ + } OUTCFG2_b; + } ; + __IM uint32_t RESERVED8; + + union { + __IOM uint32_t OUTCFG3; /*!< (@ 0x00000114) Counter/Timer Output Config 3 */ + + struct { + __IOM uint32_t CFG30 : 3; /*!< [2..0] Pad output 30 configuration */ + __IOM uint32_t CFG31 : 3; /*!< [5..3] Pad output 31 configuration */ + } OUTCFG3_b; + } ; + + union { + __IOM uint32_t INCFG; /*!< (@ 0x00000118) Counter/Timer Input Config */ + + struct { + __IOM uint32_t CFGA0 : 1; /*!< [0..0] CTIMER A0 input configuration */ + __IOM uint32_t CFGB0 : 1; /*!< [1..1] CTIMER B0 input configuration */ + __IOM uint32_t CFGA1 : 1; /*!< [2..2] CTIMER A1 input configuration */ + __IOM uint32_t CFGB1 : 1; /*!< [3..3] CTIMER B1 input configuration */ + __IOM uint32_t CFGA2 : 1; /*!< [4..4] CTIMER A2 input configuration */ + __IOM uint32_t CFGB2 : 1; /*!< [5..5] CTIMER B2 input configuration */ + __IOM uint32_t CFGA3 : 1; /*!< [6..6] CTIMER A3 input configuration */ + __IOM uint32_t CFGB3 : 1; /*!< [7..7] CTIMER B3 input configuration */ + __IOM uint32_t CFGA4 : 1; /*!< [8..8] CTIMER A4 input configuration */ + __IOM uint32_t CFGB4 : 1; /*!< [9..9] CTIMER B4 input configuration */ + __IOM uint32_t CFGA5 : 1; /*!< [10..10] CTIMER A5 input configuration */ + __IOM uint32_t CFGB5 : 1; /*!< [11..11] CTIMER B5 input configuration */ + __IOM uint32_t CFGA6 : 1; /*!< [12..12] CTIMER A6 input configuration */ + __IOM uint32_t CFGB6 : 1; /*!< [13..13] CTIMER B6 input configuration */ + __IOM uint32_t CFGA7 : 1; /*!< [14..14] CTIMER A7 input configuration */ + __IOM uint32_t CFGB7 : 1; /*!< [15..15] CTIMER B7 input configuration */ + } INCFG_b; + } ; + __IM uint32_t RESERVED9[9]; + + union { + __IOM uint32_t STCFG; /*!< (@ 0x00000140) Configuration Register */ + + struct { + __IOM uint32_t CLKSEL : 4; /*!< [3..0] Selects an appropriate clock source and divider to use + for the System Timer clock. */ + __IM uint32_t : 4; + __IOM uint32_t COMPARE_A_EN : 1; /*!< [8..8] Selects whether compare is enabled for the corresponding + SCMPR register. If compare is enabled, the interrupt status + is set once the comparision is met. */ + __IOM uint32_t COMPARE_B_EN : 1; /*!< [9..9] Selects whether compare is enabled for the corresponding + SCMPR register. If compare is enabled, the interrupt status + is set once the comparision is met. */ + __IOM uint32_t COMPARE_C_EN : 1; /*!< [10..10] Selects whether compare is enabled for the corresponding + SCMPR register. If compare is enabled, the interrupt status + is set once the comparision is met. */ + __IOM uint32_t COMPARE_D_EN : 1; /*!< [11..11] Selects whether compare is enabled for the corresponding + SCMPR register. If compare is enabled, the interrupt status + is set once the comparision is met. */ + __IOM uint32_t COMPARE_E_EN : 1; /*!< [12..12] Selects whether compare is enabled for the corresponding + SCMPR register. If compare is enabled, the interrupt status + is set once the comparision is met. */ + __IOM uint32_t COMPARE_F_EN : 1; /*!< [13..13] Selects whether compare is enabled for the corresponding + SCMPR register. If compare is enabled, the interrupt status + is set once the comparision is met. */ + __IOM uint32_t COMPARE_G_EN : 1; /*!< [14..14] Selects whether compare is enabled for the corresponding + SCMPR register. If compare is enabled, the interrupt status + is set once the comparision is met. */ + __IOM uint32_t COMPARE_H_EN : 1; /*!< [15..15] Selects whether compare is enabled for the corresponding + SCMPR register. If compare is enabled, the interrupt status + is set once the comparision is met. */ + __IM uint32_t : 14; + __IOM uint32_t CLEAR : 1; /*!< [30..30] Set this bit to one to clear the System Timer register. + If this bit is set to '1', the system timer register will + stay cleared. It needs to be set to '0' for the system + timer to start running. */ + __IOM uint32_t FREEZE : 1; /*!< [31..31] Set this bit to one to freeze the clock input to the + COUNTER register. Once frozen, the value can be safely + written from the MCU. Unfreeze to resume. */ + } STCFG_b; + } ; + + union { + __IOM uint32_t STTMR; /*!< (@ 0x00000144) System Timer Count Register (Real Time Counter) */ + + struct { + __IOM uint32_t STTMR : 32; /*!< [31..0] Value of the 32-bit counter as it ticks over. */ + } STTMR_b; + } ; + + union { + __IOM uint32_t CAPTURECONTROL; /*!< (@ 0x00000148) Capture Control Register */ + + struct { + __IOM uint32_t CAPTURE0 : 1; /*!< [0..0] Selects whether capture is enabled for the specified + capture register. */ + __IOM uint32_t CAPTURE1 : 1; /*!< [1..1] Selects whether capture is enabled for the specified + capture register. */ + __IOM uint32_t CAPTURE2 : 1; /*!< [2..2] Selects whether capture is enabled for the specified + capture register. */ + __IOM uint32_t CAPTURE3 : 1; /*!< [3..3] Selects whether capture is enabled for the specified + capture register. */ + } CAPTURECONTROL_b; + } ; + __IM uint32_t RESERVED10; + + union { + __IOM uint32_t SCMPR0; /*!< (@ 0x00000150) Compare Register A */ + + struct { + __IOM uint32_t SCMPR0 : 32; /*!< [31..0] Compare this value to the value in the COUNTER register + according to the match criterion, as selected in the COMPARE_A_EN + bit in the REG_CTIMER_STCGF register. */ + } SCMPR0_b; + } ; + + union { + __IOM uint32_t SCMPR1; /*!< (@ 0x00000154) Compare Register B */ + + struct { + __IOM uint32_t SCMPR1 : 32; /*!< [31..0] Compare this value to the value in the COUNTER register + according to the match criterion, as selected in the COMPARE_B_EN + bit in the REG_CTIMER_STCGF register. */ + } SCMPR1_b; + } ; + + union { + __IOM uint32_t SCMPR2; /*!< (@ 0x00000158) Compare Register C */ + + struct { + __IOM uint32_t SCMPR2 : 32; /*!< [31..0] Compare this value to the value in the COUNTER register + according to the match criterion, as selected in the COMPARE_C_EN + bit in the REG_CTIMER_STCGF register. */ + } SCMPR2_b; + } ; + + union { + __IOM uint32_t SCMPR3; /*!< (@ 0x0000015C) Compare Register D */ + + struct { + __IOM uint32_t SCMPR3 : 32; /*!< [31..0] Compare this value to the value in the COUNTER register + according to the match criterion, as selected in the COMPARE_D_EN + bit in the REG_CTIMER_STCGF register. */ + } SCMPR3_b; + } ; + + union { + __IOM uint32_t SCMPR4; /*!< (@ 0x00000160) Compare Register E */ + + struct { + __IOM uint32_t SCMPR4 : 32; /*!< [31..0] Compare this value to the value in the COUNTER register + according to the match criterion, as selected in the COMPARE_E_EN + bit in the REG_CTIMER_STCGF register. */ + } SCMPR4_b; + } ; + + union { + __IOM uint32_t SCMPR5; /*!< (@ 0x00000164) Compare Register F */ + + struct { + __IOM uint32_t SCMPR5 : 32; /*!< [31..0] Compare this value to the value in the COUNTER register + according to the match criterion, as selected in the COMPARE_F_EN + bit in the REG_CTIMER_STCGF register. */ + } SCMPR5_b; + } ; + + union { + __IOM uint32_t SCMPR6; /*!< (@ 0x00000168) Compare Register G */ + + struct { + __IOM uint32_t SCMPR6 : 32; /*!< [31..0] Compare this value to the value in the COUNTER register + according to the match criterion, as selected in the COMPARE_G_EN + bit in the REG_CTIMER_STCGF register. */ + } SCMPR6_b; + } ; + + union { + __IOM uint32_t SCMPR7; /*!< (@ 0x0000016C) Compare Register H */ + + struct { + __IOM uint32_t SCMPR7 : 32; /*!< [31..0] Compare this value to the value in the COUNTER register + according to the match criterion, as selected in the COMPARE_H_EN + bit in the REG_CTIMER_STCGF register. */ + } SCMPR7_b; + } ; + __IM uint32_t RESERVED11[28]; + + union { + __IOM uint32_t SCAPT0; /*!< (@ 0x000001E0) Capture Register A */ + + struct { + __IOM uint32_t SCAPT0 : 32; /*!< [31..0] Whenever the event is detected, the value in the COUNTER + is copied into this register and the corresponding interrupt + status bit is set. */ + } SCAPT0_b; + } ; + + union { + __IOM uint32_t SCAPT1; /*!< (@ 0x000001E4) Capture Register B */ + + struct { + __IOM uint32_t SCAPT1 : 32; /*!< [31..0] Whenever the event is detected, the value in the COUNTER + is copied into this register and the corresponding interrupt + status bit is set. */ + } SCAPT1_b; + } ; + + union { + __IOM uint32_t SCAPT2; /*!< (@ 0x000001E8) Capture Register C */ + + struct { + __IOM uint32_t SCAPT2 : 32; /*!< [31..0] Whenever the event is detected, the value in the COUNTER + is copied into this register and the corresponding interrupt + status bit is set. */ + } SCAPT2_b; + } ; + + union { + __IOM uint32_t SCAPT3; /*!< (@ 0x000001EC) Capture Register D */ + + struct { + __IOM uint32_t SCAPT3 : 32; /*!< [31..0] Whenever the event is detected, the value in the COUNTER + is copied into this register and the corresponding interrupt + status bit is set. */ + } SCAPT3_b; + } ; + + union { + __IOM uint32_t SNVR0; /*!< (@ 0x000001F0) System Timer NVRAM_A Register */ + + struct { + __IOM uint32_t SNVR0 : 32; /*!< [31..0] Value of the 32-bit counter as it ticks over. */ + } SNVR0_b; + } ; + + union { + __IOM uint32_t SNVR1; /*!< (@ 0x000001F4) System Timer NVRAM_B Register */ + + struct { + __IOM uint32_t SNVR1 : 32; /*!< [31..0] Value of the 32-bit counter as it ticks over. */ + } SNVR1_b; + } ; + + union { + __IOM uint32_t SNVR2; /*!< (@ 0x000001F8) System Timer NVRAM_C Register */ + + struct { + __IOM uint32_t SNVR2 : 32; /*!< [31..0] Value of the 32-bit counter as it ticks over. */ + } SNVR2_b; + } ; + + union { + __IOM uint32_t SNVR3; /*!< (@ 0x000001FC) System Timer NVRAM_D Register */ + + struct { + __IOM uint32_t SNVR3 : 32; /*!< [31..0] Value of the 32-bit counter as it ticks over. */ + } SNVR3_b; + } ; + + union { + __IOM uint32_t INTEN; /*!< (@ 0x00000200) Counter/Timer Interrupts: Enable */ + + struct { + __IOM uint32_t CTMRA0C0INT : 1; /*!< [0..0] Counter/Timer A0 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB0C0INT : 1; /*!< [1..1] Counter/Timer B0 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA1C0INT : 1; /*!< [2..2] Counter/Timer A1 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB1C0INT : 1; /*!< [3..3] Counter/Timer B1 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA2C0INT : 1; /*!< [4..4] Counter/Timer A2 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB2C0INT : 1; /*!< [5..5] Counter/Timer B2 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA3C0INT : 1; /*!< [6..6] Counter/Timer A3 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB3C0INT : 1; /*!< [7..7] Counter/Timer B3 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA4C0INT : 1; /*!< [8..8] Counter/Timer A4 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB4C0INT : 1; /*!< [9..9] Counter/Timer B4 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA5C0INT : 1; /*!< [10..10] Counter/Timer A5 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB5C0INT : 1; /*!< [11..11] Counter/Timer B5 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA6C0INT : 1; /*!< [12..12] Counter/Timer A6 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB6C0INT : 1; /*!< [13..13] Counter/Timer B6 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA7C0INT : 1; /*!< [14..14] Counter/Timer A7 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB7C0INT : 1; /*!< [15..15] Counter/Timer B7 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA0C1INT : 1; /*!< [16..16] Counter/Timer A0 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB0C1INT : 1; /*!< [17..17] Counter/Timer B0 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA1C1INT : 1; /*!< [18..18] Counter/Timer A1 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB1C1INT : 1; /*!< [19..19] Counter/Timer B1 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA2C1INT : 1; /*!< [20..20] Counter/Timer A2 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB2C1INT : 1; /*!< [21..21] Counter/Timer B2 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA3C1INT : 1; /*!< [22..22] Counter/Timer A3 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB3C1INT : 1; /*!< [23..23] Counter/Timer B3 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA4C1INT : 1; /*!< [24..24] Counter/Timer A4 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB4C1INT : 1; /*!< [25..25] Counter/Timer B4 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA5C1INT : 1; /*!< [26..26] Counter/Timer A5 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB5C1INT : 1; /*!< [27..27] Counter/Timer B5 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA6C1INT : 1; /*!< [28..28] Counter/Timer A6 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB6C1INT : 1; /*!< [29..29] Counter/Timer B6 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA7C1INT : 1; /*!< [30..30] Counter/Timer A7 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB7C1INT : 1; /*!< [31..31] Counter/Timer B7 interrupt based on COMPR1. */ + } INTEN_b; + } ; + + union { + __IOM uint32_t INTSTAT; /*!< (@ 0x00000204) Counter/Timer Interrupts: Status */ + + struct { + __IOM uint32_t CTMRA0C0INT : 1; /*!< [0..0] Counter/Timer A0 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB0C0INT : 1; /*!< [1..1] Counter/Timer B0 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA1C0INT : 1; /*!< [2..2] Counter/Timer A1 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB1C0INT : 1; /*!< [3..3] Counter/Timer B1 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA2C0INT : 1; /*!< [4..4] Counter/Timer A2 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB2C0INT : 1; /*!< [5..5] Counter/Timer B2 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA3C0INT : 1; /*!< [6..6] Counter/Timer A3 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB3C0INT : 1; /*!< [7..7] Counter/Timer B3 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA4C0INT : 1; /*!< [8..8] Counter/Timer A4 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB4C0INT : 1; /*!< [9..9] Counter/Timer B4 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA5C0INT : 1; /*!< [10..10] Counter/Timer A5 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB5C0INT : 1; /*!< [11..11] Counter/Timer B5 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA6C0INT : 1; /*!< [12..12] Counter/Timer A6 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB6C0INT : 1; /*!< [13..13] Counter/Timer B6 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA7C0INT : 1; /*!< [14..14] Counter/Timer A7 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB7C0INT : 1; /*!< [15..15] Counter/Timer B7 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA0C1INT : 1; /*!< [16..16] Counter/Timer A0 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB0C1INT : 1; /*!< [17..17] Counter/Timer B0 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA1C1INT : 1; /*!< [18..18] Counter/Timer A1 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB1C1INT : 1; /*!< [19..19] Counter/Timer B1 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA2C1INT : 1; /*!< [20..20] Counter/Timer A2 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB2C1INT : 1; /*!< [21..21] Counter/Timer B2 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA3C1INT : 1; /*!< [22..22] Counter/Timer A3 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB3C1INT : 1; /*!< [23..23] Counter/Timer B3 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA4C1INT : 1; /*!< [24..24] Counter/Timer A4 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB4C1INT : 1; /*!< [25..25] Counter/Timer B4 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA5C1INT : 1; /*!< [26..26] Counter/Timer A5 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB5C1INT : 1; /*!< [27..27] Counter/Timer B5 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA6C1INT : 1; /*!< [28..28] Counter/Timer A6 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB6C1INT : 1; /*!< [29..29] Counter/Timer B6 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA7C1INT : 1; /*!< [30..30] Counter/Timer A7 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB7C1INT : 1; /*!< [31..31] Counter/Timer B7 interrupt based on COMPR1. */ + } INTSTAT_b; + } ; + + union { + __IOM uint32_t INTCLR; /*!< (@ 0x00000208) Counter/Timer Interrupts: Clear */ + + struct { + __IOM uint32_t CTMRA0C0INT : 1; /*!< [0..0] Counter/Timer A0 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB0C0INT : 1; /*!< [1..1] Counter/Timer B0 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA1C0INT : 1; /*!< [2..2] Counter/Timer A1 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB1C0INT : 1; /*!< [3..3] Counter/Timer B1 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA2C0INT : 1; /*!< [4..4] Counter/Timer A2 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB2C0INT : 1; /*!< [5..5] Counter/Timer B2 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA3C0INT : 1; /*!< [6..6] Counter/Timer A3 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB3C0INT : 1; /*!< [7..7] Counter/Timer B3 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA4C0INT : 1; /*!< [8..8] Counter/Timer A4 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB4C0INT : 1; /*!< [9..9] Counter/Timer B4 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA5C0INT : 1; /*!< [10..10] Counter/Timer A5 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB5C0INT : 1; /*!< [11..11] Counter/Timer B5 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA6C0INT : 1; /*!< [12..12] Counter/Timer A6 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB6C0INT : 1; /*!< [13..13] Counter/Timer B6 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA7C0INT : 1; /*!< [14..14] Counter/Timer A7 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB7C0INT : 1; /*!< [15..15] Counter/Timer B7 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA0C1INT : 1; /*!< [16..16] Counter/Timer A0 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB0C1INT : 1; /*!< [17..17] Counter/Timer B0 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA1C1INT : 1; /*!< [18..18] Counter/Timer A1 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB1C1INT : 1; /*!< [19..19] Counter/Timer B1 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA2C1INT : 1; /*!< [20..20] Counter/Timer A2 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB2C1INT : 1; /*!< [21..21] Counter/Timer B2 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA3C1INT : 1; /*!< [22..22] Counter/Timer A3 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB3C1INT : 1; /*!< [23..23] Counter/Timer B3 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA4C1INT : 1; /*!< [24..24] Counter/Timer A4 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB4C1INT : 1; /*!< [25..25] Counter/Timer B4 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA5C1INT : 1; /*!< [26..26] Counter/Timer A5 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB5C1INT : 1; /*!< [27..27] Counter/Timer B5 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA6C1INT : 1; /*!< [28..28] Counter/Timer A6 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB6C1INT : 1; /*!< [29..29] Counter/Timer B6 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA7C1INT : 1; /*!< [30..30] Counter/Timer A7 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB7C1INT : 1; /*!< [31..31] Counter/Timer B7 interrupt based on COMPR1. */ + } INTCLR_b; + } ; + + union { + __IOM uint32_t INTSET; /*!< (@ 0x0000020C) Counter/Timer Interrupts: Set */ + + struct { + __IOM uint32_t CTMRA0C0INT : 1; /*!< [0..0] Counter/Timer A0 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB0C0INT : 1; /*!< [1..1] Counter/Timer B0 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA1C0INT : 1; /*!< [2..2] Counter/Timer A1 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB1C0INT : 1; /*!< [3..3] Counter/Timer B1 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA2C0INT : 1; /*!< [4..4] Counter/Timer A2 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB2C0INT : 1; /*!< [5..5] Counter/Timer B2 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA3C0INT : 1; /*!< [6..6] Counter/Timer A3 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB3C0INT : 1; /*!< [7..7] Counter/Timer B3 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA4C0INT : 1; /*!< [8..8] Counter/Timer A4 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB4C0INT : 1; /*!< [9..9] Counter/Timer B4 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA5C0INT : 1; /*!< [10..10] Counter/Timer A5 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB5C0INT : 1; /*!< [11..11] Counter/Timer B5 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA6C0INT : 1; /*!< [12..12] Counter/Timer A6 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB6C0INT : 1; /*!< [13..13] Counter/Timer B6 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA7C0INT : 1; /*!< [14..14] Counter/Timer A7 interrupt based on COMPR0. */ + __IOM uint32_t CTMRB7C0INT : 1; /*!< [15..15] Counter/Timer B7 interrupt based on COMPR0. */ + __IOM uint32_t CTMRA0C1INT : 1; /*!< [16..16] Counter/Timer A0 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB0C1INT : 1; /*!< [17..17] Counter/Timer B0 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA1C1INT : 1; /*!< [18..18] Counter/Timer A1 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB1C1INT : 1; /*!< [19..19] Counter/Timer B1 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA2C1INT : 1; /*!< [20..20] Counter/Timer A2 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB2C1INT : 1; /*!< [21..21] Counter/Timer B2 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA3C1INT : 1; /*!< [22..22] Counter/Timer A3 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB3C1INT : 1; /*!< [23..23] Counter/Timer B3 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA4C1INT : 1; /*!< [24..24] Counter/Timer A4 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB4C1INT : 1; /*!< [25..25] Counter/Timer B4 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA5C1INT : 1; /*!< [26..26] Counter/Timer A5 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB5C1INT : 1; /*!< [27..27] Counter/Timer B5 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA6C1INT : 1; /*!< [28..28] Counter/Timer A6 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB6C1INT : 1; /*!< [29..29] Counter/Timer B6 interrupt based on COMPR1. */ + __IOM uint32_t CTMRA7C1INT : 1; /*!< [30..30] Counter/Timer A7 interrupt based on COMPR1. */ + __IOM uint32_t CTMRB7C1INT : 1; /*!< [31..31] Counter/Timer B7 interrupt based on COMPR1. */ + } INTSET_b; + } ; + __IM uint32_t RESERVED12[60]; + + union { + __IOM uint32_t STMINTEN; /*!< (@ 0x00000300) STIMER Interrupt registers: Enable */ + + struct { + __IOM uint32_t COMPAREA : 1; /*!< [0..0] COUNTER is greater than or equal to COMPARE register + A. */ + __IOM uint32_t COMPAREB : 1; /*!< [1..1] COUNTER is greater than or equal to COMPARE register + B. */ + __IOM uint32_t COMPAREC : 1; /*!< [2..2] COUNTER is greater than or equal to COMPARE register + C. */ + __IOM uint32_t COMPARED : 1; /*!< [3..3] COUNTER is greater than or equal to COMPARE register + D. */ + __IOM uint32_t COMPAREE : 1; /*!< [4..4] COUNTER is greater than or equal to COMPARE register + E. */ + __IOM uint32_t COMPAREF : 1; /*!< [5..5] COUNTER is greater than or equal to COMPARE register + F. */ + __IOM uint32_t COMPAREG : 1; /*!< [6..6] COUNTER is greater than or equal to COMPARE register + G. */ + __IOM uint32_t COMPAREH : 1; /*!< [7..7] COUNTER is greater than or equal to COMPARE register + H. */ + __IOM uint32_t OVERFLOW : 1; /*!< [8..8] COUNTER over flowed from 0xFFFFFFFF back to 0x00000000. */ + __IOM uint32_t CAPTUREA : 1; /*!< [9..9] CAPTURE register A has grabbed the value in the counter */ + __IOM uint32_t CAPTUREB : 1; /*!< [10..10] CAPTURE register B has grabbed the value in the counter */ + __IOM uint32_t CAPTUREC : 1; /*!< [11..11] CAPTURE register C has grabbed the value in the counter */ + __IOM uint32_t CAPTURED : 1; /*!< [12..12] CAPTURE register D has grabbed the value in the counter */ + } STMINTEN_b; + } ; + + union { + __IOM uint32_t STMINTSTAT; /*!< (@ 0x00000304) STIMER Interrupt registers: Status */ + + struct { + __IOM uint32_t COMPAREA : 1; /*!< [0..0] COUNTER is greater than or equal to COMPARE register + A. */ + __IOM uint32_t COMPAREB : 1; /*!< [1..1] COUNTER is greater than or equal to COMPARE register + B. */ + __IOM uint32_t COMPAREC : 1; /*!< [2..2] COUNTER is greater than or equal to COMPARE register + C. */ + __IOM uint32_t COMPARED : 1; /*!< [3..3] COUNTER is greater than or equal to COMPARE register + D. */ + __IOM uint32_t COMPAREE : 1; /*!< [4..4] COUNTER is greater than or equal to COMPARE register + E. */ + __IOM uint32_t COMPAREF : 1; /*!< [5..5] COUNTER is greater than or equal to COMPARE register + F. */ + __IOM uint32_t COMPAREG : 1; /*!< [6..6] COUNTER is greater than or equal to COMPARE register + G. */ + __IOM uint32_t COMPAREH : 1; /*!< [7..7] COUNTER is greater than or equal to COMPARE register + H. */ + __IOM uint32_t OVERFLOW : 1; /*!< [8..8] COUNTER over flowed from 0xFFFFFFFF back to 0x00000000. */ + __IOM uint32_t CAPTUREA : 1; /*!< [9..9] CAPTURE register A has grabbed the value in the counter */ + __IOM uint32_t CAPTUREB : 1; /*!< [10..10] CAPTURE register B has grabbed the value in the counter */ + __IOM uint32_t CAPTUREC : 1; /*!< [11..11] CAPTURE register C has grabbed the value in the counter */ + __IOM uint32_t CAPTURED : 1; /*!< [12..12] CAPTURE register D has grabbed the value in the counter */ + } STMINTSTAT_b; + } ; + + union { + __IOM uint32_t STMINTCLR; /*!< (@ 0x00000308) STIMER Interrupt registers: Clear */ + + struct { + __IOM uint32_t COMPAREA : 1; /*!< [0..0] COUNTER is greater than or equal to COMPARE register + A. */ + __IOM uint32_t COMPAREB : 1; /*!< [1..1] COUNTER is greater than or equal to COMPARE register + B. */ + __IOM uint32_t COMPAREC : 1; /*!< [2..2] COUNTER is greater than or equal to COMPARE register + C. */ + __IOM uint32_t COMPARED : 1; /*!< [3..3] COUNTER is greater than or equal to COMPARE register + D. */ + __IOM uint32_t COMPAREE : 1; /*!< [4..4] COUNTER is greater than or equal to COMPARE register + E. */ + __IOM uint32_t COMPAREF : 1; /*!< [5..5] COUNTER is greater than or equal to COMPARE register + F. */ + __IOM uint32_t COMPAREG : 1; /*!< [6..6] COUNTER is greater than or equal to COMPARE register + G. */ + __IOM uint32_t COMPAREH : 1; /*!< [7..7] COUNTER is greater than or equal to COMPARE register + H. */ + __IOM uint32_t OVERFLOW : 1; /*!< [8..8] COUNTER over flowed from 0xFFFFFFFF back to 0x00000000. */ + __IOM uint32_t CAPTUREA : 1; /*!< [9..9] CAPTURE register A has grabbed the value in the counter */ + __IOM uint32_t CAPTUREB : 1; /*!< [10..10] CAPTURE register B has grabbed the value in the counter */ + __IOM uint32_t CAPTUREC : 1; /*!< [11..11] CAPTURE register C has grabbed the value in the counter */ + __IOM uint32_t CAPTURED : 1; /*!< [12..12] CAPTURE register D has grabbed the value in the counter */ + } STMINTCLR_b; + } ; + + union { + __IOM uint32_t STMINTSET; /*!< (@ 0x0000030C) STIMER Interrupt registers: Set */ + + struct { + __IOM uint32_t COMPAREA : 1; /*!< [0..0] COUNTER is greater than or equal to COMPARE register + A. */ + __IOM uint32_t COMPAREB : 1; /*!< [1..1] COUNTER is greater than or equal to COMPARE register + B. */ + __IOM uint32_t COMPAREC : 1; /*!< [2..2] COUNTER is greater than or equal to COMPARE register + C. */ + __IOM uint32_t COMPARED : 1; /*!< [3..3] COUNTER is greater than or equal to COMPARE register + D. */ + __IOM uint32_t COMPAREE : 1; /*!< [4..4] COUNTER is greater than or equal to COMPARE register + E. */ + __IOM uint32_t COMPAREF : 1; /*!< [5..5] COUNTER is greater than or equal to COMPARE register + F. */ + __IOM uint32_t COMPAREG : 1; /*!< [6..6] COUNTER is greater than or equal to COMPARE register + G. */ + __IOM uint32_t COMPAREH : 1; /*!< [7..7] COUNTER is greater than or equal to COMPARE register + H. */ + __IOM uint32_t OVERFLOW : 1; /*!< [8..8] COUNTER over flowed from 0xFFFFFFFF back to 0x00000000. */ + __IOM uint32_t CAPTUREA : 1; /*!< [9..9] CAPTURE register A has grabbed the value in the counter */ + __IOM uint32_t CAPTUREB : 1; /*!< [10..10] CAPTURE register B has grabbed the value in the counter */ + __IOM uint32_t CAPTUREC : 1; /*!< [11..11] CAPTURE register C has grabbed the value in the counter */ + __IOM uint32_t CAPTURED : 1; /*!< [12..12] CAPTURE register D has grabbed the value in the counter */ + } STMINTSET_b; + } ; +} CTIMER_Type; /*!< Size = 784 (0x310) */ + + + +/* =========================================================================================================================== */ +/* ================ GPIO ================ */ +/* =========================================================================================================================== */ + + +/** + * @brief General Purpose IO (GPIO) + */ + +typedef struct { /*!< (@ 0x40010000) GPIO Structure */ + + union { + __IOM uint32_t PADREGA; /*!< (@ 0x00000000) Pad Configuration Register A (Pads 0-3) */ + + struct { + __IOM uint32_t PAD0PULL : 1; /*!< [0..0] Pad 0 pullup enable */ + __IOM uint32_t PAD0INPEN : 1; /*!< [1..1] Pad 0 input enable */ + __IOM uint32_t PAD0STRNG : 1; /*!< [2..2] Pad 0 drive strength */ + __IOM uint32_t PAD0FNCSEL : 3; /*!< [5..3] Pad 0 function select */ + __IOM uint32_t PAD0RSEL : 2; /*!< [7..6] Pad 0 pullup resistor selection. */ + __IOM uint32_t PAD1PULL : 1; /*!< [8..8] Pad 1 pullup enable */ + __IOM uint32_t PAD1INPEN : 1; /*!< [9..9] Pad 1 input enable */ + __IOM uint32_t PAD1STRNG : 1; /*!< [10..10] Pad 1 drive strength */ + __IOM uint32_t PAD1FNCSEL : 3; /*!< [13..11] Pad 1 function select */ + __IOM uint32_t PAD1RSEL : 2; /*!< [15..14] Pad 1 pullup resistor selection. */ + __IOM uint32_t PAD2PULL : 1; /*!< [16..16] Pad 2 pullup enable */ + __IOM uint32_t PAD2INPEN : 1; /*!< [17..17] Pad 2 input enable */ + __IOM uint32_t PAD2STRNG : 1; /*!< [18..18] Pad 2 drive strength */ + __IOM uint32_t PAD2FNCSEL : 3; /*!< [21..19] Pad 2 function select */ + __IM uint32_t : 2; + __IOM uint32_t PAD3PULL : 1; /*!< [24..24] Pad 3 pullup enable */ + __IOM uint32_t PAD3INPEN : 1; /*!< [25..25] Pad 3 input enable. */ + __IOM uint32_t PAD3STRNG : 1; /*!< [26..26] Pad 3 drive strength. */ + __IOM uint32_t PAD3FNCSEL : 3; /*!< [29..27] Pad 3 function select */ + __IOM uint32_t PAD3PWRUP : 1; /*!< [30..30] Pad 3 VDD power switch enable */ + } PADREGA_b; + } ; + + union { + __IOM uint32_t PADREGB; /*!< (@ 0x00000004) Pad Configuration Register B (Pads 4-7) */ + + struct { + __IOM uint32_t PAD4PULL : 1; /*!< [0..0] Pad 4 pullup enable */ + __IOM uint32_t PAD4INPEN : 1; /*!< [1..1] Pad 4 input enable */ + __IOM uint32_t PAD4STRNG : 1; /*!< [2..2] Pad 4 drive strength */ + __IOM uint32_t PAD4FNCSEL : 3; /*!< [5..3] Pad 4 function select */ + __IM uint32_t : 2; + __IOM uint32_t PAD5PULL : 1; /*!< [8..8] Pad 5 pullup enable */ + __IOM uint32_t PAD5INPEN : 1; /*!< [9..9] Pad 5 input enable */ + __IOM uint32_t PAD5STRNG : 1; /*!< [10..10] Pad 5 drive strength */ + __IOM uint32_t PAD5FNCSEL : 3; /*!< [13..11] Pad 5 function select */ + __IOM uint32_t PAD5RSEL : 2; /*!< [15..14] Pad 5 pullup resistor selection. */ + __IOM uint32_t PAD6PULL : 1; /*!< [16..16] Pad 6 pullup enable */ + __IOM uint32_t PAD6INPEN : 1; /*!< [17..17] Pad 6 input enable */ + __IOM uint32_t PAD6STRNG : 1; /*!< [18..18] Pad 6 drive strength */ + __IOM uint32_t PAD6FNCSEL : 3; /*!< [21..19] Pad 6 function select */ + __IOM uint32_t PAD6RSEL : 2; /*!< [23..22] Pad 6 pullup resistor selection. */ + __IOM uint32_t PAD7PULL : 1; /*!< [24..24] Pad 7 pullup enable */ + __IOM uint32_t PAD7INPEN : 1; /*!< [25..25] Pad 7 input enable */ + __IOM uint32_t PAD7STRNG : 1; /*!< [26..26] Pad 7 drive strength */ + __IOM uint32_t PAD7FNCSEL : 3; /*!< [29..27] Pad 7 function select */ + } PADREGB_b; + } ; + + union { + __IOM uint32_t PADREGC; /*!< (@ 0x00000008) Pad Configuration Register C (Pads 8-11) */ + + struct { + __IOM uint32_t PAD8PULL : 1; /*!< [0..0] Pad 8 pullup enable */ + __IOM uint32_t PAD8INPEN : 1; /*!< [1..1] Pad 8 input enable */ + __IOM uint32_t PAD8STRNG : 1; /*!< [2..2] Pad 8 drive strength */ + __IOM uint32_t PAD8FNCSEL : 3; /*!< [5..3] Pad 8 function select */ + __IOM uint32_t PAD8RSEL : 2; /*!< [7..6] Pad 8 pullup resistor selection. */ + __IOM uint32_t PAD9PULL : 1; /*!< [8..8] Pad 9 pullup enable */ + __IOM uint32_t PAD9INPEN : 1; /*!< [9..9] Pad 9 input enable */ + __IOM uint32_t PAD9STRNG : 1; /*!< [10..10] Pad 9 drive strength */ + __IOM uint32_t PAD9FNCSEL : 3; /*!< [13..11] Pad 9 function select */ + __IOM uint32_t PAD9RSEL : 2; /*!< [15..14] Pad 9 pullup resistor selection */ + __IOM uint32_t PAD10PULL : 1; /*!< [16..16] Pad 10 pullup enable */ + __IOM uint32_t PAD10INPEN : 1; /*!< [17..17] Pad 10 input enable */ + __IOM uint32_t PAD10STRNG : 1; /*!< [18..18] Pad 10 drive strength */ + __IOM uint32_t PAD10FNCSEL : 3; /*!< [21..19] Pad 10 function select */ + __IM uint32_t : 2; + __IOM uint32_t PAD11PULL : 1; /*!< [24..24] Pad 11 pullup enable */ + __IOM uint32_t PAD11INPEN : 1; /*!< [25..25] Pad 11 input enable */ + __IOM uint32_t PAD11STRNG : 1; /*!< [26..26] Pad 11 drive strength */ + __IOM uint32_t PAD11FNCSEL : 3; /*!< [29..27] Pad 11 function select */ + } PADREGC_b; + } ; + + union { + __IOM uint32_t PADREGD; /*!< (@ 0x0000000C) Pad Configuration Register D (Pads 12-15) */ + + struct { + __IOM uint32_t PAD12PULL : 1; /*!< [0..0] Pad 12 pullup enable */ + __IOM uint32_t PAD12INPEN : 1; /*!< [1..1] Pad 12 input enable */ + __IOM uint32_t PAD12STRNG : 1; /*!< [2..2] Pad 12 drive strength */ + __IOM uint32_t PAD12FNCSEL : 3; /*!< [5..3] Pad 12 function select */ + __IM uint32_t : 2; + __IOM uint32_t PAD13PULL : 1; /*!< [8..8] Pad 13 pullup enable */ + __IOM uint32_t PAD13INPEN : 1; /*!< [9..9] Pad 13 input enable */ + __IOM uint32_t PAD13STRNG : 1; /*!< [10..10] Pad 13 drive strength */ + __IOM uint32_t PAD13FNCSEL : 3; /*!< [13..11] Pad 13 function select */ + __IM uint32_t : 2; + __IOM uint32_t PAD14PULL : 1; /*!< [16..16] Pad 14 pullup enable */ + __IOM uint32_t PAD14INPEN : 1; /*!< [17..17] Pad 14 input enable */ + __IOM uint32_t PAD14STRNG : 1; /*!< [18..18] Pad 14 drive strength */ + __IOM uint32_t PAD14FNCSEL : 3; /*!< [21..19] Pad 14 function select */ + __IM uint32_t : 2; + __IOM uint32_t PAD15PULL : 1; /*!< [24..24] Pad 15 pullup enable */ + __IOM uint32_t PAD15INPEN : 1; /*!< [25..25] Pad 15 input enable */ + __IOM uint32_t PAD15STRNG : 1; /*!< [26..26] Pad 15 drive strength */ + __IOM uint32_t PAD15FNCSEL : 3; /*!< [29..27] Pad 15 function select */ + } PADREGD_b; + } ; + + union { + __IOM uint32_t PADREGE; /*!< (@ 0x00000010) Pad Configuration Register E (Pads 16-19) */ + + struct { + __IOM uint32_t PAD16PULL : 1; /*!< [0..0] Pad 16 pullup enable */ + __IOM uint32_t PAD16INPEN : 1; /*!< [1..1] Pad 16 input enable */ + __IOM uint32_t PAD16STRNG : 1; /*!< [2..2] Pad 16 drive strength */ + __IOM uint32_t PAD16FNCSEL : 3; /*!< [5..3] Pad 16 function select */ + __IM uint32_t : 2; + __IOM uint32_t PAD17PULL : 1; /*!< [8..8] Pad 17 pullup enable */ + __IOM uint32_t PAD17INPEN : 1; /*!< [9..9] Pad 17 input enable */ + __IOM uint32_t PAD17STRNG : 1; /*!< [10..10] Pad 17 drive strength */ + __IOM uint32_t PAD17FNCSEL : 3; /*!< [13..11] Pad 17 function select */ + __IM uint32_t : 2; + __IOM uint32_t PAD18PULL : 1; /*!< [16..16] Pad 18 pullup enable */ + __IOM uint32_t PAD18INPEN : 1; /*!< [17..17] Pad 18 input enable */ + __IOM uint32_t PAD18STRNG : 1; /*!< [18..18] Pad 18 drive strength */ + __IOM uint32_t PAD18FNCSEL : 3; /*!< [21..19] Pad 18 function select */ + __IM uint32_t : 2; + __IOM uint32_t PAD19PULL : 1; /*!< [24..24] Pad 19 pullup enable */ + __IOM uint32_t PAD19INPEN : 1; /*!< [25..25] Pad 19 input enable */ + __IOM uint32_t PAD19STRNG : 1; /*!< [26..26] Pad 19 drive strength */ + __IOM uint32_t PAD19FNCSEL : 3; /*!< [29..27] Pad 19 function select */ + } PADREGE_b; + } ; + + union { + __IOM uint32_t PADREGF; /*!< (@ 0x00000014) Pad Configuration Register F (Pads 20-23) */ + + struct { + __IOM uint32_t PAD20PULL : 1; /*!< [0..0] Pad 20 pulldown enable */ + __IOM uint32_t PAD20INPEN : 1; /*!< [1..1] Pad 20 input enable */ + __IOM uint32_t PAD20STRNG : 1; /*!< [2..2] Pad 20 drive strength */ + __IOM uint32_t PAD20FNCSEL : 3; /*!< [5..3] Pad 20 function select */ + __IM uint32_t : 2; + __IOM uint32_t PAD21PULL : 1; /*!< [8..8] Pad 21 pullup enable */ + __IOM uint32_t PAD21INPEN : 1; /*!< [9..9] Pad 21 input enable */ + __IOM uint32_t PAD21STRNG : 1; /*!< [10..10] Pad 21 drive strength */ + __IOM uint32_t PAD21FNCSEL : 3; /*!< [13..11] Pad 21 function select */ + __IM uint32_t : 2; + __IOM uint32_t PAD22PULL : 1; /*!< [16..16] Pad 22 pullup enable */ + __IOM uint32_t PAD22INPEN : 1; /*!< [17..17] Pad 22 input enable */ + __IOM uint32_t PAD22STRNG : 1; /*!< [18..18] Pad 22 drive strength */ + __IOM uint32_t PAD22FNCSEL : 3; /*!< [21..19] Pad 22 function select */ + __IM uint32_t : 2; + __IOM uint32_t PAD23PULL : 1; /*!< [24..24] Pad 23 pullup enable */ + __IOM uint32_t PAD23INPEN : 1; /*!< [25..25] Pad 23 input enable */ + __IOM uint32_t PAD23STRNG : 1; /*!< [26..26] Pad 23 drive strength */ + __IOM uint32_t PAD23FNCSEL : 3; /*!< [29..27] Pad 23 function select */ + } PADREGF_b; + } ; + + union { + __IOM uint32_t PADREGG; /*!< (@ 0x00000018) Pad Configuration Register G (Pads 24-27) */ + + struct { + __IOM uint32_t PAD24PULL : 1; /*!< [0..0] Pad 24 pullup enable */ + __IOM uint32_t PAD24INPEN : 1; /*!< [1..1] Pad 24 input enable */ + __IOM uint32_t PAD24STRNG : 1; /*!< [2..2] Pad 24 drive strength */ + __IOM uint32_t PAD24FNCSEL : 3; /*!< [5..3] Pad 24 function select */ + __IM uint32_t : 2; + __IOM uint32_t PAD25PULL : 1; /*!< [8..8] Pad 25 pullup enable */ + __IOM uint32_t PAD25INPEN : 1; /*!< [9..9] Pad 25 input enable */ + __IOM uint32_t PAD25STRNG : 1; /*!< [10..10] Pad 25 drive strength */ + __IOM uint32_t PAD25FNCSEL : 3; /*!< [13..11] Pad 25 function select */ + __IOM uint32_t PAD25RSEL : 2; /*!< [15..14] Pad 25 pullup resistor selection. */ + __IOM uint32_t PAD26PULL : 1; /*!< [16..16] Pad 26 pullup enable */ + __IOM uint32_t PAD26INPEN : 1; /*!< [17..17] Pad 26 input enable */ + __IOM uint32_t PAD26STRNG : 1; /*!< [18..18] Pad 26 drive strength */ + __IOM uint32_t PAD26FNCSEL : 3; /*!< [21..19] Pad 26 function select */ + __IM uint32_t : 2; + __IOM uint32_t PAD27PULL : 1; /*!< [24..24] Pad 27 pullup enable */ + __IOM uint32_t PAD27INPEN : 1; /*!< [25..25] Pad 27 input enable */ + __IOM uint32_t PAD27STRNG : 1; /*!< [26..26] Pad 27 drive strength */ + __IOM uint32_t PAD27FNCSEL : 3; /*!< [29..27] Pad 27 function select */ + __IOM uint32_t PAD27RSEL : 2; /*!< [31..30] Pad 27 pullup resistor selection. */ + } PADREGG_b; + } ; + + union { + __IOM uint32_t PADREGH; /*!< (@ 0x0000001C) Pad Configuration Register H (Pads 28-31) */ + + struct { + __IOM uint32_t PAD28PULL : 1; /*!< [0..0] Pad 28 pullup enable */ + __IOM uint32_t PAD28INPEN : 1; /*!< [1..1] Pad 28 input enable */ + __IOM uint32_t PAD28STRNG : 1; /*!< [2..2] Pad 28 drive strength */ + __IOM uint32_t PAD28FNCSEL : 3; /*!< [5..3] Pad 28 function select */ + __IM uint32_t : 2; + __IOM uint32_t PAD29PULL : 1; /*!< [8..8] Pad 29 pullup enable */ + __IOM uint32_t PAD29INPEN : 1; /*!< [9..9] Pad 29 input enable */ + __IOM uint32_t PAD29STRNG : 1; /*!< [10..10] Pad 29 drive strength */ + __IOM uint32_t PAD29FNCSEL : 3; /*!< [13..11] Pad 29 function select */ + __IM uint32_t : 2; + __IOM uint32_t PAD30PULL : 1; /*!< [16..16] Pad 30 pullup enable */ + __IOM uint32_t PAD30INPEN : 1; /*!< [17..17] Pad 30 input enable */ + __IOM uint32_t PAD30STRNG : 1; /*!< [18..18] Pad 30 drive strength */ + __IOM uint32_t PAD30FNCSEL : 3; /*!< [21..19] Pad 30 function select */ + __IM uint32_t : 2; + __IOM uint32_t PAD31PULL : 1; /*!< [24..24] Pad 31 pullup enable */ + __IOM uint32_t PAD31INPEN : 1; /*!< [25..25] Pad 31 input enable */ + __IOM uint32_t PAD31STRNG : 1; /*!< [26..26] Pad 31 drive strength */ + __IOM uint32_t PAD31FNCSEL : 3; /*!< [29..27] Pad 31 function select */ + } PADREGH_b; + } ; + + union { + __IOM uint32_t PADREGI; /*!< (@ 0x00000020) Pad Configuration Register I (Pads 32-25) */ + + struct { + __IOM uint32_t PAD32PULL : 1; /*!< [0..0] Pad 32 pullup enable */ + __IOM uint32_t PAD32INPEN : 1; /*!< [1..1] Pad 32 input enable */ + __IOM uint32_t PAD32STRNG : 1; /*!< [2..2] Pad 32 drive strength */ + __IOM uint32_t PAD32FNCSEL : 3; /*!< [5..3] Pad 32 function select */ + __IM uint32_t : 2; + __IOM uint32_t PAD33PULL : 1; /*!< [8..8] Pad 33 pullup enable */ + __IOM uint32_t PAD33INPEN : 1; /*!< [9..9] Pad 33 input enable */ + __IOM uint32_t PAD33STRNG : 1; /*!< [10..10] Pad 33 drive strength */ + __IOM uint32_t PAD33FNCSEL : 3; /*!< [13..11] Pad 33 function select */ + __IM uint32_t : 2; + __IOM uint32_t PAD34PULL : 1; /*!< [16..16] Pad 34 pullup enable */ + __IOM uint32_t PAD34INPEN : 1; /*!< [17..17] Pad 34 input enable */ + __IOM uint32_t PAD34STRNG : 1; /*!< [18..18] Pad 34 drive strength */ + __IOM uint32_t PAD34FNCSEL : 3; /*!< [21..19] Pad 34 function select */ + __IM uint32_t : 2; + __IOM uint32_t PAD35PULL : 1; /*!< [24..24] Pad 35 pullup enable */ + __IOM uint32_t PAD35INPEN : 1; /*!< [25..25] Pad 35 input enable */ + __IOM uint32_t PAD35STRNG : 1; /*!< [26..26] Pad 35 drive strength */ + __IOM uint32_t PAD35FNCSEL : 3; /*!< [29..27] Pad 35 function select */ + } PADREGI_b; + } ; + + union { + __IOM uint32_t PADREGJ; /*!< (@ 0x00000024) Pad Configuration Register J (Pads 36-39) */ + + struct { + __IOM uint32_t PAD36PULL : 1; /*!< [0..0] Pad 36 pullup enable */ + __IOM uint32_t PAD36INPEN : 1; /*!< [1..1] Pad 36 input enable */ + __IOM uint32_t PAD36STRNG : 1; /*!< [2..2] Pad 36 drive strength */ + __IOM uint32_t PAD36FNCSEL : 3; /*!< [5..3] Pad 36 function select */ + __IOM uint32_t PAD36PWRUP : 1; /*!< [6..6] Pad 36 VDD power switch enable */ + __IM uint32_t : 1; + __IOM uint32_t PAD37PULL : 1; /*!< [8..8] Pad 37 pullup enable */ + __IOM uint32_t PAD37INPEN : 1; /*!< [9..9] Pad 37 input enable */ + __IOM uint32_t PAD37STRNG : 1; /*!< [10..10] Pad 37 drive strength */ + __IOM uint32_t PAD37FNCSEL : 3; /*!< [13..11] Pad 37 function select */ + __IM uint32_t : 1; + __IOM uint32_t PAD37PWRDN : 1; /*!< [15..15] Pad 37 VSS power switch enable */ + __IOM uint32_t PAD38PULL : 1; /*!< [16..16] Pad 38 pullup enable */ + __IOM uint32_t PAD38INPEN : 1; /*!< [17..17] Pad 38 input enable */ + __IOM uint32_t PAD38STRNG : 1; /*!< [18..18] Pad 38 drive strength */ + __IOM uint32_t PAD38FNCSEL : 3; /*!< [21..19] Pad 38 function select */ + __IM uint32_t : 2; + __IOM uint32_t PAD39PULL : 1; /*!< [24..24] Pad 39 pullup enable */ + __IOM uint32_t PAD39INPEN : 1; /*!< [25..25] Pad 39 input enable */ + __IOM uint32_t PAD39STRNG : 1; /*!< [26..26] Pad 39 drive strength */ + __IOM uint32_t PAD39FNCSEL : 3; /*!< [29..27] Pad 39 function select */ + __IOM uint32_t PAD39RSEL : 2; /*!< [31..30] Pad 39 pullup resistor selection. */ + } PADREGJ_b; + } ; + + union { + __IOM uint32_t PADREGK; /*!< (@ 0x00000028) Pad Configuration Register K (Pads 40-43) */ + + struct { + __IOM uint32_t PAD40PULL : 1; /*!< [0..0] Pad 40 pullup enable */ + __IOM uint32_t PAD40INPEN : 1; /*!< [1..1] Pad 40 input enable */ + __IOM uint32_t PAD40STRNG : 1; /*!< [2..2] Pad 40 drive strength */ + __IOM uint32_t PAD40FNCSEL : 3; /*!< [5..3] Pad 40 function select */ + __IOM uint32_t PAD40RSEL : 2; /*!< [7..6] Pad 40 pullup resistor selection. */ + __IOM uint32_t PAD41PULL : 1; /*!< [8..8] Pad 41 pullup enable */ + __IOM uint32_t PAD41INPEN : 1; /*!< [9..9] Pad 41 input enable */ + __IOM uint32_t PAD41STRNG : 1; /*!< [10..10] Pad 41 drive strength */ + __IOM uint32_t PAD41FNCSEL : 3; /*!< [13..11] Pad 41 function select */ + __IM uint32_t : 1; + __IOM uint32_t PAD41PWRDN : 1; /*!< [15..15] Pad 41 power switch enable */ + __IOM uint32_t PAD42PULL : 1; /*!< [16..16] Pad 42 pullup enable */ + __IOM uint32_t PAD42INPEN : 1; /*!< [17..17] Pad 42 input enable */ + __IOM uint32_t PAD42STRNG : 1; /*!< [18..18] Pad 42 drive strength */ + __IOM uint32_t PAD42FNCSEL : 3; /*!< [21..19] Pad 42 function select */ + __IOM uint32_t PAD42RSEL : 2; /*!< [23..22] Pad 42 pullup resistor selection. */ + __IOM uint32_t PAD43PULL : 1; /*!< [24..24] Pad 43 pullup enable */ + __IOM uint32_t PAD43INPEN : 1; /*!< [25..25] Pad 43 input enable */ + __IOM uint32_t PAD43STRNG : 1; /*!< [26..26] Pad 43 drive strength */ + __IOM uint32_t PAD43FNCSEL : 3; /*!< [29..27] Pad 43 function select */ + __IOM uint32_t PAD43RSEL : 2; /*!< [31..30] Pad 43 pullup resistor selection. */ + } PADREGK_b; + } ; + + union { + __IOM uint32_t PADREGL; /*!< (@ 0x0000002C) Pad Configuration Register L (Pads 44-47) */ + + struct { + __IOM uint32_t PAD44PULL : 1; /*!< [0..0] Pad 44 pullup enable */ + __IOM uint32_t PAD44INPEN : 1; /*!< [1..1] Pad 44 input enable */ + __IOM uint32_t PAD44STRNG : 1; /*!< [2..2] Pad 44 drive strength */ + __IOM uint32_t PAD44FNCSEL : 3; /*!< [5..3] Pad 44 function select */ + __IM uint32_t : 2; + __IOM uint32_t PAD45PULL : 1; /*!< [8..8] Pad 45 pullup enable */ + __IOM uint32_t PAD45INPEN : 1; /*!< [9..9] Pad 45 input enable */ + __IOM uint32_t PAD45STRNG : 1; /*!< [10..10] Pad 45 drive strength */ + __IOM uint32_t PAD45FNCSEL : 3; /*!< [13..11] Pad 45 function select */ + __IM uint32_t : 2; + __IOM uint32_t PAD46PULL : 1; /*!< [16..16] Pad 46 pullup enable */ + __IOM uint32_t PAD46INPEN : 1; /*!< [17..17] Pad 46 input enable */ + __IOM uint32_t PAD46STRNG : 1; /*!< [18..18] Pad 46 drive strength */ + __IOM uint32_t PAD46FNCSEL : 3; /*!< [21..19] Pad 46 function select */ + __IM uint32_t : 2; + __IOM uint32_t PAD47PULL : 1; /*!< [24..24] Pad 47 pullup enable */ + __IOM uint32_t PAD47INPEN : 1; /*!< [25..25] Pad 47 input enable */ + __IOM uint32_t PAD47STRNG : 1; /*!< [26..26] Pad 47 drive strength */ + __IOM uint32_t PAD47FNCSEL : 3; /*!< [29..27] Pad 47 function select */ + } PADREGL_b; + } ; + + union { + __IOM uint32_t PADREGM; /*!< (@ 0x00000030) Pad Configuration Register M (Pads 47-48) */ + + struct { + __IOM uint32_t PAD48PULL : 1; /*!< [0..0] Pad 48 pullup enable */ + __IOM uint32_t PAD48INPEN : 1; /*!< [1..1] Pad 48 input enable */ + __IOM uint32_t PAD48STRNG : 1; /*!< [2..2] Pad 48 drive strength */ + __IOM uint32_t PAD48FNCSEL : 3; /*!< [5..3] Pad 48 function select */ + __IOM uint32_t PAD48RSEL : 2; /*!< [7..6] Pad 48 pullup resistor selection. */ + __IOM uint32_t PAD49PULL : 1; /*!< [8..8] Pad 49 pullup enable */ + __IOM uint32_t PAD49INPEN : 1; /*!< [9..9] Pad 49 input enable */ + __IOM uint32_t PAD49STRNG : 1; /*!< [10..10] Pad 49 drive strength */ + __IOM uint32_t PAD49FNCSEL : 3; /*!< [13..11] Pad 49 function select */ + __IOM uint32_t PAD49RSEL : 2; /*!< [15..14] Pad 49 pullup resistor selection. */ + } PADREGM_b; + } ; + __IM uint32_t RESERVED[3]; + + union { + __IOM uint32_t CFGA; /*!< (@ 0x00000040) GPIO Configuration Register A (Pads 0-7) */ + + struct { + __IOM uint32_t GPIO0INCFG : 1; /*!< [0..0] GPIO0 input enable. */ + __IOM uint32_t GPIO0OUTCFG : 2; /*!< [2..1] GPIO0 output configuration. */ + __IOM uint32_t GPIO0INTD : 1; /*!< [3..3] GPIO0 interrupt direction. */ + __IOM uint32_t GPIO1INCFG : 1; /*!< [4..4] GPIO1 input enable. */ + __IOM uint32_t GPIO1OUTCFG : 2; /*!< [6..5] GPIO1 output configuration. */ + __IOM uint32_t GPIO1INTD : 1; /*!< [7..7] GPIO1 interrupt direction. */ + __IOM uint32_t GPIO2INCFG : 1; /*!< [8..8] GPIO2 input enable. */ + __IOM uint32_t GPIO2OUTCFG : 2; /*!< [10..9] GPIO2 output configuration. */ + __IOM uint32_t GPIO2INTD : 1; /*!< [11..11] GPIO2 interrupt direction. */ + __IOM uint32_t GPIO3INCFG : 1; /*!< [12..12] GPIO3 input enable. */ + __IOM uint32_t GPIO3OUTCFG : 2; /*!< [14..13] GPIO3 output configuration. */ + __IOM uint32_t GPIO3INTD : 1; /*!< [15..15] GPIO3 interrupt direction. */ + __IOM uint32_t GPIO4INCFG : 1; /*!< [16..16] GPIO4 input enable. */ + __IOM uint32_t GPIO4OUTCFG : 2; /*!< [18..17] GPIO4 output configuration. */ + __IOM uint32_t GPIO4INTD : 1; /*!< [19..19] GPIO4 interrupt direction. */ + __IOM uint32_t GPIO5INCFG : 1; /*!< [20..20] GPIO5 input enable. */ + __IOM uint32_t GPIO5OUTCFG : 2; /*!< [22..21] GPIO5 output configuration. */ + __IOM uint32_t GPIO5INTD : 1; /*!< [23..23] GPIO5 interrupt direction. */ + __IOM uint32_t GPIO6INCFG : 1; /*!< [24..24] GPIO6 input enable. */ + __IOM uint32_t GPIO6OUTCFG : 2; /*!< [26..25] GPIO6 output configuration. */ + __IOM uint32_t GPIO6INTD : 1; /*!< [27..27] GPIO6 interrupt direction. */ + __IOM uint32_t GPIO7INCFG : 1; /*!< [28..28] GPIO7 input enable. */ + __IOM uint32_t GPIO7OUTCFG : 2; /*!< [30..29] GPIO7 output configuration. */ + __IOM uint32_t GPIO7INTD : 1; /*!< [31..31] GPIO7 interrupt direction, nCE polarity. */ + } CFGA_b; + } ; + + union { + __IOM uint32_t CFGB; /*!< (@ 0x00000044) GPIO Configuration Register B (Pads 8-15) */ + + struct { + __IOM uint32_t GPIO8INCFG : 1; /*!< [0..0] GPIO8 input enable. */ + __IOM uint32_t GPIO8OUTCFG : 2; /*!< [2..1] GPIO8 output configuration. */ + __IOM uint32_t GPIO8INTD : 1; /*!< [3..3] GPIO8 interrupt direction. */ + __IOM uint32_t GPIO9INCFG : 1; /*!< [4..4] GPIO9 input enable. */ + __IOM uint32_t GPIO9OUTCFG : 2; /*!< [6..5] GPIO9 output configuration. */ + __IOM uint32_t GPIO9INTD : 1; /*!< [7..7] GPIO9 interrupt direction. */ + __IOM uint32_t GPIO10INCFG : 1; /*!< [8..8] GPIO10 input enable. */ + __IOM uint32_t GPIO10OUTCFG : 2; /*!< [10..9] GPIO10 output configuration. */ + __IOM uint32_t GPIO10INTD : 1; /*!< [11..11] GPIO10 interrupt direction. */ + __IOM uint32_t GPIO11INCFG : 1; /*!< [12..12] GPIO11 input enable. */ + __IOM uint32_t GPIO11OUTCFG : 2; /*!< [14..13] GPIO11 output configuration. */ + __IOM uint32_t GPIO11INTD : 1; /*!< [15..15] GPIO11 interrupt direction. */ + __IOM uint32_t GPIO12INCFG : 1; /*!< [16..16] GPIO12 input enable. */ + __IOM uint32_t GPIO12OUTCFG : 2; /*!< [18..17] GPIO12 output configuration. */ + __IOM uint32_t GPIO12INTD : 1; /*!< [19..19] GPIO12 interrupt direction. */ + __IOM uint32_t GPIO13INCFG : 1; /*!< [20..20] GPIO13 input enable. */ + __IOM uint32_t GPIO13OUTCFG : 2; /*!< [22..21] GPIO13 output configuration. */ + __IOM uint32_t GPIO13INTD : 1; /*!< [23..23] GPIO13 interrupt direction. */ + __IOM uint32_t GPIO14INCFG : 1; /*!< [24..24] GPIO14 input enable. */ + __IOM uint32_t GPIO14OUTCFG : 2; /*!< [26..25] GPIO14 output configuration. */ + __IOM uint32_t GPIO14INTD : 1; /*!< [27..27] GPIO14 interrupt direction. */ + __IOM uint32_t GPIO15INCFG : 1; /*!< [28..28] GPIO15 input enable. */ + __IOM uint32_t GPIO15OUTCFG : 2; /*!< [30..29] GPIO15 output configuration. */ + __IOM uint32_t GPIO15INTD : 1; /*!< [31..31] GPIO15 interrupt direction. */ + } CFGB_b; + } ; + + union { + __IOM uint32_t CFGC; /*!< (@ 0x00000048) GPIO Configuration Register C (Pads 16-23) */ + + struct { + __IOM uint32_t GPIO16INCFG : 1; /*!< [0..0] GPIO16 input enable. */ + __IOM uint32_t GPIO16OUTCFG : 2; /*!< [2..1] GPIO16 output configuration. */ + __IOM uint32_t GPIO16INTD : 1; /*!< [3..3] GPIO16 interrupt direction. */ + __IOM uint32_t GPIO17INCFG : 1; /*!< [4..4] GPIO17 input enable. */ + __IOM uint32_t GPIO17OUTCFG : 2; /*!< [6..5] GPIO17 output configuration. */ + __IOM uint32_t GPIO17INTD : 1; /*!< [7..7] GPIO17 interrupt direction. */ + __IOM uint32_t GPIO18INCFG : 1; /*!< [8..8] GPIO18 input enable. */ + __IOM uint32_t GPIO18OUTCFG : 2; /*!< [10..9] GPIO18 output configuration. */ + __IOM uint32_t GPIO18INTD : 1; /*!< [11..11] GPIO18 interrupt direction. */ + __IOM uint32_t GPIO19INCFG : 1; /*!< [12..12] GPIO19 input enable. */ + __IOM uint32_t GPIO19OUTCFG : 2; /*!< [14..13] GPIO19 output configuration. */ + __IOM uint32_t GPIO19INTD : 1; /*!< [15..15] GPIO19 interrupt direction. */ + __IOM uint32_t GPIO20INCFG : 1; /*!< [16..16] GPIO20 input enable. */ + __IOM uint32_t GPIO20OUTCFG : 2; /*!< [18..17] GPIO20 output configuration. */ + __IOM uint32_t GPIO20INTD : 1; /*!< [19..19] GPIO20 interrupt direction. */ + __IOM uint32_t GPIO21INCFG : 1; /*!< [20..20] GPIO21 input enable. */ + __IOM uint32_t GPIO21OUTCFG : 2; /*!< [22..21] GPIO21 output configuration. */ + __IOM uint32_t GPIO21INTD : 1; /*!< [23..23] GPIO21 interrupt direction. */ + __IOM uint32_t GPIO22INCFG : 1; /*!< [24..24] GPIO22 input enable. */ + __IOM uint32_t GPIO22OUTCFG : 2; /*!< [26..25] GPIO22 output configuration. */ + __IOM uint32_t GPIO22INTD : 1; /*!< [27..27] GPIO22 interrupt direction. */ + __IOM uint32_t GPIO23INCFG : 1; /*!< [28..28] GPIO23 input enable. */ + __IOM uint32_t GPIO23OUTCFG : 2; /*!< [30..29] GPIO23 output configuration. */ + __IOM uint32_t GPIO23INTD : 1; /*!< [31..31] GPIO23 interrupt direction. */ + } CFGC_b; + } ; + + union { + __IOM uint32_t CFGD; /*!< (@ 0x0000004C) GPIO Configuration Register D (Pads 24-31) */ + + struct { + __IOM uint32_t GPIO24INCFG : 1; /*!< [0..0] GPIO24 input enable. */ + __IOM uint32_t GPIO24OUTCFG : 2; /*!< [2..1] GPIO24 output configuration. */ + __IOM uint32_t GPIO24INTD : 1; /*!< [3..3] GPIO24 interrupt direction. */ + __IOM uint32_t GPIO25INCFG : 1; /*!< [4..4] GPIO25 input enable. */ + __IOM uint32_t GPIO25OUTCFG : 2; /*!< [6..5] GPIO25 output configuration. */ + __IOM uint32_t GPIO25INTD : 1; /*!< [7..7] GPIO25 interrupt direction. */ + __IOM uint32_t GPIO26INCFG : 1; /*!< [8..8] GPIO26 input enable. */ + __IOM uint32_t GPIO26OUTCFG : 2; /*!< [10..9] GPIO26 output configuration. */ + __IOM uint32_t GPIO26INTD : 1; /*!< [11..11] GPIO26 interrupt direction. */ + __IOM uint32_t GPIO27INCFG : 1; /*!< [12..12] GPIO27 input enable. */ + __IOM uint32_t GPIO27OUTCFG : 2; /*!< [14..13] GPIO27 output configuration. */ + __IOM uint32_t GPIO27INTD : 1; /*!< [15..15] GPIO27 interrupt direction. */ + __IOM uint32_t GPIO28INCFG : 1; /*!< [16..16] GPIO28 input enable. */ + __IOM uint32_t GPIO28OUTCFG : 2; /*!< [18..17] GPIO28 output configuration. */ + __IOM uint32_t GPIO28INTD : 1; /*!< [19..19] GPIO28 interrupt direction. */ + __IOM uint32_t GPIO29INCFG : 1; /*!< [20..20] GPIO29 input enable. */ + __IOM uint32_t GPIO29OUTCFG : 2; /*!< [22..21] GPIO29 output configuration. */ + __IOM uint32_t GPIO29INTD : 1; /*!< [23..23] GPIO29 interrupt direction. */ + __IOM uint32_t GPIO30INCFG : 1; /*!< [24..24] GPIO30 input enable. */ + __IOM uint32_t GPIO30OUTCFG : 2; /*!< [26..25] GPIO30 output configuration. */ + __IOM uint32_t GPIO30INTD : 1; /*!< [27..27] GPIO30 interrupt direction. */ + __IOM uint32_t GPIO31INCFG : 1; /*!< [28..28] GPIO31 input enable. */ + __IOM uint32_t GPIO31OUTCFG : 2; /*!< [30..29] GPIO31 output configuration. */ + __IOM uint32_t GPIO31INTD : 1; /*!< [31..31] GPIO31 interrupt direction. */ + } CFGD_b; + } ; + + union { + __IOM uint32_t CFGE; /*!< (@ 0x00000050) GPIO Configuration Register E (Pads 32-39) */ + + struct { + __IOM uint32_t GPIO32INCFG : 1; /*!< [0..0] GPIO32 input enable. */ + __IOM uint32_t GPIO32OUTCFG : 2; /*!< [2..1] GPIO32 output configuration. */ + __IOM uint32_t GPIO32INTD : 1; /*!< [3..3] GPIO32 interrupt direction. */ + __IOM uint32_t GPIO33INCFG : 1; /*!< [4..4] GPIO33 input enable. */ + __IOM uint32_t GPIO33OUTCFG : 2; /*!< [6..5] GPIO33 output configuration. */ + __IOM uint32_t GPIO33INTD : 1; /*!< [7..7] GPIO33 interrupt direction. */ + __IOM uint32_t GPIO34INCFG : 1; /*!< [8..8] GPIO34 input enable. */ + __IOM uint32_t GPIO34OUTCFG : 2; /*!< [10..9] GPIO34 output configuration. */ + __IOM uint32_t GPIO34INTD : 1; /*!< [11..11] GPIO34 interrupt direction. */ + __IOM uint32_t GPIO35INCFG : 1; /*!< [12..12] GPIO35 input enable. */ + __IOM uint32_t GPIO35OUTCFG : 2; /*!< [14..13] GPIO35 output configuration. */ + __IOM uint32_t GPIO35INTD : 1; /*!< [15..15] GPIO35 interrupt direction. */ + __IOM uint32_t GPIO36INCFG : 1; /*!< [16..16] GPIO36 input enable. */ + __IOM uint32_t GPIO36OUTCFG : 2; /*!< [18..17] GPIO36 output configuration. */ + __IOM uint32_t GPIO36INTD : 1; /*!< [19..19] GPIO36 interrupt direction. */ + __IOM uint32_t GPIO37INCFG : 1; /*!< [20..20] GPIO37 input enable. */ + __IOM uint32_t GPIO37OUTCFG : 2; /*!< [22..21] GPIO37 output configuration. */ + __IOM uint32_t GPIO37INTD : 1; /*!< [23..23] GPIO37 interrupt direction. */ + __IOM uint32_t GPIO38INCFG : 1; /*!< [24..24] GPIO38 input enable. */ + __IOM uint32_t GPIO38OUTCFG : 2; /*!< [26..25] GPIO38 output configuration. */ + __IOM uint32_t GPIO38INTD : 1; /*!< [27..27] GPIO38 interrupt direction. */ + __IOM uint32_t GPIO39INCFG : 1; /*!< [28..28] GPIO39 input enable. */ + __IOM uint32_t GPIO39OUTCFG : 2; /*!< [30..29] GPIO39 output configuration. */ + __IOM uint32_t GPIO39INTD : 1; /*!< [31..31] GPIO39 interrupt direction. */ + } CFGE_b; + } ; + + union { + __IOM uint32_t CFGF; /*!< (@ 0x00000054) GPIO Configuration Register F (Pads 40 -47) */ + + struct { + __IOM uint32_t GPIO40INCFG : 1; /*!< [0..0] GPIO40 input enable. */ + __IOM uint32_t GPIO40OUTCFG : 2; /*!< [2..1] GPIO40 output configuration. */ + __IOM uint32_t GPIO40INTD : 1; /*!< [3..3] GPIO40 interrupt direction. */ + __IOM uint32_t GPIO41INCFG : 1; /*!< [4..4] GPIO41 input enable. */ + __IOM uint32_t GPIO41OUTCFG : 2; /*!< [6..5] GPIO41 output configuration. */ + __IOM uint32_t GPIO41INTD : 1; /*!< [7..7] GPIO41 interrupt direction. */ + __IOM uint32_t GPIO42INCFG : 1; /*!< [8..8] GPIO42 input enable. */ + __IOM uint32_t GPIO42OUTCFG : 2; /*!< [10..9] GPIO42 output configuration. */ + __IOM uint32_t GPIO42INTD : 1; /*!< [11..11] GPIO42 interrupt direction. */ + __IOM uint32_t GPIO43INCFG : 1; /*!< [12..12] GPIO43 input enable. */ + __IOM uint32_t GPIO43OUTCFG : 2; /*!< [14..13] GPIO43 output configuration. */ + __IOM uint32_t GPIO43INTD : 1; /*!< [15..15] GPIO43 interrupt direction. */ + __IOM uint32_t GPIO44INCFG : 1; /*!< [16..16] GPIO44 input enable. */ + __IOM uint32_t GPIO44OUTCFG : 2; /*!< [18..17] GPIO44 output configuration. */ + __IOM uint32_t GPIO44INTD : 1; /*!< [19..19] GPIO44 interrupt direction. */ + __IOM uint32_t GPIO45INCFG : 1; /*!< [20..20] GPIO45 input enable. */ + __IOM uint32_t GPIO45OUTCFG : 2; /*!< [22..21] GPIO45 output configuration. */ + __IOM uint32_t GPIO45INTD : 1; /*!< [23..23] GPIO45 interrupt direction. */ + __IOM uint32_t GPIO46INCFG : 1; /*!< [24..24] GPIO46 input enable. */ + __IOM uint32_t GPIO46OUTCFG : 2; /*!< [26..25] GPIO46 output configuration. */ + __IOM uint32_t GPIO46INTD : 1; /*!< [27..27] GPIO46 interrupt direction. */ + __IOM uint32_t GPIO47INCFG : 1; /*!< [28..28] GPIO47 input enable. */ + __IOM uint32_t GPIO47OUTCFG : 2; /*!< [30..29] GPIO47 output configuration. */ + __IOM uint32_t GPIO47INTD : 1; /*!< [31..31] GPIO47 interrupt direction. */ + } CFGF_b; + } ; + + union { + __IOM uint32_t CFGG; /*!< (@ 0x00000058) GPIO Configuration Register G (Pads 48-49) */ + + struct { + __IOM uint32_t GPIO48INCFG : 1; /*!< [0..0] GPIO48 input enable. */ + __IOM uint32_t GPIO48OUTCFG : 2; /*!< [2..1] GPIO48 output configuration. */ + __IOM uint32_t GPIO48INTD : 1; /*!< [3..3] GPIO48 interrupt direction. */ + __IOM uint32_t GPIO49INCFG : 1; /*!< [4..4] GPIO49 input enable. */ + __IOM uint32_t GPIO49OUTCFG : 2; /*!< [6..5] GPIO49 output configuration. */ + __IOM uint32_t GPIO49INTD : 1; /*!< [7..7] GPIO49 interrupt direction. */ + } CFGG_b; + } ; + __IM uint32_t RESERVED1; + + union { + __IOM uint32_t PADKEY; /*!< (@ 0x00000060) Key Register for all pad configuration registers */ + + struct { + __IOM uint32_t PADKEY : 32; /*!< [31..0] Key register value. */ + } PADKEY_b; + } ; + __IM uint32_t RESERVED2[7]; + + union { + __IOM uint32_t RDA; /*!< (@ 0x00000080) GPIO Input Register A */ + + struct { + __IOM uint32_t RDA : 32; /*!< [31..0] GPIO31-0 read data. */ + } RDA_b; + } ; + + union { + __IOM uint32_t RDB; /*!< (@ 0x00000084) GPIO Input Register B */ + + struct { + __IOM uint32_t RDB : 18; /*!< [17..0] GPIO49-32 read data. */ + } RDB_b; + } ; + + union { + __IOM uint32_t WTA; /*!< (@ 0x00000088) GPIO Output Register A */ + + struct { + __IOM uint32_t WTA : 32; /*!< [31..0] GPIO31-0 write data. */ + } WTA_b; + } ; + + union { + __IOM uint32_t WTB; /*!< (@ 0x0000008C) GPIO Output Register B */ + + struct { + __IOM uint32_t WTB : 18; /*!< [17..0] GPIO49-32 write data. */ + } WTB_b; + } ; + + union { + __IOM uint32_t WTSA; /*!< (@ 0x00000090) GPIO Output Register A Set */ + + struct { + __IOM uint32_t WTSA : 32; /*!< [31..0] Set the GPIO31-0 write data. */ + } WTSA_b; + } ; + + union { + __IOM uint32_t WTSB; /*!< (@ 0x00000094) GPIO Output Register B Set */ + + struct { + __IOM uint32_t WTSB : 18; /*!< [17..0] Set the GPIO49-32 write data. */ + } WTSB_b; + } ; + + union { + __IOM uint32_t WTCA; /*!< (@ 0x00000098) GPIO Output Register A Clear */ + + struct { + __IOM uint32_t WTCA : 32; /*!< [31..0] Clear the GPIO31-0 write data. */ + } WTCA_b; + } ; + + union { + __IOM uint32_t WTCB; /*!< (@ 0x0000009C) GPIO Output Register B Clear */ + + struct { + __IOM uint32_t WTCB : 18; /*!< [17..0] Clear the GPIO49-32 write data. */ + } WTCB_b; + } ; + + union { + __IOM uint32_t ENA; /*!< (@ 0x000000A0) GPIO Enable Register A */ + + struct { + __IOM uint32_t ENA : 32; /*!< [31..0] GPIO31-0 output enables */ + } ENA_b; + } ; + + union { + __IOM uint32_t ENB; /*!< (@ 0x000000A4) GPIO Enable Register B */ + + struct { + __IOM uint32_t ENB : 18; /*!< [17..0] GPIO49-32 output enables */ + } ENB_b; + } ; + + union { + __IOM uint32_t ENSA; /*!< (@ 0x000000A8) GPIO Enable Register A Set */ + + struct { + __IOM uint32_t ENSA : 32; /*!< [31..0] Set the GPIO31-0 output enables */ + } ENSA_b; + } ; + + union { + __IOM uint32_t ENSB; /*!< (@ 0x000000AC) GPIO Enable Register B Set */ + + struct { + __IOM uint32_t ENSB : 18; /*!< [17..0] Set the GPIO49-32 output enables */ + } ENSB_b; + } ; + __IM uint32_t RESERVED3; + + union { + __IOM uint32_t ENCA; /*!< (@ 0x000000B4) GPIO Enable Register A Clear */ + + struct { + __IOM uint32_t ENCA : 32; /*!< [31..0] Clear the GPIO31-0 output enables */ + } ENCA_b; + } ; + + union { + __IOM uint32_t ENCB; /*!< (@ 0x000000B8) GPIO Enable Register B Clear */ + + struct { + __IOM uint32_t ENCB : 18; /*!< [17..0] Clear the GPIO49-32 output enables */ + } ENCB_b; + } ; + + union { + __IOM uint32_t STMRCAP; /*!< (@ 0x000000BC) STIMER Capture Control */ + + struct { + __IOM uint32_t STSEL0 : 6; /*!< [5..0] STIMER Capture 0 Select. */ + __IOM uint32_t STPOL0 : 1; /*!< [6..6] STIMER Capture 0 Polarity. */ + __IM uint32_t : 1; + __IOM uint32_t STSEL1 : 6; /*!< [13..8] STIMER Capture 1 Select. */ + __IOM uint32_t STPOL1 : 1; /*!< [14..14] STIMER Capture 1 Polarity. */ + __IM uint32_t : 1; + __IOM uint32_t STSEL2 : 6; /*!< [21..16] STIMER Capture 2 Select. */ + __IOM uint32_t STPOL2 : 1; /*!< [22..22] STIMER Capture 2 Polarity. */ + __IM uint32_t : 1; + __IOM uint32_t STSEL3 : 6; /*!< [29..24] STIMER Capture 3 Select. */ + __IOM uint32_t STPOL3 : 1; /*!< [30..30] STIMER Capture 3 Polarity. */ + } STMRCAP_b; + } ; + + union { + __IOM uint32_t IOM0IRQ; /*!< (@ 0x000000C0) IOM0 Flow Control IRQ Select */ + + struct { + __IOM uint32_t IOM0IRQ : 6; /*!< [5..0] IOMSTR0 IRQ pad select. */ + } IOM0IRQ_b; + } ; + + union { + __IOM uint32_t IOM1IRQ; /*!< (@ 0x000000C4) IOM1 Flow Control IRQ Select */ + + struct { + __IOM uint32_t IOM1IRQ : 6; /*!< [5..0] IOMSTR1 IRQ pad select. */ + } IOM1IRQ_b; + } ; + + union { + __IOM uint32_t IOM2IRQ; /*!< (@ 0x000000C8) IOM2 Flow Control IRQ Select */ + + struct { + __IOM uint32_t IOM2IRQ : 6; /*!< [5..0] IOMSTR2 IRQ pad select. */ + } IOM2IRQ_b; + } ; + + union { + __IOM uint32_t IOM3IRQ; /*!< (@ 0x000000CC) IOM3 Flow Control IRQ Select */ + + struct { + __IOM uint32_t IOM3IRQ : 6; /*!< [5..0] IOMSTR3 IRQ pad select. */ + } IOM3IRQ_b; + } ; + + union { + __IOM uint32_t IOM4IRQ; /*!< (@ 0x000000D0) IOM4 Flow Control IRQ Select */ + + struct { + __IOM uint32_t IOM4IRQ : 6; /*!< [5..0] IOMSTR4 IRQ pad select. */ + } IOM4IRQ_b; + } ; + + union { + __IOM uint32_t IOM5IRQ; /*!< (@ 0x000000D4) IOM5 Flow Control IRQ Select */ + + struct { + __IOM uint32_t IOM5IRQ : 6; /*!< [5..0] IOMSTR5 IRQ pad select. */ + } IOM5IRQ_b; + } ; + + union { + __IOM uint32_t BLEIFIRQ; /*!< (@ 0x000000D8) BLEIF Flow Control IRQ Select */ + + struct { + __IOM uint32_t BLEIFIRQ : 6; /*!< [5..0] BLEIF IRQ pad select. */ + } BLEIFIRQ_b; + } ; + + union { + __IOM uint32_t GPIOOBS; /*!< (@ 0x000000DC) GPIO Observation Mode Sample register */ + + struct { + __IOM uint32_t OBS_DATA : 16; /*!< [15..0] Sample of the data output on the GPIO observation port. + May have async sampling issues, as the data is not synronized + to the read operation. Intended for debug purposes only */ + } GPIOOBS_b; + } ; + + union { + __IOM uint32_t ALTPADCFGA; /*!< (@ 0x000000E0) Alternate Pad Configuration reg0 (Pads 3,2,1,0) */ + + struct { + __IOM uint32_t PAD0_DS1 : 1; /*!< [0..0] Pad 0 high order drive strength selection. Used in conjunction + with PAD0STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD0_SR : 1; /*!< [4..4] Pad 0 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD1_DS1 : 1; /*!< [8..8] Pad 1 high order drive strength selection. Used in conjunction + with PAD1STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD1_SR : 1; /*!< [12..12] Pad 1 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD2_DS1 : 1; /*!< [16..16] Pad 2 high order drive strength selection. Used in + conjunction with PAD2STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD2_SR : 1; /*!< [20..20] Pad 2 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD3_DS1 : 1; /*!< [24..24] Pad 3 high order drive strength selection. Used in + conjunction with PAD3STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD3_SR : 1; /*!< [28..28] Pad 3 slew rate selection. */ + } ALTPADCFGA_b; + } ; + + union { + __IOM uint32_t ALTPADCFGB; /*!< (@ 0x000000E4) Alternate Pad Configuration reg1 (Pads 7,6,5,4) */ + + struct { + __IOM uint32_t PAD4_DS1 : 1; /*!< [0..0] Pad 4 high order drive strength selection. Used in conjunction + with PAD4STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD4_SR : 1; /*!< [4..4] Pad 4 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD5_DS1 : 1; /*!< [8..8] Pad 5 high order drive strength selection. Used in conjunction + with PAD5STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD5_SR : 1; /*!< [12..12] Pad 5 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD6_DS1 : 1; /*!< [16..16] Pad 6 high order drive strength selection. Used in + conjunction with PAD6STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD6_SR : 1; /*!< [20..20] Pad 6 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD7_DS1 : 1; /*!< [24..24] Pad 7 high order drive strength selection. Used in + conjunction with PAD7STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD7_SR : 1; /*!< [28..28] Pad 7 slew rate selection. */ + } ALTPADCFGB_b; + } ; + + union { + __IOM uint32_t ALTPADCFGC; /*!< (@ 0x000000E8) Alternate Pad Configuration reg2 (Pads 11,10,9,8) */ + + struct { + __IOM uint32_t PAD8_DS1 : 1; /*!< [0..0] Pad 8 high order drive strength selection. Used in conjunction + with PAD8STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD8_SR : 1; /*!< [4..4] Pad 8 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD9_DS1 : 1; /*!< [8..8] Pad 9 high order drive strength selection. Used in conjunction + with PAD9STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD9_SR : 1; /*!< [12..12] Pad 9 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD10_DS1 : 1; /*!< [16..16] Pad 10 high order drive strength selection. Used in + conjunction with PAD10STRNG field to set the pad drive + strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD10_SR : 1; /*!< [20..20] Pad 10 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD11_DS1 : 1; /*!< [24..24] Pad 11 high order drive strength selection. Used in + conjunction with PAD11STRNG field to set the pad drive + strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD11_SR : 1; /*!< [28..28] Pad 11 slew rate selection. */ + } ALTPADCFGC_b; + } ; + + union { + __IOM uint32_t ALTPADCFGD; /*!< (@ 0x000000EC) Alternate Pad Configuration reg3 (Pads 15,14,13,12) */ + + struct { + __IOM uint32_t PAD12_DS1 : 1; /*!< [0..0] Pad 12 high order drive strength selection. Used in conjunction + with PAD12STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD12_SR : 1; /*!< [4..4] Pad 12 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD13_DS1 : 1; /*!< [8..8] Pad 13 high order drive strength selection. Used in conjunction + with PAD13STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD13_SR : 1; /*!< [12..12] Pad 13 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD14_DS1 : 1; /*!< [16..16] Pad 14 high order drive strength selection. Used in + conjunction with PAD14STRNG field to set the pad drive + strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD14_SR : 1; /*!< [20..20] Pad 14 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD15_DS1 : 1; /*!< [24..24] Pad 15 high order drive strength selection. Used in + conjunction with PAD15STRNG field to set the pad drive + strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD15_SR : 1; /*!< [28..28] Pad 15 slew rate selection. */ + } ALTPADCFGD_b; + } ; + + union { + __IOM uint32_t ALTPADCFGE; /*!< (@ 0x000000F0) Alternate Pad Configuration reg4 (Pads 19,18,17,16) */ + + struct { + __IOM uint32_t PAD16_DS1 : 1; /*!< [0..0] Pad 16 high order drive strength selection. Used in conjunction + with PAD16STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD16_SR : 1; /*!< [4..4] Pad 16 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD17_DS1 : 1; /*!< [8..8] Pad 17 high order drive strength selection. Used in conjunction + with PAD17STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD17_SR : 1; /*!< [12..12] Pad 17 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD18_DS1 : 1; /*!< [16..16] Pad 18 high order drive strength selection. Used in + conjunction with PAD18STRNG field to set the pad drive + strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD18_SR : 1; /*!< [20..20] Pad 18 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD19_DS1 : 1; /*!< [24..24] Pad 19 high order drive strength selection. Used in + conjunction with PAD19STRNG field to set the pad drive + strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD19_SR : 1; /*!< [28..28] Pad 19 slew rate selection. */ + } ALTPADCFGE_b; + } ; + + union { + __IOM uint32_t ALTPADCFGF; /*!< (@ 0x000000F4) Alternate Pad Configuration reg5 (Pads 23,22,21,20) */ + + struct { + __IOM uint32_t PAD20_DS1 : 1; /*!< [0..0] Pad 20 high order drive strength selection. Used in conjunction + with PAD20STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD20_SR : 1; /*!< [4..4] Pad 20 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD21_DS1 : 1; /*!< [8..8] Pad 21 high order drive strength selection. Used in conjunction + with PAD21STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD21_SR : 1; /*!< [12..12] Pad 21 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD22_DS1 : 1; /*!< [16..16] Pad 22 high order drive strength selection. Used in + conjunction with PAD22STRNG field to set the pad drive + strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD22_SR : 1; /*!< [20..20] Pad 22 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD23_DS1 : 1; /*!< [24..24] Pad 23 high order drive strength selection. Used in + conjunction with PAD23STRNG field to set the pad drive + strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD23_SR : 1; /*!< [28..28] Pad 23 slew rate selection. */ + } ALTPADCFGF_b; + } ; + + union { + __IOM uint32_t ALTPADCFGG; /*!< (@ 0x000000F8) Alternate Pad Configuration reg6 (Pads 27,26,25,24) */ + + struct { + __IOM uint32_t PAD24_DS1 : 1; /*!< [0..0] Pad 24 high order drive strength selection. Used in conjunction + with PAD24STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD24_SR : 1; /*!< [4..4] Pad 24 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD25_DS1 : 1; /*!< [8..8] Pad 25 high order drive strength selection. Used in conjunction + with PAD25STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD25_SR : 1; /*!< [12..12] Pad 25 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD26_DS1 : 1; /*!< [16..16] Pad 26 high order drive strength selection. Used in + conjunction with PAD26STRNG field to set the pad drive + strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD26_SR : 1; /*!< [20..20] Pad 26 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD27_DS1 : 1; /*!< [24..24] Pad 27 high order drive strength selection. Used in + conjunction with PAD27STRNG field to set the pad drive + strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD27_SR : 1; /*!< [28..28] Pad 27 slew rate selection. */ + } ALTPADCFGG_b; + } ; + + union { + __IOM uint32_t ALTPADCFGH; /*!< (@ 0x000000FC) Alternate Pad Configuration reg7 (Pads 31,30,29,28) */ + + struct { + __IOM uint32_t PAD28_DS1 : 1; /*!< [0..0] Pad 28 high order drive strength selection. Used in conjunction + with PAD28STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD28_SR : 1; /*!< [4..4] Pad 28 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD29_DS1 : 1; /*!< [8..8] Pad 29 high order drive strength selection. Used in conjunction + with PAD29STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD29_SR : 1; /*!< [12..12] Pad 29 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD30_DS1 : 1; /*!< [16..16] Pad 30 high order drive strength selection. Used in + conjunction with PAD30STRNG field to set the pad drive + strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD30_SR : 1; /*!< [20..20] Pad 30 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD31_DS1 : 1; /*!< [24..24] Pad 31 high order drive strength selection. Used in + conjunction with PAD31STRNG field to set the pad drive + strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD31_SR : 1; /*!< [28..28] Pad 31 slew rate selection. */ + } ALTPADCFGH_b; + } ; + + union { + __IOM uint32_t ALTPADCFGI; /*!< (@ 0x00000100) Alternate Pad Configuration reg8 (Pads 35,34,33,32) */ + + struct { + __IOM uint32_t PAD32_DS1 : 1; /*!< [0..0] Pad 32 high order drive strength selection. Used in conjunction + with PAD32STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD32_SR : 1; /*!< [4..4] Pad 32 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD33_DS1 : 1; /*!< [8..8] Pad 33 high order drive strength selection. Used in conjunction + with PAD33STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD33_SR : 1; /*!< [12..12] Pad 33 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD34_DS1 : 1; /*!< [16..16] Pad 34 high order drive strength selection. Used in + conjunction with PAD34STRNG field to set the pad drive + strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD34_SR : 1; /*!< [20..20] Pad 34 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD35_DS1 : 1; /*!< [24..24] Pad 35 high order drive strength selection. Used in + conjunction with PAD35STRNG field to set the pad drive + strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD35_SR : 1; /*!< [28..28] Pad 35 slew rate selection. */ + } ALTPADCFGI_b; + } ; + + union { + __IOM uint32_t ALTPADCFGJ; /*!< (@ 0x00000104) Alternate Pad Configuration reg9 (Pads 39,38,37,36) */ + + struct { + __IOM uint32_t PAD36_DS1 : 1; /*!< [0..0] Pad 36 high order drive strength selection. Used in conjunction + with PAD36STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD36_SR : 1; /*!< [4..4] Pad 36 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD37_DS1 : 1; /*!< [8..8] Pad 37 high order drive strength selection. Used in conjunction + with PAD37STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD37_SR : 1; /*!< [12..12] Pad 37 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD38_DS1 : 1; /*!< [16..16] Pad 38 high order drive strength selection. Used in + conjunction with PAD38STRNG field to set the pad drive + strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD38_SR : 1; /*!< [20..20] Pad 38 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD39_DS1 : 1; /*!< [24..24] Pad 39 high order drive strength selection. Used in + conjunction with PAD39STRNG field to set the pad drive + strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD39_SR : 1; /*!< [28..28] Pad 39 slew rate selection. */ + } ALTPADCFGJ_b; + } ; + + union { + __IOM uint32_t ALTPADCFGK; /*!< (@ 0x00000108) Alternate Pad Configuration reg10 (Pads 43,42,41,40) */ + + struct { + __IOM uint32_t PAD40_DS1 : 1; /*!< [0..0] Pad 40 high order drive strength selection. Used in conjunction + with PAD40STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD40_SR : 1; /*!< [4..4] Pad 40 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD41_DS1 : 1; /*!< [8..8] Pad 41 high order drive strength selection. Used in conjunction + with PAD41STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD41_SR : 1; /*!< [12..12] Pad 41 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD42_DS1 : 1; /*!< [16..16] Pad 42 high order drive strength selection. Used in + conjunction with PAD42STRNG field to set the pad drive + strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD42_SR : 1; /*!< [20..20] Pad 42 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD43_DS1 : 1; /*!< [24..24] Pad 43 high order drive strength selection. Used in + conjunction with PAD43STRNG field to set the pad drive + strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD43_SR : 1; /*!< [28..28] Pad 43 slew rate selection. */ + } ALTPADCFGK_b; + } ; + + union { + __IOM uint32_t ALTPADCFGL; /*!< (@ 0x0000010C) Alternate Pad Configuration reg11 (Pads 47,46,45,44) */ + + struct { + __IOM uint32_t PAD44_DS1 : 1; /*!< [0..0] Pad 44 high order drive strength selection. Used in conjunction + with PAD44STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD44_SR : 1; /*!< [4..4] Pad 44 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD45_DS1 : 1; /*!< [8..8] Pad 45 high order drive strength selection. Used in conjunction + with PAD45STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD45_SR : 1; /*!< [12..12] Pad 45 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD46_DS1 : 1; /*!< [16..16] Pad 46 high order drive strength selection. Used in + conjunction with PAD46STRNG field to set the pad drive + strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD46_SR : 1; /*!< [20..20] Pad 46 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD47_DS1 : 1; /*!< [24..24] Pad 47 high order drive strength selection. Used in + conjunction with PAD47STRNG field to set the pad drive + strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD47_SR : 1; /*!< [28..28] Pad 47 slew rate selection. */ + } ALTPADCFGL_b; + } ; + + union { + __IOM uint32_t ALTPADCFGM; /*!< (@ 0x00000110) Alternate Pad Configuration reg12 (Pads 49,48) */ + + struct { + __IOM uint32_t PAD48_DS1 : 1; /*!< [0..0] Pad 48 high order drive strength selection. Used in conjunction + with PAD48STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD48_SR : 1; /*!< [4..4] Pad 48 slew rate selection. */ + __IM uint32_t : 3; + __IOM uint32_t PAD49_DS1 : 1; /*!< [8..8] Pad 49 high order drive strength selection. Used in conjunction + with PAD49STRNG field to set the pad drive strength. */ + __IM uint32_t : 3; + __IOM uint32_t PAD49_SR : 1; /*!< [12..12] Pad 49 slew rate selection. */ + } ALTPADCFGM_b; + } ; + + union { + __IOM uint32_t SCDET; /*!< (@ 0x00000114) SCARD Card Detect select */ + + struct { + __IOM uint32_t SCDET : 6; /*!< [5..0] SCARD card detect pad select. */ + } SCDET_b; + } ; + + union { + __IOM uint32_t CTENCFG; /*!< (@ 0x00000118) Counter/Timer Enable Config */ + + struct { + __IOM uint32_t EN0 : 1; /*!< [0..0] CT0 Enable */ + __IOM uint32_t EN1 : 1; /*!< [1..1] CT1 Enable */ + __IOM uint32_t EN2 : 1; /*!< [2..2] CT2 Enable */ + __IOM uint32_t EN3 : 1; /*!< [3..3] CT3 Enable */ + __IOM uint32_t EN4 : 1; /*!< [4..4] CT4 Enable */ + __IOM uint32_t EN5 : 1; /*!< [5..5] CT5 Enable */ + __IOM uint32_t EN6 : 1; /*!< [6..6] CT6 Enable */ + __IOM uint32_t EN7 : 1; /*!< [7..7] CT7 Enable */ + __IOM uint32_t EN8 : 1; /*!< [8..8] CT8 Enable */ + __IOM uint32_t EN9 : 1; /*!< [9..9] CT9 Enable */ + __IOM uint32_t EN10 : 1; /*!< [10..10] CT10 Enable */ + __IOM uint32_t EN11 : 1; /*!< [11..11] CT11 Enable */ + __IOM uint32_t EN12 : 1; /*!< [12..12] CT12 Enable */ + __IOM uint32_t EN13 : 1; /*!< [13..13] CT13 Enable */ + __IOM uint32_t EN14 : 1; /*!< [14..14] CT14 Enable */ + __IOM uint32_t EN15 : 1; /*!< [15..15] CT15 Enable */ + __IOM uint32_t EN16 : 1; /*!< [16..16] CT16 Enable */ + __IOM uint32_t EN17 : 1; /*!< [17..17] CT17 Enable */ + __IOM uint32_t EN18 : 1; /*!< [18..18] CT18 Enable */ + __IOM uint32_t EN19 : 1; /*!< [19..19] CT19 Enable */ + __IOM uint32_t EN20 : 1; /*!< [20..20] CT20 Enable */ + __IOM uint32_t EN21 : 1; /*!< [21..21] CT21 Enable */ + __IOM uint32_t EN22 : 1; /*!< [22..22] CT22 Enable */ + __IOM uint32_t EN23 : 1; /*!< [23..23] CT23 Enable */ + __IOM uint32_t EN24 : 1; /*!< [24..24] CT24 Enable */ + __IOM uint32_t EN25 : 1; /*!< [25..25] CT25 Enable */ + __IOM uint32_t EN26 : 1; /*!< [26..26] CT26 Enable */ + __IOM uint32_t EN27 : 1; /*!< [27..27] CT27 Enable */ + __IOM uint32_t EN28 : 1; /*!< [28..28] CT28 Enable */ + __IOM uint32_t EN29 : 1; /*!< [29..29] CT29 Enable */ + __IOM uint32_t EN30 : 1; /*!< [30..30] CT30 Enable */ + __IOM uint32_t EN31 : 1; /*!< [31..31] CT31 Enable */ + } CTENCFG_b; + } ; + __IM uint32_t RESERVED4[57]; + + union { + __IOM uint32_t INT0EN; /*!< (@ 0x00000200) GPIO Interrupt Registers 31-0: Enable */ + + struct { + __IOM uint32_t GPIO0 : 1; /*!< [0..0] GPIO0 interrupt. */ + __IOM uint32_t GPIO1 : 1; /*!< [1..1] GPIO1 interrupt. */ + __IOM uint32_t GPIO2 : 1; /*!< [2..2] GPIO2 interrupt. */ + __IOM uint32_t GPIO3 : 1; /*!< [3..3] GPIO3 interrupt. */ + __IOM uint32_t GPIO4 : 1; /*!< [4..4] GPIO4 interrupt. */ + __IOM uint32_t GPIO5 : 1; /*!< [5..5] GPIO5 interrupt. */ + __IOM uint32_t GPIO6 : 1; /*!< [6..6] GPIO6 interrupt. */ + __IOM uint32_t GPIO7 : 1; /*!< [7..7] GPIO7 interrupt. */ + __IOM uint32_t GPIO8 : 1; /*!< [8..8] GPIO8 interrupt. */ + __IOM uint32_t GPIO9 : 1; /*!< [9..9] GPIO9 interrupt. */ + __IOM uint32_t GPIO10 : 1; /*!< [10..10] GPIO10 interrupt. */ + __IOM uint32_t GPIO11 : 1; /*!< [11..11] GPIO11 interrupt. */ + __IOM uint32_t GPIO12 : 1; /*!< [12..12] GPIO12 interrupt. */ + __IOM uint32_t GPIO13 : 1; /*!< [13..13] GPIO13 interrupt. */ + __IOM uint32_t GPIO14 : 1; /*!< [14..14] GPIO14 interrupt. */ + __IOM uint32_t GPIO15 : 1; /*!< [15..15] GPIO15 interrupt. */ + __IOM uint32_t GPIO16 : 1; /*!< [16..16] GPIO16 interrupt. */ + __IOM uint32_t GPIO17 : 1; /*!< [17..17] GPIO17 interrupt. */ + __IOM uint32_t GPIO18 : 1; /*!< [18..18] GPIO18interrupt. */ + __IOM uint32_t GPIO19 : 1; /*!< [19..19] GPIO19 interrupt. */ + __IOM uint32_t GPIO20 : 1; /*!< [20..20] GPIO20 interrupt. */ + __IOM uint32_t GPIO21 : 1; /*!< [21..21] GPIO21 interrupt. */ + __IOM uint32_t GPIO22 : 1; /*!< [22..22] GPIO22 interrupt. */ + __IOM uint32_t GPIO23 : 1; /*!< [23..23] GPIO23 interrupt. */ + __IOM uint32_t GPIO24 : 1; /*!< [24..24] GPIO24 interrupt. */ + __IOM uint32_t GPIO25 : 1; /*!< [25..25] GPIO25 interrupt. */ + __IOM uint32_t GPIO26 : 1; /*!< [26..26] GPIO26 interrupt. */ + __IOM uint32_t GPIO27 : 1; /*!< [27..27] GPIO27 interrupt. */ + __IOM uint32_t GPIO28 : 1; /*!< [28..28] GPIO28 interrupt. */ + __IOM uint32_t GPIO29 : 1; /*!< [29..29] GPIO29 interrupt. */ + __IOM uint32_t GPIO30 : 1; /*!< [30..30] GPIO30 interrupt. */ + __IOM uint32_t GPIO31 : 1; /*!< [31..31] GPIO31 interrupt. */ + } INT0EN_b; + } ; + + union { + __IOM uint32_t INT0STAT; /*!< (@ 0x00000204) GPIO Interrupt Registers 31-0: Status */ + + struct { + __IOM uint32_t GPIO0 : 1; /*!< [0..0] GPIO0 interrupt. */ + __IOM uint32_t GPIO1 : 1; /*!< [1..1] GPIO1 interrupt. */ + __IOM uint32_t GPIO2 : 1; /*!< [2..2] GPIO2 interrupt. */ + __IOM uint32_t GPIO3 : 1; /*!< [3..3] GPIO3 interrupt. */ + __IOM uint32_t GPIO4 : 1; /*!< [4..4] GPIO4 interrupt. */ + __IOM uint32_t GPIO5 : 1; /*!< [5..5] GPIO5 interrupt. */ + __IOM uint32_t GPIO6 : 1; /*!< [6..6] GPIO6 interrupt. */ + __IOM uint32_t GPIO7 : 1; /*!< [7..7] GPIO7 interrupt. */ + __IOM uint32_t GPIO8 : 1; /*!< [8..8] GPIO8 interrupt. */ + __IOM uint32_t GPIO9 : 1; /*!< [9..9] GPIO9 interrupt. */ + __IOM uint32_t GPIO10 : 1; /*!< [10..10] GPIO10 interrupt. */ + __IOM uint32_t GPIO11 : 1; /*!< [11..11] GPIO11 interrupt. */ + __IOM uint32_t GPIO12 : 1; /*!< [12..12] GPIO12 interrupt. */ + __IOM uint32_t GPIO13 : 1; /*!< [13..13] GPIO13 interrupt. */ + __IOM uint32_t GPIO14 : 1; /*!< [14..14] GPIO14 interrupt. */ + __IOM uint32_t GPIO15 : 1; /*!< [15..15] GPIO15 interrupt. */ + __IOM uint32_t GPIO16 : 1; /*!< [16..16] GPIO16 interrupt. */ + __IOM uint32_t GPIO17 : 1; /*!< [17..17] GPIO17 interrupt. */ + __IOM uint32_t GPIO18 : 1; /*!< [18..18] GPIO18interrupt. */ + __IOM uint32_t GPIO19 : 1; /*!< [19..19] GPIO19 interrupt. */ + __IOM uint32_t GPIO20 : 1; /*!< [20..20] GPIO20 interrupt. */ + __IOM uint32_t GPIO21 : 1; /*!< [21..21] GPIO21 interrupt. */ + __IOM uint32_t GPIO22 : 1; /*!< [22..22] GPIO22 interrupt. */ + __IOM uint32_t GPIO23 : 1; /*!< [23..23] GPIO23 interrupt. */ + __IOM uint32_t GPIO24 : 1; /*!< [24..24] GPIO24 interrupt. */ + __IOM uint32_t GPIO25 : 1; /*!< [25..25] GPIO25 interrupt. */ + __IOM uint32_t GPIO26 : 1; /*!< [26..26] GPIO26 interrupt. */ + __IOM uint32_t GPIO27 : 1; /*!< [27..27] GPIO27 interrupt. */ + __IOM uint32_t GPIO28 : 1; /*!< [28..28] GPIO28 interrupt. */ + __IOM uint32_t GPIO29 : 1; /*!< [29..29] GPIO29 interrupt. */ + __IOM uint32_t GPIO30 : 1; /*!< [30..30] GPIO30 interrupt. */ + __IOM uint32_t GPIO31 : 1; /*!< [31..31] GPIO31 interrupt. */ + } INT0STAT_b; + } ; + + union { + __IOM uint32_t INT0CLR; /*!< (@ 0x00000208) GPIO Interrupt Registers 31-0: Clear */ + + struct { + __IOM uint32_t GPIO0 : 1; /*!< [0..0] GPIO0 interrupt. */ + __IOM uint32_t GPIO1 : 1; /*!< [1..1] GPIO1 interrupt. */ + __IOM uint32_t GPIO2 : 1; /*!< [2..2] GPIO2 interrupt. */ + __IOM uint32_t GPIO3 : 1; /*!< [3..3] GPIO3 interrupt. */ + __IOM uint32_t GPIO4 : 1; /*!< [4..4] GPIO4 interrupt. */ + __IOM uint32_t GPIO5 : 1; /*!< [5..5] GPIO5 interrupt. */ + __IOM uint32_t GPIO6 : 1; /*!< [6..6] GPIO6 interrupt. */ + __IOM uint32_t GPIO7 : 1; /*!< [7..7] GPIO7 interrupt. */ + __IOM uint32_t GPIO8 : 1; /*!< [8..8] GPIO8 interrupt. */ + __IOM uint32_t GPIO9 : 1; /*!< [9..9] GPIO9 interrupt. */ + __IOM uint32_t GPIO10 : 1; /*!< [10..10] GPIO10 interrupt. */ + __IOM uint32_t GPIO11 : 1; /*!< [11..11] GPIO11 interrupt. */ + __IOM uint32_t GPIO12 : 1; /*!< [12..12] GPIO12 interrupt. */ + __IOM uint32_t GPIO13 : 1; /*!< [13..13] GPIO13 interrupt. */ + __IOM uint32_t GPIO14 : 1; /*!< [14..14] GPIO14 interrupt. */ + __IOM uint32_t GPIO15 : 1; /*!< [15..15] GPIO15 interrupt. */ + __IOM uint32_t GPIO16 : 1; /*!< [16..16] GPIO16 interrupt. */ + __IOM uint32_t GPIO17 : 1; /*!< [17..17] GPIO17 interrupt. */ + __IOM uint32_t GPIO18 : 1; /*!< [18..18] GPIO18interrupt. */ + __IOM uint32_t GPIO19 : 1; /*!< [19..19] GPIO19 interrupt. */ + __IOM uint32_t GPIO20 : 1; /*!< [20..20] GPIO20 interrupt. */ + __IOM uint32_t GPIO21 : 1; /*!< [21..21] GPIO21 interrupt. */ + __IOM uint32_t GPIO22 : 1; /*!< [22..22] GPIO22 interrupt. */ + __IOM uint32_t GPIO23 : 1; /*!< [23..23] GPIO23 interrupt. */ + __IOM uint32_t GPIO24 : 1; /*!< [24..24] GPIO24 interrupt. */ + __IOM uint32_t GPIO25 : 1; /*!< [25..25] GPIO25 interrupt. */ + __IOM uint32_t GPIO26 : 1; /*!< [26..26] GPIO26 interrupt. */ + __IOM uint32_t GPIO27 : 1; /*!< [27..27] GPIO27 interrupt. */ + __IOM uint32_t GPIO28 : 1; /*!< [28..28] GPIO28 interrupt. */ + __IOM uint32_t GPIO29 : 1; /*!< [29..29] GPIO29 interrupt. */ + __IOM uint32_t GPIO30 : 1; /*!< [30..30] GPIO30 interrupt. */ + __IOM uint32_t GPIO31 : 1; /*!< [31..31] GPIO31 interrupt. */ + } INT0CLR_b; + } ; + + union { + __IOM uint32_t INT0SET; /*!< (@ 0x0000020C) GPIO Interrupt Registers 31-0: Set */ + + struct { + __IOM uint32_t GPIO0 : 1; /*!< [0..0] GPIO0 interrupt. */ + __IOM uint32_t GPIO1 : 1; /*!< [1..1] GPIO1 interrupt. */ + __IOM uint32_t GPIO2 : 1; /*!< [2..2] GPIO2 interrupt. */ + __IOM uint32_t GPIO3 : 1; /*!< [3..3] GPIO3 interrupt. */ + __IOM uint32_t GPIO4 : 1; /*!< [4..4] GPIO4 interrupt. */ + __IOM uint32_t GPIO5 : 1; /*!< [5..5] GPIO5 interrupt. */ + __IOM uint32_t GPIO6 : 1; /*!< [6..6] GPIO6 interrupt. */ + __IOM uint32_t GPIO7 : 1; /*!< [7..7] GPIO7 interrupt. */ + __IOM uint32_t GPIO8 : 1; /*!< [8..8] GPIO8 interrupt. */ + __IOM uint32_t GPIO9 : 1; /*!< [9..9] GPIO9 interrupt. */ + __IOM uint32_t GPIO10 : 1; /*!< [10..10] GPIO10 interrupt. */ + __IOM uint32_t GPIO11 : 1; /*!< [11..11] GPIO11 interrupt. */ + __IOM uint32_t GPIO12 : 1; /*!< [12..12] GPIO12 interrupt. */ + __IOM uint32_t GPIO13 : 1; /*!< [13..13] GPIO13 interrupt. */ + __IOM uint32_t GPIO14 : 1; /*!< [14..14] GPIO14 interrupt. */ + __IOM uint32_t GPIO15 : 1; /*!< [15..15] GPIO15 interrupt. */ + __IOM uint32_t GPIO16 : 1; /*!< [16..16] GPIO16 interrupt. */ + __IOM uint32_t GPIO17 : 1; /*!< [17..17] GPIO17 interrupt. */ + __IOM uint32_t GPIO18 : 1; /*!< [18..18] GPIO18interrupt. */ + __IOM uint32_t GPIO19 : 1; /*!< [19..19] GPIO19 interrupt. */ + __IOM uint32_t GPIO20 : 1; /*!< [20..20] GPIO20 interrupt. */ + __IOM uint32_t GPIO21 : 1; /*!< [21..21] GPIO21 interrupt. */ + __IOM uint32_t GPIO22 : 1; /*!< [22..22] GPIO22 interrupt. */ + __IOM uint32_t GPIO23 : 1; /*!< [23..23] GPIO23 interrupt. */ + __IOM uint32_t GPIO24 : 1; /*!< [24..24] GPIO24 interrupt. */ + __IOM uint32_t GPIO25 : 1; /*!< [25..25] GPIO25 interrupt. */ + __IOM uint32_t GPIO26 : 1; /*!< [26..26] GPIO26 interrupt. */ + __IOM uint32_t GPIO27 : 1; /*!< [27..27] GPIO27 interrupt. */ + __IOM uint32_t GPIO28 : 1; /*!< [28..28] GPIO28 interrupt. */ + __IOM uint32_t GPIO29 : 1; /*!< [29..29] GPIO29 interrupt. */ + __IOM uint32_t GPIO30 : 1; /*!< [30..30] GPIO30 interrupt. */ + __IOM uint32_t GPIO31 : 1; /*!< [31..31] GPIO31 interrupt. */ + } INT0SET_b; + } ; + + union { + __IOM uint32_t INT1EN; /*!< (@ 0x00000210) GPIO Interrupt Registers 49-32: Enable */ + + struct { + __IOM uint32_t GPIO32 : 1; /*!< [0..0] GPIO32 interrupt. */ + __IOM uint32_t GPIO33 : 1; /*!< [1..1] GPIO33 interrupt. */ + __IOM uint32_t GPIO34 : 1; /*!< [2..2] GPIO34 interrupt. */ + __IOM uint32_t GPIO35 : 1; /*!< [3..3] GPIO35 interrupt. */ + __IOM uint32_t GPIO36 : 1; /*!< [4..4] GPIO36 interrupt. */ + __IOM uint32_t GPIO37 : 1; /*!< [5..5] GPIO37 interrupt. */ + __IOM uint32_t GPIO38 : 1; /*!< [6..6] GPIO38 interrupt. */ + __IOM uint32_t GPIO39 : 1; /*!< [7..7] GPIO39 interrupt. */ + __IOM uint32_t GPIO40 : 1; /*!< [8..8] GPIO40 interrupt. */ + __IOM uint32_t GPIO41 : 1; /*!< [9..9] GPIO41 interrupt. */ + __IOM uint32_t GPIO42 : 1; /*!< [10..10] GPIO42 interrupt. */ + __IOM uint32_t GPIO43 : 1; /*!< [11..11] GPIO43 interrupt. */ + __IOM uint32_t GPIO44 : 1; /*!< [12..12] GPIO44 interrupt. */ + __IOM uint32_t GPIO45 : 1; /*!< [13..13] GPIO45 interrupt. */ + __IOM uint32_t GPIO46 : 1; /*!< [14..14] GPIO46 interrupt. */ + __IOM uint32_t GPIO47 : 1; /*!< [15..15] GPIO47 interrupt. */ + __IOM uint32_t GPIO48 : 1; /*!< [16..16] GPIO48 interrupt. */ + __IOM uint32_t GPIO49 : 1; /*!< [17..17] GPIO49 interrupt. */ + } INT1EN_b; + } ; + + union { + __IOM uint32_t INT1STAT; /*!< (@ 0x00000214) GPIO Interrupt Registers 49-32: Status */ + + struct { + __IOM uint32_t GPIO32 : 1; /*!< [0..0] GPIO32 interrupt. */ + __IOM uint32_t GPIO33 : 1; /*!< [1..1] GPIO33 interrupt. */ + __IOM uint32_t GPIO34 : 1; /*!< [2..2] GPIO34 interrupt. */ + __IOM uint32_t GPIO35 : 1; /*!< [3..3] GPIO35 interrupt. */ + __IOM uint32_t GPIO36 : 1; /*!< [4..4] GPIO36 interrupt. */ + __IOM uint32_t GPIO37 : 1; /*!< [5..5] GPIO37 interrupt. */ + __IOM uint32_t GPIO38 : 1; /*!< [6..6] GPIO38 interrupt. */ + __IOM uint32_t GPIO39 : 1; /*!< [7..7] GPIO39 interrupt. */ + __IOM uint32_t GPIO40 : 1; /*!< [8..8] GPIO40 interrupt. */ + __IOM uint32_t GPIO41 : 1; /*!< [9..9] GPIO41 interrupt. */ + __IOM uint32_t GPIO42 : 1; /*!< [10..10] GPIO42 interrupt. */ + __IOM uint32_t GPIO43 : 1; /*!< [11..11] GPIO43 interrupt. */ + __IOM uint32_t GPIO44 : 1; /*!< [12..12] GPIO44 interrupt. */ + __IOM uint32_t GPIO45 : 1; /*!< [13..13] GPIO45 interrupt. */ + __IOM uint32_t GPIO46 : 1; /*!< [14..14] GPIO46 interrupt. */ + __IOM uint32_t GPIO47 : 1; /*!< [15..15] GPIO47 interrupt. */ + __IOM uint32_t GPIO48 : 1; /*!< [16..16] GPIO48 interrupt. */ + __IOM uint32_t GPIO49 : 1; /*!< [17..17] GPIO49 interrupt. */ + } INT1STAT_b; + } ; + + union { + __IOM uint32_t INT1CLR; /*!< (@ 0x00000218) GPIO Interrupt Registers 49-32: Clear */ + + struct { + __IOM uint32_t GPIO32 : 1; /*!< [0..0] GPIO32 interrupt. */ + __IOM uint32_t GPIO33 : 1; /*!< [1..1] GPIO33 interrupt. */ + __IOM uint32_t GPIO34 : 1; /*!< [2..2] GPIO34 interrupt. */ + __IOM uint32_t GPIO35 : 1; /*!< [3..3] GPIO35 interrupt. */ + __IOM uint32_t GPIO36 : 1; /*!< [4..4] GPIO36 interrupt. */ + __IOM uint32_t GPIO37 : 1; /*!< [5..5] GPIO37 interrupt. */ + __IOM uint32_t GPIO38 : 1; /*!< [6..6] GPIO38 interrupt. */ + __IOM uint32_t GPIO39 : 1; /*!< [7..7] GPIO39 interrupt. */ + __IOM uint32_t GPIO40 : 1; /*!< [8..8] GPIO40 interrupt. */ + __IOM uint32_t GPIO41 : 1; /*!< [9..9] GPIO41 interrupt. */ + __IOM uint32_t GPIO42 : 1; /*!< [10..10] GPIO42 interrupt. */ + __IOM uint32_t GPIO43 : 1; /*!< [11..11] GPIO43 interrupt. */ + __IOM uint32_t GPIO44 : 1; /*!< [12..12] GPIO44 interrupt. */ + __IOM uint32_t GPIO45 : 1; /*!< [13..13] GPIO45 interrupt. */ + __IOM uint32_t GPIO46 : 1; /*!< [14..14] GPIO46 interrupt. */ + __IOM uint32_t GPIO47 : 1; /*!< [15..15] GPIO47 interrupt. */ + __IOM uint32_t GPIO48 : 1; /*!< [16..16] GPIO48 interrupt. */ + __IOM uint32_t GPIO49 : 1; /*!< [17..17] GPIO49 interrupt. */ + } INT1CLR_b; + } ; + + union { + __IOM uint32_t INT1SET; /*!< (@ 0x0000021C) GPIO Interrupt Registers 49-32: Set */ + + struct { + __IOM uint32_t GPIO32 : 1; /*!< [0..0] GPIO32 interrupt. */ + __IOM uint32_t GPIO33 : 1; /*!< [1..1] GPIO33 interrupt. */ + __IOM uint32_t GPIO34 : 1; /*!< [2..2] GPIO34 interrupt. */ + __IOM uint32_t GPIO35 : 1; /*!< [3..3] GPIO35 interrupt. */ + __IOM uint32_t GPIO36 : 1; /*!< [4..4] GPIO36 interrupt. */ + __IOM uint32_t GPIO37 : 1; /*!< [5..5] GPIO37 interrupt. */ + __IOM uint32_t GPIO38 : 1; /*!< [6..6] GPIO38 interrupt. */ + __IOM uint32_t GPIO39 : 1; /*!< [7..7] GPIO39 interrupt. */ + __IOM uint32_t GPIO40 : 1; /*!< [8..8] GPIO40 interrupt. */ + __IOM uint32_t GPIO41 : 1; /*!< [9..9] GPIO41 interrupt. */ + __IOM uint32_t GPIO42 : 1; /*!< [10..10] GPIO42 interrupt. */ + __IOM uint32_t GPIO43 : 1; /*!< [11..11] GPIO43 interrupt. */ + __IOM uint32_t GPIO44 : 1; /*!< [12..12] GPIO44 interrupt. */ + __IOM uint32_t GPIO45 : 1; /*!< [13..13] GPIO45 interrupt. */ + __IOM uint32_t GPIO46 : 1; /*!< [14..14] GPIO46 interrupt. */ + __IOM uint32_t GPIO47 : 1; /*!< [15..15] GPIO47 interrupt. */ + __IOM uint32_t GPIO48 : 1; /*!< [16..16] GPIO48 interrupt. */ + __IOM uint32_t GPIO49 : 1; /*!< [17..17] GPIO49 interrupt. */ + } INT1SET_b; + } ; +} GPIO_Type; /*!< Size = 544 (0x220) */ + + + +/* =========================================================================================================================== */ +/* ================ IOM0 ================ */ +/* =========================================================================================================================== */ + + +/** + * @brief IO Peripheral Master (IOM0) + */ + +typedef struct { /*!< (@ 0x50004000) IOM0 Structure */ + + union { + __IOM uint32_t FIFO; /*!< (@ 0x00000000) FIFO Access Port */ + + struct { + __IOM uint32_t FIFO : 32; /*!< [31..0] FIFO direct access. Only locations 0 - 3F will return + valid information. */ + } FIFO_b; + } ; + __IM uint32_t RESERVED[63]; + + union { + __IOM uint32_t FIFOPTR; /*!< (@ 0x00000100) FIFO size and remaining slots open values */ + + struct { + __IOM uint32_t FIFO0SIZ : 8; /*!< [7..0] The number of valid data bytes currently in the FIFO + 0 (written by MCU, read by interface) */ + __IOM uint32_t FIFO0REM : 8; /*!< [15..8] The number of remaining data bytes slots currently in + FIFO 0 (written by MCU, read by interface) */ + __IOM uint32_t FIFO1SIZ : 8; /*!< [23..16] The number of valid data bytes currently in FIFO 1 + (written by interface, read by MCU) */ + __IOM uint32_t FIFO1REM : 8; /*!< [31..24] The number of remaining data bytes slots currently + in FIFO 1 (written by interface, read by MCU) */ + } FIFOPTR_b; + } ; + + union { + __IOM uint32_t FIFOTHR; /*!< (@ 0x00000104) FIFO Threshold Configuration */ + + struct { + __IOM uint32_t FIFORTHR : 6; /*!< [5..0] FIFO read threshold in bytes. A value of 0 will disable + the read FIFO level from activating the threshold interrupt. + If this field is non-zero, it will trigger a threshold + interrupt when the read fifo contains FIFORTHR valid bytes + of data, as indicated by the FIFO1SIZ field. This is intended + to signal when a data transfer of FIFORTHR bytes can be + done from the IOM module to the host via the read fifo + to support large IOM read operations. */ + __IM uint32_t : 2; + __IOM uint32_t FIFOWTHR : 6; /*!< [13..8] FIFO write threshold in bytes. A value of 0 will disable + the write FIFO level from activating the threshold interrupt. + If this field is non-zero, it will trigger a threshold + interrupt when the write fifo contains FIFOWTHR free bytes, + as indicated by the FIFO0REM field. This is intended to + signal when a transfer of FIFOWTHR bytes can be done from + the host to the IOM write fifo to support large IOM write + operations. */ + } FIFOTHR_b; + } ; + + union { + __IOM uint32_t FIFOPOP; /*!< (@ 0x00000108) FIFO POP register */ + + struct { + __IOM uint32_t FIFODOUT : 32; /*!< [31..0] This register will return the read data indicated by + the current read pointer on reads. If the POPWR control + bit in the FIFOCTRL register is reset (0), the fifo read + pointer will be advanced by one word as a result of the + read.If the POPWR bit is set (1), the fifo read pointer + will only be advanced after a write operation to this register. + The write data is ignored for this register.If less than + a even word multiple is available, and the command is completed, + the module will return the word containing */ + } FIFOPOP_b; + } ; + + union { + __IOM uint32_t FIFOPUSH; /*!< (@ 0x0000010C) FIFO PUSH register */ + + struct { + __IOM uint32_t FIFODIN : 32; /*!< [31..0] This register is used to write the FIFORAM in FIFO mode + and will cause a push event to occur to the next open slot + within the FIFORAM. Writing to this register will cause + the write point to increment by 1 word(4 bytes). */ + } FIFOPUSH_b; + } ; + + union { + __IOM uint32_t FIFOCTRL; /*!< (@ 0x00000110) FIFO Control Register */ + + struct { + __IOM uint32_t POPWR : 1; /*!< [0..0] Selects the mode in which 'pop' events are done for the + fifo read operations. A value of '1' will prevent a pop + event on a read operation, and will require a write to + the FIFOPOP register to create a pop event.A value of '0' + in this register will allow a pop event to occur on the + read of the FIFOPOP register, and may cause inadvertant + fifo pops when used in a debugging mode. */ + __IOM uint32_t FIFORSTN : 1; /*!< [1..1] Active low manual reset of the fifo. Write to 0 to reset + fifo, and then write to 1 to remove the reset. */ + } FIFOCTRL_b; + } ; + + union { + __IOM uint32_t FIFOLOC; /*!< (@ 0x00000114) FIFO Pointers */ + + struct { + __IOM uint32_t FIFOWPTR : 4; /*!< [3..0] Current FIFO write pointer. Value is the index into the + outgoing FIFO (FIFO0), which is used during write operations + to external devices. */ + __IM uint32_t : 4; + __IOM uint32_t FIFORPTR : 4; /*!< [11..8] Current FIFO read pointer. Used to index into the incoming + FIFO (FIFO1), which is used to store read data returned + from external devices during a read operation. */ + } FIFOLOC_b; + } ; + __IM uint32_t RESERVED1[58]; + + union { + __IOM uint32_t INTEN; /*!< (@ 0x00000200) IO Master Interrupts: Enable */ + + struct { + __IOM uint32_t CMDCMP : 1; /*!< [0..0] Command Complete interrupt. Asserted when the current + operation has completed. For repeated commands, this will + only be asserted when the final repeated command is completed. */ + __IOM uint32_t THR : 1; /*!< [1..1] FIFO Threshold interrupt. For write operations, asserted + when the number of free bytes in the write FIFO equals + or exceeds the WTHR field.For read operations, asserted + when the number of valid bytes in the read FIFO equals + of exceeds the value set in the RTHR field. */ + __IOM uint32_t FUNDFL : 1; /*!< [2..2] Read FIFO Underflow interrupt. This occurs when software + tries to pop from an empty fifo. */ + __IOM uint32_t FOVFL : 1; /*!< [3..3] Write FIFO Overflow interrupt. This occurs when software + tries to write to a full fifo. The current operation does + not stop. */ + __IOM uint32_t NAK : 1; /*!< [4..4] I2C NAK interrupt. Asserted when an unexpected NAK has + been received on the I2C bus. */ + __IOM uint32_t IACC : 1; /*!< [5..5] illegal FIFO access interrupt. Asserted when there is + a overflow or underflow event */ + __IOM uint32_t ICMD : 1; /*!< [6..6] illegal command interrupt. Asserted when a command is + written when an active command is in progress. */ + __IOM uint32_t START : 1; /*!< [7..7] START command interrupt. Asserted when another master + on the bus has signaled a START command. */ + __IOM uint32_t STOP : 1; /*!< [8..8] STOP command interrupt. Asserted when another master + on the bus has signaled a STOP command. */ + __IOM uint32_t ARB : 1; /*!< [9..9] Arbitration loss interrupt. Asserted when arbitration + is enabled and has been lost to another master on the bus. */ + __IOM uint32_t DCMP : 1; /*!< [10..10] DMA Complete. Processing of the DMA operation has completed + and the DMA submodule is returned into the idle state */ + __IOM uint32_t DERR : 1; /*!< [11..11] DMA Error encountered during the processing of the + DMA command. The DMA error could occur when the memory + access specified in the DMA operation is not available + or incorrectly specified. */ + __IOM uint32_t CQPAUSED : 1; /*!< [12..12] Command queue is paused due to an active event enabled + in the PAUSEEN register. The interrupt is posted when the + event is enabled within the PAUSEEN register, the mask + is active in the CQIRQMASK field and the event occurs. */ + __IOM uint32_t CQUPD : 1; /*!< [13..13] CQ write operation performed a register write with + the register address bit 0 set to 1. The low address bits + in the CQ address fields are unused and bit 0 can be used + to trigger an interrupt to indicate when this register + write is performed by the CQ operation. */ + __IOM uint32_t CQERR : 1; /*!< [14..14] Error during command queue operations */ + } INTEN_b; + } ; + + union { + __IOM uint32_t INTSTAT; /*!< (@ 0x00000204) IO Master Interrupts: Status */ + + struct { + __IOM uint32_t CMDCMP : 1; /*!< [0..0] Command Complete interrupt. Asserted when the current + operation has completed. For repeated commands, this will + only be asserted when the final repeated command is completed. */ + __IOM uint32_t THR : 1; /*!< [1..1] FIFO Threshold interrupt. For write operations, asserted + when the number of free bytes in the write FIFO equals + or exceeds the WTHR field.For read operations, asserted + when the number of valid bytes in the read FIFO equals + of exceeds the value set in the RTHR field. */ + __IOM uint32_t FUNDFL : 1; /*!< [2..2] Read FIFO Underflow interrupt. This occurs when software + tries to pop from an empty fifo. */ + __IOM uint32_t FOVFL : 1; /*!< [3..3] Write FIFO Overflow interrupt. This occurs when software + tries to write to a full fifo. The current operation does + not stop. */ + __IOM uint32_t NAK : 1; /*!< [4..4] I2C NAK interrupt. Asserted when an unexpected NAK has + been received on the I2C bus. */ + __IOM uint32_t IACC : 1; /*!< [5..5] illegal FIFO access interrupt. Asserted when there is + a overflow or underflow event */ + __IOM uint32_t ICMD : 1; /*!< [6..6] illegal command interrupt. Asserted when a command is + written when an active command is in progress. */ + __IOM uint32_t START : 1; /*!< [7..7] START command interrupt. Asserted when another master + on the bus has signaled a START command. */ + __IOM uint32_t STOP : 1; /*!< [8..8] STOP command interrupt. Asserted when another master + on the bus has signaled a STOP command. */ + __IOM uint32_t ARB : 1; /*!< [9..9] Arbitration loss interrupt. Asserted when arbitration + is enabled and has been lost to another master on the bus. */ + __IOM uint32_t DCMP : 1; /*!< [10..10] DMA Complete. Processing of the DMA operation has completed + and the DMA submodule is returned into the idle state */ + __IOM uint32_t DERR : 1; /*!< [11..11] DMA Error encountered during the processing of the + DMA command. The DMA error could occur when the memory + access specified in the DMA operation is not available + or incorrectly specified. */ + __IOM uint32_t CQPAUSED : 1; /*!< [12..12] Command queue is paused due to an active event enabled + in the PAUSEEN register. The interrupt is posted when the + event is enabled within the PAUSEEN register, the mask + is active in the CQIRQMASK field and the event occurs. */ + __IOM uint32_t CQUPD : 1; /*!< [13..13] CQ write operation performed a register write with + the register address bit 0 set to 1. The low address bits + in the CQ address fields are unused and bit 0 can be used + to trigger an interrupt to indicate when this register + write is performed by the CQ operation. */ + __IOM uint32_t CQERR : 1; /*!< [14..14] Error during command queue operations */ + } INTSTAT_b; + } ; + + union { + __IOM uint32_t INTCLR; /*!< (@ 0x00000208) IO Master Interrupts: Clear */ + + struct { + __IOM uint32_t CMDCMP : 1; /*!< [0..0] Command Complete interrupt. Asserted when the current + operation has completed. For repeated commands, this will + only be asserted when the final repeated command is completed. */ + __IOM uint32_t THR : 1; /*!< [1..1] FIFO Threshold interrupt. For write operations, asserted + when the number of free bytes in the write FIFO equals + or exceeds the WTHR field.For read operations, asserted + when the number of valid bytes in the read FIFO equals + of exceeds the value set in the RTHR field. */ + __IOM uint32_t FUNDFL : 1; /*!< [2..2] Read FIFO Underflow interrupt. This occurs when software + tries to pop from an empty fifo. */ + __IOM uint32_t FOVFL : 1; /*!< [3..3] Write FIFO Overflow interrupt. This occurs when software + tries to write to a full fifo. The current operation does + not stop. */ + __IOM uint32_t NAK : 1; /*!< [4..4] I2C NAK interrupt. Asserted when an unexpected NAK has + been received on the I2C bus. */ + __IOM uint32_t IACC : 1; /*!< [5..5] illegal FIFO access interrupt. Asserted when there is + a overflow or underflow event */ + __IOM uint32_t ICMD : 1; /*!< [6..6] illegal command interrupt. Asserted when a command is + written when an active command is in progress. */ + __IOM uint32_t START : 1; /*!< [7..7] START command interrupt. Asserted when another master + on the bus has signaled a START command. */ + __IOM uint32_t STOP : 1; /*!< [8..8] STOP command interrupt. Asserted when another master + on the bus has signaled a STOP command. */ + __IOM uint32_t ARB : 1; /*!< [9..9] Arbitration loss interrupt. Asserted when arbitration + is enabled and has been lost to another master on the bus. */ + __IOM uint32_t DCMP : 1; /*!< [10..10] DMA Complete. Processing of the DMA operation has completed + and the DMA submodule is returned into the idle state */ + __IOM uint32_t DERR : 1; /*!< [11..11] DMA Error encountered during the processing of the + DMA command. The DMA error could occur when the memory + access specified in the DMA operation is not available + or incorrectly specified. */ + __IOM uint32_t CQPAUSED : 1; /*!< [12..12] Command queue is paused due to an active event enabled + in the PAUSEEN register. The interrupt is posted when the + event is enabled within the PAUSEEN register, the mask + is active in the CQIRQMASK field and the event occurs. */ + __IOM uint32_t CQUPD : 1; /*!< [13..13] CQ write operation performed a register write with + the register address bit 0 set to 1. The low address bits + in the CQ address fields are unused and bit 0 can be used + to trigger an interrupt to indicate when this register + write is performed by the CQ operation. */ + __IOM uint32_t CQERR : 1; /*!< [14..14] Error during command queue operations */ + } INTCLR_b; + } ; + + union { + __IOM uint32_t INTSET; /*!< (@ 0x0000020C) IO Master Interrupts: Set */ + + struct { + __IOM uint32_t CMDCMP : 1; /*!< [0..0] Command Complete interrupt. Asserted when the current + operation has completed. For repeated commands, this will + only be asserted when the final repeated command is completed. */ + __IOM uint32_t THR : 1; /*!< [1..1] FIFO Threshold interrupt. For write operations, asserted + when the number of free bytes in the write FIFO equals + or exceeds the WTHR field.For read operations, asserted + when the number of valid bytes in the read FIFO equals + of exceeds the value set in the RTHR field. */ + __IOM uint32_t FUNDFL : 1; /*!< [2..2] Read FIFO Underflow interrupt. This occurs when software + tries to pop from an empty fifo. */ + __IOM uint32_t FOVFL : 1; /*!< [3..3] Write FIFO Overflow interrupt. This occurs when software + tries to write to a full fifo. The current operation does + not stop. */ + __IOM uint32_t NAK : 1; /*!< [4..4] I2C NAK interrupt. Asserted when an unexpected NAK has + been received on the I2C bus. */ + __IOM uint32_t IACC : 1; /*!< [5..5] illegal FIFO access interrupt. Asserted when there is + a overflow or underflow event */ + __IOM uint32_t ICMD : 1; /*!< [6..6] illegal command interrupt. Asserted when a command is + written when an active command is in progress. */ + __IOM uint32_t START : 1; /*!< [7..7] START command interrupt. Asserted when another master + on the bus has signaled a START command. */ + __IOM uint32_t STOP : 1; /*!< [8..8] STOP command interrupt. Asserted when another master + on the bus has signaled a STOP command. */ + __IOM uint32_t ARB : 1; /*!< [9..9] Arbitration loss interrupt. Asserted when arbitration + is enabled and has been lost to another master on the bus. */ + __IOM uint32_t DCMP : 1; /*!< [10..10] DMA Complete. Processing of the DMA operation has completed + and the DMA submodule is returned into the idle state */ + __IOM uint32_t DERR : 1; /*!< [11..11] DMA Error encountered during the processing of the + DMA command. The DMA error could occur when the memory + access specified in the DMA operation is not available + or incorrectly specified. */ + __IOM uint32_t CQPAUSED : 1; /*!< [12..12] Command queue is paused due to an active event enabled + in the PAUSEEN register. The interrupt is posted when the + event is enabled within the PAUSEEN register, the mask + is active in the CQIRQMASK field and the event occurs. */ + __IOM uint32_t CQUPD : 1; /*!< [13..13] CQ write operation performed a register write with + the register address bit 0 set to 1. The low address bits + in the CQ address fields are unused and bit 0 can be used + to trigger an interrupt to indicate when this register + write is performed by the CQ operation. */ + __IOM uint32_t CQERR : 1; /*!< [14..14] Error during command queue operations */ + } INTSET_b; + } ; + + union { + __IOM uint32_t CLKCFG; /*!< (@ 0x00000210) I/O Clock Configuration */ + + struct { + __IOM uint32_t IOCLKEN : 1; /*!< [0..0] Enable for the interface clock. Must be enabled prior + to executing any IO operations. */ + __IM uint32_t : 7; + __IOM uint32_t FSEL : 3; /*!< [10..8] Select the input clock frequency. */ + __IOM uint32_t DIV3 : 1; /*!< [11..11] Enable divide by 3 of the source IOCLK. Division by + 3 is done before the DIVEN programmable divider, and if + enabledwill provide the divided by 3 clock as the source + to the programmable divider. */ + __IOM uint32_t DIVEN : 1; /*!< [12..12] Enable clock division by TOTPER and LOWPER */ + __IM uint32_t : 3; + __IOM uint32_t LOWPER : 8; /*!< [23..16] Clock low clock count minus 1. This provides the number + of clocks the divided clock will be low when the DIVEN + = 1.Only applicable when DIVEN = 1. */ + __IOM uint32_t TOTPER : 8; /*!< [31..24] Clock total clock count minus 1. This provides the + total period of the divided clock -1 when the DIVEN is + active. Thesource clock is selected by FSEL. Only applicable + when DIVEN = 1. */ + } CLKCFG_b; + } ; + + union { + __IOM uint32_t SUBMODCTRL; /*!< (@ 0x00000214) Submodule control */ + + struct { + __IOM uint32_t SMOD0EN : 1; /*!< [0..0] Submodule 0 enable (1) or disable (0) */ + __IOM uint32_t SMOD0TYPE : 3; /*!< [3..1] Submodule 0 module type. This is the SPI Master interface. */ + __IOM uint32_t SMOD1EN : 1; /*!< [4..4] Submodule 1 enable (1) or disable (0) */ + __IOM uint32_t SMOD1TYPE : 3; /*!< [7..5] Submodule 0 module type. This is the I2C Master interface */ + } SUBMODCTRL_b; + } ; + + union { + __IOM uint32_t CMD; /*!< (@ 0x00000218) Command and offset Register */ + + struct { + __IOM uint32_t CMD : 5; /*!< [4..0] Command for submodule. */ + __IOM uint32_t OFFSETCNT : 2; /*!< [6..5] Number of offset bytes to use for the command - 0, 1, + 2, 3 are valid selections. The second (byte 1) and third + byte (byte 2) are read from the OFFSETHI register, and + the low order byte is pulled from this register in the + OFFSETLO field.Offset bytes are transmitted highest byte + first. EG if offsetcnt == 3, OFFSETHI[15:8] will be transmitted + first, then OFFSETHI[7:0] then OFFSETLO.If offsetcnt == + 2, OFFSETHI[7:0] will be transmitted, then OFFSETLO.If + offsetcnt == 1, only OFFSETLO will be transmitted. */ + __IOM uint32_t CONT : 1; /*!< [7..7] Contine to hold the bus after the current transaction + if set to a 1 with a new command issued. */ + __IOM uint32_t TSIZE : 12; /*!< [19..8] Defines the transaction size in bytes. The offset transfer + is not included in this size. */ + __IOM uint32_t CMDSEL : 2; /*!< [21..20] Command Specific selection information. Not used in + Master I2C. Used as CEn select for Master SPI transactions */ + __IM uint32_t : 2; + __IOM uint32_t OFFSETLO : 8; /*!< [31..24] This register holds the low order byte of offset to + be used in the transaction. The number of offset bytes + to use is set with bits 1:0 of the command. */ + } CMD_b; + } ; + + union { + __IOM uint32_t CMDRPT; /*!< (@ 0x0000021C) Command Repeat Register */ + + struct { + __IOM uint32_t CMDRPT : 5; /*!< [4..0] Count of number of times to repeat the next command. */ + } CMDRPT_b; + } ; + + union { + __IOM uint32_t OFFSETHI; /*!< (@ 0x00000220) High order 2 bytes of 3 byte offset for IO transaction */ + + struct { + __IOM uint32_t OFFSETHI : 16; /*!< [15..0] Holds the high order 2 bytes of the 3 byte addressing/offset + field to use with IO commands. The number of offset bytes + to use is specified in the command register */ + } OFFSETHI_b; + } ; + + union { + __IOM uint32_t CMDSTAT; /*!< (@ 0x00000224) Command status */ + + struct { + __IOM uint32_t CCMD : 5; /*!< [4..0] current command that is being executed */ + __IOM uint32_t CMDSTAT : 3; /*!< [7..5] The current status of the command execution. */ + __IOM uint32_t CTSIZE : 12; /*!< [19..8] The current number of bytes still to be transferred + with this command. This field will count down to zero. */ + } CMDSTAT_b; + } ; + __IM uint32_t RESERVED2[6]; + + union { + __IOM uint32_t DMATRIGEN; /*!< (@ 0x00000240) DMA Trigger Enable Register */ + + struct { + __IOM uint32_t DCMDCMPEN : 1; /*!< [0..0] Trigger DMA upon command complete. Enables the trigger + of the DMA when a command is completed. When this event + is triggered, the number of words transferred will be the + lesser of the remaining TOTCOUNT bytes, or */ + __IOM uint32_t DTHREN : 1; /*!< [1..1] Trigger DMA upon THR level reached. For M2P DMA operations + (IOM writes), the trigger will assert when the write FIFO + has (WTHR/4) number of words free in the write FIFO, and + will transfer (WTHR/4) number of wordsor, if the number + of words left to transfer is less than the WTHR value, + will transfer the remaining byte count.For P2M DMA operations, + the trigger will assert when the read FIFO has (RTHR/4) + words available in the read FIFO, and will transfer (RTHR/4) + words to SRAM. This trigger will NOT asser */ + } DMATRIGEN_b; + } ; + + union { + __IOM uint32_t DMATRIGSTAT; /*!< (@ 0x00000244) DMA Trigger Status Register */ + + struct { + __IOM uint32_t DCMDCMP : 1; /*!< [0..0] Triggered DMA from Command complete event. Bit is read + only and can be cleared by disabling the DCMDCMP trigger + enable or by disabling DMA. */ + __IOM uint32_t DTHR : 1; /*!< [1..1] Triggered DMA from THR event. Bit is read only and can + be cleared by disabling the DTHR trigger enable or by disabling + DMA. */ + __IOM uint32_t DTOTCMP : 1; /*!< [2..2] DMA triggered when DCMDCMP = 0, and the amount of data + in the FIFO was enough to complete the DMA operation (greater + than or equal to current TOTCOUNT) when the command completed. + This trigger is default active when the DCMDCMP trigger + isdisabled and there is enough data in the FIFO to complete + the DMA operation. */ + } DMATRIGSTAT_b; + } ; + __IM uint32_t RESERVED3[14]; + + union { + __IOM uint32_t DMACFG; /*!< (@ 0x00000280) DMA Configuration Register */ + + struct { + __IOM uint32_t DMAEN : 1; /*!< [0..0] DMA Enable. Setting this bit to EN will start the DMA + operation. This should be the last DMA related register + set prior to issuing the command */ + __IOM uint32_t DMADIR : 1; /*!< [1..1] Direction */ + __IM uint32_t : 6; + __IOM uint32_t DMAPRI : 1; /*!< [8..8] Sets the Priority of the DMA request */ + __IOM uint32_t DPWROFF : 1; /*!< [9..9] Power off module after DMA is complete. If this bit is + active, the module will request to power off the supply + it is attached to. If there are other units still requiring + power from the same domain, power down will not be performed. */ + } DMACFG_b; + } ; + __IM uint32_t RESERVED4; + + union { + __IOM uint32_t DMATOTCOUNT; /*!< (@ 0x00000288) DMA Total Transfer Count */ + + struct { + __IOM uint32_t TOTCOUNT : 12; /*!< [11..0] Triggered DMA from Command complete event occured. Bit + is read only and can be cleared by disabling the DTHR trigger + enable or by disabling DMA. */ + } DMATOTCOUNT_b; + } ; + + union { + __IOM uint32_t DMATARGADDR; /*!< (@ 0x0000028C) DMA Target Address Register */ + + struct { + __IOM uint32_t TARGADDR : 20; /*!< [19..0] Bits [19:0] of the target byte address for source of + DMA (either read or write). The address can be any byte + alignment, and does not have to be word aligned. In cases + of non-word aligned addresses, the DMA logic will take + care for ensuring only the target bytes are read/written. */ + __IM uint32_t : 8; + __IOM uint32_t TARGADDR28 : 1; /*!< [28..28] Bit 28 of the target byte address for source of DMA + (either read or write). In cases of non-word aligned addresses, + the DMA logic will take care for ensuring only the target + bytes are read/written.Setting to '1' will select the SRAM. + Setting to '0' will select the flash */ + } DMATARGADDR_b; + } ; + + union { + __IOM uint32_t DMASTAT; /*!< (@ 0x00000290) DMA Status Register */ + + struct { + __IOM uint32_t DMATIP : 1; /*!< [0..0] DMA Transfer In Progress indicator. 1 will indicate that + a DMA transfer is active. The DMA transfer may be waiting + on data, transferring data, or waiting for priority.All + of these will be indicated with a 1. A 0 will indicate + that the DMA is fully complete and no further transactions + will be done. This bit is read only. */ + __IOM uint32_t DMACPL : 1; /*!< [1..1] DMA Transfer Complete. This signals the end of the DMA + operation. This bit can be cleared by writing to 0, and + will also be cleared when a new DMA is started. */ + __IOM uint32_t DMAERR : 1; /*!< [2..2] DMA Error. This active high bit signals an error was + encountered during the DMA operation. The bit can be cleared + by writing to 0. Once set, this bit will remain set until + cleared by software. */ + } DMASTAT_b; + } ; + + union { + __IOM uint32_t CQCFG; /*!< (@ 0x00000294) Command Queue Configuration Register */ + + struct { + __IOM uint32_t CQEN : 1; /*!< [0..0] Command queue enable. When set, will enable the processing + of the command queue and fetches of address/data pairs + will proceed from the word address within the CQADDR register. + Can be disabledusing a CQ executed write to this bit as + well. */ + __IOM uint32_t CQPRI : 1; /*!< [1..1] Sets the Priority of the command queue dma request */ + } CQCFG_b; + } ; + + union { + __IOM uint32_t CQADDR; /*!< (@ 0x00000298) CQ Target Read Address Register */ + + struct { + __IM uint32_t : 2; + __IOM uint32_t CQADDR : 18; /*!< [19..2] Bits 19:2 of target byte address for source of CQ (read + only). The buffer must be aligned on a word boundary */ + __IM uint32_t : 8; + __IOM uint32_t CQADDR28 : 1; /*!< [28..28] Bit 28 of target byte address for source of CQ (read + only). Used to denote Flash (0) or SRAM (1) access */ + } CQADDR_b; + } ; + + union { + __IOM uint32_t CQSTAT; /*!< (@ 0x0000029C) Command Queue Status Register */ + + struct { + __IOM uint32_t CQTIP : 1; /*!< [0..0] Command queue Transfer In Progress indicator. 1 will + indicate that a CQ transfer is active and this will remain + active even when paused waiting for external event. */ + __IOM uint32_t CQPAUSED : 1; /*!< [1..1] Command queue operation is currently paused. */ + __IOM uint32_t CQERR : 1; /*!< [2..2] Command queue processing Error. This active high bit + signals that an error was encountered during the CQ operation. */ + } CQSTAT_b; + } ; + + union { + __IOM uint32_t CQFLAGS; /*!< (@ 0x000002A0) Command Queue Flag Register */ + + struct { + __IOM uint32_t CQFLAGS : 16; /*!< [15..0] Current flag status (read-only). Bits [7:0] are software + controllable and bits [15:8] are hardware status. */ + __IOM uint32_t CQIRQMASK : 16; /*!< [31..16] Mask the bits used to generate the command queue interrupt. + A '1' in the bit position will enable the pause event to + trigger the interrupt, if the CQWT_int interrupt is enabled. + Bits definitions are the same as CQPAUSE */ + } CQFLAGS_b; + } ; + + union { + __IOM uint32_t CQSETCLEAR; /*!< (@ 0x000002A4) Command Queue Flag Set/Clear Register */ + + struct { + __IOM uint32_t CQFSET : 8; /*!< [7..0] Set CQFlag status bits. Will set to 1 the value of any + SWFLAG with a '1' in the corresponding bit position of + this field */ + __IOM uint32_t CQFTGL : 8; /*!< [15..8] Toggle the indicated bit. Will toggle the value of any + SWFLAG with a '1' in the corresponding bit position of + this field */ + __IOM uint32_t CQFCLR : 8; /*!< [23..16] Clear CQFlag status bits. Will clear to 0 any SWFLAG + with a '1' in the corresponding bit position of this field */ + } CQSETCLEAR_b; + } ; + + union { + __IOM uint32_t CQPAUSEEN; /*!< (@ 0x000002A8) Command Queue Pause Enable Register */ + + struct { + __IOM uint32_t CQPEN : 16; /*!< [15..0] Enables the specified event to pause command processing + when active */ + } CQPAUSEEN_b; + } ; + + union { + __IOM uint32_t CQCURIDX; /*!< (@ 0x000002AC) IOM Command Queue current index value . Compared + to the CQENDIDX reg contents to generate + the IDXEQ Pause event for command queue */ + + struct { + __IOM uint32_t CQCURIDX : 8; /*!< [7..0] Holds 8 bits of data that will be compared with the CQENDIX + register field. If the values match, the IDXEQ pause event + will be activated, which will cause the pausing of command + quue operation if the IDXEQ bit is enabled in CQPAUSEEN. */ + } CQCURIDX_b; + } ; + + union { + __IOM uint32_t CQENDIDX; /*!< (@ 0x000002B0) IOM Command Queue current index value . Compared + to the CQCURIDX reg contents to generate + the IDXEQ Pause event for command queue */ + + struct { + __IOM uint32_t CQENDIDX : 8; /*!< [7..0] Holds 8 bits of data that will be compared with the CQCURIX + register field. If the values match, the IDXEQ pause event + will be activated, which will cause the pausing of command + quue operation if the IDXEQ bit is enabled in CQPAUSEEN. */ + } CQENDIDX_b; + } ; + + union { + __IOM uint32_t STATUS; /*!< (@ 0x000002B4) IOM Module Status Register */ + + struct { + __IOM uint32_t ERR : 1; /*!< [0..0] Bit has been deprecated. Please refer to the other error + indicators. This will always return 0. */ + __IOM uint32_t CMDACT : 1; /*!< [1..1] Indicates if the active I/O Command is currently processing + a transaction, or command is complete, but the FIFO pointers + are still syncronizing internally. This bit will go high + atthe start of the transaction, and will go low when the + command is complete, and the data and pointers within the + FIFO have been syncronized. */ + __IOM uint32_t IDLEST : 1; /*!< [2..2] indicates if the active I/O state machine is IDLE. Note + - The state machine could be in idle state due to holdoffs + from data availability, or as the command gets propagated + into the logic from the registers. */ + } STATUS_b; + } ; + __IM uint32_t RESERVED5[18]; + + union { + __IOM uint32_t MSPICFG; /*!< (@ 0x00000300) SPI module master configuration */ + + struct { + __IOM uint32_t SPOL : 1; /*!< [0..0] selects SPI polarity. */ + __IOM uint32_t SPHA : 1; /*!< [1..1] selects SPI phase. */ + __IOM uint32_t FULLDUP : 1; /*!< [2..2] Enables full duplex mode for Master SPI write operations. + Data will be captured simultaneously into the read fifo */ + __IM uint32_t : 13; + __IOM uint32_t WTFC : 1; /*!< [16..16] enables write mode flow control. */ + __IOM uint32_t RDFC : 1; /*!< [17..17] enables read mode flow control. */ + __IOM uint32_t MOSIINV : 1; /*!< [18..18] inverts MOSI when flow control is enabled. */ + __IM uint32_t : 1; + __IOM uint32_t WTFCIRQ : 1; /*!< [20..20] selects the write mode flow control signal. */ + __IOM uint32_t WTFCPOL : 1; /*!< [21..21] selects the write flow control signal polarity. The + transfers are halted when the selected flow control signal + is OPPOSITE polarity of bit. (For example: WTFCPOL = 0 + will allow a IRQ=1 to pause transfers). */ + __IOM uint32_t RDFCPOL : 1; /*!< [22..22] selects the read flow control signal polarity. */ + __IOM uint32_t SPILSB : 1; /*!< [23..23] Selects data transfer as MSB first (0) or LSB first + (1) for the data portion of the SPI transaction. The offset + bytes are always transmitted MSB first. */ + __IOM uint32_t DINDLY : 3; /*!< [26..24] Delay tap to use for the input signal (MISO). This + gives more hold time on the input data. */ + __IOM uint32_t DOUTDLY : 3; /*!< [29..27] Delay tap to use for the output signal (MOSI). This + give more hold time on the output data */ + __IOM uint32_t MSPIRST : 1; /*!< [30..30] Not used. To reset the module, toggle the SMOD_EN for + the module */ + } MSPICFG_b; + } ; + __IM uint32_t RESERVED6[63]; + + union { + __IOM uint32_t MI2CCFG; /*!< (@ 0x00000400) I2C Master configuration */ + + struct { + __IOM uint32_t ADDRSZ : 1; /*!< [0..0] Sets the I2C master device address size to either 7b + (0) or 10b (1). */ + __IOM uint32_t I2CLSB : 1; /*!< [1..1] Direction of data transmit and receive, MSB(0) or LSB(1) + first. Default per I2C specification is MSB first. This + applies to both read and write data, and read data will + be bit */ + __IOM uint32_t ARBEN : 1; /*!< [2..2] Enables multi-master arbitration for the I2C master. + If the bus is known to have only a single master, this + function can be disabled to save clock cycles on I2C transactions */ + __IM uint32_t : 1; + __IOM uint32_t SDADLY : 2; /*!< [5..4] Delay to enable on the SDA output. Values are 0x0-0x3. */ + __IOM uint32_t MI2CRST : 1; /*!< [6..6] Not used. To reset the module, toggle the SMOD_EN for + the module */ + __IM uint32_t : 1; + __IOM uint32_t SCLENDLY : 4; /*!< [11..8] Number of IOCLK cycles to delay the rising edge of the + SCL output en (clock will go low on this edge). Used to + allow clock shaping. */ + __IOM uint32_t SDAENDLY : 4; /*!< [15..12] Number of IOCLK cycles to delay the SDA output en (all + transitions affected). Used to delay data relative to clock */ + __IOM uint32_t SMPCNT : 8; /*!< [23..16] Number of Base clk cycles to wait before sampling the + SCL clock to determine if a clock stretch event has occured */ + __IOM uint32_t STRDIS : 1; /*!< [24..24] Disable detection of clock stretch events smaller than + 1 cycle */ + } MI2CCFG_b; + } ; + + union { + __IOM uint32_t DEVCFG; /*!< (@ 0x00000404) I2C Device Configuration register */ + + struct { + __IOM uint32_t DEVADDR : 10; /*!< [9..0] I2C address of the device that the Master will use to + target for read/write operations. This can be either a + 7b or 10b address. */ + } DEVCFG_b; + } ; + __IM uint32_t RESERVED7[2]; + + union { + __IOM uint32_t IOMDBG; /*!< (@ 0x00000410) IOM Debug Register */ + + struct { + __IOM uint32_t DBGEN : 1; /*!< [0..0] Debug Enable. Setting bit will enable the update of data + within this register, otherwise it is clock gated for power + savings */ + __IOM uint32_t IOCLKON : 1; /*!< [1..1] IOCLK debug clock control. Enable IO_CLK to be active + when this bit is '1'. Otherwise, the clock is controlled + with gating from the logic as needed. */ + __IOM uint32_t APBCLKON : 1; /*!< [2..2] APBCLK debug clock control. Enable APB_CLK to be active + when this bit is '1'. Otherwise, the clock is controlled + with gating from the logic as needed. */ + __IOM uint32_t DBGDATA : 29; /*!< [31..3] Debug control for various options. DBGDATA[1:0] is used + to select between different debug data available in the + DBG0 and DBG1 registers. */ + } IOMDBG_b; + } ; +} IOM0_Type; /*!< Size = 1044 (0x414) */ + + + +/* =========================================================================================================================== */ +/* ================ IOSLAVE ================ */ +/* =========================================================================================================================== */ + + +/** + * @brief I2C/SPI Slave (IOSLAVE) + */ + +typedef struct { /*!< (@ 0x50000000) IOSLAVE Structure */ + __IM uint32_t RESERVED[64]; + + union { + __IOM uint32_t FIFOPTR; /*!< (@ 0x00000100) Current FIFO Pointer */ + + struct { + __IOM uint32_t FIFOPTR : 8; /*!< [7..0] Current FIFO pointer. */ + __IOM uint32_t FIFOSIZ : 8; /*!< [15..8] The number of bytes currently in the hardware FIFO. */ + } FIFOPTR_b; + } ; + + union { + __IOM uint32_t FIFOCFG; /*!< (@ 0x00000104) FIFO Configuration */ + + struct { + __IOM uint32_t FIFOBASE : 5; /*!< [4..0] These bits hold the base address of the I/O FIFO in 8 + byte segments. The IO Slave FIFO is situated in LRAM at + (FIFOBASE*8) to (FIFOMAX*8-1). */ + __IM uint32_t : 3; + __IOM uint32_t FIFOMAX : 6; /*!< [13..8] These bits hold the maximum FIFO address in 8 byte segments. + It is also the beginning of the RAM area of the LRAM. Note + that no RAM area is configured if FIFOMAX is set to 0x1F. */ + __IM uint32_t : 10; + __IOM uint32_t ROBASE : 6; /*!< [29..24] Defines the read-only area. The IO Slave read-only + area is situated in LRAM at (ROBASE*8) to (FIFOBASE*8-1) */ + } FIFOCFG_b; + } ; + + union { + __IOM uint32_t FIFOTHR; /*!< (@ 0x00000108) FIFO Threshold Configuration */ + + struct { + __IOM uint32_t FIFOTHR : 8; /*!< [7..0] FIFO size interrupt threshold. */ + } FIFOTHR_b; + } ; + + union { + __IOM uint32_t FUPD; /*!< (@ 0x0000010C) FIFO Update Status */ + + struct { + __IOM uint32_t FIFOUPD : 1; /*!< [0..0] This bit indicates that a FIFO update is underway. */ + __IOM uint32_t IOREAD : 1; /*!< [1..1] This bitfield indicates an IO read is active. */ + } FUPD_b; + } ; + + union { + __IOM uint32_t FIFOCTR; /*!< (@ 0x00000110) Overall FIFO Counter */ + + struct { + __IOM uint32_t FIFOCTR : 10; /*!< [9..0] Virtual FIFO byte count */ + } FIFOCTR_b; + } ; + + union { + __IOM uint32_t FIFOINC; /*!< (@ 0x00000114) Overall FIFO Counter Increment */ + + struct { + __IOM uint32_t FIFOINC : 10; /*!< [9..0] Increment the Overall FIFO Counter by this value on a + write */ + } FIFOINC_b; + } ; + + union { + __IOM uint32_t CFG; /*!< (@ 0x00000118) I/O Slave Configuration */ + + struct { + __IOM uint32_t IFCSEL : 1; /*!< [0..0] This bit selects the I/O interface. */ + __IOM uint32_t SPOL : 1; /*!< [1..1] This bit selects SPI polarity. */ + __IOM uint32_t LSB : 1; /*!< [2..2] This bit selects the transfer bit ordering. */ + __IM uint32_t : 1; + __IOM uint32_t STARTRD : 1; /*!< [4..4] This bit holds the cycle to initiate an I/O RAM read. */ + __IM uint32_t : 3; + __IOM uint32_t I2CADDR : 12; /*!< [19..8] 7-bit or 10-bit I2C device address. */ + __IM uint32_t : 11; + __IOM uint32_t IFCEN : 1; /*!< [31..31] IOSLAVE interface enable. */ + } CFG_b; + } ; + + union { + __IOM uint32_t PRENC; /*!< (@ 0x0000011C) I/O Slave Interrupt Priority Encode */ + + struct { + __IOM uint32_t PRENC : 5; /*!< [4..0] These bits hold the priority encode of the REGACC interrupts. */ + } PRENC_b; + } ; + + union { + __IOM uint32_t IOINTCTL; /*!< (@ 0x00000120) I/O Interrupt Control */ + + struct { + __IOM uint32_t IOINTEN : 8; /*!< [7..0] These read-only bits indicate whether the IOINT interrupts + are enabled. */ + __IOM uint32_t IOINT : 8; /*!< [15..8] These bits read the IOINT interrupts. */ + __IOM uint32_t IOINTCLR : 1; /*!< [16..16] This bit clears all of the IOINT interrupts when written + with a 1. */ + __IM uint32_t : 7; + __IOM uint32_t IOINTSET : 8; /*!< [31..24] These bits set the IOINT interrupts when written with + a 1. */ + } IOINTCTL_b; + } ; + + union { + __IOM uint32_t GENADD; /*!< (@ 0x00000124) General Address Data */ + + struct { + __IOM uint32_t GADATA : 8; /*!< [7..0] The data supplied on the last General Address reference. */ + } GENADD_b; + } ; + __IM uint32_t RESERVED1[54]; + + union { + __IOM uint32_t INTEN; /*!< (@ 0x00000200) IO Slave Interrupts: Enable */ + + struct { + __IOM uint32_t FSIZE : 1; /*!< [0..0] FIFO Size interrupt. */ + __IOM uint32_t FOVFL : 1; /*!< [1..1] FIFO Overflow interrupt. */ + __IOM uint32_t FUNDFL : 1; /*!< [2..2] FIFO Underflow interrupt. */ + __IOM uint32_t FRDERR : 1; /*!< [3..3] FIFO Read Error interrupt. */ + __IOM uint32_t GENAD : 1; /*!< [4..4] I2C General Address interrupt. */ + __IOM uint32_t IOINTW : 1; /*!< [5..5] IO Write interrupt. */ + __IOM uint32_t XCMPRF : 1; /*!< [6..6] Transfer complete interrupt, read from FIFO space. */ + __IOM uint32_t XCMPRR : 1; /*!< [7..7] Transfer complete interrupt, read from register space. */ + __IOM uint32_t XCMPWF : 1; /*!< [8..8] Transfer complete interrupt, write to FIFO space. */ + __IOM uint32_t XCMPWR : 1; /*!< [9..9] Transfer complete interrupt, write to register space. */ + } INTEN_b; + } ; + + union { + __IOM uint32_t INTSTAT; /*!< (@ 0x00000204) IO Slave Interrupts: Status */ + + struct { + __IOM uint32_t FSIZE : 1; /*!< [0..0] FIFO Size interrupt. */ + __IOM uint32_t FOVFL : 1; /*!< [1..1] FIFO Overflow interrupt. */ + __IOM uint32_t FUNDFL : 1; /*!< [2..2] FIFO Underflow interrupt. */ + __IOM uint32_t FRDERR : 1; /*!< [3..3] FIFO Read Error interrupt. */ + __IOM uint32_t GENAD : 1; /*!< [4..4] I2C General Address interrupt. */ + __IOM uint32_t IOINTW : 1; /*!< [5..5] IO Write interrupt. */ + __IOM uint32_t XCMPRF : 1; /*!< [6..6] Transfer complete interrupt, read from FIFO space. */ + __IOM uint32_t XCMPRR : 1; /*!< [7..7] Transfer complete interrupt, read from register space. */ + __IOM uint32_t XCMPWF : 1; /*!< [8..8] Transfer complete interrupt, write to FIFO space. */ + __IOM uint32_t XCMPWR : 1; /*!< [9..9] Transfer complete interrupt, write to register space. */ + } INTSTAT_b; + } ; + + union { + __IOM uint32_t INTCLR; /*!< (@ 0x00000208) IO Slave Interrupts: Clear */ + + struct { + __IOM uint32_t FSIZE : 1; /*!< [0..0] FIFO Size interrupt. */ + __IOM uint32_t FOVFL : 1; /*!< [1..1] FIFO Overflow interrupt. */ + __IOM uint32_t FUNDFL : 1; /*!< [2..2] FIFO Underflow interrupt. */ + __IOM uint32_t FRDERR : 1; /*!< [3..3] FIFO Read Error interrupt. */ + __IOM uint32_t GENAD : 1; /*!< [4..4] I2C General Address interrupt. */ + __IOM uint32_t IOINTW : 1; /*!< [5..5] IO Write interrupt. */ + __IOM uint32_t XCMPRF : 1; /*!< [6..6] Transfer complete interrupt, read from FIFO space. */ + __IOM uint32_t XCMPRR : 1; /*!< [7..7] Transfer complete interrupt, read from register space. */ + __IOM uint32_t XCMPWF : 1; /*!< [8..8] Transfer complete interrupt, write to FIFO space. */ + __IOM uint32_t XCMPWR : 1; /*!< [9..9] Transfer complete interrupt, write to register space. */ + } INTCLR_b; + } ; + + union { + __IOM uint32_t INTSET; /*!< (@ 0x0000020C) IO Slave Interrupts: Set */ + + struct { + __IOM uint32_t FSIZE : 1; /*!< [0..0] FIFO Size interrupt. */ + __IOM uint32_t FOVFL : 1; /*!< [1..1] FIFO Overflow interrupt. */ + __IOM uint32_t FUNDFL : 1; /*!< [2..2] FIFO Underflow interrupt. */ + __IOM uint32_t FRDERR : 1; /*!< [3..3] FIFO Read Error interrupt. */ + __IOM uint32_t GENAD : 1; /*!< [4..4] I2C General Address interrupt. */ + __IOM uint32_t IOINTW : 1; /*!< [5..5] IO Write interrupt. */ + __IOM uint32_t XCMPRF : 1; /*!< [6..6] Transfer complete interrupt, read from FIFO space. */ + __IOM uint32_t XCMPRR : 1; /*!< [7..7] Transfer complete interrupt, read from register space. */ + __IOM uint32_t XCMPWF : 1; /*!< [8..8] Transfer complete interrupt, write to FIFO space. */ + __IOM uint32_t XCMPWR : 1; /*!< [9..9] Transfer complete interrupt, write to register space. */ + } INTSET_b; + } ; + + union { + __IOM uint32_t REGACCINTEN; /*!< (@ 0x00000210) Register Access Interrupts: Enable */ + + struct { + __IOM uint32_t REGACC : 32; /*!< [31..0] Register access interrupts. */ + } REGACCINTEN_b; + } ; + + union { + __IOM uint32_t REGACCINTSTAT; /*!< (@ 0x00000214) Register Access Interrupts: Status */ + + struct { + __IOM uint32_t REGACC : 32; /*!< [31..0] Register access interrupts. */ + } REGACCINTSTAT_b; + } ; + + union { + __IOM uint32_t REGACCINTCLR; /*!< (@ 0x00000218) Register Access Interrupts: Clear */ + + struct { + __IOM uint32_t REGACC : 32; /*!< [31..0] Register access interrupts. */ + } REGACCINTCLR_b; + } ; + + union { + __IOM uint32_t REGACCINTSET; /*!< (@ 0x0000021C) Register Access Interrupts: Set */ + + struct { + __IOM uint32_t REGACC : 32; /*!< [31..0] Register access interrupts. */ + } REGACCINTSET_b; + } ; +} IOSLAVE_Type; /*!< Size = 544 (0x220) */ + + + +/* =========================================================================================================================== */ +/* ================ MCUCTRL ================ */ +/* =========================================================================================================================== */ + + +/** + * @brief MCU Miscellaneous Control Logic (MCUCTRL) + */ + +typedef struct { /*!< (@ 0x40020000) MCUCTRL Structure */ + + union { + __IOM uint32_t CHIPPN; /*!< (@ 0x00000000) Chip Information Register */ + + struct { + __IOM uint32_t PARTNUM : 32; /*!< [31..0] BCD part number. */ + } CHIPPN_b; + } ; + + union { + __IOM uint32_t CHIPID0; /*!< (@ 0x00000004) Unique Chip ID 0 */ + + struct { + __IOM uint32_t CHIPID0 : 32; /*!< [31..0] Unique chip ID 0. */ + } CHIPID0_b; + } ; + + union { + __IOM uint32_t CHIPID1; /*!< (@ 0x00000008) Unique Chip ID 1 */ + + struct { + __IOM uint32_t CHIPID1 : 32; /*!< [31..0] Unique chip ID 1. */ + } CHIPID1_b; + } ; + + union { + __IOM uint32_t CHIPREV; /*!< (@ 0x0000000C) Chip Revision */ + + struct { + __IOM uint32_t REVMIN : 4; /*!< [3..0] Minor Revision ID. */ + __IOM uint32_t REVMAJ : 4; /*!< [7..4] Major Revision ID. */ + __IOM uint32_t SIPART : 12; /*!< [19..8] Silicon Part ID */ + } CHIPREV_b; + } ; + + union { + __IOM uint32_t VENDORID; /*!< (@ 0x00000010) Unique Vendor ID */ + + struct { + __IOM uint32_t VENDORID : 32; /*!< [31..0] Unique Vendor ID */ + } VENDORID_b; + } ; + + union { + __IOM uint32_t SKU; /*!< (@ 0x00000014) Unique Chip SKU */ + + struct { + __IOM uint32_t ALLOWBURST : 1; /*!< [0..0] Allow Burst feature */ + __IOM uint32_t ALLOWBLE : 1; /*!< [1..1] Allow BLE feature */ + __IOM uint32_t SECBOOT : 1; /*!< [2..2] Secure boot feature allowed */ + } SKU_b; + } ; + + union { + __IOM uint32_t FEATUREENABLE; /*!< (@ 0x00000018) Feature Enable on Burst and BLE */ + + struct { + __IOM uint32_t BLEREQ : 1; /*!< [0..0] Controls the BLE functionality */ + __IOM uint32_t BLEACK : 1; /*!< [1..1] ACK for BLEREQ */ + __IOM uint32_t BLEAVAIL : 1; /*!< [2..2] AVAILABILITY of the BLE functionality */ + __IM uint32_t : 1; + __IOM uint32_t BURSTREQ : 1; /*!< [4..4] Controls the Burst functionality */ + __IOM uint32_t BURSTACK : 1; /*!< [5..5] ACK for BURSTREQ */ + __IOM uint32_t BURSTAVAIL : 1; /*!< [6..6] Availability of Burst functionality */ + } FEATUREENABLE_b; + } ; + __IM uint32_t RESERVED; + + union { + __IOM uint32_t DEBUGGER; /*!< (@ 0x00000020) Debugger Control */ + + struct { + __IOM uint32_t LOCKOUT : 1; /*!< [0..0] Lockout of debugger (SWD). */ + } DEBUGGER_b; + } ; + __IM uint32_t RESERVED1[55]; + + union { + __IOM uint32_t BODCTRL; /*!< (@ 0x00000100) BOD control Register */ + + struct { + __IOM uint32_t BODLPWD : 1; /*!< [0..0] BODL Power Down. */ + __IOM uint32_t BODHPWD : 1; /*!< [1..1] BODH Power Down. */ + __IOM uint32_t BODCPWD : 1; /*!< [2..2] BODC Power Down. */ + __IOM uint32_t BODFPWD : 1; /*!< [3..3] BODF Power Down. */ + __IOM uint32_t BODLVREFSEL : 1; /*!< [4..4] BODL External Reference Select. Note: the SWE mux select + in PWRSEQ2SWE must be set for this to take effect. */ + __IOM uint32_t BODHVREFSEL : 1; /*!< [5..5] BODH External Reference Select. Note: the SWE mux select + in PWRSEQ2SWE must be set for this to take effect. */ + } BODCTRL_b; + } ; + + union { + __IOM uint32_t ADCPWRDLY; /*!< (@ 0x00000104) ADC Power Up Delay Control */ + + struct { + __IOM uint32_t ADCPWR0 : 8; /*!< [7..0] ADC Reference Buffer Power Enable delay in 64 ADC CLK + increments for ADC_CLKSEL = 0x1, 32 ADC CLOCK increments + for ADC_CLKSEL = 0x2. */ + __IOM uint32_t ADCPWR1 : 8; /*!< [15..8] ADC Reference Keeper enable delay in 16 ADC CLK increments + for ADC_CLKSEL = 0x1, 8 ADC CLOCK increments for ADC_CLKSEL + = 0x2. */ + } ADCPWRDLY_b; + } ; + __IM uint32_t RESERVED2; + + union { + __IOM uint32_t ADCCAL; /*!< (@ 0x0000010C) ADC Calibration Control */ + + struct { + __IOM uint32_t CALONPWRUP : 1; /*!< [0..0] Run ADC Calibration on initial power up sequence */ + __IOM uint32_t ADCCALIBRATED : 1; /*!< [1..1] Status for ADC Calibration */ + } ADCCAL_b; + } ; + + union { + __IOM uint32_t ADCBATTLOAD; /*!< (@ 0x00000110) ADC Battery Load Enable */ + + struct { + __IOM uint32_t BATTLOAD : 1; /*!< [0..0] Enable the ADC battery load resistor */ + } ADCBATTLOAD_b; + } ; + __IM uint32_t RESERVED3; + + union { + __IOM uint32_t ADCTRIM; /*!< (@ 0x00000118) ADC Trims */ + + struct { + __IOM uint32_t ADCREFKEEPIBTRIM : 2; /*!< [1..0] ADC Reference Ibias trim */ + __IM uint32_t : 4; + __IOM uint32_t ADCREFBUFTRIM : 5; /*!< [10..6] ADC Reference buffer trim */ + __IOM uint32_t ADCRFBUFIBTRIM : 2; /*!< [12..11] ADC reference buffer input bias trim */ + } ADCTRIM_b; + } ; + + union { + __IOM uint32_t ADCREFCOMP; /*!< (@ 0x0000011C) ADC Referece Keeper and Comparator Control */ + + struct { + __IOM uint32_t ADC_REFCOMP_OUT : 1; /*!< [0..0] Output of the ADC reference comparator */ + __IM uint32_t : 7; + __IOM uint32_t ADCREFKEEPTRIM : 5; /*!< [12..8] ADC Reference Keeper Trim */ + __IM uint32_t : 3; + __IOM uint32_t ADCRFCMPEN : 1; /*!< [16..16] ADC Reference comparator power down */ + } ADCREFCOMP_b; + } ; + + union { + __IOM uint32_t XTALCTRL; /*!< (@ 0x00000120) XTAL Oscillator Control */ + + struct { + __IOM uint32_t XTALSWE : 1; /*!< [0..0] XTAL Software Override Enable. */ + __IOM uint32_t FDBKDSBLXTAL : 1; /*!< [1..1] XTAL Oscillator Disable Feedback. */ + __IOM uint32_t BYPCMPRXTAL : 1; /*!< [2..2] XTAL Oscillator Bypass Comparator. */ + __IOM uint32_t PDNBCOREXTAL : 1; /*!< [3..3] XTAL Oscillator Power Down Core. */ + __IOM uint32_t PDNBCMPRXTAL : 1; /*!< [4..4] XTAL Oscillator Power Down Comparator. */ + __IOM uint32_t PWDBODXTAL : 1; /*!< [5..5] XTAL Power down on brown out. */ + __IOM uint32_t XTALIBUFTRIM : 2; /*!< [7..6] XTAL IBUFF trim */ + __IOM uint32_t XTALICOMPTRIM : 2; /*!< [9..8] XTAL ICOMP trim */ + } XTALCTRL_b; + } ; + + union { + __IOM uint32_t XTALGENCTRL; /*!< (@ 0x00000124) XTAL Oscillator General Control */ + + struct { + __IOM uint32_t ACWARMUP : 2; /*!< [1..0] Auto-calibration delay control */ + __IOM uint32_t XTALBIASTRIM : 6; /*!< [7..2] XTAL BIAS trim */ + __IOM uint32_t XTALKSBIASTRIM : 6; /*!< [13..8] XTAL IBIAS Kick start trim. This trim value is used + during the startup process to enable a faster lock. */ + } XTALGENCTRL_b; + } ; + __IM uint32_t RESERVED4[28]; + + union { + __IOM uint32_t MISCCTRL; /*!< (@ 0x00000198) Miscellaneous control register. */ + + struct { + __IOM uint32_t RESERVED_RW_0 : 5; /*!< [4..0] Reserved bits, always leave unchanged. The MISCCTRL register + must be modified via atomic RMW, leaving this bitfield + completely unmodified. Failure to do so will result in + unpredictable behavior. */ + __IOM uint32_t BLE_RESETN : 1; /*!< [5..5] BLE reset signal. */ + } MISCCTRL_b; + } ; + __IM uint32_t RESERVED5; + + union { + __IOM uint32_t BOOTLOADER; /*!< (@ 0x000001A0) Bootloader and secure boot functions */ + + struct { + __IOM uint32_t BOOTLOADERLOW : 1; /*!< [0..0] Determines whether the bootloader code is visible at + address 0x00000000 or not. Resets to 1, write 1 to clear. */ + __IOM uint32_t SBLOCK : 1; /*!< [1..1] Secure boot lock. Always resets to 1, write 1 to clear. + Enables system visibility to bootloader until set. */ + __IOM uint32_t PROTLOCK : 1; /*!< [2..2] Flash protection lock. Always resets to 1, write 1 to + clear. Enables writes to flash protection register set. */ + __IM uint32_t : 23; + __IOM uint32_t SECBOOTFEATURE : 2; /*!< [27..26] Indicates whether the secure boot feature is enabled. */ + __IOM uint32_t SECBOOT : 2; /*!< [29..28] Indicates whether the secure boot on cold reset is + enabled */ + __IOM uint32_t SECBOOTONRST : 2; /*!< [31..30] Indicates whether the secure boot on warm reset is + enabled */ + } BOOTLOADER_b; + } ; + + union { + __IOM uint32_t SHADOWVALID; /*!< (@ 0x000001A4) Register to indicate whether the shadow registers + have been successfully loaded from the Flash + Information Space. */ + + struct { + __IOM uint32_t VALID : 1; /*!< [0..0] Indicates whether the shadow registers contain valid + data from the Flash Information Space. */ + __IOM uint32_t BLDSLEEP : 1; /*!< [1..1] Indicates whether the bootloader should sleep or deep + sleep if no image loaded. */ + __IOM uint32_t INFO0_VALID : 1; /*!< [2..2] Indicates whether info0 contains valid data */ + } SHADOWVALID_b; + } ; + __IM uint32_t RESERVED6[2]; + + union { + __IOM uint32_t SCRATCH0; /*!< (@ 0x000001B0) Scratch register that is not reset by any reset */ + + struct { + __IOM uint32_t SCRATCH0 : 32; /*!< [31..0] Scratch register 0. */ + } SCRATCH0_b; + } ; + + union { + __IOM uint32_t SCRATCH1; /*!< (@ 0x000001B4) Scratch register that is not reset by any reset */ + + struct { + __IOM uint32_t SCRATCH1 : 32; /*!< [31..0] Scratch register 1. */ + } SCRATCH1_b; + } ; + __IM uint32_t RESERVED7[2]; + + union { + __IOM uint32_t ICODEFAULTADDR; /*!< (@ 0x000001C0) ICODE bus address which was present when a bus + fault occurred. */ + + struct { + __IOM uint32_t ICODEFAULTADDR : 32; /*!< [31..0] The ICODE bus address observed when a Bus Fault occurred. + Once an address is captured in this field, it is held until + the corresponding Fault Observed bit is cleared in the + FAULTSTATUS register. */ + } ICODEFAULTADDR_b; + } ; + + union { + __IOM uint32_t DCODEFAULTADDR; /*!< (@ 0x000001C4) DCODE bus address which was present when a bus + fault occurred. */ + + struct { + __IOM uint32_t DCODEFAULTADDR : 32; /*!< [31..0] The DCODE bus address observed when a Bus Fault occurred. + Once an address is captured in this field, it is held until + the corresponding Fault Observed bit is cleared in the + FAULTSTATUS register. */ + } DCODEFAULTADDR_b; + } ; + + union { + __IOM uint32_t SYSFAULTADDR; /*!< (@ 0x000001C8) System bus address which was present when a bus + fault occurred. */ + + struct { + __IOM uint32_t SYSFAULTADDR : 32; /*!< [31..0] SYS bus address observed when a Bus Fault occurred. + Once an address is captured in this field, it is held until + the corresponding Fault Observed bit is cleared in the + FAULTSTATUS register. */ + } SYSFAULTADDR_b; + } ; + + union { + __IOM uint32_t FAULTSTATUS; /*!< (@ 0x000001CC) Reflects the status of the bus decoders' fault + detection. Any write to this register will + clear all of the status bits within the + register. */ + + struct { + __IOM uint32_t ICODEFAULT : 1; /*!< [0..0] The ICODE Bus Decoder Fault Detected bit. When set, a + fault has been detected, and the ICODEFAULTADDR register + will contain the bus address which generated the fault. */ + __IOM uint32_t DCODEFAULT : 1; /*!< [1..1] DCODE Bus Decoder Fault Detected bit. When set, a fault + has been detected, and the DCODEFAULTADDR register will + contain the bus address which generated the fault. */ + __IOM uint32_t SYSFAULT : 1; /*!< [2..2] SYS Bus Decoder Fault Detected bit. When set, a fault + has been detected, and the SYSFAULTADDR register will contain + the bus address which generated the fault. */ + } FAULTSTATUS_b; + } ; + + union { + __IOM uint32_t FAULTCAPTUREEN; /*!< (@ 0x000001D0) Enable the fault capture registers */ + + struct { + __IOM uint32_t FAULTCAPTUREEN : 1; /*!< [0..0] Fault Capture Enable field. When set, the Fault Capture + monitors are enabled and addresses which generate a hard + fault are captured into the FAULTADDR registers. */ + } FAULTCAPTUREEN_b; + } ; + __IM uint32_t RESERVED8[11]; + + union { + __IOM uint32_t DBGR1; /*!< (@ 0x00000200) Read-only debug register 1 */ + + struct { + __IOM uint32_t ONETO8 : 32; /*!< [31..0] Read-only register for communication validation */ + } DBGR1_b; + } ; + + union { + __IOM uint32_t DBGR2; /*!< (@ 0x00000204) Read-only debug register 2 */ + + struct { + __IOM uint32_t COOLCODE : 32; /*!< [31..0] Read-only register for communication validation */ + } DBGR2_b; + } ; + __IM uint32_t RESERVED9[6]; + + union { + __IOM uint32_t PMUENABLE; /*!< (@ 0x00000220) Control bit to enable/disable the PMU */ + + struct { + __IOM uint32_t ENABLE : 1; /*!< [0..0] PMU Enable Control bit. When set, the MCU's PMU will + place the MCU into the lowest power consuming Deep Sleep + mode upon execution of a WFI instruction (dependent on + the setting of the SLEEPDEEP bit in the ARM SCR register). + When cleared, regardless of the requested sleep mode, the + PMU will not enter the lowest power Deep Sleep mode, instead + entering the Sleep mode. */ + } PMUENABLE_b; + } ; + __IM uint32_t RESERVED10[11]; + + union { + __IOM uint32_t TPIUCTRL; /*!< (@ 0x00000250) TPIU Control Register. Determines the clock enable + and frequency for the M4's TPIU interface. */ + + struct { + __IOM uint32_t ENABLE : 1; /*!< [0..0] TPIU Enable field. When set, the ARM M4 TPIU is enabled + and data can be streamed out of the MCU's SWO port using + the ARM ITM and TPIU modules. */ + __IM uint32_t : 7; + __IOM uint32_t CLKSEL : 3; /*!< [10..8] This field selects the frequency of the ARM M4 TPIU + port. */ + } TPIUCTRL_b; + } ; + __IM uint32_t RESERVED11[4]; + + union { + __IOM uint32_t OTAPOINTER; /*!< (@ 0x00000264) OTA (Over the Air) Update Pointer/Status. Reset + only by POA */ + + struct { + __IOM uint32_t OTAVALID : 1; /*!< [0..0] Indicates that an OTA update is valid */ + __IOM uint32_t OTASBLUPDATE : 1; /*!< [1..1] Indicates that the sbl_init has been updated */ + __IOM uint32_t OTAPOINTER : 30; /*!< [31..2] Flash page pointer with updated OTA image */ + } OTAPOINTER_b; + } ; + __IM uint32_t RESERVED12[6]; + + union { + __IOM uint32_t APBDMACTRL; /*!< (@ 0x00000280) DMA Control Register. Determines misc settings + for DMA operation */ + + struct { + __IOM uint32_t DMA_ENABLE : 1; /*!< [0..0] Enable the DMA controller. When disabled, DMA requests + will be ignored by the controller */ + __IOM uint32_t DECODEABORT : 1; /*!< [1..1] APB Decode Abort. When set, the APB bridge will issue + a data abort (bus fault) on transactions to peripherals + that are powered down. When set to 0, writes are quietly + discarded and reads return 0. */ + __IM uint32_t : 6; + __IOM uint32_t HYSTERESIS : 8; /*!< [15..8] This field determines how long the DMA will remain active + during deep sleep before shutting down and returning the + system to full deep sleep. Values are based on a 94KHz + clock and are roughly 10us increments for a range of ~10us + to 2.55ms */ + } APBDMACTRL_b; + } ; + + union { + __IOM uint32_t SRAMMODE; /*!< (@ 0x00000284) SRAM Controller mode bits */ + + struct { + __IOM uint32_t IPREFETCH : 1; /*!< [0..0] When set, instruction accesses to the SRAM banks will + be prefetched (normally 2 cycle read access). Generally, + this mode bit should be set for improved performance when + executing instructions from SRAM. */ + __IOM uint32_t IPREFETCH_CACHE : 1; /*!< [1..1] Secondary prefetch feature that will cache prefetched + data across bus waitstates (requires IPREFETCH to be set). */ + __IM uint32_t : 2; + __IOM uint32_t DPREFETCH : 1; /*!< [4..4] When set, data bus accesses to the SRAM banks will be + prefetched (normally 2 cycle read access). Use of this + mode bit is only recommended if the work flow has a large + number of sequential accesses. */ + __IOM uint32_t DPREFETCH_CACHE : 1; /*!< [5..5] Secondary prefetch feature that will cache prefetched + data across bus waitstates (requires DPREFETCH to be set). */ + } SRAMMODE_b; + } ; + __IM uint32_t RESERVED13[48]; + + union { + __IOM uint32_t KEXTCLKSEL; /*!< (@ 0x00000348) Key Register to enable the use of external clock + selects via the EXTCLKSEL reg */ + + struct { + __IOM uint32_t KEXTCLKSEL : 32; /*!< [31..0] Key register value. */ + } KEXTCLKSEL_b; + } ; + __IM uint32_t RESERVED14[4]; + + union { + __IOM uint32_t SIMOBUCK4; /*!< (@ 0x0000035C) SIMO Buck Control Reg1 */ + + struct { + __IOM uint32_t SIMOBUCKMEMLPLOWTONTRIM : 4;/*!< [3..0] simobuck_mem_lp_low_ton_trim */ + __IOM uint32_t SIMOBUCKMEMACTDRVSTRTRIM : 2;/*!< [5..4] simobuck_mem_act_drvstr_trim */ + __IOM uint32_t SIMOBUCKMEMLPDRVSTRTRIM : 2;/*!< [7..6] simobuck_mem_lp_drvstr_trim */ + __IOM uint32_t SIMOBUCKMEMLEAKAGETRIM : 2;/*!< [9..8] simobuck_mem_leakage_trim */ + __IOM uint32_t SIMOBUCKZXTRIM : 4; /*!< [13..10] simobuck_zx_trim */ + __IOM uint32_t SIMOBUCKUVLOCNTRTRIM : 3; /*!< [16..14] simobuck_uvlo_cntr_trim */ + __IOM uint32_t SIMOBUCKUVLODRVSTRTRIM : 3;/*!< [19..17] simobuck_uvlo_drvstr_trim */ + __IOM uint32_t SIMOBUCKEXTCLKSEL : 1; /*!< [20..20] simobuck_extclk_sel */ + __IOM uint32_t SIMOBUCKCLKDIVSEL : 2; /*!< [22..21] simobuck_clkdiv_sel */ + __IOM uint32_t SIMOBUCKCOMP2LPEN : 1; /*!< [23..23] simobuck_comp2_lp_en */ + __IOM uint32_t SIMOBUCKCOMP2TIMEOUTEN : 1;/*!< [24..24] simobuck_comp2_timeout_en */ + __IOM uint32_t SIMOBUCKPRIORITYSEL : 1; /*!< [25..25] simobuck_priority_sel */ + __IOM uint32_t SIMOBUCKUVLOMODE : 2; /*!< [27..26] simobuck_uvlo_mode */ + __IOM uint32_t SIMOBUCKIBIASTRIM : 4; /*!< [31..28] simobuck_bias_trim */ + } SIMOBUCK4_b; + } ; + __IM uint32_t RESERVED15[2]; + + union { + __IOM uint32_t BLEBUCK2; /*!< (@ 0x00000368) BLEBUCK2 Control Reg */ + + struct { + __IOM uint32_t BLEBUCKTONLOWTRIM : 6; /*!< [5..0] blebuck_ton_low_trim */ + __IOM uint32_t BLEBUCKTONHITRIM : 6; /*!< [11..6] blebuck_ton_hi_trim */ + __IOM uint32_t BLEBUCKTOND2ATRIM : 6; /*!< [17..12] blebuck_ton_trim */ + } BLEBUCK2_b; + } ; + __IM uint32_t RESERVED16[13]; + + union { + __IOM uint32_t FLASHWPROT0; /*!< (@ 0x000003A0) Flash Write Protection Bits */ + + struct { + __IOM uint32_t FW0BITS : 32; /*!< [31..0] Write protect flash 0x00000000 - 0x0007FFFF. Each bit + provides write protection for 16KB chunks of flash data + space. Bits are cleared by writing a 1 to the bit. When + read, 0 indicates the region is protected. Bits are sticky + (can be set when PROTLOCK is 1, but only cleared by reset) */ + } FLASHWPROT0_b; + } ; + + union { + __IOM uint32_t FLASHWPROT1; /*!< (@ 0x000003A4) Flash Write Protection Bits */ + + struct { + __IOM uint32_t FW1BITS : 32; /*!< [31..0] Write protect flash 0x00080000 - 0x000FFFFF. Each bit + provides write protection for 16KB chunks of flash data + space. Bits are cleared by writing a 1 to the bit. When + read, 0 indicates the region is protected. Bits are sticky + (can be set when PROTLOCK is 1, but only cleared by reset) */ + } FLASHWPROT1_b; + } ; + __IM uint32_t RESERVED17[2]; + + union { + __IOM uint32_t FLASHRPROT0; /*!< (@ 0x000003B0) Flash Read Protection Bits */ + + struct { + __IOM uint32_t FR0BITS : 32; /*!< [31..0] Copy (read) protect flash 0x00000000 - 0x0007FFFF. Each + bit provides read protection for 16KB chunks of flash. + Bits are cleared by writing a 1 to the bit. When read, + 0 indicates the region is protected. Bits are sticky (can + be set when PROTLOCK is 1, but only cleared by reset) */ + } FLASHRPROT0_b; + } ; + + union { + __IOM uint32_t FLASHRPROT1; /*!< (@ 0x000003B4) Flash Read Protection Bits */ + + struct { + __IOM uint32_t FR1BITS : 32; /*!< [31..0] Copy (read) protect flash 0x00080000 - 0x000FFFFF. Each + bit provides read protection for 16KB chunks of flash. + Bits are cleared by writing a 1 to the bit. When read, + 0 indicates the region is protected. Bits are sticky (can + be set when PROTLOCK is 1, but only cleared by reset) */ + } FLASHRPROT1_b; + } ; + __IM uint32_t RESERVED18[2]; + + union { + __IOM uint32_t DMASRAMWRITEPROTECT0; /*!< (@ 0x000003C0) SRAM write-protection bits. */ + + struct { + __IOM uint32_t DMA_WPROT0 : 32; /*!< [31..0] Write protect SRAM from DMA. Each bit provides write + protection for an 8KB region of memory. When set to 1, + the region will be protected from DMA writes, when set + to 0, DMA may write the region. */ + } DMASRAMWRITEPROTECT0_b; + } ; + + union { + __IOM uint32_t DMASRAMWRITEPROTECT1; /*!< (@ 0x000003C4) SRAM write-protection bits. */ + + struct { + __IOM uint32_t DMA_WPROT1 : 16; /*!< [15..0] Write protect SRAM from DMA. Each bit provides write + protection for an 8KB region of memory. When set to 1, + the region will be protected from DMA writes, when set + to 0, DMA may write the region. */ + } DMASRAMWRITEPROTECT1_b; + } ; + __IM uint32_t RESERVED19[2]; + + union { + __IOM uint32_t DMASRAMREADPROTECT0; /*!< (@ 0x000003D0) SRAM read-protection bits. */ + + struct { + __IOM uint32_t DMA_RPROT0 : 32; /*!< [31..0] Read protect SRAM from DMA. Each bit provides write + protection for an 8KB region of memory. When set to 1, + the region will be protected from DMA reads, when set to + 0, DMA may read the region. */ + } DMASRAMREADPROTECT0_b; + } ; + + union { + __IOM uint32_t DMASRAMREADPROTECT1; /*!< (@ 0x000003D4) SRAM read-protection bits. */ + + struct { + __IOM uint32_t DMA_RPROT1 : 16; /*!< [15..0] Read protect SRAM from DMA. Each bit provides write + protection for an 8KB region of memory. When set to 1, + the region will be protected from DMA reads, when set to + 0, DMA may read the region. */ + } DMASRAMREADPROTECT1_b; + } ; +} MCUCTRL_Type; /*!< Size = 984 (0x3d8) */ + + + +/* =========================================================================================================================== */ +/* ================ MSPI ================ */ +/* =========================================================================================================================== */ + + +/** + * @brief Multibit SPI Master (MSPI) + */ + +typedef struct { /*!< (@ 0x50014000) MSPI Structure */ + + union { + __IOM uint32_t CTRL; /*!< (@ 0x00000000) MSPI PIO Transfer Control/Status Register */ + + struct { + __IOM uint32_t START : 1; /*!< [0..0] Write to 1 to initiate a PIO transaction on the bus (typically + the entire register should be written at once with this + bit set). */ + __IOM uint32_t STATUS : 1; /*!< [1..1] Command status: 1 indicates command has completed. Cleared + by writing 1 to this bit or starting a new transfer. */ + __IOM uint32_t BUSY : 1; /*!< [2..2] Command status: 1 indicates controller is busy (command + in progress) */ + __IOM uint32_t QUADCMD : 1; /*!< [3..3] Flag indicating that the operation is a command that + should be replicated to both devices in paired QUAD mode. + This is typically only used when reading/writing configuration + registers in paired flash devices (do not set for memory + transfers). */ + __IM uint32_t : 1; + __IOM uint32_t CONT : 1; /*!< [5..5] Continuation transfer. When 1, indicates that the MSPI + will hold CE low after the transaction completes. This + is included for compatibility with IOM module since the + MSPI transfer module can handle most cases in a single + transfer. NOTE: CONT functionality only works with CLKDIV=2 + (24 MHz). */ + __IOM uint32_t BIGENDIAN : 1; /*!< [6..6] 1 indicates data in FIFO is in big endian format (MSB + first); 0 indicates little endian data (default, LSB first). */ + __IOM uint32_t ENTURN : 1; /*!< [7..7] Indicates whether TX->RX turnaround cycles should be + enabled for this operation (see TURNAROUND field in CFG + register). */ + __IOM uint32_t SENDA : 1; /*!< [8..8] Indicates whether an address phase should be sent (see + ADDR register and ASIZE field in CFG register) */ + __IOM uint32_t SENDI : 1; /*!< [9..9] Indicates whether an instruction phase should be sent + (see INSTR field and ISIZE field in CFG register) */ + __IOM uint32_t TXRX : 1; /*!< [10..10] 1 Indicates a TX operation, 0 indicates an RX operation + of XFERBYTES */ + __IOM uint32_t PIOSCRAMBLE : 1; /*!< [11..11] Enables data scrambling for PIO opertions. This should + only be used for data operations and never for commands + to a device. */ + __IM uint32_t : 4; + __IOM uint32_t XFERBYTES : 16; /*!< [31..16] Number of bytes to transmit or receive (based on TXRX + bit) */ + } CTRL_b; + } ; + + union { + __IOM uint32_t CFG; /*!< (@ 0x00000004) MSPI Transfer Configuration Register */ + + struct { + __IOM uint32_t DEVCFG : 4; /*!< [3..0] Flash configuration for XIP and AUTO DMA operations. + Controls value for SER (Slave Enable) for XIP operations + and address generation for DMA/XIP modes. Also used to + configure SPIFRF (frame format). */ + __IOM uint32_t ASIZE : 2; /*!< [5..4] Address Size. Address bytes to send from ADDR register + name = A1 value = 0x0 desc = Send one address byteenum + name = A2 value = 0x1 desc = Send two address bytesenum + name = A3 value = 0x2 desc = Send three address bytesenum + name = A4 value = 0x3 desc = Send four address bytes */ + __IOM uint32_t ISIZE : 1; /*!< [6..6] Instruction Sizeenum name = I8 value = 0x0 desc = Instruction + is 1 byteenum name = I16 value = 0x1 desc = Instruction + is 2 bytes */ + __IOM uint32_t SEPIO : 1; /*!< [7..7] Separate IO configuration. This bit should be set when + the target device has separate MOSI and MISO pins. Respective + IN/OUT bits below should be set to map pins. */ + __IOM uint32_t TURNAROUND : 6; /*!< [13..8] Number of turnaound cycles (for TX->RX transitions). + Qualified by ENTURN or XIPENTURN bit field. */ + __IM uint32_t : 2; + __IOM uint32_t CPHA : 1; /*!< [16..16] Serial clock phase. */ + __IOM uint32_t CPOL : 1; /*!< [17..17] Serial clock polarity. */ + } CFG_b; + } ; + + union { + __IOM uint32_t ADDR; /*!< (@ 0x00000008) MSPI Transfer Address Register */ + + struct { + __IOM uint32_t ADDR : 32; /*!< [31..0] Optional Address field to send (after optional instruction + field) - qualified by ASIZE in CMD register. NOTE: This + register is aliased to DMADEVADDR. */ + } ADDR_b; + } ; + + union { + __IOM uint32_t INSTR; /*!< (@ 0x0000000C) MSPI Transfer Instruction */ + + struct { + __IOM uint32_t INSTR : 16; /*!< [15..0] Optional Instruction field to send (1st byte) - qualified + by ISEND/ISIZE */ + } INSTR_b; + } ; + + union { + __IOM uint32_t TXFIFO; /*!< (@ 0x00000010) TX Data FIFO */ + + struct { + __IOM uint32_t TXFIFO : 32; /*!< [31..0] Data to be transmitted. Data should normall be aligned + to the LSB (pad the upper bits with zeros) unless BIGENDIAN + is set. */ + } TXFIFO_b; + } ; + + union { + __IOM uint32_t RXFIFO; /*!< (@ 0x00000014) RX Data FIFO */ + + struct { + __IOM uint32_t RXFIFO : 32; /*!< [31..0] Receive data. Data is aligned to the LSB (padded zeros + on upper bits) unless BIGENDIAN is set. */ + } RXFIFO_b; + } ; + + union { + __IOM uint32_t TXENTRIES; /*!< (@ 0x00000018) TX FIFO Entries */ + + struct { + __IOM uint32_t TXENTRIES : 5; /*!< [4..0] Number of 32-bit words/entries in TX FIFO */ + } TXENTRIES_b; + } ; + + union { + __IOM uint32_t RXENTRIES; /*!< (@ 0x0000001C) RX FIFO Entries */ + + struct { + __IOM uint32_t RXENTRIES : 5; /*!< [4..0] Number of 32-bit words/entries in RX FIFO */ + } RXENTRIES_b; + } ; + + union { + __IOM uint32_t THRESHOLD; /*!< (@ 0x00000020) TX/RX FIFO Threshhold Levels */ + + struct { + __IOM uint32_t TXTHRESH : 5; /*!< [4..0] Number of entries in TX FIFO that cause TXF interrupt */ + __IM uint32_t : 3; + __IOM uint32_t RXTHRESH : 5; /*!< [12..8] Number of entries in TX FIFO that cause RXE interrupt */ + } THRESHOLD_b; + } ; + __IM uint32_t RESERVED[55]; + + union { + __IOM uint32_t MSPICFG; /*!< (@ 0x00000100) MSPI Module Configuration */ + + struct { + __IOM uint32_t APBCLK : 1; /*!< [0..0] Enable continuous APB clock. For power-efficient operation, + APBCLK should be set to 0. */ + __IOM uint32_t RXCAP : 1; /*!< [1..1] Controls RX data capture phase. A setting of 0 (NORMAL) + captures read data at the normal capture point relative + to the internal clock launch point. However, to accomodate + chip/pad/board delays, a setting of RXCAP of 1 is expected + to be used to align the capture point with the return data + window. This bit is used in conjunction with RXNEG to provide + 4 unique capture points, all about 10ns apart. */ + __IOM uint32_t RXNEG : 1; /*!< [2..2] Adjusts the RX capture phase to the negedge of the 48MHz + internal clock (~10ns early). For normal operation, it + is expected that RXNEG will be set to 0. */ + __IOM uint32_t TXNEG : 1; /*!< [3..3] Launches TX data a half clock cycle (~10ns) early. This + should normally be programmed to zero (NORMAL). */ + __IOM uint32_t IOMSEL : 3; /*!< [6..4] Selects which IOM is selected for CQ handshake status. */ + __IM uint32_t : 1; + __IOM uint32_t CLKDIV : 6; /*!< [13..8] Clock Divider. Allows dividing 48 MHz base clock by + integer multiples. Enumerations are provided for common + frequency, but any integer divide from 48 MHz is allowed. + Odd divide ratios will result in a 33/66 percent duty cycle + with a long low clock pulse (to allow longer round-trip + for read data). */ + __IM uint32_t : 15; + __IOM uint32_t FIFORESET : 1; /*!< [29..29] Reset MSPI FIFO (active high). 1=reset FIFO, 0=normal + operation. May be used to manually flush the FIFO in error + handling. */ + __IOM uint32_t IPRSTN : 1; /*!< [30..30] IP block reset. Write to 0 to put the transfer module + in reset or 1 for normal operation. This may be required + after error conditions to clear the transfer on the bus. */ + __IOM uint32_t PRSTN : 1; /*!< [31..31] Peripheral reset. Master reset to the entire MSPI module + (DMA, XIP, and transfer state machines). 1=normal operation, + 0=in reset. */ + } MSPICFG_b; + } ; + + union { + __IOM uint32_t PADCFG; /*!< (@ 0x00000104) MSPI Output Pad Configuration */ + + struct { + __IOM uint32_t OUT3 : 1; /*!< [0..0] Output pad 3 configuration. 0=data[3] 1=CLK */ + __IOM uint32_t OUT4 : 1; /*!< [1..1] Output pad 4 configuration. 0=data[4] 1=data[0] */ + __IOM uint32_t OUT5 : 1; /*!< [2..2] Output pad 5 configuration. 0=data[5] 1=data[1] */ + __IOM uint32_t OUT6 : 1; /*!< [3..3] Output pad 6 configuration. 0=data[6] 1=data[2] */ + __IOM uint32_t OUT7 : 1; /*!< [4..4] Output pad 7 configuration. 0=data[7] 1=data[3] */ + __IM uint32_t : 11; + __IOM uint32_t IN0 : 2; /*!< [17..16] Data Input pad 0 pin muxing: 0=pad[0] 1=pad[4] 2=pad[1] + 3=pad[5] */ + __IOM uint32_t IN1 : 1; /*!< [18..18] Data Input pad 1 pin muxing: 0=pad[1] 1=pad[5] */ + __IOM uint32_t IN2 : 1; /*!< [19..19] Data Input pad 2 pin muxing: 0=pad[2] 1=pad[6] */ + __IOM uint32_t IN3 : 1; /*!< [20..20] Data Input pad 3 pin muxing: 0=pad[3] 1=pad[7] */ + __IOM uint32_t REVCS : 1; /*!< [21..21] Reverse CS connections. Allows CS1 to be associated + with lower data lanes and CS0 to be associated with upper + data lines */ + } PADCFG_b; + } ; + + union { + __IOM uint32_t PADOUTEN; /*!< (@ 0x00000108) MSPI Output Enable Pad Configuration */ + + struct { + __IOM uint32_t OUTEN : 9; /*!< [8..0] Output pad enable configuration. Indicates which pads + should be driven. Bits [3:0] are Quad0 data, [7:4] are + Quad1 data, and [8] is clock. */ + } PADOUTEN_b; + } ; + + union { + __IOM uint32_t FLASH; /*!< (@ 0x0000010C) Configuration for XIP/DMA support of SPI flash + modules. */ + + struct { + __IOM uint32_t XIPEN : 1; /*!< [0..0] Enable the XIP (eXecute In Place) function which effectively + enables the address decoding of the MSPI device in the + flash/cache address space at address 0x04000000-0x07FFFFFF. */ + __IM uint32_t : 1; + __IOM uint32_t XIPACK : 2; /*!< [3..2] Controls transmission of Micron XIP acknowledge cycles + (Micron Flash devices only) */ + __IOM uint32_t XIPBIGENDIAN : 1; /*!< [4..4] Indicates whether XIP/AUTO DMA data transfers are in + big or little endian format */ + __IOM uint32_t XIPENTURN : 1; /*!< [5..5] Indicates whether XIP/AUTO DMA operations should enable + TX->RX turnaround cycles */ + __IOM uint32_t XIPSENDA : 1; /*!< [6..6] Indicates whether XIP/AUTO DMA operations should send + an an address phase (see DMADEVADDR register and ASIZE + field in CFG) */ + __IOM uint32_t XIPSENDI : 1; /*!< [7..7] Indicates whether XIP/AUTO DMA operations should send + an instruction (see READINSTR field and ISIZE field in + CFG) */ + __IOM uint32_t XIPMIXED : 3; /*!< [10..8] Reserved. Set to 0x0 */ + __IM uint32_t : 5; + __IOM uint32_t WRITEINSTR : 8; /*!< [23..16] Write command sent for DMA operations */ + __IOM uint32_t READINSTR : 8; /*!< [31..24] Read command sent to flash for DMA/XIP operations */ + } FLASH_b; + } ; + __IM uint32_t RESERVED1[4]; + + union { + __IOM uint32_t SCRAMBLING; /*!< (@ 0x00000120) External Flash Scrambling Controls */ + + struct { + __IOM uint32_t SCRSTART : 10; /*!< [9..0] Scrambling region start address [25:16] (64K block granularity). + The START block is the FIRST block included in the scrambled + address range. */ + __IM uint32_t : 6; + __IOM uint32_t SCREND : 10; /*!< [25..16] Scrambling region end address [25:16] (64K block granularity). + The END block is the LAST block included in the scrambled + address range. */ + __IM uint32_t : 5; + __IOM uint32_t SCRENABLE : 1; /*!< [31..31] Enables Data Scrambling Region. When 1 reads and writes + to the range will be scrambled. When 0, data will be read/written + unmodified. Address range is specified in 64K granularity + and the START/END ranges are included within the range. */ + } SCRAMBLING_b; + } ; + __IM uint32_t RESERVED2[55]; + + union { + __IOM uint32_t INTEN; /*!< (@ 0x00000200) MSPI Master Interrupts: Enable */ + + struct { + __IOM uint32_t CMDCMP : 1; /*!< [0..0] Transfer complete. Note that DMA and CQ operations are + layered, so CMDCMP, DCMP, and CQ* can all be signalled + simultaneously */ + __IOM uint32_t TXE : 1; /*!< [1..1] Transmit FIFO empty. */ + __IOM uint32_t TXO : 1; /*!< [2..2] Transmit FIFO Overflow (only occurs when SW writes to + a full FIFO). */ + __IOM uint32_t RXU : 1; /*!< [3..3] Receive FIFO underflow (only occurs when SW reads from + an empty FIFO) */ + __IOM uint32_t RXO : 1; /*!< [4..4] Receive FIFO overflow (cannot happen in MSPI design -- + MSPI bus pins will stall) */ + __IOM uint32_t RXF : 1; /*!< [5..5] Receive FIFO full */ + __IOM uint32_t DCMP : 1; /*!< [6..6] DMA Complete Interrupt */ + __IOM uint32_t DERR : 1; /*!< [7..7] DMA Error Interrupt */ + __IOM uint32_t CQCMP : 1; /*!< [8..8] Command Queue Complete Interrupt */ + __IOM uint32_t CQUPD : 1; /*!< [9..9] Command Queue Update Interrupt. Issued whenever the CQ + performs an operation where address bit[0] is set. Useful + for triggering CURIDX interrupts. */ + __IOM uint32_t CQPAUSED : 1; /*!< [10..10] Command Queue is Paused. */ + __IOM uint32_t CQERR : 1; /*!< [11..11] Command Queue Error Interrupt */ + __IOM uint32_t SCRERR : 1; /*!< [12..12] Scrambling Alignment Error. Scrambling operations must + be aligned to word (4-byte) start address. */ + } INTEN_b; + } ; + + union { + __IOM uint32_t INTSTAT; /*!< (@ 0x00000204) MSPI Master Interrupts: Status */ + + struct { + __IOM uint32_t CMDCMP : 1; /*!< [0..0] Transfer complete. Note that DMA and CQ operations are + layered, so CMDCMP, DCMP, and CQ* can all be signalled + simultaneously */ + __IOM uint32_t TXE : 1; /*!< [1..1] Transmit FIFO empty. */ + __IOM uint32_t TXO : 1; /*!< [2..2] Transmit FIFO Overflow (only occurs when SW writes to + a full FIFO). */ + __IOM uint32_t RXU : 1; /*!< [3..3] Receive FIFO underflow (only occurs when SW reads from + an empty FIFO) */ + __IOM uint32_t RXO : 1; /*!< [4..4] Receive FIFO overflow (cannot happen in MSPI design -- + MSPI bus pins will stall) */ + __IOM uint32_t RXF : 1; /*!< [5..5] Receive FIFO full */ + __IOM uint32_t DCMP : 1; /*!< [6..6] DMA Complete Interrupt */ + __IOM uint32_t DERR : 1; /*!< [7..7] DMA Error Interrupt */ + __IOM uint32_t CQCMP : 1; /*!< [8..8] Command Queue Complete Interrupt */ + __IOM uint32_t CQUPD : 1; /*!< [9..9] Command Queue Update Interrupt. Issued whenever the CQ + performs an operation where address bit[0] is set. Useful + for triggering CURIDX interrupts. */ + __IOM uint32_t CQPAUSED : 1; /*!< [10..10] Command Queue is Paused. */ + __IOM uint32_t CQERR : 1; /*!< [11..11] Command Queue Error Interrupt */ + __IOM uint32_t SCRERR : 1; /*!< [12..12] Scrambling Alignment Error. Scrambling operations must + be aligned to word (4-byte) start address. */ + } INTSTAT_b; + } ; + + union { + __IOM uint32_t INTCLR; /*!< (@ 0x00000208) MSPI Master Interrupts: Clear */ + + struct { + __IOM uint32_t CMDCMP : 1; /*!< [0..0] Transfer complete. Note that DMA and CQ operations are + layered, so CMDCMP, DCMP, and CQ* can all be signalled + simultaneously */ + __IOM uint32_t TXE : 1; /*!< [1..1] Transmit FIFO empty. */ + __IOM uint32_t TXO : 1; /*!< [2..2] Transmit FIFO Overflow (only occurs when SW writes to + a full FIFO). */ + __IOM uint32_t RXU : 1; /*!< [3..3] Receive FIFO underflow (only occurs when SW reads from + an empty FIFO) */ + __IOM uint32_t RXO : 1; /*!< [4..4] Receive FIFO overflow (cannot happen in MSPI design -- + MSPI bus pins will stall) */ + __IOM uint32_t RXF : 1; /*!< [5..5] Receive FIFO full */ + __IOM uint32_t DCMP : 1; /*!< [6..6] DMA Complete Interrupt */ + __IOM uint32_t DERR : 1; /*!< [7..7] DMA Error Interrupt */ + __IOM uint32_t CQCMP : 1; /*!< [8..8] Command Queue Complete Interrupt */ + __IOM uint32_t CQUPD : 1; /*!< [9..9] Command Queue Update Interrupt. Issued whenever the CQ + performs an operation where address bit[0] is set. Useful + for triggering CURIDX interrupts. */ + __IOM uint32_t CQPAUSED : 1; /*!< [10..10] Command Queue is Paused. */ + __IOM uint32_t CQERR : 1; /*!< [11..11] Command Queue Error Interrupt */ + __IOM uint32_t SCRERR : 1; /*!< [12..12] Scrambling Alignment Error. Scrambling operations must + be aligned to word (4-byte) start address. */ + } INTCLR_b; + } ; + + union { + __IOM uint32_t INTSET; /*!< (@ 0x0000020C) MSPI Master Interrupts: Set */ + + struct { + __IOM uint32_t CMDCMP : 1; /*!< [0..0] Transfer complete. Note that DMA and CQ operations are + layered, so CMDCMP, DCMP, and CQ* can all be signalled + simultaneously */ + __IOM uint32_t TXE : 1; /*!< [1..1] Transmit FIFO empty. */ + __IOM uint32_t TXO : 1; /*!< [2..2] Transmit FIFO Overflow (only occurs when SW writes to + a full FIFO). */ + __IOM uint32_t RXU : 1; /*!< [3..3] Receive FIFO underflow (only occurs when SW reads from + an empty FIFO) */ + __IOM uint32_t RXO : 1; /*!< [4..4] Receive FIFO overflow (cannot happen in MSPI design -- + MSPI bus pins will stall) */ + __IOM uint32_t RXF : 1; /*!< [5..5] Receive FIFO full */ + __IOM uint32_t DCMP : 1; /*!< [6..6] DMA Complete Interrupt */ + __IOM uint32_t DERR : 1; /*!< [7..7] DMA Error Interrupt */ + __IOM uint32_t CQCMP : 1; /*!< [8..8] Command Queue Complete Interrupt */ + __IOM uint32_t CQUPD : 1; /*!< [9..9] Command Queue Update Interrupt. Issued whenever the CQ + performs an operation where address bit[0] is set. Useful + for triggering CURIDX interrupts. */ + __IOM uint32_t CQPAUSED : 1; /*!< [10..10] Command Queue is Paused. */ + __IOM uint32_t CQERR : 1; /*!< [11..11] Command Queue Error Interrupt */ + __IOM uint32_t SCRERR : 1; /*!< [12..12] Scrambling Alignment Error. Scrambling operations must + be aligned to word (4-byte) start address. */ + } INTSET_b; + } ; + __IM uint32_t RESERVED3[16]; + + union { + __IOM uint32_t DMACFG; /*!< (@ 0x00000250) DMA Configuration Register */ + + struct { + __IOM uint32_t DMAEN : 2; /*!< [1..0] DMA Enable. Setting this bit to EN will start the DMA + operation */ + __IOM uint32_t DMADIR : 1; /*!< [2..2] Direction */ + __IOM uint32_t DMAPRI : 2; /*!< [4..3] Sets the Priority of the DMA request */ + __IM uint32_t : 13; + __IOM uint32_t DMAPWROFF : 1; /*!< [18..18] Power off MSPI domain upon completion of DMA operation. */ + } DMACFG_b; + } ; + + union { + __IOM uint32_t DMASTAT; /*!< (@ 0x00000254) DMA Status Register */ + + struct { + __IOM uint32_t DMATIP : 1; /*!< [0..0] DMA Transfer In Progress indicator. 1 will indicate that + a DMA transfer is active. The DMA transfer may be waiting + on data, transferring data, or waiting for priority. All + of these will be indicated with a 1. A 0 will indicate + that the DMA is fully complete and no further transactions + will be done. */ + __IOM uint32_t DMACPL : 1; /*!< [1..1] DMA Transfer Complete. This signals the end of the DMA + operation. */ + __IOM uint32_t DMAERR : 1; /*!< [2..2] DMA Error. This active high bit signals that an error + was encountered during the DMA operation. */ + __IOM uint32_t SCRERR : 1; /*!< [3..3] Scrambling Access Alignment Error. This active high bit + signals that a scrambling operation was specified for a + non-word aligned DEVADDR. */ + } DMASTAT_b; + } ; + + union { + __IOM uint32_t DMATARGADDR; /*!< (@ 0x00000258) DMA Target Address Register */ + + struct { + __IOM uint32_t TARGADDR : 32; /*!< [31..0] Target byte address for source of DMA (either read or + write). In cases of non-word aligned addresses, the DMA + logic will take care for ensuring only the target bytes + are read/written. */ + } DMATARGADDR_b; + } ; + + union { + __IOM uint32_t DMADEVADDR; /*!< (@ 0x0000025C) DMA Device Address Register */ + + struct { + __IOM uint32_t DEVADDR : 32; /*!< [31..0] SPI Device address for automated DMA transactions (both + read and write). */ + } DMADEVADDR_b; + } ; + + union { + __IOM uint32_t DMATOTCOUNT; /*!< (@ 0x00000260) DMA Total Transfer Count */ + + struct { + __IOM uint32_t TOTCOUNT : 16; /*!< [15..0] Total Transfer Count in bytes. */ + } DMATOTCOUNT_b; + } ; + + union { + __IOM uint32_t DMABCOUNT; /*!< (@ 0x00000264) DMA BYTE Transfer Count */ + + struct { + __IOM uint32_t BCOUNT : 8; /*!< [7..0] Burst transfer size in bytes. This is the number of bytes + transferred when a FIFO trigger event occurs. Recommended + values are 16 or 32. */ + } DMABCOUNT_b; + } ; + __IM uint32_t RESERVED4[4]; + + union { + __IOM uint32_t DMATHRESH; /*!< (@ 0x00000278) DMA Transmit Trigger Threshhold */ + + struct { + __IOM uint32_t DMATHRESH : 4; /*!< [3..0] DMA transfer FIFO level trigger. For read operations, + DMA is triggered when the FIFO level is greater than this + value. For write operations, DMA is triggered when the + FIFO level is less than this level. Each DMA operation + will consist of BCOUNT bytes. */ + } DMATHRESH_b; + } ; + __IM uint32_t RESERVED5[9]; + + union { + __IOM uint32_t CQCFG; /*!< (@ 0x000002A0) Command Queue Configuration Register */ + + struct { + __IOM uint32_t CQEN : 1; /*!< [0..0] Command queue enable. When set, will enable the processing + of the command queue */ + __IOM uint32_t CQPRI : 1; /*!< [1..1] Sets the Priority of the command queue dma request */ + __IOM uint32_t CQPWROFF : 1; /*!< [2..2] Power off MSPI domain upon completion of DMA operation. */ + __IOM uint32_t CQAUTOCLEARMASK : 1; /*!< [3..3] Eanble clear of CQMASK after each pause operation. This + may be useful when using software flags to pause CQ. */ + } CQCFG_b; + } ; + __IM uint32_t RESERVED6; + + union { + __IOM uint32_t CQADDR; /*!< (@ 0x000002A8) CQ Target Read Address Register */ + + struct { + __IOM uint32_t CQADDR : 29; /*!< [28..0] Address of command queue buffer in SRAM or flash. The + buffer address must be aligned to a word boundary. */ + } CQADDR_b; + } ; + + union { + __IOM uint32_t CQSTAT; /*!< (@ 0x000002AC) Command Queue Status Register */ + + struct { + __IOM uint32_t CQTIP : 1; /*!< [0..0] Command queue Transfer In Progress indicator. 1 will + indicate that a CQ transfer is active and this will remain + active even when paused waiting for external event. */ + __IOM uint32_t CQCPL : 1; /*!< [1..1] Command queue operation Complete. This signals the end + of the command queue operation. */ + __IOM uint32_t CQERR : 1; /*!< [2..2] Command queue processing Error. This active high bit + signals that an error was encountered during the CQ operation. */ + __IOM uint32_t CQPAUSED : 1; /*!< [3..3] Command queue is currently paused status. */ + } CQSTAT_b; + } ; + + union { + __IOM uint32_t CQFLAGS; /*!< (@ 0x000002B0) Command Queue Flag Register */ + + struct { + __IOM uint32_t CQFLAGS : 16; /*!< [15..0] Current flag status (read-only). Bits [7:0] are software + controllable and bits [15:8] are hardware status. */ + } CQFLAGS_b; + } ; + + union { + __IOM uint32_t CQSETCLEAR; /*!< (@ 0x000002B4) Command Queue Flag Set/Clear Register */ + + struct { + __IOM uint32_t CQFSET : 8; /*!< [7..0] Set CQFlag status bits. Set has priority over clear if + both are high. */ + __IOM uint32_t CQFTOGGLE : 8; /*!< [15..8] Toggle CQFlag status bits */ + __IOM uint32_t CQFCLR : 8; /*!< [23..16] Clear CQFlag status bits. */ + } CQSETCLEAR_b; + } ; + + union { + __IOM uint32_t CQPAUSE; /*!< (@ 0x000002B8) Command Queue Pause Mask Register */ + + struct { + __IOM uint32_t CQMASK : 16; /*!< [15..0] CQ will pause processing until all specified events + are satisfied. */ + } CQPAUSE_b; + } ; + __IM uint32_t RESERVED7; + + union { + __IOM uint32_t CQCURIDX; /*!< (@ 0x000002C0) Command Queue Current Index */ + + struct { + __IOM uint32_t CQCURIDX : 8; /*!< [7..0] Can be used to indicate the current position of the command + queue by having CQ operations write this field. A CQ hardware + status flag indicates when CURIDX and ENDIDX are not equal, + allowing SW to pause the CQ processing until the end index + is updated. */ + } CQCURIDX_b; + } ; + + union { + __IOM uint32_t CQENDIDX; /*!< (@ 0x000002C4) Command Queue End Index */ + + struct { + __IOM uint32_t CQENDIDX : 8; /*!< [7..0] Can be used to indicate the end position of the command + queue. A CQ hardware status bit indices when CURIDX != + ENDIDX so that the CQ can be paused when it reaches the + end pointer. */ + } CQENDIDX_b; + } ; +} MSPI_Type; /*!< Size = 712 (0x2c8) */ + + + +/* =========================================================================================================================== */ +/* ================ PDM ================ */ +/* =========================================================================================================================== */ + + +/** + * @brief PDM Audio (PDM) + */ + +typedef struct { /*!< (@ 0x50011000) PDM Structure */ + + union { + __IOM uint32_t PCFG; /*!< (@ 0x00000000) PDM Configuration Register */ + + struct { + __IOM uint32_t PDMCOREEN : 1; /*!< [0..0] Data Streaming Control. */ + __IOM uint32_t SOFTMUTE : 1; /*!< [1..1] Soft mute control. */ + __IOM uint32_t CYCLES : 3; /*!< [4..2] Number of clocks during gain-setting changes. */ + __IOM uint32_t HPCUTOFF : 4; /*!< [8..5] High pass filter coefficients. */ + __IOM uint32_t ADCHPD : 1; /*!< [9..9] High pass filter control. */ + __IOM uint32_t SINCRATE : 7; /*!< [16..10] SINC decimation rate. */ + __IOM uint32_t MCLKDIV : 2; /*!< [18..17] PDM_CLK frequency divisor. */ + __IM uint32_t : 2; + __IOM uint32_t PGALEFT : 5; /*!< [25..21] Left channel PGA gain. */ + __IOM uint32_t PGARIGHT : 5; /*!< [30..26] Right channel PGA gain. */ + __IOM uint32_t LRSWAP : 1; /*!< [31..31] Left/right channel swap. */ + } PCFG_b; + } ; + + union { + __IOM uint32_t VCFG; /*!< (@ 0x00000004) Voice Configuration Register */ + + struct { + __IM uint32_t : 3; + __IOM uint32_t CHSET : 2; /*!< [4..3] Set PCM channels. */ + __IM uint32_t : 3; + __IOM uint32_t PCMPACK : 1; /*!< [8..8] PCM data packing enable. */ + __IM uint32_t : 7; + __IOM uint32_t SELAP : 1; /*!< [16..16] Select PDM input clock source. */ + __IOM uint32_t DMICKDEL : 1; /*!< [17..17] PDM clock sampling delay. */ + __IM uint32_t : 1; + __IOM uint32_t BCLKINV : 1; /*!< [19..19] I2S BCLK input inversion. */ + __IOM uint32_t I2SEN : 1; /*!< [20..20] I2S interface enable. */ + __IM uint32_t : 5; + __IOM uint32_t PDMCLKEN : 1; /*!< [26..26] Enable the serial clock. */ + __IOM uint32_t PDMCLKSEL : 3; /*!< [29..27] Select the PDM input clock. */ + __IOM uint32_t RSTB : 1; /*!< [30..30] Reset the IP core. */ + __IOM uint32_t IOCLKEN : 1; /*!< [31..31] Enable the IO clock. */ + } VCFG_b; + } ; + + union { + __IOM uint32_t VOICESTAT; /*!< (@ 0x00000008) Voice Status Register */ + + struct { + __IOM uint32_t FIFOCNT : 6; /*!< [5..0] Valid 32-bit entries currently in the FIFO. */ + } VOICESTAT_b; + } ; + + union { + __IOM uint32_t FIFOREAD; /*!< (@ 0x0000000C) FIFO Read */ + + struct { + __IOM uint32_t FIFOREAD : 32; /*!< [31..0] FIFO read data. */ + } FIFOREAD_b; + } ; + + union { + __IOM uint32_t FIFOFLUSH; /*!< (@ 0x00000010) FIFO Flush */ + + struct { + __IOM uint32_t FIFOFLUSH : 1; /*!< [0..0] FIFO FLUSH. */ + } FIFOFLUSH_b; + } ; + + union { + __IOM uint32_t FIFOTHR; /*!< (@ 0x00000014) FIFO Threshold */ + + struct { + __IOM uint32_t FIFOTHR : 5; /*!< [4..0] FIFO Threshold value. When the FIFO count is equal to, + or larger than this value (in words), a THR interrupt is + generated (if enabled) */ + } FIFOTHR_b; + } ; + __IM uint32_t RESERVED[122]; + + union { + __IOM uint32_t INTEN; /*!< (@ 0x00000200) IO Master Interrupts: Enable */ + + struct { + __IOM uint32_t THR : 1; /*!< [0..0] This is the FIFO threshold interrupt. */ + __IOM uint32_t OVF : 1; /*!< [1..1] This is the FIFO overflow interrupt. */ + __IOM uint32_t UNDFL : 1; /*!< [2..2] This is the FIFO underflow interrupt. */ + __IOM uint32_t DCMP : 1; /*!< [3..3] DMA completed a transfer */ + __IOM uint32_t DERR : 1; /*!< [4..4] DMA Error receieved */ + } INTEN_b; + } ; + + union { + __IOM uint32_t INTSTAT; /*!< (@ 0x00000204) IO Master Interrupts: Status */ + + struct { + __IOM uint32_t THR : 1; /*!< [0..0] This is the FIFO threshold interrupt. */ + __IOM uint32_t OVF : 1; /*!< [1..1] This is the FIFO overflow interrupt. */ + __IOM uint32_t UNDFL : 1; /*!< [2..2] This is the FIFO underflow interrupt. */ + __IOM uint32_t DCMP : 1; /*!< [3..3] DMA completed a transfer */ + __IOM uint32_t DERR : 1; /*!< [4..4] DMA Error receieved */ + } INTSTAT_b; + } ; + + union { + __IOM uint32_t INTCLR; /*!< (@ 0x00000208) IO Master Interrupts: Clear */ + + struct { + __IOM uint32_t THR : 1; /*!< [0..0] This is the FIFO threshold interrupt. */ + __IOM uint32_t OVF : 1; /*!< [1..1] This is the FIFO overflow interrupt. */ + __IOM uint32_t UNDFL : 1; /*!< [2..2] This is the FIFO underflow interrupt. */ + __IOM uint32_t DCMP : 1; /*!< [3..3] DMA completed a transfer */ + __IOM uint32_t DERR : 1; /*!< [4..4] DMA Error receieved */ + } INTCLR_b; + } ; + + union { + __IOM uint32_t INTSET; /*!< (@ 0x0000020C) IO Master Interrupts: Set */ + + struct { + __IOM uint32_t THR : 1; /*!< [0..0] This is the FIFO threshold interrupt. */ + __IOM uint32_t OVF : 1; /*!< [1..1] This is the FIFO overflow interrupt. */ + __IOM uint32_t UNDFL : 1; /*!< [2..2] This is the FIFO underflow interrupt. */ + __IOM uint32_t DCMP : 1; /*!< [3..3] DMA completed a transfer */ + __IOM uint32_t DERR : 1; /*!< [4..4] DMA Error receieved */ + } INTSET_b; + } ; + __IM uint32_t RESERVED1[12]; + + union { + __IOM uint32_t DMATRIGEN; /*!< (@ 0x00000240) DMA Trigger Enable Register */ + + struct { + __IOM uint32_t DTHR : 1; /*!< [0..0] Trigger DMA upon when FIFO iss filled to level indicated + by the FIFO THRESHOLD,at granularity of 16 bytes only */ + __IOM uint32_t DTHR90 : 1; /*!< [1..1] Trigger DMA at FIFO 90 percent full. This signal is also + used internally for AUTOHIP function */ + } DMATRIGEN_b; + } ; + + union { + __IOM uint32_t DMATRIGSTAT; /*!< (@ 0x00000244) DMA Trigger Status Register */ + + struct { + __IOM uint32_t DTHRSTAT : 1; /*!< [0..0] Triggered DMA from FIFO reaching threshold */ + __IOM uint32_t DTHR90STAT : 1; /*!< [1..1] Triggered DMA from FIFO reaching 90 percent full */ + } DMATRIGSTAT_b; + } ; + __IM uint32_t RESERVED2[14]; + + union { + __IOM uint32_t DMACFG; /*!< (@ 0x00000280) DMA Configuration Register */ + + struct { + __IOM uint32_t DMAEN : 1; /*!< [0..0] DMA Enable */ + __IM uint32_t : 1; + __IOM uint32_t DMADIR : 1; /*!< [2..2] Direction */ + __IM uint32_t : 5; + __IOM uint32_t DMAPRI : 1; /*!< [8..8] Sets the Priority of the DMA request */ + __IOM uint32_t DAUTOHIP : 1; /*!< [9..9] Raise priority to high on fifo full, and DMAPRI set to + low */ + __IOM uint32_t DPWROFF : 1; /*!< [10..10] Power Off the ADC System upon DMACPL. */ + } DMACFG_b; + } ; + __IM uint32_t RESERVED3; + + union { + __IOM uint32_t DMATOTCOUNT; /*!< (@ 0x00000288) DMA Total Transfer Count */ + + struct { + __IOM uint32_t TOTCOUNT : 20; /*!< [19..0] Total Transfer Count. The transfer count must be a multiple + of the THR setting to avoid DMA overruns. */ + } DMATOTCOUNT_b; + } ; + + union { + __IOM uint32_t DMATARGADDR; /*!< (@ 0x0000028C) DMA Target Address Register */ + + struct { + __IOM uint32_t LTARGADDR : 20; /*!< [19..0] DMA Target Address. This register is not updated with + the current address of the DMA, but will remain static + with the original address during the DMA transfer. */ + __IOM uint32_t UTARGADDR : 12; /*!< [31..20] SRAM Target */ + } DMATARGADDR_b; + } ; + + union { + __IOM uint32_t DMASTAT; /*!< (@ 0x00000290) DMA Status Register */ + + struct { + __IOM uint32_t DMATIP : 1; /*!< [0..0] DMA Transfer In Progress */ + __IOM uint32_t DMACPL : 1; /*!< [1..1] DMA Transfer Complete */ + __IOM uint32_t DMAERR : 1; /*!< [2..2] DMA Error */ + } DMASTAT_b; + } ; +} PDM_Type; /*!< Size = 660 (0x294) */ + + + +/* =========================================================================================================================== */ +/* ================ PWRCTRL ================ */ +/* =========================================================================================================================== */ + + +/** + * @brief PWR Controller Register Bank (PWRCTRL) + */ + +typedef struct { /*!< (@ 0x40021000) PWRCTRL Structure */ + + union { + __IOM uint32_t SUPPLYSRC; /*!< (@ 0x00000000) Voltage Regulator Select Register */ + + struct { + __IOM uint32_t BLEBUCKEN : 1; /*!< [0..0] Enables and Selects the BLE Buck as the supply for the + BLE power domain or for Burst LDO. It takes the initial + value from Customer INFO space. Buck will be powered up + only if there is an active request for BLEH domain or Burst + mode and appropriate feature is allowed. */ + } SUPPLYSRC_b; + } ; + + union { + __IOM uint32_t SUPPLYSTATUS; /*!< (@ 0x00000004) Voltage Regulators status */ + + struct { + __IOM uint32_t SIMOBUCKON : 1; /*!< [0..0] Indicates whether the Core/Mem low-voltage domains are + supplied from the LDO or the Buck. */ + __IOM uint32_t BLEBUCKON : 1; /*!< [1..1] Indicates whether the BLE (if supported) domain and burst + (if supported) domain is supplied from the LDO or the Buck. + Buck will be powered up only if there is an active request + for BLEH domain or Burst mode and appropriate reature is + allowed. */ + } SUPPLYSTATUS_b; + } ; + + union { + __IOM uint32_t DEVPWREN; /*!< (@ 0x00000008) Device Power Enables */ + + struct { + __IOM uint32_t PWRIOS : 1; /*!< [0..0] Power up IO Slave */ + __IOM uint32_t PWRIOM0 : 1; /*!< [1..1] Power up IO Master 0 */ + __IOM uint32_t PWRIOM1 : 1; /*!< [2..2] Power up IO Master 1 */ + __IOM uint32_t PWRIOM2 : 1; /*!< [3..3] Power up IO Master 2 */ + __IOM uint32_t PWRIOM3 : 1; /*!< [4..4] Power up IO Master 3 */ + __IOM uint32_t PWRIOM4 : 1; /*!< [5..5] Power up IO Master 4 */ + __IOM uint32_t PWRIOM5 : 1; /*!< [6..6] Power up IO Master 5 */ + __IOM uint32_t PWRUART0 : 1; /*!< [7..7] Power up UART Controller 0 */ + __IOM uint32_t PWRUART1 : 1; /*!< [8..8] Power up UART Controller 1 */ + __IOM uint32_t PWRADC : 1; /*!< [9..9] Power up ADC Digital Controller */ + __IOM uint32_t PWRSCARD : 1; /*!< [10..10] Power up SCARD Controller */ + __IOM uint32_t PWRMSPI : 1; /*!< [11..11] Power up MSPI Controller */ + __IOM uint32_t PWRPDM : 1; /*!< [12..12] Power up PDM block */ + __IOM uint32_t PWRBLEL : 1; /*!< [13..13] Power up BLE controller */ + } DEVPWREN_b; + } ; + + union { + __IOM uint32_t MEMPWDINSLEEP; /*!< (@ 0x0000000C) Powerdown SRAM banks in Deep Sleep mode */ + + struct { + __IOM uint32_t DTCMPWDSLP : 3; /*!< [2..0] power down DTCM in deep sleep */ + __IOM uint32_t SRAMPWDSLP : 10; /*!< [12..3] Selects which SRAM banks are powered down in deep sleep + mode, causing the contents of the bank to be lost. */ + __IOM uint32_t FLASH0PWDSLP : 1; /*!< [13..13] Powerdown flash0 in deep sleep */ + __IOM uint32_t FLASH1PWDSLP : 1; /*!< [14..14] Powerdown flash1 in deep sleep */ + __IM uint32_t : 16; + __IOM uint32_t CACHEPWDSLP : 1; /*!< [31..31] power down cache in deep sleep */ + } MEMPWDINSLEEP_b; + } ; + + union { + __IOM uint32_t MEMPWREN; /*!< (@ 0x00000010) Enables individual banks of the MEMORY array */ + + struct { + __IOM uint32_t DTCM : 3; /*!< [2..0] Power up DTCM */ + __IOM uint32_t SRAM : 10; /*!< [12..3] Power up SRAM groups */ + __IOM uint32_t FLASH0 : 1; /*!< [13..13] Power up Flash0 */ + __IOM uint32_t FLASH1 : 1; /*!< [14..14] Power up Flash1 */ + __IM uint32_t : 15; + __IOM uint32_t CACHEB0 : 1; /*!< [30..30] Power up Cache Bank 0. This works in conjunction with + Cache enable from flash_cache module. To power up cache + bank0, cache has to be enabled and this bit has to be set. */ + __IOM uint32_t CACHEB2 : 1; /*!< [31..31] Power up Cache Bank 2. This works in conjunction with + Cache enable from flash_cache module. To power up cache + bank2, cache has to be enabled and this bit has to be set. */ + } MEMPWREN_b; + } ; + + union { + __IOM uint32_t MEMPWRSTATUS; /*!< (@ 0x00000014) Mem Power ON Status */ + + struct { + __IOM uint32_t DTCM00 : 1; /*!< [0..0] This bit is 1 if power is supplied to DTCM GROUP0_0 */ + __IOM uint32_t DTCM01 : 1; /*!< [1..1] This bit is 1 if power is supplied to DTCM GROUP0_1 */ + __IOM uint32_t DTCM1 : 1; /*!< [2..2] This bit is 1 if power is supplied to DTCM GROUP1 */ + __IOM uint32_t SRAM0 : 1; /*!< [3..3] This bit is 1 if power is supplied to SRAM GROUP0 */ + __IOM uint32_t SRAM1 : 1; /*!< [4..4] This bit is 1 if power is supplied to SRAM GROUP1 */ + __IOM uint32_t SRAM2 : 1; /*!< [5..5] This bit is 1 if power is supplied to SRAM GROUP2 */ + __IOM uint32_t SRAM3 : 1; /*!< [6..6] This bit is 1 if power is supplied to SRAM GROUP3 */ + __IOM uint32_t SRAM4 : 1; /*!< [7..7] This bit is 1 if power is supplied to SRAM GROUP4 */ + __IOM uint32_t SRAM5 : 1; /*!< [8..8] This bit is 1 if power is supplied to SRAM GROUP5 */ + __IOM uint32_t SRAM6 : 1; /*!< [9..9] This bit is 1 if power is supplied to SRAM GROUP6 */ + __IOM uint32_t SRAM7 : 1; /*!< [10..10] This bit is 1 if power is supplied to SRAM GROUP7 */ + __IOM uint32_t SRAM8 : 1; /*!< [11..11] This bit is 1 if power is supplied to SRAM GROUP8 */ + __IOM uint32_t SRAM9 : 1; /*!< [12..12] This bit is 1 if power is supplied to SRAM GROUP9 */ + __IOM uint32_t FLASH0 : 1; /*!< [13..13] This bit is 1 if power is supplied to FLASH 0 */ + __IOM uint32_t FLASH1 : 1; /*!< [14..14] This bit is 1 if power is supplied to FLASH 1 */ + __IOM uint32_t CACHEB0 : 1; /*!< [15..15] This bit is 1 if power is supplied to Cache Bank 0 */ + __IOM uint32_t CACHEB2 : 1; /*!< [16..16] This bit is 1 if power is supplied to Cache Bank 2 */ + } MEMPWRSTATUS_b; + } ; + + union { + __IOM uint32_t DEVPWRSTATUS; /*!< (@ 0x00000018) Device Power ON Status */ + + struct { + __IOM uint32_t MCUL : 1; /*!< [0..0] This bit is 1 if power is supplied to MCUL */ + __IOM uint32_t MCUH : 1; /*!< [1..1] This bit is 1 if power is supplied to MCUH */ + __IOM uint32_t HCPA : 1; /*!< [2..2] This bit is 1 if power is supplied to HCPA domain (IO + SLAVE, UART0, UART1, SCARD) */ + __IOM uint32_t HCPB : 1; /*!< [3..3] This bit is 1 if power is supplied to HCPB domain (IO + MASTER 0, 1, 2) */ + __IOM uint32_t HCPC : 1; /*!< [4..4] This bit is 1 if power is supplied to HCPC domain (IO + MASTER4, 5, 6) */ + __IOM uint32_t PWRADC : 1; /*!< [5..5] This bit is 1 if power is supplied to ADC */ + __IOM uint32_t PWRMSPI : 1; /*!< [6..6] This bit is 1 if power is supplied to MSPI */ + __IOM uint32_t PWRPDM : 1; /*!< [7..7] This bit is 1 if power is supplied to PDM */ + __IOM uint32_t BLEL : 1; /*!< [8..8] This bit is 1 if power is supplied to BLEL */ + __IOM uint32_t BLEH : 1; /*!< [9..9] This bit is 1 if power is supplied to BLEH */ + __IM uint32_t : 19; + __IOM uint32_t CORESLEEP : 1; /*!< [29..29] This bit is 1 if CORE has been in SLEEP State. Write + '1' to this bit to clear it. */ + __IOM uint32_t COREDEEPSLEEP : 1; /*!< [30..30] This bit is 1 if CORE has been in Deep Sleep. Write + '1' to this bit to clear it. */ + __IOM uint32_t SYSDEEPSLEEP : 1; /*!< [31..31] This bit is 1 if SYSTEM has been in Deep Sleep. Write + '1' to this bit to clear it. */ + } DEVPWRSTATUS_b; + } ; + + union { + __IOM uint32_t SRAMCTRL; /*!< (@ 0x0000001C) SRAM Control register */ + + struct { + __IM uint32_t : 1; + __IOM uint32_t SRAMCLKGATE : 1; /*!< [1..1] This bit is 1 if clock gating is allowed for individual + system SRAMs */ + __IOM uint32_t SRAMMASTERCLKGATE : 1; /*!< [2..2] This bit is 1 when the master clock gate is enabled (top-level + clock gate for entire SRAM block) */ + __IM uint32_t : 5; + __IOM uint32_t SRAMLIGHTSLEEP : 12; /*!< [19..8] Light Sleep enable for each TCM/SRAM bank. When 1, corresponding + bank will be put into light sleep. For optimal power, banks + should be put into light sleep while the system is active + but the bank has minimal or no accesses. */ + } SRAMCTRL_b; + } ; + + union { + __IOM uint32_t ADCSTATUS; /*!< (@ 0x00000020) Power Status Register for ADC Block */ + + struct { + __IOM uint32_t ADCPWD : 1; /*!< [0..0] This bit indicates that the ADC is powered down */ + __IOM uint32_t BGTPWD : 1; /*!< [1..1] This bit indicates that the ADC Band Gap is powered down */ + __IOM uint32_t VPTATPWD : 1; /*!< [2..2] This bit indicates that the ADC temperature sensor input + buffer is powered down */ + __IOM uint32_t VBATPWD : 1; /*!< [3..3] This bit indicates that the ADC VBAT resistor divider + is powered down */ + __IOM uint32_t REFKEEPPWD : 1; /*!< [4..4] This bit indicates that the ADC REFKEEP is powered down */ + __IOM uint32_t REFBUFPWD : 1; /*!< [5..5] This bit indicates that the ADC REFBUF is powered down */ + } ADCSTATUS_b; + } ; + + union { + __IOM uint32_t MISC; /*!< (@ 0x00000024) Power Optimization Control Bits */ + + struct { + __IOM uint32_t SIMOBUCKEN : 1; /*!< [0..0] Enables and Selects the SIMO Buck as the supply for the + low-voltage power domain. It takes the initial value from + the bit set in Customer INFO space. */ + __IOM uint32_t FORCECOREVRLPPDM : 1; /*!< [1..1] Control bit to enable the core VR to go into LP mode + with HCPA/B/C/MSPI are powered off but PDM is powered on */ + __IOM uint32_t FORCECOREVRLPTIMERS : 1; /*!< [2..2] Control Bit to force Core VR to LP mode in deep sleep + even when hfrc based ctimer or stimer is running. */ + __IOM uint32_t FORCEMEMVRLPTIMERS : 1; /*!< [3..3] Control Bit to force Mem VR to LP mode in deep sleep + even when hfrc based ctimer or stimer is running. */ + __IOM uint32_t FORCEMEMVRADC : 2; /*!< [5..4] Control Bit to force mem VR to LP or ACT mode in deep + sleep when ADC is powered ON. 0x3 results in picking LP + mode. */ + __IOM uint32_t MEMVRLPBLE : 1; /*!< [6..6] Control Bit to let Mem VR go to lp mode in deep sleep + even when BLEL or BLEH is powered on given none of the + other domains require it. */ + __IOM uint32_t FORCEBLEBUCKACT : 1; /*!< [7..7] Control Bit to enable BLE Buck to be in active state + when BLE Buck is enabled. Default behavior is to be in + active only when Burst or BLEH power on are requested. */ + } MISC_b; + } ; + + union { + __IOM uint32_t DEVPWREVENTEN; /*!< (@ 0x00000028) Event enable register to control which DEVPWRSTATUS + bits are routed to event input of CPU. */ + + struct { + __IOM uint32_t MCULEVEN : 1; /*!< [0..0] Control MCUL power-on status event */ + __IOM uint32_t MCUHEVEN : 1; /*!< [1..1] Control MCUH power-on status event */ + __IOM uint32_t HCPAEVEN : 1; /*!< [2..2] Control HCPA power-on status event */ + __IOM uint32_t HCPBEVEN : 1; /*!< [3..3] Control HCPB power-on status event */ + __IOM uint32_t HCPCEVEN : 1; /*!< [4..4] Control HCPC power-on status event */ + __IOM uint32_t ADCEVEN : 1; /*!< [5..5] Control ADC power-on status event */ + __IOM uint32_t MSPIEVEN : 1; /*!< [6..6] Control MSPI power-on status event */ + __IOM uint32_t PDMEVEN : 1; /*!< [7..7] Control PDM power-on status event */ + __IOM uint32_t BLELEVEN : 1; /*!< [8..8] Control BLE power-on status event */ + __IM uint32_t : 20; + __IOM uint32_t BLEFEATUREEVEN : 1; /*!< [29..29] Control BLEFEATURE status event */ + __IOM uint32_t BURSTFEATUREEVEN : 1; /*!< [30..30] Control BURSTFEATURE status event */ + __IOM uint32_t BURSTEVEN : 1; /*!< [31..31] Control BURST status event */ + } DEVPWREVENTEN_b; + } ; + + union { + __IOM uint32_t MEMPWREVENTEN; /*!< (@ 0x0000002C) Event enable register to control which MEMPWRSTATUS + bits are routed to event input of CPU. */ + + struct { + __IOM uint32_t DTCMEN : 3; /*!< [2..0] Enable DTCM power-on status event */ + __IOM uint32_t SRAMEN : 10; /*!< [12..3] Control SRAM power-on status event */ + __IOM uint32_t FLASH0EN : 1; /*!< [13..13] Control Flash power-on status event */ + __IOM uint32_t FLASH1EN : 1; /*!< [14..14] Control Flash power-on status event */ + __IM uint32_t : 15; + __IOM uint32_t CACHEB0EN : 1; /*!< [30..30] Control CACHE BANK 0 power-on status event */ + __IOM uint32_t CACHEB2EN : 1; /*!< [31..31] Control CACHEB2 power-on status event */ + } MEMPWREVENTEN_b; + } ; +} PWRCTRL_Type; /*!< Size = 48 (0x30) */ + + + +/* =========================================================================================================================== */ +/* ================ RSTGEN ================ */ +/* =========================================================================================================================== */ + + +/** + * @brief MCU Reset Generator (RSTGEN) + */ + +typedef struct { /*!< (@ 0x40000000) RSTGEN Structure */ + + union { + __IOM uint32_t CFG; /*!< (@ 0x00000000) Configuration Register */ + + struct { + __IOM uint32_t BODHREN : 1; /*!< [0..0] Brown out high (2.1v) reset enable. */ + __IOM uint32_t WDREN : 1; /*!< [1..1] Watchdog Timer Reset Enable. NOTE: The WDT module must + also be configured for WDT reset. This includes enabling + the RESEN bit in WDTCFG register in Watch dog timer block. */ + } CFG_b; + } ; + + union { + __IOM uint32_t SWPOI; /*!< (@ 0x00000004) Software POI Reset */ + + struct { + __IOM uint32_t SWPOIKEY : 8; /*!< [7..0] 0x1B generates a software POI reset. This is a write-only + register. Reading from this register will yield only all + 0s. */ + } SWPOI_b; + } ; + + union { + __IOM uint32_t SWPOR; /*!< (@ 0x00000008) Software POR Reset */ + + struct { + __IOM uint32_t SWPORKEY : 8; /*!< [7..0] 0xD4 generates a software POR reset. */ + } SWPOR_b; + } ; + __IM uint32_t RESERVED[2]; + + union { + __IOM uint32_t TPIURST; /*!< (@ 0x00000014) TPIU reset */ + + struct { + __IOM uint32_t TPIURST : 1; /*!< [0..0] Static reset for the TPIU. Write to '1' to assert reset + to TPIU. Write to '0' to clear the reset. */ + } TPIURST_b; + } ; + __IM uint32_t RESERVED1[122]; + + union { + __IOM uint32_t INTEN; /*!< (@ 0x00000200) Reset Interrupt register: Enable */ + + struct { + __IOM uint32_t BODH : 1; /*!< [0..0] Enables an interrupt that triggers when VCC is below + BODH level. */ + } INTEN_b; + } ; + + union { + __IOM uint32_t INTSTAT; /*!< (@ 0x00000204) Reset Interrupt register: Status */ + + struct { + __IOM uint32_t BODH : 1; /*!< [0..0] Enables an interrupt that triggers when VCC is below + BODH level. */ + } INTSTAT_b; + } ; + + union { + __IOM uint32_t INTCLR; /*!< (@ 0x00000208) Reset Interrupt register: Clear */ + + struct { + __IOM uint32_t BODH : 1; /*!< [0..0] Enables an interrupt that triggers when VCC is below + BODH level. */ + } INTCLR_b; + } ; + + union { + __IOM uint32_t INTSET; /*!< (@ 0x0000020C) Reset Interrupt register: Set */ + + struct { + __IOM uint32_t BODH : 1; /*!< [0..0] Enables an interrupt that triggers when VCC is below + BODH level. */ + } INTSET_b; + } ; + __IM uint32_t RESERVED2[67107708]; + + union { + __IOM uint32_t STAT; /*!< (@ 0x0FFFF000) Status Register (SBL) */ + + struct { + __IOM uint32_t EXRSTAT : 1; /*!< [0..0] Reset was initiated by an External Reset (SBL). */ + __IOM uint32_t PORSTAT : 1; /*!< [1..1] Reset was initiated by a Power-On Reset (SBL). */ + __IOM uint32_t BORSTAT : 1; /*!< [2..2] Reset was initiated by a Brown-Out Reset (SBL). */ + __IOM uint32_t SWRSTAT : 1; /*!< [3..3] Reset was a initiated by SW POR or AIRCR Reset (SBL). */ + __IOM uint32_t POIRSTAT : 1; /*!< [4..4] Reset was a initiated by Software POI Reset (SBL). */ + __IOM uint32_t DBGRSTAT : 1; /*!< [5..5] Reset was a initiated by Debugger Reset (SBL). */ + __IOM uint32_t WDRSTAT : 1; /*!< [6..6] Reset was initiated by a Watchdog Timer Reset (SBL). */ + __IOM uint32_t BOUSTAT : 1; /*!< [7..7] An Unregulated Supply Brownout Event occurred (SBL). */ + __IOM uint32_t BOCSTAT : 1; /*!< [8..8] A Core Regulator Brownout Event occurred (SBL). */ + __IOM uint32_t BOFSTAT : 1; /*!< [9..9] A Memory Regulator Brownout Event occurred (SBL). */ + __IOM uint32_t BOBSTAT : 1; /*!< [10..10] A BLE/Burst Regulator Brownout Event occurred (SBL). */ + __IM uint32_t : 19; + __IOM uint32_t FBOOT : 1; /*!< [30..30] Set if current boot was initiated by soft reset and + resulted in Fast Boot (SBL). */ + __IOM uint32_t SBOOT : 1; /*!< [31..31] Set when booting securely (SBL). */ + } STAT_b; + } ; +} RSTGEN_Type; /*!< Size = 268431364 (0xffff004) */ + + + +/* =========================================================================================================================== */ +/* ================ RTC ================ */ +/* =========================================================================================================================== */ + + +/** + * @brief Real Time Clock (RTC) + */ + +typedef struct { /*!< (@ 0x40004200) RTC Structure */ + __IM uint32_t RESERVED[16]; + + union { + __IOM uint32_t CTRLOW; /*!< (@ 0x00000040) RTC Counters Lower */ + + struct { + __IOM uint32_t CTR100 : 8; /*!< [7..0] 100ths of a second Counter */ + __IOM uint32_t CTRSEC : 7; /*!< [14..8] Seconds Counter */ + __IM uint32_t : 1; + __IOM uint32_t CTRMIN : 7; /*!< [22..16] Minutes Counter */ + __IM uint32_t : 1; + __IOM uint32_t CTRHR : 6; /*!< [29..24] Hours Counter */ + } CTRLOW_b; + } ; + + union { + __IOM uint32_t CTRUP; /*!< (@ 0x00000044) RTC Counters Upper */ + + struct { + __IOM uint32_t CTRDATE : 6; /*!< [5..0] Date Counter */ + __IM uint32_t : 2; + __IOM uint32_t CTRMO : 5; /*!< [12..8] Months Counter */ + __IM uint32_t : 3; + __IOM uint32_t CTRYR : 8; /*!< [23..16] Years Counter */ + __IOM uint32_t CTRWKDY : 3; /*!< [26..24] Weekdays Counter */ + __IOM uint32_t CB : 1; /*!< [27..27] Century */ + __IOM uint32_t CEB : 1; /*!< [28..28] Century enable */ + __IM uint32_t : 2; + __IOM uint32_t CTERR : 1; /*!< [31..31] Counter read error status. Error is triggered when + software reads the lower word of the counters, and fails + to read the upper counter within 1/100 second. This is + because when the lower counter is read, the upper counter + is held off from incrementing until it is read so that + the full time stamp can be read. */ + } CTRUP_b; + } ; + + union { + __IOM uint32_t ALMLOW; /*!< (@ 0x00000048) RTC Alarms Lower */ + + struct { + __IOM uint32_t ALM100 : 8; /*!< [7..0] 100ths of a second Alarm */ + __IOM uint32_t ALMSEC : 7; /*!< [14..8] Seconds Alarm */ + __IM uint32_t : 1; + __IOM uint32_t ALMMIN : 7; /*!< [22..16] Minutes Alarm */ + __IM uint32_t : 1; + __IOM uint32_t ALMHR : 6; /*!< [29..24] Hours Alarm */ + } ALMLOW_b; + } ; + + union { + __IOM uint32_t ALMUP; /*!< (@ 0x0000004C) RTC Alarms Upper */ + + struct { + __IOM uint32_t ALMDATE : 6; /*!< [5..0] Date Alarm */ + __IM uint32_t : 2; + __IOM uint32_t ALMMO : 5; /*!< [12..8] Months Alarm */ + __IM uint32_t : 3; + __IOM uint32_t ALMWKDY : 3; /*!< [18..16] Weekdays Alarm */ + } ALMUP_b; + } ; + + union { + __IOM uint32_t RTCCTL; /*!< (@ 0x00000050) RTC Control Register */ + + struct { + __IOM uint32_t WRTC : 1; /*!< [0..0] Counter write control */ + __IOM uint32_t RPT : 3; /*!< [3..1] Alarm repeat interval */ + __IOM uint32_t RSTOP : 1; /*!< [4..4] RTC input clock control */ + __IOM uint32_t HR1224 : 1; /*!< [5..5] Hours Counter mode */ + } RTCCTL_b; + } ; + __IM uint32_t RESERVED1[43]; + + union { + __IOM uint32_t INTEN; /*!< (@ 0x00000100) RTC Interrupt Register: Enable */ + + struct { + __IOM uint32_t ALM : 1; /*!< [0..0] RTC Alarm interrupt */ + } INTEN_b; + } ; + + union { + __IOM uint32_t INTSTAT; /*!< (@ 0x00000104) RTC Interrupt Register: Status */ + + struct { + __IOM uint32_t ALM : 1; /*!< [0..0] RTC Alarm interrupt */ + } INTSTAT_b; + } ; + + union { + __IOM uint32_t INTCLR; /*!< (@ 0x00000108) RTC Interrupt Register: Clear */ + + struct { + __IOM uint32_t ALM : 1; /*!< [0..0] RTC Alarm interrupt */ + } INTCLR_b; + } ; + + union { + __IOM uint32_t INTSET; /*!< (@ 0x0000010C) RTC Interrupt Register: Set */ + + struct { + __IOM uint32_t ALM : 1; /*!< [0..0] RTC Alarm interrupt */ + } INTSET_b; + } ; +} RTC_Type; /*!< Size = 272 (0x110) */ + + + +/* =========================================================================================================================== */ +/* ================ SCARD ================ */ +/* =========================================================================================================================== */ + + +/** + * @brief Serial ISO7816 (SCARD) + */ + +typedef struct { /*!< (@ 0x40080000) SCARD Structure */ + + union { + __IOM uint32_t SR; /*!< (@ 0x00000000) ISO7816 interrupt status */ + + struct { + __IOM uint32_t FNE : 1; /*!< [0..0] RX FIFO not empty. */ + __IOM uint32_t TBERBF : 1; /*!< [1..1] FIFO empty (transmit) or full (receive). */ + __IOM uint32_t FER : 1; /*!< [2..2] Framing error. */ + __IOM uint32_t OVR : 1; /*!< [3..3] RX FIFO overflow. */ + __IOM uint32_t PE : 1; /*!< [4..4] Parity Error. */ + __IOM uint32_t FT2REND : 1; /*!< [5..5] TX to RX finished. */ + __IOM uint32_t FHF : 1; /*!< [6..6] FIFO Half Full. */ + } SR_b; + } ; + __IM uint32_t RESERVED[3]; + + union { + __IOM uint32_t DR; /*!< (@ 0x00000010) ISO7816 data */ + + struct { + __IOM uint32_t DR : 8; /*!< [7..0] Data register. */ + } DR_b; + } ; + __IM uint32_t RESERVED1[3]; + + union { + __IOM uint32_t SR1; /*!< (@ 0x00000020) ISO7816 interrupt status 1 */ + + struct { + __IOM uint32_t ECNTOVER : 1; /*!< [0..0] ETU counter overflow. */ + __IOM uint32_t PRL : 1; /*!< [1..1] Card insert/remove. */ + __IOM uint32_t SYNCEND : 1; /*!< [2..2] Write complete synchronization. */ + __IOM uint32_t IDLE : 1; /*!< [3..3] ISO7816 idle. */ + } SR1_b; + } ; + __IM uint32_t RESERVED2[5]; + + union { + __IOM uint32_t RETXCNTRMI; /*!< (@ 0x00000038) ISO7816 resent count inquiry */ + + struct { + __IOM uint32_t RETXCNTRMI : 4; /*!< [3..0] Resent count inquiry register. */ + } RETXCNTRMI_b; + } ; + __IM uint32_t RESERVED3[49]; + + union { + __IOM uint32_t CLKCTRL; /*!< (@ 0x00000100) Clock Control */ + + struct { + __IOM uint32_t CLKEN : 1; /*!< [0..0] Enable the serial source clock for SCARD. */ + __IOM uint32_t APBCLKEN : 1; /*!< [1..1] Enable the SCARD APB clock to run continuously. */ + } CLKCTRL_b; + } ; +} SCARD_Type; /*!< Size = 260 (0x104) */ + + + +/* =========================================================================================================================== */ +/* ================ SECURITY ================ */ +/* =========================================================================================================================== */ + + +/** + * @brief Security Interfaces (SECURITY) + */ + +typedef struct { /*!< (@ 0x40030000) SECURITY Structure */ + + union { + __IOM uint32_t CTRL; /*!< (@ 0x00000000) Control Register */ + + struct { + __IOM uint32_t ENABLE : 1; /*!< [0..0] Function Enable. Software should set the ENABLE bit to + initiate a CRC operation. Hardware will clear the ENABLE + bit upon completion. */ + __IM uint32_t : 3; + __IOM uint32_t FUNCTION : 4; /*!< [7..4] Function Select */ + __IM uint32_t : 23; + __IOM uint32_t CRCERROR : 1; /*!< [31..31] CRC Error Status - Set to 1 if an error occurs during + a CRC operation. Cleared when CTRL register is written + (with any value). Usually indicates an invalid address + range. */ + } CTRL_b; + } ; + __IM uint32_t RESERVED[3]; + + union { + __IOM uint32_t SRCADDR; /*!< (@ 0x00000010) Source Addresss */ + + struct { + __IOM uint32_t ADDR : 32; /*!< [31..0] Source Buffer Address. Address may be byte aligned, + but the length must be a multiple of 4 bits. */ + } SRCADDR_b; + } ; + __IM uint32_t RESERVED1[3]; + + union { + __IOM uint32_t LEN; /*!< (@ 0x00000020) Length */ + + struct { + __IM uint32_t : 2; + __IOM uint32_t LEN : 18; /*!< [19..2] Buffer size (bottom two bits assumed to be zero to ensure + a multiple of 4 bytes) */ + } LEN_b; + } ; + __IM uint32_t RESERVED2[3]; + + union { + __IOM uint32_t RESULT; /*!< (@ 0x00000030) CRC Seed/Result Register */ + + struct { + __IOM uint32_t CRC : 32; /*!< [31..0] CRC Seed/Result. Software must seed the CRC with 0xFFFFFFFF + before starting a CRC operation (unless the CRC is continued + from a previous operation). */ + } RESULT_b; + } ; + __IM uint32_t RESERVED3[17]; + + union { + __IOM uint32_t LOCKCTRL; /*!< (@ 0x00000078) LOCK Control Register */ + + struct { + __IOM uint32_t SELECT : 8; /*!< [7..0] LOCK Function Select register. */ + } LOCKCTRL_b; + } ; + + union { + __IOM uint32_t LOCKSTAT; /*!< (@ 0x0000007C) LOCK Status Register */ + + struct { + __IOM uint32_t STATUS : 32; /*!< [31..0] LOCK Status register. This register is a bitmask for + which resources are currently unlocked. These bits are + one-hot per resource. */ + } LOCKSTAT_b; + } ; + + union { + __IOM uint32_t KEY0; /*!< (@ 0x00000080) Key0 Register */ + + struct { + __IOM uint32_t KEY0 : 32; /*!< [31..0] Bits [31:0] of the 128-bit key should be written to + this register. To protect key values, the register always + returns 0x00000000. */ + } KEY0_b; + } ; + + union { + __IOM uint32_t KEY1; /*!< (@ 0x00000084) Key1 Register */ + + struct { + __IOM uint32_t KEY1 : 32; /*!< [31..0] Bits [63:32] of the 128-bit key should be written to + this register. To protect key values, the register always + returns 0x00000000. */ + } KEY1_b; + } ; + + union { + __IOM uint32_t KEY2; /*!< (@ 0x00000088) Key2 Register */ + + struct { + __IOM uint32_t KEY2 : 32; /*!< [31..0] Bits [95:64] of the 128-bit key should be written to + this register. To protect key values, the register always + returns 0x00000000. */ + } KEY2_b; + } ; + + union { + __IOM uint32_t KEY3; /*!< (@ 0x0000008C) Key3 Register */ + + struct { + __IOM uint32_t KEY3 : 32; /*!< [31..0] Bits [127:96] of the 128-bit key should be written to + this register. To protect key values, the register always + returns 0x00000000. */ + } KEY3_b; + } ; +} SECURITY_Type; /*!< Size = 144 (0x90) */ + + + +/* =========================================================================================================================== */ +/* ================ UART0 ================ */ +/* =========================================================================================================================== */ + + +/** + * @brief Serial UART (UART0) + */ + +typedef struct { /*!< (@ 0x4001C000) UART0 Structure */ + + union { + __IOM uint32_t DR; /*!< (@ 0x00000000) UART Data Register */ + + struct { + __IOM uint32_t DATA : 8; /*!< [7..0] This is the UART data port. */ + __IOM uint32_t FEDATA : 1; /*!< [8..8] This is the framing error indicator. */ + __IOM uint32_t PEDATA : 1; /*!< [9..9] This is the parity error indicator. */ + __IOM uint32_t BEDATA : 1; /*!< [10..10] This is the break error indicator. */ + __IOM uint32_t OEDATA : 1; /*!< [11..11] This is the overrun error indicator. */ + } DR_b; + } ; + + union { + __IOM uint32_t RSR; /*!< (@ 0x00000004) UART Status Register */ + + struct { + __IOM uint32_t FESTAT : 1; /*!< [0..0] This is the framing error indicator. */ + __IOM uint32_t PESTAT : 1; /*!< [1..1] This is the parity error indicator. */ + __IOM uint32_t BESTAT : 1; /*!< [2..2] This is the break error indicator. */ + __IOM uint32_t OESTAT : 1; /*!< [3..3] This is the overrun error indicator. */ + } RSR_b; + } ; + __IM uint32_t RESERVED[4]; + + union { + __IOM uint32_t FR; /*!< (@ 0x00000018) Flag Register */ + + struct { + __IOM uint32_t CTS : 1; /*!< [0..0] This bit holds the clear to send indicator. */ + __IOM uint32_t DSR : 1; /*!< [1..1] This bit holds the data set ready indicator. */ + __IOM uint32_t DCD : 1; /*!< [2..2] This bit holds the data carrier detect indicator. */ + __IOM uint32_t BUSY : 1; /*!< [3..3] This bit holds the busy indicator. */ + __IOM uint32_t RXFE : 1; /*!< [4..4] This bit holds the receive FIFO empty indicator. */ + __IOM uint32_t TXFF : 1; /*!< [5..5] This bit holds the transmit FIFO full indicator. */ + __IOM uint32_t RXFF : 1; /*!< [6..6] This bit holds the receive FIFO full indicator. */ + __IOM uint32_t TXFE : 1; /*!< [7..7] This bit holds the transmit FIFO empty indicator. */ + __IOM uint32_t TXBUSY : 1; /*!< [8..8] This bit holds the transmit BUSY indicator. */ + } FR_b; + } ; + __IM uint32_t RESERVED1; + + union { + __IOM uint32_t ILPR; /*!< (@ 0x00000020) IrDA Counter */ + + struct { + __IOM uint32_t ILPDVSR : 8; /*!< [7..0] These bits hold the IrDA counter divisor. */ + } ILPR_b; + } ; + + union { + __IOM uint32_t IBRD; /*!< (@ 0x00000024) Integer Baud Rate Divisor */ + + struct { + __IOM uint32_t DIVINT : 16; /*!< [15..0] These bits hold the baud integer divisor. */ + } IBRD_b; + } ; + + union { + __IOM uint32_t FBRD; /*!< (@ 0x00000028) Fractional Baud Rate Divisor */ + + struct { + __IOM uint32_t DIVFRAC : 6; /*!< [5..0] These bits hold the baud fractional divisor. */ + } FBRD_b; + } ; + + union { + __IOM uint32_t LCRH; /*!< (@ 0x0000002C) Line Control High */ + + struct { + __IOM uint32_t BRK : 1; /*!< [0..0] This bit holds the break set. */ + __IOM uint32_t PEN : 1; /*!< [1..1] This bit holds the parity enable. */ + __IOM uint32_t EPS : 1; /*!< [2..2] This bit holds the even parity select. */ + __IOM uint32_t STP2 : 1; /*!< [3..3] This bit holds the two stop bits select. */ + __IOM uint32_t FEN : 1; /*!< [4..4] This bit holds the FIFO enable. */ + __IOM uint32_t WLEN : 2; /*!< [6..5] These bits hold the write length. */ + __IOM uint32_t SPS : 1; /*!< [7..7] This bit holds the stick parity select. */ + } LCRH_b; + } ; + + union { + __IOM uint32_t CR; /*!< (@ 0x00000030) Control Register */ + + struct { + __IOM uint32_t UARTEN : 1; /*!< [0..0] This bit is the UART enable. */ + __IOM uint32_t SIREN : 1; /*!< [1..1] This bit is the SIR ENDEC enable. */ + __IOM uint32_t SIRLP : 1; /*!< [2..2] This bit is the SIR low power select. */ + __IOM uint32_t CLKEN : 1; /*!< [3..3] This bit is the UART clock enable. */ + __IOM uint32_t CLKSEL : 3; /*!< [6..4] This bitfield is the UART clock select. */ + __IOM uint32_t LBE : 1; /*!< [7..7] This bit is the loopback enable. */ + __IOM uint32_t TXE : 1; /*!< [8..8] This bit is the transmit enable. */ + __IOM uint32_t RXE : 1; /*!< [9..9] This bit is the receive enable. */ + __IOM uint32_t DTR : 1; /*!< [10..10] This bit enables data transmit ready. */ + __IOM uint32_t RTS : 1; /*!< [11..11] This bit enables request to send. */ + __IOM uint32_t OUT1 : 1; /*!< [12..12] This bit holds modem Out1. */ + __IOM uint32_t OUT2 : 1; /*!< [13..13] This bit holds modem Out2. */ + __IOM uint32_t RTSEN : 1; /*!< [14..14] This bit enables RTS hardware flow control. */ + __IOM uint32_t CTSEN : 1; /*!< [15..15] This bit enables CTS hardware flow control. */ + } CR_b; + } ; + + union { + __IOM uint32_t IFLS; /*!< (@ 0x00000034) FIFO Interrupt Level Select */ + + struct { + __IOM uint32_t TXIFLSEL : 3; /*!< [2..0] These bits hold the transmit FIFO interrupt level. */ + __IOM uint32_t RXIFLSEL : 3; /*!< [5..3] These bits hold the receive FIFO interrupt level. */ + } IFLS_b; + } ; + + union { + __IOM uint32_t IER; /*!< (@ 0x00000038) Interrupt Enable */ + + struct { + __IOM uint32_t TXCMPMIM : 1; /*!< [0..0] This bit holds the modem TXCMP interrupt enable. */ + __IOM uint32_t CTSMIM : 1; /*!< [1..1] This bit holds the modem CTS interrupt enable. */ + __IOM uint32_t DCDMIM : 1; /*!< [2..2] This bit holds the modem DCD interrupt enable. */ + __IOM uint32_t DSRMIM : 1; /*!< [3..3] This bit holds the modem DSR interrupt enable. */ + __IOM uint32_t RXIM : 1; /*!< [4..4] This bit holds the receive interrupt enable. */ + __IOM uint32_t TXIM : 1; /*!< [5..5] This bit holds the transmit interrupt enable. */ + __IOM uint32_t RTIM : 1; /*!< [6..6] This bit holds the receive timeout interrupt enable. */ + __IOM uint32_t FEIM : 1; /*!< [7..7] This bit holds the framing error interrupt enable. */ + __IOM uint32_t PEIM : 1; /*!< [8..8] This bit holds the parity error interrupt enable. */ + __IOM uint32_t BEIM : 1; /*!< [9..9] This bit holds the break error interrupt enable. */ + __IOM uint32_t OEIM : 1; /*!< [10..10] This bit holds the overflow interrupt enable. */ + } IER_b; + } ; + + union { + __IOM uint32_t IES; /*!< (@ 0x0000003C) Interrupt Status */ + + struct { + __IOM uint32_t TXCMPMRIS : 1; /*!< [0..0] This bit holds the modem TXCMP interrupt status. */ + __IOM uint32_t CTSMRIS : 1; /*!< [1..1] This bit holds the modem CTS interrupt status. */ + __IOM uint32_t DCDMRIS : 1; /*!< [2..2] This bit holds the modem DCD interrupt status. */ + __IOM uint32_t DSRMRIS : 1; /*!< [3..3] This bit holds the modem DSR interrupt status. */ + __IOM uint32_t RXRIS : 1; /*!< [4..4] This bit holds the receive interrupt status. */ + __IOM uint32_t TXRIS : 1; /*!< [5..5] This bit holds the transmit interrupt status. */ + __IOM uint32_t RTRIS : 1; /*!< [6..6] This bit holds the receive timeout interrupt status. */ + __IOM uint32_t FERIS : 1; /*!< [7..7] This bit holds the framing error interrupt status. */ + __IOM uint32_t PERIS : 1; /*!< [8..8] This bit holds the parity error interrupt status. */ + __IOM uint32_t BERIS : 1; /*!< [9..9] This bit holds the break error interrupt status. */ + __IOM uint32_t OERIS : 1; /*!< [10..10] This bit holds the overflow interrupt status. */ + } IES_b; + } ; + + union { + __IOM uint32_t MIS; /*!< (@ 0x00000040) Masked Interrupt Status */ + + struct { + __IOM uint32_t TXCMPMMIS : 1; /*!< [0..0] This bit holds the modem TXCMP interrupt status masked. */ + __IOM uint32_t CTSMMIS : 1; /*!< [1..1] This bit holds the modem CTS interrupt status masked. */ + __IOM uint32_t DCDMMIS : 1; /*!< [2..2] This bit holds the modem DCD interrupt status masked. */ + __IOM uint32_t DSRMMIS : 1; /*!< [3..3] This bit holds the modem DSR interrupt status masked. */ + __IOM uint32_t RXMIS : 1; /*!< [4..4] This bit holds the receive interrupt status masked. */ + __IOM uint32_t TXMIS : 1; /*!< [5..5] This bit holds the transmit interrupt status masked. */ + __IOM uint32_t RTMIS : 1; /*!< [6..6] This bit holds the receive timeout interrupt status masked. */ + __IOM uint32_t FEMIS : 1; /*!< [7..7] This bit holds the framing error interrupt status masked. */ + __IOM uint32_t PEMIS : 1; /*!< [8..8] This bit holds the parity error interrupt status masked. */ + __IOM uint32_t BEMIS : 1; /*!< [9..9] This bit holds the break error interrupt status masked. */ + __IOM uint32_t OEMIS : 1; /*!< [10..10] This bit holds the overflow interrupt status masked. */ + } MIS_b; + } ; + + union { + __IOM uint32_t IEC; /*!< (@ 0x00000044) Interrupt Clear */ + + struct { + __IOM uint32_t TXCMPMIC : 1; /*!< [0..0] This bit holds the modem TXCMP interrupt clear. */ + __IOM uint32_t CTSMIC : 1; /*!< [1..1] This bit holds the modem CTS interrupt clear. */ + __IOM uint32_t DCDMIC : 1; /*!< [2..2] This bit holds the modem DCD interrupt clear. */ + __IOM uint32_t DSRMIC : 1; /*!< [3..3] This bit holds the modem DSR interrupt clear. */ + __IOM uint32_t RXIC : 1; /*!< [4..4] This bit holds the receive interrupt clear. */ + __IOM uint32_t TXIC : 1; /*!< [5..5] This bit holds the transmit interrupt clear. */ + __IOM uint32_t RTIC : 1; /*!< [6..6] This bit holds the receive timeout interrupt clear. */ + __IOM uint32_t FEIC : 1; /*!< [7..7] This bit holds the framing error interrupt clear. */ + __IOM uint32_t PEIC : 1; /*!< [8..8] This bit holds the parity error interrupt clear. */ + __IOM uint32_t BEIC : 1; /*!< [9..9] This bit holds the break error interrupt clear. */ + __IOM uint32_t OEIC : 1; /*!< [10..10] This bit holds the overflow interrupt clear. */ + } IEC_b; + } ; +} UART0_Type; /*!< Size = 72 (0x48) */ + + + +/* =========================================================================================================================== */ +/* ================ VCOMP ================ */ +/* =========================================================================================================================== */ + + +/** + * @brief Voltage Comparator (VCOMP) + */ + +typedef struct { /*!< (@ 0x4000C000) VCOMP Structure */ + + union { + __IOM uint32_t CFG; /*!< (@ 0x00000000) Configuration Register */ + + struct { + __IOM uint32_t PSEL : 2; /*!< [1..0] This bitfield selects the positive input to the comparator. */ + __IM uint32_t : 6; + __IOM uint32_t NSEL : 2; /*!< [9..8] This bitfield selects the negative input to the comparator. */ + __IM uint32_t : 6; + __IOM uint32_t LVLSEL : 4; /*!< [19..16] When the reference input NSEL is set to NSEL_DAC, this + bitfield selects the voltage level for the negative input + to the comparator. */ + } CFG_b; + } ; + + union { + __IOM uint32_t STAT; /*!< (@ 0x00000004) Status Register */ + + struct { + __IOM uint32_t CMPOUT : 1; /*!< [0..0] This bit is 1 if the positive input of the comparator + is greater than the negative input. */ + __IOM uint32_t PWDSTAT : 1; /*!< [1..1] This bit indicates the power down state of the voltage + comparator. */ + } STAT_b; + } ; + + union { + __IOM uint32_t PWDKEY; /*!< (@ 0x00000008) Key Register for Powering Down the Voltage Comparator */ + + struct { + __IOM uint32_t PWDKEY : 32; /*!< [31..0] Key register value. */ + } PWDKEY_b; + } ; + __IM uint32_t RESERVED[125]; + + union { + __IOM uint32_t INTEN; /*!< (@ 0x00000200) Voltage Comparator Interrupt registers: Enable */ + + struct { + __IOM uint32_t OUTLOW : 1; /*!< [0..0] This bit is the vcompout low interrupt. */ + __IOM uint32_t OUTHI : 1; /*!< [1..1] This bit is the vcompout high interrupt. */ + } INTEN_b; + } ; + + union { + __IOM uint32_t INTSTAT; /*!< (@ 0x00000204) Voltage Comparator Interrupt registers: Status */ + + struct { + __IOM uint32_t OUTLOW : 1; /*!< [0..0] This bit is the vcompout low interrupt. */ + __IOM uint32_t OUTHI : 1; /*!< [1..1] This bit is the vcompout high interrupt. */ + } INTSTAT_b; + } ; + + union { + __IOM uint32_t INTCLR; /*!< (@ 0x00000208) Voltage Comparator Interrupt registers: Clear */ + + struct { + __IOM uint32_t OUTLOW : 1; /*!< [0..0] This bit is the vcompout low interrupt. */ + __IOM uint32_t OUTHI : 1; /*!< [1..1] This bit is the vcompout high interrupt. */ + } INTCLR_b; + } ; + + union { + __IOM uint32_t INTSET; /*!< (@ 0x0000020C) Voltage Comparator Interrupt registers: Set */ + + struct { + __IOM uint32_t OUTLOW : 1; /*!< [0..0] This bit is the vcompout low interrupt. */ + __IOM uint32_t OUTHI : 1; /*!< [1..1] This bit is the vcompout high interrupt. */ + } INTSET_b; + } ; +} VCOMP_Type; /*!< Size = 528 (0x210) */ + + + +/* =========================================================================================================================== */ +/* ================ WDT ================ */ +/* =========================================================================================================================== */ + + +/** + * @brief Watchdog Timer (WDT) + */ + +typedef struct { /*!< (@ 0x40024000) WDT Structure */ + + union { + __IOM uint32_t CFG; /*!< (@ 0x00000000) Configuration Register */ + + struct { + __IOM uint32_t WDTEN : 1; /*!< [0..0] This bitfield enables the WDT. */ + __IOM uint32_t INTEN : 1; /*!< [1..1] This bitfield enables the WDT interrupt. Note : This + bit must be set before the interrupt status bit will reflect + a watchdog timer expiration. The IER interrupt register + must also be enabled for a WDT interrupt to be sent to + the NVIC. */ + __IOM uint32_t RESEN : 1; /*!< [2..2] This bitfield enables the WDT reset. This needs to be + set together with the WDREN bit in REG_RSTGEN_CFG register + (in reset gen) to trigger the reset. */ + __IM uint32_t : 5; + __IOM uint32_t RESVAL : 8; /*!< [15..8] This bitfield is the compare value for counter bits + 7:0 to generate a watchdog reset. This will cause a software + reset. */ + __IOM uint32_t INTVAL : 8; /*!< [23..16] This bitfield is the compare value for counter bits + 7:0 to generate a watchdog interrupt. */ + __IOM uint32_t CLKSEL : 3; /*!< [26..24] Select the frequency for the WDT. All values not enumerated + below are undefined. */ + } CFG_b; + } ; + + union { + __IOM uint32_t RSTRT; /*!< (@ 0x00000004) Restart the watchdog timer. */ + + struct { + __IOM uint32_t RSTRT : 8; /*!< [7..0] Writing 0xB2 to WDTRSTRT restarts the watchdog timer. + This is a write only register. Reading this register will + only provide all 0. */ + } RSTRT_b; + } ; + + union { + __IOM uint32_t LOCK; /*!< (@ 0x00000008) Locks the WDT */ + + struct { + __IOM uint32_t LOCK : 8; /*!< [7..0] Writing 0x3A locks the watchdog timer. Once locked, the + WDTCFG reg cannot be written and WDTEN is set. */ + } LOCK_b; + } ; + + union { + __IOM uint32_t COUNT; /*!< (@ 0x0000000C) Current Counter Value for WDT */ + + struct { + __IOM uint32_t COUNT : 8; /*!< [7..0] Read-Only current value of the WDT counter */ + } COUNT_b; + } ; + __IM uint32_t RESERVED[124]; + + union { + __IOM uint32_t INTEN; /*!< (@ 0x00000200) WDT Interrupt register: Enable */ + + struct { + __IOM uint32_t WDTINT : 1; /*!< [0..0] Watchdog Timer Interrupt. */ + } INTEN_b; + } ; + + union { + __IOM uint32_t INTSTAT; /*!< (@ 0x00000204) WDT Interrupt register: Status */ + + struct { + __IOM uint32_t WDTINT : 1; /*!< [0..0] Watchdog Timer Interrupt. */ + } INTSTAT_b; + } ; + + union { + __IOM uint32_t INTCLR; /*!< (@ 0x00000208) WDT Interrupt register: Clear */ + + struct { + __IOM uint32_t WDTINT : 1; /*!< [0..0] Watchdog Timer Interrupt. */ + } INTCLR_b; + } ; + + union { + __IOM uint32_t INTSET; /*!< (@ 0x0000020C) WDT Interrupt register: Set */ + + struct { + __IOM uint32_t WDTINT : 1; /*!< [0..0] Watchdog Timer Interrupt. */ + } INTSET_b; + } ; +} WDT_Type; /*!< Size = 528 (0x210) */ + + +/** @} */ /* End of group Device_Peripheral_peripherals */ + + +/* =========================================================================================================================== */ +/* ================ Device Specific Peripheral Address Map ================ */ +/* =========================================================================================================================== */ + + +/** @addtogroup Device_Peripheral_peripheralAddr + * @{ + */ + +#define ADC_BASE 0x50010000UL +#define APBDMA_BASE 0x40011000UL +#define BLEIF_BASE 0x5000C000UL +#define CACHECTRL_BASE 0x40018000UL +#define CLKGEN_BASE 0x40004000UL +#define CTIMER_BASE 0x40008000UL +#define GPIO_BASE 0x40010000UL +#define IOM0_BASE 0x50004000UL +#define IOM1_BASE 0x50005000UL +#define IOM2_BASE 0x50006000UL +#define IOM3_BASE 0x50007000UL +#define IOM4_BASE 0x50008000UL +#define IOM5_BASE 0x50009000UL +#define IOSLAVE_BASE 0x50000000UL +#define MCUCTRL_BASE 0x40020000UL +#define MSPI_BASE 0x50014000UL +#define PDM_BASE 0x50011000UL +#define PWRCTRL_BASE 0x40021000UL +#define RSTGEN_BASE 0x40000000UL +#define RTC_BASE 0x40004200UL +#define SCARD_BASE 0x40080000UL +#define SECURITY_BASE 0x40030000UL +#define UART0_BASE 0x4001C000UL +#define UART1_BASE 0x4001D000UL +#define VCOMP_BASE 0x4000C000UL +#define WDT_BASE 0x40024000UL + +/** @} */ /* End of group Device_Peripheral_peripheralAddr */ + + +/* =========================================================================================================================== */ +/* ================ Peripheral declaration ================ */ +/* =========================================================================================================================== */ + + +/** @addtogroup Device_Peripheral_declaration + * @{ + */ + +#define ADC ((ADC_Type*) ADC_BASE) +#define APBDMA ((APBDMA_Type*) APBDMA_BASE) +#define BLEIF ((BLEIF_Type*) BLEIF_BASE) +#define CACHECTRL ((CACHECTRL_Type*) CACHECTRL_BASE) +#define CLKGEN ((CLKGEN_Type*) CLKGEN_BASE) +#define CTIMER ((CTIMER_Type*) CTIMER_BASE) +#define GPIO ((GPIO_Type*) GPIO_BASE) +#define IOM0 ((IOM0_Type*) IOM0_BASE) +#define IOM1 ((IOM0_Type*) IOM1_BASE) +#define IOM2 ((IOM0_Type*) IOM2_BASE) +#define IOM3 ((IOM0_Type*) IOM3_BASE) +#define IOM4 ((IOM0_Type*) IOM4_BASE) +#define IOM5 ((IOM0_Type*) IOM5_BASE) +#define IOSLAVE ((IOSLAVE_Type*) IOSLAVE_BASE) +#define MCUCTRL ((MCUCTRL_Type*) MCUCTRL_BASE) +#define MSPI ((MSPI_Type*) MSPI_BASE) +#define PDM ((PDM_Type*) PDM_BASE) +#define PWRCTRL ((PWRCTRL_Type*) PWRCTRL_BASE) +#define RSTGEN ((RSTGEN_Type*) RSTGEN_BASE) +#define RTC ((RTC_Type*) RTC_BASE) +#define SCARD ((SCARD_Type*) SCARD_BASE) +#define SECURITY ((SECURITY_Type*) SECURITY_BASE) +#define UART0 ((UART0_Type*) UART0_BASE) +#define UART1 ((UART0_Type*) UART1_BASE) +#define VCOMP ((VCOMP_Type*) VCOMP_BASE) +#define WDT ((WDT_Type*) WDT_BASE) + +/** @} */ /* End of group Device_Peripheral_declaration */ + + +/* ========================================= End of section using anonymous unions ========================================= */ +#if defined (__CC_ARM) + #pragma pop +#elif defined (__ICCARM__) + /* leave anonymous unions enabled */ +#elif (__ARMCC_VERSION >= 6010050) + #pragma clang diagnostic pop +#elif defined (__GNUC__) + /* anonymous unions are enabled by default */ +#elif defined (__TMS470__) + /* anonymous unions are enabled by default */ +#elif defined (__TASKING__) + #pragma warning restore +#elif defined (__CSMC__) + /* anonymous unions are enabled by default */ +#endif + + +/* =========================================================================================================================== */ +/* ================ Pos/Mask Peripheral Section ================ */ +/* =========================================================================================================================== */ + + +/** @addtogroup PosMask_peripherals + * @{ + */ + + + +/* =========================================================================================================================== */ +/* ================ ADC ================ */ +/* =========================================================================================================================== */ + +/* ========================================================== CFG ========================================================== */ +#define ADC_CFG_CLKSEL_Pos (24UL) /*!< ADC CFG: CLKSEL (Bit 24) */ +#define ADC_CFG_CLKSEL_Msk (0x3000000UL) /*!< ADC CFG: CLKSEL (Bitfield-Mask: 0x03) */ +#define ADC_CFG_TRIGPOL_Pos (19UL) /*!< ADC CFG: TRIGPOL (Bit 19) */ +#define ADC_CFG_TRIGPOL_Msk (0x80000UL) /*!< ADC CFG: TRIGPOL (Bitfield-Mask: 0x01) */ +#define ADC_CFG_TRIGSEL_Pos (16UL) /*!< ADC CFG: TRIGSEL (Bit 16) */ +#define ADC_CFG_TRIGSEL_Msk (0x70000UL) /*!< ADC CFG: TRIGSEL (Bitfield-Mask: 0x07) */ +#define ADC_CFG_DFIFORDEN_Pos (12UL) /*!< ADC CFG: DFIFORDEN (Bit 12) */ +#define ADC_CFG_DFIFORDEN_Msk (0x1000UL) /*!< ADC CFG: DFIFORDEN (Bitfield-Mask: 0x01) */ +#define ADC_CFG_REFSEL_Pos (8UL) /*!< ADC CFG: REFSEL (Bit 8) */ +#define ADC_CFG_REFSEL_Msk (0x300UL) /*!< ADC CFG: REFSEL (Bitfield-Mask: 0x03) */ +#define ADC_CFG_CKMODE_Pos (4UL) /*!< ADC CFG: CKMODE (Bit 4) */ +#define ADC_CFG_CKMODE_Msk (0x10UL) /*!< ADC CFG: CKMODE (Bitfield-Mask: 0x01) */ +#define ADC_CFG_LPMODE_Pos (3UL) /*!< ADC CFG: LPMODE (Bit 3) */ +#define ADC_CFG_LPMODE_Msk (0x8UL) /*!< ADC CFG: LPMODE (Bitfield-Mask: 0x01) */ +#define ADC_CFG_RPTEN_Pos (2UL) /*!< ADC CFG: RPTEN (Bit 2) */ +#define ADC_CFG_RPTEN_Msk (0x4UL) /*!< ADC CFG: RPTEN (Bitfield-Mask: 0x01) */ +#define ADC_CFG_ADCEN_Pos (0UL) /*!< ADC CFG: ADCEN (Bit 0) */ +#define ADC_CFG_ADCEN_Msk (0x1UL) /*!< ADC CFG: ADCEN (Bitfield-Mask: 0x01) */ +/* ========================================================= STAT ========================================================== */ +#define ADC_STAT_PWDSTAT_Pos (0UL) /*!< ADC STAT: PWDSTAT (Bit 0) */ +#define ADC_STAT_PWDSTAT_Msk (0x1UL) /*!< ADC STAT: PWDSTAT (Bitfield-Mask: 0x01) */ +/* ========================================================== SWT ========================================================== */ +#define ADC_SWT_SWT_Pos (0UL) /*!< ADC SWT: SWT (Bit 0) */ +#define ADC_SWT_SWT_Msk (0xffUL) /*!< ADC SWT: SWT (Bitfield-Mask: 0xff) */ +/* ======================================================== SL0CFG ========================================================= */ +#define ADC_SL0CFG_ADSEL0_Pos (24UL) /*!< ADC SL0CFG: ADSEL0 (Bit 24) */ +#define ADC_SL0CFG_ADSEL0_Msk (0x7000000UL) /*!< ADC SL0CFG: ADSEL0 (Bitfield-Mask: 0x07) */ +#define ADC_SL0CFG_PRMODE0_Pos (16UL) /*!< ADC SL0CFG: PRMODE0 (Bit 16) */ +#define ADC_SL0CFG_PRMODE0_Msk (0x30000UL) /*!< ADC SL0CFG: PRMODE0 (Bitfield-Mask: 0x03) */ +#define ADC_SL0CFG_CHSEL0_Pos (8UL) /*!< ADC SL0CFG: CHSEL0 (Bit 8) */ +#define ADC_SL0CFG_CHSEL0_Msk (0xf00UL) /*!< ADC SL0CFG: CHSEL0 (Bitfield-Mask: 0x0f) */ +#define ADC_SL0CFG_WCEN0_Pos (1UL) /*!< ADC SL0CFG: WCEN0 (Bit 1) */ +#define ADC_SL0CFG_WCEN0_Msk (0x2UL) /*!< ADC SL0CFG: WCEN0 (Bitfield-Mask: 0x01) */ +#define ADC_SL0CFG_SLEN0_Pos (0UL) /*!< ADC SL0CFG: SLEN0 (Bit 0) */ +#define ADC_SL0CFG_SLEN0_Msk (0x1UL) /*!< ADC SL0CFG: SLEN0 (Bitfield-Mask: 0x01) */ +/* ======================================================== SL1CFG ========================================================= */ +#define ADC_SL1CFG_ADSEL1_Pos (24UL) /*!< ADC SL1CFG: ADSEL1 (Bit 24) */ +#define ADC_SL1CFG_ADSEL1_Msk (0x7000000UL) /*!< ADC SL1CFG: ADSEL1 (Bitfield-Mask: 0x07) */ +#define ADC_SL1CFG_PRMODE1_Pos (16UL) /*!< ADC SL1CFG: PRMODE1 (Bit 16) */ +#define ADC_SL1CFG_PRMODE1_Msk (0x30000UL) /*!< ADC SL1CFG: PRMODE1 (Bitfield-Mask: 0x03) */ +#define ADC_SL1CFG_CHSEL1_Pos (8UL) /*!< ADC SL1CFG: CHSEL1 (Bit 8) */ +#define ADC_SL1CFG_CHSEL1_Msk (0xf00UL) /*!< ADC SL1CFG: CHSEL1 (Bitfield-Mask: 0x0f) */ +#define ADC_SL1CFG_WCEN1_Pos (1UL) /*!< ADC SL1CFG: WCEN1 (Bit 1) */ +#define ADC_SL1CFG_WCEN1_Msk (0x2UL) /*!< ADC SL1CFG: WCEN1 (Bitfield-Mask: 0x01) */ +#define ADC_SL1CFG_SLEN1_Pos (0UL) /*!< ADC SL1CFG: SLEN1 (Bit 0) */ +#define ADC_SL1CFG_SLEN1_Msk (0x1UL) /*!< ADC SL1CFG: SLEN1 (Bitfield-Mask: 0x01) */ +/* ======================================================== SL2CFG ========================================================= */ +#define ADC_SL2CFG_ADSEL2_Pos (24UL) /*!< ADC SL2CFG: ADSEL2 (Bit 24) */ +#define ADC_SL2CFG_ADSEL2_Msk (0x7000000UL) /*!< ADC SL2CFG: ADSEL2 (Bitfield-Mask: 0x07) */ +#define ADC_SL2CFG_PRMODE2_Pos (16UL) /*!< ADC SL2CFG: PRMODE2 (Bit 16) */ +#define ADC_SL2CFG_PRMODE2_Msk (0x30000UL) /*!< ADC SL2CFG: PRMODE2 (Bitfield-Mask: 0x03) */ +#define ADC_SL2CFG_CHSEL2_Pos (8UL) /*!< ADC SL2CFG: CHSEL2 (Bit 8) */ +#define ADC_SL2CFG_CHSEL2_Msk (0xf00UL) /*!< ADC SL2CFG: CHSEL2 (Bitfield-Mask: 0x0f) */ +#define ADC_SL2CFG_WCEN2_Pos (1UL) /*!< ADC SL2CFG: WCEN2 (Bit 1) */ +#define ADC_SL2CFG_WCEN2_Msk (0x2UL) /*!< ADC SL2CFG: WCEN2 (Bitfield-Mask: 0x01) */ +#define ADC_SL2CFG_SLEN2_Pos (0UL) /*!< ADC SL2CFG: SLEN2 (Bit 0) */ +#define ADC_SL2CFG_SLEN2_Msk (0x1UL) /*!< ADC SL2CFG: SLEN2 (Bitfield-Mask: 0x01) */ +/* ======================================================== SL3CFG ========================================================= */ +#define ADC_SL3CFG_ADSEL3_Pos (24UL) /*!< ADC SL3CFG: ADSEL3 (Bit 24) */ +#define ADC_SL3CFG_ADSEL3_Msk (0x7000000UL) /*!< ADC SL3CFG: ADSEL3 (Bitfield-Mask: 0x07) */ +#define ADC_SL3CFG_PRMODE3_Pos (16UL) /*!< ADC SL3CFG: PRMODE3 (Bit 16) */ +#define ADC_SL3CFG_PRMODE3_Msk (0x30000UL) /*!< ADC SL3CFG: PRMODE3 (Bitfield-Mask: 0x03) */ +#define ADC_SL3CFG_CHSEL3_Pos (8UL) /*!< ADC SL3CFG: CHSEL3 (Bit 8) */ +#define ADC_SL3CFG_CHSEL3_Msk (0xf00UL) /*!< ADC SL3CFG: CHSEL3 (Bitfield-Mask: 0x0f) */ +#define ADC_SL3CFG_WCEN3_Pos (1UL) /*!< ADC SL3CFG: WCEN3 (Bit 1) */ +#define ADC_SL3CFG_WCEN3_Msk (0x2UL) /*!< ADC SL3CFG: WCEN3 (Bitfield-Mask: 0x01) */ +#define ADC_SL3CFG_SLEN3_Pos (0UL) /*!< ADC SL3CFG: SLEN3 (Bit 0) */ +#define ADC_SL3CFG_SLEN3_Msk (0x1UL) /*!< ADC SL3CFG: SLEN3 (Bitfield-Mask: 0x01) */ +/* ======================================================== SL4CFG ========================================================= */ +#define ADC_SL4CFG_ADSEL4_Pos (24UL) /*!< ADC SL4CFG: ADSEL4 (Bit 24) */ +#define ADC_SL4CFG_ADSEL4_Msk (0x7000000UL) /*!< ADC SL4CFG: ADSEL4 (Bitfield-Mask: 0x07) */ +#define ADC_SL4CFG_PRMODE4_Pos (16UL) /*!< ADC SL4CFG: PRMODE4 (Bit 16) */ +#define ADC_SL4CFG_PRMODE4_Msk (0x30000UL) /*!< ADC SL4CFG: PRMODE4 (Bitfield-Mask: 0x03) */ +#define ADC_SL4CFG_CHSEL4_Pos (8UL) /*!< ADC SL4CFG: CHSEL4 (Bit 8) */ +#define ADC_SL4CFG_CHSEL4_Msk (0xf00UL) /*!< ADC SL4CFG: CHSEL4 (Bitfield-Mask: 0x0f) */ +#define ADC_SL4CFG_WCEN4_Pos (1UL) /*!< ADC SL4CFG: WCEN4 (Bit 1) */ +#define ADC_SL4CFG_WCEN4_Msk (0x2UL) /*!< ADC SL4CFG: WCEN4 (Bitfield-Mask: 0x01) */ +#define ADC_SL4CFG_SLEN4_Pos (0UL) /*!< ADC SL4CFG: SLEN4 (Bit 0) */ +#define ADC_SL4CFG_SLEN4_Msk (0x1UL) /*!< ADC SL4CFG: SLEN4 (Bitfield-Mask: 0x01) */ +/* ======================================================== SL5CFG ========================================================= */ +#define ADC_SL5CFG_ADSEL5_Pos (24UL) /*!< ADC SL5CFG: ADSEL5 (Bit 24) */ +#define ADC_SL5CFG_ADSEL5_Msk (0x7000000UL) /*!< ADC SL5CFG: ADSEL5 (Bitfield-Mask: 0x07) */ +#define ADC_SL5CFG_PRMODE5_Pos (16UL) /*!< ADC SL5CFG: PRMODE5 (Bit 16) */ +#define ADC_SL5CFG_PRMODE5_Msk (0x30000UL) /*!< ADC SL5CFG: PRMODE5 (Bitfield-Mask: 0x03) */ +#define ADC_SL5CFG_CHSEL5_Pos (8UL) /*!< ADC SL5CFG: CHSEL5 (Bit 8) */ +#define ADC_SL5CFG_CHSEL5_Msk (0xf00UL) /*!< ADC SL5CFG: CHSEL5 (Bitfield-Mask: 0x0f) */ +#define ADC_SL5CFG_WCEN5_Pos (1UL) /*!< ADC SL5CFG: WCEN5 (Bit 1) */ +#define ADC_SL5CFG_WCEN5_Msk (0x2UL) /*!< ADC SL5CFG: WCEN5 (Bitfield-Mask: 0x01) */ +#define ADC_SL5CFG_SLEN5_Pos (0UL) /*!< ADC SL5CFG: SLEN5 (Bit 0) */ +#define ADC_SL5CFG_SLEN5_Msk (0x1UL) /*!< ADC SL5CFG: SLEN5 (Bitfield-Mask: 0x01) */ +/* ======================================================== SL6CFG ========================================================= */ +#define ADC_SL6CFG_ADSEL6_Pos (24UL) /*!< ADC SL6CFG: ADSEL6 (Bit 24) */ +#define ADC_SL6CFG_ADSEL6_Msk (0x7000000UL) /*!< ADC SL6CFG: ADSEL6 (Bitfield-Mask: 0x07) */ +#define ADC_SL6CFG_PRMODE6_Pos (16UL) /*!< ADC SL6CFG: PRMODE6 (Bit 16) */ +#define ADC_SL6CFG_PRMODE6_Msk (0x30000UL) /*!< ADC SL6CFG: PRMODE6 (Bitfield-Mask: 0x03) */ +#define ADC_SL6CFG_CHSEL6_Pos (8UL) /*!< ADC SL6CFG: CHSEL6 (Bit 8) */ +#define ADC_SL6CFG_CHSEL6_Msk (0xf00UL) /*!< ADC SL6CFG: CHSEL6 (Bitfield-Mask: 0x0f) */ +#define ADC_SL6CFG_WCEN6_Pos (1UL) /*!< ADC SL6CFG: WCEN6 (Bit 1) */ +#define ADC_SL6CFG_WCEN6_Msk (0x2UL) /*!< ADC SL6CFG: WCEN6 (Bitfield-Mask: 0x01) */ +#define ADC_SL6CFG_SLEN6_Pos (0UL) /*!< ADC SL6CFG: SLEN6 (Bit 0) */ +#define ADC_SL6CFG_SLEN6_Msk (0x1UL) /*!< ADC SL6CFG: SLEN6 (Bitfield-Mask: 0x01) */ +/* ======================================================== SL7CFG ========================================================= */ +#define ADC_SL7CFG_ADSEL7_Pos (24UL) /*!< ADC SL7CFG: ADSEL7 (Bit 24) */ +#define ADC_SL7CFG_ADSEL7_Msk (0x7000000UL) /*!< ADC SL7CFG: ADSEL7 (Bitfield-Mask: 0x07) */ +#define ADC_SL7CFG_PRMODE7_Pos (16UL) /*!< ADC SL7CFG: PRMODE7 (Bit 16) */ +#define ADC_SL7CFG_PRMODE7_Msk (0x30000UL) /*!< ADC SL7CFG: PRMODE7 (Bitfield-Mask: 0x03) */ +#define ADC_SL7CFG_CHSEL7_Pos (8UL) /*!< ADC SL7CFG: CHSEL7 (Bit 8) */ +#define ADC_SL7CFG_CHSEL7_Msk (0xf00UL) /*!< ADC SL7CFG: CHSEL7 (Bitfield-Mask: 0x0f) */ +#define ADC_SL7CFG_WCEN7_Pos (1UL) /*!< ADC SL7CFG: WCEN7 (Bit 1) */ +#define ADC_SL7CFG_WCEN7_Msk (0x2UL) /*!< ADC SL7CFG: WCEN7 (Bitfield-Mask: 0x01) */ +#define ADC_SL7CFG_SLEN7_Pos (0UL) /*!< ADC SL7CFG: SLEN7 (Bit 0) */ +#define ADC_SL7CFG_SLEN7_Msk (0x1UL) /*!< ADC SL7CFG: SLEN7 (Bitfield-Mask: 0x01) */ +/* ========================================================= WULIM ========================================================= */ +#define ADC_WULIM_ULIM_Pos (0UL) /*!< ADC WULIM: ULIM (Bit 0) */ +#define ADC_WULIM_ULIM_Msk (0xfffffUL) /*!< ADC WULIM: ULIM (Bitfield-Mask: 0xfffff) */ +/* ========================================================= WLLIM ========================================================= */ +#define ADC_WLLIM_LLIM_Pos (0UL) /*!< ADC WLLIM: LLIM (Bit 0) */ +#define ADC_WLLIM_LLIM_Msk (0xfffffUL) /*!< ADC WLLIM: LLIM (Bitfield-Mask: 0xfffff) */ +/* ======================================================== SCWLIM ========================================================= */ +#define ADC_SCWLIM_SCWLIMEN_Pos (0UL) /*!< ADC SCWLIM: SCWLIMEN (Bit 0) */ +#define ADC_SCWLIM_SCWLIMEN_Msk (0x1UL) /*!< ADC SCWLIM: SCWLIMEN (Bitfield-Mask: 0x01) */ +/* ========================================================= FIFO ========================================================== */ +#define ADC_FIFO_RSVD_Pos (31UL) /*!< ADC FIFO: RSVD (Bit 31) */ +#define ADC_FIFO_RSVD_Msk (0x80000000UL) /*!< ADC FIFO: RSVD (Bitfield-Mask: 0x01) */ +#define ADC_FIFO_SLOTNUM_Pos (28UL) /*!< ADC FIFO: SLOTNUM (Bit 28) */ +#define ADC_FIFO_SLOTNUM_Msk (0x70000000UL) /*!< ADC FIFO: SLOTNUM (Bitfield-Mask: 0x07) */ +#define ADC_FIFO_COUNT_Pos (20UL) /*!< ADC FIFO: COUNT (Bit 20) */ +#define ADC_FIFO_COUNT_Msk (0xff00000UL) /*!< ADC FIFO: COUNT (Bitfield-Mask: 0xff) */ +#define ADC_FIFO_DATA_Pos (0UL) /*!< ADC FIFO: DATA (Bit 0) */ +#define ADC_FIFO_DATA_Msk (0xfffffUL) /*!< ADC FIFO: DATA (Bitfield-Mask: 0xfffff) */ +/* ======================================================== FIFOPR ========================================================= */ +#define ADC_FIFOPR_RSVDPR_Pos (31UL) /*!< ADC FIFOPR: RSVDPR (Bit 31) */ +#define ADC_FIFOPR_RSVDPR_Msk (0x80000000UL) /*!< ADC FIFOPR: RSVDPR (Bitfield-Mask: 0x01) */ +#define ADC_FIFOPR_SLOTNUMPR_Pos (28UL) /*!< ADC FIFOPR: SLOTNUMPR (Bit 28) */ +#define ADC_FIFOPR_SLOTNUMPR_Msk (0x70000000UL) /*!< ADC FIFOPR: SLOTNUMPR (Bitfield-Mask: 0x07) */ +#define ADC_FIFOPR_COUNT_Pos (20UL) /*!< ADC FIFOPR: COUNT (Bit 20) */ +#define ADC_FIFOPR_COUNT_Msk (0xff00000UL) /*!< ADC FIFOPR: COUNT (Bitfield-Mask: 0xff) */ +#define ADC_FIFOPR_DATA_Pos (0UL) /*!< ADC FIFOPR: DATA (Bit 0) */ +#define ADC_FIFOPR_DATA_Msk (0xfffffUL) /*!< ADC FIFOPR: DATA (Bitfield-Mask: 0xfffff) */ +/* ========================================================= INTEN ========================================================= */ +#define ADC_INTEN_DERR_Pos (7UL) /*!< ADC INTEN: DERR (Bit 7) */ +#define ADC_INTEN_DERR_Msk (0x80UL) /*!< ADC INTEN: DERR (Bitfield-Mask: 0x01) */ +#define ADC_INTEN_DCMP_Pos (6UL) /*!< ADC INTEN: DCMP (Bit 6) */ +#define ADC_INTEN_DCMP_Msk (0x40UL) /*!< ADC INTEN: DCMP (Bitfield-Mask: 0x01) */ +#define ADC_INTEN_WCINC_Pos (5UL) /*!< ADC INTEN: WCINC (Bit 5) */ +#define ADC_INTEN_WCINC_Msk (0x20UL) /*!< ADC INTEN: WCINC (Bitfield-Mask: 0x01) */ +#define ADC_INTEN_WCEXC_Pos (4UL) /*!< ADC INTEN: WCEXC (Bit 4) */ +#define ADC_INTEN_WCEXC_Msk (0x10UL) /*!< ADC INTEN: WCEXC (Bitfield-Mask: 0x01) */ +#define ADC_INTEN_FIFOOVR2_Pos (3UL) /*!< ADC INTEN: FIFOOVR2 (Bit 3) */ +#define ADC_INTEN_FIFOOVR2_Msk (0x8UL) /*!< ADC INTEN: FIFOOVR2 (Bitfield-Mask: 0x01) */ +#define ADC_INTEN_FIFOOVR1_Pos (2UL) /*!< ADC INTEN: FIFOOVR1 (Bit 2) */ +#define ADC_INTEN_FIFOOVR1_Msk (0x4UL) /*!< ADC INTEN: FIFOOVR1 (Bitfield-Mask: 0x01) */ +#define ADC_INTEN_SCNCMP_Pos (1UL) /*!< ADC INTEN: SCNCMP (Bit 1) */ +#define ADC_INTEN_SCNCMP_Msk (0x2UL) /*!< ADC INTEN: SCNCMP (Bitfield-Mask: 0x01) */ +#define ADC_INTEN_CNVCMP_Pos (0UL) /*!< ADC INTEN: CNVCMP (Bit 0) */ +#define ADC_INTEN_CNVCMP_Msk (0x1UL) /*!< ADC INTEN: CNVCMP (Bitfield-Mask: 0x01) */ +/* ======================================================== INTSTAT ======================================================== */ +#define ADC_INTSTAT_DERR_Pos (7UL) /*!< ADC INTSTAT: DERR (Bit 7) */ +#define ADC_INTSTAT_DERR_Msk (0x80UL) /*!< ADC INTSTAT: DERR (Bitfield-Mask: 0x01) */ +#define ADC_INTSTAT_DCMP_Pos (6UL) /*!< ADC INTSTAT: DCMP (Bit 6) */ +#define ADC_INTSTAT_DCMP_Msk (0x40UL) /*!< ADC INTSTAT: DCMP (Bitfield-Mask: 0x01) */ +#define ADC_INTSTAT_WCINC_Pos (5UL) /*!< ADC INTSTAT: WCINC (Bit 5) */ +#define ADC_INTSTAT_WCINC_Msk (0x20UL) /*!< ADC INTSTAT: WCINC (Bitfield-Mask: 0x01) */ +#define ADC_INTSTAT_WCEXC_Pos (4UL) /*!< ADC INTSTAT: WCEXC (Bit 4) */ +#define ADC_INTSTAT_WCEXC_Msk (0x10UL) /*!< ADC INTSTAT: WCEXC (Bitfield-Mask: 0x01) */ +#define ADC_INTSTAT_FIFOOVR2_Pos (3UL) /*!< ADC INTSTAT: FIFOOVR2 (Bit 3) */ +#define ADC_INTSTAT_FIFOOVR2_Msk (0x8UL) /*!< ADC INTSTAT: FIFOOVR2 (Bitfield-Mask: 0x01) */ +#define ADC_INTSTAT_FIFOOVR1_Pos (2UL) /*!< ADC INTSTAT: FIFOOVR1 (Bit 2) */ +#define ADC_INTSTAT_FIFOOVR1_Msk (0x4UL) /*!< ADC INTSTAT: FIFOOVR1 (Bitfield-Mask: 0x01) */ +#define ADC_INTSTAT_SCNCMP_Pos (1UL) /*!< ADC INTSTAT: SCNCMP (Bit 1) */ +#define ADC_INTSTAT_SCNCMP_Msk (0x2UL) /*!< ADC INTSTAT: SCNCMP (Bitfield-Mask: 0x01) */ +#define ADC_INTSTAT_CNVCMP_Pos (0UL) /*!< ADC INTSTAT: CNVCMP (Bit 0) */ +#define ADC_INTSTAT_CNVCMP_Msk (0x1UL) /*!< ADC INTSTAT: CNVCMP (Bitfield-Mask: 0x01) */ +/* ======================================================== INTCLR ========================================================= */ +#define ADC_INTCLR_DERR_Pos (7UL) /*!< ADC INTCLR: DERR (Bit 7) */ +#define ADC_INTCLR_DERR_Msk (0x80UL) /*!< ADC INTCLR: DERR (Bitfield-Mask: 0x01) */ +#define ADC_INTCLR_DCMP_Pos (6UL) /*!< ADC INTCLR: DCMP (Bit 6) */ +#define ADC_INTCLR_DCMP_Msk (0x40UL) /*!< ADC INTCLR: DCMP (Bitfield-Mask: 0x01) */ +#define ADC_INTCLR_WCINC_Pos (5UL) /*!< ADC INTCLR: WCINC (Bit 5) */ +#define ADC_INTCLR_WCINC_Msk (0x20UL) /*!< ADC INTCLR: WCINC (Bitfield-Mask: 0x01) */ +#define ADC_INTCLR_WCEXC_Pos (4UL) /*!< ADC INTCLR: WCEXC (Bit 4) */ +#define ADC_INTCLR_WCEXC_Msk (0x10UL) /*!< ADC INTCLR: WCEXC (Bitfield-Mask: 0x01) */ +#define ADC_INTCLR_FIFOOVR2_Pos (3UL) /*!< ADC INTCLR: FIFOOVR2 (Bit 3) */ +#define ADC_INTCLR_FIFOOVR2_Msk (0x8UL) /*!< ADC INTCLR: FIFOOVR2 (Bitfield-Mask: 0x01) */ +#define ADC_INTCLR_FIFOOVR1_Pos (2UL) /*!< ADC INTCLR: FIFOOVR1 (Bit 2) */ +#define ADC_INTCLR_FIFOOVR1_Msk (0x4UL) /*!< ADC INTCLR: FIFOOVR1 (Bitfield-Mask: 0x01) */ +#define ADC_INTCLR_SCNCMP_Pos (1UL) /*!< ADC INTCLR: SCNCMP (Bit 1) */ +#define ADC_INTCLR_SCNCMP_Msk (0x2UL) /*!< ADC INTCLR: SCNCMP (Bitfield-Mask: 0x01) */ +#define ADC_INTCLR_CNVCMP_Pos (0UL) /*!< ADC INTCLR: CNVCMP (Bit 0) */ +#define ADC_INTCLR_CNVCMP_Msk (0x1UL) /*!< ADC INTCLR: CNVCMP (Bitfield-Mask: 0x01) */ +/* ======================================================== INTSET ========================================================= */ +#define ADC_INTSET_DERR_Pos (7UL) /*!< ADC INTSET: DERR (Bit 7) */ +#define ADC_INTSET_DERR_Msk (0x80UL) /*!< ADC INTSET: DERR (Bitfield-Mask: 0x01) */ +#define ADC_INTSET_DCMP_Pos (6UL) /*!< ADC INTSET: DCMP (Bit 6) */ +#define ADC_INTSET_DCMP_Msk (0x40UL) /*!< ADC INTSET: DCMP (Bitfield-Mask: 0x01) */ +#define ADC_INTSET_WCINC_Pos (5UL) /*!< ADC INTSET: WCINC (Bit 5) */ +#define ADC_INTSET_WCINC_Msk (0x20UL) /*!< ADC INTSET: WCINC (Bitfield-Mask: 0x01) */ +#define ADC_INTSET_WCEXC_Pos (4UL) /*!< ADC INTSET: WCEXC (Bit 4) */ +#define ADC_INTSET_WCEXC_Msk (0x10UL) /*!< ADC INTSET: WCEXC (Bitfield-Mask: 0x01) */ +#define ADC_INTSET_FIFOOVR2_Pos (3UL) /*!< ADC INTSET: FIFOOVR2 (Bit 3) */ +#define ADC_INTSET_FIFOOVR2_Msk (0x8UL) /*!< ADC INTSET: FIFOOVR2 (Bitfield-Mask: 0x01) */ +#define ADC_INTSET_FIFOOVR1_Pos (2UL) /*!< ADC INTSET: FIFOOVR1 (Bit 2) */ +#define ADC_INTSET_FIFOOVR1_Msk (0x4UL) /*!< ADC INTSET: FIFOOVR1 (Bitfield-Mask: 0x01) */ +#define ADC_INTSET_SCNCMP_Pos (1UL) /*!< ADC INTSET: SCNCMP (Bit 1) */ +#define ADC_INTSET_SCNCMP_Msk (0x2UL) /*!< ADC INTSET: SCNCMP (Bitfield-Mask: 0x01) */ +#define ADC_INTSET_CNVCMP_Pos (0UL) /*!< ADC INTSET: CNVCMP (Bit 0) */ +#define ADC_INTSET_CNVCMP_Msk (0x1UL) /*!< ADC INTSET: CNVCMP (Bitfield-Mask: 0x01) */ +/* ======================================================= DMATRIGEN ======================================================= */ +#define ADC_DMATRIGEN_DFIFOFULL_Pos (1UL) /*!< ADC DMATRIGEN: DFIFOFULL (Bit 1) */ +#define ADC_DMATRIGEN_DFIFOFULL_Msk (0x2UL) /*!< ADC DMATRIGEN: DFIFOFULL (Bitfield-Mask: 0x01) */ +#define ADC_DMATRIGEN_DFIFO75_Pos (0UL) /*!< ADC DMATRIGEN: DFIFO75 (Bit 0) */ +#define ADC_DMATRIGEN_DFIFO75_Msk (0x1UL) /*!< ADC DMATRIGEN: DFIFO75 (Bitfield-Mask: 0x01) */ +/* ====================================================== DMATRIGSTAT ====================================================== */ +#define ADC_DMATRIGSTAT_DFULLSTAT_Pos (1UL) /*!< ADC DMATRIGSTAT: DFULLSTAT (Bit 1) */ +#define ADC_DMATRIGSTAT_DFULLSTAT_Msk (0x2UL) /*!< ADC DMATRIGSTAT: DFULLSTAT (Bitfield-Mask: 0x01) */ +#define ADC_DMATRIGSTAT_D75STAT_Pos (0UL) /*!< ADC DMATRIGSTAT: D75STAT (Bit 0) */ +#define ADC_DMATRIGSTAT_D75STAT_Msk (0x1UL) /*!< ADC DMATRIGSTAT: D75STAT (Bitfield-Mask: 0x01) */ +/* ======================================================== DMACFG ========================================================= */ +#define ADC_DMACFG_DPWROFF_Pos (18UL) /*!< ADC DMACFG: DPWROFF (Bit 18) */ +#define ADC_DMACFG_DPWROFF_Msk (0x40000UL) /*!< ADC DMACFG: DPWROFF (Bitfield-Mask: 0x01) */ +#define ADC_DMACFG_DMAMSK_Pos (17UL) /*!< ADC DMACFG: DMAMSK (Bit 17) */ +#define ADC_DMACFG_DMAMSK_Msk (0x20000UL) /*!< ADC DMACFG: DMAMSK (Bitfield-Mask: 0x01) */ +#define ADC_DMACFG_DMAHONSTAT_Pos (16UL) /*!< ADC DMACFG: DMAHONSTAT (Bit 16) */ +#define ADC_DMACFG_DMAHONSTAT_Msk (0x10000UL) /*!< ADC DMACFG: DMAHONSTAT (Bitfield-Mask: 0x01) */ +#define ADC_DMACFG_DMADYNPRI_Pos (9UL) /*!< ADC DMACFG: DMADYNPRI (Bit 9) */ +#define ADC_DMACFG_DMADYNPRI_Msk (0x200UL) /*!< ADC DMACFG: DMADYNPRI (Bitfield-Mask: 0x01) */ +#define ADC_DMACFG_DMAPRI_Pos (8UL) /*!< ADC DMACFG: DMAPRI (Bit 8) */ +#define ADC_DMACFG_DMAPRI_Msk (0x100UL) /*!< ADC DMACFG: DMAPRI (Bitfield-Mask: 0x01) */ +#define ADC_DMACFG_DMADIR_Pos (2UL) /*!< ADC DMACFG: DMADIR (Bit 2) */ +#define ADC_DMACFG_DMADIR_Msk (0x4UL) /*!< ADC DMACFG: DMADIR (Bitfield-Mask: 0x01) */ +#define ADC_DMACFG_DMAEN_Pos (0UL) /*!< ADC DMACFG: DMAEN (Bit 0) */ +#define ADC_DMACFG_DMAEN_Msk (0x1UL) /*!< ADC DMACFG: DMAEN (Bitfield-Mask: 0x01) */ +/* ====================================================== DMATOTCOUNT ====================================================== */ +#define ADC_DMATOTCOUNT_TOTCOUNT_Pos (2UL) /*!< ADC DMATOTCOUNT: TOTCOUNT (Bit 2) */ +#define ADC_DMATOTCOUNT_TOTCOUNT_Msk (0x3fffcUL) /*!< ADC DMATOTCOUNT: TOTCOUNT (Bitfield-Mask: 0xffff) */ +/* ====================================================== DMATARGADDR ====================================================== */ +#define ADC_DMATARGADDR_UTARGADDR_Pos (19UL) /*!< ADC DMATARGADDR: UTARGADDR (Bit 19) */ +#define ADC_DMATARGADDR_UTARGADDR_Msk (0xfff80000UL) /*!< ADC DMATARGADDR: UTARGADDR (Bitfield-Mask: 0x1fff) */ +#define ADC_DMATARGADDR_LTARGADDR_Pos (0UL) /*!< ADC DMATARGADDR: LTARGADDR (Bit 0) */ +#define ADC_DMATARGADDR_LTARGADDR_Msk (0x7ffffUL) /*!< ADC DMATARGADDR: LTARGADDR (Bitfield-Mask: 0x7ffff) */ +/* ======================================================== DMASTAT ======================================================== */ +#define ADC_DMASTAT_DMAERR_Pos (2UL) /*!< ADC DMASTAT: DMAERR (Bit 2) */ +#define ADC_DMASTAT_DMAERR_Msk (0x4UL) /*!< ADC DMASTAT: DMAERR (Bitfield-Mask: 0x01) */ +#define ADC_DMASTAT_DMACPL_Pos (1UL) /*!< ADC DMASTAT: DMACPL (Bit 1) */ +#define ADC_DMASTAT_DMACPL_Msk (0x2UL) /*!< ADC DMASTAT: DMACPL (Bitfield-Mask: 0x01) */ +#define ADC_DMASTAT_DMATIP_Pos (0UL) /*!< ADC DMASTAT: DMATIP (Bit 0) */ +#define ADC_DMASTAT_DMATIP_Msk (0x1UL) /*!< ADC DMASTAT: DMATIP (Bitfield-Mask: 0x01) */ + + +/* =========================================================================================================================== */ +/* ================ APBDMA ================ */ +/* =========================================================================================================================== */ + +/* ======================================================== BBVALUE ======================================================== */ +#define APBDMA_BBVALUE_PIN_Pos (16UL) /*!< APBDMA BBVALUE: PIN (Bit 16) */ +#define APBDMA_BBVALUE_PIN_Msk (0xff0000UL) /*!< APBDMA BBVALUE: PIN (Bitfield-Mask: 0xff) */ +#define APBDMA_BBVALUE_DATAOUT_Pos (0UL) /*!< APBDMA BBVALUE: DATAOUT (Bit 0) */ +#define APBDMA_BBVALUE_DATAOUT_Msk (0xffUL) /*!< APBDMA BBVALUE: DATAOUT (Bitfield-Mask: 0xff) */ +/* ====================================================== BBSETCLEAR ======================================================= */ +#define APBDMA_BBSETCLEAR_CLEAR_Pos (16UL) /*!< APBDMA BBSETCLEAR: CLEAR (Bit 16) */ +#define APBDMA_BBSETCLEAR_CLEAR_Msk (0xff0000UL) /*!< APBDMA BBSETCLEAR: CLEAR (Bitfield-Mask: 0xff) */ +#define APBDMA_BBSETCLEAR_SET_Pos (0UL) /*!< APBDMA BBSETCLEAR: SET (Bit 0) */ +#define APBDMA_BBSETCLEAR_SET_Msk (0xffUL) /*!< APBDMA BBSETCLEAR: SET (Bitfield-Mask: 0xff) */ +/* ======================================================== BBINPUT ======================================================== */ +#define APBDMA_BBINPUT_DATAIN_Pos (0UL) /*!< APBDMA BBINPUT: DATAIN (Bit 0) */ +#define APBDMA_BBINPUT_DATAIN_Msk (0xffUL) /*!< APBDMA BBINPUT: DATAIN (Bitfield-Mask: 0xff) */ +/* ======================================================= DEBUGDATA ======================================================= */ +#define APBDMA_DEBUGDATA_DEBUGDATA_Pos (0UL) /*!< APBDMA DEBUGDATA: DEBUGDATA (Bit 0) */ +#define APBDMA_DEBUGDATA_DEBUGDATA_Msk (0xffffffffUL) /*!< APBDMA DEBUGDATA: DEBUGDATA (Bitfield-Mask: 0xffffffff) */ +/* ========================================================= DEBUG ========================================================= */ +#define APBDMA_DEBUG_DEBUGEN_Pos (0UL) /*!< APBDMA DEBUG: DEBUGEN (Bit 0) */ +#define APBDMA_DEBUG_DEBUGEN_Msk (0xfUL) /*!< APBDMA DEBUG: DEBUGEN (Bitfield-Mask: 0x0f) */ + + +/* =========================================================================================================================== */ +/* ================ BLEIF ================ */ +/* =========================================================================================================================== */ + +/* ========================================================= FIFO ========================================================== */ +#define BLEIF_FIFO_FIFO_Pos (0UL) /*!< BLEIF FIFO: FIFO (Bit 0) */ +#define BLEIF_FIFO_FIFO_Msk (0xffffffffUL) /*!< BLEIF FIFO: FIFO (Bitfield-Mask: 0xffffffff) */ +/* ======================================================== FIFOPTR ======================================================== */ +#define BLEIF_FIFOPTR_FIFO1REM_Pos (24UL) /*!< BLEIF FIFOPTR: FIFO1REM (Bit 24) */ +#define BLEIF_FIFOPTR_FIFO1REM_Msk (0xff000000UL) /*!< BLEIF FIFOPTR: FIFO1REM (Bitfield-Mask: 0xff) */ +#define BLEIF_FIFOPTR_FIFO1SIZ_Pos (16UL) /*!< BLEIF FIFOPTR: FIFO1SIZ (Bit 16) */ +#define BLEIF_FIFOPTR_FIFO1SIZ_Msk (0xff0000UL) /*!< BLEIF FIFOPTR: FIFO1SIZ (Bitfield-Mask: 0xff) */ +#define BLEIF_FIFOPTR_FIFO0REM_Pos (8UL) /*!< BLEIF FIFOPTR: FIFO0REM (Bit 8) */ +#define BLEIF_FIFOPTR_FIFO0REM_Msk (0xff00UL) /*!< BLEIF FIFOPTR: FIFO0REM (Bitfield-Mask: 0xff) */ +#define BLEIF_FIFOPTR_FIFO0SIZ_Pos (0UL) /*!< BLEIF FIFOPTR: FIFO0SIZ (Bit 0) */ +#define BLEIF_FIFOPTR_FIFO0SIZ_Msk (0xffUL) /*!< BLEIF FIFOPTR: FIFO0SIZ (Bitfield-Mask: 0xff) */ +/* ======================================================== FIFOTHR ======================================================== */ +#define BLEIF_FIFOTHR_FIFOWTHR_Pos (8UL) /*!< BLEIF FIFOTHR: FIFOWTHR (Bit 8) */ +#define BLEIF_FIFOTHR_FIFOWTHR_Msk (0x3f00UL) /*!< BLEIF FIFOTHR: FIFOWTHR (Bitfield-Mask: 0x3f) */ +#define BLEIF_FIFOTHR_FIFORTHR_Pos (0UL) /*!< BLEIF FIFOTHR: FIFORTHR (Bit 0) */ +#define BLEIF_FIFOTHR_FIFORTHR_Msk (0x3fUL) /*!< BLEIF FIFOTHR: FIFORTHR (Bitfield-Mask: 0x3f) */ +/* ======================================================== FIFOPOP ======================================================== */ +#define BLEIF_FIFOPOP_FIFODOUT_Pos (0UL) /*!< BLEIF FIFOPOP: FIFODOUT (Bit 0) */ +#define BLEIF_FIFOPOP_FIFODOUT_Msk (0xffffffffUL) /*!< BLEIF FIFOPOP: FIFODOUT (Bitfield-Mask: 0xffffffff) */ +/* ======================================================= FIFOPUSH ======================================================== */ +#define BLEIF_FIFOPUSH_FIFODIN_Pos (0UL) /*!< BLEIF FIFOPUSH: FIFODIN (Bit 0) */ +#define BLEIF_FIFOPUSH_FIFODIN_Msk (0xffffffffUL) /*!< BLEIF FIFOPUSH: FIFODIN (Bitfield-Mask: 0xffffffff) */ +/* ======================================================= FIFOCTRL ======================================================== */ +#define BLEIF_FIFOCTRL_FIFORSTN_Pos (1UL) /*!< BLEIF FIFOCTRL: FIFORSTN (Bit 1) */ +#define BLEIF_FIFOCTRL_FIFORSTN_Msk (0x2UL) /*!< BLEIF FIFOCTRL: FIFORSTN (Bitfield-Mask: 0x01) */ +#define BLEIF_FIFOCTRL_POPWR_Pos (0UL) /*!< BLEIF FIFOCTRL: POPWR (Bit 0) */ +#define BLEIF_FIFOCTRL_POPWR_Msk (0x1UL) /*!< BLEIF FIFOCTRL: POPWR (Bitfield-Mask: 0x01) */ +/* ======================================================== FIFOLOC ======================================================== */ +#define BLEIF_FIFOLOC_FIFORPTR_Pos (8UL) /*!< BLEIF FIFOLOC: FIFORPTR (Bit 8) */ +#define BLEIF_FIFOLOC_FIFORPTR_Msk (0xf00UL) /*!< BLEIF FIFOLOC: FIFORPTR (Bitfield-Mask: 0x0f) */ +#define BLEIF_FIFOLOC_FIFOWPTR_Pos (0UL) /*!< BLEIF FIFOLOC: FIFOWPTR (Bit 0) */ +#define BLEIF_FIFOLOC_FIFOWPTR_Msk (0xfUL) /*!< BLEIF FIFOLOC: FIFOWPTR (Bitfield-Mask: 0x0f) */ +/* ======================================================== CLKCFG ========================================================= */ +#define BLEIF_CLKCFG_DIV3_Pos (12UL) /*!< BLEIF CLKCFG: DIV3 (Bit 12) */ +#define BLEIF_CLKCFG_DIV3_Msk (0x1000UL) /*!< BLEIF CLKCFG: DIV3 (Bitfield-Mask: 0x01) */ +#define BLEIF_CLKCFG_CLK32KEN_Pos (11UL) /*!< BLEIF CLKCFG: CLK32KEN (Bit 11) */ +#define BLEIF_CLKCFG_CLK32KEN_Msk (0x800UL) /*!< BLEIF CLKCFG: CLK32KEN (Bitfield-Mask: 0x01) */ +#define BLEIF_CLKCFG_FSEL_Pos (8UL) /*!< BLEIF CLKCFG: FSEL (Bit 8) */ +#define BLEIF_CLKCFG_FSEL_Msk (0x700UL) /*!< BLEIF CLKCFG: FSEL (Bitfield-Mask: 0x07) */ +#define BLEIF_CLKCFG_IOCLKEN_Pos (0UL) /*!< BLEIF CLKCFG: IOCLKEN (Bit 0) */ +#define BLEIF_CLKCFG_IOCLKEN_Msk (0x1UL) /*!< BLEIF CLKCFG: IOCLKEN (Bitfield-Mask: 0x01) */ +/* ========================================================== CMD ========================================================== */ +#define BLEIF_CMD_OFFSETLO_Pos (24UL) /*!< BLEIF CMD: OFFSETLO (Bit 24) */ +#define BLEIF_CMD_OFFSETLO_Msk (0xff000000UL) /*!< BLEIF CMD: OFFSETLO (Bitfield-Mask: 0xff) */ +#define BLEIF_CMD_CMDSEL_Pos (20UL) /*!< BLEIF CMD: CMDSEL (Bit 20) */ +#define BLEIF_CMD_CMDSEL_Msk (0x300000UL) /*!< BLEIF CMD: CMDSEL (Bitfield-Mask: 0x03) */ +#define BLEIF_CMD_TSIZE_Pos (8UL) /*!< BLEIF CMD: TSIZE (Bit 8) */ +#define BLEIF_CMD_TSIZE_Msk (0xfff00UL) /*!< BLEIF CMD: TSIZE (Bitfield-Mask: 0xfff) */ +#define BLEIF_CMD_CONT_Pos (7UL) /*!< BLEIF CMD: CONT (Bit 7) */ +#define BLEIF_CMD_CONT_Msk (0x80UL) /*!< BLEIF CMD: CONT (Bitfield-Mask: 0x01) */ +#define BLEIF_CMD_OFFSETCNT_Pos (5UL) /*!< BLEIF CMD: OFFSETCNT (Bit 5) */ +#define BLEIF_CMD_OFFSETCNT_Msk (0x60UL) /*!< BLEIF CMD: OFFSETCNT (Bitfield-Mask: 0x03) */ +#define BLEIF_CMD_CMD_Pos (0UL) /*!< BLEIF CMD: CMD (Bit 0) */ +#define BLEIF_CMD_CMD_Msk (0x1fUL) /*!< BLEIF CMD: CMD (Bitfield-Mask: 0x1f) */ +/* ======================================================== CMDRPT ========================================================= */ +#define BLEIF_CMDRPT_CMDRPT_Pos (0UL) /*!< BLEIF CMDRPT: CMDRPT (Bit 0) */ +#define BLEIF_CMDRPT_CMDRPT_Msk (0x1fUL) /*!< BLEIF CMDRPT: CMDRPT (Bitfield-Mask: 0x1f) */ +/* ======================================================= OFFSETHI ======================================================== */ +#define BLEIF_OFFSETHI_OFFSETHI_Pos (0UL) /*!< BLEIF OFFSETHI: OFFSETHI (Bit 0) */ +#define BLEIF_OFFSETHI_OFFSETHI_Msk (0xffffUL) /*!< BLEIF OFFSETHI: OFFSETHI (Bitfield-Mask: 0xffff) */ +/* ======================================================== CMDSTAT ======================================================== */ +#define BLEIF_CMDSTAT_CTSIZE_Pos (8UL) /*!< BLEIF CMDSTAT: CTSIZE (Bit 8) */ +#define BLEIF_CMDSTAT_CTSIZE_Msk (0xfff00UL) /*!< BLEIF CMDSTAT: CTSIZE (Bitfield-Mask: 0xfff) */ +#define BLEIF_CMDSTAT_CMDSTAT_Pos (5UL) /*!< BLEIF CMDSTAT: CMDSTAT (Bit 5) */ +#define BLEIF_CMDSTAT_CMDSTAT_Msk (0xe0UL) /*!< BLEIF CMDSTAT: CMDSTAT (Bitfield-Mask: 0x07) */ +#define BLEIF_CMDSTAT_CCMD_Pos (0UL) /*!< BLEIF CMDSTAT: CCMD (Bit 0) */ +#define BLEIF_CMDSTAT_CCMD_Msk (0x1fUL) /*!< BLEIF CMDSTAT: CCMD (Bitfield-Mask: 0x1f) */ +/* ========================================================= INTEN ========================================================= */ +#define BLEIF_INTEN_B2MSHUTDN_Pos (16UL) /*!< BLEIF INTEN: B2MSHUTDN (Bit 16) */ +#define BLEIF_INTEN_B2MSHUTDN_Msk (0x10000UL) /*!< BLEIF INTEN: B2MSHUTDN (Bitfield-Mask: 0x01) */ +#define BLEIF_INTEN_B2MACTIVE_Pos (15UL) /*!< BLEIF INTEN: B2MACTIVE (Bit 15) */ +#define BLEIF_INTEN_B2MACTIVE_Msk (0x8000UL) /*!< BLEIF INTEN: B2MACTIVE (Bitfield-Mask: 0x01) */ +#define BLEIF_INTEN_B2MSLEEP_Pos (14UL) /*!< BLEIF INTEN: B2MSLEEP (Bit 14) */ +#define BLEIF_INTEN_B2MSLEEP_Msk (0x4000UL) /*!< BLEIF INTEN: B2MSLEEP (Bitfield-Mask: 0x01) */ +#define BLEIF_INTEN_CQERR_Pos (13UL) /*!< BLEIF INTEN: CQERR (Bit 13) */ +#define BLEIF_INTEN_CQERR_Msk (0x2000UL) /*!< BLEIF INTEN: CQERR (Bitfield-Mask: 0x01) */ +#define BLEIF_INTEN_CQUPD_Pos (12UL) /*!< BLEIF INTEN: CQUPD (Bit 12) */ +#define BLEIF_INTEN_CQUPD_Msk (0x1000UL) /*!< BLEIF INTEN: CQUPD (Bitfield-Mask: 0x01) */ +#define BLEIF_INTEN_CQPAUSED_Pos (11UL) /*!< BLEIF INTEN: CQPAUSED (Bit 11) */ +#define BLEIF_INTEN_CQPAUSED_Msk (0x800UL) /*!< BLEIF INTEN: CQPAUSED (Bitfield-Mask: 0x01) */ +#define BLEIF_INTEN_DERR_Pos (10UL) /*!< BLEIF INTEN: DERR (Bit 10) */ +#define BLEIF_INTEN_DERR_Msk (0x400UL) /*!< BLEIF INTEN: DERR (Bitfield-Mask: 0x01) */ +#define BLEIF_INTEN_DCMP_Pos (9UL) /*!< BLEIF INTEN: DCMP (Bit 9) */ +#define BLEIF_INTEN_DCMP_Msk (0x200UL) /*!< BLEIF INTEN: DCMP (Bitfield-Mask: 0x01) */ +#define BLEIF_INTEN_BLECSSTAT_Pos (8UL) /*!< BLEIF INTEN: BLECSSTAT (Bit 8) */ +#define BLEIF_INTEN_BLECSSTAT_Msk (0x100UL) /*!< BLEIF INTEN: BLECSSTAT (Bitfield-Mask: 0x01) */ +#define BLEIF_INTEN_BLECIRQ_Pos (7UL) /*!< BLEIF INTEN: BLECIRQ (Bit 7) */ +#define BLEIF_INTEN_BLECIRQ_Msk (0x80UL) /*!< BLEIF INTEN: BLECIRQ (Bitfield-Mask: 0x01) */ +#define BLEIF_INTEN_ICMD_Pos (6UL) /*!< BLEIF INTEN: ICMD (Bit 6) */ +#define BLEIF_INTEN_ICMD_Msk (0x40UL) /*!< BLEIF INTEN: ICMD (Bitfield-Mask: 0x01) */ +#define BLEIF_INTEN_IACC_Pos (5UL) /*!< BLEIF INTEN: IACC (Bit 5) */ +#define BLEIF_INTEN_IACC_Msk (0x20UL) /*!< BLEIF INTEN: IACC (Bitfield-Mask: 0x01) */ +#define BLEIF_INTEN_B2MST_Pos (4UL) /*!< BLEIF INTEN: B2MST (Bit 4) */ +#define BLEIF_INTEN_B2MST_Msk (0x10UL) /*!< BLEIF INTEN: B2MST (Bitfield-Mask: 0x01) */ +#define BLEIF_INTEN_FOVFL_Pos (3UL) /*!< BLEIF INTEN: FOVFL (Bit 3) */ +#define BLEIF_INTEN_FOVFL_Msk (0x8UL) /*!< BLEIF INTEN: FOVFL (Bitfield-Mask: 0x01) */ +#define BLEIF_INTEN_FUNDFL_Pos (2UL) /*!< BLEIF INTEN: FUNDFL (Bit 2) */ +#define BLEIF_INTEN_FUNDFL_Msk (0x4UL) /*!< BLEIF INTEN: FUNDFL (Bitfield-Mask: 0x01) */ +#define BLEIF_INTEN_THR_Pos (1UL) /*!< BLEIF INTEN: THR (Bit 1) */ +#define BLEIF_INTEN_THR_Msk (0x2UL) /*!< BLEIF INTEN: THR (Bitfield-Mask: 0x01) */ +#define BLEIF_INTEN_CMDCMP_Pos (0UL) /*!< BLEIF INTEN: CMDCMP (Bit 0) */ +#define BLEIF_INTEN_CMDCMP_Msk (0x1UL) /*!< BLEIF INTEN: CMDCMP (Bitfield-Mask: 0x01) */ +/* ======================================================== INTSTAT ======================================================== */ +#define BLEIF_INTSTAT_B2MSHUTDN_Pos (16UL) /*!< BLEIF INTSTAT: B2MSHUTDN (Bit 16) */ +#define BLEIF_INTSTAT_B2MSHUTDN_Msk (0x10000UL) /*!< BLEIF INTSTAT: B2MSHUTDN (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSTAT_B2MACTIVE_Pos (15UL) /*!< BLEIF INTSTAT: B2MACTIVE (Bit 15) */ +#define BLEIF_INTSTAT_B2MACTIVE_Msk (0x8000UL) /*!< BLEIF INTSTAT: B2MACTIVE (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSTAT_B2MSLEEP_Pos (14UL) /*!< BLEIF INTSTAT: B2MSLEEP (Bit 14) */ +#define BLEIF_INTSTAT_B2MSLEEP_Msk (0x4000UL) /*!< BLEIF INTSTAT: B2MSLEEP (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSTAT_CQERR_Pos (13UL) /*!< BLEIF INTSTAT: CQERR (Bit 13) */ +#define BLEIF_INTSTAT_CQERR_Msk (0x2000UL) /*!< BLEIF INTSTAT: CQERR (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSTAT_CQUPD_Pos (12UL) /*!< BLEIF INTSTAT: CQUPD (Bit 12) */ +#define BLEIF_INTSTAT_CQUPD_Msk (0x1000UL) /*!< BLEIF INTSTAT: CQUPD (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSTAT_CQPAUSED_Pos (11UL) /*!< BLEIF INTSTAT: CQPAUSED (Bit 11) */ +#define BLEIF_INTSTAT_CQPAUSED_Msk (0x800UL) /*!< BLEIF INTSTAT: CQPAUSED (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSTAT_DERR_Pos (10UL) /*!< BLEIF INTSTAT: DERR (Bit 10) */ +#define BLEIF_INTSTAT_DERR_Msk (0x400UL) /*!< BLEIF INTSTAT: DERR (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSTAT_DCMP_Pos (9UL) /*!< BLEIF INTSTAT: DCMP (Bit 9) */ +#define BLEIF_INTSTAT_DCMP_Msk (0x200UL) /*!< BLEIF INTSTAT: DCMP (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSTAT_BLECSSTAT_Pos (8UL) /*!< BLEIF INTSTAT: BLECSSTAT (Bit 8) */ +#define BLEIF_INTSTAT_BLECSSTAT_Msk (0x100UL) /*!< BLEIF INTSTAT: BLECSSTAT (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSTAT_BLECIRQ_Pos (7UL) /*!< BLEIF INTSTAT: BLECIRQ (Bit 7) */ +#define BLEIF_INTSTAT_BLECIRQ_Msk (0x80UL) /*!< BLEIF INTSTAT: BLECIRQ (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSTAT_ICMD_Pos (6UL) /*!< BLEIF INTSTAT: ICMD (Bit 6) */ +#define BLEIF_INTSTAT_ICMD_Msk (0x40UL) /*!< BLEIF INTSTAT: ICMD (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSTAT_IACC_Pos (5UL) /*!< BLEIF INTSTAT: IACC (Bit 5) */ +#define BLEIF_INTSTAT_IACC_Msk (0x20UL) /*!< BLEIF INTSTAT: IACC (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSTAT_B2MST_Pos (4UL) /*!< BLEIF INTSTAT: B2MST (Bit 4) */ +#define BLEIF_INTSTAT_B2MST_Msk (0x10UL) /*!< BLEIF INTSTAT: B2MST (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSTAT_FOVFL_Pos (3UL) /*!< BLEIF INTSTAT: FOVFL (Bit 3) */ +#define BLEIF_INTSTAT_FOVFL_Msk (0x8UL) /*!< BLEIF INTSTAT: FOVFL (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSTAT_FUNDFL_Pos (2UL) /*!< BLEIF INTSTAT: FUNDFL (Bit 2) */ +#define BLEIF_INTSTAT_FUNDFL_Msk (0x4UL) /*!< BLEIF INTSTAT: FUNDFL (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSTAT_THR_Pos (1UL) /*!< BLEIF INTSTAT: THR (Bit 1) */ +#define BLEIF_INTSTAT_THR_Msk (0x2UL) /*!< BLEIF INTSTAT: THR (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSTAT_CMDCMP_Pos (0UL) /*!< BLEIF INTSTAT: CMDCMP (Bit 0) */ +#define BLEIF_INTSTAT_CMDCMP_Msk (0x1UL) /*!< BLEIF INTSTAT: CMDCMP (Bitfield-Mask: 0x01) */ +/* ======================================================== INTCLR ========================================================= */ +#define BLEIF_INTCLR_B2MSHUTDN_Pos (16UL) /*!< BLEIF INTCLR: B2MSHUTDN (Bit 16) */ +#define BLEIF_INTCLR_B2MSHUTDN_Msk (0x10000UL) /*!< BLEIF INTCLR: B2MSHUTDN (Bitfield-Mask: 0x01) */ +#define BLEIF_INTCLR_B2MACTIVE_Pos (15UL) /*!< BLEIF INTCLR: B2MACTIVE (Bit 15) */ +#define BLEIF_INTCLR_B2MACTIVE_Msk (0x8000UL) /*!< BLEIF INTCLR: B2MACTIVE (Bitfield-Mask: 0x01) */ +#define BLEIF_INTCLR_B2MSLEEP_Pos (14UL) /*!< BLEIF INTCLR: B2MSLEEP (Bit 14) */ +#define BLEIF_INTCLR_B2MSLEEP_Msk (0x4000UL) /*!< BLEIF INTCLR: B2MSLEEP (Bitfield-Mask: 0x01) */ +#define BLEIF_INTCLR_CQERR_Pos (13UL) /*!< BLEIF INTCLR: CQERR (Bit 13) */ +#define BLEIF_INTCLR_CQERR_Msk (0x2000UL) /*!< BLEIF INTCLR: CQERR (Bitfield-Mask: 0x01) */ +#define BLEIF_INTCLR_CQUPD_Pos (12UL) /*!< BLEIF INTCLR: CQUPD (Bit 12) */ +#define BLEIF_INTCLR_CQUPD_Msk (0x1000UL) /*!< BLEIF INTCLR: CQUPD (Bitfield-Mask: 0x01) */ +#define BLEIF_INTCLR_CQPAUSED_Pos (11UL) /*!< BLEIF INTCLR: CQPAUSED (Bit 11) */ +#define BLEIF_INTCLR_CQPAUSED_Msk (0x800UL) /*!< BLEIF INTCLR: CQPAUSED (Bitfield-Mask: 0x01) */ +#define BLEIF_INTCLR_DERR_Pos (10UL) /*!< BLEIF INTCLR: DERR (Bit 10) */ +#define BLEIF_INTCLR_DERR_Msk (0x400UL) /*!< BLEIF INTCLR: DERR (Bitfield-Mask: 0x01) */ +#define BLEIF_INTCLR_DCMP_Pos (9UL) /*!< BLEIF INTCLR: DCMP (Bit 9) */ +#define BLEIF_INTCLR_DCMP_Msk (0x200UL) /*!< BLEIF INTCLR: DCMP (Bitfield-Mask: 0x01) */ +#define BLEIF_INTCLR_BLECSSTAT_Pos (8UL) /*!< BLEIF INTCLR: BLECSSTAT (Bit 8) */ +#define BLEIF_INTCLR_BLECSSTAT_Msk (0x100UL) /*!< BLEIF INTCLR: BLECSSTAT (Bitfield-Mask: 0x01) */ +#define BLEIF_INTCLR_BLECIRQ_Pos (7UL) /*!< BLEIF INTCLR: BLECIRQ (Bit 7) */ +#define BLEIF_INTCLR_BLECIRQ_Msk (0x80UL) /*!< BLEIF INTCLR: BLECIRQ (Bitfield-Mask: 0x01) */ +#define BLEIF_INTCLR_ICMD_Pos (6UL) /*!< BLEIF INTCLR: ICMD (Bit 6) */ +#define BLEIF_INTCLR_ICMD_Msk (0x40UL) /*!< BLEIF INTCLR: ICMD (Bitfield-Mask: 0x01) */ +#define BLEIF_INTCLR_IACC_Pos (5UL) /*!< BLEIF INTCLR: IACC (Bit 5) */ +#define BLEIF_INTCLR_IACC_Msk (0x20UL) /*!< BLEIF INTCLR: IACC (Bitfield-Mask: 0x01) */ +#define BLEIF_INTCLR_B2MST_Pos (4UL) /*!< BLEIF INTCLR: B2MST (Bit 4) */ +#define BLEIF_INTCLR_B2MST_Msk (0x10UL) /*!< BLEIF INTCLR: B2MST (Bitfield-Mask: 0x01) */ +#define BLEIF_INTCLR_FOVFL_Pos (3UL) /*!< BLEIF INTCLR: FOVFL (Bit 3) */ +#define BLEIF_INTCLR_FOVFL_Msk (0x8UL) /*!< BLEIF INTCLR: FOVFL (Bitfield-Mask: 0x01) */ +#define BLEIF_INTCLR_FUNDFL_Pos (2UL) /*!< BLEIF INTCLR: FUNDFL (Bit 2) */ +#define BLEIF_INTCLR_FUNDFL_Msk (0x4UL) /*!< BLEIF INTCLR: FUNDFL (Bitfield-Mask: 0x01) */ +#define BLEIF_INTCLR_THR_Pos (1UL) /*!< BLEIF INTCLR: THR (Bit 1) */ +#define BLEIF_INTCLR_THR_Msk (0x2UL) /*!< BLEIF INTCLR: THR (Bitfield-Mask: 0x01) */ +#define BLEIF_INTCLR_CMDCMP_Pos (0UL) /*!< BLEIF INTCLR: CMDCMP (Bit 0) */ +#define BLEIF_INTCLR_CMDCMP_Msk (0x1UL) /*!< BLEIF INTCLR: CMDCMP (Bitfield-Mask: 0x01) */ +/* ======================================================== INTSET ========================================================= */ +#define BLEIF_INTSET_B2MSHUTDN_Pos (16UL) /*!< BLEIF INTSET: B2MSHUTDN (Bit 16) */ +#define BLEIF_INTSET_B2MSHUTDN_Msk (0x10000UL) /*!< BLEIF INTSET: B2MSHUTDN (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSET_B2MACTIVE_Pos (15UL) /*!< BLEIF INTSET: B2MACTIVE (Bit 15) */ +#define BLEIF_INTSET_B2MACTIVE_Msk (0x8000UL) /*!< BLEIF INTSET: B2MACTIVE (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSET_B2MSLEEP_Pos (14UL) /*!< BLEIF INTSET: B2MSLEEP (Bit 14) */ +#define BLEIF_INTSET_B2MSLEEP_Msk (0x4000UL) /*!< BLEIF INTSET: B2MSLEEP (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSET_CQERR_Pos (13UL) /*!< BLEIF INTSET: CQERR (Bit 13) */ +#define BLEIF_INTSET_CQERR_Msk (0x2000UL) /*!< BLEIF INTSET: CQERR (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSET_CQUPD_Pos (12UL) /*!< BLEIF INTSET: CQUPD (Bit 12) */ +#define BLEIF_INTSET_CQUPD_Msk (0x1000UL) /*!< BLEIF INTSET: CQUPD (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSET_CQPAUSED_Pos (11UL) /*!< BLEIF INTSET: CQPAUSED (Bit 11) */ +#define BLEIF_INTSET_CQPAUSED_Msk (0x800UL) /*!< BLEIF INTSET: CQPAUSED (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSET_DERR_Pos (10UL) /*!< BLEIF INTSET: DERR (Bit 10) */ +#define BLEIF_INTSET_DERR_Msk (0x400UL) /*!< BLEIF INTSET: DERR (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSET_DCMP_Pos (9UL) /*!< BLEIF INTSET: DCMP (Bit 9) */ +#define BLEIF_INTSET_DCMP_Msk (0x200UL) /*!< BLEIF INTSET: DCMP (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSET_BLECSSTAT_Pos (8UL) /*!< BLEIF INTSET: BLECSSTAT (Bit 8) */ +#define BLEIF_INTSET_BLECSSTAT_Msk (0x100UL) /*!< BLEIF INTSET: BLECSSTAT (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSET_BLECIRQ_Pos (7UL) /*!< BLEIF INTSET: BLECIRQ (Bit 7) */ +#define BLEIF_INTSET_BLECIRQ_Msk (0x80UL) /*!< BLEIF INTSET: BLECIRQ (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSET_ICMD_Pos (6UL) /*!< BLEIF INTSET: ICMD (Bit 6) */ +#define BLEIF_INTSET_ICMD_Msk (0x40UL) /*!< BLEIF INTSET: ICMD (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSET_IACC_Pos (5UL) /*!< BLEIF INTSET: IACC (Bit 5) */ +#define BLEIF_INTSET_IACC_Msk (0x20UL) /*!< BLEIF INTSET: IACC (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSET_B2MST_Pos (4UL) /*!< BLEIF INTSET: B2MST (Bit 4) */ +#define BLEIF_INTSET_B2MST_Msk (0x10UL) /*!< BLEIF INTSET: B2MST (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSET_FOVFL_Pos (3UL) /*!< BLEIF INTSET: FOVFL (Bit 3) */ +#define BLEIF_INTSET_FOVFL_Msk (0x8UL) /*!< BLEIF INTSET: FOVFL (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSET_FUNDFL_Pos (2UL) /*!< BLEIF INTSET: FUNDFL (Bit 2) */ +#define BLEIF_INTSET_FUNDFL_Msk (0x4UL) /*!< BLEIF INTSET: FUNDFL (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSET_THR_Pos (1UL) /*!< BLEIF INTSET: THR (Bit 1) */ +#define BLEIF_INTSET_THR_Msk (0x2UL) /*!< BLEIF INTSET: THR (Bitfield-Mask: 0x01) */ +#define BLEIF_INTSET_CMDCMP_Pos (0UL) /*!< BLEIF INTSET: CMDCMP (Bit 0) */ +#define BLEIF_INTSET_CMDCMP_Msk (0x1UL) /*!< BLEIF INTSET: CMDCMP (Bitfield-Mask: 0x01) */ +/* ======================================================= DMATRIGEN ======================================================= */ +#define BLEIF_DMATRIGEN_DTHREN_Pos (1UL) /*!< BLEIF DMATRIGEN: DTHREN (Bit 1) */ +#define BLEIF_DMATRIGEN_DTHREN_Msk (0x2UL) /*!< BLEIF DMATRIGEN: DTHREN (Bitfield-Mask: 0x01) */ +#define BLEIF_DMATRIGEN_DCMDCMPEN_Pos (0UL) /*!< BLEIF DMATRIGEN: DCMDCMPEN (Bit 0) */ +#define BLEIF_DMATRIGEN_DCMDCMPEN_Msk (0x1UL) /*!< BLEIF DMATRIGEN: DCMDCMPEN (Bitfield-Mask: 0x01) */ +/* ====================================================== DMATRIGSTAT ====================================================== */ +#define BLEIF_DMATRIGSTAT_DTOTCMP_Pos (2UL) /*!< BLEIF DMATRIGSTAT: DTOTCMP (Bit 2) */ +#define BLEIF_DMATRIGSTAT_DTOTCMP_Msk (0x4UL) /*!< BLEIF DMATRIGSTAT: DTOTCMP (Bitfield-Mask: 0x01) */ +#define BLEIF_DMATRIGSTAT_DTHR_Pos (1UL) /*!< BLEIF DMATRIGSTAT: DTHR (Bit 1) */ +#define BLEIF_DMATRIGSTAT_DTHR_Msk (0x2UL) /*!< BLEIF DMATRIGSTAT: DTHR (Bitfield-Mask: 0x01) */ +#define BLEIF_DMATRIGSTAT_DCMDCMP_Pos (0UL) /*!< BLEIF DMATRIGSTAT: DCMDCMP (Bit 0) */ +#define BLEIF_DMATRIGSTAT_DCMDCMP_Msk (0x1UL) /*!< BLEIF DMATRIGSTAT: DCMDCMP (Bitfield-Mask: 0x01) */ +/* ======================================================== DMACFG ========================================================= */ +#define BLEIF_DMACFG_DPWROFF_Pos (9UL) /*!< BLEIF DMACFG: DPWROFF (Bit 9) */ +#define BLEIF_DMACFG_DPWROFF_Msk (0x200UL) /*!< BLEIF DMACFG: DPWROFF (Bitfield-Mask: 0x01) */ +#define BLEIF_DMACFG_DMAPRI_Pos (8UL) /*!< BLEIF DMACFG: DMAPRI (Bit 8) */ +#define BLEIF_DMACFG_DMAPRI_Msk (0x100UL) /*!< BLEIF DMACFG: DMAPRI (Bitfield-Mask: 0x01) */ +#define BLEIF_DMACFG_DMADIR_Pos (1UL) /*!< BLEIF DMACFG: DMADIR (Bit 1) */ +#define BLEIF_DMACFG_DMADIR_Msk (0x2UL) /*!< BLEIF DMACFG: DMADIR (Bitfield-Mask: 0x01) */ +#define BLEIF_DMACFG_DMAEN_Pos (0UL) /*!< BLEIF DMACFG: DMAEN (Bit 0) */ +#define BLEIF_DMACFG_DMAEN_Msk (0x1UL) /*!< BLEIF DMACFG: DMAEN (Bitfield-Mask: 0x01) */ +/* ====================================================== DMATOTCOUNT ====================================================== */ +#define BLEIF_DMATOTCOUNT_TOTCOUNT_Pos (0UL) /*!< BLEIF DMATOTCOUNT: TOTCOUNT (Bit 0) */ +#define BLEIF_DMATOTCOUNT_TOTCOUNT_Msk (0xfffUL) /*!< BLEIF DMATOTCOUNT: TOTCOUNT (Bitfield-Mask: 0xfff) */ +/* ====================================================== DMATARGADDR ====================================================== */ +#define BLEIF_DMATARGADDR_TARGADDR28_Pos (28UL) /*!< BLEIF DMATARGADDR: TARGADDR28 (Bit 28) */ +#define BLEIF_DMATARGADDR_TARGADDR28_Msk (0x10000000UL) /*!< BLEIF DMATARGADDR: TARGADDR28 (Bitfield-Mask: 0x01) */ +#define BLEIF_DMATARGADDR_TARGADDR_Pos (0UL) /*!< BLEIF DMATARGADDR: TARGADDR (Bit 0) */ +#define BLEIF_DMATARGADDR_TARGADDR_Msk (0xfffffUL) /*!< BLEIF DMATARGADDR: TARGADDR (Bitfield-Mask: 0xfffff) */ +/* ======================================================== DMASTAT ======================================================== */ +#define BLEIF_DMASTAT_DMAERR_Pos (2UL) /*!< BLEIF DMASTAT: DMAERR (Bit 2) */ +#define BLEIF_DMASTAT_DMAERR_Msk (0x4UL) /*!< BLEIF DMASTAT: DMAERR (Bitfield-Mask: 0x01) */ +#define BLEIF_DMASTAT_DMACPL_Pos (1UL) /*!< BLEIF DMASTAT: DMACPL (Bit 1) */ +#define BLEIF_DMASTAT_DMACPL_Msk (0x2UL) /*!< BLEIF DMASTAT: DMACPL (Bitfield-Mask: 0x01) */ +#define BLEIF_DMASTAT_DMATIP_Pos (0UL) /*!< BLEIF DMASTAT: DMATIP (Bit 0) */ +#define BLEIF_DMASTAT_DMATIP_Msk (0x1UL) /*!< BLEIF DMASTAT: DMATIP (Bitfield-Mask: 0x01) */ +/* ========================================================= CQCFG ========================================================= */ +#define BLEIF_CQCFG_CQPRI_Pos (1UL) /*!< BLEIF CQCFG: CQPRI (Bit 1) */ +#define BLEIF_CQCFG_CQPRI_Msk (0x2UL) /*!< BLEIF CQCFG: CQPRI (Bitfield-Mask: 0x01) */ +#define BLEIF_CQCFG_CQEN_Pos (0UL) /*!< BLEIF CQCFG: CQEN (Bit 0) */ +#define BLEIF_CQCFG_CQEN_Msk (0x1UL) /*!< BLEIF CQCFG: CQEN (Bitfield-Mask: 0x01) */ +/* ======================================================== CQADDR ========================================================= */ +#define BLEIF_CQADDR_CQADDR28_Pos (28UL) /*!< BLEIF CQADDR: CQADDR28 (Bit 28) */ +#define BLEIF_CQADDR_CQADDR28_Msk (0x10000000UL) /*!< BLEIF CQADDR: CQADDR28 (Bitfield-Mask: 0x01) */ +#define BLEIF_CQADDR_CQADDR_Pos (2UL) /*!< BLEIF CQADDR: CQADDR (Bit 2) */ +#define BLEIF_CQADDR_CQADDR_Msk (0xffffcUL) /*!< BLEIF CQADDR: CQADDR (Bitfield-Mask: 0x3ffff) */ +/* ======================================================== CQSTAT ========================================================= */ +#define BLEIF_CQSTAT_CQERR_Pos (2UL) /*!< BLEIF CQSTAT: CQERR (Bit 2) */ +#define BLEIF_CQSTAT_CQERR_Msk (0x4UL) /*!< BLEIF CQSTAT: CQERR (Bitfield-Mask: 0x01) */ +#define BLEIF_CQSTAT_CQPAUSED_Pos (1UL) /*!< BLEIF CQSTAT: CQPAUSED (Bit 1) */ +#define BLEIF_CQSTAT_CQPAUSED_Msk (0x2UL) /*!< BLEIF CQSTAT: CQPAUSED (Bitfield-Mask: 0x01) */ +#define BLEIF_CQSTAT_CQTIP_Pos (0UL) /*!< BLEIF CQSTAT: CQTIP (Bit 0) */ +#define BLEIF_CQSTAT_CQTIP_Msk (0x1UL) /*!< BLEIF CQSTAT: CQTIP (Bitfield-Mask: 0x01) */ +/* ======================================================== CQFLAGS ======================================================== */ +#define BLEIF_CQFLAGS_CQIRQMASK_Pos (16UL) /*!< BLEIF CQFLAGS: CQIRQMASK (Bit 16) */ +#define BLEIF_CQFLAGS_CQIRQMASK_Msk (0xffff0000UL) /*!< BLEIF CQFLAGS: CQIRQMASK (Bitfield-Mask: 0xffff) */ +#define BLEIF_CQFLAGS_CQFLAGS_Pos (0UL) /*!< BLEIF CQFLAGS: CQFLAGS (Bit 0) */ +#define BLEIF_CQFLAGS_CQFLAGS_Msk (0xffffUL) /*!< BLEIF CQFLAGS: CQFLAGS (Bitfield-Mask: 0xffff) */ +/* ====================================================== CQSETCLEAR ======================================================= */ +#define BLEIF_CQSETCLEAR_CQFCLR_Pos (16UL) /*!< BLEIF CQSETCLEAR: CQFCLR (Bit 16) */ +#define BLEIF_CQSETCLEAR_CQFCLR_Msk (0xff0000UL) /*!< BLEIF CQSETCLEAR: CQFCLR (Bitfield-Mask: 0xff) */ +#define BLEIF_CQSETCLEAR_CQFTGL_Pos (8UL) /*!< BLEIF CQSETCLEAR: CQFTGL (Bit 8) */ +#define BLEIF_CQSETCLEAR_CQFTGL_Msk (0xff00UL) /*!< BLEIF CQSETCLEAR: CQFTGL (Bitfield-Mask: 0xff) */ +#define BLEIF_CQSETCLEAR_CQFSET_Pos (0UL) /*!< BLEIF CQSETCLEAR: CQFSET (Bit 0) */ +#define BLEIF_CQSETCLEAR_CQFSET_Msk (0xffUL) /*!< BLEIF CQSETCLEAR: CQFSET (Bitfield-Mask: 0xff) */ +/* ======================================================= CQPAUSEEN ======================================================= */ +#define BLEIF_CQPAUSEEN_CQPEN_Pos (0UL) /*!< BLEIF CQPAUSEEN: CQPEN (Bit 0) */ +#define BLEIF_CQPAUSEEN_CQPEN_Msk (0xffffUL) /*!< BLEIF CQPAUSEEN: CQPEN (Bitfield-Mask: 0xffff) */ +/* ======================================================= CQCURIDX ======================================================== */ +#define BLEIF_CQCURIDX_CQCURIDX_Pos (0UL) /*!< BLEIF CQCURIDX: CQCURIDX (Bit 0) */ +#define BLEIF_CQCURIDX_CQCURIDX_Msk (0xffUL) /*!< BLEIF CQCURIDX: CQCURIDX (Bitfield-Mask: 0xff) */ +/* ======================================================= CQENDIDX ======================================================== */ +#define BLEIF_CQENDIDX_CQENDIDX_Pos (0UL) /*!< BLEIF CQENDIDX: CQENDIDX (Bit 0) */ +#define BLEIF_CQENDIDX_CQENDIDX_Msk (0xffUL) /*!< BLEIF CQENDIDX: CQENDIDX (Bitfield-Mask: 0xff) */ +/* ======================================================== STATUS ========================================================= */ +#define BLEIF_STATUS_IDLEST_Pos (2UL) /*!< BLEIF STATUS: IDLEST (Bit 2) */ +#define BLEIF_STATUS_IDLEST_Msk (0x4UL) /*!< BLEIF STATUS: IDLEST (Bitfield-Mask: 0x01) */ +#define BLEIF_STATUS_CMDACT_Pos (1UL) /*!< BLEIF STATUS: CMDACT (Bit 1) */ +#define BLEIF_STATUS_CMDACT_Msk (0x2UL) /*!< BLEIF STATUS: CMDACT (Bitfield-Mask: 0x01) */ +#define BLEIF_STATUS_ERR_Pos (0UL) /*!< BLEIF STATUS: ERR (Bit 0) */ +#define BLEIF_STATUS_ERR_Msk (0x1UL) /*!< BLEIF STATUS: ERR (Bitfield-Mask: 0x01) */ +/* ======================================================== MSPICFG ======================================================== */ +#define BLEIF_MSPICFG_MSPIRST_Pos (30UL) /*!< BLEIF MSPICFG: MSPIRST (Bit 30) */ +#define BLEIF_MSPICFG_MSPIRST_Msk (0x40000000UL) /*!< BLEIF MSPICFG: MSPIRST (Bitfield-Mask: 0x01) */ +#define BLEIF_MSPICFG_DOUTDLY_Pos (27UL) /*!< BLEIF MSPICFG: DOUTDLY (Bit 27) */ +#define BLEIF_MSPICFG_DOUTDLY_Msk (0x38000000UL) /*!< BLEIF MSPICFG: DOUTDLY (Bitfield-Mask: 0x07) */ +#define BLEIF_MSPICFG_DINDLY_Pos (24UL) /*!< BLEIF MSPICFG: DINDLY (Bit 24) */ +#define BLEIF_MSPICFG_DINDLY_Msk (0x7000000UL) /*!< BLEIF MSPICFG: DINDLY (Bitfield-Mask: 0x07) */ +#define BLEIF_MSPICFG_SPILSB_Pos (23UL) /*!< BLEIF MSPICFG: SPILSB (Bit 23) */ +#define BLEIF_MSPICFG_SPILSB_Msk (0x800000UL) /*!< BLEIF MSPICFG: SPILSB (Bitfield-Mask: 0x01) */ +#define BLEIF_MSPICFG_RDFCPOL_Pos (22UL) /*!< BLEIF MSPICFG: RDFCPOL (Bit 22) */ +#define BLEIF_MSPICFG_RDFCPOL_Msk (0x400000UL) /*!< BLEIF MSPICFG: RDFCPOL (Bitfield-Mask: 0x01) */ +#define BLEIF_MSPICFG_WTFCPOL_Pos (21UL) /*!< BLEIF MSPICFG: WTFCPOL (Bit 21) */ +#define BLEIF_MSPICFG_WTFCPOL_Msk (0x200000UL) /*!< BLEIF MSPICFG: WTFCPOL (Bitfield-Mask: 0x01) */ +#define BLEIF_MSPICFG_RDFC_Pos (17UL) /*!< BLEIF MSPICFG: RDFC (Bit 17) */ +#define BLEIF_MSPICFG_RDFC_Msk (0x20000UL) /*!< BLEIF MSPICFG: RDFC (Bitfield-Mask: 0x01) */ +#define BLEIF_MSPICFG_WTFC_Pos (16UL) /*!< BLEIF MSPICFG: WTFC (Bit 16) */ +#define BLEIF_MSPICFG_WTFC_Msk (0x10000UL) /*!< BLEIF MSPICFG: WTFC (Bitfield-Mask: 0x01) */ +#define BLEIF_MSPICFG_FULLDUP_Pos (2UL) /*!< BLEIF MSPICFG: FULLDUP (Bit 2) */ +#define BLEIF_MSPICFG_FULLDUP_Msk (0x4UL) /*!< BLEIF MSPICFG: FULLDUP (Bitfield-Mask: 0x01) */ +#define BLEIF_MSPICFG_SPHA_Pos (1UL) /*!< BLEIF MSPICFG: SPHA (Bit 1) */ +#define BLEIF_MSPICFG_SPHA_Msk (0x2UL) /*!< BLEIF MSPICFG: SPHA (Bitfield-Mask: 0x01) */ +#define BLEIF_MSPICFG_SPOL_Pos (0UL) /*!< BLEIF MSPICFG: SPOL (Bit 0) */ +#define BLEIF_MSPICFG_SPOL_Msk (0x1UL) /*!< BLEIF MSPICFG: SPOL (Bitfield-Mask: 0x01) */ +/* ======================================================== BLECFG ========================================================= */ +#define BLEIF_BLECFG_SPIISOCTL_Pos (14UL) /*!< BLEIF BLECFG: SPIISOCTL (Bit 14) */ +#define BLEIF_BLECFG_SPIISOCTL_Msk (0xc000UL) /*!< BLEIF BLECFG: SPIISOCTL (Bitfield-Mask: 0x03) */ +#define BLEIF_BLECFG_PWRISOCTL_Pos (12UL) /*!< BLEIF BLECFG: PWRISOCTL (Bit 12) */ +#define BLEIF_BLECFG_PWRISOCTL_Msk (0x3000UL) /*!< BLEIF BLECFG: PWRISOCTL (Bitfield-Mask: 0x03) */ +#define BLEIF_BLECFG_STAYASLEEP_Pos (11UL) /*!< BLEIF BLECFG: STAYASLEEP (Bit 11) */ +#define BLEIF_BLECFG_STAYASLEEP_Msk (0x800UL) /*!< BLEIF BLECFG: STAYASLEEP (Bitfield-Mask: 0x01) */ +#define BLEIF_BLECFG_FRCCLK_Pos (10UL) /*!< BLEIF BLECFG: FRCCLK (Bit 10) */ +#define BLEIF_BLECFG_FRCCLK_Msk (0x400UL) /*!< BLEIF BLECFG: FRCCLK (Bitfield-Mask: 0x01) */ +#define BLEIF_BLECFG_MCUFRCSLP_Pos (9UL) /*!< BLEIF BLECFG: MCUFRCSLP (Bit 9) */ +#define BLEIF_BLECFG_MCUFRCSLP_Msk (0x200UL) /*!< BLEIF BLECFG: MCUFRCSLP (Bitfield-Mask: 0x01) */ +#define BLEIF_BLECFG_WT4ACTOFF_Pos (8UL) /*!< BLEIF BLECFG: WT4ACTOFF (Bit 8) */ +#define BLEIF_BLECFG_WT4ACTOFF_Msk (0x100UL) /*!< BLEIF BLECFG: WT4ACTOFF (Bitfield-Mask: 0x01) */ +#define BLEIF_BLECFG_BLEHREQCTL_Pos (6UL) /*!< BLEIF BLECFG: BLEHREQCTL (Bit 6) */ +#define BLEIF_BLECFG_BLEHREQCTL_Msk (0xc0UL) /*!< BLEIF BLECFG: BLEHREQCTL (Bitfield-Mask: 0x03) */ +#define BLEIF_BLECFG_DCDCFLGCTL_Pos (4UL) /*!< BLEIF BLECFG: DCDCFLGCTL (Bit 4) */ +#define BLEIF_BLECFG_DCDCFLGCTL_Msk (0x30UL) /*!< BLEIF BLECFG: DCDCFLGCTL (Bitfield-Mask: 0x03) */ +#define BLEIF_BLECFG_WAKEUPCTL_Pos (2UL) /*!< BLEIF BLECFG: WAKEUPCTL (Bit 2) */ +#define BLEIF_BLECFG_WAKEUPCTL_Msk (0xcUL) /*!< BLEIF BLECFG: WAKEUPCTL (Bitfield-Mask: 0x03) */ +#define BLEIF_BLECFG_BLERSTN_Pos (1UL) /*!< BLEIF BLECFG: BLERSTN (Bit 1) */ +#define BLEIF_BLECFG_BLERSTN_Msk (0x2UL) /*!< BLEIF BLECFG: BLERSTN (Bitfield-Mask: 0x01) */ +#define BLEIF_BLECFG_PWRSMEN_Pos (0UL) /*!< BLEIF BLECFG: PWRSMEN (Bit 0) */ +#define BLEIF_BLECFG_PWRSMEN_Msk (0x1UL) /*!< BLEIF BLECFG: PWRSMEN (Bitfield-Mask: 0x01) */ +/* ======================================================== PWRCMD ========================================================= */ +#define BLEIF_PWRCMD_RESTART_Pos (1UL) /*!< BLEIF PWRCMD: RESTART (Bit 1) */ +#define BLEIF_PWRCMD_RESTART_Msk (0x2UL) /*!< BLEIF PWRCMD: RESTART (Bitfield-Mask: 0x01) */ +#define BLEIF_PWRCMD_WAKEREQ_Pos (0UL) /*!< BLEIF PWRCMD: WAKEREQ (Bit 0) */ +#define BLEIF_PWRCMD_WAKEREQ_Msk (0x1UL) /*!< BLEIF PWRCMD: WAKEREQ (Bitfield-Mask: 0x01) */ +/* ======================================================== BSTATUS ======================================================== */ +#define BLEIF_BSTATUS_BLEHREQ_Pos (12UL) /*!< BLEIF BSTATUS: BLEHREQ (Bit 12) */ +#define BLEIF_BSTATUS_BLEHREQ_Msk (0x1000UL) /*!< BLEIF BSTATUS: BLEHREQ (Bitfield-Mask: 0x01) */ +#define BLEIF_BSTATUS_BLEHACK_Pos (11UL) /*!< BLEIF BSTATUS: BLEHACK (Bit 11) */ +#define BLEIF_BSTATUS_BLEHACK_Msk (0x800UL) /*!< BLEIF BSTATUS: BLEHACK (Bitfield-Mask: 0x01) */ +#define BLEIF_BSTATUS_PWRST_Pos (8UL) /*!< BLEIF BSTATUS: PWRST (Bit 8) */ +#define BLEIF_BSTATUS_PWRST_Msk (0x700UL) /*!< BLEIF BSTATUS: PWRST (Bitfield-Mask: 0x07) */ +#define BLEIF_BSTATUS_BLEIRQ_Pos (7UL) /*!< BLEIF BSTATUS: BLEIRQ (Bit 7) */ +#define BLEIF_BSTATUS_BLEIRQ_Msk (0x80UL) /*!< BLEIF BSTATUS: BLEIRQ (Bitfield-Mask: 0x01) */ +#define BLEIF_BSTATUS_WAKEUP_Pos (6UL) /*!< BLEIF BSTATUS: WAKEUP (Bit 6) */ +#define BLEIF_BSTATUS_WAKEUP_Msk (0x40UL) /*!< BLEIF BSTATUS: WAKEUP (Bitfield-Mask: 0x01) */ +#define BLEIF_BSTATUS_DCDCFLAG_Pos (5UL) /*!< BLEIF BSTATUS: DCDCFLAG (Bit 5) */ +#define BLEIF_BSTATUS_DCDCFLAG_Msk (0x20UL) /*!< BLEIF BSTATUS: DCDCFLAG (Bitfield-Mask: 0x01) */ +#define BLEIF_BSTATUS_DCDCREQ_Pos (4UL) /*!< BLEIF BSTATUS: DCDCREQ (Bit 4) */ +#define BLEIF_BSTATUS_DCDCREQ_Msk (0x10UL) /*!< BLEIF BSTATUS: DCDCREQ (Bitfield-Mask: 0x01) */ +#define BLEIF_BSTATUS_SPISTATUS_Pos (3UL) /*!< BLEIF BSTATUS: SPISTATUS (Bit 3) */ +#define BLEIF_BSTATUS_SPISTATUS_Msk (0x8UL) /*!< BLEIF BSTATUS: SPISTATUS (Bitfield-Mask: 0x01) */ +#define BLEIF_BSTATUS_B2MSTATE_Pos (0UL) /*!< BLEIF BSTATUS: B2MSTATE (Bit 0) */ +#define BLEIF_BSTATUS_B2MSTATE_Msk (0x7UL) /*!< BLEIF BSTATUS: B2MSTATE (Bitfield-Mask: 0x07) */ +/* ======================================================== BLEDBG ========================================================= */ +#define BLEIF_BLEDBG_DBGDATA_Pos (3UL) /*!< BLEIF BLEDBG: DBGDATA (Bit 3) */ +#define BLEIF_BLEDBG_DBGDATA_Msk (0xfffffff8UL) /*!< BLEIF BLEDBG: DBGDATA (Bitfield-Mask: 0x1fffffff) */ +#define BLEIF_BLEDBG_APBCLKON_Pos (2UL) /*!< BLEIF BLEDBG: APBCLKON (Bit 2) */ +#define BLEIF_BLEDBG_APBCLKON_Msk (0x4UL) /*!< BLEIF BLEDBG: APBCLKON (Bitfield-Mask: 0x01) */ +#define BLEIF_BLEDBG_IOCLKON_Pos (1UL) /*!< BLEIF BLEDBG: IOCLKON (Bit 1) */ +#define BLEIF_BLEDBG_IOCLKON_Msk (0x2UL) /*!< BLEIF BLEDBG: IOCLKON (Bitfield-Mask: 0x01) */ +#define BLEIF_BLEDBG_DBGEN_Pos (0UL) /*!< BLEIF BLEDBG: DBGEN (Bit 0) */ +#define BLEIF_BLEDBG_DBGEN_Msk (0x1UL) /*!< BLEIF BLEDBG: DBGEN (Bitfield-Mask: 0x01) */ + + +/* =========================================================================================================================== */ +/* ================ CACHECTRL ================ */ +/* =========================================================================================================================== */ + +/* ======================================================= CACHECFG ======================================================== */ +#define CACHECTRL_CACHECFG_ENABLE_MONITOR_Pos (24UL) /*!< CACHECTRL CACHECFG: ENABLE_MONITOR (Bit 24) */ +#define CACHECTRL_CACHECFG_ENABLE_MONITOR_Msk (0x1000000UL) /*!< CACHECTRL CACHECFG: ENABLE_MONITOR (Bitfield-Mask: 0x01) */ +#define CACHECTRL_CACHECFG_DATA_CLKGATE_Pos (20UL) /*!< CACHECTRL CACHECFG: DATA_CLKGATE (Bit 20) */ +#define CACHECTRL_CACHECFG_DATA_CLKGATE_Msk (0x100000UL) /*!< CACHECTRL CACHECFG: DATA_CLKGATE (Bitfield-Mask: 0x01) */ +#define CACHECTRL_CACHECFG_CACHE_LS_Pos (11UL) /*!< CACHECTRL CACHECFG: CACHE_LS (Bit 11) */ +#define CACHECTRL_CACHECFG_CACHE_LS_Msk (0x800UL) /*!< CACHECTRL CACHECFG: CACHE_LS (Bitfield-Mask: 0x01) */ +#define CACHECTRL_CACHECFG_CACHE_CLKGATE_Pos (10UL) /*!< CACHECTRL CACHECFG: CACHE_CLKGATE (Bit 10) */ +#define CACHECTRL_CACHECFG_CACHE_CLKGATE_Msk (0x400UL) /*!< CACHECTRL CACHECFG: CACHE_CLKGATE (Bitfield-Mask: 0x01) */ +#define CACHECTRL_CACHECFG_DCACHE_ENABLE_Pos (9UL) /*!< CACHECTRL CACHECFG: DCACHE_ENABLE (Bit 9) */ +#define CACHECTRL_CACHECFG_DCACHE_ENABLE_Msk (0x200UL) /*!< CACHECTRL CACHECFG: DCACHE_ENABLE (Bitfield-Mask: 0x01) */ +#define CACHECTRL_CACHECFG_ICACHE_ENABLE_Pos (8UL) /*!< CACHECTRL CACHECFG: ICACHE_ENABLE (Bit 8) */ +#define CACHECTRL_CACHECFG_ICACHE_ENABLE_Msk (0x100UL) /*!< CACHECTRL CACHECFG: ICACHE_ENABLE (Bitfield-Mask: 0x01) */ +#define CACHECTRL_CACHECFG_CONFIG_Pos (4UL) /*!< CACHECTRL CACHECFG: CONFIG (Bit 4) */ +#define CACHECTRL_CACHECFG_CONFIG_Msk (0xf0UL) /*!< CACHECTRL CACHECFG: CONFIG (Bitfield-Mask: 0x0f) */ +#define CACHECTRL_CACHECFG_ENABLE_NC1_Pos (3UL) /*!< CACHECTRL CACHECFG: ENABLE_NC1 (Bit 3) */ +#define CACHECTRL_CACHECFG_ENABLE_NC1_Msk (0x8UL) /*!< CACHECTRL CACHECFG: ENABLE_NC1 (Bitfield-Mask: 0x01) */ +#define CACHECTRL_CACHECFG_ENABLE_NC0_Pos (2UL) /*!< CACHECTRL CACHECFG: ENABLE_NC0 (Bit 2) */ +#define CACHECTRL_CACHECFG_ENABLE_NC0_Msk (0x4UL) /*!< CACHECTRL CACHECFG: ENABLE_NC0 (Bitfield-Mask: 0x01) */ +#define CACHECTRL_CACHECFG_LRU_Pos (1UL) /*!< CACHECTRL CACHECFG: LRU (Bit 1) */ +#define CACHECTRL_CACHECFG_LRU_Msk (0x2UL) /*!< CACHECTRL CACHECFG: LRU (Bitfield-Mask: 0x01) */ +#define CACHECTRL_CACHECFG_ENABLE_Pos (0UL) /*!< CACHECTRL CACHECFG: ENABLE (Bit 0) */ +#define CACHECTRL_CACHECFG_ENABLE_Msk (0x1UL) /*!< CACHECTRL CACHECFG: ENABLE (Bitfield-Mask: 0x01) */ +/* ======================================================= FLASHCFG ======================================================== */ +#define CACHECTRL_FLASHCFG_LPMMODE_Pos (12UL) /*!< CACHECTRL FLASHCFG: LPMMODE (Bit 12) */ +#define CACHECTRL_FLASHCFG_LPMMODE_Msk (0x3000UL) /*!< CACHECTRL FLASHCFG: LPMMODE (Bitfield-Mask: 0x03) */ +#define CACHECTRL_FLASHCFG_LPM_RD_WAIT_Pos (8UL) /*!< CACHECTRL FLASHCFG: LPM_RD_WAIT (Bit 8) */ +#define CACHECTRL_FLASHCFG_LPM_RD_WAIT_Msk (0xf00UL) /*!< CACHECTRL FLASHCFG: LPM_RD_WAIT (Bitfield-Mask: 0x0f) */ +#define CACHECTRL_FLASHCFG_SEDELAY_Pos (4UL) /*!< CACHECTRL FLASHCFG: SEDELAY (Bit 4) */ +#define CACHECTRL_FLASHCFG_SEDELAY_Msk (0x70UL) /*!< CACHECTRL FLASHCFG: SEDELAY (Bitfield-Mask: 0x07) */ +#define CACHECTRL_FLASHCFG_RD_WAIT_Pos (0UL) /*!< CACHECTRL FLASHCFG: RD_WAIT (Bit 0) */ +#define CACHECTRL_FLASHCFG_RD_WAIT_Msk (0xfUL) /*!< CACHECTRL FLASHCFG: RD_WAIT (Bitfield-Mask: 0x0f) */ +/* ========================================================= CTRL ========================================================== */ +#define CACHECTRL_CTRL_FLASH1_SLM_ENABLE_Pos (10UL) /*!< CACHECTRL CTRL: FLASH1_SLM_ENABLE (Bit 10) */ +#define CACHECTRL_CTRL_FLASH1_SLM_ENABLE_Msk (0x400UL) /*!< CACHECTRL CTRL: FLASH1_SLM_ENABLE (Bitfield-Mask: 0x01) */ +#define CACHECTRL_CTRL_FLASH1_SLM_DISABLE_Pos (9UL) /*!< CACHECTRL CTRL: FLASH1_SLM_DISABLE (Bit 9) */ +#define CACHECTRL_CTRL_FLASH1_SLM_DISABLE_Msk (0x200UL) /*!< CACHECTRL CTRL: FLASH1_SLM_DISABLE (Bitfield-Mask: 0x01) */ +#define CACHECTRL_CTRL_FLASH1_SLM_STATUS_Pos (8UL) /*!< CACHECTRL CTRL: FLASH1_SLM_STATUS (Bit 8) */ +#define CACHECTRL_CTRL_FLASH1_SLM_STATUS_Msk (0x100UL) /*!< CACHECTRL CTRL: FLASH1_SLM_STATUS (Bitfield-Mask: 0x01) */ +#define CACHECTRL_CTRL_FLASH0_SLM_ENABLE_Pos (6UL) /*!< CACHECTRL CTRL: FLASH0_SLM_ENABLE (Bit 6) */ +#define CACHECTRL_CTRL_FLASH0_SLM_ENABLE_Msk (0x40UL) /*!< CACHECTRL CTRL: FLASH0_SLM_ENABLE (Bitfield-Mask: 0x01) */ +#define CACHECTRL_CTRL_FLASH0_SLM_DISABLE_Pos (5UL) /*!< CACHECTRL CTRL: FLASH0_SLM_DISABLE (Bit 5) */ +#define CACHECTRL_CTRL_FLASH0_SLM_DISABLE_Msk (0x20UL) /*!< CACHECTRL CTRL: FLASH0_SLM_DISABLE (Bitfield-Mask: 0x01) */ +#define CACHECTRL_CTRL_FLASH0_SLM_STATUS_Pos (4UL) /*!< CACHECTRL CTRL: FLASH0_SLM_STATUS (Bit 4) */ +#define CACHECTRL_CTRL_FLASH0_SLM_STATUS_Msk (0x10UL) /*!< CACHECTRL CTRL: FLASH0_SLM_STATUS (Bitfield-Mask: 0x01) */ +#define CACHECTRL_CTRL_CACHE_READY_Pos (2UL) /*!< CACHECTRL CTRL: CACHE_READY (Bit 2) */ +#define CACHECTRL_CTRL_CACHE_READY_Msk (0x4UL) /*!< CACHECTRL CTRL: CACHE_READY (Bitfield-Mask: 0x01) */ +#define CACHECTRL_CTRL_RESET_STAT_Pos (1UL) /*!< CACHECTRL CTRL: RESET_STAT (Bit 1) */ +#define CACHECTRL_CTRL_RESET_STAT_Msk (0x2UL) /*!< CACHECTRL CTRL: RESET_STAT (Bitfield-Mask: 0x01) */ +#define CACHECTRL_CTRL_INVALIDATE_Pos (0UL) /*!< CACHECTRL CTRL: INVALIDATE (Bit 0) */ +#define CACHECTRL_CTRL_INVALIDATE_Msk (0x1UL) /*!< CACHECTRL CTRL: INVALIDATE (Bitfield-Mask: 0x01) */ +/* ======================================================= NCR0START ======================================================= */ +#define CACHECTRL_NCR0START_ADDR_Pos (4UL) /*!< CACHECTRL NCR0START: ADDR (Bit 4) */ +#define CACHECTRL_NCR0START_ADDR_Msk (0x7fffff0UL) /*!< CACHECTRL NCR0START: ADDR (Bitfield-Mask: 0x7fffff) */ +/* ======================================================== NCR0END ======================================================== */ +#define CACHECTRL_NCR0END_ADDR_Pos (4UL) /*!< CACHECTRL NCR0END: ADDR (Bit 4) */ +#define CACHECTRL_NCR0END_ADDR_Msk (0x7fffff0UL) /*!< CACHECTRL NCR0END: ADDR (Bitfield-Mask: 0x7fffff) */ +/* ======================================================= NCR1START ======================================================= */ +#define CACHECTRL_NCR1START_ADDR_Pos (4UL) /*!< CACHECTRL NCR1START: ADDR (Bit 4) */ +#define CACHECTRL_NCR1START_ADDR_Msk (0x7fffff0UL) /*!< CACHECTRL NCR1START: ADDR (Bitfield-Mask: 0x7fffff) */ +/* ======================================================== NCR1END ======================================================== */ +#define CACHECTRL_NCR1END_ADDR_Pos (4UL) /*!< CACHECTRL NCR1END: ADDR (Bit 4) */ +#define CACHECTRL_NCR1END_ADDR_Msk (0x7fffff0UL) /*!< CACHECTRL NCR1END: ADDR (Bitfield-Mask: 0x7fffff) */ +/* ========================================================= DMON0 ========================================================= */ +#define CACHECTRL_DMON0_DACCESS_COUNT_Pos (0UL) /*!< CACHECTRL DMON0: DACCESS_COUNT (Bit 0) */ +#define CACHECTRL_DMON0_DACCESS_COUNT_Msk (0xffffffffUL) /*!< CACHECTRL DMON0: DACCESS_COUNT (Bitfield-Mask: 0xffffffff) */ +/* ========================================================= DMON1 ========================================================= */ +#define CACHECTRL_DMON1_DLOOKUP_COUNT_Pos (0UL) /*!< CACHECTRL DMON1: DLOOKUP_COUNT (Bit 0) */ +#define CACHECTRL_DMON1_DLOOKUP_COUNT_Msk (0xffffffffUL) /*!< CACHECTRL DMON1: DLOOKUP_COUNT (Bitfield-Mask: 0xffffffff) */ +/* ========================================================= DMON2 ========================================================= */ +#define CACHECTRL_DMON2_DHIT_COUNT_Pos (0UL) /*!< CACHECTRL DMON2: DHIT_COUNT (Bit 0) */ +#define CACHECTRL_DMON2_DHIT_COUNT_Msk (0xffffffffUL) /*!< CACHECTRL DMON2: DHIT_COUNT (Bitfield-Mask: 0xffffffff) */ +/* ========================================================= DMON3 ========================================================= */ +#define CACHECTRL_DMON3_DLINE_COUNT_Pos (0UL) /*!< CACHECTRL DMON3: DLINE_COUNT (Bit 0) */ +#define CACHECTRL_DMON3_DLINE_COUNT_Msk (0xffffffffUL) /*!< CACHECTRL DMON3: DLINE_COUNT (Bitfield-Mask: 0xffffffff) */ +/* ========================================================= IMON0 ========================================================= */ +#define CACHECTRL_IMON0_IACCESS_COUNT_Pos (0UL) /*!< CACHECTRL IMON0: IACCESS_COUNT (Bit 0) */ +#define CACHECTRL_IMON0_IACCESS_COUNT_Msk (0xffffffffUL) /*!< CACHECTRL IMON0: IACCESS_COUNT (Bitfield-Mask: 0xffffffff) */ +/* ========================================================= IMON1 ========================================================= */ +#define CACHECTRL_IMON1_ILOOKUP_COUNT_Pos (0UL) /*!< CACHECTRL IMON1: ILOOKUP_COUNT (Bit 0) */ +#define CACHECTRL_IMON1_ILOOKUP_COUNT_Msk (0xffffffffUL) /*!< CACHECTRL IMON1: ILOOKUP_COUNT (Bitfield-Mask: 0xffffffff) */ +/* ========================================================= IMON2 ========================================================= */ +#define CACHECTRL_IMON2_IHIT_COUNT_Pos (0UL) /*!< CACHECTRL IMON2: IHIT_COUNT (Bit 0) */ +#define CACHECTRL_IMON2_IHIT_COUNT_Msk (0xffffffffUL) /*!< CACHECTRL IMON2: IHIT_COUNT (Bitfield-Mask: 0xffffffff) */ +/* ========================================================= IMON3 ========================================================= */ +#define CACHECTRL_IMON3_ILINE_COUNT_Pos (0UL) /*!< CACHECTRL IMON3: ILINE_COUNT (Bit 0) */ +#define CACHECTRL_IMON3_ILINE_COUNT_Msk (0xffffffffUL) /*!< CACHECTRL IMON3: ILINE_COUNT (Bitfield-Mask: 0xffffffff) */ + + +/* =========================================================================================================================== */ +/* ================ CLKGEN ================ */ +/* =========================================================================================================================== */ + +/* ========================================================= CALXT ========================================================= */ +#define CLKGEN_CALXT_CALXT_Pos (0UL) /*!< CLKGEN CALXT: CALXT (Bit 0) */ +#define CLKGEN_CALXT_CALXT_Msk (0x7ffUL) /*!< CLKGEN CALXT: CALXT (Bitfield-Mask: 0x7ff) */ +/* ========================================================= CALRC ========================================================= */ +#define CLKGEN_CALRC_CALRC_Pos (0UL) /*!< CLKGEN CALRC: CALRC (Bit 0) */ +#define CLKGEN_CALRC_CALRC_Msk (0x3ffffUL) /*!< CLKGEN CALRC: CALRC (Bitfield-Mask: 0x3ffff) */ +/* ======================================================== ACALCTR ======================================================== */ +#define CLKGEN_ACALCTR_ACALCTR_Pos (0UL) /*!< CLKGEN ACALCTR: ACALCTR (Bit 0) */ +#define CLKGEN_ACALCTR_ACALCTR_Msk (0xffffffUL) /*!< CLKGEN ACALCTR: ACALCTR (Bitfield-Mask: 0xffffff) */ +/* ========================================================= OCTRL ========================================================= */ +#define CLKGEN_OCTRL_ACAL_Pos (8UL) /*!< CLKGEN OCTRL: ACAL (Bit 8) */ +#define CLKGEN_OCTRL_ACAL_Msk (0x700UL) /*!< CLKGEN OCTRL: ACAL (Bitfield-Mask: 0x07) */ +#define CLKGEN_OCTRL_OSEL_Pos (7UL) /*!< CLKGEN OCTRL: OSEL (Bit 7) */ +#define CLKGEN_OCTRL_OSEL_Msk (0x80UL) /*!< CLKGEN OCTRL: OSEL (Bitfield-Mask: 0x01) */ +#define CLKGEN_OCTRL_FOS_Pos (6UL) /*!< CLKGEN OCTRL: FOS (Bit 6) */ +#define CLKGEN_OCTRL_FOS_Msk (0x40UL) /*!< CLKGEN OCTRL: FOS (Bitfield-Mask: 0x01) */ +#define CLKGEN_OCTRL_STOPRC_Pos (1UL) /*!< CLKGEN OCTRL: STOPRC (Bit 1) */ +#define CLKGEN_OCTRL_STOPRC_Msk (0x2UL) /*!< CLKGEN OCTRL: STOPRC (Bitfield-Mask: 0x01) */ +#define CLKGEN_OCTRL_STOPXT_Pos (0UL) /*!< CLKGEN OCTRL: STOPXT (Bit 0) */ +#define CLKGEN_OCTRL_STOPXT_Msk (0x1UL) /*!< CLKGEN OCTRL: STOPXT (Bitfield-Mask: 0x01) */ +/* ======================================================== CLKOUT ========================================================= */ +#define CLKGEN_CLKOUT_CKEN_Pos (7UL) /*!< CLKGEN CLKOUT: CKEN (Bit 7) */ +#define CLKGEN_CLKOUT_CKEN_Msk (0x80UL) /*!< CLKGEN CLKOUT: CKEN (Bitfield-Mask: 0x01) */ +#define CLKGEN_CLKOUT_CKSEL_Pos (0UL) /*!< CLKGEN CLKOUT: CKSEL (Bit 0) */ +#define CLKGEN_CLKOUT_CKSEL_Msk (0x3fUL) /*!< CLKGEN CLKOUT: CKSEL (Bitfield-Mask: 0x3f) */ +/* ======================================================== CLKKEY ========================================================= */ +#define CLKGEN_CLKKEY_CLKKEY_Pos (0UL) /*!< CLKGEN CLKKEY: CLKKEY (Bit 0) */ +#define CLKGEN_CLKKEY_CLKKEY_Msk (0xffffffffUL) /*!< CLKGEN CLKKEY: CLKKEY (Bitfield-Mask: 0xffffffff) */ +/* ========================================================= CCTRL ========================================================= */ +#define CLKGEN_CCTRL_CORESEL_Pos (0UL) /*!< CLKGEN CCTRL: CORESEL (Bit 0) */ +#define CLKGEN_CCTRL_CORESEL_Msk (0x1UL) /*!< CLKGEN CCTRL: CORESEL (Bitfield-Mask: 0x01) */ +/* ======================================================== STATUS ========================================================= */ +#define CLKGEN_STATUS_OSCF_Pos (1UL) /*!< CLKGEN STATUS: OSCF (Bit 1) */ +#define CLKGEN_STATUS_OSCF_Msk (0x2UL) /*!< CLKGEN STATUS: OSCF (Bitfield-Mask: 0x01) */ +#define CLKGEN_STATUS_OMODE_Pos (0UL) /*!< CLKGEN STATUS: OMODE (Bit 0) */ +#define CLKGEN_STATUS_OMODE_Msk (0x1UL) /*!< CLKGEN STATUS: OMODE (Bitfield-Mask: 0x01) */ +/* ========================================================= HFADJ ========================================================= */ +#define CLKGEN_HFADJ_HFADJGAIN_Pos (21UL) /*!< CLKGEN HFADJ: HFADJGAIN (Bit 21) */ +#define CLKGEN_HFADJ_HFADJGAIN_Msk (0xe00000UL) /*!< CLKGEN HFADJ: HFADJGAIN (Bitfield-Mask: 0x07) */ +#define CLKGEN_HFADJ_HFWARMUP_Pos (20UL) /*!< CLKGEN HFADJ: HFWARMUP (Bit 20) */ +#define CLKGEN_HFADJ_HFWARMUP_Msk (0x100000UL) /*!< CLKGEN HFADJ: HFWARMUP (Bitfield-Mask: 0x01) */ +#define CLKGEN_HFADJ_HFXTADJ_Pos (8UL) /*!< CLKGEN HFADJ: HFXTADJ (Bit 8) */ +#define CLKGEN_HFADJ_HFXTADJ_Msk (0xfff00UL) /*!< CLKGEN HFADJ: HFXTADJ (Bitfield-Mask: 0xfff) */ +#define CLKGEN_HFADJ_HFADJCK_Pos (1UL) /*!< CLKGEN HFADJ: HFADJCK (Bit 1) */ +#define CLKGEN_HFADJ_HFADJCK_Msk (0xeUL) /*!< CLKGEN HFADJ: HFADJCK (Bitfield-Mask: 0x07) */ +#define CLKGEN_HFADJ_HFADJEN_Pos (0UL) /*!< CLKGEN HFADJ: HFADJEN (Bit 0) */ +#define CLKGEN_HFADJ_HFADJEN_Msk (0x1UL) /*!< CLKGEN HFADJ: HFADJEN (Bitfield-Mask: 0x01) */ +/* ====================================================== CLOCKENSTAT ====================================================== */ +#define CLKGEN_CLOCKENSTAT_CLOCKENSTAT_Pos (0UL) /*!< CLKGEN CLOCKENSTAT: CLOCKENSTAT (Bit 0) */ +#define CLKGEN_CLOCKENSTAT_CLOCKENSTAT_Msk (0xffffffffUL) /*!< CLKGEN CLOCKENSTAT: CLOCKENSTAT (Bitfield-Mask: 0xffffffff) */ +/* ===================================================== CLOCKEN2STAT ====================================================== */ +#define CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_Pos (0UL) /*!< CLKGEN CLOCKEN2STAT: CLOCKEN2STAT (Bit 0) */ +#define CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_Msk (0xffffffffUL) /*!< CLKGEN CLOCKEN2STAT: CLOCKEN2STAT (Bitfield-Mask: 0xffffffff) */ +/* ===================================================== CLOCKEN3STAT ====================================================== */ +#define CLKGEN_CLOCKEN3STAT_CLOCKEN3STAT_Pos (0UL) /*!< CLKGEN CLOCKEN3STAT: CLOCKEN3STAT (Bit 0) */ +#define CLKGEN_CLOCKEN3STAT_CLOCKEN3STAT_Msk (0xffffffffUL) /*!< CLKGEN CLOCKEN3STAT: CLOCKEN3STAT (Bitfield-Mask: 0xffffffff) */ +/* ======================================================= FREQCTRL ======================================================== */ +#define CLKGEN_FREQCTRL_BURSTSTATUS_Pos (2UL) /*!< CLKGEN FREQCTRL: BURSTSTATUS (Bit 2) */ +#define CLKGEN_FREQCTRL_BURSTSTATUS_Msk (0x4UL) /*!< CLKGEN FREQCTRL: BURSTSTATUS (Bitfield-Mask: 0x01) */ +#define CLKGEN_FREQCTRL_BURSTACK_Pos (1UL) /*!< CLKGEN FREQCTRL: BURSTACK (Bit 1) */ +#define CLKGEN_FREQCTRL_BURSTACK_Msk (0x2UL) /*!< CLKGEN FREQCTRL: BURSTACK (Bitfield-Mask: 0x01) */ +#define CLKGEN_FREQCTRL_BURSTREQ_Pos (0UL) /*!< CLKGEN FREQCTRL: BURSTREQ (Bit 0) */ +#define CLKGEN_FREQCTRL_BURSTREQ_Msk (0x1UL) /*!< CLKGEN FREQCTRL: BURSTREQ (Bitfield-Mask: 0x01) */ +/* ===================================================== BLEBUCKTONADJ ===================================================== */ +#define CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTEN_Pos (27UL) /*!< CLKGEN BLEBUCKTONADJ: ZEROLENDETECTEN (Bit 27) */ +#define CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTEN_Msk (0x8000000UL) /*!< CLKGEN BLEBUCKTONADJ: ZEROLENDETECTEN (Bitfield-Mask: 0x01) */ +#define CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_Pos (23UL) /*!< CLKGEN BLEBUCKTONADJ: ZEROLENDETECTTRIM (Bit 23) */ +#define CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_Msk (0x7800000UL) /*!< CLKGEN BLEBUCKTONADJ: ZEROLENDETECTTRIM (Bitfield-Mask: 0x0f) */ +#define CLKGEN_BLEBUCKTONADJ_TONADJUSTEN_Pos (22UL) /*!< CLKGEN BLEBUCKTONADJ: TONADJUSTEN (Bit 22) */ +#define CLKGEN_BLEBUCKTONADJ_TONADJUSTEN_Msk (0x400000UL) /*!< CLKGEN BLEBUCKTONADJ: TONADJUSTEN (Bitfield-Mask: 0x01) */ +#define CLKGEN_BLEBUCKTONADJ_TONADJUSTPERIOD_Pos (20UL) /*!< CLKGEN BLEBUCKTONADJ: TONADJUSTPERIOD (Bit 20) */ +#define CLKGEN_BLEBUCKTONADJ_TONADJUSTPERIOD_Msk (0x300000UL) /*!< CLKGEN BLEBUCKTONADJ: TONADJUSTPERIOD (Bitfield-Mask: 0x03) */ +#define CLKGEN_BLEBUCKTONADJ_TONHIGHTHRESHOLD_Pos (10UL) /*!< CLKGEN BLEBUCKTONADJ: TONHIGHTHRESHOLD (Bit 10) */ +#define CLKGEN_BLEBUCKTONADJ_TONHIGHTHRESHOLD_Msk (0xffc00UL) /*!< CLKGEN BLEBUCKTONADJ: TONHIGHTHRESHOLD (Bitfield-Mask: 0x3ff) */ +#define CLKGEN_BLEBUCKTONADJ_TONLOWTHRESHOLD_Pos (0UL) /*!< CLKGEN BLEBUCKTONADJ: TONLOWTHRESHOLD (Bit 0) */ +#define CLKGEN_BLEBUCKTONADJ_TONLOWTHRESHOLD_Msk (0x3ffUL) /*!< CLKGEN BLEBUCKTONADJ: TONLOWTHRESHOLD (Bitfield-Mask: 0x3ff) */ +/* ======================================================= INTRPTEN ======================================================== */ +#define CLKGEN_INTRPTEN_OF_Pos (2UL) /*!< CLKGEN INTRPTEN: OF (Bit 2) */ +#define CLKGEN_INTRPTEN_OF_Msk (0x4UL) /*!< CLKGEN INTRPTEN: OF (Bitfield-Mask: 0x01) */ +#define CLKGEN_INTRPTEN_ACC_Pos (1UL) /*!< CLKGEN INTRPTEN: ACC (Bit 1) */ +#define CLKGEN_INTRPTEN_ACC_Msk (0x2UL) /*!< CLKGEN INTRPTEN: ACC (Bitfield-Mask: 0x01) */ +#define CLKGEN_INTRPTEN_ACF_Pos (0UL) /*!< CLKGEN INTRPTEN: ACF (Bit 0) */ +#define CLKGEN_INTRPTEN_ACF_Msk (0x1UL) /*!< CLKGEN INTRPTEN: ACF (Bitfield-Mask: 0x01) */ +/* ====================================================== INTRPTSTAT ======================================================= */ +#define CLKGEN_INTRPTSTAT_OF_Pos (2UL) /*!< CLKGEN INTRPTSTAT: OF (Bit 2) */ +#define CLKGEN_INTRPTSTAT_OF_Msk (0x4UL) /*!< CLKGEN INTRPTSTAT: OF (Bitfield-Mask: 0x01) */ +#define CLKGEN_INTRPTSTAT_ACC_Pos (1UL) /*!< CLKGEN INTRPTSTAT: ACC (Bit 1) */ +#define CLKGEN_INTRPTSTAT_ACC_Msk (0x2UL) /*!< CLKGEN INTRPTSTAT: ACC (Bitfield-Mask: 0x01) */ +#define CLKGEN_INTRPTSTAT_ACF_Pos (0UL) /*!< CLKGEN INTRPTSTAT: ACF (Bit 0) */ +#define CLKGEN_INTRPTSTAT_ACF_Msk (0x1UL) /*!< CLKGEN INTRPTSTAT: ACF (Bitfield-Mask: 0x01) */ +/* ======================================================= INTRPTCLR ======================================================= */ +#define CLKGEN_INTRPTCLR_OF_Pos (2UL) /*!< CLKGEN INTRPTCLR: OF (Bit 2) */ +#define CLKGEN_INTRPTCLR_OF_Msk (0x4UL) /*!< CLKGEN INTRPTCLR: OF (Bitfield-Mask: 0x01) */ +#define CLKGEN_INTRPTCLR_ACC_Pos (1UL) /*!< CLKGEN INTRPTCLR: ACC (Bit 1) */ +#define CLKGEN_INTRPTCLR_ACC_Msk (0x2UL) /*!< CLKGEN INTRPTCLR: ACC (Bitfield-Mask: 0x01) */ +#define CLKGEN_INTRPTCLR_ACF_Pos (0UL) /*!< CLKGEN INTRPTCLR: ACF (Bit 0) */ +#define CLKGEN_INTRPTCLR_ACF_Msk (0x1UL) /*!< CLKGEN INTRPTCLR: ACF (Bitfield-Mask: 0x01) */ +/* ======================================================= INTRPTSET ======================================================= */ +#define CLKGEN_INTRPTSET_OF_Pos (2UL) /*!< CLKGEN INTRPTSET: OF (Bit 2) */ +#define CLKGEN_INTRPTSET_OF_Msk (0x4UL) /*!< CLKGEN INTRPTSET: OF (Bitfield-Mask: 0x01) */ +#define CLKGEN_INTRPTSET_ACC_Pos (1UL) /*!< CLKGEN INTRPTSET: ACC (Bit 1) */ +#define CLKGEN_INTRPTSET_ACC_Msk (0x2UL) /*!< CLKGEN INTRPTSET: ACC (Bitfield-Mask: 0x01) */ +#define CLKGEN_INTRPTSET_ACF_Pos (0UL) /*!< CLKGEN INTRPTSET: ACF (Bit 0) */ +#define CLKGEN_INTRPTSET_ACF_Msk (0x1UL) /*!< CLKGEN INTRPTSET: ACF (Bitfield-Mask: 0x01) */ + + +/* =========================================================================================================================== */ +/* ================ CTIMER ================ */ +/* =========================================================================================================================== */ + +/* ========================================================= TMR0 ========================================================== */ +#define CTIMER_TMR0_CTTMRB0_Pos (16UL) /*!< CTIMER TMR0: CTTMRB0 (Bit 16) */ +#define CTIMER_TMR0_CTTMRB0_Msk (0xffff0000UL) /*!< CTIMER TMR0: CTTMRB0 (Bitfield-Mask: 0xffff) */ +#define CTIMER_TMR0_CTTMRA0_Pos (0UL) /*!< CTIMER TMR0: CTTMRA0 (Bit 0) */ +#define CTIMER_TMR0_CTTMRA0_Msk (0xffffUL) /*!< CTIMER TMR0: CTTMRA0 (Bitfield-Mask: 0xffff) */ +/* ======================================================== CMPRA0 ========================================================= */ +#define CTIMER_CMPRA0_CMPR1A0_Pos (16UL) /*!< CTIMER CMPRA0: CMPR1A0 (Bit 16) */ +#define CTIMER_CMPRA0_CMPR1A0_Msk (0xffff0000UL) /*!< CTIMER CMPRA0: CMPR1A0 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRA0_CMPR0A0_Pos (0UL) /*!< CTIMER CMPRA0: CMPR0A0 (Bit 0) */ +#define CTIMER_CMPRA0_CMPR0A0_Msk (0xffffUL) /*!< CTIMER CMPRA0: CMPR0A0 (Bitfield-Mask: 0xffff) */ +/* ======================================================== CMPRB0 ========================================================= */ +#define CTIMER_CMPRB0_CMPR1B0_Pos (16UL) /*!< CTIMER CMPRB0: CMPR1B0 (Bit 16) */ +#define CTIMER_CMPRB0_CMPR1B0_Msk (0xffff0000UL) /*!< CTIMER CMPRB0: CMPR1B0 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRB0_CMPR0B0_Pos (0UL) /*!< CTIMER CMPRB0: CMPR0B0 (Bit 0) */ +#define CTIMER_CMPRB0_CMPR0B0_Msk (0xffffUL) /*!< CTIMER CMPRB0: CMPR0B0 (Bitfield-Mask: 0xffff) */ +/* ========================================================= CTRL0 ========================================================= */ +#define CTIMER_CTRL0_CTLINK0_Pos (31UL) /*!< CTIMER CTRL0: CTLINK0 (Bit 31) */ +#define CTIMER_CTRL0_CTLINK0_Msk (0x80000000UL) /*!< CTIMER CTRL0: CTLINK0 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL0_TMRB0POL_Pos (28UL) /*!< CTIMER CTRL0: TMRB0POL (Bit 28) */ +#define CTIMER_CTRL0_TMRB0POL_Msk (0x10000000UL) /*!< CTIMER CTRL0: TMRB0POL (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL0_TMRB0CLR_Pos (27UL) /*!< CTIMER CTRL0: TMRB0CLR (Bit 27) */ +#define CTIMER_CTRL0_TMRB0CLR_Msk (0x8000000UL) /*!< CTIMER CTRL0: TMRB0CLR (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL0_TMRB0IE1_Pos (26UL) /*!< CTIMER CTRL0: TMRB0IE1 (Bit 26) */ +#define CTIMER_CTRL0_TMRB0IE1_Msk (0x4000000UL) /*!< CTIMER CTRL0: TMRB0IE1 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL0_TMRB0IE0_Pos (25UL) /*!< CTIMER CTRL0: TMRB0IE0 (Bit 25) */ +#define CTIMER_CTRL0_TMRB0IE0_Msk (0x2000000UL) /*!< CTIMER CTRL0: TMRB0IE0 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL0_TMRB0FN_Pos (22UL) /*!< CTIMER CTRL0: TMRB0FN (Bit 22) */ +#define CTIMER_CTRL0_TMRB0FN_Msk (0x1c00000UL) /*!< CTIMER CTRL0: TMRB0FN (Bitfield-Mask: 0x07) */ +#define CTIMER_CTRL0_TMRB0CLK_Pos (17UL) /*!< CTIMER CTRL0: TMRB0CLK (Bit 17) */ +#define CTIMER_CTRL0_TMRB0CLK_Msk (0x3e0000UL) /*!< CTIMER CTRL0: TMRB0CLK (Bitfield-Mask: 0x1f) */ +#define CTIMER_CTRL0_TMRB0EN_Pos (16UL) /*!< CTIMER CTRL0: TMRB0EN (Bit 16) */ +#define CTIMER_CTRL0_TMRB0EN_Msk (0x10000UL) /*!< CTIMER CTRL0: TMRB0EN (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL0_TMRA0POL_Pos (12UL) /*!< CTIMER CTRL0: TMRA0POL (Bit 12) */ +#define CTIMER_CTRL0_TMRA0POL_Msk (0x1000UL) /*!< CTIMER CTRL0: TMRA0POL (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL0_TMRA0CLR_Pos (11UL) /*!< CTIMER CTRL0: TMRA0CLR (Bit 11) */ +#define CTIMER_CTRL0_TMRA0CLR_Msk (0x800UL) /*!< CTIMER CTRL0: TMRA0CLR (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL0_TMRA0IE1_Pos (10UL) /*!< CTIMER CTRL0: TMRA0IE1 (Bit 10) */ +#define CTIMER_CTRL0_TMRA0IE1_Msk (0x400UL) /*!< CTIMER CTRL0: TMRA0IE1 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL0_TMRA0IE0_Pos (9UL) /*!< CTIMER CTRL0: TMRA0IE0 (Bit 9) */ +#define CTIMER_CTRL0_TMRA0IE0_Msk (0x200UL) /*!< CTIMER CTRL0: TMRA0IE0 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL0_TMRA0FN_Pos (6UL) /*!< CTIMER CTRL0: TMRA0FN (Bit 6) */ +#define CTIMER_CTRL0_TMRA0FN_Msk (0x1c0UL) /*!< CTIMER CTRL0: TMRA0FN (Bitfield-Mask: 0x07) */ +#define CTIMER_CTRL0_TMRA0CLK_Pos (1UL) /*!< CTIMER CTRL0: TMRA0CLK (Bit 1) */ +#define CTIMER_CTRL0_TMRA0CLK_Msk (0x3eUL) /*!< CTIMER CTRL0: TMRA0CLK (Bitfield-Mask: 0x1f) */ +#define CTIMER_CTRL0_TMRA0EN_Pos (0UL) /*!< CTIMER CTRL0: TMRA0EN (Bit 0) */ +#define CTIMER_CTRL0_TMRA0EN_Msk (0x1UL) /*!< CTIMER CTRL0: TMRA0EN (Bitfield-Mask: 0x01) */ +/* ======================================================= CMPRAUXA0 ======================================================= */ +#define CTIMER_CMPRAUXA0_CMPR3A0_Pos (16UL) /*!< CTIMER CMPRAUXA0: CMPR3A0 (Bit 16) */ +#define CTIMER_CMPRAUXA0_CMPR3A0_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXA0: CMPR3A0 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRAUXA0_CMPR2A0_Pos (0UL) /*!< CTIMER CMPRAUXA0: CMPR2A0 (Bit 0) */ +#define CTIMER_CMPRAUXA0_CMPR2A0_Msk (0xffffUL) /*!< CTIMER CMPRAUXA0: CMPR2A0 (Bitfield-Mask: 0xffff) */ +/* ======================================================= CMPRAUXB0 ======================================================= */ +#define CTIMER_CMPRAUXB0_CMPR3B0_Pos (16UL) /*!< CTIMER CMPRAUXB0: CMPR3B0 (Bit 16) */ +#define CTIMER_CMPRAUXB0_CMPR3B0_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXB0: CMPR3B0 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRAUXB0_CMPR2B0_Pos (0UL) /*!< CTIMER CMPRAUXB0: CMPR2B0 (Bit 0) */ +#define CTIMER_CMPRAUXB0_CMPR2B0_Msk (0xffffUL) /*!< CTIMER CMPRAUXB0: CMPR2B0 (Bitfield-Mask: 0xffff) */ +/* ========================================================= AUX0 ========================================================== */ +#define CTIMER_AUX0_TMRB0EN23_Pos (30UL) /*!< CTIMER AUX0: TMRB0EN23 (Bit 30) */ +#define CTIMER_AUX0_TMRB0EN23_Msk (0x40000000UL) /*!< CTIMER AUX0: TMRB0EN23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX0_TMRB0POL23_Pos (29UL) /*!< CTIMER AUX0: TMRB0POL23 (Bit 29) */ +#define CTIMER_AUX0_TMRB0POL23_Msk (0x20000000UL) /*!< CTIMER AUX0: TMRB0POL23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX0_TMRB0TINV_Pos (28UL) /*!< CTIMER AUX0: TMRB0TINV (Bit 28) */ +#define CTIMER_AUX0_TMRB0TINV_Msk (0x10000000UL) /*!< CTIMER AUX0: TMRB0TINV (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX0_TMRB0NOSYNC_Pos (27UL) /*!< CTIMER AUX0: TMRB0NOSYNC (Bit 27) */ +#define CTIMER_AUX0_TMRB0NOSYNC_Msk (0x8000000UL) /*!< CTIMER AUX0: TMRB0NOSYNC (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX0_TMRB0TRIG_Pos (23UL) /*!< CTIMER AUX0: TMRB0TRIG (Bit 23) */ +#define CTIMER_AUX0_TMRB0TRIG_Msk (0x7800000UL) /*!< CTIMER AUX0: TMRB0TRIG (Bitfield-Mask: 0x0f) */ +#define CTIMER_AUX0_TMRB0LMT_Pos (16UL) /*!< CTIMER AUX0: TMRB0LMT (Bit 16) */ +#define CTIMER_AUX0_TMRB0LMT_Msk (0x3f0000UL) /*!< CTIMER AUX0: TMRB0LMT (Bitfield-Mask: 0x3f) */ +#define CTIMER_AUX0_TMRA0EN23_Pos (14UL) /*!< CTIMER AUX0: TMRA0EN23 (Bit 14) */ +#define CTIMER_AUX0_TMRA0EN23_Msk (0x4000UL) /*!< CTIMER AUX0: TMRA0EN23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX0_TMRA0POL23_Pos (13UL) /*!< CTIMER AUX0: TMRA0POL23 (Bit 13) */ +#define CTIMER_AUX0_TMRA0POL23_Msk (0x2000UL) /*!< CTIMER AUX0: TMRA0POL23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX0_TMRA0TINV_Pos (12UL) /*!< CTIMER AUX0: TMRA0TINV (Bit 12) */ +#define CTIMER_AUX0_TMRA0TINV_Msk (0x1000UL) /*!< CTIMER AUX0: TMRA0TINV (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX0_TMRA0NOSYNC_Pos (11UL) /*!< CTIMER AUX0: TMRA0NOSYNC (Bit 11) */ +#define CTIMER_AUX0_TMRA0NOSYNC_Msk (0x800UL) /*!< CTIMER AUX0: TMRA0NOSYNC (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX0_TMRA0TRIG_Pos (7UL) /*!< CTIMER AUX0: TMRA0TRIG (Bit 7) */ +#define CTIMER_AUX0_TMRA0TRIG_Msk (0x780UL) /*!< CTIMER AUX0: TMRA0TRIG (Bitfield-Mask: 0x0f) */ +#define CTIMER_AUX0_TMRA0LMT_Pos (0UL) /*!< CTIMER AUX0: TMRA0LMT (Bit 0) */ +#define CTIMER_AUX0_TMRA0LMT_Msk (0x7fUL) /*!< CTIMER AUX0: TMRA0LMT (Bitfield-Mask: 0x7f) */ +/* ========================================================= TMR1 ========================================================== */ +#define CTIMER_TMR1_CTTMRB1_Pos (16UL) /*!< CTIMER TMR1: CTTMRB1 (Bit 16) */ +#define CTIMER_TMR1_CTTMRB1_Msk (0xffff0000UL) /*!< CTIMER TMR1: CTTMRB1 (Bitfield-Mask: 0xffff) */ +#define CTIMER_TMR1_CTTMRA1_Pos (0UL) /*!< CTIMER TMR1: CTTMRA1 (Bit 0) */ +#define CTIMER_TMR1_CTTMRA1_Msk (0xffffUL) /*!< CTIMER TMR1: CTTMRA1 (Bitfield-Mask: 0xffff) */ +/* ======================================================== CMPRA1 ========================================================= */ +#define CTIMER_CMPRA1_CMPR1A1_Pos (16UL) /*!< CTIMER CMPRA1: CMPR1A1 (Bit 16) */ +#define CTIMER_CMPRA1_CMPR1A1_Msk (0xffff0000UL) /*!< CTIMER CMPRA1: CMPR1A1 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRA1_CMPR0A1_Pos (0UL) /*!< CTIMER CMPRA1: CMPR0A1 (Bit 0) */ +#define CTIMER_CMPRA1_CMPR0A1_Msk (0xffffUL) /*!< CTIMER CMPRA1: CMPR0A1 (Bitfield-Mask: 0xffff) */ +/* ======================================================== CMPRB1 ========================================================= */ +#define CTIMER_CMPRB1_CMPR1B1_Pos (16UL) /*!< CTIMER CMPRB1: CMPR1B1 (Bit 16) */ +#define CTIMER_CMPRB1_CMPR1B1_Msk (0xffff0000UL) /*!< CTIMER CMPRB1: CMPR1B1 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRB1_CMPR0B1_Pos (0UL) /*!< CTIMER CMPRB1: CMPR0B1 (Bit 0) */ +#define CTIMER_CMPRB1_CMPR0B1_Msk (0xffffUL) /*!< CTIMER CMPRB1: CMPR0B1 (Bitfield-Mask: 0xffff) */ +/* ========================================================= CTRL1 ========================================================= */ +#define CTIMER_CTRL1_CTLINK1_Pos (31UL) /*!< CTIMER CTRL1: CTLINK1 (Bit 31) */ +#define CTIMER_CTRL1_CTLINK1_Msk (0x80000000UL) /*!< CTIMER CTRL1: CTLINK1 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL1_TMRB1POL_Pos (28UL) /*!< CTIMER CTRL1: TMRB1POL (Bit 28) */ +#define CTIMER_CTRL1_TMRB1POL_Msk (0x10000000UL) /*!< CTIMER CTRL1: TMRB1POL (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL1_TMRB1CLR_Pos (27UL) /*!< CTIMER CTRL1: TMRB1CLR (Bit 27) */ +#define CTIMER_CTRL1_TMRB1CLR_Msk (0x8000000UL) /*!< CTIMER CTRL1: TMRB1CLR (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL1_TMRB1IE1_Pos (26UL) /*!< CTIMER CTRL1: TMRB1IE1 (Bit 26) */ +#define CTIMER_CTRL1_TMRB1IE1_Msk (0x4000000UL) /*!< CTIMER CTRL1: TMRB1IE1 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL1_TMRB1IE0_Pos (25UL) /*!< CTIMER CTRL1: TMRB1IE0 (Bit 25) */ +#define CTIMER_CTRL1_TMRB1IE0_Msk (0x2000000UL) /*!< CTIMER CTRL1: TMRB1IE0 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL1_TMRB1FN_Pos (22UL) /*!< CTIMER CTRL1: TMRB1FN (Bit 22) */ +#define CTIMER_CTRL1_TMRB1FN_Msk (0x1c00000UL) /*!< CTIMER CTRL1: TMRB1FN (Bitfield-Mask: 0x07) */ +#define CTIMER_CTRL1_TMRB1CLK_Pos (17UL) /*!< CTIMER CTRL1: TMRB1CLK (Bit 17) */ +#define CTIMER_CTRL1_TMRB1CLK_Msk (0x3e0000UL) /*!< CTIMER CTRL1: TMRB1CLK (Bitfield-Mask: 0x1f) */ +#define CTIMER_CTRL1_TMRB1EN_Pos (16UL) /*!< CTIMER CTRL1: TMRB1EN (Bit 16) */ +#define CTIMER_CTRL1_TMRB1EN_Msk (0x10000UL) /*!< CTIMER CTRL1: TMRB1EN (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL1_TMRA1POL_Pos (12UL) /*!< CTIMER CTRL1: TMRA1POL (Bit 12) */ +#define CTIMER_CTRL1_TMRA1POL_Msk (0x1000UL) /*!< CTIMER CTRL1: TMRA1POL (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL1_TMRA1CLR_Pos (11UL) /*!< CTIMER CTRL1: TMRA1CLR (Bit 11) */ +#define CTIMER_CTRL1_TMRA1CLR_Msk (0x800UL) /*!< CTIMER CTRL1: TMRA1CLR (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL1_TMRA1IE1_Pos (10UL) /*!< CTIMER CTRL1: TMRA1IE1 (Bit 10) */ +#define CTIMER_CTRL1_TMRA1IE1_Msk (0x400UL) /*!< CTIMER CTRL1: TMRA1IE1 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL1_TMRA1IE0_Pos (9UL) /*!< CTIMER CTRL1: TMRA1IE0 (Bit 9) */ +#define CTIMER_CTRL1_TMRA1IE0_Msk (0x200UL) /*!< CTIMER CTRL1: TMRA1IE0 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL1_TMRA1FN_Pos (6UL) /*!< CTIMER CTRL1: TMRA1FN (Bit 6) */ +#define CTIMER_CTRL1_TMRA1FN_Msk (0x1c0UL) /*!< CTIMER CTRL1: TMRA1FN (Bitfield-Mask: 0x07) */ +#define CTIMER_CTRL1_TMRA1CLK_Pos (1UL) /*!< CTIMER CTRL1: TMRA1CLK (Bit 1) */ +#define CTIMER_CTRL1_TMRA1CLK_Msk (0x3eUL) /*!< CTIMER CTRL1: TMRA1CLK (Bitfield-Mask: 0x1f) */ +#define CTIMER_CTRL1_TMRA1EN_Pos (0UL) /*!< CTIMER CTRL1: TMRA1EN (Bit 0) */ +#define CTIMER_CTRL1_TMRA1EN_Msk (0x1UL) /*!< CTIMER CTRL1: TMRA1EN (Bitfield-Mask: 0x01) */ +/* ======================================================= CMPRAUXA1 ======================================================= */ +#define CTIMER_CMPRAUXA1_CMPR3A1_Pos (16UL) /*!< CTIMER CMPRAUXA1: CMPR3A1 (Bit 16) */ +#define CTIMER_CMPRAUXA1_CMPR3A1_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXA1: CMPR3A1 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRAUXA1_CMPR2A1_Pos (0UL) /*!< CTIMER CMPRAUXA1: CMPR2A1 (Bit 0) */ +#define CTIMER_CMPRAUXA1_CMPR2A1_Msk (0xffffUL) /*!< CTIMER CMPRAUXA1: CMPR2A1 (Bitfield-Mask: 0xffff) */ +/* ======================================================= CMPRAUXB1 ======================================================= */ +#define CTIMER_CMPRAUXB1_CMPR3B1_Pos (16UL) /*!< CTIMER CMPRAUXB1: CMPR3B1 (Bit 16) */ +#define CTIMER_CMPRAUXB1_CMPR3B1_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXB1: CMPR3B1 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRAUXB1_CMPR2B1_Pos (0UL) /*!< CTIMER CMPRAUXB1: CMPR2B1 (Bit 0) */ +#define CTIMER_CMPRAUXB1_CMPR2B1_Msk (0xffffUL) /*!< CTIMER CMPRAUXB1: CMPR2B1 (Bitfield-Mask: 0xffff) */ +/* ========================================================= AUX1 ========================================================== */ +#define CTIMER_AUX1_TMRB1EN23_Pos (30UL) /*!< CTIMER AUX1: TMRB1EN23 (Bit 30) */ +#define CTIMER_AUX1_TMRB1EN23_Msk (0x40000000UL) /*!< CTIMER AUX1: TMRB1EN23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX1_TMRB1POL23_Pos (29UL) /*!< CTIMER AUX1: TMRB1POL23 (Bit 29) */ +#define CTIMER_AUX1_TMRB1POL23_Msk (0x20000000UL) /*!< CTIMER AUX1: TMRB1POL23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX1_TMRB1TINV_Pos (28UL) /*!< CTIMER AUX1: TMRB1TINV (Bit 28) */ +#define CTIMER_AUX1_TMRB1TINV_Msk (0x10000000UL) /*!< CTIMER AUX1: TMRB1TINV (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX1_TMRB1NOSYNC_Pos (27UL) /*!< CTIMER AUX1: TMRB1NOSYNC (Bit 27) */ +#define CTIMER_AUX1_TMRB1NOSYNC_Msk (0x8000000UL) /*!< CTIMER AUX1: TMRB1NOSYNC (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX1_TMRB1TRIG_Pos (23UL) /*!< CTIMER AUX1: TMRB1TRIG (Bit 23) */ +#define CTIMER_AUX1_TMRB1TRIG_Msk (0x7800000UL) /*!< CTIMER AUX1: TMRB1TRIG (Bitfield-Mask: 0x0f) */ +#define CTIMER_AUX1_TMRB1LMT_Pos (16UL) /*!< CTIMER AUX1: TMRB1LMT (Bit 16) */ +#define CTIMER_AUX1_TMRB1LMT_Msk (0x3f0000UL) /*!< CTIMER AUX1: TMRB1LMT (Bitfield-Mask: 0x3f) */ +#define CTIMER_AUX1_TMRA1EN23_Pos (14UL) /*!< CTIMER AUX1: TMRA1EN23 (Bit 14) */ +#define CTIMER_AUX1_TMRA1EN23_Msk (0x4000UL) /*!< CTIMER AUX1: TMRA1EN23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX1_TMRA1POL23_Pos (13UL) /*!< CTIMER AUX1: TMRA1POL23 (Bit 13) */ +#define CTIMER_AUX1_TMRA1POL23_Msk (0x2000UL) /*!< CTIMER AUX1: TMRA1POL23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX1_TMRA1TINV_Pos (12UL) /*!< CTIMER AUX1: TMRA1TINV (Bit 12) */ +#define CTIMER_AUX1_TMRA1TINV_Msk (0x1000UL) /*!< CTIMER AUX1: TMRA1TINV (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX1_TMRA1NOSYNC_Pos (11UL) /*!< CTIMER AUX1: TMRA1NOSYNC (Bit 11) */ +#define CTIMER_AUX1_TMRA1NOSYNC_Msk (0x800UL) /*!< CTIMER AUX1: TMRA1NOSYNC (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX1_TMRA1TRIG_Pos (7UL) /*!< CTIMER AUX1: TMRA1TRIG (Bit 7) */ +#define CTIMER_AUX1_TMRA1TRIG_Msk (0x780UL) /*!< CTIMER AUX1: TMRA1TRIG (Bitfield-Mask: 0x0f) */ +#define CTIMER_AUX1_TMRA1LMT_Pos (0UL) /*!< CTIMER AUX1: TMRA1LMT (Bit 0) */ +#define CTIMER_AUX1_TMRA1LMT_Msk (0x7fUL) /*!< CTIMER AUX1: TMRA1LMT (Bitfield-Mask: 0x7f) */ +/* ========================================================= TMR2 ========================================================== */ +#define CTIMER_TMR2_CTTMRB2_Pos (16UL) /*!< CTIMER TMR2: CTTMRB2 (Bit 16) */ +#define CTIMER_TMR2_CTTMRB2_Msk (0xffff0000UL) /*!< CTIMER TMR2: CTTMRB2 (Bitfield-Mask: 0xffff) */ +#define CTIMER_TMR2_CTTMRA2_Pos (0UL) /*!< CTIMER TMR2: CTTMRA2 (Bit 0) */ +#define CTIMER_TMR2_CTTMRA2_Msk (0xffffUL) /*!< CTIMER TMR2: CTTMRA2 (Bitfield-Mask: 0xffff) */ +/* ======================================================== CMPRA2 ========================================================= */ +#define CTIMER_CMPRA2_CMPR1A2_Pos (16UL) /*!< CTIMER CMPRA2: CMPR1A2 (Bit 16) */ +#define CTIMER_CMPRA2_CMPR1A2_Msk (0xffff0000UL) /*!< CTIMER CMPRA2: CMPR1A2 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRA2_CMPR0A2_Pos (0UL) /*!< CTIMER CMPRA2: CMPR0A2 (Bit 0) */ +#define CTIMER_CMPRA2_CMPR0A2_Msk (0xffffUL) /*!< CTIMER CMPRA2: CMPR0A2 (Bitfield-Mask: 0xffff) */ +/* ======================================================== CMPRB2 ========================================================= */ +#define CTIMER_CMPRB2_CMPR1B2_Pos (16UL) /*!< CTIMER CMPRB2: CMPR1B2 (Bit 16) */ +#define CTIMER_CMPRB2_CMPR1B2_Msk (0xffff0000UL) /*!< CTIMER CMPRB2: CMPR1B2 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRB2_CMPR0B2_Pos (0UL) /*!< CTIMER CMPRB2: CMPR0B2 (Bit 0) */ +#define CTIMER_CMPRB2_CMPR0B2_Msk (0xffffUL) /*!< CTIMER CMPRB2: CMPR0B2 (Bitfield-Mask: 0xffff) */ +/* ========================================================= CTRL2 ========================================================= */ +#define CTIMER_CTRL2_CTLINK2_Pos (31UL) /*!< CTIMER CTRL2: CTLINK2 (Bit 31) */ +#define CTIMER_CTRL2_CTLINK2_Msk (0x80000000UL) /*!< CTIMER CTRL2: CTLINK2 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL2_TMRB2POL_Pos (28UL) /*!< CTIMER CTRL2: TMRB2POL (Bit 28) */ +#define CTIMER_CTRL2_TMRB2POL_Msk (0x10000000UL) /*!< CTIMER CTRL2: TMRB2POL (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL2_TMRB2CLR_Pos (27UL) /*!< CTIMER CTRL2: TMRB2CLR (Bit 27) */ +#define CTIMER_CTRL2_TMRB2CLR_Msk (0x8000000UL) /*!< CTIMER CTRL2: TMRB2CLR (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL2_TMRB2IE1_Pos (26UL) /*!< CTIMER CTRL2: TMRB2IE1 (Bit 26) */ +#define CTIMER_CTRL2_TMRB2IE1_Msk (0x4000000UL) /*!< CTIMER CTRL2: TMRB2IE1 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL2_TMRB2IE0_Pos (25UL) /*!< CTIMER CTRL2: TMRB2IE0 (Bit 25) */ +#define CTIMER_CTRL2_TMRB2IE0_Msk (0x2000000UL) /*!< CTIMER CTRL2: TMRB2IE0 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL2_TMRB2FN_Pos (22UL) /*!< CTIMER CTRL2: TMRB2FN (Bit 22) */ +#define CTIMER_CTRL2_TMRB2FN_Msk (0x1c00000UL) /*!< CTIMER CTRL2: TMRB2FN (Bitfield-Mask: 0x07) */ +#define CTIMER_CTRL2_TMRB2CLK_Pos (17UL) /*!< CTIMER CTRL2: TMRB2CLK (Bit 17) */ +#define CTIMER_CTRL2_TMRB2CLK_Msk (0x3e0000UL) /*!< CTIMER CTRL2: TMRB2CLK (Bitfield-Mask: 0x1f) */ +#define CTIMER_CTRL2_TMRB2EN_Pos (16UL) /*!< CTIMER CTRL2: TMRB2EN (Bit 16) */ +#define CTIMER_CTRL2_TMRB2EN_Msk (0x10000UL) /*!< CTIMER CTRL2: TMRB2EN (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL2_TMRA2POL_Pos (12UL) /*!< CTIMER CTRL2: TMRA2POL (Bit 12) */ +#define CTIMER_CTRL2_TMRA2POL_Msk (0x1000UL) /*!< CTIMER CTRL2: TMRA2POL (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL2_TMRA2CLR_Pos (11UL) /*!< CTIMER CTRL2: TMRA2CLR (Bit 11) */ +#define CTIMER_CTRL2_TMRA2CLR_Msk (0x800UL) /*!< CTIMER CTRL2: TMRA2CLR (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL2_TMRA2IE1_Pos (10UL) /*!< CTIMER CTRL2: TMRA2IE1 (Bit 10) */ +#define CTIMER_CTRL2_TMRA2IE1_Msk (0x400UL) /*!< CTIMER CTRL2: TMRA2IE1 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL2_TMRA2IE0_Pos (9UL) /*!< CTIMER CTRL2: TMRA2IE0 (Bit 9) */ +#define CTIMER_CTRL2_TMRA2IE0_Msk (0x200UL) /*!< CTIMER CTRL2: TMRA2IE0 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL2_TMRA2FN_Pos (6UL) /*!< CTIMER CTRL2: TMRA2FN (Bit 6) */ +#define CTIMER_CTRL2_TMRA2FN_Msk (0x1c0UL) /*!< CTIMER CTRL2: TMRA2FN (Bitfield-Mask: 0x07) */ +#define CTIMER_CTRL2_TMRA2CLK_Pos (1UL) /*!< CTIMER CTRL2: TMRA2CLK (Bit 1) */ +#define CTIMER_CTRL2_TMRA2CLK_Msk (0x3eUL) /*!< CTIMER CTRL2: TMRA2CLK (Bitfield-Mask: 0x1f) */ +#define CTIMER_CTRL2_TMRA2EN_Pos (0UL) /*!< CTIMER CTRL2: TMRA2EN (Bit 0) */ +#define CTIMER_CTRL2_TMRA2EN_Msk (0x1UL) /*!< CTIMER CTRL2: TMRA2EN (Bitfield-Mask: 0x01) */ +/* ======================================================= CMPRAUXA2 ======================================================= */ +#define CTIMER_CMPRAUXA2_CMPR3A2_Pos (16UL) /*!< CTIMER CMPRAUXA2: CMPR3A2 (Bit 16) */ +#define CTIMER_CMPRAUXA2_CMPR3A2_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXA2: CMPR3A2 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRAUXA2_CMPR2A2_Pos (0UL) /*!< CTIMER CMPRAUXA2: CMPR2A2 (Bit 0) */ +#define CTIMER_CMPRAUXA2_CMPR2A2_Msk (0xffffUL) /*!< CTIMER CMPRAUXA2: CMPR2A2 (Bitfield-Mask: 0xffff) */ +/* ======================================================= CMPRAUXB2 ======================================================= */ +#define CTIMER_CMPRAUXB2_CMPR3B2_Pos (16UL) /*!< CTIMER CMPRAUXB2: CMPR3B2 (Bit 16) */ +#define CTIMER_CMPRAUXB2_CMPR3B2_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXB2: CMPR3B2 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRAUXB2_CMPR2B2_Pos (0UL) /*!< CTIMER CMPRAUXB2: CMPR2B2 (Bit 0) */ +#define CTIMER_CMPRAUXB2_CMPR2B2_Msk (0xffffUL) /*!< CTIMER CMPRAUXB2: CMPR2B2 (Bitfield-Mask: 0xffff) */ +/* ========================================================= AUX2 ========================================================== */ +#define CTIMER_AUX2_TMRB2EN23_Pos (30UL) /*!< CTIMER AUX2: TMRB2EN23 (Bit 30) */ +#define CTIMER_AUX2_TMRB2EN23_Msk (0x40000000UL) /*!< CTIMER AUX2: TMRB2EN23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX2_TMRB2POL23_Pos (29UL) /*!< CTIMER AUX2: TMRB2POL23 (Bit 29) */ +#define CTIMER_AUX2_TMRB2POL23_Msk (0x20000000UL) /*!< CTIMER AUX2: TMRB2POL23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX2_TMRB2TINV_Pos (28UL) /*!< CTIMER AUX2: TMRB2TINV (Bit 28) */ +#define CTIMER_AUX2_TMRB2TINV_Msk (0x10000000UL) /*!< CTIMER AUX2: TMRB2TINV (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX2_TMRB2NOSYNC_Pos (27UL) /*!< CTIMER AUX2: TMRB2NOSYNC (Bit 27) */ +#define CTIMER_AUX2_TMRB2NOSYNC_Msk (0x8000000UL) /*!< CTIMER AUX2: TMRB2NOSYNC (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX2_TMRB2TRIG_Pos (23UL) /*!< CTIMER AUX2: TMRB2TRIG (Bit 23) */ +#define CTIMER_AUX2_TMRB2TRIG_Msk (0x7800000UL) /*!< CTIMER AUX2: TMRB2TRIG (Bitfield-Mask: 0x0f) */ +#define CTIMER_AUX2_TMRB2LMT_Pos (16UL) /*!< CTIMER AUX2: TMRB2LMT (Bit 16) */ +#define CTIMER_AUX2_TMRB2LMT_Msk (0x3f0000UL) /*!< CTIMER AUX2: TMRB2LMT (Bitfield-Mask: 0x3f) */ +#define CTIMER_AUX2_TMRA2EN23_Pos (14UL) /*!< CTIMER AUX2: TMRA2EN23 (Bit 14) */ +#define CTIMER_AUX2_TMRA2EN23_Msk (0x4000UL) /*!< CTIMER AUX2: TMRA2EN23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX2_TMRA2POL23_Pos (13UL) /*!< CTIMER AUX2: TMRA2POL23 (Bit 13) */ +#define CTIMER_AUX2_TMRA2POL23_Msk (0x2000UL) /*!< CTIMER AUX2: TMRA2POL23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX2_TMRA2TINV_Pos (12UL) /*!< CTIMER AUX2: TMRA2TINV (Bit 12) */ +#define CTIMER_AUX2_TMRA2TINV_Msk (0x1000UL) /*!< CTIMER AUX2: TMRA2TINV (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX2_TMRA2NOSYNC_Pos (11UL) /*!< CTIMER AUX2: TMRA2NOSYNC (Bit 11) */ +#define CTIMER_AUX2_TMRA2NOSYNC_Msk (0x800UL) /*!< CTIMER AUX2: TMRA2NOSYNC (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX2_TMRA2TRIG_Pos (7UL) /*!< CTIMER AUX2: TMRA2TRIG (Bit 7) */ +#define CTIMER_AUX2_TMRA2TRIG_Msk (0x780UL) /*!< CTIMER AUX2: TMRA2TRIG (Bitfield-Mask: 0x0f) */ +#define CTIMER_AUX2_TMRA2LMT_Pos (0UL) /*!< CTIMER AUX2: TMRA2LMT (Bit 0) */ +#define CTIMER_AUX2_TMRA2LMT_Msk (0x7fUL) /*!< CTIMER AUX2: TMRA2LMT (Bitfield-Mask: 0x7f) */ +/* ========================================================= TMR3 ========================================================== */ +#define CTIMER_TMR3_CTTMRB3_Pos (16UL) /*!< CTIMER TMR3: CTTMRB3 (Bit 16) */ +#define CTIMER_TMR3_CTTMRB3_Msk (0xffff0000UL) /*!< CTIMER TMR3: CTTMRB3 (Bitfield-Mask: 0xffff) */ +#define CTIMER_TMR3_CTTMRA3_Pos (0UL) /*!< CTIMER TMR3: CTTMRA3 (Bit 0) */ +#define CTIMER_TMR3_CTTMRA3_Msk (0xffffUL) /*!< CTIMER TMR3: CTTMRA3 (Bitfield-Mask: 0xffff) */ +/* ======================================================== CMPRA3 ========================================================= */ +#define CTIMER_CMPRA3_CMPR1A3_Pos (16UL) /*!< CTIMER CMPRA3: CMPR1A3 (Bit 16) */ +#define CTIMER_CMPRA3_CMPR1A3_Msk (0xffff0000UL) /*!< CTIMER CMPRA3: CMPR1A3 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRA3_CMPR0A3_Pos (0UL) /*!< CTIMER CMPRA3: CMPR0A3 (Bit 0) */ +#define CTIMER_CMPRA3_CMPR0A3_Msk (0xffffUL) /*!< CTIMER CMPRA3: CMPR0A3 (Bitfield-Mask: 0xffff) */ +/* ======================================================== CMPRB3 ========================================================= */ +#define CTIMER_CMPRB3_CMPR1B3_Pos (16UL) /*!< CTIMER CMPRB3: CMPR1B3 (Bit 16) */ +#define CTIMER_CMPRB3_CMPR1B3_Msk (0xffff0000UL) /*!< CTIMER CMPRB3: CMPR1B3 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRB3_CMPR0B3_Pos (0UL) /*!< CTIMER CMPRB3: CMPR0B3 (Bit 0) */ +#define CTIMER_CMPRB3_CMPR0B3_Msk (0xffffUL) /*!< CTIMER CMPRB3: CMPR0B3 (Bitfield-Mask: 0xffff) */ +/* ========================================================= CTRL3 ========================================================= */ +#define CTIMER_CTRL3_CTLINK3_Pos (31UL) /*!< CTIMER CTRL3: CTLINK3 (Bit 31) */ +#define CTIMER_CTRL3_CTLINK3_Msk (0x80000000UL) /*!< CTIMER CTRL3: CTLINK3 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL3_TMRB3POL_Pos (28UL) /*!< CTIMER CTRL3: TMRB3POL (Bit 28) */ +#define CTIMER_CTRL3_TMRB3POL_Msk (0x10000000UL) /*!< CTIMER CTRL3: TMRB3POL (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL3_TMRB3CLR_Pos (27UL) /*!< CTIMER CTRL3: TMRB3CLR (Bit 27) */ +#define CTIMER_CTRL3_TMRB3CLR_Msk (0x8000000UL) /*!< CTIMER CTRL3: TMRB3CLR (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL3_TMRB3IE1_Pos (26UL) /*!< CTIMER CTRL3: TMRB3IE1 (Bit 26) */ +#define CTIMER_CTRL3_TMRB3IE1_Msk (0x4000000UL) /*!< CTIMER CTRL3: TMRB3IE1 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL3_TMRB3IE0_Pos (25UL) /*!< CTIMER CTRL3: TMRB3IE0 (Bit 25) */ +#define CTIMER_CTRL3_TMRB3IE0_Msk (0x2000000UL) /*!< CTIMER CTRL3: TMRB3IE0 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL3_TMRB3FN_Pos (22UL) /*!< CTIMER CTRL3: TMRB3FN (Bit 22) */ +#define CTIMER_CTRL3_TMRB3FN_Msk (0x1c00000UL) /*!< CTIMER CTRL3: TMRB3FN (Bitfield-Mask: 0x07) */ +#define CTIMER_CTRL3_TMRB3CLK_Pos (17UL) /*!< CTIMER CTRL3: TMRB3CLK (Bit 17) */ +#define CTIMER_CTRL3_TMRB3CLK_Msk (0x3e0000UL) /*!< CTIMER CTRL3: TMRB3CLK (Bitfield-Mask: 0x1f) */ +#define CTIMER_CTRL3_TMRB3EN_Pos (16UL) /*!< CTIMER CTRL3: TMRB3EN (Bit 16) */ +#define CTIMER_CTRL3_TMRB3EN_Msk (0x10000UL) /*!< CTIMER CTRL3: TMRB3EN (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL3_ADCEN_Pos (15UL) /*!< CTIMER CTRL3: ADCEN (Bit 15) */ +#define CTIMER_CTRL3_ADCEN_Msk (0x8000UL) /*!< CTIMER CTRL3: ADCEN (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL3_TMRA3POL_Pos (12UL) /*!< CTIMER CTRL3: TMRA3POL (Bit 12) */ +#define CTIMER_CTRL3_TMRA3POL_Msk (0x1000UL) /*!< CTIMER CTRL3: TMRA3POL (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL3_TMRA3CLR_Pos (11UL) /*!< CTIMER CTRL3: TMRA3CLR (Bit 11) */ +#define CTIMER_CTRL3_TMRA3CLR_Msk (0x800UL) /*!< CTIMER CTRL3: TMRA3CLR (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL3_TMRA3IE1_Pos (10UL) /*!< CTIMER CTRL3: TMRA3IE1 (Bit 10) */ +#define CTIMER_CTRL3_TMRA3IE1_Msk (0x400UL) /*!< CTIMER CTRL3: TMRA3IE1 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL3_TMRA3IE0_Pos (9UL) /*!< CTIMER CTRL3: TMRA3IE0 (Bit 9) */ +#define CTIMER_CTRL3_TMRA3IE0_Msk (0x200UL) /*!< CTIMER CTRL3: TMRA3IE0 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL3_TMRA3FN_Pos (6UL) /*!< CTIMER CTRL3: TMRA3FN (Bit 6) */ +#define CTIMER_CTRL3_TMRA3FN_Msk (0x1c0UL) /*!< CTIMER CTRL3: TMRA3FN (Bitfield-Mask: 0x07) */ +#define CTIMER_CTRL3_TMRA3CLK_Pos (1UL) /*!< CTIMER CTRL3: TMRA3CLK (Bit 1) */ +#define CTIMER_CTRL3_TMRA3CLK_Msk (0x3eUL) /*!< CTIMER CTRL3: TMRA3CLK (Bitfield-Mask: 0x1f) */ +#define CTIMER_CTRL3_TMRA3EN_Pos (0UL) /*!< CTIMER CTRL3: TMRA3EN (Bit 0) */ +#define CTIMER_CTRL3_TMRA3EN_Msk (0x1UL) /*!< CTIMER CTRL3: TMRA3EN (Bitfield-Mask: 0x01) */ +/* ======================================================= CMPRAUXA3 ======================================================= */ +#define CTIMER_CMPRAUXA3_CMPR3A3_Pos (16UL) /*!< CTIMER CMPRAUXA3: CMPR3A3 (Bit 16) */ +#define CTIMER_CMPRAUXA3_CMPR3A3_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXA3: CMPR3A3 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRAUXA3_CMPR2A3_Pos (0UL) /*!< CTIMER CMPRAUXA3: CMPR2A3 (Bit 0) */ +#define CTIMER_CMPRAUXA3_CMPR2A3_Msk (0xffffUL) /*!< CTIMER CMPRAUXA3: CMPR2A3 (Bitfield-Mask: 0xffff) */ +/* ======================================================= CMPRAUXB3 ======================================================= */ +#define CTIMER_CMPRAUXB3_CMPR3B3_Pos (16UL) /*!< CTIMER CMPRAUXB3: CMPR3B3 (Bit 16) */ +#define CTIMER_CMPRAUXB3_CMPR3B3_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXB3: CMPR3B3 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRAUXB3_CMPR2B3_Pos (0UL) /*!< CTIMER CMPRAUXB3: CMPR2B3 (Bit 0) */ +#define CTIMER_CMPRAUXB3_CMPR2B3_Msk (0xffffUL) /*!< CTIMER CMPRAUXB3: CMPR2B3 (Bitfield-Mask: 0xffff) */ +/* ========================================================= AUX3 ========================================================== */ +#define CTIMER_AUX3_TMRB3EN23_Pos (30UL) /*!< CTIMER AUX3: TMRB3EN23 (Bit 30) */ +#define CTIMER_AUX3_TMRB3EN23_Msk (0x40000000UL) /*!< CTIMER AUX3: TMRB3EN23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX3_TMRB3POL23_Pos (29UL) /*!< CTIMER AUX3: TMRB3POL23 (Bit 29) */ +#define CTIMER_AUX3_TMRB3POL23_Msk (0x20000000UL) /*!< CTIMER AUX3: TMRB3POL23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX3_TMRB3TINV_Pos (28UL) /*!< CTIMER AUX3: TMRB3TINV (Bit 28) */ +#define CTIMER_AUX3_TMRB3TINV_Msk (0x10000000UL) /*!< CTIMER AUX3: TMRB3TINV (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX3_TMRB3NOSYNC_Pos (27UL) /*!< CTIMER AUX3: TMRB3NOSYNC (Bit 27) */ +#define CTIMER_AUX3_TMRB3NOSYNC_Msk (0x8000000UL) /*!< CTIMER AUX3: TMRB3NOSYNC (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX3_TMRB3TRIG_Pos (23UL) /*!< CTIMER AUX3: TMRB3TRIG (Bit 23) */ +#define CTIMER_AUX3_TMRB3TRIG_Msk (0x7800000UL) /*!< CTIMER AUX3: TMRB3TRIG (Bitfield-Mask: 0x0f) */ +#define CTIMER_AUX3_TMRB3LMT_Pos (16UL) /*!< CTIMER AUX3: TMRB3LMT (Bit 16) */ +#define CTIMER_AUX3_TMRB3LMT_Msk (0x3f0000UL) /*!< CTIMER AUX3: TMRB3LMT (Bitfield-Mask: 0x3f) */ +#define CTIMER_AUX3_TMRA3EN23_Pos (14UL) /*!< CTIMER AUX3: TMRA3EN23 (Bit 14) */ +#define CTIMER_AUX3_TMRA3EN23_Msk (0x4000UL) /*!< CTIMER AUX3: TMRA3EN23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX3_TMRA3POL23_Pos (13UL) /*!< CTIMER AUX3: TMRA3POL23 (Bit 13) */ +#define CTIMER_AUX3_TMRA3POL23_Msk (0x2000UL) /*!< CTIMER AUX3: TMRA3POL23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX3_TMRA3TINV_Pos (12UL) /*!< CTIMER AUX3: TMRA3TINV (Bit 12) */ +#define CTIMER_AUX3_TMRA3TINV_Msk (0x1000UL) /*!< CTIMER AUX3: TMRA3TINV (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX3_TMRA3NOSYNC_Pos (11UL) /*!< CTIMER AUX3: TMRA3NOSYNC (Bit 11) */ +#define CTIMER_AUX3_TMRA3NOSYNC_Msk (0x800UL) /*!< CTIMER AUX3: TMRA3NOSYNC (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX3_TMRA3TRIG_Pos (7UL) /*!< CTIMER AUX3: TMRA3TRIG (Bit 7) */ +#define CTIMER_AUX3_TMRA3TRIG_Msk (0x780UL) /*!< CTIMER AUX3: TMRA3TRIG (Bitfield-Mask: 0x0f) */ +#define CTIMER_AUX3_TMRA3LMT_Pos (0UL) /*!< CTIMER AUX3: TMRA3LMT (Bit 0) */ +#define CTIMER_AUX3_TMRA3LMT_Msk (0x7fUL) /*!< CTIMER AUX3: TMRA3LMT (Bitfield-Mask: 0x7f) */ +/* ========================================================= TMR4 ========================================================== */ +#define CTIMER_TMR4_CTTMRB4_Pos (16UL) /*!< CTIMER TMR4: CTTMRB4 (Bit 16) */ +#define CTIMER_TMR4_CTTMRB4_Msk (0xffff0000UL) /*!< CTIMER TMR4: CTTMRB4 (Bitfield-Mask: 0xffff) */ +#define CTIMER_TMR4_CTTMRA4_Pos (0UL) /*!< CTIMER TMR4: CTTMRA4 (Bit 0) */ +#define CTIMER_TMR4_CTTMRA4_Msk (0xffffUL) /*!< CTIMER TMR4: CTTMRA4 (Bitfield-Mask: 0xffff) */ +/* ======================================================== CMPRA4 ========================================================= */ +#define CTIMER_CMPRA4_CMPR1A4_Pos (16UL) /*!< CTIMER CMPRA4: CMPR1A4 (Bit 16) */ +#define CTIMER_CMPRA4_CMPR1A4_Msk (0xffff0000UL) /*!< CTIMER CMPRA4: CMPR1A4 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRA4_CMPR0A4_Pos (0UL) /*!< CTIMER CMPRA4: CMPR0A4 (Bit 0) */ +#define CTIMER_CMPRA4_CMPR0A4_Msk (0xffffUL) /*!< CTIMER CMPRA4: CMPR0A4 (Bitfield-Mask: 0xffff) */ +/* ======================================================== CMPRB4 ========================================================= */ +#define CTIMER_CMPRB4_CMPR1B4_Pos (16UL) /*!< CTIMER CMPRB4: CMPR1B4 (Bit 16) */ +#define CTIMER_CMPRB4_CMPR1B4_Msk (0xffff0000UL) /*!< CTIMER CMPRB4: CMPR1B4 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRB4_CMPR0B4_Pos (0UL) /*!< CTIMER CMPRB4: CMPR0B4 (Bit 0) */ +#define CTIMER_CMPRB4_CMPR0B4_Msk (0xffffUL) /*!< CTIMER CMPRB4: CMPR0B4 (Bitfield-Mask: 0xffff) */ +/* ========================================================= CTRL4 ========================================================= */ +#define CTIMER_CTRL4_CTLINK4_Pos (31UL) /*!< CTIMER CTRL4: CTLINK4 (Bit 31) */ +#define CTIMER_CTRL4_CTLINK4_Msk (0x80000000UL) /*!< CTIMER CTRL4: CTLINK4 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL4_TMRB4POL_Pos (28UL) /*!< CTIMER CTRL4: TMRB4POL (Bit 28) */ +#define CTIMER_CTRL4_TMRB4POL_Msk (0x10000000UL) /*!< CTIMER CTRL4: TMRB4POL (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL4_TMRB4CLR_Pos (27UL) /*!< CTIMER CTRL4: TMRB4CLR (Bit 27) */ +#define CTIMER_CTRL4_TMRB4CLR_Msk (0x8000000UL) /*!< CTIMER CTRL4: TMRB4CLR (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL4_TMRB4IE1_Pos (26UL) /*!< CTIMER CTRL4: TMRB4IE1 (Bit 26) */ +#define CTIMER_CTRL4_TMRB4IE1_Msk (0x4000000UL) /*!< CTIMER CTRL4: TMRB4IE1 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL4_TMRB4IE0_Pos (25UL) /*!< CTIMER CTRL4: TMRB4IE0 (Bit 25) */ +#define CTIMER_CTRL4_TMRB4IE0_Msk (0x2000000UL) /*!< CTIMER CTRL4: TMRB4IE0 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL4_TMRB4FN_Pos (22UL) /*!< CTIMER CTRL4: TMRB4FN (Bit 22) */ +#define CTIMER_CTRL4_TMRB4FN_Msk (0x1c00000UL) /*!< CTIMER CTRL4: TMRB4FN (Bitfield-Mask: 0x07) */ +#define CTIMER_CTRL4_TMRB4CLK_Pos (17UL) /*!< CTIMER CTRL4: TMRB4CLK (Bit 17) */ +#define CTIMER_CTRL4_TMRB4CLK_Msk (0x3e0000UL) /*!< CTIMER CTRL4: TMRB4CLK (Bitfield-Mask: 0x1f) */ +#define CTIMER_CTRL4_TMRB4EN_Pos (16UL) /*!< CTIMER CTRL4: TMRB4EN (Bit 16) */ +#define CTIMER_CTRL4_TMRB4EN_Msk (0x10000UL) /*!< CTIMER CTRL4: TMRB4EN (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL4_TMRA4POL_Pos (12UL) /*!< CTIMER CTRL4: TMRA4POL (Bit 12) */ +#define CTIMER_CTRL4_TMRA4POL_Msk (0x1000UL) /*!< CTIMER CTRL4: TMRA4POL (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL4_TMRA4CLR_Pos (11UL) /*!< CTIMER CTRL4: TMRA4CLR (Bit 11) */ +#define CTIMER_CTRL4_TMRA4CLR_Msk (0x800UL) /*!< CTIMER CTRL4: TMRA4CLR (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL4_TMRA4IE1_Pos (10UL) /*!< CTIMER CTRL4: TMRA4IE1 (Bit 10) */ +#define CTIMER_CTRL4_TMRA4IE1_Msk (0x400UL) /*!< CTIMER CTRL4: TMRA4IE1 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL4_TMRA4IE0_Pos (9UL) /*!< CTIMER CTRL4: TMRA4IE0 (Bit 9) */ +#define CTIMER_CTRL4_TMRA4IE0_Msk (0x200UL) /*!< CTIMER CTRL4: TMRA4IE0 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL4_TMRA4FN_Pos (6UL) /*!< CTIMER CTRL4: TMRA4FN (Bit 6) */ +#define CTIMER_CTRL4_TMRA4FN_Msk (0x1c0UL) /*!< CTIMER CTRL4: TMRA4FN (Bitfield-Mask: 0x07) */ +#define CTIMER_CTRL4_TMRA4CLK_Pos (1UL) /*!< CTIMER CTRL4: TMRA4CLK (Bit 1) */ +#define CTIMER_CTRL4_TMRA4CLK_Msk (0x3eUL) /*!< CTIMER CTRL4: TMRA4CLK (Bitfield-Mask: 0x1f) */ +#define CTIMER_CTRL4_TMRA4EN_Pos (0UL) /*!< CTIMER CTRL4: TMRA4EN (Bit 0) */ +#define CTIMER_CTRL4_TMRA4EN_Msk (0x1UL) /*!< CTIMER CTRL4: TMRA4EN (Bitfield-Mask: 0x01) */ +/* ======================================================= CMPRAUXA4 ======================================================= */ +#define CTIMER_CMPRAUXA4_CMPR3A4_Pos (16UL) /*!< CTIMER CMPRAUXA4: CMPR3A4 (Bit 16) */ +#define CTIMER_CMPRAUXA4_CMPR3A4_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXA4: CMPR3A4 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRAUXA4_CMPR2A4_Pos (0UL) /*!< CTIMER CMPRAUXA4: CMPR2A4 (Bit 0) */ +#define CTIMER_CMPRAUXA4_CMPR2A4_Msk (0xffffUL) /*!< CTIMER CMPRAUXA4: CMPR2A4 (Bitfield-Mask: 0xffff) */ +/* ======================================================= CMPRAUXB4 ======================================================= */ +#define CTIMER_CMPRAUXB4_CMPR3B4_Pos (16UL) /*!< CTIMER CMPRAUXB4: CMPR3B4 (Bit 16) */ +#define CTIMER_CMPRAUXB4_CMPR3B4_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXB4: CMPR3B4 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRAUXB4_CMPR2B4_Pos (0UL) /*!< CTIMER CMPRAUXB4: CMPR2B4 (Bit 0) */ +#define CTIMER_CMPRAUXB4_CMPR2B4_Msk (0xffffUL) /*!< CTIMER CMPRAUXB4: CMPR2B4 (Bitfield-Mask: 0xffff) */ +/* ========================================================= AUX4 ========================================================== */ +#define CTIMER_AUX4_TMRB4EN23_Pos (30UL) /*!< CTIMER AUX4: TMRB4EN23 (Bit 30) */ +#define CTIMER_AUX4_TMRB4EN23_Msk (0x40000000UL) /*!< CTIMER AUX4: TMRB4EN23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX4_TMRB4POL23_Pos (29UL) /*!< CTIMER AUX4: TMRB4POL23 (Bit 29) */ +#define CTIMER_AUX4_TMRB4POL23_Msk (0x20000000UL) /*!< CTIMER AUX4: TMRB4POL23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX4_TMRB4TINV_Pos (28UL) /*!< CTIMER AUX4: TMRB4TINV (Bit 28) */ +#define CTIMER_AUX4_TMRB4TINV_Msk (0x10000000UL) /*!< CTIMER AUX4: TMRB4TINV (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX4_TMRB4NOSYNC_Pos (27UL) /*!< CTIMER AUX4: TMRB4NOSYNC (Bit 27) */ +#define CTIMER_AUX4_TMRB4NOSYNC_Msk (0x8000000UL) /*!< CTIMER AUX4: TMRB4NOSYNC (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX4_TMRB4TRIG_Pos (23UL) /*!< CTIMER AUX4: TMRB4TRIG (Bit 23) */ +#define CTIMER_AUX4_TMRB4TRIG_Msk (0x7800000UL) /*!< CTIMER AUX4: TMRB4TRIG (Bitfield-Mask: 0x0f) */ +#define CTIMER_AUX4_TMRB4LMT_Pos (16UL) /*!< CTIMER AUX4: TMRB4LMT (Bit 16) */ +#define CTIMER_AUX4_TMRB4LMT_Msk (0x3f0000UL) /*!< CTIMER AUX4: TMRB4LMT (Bitfield-Mask: 0x3f) */ +#define CTIMER_AUX4_TMRA4EN23_Pos (14UL) /*!< CTIMER AUX4: TMRA4EN23 (Bit 14) */ +#define CTIMER_AUX4_TMRA4EN23_Msk (0x4000UL) /*!< CTIMER AUX4: TMRA4EN23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX4_TMRA4POL23_Pos (13UL) /*!< CTIMER AUX4: TMRA4POL23 (Bit 13) */ +#define CTIMER_AUX4_TMRA4POL23_Msk (0x2000UL) /*!< CTIMER AUX4: TMRA4POL23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX4_TMRA4TINV_Pos (12UL) /*!< CTIMER AUX4: TMRA4TINV (Bit 12) */ +#define CTIMER_AUX4_TMRA4TINV_Msk (0x1000UL) /*!< CTIMER AUX4: TMRA4TINV (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX4_TMRA4NOSYNC_Pos (11UL) /*!< CTIMER AUX4: TMRA4NOSYNC (Bit 11) */ +#define CTIMER_AUX4_TMRA4NOSYNC_Msk (0x800UL) /*!< CTIMER AUX4: TMRA4NOSYNC (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX4_TMRA4TRIG_Pos (7UL) /*!< CTIMER AUX4: TMRA4TRIG (Bit 7) */ +#define CTIMER_AUX4_TMRA4TRIG_Msk (0x780UL) /*!< CTIMER AUX4: TMRA4TRIG (Bitfield-Mask: 0x0f) */ +#define CTIMER_AUX4_TMRA4LMT_Pos (0UL) /*!< CTIMER AUX4: TMRA4LMT (Bit 0) */ +#define CTIMER_AUX4_TMRA4LMT_Msk (0x7fUL) /*!< CTIMER AUX4: TMRA4LMT (Bitfield-Mask: 0x7f) */ +/* ========================================================= TMR5 ========================================================== */ +#define CTIMER_TMR5_CTTMRB5_Pos (16UL) /*!< CTIMER TMR5: CTTMRB5 (Bit 16) */ +#define CTIMER_TMR5_CTTMRB5_Msk (0xffff0000UL) /*!< CTIMER TMR5: CTTMRB5 (Bitfield-Mask: 0xffff) */ +#define CTIMER_TMR5_CTTMRA5_Pos (0UL) /*!< CTIMER TMR5: CTTMRA5 (Bit 0) */ +#define CTIMER_TMR5_CTTMRA5_Msk (0xffffUL) /*!< CTIMER TMR5: CTTMRA5 (Bitfield-Mask: 0xffff) */ +/* ======================================================== CMPRA5 ========================================================= */ +#define CTIMER_CMPRA5_CMPR1A5_Pos (16UL) /*!< CTIMER CMPRA5: CMPR1A5 (Bit 16) */ +#define CTIMER_CMPRA5_CMPR1A5_Msk (0xffff0000UL) /*!< CTIMER CMPRA5: CMPR1A5 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRA5_CMPR0A5_Pos (0UL) /*!< CTIMER CMPRA5: CMPR0A5 (Bit 0) */ +#define CTIMER_CMPRA5_CMPR0A5_Msk (0xffffUL) /*!< CTIMER CMPRA5: CMPR0A5 (Bitfield-Mask: 0xffff) */ +/* ======================================================== CMPRB5 ========================================================= */ +#define CTIMER_CMPRB5_CMPR1B5_Pos (16UL) /*!< CTIMER CMPRB5: CMPR1B5 (Bit 16) */ +#define CTIMER_CMPRB5_CMPR1B5_Msk (0xffff0000UL) /*!< CTIMER CMPRB5: CMPR1B5 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRB5_CMPR0B5_Pos (0UL) /*!< CTIMER CMPRB5: CMPR0B5 (Bit 0) */ +#define CTIMER_CMPRB5_CMPR0B5_Msk (0xffffUL) /*!< CTIMER CMPRB5: CMPR0B5 (Bitfield-Mask: 0xffff) */ +/* ========================================================= CTRL5 ========================================================= */ +#define CTIMER_CTRL5_CTLINK5_Pos (31UL) /*!< CTIMER CTRL5: CTLINK5 (Bit 31) */ +#define CTIMER_CTRL5_CTLINK5_Msk (0x80000000UL) /*!< CTIMER CTRL5: CTLINK5 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL5_TMRB5POL_Pos (28UL) /*!< CTIMER CTRL5: TMRB5POL (Bit 28) */ +#define CTIMER_CTRL5_TMRB5POL_Msk (0x10000000UL) /*!< CTIMER CTRL5: TMRB5POL (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL5_TMRB5CLR_Pos (27UL) /*!< CTIMER CTRL5: TMRB5CLR (Bit 27) */ +#define CTIMER_CTRL5_TMRB5CLR_Msk (0x8000000UL) /*!< CTIMER CTRL5: TMRB5CLR (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL5_TMRB5IE1_Pos (26UL) /*!< CTIMER CTRL5: TMRB5IE1 (Bit 26) */ +#define CTIMER_CTRL5_TMRB5IE1_Msk (0x4000000UL) /*!< CTIMER CTRL5: TMRB5IE1 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL5_TMRB5IE0_Pos (25UL) /*!< CTIMER CTRL5: TMRB5IE0 (Bit 25) */ +#define CTIMER_CTRL5_TMRB5IE0_Msk (0x2000000UL) /*!< CTIMER CTRL5: TMRB5IE0 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL5_TMRB5FN_Pos (22UL) /*!< CTIMER CTRL5: TMRB5FN (Bit 22) */ +#define CTIMER_CTRL5_TMRB5FN_Msk (0x1c00000UL) /*!< CTIMER CTRL5: TMRB5FN (Bitfield-Mask: 0x07) */ +#define CTIMER_CTRL5_TMRB5CLK_Pos (17UL) /*!< CTIMER CTRL5: TMRB5CLK (Bit 17) */ +#define CTIMER_CTRL5_TMRB5CLK_Msk (0x3e0000UL) /*!< CTIMER CTRL5: TMRB5CLK (Bitfield-Mask: 0x1f) */ +#define CTIMER_CTRL5_TMRB5EN_Pos (16UL) /*!< CTIMER CTRL5: TMRB5EN (Bit 16) */ +#define CTIMER_CTRL5_TMRB5EN_Msk (0x10000UL) /*!< CTIMER CTRL5: TMRB5EN (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL5_TMRA5POL_Pos (12UL) /*!< CTIMER CTRL5: TMRA5POL (Bit 12) */ +#define CTIMER_CTRL5_TMRA5POL_Msk (0x1000UL) /*!< CTIMER CTRL5: TMRA5POL (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL5_TMRA5CLR_Pos (11UL) /*!< CTIMER CTRL5: TMRA5CLR (Bit 11) */ +#define CTIMER_CTRL5_TMRA5CLR_Msk (0x800UL) /*!< CTIMER CTRL5: TMRA5CLR (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL5_TMRA5IE1_Pos (10UL) /*!< CTIMER CTRL5: TMRA5IE1 (Bit 10) */ +#define CTIMER_CTRL5_TMRA5IE1_Msk (0x400UL) /*!< CTIMER CTRL5: TMRA5IE1 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL5_TMRA5IE0_Pos (9UL) /*!< CTIMER CTRL5: TMRA5IE0 (Bit 9) */ +#define CTIMER_CTRL5_TMRA5IE0_Msk (0x200UL) /*!< CTIMER CTRL5: TMRA5IE0 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL5_TMRA5FN_Pos (6UL) /*!< CTIMER CTRL5: TMRA5FN (Bit 6) */ +#define CTIMER_CTRL5_TMRA5FN_Msk (0x1c0UL) /*!< CTIMER CTRL5: TMRA5FN (Bitfield-Mask: 0x07) */ +#define CTIMER_CTRL5_TMRA5CLK_Pos (1UL) /*!< CTIMER CTRL5: TMRA5CLK (Bit 1) */ +#define CTIMER_CTRL5_TMRA5CLK_Msk (0x3eUL) /*!< CTIMER CTRL5: TMRA5CLK (Bitfield-Mask: 0x1f) */ +#define CTIMER_CTRL5_TMRA5EN_Pos (0UL) /*!< CTIMER CTRL5: TMRA5EN (Bit 0) */ +#define CTIMER_CTRL5_TMRA5EN_Msk (0x1UL) /*!< CTIMER CTRL5: TMRA5EN (Bitfield-Mask: 0x01) */ +/* ======================================================= CMPRAUXA5 ======================================================= */ +#define CTIMER_CMPRAUXA5_CMPR3A5_Pos (16UL) /*!< CTIMER CMPRAUXA5: CMPR3A5 (Bit 16) */ +#define CTIMER_CMPRAUXA5_CMPR3A5_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXA5: CMPR3A5 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRAUXA5_CMPR2A5_Pos (0UL) /*!< CTIMER CMPRAUXA5: CMPR2A5 (Bit 0) */ +#define CTIMER_CMPRAUXA5_CMPR2A5_Msk (0xffffUL) /*!< CTIMER CMPRAUXA5: CMPR2A5 (Bitfield-Mask: 0xffff) */ +/* ======================================================= CMPRAUXB5 ======================================================= */ +#define CTIMER_CMPRAUXB5_CMPR3B5_Pos (16UL) /*!< CTIMER CMPRAUXB5: CMPR3B5 (Bit 16) */ +#define CTIMER_CMPRAUXB5_CMPR3B5_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXB5: CMPR3B5 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRAUXB5_CMPR2B5_Pos (0UL) /*!< CTIMER CMPRAUXB5: CMPR2B5 (Bit 0) */ +#define CTIMER_CMPRAUXB5_CMPR2B5_Msk (0xffffUL) /*!< CTIMER CMPRAUXB5: CMPR2B5 (Bitfield-Mask: 0xffff) */ +/* ========================================================= AUX5 ========================================================== */ +#define CTIMER_AUX5_TMRB5EN23_Pos (30UL) /*!< CTIMER AUX5: TMRB5EN23 (Bit 30) */ +#define CTIMER_AUX5_TMRB5EN23_Msk (0x40000000UL) /*!< CTIMER AUX5: TMRB5EN23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX5_TMRB5POL23_Pos (29UL) /*!< CTIMER AUX5: TMRB5POL23 (Bit 29) */ +#define CTIMER_AUX5_TMRB5POL23_Msk (0x20000000UL) /*!< CTIMER AUX5: TMRB5POL23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX5_TMRB5TINV_Pos (28UL) /*!< CTIMER AUX5: TMRB5TINV (Bit 28) */ +#define CTIMER_AUX5_TMRB5TINV_Msk (0x10000000UL) /*!< CTIMER AUX5: TMRB5TINV (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX5_TMRB5NOSYNC_Pos (27UL) /*!< CTIMER AUX5: TMRB5NOSYNC (Bit 27) */ +#define CTIMER_AUX5_TMRB5NOSYNC_Msk (0x8000000UL) /*!< CTIMER AUX5: TMRB5NOSYNC (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX5_TMRB5TRIG_Pos (23UL) /*!< CTIMER AUX5: TMRB5TRIG (Bit 23) */ +#define CTIMER_AUX5_TMRB5TRIG_Msk (0x7800000UL) /*!< CTIMER AUX5: TMRB5TRIG (Bitfield-Mask: 0x0f) */ +#define CTIMER_AUX5_TMRB5LMT_Pos (16UL) /*!< CTIMER AUX5: TMRB5LMT (Bit 16) */ +#define CTIMER_AUX5_TMRB5LMT_Msk (0x3f0000UL) /*!< CTIMER AUX5: TMRB5LMT (Bitfield-Mask: 0x3f) */ +#define CTIMER_AUX5_TMRA5EN23_Pos (14UL) /*!< CTIMER AUX5: TMRA5EN23 (Bit 14) */ +#define CTIMER_AUX5_TMRA5EN23_Msk (0x4000UL) /*!< CTIMER AUX5: TMRA5EN23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX5_TMRA5POL23_Pos (13UL) /*!< CTIMER AUX5: TMRA5POL23 (Bit 13) */ +#define CTIMER_AUX5_TMRA5POL23_Msk (0x2000UL) /*!< CTIMER AUX5: TMRA5POL23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX5_TMRA5TINV_Pos (12UL) /*!< CTIMER AUX5: TMRA5TINV (Bit 12) */ +#define CTIMER_AUX5_TMRA5TINV_Msk (0x1000UL) /*!< CTIMER AUX5: TMRA5TINV (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX5_TMRA5NOSYNC_Pos (11UL) /*!< CTIMER AUX5: TMRA5NOSYNC (Bit 11) */ +#define CTIMER_AUX5_TMRA5NOSYNC_Msk (0x800UL) /*!< CTIMER AUX5: TMRA5NOSYNC (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX5_TMRA5TRIG_Pos (7UL) /*!< CTIMER AUX5: TMRA5TRIG (Bit 7) */ +#define CTIMER_AUX5_TMRA5TRIG_Msk (0x780UL) /*!< CTIMER AUX5: TMRA5TRIG (Bitfield-Mask: 0x0f) */ +#define CTIMER_AUX5_TMRA5LMT_Pos (0UL) /*!< CTIMER AUX5: TMRA5LMT (Bit 0) */ +#define CTIMER_AUX5_TMRA5LMT_Msk (0x7fUL) /*!< CTIMER AUX5: TMRA5LMT (Bitfield-Mask: 0x7f) */ +/* ========================================================= TMR6 ========================================================== */ +#define CTIMER_TMR6_CTTMRB6_Pos (16UL) /*!< CTIMER TMR6: CTTMRB6 (Bit 16) */ +#define CTIMER_TMR6_CTTMRB6_Msk (0xffff0000UL) /*!< CTIMER TMR6: CTTMRB6 (Bitfield-Mask: 0xffff) */ +#define CTIMER_TMR6_CTTMRA6_Pos (0UL) /*!< CTIMER TMR6: CTTMRA6 (Bit 0) */ +#define CTIMER_TMR6_CTTMRA6_Msk (0xffffUL) /*!< CTIMER TMR6: CTTMRA6 (Bitfield-Mask: 0xffff) */ +/* ======================================================== CMPRA6 ========================================================= */ +#define CTIMER_CMPRA6_CMPR1A6_Pos (16UL) /*!< CTIMER CMPRA6: CMPR1A6 (Bit 16) */ +#define CTIMER_CMPRA6_CMPR1A6_Msk (0xffff0000UL) /*!< CTIMER CMPRA6: CMPR1A6 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRA6_CMPR0A6_Pos (0UL) /*!< CTIMER CMPRA6: CMPR0A6 (Bit 0) */ +#define CTIMER_CMPRA6_CMPR0A6_Msk (0xffffUL) /*!< CTIMER CMPRA6: CMPR0A6 (Bitfield-Mask: 0xffff) */ +/* ======================================================== CMPRB6 ========================================================= */ +#define CTIMER_CMPRB6_CMPR1B6_Pos (16UL) /*!< CTIMER CMPRB6: CMPR1B6 (Bit 16) */ +#define CTIMER_CMPRB6_CMPR1B6_Msk (0xffff0000UL) /*!< CTIMER CMPRB6: CMPR1B6 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRB6_CMPR0B6_Pos (0UL) /*!< CTIMER CMPRB6: CMPR0B6 (Bit 0) */ +#define CTIMER_CMPRB6_CMPR0B6_Msk (0xffffUL) /*!< CTIMER CMPRB6: CMPR0B6 (Bitfield-Mask: 0xffff) */ +/* ========================================================= CTRL6 ========================================================= */ +#define CTIMER_CTRL6_CTLINK6_Pos (31UL) /*!< CTIMER CTRL6: CTLINK6 (Bit 31) */ +#define CTIMER_CTRL6_CTLINK6_Msk (0x80000000UL) /*!< CTIMER CTRL6: CTLINK6 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL6_TMRB6POL_Pos (28UL) /*!< CTIMER CTRL6: TMRB6POL (Bit 28) */ +#define CTIMER_CTRL6_TMRB6POL_Msk (0x10000000UL) /*!< CTIMER CTRL6: TMRB6POL (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL6_TMRB6CLR_Pos (27UL) /*!< CTIMER CTRL6: TMRB6CLR (Bit 27) */ +#define CTIMER_CTRL6_TMRB6CLR_Msk (0x8000000UL) /*!< CTIMER CTRL6: TMRB6CLR (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL6_TMRB6IE1_Pos (26UL) /*!< CTIMER CTRL6: TMRB6IE1 (Bit 26) */ +#define CTIMER_CTRL6_TMRB6IE1_Msk (0x4000000UL) /*!< CTIMER CTRL6: TMRB6IE1 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL6_TMRB6IE0_Pos (25UL) /*!< CTIMER CTRL6: TMRB6IE0 (Bit 25) */ +#define CTIMER_CTRL6_TMRB6IE0_Msk (0x2000000UL) /*!< CTIMER CTRL6: TMRB6IE0 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL6_TMRB6FN_Pos (22UL) /*!< CTIMER CTRL6: TMRB6FN (Bit 22) */ +#define CTIMER_CTRL6_TMRB6FN_Msk (0x1c00000UL) /*!< CTIMER CTRL6: TMRB6FN (Bitfield-Mask: 0x07) */ +#define CTIMER_CTRL6_TMRB6CLK_Pos (17UL) /*!< CTIMER CTRL6: TMRB6CLK (Bit 17) */ +#define CTIMER_CTRL6_TMRB6CLK_Msk (0x3e0000UL) /*!< CTIMER CTRL6: TMRB6CLK (Bitfield-Mask: 0x1f) */ +#define CTIMER_CTRL6_TMRB6EN_Pos (16UL) /*!< CTIMER CTRL6: TMRB6EN (Bit 16) */ +#define CTIMER_CTRL6_TMRB6EN_Msk (0x10000UL) /*!< CTIMER CTRL6: TMRB6EN (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL6_TMRA6POL_Pos (12UL) /*!< CTIMER CTRL6: TMRA6POL (Bit 12) */ +#define CTIMER_CTRL6_TMRA6POL_Msk (0x1000UL) /*!< CTIMER CTRL6: TMRA6POL (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL6_TMRA6CLR_Pos (11UL) /*!< CTIMER CTRL6: TMRA6CLR (Bit 11) */ +#define CTIMER_CTRL6_TMRA6CLR_Msk (0x800UL) /*!< CTIMER CTRL6: TMRA6CLR (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL6_TMRA6IE1_Pos (10UL) /*!< CTIMER CTRL6: TMRA6IE1 (Bit 10) */ +#define CTIMER_CTRL6_TMRA6IE1_Msk (0x400UL) /*!< CTIMER CTRL6: TMRA6IE1 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL6_TMRA6IE0_Pos (9UL) /*!< CTIMER CTRL6: TMRA6IE0 (Bit 9) */ +#define CTIMER_CTRL6_TMRA6IE0_Msk (0x200UL) /*!< CTIMER CTRL6: TMRA6IE0 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL6_TMRA6FN_Pos (6UL) /*!< CTIMER CTRL6: TMRA6FN (Bit 6) */ +#define CTIMER_CTRL6_TMRA6FN_Msk (0x1c0UL) /*!< CTIMER CTRL6: TMRA6FN (Bitfield-Mask: 0x07) */ +#define CTIMER_CTRL6_TMRA6CLK_Pos (1UL) /*!< CTIMER CTRL6: TMRA6CLK (Bit 1) */ +#define CTIMER_CTRL6_TMRA6CLK_Msk (0x3eUL) /*!< CTIMER CTRL6: TMRA6CLK (Bitfield-Mask: 0x1f) */ +#define CTIMER_CTRL6_TMRA6EN_Pos (0UL) /*!< CTIMER CTRL6: TMRA6EN (Bit 0) */ +#define CTIMER_CTRL6_TMRA6EN_Msk (0x1UL) /*!< CTIMER CTRL6: TMRA6EN (Bitfield-Mask: 0x01) */ +/* ======================================================= CMPRAUXA6 ======================================================= */ +#define CTIMER_CMPRAUXA6_CMPR3A6_Pos (16UL) /*!< CTIMER CMPRAUXA6: CMPR3A6 (Bit 16) */ +#define CTIMER_CMPRAUXA6_CMPR3A6_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXA6: CMPR3A6 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRAUXA6_CMPR2A6_Pos (0UL) /*!< CTIMER CMPRAUXA6: CMPR2A6 (Bit 0) */ +#define CTIMER_CMPRAUXA6_CMPR2A6_Msk (0xffffUL) /*!< CTIMER CMPRAUXA6: CMPR2A6 (Bitfield-Mask: 0xffff) */ +/* ======================================================= CMPRAUXB6 ======================================================= */ +#define CTIMER_CMPRAUXB6_CMPR3B6_Pos (16UL) /*!< CTIMER CMPRAUXB6: CMPR3B6 (Bit 16) */ +#define CTIMER_CMPRAUXB6_CMPR3B6_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXB6: CMPR3B6 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRAUXB6_CMPR2B6_Pos (0UL) /*!< CTIMER CMPRAUXB6: CMPR2B6 (Bit 0) */ +#define CTIMER_CMPRAUXB6_CMPR2B6_Msk (0xffffUL) /*!< CTIMER CMPRAUXB6: CMPR2B6 (Bitfield-Mask: 0xffff) */ +/* ========================================================= AUX6 ========================================================== */ +#define CTIMER_AUX6_TMRB6EN23_Pos (30UL) /*!< CTIMER AUX6: TMRB6EN23 (Bit 30) */ +#define CTIMER_AUX6_TMRB6EN23_Msk (0x40000000UL) /*!< CTIMER AUX6: TMRB6EN23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX6_TMRB6POL23_Pos (29UL) /*!< CTIMER AUX6: TMRB6POL23 (Bit 29) */ +#define CTIMER_AUX6_TMRB6POL23_Msk (0x20000000UL) /*!< CTIMER AUX6: TMRB6POL23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX6_TMRB6TINV_Pos (28UL) /*!< CTIMER AUX6: TMRB6TINV (Bit 28) */ +#define CTIMER_AUX6_TMRB6TINV_Msk (0x10000000UL) /*!< CTIMER AUX6: TMRB6TINV (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX6_TMRB6NOSYNC_Pos (27UL) /*!< CTIMER AUX6: TMRB6NOSYNC (Bit 27) */ +#define CTIMER_AUX6_TMRB6NOSYNC_Msk (0x8000000UL) /*!< CTIMER AUX6: TMRB6NOSYNC (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX6_TMRB6TRIG_Pos (23UL) /*!< CTIMER AUX6: TMRB6TRIG (Bit 23) */ +#define CTIMER_AUX6_TMRB6TRIG_Msk (0x7800000UL) /*!< CTIMER AUX6: TMRB6TRIG (Bitfield-Mask: 0x0f) */ +#define CTIMER_AUX6_TMRB6LMT_Pos (16UL) /*!< CTIMER AUX6: TMRB6LMT (Bit 16) */ +#define CTIMER_AUX6_TMRB6LMT_Msk (0x3f0000UL) /*!< CTIMER AUX6: TMRB6LMT (Bitfield-Mask: 0x3f) */ +#define CTIMER_AUX6_TMRA6EN23_Pos (14UL) /*!< CTIMER AUX6: TMRA6EN23 (Bit 14) */ +#define CTIMER_AUX6_TMRA6EN23_Msk (0x4000UL) /*!< CTIMER AUX6: TMRA6EN23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX6_TMRA6POL23_Pos (13UL) /*!< CTIMER AUX6: TMRA6POL23 (Bit 13) */ +#define CTIMER_AUX6_TMRA6POL23_Msk (0x2000UL) /*!< CTIMER AUX6: TMRA6POL23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX6_TMRA6TINV_Pos (12UL) /*!< CTIMER AUX6: TMRA6TINV (Bit 12) */ +#define CTIMER_AUX6_TMRA6TINV_Msk (0x1000UL) /*!< CTIMER AUX6: TMRA6TINV (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX6_TMRA6NOSYNC_Pos (11UL) /*!< CTIMER AUX6: TMRA6NOSYNC (Bit 11) */ +#define CTIMER_AUX6_TMRA6NOSYNC_Msk (0x800UL) /*!< CTIMER AUX6: TMRA6NOSYNC (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX6_TMRA6TRIG_Pos (7UL) /*!< CTIMER AUX6: TMRA6TRIG (Bit 7) */ +#define CTIMER_AUX6_TMRA6TRIG_Msk (0x780UL) /*!< CTIMER AUX6: TMRA6TRIG (Bitfield-Mask: 0x0f) */ +#define CTIMER_AUX6_TMRA6LMT_Pos (0UL) /*!< CTIMER AUX6: TMRA6LMT (Bit 0) */ +#define CTIMER_AUX6_TMRA6LMT_Msk (0x7fUL) /*!< CTIMER AUX6: TMRA6LMT (Bitfield-Mask: 0x7f) */ +/* ========================================================= TMR7 ========================================================== */ +#define CTIMER_TMR7_CTTMRB7_Pos (16UL) /*!< CTIMER TMR7: CTTMRB7 (Bit 16) */ +#define CTIMER_TMR7_CTTMRB7_Msk (0xffff0000UL) /*!< CTIMER TMR7: CTTMRB7 (Bitfield-Mask: 0xffff) */ +#define CTIMER_TMR7_CTTMRA7_Pos (0UL) /*!< CTIMER TMR7: CTTMRA7 (Bit 0) */ +#define CTIMER_TMR7_CTTMRA7_Msk (0xffffUL) /*!< CTIMER TMR7: CTTMRA7 (Bitfield-Mask: 0xffff) */ +/* ======================================================== CMPRA7 ========================================================= */ +#define CTIMER_CMPRA7_CMPR1A7_Pos (16UL) /*!< CTIMER CMPRA7: CMPR1A7 (Bit 16) */ +#define CTIMER_CMPRA7_CMPR1A7_Msk (0xffff0000UL) /*!< CTIMER CMPRA7: CMPR1A7 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRA7_CMPR0A7_Pos (0UL) /*!< CTIMER CMPRA7: CMPR0A7 (Bit 0) */ +#define CTIMER_CMPRA7_CMPR0A7_Msk (0xffffUL) /*!< CTIMER CMPRA7: CMPR0A7 (Bitfield-Mask: 0xffff) */ +/* ======================================================== CMPRB7 ========================================================= */ +#define CTIMER_CMPRB7_CMPR1B7_Pos (16UL) /*!< CTIMER CMPRB7: CMPR1B7 (Bit 16) */ +#define CTIMER_CMPRB7_CMPR1B7_Msk (0xffff0000UL) /*!< CTIMER CMPRB7: CMPR1B7 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRB7_CMPR0B7_Pos (0UL) /*!< CTIMER CMPRB7: CMPR0B7 (Bit 0) */ +#define CTIMER_CMPRB7_CMPR0B7_Msk (0xffffUL) /*!< CTIMER CMPRB7: CMPR0B7 (Bitfield-Mask: 0xffff) */ +/* ========================================================= CTRL7 ========================================================= */ +#define CTIMER_CTRL7_CTLINK7_Pos (31UL) /*!< CTIMER CTRL7: CTLINK7 (Bit 31) */ +#define CTIMER_CTRL7_CTLINK7_Msk (0x80000000UL) /*!< CTIMER CTRL7: CTLINK7 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL7_TMRB7POL_Pos (28UL) /*!< CTIMER CTRL7: TMRB7POL (Bit 28) */ +#define CTIMER_CTRL7_TMRB7POL_Msk (0x10000000UL) /*!< CTIMER CTRL7: TMRB7POL (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL7_TMRB7CLR_Pos (27UL) /*!< CTIMER CTRL7: TMRB7CLR (Bit 27) */ +#define CTIMER_CTRL7_TMRB7CLR_Msk (0x8000000UL) /*!< CTIMER CTRL7: TMRB7CLR (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL7_TMRB7IE1_Pos (26UL) /*!< CTIMER CTRL7: TMRB7IE1 (Bit 26) */ +#define CTIMER_CTRL7_TMRB7IE1_Msk (0x4000000UL) /*!< CTIMER CTRL7: TMRB7IE1 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL7_TMRB7IE0_Pos (25UL) /*!< CTIMER CTRL7: TMRB7IE0 (Bit 25) */ +#define CTIMER_CTRL7_TMRB7IE0_Msk (0x2000000UL) /*!< CTIMER CTRL7: TMRB7IE0 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL7_TMRB7FN_Pos (22UL) /*!< CTIMER CTRL7: TMRB7FN (Bit 22) */ +#define CTIMER_CTRL7_TMRB7FN_Msk (0x1c00000UL) /*!< CTIMER CTRL7: TMRB7FN (Bitfield-Mask: 0x07) */ +#define CTIMER_CTRL7_TMRB7CLK_Pos (17UL) /*!< CTIMER CTRL7: TMRB7CLK (Bit 17) */ +#define CTIMER_CTRL7_TMRB7CLK_Msk (0x3e0000UL) /*!< CTIMER CTRL7: TMRB7CLK (Bitfield-Mask: 0x1f) */ +#define CTIMER_CTRL7_TMRB7EN_Pos (16UL) /*!< CTIMER CTRL7: TMRB7EN (Bit 16) */ +#define CTIMER_CTRL7_TMRB7EN_Msk (0x10000UL) /*!< CTIMER CTRL7: TMRB7EN (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL7_TMRA7POL_Pos (12UL) /*!< CTIMER CTRL7: TMRA7POL (Bit 12) */ +#define CTIMER_CTRL7_TMRA7POL_Msk (0x1000UL) /*!< CTIMER CTRL7: TMRA7POL (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL7_TMRA7CLR_Pos (11UL) /*!< CTIMER CTRL7: TMRA7CLR (Bit 11) */ +#define CTIMER_CTRL7_TMRA7CLR_Msk (0x800UL) /*!< CTIMER CTRL7: TMRA7CLR (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL7_TMRA7IE1_Pos (10UL) /*!< CTIMER CTRL7: TMRA7IE1 (Bit 10) */ +#define CTIMER_CTRL7_TMRA7IE1_Msk (0x400UL) /*!< CTIMER CTRL7: TMRA7IE1 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL7_TMRA7IE0_Pos (9UL) /*!< CTIMER CTRL7: TMRA7IE0 (Bit 9) */ +#define CTIMER_CTRL7_TMRA7IE0_Msk (0x200UL) /*!< CTIMER CTRL7: TMRA7IE0 (Bitfield-Mask: 0x01) */ +#define CTIMER_CTRL7_TMRA7FN_Pos (6UL) /*!< CTIMER CTRL7: TMRA7FN (Bit 6) */ +#define CTIMER_CTRL7_TMRA7FN_Msk (0x1c0UL) /*!< CTIMER CTRL7: TMRA7FN (Bitfield-Mask: 0x07) */ +#define CTIMER_CTRL7_TMRA7CLK_Pos (1UL) /*!< CTIMER CTRL7: TMRA7CLK (Bit 1) */ +#define CTIMER_CTRL7_TMRA7CLK_Msk (0x3eUL) /*!< CTIMER CTRL7: TMRA7CLK (Bitfield-Mask: 0x1f) */ +#define CTIMER_CTRL7_TMRA7EN_Pos (0UL) /*!< CTIMER CTRL7: TMRA7EN (Bit 0) */ +#define CTIMER_CTRL7_TMRA7EN_Msk (0x1UL) /*!< CTIMER CTRL7: TMRA7EN (Bitfield-Mask: 0x01) */ +/* ======================================================= CMPRAUXA7 ======================================================= */ +#define CTIMER_CMPRAUXA7_CMPR3A7_Pos (16UL) /*!< CTIMER CMPRAUXA7: CMPR3A7 (Bit 16) */ +#define CTIMER_CMPRAUXA7_CMPR3A7_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXA7: CMPR3A7 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRAUXA7_CMPR2A7_Pos (0UL) /*!< CTIMER CMPRAUXA7: CMPR2A7 (Bit 0) */ +#define CTIMER_CMPRAUXA7_CMPR2A7_Msk (0xffffUL) /*!< CTIMER CMPRAUXA7: CMPR2A7 (Bitfield-Mask: 0xffff) */ +/* ======================================================= CMPRAUXB7 ======================================================= */ +#define CTIMER_CMPRAUXB7_CMPR3B7_Pos (16UL) /*!< CTIMER CMPRAUXB7: CMPR3B7 (Bit 16) */ +#define CTIMER_CMPRAUXB7_CMPR3B7_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXB7: CMPR3B7 (Bitfield-Mask: 0xffff) */ +#define CTIMER_CMPRAUXB7_CMPR2B7_Pos (0UL) /*!< CTIMER CMPRAUXB7: CMPR2B7 (Bit 0) */ +#define CTIMER_CMPRAUXB7_CMPR2B7_Msk (0xffffUL) /*!< CTIMER CMPRAUXB7: CMPR2B7 (Bitfield-Mask: 0xffff) */ +/* ========================================================= AUX7 ========================================================== */ +#define CTIMER_AUX7_TMRB7EN23_Pos (30UL) /*!< CTIMER AUX7: TMRB7EN23 (Bit 30) */ +#define CTIMER_AUX7_TMRB7EN23_Msk (0x40000000UL) /*!< CTIMER AUX7: TMRB7EN23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX7_TMRB7POL23_Pos (29UL) /*!< CTIMER AUX7: TMRB7POL23 (Bit 29) */ +#define CTIMER_AUX7_TMRB7POL23_Msk (0x20000000UL) /*!< CTIMER AUX7: TMRB7POL23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX7_TMRB7TINV_Pos (28UL) /*!< CTIMER AUX7: TMRB7TINV (Bit 28) */ +#define CTIMER_AUX7_TMRB7TINV_Msk (0x10000000UL) /*!< CTIMER AUX7: TMRB7TINV (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX7_TMRB7NOSYNC_Pos (27UL) /*!< CTIMER AUX7: TMRB7NOSYNC (Bit 27) */ +#define CTIMER_AUX7_TMRB7NOSYNC_Msk (0x8000000UL) /*!< CTIMER AUX7: TMRB7NOSYNC (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX7_TMRB7TRIG_Pos (23UL) /*!< CTIMER AUX7: TMRB7TRIG (Bit 23) */ +#define CTIMER_AUX7_TMRB7TRIG_Msk (0x7800000UL) /*!< CTIMER AUX7: TMRB7TRIG (Bitfield-Mask: 0x0f) */ +#define CTIMER_AUX7_TMRB7LMT_Pos (16UL) /*!< CTIMER AUX7: TMRB7LMT (Bit 16) */ +#define CTIMER_AUX7_TMRB7LMT_Msk (0x3f0000UL) /*!< CTIMER AUX7: TMRB7LMT (Bitfield-Mask: 0x3f) */ +#define CTIMER_AUX7_TMRA7EN23_Pos (14UL) /*!< CTIMER AUX7: TMRA7EN23 (Bit 14) */ +#define CTIMER_AUX7_TMRA7EN23_Msk (0x4000UL) /*!< CTIMER AUX7: TMRA7EN23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX7_TMRA7POL23_Pos (13UL) /*!< CTIMER AUX7: TMRA7POL23 (Bit 13) */ +#define CTIMER_AUX7_TMRA7POL23_Msk (0x2000UL) /*!< CTIMER AUX7: TMRA7POL23 (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX7_TMRA7TINV_Pos (12UL) /*!< CTIMER AUX7: TMRA7TINV (Bit 12) */ +#define CTIMER_AUX7_TMRA7TINV_Msk (0x1000UL) /*!< CTIMER AUX7: TMRA7TINV (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX7_TMRA7NOSYNC_Pos (11UL) /*!< CTIMER AUX7: TMRA7NOSYNC (Bit 11) */ +#define CTIMER_AUX7_TMRA7NOSYNC_Msk (0x800UL) /*!< CTIMER AUX7: TMRA7NOSYNC (Bitfield-Mask: 0x01) */ +#define CTIMER_AUX7_TMRA7TRIG_Pos (7UL) /*!< CTIMER AUX7: TMRA7TRIG (Bit 7) */ +#define CTIMER_AUX7_TMRA7TRIG_Msk (0x780UL) /*!< CTIMER AUX7: TMRA7TRIG (Bitfield-Mask: 0x0f) */ +#define CTIMER_AUX7_TMRA7LMT_Pos (0UL) /*!< CTIMER AUX7: TMRA7LMT (Bit 0) */ +#define CTIMER_AUX7_TMRA7LMT_Msk (0x7fUL) /*!< CTIMER AUX7: TMRA7LMT (Bitfield-Mask: 0x7f) */ +/* ======================================================== GLOBEN ========================================================= */ +#define CTIMER_GLOBEN_ENB7_Pos (15UL) /*!< CTIMER GLOBEN: ENB7 (Bit 15) */ +#define CTIMER_GLOBEN_ENB7_Msk (0x8000UL) /*!< CTIMER GLOBEN: ENB7 (Bitfield-Mask: 0x01) */ +#define CTIMER_GLOBEN_ENA7_Pos (14UL) /*!< CTIMER GLOBEN: ENA7 (Bit 14) */ +#define CTIMER_GLOBEN_ENA7_Msk (0x4000UL) /*!< CTIMER GLOBEN: ENA7 (Bitfield-Mask: 0x01) */ +#define CTIMER_GLOBEN_ENB6_Pos (13UL) /*!< CTIMER GLOBEN: ENB6 (Bit 13) */ +#define CTIMER_GLOBEN_ENB6_Msk (0x2000UL) /*!< CTIMER GLOBEN: ENB6 (Bitfield-Mask: 0x01) */ +#define CTIMER_GLOBEN_ENA6_Pos (12UL) /*!< CTIMER GLOBEN: ENA6 (Bit 12) */ +#define CTIMER_GLOBEN_ENA6_Msk (0x1000UL) /*!< CTIMER GLOBEN: ENA6 (Bitfield-Mask: 0x01) */ +#define CTIMER_GLOBEN_ENB5_Pos (11UL) /*!< CTIMER GLOBEN: ENB5 (Bit 11) */ +#define CTIMER_GLOBEN_ENB5_Msk (0x800UL) /*!< CTIMER GLOBEN: ENB5 (Bitfield-Mask: 0x01) */ +#define CTIMER_GLOBEN_ENA5_Pos (10UL) /*!< CTIMER GLOBEN: ENA5 (Bit 10) */ +#define CTIMER_GLOBEN_ENA5_Msk (0x400UL) /*!< CTIMER GLOBEN: ENA5 (Bitfield-Mask: 0x01) */ +#define CTIMER_GLOBEN_ENB4_Pos (9UL) /*!< CTIMER GLOBEN: ENB4 (Bit 9) */ +#define CTIMER_GLOBEN_ENB4_Msk (0x200UL) /*!< CTIMER GLOBEN: ENB4 (Bitfield-Mask: 0x01) */ +#define CTIMER_GLOBEN_ENA4_Pos (8UL) /*!< CTIMER GLOBEN: ENA4 (Bit 8) */ +#define CTIMER_GLOBEN_ENA4_Msk (0x100UL) /*!< CTIMER GLOBEN: ENA4 (Bitfield-Mask: 0x01) */ +#define CTIMER_GLOBEN_ENB3_Pos (7UL) /*!< CTIMER GLOBEN: ENB3 (Bit 7) */ +#define CTIMER_GLOBEN_ENB3_Msk (0x80UL) /*!< CTIMER GLOBEN: ENB3 (Bitfield-Mask: 0x01) */ +#define CTIMER_GLOBEN_ENA3_Pos (6UL) /*!< CTIMER GLOBEN: ENA3 (Bit 6) */ +#define CTIMER_GLOBEN_ENA3_Msk (0x40UL) /*!< CTIMER GLOBEN: ENA3 (Bitfield-Mask: 0x01) */ +#define CTIMER_GLOBEN_ENB2_Pos (5UL) /*!< CTIMER GLOBEN: ENB2 (Bit 5) */ +#define CTIMER_GLOBEN_ENB2_Msk (0x20UL) /*!< CTIMER GLOBEN: ENB2 (Bitfield-Mask: 0x01) */ +#define CTIMER_GLOBEN_ENA2_Pos (4UL) /*!< CTIMER GLOBEN: ENA2 (Bit 4) */ +#define CTIMER_GLOBEN_ENA2_Msk (0x10UL) /*!< CTIMER GLOBEN: ENA2 (Bitfield-Mask: 0x01) */ +#define CTIMER_GLOBEN_ENB1_Pos (3UL) /*!< CTIMER GLOBEN: ENB1 (Bit 3) */ +#define CTIMER_GLOBEN_ENB1_Msk (0x8UL) /*!< CTIMER GLOBEN: ENB1 (Bitfield-Mask: 0x01) */ +#define CTIMER_GLOBEN_ENA1_Pos (2UL) /*!< CTIMER GLOBEN: ENA1 (Bit 2) */ +#define CTIMER_GLOBEN_ENA1_Msk (0x4UL) /*!< CTIMER GLOBEN: ENA1 (Bitfield-Mask: 0x01) */ +#define CTIMER_GLOBEN_ENB0_Pos (1UL) /*!< CTIMER GLOBEN: ENB0 (Bit 1) */ +#define CTIMER_GLOBEN_ENB0_Msk (0x2UL) /*!< CTIMER GLOBEN: ENB0 (Bitfield-Mask: 0x01) */ +#define CTIMER_GLOBEN_ENA0_Pos (0UL) /*!< CTIMER GLOBEN: ENA0 (Bit 0) */ +#define CTIMER_GLOBEN_ENA0_Msk (0x1UL) /*!< CTIMER GLOBEN: ENA0 (Bitfield-Mask: 0x01) */ +/* ======================================================== OUTCFG0 ======================================================== */ +#define CTIMER_OUTCFG0_CFG9_Pos (28UL) /*!< CTIMER OUTCFG0: CFG9 (Bit 28) */ +#define CTIMER_OUTCFG0_CFG9_Msk (0x70000000UL) /*!< CTIMER OUTCFG0: CFG9 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG0_CFG8_Pos (25UL) /*!< CTIMER OUTCFG0: CFG8 (Bit 25) */ +#define CTIMER_OUTCFG0_CFG8_Msk (0xe000000UL) /*!< CTIMER OUTCFG0: CFG8 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG0_CFG7_Pos (22UL) /*!< CTIMER OUTCFG0: CFG7 (Bit 22) */ +#define CTIMER_OUTCFG0_CFG7_Msk (0x1c00000UL) /*!< CTIMER OUTCFG0: CFG7 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG0_CFG6_Pos (19UL) /*!< CTIMER OUTCFG0: CFG6 (Bit 19) */ +#define CTIMER_OUTCFG0_CFG6_Msk (0x380000UL) /*!< CTIMER OUTCFG0: CFG6 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG0_CFG5_Pos (16UL) /*!< CTIMER OUTCFG0: CFG5 (Bit 16) */ +#define CTIMER_OUTCFG0_CFG5_Msk (0x70000UL) /*!< CTIMER OUTCFG0: CFG5 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG0_CFG4_Pos (12UL) /*!< CTIMER OUTCFG0: CFG4 (Bit 12) */ +#define CTIMER_OUTCFG0_CFG4_Msk (0x7000UL) /*!< CTIMER OUTCFG0: CFG4 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG0_CFG3_Pos (9UL) /*!< CTIMER OUTCFG0: CFG3 (Bit 9) */ +#define CTIMER_OUTCFG0_CFG3_Msk (0xe00UL) /*!< CTIMER OUTCFG0: CFG3 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG0_CFG2_Pos (6UL) /*!< CTIMER OUTCFG0: CFG2 (Bit 6) */ +#define CTIMER_OUTCFG0_CFG2_Msk (0x1c0UL) /*!< CTIMER OUTCFG0: CFG2 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG0_CFG1_Pos (3UL) /*!< CTIMER OUTCFG0: CFG1 (Bit 3) */ +#define CTIMER_OUTCFG0_CFG1_Msk (0x38UL) /*!< CTIMER OUTCFG0: CFG1 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG0_CFG0_Pos (0UL) /*!< CTIMER OUTCFG0: CFG0 (Bit 0) */ +#define CTIMER_OUTCFG0_CFG0_Msk (0x7UL) /*!< CTIMER OUTCFG0: CFG0 (Bitfield-Mask: 0x07) */ +/* ======================================================== OUTCFG1 ======================================================== */ +#define CTIMER_OUTCFG1_CFG19_Pos (28UL) /*!< CTIMER OUTCFG1: CFG19 (Bit 28) */ +#define CTIMER_OUTCFG1_CFG19_Msk (0x70000000UL) /*!< CTIMER OUTCFG1: CFG19 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG1_CFG18_Pos (25UL) /*!< CTIMER OUTCFG1: CFG18 (Bit 25) */ +#define CTIMER_OUTCFG1_CFG18_Msk (0xe000000UL) /*!< CTIMER OUTCFG1: CFG18 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG1_CFG17_Pos (22UL) /*!< CTIMER OUTCFG1: CFG17 (Bit 22) */ +#define CTIMER_OUTCFG1_CFG17_Msk (0x1c00000UL) /*!< CTIMER OUTCFG1: CFG17 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG1_CFG16_Pos (19UL) /*!< CTIMER OUTCFG1: CFG16 (Bit 19) */ +#define CTIMER_OUTCFG1_CFG16_Msk (0x380000UL) /*!< CTIMER OUTCFG1: CFG16 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG1_CFG15_Pos (16UL) /*!< CTIMER OUTCFG1: CFG15 (Bit 16) */ +#define CTIMER_OUTCFG1_CFG15_Msk (0x70000UL) /*!< CTIMER OUTCFG1: CFG15 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG1_CFG14_Pos (12UL) /*!< CTIMER OUTCFG1: CFG14 (Bit 12) */ +#define CTIMER_OUTCFG1_CFG14_Msk (0x7000UL) /*!< CTIMER OUTCFG1: CFG14 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG1_CFG13_Pos (9UL) /*!< CTIMER OUTCFG1: CFG13 (Bit 9) */ +#define CTIMER_OUTCFG1_CFG13_Msk (0xe00UL) /*!< CTIMER OUTCFG1: CFG13 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG1_CFG12_Pos (6UL) /*!< CTIMER OUTCFG1: CFG12 (Bit 6) */ +#define CTIMER_OUTCFG1_CFG12_Msk (0x1c0UL) /*!< CTIMER OUTCFG1: CFG12 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG1_CFG11_Pos (3UL) /*!< CTIMER OUTCFG1: CFG11 (Bit 3) */ +#define CTIMER_OUTCFG1_CFG11_Msk (0x38UL) /*!< CTIMER OUTCFG1: CFG11 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG1_CFG10_Pos (0UL) /*!< CTIMER OUTCFG1: CFG10 (Bit 0) */ +#define CTIMER_OUTCFG1_CFG10_Msk (0x7UL) /*!< CTIMER OUTCFG1: CFG10 (Bitfield-Mask: 0x07) */ +/* ======================================================== OUTCFG2 ======================================================== */ +#define CTIMER_OUTCFG2_CFG29_Pos (28UL) /*!< CTIMER OUTCFG2: CFG29 (Bit 28) */ +#define CTIMER_OUTCFG2_CFG29_Msk (0x70000000UL) /*!< CTIMER OUTCFG2: CFG29 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG2_CFG28_Pos (25UL) /*!< CTIMER OUTCFG2: CFG28 (Bit 25) */ +#define CTIMER_OUTCFG2_CFG28_Msk (0xe000000UL) /*!< CTIMER OUTCFG2: CFG28 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG2_CFG27_Pos (22UL) /*!< CTIMER OUTCFG2: CFG27 (Bit 22) */ +#define CTIMER_OUTCFG2_CFG27_Msk (0x1c00000UL) /*!< CTIMER OUTCFG2: CFG27 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG2_CFG26_Pos (19UL) /*!< CTIMER OUTCFG2: CFG26 (Bit 19) */ +#define CTIMER_OUTCFG2_CFG26_Msk (0x380000UL) /*!< CTIMER OUTCFG2: CFG26 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG2_CFG25_Pos (16UL) /*!< CTIMER OUTCFG2: CFG25 (Bit 16) */ +#define CTIMER_OUTCFG2_CFG25_Msk (0x70000UL) /*!< CTIMER OUTCFG2: CFG25 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG2_CFG24_Pos (12UL) /*!< CTIMER OUTCFG2: CFG24 (Bit 12) */ +#define CTIMER_OUTCFG2_CFG24_Msk (0x7000UL) /*!< CTIMER OUTCFG2: CFG24 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG2_CFG23_Pos (9UL) /*!< CTIMER OUTCFG2: CFG23 (Bit 9) */ +#define CTIMER_OUTCFG2_CFG23_Msk (0xe00UL) /*!< CTIMER OUTCFG2: CFG23 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG2_CFG22_Pos (6UL) /*!< CTIMER OUTCFG2: CFG22 (Bit 6) */ +#define CTIMER_OUTCFG2_CFG22_Msk (0x1c0UL) /*!< CTIMER OUTCFG2: CFG22 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG2_CFG21_Pos (3UL) /*!< CTIMER OUTCFG2: CFG21 (Bit 3) */ +#define CTIMER_OUTCFG2_CFG21_Msk (0x38UL) /*!< CTIMER OUTCFG2: CFG21 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG2_CFG20_Pos (0UL) /*!< CTIMER OUTCFG2: CFG20 (Bit 0) */ +#define CTIMER_OUTCFG2_CFG20_Msk (0x7UL) /*!< CTIMER OUTCFG2: CFG20 (Bitfield-Mask: 0x07) */ +/* ======================================================== OUTCFG3 ======================================================== */ +#define CTIMER_OUTCFG3_CFG31_Pos (3UL) /*!< CTIMER OUTCFG3: CFG31 (Bit 3) */ +#define CTIMER_OUTCFG3_CFG31_Msk (0x38UL) /*!< CTIMER OUTCFG3: CFG31 (Bitfield-Mask: 0x07) */ +#define CTIMER_OUTCFG3_CFG30_Pos (0UL) /*!< CTIMER OUTCFG3: CFG30 (Bit 0) */ +#define CTIMER_OUTCFG3_CFG30_Msk (0x7UL) /*!< CTIMER OUTCFG3: CFG30 (Bitfield-Mask: 0x07) */ +/* ========================================================= INCFG ========================================================= */ +#define CTIMER_INCFG_CFGB7_Pos (15UL) /*!< CTIMER INCFG: CFGB7 (Bit 15) */ +#define CTIMER_INCFG_CFGB7_Msk (0x8000UL) /*!< CTIMER INCFG: CFGB7 (Bitfield-Mask: 0x01) */ +#define CTIMER_INCFG_CFGA7_Pos (14UL) /*!< CTIMER INCFG: CFGA7 (Bit 14) */ +#define CTIMER_INCFG_CFGA7_Msk (0x4000UL) /*!< CTIMER INCFG: CFGA7 (Bitfield-Mask: 0x01) */ +#define CTIMER_INCFG_CFGB6_Pos (13UL) /*!< CTIMER INCFG: CFGB6 (Bit 13) */ +#define CTIMER_INCFG_CFGB6_Msk (0x2000UL) /*!< CTIMER INCFG: CFGB6 (Bitfield-Mask: 0x01) */ +#define CTIMER_INCFG_CFGA6_Pos (12UL) /*!< CTIMER INCFG: CFGA6 (Bit 12) */ +#define CTIMER_INCFG_CFGA6_Msk (0x1000UL) /*!< CTIMER INCFG: CFGA6 (Bitfield-Mask: 0x01) */ +#define CTIMER_INCFG_CFGB5_Pos (11UL) /*!< CTIMER INCFG: CFGB5 (Bit 11) */ +#define CTIMER_INCFG_CFGB5_Msk (0x800UL) /*!< CTIMER INCFG: CFGB5 (Bitfield-Mask: 0x01) */ +#define CTIMER_INCFG_CFGA5_Pos (10UL) /*!< CTIMER INCFG: CFGA5 (Bit 10) */ +#define CTIMER_INCFG_CFGA5_Msk (0x400UL) /*!< CTIMER INCFG: CFGA5 (Bitfield-Mask: 0x01) */ +#define CTIMER_INCFG_CFGB4_Pos (9UL) /*!< CTIMER INCFG: CFGB4 (Bit 9) */ +#define CTIMER_INCFG_CFGB4_Msk (0x200UL) /*!< CTIMER INCFG: CFGB4 (Bitfield-Mask: 0x01) */ +#define CTIMER_INCFG_CFGA4_Pos (8UL) /*!< CTIMER INCFG: CFGA4 (Bit 8) */ +#define CTIMER_INCFG_CFGA4_Msk (0x100UL) /*!< CTIMER INCFG: CFGA4 (Bitfield-Mask: 0x01) */ +#define CTIMER_INCFG_CFGB3_Pos (7UL) /*!< CTIMER INCFG: CFGB3 (Bit 7) */ +#define CTIMER_INCFG_CFGB3_Msk (0x80UL) /*!< CTIMER INCFG: CFGB3 (Bitfield-Mask: 0x01) */ +#define CTIMER_INCFG_CFGA3_Pos (6UL) /*!< CTIMER INCFG: CFGA3 (Bit 6) */ +#define CTIMER_INCFG_CFGA3_Msk (0x40UL) /*!< CTIMER INCFG: CFGA3 (Bitfield-Mask: 0x01) */ +#define CTIMER_INCFG_CFGB2_Pos (5UL) /*!< CTIMER INCFG: CFGB2 (Bit 5) */ +#define CTIMER_INCFG_CFGB2_Msk (0x20UL) /*!< CTIMER INCFG: CFGB2 (Bitfield-Mask: 0x01) */ +#define CTIMER_INCFG_CFGA2_Pos (4UL) /*!< CTIMER INCFG: CFGA2 (Bit 4) */ +#define CTIMER_INCFG_CFGA2_Msk (0x10UL) /*!< CTIMER INCFG: CFGA2 (Bitfield-Mask: 0x01) */ +#define CTIMER_INCFG_CFGB1_Pos (3UL) /*!< CTIMER INCFG: CFGB1 (Bit 3) */ +#define CTIMER_INCFG_CFGB1_Msk (0x8UL) /*!< CTIMER INCFG: CFGB1 (Bitfield-Mask: 0x01) */ +#define CTIMER_INCFG_CFGA1_Pos (2UL) /*!< CTIMER INCFG: CFGA1 (Bit 2) */ +#define CTIMER_INCFG_CFGA1_Msk (0x4UL) /*!< CTIMER INCFG: CFGA1 (Bitfield-Mask: 0x01) */ +#define CTIMER_INCFG_CFGB0_Pos (1UL) /*!< CTIMER INCFG: CFGB0 (Bit 1) */ +#define CTIMER_INCFG_CFGB0_Msk (0x2UL) /*!< CTIMER INCFG: CFGB0 (Bitfield-Mask: 0x01) */ +#define CTIMER_INCFG_CFGA0_Pos (0UL) /*!< CTIMER INCFG: CFGA0 (Bit 0) */ +#define CTIMER_INCFG_CFGA0_Msk (0x1UL) /*!< CTIMER INCFG: CFGA0 (Bitfield-Mask: 0x01) */ +/* ========================================================= STCFG ========================================================= */ +#define CTIMER_STCFG_FREEZE_Pos (31UL) /*!< CTIMER STCFG: FREEZE (Bit 31) */ +#define CTIMER_STCFG_FREEZE_Msk (0x80000000UL) /*!< CTIMER STCFG: FREEZE (Bitfield-Mask: 0x01) */ +#define CTIMER_STCFG_CLEAR_Pos (30UL) /*!< CTIMER STCFG: CLEAR (Bit 30) */ +#define CTIMER_STCFG_CLEAR_Msk (0x40000000UL) /*!< CTIMER STCFG: CLEAR (Bitfield-Mask: 0x01) */ +#define CTIMER_STCFG_COMPARE_H_EN_Pos (15UL) /*!< CTIMER STCFG: COMPARE_H_EN (Bit 15) */ +#define CTIMER_STCFG_COMPARE_H_EN_Msk (0x8000UL) /*!< CTIMER STCFG: COMPARE_H_EN (Bitfield-Mask: 0x01) */ +#define CTIMER_STCFG_COMPARE_G_EN_Pos (14UL) /*!< CTIMER STCFG: COMPARE_G_EN (Bit 14) */ +#define CTIMER_STCFG_COMPARE_G_EN_Msk (0x4000UL) /*!< CTIMER STCFG: COMPARE_G_EN (Bitfield-Mask: 0x01) */ +#define CTIMER_STCFG_COMPARE_F_EN_Pos (13UL) /*!< CTIMER STCFG: COMPARE_F_EN (Bit 13) */ +#define CTIMER_STCFG_COMPARE_F_EN_Msk (0x2000UL) /*!< CTIMER STCFG: COMPARE_F_EN (Bitfield-Mask: 0x01) */ +#define CTIMER_STCFG_COMPARE_E_EN_Pos (12UL) /*!< CTIMER STCFG: COMPARE_E_EN (Bit 12) */ +#define CTIMER_STCFG_COMPARE_E_EN_Msk (0x1000UL) /*!< CTIMER STCFG: COMPARE_E_EN (Bitfield-Mask: 0x01) */ +#define CTIMER_STCFG_COMPARE_D_EN_Pos (11UL) /*!< CTIMER STCFG: COMPARE_D_EN (Bit 11) */ +#define CTIMER_STCFG_COMPARE_D_EN_Msk (0x800UL) /*!< CTIMER STCFG: COMPARE_D_EN (Bitfield-Mask: 0x01) */ +#define CTIMER_STCFG_COMPARE_C_EN_Pos (10UL) /*!< CTIMER STCFG: COMPARE_C_EN (Bit 10) */ +#define CTIMER_STCFG_COMPARE_C_EN_Msk (0x400UL) /*!< CTIMER STCFG: COMPARE_C_EN (Bitfield-Mask: 0x01) */ +#define CTIMER_STCFG_COMPARE_B_EN_Pos (9UL) /*!< CTIMER STCFG: COMPARE_B_EN (Bit 9) */ +#define CTIMER_STCFG_COMPARE_B_EN_Msk (0x200UL) /*!< CTIMER STCFG: COMPARE_B_EN (Bitfield-Mask: 0x01) */ +#define CTIMER_STCFG_COMPARE_A_EN_Pos (8UL) /*!< CTIMER STCFG: COMPARE_A_EN (Bit 8) */ +#define CTIMER_STCFG_COMPARE_A_EN_Msk (0x100UL) /*!< CTIMER STCFG: COMPARE_A_EN (Bitfield-Mask: 0x01) */ +#define CTIMER_STCFG_CLKSEL_Pos (0UL) /*!< CTIMER STCFG: CLKSEL (Bit 0) */ +#define CTIMER_STCFG_CLKSEL_Msk (0xfUL) /*!< CTIMER STCFG: CLKSEL (Bitfield-Mask: 0x0f) */ +/* ========================================================= STTMR ========================================================= */ +#define CTIMER_STTMR_STTMR_Pos (0UL) /*!< CTIMER STTMR: STTMR (Bit 0) */ +#define CTIMER_STTMR_STTMR_Msk (0xffffffffUL) /*!< CTIMER STTMR: STTMR (Bitfield-Mask: 0xffffffff) */ +/* ==================================================== CAPTURECONTROL ===================================================== */ +#define CTIMER_CAPTURECONTROL_CAPTURE3_Pos (3UL) /*!< CTIMER CAPTURECONTROL: CAPTURE3 (Bit 3) */ +#define CTIMER_CAPTURECONTROL_CAPTURE3_Msk (0x8UL) /*!< CTIMER CAPTURECONTROL: CAPTURE3 (Bitfield-Mask: 0x01) */ +#define CTIMER_CAPTURECONTROL_CAPTURE2_Pos (2UL) /*!< CTIMER CAPTURECONTROL: CAPTURE2 (Bit 2) */ +#define CTIMER_CAPTURECONTROL_CAPTURE2_Msk (0x4UL) /*!< CTIMER CAPTURECONTROL: CAPTURE2 (Bitfield-Mask: 0x01) */ +#define CTIMER_CAPTURECONTROL_CAPTURE1_Pos (1UL) /*!< CTIMER CAPTURECONTROL: CAPTURE1 (Bit 1) */ +#define CTIMER_CAPTURECONTROL_CAPTURE1_Msk (0x2UL) /*!< CTIMER CAPTURECONTROL: CAPTURE1 (Bitfield-Mask: 0x01) */ +#define CTIMER_CAPTURECONTROL_CAPTURE0_Pos (0UL) /*!< CTIMER CAPTURECONTROL: CAPTURE0 (Bit 0) */ +#define CTIMER_CAPTURECONTROL_CAPTURE0_Msk (0x1UL) /*!< CTIMER CAPTURECONTROL: CAPTURE0 (Bitfield-Mask: 0x01) */ +/* ======================================================== SCMPR0 ========================================================= */ +#define CTIMER_SCMPR0_SCMPR0_Pos (0UL) /*!< CTIMER SCMPR0: SCMPR0 (Bit 0) */ +#define CTIMER_SCMPR0_SCMPR0_Msk (0xffffffffUL) /*!< CTIMER SCMPR0: SCMPR0 (Bitfield-Mask: 0xffffffff) */ +/* ======================================================== SCMPR1 ========================================================= */ +#define CTIMER_SCMPR1_SCMPR1_Pos (0UL) /*!< CTIMER SCMPR1: SCMPR1 (Bit 0) */ +#define CTIMER_SCMPR1_SCMPR1_Msk (0xffffffffUL) /*!< CTIMER SCMPR1: SCMPR1 (Bitfield-Mask: 0xffffffff) */ +/* ======================================================== SCMPR2 ========================================================= */ +#define CTIMER_SCMPR2_SCMPR2_Pos (0UL) /*!< CTIMER SCMPR2: SCMPR2 (Bit 0) */ +#define CTIMER_SCMPR2_SCMPR2_Msk (0xffffffffUL) /*!< CTIMER SCMPR2: SCMPR2 (Bitfield-Mask: 0xffffffff) */ +/* ======================================================== SCMPR3 ========================================================= */ +#define CTIMER_SCMPR3_SCMPR3_Pos (0UL) /*!< CTIMER SCMPR3: SCMPR3 (Bit 0) */ +#define CTIMER_SCMPR3_SCMPR3_Msk (0xffffffffUL) /*!< CTIMER SCMPR3: SCMPR3 (Bitfield-Mask: 0xffffffff) */ +/* ======================================================== SCMPR4 ========================================================= */ +#define CTIMER_SCMPR4_SCMPR4_Pos (0UL) /*!< CTIMER SCMPR4: SCMPR4 (Bit 0) */ +#define CTIMER_SCMPR4_SCMPR4_Msk (0xffffffffUL) /*!< CTIMER SCMPR4: SCMPR4 (Bitfield-Mask: 0xffffffff) */ +/* ======================================================== SCMPR5 ========================================================= */ +#define CTIMER_SCMPR5_SCMPR5_Pos (0UL) /*!< CTIMER SCMPR5: SCMPR5 (Bit 0) */ +#define CTIMER_SCMPR5_SCMPR5_Msk (0xffffffffUL) /*!< CTIMER SCMPR5: SCMPR5 (Bitfield-Mask: 0xffffffff) */ +/* ======================================================== SCMPR6 ========================================================= */ +#define CTIMER_SCMPR6_SCMPR6_Pos (0UL) /*!< CTIMER SCMPR6: SCMPR6 (Bit 0) */ +#define CTIMER_SCMPR6_SCMPR6_Msk (0xffffffffUL) /*!< CTIMER SCMPR6: SCMPR6 (Bitfield-Mask: 0xffffffff) */ +/* ======================================================== SCMPR7 ========================================================= */ +#define CTIMER_SCMPR7_SCMPR7_Pos (0UL) /*!< CTIMER SCMPR7: SCMPR7 (Bit 0) */ +#define CTIMER_SCMPR7_SCMPR7_Msk (0xffffffffUL) /*!< CTIMER SCMPR7: SCMPR7 (Bitfield-Mask: 0xffffffff) */ +/* ======================================================== SCAPT0 ========================================================= */ +#define CTIMER_SCAPT0_SCAPT0_Pos (0UL) /*!< CTIMER SCAPT0: SCAPT0 (Bit 0) */ +#define CTIMER_SCAPT0_SCAPT0_Msk (0xffffffffUL) /*!< CTIMER SCAPT0: SCAPT0 (Bitfield-Mask: 0xffffffff) */ +/* ======================================================== SCAPT1 ========================================================= */ +#define CTIMER_SCAPT1_SCAPT1_Pos (0UL) /*!< CTIMER SCAPT1: SCAPT1 (Bit 0) */ +#define CTIMER_SCAPT1_SCAPT1_Msk (0xffffffffUL) /*!< CTIMER SCAPT1: SCAPT1 (Bitfield-Mask: 0xffffffff) */ +/* ======================================================== SCAPT2 ========================================================= */ +#define CTIMER_SCAPT2_SCAPT2_Pos (0UL) /*!< CTIMER SCAPT2: SCAPT2 (Bit 0) */ +#define CTIMER_SCAPT2_SCAPT2_Msk (0xffffffffUL) /*!< CTIMER SCAPT2: SCAPT2 (Bitfield-Mask: 0xffffffff) */ +/* ======================================================== SCAPT3 ========================================================= */ +#define CTIMER_SCAPT3_SCAPT3_Pos (0UL) /*!< CTIMER SCAPT3: SCAPT3 (Bit 0) */ +#define CTIMER_SCAPT3_SCAPT3_Msk (0xffffffffUL) /*!< CTIMER SCAPT3: SCAPT3 (Bitfield-Mask: 0xffffffff) */ +/* ========================================================= SNVR0 ========================================================= */ +#define CTIMER_SNVR0_SNVR0_Pos (0UL) /*!< CTIMER SNVR0: SNVR0 (Bit 0) */ +#define CTIMER_SNVR0_SNVR0_Msk (0xffffffffUL) /*!< CTIMER SNVR0: SNVR0 (Bitfield-Mask: 0xffffffff) */ +/* ========================================================= SNVR1 ========================================================= */ +#define CTIMER_SNVR1_SNVR1_Pos (0UL) /*!< CTIMER SNVR1: SNVR1 (Bit 0) */ +#define CTIMER_SNVR1_SNVR1_Msk (0xffffffffUL) /*!< CTIMER SNVR1: SNVR1 (Bitfield-Mask: 0xffffffff) */ +/* ========================================================= SNVR2 ========================================================= */ +#define CTIMER_SNVR2_SNVR2_Pos (0UL) /*!< CTIMER SNVR2: SNVR2 (Bit 0) */ +#define CTIMER_SNVR2_SNVR2_Msk (0xffffffffUL) /*!< CTIMER SNVR2: SNVR2 (Bitfield-Mask: 0xffffffff) */ +/* ========================================================= SNVR3 ========================================================= */ +#define CTIMER_SNVR3_SNVR3_Pos (0UL) /*!< CTIMER SNVR3: SNVR3 (Bit 0) */ +#define CTIMER_SNVR3_SNVR3_Msk (0xffffffffUL) /*!< CTIMER SNVR3: SNVR3 (Bitfield-Mask: 0xffffffff) */ +/* ========================================================= INTEN ========================================================= */ +#define CTIMER_INTEN_CTMRB7C1INT_Pos (31UL) /*!< CTIMER INTEN: CTMRB7C1INT (Bit 31) */ +#define CTIMER_INTEN_CTMRB7C1INT_Msk (0x80000000UL) /*!< CTIMER INTEN: CTMRB7C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRA7C1INT_Pos (30UL) /*!< CTIMER INTEN: CTMRA7C1INT (Bit 30) */ +#define CTIMER_INTEN_CTMRA7C1INT_Msk (0x40000000UL) /*!< CTIMER INTEN: CTMRA7C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRB6C1INT_Pos (29UL) /*!< CTIMER INTEN: CTMRB6C1INT (Bit 29) */ +#define CTIMER_INTEN_CTMRB6C1INT_Msk (0x20000000UL) /*!< CTIMER INTEN: CTMRB6C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRA6C1INT_Pos (28UL) /*!< CTIMER INTEN: CTMRA6C1INT (Bit 28) */ +#define CTIMER_INTEN_CTMRA6C1INT_Msk (0x10000000UL) /*!< CTIMER INTEN: CTMRA6C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRB5C1INT_Pos (27UL) /*!< CTIMER INTEN: CTMRB5C1INT (Bit 27) */ +#define CTIMER_INTEN_CTMRB5C1INT_Msk (0x8000000UL) /*!< CTIMER INTEN: CTMRB5C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRA5C1INT_Pos (26UL) /*!< CTIMER INTEN: CTMRA5C1INT (Bit 26) */ +#define CTIMER_INTEN_CTMRA5C1INT_Msk (0x4000000UL) /*!< CTIMER INTEN: CTMRA5C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRB4C1INT_Pos (25UL) /*!< CTIMER INTEN: CTMRB4C1INT (Bit 25) */ +#define CTIMER_INTEN_CTMRB4C1INT_Msk (0x2000000UL) /*!< CTIMER INTEN: CTMRB4C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRA4C1INT_Pos (24UL) /*!< CTIMER INTEN: CTMRA4C1INT (Bit 24) */ +#define CTIMER_INTEN_CTMRA4C1INT_Msk (0x1000000UL) /*!< CTIMER INTEN: CTMRA4C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRB3C1INT_Pos (23UL) /*!< CTIMER INTEN: CTMRB3C1INT (Bit 23) */ +#define CTIMER_INTEN_CTMRB3C1INT_Msk (0x800000UL) /*!< CTIMER INTEN: CTMRB3C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRA3C1INT_Pos (22UL) /*!< CTIMER INTEN: CTMRA3C1INT (Bit 22) */ +#define CTIMER_INTEN_CTMRA3C1INT_Msk (0x400000UL) /*!< CTIMER INTEN: CTMRA3C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRB2C1INT_Pos (21UL) /*!< CTIMER INTEN: CTMRB2C1INT (Bit 21) */ +#define CTIMER_INTEN_CTMRB2C1INT_Msk (0x200000UL) /*!< CTIMER INTEN: CTMRB2C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRA2C1INT_Pos (20UL) /*!< CTIMER INTEN: CTMRA2C1INT (Bit 20) */ +#define CTIMER_INTEN_CTMRA2C1INT_Msk (0x100000UL) /*!< CTIMER INTEN: CTMRA2C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRB1C1INT_Pos (19UL) /*!< CTIMER INTEN: CTMRB1C1INT (Bit 19) */ +#define CTIMER_INTEN_CTMRB1C1INT_Msk (0x80000UL) /*!< CTIMER INTEN: CTMRB1C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRA1C1INT_Pos (18UL) /*!< CTIMER INTEN: CTMRA1C1INT (Bit 18) */ +#define CTIMER_INTEN_CTMRA1C1INT_Msk (0x40000UL) /*!< CTIMER INTEN: CTMRA1C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRB0C1INT_Pos (17UL) /*!< CTIMER INTEN: CTMRB0C1INT (Bit 17) */ +#define CTIMER_INTEN_CTMRB0C1INT_Msk (0x20000UL) /*!< CTIMER INTEN: CTMRB0C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRA0C1INT_Pos (16UL) /*!< CTIMER INTEN: CTMRA0C1INT (Bit 16) */ +#define CTIMER_INTEN_CTMRA0C1INT_Msk (0x10000UL) /*!< CTIMER INTEN: CTMRA0C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRB7C0INT_Pos (15UL) /*!< CTIMER INTEN: CTMRB7C0INT (Bit 15) */ +#define CTIMER_INTEN_CTMRB7C0INT_Msk (0x8000UL) /*!< CTIMER INTEN: CTMRB7C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRA7C0INT_Pos (14UL) /*!< CTIMER INTEN: CTMRA7C0INT (Bit 14) */ +#define CTIMER_INTEN_CTMRA7C0INT_Msk (0x4000UL) /*!< CTIMER INTEN: CTMRA7C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRB6C0INT_Pos (13UL) /*!< CTIMER INTEN: CTMRB6C0INT (Bit 13) */ +#define CTIMER_INTEN_CTMRB6C0INT_Msk (0x2000UL) /*!< CTIMER INTEN: CTMRB6C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRA6C0INT_Pos (12UL) /*!< CTIMER INTEN: CTMRA6C0INT (Bit 12) */ +#define CTIMER_INTEN_CTMRA6C0INT_Msk (0x1000UL) /*!< CTIMER INTEN: CTMRA6C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRB5C0INT_Pos (11UL) /*!< CTIMER INTEN: CTMRB5C0INT (Bit 11) */ +#define CTIMER_INTEN_CTMRB5C0INT_Msk (0x800UL) /*!< CTIMER INTEN: CTMRB5C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRA5C0INT_Pos (10UL) /*!< CTIMER INTEN: CTMRA5C0INT (Bit 10) */ +#define CTIMER_INTEN_CTMRA5C0INT_Msk (0x400UL) /*!< CTIMER INTEN: CTMRA5C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRB4C0INT_Pos (9UL) /*!< CTIMER INTEN: CTMRB4C0INT (Bit 9) */ +#define CTIMER_INTEN_CTMRB4C0INT_Msk (0x200UL) /*!< CTIMER INTEN: CTMRB4C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRA4C0INT_Pos (8UL) /*!< CTIMER INTEN: CTMRA4C0INT (Bit 8) */ +#define CTIMER_INTEN_CTMRA4C0INT_Msk (0x100UL) /*!< CTIMER INTEN: CTMRA4C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRB3C0INT_Pos (7UL) /*!< CTIMER INTEN: CTMRB3C0INT (Bit 7) */ +#define CTIMER_INTEN_CTMRB3C0INT_Msk (0x80UL) /*!< CTIMER INTEN: CTMRB3C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRA3C0INT_Pos (6UL) /*!< CTIMER INTEN: CTMRA3C0INT (Bit 6) */ +#define CTIMER_INTEN_CTMRA3C0INT_Msk (0x40UL) /*!< CTIMER INTEN: CTMRA3C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRB2C0INT_Pos (5UL) /*!< CTIMER INTEN: CTMRB2C0INT (Bit 5) */ +#define CTIMER_INTEN_CTMRB2C0INT_Msk (0x20UL) /*!< CTIMER INTEN: CTMRB2C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRA2C0INT_Pos (4UL) /*!< CTIMER INTEN: CTMRA2C0INT (Bit 4) */ +#define CTIMER_INTEN_CTMRA2C0INT_Msk (0x10UL) /*!< CTIMER INTEN: CTMRA2C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRB1C0INT_Pos (3UL) /*!< CTIMER INTEN: CTMRB1C0INT (Bit 3) */ +#define CTIMER_INTEN_CTMRB1C0INT_Msk (0x8UL) /*!< CTIMER INTEN: CTMRB1C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRA1C0INT_Pos (2UL) /*!< CTIMER INTEN: CTMRA1C0INT (Bit 2) */ +#define CTIMER_INTEN_CTMRA1C0INT_Msk (0x4UL) /*!< CTIMER INTEN: CTMRA1C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRB0C0INT_Pos (1UL) /*!< CTIMER INTEN: CTMRB0C0INT (Bit 1) */ +#define CTIMER_INTEN_CTMRB0C0INT_Msk (0x2UL) /*!< CTIMER INTEN: CTMRB0C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTEN_CTMRA0C0INT_Pos (0UL) /*!< CTIMER INTEN: CTMRA0C0INT (Bit 0) */ +#define CTIMER_INTEN_CTMRA0C0INT_Msk (0x1UL) /*!< CTIMER INTEN: CTMRA0C0INT (Bitfield-Mask: 0x01) */ +/* ======================================================== INTSTAT ======================================================== */ +#define CTIMER_INTSTAT_CTMRB7C1INT_Pos (31UL) /*!< CTIMER INTSTAT: CTMRB7C1INT (Bit 31) */ +#define CTIMER_INTSTAT_CTMRB7C1INT_Msk (0x80000000UL) /*!< CTIMER INTSTAT: CTMRB7C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRA7C1INT_Pos (30UL) /*!< CTIMER INTSTAT: CTMRA7C1INT (Bit 30) */ +#define CTIMER_INTSTAT_CTMRA7C1INT_Msk (0x40000000UL) /*!< CTIMER INTSTAT: CTMRA7C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRB6C1INT_Pos (29UL) /*!< CTIMER INTSTAT: CTMRB6C1INT (Bit 29) */ +#define CTIMER_INTSTAT_CTMRB6C1INT_Msk (0x20000000UL) /*!< CTIMER INTSTAT: CTMRB6C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRA6C1INT_Pos (28UL) /*!< CTIMER INTSTAT: CTMRA6C1INT (Bit 28) */ +#define CTIMER_INTSTAT_CTMRA6C1INT_Msk (0x10000000UL) /*!< CTIMER INTSTAT: CTMRA6C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRB5C1INT_Pos (27UL) /*!< CTIMER INTSTAT: CTMRB5C1INT (Bit 27) */ +#define CTIMER_INTSTAT_CTMRB5C1INT_Msk (0x8000000UL) /*!< CTIMER INTSTAT: CTMRB5C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRA5C1INT_Pos (26UL) /*!< CTIMER INTSTAT: CTMRA5C1INT (Bit 26) */ +#define CTIMER_INTSTAT_CTMRA5C1INT_Msk (0x4000000UL) /*!< CTIMER INTSTAT: CTMRA5C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRB4C1INT_Pos (25UL) /*!< CTIMER INTSTAT: CTMRB4C1INT (Bit 25) */ +#define CTIMER_INTSTAT_CTMRB4C1INT_Msk (0x2000000UL) /*!< CTIMER INTSTAT: CTMRB4C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRA4C1INT_Pos (24UL) /*!< CTIMER INTSTAT: CTMRA4C1INT (Bit 24) */ +#define CTIMER_INTSTAT_CTMRA4C1INT_Msk (0x1000000UL) /*!< CTIMER INTSTAT: CTMRA4C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRB3C1INT_Pos (23UL) /*!< CTIMER INTSTAT: CTMRB3C1INT (Bit 23) */ +#define CTIMER_INTSTAT_CTMRB3C1INT_Msk (0x800000UL) /*!< CTIMER INTSTAT: CTMRB3C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRA3C1INT_Pos (22UL) /*!< CTIMER INTSTAT: CTMRA3C1INT (Bit 22) */ +#define CTIMER_INTSTAT_CTMRA3C1INT_Msk (0x400000UL) /*!< CTIMER INTSTAT: CTMRA3C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRB2C1INT_Pos (21UL) /*!< CTIMER INTSTAT: CTMRB2C1INT (Bit 21) */ +#define CTIMER_INTSTAT_CTMRB2C1INT_Msk (0x200000UL) /*!< CTIMER INTSTAT: CTMRB2C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRA2C1INT_Pos (20UL) /*!< CTIMER INTSTAT: CTMRA2C1INT (Bit 20) */ +#define CTIMER_INTSTAT_CTMRA2C1INT_Msk (0x100000UL) /*!< CTIMER INTSTAT: CTMRA2C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRB1C1INT_Pos (19UL) /*!< CTIMER INTSTAT: CTMRB1C1INT (Bit 19) */ +#define CTIMER_INTSTAT_CTMRB1C1INT_Msk (0x80000UL) /*!< CTIMER INTSTAT: CTMRB1C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRA1C1INT_Pos (18UL) /*!< CTIMER INTSTAT: CTMRA1C1INT (Bit 18) */ +#define CTIMER_INTSTAT_CTMRA1C1INT_Msk (0x40000UL) /*!< CTIMER INTSTAT: CTMRA1C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRB0C1INT_Pos (17UL) /*!< CTIMER INTSTAT: CTMRB0C1INT (Bit 17) */ +#define CTIMER_INTSTAT_CTMRB0C1INT_Msk (0x20000UL) /*!< CTIMER INTSTAT: CTMRB0C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRA0C1INT_Pos (16UL) /*!< CTIMER INTSTAT: CTMRA0C1INT (Bit 16) */ +#define CTIMER_INTSTAT_CTMRA0C1INT_Msk (0x10000UL) /*!< CTIMER INTSTAT: CTMRA0C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRB7C0INT_Pos (15UL) /*!< CTIMER INTSTAT: CTMRB7C0INT (Bit 15) */ +#define CTIMER_INTSTAT_CTMRB7C0INT_Msk (0x8000UL) /*!< CTIMER INTSTAT: CTMRB7C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRA7C0INT_Pos (14UL) /*!< CTIMER INTSTAT: CTMRA7C0INT (Bit 14) */ +#define CTIMER_INTSTAT_CTMRA7C0INT_Msk (0x4000UL) /*!< CTIMER INTSTAT: CTMRA7C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRB6C0INT_Pos (13UL) /*!< CTIMER INTSTAT: CTMRB6C0INT (Bit 13) */ +#define CTIMER_INTSTAT_CTMRB6C0INT_Msk (0x2000UL) /*!< CTIMER INTSTAT: CTMRB6C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRA6C0INT_Pos (12UL) /*!< CTIMER INTSTAT: CTMRA6C0INT (Bit 12) */ +#define CTIMER_INTSTAT_CTMRA6C0INT_Msk (0x1000UL) /*!< CTIMER INTSTAT: CTMRA6C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRB5C0INT_Pos (11UL) /*!< CTIMER INTSTAT: CTMRB5C0INT (Bit 11) */ +#define CTIMER_INTSTAT_CTMRB5C0INT_Msk (0x800UL) /*!< CTIMER INTSTAT: CTMRB5C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRA5C0INT_Pos (10UL) /*!< CTIMER INTSTAT: CTMRA5C0INT (Bit 10) */ +#define CTIMER_INTSTAT_CTMRA5C0INT_Msk (0x400UL) /*!< CTIMER INTSTAT: CTMRA5C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRB4C0INT_Pos (9UL) /*!< CTIMER INTSTAT: CTMRB4C0INT (Bit 9) */ +#define CTIMER_INTSTAT_CTMRB4C0INT_Msk (0x200UL) /*!< CTIMER INTSTAT: CTMRB4C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRA4C0INT_Pos (8UL) /*!< CTIMER INTSTAT: CTMRA4C0INT (Bit 8) */ +#define CTIMER_INTSTAT_CTMRA4C0INT_Msk (0x100UL) /*!< CTIMER INTSTAT: CTMRA4C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRB3C0INT_Pos (7UL) /*!< CTIMER INTSTAT: CTMRB3C0INT (Bit 7) */ +#define CTIMER_INTSTAT_CTMRB3C0INT_Msk (0x80UL) /*!< CTIMER INTSTAT: CTMRB3C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRA3C0INT_Pos (6UL) /*!< CTIMER INTSTAT: CTMRA3C0INT (Bit 6) */ +#define CTIMER_INTSTAT_CTMRA3C0INT_Msk (0x40UL) /*!< CTIMER INTSTAT: CTMRA3C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRB2C0INT_Pos (5UL) /*!< CTIMER INTSTAT: CTMRB2C0INT (Bit 5) */ +#define CTIMER_INTSTAT_CTMRB2C0INT_Msk (0x20UL) /*!< CTIMER INTSTAT: CTMRB2C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRA2C0INT_Pos (4UL) /*!< CTIMER INTSTAT: CTMRA2C0INT (Bit 4) */ +#define CTIMER_INTSTAT_CTMRA2C0INT_Msk (0x10UL) /*!< CTIMER INTSTAT: CTMRA2C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRB1C0INT_Pos (3UL) /*!< CTIMER INTSTAT: CTMRB1C0INT (Bit 3) */ +#define CTIMER_INTSTAT_CTMRB1C0INT_Msk (0x8UL) /*!< CTIMER INTSTAT: CTMRB1C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRA1C0INT_Pos (2UL) /*!< CTIMER INTSTAT: CTMRA1C0INT (Bit 2) */ +#define CTIMER_INTSTAT_CTMRA1C0INT_Msk (0x4UL) /*!< CTIMER INTSTAT: CTMRA1C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRB0C0INT_Pos (1UL) /*!< CTIMER INTSTAT: CTMRB0C0INT (Bit 1) */ +#define CTIMER_INTSTAT_CTMRB0C0INT_Msk (0x2UL) /*!< CTIMER INTSTAT: CTMRB0C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSTAT_CTMRA0C0INT_Pos (0UL) /*!< CTIMER INTSTAT: CTMRA0C0INT (Bit 0) */ +#define CTIMER_INTSTAT_CTMRA0C0INT_Msk (0x1UL) /*!< CTIMER INTSTAT: CTMRA0C0INT (Bitfield-Mask: 0x01) */ +/* ======================================================== INTCLR ========================================================= */ +#define CTIMER_INTCLR_CTMRB7C1INT_Pos (31UL) /*!< CTIMER INTCLR: CTMRB7C1INT (Bit 31) */ +#define CTIMER_INTCLR_CTMRB7C1INT_Msk (0x80000000UL) /*!< CTIMER INTCLR: CTMRB7C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRA7C1INT_Pos (30UL) /*!< CTIMER INTCLR: CTMRA7C1INT (Bit 30) */ +#define CTIMER_INTCLR_CTMRA7C1INT_Msk (0x40000000UL) /*!< CTIMER INTCLR: CTMRA7C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRB6C1INT_Pos (29UL) /*!< CTIMER INTCLR: CTMRB6C1INT (Bit 29) */ +#define CTIMER_INTCLR_CTMRB6C1INT_Msk (0x20000000UL) /*!< CTIMER INTCLR: CTMRB6C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRA6C1INT_Pos (28UL) /*!< CTIMER INTCLR: CTMRA6C1INT (Bit 28) */ +#define CTIMER_INTCLR_CTMRA6C1INT_Msk (0x10000000UL) /*!< CTIMER INTCLR: CTMRA6C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRB5C1INT_Pos (27UL) /*!< CTIMER INTCLR: CTMRB5C1INT (Bit 27) */ +#define CTIMER_INTCLR_CTMRB5C1INT_Msk (0x8000000UL) /*!< CTIMER INTCLR: CTMRB5C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRA5C1INT_Pos (26UL) /*!< CTIMER INTCLR: CTMRA5C1INT (Bit 26) */ +#define CTIMER_INTCLR_CTMRA5C1INT_Msk (0x4000000UL) /*!< CTIMER INTCLR: CTMRA5C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRB4C1INT_Pos (25UL) /*!< CTIMER INTCLR: CTMRB4C1INT (Bit 25) */ +#define CTIMER_INTCLR_CTMRB4C1INT_Msk (0x2000000UL) /*!< CTIMER INTCLR: CTMRB4C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRA4C1INT_Pos (24UL) /*!< CTIMER INTCLR: CTMRA4C1INT (Bit 24) */ +#define CTIMER_INTCLR_CTMRA4C1INT_Msk (0x1000000UL) /*!< CTIMER INTCLR: CTMRA4C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRB3C1INT_Pos (23UL) /*!< CTIMER INTCLR: CTMRB3C1INT (Bit 23) */ +#define CTIMER_INTCLR_CTMRB3C1INT_Msk (0x800000UL) /*!< CTIMER INTCLR: CTMRB3C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRA3C1INT_Pos (22UL) /*!< CTIMER INTCLR: CTMRA3C1INT (Bit 22) */ +#define CTIMER_INTCLR_CTMRA3C1INT_Msk (0x400000UL) /*!< CTIMER INTCLR: CTMRA3C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRB2C1INT_Pos (21UL) /*!< CTIMER INTCLR: CTMRB2C1INT (Bit 21) */ +#define CTIMER_INTCLR_CTMRB2C1INT_Msk (0x200000UL) /*!< CTIMER INTCLR: CTMRB2C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRA2C1INT_Pos (20UL) /*!< CTIMER INTCLR: CTMRA2C1INT (Bit 20) */ +#define CTIMER_INTCLR_CTMRA2C1INT_Msk (0x100000UL) /*!< CTIMER INTCLR: CTMRA2C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRB1C1INT_Pos (19UL) /*!< CTIMER INTCLR: CTMRB1C1INT (Bit 19) */ +#define CTIMER_INTCLR_CTMRB1C1INT_Msk (0x80000UL) /*!< CTIMER INTCLR: CTMRB1C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRA1C1INT_Pos (18UL) /*!< CTIMER INTCLR: CTMRA1C1INT (Bit 18) */ +#define CTIMER_INTCLR_CTMRA1C1INT_Msk (0x40000UL) /*!< CTIMER INTCLR: CTMRA1C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRB0C1INT_Pos (17UL) /*!< CTIMER INTCLR: CTMRB0C1INT (Bit 17) */ +#define CTIMER_INTCLR_CTMRB0C1INT_Msk (0x20000UL) /*!< CTIMER INTCLR: CTMRB0C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRA0C1INT_Pos (16UL) /*!< CTIMER INTCLR: CTMRA0C1INT (Bit 16) */ +#define CTIMER_INTCLR_CTMRA0C1INT_Msk (0x10000UL) /*!< CTIMER INTCLR: CTMRA0C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRB7C0INT_Pos (15UL) /*!< CTIMER INTCLR: CTMRB7C0INT (Bit 15) */ +#define CTIMER_INTCLR_CTMRB7C0INT_Msk (0x8000UL) /*!< CTIMER INTCLR: CTMRB7C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRA7C0INT_Pos (14UL) /*!< CTIMER INTCLR: CTMRA7C0INT (Bit 14) */ +#define CTIMER_INTCLR_CTMRA7C0INT_Msk (0x4000UL) /*!< CTIMER INTCLR: CTMRA7C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRB6C0INT_Pos (13UL) /*!< CTIMER INTCLR: CTMRB6C0INT (Bit 13) */ +#define CTIMER_INTCLR_CTMRB6C0INT_Msk (0x2000UL) /*!< CTIMER INTCLR: CTMRB6C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRA6C0INT_Pos (12UL) /*!< CTIMER INTCLR: CTMRA6C0INT (Bit 12) */ +#define CTIMER_INTCLR_CTMRA6C0INT_Msk (0x1000UL) /*!< CTIMER INTCLR: CTMRA6C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRB5C0INT_Pos (11UL) /*!< CTIMER INTCLR: CTMRB5C0INT (Bit 11) */ +#define CTIMER_INTCLR_CTMRB5C0INT_Msk (0x800UL) /*!< CTIMER INTCLR: CTMRB5C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRA5C0INT_Pos (10UL) /*!< CTIMER INTCLR: CTMRA5C0INT (Bit 10) */ +#define CTIMER_INTCLR_CTMRA5C0INT_Msk (0x400UL) /*!< CTIMER INTCLR: CTMRA5C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRB4C0INT_Pos (9UL) /*!< CTIMER INTCLR: CTMRB4C0INT (Bit 9) */ +#define CTIMER_INTCLR_CTMRB4C0INT_Msk (0x200UL) /*!< CTIMER INTCLR: CTMRB4C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRA4C0INT_Pos (8UL) /*!< CTIMER INTCLR: CTMRA4C0INT (Bit 8) */ +#define CTIMER_INTCLR_CTMRA4C0INT_Msk (0x100UL) /*!< CTIMER INTCLR: CTMRA4C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRB3C0INT_Pos (7UL) /*!< CTIMER INTCLR: CTMRB3C0INT (Bit 7) */ +#define CTIMER_INTCLR_CTMRB3C0INT_Msk (0x80UL) /*!< CTIMER INTCLR: CTMRB3C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRA3C0INT_Pos (6UL) /*!< CTIMER INTCLR: CTMRA3C0INT (Bit 6) */ +#define CTIMER_INTCLR_CTMRA3C0INT_Msk (0x40UL) /*!< CTIMER INTCLR: CTMRA3C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRB2C0INT_Pos (5UL) /*!< CTIMER INTCLR: CTMRB2C0INT (Bit 5) */ +#define CTIMER_INTCLR_CTMRB2C0INT_Msk (0x20UL) /*!< CTIMER INTCLR: CTMRB2C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRA2C0INT_Pos (4UL) /*!< CTIMER INTCLR: CTMRA2C0INT (Bit 4) */ +#define CTIMER_INTCLR_CTMRA2C0INT_Msk (0x10UL) /*!< CTIMER INTCLR: CTMRA2C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRB1C0INT_Pos (3UL) /*!< CTIMER INTCLR: CTMRB1C0INT (Bit 3) */ +#define CTIMER_INTCLR_CTMRB1C0INT_Msk (0x8UL) /*!< CTIMER INTCLR: CTMRB1C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRA1C0INT_Pos (2UL) /*!< CTIMER INTCLR: CTMRA1C0INT (Bit 2) */ +#define CTIMER_INTCLR_CTMRA1C0INT_Msk (0x4UL) /*!< CTIMER INTCLR: CTMRA1C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRB0C0INT_Pos (1UL) /*!< CTIMER INTCLR: CTMRB0C0INT (Bit 1) */ +#define CTIMER_INTCLR_CTMRB0C0INT_Msk (0x2UL) /*!< CTIMER INTCLR: CTMRB0C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTCLR_CTMRA0C0INT_Pos (0UL) /*!< CTIMER INTCLR: CTMRA0C0INT (Bit 0) */ +#define CTIMER_INTCLR_CTMRA0C0INT_Msk (0x1UL) /*!< CTIMER INTCLR: CTMRA0C0INT (Bitfield-Mask: 0x01) */ +/* ======================================================== INTSET ========================================================= */ +#define CTIMER_INTSET_CTMRB7C1INT_Pos (31UL) /*!< CTIMER INTSET: CTMRB7C1INT (Bit 31) */ +#define CTIMER_INTSET_CTMRB7C1INT_Msk (0x80000000UL) /*!< CTIMER INTSET: CTMRB7C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRA7C1INT_Pos (30UL) /*!< CTIMER INTSET: CTMRA7C1INT (Bit 30) */ +#define CTIMER_INTSET_CTMRA7C1INT_Msk (0x40000000UL) /*!< CTIMER INTSET: CTMRA7C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRB6C1INT_Pos (29UL) /*!< CTIMER INTSET: CTMRB6C1INT (Bit 29) */ +#define CTIMER_INTSET_CTMRB6C1INT_Msk (0x20000000UL) /*!< CTIMER INTSET: CTMRB6C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRA6C1INT_Pos (28UL) /*!< CTIMER INTSET: CTMRA6C1INT (Bit 28) */ +#define CTIMER_INTSET_CTMRA6C1INT_Msk (0x10000000UL) /*!< CTIMER INTSET: CTMRA6C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRB5C1INT_Pos (27UL) /*!< CTIMER INTSET: CTMRB5C1INT (Bit 27) */ +#define CTIMER_INTSET_CTMRB5C1INT_Msk (0x8000000UL) /*!< CTIMER INTSET: CTMRB5C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRA5C1INT_Pos (26UL) /*!< CTIMER INTSET: CTMRA5C1INT (Bit 26) */ +#define CTIMER_INTSET_CTMRA5C1INT_Msk (0x4000000UL) /*!< CTIMER INTSET: CTMRA5C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRB4C1INT_Pos (25UL) /*!< CTIMER INTSET: CTMRB4C1INT (Bit 25) */ +#define CTIMER_INTSET_CTMRB4C1INT_Msk (0x2000000UL) /*!< CTIMER INTSET: CTMRB4C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRA4C1INT_Pos (24UL) /*!< CTIMER INTSET: CTMRA4C1INT (Bit 24) */ +#define CTIMER_INTSET_CTMRA4C1INT_Msk (0x1000000UL) /*!< CTIMER INTSET: CTMRA4C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRB3C1INT_Pos (23UL) /*!< CTIMER INTSET: CTMRB3C1INT (Bit 23) */ +#define CTIMER_INTSET_CTMRB3C1INT_Msk (0x800000UL) /*!< CTIMER INTSET: CTMRB3C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRA3C1INT_Pos (22UL) /*!< CTIMER INTSET: CTMRA3C1INT (Bit 22) */ +#define CTIMER_INTSET_CTMRA3C1INT_Msk (0x400000UL) /*!< CTIMER INTSET: CTMRA3C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRB2C1INT_Pos (21UL) /*!< CTIMER INTSET: CTMRB2C1INT (Bit 21) */ +#define CTIMER_INTSET_CTMRB2C1INT_Msk (0x200000UL) /*!< CTIMER INTSET: CTMRB2C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRA2C1INT_Pos (20UL) /*!< CTIMER INTSET: CTMRA2C1INT (Bit 20) */ +#define CTIMER_INTSET_CTMRA2C1INT_Msk (0x100000UL) /*!< CTIMER INTSET: CTMRA2C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRB1C1INT_Pos (19UL) /*!< CTIMER INTSET: CTMRB1C1INT (Bit 19) */ +#define CTIMER_INTSET_CTMRB1C1INT_Msk (0x80000UL) /*!< CTIMER INTSET: CTMRB1C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRA1C1INT_Pos (18UL) /*!< CTIMER INTSET: CTMRA1C1INT (Bit 18) */ +#define CTIMER_INTSET_CTMRA1C1INT_Msk (0x40000UL) /*!< CTIMER INTSET: CTMRA1C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRB0C1INT_Pos (17UL) /*!< CTIMER INTSET: CTMRB0C1INT (Bit 17) */ +#define CTIMER_INTSET_CTMRB0C1INT_Msk (0x20000UL) /*!< CTIMER INTSET: CTMRB0C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRA0C1INT_Pos (16UL) /*!< CTIMER INTSET: CTMRA0C1INT (Bit 16) */ +#define CTIMER_INTSET_CTMRA0C1INT_Msk (0x10000UL) /*!< CTIMER INTSET: CTMRA0C1INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRB7C0INT_Pos (15UL) /*!< CTIMER INTSET: CTMRB7C0INT (Bit 15) */ +#define CTIMER_INTSET_CTMRB7C0INT_Msk (0x8000UL) /*!< CTIMER INTSET: CTMRB7C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRA7C0INT_Pos (14UL) /*!< CTIMER INTSET: CTMRA7C0INT (Bit 14) */ +#define CTIMER_INTSET_CTMRA7C0INT_Msk (0x4000UL) /*!< CTIMER INTSET: CTMRA7C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRB6C0INT_Pos (13UL) /*!< CTIMER INTSET: CTMRB6C0INT (Bit 13) */ +#define CTIMER_INTSET_CTMRB6C0INT_Msk (0x2000UL) /*!< CTIMER INTSET: CTMRB6C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRA6C0INT_Pos (12UL) /*!< CTIMER INTSET: CTMRA6C0INT (Bit 12) */ +#define CTIMER_INTSET_CTMRA6C0INT_Msk (0x1000UL) /*!< CTIMER INTSET: CTMRA6C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRB5C0INT_Pos (11UL) /*!< CTIMER INTSET: CTMRB5C0INT (Bit 11) */ +#define CTIMER_INTSET_CTMRB5C0INT_Msk (0x800UL) /*!< CTIMER INTSET: CTMRB5C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRA5C0INT_Pos (10UL) /*!< CTIMER INTSET: CTMRA5C0INT (Bit 10) */ +#define CTIMER_INTSET_CTMRA5C0INT_Msk (0x400UL) /*!< CTIMER INTSET: CTMRA5C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRB4C0INT_Pos (9UL) /*!< CTIMER INTSET: CTMRB4C0INT (Bit 9) */ +#define CTIMER_INTSET_CTMRB4C0INT_Msk (0x200UL) /*!< CTIMER INTSET: CTMRB4C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRA4C0INT_Pos (8UL) /*!< CTIMER INTSET: CTMRA4C0INT (Bit 8) */ +#define CTIMER_INTSET_CTMRA4C0INT_Msk (0x100UL) /*!< CTIMER INTSET: CTMRA4C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRB3C0INT_Pos (7UL) /*!< CTIMER INTSET: CTMRB3C0INT (Bit 7) */ +#define CTIMER_INTSET_CTMRB3C0INT_Msk (0x80UL) /*!< CTIMER INTSET: CTMRB3C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRA3C0INT_Pos (6UL) /*!< CTIMER INTSET: CTMRA3C0INT (Bit 6) */ +#define CTIMER_INTSET_CTMRA3C0INT_Msk (0x40UL) /*!< CTIMER INTSET: CTMRA3C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRB2C0INT_Pos (5UL) /*!< CTIMER INTSET: CTMRB2C0INT (Bit 5) */ +#define CTIMER_INTSET_CTMRB2C0INT_Msk (0x20UL) /*!< CTIMER INTSET: CTMRB2C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRA2C0INT_Pos (4UL) /*!< CTIMER INTSET: CTMRA2C0INT (Bit 4) */ +#define CTIMER_INTSET_CTMRA2C0INT_Msk (0x10UL) /*!< CTIMER INTSET: CTMRA2C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRB1C0INT_Pos (3UL) /*!< CTIMER INTSET: CTMRB1C0INT (Bit 3) */ +#define CTIMER_INTSET_CTMRB1C0INT_Msk (0x8UL) /*!< CTIMER INTSET: CTMRB1C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRA1C0INT_Pos (2UL) /*!< CTIMER INTSET: CTMRA1C0INT (Bit 2) */ +#define CTIMER_INTSET_CTMRA1C0INT_Msk (0x4UL) /*!< CTIMER INTSET: CTMRA1C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRB0C0INT_Pos (1UL) /*!< CTIMER INTSET: CTMRB0C0INT (Bit 1) */ +#define CTIMER_INTSET_CTMRB0C0INT_Msk (0x2UL) /*!< CTIMER INTSET: CTMRB0C0INT (Bitfield-Mask: 0x01) */ +#define CTIMER_INTSET_CTMRA0C0INT_Pos (0UL) /*!< CTIMER INTSET: CTMRA0C0INT (Bit 0) */ +#define CTIMER_INTSET_CTMRA0C0INT_Msk (0x1UL) /*!< CTIMER INTSET: CTMRA0C0INT (Bitfield-Mask: 0x01) */ +/* ======================================================= STMINTEN ======================================================== */ +#define CTIMER_STMINTEN_CAPTURED_Pos (12UL) /*!< CTIMER STMINTEN: CAPTURED (Bit 12) */ +#define CTIMER_STMINTEN_CAPTURED_Msk (0x1000UL) /*!< CTIMER STMINTEN: CAPTURED (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTEN_CAPTUREC_Pos (11UL) /*!< CTIMER STMINTEN: CAPTUREC (Bit 11) */ +#define CTIMER_STMINTEN_CAPTUREC_Msk (0x800UL) /*!< CTIMER STMINTEN: CAPTUREC (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTEN_CAPTUREB_Pos (10UL) /*!< CTIMER STMINTEN: CAPTUREB (Bit 10) */ +#define CTIMER_STMINTEN_CAPTUREB_Msk (0x400UL) /*!< CTIMER STMINTEN: CAPTUREB (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTEN_CAPTUREA_Pos (9UL) /*!< CTIMER STMINTEN: CAPTUREA (Bit 9) */ +#define CTIMER_STMINTEN_CAPTUREA_Msk (0x200UL) /*!< CTIMER STMINTEN: CAPTUREA (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTEN_OVERFLOW_Pos (8UL) /*!< CTIMER STMINTEN: OVERFLOW (Bit 8) */ +#define CTIMER_STMINTEN_OVERFLOW_Msk (0x100UL) /*!< CTIMER STMINTEN: OVERFLOW (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTEN_COMPAREH_Pos (7UL) /*!< CTIMER STMINTEN: COMPAREH (Bit 7) */ +#define CTIMER_STMINTEN_COMPAREH_Msk (0x80UL) /*!< CTIMER STMINTEN: COMPAREH (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTEN_COMPAREG_Pos (6UL) /*!< CTIMER STMINTEN: COMPAREG (Bit 6) */ +#define CTIMER_STMINTEN_COMPAREG_Msk (0x40UL) /*!< CTIMER STMINTEN: COMPAREG (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTEN_COMPAREF_Pos (5UL) /*!< CTIMER STMINTEN: COMPAREF (Bit 5) */ +#define CTIMER_STMINTEN_COMPAREF_Msk (0x20UL) /*!< CTIMER STMINTEN: COMPAREF (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTEN_COMPAREE_Pos (4UL) /*!< CTIMER STMINTEN: COMPAREE (Bit 4) */ +#define CTIMER_STMINTEN_COMPAREE_Msk (0x10UL) /*!< CTIMER STMINTEN: COMPAREE (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTEN_COMPARED_Pos (3UL) /*!< CTIMER STMINTEN: COMPARED (Bit 3) */ +#define CTIMER_STMINTEN_COMPARED_Msk (0x8UL) /*!< CTIMER STMINTEN: COMPARED (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTEN_COMPAREC_Pos (2UL) /*!< CTIMER STMINTEN: COMPAREC (Bit 2) */ +#define CTIMER_STMINTEN_COMPAREC_Msk (0x4UL) /*!< CTIMER STMINTEN: COMPAREC (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTEN_COMPAREB_Pos (1UL) /*!< CTIMER STMINTEN: COMPAREB (Bit 1) */ +#define CTIMER_STMINTEN_COMPAREB_Msk (0x2UL) /*!< CTIMER STMINTEN: COMPAREB (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTEN_COMPAREA_Pos (0UL) /*!< CTIMER STMINTEN: COMPAREA (Bit 0) */ +#define CTIMER_STMINTEN_COMPAREA_Msk (0x1UL) /*!< CTIMER STMINTEN: COMPAREA (Bitfield-Mask: 0x01) */ +/* ====================================================== STMINTSTAT ======================================================= */ +#define CTIMER_STMINTSTAT_CAPTURED_Pos (12UL) /*!< CTIMER STMINTSTAT: CAPTURED (Bit 12) */ +#define CTIMER_STMINTSTAT_CAPTURED_Msk (0x1000UL) /*!< CTIMER STMINTSTAT: CAPTURED (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTSTAT_CAPTUREC_Pos (11UL) /*!< CTIMER STMINTSTAT: CAPTUREC (Bit 11) */ +#define CTIMER_STMINTSTAT_CAPTUREC_Msk (0x800UL) /*!< CTIMER STMINTSTAT: CAPTUREC (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTSTAT_CAPTUREB_Pos (10UL) /*!< CTIMER STMINTSTAT: CAPTUREB (Bit 10) */ +#define CTIMER_STMINTSTAT_CAPTUREB_Msk (0x400UL) /*!< CTIMER STMINTSTAT: CAPTUREB (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTSTAT_CAPTUREA_Pos (9UL) /*!< CTIMER STMINTSTAT: CAPTUREA (Bit 9) */ +#define CTIMER_STMINTSTAT_CAPTUREA_Msk (0x200UL) /*!< CTIMER STMINTSTAT: CAPTUREA (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTSTAT_OVERFLOW_Pos (8UL) /*!< CTIMER STMINTSTAT: OVERFLOW (Bit 8) */ +#define CTIMER_STMINTSTAT_OVERFLOW_Msk (0x100UL) /*!< CTIMER STMINTSTAT: OVERFLOW (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTSTAT_COMPAREH_Pos (7UL) /*!< CTIMER STMINTSTAT: COMPAREH (Bit 7) */ +#define CTIMER_STMINTSTAT_COMPAREH_Msk (0x80UL) /*!< CTIMER STMINTSTAT: COMPAREH (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTSTAT_COMPAREG_Pos (6UL) /*!< CTIMER STMINTSTAT: COMPAREG (Bit 6) */ +#define CTIMER_STMINTSTAT_COMPAREG_Msk (0x40UL) /*!< CTIMER STMINTSTAT: COMPAREG (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTSTAT_COMPAREF_Pos (5UL) /*!< CTIMER STMINTSTAT: COMPAREF (Bit 5) */ +#define CTIMER_STMINTSTAT_COMPAREF_Msk (0x20UL) /*!< CTIMER STMINTSTAT: COMPAREF (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTSTAT_COMPAREE_Pos (4UL) /*!< CTIMER STMINTSTAT: COMPAREE (Bit 4) */ +#define CTIMER_STMINTSTAT_COMPAREE_Msk (0x10UL) /*!< CTIMER STMINTSTAT: COMPAREE (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTSTAT_COMPARED_Pos (3UL) /*!< CTIMER STMINTSTAT: COMPARED (Bit 3) */ +#define CTIMER_STMINTSTAT_COMPARED_Msk (0x8UL) /*!< CTIMER STMINTSTAT: COMPARED (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTSTAT_COMPAREC_Pos (2UL) /*!< CTIMER STMINTSTAT: COMPAREC (Bit 2) */ +#define CTIMER_STMINTSTAT_COMPAREC_Msk (0x4UL) /*!< CTIMER STMINTSTAT: COMPAREC (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTSTAT_COMPAREB_Pos (1UL) /*!< CTIMER STMINTSTAT: COMPAREB (Bit 1) */ +#define CTIMER_STMINTSTAT_COMPAREB_Msk (0x2UL) /*!< CTIMER STMINTSTAT: COMPAREB (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTSTAT_COMPAREA_Pos (0UL) /*!< CTIMER STMINTSTAT: COMPAREA (Bit 0) */ +#define CTIMER_STMINTSTAT_COMPAREA_Msk (0x1UL) /*!< CTIMER STMINTSTAT: COMPAREA (Bitfield-Mask: 0x01) */ +/* ======================================================= STMINTCLR ======================================================= */ +#define CTIMER_STMINTCLR_CAPTURED_Pos (12UL) /*!< CTIMER STMINTCLR: CAPTURED (Bit 12) */ +#define CTIMER_STMINTCLR_CAPTURED_Msk (0x1000UL) /*!< CTIMER STMINTCLR: CAPTURED (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTCLR_CAPTUREC_Pos (11UL) /*!< CTIMER STMINTCLR: CAPTUREC (Bit 11) */ +#define CTIMER_STMINTCLR_CAPTUREC_Msk (0x800UL) /*!< CTIMER STMINTCLR: CAPTUREC (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTCLR_CAPTUREB_Pos (10UL) /*!< CTIMER STMINTCLR: CAPTUREB (Bit 10) */ +#define CTIMER_STMINTCLR_CAPTUREB_Msk (0x400UL) /*!< CTIMER STMINTCLR: CAPTUREB (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTCLR_CAPTUREA_Pos (9UL) /*!< CTIMER STMINTCLR: CAPTUREA (Bit 9) */ +#define CTIMER_STMINTCLR_CAPTUREA_Msk (0x200UL) /*!< CTIMER STMINTCLR: CAPTUREA (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTCLR_OVERFLOW_Pos (8UL) /*!< CTIMER STMINTCLR: OVERFLOW (Bit 8) */ +#define CTIMER_STMINTCLR_OVERFLOW_Msk (0x100UL) /*!< CTIMER STMINTCLR: OVERFLOW (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTCLR_COMPAREH_Pos (7UL) /*!< CTIMER STMINTCLR: COMPAREH (Bit 7) */ +#define CTIMER_STMINTCLR_COMPAREH_Msk (0x80UL) /*!< CTIMER STMINTCLR: COMPAREH (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTCLR_COMPAREG_Pos (6UL) /*!< CTIMER STMINTCLR: COMPAREG (Bit 6) */ +#define CTIMER_STMINTCLR_COMPAREG_Msk (0x40UL) /*!< CTIMER STMINTCLR: COMPAREG (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTCLR_COMPAREF_Pos (5UL) /*!< CTIMER STMINTCLR: COMPAREF (Bit 5) */ +#define CTIMER_STMINTCLR_COMPAREF_Msk (0x20UL) /*!< CTIMER STMINTCLR: COMPAREF (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTCLR_COMPAREE_Pos (4UL) /*!< CTIMER STMINTCLR: COMPAREE (Bit 4) */ +#define CTIMER_STMINTCLR_COMPAREE_Msk (0x10UL) /*!< CTIMER STMINTCLR: COMPAREE (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTCLR_COMPARED_Pos (3UL) /*!< CTIMER STMINTCLR: COMPARED (Bit 3) */ +#define CTIMER_STMINTCLR_COMPARED_Msk (0x8UL) /*!< CTIMER STMINTCLR: COMPARED (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTCLR_COMPAREC_Pos (2UL) /*!< CTIMER STMINTCLR: COMPAREC (Bit 2) */ +#define CTIMER_STMINTCLR_COMPAREC_Msk (0x4UL) /*!< CTIMER STMINTCLR: COMPAREC (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTCLR_COMPAREB_Pos (1UL) /*!< CTIMER STMINTCLR: COMPAREB (Bit 1) */ +#define CTIMER_STMINTCLR_COMPAREB_Msk (0x2UL) /*!< CTIMER STMINTCLR: COMPAREB (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTCLR_COMPAREA_Pos (0UL) /*!< CTIMER STMINTCLR: COMPAREA (Bit 0) */ +#define CTIMER_STMINTCLR_COMPAREA_Msk (0x1UL) /*!< CTIMER STMINTCLR: COMPAREA (Bitfield-Mask: 0x01) */ +/* ======================================================= STMINTSET ======================================================= */ +#define CTIMER_STMINTSET_CAPTURED_Pos (12UL) /*!< CTIMER STMINTSET: CAPTURED (Bit 12) */ +#define CTIMER_STMINTSET_CAPTURED_Msk (0x1000UL) /*!< CTIMER STMINTSET: CAPTURED (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTSET_CAPTUREC_Pos (11UL) /*!< CTIMER STMINTSET: CAPTUREC (Bit 11) */ +#define CTIMER_STMINTSET_CAPTUREC_Msk (0x800UL) /*!< CTIMER STMINTSET: CAPTUREC (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTSET_CAPTUREB_Pos (10UL) /*!< CTIMER STMINTSET: CAPTUREB (Bit 10) */ +#define CTIMER_STMINTSET_CAPTUREB_Msk (0x400UL) /*!< CTIMER STMINTSET: CAPTUREB (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTSET_CAPTUREA_Pos (9UL) /*!< CTIMER STMINTSET: CAPTUREA (Bit 9) */ +#define CTIMER_STMINTSET_CAPTUREA_Msk (0x200UL) /*!< CTIMER STMINTSET: CAPTUREA (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTSET_OVERFLOW_Pos (8UL) /*!< CTIMER STMINTSET: OVERFLOW (Bit 8) */ +#define CTIMER_STMINTSET_OVERFLOW_Msk (0x100UL) /*!< CTIMER STMINTSET: OVERFLOW (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTSET_COMPAREH_Pos (7UL) /*!< CTIMER STMINTSET: COMPAREH (Bit 7) */ +#define CTIMER_STMINTSET_COMPAREH_Msk (0x80UL) /*!< CTIMER STMINTSET: COMPAREH (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTSET_COMPAREG_Pos (6UL) /*!< CTIMER STMINTSET: COMPAREG (Bit 6) */ +#define CTIMER_STMINTSET_COMPAREG_Msk (0x40UL) /*!< CTIMER STMINTSET: COMPAREG (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTSET_COMPAREF_Pos (5UL) /*!< CTIMER STMINTSET: COMPAREF (Bit 5) */ +#define CTIMER_STMINTSET_COMPAREF_Msk (0x20UL) /*!< CTIMER STMINTSET: COMPAREF (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTSET_COMPAREE_Pos (4UL) /*!< CTIMER STMINTSET: COMPAREE (Bit 4) */ +#define CTIMER_STMINTSET_COMPAREE_Msk (0x10UL) /*!< CTIMER STMINTSET: COMPAREE (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTSET_COMPARED_Pos (3UL) /*!< CTIMER STMINTSET: COMPARED (Bit 3) */ +#define CTIMER_STMINTSET_COMPARED_Msk (0x8UL) /*!< CTIMER STMINTSET: COMPARED (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTSET_COMPAREC_Pos (2UL) /*!< CTIMER STMINTSET: COMPAREC (Bit 2) */ +#define CTIMER_STMINTSET_COMPAREC_Msk (0x4UL) /*!< CTIMER STMINTSET: COMPAREC (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTSET_COMPAREB_Pos (1UL) /*!< CTIMER STMINTSET: COMPAREB (Bit 1) */ +#define CTIMER_STMINTSET_COMPAREB_Msk (0x2UL) /*!< CTIMER STMINTSET: COMPAREB (Bitfield-Mask: 0x01) */ +#define CTIMER_STMINTSET_COMPAREA_Pos (0UL) /*!< CTIMER STMINTSET: COMPAREA (Bit 0) */ +#define CTIMER_STMINTSET_COMPAREA_Msk (0x1UL) /*!< CTIMER STMINTSET: COMPAREA (Bitfield-Mask: 0x01) */ + + +/* =========================================================================================================================== */ +/* ================ GPIO ================ */ +/* =========================================================================================================================== */ + +/* ======================================================== PADREGA ======================================================== */ +#define GPIO_PADREGA_PAD3PWRUP_Pos (30UL) /*!< GPIO PADREGA: PAD3PWRUP (Bit 30) */ +#define GPIO_PADREGA_PAD3PWRUP_Msk (0x40000000UL) /*!< GPIO PADREGA: PAD3PWRUP (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGA_PAD3FNCSEL_Pos (27UL) /*!< GPIO PADREGA: PAD3FNCSEL (Bit 27) */ +#define GPIO_PADREGA_PAD3FNCSEL_Msk (0x38000000UL) /*!< GPIO PADREGA: PAD3FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGA_PAD3STRNG_Pos (26UL) /*!< GPIO PADREGA: PAD3STRNG (Bit 26) */ +#define GPIO_PADREGA_PAD3STRNG_Msk (0x4000000UL) /*!< GPIO PADREGA: PAD3STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGA_PAD3INPEN_Pos (25UL) /*!< GPIO PADREGA: PAD3INPEN (Bit 25) */ +#define GPIO_PADREGA_PAD3INPEN_Msk (0x2000000UL) /*!< GPIO PADREGA: PAD3INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGA_PAD3PULL_Pos (24UL) /*!< GPIO PADREGA: PAD3PULL (Bit 24) */ +#define GPIO_PADREGA_PAD3PULL_Msk (0x1000000UL) /*!< GPIO PADREGA: PAD3PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGA_PAD2FNCSEL_Pos (19UL) /*!< GPIO PADREGA: PAD2FNCSEL (Bit 19) */ +#define GPIO_PADREGA_PAD2FNCSEL_Msk (0x380000UL) /*!< GPIO PADREGA: PAD2FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGA_PAD2STRNG_Pos (18UL) /*!< GPIO PADREGA: PAD2STRNG (Bit 18) */ +#define GPIO_PADREGA_PAD2STRNG_Msk (0x40000UL) /*!< GPIO PADREGA: PAD2STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGA_PAD2INPEN_Pos (17UL) /*!< GPIO PADREGA: PAD2INPEN (Bit 17) */ +#define GPIO_PADREGA_PAD2INPEN_Msk (0x20000UL) /*!< GPIO PADREGA: PAD2INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGA_PAD2PULL_Pos (16UL) /*!< GPIO PADREGA: PAD2PULL (Bit 16) */ +#define GPIO_PADREGA_PAD2PULL_Msk (0x10000UL) /*!< GPIO PADREGA: PAD2PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGA_PAD1RSEL_Pos (14UL) /*!< GPIO PADREGA: PAD1RSEL (Bit 14) */ +#define GPIO_PADREGA_PAD1RSEL_Msk (0xc000UL) /*!< GPIO PADREGA: PAD1RSEL (Bitfield-Mask: 0x03) */ +#define GPIO_PADREGA_PAD1FNCSEL_Pos (11UL) /*!< GPIO PADREGA: PAD1FNCSEL (Bit 11) */ +#define GPIO_PADREGA_PAD1FNCSEL_Msk (0x3800UL) /*!< GPIO PADREGA: PAD1FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGA_PAD1STRNG_Pos (10UL) /*!< GPIO PADREGA: PAD1STRNG (Bit 10) */ +#define GPIO_PADREGA_PAD1STRNG_Msk (0x400UL) /*!< GPIO PADREGA: PAD1STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGA_PAD1INPEN_Pos (9UL) /*!< GPIO PADREGA: PAD1INPEN (Bit 9) */ +#define GPIO_PADREGA_PAD1INPEN_Msk (0x200UL) /*!< GPIO PADREGA: PAD1INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGA_PAD1PULL_Pos (8UL) /*!< GPIO PADREGA: PAD1PULL (Bit 8) */ +#define GPIO_PADREGA_PAD1PULL_Msk (0x100UL) /*!< GPIO PADREGA: PAD1PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGA_PAD0RSEL_Pos (6UL) /*!< GPIO PADREGA: PAD0RSEL (Bit 6) */ +#define GPIO_PADREGA_PAD0RSEL_Msk (0xc0UL) /*!< GPIO PADREGA: PAD0RSEL (Bitfield-Mask: 0x03) */ +#define GPIO_PADREGA_PAD0FNCSEL_Pos (3UL) /*!< GPIO PADREGA: PAD0FNCSEL (Bit 3) */ +#define GPIO_PADREGA_PAD0FNCSEL_Msk (0x38UL) /*!< GPIO PADREGA: PAD0FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGA_PAD0STRNG_Pos (2UL) /*!< GPIO PADREGA: PAD0STRNG (Bit 2) */ +#define GPIO_PADREGA_PAD0STRNG_Msk (0x4UL) /*!< GPIO PADREGA: PAD0STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGA_PAD0INPEN_Pos (1UL) /*!< GPIO PADREGA: PAD0INPEN (Bit 1) */ +#define GPIO_PADREGA_PAD0INPEN_Msk (0x2UL) /*!< GPIO PADREGA: PAD0INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGA_PAD0PULL_Pos (0UL) /*!< GPIO PADREGA: PAD0PULL (Bit 0) */ +#define GPIO_PADREGA_PAD0PULL_Msk (0x1UL) /*!< GPIO PADREGA: PAD0PULL (Bitfield-Mask: 0x01) */ +/* ======================================================== PADREGB ======================================================== */ +#define GPIO_PADREGB_PAD7FNCSEL_Pos (27UL) /*!< GPIO PADREGB: PAD7FNCSEL (Bit 27) */ +#define GPIO_PADREGB_PAD7FNCSEL_Msk (0x38000000UL) /*!< GPIO PADREGB: PAD7FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGB_PAD7STRNG_Pos (26UL) /*!< GPIO PADREGB: PAD7STRNG (Bit 26) */ +#define GPIO_PADREGB_PAD7STRNG_Msk (0x4000000UL) /*!< GPIO PADREGB: PAD7STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGB_PAD7INPEN_Pos (25UL) /*!< GPIO PADREGB: PAD7INPEN (Bit 25) */ +#define GPIO_PADREGB_PAD7INPEN_Msk (0x2000000UL) /*!< GPIO PADREGB: PAD7INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGB_PAD7PULL_Pos (24UL) /*!< GPIO PADREGB: PAD7PULL (Bit 24) */ +#define GPIO_PADREGB_PAD7PULL_Msk (0x1000000UL) /*!< GPIO PADREGB: PAD7PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGB_PAD6RSEL_Pos (22UL) /*!< GPIO PADREGB: PAD6RSEL (Bit 22) */ +#define GPIO_PADREGB_PAD6RSEL_Msk (0xc00000UL) /*!< GPIO PADREGB: PAD6RSEL (Bitfield-Mask: 0x03) */ +#define GPIO_PADREGB_PAD6FNCSEL_Pos (19UL) /*!< GPIO PADREGB: PAD6FNCSEL (Bit 19) */ +#define GPIO_PADREGB_PAD6FNCSEL_Msk (0x380000UL) /*!< GPIO PADREGB: PAD6FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGB_PAD6STRNG_Pos (18UL) /*!< GPIO PADREGB: PAD6STRNG (Bit 18) */ +#define GPIO_PADREGB_PAD6STRNG_Msk (0x40000UL) /*!< GPIO PADREGB: PAD6STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGB_PAD6INPEN_Pos (17UL) /*!< GPIO PADREGB: PAD6INPEN (Bit 17) */ +#define GPIO_PADREGB_PAD6INPEN_Msk (0x20000UL) /*!< GPIO PADREGB: PAD6INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGB_PAD6PULL_Pos (16UL) /*!< GPIO PADREGB: PAD6PULL (Bit 16) */ +#define GPIO_PADREGB_PAD6PULL_Msk (0x10000UL) /*!< GPIO PADREGB: PAD6PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGB_PAD5RSEL_Pos (14UL) /*!< GPIO PADREGB: PAD5RSEL (Bit 14) */ +#define GPIO_PADREGB_PAD5RSEL_Msk (0xc000UL) /*!< GPIO PADREGB: PAD5RSEL (Bitfield-Mask: 0x03) */ +#define GPIO_PADREGB_PAD5FNCSEL_Pos (11UL) /*!< GPIO PADREGB: PAD5FNCSEL (Bit 11) */ +#define GPIO_PADREGB_PAD5FNCSEL_Msk (0x3800UL) /*!< GPIO PADREGB: PAD5FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGB_PAD5STRNG_Pos (10UL) /*!< GPIO PADREGB: PAD5STRNG (Bit 10) */ +#define GPIO_PADREGB_PAD5STRNG_Msk (0x400UL) /*!< GPIO PADREGB: PAD5STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGB_PAD5INPEN_Pos (9UL) /*!< GPIO PADREGB: PAD5INPEN (Bit 9) */ +#define GPIO_PADREGB_PAD5INPEN_Msk (0x200UL) /*!< GPIO PADREGB: PAD5INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGB_PAD5PULL_Pos (8UL) /*!< GPIO PADREGB: PAD5PULL (Bit 8) */ +#define GPIO_PADREGB_PAD5PULL_Msk (0x100UL) /*!< GPIO PADREGB: PAD5PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGB_PAD4FNCSEL_Pos (3UL) /*!< GPIO PADREGB: PAD4FNCSEL (Bit 3) */ +#define GPIO_PADREGB_PAD4FNCSEL_Msk (0x38UL) /*!< GPIO PADREGB: PAD4FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGB_PAD4STRNG_Pos (2UL) /*!< GPIO PADREGB: PAD4STRNG (Bit 2) */ +#define GPIO_PADREGB_PAD4STRNG_Msk (0x4UL) /*!< GPIO PADREGB: PAD4STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGB_PAD4INPEN_Pos (1UL) /*!< GPIO PADREGB: PAD4INPEN (Bit 1) */ +#define GPIO_PADREGB_PAD4INPEN_Msk (0x2UL) /*!< GPIO PADREGB: PAD4INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGB_PAD4PULL_Pos (0UL) /*!< GPIO PADREGB: PAD4PULL (Bit 0) */ +#define GPIO_PADREGB_PAD4PULL_Msk (0x1UL) /*!< GPIO PADREGB: PAD4PULL (Bitfield-Mask: 0x01) */ +/* ======================================================== PADREGC ======================================================== */ +#define GPIO_PADREGC_PAD11FNCSEL_Pos (27UL) /*!< GPIO PADREGC: PAD11FNCSEL (Bit 27) */ +#define GPIO_PADREGC_PAD11FNCSEL_Msk (0x38000000UL) /*!< GPIO PADREGC: PAD11FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGC_PAD11STRNG_Pos (26UL) /*!< GPIO PADREGC: PAD11STRNG (Bit 26) */ +#define GPIO_PADREGC_PAD11STRNG_Msk (0x4000000UL) /*!< GPIO PADREGC: PAD11STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGC_PAD11INPEN_Pos (25UL) /*!< GPIO PADREGC: PAD11INPEN (Bit 25) */ +#define GPIO_PADREGC_PAD11INPEN_Msk (0x2000000UL) /*!< GPIO PADREGC: PAD11INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGC_PAD11PULL_Pos (24UL) /*!< GPIO PADREGC: PAD11PULL (Bit 24) */ +#define GPIO_PADREGC_PAD11PULL_Msk (0x1000000UL) /*!< GPIO PADREGC: PAD11PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGC_PAD10FNCSEL_Pos (19UL) /*!< GPIO PADREGC: PAD10FNCSEL (Bit 19) */ +#define GPIO_PADREGC_PAD10FNCSEL_Msk (0x380000UL) /*!< GPIO PADREGC: PAD10FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGC_PAD10STRNG_Pos (18UL) /*!< GPIO PADREGC: PAD10STRNG (Bit 18) */ +#define GPIO_PADREGC_PAD10STRNG_Msk (0x40000UL) /*!< GPIO PADREGC: PAD10STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGC_PAD10INPEN_Pos (17UL) /*!< GPIO PADREGC: PAD10INPEN (Bit 17) */ +#define GPIO_PADREGC_PAD10INPEN_Msk (0x20000UL) /*!< GPIO PADREGC: PAD10INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGC_PAD10PULL_Pos (16UL) /*!< GPIO PADREGC: PAD10PULL (Bit 16) */ +#define GPIO_PADREGC_PAD10PULL_Msk (0x10000UL) /*!< GPIO PADREGC: PAD10PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGC_PAD9RSEL_Pos (14UL) /*!< GPIO PADREGC: PAD9RSEL (Bit 14) */ +#define GPIO_PADREGC_PAD9RSEL_Msk (0xc000UL) /*!< GPIO PADREGC: PAD9RSEL (Bitfield-Mask: 0x03) */ +#define GPIO_PADREGC_PAD9FNCSEL_Pos (11UL) /*!< GPIO PADREGC: PAD9FNCSEL (Bit 11) */ +#define GPIO_PADREGC_PAD9FNCSEL_Msk (0x3800UL) /*!< GPIO PADREGC: PAD9FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGC_PAD9STRNG_Pos (10UL) /*!< GPIO PADREGC: PAD9STRNG (Bit 10) */ +#define GPIO_PADREGC_PAD9STRNG_Msk (0x400UL) /*!< GPIO PADREGC: PAD9STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGC_PAD9INPEN_Pos (9UL) /*!< GPIO PADREGC: PAD9INPEN (Bit 9) */ +#define GPIO_PADREGC_PAD9INPEN_Msk (0x200UL) /*!< GPIO PADREGC: PAD9INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGC_PAD9PULL_Pos (8UL) /*!< GPIO PADREGC: PAD9PULL (Bit 8) */ +#define GPIO_PADREGC_PAD9PULL_Msk (0x100UL) /*!< GPIO PADREGC: PAD9PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGC_PAD8RSEL_Pos (6UL) /*!< GPIO PADREGC: PAD8RSEL (Bit 6) */ +#define GPIO_PADREGC_PAD8RSEL_Msk (0xc0UL) /*!< GPIO PADREGC: PAD8RSEL (Bitfield-Mask: 0x03) */ +#define GPIO_PADREGC_PAD8FNCSEL_Pos (3UL) /*!< GPIO PADREGC: PAD8FNCSEL (Bit 3) */ +#define GPIO_PADREGC_PAD8FNCSEL_Msk (0x38UL) /*!< GPIO PADREGC: PAD8FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGC_PAD8STRNG_Pos (2UL) /*!< GPIO PADREGC: PAD8STRNG (Bit 2) */ +#define GPIO_PADREGC_PAD8STRNG_Msk (0x4UL) /*!< GPIO PADREGC: PAD8STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGC_PAD8INPEN_Pos (1UL) /*!< GPIO PADREGC: PAD8INPEN (Bit 1) */ +#define GPIO_PADREGC_PAD8INPEN_Msk (0x2UL) /*!< GPIO PADREGC: PAD8INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGC_PAD8PULL_Pos (0UL) /*!< GPIO PADREGC: PAD8PULL (Bit 0) */ +#define GPIO_PADREGC_PAD8PULL_Msk (0x1UL) /*!< GPIO PADREGC: PAD8PULL (Bitfield-Mask: 0x01) */ +/* ======================================================== PADREGD ======================================================== */ +#define GPIO_PADREGD_PAD15FNCSEL_Pos (27UL) /*!< GPIO PADREGD: PAD15FNCSEL (Bit 27) */ +#define GPIO_PADREGD_PAD15FNCSEL_Msk (0x38000000UL) /*!< GPIO PADREGD: PAD15FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGD_PAD15STRNG_Pos (26UL) /*!< GPIO PADREGD: PAD15STRNG (Bit 26) */ +#define GPIO_PADREGD_PAD15STRNG_Msk (0x4000000UL) /*!< GPIO PADREGD: PAD15STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGD_PAD15INPEN_Pos (25UL) /*!< GPIO PADREGD: PAD15INPEN (Bit 25) */ +#define GPIO_PADREGD_PAD15INPEN_Msk (0x2000000UL) /*!< GPIO PADREGD: PAD15INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGD_PAD15PULL_Pos (24UL) /*!< GPIO PADREGD: PAD15PULL (Bit 24) */ +#define GPIO_PADREGD_PAD15PULL_Msk (0x1000000UL) /*!< GPIO PADREGD: PAD15PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGD_PAD14FNCSEL_Pos (19UL) /*!< GPIO PADREGD: PAD14FNCSEL (Bit 19) */ +#define GPIO_PADREGD_PAD14FNCSEL_Msk (0x380000UL) /*!< GPIO PADREGD: PAD14FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGD_PAD14STRNG_Pos (18UL) /*!< GPIO PADREGD: PAD14STRNG (Bit 18) */ +#define GPIO_PADREGD_PAD14STRNG_Msk (0x40000UL) /*!< GPIO PADREGD: PAD14STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGD_PAD14INPEN_Pos (17UL) /*!< GPIO PADREGD: PAD14INPEN (Bit 17) */ +#define GPIO_PADREGD_PAD14INPEN_Msk (0x20000UL) /*!< GPIO PADREGD: PAD14INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGD_PAD14PULL_Pos (16UL) /*!< GPIO PADREGD: PAD14PULL (Bit 16) */ +#define GPIO_PADREGD_PAD14PULL_Msk (0x10000UL) /*!< GPIO PADREGD: PAD14PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGD_PAD13FNCSEL_Pos (11UL) /*!< GPIO PADREGD: PAD13FNCSEL (Bit 11) */ +#define GPIO_PADREGD_PAD13FNCSEL_Msk (0x3800UL) /*!< GPIO PADREGD: PAD13FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGD_PAD13STRNG_Pos (10UL) /*!< GPIO PADREGD: PAD13STRNG (Bit 10) */ +#define GPIO_PADREGD_PAD13STRNG_Msk (0x400UL) /*!< GPIO PADREGD: PAD13STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGD_PAD13INPEN_Pos (9UL) /*!< GPIO PADREGD: PAD13INPEN (Bit 9) */ +#define GPIO_PADREGD_PAD13INPEN_Msk (0x200UL) /*!< GPIO PADREGD: PAD13INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGD_PAD13PULL_Pos (8UL) /*!< GPIO PADREGD: PAD13PULL (Bit 8) */ +#define GPIO_PADREGD_PAD13PULL_Msk (0x100UL) /*!< GPIO PADREGD: PAD13PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGD_PAD12FNCSEL_Pos (3UL) /*!< GPIO PADREGD: PAD12FNCSEL (Bit 3) */ +#define GPIO_PADREGD_PAD12FNCSEL_Msk (0x38UL) /*!< GPIO PADREGD: PAD12FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGD_PAD12STRNG_Pos (2UL) /*!< GPIO PADREGD: PAD12STRNG (Bit 2) */ +#define GPIO_PADREGD_PAD12STRNG_Msk (0x4UL) /*!< GPIO PADREGD: PAD12STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGD_PAD12INPEN_Pos (1UL) /*!< GPIO PADREGD: PAD12INPEN (Bit 1) */ +#define GPIO_PADREGD_PAD12INPEN_Msk (0x2UL) /*!< GPIO PADREGD: PAD12INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGD_PAD12PULL_Pos (0UL) /*!< GPIO PADREGD: PAD12PULL (Bit 0) */ +#define GPIO_PADREGD_PAD12PULL_Msk (0x1UL) /*!< GPIO PADREGD: PAD12PULL (Bitfield-Mask: 0x01) */ +/* ======================================================== PADREGE ======================================================== */ +#define GPIO_PADREGE_PAD19FNCSEL_Pos (27UL) /*!< GPIO PADREGE: PAD19FNCSEL (Bit 27) */ +#define GPIO_PADREGE_PAD19FNCSEL_Msk (0x38000000UL) /*!< GPIO PADREGE: PAD19FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGE_PAD19STRNG_Pos (26UL) /*!< GPIO PADREGE: PAD19STRNG (Bit 26) */ +#define GPIO_PADREGE_PAD19STRNG_Msk (0x4000000UL) /*!< GPIO PADREGE: PAD19STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGE_PAD19INPEN_Pos (25UL) /*!< GPIO PADREGE: PAD19INPEN (Bit 25) */ +#define GPIO_PADREGE_PAD19INPEN_Msk (0x2000000UL) /*!< GPIO PADREGE: PAD19INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGE_PAD19PULL_Pos (24UL) /*!< GPIO PADREGE: PAD19PULL (Bit 24) */ +#define GPIO_PADREGE_PAD19PULL_Msk (0x1000000UL) /*!< GPIO PADREGE: PAD19PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGE_PAD18FNCSEL_Pos (19UL) /*!< GPIO PADREGE: PAD18FNCSEL (Bit 19) */ +#define GPIO_PADREGE_PAD18FNCSEL_Msk (0x380000UL) /*!< GPIO PADREGE: PAD18FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGE_PAD18STRNG_Pos (18UL) /*!< GPIO PADREGE: PAD18STRNG (Bit 18) */ +#define GPIO_PADREGE_PAD18STRNG_Msk (0x40000UL) /*!< GPIO PADREGE: PAD18STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGE_PAD18INPEN_Pos (17UL) /*!< GPIO PADREGE: PAD18INPEN (Bit 17) */ +#define GPIO_PADREGE_PAD18INPEN_Msk (0x20000UL) /*!< GPIO PADREGE: PAD18INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGE_PAD18PULL_Pos (16UL) /*!< GPIO PADREGE: PAD18PULL (Bit 16) */ +#define GPIO_PADREGE_PAD18PULL_Msk (0x10000UL) /*!< GPIO PADREGE: PAD18PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGE_PAD17FNCSEL_Pos (11UL) /*!< GPIO PADREGE: PAD17FNCSEL (Bit 11) */ +#define GPIO_PADREGE_PAD17FNCSEL_Msk (0x3800UL) /*!< GPIO PADREGE: PAD17FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGE_PAD17STRNG_Pos (10UL) /*!< GPIO PADREGE: PAD17STRNG (Bit 10) */ +#define GPIO_PADREGE_PAD17STRNG_Msk (0x400UL) /*!< GPIO PADREGE: PAD17STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGE_PAD17INPEN_Pos (9UL) /*!< GPIO PADREGE: PAD17INPEN (Bit 9) */ +#define GPIO_PADREGE_PAD17INPEN_Msk (0x200UL) /*!< GPIO PADREGE: PAD17INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGE_PAD17PULL_Pos (8UL) /*!< GPIO PADREGE: PAD17PULL (Bit 8) */ +#define GPIO_PADREGE_PAD17PULL_Msk (0x100UL) /*!< GPIO PADREGE: PAD17PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGE_PAD16FNCSEL_Pos (3UL) /*!< GPIO PADREGE: PAD16FNCSEL (Bit 3) */ +#define GPIO_PADREGE_PAD16FNCSEL_Msk (0x38UL) /*!< GPIO PADREGE: PAD16FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGE_PAD16STRNG_Pos (2UL) /*!< GPIO PADREGE: PAD16STRNG (Bit 2) */ +#define GPIO_PADREGE_PAD16STRNG_Msk (0x4UL) /*!< GPIO PADREGE: PAD16STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGE_PAD16INPEN_Pos (1UL) /*!< GPIO PADREGE: PAD16INPEN (Bit 1) */ +#define GPIO_PADREGE_PAD16INPEN_Msk (0x2UL) /*!< GPIO PADREGE: PAD16INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGE_PAD16PULL_Pos (0UL) /*!< GPIO PADREGE: PAD16PULL (Bit 0) */ +#define GPIO_PADREGE_PAD16PULL_Msk (0x1UL) /*!< GPIO PADREGE: PAD16PULL (Bitfield-Mask: 0x01) */ +/* ======================================================== PADREGF ======================================================== */ +#define GPIO_PADREGF_PAD23FNCSEL_Pos (27UL) /*!< GPIO PADREGF: PAD23FNCSEL (Bit 27) */ +#define GPIO_PADREGF_PAD23FNCSEL_Msk (0x38000000UL) /*!< GPIO PADREGF: PAD23FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGF_PAD23STRNG_Pos (26UL) /*!< GPIO PADREGF: PAD23STRNG (Bit 26) */ +#define GPIO_PADREGF_PAD23STRNG_Msk (0x4000000UL) /*!< GPIO PADREGF: PAD23STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGF_PAD23INPEN_Pos (25UL) /*!< GPIO PADREGF: PAD23INPEN (Bit 25) */ +#define GPIO_PADREGF_PAD23INPEN_Msk (0x2000000UL) /*!< GPIO PADREGF: PAD23INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGF_PAD23PULL_Pos (24UL) /*!< GPIO PADREGF: PAD23PULL (Bit 24) */ +#define GPIO_PADREGF_PAD23PULL_Msk (0x1000000UL) /*!< GPIO PADREGF: PAD23PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGF_PAD22FNCSEL_Pos (19UL) /*!< GPIO PADREGF: PAD22FNCSEL (Bit 19) */ +#define GPIO_PADREGF_PAD22FNCSEL_Msk (0x380000UL) /*!< GPIO PADREGF: PAD22FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGF_PAD22STRNG_Pos (18UL) /*!< GPIO PADREGF: PAD22STRNG (Bit 18) */ +#define GPIO_PADREGF_PAD22STRNG_Msk (0x40000UL) /*!< GPIO PADREGF: PAD22STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGF_PAD22INPEN_Pos (17UL) /*!< GPIO PADREGF: PAD22INPEN (Bit 17) */ +#define GPIO_PADREGF_PAD22INPEN_Msk (0x20000UL) /*!< GPIO PADREGF: PAD22INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGF_PAD22PULL_Pos (16UL) /*!< GPIO PADREGF: PAD22PULL (Bit 16) */ +#define GPIO_PADREGF_PAD22PULL_Msk (0x10000UL) /*!< GPIO PADREGF: PAD22PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGF_PAD21FNCSEL_Pos (11UL) /*!< GPIO PADREGF: PAD21FNCSEL (Bit 11) */ +#define GPIO_PADREGF_PAD21FNCSEL_Msk (0x3800UL) /*!< GPIO PADREGF: PAD21FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGF_PAD21STRNG_Pos (10UL) /*!< GPIO PADREGF: PAD21STRNG (Bit 10) */ +#define GPIO_PADREGF_PAD21STRNG_Msk (0x400UL) /*!< GPIO PADREGF: PAD21STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGF_PAD21INPEN_Pos (9UL) /*!< GPIO PADREGF: PAD21INPEN (Bit 9) */ +#define GPIO_PADREGF_PAD21INPEN_Msk (0x200UL) /*!< GPIO PADREGF: PAD21INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGF_PAD21PULL_Pos (8UL) /*!< GPIO PADREGF: PAD21PULL (Bit 8) */ +#define GPIO_PADREGF_PAD21PULL_Msk (0x100UL) /*!< GPIO PADREGF: PAD21PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGF_PAD20FNCSEL_Pos (3UL) /*!< GPIO PADREGF: PAD20FNCSEL (Bit 3) */ +#define GPIO_PADREGF_PAD20FNCSEL_Msk (0x38UL) /*!< GPIO PADREGF: PAD20FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGF_PAD20STRNG_Pos (2UL) /*!< GPIO PADREGF: PAD20STRNG (Bit 2) */ +#define GPIO_PADREGF_PAD20STRNG_Msk (0x4UL) /*!< GPIO PADREGF: PAD20STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGF_PAD20INPEN_Pos (1UL) /*!< GPIO PADREGF: PAD20INPEN (Bit 1) */ +#define GPIO_PADREGF_PAD20INPEN_Msk (0x2UL) /*!< GPIO PADREGF: PAD20INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGF_PAD20PULL_Pos (0UL) /*!< GPIO PADREGF: PAD20PULL (Bit 0) */ +#define GPIO_PADREGF_PAD20PULL_Msk (0x1UL) /*!< GPIO PADREGF: PAD20PULL (Bitfield-Mask: 0x01) */ +/* ======================================================== PADREGG ======================================================== */ +#define GPIO_PADREGG_PAD27RSEL_Pos (30UL) /*!< GPIO PADREGG: PAD27RSEL (Bit 30) */ +#define GPIO_PADREGG_PAD27RSEL_Msk (0xc0000000UL) /*!< GPIO PADREGG: PAD27RSEL (Bitfield-Mask: 0x03) */ +#define GPIO_PADREGG_PAD27FNCSEL_Pos (27UL) /*!< GPIO PADREGG: PAD27FNCSEL (Bit 27) */ +#define GPIO_PADREGG_PAD27FNCSEL_Msk (0x38000000UL) /*!< GPIO PADREGG: PAD27FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGG_PAD27STRNG_Pos (26UL) /*!< GPIO PADREGG: PAD27STRNG (Bit 26) */ +#define GPIO_PADREGG_PAD27STRNG_Msk (0x4000000UL) /*!< GPIO PADREGG: PAD27STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGG_PAD27INPEN_Pos (25UL) /*!< GPIO PADREGG: PAD27INPEN (Bit 25) */ +#define GPIO_PADREGG_PAD27INPEN_Msk (0x2000000UL) /*!< GPIO PADREGG: PAD27INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGG_PAD27PULL_Pos (24UL) /*!< GPIO PADREGG: PAD27PULL (Bit 24) */ +#define GPIO_PADREGG_PAD27PULL_Msk (0x1000000UL) /*!< GPIO PADREGG: PAD27PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGG_PAD26FNCSEL_Pos (19UL) /*!< GPIO PADREGG: PAD26FNCSEL (Bit 19) */ +#define GPIO_PADREGG_PAD26FNCSEL_Msk (0x380000UL) /*!< GPIO PADREGG: PAD26FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGG_PAD26STRNG_Pos (18UL) /*!< GPIO PADREGG: PAD26STRNG (Bit 18) */ +#define GPIO_PADREGG_PAD26STRNG_Msk (0x40000UL) /*!< GPIO PADREGG: PAD26STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGG_PAD26INPEN_Pos (17UL) /*!< GPIO PADREGG: PAD26INPEN (Bit 17) */ +#define GPIO_PADREGG_PAD26INPEN_Msk (0x20000UL) /*!< GPIO PADREGG: PAD26INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGG_PAD26PULL_Pos (16UL) /*!< GPIO PADREGG: PAD26PULL (Bit 16) */ +#define GPIO_PADREGG_PAD26PULL_Msk (0x10000UL) /*!< GPIO PADREGG: PAD26PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGG_PAD25RSEL_Pos (14UL) /*!< GPIO PADREGG: PAD25RSEL (Bit 14) */ +#define GPIO_PADREGG_PAD25RSEL_Msk (0xc000UL) /*!< GPIO PADREGG: PAD25RSEL (Bitfield-Mask: 0x03) */ +#define GPIO_PADREGG_PAD25FNCSEL_Pos (11UL) /*!< GPIO PADREGG: PAD25FNCSEL (Bit 11) */ +#define GPIO_PADREGG_PAD25FNCSEL_Msk (0x3800UL) /*!< GPIO PADREGG: PAD25FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGG_PAD25STRNG_Pos (10UL) /*!< GPIO PADREGG: PAD25STRNG (Bit 10) */ +#define GPIO_PADREGG_PAD25STRNG_Msk (0x400UL) /*!< GPIO PADREGG: PAD25STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGG_PAD25INPEN_Pos (9UL) /*!< GPIO PADREGG: PAD25INPEN (Bit 9) */ +#define GPIO_PADREGG_PAD25INPEN_Msk (0x200UL) /*!< GPIO PADREGG: PAD25INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGG_PAD25PULL_Pos (8UL) /*!< GPIO PADREGG: PAD25PULL (Bit 8) */ +#define GPIO_PADREGG_PAD25PULL_Msk (0x100UL) /*!< GPIO PADREGG: PAD25PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGG_PAD24FNCSEL_Pos (3UL) /*!< GPIO PADREGG: PAD24FNCSEL (Bit 3) */ +#define GPIO_PADREGG_PAD24FNCSEL_Msk (0x38UL) /*!< GPIO PADREGG: PAD24FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGG_PAD24STRNG_Pos (2UL) /*!< GPIO PADREGG: PAD24STRNG (Bit 2) */ +#define GPIO_PADREGG_PAD24STRNG_Msk (0x4UL) /*!< GPIO PADREGG: PAD24STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGG_PAD24INPEN_Pos (1UL) /*!< GPIO PADREGG: PAD24INPEN (Bit 1) */ +#define GPIO_PADREGG_PAD24INPEN_Msk (0x2UL) /*!< GPIO PADREGG: PAD24INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGG_PAD24PULL_Pos (0UL) /*!< GPIO PADREGG: PAD24PULL (Bit 0) */ +#define GPIO_PADREGG_PAD24PULL_Msk (0x1UL) /*!< GPIO PADREGG: PAD24PULL (Bitfield-Mask: 0x01) */ +/* ======================================================== PADREGH ======================================================== */ +#define GPIO_PADREGH_PAD31FNCSEL_Pos (27UL) /*!< GPIO PADREGH: PAD31FNCSEL (Bit 27) */ +#define GPIO_PADREGH_PAD31FNCSEL_Msk (0x38000000UL) /*!< GPIO PADREGH: PAD31FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGH_PAD31STRNG_Pos (26UL) /*!< GPIO PADREGH: PAD31STRNG (Bit 26) */ +#define GPIO_PADREGH_PAD31STRNG_Msk (0x4000000UL) /*!< GPIO PADREGH: PAD31STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGH_PAD31INPEN_Pos (25UL) /*!< GPIO PADREGH: PAD31INPEN (Bit 25) */ +#define GPIO_PADREGH_PAD31INPEN_Msk (0x2000000UL) /*!< GPIO PADREGH: PAD31INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGH_PAD31PULL_Pos (24UL) /*!< GPIO PADREGH: PAD31PULL (Bit 24) */ +#define GPIO_PADREGH_PAD31PULL_Msk (0x1000000UL) /*!< GPIO PADREGH: PAD31PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGH_PAD30FNCSEL_Pos (19UL) /*!< GPIO PADREGH: PAD30FNCSEL (Bit 19) */ +#define GPIO_PADREGH_PAD30FNCSEL_Msk (0x380000UL) /*!< GPIO PADREGH: PAD30FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGH_PAD30STRNG_Pos (18UL) /*!< GPIO PADREGH: PAD30STRNG (Bit 18) */ +#define GPIO_PADREGH_PAD30STRNG_Msk (0x40000UL) /*!< GPIO PADREGH: PAD30STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGH_PAD30INPEN_Pos (17UL) /*!< GPIO PADREGH: PAD30INPEN (Bit 17) */ +#define GPIO_PADREGH_PAD30INPEN_Msk (0x20000UL) /*!< GPIO PADREGH: PAD30INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGH_PAD30PULL_Pos (16UL) /*!< GPIO PADREGH: PAD30PULL (Bit 16) */ +#define GPIO_PADREGH_PAD30PULL_Msk (0x10000UL) /*!< GPIO PADREGH: PAD30PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGH_PAD29FNCSEL_Pos (11UL) /*!< GPIO PADREGH: PAD29FNCSEL (Bit 11) */ +#define GPIO_PADREGH_PAD29FNCSEL_Msk (0x3800UL) /*!< GPIO PADREGH: PAD29FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGH_PAD29STRNG_Pos (10UL) /*!< GPIO PADREGH: PAD29STRNG (Bit 10) */ +#define GPIO_PADREGH_PAD29STRNG_Msk (0x400UL) /*!< GPIO PADREGH: PAD29STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGH_PAD29INPEN_Pos (9UL) /*!< GPIO PADREGH: PAD29INPEN (Bit 9) */ +#define GPIO_PADREGH_PAD29INPEN_Msk (0x200UL) /*!< GPIO PADREGH: PAD29INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGH_PAD29PULL_Pos (8UL) /*!< GPIO PADREGH: PAD29PULL (Bit 8) */ +#define GPIO_PADREGH_PAD29PULL_Msk (0x100UL) /*!< GPIO PADREGH: PAD29PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGH_PAD28FNCSEL_Pos (3UL) /*!< GPIO PADREGH: PAD28FNCSEL (Bit 3) */ +#define GPIO_PADREGH_PAD28FNCSEL_Msk (0x38UL) /*!< GPIO PADREGH: PAD28FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGH_PAD28STRNG_Pos (2UL) /*!< GPIO PADREGH: PAD28STRNG (Bit 2) */ +#define GPIO_PADREGH_PAD28STRNG_Msk (0x4UL) /*!< GPIO PADREGH: PAD28STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGH_PAD28INPEN_Pos (1UL) /*!< GPIO PADREGH: PAD28INPEN (Bit 1) */ +#define GPIO_PADREGH_PAD28INPEN_Msk (0x2UL) /*!< GPIO PADREGH: PAD28INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGH_PAD28PULL_Pos (0UL) /*!< GPIO PADREGH: PAD28PULL (Bit 0) */ +#define GPIO_PADREGH_PAD28PULL_Msk (0x1UL) /*!< GPIO PADREGH: PAD28PULL (Bitfield-Mask: 0x01) */ +/* ======================================================== PADREGI ======================================================== */ +#define GPIO_PADREGI_PAD35FNCSEL_Pos (27UL) /*!< GPIO PADREGI: PAD35FNCSEL (Bit 27) */ +#define GPIO_PADREGI_PAD35FNCSEL_Msk (0x38000000UL) /*!< GPIO PADREGI: PAD35FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGI_PAD35STRNG_Pos (26UL) /*!< GPIO PADREGI: PAD35STRNG (Bit 26) */ +#define GPIO_PADREGI_PAD35STRNG_Msk (0x4000000UL) /*!< GPIO PADREGI: PAD35STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGI_PAD35INPEN_Pos (25UL) /*!< GPIO PADREGI: PAD35INPEN (Bit 25) */ +#define GPIO_PADREGI_PAD35INPEN_Msk (0x2000000UL) /*!< GPIO PADREGI: PAD35INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGI_PAD35PULL_Pos (24UL) /*!< GPIO PADREGI: PAD35PULL (Bit 24) */ +#define GPIO_PADREGI_PAD35PULL_Msk (0x1000000UL) /*!< GPIO PADREGI: PAD35PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGI_PAD34FNCSEL_Pos (19UL) /*!< GPIO PADREGI: PAD34FNCSEL (Bit 19) */ +#define GPIO_PADREGI_PAD34FNCSEL_Msk (0x380000UL) /*!< GPIO PADREGI: PAD34FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGI_PAD34STRNG_Pos (18UL) /*!< GPIO PADREGI: PAD34STRNG (Bit 18) */ +#define GPIO_PADREGI_PAD34STRNG_Msk (0x40000UL) /*!< GPIO PADREGI: PAD34STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGI_PAD34INPEN_Pos (17UL) /*!< GPIO PADREGI: PAD34INPEN (Bit 17) */ +#define GPIO_PADREGI_PAD34INPEN_Msk (0x20000UL) /*!< GPIO PADREGI: PAD34INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGI_PAD34PULL_Pos (16UL) /*!< GPIO PADREGI: PAD34PULL (Bit 16) */ +#define GPIO_PADREGI_PAD34PULL_Msk (0x10000UL) /*!< GPIO PADREGI: PAD34PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGI_PAD33FNCSEL_Pos (11UL) /*!< GPIO PADREGI: PAD33FNCSEL (Bit 11) */ +#define GPIO_PADREGI_PAD33FNCSEL_Msk (0x3800UL) /*!< GPIO PADREGI: PAD33FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGI_PAD33STRNG_Pos (10UL) /*!< GPIO PADREGI: PAD33STRNG (Bit 10) */ +#define GPIO_PADREGI_PAD33STRNG_Msk (0x400UL) /*!< GPIO PADREGI: PAD33STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGI_PAD33INPEN_Pos (9UL) /*!< GPIO PADREGI: PAD33INPEN (Bit 9) */ +#define GPIO_PADREGI_PAD33INPEN_Msk (0x200UL) /*!< GPIO PADREGI: PAD33INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGI_PAD33PULL_Pos (8UL) /*!< GPIO PADREGI: PAD33PULL (Bit 8) */ +#define GPIO_PADREGI_PAD33PULL_Msk (0x100UL) /*!< GPIO PADREGI: PAD33PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGI_PAD32FNCSEL_Pos (3UL) /*!< GPIO PADREGI: PAD32FNCSEL (Bit 3) */ +#define GPIO_PADREGI_PAD32FNCSEL_Msk (0x38UL) /*!< GPIO PADREGI: PAD32FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGI_PAD32STRNG_Pos (2UL) /*!< GPIO PADREGI: PAD32STRNG (Bit 2) */ +#define GPIO_PADREGI_PAD32STRNG_Msk (0x4UL) /*!< GPIO PADREGI: PAD32STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGI_PAD32INPEN_Pos (1UL) /*!< GPIO PADREGI: PAD32INPEN (Bit 1) */ +#define GPIO_PADREGI_PAD32INPEN_Msk (0x2UL) /*!< GPIO PADREGI: PAD32INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGI_PAD32PULL_Pos (0UL) /*!< GPIO PADREGI: PAD32PULL (Bit 0) */ +#define GPIO_PADREGI_PAD32PULL_Msk (0x1UL) /*!< GPIO PADREGI: PAD32PULL (Bitfield-Mask: 0x01) */ +/* ======================================================== PADREGJ ======================================================== */ +#define GPIO_PADREGJ_PAD39RSEL_Pos (30UL) /*!< GPIO PADREGJ: PAD39RSEL (Bit 30) */ +#define GPIO_PADREGJ_PAD39RSEL_Msk (0xc0000000UL) /*!< GPIO PADREGJ: PAD39RSEL (Bitfield-Mask: 0x03) */ +#define GPIO_PADREGJ_PAD39FNCSEL_Pos (27UL) /*!< GPIO PADREGJ: PAD39FNCSEL (Bit 27) */ +#define GPIO_PADREGJ_PAD39FNCSEL_Msk (0x38000000UL) /*!< GPIO PADREGJ: PAD39FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGJ_PAD39STRNG_Pos (26UL) /*!< GPIO PADREGJ: PAD39STRNG (Bit 26) */ +#define GPIO_PADREGJ_PAD39STRNG_Msk (0x4000000UL) /*!< GPIO PADREGJ: PAD39STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGJ_PAD39INPEN_Pos (25UL) /*!< GPIO PADREGJ: PAD39INPEN (Bit 25) */ +#define GPIO_PADREGJ_PAD39INPEN_Msk (0x2000000UL) /*!< GPIO PADREGJ: PAD39INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGJ_PAD39PULL_Pos (24UL) /*!< GPIO PADREGJ: PAD39PULL (Bit 24) */ +#define GPIO_PADREGJ_PAD39PULL_Msk (0x1000000UL) /*!< GPIO PADREGJ: PAD39PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGJ_PAD38FNCSEL_Pos (19UL) /*!< GPIO PADREGJ: PAD38FNCSEL (Bit 19) */ +#define GPIO_PADREGJ_PAD38FNCSEL_Msk (0x380000UL) /*!< GPIO PADREGJ: PAD38FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGJ_PAD38STRNG_Pos (18UL) /*!< GPIO PADREGJ: PAD38STRNG (Bit 18) */ +#define GPIO_PADREGJ_PAD38STRNG_Msk (0x40000UL) /*!< GPIO PADREGJ: PAD38STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGJ_PAD38INPEN_Pos (17UL) /*!< GPIO PADREGJ: PAD38INPEN (Bit 17) */ +#define GPIO_PADREGJ_PAD38INPEN_Msk (0x20000UL) /*!< GPIO PADREGJ: PAD38INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGJ_PAD38PULL_Pos (16UL) /*!< GPIO PADREGJ: PAD38PULL (Bit 16) */ +#define GPIO_PADREGJ_PAD38PULL_Msk (0x10000UL) /*!< GPIO PADREGJ: PAD38PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGJ_PAD37PWRDN_Pos (15UL) /*!< GPIO PADREGJ: PAD37PWRDN (Bit 15) */ +#define GPIO_PADREGJ_PAD37PWRDN_Msk (0x8000UL) /*!< GPIO PADREGJ: PAD37PWRDN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGJ_PAD37FNCSEL_Pos (11UL) /*!< GPIO PADREGJ: PAD37FNCSEL (Bit 11) */ +#define GPIO_PADREGJ_PAD37FNCSEL_Msk (0x3800UL) /*!< GPIO PADREGJ: PAD37FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGJ_PAD37STRNG_Pos (10UL) /*!< GPIO PADREGJ: PAD37STRNG (Bit 10) */ +#define GPIO_PADREGJ_PAD37STRNG_Msk (0x400UL) /*!< GPIO PADREGJ: PAD37STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGJ_PAD37INPEN_Pos (9UL) /*!< GPIO PADREGJ: PAD37INPEN (Bit 9) */ +#define GPIO_PADREGJ_PAD37INPEN_Msk (0x200UL) /*!< GPIO PADREGJ: PAD37INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGJ_PAD37PULL_Pos (8UL) /*!< GPIO PADREGJ: PAD37PULL (Bit 8) */ +#define GPIO_PADREGJ_PAD37PULL_Msk (0x100UL) /*!< GPIO PADREGJ: PAD37PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGJ_PAD36PWRUP_Pos (6UL) /*!< GPIO PADREGJ: PAD36PWRUP (Bit 6) */ +#define GPIO_PADREGJ_PAD36PWRUP_Msk (0x40UL) /*!< GPIO PADREGJ: PAD36PWRUP (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGJ_PAD36FNCSEL_Pos (3UL) /*!< GPIO PADREGJ: PAD36FNCSEL (Bit 3) */ +#define GPIO_PADREGJ_PAD36FNCSEL_Msk (0x38UL) /*!< GPIO PADREGJ: PAD36FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGJ_PAD36STRNG_Pos (2UL) /*!< GPIO PADREGJ: PAD36STRNG (Bit 2) */ +#define GPIO_PADREGJ_PAD36STRNG_Msk (0x4UL) /*!< GPIO PADREGJ: PAD36STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGJ_PAD36INPEN_Pos (1UL) /*!< GPIO PADREGJ: PAD36INPEN (Bit 1) */ +#define GPIO_PADREGJ_PAD36INPEN_Msk (0x2UL) /*!< GPIO PADREGJ: PAD36INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGJ_PAD36PULL_Pos (0UL) /*!< GPIO PADREGJ: PAD36PULL (Bit 0) */ +#define GPIO_PADREGJ_PAD36PULL_Msk (0x1UL) /*!< GPIO PADREGJ: PAD36PULL (Bitfield-Mask: 0x01) */ +/* ======================================================== PADREGK ======================================================== */ +#define GPIO_PADREGK_PAD43RSEL_Pos (30UL) /*!< GPIO PADREGK: PAD43RSEL (Bit 30) */ +#define GPIO_PADREGK_PAD43RSEL_Msk (0xc0000000UL) /*!< GPIO PADREGK: PAD43RSEL (Bitfield-Mask: 0x03) */ +#define GPIO_PADREGK_PAD43FNCSEL_Pos (27UL) /*!< GPIO PADREGK: PAD43FNCSEL (Bit 27) */ +#define GPIO_PADREGK_PAD43FNCSEL_Msk (0x38000000UL) /*!< GPIO PADREGK: PAD43FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGK_PAD43STRNG_Pos (26UL) /*!< GPIO PADREGK: PAD43STRNG (Bit 26) */ +#define GPIO_PADREGK_PAD43STRNG_Msk (0x4000000UL) /*!< GPIO PADREGK: PAD43STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGK_PAD43INPEN_Pos (25UL) /*!< GPIO PADREGK: PAD43INPEN (Bit 25) */ +#define GPIO_PADREGK_PAD43INPEN_Msk (0x2000000UL) /*!< GPIO PADREGK: PAD43INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGK_PAD43PULL_Pos (24UL) /*!< GPIO PADREGK: PAD43PULL (Bit 24) */ +#define GPIO_PADREGK_PAD43PULL_Msk (0x1000000UL) /*!< GPIO PADREGK: PAD43PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGK_PAD42RSEL_Pos (22UL) /*!< GPIO PADREGK: PAD42RSEL (Bit 22) */ +#define GPIO_PADREGK_PAD42RSEL_Msk (0xc00000UL) /*!< GPIO PADREGK: PAD42RSEL (Bitfield-Mask: 0x03) */ +#define GPIO_PADREGK_PAD42FNCSEL_Pos (19UL) /*!< GPIO PADREGK: PAD42FNCSEL (Bit 19) */ +#define GPIO_PADREGK_PAD42FNCSEL_Msk (0x380000UL) /*!< GPIO PADREGK: PAD42FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGK_PAD42STRNG_Pos (18UL) /*!< GPIO PADREGK: PAD42STRNG (Bit 18) */ +#define GPIO_PADREGK_PAD42STRNG_Msk (0x40000UL) /*!< GPIO PADREGK: PAD42STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGK_PAD42INPEN_Pos (17UL) /*!< GPIO PADREGK: PAD42INPEN (Bit 17) */ +#define GPIO_PADREGK_PAD42INPEN_Msk (0x20000UL) /*!< GPIO PADREGK: PAD42INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGK_PAD42PULL_Pos (16UL) /*!< GPIO PADREGK: PAD42PULL (Bit 16) */ +#define GPIO_PADREGK_PAD42PULL_Msk (0x10000UL) /*!< GPIO PADREGK: PAD42PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGK_PAD41PWRDN_Pos (15UL) /*!< GPIO PADREGK: PAD41PWRDN (Bit 15) */ +#define GPIO_PADREGK_PAD41PWRDN_Msk (0x8000UL) /*!< GPIO PADREGK: PAD41PWRDN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGK_PAD41FNCSEL_Pos (11UL) /*!< GPIO PADREGK: PAD41FNCSEL (Bit 11) */ +#define GPIO_PADREGK_PAD41FNCSEL_Msk (0x3800UL) /*!< GPIO PADREGK: PAD41FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGK_PAD41STRNG_Pos (10UL) /*!< GPIO PADREGK: PAD41STRNG (Bit 10) */ +#define GPIO_PADREGK_PAD41STRNG_Msk (0x400UL) /*!< GPIO PADREGK: PAD41STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGK_PAD41INPEN_Pos (9UL) /*!< GPIO PADREGK: PAD41INPEN (Bit 9) */ +#define GPIO_PADREGK_PAD41INPEN_Msk (0x200UL) /*!< GPIO PADREGK: PAD41INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGK_PAD41PULL_Pos (8UL) /*!< GPIO PADREGK: PAD41PULL (Bit 8) */ +#define GPIO_PADREGK_PAD41PULL_Msk (0x100UL) /*!< GPIO PADREGK: PAD41PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGK_PAD40RSEL_Pos (6UL) /*!< GPIO PADREGK: PAD40RSEL (Bit 6) */ +#define GPIO_PADREGK_PAD40RSEL_Msk (0xc0UL) /*!< GPIO PADREGK: PAD40RSEL (Bitfield-Mask: 0x03) */ +#define GPIO_PADREGK_PAD40FNCSEL_Pos (3UL) /*!< GPIO PADREGK: PAD40FNCSEL (Bit 3) */ +#define GPIO_PADREGK_PAD40FNCSEL_Msk (0x38UL) /*!< GPIO PADREGK: PAD40FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGK_PAD40STRNG_Pos (2UL) /*!< GPIO PADREGK: PAD40STRNG (Bit 2) */ +#define GPIO_PADREGK_PAD40STRNG_Msk (0x4UL) /*!< GPIO PADREGK: PAD40STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGK_PAD40INPEN_Pos (1UL) /*!< GPIO PADREGK: PAD40INPEN (Bit 1) */ +#define GPIO_PADREGK_PAD40INPEN_Msk (0x2UL) /*!< GPIO PADREGK: PAD40INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGK_PAD40PULL_Pos (0UL) /*!< GPIO PADREGK: PAD40PULL (Bit 0) */ +#define GPIO_PADREGK_PAD40PULL_Msk (0x1UL) /*!< GPIO PADREGK: PAD40PULL (Bitfield-Mask: 0x01) */ +/* ======================================================== PADREGL ======================================================== */ +#define GPIO_PADREGL_PAD47FNCSEL_Pos (27UL) /*!< GPIO PADREGL: PAD47FNCSEL (Bit 27) */ +#define GPIO_PADREGL_PAD47FNCSEL_Msk (0x38000000UL) /*!< GPIO PADREGL: PAD47FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGL_PAD47STRNG_Pos (26UL) /*!< GPIO PADREGL: PAD47STRNG (Bit 26) */ +#define GPIO_PADREGL_PAD47STRNG_Msk (0x4000000UL) /*!< GPIO PADREGL: PAD47STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGL_PAD47INPEN_Pos (25UL) /*!< GPIO PADREGL: PAD47INPEN (Bit 25) */ +#define GPIO_PADREGL_PAD47INPEN_Msk (0x2000000UL) /*!< GPIO PADREGL: PAD47INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGL_PAD47PULL_Pos (24UL) /*!< GPIO PADREGL: PAD47PULL (Bit 24) */ +#define GPIO_PADREGL_PAD47PULL_Msk (0x1000000UL) /*!< GPIO PADREGL: PAD47PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGL_PAD46FNCSEL_Pos (19UL) /*!< GPIO PADREGL: PAD46FNCSEL (Bit 19) */ +#define GPIO_PADREGL_PAD46FNCSEL_Msk (0x380000UL) /*!< GPIO PADREGL: PAD46FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGL_PAD46STRNG_Pos (18UL) /*!< GPIO PADREGL: PAD46STRNG (Bit 18) */ +#define GPIO_PADREGL_PAD46STRNG_Msk (0x40000UL) /*!< GPIO PADREGL: PAD46STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGL_PAD46INPEN_Pos (17UL) /*!< GPIO PADREGL: PAD46INPEN (Bit 17) */ +#define GPIO_PADREGL_PAD46INPEN_Msk (0x20000UL) /*!< GPIO PADREGL: PAD46INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGL_PAD46PULL_Pos (16UL) /*!< GPIO PADREGL: PAD46PULL (Bit 16) */ +#define GPIO_PADREGL_PAD46PULL_Msk (0x10000UL) /*!< GPIO PADREGL: PAD46PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGL_PAD45FNCSEL_Pos (11UL) /*!< GPIO PADREGL: PAD45FNCSEL (Bit 11) */ +#define GPIO_PADREGL_PAD45FNCSEL_Msk (0x3800UL) /*!< GPIO PADREGL: PAD45FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGL_PAD45STRNG_Pos (10UL) /*!< GPIO PADREGL: PAD45STRNG (Bit 10) */ +#define GPIO_PADREGL_PAD45STRNG_Msk (0x400UL) /*!< GPIO PADREGL: PAD45STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGL_PAD45INPEN_Pos (9UL) /*!< GPIO PADREGL: PAD45INPEN (Bit 9) */ +#define GPIO_PADREGL_PAD45INPEN_Msk (0x200UL) /*!< GPIO PADREGL: PAD45INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGL_PAD45PULL_Pos (8UL) /*!< GPIO PADREGL: PAD45PULL (Bit 8) */ +#define GPIO_PADREGL_PAD45PULL_Msk (0x100UL) /*!< GPIO PADREGL: PAD45PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGL_PAD44FNCSEL_Pos (3UL) /*!< GPIO PADREGL: PAD44FNCSEL (Bit 3) */ +#define GPIO_PADREGL_PAD44FNCSEL_Msk (0x38UL) /*!< GPIO PADREGL: PAD44FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGL_PAD44STRNG_Pos (2UL) /*!< GPIO PADREGL: PAD44STRNG (Bit 2) */ +#define GPIO_PADREGL_PAD44STRNG_Msk (0x4UL) /*!< GPIO PADREGL: PAD44STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGL_PAD44INPEN_Pos (1UL) /*!< GPIO PADREGL: PAD44INPEN (Bit 1) */ +#define GPIO_PADREGL_PAD44INPEN_Msk (0x2UL) /*!< GPIO PADREGL: PAD44INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGL_PAD44PULL_Pos (0UL) /*!< GPIO PADREGL: PAD44PULL (Bit 0) */ +#define GPIO_PADREGL_PAD44PULL_Msk (0x1UL) /*!< GPIO PADREGL: PAD44PULL (Bitfield-Mask: 0x01) */ +/* ======================================================== PADREGM ======================================================== */ +#define GPIO_PADREGM_PAD49RSEL_Pos (14UL) /*!< GPIO PADREGM: PAD49RSEL (Bit 14) */ +#define GPIO_PADREGM_PAD49RSEL_Msk (0xc000UL) /*!< GPIO PADREGM: PAD49RSEL (Bitfield-Mask: 0x03) */ +#define GPIO_PADREGM_PAD49FNCSEL_Pos (11UL) /*!< GPIO PADREGM: PAD49FNCSEL (Bit 11) */ +#define GPIO_PADREGM_PAD49FNCSEL_Msk (0x3800UL) /*!< GPIO PADREGM: PAD49FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGM_PAD49STRNG_Pos (10UL) /*!< GPIO PADREGM: PAD49STRNG (Bit 10) */ +#define GPIO_PADREGM_PAD49STRNG_Msk (0x400UL) /*!< GPIO PADREGM: PAD49STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGM_PAD49INPEN_Pos (9UL) /*!< GPIO PADREGM: PAD49INPEN (Bit 9) */ +#define GPIO_PADREGM_PAD49INPEN_Msk (0x200UL) /*!< GPIO PADREGM: PAD49INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGM_PAD49PULL_Pos (8UL) /*!< GPIO PADREGM: PAD49PULL (Bit 8) */ +#define GPIO_PADREGM_PAD49PULL_Msk (0x100UL) /*!< GPIO PADREGM: PAD49PULL (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGM_PAD48RSEL_Pos (6UL) /*!< GPIO PADREGM: PAD48RSEL (Bit 6) */ +#define GPIO_PADREGM_PAD48RSEL_Msk (0xc0UL) /*!< GPIO PADREGM: PAD48RSEL (Bitfield-Mask: 0x03) */ +#define GPIO_PADREGM_PAD48FNCSEL_Pos (3UL) /*!< GPIO PADREGM: PAD48FNCSEL (Bit 3) */ +#define GPIO_PADREGM_PAD48FNCSEL_Msk (0x38UL) /*!< GPIO PADREGM: PAD48FNCSEL (Bitfield-Mask: 0x07) */ +#define GPIO_PADREGM_PAD48STRNG_Pos (2UL) /*!< GPIO PADREGM: PAD48STRNG (Bit 2) */ +#define GPIO_PADREGM_PAD48STRNG_Msk (0x4UL) /*!< GPIO PADREGM: PAD48STRNG (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGM_PAD48INPEN_Pos (1UL) /*!< GPIO PADREGM: PAD48INPEN (Bit 1) */ +#define GPIO_PADREGM_PAD48INPEN_Msk (0x2UL) /*!< GPIO PADREGM: PAD48INPEN (Bitfield-Mask: 0x01) */ +#define GPIO_PADREGM_PAD48PULL_Pos (0UL) /*!< GPIO PADREGM: PAD48PULL (Bit 0) */ +#define GPIO_PADREGM_PAD48PULL_Msk (0x1UL) /*!< GPIO PADREGM: PAD48PULL (Bitfield-Mask: 0x01) */ +/* ========================================================= CFGA ========================================================== */ +#define GPIO_CFGA_GPIO7INTD_Pos (31UL) /*!< GPIO CFGA: GPIO7INTD (Bit 31) */ +#define GPIO_CFGA_GPIO7INTD_Msk (0x80000000UL) /*!< GPIO CFGA: GPIO7INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGA_GPIO7OUTCFG_Pos (29UL) /*!< GPIO CFGA: GPIO7OUTCFG (Bit 29) */ +#define GPIO_CFGA_GPIO7OUTCFG_Msk (0x60000000UL) /*!< GPIO CFGA: GPIO7OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGA_GPIO7INCFG_Pos (28UL) /*!< GPIO CFGA: GPIO7INCFG (Bit 28) */ +#define GPIO_CFGA_GPIO7INCFG_Msk (0x10000000UL) /*!< GPIO CFGA: GPIO7INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGA_GPIO6INTD_Pos (27UL) /*!< GPIO CFGA: GPIO6INTD (Bit 27) */ +#define GPIO_CFGA_GPIO6INTD_Msk (0x8000000UL) /*!< GPIO CFGA: GPIO6INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGA_GPIO6OUTCFG_Pos (25UL) /*!< GPIO CFGA: GPIO6OUTCFG (Bit 25) */ +#define GPIO_CFGA_GPIO6OUTCFG_Msk (0x6000000UL) /*!< GPIO CFGA: GPIO6OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGA_GPIO6INCFG_Pos (24UL) /*!< GPIO CFGA: GPIO6INCFG (Bit 24) */ +#define GPIO_CFGA_GPIO6INCFG_Msk (0x1000000UL) /*!< GPIO CFGA: GPIO6INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGA_GPIO5INTD_Pos (23UL) /*!< GPIO CFGA: GPIO5INTD (Bit 23) */ +#define GPIO_CFGA_GPIO5INTD_Msk (0x800000UL) /*!< GPIO CFGA: GPIO5INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGA_GPIO5OUTCFG_Pos (21UL) /*!< GPIO CFGA: GPIO5OUTCFG (Bit 21) */ +#define GPIO_CFGA_GPIO5OUTCFG_Msk (0x600000UL) /*!< GPIO CFGA: GPIO5OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGA_GPIO5INCFG_Pos (20UL) /*!< GPIO CFGA: GPIO5INCFG (Bit 20) */ +#define GPIO_CFGA_GPIO5INCFG_Msk (0x100000UL) /*!< GPIO CFGA: GPIO5INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGA_GPIO4INTD_Pos (19UL) /*!< GPIO CFGA: GPIO4INTD (Bit 19) */ +#define GPIO_CFGA_GPIO4INTD_Msk (0x80000UL) /*!< GPIO CFGA: GPIO4INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGA_GPIO4OUTCFG_Pos (17UL) /*!< GPIO CFGA: GPIO4OUTCFG (Bit 17) */ +#define GPIO_CFGA_GPIO4OUTCFG_Msk (0x60000UL) /*!< GPIO CFGA: GPIO4OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGA_GPIO4INCFG_Pos (16UL) /*!< GPIO CFGA: GPIO4INCFG (Bit 16) */ +#define GPIO_CFGA_GPIO4INCFG_Msk (0x10000UL) /*!< GPIO CFGA: GPIO4INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGA_GPIO3INTD_Pos (15UL) /*!< GPIO CFGA: GPIO3INTD (Bit 15) */ +#define GPIO_CFGA_GPIO3INTD_Msk (0x8000UL) /*!< GPIO CFGA: GPIO3INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGA_GPIO3OUTCFG_Pos (13UL) /*!< GPIO CFGA: GPIO3OUTCFG (Bit 13) */ +#define GPIO_CFGA_GPIO3OUTCFG_Msk (0x6000UL) /*!< GPIO CFGA: GPIO3OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGA_GPIO3INCFG_Pos (12UL) /*!< GPIO CFGA: GPIO3INCFG (Bit 12) */ +#define GPIO_CFGA_GPIO3INCFG_Msk (0x1000UL) /*!< GPIO CFGA: GPIO3INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGA_GPIO2INTD_Pos (11UL) /*!< GPIO CFGA: GPIO2INTD (Bit 11) */ +#define GPIO_CFGA_GPIO2INTD_Msk (0x800UL) /*!< GPIO CFGA: GPIO2INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGA_GPIO2OUTCFG_Pos (9UL) /*!< GPIO CFGA: GPIO2OUTCFG (Bit 9) */ +#define GPIO_CFGA_GPIO2OUTCFG_Msk (0x600UL) /*!< GPIO CFGA: GPIO2OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGA_GPIO2INCFG_Pos (8UL) /*!< GPIO CFGA: GPIO2INCFG (Bit 8) */ +#define GPIO_CFGA_GPIO2INCFG_Msk (0x100UL) /*!< GPIO CFGA: GPIO2INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGA_GPIO1INTD_Pos (7UL) /*!< GPIO CFGA: GPIO1INTD (Bit 7) */ +#define GPIO_CFGA_GPIO1INTD_Msk (0x80UL) /*!< GPIO CFGA: GPIO1INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGA_GPIO1OUTCFG_Pos (5UL) /*!< GPIO CFGA: GPIO1OUTCFG (Bit 5) */ +#define GPIO_CFGA_GPIO1OUTCFG_Msk (0x60UL) /*!< GPIO CFGA: GPIO1OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGA_GPIO1INCFG_Pos (4UL) /*!< GPIO CFGA: GPIO1INCFG (Bit 4) */ +#define GPIO_CFGA_GPIO1INCFG_Msk (0x10UL) /*!< GPIO CFGA: GPIO1INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGA_GPIO0INTD_Pos (3UL) /*!< GPIO CFGA: GPIO0INTD (Bit 3) */ +#define GPIO_CFGA_GPIO0INTD_Msk (0x8UL) /*!< GPIO CFGA: GPIO0INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGA_GPIO0OUTCFG_Pos (1UL) /*!< GPIO CFGA: GPIO0OUTCFG (Bit 1) */ +#define GPIO_CFGA_GPIO0OUTCFG_Msk (0x6UL) /*!< GPIO CFGA: GPIO0OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGA_GPIO0INCFG_Pos (0UL) /*!< GPIO CFGA: GPIO0INCFG (Bit 0) */ +#define GPIO_CFGA_GPIO0INCFG_Msk (0x1UL) /*!< GPIO CFGA: GPIO0INCFG (Bitfield-Mask: 0x01) */ +/* ========================================================= CFGB ========================================================== */ +#define GPIO_CFGB_GPIO15INTD_Pos (31UL) /*!< GPIO CFGB: GPIO15INTD (Bit 31) */ +#define GPIO_CFGB_GPIO15INTD_Msk (0x80000000UL) /*!< GPIO CFGB: GPIO15INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGB_GPIO15OUTCFG_Pos (29UL) /*!< GPIO CFGB: GPIO15OUTCFG (Bit 29) */ +#define GPIO_CFGB_GPIO15OUTCFG_Msk (0x60000000UL) /*!< GPIO CFGB: GPIO15OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGB_GPIO15INCFG_Pos (28UL) /*!< GPIO CFGB: GPIO15INCFG (Bit 28) */ +#define GPIO_CFGB_GPIO15INCFG_Msk (0x10000000UL) /*!< GPIO CFGB: GPIO15INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGB_GPIO14INTD_Pos (27UL) /*!< GPIO CFGB: GPIO14INTD (Bit 27) */ +#define GPIO_CFGB_GPIO14INTD_Msk (0x8000000UL) /*!< GPIO CFGB: GPIO14INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGB_GPIO14OUTCFG_Pos (25UL) /*!< GPIO CFGB: GPIO14OUTCFG (Bit 25) */ +#define GPIO_CFGB_GPIO14OUTCFG_Msk (0x6000000UL) /*!< GPIO CFGB: GPIO14OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGB_GPIO14INCFG_Pos (24UL) /*!< GPIO CFGB: GPIO14INCFG (Bit 24) */ +#define GPIO_CFGB_GPIO14INCFG_Msk (0x1000000UL) /*!< GPIO CFGB: GPIO14INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGB_GPIO13INTD_Pos (23UL) /*!< GPIO CFGB: GPIO13INTD (Bit 23) */ +#define GPIO_CFGB_GPIO13INTD_Msk (0x800000UL) /*!< GPIO CFGB: GPIO13INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGB_GPIO13OUTCFG_Pos (21UL) /*!< GPIO CFGB: GPIO13OUTCFG (Bit 21) */ +#define GPIO_CFGB_GPIO13OUTCFG_Msk (0x600000UL) /*!< GPIO CFGB: GPIO13OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGB_GPIO13INCFG_Pos (20UL) /*!< GPIO CFGB: GPIO13INCFG (Bit 20) */ +#define GPIO_CFGB_GPIO13INCFG_Msk (0x100000UL) /*!< GPIO CFGB: GPIO13INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGB_GPIO12INTD_Pos (19UL) /*!< GPIO CFGB: GPIO12INTD (Bit 19) */ +#define GPIO_CFGB_GPIO12INTD_Msk (0x80000UL) /*!< GPIO CFGB: GPIO12INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGB_GPIO12OUTCFG_Pos (17UL) /*!< GPIO CFGB: GPIO12OUTCFG (Bit 17) */ +#define GPIO_CFGB_GPIO12OUTCFG_Msk (0x60000UL) /*!< GPIO CFGB: GPIO12OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGB_GPIO12INCFG_Pos (16UL) /*!< GPIO CFGB: GPIO12INCFG (Bit 16) */ +#define GPIO_CFGB_GPIO12INCFG_Msk (0x10000UL) /*!< GPIO CFGB: GPIO12INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGB_GPIO11INTD_Pos (15UL) /*!< GPIO CFGB: GPIO11INTD (Bit 15) */ +#define GPIO_CFGB_GPIO11INTD_Msk (0x8000UL) /*!< GPIO CFGB: GPIO11INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGB_GPIO11OUTCFG_Pos (13UL) /*!< GPIO CFGB: GPIO11OUTCFG (Bit 13) */ +#define GPIO_CFGB_GPIO11OUTCFG_Msk (0x6000UL) /*!< GPIO CFGB: GPIO11OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGB_GPIO11INCFG_Pos (12UL) /*!< GPIO CFGB: GPIO11INCFG (Bit 12) */ +#define GPIO_CFGB_GPIO11INCFG_Msk (0x1000UL) /*!< GPIO CFGB: GPIO11INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGB_GPIO10INTD_Pos (11UL) /*!< GPIO CFGB: GPIO10INTD (Bit 11) */ +#define GPIO_CFGB_GPIO10INTD_Msk (0x800UL) /*!< GPIO CFGB: GPIO10INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGB_GPIO10OUTCFG_Pos (9UL) /*!< GPIO CFGB: GPIO10OUTCFG (Bit 9) */ +#define GPIO_CFGB_GPIO10OUTCFG_Msk (0x600UL) /*!< GPIO CFGB: GPIO10OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGB_GPIO10INCFG_Pos (8UL) /*!< GPIO CFGB: GPIO10INCFG (Bit 8) */ +#define GPIO_CFGB_GPIO10INCFG_Msk (0x100UL) /*!< GPIO CFGB: GPIO10INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGB_GPIO9INTD_Pos (7UL) /*!< GPIO CFGB: GPIO9INTD (Bit 7) */ +#define GPIO_CFGB_GPIO9INTD_Msk (0x80UL) /*!< GPIO CFGB: GPIO9INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGB_GPIO9OUTCFG_Pos (5UL) /*!< GPIO CFGB: GPIO9OUTCFG (Bit 5) */ +#define GPIO_CFGB_GPIO9OUTCFG_Msk (0x60UL) /*!< GPIO CFGB: GPIO9OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGB_GPIO9INCFG_Pos (4UL) /*!< GPIO CFGB: GPIO9INCFG (Bit 4) */ +#define GPIO_CFGB_GPIO9INCFG_Msk (0x10UL) /*!< GPIO CFGB: GPIO9INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGB_GPIO8INTD_Pos (3UL) /*!< GPIO CFGB: GPIO8INTD (Bit 3) */ +#define GPIO_CFGB_GPIO8INTD_Msk (0x8UL) /*!< GPIO CFGB: GPIO8INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGB_GPIO8OUTCFG_Pos (1UL) /*!< GPIO CFGB: GPIO8OUTCFG (Bit 1) */ +#define GPIO_CFGB_GPIO8OUTCFG_Msk (0x6UL) /*!< GPIO CFGB: GPIO8OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGB_GPIO8INCFG_Pos (0UL) /*!< GPIO CFGB: GPIO8INCFG (Bit 0) */ +#define GPIO_CFGB_GPIO8INCFG_Msk (0x1UL) /*!< GPIO CFGB: GPIO8INCFG (Bitfield-Mask: 0x01) */ +/* ========================================================= CFGC ========================================================== */ +#define GPIO_CFGC_GPIO23INTD_Pos (31UL) /*!< GPIO CFGC: GPIO23INTD (Bit 31) */ +#define GPIO_CFGC_GPIO23INTD_Msk (0x80000000UL) /*!< GPIO CFGC: GPIO23INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGC_GPIO23OUTCFG_Pos (29UL) /*!< GPIO CFGC: GPIO23OUTCFG (Bit 29) */ +#define GPIO_CFGC_GPIO23OUTCFG_Msk (0x60000000UL) /*!< GPIO CFGC: GPIO23OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGC_GPIO23INCFG_Pos (28UL) /*!< GPIO CFGC: GPIO23INCFG (Bit 28) */ +#define GPIO_CFGC_GPIO23INCFG_Msk (0x10000000UL) /*!< GPIO CFGC: GPIO23INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGC_GPIO22INTD_Pos (27UL) /*!< GPIO CFGC: GPIO22INTD (Bit 27) */ +#define GPIO_CFGC_GPIO22INTD_Msk (0x8000000UL) /*!< GPIO CFGC: GPIO22INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGC_GPIO22OUTCFG_Pos (25UL) /*!< GPIO CFGC: GPIO22OUTCFG (Bit 25) */ +#define GPIO_CFGC_GPIO22OUTCFG_Msk (0x6000000UL) /*!< GPIO CFGC: GPIO22OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGC_GPIO22INCFG_Pos (24UL) /*!< GPIO CFGC: GPIO22INCFG (Bit 24) */ +#define GPIO_CFGC_GPIO22INCFG_Msk (0x1000000UL) /*!< GPIO CFGC: GPIO22INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGC_GPIO21INTD_Pos (23UL) /*!< GPIO CFGC: GPIO21INTD (Bit 23) */ +#define GPIO_CFGC_GPIO21INTD_Msk (0x800000UL) /*!< GPIO CFGC: GPIO21INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGC_GPIO21OUTCFG_Pos (21UL) /*!< GPIO CFGC: GPIO21OUTCFG (Bit 21) */ +#define GPIO_CFGC_GPIO21OUTCFG_Msk (0x600000UL) /*!< GPIO CFGC: GPIO21OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGC_GPIO21INCFG_Pos (20UL) /*!< GPIO CFGC: GPIO21INCFG (Bit 20) */ +#define GPIO_CFGC_GPIO21INCFG_Msk (0x100000UL) /*!< GPIO CFGC: GPIO21INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGC_GPIO20INTD_Pos (19UL) /*!< GPIO CFGC: GPIO20INTD (Bit 19) */ +#define GPIO_CFGC_GPIO20INTD_Msk (0x80000UL) /*!< GPIO CFGC: GPIO20INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGC_GPIO20OUTCFG_Pos (17UL) /*!< GPIO CFGC: GPIO20OUTCFG (Bit 17) */ +#define GPIO_CFGC_GPIO20OUTCFG_Msk (0x60000UL) /*!< GPIO CFGC: GPIO20OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGC_GPIO20INCFG_Pos (16UL) /*!< GPIO CFGC: GPIO20INCFG (Bit 16) */ +#define GPIO_CFGC_GPIO20INCFG_Msk (0x10000UL) /*!< GPIO CFGC: GPIO20INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGC_GPIO19INTD_Pos (15UL) /*!< GPIO CFGC: GPIO19INTD (Bit 15) */ +#define GPIO_CFGC_GPIO19INTD_Msk (0x8000UL) /*!< GPIO CFGC: GPIO19INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGC_GPIO19OUTCFG_Pos (13UL) /*!< GPIO CFGC: GPIO19OUTCFG (Bit 13) */ +#define GPIO_CFGC_GPIO19OUTCFG_Msk (0x6000UL) /*!< GPIO CFGC: GPIO19OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGC_GPIO19INCFG_Pos (12UL) /*!< GPIO CFGC: GPIO19INCFG (Bit 12) */ +#define GPIO_CFGC_GPIO19INCFG_Msk (0x1000UL) /*!< GPIO CFGC: GPIO19INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGC_GPIO18INTD_Pos (11UL) /*!< GPIO CFGC: GPIO18INTD (Bit 11) */ +#define GPIO_CFGC_GPIO18INTD_Msk (0x800UL) /*!< GPIO CFGC: GPIO18INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGC_GPIO18OUTCFG_Pos (9UL) /*!< GPIO CFGC: GPIO18OUTCFG (Bit 9) */ +#define GPIO_CFGC_GPIO18OUTCFG_Msk (0x600UL) /*!< GPIO CFGC: GPIO18OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGC_GPIO18INCFG_Pos (8UL) /*!< GPIO CFGC: GPIO18INCFG (Bit 8) */ +#define GPIO_CFGC_GPIO18INCFG_Msk (0x100UL) /*!< GPIO CFGC: GPIO18INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGC_GPIO17INTD_Pos (7UL) /*!< GPIO CFGC: GPIO17INTD (Bit 7) */ +#define GPIO_CFGC_GPIO17INTD_Msk (0x80UL) /*!< GPIO CFGC: GPIO17INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGC_GPIO17OUTCFG_Pos (5UL) /*!< GPIO CFGC: GPIO17OUTCFG (Bit 5) */ +#define GPIO_CFGC_GPIO17OUTCFG_Msk (0x60UL) /*!< GPIO CFGC: GPIO17OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGC_GPIO17INCFG_Pos (4UL) /*!< GPIO CFGC: GPIO17INCFG (Bit 4) */ +#define GPIO_CFGC_GPIO17INCFG_Msk (0x10UL) /*!< GPIO CFGC: GPIO17INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGC_GPIO16INTD_Pos (3UL) /*!< GPIO CFGC: GPIO16INTD (Bit 3) */ +#define GPIO_CFGC_GPIO16INTD_Msk (0x8UL) /*!< GPIO CFGC: GPIO16INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGC_GPIO16OUTCFG_Pos (1UL) /*!< GPIO CFGC: GPIO16OUTCFG (Bit 1) */ +#define GPIO_CFGC_GPIO16OUTCFG_Msk (0x6UL) /*!< GPIO CFGC: GPIO16OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGC_GPIO16INCFG_Pos (0UL) /*!< GPIO CFGC: GPIO16INCFG (Bit 0) */ +#define GPIO_CFGC_GPIO16INCFG_Msk (0x1UL) /*!< GPIO CFGC: GPIO16INCFG (Bitfield-Mask: 0x01) */ +/* ========================================================= CFGD ========================================================== */ +#define GPIO_CFGD_GPIO31INTD_Pos (31UL) /*!< GPIO CFGD: GPIO31INTD (Bit 31) */ +#define GPIO_CFGD_GPIO31INTD_Msk (0x80000000UL) /*!< GPIO CFGD: GPIO31INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGD_GPIO31OUTCFG_Pos (29UL) /*!< GPIO CFGD: GPIO31OUTCFG (Bit 29) */ +#define GPIO_CFGD_GPIO31OUTCFG_Msk (0x60000000UL) /*!< GPIO CFGD: GPIO31OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGD_GPIO31INCFG_Pos (28UL) /*!< GPIO CFGD: GPIO31INCFG (Bit 28) */ +#define GPIO_CFGD_GPIO31INCFG_Msk (0x10000000UL) /*!< GPIO CFGD: GPIO31INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGD_GPIO30INTD_Pos (27UL) /*!< GPIO CFGD: GPIO30INTD (Bit 27) */ +#define GPIO_CFGD_GPIO30INTD_Msk (0x8000000UL) /*!< GPIO CFGD: GPIO30INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGD_GPIO30OUTCFG_Pos (25UL) /*!< GPIO CFGD: GPIO30OUTCFG (Bit 25) */ +#define GPIO_CFGD_GPIO30OUTCFG_Msk (0x6000000UL) /*!< GPIO CFGD: GPIO30OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGD_GPIO30INCFG_Pos (24UL) /*!< GPIO CFGD: GPIO30INCFG (Bit 24) */ +#define GPIO_CFGD_GPIO30INCFG_Msk (0x1000000UL) /*!< GPIO CFGD: GPIO30INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGD_GPIO29INTD_Pos (23UL) /*!< GPIO CFGD: GPIO29INTD (Bit 23) */ +#define GPIO_CFGD_GPIO29INTD_Msk (0x800000UL) /*!< GPIO CFGD: GPIO29INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGD_GPIO29OUTCFG_Pos (21UL) /*!< GPIO CFGD: GPIO29OUTCFG (Bit 21) */ +#define GPIO_CFGD_GPIO29OUTCFG_Msk (0x600000UL) /*!< GPIO CFGD: GPIO29OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGD_GPIO29INCFG_Pos (20UL) /*!< GPIO CFGD: GPIO29INCFG (Bit 20) */ +#define GPIO_CFGD_GPIO29INCFG_Msk (0x100000UL) /*!< GPIO CFGD: GPIO29INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGD_GPIO28INTD_Pos (19UL) /*!< GPIO CFGD: GPIO28INTD (Bit 19) */ +#define GPIO_CFGD_GPIO28INTD_Msk (0x80000UL) /*!< GPIO CFGD: GPIO28INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGD_GPIO28OUTCFG_Pos (17UL) /*!< GPIO CFGD: GPIO28OUTCFG (Bit 17) */ +#define GPIO_CFGD_GPIO28OUTCFG_Msk (0x60000UL) /*!< GPIO CFGD: GPIO28OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGD_GPIO28INCFG_Pos (16UL) /*!< GPIO CFGD: GPIO28INCFG (Bit 16) */ +#define GPIO_CFGD_GPIO28INCFG_Msk (0x10000UL) /*!< GPIO CFGD: GPIO28INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGD_GPIO27INTD_Pos (15UL) /*!< GPIO CFGD: GPIO27INTD (Bit 15) */ +#define GPIO_CFGD_GPIO27INTD_Msk (0x8000UL) /*!< GPIO CFGD: GPIO27INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGD_GPIO27OUTCFG_Pos (13UL) /*!< GPIO CFGD: GPIO27OUTCFG (Bit 13) */ +#define GPIO_CFGD_GPIO27OUTCFG_Msk (0x6000UL) /*!< GPIO CFGD: GPIO27OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGD_GPIO27INCFG_Pos (12UL) /*!< GPIO CFGD: GPIO27INCFG (Bit 12) */ +#define GPIO_CFGD_GPIO27INCFG_Msk (0x1000UL) /*!< GPIO CFGD: GPIO27INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGD_GPIO26INTD_Pos (11UL) /*!< GPIO CFGD: GPIO26INTD (Bit 11) */ +#define GPIO_CFGD_GPIO26INTD_Msk (0x800UL) /*!< GPIO CFGD: GPIO26INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGD_GPIO26OUTCFG_Pos (9UL) /*!< GPIO CFGD: GPIO26OUTCFG (Bit 9) */ +#define GPIO_CFGD_GPIO26OUTCFG_Msk (0x600UL) /*!< GPIO CFGD: GPIO26OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGD_GPIO26INCFG_Pos (8UL) /*!< GPIO CFGD: GPIO26INCFG (Bit 8) */ +#define GPIO_CFGD_GPIO26INCFG_Msk (0x100UL) /*!< GPIO CFGD: GPIO26INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGD_GPIO25INTD_Pos (7UL) /*!< GPIO CFGD: GPIO25INTD (Bit 7) */ +#define GPIO_CFGD_GPIO25INTD_Msk (0x80UL) /*!< GPIO CFGD: GPIO25INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGD_GPIO25OUTCFG_Pos (5UL) /*!< GPIO CFGD: GPIO25OUTCFG (Bit 5) */ +#define GPIO_CFGD_GPIO25OUTCFG_Msk (0x60UL) /*!< GPIO CFGD: GPIO25OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGD_GPIO25INCFG_Pos (4UL) /*!< GPIO CFGD: GPIO25INCFG (Bit 4) */ +#define GPIO_CFGD_GPIO25INCFG_Msk (0x10UL) /*!< GPIO CFGD: GPIO25INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGD_GPIO24INTD_Pos (3UL) /*!< GPIO CFGD: GPIO24INTD (Bit 3) */ +#define GPIO_CFGD_GPIO24INTD_Msk (0x8UL) /*!< GPIO CFGD: GPIO24INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGD_GPIO24OUTCFG_Pos (1UL) /*!< GPIO CFGD: GPIO24OUTCFG (Bit 1) */ +#define GPIO_CFGD_GPIO24OUTCFG_Msk (0x6UL) /*!< GPIO CFGD: GPIO24OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGD_GPIO24INCFG_Pos (0UL) /*!< GPIO CFGD: GPIO24INCFG (Bit 0) */ +#define GPIO_CFGD_GPIO24INCFG_Msk (0x1UL) /*!< GPIO CFGD: GPIO24INCFG (Bitfield-Mask: 0x01) */ +/* ========================================================= CFGE ========================================================== */ +#define GPIO_CFGE_GPIO39INTD_Pos (31UL) /*!< GPIO CFGE: GPIO39INTD (Bit 31) */ +#define GPIO_CFGE_GPIO39INTD_Msk (0x80000000UL) /*!< GPIO CFGE: GPIO39INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGE_GPIO39OUTCFG_Pos (29UL) /*!< GPIO CFGE: GPIO39OUTCFG (Bit 29) */ +#define GPIO_CFGE_GPIO39OUTCFG_Msk (0x60000000UL) /*!< GPIO CFGE: GPIO39OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGE_GPIO39INCFG_Pos (28UL) /*!< GPIO CFGE: GPIO39INCFG (Bit 28) */ +#define GPIO_CFGE_GPIO39INCFG_Msk (0x10000000UL) /*!< GPIO CFGE: GPIO39INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGE_GPIO38INTD_Pos (27UL) /*!< GPIO CFGE: GPIO38INTD (Bit 27) */ +#define GPIO_CFGE_GPIO38INTD_Msk (0x8000000UL) /*!< GPIO CFGE: GPIO38INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGE_GPIO38OUTCFG_Pos (25UL) /*!< GPIO CFGE: GPIO38OUTCFG (Bit 25) */ +#define GPIO_CFGE_GPIO38OUTCFG_Msk (0x6000000UL) /*!< GPIO CFGE: GPIO38OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGE_GPIO38INCFG_Pos (24UL) /*!< GPIO CFGE: GPIO38INCFG (Bit 24) */ +#define GPIO_CFGE_GPIO38INCFG_Msk (0x1000000UL) /*!< GPIO CFGE: GPIO38INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGE_GPIO37INTD_Pos (23UL) /*!< GPIO CFGE: GPIO37INTD (Bit 23) */ +#define GPIO_CFGE_GPIO37INTD_Msk (0x800000UL) /*!< GPIO CFGE: GPIO37INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGE_GPIO37OUTCFG_Pos (21UL) /*!< GPIO CFGE: GPIO37OUTCFG (Bit 21) */ +#define GPIO_CFGE_GPIO37OUTCFG_Msk (0x600000UL) /*!< GPIO CFGE: GPIO37OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGE_GPIO37INCFG_Pos (20UL) /*!< GPIO CFGE: GPIO37INCFG (Bit 20) */ +#define GPIO_CFGE_GPIO37INCFG_Msk (0x100000UL) /*!< GPIO CFGE: GPIO37INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGE_GPIO36INTD_Pos (19UL) /*!< GPIO CFGE: GPIO36INTD (Bit 19) */ +#define GPIO_CFGE_GPIO36INTD_Msk (0x80000UL) /*!< GPIO CFGE: GPIO36INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGE_GPIO36OUTCFG_Pos (17UL) /*!< GPIO CFGE: GPIO36OUTCFG (Bit 17) */ +#define GPIO_CFGE_GPIO36OUTCFG_Msk (0x60000UL) /*!< GPIO CFGE: GPIO36OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGE_GPIO36INCFG_Pos (16UL) /*!< GPIO CFGE: GPIO36INCFG (Bit 16) */ +#define GPIO_CFGE_GPIO36INCFG_Msk (0x10000UL) /*!< GPIO CFGE: GPIO36INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGE_GPIO35INTD_Pos (15UL) /*!< GPIO CFGE: GPIO35INTD (Bit 15) */ +#define GPIO_CFGE_GPIO35INTD_Msk (0x8000UL) /*!< GPIO CFGE: GPIO35INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGE_GPIO35OUTCFG_Pos (13UL) /*!< GPIO CFGE: GPIO35OUTCFG (Bit 13) */ +#define GPIO_CFGE_GPIO35OUTCFG_Msk (0x6000UL) /*!< GPIO CFGE: GPIO35OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGE_GPIO35INCFG_Pos (12UL) /*!< GPIO CFGE: GPIO35INCFG (Bit 12) */ +#define GPIO_CFGE_GPIO35INCFG_Msk (0x1000UL) /*!< GPIO CFGE: GPIO35INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGE_GPIO34INTD_Pos (11UL) /*!< GPIO CFGE: GPIO34INTD (Bit 11) */ +#define GPIO_CFGE_GPIO34INTD_Msk (0x800UL) /*!< GPIO CFGE: GPIO34INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGE_GPIO34OUTCFG_Pos (9UL) /*!< GPIO CFGE: GPIO34OUTCFG (Bit 9) */ +#define GPIO_CFGE_GPIO34OUTCFG_Msk (0x600UL) /*!< GPIO CFGE: GPIO34OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGE_GPIO34INCFG_Pos (8UL) /*!< GPIO CFGE: GPIO34INCFG (Bit 8) */ +#define GPIO_CFGE_GPIO34INCFG_Msk (0x100UL) /*!< GPIO CFGE: GPIO34INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGE_GPIO33INTD_Pos (7UL) /*!< GPIO CFGE: GPIO33INTD (Bit 7) */ +#define GPIO_CFGE_GPIO33INTD_Msk (0x80UL) /*!< GPIO CFGE: GPIO33INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGE_GPIO33OUTCFG_Pos (5UL) /*!< GPIO CFGE: GPIO33OUTCFG (Bit 5) */ +#define GPIO_CFGE_GPIO33OUTCFG_Msk (0x60UL) /*!< GPIO CFGE: GPIO33OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGE_GPIO33INCFG_Pos (4UL) /*!< GPIO CFGE: GPIO33INCFG (Bit 4) */ +#define GPIO_CFGE_GPIO33INCFG_Msk (0x10UL) /*!< GPIO CFGE: GPIO33INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGE_GPIO32INTD_Pos (3UL) /*!< GPIO CFGE: GPIO32INTD (Bit 3) */ +#define GPIO_CFGE_GPIO32INTD_Msk (0x8UL) /*!< GPIO CFGE: GPIO32INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGE_GPIO32OUTCFG_Pos (1UL) /*!< GPIO CFGE: GPIO32OUTCFG (Bit 1) */ +#define GPIO_CFGE_GPIO32OUTCFG_Msk (0x6UL) /*!< GPIO CFGE: GPIO32OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGE_GPIO32INCFG_Pos (0UL) /*!< GPIO CFGE: GPIO32INCFG (Bit 0) */ +#define GPIO_CFGE_GPIO32INCFG_Msk (0x1UL) /*!< GPIO CFGE: GPIO32INCFG (Bitfield-Mask: 0x01) */ +/* ========================================================= CFGF ========================================================== */ +#define GPIO_CFGF_GPIO47INTD_Pos (31UL) /*!< GPIO CFGF: GPIO47INTD (Bit 31) */ +#define GPIO_CFGF_GPIO47INTD_Msk (0x80000000UL) /*!< GPIO CFGF: GPIO47INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGF_GPIO47OUTCFG_Pos (29UL) /*!< GPIO CFGF: GPIO47OUTCFG (Bit 29) */ +#define GPIO_CFGF_GPIO47OUTCFG_Msk (0x60000000UL) /*!< GPIO CFGF: GPIO47OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGF_GPIO47INCFG_Pos (28UL) /*!< GPIO CFGF: GPIO47INCFG (Bit 28) */ +#define GPIO_CFGF_GPIO47INCFG_Msk (0x10000000UL) /*!< GPIO CFGF: GPIO47INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGF_GPIO46INTD_Pos (27UL) /*!< GPIO CFGF: GPIO46INTD (Bit 27) */ +#define GPIO_CFGF_GPIO46INTD_Msk (0x8000000UL) /*!< GPIO CFGF: GPIO46INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGF_GPIO46OUTCFG_Pos (25UL) /*!< GPIO CFGF: GPIO46OUTCFG (Bit 25) */ +#define GPIO_CFGF_GPIO46OUTCFG_Msk (0x6000000UL) /*!< GPIO CFGF: GPIO46OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGF_GPIO46INCFG_Pos (24UL) /*!< GPIO CFGF: GPIO46INCFG (Bit 24) */ +#define GPIO_CFGF_GPIO46INCFG_Msk (0x1000000UL) /*!< GPIO CFGF: GPIO46INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGF_GPIO45INTD_Pos (23UL) /*!< GPIO CFGF: GPIO45INTD (Bit 23) */ +#define GPIO_CFGF_GPIO45INTD_Msk (0x800000UL) /*!< GPIO CFGF: GPIO45INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGF_GPIO45OUTCFG_Pos (21UL) /*!< GPIO CFGF: GPIO45OUTCFG (Bit 21) */ +#define GPIO_CFGF_GPIO45OUTCFG_Msk (0x600000UL) /*!< GPIO CFGF: GPIO45OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGF_GPIO45INCFG_Pos (20UL) /*!< GPIO CFGF: GPIO45INCFG (Bit 20) */ +#define GPIO_CFGF_GPIO45INCFG_Msk (0x100000UL) /*!< GPIO CFGF: GPIO45INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGF_GPIO44INTD_Pos (19UL) /*!< GPIO CFGF: GPIO44INTD (Bit 19) */ +#define GPIO_CFGF_GPIO44INTD_Msk (0x80000UL) /*!< GPIO CFGF: GPIO44INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGF_GPIO44OUTCFG_Pos (17UL) /*!< GPIO CFGF: GPIO44OUTCFG (Bit 17) */ +#define GPIO_CFGF_GPIO44OUTCFG_Msk (0x60000UL) /*!< GPIO CFGF: GPIO44OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGF_GPIO44INCFG_Pos (16UL) /*!< GPIO CFGF: GPIO44INCFG (Bit 16) */ +#define GPIO_CFGF_GPIO44INCFG_Msk (0x10000UL) /*!< GPIO CFGF: GPIO44INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGF_GPIO43INTD_Pos (15UL) /*!< GPIO CFGF: GPIO43INTD (Bit 15) */ +#define GPIO_CFGF_GPIO43INTD_Msk (0x8000UL) /*!< GPIO CFGF: GPIO43INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGF_GPIO43OUTCFG_Pos (13UL) /*!< GPIO CFGF: GPIO43OUTCFG (Bit 13) */ +#define GPIO_CFGF_GPIO43OUTCFG_Msk (0x6000UL) /*!< GPIO CFGF: GPIO43OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGF_GPIO43INCFG_Pos (12UL) /*!< GPIO CFGF: GPIO43INCFG (Bit 12) */ +#define GPIO_CFGF_GPIO43INCFG_Msk (0x1000UL) /*!< GPIO CFGF: GPIO43INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGF_GPIO42INTD_Pos (11UL) /*!< GPIO CFGF: GPIO42INTD (Bit 11) */ +#define GPIO_CFGF_GPIO42INTD_Msk (0x800UL) /*!< GPIO CFGF: GPIO42INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGF_GPIO42OUTCFG_Pos (9UL) /*!< GPIO CFGF: GPIO42OUTCFG (Bit 9) */ +#define GPIO_CFGF_GPIO42OUTCFG_Msk (0x600UL) /*!< GPIO CFGF: GPIO42OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGF_GPIO42INCFG_Pos (8UL) /*!< GPIO CFGF: GPIO42INCFG (Bit 8) */ +#define GPIO_CFGF_GPIO42INCFG_Msk (0x100UL) /*!< GPIO CFGF: GPIO42INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGF_GPIO41INTD_Pos (7UL) /*!< GPIO CFGF: GPIO41INTD (Bit 7) */ +#define GPIO_CFGF_GPIO41INTD_Msk (0x80UL) /*!< GPIO CFGF: GPIO41INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGF_GPIO41OUTCFG_Pos (5UL) /*!< GPIO CFGF: GPIO41OUTCFG (Bit 5) */ +#define GPIO_CFGF_GPIO41OUTCFG_Msk (0x60UL) /*!< GPIO CFGF: GPIO41OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGF_GPIO41INCFG_Pos (4UL) /*!< GPIO CFGF: GPIO41INCFG (Bit 4) */ +#define GPIO_CFGF_GPIO41INCFG_Msk (0x10UL) /*!< GPIO CFGF: GPIO41INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGF_GPIO40INTD_Pos (3UL) /*!< GPIO CFGF: GPIO40INTD (Bit 3) */ +#define GPIO_CFGF_GPIO40INTD_Msk (0x8UL) /*!< GPIO CFGF: GPIO40INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGF_GPIO40OUTCFG_Pos (1UL) /*!< GPIO CFGF: GPIO40OUTCFG (Bit 1) */ +#define GPIO_CFGF_GPIO40OUTCFG_Msk (0x6UL) /*!< GPIO CFGF: GPIO40OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGF_GPIO40INCFG_Pos (0UL) /*!< GPIO CFGF: GPIO40INCFG (Bit 0) */ +#define GPIO_CFGF_GPIO40INCFG_Msk (0x1UL) /*!< GPIO CFGF: GPIO40INCFG (Bitfield-Mask: 0x01) */ +/* ========================================================= CFGG ========================================================== */ +#define GPIO_CFGG_GPIO49INTD_Pos (7UL) /*!< GPIO CFGG: GPIO49INTD (Bit 7) */ +#define GPIO_CFGG_GPIO49INTD_Msk (0x80UL) /*!< GPIO CFGG: GPIO49INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGG_GPIO49OUTCFG_Pos (5UL) /*!< GPIO CFGG: GPIO49OUTCFG (Bit 5) */ +#define GPIO_CFGG_GPIO49OUTCFG_Msk (0x60UL) /*!< GPIO CFGG: GPIO49OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGG_GPIO49INCFG_Pos (4UL) /*!< GPIO CFGG: GPIO49INCFG (Bit 4) */ +#define GPIO_CFGG_GPIO49INCFG_Msk (0x10UL) /*!< GPIO CFGG: GPIO49INCFG (Bitfield-Mask: 0x01) */ +#define GPIO_CFGG_GPIO48INTD_Pos (3UL) /*!< GPIO CFGG: GPIO48INTD (Bit 3) */ +#define GPIO_CFGG_GPIO48INTD_Msk (0x8UL) /*!< GPIO CFGG: GPIO48INTD (Bitfield-Mask: 0x01) */ +#define GPIO_CFGG_GPIO48OUTCFG_Pos (1UL) /*!< GPIO CFGG: GPIO48OUTCFG (Bit 1) */ +#define GPIO_CFGG_GPIO48OUTCFG_Msk (0x6UL) /*!< GPIO CFGG: GPIO48OUTCFG (Bitfield-Mask: 0x03) */ +#define GPIO_CFGG_GPIO48INCFG_Pos (0UL) /*!< GPIO CFGG: GPIO48INCFG (Bit 0) */ +#define GPIO_CFGG_GPIO48INCFG_Msk (0x1UL) /*!< GPIO CFGG: GPIO48INCFG (Bitfield-Mask: 0x01) */ +/* ======================================================== PADKEY ========================================================= */ +#define GPIO_PADKEY_PADKEY_Pos (0UL) /*!< GPIO PADKEY: PADKEY (Bit 0) */ +#define GPIO_PADKEY_PADKEY_Msk (0xffffffffUL) /*!< GPIO PADKEY: PADKEY (Bitfield-Mask: 0xffffffff) */ +/* ========================================================== RDA ========================================================== */ +#define GPIO_RDA_RDA_Pos (0UL) /*!< GPIO RDA: RDA (Bit 0) */ +#define GPIO_RDA_RDA_Msk (0xffffffffUL) /*!< GPIO RDA: RDA (Bitfield-Mask: 0xffffffff) */ +/* ========================================================== RDB ========================================================== */ +#define GPIO_RDB_RDB_Pos (0UL) /*!< GPIO RDB: RDB (Bit 0) */ +#define GPIO_RDB_RDB_Msk (0x3ffffUL) /*!< GPIO RDB: RDB (Bitfield-Mask: 0x3ffff) */ +/* ========================================================== WTA ========================================================== */ +#define GPIO_WTA_WTA_Pos (0UL) /*!< GPIO WTA: WTA (Bit 0) */ +#define GPIO_WTA_WTA_Msk (0xffffffffUL) /*!< GPIO WTA: WTA (Bitfield-Mask: 0xffffffff) */ +/* ========================================================== WTB ========================================================== */ +#define GPIO_WTB_WTB_Pos (0UL) /*!< GPIO WTB: WTB (Bit 0) */ +#define GPIO_WTB_WTB_Msk (0x3ffffUL) /*!< GPIO WTB: WTB (Bitfield-Mask: 0x3ffff) */ +/* ========================================================= WTSA ========================================================== */ +#define GPIO_WTSA_WTSA_Pos (0UL) /*!< GPIO WTSA: WTSA (Bit 0) */ +#define GPIO_WTSA_WTSA_Msk (0xffffffffUL) /*!< GPIO WTSA: WTSA (Bitfield-Mask: 0xffffffff) */ +/* ========================================================= WTSB ========================================================== */ +#define GPIO_WTSB_WTSB_Pos (0UL) /*!< GPIO WTSB: WTSB (Bit 0) */ +#define GPIO_WTSB_WTSB_Msk (0x3ffffUL) /*!< GPIO WTSB: WTSB (Bitfield-Mask: 0x3ffff) */ +/* ========================================================= WTCA ========================================================== */ +#define GPIO_WTCA_WTCA_Pos (0UL) /*!< GPIO WTCA: WTCA (Bit 0) */ +#define GPIO_WTCA_WTCA_Msk (0xffffffffUL) /*!< GPIO WTCA: WTCA (Bitfield-Mask: 0xffffffff) */ +/* ========================================================= WTCB ========================================================== */ +#define GPIO_WTCB_WTCB_Pos (0UL) /*!< GPIO WTCB: WTCB (Bit 0) */ +#define GPIO_WTCB_WTCB_Msk (0x3ffffUL) /*!< GPIO WTCB: WTCB (Bitfield-Mask: 0x3ffff) */ +/* ========================================================== ENA ========================================================== */ +#define GPIO_ENA_ENA_Pos (0UL) /*!< GPIO ENA: ENA (Bit 0) */ +#define GPIO_ENA_ENA_Msk (0xffffffffUL) /*!< GPIO ENA: ENA (Bitfield-Mask: 0xffffffff) */ +/* ========================================================== ENB ========================================================== */ +#define GPIO_ENB_ENB_Pos (0UL) /*!< GPIO ENB: ENB (Bit 0) */ +#define GPIO_ENB_ENB_Msk (0x3ffffUL) /*!< GPIO ENB: ENB (Bitfield-Mask: 0x3ffff) */ +/* ========================================================= ENSA ========================================================== */ +#define GPIO_ENSA_ENSA_Pos (0UL) /*!< GPIO ENSA: ENSA (Bit 0) */ +#define GPIO_ENSA_ENSA_Msk (0xffffffffUL) /*!< GPIO ENSA: ENSA (Bitfield-Mask: 0xffffffff) */ +/* ========================================================= ENSB ========================================================== */ +#define GPIO_ENSB_ENSB_Pos (0UL) /*!< GPIO ENSB: ENSB (Bit 0) */ +#define GPIO_ENSB_ENSB_Msk (0x3ffffUL) /*!< GPIO ENSB: ENSB (Bitfield-Mask: 0x3ffff) */ +/* ========================================================= ENCA ========================================================== */ +#define GPIO_ENCA_ENCA_Pos (0UL) /*!< GPIO ENCA: ENCA (Bit 0) */ +#define GPIO_ENCA_ENCA_Msk (0xffffffffUL) /*!< GPIO ENCA: ENCA (Bitfield-Mask: 0xffffffff) */ +/* ========================================================= ENCB ========================================================== */ +#define GPIO_ENCB_ENCB_Pos (0UL) /*!< GPIO ENCB: ENCB (Bit 0) */ +#define GPIO_ENCB_ENCB_Msk (0x3ffffUL) /*!< GPIO ENCB: ENCB (Bitfield-Mask: 0x3ffff) */ +/* ======================================================== STMRCAP ======================================================== */ +#define GPIO_STMRCAP_STPOL3_Pos (30UL) /*!< GPIO STMRCAP: STPOL3 (Bit 30) */ +#define GPIO_STMRCAP_STPOL3_Msk (0x40000000UL) /*!< GPIO STMRCAP: STPOL3 (Bitfield-Mask: 0x01) */ +#define GPIO_STMRCAP_STSEL3_Pos (24UL) /*!< GPIO STMRCAP: STSEL3 (Bit 24) */ +#define GPIO_STMRCAP_STSEL3_Msk (0x3f000000UL) /*!< GPIO STMRCAP: STSEL3 (Bitfield-Mask: 0x3f) */ +#define GPIO_STMRCAP_STPOL2_Pos (22UL) /*!< GPIO STMRCAP: STPOL2 (Bit 22) */ +#define GPIO_STMRCAP_STPOL2_Msk (0x400000UL) /*!< GPIO STMRCAP: STPOL2 (Bitfield-Mask: 0x01) */ +#define GPIO_STMRCAP_STSEL2_Pos (16UL) /*!< GPIO STMRCAP: STSEL2 (Bit 16) */ +#define GPIO_STMRCAP_STSEL2_Msk (0x3f0000UL) /*!< GPIO STMRCAP: STSEL2 (Bitfield-Mask: 0x3f) */ +#define GPIO_STMRCAP_STPOL1_Pos (14UL) /*!< GPIO STMRCAP: STPOL1 (Bit 14) */ +#define GPIO_STMRCAP_STPOL1_Msk (0x4000UL) /*!< GPIO STMRCAP: STPOL1 (Bitfield-Mask: 0x01) */ +#define GPIO_STMRCAP_STSEL1_Pos (8UL) /*!< GPIO STMRCAP: STSEL1 (Bit 8) */ +#define GPIO_STMRCAP_STSEL1_Msk (0x3f00UL) /*!< GPIO STMRCAP: STSEL1 (Bitfield-Mask: 0x3f) */ +#define GPIO_STMRCAP_STPOL0_Pos (6UL) /*!< GPIO STMRCAP: STPOL0 (Bit 6) */ +#define GPIO_STMRCAP_STPOL0_Msk (0x40UL) /*!< GPIO STMRCAP: STPOL0 (Bitfield-Mask: 0x01) */ +#define GPIO_STMRCAP_STSEL0_Pos (0UL) /*!< GPIO STMRCAP: STSEL0 (Bit 0) */ +#define GPIO_STMRCAP_STSEL0_Msk (0x3fUL) /*!< GPIO STMRCAP: STSEL0 (Bitfield-Mask: 0x3f) */ +/* ======================================================== IOM0IRQ ======================================================== */ +#define GPIO_IOM0IRQ_IOM0IRQ_Pos (0UL) /*!< GPIO IOM0IRQ: IOM0IRQ (Bit 0) */ +#define GPIO_IOM0IRQ_IOM0IRQ_Msk (0x3fUL) /*!< GPIO IOM0IRQ: IOM0IRQ (Bitfield-Mask: 0x3f) */ +/* ======================================================== IOM1IRQ ======================================================== */ +#define GPIO_IOM1IRQ_IOM1IRQ_Pos (0UL) /*!< GPIO IOM1IRQ: IOM1IRQ (Bit 0) */ +#define GPIO_IOM1IRQ_IOM1IRQ_Msk (0x3fUL) /*!< GPIO IOM1IRQ: IOM1IRQ (Bitfield-Mask: 0x3f) */ +/* ======================================================== IOM2IRQ ======================================================== */ +#define GPIO_IOM2IRQ_IOM2IRQ_Pos (0UL) /*!< GPIO IOM2IRQ: IOM2IRQ (Bit 0) */ +#define GPIO_IOM2IRQ_IOM2IRQ_Msk (0x3fUL) /*!< GPIO IOM2IRQ: IOM2IRQ (Bitfield-Mask: 0x3f) */ +/* ======================================================== IOM3IRQ ======================================================== */ +#define GPIO_IOM3IRQ_IOM3IRQ_Pos (0UL) /*!< GPIO IOM3IRQ: IOM3IRQ (Bit 0) */ +#define GPIO_IOM3IRQ_IOM3IRQ_Msk (0x3fUL) /*!< GPIO IOM3IRQ: IOM3IRQ (Bitfield-Mask: 0x3f) */ +/* ======================================================== IOM4IRQ ======================================================== */ +#define GPIO_IOM4IRQ_IOM4IRQ_Pos (0UL) /*!< GPIO IOM4IRQ: IOM4IRQ (Bit 0) */ +#define GPIO_IOM4IRQ_IOM4IRQ_Msk (0x3fUL) /*!< GPIO IOM4IRQ: IOM4IRQ (Bitfield-Mask: 0x3f) */ +/* ======================================================== IOM5IRQ ======================================================== */ +#define GPIO_IOM5IRQ_IOM5IRQ_Pos (0UL) /*!< GPIO IOM5IRQ: IOM5IRQ (Bit 0) */ +#define GPIO_IOM5IRQ_IOM5IRQ_Msk (0x3fUL) /*!< GPIO IOM5IRQ: IOM5IRQ (Bitfield-Mask: 0x3f) */ +/* ======================================================= BLEIFIRQ ======================================================== */ +#define GPIO_BLEIFIRQ_BLEIFIRQ_Pos (0UL) /*!< GPIO BLEIFIRQ: BLEIFIRQ (Bit 0) */ +#define GPIO_BLEIFIRQ_BLEIFIRQ_Msk (0x3fUL) /*!< GPIO BLEIFIRQ: BLEIFIRQ (Bitfield-Mask: 0x3f) */ +/* ======================================================== GPIOOBS ======================================================== */ +#define GPIO_GPIOOBS_OBS_DATA_Pos (0UL) /*!< GPIO GPIOOBS: OBS_DATA (Bit 0) */ +#define GPIO_GPIOOBS_OBS_DATA_Msk (0xffffUL) /*!< GPIO GPIOOBS: OBS_DATA (Bitfield-Mask: 0xffff) */ +/* ====================================================== ALTPADCFGA ======================================================= */ +#define GPIO_ALTPADCFGA_PAD3_SR_Pos (28UL) /*!< GPIO ALTPADCFGA: PAD3_SR (Bit 28) */ +#define GPIO_ALTPADCFGA_PAD3_SR_Msk (0x10000000UL) /*!< GPIO ALTPADCFGA: PAD3_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGA_PAD3_DS1_Pos (24UL) /*!< GPIO ALTPADCFGA: PAD3_DS1 (Bit 24) */ +#define GPIO_ALTPADCFGA_PAD3_DS1_Msk (0x1000000UL) /*!< GPIO ALTPADCFGA: PAD3_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGA_PAD2_SR_Pos (20UL) /*!< GPIO ALTPADCFGA: PAD2_SR (Bit 20) */ +#define GPIO_ALTPADCFGA_PAD2_SR_Msk (0x100000UL) /*!< GPIO ALTPADCFGA: PAD2_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGA_PAD2_DS1_Pos (16UL) /*!< GPIO ALTPADCFGA: PAD2_DS1 (Bit 16) */ +#define GPIO_ALTPADCFGA_PAD2_DS1_Msk (0x10000UL) /*!< GPIO ALTPADCFGA: PAD2_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGA_PAD1_SR_Pos (12UL) /*!< GPIO ALTPADCFGA: PAD1_SR (Bit 12) */ +#define GPIO_ALTPADCFGA_PAD1_SR_Msk (0x1000UL) /*!< GPIO ALTPADCFGA: PAD1_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGA_PAD1_DS1_Pos (8UL) /*!< GPIO ALTPADCFGA: PAD1_DS1 (Bit 8) */ +#define GPIO_ALTPADCFGA_PAD1_DS1_Msk (0x100UL) /*!< GPIO ALTPADCFGA: PAD1_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGA_PAD0_SR_Pos (4UL) /*!< GPIO ALTPADCFGA: PAD0_SR (Bit 4) */ +#define GPIO_ALTPADCFGA_PAD0_SR_Msk (0x10UL) /*!< GPIO ALTPADCFGA: PAD0_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGA_PAD0_DS1_Pos (0UL) /*!< GPIO ALTPADCFGA: PAD0_DS1 (Bit 0) */ +#define GPIO_ALTPADCFGA_PAD0_DS1_Msk (0x1UL) /*!< GPIO ALTPADCFGA: PAD0_DS1 (Bitfield-Mask: 0x01) */ +/* ====================================================== ALTPADCFGB ======================================================= */ +#define GPIO_ALTPADCFGB_PAD7_SR_Pos (28UL) /*!< GPIO ALTPADCFGB: PAD7_SR (Bit 28) */ +#define GPIO_ALTPADCFGB_PAD7_SR_Msk (0x10000000UL) /*!< GPIO ALTPADCFGB: PAD7_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGB_PAD7_DS1_Pos (24UL) /*!< GPIO ALTPADCFGB: PAD7_DS1 (Bit 24) */ +#define GPIO_ALTPADCFGB_PAD7_DS1_Msk (0x1000000UL) /*!< GPIO ALTPADCFGB: PAD7_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGB_PAD6_SR_Pos (20UL) /*!< GPIO ALTPADCFGB: PAD6_SR (Bit 20) */ +#define GPIO_ALTPADCFGB_PAD6_SR_Msk (0x100000UL) /*!< GPIO ALTPADCFGB: PAD6_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGB_PAD6_DS1_Pos (16UL) /*!< GPIO ALTPADCFGB: PAD6_DS1 (Bit 16) */ +#define GPIO_ALTPADCFGB_PAD6_DS1_Msk (0x10000UL) /*!< GPIO ALTPADCFGB: PAD6_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGB_PAD5_SR_Pos (12UL) /*!< GPIO ALTPADCFGB: PAD5_SR (Bit 12) */ +#define GPIO_ALTPADCFGB_PAD5_SR_Msk (0x1000UL) /*!< GPIO ALTPADCFGB: PAD5_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGB_PAD5_DS1_Pos (8UL) /*!< GPIO ALTPADCFGB: PAD5_DS1 (Bit 8) */ +#define GPIO_ALTPADCFGB_PAD5_DS1_Msk (0x100UL) /*!< GPIO ALTPADCFGB: PAD5_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGB_PAD4_SR_Pos (4UL) /*!< GPIO ALTPADCFGB: PAD4_SR (Bit 4) */ +#define GPIO_ALTPADCFGB_PAD4_SR_Msk (0x10UL) /*!< GPIO ALTPADCFGB: PAD4_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGB_PAD4_DS1_Pos (0UL) /*!< GPIO ALTPADCFGB: PAD4_DS1 (Bit 0) */ +#define GPIO_ALTPADCFGB_PAD4_DS1_Msk (0x1UL) /*!< GPIO ALTPADCFGB: PAD4_DS1 (Bitfield-Mask: 0x01) */ +/* ====================================================== ALTPADCFGC ======================================================= */ +#define GPIO_ALTPADCFGC_PAD11_SR_Pos (28UL) /*!< GPIO ALTPADCFGC: PAD11_SR (Bit 28) */ +#define GPIO_ALTPADCFGC_PAD11_SR_Msk (0x10000000UL) /*!< GPIO ALTPADCFGC: PAD11_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGC_PAD11_DS1_Pos (24UL) /*!< GPIO ALTPADCFGC: PAD11_DS1 (Bit 24) */ +#define GPIO_ALTPADCFGC_PAD11_DS1_Msk (0x1000000UL) /*!< GPIO ALTPADCFGC: PAD11_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGC_PAD10_SR_Pos (20UL) /*!< GPIO ALTPADCFGC: PAD10_SR (Bit 20) */ +#define GPIO_ALTPADCFGC_PAD10_SR_Msk (0x100000UL) /*!< GPIO ALTPADCFGC: PAD10_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGC_PAD10_DS1_Pos (16UL) /*!< GPIO ALTPADCFGC: PAD10_DS1 (Bit 16) */ +#define GPIO_ALTPADCFGC_PAD10_DS1_Msk (0x10000UL) /*!< GPIO ALTPADCFGC: PAD10_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGC_PAD9_SR_Pos (12UL) /*!< GPIO ALTPADCFGC: PAD9_SR (Bit 12) */ +#define GPIO_ALTPADCFGC_PAD9_SR_Msk (0x1000UL) /*!< GPIO ALTPADCFGC: PAD9_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGC_PAD9_DS1_Pos (8UL) /*!< GPIO ALTPADCFGC: PAD9_DS1 (Bit 8) */ +#define GPIO_ALTPADCFGC_PAD9_DS1_Msk (0x100UL) /*!< GPIO ALTPADCFGC: PAD9_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGC_PAD8_SR_Pos (4UL) /*!< GPIO ALTPADCFGC: PAD8_SR (Bit 4) */ +#define GPIO_ALTPADCFGC_PAD8_SR_Msk (0x10UL) /*!< GPIO ALTPADCFGC: PAD8_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGC_PAD8_DS1_Pos (0UL) /*!< GPIO ALTPADCFGC: PAD8_DS1 (Bit 0) */ +#define GPIO_ALTPADCFGC_PAD8_DS1_Msk (0x1UL) /*!< GPIO ALTPADCFGC: PAD8_DS1 (Bitfield-Mask: 0x01) */ +/* ====================================================== ALTPADCFGD ======================================================= */ +#define GPIO_ALTPADCFGD_PAD15_SR_Pos (28UL) /*!< GPIO ALTPADCFGD: PAD15_SR (Bit 28) */ +#define GPIO_ALTPADCFGD_PAD15_SR_Msk (0x10000000UL) /*!< GPIO ALTPADCFGD: PAD15_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGD_PAD15_DS1_Pos (24UL) /*!< GPIO ALTPADCFGD: PAD15_DS1 (Bit 24) */ +#define GPIO_ALTPADCFGD_PAD15_DS1_Msk (0x1000000UL) /*!< GPIO ALTPADCFGD: PAD15_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGD_PAD14_SR_Pos (20UL) /*!< GPIO ALTPADCFGD: PAD14_SR (Bit 20) */ +#define GPIO_ALTPADCFGD_PAD14_SR_Msk (0x100000UL) /*!< GPIO ALTPADCFGD: PAD14_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGD_PAD14_DS1_Pos (16UL) /*!< GPIO ALTPADCFGD: PAD14_DS1 (Bit 16) */ +#define GPIO_ALTPADCFGD_PAD14_DS1_Msk (0x10000UL) /*!< GPIO ALTPADCFGD: PAD14_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGD_PAD13_SR_Pos (12UL) /*!< GPIO ALTPADCFGD: PAD13_SR (Bit 12) */ +#define GPIO_ALTPADCFGD_PAD13_SR_Msk (0x1000UL) /*!< GPIO ALTPADCFGD: PAD13_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGD_PAD13_DS1_Pos (8UL) /*!< GPIO ALTPADCFGD: PAD13_DS1 (Bit 8) */ +#define GPIO_ALTPADCFGD_PAD13_DS1_Msk (0x100UL) /*!< GPIO ALTPADCFGD: PAD13_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGD_PAD12_SR_Pos (4UL) /*!< GPIO ALTPADCFGD: PAD12_SR (Bit 4) */ +#define GPIO_ALTPADCFGD_PAD12_SR_Msk (0x10UL) /*!< GPIO ALTPADCFGD: PAD12_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGD_PAD12_DS1_Pos (0UL) /*!< GPIO ALTPADCFGD: PAD12_DS1 (Bit 0) */ +#define GPIO_ALTPADCFGD_PAD12_DS1_Msk (0x1UL) /*!< GPIO ALTPADCFGD: PAD12_DS1 (Bitfield-Mask: 0x01) */ +/* ====================================================== ALTPADCFGE ======================================================= */ +#define GPIO_ALTPADCFGE_PAD19_SR_Pos (28UL) /*!< GPIO ALTPADCFGE: PAD19_SR (Bit 28) */ +#define GPIO_ALTPADCFGE_PAD19_SR_Msk (0x10000000UL) /*!< GPIO ALTPADCFGE: PAD19_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGE_PAD19_DS1_Pos (24UL) /*!< GPIO ALTPADCFGE: PAD19_DS1 (Bit 24) */ +#define GPIO_ALTPADCFGE_PAD19_DS1_Msk (0x1000000UL) /*!< GPIO ALTPADCFGE: PAD19_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGE_PAD18_SR_Pos (20UL) /*!< GPIO ALTPADCFGE: PAD18_SR (Bit 20) */ +#define GPIO_ALTPADCFGE_PAD18_SR_Msk (0x100000UL) /*!< GPIO ALTPADCFGE: PAD18_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGE_PAD18_DS1_Pos (16UL) /*!< GPIO ALTPADCFGE: PAD18_DS1 (Bit 16) */ +#define GPIO_ALTPADCFGE_PAD18_DS1_Msk (0x10000UL) /*!< GPIO ALTPADCFGE: PAD18_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGE_PAD17_SR_Pos (12UL) /*!< GPIO ALTPADCFGE: PAD17_SR (Bit 12) */ +#define GPIO_ALTPADCFGE_PAD17_SR_Msk (0x1000UL) /*!< GPIO ALTPADCFGE: PAD17_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGE_PAD17_DS1_Pos (8UL) /*!< GPIO ALTPADCFGE: PAD17_DS1 (Bit 8) */ +#define GPIO_ALTPADCFGE_PAD17_DS1_Msk (0x100UL) /*!< GPIO ALTPADCFGE: PAD17_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGE_PAD16_SR_Pos (4UL) /*!< GPIO ALTPADCFGE: PAD16_SR (Bit 4) */ +#define GPIO_ALTPADCFGE_PAD16_SR_Msk (0x10UL) /*!< GPIO ALTPADCFGE: PAD16_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGE_PAD16_DS1_Pos (0UL) /*!< GPIO ALTPADCFGE: PAD16_DS1 (Bit 0) */ +#define GPIO_ALTPADCFGE_PAD16_DS1_Msk (0x1UL) /*!< GPIO ALTPADCFGE: PAD16_DS1 (Bitfield-Mask: 0x01) */ +/* ====================================================== ALTPADCFGF ======================================================= */ +#define GPIO_ALTPADCFGF_PAD23_SR_Pos (28UL) /*!< GPIO ALTPADCFGF: PAD23_SR (Bit 28) */ +#define GPIO_ALTPADCFGF_PAD23_SR_Msk (0x10000000UL) /*!< GPIO ALTPADCFGF: PAD23_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGF_PAD23_DS1_Pos (24UL) /*!< GPIO ALTPADCFGF: PAD23_DS1 (Bit 24) */ +#define GPIO_ALTPADCFGF_PAD23_DS1_Msk (0x1000000UL) /*!< GPIO ALTPADCFGF: PAD23_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGF_PAD22_SR_Pos (20UL) /*!< GPIO ALTPADCFGF: PAD22_SR (Bit 20) */ +#define GPIO_ALTPADCFGF_PAD22_SR_Msk (0x100000UL) /*!< GPIO ALTPADCFGF: PAD22_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGF_PAD22_DS1_Pos (16UL) /*!< GPIO ALTPADCFGF: PAD22_DS1 (Bit 16) */ +#define GPIO_ALTPADCFGF_PAD22_DS1_Msk (0x10000UL) /*!< GPIO ALTPADCFGF: PAD22_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGF_PAD21_SR_Pos (12UL) /*!< GPIO ALTPADCFGF: PAD21_SR (Bit 12) */ +#define GPIO_ALTPADCFGF_PAD21_SR_Msk (0x1000UL) /*!< GPIO ALTPADCFGF: PAD21_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGF_PAD21_DS1_Pos (8UL) /*!< GPIO ALTPADCFGF: PAD21_DS1 (Bit 8) */ +#define GPIO_ALTPADCFGF_PAD21_DS1_Msk (0x100UL) /*!< GPIO ALTPADCFGF: PAD21_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGF_PAD20_SR_Pos (4UL) /*!< GPIO ALTPADCFGF: PAD20_SR (Bit 4) */ +#define GPIO_ALTPADCFGF_PAD20_SR_Msk (0x10UL) /*!< GPIO ALTPADCFGF: PAD20_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGF_PAD20_DS1_Pos (0UL) /*!< GPIO ALTPADCFGF: PAD20_DS1 (Bit 0) */ +#define GPIO_ALTPADCFGF_PAD20_DS1_Msk (0x1UL) /*!< GPIO ALTPADCFGF: PAD20_DS1 (Bitfield-Mask: 0x01) */ +/* ====================================================== ALTPADCFGG ======================================================= */ +#define GPIO_ALTPADCFGG_PAD27_SR_Pos (28UL) /*!< GPIO ALTPADCFGG: PAD27_SR (Bit 28) */ +#define GPIO_ALTPADCFGG_PAD27_SR_Msk (0x10000000UL) /*!< GPIO ALTPADCFGG: PAD27_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGG_PAD27_DS1_Pos (24UL) /*!< GPIO ALTPADCFGG: PAD27_DS1 (Bit 24) */ +#define GPIO_ALTPADCFGG_PAD27_DS1_Msk (0x1000000UL) /*!< GPIO ALTPADCFGG: PAD27_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGG_PAD26_SR_Pos (20UL) /*!< GPIO ALTPADCFGG: PAD26_SR (Bit 20) */ +#define GPIO_ALTPADCFGG_PAD26_SR_Msk (0x100000UL) /*!< GPIO ALTPADCFGG: PAD26_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGG_PAD26_DS1_Pos (16UL) /*!< GPIO ALTPADCFGG: PAD26_DS1 (Bit 16) */ +#define GPIO_ALTPADCFGG_PAD26_DS1_Msk (0x10000UL) /*!< GPIO ALTPADCFGG: PAD26_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGG_PAD25_SR_Pos (12UL) /*!< GPIO ALTPADCFGG: PAD25_SR (Bit 12) */ +#define GPIO_ALTPADCFGG_PAD25_SR_Msk (0x1000UL) /*!< GPIO ALTPADCFGG: PAD25_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGG_PAD25_DS1_Pos (8UL) /*!< GPIO ALTPADCFGG: PAD25_DS1 (Bit 8) */ +#define GPIO_ALTPADCFGG_PAD25_DS1_Msk (0x100UL) /*!< GPIO ALTPADCFGG: PAD25_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGG_PAD24_SR_Pos (4UL) /*!< GPIO ALTPADCFGG: PAD24_SR (Bit 4) */ +#define GPIO_ALTPADCFGG_PAD24_SR_Msk (0x10UL) /*!< GPIO ALTPADCFGG: PAD24_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGG_PAD24_DS1_Pos (0UL) /*!< GPIO ALTPADCFGG: PAD24_DS1 (Bit 0) */ +#define GPIO_ALTPADCFGG_PAD24_DS1_Msk (0x1UL) /*!< GPIO ALTPADCFGG: PAD24_DS1 (Bitfield-Mask: 0x01) */ +/* ====================================================== ALTPADCFGH ======================================================= */ +#define GPIO_ALTPADCFGH_PAD31_SR_Pos (28UL) /*!< GPIO ALTPADCFGH: PAD31_SR (Bit 28) */ +#define GPIO_ALTPADCFGH_PAD31_SR_Msk (0x10000000UL) /*!< GPIO ALTPADCFGH: PAD31_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGH_PAD31_DS1_Pos (24UL) /*!< GPIO ALTPADCFGH: PAD31_DS1 (Bit 24) */ +#define GPIO_ALTPADCFGH_PAD31_DS1_Msk (0x1000000UL) /*!< GPIO ALTPADCFGH: PAD31_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGH_PAD30_SR_Pos (20UL) /*!< GPIO ALTPADCFGH: PAD30_SR (Bit 20) */ +#define GPIO_ALTPADCFGH_PAD30_SR_Msk (0x100000UL) /*!< GPIO ALTPADCFGH: PAD30_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGH_PAD30_DS1_Pos (16UL) /*!< GPIO ALTPADCFGH: PAD30_DS1 (Bit 16) */ +#define GPIO_ALTPADCFGH_PAD30_DS1_Msk (0x10000UL) /*!< GPIO ALTPADCFGH: PAD30_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGH_PAD29_SR_Pos (12UL) /*!< GPIO ALTPADCFGH: PAD29_SR (Bit 12) */ +#define GPIO_ALTPADCFGH_PAD29_SR_Msk (0x1000UL) /*!< GPIO ALTPADCFGH: PAD29_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGH_PAD29_DS1_Pos (8UL) /*!< GPIO ALTPADCFGH: PAD29_DS1 (Bit 8) */ +#define GPIO_ALTPADCFGH_PAD29_DS1_Msk (0x100UL) /*!< GPIO ALTPADCFGH: PAD29_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGH_PAD28_SR_Pos (4UL) /*!< GPIO ALTPADCFGH: PAD28_SR (Bit 4) */ +#define GPIO_ALTPADCFGH_PAD28_SR_Msk (0x10UL) /*!< GPIO ALTPADCFGH: PAD28_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGH_PAD28_DS1_Pos (0UL) /*!< GPIO ALTPADCFGH: PAD28_DS1 (Bit 0) */ +#define GPIO_ALTPADCFGH_PAD28_DS1_Msk (0x1UL) /*!< GPIO ALTPADCFGH: PAD28_DS1 (Bitfield-Mask: 0x01) */ +/* ====================================================== ALTPADCFGI ======================================================= */ +#define GPIO_ALTPADCFGI_PAD35_SR_Pos (28UL) /*!< GPIO ALTPADCFGI: PAD35_SR (Bit 28) */ +#define GPIO_ALTPADCFGI_PAD35_SR_Msk (0x10000000UL) /*!< GPIO ALTPADCFGI: PAD35_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGI_PAD35_DS1_Pos (24UL) /*!< GPIO ALTPADCFGI: PAD35_DS1 (Bit 24) */ +#define GPIO_ALTPADCFGI_PAD35_DS1_Msk (0x1000000UL) /*!< GPIO ALTPADCFGI: PAD35_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGI_PAD34_SR_Pos (20UL) /*!< GPIO ALTPADCFGI: PAD34_SR (Bit 20) */ +#define GPIO_ALTPADCFGI_PAD34_SR_Msk (0x100000UL) /*!< GPIO ALTPADCFGI: PAD34_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGI_PAD34_DS1_Pos (16UL) /*!< GPIO ALTPADCFGI: PAD34_DS1 (Bit 16) */ +#define GPIO_ALTPADCFGI_PAD34_DS1_Msk (0x10000UL) /*!< GPIO ALTPADCFGI: PAD34_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGI_PAD33_SR_Pos (12UL) /*!< GPIO ALTPADCFGI: PAD33_SR (Bit 12) */ +#define GPIO_ALTPADCFGI_PAD33_SR_Msk (0x1000UL) /*!< GPIO ALTPADCFGI: PAD33_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGI_PAD33_DS1_Pos (8UL) /*!< GPIO ALTPADCFGI: PAD33_DS1 (Bit 8) */ +#define GPIO_ALTPADCFGI_PAD33_DS1_Msk (0x100UL) /*!< GPIO ALTPADCFGI: PAD33_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGI_PAD32_SR_Pos (4UL) /*!< GPIO ALTPADCFGI: PAD32_SR (Bit 4) */ +#define GPIO_ALTPADCFGI_PAD32_SR_Msk (0x10UL) /*!< GPIO ALTPADCFGI: PAD32_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGI_PAD32_DS1_Pos (0UL) /*!< GPIO ALTPADCFGI: PAD32_DS1 (Bit 0) */ +#define GPIO_ALTPADCFGI_PAD32_DS1_Msk (0x1UL) /*!< GPIO ALTPADCFGI: PAD32_DS1 (Bitfield-Mask: 0x01) */ +/* ====================================================== ALTPADCFGJ ======================================================= */ +#define GPIO_ALTPADCFGJ_PAD39_SR_Pos (28UL) /*!< GPIO ALTPADCFGJ: PAD39_SR (Bit 28) */ +#define GPIO_ALTPADCFGJ_PAD39_SR_Msk (0x10000000UL) /*!< GPIO ALTPADCFGJ: PAD39_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGJ_PAD39_DS1_Pos (24UL) /*!< GPIO ALTPADCFGJ: PAD39_DS1 (Bit 24) */ +#define GPIO_ALTPADCFGJ_PAD39_DS1_Msk (0x1000000UL) /*!< GPIO ALTPADCFGJ: PAD39_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGJ_PAD38_SR_Pos (20UL) /*!< GPIO ALTPADCFGJ: PAD38_SR (Bit 20) */ +#define GPIO_ALTPADCFGJ_PAD38_SR_Msk (0x100000UL) /*!< GPIO ALTPADCFGJ: PAD38_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGJ_PAD38_DS1_Pos (16UL) /*!< GPIO ALTPADCFGJ: PAD38_DS1 (Bit 16) */ +#define GPIO_ALTPADCFGJ_PAD38_DS1_Msk (0x10000UL) /*!< GPIO ALTPADCFGJ: PAD38_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGJ_PAD37_SR_Pos (12UL) /*!< GPIO ALTPADCFGJ: PAD37_SR (Bit 12) */ +#define GPIO_ALTPADCFGJ_PAD37_SR_Msk (0x1000UL) /*!< GPIO ALTPADCFGJ: PAD37_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGJ_PAD37_DS1_Pos (8UL) /*!< GPIO ALTPADCFGJ: PAD37_DS1 (Bit 8) */ +#define GPIO_ALTPADCFGJ_PAD37_DS1_Msk (0x100UL) /*!< GPIO ALTPADCFGJ: PAD37_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGJ_PAD36_SR_Pos (4UL) /*!< GPIO ALTPADCFGJ: PAD36_SR (Bit 4) */ +#define GPIO_ALTPADCFGJ_PAD36_SR_Msk (0x10UL) /*!< GPIO ALTPADCFGJ: PAD36_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGJ_PAD36_DS1_Pos (0UL) /*!< GPIO ALTPADCFGJ: PAD36_DS1 (Bit 0) */ +#define GPIO_ALTPADCFGJ_PAD36_DS1_Msk (0x1UL) /*!< GPIO ALTPADCFGJ: PAD36_DS1 (Bitfield-Mask: 0x01) */ +/* ====================================================== ALTPADCFGK ======================================================= */ +#define GPIO_ALTPADCFGK_PAD43_SR_Pos (28UL) /*!< GPIO ALTPADCFGK: PAD43_SR (Bit 28) */ +#define GPIO_ALTPADCFGK_PAD43_SR_Msk (0x10000000UL) /*!< GPIO ALTPADCFGK: PAD43_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGK_PAD43_DS1_Pos (24UL) /*!< GPIO ALTPADCFGK: PAD43_DS1 (Bit 24) */ +#define GPIO_ALTPADCFGK_PAD43_DS1_Msk (0x1000000UL) /*!< GPIO ALTPADCFGK: PAD43_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGK_PAD42_SR_Pos (20UL) /*!< GPIO ALTPADCFGK: PAD42_SR (Bit 20) */ +#define GPIO_ALTPADCFGK_PAD42_SR_Msk (0x100000UL) /*!< GPIO ALTPADCFGK: PAD42_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGK_PAD42_DS1_Pos (16UL) /*!< GPIO ALTPADCFGK: PAD42_DS1 (Bit 16) */ +#define GPIO_ALTPADCFGK_PAD42_DS1_Msk (0x10000UL) /*!< GPIO ALTPADCFGK: PAD42_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGK_PAD41_SR_Pos (12UL) /*!< GPIO ALTPADCFGK: PAD41_SR (Bit 12) */ +#define GPIO_ALTPADCFGK_PAD41_SR_Msk (0x1000UL) /*!< GPIO ALTPADCFGK: PAD41_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGK_PAD41_DS1_Pos (8UL) /*!< GPIO ALTPADCFGK: PAD41_DS1 (Bit 8) */ +#define GPIO_ALTPADCFGK_PAD41_DS1_Msk (0x100UL) /*!< GPIO ALTPADCFGK: PAD41_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGK_PAD40_SR_Pos (4UL) /*!< GPIO ALTPADCFGK: PAD40_SR (Bit 4) */ +#define GPIO_ALTPADCFGK_PAD40_SR_Msk (0x10UL) /*!< GPIO ALTPADCFGK: PAD40_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGK_PAD40_DS1_Pos (0UL) /*!< GPIO ALTPADCFGK: PAD40_DS1 (Bit 0) */ +#define GPIO_ALTPADCFGK_PAD40_DS1_Msk (0x1UL) /*!< GPIO ALTPADCFGK: PAD40_DS1 (Bitfield-Mask: 0x01) */ +/* ====================================================== ALTPADCFGL ======================================================= */ +#define GPIO_ALTPADCFGL_PAD47_SR_Pos (28UL) /*!< GPIO ALTPADCFGL: PAD47_SR (Bit 28) */ +#define GPIO_ALTPADCFGL_PAD47_SR_Msk (0x10000000UL) /*!< GPIO ALTPADCFGL: PAD47_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGL_PAD47_DS1_Pos (24UL) /*!< GPIO ALTPADCFGL: PAD47_DS1 (Bit 24) */ +#define GPIO_ALTPADCFGL_PAD47_DS1_Msk (0x1000000UL) /*!< GPIO ALTPADCFGL: PAD47_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGL_PAD46_SR_Pos (20UL) /*!< GPIO ALTPADCFGL: PAD46_SR (Bit 20) */ +#define GPIO_ALTPADCFGL_PAD46_SR_Msk (0x100000UL) /*!< GPIO ALTPADCFGL: PAD46_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGL_PAD46_DS1_Pos (16UL) /*!< GPIO ALTPADCFGL: PAD46_DS1 (Bit 16) */ +#define GPIO_ALTPADCFGL_PAD46_DS1_Msk (0x10000UL) /*!< GPIO ALTPADCFGL: PAD46_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGL_PAD45_SR_Pos (12UL) /*!< GPIO ALTPADCFGL: PAD45_SR (Bit 12) */ +#define GPIO_ALTPADCFGL_PAD45_SR_Msk (0x1000UL) /*!< GPIO ALTPADCFGL: PAD45_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGL_PAD45_DS1_Pos (8UL) /*!< GPIO ALTPADCFGL: PAD45_DS1 (Bit 8) */ +#define GPIO_ALTPADCFGL_PAD45_DS1_Msk (0x100UL) /*!< GPIO ALTPADCFGL: PAD45_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGL_PAD44_SR_Pos (4UL) /*!< GPIO ALTPADCFGL: PAD44_SR (Bit 4) */ +#define GPIO_ALTPADCFGL_PAD44_SR_Msk (0x10UL) /*!< GPIO ALTPADCFGL: PAD44_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGL_PAD44_DS1_Pos (0UL) /*!< GPIO ALTPADCFGL: PAD44_DS1 (Bit 0) */ +#define GPIO_ALTPADCFGL_PAD44_DS1_Msk (0x1UL) /*!< GPIO ALTPADCFGL: PAD44_DS1 (Bitfield-Mask: 0x01) */ +/* ====================================================== ALTPADCFGM ======================================================= */ +#define GPIO_ALTPADCFGM_PAD49_SR_Pos (12UL) /*!< GPIO ALTPADCFGM: PAD49_SR (Bit 12) */ +#define GPIO_ALTPADCFGM_PAD49_SR_Msk (0x1000UL) /*!< GPIO ALTPADCFGM: PAD49_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGM_PAD49_DS1_Pos (8UL) /*!< GPIO ALTPADCFGM: PAD49_DS1 (Bit 8) */ +#define GPIO_ALTPADCFGM_PAD49_DS1_Msk (0x100UL) /*!< GPIO ALTPADCFGM: PAD49_DS1 (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGM_PAD48_SR_Pos (4UL) /*!< GPIO ALTPADCFGM: PAD48_SR (Bit 4) */ +#define GPIO_ALTPADCFGM_PAD48_SR_Msk (0x10UL) /*!< GPIO ALTPADCFGM: PAD48_SR (Bitfield-Mask: 0x01) */ +#define GPIO_ALTPADCFGM_PAD48_DS1_Pos (0UL) /*!< GPIO ALTPADCFGM: PAD48_DS1 (Bit 0) */ +#define GPIO_ALTPADCFGM_PAD48_DS1_Msk (0x1UL) /*!< GPIO ALTPADCFGM: PAD48_DS1 (Bitfield-Mask: 0x01) */ +/* ========================================================= SCDET ========================================================= */ +#define GPIO_SCDET_SCDET_Pos (0UL) /*!< GPIO SCDET: SCDET (Bit 0) */ +#define GPIO_SCDET_SCDET_Msk (0x3fUL) /*!< GPIO SCDET: SCDET (Bitfield-Mask: 0x3f) */ +/* ======================================================== CTENCFG ======================================================== */ +#define GPIO_CTENCFG_EN31_Pos (31UL) /*!< GPIO CTENCFG: EN31 (Bit 31) */ +#define GPIO_CTENCFG_EN31_Msk (0x80000000UL) /*!< GPIO CTENCFG: EN31 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN30_Pos (30UL) /*!< GPIO CTENCFG: EN30 (Bit 30) */ +#define GPIO_CTENCFG_EN30_Msk (0x40000000UL) /*!< GPIO CTENCFG: EN30 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN29_Pos (29UL) /*!< GPIO CTENCFG: EN29 (Bit 29) */ +#define GPIO_CTENCFG_EN29_Msk (0x20000000UL) /*!< GPIO CTENCFG: EN29 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN28_Pos (28UL) /*!< GPIO CTENCFG: EN28 (Bit 28) */ +#define GPIO_CTENCFG_EN28_Msk (0x10000000UL) /*!< GPIO CTENCFG: EN28 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN27_Pos (27UL) /*!< GPIO CTENCFG: EN27 (Bit 27) */ +#define GPIO_CTENCFG_EN27_Msk (0x8000000UL) /*!< GPIO CTENCFG: EN27 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN26_Pos (26UL) /*!< GPIO CTENCFG: EN26 (Bit 26) */ +#define GPIO_CTENCFG_EN26_Msk (0x4000000UL) /*!< GPIO CTENCFG: EN26 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN25_Pos (25UL) /*!< GPIO CTENCFG: EN25 (Bit 25) */ +#define GPIO_CTENCFG_EN25_Msk (0x2000000UL) /*!< GPIO CTENCFG: EN25 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN24_Pos (24UL) /*!< GPIO CTENCFG: EN24 (Bit 24) */ +#define GPIO_CTENCFG_EN24_Msk (0x1000000UL) /*!< GPIO CTENCFG: EN24 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN23_Pos (23UL) /*!< GPIO CTENCFG: EN23 (Bit 23) */ +#define GPIO_CTENCFG_EN23_Msk (0x800000UL) /*!< GPIO CTENCFG: EN23 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN22_Pos (22UL) /*!< GPIO CTENCFG: EN22 (Bit 22) */ +#define GPIO_CTENCFG_EN22_Msk (0x400000UL) /*!< GPIO CTENCFG: EN22 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN21_Pos (21UL) /*!< GPIO CTENCFG: EN21 (Bit 21) */ +#define GPIO_CTENCFG_EN21_Msk (0x200000UL) /*!< GPIO CTENCFG: EN21 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN20_Pos (20UL) /*!< GPIO CTENCFG: EN20 (Bit 20) */ +#define GPIO_CTENCFG_EN20_Msk (0x100000UL) /*!< GPIO CTENCFG: EN20 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN19_Pos (19UL) /*!< GPIO CTENCFG: EN19 (Bit 19) */ +#define GPIO_CTENCFG_EN19_Msk (0x80000UL) /*!< GPIO CTENCFG: EN19 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN18_Pos (18UL) /*!< GPIO CTENCFG: EN18 (Bit 18) */ +#define GPIO_CTENCFG_EN18_Msk (0x40000UL) /*!< GPIO CTENCFG: EN18 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN17_Pos (17UL) /*!< GPIO CTENCFG: EN17 (Bit 17) */ +#define GPIO_CTENCFG_EN17_Msk (0x20000UL) /*!< GPIO CTENCFG: EN17 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN16_Pos (16UL) /*!< GPIO CTENCFG: EN16 (Bit 16) */ +#define GPIO_CTENCFG_EN16_Msk (0x10000UL) /*!< GPIO CTENCFG: EN16 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN15_Pos (15UL) /*!< GPIO CTENCFG: EN15 (Bit 15) */ +#define GPIO_CTENCFG_EN15_Msk (0x8000UL) /*!< GPIO CTENCFG: EN15 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN14_Pos (14UL) /*!< GPIO CTENCFG: EN14 (Bit 14) */ +#define GPIO_CTENCFG_EN14_Msk (0x4000UL) /*!< GPIO CTENCFG: EN14 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN13_Pos (13UL) /*!< GPIO CTENCFG: EN13 (Bit 13) */ +#define GPIO_CTENCFG_EN13_Msk (0x2000UL) /*!< GPIO CTENCFG: EN13 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN12_Pos (12UL) /*!< GPIO CTENCFG: EN12 (Bit 12) */ +#define GPIO_CTENCFG_EN12_Msk (0x1000UL) /*!< GPIO CTENCFG: EN12 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN11_Pos (11UL) /*!< GPIO CTENCFG: EN11 (Bit 11) */ +#define GPIO_CTENCFG_EN11_Msk (0x800UL) /*!< GPIO CTENCFG: EN11 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN10_Pos (10UL) /*!< GPIO CTENCFG: EN10 (Bit 10) */ +#define GPIO_CTENCFG_EN10_Msk (0x400UL) /*!< GPIO CTENCFG: EN10 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN9_Pos (9UL) /*!< GPIO CTENCFG: EN9 (Bit 9) */ +#define GPIO_CTENCFG_EN9_Msk (0x200UL) /*!< GPIO CTENCFG: EN9 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN8_Pos (8UL) /*!< GPIO CTENCFG: EN8 (Bit 8) */ +#define GPIO_CTENCFG_EN8_Msk (0x100UL) /*!< GPIO CTENCFG: EN8 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN7_Pos (7UL) /*!< GPIO CTENCFG: EN7 (Bit 7) */ +#define GPIO_CTENCFG_EN7_Msk (0x80UL) /*!< GPIO CTENCFG: EN7 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN6_Pos (6UL) /*!< GPIO CTENCFG: EN6 (Bit 6) */ +#define GPIO_CTENCFG_EN6_Msk (0x40UL) /*!< GPIO CTENCFG: EN6 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN5_Pos (5UL) /*!< GPIO CTENCFG: EN5 (Bit 5) */ +#define GPIO_CTENCFG_EN5_Msk (0x20UL) /*!< GPIO CTENCFG: EN5 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN4_Pos (4UL) /*!< GPIO CTENCFG: EN4 (Bit 4) */ +#define GPIO_CTENCFG_EN4_Msk (0x10UL) /*!< GPIO CTENCFG: EN4 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN3_Pos (3UL) /*!< GPIO CTENCFG: EN3 (Bit 3) */ +#define GPIO_CTENCFG_EN3_Msk (0x8UL) /*!< GPIO CTENCFG: EN3 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN2_Pos (2UL) /*!< GPIO CTENCFG: EN2 (Bit 2) */ +#define GPIO_CTENCFG_EN2_Msk (0x4UL) /*!< GPIO CTENCFG: EN2 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN1_Pos (1UL) /*!< GPIO CTENCFG: EN1 (Bit 1) */ +#define GPIO_CTENCFG_EN1_Msk (0x2UL) /*!< GPIO CTENCFG: EN1 (Bitfield-Mask: 0x01) */ +#define GPIO_CTENCFG_EN0_Pos (0UL) /*!< GPIO CTENCFG: EN0 (Bit 0) */ +#define GPIO_CTENCFG_EN0_Msk (0x1UL) /*!< GPIO CTENCFG: EN0 (Bitfield-Mask: 0x01) */ +/* ======================================================== INT0EN ========================================================= */ +#define GPIO_INT0EN_GPIO31_Pos (31UL) /*!< GPIO INT0EN: GPIO31 (Bit 31) */ +#define GPIO_INT0EN_GPIO31_Msk (0x80000000UL) /*!< GPIO INT0EN: GPIO31 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO30_Pos (30UL) /*!< GPIO INT0EN: GPIO30 (Bit 30) */ +#define GPIO_INT0EN_GPIO30_Msk (0x40000000UL) /*!< GPIO INT0EN: GPIO30 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO29_Pos (29UL) /*!< GPIO INT0EN: GPIO29 (Bit 29) */ +#define GPIO_INT0EN_GPIO29_Msk (0x20000000UL) /*!< GPIO INT0EN: GPIO29 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO28_Pos (28UL) /*!< GPIO INT0EN: GPIO28 (Bit 28) */ +#define GPIO_INT0EN_GPIO28_Msk (0x10000000UL) /*!< GPIO INT0EN: GPIO28 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO27_Pos (27UL) /*!< GPIO INT0EN: GPIO27 (Bit 27) */ +#define GPIO_INT0EN_GPIO27_Msk (0x8000000UL) /*!< GPIO INT0EN: GPIO27 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO26_Pos (26UL) /*!< GPIO INT0EN: GPIO26 (Bit 26) */ +#define GPIO_INT0EN_GPIO26_Msk (0x4000000UL) /*!< GPIO INT0EN: GPIO26 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO25_Pos (25UL) /*!< GPIO INT0EN: GPIO25 (Bit 25) */ +#define GPIO_INT0EN_GPIO25_Msk (0x2000000UL) /*!< GPIO INT0EN: GPIO25 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO24_Pos (24UL) /*!< GPIO INT0EN: GPIO24 (Bit 24) */ +#define GPIO_INT0EN_GPIO24_Msk (0x1000000UL) /*!< GPIO INT0EN: GPIO24 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO23_Pos (23UL) /*!< GPIO INT0EN: GPIO23 (Bit 23) */ +#define GPIO_INT0EN_GPIO23_Msk (0x800000UL) /*!< GPIO INT0EN: GPIO23 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO22_Pos (22UL) /*!< GPIO INT0EN: GPIO22 (Bit 22) */ +#define GPIO_INT0EN_GPIO22_Msk (0x400000UL) /*!< GPIO INT0EN: GPIO22 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO21_Pos (21UL) /*!< GPIO INT0EN: GPIO21 (Bit 21) */ +#define GPIO_INT0EN_GPIO21_Msk (0x200000UL) /*!< GPIO INT0EN: GPIO21 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO20_Pos (20UL) /*!< GPIO INT0EN: GPIO20 (Bit 20) */ +#define GPIO_INT0EN_GPIO20_Msk (0x100000UL) /*!< GPIO INT0EN: GPIO20 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO19_Pos (19UL) /*!< GPIO INT0EN: GPIO19 (Bit 19) */ +#define GPIO_INT0EN_GPIO19_Msk (0x80000UL) /*!< GPIO INT0EN: GPIO19 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO18_Pos (18UL) /*!< GPIO INT0EN: GPIO18 (Bit 18) */ +#define GPIO_INT0EN_GPIO18_Msk (0x40000UL) /*!< GPIO INT0EN: GPIO18 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO17_Pos (17UL) /*!< GPIO INT0EN: GPIO17 (Bit 17) */ +#define GPIO_INT0EN_GPIO17_Msk (0x20000UL) /*!< GPIO INT0EN: GPIO17 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO16_Pos (16UL) /*!< GPIO INT0EN: GPIO16 (Bit 16) */ +#define GPIO_INT0EN_GPIO16_Msk (0x10000UL) /*!< GPIO INT0EN: GPIO16 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO15_Pos (15UL) /*!< GPIO INT0EN: GPIO15 (Bit 15) */ +#define GPIO_INT0EN_GPIO15_Msk (0x8000UL) /*!< GPIO INT0EN: GPIO15 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO14_Pos (14UL) /*!< GPIO INT0EN: GPIO14 (Bit 14) */ +#define GPIO_INT0EN_GPIO14_Msk (0x4000UL) /*!< GPIO INT0EN: GPIO14 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO13_Pos (13UL) /*!< GPIO INT0EN: GPIO13 (Bit 13) */ +#define GPIO_INT0EN_GPIO13_Msk (0x2000UL) /*!< GPIO INT0EN: GPIO13 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO12_Pos (12UL) /*!< GPIO INT0EN: GPIO12 (Bit 12) */ +#define GPIO_INT0EN_GPIO12_Msk (0x1000UL) /*!< GPIO INT0EN: GPIO12 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO11_Pos (11UL) /*!< GPIO INT0EN: GPIO11 (Bit 11) */ +#define GPIO_INT0EN_GPIO11_Msk (0x800UL) /*!< GPIO INT0EN: GPIO11 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO10_Pos (10UL) /*!< GPIO INT0EN: GPIO10 (Bit 10) */ +#define GPIO_INT0EN_GPIO10_Msk (0x400UL) /*!< GPIO INT0EN: GPIO10 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO9_Pos (9UL) /*!< GPIO INT0EN: GPIO9 (Bit 9) */ +#define GPIO_INT0EN_GPIO9_Msk (0x200UL) /*!< GPIO INT0EN: GPIO9 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO8_Pos (8UL) /*!< GPIO INT0EN: GPIO8 (Bit 8) */ +#define GPIO_INT0EN_GPIO8_Msk (0x100UL) /*!< GPIO INT0EN: GPIO8 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO7_Pos (7UL) /*!< GPIO INT0EN: GPIO7 (Bit 7) */ +#define GPIO_INT0EN_GPIO7_Msk (0x80UL) /*!< GPIO INT0EN: GPIO7 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO6_Pos (6UL) /*!< GPIO INT0EN: GPIO6 (Bit 6) */ +#define GPIO_INT0EN_GPIO6_Msk (0x40UL) /*!< GPIO INT0EN: GPIO6 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO5_Pos (5UL) /*!< GPIO INT0EN: GPIO5 (Bit 5) */ +#define GPIO_INT0EN_GPIO5_Msk (0x20UL) /*!< GPIO INT0EN: GPIO5 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO4_Pos (4UL) /*!< GPIO INT0EN: GPIO4 (Bit 4) */ +#define GPIO_INT0EN_GPIO4_Msk (0x10UL) /*!< GPIO INT0EN: GPIO4 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO3_Pos (3UL) /*!< GPIO INT0EN: GPIO3 (Bit 3) */ +#define GPIO_INT0EN_GPIO3_Msk (0x8UL) /*!< GPIO INT0EN: GPIO3 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO2_Pos (2UL) /*!< GPIO INT0EN: GPIO2 (Bit 2) */ +#define GPIO_INT0EN_GPIO2_Msk (0x4UL) /*!< GPIO INT0EN: GPIO2 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO1_Pos (1UL) /*!< GPIO INT0EN: GPIO1 (Bit 1) */ +#define GPIO_INT0EN_GPIO1_Msk (0x2UL) /*!< GPIO INT0EN: GPIO1 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0EN_GPIO0_Pos (0UL) /*!< GPIO INT0EN: GPIO0 (Bit 0) */ +#define GPIO_INT0EN_GPIO0_Msk (0x1UL) /*!< GPIO INT0EN: GPIO0 (Bitfield-Mask: 0x01) */ +/* ======================================================= INT0STAT ======================================================== */ +#define GPIO_INT0STAT_GPIO31_Pos (31UL) /*!< GPIO INT0STAT: GPIO31 (Bit 31) */ +#define GPIO_INT0STAT_GPIO31_Msk (0x80000000UL) /*!< GPIO INT0STAT: GPIO31 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO30_Pos (30UL) /*!< GPIO INT0STAT: GPIO30 (Bit 30) */ +#define GPIO_INT0STAT_GPIO30_Msk (0x40000000UL) /*!< GPIO INT0STAT: GPIO30 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO29_Pos (29UL) /*!< GPIO INT0STAT: GPIO29 (Bit 29) */ +#define GPIO_INT0STAT_GPIO29_Msk (0x20000000UL) /*!< GPIO INT0STAT: GPIO29 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO28_Pos (28UL) /*!< GPIO INT0STAT: GPIO28 (Bit 28) */ +#define GPIO_INT0STAT_GPIO28_Msk (0x10000000UL) /*!< GPIO INT0STAT: GPIO28 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO27_Pos (27UL) /*!< GPIO INT0STAT: GPIO27 (Bit 27) */ +#define GPIO_INT0STAT_GPIO27_Msk (0x8000000UL) /*!< GPIO INT0STAT: GPIO27 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO26_Pos (26UL) /*!< GPIO INT0STAT: GPIO26 (Bit 26) */ +#define GPIO_INT0STAT_GPIO26_Msk (0x4000000UL) /*!< GPIO INT0STAT: GPIO26 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO25_Pos (25UL) /*!< GPIO INT0STAT: GPIO25 (Bit 25) */ +#define GPIO_INT0STAT_GPIO25_Msk (0x2000000UL) /*!< GPIO INT0STAT: GPIO25 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO24_Pos (24UL) /*!< GPIO INT0STAT: GPIO24 (Bit 24) */ +#define GPIO_INT0STAT_GPIO24_Msk (0x1000000UL) /*!< GPIO INT0STAT: GPIO24 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO23_Pos (23UL) /*!< GPIO INT0STAT: GPIO23 (Bit 23) */ +#define GPIO_INT0STAT_GPIO23_Msk (0x800000UL) /*!< GPIO INT0STAT: GPIO23 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO22_Pos (22UL) /*!< GPIO INT0STAT: GPIO22 (Bit 22) */ +#define GPIO_INT0STAT_GPIO22_Msk (0x400000UL) /*!< GPIO INT0STAT: GPIO22 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO21_Pos (21UL) /*!< GPIO INT0STAT: GPIO21 (Bit 21) */ +#define GPIO_INT0STAT_GPIO21_Msk (0x200000UL) /*!< GPIO INT0STAT: GPIO21 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO20_Pos (20UL) /*!< GPIO INT0STAT: GPIO20 (Bit 20) */ +#define GPIO_INT0STAT_GPIO20_Msk (0x100000UL) /*!< GPIO INT0STAT: GPIO20 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO19_Pos (19UL) /*!< GPIO INT0STAT: GPIO19 (Bit 19) */ +#define GPIO_INT0STAT_GPIO19_Msk (0x80000UL) /*!< GPIO INT0STAT: GPIO19 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO18_Pos (18UL) /*!< GPIO INT0STAT: GPIO18 (Bit 18) */ +#define GPIO_INT0STAT_GPIO18_Msk (0x40000UL) /*!< GPIO INT0STAT: GPIO18 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO17_Pos (17UL) /*!< GPIO INT0STAT: GPIO17 (Bit 17) */ +#define GPIO_INT0STAT_GPIO17_Msk (0x20000UL) /*!< GPIO INT0STAT: GPIO17 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO16_Pos (16UL) /*!< GPIO INT0STAT: GPIO16 (Bit 16) */ +#define GPIO_INT0STAT_GPIO16_Msk (0x10000UL) /*!< GPIO INT0STAT: GPIO16 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO15_Pos (15UL) /*!< GPIO INT0STAT: GPIO15 (Bit 15) */ +#define GPIO_INT0STAT_GPIO15_Msk (0x8000UL) /*!< GPIO INT0STAT: GPIO15 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO14_Pos (14UL) /*!< GPIO INT0STAT: GPIO14 (Bit 14) */ +#define GPIO_INT0STAT_GPIO14_Msk (0x4000UL) /*!< GPIO INT0STAT: GPIO14 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO13_Pos (13UL) /*!< GPIO INT0STAT: GPIO13 (Bit 13) */ +#define GPIO_INT0STAT_GPIO13_Msk (0x2000UL) /*!< GPIO INT0STAT: GPIO13 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO12_Pos (12UL) /*!< GPIO INT0STAT: GPIO12 (Bit 12) */ +#define GPIO_INT0STAT_GPIO12_Msk (0x1000UL) /*!< GPIO INT0STAT: GPIO12 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO11_Pos (11UL) /*!< GPIO INT0STAT: GPIO11 (Bit 11) */ +#define GPIO_INT0STAT_GPIO11_Msk (0x800UL) /*!< GPIO INT0STAT: GPIO11 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO10_Pos (10UL) /*!< GPIO INT0STAT: GPIO10 (Bit 10) */ +#define GPIO_INT0STAT_GPIO10_Msk (0x400UL) /*!< GPIO INT0STAT: GPIO10 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO9_Pos (9UL) /*!< GPIO INT0STAT: GPIO9 (Bit 9) */ +#define GPIO_INT0STAT_GPIO9_Msk (0x200UL) /*!< GPIO INT0STAT: GPIO9 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO8_Pos (8UL) /*!< GPIO INT0STAT: GPIO8 (Bit 8) */ +#define GPIO_INT0STAT_GPIO8_Msk (0x100UL) /*!< GPIO INT0STAT: GPIO8 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO7_Pos (7UL) /*!< GPIO INT0STAT: GPIO7 (Bit 7) */ +#define GPIO_INT0STAT_GPIO7_Msk (0x80UL) /*!< GPIO INT0STAT: GPIO7 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO6_Pos (6UL) /*!< GPIO INT0STAT: GPIO6 (Bit 6) */ +#define GPIO_INT0STAT_GPIO6_Msk (0x40UL) /*!< GPIO INT0STAT: GPIO6 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO5_Pos (5UL) /*!< GPIO INT0STAT: GPIO5 (Bit 5) */ +#define GPIO_INT0STAT_GPIO5_Msk (0x20UL) /*!< GPIO INT0STAT: GPIO5 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO4_Pos (4UL) /*!< GPIO INT0STAT: GPIO4 (Bit 4) */ +#define GPIO_INT0STAT_GPIO4_Msk (0x10UL) /*!< GPIO INT0STAT: GPIO4 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO3_Pos (3UL) /*!< GPIO INT0STAT: GPIO3 (Bit 3) */ +#define GPIO_INT0STAT_GPIO3_Msk (0x8UL) /*!< GPIO INT0STAT: GPIO3 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO2_Pos (2UL) /*!< GPIO INT0STAT: GPIO2 (Bit 2) */ +#define GPIO_INT0STAT_GPIO2_Msk (0x4UL) /*!< GPIO INT0STAT: GPIO2 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO1_Pos (1UL) /*!< GPIO INT0STAT: GPIO1 (Bit 1) */ +#define GPIO_INT0STAT_GPIO1_Msk (0x2UL) /*!< GPIO INT0STAT: GPIO1 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0STAT_GPIO0_Pos (0UL) /*!< GPIO INT0STAT: GPIO0 (Bit 0) */ +#define GPIO_INT0STAT_GPIO0_Msk (0x1UL) /*!< GPIO INT0STAT: GPIO0 (Bitfield-Mask: 0x01) */ +/* ======================================================== INT0CLR ======================================================== */ +#define GPIO_INT0CLR_GPIO31_Pos (31UL) /*!< GPIO INT0CLR: GPIO31 (Bit 31) */ +#define GPIO_INT0CLR_GPIO31_Msk (0x80000000UL) /*!< GPIO INT0CLR: GPIO31 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO30_Pos (30UL) /*!< GPIO INT0CLR: GPIO30 (Bit 30) */ +#define GPIO_INT0CLR_GPIO30_Msk (0x40000000UL) /*!< GPIO INT0CLR: GPIO30 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO29_Pos (29UL) /*!< GPIO INT0CLR: GPIO29 (Bit 29) */ +#define GPIO_INT0CLR_GPIO29_Msk (0x20000000UL) /*!< GPIO INT0CLR: GPIO29 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO28_Pos (28UL) /*!< GPIO INT0CLR: GPIO28 (Bit 28) */ +#define GPIO_INT0CLR_GPIO28_Msk (0x10000000UL) /*!< GPIO INT0CLR: GPIO28 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO27_Pos (27UL) /*!< GPIO INT0CLR: GPIO27 (Bit 27) */ +#define GPIO_INT0CLR_GPIO27_Msk (0x8000000UL) /*!< GPIO INT0CLR: GPIO27 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO26_Pos (26UL) /*!< GPIO INT0CLR: GPIO26 (Bit 26) */ +#define GPIO_INT0CLR_GPIO26_Msk (0x4000000UL) /*!< GPIO INT0CLR: GPIO26 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO25_Pos (25UL) /*!< GPIO INT0CLR: GPIO25 (Bit 25) */ +#define GPIO_INT0CLR_GPIO25_Msk (0x2000000UL) /*!< GPIO INT0CLR: GPIO25 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO24_Pos (24UL) /*!< GPIO INT0CLR: GPIO24 (Bit 24) */ +#define GPIO_INT0CLR_GPIO24_Msk (0x1000000UL) /*!< GPIO INT0CLR: GPIO24 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO23_Pos (23UL) /*!< GPIO INT0CLR: GPIO23 (Bit 23) */ +#define GPIO_INT0CLR_GPIO23_Msk (0x800000UL) /*!< GPIO INT0CLR: GPIO23 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO22_Pos (22UL) /*!< GPIO INT0CLR: GPIO22 (Bit 22) */ +#define GPIO_INT0CLR_GPIO22_Msk (0x400000UL) /*!< GPIO INT0CLR: GPIO22 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO21_Pos (21UL) /*!< GPIO INT0CLR: GPIO21 (Bit 21) */ +#define GPIO_INT0CLR_GPIO21_Msk (0x200000UL) /*!< GPIO INT0CLR: GPIO21 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO20_Pos (20UL) /*!< GPIO INT0CLR: GPIO20 (Bit 20) */ +#define GPIO_INT0CLR_GPIO20_Msk (0x100000UL) /*!< GPIO INT0CLR: GPIO20 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO19_Pos (19UL) /*!< GPIO INT0CLR: GPIO19 (Bit 19) */ +#define GPIO_INT0CLR_GPIO19_Msk (0x80000UL) /*!< GPIO INT0CLR: GPIO19 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO18_Pos (18UL) /*!< GPIO INT0CLR: GPIO18 (Bit 18) */ +#define GPIO_INT0CLR_GPIO18_Msk (0x40000UL) /*!< GPIO INT0CLR: GPIO18 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO17_Pos (17UL) /*!< GPIO INT0CLR: GPIO17 (Bit 17) */ +#define GPIO_INT0CLR_GPIO17_Msk (0x20000UL) /*!< GPIO INT0CLR: GPIO17 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO16_Pos (16UL) /*!< GPIO INT0CLR: GPIO16 (Bit 16) */ +#define GPIO_INT0CLR_GPIO16_Msk (0x10000UL) /*!< GPIO INT0CLR: GPIO16 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO15_Pos (15UL) /*!< GPIO INT0CLR: GPIO15 (Bit 15) */ +#define GPIO_INT0CLR_GPIO15_Msk (0x8000UL) /*!< GPIO INT0CLR: GPIO15 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO14_Pos (14UL) /*!< GPIO INT0CLR: GPIO14 (Bit 14) */ +#define GPIO_INT0CLR_GPIO14_Msk (0x4000UL) /*!< GPIO INT0CLR: GPIO14 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO13_Pos (13UL) /*!< GPIO INT0CLR: GPIO13 (Bit 13) */ +#define GPIO_INT0CLR_GPIO13_Msk (0x2000UL) /*!< GPIO INT0CLR: GPIO13 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO12_Pos (12UL) /*!< GPIO INT0CLR: GPIO12 (Bit 12) */ +#define GPIO_INT0CLR_GPIO12_Msk (0x1000UL) /*!< GPIO INT0CLR: GPIO12 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO11_Pos (11UL) /*!< GPIO INT0CLR: GPIO11 (Bit 11) */ +#define GPIO_INT0CLR_GPIO11_Msk (0x800UL) /*!< GPIO INT0CLR: GPIO11 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO10_Pos (10UL) /*!< GPIO INT0CLR: GPIO10 (Bit 10) */ +#define GPIO_INT0CLR_GPIO10_Msk (0x400UL) /*!< GPIO INT0CLR: GPIO10 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO9_Pos (9UL) /*!< GPIO INT0CLR: GPIO9 (Bit 9) */ +#define GPIO_INT0CLR_GPIO9_Msk (0x200UL) /*!< GPIO INT0CLR: GPIO9 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO8_Pos (8UL) /*!< GPIO INT0CLR: GPIO8 (Bit 8) */ +#define GPIO_INT0CLR_GPIO8_Msk (0x100UL) /*!< GPIO INT0CLR: GPIO8 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO7_Pos (7UL) /*!< GPIO INT0CLR: GPIO7 (Bit 7) */ +#define GPIO_INT0CLR_GPIO7_Msk (0x80UL) /*!< GPIO INT0CLR: GPIO7 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO6_Pos (6UL) /*!< GPIO INT0CLR: GPIO6 (Bit 6) */ +#define GPIO_INT0CLR_GPIO6_Msk (0x40UL) /*!< GPIO INT0CLR: GPIO6 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO5_Pos (5UL) /*!< GPIO INT0CLR: GPIO5 (Bit 5) */ +#define GPIO_INT0CLR_GPIO5_Msk (0x20UL) /*!< GPIO INT0CLR: GPIO5 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO4_Pos (4UL) /*!< GPIO INT0CLR: GPIO4 (Bit 4) */ +#define GPIO_INT0CLR_GPIO4_Msk (0x10UL) /*!< GPIO INT0CLR: GPIO4 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO3_Pos (3UL) /*!< GPIO INT0CLR: GPIO3 (Bit 3) */ +#define GPIO_INT0CLR_GPIO3_Msk (0x8UL) /*!< GPIO INT0CLR: GPIO3 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO2_Pos (2UL) /*!< GPIO INT0CLR: GPIO2 (Bit 2) */ +#define GPIO_INT0CLR_GPIO2_Msk (0x4UL) /*!< GPIO INT0CLR: GPIO2 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO1_Pos (1UL) /*!< GPIO INT0CLR: GPIO1 (Bit 1) */ +#define GPIO_INT0CLR_GPIO1_Msk (0x2UL) /*!< GPIO INT0CLR: GPIO1 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0CLR_GPIO0_Pos (0UL) /*!< GPIO INT0CLR: GPIO0 (Bit 0) */ +#define GPIO_INT0CLR_GPIO0_Msk (0x1UL) /*!< GPIO INT0CLR: GPIO0 (Bitfield-Mask: 0x01) */ +/* ======================================================== INT0SET ======================================================== */ +#define GPIO_INT0SET_GPIO31_Pos (31UL) /*!< GPIO INT0SET: GPIO31 (Bit 31) */ +#define GPIO_INT0SET_GPIO31_Msk (0x80000000UL) /*!< GPIO INT0SET: GPIO31 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO30_Pos (30UL) /*!< GPIO INT0SET: GPIO30 (Bit 30) */ +#define GPIO_INT0SET_GPIO30_Msk (0x40000000UL) /*!< GPIO INT0SET: GPIO30 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO29_Pos (29UL) /*!< GPIO INT0SET: GPIO29 (Bit 29) */ +#define GPIO_INT0SET_GPIO29_Msk (0x20000000UL) /*!< GPIO INT0SET: GPIO29 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO28_Pos (28UL) /*!< GPIO INT0SET: GPIO28 (Bit 28) */ +#define GPIO_INT0SET_GPIO28_Msk (0x10000000UL) /*!< GPIO INT0SET: GPIO28 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO27_Pos (27UL) /*!< GPIO INT0SET: GPIO27 (Bit 27) */ +#define GPIO_INT0SET_GPIO27_Msk (0x8000000UL) /*!< GPIO INT0SET: GPIO27 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO26_Pos (26UL) /*!< GPIO INT0SET: GPIO26 (Bit 26) */ +#define GPIO_INT0SET_GPIO26_Msk (0x4000000UL) /*!< GPIO INT0SET: GPIO26 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO25_Pos (25UL) /*!< GPIO INT0SET: GPIO25 (Bit 25) */ +#define GPIO_INT0SET_GPIO25_Msk (0x2000000UL) /*!< GPIO INT0SET: GPIO25 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO24_Pos (24UL) /*!< GPIO INT0SET: GPIO24 (Bit 24) */ +#define GPIO_INT0SET_GPIO24_Msk (0x1000000UL) /*!< GPIO INT0SET: GPIO24 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO23_Pos (23UL) /*!< GPIO INT0SET: GPIO23 (Bit 23) */ +#define GPIO_INT0SET_GPIO23_Msk (0x800000UL) /*!< GPIO INT0SET: GPIO23 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO22_Pos (22UL) /*!< GPIO INT0SET: GPIO22 (Bit 22) */ +#define GPIO_INT0SET_GPIO22_Msk (0x400000UL) /*!< GPIO INT0SET: GPIO22 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO21_Pos (21UL) /*!< GPIO INT0SET: GPIO21 (Bit 21) */ +#define GPIO_INT0SET_GPIO21_Msk (0x200000UL) /*!< GPIO INT0SET: GPIO21 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO20_Pos (20UL) /*!< GPIO INT0SET: GPIO20 (Bit 20) */ +#define GPIO_INT0SET_GPIO20_Msk (0x100000UL) /*!< GPIO INT0SET: GPIO20 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO19_Pos (19UL) /*!< GPIO INT0SET: GPIO19 (Bit 19) */ +#define GPIO_INT0SET_GPIO19_Msk (0x80000UL) /*!< GPIO INT0SET: GPIO19 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO18_Pos (18UL) /*!< GPIO INT0SET: GPIO18 (Bit 18) */ +#define GPIO_INT0SET_GPIO18_Msk (0x40000UL) /*!< GPIO INT0SET: GPIO18 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO17_Pos (17UL) /*!< GPIO INT0SET: GPIO17 (Bit 17) */ +#define GPIO_INT0SET_GPIO17_Msk (0x20000UL) /*!< GPIO INT0SET: GPIO17 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO16_Pos (16UL) /*!< GPIO INT0SET: GPIO16 (Bit 16) */ +#define GPIO_INT0SET_GPIO16_Msk (0x10000UL) /*!< GPIO INT0SET: GPIO16 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO15_Pos (15UL) /*!< GPIO INT0SET: GPIO15 (Bit 15) */ +#define GPIO_INT0SET_GPIO15_Msk (0x8000UL) /*!< GPIO INT0SET: GPIO15 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO14_Pos (14UL) /*!< GPIO INT0SET: GPIO14 (Bit 14) */ +#define GPIO_INT0SET_GPIO14_Msk (0x4000UL) /*!< GPIO INT0SET: GPIO14 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO13_Pos (13UL) /*!< GPIO INT0SET: GPIO13 (Bit 13) */ +#define GPIO_INT0SET_GPIO13_Msk (0x2000UL) /*!< GPIO INT0SET: GPIO13 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO12_Pos (12UL) /*!< GPIO INT0SET: GPIO12 (Bit 12) */ +#define GPIO_INT0SET_GPIO12_Msk (0x1000UL) /*!< GPIO INT0SET: GPIO12 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO11_Pos (11UL) /*!< GPIO INT0SET: GPIO11 (Bit 11) */ +#define GPIO_INT0SET_GPIO11_Msk (0x800UL) /*!< GPIO INT0SET: GPIO11 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO10_Pos (10UL) /*!< GPIO INT0SET: GPIO10 (Bit 10) */ +#define GPIO_INT0SET_GPIO10_Msk (0x400UL) /*!< GPIO INT0SET: GPIO10 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO9_Pos (9UL) /*!< GPIO INT0SET: GPIO9 (Bit 9) */ +#define GPIO_INT0SET_GPIO9_Msk (0x200UL) /*!< GPIO INT0SET: GPIO9 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO8_Pos (8UL) /*!< GPIO INT0SET: GPIO8 (Bit 8) */ +#define GPIO_INT0SET_GPIO8_Msk (0x100UL) /*!< GPIO INT0SET: GPIO8 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO7_Pos (7UL) /*!< GPIO INT0SET: GPIO7 (Bit 7) */ +#define GPIO_INT0SET_GPIO7_Msk (0x80UL) /*!< GPIO INT0SET: GPIO7 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO6_Pos (6UL) /*!< GPIO INT0SET: GPIO6 (Bit 6) */ +#define GPIO_INT0SET_GPIO6_Msk (0x40UL) /*!< GPIO INT0SET: GPIO6 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO5_Pos (5UL) /*!< GPIO INT0SET: GPIO5 (Bit 5) */ +#define GPIO_INT0SET_GPIO5_Msk (0x20UL) /*!< GPIO INT0SET: GPIO5 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO4_Pos (4UL) /*!< GPIO INT0SET: GPIO4 (Bit 4) */ +#define GPIO_INT0SET_GPIO4_Msk (0x10UL) /*!< GPIO INT0SET: GPIO4 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO3_Pos (3UL) /*!< GPIO INT0SET: GPIO3 (Bit 3) */ +#define GPIO_INT0SET_GPIO3_Msk (0x8UL) /*!< GPIO INT0SET: GPIO3 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO2_Pos (2UL) /*!< GPIO INT0SET: GPIO2 (Bit 2) */ +#define GPIO_INT0SET_GPIO2_Msk (0x4UL) /*!< GPIO INT0SET: GPIO2 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO1_Pos (1UL) /*!< GPIO INT0SET: GPIO1 (Bit 1) */ +#define GPIO_INT0SET_GPIO1_Msk (0x2UL) /*!< GPIO INT0SET: GPIO1 (Bitfield-Mask: 0x01) */ +#define GPIO_INT0SET_GPIO0_Pos (0UL) /*!< GPIO INT0SET: GPIO0 (Bit 0) */ +#define GPIO_INT0SET_GPIO0_Msk (0x1UL) /*!< GPIO INT0SET: GPIO0 (Bitfield-Mask: 0x01) */ +/* ======================================================== INT1EN ========================================================= */ +#define GPIO_INT1EN_GPIO49_Pos (17UL) /*!< GPIO INT1EN: GPIO49 (Bit 17) */ +#define GPIO_INT1EN_GPIO49_Msk (0x20000UL) /*!< GPIO INT1EN: GPIO49 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1EN_GPIO48_Pos (16UL) /*!< GPIO INT1EN: GPIO48 (Bit 16) */ +#define GPIO_INT1EN_GPIO48_Msk (0x10000UL) /*!< GPIO INT1EN: GPIO48 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1EN_GPIO47_Pos (15UL) /*!< GPIO INT1EN: GPIO47 (Bit 15) */ +#define GPIO_INT1EN_GPIO47_Msk (0x8000UL) /*!< GPIO INT1EN: GPIO47 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1EN_GPIO46_Pos (14UL) /*!< GPIO INT1EN: GPIO46 (Bit 14) */ +#define GPIO_INT1EN_GPIO46_Msk (0x4000UL) /*!< GPIO INT1EN: GPIO46 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1EN_GPIO45_Pos (13UL) /*!< GPIO INT1EN: GPIO45 (Bit 13) */ +#define GPIO_INT1EN_GPIO45_Msk (0x2000UL) /*!< GPIO INT1EN: GPIO45 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1EN_GPIO44_Pos (12UL) /*!< GPIO INT1EN: GPIO44 (Bit 12) */ +#define GPIO_INT1EN_GPIO44_Msk (0x1000UL) /*!< GPIO INT1EN: GPIO44 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1EN_GPIO43_Pos (11UL) /*!< GPIO INT1EN: GPIO43 (Bit 11) */ +#define GPIO_INT1EN_GPIO43_Msk (0x800UL) /*!< GPIO INT1EN: GPIO43 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1EN_GPIO42_Pos (10UL) /*!< GPIO INT1EN: GPIO42 (Bit 10) */ +#define GPIO_INT1EN_GPIO42_Msk (0x400UL) /*!< GPIO INT1EN: GPIO42 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1EN_GPIO41_Pos (9UL) /*!< GPIO INT1EN: GPIO41 (Bit 9) */ +#define GPIO_INT1EN_GPIO41_Msk (0x200UL) /*!< GPIO INT1EN: GPIO41 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1EN_GPIO40_Pos (8UL) /*!< GPIO INT1EN: GPIO40 (Bit 8) */ +#define GPIO_INT1EN_GPIO40_Msk (0x100UL) /*!< GPIO INT1EN: GPIO40 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1EN_GPIO39_Pos (7UL) /*!< GPIO INT1EN: GPIO39 (Bit 7) */ +#define GPIO_INT1EN_GPIO39_Msk (0x80UL) /*!< GPIO INT1EN: GPIO39 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1EN_GPIO38_Pos (6UL) /*!< GPIO INT1EN: GPIO38 (Bit 6) */ +#define GPIO_INT1EN_GPIO38_Msk (0x40UL) /*!< GPIO INT1EN: GPIO38 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1EN_GPIO37_Pos (5UL) /*!< GPIO INT1EN: GPIO37 (Bit 5) */ +#define GPIO_INT1EN_GPIO37_Msk (0x20UL) /*!< GPIO INT1EN: GPIO37 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1EN_GPIO36_Pos (4UL) /*!< GPIO INT1EN: GPIO36 (Bit 4) */ +#define GPIO_INT1EN_GPIO36_Msk (0x10UL) /*!< GPIO INT1EN: GPIO36 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1EN_GPIO35_Pos (3UL) /*!< GPIO INT1EN: GPIO35 (Bit 3) */ +#define GPIO_INT1EN_GPIO35_Msk (0x8UL) /*!< GPIO INT1EN: GPIO35 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1EN_GPIO34_Pos (2UL) /*!< GPIO INT1EN: GPIO34 (Bit 2) */ +#define GPIO_INT1EN_GPIO34_Msk (0x4UL) /*!< GPIO INT1EN: GPIO34 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1EN_GPIO33_Pos (1UL) /*!< GPIO INT1EN: GPIO33 (Bit 1) */ +#define GPIO_INT1EN_GPIO33_Msk (0x2UL) /*!< GPIO INT1EN: GPIO33 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1EN_GPIO32_Pos (0UL) /*!< GPIO INT1EN: GPIO32 (Bit 0) */ +#define GPIO_INT1EN_GPIO32_Msk (0x1UL) /*!< GPIO INT1EN: GPIO32 (Bitfield-Mask: 0x01) */ +/* ======================================================= INT1STAT ======================================================== */ +#define GPIO_INT1STAT_GPIO49_Pos (17UL) /*!< GPIO INT1STAT: GPIO49 (Bit 17) */ +#define GPIO_INT1STAT_GPIO49_Msk (0x20000UL) /*!< GPIO INT1STAT: GPIO49 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1STAT_GPIO48_Pos (16UL) /*!< GPIO INT1STAT: GPIO48 (Bit 16) */ +#define GPIO_INT1STAT_GPIO48_Msk (0x10000UL) /*!< GPIO INT1STAT: GPIO48 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1STAT_GPIO47_Pos (15UL) /*!< GPIO INT1STAT: GPIO47 (Bit 15) */ +#define GPIO_INT1STAT_GPIO47_Msk (0x8000UL) /*!< GPIO INT1STAT: GPIO47 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1STAT_GPIO46_Pos (14UL) /*!< GPIO INT1STAT: GPIO46 (Bit 14) */ +#define GPIO_INT1STAT_GPIO46_Msk (0x4000UL) /*!< GPIO INT1STAT: GPIO46 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1STAT_GPIO45_Pos (13UL) /*!< GPIO INT1STAT: GPIO45 (Bit 13) */ +#define GPIO_INT1STAT_GPIO45_Msk (0x2000UL) /*!< GPIO INT1STAT: GPIO45 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1STAT_GPIO44_Pos (12UL) /*!< GPIO INT1STAT: GPIO44 (Bit 12) */ +#define GPIO_INT1STAT_GPIO44_Msk (0x1000UL) /*!< GPIO INT1STAT: GPIO44 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1STAT_GPIO43_Pos (11UL) /*!< GPIO INT1STAT: GPIO43 (Bit 11) */ +#define GPIO_INT1STAT_GPIO43_Msk (0x800UL) /*!< GPIO INT1STAT: GPIO43 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1STAT_GPIO42_Pos (10UL) /*!< GPIO INT1STAT: GPIO42 (Bit 10) */ +#define GPIO_INT1STAT_GPIO42_Msk (0x400UL) /*!< GPIO INT1STAT: GPIO42 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1STAT_GPIO41_Pos (9UL) /*!< GPIO INT1STAT: GPIO41 (Bit 9) */ +#define GPIO_INT1STAT_GPIO41_Msk (0x200UL) /*!< GPIO INT1STAT: GPIO41 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1STAT_GPIO40_Pos (8UL) /*!< GPIO INT1STAT: GPIO40 (Bit 8) */ +#define GPIO_INT1STAT_GPIO40_Msk (0x100UL) /*!< GPIO INT1STAT: GPIO40 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1STAT_GPIO39_Pos (7UL) /*!< GPIO INT1STAT: GPIO39 (Bit 7) */ +#define GPIO_INT1STAT_GPIO39_Msk (0x80UL) /*!< GPIO INT1STAT: GPIO39 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1STAT_GPIO38_Pos (6UL) /*!< GPIO INT1STAT: GPIO38 (Bit 6) */ +#define GPIO_INT1STAT_GPIO38_Msk (0x40UL) /*!< GPIO INT1STAT: GPIO38 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1STAT_GPIO37_Pos (5UL) /*!< GPIO INT1STAT: GPIO37 (Bit 5) */ +#define GPIO_INT1STAT_GPIO37_Msk (0x20UL) /*!< GPIO INT1STAT: GPIO37 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1STAT_GPIO36_Pos (4UL) /*!< GPIO INT1STAT: GPIO36 (Bit 4) */ +#define GPIO_INT1STAT_GPIO36_Msk (0x10UL) /*!< GPIO INT1STAT: GPIO36 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1STAT_GPIO35_Pos (3UL) /*!< GPIO INT1STAT: GPIO35 (Bit 3) */ +#define GPIO_INT1STAT_GPIO35_Msk (0x8UL) /*!< GPIO INT1STAT: GPIO35 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1STAT_GPIO34_Pos (2UL) /*!< GPIO INT1STAT: GPIO34 (Bit 2) */ +#define GPIO_INT1STAT_GPIO34_Msk (0x4UL) /*!< GPIO INT1STAT: GPIO34 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1STAT_GPIO33_Pos (1UL) /*!< GPIO INT1STAT: GPIO33 (Bit 1) */ +#define GPIO_INT1STAT_GPIO33_Msk (0x2UL) /*!< GPIO INT1STAT: GPIO33 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1STAT_GPIO32_Pos (0UL) /*!< GPIO INT1STAT: GPIO32 (Bit 0) */ +#define GPIO_INT1STAT_GPIO32_Msk (0x1UL) /*!< GPIO INT1STAT: GPIO32 (Bitfield-Mask: 0x01) */ +/* ======================================================== INT1CLR ======================================================== */ +#define GPIO_INT1CLR_GPIO49_Pos (17UL) /*!< GPIO INT1CLR: GPIO49 (Bit 17) */ +#define GPIO_INT1CLR_GPIO49_Msk (0x20000UL) /*!< GPIO INT1CLR: GPIO49 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1CLR_GPIO48_Pos (16UL) /*!< GPIO INT1CLR: GPIO48 (Bit 16) */ +#define GPIO_INT1CLR_GPIO48_Msk (0x10000UL) /*!< GPIO INT1CLR: GPIO48 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1CLR_GPIO47_Pos (15UL) /*!< GPIO INT1CLR: GPIO47 (Bit 15) */ +#define GPIO_INT1CLR_GPIO47_Msk (0x8000UL) /*!< GPIO INT1CLR: GPIO47 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1CLR_GPIO46_Pos (14UL) /*!< GPIO INT1CLR: GPIO46 (Bit 14) */ +#define GPIO_INT1CLR_GPIO46_Msk (0x4000UL) /*!< GPIO INT1CLR: GPIO46 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1CLR_GPIO45_Pos (13UL) /*!< GPIO INT1CLR: GPIO45 (Bit 13) */ +#define GPIO_INT1CLR_GPIO45_Msk (0x2000UL) /*!< GPIO INT1CLR: GPIO45 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1CLR_GPIO44_Pos (12UL) /*!< GPIO INT1CLR: GPIO44 (Bit 12) */ +#define GPIO_INT1CLR_GPIO44_Msk (0x1000UL) /*!< GPIO INT1CLR: GPIO44 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1CLR_GPIO43_Pos (11UL) /*!< GPIO INT1CLR: GPIO43 (Bit 11) */ +#define GPIO_INT1CLR_GPIO43_Msk (0x800UL) /*!< GPIO INT1CLR: GPIO43 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1CLR_GPIO42_Pos (10UL) /*!< GPIO INT1CLR: GPIO42 (Bit 10) */ +#define GPIO_INT1CLR_GPIO42_Msk (0x400UL) /*!< GPIO INT1CLR: GPIO42 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1CLR_GPIO41_Pos (9UL) /*!< GPIO INT1CLR: GPIO41 (Bit 9) */ +#define GPIO_INT1CLR_GPIO41_Msk (0x200UL) /*!< GPIO INT1CLR: GPIO41 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1CLR_GPIO40_Pos (8UL) /*!< GPIO INT1CLR: GPIO40 (Bit 8) */ +#define GPIO_INT1CLR_GPIO40_Msk (0x100UL) /*!< GPIO INT1CLR: GPIO40 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1CLR_GPIO39_Pos (7UL) /*!< GPIO INT1CLR: GPIO39 (Bit 7) */ +#define GPIO_INT1CLR_GPIO39_Msk (0x80UL) /*!< GPIO INT1CLR: GPIO39 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1CLR_GPIO38_Pos (6UL) /*!< GPIO INT1CLR: GPIO38 (Bit 6) */ +#define GPIO_INT1CLR_GPIO38_Msk (0x40UL) /*!< GPIO INT1CLR: GPIO38 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1CLR_GPIO37_Pos (5UL) /*!< GPIO INT1CLR: GPIO37 (Bit 5) */ +#define GPIO_INT1CLR_GPIO37_Msk (0x20UL) /*!< GPIO INT1CLR: GPIO37 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1CLR_GPIO36_Pos (4UL) /*!< GPIO INT1CLR: GPIO36 (Bit 4) */ +#define GPIO_INT1CLR_GPIO36_Msk (0x10UL) /*!< GPIO INT1CLR: GPIO36 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1CLR_GPIO35_Pos (3UL) /*!< GPIO INT1CLR: GPIO35 (Bit 3) */ +#define GPIO_INT1CLR_GPIO35_Msk (0x8UL) /*!< GPIO INT1CLR: GPIO35 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1CLR_GPIO34_Pos (2UL) /*!< GPIO INT1CLR: GPIO34 (Bit 2) */ +#define GPIO_INT1CLR_GPIO34_Msk (0x4UL) /*!< GPIO INT1CLR: GPIO34 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1CLR_GPIO33_Pos (1UL) /*!< GPIO INT1CLR: GPIO33 (Bit 1) */ +#define GPIO_INT1CLR_GPIO33_Msk (0x2UL) /*!< GPIO INT1CLR: GPIO33 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1CLR_GPIO32_Pos (0UL) /*!< GPIO INT1CLR: GPIO32 (Bit 0) */ +#define GPIO_INT1CLR_GPIO32_Msk (0x1UL) /*!< GPIO INT1CLR: GPIO32 (Bitfield-Mask: 0x01) */ +/* ======================================================== INT1SET ======================================================== */ +#define GPIO_INT1SET_GPIO49_Pos (17UL) /*!< GPIO INT1SET: GPIO49 (Bit 17) */ +#define GPIO_INT1SET_GPIO49_Msk (0x20000UL) /*!< GPIO INT1SET: GPIO49 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1SET_GPIO48_Pos (16UL) /*!< GPIO INT1SET: GPIO48 (Bit 16) */ +#define GPIO_INT1SET_GPIO48_Msk (0x10000UL) /*!< GPIO INT1SET: GPIO48 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1SET_GPIO47_Pos (15UL) /*!< GPIO INT1SET: GPIO47 (Bit 15) */ +#define GPIO_INT1SET_GPIO47_Msk (0x8000UL) /*!< GPIO INT1SET: GPIO47 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1SET_GPIO46_Pos (14UL) /*!< GPIO INT1SET: GPIO46 (Bit 14) */ +#define GPIO_INT1SET_GPIO46_Msk (0x4000UL) /*!< GPIO INT1SET: GPIO46 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1SET_GPIO45_Pos (13UL) /*!< GPIO INT1SET: GPIO45 (Bit 13) */ +#define GPIO_INT1SET_GPIO45_Msk (0x2000UL) /*!< GPIO INT1SET: GPIO45 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1SET_GPIO44_Pos (12UL) /*!< GPIO INT1SET: GPIO44 (Bit 12) */ +#define GPIO_INT1SET_GPIO44_Msk (0x1000UL) /*!< GPIO INT1SET: GPIO44 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1SET_GPIO43_Pos (11UL) /*!< GPIO INT1SET: GPIO43 (Bit 11) */ +#define GPIO_INT1SET_GPIO43_Msk (0x800UL) /*!< GPIO INT1SET: GPIO43 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1SET_GPIO42_Pos (10UL) /*!< GPIO INT1SET: GPIO42 (Bit 10) */ +#define GPIO_INT1SET_GPIO42_Msk (0x400UL) /*!< GPIO INT1SET: GPIO42 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1SET_GPIO41_Pos (9UL) /*!< GPIO INT1SET: GPIO41 (Bit 9) */ +#define GPIO_INT1SET_GPIO41_Msk (0x200UL) /*!< GPIO INT1SET: GPIO41 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1SET_GPIO40_Pos (8UL) /*!< GPIO INT1SET: GPIO40 (Bit 8) */ +#define GPIO_INT1SET_GPIO40_Msk (0x100UL) /*!< GPIO INT1SET: GPIO40 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1SET_GPIO39_Pos (7UL) /*!< GPIO INT1SET: GPIO39 (Bit 7) */ +#define GPIO_INT1SET_GPIO39_Msk (0x80UL) /*!< GPIO INT1SET: GPIO39 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1SET_GPIO38_Pos (6UL) /*!< GPIO INT1SET: GPIO38 (Bit 6) */ +#define GPIO_INT1SET_GPIO38_Msk (0x40UL) /*!< GPIO INT1SET: GPIO38 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1SET_GPIO37_Pos (5UL) /*!< GPIO INT1SET: GPIO37 (Bit 5) */ +#define GPIO_INT1SET_GPIO37_Msk (0x20UL) /*!< GPIO INT1SET: GPIO37 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1SET_GPIO36_Pos (4UL) /*!< GPIO INT1SET: GPIO36 (Bit 4) */ +#define GPIO_INT1SET_GPIO36_Msk (0x10UL) /*!< GPIO INT1SET: GPIO36 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1SET_GPIO35_Pos (3UL) /*!< GPIO INT1SET: GPIO35 (Bit 3) */ +#define GPIO_INT1SET_GPIO35_Msk (0x8UL) /*!< GPIO INT1SET: GPIO35 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1SET_GPIO34_Pos (2UL) /*!< GPIO INT1SET: GPIO34 (Bit 2) */ +#define GPIO_INT1SET_GPIO34_Msk (0x4UL) /*!< GPIO INT1SET: GPIO34 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1SET_GPIO33_Pos (1UL) /*!< GPIO INT1SET: GPIO33 (Bit 1) */ +#define GPIO_INT1SET_GPIO33_Msk (0x2UL) /*!< GPIO INT1SET: GPIO33 (Bitfield-Mask: 0x01) */ +#define GPIO_INT1SET_GPIO32_Pos (0UL) /*!< GPIO INT1SET: GPIO32 (Bit 0) */ +#define GPIO_INT1SET_GPIO32_Msk (0x1UL) /*!< GPIO INT1SET: GPIO32 (Bitfield-Mask: 0x01) */ + + +/* =========================================================================================================================== */ +/* ================ IOM0 ================ */ +/* =========================================================================================================================== */ + +/* ========================================================= FIFO ========================================================== */ +#define IOM0_FIFO_FIFO_Pos (0UL) /*!< IOM0 FIFO: FIFO (Bit 0) */ +#define IOM0_FIFO_FIFO_Msk (0xffffffffUL) /*!< IOM0 FIFO: FIFO (Bitfield-Mask: 0xffffffff) */ +/* ======================================================== FIFOPTR ======================================================== */ +#define IOM0_FIFOPTR_FIFO1REM_Pos (24UL) /*!< IOM0 FIFOPTR: FIFO1REM (Bit 24) */ +#define IOM0_FIFOPTR_FIFO1REM_Msk (0xff000000UL) /*!< IOM0 FIFOPTR: FIFO1REM (Bitfield-Mask: 0xff) */ +#define IOM0_FIFOPTR_FIFO1SIZ_Pos (16UL) /*!< IOM0 FIFOPTR: FIFO1SIZ (Bit 16) */ +#define IOM0_FIFOPTR_FIFO1SIZ_Msk (0xff0000UL) /*!< IOM0 FIFOPTR: FIFO1SIZ (Bitfield-Mask: 0xff) */ +#define IOM0_FIFOPTR_FIFO0REM_Pos (8UL) /*!< IOM0 FIFOPTR: FIFO0REM (Bit 8) */ +#define IOM0_FIFOPTR_FIFO0REM_Msk (0xff00UL) /*!< IOM0 FIFOPTR: FIFO0REM (Bitfield-Mask: 0xff) */ +#define IOM0_FIFOPTR_FIFO0SIZ_Pos (0UL) /*!< IOM0 FIFOPTR: FIFO0SIZ (Bit 0) */ +#define IOM0_FIFOPTR_FIFO0SIZ_Msk (0xffUL) /*!< IOM0 FIFOPTR: FIFO0SIZ (Bitfield-Mask: 0xff) */ +/* ======================================================== FIFOTHR ======================================================== */ +#define IOM0_FIFOTHR_FIFOWTHR_Pos (8UL) /*!< IOM0 FIFOTHR: FIFOWTHR (Bit 8) */ +#define IOM0_FIFOTHR_FIFOWTHR_Msk (0x3f00UL) /*!< IOM0 FIFOTHR: FIFOWTHR (Bitfield-Mask: 0x3f) */ +#define IOM0_FIFOTHR_FIFORTHR_Pos (0UL) /*!< IOM0 FIFOTHR: FIFORTHR (Bit 0) */ +#define IOM0_FIFOTHR_FIFORTHR_Msk (0x3fUL) /*!< IOM0 FIFOTHR: FIFORTHR (Bitfield-Mask: 0x3f) */ +/* ======================================================== FIFOPOP ======================================================== */ +#define IOM0_FIFOPOP_FIFODOUT_Pos (0UL) /*!< IOM0 FIFOPOP: FIFODOUT (Bit 0) */ +#define IOM0_FIFOPOP_FIFODOUT_Msk (0xffffffffUL) /*!< IOM0 FIFOPOP: FIFODOUT (Bitfield-Mask: 0xffffffff) */ +/* ======================================================= FIFOPUSH ======================================================== */ +#define IOM0_FIFOPUSH_FIFODIN_Pos (0UL) /*!< IOM0 FIFOPUSH: FIFODIN (Bit 0) */ +#define IOM0_FIFOPUSH_FIFODIN_Msk (0xffffffffUL) /*!< IOM0 FIFOPUSH: FIFODIN (Bitfield-Mask: 0xffffffff) */ +/* ======================================================= FIFOCTRL ======================================================== */ +#define IOM0_FIFOCTRL_FIFORSTN_Pos (1UL) /*!< IOM0 FIFOCTRL: FIFORSTN (Bit 1) */ +#define IOM0_FIFOCTRL_FIFORSTN_Msk (0x2UL) /*!< IOM0 FIFOCTRL: FIFORSTN (Bitfield-Mask: 0x01) */ +#define IOM0_FIFOCTRL_POPWR_Pos (0UL) /*!< IOM0 FIFOCTRL: POPWR (Bit 0) */ +#define IOM0_FIFOCTRL_POPWR_Msk (0x1UL) /*!< IOM0 FIFOCTRL: POPWR (Bitfield-Mask: 0x01) */ +/* ======================================================== FIFOLOC ======================================================== */ +#define IOM0_FIFOLOC_FIFORPTR_Pos (8UL) /*!< IOM0 FIFOLOC: FIFORPTR (Bit 8) */ +#define IOM0_FIFOLOC_FIFORPTR_Msk (0xf00UL) /*!< IOM0 FIFOLOC: FIFORPTR (Bitfield-Mask: 0x0f) */ +#define IOM0_FIFOLOC_FIFOWPTR_Pos (0UL) /*!< IOM0 FIFOLOC: FIFOWPTR (Bit 0) */ +#define IOM0_FIFOLOC_FIFOWPTR_Msk (0xfUL) /*!< IOM0 FIFOLOC: FIFOWPTR (Bitfield-Mask: 0x0f) */ +/* ========================================================= INTEN ========================================================= */ +#define IOM0_INTEN_CQERR_Pos (14UL) /*!< IOM0 INTEN: CQERR (Bit 14) */ +#define IOM0_INTEN_CQERR_Msk (0x4000UL) /*!< IOM0 INTEN: CQERR (Bitfield-Mask: 0x01) */ +#define IOM0_INTEN_CQUPD_Pos (13UL) /*!< IOM0 INTEN: CQUPD (Bit 13) */ +#define IOM0_INTEN_CQUPD_Msk (0x2000UL) /*!< IOM0 INTEN: CQUPD (Bitfield-Mask: 0x01) */ +#define IOM0_INTEN_CQPAUSED_Pos (12UL) /*!< IOM0 INTEN: CQPAUSED (Bit 12) */ +#define IOM0_INTEN_CQPAUSED_Msk (0x1000UL) /*!< IOM0 INTEN: CQPAUSED (Bitfield-Mask: 0x01) */ +#define IOM0_INTEN_DERR_Pos (11UL) /*!< IOM0 INTEN: DERR (Bit 11) */ +#define IOM0_INTEN_DERR_Msk (0x800UL) /*!< IOM0 INTEN: DERR (Bitfield-Mask: 0x01) */ +#define IOM0_INTEN_DCMP_Pos (10UL) /*!< IOM0 INTEN: DCMP (Bit 10) */ +#define IOM0_INTEN_DCMP_Msk (0x400UL) /*!< IOM0 INTEN: DCMP (Bitfield-Mask: 0x01) */ +#define IOM0_INTEN_ARB_Pos (9UL) /*!< IOM0 INTEN: ARB (Bit 9) */ +#define IOM0_INTEN_ARB_Msk (0x200UL) /*!< IOM0 INTEN: ARB (Bitfield-Mask: 0x01) */ +#define IOM0_INTEN_STOP_Pos (8UL) /*!< IOM0 INTEN: STOP (Bit 8) */ +#define IOM0_INTEN_STOP_Msk (0x100UL) /*!< IOM0 INTEN: STOP (Bitfield-Mask: 0x01) */ +#define IOM0_INTEN_START_Pos (7UL) /*!< IOM0 INTEN: START (Bit 7) */ +#define IOM0_INTEN_START_Msk (0x80UL) /*!< IOM0 INTEN: START (Bitfield-Mask: 0x01) */ +#define IOM0_INTEN_ICMD_Pos (6UL) /*!< IOM0 INTEN: ICMD (Bit 6) */ +#define IOM0_INTEN_ICMD_Msk (0x40UL) /*!< IOM0 INTEN: ICMD (Bitfield-Mask: 0x01) */ +#define IOM0_INTEN_IACC_Pos (5UL) /*!< IOM0 INTEN: IACC (Bit 5) */ +#define IOM0_INTEN_IACC_Msk (0x20UL) /*!< IOM0 INTEN: IACC (Bitfield-Mask: 0x01) */ +#define IOM0_INTEN_NAK_Pos (4UL) /*!< IOM0 INTEN: NAK (Bit 4) */ +#define IOM0_INTEN_NAK_Msk (0x10UL) /*!< IOM0 INTEN: NAK (Bitfield-Mask: 0x01) */ +#define IOM0_INTEN_FOVFL_Pos (3UL) /*!< IOM0 INTEN: FOVFL (Bit 3) */ +#define IOM0_INTEN_FOVFL_Msk (0x8UL) /*!< IOM0 INTEN: FOVFL (Bitfield-Mask: 0x01) */ +#define IOM0_INTEN_FUNDFL_Pos (2UL) /*!< IOM0 INTEN: FUNDFL (Bit 2) */ +#define IOM0_INTEN_FUNDFL_Msk (0x4UL) /*!< IOM0 INTEN: FUNDFL (Bitfield-Mask: 0x01) */ +#define IOM0_INTEN_THR_Pos (1UL) /*!< IOM0 INTEN: THR (Bit 1) */ +#define IOM0_INTEN_THR_Msk (0x2UL) /*!< IOM0 INTEN: THR (Bitfield-Mask: 0x01) */ +#define IOM0_INTEN_CMDCMP_Pos (0UL) /*!< IOM0 INTEN: CMDCMP (Bit 0) */ +#define IOM0_INTEN_CMDCMP_Msk (0x1UL) /*!< IOM0 INTEN: CMDCMP (Bitfield-Mask: 0x01) */ +/* ======================================================== INTSTAT ======================================================== */ +#define IOM0_INTSTAT_CQERR_Pos (14UL) /*!< IOM0 INTSTAT: CQERR (Bit 14) */ +#define IOM0_INTSTAT_CQERR_Msk (0x4000UL) /*!< IOM0 INTSTAT: CQERR (Bitfield-Mask: 0x01) */ +#define IOM0_INTSTAT_CQUPD_Pos (13UL) /*!< IOM0 INTSTAT: CQUPD (Bit 13) */ +#define IOM0_INTSTAT_CQUPD_Msk (0x2000UL) /*!< IOM0 INTSTAT: CQUPD (Bitfield-Mask: 0x01) */ +#define IOM0_INTSTAT_CQPAUSED_Pos (12UL) /*!< IOM0 INTSTAT: CQPAUSED (Bit 12) */ +#define IOM0_INTSTAT_CQPAUSED_Msk (0x1000UL) /*!< IOM0 INTSTAT: CQPAUSED (Bitfield-Mask: 0x01) */ +#define IOM0_INTSTAT_DERR_Pos (11UL) /*!< IOM0 INTSTAT: DERR (Bit 11) */ +#define IOM0_INTSTAT_DERR_Msk (0x800UL) /*!< IOM0 INTSTAT: DERR (Bitfield-Mask: 0x01) */ +#define IOM0_INTSTAT_DCMP_Pos (10UL) /*!< IOM0 INTSTAT: DCMP (Bit 10) */ +#define IOM0_INTSTAT_DCMP_Msk (0x400UL) /*!< IOM0 INTSTAT: DCMP (Bitfield-Mask: 0x01) */ +#define IOM0_INTSTAT_ARB_Pos (9UL) /*!< IOM0 INTSTAT: ARB (Bit 9) */ +#define IOM0_INTSTAT_ARB_Msk (0x200UL) /*!< IOM0 INTSTAT: ARB (Bitfield-Mask: 0x01) */ +#define IOM0_INTSTAT_STOP_Pos (8UL) /*!< IOM0 INTSTAT: STOP (Bit 8) */ +#define IOM0_INTSTAT_STOP_Msk (0x100UL) /*!< IOM0 INTSTAT: STOP (Bitfield-Mask: 0x01) */ +#define IOM0_INTSTAT_START_Pos (7UL) /*!< IOM0 INTSTAT: START (Bit 7) */ +#define IOM0_INTSTAT_START_Msk (0x80UL) /*!< IOM0 INTSTAT: START (Bitfield-Mask: 0x01) */ +#define IOM0_INTSTAT_ICMD_Pos (6UL) /*!< IOM0 INTSTAT: ICMD (Bit 6) */ +#define IOM0_INTSTAT_ICMD_Msk (0x40UL) /*!< IOM0 INTSTAT: ICMD (Bitfield-Mask: 0x01) */ +#define IOM0_INTSTAT_IACC_Pos (5UL) /*!< IOM0 INTSTAT: IACC (Bit 5) */ +#define IOM0_INTSTAT_IACC_Msk (0x20UL) /*!< IOM0 INTSTAT: IACC (Bitfield-Mask: 0x01) */ +#define IOM0_INTSTAT_NAK_Pos (4UL) /*!< IOM0 INTSTAT: NAK (Bit 4) */ +#define IOM0_INTSTAT_NAK_Msk (0x10UL) /*!< IOM0 INTSTAT: NAK (Bitfield-Mask: 0x01) */ +#define IOM0_INTSTAT_FOVFL_Pos (3UL) /*!< IOM0 INTSTAT: FOVFL (Bit 3) */ +#define IOM0_INTSTAT_FOVFL_Msk (0x8UL) /*!< IOM0 INTSTAT: FOVFL (Bitfield-Mask: 0x01) */ +#define IOM0_INTSTAT_FUNDFL_Pos (2UL) /*!< IOM0 INTSTAT: FUNDFL (Bit 2) */ +#define IOM0_INTSTAT_FUNDFL_Msk (0x4UL) /*!< IOM0 INTSTAT: FUNDFL (Bitfield-Mask: 0x01) */ +#define IOM0_INTSTAT_THR_Pos (1UL) /*!< IOM0 INTSTAT: THR (Bit 1) */ +#define IOM0_INTSTAT_THR_Msk (0x2UL) /*!< IOM0 INTSTAT: THR (Bitfield-Mask: 0x01) */ +#define IOM0_INTSTAT_CMDCMP_Pos (0UL) /*!< IOM0 INTSTAT: CMDCMP (Bit 0) */ +#define IOM0_INTSTAT_CMDCMP_Msk (0x1UL) /*!< IOM0 INTSTAT: CMDCMP (Bitfield-Mask: 0x01) */ +/* ======================================================== INTCLR ========================================================= */ +#define IOM0_INTCLR_CQERR_Pos (14UL) /*!< IOM0 INTCLR: CQERR (Bit 14) */ +#define IOM0_INTCLR_CQERR_Msk (0x4000UL) /*!< IOM0 INTCLR: CQERR (Bitfield-Mask: 0x01) */ +#define IOM0_INTCLR_CQUPD_Pos (13UL) /*!< IOM0 INTCLR: CQUPD (Bit 13) */ +#define IOM0_INTCLR_CQUPD_Msk (0x2000UL) /*!< IOM0 INTCLR: CQUPD (Bitfield-Mask: 0x01) */ +#define IOM0_INTCLR_CQPAUSED_Pos (12UL) /*!< IOM0 INTCLR: CQPAUSED (Bit 12) */ +#define IOM0_INTCLR_CQPAUSED_Msk (0x1000UL) /*!< IOM0 INTCLR: CQPAUSED (Bitfield-Mask: 0x01) */ +#define IOM0_INTCLR_DERR_Pos (11UL) /*!< IOM0 INTCLR: DERR (Bit 11) */ +#define IOM0_INTCLR_DERR_Msk (0x800UL) /*!< IOM0 INTCLR: DERR (Bitfield-Mask: 0x01) */ +#define IOM0_INTCLR_DCMP_Pos (10UL) /*!< IOM0 INTCLR: DCMP (Bit 10) */ +#define IOM0_INTCLR_DCMP_Msk (0x400UL) /*!< IOM0 INTCLR: DCMP (Bitfield-Mask: 0x01) */ +#define IOM0_INTCLR_ARB_Pos (9UL) /*!< IOM0 INTCLR: ARB (Bit 9) */ +#define IOM0_INTCLR_ARB_Msk (0x200UL) /*!< IOM0 INTCLR: ARB (Bitfield-Mask: 0x01) */ +#define IOM0_INTCLR_STOP_Pos (8UL) /*!< IOM0 INTCLR: STOP (Bit 8) */ +#define IOM0_INTCLR_STOP_Msk (0x100UL) /*!< IOM0 INTCLR: STOP (Bitfield-Mask: 0x01) */ +#define IOM0_INTCLR_START_Pos (7UL) /*!< IOM0 INTCLR: START (Bit 7) */ +#define IOM0_INTCLR_START_Msk (0x80UL) /*!< IOM0 INTCLR: START (Bitfield-Mask: 0x01) */ +#define IOM0_INTCLR_ICMD_Pos (6UL) /*!< IOM0 INTCLR: ICMD (Bit 6) */ +#define IOM0_INTCLR_ICMD_Msk (0x40UL) /*!< IOM0 INTCLR: ICMD (Bitfield-Mask: 0x01) */ +#define IOM0_INTCLR_IACC_Pos (5UL) /*!< IOM0 INTCLR: IACC (Bit 5) */ +#define IOM0_INTCLR_IACC_Msk (0x20UL) /*!< IOM0 INTCLR: IACC (Bitfield-Mask: 0x01) */ +#define IOM0_INTCLR_NAK_Pos (4UL) /*!< IOM0 INTCLR: NAK (Bit 4) */ +#define IOM0_INTCLR_NAK_Msk (0x10UL) /*!< IOM0 INTCLR: NAK (Bitfield-Mask: 0x01) */ +#define IOM0_INTCLR_FOVFL_Pos (3UL) /*!< IOM0 INTCLR: FOVFL (Bit 3) */ +#define IOM0_INTCLR_FOVFL_Msk (0x8UL) /*!< IOM0 INTCLR: FOVFL (Bitfield-Mask: 0x01) */ +#define IOM0_INTCLR_FUNDFL_Pos (2UL) /*!< IOM0 INTCLR: FUNDFL (Bit 2) */ +#define IOM0_INTCLR_FUNDFL_Msk (0x4UL) /*!< IOM0 INTCLR: FUNDFL (Bitfield-Mask: 0x01) */ +#define IOM0_INTCLR_THR_Pos (1UL) /*!< IOM0 INTCLR: THR (Bit 1) */ +#define IOM0_INTCLR_THR_Msk (0x2UL) /*!< IOM0 INTCLR: THR (Bitfield-Mask: 0x01) */ +#define IOM0_INTCLR_CMDCMP_Pos (0UL) /*!< IOM0 INTCLR: CMDCMP (Bit 0) */ +#define IOM0_INTCLR_CMDCMP_Msk (0x1UL) /*!< IOM0 INTCLR: CMDCMP (Bitfield-Mask: 0x01) */ +/* ======================================================== INTSET ========================================================= */ +#define IOM0_INTSET_CQERR_Pos (14UL) /*!< IOM0 INTSET: CQERR (Bit 14) */ +#define IOM0_INTSET_CQERR_Msk (0x4000UL) /*!< IOM0 INTSET: CQERR (Bitfield-Mask: 0x01) */ +#define IOM0_INTSET_CQUPD_Pos (13UL) /*!< IOM0 INTSET: CQUPD (Bit 13) */ +#define IOM0_INTSET_CQUPD_Msk (0x2000UL) /*!< IOM0 INTSET: CQUPD (Bitfield-Mask: 0x01) */ +#define IOM0_INTSET_CQPAUSED_Pos (12UL) /*!< IOM0 INTSET: CQPAUSED (Bit 12) */ +#define IOM0_INTSET_CQPAUSED_Msk (0x1000UL) /*!< IOM0 INTSET: CQPAUSED (Bitfield-Mask: 0x01) */ +#define IOM0_INTSET_DERR_Pos (11UL) /*!< IOM0 INTSET: DERR (Bit 11) */ +#define IOM0_INTSET_DERR_Msk (0x800UL) /*!< IOM0 INTSET: DERR (Bitfield-Mask: 0x01) */ +#define IOM0_INTSET_DCMP_Pos (10UL) /*!< IOM0 INTSET: DCMP (Bit 10) */ +#define IOM0_INTSET_DCMP_Msk (0x400UL) /*!< IOM0 INTSET: DCMP (Bitfield-Mask: 0x01) */ +#define IOM0_INTSET_ARB_Pos (9UL) /*!< IOM0 INTSET: ARB (Bit 9) */ +#define IOM0_INTSET_ARB_Msk (0x200UL) /*!< IOM0 INTSET: ARB (Bitfield-Mask: 0x01) */ +#define IOM0_INTSET_STOP_Pos (8UL) /*!< IOM0 INTSET: STOP (Bit 8) */ +#define IOM0_INTSET_STOP_Msk (0x100UL) /*!< IOM0 INTSET: STOP (Bitfield-Mask: 0x01) */ +#define IOM0_INTSET_START_Pos (7UL) /*!< IOM0 INTSET: START (Bit 7) */ +#define IOM0_INTSET_START_Msk (0x80UL) /*!< IOM0 INTSET: START (Bitfield-Mask: 0x01) */ +#define IOM0_INTSET_ICMD_Pos (6UL) /*!< IOM0 INTSET: ICMD (Bit 6) */ +#define IOM0_INTSET_ICMD_Msk (0x40UL) /*!< IOM0 INTSET: ICMD (Bitfield-Mask: 0x01) */ +#define IOM0_INTSET_IACC_Pos (5UL) /*!< IOM0 INTSET: IACC (Bit 5) */ +#define IOM0_INTSET_IACC_Msk (0x20UL) /*!< IOM0 INTSET: IACC (Bitfield-Mask: 0x01) */ +#define IOM0_INTSET_NAK_Pos (4UL) /*!< IOM0 INTSET: NAK (Bit 4) */ +#define IOM0_INTSET_NAK_Msk (0x10UL) /*!< IOM0 INTSET: NAK (Bitfield-Mask: 0x01) */ +#define IOM0_INTSET_FOVFL_Pos (3UL) /*!< IOM0 INTSET: FOVFL (Bit 3) */ +#define IOM0_INTSET_FOVFL_Msk (0x8UL) /*!< IOM0 INTSET: FOVFL (Bitfield-Mask: 0x01) */ +#define IOM0_INTSET_FUNDFL_Pos (2UL) /*!< IOM0 INTSET: FUNDFL (Bit 2) */ +#define IOM0_INTSET_FUNDFL_Msk (0x4UL) /*!< IOM0 INTSET: FUNDFL (Bitfield-Mask: 0x01) */ +#define IOM0_INTSET_THR_Pos (1UL) /*!< IOM0 INTSET: THR (Bit 1) */ +#define IOM0_INTSET_THR_Msk (0x2UL) /*!< IOM0 INTSET: THR (Bitfield-Mask: 0x01) */ +#define IOM0_INTSET_CMDCMP_Pos (0UL) /*!< IOM0 INTSET: CMDCMP (Bit 0) */ +#define IOM0_INTSET_CMDCMP_Msk (0x1UL) /*!< IOM0 INTSET: CMDCMP (Bitfield-Mask: 0x01) */ +/* ======================================================== CLKCFG ========================================================= */ +#define IOM0_CLKCFG_TOTPER_Pos (24UL) /*!< IOM0 CLKCFG: TOTPER (Bit 24) */ +#define IOM0_CLKCFG_TOTPER_Msk (0xff000000UL) /*!< IOM0 CLKCFG: TOTPER (Bitfield-Mask: 0xff) */ +#define IOM0_CLKCFG_LOWPER_Pos (16UL) /*!< IOM0 CLKCFG: LOWPER (Bit 16) */ +#define IOM0_CLKCFG_LOWPER_Msk (0xff0000UL) /*!< IOM0 CLKCFG: LOWPER (Bitfield-Mask: 0xff) */ +#define IOM0_CLKCFG_DIVEN_Pos (12UL) /*!< IOM0 CLKCFG: DIVEN (Bit 12) */ +#define IOM0_CLKCFG_DIVEN_Msk (0x1000UL) /*!< IOM0 CLKCFG: DIVEN (Bitfield-Mask: 0x01) */ +#define IOM0_CLKCFG_DIV3_Pos (11UL) /*!< IOM0 CLKCFG: DIV3 (Bit 11) */ +#define IOM0_CLKCFG_DIV3_Msk (0x800UL) /*!< IOM0 CLKCFG: DIV3 (Bitfield-Mask: 0x01) */ +#define IOM0_CLKCFG_FSEL_Pos (8UL) /*!< IOM0 CLKCFG: FSEL (Bit 8) */ +#define IOM0_CLKCFG_FSEL_Msk (0x700UL) /*!< IOM0 CLKCFG: FSEL (Bitfield-Mask: 0x07) */ +#define IOM0_CLKCFG_IOCLKEN_Pos (0UL) /*!< IOM0 CLKCFG: IOCLKEN (Bit 0) */ +#define IOM0_CLKCFG_IOCLKEN_Msk (0x1UL) /*!< IOM0 CLKCFG: IOCLKEN (Bitfield-Mask: 0x01) */ +/* ====================================================== SUBMODCTRL ======================================================= */ +#define IOM0_SUBMODCTRL_SMOD1TYPE_Pos (5UL) /*!< IOM0 SUBMODCTRL: SMOD1TYPE (Bit 5) */ +#define IOM0_SUBMODCTRL_SMOD1TYPE_Msk (0xe0UL) /*!< IOM0 SUBMODCTRL: SMOD1TYPE (Bitfield-Mask: 0x07) */ +#define IOM0_SUBMODCTRL_SMOD1EN_Pos (4UL) /*!< IOM0 SUBMODCTRL: SMOD1EN (Bit 4) */ +#define IOM0_SUBMODCTRL_SMOD1EN_Msk (0x10UL) /*!< IOM0 SUBMODCTRL: SMOD1EN (Bitfield-Mask: 0x01) */ +#define IOM0_SUBMODCTRL_SMOD0TYPE_Pos (1UL) /*!< IOM0 SUBMODCTRL: SMOD0TYPE (Bit 1) */ +#define IOM0_SUBMODCTRL_SMOD0TYPE_Msk (0xeUL) /*!< IOM0 SUBMODCTRL: SMOD0TYPE (Bitfield-Mask: 0x07) */ +#define IOM0_SUBMODCTRL_SMOD0EN_Pos (0UL) /*!< IOM0 SUBMODCTRL: SMOD0EN (Bit 0) */ +#define IOM0_SUBMODCTRL_SMOD0EN_Msk (0x1UL) /*!< IOM0 SUBMODCTRL: SMOD0EN (Bitfield-Mask: 0x01) */ +/* ========================================================== CMD ========================================================== */ +#define IOM0_CMD_OFFSETLO_Pos (24UL) /*!< IOM0 CMD: OFFSETLO (Bit 24) */ +#define IOM0_CMD_OFFSETLO_Msk (0xff000000UL) /*!< IOM0 CMD: OFFSETLO (Bitfield-Mask: 0xff) */ +#define IOM0_CMD_CMDSEL_Pos (20UL) /*!< IOM0 CMD: CMDSEL (Bit 20) */ +#define IOM0_CMD_CMDSEL_Msk (0x300000UL) /*!< IOM0 CMD: CMDSEL (Bitfield-Mask: 0x03) */ +#define IOM0_CMD_TSIZE_Pos (8UL) /*!< IOM0 CMD: TSIZE (Bit 8) */ +#define IOM0_CMD_TSIZE_Msk (0xfff00UL) /*!< IOM0 CMD: TSIZE (Bitfield-Mask: 0xfff) */ +#define IOM0_CMD_CONT_Pos (7UL) /*!< IOM0 CMD: CONT (Bit 7) */ +#define IOM0_CMD_CONT_Msk (0x80UL) /*!< IOM0 CMD: CONT (Bitfield-Mask: 0x01) */ +#define IOM0_CMD_OFFSETCNT_Pos (5UL) /*!< IOM0 CMD: OFFSETCNT (Bit 5) */ +#define IOM0_CMD_OFFSETCNT_Msk (0x60UL) /*!< IOM0 CMD: OFFSETCNT (Bitfield-Mask: 0x03) */ +#define IOM0_CMD_CMD_Pos (0UL) /*!< IOM0 CMD: CMD (Bit 0) */ +#define IOM0_CMD_CMD_Msk (0x1fUL) /*!< IOM0 CMD: CMD (Bitfield-Mask: 0x1f) */ +/* ======================================================== CMDRPT ========================================================= */ +#define IOM0_CMDRPT_CMDRPT_Pos (0UL) /*!< IOM0 CMDRPT: CMDRPT (Bit 0) */ +#define IOM0_CMDRPT_CMDRPT_Msk (0x1fUL) /*!< IOM0 CMDRPT: CMDRPT (Bitfield-Mask: 0x1f) */ +/* ======================================================= OFFSETHI ======================================================== */ +#define IOM0_OFFSETHI_OFFSETHI_Pos (0UL) /*!< IOM0 OFFSETHI: OFFSETHI (Bit 0) */ +#define IOM0_OFFSETHI_OFFSETHI_Msk (0xffffUL) /*!< IOM0 OFFSETHI: OFFSETHI (Bitfield-Mask: 0xffff) */ +/* ======================================================== CMDSTAT ======================================================== */ +#define IOM0_CMDSTAT_CTSIZE_Pos (8UL) /*!< IOM0 CMDSTAT: CTSIZE (Bit 8) */ +#define IOM0_CMDSTAT_CTSIZE_Msk (0xfff00UL) /*!< IOM0 CMDSTAT: CTSIZE (Bitfield-Mask: 0xfff) */ +#define IOM0_CMDSTAT_CMDSTAT_Pos (5UL) /*!< IOM0 CMDSTAT: CMDSTAT (Bit 5) */ +#define IOM0_CMDSTAT_CMDSTAT_Msk (0xe0UL) /*!< IOM0 CMDSTAT: CMDSTAT (Bitfield-Mask: 0x07) */ +#define IOM0_CMDSTAT_CCMD_Pos (0UL) /*!< IOM0 CMDSTAT: CCMD (Bit 0) */ +#define IOM0_CMDSTAT_CCMD_Msk (0x1fUL) /*!< IOM0 CMDSTAT: CCMD (Bitfield-Mask: 0x1f) */ +/* ======================================================= DMATRIGEN ======================================================= */ +#define IOM0_DMATRIGEN_DTHREN_Pos (1UL) /*!< IOM0 DMATRIGEN: DTHREN (Bit 1) */ +#define IOM0_DMATRIGEN_DTHREN_Msk (0x2UL) /*!< IOM0 DMATRIGEN: DTHREN (Bitfield-Mask: 0x01) */ +#define IOM0_DMATRIGEN_DCMDCMPEN_Pos (0UL) /*!< IOM0 DMATRIGEN: DCMDCMPEN (Bit 0) */ +#define IOM0_DMATRIGEN_DCMDCMPEN_Msk (0x1UL) /*!< IOM0 DMATRIGEN: DCMDCMPEN (Bitfield-Mask: 0x01) */ +/* ====================================================== DMATRIGSTAT ====================================================== */ +#define IOM0_DMATRIGSTAT_DTOTCMP_Pos (2UL) /*!< IOM0 DMATRIGSTAT: DTOTCMP (Bit 2) */ +#define IOM0_DMATRIGSTAT_DTOTCMP_Msk (0x4UL) /*!< IOM0 DMATRIGSTAT: DTOTCMP (Bitfield-Mask: 0x01) */ +#define IOM0_DMATRIGSTAT_DTHR_Pos (1UL) /*!< IOM0 DMATRIGSTAT: DTHR (Bit 1) */ +#define IOM0_DMATRIGSTAT_DTHR_Msk (0x2UL) /*!< IOM0 DMATRIGSTAT: DTHR (Bitfield-Mask: 0x01) */ +#define IOM0_DMATRIGSTAT_DCMDCMP_Pos (0UL) /*!< IOM0 DMATRIGSTAT: DCMDCMP (Bit 0) */ +#define IOM0_DMATRIGSTAT_DCMDCMP_Msk (0x1UL) /*!< IOM0 DMATRIGSTAT: DCMDCMP (Bitfield-Mask: 0x01) */ +/* ======================================================== DMACFG ========================================================= */ +#define IOM0_DMACFG_DPWROFF_Pos (9UL) /*!< IOM0 DMACFG: DPWROFF (Bit 9) */ +#define IOM0_DMACFG_DPWROFF_Msk (0x200UL) /*!< IOM0 DMACFG: DPWROFF (Bitfield-Mask: 0x01) */ +#define IOM0_DMACFG_DMAPRI_Pos (8UL) /*!< IOM0 DMACFG: DMAPRI (Bit 8) */ +#define IOM0_DMACFG_DMAPRI_Msk (0x100UL) /*!< IOM0 DMACFG: DMAPRI (Bitfield-Mask: 0x01) */ +#define IOM0_DMACFG_DMADIR_Pos (1UL) /*!< IOM0 DMACFG: DMADIR (Bit 1) */ +#define IOM0_DMACFG_DMADIR_Msk (0x2UL) /*!< IOM0 DMACFG: DMADIR (Bitfield-Mask: 0x01) */ +#define IOM0_DMACFG_DMAEN_Pos (0UL) /*!< IOM0 DMACFG: DMAEN (Bit 0) */ +#define IOM0_DMACFG_DMAEN_Msk (0x1UL) /*!< IOM0 DMACFG: DMAEN (Bitfield-Mask: 0x01) */ +/* ====================================================== DMATOTCOUNT ====================================================== */ +#define IOM0_DMATOTCOUNT_TOTCOUNT_Pos (0UL) /*!< IOM0 DMATOTCOUNT: TOTCOUNT (Bit 0) */ +#define IOM0_DMATOTCOUNT_TOTCOUNT_Msk (0xfffUL) /*!< IOM0 DMATOTCOUNT: TOTCOUNT (Bitfield-Mask: 0xfff) */ +/* ====================================================== DMATARGADDR ====================================================== */ +#define IOM0_DMATARGADDR_TARGADDR28_Pos (28UL) /*!< IOM0 DMATARGADDR: TARGADDR28 (Bit 28) */ +#define IOM0_DMATARGADDR_TARGADDR28_Msk (0x10000000UL) /*!< IOM0 DMATARGADDR: TARGADDR28 (Bitfield-Mask: 0x01) */ +#define IOM0_DMATARGADDR_TARGADDR_Pos (0UL) /*!< IOM0 DMATARGADDR: TARGADDR (Bit 0) */ +#define IOM0_DMATARGADDR_TARGADDR_Msk (0xfffffUL) /*!< IOM0 DMATARGADDR: TARGADDR (Bitfield-Mask: 0xfffff) */ +/* ======================================================== DMASTAT ======================================================== */ +#define IOM0_DMASTAT_DMAERR_Pos (2UL) /*!< IOM0 DMASTAT: DMAERR (Bit 2) */ +#define IOM0_DMASTAT_DMAERR_Msk (0x4UL) /*!< IOM0 DMASTAT: DMAERR (Bitfield-Mask: 0x01) */ +#define IOM0_DMASTAT_DMACPL_Pos (1UL) /*!< IOM0 DMASTAT: DMACPL (Bit 1) */ +#define IOM0_DMASTAT_DMACPL_Msk (0x2UL) /*!< IOM0 DMASTAT: DMACPL (Bitfield-Mask: 0x01) */ +#define IOM0_DMASTAT_DMATIP_Pos (0UL) /*!< IOM0 DMASTAT: DMATIP (Bit 0) */ +#define IOM0_DMASTAT_DMATIP_Msk (0x1UL) /*!< IOM0 DMASTAT: DMATIP (Bitfield-Mask: 0x01) */ +/* ========================================================= CQCFG ========================================================= */ +#define IOM0_CQCFG_CQPRI_Pos (1UL) /*!< IOM0 CQCFG: CQPRI (Bit 1) */ +#define IOM0_CQCFG_CQPRI_Msk (0x2UL) /*!< IOM0 CQCFG: CQPRI (Bitfield-Mask: 0x01) */ +#define IOM0_CQCFG_CQEN_Pos (0UL) /*!< IOM0 CQCFG: CQEN (Bit 0) */ +#define IOM0_CQCFG_CQEN_Msk (0x1UL) /*!< IOM0 CQCFG: CQEN (Bitfield-Mask: 0x01) */ +/* ======================================================== CQADDR ========================================================= */ +#define IOM0_CQADDR_CQADDR28_Pos (28UL) /*!< IOM0 CQADDR: CQADDR28 (Bit 28) */ +#define IOM0_CQADDR_CQADDR28_Msk (0x10000000UL) /*!< IOM0 CQADDR: CQADDR28 (Bitfield-Mask: 0x01) */ +#define IOM0_CQADDR_CQADDR_Pos (2UL) /*!< IOM0 CQADDR: CQADDR (Bit 2) */ +#define IOM0_CQADDR_CQADDR_Msk (0xffffcUL) /*!< IOM0 CQADDR: CQADDR (Bitfield-Mask: 0x3ffff) */ +/* ======================================================== CQSTAT ========================================================= */ +#define IOM0_CQSTAT_CQERR_Pos (2UL) /*!< IOM0 CQSTAT: CQERR (Bit 2) */ +#define IOM0_CQSTAT_CQERR_Msk (0x4UL) /*!< IOM0 CQSTAT: CQERR (Bitfield-Mask: 0x01) */ +#define IOM0_CQSTAT_CQPAUSED_Pos (1UL) /*!< IOM0 CQSTAT: CQPAUSED (Bit 1) */ +#define IOM0_CQSTAT_CQPAUSED_Msk (0x2UL) /*!< IOM0 CQSTAT: CQPAUSED (Bitfield-Mask: 0x01) */ +#define IOM0_CQSTAT_CQTIP_Pos (0UL) /*!< IOM0 CQSTAT: CQTIP (Bit 0) */ +#define IOM0_CQSTAT_CQTIP_Msk (0x1UL) /*!< IOM0 CQSTAT: CQTIP (Bitfield-Mask: 0x01) */ +/* ======================================================== CQFLAGS ======================================================== */ +#define IOM0_CQFLAGS_CQIRQMASK_Pos (16UL) /*!< IOM0 CQFLAGS: CQIRQMASK (Bit 16) */ +#define IOM0_CQFLAGS_CQIRQMASK_Msk (0xffff0000UL) /*!< IOM0 CQFLAGS: CQIRQMASK (Bitfield-Mask: 0xffff) */ +#define IOM0_CQFLAGS_CQFLAGS_Pos (0UL) /*!< IOM0 CQFLAGS: CQFLAGS (Bit 0) */ +#define IOM0_CQFLAGS_CQFLAGS_Msk (0xffffUL) /*!< IOM0 CQFLAGS: CQFLAGS (Bitfield-Mask: 0xffff) */ +/* ====================================================== CQSETCLEAR ======================================================= */ +#define IOM0_CQSETCLEAR_CQFCLR_Pos (16UL) /*!< IOM0 CQSETCLEAR: CQFCLR (Bit 16) */ +#define IOM0_CQSETCLEAR_CQFCLR_Msk (0xff0000UL) /*!< IOM0 CQSETCLEAR: CQFCLR (Bitfield-Mask: 0xff) */ +#define IOM0_CQSETCLEAR_CQFTGL_Pos (8UL) /*!< IOM0 CQSETCLEAR: CQFTGL (Bit 8) */ +#define IOM0_CQSETCLEAR_CQFTGL_Msk (0xff00UL) /*!< IOM0 CQSETCLEAR: CQFTGL (Bitfield-Mask: 0xff) */ +#define IOM0_CQSETCLEAR_CQFSET_Pos (0UL) /*!< IOM0 CQSETCLEAR: CQFSET (Bit 0) */ +#define IOM0_CQSETCLEAR_CQFSET_Msk (0xffUL) /*!< IOM0 CQSETCLEAR: CQFSET (Bitfield-Mask: 0xff) */ +/* ======================================================= CQPAUSEEN ======================================================= */ +#define IOM0_CQPAUSEEN_CQPEN_Pos (0UL) /*!< IOM0 CQPAUSEEN: CQPEN (Bit 0) */ +#define IOM0_CQPAUSEEN_CQPEN_Msk (0xffffUL) /*!< IOM0 CQPAUSEEN: CQPEN (Bitfield-Mask: 0xffff) */ +/* ======================================================= CQCURIDX ======================================================== */ +#define IOM0_CQCURIDX_CQCURIDX_Pos (0UL) /*!< IOM0 CQCURIDX: CQCURIDX (Bit 0) */ +#define IOM0_CQCURIDX_CQCURIDX_Msk (0xffUL) /*!< IOM0 CQCURIDX: CQCURIDX (Bitfield-Mask: 0xff) */ +/* ======================================================= CQENDIDX ======================================================== */ +#define IOM0_CQENDIDX_CQENDIDX_Pos (0UL) /*!< IOM0 CQENDIDX: CQENDIDX (Bit 0) */ +#define IOM0_CQENDIDX_CQENDIDX_Msk (0xffUL) /*!< IOM0 CQENDIDX: CQENDIDX (Bitfield-Mask: 0xff) */ +/* ======================================================== STATUS ========================================================= */ +#define IOM0_STATUS_IDLEST_Pos (2UL) /*!< IOM0 STATUS: IDLEST (Bit 2) */ +#define IOM0_STATUS_IDLEST_Msk (0x4UL) /*!< IOM0 STATUS: IDLEST (Bitfield-Mask: 0x01) */ +#define IOM0_STATUS_CMDACT_Pos (1UL) /*!< IOM0 STATUS: CMDACT (Bit 1) */ +#define IOM0_STATUS_CMDACT_Msk (0x2UL) /*!< IOM0 STATUS: CMDACT (Bitfield-Mask: 0x01) */ +#define IOM0_STATUS_ERR_Pos (0UL) /*!< IOM0 STATUS: ERR (Bit 0) */ +#define IOM0_STATUS_ERR_Msk (0x1UL) /*!< IOM0 STATUS: ERR (Bitfield-Mask: 0x01) */ +/* ======================================================== MSPICFG ======================================================== */ +#define IOM0_MSPICFG_MSPIRST_Pos (30UL) /*!< IOM0 MSPICFG: MSPIRST (Bit 30) */ +#define IOM0_MSPICFG_MSPIRST_Msk (0x40000000UL) /*!< IOM0 MSPICFG: MSPIRST (Bitfield-Mask: 0x01) */ +#define IOM0_MSPICFG_DOUTDLY_Pos (27UL) /*!< IOM0 MSPICFG: DOUTDLY (Bit 27) */ +#define IOM0_MSPICFG_DOUTDLY_Msk (0x38000000UL) /*!< IOM0 MSPICFG: DOUTDLY (Bitfield-Mask: 0x07) */ +#define IOM0_MSPICFG_DINDLY_Pos (24UL) /*!< IOM0 MSPICFG: DINDLY (Bit 24) */ +#define IOM0_MSPICFG_DINDLY_Msk (0x7000000UL) /*!< IOM0 MSPICFG: DINDLY (Bitfield-Mask: 0x07) */ +#define IOM0_MSPICFG_SPILSB_Pos (23UL) /*!< IOM0 MSPICFG: SPILSB (Bit 23) */ +#define IOM0_MSPICFG_SPILSB_Msk (0x800000UL) /*!< IOM0 MSPICFG: SPILSB (Bitfield-Mask: 0x01) */ +#define IOM0_MSPICFG_RDFCPOL_Pos (22UL) /*!< IOM0 MSPICFG: RDFCPOL (Bit 22) */ +#define IOM0_MSPICFG_RDFCPOL_Msk (0x400000UL) /*!< IOM0 MSPICFG: RDFCPOL (Bitfield-Mask: 0x01) */ +#define IOM0_MSPICFG_WTFCPOL_Pos (21UL) /*!< IOM0 MSPICFG: WTFCPOL (Bit 21) */ +#define IOM0_MSPICFG_WTFCPOL_Msk (0x200000UL) /*!< IOM0 MSPICFG: WTFCPOL (Bitfield-Mask: 0x01) */ +#define IOM0_MSPICFG_WTFCIRQ_Pos (20UL) /*!< IOM0 MSPICFG: WTFCIRQ (Bit 20) */ +#define IOM0_MSPICFG_WTFCIRQ_Msk (0x100000UL) /*!< IOM0 MSPICFG: WTFCIRQ (Bitfield-Mask: 0x01) */ +#define IOM0_MSPICFG_MOSIINV_Pos (18UL) /*!< IOM0 MSPICFG: MOSIINV (Bit 18) */ +#define IOM0_MSPICFG_MOSIINV_Msk (0x40000UL) /*!< IOM0 MSPICFG: MOSIINV (Bitfield-Mask: 0x01) */ +#define IOM0_MSPICFG_RDFC_Pos (17UL) /*!< IOM0 MSPICFG: RDFC (Bit 17) */ +#define IOM0_MSPICFG_RDFC_Msk (0x20000UL) /*!< IOM0 MSPICFG: RDFC (Bitfield-Mask: 0x01) */ +#define IOM0_MSPICFG_WTFC_Pos (16UL) /*!< IOM0 MSPICFG: WTFC (Bit 16) */ +#define IOM0_MSPICFG_WTFC_Msk (0x10000UL) /*!< IOM0 MSPICFG: WTFC (Bitfield-Mask: 0x01) */ +#define IOM0_MSPICFG_FULLDUP_Pos (2UL) /*!< IOM0 MSPICFG: FULLDUP (Bit 2) */ +#define IOM0_MSPICFG_FULLDUP_Msk (0x4UL) /*!< IOM0 MSPICFG: FULLDUP (Bitfield-Mask: 0x01) */ +#define IOM0_MSPICFG_SPHA_Pos (1UL) /*!< IOM0 MSPICFG: SPHA (Bit 1) */ +#define IOM0_MSPICFG_SPHA_Msk (0x2UL) /*!< IOM0 MSPICFG: SPHA (Bitfield-Mask: 0x01) */ +#define IOM0_MSPICFG_SPOL_Pos (0UL) /*!< IOM0 MSPICFG: SPOL (Bit 0) */ +#define IOM0_MSPICFG_SPOL_Msk (0x1UL) /*!< IOM0 MSPICFG: SPOL (Bitfield-Mask: 0x01) */ +/* ======================================================== MI2CCFG ======================================================== */ +#define IOM0_MI2CCFG_STRDIS_Pos (24UL) /*!< IOM0 MI2CCFG: STRDIS (Bit 24) */ +#define IOM0_MI2CCFG_STRDIS_Msk (0x1000000UL) /*!< IOM0 MI2CCFG: STRDIS (Bitfield-Mask: 0x01) */ +#define IOM0_MI2CCFG_SMPCNT_Pos (16UL) /*!< IOM0 MI2CCFG: SMPCNT (Bit 16) */ +#define IOM0_MI2CCFG_SMPCNT_Msk (0xff0000UL) /*!< IOM0 MI2CCFG: SMPCNT (Bitfield-Mask: 0xff) */ +#define IOM0_MI2CCFG_SDAENDLY_Pos (12UL) /*!< IOM0 MI2CCFG: SDAENDLY (Bit 12) */ +#define IOM0_MI2CCFG_SDAENDLY_Msk (0xf000UL) /*!< IOM0 MI2CCFG: SDAENDLY (Bitfield-Mask: 0x0f) */ +#define IOM0_MI2CCFG_SCLENDLY_Pos (8UL) /*!< IOM0 MI2CCFG: SCLENDLY (Bit 8) */ +#define IOM0_MI2CCFG_SCLENDLY_Msk (0xf00UL) /*!< IOM0 MI2CCFG: SCLENDLY (Bitfield-Mask: 0x0f) */ +#define IOM0_MI2CCFG_MI2CRST_Pos (6UL) /*!< IOM0 MI2CCFG: MI2CRST (Bit 6) */ +#define IOM0_MI2CCFG_MI2CRST_Msk (0x40UL) /*!< IOM0 MI2CCFG: MI2CRST (Bitfield-Mask: 0x01) */ +#define IOM0_MI2CCFG_SDADLY_Pos (4UL) /*!< IOM0 MI2CCFG: SDADLY (Bit 4) */ +#define IOM0_MI2CCFG_SDADLY_Msk (0x30UL) /*!< IOM0 MI2CCFG: SDADLY (Bitfield-Mask: 0x03) */ +#define IOM0_MI2CCFG_ARBEN_Pos (2UL) /*!< IOM0 MI2CCFG: ARBEN (Bit 2) */ +#define IOM0_MI2CCFG_ARBEN_Msk (0x4UL) /*!< IOM0 MI2CCFG: ARBEN (Bitfield-Mask: 0x01) */ +#define IOM0_MI2CCFG_I2CLSB_Pos (1UL) /*!< IOM0 MI2CCFG: I2CLSB (Bit 1) */ +#define IOM0_MI2CCFG_I2CLSB_Msk (0x2UL) /*!< IOM0 MI2CCFG: I2CLSB (Bitfield-Mask: 0x01) */ +#define IOM0_MI2CCFG_ADDRSZ_Pos (0UL) /*!< IOM0 MI2CCFG: ADDRSZ (Bit 0) */ +#define IOM0_MI2CCFG_ADDRSZ_Msk (0x1UL) /*!< IOM0 MI2CCFG: ADDRSZ (Bitfield-Mask: 0x01) */ +/* ======================================================== DEVCFG ========================================================= */ +#define IOM0_DEVCFG_DEVADDR_Pos (0UL) /*!< IOM0 DEVCFG: DEVADDR (Bit 0) */ +#define IOM0_DEVCFG_DEVADDR_Msk (0x3ffUL) /*!< IOM0 DEVCFG: DEVADDR (Bitfield-Mask: 0x3ff) */ +/* ======================================================== IOMDBG ========================================================= */ +#define IOM0_IOMDBG_DBGDATA_Pos (3UL) /*!< IOM0 IOMDBG: DBGDATA (Bit 3) */ +#define IOM0_IOMDBG_DBGDATA_Msk (0xfffffff8UL) /*!< IOM0 IOMDBG: DBGDATA (Bitfield-Mask: 0x1fffffff) */ +#define IOM0_IOMDBG_APBCLKON_Pos (2UL) /*!< IOM0 IOMDBG: APBCLKON (Bit 2) */ +#define IOM0_IOMDBG_APBCLKON_Msk (0x4UL) /*!< IOM0 IOMDBG: APBCLKON (Bitfield-Mask: 0x01) */ +#define IOM0_IOMDBG_IOCLKON_Pos (1UL) /*!< IOM0 IOMDBG: IOCLKON (Bit 1) */ +#define IOM0_IOMDBG_IOCLKON_Msk (0x2UL) /*!< IOM0 IOMDBG: IOCLKON (Bitfield-Mask: 0x01) */ +#define IOM0_IOMDBG_DBGEN_Pos (0UL) /*!< IOM0 IOMDBG: DBGEN (Bit 0) */ +#define IOM0_IOMDBG_DBGEN_Msk (0x1UL) /*!< IOM0 IOMDBG: DBGEN (Bitfield-Mask: 0x01) */ + + +/* =========================================================================================================================== */ +/* ================ IOSLAVE ================ */ +/* =========================================================================================================================== */ + +/* ======================================================== FIFOPTR ======================================================== */ +#define IOSLAVE_FIFOPTR_FIFOSIZ_Pos (8UL) /*!< IOSLAVE FIFOPTR: FIFOSIZ (Bit 8) */ +#define IOSLAVE_FIFOPTR_FIFOSIZ_Msk (0xff00UL) /*!< IOSLAVE FIFOPTR: FIFOSIZ (Bitfield-Mask: 0xff) */ +#define IOSLAVE_FIFOPTR_FIFOPTR_Pos (0UL) /*!< IOSLAVE FIFOPTR: FIFOPTR (Bit 0) */ +#define IOSLAVE_FIFOPTR_FIFOPTR_Msk (0xffUL) /*!< IOSLAVE FIFOPTR: FIFOPTR (Bitfield-Mask: 0xff) */ +/* ======================================================== FIFOCFG ======================================================== */ +#define IOSLAVE_FIFOCFG_ROBASE_Pos (24UL) /*!< IOSLAVE FIFOCFG: ROBASE (Bit 24) */ +#define IOSLAVE_FIFOCFG_ROBASE_Msk (0x3f000000UL) /*!< IOSLAVE FIFOCFG: ROBASE (Bitfield-Mask: 0x3f) */ +#define IOSLAVE_FIFOCFG_FIFOMAX_Pos (8UL) /*!< IOSLAVE FIFOCFG: FIFOMAX (Bit 8) */ +#define IOSLAVE_FIFOCFG_FIFOMAX_Msk (0x3f00UL) /*!< IOSLAVE FIFOCFG: FIFOMAX (Bitfield-Mask: 0x3f) */ +#define IOSLAVE_FIFOCFG_FIFOBASE_Pos (0UL) /*!< IOSLAVE FIFOCFG: FIFOBASE (Bit 0) */ +#define IOSLAVE_FIFOCFG_FIFOBASE_Msk (0x1fUL) /*!< IOSLAVE FIFOCFG: FIFOBASE (Bitfield-Mask: 0x1f) */ +/* ======================================================== FIFOTHR ======================================================== */ +#define IOSLAVE_FIFOTHR_FIFOTHR_Pos (0UL) /*!< IOSLAVE FIFOTHR: FIFOTHR (Bit 0) */ +#define IOSLAVE_FIFOTHR_FIFOTHR_Msk (0xffUL) /*!< IOSLAVE FIFOTHR: FIFOTHR (Bitfield-Mask: 0xff) */ +/* ========================================================= FUPD ========================================================== */ +#define IOSLAVE_FUPD_IOREAD_Pos (1UL) /*!< IOSLAVE FUPD: IOREAD (Bit 1) */ +#define IOSLAVE_FUPD_IOREAD_Msk (0x2UL) /*!< IOSLAVE FUPD: IOREAD (Bitfield-Mask: 0x01) */ +#define IOSLAVE_FUPD_FIFOUPD_Pos (0UL) /*!< IOSLAVE FUPD: FIFOUPD (Bit 0) */ +#define IOSLAVE_FUPD_FIFOUPD_Msk (0x1UL) /*!< IOSLAVE FUPD: FIFOUPD (Bitfield-Mask: 0x01) */ +/* ======================================================== FIFOCTR ======================================================== */ +#define IOSLAVE_FIFOCTR_FIFOCTR_Pos (0UL) /*!< IOSLAVE FIFOCTR: FIFOCTR (Bit 0) */ +#define IOSLAVE_FIFOCTR_FIFOCTR_Msk (0x3ffUL) /*!< IOSLAVE FIFOCTR: FIFOCTR (Bitfield-Mask: 0x3ff) */ +/* ======================================================== FIFOINC ======================================================== */ +#define IOSLAVE_FIFOINC_FIFOINC_Pos (0UL) /*!< IOSLAVE FIFOINC: FIFOINC (Bit 0) */ +#define IOSLAVE_FIFOINC_FIFOINC_Msk (0x3ffUL) /*!< IOSLAVE FIFOINC: FIFOINC (Bitfield-Mask: 0x3ff) */ +/* ========================================================== CFG ========================================================== */ +#define IOSLAVE_CFG_IFCEN_Pos (31UL) /*!< IOSLAVE CFG: IFCEN (Bit 31) */ +#define IOSLAVE_CFG_IFCEN_Msk (0x80000000UL) /*!< IOSLAVE CFG: IFCEN (Bitfield-Mask: 0x01) */ +#define IOSLAVE_CFG_I2CADDR_Pos (8UL) /*!< IOSLAVE CFG: I2CADDR (Bit 8) */ +#define IOSLAVE_CFG_I2CADDR_Msk (0xfff00UL) /*!< IOSLAVE CFG: I2CADDR (Bitfield-Mask: 0xfff) */ +#define IOSLAVE_CFG_STARTRD_Pos (4UL) /*!< IOSLAVE CFG: STARTRD (Bit 4) */ +#define IOSLAVE_CFG_STARTRD_Msk (0x10UL) /*!< IOSLAVE CFG: STARTRD (Bitfield-Mask: 0x01) */ +#define IOSLAVE_CFG_LSB_Pos (2UL) /*!< IOSLAVE CFG: LSB (Bit 2) */ +#define IOSLAVE_CFG_LSB_Msk (0x4UL) /*!< IOSLAVE CFG: LSB (Bitfield-Mask: 0x01) */ +#define IOSLAVE_CFG_SPOL_Pos (1UL) /*!< IOSLAVE CFG: SPOL (Bit 1) */ +#define IOSLAVE_CFG_SPOL_Msk (0x2UL) /*!< IOSLAVE CFG: SPOL (Bitfield-Mask: 0x01) */ +#define IOSLAVE_CFG_IFCSEL_Pos (0UL) /*!< IOSLAVE CFG: IFCSEL (Bit 0) */ +#define IOSLAVE_CFG_IFCSEL_Msk (0x1UL) /*!< IOSLAVE CFG: IFCSEL (Bitfield-Mask: 0x01) */ +/* ========================================================= PRENC ========================================================= */ +#define IOSLAVE_PRENC_PRENC_Pos (0UL) /*!< IOSLAVE PRENC: PRENC (Bit 0) */ +#define IOSLAVE_PRENC_PRENC_Msk (0x1fUL) /*!< IOSLAVE PRENC: PRENC (Bitfield-Mask: 0x1f) */ +/* ======================================================= IOINTCTL ======================================================== */ +#define IOSLAVE_IOINTCTL_IOINTSET_Pos (24UL) /*!< IOSLAVE IOINTCTL: IOINTSET (Bit 24) */ +#define IOSLAVE_IOINTCTL_IOINTSET_Msk (0xff000000UL) /*!< IOSLAVE IOINTCTL: IOINTSET (Bitfield-Mask: 0xff) */ +#define IOSLAVE_IOINTCTL_IOINTCLR_Pos (16UL) /*!< IOSLAVE IOINTCTL: IOINTCLR (Bit 16) */ +#define IOSLAVE_IOINTCTL_IOINTCLR_Msk (0x10000UL) /*!< IOSLAVE IOINTCTL: IOINTCLR (Bitfield-Mask: 0x01) */ +#define IOSLAVE_IOINTCTL_IOINT_Pos (8UL) /*!< IOSLAVE IOINTCTL: IOINT (Bit 8) */ +#define IOSLAVE_IOINTCTL_IOINT_Msk (0xff00UL) /*!< IOSLAVE IOINTCTL: IOINT (Bitfield-Mask: 0xff) */ +#define IOSLAVE_IOINTCTL_IOINTEN_Pos (0UL) /*!< IOSLAVE IOINTCTL: IOINTEN (Bit 0) */ +#define IOSLAVE_IOINTCTL_IOINTEN_Msk (0xffUL) /*!< IOSLAVE IOINTCTL: IOINTEN (Bitfield-Mask: 0xff) */ +/* ======================================================== GENADD ========================================================= */ +#define IOSLAVE_GENADD_GADATA_Pos (0UL) /*!< IOSLAVE GENADD: GADATA (Bit 0) */ +#define IOSLAVE_GENADD_GADATA_Msk (0xffUL) /*!< IOSLAVE GENADD: GADATA (Bitfield-Mask: 0xff) */ +/* ========================================================= INTEN ========================================================= */ +#define IOSLAVE_INTEN_XCMPWR_Pos (9UL) /*!< IOSLAVE INTEN: XCMPWR (Bit 9) */ +#define IOSLAVE_INTEN_XCMPWR_Msk (0x200UL) /*!< IOSLAVE INTEN: XCMPWR (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTEN_XCMPWF_Pos (8UL) /*!< IOSLAVE INTEN: XCMPWF (Bit 8) */ +#define IOSLAVE_INTEN_XCMPWF_Msk (0x100UL) /*!< IOSLAVE INTEN: XCMPWF (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTEN_XCMPRR_Pos (7UL) /*!< IOSLAVE INTEN: XCMPRR (Bit 7) */ +#define IOSLAVE_INTEN_XCMPRR_Msk (0x80UL) /*!< IOSLAVE INTEN: XCMPRR (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTEN_XCMPRF_Pos (6UL) /*!< IOSLAVE INTEN: XCMPRF (Bit 6) */ +#define IOSLAVE_INTEN_XCMPRF_Msk (0x40UL) /*!< IOSLAVE INTEN: XCMPRF (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTEN_IOINTW_Pos (5UL) /*!< IOSLAVE INTEN: IOINTW (Bit 5) */ +#define IOSLAVE_INTEN_IOINTW_Msk (0x20UL) /*!< IOSLAVE INTEN: IOINTW (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTEN_GENAD_Pos (4UL) /*!< IOSLAVE INTEN: GENAD (Bit 4) */ +#define IOSLAVE_INTEN_GENAD_Msk (0x10UL) /*!< IOSLAVE INTEN: GENAD (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTEN_FRDERR_Pos (3UL) /*!< IOSLAVE INTEN: FRDERR (Bit 3) */ +#define IOSLAVE_INTEN_FRDERR_Msk (0x8UL) /*!< IOSLAVE INTEN: FRDERR (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTEN_FUNDFL_Pos (2UL) /*!< IOSLAVE INTEN: FUNDFL (Bit 2) */ +#define IOSLAVE_INTEN_FUNDFL_Msk (0x4UL) /*!< IOSLAVE INTEN: FUNDFL (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTEN_FOVFL_Pos (1UL) /*!< IOSLAVE INTEN: FOVFL (Bit 1) */ +#define IOSLAVE_INTEN_FOVFL_Msk (0x2UL) /*!< IOSLAVE INTEN: FOVFL (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTEN_FSIZE_Pos (0UL) /*!< IOSLAVE INTEN: FSIZE (Bit 0) */ +#define IOSLAVE_INTEN_FSIZE_Msk (0x1UL) /*!< IOSLAVE INTEN: FSIZE (Bitfield-Mask: 0x01) */ +/* ======================================================== INTSTAT ======================================================== */ +#define IOSLAVE_INTSTAT_XCMPWR_Pos (9UL) /*!< IOSLAVE INTSTAT: XCMPWR (Bit 9) */ +#define IOSLAVE_INTSTAT_XCMPWR_Msk (0x200UL) /*!< IOSLAVE INTSTAT: XCMPWR (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTSTAT_XCMPWF_Pos (8UL) /*!< IOSLAVE INTSTAT: XCMPWF (Bit 8) */ +#define IOSLAVE_INTSTAT_XCMPWF_Msk (0x100UL) /*!< IOSLAVE INTSTAT: XCMPWF (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTSTAT_XCMPRR_Pos (7UL) /*!< IOSLAVE INTSTAT: XCMPRR (Bit 7) */ +#define IOSLAVE_INTSTAT_XCMPRR_Msk (0x80UL) /*!< IOSLAVE INTSTAT: XCMPRR (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTSTAT_XCMPRF_Pos (6UL) /*!< IOSLAVE INTSTAT: XCMPRF (Bit 6) */ +#define IOSLAVE_INTSTAT_XCMPRF_Msk (0x40UL) /*!< IOSLAVE INTSTAT: XCMPRF (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTSTAT_IOINTW_Pos (5UL) /*!< IOSLAVE INTSTAT: IOINTW (Bit 5) */ +#define IOSLAVE_INTSTAT_IOINTW_Msk (0x20UL) /*!< IOSLAVE INTSTAT: IOINTW (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTSTAT_GENAD_Pos (4UL) /*!< IOSLAVE INTSTAT: GENAD (Bit 4) */ +#define IOSLAVE_INTSTAT_GENAD_Msk (0x10UL) /*!< IOSLAVE INTSTAT: GENAD (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTSTAT_FRDERR_Pos (3UL) /*!< IOSLAVE INTSTAT: FRDERR (Bit 3) */ +#define IOSLAVE_INTSTAT_FRDERR_Msk (0x8UL) /*!< IOSLAVE INTSTAT: FRDERR (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTSTAT_FUNDFL_Pos (2UL) /*!< IOSLAVE INTSTAT: FUNDFL (Bit 2) */ +#define IOSLAVE_INTSTAT_FUNDFL_Msk (0x4UL) /*!< IOSLAVE INTSTAT: FUNDFL (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTSTAT_FOVFL_Pos (1UL) /*!< IOSLAVE INTSTAT: FOVFL (Bit 1) */ +#define IOSLAVE_INTSTAT_FOVFL_Msk (0x2UL) /*!< IOSLAVE INTSTAT: FOVFL (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTSTAT_FSIZE_Pos (0UL) /*!< IOSLAVE INTSTAT: FSIZE (Bit 0) */ +#define IOSLAVE_INTSTAT_FSIZE_Msk (0x1UL) /*!< IOSLAVE INTSTAT: FSIZE (Bitfield-Mask: 0x01) */ +/* ======================================================== INTCLR ========================================================= */ +#define IOSLAVE_INTCLR_XCMPWR_Pos (9UL) /*!< IOSLAVE INTCLR: XCMPWR (Bit 9) */ +#define IOSLAVE_INTCLR_XCMPWR_Msk (0x200UL) /*!< IOSLAVE INTCLR: XCMPWR (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTCLR_XCMPWF_Pos (8UL) /*!< IOSLAVE INTCLR: XCMPWF (Bit 8) */ +#define IOSLAVE_INTCLR_XCMPWF_Msk (0x100UL) /*!< IOSLAVE INTCLR: XCMPWF (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTCLR_XCMPRR_Pos (7UL) /*!< IOSLAVE INTCLR: XCMPRR (Bit 7) */ +#define IOSLAVE_INTCLR_XCMPRR_Msk (0x80UL) /*!< IOSLAVE INTCLR: XCMPRR (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTCLR_XCMPRF_Pos (6UL) /*!< IOSLAVE INTCLR: XCMPRF (Bit 6) */ +#define IOSLAVE_INTCLR_XCMPRF_Msk (0x40UL) /*!< IOSLAVE INTCLR: XCMPRF (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTCLR_IOINTW_Pos (5UL) /*!< IOSLAVE INTCLR: IOINTW (Bit 5) */ +#define IOSLAVE_INTCLR_IOINTW_Msk (0x20UL) /*!< IOSLAVE INTCLR: IOINTW (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTCLR_GENAD_Pos (4UL) /*!< IOSLAVE INTCLR: GENAD (Bit 4) */ +#define IOSLAVE_INTCLR_GENAD_Msk (0x10UL) /*!< IOSLAVE INTCLR: GENAD (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTCLR_FRDERR_Pos (3UL) /*!< IOSLAVE INTCLR: FRDERR (Bit 3) */ +#define IOSLAVE_INTCLR_FRDERR_Msk (0x8UL) /*!< IOSLAVE INTCLR: FRDERR (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTCLR_FUNDFL_Pos (2UL) /*!< IOSLAVE INTCLR: FUNDFL (Bit 2) */ +#define IOSLAVE_INTCLR_FUNDFL_Msk (0x4UL) /*!< IOSLAVE INTCLR: FUNDFL (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTCLR_FOVFL_Pos (1UL) /*!< IOSLAVE INTCLR: FOVFL (Bit 1) */ +#define IOSLAVE_INTCLR_FOVFL_Msk (0x2UL) /*!< IOSLAVE INTCLR: FOVFL (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTCLR_FSIZE_Pos (0UL) /*!< IOSLAVE INTCLR: FSIZE (Bit 0) */ +#define IOSLAVE_INTCLR_FSIZE_Msk (0x1UL) /*!< IOSLAVE INTCLR: FSIZE (Bitfield-Mask: 0x01) */ +/* ======================================================== INTSET ========================================================= */ +#define IOSLAVE_INTSET_XCMPWR_Pos (9UL) /*!< IOSLAVE INTSET: XCMPWR (Bit 9) */ +#define IOSLAVE_INTSET_XCMPWR_Msk (0x200UL) /*!< IOSLAVE INTSET: XCMPWR (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTSET_XCMPWF_Pos (8UL) /*!< IOSLAVE INTSET: XCMPWF (Bit 8) */ +#define IOSLAVE_INTSET_XCMPWF_Msk (0x100UL) /*!< IOSLAVE INTSET: XCMPWF (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTSET_XCMPRR_Pos (7UL) /*!< IOSLAVE INTSET: XCMPRR (Bit 7) */ +#define IOSLAVE_INTSET_XCMPRR_Msk (0x80UL) /*!< IOSLAVE INTSET: XCMPRR (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTSET_XCMPRF_Pos (6UL) /*!< IOSLAVE INTSET: XCMPRF (Bit 6) */ +#define IOSLAVE_INTSET_XCMPRF_Msk (0x40UL) /*!< IOSLAVE INTSET: XCMPRF (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTSET_IOINTW_Pos (5UL) /*!< IOSLAVE INTSET: IOINTW (Bit 5) */ +#define IOSLAVE_INTSET_IOINTW_Msk (0x20UL) /*!< IOSLAVE INTSET: IOINTW (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTSET_GENAD_Pos (4UL) /*!< IOSLAVE INTSET: GENAD (Bit 4) */ +#define IOSLAVE_INTSET_GENAD_Msk (0x10UL) /*!< IOSLAVE INTSET: GENAD (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTSET_FRDERR_Pos (3UL) /*!< IOSLAVE INTSET: FRDERR (Bit 3) */ +#define IOSLAVE_INTSET_FRDERR_Msk (0x8UL) /*!< IOSLAVE INTSET: FRDERR (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTSET_FUNDFL_Pos (2UL) /*!< IOSLAVE INTSET: FUNDFL (Bit 2) */ +#define IOSLAVE_INTSET_FUNDFL_Msk (0x4UL) /*!< IOSLAVE INTSET: FUNDFL (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTSET_FOVFL_Pos (1UL) /*!< IOSLAVE INTSET: FOVFL (Bit 1) */ +#define IOSLAVE_INTSET_FOVFL_Msk (0x2UL) /*!< IOSLAVE INTSET: FOVFL (Bitfield-Mask: 0x01) */ +#define IOSLAVE_INTSET_FSIZE_Pos (0UL) /*!< IOSLAVE INTSET: FSIZE (Bit 0) */ +#define IOSLAVE_INTSET_FSIZE_Msk (0x1UL) /*!< IOSLAVE INTSET: FSIZE (Bitfield-Mask: 0x01) */ +/* ====================================================== REGACCINTEN ====================================================== */ +#define IOSLAVE_REGACCINTEN_REGACC_Pos (0UL) /*!< IOSLAVE REGACCINTEN: REGACC (Bit 0) */ +#define IOSLAVE_REGACCINTEN_REGACC_Msk (0xffffffffUL) /*!< IOSLAVE REGACCINTEN: REGACC (Bitfield-Mask: 0xffffffff) */ +/* ===================================================== REGACCINTSTAT ===================================================== */ +#define IOSLAVE_REGACCINTSTAT_REGACC_Pos (0UL) /*!< IOSLAVE REGACCINTSTAT: REGACC (Bit 0) */ +#define IOSLAVE_REGACCINTSTAT_REGACC_Msk (0xffffffffUL) /*!< IOSLAVE REGACCINTSTAT: REGACC (Bitfield-Mask: 0xffffffff) */ +/* ===================================================== REGACCINTCLR ====================================================== */ +#define IOSLAVE_REGACCINTCLR_REGACC_Pos (0UL) /*!< IOSLAVE REGACCINTCLR: REGACC (Bit 0) */ +#define IOSLAVE_REGACCINTCLR_REGACC_Msk (0xffffffffUL) /*!< IOSLAVE REGACCINTCLR: REGACC (Bitfield-Mask: 0xffffffff) */ +/* ===================================================== REGACCINTSET ====================================================== */ +#define IOSLAVE_REGACCINTSET_REGACC_Pos (0UL) /*!< IOSLAVE REGACCINTSET: REGACC (Bit 0) */ +#define IOSLAVE_REGACCINTSET_REGACC_Msk (0xffffffffUL) /*!< IOSLAVE REGACCINTSET: REGACC (Bitfield-Mask: 0xffffffff) */ + + +/* =========================================================================================================================== */ +/* ================ MCUCTRL ================ */ +/* =========================================================================================================================== */ + +/* ======================================================== CHIPPN ========================================================= */ +#define MCUCTRL_CHIPPN_PARTNUM_Pos (0UL) /*!< MCUCTRL CHIPPN: PARTNUM (Bit 0) */ +#define MCUCTRL_CHIPPN_PARTNUM_Msk (0xffffffffUL) /*!< MCUCTRL CHIPPN: PARTNUM (Bitfield-Mask: 0xffffffff) */ +/* ======================================================== CHIPID0 ======================================================== */ +#define MCUCTRL_CHIPID0_CHIPID0_Pos (0UL) /*!< MCUCTRL CHIPID0: CHIPID0 (Bit 0) */ +#define MCUCTRL_CHIPID0_CHIPID0_Msk (0xffffffffUL) /*!< MCUCTRL CHIPID0: CHIPID0 (Bitfield-Mask: 0xffffffff) */ +/* ======================================================== CHIPID1 ======================================================== */ +#define MCUCTRL_CHIPID1_CHIPID1_Pos (0UL) /*!< MCUCTRL CHIPID1: CHIPID1 (Bit 0) */ +#define MCUCTRL_CHIPID1_CHIPID1_Msk (0xffffffffUL) /*!< MCUCTRL CHIPID1: CHIPID1 (Bitfield-Mask: 0xffffffff) */ +/* ======================================================== CHIPREV ======================================================== */ +#define MCUCTRL_CHIPREV_SIPART_Pos (8UL) /*!< MCUCTRL CHIPREV: SIPART (Bit 8) */ +#define MCUCTRL_CHIPREV_SIPART_Msk (0xfff00UL) /*!< MCUCTRL CHIPREV: SIPART (Bitfield-Mask: 0xfff) */ +#define MCUCTRL_CHIPREV_REVMAJ_Pos (4UL) /*!< MCUCTRL CHIPREV: REVMAJ (Bit 4) */ +#define MCUCTRL_CHIPREV_REVMAJ_Msk (0xf0UL) /*!< MCUCTRL CHIPREV: REVMAJ (Bitfield-Mask: 0x0f) */ +#define MCUCTRL_CHIPREV_REVMIN_Pos (0UL) /*!< MCUCTRL CHIPREV: REVMIN (Bit 0) */ +#define MCUCTRL_CHIPREV_REVMIN_Msk (0xfUL) /*!< MCUCTRL CHIPREV: REVMIN (Bitfield-Mask: 0x0f) */ +/* ======================================================= VENDORID ======================================================== */ +#define MCUCTRL_VENDORID_VENDORID_Pos (0UL) /*!< MCUCTRL VENDORID: VENDORID (Bit 0) */ +#define MCUCTRL_VENDORID_VENDORID_Msk (0xffffffffUL) /*!< MCUCTRL VENDORID: VENDORID (Bitfield-Mask: 0xffffffff) */ +/* ========================================================== SKU ========================================================== */ +#define MCUCTRL_SKU_SECBOOT_Pos (2UL) /*!< MCUCTRL SKU: SECBOOT (Bit 2) */ +#define MCUCTRL_SKU_SECBOOT_Msk (0x4UL) /*!< MCUCTRL SKU: SECBOOT (Bitfield-Mask: 0x01) */ +#define MCUCTRL_SKU_ALLOWBLE_Pos (1UL) /*!< MCUCTRL SKU: ALLOWBLE (Bit 1) */ +#define MCUCTRL_SKU_ALLOWBLE_Msk (0x2UL) /*!< MCUCTRL SKU: ALLOWBLE (Bitfield-Mask: 0x01) */ +#define MCUCTRL_SKU_ALLOWBURST_Pos (0UL) /*!< MCUCTRL SKU: ALLOWBURST (Bit 0) */ +#define MCUCTRL_SKU_ALLOWBURST_Msk (0x1UL) /*!< MCUCTRL SKU: ALLOWBURST (Bitfield-Mask: 0x01) */ +/* ===================================================== FEATUREENABLE ===================================================== */ +#define MCUCTRL_FEATUREENABLE_BURSTAVAIL_Pos (6UL) /*!< MCUCTRL FEATUREENABLE: BURSTAVAIL (Bit 6) */ +#define MCUCTRL_FEATUREENABLE_BURSTAVAIL_Msk (0x40UL) /*!< MCUCTRL FEATUREENABLE: BURSTAVAIL (Bitfield-Mask: 0x01) */ +#define MCUCTRL_FEATUREENABLE_BURSTACK_Pos (5UL) /*!< MCUCTRL FEATUREENABLE: BURSTACK (Bit 5) */ +#define MCUCTRL_FEATUREENABLE_BURSTACK_Msk (0x20UL) /*!< MCUCTRL FEATUREENABLE: BURSTACK (Bitfield-Mask: 0x01) */ +#define MCUCTRL_FEATUREENABLE_BURSTREQ_Pos (4UL) /*!< MCUCTRL FEATUREENABLE: BURSTREQ (Bit 4) */ +#define MCUCTRL_FEATUREENABLE_BURSTREQ_Msk (0x10UL) /*!< MCUCTRL FEATUREENABLE: BURSTREQ (Bitfield-Mask: 0x01) */ +#define MCUCTRL_FEATUREENABLE_BLEAVAIL_Pos (2UL) /*!< MCUCTRL FEATUREENABLE: BLEAVAIL (Bit 2) */ +#define MCUCTRL_FEATUREENABLE_BLEAVAIL_Msk (0x4UL) /*!< MCUCTRL FEATUREENABLE: BLEAVAIL (Bitfield-Mask: 0x01) */ +#define MCUCTRL_FEATUREENABLE_BLEACK_Pos (1UL) /*!< MCUCTRL FEATUREENABLE: BLEACK (Bit 1) */ +#define MCUCTRL_FEATUREENABLE_BLEACK_Msk (0x2UL) /*!< MCUCTRL FEATUREENABLE: BLEACK (Bitfield-Mask: 0x01) */ +#define MCUCTRL_FEATUREENABLE_BLEREQ_Pos (0UL) /*!< MCUCTRL FEATUREENABLE: BLEREQ (Bit 0) */ +#define MCUCTRL_FEATUREENABLE_BLEREQ_Msk (0x1UL) /*!< MCUCTRL FEATUREENABLE: BLEREQ (Bitfield-Mask: 0x01) */ +/* ======================================================= DEBUGGER ======================================================== */ +#define MCUCTRL_DEBUGGER_LOCKOUT_Pos (0UL) /*!< MCUCTRL DEBUGGER: LOCKOUT (Bit 0) */ +#define MCUCTRL_DEBUGGER_LOCKOUT_Msk (0x1UL) /*!< MCUCTRL DEBUGGER: LOCKOUT (Bitfield-Mask: 0x01) */ +/* ======================================================== BODCTRL ======================================================== */ +#define MCUCTRL_BODCTRL_BODHVREFSEL_Pos (5UL) /*!< MCUCTRL BODCTRL: BODHVREFSEL (Bit 5) */ +#define MCUCTRL_BODCTRL_BODHVREFSEL_Msk (0x20UL) /*!< MCUCTRL BODCTRL: BODHVREFSEL (Bitfield-Mask: 0x01) */ +#define MCUCTRL_BODCTRL_BODLVREFSEL_Pos (4UL) /*!< MCUCTRL BODCTRL: BODLVREFSEL (Bit 4) */ +#define MCUCTRL_BODCTRL_BODLVREFSEL_Msk (0x10UL) /*!< MCUCTRL BODCTRL: BODLVREFSEL (Bitfield-Mask: 0x01) */ +#define MCUCTRL_BODCTRL_BODFPWD_Pos (3UL) /*!< MCUCTRL BODCTRL: BODFPWD (Bit 3) */ +#define MCUCTRL_BODCTRL_BODFPWD_Msk (0x8UL) /*!< MCUCTRL BODCTRL: BODFPWD (Bitfield-Mask: 0x01) */ +#define MCUCTRL_BODCTRL_BODCPWD_Pos (2UL) /*!< MCUCTRL BODCTRL: BODCPWD (Bit 2) */ +#define MCUCTRL_BODCTRL_BODCPWD_Msk (0x4UL) /*!< MCUCTRL BODCTRL: BODCPWD (Bitfield-Mask: 0x01) */ +#define MCUCTRL_BODCTRL_BODHPWD_Pos (1UL) /*!< MCUCTRL BODCTRL: BODHPWD (Bit 1) */ +#define MCUCTRL_BODCTRL_BODHPWD_Msk (0x2UL) /*!< MCUCTRL BODCTRL: BODHPWD (Bitfield-Mask: 0x01) */ +#define MCUCTRL_BODCTRL_BODLPWD_Pos (0UL) /*!< MCUCTRL BODCTRL: BODLPWD (Bit 0) */ +#define MCUCTRL_BODCTRL_BODLPWD_Msk (0x1UL) /*!< MCUCTRL BODCTRL: BODLPWD (Bitfield-Mask: 0x01) */ +/* ======================================================= ADCPWRDLY ======================================================= */ +#define MCUCTRL_ADCPWRDLY_ADCPWR1_Pos (8UL) /*!< MCUCTRL ADCPWRDLY: ADCPWR1 (Bit 8) */ +#define MCUCTRL_ADCPWRDLY_ADCPWR1_Msk (0xff00UL) /*!< MCUCTRL ADCPWRDLY: ADCPWR1 (Bitfield-Mask: 0xff) */ +#define MCUCTRL_ADCPWRDLY_ADCPWR0_Pos (0UL) /*!< MCUCTRL ADCPWRDLY: ADCPWR0 (Bit 0) */ +#define MCUCTRL_ADCPWRDLY_ADCPWR0_Msk (0xffUL) /*!< MCUCTRL ADCPWRDLY: ADCPWR0 (Bitfield-Mask: 0xff) */ +/* ======================================================== ADCCAL ========================================================= */ +#define MCUCTRL_ADCCAL_ADCCALIBRATED_Pos (1UL) /*!< MCUCTRL ADCCAL: ADCCALIBRATED (Bit 1) */ +#define MCUCTRL_ADCCAL_ADCCALIBRATED_Msk (0x2UL) /*!< MCUCTRL ADCCAL: ADCCALIBRATED (Bitfield-Mask: 0x01) */ +#define MCUCTRL_ADCCAL_CALONPWRUP_Pos (0UL) /*!< MCUCTRL ADCCAL: CALONPWRUP (Bit 0) */ +#define MCUCTRL_ADCCAL_CALONPWRUP_Msk (0x1UL) /*!< MCUCTRL ADCCAL: CALONPWRUP (Bitfield-Mask: 0x01) */ +/* ====================================================== ADCBATTLOAD ====================================================== */ +#define MCUCTRL_ADCBATTLOAD_BATTLOAD_Pos (0UL) /*!< MCUCTRL ADCBATTLOAD: BATTLOAD (Bit 0) */ +#define MCUCTRL_ADCBATTLOAD_BATTLOAD_Msk (0x1UL) /*!< MCUCTRL ADCBATTLOAD: BATTLOAD (Bitfield-Mask: 0x01) */ +/* ======================================================== ADCTRIM ======================================================== */ +#define MCUCTRL_ADCTRIM_ADCRFBUFIBTRIM_Pos (11UL) /*!< MCUCTRL ADCTRIM: ADCRFBUFIBTRIM (Bit 11) */ +#define MCUCTRL_ADCTRIM_ADCRFBUFIBTRIM_Msk (0x1800UL) /*!< MCUCTRL ADCTRIM: ADCRFBUFIBTRIM (Bitfield-Mask: 0x03) */ +#define MCUCTRL_ADCTRIM_ADCREFBUFTRIM_Pos (6UL) /*!< MCUCTRL ADCTRIM: ADCREFBUFTRIM (Bit 6) */ +#define MCUCTRL_ADCTRIM_ADCREFBUFTRIM_Msk (0x7c0UL) /*!< MCUCTRL ADCTRIM: ADCREFBUFTRIM (Bitfield-Mask: 0x1f) */ +#define MCUCTRL_ADCTRIM_ADCREFKEEPIBTRIM_Pos (0UL) /*!< MCUCTRL ADCTRIM: ADCREFKEEPIBTRIM (Bit 0) */ +#define MCUCTRL_ADCTRIM_ADCREFKEEPIBTRIM_Msk (0x3UL) /*!< MCUCTRL ADCTRIM: ADCREFKEEPIBTRIM (Bitfield-Mask: 0x03) */ +/* ====================================================== ADCREFCOMP ======================================================= */ +#define MCUCTRL_ADCREFCOMP_ADCRFCMPEN_Pos (16UL) /*!< MCUCTRL ADCREFCOMP: ADCRFCMPEN (Bit 16) */ +#define MCUCTRL_ADCREFCOMP_ADCRFCMPEN_Msk (0x10000UL) /*!< MCUCTRL ADCREFCOMP: ADCRFCMPEN (Bitfield-Mask: 0x01) */ +#define MCUCTRL_ADCREFCOMP_ADCREFKEEPTRIM_Pos (8UL) /*!< MCUCTRL ADCREFCOMP: ADCREFKEEPTRIM (Bit 8) */ +#define MCUCTRL_ADCREFCOMP_ADCREFKEEPTRIM_Msk (0x1f00UL) /*!< MCUCTRL ADCREFCOMP: ADCREFKEEPTRIM (Bitfield-Mask: 0x1f) */ +#define MCUCTRL_ADCREFCOMP_ADC_REFCOMP_OUT_Pos (0UL) /*!< MCUCTRL ADCREFCOMP: ADC_REFCOMP_OUT (Bit 0) */ +#define MCUCTRL_ADCREFCOMP_ADC_REFCOMP_OUT_Msk (0x1UL) /*!< MCUCTRL ADCREFCOMP: ADC_REFCOMP_OUT (Bitfield-Mask: 0x01) */ +/* ======================================================= XTALCTRL ======================================================== */ +#define MCUCTRL_XTALCTRL_XTALICOMPTRIM_Pos (8UL) /*!< MCUCTRL XTALCTRL: XTALICOMPTRIM (Bit 8) */ +#define MCUCTRL_XTALCTRL_XTALICOMPTRIM_Msk (0x300UL) /*!< MCUCTRL XTALCTRL: XTALICOMPTRIM (Bitfield-Mask: 0x03) */ +#define MCUCTRL_XTALCTRL_XTALIBUFTRIM_Pos (6UL) /*!< MCUCTRL XTALCTRL: XTALIBUFTRIM (Bit 6) */ +#define MCUCTRL_XTALCTRL_XTALIBUFTRIM_Msk (0xc0UL) /*!< MCUCTRL XTALCTRL: XTALIBUFTRIM (Bitfield-Mask: 0x03) */ +#define MCUCTRL_XTALCTRL_PWDBODXTAL_Pos (5UL) /*!< MCUCTRL XTALCTRL: PWDBODXTAL (Bit 5) */ +#define MCUCTRL_XTALCTRL_PWDBODXTAL_Msk (0x20UL) /*!< MCUCTRL XTALCTRL: PWDBODXTAL (Bitfield-Mask: 0x01) */ +#define MCUCTRL_XTALCTRL_PDNBCMPRXTAL_Pos (4UL) /*!< MCUCTRL XTALCTRL: PDNBCMPRXTAL (Bit 4) */ +#define MCUCTRL_XTALCTRL_PDNBCMPRXTAL_Msk (0x10UL) /*!< MCUCTRL XTALCTRL: PDNBCMPRXTAL (Bitfield-Mask: 0x01) */ +#define MCUCTRL_XTALCTRL_PDNBCOREXTAL_Pos (3UL) /*!< MCUCTRL XTALCTRL: PDNBCOREXTAL (Bit 3) */ +#define MCUCTRL_XTALCTRL_PDNBCOREXTAL_Msk (0x8UL) /*!< MCUCTRL XTALCTRL: PDNBCOREXTAL (Bitfield-Mask: 0x01) */ +#define MCUCTRL_XTALCTRL_BYPCMPRXTAL_Pos (2UL) /*!< MCUCTRL XTALCTRL: BYPCMPRXTAL (Bit 2) */ +#define MCUCTRL_XTALCTRL_BYPCMPRXTAL_Msk (0x4UL) /*!< MCUCTRL XTALCTRL: BYPCMPRXTAL (Bitfield-Mask: 0x01) */ +#define MCUCTRL_XTALCTRL_FDBKDSBLXTAL_Pos (1UL) /*!< MCUCTRL XTALCTRL: FDBKDSBLXTAL (Bit 1) */ +#define MCUCTRL_XTALCTRL_FDBKDSBLXTAL_Msk (0x2UL) /*!< MCUCTRL XTALCTRL: FDBKDSBLXTAL (Bitfield-Mask: 0x01) */ +#define MCUCTRL_XTALCTRL_XTALSWE_Pos (0UL) /*!< MCUCTRL XTALCTRL: XTALSWE (Bit 0) */ +#define MCUCTRL_XTALCTRL_XTALSWE_Msk (0x1UL) /*!< MCUCTRL XTALCTRL: XTALSWE (Bitfield-Mask: 0x01) */ +/* ====================================================== XTALGENCTRL ====================================================== */ +#define MCUCTRL_XTALGENCTRL_XTALKSBIASTRIM_Pos (8UL) /*!< MCUCTRL XTALGENCTRL: XTALKSBIASTRIM (Bit 8) */ +#define MCUCTRL_XTALGENCTRL_XTALKSBIASTRIM_Msk (0x3f00UL) /*!< MCUCTRL XTALGENCTRL: XTALKSBIASTRIM (Bitfield-Mask: 0x3f) */ +#define MCUCTRL_XTALGENCTRL_XTALBIASTRIM_Pos (2UL) /*!< MCUCTRL XTALGENCTRL: XTALBIASTRIM (Bit 2) */ +#define MCUCTRL_XTALGENCTRL_XTALBIASTRIM_Msk (0xfcUL) /*!< MCUCTRL XTALGENCTRL: XTALBIASTRIM (Bitfield-Mask: 0x3f) */ +#define MCUCTRL_XTALGENCTRL_ACWARMUP_Pos (0UL) /*!< MCUCTRL XTALGENCTRL: ACWARMUP (Bit 0) */ +#define MCUCTRL_XTALGENCTRL_ACWARMUP_Msk (0x3UL) /*!< MCUCTRL XTALGENCTRL: ACWARMUP (Bitfield-Mask: 0x03) */ +/* ======================================================= MISCCTRL ======================================================== */ +#define MCUCTRL_MISCCTRL_BLE_RESETN_Pos (5UL) /*!< MCUCTRL MISCCTRL: BLE_RESETN (Bit 5) */ +#define MCUCTRL_MISCCTRL_BLE_RESETN_Msk (0x20UL) /*!< MCUCTRL MISCCTRL: BLE_RESETN (Bitfield-Mask: 0x01) */ +#define MCUCTRL_MISCCTRL_RESERVED_RW_0_Pos (0UL) /*!< MCUCTRL MISCCTRL: RESERVED_RW_0 (Bit 0) */ +#define MCUCTRL_MISCCTRL_RESERVED_RW_0_Msk (0x1fUL) /*!< MCUCTRL MISCCTRL: RESERVED_RW_0 (Bitfield-Mask: 0x1f) */ +/* ====================================================== BOOTLOADER ======================================================= */ +#define MCUCTRL_BOOTLOADER_SECBOOTONRST_Pos (30UL) /*!< MCUCTRL BOOTLOADER: SECBOOTONRST (Bit 30) */ +#define MCUCTRL_BOOTLOADER_SECBOOTONRST_Msk (0xc0000000UL) /*!< MCUCTRL BOOTLOADER: SECBOOTONRST (Bitfield-Mask: 0x03) */ +#define MCUCTRL_BOOTLOADER_SECBOOT_Pos (28UL) /*!< MCUCTRL BOOTLOADER: SECBOOT (Bit 28) */ +#define MCUCTRL_BOOTLOADER_SECBOOT_Msk (0x30000000UL) /*!< MCUCTRL BOOTLOADER: SECBOOT (Bitfield-Mask: 0x03) */ +#define MCUCTRL_BOOTLOADER_SECBOOTFEATURE_Pos (26UL) /*!< MCUCTRL BOOTLOADER: SECBOOTFEATURE (Bit 26) */ +#define MCUCTRL_BOOTLOADER_SECBOOTFEATURE_Msk (0xc000000UL) /*!< MCUCTRL BOOTLOADER: SECBOOTFEATURE (Bitfield-Mask: 0x03) */ +#define MCUCTRL_BOOTLOADER_PROTLOCK_Pos (2UL) /*!< MCUCTRL BOOTLOADER: PROTLOCK (Bit 2) */ +#define MCUCTRL_BOOTLOADER_PROTLOCK_Msk (0x4UL) /*!< MCUCTRL BOOTLOADER: PROTLOCK (Bitfield-Mask: 0x01) */ +#define MCUCTRL_BOOTLOADER_SBLOCK_Pos (1UL) /*!< MCUCTRL BOOTLOADER: SBLOCK (Bit 1) */ +#define MCUCTRL_BOOTLOADER_SBLOCK_Msk (0x2UL) /*!< MCUCTRL BOOTLOADER: SBLOCK (Bitfield-Mask: 0x01) */ +#define MCUCTRL_BOOTLOADER_BOOTLOADERLOW_Pos (0UL) /*!< MCUCTRL BOOTLOADER: BOOTLOADERLOW (Bit 0) */ +#define MCUCTRL_BOOTLOADER_BOOTLOADERLOW_Msk (0x1UL) /*!< MCUCTRL BOOTLOADER: BOOTLOADERLOW (Bitfield-Mask: 0x01) */ +/* ====================================================== SHADOWVALID ====================================================== */ +#define MCUCTRL_SHADOWVALID_INFO0_VALID_Pos (2UL) /*!< MCUCTRL SHADOWVALID: INFO0_VALID (Bit 2) */ +#define MCUCTRL_SHADOWVALID_INFO0_VALID_Msk (0x4UL) /*!< MCUCTRL SHADOWVALID: INFO0_VALID (Bitfield-Mask: 0x01) */ +#define MCUCTRL_SHADOWVALID_BLDSLEEP_Pos (1UL) /*!< MCUCTRL SHADOWVALID: BLDSLEEP (Bit 1) */ +#define MCUCTRL_SHADOWVALID_BLDSLEEP_Msk (0x2UL) /*!< MCUCTRL SHADOWVALID: BLDSLEEP (Bitfield-Mask: 0x01) */ +#define MCUCTRL_SHADOWVALID_VALID_Pos (0UL) /*!< MCUCTRL SHADOWVALID: VALID (Bit 0) */ +#define MCUCTRL_SHADOWVALID_VALID_Msk (0x1UL) /*!< MCUCTRL SHADOWVALID: VALID (Bitfield-Mask: 0x01) */ +/* ======================================================= SCRATCH0 ======================================================== */ +#define MCUCTRL_SCRATCH0_SCRATCH0_Pos (0UL) /*!< MCUCTRL SCRATCH0: SCRATCH0 (Bit 0) */ +#define MCUCTRL_SCRATCH0_SCRATCH0_Msk (0xffffffffUL) /*!< MCUCTRL SCRATCH0: SCRATCH0 (Bitfield-Mask: 0xffffffff) */ +/* ======================================================= SCRATCH1 ======================================================== */ +#define MCUCTRL_SCRATCH1_SCRATCH1_Pos (0UL) /*!< MCUCTRL SCRATCH1: SCRATCH1 (Bit 0) */ +#define MCUCTRL_SCRATCH1_SCRATCH1_Msk (0xffffffffUL) /*!< MCUCTRL SCRATCH1: SCRATCH1 (Bitfield-Mask: 0xffffffff) */ +/* ==================================================== ICODEFAULTADDR ===================================================== */ +#define MCUCTRL_ICODEFAULTADDR_ICODEFAULTADDR_Pos (0UL) /*!< MCUCTRL ICODEFAULTADDR: ICODEFAULTADDR (Bit 0) */ +#define MCUCTRL_ICODEFAULTADDR_ICODEFAULTADDR_Msk (0xffffffffUL) /*!< MCUCTRL ICODEFAULTADDR: ICODEFAULTADDR (Bitfield-Mask: 0xffffffff) */ +/* ==================================================== DCODEFAULTADDR ===================================================== */ +#define MCUCTRL_DCODEFAULTADDR_DCODEFAULTADDR_Pos (0UL) /*!< MCUCTRL DCODEFAULTADDR: DCODEFAULTADDR (Bit 0) */ +#define MCUCTRL_DCODEFAULTADDR_DCODEFAULTADDR_Msk (0xffffffffUL) /*!< MCUCTRL DCODEFAULTADDR: DCODEFAULTADDR (Bitfield-Mask: 0xffffffff) */ +/* ===================================================== SYSFAULTADDR ====================================================== */ +#define MCUCTRL_SYSFAULTADDR_SYSFAULTADDR_Pos (0UL) /*!< MCUCTRL SYSFAULTADDR: SYSFAULTADDR (Bit 0) */ +#define MCUCTRL_SYSFAULTADDR_SYSFAULTADDR_Msk (0xffffffffUL) /*!< MCUCTRL SYSFAULTADDR: SYSFAULTADDR (Bitfield-Mask: 0xffffffff) */ +/* ====================================================== FAULTSTATUS ====================================================== */ +#define MCUCTRL_FAULTSTATUS_SYSFAULT_Pos (2UL) /*!< MCUCTRL FAULTSTATUS: SYSFAULT (Bit 2) */ +#define MCUCTRL_FAULTSTATUS_SYSFAULT_Msk (0x4UL) /*!< MCUCTRL FAULTSTATUS: SYSFAULT (Bitfield-Mask: 0x01) */ +#define MCUCTRL_FAULTSTATUS_DCODEFAULT_Pos (1UL) /*!< MCUCTRL FAULTSTATUS: DCODEFAULT (Bit 1) */ +#define MCUCTRL_FAULTSTATUS_DCODEFAULT_Msk (0x2UL) /*!< MCUCTRL FAULTSTATUS: DCODEFAULT (Bitfield-Mask: 0x01) */ +#define MCUCTRL_FAULTSTATUS_ICODEFAULT_Pos (0UL) /*!< MCUCTRL FAULTSTATUS: ICODEFAULT (Bit 0) */ +#define MCUCTRL_FAULTSTATUS_ICODEFAULT_Msk (0x1UL) /*!< MCUCTRL FAULTSTATUS: ICODEFAULT (Bitfield-Mask: 0x01) */ +/* ==================================================== FAULTCAPTUREEN ===================================================== */ +#define MCUCTRL_FAULTCAPTUREEN_FAULTCAPTUREEN_Pos (0UL) /*!< MCUCTRL FAULTCAPTUREEN: FAULTCAPTUREEN (Bit 0) */ +#define MCUCTRL_FAULTCAPTUREEN_FAULTCAPTUREEN_Msk (0x1UL) /*!< MCUCTRL FAULTCAPTUREEN: FAULTCAPTUREEN (Bitfield-Mask: 0x01) */ +/* ========================================================= DBGR1 ========================================================= */ +#define MCUCTRL_DBGR1_ONETO8_Pos (0UL) /*!< MCUCTRL DBGR1: ONETO8 (Bit 0) */ +#define MCUCTRL_DBGR1_ONETO8_Msk (0xffffffffUL) /*!< MCUCTRL DBGR1: ONETO8 (Bitfield-Mask: 0xffffffff) */ +/* ========================================================= DBGR2 ========================================================= */ +#define MCUCTRL_DBGR2_COOLCODE_Pos (0UL) /*!< MCUCTRL DBGR2: COOLCODE (Bit 0) */ +#define MCUCTRL_DBGR2_COOLCODE_Msk (0xffffffffUL) /*!< MCUCTRL DBGR2: COOLCODE (Bitfield-Mask: 0xffffffff) */ +/* ======================================================= PMUENABLE ======================================================= */ +#define MCUCTRL_PMUENABLE_ENABLE_Pos (0UL) /*!< MCUCTRL PMUENABLE: ENABLE (Bit 0) */ +#define MCUCTRL_PMUENABLE_ENABLE_Msk (0x1UL) /*!< MCUCTRL PMUENABLE: ENABLE (Bitfield-Mask: 0x01) */ +/* ======================================================= TPIUCTRL ======================================================== */ +#define MCUCTRL_TPIUCTRL_CLKSEL_Pos (8UL) /*!< MCUCTRL TPIUCTRL: CLKSEL (Bit 8) */ +#define MCUCTRL_TPIUCTRL_CLKSEL_Msk (0x700UL) /*!< MCUCTRL TPIUCTRL: CLKSEL (Bitfield-Mask: 0x07) */ +#define MCUCTRL_TPIUCTRL_ENABLE_Pos (0UL) /*!< MCUCTRL TPIUCTRL: ENABLE (Bit 0) */ +#define MCUCTRL_TPIUCTRL_ENABLE_Msk (0x1UL) /*!< MCUCTRL TPIUCTRL: ENABLE (Bitfield-Mask: 0x01) */ +/* ====================================================== OTAPOINTER ======================================================= */ +#define MCUCTRL_OTAPOINTER_OTAPOINTER_Pos (2UL) /*!< MCUCTRL OTAPOINTER: OTAPOINTER (Bit 2) */ +#define MCUCTRL_OTAPOINTER_OTAPOINTER_Msk (0xfffffffcUL) /*!< MCUCTRL OTAPOINTER: OTAPOINTER (Bitfield-Mask: 0x3fffffff) */ +#define MCUCTRL_OTAPOINTER_OTASBLUPDATE_Pos (1UL) /*!< MCUCTRL OTAPOINTER: OTASBLUPDATE (Bit 1) */ +#define MCUCTRL_OTAPOINTER_OTASBLUPDATE_Msk (0x2UL) /*!< MCUCTRL OTAPOINTER: OTASBLUPDATE (Bitfield-Mask: 0x01) */ +#define MCUCTRL_OTAPOINTER_OTAVALID_Pos (0UL) /*!< MCUCTRL OTAPOINTER: OTAVALID (Bit 0) */ +#define MCUCTRL_OTAPOINTER_OTAVALID_Msk (0x1UL) /*!< MCUCTRL OTAPOINTER: OTAVALID (Bitfield-Mask: 0x01) */ +/* ====================================================== APBDMACTRL ======================================================= */ +#define MCUCTRL_APBDMACTRL_HYSTERESIS_Pos (8UL) /*!< MCUCTRL APBDMACTRL: HYSTERESIS (Bit 8) */ +#define MCUCTRL_APBDMACTRL_HYSTERESIS_Msk (0xff00UL) /*!< MCUCTRL APBDMACTRL: HYSTERESIS (Bitfield-Mask: 0xff) */ +#define MCUCTRL_APBDMACTRL_DECODEABORT_Pos (1UL) /*!< MCUCTRL APBDMACTRL: DECODEABORT (Bit 1) */ +#define MCUCTRL_APBDMACTRL_DECODEABORT_Msk (0x2UL) /*!< MCUCTRL APBDMACTRL: DECODEABORT (Bitfield-Mask: 0x01) */ +#define MCUCTRL_APBDMACTRL_DMA_ENABLE_Pos (0UL) /*!< MCUCTRL APBDMACTRL: DMA_ENABLE (Bit 0) */ +#define MCUCTRL_APBDMACTRL_DMA_ENABLE_Msk (0x1UL) /*!< MCUCTRL APBDMACTRL: DMA_ENABLE (Bitfield-Mask: 0x01) */ +/* ======================================================= SRAMMODE ======================================================== */ +#define MCUCTRL_SRAMMODE_DPREFETCH_CACHE_Pos (5UL) /*!< MCUCTRL SRAMMODE: DPREFETCH_CACHE (Bit 5) */ +#define MCUCTRL_SRAMMODE_DPREFETCH_CACHE_Msk (0x20UL) /*!< MCUCTRL SRAMMODE: DPREFETCH_CACHE (Bitfield-Mask: 0x01) */ +#define MCUCTRL_SRAMMODE_DPREFETCH_Pos (4UL) /*!< MCUCTRL SRAMMODE: DPREFETCH (Bit 4) */ +#define MCUCTRL_SRAMMODE_DPREFETCH_Msk (0x10UL) /*!< MCUCTRL SRAMMODE: DPREFETCH (Bitfield-Mask: 0x01) */ +#define MCUCTRL_SRAMMODE_IPREFETCH_CACHE_Pos (1UL) /*!< MCUCTRL SRAMMODE: IPREFETCH_CACHE (Bit 1) */ +#define MCUCTRL_SRAMMODE_IPREFETCH_CACHE_Msk (0x2UL) /*!< MCUCTRL SRAMMODE: IPREFETCH_CACHE (Bitfield-Mask: 0x01) */ +#define MCUCTRL_SRAMMODE_IPREFETCH_Pos (0UL) /*!< MCUCTRL SRAMMODE: IPREFETCH (Bit 0) */ +#define MCUCTRL_SRAMMODE_IPREFETCH_Msk (0x1UL) /*!< MCUCTRL SRAMMODE: IPREFETCH (Bitfield-Mask: 0x01) */ +/* ====================================================== KEXTCLKSEL ======================================================= */ +#define MCUCTRL_KEXTCLKSEL_KEXTCLKSEL_Pos (0UL) /*!< MCUCTRL KEXTCLKSEL: KEXTCLKSEL (Bit 0) */ +#define MCUCTRL_KEXTCLKSEL_KEXTCLKSEL_Msk (0xffffffffUL) /*!< MCUCTRL KEXTCLKSEL: KEXTCLKSEL (Bitfield-Mask: 0xffffffff) */ +/* ======================================================= SIMOBUCK4 ======================================================= */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKIBIASTRIM_Pos (28UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKIBIASTRIM (Bit 28) */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKIBIASTRIM_Msk (0xf0000000UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKIBIASTRIM (Bitfield-Mask: 0x0f) */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKUVLOMODE_Pos (26UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKUVLOMODE (Bit 26) */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKUVLOMODE_Msk (0xc000000UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKUVLOMODE (Bitfield-Mask: 0x03) */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKPRIORITYSEL_Pos (25UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKPRIORITYSEL (Bit 25) */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKPRIORITYSEL_Msk (0x2000000UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKPRIORITYSEL (Bitfield-Mask: 0x01) */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKCOMP2TIMEOUTEN_Pos (24UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKCOMP2TIMEOUTEN (Bit 24) */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKCOMP2TIMEOUTEN_Msk (0x1000000UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKCOMP2TIMEOUTEN (Bitfield-Mask: 0x01) */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKCOMP2LPEN_Pos (23UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKCOMP2LPEN (Bit 23) */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKCOMP2LPEN_Msk (0x800000UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKCOMP2LPEN (Bitfield-Mask: 0x01) */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKCLKDIVSEL_Pos (21UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKCLKDIVSEL (Bit 21) */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKCLKDIVSEL_Msk (0x600000UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKCLKDIVSEL (Bitfield-Mask: 0x03) */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKEXTCLKSEL_Pos (20UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKEXTCLKSEL (Bit 20) */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKEXTCLKSEL_Msk (0x100000UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKEXTCLKSEL (Bitfield-Mask: 0x01) */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKUVLODRVSTRTRIM_Pos (17UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKUVLODRVSTRTRIM (Bit 17) */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKUVLODRVSTRTRIM_Msk (0xe0000UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKUVLODRVSTRTRIM (Bitfield-Mask: 0x07) */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKUVLOCNTRTRIM_Pos (14UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKUVLOCNTRTRIM (Bit 14) */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKUVLOCNTRTRIM_Msk (0x1c000UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKUVLOCNTRTRIM (Bitfield-Mask: 0x07) */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKZXTRIM_Pos (10UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKZXTRIM (Bit 10) */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKZXTRIM_Msk (0x3c00UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKZXTRIM (Bitfield-Mask: 0x0f) */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKMEMLEAKAGETRIM_Pos (8UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKMEMLEAKAGETRIM (Bit 8) */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKMEMLEAKAGETRIM_Msk (0x300UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKMEMLEAKAGETRIM (Bitfield-Mask: 0x03) */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKMEMLPDRVSTRTRIM_Pos (6UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKMEMLPDRVSTRTRIM (Bit 6) */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKMEMLPDRVSTRTRIM_Msk (0xc0UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKMEMLPDRVSTRTRIM (Bitfield-Mask: 0x03) */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKMEMACTDRVSTRTRIM_Pos (4UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKMEMACTDRVSTRTRIM (Bit 4) */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKMEMACTDRVSTRTRIM_Msk (0x30UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKMEMACTDRVSTRTRIM (Bitfield-Mask: 0x03) */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKMEMLPLOWTONTRIM_Pos (0UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKMEMLPLOWTONTRIM (Bit 0) */ +#define MCUCTRL_SIMOBUCK4_SIMOBUCKMEMLPLOWTONTRIM_Msk (0xfUL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKMEMLPLOWTONTRIM (Bitfield-Mask: 0x0f) */ +/* ======================================================= BLEBUCK2 ======================================================== */ +#define MCUCTRL_BLEBUCK2_BLEBUCKTOND2ATRIM_Pos (12UL) /*!< MCUCTRL BLEBUCK2: BLEBUCKTOND2ATRIM (Bit 12) */ +#define MCUCTRL_BLEBUCK2_BLEBUCKTOND2ATRIM_Msk (0x3f000UL) /*!< MCUCTRL BLEBUCK2: BLEBUCKTOND2ATRIM (Bitfield-Mask: 0x3f) */ +#define MCUCTRL_BLEBUCK2_BLEBUCKTONHITRIM_Pos (6UL) /*!< MCUCTRL BLEBUCK2: BLEBUCKTONHITRIM (Bit 6) */ +#define MCUCTRL_BLEBUCK2_BLEBUCKTONHITRIM_Msk (0xfc0UL) /*!< MCUCTRL BLEBUCK2: BLEBUCKTONHITRIM (Bitfield-Mask: 0x3f) */ +#define MCUCTRL_BLEBUCK2_BLEBUCKTONLOWTRIM_Pos (0UL) /*!< MCUCTRL BLEBUCK2: BLEBUCKTONLOWTRIM (Bit 0) */ +#define MCUCTRL_BLEBUCK2_BLEBUCKTONLOWTRIM_Msk (0x3fUL) /*!< MCUCTRL BLEBUCK2: BLEBUCKTONLOWTRIM (Bitfield-Mask: 0x3f) */ +/* ====================================================== FLASHWPROT0 ====================================================== */ +#define MCUCTRL_FLASHWPROT0_FW0BITS_Pos (0UL) /*!< MCUCTRL FLASHWPROT0: FW0BITS (Bit 0) */ +#define MCUCTRL_FLASHWPROT0_FW0BITS_Msk (0xffffffffUL) /*!< MCUCTRL FLASHWPROT0: FW0BITS (Bitfield-Mask: 0xffffffff) */ +/* ====================================================== FLASHWPROT1 ====================================================== */ +#define MCUCTRL_FLASHWPROT1_FW1BITS_Pos (0UL) /*!< MCUCTRL FLASHWPROT1: FW1BITS (Bit 0) */ +#define MCUCTRL_FLASHWPROT1_FW1BITS_Msk (0xffffffffUL) /*!< MCUCTRL FLASHWPROT1: FW1BITS (Bitfield-Mask: 0xffffffff) */ +/* ====================================================== FLASHRPROT0 ====================================================== */ +#define MCUCTRL_FLASHRPROT0_FR0BITS_Pos (0UL) /*!< MCUCTRL FLASHRPROT0: FR0BITS (Bit 0) */ +#define MCUCTRL_FLASHRPROT0_FR0BITS_Msk (0xffffffffUL) /*!< MCUCTRL FLASHRPROT0: FR0BITS (Bitfield-Mask: 0xffffffff) */ +/* ====================================================== FLASHRPROT1 ====================================================== */ +#define MCUCTRL_FLASHRPROT1_FR1BITS_Pos (0UL) /*!< MCUCTRL FLASHRPROT1: FR1BITS (Bit 0) */ +#define MCUCTRL_FLASHRPROT1_FR1BITS_Msk (0xffffffffUL) /*!< MCUCTRL FLASHRPROT1: FR1BITS (Bitfield-Mask: 0xffffffff) */ +/* ================================================= DMASRAMWRITEPROTECT0 ================================================== */ +#define MCUCTRL_DMASRAMWRITEPROTECT0_DMA_WPROT0_Pos (0UL) /*!< MCUCTRL DMASRAMWRITEPROTECT0: DMA_WPROT0 (Bit 0) */ +#define MCUCTRL_DMASRAMWRITEPROTECT0_DMA_WPROT0_Msk (0xffffffffUL) /*!< MCUCTRL DMASRAMWRITEPROTECT0: DMA_WPROT0 (Bitfield-Mask: 0xffffffff) */ +/* ================================================= DMASRAMWRITEPROTECT1 ================================================== */ +#define MCUCTRL_DMASRAMWRITEPROTECT1_DMA_WPROT1_Pos (0UL) /*!< MCUCTRL DMASRAMWRITEPROTECT1: DMA_WPROT1 (Bit 0) */ +#define MCUCTRL_DMASRAMWRITEPROTECT1_DMA_WPROT1_Msk (0xffffUL) /*!< MCUCTRL DMASRAMWRITEPROTECT1: DMA_WPROT1 (Bitfield-Mask: 0xffff) */ +/* ================================================== DMASRAMREADPROTECT0 ================================================== */ +#define MCUCTRL_DMASRAMREADPROTECT0_DMA_RPROT0_Pos (0UL) /*!< MCUCTRL DMASRAMREADPROTECT0: DMA_RPROT0 (Bit 0) */ +#define MCUCTRL_DMASRAMREADPROTECT0_DMA_RPROT0_Msk (0xffffffffUL) /*!< MCUCTRL DMASRAMREADPROTECT0: DMA_RPROT0 (Bitfield-Mask: 0xffffffff) */ +/* ================================================== DMASRAMREADPROTECT1 ================================================== */ +#define MCUCTRL_DMASRAMREADPROTECT1_DMA_RPROT1_Pos (0UL) /*!< MCUCTRL DMASRAMREADPROTECT1: DMA_RPROT1 (Bit 0) */ +#define MCUCTRL_DMASRAMREADPROTECT1_DMA_RPROT1_Msk (0xffffUL) /*!< MCUCTRL DMASRAMREADPROTECT1: DMA_RPROT1 (Bitfield-Mask: 0xffff) */ + + +/* =========================================================================================================================== */ +/* ================ MSPI ================ */ +/* =========================================================================================================================== */ + +/* ========================================================= CTRL ========================================================== */ +#define MSPI_CTRL_XFERBYTES_Pos (16UL) /*!< MSPI CTRL: XFERBYTES (Bit 16) */ +#define MSPI_CTRL_XFERBYTES_Msk (0xffff0000UL) /*!< MSPI CTRL: XFERBYTES (Bitfield-Mask: 0xffff) */ +#define MSPI_CTRL_PIOSCRAMBLE_Pos (11UL) /*!< MSPI CTRL: PIOSCRAMBLE (Bit 11) */ +#define MSPI_CTRL_PIOSCRAMBLE_Msk (0x800UL) /*!< MSPI CTRL: PIOSCRAMBLE (Bitfield-Mask: 0x01) */ +#define MSPI_CTRL_TXRX_Pos (10UL) /*!< MSPI CTRL: TXRX (Bit 10) */ +#define MSPI_CTRL_TXRX_Msk (0x400UL) /*!< MSPI CTRL: TXRX (Bitfield-Mask: 0x01) */ +#define MSPI_CTRL_SENDI_Pos (9UL) /*!< MSPI CTRL: SENDI (Bit 9) */ +#define MSPI_CTRL_SENDI_Msk (0x200UL) /*!< MSPI CTRL: SENDI (Bitfield-Mask: 0x01) */ +#define MSPI_CTRL_SENDA_Pos (8UL) /*!< MSPI CTRL: SENDA (Bit 8) */ +#define MSPI_CTRL_SENDA_Msk (0x100UL) /*!< MSPI CTRL: SENDA (Bitfield-Mask: 0x01) */ +#define MSPI_CTRL_ENTURN_Pos (7UL) /*!< MSPI CTRL: ENTURN (Bit 7) */ +#define MSPI_CTRL_ENTURN_Msk (0x80UL) /*!< MSPI CTRL: ENTURN (Bitfield-Mask: 0x01) */ +#define MSPI_CTRL_BIGENDIAN_Pos (6UL) /*!< MSPI CTRL: BIGENDIAN (Bit 6) */ +#define MSPI_CTRL_BIGENDIAN_Msk (0x40UL) /*!< MSPI CTRL: BIGENDIAN (Bitfield-Mask: 0x01) */ +#define MSPI_CTRL_CONT_Pos (5UL) /*!< MSPI CTRL: CONT (Bit 5) */ +#define MSPI_CTRL_CONT_Msk (0x20UL) /*!< MSPI CTRL: CONT (Bitfield-Mask: 0x01) */ +#define MSPI_CTRL_QUADCMD_Pos (3UL) /*!< MSPI CTRL: QUADCMD (Bit 3) */ +#define MSPI_CTRL_QUADCMD_Msk (0x8UL) /*!< MSPI CTRL: QUADCMD (Bitfield-Mask: 0x01) */ +#define MSPI_CTRL_BUSY_Pos (2UL) /*!< MSPI CTRL: BUSY (Bit 2) */ +#define MSPI_CTRL_BUSY_Msk (0x4UL) /*!< MSPI CTRL: BUSY (Bitfield-Mask: 0x01) */ +#define MSPI_CTRL_STATUS_Pos (1UL) /*!< MSPI CTRL: STATUS (Bit 1) */ +#define MSPI_CTRL_STATUS_Msk (0x2UL) /*!< MSPI CTRL: STATUS (Bitfield-Mask: 0x01) */ +#define MSPI_CTRL_START_Pos (0UL) /*!< MSPI CTRL: START (Bit 0) */ +#define MSPI_CTRL_START_Msk (0x1UL) /*!< MSPI CTRL: START (Bitfield-Mask: 0x01) */ +/* ========================================================== CFG ========================================================== */ +#define MSPI_CFG_CPOL_Pos (17UL) /*!< MSPI CFG: CPOL (Bit 17) */ +#define MSPI_CFG_CPOL_Msk (0x20000UL) /*!< MSPI CFG: CPOL (Bitfield-Mask: 0x01) */ +#define MSPI_CFG_CPHA_Pos (16UL) /*!< MSPI CFG: CPHA (Bit 16) */ +#define MSPI_CFG_CPHA_Msk (0x10000UL) /*!< MSPI CFG: CPHA (Bitfield-Mask: 0x01) */ +#define MSPI_CFG_TURNAROUND_Pos (8UL) /*!< MSPI CFG: TURNAROUND (Bit 8) */ +#define MSPI_CFG_TURNAROUND_Msk (0x3f00UL) /*!< MSPI CFG: TURNAROUND (Bitfield-Mask: 0x3f) */ +#define MSPI_CFG_SEPIO_Pos (7UL) /*!< MSPI CFG: SEPIO (Bit 7) */ +#define MSPI_CFG_SEPIO_Msk (0x80UL) /*!< MSPI CFG: SEPIO (Bitfield-Mask: 0x01) */ +#define MSPI_CFG_ISIZE_Pos (6UL) /*!< MSPI CFG: ISIZE (Bit 6) */ +#define MSPI_CFG_ISIZE_Msk (0x40UL) /*!< MSPI CFG: ISIZE (Bitfield-Mask: 0x01) */ +#define MSPI_CFG_ASIZE_Pos (4UL) /*!< MSPI CFG: ASIZE (Bit 4) */ +#define MSPI_CFG_ASIZE_Msk (0x30UL) /*!< MSPI CFG: ASIZE (Bitfield-Mask: 0x03) */ +#define MSPI_CFG_DEVCFG_Pos (0UL) /*!< MSPI CFG: DEVCFG (Bit 0) */ +#define MSPI_CFG_DEVCFG_Msk (0xfUL) /*!< MSPI CFG: DEVCFG (Bitfield-Mask: 0x0f) */ +/* ========================================================= ADDR ========================================================== */ +#define MSPI_ADDR_ADDR_Pos (0UL) /*!< MSPI ADDR: ADDR (Bit 0) */ +#define MSPI_ADDR_ADDR_Msk (0xffffffffUL) /*!< MSPI ADDR: ADDR (Bitfield-Mask: 0xffffffff) */ +/* ========================================================= INSTR ========================================================= */ +#define MSPI_INSTR_INSTR_Pos (0UL) /*!< MSPI INSTR: INSTR (Bit 0) */ +#define MSPI_INSTR_INSTR_Msk (0xffffUL) /*!< MSPI INSTR: INSTR (Bitfield-Mask: 0xffff) */ +/* ======================================================== TXFIFO ========================================================= */ +#define MSPI_TXFIFO_TXFIFO_Pos (0UL) /*!< MSPI TXFIFO: TXFIFO (Bit 0) */ +#define MSPI_TXFIFO_TXFIFO_Msk (0xffffffffUL) /*!< MSPI TXFIFO: TXFIFO (Bitfield-Mask: 0xffffffff) */ +/* ======================================================== RXFIFO ========================================================= */ +#define MSPI_RXFIFO_RXFIFO_Pos (0UL) /*!< MSPI RXFIFO: RXFIFO (Bit 0) */ +#define MSPI_RXFIFO_RXFIFO_Msk (0xffffffffUL) /*!< MSPI RXFIFO: RXFIFO (Bitfield-Mask: 0xffffffff) */ +/* ======================================================= TXENTRIES ======================================================= */ +#define MSPI_TXENTRIES_TXENTRIES_Pos (0UL) /*!< MSPI TXENTRIES: TXENTRIES (Bit 0) */ +#define MSPI_TXENTRIES_TXENTRIES_Msk (0x1fUL) /*!< MSPI TXENTRIES: TXENTRIES (Bitfield-Mask: 0x1f) */ +/* ======================================================= RXENTRIES ======================================================= */ +#define MSPI_RXENTRIES_RXENTRIES_Pos (0UL) /*!< MSPI RXENTRIES: RXENTRIES (Bit 0) */ +#define MSPI_RXENTRIES_RXENTRIES_Msk (0x1fUL) /*!< MSPI RXENTRIES: RXENTRIES (Bitfield-Mask: 0x1f) */ +/* ======================================================= THRESHOLD ======================================================= */ +#define MSPI_THRESHOLD_RXTHRESH_Pos (8UL) /*!< MSPI THRESHOLD: RXTHRESH (Bit 8) */ +#define MSPI_THRESHOLD_RXTHRESH_Msk (0x1f00UL) /*!< MSPI THRESHOLD: RXTHRESH (Bitfield-Mask: 0x1f) */ +#define MSPI_THRESHOLD_TXTHRESH_Pos (0UL) /*!< MSPI THRESHOLD: TXTHRESH (Bit 0) */ +#define MSPI_THRESHOLD_TXTHRESH_Msk (0x1fUL) /*!< MSPI THRESHOLD: TXTHRESH (Bitfield-Mask: 0x1f) */ +/* ======================================================== MSPICFG ======================================================== */ +#define MSPI_MSPICFG_PRSTN_Pos (31UL) /*!< MSPI MSPICFG: PRSTN (Bit 31) */ +#define MSPI_MSPICFG_PRSTN_Msk (0x80000000UL) /*!< MSPI MSPICFG: PRSTN (Bitfield-Mask: 0x01) */ +#define MSPI_MSPICFG_IPRSTN_Pos (30UL) /*!< MSPI MSPICFG: IPRSTN (Bit 30) */ +#define MSPI_MSPICFG_IPRSTN_Msk (0x40000000UL) /*!< MSPI MSPICFG: IPRSTN (Bitfield-Mask: 0x01) */ +#define MSPI_MSPICFG_FIFORESET_Pos (29UL) /*!< MSPI MSPICFG: FIFORESET (Bit 29) */ +#define MSPI_MSPICFG_FIFORESET_Msk (0x20000000UL) /*!< MSPI MSPICFG: FIFORESET (Bitfield-Mask: 0x01) */ +#define MSPI_MSPICFG_CLKDIV_Pos (8UL) /*!< MSPI MSPICFG: CLKDIV (Bit 8) */ +#define MSPI_MSPICFG_CLKDIV_Msk (0x3f00UL) /*!< MSPI MSPICFG: CLKDIV (Bitfield-Mask: 0x3f) */ +#define MSPI_MSPICFG_IOMSEL_Pos (4UL) /*!< MSPI MSPICFG: IOMSEL (Bit 4) */ +#define MSPI_MSPICFG_IOMSEL_Msk (0x70UL) /*!< MSPI MSPICFG: IOMSEL (Bitfield-Mask: 0x07) */ +#define MSPI_MSPICFG_TXNEG_Pos (3UL) /*!< MSPI MSPICFG: TXNEG (Bit 3) */ +#define MSPI_MSPICFG_TXNEG_Msk (0x8UL) /*!< MSPI MSPICFG: TXNEG (Bitfield-Mask: 0x01) */ +#define MSPI_MSPICFG_RXNEG_Pos (2UL) /*!< MSPI MSPICFG: RXNEG (Bit 2) */ +#define MSPI_MSPICFG_RXNEG_Msk (0x4UL) /*!< MSPI MSPICFG: RXNEG (Bitfield-Mask: 0x01) */ +#define MSPI_MSPICFG_RXCAP_Pos (1UL) /*!< MSPI MSPICFG: RXCAP (Bit 1) */ +#define MSPI_MSPICFG_RXCAP_Msk (0x2UL) /*!< MSPI MSPICFG: RXCAP (Bitfield-Mask: 0x01) */ +#define MSPI_MSPICFG_APBCLK_Pos (0UL) /*!< MSPI MSPICFG: APBCLK (Bit 0) */ +#define MSPI_MSPICFG_APBCLK_Msk (0x1UL) /*!< MSPI MSPICFG: APBCLK (Bitfield-Mask: 0x01) */ +/* ======================================================== PADCFG ========================================================= */ +#define MSPI_PADCFG_REVCS_Pos (21UL) /*!< MSPI PADCFG: REVCS (Bit 21) */ +#define MSPI_PADCFG_REVCS_Msk (0x200000UL) /*!< MSPI PADCFG: REVCS (Bitfield-Mask: 0x01) */ +#define MSPI_PADCFG_IN3_Pos (20UL) /*!< MSPI PADCFG: IN3 (Bit 20) */ +#define MSPI_PADCFG_IN3_Msk (0x100000UL) /*!< MSPI PADCFG: IN3 (Bitfield-Mask: 0x01) */ +#define MSPI_PADCFG_IN2_Pos (19UL) /*!< MSPI PADCFG: IN2 (Bit 19) */ +#define MSPI_PADCFG_IN2_Msk (0x80000UL) /*!< MSPI PADCFG: IN2 (Bitfield-Mask: 0x01) */ +#define MSPI_PADCFG_IN1_Pos (18UL) /*!< MSPI PADCFG: IN1 (Bit 18) */ +#define MSPI_PADCFG_IN1_Msk (0x40000UL) /*!< MSPI PADCFG: IN1 (Bitfield-Mask: 0x01) */ +#define MSPI_PADCFG_IN0_Pos (16UL) /*!< MSPI PADCFG: IN0 (Bit 16) */ +#define MSPI_PADCFG_IN0_Msk (0x30000UL) /*!< MSPI PADCFG: IN0 (Bitfield-Mask: 0x03) */ +#define MSPI_PADCFG_OUT7_Pos (4UL) /*!< MSPI PADCFG: OUT7 (Bit 4) */ +#define MSPI_PADCFG_OUT7_Msk (0x10UL) /*!< MSPI PADCFG: OUT7 (Bitfield-Mask: 0x01) */ +#define MSPI_PADCFG_OUT6_Pos (3UL) /*!< MSPI PADCFG: OUT6 (Bit 3) */ +#define MSPI_PADCFG_OUT6_Msk (0x8UL) /*!< MSPI PADCFG: OUT6 (Bitfield-Mask: 0x01) */ +#define MSPI_PADCFG_OUT5_Pos (2UL) /*!< MSPI PADCFG: OUT5 (Bit 2) */ +#define MSPI_PADCFG_OUT5_Msk (0x4UL) /*!< MSPI PADCFG: OUT5 (Bitfield-Mask: 0x01) */ +#define MSPI_PADCFG_OUT4_Pos (1UL) /*!< MSPI PADCFG: OUT4 (Bit 1) */ +#define MSPI_PADCFG_OUT4_Msk (0x2UL) /*!< MSPI PADCFG: OUT4 (Bitfield-Mask: 0x01) */ +#define MSPI_PADCFG_OUT3_Pos (0UL) /*!< MSPI PADCFG: OUT3 (Bit 0) */ +#define MSPI_PADCFG_OUT3_Msk (0x1UL) /*!< MSPI PADCFG: OUT3 (Bitfield-Mask: 0x01) */ +/* ======================================================= PADOUTEN ======================================================== */ +#define MSPI_PADOUTEN_OUTEN_Pos (0UL) /*!< MSPI PADOUTEN: OUTEN (Bit 0) */ +#define MSPI_PADOUTEN_OUTEN_Msk (0x1ffUL) /*!< MSPI PADOUTEN: OUTEN (Bitfield-Mask: 0x1ff) */ +/* ========================================================= FLASH ========================================================= */ +#define MSPI_FLASH_READINSTR_Pos (24UL) /*!< MSPI FLASH: READINSTR (Bit 24) */ +#define MSPI_FLASH_READINSTR_Msk (0xff000000UL) /*!< MSPI FLASH: READINSTR (Bitfield-Mask: 0xff) */ +#define MSPI_FLASH_WRITEINSTR_Pos (16UL) /*!< MSPI FLASH: WRITEINSTR (Bit 16) */ +#define MSPI_FLASH_WRITEINSTR_Msk (0xff0000UL) /*!< MSPI FLASH: WRITEINSTR (Bitfield-Mask: 0xff) */ +#define MSPI_FLASH_XIPMIXED_Pos (8UL) /*!< MSPI FLASH: XIPMIXED (Bit 8) */ +#define MSPI_FLASH_XIPMIXED_Msk (0x700UL) /*!< MSPI FLASH: XIPMIXED (Bitfield-Mask: 0x07) */ +#define MSPI_FLASH_XIPSENDI_Pos (7UL) /*!< MSPI FLASH: XIPSENDI (Bit 7) */ +#define MSPI_FLASH_XIPSENDI_Msk (0x80UL) /*!< MSPI FLASH: XIPSENDI (Bitfield-Mask: 0x01) */ +#define MSPI_FLASH_XIPSENDA_Pos (6UL) /*!< MSPI FLASH: XIPSENDA (Bit 6) */ +#define MSPI_FLASH_XIPSENDA_Msk (0x40UL) /*!< MSPI FLASH: XIPSENDA (Bitfield-Mask: 0x01) */ +#define MSPI_FLASH_XIPENTURN_Pos (5UL) /*!< MSPI FLASH: XIPENTURN (Bit 5) */ +#define MSPI_FLASH_XIPENTURN_Msk (0x20UL) /*!< MSPI FLASH: XIPENTURN (Bitfield-Mask: 0x01) */ +#define MSPI_FLASH_XIPBIGENDIAN_Pos (4UL) /*!< MSPI FLASH: XIPBIGENDIAN (Bit 4) */ +#define MSPI_FLASH_XIPBIGENDIAN_Msk (0x10UL) /*!< MSPI FLASH: XIPBIGENDIAN (Bitfield-Mask: 0x01) */ +#define MSPI_FLASH_XIPACK_Pos (2UL) /*!< MSPI FLASH: XIPACK (Bit 2) */ +#define MSPI_FLASH_XIPACK_Msk (0xcUL) /*!< MSPI FLASH: XIPACK (Bitfield-Mask: 0x03) */ +#define MSPI_FLASH_XIPEN_Pos (0UL) /*!< MSPI FLASH: XIPEN (Bit 0) */ +#define MSPI_FLASH_XIPEN_Msk (0x1UL) /*!< MSPI FLASH: XIPEN (Bitfield-Mask: 0x01) */ +/* ====================================================== SCRAMBLING ======================================================= */ +#define MSPI_SCRAMBLING_SCRENABLE_Pos (31UL) /*!< MSPI SCRAMBLING: SCRENABLE (Bit 31) */ +#define MSPI_SCRAMBLING_SCRENABLE_Msk (0x80000000UL) /*!< MSPI SCRAMBLING: SCRENABLE (Bitfield-Mask: 0x01) */ +#define MSPI_SCRAMBLING_SCREND_Pos (16UL) /*!< MSPI SCRAMBLING: SCREND (Bit 16) */ +#define MSPI_SCRAMBLING_SCREND_Msk (0x3ff0000UL) /*!< MSPI SCRAMBLING: SCREND (Bitfield-Mask: 0x3ff) */ +#define MSPI_SCRAMBLING_SCRSTART_Pos (0UL) /*!< MSPI SCRAMBLING: SCRSTART (Bit 0) */ +#define MSPI_SCRAMBLING_SCRSTART_Msk (0x3ffUL) /*!< MSPI SCRAMBLING: SCRSTART (Bitfield-Mask: 0x3ff) */ +/* ========================================================= INTEN ========================================================= */ +#define MSPI_INTEN_SCRERR_Pos (12UL) /*!< MSPI INTEN: SCRERR (Bit 12) */ +#define MSPI_INTEN_SCRERR_Msk (0x1000UL) /*!< MSPI INTEN: SCRERR (Bitfield-Mask: 0x01) */ +#define MSPI_INTEN_CQERR_Pos (11UL) /*!< MSPI INTEN: CQERR (Bit 11) */ +#define MSPI_INTEN_CQERR_Msk (0x800UL) /*!< MSPI INTEN: CQERR (Bitfield-Mask: 0x01) */ +#define MSPI_INTEN_CQPAUSED_Pos (10UL) /*!< MSPI INTEN: CQPAUSED (Bit 10) */ +#define MSPI_INTEN_CQPAUSED_Msk (0x400UL) /*!< MSPI INTEN: CQPAUSED (Bitfield-Mask: 0x01) */ +#define MSPI_INTEN_CQUPD_Pos (9UL) /*!< MSPI INTEN: CQUPD (Bit 9) */ +#define MSPI_INTEN_CQUPD_Msk (0x200UL) /*!< MSPI INTEN: CQUPD (Bitfield-Mask: 0x01) */ +#define MSPI_INTEN_CQCMP_Pos (8UL) /*!< MSPI INTEN: CQCMP (Bit 8) */ +#define MSPI_INTEN_CQCMP_Msk (0x100UL) /*!< MSPI INTEN: CQCMP (Bitfield-Mask: 0x01) */ +#define MSPI_INTEN_DERR_Pos (7UL) /*!< MSPI INTEN: DERR (Bit 7) */ +#define MSPI_INTEN_DERR_Msk (0x80UL) /*!< MSPI INTEN: DERR (Bitfield-Mask: 0x01) */ +#define MSPI_INTEN_DCMP_Pos (6UL) /*!< MSPI INTEN: DCMP (Bit 6) */ +#define MSPI_INTEN_DCMP_Msk (0x40UL) /*!< MSPI INTEN: DCMP (Bitfield-Mask: 0x01) */ +#define MSPI_INTEN_RXF_Pos (5UL) /*!< MSPI INTEN: RXF (Bit 5) */ +#define MSPI_INTEN_RXF_Msk (0x20UL) /*!< MSPI INTEN: RXF (Bitfield-Mask: 0x01) */ +#define MSPI_INTEN_RXO_Pos (4UL) /*!< MSPI INTEN: RXO (Bit 4) */ +#define MSPI_INTEN_RXO_Msk (0x10UL) /*!< MSPI INTEN: RXO (Bitfield-Mask: 0x01) */ +#define MSPI_INTEN_RXU_Pos (3UL) /*!< MSPI INTEN: RXU (Bit 3) */ +#define MSPI_INTEN_RXU_Msk (0x8UL) /*!< MSPI INTEN: RXU (Bitfield-Mask: 0x01) */ +#define MSPI_INTEN_TXO_Pos (2UL) /*!< MSPI INTEN: TXO (Bit 2) */ +#define MSPI_INTEN_TXO_Msk (0x4UL) /*!< MSPI INTEN: TXO (Bitfield-Mask: 0x01) */ +#define MSPI_INTEN_TXE_Pos (1UL) /*!< MSPI INTEN: TXE (Bit 1) */ +#define MSPI_INTEN_TXE_Msk (0x2UL) /*!< MSPI INTEN: TXE (Bitfield-Mask: 0x01) */ +#define MSPI_INTEN_CMDCMP_Pos (0UL) /*!< MSPI INTEN: CMDCMP (Bit 0) */ +#define MSPI_INTEN_CMDCMP_Msk (0x1UL) /*!< MSPI INTEN: CMDCMP (Bitfield-Mask: 0x01) */ +/* ======================================================== INTSTAT ======================================================== */ +#define MSPI_INTSTAT_SCRERR_Pos (12UL) /*!< MSPI INTSTAT: SCRERR (Bit 12) */ +#define MSPI_INTSTAT_SCRERR_Msk (0x1000UL) /*!< MSPI INTSTAT: SCRERR (Bitfield-Mask: 0x01) */ +#define MSPI_INTSTAT_CQERR_Pos (11UL) /*!< MSPI INTSTAT: CQERR (Bit 11) */ +#define MSPI_INTSTAT_CQERR_Msk (0x800UL) /*!< MSPI INTSTAT: CQERR (Bitfield-Mask: 0x01) */ +#define MSPI_INTSTAT_CQPAUSED_Pos (10UL) /*!< MSPI INTSTAT: CQPAUSED (Bit 10) */ +#define MSPI_INTSTAT_CQPAUSED_Msk (0x400UL) /*!< MSPI INTSTAT: CQPAUSED (Bitfield-Mask: 0x01) */ +#define MSPI_INTSTAT_CQUPD_Pos (9UL) /*!< MSPI INTSTAT: CQUPD (Bit 9) */ +#define MSPI_INTSTAT_CQUPD_Msk (0x200UL) /*!< MSPI INTSTAT: CQUPD (Bitfield-Mask: 0x01) */ +#define MSPI_INTSTAT_CQCMP_Pos (8UL) /*!< MSPI INTSTAT: CQCMP (Bit 8) */ +#define MSPI_INTSTAT_CQCMP_Msk (0x100UL) /*!< MSPI INTSTAT: CQCMP (Bitfield-Mask: 0x01) */ +#define MSPI_INTSTAT_DERR_Pos (7UL) /*!< MSPI INTSTAT: DERR (Bit 7) */ +#define MSPI_INTSTAT_DERR_Msk (0x80UL) /*!< MSPI INTSTAT: DERR (Bitfield-Mask: 0x01) */ +#define MSPI_INTSTAT_DCMP_Pos (6UL) /*!< MSPI INTSTAT: DCMP (Bit 6) */ +#define MSPI_INTSTAT_DCMP_Msk (0x40UL) /*!< MSPI INTSTAT: DCMP (Bitfield-Mask: 0x01) */ +#define MSPI_INTSTAT_RXF_Pos (5UL) /*!< MSPI INTSTAT: RXF (Bit 5) */ +#define MSPI_INTSTAT_RXF_Msk (0x20UL) /*!< MSPI INTSTAT: RXF (Bitfield-Mask: 0x01) */ +#define MSPI_INTSTAT_RXO_Pos (4UL) /*!< MSPI INTSTAT: RXO (Bit 4) */ +#define MSPI_INTSTAT_RXO_Msk (0x10UL) /*!< MSPI INTSTAT: RXO (Bitfield-Mask: 0x01) */ +#define MSPI_INTSTAT_RXU_Pos (3UL) /*!< MSPI INTSTAT: RXU (Bit 3) */ +#define MSPI_INTSTAT_RXU_Msk (0x8UL) /*!< MSPI INTSTAT: RXU (Bitfield-Mask: 0x01) */ +#define MSPI_INTSTAT_TXO_Pos (2UL) /*!< MSPI INTSTAT: TXO (Bit 2) */ +#define MSPI_INTSTAT_TXO_Msk (0x4UL) /*!< MSPI INTSTAT: TXO (Bitfield-Mask: 0x01) */ +#define MSPI_INTSTAT_TXE_Pos (1UL) /*!< MSPI INTSTAT: TXE (Bit 1) */ +#define MSPI_INTSTAT_TXE_Msk (0x2UL) /*!< MSPI INTSTAT: TXE (Bitfield-Mask: 0x01) */ +#define MSPI_INTSTAT_CMDCMP_Pos (0UL) /*!< MSPI INTSTAT: CMDCMP (Bit 0) */ +#define MSPI_INTSTAT_CMDCMP_Msk (0x1UL) /*!< MSPI INTSTAT: CMDCMP (Bitfield-Mask: 0x01) */ +/* ======================================================== INTCLR ========================================================= */ +#define MSPI_INTCLR_SCRERR_Pos (12UL) /*!< MSPI INTCLR: SCRERR (Bit 12) */ +#define MSPI_INTCLR_SCRERR_Msk (0x1000UL) /*!< MSPI INTCLR: SCRERR (Bitfield-Mask: 0x01) */ +#define MSPI_INTCLR_CQERR_Pos (11UL) /*!< MSPI INTCLR: CQERR (Bit 11) */ +#define MSPI_INTCLR_CQERR_Msk (0x800UL) /*!< MSPI INTCLR: CQERR (Bitfield-Mask: 0x01) */ +#define MSPI_INTCLR_CQPAUSED_Pos (10UL) /*!< MSPI INTCLR: CQPAUSED (Bit 10) */ +#define MSPI_INTCLR_CQPAUSED_Msk (0x400UL) /*!< MSPI INTCLR: CQPAUSED (Bitfield-Mask: 0x01) */ +#define MSPI_INTCLR_CQUPD_Pos (9UL) /*!< MSPI INTCLR: CQUPD (Bit 9) */ +#define MSPI_INTCLR_CQUPD_Msk (0x200UL) /*!< MSPI INTCLR: CQUPD (Bitfield-Mask: 0x01) */ +#define MSPI_INTCLR_CQCMP_Pos (8UL) /*!< MSPI INTCLR: CQCMP (Bit 8) */ +#define MSPI_INTCLR_CQCMP_Msk (0x100UL) /*!< MSPI INTCLR: CQCMP (Bitfield-Mask: 0x01) */ +#define MSPI_INTCLR_DERR_Pos (7UL) /*!< MSPI INTCLR: DERR (Bit 7) */ +#define MSPI_INTCLR_DERR_Msk (0x80UL) /*!< MSPI INTCLR: DERR (Bitfield-Mask: 0x01) */ +#define MSPI_INTCLR_DCMP_Pos (6UL) /*!< MSPI INTCLR: DCMP (Bit 6) */ +#define MSPI_INTCLR_DCMP_Msk (0x40UL) /*!< MSPI INTCLR: DCMP (Bitfield-Mask: 0x01) */ +#define MSPI_INTCLR_RXF_Pos (5UL) /*!< MSPI INTCLR: RXF (Bit 5) */ +#define MSPI_INTCLR_RXF_Msk (0x20UL) /*!< MSPI INTCLR: RXF (Bitfield-Mask: 0x01) */ +#define MSPI_INTCLR_RXO_Pos (4UL) /*!< MSPI INTCLR: RXO (Bit 4) */ +#define MSPI_INTCLR_RXO_Msk (0x10UL) /*!< MSPI INTCLR: RXO (Bitfield-Mask: 0x01) */ +#define MSPI_INTCLR_RXU_Pos (3UL) /*!< MSPI INTCLR: RXU (Bit 3) */ +#define MSPI_INTCLR_RXU_Msk (0x8UL) /*!< MSPI INTCLR: RXU (Bitfield-Mask: 0x01) */ +#define MSPI_INTCLR_TXO_Pos (2UL) /*!< MSPI INTCLR: TXO (Bit 2) */ +#define MSPI_INTCLR_TXO_Msk (0x4UL) /*!< MSPI INTCLR: TXO (Bitfield-Mask: 0x01) */ +#define MSPI_INTCLR_TXE_Pos (1UL) /*!< MSPI INTCLR: TXE (Bit 1) */ +#define MSPI_INTCLR_TXE_Msk (0x2UL) /*!< MSPI INTCLR: TXE (Bitfield-Mask: 0x01) */ +#define MSPI_INTCLR_CMDCMP_Pos (0UL) /*!< MSPI INTCLR: CMDCMP (Bit 0) */ +#define MSPI_INTCLR_CMDCMP_Msk (0x1UL) /*!< MSPI INTCLR: CMDCMP (Bitfield-Mask: 0x01) */ +/* ======================================================== INTSET ========================================================= */ +#define MSPI_INTSET_SCRERR_Pos (12UL) /*!< MSPI INTSET: SCRERR (Bit 12) */ +#define MSPI_INTSET_SCRERR_Msk (0x1000UL) /*!< MSPI INTSET: SCRERR (Bitfield-Mask: 0x01) */ +#define MSPI_INTSET_CQERR_Pos (11UL) /*!< MSPI INTSET: CQERR (Bit 11) */ +#define MSPI_INTSET_CQERR_Msk (0x800UL) /*!< MSPI INTSET: CQERR (Bitfield-Mask: 0x01) */ +#define MSPI_INTSET_CQPAUSED_Pos (10UL) /*!< MSPI INTSET: CQPAUSED (Bit 10) */ +#define MSPI_INTSET_CQPAUSED_Msk (0x400UL) /*!< MSPI INTSET: CQPAUSED (Bitfield-Mask: 0x01) */ +#define MSPI_INTSET_CQUPD_Pos (9UL) /*!< MSPI INTSET: CQUPD (Bit 9) */ +#define MSPI_INTSET_CQUPD_Msk (0x200UL) /*!< MSPI INTSET: CQUPD (Bitfield-Mask: 0x01) */ +#define MSPI_INTSET_CQCMP_Pos (8UL) /*!< MSPI INTSET: CQCMP (Bit 8) */ +#define MSPI_INTSET_CQCMP_Msk (0x100UL) /*!< MSPI INTSET: CQCMP (Bitfield-Mask: 0x01) */ +#define MSPI_INTSET_DERR_Pos (7UL) /*!< MSPI INTSET: DERR (Bit 7) */ +#define MSPI_INTSET_DERR_Msk (0x80UL) /*!< MSPI INTSET: DERR (Bitfield-Mask: 0x01) */ +#define MSPI_INTSET_DCMP_Pos (6UL) /*!< MSPI INTSET: DCMP (Bit 6) */ +#define MSPI_INTSET_DCMP_Msk (0x40UL) /*!< MSPI INTSET: DCMP (Bitfield-Mask: 0x01) */ +#define MSPI_INTSET_RXF_Pos (5UL) /*!< MSPI INTSET: RXF (Bit 5) */ +#define MSPI_INTSET_RXF_Msk (0x20UL) /*!< MSPI INTSET: RXF (Bitfield-Mask: 0x01) */ +#define MSPI_INTSET_RXO_Pos (4UL) /*!< MSPI INTSET: RXO (Bit 4) */ +#define MSPI_INTSET_RXO_Msk (0x10UL) /*!< MSPI INTSET: RXO (Bitfield-Mask: 0x01) */ +#define MSPI_INTSET_RXU_Pos (3UL) /*!< MSPI INTSET: RXU (Bit 3) */ +#define MSPI_INTSET_RXU_Msk (0x8UL) /*!< MSPI INTSET: RXU (Bitfield-Mask: 0x01) */ +#define MSPI_INTSET_TXO_Pos (2UL) /*!< MSPI INTSET: TXO (Bit 2) */ +#define MSPI_INTSET_TXO_Msk (0x4UL) /*!< MSPI INTSET: TXO (Bitfield-Mask: 0x01) */ +#define MSPI_INTSET_TXE_Pos (1UL) /*!< MSPI INTSET: TXE (Bit 1) */ +#define MSPI_INTSET_TXE_Msk (0x2UL) /*!< MSPI INTSET: TXE (Bitfield-Mask: 0x01) */ +#define MSPI_INTSET_CMDCMP_Pos (0UL) /*!< MSPI INTSET: CMDCMP (Bit 0) */ +#define MSPI_INTSET_CMDCMP_Msk (0x1UL) /*!< MSPI INTSET: CMDCMP (Bitfield-Mask: 0x01) */ +/* ======================================================== DMACFG ========================================================= */ +#define MSPI_DMACFG_DMAPWROFF_Pos (18UL) /*!< MSPI DMACFG: DMAPWROFF (Bit 18) */ +#define MSPI_DMACFG_DMAPWROFF_Msk (0x40000UL) /*!< MSPI DMACFG: DMAPWROFF (Bitfield-Mask: 0x01) */ +#define MSPI_DMACFG_DMAPRI_Pos (3UL) /*!< MSPI DMACFG: DMAPRI (Bit 3) */ +#define MSPI_DMACFG_DMAPRI_Msk (0x18UL) /*!< MSPI DMACFG: DMAPRI (Bitfield-Mask: 0x03) */ +#define MSPI_DMACFG_DMADIR_Pos (2UL) /*!< MSPI DMACFG: DMADIR (Bit 2) */ +#define MSPI_DMACFG_DMADIR_Msk (0x4UL) /*!< MSPI DMACFG: DMADIR (Bitfield-Mask: 0x01) */ +#define MSPI_DMACFG_DMAEN_Pos (0UL) /*!< MSPI DMACFG: DMAEN (Bit 0) */ +#define MSPI_DMACFG_DMAEN_Msk (0x3UL) /*!< MSPI DMACFG: DMAEN (Bitfield-Mask: 0x03) */ +/* ======================================================== DMASTAT ======================================================== */ +#define MSPI_DMASTAT_SCRERR_Pos (3UL) /*!< MSPI DMASTAT: SCRERR (Bit 3) */ +#define MSPI_DMASTAT_SCRERR_Msk (0x8UL) /*!< MSPI DMASTAT: SCRERR (Bitfield-Mask: 0x01) */ +#define MSPI_DMASTAT_DMAERR_Pos (2UL) /*!< MSPI DMASTAT: DMAERR (Bit 2) */ +#define MSPI_DMASTAT_DMAERR_Msk (0x4UL) /*!< MSPI DMASTAT: DMAERR (Bitfield-Mask: 0x01) */ +#define MSPI_DMASTAT_DMACPL_Pos (1UL) /*!< MSPI DMASTAT: DMACPL (Bit 1) */ +#define MSPI_DMASTAT_DMACPL_Msk (0x2UL) /*!< MSPI DMASTAT: DMACPL (Bitfield-Mask: 0x01) */ +#define MSPI_DMASTAT_DMATIP_Pos (0UL) /*!< MSPI DMASTAT: DMATIP (Bit 0) */ +#define MSPI_DMASTAT_DMATIP_Msk (0x1UL) /*!< MSPI DMASTAT: DMATIP (Bitfield-Mask: 0x01) */ +/* ====================================================== DMATARGADDR ====================================================== */ +#define MSPI_DMATARGADDR_TARGADDR_Pos (0UL) /*!< MSPI DMATARGADDR: TARGADDR (Bit 0) */ +#define MSPI_DMATARGADDR_TARGADDR_Msk (0xffffffffUL) /*!< MSPI DMATARGADDR: TARGADDR (Bitfield-Mask: 0xffffffff) */ +/* ====================================================== DMADEVADDR ======================================================= */ +#define MSPI_DMADEVADDR_DEVADDR_Pos (0UL) /*!< MSPI DMADEVADDR: DEVADDR (Bit 0) */ +#define MSPI_DMADEVADDR_DEVADDR_Msk (0xffffffffUL) /*!< MSPI DMADEVADDR: DEVADDR (Bitfield-Mask: 0xffffffff) */ +/* ====================================================== DMATOTCOUNT ====================================================== */ +#define MSPI_DMATOTCOUNT_TOTCOUNT_Pos (0UL) /*!< MSPI DMATOTCOUNT: TOTCOUNT (Bit 0) */ +#define MSPI_DMATOTCOUNT_TOTCOUNT_Msk (0xffffUL) /*!< MSPI DMATOTCOUNT: TOTCOUNT (Bitfield-Mask: 0xffff) */ +/* ======================================================= DMABCOUNT ======================================================= */ +#define MSPI_DMABCOUNT_BCOUNT_Pos (0UL) /*!< MSPI DMABCOUNT: BCOUNT (Bit 0) */ +#define MSPI_DMABCOUNT_BCOUNT_Msk (0xffUL) /*!< MSPI DMABCOUNT: BCOUNT (Bitfield-Mask: 0xff) */ +/* ======================================================= DMATHRESH ======================================================= */ +#define MSPI_DMATHRESH_DMATHRESH_Pos (0UL) /*!< MSPI DMATHRESH: DMATHRESH (Bit 0) */ +#define MSPI_DMATHRESH_DMATHRESH_Msk (0xfUL) /*!< MSPI DMATHRESH: DMATHRESH (Bitfield-Mask: 0x0f) */ +/* ========================================================= CQCFG ========================================================= */ +#define MSPI_CQCFG_CQAUTOCLEARMASK_Pos (3UL) /*!< MSPI CQCFG: CQAUTOCLEARMASK (Bit 3) */ +#define MSPI_CQCFG_CQAUTOCLEARMASK_Msk (0x8UL) /*!< MSPI CQCFG: CQAUTOCLEARMASK (Bitfield-Mask: 0x01) */ +#define MSPI_CQCFG_CQPWROFF_Pos (2UL) /*!< MSPI CQCFG: CQPWROFF (Bit 2) */ +#define MSPI_CQCFG_CQPWROFF_Msk (0x4UL) /*!< MSPI CQCFG: CQPWROFF (Bitfield-Mask: 0x01) */ +#define MSPI_CQCFG_CQPRI_Pos (1UL) /*!< MSPI CQCFG: CQPRI (Bit 1) */ +#define MSPI_CQCFG_CQPRI_Msk (0x2UL) /*!< MSPI CQCFG: CQPRI (Bitfield-Mask: 0x01) */ +#define MSPI_CQCFG_CQEN_Pos (0UL) /*!< MSPI CQCFG: CQEN (Bit 0) */ +#define MSPI_CQCFG_CQEN_Msk (0x1UL) /*!< MSPI CQCFG: CQEN (Bitfield-Mask: 0x01) */ +/* ======================================================== CQADDR ========================================================= */ +#define MSPI_CQADDR_CQADDR_Pos (0UL) /*!< MSPI CQADDR: CQADDR (Bit 0) */ +#define MSPI_CQADDR_CQADDR_Msk (0x1fffffffUL) /*!< MSPI CQADDR: CQADDR (Bitfield-Mask: 0x1fffffff) */ +/* ======================================================== CQSTAT ========================================================= */ +#define MSPI_CQSTAT_CQPAUSED_Pos (3UL) /*!< MSPI CQSTAT: CQPAUSED (Bit 3) */ +#define MSPI_CQSTAT_CQPAUSED_Msk (0x8UL) /*!< MSPI CQSTAT: CQPAUSED (Bitfield-Mask: 0x01) */ +#define MSPI_CQSTAT_CQERR_Pos (2UL) /*!< MSPI CQSTAT: CQERR (Bit 2) */ +#define MSPI_CQSTAT_CQERR_Msk (0x4UL) /*!< MSPI CQSTAT: CQERR (Bitfield-Mask: 0x01) */ +#define MSPI_CQSTAT_CQCPL_Pos (1UL) /*!< MSPI CQSTAT: CQCPL (Bit 1) */ +#define MSPI_CQSTAT_CQCPL_Msk (0x2UL) /*!< MSPI CQSTAT: CQCPL (Bitfield-Mask: 0x01) */ +#define MSPI_CQSTAT_CQTIP_Pos (0UL) /*!< MSPI CQSTAT: CQTIP (Bit 0) */ +#define MSPI_CQSTAT_CQTIP_Msk (0x1UL) /*!< MSPI CQSTAT: CQTIP (Bitfield-Mask: 0x01) */ +/* ======================================================== CQFLAGS ======================================================== */ +#define MSPI_CQFLAGS_CQFLAGS_Pos (0UL) /*!< MSPI CQFLAGS: CQFLAGS (Bit 0) */ +#define MSPI_CQFLAGS_CQFLAGS_Msk (0xffffUL) /*!< MSPI CQFLAGS: CQFLAGS (Bitfield-Mask: 0xffff) */ +/* ====================================================== CQSETCLEAR ======================================================= */ +#define MSPI_CQSETCLEAR_CQFCLR_Pos (16UL) /*!< MSPI CQSETCLEAR: CQFCLR (Bit 16) */ +#define MSPI_CQSETCLEAR_CQFCLR_Msk (0xff0000UL) /*!< MSPI CQSETCLEAR: CQFCLR (Bitfield-Mask: 0xff) */ +#define MSPI_CQSETCLEAR_CQFTOGGLE_Pos (8UL) /*!< MSPI CQSETCLEAR: CQFTOGGLE (Bit 8) */ +#define MSPI_CQSETCLEAR_CQFTOGGLE_Msk (0xff00UL) /*!< MSPI CQSETCLEAR: CQFTOGGLE (Bitfield-Mask: 0xff) */ +#define MSPI_CQSETCLEAR_CQFSET_Pos (0UL) /*!< MSPI CQSETCLEAR: CQFSET (Bit 0) */ +#define MSPI_CQSETCLEAR_CQFSET_Msk (0xffUL) /*!< MSPI CQSETCLEAR: CQFSET (Bitfield-Mask: 0xff) */ +/* ======================================================== CQPAUSE ======================================================== */ +#define MSPI_CQPAUSE_CQMASK_Pos (0UL) /*!< MSPI CQPAUSE: CQMASK (Bit 0) */ +#define MSPI_CQPAUSE_CQMASK_Msk (0xffffUL) /*!< MSPI CQPAUSE: CQMASK (Bitfield-Mask: 0xffff) */ +/* ======================================================= CQCURIDX ======================================================== */ +#define MSPI_CQCURIDX_CQCURIDX_Pos (0UL) /*!< MSPI CQCURIDX: CQCURIDX (Bit 0) */ +#define MSPI_CQCURIDX_CQCURIDX_Msk (0xffUL) /*!< MSPI CQCURIDX: CQCURIDX (Bitfield-Mask: 0xff) */ +/* ======================================================= CQENDIDX ======================================================== */ +#define MSPI_CQENDIDX_CQENDIDX_Pos (0UL) /*!< MSPI CQENDIDX: CQENDIDX (Bit 0) */ +#define MSPI_CQENDIDX_CQENDIDX_Msk (0xffUL) /*!< MSPI CQENDIDX: CQENDIDX (Bitfield-Mask: 0xff) */ + + +/* =========================================================================================================================== */ +/* ================ PDM ================ */ +/* =========================================================================================================================== */ + +/* ========================================================= PCFG ========================================================== */ +#define PDM_PCFG_LRSWAP_Pos (31UL) /*!< PDM PCFG: LRSWAP (Bit 31) */ +#define PDM_PCFG_LRSWAP_Msk (0x80000000UL) /*!< PDM PCFG: LRSWAP (Bitfield-Mask: 0x01) */ +#define PDM_PCFG_PGARIGHT_Pos (26UL) /*!< PDM PCFG: PGARIGHT (Bit 26) */ +#define PDM_PCFG_PGARIGHT_Msk (0x7c000000UL) /*!< PDM PCFG: PGARIGHT (Bitfield-Mask: 0x1f) */ +#define PDM_PCFG_PGALEFT_Pos (21UL) /*!< PDM PCFG: PGALEFT (Bit 21) */ +#define PDM_PCFG_PGALEFT_Msk (0x3e00000UL) /*!< PDM PCFG: PGALEFT (Bitfield-Mask: 0x1f) */ +#define PDM_PCFG_MCLKDIV_Pos (17UL) /*!< PDM PCFG: MCLKDIV (Bit 17) */ +#define PDM_PCFG_MCLKDIV_Msk (0x60000UL) /*!< PDM PCFG: MCLKDIV (Bitfield-Mask: 0x03) */ +#define PDM_PCFG_SINCRATE_Pos (10UL) /*!< PDM PCFG: SINCRATE (Bit 10) */ +#define PDM_PCFG_SINCRATE_Msk (0x1fc00UL) /*!< PDM PCFG: SINCRATE (Bitfield-Mask: 0x7f) */ +#define PDM_PCFG_ADCHPD_Pos (9UL) /*!< PDM PCFG: ADCHPD (Bit 9) */ +#define PDM_PCFG_ADCHPD_Msk (0x200UL) /*!< PDM PCFG: ADCHPD (Bitfield-Mask: 0x01) */ +#define PDM_PCFG_HPCUTOFF_Pos (5UL) /*!< PDM PCFG: HPCUTOFF (Bit 5) */ +#define PDM_PCFG_HPCUTOFF_Msk (0x1e0UL) /*!< PDM PCFG: HPCUTOFF (Bitfield-Mask: 0x0f) */ +#define PDM_PCFG_CYCLES_Pos (2UL) /*!< PDM PCFG: CYCLES (Bit 2) */ +#define PDM_PCFG_CYCLES_Msk (0x1cUL) /*!< PDM PCFG: CYCLES (Bitfield-Mask: 0x07) */ +#define PDM_PCFG_SOFTMUTE_Pos (1UL) /*!< PDM PCFG: SOFTMUTE (Bit 1) */ +#define PDM_PCFG_SOFTMUTE_Msk (0x2UL) /*!< PDM PCFG: SOFTMUTE (Bitfield-Mask: 0x01) */ +#define PDM_PCFG_PDMCOREEN_Pos (0UL) /*!< PDM PCFG: PDMCOREEN (Bit 0) */ +#define PDM_PCFG_PDMCOREEN_Msk (0x1UL) /*!< PDM PCFG: PDMCOREEN (Bitfield-Mask: 0x01) */ +/* ========================================================= VCFG ========================================================== */ +#define PDM_VCFG_IOCLKEN_Pos (31UL) /*!< PDM VCFG: IOCLKEN (Bit 31) */ +#define PDM_VCFG_IOCLKEN_Msk (0x80000000UL) /*!< PDM VCFG: IOCLKEN (Bitfield-Mask: 0x01) */ +#define PDM_VCFG_RSTB_Pos (30UL) /*!< PDM VCFG: RSTB (Bit 30) */ +#define PDM_VCFG_RSTB_Msk (0x40000000UL) /*!< PDM VCFG: RSTB (Bitfield-Mask: 0x01) */ +#define PDM_VCFG_PDMCLKSEL_Pos (27UL) /*!< PDM VCFG: PDMCLKSEL (Bit 27) */ +#define PDM_VCFG_PDMCLKSEL_Msk (0x38000000UL) /*!< PDM VCFG: PDMCLKSEL (Bitfield-Mask: 0x07) */ +#define PDM_VCFG_PDMCLKEN_Pos (26UL) /*!< PDM VCFG: PDMCLKEN (Bit 26) */ +#define PDM_VCFG_PDMCLKEN_Msk (0x4000000UL) /*!< PDM VCFG: PDMCLKEN (Bitfield-Mask: 0x01) */ +#define PDM_VCFG_I2SEN_Pos (20UL) /*!< PDM VCFG: I2SEN (Bit 20) */ +#define PDM_VCFG_I2SEN_Msk (0x100000UL) /*!< PDM VCFG: I2SEN (Bitfield-Mask: 0x01) */ +#define PDM_VCFG_BCLKINV_Pos (19UL) /*!< PDM VCFG: BCLKINV (Bit 19) */ +#define PDM_VCFG_BCLKINV_Msk (0x80000UL) /*!< PDM VCFG: BCLKINV (Bitfield-Mask: 0x01) */ +#define PDM_VCFG_DMICKDEL_Pos (17UL) /*!< PDM VCFG: DMICKDEL (Bit 17) */ +#define PDM_VCFG_DMICKDEL_Msk (0x20000UL) /*!< PDM VCFG: DMICKDEL (Bitfield-Mask: 0x01) */ +#define PDM_VCFG_SELAP_Pos (16UL) /*!< PDM VCFG: SELAP (Bit 16) */ +#define PDM_VCFG_SELAP_Msk (0x10000UL) /*!< PDM VCFG: SELAP (Bitfield-Mask: 0x01) */ +#define PDM_VCFG_PCMPACK_Pos (8UL) /*!< PDM VCFG: PCMPACK (Bit 8) */ +#define PDM_VCFG_PCMPACK_Msk (0x100UL) /*!< PDM VCFG: PCMPACK (Bitfield-Mask: 0x01) */ +#define PDM_VCFG_CHSET_Pos (3UL) /*!< PDM VCFG: CHSET (Bit 3) */ +#define PDM_VCFG_CHSET_Msk (0x18UL) /*!< PDM VCFG: CHSET (Bitfield-Mask: 0x03) */ +/* ======================================================= VOICESTAT ======================================================= */ +#define PDM_VOICESTAT_FIFOCNT_Pos (0UL) /*!< PDM VOICESTAT: FIFOCNT (Bit 0) */ +#define PDM_VOICESTAT_FIFOCNT_Msk (0x3fUL) /*!< PDM VOICESTAT: FIFOCNT (Bitfield-Mask: 0x3f) */ +/* ======================================================= FIFOREAD ======================================================== */ +#define PDM_FIFOREAD_FIFOREAD_Pos (0UL) /*!< PDM FIFOREAD: FIFOREAD (Bit 0) */ +#define PDM_FIFOREAD_FIFOREAD_Msk (0xffffffffUL) /*!< PDM FIFOREAD: FIFOREAD (Bitfield-Mask: 0xffffffff) */ +/* ======================================================= FIFOFLUSH ======================================================= */ +#define PDM_FIFOFLUSH_FIFOFLUSH_Pos (0UL) /*!< PDM FIFOFLUSH: FIFOFLUSH (Bit 0) */ +#define PDM_FIFOFLUSH_FIFOFLUSH_Msk (0x1UL) /*!< PDM FIFOFLUSH: FIFOFLUSH (Bitfield-Mask: 0x01) */ +/* ======================================================== FIFOTHR ======================================================== */ +#define PDM_FIFOTHR_FIFOTHR_Pos (0UL) /*!< PDM FIFOTHR: FIFOTHR (Bit 0) */ +#define PDM_FIFOTHR_FIFOTHR_Msk (0x1fUL) /*!< PDM FIFOTHR: FIFOTHR (Bitfield-Mask: 0x1f) */ +/* ========================================================= INTEN ========================================================= */ +#define PDM_INTEN_DERR_Pos (4UL) /*!< PDM INTEN: DERR (Bit 4) */ +#define PDM_INTEN_DERR_Msk (0x10UL) /*!< PDM INTEN: DERR (Bitfield-Mask: 0x01) */ +#define PDM_INTEN_DCMP_Pos (3UL) /*!< PDM INTEN: DCMP (Bit 3) */ +#define PDM_INTEN_DCMP_Msk (0x8UL) /*!< PDM INTEN: DCMP (Bitfield-Mask: 0x01) */ +#define PDM_INTEN_UNDFL_Pos (2UL) /*!< PDM INTEN: UNDFL (Bit 2) */ +#define PDM_INTEN_UNDFL_Msk (0x4UL) /*!< PDM INTEN: UNDFL (Bitfield-Mask: 0x01) */ +#define PDM_INTEN_OVF_Pos (1UL) /*!< PDM INTEN: OVF (Bit 1) */ +#define PDM_INTEN_OVF_Msk (0x2UL) /*!< PDM INTEN: OVF (Bitfield-Mask: 0x01) */ +#define PDM_INTEN_THR_Pos (0UL) /*!< PDM INTEN: THR (Bit 0) */ +#define PDM_INTEN_THR_Msk (0x1UL) /*!< PDM INTEN: THR (Bitfield-Mask: 0x01) */ +/* ======================================================== INTSTAT ======================================================== */ +#define PDM_INTSTAT_DERR_Pos (4UL) /*!< PDM INTSTAT: DERR (Bit 4) */ +#define PDM_INTSTAT_DERR_Msk (0x10UL) /*!< PDM INTSTAT: DERR (Bitfield-Mask: 0x01) */ +#define PDM_INTSTAT_DCMP_Pos (3UL) /*!< PDM INTSTAT: DCMP (Bit 3) */ +#define PDM_INTSTAT_DCMP_Msk (0x8UL) /*!< PDM INTSTAT: DCMP (Bitfield-Mask: 0x01) */ +#define PDM_INTSTAT_UNDFL_Pos (2UL) /*!< PDM INTSTAT: UNDFL (Bit 2) */ +#define PDM_INTSTAT_UNDFL_Msk (0x4UL) /*!< PDM INTSTAT: UNDFL (Bitfield-Mask: 0x01) */ +#define PDM_INTSTAT_OVF_Pos (1UL) /*!< PDM INTSTAT: OVF (Bit 1) */ +#define PDM_INTSTAT_OVF_Msk (0x2UL) /*!< PDM INTSTAT: OVF (Bitfield-Mask: 0x01) */ +#define PDM_INTSTAT_THR_Pos (0UL) /*!< PDM INTSTAT: THR (Bit 0) */ +#define PDM_INTSTAT_THR_Msk (0x1UL) /*!< PDM INTSTAT: THR (Bitfield-Mask: 0x01) */ +/* ======================================================== INTCLR ========================================================= */ +#define PDM_INTCLR_DERR_Pos (4UL) /*!< PDM INTCLR: DERR (Bit 4) */ +#define PDM_INTCLR_DERR_Msk (0x10UL) /*!< PDM INTCLR: DERR (Bitfield-Mask: 0x01) */ +#define PDM_INTCLR_DCMP_Pos (3UL) /*!< PDM INTCLR: DCMP (Bit 3) */ +#define PDM_INTCLR_DCMP_Msk (0x8UL) /*!< PDM INTCLR: DCMP (Bitfield-Mask: 0x01) */ +#define PDM_INTCLR_UNDFL_Pos (2UL) /*!< PDM INTCLR: UNDFL (Bit 2) */ +#define PDM_INTCLR_UNDFL_Msk (0x4UL) /*!< PDM INTCLR: UNDFL (Bitfield-Mask: 0x01) */ +#define PDM_INTCLR_OVF_Pos (1UL) /*!< PDM INTCLR: OVF (Bit 1) */ +#define PDM_INTCLR_OVF_Msk (0x2UL) /*!< PDM INTCLR: OVF (Bitfield-Mask: 0x01) */ +#define PDM_INTCLR_THR_Pos (0UL) /*!< PDM INTCLR: THR (Bit 0) */ +#define PDM_INTCLR_THR_Msk (0x1UL) /*!< PDM INTCLR: THR (Bitfield-Mask: 0x01) */ +/* ======================================================== INTSET ========================================================= */ +#define PDM_INTSET_DERR_Pos (4UL) /*!< PDM INTSET: DERR (Bit 4) */ +#define PDM_INTSET_DERR_Msk (0x10UL) /*!< PDM INTSET: DERR (Bitfield-Mask: 0x01) */ +#define PDM_INTSET_DCMP_Pos (3UL) /*!< PDM INTSET: DCMP (Bit 3) */ +#define PDM_INTSET_DCMP_Msk (0x8UL) /*!< PDM INTSET: DCMP (Bitfield-Mask: 0x01) */ +#define PDM_INTSET_UNDFL_Pos (2UL) /*!< PDM INTSET: UNDFL (Bit 2) */ +#define PDM_INTSET_UNDFL_Msk (0x4UL) /*!< PDM INTSET: UNDFL (Bitfield-Mask: 0x01) */ +#define PDM_INTSET_OVF_Pos (1UL) /*!< PDM INTSET: OVF (Bit 1) */ +#define PDM_INTSET_OVF_Msk (0x2UL) /*!< PDM INTSET: OVF (Bitfield-Mask: 0x01) */ +#define PDM_INTSET_THR_Pos (0UL) /*!< PDM INTSET: THR (Bit 0) */ +#define PDM_INTSET_THR_Msk (0x1UL) /*!< PDM INTSET: THR (Bitfield-Mask: 0x01) */ +/* ======================================================= DMATRIGEN ======================================================= */ +#define PDM_DMATRIGEN_DTHR90_Pos (1UL) /*!< PDM DMATRIGEN: DTHR90 (Bit 1) */ +#define PDM_DMATRIGEN_DTHR90_Msk (0x2UL) /*!< PDM DMATRIGEN: DTHR90 (Bitfield-Mask: 0x01) */ +#define PDM_DMATRIGEN_DTHR_Pos (0UL) /*!< PDM DMATRIGEN: DTHR (Bit 0) */ +#define PDM_DMATRIGEN_DTHR_Msk (0x1UL) /*!< PDM DMATRIGEN: DTHR (Bitfield-Mask: 0x01) */ +/* ====================================================== DMATRIGSTAT ====================================================== */ +#define PDM_DMATRIGSTAT_DTHR90STAT_Pos (1UL) /*!< PDM DMATRIGSTAT: DTHR90STAT (Bit 1) */ +#define PDM_DMATRIGSTAT_DTHR90STAT_Msk (0x2UL) /*!< PDM DMATRIGSTAT: DTHR90STAT (Bitfield-Mask: 0x01) */ +#define PDM_DMATRIGSTAT_DTHRSTAT_Pos (0UL) /*!< PDM DMATRIGSTAT: DTHRSTAT (Bit 0) */ +#define PDM_DMATRIGSTAT_DTHRSTAT_Msk (0x1UL) /*!< PDM DMATRIGSTAT: DTHRSTAT (Bitfield-Mask: 0x01) */ +/* ======================================================== DMACFG ========================================================= */ +#define PDM_DMACFG_DPWROFF_Pos (10UL) /*!< PDM DMACFG: DPWROFF (Bit 10) */ +#define PDM_DMACFG_DPWROFF_Msk (0x400UL) /*!< PDM DMACFG: DPWROFF (Bitfield-Mask: 0x01) */ +#define PDM_DMACFG_DAUTOHIP_Pos (9UL) /*!< PDM DMACFG: DAUTOHIP (Bit 9) */ +#define PDM_DMACFG_DAUTOHIP_Msk (0x200UL) /*!< PDM DMACFG: DAUTOHIP (Bitfield-Mask: 0x01) */ +#define PDM_DMACFG_DMAPRI_Pos (8UL) /*!< PDM DMACFG: DMAPRI (Bit 8) */ +#define PDM_DMACFG_DMAPRI_Msk (0x100UL) /*!< PDM DMACFG: DMAPRI (Bitfield-Mask: 0x01) */ +#define PDM_DMACFG_DMADIR_Pos (2UL) /*!< PDM DMACFG: DMADIR (Bit 2) */ +#define PDM_DMACFG_DMADIR_Msk (0x4UL) /*!< PDM DMACFG: DMADIR (Bitfield-Mask: 0x01) */ +#define PDM_DMACFG_DMAEN_Pos (0UL) /*!< PDM DMACFG: DMAEN (Bit 0) */ +#define PDM_DMACFG_DMAEN_Msk (0x1UL) /*!< PDM DMACFG: DMAEN (Bitfield-Mask: 0x01) */ +/* ====================================================== DMATOTCOUNT ====================================================== */ +#define PDM_DMATOTCOUNT_TOTCOUNT_Pos (0UL) /*!< PDM DMATOTCOUNT: TOTCOUNT (Bit 0) */ +#define PDM_DMATOTCOUNT_TOTCOUNT_Msk (0xfffffUL) /*!< PDM DMATOTCOUNT: TOTCOUNT (Bitfield-Mask: 0xfffff) */ +/* ====================================================== DMATARGADDR ====================================================== */ +#define PDM_DMATARGADDR_UTARGADDR_Pos (20UL) /*!< PDM DMATARGADDR: UTARGADDR (Bit 20) */ +#define PDM_DMATARGADDR_UTARGADDR_Msk (0xfff00000UL) /*!< PDM DMATARGADDR: UTARGADDR (Bitfield-Mask: 0xfff) */ +#define PDM_DMATARGADDR_LTARGADDR_Pos (0UL) /*!< PDM DMATARGADDR: LTARGADDR (Bit 0) */ +#define PDM_DMATARGADDR_LTARGADDR_Msk (0xfffffUL) /*!< PDM DMATARGADDR: LTARGADDR (Bitfield-Mask: 0xfffff) */ +/* ======================================================== DMASTAT ======================================================== */ +#define PDM_DMASTAT_DMAERR_Pos (2UL) /*!< PDM DMASTAT: DMAERR (Bit 2) */ +#define PDM_DMASTAT_DMAERR_Msk (0x4UL) /*!< PDM DMASTAT: DMAERR (Bitfield-Mask: 0x01) */ +#define PDM_DMASTAT_DMACPL_Pos (1UL) /*!< PDM DMASTAT: DMACPL (Bit 1) */ +#define PDM_DMASTAT_DMACPL_Msk (0x2UL) /*!< PDM DMASTAT: DMACPL (Bitfield-Mask: 0x01) */ +#define PDM_DMASTAT_DMATIP_Pos (0UL) /*!< PDM DMASTAT: DMATIP (Bit 0) */ +#define PDM_DMASTAT_DMATIP_Msk (0x1UL) /*!< PDM DMASTAT: DMATIP (Bitfield-Mask: 0x01) */ + + +/* =========================================================================================================================== */ +/* ================ PWRCTRL ================ */ +/* =========================================================================================================================== */ + +/* ======================================================= SUPPLYSRC ======================================================= */ +#define PWRCTRL_SUPPLYSRC_BLEBUCKEN_Pos (0UL) /*!< PWRCTRL SUPPLYSRC: BLEBUCKEN (Bit 0) */ +#define PWRCTRL_SUPPLYSRC_BLEBUCKEN_Msk (0x1UL) /*!< PWRCTRL SUPPLYSRC: BLEBUCKEN (Bitfield-Mask: 0x01) */ +/* ===================================================== SUPPLYSTATUS ====================================================== */ +#define PWRCTRL_SUPPLYSTATUS_BLEBUCKON_Pos (1UL) /*!< PWRCTRL SUPPLYSTATUS: BLEBUCKON (Bit 1) */ +#define PWRCTRL_SUPPLYSTATUS_BLEBUCKON_Msk (0x2UL) /*!< PWRCTRL SUPPLYSTATUS: BLEBUCKON (Bitfield-Mask: 0x01) */ +#define PWRCTRL_SUPPLYSTATUS_SIMOBUCKON_Pos (0UL) /*!< PWRCTRL SUPPLYSTATUS: SIMOBUCKON (Bit 0) */ +#define PWRCTRL_SUPPLYSTATUS_SIMOBUCKON_Msk (0x1UL) /*!< PWRCTRL SUPPLYSTATUS: SIMOBUCKON (Bitfield-Mask: 0x01) */ +/* ======================================================= DEVPWREN ======================================================== */ +#define PWRCTRL_DEVPWREN_PWRBLEL_Pos (13UL) /*!< PWRCTRL DEVPWREN: PWRBLEL (Bit 13) */ +#define PWRCTRL_DEVPWREN_PWRBLEL_Msk (0x2000UL) /*!< PWRCTRL DEVPWREN: PWRBLEL (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWREN_PWRPDM_Pos (12UL) /*!< PWRCTRL DEVPWREN: PWRPDM (Bit 12) */ +#define PWRCTRL_DEVPWREN_PWRPDM_Msk (0x1000UL) /*!< PWRCTRL DEVPWREN: PWRPDM (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWREN_PWRMSPI_Pos (11UL) /*!< PWRCTRL DEVPWREN: PWRMSPI (Bit 11) */ +#define PWRCTRL_DEVPWREN_PWRMSPI_Msk (0x800UL) /*!< PWRCTRL DEVPWREN: PWRMSPI (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWREN_PWRSCARD_Pos (10UL) /*!< PWRCTRL DEVPWREN: PWRSCARD (Bit 10) */ +#define PWRCTRL_DEVPWREN_PWRSCARD_Msk (0x400UL) /*!< PWRCTRL DEVPWREN: PWRSCARD (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWREN_PWRADC_Pos (9UL) /*!< PWRCTRL DEVPWREN: PWRADC (Bit 9) */ +#define PWRCTRL_DEVPWREN_PWRADC_Msk (0x200UL) /*!< PWRCTRL DEVPWREN: PWRADC (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWREN_PWRUART1_Pos (8UL) /*!< PWRCTRL DEVPWREN: PWRUART1 (Bit 8) */ +#define PWRCTRL_DEVPWREN_PWRUART1_Msk (0x100UL) /*!< PWRCTRL DEVPWREN: PWRUART1 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWREN_PWRUART0_Pos (7UL) /*!< PWRCTRL DEVPWREN: PWRUART0 (Bit 7) */ +#define PWRCTRL_DEVPWREN_PWRUART0_Msk (0x80UL) /*!< PWRCTRL DEVPWREN: PWRUART0 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWREN_PWRIOM5_Pos (6UL) /*!< PWRCTRL DEVPWREN: PWRIOM5 (Bit 6) */ +#define PWRCTRL_DEVPWREN_PWRIOM5_Msk (0x40UL) /*!< PWRCTRL DEVPWREN: PWRIOM5 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWREN_PWRIOM4_Pos (5UL) /*!< PWRCTRL DEVPWREN: PWRIOM4 (Bit 5) */ +#define PWRCTRL_DEVPWREN_PWRIOM4_Msk (0x20UL) /*!< PWRCTRL DEVPWREN: PWRIOM4 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWREN_PWRIOM3_Pos (4UL) /*!< PWRCTRL DEVPWREN: PWRIOM3 (Bit 4) */ +#define PWRCTRL_DEVPWREN_PWRIOM3_Msk (0x10UL) /*!< PWRCTRL DEVPWREN: PWRIOM3 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWREN_PWRIOM2_Pos (3UL) /*!< PWRCTRL DEVPWREN: PWRIOM2 (Bit 3) */ +#define PWRCTRL_DEVPWREN_PWRIOM2_Msk (0x8UL) /*!< PWRCTRL DEVPWREN: PWRIOM2 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWREN_PWRIOM1_Pos (2UL) /*!< PWRCTRL DEVPWREN: PWRIOM1 (Bit 2) */ +#define PWRCTRL_DEVPWREN_PWRIOM1_Msk (0x4UL) /*!< PWRCTRL DEVPWREN: PWRIOM1 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWREN_PWRIOM0_Pos (1UL) /*!< PWRCTRL DEVPWREN: PWRIOM0 (Bit 1) */ +#define PWRCTRL_DEVPWREN_PWRIOM0_Msk (0x2UL) /*!< PWRCTRL DEVPWREN: PWRIOM0 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWREN_PWRIOS_Pos (0UL) /*!< PWRCTRL DEVPWREN: PWRIOS (Bit 0) */ +#define PWRCTRL_DEVPWREN_PWRIOS_Msk (0x1UL) /*!< PWRCTRL DEVPWREN: PWRIOS (Bitfield-Mask: 0x01) */ +/* ===================================================== MEMPWDINSLEEP ===================================================== */ +#define PWRCTRL_MEMPWDINSLEEP_CACHEPWDSLP_Pos (31UL) /*!< PWRCTRL MEMPWDINSLEEP: CACHEPWDSLP (Bit 31) */ +#define PWRCTRL_MEMPWDINSLEEP_CACHEPWDSLP_Msk (0x80000000UL) /*!< PWRCTRL MEMPWDINSLEEP: CACHEPWDSLP (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MEMPWDINSLEEP_FLASH1PWDSLP_Pos (14UL) /*!< PWRCTRL MEMPWDINSLEEP: FLASH1PWDSLP (Bit 14) */ +#define PWRCTRL_MEMPWDINSLEEP_FLASH1PWDSLP_Msk (0x4000UL) /*!< PWRCTRL MEMPWDINSLEEP: FLASH1PWDSLP (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MEMPWDINSLEEP_FLASH0PWDSLP_Pos (13UL) /*!< PWRCTRL MEMPWDINSLEEP: FLASH0PWDSLP (Bit 13) */ +#define PWRCTRL_MEMPWDINSLEEP_FLASH0PWDSLP_Msk (0x2000UL) /*!< PWRCTRL MEMPWDINSLEEP: FLASH0PWDSLP (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_Pos (3UL) /*!< PWRCTRL MEMPWDINSLEEP: SRAMPWDSLP (Bit 3) */ +#define PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_Msk (0x1ff8UL) /*!< PWRCTRL MEMPWDINSLEEP: SRAMPWDSLP (Bitfield-Mask: 0x3ff) */ +#define PWRCTRL_MEMPWDINSLEEP_DTCMPWDSLP_Pos (0UL) /*!< PWRCTRL MEMPWDINSLEEP: DTCMPWDSLP (Bit 0) */ +#define PWRCTRL_MEMPWDINSLEEP_DTCMPWDSLP_Msk (0x7UL) /*!< PWRCTRL MEMPWDINSLEEP: DTCMPWDSLP (Bitfield-Mask: 0x07) */ +/* ======================================================= MEMPWREN ======================================================== */ +#define PWRCTRL_MEMPWREN_CACHEB2_Pos (31UL) /*!< PWRCTRL MEMPWREN: CACHEB2 (Bit 31) */ +#define PWRCTRL_MEMPWREN_CACHEB2_Msk (0x80000000UL) /*!< PWRCTRL MEMPWREN: CACHEB2 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MEMPWREN_CACHEB0_Pos (30UL) /*!< PWRCTRL MEMPWREN: CACHEB0 (Bit 30) */ +#define PWRCTRL_MEMPWREN_CACHEB0_Msk (0x40000000UL) /*!< PWRCTRL MEMPWREN: CACHEB0 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MEMPWREN_FLASH1_Pos (14UL) /*!< PWRCTRL MEMPWREN: FLASH1 (Bit 14) */ +#define PWRCTRL_MEMPWREN_FLASH1_Msk (0x4000UL) /*!< PWRCTRL MEMPWREN: FLASH1 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MEMPWREN_FLASH0_Pos (13UL) /*!< PWRCTRL MEMPWREN: FLASH0 (Bit 13) */ +#define PWRCTRL_MEMPWREN_FLASH0_Msk (0x2000UL) /*!< PWRCTRL MEMPWREN: FLASH0 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MEMPWREN_SRAM_Pos (3UL) /*!< PWRCTRL MEMPWREN: SRAM (Bit 3) */ +#define PWRCTRL_MEMPWREN_SRAM_Msk (0x1ff8UL) /*!< PWRCTRL MEMPWREN: SRAM (Bitfield-Mask: 0x3ff) */ +#define PWRCTRL_MEMPWREN_DTCM_Pos (0UL) /*!< PWRCTRL MEMPWREN: DTCM (Bit 0) */ +#define PWRCTRL_MEMPWREN_DTCM_Msk (0x7UL) /*!< PWRCTRL MEMPWREN: DTCM (Bitfield-Mask: 0x07) */ +/* ===================================================== MEMPWRSTATUS ====================================================== */ +#define PWRCTRL_MEMPWRSTATUS_CACHEB2_Pos (16UL) /*!< PWRCTRL MEMPWRSTATUS: CACHEB2 (Bit 16) */ +#define PWRCTRL_MEMPWRSTATUS_CACHEB2_Msk (0x10000UL) /*!< PWRCTRL MEMPWRSTATUS: CACHEB2 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MEMPWRSTATUS_CACHEB0_Pos (15UL) /*!< PWRCTRL MEMPWRSTATUS: CACHEB0 (Bit 15) */ +#define PWRCTRL_MEMPWRSTATUS_CACHEB0_Msk (0x8000UL) /*!< PWRCTRL MEMPWRSTATUS: CACHEB0 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MEMPWRSTATUS_FLASH1_Pos (14UL) /*!< PWRCTRL MEMPWRSTATUS: FLASH1 (Bit 14) */ +#define PWRCTRL_MEMPWRSTATUS_FLASH1_Msk (0x4000UL) /*!< PWRCTRL MEMPWRSTATUS: FLASH1 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MEMPWRSTATUS_FLASH0_Pos (13UL) /*!< PWRCTRL MEMPWRSTATUS: FLASH0 (Bit 13) */ +#define PWRCTRL_MEMPWRSTATUS_FLASH0_Msk (0x2000UL) /*!< PWRCTRL MEMPWRSTATUS: FLASH0 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MEMPWRSTATUS_SRAM9_Pos (12UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM9 (Bit 12) */ +#define PWRCTRL_MEMPWRSTATUS_SRAM9_Msk (0x1000UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM9 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MEMPWRSTATUS_SRAM8_Pos (11UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM8 (Bit 11) */ +#define PWRCTRL_MEMPWRSTATUS_SRAM8_Msk (0x800UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM8 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MEMPWRSTATUS_SRAM7_Pos (10UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM7 (Bit 10) */ +#define PWRCTRL_MEMPWRSTATUS_SRAM7_Msk (0x400UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM7 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MEMPWRSTATUS_SRAM6_Pos (9UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM6 (Bit 9) */ +#define PWRCTRL_MEMPWRSTATUS_SRAM6_Msk (0x200UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM6 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MEMPWRSTATUS_SRAM5_Pos (8UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM5 (Bit 8) */ +#define PWRCTRL_MEMPWRSTATUS_SRAM5_Msk (0x100UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM5 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MEMPWRSTATUS_SRAM4_Pos (7UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM4 (Bit 7) */ +#define PWRCTRL_MEMPWRSTATUS_SRAM4_Msk (0x80UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM4 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MEMPWRSTATUS_SRAM3_Pos (6UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM3 (Bit 6) */ +#define PWRCTRL_MEMPWRSTATUS_SRAM3_Msk (0x40UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM3 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MEMPWRSTATUS_SRAM2_Pos (5UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM2 (Bit 5) */ +#define PWRCTRL_MEMPWRSTATUS_SRAM2_Msk (0x20UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM2 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MEMPWRSTATUS_SRAM1_Pos (4UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM1 (Bit 4) */ +#define PWRCTRL_MEMPWRSTATUS_SRAM1_Msk (0x10UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM1 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MEMPWRSTATUS_SRAM0_Pos (3UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM0 (Bit 3) */ +#define PWRCTRL_MEMPWRSTATUS_SRAM0_Msk (0x8UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM0 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MEMPWRSTATUS_DTCM1_Pos (2UL) /*!< PWRCTRL MEMPWRSTATUS: DTCM1 (Bit 2) */ +#define PWRCTRL_MEMPWRSTATUS_DTCM1_Msk (0x4UL) /*!< PWRCTRL MEMPWRSTATUS: DTCM1 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MEMPWRSTATUS_DTCM01_Pos (1UL) /*!< PWRCTRL MEMPWRSTATUS: DTCM01 (Bit 1) */ +#define PWRCTRL_MEMPWRSTATUS_DTCM01_Msk (0x2UL) /*!< PWRCTRL MEMPWRSTATUS: DTCM01 (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MEMPWRSTATUS_DTCM00_Pos (0UL) /*!< PWRCTRL MEMPWRSTATUS: DTCM00 (Bit 0) */ +#define PWRCTRL_MEMPWRSTATUS_DTCM00_Msk (0x1UL) /*!< PWRCTRL MEMPWRSTATUS: DTCM00 (Bitfield-Mask: 0x01) */ +/* ===================================================== DEVPWRSTATUS ====================================================== */ +#define PWRCTRL_DEVPWRSTATUS_SYSDEEPSLEEP_Pos (31UL) /*!< PWRCTRL DEVPWRSTATUS: SYSDEEPSLEEP (Bit 31) */ +#define PWRCTRL_DEVPWRSTATUS_SYSDEEPSLEEP_Msk (0x80000000UL) /*!< PWRCTRL DEVPWRSTATUS: SYSDEEPSLEEP (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWRSTATUS_COREDEEPSLEEP_Pos (30UL) /*!< PWRCTRL DEVPWRSTATUS: COREDEEPSLEEP (Bit 30) */ +#define PWRCTRL_DEVPWRSTATUS_COREDEEPSLEEP_Msk (0x40000000UL) /*!< PWRCTRL DEVPWRSTATUS: COREDEEPSLEEP (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWRSTATUS_CORESLEEP_Pos (29UL) /*!< PWRCTRL DEVPWRSTATUS: CORESLEEP (Bit 29) */ +#define PWRCTRL_DEVPWRSTATUS_CORESLEEP_Msk (0x20000000UL) /*!< PWRCTRL DEVPWRSTATUS: CORESLEEP (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWRSTATUS_BLEH_Pos (9UL) /*!< PWRCTRL DEVPWRSTATUS: BLEH (Bit 9) */ +#define PWRCTRL_DEVPWRSTATUS_BLEH_Msk (0x200UL) /*!< PWRCTRL DEVPWRSTATUS: BLEH (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWRSTATUS_BLEL_Pos (8UL) /*!< PWRCTRL DEVPWRSTATUS: BLEL (Bit 8) */ +#define PWRCTRL_DEVPWRSTATUS_BLEL_Msk (0x100UL) /*!< PWRCTRL DEVPWRSTATUS: BLEL (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWRSTATUS_PWRPDM_Pos (7UL) /*!< PWRCTRL DEVPWRSTATUS: PWRPDM (Bit 7) */ +#define PWRCTRL_DEVPWRSTATUS_PWRPDM_Msk (0x80UL) /*!< PWRCTRL DEVPWRSTATUS: PWRPDM (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWRSTATUS_PWRMSPI_Pos (6UL) /*!< PWRCTRL DEVPWRSTATUS: PWRMSPI (Bit 6) */ +#define PWRCTRL_DEVPWRSTATUS_PWRMSPI_Msk (0x40UL) /*!< PWRCTRL DEVPWRSTATUS: PWRMSPI (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWRSTATUS_PWRADC_Pos (5UL) /*!< PWRCTRL DEVPWRSTATUS: PWRADC (Bit 5) */ +#define PWRCTRL_DEVPWRSTATUS_PWRADC_Msk (0x20UL) /*!< PWRCTRL DEVPWRSTATUS: PWRADC (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWRSTATUS_HCPC_Pos (4UL) /*!< PWRCTRL DEVPWRSTATUS: HCPC (Bit 4) */ +#define PWRCTRL_DEVPWRSTATUS_HCPC_Msk (0x10UL) /*!< PWRCTRL DEVPWRSTATUS: HCPC (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWRSTATUS_HCPB_Pos (3UL) /*!< PWRCTRL DEVPWRSTATUS: HCPB (Bit 3) */ +#define PWRCTRL_DEVPWRSTATUS_HCPB_Msk (0x8UL) /*!< PWRCTRL DEVPWRSTATUS: HCPB (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWRSTATUS_HCPA_Pos (2UL) /*!< PWRCTRL DEVPWRSTATUS: HCPA (Bit 2) */ +#define PWRCTRL_DEVPWRSTATUS_HCPA_Msk (0x4UL) /*!< PWRCTRL DEVPWRSTATUS: HCPA (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWRSTATUS_MCUH_Pos (1UL) /*!< PWRCTRL DEVPWRSTATUS: MCUH (Bit 1) */ +#define PWRCTRL_DEVPWRSTATUS_MCUH_Msk (0x2UL) /*!< PWRCTRL DEVPWRSTATUS: MCUH (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWRSTATUS_MCUL_Pos (0UL) /*!< PWRCTRL DEVPWRSTATUS: MCUL (Bit 0) */ +#define PWRCTRL_DEVPWRSTATUS_MCUL_Msk (0x1UL) /*!< PWRCTRL DEVPWRSTATUS: MCUL (Bitfield-Mask: 0x01) */ +/* ======================================================= SRAMCTRL ======================================================== */ +#define PWRCTRL_SRAMCTRL_SRAMLIGHTSLEEP_Pos (8UL) /*!< PWRCTRL SRAMCTRL: SRAMLIGHTSLEEP (Bit 8) */ +#define PWRCTRL_SRAMCTRL_SRAMLIGHTSLEEP_Msk (0xfff00UL) /*!< PWRCTRL SRAMCTRL: SRAMLIGHTSLEEP (Bitfield-Mask: 0xfff) */ +#define PWRCTRL_SRAMCTRL_SRAMMASTERCLKGATE_Pos (2UL) /*!< PWRCTRL SRAMCTRL: SRAMMASTERCLKGATE (Bit 2) */ +#define PWRCTRL_SRAMCTRL_SRAMMASTERCLKGATE_Msk (0x4UL) /*!< PWRCTRL SRAMCTRL: SRAMMASTERCLKGATE (Bitfield-Mask: 0x01) */ +#define PWRCTRL_SRAMCTRL_SRAMCLKGATE_Pos (1UL) /*!< PWRCTRL SRAMCTRL: SRAMCLKGATE (Bit 1) */ +#define PWRCTRL_SRAMCTRL_SRAMCLKGATE_Msk (0x2UL) /*!< PWRCTRL SRAMCTRL: SRAMCLKGATE (Bitfield-Mask: 0x01) */ +/* ======================================================= ADCSTATUS ======================================================= */ +#define PWRCTRL_ADCSTATUS_REFBUFPWD_Pos (5UL) /*!< PWRCTRL ADCSTATUS: REFBUFPWD (Bit 5) */ +#define PWRCTRL_ADCSTATUS_REFBUFPWD_Msk (0x20UL) /*!< PWRCTRL ADCSTATUS: REFBUFPWD (Bitfield-Mask: 0x01) */ +#define PWRCTRL_ADCSTATUS_REFKEEPPWD_Pos (4UL) /*!< PWRCTRL ADCSTATUS: REFKEEPPWD (Bit 4) */ +#define PWRCTRL_ADCSTATUS_REFKEEPPWD_Msk (0x10UL) /*!< PWRCTRL ADCSTATUS: REFKEEPPWD (Bitfield-Mask: 0x01) */ +#define PWRCTRL_ADCSTATUS_VBATPWD_Pos (3UL) /*!< PWRCTRL ADCSTATUS: VBATPWD (Bit 3) */ +#define PWRCTRL_ADCSTATUS_VBATPWD_Msk (0x8UL) /*!< PWRCTRL ADCSTATUS: VBATPWD (Bitfield-Mask: 0x01) */ +#define PWRCTRL_ADCSTATUS_VPTATPWD_Pos (2UL) /*!< PWRCTRL ADCSTATUS: VPTATPWD (Bit 2) */ +#define PWRCTRL_ADCSTATUS_VPTATPWD_Msk (0x4UL) /*!< PWRCTRL ADCSTATUS: VPTATPWD (Bitfield-Mask: 0x01) */ +#define PWRCTRL_ADCSTATUS_BGTPWD_Pos (1UL) /*!< PWRCTRL ADCSTATUS: BGTPWD (Bit 1) */ +#define PWRCTRL_ADCSTATUS_BGTPWD_Msk (0x2UL) /*!< PWRCTRL ADCSTATUS: BGTPWD (Bitfield-Mask: 0x01) */ +#define PWRCTRL_ADCSTATUS_ADCPWD_Pos (0UL) /*!< PWRCTRL ADCSTATUS: ADCPWD (Bit 0) */ +#define PWRCTRL_ADCSTATUS_ADCPWD_Msk (0x1UL) /*!< PWRCTRL ADCSTATUS: ADCPWD (Bitfield-Mask: 0x01) */ +/* ========================================================= MISC ========================================================== */ +#define PWRCTRL_MISC_FORCEBLEBUCKACT_Pos (7UL) /*!< PWRCTRL MISC: FORCEBLEBUCKACT (Bit 7) */ +#define PWRCTRL_MISC_FORCEBLEBUCKACT_Msk (0x80UL) /*!< PWRCTRL MISC: FORCEBLEBUCKACT (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MISC_MEMVRLPBLE_Pos (6UL) /*!< PWRCTRL MISC: MEMVRLPBLE (Bit 6) */ +#define PWRCTRL_MISC_MEMVRLPBLE_Msk (0x40UL) /*!< PWRCTRL MISC: MEMVRLPBLE (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MISC_FORCEMEMVRADC_Pos (4UL) /*!< PWRCTRL MISC: FORCEMEMVRADC (Bit 4) */ +#define PWRCTRL_MISC_FORCEMEMVRADC_Msk (0x30UL) /*!< PWRCTRL MISC: FORCEMEMVRADC (Bitfield-Mask: 0x03) */ +#define PWRCTRL_MISC_FORCEMEMVRLPTIMERS_Pos (3UL) /*!< PWRCTRL MISC: FORCEMEMVRLPTIMERS (Bit 3) */ +#define PWRCTRL_MISC_FORCEMEMVRLPTIMERS_Msk (0x8UL) /*!< PWRCTRL MISC: FORCEMEMVRLPTIMERS (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MISC_FORCECOREVRLPTIMERS_Pos (2UL) /*!< PWRCTRL MISC: FORCECOREVRLPTIMERS (Bit 2) */ +#define PWRCTRL_MISC_FORCECOREVRLPTIMERS_Msk (0x4UL) /*!< PWRCTRL MISC: FORCECOREVRLPTIMERS (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MISC_FORCECOREVRLPPDM_Pos (1UL) /*!< PWRCTRL MISC: FORCECOREVRLPPDM (Bit 1) */ +#define PWRCTRL_MISC_FORCECOREVRLPPDM_Msk (0x2UL) /*!< PWRCTRL MISC: FORCECOREVRLPPDM (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MISC_SIMOBUCKEN_Pos (0UL) /*!< PWRCTRL MISC: SIMOBUCKEN (Bit 0) */ +#define PWRCTRL_MISC_SIMOBUCKEN_Msk (0x1UL) /*!< PWRCTRL MISC: SIMOBUCKEN (Bitfield-Mask: 0x01) */ +/* ===================================================== DEVPWREVENTEN ===================================================== */ +#define PWRCTRL_DEVPWREVENTEN_BURSTEVEN_Pos (31UL) /*!< PWRCTRL DEVPWREVENTEN: BURSTEVEN (Bit 31) */ +#define PWRCTRL_DEVPWREVENTEN_BURSTEVEN_Msk (0x80000000UL) /*!< PWRCTRL DEVPWREVENTEN: BURSTEVEN (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWREVENTEN_BURSTFEATUREEVEN_Pos (30UL) /*!< PWRCTRL DEVPWREVENTEN: BURSTFEATUREEVEN (Bit 30) */ +#define PWRCTRL_DEVPWREVENTEN_BURSTFEATUREEVEN_Msk (0x40000000UL) /*!< PWRCTRL DEVPWREVENTEN: BURSTFEATUREEVEN (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWREVENTEN_BLEFEATUREEVEN_Pos (29UL) /*!< PWRCTRL DEVPWREVENTEN: BLEFEATUREEVEN (Bit 29) */ +#define PWRCTRL_DEVPWREVENTEN_BLEFEATUREEVEN_Msk (0x20000000UL) /*!< PWRCTRL DEVPWREVENTEN: BLEFEATUREEVEN (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWREVENTEN_BLELEVEN_Pos (8UL) /*!< PWRCTRL DEVPWREVENTEN: BLELEVEN (Bit 8) */ +#define PWRCTRL_DEVPWREVENTEN_BLELEVEN_Msk (0x100UL) /*!< PWRCTRL DEVPWREVENTEN: BLELEVEN (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWREVENTEN_PDMEVEN_Pos (7UL) /*!< PWRCTRL DEVPWREVENTEN: PDMEVEN (Bit 7) */ +#define PWRCTRL_DEVPWREVENTEN_PDMEVEN_Msk (0x80UL) /*!< PWRCTRL DEVPWREVENTEN: PDMEVEN (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWREVENTEN_MSPIEVEN_Pos (6UL) /*!< PWRCTRL DEVPWREVENTEN: MSPIEVEN (Bit 6) */ +#define PWRCTRL_DEVPWREVENTEN_MSPIEVEN_Msk (0x40UL) /*!< PWRCTRL DEVPWREVENTEN: MSPIEVEN (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWREVENTEN_ADCEVEN_Pos (5UL) /*!< PWRCTRL DEVPWREVENTEN: ADCEVEN (Bit 5) */ +#define PWRCTRL_DEVPWREVENTEN_ADCEVEN_Msk (0x20UL) /*!< PWRCTRL DEVPWREVENTEN: ADCEVEN (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWREVENTEN_HCPCEVEN_Pos (4UL) /*!< PWRCTRL DEVPWREVENTEN: HCPCEVEN (Bit 4) */ +#define PWRCTRL_DEVPWREVENTEN_HCPCEVEN_Msk (0x10UL) /*!< PWRCTRL DEVPWREVENTEN: HCPCEVEN (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWREVENTEN_HCPBEVEN_Pos (3UL) /*!< PWRCTRL DEVPWREVENTEN: HCPBEVEN (Bit 3) */ +#define PWRCTRL_DEVPWREVENTEN_HCPBEVEN_Msk (0x8UL) /*!< PWRCTRL DEVPWREVENTEN: HCPBEVEN (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWREVENTEN_HCPAEVEN_Pos (2UL) /*!< PWRCTRL DEVPWREVENTEN: HCPAEVEN (Bit 2) */ +#define PWRCTRL_DEVPWREVENTEN_HCPAEVEN_Msk (0x4UL) /*!< PWRCTRL DEVPWREVENTEN: HCPAEVEN (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWREVENTEN_MCUHEVEN_Pos (1UL) /*!< PWRCTRL DEVPWREVENTEN: MCUHEVEN (Bit 1) */ +#define PWRCTRL_DEVPWREVENTEN_MCUHEVEN_Msk (0x2UL) /*!< PWRCTRL DEVPWREVENTEN: MCUHEVEN (Bitfield-Mask: 0x01) */ +#define PWRCTRL_DEVPWREVENTEN_MCULEVEN_Pos (0UL) /*!< PWRCTRL DEVPWREVENTEN: MCULEVEN (Bit 0) */ +#define PWRCTRL_DEVPWREVENTEN_MCULEVEN_Msk (0x1UL) /*!< PWRCTRL DEVPWREVENTEN: MCULEVEN (Bitfield-Mask: 0x01) */ +/* ===================================================== MEMPWREVENTEN ===================================================== */ +#define PWRCTRL_MEMPWREVENTEN_CACHEB2EN_Pos (31UL) /*!< PWRCTRL MEMPWREVENTEN: CACHEB2EN (Bit 31) */ +#define PWRCTRL_MEMPWREVENTEN_CACHEB2EN_Msk (0x80000000UL) /*!< PWRCTRL MEMPWREVENTEN: CACHEB2EN (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MEMPWREVENTEN_CACHEB0EN_Pos (30UL) /*!< PWRCTRL MEMPWREVENTEN: CACHEB0EN (Bit 30) */ +#define PWRCTRL_MEMPWREVENTEN_CACHEB0EN_Msk (0x40000000UL) /*!< PWRCTRL MEMPWREVENTEN: CACHEB0EN (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MEMPWREVENTEN_FLASH1EN_Pos (14UL) /*!< PWRCTRL MEMPWREVENTEN: FLASH1EN (Bit 14) */ +#define PWRCTRL_MEMPWREVENTEN_FLASH1EN_Msk (0x4000UL) /*!< PWRCTRL MEMPWREVENTEN: FLASH1EN (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MEMPWREVENTEN_FLASH0EN_Pos (13UL) /*!< PWRCTRL MEMPWREVENTEN: FLASH0EN (Bit 13) */ +#define PWRCTRL_MEMPWREVENTEN_FLASH0EN_Msk (0x2000UL) /*!< PWRCTRL MEMPWREVENTEN: FLASH0EN (Bitfield-Mask: 0x01) */ +#define PWRCTRL_MEMPWREVENTEN_SRAMEN_Pos (3UL) /*!< PWRCTRL MEMPWREVENTEN: SRAMEN (Bit 3) */ +#define PWRCTRL_MEMPWREVENTEN_SRAMEN_Msk (0x1ff8UL) /*!< PWRCTRL MEMPWREVENTEN: SRAMEN (Bitfield-Mask: 0x3ff) */ +#define PWRCTRL_MEMPWREVENTEN_DTCMEN_Pos (0UL) /*!< PWRCTRL MEMPWREVENTEN: DTCMEN (Bit 0) */ +#define PWRCTRL_MEMPWREVENTEN_DTCMEN_Msk (0x7UL) /*!< PWRCTRL MEMPWREVENTEN: DTCMEN (Bitfield-Mask: 0x07) */ + + +/* =========================================================================================================================== */ +/* ================ RSTGEN ================ */ +/* =========================================================================================================================== */ + +/* ========================================================== CFG ========================================================== */ +#define RSTGEN_CFG_WDREN_Pos (1UL) /*!< RSTGEN CFG: WDREN (Bit 1) */ +#define RSTGEN_CFG_WDREN_Msk (0x2UL) /*!< RSTGEN CFG: WDREN (Bitfield-Mask: 0x01) */ +#define RSTGEN_CFG_BODHREN_Pos (0UL) /*!< RSTGEN CFG: BODHREN (Bit 0) */ +#define RSTGEN_CFG_BODHREN_Msk (0x1UL) /*!< RSTGEN CFG: BODHREN (Bitfield-Mask: 0x01) */ +/* ========================================================= SWPOI ========================================================= */ +#define RSTGEN_SWPOI_SWPOIKEY_Pos (0UL) /*!< RSTGEN SWPOI: SWPOIKEY (Bit 0) */ +#define RSTGEN_SWPOI_SWPOIKEY_Msk (0xffUL) /*!< RSTGEN SWPOI: SWPOIKEY (Bitfield-Mask: 0xff) */ +/* ========================================================= SWPOR ========================================================= */ +#define RSTGEN_SWPOR_SWPORKEY_Pos (0UL) /*!< RSTGEN SWPOR: SWPORKEY (Bit 0) */ +#define RSTGEN_SWPOR_SWPORKEY_Msk (0xffUL) /*!< RSTGEN SWPOR: SWPORKEY (Bitfield-Mask: 0xff) */ +/* ======================================================== TPIURST ======================================================== */ +#define RSTGEN_TPIURST_TPIURST_Pos (0UL) /*!< RSTGEN TPIURST: TPIURST (Bit 0) */ +#define RSTGEN_TPIURST_TPIURST_Msk (0x1UL) /*!< RSTGEN TPIURST: TPIURST (Bitfield-Mask: 0x01) */ +/* ========================================================= INTEN ========================================================= */ +#define RSTGEN_INTEN_BODH_Pos (0UL) /*!< RSTGEN INTEN: BODH (Bit 0) */ +#define RSTGEN_INTEN_BODH_Msk (0x1UL) /*!< RSTGEN INTEN: BODH (Bitfield-Mask: 0x01) */ +/* ======================================================== INTSTAT ======================================================== */ +#define RSTGEN_INTSTAT_BODH_Pos (0UL) /*!< RSTGEN INTSTAT: BODH (Bit 0) */ +#define RSTGEN_INTSTAT_BODH_Msk (0x1UL) /*!< RSTGEN INTSTAT: BODH (Bitfield-Mask: 0x01) */ +/* ======================================================== INTCLR ========================================================= */ +#define RSTGEN_INTCLR_BODH_Pos (0UL) /*!< RSTGEN INTCLR: BODH (Bit 0) */ +#define RSTGEN_INTCLR_BODH_Msk (0x1UL) /*!< RSTGEN INTCLR: BODH (Bitfield-Mask: 0x01) */ +/* ======================================================== INTSET ========================================================= */ +#define RSTGEN_INTSET_BODH_Pos (0UL) /*!< RSTGEN INTSET: BODH (Bit 0) */ +#define RSTGEN_INTSET_BODH_Msk (0x1UL) /*!< RSTGEN INTSET: BODH (Bitfield-Mask: 0x01) */ +/* ========================================================= STAT ========================================================== */ +#define RSTGEN_STAT_SBOOT_Pos (31UL) /*!< RSTGEN STAT: SBOOT (Bit 31) */ +#define RSTGEN_STAT_SBOOT_Msk (0x80000000UL) /*!< RSTGEN STAT: SBOOT (Bitfield-Mask: 0x01) */ +#define RSTGEN_STAT_FBOOT_Pos (30UL) /*!< RSTGEN STAT: FBOOT (Bit 30) */ +#define RSTGEN_STAT_FBOOT_Msk (0x40000000UL) /*!< RSTGEN STAT: FBOOT (Bitfield-Mask: 0x01) */ +#define RSTGEN_STAT_BOBSTAT_Pos (10UL) /*!< RSTGEN STAT: BOBSTAT (Bit 10) */ +#define RSTGEN_STAT_BOBSTAT_Msk (0x400UL) /*!< RSTGEN STAT: BOBSTAT (Bitfield-Mask: 0x01) */ +#define RSTGEN_STAT_BOFSTAT_Pos (9UL) /*!< RSTGEN STAT: BOFSTAT (Bit 9) */ +#define RSTGEN_STAT_BOFSTAT_Msk (0x200UL) /*!< RSTGEN STAT: BOFSTAT (Bitfield-Mask: 0x01) */ +#define RSTGEN_STAT_BOCSTAT_Pos (8UL) /*!< RSTGEN STAT: BOCSTAT (Bit 8) */ +#define RSTGEN_STAT_BOCSTAT_Msk (0x100UL) /*!< RSTGEN STAT: BOCSTAT (Bitfield-Mask: 0x01) */ +#define RSTGEN_STAT_BOUSTAT_Pos (7UL) /*!< RSTGEN STAT: BOUSTAT (Bit 7) */ +#define RSTGEN_STAT_BOUSTAT_Msk (0x80UL) /*!< RSTGEN STAT: BOUSTAT (Bitfield-Mask: 0x01) */ +#define RSTGEN_STAT_WDRSTAT_Pos (6UL) /*!< RSTGEN STAT: WDRSTAT (Bit 6) */ +#define RSTGEN_STAT_WDRSTAT_Msk (0x40UL) /*!< RSTGEN STAT: WDRSTAT (Bitfield-Mask: 0x01) */ +#define RSTGEN_STAT_DBGRSTAT_Pos (5UL) /*!< RSTGEN STAT: DBGRSTAT (Bit 5) */ +#define RSTGEN_STAT_DBGRSTAT_Msk (0x20UL) /*!< RSTGEN STAT: DBGRSTAT (Bitfield-Mask: 0x01) */ +#define RSTGEN_STAT_POIRSTAT_Pos (4UL) /*!< RSTGEN STAT: POIRSTAT (Bit 4) */ +#define RSTGEN_STAT_POIRSTAT_Msk (0x10UL) /*!< RSTGEN STAT: POIRSTAT (Bitfield-Mask: 0x01) */ +#define RSTGEN_STAT_SWRSTAT_Pos (3UL) /*!< RSTGEN STAT: SWRSTAT (Bit 3) */ +#define RSTGEN_STAT_SWRSTAT_Msk (0x8UL) /*!< RSTGEN STAT: SWRSTAT (Bitfield-Mask: 0x01) */ +#define RSTGEN_STAT_BORSTAT_Pos (2UL) /*!< RSTGEN STAT: BORSTAT (Bit 2) */ +#define RSTGEN_STAT_BORSTAT_Msk (0x4UL) /*!< RSTGEN STAT: BORSTAT (Bitfield-Mask: 0x01) */ +#define RSTGEN_STAT_PORSTAT_Pos (1UL) /*!< RSTGEN STAT: PORSTAT (Bit 1) */ +#define RSTGEN_STAT_PORSTAT_Msk (0x2UL) /*!< RSTGEN STAT: PORSTAT (Bitfield-Mask: 0x01) */ +#define RSTGEN_STAT_EXRSTAT_Pos (0UL) /*!< RSTGEN STAT: EXRSTAT (Bit 0) */ +#define RSTGEN_STAT_EXRSTAT_Msk (0x1UL) /*!< RSTGEN STAT: EXRSTAT (Bitfield-Mask: 0x01) */ + + +/* =========================================================================================================================== */ +/* ================ RTC ================ */ +/* =========================================================================================================================== */ + +/* ======================================================== CTRLOW ========================================================= */ +#define RTC_CTRLOW_CTRHR_Pos (24UL) /*!< RTC CTRLOW: CTRHR (Bit 24) */ +#define RTC_CTRLOW_CTRHR_Msk (0x3f000000UL) /*!< RTC CTRLOW: CTRHR (Bitfield-Mask: 0x3f) */ +#define RTC_CTRLOW_CTRMIN_Pos (16UL) /*!< RTC CTRLOW: CTRMIN (Bit 16) */ +#define RTC_CTRLOW_CTRMIN_Msk (0x7f0000UL) /*!< RTC CTRLOW: CTRMIN (Bitfield-Mask: 0x7f) */ +#define RTC_CTRLOW_CTRSEC_Pos (8UL) /*!< RTC CTRLOW: CTRSEC (Bit 8) */ +#define RTC_CTRLOW_CTRSEC_Msk (0x7f00UL) /*!< RTC CTRLOW: CTRSEC (Bitfield-Mask: 0x7f) */ +#define RTC_CTRLOW_CTR100_Pos (0UL) /*!< RTC CTRLOW: CTR100 (Bit 0) */ +#define RTC_CTRLOW_CTR100_Msk (0xffUL) /*!< RTC CTRLOW: CTR100 (Bitfield-Mask: 0xff) */ +/* ========================================================= CTRUP ========================================================= */ +#define RTC_CTRUP_CTERR_Pos (31UL) /*!< RTC CTRUP: CTERR (Bit 31) */ +#define RTC_CTRUP_CTERR_Msk (0x80000000UL) /*!< RTC CTRUP: CTERR (Bitfield-Mask: 0x01) */ +#define RTC_CTRUP_CEB_Pos (28UL) /*!< RTC CTRUP: CEB (Bit 28) */ +#define RTC_CTRUP_CEB_Msk (0x10000000UL) /*!< RTC CTRUP: CEB (Bitfield-Mask: 0x01) */ +#define RTC_CTRUP_CB_Pos (27UL) /*!< RTC CTRUP: CB (Bit 27) */ +#define RTC_CTRUP_CB_Msk (0x8000000UL) /*!< RTC CTRUP: CB (Bitfield-Mask: 0x01) */ +#define RTC_CTRUP_CTRWKDY_Pos (24UL) /*!< RTC CTRUP: CTRWKDY (Bit 24) */ +#define RTC_CTRUP_CTRWKDY_Msk (0x7000000UL) /*!< RTC CTRUP: CTRWKDY (Bitfield-Mask: 0x07) */ +#define RTC_CTRUP_CTRYR_Pos (16UL) /*!< RTC CTRUP: CTRYR (Bit 16) */ +#define RTC_CTRUP_CTRYR_Msk (0xff0000UL) /*!< RTC CTRUP: CTRYR (Bitfield-Mask: 0xff) */ +#define RTC_CTRUP_CTRMO_Pos (8UL) /*!< RTC CTRUP: CTRMO (Bit 8) */ +#define RTC_CTRUP_CTRMO_Msk (0x1f00UL) /*!< RTC CTRUP: CTRMO (Bitfield-Mask: 0x1f) */ +#define RTC_CTRUP_CTRDATE_Pos (0UL) /*!< RTC CTRUP: CTRDATE (Bit 0) */ +#define RTC_CTRUP_CTRDATE_Msk (0x3fUL) /*!< RTC CTRUP: CTRDATE (Bitfield-Mask: 0x3f) */ +/* ======================================================== ALMLOW ========================================================= */ +#define RTC_ALMLOW_ALMHR_Pos (24UL) /*!< RTC ALMLOW: ALMHR (Bit 24) */ +#define RTC_ALMLOW_ALMHR_Msk (0x3f000000UL) /*!< RTC ALMLOW: ALMHR (Bitfield-Mask: 0x3f) */ +#define RTC_ALMLOW_ALMMIN_Pos (16UL) /*!< RTC ALMLOW: ALMMIN (Bit 16) */ +#define RTC_ALMLOW_ALMMIN_Msk (0x7f0000UL) /*!< RTC ALMLOW: ALMMIN (Bitfield-Mask: 0x7f) */ +#define RTC_ALMLOW_ALMSEC_Pos (8UL) /*!< RTC ALMLOW: ALMSEC (Bit 8) */ +#define RTC_ALMLOW_ALMSEC_Msk (0x7f00UL) /*!< RTC ALMLOW: ALMSEC (Bitfield-Mask: 0x7f) */ +#define RTC_ALMLOW_ALM100_Pos (0UL) /*!< RTC ALMLOW: ALM100 (Bit 0) */ +#define RTC_ALMLOW_ALM100_Msk (0xffUL) /*!< RTC ALMLOW: ALM100 (Bitfield-Mask: 0xff) */ +/* ========================================================= ALMUP ========================================================= */ +#define RTC_ALMUP_ALMWKDY_Pos (16UL) /*!< RTC ALMUP: ALMWKDY (Bit 16) */ +#define RTC_ALMUP_ALMWKDY_Msk (0x70000UL) /*!< RTC ALMUP: ALMWKDY (Bitfield-Mask: 0x07) */ +#define RTC_ALMUP_ALMMO_Pos (8UL) /*!< RTC ALMUP: ALMMO (Bit 8) */ +#define RTC_ALMUP_ALMMO_Msk (0x1f00UL) /*!< RTC ALMUP: ALMMO (Bitfield-Mask: 0x1f) */ +#define RTC_ALMUP_ALMDATE_Pos (0UL) /*!< RTC ALMUP: ALMDATE (Bit 0) */ +#define RTC_ALMUP_ALMDATE_Msk (0x3fUL) /*!< RTC ALMUP: ALMDATE (Bitfield-Mask: 0x3f) */ +/* ======================================================== RTCCTL ========================================================= */ +#define RTC_RTCCTL_HR1224_Pos (5UL) /*!< RTC RTCCTL: HR1224 (Bit 5) */ +#define RTC_RTCCTL_HR1224_Msk (0x20UL) /*!< RTC RTCCTL: HR1224 (Bitfield-Mask: 0x01) */ +#define RTC_RTCCTL_RSTOP_Pos (4UL) /*!< RTC RTCCTL: RSTOP (Bit 4) */ +#define RTC_RTCCTL_RSTOP_Msk (0x10UL) /*!< RTC RTCCTL: RSTOP (Bitfield-Mask: 0x01) */ +#define RTC_RTCCTL_RPT_Pos (1UL) /*!< RTC RTCCTL: RPT (Bit 1) */ +#define RTC_RTCCTL_RPT_Msk (0xeUL) /*!< RTC RTCCTL: RPT (Bitfield-Mask: 0x07) */ +#define RTC_RTCCTL_WRTC_Pos (0UL) /*!< RTC RTCCTL: WRTC (Bit 0) */ +#define RTC_RTCCTL_WRTC_Msk (0x1UL) /*!< RTC RTCCTL: WRTC (Bitfield-Mask: 0x01) */ +/* ========================================================= INTEN ========================================================= */ +#define RTC_INTEN_ALM_Pos (0UL) /*!< RTC INTEN: ALM (Bit 0) */ +#define RTC_INTEN_ALM_Msk (0x1UL) /*!< RTC INTEN: ALM (Bitfield-Mask: 0x01) */ +/* ======================================================== INTSTAT ======================================================== */ +#define RTC_INTSTAT_ALM_Pos (0UL) /*!< RTC INTSTAT: ALM (Bit 0) */ +#define RTC_INTSTAT_ALM_Msk (0x1UL) /*!< RTC INTSTAT: ALM (Bitfield-Mask: 0x01) */ +/* ======================================================== INTCLR ========================================================= */ +#define RTC_INTCLR_ALM_Pos (0UL) /*!< RTC INTCLR: ALM (Bit 0) */ +#define RTC_INTCLR_ALM_Msk (0x1UL) /*!< RTC INTCLR: ALM (Bitfield-Mask: 0x01) */ +/* ======================================================== INTSET ========================================================= */ +#define RTC_INTSET_ALM_Pos (0UL) /*!< RTC INTSET: ALM (Bit 0) */ +#define RTC_INTSET_ALM_Msk (0x1UL) /*!< RTC INTSET: ALM (Bitfield-Mask: 0x01) */ + + +/* =========================================================================================================================== */ +/* ================ SCARD ================ */ +/* =========================================================================================================================== */ + +/* ========================================================== SR =========================================================== */ +#define SCARD_SR_FHF_Pos (6UL) /*!< SCARD SR: FHF (Bit 6) */ +#define SCARD_SR_FHF_Msk (0x40UL) /*!< SCARD SR: FHF (Bitfield-Mask: 0x01) */ +#define SCARD_SR_FT2REND_Pos (5UL) /*!< SCARD SR: FT2REND (Bit 5) */ +#define SCARD_SR_FT2REND_Msk (0x20UL) /*!< SCARD SR: FT2REND (Bitfield-Mask: 0x01) */ +#define SCARD_SR_PE_Pos (4UL) /*!< SCARD SR: PE (Bit 4) */ +#define SCARD_SR_PE_Msk (0x10UL) /*!< SCARD SR: PE (Bitfield-Mask: 0x01) */ +#define SCARD_SR_OVR_Pos (3UL) /*!< SCARD SR: OVR (Bit 3) */ +#define SCARD_SR_OVR_Msk (0x8UL) /*!< SCARD SR: OVR (Bitfield-Mask: 0x01) */ +#define SCARD_SR_FER_Pos (2UL) /*!< SCARD SR: FER (Bit 2) */ +#define SCARD_SR_FER_Msk (0x4UL) /*!< SCARD SR: FER (Bitfield-Mask: 0x01) */ +#define SCARD_SR_TBERBF_Pos (1UL) /*!< SCARD SR: TBERBF (Bit 1) */ +#define SCARD_SR_TBERBF_Msk (0x2UL) /*!< SCARD SR: TBERBF (Bitfield-Mask: 0x01) */ +#define SCARD_SR_FNE_Pos (0UL) /*!< SCARD SR: FNE (Bit 0) */ +#define SCARD_SR_FNE_Msk (0x1UL) /*!< SCARD SR: FNE (Bitfield-Mask: 0x01) */ +/* ========================================================== DR =========================================================== */ +#define SCARD_DR_DR_Pos (0UL) /*!< SCARD DR: DR (Bit 0) */ +#define SCARD_DR_DR_Msk (0xffUL) /*!< SCARD DR: DR (Bitfield-Mask: 0xff) */ +/* ========================================================== SR1 ========================================================== */ +#define SCARD_SR1_IDLE_Pos (3UL) /*!< SCARD SR1: IDLE (Bit 3) */ +#define SCARD_SR1_IDLE_Msk (0x8UL) /*!< SCARD SR1: IDLE (Bitfield-Mask: 0x01) */ +#define SCARD_SR1_SYNCEND_Pos (2UL) /*!< SCARD SR1: SYNCEND (Bit 2) */ +#define SCARD_SR1_SYNCEND_Msk (0x4UL) /*!< SCARD SR1: SYNCEND (Bitfield-Mask: 0x01) */ +#define SCARD_SR1_PRL_Pos (1UL) /*!< SCARD SR1: PRL (Bit 1) */ +#define SCARD_SR1_PRL_Msk (0x2UL) /*!< SCARD SR1: PRL (Bitfield-Mask: 0x01) */ +#define SCARD_SR1_ECNTOVER_Pos (0UL) /*!< SCARD SR1: ECNTOVER (Bit 0) */ +#define SCARD_SR1_ECNTOVER_Msk (0x1UL) /*!< SCARD SR1: ECNTOVER (Bitfield-Mask: 0x01) */ +/* ====================================================== RETXCNTRMI ======================================================= */ +#define SCARD_RETXCNTRMI_RETXCNTRMI_Pos (0UL) /*!< SCARD RETXCNTRMI: RETXCNTRMI (Bit 0) */ +#define SCARD_RETXCNTRMI_RETXCNTRMI_Msk (0xfUL) /*!< SCARD RETXCNTRMI: RETXCNTRMI (Bitfield-Mask: 0x0f) */ +/* ======================================================== CLKCTRL ======================================================== */ +#define SCARD_CLKCTRL_APBCLKEN_Pos (1UL) /*!< SCARD CLKCTRL: APBCLKEN (Bit 1) */ +#define SCARD_CLKCTRL_APBCLKEN_Msk (0x2UL) /*!< SCARD CLKCTRL: APBCLKEN (Bitfield-Mask: 0x01) */ +#define SCARD_CLKCTRL_CLKEN_Pos (0UL) /*!< SCARD CLKCTRL: CLKEN (Bit 0) */ +#define SCARD_CLKCTRL_CLKEN_Msk (0x1UL) /*!< SCARD CLKCTRL: CLKEN (Bitfield-Mask: 0x01) */ + + +/* =========================================================================================================================== */ +/* ================ SECURITY ================ */ +/* =========================================================================================================================== */ + +/* ========================================================= CTRL ========================================================== */ +#define SECURITY_CTRL_CRCERROR_Pos (31UL) /*!< SECURITY CTRL: CRCERROR (Bit 31) */ +#define SECURITY_CTRL_CRCERROR_Msk (0x80000000UL) /*!< SECURITY CTRL: CRCERROR (Bitfield-Mask: 0x01) */ +#define SECURITY_CTRL_FUNCTION_Pos (4UL) /*!< SECURITY CTRL: FUNCTION (Bit 4) */ +#define SECURITY_CTRL_FUNCTION_Msk (0xf0UL) /*!< SECURITY CTRL: FUNCTION (Bitfield-Mask: 0x0f) */ +#define SECURITY_CTRL_ENABLE_Pos (0UL) /*!< SECURITY CTRL: ENABLE (Bit 0) */ +#define SECURITY_CTRL_ENABLE_Msk (0x1UL) /*!< SECURITY CTRL: ENABLE (Bitfield-Mask: 0x01) */ +/* ======================================================== SRCADDR ======================================================== */ +#define SECURITY_SRCADDR_ADDR_Pos (0UL) /*!< SECURITY SRCADDR: ADDR (Bit 0) */ +#define SECURITY_SRCADDR_ADDR_Msk (0xffffffffUL) /*!< SECURITY SRCADDR: ADDR (Bitfield-Mask: 0xffffffff) */ +/* ========================================================== LEN ========================================================== */ +#define SECURITY_LEN_LEN_Pos (2UL) /*!< SECURITY LEN: LEN (Bit 2) */ +#define SECURITY_LEN_LEN_Msk (0xffffcUL) /*!< SECURITY LEN: LEN (Bitfield-Mask: 0x3ffff) */ +/* ======================================================== RESULT ========================================================= */ +#define SECURITY_RESULT_CRC_Pos (0UL) /*!< SECURITY RESULT: CRC (Bit 0) */ +#define SECURITY_RESULT_CRC_Msk (0xffffffffUL) /*!< SECURITY RESULT: CRC (Bitfield-Mask: 0xffffffff) */ +/* ======================================================= LOCKCTRL ======================================================== */ +#define SECURITY_LOCKCTRL_SELECT_Pos (0UL) /*!< SECURITY LOCKCTRL: SELECT (Bit 0) */ +#define SECURITY_LOCKCTRL_SELECT_Msk (0xffUL) /*!< SECURITY LOCKCTRL: SELECT (Bitfield-Mask: 0xff) */ +/* ======================================================= LOCKSTAT ======================================================== */ +#define SECURITY_LOCKSTAT_STATUS_Pos (0UL) /*!< SECURITY LOCKSTAT: STATUS (Bit 0) */ +#define SECURITY_LOCKSTAT_STATUS_Msk (0xffffffffUL) /*!< SECURITY LOCKSTAT: STATUS (Bitfield-Mask: 0xffffffff) */ +/* ========================================================= KEY0 ========================================================== */ +#define SECURITY_KEY0_KEY0_Pos (0UL) /*!< SECURITY KEY0: KEY0 (Bit 0) */ +#define SECURITY_KEY0_KEY0_Msk (0xffffffffUL) /*!< SECURITY KEY0: KEY0 (Bitfield-Mask: 0xffffffff) */ +/* ========================================================= KEY1 ========================================================== */ +#define SECURITY_KEY1_KEY1_Pos (0UL) /*!< SECURITY KEY1: KEY1 (Bit 0) */ +#define SECURITY_KEY1_KEY1_Msk (0xffffffffUL) /*!< SECURITY KEY1: KEY1 (Bitfield-Mask: 0xffffffff) */ +/* ========================================================= KEY2 ========================================================== */ +#define SECURITY_KEY2_KEY2_Pos (0UL) /*!< SECURITY KEY2: KEY2 (Bit 0) */ +#define SECURITY_KEY2_KEY2_Msk (0xffffffffUL) /*!< SECURITY KEY2: KEY2 (Bitfield-Mask: 0xffffffff) */ +/* ========================================================= KEY3 ========================================================== */ +#define SECURITY_KEY3_KEY3_Pos (0UL) /*!< SECURITY KEY3: KEY3 (Bit 0) */ +#define SECURITY_KEY3_KEY3_Msk (0xffffffffUL) /*!< SECURITY KEY3: KEY3 (Bitfield-Mask: 0xffffffff) */ + + +/* =========================================================================================================================== */ +/* ================ UART0 ================ */ +/* =========================================================================================================================== */ + +/* ========================================================== DR =========================================================== */ +#define UART0_DR_OEDATA_Pos (11UL) /*!< UART0 DR: OEDATA (Bit 11) */ +#define UART0_DR_OEDATA_Msk (0x800UL) /*!< UART0 DR: OEDATA (Bitfield-Mask: 0x01) */ +#define UART0_DR_BEDATA_Pos (10UL) /*!< UART0 DR: BEDATA (Bit 10) */ +#define UART0_DR_BEDATA_Msk (0x400UL) /*!< UART0 DR: BEDATA (Bitfield-Mask: 0x01) */ +#define UART0_DR_PEDATA_Pos (9UL) /*!< UART0 DR: PEDATA (Bit 9) */ +#define UART0_DR_PEDATA_Msk (0x200UL) /*!< UART0 DR: PEDATA (Bitfield-Mask: 0x01) */ +#define UART0_DR_FEDATA_Pos (8UL) /*!< UART0 DR: FEDATA (Bit 8) */ +#define UART0_DR_FEDATA_Msk (0x100UL) /*!< UART0 DR: FEDATA (Bitfield-Mask: 0x01) */ +#define UART0_DR_DATA_Pos (0UL) /*!< UART0 DR: DATA (Bit 0) */ +#define UART0_DR_DATA_Msk (0xffUL) /*!< UART0 DR: DATA (Bitfield-Mask: 0xff) */ +/* ========================================================== RSR ========================================================== */ +#define UART0_RSR_OESTAT_Pos (3UL) /*!< UART0 RSR: OESTAT (Bit 3) */ +#define UART0_RSR_OESTAT_Msk (0x8UL) /*!< UART0 RSR: OESTAT (Bitfield-Mask: 0x01) */ +#define UART0_RSR_BESTAT_Pos (2UL) /*!< UART0 RSR: BESTAT (Bit 2) */ +#define UART0_RSR_BESTAT_Msk (0x4UL) /*!< UART0 RSR: BESTAT (Bitfield-Mask: 0x01) */ +#define UART0_RSR_PESTAT_Pos (1UL) /*!< UART0 RSR: PESTAT (Bit 1) */ +#define UART0_RSR_PESTAT_Msk (0x2UL) /*!< UART0 RSR: PESTAT (Bitfield-Mask: 0x01) */ +#define UART0_RSR_FESTAT_Pos (0UL) /*!< UART0 RSR: FESTAT (Bit 0) */ +#define UART0_RSR_FESTAT_Msk (0x1UL) /*!< UART0 RSR: FESTAT (Bitfield-Mask: 0x01) */ +/* ========================================================== FR =========================================================== */ +#define UART0_FR_TXBUSY_Pos (8UL) /*!< UART0 FR: TXBUSY (Bit 8) */ +#define UART0_FR_TXBUSY_Msk (0x100UL) /*!< UART0 FR: TXBUSY (Bitfield-Mask: 0x01) */ +#define UART0_FR_TXFE_Pos (7UL) /*!< UART0 FR: TXFE (Bit 7) */ +#define UART0_FR_TXFE_Msk (0x80UL) /*!< UART0 FR: TXFE (Bitfield-Mask: 0x01) */ +#define UART0_FR_RXFF_Pos (6UL) /*!< UART0 FR: RXFF (Bit 6) */ +#define UART0_FR_RXFF_Msk (0x40UL) /*!< UART0 FR: RXFF (Bitfield-Mask: 0x01) */ +#define UART0_FR_TXFF_Pos (5UL) /*!< UART0 FR: TXFF (Bit 5) */ +#define UART0_FR_TXFF_Msk (0x20UL) /*!< UART0 FR: TXFF (Bitfield-Mask: 0x01) */ +#define UART0_FR_RXFE_Pos (4UL) /*!< UART0 FR: RXFE (Bit 4) */ +#define UART0_FR_RXFE_Msk (0x10UL) /*!< UART0 FR: RXFE (Bitfield-Mask: 0x01) */ +#define UART0_FR_BUSY_Pos (3UL) /*!< UART0 FR: BUSY (Bit 3) */ +#define UART0_FR_BUSY_Msk (0x8UL) /*!< UART0 FR: BUSY (Bitfield-Mask: 0x01) */ +#define UART0_FR_DCD_Pos (2UL) /*!< UART0 FR: DCD (Bit 2) */ +#define UART0_FR_DCD_Msk (0x4UL) /*!< UART0 FR: DCD (Bitfield-Mask: 0x01) */ +#define UART0_FR_DSR_Pos (1UL) /*!< UART0 FR: DSR (Bit 1) */ +#define UART0_FR_DSR_Msk (0x2UL) /*!< UART0 FR: DSR (Bitfield-Mask: 0x01) */ +#define UART0_FR_CTS_Pos (0UL) /*!< UART0 FR: CTS (Bit 0) */ +#define UART0_FR_CTS_Msk (0x1UL) /*!< UART0 FR: CTS (Bitfield-Mask: 0x01) */ +/* ========================================================= ILPR ========================================================== */ +#define UART0_ILPR_ILPDVSR_Pos (0UL) /*!< UART0 ILPR: ILPDVSR (Bit 0) */ +#define UART0_ILPR_ILPDVSR_Msk (0xffUL) /*!< UART0 ILPR: ILPDVSR (Bitfield-Mask: 0xff) */ +/* ========================================================= IBRD ========================================================== */ +#define UART0_IBRD_DIVINT_Pos (0UL) /*!< UART0 IBRD: DIVINT (Bit 0) */ +#define UART0_IBRD_DIVINT_Msk (0xffffUL) /*!< UART0 IBRD: DIVINT (Bitfield-Mask: 0xffff) */ +/* ========================================================= FBRD ========================================================== */ +#define UART0_FBRD_DIVFRAC_Pos (0UL) /*!< UART0 FBRD: DIVFRAC (Bit 0) */ +#define UART0_FBRD_DIVFRAC_Msk (0x3fUL) /*!< UART0 FBRD: DIVFRAC (Bitfield-Mask: 0x3f) */ +/* ========================================================= LCRH ========================================================== */ +#define UART0_LCRH_SPS_Pos (7UL) /*!< UART0 LCRH: SPS (Bit 7) */ +#define UART0_LCRH_SPS_Msk (0x80UL) /*!< UART0 LCRH: SPS (Bitfield-Mask: 0x01) */ +#define UART0_LCRH_WLEN_Pos (5UL) /*!< UART0 LCRH: WLEN (Bit 5) */ +#define UART0_LCRH_WLEN_Msk (0x60UL) /*!< UART0 LCRH: WLEN (Bitfield-Mask: 0x03) */ +#define UART0_LCRH_FEN_Pos (4UL) /*!< UART0 LCRH: FEN (Bit 4) */ +#define UART0_LCRH_FEN_Msk (0x10UL) /*!< UART0 LCRH: FEN (Bitfield-Mask: 0x01) */ +#define UART0_LCRH_STP2_Pos (3UL) /*!< UART0 LCRH: STP2 (Bit 3) */ +#define UART0_LCRH_STP2_Msk (0x8UL) /*!< UART0 LCRH: STP2 (Bitfield-Mask: 0x01) */ +#define UART0_LCRH_EPS_Pos (2UL) /*!< UART0 LCRH: EPS (Bit 2) */ +#define UART0_LCRH_EPS_Msk (0x4UL) /*!< UART0 LCRH: EPS (Bitfield-Mask: 0x01) */ +#define UART0_LCRH_PEN_Pos (1UL) /*!< UART0 LCRH: PEN (Bit 1) */ +#define UART0_LCRH_PEN_Msk (0x2UL) /*!< UART0 LCRH: PEN (Bitfield-Mask: 0x01) */ +#define UART0_LCRH_BRK_Pos (0UL) /*!< UART0 LCRH: BRK (Bit 0) */ +#define UART0_LCRH_BRK_Msk (0x1UL) /*!< UART0 LCRH: BRK (Bitfield-Mask: 0x01) */ +/* ========================================================== CR =========================================================== */ +#define UART0_CR_CTSEN_Pos (15UL) /*!< UART0 CR: CTSEN (Bit 15) */ +#define UART0_CR_CTSEN_Msk (0x8000UL) /*!< UART0 CR: CTSEN (Bitfield-Mask: 0x01) */ +#define UART0_CR_RTSEN_Pos (14UL) /*!< UART0 CR: RTSEN (Bit 14) */ +#define UART0_CR_RTSEN_Msk (0x4000UL) /*!< UART0 CR: RTSEN (Bitfield-Mask: 0x01) */ +#define UART0_CR_OUT2_Pos (13UL) /*!< UART0 CR: OUT2 (Bit 13) */ +#define UART0_CR_OUT2_Msk (0x2000UL) /*!< UART0 CR: OUT2 (Bitfield-Mask: 0x01) */ +#define UART0_CR_OUT1_Pos (12UL) /*!< UART0 CR: OUT1 (Bit 12) */ +#define UART0_CR_OUT1_Msk (0x1000UL) /*!< UART0 CR: OUT1 (Bitfield-Mask: 0x01) */ +#define UART0_CR_RTS_Pos (11UL) /*!< UART0 CR: RTS (Bit 11) */ +#define UART0_CR_RTS_Msk (0x800UL) /*!< UART0 CR: RTS (Bitfield-Mask: 0x01) */ +#define UART0_CR_DTR_Pos (10UL) /*!< UART0 CR: DTR (Bit 10) */ +#define UART0_CR_DTR_Msk (0x400UL) /*!< UART0 CR: DTR (Bitfield-Mask: 0x01) */ +#define UART0_CR_RXE_Pos (9UL) /*!< UART0 CR: RXE (Bit 9) */ +#define UART0_CR_RXE_Msk (0x200UL) /*!< UART0 CR: RXE (Bitfield-Mask: 0x01) */ +#define UART0_CR_TXE_Pos (8UL) /*!< UART0 CR: TXE (Bit 8) */ +#define UART0_CR_TXE_Msk (0x100UL) /*!< UART0 CR: TXE (Bitfield-Mask: 0x01) */ +#define UART0_CR_LBE_Pos (7UL) /*!< UART0 CR: LBE (Bit 7) */ +#define UART0_CR_LBE_Msk (0x80UL) /*!< UART0 CR: LBE (Bitfield-Mask: 0x01) */ +#define UART0_CR_CLKSEL_Pos (4UL) /*!< UART0 CR: CLKSEL (Bit 4) */ +#define UART0_CR_CLKSEL_Msk (0x70UL) /*!< UART0 CR: CLKSEL (Bitfield-Mask: 0x07) */ +#define UART0_CR_CLKEN_Pos (3UL) /*!< UART0 CR: CLKEN (Bit 3) */ +#define UART0_CR_CLKEN_Msk (0x8UL) /*!< UART0 CR: CLKEN (Bitfield-Mask: 0x01) */ +#define UART0_CR_SIRLP_Pos (2UL) /*!< UART0 CR: SIRLP (Bit 2) */ +#define UART0_CR_SIRLP_Msk (0x4UL) /*!< UART0 CR: SIRLP (Bitfield-Mask: 0x01) */ +#define UART0_CR_SIREN_Pos (1UL) /*!< UART0 CR: SIREN (Bit 1) */ +#define UART0_CR_SIREN_Msk (0x2UL) /*!< UART0 CR: SIREN (Bitfield-Mask: 0x01) */ +#define UART0_CR_UARTEN_Pos (0UL) /*!< UART0 CR: UARTEN (Bit 0) */ +#define UART0_CR_UARTEN_Msk (0x1UL) /*!< UART0 CR: UARTEN (Bitfield-Mask: 0x01) */ +/* ========================================================= IFLS ========================================================== */ +#define UART0_IFLS_RXIFLSEL_Pos (3UL) /*!< UART0 IFLS: RXIFLSEL (Bit 3) */ +#define UART0_IFLS_RXIFLSEL_Msk (0x38UL) /*!< UART0 IFLS: RXIFLSEL (Bitfield-Mask: 0x07) */ +#define UART0_IFLS_TXIFLSEL_Pos (0UL) /*!< UART0 IFLS: TXIFLSEL (Bit 0) */ +#define UART0_IFLS_TXIFLSEL_Msk (0x7UL) /*!< UART0 IFLS: TXIFLSEL (Bitfield-Mask: 0x07) */ +/* ========================================================== IER ========================================================== */ +#define UART0_IER_OEIM_Pos (10UL) /*!< UART0 IER: OEIM (Bit 10) */ +#define UART0_IER_OEIM_Msk (0x400UL) /*!< UART0 IER: OEIM (Bitfield-Mask: 0x01) */ +#define UART0_IER_BEIM_Pos (9UL) /*!< UART0 IER: BEIM (Bit 9) */ +#define UART0_IER_BEIM_Msk (0x200UL) /*!< UART0 IER: BEIM (Bitfield-Mask: 0x01) */ +#define UART0_IER_PEIM_Pos (8UL) /*!< UART0 IER: PEIM (Bit 8) */ +#define UART0_IER_PEIM_Msk (0x100UL) /*!< UART0 IER: PEIM (Bitfield-Mask: 0x01) */ +#define UART0_IER_FEIM_Pos (7UL) /*!< UART0 IER: FEIM (Bit 7) */ +#define UART0_IER_FEIM_Msk (0x80UL) /*!< UART0 IER: FEIM (Bitfield-Mask: 0x01) */ +#define UART0_IER_RTIM_Pos (6UL) /*!< UART0 IER: RTIM (Bit 6) */ +#define UART0_IER_RTIM_Msk (0x40UL) /*!< UART0 IER: RTIM (Bitfield-Mask: 0x01) */ +#define UART0_IER_TXIM_Pos (5UL) /*!< UART0 IER: TXIM (Bit 5) */ +#define UART0_IER_TXIM_Msk (0x20UL) /*!< UART0 IER: TXIM (Bitfield-Mask: 0x01) */ +#define UART0_IER_RXIM_Pos (4UL) /*!< UART0 IER: RXIM (Bit 4) */ +#define UART0_IER_RXIM_Msk (0x10UL) /*!< UART0 IER: RXIM (Bitfield-Mask: 0x01) */ +#define UART0_IER_DSRMIM_Pos (3UL) /*!< UART0 IER: DSRMIM (Bit 3) */ +#define UART0_IER_DSRMIM_Msk (0x8UL) /*!< UART0 IER: DSRMIM (Bitfield-Mask: 0x01) */ +#define UART0_IER_DCDMIM_Pos (2UL) /*!< UART0 IER: DCDMIM (Bit 2) */ +#define UART0_IER_DCDMIM_Msk (0x4UL) /*!< UART0 IER: DCDMIM (Bitfield-Mask: 0x01) */ +#define UART0_IER_CTSMIM_Pos (1UL) /*!< UART0 IER: CTSMIM (Bit 1) */ +#define UART0_IER_CTSMIM_Msk (0x2UL) /*!< UART0 IER: CTSMIM (Bitfield-Mask: 0x01) */ +#define UART0_IER_TXCMPMIM_Pos (0UL) /*!< UART0 IER: TXCMPMIM (Bit 0) */ +#define UART0_IER_TXCMPMIM_Msk (0x1UL) /*!< UART0 IER: TXCMPMIM (Bitfield-Mask: 0x01) */ +/* ========================================================== IES ========================================================== */ +#define UART0_IES_OERIS_Pos (10UL) /*!< UART0 IES: OERIS (Bit 10) */ +#define UART0_IES_OERIS_Msk (0x400UL) /*!< UART0 IES: OERIS (Bitfield-Mask: 0x01) */ +#define UART0_IES_BERIS_Pos (9UL) /*!< UART0 IES: BERIS (Bit 9) */ +#define UART0_IES_BERIS_Msk (0x200UL) /*!< UART0 IES: BERIS (Bitfield-Mask: 0x01) */ +#define UART0_IES_PERIS_Pos (8UL) /*!< UART0 IES: PERIS (Bit 8) */ +#define UART0_IES_PERIS_Msk (0x100UL) /*!< UART0 IES: PERIS (Bitfield-Mask: 0x01) */ +#define UART0_IES_FERIS_Pos (7UL) /*!< UART0 IES: FERIS (Bit 7) */ +#define UART0_IES_FERIS_Msk (0x80UL) /*!< UART0 IES: FERIS (Bitfield-Mask: 0x01) */ +#define UART0_IES_RTRIS_Pos (6UL) /*!< UART0 IES: RTRIS (Bit 6) */ +#define UART0_IES_RTRIS_Msk (0x40UL) /*!< UART0 IES: RTRIS (Bitfield-Mask: 0x01) */ +#define UART0_IES_TXRIS_Pos (5UL) /*!< UART0 IES: TXRIS (Bit 5) */ +#define UART0_IES_TXRIS_Msk (0x20UL) /*!< UART0 IES: TXRIS (Bitfield-Mask: 0x01) */ +#define UART0_IES_RXRIS_Pos (4UL) /*!< UART0 IES: RXRIS (Bit 4) */ +#define UART0_IES_RXRIS_Msk (0x10UL) /*!< UART0 IES: RXRIS (Bitfield-Mask: 0x01) */ +#define UART0_IES_DSRMRIS_Pos (3UL) /*!< UART0 IES: DSRMRIS (Bit 3) */ +#define UART0_IES_DSRMRIS_Msk (0x8UL) /*!< UART0 IES: DSRMRIS (Bitfield-Mask: 0x01) */ +#define UART0_IES_DCDMRIS_Pos (2UL) /*!< UART0 IES: DCDMRIS (Bit 2) */ +#define UART0_IES_DCDMRIS_Msk (0x4UL) /*!< UART0 IES: DCDMRIS (Bitfield-Mask: 0x01) */ +#define UART0_IES_CTSMRIS_Pos (1UL) /*!< UART0 IES: CTSMRIS (Bit 1) */ +#define UART0_IES_CTSMRIS_Msk (0x2UL) /*!< UART0 IES: CTSMRIS (Bitfield-Mask: 0x01) */ +#define UART0_IES_TXCMPMRIS_Pos (0UL) /*!< UART0 IES: TXCMPMRIS (Bit 0) */ +#define UART0_IES_TXCMPMRIS_Msk (0x1UL) /*!< UART0 IES: TXCMPMRIS (Bitfield-Mask: 0x01) */ +/* ========================================================== MIS ========================================================== */ +#define UART0_MIS_OEMIS_Pos (10UL) /*!< UART0 MIS: OEMIS (Bit 10) */ +#define UART0_MIS_OEMIS_Msk (0x400UL) /*!< UART0 MIS: OEMIS (Bitfield-Mask: 0x01) */ +#define UART0_MIS_BEMIS_Pos (9UL) /*!< UART0 MIS: BEMIS (Bit 9) */ +#define UART0_MIS_BEMIS_Msk (0x200UL) /*!< UART0 MIS: BEMIS (Bitfield-Mask: 0x01) */ +#define UART0_MIS_PEMIS_Pos (8UL) /*!< UART0 MIS: PEMIS (Bit 8) */ +#define UART0_MIS_PEMIS_Msk (0x100UL) /*!< UART0 MIS: PEMIS (Bitfield-Mask: 0x01) */ +#define UART0_MIS_FEMIS_Pos (7UL) /*!< UART0 MIS: FEMIS (Bit 7) */ +#define UART0_MIS_FEMIS_Msk (0x80UL) /*!< UART0 MIS: FEMIS (Bitfield-Mask: 0x01) */ +#define UART0_MIS_RTMIS_Pos (6UL) /*!< UART0 MIS: RTMIS (Bit 6) */ +#define UART0_MIS_RTMIS_Msk (0x40UL) /*!< UART0 MIS: RTMIS (Bitfield-Mask: 0x01) */ +#define UART0_MIS_TXMIS_Pos (5UL) /*!< UART0 MIS: TXMIS (Bit 5) */ +#define UART0_MIS_TXMIS_Msk (0x20UL) /*!< UART0 MIS: TXMIS (Bitfield-Mask: 0x01) */ +#define UART0_MIS_RXMIS_Pos (4UL) /*!< UART0 MIS: RXMIS (Bit 4) */ +#define UART0_MIS_RXMIS_Msk (0x10UL) /*!< UART0 MIS: RXMIS (Bitfield-Mask: 0x01) */ +#define UART0_MIS_DSRMMIS_Pos (3UL) /*!< UART0 MIS: DSRMMIS (Bit 3) */ +#define UART0_MIS_DSRMMIS_Msk (0x8UL) /*!< UART0 MIS: DSRMMIS (Bitfield-Mask: 0x01) */ +#define UART0_MIS_DCDMMIS_Pos (2UL) /*!< UART0 MIS: DCDMMIS (Bit 2) */ +#define UART0_MIS_DCDMMIS_Msk (0x4UL) /*!< UART0 MIS: DCDMMIS (Bitfield-Mask: 0x01) */ +#define UART0_MIS_CTSMMIS_Pos (1UL) /*!< UART0 MIS: CTSMMIS (Bit 1) */ +#define UART0_MIS_CTSMMIS_Msk (0x2UL) /*!< UART0 MIS: CTSMMIS (Bitfield-Mask: 0x01) */ +#define UART0_MIS_TXCMPMMIS_Pos (0UL) /*!< UART0 MIS: TXCMPMMIS (Bit 0) */ +#define UART0_MIS_TXCMPMMIS_Msk (0x1UL) /*!< UART0 MIS: TXCMPMMIS (Bitfield-Mask: 0x01) */ +/* ========================================================== IEC ========================================================== */ +#define UART0_IEC_OEIC_Pos (10UL) /*!< UART0 IEC: OEIC (Bit 10) */ +#define UART0_IEC_OEIC_Msk (0x400UL) /*!< UART0 IEC: OEIC (Bitfield-Mask: 0x01) */ +#define UART0_IEC_BEIC_Pos (9UL) /*!< UART0 IEC: BEIC (Bit 9) */ +#define UART0_IEC_BEIC_Msk (0x200UL) /*!< UART0 IEC: BEIC (Bitfield-Mask: 0x01) */ +#define UART0_IEC_PEIC_Pos (8UL) /*!< UART0 IEC: PEIC (Bit 8) */ +#define UART0_IEC_PEIC_Msk (0x100UL) /*!< UART0 IEC: PEIC (Bitfield-Mask: 0x01) */ +#define UART0_IEC_FEIC_Pos (7UL) /*!< UART0 IEC: FEIC (Bit 7) */ +#define UART0_IEC_FEIC_Msk (0x80UL) /*!< UART0 IEC: FEIC (Bitfield-Mask: 0x01) */ +#define UART0_IEC_RTIC_Pos (6UL) /*!< UART0 IEC: RTIC (Bit 6) */ +#define UART0_IEC_RTIC_Msk (0x40UL) /*!< UART0 IEC: RTIC (Bitfield-Mask: 0x01) */ +#define UART0_IEC_TXIC_Pos (5UL) /*!< UART0 IEC: TXIC (Bit 5) */ +#define UART0_IEC_TXIC_Msk (0x20UL) /*!< UART0 IEC: TXIC (Bitfield-Mask: 0x01) */ +#define UART0_IEC_RXIC_Pos (4UL) /*!< UART0 IEC: RXIC (Bit 4) */ +#define UART0_IEC_RXIC_Msk (0x10UL) /*!< UART0 IEC: RXIC (Bitfield-Mask: 0x01) */ +#define UART0_IEC_DSRMIC_Pos (3UL) /*!< UART0 IEC: DSRMIC (Bit 3) */ +#define UART0_IEC_DSRMIC_Msk (0x8UL) /*!< UART0 IEC: DSRMIC (Bitfield-Mask: 0x01) */ +#define UART0_IEC_DCDMIC_Pos (2UL) /*!< UART0 IEC: DCDMIC (Bit 2) */ +#define UART0_IEC_DCDMIC_Msk (0x4UL) /*!< UART0 IEC: DCDMIC (Bitfield-Mask: 0x01) */ +#define UART0_IEC_CTSMIC_Pos (1UL) /*!< UART0 IEC: CTSMIC (Bit 1) */ +#define UART0_IEC_CTSMIC_Msk (0x2UL) /*!< UART0 IEC: CTSMIC (Bitfield-Mask: 0x01) */ +#define UART0_IEC_TXCMPMIC_Pos (0UL) /*!< UART0 IEC: TXCMPMIC (Bit 0) */ +#define UART0_IEC_TXCMPMIC_Msk (0x1UL) /*!< UART0 IEC: TXCMPMIC (Bitfield-Mask: 0x01) */ + + +/* =========================================================================================================================== */ +/* ================ VCOMP ================ */ +/* =========================================================================================================================== */ + +/* ========================================================== CFG ========================================================== */ +#define VCOMP_CFG_LVLSEL_Pos (16UL) /*!< VCOMP CFG: LVLSEL (Bit 16) */ +#define VCOMP_CFG_LVLSEL_Msk (0xf0000UL) /*!< VCOMP CFG: LVLSEL (Bitfield-Mask: 0x0f) */ +#define VCOMP_CFG_NSEL_Pos (8UL) /*!< VCOMP CFG: NSEL (Bit 8) */ +#define VCOMP_CFG_NSEL_Msk (0x300UL) /*!< VCOMP CFG: NSEL (Bitfield-Mask: 0x03) */ +#define VCOMP_CFG_PSEL_Pos (0UL) /*!< VCOMP CFG: PSEL (Bit 0) */ +#define VCOMP_CFG_PSEL_Msk (0x3UL) /*!< VCOMP CFG: PSEL (Bitfield-Mask: 0x03) */ +/* ========================================================= STAT ========================================================== */ +#define VCOMP_STAT_PWDSTAT_Pos (1UL) /*!< VCOMP STAT: PWDSTAT (Bit 1) */ +#define VCOMP_STAT_PWDSTAT_Msk (0x2UL) /*!< VCOMP STAT: PWDSTAT (Bitfield-Mask: 0x01) */ +#define VCOMP_STAT_CMPOUT_Pos (0UL) /*!< VCOMP STAT: CMPOUT (Bit 0) */ +#define VCOMP_STAT_CMPOUT_Msk (0x1UL) /*!< VCOMP STAT: CMPOUT (Bitfield-Mask: 0x01) */ +/* ======================================================== PWDKEY ========================================================= */ +#define VCOMP_PWDKEY_PWDKEY_Pos (0UL) /*!< VCOMP PWDKEY: PWDKEY (Bit 0) */ +#define VCOMP_PWDKEY_PWDKEY_Msk (0xffffffffUL) /*!< VCOMP PWDKEY: PWDKEY (Bitfield-Mask: 0xffffffff) */ +/* ========================================================= INTEN ========================================================= */ +#define VCOMP_INTEN_OUTHI_Pos (1UL) /*!< VCOMP INTEN: OUTHI (Bit 1) */ +#define VCOMP_INTEN_OUTHI_Msk (0x2UL) /*!< VCOMP INTEN: OUTHI (Bitfield-Mask: 0x01) */ +#define VCOMP_INTEN_OUTLOW_Pos (0UL) /*!< VCOMP INTEN: OUTLOW (Bit 0) */ +#define VCOMP_INTEN_OUTLOW_Msk (0x1UL) /*!< VCOMP INTEN: OUTLOW (Bitfield-Mask: 0x01) */ +/* ======================================================== INTSTAT ======================================================== */ +#define VCOMP_INTSTAT_OUTHI_Pos (1UL) /*!< VCOMP INTSTAT: OUTHI (Bit 1) */ +#define VCOMP_INTSTAT_OUTHI_Msk (0x2UL) /*!< VCOMP INTSTAT: OUTHI (Bitfield-Mask: 0x01) */ +#define VCOMP_INTSTAT_OUTLOW_Pos (0UL) /*!< VCOMP INTSTAT: OUTLOW (Bit 0) */ +#define VCOMP_INTSTAT_OUTLOW_Msk (0x1UL) /*!< VCOMP INTSTAT: OUTLOW (Bitfield-Mask: 0x01) */ +/* ======================================================== INTCLR ========================================================= */ +#define VCOMP_INTCLR_OUTHI_Pos (1UL) /*!< VCOMP INTCLR: OUTHI (Bit 1) */ +#define VCOMP_INTCLR_OUTHI_Msk (0x2UL) /*!< VCOMP INTCLR: OUTHI (Bitfield-Mask: 0x01) */ +#define VCOMP_INTCLR_OUTLOW_Pos (0UL) /*!< VCOMP INTCLR: OUTLOW (Bit 0) */ +#define VCOMP_INTCLR_OUTLOW_Msk (0x1UL) /*!< VCOMP INTCLR: OUTLOW (Bitfield-Mask: 0x01) */ +/* ======================================================== INTSET ========================================================= */ +#define VCOMP_INTSET_OUTHI_Pos (1UL) /*!< VCOMP INTSET: OUTHI (Bit 1) */ +#define VCOMP_INTSET_OUTHI_Msk (0x2UL) /*!< VCOMP INTSET: OUTHI (Bitfield-Mask: 0x01) */ +#define VCOMP_INTSET_OUTLOW_Pos (0UL) /*!< VCOMP INTSET: OUTLOW (Bit 0) */ +#define VCOMP_INTSET_OUTLOW_Msk (0x1UL) /*!< VCOMP INTSET: OUTLOW (Bitfield-Mask: 0x01) */ + + +/* =========================================================================================================================== */ +/* ================ WDT ================ */ +/* =========================================================================================================================== */ + +/* ========================================================== CFG ========================================================== */ +#define WDT_CFG_CLKSEL_Pos (24UL) /*!< WDT CFG: CLKSEL (Bit 24) */ +#define WDT_CFG_CLKSEL_Msk (0x7000000UL) /*!< WDT CFG: CLKSEL (Bitfield-Mask: 0x07) */ +#define WDT_CFG_INTVAL_Pos (16UL) /*!< WDT CFG: INTVAL (Bit 16) */ +#define WDT_CFG_INTVAL_Msk (0xff0000UL) /*!< WDT CFG: INTVAL (Bitfield-Mask: 0xff) */ +#define WDT_CFG_RESVAL_Pos (8UL) /*!< WDT CFG: RESVAL (Bit 8) */ +#define WDT_CFG_RESVAL_Msk (0xff00UL) /*!< WDT CFG: RESVAL (Bitfield-Mask: 0xff) */ +#define WDT_CFG_RESEN_Pos (2UL) /*!< WDT CFG: RESEN (Bit 2) */ +#define WDT_CFG_RESEN_Msk (0x4UL) /*!< WDT CFG: RESEN (Bitfield-Mask: 0x01) */ +#define WDT_CFG_INTEN_Pos (1UL) /*!< WDT CFG: INTEN (Bit 1) */ +#define WDT_CFG_INTEN_Msk (0x2UL) /*!< WDT CFG: INTEN (Bitfield-Mask: 0x01) */ +#define WDT_CFG_WDTEN_Pos (0UL) /*!< WDT CFG: WDTEN (Bit 0) */ +#define WDT_CFG_WDTEN_Msk (0x1UL) /*!< WDT CFG: WDTEN (Bitfield-Mask: 0x01) */ +/* ========================================================= RSTRT ========================================================= */ +#define WDT_RSTRT_RSTRT_Pos (0UL) /*!< WDT RSTRT: RSTRT (Bit 0) */ +#define WDT_RSTRT_RSTRT_Msk (0xffUL) /*!< WDT RSTRT: RSTRT (Bitfield-Mask: 0xff) */ +/* ========================================================= LOCK ========================================================== */ +#define WDT_LOCK_LOCK_Pos (0UL) /*!< WDT LOCK: LOCK (Bit 0) */ +#define WDT_LOCK_LOCK_Msk (0xffUL) /*!< WDT LOCK: LOCK (Bitfield-Mask: 0xff) */ +/* ========================================================= COUNT ========================================================= */ +#define WDT_COUNT_COUNT_Pos (0UL) /*!< WDT COUNT: COUNT (Bit 0) */ +#define WDT_COUNT_COUNT_Msk (0xffUL) /*!< WDT COUNT: COUNT (Bitfield-Mask: 0xff) */ +/* ========================================================= INTEN ========================================================= */ +#define WDT_INTEN_WDTINT_Pos (0UL) /*!< WDT INTEN: WDTINT (Bit 0) */ +#define WDT_INTEN_WDTINT_Msk (0x1UL) /*!< WDT INTEN: WDTINT (Bitfield-Mask: 0x01) */ +/* ======================================================== INTSTAT ======================================================== */ +#define WDT_INTSTAT_WDTINT_Pos (0UL) /*!< WDT INTSTAT: WDTINT (Bit 0) */ +#define WDT_INTSTAT_WDTINT_Msk (0x1UL) /*!< WDT INTSTAT: WDTINT (Bitfield-Mask: 0x01) */ +/* ======================================================== INTCLR ========================================================= */ +#define WDT_INTCLR_WDTINT_Pos (0UL) /*!< WDT INTCLR: WDTINT (Bit 0) */ +#define WDT_INTCLR_WDTINT_Msk (0x1UL) /*!< WDT INTCLR: WDTINT (Bitfield-Mask: 0x01) */ +/* ======================================================== INTSET ========================================================= */ +#define WDT_INTSET_WDTINT_Pos (0UL) /*!< WDT INTSET: WDTINT (Bit 0) */ +#define WDT_INTSET_WDTINT_Msk (0x1UL) /*!< WDT INTSET: WDTINT (Bitfield-Mask: 0x01) */ + +/** @} */ /* End of group PosMask_peripherals */ + + +/* =========================================================================================================================== */ +/* ================ Enumerated Values Peripheral Section ================ */ +/* =========================================================================================================================== */ + + +/** @addtogroup EnumValue_peripherals + * @{ + */ + + + +/* =========================================================================================================================== */ +/* ================ ADC ================ */ +/* =========================================================================================================================== */ + +/* ========================================================== CFG ========================================================== */ +/* ================================================ ADC CFG CLKSEL [24..25] ================================================ */ +typedef enum { /*!< ADC_CFG_CLKSEL */ + ADC_CFG_CLKSEL_OFF = 0, /*!< OFF : Off mode. The HFRC or HFRC_DIV2 clock must be selected + for the ADC to function. The ADC controller + automatically shuts off the clock in it's + low power modes. When setting ADCEN to '0', + the CLKSEL should remain set to one of the + two clock selects for proper power down + sequencing. value. */ + ADC_CFG_CLKSEL_HFRC = 1, /*!< HFRC : HFRC Core Clock divided by (CORESEL+1) value. */ + ADC_CFG_CLKSEL_HFRC_DIV2 = 2, /*!< HFRC_DIV2 : HFRC Core Clock / 2 further divided by (CORESEL+1) + value. */ +} ADC_CFG_CLKSEL_Enum; + +/* =============================================== ADC CFG TRIGPOL [19..19] ================================================ */ +typedef enum { /*!< ADC_CFG_TRIGPOL */ + ADC_CFG_TRIGPOL_RISING_EDGE = 0, /*!< RISING_EDGE : Trigger on rising edge. value. */ + ADC_CFG_TRIGPOL_FALLING_EDGE = 1, /*!< FALLING_EDGE : Trigger on falling edge. value. */ +} ADC_CFG_TRIGPOL_Enum; + +/* =============================================== ADC CFG TRIGSEL [16..18] ================================================ */ +typedef enum { /*!< ADC_CFG_TRIGSEL */ + ADC_CFG_TRIGSEL_EXT0 = 0, /*!< EXT0 : Off chip External Trigger0 (ADC_ET0) value. */ + ADC_CFG_TRIGSEL_EXT1 = 1, /*!< EXT1 : Off chip External Trigger1 (ADC_ET1) value. */ + ADC_CFG_TRIGSEL_EXT2 = 2, /*!< EXT2 : Off chip External Trigger2 (ADC_ET2) value. */ + ADC_CFG_TRIGSEL_EXT3 = 3, /*!< EXT3 : Off chip External Trigger3 (ADC_ET3) value. */ + ADC_CFG_TRIGSEL_VCOMP = 4, /*!< VCOMP : Voltage Comparator Output value. */ + ADC_CFG_TRIGSEL_SWT = 7, /*!< SWT : Software Trigger value. */ +} ADC_CFG_TRIGSEL_Enum; + +/* ============================================== ADC CFG DFIFORDEN [12..12] =============================================== */ +typedef enum { /*!< ADC_CFG_DFIFORDEN */ + ADC_CFG_DFIFORDEN_DIS = 0, /*!< DIS : Destructive Reads are prevented. Reads to the FIFOPR register + will not POP an entry off the FIFO. value. */ + ADC_CFG_DFIFORDEN_EN = 1, /*!< EN : Reads to the FIFOPR registger will automatically pop an + entry off the FIFO. value. */ +} ADC_CFG_DFIFORDEN_Enum; + +/* ================================================= ADC CFG REFSEL [8..9] ================================================= */ +typedef enum { /*!< ADC_CFG_REFSEL */ + ADC_CFG_REFSEL_INT2P0 = 0, /*!< INT2P0 : Internal 2.0V Bandgap Reference Voltage value. */ + ADC_CFG_REFSEL_INT1P5 = 1, /*!< INT1P5 : Internal 1.5V Bandgap Reference Voltage value. */ + ADC_CFG_REFSEL_EXT2P0 = 2, /*!< EXT2P0 : Off Chip 2.0V Reference value. */ + ADC_CFG_REFSEL_EXT1P5 = 3, /*!< EXT1P5 : Off Chip 1.5V Reference value. */ +} ADC_CFG_REFSEL_Enum; + +/* ================================================= ADC CFG CKMODE [4..4] ================================================= */ +typedef enum { /*!< ADC_CFG_CKMODE */ + ADC_CFG_CKMODE_LPCKMODE = 0, /*!< LPCKMODE : Disable the clock between scans for LPMODE0. Set + LPCKMODE to 0x1 while configuring the ADC. value. */ + ADC_CFG_CKMODE_LLCKMODE = 1, /*!< LLCKMODE : Low Latency Clock Mode. When set, HFRC and the adc_clk + will remain on while in functioning in LPMODE0. value. */ +} ADC_CFG_CKMODE_Enum; + +/* ================================================= ADC CFG LPMODE [3..3] ================================================= */ +typedef enum { /*!< ADC_CFG_LPMODE */ + ADC_CFG_LPMODE_MODE0 = 0, /*!< MODE0 : Low Power Mode 0. Leaves the ADC fully powered between + scans with minimum latency between a trigger event and + sample data collection. value. */ + ADC_CFG_LPMODE_MODE1 = 1, /*!< MODE1 : Low Power Mode 1. Powers down all circuity and clocks + associated with the ADC until the next trigger event. Between + scans, the reference buffer requires up to 50us of delay + from a scan trigger event before the conversion will commence + while operating in this mode. value. */ +} ADC_CFG_LPMODE_Enum; + +/* ================================================= ADC CFG RPTEN [2..2] ================================================== */ +typedef enum { /*!< ADC_CFG_RPTEN */ + ADC_CFG_RPTEN_SINGLE_SCAN = 0, /*!< SINGLE_SCAN : In Single Scan Mode, the ADC will complete a single + scan upon each trigger event. value. */ + ADC_CFG_RPTEN_REPEATING_SCAN = 1, /*!< REPEATING_SCAN : In Repeating Scan Mode, the ADC will complete + it's first scan upon the initial trigger event and all + subsequent scans will occur at regular intervals defined + by the configuration programmed for the CTTMRA3 internal + timer until the timer is disabled or the ADC is disabled. + When disabling the ADC (setting ADCEN to '0'), the RPTEN + bit should be cleared. value. */ +} ADC_CFG_RPTEN_Enum; + +/* ================================================= ADC CFG ADCEN [0..0] ================================================== */ +typedef enum { /*!< ADC_CFG_ADCEN */ + ADC_CFG_ADCEN_DIS = 0, /*!< DIS : Disable the ADC module. value. */ + ADC_CFG_ADCEN_EN = 1, /*!< EN : Enable the ADC module. value. */ +} ADC_CFG_ADCEN_Enum; + +/* ========================================================= STAT ========================================================== */ +/* ================================================ ADC STAT PWDSTAT [0..0] ================================================ */ +typedef enum { /*!< ADC_STAT_PWDSTAT */ + ADC_STAT_PWDSTAT_ON = 0, /*!< ON : Powered on. value. */ + ADC_STAT_PWDSTAT_POWERED_DOWN = 1, /*!< POWERED_DOWN : ADC Low Power Mode 1. value. */ +} ADC_STAT_PWDSTAT_Enum; + +/* ========================================================== SWT ========================================================== */ +/* ================================================== ADC SWT SWT [0..7] =================================================== */ +typedef enum { /*!< ADC_SWT_SWT */ + ADC_SWT_SWT_GEN_SW_TRIGGER = 55, /*!< GEN_SW_TRIGGER : Writing this value generates a software trigger. + value. */ +} ADC_SWT_SWT_Enum; + +/* ======================================================== SL0CFG ========================================================= */ +/* ============================================== ADC SL0CFG ADSEL0 [24..26] =============================================== */ +typedef enum { /*!< ADC_SL0CFG_ADSEL0 */ + ADC_SL0CFG_ADSEL0_AVG_1_MSRMT = 0, /*!< AVG_1_MSRMT : Average in 1 measurement in the accumulate divide + module for this slot. value. */ + ADC_SL0CFG_ADSEL0_AVG_2_MSRMTS = 1, /*!< AVG_2_MSRMTS : Average in 2 measurements in the accumulate divide + module for this slot. value. */ + ADC_SL0CFG_ADSEL0_AVG_4_MSRMTS = 2, /*!< AVG_4_MSRMTS : Average in 4 measurements in the accumulate divide + module for this slot. value. */ + ADC_SL0CFG_ADSEL0_AVG_8_MSRMT = 3, /*!< AVG_8_MSRMT : Average in 8 measurements in the accumulate divide + module for this slot. value. */ + ADC_SL0CFG_ADSEL0_AVG_16_MSRMTS = 4, /*!< AVG_16_MSRMTS : Average in 16 measurements in the accumulate + divide module for this slot. value. */ + ADC_SL0CFG_ADSEL0_AVG_32_MSRMTS = 5, /*!< AVG_32_MSRMTS : Average in 32 measurements in the accumulate + divide module for this slot. value. */ + ADC_SL0CFG_ADSEL0_AVG_64_MSRMTS = 6, /*!< AVG_64_MSRMTS : Average in 64 measurements in the accumulate + divide module for this slot. value. */ + ADC_SL0CFG_ADSEL0_AVG_128_MSRMTS = 7, /*!< AVG_128_MSRMTS : Average in 128 measurements in the accumulate + divide module for this slot. value. */ +} ADC_SL0CFG_ADSEL0_Enum; + +/* ============================================== ADC SL0CFG PRMODE0 [16..17] ============================================== */ +typedef enum { /*!< ADC_SL0CFG_PRMODE0 */ + ADC_SL0CFG_PRMODE0_P14B = 0, /*!< P14B : 14-bit precision mode value. */ + ADC_SL0CFG_PRMODE0_P12B = 1, /*!< P12B : 12-bit precision mode value. */ + ADC_SL0CFG_PRMODE0_P10B = 2, /*!< P10B : 10-bit precision mode value. */ + ADC_SL0CFG_PRMODE0_P8B = 3, /*!< P8B : 8-bit precision mode value. */ +} ADC_SL0CFG_PRMODE0_Enum; + +/* =============================================== ADC SL0CFG CHSEL0 [8..11] =============================================== */ +typedef enum { /*!< ADC_SL0CFG_CHSEL0 */ + ADC_SL0CFG_CHSEL0_SE0 = 0, /*!< SE0 : single ended external GPIO connection to pad16. value. */ + ADC_SL0CFG_CHSEL0_SE1 = 1, /*!< SE1 : single ended external GPIO connection to pad29. value. */ + ADC_SL0CFG_CHSEL0_SE2 = 2, /*!< SE2 : single ended external GPIO connection to pad11. value. */ + ADC_SL0CFG_CHSEL0_SE3 = 3, /*!< SE3 : single ended external GPIO connection to pad31. value. */ + ADC_SL0CFG_CHSEL0_SE4 = 4, /*!< SE4 : single ended external GPIO connection to pad32. value. */ + ADC_SL0CFG_CHSEL0_SE5 = 5, /*!< SE5 : single ended external GPIO connection to pad33. value. */ + ADC_SL0CFG_CHSEL0_SE6 = 6, /*!< SE6 : single ended external GPIO connection to pad34. value. */ + ADC_SL0CFG_CHSEL0_SE7 = 7, /*!< SE7 : single ended external GPIO connection to pad35. value. */ + ADC_SL0CFG_CHSEL0_SE8 = 8, /*!< SE8 : single ended external GPIO connection to pad13. value. */ + ADC_SL0CFG_CHSEL0_SE9 = 9, /*!< SE9 : single ended external GPIO connection to pad12. value. */ + ADC_SL0CFG_CHSEL0_DF0 = 10, /*!< DF0 : differential external GPIO connections to pad12(N) and + pad13(P). value. */ + ADC_SL0CFG_CHSEL0_DF1 = 11, /*!< DF1 : differential external GPIO connections to pad15(N) and + pad14(P). value. */ + ADC_SL0CFG_CHSEL0_TEMP = 12, /*!< TEMP : internal temperature sensor. value. */ + ADC_SL0CFG_CHSEL0_BATT = 13, /*!< BATT : internal voltage divide-by-3 connection. value. */ + ADC_SL0CFG_CHSEL0_VSS = 14, /*!< VSS : Input VSS value. */ +} ADC_SL0CFG_CHSEL0_Enum; + +/* ================================================ ADC SL0CFG WCEN0 [1..1] ================================================ */ +typedef enum { /*!< ADC_SL0CFG_WCEN0 */ + ADC_SL0CFG_WCEN0_WCEN = 1, /*!< WCEN : Enable the window compare for slot 0. value. */ +} ADC_SL0CFG_WCEN0_Enum; + +/* ================================================ ADC SL0CFG SLEN0 [0..0] ================================================ */ +typedef enum { /*!< ADC_SL0CFG_SLEN0 */ + ADC_SL0CFG_SLEN0_SLEN = 1, /*!< SLEN : Enable slot 0 for ADC conversions. value. */ +} ADC_SL0CFG_SLEN0_Enum; + +/* ======================================================== SL1CFG ========================================================= */ +/* ============================================== ADC SL1CFG ADSEL1 [24..26] =============================================== */ +typedef enum { /*!< ADC_SL1CFG_ADSEL1 */ + ADC_SL1CFG_ADSEL1_AVG_1_MSRMT = 0, /*!< AVG_1_MSRMT : Average in 1 measurement in the accumulate divide + module for this slot. value. */ + ADC_SL1CFG_ADSEL1_AVG_2_MSRMTS = 1, /*!< AVG_2_MSRMTS : Average in 2 measurements in the accumulate divide + module for this slot. value. */ + ADC_SL1CFG_ADSEL1_AVG_4_MSRMTS = 2, /*!< AVG_4_MSRMTS : Average in 4 measurements in the accumulate divide + module for this slot. value. */ + ADC_SL1CFG_ADSEL1_AVG_8_MSRMT = 3, /*!< AVG_8_MSRMT : Average in 8 measurements in the accumulate divide + module for this slot. value. */ + ADC_SL1CFG_ADSEL1_AVG_16_MSRMTS = 4, /*!< AVG_16_MSRMTS : Average in 16 measurements in the accumulate + divide module for this slot. value. */ + ADC_SL1CFG_ADSEL1_AVG_32_MSRMTS = 5, /*!< AVG_32_MSRMTS : Average in 32 measurements in the accumulate + divide module for this slot. value. */ + ADC_SL1CFG_ADSEL1_AVG_64_MSRMTS = 6, /*!< AVG_64_MSRMTS : Average in 64 measurements in the accumulate + divide module for this slot. value. */ + ADC_SL1CFG_ADSEL1_AVG_128_MSRMTS = 7, /*!< AVG_128_MSRMTS : Average in 128 measurements in the accumulate + divide module for this slot. value. */ +} ADC_SL1CFG_ADSEL1_Enum; + +/* ============================================== ADC SL1CFG PRMODE1 [16..17] ============================================== */ +typedef enum { /*!< ADC_SL1CFG_PRMODE1 */ + ADC_SL1CFG_PRMODE1_P14B = 0, /*!< P14B : 14-bit precision mode value. */ + ADC_SL1CFG_PRMODE1_P12B = 1, /*!< P12B : 12-bit precision mode value. */ + ADC_SL1CFG_PRMODE1_P10B = 2, /*!< P10B : 10-bit precision mode value. */ + ADC_SL1CFG_PRMODE1_P8B = 3, /*!< P8B : 8-bit precision mode value. */ +} ADC_SL1CFG_PRMODE1_Enum; + +/* =============================================== ADC SL1CFG CHSEL1 [8..11] =============================================== */ +typedef enum { /*!< ADC_SL1CFG_CHSEL1 */ + ADC_SL1CFG_CHSEL1_SE0 = 0, /*!< SE0 : single ended external GPIO connection to pad16. value. */ + ADC_SL1CFG_CHSEL1_SE1 = 1, /*!< SE1 : single ended external GPIO connection to pad29. value. */ + ADC_SL1CFG_CHSEL1_SE2 = 2, /*!< SE2 : single ended external GPIO connection to pad11. value. */ + ADC_SL1CFG_CHSEL1_SE3 = 3, /*!< SE3 : single ended external GPIO connection to pad31. value. */ + ADC_SL1CFG_CHSEL1_SE4 = 4, /*!< SE4 : single ended external GPIO connection to pad32. value. */ + ADC_SL1CFG_CHSEL1_SE5 = 5, /*!< SE5 : single ended external GPIO connection to pad33. value. */ + ADC_SL1CFG_CHSEL1_SE6 = 6, /*!< SE6 : single ended external GPIO connection to pad34. value. */ + ADC_SL1CFG_CHSEL1_SE7 = 7, /*!< SE7 : single ended external GPIO connection to pad35. value. */ + ADC_SL1CFG_CHSEL1_SE8 = 8, /*!< SE8 : single ended external GPIO connection to pad13. value. */ + ADC_SL1CFG_CHSEL1_SE9 = 9, /*!< SE9 : single ended external GPIO connection to pad12. value. */ + ADC_SL1CFG_CHSEL1_DF0 = 10, /*!< DF0 : differential external GPIO connections to pad12(N) and + pad13(P). value. */ + ADC_SL1CFG_CHSEL1_DF1 = 11, /*!< DF1 : differential external GPIO connections to pad15(N) and + pad14(P). value. */ + ADC_SL1CFG_CHSEL1_TEMP = 12, /*!< TEMP : internal temperature sensor. value. */ + ADC_SL1CFG_CHSEL1_BATT = 13, /*!< BATT : internal voltage divide-by-3 connection. value. */ + ADC_SL1CFG_CHSEL1_VSS = 14, /*!< VSS : Input VSS value. */ +} ADC_SL1CFG_CHSEL1_Enum; + +/* ================================================ ADC SL1CFG WCEN1 [1..1] ================================================ */ +typedef enum { /*!< ADC_SL1CFG_WCEN1 */ + ADC_SL1CFG_WCEN1_WCEN = 1, /*!< WCEN : Enable the window compare for slot 1. value. */ +} ADC_SL1CFG_WCEN1_Enum; + +/* ================================================ ADC SL1CFG SLEN1 [0..0] ================================================ */ +typedef enum { /*!< ADC_SL1CFG_SLEN1 */ + ADC_SL1CFG_SLEN1_SLEN = 1, /*!< SLEN : Enable slot 1 for ADC conversions. value. */ +} ADC_SL1CFG_SLEN1_Enum; + +/* ======================================================== SL2CFG ========================================================= */ +/* ============================================== ADC SL2CFG ADSEL2 [24..26] =============================================== */ +typedef enum { /*!< ADC_SL2CFG_ADSEL2 */ + ADC_SL2CFG_ADSEL2_AVG_1_MSRMT = 0, /*!< AVG_1_MSRMT : Average in 1 measurement in the accumulate divide + module for this slot. value. */ + ADC_SL2CFG_ADSEL2_AVG_2_MSRMTS = 1, /*!< AVG_2_MSRMTS : Average in 2 measurements in the accumulate divide + module for this slot. value. */ + ADC_SL2CFG_ADSEL2_AVG_4_MSRMTS = 2, /*!< AVG_4_MSRMTS : Average in 4 measurements in the accumulate divide + module for this slot. value. */ + ADC_SL2CFG_ADSEL2_AVG_8_MSRMT = 3, /*!< AVG_8_MSRMT : Average in 8 measurements in the accumulate divide + module for this slot. value. */ + ADC_SL2CFG_ADSEL2_AVG_16_MSRMTS = 4, /*!< AVG_16_MSRMTS : Average in 16 measurements in the accumulate + divide module for this slot. value. */ + ADC_SL2CFG_ADSEL2_AVG_32_MSRMTS = 5, /*!< AVG_32_MSRMTS : Average in 32 measurements in the accumulate + divide module for this slot. value. */ + ADC_SL2CFG_ADSEL2_AVG_64_MSRMTS = 6, /*!< AVG_64_MSRMTS : Average in 64 measurements in the accumulate + divide module for this slot. value. */ + ADC_SL2CFG_ADSEL2_AVG_128_MSRMTS = 7, /*!< AVG_128_MSRMTS : Average in 128 measurements in the accumulate + divide module for this slot. value. */ +} ADC_SL2CFG_ADSEL2_Enum; + +/* ============================================== ADC SL2CFG PRMODE2 [16..17] ============================================== */ +typedef enum { /*!< ADC_SL2CFG_PRMODE2 */ + ADC_SL2CFG_PRMODE2_P14B = 0, /*!< P14B : 14-bit precision mode value. */ + ADC_SL2CFG_PRMODE2_P12B = 1, /*!< P12B : 12-bit precision mode value. */ + ADC_SL2CFG_PRMODE2_P10B = 2, /*!< P10B : 10-bit precision mode value. */ + ADC_SL2CFG_PRMODE2_P8B = 3, /*!< P8B : 8-bit precision mode value. */ +} ADC_SL2CFG_PRMODE2_Enum; + +/* =============================================== ADC SL2CFG CHSEL2 [8..11] =============================================== */ +typedef enum { /*!< ADC_SL2CFG_CHSEL2 */ + ADC_SL2CFG_CHSEL2_SE0 = 0, /*!< SE0 : single ended external GPIO connection to pad16. value. */ + ADC_SL2CFG_CHSEL2_SE1 = 1, /*!< SE1 : single ended external GPIO connection to pad29. value. */ + ADC_SL2CFG_CHSEL2_SE2 = 2, /*!< SE2 : single ended external GPIO connection to pad11. value. */ + ADC_SL2CFG_CHSEL2_SE3 = 3, /*!< SE3 : single ended external GPIO connection to pad31. value. */ + ADC_SL2CFG_CHSEL2_SE4 = 4, /*!< SE4 : single ended external GPIO connection to pad32. value. */ + ADC_SL2CFG_CHSEL2_SE5 = 5, /*!< SE5 : single ended external GPIO connection to pad33. value. */ + ADC_SL2CFG_CHSEL2_SE6 = 6, /*!< SE6 : single ended external GPIO connection to pad34. value. */ + ADC_SL2CFG_CHSEL2_SE7 = 7, /*!< SE7 : single ended external GPIO connection to pad35. value. */ + ADC_SL2CFG_CHSEL2_SE8 = 8, /*!< SE8 : single ended external GPIO connection to pad13. value. */ + ADC_SL2CFG_CHSEL2_SE9 = 9, /*!< SE9 : single ended external GPIO connection to pad12. value. */ + ADC_SL2CFG_CHSEL2_DF0 = 10, /*!< DF0 : differential external GPIO connections to pad12(N) and + pad13(P). value. */ + ADC_SL2CFG_CHSEL2_DF1 = 11, /*!< DF1 : differential external GPIO connections to pad15(N) and + pad14(P). value. */ + ADC_SL2CFG_CHSEL2_TEMP = 12, /*!< TEMP : internal temperature sensor. value. */ + ADC_SL2CFG_CHSEL2_BATT = 13, /*!< BATT : internal voltage divide-by-3 connection. value. */ + ADC_SL2CFG_CHSEL2_VSS = 14, /*!< VSS : Input VSS value. */ +} ADC_SL2CFG_CHSEL2_Enum; + +/* ================================================ ADC SL2CFG WCEN2 [1..1] ================================================ */ +typedef enum { /*!< ADC_SL2CFG_WCEN2 */ + ADC_SL2CFG_WCEN2_WCEN = 1, /*!< WCEN : Enable the window compare for slot 2. value. */ +} ADC_SL2CFG_WCEN2_Enum; + +/* ================================================ ADC SL2CFG SLEN2 [0..0] ================================================ */ +typedef enum { /*!< ADC_SL2CFG_SLEN2 */ + ADC_SL2CFG_SLEN2_SLEN = 1, /*!< SLEN : Enable slot 2 for ADC conversions. value. */ +} ADC_SL2CFG_SLEN2_Enum; + +/* ======================================================== SL3CFG ========================================================= */ +/* ============================================== ADC SL3CFG ADSEL3 [24..26] =============================================== */ +typedef enum { /*!< ADC_SL3CFG_ADSEL3 */ + ADC_SL3CFG_ADSEL3_AVG_1_MSRMT = 0, /*!< AVG_1_MSRMT : Average in 1 measurement in the accumulate divide + module for this slot. value. */ + ADC_SL3CFG_ADSEL3_AVG_2_MSRMTS = 1, /*!< AVG_2_MSRMTS : Average in 2 measurements in the accumulate divide + module for this slot. value. */ + ADC_SL3CFG_ADSEL3_AVG_4_MSRMTS = 2, /*!< AVG_4_MSRMTS : Average in 4 measurements in the accumulate divide + module for this slot. value. */ + ADC_SL3CFG_ADSEL3_AVG_8_MSRMT = 3, /*!< AVG_8_MSRMT : Average in 8 measurements in the accumulate divide + module for this slot. value. */ + ADC_SL3CFG_ADSEL3_AVG_16_MSRMTS = 4, /*!< AVG_16_MSRMTS : Average in 16 measurements in the accumulate + divide module for this slot. value. */ + ADC_SL3CFG_ADSEL3_AVG_32_MSRMTS = 5, /*!< AVG_32_MSRMTS : Average in 32 measurements in the accumulate + divide module for this slot. value. */ + ADC_SL3CFG_ADSEL3_AVG_64_MSRMTS = 6, /*!< AVG_64_MSRMTS : Average in 64 measurements in the accumulate + divide module for this slot. value. */ + ADC_SL3CFG_ADSEL3_AVG_128_MSRMTS = 7, /*!< AVG_128_MSRMTS : Average in 128 measurements in the accumulate + divide module for this slot. value. */ +} ADC_SL3CFG_ADSEL3_Enum; + +/* ============================================== ADC SL3CFG PRMODE3 [16..17] ============================================== */ +typedef enum { /*!< ADC_SL3CFG_PRMODE3 */ + ADC_SL3CFG_PRMODE3_P14B = 0, /*!< P14B : 14-bit precision mode value. */ + ADC_SL3CFG_PRMODE3_P12B = 1, /*!< P12B : 12-bit precision mode value. */ + ADC_SL3CFG_PRMODE3_P10B = 2, /*!< P10B : 10-bit precision mode value. */ + ADC_SL3CFG_PRMODE3_P8B = 3, /*!< P8B : 8-bit precision mode value. */ +} ADC_SL3CFG_PRMODE3_Enum; + +/* =============================================== ADC SL3CFG CHSEL3 [8..11] =============================================== */ +typedef enum { /*!< ADC_SL3CFG_CHSEL3 */ + ADC_SL3CFG_CHSEL3_SE0 = 0, /*!< SE0 : single ended external GPIO connection to pad16. value. */ + ADC_SL3CFG_CHSEL3_SE1 = 1, /*!< SE1 : single ended external GPIO connection to pad29. value. */ + ADC_SL3CFG_CHSEL3_SE2 = 2, /*!< SE2 : single ended external GPIO connection to pad11. value. */ + ADC_SL3CFG_CHSEL3_SE3 = 3, /*!< SE3 : single ended external GPIO connection to pad31. value. */ + ADC_SL3CFG_CHSEL3_SE4 = 4, /*!< SE4 : single ended external GPIO connection to pad32. value. */ + ADC_SL3CFG_CHSEL3_SE5 = 5, /*!< SE5 : single ended external GPIO connection to pad33. value. */ + ADC_SL3CFG_CHSEL3_SE6 = 6, /*!< SE6 : single ended external GPIO connection to pad34. value. */ + ADC_SL3CFG_CHSEL3_SE7 = 7, /*!< SE7 : single ended external GPIO connection to pad35. value. */ + ADC_SL3CFG_CHSEL3_SE8 = 8, /*!< SE8 : single ended external GPIO connection to pad13. value. */ + ADC_SL3CFG_CHSEL3_SE9 = 9, /*!< SE9 : single ended external GPIO connection to pad12. value. */ + ADC_SL3CFG_CHSEL3_DF0 = 10, /*!< DF0 : differential external GPIO connections to pad12(N) and + pad13(P). value. */ + ADC_SL3CFG_CHSEL3_DF1 = 11, /*!< DF1 : differential external GPIO connections to pad15(N) and + pad14(P). value. */ + ADC_SL3CFG_CHSEL3_TEMP = 12, /*!< TEMP : internal temperature sensor. value. */ + ADC_SL3CFG_CHSEL3_BATT = 13, /*!< BATT : internal voltage divide-by-3 connection. value. */ + ADC_SL3CFG_CHSEL3_VSS = 14, /*!< VSS : Input VSS value. */ +} ADC_SL3CFG_CHSEL3_Enum; + +/* ================================================ ADC SL3CFG WCEN3 [1..1] ================================================ */ +typedef enum { /*!< ADC_SL3CFG_WCEN3 */ + ADC_SL3CFG_WCEN3_WCEN = 1, /*!< WCEN : Enable the window compare for slot 3. value. */ +} ADC_SL3CFG_WCEN3_Enum; + +/* ================================================ ADC SL3CFG SLEN3 [0..0] ================================================ */ +typedef enum { /*!< ADC_SL3CFG_SLEN3 */ + ADC_SL3CFG_SLEN3_SLEN = 1, /*!< SLEN : Enable slot 3 for ADC conversions. value. */ +} ADC_SL3CFG_SLEN3_Enum; + +/* ======================================================== SL4CFG ========================================================= */ +/* ============================================== ADC SL4CFG ADSEL4 [24..26] =============================================== */ +typedef enum { /*!< ADC_SL4CFG_ADSEL4 */ + ADC_SL4CFG_ADSEL4_AVG_1_MSRMT = 0, /*!< AVG_1_MSRMT : Average in 1 measurement in the accumulate divide + module for this slot. value. */ + ADC_SL4CFG_ADSEL4_AVG_2_MSRMTS = 1, /*!< AVG_2_MSRMTS : Average in 2 measurements in the accumulate divide + module for this slot. value. */ + ADC_SL4CFG_ADSEL4_AVG_4_MSRMTS = 2, /*!< AVG_4_MSRMTS : Average in 4 measurements in the accumulate divide + module for this slot. value. */ + ADC_SL4CFG_ADSEL4_AVG_8_MSRMT = 3, /*!< AVG_8_MSRMT : Average in 8 measurements in the accumulate divide + module for this slot. value. */ + ADC_SL4CFG_ADSEL4_AVG_16_MSRMTS = 4, /*!< AVG_16_MSRMTS : Average in 16 measurements in the accumulate + divide module for this slot. value. */ + ADC_SL4CFG_ADSEL4_AVG_32_MSRMTS = 5, /*!< AVG_32_MSRMTS : Average in 32 measurements in the accumulate + divide module for this slot. value. */ + ADC_SL4CFG_ADSEL4_AVG_64_MSRMTS = 6, /*!< AVG_64_MSRMTS : Average in 64 measurements in the accumulate + divide module for this slot. value. */ + ADC_SL4CFG_ADSEL4_AVG_128_MSRMTS = 7, /*!< AVG_128_MSRMTS : Average in 128 measurements in the accumulate + divide module for this slot. value. */ +} ADC_SL4CFG_ADSEL4_Enum; + +/* ============================================== ADC SL4CFG PRMODE4 [16..17] ============================================== */ +typedef enum { /*!< ADC_SL4CFG_PRMODE4 */ + ADC_SL4CFG_PRMODE4_P14B = 0, /*!< P14B : 14-bit precision mode value. */ + ADC_SL4CFG_PRMODE4_P12B = 1, /*!< P12B : 12-bit precision mode value. */ + ADC_SL4CFG_PRMODE4_P10B = 2, /*!< P10B : 10-bit precision mode value. */ + ADC_SL4CFG_PRMODE4_P8B = 3, /*!< P8B : 8-bit precision mode value. */ +} ADC_SL4CFG_PRMODE4_Enum; + +/* =============================================== ADC SL4CFG CHSEL4 [8..11] =============================================== */ +typedef enum { /*!< ADC_SL4CFG_CHSEL4 */ + ADC_SL4CFG_CHSEL4_SE0 = 0, /*!< SE0 : single ended external GPIO connection to pad16. value. */ + ADC_SL4CFG_CHSEL4_SE1 = 1, /*!< SE1 : single ended external GPIO connection to pad29. value. */ + ADC_SL4CFG_CHSEL4_SE2 = 2, /*!< SE2 : single ended external GPIO connection to pad11. value. */ + ADC_SL4CFG_CHSEL4_SE3 = 3, /*!< SE3 : single ended external GPIO connection to pad31. value. */ + ADC_SL4CFG_CHSEL4_SE4 = 4, /*!< SE4 : single ended external GPIO connection to pad32. value. */ + ADC_SL4CFG_CHSEL4_SE5 = 5, /*!< SE5 : single ended external GPIO connection to pad33. value. */ + ADC_SL4CFG_CHSEL4_SE6 = 6, /*!< SE6 : single ended external GPIO connection to pad34. value. */ + ADC_SL4CFG_CHSEL4_SE7 = 7, /*!< SE7 : single ended external GPIO connection to pad35. value. */ + ADC_SL4CFG_CHSEL4_SE8 = 8, /*!< SE8 : single ended external GPIO connection to pad13. value. */ + ADC_SL4CFG_CHSEL4_SE9 = 9, /*!< SE9 : single ended external GPIO connection to pad12. value. */ + ADC_SL4CFG_CHSEL4_DF0 = 10, /*!< DF0 : differential external GPIO connections to pad12(N) and + pad13(P). value. */ + ADC_SL4CFG_CHSEL4_DF1 = 11, /*!< DF1 : differential external GPIO connections to pad15(N) and + pad14(P). value. */ + ADC_SL4CFG_CHSEL4_TEMP = 12, /*!< TEMP : internal temperature sensor. value. */ + ADC_SL4CFG_CHSEL4_BATT = 13, /*!< BATT : internal voltage divide-by-3 connection. value. */ + ADC_SL4CFG_CHSEL4_VSS = 14, /*!< VSS : Input VSS value. */ +} ADC_SL4CFG_CHSEL4_Enum; + +/* ================================================ ADC SL4CFG WCEN4 [1..1] ================================================ */ +typedef enum { /*!< ADC_SL4CFG_WCEN4 */ + ADC_SL4CFG_WCEN4_WCEN = 1, /*!< WCEN : Enable the window compare for slot 4. value. */ +} ADC_SL4CFG_WCEN4_Enum; + +/* ================================================ ADC SL4CFG SLEN4 [0..0] ================================================ */ +typedef enum { /*!< ADC_SL4CFG_SLEN4 */ + ADC_SL4CFG_SLEN4_SLEN = 1, /*!< SLEN : Enable slot 4 for ADC conversions. value. */ +} ADC_SL4CFG_SLEN4_Enum; + +/* ======================================================== SL5CFG ========================================================= */ +/* ============================================== ADC SL5CFG ADSEL5 [24..26] =============================================== */ +typedef enum { /*!< ADC_SL5CFG_ADSEL5 */ + ADC_SL5CFG_ADSEL5_AVG_1_MSRMT = 0, /*!< AVG_1_MSRMT : Average in 1 measurement in the accumulate divide + module for this slot. value. */ + ADC_SL5CFG_ADSEL5_AVG_2_MSRMTS = 1, /*!< AVG_2_MSRMTS : Average in 2 measurements in the accumulate divide + module for this slot. value. */ + ADC_SL5CFG_ADSEL5_AVG_4_MSRMTS = 2, /*!< AVG_4_MSRMTS : Average in 4 measurements in the accumulate divide + module for this slot. value. */ + ADC_SL5CFG_ADSEL5_AVG_8_MSRMT = 3, /*!< AVG_8_MSRMT : Average in 8 measurements in the accumulate divide + module for this slot. value. */ + ADC_SL5CFG_ADSEL5_AVG_16_MSRMTS = 4, /*!< AVG_16_MSRMTS : Average in 16 measurements in the accumulate + divide module for this slot. value. */ + ADC_SL5CFG_ADSEL5_AVG_32_MSRMTS = 5, /*!< AVG_32_MSRMTS : Average in 32 measurements in the accumulate + divide module for this slot. value. */ + ADC_SL5CFG_ADSEL5_AVG_64_MSRMTS = 6, /*!< AVG_64_MSRMTS : Average in 64 measurements in the accumulate + divide module for this slot. value. */ + ADC_SL5CFG_ADSEL5_AVG_128_MSRMTS = 7, /*!< AVG_128_MSRMTS : Average in 128 measurements in the accumulate + divide module for this slot. value. */ +} ADC_SL5CFG_ADSEL5_Enum; + +/* ============================================== ADC SL5CFG PRMODE5 [16..17] ============================================== */ +typedef enum { /*!< ADC_SL5CFG_PRMODE5 */ + ADC_SL5CFG_PRMODE5_P14B = 0, /*!< P14B : 14-bit precision mode value. */ + ADC_SL5CFG_PRMODE5_P12B = 1, /*!< P12B : 12-bit precision mode value. */ + ADC_SL5CFG_PRMODE5_P10B = 2, /*!< P10B : 10-bit precision mode value. */ + ADC_SL5CFG_PRMODE5_P8B = 3, /*!< P8B : 8-bit precision mode value. */ +} ADC_SL5CFG_PRMODE5_Enum; + +/* =============================================== ADC SL5CFG CHSEL5 [8..11] =============================================== */ +typedef enum { /*!< ADC_SL5CFG_CHSEL5 */ + ADC_SL5CFG_CHSEL5_SE0 = 0, /*!< SE0 : single ended external GPIO connection to pad16. value. */ + ADC_SL5CFG_CHSEL5_SE1 = 1, /*!< SE1 : single ended external GPIO connection to pad29. value. */ + ADC_SL5CFG_CHSEL5_SE2 = 2, /*!< SE2 : single ended external GPIO connection to pad11. value. */ + ADC_SL5CFG_CHSEL5_SE3 = 3, /*!< SE3 : single ended external GPIO connection to pad31. value. */ + ADC_SL5CFG_CHSEL5_SE4 = 4, /*!< SE4 : single ended external GPIO connection to pad32. value. */ + ADC_SL5CFG_CHSEL5_SE5 = 5, /*!< SE5 : single ended external GPIO connection to pad33. value. */ + ADC_SL5CFG_CHSEL5_SE6 = 6, /*!< SE6 : single ended external GPIO connection to pad34. value. */ + ADC_SL5CFG_CHSEL5_SE7 = 7, /*!< SE7 : single ended external GPIO connection to pad35. value. */ + ADC_SL5CFG_CHSEL5_SE8 = 8, /*!< SE8 : single ended external GPIO connection to pad13. value. */ + ADC_SL5CFG_CHSEL5_SE9 = 9, /*!< SE9 : single ended external GPIO connection to pad12. value. */ + ADC_SL5CFG_CHSEL5_DF0 = 10, /*!< DF0 : differential external GPIO connections to pad12(N) and + pad13(P). value. */ + ADC_SL5CFG_CHSEL5_DF1 = 11, /*!< DF1 : differential external GPIO connections to pad15(N) and + pad14(P). value. */ + ADC_SL5CFG_CHSEL5_TEMP = 12, /*!< TEMP : internal temperature sensor. value. */ + ADC_SL5CFG_CHSEL5_BATT = 13, /*!< BATT : internal voltage divide-by-3 connection. value. */ + ADC_SL5CFG_CHSEL5_VSS = 14, /*!< VSS : Input VSS value. */ +} ADC_SL5CFG_CHSEL5_Enum; + +/* ================================================ ADC SL5CFG WCEN5 [1..1] ================================================ */ +typedef enum { /*!< ADC_SL5CFG_WCEN5 */ + ADC_SL5CFG_WCEN5_WCEN = 1, /*!< WCEN : Enable the window compare for slot 5. value. */ +} ADC_SL5CFG_WCEN5_Enum; + +/* ================================================ ADC SL5CFG SLEN5 [0..0] ================================================ */ +typedef enum { /*!< ADC_SL5CFG_SLEN5 */ + ADC_SL5CFG_SLEN5_SLEN = 1, /*!< SLEN : Enable slot 5 for ADC conversions. value. */ +} ADC_SL5CFG_SLEN5_Enum; + +/* ======================================================== SL6CFG ========================================================= */ +/* ============================================== ADC SL6CFG ADSEL6 [24..26] =============================================== */ +typedef enum { /*!< ADC_SL6CFG_ADSEL6 */ + ADC_SL6CFG_ADSEL6_AVG_1_MSRMT = 0, /*!< AVG_1_MSRMT : Average in 1 measurement in the accumulate divide + module for this slot. value. */ + ADC_SL6CFG_ADSEL6_AVG_2_MSRMTS = 1, /*!< AVG_2_MSRMTS : Average in 2 measurements in the accumulate divide + module for this slot. value. */ + ADC_SL6CFG_ADSEL6_AVG_4_MSRMTS = 2, /*!< AVG_4_MSRMTS : Average in 4 measurements in the accumulate divide + module for this slot. value. */ + ADC_SL6CFG_ADSEL6_AVG_8_MSRMT = 3, /*!< AVG_8_MSRMT : Average in 8 measurements in the accumulate divide + module for this slot. value. */ + ADC_SL6CFG_ADSEL6_AVG_16_MSRMTS = 4, /*!< AVG_16_MSRMTS : Average in 16 measurements in the accumulate + divide module for this slot. value. */ + ADC_SL6CFG_ADSEL6_AVG_32_MSRMTS = 5, /*!< AVG_32_MSRMTS : Average in 32 measurements in the accumulate + divide module for this slot. value. */ + ADC_SL6CFG_ADSEL6_AVG_64_MSRMTS = 6, /*!< AVG_64_MSRMTS : Average in 64 measurements in the accumulate + divide module for this slot. value. */ + ADC_SL6CFG_ADSEL6_AVG_128_MSRMTS = 7, /*!< AVG_128_MSRMTS : Average in 128 measurements in the accumulate + divide module for this slot. value. */ +} ADC_SL6CFG_ADSEL6_Enum; + +/* ============================================== ADC SL6CFG PRMODE6 [16..17] ============================================== */ +typedef enum { /*!< ADC_SL6CFG_PRMODE6 */ + ADC_SL6CFG_PRMODE6_P14B = 0, /*!< P14B : 14-bit precision mode value. */ + ADC_SL6CFG_PRMODE6_P12B = 1, /*!< P12B : 12-bit precision mode value. */ + ADC_SL6CFG_PRMODE6_P10B = 2, /*!< P10B : 10-bit precision mode value. */ + ADC_SL6CFG_PRMODE6_P8B = 3, /*!< P8B : 8-bit precision mode value. */ +} ADC_SL6CFG_PRMODE6_Enum; + +/* =============================================== ADC SL6CFG CHSEL6 [8..11] =============================================== */ +typedef enum { /*!< ADC_SL6CFG_CHSEL6 */ + ADC_SL6CFG_CHSEL6_SE0 = 0, /*!< SE0 : single ended external GPIO connection to pad16. value. */ + ADC_SL6CFG_CHSEL6_SE1 = 1, /*!< SE1 : single ended external GPIO connection to pad29. value. */ + ADC_SL6CFG_CHSEL6_SE2 = 2, /*!< SE2 : single ended external GPIO connection to pad11. value. */ + ADC_SL6CFG_CHSEL6_SE3 = 3, /*!< SE3 : single ended external GPIO connection to pad31. value. */ + ADC_SL6CFG_CHSEL6_SE4 = 4, /*!< SE4 : single ended external GPIO connection to pad32. value. */ + ADC_SL6CFG_CHSEL6_SE5 = 5, /*!< SE5 : single ended external GPIO connection to pad33. value. */ + ADC_SL6CFG_CHSEL6_SE6 = 6, /*!< SE6 : single ended external GPIO connection to pad34. value. */ + ADC_SL6CFG_CHSEL6_SE7 = 7, /*!< SE7 : single ended external GPIO connection to pad35. value. */ + ADC_SL6CFG_CHSEL6_SE8 = 8, /*!< SE8 : single ended external GPIO connection to pad13. value. */ + ADC_SL6CFG_CHSEL6_SE9 = 9, /*!< SE9 : single ended external GPIO connection to pad12. value. */ + ADC_SL6CFG_CHSEL6_DF0 = 10, /*!< DF0 : differential external GPIO connections to pad12(N) and + pad13(P). value. */ + ADC_SL6CFG_CHSEL6_DF1 = 11, /*!< DF1 : differential external GPIO connections to pad15(N) and + pad14(P). value. */ + ADC_SL6CFG_CHSEL6_TEMP = 12, /*!< TEMP : internal temperature sensor. value. */ + ADC_SL6CFG_CHSEL6_BATT = 13, /*!< BATT : internal voltage divide-by-3 connection. value. */ + ADC_SL6CFG_CHSEL6_VSS = 14, /*!< VSS : Input VSS value. */ +} ADC_SL6CFG_CHSEL6_Enum; + +/* ================================================ ADC SL6CFG WCEN6 [1..1] ================================================ */ +typedef enum { /*!< ADC_SL6CFG_WCEN6 */ + ADC_SL6CFG_WCEN6_WCEN = 1, /*!< WCEN : Enable the window compare for slot 6. value. */ +} ADC_SL6CFG_WCEN6_Enum; + +/* ================================================ ADC SL6CFG SLEN6 [0..0] ================================================ */ +typedef enum { /*!< ADC_SL6CFG_SLEN6 */ + ADC_SL6CFG_SLEN6_SLEN = 1, /*!< SLEN : Enable slot 6 for ADC conversions. value. */ +} ADC_SL6CFG_SLEN6_Enum; + +/* ======================================================== SL7CFG ========================================================= */ +/* ============================================== ADC SL7CFG ADSEL7 [24..26] =============================================== */ +typedef enum { /*!< ADC_SL7CFG_ADSEL7 */ + ADC_SL7CFG_ADSEL7_AVG_1_MSRMT = 0, /*!< AVG_1_MSRMT : Average in 1 measurement in the accumulate divide + module for this slot. value. */ + ADC_SL7CFG_ADSEL7_AVG_2_MSRMTS = 1, /*!< AVG_2_MSRMTS : Average in 2 measurements in the accumulate divide + module for this slot. value. */ + ADC_SL7CFG_ADSEL7_AVG_4_MSRMTS = 2, /*!< AVG_4_MSRMTS : Average in 4 measurements in the accumulate divide + module for this slot. value. */ + ADC_SL7CFG_ADSEL7_AVG_8_MSRMT = 3, /*!< AVG_8_MSRMT : Average in 8 measurements in the accumulate divide + module for this slot. value. */ + ADC_SL7CFG_ADSEL7_AVG_16_MSRMTS = 4, /*!< AVG_16_MSRMTS : Average in 16 measurements in the accumulate + divide module for this slot. value. */ + ADC_SL7CFG_ADSEL7_AVG_32_MSRMTS = 5, /*!< AVG_32_MSRMTS : Average in 32 measurements in the accumulate + divide module for this slot. value. */ + ADC_SL7CFG_ADSEL7_AVG_64_MSRMTS = 6, /*!< AVG_64_MSRMTS : Average in 64 measurements in the accumulate + divide module for this slot. value. */ + ADC_SL7CFG_ADSEL7_AVG_128_MSRMTS = 7, /*!< AVG_128_MSRMTS : Average in 128 measurements in the accumulate + divide module for this slot. value. */ +} ADC_SL7CFG_ADSEL7_Enum; + +/* ============================================== ADC SL7CFG PRMODE7 [16..17] ============================================== */ +typedef enum { /*!< ADC_SL7CFG_PRMODE7 */ + ADC_SL7CFG_PRMODE7_P14B = 0, /*!< P14B : 14-bit precision mode value. */ + ADC_SL7CFG_PRMODE7_P12B = 1, /*!< P12B : 12-bit precision mode value. */ + ADC_SL7CFG_PRMODE7_P10B = 2, /*!< P10B : 10-bit precision mode value. */ + ADC_SL7CFG_PRMODE7_P8B = 3, /*!< P8B : 8-bit precision mode value. */ +} ADC_SL7CFG_PRMODE7_Enum; + +/* =============================================== ADC SL7CFG CHSEL7 [8..11] =============================================== */ +typedef enum { /*!< ADC_SL7CFG_CHSEL7 */ + ADC_SL7CFG_CHSEL7_SE0 = 0, /*!< SE0 : single ended external GPIO connection to pad16. value. */ + ADC_SL7CFG_CHSEL7_SE1 = 1, /*!< SE1 : single ended external GPIO connection to pad29. value. */ + ADC_SL7CFG_CHSEL7_SE2 = 2, /*!< SE2 : single ended external GPIO connection to pad11. value. */ + ADC_SL7CFG_CHSEL7_SE3 = 3, /*!< SE3 : single ended external GPIO connection to pad31. value. */ + ADC_SL7CFG_CHSEL7_SE4 = 4, /*!< SE4 : single ended external GPIO connection to pad32. value. */ + ADC_SL7CFG_CHSEL7_SE5 = 5, /*!< SE5 : single ended external GPIO connection to pad33. value. */ + ADC_SL7CFG_CHSEL7_SE6 = 6, /*!< SE6 : single ended external GPIO connection to pad34. value. */ + ADC_SL7CFG_CHSEL7_SE7 = 7, /*!< SE7 : single ended external GPIO connection to pad35. value. */ + ADC_SL7CFG_CHSEL7_SE8 = 8, /*!< SE8 : single ended external GPIO connection to pad13. value. */ + ADC_SL7CFG_CHSEL7_SE9 = 9, /*!< SE9 : single ended external GPIO connection to pad12. value. */ + ADC_SL7CFG_CHSEL7_DF0 = 10, /*!< DF0 : differential external GPIO connections to pad12(N) and + pad13(P). value. */ + ADC_SL7CFG_CHSEL7_DF1 = 11, /*!< DF1 : differential external GPIO connections to pad15(N) and + pad14(P). value. */ + ADC_SL7CFG_CHSEL7_TEMP = 12, /*!< TEMP : internal temperature sensor. value. */ + ADC_SL7CFG_CHSEL7_BATT = 13, /*!< BATT : internal voltage divide-by-3 connection. value. */ + ADC_SL7CFG_CHSEL7_VSS = 14, /*!< VSS : Input VSS value. */ +} ADC_SL7CFG_CHSEL7_Enum; + +/* ================================================ ADC SL7CFG WCEN7 [1..1] ================================================ */ +typedef enum { /*!< ADC_SL7CFG_WCEN7 */ + ADC_SL7CFG_WCEN7_WCEN = 1, /*!< WCEN : Enable the window compare for slot 7. value. */ +} ADC_SL7CFG_WCEN7_Enum; + +/* ================================================ ADC SL7CFG SLEN7 [0..0] ================================================ */ +typedef enum { /*!< ADC_SL7CFG_SLEN7 */ + ADC_SL7CFG_SLEN7_SLEN = 1, /*!< SLEN : Enable slot 7 for ADC conversions. value. */ +} ADC_SL7CFG_SLEN7_Enum; + +/* ========================================================= WULIM ========================================================= */ +/* ========================================================= WLLIM ========================================================= */ +/* ======================================================== SCWLIM ========================================================= */ +/* ========================================================= FIFO ========================================================== */ +/* ======================================================== FIFOPR ========================================================= */ +/* ========================================================= INTEN ========================================================= */ +/* ================================================= ADC INTEN DERR [7..7] ================================================= */ +typedef enum { /*!< ADC_INTEN_DERR */ + ADC_INTEN_DERR_DMAERROR = 1, /*!< DMAERROR : DMA Error Condition Occurred value. */ +} ADC_INTEN_DERR_Enum; + +/* ================================================= ADC INTEN DCMP [6..6] ================================================= */ +typedef enum { /*!< ADC_INTEN_DCMP */ + ADC_INTEN_DCMP_DMACOMPLETE = 1, /*!< DMACOMPLETE : DMA Completed a transfer value. */ +} ADC_INTEN_DCMP_Enum; + +/* ================================================ ADC INTEN WCINC [5..5] ================================================= */ +typedef enum { /*!< ADC_INTEN_WCINC */ + ADC_INTEN_WCINC_WCINCINT = 1, /*!< WCINCINT : Window comparitor voltage incursion interrupt. value. */ +} ADC_INTEN_WCINC_Enum; + +/* ================================================ ADC INTEN WCEXC [4..4] ================================================= */ +typedef enum { /*!< ADC_INTEN_WCEXC */ + ADC_INTEN_WCEXC_WCEXCINT = 1, /*!< WCEXCINT : Window comparitor voltage excursion interrupt. value. */ +} ADC_INTEN_WCEXC_Enum; + +/* =============================================== ADC INTEN FIFOOVR2 [3..3] =============================================== */ +typedef enum { /*!< ADC_INTEN_FIFOOVR2 */ + ADC_INTEN_FIFOOVR2_FIFOFULLINT = 1, /*!< FIFOFULLINT : FIFO 100 percent full interrupt. value. */ +} ADC_INTEN_FIFOOVR2_Enum; + +/* =============================================== ADC INTEN FIFOOVR1 [2..2] =============================================== */ +typedef enum { /*!< ADC_INTEN_FIFOOVR1 */ + ADC_INTEN_FIFOOVR1_FIFO75INT = 1, /*!< FIFO75INT : FIFO 75 percent full interrupt. value. */ +} ADC_INTEN_FIFOOVR1_Enum; + +/* ================================================ ADC INTEN SCNCMP [1..1] ================================================ */ +typedef enum { /*!< ADC_INTEN_SCNCMP */ + ADC_INTEN_SCNCMP_SCNCMPINT = 1, /*!< SCNCMPINT : ADC scan complete interrupt. value. */ +} ADC_INTEN_SCNCMP_Enum; + +/* ================================================ ADC INTEN CNVCMP [0..0] ================================================ */ +typedef enum { /*!< ADC_INTEN_CNVCMP */ + ADC_INTEN_CNVCMP_CNVCMPINT = 1, /*!< CNVCMPINT : ADC conversion complete interrupt. value. */ +} ADC_INTEN_CNVCMP_Enum; + +/* ======================================================== INTSTAT ======================================================== */ +/* ================================================ ADC INTSTAT DERR [7..7] ================================================ */ +typedef enum { /*!< ADC_INTSTAT_DERR */ + ADC_INTSTAT_DERR_DMAERROR = 1, /*!< DMAERROR : DMA Error Condition Occurred value. */ +} ADC_INTSTAT_DERR_Enum; + +/* ================================================ ADC INTSTAT DCMP [6..6] ================================================ */ +typedef enum { /*!< ADC_INTSTAT_DCMP */ + ADC_INTSTAT_DCMP_DMACOMPLETE = 1, /*!< DMACOMPLETE : DMA Completed a transfer value. */ +} ADC_INTSTAT_DCMP_Enum; + +/* =============================================== ADC INTSTAT WCINC [5..5] ================================================ */ +typedef enum { /*!< ADC_INTSTAT_WCINC */ + ADC_INTSTAT_WCINC_WCINCINT = 1, /*!< WCINCINT : Window comparitor voltage incursion interrupt. value. */ +} ADC_INTSTAT_WCINC_Enum; + +/* =============================================== ADC INTSTAT WCEXC [4..4] ================================================ */ +typedef enum { /*!< ADC_INTSTAT_WCEXC */ + ADC_INTSTAT_WCEXC_WCEXCINT = 1, /*!< WCEXCINT : Window comparitor voltage excursion interrupt. value. */ +} ADC_INTSTAT_WCEXC_Enum; + +/* ============================================== ADC INTSTAT FIFOOVR2 [3..3] ============================================== */ +typedef enum { /*!< ADC_INTSTAT_FIFOOVR2 */ + ADC_INTSTAT_FIFOOVR2_FIFOFULLINT = 1, /*!< FIFOFULLINT : FIFO 100 percent full interrupt. value. */ +} ADC_INTSTAT_FIFOOVR2_Enum; + +/* ============================================== ADC INTSTAT FIFOOVR1 [2..2] ============================================== */ +typedef enum { /*!< ADC_INTSTAT_FIFOOVR1 */ + ADC_INTSTAT_FIFOOVR1_FIFO75INT = 1, /*!< FIFO75INT : FIFO 75 percent full interrupt. value. */ +} ADC_INTSTAT_FIFOOVR1_Enum; + +/* =============================================== ADC INTSTAT SCNCMP [1..1] =============================================== */ +typedef enum { /*!< ADC_INTSTAT_SCNCMP */ + ADC_INTSTAT_SCNCMP_SCNCMPINT = 1, /*!< SCNCMPINT : ADC scan complete interrupt. value. */ +} ADC_INTSTAT_SCNCMP_Enum; + +/* =============================================== ADC INTSTAT CNVCMP [0..0] =============================================== */ +typedef enum { /*!< ADC_INTSTAT_CNVCMP */ + ADC_INTSTAT_CNVCMP_CNVCMPINT = 1, /*!< CNVCMPINT : ADC conversion complete interrupt. value. */ +} ADC_INTSTAT_CNVCMP_Enum; + +/* ======================================================== INTCLR ========================================================= */ +/* ================================================ ADC INTCLR DERR [7..7] ================================================= */ +typedef enum { /*!< ADC_INTCLR_DERR */ + ADC_INTCLR_DERR_DMAERROR = 1, /*!< DMAERROR : DMA Error Condition Occurred value. */ +} ADC_INTCLR_DERR_Enum; + +/* ================================================ ADC INTCLR DCMP [6..6] ================================================= */ +typedef enum { /*!< ADC_INTCLR_DCMP */ + ADC_INTCLR_DCMP_DMACOMPLETE = 1, /*!< DMACOMPLETE : DMA Completed a transfer value. */ +} ADC_INTCLR_DCMP_Enum; + +/* ================================================ ADC INTCLR WCINC [5..5] ================================================ */ +typedef enum { /*!< ADC_INTCLR_WCINC */ + ADC_INTCLR_WCINC_WCINCINT = 1, /*!< WCINCINT : Window comparitor voltage incursion interrupt. value. */ +} ADC_INTCLR_WCINC_Enum; + +/* ================================================ ADC INTCLR WCEXC [4..4] ================================================ */ +typedef enum { /*!< ADC_INTCLR_WCEXC */ + ADC_INTCLR_WCEXC_WCEXCINT = 1, /*!< WCEXCINT : Window comparitor voltage excursion interrupt. value. */ +} ADC_INTCLR_WCEXC_Enum; + +/* ============================================== ADC INTCLR FIFOOVR2 [3..3] =============================================== */ +typedef enum { /*!< ADC_INTCLR_FIFOOVR2 */ + ADC_INTCLR_FIFOOVR2_FIFOFULLINT = 1, /*!< FIFOFULLINT : FIFO 100 percent full interrupt. value. */ +} ADC_INTCLR_FIFOOVR2_Enum; + +/* ============================================== ADC INTCLR FIFOOVR1 [2..2] =============================================== */ +typedef enum { /*!< ADC_INTCLR_FIFOOVR1 */ + ADC_INTCLR_FIFOOVR1_FIFO75INT = 1, /*!< FIFO75INT : FIFO 75 percent full interrupt. value. */ +} ADC_INTCLR_FIFOOVR1_Enum; + +/* =============================================== ADC INTCLR SCNCMP [1..1] ================================================ */ +typedef enum { /*!< ADC_INTCLR_SCNCMP */ + ADC_INTCLR_SCNCMP_SCNCMPINT = 1, /*!< SCNCMPINT : ADC scan complete interrupt. value. */ +} ADC_INTCLR_SCNCMP_Enum; + +/* =============================================== ADC INTCLR CNVCMP [0..0] ================================================ */ +typedef enum { /*!< ADC_INTCLR_CNVCMP */ + ADC_INTCLR_CNVCMP_CNVCMPINT = 1, /*!< CNVCMPINT : ADC conversion complete interrupt. value. */ +} ADC_INTCLR_CNVCMP_Enum; + +/* ======================================================== INTSET ========================================================= */ +/* ================================================ ADC INTSET DERR [7..7] ================================================= */ +typedef enum { /*!< ADC_INTSET_DERR */ + ADC_INTSET_DERR_DMAERROR = 1, /*!< DMAERROR : DMA Error Condition Occurred value. */ +} ADC_INTSET_DERR_Enum; + +/* ================================================ ADC INTSET DCMP [6..6] ================================================= */ +typedef enum { /*!< ADC_INTSET_DCMP */ + ADC_INTSET_DCMP_DMACOMPLETE = 1, /*!< DMACOMPLETE : DMA Completed a transfer value. */ +} ADC_INTSET_DCMP_Enum; + +/* ================================================ ADC INTSET WCINC [5..5] ================================================ */ +typedef enum { /*!< ADC_INTSET_WCINC */ + ADC_INTSET_WCINC_WCINCINT = 1, /*!< WCINCINT : Window comparitor voltage incursion interrupt. value. */ +} ADC_INTSET_WCINC_Enum; + +/* ================================================ ADC INTSET WCEXC [4..4] ================================================ */ +typedef enum { /*!< ADC_INTSET_WCEXC */ + ADC_INTSET_WCEXC_WCEXCINT = 1, /*!< WCEXCINT : Window comparitor voltage excursion interrupt. value. */ +} ADC_INTSET_WCEXC_Enum; + +/* ============================================== ADC INTSET FIFOOVR2 [3..3] =============================================== */ +typedef enum { /*!< ADC_INTSET_FIFOOVR2 */ + ADC_INTSET_FIFOOVR2_FIFOFULLINT = 1, /*!< FIFOFULLINT : FIFO 100 percent full interrupt. value. */ +} ADC_INTSET_FIFOOVR2_Enum; + +/* ============================================== ADC INTSET FIFOOVR1 [2..2] =============================================== */ +typedef enum { /*!< ADC_INTSET_FIFOOVR1 */ + ADC_INTSET_FIFOOVR1_FIFO75INT = 1, /*!< FIFO75INT : FIFO 75 percent full interrupt. value. */ +} ADC_INTSET_FIFOOVR1_Enum; + +/* =============================================== ADC INTSET SCNCMP [1..1] ================================================ */ +typedef enum { /*!< ADC_INTSET_SCNCMP */ + ADC_INTSET_SCNCMP_SCNCMPINT = 1, /*!< SCNCMPINT : ADC scan complete interrupt. value. */ +} ADC_INTSET_SCNCMP_Enum; + +/* =============================================== ADC INTSET CNVCMP [0..0] ================================================ */ +typedef enum { /*!< ADC_INTSET_CNVCMP */ + ADC_INTSET_CNVCMP_CNVCMPINT = 1, /*!< CNVCMPINT : ADC conversion complete interrupt. value. */ +} ADC_INTSET_CNVCMP_Enum; + +/* ======================================================= DMATRIGEN ======================================================= */ +/* ====================================================== DMATRIGSTAT ====================================================== */ +/* ======================================================== DMACFG ========================================================= */ +/* ============================================== ADC DMACFG DMAMSK [17..17] =============================================== */ +typedef enum { /*!< ADC_DMACFG_DMAMSK */ + ADC_DMACFG_DMAMSK_DIS = 0, /*!< DIS : FIFO Contents are copied directly to memory without modification. + value. */ + ADC_DMACFG_DMAMSK_EN = 1, /*!< EN : Only the FIFODATA contents are copied to memory on DMA + transfers. The SLOTNUM and FIFOCNT contents are cleared + to zero. value. */ +} ADC_DMACFG_DMAMSK_Enum; + +/* ============================================ ADC DMACFG DMAHONSTAT [16..16] ============================================= */ +typedef enum { /*!< ADC_DMACFG_DMAHONSTAT */ + ADC_DMACFG_DMAHONSTAT_DIS = 0, /*!< DIS : ADC conversions will continue regardless of DMA status + register value. */ + ADC_DMACFG_DMAHONSTAT_EN = 1, /*!< EN : ADC conversions will not progress if DMAERR or DMACPL bits + in DMA status register are set. value. */ +} ADC_DMACFG_DMAHONSTAT_Enum; + +/* ============================================== ADC DMACFG DMADYNPRI [9..9] ============================================== */ +typedef enum { /*!< ADC_DMACFG_DMADYNPRI */ + ADC_DMACFG_DMADYNPRI_DIS = 0, /*!< DIS : Disable dynamic priority (use DMAPRI setting only) value. */ + ADC_DMACFG_DMADYNPRI_EN = 1, /*!< EN : Enable dynamic priority value. */ +} ADC_DMACFG_DMADYNPRI_Enum; + +/* =============================================== ADC DMACFG DMAPRI [8..8] ================================================ */ +typedef enum { /*!< ADC_DMACFG_DMAPRI */ + ADC_DMACFG_DMAPRI_LOW = 0, /*!< LOW : Low Priority (service as best effort) value. */ + ADC_DMACFG_DMAPRI_HIGH = 1, /*!< HIGH : High Priority (service immediately) value. */ +} ADC_DMACFG_DMAPRI_Enum; + +/* =============================================== ADC DMACFG DMADIR [2..2] ================================================ */ +typedef enum { /*!< ADC_DMACFG_DMADIR */ + ADC_DMACFG_DMADIR_P2M = 0, /*!< P2M : Peripheral to Memory (SRAM) transaction value. */ + ADC_DMACFG_DMADIR_M2P = 1, /*!< M2P : Memory to Peripheral transaction value. */ +} ADC_DMACFG_DMADIR_Enum; + +/* ================================================ ADC DMACFG DMAEN [0..0] ================================================ */ +typedef enum { /*!< ADC_DMACFG_DMAEN */ + ADC_DMACFG_DMAEN_DIS = 0, /*!< DIS : Disable DMA Function value. */ + ADC_DMACFG_DMAEN_EN = 1, /*!< EN : Enable DMA Function value. */ +} ADC_DMACFG_DMAEN_Enum; + +/* ====================================================== DMATOTCOUNT ====================================================== */ +/* ====================================================== DMATARGADDR ====================================================== */ +/* ======================================================== DMASTAT ======================================================== */ + + +/* =========================================================================================================================== */ +/* ================ APBDMA ================ */ +/* =========================================================================================================================== */ + +/* ======================================================== BBVALUE ======================================================== */ +/* ====================================================== BBSETCLEAR ======================================================= */ +/* ======================================================== BBINPUT ======================================================== */ +/* ======================================================= DEBUGDATA ======================================================= */ +/* ========================================================= DEBUG ========================================================= */ +/* ============================================== APBDMA DEBUG DEBUGEN [0..3] ============================================== */ +typedef enum { /*!< APBDMA_DEBUG_DEBUGEN */ + APBDMA_DEBUG_DEBUGEN_OFF = 0, /*!< OFF : Debug Disabled value. */ + APBDMA_DEBUG_DEBUGEN_ARB = 1, /*!< ARB : Debug Arb values value. */ +} APBDMA_DEBUG_DEBUGEN_Enum; + + + +/* =========================================================================================================================== */ +/* ================ BLEIF ================ */ +/* =========================================================================================================================== */ + +/* ========================================================= FIFO ========================================================== */ +/* ======================================================== FIFOPTR ======================================================== */ +/* ======================================================== FIFOTHR ======================================================== */ +/* ======================================================== FIFOPOP ======================================================== */ +/* ======================================================= FIFOPUSH ======================================================== */ +/* ======================================================= FIFOCTRL ======================================================== */ +/* ======================================================== FIFOLOC ======================================================== */ +/* ======================================================== CLKCFG ========================================================= */ +/* =============================================== BLEIF CLKCFG FSEL [8..10] =============================================== */ +typedef enum { /*!< BLEIF_CLKCFG_FSEL */ + BLEIF_CLKCFG_FSEL_MIN_PWR = 0, /*!< MIN_PWR : Selects the minimum power clock. This setting should + be used whenever the IOM is not active. value. */ + BLEIF_CLKCFG_FSEL_HFRC = 1, /*!< HFRC : Selects the HFRC as the input clock. value. */ + BLEIF_CLKCFG_FSEL_HFRC_DIV2 = 2, /*!< HFRC_DIV2 : Selects the HFRC / 2 as the input clock. value. */ + BLEIF_CLKCFG_FSEL_HFRC_DIV4 = 3, /*!< HFRC_DIV4 : Selects the HFRC / 4 as the input clock. value. */ + BLEIF_CLKCFG_FSEL_HFRC_DIV8 = 4, /*!< HFRC_DIV8 : Selects the HFRC / 8 as the input clock. value. */ + BLEIF_CLKCFG_FSEL_HFRC_DIV16 = 5, /*!< HFRC_DIV16 : Selects the HFRC / 16 as the input clock. value. */ + BLEIF_CLKCFG_FSEL_HFRC_DIV32 = 6, /*!< HFRC_DIV32 : Selects the HFRC / 32 as the input clock. value. */ + BLEIF_CLKCFG_FSEL_HFRC_DIV64 = 7, /*!< HFRC_DIV64 : Selects the HFRC / 64 as the input clock. value. */ +} BLEIF_CLKCFG_FSEL_Enum; + +/* ========================================================== CMD ========================================================== */ +/* ================================================= BLEIF CMD CMD [0..4] ================================================== */ +typedef enum { /*!< BLEIF_CMD_CMD */ + BLEIF_CMD_CMD_WRITE = 1, /*!< WRITE : Write command using count of offset bytes specified + in the OFFSETCNT field value. */ + BLEIF_CMD_CMD_READ = 2, /*!< READ : Read command using count of offset bytes specified in + the OFFSETCNT field value. */ +} BLEIF_CMD_CMD_Enum; + +/* ======================================================== CMDRPT ========================================================= */ +/* ======================================================= OFFSETHI ======================================================== */ +/* ======================================================== CMDSTAT ======================================================== */ +/* ============================================= BLEIF CMDSTAT CMDSTAT [5..7] ============================================== */ +typedef enum { /*!< BLEIF_CMDSTAT_CMDSTAT */ + BLEIF_CMDSTAT_CMDSTAT_ERR = 1, /*!< ERR : Error encountered with command value. */ + BLEIF_CMDSTAT_CMDSTAT_ACTIVE = 2, /*!< ACTIVE : Actively processing command value. */ + BLEIF_CMDSTAT_CMDSTAT_IDLE = 4, /*!< IDLE : Idle state, no active command, no error value. */ + BLEIF_CMDSTAT_CMDSTAT_WAIT = 6, /*!< WAIT : Command in progress, but waiting on data from host value. */ +} BLEIF_CMDSTAT_CMDSTAT_Enum; + +/* ========================================================= INTEN ========================================================= */ +/* ======================================================== INTSTAT ======================================================== */ +/* ======================================================== INTCLR ========================================================= */ +/* ======================================================== INTSET ========================================================= */ +/* ======================================================= DMATRIGEN ======================================================= */ +/* ====================================================== DMATRIGSTAT ====================================================== */ +/* ======================================================== DMACFG ========================================================= */ +/* ============================================== BLEIF DMACFG DPWROFF [9..9] ============================================== */ +typedef enum { /*!< BLEIF_DMACFG_DPWROFF */ + BLEIF_DMACFG_DPWROFF_DIS = 0, /*!< DIS : Power off disabled value. */ + BLEIF_DMACFG_DPWROFF_EN = 1, /*!< EN : Power off enabled value. */ +} BLEIF_DMACFG_DPWROFF_Enum; + +/* ============================================== BLEIF DMACFG DMAPRI [8..8] =============================================== */ +typedef enum { /*!< BLEIF_DMACFG_DMAPRI */ + BLEIF_DMACFG_DMAPRI_LOW = 0, /*!< LOW : Low Priority (service as best effort) value. */ + BLEIF_DMACFG_DMAPRI_HIGH = 1, /*!< HIGH : High Priority (service immediately) value. */ +} BLEIF_DMACFG_DMAPRI_Enum; + +/* ============================================== BLEIF DMACFG DMADIR [1..1] =============================================== */ +typedef enum { /*!< BLEIF_DMACFG_DMADIR */ + BLEIF_DMACFG_DMADIR_P2M = 0, /*!< P2M : Peripheral to Memory (SRAM) transaction. To be set when + doing IOM read operations, ie reading data from external + devices. value. */ + BLEIF_DMACFG_DMADIR_M2P = 1, /*!< M2P : Memory to Peripheral transaction. To be set when doing + IOM write operations, ie writing data to external devices. + value. */ +} BLEIF_DMACFG_DMADIR_Enum; + +/* =============================================== BLEIF DMACFG DMAEN [0..0] =============================================== */ +typedef enum { /*!< BLEIF_DMACFG_DMAEN */ + BLEIF_DMACFG_DMAEN_DIS = 0, /*!< DIS : Disable DMA Function value. */ + BLEIF_DMACFG_DMAEN_EN = 1, /*!< EN : Enable DMA Function value. */ +} BLEIF_DMACFG_DMAEN_Enum; + +/* ====================================================== DMATOTCOUNT ====================================================== */ +/* ====================================================== DMATARGADDR ====================================================== */ +/* ======================================================== DMASTAT ======================================================== */ +/* ========================================================= CQCFG ========================================================= */ +/* =============================================== BLEIF CQCFG CQPRI [1..1] ================================================ */ +typedef enum { /*!< BLEIF_CQCFG_CQPRI */ + BLEIF_CQCFG_CQPRI_LOW = 0, /*!< LOW : Low Priority (service as best effort) value. */ + BLEIF_CQCFG_CQPRI_HIGH = 1, /*!< HIGH : High Priority (service immediately) value. */ +} BLEIF_CQCFG_CQPRI_Enum; + +/* ================================================ BLEIF CQCFG CQEN [0..0] ================================================ */ +typedef enum { /*!< BLEIF_CQCFG_CQEN */ + BLEIF_CQCFG_CQEN_DIS = 0, /*!< DIS : Disable CQ Function value. */ + BLEIF_CQCFG_CQEN_EN = 1, /*!< EN : Enable CQ Function value. */ +} BLEIF_CQCFG_CQEN_Enum; + +/* ======================================================== CQADDR ========================================================= */ +/* ======================================================== CQSTAT ========================================================= */ +/* ======================================================== CQFLAGS ======================================================== */ +/* ====================================================== CQSETCLEAR ======================================================= */ +/* ======================================================= CQPAUSEEN ======================================================= */ +/* ============================================= BLEIF CQPAUSEEN CQPEN [0..15] ============================================= */ +typedef enum { /*!< BLEIF_CQPAUSEEN_CQPEN */ + BLEIF_CQPAUSEEN_CQPEN_CNTEQ = 32768, /*!< CNTEQ : Pauses command queue processing when HWCNT matches SWCNT + value. */ + BLEIF_CQPAUSEEN_CQPEN_BLEXOREN = 16384, /*!< BLEXOREN : Pause command queue when input BLE bit XORed with + SWFLAG4 is '1' value. */ + BLEIF_CQPAUSEEN_CQPEN_IOMXOREN = 8192, /*!< IOMXOREN : Pause command queue when input IOM bit XORed with + SWFLAG3 is '1' value. */ + BLEIF_CQPAUSEEN_CQPEN_GPIOXOREN = 4096, /*!< GPIOXOREN : Pause command queue when input GPIO irq_bit XORed + with SWFLAG2 is '1' value. */ + BLEIF_CQPAUSEEN_CQPEN_MSPI1XNOREN = 2048, /*!< MSPI1XNOREN : Pause command queue when input MSPI1 bit XNORed + with SWFLAG1 is '1' value. */ + BLEIF_CQPAUSEEN_CQPEN_MSPI0XNOREN = 1024, /*!< MSPI0XNOREN : Pause command queue when input MSPI0 bit XNORed + with SWFLAG0 is '1' value. */ + BLEIF_CQPAUSEEN_CQPEN_MSPI1XOREN = 512, /*!< MSPI1XOREN : Pause command queue when input MSPI1 bit XORed + with SWFLAG1 is '1' value. */ + BLEIF_CQPAUSEEN_CQPEN_MSPI0XOREN = 256, /*!< MSPI0XOREN : Pause command queue when input MSPI0 bit XORed + with SWFLAG0 is '1' value. */ + BLEIF_CQPAUSEEN_CQPEN_SWFLAGEN7 = 128, /*!< SWFLAGEN7 : Pause the command queue when software flag bit 7 + is '1'. value. */ + BLEIF_CQPAUSEEN_CQPEN_SWFLAGEN6 = 64, /*!< SWFLAGEN6 : Pause the command queue when software flag bit 7 + is '1' value. */ + BLEIF_CQPAUSEEN_CQPEN_SWFLAGEN5 = 32, /*!< SWFLAGEN5 : Pause the command queue when software flag bit 7 + is '1' value. */ + BLEIF_CQPAUSEEN_CQPEN_SWFLAGEN4 = 16, /*!< SWFLAGEN4 : Pause the command queue when software flag bit 7 + is '1' value. */ + BLEIF_CQPAUSEEN_CQPEN_SWFLAGEN3 = 8, /*!< SWFLAGEN3 : Pause the command queue when software flag bit 7 + is '1' value. */ + BLEIF_CQPAUSEEN_CQPEN_SWFLAGEN2 = 4, /*!< SWFLAGEN2 : Pause the command queue when software flag bit 7 + is '1' value. */ + BLEIF_CQPAUSEEN_CQPEN_SWFLAGEN1 = 2, /*!< SWFLAGEN1 : Pause the command queue when software flag bit 7 + is '1' value. */ + BLEIF_CQPAUSEEN_CQPEN_SWFLGEN0 = 1, /*!< SWFLGEN0 : Pause the command queue when software flag bit 7 + is '1' value. */ +} BLEIF_CQPAUSEEN_CQPEN_Enum; + +/* ======================================================= CQCURIDX ======================================================== */ +/* ======================================================= CQENDIDX ======================================================== */ +/* ======================================================== STATUS ========================================================= */ +/* ============================================== BLEIF STATUS IDLEST [2..2] =============================================== */ +typedef enum { /*!< BLEIF_STATUS_IDLEST */ + BLEIF_STATUS_IDLEST_IDLE = 1, /*!< IDLE : The I/O state machine is in the idle state. value. */ +} BLEIF_STATUS_IDLEST_Enum; + +/* ============================================== BLEIF STATUS CMDACT [1..1] =============================================== */ +typedef enum { /*!< BLEIF_STATUS_CMDACT */ + BLEIF_STATUS_CMDACT_ACTIVE = 1, /*!< ACTIVE : An I/O command is active. Indicates the active module + has an active command and is processing this. De-asserted + when the command is completed. value. */ +} BLEIF_STATUS_CMDACT_Enum; + +/* ================================================ BLEIF STATUS ERR [0..0] ================================================ */ +typedef enum { /*!< BLEIF_STATUS_ERR */ + BLEIF_STATUS_ERR_ERROR = 1, /*!< ERROR : Bit has been deprecated and will always return 0. value. */ +} BLEIF_STATUS_ERR_Enum; + +/* ======================================================== MSPICFG ======================================================== */ +/* ============================================= BLEIF MSPICFG SPILSB [23..23] ============================================= */ +typedef enum { /*!< BLEIF_MSPICFG_SPILSB */ + BLEIF_MSPICFG_SPILSB_MSB = 0, /*!< MSB : Send and receive MSB bit first value. */ + BLEIF_MSPICFG_SPILSB_LSB = 1, /*!< LSB : Send and receive LSB bit first value. */ +} BLEIF_MSPICFG_SPILSB_Enum; + +/* ============================================ BLEIF MSPICFG RDFCPOL [22..22] ============================================= */ +typedef enum { /*!< BLEIF_MSPICFG_RDFCPOL */ + BLEIF_MSPICFG_RDFCPOL_NORMAL = 0, /*!< NORMAL : SPI_STATUS signal from BLE Core high(1) creates flow + control and new read spi transactions will not be started + until the signal goes low.(default) value. */ + BLEIF_MSPICFG_RDFCPOL_INVERTED = 1, /*!< INVERTED : SPI_STATUS signal from BLE Core low(0) creates flow + control and new read spi transactions will not be started + until the signal goes high. value. */ +} BLEIF_MSPICFG_RDFCPOL_Enum; + +/* ============================================ BLEIF MSPICFG WTFCPOL [21..21] ============================================= */ +typedef enum { /*!< BLEIF_MSPICFG_WTFCPOL */ + BLEIF_MSPICFG_WTFCPOL_NORMAL = 0, /*!< NORMAL : SPI_STATUS signal from BLE Core high(1) creates flow + control and new write spi transactions will not be started + until the signal goes low.(default) value. */ + BLEIF_MSPICFG_WTFCPOL_INVERTED = 1, /*!< INVERTED : SPI_STATUS signal from BLE Core high(1) creates low(0) + control and new write spi transactions will not be started + until the signal goes high. value. */ +} BLEIF_MSPICFG_WTFCPOL_Enum; + +/* ============================================== BLEIF MSPICFG RDFC [17..17] ============================================== */ +typedef enum { /*!< BLEIF_MSPICFG_RDFC */ + BLEIF_MSPICFG_RDFC_DIS = 0, /*!< DIS : Read mode flow control disabled. value. */ + BLEIF_MSPICFG_RDFC_EN = 1, /*!< EN : Read mode flow control enabled. value. */ +} BLEIF_MSPICFG_RDFC_Enum; + +/* ============================================== BLEIF MSPICFG WTFC [16..16] ============================================== */ +typedef enum { /*!< BLEIF_MSPICFG_WTFC */ + BLEIF_MSPICFG_WTFC_DIS = 0, /*!< DIS : Write mode flow control disabled. value. */ + BLEIF_MSPICFG_WTFC_EN = 1, /*!< EN : Write mode flow control enabled. value. */ +} BLEIF_MSPICFG_WTFC_Enum; + +/* =============================================== BLEIF MSPICFG SPHA [1..1] =============================================== */ +typedef enum { /*!< BLEIF_MSPICFG_SPHA */ + BLEIF_MSPICFG_SPHA_SAMPLE_LEADING_EDGE = 0, /*!< SAMPLE_LEADING_EDGE : Sample on the leading (first) clock edge, + rising or falling dependant on the value of SPOL value. */ + BLEIF_MSPICFG_SPHA_SAMPLE_TRAILING_EDGE = 1, /*!< SAMPLE_TRAILING_EDGE : Sample on the trailing (second) clock + edge, rising of falling dependant on the value of SPOL + value. */ +} BLEIF_MSPICFG_SPHA_Enum; + +/* =============================================== BLEIF MSPICFG SPOL [0..0] =============================================== */ +typedef enum { /*!< BLEIF_MSPICFG_SPOL */ + BLEIF_MSPICFG_SPOL_CLK_BASE_0 = 0, /*!< CLK_BASE_0 : The initial value of the clock is 0. value. */ + BLEIF_MSPICFG_SPOL_CLK_BASE_1 = 1, /*!< CLK_BASE_1 : The initial value of the clock is 1. value. */ +} BLEIF_MSPICFG_SPOL_Enum; + +/* ======================================================== BLECFG ========================================================= */ +/* ============================================ BLEIF BLECFG SPIISOCTL [14..15] ============================================ */ +typedef enum { /*!< BLEIF_BLECFG_SPIISOCTL */ + BLEIF_BLECFG_SPIISOCTL_ON = 3, /*!< ON : SPI signals from BLE Core to/from MCU Core are isolated. + value. */ + BLEIF_BLECFG_SPIISOCTL_OFF = 2, /*!< OFF : SPI signals from BLE Core to/from MCU Core are not isolated. + value. */ + BLEIF_BLECFG_SPIISOCTL_AUTO = 0, /*!< AUTO : SPI signals from BLE Core to/from MCU Core are automatically + isolated by the logic value. */ +} BLEIF_BLECFG_SPIISOCTL_Enum; + +/* ============================================ BLEIF BLECFG PWRISOCTL [12..13] ============================================ */ +typedef enum { /*!< BLEIF_BLECFG_PWRISOCTL */ + BLEIF_BLECFG_PWRISOCTL_ON = 3, /*!< ON : BLEH power signal isolation to on (isolated). value. */ + BLEIF_BLECFG_PWRISOCTL_OFF = 2, /*!< OFF : BLEH power signal isolation to off (not isolated). value. */ + BLEIF_BLECFG_PWRISOCTL_AUTO = 0, /*!< AUTO : BLEH Power signal isolation is controlled automatically + through the interface logic value. */ +} BLEIF_BLECFG_PWRISOCTL_Enum; + +/* ============================================ BLEIF BLECFG BLEHREQCTL [6..7] ============================================= */ +typedef enum { /*!< BLEIF_BLECFG_BLEHREQCTL */ + BLEIF_BLECFG_BLEHREQCTL_ON = 3, /*!< ON : BLEH Power-on reg signal is set to on (1). value. */ + BLEIF_BLECFG_BLEHREQCTL_OFF = 2, /*!< OFF : BLEH Power-on signal is set to off (0). value. */ + BLEIF_BLECFG_BLEHREQCTL_AUTO = 0, /*!< AUTO : BLEH Power-on signal is controlled by the PWRSM logic + and automatically controlled value. */ +} BLEIF_BLECFG_BLEHREQCTL_Enum; + +/* ============================================ BLEIF BLECFG DCDCFLGCTL [4..5] ============================================= */ +typedef enum { /*!< BLEIF_BLECFG_DCDCFLGCTL */ + BLEIF_BLECFG_DCDCFLGCTL_ON = 3, /*!< ON : DCDC Flag signal is set to on (1). value. */ + BLEIF_BLECFG_DCDCFLGCTL_OFF = 2, /*!< OFF : DCDC Flag signal is set to off (0). value. */ + BLEIF_BLECFG_DCDCFLGCTL_AUTO = 0, /*!< AUTO : DCDC Flag signal is controlled by the PWRSM logic and + automatically controlled value. */ +} BLEIF_BLECFG_DCDCFLGCTL_Enum; + +/* ============================================= BLEIF BLECFG WAKEUPCTL [2..3] ============================================= */ +typedef enum { /*!< BLEIF_BLECFG_WAKEUPCTL */ + BLEIF_BLECFG_WAKEUPCTL_ON = 3, /*!< ON : Wake signal is set to on (1). value. */ + BLEIF_BLECFG_WAKEUPCTL_OFF = 2, /*!< OFF : Wake signal is set to off (0). value. */ + BLEIF_BLECFG_WAKEUPCTL_AUTO = 0, /*!< AUTO : Wake signal is controlled by the PWRSM logic and automatically + controlled value. */ +} BLEIF_BLECFG_WAKEUPCTL_Enum; + +/* ============================================== BLEIF BLECFG BLERSTN [1..1] ============================================== */ +typedef enum { /*!< BLEIF_BLECFG_BLERSTN */ + BLEIF_BLECFG_BLERSTN_ACTIVE = 1, /*!< ACTIVE : The reset signal is active (0) value. */ + BLEIF_BLECFG_BLERSTN_INACTIVE = 0, /*!< INACTIVE : The reset signal is inactive (1) value. */ +} BLEIF_BLECFG_BLERSTN_Enum; + +/* ============================================== BLEIF BLECFG PWRSMEN [0..0] ============================================== */ +typedef enum { /*!< BLEIF_BLECFG_PWRSMEN */ + BLEIF_BLECFG_PWRSMEN_ON = 1, /*!< ON : Internal power state machine is enabled and will sequence + the BLEH power domain as indicated in the design document. + Overrides for the power signals are not enabled. value. */ + BLEIF_BLECFG_PWRSMEN_OFF = 0, /*!< OFF : Internal power state machine is disabled and will not + sequence the BLEH power domain. The values of the overrides + will be used to drive the output sequencing signals value. */ +} BLEIF_BLECFG_PWRSMEN_Enum; + +/* ======================================================== PWRCMD ========================================================= */ +/* ======================================================== BSTATUS ======================================================== */ +/* ============================================== BLEIF BSTATUS PWRST [8..10] ============================================== */ +typedef enum { /*!< BLEIF_BSTATUS_PWRST */ + BLEIF_BSTATUS_PWRST_OFF = 0, /*!< OFF : Internal power state machine is disabled and will not + sequence the BLEH power domain. The values of the overrides + will be used to drive the output sequencing signals value. */ + BLEIF_BSTATUS_PWRST_INIT = 1, /*!< INIT : Initialization state. BLEH not powered value. */ + BLEIF_BSTATUS_PWRST_PWRON = 2, /*!< PWRON : Waiting for the powerup of the BLEH value. */ + BLEIF_BSTATUS_PWRST_ACTIVE = 3, /*!< ACTIVE : The BLE Core is powered and active value. */ + BLEIF_BSTATUS_PWRST_SLEEP = 6, /*!< SLEEP : The BLE Core has entered sleep mode and the power request + is inactive value. */ + BLEIF_BSTATUS_PWRST_SHUTDOWN = 4, /*!< SHUTDOWN : The BLE Core is in shutdown mode value. */ +} BLEIF_BSTATUS_PWRST_Enum; + +/* ============================================= BLEIF BSTATUS B2MSTATE [0..2] ============================================= */ +typedef enum { /*!< BLEIF_BSTATUS_B2MSTATE */ + BLEIF_BSTATUS_B2MSTATE_RESET = 0, /*!< RESET : Reset State value. */ + BLEIF_BSTATUS_B2MSTATE_Sleep = 1, /*!< Sleep : Sleep state. value. */ + BLEIF_BSTATUS_B2MSTATE_Standby = 2, /*!< Standby : Standby State value. */ + BLEIF_BSTATUS_B2MSTATE_Idle = 3, /*!< Idle : Idle state value. */ + BLEIF_BSTATUS_B2MSTATE_Active = 4, /*!< Active : Active state. value. */ +} BLEIF_BSTATUS_B2MSTATE_Enum; + +/* ======================================================== BLEDBG ========================================================= */ + + +/* =========================================================================================================================== */ +/* ================ CACHECTRL ================ */ +/* =========================================================================================================================== */ + +/* ======================================================= CACHECFG ======================================================== */ +/* =========================================== CACHECTRL CACHECFG CONFIG [4..7] ============================================ */ +typedef enum { /*!< CACHECTRL_CACHECFG_CONFIG */ + CACHECTRL_CACHECFG_CONFIG_W1_128B_512E = 4, /*!< W1_128B_512E : Direct mapped, 128-bit linesize, 512 entries + (4 SRAMs active) value. */ + CACHECTRL_CACHECFG_CONFIG_W2_128B_512E = 5, /*!< W2_128B_512E : Two-way set associative, 128-bit linesize, 512 + entries (8 SRAMs active) value. */ + CACHECTRL_CACHECFG_CONFIG_W1_128B_1024E = 8, /*!< W1_128B_1024E : Direct mapped, 128-bit linesize, 1024 entries + (8 SRAMs active) value. */ +} CACHECTRL_CACHECFG_CONFIG_Enum; + +/* ======================================================= FLASHCFG ======================================================== */ +/* ========================================== CACHECTRL FLASHCFG LPMMODE [12..13] ========================================== */ +typedef enum { /*!< CACHECTRL_FLASHCFG_LPMMODE */ + CACHECTRL_FLASHCFG_LPMMODE_NEVER = 0, /*!< NEVER : High power mode (LPM not used). value. */ + CACHECTRL_FLASHCFG_LPMMODE_STANDBY = 1, /*!< STANDBY : Fast Standby mode. LPM deasserted for read operations, + but asserted while flash IDLE. value. */ + CACHECTRL_FLASHCFG_LPMMODE_ALWAYS = 2, /*!< ALWAYS : Low Power mode. LPM always asserted for reads. LPM_RD_WAIT + must be programmed to accomodate longer read access times. + value. */ +} CACHECTRL_FLASHCFG_LPMMODE_Enum; + +/* ========================================================= CTRL ========================================================== */ +/* =========================================== CACHECTRL CTRL RESET_STAT [1..1] ============================================ */ +typedef enum { /*!< CACHECTRL_CTRL_RESET_STAT */ + CACHECTRL_CTRL_RESET_STAT_CLEAR = 1, /*!< CLEAR : Clear Cache Stats value. */ +} CACHECTRL_CTRL_RESET_STAT_Enum; + +/* ======================================================= NCR0START ======================================================= */ +/* ======================================================== NCR0END ======================================================== */ +/* ======================================================= NCR1START ======================================================= */ +/* ======================================================== NCR1END ======================================================== */ +/* ========================================================= DMON0 ========================================================= */ +/* ========================================================= DMON1 ========================================================= */ +/* ========================================================= DMON2 ========================================================= */ +/* ========================================================= DMON3 ========================================================= */ +/* ========================================================= IMON0 ========================================================= */ +/* ========================================================= IMON1 ========================================================= */ +/* ========================================================= IMON2 ========================================================= */ +/* ========================================================= IMON3 ========================================================= */ + + +/* =========================================================================================================================== */ +/* ================ CLKGEN ================ */ +/* =========================================================================================================================== */ + +/* ========================================================= CALXT ========================================================= */ +/* ========================================================= CALRC ========================================================= */ +/* ======================================================== ACALCTR ======================================================== */ +/* ========================================================= OCTRL ========================================================= */ +/* =============================================== CLKGEN OCTRL ACAL [8..10] =============================================== */ +typedef enum { /*!< CLKGEN_OCTRL_ACAL */ + CLKGEN_OCTRL_ACAL_DIS = 0, /*!< DIS : Disable Autocalibration value. */ + CLKGEN_OCTRL_ACAL_1024SEC = 2, /*!< 1024SEC : Autocalibrate every 1024 seconds. Once autocalibration + is done, an interrupt will be triggered at the end of 1024 + seconds. value. */ + CLKGEN_OCTRL_ACAL_512SEC = 3, /*!< 512SEC : Autocalibrate every 512 seconds. Once autocalibration + is done, an interrupt will be trigged at the end of 512 + seconds. value. */ + CLKGEN_OCTRL_ACAL_XTFREQ = 6, /*!< XTFREQ : Frequency measurement using XT. The XT clock is normally + considered much more accurate than the LFRC clock source. + value. */ + CLKGEN_OCTRL_ACAL_EXTFREQ = 7, /*!< EXTFREQ : Frequency measurement using external clock. value. */ +} CLKGEN_OCTRL_ACAL_Enum; + +/* =============================================== CLKGEN OCTRL OSEL [7..7] ================================================ */ +typedef enum { /*!< CLKGEN_OCTRL_OSEL */ + CLKGEN_OCTRL_OSEL_RTC_XT = 0, /*!< RTC_XT : RTC uses the XT value. */ + CLKGEN_OCTRL_OSEL_RTC_LFRC = 1, /*!< RTC_LFRC : RTC uses the LFRC value. */ +} CLKGEN_OCTRL_OSEL_Enum; + +/* ================================================ CLKGEN OCTRL FOS [6..6] ================================================ */ +typedef enum { /*!< CLKGEN_OCTRL_FOS */ + CLKGEN_OCTRL_FOS_DIS = 0, /*!< DIS : Disable the oscillator switch on failure function. value. */ + CLKGEN_OCTRL_FOS_EN = 1, /*!< EN : Enable the oscillator switch on failure function. value. */ +} CLKGEN_OCTRL_FOS_Enum; + +/* ============================================== CLKGEN OCTRL STOPRC [1..1] =============================================== */ +typedef enum { /*!< CLKGEN_OCTRL_STOPRC */ + CLKGEN_OCTRL_STOPRC_EN = 0, /*!< EN : Enable the LFRC Oscillator to drive the RTC value. */ + CLKGEN_OCTRL_STOPRC_STOP = 1, /*!< STOP : Stop the LFRC Oscillator when driving the RTC value. */ +} CLKGEN_OCTRL_STOPRC_Enum; + +/* ============================================== CLKGEN OCTRL STOPXT [0..0] =============================================== */ +typedef enum { /*!< CLKGEN_OCTRL_STOPXT */ + CLKGEN_OCTRL_STOPXT_EN = 0, /*!< EN : Enable the XT Oscillator to drive the RTC value. */ + CLKGEN_OCTRL_STOPXT_STOP = 1, /*!< STOP : Stop the XT Oscillator when driving the RTC value. */ +} CLKGEN_OCTRL_STOPXT_Enum; + +/* ======================================================== CLKOUT ========================================================= */ +/* =============================================== CLKGEN CLKOUT CKEN [7..7] =============================================== */ +typedef enum { /*!< CLKGEN_CLKOUT_CKEN */ + CLKGEN_CLKOUT_CKEN_DIS = 0, /*!< DIS : Disable CLKOUT value. */ + CLKGEN_CLKOUT_CKEN_EN = 1, /*!< EN : Enable CLKOUT value. */ +} CLKGEN_CLKOUT_CKEN_Enum; + +/* ============================================== CLKGEN CLKOUT CKSEL [0..5] =============================================== */ +typedef enum { /*!< CLKGEN_CLKOUT_CKSEL */ + CLKGEN_CLKOUT_CKSEL_LFRC = 0, /*!< LFRC : LFRC value. */ + CLKGEN_CLKOUT_CKSEL_XT_DIV2 = 1, /*!< XT_DIV2 : XT / 2 value. */ + CLKGEN_CLKOUT_CKSEL_XT_DIV4 = 2, /*!< XT_DIV4 : XT / 4 value. */ + CLKGEN_CLKOUT_CKSEL_XT_DIV8 = 3, /*!< XT_DIV8 : XT / 8 value. */ + CLKGEN_CLKOUT_CKSEL_XT_DIV16 = 4, /*!< XT_DIV16 : XT / 16 value. */ + CLKGEN_CLKOUT_CKSEL_XT_DIV32 = 5, /*!< XT_DIV32 : XT / 32 value. */ + CLKGEN_CLKOUT_CKSEL_RTC_1Hz = 16, /*!< RTC_1Hz : 1 Hz as selected in RTC value. */ + CLKGEN_CLKOUT_CKSEL_XT_DIV2M = 22, /*!< XT_DIV2M : XT / 2^21 value. */ + CLKGEN_CLKOUT_CKSEL_XT = 23, /*!< XT : XT value. */ + CLKGEN_CLKOUT_CKSEL_CG_100Hz = 24, /*!< CG_100Hz : 100 Hz as selected in CLKGEN value. */ + CLKGEN_CLKOUT_CKSEL_HFRC = 25, /*!< HFRC : HFRC value. */ + CLKGEN_CLKOUT_CKSEL_HFRC_DIV4 = 26, /*!< HFRC_DIV4 : HFRC / 4 value. */ + CLKGEN_CLKOUT_CKSEL_HFRC_DIV8 = 27, /*!< HFRC_DIV8 : HFRC / 8 value. */ + CLKGEN_CLKOUT_CKSEL_HFRC_DIV16 = 28, /*!< HFRC_DIV16 : HFRC / 16 value. */ + CLKGEN_CLKOUT_CKSEL_HFRC_DIV64 = 29, /*!< HFRC_DIV64 : HFRC / 64 value. */ + CLKGEN_CLKOUT_CKSEL_HFRC_DIV128 = 30, /*!< HFRC_DIV128 : HFRC / 128 value. */ + CLKGEN_CLKOUT_CKSEL_HFRC_DIV256 = 31, /*!< HFRC_DIV256 : HFRC / 256 value. */ + CLKGEN_CLKOUT_CKSEL_HFRC_DIV512 = 32, /*!< HFRC_DIV512 : HFRC / 512 value. */ + CLKGEN_CLKOUT_CKSEL_FLASH_CLK = 34, /*!< FLASH_CLK : Flash Clock value. */ + CLKGEN_CLKOUT_CKSEL_LFRC_DIV2 = 35, /*!< LFRC_DIV2 : LFRC / 2 value. */ + CLKGEN_CLKOUT_CKSEL_LFRC_DIV32 = 36, /*!< LFRC_DIV32 : LFRC / 32 value. */ + CLKGEN_CLKOUT_CKSEL_LFRC_DIV512 = 37, /*!< LFRC_DIV512 : LFRC / 512 value. */ + CLKGEN_CLKOUT_CKSEL_LFRC_DIV32K = 38, /*!< LFRC_DIV32K : LFRC / 32768 value. */ + CLKGEN_CLKOUT_CKSEL_XT_DIV256 = 39, /*!< XT_DIV256 : XT / 256 value. */ + CLKGEN_CLKOUT_CKSEL_XT_DIV8K = 40, /*!< XT_DIV8K : XT / 8192 value. */ + CLKGEN_CLKOUT_CKSEL_XT_DIV64K = 41, /*!< XT_DIV64K : XT / 2^16 value. */ + CLKGEN_CLKOUT_CKSEL_ULFRC_DIV16 = 42, /*!< ULFRC_DIV16 : Uncal LFRC / 16 value. */ + CLKGEN_CLKOUT_CKSEL_ULFRC_DIV128 = 43, /*!< ULFRC_DIV128 : Uncal LFRC / 128 value. */ + CLKGEN_CLKOUT_CKSEL_ULFRC_1Hz = 44, /*!< ULFRC_1Hz : Uncal LFRC / 1024 value. */ + CLKGEN_CLKOUT_CKSEL_ULFRC_DIV4K = 45, /*!< ULFRC_DIV4K : Uncal LFRC / 4096 value. */ + CLKGEN_CLKOUT_CKSEL_ULFRC_DIV1M = 46, /*!< ULFRC_DIV1M : Uncal LFRC / 2^20 value. */ + CLKGEN_CLKOUT_CKSEL_HFRC_DIV64K = 47, /*!< HFRC_DIV64K : HFRC / 2^16 value. */ + CLKGEN_CLKOUT_CKSEL_HFRC_DIV16M = 48, /*!< HFRC_DIV16M : HFRC / 2^24 value. */ + CLKGEN_CLKOUT_CKSEL_LFRC_DIV1M = 49, /*!< LFRC_DIV1M : LFRC / 2^20 value. */ + CLKGEN_CLKOUT_CKSEL_HFRCNE = 50, /*!< HFRCNE : HFRC (not autoenabled) value. */ + CLKGEN_CLKOUT_CKSEL_HFRCNE_DIV8 = 51, /*!< HFRCNE_DIV8 : HFRC / 8 (not autoenabled) value. */ + CLKGEN_CLKOUT_CKSEL_XTNE = 53, /*!< XTNE : XT (not autoenabled) value. */ + CLKGEN_CLKOUT_CKSEL_XTNE_DIV16 = 54, /*!< XTNE_DIV16 : XT / 16 (not autoenabled) value. */ + CLKGEN_CLKOUT_CKSEL_LFRCNE_DIV32 = 55, /*!< LFRCNE_DIV32 : LFRC / 32 (not autoenabled) value. */ + CLKGEN_CLKOUT_CKSEL_LFRCNE = 57, /*!< LFRCNE : LFRC (not autoenabled) - Default for undefined values + value. */ +} CLKGEN_CLKOUT_CKSEL_Enum; + +/* ======================================================== CLKKEY ========================================================= */ +/* ============================================= CLKGEN CLKKEY CLKKEY [0..31] ============================================== */ +typedef enum { /*!< CLKGEN_CLKKEY_CLKKEY */ + CLKGEN_CLKKEY_CLKKEY_Key = 71, /*!< Key : Key value. */ +} CLKGEN_CLKKEY_CLKKEY_Enum; + +/* ========================================================= CCTRL ========================================================= */ +/* ============================================== CLKGEN CCTRL CORESEL [0..0] ============================================== */ +typedef enum { /*!< CLKGEN_CCTRL_CORESEL */ + CLKGEN_CCTRL_CORESEL_HFRC = 0, /*!< HFRC : Core Clock is HFRC value. */ + CLKGEN_CCTRL_CORESEL_HFRC_DIV2 = 1, /*!< HFRC_DIV2 : Core Clock is HFRC / 2 value. */ +} CLKGEN_CCTRL_CORESEL_Enum; + +/* ======================================================== STATUS ========================================================= */ +/* ========================================================= HFADJ ========================================================= */ +/* ============================================ CLKGEN HFADJ HFADJGAIN [21..23] ============================================ */ +typedef enum { /*!< CLKGEN_HFADJ_HFADJGAIN */ + CLKGEN_HFADJ_HFADJGAIN_Gain_of_1 = 0, /*!< Gain_of_1 : HF Adjust with Gain of 1 value. */ + CLKGEN_HFADJ_HFADJGAIN_Gain_of_1_in_2 = 1, /*!< Gain_of_1_in_2 : HF Adjust with Gain of 0.5 value. */ + CLKGEN_HFADJ_HFADJGAIN_Gain_of_1_in_4 = 2, /*!< Gain_of_1_in_4 : HF Adjust with Gain of 0.25 value. */ + CLKGEN_HFADJ_HFADJGAIN_Gain_of_1_in_8 = 3, /*!< Gain_of_1_in_8 : HF Adjust with Gain of 0.125 value. */ + CLKGEN_HFADJ_HFADJGAIN_Gain_of_1_in_16 = 4, /*!< Gain_of_1_in_16 : HF Adjust with Gain of 0.0625 value. */ + CLKGEN_HFADJ_HFADJGAIN_Gain_of_1_in_32 = 5, /*!< Gain_of_1_in_32 : HF Adjust with Gain of 0.03125 value. */ +} CLKGEN_HFADJ_HFADJGAIN_Enum; + +/* ============================================ CLKGEN HFADJ HFWARMUP [20..20] ============================================= */ +typedef enum { /*!< CLKGEN_HFADJ_HFWARMUP */ + CLKGEN_HFADJ_HFWARMUP_1SEC = 0, /*!< 1SEC : Autoadjust XT warmup period = 1-2 seconds value. */ + CLKGEN_HFADJ_HFWARMUP_2SEC = 1, /*!< 2SEC : Autoadjust XT warmup period = 2-4 seconds value. */ +} CLKGEN_HFADJ_HFWARMUP_Enum; + +/* ============================================== CLKGEN HFADJ HFADJCK [1..3] ============================================== */ +typedef enum { /*!< CLKGEN_HFADJ_HFADJCK */ + CLKGEN_HFADJ_HFADJCK_4SEC = 0, /*!< 4SEC : Autoadjust repeat period = 4 seconds value. */ + CLKGEN_HFADJ_HFADJCK_16SEC = 1, /*!< 16SEC : Autoadjust repeat period = 16 seconds value. */ + CLKGEN_HFADJ_HFADJCK_32SEC = 2, /*!< 32SEC : Autoadjust repeat period = 32 seconds value. */ + CLKGEN_HFADJ_HFADJCK_64SEC = 3, /*!< 64SEC : Autoadjust repeat period = 64 seconds value. */ + CLKGEN_HFADJ_HFADJCK_128SEC = 4, /*!< 128SEC : Autoadjust repeat period = 128 seconds value. */ + CLKGEN_HFADJ_HFADJCK_256SEC = 5, /*!< 256SEC : Autoadjust repeat period = 256 seconds value. */ + CLKGEN_HFADJ_HFADJCK_512SEC = 6, /*!< 512SEC : Autoadjust repeat period = 512 seconds value. */ + CLKGEN_HFADJ_HFADJCK_1024SEC = 7, /*!< 1024SEC : Autoadjust repeat period = 1024 seconds value. */ +} CLKGEN_HFADJ_HFADJCK_Enum; + +/* ============================================== CLKGEN HFADJ HFADJEN [0..0] ============================================== */ +typedef enum { /*!< CLKGEN_HFADJ_HFADJEN */ + CLKGEN_HFADJ_HFADJEN_DIS = 0, /*!< DIS : Disable the HFRC adjustment value. */ + CLKGEN_HFADJ_HFADJEN_EN = 1, /*!< EN : Enable the HFRC adjustment value. */ +} CLKGEN_HFADJ_HFADJEN_Enum; + +/* ====================================================== CLOCKENSTAT ====================================================== */ +/* ======================================== CLKGEN CLOCKENSTAT CLOCKENSTAT [0..31] ========================================= */ +typedef enum { /*!< CLKGEN_CLOCKENSTAT_CLOCKENSTAT */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_ADC_CLKEN = 1, /*!< ADC_CLKEN : Clock enable for the ADC. value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_APBDMA_ACTIVITY_CLKEN = 2,/*!< APBDMA_ACTIVITY_CLKEN : Clock enable for the APBDMA ACTIVITY + value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_APBDMA_AOH_CLKEN = 4,/*!< APBDMA_AOH_CLKEN : Clock enable for the APBDMA AOH DOMAIN value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_APBDMA_AOL_CLKEN = 8,/*!< APBDMA_AOL_CLKEN : Clock enable for the APBDMA AOL DOMAIN value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_APBDMA_APB_CLKEN = 16,/*!< APBDMA_APB_CLKEN : Clock enable for the APBDMA_APB value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_APBDMA_BLEL_CLKEN = 32,/*!< APBDMA_BLEL_CLKEN : Clock enable for the APBDMA_BLEL value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_APBDMA_HCPA_CLKEN = 64,/*!< APBDMA_HCPA_CLKEN : Clock enable for the APBDMA_HCPA value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_APBDMA_HCPB_CLKEN = 128,/*!< APBDMA_HCPB_CLKEN : Clock enable for the APBDMA_HCPB value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_APBDMA_HCPC_CLKEN = 256,/*!< APBDMA_HCPC_CLKEN : Clock enable for the APBDMA_HCPC value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_APBDMA_MSPI_CLKEN = 512,/*!< APBDMA_MSPI_CLKEN : Clock enable for the APBDMA_MSPI value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_APBDMA_PDM_CLKEN = 1024,/*!< APBDMA_PDM_CLKEN : Clock enable for the APBDMA_PDM value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_BLEIF_CLK_CLKEN = 2048,/*!< BLEIF_CLK_CLKEN : Clock enable for the BLEIF value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_BLEIF_CLK32K_CLKEN = 4096,/*!< BLEIF_CLK32K_CLKEN : Clock enable for the BLEIF 32khZ CLOCK + value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER_CLKEN = 8192,/*!< CTIMER_CLKEN : Clock enable for the CTIMER BLOCK value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER0A_CLKEN = 16384,/*!< CTIMER0A_CLKEN : Clock enable for the CTIMER0A value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER0B_CLKEN = 32768,/*!< CTIMER0B_CLKEN : Clock enable for the CTIMER0B value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER1A_CLKEN = 65536,/*!< CTIMER1A_CLKEN : Clock enable for the CTIMER1A value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER1B_CLKEN = 131072,/*!< CTIMER1B_CLKEN : Clock enable for the CTIMER1B value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER2A_CLKEN = 262144,/*!< CTIMER2A_CLKEN : Clock enable for the CTIMER2A value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER2B_CLKEN = 524288,/*!< CTIMER2B_CLKEN : Clock enable for the CTIMER2B value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER3A_CLKEN = 1048576,/*!< CTIMER3A_CLKEN : Clock enable for the CTIMER3A value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER3B_CLKEN = 2097152,/*!< CTIMER3B_CLKEN : Clock enable for the CTIMER3B value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER4A_CLKEN = 4194304,/*!< CTIMER4A_CLKEN : Clock enable for the CTIMER4A value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER4B_CLKEN = 8388608,/*!< CTIMER4B_CLKEN : Clock enable for the CTIMER4B value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER5A_CLKEN = 16777216,/*!< CTIMER5A_CLKEN : Clock enable for the CTIMER5A value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER5B_CLKEN = 33554432,/*!< CTIMER5B_CLKEN : Clock enable for the CTIMER5B value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER6A_CLKEN = 67108864,/*!< CTIMER6A_CLKEN : Clock enable for the CTIMER6A value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER6B_CLKEN = 134217728,/*!< CTIMER6B_CLKEN : Clock enable for the CTIMER6B value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER7A_CLKEN = 268435456,/*!< CTIMER7A_CLKEN : Clock enable for the CTIMER7A value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER7B_CLKEN = 536870912,/*!< CTIMER7B_CLKEN : Clock enable for the CTIMER7B value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_DAP_CLKEN = 1073741824,/*!< DAP_CLKEN : Clock enable for the DAP value. */ + CLKGEN_CLOCKENSTAT_CLOCKENSTAT_IOMSTRIFC0_CLKEN = -2147483648,/*!< IOMSTRIFC0_CLKEN : Clock enable for the IOMSTRIFC0 value. */ +} CLKGEN_CLOCKENSTAT_CLOCKENSTAT_Enum; + +/* ===================================================== CLOCKEN2STAT ====================================================== */ +/* ======================================= CLKGEN CLOCKEN2STAT CLOCKEN2STAT [0..31] ======================================== */ +typedef enum { /*!< CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT */ + CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_IOMSTRIFC1_CLKEN = 1,/*!< IOMSTRIFC1_CLKEN : Clock enable for the IO MASTER 1 IFC INTERFACE + value. */ + CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_IOMSTRIFC2_CLKEN = 2,/*!< IOMSTRIFC2_CLKEN : Clock enable for the IO MASTER 2 IFC INTERFACE + value. */ + CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_IOMSTRIFC3_CLKEN = 4,/*!< IOMSTRIFC3_CLKEN : Clock enable for the IO MASTER 3 IFC INTERFACE + value. */ + CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_IOMSTRIFC4_CLKEN = 8,/*!< IOMSTRIFC4_CLKEN : Clock enable for the IO MASTER 4 IFC INTERFACE + value. */ + CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_IOMSTRIFC5_CLKEN = 16,/*!< IOMSTRIFC5_CLKEN : Clock enable for the IO MASTER 5 IFC INTERFACE + value. */ + CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_PDM_CLKEN = 32,/*!< PDM_CLKEN : Clock enable for the PDM value. */ + CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_PDMIFC_CLKEN = 64,/*!< PDMIFC_CLKEN : Clock enable for the PDM INTERFACE value. */ + CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_PWRCTRL_CLKEN = 128,/*!< PWRCTRL_CLKEN : Clock enable for the PWRCTRL value. */ + CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_RSTGEN_CLKEN = 256,/*!< RSTGEN_CLKEN : Clock enable for the RSTGEN value. */ + CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_SCARD_CLKEN = 512,/*!< SCARD_CLKEN : Clock enable for the SCARD value. */ + CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_SCARD_ALTAPB_CLKEN = 1024,/*!< SCARD_ALTAPB_CLKEN : Clock enable for the SCARD ALTAPB value. */ + CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_STIMER_CNT_CLKEN = 2048,/*!< STIMER_CNT_CLKEN : Clock enable for the STIMER_CNT_CLKEN value. */ + CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_TPIU_CLKEN = 4096,/*!< TPIU_CLKEN : Clock enable for the TPIU_CLKEN value. */ + CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_UART0HF_CLKEN = 8192,/*!< UART0HF_CLKEN : Clock enable for the UART0 HF value. */ + CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_UART1HF_CLKEN = 16384,/*!< UART1HF_CLKEN : Clock enable for the UART1 HF value. */ + CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_XT_32KHZ_EN = 1073741824,/*!< XT_32KHZ_EN : Clock enable for the XT 32KHZ value. */ + CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_FORCEHFRC = -2147483648,/*!< FORCEHFRC : HFRC is forced on Status. value. */ +} CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_Enum; + +/* ===================================================== CLOCKEN3STAT ====================================================== */ +/* ======================================= CLKGEN CLOCKEN3STAT CLOCKEN3STAT [0..31] ======================================== */ +typedef enum { /*!< CLKGEN_CLOCKEN3STAT_CLOCKEN3STAT */ + CLKGEN_CLOCKEN3STAT_CLOCKEN3STAT_XTAL_enabled = 16777216,/*!< XTAL_enabled : XTAL is enabled value. */ + CLKGEN_CLOCKEN3STAT_CLOCKEN3STAT_HFRC_enabled = 33554432,/*!< HFRC_enabled : HFRC is enabled value. */ + CLKGEN_CLOCKEN3STAT_CLOCKEN3STAT_HFADJEN = 67108864,/*!< HFADJEN : HFRC Adjust enabled value. */ + CLKGEN_CLOCKEN3STAT_CLOCKEN3STAT_HFRC_en_out = 134217728,/*!< HFRC_en_out : HFRC Enabled out value. */ + CLKGEN_CLOCKEN3STAT_CLOCKEN3STAT_RTC_XT = 268435456,/*!< RTC_XT : RTC use XT value. */ + CLKGEN_CLOCKEN3STAT_CLOCKEN3STAT_clkout_xtal_en = 536870912,/*!< clkout_xtal_en : XTAL clkout enabled value. */ + CLKGEN_CLOCKEN3STAT_CLOCKEN3STAT_clkout_hfrc_en = 1073741824,/*!< clkout_hfrc_en : HFRC clkout enabled value. */ + CLKGEN_CLOCKEN3STAT_CLOCKEN3STAT_flashclk_en = -2147483648,/*!< flashclk_en : Flash clk is enabled value. */ +} CLKGEN_CLOCKEN3STAT_CLOCKEN3STAT_Enum; + +/* ======================================================= FREQCTRL ======================================================== */ +/* ============================================ CLKGEN FREQCTRL BURSTREQ [0..0] ============================================ */ +typedef enum { /*!< CLKGEN_FREQCTRL_BURSTREQ */ + CLKGEN_FREQCTRL_BURSTREQ_DIS = 0, /*!< DIS : Frequency for ARM core stays at 48MHz value. */ + CLKGEN_FREQCTRL_BURSTREQ_EN = 1, /*!< EN : Frequency for ARM core is increased to 96MHz value. */ +} CLKGEN_FREQCTRL_BURSTREQ_Enum; + +/* ===================================================== BLEBUCKTONADJ ===================================================== */ +/* ===================================== CLKGEN BLEBUCKTONADJ ZEROLENDETECTEN [27..27] ===================================== */ +typedef enum { /*!< CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTEN */ + CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTEN_DIS = 0, /*!< DIS : Disable Zero Length Detect value. */ + CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTEN_EN = 1, /*!< EN : Enable Zero Length Detect value. */ +} CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTEN_Enum; + +/* ==================================== CLKGEN BLEBUCKTONADJ ZEROLENDETECTTRIM [23..26] ==================================== */ +typedef enum { /*!< CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM */ + CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_SetF = 15,/*!< SetF : Indicator send when the BLE BUCK asserts blebuck_comp1 + for about 81us (10 percent margin of error) or more value. */ + CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_SetE = 14,/*!< SetE : Indicator send when the BLE BUCK asserts blebuck_comp1 + for about 75.6us (10 percent margin of error) or more value. */ + CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_SetD = 13,/*!< SetD : Indicator send when the BLE BUCK asserts blebuck_comp1 + for about 70.2us (10 percent margin of error) or more value. */ + CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_SetC = 12,/*!< SetC : Indicator send when the BLE BUCK asserts blebuck_comp1 + for about 64.8us (10 percent margin of error) or more value. */ + CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_SetB = 11,/*!< SetB : Indicator send when the BLE BUCK asserts blebuck_comp1 + for about 59.4us (10 percent margin of error) or more value. */ + CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_SetA = 10,/*!< SetA : Indicator send when the BLE BUCK asserts blebuck_comp1 + for about 54.0us (10 percent margin of error) or more value. */ + CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_Set9 = 9,/*!< Set9 : Indicator send when the BLE BUCK asserts blebuck_comp1 + for about 48.6us (10 percent margin of error) or more value. */ + CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_Set8 = 8,/*!< Set8 : Indicator send when the BLE BUCK asserts blebuck_comp1 + for about 43.2us (10 percent margin of error) or more value. */ + CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_Set7 = 7,/*!< Set7 : Indicator send when the BLE BUCK asserts blebuck_comp1 + for about 37.8us (10 percent margin of error) or more value. */ + CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_Set6 = 6,/*!< Set6 : Indicator send when the BLE BUCK asserts blebuck_comp1 + for about 32.4us (10 percent margin of error) or more value. */ + CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_Set5 = 5,/*!< Set5 : Indicator send when the BLE BUCK asserts blebuck_comp1 + for about 27.0us (10 percent margin of error) or more value. */ + CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_Set4 = 4,/*!< Set4 : Indicator send when the BLE BUCK asserts blebuck_comp1 + for about 21.6us (10 percent margin of error) or more value. */ + CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_Set3 = 3,/*!< Set3 : Indicator send when the BLE BUCK asserts blebuck_comp1 + for about 16.2us (10 percent margin of error) or more value. */ + CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_Set2 = 2,/*!< Set2 : Indicator send when the BLE BUCK asserts blebuck_comp1 + for about 10.8us (10 percent margin of error) or more value. */ + CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_Set1 = 1,/*!< Set1 : Indicator send when the BLE BUCK asserts blebuck_comp1 + for about 5.4us (10 percent margin of error) or more value. */ + CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_Set0 = 0,/*!< Set0 : Indicator send when the BLE BUCK asserts blebuck_comp1 + for about 2.0us (10 percent margin of error) or more value. */ +} CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_Enum; + +/* ======================================= CLKGEN BLEBUCKTONADJ TONADJUSTEN [22..22] ======================================= */ +typedef enum { /*!< CLKGEN_BLEBUCKTONADJ_TONADJUSTEN */ + CLKGEN_BLEBUCKTONADJ_TONADJUSTEN_DIS = 0, /*!< DIS : Disable Adjust for BLE BUCK TON trim value. */ + CLKGEN_BLEBUCKTONADJ_TONADJUSTEN_EN = 1, /*!< EN : Enable Adjust for BLE BUCK TON trim value. */ +} CLKGEN_BLEBUCKTONADJ_TONADJUSTEN_Enum; + +/* ===================================== CLKGEN BLEBUCKTONADJ TONADJUSTPERIOD [20..21] ===================================== */ +typedef enum { /*!< CLKGEN_BLEBUCKTONADJ_TONADJUSTPERIOD */ + CLKGEN_BLEBUCKTONADJ_TONADJUSTPERIOD_HFRC_3KHz = 3,/*!< HFRC_3KHz : Adjust done for every 1 3KHz period value. */ + CLKGEN_BLEBUCKTONADJ_TONADJUSTPERIOD_HFRC_12KHz = 2,/*!< HFRC_12KHz : Adjust done for every 1 12KHz period value. */ + CLKGEN_BLEBUCKTONADJ_TONADJUSTPERIOD_HFRC_47KHz = 1,/*!< HFRC_47KHz : Adjust done for every 1 47KHz period value. */ + CLKGEN_BLEBUCKTONADJ_TONADJUSTPERIOD_HFRC_94KHz = 0,/*!< HFRC_94KHz : Adjust done for every 1 94KHz period value. */ +} CLKGEN_BLEBUCKTONADJ_TONADJUSTPERIOD_Enum; + +/* ======================================================= INTRPTEN ======================================================== */ +/* ====================================================== INTRPTSTAT ======================================================= */ +/* ======================================================= INTRPTCLR ======================================================= */ +/* ======================================================= INTRPTSET ======================================================= */ + + +/* =========================================================================================================================== */ +/* ================ CTIMER ================ */ +/* =========================================================================================================================== */ + +/* ========================================================= TMR0 ========================================================== */ +/* ======================================================== CMPRA0 ========================================================= */ +/* ======================================================== CMPRB0 ========================================================= */ +/* ========================================================= CTRL0 ========================================================= */ +/* ============================================= CTIMER CTRL0 CTLINK0 [31..31] ============================================= */ +typedef enum { /*!< CTIMER_CTRL0_CTLINK0 */ + CTIMER_CTRL0_CTLINK0_TWO_16BIT_TIMERS = 0, /*!< TWO_16BIT_TIMERS : Use A0/B0 timers as two independent 16-bit + timers (default). value. */ + CTIMER_CTRL0_CTLINK0_32BIT_TIMER = 1, /*!< 32BIT_TIMER : Link A0/B0 timers into a single 32-bit timer. + value. */ +} CTIMER_CTRL0_CTLINK0_Enum; + +/* ============================================ CTIMER CTRL0 TMRB0POL [28..28] ============================================= */ +typedef enum { /*!< CTIMER_CTRL0_TMRB0POL */ + CTIMER_CTRL0_TMRB0POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINB0 pin is the same as the + timer output. value. */ + CTIMER_CTRL0_TMRB0POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINB0 pin is the inverse of + the timer output. value. */ +} CTIMER_CTRL0_TMRB0POL_Enum; + +/* ============================================ CTIMER CTRL0 TMRB0CLR [27..27] ============================================= */ +typedef enum { /*!< CTIMER_CTRL0_TMRB0CLR */ + CTIMER_CTRL0_TMRB0CLR_RUN = 0, /*!< RUN : Allow counter/timer B0 to run value. */ + CTIMER_CTRL0_TMRB0CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer B0 at 0x0000. value. */ +} CTIMER_CTRL0_TMRB0CLR_Enum; + +/* ============================================ CTIMER CTRL0 TMRB0IE1 [26..26] ============================================= */ +typedef enum { /*!< CTIMER_CTRL0_TMRB0IE1 */ + CTIMER_CTRL0_TMRB0IE1_DIS = 0, /*!< DIS : Disable counter/timer B0 from generating an interrupt + based on COMPR1. value. */ + CTIMER_CTRL0_TMRB0IE1_EN = 1, /*!< EN : Enable counter/timer B0 to generate an interrupt based + on COMPR1. value. */ +} CTIMER_CTRL0_TMRB0IE1_Enum; + +/* ============================================ CTIMER CTRL0 TMRB0IE0 [25..25] ============================================= */ +typedef enum { /*!< CTIMER_CTRL0_TMRB0IE0 */ + CTIMER_CTRL0_TMRB0IE0_DIS = 0, /*!< DIS : Disable counter/timer B0 from generating an interrupt + based on COMPR0. value. */ + CTIMER_CTRL0_TMRB0IE0_EN = 1, /*!< EN : Enable counter/timer B0 to generate an interrupt based + on COMPR0 value. */ +} CTIMER_CTRL0_TMRB0IE0_Enum; + +/* ============================================= CTIMER CTRL0 TMRB0FN [22..24] ============================================= */ +typedef enum { /*!< CTIMER_CTRL0_TMRB0FN */ + CTIMER_CTRL0_TMRB0FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count + to CMPR0B0, stop. value. */ + CTIMER_CTRL0_TMRB0FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide + pulses). Count to CMPR0B0, restart. value. */ + CTIMER_CTRL0_TMRB0FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0B0, assert, + count to CMPR1B0, deassert, stop. value. */ + CTIMER_CTRL0_TMRB0FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0B0, assert, count + to CMPR1B0, deassert, restart. value. */ + CTIMER_CTRL0_TMRB0FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ + CTIMER_CTRL0_TMRB0FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ + CTIMER_CTRL0_TMRB0FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. + value. */ + CTIMER_CTRL0_TMRB0FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ +} CTIMER_CTRL0_TMRB0FN_Enum; + +/* ============================================ CTIMER CTRL0 TMRB0CLK [17..21] ============================================= */ +typedef enum { /*!< CTIMER_CTRL0_TMRB0CLK */ + CTIMER_CTRL0_TMRB0CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINB. value. */ + CTIMER_CTRL0_TMRB0CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ + CTIMER_CTRL0_TMRB0CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ + CTIMER_CTRL0_TMRB0CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ + CTIMER_CTRL0_TMRB0CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ + CTIMER_CTRL0_TMRB0CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ + CTIMER_CTRL0_TMRB0CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ + CTIMER_CTRL0_TMRB0CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ + CTIMER_CTRL0_TMRB0CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ + CTIMER_CTRL0_TMRB0CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ + CTIMER_CTRL0_TMRB0CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ + CTIMER_CTRL0_TMRB0CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ + CTIMER_CTRL0_TMRB0CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ + CTIMER_CTRL0_TMRB0CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ + CTIMER_CTRL0_TMRB0CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. + value. */ + CTIMER_CTRL0_TMRB0CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ + CTIMER_CTRL0_TMRB0CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ + CTIMER_CTRL0_TMRB0CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ + CTIMER_CTRL0_TMRB0CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ + CTIMER_CTRL0_TMRB0CLK_CTMRA0 = 20, /*!< CTMRA0 : Clock source is CTIMERA0 OUT. value. */ + CTIMER_CTRL0_TMRB0CLK_CTMRB1 = 21, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ + CTIMER_CTRL0_TMRB0CLK_CTMRA1 = 22, /*!< CTMRA1 : Clock source is CTIMERA1 OUT. value. */ + CTIMER_CTRL0_TMRB0CLK_CTMRA2 = 23, /*!< CTMRA2 : Clock source is CTIMERA2 OUT. value. */ + CTIMER_CTRL0_TMRB0CLK_CTMRB2 = 24, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ + CTIMER_CTRL0_TMRB0CLK_CTMRB3 = 25, /*!< CTMRB3 : Clock source is CTIMERB3 OUT. value. */ + CTIMER_CTRL0_TMRB0CLK_CTMRB4 = 26, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ + CTIMER_CTRL0_TMRB0CLK_CTMRB5 = 27, /*!< CTMRB5 : Clock source is CTIMERB5 OUT. value. */ + CTIMER_CTRL0_TMRB0CLK_CTMRB6 = 28, /*!< CTMRB6 : Clock source is CTIMERB6 OUT. value. */ + CTIMER_CTRL0_TMRB0CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ + CTIMER_CTRL0_TMRB0CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ + CTIMER_CTRL0_TMRB0CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ +} CTIMER_CTRL0_TMRB0CLK_Enum; + +/* ============================================= CTIMER CTRL0 TMRB0EN [16..16] ============================================= */ +typedef enum { /*!< CTIMER_CTRL0_TMRB0EN */ + CTIMER_CTRL0_TMRB0EN_DIS = 0, /*!< DIS : Counter/Timer B0 Disable. value. */ + CTIMER_CTRL0_TMRB0EN_EN = 1, /*!< EN : Counter/Timer B0 Enable. value. */ +} CTIMER_CTRL0_TMRB0EN_Enum; + +/* ============================================ CTIMER CTRL0 TMRA0POL [12..12] ============================================= */ +typedef enum { /*!< CTIMER_CTRL0_TMRA0POL */ + CTIMER_CTRL0_TMRA0POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINA0 pin is the same as the + timer output. value. */ + CTIMER_CTRL0_TMRA0POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINA0 pin is the inverse of + the timer output. value. */ +} CTIMER_CTRL0_TMRA0POL_Enum; + +/* ============================================ CTIMER CTRL0 TMRA0CLR [11..11] ============================================= */ +typedef enum { /*!< CTIMER_CTRL0_TMRA0CLR */ + CTIMER_CTRL0_TMRA0CLR_RUN = 0, /*!< RUN : Allow counter/timer A0 to run value. */ + CTIMER_CTRL0_TMRA0CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer A0 at 0x0000. value. */ +} CTIMER_CTRL0_TMRA0CLR_Enum; + +/* ============================================ CTIMER CTRL0 TMRA0IE1 [10..10] ============================================= */ +typedef enum { /*!< CTIMER_CTRL0_TMRA0IE1 */ + CTIMER_CTRL0_TMRA0IE1_DIS = 0, /*!< DIS : Disable counter/timer A0 from generating an interrupt + based on COMPR1. value. */ + CTIMER_CTRL0_TMRA0IE1_EN = 1, /*!< EN : Enable counter/timer A0 to generate an interrupt based + on COMPR1. value. */ +} CTIMER_CTRL0_TMRA0IE1_Enum; + +/* ============================================= CTIMER CTRL0 TMRA0IE0 [9..9] ============================================== */ +typedef enum { /*!< CTIMER_CTRL0_TMRA0IE0 */ + CTIMER_CTRL0_TMRA0IE0_DIS = 0, /*!< DIS : Disable counter/timer A0 from generating an interrupt + based on COMPR0. value. */ + CTIMER_CTRL0_TMRA0IE0_EN = 1, /*!< EN : Enable counter/timer A0 to generate an interrupt based + on COMPR0. value. */ +} CTIMER_CTRL0_TMRA0IE0_Enum; + +/* ============================================== CTIMER CTRL0 TMRA0FN [6..8] ============================================== */ +typedef enum { /*!< CTIMER_CTRL0_TMRA0FN */ + CTIMER_CTRL0_TMRA0FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count + to CMPR0A0, stop. value. */ + CTIMER_CTRL0_TMRA0FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide + pulses). Count to CMPR0A0, restart. value. */ + CTIMER_CTRL0_TMRA0FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0A0, assert, + count to CMPR1A0, deassert, stop. value. */ + CTIMER_CTRL0_TMRA0FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0A0, assert, count + to CMPR1A0, deassert, restart. value. */ + CTIMER_CTRL0_TMRA0FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ + CTIMER_CTRL0_TMRA0FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ + CTIMER_CTRL0_TMRA0FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. + value. */ + CTIMER_CTRL0_TMRA0FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ +} CTIMER_CTRL0_TMRA0FN_Enum; + +/* ============================================= CTIMER CTRL0 TMRA0CLK [1..5] ============================================== */ +typedef enum { /*!< CTIMER_CTRL0_TMRA0CLK */ + CTIMER_CTRL0_TMRA0CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINA. value. */ + CTIMER_CTRL0_TMRA0CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ + CTIMER_CTRL0_TMRA0CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ + CTIMER_CTRL0_TMRA0CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ + CTIMER_CTRL0_TMRA0CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ + CTIMER_CTRL0_TMRA0CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ + CTIMER_CTRL0_TMRA0CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ + CTIMER_CTRL0_TMRA0CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ + CTIMER_CTRL0_TMRA0CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ + CTIMER_CTRL0_TMRA0CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ + CTIMER_CTRL0_TMRA0CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ + CTIMER_CTRL0_TMRA0CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ + CTIMER_CTRL0_TMRA0CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ + CTIMER_CTRL0_TMRA0CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ + CTIMER_CTRL0_TMRA0CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. + value. */ + CTIMER_CTRL0_TMRA0CLK_HCLK_DIV4 = 15, /*!< HCLK_DIV4 : Clock source is HCLK / 4. value. */ + CTIMER_CTRL0_TMRA0CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ + CTIMER_CTRL0_TMRA0CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ + CTIMER_CTRL0_TMRA0CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ + CTIMER_CTRL0_TMRA0CLK_CTMRB0 = 20, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ + CTIMER_CTRL0_TMRA0CLK_CTMRA1 = 21, /*!< CTMRA1 : Clock source is CTIMERA1 OUT. value. */ + CTIMER_CTRL0_TMRA0CLK_CTMRB1 = 22, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ + CTIMER_CTRL0_TMRA0CLK_CTMRA2 = 23, /*!< CTMRA2 : Clock source is CTIMERA2 OUT. value. */ + CTIMER_CTRL0_TMRA0CLK_CTMRB2 = 24, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ + CTIMER_CTRL0_TMRA0CLK_CTMRB3 = 25, /*!< CTMRB3 : Clock source is CTIMERB3 OUT. value. */ + CTIMER_CTRL0_TMRA0CLK_CTMRB4 = 26, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ + CTIMER_CTRL0_TMRA0CLK_CTMRB5 = 27, /*!< CTMRB5 : Clock source is CTIMERB5 OUT. value. */ + CTIMER_CTRL0_TMRA0CLK_CTMRB6 = 28, /*!< CTMRB6 : Clock source is CTIMERB6 OUT. value. */ + CTIMER_CTRL0_TMRA0CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ + CTIMER_CTRL0_TMRA0CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ + CTIMER_CTRL0_TMRA0CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ +} CTIMER_CTRL0_TMRA0CLK_Enum; + +/* ============================================== CTIMER CTRL0 TMRA0EN [0..0] ============================================== */ +typedef enum { /*!< CTIMER_CTRL0_TMRA0EN */ + CTIMER_CTRL0_TMRA0EN_DIS = 0, /*!< DIS : Counter/Timer A0 Disable. value. */ + CTIMER_CTRL0_TMRA0EN_EN = 1, /*!< EN : Counter/Timer A0 Enable. value. */ +} CTIMER_CTRL0_TMRA0EN_Enum; + +/* ======================================================= CMPRAUXA0 ======================================================= */ +/* ======================================================= CMPRAUXB0 ======================================================= */ +/* ========================================================= AUX0 ========================================================== */ +/* ============================================ CTIMER AUX0 TMRB0EN23 [30..30] ============================================= */ +typedef enum { /*!< CTIMER_AUX0_TMRB0EN23 */ + CTIMER_AUX0_TMRB0EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ + CTIMER_AUX0_TMRB0EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ +} CTIMER_AUX0_TMRB0EN23_Enum; + +/* ============================================ CTIMER AUX0 TMRB0POL23 [29..29] ============================================ */ +typedef enum { /*!< CTIMER_AUX0_TMRB0POL23 */ + CTIMER_AUX0_TMRB0POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ + CTIMER_AUX0_TMRB0POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ +} CTIMER_AUX0_TMRB0POL23_Enum; + +/* ============================================ CTIMER AUX0 TMRB0TINV [28..28] ============================================= */ +typedef enum { /*!< CTIMER_AUX0_TMRB0TINV */ + CTIMER_AUX0_TMRB0TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ + CTIMER_AUX0_TMRB0TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ +} CTIMER_AUX0_TMRB0TINV_Enum; + +/* =========================================== CTIMER AUX0 TMRB0NOSYNC [27..27] ============================================ */ +typedef enum { /*!< CTIMER_AUX0_TMRB0NOSYNC */ + CTIMER_AUX0_TMRB0NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ + CTIMER_AUX0_TMRB0NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ +} CTIMER_AUX0_TMRB0NOSYNC_Enum; + +/* ============================================ CTIMER AUX0 TMRB0TRIG [23..26] ============================================= */ +typedef enum { /*!< CTIMER_AUX0_TMRB0TRIG */ + CTIMER_AUX0_TMRB0TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ + CTIMER_AUX0_TMRB0TRIG_A0OUT = 1, /*!< A0OUT : Trigger source is CTIMERA0 OUT. value. */ + CTIMER_AUX0_TMRB0TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ + CTIMER_AUX0_TMRB0TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ + CTIMER_AUX0_TMRB0TRIG_B2OUT = 4, /*!< B2OUT : Trigger source is CTIMERB2 OUT. value. */ + CTIMER_AUX0_TMRB0TRIG_B5OUT = 5, /*!< B5OUT : Trigger source is CTIMERB5 OUT. value. */ + CTIMER_AUX0_TMRB0TRIG_A4OUT = 6, /*!< A4OUT : Trigger source is CTIMERA4 OUT. value. */ + CTIMER_AUX0_TMRB0TRIG_B4OUT = 7, /*!< B4OUT : Trigger source is CTIMERB4 OUT. value. */ + CTIMER_AUX0_TMRB0TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ + CTIMER_AUX0_TMRB0TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ + CTIMER_AUX0_TMRB0TRIG_B7OUT2 = 10, /*!< B7OUT2 : Trigger source is CTIMERB7 OUT2. value. */ + CTIMER_AUX0_TMRB0TRIG_A2OUT2 = 11, /*!< A2OUT2 : Trigger source is CTIMERA2 OUT2. value. */ + CTIMER_AUX0_TMRB0TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ + CTIMER_AUX0_TMRB0TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ + CTIMER_AUX0_TMRB0TRIG_B5OUT2DUAL = 14, /*!< B5OUT2DUAL : Trigger source is CTIMERB5 OUT2, dual edge. value. */ + CTIMER_AUX0_TMRB0TRIG_A5OUT2DUAL = 15, /*!< A5OUT2DUAL : Trigger source is CTIMERA5 OUT2, dual edge. value. */ +} CTIMER_AUX0_TMRB0TRIG_Enum; + +/* ============================================ CTIMER AUX0 TMRA0EN23 [14..14] ============================================= */ +typedef enum { /*!< CTIMER_AUX0_TMRA0EN23 */ + CTIMER_AUX0_TMRA0EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ + CTIMER_AUX0_TMRA0EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ +} CTIMER_AUX0_TMRA0EN23_Enum; + +/* ============================================ CTIMER AUX0 TMRA0POL23 [13..13] ============================================ */ +typedef enum { /*!< CTIMER_AUX0_TMRA0POL23 */ + CTIMER_AUX0_TMRA0POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ + CTIMER_AUX0_TMRA0POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ +} CTIMER_AUX0_TMRA0POL23_Enum; + +/* ============================================ CTIMER AUX0 TMRA0TINV [12..12] ============================================= */ +typedef enum { /*!< CTIMER_AUX0_TMRA0TINV */ + CTIMER_AUX0_TMRA0TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ + CTIMER_AUX0_TMRA0TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ +} CTIMER_AUX0_TMRA0TINV_Enum; + +/* =========================================== CTIMER AUX0 TMRA0NOSYNC [11..11] ============================================ */ +typedef enum { /*!< CTIMER_AUX0_TMRA0NOSYNC */ + CTIMER_AUX0_TMRA0NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ + CTIMER_AUX0_TMRA0NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ +} CTIMER_AUX0_TMRA0NOSYNC_Enum; + +/* ============================================= CTIMER AUX0 TMRA0TRIG [7..10] ============================================= */ +typedef enum { /*!< CTIMER_AUX0_TMRA0TRIG */ + CTIMER_AUX0_TMRA0TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ + CTIMER_AUX0_TMRA0TRIG_B0OUT = 1, /*!< B0OUT : Trigger source is CTIMERB0 OUT. value. */ + CTIMER_AUX0_TMRA0TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ + CTIMER_AUX0_TMRA0TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ + CTIMER_AUX0_TMRA0TRIG_A1OUT = 4, /*!< A1OUT : Trigger source is CTIMERA1 OUT. value. */ + CTIMER_AUX0_TMRA0TRIG_B1OUT = 5, /*!< B1OUT : Trigger source is CTIMERB1 OUT. value. */ + CTIMER_AUX0_TMRA0TRIG_A5OUT = 6, /*!< A5OUT : Trigger source is CTIMERA5 OUT. value. */ + CTIMER_AUX0_TMRA0TRIG_B5OUT = 7, /*!< B5OUT : Trigger source is CTIMERB5 OUT. value. */ + CTIMER_AUX0_TMRA0TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ + CTIMER_AUX0_TMRA0TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ + CTIMER_AUX0_TMRA0TRIG_B6OUT2 = 10, /*!< B6OUT2 : Trigger source is CTIMERB6 OUT2. value. */ + CTIMER_AUX0_TMRA0TRIG_A2OUT2 = 11, /*!< A2OUT2 : Trigger source is CTIMERA2 OUT2. value. */ + CTIMER_AUX0_TMRA0TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ + CTIMER_AUX0_TMRA0TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ + CTIMER_AUX0_TMRA0TRIG_B4OUT2DUAL = 14, /*!< B4OUT2DUAL : Trigger source is CTIMERB4 OUT2, dual edge. value. */ + CTIMER_AUX0_TMRA0TRIG_A4OUT2DUAL = 15, /*!< A4OUT2DUAL : Trigger source is CTIMERA4 OUT2, dual edge. value. */ +} CTIMER_AUX0_TMRA0TRIG_Enum; + +/* ========================================================= TMR1 ========================================================== */ +/* ======================================================== CMPRA1 ========================================================= */ +/* ======================================================== CMPRB1 ========================================================= */ +/* ========================================================= CTRL1 ========================================================= */ +/* ============================================= CTIMER CTRL1 CTLINK1 [31..31] ============================================= */ +typedef enum { /*!< CTIMER_CTRL1_CTLINK1 */ + CTIMER_CTRL1_CTLINK1_TWO_16BIT_TIMERS = 0, /*!< TWO_16BIT_TIMERS : Use A1/B1 timers as two independent 16-bit + timers (default). value. */ + CTIMER_CTRL1_CTLINK1_32BIT_TIMER = 1, /*!< 32BIT_TIMER : Link A1/B1 timers into a single 32-bit timer. + value. */ +} CTIMER_CTRL1_CTLINK1_Enum; + +/* ============================================ CTIMER CTRL1 TMRB1POL [28..28] ============================================= */ +typedef enum { /*!< CTIMER_CTRL1_TMRB1POL */ + CTIMER_CTRL1_TMRB1POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINB1 pin is the same as the + timer output. value. */ + CTIMER_CTRL1_TMRB1POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINB1 pin is the inverse of + the timer output. value. */ +} CTIMER_CTRL1_TMRB1POL_Enum; + +/* ============================================ CTIMER CTRL1 TMRB1CLR [27..27] ============================================= */ +typedef enum { /*!< CTIMER_CTRL1_TMRB1CLR */ + CTIMER_CTRL1_TMRB1CLR_RUN = 0, /*!< RUN : Allow counter/timer B1 to run value. */ + CTIMER_CTRL1_TMRB1CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer B1 at 0x0000. value. */ +} CTIMER_CTRL1_TMRB1CLR_Enum; + +/* ============================================ CTIMER CTRL1 TMRB1IE1 [26..26] ============================================= */ +typedef enum { /*!< CTIMER_CTRL1_TMRB1IE1 */ + CTIMER_CTRL1_TMRB1IE1_DIS = 0, /*!< DIS : Disable counter/timer B1 from generating an interrupt + based on COMPR1. value. */ + CTIMER_CTRL1_TMRB1IE1_EN = 1, /*!< EN : Enable counter/timer B1 to generate an interrupt based + on COMPR1. value. */ +} CTIMER_CTRL1_TMRB1IE1_Enum; + +/* ============================================ CTIMER CTRL1 TMRB1IE0 [25..25] ============================================= */ +typedef enum { /*!< CTIMER_CTRL1_TMRB1IE0 */ + CTIMER_CTRL1_TMRB1IE0_DIS = 0, /*!< DIS : Disable counter/timer B1 from generating an interrupt + based on COMPR0. value. */ + CTIMER_CTRL1_TMRB1IE0_EN = 1, /*!< EN : Enable counter/timer B1 to generate an interrupt based + on COMPR0 value. */ +} CTIMER_CTRL1_TMRB1IE0_Enum; + +/* ============================================= CTIMER CTRL1 TMRB1FN [22..24] ============================================= */ +typedef enum { /*!< CTIMER_CTRL1_TMRB1FN */ + CTIMER_CTRL1_TMRB1FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count + to CMPR0B1, stop. value. */ + CTIMER_CTRL1_TMRB1FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide + pulses). Count to CMPR0B1, restart. value. */ + CTIMER_CTRL1_TMRB1FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0B1, assert, + count to CMPR1B1, deassert, stop. value. */ + CTIMER_CTRL1_TMRB1FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0B1, assert, count + to CMPR1B1, deassert, restart. value. */ + CTIMER_CTRL1_TMRB1FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ + CTIMER_CTRL1_TMRB1FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ + CTIMER_CTRL1_TMRB1FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. + value. */ + CTIMER_CTRL1_TMRB1FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ +} CTIMER_CTRL1_TMRB1FN_Enum; + +/* ============================================ CTIMER CTRL1 TMRB1CLK [17..21] ============================================= */ +typedef enum { /*!< CTIMER_CTRL1_TMRB1CLK */ + CTIMER_CTRL1_TMRB1CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINB. value. */ + CTIMER_CTRL1_TMRB1CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ + CTIMER_CTRL1_TMRB1CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ + CTIMER_CTRL1_TMRB1CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ + CTIMER_CTRL1_TMRB1CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ + CTIMER_CTRL1_TMRB1CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ + CTIMER_CTRL1_TMRB1CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ + CTIMER_CTRL1_TMRB1CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ + CTIMER_CTRL1_TMRB1CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ + CTIMER_CTRL1_TMRB1CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ + CTIMER_CTRL1_TMRB1CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ + CTIMER_CTRL1_TMRB1CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ + CTIMER_CTRL1_TMRB1CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ + CTIMER_CTRL1_TMRB1CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ + CTIMER_CTRL1_TMRB1CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. + value. */ + CTIMER_CTRL1_TMRB1CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ + CTIMER_CTRL1_TMRB1CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ + CTIMER_CTRL1_TMRB1CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ + CTIMER_CTRL1_TMRB1CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ + CTIMER_CTRL1_TMRB1CLK_CTMRA1 = 20, /*!< CTMRA1 : Clock source is CTIMERA1 OUT. value. */ + CTIMER_CTRL1_TMRB1CLK_CTMRA0 = 21, /*!< CTMRA0 : Clock source is CTIMERA0 OUT. value. */ + CTIMER_CTRL1_TMRB1CLK_CTMRB0 = 22, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ + CTIMER_CTRL1_TMRB1CLK_CTMRA2 = 23, /*!< CTMRA2 : Clock source is CTIMERA2 OUT. value. */ + CTIMER_CTRL1_TMRB1CLK_CTMRB2 = 24, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ + CTIMER_CTRL1_TMRB1CLK_CTMRB3 = 25, /*!< CTMRB3 : Clock source is CTIMERB3 OUT. value. */ + CTIMER_CTRL1_TMRB1CLK_CTMRB4 = 26, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ + CTIMER_CTRL1_TMRB1CLK_CTMRB5 = 27, /*!< CTMRB5 : Clock source is CTIMERB5 OUT. value. */ + CTIMER_CTRL1_TMRB1CLK_CTMRB6 = 28, /*!< CTMRB6 : Clock source is CTIMERB6 OUT. value. */ + CTIMER_CTRL1_TMRB1CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ + CTIMER_CTRL1_TMRB1CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ + CTIMER_CTRL1_TMRB1CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ +} CTIMER_CTRL1_TMRB1CLK_Enum; + +/* ============================================= CTIMER CTRL1 TMRB1EN [16..16] ============================================= */ +typedef enum { /*!< CTIMER_CTRL1_TMRB1EN */ + CTIMER_CTRL1_TMRB1EN_DIS = 0, /*!< DIS : Counter/Timer B1 Disable. value. */ + CTIMER_CTRL1_TMRB1EN_EN = 1, /*!< EN : Counter/Timer B1 Enable. value. */ +} CTIMER_CTRL1_TMRB1EN_Enum; + +/* ============================================ CTIMER CTRL1 TMRA1POL [12..12] ============================================= */ +typedef enum { /*!< CTIMER_CTRL1_TMRA1POL */ + CTIMER_CTRL1_TMRA1POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINA1 pin is the same as the + timer output. value. */ + CTIMER_CTRL1_TMRA1POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINA1 pin is the inverse of + the timer output. value. */ +} CTIMER_CTRL1_TMRA1POL_Enum; + +/* ============================================ CTIMER CTRL1 TMRA1CLR [11..11] ============================================= */ +typedef enum { /*!< CTIMER_CTRL1_TMRA1CLR */ + CTIMER_CTRL1_TMRA1CLR_RUN = 0, /*!< RUN : Allow counter/timer A1 to run value. */ + CTIMER_CTRL1_TMRA1CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer A1 at 0x0000. value. */ +} CTIMER_CTRL1_TMRA1CLR_Enum; + +/* ============================================ CTIMER CTRL1 TMRA1IE1 [10..10] ============================================= */ +typedef enum { /*!< CTIMER_CTRL1_TMRA1IE1 */ + CTIMER_CTRL1_TMRA1IE1_DIS = 0, /*!< DIS : Disable counter/timer A1 from generating an interrupt + based on COMPR1. value. */ + CTIMER_CTRL1_TMRA1IE1_EN = 1, /*!< EN : Enable counter/timer A1 to generate an interrupt based + on COMPR1. value. */ +} CTIMER_CTRL1_TMRA1IE1_Enum; + +/* ============================================= CTIMER CTRL1 TMRA1IE0 [9..9] ============================================== */ +typedef enum { /*!< CTIMER_CTRL1_TMRA1IE0 */ + CTIMER_CTRL1_TMRA1IE0_DIS = 0, /*!< DIS : Disable counter/timer A1 from generating an interrupt + based on COMPR0. value. */ + CTIMER_CTRL1_TMRA1IE0_EN = 1, /*!< EN : Enable counter/timer A1 to generate an interrupt based + on COMPR0. value. */ +} CTIMER_CTRL1_TMRA1IE0_Enum; + +/* ============================================== CTIMER CTRL1 TMRA1FN [6..8] ============================================== */ +typedef enum { /*!< CTIMER_CTRL1_TMRA1FN */ + CTIMER_CTRL1_TMRA1FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count + to CMPR0A1, stop. value. */ + CTIMER_CTRL1_TMRA1FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide + pulses). Count to CMPR0A1, restart. value. */ + CTIMER_CTRL1_TMRA1FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0A1, assert, + count to CMPR1A1, deassert, stop. value. */ + CTIMER_CTRL1_TMRA1FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0A1, assert, count + to CMPR1A1, deassert, restart. value. */ + CTIMER_CTRL1_TMRA1FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ + CTIMER_CTRL1_TMRA1FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ + CTIMER_CTRL1_TMRA1FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. + value. */ + CTIMER_CTRL1_TMRA1FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ +} CTIMER_CTRL1_TMRA1FN_Enum; + +/* ============================================= CTIMER CTRL1 TMRA1CLK [1..5] ============================================== */ +typedef enum { /*!< CTIMER_CTRL1_TMRA1CLK */ + CTIMER_CTRL1_TMRA1CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINA. value. */ + CTIMER_CTRL1_TMRA1CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ + CTIMER_CTRL1_TMRA1CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ + CTIMER_CTRL1_TMRA1CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ + CTIMER_CTRL1_TMRA1CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ + CTIMER_CTRL1_TMRA1CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ + CTIMER_CTRL1_TMRA1CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ + CTIMER_CTRL1_TMRA1CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ + CTIMER_CTRL1_TMRA1CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ + CTIMER_CTRL1_TMRA1CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ + CTIMER_CTRL1_TMRA1CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ + CTIMER_CTRL1_TMRA1CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ + CTIMER_CTRL1_TMRA1CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ + CTIMER_CTRL1_TMRA1CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ + CTIMER_CTRL1_TMRA1CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. + value. */ + CTIMER_CTRL1_TMRA1CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ + CTIMER_CTRL1_TMRA1CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ + CTIMER_CTRL1_TMRA1CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ + CTIMER_CTRL1_TMRA1CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ + CTIMER_CTRL1_TMRA1CLK_CTMRB1 = 20, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ + CTIMER_CTRL1_TMRA1CLK_CTMRA0 = 21, /*!< CTMRA0 : Clock source is CTIMERA0 OUT. value. */ + CTIMER_CTRL1_TMRA1CLK_CTMRB0 = 22, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ + CTIMER_CTRL1_TMRA1CLK_CTMRA2 = 23, /*!< CTMRA2 : Clock source is CTIMERA2 OUT. value. */ + CTIMER_CTRL1_TMRA1CLK_CTMRB2 = 24, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ + CTIMER_CTRL1_TMRA1CLK_CTMRB3 = 25, /*!< CTMRB3 : Clock source is CTIMERB3 OUT. value. */ + CTIMER_CTRL1_TMRA1CLK_CTMRB4 = 26, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ + CTIMER_CTRL1_TMRA1CLK_CTMRB5 = 27, /*!< CTMRB5 : Clock source is CTIMERB5 OUT. value. */ + CTIMER_CTRL1_TMRA1CLK_CTMRB6 = 28, /*!< CTMRB6 : Clock source is CTIMERB6 OUT. value. */ + CTIMER_CTRL1_TMRA1CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ + CTIMER_CTRL1_TMRA1CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ + CTIMER_CTRL1_TMRA1CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ +} CTIMER_CTRL1_TMRA1CLK_Enum; + +/* ============================================== CTIMER CTRL1 TMRA1EN [0..0] ============================================== */ +typedef enum { /*!< CTIMER_CTRL1_TMRA1EN */ + CTIMER_CTRL1_TMRA1EN_DIS = 0, /*!< DIS : Counter/Timer A1 Disable. value. */ + CTIMER_CTRL1_TMRA1EN_EN = 1, /*!< EN : Counter/Timer A1 Enable. value. */ +} CTIMER_CTRL1_TMRA1EN_Enum; + +/* ======================================================= CMPRAUXA1 ======================================================= */ +/* ======================================================= CMPRAUXB1 ======================================================= */ +/* ========================================================= AUX1 ========================================================== */ +/* ============================================ CTIMER AUX1 TMRB1EN23 [30..30] ============================================= */ +typedef enum { /*!< CTIMER_AUX1_TMRB1EN23 */ + CTIMER_AUX1_TMRB1EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ + CTIMER_AUX1_TMRB1EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ +} CTIMER_AUX1_TMRB1EN23_Enum; + +/* ============================================ CTIMER AUX1 TMRB1POL23 [29..29] ============================================ */ +typedef enum { /*!< CTIMER_AUX1_TMRB1POL23 */ + CTIMER_AUX1_TMRB1POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ + CTIMER_AUX1_TMRB1POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ +} CTIMER_AUX1_TMRB1POL23_Enum; + +/* ============================================ CTIMER AUX1 TMRB1TINV [28..28] ============================================= */ +typedef enum { /*!< CTIMER_AUX1_TMRB1TINV */ + CTIMER_AUX1_TMRB1TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ + CTIMER_AUX1_TMRB1TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ +} CTIMER_AUX1_TMRB1TINV_Enum; + +/* =========================================== CTIMER AUX1 TMRB1NOSYNC [27..27] ============================================ */ +typedef enum { /*!< CTIMER_AUX1_TMRB1NOSYNC */ + CTIMER_AUX1_TMRB1NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ + CTIMER_AUX1_TMRB1NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ +} CTIMER_AUX1_TMRB1NOSYNC_Enum; + +/* ============================================ CTIMER AUX1 TMRB1TRIG [23..26] ============================================= */ +typedef enum { /*!< CTIMER_AUX1_TMRB1TRIG */ + CTIMER_AUX1_TMRB1TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ + CTIMER_AUX1_TMRB1TRIG_A1OUT = 1, /*!< A1OUT : Trigger source is CTIMERA1 OUT. value. */ + CTIMER_AUX1_TMRB1TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ + CTIMER_AUX1_TMRB1TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ + CTIMER_AUX1_TMRB1TRIG_A6OUT = 4, /*!< A6OUT : Trigger source is CTIMERA6 OUT. value. */ + CTIMER_AUX1_TMRB1TRIG_B6OUT = 5, /*!< B6OUT : Trigger source is CTIMERB6 OUT. value. */ + CTIMER_AUX1_TMRB1TRIG_A0OUT = 6, /*!< A0OUT : Trigger source is CTIMERA0 OUT. value. */ + CTIMER_AUX1_TMRB1TRIG_B0OUT = 7, /*!< B0OUT : Trigger source is CTIMERB0 OUT. value. */ + CTIMER_AUX1_TMRB1TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ + CTIMER_AUX1_TMRB1TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ + CTIMER_AUX1_TMRB1TRIG_A4OUT2 = 10, /*!< A4OUT2 : Trigger source is CTIMERA4 OUT2. value. */ + CTIMER_AUX1_TMRB1TRIG_B4OUT2 = 11, /*!< B4OUT2 : Trigger source is CTIMERB4 OUT2. value. */ + CTIMER_AUX1_TMRB1TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ + CTIMER_AUX1_TMRB1TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ + CTIMER_AUX1_TMRB1TRIG_B5OUT2DUAL = 14, /*!< B5OUT2DUAL : Trigger source is CTIMERB5 OUT2, dual edge. value. */ + CTIMER_AUX1_TMRB1TRIG_A5OUT2DUAL = 15, /*!< A5OUT2DUAL : Trigger source is CTIMERA5 OUT2, dual edge. value. */ +} CTIMER_AUX1_TMRB1TRIG_Enum; + +/* ============================================ CTIMER AUX1 TMRA1EN23 [14..14] ============================================= */ +typedef enum { /*!< CTIMER_AUX1_TMRA1EN23 */ + CTIMER_AUX1_TMRA1EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ + CTIMER_AUX1_TMRA1EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ +} CTIMER_AUX1_TMRA1EN23_Enum; + +/* ============================================ CTIMER AUX1 TMRA1POL23 [13..13] ============================================ */ +typedef enum { /*!< CTIMER_AUX1_TMRA1POL23 */ + CTIMER_AUX1_TMRA1POL23_NORMAL = 0, /*!< NORMAL : Upper output normal polarity value. */ + CTIMER_AUX1_TMRA1POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ +} CTIMER_AUX1_TMRA1POL23_Enum; + +/* ============================================ CTIMER AUX1 TMRA1TINV [12..12] ============================================= */ +typedef enum { /*!< CTIMER_AUX1_TMRA1TINV */ + CTIMER_AUX1_TMRA1TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ + CTIMER_AUX1_TMRA1TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ +} CTIMER_AUX1_TMRA1TINV_Enum; + +/* =========================================== CTIMER AUX1 TMRA1NOSYNC [11..11] ============================================ */ +typedef enum { /*!< CTIMER_AUX1_TMRA1NOSYNC */ + CTIMER_AUX1_TMRA1NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ + CTIMER_AUX1_TMRA1NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ +} CTIMER_AUX1_TMRA1NOSYNC_Enum; + +/* ============================================= CTIMER AUX1 TMRA1TRIG [7..10] ============================================= */ +typedef enum { /*!< CTIMER_AUX1_TMRA1TRIG */ + CTIMER_AUX1_TMRA1TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ + CTIMER_AUX1_TMRA1TRIG_B1OUT = 1, /*!< B1OUT : Trigger source is CTIMERB1 OUT. value. */ + CTIMER_AUX1_TMRA1TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ + CTIMER_AUX1_TMRA1TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ + CTIMER_AUX1_TMRA1TRIG_A0OUT = 4, /*!< A0OUT : Trigger source is CTIMERA0 OUT. value. */ + CTIMER_AUX1_TMRA1TRIG_B0OUT = 5, /*!< B0OUT : Trigger source is CTIMERB0 OUT. value. */ + CTIMER_AUX1_TMRA1TRIG_A5OUT = 6, /*!< A5OUT : Trigger source is CTIMERA5 OUT. value. */ + CTIMER_AUX1_TMRA1TRIG_B5OUT = 7, /*!< B5OUT : Trigger source is CTIMERB5 OUT. value. */ + CTIMER_AUX1_TMRA1TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ + CTIMER_AUX1_TMRA1TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ + CTIMER_AUX1_TMRA1TRIG_A4OUT2 = 10, /*!< A4OUT2 : Trigger source is CTIMERA4 OUT2. value. */ + CTIMER_AUX1_TMRA1TRIG_B4OUT2 = 11, /*!< B4OUT2 : Trigger source is CTIMERB4 OUT2. value. */ + CTIMER_AUX1_TMRA1TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ + CTIMER_AUX1_TMRA1TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ + CTIMER_AUX1_TMRA1TRIG_B5OUT2DUAL = 14, /*!< B5OUT2DUAL : Trigger source is CTIMERB5 OUT2, dual edge. value. */ + CTIMER_AUX1_TMRA1TRIG_A5OUT2DUAL = 15, /*!< A5OUT2DUAL : Trigger source is CTIMERA5 OUT2, dual edge. value. */ +} CTIMER_AUX1_TMRA1TRIG_Enum; + +/* ========================================================= TMR2 ========================================================== */ +/* ======================================================== CMPRA2 ========================================================= */ +/* ======================================================== CMPRB2 ========================================================= */ +/* ========================================================= CTRL2 ========================================================= */ +/* ============================================= CTIMER CTRL2 CTLINK2 [31..31] ============================================= */ +typedef enum { /*!< CTIMER_CTRL2_CTLINK2 */ + CTIMER_CTRL2_CTLINK2_TWO_16BIT_TIMERS = 0, /*!< TWO_16BIT_TIMERS : Use A2/B2 timers as two independent 16-bit + timers (default). value. */ + CTIMER_CTRL2_CTLINK2_32BIT_TIMER = 1, /*!< 32BIT_TIMER : Link A2/B2 timers into a single 32-bit timer. + value. */ +} CTIMER_CTRL2_CTLINK2_Enum; + +/* ============================================ CTIMER CTRL2 TMRB2POL [28..28] ============================================= */ +typedef enum { /*!< CTIMER_CTRL2_TMRB2POL */ + CTIMER_CTRL2_TMRB2POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINB2 pin is the same as the + timer output. value. */ + CTIMER_CTRL2_TMRB2POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINB2 pin is the inverse of + the timer output. value. */ +} CTIMER_CTRL2_TMRB2POL_Enum; + +/* ============================================ CTIMER CTRL2 TMRB2CLR [27..27] ============================================= */ +typedef enum { /*!< CTIMER_CTRL2_TMRB2CLR */ + CTIMER_CTRL2_TMRB2CLR_RUN = 0, /*!< RUN : Allow counter/timer B2 to run value. */ + CTIMER_CTRL2_TMRB2CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer B2 at 0x0000. value. */ +} CTIMER_CTRL2_TMRB2CLR_Enum; + +/* ============================================ CTIMER CTRL2 TMRB2IE1 [26..26] ============================================= */ +typedef enum { /*!< CTIMER_CTRL2_TMRB2IE1 */ + CTIMER_CTRL2_TMRB2IE1_DIS = 0, /*!< DIS : Disable counter/timer B2 from generating an interrupt + based on COMPR1. value. */ + CTIMER_CTRL2_TMRB2IE1_EN = 1, /*!< EN : Enable counter/timer B2 to generate an interrupt based + on COMPR1. value. */ +} CTIMER_CTRL2_TMRB2IE1_Enum; + +/* ============================================ CTIMER CTRL2 TMRB2IE0 [25..25] ============================================= */ +typedef enum { /*!< CTIMER_CTRL2_TMRB2IE0 */ + CTIMER_CTRL2_TMRB2IE0_DIS = 0, /*!< DIS : Disable counter/timer B2 from generating an interrupt + based on COMPR0. value. */ + CTIMER_CTRL2_TMRB2IE0_EN = 1, /*!< EN : Enable counter/timer B2 to generate an interrupt based + on COMPR0 value. */ +} CTIMER_CTRL2_TMRB2IE0_Enum; + +/* ============================================= CTIMER CTRL2 TMRB2FN [22..24] ============================================= */ +typedef enum { /*!< CTIMER_CTRL2_TMRB2FN */ + CTIMER_CTRL2_TMRB2FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count + to CMPR0B2, stop. value. */ + CTIMER_CTRL2_TMRB2FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide + pulses). Count to CMPR0B2, restart. value. */ + CTIMER_CTRL2_TMRB2FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0B2, assert, + count to CMPR1B2, deassert, stop. value. */ + CTIMER_CTRL2_TMRB2FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0B2, assert, count + to CMPR1B2, deassert, restart. value. */ + CTIMER_CTRL2_TMRB2FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ + CTIMER_CTRL2_TMRB2FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ + CTIMER_CTRL2_TMRB2FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. + value. */ + CTIMER_CTRL2_TMRB2FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ +} CTIMER_CTRL2_TMRB2FN_Enum; + +/* ============================================ CTIMER CTRL2 TMRB2CLK [17..21] ============================================= */ +typedef enum { /*!< CTIMER_CTRL2_TMRB2CLK */ + CTIMER_CTRL2_TMRB2CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINB. value. */ + CTIMER_CTRL2_TMRB2CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ + CTIMER_CTRL2_TMRB2CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ + CTIMER_CTRL2_TMRB2CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ + CTIMER_CTRL2_TMRB2CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ + CTIMER_CTRL2_TMRB2CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ + CTIMER_CTRL2_TMRB2CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ + CTIMER_CTRL2_TMRB2CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ + CTIMER_CTRL2_TMRB2CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ + CTIMER_CTRL2_TMRB2CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ + CTIMER_CTRL2_TMRB2CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ + CTIMER_CTRL2_TMRB2CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ + CTIMER_CTRL2_TMRB2CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ + CTIMER_CTRL2_TMRB2CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ + CTIMER_CTRL2_TMRB2CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. + value. */ + CTIMER_CTRL2_TMRB2CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ + CTIMER_CTRL2_TMRB2CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ + CTIMER_CTRL2_TMRB2CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ + CTIMER_CTRL2_TMRB2CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ + CTIMER_CTRL2_TMRB2CLK_CTMRA2 = 20, /*!< CTMRA2 : Clock source is CTIMERA2 OUT. value. */ + CTIMER_CTRL2_TMRB2CLK_CTMRB3 = 21, /*!< CTMRB3 : Clock source is CTIMERA3 OUT. value. */ + CTIMER_CTRL2_TMRB2CLK_CTMRA3 = 22, /*!< CTMRA3 : Clock source is CTIMERB3 OUT. value. */ + CTIMER_CTRL2_TMRB2CLK_CTMRA4 = 23, /*!< CTMRA4 : Clock source is CTIMERA4 OUT. value. */ + CTIMER_CTRL2_TMRB2CLK_CTMRB4 = 24, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ + CTIMER_CTRL2_TMRB2CLK_CTMRB0 = 25, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ + CTIMER_CTRL2_TMRB2CLK_CTMRB1 = 26, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ + CTIMER_CTRL2_TMRB2CLK_CTMRB5 = 27, /*!< CTMRB5 : Clock source is CTIMERB5 OUT. value. */ + CTIMER_CTRL2_TMRB2CLK_CTMRB6 = 28, /*!< CTMRB6 : Clock source is CTIMERB6 OUT. value. */ + CTIMER_CTRL2_TMRB2CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ + CTIMER_CTRL2_TMRB2CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ + CTIMER_CTRL2_TMRB2CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ +} CTIMER_CTRL2_TMRB2CLK_Enum; + +/* ============================================= CTIMER CTRL2 TMRB2EN [16..16] ============================================= */ +typedef enum { /*!< CTIMER_CTRL2_TMRB2EN */ + CTIMER_CTRL2_TMRB2EN_DIS = 0, /*!< DIS : Counter/Timer B2 Disable. value. */ + CTIMER_CTRL2_TMRB2EN_EN = 1, /*!< EN : Counter/Timer B2 Enable. value. */ +} CTIMER_CTRL2_TMRB2EN_Enum; + +/* ============================================ CTIMER CTRL2 TMRA2POL [12..12] ============================================= */ +typedef enum { /*!< CTIMER_CTRL2_TMRA2POL */ + CTIMER_CTRL2_TMRA2POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINA2 pin is the same as the + timer output. value. */ + CTIMER_CTRL2_TMRA2POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINA2 pin is the inverse of + the timer output. value. */ +} CTIMER_CTRL2_TMRA2POL_Enum; + +/* ============================================ CTIMER CTRL2 TMRA2CLR [11..11] ============================================= */ +typedef enum { /*!< CTIMER_CTRL2_TMRA2CLR */ + CTIMER_CTRL2_TMRA2CLR_RUN = 0, /*!< RUN : Allow counter/timer A2 to run value. */ + CTIMER_CTRL2_TMRA2CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer A2 at 0x0000. value. */ +} CTIMER_CTRL2_TMRA2CLR_Enum; + +/* ============================================ CTIMER CTRL2 TMRA2IE1 [10..10] ============================================= */ +typedef enum { /*!< CTIMER_CTRL2_TMRA2IE1 */ + CTIMER_CTRL2_TMRA2IE1_DIS = 0, /*!< DIS : Disable counter/timer A2 from generating an interrupt + based on COMPR1. value. */ + CTIMER_CTRL2_TMRA2IE1_EN = 1, /*!< EN : Enable counter/timer A2 to generate an interrupt based + on COMPR1. value. */ +} CTIMER_CTRL2_TMRA2IE1_Enum; + +/* ============================================= CTIMER CTRL2 TMRA2IE0 [9..9] ============================================== */ +typedef enum { /*!< CTIMER_CTRL2_TMRA2IE0 */ + CTIMER_CTRL2_TMRA2IE0_DIS = 0, /*!< DIS : Disable counter/timer A2 from generating an interrupt + based on COMPR0. value. */ + CTIMER_CTRL2_TMRA2IE0_EN = 1, /*!< EN : Enable counter/timer A2 to generate an interrupt based + on COMPR0. value. */ +} CTIMER_CTRL2_TMRA2IE0_Enum; + +/* ============================================== CTIMER CTRL2 TMRA2FN [6..8] ============================================== */ +typedef enum { /*!< CTIMER_CTRL2_TMRA2FN */ + CTIMER_CTRL2_TMRA2FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count + to CMPR0A2, stop. value. */ + CTIMER_CTRL2_TMRA2FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide + pulses). Count to CMPR0A2, restart. value. */ + CTIMER_CTRL2_TMRA2FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0A2, assert, + count to CMPR1A2, deassert, stop. value. */ + CTIMER_CTRL2_TMRA2FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0A2, assert, count + to CMPR1A2, deassert, restart. value. */ + CTIMER_CTRL2_TMRA2FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ + CTIMER_CTRL2_TMRA2FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ + CTIMER_CTRL2_TMRA2FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. + value. */ + CTIMER_CTRL2_TMRA2FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ +} CTIMER_CTRL2_TMRA2FN_Enum; + +/* ============================================= CTIMER CTRL2 TMRA2CLK [1..5] ============================================== */ +typedef enum { /*!< CTIMER_CTRL2_TMRA2CLK */ + CTIMER_CTRL2_TMRA2CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINA. value. */ + CTIMER_CTRL2_TMRA2CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ + CTIMER_CTRL2_TMRA2CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ + CTIMER_CTRL2_TMRA2CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ + CTIMER_CTRL2_TMRA2CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ + CTIMER_CTRL2_TMRA2CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ + CTIMER_CTRL2_TMRA2CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ + CTIMER_CTRL2_TMRA2CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ + CTIMER_CTRL2_TMRA2CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ + CTIMER_CTRL2_TMRA2CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ + CTIMER_CTRL2_TMRA2CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ + CTIMER_CTRL2_TMRA2CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ + CTIMER_CTRL2_TMRA2CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ + CTIMER_CTRL2_TMRA2CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ + CTIMER_CTRL2_TMRA2CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. + value. */ + CTIMER_CTRL2_TMRA2CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ + CTIMER_CTRL2_TMRA2CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ + CTIMER_CTRL2_TMRA2CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ + CTIMER_CTRL2_TMRA2CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ + CTIMER_CTRL2_TMRA2CLK_CTMRB2 = 20, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ + CTIMER_CTRL2_TMRA2CLK_CTMRB3 = 21, /*!< CTMRB3 : Clock source is CTIMERA3 OUT. value. */ + CTIMER_CTRL2_TMRA2CLK_CTMRA3 = 22, /*!< CTMRA3 : Clock source is CTIMERB3 OUT. value. */ + CTIMER_CTRL2_TMRA2CLK_CTMRA4 = 23, /*!< CTMRA4 : Clock source is CTIMERA4 OUT. value. */ + CTIMER_CTRL2_TMRA2CLK_CTMRB4 = 24, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ + CTIMER_CTRL2_TMRA2CLK_CTMRB0 = 25, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ + CTIMER_CTRL2_TMRA2CLK_CTMRB1 = 26, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ + CTIMER_CTRL2_TMRA2CLK_CTMRB5 = 27, /*!< CTMRB5 : Clock source is CTIMERB5 OUT. value. */ + CTIMER_CTRL2_TMRA2CLK_CTMRB6 = 28, /*!< CTMRB6 : Clock source is CTIMERB6 OUT. value. */ + CTIMER_CTRL2_TMRA2CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ + CTIMER_CTRL2_TMRA2CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ + CTIMER_CTRL2_TMRA2CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ +} CTIMER_CTRL2_TMRA2CLK_Enum; + +/* ============================================== CTIMER CTRL2 TMRA2EN [0..0] ============================================== */ +typedef enum { /*!< CTIMER_CTRL2_TMRA2EN */ + CTIMER_CTRL2_TMRA2EN_DIS = 0, /*!< DIS : Counter/Timer A2 Disable. value. */ + CTIMER_CTRL2_TMRA2EN_EN = 1, /*!< EN : Counter/Timer A2 Enable. value. */ +} CTIMER_CTRL2_TMRA2EN_Enum; + +/* ======================================================= CMPRAUXA2 ======================================================= */ +/* ======================================================= CMPRAUXB2 ======================================================= */ +/* ========================================================= AUX2 ========================================================== */ +/* ============================================ CTIMER AUX2 TMRB2EN23 [30..30] ============================================= */ +typedef enum { /*!< CTIMER_AUX2_TMRB2EN23 */ + CTIMER_AUX2_TMRB2EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ + CTIMER_AUX2_TMRB2EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ +} CTIMER_AUX2_TMRB2EN23_Enum; + +/* ============================================ CTIMER AUX2 TMRB2POL23 [29..29] ============================================ */ +typedef enum { /*!< CTIMER_AUX2_TMRB2POL23 */ + CTIMER_AUX2_TMRB2POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ + CTIMER_AUX2_TMRB2POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ +} CTIMER_AUX2_TMRB2POL23_Enum; + +/* ============================================ CTIMER AUX2 TMRB2TINV [28..28] ============================================= */ +typedef enum { /*!< CTIMER_AUX2_TMRB2TINV */ + CTIMER_AUX2_TMRB2TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ + CTIMER_AUX2_TMRB2TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ +} CTIMER_AUX2_TMRB2TINV_Enum; + +/* =========================================== CTIMER AUX2 TMRB2NOSYNC [27..27] ============================================ */ +typedef enum { /*!< CTIMER_AUX2_TMRB2NOSYNC */ + CTIMER_AUX2_TMRB2NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ + CTIMER_AUX2_TMRB2NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ +} CTIMER_AUX2_TMRB2NOSYNC_Enum; + +/* ============================================ CTIMER AUX2 TMRB2TRIG [23..26] ============================================= */ +typedef enum { /*!< CTIMER_AUX2_TMRB2TRIG */ + CTIMER_AUX2_TMRB2TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ + CTIMER_AUX2_TMRB2TRIG_A2OUT = 1, /*!< A2OUT : Trigger source is CTIMERA2 OUT. value. */ + CTIMER_AUX2_TMRB2TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ + CTIMER_AUX2_TMRB2TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ + CTIMER_AUX2_TMRB2TRIG_A1OUT = 4, /*!< A1OUT : Trigger source is CTIMERA1 OUT. value. */ + CTIMER_AUX2_TMRB2TRIG_B1OUT = 5, /*!< B1OUT : Trigger source is CTIMERB1 OUT. value. */ + CTIMER_AUX2_TMRB2TRIG_A4OUT = 6, /*!< A4OUT : Trigger source is CTIMERA4 OUT. value. */ + CTIMER_AUX2_TMRB2TRIG_B4OUT = 7, /*!< B4OUT : Trigger source is CTIMERB4 OUT. value. */ + CTIMER_AUX2_TMRB2TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ + CTIMER_AUX2_TMRB2TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ + CTIMER_AUX2_TMRB2TRIG_A5OUT2 = 10, /*!< A5OUT2 : Trigger source is CTIMERA5 OUT2. value. */ + CTIMER_AUX2_TMRB2TRIG_B5OUT2 = 11, /*!< B5OUT2 : Trigger source is CTIMERB5 OUT2. value. */ + CTIMER_AUX2_TMRB2TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ + CTIMER_AUX2_TMRB2TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ + CTIMER_AUX2_TMRB2TRIG_B4OUT2DUAL = 14, /*!< B4OUT2DUAL : Trigger source is CTIMERB4 OUT2, dual edge. value. */ + CTIMER_AUX2_TMRB2TRIG_A4OUT2DUAL = 15, /*!< A4OUT2DUAL : Trigger source is CTIMERA4 OUT2, dual edge. value. */ +} CTIMER_AUX2_TMRB2TRIG_Enum; + +/* ============================================ CTIMER AUX2 TMRA2EN23 [14..14] ============================================= */ +typedef enum { /*!< CTIMER_AUX2_TMRA2EN23 */ + CTIMER_AUX2_TMRA2EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ + CTIMER_AUX2_TMRA2EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ +} CTIMER_AUX2_TMRA2EN23_Enum; + +/* ============================================ CTIMER AUX2 TMRA2POL23 [13..13] ============================================ */ +typedef enum { /*!< CTIMER_AUX2_TMRA2POL23 */ + CTIMER_AUX2_TMRA2POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ + CTIMER_AUX2_TMRA2POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ +} CTIMER_AUX2_TMRA2POL23_Enum; + +/* ============================================ CTIMER AUX2 TMRA2TINV [12..12] ============================================= */ +typedef enum { /*!< CTIMER_AUX2_TMRA2TINV */ + CTIMER_AUX2_TMRA2TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ + CTIMER_AUX2_TMRA2TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ +} CTIMER_AUX2_TMRA2TINV_Enum; + +/* =========================================== CTIMER AUX2 TMRA2NOSYNC [11..11] ============================================ */ +typedef enum { /*!< CTIMER_AUX2_TMRA2NOSYNC */ + CTIMER_AUX2_TMRA2NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ + CTIMER_AUX2_TMRA2NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ +} CTIMER_AUX2_TMRA2NOSYNC_Enum; + +/* ============================================= CTIMER AUX2 TMRA2TRIG [7..10] ============================================= */ +typedef enum { /*!< CTIMER_AUX2_TMRA2TRIG */ + CTIMER_AUX2_TMRA2TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ + CTIMER_AUX2_TMRA2TRIG_B2OUT = 1, /*!< B2OUT : Trigger source is CTIMERB2 OUT. value. */ + CTIMER_AUX2_TMRA2TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ + CTIMER_AUX2_TMRA2TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ + CTIMER_AUX2_TMRA2TRIG_A0OUT = 4, /*!< A0OUT : Trigger source is CTIMERA0 OUT. value. */ + CTIMER_AUX2_TMRA2TRIG_B0OUT = 5, /*!< B0OUT : Trigger source is CTIMERB0 OUT. value. */ + CTIMER_AUX2_TMRA2TRIG_A4OUT = 6, /*!< A4OUT : Trigger source is CTIMERA4 OUT. value. */ + CTIMER_AUX2_TMRA2TRIG_B4OUT = 7, /*!< B4OUT : Trigger source is CTIMERB4 OUT. value. */ + CTIMER_AUX2_TMRA2TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ + CTIMER_AUX2_TMRA2TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ + CTIMER_AUX2_TMRA2TRIG_A5OUT2 = 10, /*!< A5OUT2 : Trigger source is CTIMERA5 OUT2. value. */ + CTIMER_AUX2_TMRA2TRIG_B5OUT2 = 11, /*!< B5OUT2 : Trigger source is CTIMERB5 OUT2. value. */ + CTIMER_AUX2_TMRA2TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ + CTIMER_AUX2_TMRA2TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ + CTIMER_AUX2_TMRA2TRIG_B4OUT2DUAL = 14, /*!< B4OUT2DUAL : Trigger source is CTIMERB4 OUT2, dual edge. value. */ + CTIMER_AUX2_TMRA2TRIG_A4OUT2DUAL = 15, /*!< A4OUT2DUAL : Trigger source is CTIMERA4 OUT2, dual edge. value. */ +} CTIMER_AUX2_TMRA2TRIG_Enum; + +/* ========================================================= TMR3 ========================================================== */ +/* ======================================================== CMPRA3 ========================================================= */ +/* ======================================================== CMPRB3 ========================================================= */ +/* ========================================================= CTRL3 ========================================================= */ +/* ============================================= CTIMER CTRL3 CTLINK3 [31..31] ============================================= */ +typedef enum { /*!< CTIMER_CTRL3_CTLINK3 */ + CTIMER_CTRL3_CTLINK3_TWO_16BIT_TIMERS = 0, /*!< TWO_16BIT_TIMERS : Use A3/B3 timers as two independent 16-bit + timers (default). value. */ + CTIMER_CTRL3_CTLINK3_32BIT_TIMER = 1, /*!< 32BIT_TIMER : Link A3/B3 timers into a single 32-bit timer. + value. */ +} CTIMER_CTRL3_CTLINK3_Enum; + +/* ============================================ CTIMER CTRL3 TMRB3POL [28..28] ============================================= */ +typedef enum { /*!< CTIMER_CTRL3_TMRB3POL */ + CTIMER_CTRL3_TMRB3POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINB3 pin is the same as the + timer output. value. */ + CTIMER_CTRL3_TMRB3POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINB3 pin is the inverse of + the timer output. value. */ +} CTIMER_CTRL3_TMRB3POL_Enum; + +/* ============================================ CTIMER CTRL3 TMRB3CLR [27..27] ============================================= */ +typedef enum { /*!< CTIMER_CTRL3_TMRB3CLR */ + CTIMER_CTRL3_TMRB3CLR_RUN = 0, /*!< RUN : Allow counter/timer B3 to run value. */ + CTIMER_CTRL3_TMRB3CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer B3 at 0x0000. value. */ +} CTIMER_CTRL3_TMRB3CLR_Enum; + +/* ============================================ CTIMER CTRL3 TMRB3IE1 [26..26] ============================================= */ +typedef enum { /*!< CTIMER_CTRL3_TMRB3IE1 */ + CTIMER_CTRL3_TMRB3IE1_DIS = 0, /*!< DIS : Disable counter/timer B3 from generating an interrupt + based on COMPR1. value. */ + CTIMER_CTRL3_TMRB3IE1_EN = 1, /*!< EN : Enable counter/timer B3 to generate an interrupt based + on COMPR1. value. */ +} CTIMER_CTRL3_TMRB3IE1_Enum; + +/* ============================================ CTIMER CTRL3 TMRB3IE0 [25..25] ============================================= */ +typedef enum { /*!< CTIMER_CTRL3_TMRB3IE0 */ + CTIMER_CTRL3_TMRB3IE0_DIS = 0, /*!< DIS : Disable counter/timer B3 from generating an interrupt + based on COMPR0. value. */ + CTIMER_CTRL3_TMRB3IE0_EN = 1, /*!< EN : Enable counter/timer B3 to generate an interrupt based + on COMPR0 value. */ +} CTIMER_CTRL3_TMRB3IE0_Enum; + +/* ============================================= CTIMER CTRL3 TMRB3FN [22..24] ============================================= */ +typedef enum { /*!< CTIMER_CTRL3_TMRB3FN */ + CTIMER_CTRL3_TMRB3FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count + to CMPR0B3, stop. value. */ + CTIMER_CTRL3_TMRB3FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide + pulses). Count to CMPR0B3, restart. value. */ + CTIMER_CTRL3_TMRB3FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0B3, assert, + count to CMPR1B3, deassert, stop. value. */ + CTIMER_CTRL3_TMRB3FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0B3, assert, count + to CMPR1B3, deassert, restart. value. */ + CTIMER_CTRL3_TMRB3FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ + CTIMER_CTRL3_TMRB3FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ + CTIMER_CTRL3_TMRB3FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. + value. */ + CTIMER_CTRL3_TMRB3FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ +} CTIMER_CTRL3_TMRB3FN_Enum; + +/* ============================================ CTIMER CTRL3 TMRB3CLK [17..21] ============================================= */ +typedef enum { /*!< CTIMER_CTRL3_TMRB3CLK */ + CTIMER_CTRL3_TMRB3CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINB. value. */ + CTIMER_CTRL3_TMRB3CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ + CTIMER_CTRL3_TMRB3CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ + CTIMER_CTRL3_TMRB3CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ + CTIMER_CTRL3_TMRB3CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ + CTIMER_CTRL3_TMRB3CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ + CTIMER_CTRL3_TMRB3CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ + CTIMER_CTRL3_TMRB3CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ + CTIMER_CTRL3_TMRB3CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ + CTIMER_CTRL3_TMRB3CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ + CTIMER_CTRL3_TMRB3CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ + CTIMER_CTRL3_TMRB3CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ + CTIMER_CTRL3_TMRB3CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ + CTIMER_CTRL3_TMRB3CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ + CTIMER_CTRL3_TMRB3CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. + value. */ + CTIMER_CTRL3_TMRB3CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ + CTIMER_CTRL3_TMRB3CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ + CTIMER_CTRL3_TMRB3CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ + CTIMER_CTRL3_TMRB3CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ + CTIMER_CTRL3_TMRB3CLK_CTMRA3 = 20, /*!< CTMRA3 : Clock source is CTIMERA3 OUT. value. */ + CTIMER_CTRL3_TMRB3CLK_CTMRA2 = 21, /*!< CTMRA2 : Clock source is CTIMERA2 OUT. value. */ + CTIMER_CTRL3_TMRB3CLK_CTMRB2 = 22, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ + CTIMER_CTRL3_TMRB3CLK_CTMRA4 = 23, /*!< CTMRA4 : Clock source is CTIMERA4 OUT. value. */ + CTIMER_CTRL3_TMRB3CLK_CTMRB4 = 24, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ + CTIMER_CTRL3_TMRB3CLK_CTMRB0 = 25, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ + CTIMER_CTRL3_TMRB3CLK_CTMRB1 = 26, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ + CTIMER_CTRL3_TMRB3CLK_CTMRB5 = 27, /*!< CTMRB5 : Clock source is CTIMERB5 OUT. value. */ + CTIMER_CTRL3_TMRB3CLK_CTMRB6 = 28, /*!< CTMRB6 : Clock source is CTIMERB6 OUT. value. */ + CTIMER_CTRL3_TMRB3CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ + CTIMER_CTRL3_TMRB3CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ + CTIMER_CTRL3_TMRB3CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ +} CTIMER_CTRL3_TMRB3CLK_Enum; + +/* ============================================= CTIMER CTRL3 TMRB3EN [16..16] ============================================= */ +typedef enum { /*!< CTIMER_CTRL3_TMRB3EN */ + CTIMER_CTRL3_TMRB3EN_DIS = 0, /*!< DIS : Counter/Timer B3 Disable. value. */ + CTIMER_CTRL3_TMRB3EN_EN = 1, /*!< EN : Counter/Timer B3 Enable. value. */ +} CTIMER_CTRL3_TMRB3EN_Enum; + +/* ============================================ CTIMER CTRL3 TMRA3POL [12..12] ============================================= */ +typedef enum { /*!< CTIMER_CTRL3_TMRA3POL */ + CTIMER_CTRL3_TMRA3POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINA3 pin is the same as the + timer output. value. */ + CTIMER_CTRL3_TMRA3POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINA3 pin is the inverse of + the timer output. value. */ +} CTIMER_CTRL3_TMRA3POL_Enum; + +/* ============================================ CTIMER CTRL3 TMRA3CLR [11..11] ============================================= */ +typedef enum { /*!< CTIMER_CTRL3_TMRA3CLR */ + CTIMER_CTRL3_TMRA3CLR_RUN = 0, /*!< RUN : Allow counter/timer A3 to run value. */ + CTIMER_CTRL3_TMRA3CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer A3 at 0x0000. value. */ +} CTIMER_CTRL3_TMRA3CLR_Enum; + +/* ============================================ CTIMER CTRL3 TMRA3IE1 [10..10] ============================================= */ +typedef enum { /*!< CTIMER_CTRL3_TMRA3IE1 */ + CTIMER_CTRL3_TMRA3IE1_DIS = 0, /*!< DIS : Disable counter/timer A3 from generating an interrupt + based on COMPR1. value. */ + CTIMER_CTRL3_TMRA3IE1_EN = 1, /*!< EN : Enable counter/timer A3 to generate an interrupt based + on COMPR1. value. */ +} CTIMER_CTRL3_TMRA3IE1_Enum; + +/* ============================================= CTIMER CTRL3 TMRA3IE0 [9..9] ============================================== */ +typedef enum { /*!< CTIMER_CTRL3_TMRA3IE0 */ + CTIMER_CTRL3_TMRA3IE0_DIS = 0, /*!< DIS : Disable counter/timer A3 from generating an interrupt + based on COMPR0. value. */ + CTIMER_CTRL3_TMRA3IE0_EN = 1, /*!< EN : Enable counter/timer A3 to generate an interrupt based + on COMPR0. value. */ +} CTIMER_CTRL3_TMRA3IE0_Enum; + +/* ============================================== CTIMER CTRL3 TMRA3FN [6..8] ============================================== */ +typedef enum { /*!< CTIMER_CTRL3_TMRA3FN */ + CTIMER_CTRL3_TMRA3FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count + to CMPR0A3, stop. value. */ + CTIMER_CTRL3_TMRA3FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide + pulses). Count to CMPR0A3, restart. value. */ + CTIMER_CTRL3_TMRA3FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0A3, assert, + count to CMPR1A3, deassert, stop. value. */ + CTIMER_CTRL3_TMRA3FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0A3, assert, count + to CMPR1A3, deassert, restart. value. */ + CTIMER_CTRL3_TMRA3FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ + CTIMER_CTRL3_TMRA3FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ + CTIMER_CTRL3_TMRA3FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. + value. */ + CTIMER_CTRL3_TMRA3FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ +} CTIMER_CTRL3_TMRA3FN_Enum; + +/* ============================================= CTIMER CTRL3 TMRA3CLK [1..5] ============================================== */ +typedef enum { /*!< CTIMER_CTRL3_TMRA3CLK */ + CTIMER_CTRL3_TMRA3CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINA. value. */ + CTIMER_CTRL3_TMRA3CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ + CTIMER_CTRL3_TMRA3CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ + CTIMER_CTRL3_TMRA3CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ + CTIMER_CTRL3_TMRA3CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ + CTIMER_CTRL3_TMRA3CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ + CTIMER_CTRL3_TMRA3CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ + CTIMER_CTRL3_TMRA3CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ + CTIMER_CTRL3_TMRA3CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ + CTIMER_CTRL3_TMRA3CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ + CTIMER_CTRL3_TMRA3CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ + CTIMER_CTRL3_TMRA3CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ + CTIMER_CTRL3_TMRA3CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ + CTIMER_CTRL3_TMRA3CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ + CTIMER_CTRL3_TMRA3CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. + value. */ + CTIMER_CTRL3_TMRA3CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ + CTIMER_CTRL3_TMRA3CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ + CTIMER_CTRL3_TMRA3CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ + CTIMER_CTRL3_TMRA3CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ + CTIMER_CTRL3_TMRA3CLK_CTMRB3 = 20, /*!< CTMRB3 : Clock source is CTIMERB3 OUT. value. */ + CTIMER_CTRL3_TMRA3CLK_CTMRA2 = 21, /*!< CTMRA2 : Clock source is CTIMERA2 OUT. value. */ + CTIMER_CTRL3_TMRA3CLK_CTMRB2 = 22, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ + CTIMER_CTRL3_TMRA3CLK_CTMRA4 = 23, /*!< CTMRA4 : Clock source is CTIMERA4 OUT. value. */ + CTIMER_CTRL3_TMRA3CLK_CTMRB4 = 24, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ + CTIMER_CTRL3_TMRA3CLK_CTMRB0 = 25, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ + CTIMER_CTRL3_TMRA3CLK_CTMRB1 = 26, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ + CTIMER_CTRL3_TMRA3CLK_CTMRB5 = 27, /*!< CTMRB5 : Clock source is CTIMERB5 OUT. value. */ + CTIMER_CTRL3_TMRA3CLK_CTMRB6 = 28, /*!< CTMRB6 : Clock source is CTIMERB6 OUT. value. */ + CTIMER_CTRL3_TMRA3CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ + CTIMER_CTRL3_TMRA3CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ + CTIMER_CTRL3_TMRA3CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ +} CTIMER_CTRL3_TMRA3CLK_Enum; + +/* ============================================== CTIMER CTRL3 TMRA3EN [0..0] ============================================== */ +typedef enum { /*!< CTIMER_CTRL3_TMRA3EN */ + CTIMER_CTRL3_TMRA3EN_DIS = 0, /*!< DIS : Counter/Timer A3 Disable. value. */ + CTIMER_CTRL3_TMRA3EN_EN = 1, /*!< EN : Counter/Timer A3 Enable. value. */ +} CTIMER_CTRL3_TMRA3EN_Enum; + +/* ======================================================= CMPRAUXA3 ======================================================= */ +/* ======================================================= CMPRAUXB3 ======================================================= */ +/* ========================================================= AUX3 ========================================================== */ +/* ============================================ CTIMER AUX3 TMRB3EN23 [30..30] ============================================= */ +typedef enum { /*!< CTIMER_AUX3_TMRB3EN23 */ + CTIMER_AUX3_TMRB3EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ + CTIMER_AUX3_TMRB3EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ +} CTIMER_AUX3_TMRB3EN23_Enum; + +/* ============================================ CTIMER AUX3 TMRB3POL23 [29..29] ============================================ */ +typedef enum { /*!< CTIMER_AUX3_TMRB3POL23 */ + CTIMER_AUX3_TMRB3POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ + CTIMER_AUX3_TMRB3POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ +} CTIMER_AUX3_TMRB3POL23_Enum; + +/* ============================================ CTIMER AUX3 TMRB3TINV [28..28] ============================================= */ +typedef enum { /*!< CTIMER_AUX3_TMRB3TINV */ + CTIMER_AUX3_TMRB3TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ + CTIMER_AUX3_TMRB3TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ +} CTIMER_AUX3_TMRB3TINV_Enum; + +/* =========================================== CTIMER AUX3 TMRB3NOSYNC [27..27] ============================================ */ +typedef enum { /*!< CTIMER_AUX3_TMRB3NOSYNC */ + CTIMER_AUX3_TMRB3NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ + CTIMER_AUX3_TMRB3NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ +} CTIMER_AUX3_TMRB3NOSYNC_Enum; + +/* ============================================ CTIMER AUX3 TMRB3TRIG [23..26] ============================================= */ +typedef enum { /*!< CTIMER_AUX3_TMRB3TRIG */ + CTIMER_AUX3_TMRB3TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ + CTIMER_AUX3_TMRB3TRIG_A3OUT = 1, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ + CTIMER_AUX3_TMRB3TRIG_B2OUT = 2, /*!< B2OUT : Trigger source is CTIMERB2 OUT. value. */ + CTIMER_AUX3_TMRB3TRIG_A2OUT = 3, /*!< A2OUT : Trigger source is CTIMERA2 OUT. value. */ + CTIMER_AUX3_TMRB3TRIG_A4OUT = 4, /*!< A4OUT : Trigger source is CTIMERA4 OUT. value. */ + CTIMER_AUX3_TMRB3TRIG_B4OUT = 5, /*!< B4OUT : Trigger source is CTIMERB4 OUT. value. */ + CTIMER_AUX3_TMRB3TRIG_A6OUT = 6, /*!< A6OUT : Trigger source is CTIMERA6 OUT. value. */ + CTIMER_AUX3_TMRB3TRIG_B6OUT = 7, /*!< B6OUT : Trigger source is CTIMERB6 OUT. value. */ + CTIMER_AUX3_TMRB3TRIG_B5OUT2 = 8, /*!< B5OUT2 : Trigger source is CTIMERB5 OUT2. value. */ + CTIMER_AUX3_TMRB3TRIG_A5OUT2 = 9, /*!< A5OUT2 : Trigger source is CTIMERA5 OUT2. value. */ + CTIMER_AUX3_TMRB3TRIG_A1OUT2 = 10, /*!< A1OUT2 : Trigger source is CTIMERA1 OUT2. value. */ + CTIMER_AUX3_TMRB3TRIG_B1OUT2 = 11, /*!< B1OUT2 : Trigger source is CTIMERB1 OUT2. value. */ + CTIMER_AUX3_TMRB3TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ + CTIMER_AUX3_TMRB3TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ + CTIMER_AUX3_TMRB3TRIG_B2OUT2DUAL = 14, /*!< B2OUT2DUAL : Trigger source is CTIMERB2 OUT2, dual edge. value. */ + CTIMER_AUX3_TMRB3TRIG_A2OUT2DUAL = 15, /*!< A2OUT2DUAL : Trigger source is CTIMERA2 OUT2, dual edge. value. */ +} CTIMER_AUX3_TMRB3TRIG_Enum; + +/* ============================================ CTIMER AUX3 TMRA3EN23 [14..14] ============================================= */ +typedef enum { /*!< CTIMER_AUX3_TMRA3EN23 */ + CTIMER_AUX3_TMRA3EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ + CTIMER_AUX3_TMRA3EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ +} CTIMER_AUX3_TMRA3EN23_Enum; + +/* ============================================ CTIMER AUX3 TMRA3POL23 [13..13] ============================================ */ +typedef enum { /*!< CTIMER_AUX3_TMRA3POL23 */ + CTIMER_AUX3_TMRA3POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ + CTIMER_AUX3_TMRA3POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ +} CTIMER_AUX3_TMRA3POL23_Enum; + +/* ============================================ CTIMER AUX3 TMRA3TINV [12..12] ============================================= */ +typedef enum { /*!< CTIMER_AUX3_TMRA3TINV */ + CTIMER_AUX3_TMRA3TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ + CTIMER_AUX3_TMRA3TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ +} CTIMER_AUX3_TMRA3TINV_Enum; + +/* =========================================== CTIMER AUX3 TMRA3NOSYNC [11..11] ============================================ */ +typedef enum { /*!< CTIMER_AUX3_TMRA3NOSYNC */ + CTIMER_AUX3_TMRA3NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ + CTIMER_AUX3_TMRA3NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ +} CTIMER_AUX3_TMRA3NOSYNC_Enum; + +/* ============================================= CTIMER AUX3 TMRA3TRIG [7..10] ============================================= */ +typedef enum { /*!< CTIMER_AUX3_TMRA3TRIG */ + CTIMER_AUX3_TMRA3TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ + CTIMER_AUX3_TMRA3TRIG_B3OUT = 1, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ + CTIMER_AUX3_TMRA3TRIG_B2OUT = 2, /*!< B2OUT : Trigger source is CTIMERB2 OUT. value. */ + CTIMER_AUX3_TMRA3TRIG_A2OUT = 3, /*!< A2OUT : Trigger source is CTIMERA2 OUT. value. */ + CTIMER_AUX3_TMRA3TRIG_A4OUT = 4, /*!< A4OUT : Trigger source is CTIMERA4 OUT. value. */ + CTIMER_AUX3_TMRA3TRIG_B4OUT = 5, /*!< B4OUT : Trigger source is CTIMERB4 OUT. value. */ + CTIMER_AUX3_TMRA3TRIG_A7OUT = 6, /*!< A7OUT : Trigger source is CTIMERA7 OUT. value. */ + CTIMER_AUX3_TMRA3TRIG_B7OUT = 7, /*!< B7OUT : Trigger source is CTIMERB7 OUT. value. */ + CTIMER_AUX3_TMRA3TRIG_B5OUT2 = 8, /*!< B5OUT2 : Trigger source is CTIMERB5 OUT2. value. */ + CTIMER_AUX3_TMRA3TRIG_A5OUT2 = 9, /*!< A5OUT2 : Trigger source is CTIMERA5 OUT2. value. */ + CTIMER_AUX3_TMRA3TRIG_A1OUT2 = 10, /*!< A1OUT2 : Trigger source is CTIMERA1 OUT2. value. */ + CTIMER_AUX3_TMRA3TRIG_B1OUT2 = 11, /*!< B1OUT2 : Trigger source is CTIMERB1 OUT2. value. */ + CTIMER_AUX3_TMRA3TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ + CTIMER_AUX3_TMRA3TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ + CTIMER_AUX3_TMRA3TRIG_B2OUT2DUAL = 14, /*!< B2OUT2DUAL : Trigger source is CTIMERB2 OUT2, dual edge. value. */ + CTIMER_AUX3_TMRA3TRIG_A2OUT2DUAL = 15, /*!< A2OUT2DUAL : Trigger source is CTIMERA2 OUT2, dual edge. value. */ +} CTIMER_AUX3_TMRA3TRIG_Enum; + +/* ========================================================= TMR4 ========================================================== */ +/* ======================================================== CMPRA4 ========================================================= */ +/* ======================================================== CMPRB4 ========================================================= */ +/* ========================================================= CTRL4 ========================================================= */ +/* ============================================= CTIMER CTRL4 CTLINK4 [31..31] ============================================= */ +typedef enum { /*!< CTIMER_CTRL4_CTLINK4 */ + CTIMER_CTRL4_CTLINK4_TWO_16BIT_TIMERS = 0, /*!< TWO_16BIT_TIMERS : Use A4/B4 timers as two independent 16-bit + timers (default). value. */ + CTIMER_CTRL4_CTLINK4_32BIT_TIMER = 1, /*!< 32BIT_TIMER : Link A4/B4 timers into a single 32-bit timer. + value. */ +} CTIMER_CTRL4_CTLINK4_Enum; + +/* ============================================ CTIMER CTRL4 TMRB4POL [28..28] ============================================= */ +typedef enum { /*!< CTIMER_CTRL4_TMRB4POL */ + CTIMER_CTRL4_TMRB4POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINB4 pin is the same as the + timer output. value. */ + CTIMER_CTRL4_TMRB4POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINB4 pin is the inverse of + the timer output. value. */ +} CTIMER_CTRL4_TMRB4POL_Enum; + +/* ============================================ CTIMER CTRL4 TMRB4CLR [27..27] ============================================= */ +typedef enum { /*!< CTIMER_CTRL4_TMRB4CLR */ + CTIMER_CTRL4_TMRB4CLR_RUN = 0, /*!< RUN : Allow counter/timer B4 to run value. */ + CTIMER_CTRL4_TMRB4CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer B4 at 0x0000. value. */ +} CTIMER_CTRL4_TMRB4CLR_Enum; + +/* ============================================ CTIMER CTRL4 TMRB4IE1 [26..26] ============================================= */ +typedef enum { /*!< CTIMER_CTRL4_TMRB4IE1 */ + CTIMER_CTRL4_TMRB4IE1_DIS = 0, /*!< DIS : Disable counter/timer B4 from generating an interrupt + based on COMPR1. value. */ + CTIMER_CTRL4_TMRB4IE1_EN = 1, /*!< EN : Enable counter/timer B4 to generate an interrupt based + on COMPR1. value. */ +} CTIMER_CTRL4_TMRB4IE1_Enum; + +/* ============================================ CTIMER CTRL4 TMRB4IE0 [25..25] ============================================= */ +typedef enum { /*!< CTIMER_CTRL4_TMRB4IE0 */ + CTIMER_CTRL4_TMRB4IE0_DIS = 0, /*!< DIS : Disable counter/timer B4 from generating an interrupt + based on COMPR0. value. */ + CTIMER_CTRL4_TMRB4IE0_EN = 1, /*!< EN : Enable counter/timer B4 to generate an interrupt based + on COMPR0 value. */ +} CTIMER_CTRL4_TMRB4IE0_Enum; + +/* ============================================= CTIMER CTRL4 TMRB4FN [22..24] ============================================= */ +typedef enum { /*!< CTIMER_CTRL4_TMRB4FN */ + CTIMER_CTRL4_TMRB4FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count + to CMPR0B4, stop. value. */ + CTIMER_CTRL4_TMRB4FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide + pulses). Count to CMPR0B4, restart. value. */ + CTIMER_CTRL4_TMRB4FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0B4, assert, + count to CMPR1B4, deassert, stop. value. */ + CTIMER_CTRL4_TMRB4FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0B4, assert, count + to CMPR1B4, deassert, restart. value. */ + CTIMER_CTRL4_TMRB4FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ + CTIMER_CTRL4_TMRB4FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ + CTIMER_CTRL4_TMRB4FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. + value. */ + CTIMER_CTRL4_TMRB4FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ +} CTIMER_CTRL4_TMRB4FN_Enum; + +/* ============================================ CTIMER CTRL4 TMRB4CLK [17..21] ============================================= */ +typedef enum { /*!< CTIMER_CTRL4_TMRB4CLK */ + CTIMER_CTRL4_TMRB4CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINB. value. */ + CTIMER_CTRL4_TMRB4CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ + CTIMER_CTRL4_TMRB4CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ + CTIMER_CTRL4_TMRB4CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ + CTIMER_CTRL4_TMRB4CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ + CTIMER_CTRL4_TMRB4CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ + CTIMER_CTRL4_TMRB4CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ + CTIMER_CTRL4_TMRB4CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ + CTIMER_CTRL4_TMRB4CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ + CTIMER_CTRL4_TMRB4CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ + CTIMER_CTRL4_TMRB4CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ + CTIMER_CTRL4_TMRB4CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ + CTIMER_CTRL4_TMRB4CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ + CTIMER_CTRL4_TMRB4CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ + CTIMER_CTRL4_TMRB4CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. + value. */ + CTIMER_CTRL4_TMRB4CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ + CTIMER_CTRL4_TMRB4CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ + CTIMER_CTRL4_TMRB4CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ + CTIMER_CTRL4_TMRB4CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ + CTIMER_CTRL4_TMRB4CLK_CTMRA4 = 20, /*!< CTMRA4 : Clock source is CTIMERA4 OUT. value. */ + CTIMER_CTRL4_TMRB4CLK_CTMRA1 = 21, /*!< CTMRA1 : Clock source is CTIMERA1 OUT. value. */ + CTIMER_CTRL4_TMRB4CLK_CTMRB1 = 22, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ + CTIMER_CTRL4_TMRB4CLK_CTMRA5 = 23, /*!< CTMRA5 : Clock source is CTIMERA5 OUT. value. */ + CTIMER_CTRL4_TMRB4CLK_CTMRB5 = 24, /*!< CTMRB5 : Clock source is CTIMERB5 OUT. value. */ + CTIMER_CTRL4_TMRB4CLK_CTMRB0 = 25, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ + CTIMER_CTRL4_TMRB4CLK_CTMRB2 = 26, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ + CTIMER_CTRL4_TMRB4CLK_CTMRB3 = 27, /*!< CTMRB3 : Clock source is CTIMERB3 OUT. value. */ + CTIMER_CTRL4_TMRB4CLK_CTMRB6 = 28, /*!< CTMRB6 : Clock source is CTIMERB6 OUT. value. */ + CTIMER_CTRL4_TMRB4CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ + CTIMER_CTRL4_TMRB4CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ + CTIMER_CTRL4_TMRB4CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ +} CTIMER_CTRL4_TMRB4CLK_Enum; + +/* ============================================= CTIMER CTRL4 TMRB4EN [16..16] ============================================= */ +typedef enum { /*!< CTIMER_CTRL4_TMRB4EN */ + CTIMER_CTRL4_TMRB4EN_DIS = 0, /*!< DIS : Counter/Timer B4 Disable. value. */ + CTIMER_CTRL4_TMRB4EN_EN = 1, /*!< EN : Counter/Timer B4 Enable. value. */ +} CTIMER_CTRL4_TMRB4EN_Enum; + +/* ============================================ CTIMER CTRL4 TMRA4POL [12..12] ============================================= */ +typedef enum { /*!< CTIMER_CTRL4_TMRA4POL */ + CTIMER_CTRL4_TMRA4POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINA4 pin is the same as the + timer output. value. */ + CTIMER_CTRL4_TMRA4POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINA4 pin is the inverse of + the timer output. value. */ +} CTIMER_CTRL4_TMRA4POL_Enum; + +/* ============================================ CTIMER CTRL4 TMRA4CLR [11..11] ============================================= */ +typedef enum { /*!< CTIMER_CTRL4_TMRA4CLR */ + CTIMER_CTRL4_TMRA4CLR_RUN = 0, /*!< RUN : Allow counter/timer A4 to run value. */ + CTIMER_CTRL4_TMRA4CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer A4 at 0x0000. value. */ +} CTIMER_CTRL4_TMRA4CLR_Enum; + +/* ============================================ CTIMER CTRL4 TMRA4IE1 [10..10] ============================================= */ +typedef enum { /*!< CTIMER_CTRL4_TMRA4IE1 */ + CTIMER_CTRL4_TMRA4IE1_DIS = 0, /*!< DIS : Disable counter/timer A4 from generating an interrupt + based on COMPR1. value. */ + CTIMER_CTRL4_TMRA4IE1_EN = 1, /*!< EN : Enable counter/timer A4 to generate an interrupt based + on COMPR1. value. */ +} CTIMER_CTRL4_TMRA4IE1_Enum; + +/* ============================================= CTIMER CTRL4 TMRA4IE0 [9..9] ============================================== */ +typedef enum { /*!< CTIMER_CTRL4_TMRA4IE0 */ + CTIMER_CTRL4_TMRA4IE0_DIS = 0, /*!< DIS : Disable counter/timer A4 from generating an interrupt + based on COMPR0. value. */ + CTIMER_CTRL4_TMRA4IE0_EN = 1, /*!< EN : Enable counter/timer A4 to generate an interrupt based + on COMPR0. value. */ +} CTIMER_CTRL4_TMRA4IE0_Enum; + +/* ============================================== CTIMER CTRL4 TMRA4FN [6..8] ============================================== */ +typedef enum { /*!< CTIMER_CTRL4_TMRA4FN */ + CTIMER_CTRL4_TMRA4FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count + to CMPR0A4, stop. value. */ + CTIMER_CTRL4_TMRA4FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide + pulses). Count to CMPR0A4, restart. value. */ + CTIMER_CTRL4_TMRA4FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0A4, assert, + count to CMPR1A4, deassert, stop. value. */ + CTIMER_CTRL4_TMRA4FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0A4, assert, count + to CMPR1A4, deassert, restart. value. */ + CTIMER_CTRL4_TMRA4FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ + CTIMER_CTRL4_TMRA4FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ + CTIMER_CTRL4_TMRA4FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. + value. */ + CTIMER_CTRL4_TMRA4FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ +} CTIMER_CTRL4_TMRA4FN_Enum; + +/* ============================================= CTIMER CTRL4 TMRA4CLK [1..5] ============================================== */ +typedef enum { /*!< CTIMER_CTRL4_TMRA4CLK */ + CTIMER_CTRL4_TMRA4CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINA. value. */ + CTIMER_CTRL4_TMRA4CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ + CTIMER_CTRL4_TMRA4CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ + CTIMER_CTRL4_TMRA4CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ + CTIMER_CTRL4_TMRA4CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ + CTIMER_CTRL4_TMRA4CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ + CTIMER_CTRL4_TMRA4CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ + CTIMER_CTRL4_TMRA4CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ + CTIMER_CTRL4_TMRA4CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ + CTIMER_CTRL4_TMRA4CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ + CTIMER_CTRL4_TMRA4CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ + CTIMER_CTRL4_TMRA4CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ + CTIMER_CTRL4_TMRA4CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ + CTIMER_CTRL4_TMRA4CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ + CTIMER_CTRL4_TMRA4CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. + value. */ + CTIMER_CTRL4_TMRA4CLK_HCLK_DIV4 = 15, /*!< HCLK_DIV4 : Clock source is HCLK / 4. value. */ + CTIMER_CTRL4_TMRA4CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ + CTIMER_CTRL4_TMRA4CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ + CTIMER_CTRL4_TMRA4CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ + CTIMER_CTRL4_TMRA4CLK_CTMRB4 = 20, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ + CTIMER_CTRL4_TMRA4CLK_CTMRA1 = 21, /*!< CTMRA1 : Clock source is CTIMERA1 OUT. value. */ + CTIMER_CTRL4_TMRA4CLK_CTMRB1 = 22, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ + CTIMER_CTRL4_TMRA4CLK_CTMRA5 = 23, /*!< CTMRA5 : Clock source is CTIMERA5 OUT. value. */ + CTIMER_CTRL4_TMRA4CLK_CTMRB5 = 24, /*!< CTMRB5 : Clock source is CTIMERB5 OUT. value. */ + CTIMER_CTRL4_TMRA4CLK_CTMRB0 = 25, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ + CTIMER_CTRL4_TMRA4CLK_CTMRB2 = 26, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ + CTIMER_CTRL4_TMRA4CLK_CTMRB3 = 27, /*!< CTMRB3 : Clock source is CTIMERB3 OUT. value. */ + CTIMER_CTRL4_TMRA4CLK_CTMRB6 = 28, /*!< CTMRB6 : Clock source is CTIMERB6 OUT. value. */ + CTIMER_CTRL4_TMRA4CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ + CTIMER_CTRL4_TMRA4CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ + CTIMER_CTRL4_TMRA4CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ +} CTIMER_CTRL4_TMRA4CLK_Enum; + +/* ============================================== CTIMER CTRL4 TMRA4EN [0..0] ============================================== */ +typedef enum { /*!< CTIMER_CTRL4_TMRA4EN */ + CTIMER_CTRL4_TMRA4EN_DIS = 0, /*!< DIS : Counter/Timer A4 Disable. value. */ + CTIMER_CTRL4_TMRA4EN_EN = 1, /*!< EN : Counter/Timer A4 Enable. value. */ +} CTIMER_CTRL4_TMRA4EN_Enum; + +/* ======================================================= CMPRAUXA4 ======================================================= */ +/* ======================================================= CMPRAUXB4 ======================================================= */ +/* ========================================================= AUX4 ========================================================== */ +/* ============================================ CTIMER AUX4 TMRB4EN23 [30..30] ============================================= */ +typedef enum { /*!< CTIMER_AUX4_TMRB4EN23 */ + CTIMER_AUX4_TMRB4EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ + CTIMER_AUX4_TMRB4EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ +} CTIMER_AUX4_TMRB4EN23_Enum; + +/* ============================================ CTIMER AUX4 TMRB4POL23 [29..29] ============================================ */ +typedef enum { /*!< CTIMER_AUX4_TMRB4POL23 */ + CTIMER_AUX4_TMRB4POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ + CTIMER_AUX4_TMRB4POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ +} CTIMER_AUX4_TMRB4POL23_Enum; + +/* ============================================ CTIMER AUX4 TMRB4TINV [28..28] ============================================= */ +typedef enum { /*!< CTIMER_AUX4_TMRB4TINV */ + CTIMER_AUX4_TMRB4TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ + CTIMER_AUX4_TMRB4TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ +} CTIMER_AUX4_TMRB4TINV_Enum; + +/* =========================================== CTIMER AUX4 TMRB4NOSYNC [27..27] ============================================ */ +typedef enum { /*!< CTIMER_AUX4_TMRB4NOSYNC */ + CTIMER_AUX4_TMRB4NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ + CTIMER_AUX4_TMRB4NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ +} CTIMER_AUX4_TMRB4NOSYNC_Enum; + +/* ============================================ CTIMER AUX4 TMRB4TRIG [23..26] ============================================= */ +typedef enum { /*!< CTIMER_AUX4_TMRB4TRIG */ + CTIMER_AUX4_TMRB4TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ + CTIMER_AUX4_TMRB4TRIG_A4OUT = 1, /*!< A4OUT : Trigger source is CTIMERA4 OUT. value. */ + CTIMER_AUX4_TMRB4TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ + CTIMER_AUX4_TMRB4TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ + CTIMER_AUX4_TMRB4TRIG_A7OUT = 4, /*!< A7OUT : Trigger source is CTIMERA7 OUT. value. */ + CTIMER_AUX4_TMRB4TRIG_B7OUT = 5, /*!< B7OUT : Trigger source is CTIMERB7 OUT. value. */ + CTIMER_AUX4_TMRB4TRIG_A1OUT = 6, /*!< A1OUT : Trigger source is CTIMERA1 OUT. value. */ + CTIMER_AUX4_TMRB4TRIG_B1OUT = 7, /*!< B1OUT : Trigger source is CTIMERB1 OUT. value. */ + CTIMER_AUX4_TMRB4TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ + CTIMER_AUX4_TMRB4TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ + CTIMER_AUX4_TMRB4TRIG_A1OUT2 = 10, /*!< A1OUT2 : Trigger source is CTIMERA1 OUT2. value. */ + CTIMER_AUX4_TMRB4TRIG_B1OUT2 = 11, /*!< B1OUT2 : Trigger source is CTIMERB1 OUT2. value. */ + CTIMER_AUX4_TMRB4TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ + CTIMER_AUX4_TMRB4TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ + CTIMER_AUX4_TMRB4TRIG_B5OUT2DUAL = 14, /*!< B5OUT2DUAL : Trigger source is CTIMERB5 OUT2, dual edge. value. */ + CTIMER_AUX4_TMRB4TRIG_A5OUT2DUAL = 15, /*!< A5OUT2DUAL : Trigger source is CTIMERA5 OUT2, dual edge. value. */ +} CTIMER_AUX4_TMRB4TRIG_Enum; + +/* ============================================ CTIMER AUX4 TMRA4EN23 [14..14] ============================================= */ +typedef enum { /*!< CTIMER_AUX4_TMRA4EN23 */ + CTIMER_AUX4_TMRA4EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ + CTIMER_AUX4_TMRA4EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ +} CTIMER_AUX4_TMRA4EN23_Enum; + +/* ============================================ CTIMER AUX4 TMRA4POL23 [13..13] ============================================ */ +typedef enum { /*!< CTIMER_AUX4_TMRA4POL23 */ + CTIMER_AUX4_TMRA4POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ + CTIMER_AUX4_TMRA4POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ +} CTIMER_AUX4_TMRA4POL23_Enum; + +/* ============================================ CTIMER AUX4 TMRA4TINV [12..12] ============================================= */ +typedef enum { /*!< CTIMER_AUX4_TMRA4TINV */ + CTIMER_AUX4_TMRA4TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ + CTIMER_AUX4_TMRA4TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ +} CTIMER_AUX4_TMRA4TINV_Enum; + +/* =========================================== CTIMER AUX4 TMRA4NOSYNC [11..11] ============================================ */ +typedef enum { /*!< CTIMER_AUX4_TMRA4NOSYNC */ + CTIMER_AUX4_TMRA4NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ + CTIMER_AUX4_TMRA4NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ +} CTIMER_AUX4_TMRA4NOSYNC_Enum; + +/* ============================================= CTIMER AUX4 TMRA4TRIG [7..10] ============================================= */ +typedef enum { /*!< CTIMER_AUX4_TMRA4TRIG */ + CTIMER_AUX4_TMRA4TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ + CTIMER_AUX4_TMRA4TRIG_B4OUT = 1, /*!< B4OUT : Trigger source is CTIMERB4 OUT. value. */ + CTIMER_AUX4_TMRA4TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ + CTIMER_AUX4_TMRA4TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ + CTIMER_AUX4_TMRA4TRIG_A6OUT = 4, /*!< A6OUT : Trigger source is CTIMERA6 OUT. value. */ + CTIMER_AUX4_TMRA4TRIG_B6OUT = 5, /*!< B6OUT : Trigger source is CTIMERB6 OUT. value. */ + CTIMER_AUX4_TMRA4TRIG_A2OUT = 6, /*!< A2OUT : Trigger source is CTIMERA2 OUT. value. */ + CTIMER_AUX4_TMRA4TRIG_B2OUT = 7, /*!< B2OUT : Trigger source is CTIMERB2 OUT. value. */ + CTIMER_AUX4_TMRA4TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ + CTIMER_AUX4_TMRA4TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ + CTIMER_AUX4_TMRA4TRIG_A1OUT2 = 10, /*!< A1OUT2 : Trigger source is CTIMERA1 OUT2. value. */ + CTIMER_AUX4_TMRA4TRIG_B1OUT2 = 11, /*!< B1OUT2 : Trigger source is CTIMERB1 OUT2. value. */ + CTIMER_AUX4_TMRA4TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ + CTIMER_AUX4_TMRA4TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ + CTIMER_AUX4_TMRA4TRIG_B5OUT2DUAL = 14, /*!< B5OUT2DUAL : Trigger source is CTIMERB5 OUT2, dual edge. value. */ + CTIMER_AUX4_TMRA4TRIG_A5OUT2DUAL = 15, /*!< A5OUT2DUAL : Trigger source is CTIMERA5 OUT2, dual edge. value. */ +} CTIMER_AUX4_TMRA4TRIG_Enum; + +/* ========================================================= TMR5 ========================================================== */ +/* ======================================================== CMPRA5 ========================================================= */ +/* ======================================================== CMPRB5 ========================================================= */ +/* ========================================================= CTRL5 ========================================================= */ +/* ============================================= CTIMER CTRL5 CTLINK5 [31..31] ============================================= */ +typedef enum { /*!< CTIMER_CTRL5_CTLINK5 */ + CTIMER_CTRL5_CTLINK5_TWO_16BIT_TIMERS = 0, /*!< TWO_16BIT_TIMERS : Use A5/B5 timers as two independent 16-bit + timers (default). value. */ + CTIMER_CTRL5_CTLINK5_32BIT_TIMER = 1, /*!< 32BIT_TIMER : Link A5/B5 timers into a single 32-bit timer. + value. */ +} CTIMER_CTRL5_CTLINK5_Enum; + +/* ============================================ CTIMER CTRL5 TMRB5POL [28..28] ============================================= */ +typedef enum { /*!< CTIMER_CTRL5_TMRB5POL */ + CTIMER_CTRL5_TMRB5POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINB5 pin is the same as the + timer output. value. */ + CTIMER_CTRL5_TMRB5POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINB5 pin is the inverse of + the timer output. value. */ +} CTIMER_CTRL5_TMRB5POL_Enum; + +/* ============================================ CTIMER CTRL5 TMRB5CLR [27..27] ============================================= */ +typedef enum { /*!< CTIMER_CTRL5_TMRB5CLR */ + CTIMER_CTRL5_TMRB5CLR_RUN = 0, /*!< RUN : Allow counter/timer B5 to run value. */ + CTIMER_CTRL5_TMRB5CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer B5 at 0x0000. value. */ +} CTIMER_CTRL5_TMRB5CLR_Enum; + +/* ============================================ CTIMER CTRL5 TMRB5IE1 [26..26] ============================================= */ +typedef enum { /*!< CTIMER_CTRL5_TMRB5IE1 */ + CTIMER_CTRL5_TMRB5IE1_DIS = 0, /*!< DIS : Disable counter/timer B5 from generating an interrupt + based on COMPR1. value. */ + CTIMER_CTRL5_TMRB5IE1_EN = 1, /*!< EN : Enable counter/timer B5 to generate an interrupt based + on COMPR1. value. */ +} CTIMER_CTRL5_TMRB5IE1_Enum; + +/* ============================================ CTIMER CTRL5 TMRB5IE0 [25..25] ============================================= */ +typedef enum { /*!< CTIMER_CTRL5_TMRB5IE0 */ + CTIMER_CTRL5_TMRB5IE0_DIS = 0, /*!< DIS : Disable counter/timer B5 from generating an interrupt + based on COMPR0. value. */ + CTIMER_CTRL5_TMRB5IE0_EN = 1, /*!< EN : Enable counter/timer B5 to generate an interrupt based + on COMPR0 value. */ +} CTIMER_CTRL5_TMRB5IE0_Enum; + +/* ============================================= CTIMER CTRL5 TMRB5FN [22..24] ============================================= */ +typedef enum { /*!< CTIMER_CTRL5_TMRB5FN */ + CTIMER_CTRL5_TMRB5FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count + to CMPR0B5, stop. value. */ + CTIMER_CTRL5_TMRB5FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide + pulses). Count to CMPR0B5, restart. value. */ + CTIMER_CTRL5_TMRB5FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0B5, assert, + count to CMPR1B5, deassert, stop. value. */ + CTIMER_CTRL5_TMRB5FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0B5, assert, count + to CMPR1B5, deassert, restart. value. */ + CTIMER_CTRL5_TMRB5FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ + CTIMER_CTRL5_TMRB5FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ + CTIMER_CTRL5_TMRB5FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. + value. */ + CTIMER_CTRL5_TMRB5FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ +} CTIMER_CTRL5_TMRB5FN_Enum; + +/* ============================================ CTIMER CTRL5 TMRB5CLK [17..21] ============================================= */ +typedef enum { /*!< CTIMER_CTRL5_TMRB5CLK */ + CTIMER_CTRL5_TMRB5CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINB. value. */ + CTIMER_CTRL5_TMRB5CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ + CTIMER_CTRL5_TMRB5CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ + CTIMER_CTRL5_TMRB5CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ + CTIMER_CTRL5_TMRB5CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ + CTIMER_CTRL5_TMRB5CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ + CTIMER_CTRL5_TMRB5CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ + CTIMER_CTRL5_TMRB5CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ + CTIMER_CTRL5_TMRB5CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ + CTIMER_CTRL5_TMRB5CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ + CTIMER_CTRL5_TMRB5CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ + CTIMER_CTRL5_TMRB5CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ + CTIMER_CTRL5_TMRB5CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ + CTIMER_CTRL5_TMRB5CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ + CTIMER_CTRL5_TMRB5CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. + value. */ + CTIMER_CTRL5_TMRB5CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ + CTIMER_CTRL5_TMRB5CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ + CTIMER_CTRL5_TMRB5CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ + CTIMER_CTRL5_TMRB5CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ + CTIMER_CTRL5_TMRB5CLK_CTMRA5 = 20, /*!< CTMRA5 : Clock source is CTIMERA5 OUT. value. */ + CTIMER_CTRL5_TMRB5CLK_CTMRA0 = 21, /*!< CTMRA0 : Clock source is CTIMERA0 OUT. value. */ + CTIMER_CTRL5_TMRB5CLK_CTMRB0 = 22, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ + CTIMER_CTRL5_TMRB5CLK_CTMRA6 = 23, /*!< CTMRA6 : Clock source is CTIMERA6 OUT. value. */ + CTIMER_CTRL5_TMRB5CLK_CTMRB6 = 24, /*!< CTMRB6 : Clock source is CTIMERB6 OUT. value. */ + CTIMER_CTRL5_TMRB5CLK_CTMRB1 = 25, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ + CTIMER_CTRL5_TMRB5CLK_CTMRB2 = 26, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ + CTIMER_CTRL5_TMRB5CLK_CTMRB3 = 27, /*!< CTMRB3 : Clock source is CTIMERB3 OUT. value. */ + CTIMER_CTRL5_TMRB5CLK_CTMRB4 = 28, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ + CTIMER_CTRL5_TMRB5CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ + CTIMER_CTRL5_TMRB5CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ + CTIMER_CTRL5_TMRB5CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ +} CTIMER_CTRL5_TMRB5CLK_Enum; + +/* ============================================= CTIMER CTRL5 TMRB5EN [16..16] ============================================= */ +typedef enum { /*!< CTIMER_CTRL5_TMRB5EN */ + CTIMER_CTRL5_TMRB5EN_DIS = 0, /*!< DIS : Counter/Timer B5 Disable. value. */ + CTIMER_CTRL5_TMRB5EN_EN = 1, /*!< EN : Counter/Timer B5 Enable. value. */ +} CTIMER_CTRL5_TMRB5EN_Enum; + +/* ============================================ CTIMER CTRL5 TMRA5POL [12..12] ============================================= */ +typedef enum { /*!< CTIMER_CTRL5_TMRA5POL */ + CTIMER_CTRL5_TMRA5POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINA5 pin is the same as the + timer output. value. */ + CTIMER_CTRL5_TMRA5POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINA5 pin is the inverse of + the timer output. value. */ +} CTIMER_CTRL5_TMRA5POL_Enum; + +/* ============================================ CTIMER CTRL5 TMRA5CLR [11..11] ============================================= */ +typedef enum { /*!< CTIMER_CTRL5_TMRA5CLR */ + CTIMER_CTRL5_TMRA5CLR_RUN = 0, /*!< RUN : Allow counter/timer A5 to run value. */ + CTIMER_CTRL5_TMRA5CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer A5 at 0x0000. value. */ +} CTIMER_CTRL5_TMRA5CLR_Enum; + +/* ============================================ CTIMER CTRL5 TMRA5IE1 [10..10] ============================================= */ +typedef enum { /*!< CTIMER_CTRL5_TMRA5IE1 */ + CTIMER_CTRL5_TMRA5IE1_DIS = 0, /*!< DIS : Disable counter/timer A5 from generating an interrupt + based on COMPR1. value. */ + CTIMER_CTRL5_TMRA5IE1_EN = 1, /*!< EN : Enable counter/timer A5 to generate an interrupt based + on COMPR1. value. */ +} CTIMER_CTRL5_TMRA5IE1_Enum; + +/* ============================================= CTIMER CTRL5 TMRA5IE0 [9..9] ============================================== */ +typedef enum { /*!< CTIMER_CTRL5_TMRA5IE0 */ + CTIMER_CTRL5_TMRA5IE0_DIS = 0, /*!< DIS : Disable counter/timer A5 from generating an interrupt + based on COMPR0. value. */ + CTIMER_CTRL5_TMRA5IE0_EN = 1, /*!< EN : Enable counter/timer A5 to generate an interrupt based + on COMPR0. value. */ +} CTIMER_CTRL5_TMRA5IE0_Enum; + +/* ============================================== CTIMER CTRL5 TMRA5FN [6..8] ============================================== */ +typedef enum { /*!< CTIMER_CTRL5_TMRA5FN */ + CTIMER_CTRL5_TMRA5FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count + to CMPR0A5, stop. value. */ + CTIMER_CTRL5_TMRA5FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide + pulses). Count to CMPR0A5, restart. value. */ + CTIMER_CTRL5_TMRA5FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0A5, assert, + count to CMPR1A5, deassert, stop. value. */ + CTIMER_CTRL5_TMRA5FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0A5, assert, count + to CMPR1A5, deassert, restart. value. */ + CTIMER_CTRL5_TMRA5FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ + CTIMER_CTRL5_TMRA5FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ + CTIMER_CTRL5_TMRA5FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. + value. */ + CTIMER_CTRL5_TMRA5FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ +} CTIMER_CTRL5_TMRA5FN_Enum; + +/* ============================================= CTIMER CTRL5 TMRA5CLK [1..5] ============================================== */ +typedef enum { /*!< CTIMER_CTRL5_TMRA5CLK */ + CTIMER_CTRL5_TMRA5CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINA. value. */ + CTIMER_CTRL5_TMRA5CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ + CTIMER_CTRL5_TMRA5CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ + CTIMER_CTRL5_TMRA5CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ + CTIMER_CTRL5_TMRA5CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ + CTIMER_CTRL5_TMRA5CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ + CTIMER_CTRL5_TMRA5CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ + CTIMER_CTRL5_TMRA5CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ + CTIMER_CTRL5_TMRA5CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ + CTIMER_CTRL5_TMRA5CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ + CTIMER_CTRL5_TMRA5CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ + CTIMER_CTRL5_TMRA5CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ + CTIMER_CTRL5_TMRA5CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ + CTIMER_CTRL5_TMRA5CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ + CTIMER_CTRL5_TMRA5CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. + value. */ + CTIMER_CTRL5_TMRA5CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ + CTIMER_CTRL5_TMRA5CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ + CTIMER_CTRL5_TMRA5CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ + CTIMER_CTRL5_TMRA5CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ + CTIMER_CTRL5_TMRA5CLK_CTMRB5 = 20, /*!< CTMRB5 : Clock source is CTIMERB5 OUT. value. */ + CTIMER_CTRL5_TMRA5CLK_CTMRA0 = 21, /*!< CTMRA0 : Clock source is CTIMERA0 OUT. value. */ + CTIMER_CTRL5_TMRA5CLK_CTMRB0 = 22, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ + CTIMER_CTRL5_TMRA5CLK_CTMRA6 = 23, /*!< CTMRA6 : Clock source is CTIMERA6 OUT. value. */ + CTIMER_CTRL5_TMRA5CLK_CTMRB6 = 24, /*!< CTMRB6 : Clock source is CTIMERB6 OUT. value. */ + CTIMER_CTRL5_TMRA5CLK_CTMRB1 = 25, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ + CTIMER_CTRL5_TMRA5CLK_CTMRB2 = 26, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ + CTIMER_CTRL5_TMRA5CLK_CTMRB3 = 27, /*!< CTMRB3 : Clock source is CTIMERB3 OUT. value. */ + CTIMER_CTRL5_TMRA5CLK_CTMRB4 = 28, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ + CTIMER_CTRL5_TMRA5CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ + CTIMER_CTRL5_TMRA5CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ + CTIMER_CTRL5_TMRA5CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ +} CTIMER_CTRL5_TMRA5CLK_Enum; + +/* ============================================== CTIMER CTRL5 TMRA5EN [0..0] ============================================== */ +typedef enum { /*!< CTIMER_CTRL5_TMRA5EN */ + CTIMER_CTRL5_TMRA5EN_DIS = 0, /*!< DIS : Counter/Timer A5 Disable. value. */ + CTIMER_CTRL5_TMRA5EN_EN = 1, /*!< EN : Counter/Timer A5 Enable. value. */ +} CTIMER_CTRL5_TMRA5EN_Enum; + +/* ======================================================= CMPRAUXA5 ======================================================= */ +/* ======================================================= CMPRAUXB5 ======================================================= */ +/* ========================================================= AUX5 ========================================================== */ +/* ============================================ CTIMER AUX5 TMRB5EN23 [30..30] ============================================= */ +typedef enum { /*!< CTIMER_AUX5_TMRB5EN23 */ + CTIMER_AUX5_TMRB5EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ + CTIMER_AUX5_TMRB5EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ +} CTIMER_AUX5_TMRB5EN23_Enum; + +/* ============================================ CTIMER AUX5 TMRB5POL23 [29..29] ============================================ */ +typedef enum { /*!< CTIMER_AUX5_TMRB5POL23 */ + CTIMER_AUX5_TMRB5POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ + CTIMER_AUX5_TMRB5POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ +} CTIMER_AUX5_TMRB5POL23_Enum; + +/* ============================================ CTIMER AUX5 TMRB5TINV [28..28] ============================================= */ +typedef enum { /*!< CTIMER_AUX5_TMRB5TINV */ + CTIMER_AUX5_TMRB5TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ + CTIMER_AUX5_TMRB5TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ +} CTIMER_AUX5_TMRB5TINV_Enum; + +/* =========================================== CTIMER AUX5 TMRB5NOSYNC [27..27] ============================================ */ +typedef enum { /*!< CTIMER_AUX5_TMRB5NOSYNC */ + CTIMER_AUX5_TMRB5NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ + CTIMER_AUX5_TMRB5NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ +} CTIMER_AUX5_TMRB5NOSYNC_Enum; + +/* ============================================ CTIMER AUX5 TMRB5TRIG [23..26] ============================================= */ +typedef enum { /*!< CTIMER_AUX5_TMRB5TRIG */ + CTIMER_AUX5_TMRB5TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ + CTIMER_AUX5_TMRB5TRIG_A5OUT = 1, /*!< A5OUT : Trigger source is CTIMERA5 OUT. value. */ + CTIMER_AUX5_TMRB5TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ + CTIMER_AUX5_TMRB5TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ + CTIMER_AUX5_TMRB5TRIG_A6OUT = 4, /*!< A6OUT : Trigger source is CTIMERA6 OUT. value. */ + CTIMER_AUX5_TMRB5TRIG_B6OUT = 5, /*!< B6OUT : Trigger source is CTIMERB6 OUT. value. */ + CTIMER_AUX5_TMRB5TRIG_A1OUT = 6, /*!< A1OUT : Trigger source is CTIMERA1 OUT. value. */ + CTIMER_AUX5_TMRB5TRIG_B1OUT = 7, /*!< B1OUT : Trigger source is CTIMERB1 OUT. value. */ + CTIMER_AUX5_TMRB5TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ + CTIMER_AUX5_TMRB5TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ + CTIMER_AUX5_TMRB5TRIG_A0OUT2 = 10, /*!< A0OUT2 : Trigger source is CTIMERA0 OUT2. value. */ + CTIMER_AUX5_TMRB5TRIG_B0OUT2 = 11, /*!< B0OUT2 : Trigger source is CTIMERB0 OUT2. value. */ + CTIMER_AUX5_TMRB5TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ + CTIMER_AUX5_TMRB5TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ + CTIMER_AUX5_TMRB5TRIG_B4OUT2DUAL = 14, /*!< B4OUT2DUAL : Trigger source is CTIMERB4 OUT2, dual edge. value. */ + CTIMER_AUX5_TMRB5TRIG_A4OUT2DUAL = 15, /*!< A4OUT2DUAL : Trigger source is CTIMERA4 OUT2, dual edge. value. */ +} CTIMER_AUX5_TMRB5TRIG_Enum; + +/* ============================================ CTIMER AUX5 TMRA5EN23 [14..14] ============================================= */ +typedef enum { /*!< CTIMER_AUX5_TMRA5EN23 */ + CTIMER_AUX5_TMRA5EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ + CTIMER_AUX5_TMRA5EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ +} CTIMER_AUX5_TMRA5EN23_Enum; + +/* ============================================ CTIMER AUX5 TMRA5POL23 [13..13] ============================================ */ +typedef enum { /*!< CTIMER_AUX5_TMRA5POL23 */ + CTIMER_AUX5_TMRA5POL23_NORMAL = 0, /*!< NORMAL : Upper output normal polarity value. */ + CTIMER_AUX5_TMRA5POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ +} CTIMER_AUX5_TMRA5POL23_Enum; + +/* ============================================ CTIMER AUX5 TMRA5TINV [12..12] ============================================= */ +typedef enum { /*!< CTIMER_AUX5_TMRA5TINV */ + CTIMER_AUX5_TMRA5TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ + CTIMER_AUX5_TMRA5TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ +} CTIMER_AUX5_TMRA5TINV_Enum; + +/* =========================================== CTIMER AUX5 TMRA5NOSYNC [11..11] ============================================ */ +typedef enum { /*!< CTIMER_AUX5_TMRA5NOSYNC */ + CTIMER_AUX5_TMRA5NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ + CTIMER_AUX5_TMRA5NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ +} CTIMER_AUX5_TMRA5NOSYNC_Enum; + +/* ============================================= CTIMER AUX5 TMRA5TRIG [7..10] ============================================= */ +typedef enum { /*!< CTIMER_AUX5_TMRA5TRIG */ + CTIMER_AUX5_TMRA5TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ + CTIMER_AUX5_TMRA5TRIG_B5OUT = 1, /*!< B5OUT : Trigger source is CTIMERB5 OUT. value. */ + CTIMER_AUX5_TMRA5TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ + CTIMER_AUX5_TMRA5TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ + CTIMER_AUX5_TMRA5TRIG_A4OUT = 4, /*!< A4OUT : Trigger source is CTIMERA4 OUT. value. */ + CTIMER_AUX5_TMRA5TRIG_B4OUT = 5, /*!< B4OUT : Trigger source is CTIMERB4 OUT. value. */ + CTIMER_AUX5_TMRA5TRIG_A2OUT = 6, /*!< A2OUT : Trigger source is CTIMERA2 OUT. value. */ + CTIMER_AUX5_TMRA5TRIG_B2OUT = 7, /*!< B2OUT : Trigger source is CTIMERB2 OUT. value. */ + CTIMER_AUX5_TMRA5TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ + CTIMER_AUX5_TMRA5TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ + CTIMER_AUX5_TMRA5TRIG_A0OUT2 = 10, /*!< A0OUT2 : Trigger source is CTIMERA0 OUT2. value. */ + CTIMER_AUX5_TMRA5TRIG_B0OUT2 = 11, /*!< B0OUT2 : Trigger source is CTIMERB0 OUT2. value. */ + CTIMER_AUX5_TMRA5TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ + CTIMER_AUX5_TMRA5TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ + CTIMER_AUX5_TMRA5TRIG_B4OUT2DUAL = 14, /*!< B4OUT2DUAL : Trigger source is CTIMERB4 OUT2, dual edge. value. */ + CTIMER_AUX5_TMRA5TRIG_A4OUT2DUAL = 15, /*!< A4OUT2DUAL : Trigger source is CTIMERA4 OUT2, dual edge. value. */ +} CTIMER_AUX5_TMRA5TRIG_Enum; + +/* ========================================================= TMR6 ========================================================== */ +/* ======================================================== CMPRA6 ========================================================= */ +/* ======================================================== CMPRB6 ========================================================= */ +/* ========================================================= CTRL6 ========================================================= */ +/* ============================================= CTIMER CTRL6 CTLINK6 [31..31] ============================================= */ +typedef enum { /*!< CTIMER_CTRL6_CTLINK6 */ + CTIMER_CTRL6_CTLINK6_TWO_16BIT_TIMERS = 0, /*!< TWO_16BIT_TIMERS : Use A6/B6 timers as two independent 16-bit + timers (default). value. */ + CTIMER_CTRL6_CTLINK6_32BIT_TIMER = 1, /*!< 32BIT_TIMER : Link A6/B6 timers into a single 32-bit timer. + value. */ +} CTIMER_CTRL6_CTLINK6_Enum; + +/* ============================================ CTIMER CTRL6 TMRB6POL [28..28] ============================================= */ +typedef enum { /*!< CTIMER_CTRL6_TMRB6POL */ + CTIMER_CTRL6_TMRB6POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINB6 pin is the same as the + timer output. value. */ + CTIMER_CTRL6_TMRB6POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINB6 pin is the inverse of + the timer output. value. */ +} CTIMER_CTRL6_TMRB6POL_Enum; + +/* ============================================ CTIMER CTRL6 TMRB6CLR [27..27] ============================================= */ +typedef enum { /*!< CTIMER_CTRL6_TMRB6CLR */ + CTIMER_CTRL6_TMRB6CLR_RUN = 0, /*!< RUN : Allow counter/timer B6 to run value. */ + CTIMER_CTRL6_TMRB6CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer B6 at 0x0000. value. */ +} CTIMER_CTRL6_TMRB6CLR_Enum; + +/* ============================================ CTIMER CTRL6 TMRB6IE1 [26..26] ============================================= */ +typedef enum { /*!< CTIMER_CTRL6_TMRB6IE1 */ + CTIMER_CTRL6_TMRB6IE1_DIS = 0, /*!< DIS : Disable counter/timer B6 from generating an interrupt + based on COMPR1. value. */ + CTIMER_CTRL6_TMRB6IE1_EN = 1, /*!< EN : Enable counter/timer B6 to generate an interrupt based + on COMPR1. value. */ +} CTIMER_CTRL6_TMRB6IE1_Enum; + +/* ============================================ CTIMER CTRL6 TMRB6IE0 [25..25] ============================================= */ +typedef enum { /*!< CTIMER_CTRL6_TMRB6IE0 */ + CTIMER_CTRL6_TMRB6IE0_DIS = 0, /*!< DIS : Disable counter/timer B6 from generating an interrupt + based on COMPR0. value. */ + CTIMER_CTRL6_TMRB6IE0_EN = 1, /*!< EN : Enable counter/timer B6 to generate an interrupt based + on COMPR0 value. */ +} CTIMER_CTRL6_TMRB6IE0_Enum; + +/* ============================================= CTIMER CTRL6 TMRB6FN [22..24] ============================================= */ +typedef enum { /*!< CTIMER_CTRL6_TMRB6FN */ + CTIMER_CTRL6_TMRB6FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count + to CMPR0B6, stop. value. */ + CTIMER_CTRL6_TMRB6FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide + pulses). Count to CMPR0B6, restart. value. */ + CTIMER_CTRL6_TMRB6FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0B6, assert, + count to CMPR1B6, deassert, stop. value. */ + CTIMER_CTRL6_TMRB6FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0B6, assert, count + to CMPR1B6, deassert, restart. value. */ + CTIMER_CTRL6_TMRB6FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ + CTIMER_CTRL6_TMRB6FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ + CTIMER_CTRL6_TMRB6FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. + value. */ + CTIMER_CTRL6_TMRB6FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ +} CTIMER_CTRL6_TMRB6FN_Enum; + +/* ============================================ CTIMER CTRL6 TMRB6CLK [17..21] ============================================= */ +typedef enum { /*!< CTIMER_CTRL6_TMRB6CLK */ + CTIMER_CTRL6_TMRB6CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINB. value. */ + CTIMER_CTRL6_TMRB6CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ + CTIMER_CTRL6_TMRB6CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ + CTIMER_CTRL6_TMRB6CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ + CTIMER_CTRL6_TMRB6CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ + CTIMER_CTRL6_TMRB6CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ + CTIMER_CTRL6_TMRB6CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ + CTIMER_CTRL6_TMRB6CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ + CTIMER_CTRL6_TMRB6CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ + CTIMER_CTRL6_TMRB6CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ + CTIMER_CTRL6_TMRB6CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ + CTIMER_CTRL6_TMRB6CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ + CTIMER_CTRL6_TMRB6CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ + CTIMER_CTRL6_TMRB6CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ + CTIMER_CTRL6_TMRB6CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. + value. */ + CTIMER_CTRL6_TMRB6CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ + CTIMER_CTRL6_TMRB6CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ + CTIMER_CTRL6_TMRB6CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ + CTIMER_CTRL6_TMRB6CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ + CTIMER_CTRL6_TMRB6CLK_CTMRA6 = 20, /*!< CTMRA6 : Clock source is CTIMERA6 OUT. value. */ + CTIMER_CTRL6_TMRB6CLK_CTMRA3 = 21, /*!< CTMRA3 : Clock source is CTIMERA3 OUT. value. */ + CTIMER_CTRL6_TMRB6CLK_CTMRB3 = 22, /*!< CTMRB3 : Clock source is CTIMERB3 OUT. value. */ + CTIMER_CTRL6_TMRB6CLK_CTMRA7 = 23, /*!< CTMRA7 : Clock source is CTIMERA7 OUT. value. */ + CTIMER_CTRL6_TMRB6CLK_CTMRB7 = 24, /*!< CTMRB7 : Clock source is CTIMERB7 OUT. value. */ + CTIMER_CTRL6_TMRB6CLK_CTMRB0 = 25, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ + CTIMER_CTRL6_TMRB6CLK_CTMRB1 = 26, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ + CTIMER_CTRL6_TMRB6CLK_CTMRB2 = 27, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ + CTIMER_CTRL6_TMRB6CLK_CTMRB4 = 28, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ + CTIMER_CTRL6_TMRB6CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ + CTIMER_CTRL6_TMRB6CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ + CTIMER_CTRL6_TMRB6CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ +} CTIMER_CTRL6_TMRB6CLK_Enum; + +/* ============================================= CTIMER CTRL6 TMRB6EN [16..16] ============================================= */ +typedef enum { /*!< CTIMER_CTRL6_TMRB6EN */ + CTIMER_CTRL6_TMRB6EN_DIS = 0, /*!< DIS : Counter/Timer B6 Disable. value. */ + CTIMER_CTRL6_TMRB6EN_EN = 1, /*!< EN : Counter/Timer B6 Enable. value. */ +} CTIMER_CTRL6_TMRB6EN_Enum; + +/* ============================================ CTIMER CTRL6 TMRA6POL [12..12] ============================================= */ +typedef enum { /*!< CTIMER_CTRL6_TMRA6POL */ + CTIMER_CTRL6_TMRA6POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINA6 pin is the same as the + timer output. value. */ + CTIMER_CTRL6_TMRA6POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINA6 pin is the inverse of + the timer output. value. */ +} CTIMER_CTRL6_TMRA6POL_Enum; + +/* ============================================ CTIMER CTRL6 TMRA6CLR [11..11] ============================================= */ +typedef enum { /*!< CTIMER_CTRL6_TMRA6CLR */ + CTIMER_CTRL6_TMRA6CLR_RUN = 0, /*!< RUN : Allow counter/timer A6 to run value. */ + CTIMER_CTRL6_TMRA6CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer A6 at 0x0000. value. */ +} CTIMER_CTRL6_TMRA6CLR_Enum; + +/* ============================================ CTIMER CTRL6 TMRA6IE1 [10..10] ============================================= */ +typedef enum { /*!< CTIMER_CTRL6_TMRA6IE1 */ + CTIMER_CTRL6_TMRA6IE1_DIS = 0, /*!< DIS : Disable counter/timer A6 from generating an interrupt + based on COMPR1. value. */ + CTIMER_CTRL6_TMRA6IE1_EN = 1, /*!< EN : Enable counter/timer A6 to generate an interrupt based + on COMPR1. value. */ +} CTIMER_CTRL6_TMRA6IE1_Enum; + +/* ============================================= CTIMER CTRL6 TMRA6IE0 [9..9] ============================================== */ +typedef enum { /*!< CTIMER_CTRL6_TMRA6IE0 */ + CTIMER_CTRL6_TMRA6IE0_DIS = 0, /*!< DIS : Disable counter/timer A6 from generating an interrupt + based on COMPR0. value. */ + CTIMER_CTRL6_TMRA6IE0_EN = 1, /*!< EN : Enable counter/timer A6 to generate an interrupt based + on COMPR0. value. */ +} CTIMER_CTRL6_TMRA6IE0_Enum; + +/* ============================================== CTIMER CTRL6 TMRA6FN [6..8] ============================================== */ +typedef enum { /*!< CTIMER_CTRL6_TMRA6FN */ + CTIMER_CTRL6_TMRA6FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count + to CMPR0A6, stop. value. */ + CTIMER_CTRL6_TMRA6FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide + pulses). Count to CMPR0A6, restart. value. */ + CTIMER_CTRL6_TMRA6FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0A6, assert, + count to CMPR1A6, deassert, stop. value. */ + CTIMER_CTRL6_TMRA6FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0A6, assert, count + to CMPR1A6, deassert, restart. value. */ + CTIMER_CTRL6_TMRA6FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ + CTIMER_CTRL6_TMRA6FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ + CTIMER_CTRL6_TMRA6FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. + value. */ + CTIMER_CTRL6_TMRA6FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ +} CTIMER_CTRL6_TMRA6FN_Enum; + +/* ============================================= CTIMER CTRL6 TMRA6CLK [1..5] ============================================== */ +typedef enum { /*!< CTIMER_CTRL6_TMRA6CLK */ + CTIMER_CTRL6_TMRA6CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINA. value. */ + CTIMER_CTRL6_TMRA6CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ + CTIMER_CTRL6_TMRA6CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ + CTIMER_CTRL6_TMRA6CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ + CTIMER_CTRL6_TMRA6CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ + CTIMER_CTRL6_TMRA6CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ + CTIMER_CTRL6_TMRA6CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ + CTIMER_CTRL6_TMRA6CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ + CTIMER_CTRL6_TMRA6CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ + CTIMER_CTRL6_TMRA6CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ + CTIMER_CTRL6_TMRA6CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ + CTIMER_CTRL6_TMRA6CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ + CTIMER_CTRL6_TMRA6CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ + CTIMER_CTRL6_TMRA6CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ + CTIMER_CTRL6_TMRA6CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. + value. */ + CTIMER_CTRL6_TMRA6CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ + CTIMER_CTRL6_TMRA6CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ + CTIMER_CTRL6_TMRA6CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ + CTIMER_CTRL6_TMRA6CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ + CTIMER_CTRL6_TMRA6CLK_CTMRB6 = 20, /*!< CTMRB6 : Clock source is CTIMERB6 OUT. value. */ + CTIMER_CTRL6_TMRA6CLK_CTMRA3 = 21, /*!< CTMRA3 : Clock source is CTIMERA3 OUT. value. */ + CTIMER_CTRL6_TMRA6CLK_CTMRB3 = 22, /*!< CTMRB3 : Clock source is CTIMERB3 OUT. value. */ + CTIMER_CTRL6_TMRA6CLK_CTMRA7 = 23, /*!< CTMRA7 : Clock source is CTIMERA7 OUT. value. */ + CTIMER_CTRL6_TMRA6CLK_CTMRB7 = 24, /*!< CTMRB7 : Clock source is CTIMERB7 OUT. value. */ + CTIMER_CTRL6_TMRA6CLK_CTMRB0 = 25, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ + CTIMER_CTRL6_TMRA6CLK_CTMRB1 = 26, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ + CTIMER_CTRL6_TMRA6CLK_CTMRB2 = 27, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ + CTIMER_CTRL6_TMRA6CLK_CTMRB4 = 28, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ + CTIMER_CTRL6_TMRA6CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ + CTIMER_CTRL6_TMRA6CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ + CTIMER_CTRL6_TMRA6CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ +} CTIMER_CTRL6_TMRA6CLK_Enum; + +/* ============================================== CTIMER CTRL6 TMRA6EN [0..0] ============================================== */ +typedef enum { /*!< CTIMER_CTRL6_TMRA6EN */ + CTIMER_CTRL6_TMRA6EN_DIS = 0, /*!< DIS : Counter/Timer A6 Disable. value. */ + CTIMER_CTRL6_TMRA6EN_EN = 1, /*!< EN : Counter/Timer A6 Enable. value. */ +} CTIMER_CTRL6_TMRA6EN_Enum; + +/* ======================================================= CMPRAUXA6 ======================================================= */ +/* ======================================================= CMPRAUXB6 ======================================================= */ +/* ========================================================= AUX6 ========================================================== */ +/* ============================================ CTIMER AUX6 TMRB6EN23 [30..30] ============================================= */ +typedef enum { /*!< CTIMER_AUX6_TMRB6EN23 */ + CTIMER_AUX6_TMRB6EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ + CTIMER_AUX6_TMRB6EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ +} CTIMER_AUX6_TMRB6EN23_Enum; + +/* ============================================ CTIMER AUX6 TMRB6POL23 [29..29] ============================================ */ +typedef enum { /*!< CTIMER_AUX6_TMRB6POL23 */ + CTIMER_AUX6_TMRB6POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ + CTIMER_AUX6_TMRB6POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ +} CTIMER_AUX6_TMRB6POL23_Enum; + +/* ============================================ CTIMER AUX6 TMRB6TINV [28..28] ============================================= */ +typedef enum { /*!< CTIMER_AUX6_TMRB6TINV */ + CTIMER_AUX6_TMRB6TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ + CTIMER_AUX6_TMRB6TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ +} CTIMER_AUX6_TMRB6TINV_Enum; + +/* =========================================== CTIMER AUX6 TMRB6NOSYNC [27..27] ============================================ */ +typedef enum { /*!< CTIMER_AUX6_TMRB6NOSYNC */ + CTIMER_AUX6_TMRB6NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ + CTIMER_AUX6_TMRB6NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ +} CTIMER_AUX6_TMRB6NOSYNC_Enum; + +/* ============================================ CTIMER AUX6 TMRB6TRIG [23..26] ============================================= */ +typedef enum { /*!< CTIMER_AUX6_TMRB6TRIG */ + CTIMER_AUX6_TMRB6TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ + CTIMER_AUX6_TMRB6TRIG_A6OUT = 1, /*!< A6OUT : Trigger source is CTIMERA6 OUT. value. */ + CTIMER_AUX6_TMRB6TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ + CTIMER_AUX6_TMRB6TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ + CTIMER_AUX6_TMRB6TRIG_A4OUT = 4, /*!< A4OUT : Trigger source is CTIMERA4 OUT. value. */ + CTIMER_AUX6_TMRB6TRIG_B4OUT = 5, /*!< B4OUT : Trigger source is CTIMERB4 OUT. value. */ + CTIMER_AUX6_TMRB6TRIG_A1OUT = 6, /*!< A1OUT : Trigger source is CTIMERA1 OUT. value. */ + CTIMER_AUX6_TMRB6TRIG_B1OUT = 7, /*!< B1OUT : Trigger source is CTIMERB1 OUT. value. */ + CTIMER_AUX6_TMRB6TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ + CTIMER_AUX6_TMRB6TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ + CTIMER_AUX6_TMRB6TRIG_A2OUT2 = 10, /*!< A2OUT2 : Trigger source is CTIMERA2 OUT2. value. */ + CTIMER_AUX6_TMRB6TRIG_B2OUT2 = 11, /*!< B2OUT2 : Trigger source is CTIMERB2 OUT2. value. */ + CTIMER_AUX6_TMRB6TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ + CTIMER_AUX6_TMRB6TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ + CTIMER_AUX6_TMRB6TRIG_B0OUT2DUAL = 14, /*!< B0OUT2DUAL : Trigger source is CTIMERB0 OUT2, dual edge. value. */ + CTIMER_AUX6_TMRB6TRIG_A0OUT2DUAL = 15, /*!< A0OUT2DUAL : Trigger source is CTIMERA0 OUT2, dual edge. value. */ +} CTIMER_AUX6_TMRB6TRIG_Enum; + +/* ============================================ CTIMER AUX6 TMRA6EN23 [14..14] ============================================= */ +typedef enum { /*!< CTIMER_AUX6_TMRA6EN23 */ + CTIMER_AUX6_TMRA6EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ + CTIMER_AUX6_TMRA6EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ +} CTIMER_AUX6_TMRA6EN23_Enum; + +/* ============================================ CTIMER AUX6 TMRA6POL23 [13..13] ============================================ */ +typedef enum { /*!< CTIMER_AUX6_TMRA6POL23 */ + CTIMER_AUX6_TMRA6POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ + CTIMER_AUX6_TMRA6POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ +} CTIMER_AUX6_TMRA6POL23_Enum; + +/* ============================================ CTIMER AUX6 TMRA6TINV [12..12] ============================================= */ +typedef enum { /*!< CTIMER_AUX6_TMRA6TINV */ + CTIMER_AUX6_TMRA6TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ + CTIMER_AUX6_TMRA6TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ +} CTIMER_AUX6_TMRA6TINV_Enum; + +/* =========================================== CTIMER AUX6 TMRA6NOSYNC [11..11] ============================================ */ +typedef enum { /*!< CTIMER_AUX6_TMRA6NOSYNC */ + CTIMER_AUX6_TMRA6NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ + CTIMER_AUX6_TMRA6NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ +} CTIMER_AUX6_TMRA6NOSYNC_Enum; + +/* ============================================= CTIMER AUX6 TMRA6TRIG [7..10] ============================================= */ +typedef enum { /*!< CTIMER_AUX6_TMRA6TRIG */ + CTIMER_AUX6_TMRA6TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ + CTIMER_AUX6_TMRA6TRIG_B6OUT = 1, /*!< B6OUT : Trigger source is CTIMERB6 OUT. value. */ + CTIMER_AUX6_TMRA6TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ + CTIMER_AUX6_TMRA6TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ + CTIMER_AUX6_TMRA6TRIG_A5OUT = 4, /*!< A5OUT : Trigger source is CTIMERA5 OUT. value. */ + CTIMER_AUX6_TMRA6TRIG_B5OUT = 5, /*!< B5OUT : Trigger source is CTIMERB5 OUT. value. */ + CTIMER_AUX6_TMRA6TRIG_A1OUT = 6, /*!< A1OUT : Trigger source is CTIMERA1 OUT. value. */ + CTIMER_AUX6_TMRA6TRIG_B1OUT = 7, /*!< B1OUT : Trigger source is CTIMERB1 OUT. value. */ + CTIMER_AUX6_TMRA6TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ + CTIMER_AUX6_TMRA6TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ + CTIMER_AUX6_TMRA6TRIG_A2OUT2 = 10, /*!< A2OUT2 : Trigger source is CTIMERA2 OUT2. value. */ + CTIMER_AUX6_TMRA6TRIG_B2OUT2 = 11, /*!< B2OUT2 : Trigger source is CTIMERBb OUT2. value. */ + CTIMER_AUX6_TMRA6TRIG_A5OUT2DUAL = 12, /*!< A5OUT2DUAL : Trigger source is CTIMERA5 OUT2, dual edge. value. */ + CTIMER_AUX6_TMRA6TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ + CTIMER_AUX6_TMRA6TRIG_B0OUT2DUAL = 14, /*!< B0OUT2DUAL : Trigger source is CTIMERB0 OUT2, dual edge. value. */ + CTIMER_AUX6_TMRA6TRIG_A0OUT2DUAL = 15, /*!< A0OUT2DUAL : Trigger source is CTIMERA0 OUT2, dual edge. value. */ +} CTIMER_AUX6_TMRA6TRIG_Enum; + +/* ========================================================= TMR7 ========================================================== */ +/* ======================================================== CMPRA7 ========================================================= */ +/* ======================================================== CMPRB7 ========================================================= */ +/* ========================================================= CTRL7 ========================================================= */ +/* ============================================= CTIMER CTRL7 CTLINK7 [31..31] ============================================= */ +typedef enum { /*!< CTIMER_CTRL7_CTLINK7 */ + CTIMER_CTRL7_CTLINK7_TWO_16BIT_TIMERS = 0, /*!< TWO_16BIT_TIMERS : Use A7/B7 timers as two independent 16-bit + timers (default). value. */ + CTIMER_CTRL7_CTLINK7_32BIT_TIMER = 1, /*!< 32BIT_TIMER : Link A7/B7 timers into a single 32-bit timer. + value. */ +} CTIMER_CTRL7_CTLINK7_Enum; + +/* ============================================ CTIMER CTRL7 TMRB7POL [28..28] ============================================= */ +typedef enum { /*!< CTIMER_CTRL7_TMRB7POL */ + CTIMER_CTRL7_TMRB7POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINB7 pin is the same as the + timer output. value. */ + CTIMER_CTRL7_TMRB7POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINB7 pin is the inverse of + the timer output. value. */ +} CTIMER_CTRL7_TMRB7POL_Enum; + +/* ============================================ CTIMER CTRL7 TMRB7CLR [27..27] ============================================= */ +typedef enum { /*!< CTIMER_CTRL7_TMRB7CLR */ + CTIMER_CTRL7_TMRB7CLR_RUN = 0, /*!< RUN : Allow counter/timer B7 to run value. */ + CTIMER_CTRL7_TMRB7CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer B7 at 0x0000. value. */ +} CTIMER_CTRL7_TMRB7CLR_Enum; + +/* ============================================ CTIMER CTRL7 TMRB7IE1 [26..26] ============================================= */ +typedef enum { /*!< CTIMER_CTRL7_TMRB7IE1 */ + CTIMER_CTRL7_TMRB7IE1_DIS = 0, /*!< DIS : Disable counter/timer B7 from generating an interrupt + based on COMPR1. value. */ + CTIMER_CTRL7_TMRB7IE1_EN = 1, /*!< EN : Enable counter/timer B7 to generate an interrupt based + on COMPR1. value. */ +} CTIMER_CTRL7_TMRB7IE1_Enum; + +/* ============================================ CTIMER CTRL7 TMRB7IE0 [25..25] ============================================= */ +typedef enum { /*!< CTIMER_CTRL7_TMRB7IE0 */ + CTIMER_CTRL7_TMRB7IE0_DIS = 0, /*!< DIS : Disable counter/timer B7 from generating an interrupt + based on COMPR0. value. */ + CTIMER_CTRL7_TMRB7IE0_EN = 1, /*!< EN : Enable counter/timer B7 to generate an interrupt based + on COMPR0 value. */ +} CTIMER_CTRL7_TMRB7IE0_Enum; + +/* ============================================= CTIMER CTRL7 TMRB7FN [22..24] ============================================= */ +typedef enum { /*!< CTIMER_CTRL7_TMRB7FN */ + CTIMER_CTRL7_TMRB7FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count + to CMPR0B7, stop. value. */ + CTIMER_CTRL7_TMRB7FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide + pulses). Count to CMPR0B7, restart. value. */ + CTIMER_CTRL7_TMRB7FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0B7, assert, + count to CMPR1B7, deassert, stop. value. */ + CTIMER_CTRL7_TMRB7FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0B7, assert, count + to CMPR1B7, deassert, restart. value. */ + CTIMER_CTRL7_TMRB7FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ + CTIMER_CTRL7_TMRB7FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ + CTIMER_CTRL7_TMRB7FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. + value. */ + CTIMER_CTRL7_TMRB7FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ +} CTIMER_CTRL7_TMRB7FN_Enum; + +/* ============================================ CTIMER CTRL7 TMRB7CLK [17..21] ============================================= */ +typedef enum { /*!< CTIMER_CTRL7_TMRB7CLK */ + CTIMER_CTRL7_TMRB7CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINB. value. */ + CTIMER_CTRL7_TMRB7CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ + CTIMER_CTRL7_TMRB7CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ + CTIMER_CTRL7_TMRB7CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ + CTIMER_CTRL7_TMRB7CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ + CTIMER_CTRL7_TMRB7CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ + CTIMER_CTRL7_TMRB7CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ + CTIMER_CTRL7_TMRB7CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ + CTIMER_CTRL7_TMRB7CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ + CTIMER_CTRL7_TMRB7CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ + CTIMER_CTRL7_TMRB7CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ + CTIMER_CTRL7_TMRB7CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ + CTIMER_CTRL7_TMRB7CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ + CTIMER_CTRL7_TMRB7CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ + CTIMER_CTRL7_TMRB7CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. + value. */ + CTIMER_CTRL7_TMRB7CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ + CTIMER_CTRL7_TMRB7CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ + CTIMER_CTRL7_TMRB7CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ + CTIMER_CTRL7_TMRB7CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ + CTIMER_CTRL7_TMRB7CLK_CTMRA7 = 20, /*!< CTMRA7 : Clock source is CTIMERA7 OUT. value. */ + CTIMER_CTRL7_TMRB7CLK_CTMRA2 = 21, /*!< CTMRA2 : Clock source is CTIMERA2 OUT. value. */ + CTIMER_CTRL7_TMRB7CLK_CTMRB2 = 22, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ + CTIMER_CTRL7_TMRB7CLK_CTMRA0 = 23, /*!< CTMRA0 : Clock source is CTIMERA0 OUT. value. */ + CTIMER_CTRL7_TMRB7CLK_CTMRB0 = 24, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ + CTIMER_CTRL7_TMRB7CLK_CTMRB1 = 25, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ + CTIMER_CTRL7_TMRB7CLK_CTMRB3 = 26, /*!< CTMRB3 : Clock source is CTIMERB3 OUT. value. */ + CTIMER_CTRL7_TMRB7CLK_CTMRB4 = 27, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ + CTIMER_CTRL7_TMRB7CLK_CTMRB5 = 28, /*!< CTMRB5 : Clock source is CTIMERB5 OUT. value. */ + CTIMER_CTRL7_TMRB7CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ + CTIMER_CTRL7_TMRB7CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ + CTIMER_CTRL7_TMRB7CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ +} CTIMER_CTRL7_TMRB7CLK_Enum; + +/* ============================================= CTIMER CTRL7 TMRB7EN [16..16] ============================================= */ +typedef enum { /*!< CTIMER_CTRL7_TMRB7EN */ + CTIMER_CTRL7_TMRB7EN_DIS = 0, /*!< DIS : Counter/Timer B7 Disable. value. */ + CTIMER_CTRL7_TMRB7EN_EN = 1, /*!< EN : Counter/Timer B7 Enable. value. */ +} CTIMER_CTRL7_TMRB7EN_Enum; + +/* ============================================ CTIMER CTRL7 TMRA7POL [12..12] ============================================= */ +typedef enum { /*!< CTIMER_CTRL7_TMRA7POL */ + CTIMER_CTRL7_TMRA7POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINA7 pin is the same as the + timer output. value. */ + CTIMER_CTRL7_TMRA7POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINA7 pin is the inverse of + the timer output. value. */ +} CTIMER_CTRL7_TMRA7POL_Enum; + +/* ============================================ CTIMER CTRL7 TMRA7CLR [11..11] ============================================= */ +typedef enum { /*!< CTIMER_CTRL7_TMRA7CLR */ + CTIMER_CTRL7_TMRA7CLR_RUN = 0, /*!< RUN : Allow counter/timer A7 to run value. */ + CTIMER_CTRL7_TMRA7CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer A7 at 0x0000. value. */ +} CTIMER_CTRL7_TMRA7CLR_Enum; + +/* ============================================ CTIMER CTRL7 TMRA7IE1 [10..10] ============================================= */ +typedef enum { /*!< CTIMER_CTRL7_TMRA7IE1 */ + CTIMER_CTRL7_TMRA7IE1_DIS = 0, /*!< DIS : Disable counter/timer A7 from generating an interrupt + based on COMPR1. value. */ + CTIMER_CTRL7_TMRA7IE1_EN = 1, /*!< EN : Enable counter/timer A7 to generate an interrupt based + on COMPR1. value. */ +} CTIMER_CTRL7_TMRA7IE1_Enum; + +/* ============================================= CTIMER CTRL7 TMRA7IE0 [9..9] ============================================== */ +typedef enum { /*!< CTIMER_CTRL7_TMRA7IE0 */ + CTIMER_CTRL7_TMRA7IE0_DIS = 0, /*!< DIS : Disable counter/timer A7 from generating an interrupt + based on COMPR0. value. */ + CTIMER_CTRL7_TMRA7IE0_EN = 1, /*!< EN : Enable counter/timer A7 to generate an interrupt based + on COMPR0. value. */ +} CTIMER_CTRL7_TMRA7IE0_Enum; + +/* ============================================== CTIMER CTRL7 TMRA7FN [6..8] ============================================== */ +typedef enum { /*!< CTIMER_CTRL7_TMRA7FN */ + CTIMER_CTRL7_TMRA7FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count + to CMPR0A7, stop. value. */ + CTIMER_CTRL7_TMRA7FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide + pulses). Count to CMPR0A7, restart. value. */ + CTIMER_CTRL7_TMRA7FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0A7, assert, + count to CMPR1A7, deassert, stop. value. */ + CTIMER_CTRL7_TMRA7FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0A7, assert, count + to CMPR1A7, deassert, restart. value. */ + CTIMER_CTRL7_TMRA7FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ + CTIMER_CTRL7_TMRA7FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ + CTIMER_CTRL7_TMRA7FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. + value. */ + CTIMER_CTRL7_TMRA7FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ +} CTIMER_CTRL7_TMRA7FN_Enum; + +/* ============================================= CTIMER CTRL7 TMRA7CLK [1..5] ============================================== */ +typedef enum { /*!< CTIMER_CTRL7_TMRA7CLK */ + CTIMER_CTRL7_TMRA7CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINA. value. */ + CTIMER_CTRL7_TMRA7CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ + CTIMER_CTRL7_TMRA7CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ + CTIMER_CTRL7_TMRA7CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ + CTIMER_CTRL7_TMRA7CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ + CTIMER_CTRL7_TMRA7CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ + CTIMER_CTRL7_TMRA7CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ + CTIMER_CTRL7_TMRA7CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ + CTIMER_CTRL7_TMRA7CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ + CTIMER_CTRL7_TMRA7CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ + CTIMER_CTRL7_TMRA7CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ + CTIMER_CTRL7_TMRA7CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ + CTIMER_CTRL7_TMRA7CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ + CTIMER_CTRL7_TMRA7CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ + CTIMER_CTRL7_TMRA7CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. + value. */ + CTIMER_CTRL7_TMRA7CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ + CTIMER_CTRL7_TMRA7CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ + CTIMER_CTRL7_TMRA7CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ + CTIMER_CTRL7_TMRA7CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ + CTIMER_CTRL7_TMRA7CLK_CTMRB7 = 20, /*!< CTMRB7 : Clock source is CTIMERB7 OUT. value. */ + CTIMER_CTRL7_TMRA7CLK_CTMRA2 = 21, /*!< CTMRA2 : Clock source is CTIMERA2 OUT. value. */ + CTIMER_CTRL7_TMRA7CLK_CTMRB2 = 22, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ + CTIMER_CTRL7_TMRA7CLK_CTMRA0 = 23, /*!< CTMRA0 : Clock source is CTIMERA0 OUT. value. */ + CTIMER_CTRL7_TMRA7CLK_CTMRB0 = 24, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ + CTIMER_CTRL7_TMRA7CLK_CTMRB1 = 25, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ + CTIMER_CTRL7_TMRA7CLK_CTMRB3 = 26, /*!< CTMRB3 : Clock source is CTIMERB3 OUT. value. */ + CTIMER_CTRL7_TMRA7CLK_CTMRB4 = 27, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ + CTIMER_CTRL7_TMRA7CLK_CTMRB5 = 28, /*!< CTMRB5 : Clock source is CTIMERB5 OUT. value. */ + CTIMER_CTRL7_TMRA7CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ + CTIMER_CTRL7_TMRA7CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ + CTIMER_CTRL7_TMRA7CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ +} CTIMER_CTRL7_TMRA7CLK_Enum; + +/* ============================================== CTIMER CTRL7 TMRA7EN [0..0] ============================================== */ +typedef enum { /*!< CTIMER_CTRL7_TMRA7EN */ + CTIMER_CTRL7_TMRA7EN_DIS = 0, /*!< DIS : Counter/Timer A7 Disable. value. */ + CTIMER_CTRL7_TMRA7EN_EN = 1, /*!< EN : Counter/Timer A7 Enable. value. */ +} CTIMER_CTRL7_TMRA7EN_Enum; + +/* ======================================================= CMPRAUXA7 ======================================================= */ +/* ======================================================= CMPRAUXB7 ======================================================= */ +/* ========================================================= AUX7 ========================================================== */ +/* ============================================ CTIMER AUX7 TMRB7EN23 [30..30] ============================================= */ +typedef enum { /*!< CTIMER_AUX7_TMRB7EN23 */ + CTIMER_AUX7_TMRB7EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ + CTIMER_AUX7_TMRB7EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ +} CTIMER_AUX7_TMRB7EN23_Enum; + +/* ============================================ CTIMER AUX7 TMRB7POL23 [29..29] ============================================ */ +typedef enum { /*!< CTIMER_AUX7_TMRB7POL23 */ + CTIMER_AUX7_TMRB7POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ + CTIMER_AUX7_TMRB7POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ +} CTIMER_AUX7_TMRB7POL23_Enum; + +/* ============================================ CTIMER AUX7 TMRB7TINV [28..28] ============================================= */ +typedef enum { /*!< CTIMER_AUX7_TMRB7TINV */ + CTIMER_AUX7_TMRB7TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ + CTIMER_AUX7_TMRB7TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ +} CTIMER_AUX7_TMRB7TINV_Enum; + +/* =========================================== CTIMER AUX7 TMRB7NOSYNC [27..27] ============================================ */ +typedef enum { /*!< CTIMER_AUX7_TMRB7NOSYNC */ + CTIMER_AUX7_TMRB7NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ + CTIMER_AUX7_TMRB7NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ +} CTIMER_AUX7_TMRB7NOSYNC_Enum; + +/* ============================================ CTIMER AUX7 TMRB7TRIG [23..26] ============================================= */ +typedef enum { /*!< CTIMER_AUX7_TMRB7TRIG */ + CTIMER_AUX7_TMRB7TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ + CTIMER_AUX7_TMRB7TRIG_A7OUT = 1, /*!< A7OUT : Trigger source is CTIMERA7 OUT. value. */ + CTIMER_AUX7_TMRB7TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ + CTIMER_AUX7_TMRB7TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ + CTIMER_AUX7_TMRB7TRIG_A5OUT = 4, /*!< A5OUT : Trigger source is CTIMERA5 OUT. value. */ + CTIMER_AUX7_TMRB7TRIG_B5OUT = 5, /*!< B5OUT : Trigger source is CTIMERB5 OUT. value. */ + CTIMER_AUX7_TMRB7TRIG_A2OUT = 6, /*!< A2OUT : Trigger source is CTIMERA2 OUT. value. */ + CTIMER_AUX7_TMRB7TRIG_B2OUT = 7, /*!< B2OUT : Trigger source is CTIMERB2 OUT. value. */ + CTIMER_AUX7_TMRB7TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ + CTIMER_AUX7_TMRB7TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ + CTIMER_AUX7_TMRB7TRIG_A2OUT2 = 10, /*!< A2OUT2 : Trigger source is CTIMERA2 OUT2. value. */ + CTIMER_AUX7_TMRB7TRIG_B2OUT2 = 11, /*!< B2OUT2 : Trigger source is CTIMERB2 OUT2. value. */ + CTIMER_AUX7_TMRB7TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ + CTIMER_AUX7_TMRB7TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ + CTIMER_AUX7_TMRB7TRIG_B1OUT2DUAL = 14, /*!< B1OUT2DUAL : Trigger source is CTIMERB1 OUT2, dual edge. value. */ + CTIMER_AUX7_TMRB7TRIG_A1OUT2DUAL = 15, /*!< A1OUT2DUAL : Trigger source is CTIMERA1 OUT2, dual edge. value. */ +} CTIMER_AUX7_TMRB7TRIG_Enum; + +/* ============================================ CTIMER AUX7 TMRA7EN23 [14..14] ============================================= */ +typedef enum { /*!< CTIMER_AUX7_TMRA7EN23 */ + CTIMER_AUX7_TMRA7EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ + CTIMER_AUX7_TMRA7EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ +} CTIMER_AUX7_TMRA7EN23_Enum; + +/* ============================================ CTIMER AUX7 TMRA7POL23 [13..13] ============================================ */ +typedef enum { /*!< CTIMER_AUX7_TMRA7POL23 */ + CTIMER_AUX7_TMRA7POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ + CTIMER_AUX7_TMRA7POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ +} CTIMER_AUX7_TMRA7POL23_Enum; + +/* ============================================ CTIMER AUX7 TMRA7TINV [12..12] ============================================= */ +typedef enum { /*!< CTIMER_AUX7_TMRA7TINV */ + CTIMER_AUX7_TMRA7TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ + CTIMER_AUX7_TMRA7TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ +} CTIMER_AUX7_TMRA7TINV_Enum; + +/* =========================================== CTIMER AUX7 TMRA7NOSYNC [11..11] ============================================ */ +typedef enum { /*!< CTIMER_AUX7_TMRA7NOSYNC */ + CTIMER_AUX7_TMRA7NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ + CTIMER_AUX7_TMRA7NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ +} CTIMER_AUX7_TMRA7NOSYNC_Enum; + +/* ============================================= CTIMER AUX7 TMRA7TRIG [7..10] ============================================= */ +typedef enum { /*!< CTIMER_AUX7_TMRA7TRIG */ + CTIMER_AUX7_TMRA7TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ + CTIMER_AUX7_TMRA7TRIG_B7OUT = 1, /*!< B7OUT : Trigger source is CTIMERB7 OUT. value. */ + CTIMER_AUX7_TMRA7TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ + CTIMER_AUX7_TMRA7TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ + CTIMER_AUX7_TMRA7TRIG_A1OUT = 4, /*!< A1OUT : Trigger source is CTIMERA1 OUT. value. */ + CTIMER_AUX7_TMRA7TRIG_B1OUT = 5, /*!< B1OUT : Trigger source is CTIMERB1 OUT. value. */ + CTIMER_AUX7_TMRA7TRIG_A4OUT = 6, /*!< A4OUT : Trigger source is CTIMERA4 OUT. value. */ + CTIMER_AUX7_TMRA7TRIG_B4OUT = 7, /*!< B4OUT : Trigger source is CTIMERB4 OUT. value. */ + CTIMER_AUX7_TMRA7TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ + CTIMER_AUX7_TMRA7TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ + CTIMER_AUX7_TMRA7TRIG_A2OUT2 = 10, /*!< A2OUT2 : Trigger source is CTIMERA2 OUT2. value. */ + CTIMER_AUX7_TMRA7TRIG_B2OUT2 = 11, /*!< B2OUT2 : Trigger source is CTIMERB2 OUT2. value. */ + CTIMER_AUX7_TMRA7TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ + CTIMER_AUX7_TMRA7TRIG_A5OUT2DUAL = 13, /*!< A5OUT2DUAL : Trigger source is CTIMERA5 OUT2, dual edge. value. */ + CTIMER_AUX7_TMRA7TRIG_B4OUT2DUAL = 14, /*!< B4OUT2DUAL : Trigger source is CTIMERB4 OUT2, dual edge. value. */ + CTIMER_AUX7_TMRA7TRIG_A4OUT2DUAL = 15, /*!< A4OUT2DUAL : Trigger source is CTIMERA4 OUT2, dual edge. value. */ +} CTIMER_AUX7_TMRA7TRIG_Enum; + +/* ======================================================== GLOBEN ========================================================= */ +/* ============================================== CTIMER GLOBEN ENB7 [15..15] ============================================== */ +typedef enum { /*!< CTIMER_GLOBEN_ENB7 */ + CTIMER_GLOBEN_ENB7_LCO = 1, /*!< LCO : Use local enable. value. */ + CTIMER_GLOBEN_ENB7_DIS = 0, /*!< DIS : Disable CTIMER. value. */ +} CTIMER_GLOBEN_ENB7_Enum; + +/* ============================================== CTIMER GLOBEN ENA7 [14..14] ============================================== */ +typedef enum { /*!< CTIMER_GLOBEN_ENA7 */ + CTIMER_GLOBEN_ENA7_LCO = 1, /*!< LCO : Use local enable. value. */ + CTIMER_GLOBEN_ENA7_DIS = 0, /*!< DIS : Disable CTIMER. value. */ +} CTIMER_GLOBEN_ENA7_Enum; + +/* ============================================== CTIMER GLOBEN ENB6 [13..13] ============================================== */ +typedef enum { /*!< CTIMER_GLOBEN_ENB6 */ + CTIMER_GLOBEN_ENB6_LCO = 1, /*!< LCO : Use local enable. value. */ + CTIMER_GLOBEN_ENB6_DIS = 0, /*!< DIS : Disable CTIMER. value. */ +} CTIMER_GLOBEN_ENB6_Enum; + +/* ============================================== CTIMER GLOBEN ENA6 [12..12] ============================================== */ +typedef enum { /*!< CTIMER_GLOBEN_ENA6 */ + CTIMER_GLOBEN_ENA6_LCO = 1, /*!< LCO : Use local enable. value. */ + CTIMER_GLOBEN_ENA6_DIS = 0, /*!< DIS : Disable CTIMER. value. */ +} CTIMER_GLOBEN_ENA6_Enum; + +/* ============================================== CTIMER GLOBEN ENB5 [11..11] ============================================== */ +typedef enum { /*!< CTIMER_GLOBEN_ENB5 */ + CTIMER_GLOBEN_ENB5_LCO = 1, /*!< LCO : Use local enable. value. */ + CTIMER_GLOBEN_ENB5_DIS = 0, /*!< DIS : Disable CTIMER. value. */ +} CTIMER_GLOBEN_ENB5_Enum; + +/* ============================================== CTIMER GLOBEN ENA5 [10..10] ============================================== */ +typedef enum { /*!< CTIMER_GLOBEN_ENA5 */ + CTIMER_GLOBEN_ENA5_LCO = 1, /*!< LCO : Use local enable. value. */ + CTIMER_GLOBEN_ENA5_DIS = 0, /*!< DIS : Disable CTIMER. value. */ +} CTIMER_GLOBEN_ENA5_Enum; + +/* =============================================== CTIMER GLOBEN ENB4 [9..9] =============================================== */ +typedef enum { /*!< CTIMER_GLOBEN_ENB4 */ + CTIMER_GLOBEN_ENB4_LCO = 1, /*!< LCO : Use local enable. value. */ + CTIMER_GLOBEN_ENB4_DIS = 0, /*!< DIS : Disable CTIMER. value. */ +} CTIMER_GLOBEN_ENB4_Enum; + +/* =============================================== CTIMER GLOBEN ENA4 [8..8] =============================================== */ +typedef enum { /*!< CTIMER_GLOBEN_ENA4 */ + CTIMER_GLOBEN_ENA4_LCO = 1, /*!< LCO : Use local enable. value. */ + CTIMER_GLOBEN_ENA4_DIS = 0, /*!< DIS : Disable CTIMER. value. */ +} CTIMER_GLOBEN_ENA4_Enum; + +/* =============================================== CTIMER GLOBEN ENB3 [7..7] =============================================== */ +typedef enum { /*!< CTIMER_GLOBEN_ENB3 */ + CTIMER_GLOBEN_ENB3_LCO = 1, /*!< LCO : Use local enable. value. */ + CTIMER_GLOBEN_ENB3_DIS = 0, /*!< DIS : Disable CTIMER. value. */ +} CTIMER_GLOBEN_ENB3_Enum; + +/* =============================================== CTIMER GLOBEN ENA3 [6..6] =============================================== */ +typedef enum { /*!< CTIMER_GLOBEN_ENA3 */ + CTIMER_GLOBEN_ENA3_LCO = 1, /*!< LCO : Use local enable. value. */ + CTIMER_GLOBEN_ENA3_DIS = 0, /*!< DIS : Disable CTIMER. value. */ +} CTIMER_GLOBEN_ENA3_Enum; + +/* =============================================== CTIMER GLOBEN ENB2 [5..5] =============================================== */ +typedef enum { /*!< CTIMER_GLOBEN_ENB2 */ + CTIMER_GLOBEN_ENB2_LCO = 1, /*!< LCO : Use local enable. value. */ + CTIMER_GLOBEN_ENB2_DIS = 0, /*!< DIS : Disable CTIMER. value. */ +} CTIMER_GLOBEN_ENB2_Enum; + +/* =============================================== CTIMER GLOBEN ENA2 [4..4] =============================================== */ +typedef enum { /*!< CTIMER_GLOBEN_ENA2 */ + CTIMER_GLOBEN_ENA2_LCO = 1, /*!< LCO : Use local enable. value. */ + CTIMER_GLOBEN_ENA2_DIS = 0, /*!< DIS : Disable CTIMER. value. */ +} CTIMER_GLOBEN_ENA2_Enum; + +/* =============================================== CTIMER GLOBEN ENB1 [3..3] =============================================== */ +typedef enum { /*!< CTIMER_GLOBEN_ENB1 */ + CTIMER_GLOBEN_ENB1_LCO = 1, /*!< LCO : Use local enable. value. */ + CTIMER_GLOBEN_ENB1_DIS = 0, /*!< DIS : Disable CTIMER. value. */ +} CTIMER_GLOBEN_ENB1_Enum; + +/* =============================================== CTIMER GLOBEN ENA1 [2..2] =============================================== */ +typedef enum { /*!< CTIMER_GLOBEN_ENA1 */ + CTIMER_GLOBEN_ENA1_LCO = 1, /*!< LCO : Use local enable. value. */ + CTIMER_GLOBEN_ENA1_DIS = 0, /*!< DIS : Disable CTIMER. value. */ +} CTIMER_GLOBEN_ENA1_Enum; + +/* =============================================== CTIMER GLOBEN ENB0 [1..1] =============================================== */ +typedef enum { /*!< CTIMER_GLOBEN_ENB0 */ + CTIMER_GLOBEN_ENB0_LCO = 1, /*!< LCO : Use local enable. value. */ + CTIMER_GLOBEN_ENB0_DIS = 0, /*!< DIS : Disable CTIMER. value. */ +} CTIMER_GLOBEN_ENB0_Enum; + +/* =============================================== CTIMER GLOBEN ENA0 [0..0] =============================================== */ +typedef enum { /*!< CTIMER_GLOBEN_ENA0 */ + CTIMER_GLOBEN_ENA0_LCO = 1, /*!< LCO : Use local enable. value. */ + CTIMER_GLOBEN_ENA0_DIS = 0, /*!< DIS : Disable CTIMER. value. */ +} CTIMER_GLOBEN_ENA0_Enum; + +/* ======================================================== OUTCFG0 ======================================================== */ +/* ============================================= CTIMER OUTCFG0 CFG9 [28..30] ============================================== */ +typedef enum { /*!< CTIMER_OUTCFG0_CFG9 */ + CTIMER_OUTCFG0_CFG9_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG0_CFG9_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG0_CFG9_B0OUT = 5, /*!< B0OUT : Output is B0OUT. value. */ + CTIMER_OUTCFG0_CFG9_A4OUT = 4, /*!< A4OUT : Output is A4OUT. value. */ + CTIMER_OUTCFG0_CFG9_A2OUT = 3, /*!< A2OUT : Output is A2OUT. value. */ + CTIMER_OUTCFG0_CFG9_A2OUT2 = 2, /*!< A2OUT2 : Output is A2OUT2 value. */ + CTIMER_OUTCFG0_CFG9_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG0_CFG9_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG0_CFG9_Enum; + +/* ============================================= CTIMER OUTCFG0 CFG8 [25..27] ============================================== */ +typedef enum { /*!< CTIMER_OUTCFG0_CFG8 */ + CTIMER_OUTCFG0_CFG8_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG0_CFG8_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG0_CFG8_B6OUT = 5, /*!< B6OUT : Output is B6OUT. value. */ + CTIMER_OUTCFG0_CFG8_A4OUT2 = 4, /*!< A4OUT2 : Output is A4OUT2. value. */ + CTIMER_OUTCFG0_CFG8_A3OUT2 = 3, /*!< A3OUT2 : Output is A3OUT. value. */ + CTIMER_OUTCFG0_CFG8_A2OUT = 2, /*!< A2OUT : Output is A2OUT value. */ + CTIMER_OUTCFG0_CFG8_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG0_CFG8_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG0_CFG8_Enum; + +/* ============================================= CTIMER OUTCFG0 CFG7 [22..24] ============================================== */ +typedef enum { /*!< CTIMER_OUTCFG0_CFG7 */ + CTIMER_OUTCFG0_CFG7_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG0_CFG7_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG0_CFG7_A7OUT = 5, /*!< A7OUT : Output is A7OUT. value. */ + CTIMER_OUTCFG0_CFG7_B5OUT = 4, /*!< B5OUT : Output is B5OUT. value. */ + CTIMER_OUTCFG0_CFG7_B1OUT = 3, /*!< B1OUT : Output is B1OUT. value. */ + CTIMER_OUTCFG0_CFG7_B1OUT2 = 2, /*!< B1OUT2 : Output is B1OUT2 value. */ + CTIMER_OUTCFG0_CFG7_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG0_CFG7_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG0_CFG7_Enum; + +/* ============================================= CTIMER OUTCFG0 CFG6 [19..21] ============================================== */ +typedef enum { /*!< CTIMER_OUTCFG0_CFG6 */ + CTIMER_OUTCFG0_CFG6_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG0_CFG6_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG0_CFG6_B7OUT = 5, /*!< B7OUT : Output is B7OUT. value. */ + CTIMER_OUTCFG0_CFG6_B5OUT2 = 4, /*!< B5OUT2 : Output is B5OUT2. value. */ + CTIMER_OUTCFG0_CFG6_A1OUT = 3, /*!< A1OUT : Output is A1OUT. value. */ + CTIMER_OUTCFG0_CFG6_B1OUT = 2, /*!< B1OUT : Output is B1OUT value. */ + CTIMER_OUTCFG0_CFG6_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG0_CFG6_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG0_CFG6_Enum; + +/* ============================================= CTIMER OUTCFG0 CFG5 [16..18] ============================================== */ +typedef enum { /*!< CTIMER_OUTCFG0_CFG5 */ + CTIMER_OUTCFG0_CFG5_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG0_CFG5_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG0_CFG5_A7OUT = 5, /*!< A7OUT : Output is A7OUT. value. */ + CTIMER_OUTCFG0_CFG5_B6OUT = 4, /*!< B6OUT : Output is A5OUT. value. */ + CTIMER_OUTCFG0_CFG5_A1OUT = 3, /*!< A1OUT : Output is A1OUT. value. */ + CTIMER_OUTCFG0_CFG5_A1OUT2 = 2, /*!< A1OUT2 : Output is A1OUT2 value. */ + CTIMER_OUTCFG0_CFG5_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG0_CFG5_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG0_CFG5_Enum; + +/* ============================================= CTIMER OUTCFG0 CFG4 [12..14] ============================================== */ +typedef enum { /*!< CTIMER_OUTCFG0_CFG4 */ + CTIMER_OUTCFG0_CFG4_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG0_CFG4_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG0_CFG4_B5OUT = 5, /*!< B5OUT : Output is B5OUT. value. */ + CTIMER_OUTCFG0_CFG4_A5OUT2 = 4, /*!< A5OUT2 : Output is A5OUT2. value. */ + CTIMER_OUTCFG0_CFG4_A2OUT2 = 3, /*!< A2OUT2 : Output is A2OUT2. value. */ + CTIMER_OUTCFG0_CFG4_A1OUT = 2, /*!< A1OUT : Output is A1OUT value. */ + CTIMER_OUTCFG0_CFG4_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG0_CFG4_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG0_CFG4_Enum; + +/* ============================================== CTIMER OUTCFG0 CFG3 [9..11] ============================================== */ +typedef enum { /*!< CTIMER_OUTCFG0_CFG3 */ + CTIMER_OUTCFG0_CFG3_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG0_CFG3_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG0_CFG3_A6OUT = 5, /*!< A6OUT : Output is A6OUT. value. */ + CTIMER_OUTCFG0_CFG3_A1OUT = 4, /*!< A1OUT : Output is A1OUT. value. */ + CTIMER_OUTCFG0_CFG3_B0OUT = 3, /*!< B0OUT : Output is B0OUT. value. */ + CTIMER_OUTCFG0_CFG3_B0OUT2 = 2, /*!< B0OUT2 : Output is B0OUT2 value. */ + CTIMER_OUTCFG0_CFG3_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG0_CFG3_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG0_CFG3_Enum; + +/* ============================================== CTIMER OUTCFG0 CFG2 [6..8] =============================================== */ +typedef enum { /*!< CTIMER_OUTCFG0_CFG2 */ + CTIMER_OUTCFG0_CFG2_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG0_CFG2_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG0_CFG2_A7OUT = 5, /*!< A7OUT : Output is A7OUT. value. */ + CTIMER_OUTCFG0_CFG2_B6OUT2 = 4, /*!< B6OUT2 : Output is B6OUT2. value. */ + CTIMER_OUTCFG0_CFG2_B1OUT2 = 3, /*!< B1OUT2 : Output is B1OUT2. value. */ + CTIMER_OUTCFG0_CFG2_B0OUT = 2, /*!< B0OUT : Output is B0OUT value. */ + CTIMER_OUTCFG0_CFG2_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG0_CFG2_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG0_CFG2_Enum; + +/* ============================================== CTIMER OUTCFG0 CFG1 [3..5] =============================================== */ +typedef enum { /*!< CTIMER_OUTCFG0_CFG1 */ + CTIMER_OUTCFG0_CFG1_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG0_CFG1_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG0_CFG1_B7OUT2 = 5, /*!< B7OUT2 : Output is B7OUT2. value. */ + CTIMER_OUTCFG0_CFG1_A5OUT = 4, /*!< A5OUT : Output is A5OUT. value. */ + CTIMER_OUTCFG0_CFG1_A0OUT = 3, /*!< A0OUT : Output is A0OUT. value. */ + CTIMER_OUTCFG0_CFG1_A0OUT2 = 2, /*!< A0OUT2 : Output is A0OUT2 value. */ + CTIMER_OUTCFG0_CFG1_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG0_CFG1_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG0_CFG1_Enum; + +/* ============================================== CTIMER OUTCFG0 CFG0 [0..2] =============================================== */ +typedef enum { /*!< CTIMER_OUTCFG0_CFG0 */ + CTIMER_OUTCFG0_CFG0_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG0_CFG0_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG0_CFG0_A6OUT = 5, /*!< A6OUT : Output is A6OUT. value. */ + CTIMER_OUTCFG0_CFG0_A5OUT2 = 4, /*!< A5OUT2 : Output is A5OUT2. value. */ + CTIMER_OUTCFG0_CFG0_B2OUT2 = 3, /*!< B2OUT2 : Output is B2OUT2. value. */ + CTIMER_OUTCFG0_CFG0_A0OUT = 2, /*!< A0OUT : Output is A0OUT value. */ + CTIMER_OUTCFG0_CFG0_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG0_CFG0_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG0_CFG0_Enum; + +/* ======================================================== OUTCFG1 ======================================================== */ +/* ============================================= CTIMER OUTCFG1 CFG19 [28..30] ============================================= */ +typedef enum { /*!< CTIMER_OUTCFG1_CFG19 */ + CTIMER_OUTCFG1_CFG19_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG1_CFG19_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG1_CFG19_B1OUT2 = 5, /*!< B1OUT2 : Output is B1OUT2. value. */ + CTIMER_OUTCFG1_CFG19_B4OUT = 4, /*!< B4OUT : Output is B4OUT. value. */ + CTIMER_OUTCFG1_CFG19_A2OUT = 3, /*!< A2OUT : Output is A2OUT. value. */ + CTIMER_OUTCFG1_CFG19_B4OUT2 = 2, /*!< B4OUT2 : Output is B4OUT2 value. */ + CTIMER_OUTCFG1_CFG19_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG1_CFG19_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG1_CFG19_Enum; + +/* ============================================= CTIMER OUTCFG1 CFG18 [25..27] ============================================= */ +typedef enum { /*!< CTIMER_OUTCFG1_CFG18 */ + CTIMER_OUTCFG1_CFG18_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG1_CFG18_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG1_CFG18_A3OUT2 = 5, /*!< A3OUT2 : Output is A3OUT2. value. */ + CTIMER_OUTCFG1_CFG18_A0OUT = 4, /*!< A0OUT : Output is A0OUT. value. */ + CTIMER_OUTCFG1_CFG18_B0OUT = 3, /*!< B0OUT : Output is B0OUT. value. */ + CTIMER_OUTCFG1_CFG18_B4OUT = 2, /*!< B4OUT : Output is B4OUT value. */ + CTIMER_OUTCFG1_CFG18_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG1_CFG18_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG1_CFG18_Enum; + +/* ============================================= CTIMER OUTCFG1 CFG17 [22..24] ============================================= */ +typedef enum { /*!< CTIMER_OUTCFG1_CFG17 */ + CTIMER_OUTCFG1_CFG17_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG1_CFG17_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG1_CFG17_A1OUT2 = 5, /*!< A1OUT2 : Output is A1OUT2. value. */ + CTIMER_OUTCFG1_CFG17_A4OUT = 4, /*!< A4OUT : Output is A4OUT. value. */ + CTIMER_OUTCFG1_CFG17_B7OUT = 3, /*!< B7OUT : Output is B7OUT. value. */ + CTIMER_OUTCFG1_CFG17_A4OUT2 = 2, /*!< A4OUT2 : Output is A4OUT2 value. */ + CTIMER_OUTCFG1_CFG17_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG1_CFG17_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG1_CFG17_Enum; + +/* ============================================= CTIMER OUTCFG1 CFG16 [19..21] ============================================= */ +typedef enum { /*!< CTIMER_OUTCFG1_CFG16 */ + CTIMER_OUTCFG1_CFG16_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG1_CFG16_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG1_CFG16_B3OUT2 = 5, /*!< B3OUT2 : Output is B3OUT2. value. */ + CTIMER_OUTCFG1_CFG16_A0OUT2 = 4, /*!< A0OUT2 : Output is A0OUT2. value. */ + CTIMER_OUTCFG1_CFG16_A0OUT = 3, /*!< A0OUT : Output is A0OUT. value. */ + CTIMER_OUTCFG1_CFG16_A4OUT = 2, /*!< A4OUT : Output is A4OUT value. */ + CTIMER_OUTCFG1_CFG16_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG1_CFG16_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG1_CFG16_Enum; + +/* ============================================= CTIMER OUTCFG1 CFG15 [16..18] ============================================= */ +typedef enum { /*!< CTIMER_OUTCFG1_CFG15 */ + CTIMER_OUTCFG1_CFG15_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG1_CFG15_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG1_CFG15_A4OUT2 = 5, /*!< A4OUT2 : Output is A4OUT2. value. */ + CTIMER_OUTCFG1_CFG15_A7OUT = 4, /*!< A7OUT : Output is B1OUT. value. */ + CTIMER_OUTCFG1_CFG15_B3OUT = 3, /*!< B3OUT : Output is B3OUT. value. */ + CTIMER_OUTCFG1_CFG15_B3OUT2 = 2, /*!< B3OUT2 : Output is B3OUT2 value. */ + CTIMER_OUTCFG1_CFG15_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG1_CFG15_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG1_CFG15_Enum; + +/* ============================================= CTIMER OUTCFG1 CFG14 [12..14] ============================================= */ +typedef enum { /*!< CTIMER_OUTCFG1_CFG14 */ + CTIMER_OUTCFG1_CFG14_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG1_CFG14_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG1_CFG14_A7OUT = 5, /*!< A7OUT : Output is A7OUT. value. */ + CTIMER_OUTCFG1_CFG14_B7OUT2 = 4, /*!< B7OUT2 : Output is B7OUT2. value. */ + CTIMER_OUTCFG1_CFG14_B1OUT = 3, /*!< B1OUT : Output is B1OUT. value. */ + CTIMER_OUTCFG1_CFG14_B3OUT = 2, /*!< B3OUT : Output is B3OUT value. */ + CTIMER_OUTCFG1_CFG14_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG1_CFG14_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG1_CFG14_Enum; + +/* ============================================= CTIMER OUTCFG1 CFG13 [9..11] ============================================== */ +typedef enum { /*!< CTIMER_OUTCFG1_CFG13 */ + CTIMER_OUTCFG1_CFG13_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG1_CFG13_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG1_CFG13_B4OUT2 = 5, /*!< B4OUT2 : Output is B4OUT2. value. */ + CTIMER_OUTCFG1_CFG13_A6OUT = 4, /*!< A6OUT : Output is B1OUT. value. */ + CTIMER_OUTCFG1_CFG13_A3OUT = 3, /*!< A3OUT : Output is A3OUT. value. */ + CTIMER_OUTCFG1_CFG13_A3OUT2 = 2, /*!< A3OUT2 : Output is A3OUT2 value. */ + CTIMER_OUTCFG1_CFG13_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG1_CFG13_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG1_CFG13_Enum; + +/* ============================================== CTIMER OUTCFG1 CFG12 [6..8] ============================================== */ +typedef enum { /*!< CTIMER_OUTCFG1_CFG12 */ + CTIMER_OUTCFG1_CFG12_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG1_CFG12_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG1_CFG12_B6OUT2 = 5, /*!< B6OUT2 : Output is B6OUT2. value. */ + CTIMER_OUTCFG1_CFG12_B0OUT2 = 4, /*!< B0OUT2 : Output is B0OUT2. value. */ + CTIMER_OUTCFG1_CFG12_B1OUT = 3, /*!< B1OUT : Output is B1OUT. value. */ + CTIMER_OUTCFG1_CFG12_A3OUT = 2, /*!< A3OUT : Output is A3OUT value. */ + CTIMER_OUTCFG1_CFG12_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG1_CFG12_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG1_CFG12_Enum; + +/* ============================================== CTIMER OUTCFG1 CFG11 [3..5] ============================================== */ +typedef enum { /*!< CTIMER_OUTCFG1_CFG11 */ + CTIMER_OUTCFG1_CFG11_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG1_CFG11_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG1_CFG11_B5OUT2 = 5, /*!< B5OUT2 : Output is B5OUT2. value. */ + CTIMER_OUTCFG1_CFG11_B4OUT = 4, /*!< B4OUT : Output is B4OUT. value. */ + CTIMER_OUTCFG1_CFG11_B2OUT = 3, /*!< B2OUT : Output is B2OUT. value. */ + CTIMER_OUTCFG1_CFG11_B2OUT2 = 2, /*!< B2OUT2 : Output is B2OUT2 value. */ + CTIMER_OUTCFG1_CFG11_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG1_CFG11_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG1_CFG11_Enum; + +/* ============================================== CTIMER OUTCFG1 CFG10 [0..2] ============================================== */ +typedef enum { /*!< CTIMER_OUTCFG1_CFG10 */ + CTIMER_OUTCFG1_CFG10_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG1_CFG10_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG1_CFG10_A6OUT = 5, /*!< A6OUT : Output is A6OUT. value. */ + CTIMER_OUTCFG1_CFG10_B4OUT2 = 4, /*!< B4OUT2 : Output is B4OUT2. value. */ + CTIMER_OUTCFG1_CFG10_B3OUT2 = 3, /*!< B3OUT2 : Output is B3OUT2. value. */ + CTIMER_OUTCFG1_CFG10_B2OUT = 2, /*!< B2OUT : Output is B2OUT value. */ + CTIMER_OUTCFG1_CFG10_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG1_CFG10_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG1_CFG10_Enum; + +/* ======================================================== OUTCFG2 ======================================================== */ +/* ============================================= CTIMER OUTCFG2 CFG29 [28..30] ============================================= */ +typedef enum { /*!< CTIMER_OUTCFG2_CFG29 */ + CTIMER_OUTCFG2_CFG29_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG2_CFG29_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG2_CFG29_A3OUT2 = 5, /*!< A3OUT2 : Output is A3OUT2. value. */ + CTIMER_OUTCFG2_CFG29_A7OUT = 4, /*!< A7OUT : Output is A7OUT. value. */ + CTIMER_OUTCFG2_CFG29_A1OUT = 3, /*!< A1OUT : Output is A1OUT. value. */ + CTIMER_OUTCFG2_CFG29_B5OUT2 = 2, /*!< B5OUT2 : Output is B5OUT2 value. */ + CTIMER_OUTCFG2_CFG29_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG2_CFG29_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG2_CFG29_Enum; + +/* ============================================= CTIMER OUTCFG2 CFG28 [25..27] ============================================= */ +typedef enum { /*!< CTIMER_OUTCFG2_CFG28 */ + CTIMER_OUTCFG2_CFG28_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG2_CFG28_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG2_CFG28_B0OUT2 = 5, /*!< B0OUT2 : Output is B0OUT2. value. */ + CTIMER_OUTCFG2_CFG28_A5OUT2 = 4, /*!< A5OUT2 : Output is A5OUT2. value. */ + CTIMER_OUTCFG2_CFG28_A3OUT = 3, /*!< A3OUT : Output is A3OUT. value. */ + CTIMER_OUTCFG2_CFG28_A7OUT = 2, /*!< A7OUT : Output is A7OUT value. */ + CTIMER_OUTCFG2_CFG28_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG2_CFG28_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG2_CFG28_Enum; + +/* ============================================= CTIMER OUTCFG2 CFG27 [22..24] ============================================= */ +typedef enum { /*!< CTIMER_OUTCFG2_CFG27 */ + CTIMER_OUTCFG2_CFG27_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG2_CFG27_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG2_CFG27_B2OUT2 = 5, /*!< B2OUT2 : Output is B2OUT2. value. */ + CTIMER_OUTCFG2_CFG27_B6OUT = 4, /*!< B6OUT : Output is B6OUT. value. */ + CTIMER_OUTCFG2_CFG27_A1OUT = 3, /*!< A1OUT : Output is A1OUT. value. */ + CTIMER_OUTCFG2_CFG27_B6OUT2 = 2, /*!< B6OUT2 : Output is B6OUT2 value. */ + CTIMER_OUTCFG2_CFG27_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG2_CFG27_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG2_CFG27_Enum; + +/* ============================================= CTIMER OUTCFG2 CFG26 [19..21] ============================================= */ +typedef enum { /*!< CTIMER_OUTCFG2_CFG26 */ + CTIMER_OUTCFG2_CFG26_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG2_CFG26_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG2_CFG26_A1OUT2 = 5, /*!< A1OUT2 : Output is A1OUT2. value. */ + CTIMER_OUTCFG2_CFG26_A5OUT = 4, /*!< A5OUT : Output is A5OUT. value. */ + CTIMER_OUTCFG2_CFG26_B2OUT = 3, /*!< B2OUT : Output is B2OUT. value. */ + CTIMER_OUTCFG2_CFG26_B6OUT = 2, /*!< B6OUT : Output is B6OUT value. */ + CTIMER_OUTCFG2_CFG26_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG2_CFG26_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG2_CFG26_Enum; + +/* ============================================= CTIMER OUTCFG2 CFG25 [16..18] ============================================= */ +typedef enum { /*!< CTIMER_OUTCFG2_CFG25 */ + CTIMER_OUTCFG2_CFG25_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG2_CFG25_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG2_CFG25_A2OUT2 = 5, /*!< A2OUT2 : Output is A2OUT2. value. */ + CTIMER_OUTCFG2_CFG25_A6OUT = 4, /*!< A6OUT : Output is A6OUT. value. */ + CTIMER_OUTCFG2_CFG25_B2OUT = 3, /*!< B2OUT : Output is B2OUT. value. */ + CTIMER_OUTCFG2_CFG25_B4OUT2 = 2, /*!< B4OUT2 : Output is B4OUT2 value. */ + CTIMER_OUTCFG2_CFG25_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG2_CFG25_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG2_CFG25_Enum; + +/* ============================================= CTIMER OUTCFG2 CFG24 [12..14] ============================================= */ +typedef enum { /*!< CTIMER_OUTCFG2_CFG24 */ + CTIMER_OUTCFG2_CFG24_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG2_CFG24_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG2_CFG24_B1OUT2 = 5, /*!< B1OUT2 : Output is B1OUT2. value. */ + CTIMER_OUTCFG2_CFG24_A1OUT = 4, /*!< A1OUT : Output is A1OUT. value. */ + CTIMER_OUTCFG2_CFG24_A2OUT = 3, /*!< A2OUT : Output is A2OUT. value. */ + CTIMER_OUTCFG2_CFG24_A6OUT = 2, /*!< A6OUT : Output is A6OUT value. */ + CTIMER_OUTCFG2_CFG24_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG2_CFG24_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG2_CFG24_Enum; + +/* ============================================= CTIMER OUTCFG2 CFG23 [9..11] ============================================== */ +typedef enum { /*!< CTIMER_OUTCFG2_CFG23 */ + CTIMER_OUTCFG2_CFG23_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG2_CFG23_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG2_CFG23_B0OUT2 = 5, /*!< B0OUT2 : Output is B0OUT2. value. */ + CTIMER_OUTCFG2_CFG23_A5OUT = 4, /*!< A5OUT : Output is A5OUT. value. */ + CTIMER_OUTCFG2_CFG23_A7OUT = 3, /*!< A7OUT : Output is B1OUT. value. */ + CTIMER_OUTCFG2_CFG23_B5OUT2 = 2, /*!< B5OUT2 : Output is B5OUT2 value. */ + CTIMER_OUTCFG2_CFG23_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG2_CFG23_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG2_CFG23_Enum; + +/* ============================================== CTIMER OUTCFG2 CFG22 [6..8] ============================================== */ +typedef enum { /*!< CTIMER_OUTCFG2_CFG22 */ + CTIMER_OUTCFG2_CFG22_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG2_CFG22_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG2_CFG22_A2OUT2 = 5, /*!< A2OUT2 : Output is A2OUT2. value. */ + CTIMER_OUTCFG2_CFG22_A1OUT = 4, /*!< A1OUT : Output is A1OUT. value. */ + CTIMER_OUTCFG2_CFG22_A6OUT = 3, /*!< A6OUT : Output is B1OUT. value. */ + CTIMER_OUTCFG2_CFG22_B5OUT = 2, /*!< B5OUT : Output is B5OUT value. */ + CTIMER_OUTCFG2_CFG22_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG2_CFG22_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG2_CFG22_Enum; + +/* ============================================== CTIMER OUTCFG2 CFG21 [3..5] ============================================== */ +typedef enum { /*!< CTIMER_OUTCFG2_CFG21 */ + CTIMER_OUTCFG2_CFG21_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG2_CFG21_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG2_CFG21_A0OUT2 = 5, /*!< A0OUT2 : Output is A0OUT2. value. */ + CTIMER_OUTCFG2_CFG21_B5OUT = 4, /*!< B5OUT : Output is B5OUT. value. */ + CTIMER_OUTCFG2_CFG21_A1OUT = 3, /*!< A1OUT : Output is A1OUT. value. */ + CTIMER_OUTCFG2_CFG21_A5OUT2 = 2, /*!< A5OUT2 : Output is A5OUT2 value. */ + CTIMER_OUTCFG2_CFG21_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG2_CFG21_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG2_CFG21_Enum; + +/* ============================================== CTIMER OUTCFG2 CFG20 [0..2] ============================================== */ +typedef enum { /*!< CTIMER_OUTCFG2_CFG20 */ + CTIMER_OUTCFG2_CFG20_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG2_CFG20_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG2_CFG20_B2OUT2 = 5, /*!< B2OUT2 : Output is B2OUT2. value. */ + CTIMER_OUTCFG2_CFG20_A1OUT2 = 4, /*!< A1OUT2 : Output is A1OUT2. value. */ + CTIMER_OUTCFG2_CFG20_A1OUT = 3, /*!< A1OUT : Output is A1OUT. value. */ + CTIMER_OUTCFG2_CFG20_A5OUT = 2, /*!< A5OUT : Output is A5OUT value. */ + CTIMER_OUTCFG2_CFG20_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG2_CFG20_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG2_CFG20_Enum; + +/* ======================================================== OUTCFG3 ======================================================== */ +/* ============================================== CTIMER OUTCFG3 CFG31 [3..5] ============================================== */ +typedef enum { /*!< CTIMER_OUTCFG3_CFG31 */ + CTIMER_OUTCFG3_CFG31_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG3_CFG31_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG3_CFG31_B3OUT2 = 5, /*!< B3OUT2 : Output is B3OUT2. value. */ + CTIMER_OUTCFG3_CFG31_B7OUT = 4, /*!< B7OUT : Output is B7OUT. value. */ + CTIMER_OUTCFG3_CFG31_A6OUT = 3, /*!< A6OUT : Output is A6OUT. value. */ + CTIMER_OUTCFG3_CFG31_B7OUT2 = 2, /*!< B7OUT2 : Output is B7OUT2 value. */ + CTIMER_OUTCFG3_CFG31_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG3_CFG31_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG3_CFG31_Enum; + +/* ============================================== CTIMER OUTCFG3 CFG30 [0..2] ============================================== */ +typedef enum { /*!< CTIMER_OUTCFG3_CFG30 */ + CTIMER_OUTCFG3_CFG30_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ + CTIMER_OUTCFG3_CFG30_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ + CTIMER_OUTCFG3_CFG30_A0OUT2 = 5, /*!< A0OUT2 : Output is A0OUT2. value. */ + CTIMER_OUTCFG3_CFG30_A4OUT2 = 4, /*!< A4OUT2 : Output is A4OUT2. value. */ + CTIMER_OUTCFG3_CFG30_B3OUT = 3, /*!< B3OUT : Output is B3OUT. value. */ + CTIMER_OUTCFG3_CFG30_B7OUT = 2, /*!< B7OUT : Output is B7OUT value. */ + CTIMER_OUTCFG3_CFG30_ONE = 1, /*!< ONE : Force output to 1. value. */ + CTIMER_OUTCFG3_CFG30_ZERO = 0, /*!< ZERO : Force output to 0 value. */ +} CTIMER_OUTCFG3_CFG30_Enum; + +/* ========================================================= INCFG ========================================================= */ +/* ============================================== CTIMER INCFG CFGB7 [15..15] ============================================== */ +typedef enum { /*!< CTIMER_INCFG_CFGB7 */ + CTIMER_INCFG_CFGB7_CT31 = 1, /*!< CT31 : Input is CT31 value. */ + CTIMER_INCFG_CFGB7_CT30 = 0, /*!< CT30 : Input is CT30 value. */ +} CTIMER_INCFG_CFGB7_Enum; + +/* ============================================== CTIMER INCFG CFGA7 [14..14] ============================================== */ +typedef enum { /*!< CTIMER_INCFG_CFGA7 */ + CTIMER_INCFG_CFGA7_CT29 = 1, /*!< CT29 : Input is CT29 value. */ + CTIMER_INCFG_CFGA7_CT28 = 0, /*!< CT28 : Input is CT28 value. */ +} CTIMER_INCFG_CFGA7_Enum; + +/* ============================================== CTIMER INCFG CFGB6 [13..13] ============================================== */ +typedef enum { /*!< CTIMER_INCFG_CFGB6 */ + CTIMER_INCFG_CFGB6_CT27 = 1, /*!< CT27 : Input is CT27 value. */ + CTIMER_INCFG_CFGB6_CT26 = 0, /*!< CT26 : Input is CT26 value. */ +} CTIMER_INCFG_CFGB6_Enum; + +/* ============================================== CTIMER INCFG CFGA6 [12..12] ============================================== */ +typedef enum { /*!< CTIMER_INCFG_CFGA6 */ + CTIMER_INCFG_CFGA6_CT25 = 1, /*!< CT25 : Input is CT25 value. */ + CTIMER_INCFG_CFGA6_CT24 = 0, /*!< CT24 : Input is CT24 value. */ +} CTIMER_INCFG_CFGA6_Enum; + +/* ============================================== CTIMER INCFG CFGB5 [11..11] ============================================== */ +typedef enum { /*!< CTIMER_INCFG_CFGB5 */ + CTIMER_INCFG_CFGB5_CT23 = 1, /*!< CT23 : Input is CT23 value. */ + CTIMER_INCFG_CFGB5_CT22 = 0, /*!< CT22 : Input is CT22 value. */ +} CTIMER_INCFG_CFGB5_Enum; + +/* ============================================== CTIMER INCFG CFGA5 [10..10] ============================================== */ +typedef enum { /*!< CTIMER_INCFG_CFGA5 */ + CTIMER_INCFG_CFGA5_CT21 = 1, /*!< CT21 : Input is CT21 value. */ + CTIMER_INCFG_CFGA5_CT20 = 0, /*!< CT20 : Input is CT20 value. */ +} CTIMER_INCFG_CFGA5_Enum; + +/* =============================================== CTIMER INCFG CFGB4 [9..9] =============================================== */ +typedef enum { /*!< CTIMER_INCFG_CFGB4 */ + CTIMER_INCFG_CFGB4_CT19 = 1, /*!< CT19 : Input is CT19 value. */ + CTIMER_INCFG_CFGB4_CT18 = 0, /*!< CT18 : Input is CT18 value. */ +} CTIMER_INCFG_CFGB4_Enum; + +/* =============================================== CTIMER INCFG CFGA4 [8..8] =============================================== */ +typedef enum { /*!< CTIMER_INCFG_CFGA4 */ + CTIMER_INCFG_CFGA4_CT17 = 1, /*!< CT17 : Input is CT17 value. */ + CTIMER_INCFG_CFGA4_CT16 = 0, /*!< CT16 : Input is CT16 value. */ +} CTIMER_INCFG_CFGA4_Enum; + +/* =============================================== CTIMER INCFG CFGB3 [7..7] =============================================== */ +typedef enum { /*!< CTIMER_INCFG_CFGB3 */ + CTIMER_INCFG_CFGB3_CT15 = 1, /*!< CT15 : Input is CT15 value. */ + CTIMER_INCFG_CFGB3_CT14 = 0, /*!< CT14 : Input is CT14 value. */ +} CTIMER_INCFG_CFGB3_Enum; + +/* =============================================== CTIMER INCFG CFGA3 [6..6] =============================================== */ +typedef enum { /*!< CTIMER_INCFG_CFGA3 */ + CTIMER_INCFG_CFGA3_CT13 = 1, /*!< CT13 : Input is CT13 value. */ + CTIMER_INCFG_CFGA3_CT12 = 0, /*!< CT12 : Input is CT12 value. */ +} CTIMER_INCFG_CFGA3_Enum; + +/* =============================================== CTIMER INCFG CFGB2 [5..5] =============================================== */ +typedef enum { /*!< CTIMER_INCFG_CFGB2 */ + CTIMER_INCFG_CFGB2_CT11 = 1, /*!< CT11 : Input is CT11 value. */ + CTIMER_INCFG_CFGB2_CT10 = 0, /*!< CT10 : Input is CT10 value. */ +} CTIMER_INCFG_CFGB2_Enum; + +/* =============================================== CTIMER INCFG CFGA2 [4..4] =============================================== */ +typedef enum { /*!< CTIMER_INCFG_CFGA2 */ + CTIMER_INCFG_CFGA2_CT9 = 1, /*!< CT9 : Input is CT9 value. */ + CTIMER_INCFG_CFGA2_CT8 = 0, /*!< CT8 : Input is CT8 value. */ +} CTIMER_INCFG_CFGA2_Enum; + +/* =============================================== CTIMER INCFG CFGB1 [3..3] =============================================== */ +typedef enum { /*!< CTIMER_INCFG_CFGB1 */ + CTIMER_INCFG_CFGB1_CT7 = 1, /*!< CT7 : Input is CT7 value. */ + CTIMER_INCFG_CFGB1_CT6 = 0, /*!< CT6 : Input is CT6 value. */ +} CTIMER_INCFG_CFGB1_Enum; + +/* =============================================== CTIMER INCFG CFGA1 [2..2] =============================================== */ +typedef enum { /*!< CTIMER_INCFG_CFGA1 */ + CTIMER_INCFG_CFGA1_CT5 = 1, /*!< CT5 : Input is CT5 value. */ + CTIMER_INCFG_CFGA1_CT4 = 0, /*!< CT4 : Input is CT4 value. */ +} CTIMER_INCFG_CFGA1_Enum; + +/* =============================================== CTIMER INCFG CFGB0 [1..1] =============================================== */ +typedef enum { /*!< CTIMER_INCFG_CFGB0 */ + CTIMER_INCFG_CFGB0_CT3 = 1, /*!< CT3 : Input is CT3 value. */ + CTIMER_INCFG_CFGB0_CT2 = 0, /*!< CT2 : Input is CT2 value. */ +} CTIMER_INCFG_CFGB0_Enum; + +/* =============================================== CTIMER INCFG CFGA0 [0..0] =============================================== */ +typedef enum { /*!< CTIMER_INCFG_CFGA0 */ + CTIMER_INCFG_CFGA0_CT1 = 1, /*!< CT1 : Input is CT1 value. */ + CTIMER_INCFG_CFGA0_CT0 = 0, /*!< CT0 : Input is CT0 value. */ +} CTIMER_INCFG_CFGA0_Enum; + +/* ========================================================= STCFG ========================================================= */ +/* ============================================= CTIMER STCFG FREEZE [31..31] ============================================== */ +typedef enum { /*!< CTIMER_STCFG_FREEZE */ + CTIMER_STCFG_FREEZE_THAW = 0, /*!< THAW : Let the COUNTER register run on its input clock. value. */ + CTIMER_STCFG_FREEZE_FREEZE = 1, /*!< FREEZE : Stop the COUNTER register for loading. value. */ +} CTIMER_STCFG_FREEZE_Enum; + +/* ============================================== CTIMER STCFG CLEAR [30..30] ============================================== */ +typedef enum { /*!< CTIMER_STCFG_CLEAR */ + CTIMER_STCFG_CLEAR_RUN = 0, /*!< RUN : Let the COUNTER register run on its input clock. value. */ + CTIMER_STCFG_CLEAR_CLEAR = 1, /*!< CLEAR : Stop the COUNTER register for loading. value. */ +} CTIMER_STCFG_CLEAR_Enum; + +/* ========================================== CTIMER STCFG COMPARE_H_EN [15..15] =========================================== */ +typedef enum { /*!< CTIMER_STCFG_COMPARE_H_EN */ + CTIMER_STCFG_COMPARE_H_EN_DISABLE = 0, /*!< DISABLE : Compare H disabled. value. */ + CTIMER_STCFG_COMPARE_H_EN_ENABLE = 1, /*!< ENABLE : Compare H enabled. value. */ +} CTIMER_STCFG_COMPARE_H_EN_Enum; + +/* ========================================== CTIMER STCFG COMPARE_G_EN [14..14] =========================================== */ +typedef enum { /*!< CTIMER_STCFG_COMPARE_G_EN */ + CTIMER_STCFG_COMPARE_G_EN_DISABLE = 0, /*!< DISABLE : Compare G disabled. value. */ + CTIMER_STCFG_COMPARE_G_EN_ENABLE = 1, /*!< ENABLE : Compare G enabled. value. */ +} CTIMER_STCFG_COMPARE_G_EN_Enum; + +/* ========================================== CTIMER STCFG COMPARE_F_EN [13..13] =========================================== */ +typedef enum { /*!< CTIMER_STCFG_COMPARE_F_EN */ + CTIMER_STCFG_COMPARE_F_EN_DISABLE = 0, /*!< DISABLE : Compare F disabled. value. */ + CTIMER_STCFG_COMPARE_F_EN_ENABLE = 1, /*!< ENABLE : Compare F enabled. value. */ +} CTIMER_STCFG_COMPARE_F_EN_Enum; + +/* ========================================== CTIMER STCFG COMPARE_E_EN [12..12] =========================================== */ +typedef enum { /*!< CTIMER_STCFG_COMPARE_E_EN */ + CTIMER_STCFG_COMPARE_E_EN_DISABLE = 0, /*!< DISABLE : Compare E disabled. value. */ + CTIMER_STCFG_COMPARE_E_EN_ENABLE = 1, /*!< ENABLE : Compare E enabled. value. */ +} CTIMER_STCFG_COMPARE_E_EN_Enum; + +/* ========================================== CTIMER STCFG COMPARE_D_EN [11..11] =========================================== */ +typedef enum { /*!< CTIMER_STCFG_COMPARE_D_EN */ + CTIMER_STCFG_COMPARE_D_EN_DISABLE = 0, /*!< DISABLE : Compare D disabled. value. */ + CTIMER_STCFG_COMPARE_D_EN_ENABLE = 1, /*!< ENABLE : Compare D enabled. value. */ +} CTIMER_STCFG_COMPARE_D_EN_Enum; + +/* ========================================== CTIMER STCFG COMPARE_C_EN [10..10] =========================================== */ +typedef enum { /*!< CTIMER_STCFG_COMPARE_C_EN */ + CTIMER_STCFG_COMPARE_C_EN_DISABLE = 0, /*!< DISABLE : Compare C disabled. value. */ + CTIMER_STCFG_COMPARE_C_EN_ENABLE = 1, /*!< ENABLE : Compare C enabled. value. */ +} CTIMER_STCFG_COMPARE_C_EN_Enum; + +/* =========================================== CTIMER STCFG COMPARE_B_EN [9..9] ============================================ */ +typedef enum { /*!< CTIMER_STCFG_COMPARE_B_EN */ + CTIMER_STCFG_COMPARE_B_EN_DISABLE = 0, /*!< DISABLE : Compare B disabled. value. */ + CTIMER_STCFG_COMPARE_B_EN_ENABLE = 1, /*!< ENABLE : Compare B enabled. value. */ +} CTIMER_STCFG_COMPARE_B_EN_Enum; + +/* =========================================== CTIMER STCFG COMPARE_A_EN [8..8] ============================================ */ +typedef enum { /*!< CTIMER_STCFG_COMPARE_A_EN */ + CTIMER_STCFG_COMPARE_A_EN_DISABLE = 0, /*!< DISABLE : Compare A disabled. value. */ + CTIMER_STCFG_COMPARE_A_EN_ENABLE = 1, /*!< ENABLE : Compare A enabled. value. */ +} CTIMER_STCFG_COMPARE_A_EN_Enum; + +/* ============================================== CTIMER STCFG CLKSEL [0..3] =============================================== */ +typedef enum { /*!< CTIMER_STCFG_CLKSEL */ + CTIMER_STCFG_CLKSEL_NOCLK = 0, /*!< NOCLK : No clock enabled. value. */ + CTIMER_STCFG_CLKSEL_HFRC_DIV16 = 1, /*!< HFRC_DIV16 : 3MHz from the HFRC clock divider. value. */ + CTIMER_STCFG_CLKSEL_HFRC_DIV256 = 2, /*!< HFRC_DIV256 : 187.5KHz from the HFRC clock divider. value. */ + CTIMER_STCFG_CLKSEL_XTAL_DIV1 = 3, /*!< XTAL_DIV1 : 32768Hz from the crystal oscillator. value. */ + CTIMER_STCFG_CLKSEL_XTAL_DIV2 = 4, /*!< XTAL_DIV2 : 16384Hz from the crystal oscillator. value. */ + CTIMER_STCFG_CLKSEL_XTAL_DIV32 = 5, /*!< XTAL_DIV32 : 1024Hz from the crystal oscillator. value. */ + CTIMER_STCFG_CLKSEL_LFRC_DIV1 = 6, /*!< LFRC_DIV1 : Approximately 1KHz from the LFRC oscillator (uncalibrated). + value. */ + CTIMER_STCFG_CLKSEL_CTIMER0A = 7, /*!< CTIMER0A : Use CTIMER 0 section A as a prescaler for the clock + source. value. */ + CTIMER_STCFG_CLKSEL_CTIMER0B = 8, /*!< CTIMER0B : Use CTIMER 0 section B (or A and B linked together) + as a prescaler for the clock source. value. */ +} CTIMER_STCFG_CLKSEL_Enum; + +/* ========================================================= STTMR ========================================================= */ +/* ==================================================== CAPTURECONTROL ===================================================== */ +/* ========================================= CTIMER CAPTURECONTROL CAPTURE3 [3..3] ========================================= */ +typedef enum { /*!< CTIMER_CAPTURECONTROL_CAPTURE3 */ + CTIMER_CAPTURECONTROL_CAPTURE3_DISABLE = 0, /*!< DISABLE : Capture function disabled. value. */ + CTIMER_CAPTURECONTROL_CAPTURE3_ENABLE = 1, /*!< ENABLE : Capture function enabled. value. */ +} CTIMER_CAPTURECONTROL_CAPTURE3_Enum; + +/* ========================================= CTIMER CAPTURECONTROL CAPTURE2 [2..2] ========================================= */ +typedef enum { /*!< CTIMER_CAPTURECONTROL_CAPTURE2 */ + CTIMER_CAPTURECONTROL_CAPTURE2_DISABLE = 0, /*!< DISABLE : Capture function disabled. value. */ + CTIMER_CAPTURECONTROL_CAPTURE2_ENABLE = 1, /*!< ENABLE : Capture function enabled. value. */ +} CTIMER_CAPTURECONTROL_CAPTURE2_Enum; + +/* ========================================= CTIMER CAPTURECONTROL CAPTURE1 [1..1] ========================================= */ +typedef enum { /*!< CTIMER_CAPTURECONTROL_CAPTURE1 */ + CTIMER_CAPTURECONTROL_CAPTURE1_DISABLE = 0, /*!< DISABLE : Capture function disabled. value. */ + CTIMER_CAPTURECONTROL_CAPTURE1_ENABLE = 1, /*!< ENABLE : Capture function enabled. value. */ +} CTIMER_CAPTURECONTROL_CAPTURE1_Enum; + +/* ========================================= CTIMER CAPTURECONTROL CAPTURE0 [0..0] ========================================= */ +typedef enum { /*!< CTIMER_CAPTURECONTROL_CAPTURE0 */ + CTIMER_CAPTURECONTROL_CAPTURE0_DISABLE = 0, /*!< DISABLE : Capture function disabled. value. */ + CTIMER_CAPTURECONTROL_CAPTURE0_ENABLE = 1, /*!< ENABLE : Capture function enabled. value. */ +} CTIMER_CAPTURECONTROL_CAPTURE0_Enum; + +/* ======================================================== SCMPR0 ========================================================= */ +/* ======================================================== SCMPR1 ========================================================= */ +/* ======================================================== SCMPR2 ========================================================= */ +/* ======================================================== SCMPR3 ========================================================= */ +/* ======================================================== SCMPR4 ========================================================= */ +/* ======================================================== SCMPR5 ========================================================= */ +/* ======================================================== SCMPR6 ========================================================= */ +/* ======================================================== SCMPR7 ========================================================= */ +/* ======================================================== SCAPT0 ========================================================= */ +/* ======================================================== SCAPT1 ========================================================= */ +/* ======================================================== SCAPT2 ========================================================= */ +/* ======================================================== SCAPT3 ========================================================= */ +/* ========================================================= SNVR0 ========================================================= */ +/* ========================================================= SNVR1 ========================================================= */ +/* ========================================================= SNVR2 ========================================================= */ +/* ========================================================= SNVR3 ========================================================= */ +/* ========================================================= INTEN ========================================================= */ +/* ======================================================== INTSTAT ======================================================== */ +/* ======================================================== INTCLR ========================================================= */ +/* ======================================================== INTSET ========================================================= */ +/* ======================================================= STMINTEN ======================================================== */ +/* =========================================== CTIMER STMINTEN CAPTURED [12..12] =========================================== */ +typedef enum { /*!< CTIMER_STMINTEN_CAPTURED */ + CTIMER_STMINTEN_CAPTURED_CAPD_INT = 1, /*!< CAPD_INT : Capture D interrupt status bit was set. value. */ +} CTIMER_STMINTEN_CAPTURED_Enum; + +/* =========================================== CTIMER STMINTEN CAPTUREC [11..11] =========================================== */ +typedef enum { /*!< CTIMER_STMINTEN_CAPTUREC */ + CTIMER_STMINTEN_CAPTUREC_CAPC_INT = 1, /*!< CAPC_INT : CAPTURE C interrupt status bit was set. value. */ +} CTIMER_STMINTEN_CAPTUREC_Enum; + +/* =========================================== CTIMER STMINTEN CAPTUREB [10..10] =========================================== */ +typedef enum { /*!< CTIMER_STMINTEN_CAPTUREB */ + CTIMER_STMINTEN_CAPTUREB_CAPB_INT = 1, /*!< CAPB_INT : CAPTURE B interrupt status bit was set. value. */ +} CTIMER_STMINTEN_CAPTUREB_Enum; + +/* ============================================ CTIMER STMINTEN CAPTUREA [9..9] ============================================ */ +typedef enum { /*!< CTIMER_STMINTEN_CAPTUREA */ + CTIMER_STMINTEN_CAPTUREA_CAPA_INT = 1, /*!< CAPA_INT : CAPTURE A interrupt status bit was set. value. */ +} CTIMER_STMINTEN_CAPTUREA_Enum; + +/* ============================================ CTIMER STMINTEN OVERFLOW [8..8] ============================================ */ +typedef enum { /*!< CTIMER_STMINTEN_OVERFLOW */ + CTIMER_STMINTEN_OVERFLOW_OFLOW_INT = 1, /*!< OFLOW_INT : Overflow interrupt status bit was set. value. */ +} CTIMER_STMINTEN_OVERFLOW_Enum; + +/* ============================================ CTIMER STMINTEN COMPAREH [7..7] ============================================ */ +typedef enum { /*!< CTIMER_STMINTEN_COMPAREH */ + CTIMER_STMINTEN_COMPAREH_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTEN_COMPAREH_Enum; + +/* ============================================ CTIMER STMINTEN COMPAREG [6..6] ============================================ */ +typedef enum { /*!< CTIMER_STMINTEN_COMPAREG */ + CTIMER_STMINTEN_COMPAREG_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTEN_COMPAREG_Enum; + +/* ============================================ CTIMER STMINTEN COMPAREF [5..5] ============================================ */ +typedef enum { /*!< CTIMER_STMINTEN_COMPAREF */ + CTIMER_STMINTEN_COMPAREF_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTEN_COMPAREF_Enum; + +/* ============================================ CTIMER STMINTEN COMPAREE [4..4] ============================================ */ +typedef enum { /*!< CTIMER_STMINTEN_COMPAREE */ + CTIMER_STMINTEN_COMPAREE_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTEN_COMPAREE_Enum; + +/* ============================================ CTIMER STMINTEN COMPARED [3..3] ============================================ */ +typedef enum { /*!< CTIMER_STMINTEN_COMPARED */ + CTIMER_STMINTEN_COMPARED_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTEN_COMPARED_Enum; + +/* ============================================ CTIMER STMINTEN COMPAREC [2..2] ============================================ */ +typedef enum { /*!< CTIMER_STMINTEN_COMPAREC */ + CTIMER_STMINTEN_COMPAREC_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTEN_COMPAREC_Enum; + +/* ============================================ CTIMER STMINTEN COMPAREB [1..1] ============================================ */ +typedef enum { /*!< CTIMER_STMINTEN_COMPAREB */ + CTIMER_STMINTEN_COMPAREB_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTEN_COMPAREB_Enum; + +/* ============================================ CTIMER STMINTEN COMPAREA [0..0] ============================================ */ +typedef enum { /*!< CTIMER_STMINTEN_COMPAREA */ + CTIMER_STMINTEN_COMPAREA_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTEN_COMPAREA_Enum; + +/* ====================================================== STMINTSTAT ======================================================= */ +/* ========================================== CTIMER STMINTSTAT CAPTURED [12..12] ========================================== */ +typedef enum { /*!< CTIMER_STMINTSTAT_CAPTURED */ + CTIMER_STMINTSTAT_CAPTURED_CAPD_INT = 1, /*!< CAPD_INT : Capture D interrupt status bit was set. value. */ +} CTIMER_STMINTSTAT_CAPTURED_Enum; + +/* ========================================== CTIMER STMINTSTAT CAPTUREC [11..11] ========================================== */ +typedef enum { /*!< CTIMER_STMINTSTAT_CAPTUREC */ + CTIMER_STMINTSTAT_CAPTUREC_CAPC_INT = 1, /*!< CAPC_INT : CAPTURE C interrupt status bit was set. value. */ +} CTIMER_STMINTSTAT_CAPTUREC_Enum; + +/* ========================================== CTIMER STMINTSTAT CAPTUREB [10..10] ========================================== */ +typedef enum { /*!< CTIMER_STMINTSTAT_CAPTUREB */ + CTIMER_STMINTSTAT_CAPTUREB_CAPB_INT = 1, /*!< CAPB_INT : CAPTURE B interrupt status bit was set. value. */ +} CTIMER_STMINTSTAT_CAPTUREB_Enum; + +/* =========================================== CTIMER STMINTSTAT CAPTUREA [9..9] =========================================== */ +typedef enum { /*!< CTIMER_STMINTSTAT_CAPTUREA */ + CTIMER_STMINTSTAT_CAPTUREA_CAPA_INT = 1, /*!< CAPA_INT : CAPTURE A interrupt status bit was set. value. */ +} CTIMER_STMINTSTAT_CAPTUREA_Enum; + +/* =========================================== CTIMER STMINTSTAT OVERFLOW [8..8] =========================================== */ +typedef enum { /*!< CTIMER_STMINTSTAT_OVERFLOW */ + CTIMER_STMINTSTAT_OVERFLOW_OFLOW_INT = 1, /*!< OFLOW_INT : Overflow interrupt status bit was set. value. */ +} CTIMER_STMINTSTAT_OVERFLOW_Enum; + +/* =========================================== CTIMER STMINTSTAT COMPAREH [7..7] =========================================== */ +typedef enum { /*!< CTIMER_STMINTSTAT_COMPAREH */ + CTIMER_STMINTSTAT_COMPAREH_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTSTAT_COMPAREH_Enum; + +/* =========================================== CTIMER STMINTSTAT COMPAREG [6..6] =========================================== */ +typedef enum { /*!< CTIMER_STMINTSTAT_COMPAREG */ + CTIMER_STMINTSTAT_COMPAREG_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTSTAT_COMPAREG_Enum; + +/* =========================================== CTIMER STMINTSTAT COMPAREF [5..5] =========================================== */ +typedef enum { /*!< CTIMER_STMINTSTAT_COMPAREF */ + CTIMER_STMINTSTAT_COMPAREF_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTSTAT_COMPAREF_Enum; + +/* =========================================== CTIMER STMINTSTAT COMPAREE [4..4] =========================================== */ +typedef enum { /*!< CTIMER_STMINTSTAT_COMPAREE */ + CTIMER_STMINTSTAT_COMPAREE_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTSTAT_COMPAREE_Enum; + +/* =========================================== CTIMER STMINTSTAT COMPARED [3..3] =========================================== */ +typedef enum { /*!< CTIMER_STMINTSTAT_COMPARED */ + CTIMER_STMINTSTAT_COMPARED_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTSTAT_COMPARED_Enum; + +/* =========================================== CTIMER STMINTSTAT COMPAREC [2..2] =========================================== */ +typedef enum { /*!< CTIMER_STMINTSTAT_COMPAREC */ + CTIMER_STMINTSTAT_COMPAREC_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTSTAT_COMPAREC_Enum; + +/* =========================================== CTIMER STMINTSTAT COMPAREB [1..1] =========================================== */ +typedef enum { /*!< CTIMER_STMINTSTAT_COMPAREB */ + CTIMER_STMINTSTAT_COMPAREB_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTSTAT_COMPAREB_Enum; + +/* =========================================== CTIMER STMINTSTAT COMPAREA [0..0] =========================================== */ +typedef enum { /*!< CTIMER_STMINTSTAT_COMPAREA */ + CTIMER_STMINTSTAT_COMPAREA_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTSTAT_COMPAREA_Enum; + +/* ======================================================= STMINTCLR ======================================================= */ +/* ========================================== CTIMER STMINTCLR CAPTURED [12..12] =========================================== */ +typedef enum { /*!< CTIMER_STMINTCLR_CAPTURED */ + CTIMER_STMINTCLR_CAPTURED_CAPD_INT = 1, /*!< CAPD_INT : Capture D interrupt status bit was set. value. */ +} CTIMER_STMINTCLR_CAPTURED_Enum; + +/* ========================================== CTIMER STMINTCLR CAPTUREC [11..11] =========================================== */ +typedef enum { /*!< CTIMER_STMINTCLR_CAPTUREC */ + CTIMER_STMINTCLR_CAPTUREC_CAPC_INT = 1, /*!< CAPC_INT : CAPTURE C interrupt status bit was set. value. */ +} CTIMER_STMINTCLR_CAPTUREC_Enum; + +/* ========================================== CTIMER STMINTCLR CAPTUREB [10..10] =========================================== */ +typedef enum { /*!< CTIMER_STMINTCLR_CAPTUREB */ + CTIMER_STMINTCLR_CAPTUREB_CAPB_INT = 1, /*!< CAPB_INT : CAPTURE B interrupt status bit was set. value. */ +} CTIMER_STMINTCLR_CAPTUREB_Enum; + +/* =========================================== CTIMER STMINTCLR CAPTUREA [9..9] ============================================ */ +typedef enum { /*!< CTIMER_STMINTCLR_CAPTUREA */ + CTIMER_STMINTCLR_CAPTUREA_CAPA_INT = 1, /*!< CAPA_INT : CAPTURE A interrupt status bit was set. value. */ +} CTIMER_STMINTCLR_CAPTUREA_Enum; + +/* =========================================== CTIMER STMINTCLR OVERFLOW [8..8] ============================================ */ +typedef enum { /*!< CTIMER_STMINTCLR_OVERFLOW */ + CTIMER_STMINTCLR_OVERFLOW_OFLOW_INT = 1, /*!< OFLOW_INT : Overflow interrupt status bit was set. value. */ +} CTIMER_STMINTCLR_OVERFLOW_Enum; + +/* =========================================== CTIMER STMINTCLR COMPAREH [7..7] ============================================ */ +typedef enum { /*!< CTIMER_STMINTCLR_COMPAREH */ + CTIMER_STMINTCLR_COMPAREH_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTCLR_COMPAREH_Enum; + +/* =========================================== CTIMER STMINTCLR COMPAREG [6..6] ============================================ */ +typedef enum { /*!< CTIMER_STMINTCLR_COMPAREG */ + CTIMER_STMINTCLR_COMPAREG_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTCLR_COMPAREG_Enum; + +/* =========================================== CTIMER STMINTCLR COMPAREF [5..5] ============================================ */ +typedef enum { /*!< CTIMER_STMINTCLR_COMPAREF */ + CTIMER_STMINTCLR_COMPAREF_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTCLR_COMPAREF_Enum; + +/* =========================================== CTIMER STMINTCLR COMPAREE [4..4] ============================================ */ +typedef enum { /*!< CTIMER_STMINTCLR_COMPAREE */ + CTIMER_STMINTCLR_COMPAREE_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTCLR_COMPAREE_Enum; + +/* =========================================== CTIMER STMINTCLR COMPARED [3..3] ============================================ */ +typedef enum { /*!< CTIMER_STMINTCLR_COMPARED */ + CTIMER_STMINTCLR_COMPARED_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTCLR_COMPARED_Enum; + +/* =========================================== CTIMER STMINTCLR COMPAREC [2..2] ============================================ */ +typedef enum { /*!< CTIMER_STMINTCLR_COMPAREC */ + CTIMER_STMINTCLR_COMPAREC_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTCLR_COMPAREC_Enum; + +/* =========================================== CTIMER STMINTCLR COMPAREB [1..1] ============================================ */ +typedef enum { /*!< CTIMER_STMINTCLR_COMPAREB */ + CTIMER_STMINTCLR_COMPAREB_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTCLR_COMPAREB_Enum; + +/* =========================================== CTIMER STMINTCLR COMPAREA [0..0] ============================================ */ +typedef enum { /*!< CTIMER_STMINTCLR_COMPAREA */ + CTIMER_STMINTCLR_COMPAREA_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTCLR_COMPAREA_Enum; + +/* ======================================================= STMINTSET ======================================================= */ +/* ========================================== CTIMER STMINTSET CAPTURED [12..12] =========================================== */ +typedef enum { /*!< CTIMER_STMINTSET_CAPTURED */ + CTIMER_STMINTSET_CAPTURED_CAPD_INT = 1, /*!< CAPD_INT : Capture D interrupt status bit was set. value. */ +} CTIMER_STMINTSET_CAPTURED_Enum; + +/* ========================================== CTIMER STMINTSET CAPTUREC [11..11] =========================================== */ +typedef enum { /*!< CTIMER_STMINTSET_CAPTUREC */ + CTIMER_STMINTSET_CAPTUREC_CAPC_INT = 1, /*!< CAPC_INT : CAPTURE C interrupt status bit was set. value. */ +} CTIMER_STMINTSET_CAPTUREC_Enum; + +/* ========================================== CTIMER STMINTSET CAPTUREB [10..10] =========================================== */ +typedef enum { /*!< CTIMER_STMINTSET_CAPTUREB */ + CTIMER_STMINTSET_CAPTUREB_CAPB_INT = 1, /*!< CAPB_INT : CAPTURE B interrupt status bit was set. value. */ +} CTIMER_STMINTSET_CAPTUREB_Enum; + +/* =========================================== CTIMER STMINTSET CAPTUREA [9..9] ============================================ */ +typedef enum { /*!< CTIMER_STMINTSET_CAPTUREA */ + CTIMER_STMINTSET_CAPTUREA_CAPA_INT = 1, /*!< CAPA_INT : CAPTURE A interrupt status bit was set. value. */ +} CTIMER_STMINTSET_CAPTUREA_Enum; + +/* =========================================== CTIMER STMINTSET OVERFLOW [8..8] ============================================ */ +typedef enum { /*!< CTIMER_STMINTSET_OVERFLOW */ + CTIMER_STMINTSET_OVERFLOW_OFLOW_INT = 1, /*!< OFLOW_INT : Overflow interrupt status bit was set. value. */ +} CTIMER_STMINTSET_OVERFLOW_Enum; + +/* =========================================== CTIMER STMINTSET COMPAREH [7..7] ============================================ */ +typedef enum { /*!< CTIMER_STMINTSET_COMPAREH */ + CTIMER_STMINTSET_COMPAREH_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTSET_COMPAREH_Enum; + +/* =========================================== CTIMER STMINTSET COMPAREG [6..6] ============================================ */ +typedef enum { /*!< CTIMER_STMINTSET_COMPAREG */ + CTIMER_STMINTSET_COMPAREG_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTSET_COMPAREG_Enum; + +/* =========================================== CTIMER STMINTSET COMPAREF [5..5] ============================================ */ +typedef enum { /*!< CTIMER_STMINTSET_COMPAREF */ + CTIMER_STMINTSET_COMPAREF_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTSET_COMPAREF_Enum; + +/* =========================================== CTIMER STMINTSET COMPAREE [4..4] ============================================ */ +typedef enum { /*!< CTIMER_STMINTSET_COMPAREE */ + CTIMER_STMINTSET_COMPAREE_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTSET_COMPAREE_Enum; + +/* =========================================== CTIMER STMINTSET COMPARED [3..3] ============================================ */ +typedef enum { /*!< CTIMER_STMINTSET_COMPARED */ + CTIMER_STMINTSET_COMPARED_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTSET_COMPARED_Enum; + +/* =========================================== CTIMER STMINTSET COMPAREC [2..2] ============================================ */ +typedef enum { /*!< CTIMER_STMINTSET_COMPAREC */ + CTIMER_STMINTSET_COMPAREC_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTSET_COMPAREC_Enum; + +/* =========================================== CTIMER STMINTSET COMPAREB [1..1] ============================================ */ +typedef enum { /*!< CTIMER_STMINTSET_COMPAREB */ + CTIMER_STMINTSET_COMPAREB_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTSET_COMPAREB_Enum; + +/* =========================================== CTIMER STMINTSET COMPAREA [0..0] ============================================ */ +typedef enum { /*!< CTIMER_STMINTSET_COMPAREA */ + CTIMER_STMINTSET_COMPAREA_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. + value. */ +} CTIMER_STMINTSET_COMPAREA_Enum; + + + +/* =========================================================================================================================== */ +/* ================ GPIO ================ */ +/* =========================================================================================================================== */ + +/* ======================================================== PADREGA ======================================================== */ +/* ============================================ GPIO PADREGA PAD3PWRUP [30..30] ============================================ */ +typedef enum { /*!< GPIO_PADREGA_PAD3PWRUP */ + GPIO_PADREGA_PAD3PWRUP_DIS = 0, /*!< DIS : Power switch disabled value. */ + GPIO_PADREGA_PAD3PWRUP_EN = 1, /*!< EN : Power switch enabled (switched to VDD) value. */ +} GPIO_PADREGA_PAD3PWRUP_Enum; + +/* =========================================== GPIO PADREGA PAD3FNCSEL [27..29] ============================================ */ +typedef enum { /*!< GPIO_PADREGA_PAD3FNCSEL */ + GPIO_PADREGA_PAD3FNCSEL_UA0RTS = 0, /*!< UA0RTS : Configure as the UART0 RTS output value. */ + GPIO_PADREGA_PAD3FNCSEL_SLnCE = 1, /*!< SLnCE : Configure as the IOSLAVE SPI nCE signal value. */ + GPIO_PADREGA_PAD3FNCSEL_NCE3 = 2, /*!< NCE3 : IOM/MSPI nCE group 3 value. */ + GPIO_PADREGA_PAD3FNCSEL_GPIO3 = 3, /*!< GPIO3 : Configure as GPIO3 value. */ + GPIO_PADREGA_PAD3FNCSEL_MSPI7 = 5, /*!< MSPI7 : MSPI data connection 7 value. */ + GPIO_PADREGA_PAD3FNCSEL_TRIG1 = 6, /*!< TRIG1 : Configure as the ADC Trigger 1 signal value. */ + GPIO_PADREGA_PAD3FNCSEL_I2S_WCLK = 7, /*!< I2S_WCLK : Configure as the PDM I2S Word Clock input value. */ +} GPIO_PADREGA_PAD3FNCSEL_Enum; + +/* ============================================ GPIO PADREGA PAD3STRNG [26..26] ============================================ */ +typedef enum { /*!< GPIO_PADREGA_PAD3STRNG */ + GPIO_PADREGA_PAD3STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGA_PAD3STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGA_PAD3STRNG_Enum; + +/* ============================================ GPIO PADREGA PAD3INPEN [25..25] ============================================ */ +typedef enum { /*!< GPIO_PADREGA_PAD3INPEN */ + GPIO_PADREGA_PAD3INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGA_PAD3INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGA_PAD3INPEN_Enum; + +/* ============================================ GPIO PADREGA PAD3PULL [24..24] ============================================= */ +typedef enum { /*!< GPIO_PADREGA_PAD3PULL */ + GPIO_PADREGA_PAD3PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGA_PAD3PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGA_PAD3PULL_Enum; + +/* =========================================== GPIO PADREGA PAD2FNCSEL [19..21] ============================================ */ +typedef enum { /*!< GPIO_PADREGA_PAD2FNCSEL */ + GPIO_PADREGA_PAD2FNCSEL_SLMISO = 1, /*!< SLMISO : Configure as the IOSLAVE SPI MISO signal value. */ + GPIO_PADREGA_PAD2FNCSEL_UART0RX = 2, /*!< UART0RX : Configure as the UART0 RX input value. */ + GPIO_PADREGA_PAD2FNCSEL_GPIO2 = 3, /*!< GPIO2 : Configure as GPIO2 value. */ + GPIO_PADREGA_PAD2FNCSEL_MSPI6 = 5, /*!< MSPI6 : CMSPI data connection 6 value. */ + GPIO_PADREGA_PAD2FNCSEL_NCE2 = 7, /*!< NCE2 : IOM/MSPI nCE group 2 value. */ +} GPIO_PADREGA_PAD2FNCSEL_Enum; + +/* ============================================ GPIO PADREGA PAD2STRNG [18..18] ============================================ */ +typedef enum { /*!< GPIO_PADREGA_PAD2STRNG */ + GPIO_PADREGA_PAD2STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGA_PAD2STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGA_PAD2STRNG_Enum; + +/* ============================================ GPIO PADREGA PAD2INPEN [17..17] ============================================ */ +typedef enum { /*!< GPIO_PADREGA_PAD2INPEN */ + GPIO_PADREGA_PAD2INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGA_PAD2INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGA_PAD2INPEN_Enum; + +/* ============================================ GPIO PADREGA PAD2PULL [16..16] ============================================= */ +typedef enum { /*!< GPIO_PADREGA_PAD2PULL */ + GPIO_PADREGA_PAD2PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGA_PAD2PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGA_PAD2PULL_Enum; + +/* ============================================ GPIO PADREGA PAD1RSEL [14..15] ============================================= */ +typedef enum { /*!< GPIO_PADREGA_PAD1RSEL */ + GPIO_PADREGA_PAD1RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ + GPIO_PADREGA_PAD1RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ + GPIO_PADREGA_PAD1RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ + GPIO_PADREGA_PAD1RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ +} GPIO_PADREGA_PAD1RSEL_Enum; + +/* =========================================== GPIO PADREGA PAD1FNCSEL [11..13] ============================================ */ +typedef enum { /*!< GPIO_PADREGA_PAD1FNCSEL */ + GPIO_PADREGA_PAD1FNCSEL_SLSDAWIR3 = 0, /*!< SLSDAWIR3 : Configure as the IOSLAVE I2C SDA or SPI WIR3 signal + value. */ + GPIO_PADREGA_PAD1FNCSEL_SLMOSI = 1, /*!< SLMOSI : Configure as the IOSLAVE SPI MOSI signal value. */ + GPIO_PADREGA_PAD1FNCSEL_UART0TX = 2, /*!< UART0TX : Configure as the UART0 TX output signal value. */ + GPIO_PADREGA_PAD1FNCSEL_GPIO1 = 3, /*!< GPIO1 : Configure as GPIO1 value. */ + GPIO_PADREGA_PAD1FNCSEL_MSPI5 = 5, /*!< MSPI5 : MSPI data connection 5 value. */ + GPIO_PADREGA_PAD1FNCSEL_NCE1 = 7, /*!< NCE1 : IOM/MSPI nCE group 1 value. */ +} GPIO_PADREGA_PAD1FNCSEL_Enum; + +/* ============================================ GPIO PADREGA PAD1STRNG [10..10] ============================================ */ +typedef enum { /*!< GPIO_PADREGA_PAD1STRNG */ + GPIO_PADREGA_PAD1STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGA_PAD1STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGA_PAD1STRNG_Enum; + +/* ============================================= GPIO PADREGA PAD1INPEN [9..9] ============================================= */ +typedef enum { /*!< GPIO_PADREGA_PAD1INPEN */ + GPIO_PADREGA_PAD1INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGA_PAD1INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGA_PAD1INPEN_Enum; + +/* ============================================= GPIO PADREGA PAD1PULL [8..8] ============================================== */ +typedef enum { /*!< GPIO_PADREGA_PAD1PULL */ + GPIO_PADREGA_PAD1PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGA_PAD1PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGA_PAD1PULL_Enum; + +/* ============================================= GPIO PADREGA PAD0RSEL [6..7] ============================================== */ +typedef enum { /*!< GPIO_PADREGA_PAD0RSEL */ + GPIO_PADREGA_PAD0RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ + GPIO_PADREGA_PAD0RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ + GPIO_PADREGA_PAD0RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ + GPIO_PADREGA_PAD0RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ +} GPIO_PADREGA_PAD0RSEL_Enum; + +/* ============================================ GPIO PADREGA PAD0FNCSEL [3..5] ============================================= */ +typedef enum { /*!< GPIO_PADREGA_PAD0FNCSEL */ + GPIO_PADREGA_PAD0FNCSEL_SLSCL = 0, /*!< SLSCL : Configure as the IOSLAVE I2C SCL signal value. */ + GPIO_PADREGA_PAD0FNCSEL_SLSCK = 1, /*!< SLSCK : Configure as the IOSLAVE SPI SCK signal value. */ + GPIO_PADREGA_PAD0FNCSEL_CLKOUT = 2, /*!< CLKOUT : Configure as the CLKOUT signal value. */ + GPIO_PADREGA_PAD0FNCSEL_GPIO0 = 3, /*!< GPIO0 : Configure as GPIO0 value. */ + GPIO_PADREGA_PAD0FNCSEL_MSPI4 = 5, /*!< MSPI4 : MSPI data connection 4 value. */ + GPIO_PADREGA_PAD0FNCSEL_NCE0 = 7, /*!< NCE0 : IOM/MSPI nCE group 0 value. */ +} GPIO_PADREGA_PAD0FNCSEL_Enum; + +/* ============================================= GPIO PADREGA PAD0STRNG [2..2] ============================================= */ +typedef enum { /*!< GPIO_PADREGA_PAD0STRNG */ + GPIO_PADREGA_PAD0STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGA_PAD0STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGA_PAD0STRNG_Enum; + +/* ============================================= GPIO PADREGA PAD0INPEN [1..1] ============================================= */ +typedef enum { /*!< GPIO_PADREGA_PAD0INPEN */ + GPIO_PADREGA_PAD0INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGA_PAD0INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGA_PAD0INPEN_Enum; + +/* ============================================= GPIO PADREGA PAD0PULL [0..0] ============================================== */ +typedef enum { /*!< GPIO_PADREGA_PAD0PULL */ + GPIO_PADREGA_PAD0PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGA_PAD0PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGA_PAD0PULL_Enum; + +/* ======================================================== PADREGB ======================================================== */ +/* =========================================== GPIO PADREGB PAD7FNCSEL [27..29] ============================================ */ +typedef enum { /*!< GPIO_PADREGB_PAD7FNCSEL */ + GPIO_PADREGB_PAD7FNCSEL_NCE7 = 0, /*!< NCE7 : IOM/MSPI nCE group 7 value. */ + GPIO_PADREGB_PAD7FNCSEL_M0MOSI = 1, /*!< M0MOSI : Configure as the IOMSTR0 SPI MOSI signal value. */ + GPIO_PADREGB_PAD7FNCSEL_CLKOUT = 2, /*!< CLKOUT : Configure as the CLKOUT signal value. */ + GPIO_PADREGB_PAD7FNCSEL_GPIO7 = 3, /*!< GPIO7 : Configure as GPIO7 value. */ + GPIO_PADREGB_PAD7FNCSEL_TRIG0 = 4, /*!< TRIG0 : Configure as the ADC Trigger 0 signal value. */ + GPIO_PADREGB_PAD7FNCSEL_UART0TX = 5, /*!< UART0TX : Configure as the UART0 TX output signal value. */ + GPIO_PADREGB_PAD7FNCSEL_CT19 = 7, /*!< CT19 : CTIMER connection 19 value. */ +} GPIO_PADREGB_PAD7FNCSEL_Enum; + +/* ============================================ GPIO PADREGB PAD7STRNG [26..26] ============================================ */ +typedef enum { /*!< GPIO_PADREGB_PAD7STRNG */ + GPIO_PADREGB_PAD7STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGB_PAD7STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGB_PAD7STRNG_Enum; + +/* ============================================ GPIO PADREGB PAD7INPEN [25..25] ============================================ */ +typedef enum { /*!< GPIO_PADREGB_PAD7INPEN */ + GPIO_PADREGB_PAD7INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGB_PAD7INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGB_PAD7INPEN_Enum; + +/* ============================================ GPIO PADREGB PAD7PULL [24..24] ============================================= */ +typedef enum { /*!< GPIO_PADREGB_PAD7PULL */ + GPIO_PADREGB_PAD7PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGB_PAD7PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGB_PAD7PULL_Enum; + +/* ============================================ GPIO PADREGB PAD6RSEL [22..23] ============================================= */ +typedef enum { /*!< GPIO_PADREGB_PAD6RSEL */ + GPIO_PADREGB_PAD6RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ + GPIO_PADREGB_PAD6RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ + GPIO_PADREGB_PAD6RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ + GPIO_PADREGB_PAD6RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ +} GPIO_PADREGB_PAD6RSEL_Enum; + +/* =========================================== GPIO PADREGB PAD6FNCSEL [19..21] ============================================ */ +typedef enum { /*!< GPIO_PADREGB_PAD6FNCSEL */ + GPIO_PADREGB_PAD6FNCSEL_M0SDAWIR3 = 0, /*!< M0SDAWIR3 : Configure as the IOMSTR0 I2C SDA or SPI WIR3 signal + value. */ + GPIO_PADREGB_PAD6FNCSEL_M0MISO = 1, /*!< M0MISO : Configure as the IOMSTR0 SPI MISO signal value. */ + GPIO_PADREGB_PAD6FNCSEL_UA0CTS = 2, /*!< UA0CTS : Configure as the UART0 CTS input signal value. */ + GPIO_PADREGB_PAD6FNCSEL_GPIO6 = 3, /*!< GPIO6 : Configure as GPIO6 value. */ + GPIO_PADREGB_PAD6FNCSEL_CT10 = 5, /*!< CT10 : CTIMER connection 10 value. */ + GPIO_PADREGB_PAD6FNCSEL_I2S_DAT = 7, /*!< I2S_DAT : Configure as the PDM I2S Data output signal value. */ +} GPIO_PADREGB_PAD6FNCSEL_Enum; + +/* ============================================ GPIO PADREGB PAD6STRNG [18..18] ============================================ */ +typedef enum { /*!< GPIO_PADREGB_PAD6STRNG */ + GPIO_PADREGB_PAD6STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGB_PAD6STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGB_PAD6STRNG_Enum; + +/* ============================================ GPIO PADREGB PAD6INPEN [17..17] ============================================ */ +typedef enum { /*!< GPIO_PADREGB_PAD6INPEN */ + GPIO_PADREGB_PAD6INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGB_PAD6INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGB_PAD6INPEN_Enum; + +/* ============================================ GPIO PADREGB PAD6PULL [16..16] ============================================= */ +typedef enum { /*!< GPIO_PADREGB_PAD6PULL */ + GPIO_PADREGB_PAD6PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGB_PAD6PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGB_PAD6PULL_Enum; + +/* ============================================ GPIO PADREGB PAD5RSEL [14..15] ============================================= */ +typedef enum { /*!< GPIO_PADREGB_PAD5RSEL */ + GPIO_PADREGB_PAD5RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ + GPIO_PADREGB_PAD5RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ + GPIO_PADREGB_PAD5RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ + GPIO_PADREGB_PAD5RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ +} GPIO_PADREGB_PAD5RSEL_Enum; + +/* =========================================== GPIO PADREGB PAD5FNCSEL [11..13] ============================================ */ +typedef enum { /*!< GPIO_PADREGB_PAD5FNCSEL */ + GPIO_PADREGB_PAD5FNCSEL_M0SCL = 0, /*!< M0SCL : Configure as the IOMSTR0 I2C SCL signal value. */ + GPIO_PADREGB_PAD5FNCSEL_M0SCK = 1, /*!< M0SCK : Configure as the IOMSTR0 SPI SCK signal value. */ + GPIO_PADREGB_PAD5FNCSEL_UA0RTS = 2, /*!< UA0RTS : Configure as the UART0 RTS signal output value. */ + GPIO_PADREGB_PAD5FNCSEL_GPIO5 = 3, /*!< GPIO5 : Configure as GPIO5 value. */ + GPIO_PADREGB_PAD5FNCSEL_EXTHFA = 5, /*!< EXTHFA : Configure as the External HFA input clock value. */ + GPIO_PADREGB_PAD5FNCSEL_CT8 = 7, /*!< CT8 : CTIMER connection 8 value. */ +} GPIO_PADREGB_PAD5FNCSEL_Enum; + +/* ============================================ GPIO PADREGB PAD5STRNG [10..10] ============================================ */ +typedef enum { /*!< GPIO_PADREGB_PAD5STRNG */ + GPIO_PADREGB_PAD5STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGB_PAD5STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGB_PAD5STRNG_Enum; + +/* ============================================= GPIO PADREGB PAD5INPEN [9..9] ============================================= */ +typedef enum { /*!< GPIO_PADREGB_PAD5INPEN */ + GPIO_PADREGB_PAD5INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGB_PAD5INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGB_PAD5INPEN_Enum; + +/* ============================================= GPIO PADREGB PAD5PULL [8..8] ============================================== */ +typedef enum { /*!< GPIO_PADREGB_PAD5PULL */ + GPIO_PADREGB_PAD5PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGB_PAD5PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGB_PAD5PULL_Enum; + +/* ============================================ GPIO PADREGB PAD4FNCSEL [3..5] ============================================= */ +typedef enum { /*!< GPIO_PADREGB_PAD4FNCSEL */ + GPIO_PADREGB_PAD4FNCSEL_UA0CTS = 0, /*!< UA0CTS : Configure as the UART0 CTS input signal value. */ + GPIO_PADREGB_PAD4FNCSEL_SLINT = 1, /*!< SLINT : Configure as the IOSLAVE interrupt out signal value. */ + GPIO_PADREGB_PAD4FNCSEL_NCE4 = 2, /*!< NCE4 : IOM/SPI nCE group 4 value. */ + GPIO_PADREGB_PAD4FNCSEL_GPIO4 = 3, /*!< GPIO4 : Configure as GPIO4 value. */ + GPIO_PADREGB_PAD4FNCSEL_UART0RX = 5, /*!< UART0RX : Configure as the UART0 RX input value. */ + GPIO_PADREGB_PAD4FNCSEL_CT17 = 6, /*!< CT17 : CTIMER connection 17 value. */ + GPIO_PADREGB_PAD4FNCSEL_MSPI2 = 7, /*!< MSPI2 : MSPI data connection 2 value. */ +} GPIO_PADREGB_PAD4FNCSEL_Enum; + +/* ============================================= GPIO PADREGB PAD4STRNG [2..2] ============================================= */ +typedef enum { /*!< GPIO_PADREGB_PAD4STRNG */ + GPIO_PADREGB_PAD4STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGB_PAD4STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGB_PAD4STRNG_Enum; + +/* ============================================= GPIO PADREGB PAD4INPEN [1..1] ============================================= */ +typedef enum { /*!< GPIO_PADREGB_PAD4INPEN */ + GPIO_PADREGB_PAD4INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGB_PAD4INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGB_PAD4INPEN_Enum; + +/* ============================================= GPIO PADREGB PAD4PULL [0..0] ============================================== */ +typedef enum { /*!< GPIO_PADREGB_PAD4PULL */ + GPIO_PADREGB_PAD4PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGB_PAD4PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGB_PAD4PULL_Enum; + +/* ======================================================== PADREGC ======================================================== */ +/* =========================================== GPIO PADREGC PAD11FNCSEL [27..29] =========================================== */ +typedef enum { /*!< GPIO_PADREGC_PAD11FNCSEL */ + GPIO_PADREGC_PAD11FNCSEL_ADCSE2 = 0, /*!< ADCSE2 : Configure as the analog input for ADC single ended + input 2 value. */ + GPIO_PADREGC_PAD11FNCSEL_NCE11 = 1, /*!< NCE11 : IOM/MSPI nCE group 11 value. */ + GPIO_PADREGC_PAD11FNCSEL_CT31 = 2, /*!< CT31 : CTIMER connection 31 value. */ + GPIO_PADREGC_PAD11FNCSEL_GPIO11 = 3, /*!< GPIO11 : Configure as GPIO11 value. */ + GPIO_PADREGC_PAD11FNCSEL_SLINT = 4, /*!< SLINT : Configure as the IOSLAVE interrupt out signal value. */ + GPIO_PADREGC_PAD11FNCSEL_UA1CTS = 5, /*!< UA1CTS : Configure as the UART1 CTS input signal value. */ + GPIO_PADREGC_PAD11FNCSEL_UART0RX = 6, /*!< UART0RX : Configure as the UART0 RX input signal value. */ + GPIO_PADREGC_PAD11FNCSEL_PDM_DATA = 7, /*!< PDM_DATA : Configure as the PDM Data input signal value. */ +} GPIO_PADREGC_PAD11FNCSEL_Enum; + +/* =========================================== GPIO PADREGC PAD11STRNG [26..26] ============================================ */ +typedef enum { /*!< GPIO_PADREGC_PAD11STRNG */ + GPIO_PADREGC_PAD11STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGC_PAD11STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGC_PAD11STRNG_Enum; + +/* =========================================== GPIO PADREGC PAD11INPEN [25..25] ============================================ */ +typedef enum { /*!< GPIO_PADREGC_PAD11INPEN */ + GPIO_PADREGC_PAD11INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGC_PAD11INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGC_PAD11INPEN_Enum; + +/* ============================================ GPIO PADREGC PAD11PULL [24..24] ============================================ */ +typedef enum { /*!< GPIO_PADREGC_PAD11PULL */ + GPIO_PADREGC_PAD11PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGC_PAD11PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGC_PAD11PULL_Enum; + +/* =========================================== GPIO PADREGC PAD10FNCSEL [19..21] =========================================== */ +typedef enum { /*!< GPIO_PADREGC_PAD10FNCSEL */ + GPIO_PADREGC_PAD10FNCSEL_M1MOSI = 1, /*!< M1MOSI : Configure as the IOMSTR1 SPI MOSI signal value. */ + GPIO_PADREGC_PAD10FNCSEL_NCE10 = 2, /*!< NCE10 : IOM/MSPI nCE group 10 value. */ + GPIO_PADREGC_PAD10FNCSEL_GPIO10 = 3, /*!< GPIO10 : Configure as GPIO10 value. */ + GPIO_PADREGC_PAD10FNCSEL_PDMCLK = 4, /*!< PDMCLK : PDM serial clock out value. */ + GPIO_PADREGC_PAD10FNCSEL_UA1RTS = 5, /*!< UA1RTS : Configure as the UART1 RTS output signal value. */ +} GPIO_PADREGC_PAD10FNCSEL_Enum; + +/* =========================================== GPIO PADREGC PAD10STRNG [18..18] ============================================ */ +typedef enum { /*!< GPIO_PADREGC_PAD10STRNG */ + GPIO_PADREGC_PAD10STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGC_PAD10STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGC_PAD10STRNG_Enum; + +/* =========================================== GPIO PADREGC PAD10INPEN [17..17] ============================================ */ +typedef enum { /*!< GPIO_PADREGC_PAD10INPEN */ + GPIO_PADREGC_PAD10INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGC_PAD10INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGC_PAD10INPEN_Enum; + +/* ============================================ GPIO PADREGC PAD10PULL [16..16] ============================================ */ +typedef enum { /*!< GPIO_PADREGC_PAD10PULL */ + GPIO_PADREGC_PAD10PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGC_PAD10PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGC_PAD10PULL_Enum; + +/* ============================================ GPIO PADREGC PAD9RSEL [14..15] ============================================= */ +typedef enum { /*!< GPIO_PADREGC_PAD9RSEL */ + GPIO_PADREGC_PAD9RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ + GPIO_PADREGC_PAD9RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ + GPIO_PADREGC_PAD9RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ + GPIO_PADREGC_PAD9RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ +} GPIO_PADREGC_PAD9RSEL_Enum; + +/* =========================================== GPIO PADREGC PAD9FNCSEL [11..13] ============================================ */ +typedef enum { /*!< GPIO_PADREGC_PAD9FNCSEL */ + GPIO_PADREGC_PAD9FNCSEL_M1SDAWIR3 = 0, /*!< M1SDAWIR3 : Configure as the IOMSTR1 I2C SDA or SPI WIR3 signal + value. */ + GPIO_PADREGC_PAD9FNCSEL_M1MISO = 1, /*!< M1MISO : Configure as the IOMSTR1 SPI MISO signal value. */ + GPIO_PADREGC_PAD9FNCSEL_NCE9 = 2, /*!< NCE9 : IOM/MSPI nCE group 9 value. */ + GPIO_PADREGC_PAD9FNCSEL_GPIO9 = 3, /*!< GPIO9 : Configure as GPIO9 value. */ + GPIO_PADREGC_PAD9FNCSEL_SCCIO = 4, /*!< SCCIO : SCARD data I/O connection value. */ + GPIO_PADREGC_PAD9FNCSEL_UART1RX = 6, /*!< UART1RX : Configure as UART1 RX input signal value. */ +} GPIO_PADREGC_PAD9FNCSEL_Enum; + +/* ============================================ GPIO PADREGC PAD9STRNG [10..10] ============================================ */ +typedef enum { /*!< GPIO_PADREGC_PAD9STRNG */ + GPIO_PADREGC_PAD9STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGC_PAD9STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGC_PAD9STRNG_Enum; + +/* ============================================= GPIO PADREGC PAD9INPEN [9..9] ============================================= */ +typedef enum { /*!< GPIO_PADREGC_PAD9INPEN */ + GPIO_PADREGC_PAD9INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGC_PAD9INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGC_PAD9INPEN_Enum; + +/* ============================================= GPIO PADREGC PAD9PULL [8..8] ============================================== */ +typedef enum { /*!< GPIO_PADREGC_PAD9PULL */ + GPIO_PADREGC_PAD9PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGC_PAD9PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGC_PAD9PULL_Enum; + +/* ============================================= GPIO PADREGC PAD8RSEL [6..7] ============================================== */ +typedef enum { /*!< GPIO_PADREGC_PAD8RSEL */ + GPIO_PADREGC_PAD8RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ + GPIO_PADREGC_PAD8RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ + GPIO_PADREGC_PAD8RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ + GPIO_PADREGC_PAD8RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ +} GPIO_PADREGC_PAD8RSEL_Enum; + +/* ============================================ GPIO PADREGC PAD8FNCSEL [3..5] ============================================= */ +typedef enum { /*!< GPIO_PADREGC_PAD8FNCSEL */ + GPIO_PADREGC_PAD8FNCSEL_M1SCL = 0, /*!< M1SCL : Configure as the IOMSTR1 I2C SCL signal value. */ + GPIO_PADREGC_PAD8FNCSEL_M1SCK = 1, /*!< M1SCK : Configure as the IOMSTR1 SPI SCK signal value. */ + GPIO_PADREGC_PAD8FNCSEL_NCE8 = 2, /*!< NCE8 : IOM/MSPI nCE group 8 value. */ + GPIO_PADREGC_PAD8FNCSEL_GPIO8 = 3, /*!< GPIO8 : Configure as GPIO8 value. */ + GPIO_PADREGC_PAD8FNCSEL_SCCLK = 4, /*!< SCCLK : SCARD serial clock output value. */ + GPIO_PADREGC_PAD8FNCSEL_UART1TX = 6, /*!< UART1TX : Configure as the UART1 TX output signal value. */ +} GPIO_PADREGC_PAD8FNCSEL_Enum; + +/* ============================================= GPIO PADREGC PAD8STRNG [2..2] ============================================= */ +typedef enum { /*!< GPIO_PADREGC_PAD8STRNG */ + GPIO_PADREGC_PAD8STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGC_PAD8STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGC_PAD8STRNG_Enum; + +/* ============================================= GPIO PADREGC PAD8INPEN [1..1] ============================================= */ +typedef enum { /*!< GPIO_PADREGC_PAD8INPEN */ + GPIO_PADREGC_PAD8INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGC_PAD8INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGC_PAD8INPEN_Enum; + +/* ============================================= GPIO PADREGC PAD8PULL [0..0] ============================================== */ +typedef enum { /*!< GPIO_PADREGC_PAD8PULL */ + GPIO_PADREGC_PAD8PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGC_PAD8PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGC_PAD8PULL_Enum; + +/* ======================================================== PADREGD ======================================================== */ +/* =========================================== GPIO PADREGD PAD15FNCSEL [27..29] =========================================== */ +typedef enum { /*!< GPIO_PADREGD_PAD15FNCSEL */ + GPIO_PADREGD_PAD15FNCSEL_ADCD1N = 0, /*!< ADCD1N : Configure as the analog ADC differential pair 1 N input + signal value. */ + GPIO_PADREGD_PAD15FNCSEL_NCE15 = 1, /*!< NCE15 : IOM/MSPI nCE group 15 value. */ + GPIO_PADREGD_PAD15FNCSEL_UART1RX = 2, /*!< UART1RX : Configure as the UART1 RX signal value. */ + GPIO_PADREGD_PAD15FNCSEL_GPIO15 = 3, /*!< GPIO15 : Configure as GPIO15 value. */ + GPIO_PADREGD_PAD15FNCSEL_PDMDATA = 4, /*!< PDMDATA : PDM serial data input value. */ + GPIO_PADREGD_PAD15FNCSEL_EXTXT = 5, /*!< EXTXT : Configure as the external XTAL oscillator input value. */ + GPIO_PADREGD_PAD15FNCSEL_SWDIO = 6, /*!< SWDIO : Configure as an alternate port for the SWDIO I/O signal + value. */ + GPIO_PADREGD_PAD15FNCSEL_SWO = 7, /*!< SWO : Configure as an SWO (Serial Wire Trace output) value. */ +} GPIO_PADREGD_PAD15FNCSEL_Enum; + +/* =========================================== GPIO PADREGD PAD15STRNG [26..26] ============================================ */ +typedef enum { /*!< GPIO_PADREGD_PAD15STRNG */ + GPIO_PADREGD_PAD15STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGD_PAD15STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGD_PAD15STRNG_Enum; + +/* =========================================== GPIO PADREGD PAD15INPEN [25..25] ============================================ */ +typedef enum { /*!< GPIO_PADREGD_PAD15INPEN */ + GPIO_PADREGD_PAD15INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGD_PAD15INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGD_PAD15INPEN_Enum; + +/* ============================================ GPIO PADREGD PAD15PULL [24..24] ============================================ */ +typedef enum { /*!< GPIO_PADREGD_PAD15PULL */ + GPIO_PADREGD_PAD15PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGD_PAD15PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGD_PAD15PULL_Enum; + +/* =========================================== GPIO PADREGD PAD14FNCSEL [19..21] =========================================== */ +typedef enum { /*!< GPIO_PADREGD_PAD14FNCSEL */ + GPIO_PADREGD_PAD14FNCSEL_ADCD1P = 0, /*!< ADCD1P : Configure as the analog ADC differential pair 1 P input + signal value. */ + GPIO_PADREGD_PAD14FNCSEL_NCE14 = 1, /*!< NCE14 : IOM/MSPI nCE group 14 value. */ + GPIO_PADREGD_PAD14FNCSEL_UART1TX = 2, /*!< UART1TX : Configure as the UART1 TX output signal value. */ + GPIO_PADREGD_PAD14FNCSEL_GPIO14 = 3, /*!< GPIO14 : Configure as GPIO14 value. */ + GPIO_PADREGD_PAD14FNCSEL_PDMCLK = 4, /*!< PDMCLK : PDM serial clock output value. */ + GPIO_PADREGD_PAD14FNCSEL_EXTHFS = 5, /*!< EXTHFS : Configure as the External HFRC oscillator input select + value. */ + GPIO_PADREGD_PAD14FNCSEL_SWDCK = 6, /*!< SWDCK : Configure as the alternate input for the SWDCK input + signal value. */ + GPIO_PADREGD_PAD14FNCSEL_32kHzXT = 7, /*!< 32kHzXT : Configure as the 32kHz crystal output signal value. */ +} GPIO_PADREGD_PAD14FNCSEL_Enum; + +/* =========================================== GPIO PADREGD PAD14STRNG [18..18] ============================================ */ +typedef enum { /*!< GPIO_PADREGD_PAD14STRNG */ + GPIO_PADREGD_PAD14STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGD_PAD14STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGD_PAD14STRNG_Enum; + +/* =========================================== GPIO PADREGD PAD14INPEN [17..17] ============================================ */ +typedef enum { /*!< GPIO_PADREGD_PAD14INPEN */ + GPIO_PADREGD_PAD14INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGD_PAD14INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGD_PAD14INPEN_Enum; + +/* ============================================ GPIO PADREGD PAD14PULL [16..16] ============================================ */ +typedef enum { /*!< GPIO_PADREGD_PAD14PULL */ + GPIO_PADREGD_PAD14PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGD_PAD14PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGD_PAD14PULL_Enum; + +/* =========================================== GPIO PADREGD PAD13FNCSEL [11..13] =========================================== */ +typedef enum { /*!< GPIO_PADREGD_PAD13FNCSEL */ + GPIO_PADREGD_PAD13FNCSEL_ADCD0PSE8 = 0, /*!< ADCD0PSE8 : Configure as the ADC Differential pair 0 P, or Single + Ended input 8 analog input signal. Determination of the + D0P vs SE8 usage is done when the particular channel is + selected within the ADC module value. */ + GPIO_PADREGD_PAD13FNCSEL_NCE13 = 1, /*!< NCE13 : IOM/MSPI nCE group 13 value. */ + GPIO_PADREGD_PAD13FNCSEL_CT2 = 2, /*!< CT2 : CTIMER connection 2 value. */ + GPIO_PADREGD_PAD13FNCSEL_GPIO13 = 3, /*!< GPIO13 : Configure as GPIO13 value. */ + GPIO_PADREGD_PAD13FNCSEL_I2SBCLK = 4, /*!< I2SBCLK : I2C interface bit clock value. */ + GPIO_PADREGD_PAD13FNCSEL_EXTHFB = 5, /*!< EXTHFB : Configure as the external HFRC oscillator input value. */ + GPIO_PADREGD_PAD13FNCSEL_UA0RTS = 6, /*!< UA0RTS : Configure as the UART0 RTS signal output value. */ + GPIO_PADREGD_PAD13FNCSEL_UART1RX = 7, /*!< UART1RX : Configure as the UART1 RX input signal value. */ +} GPIO_PADREGD_PAD13FNCSEL_Enum; + +/* =========================================== GPIO PADREGD PAD13STRNG [10..10] ============================================ */ +typedef enum { /*!< GPIO_PADREGD_PAD13STRNG */ + GPIO_PADREGD_PAD13STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGD_PAD13STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGD_PAD13STRNG_Enum; + +/* ============================================ GPIO PADREGD PAD13INPEN [9..9] ============================================= */ +typedef enum { /*!< GPIO_PADREGD_PAD13INPEN */ + GPIO_PADREGD_PAD13INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGD_PAD13INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGD_PAD13INPEN_Enum; + +/* ============================================= GPIO PADREGD PAD13PULL [8..8] ============================================= */ +typedef enum { /*!< GPIO_PADREGD_PAD13PULL */ + GPIO_PADREGD_PAD13PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGD_PAD13PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGD_PAD13PULL_Enum; + +/* ============================================ GPIO PADREGD PAD12FNCSEL [3..5] ============================================ */ +typedef enum { /*!< GPIO_PADREGD_PAD12FNCSEL */ + GPIO_PADREGD_PAD12FNCSEL_ADCD0NSE9 = 0, /*!< ADCD0NSE9 : Configure as the ADC Differential pair 0 N, or Single + Ended input 9 analog input signal. Determination of the + D0N vs SE9 usage is done when the particular channel is + selected within the ADC module value. */ + GPIO_PADREGD_PAD12FNCSEL_NCE12 = 1, /*!< NCE12 : IOM/MSPI nCE group 12 value. */ + GPIO_PADREGD_PAD12FNCSEL_CT0 = 2, /*!< CT0 : CTIMER connection 0 value. */ + GPIO_PADREGD_PAD12FNCSEL_GPIO12 = 3, /*!< GPIO12 : Configure as GPIO12 value. */ + GPIO_PADREGD_PAD12FNCSEL_SLnCE = 4, /*!< SLnCE : Configure as the IOSLAVE SPI nCE signal value. */ + GPIO_PADREGD_PAD12FNCSEL_PDMCLK = 5, /*!< PDMCLK : PDM serial clock output value. */ + GPIO_PADREGD_PAD12FNCSEL_UA0CTS = 6, /*!< UA0CTS : Configure as the UART0 CTS input signal value. */ + GPIO_PADREGD_PAD12FNCSEL_UART1TX = 7, /*!< UART1TX : Configure as the UART1 TX output signal value. */ +} GPIO_PADREGD_PAD12FNCSEL_Enum; + +/* ============================================ GPIO PADREGD PAD12STRNG [2..2] ============================================= */ +typedef enum { /*!< GPIO_PADREGD_PAD12STRNG */ + GPIO_PADREGD_PAD12STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGD_PAD12STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGD_PAD12STRNG_Enum; + +/* ============================================ GPIO PADREGD PAD12INPEN [1..1] ============================================= */ +typedef enum { /*!< GPIO_PADREGD_PAD12INPEN */ + GPIO_PADREGD_PAD12INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGD_PAD12INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGD_PAD12INPEN_Enum; + +/* ============================================= GPIO PADREGD PAD12PULL [0..0] ============================================= */ +typedef enum { /*!< GPIO_PADREGD_PAD12PULL */ + GPIO_PADREGD_PAD12PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGD_PAD12PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGD_PAD12PULL_Enum; + +/* ======================================================== PADREGE ======================================================== */ +/* =========================================== GPIO PADREGE PAD19FNCSEL [27..29] =========================================== */ +typedef enum { /*!< GPIO_PADREGE_PAD19FNCSEL */ + GPIO_PADREGE_PAD19FNCSEL_CMPRF0 = 0, /*!< CMPRF0 : Configure as the analog comparator reference 0 signal + value. */ + GPIO_PADREGE_PAD19FNCSEL_NCE19 = 1, /*!< NCE19 : IOM/MSPI nCE group 19 value. */ + GPIO_PADREGE_PAD19FNCSEL_CT6 = 2, /*!< CT6 : CTIMER conenction 6 value. */ + GPIO_PADREGE_PAD19FNCSEL_GPIO19 = 3, /*!< GPIO19 : Configure as GPIO19 value. */ + GPIO_PADREGE_PAD19FNCSEL_SCCLK = 4, /*!< SCCLK : SCARD serial clock value. */ + GPIO_PADREGE_PAD19FNCSEL_ANATEST1 = 5, /*!< ANATEST1 : Configure as the ANATEST1 I/O signal value. */ + GPIO_PADREGE_PAD19FNCSEL_UART1RX = 6, /*!< UART1RX : Configure as the UART1 RX input signal value. */ + GPIO_PADREGE_PAD19FNCSEL_I2SBCLK = 7, /*!< I2SBCLK : Configure as the PDM I2S bit clock input signal value. */ +} GPIO_PADREGE_PAD19FNCSEL_Enum; + +/* =========================================== GPIO PADREGE PAD19STRNG [26..26] ============================================ */ +typedef enum { /*!< GPIO_PADREGE_PAD19STRNG */ + GPIO_PADREGE_PAD19STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGE_PAD19STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGE_PAD19STRNG_Enum; + +/* =========================================== GPIO PADREGE PAD19INPEN [25..25] ============================================ */ +typedef enum { /*!< GPIO_PADREGE_PAD19INPEN */ + GPIO_PADREGE_PAD19INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGE_PAD19INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGE_PAD19INPEN_Enum; + +/* ============================================ GPIO PADREGE PAD19PULL [24..24] ============================================ */ +typedef enum { /*!< GPIO_PADREGE_PAD19PULL */ + GPIO_PADREGE_PAD19PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGE_PAD19PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGE_PAD19PULL_Enum; + +/* =========================================== GPIO PADREGE PAD18FNCSEL [19..21] =========================================== */ +typedef enum { /*!< GPIO_PADREGE_PAD18FNCSEL */ + GPIO_PADREGE_PAD18FNCSEL_CMPIN1 = 0, /*!< CMPIN1 : Configure as the analog comparator input 1 signal value. */ + GPIO_PADREGE_PAD18FNCSEL_NCE18 = 1, /*!< NCE18 : IOM/MSPI nCE group 18 value. */ + GPIO_PADREGE_PAD18FNCSEL_CT4 = 2, /*!< CT4 : CTIMER connection 4 value. */ + GPIO_PADREGE_PAD18FNCSEL_GPIO18 = 3, /*!< GPIO18 : Configure as GPIO18 value. */ + GPIO_PADREGE_PAD18FNCSEL_UA0RTS = 4, /*!< UA0RTS : Configure as UART0 RTS output signal value. */ + GPIO_PADREGE_PAD18FNCSEL_ANATEST2 = 5, /*!< ANATEST2 : Configure as ANATEST2 I/O signal value. */ + GPIO_PADREGE_PAD18FNCSEL_UART1TX = 6, /*!< UART1TX : Configure as UART1 TX output signal value. */ + GPIO_PADREGE_PAD18FNCSEL_SCCIO = 7, /*!< SCCIO : SCARD data input/output connectin value. */ +} GPIO_PADREGE_PAD18FNCSEL_Enum; + +/* =========================================== GPIO PADREGE PAD18STRNG [18..18] ============================================ */ +typedef enum { /*!< GPIO_PADREGE_PAD18STRNG */ + GPIO_PADREGE_PAD18STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGE_PAD18STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGE_PAD18STRNG_Enum; + +/* =========================================== GPIO PADREGE PAD18INPEN [17..17] ============================================ */ +typedef enum { /*!< GPIO_PADREGE_PAD18INPEN */ + GPIO_PADREGE_PAD18INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGE_PAD18INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGE_PAD18INPEN_Enum; + +/* ============================================ GPIO PADREGE PAD18PULL [16..16] ============================================ */ +typedef enum { /*!< GPIO_PADREGE_PAD18PULL */ + GPIO_PADREGE_PAD18PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGE_PAD18PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGE_PAD18PULL_Enum; + +/* =========================================== GPIO PADREGE PAD17FNCSEL [11..13] =========================================== */ +typedef enum { /*!< GPIO_PADREGE_PAD17FNCSEL */ + GPIO_PADREGE_PAD17FNCSEL_CMPRF1 = 0, /*!< CMPRF1 : Configure as the analog comparator reference signal + 1 input signal value. */ + GPIO_PADREGE_PAD17FNCSEL_NCE17 = 1, /*!< NCE17 : IOM/MSPI nCE group 17 value. */ + GPIO_PADREGE_PAD17FNCSEL_TRIG1 = 2, /*!< TRIG1 : Configure as the ADC Trigger 1 signal value. */ + GPIO_PADREGE_PAD17FNCSEL_GPIO17 = 3, /*!< GPIO17 : Configure as GPIO17 value. */ + GPIO_PADREGE_PAD17FNCSEL_SCCCLK = 4, /*!< SCCCLK : SCARD serial clock output value. */ + GPIO_PADREGE_PAD17FNCSEL_UART0RX = 6, /*!< UART0RX : Configure as UART0 RX input signal value. */ + GPIO_PADREGE_PAD17FNCSEL_UA1CTS = 7, /*!< UA1CTS : Configure as UART1 CTS input signal value. */ +} GPIO_PADREGE_PAD17FNCSEL_Enum; + +/* =========================================== GPIO PADREGE PAD17STRNG [10..10] ============================================ */ +typedef enum { /*!< GPIO_PADREGE_PAD17STRNG */ + GPIO_PADREGE_PAD17STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGE_PAD17STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGE_PAD17STRNG_Enum; + +/* ============================================ GPIO PADREGE PAD17INPEN [9..9] ============================================= */ +typedef enum { /*!< GPIO_PADREGE_PAD17INPEN */ + GPIO_PADREGE_PAD17INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGE_PAD17INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGE_PAD17INPEN_Enum; + +/* ============================================= GPIO PADREGE PAD17PULL [8..8] ============================================= */ +typedef enum { /*!< GPIO_PADREGE_PAD17PULL */ + GPIO_PADREGE_PAD17PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGE_PAD17PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGE_PAD17PULL_Enum; + +/* ============================================ GPIO PADREGE PAD16FNCSEL [3..5] ============================================ */ +typedef enum { /*!< GPIO_PADREGE_PAD16FNCSEL */ + GPIO_PADREGE_PAD16FNCSEL_ADCSE0 = 0, /*!< ADCSE0 : Configure as the analog ADC single ended port 0 input + signal value. */ + GPIO_PADREGE_PAD16FNCSEL_NCE16 = 1, /*!< NCE16 : IOM/MSPI nCE group 16 value. */ + GPIO_PADREGE_PAD16FNCSEL_TRIG0 = 2, /*!< TRIG0 : Configure as the ADC Trigger 0 signal value. */ + GPIO_PADREGE_PAD16FNCSEL_GPIO16 = 3, /*!< GPIO16 : Configure as GPIO16 value. */ + GPIO_PADREGE_PAD16FNCSEL_SCCRST = 4, /*!< SCCRST : SCARD reset output value. */ + GPIO_PADREGE_PAD16FNCSEL_CMPIN0 = 5, /*!< CMPIN0 : Configure as comparator input 0 signal value. */ + GPIO_PADREGE_PAD16FNCSEL_UART0TX = 6, /*!< UART0TX : Configure as UART0 TX output signal value. */ + GPIO_PADREGE_PAD16FNCSEL_UA1RTS = 7, /*!< UA1RTS : Configure as UART1 RTS output signal value. */ +} GPIO_PADREGE_PAD16FNCSEL_Enum; + +/* ============================================ GPIO PADREGE PAD16STRNG [2..2] ============================================= */ +typedef enum { /*!< GPIO_PADREGE_PAD16STRNG */ + GPIO_PADREGE_PAD16STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGE_PAD16STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGE_PAD16STRNG_Enum; + +/* ============================================ GPIO PADREGE PAD16INPEN [1..1] ============================================= */ +typedef enum { /*!< GPIO_PADREGE_PAD16INPEN */ + GPIO_PADREGE_PAD16INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGE_PAD16INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGE_PAD16INPEN_Enum; + +/* ============================================= GPIO PADREGE PAD16PULL [0..0] ============================================= */ +typedef enum { /*!< GPIO_PADREGE_PAD16PULL */ + GPIO_PADREGE_PAD16PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGE_PAD16PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGE_PAD16PULL_Enum; + +/* ======================================================== PADREGF ======================================================== */ +/* =========================================== GPIO PADREGF PAD23FNCSEL [27..29] =========================================== */ +typedef enum { /*!< GPIO_PADREGF_PAD23FNCSEL */ + GPIO_PADREGF_PAD23FNCSEL_UART0RX = 0, /*!< UART0RX : Configure as the UART0 RX signal value. */ + GPIO_PADREGF_PAD23FNCSEL_NCE23 = 1, /*!< NCE23 : IOM/MSPI nCE group 23 value. */ + GPIO_PADREGF_PAD23FNCSEL_CT14 = 2, /*!< CT14 : CTIMER connection 14 value. */ + GPIO_PADREGF_PAD23FNCSEL_GPIO23 = 3, /*!< GPIO23 : Configure as GPIO23 value. */ + GPIO_PADREGF_PAD23FNCSEL_I2SWCLK = 4, /*!< I2SWCLK : I2S word clock input value. */ + GPIO_PADREGF_PAD23FNCSEL_CMPOUT = 5, /*!< CMPOUT : Configure as voltage comparitor output value. */ + GPIO_PADREGF_PAD23FNCSEL_MSPI3 = 6, /*!< MSPI3 : MSPI data connection 3 value. */ + GPIO_PADREGF_PAD23FNCSEL_EXTXT = 7, /*!< EXTXT : External XTAL osacillatgor input value. */ +} GPIO_PADREGF_PAD23FNCSEL_Enum; + +/* =========================================== GPIO PADREGF PAD23STRNG [26..26] ============================================ */ +typedef enum { /*!< GPIO_PADREGF_PAD23STRNG */ + GPIO_PADREGF_PAD23STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGF_PAD23STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGF_PAD23STRNG_Enum; + +/* =========================================== GPIO PADREGF PAD23INPEN [25..25] ============================================ */ +typedef enum { /*!< GPIO_PADREGF_PAD23INPEN */ + GPIO_PADREGF_PAD23INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGF_PAD23INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGF_PAD23INPEN_Enum; + +/* ============================================ GPIO PADREGF PAD23PULL [24..24] ============================================ */ +typedef enum { /*!< GPIO_PADREGF_PAD23PULL */ + GPIO_PADREGF_PAD23PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGF_PAD23PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGF_PAD23PULL_Enum; + +/* =========================================== GPIO PADREGF PAD22FNCSEL [19..21] =========================================== */ +typedef enum { /*!< GPIO_PADREGF_PAD22FNCSEL */ + GPIO_PADREGF_PAD22FNCSEL_UART0TX = 0, /*!< UART0TX : Configure as the UART0 TX signal value. */ + GPIO_PADREGF_PAD22FNCSEL_NCE22 = 1, /*!< NCE22 : IOM/MSPI nCE group 22 value. */ + GPIO_PADREGF_PAD22FNCSEL_CT12 = 2, /*!< CT12 : CTIMER connection 12 value. */ + GPIO_PADREGF_PAD22FNCSEL_GPIO22 = 3, /*!< GPIO22 : Configure as GPIO22 value. */ + GPIO_PADREGF_PAD22FNCSEL_PDM_CLK = 4, /*!< PDM_CLK : Configure as the PDM CLK output value. */ + GPIO_PADREGF_PAD22FNCSEL_EXTLF = 5, /*!< EXTLF : External LFRC input value. */ + GPIO_PADREGF_PAD22FNCSEL_MSPI0 = 6, /*!< MSPI0 : MSPI data connection 0 value. */ + GPIO_PADREGF_PAD22FNCSEL_SWO = 7, /*!< SWO : Configure as the serial trace data output signal value. */ +} GPIO_PADREGF_PAD22FNCSEL_Enum; + +/* =========================================== GPIO PADREGF PAD22STRNG [18..18] ============================================ */ +typedef enum { /*!< GPIO_PADREGF_PAD22STRNG */ + GPIO_PADREGF_PAD22STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGF_PAD22STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGF_PAD22STRNG_Enum; + +/* =========================================== GPIO PADREGF PAD22INPEN [17..17] ============================================ */ +typedef enum { /*!< GPIO_PADREGF_PAD22INPEN */ + GPIO_PADREGF_PAD22INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGF_PAD22INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGF_PAD22INPEN_Enum; + +/* ============================================ GPIO PADREGF PAD22PULL [16..16] ============================================ */ +typedef enum { /*!< GPIO_PADREGF_PAD22PULL */ + GPIO_PADREGF_PAD22PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGF_PAD22PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGF_PAD22PULL_Enum; + +/* =========================================== GPIO PADREGF PAD21FNCSEL [11..13] =========================================== */ +typedef enum { /*!< GPIO_PADREGF_PAD21FNCSEL */ + GPIO_PADREGF_PAD21FNCSEL_SWDIO = 0, /*!< SWDIO : Configure as the serial wire debug data signal value. */ + GPIO_PADREGF_PAD21FNCSEL_NCE21 = 1, /*!< NCE21 : IOM/MSPI nCE group 21 value. */ + GPIO_PADREGF_PAD21FNCSEL_GPIO21 = 3, /*!< GPIO21 : Configure as GPIO21 value. */ + GPIO_PADREGF_PAD21FNCSEL_UART0RX = 4, /*!< UART0RX : Configure as UART0 RX input signal value. */ + GPIO_PADREGF_PAD21FNCSEL_UART1RX = 5, /*!< UART1RX : Configure as UART1 RX input signal value. */ + GPIO_PADREGF_PAD21FNCSEL_I2SBCLK = 6, /*!< I2SBCLK : I2S byte clock input value. */ + GPIO_PADREGF_PAD21FNCSEL_UA1CTS = 7, /*!< UA1CTS : Configure as UART1 CTS input signal value. */ +} GPIO_PADREGF_PAD21FNCSEL_Enum; + +/* =========================================== GPIO PADREGF PAD21STRNG [10..10] ============================================ */ +typedef enum { /*!< GPIO_PADREGF_PAD21STRNG */ + GPIO_PADREGF_PAD21STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGF_PAD21STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGF_PAD21STRNG_Enum; + +/* ============================================ GPIO PADREGF PAD21INPEN [9..9] ============================================= */ +typedef enum { /*!< GPIO_PADREGF_PAD21INPEN */ + GPIO_PADREGF_PAD21INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGF_PAD21INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGF_PAD21INPEN_Enum; + +/* ============================================= GPIO PADREGF PAD21PULL [8..8] ============================================= */ +typedef enum { /*!< GPIO_PADREGF_PAD21PULL */ + GPIO_PADREGF_PAD21PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGF_PAD21PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGF_PAD21PULL_Enum; + +/* ============================================ GPIO PADREGF PAD20FNCSEL [3..5] ============================================ */ +typedef enum { /*!< GPIO_PADREGF_PAD20FNCSEL */ + GPIO_PADREGF_PAD20FNCSEL_SWDCK = 0, /*!< SWDCK : Configure as the serial wire debug clock signal value. */ + GPIO_PADREGF_PAD20FNCSEL_NCE20 = 1, /*!< NCE20 : IOM/MSPI nCE group 20 value. */ + GPIO_PADREGF_PAD20FNCSEL_GPIO20 = 3, /*!< GPIO20 : Configure as GPIO20 value. */ + GPIO_PADREGF_PAD20FNCSEL_UART0TX = 4, /*!< UART0TX : Configure as UART0 TX output signal value. */ + GPIO_PADREGF_PAD20FNCSEL_UART1TX = 5, /*!< UART1TX : Configure as UART1 TX output signal value. */ + GPIO_PADREGF_PAD20FNCSEL_I2SBCLK = 6, /*!< I2SBCLK : I2S byte clock input value. */ + GPIO_PADREGF_PAD20FNCSEL_UA1RTS = 7, /*!< UA1RTS : Configure as UART1 RTS output signal value. */ +} GPIO_PADREGF_PAD20FNCSEL_Enum; + +/* ============================================ GPIO PADREGF PAD20STRNG [2..2] ============================================= */ +typedef enum { /*!< GPIO_PADREGF_PAD20STRNG */ + GPIO_PADREGF_PAD20STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGF_PAD20STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGF_PAD20STRNG_Enum; + +/* ============================================ GPIO PADREGF PAD20INPEN [1..1] ============================================= */ +typedef enum { /*!< GPIO_PADREGF_PAD20INPEN */ + GPIO_PADREGF_PAD20INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGF_PAD20INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGF_PAD20INPEN_Enum; + +/* ============================================= GPIO PADREGF PAD20PULL [0..0] ============================================= */ +typedef enum { /*!< GPIO_PADREGF_PAD20PULL */ + GPIO_PADREGF_PAD20PULL_DIS = 0, /*!< DIS : Pulldown disabled value. */ + GPIO_PADREGF_PAD20PULL_EN = 1, /*!< EN : Pulldown enabled value. */ +} GPIO_PADREGF_PAD20PULL_Enum; + +/* ======================================================== PADREGG ======================================================== */ +/* ============================================ GPIO PADREGG PAD27RSEL [30..31] ============================================ */ +typedef enum { /*!< GPIO_PADREGG_PAD27RSEL */ + GPIO_PADREGG_PAD27RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ + GPIO_PADREGG_PAD27RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ + GPIO_PADREGG_PAD27RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ + GPIO_PADREGG_PAD27RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ +} GPIO_PADREGG_PAD27RSEL_Enum; + +/* =========================================== GPIO PADREGG PAD27FNCSEL [27..29] =========================================== */ +typedef enum { /*!< GPIO_PADREGG_PAD27FNCSEL */ + GPIO_PADREGG_PAD27FNCSEL_UART0RX = 0, /*!< UART0RX : Configure as UART0 RX input signal value. */ + GPIO_PADREGG_PAD27FNCSEL_NCE27 = 1, /*!< NCE27 : IOM/MSPI nCE group 27 value. */ + GPIO_PADREGG_PAD27FNCSEL_CT5 = 2, /*!< CT5 : CTIMER connection 5 value. */ + GPIO_PADREGG_PAD27FNCSEL_GPIO27 = 3, /*!< GPIO27 : Configure as GPIO27 value. */ + GPIO_PADREGG_PAD27FNCSEL_M2SCL = 4, /*!< M2SCL : Configure as I2C clock I/O signal from IOMSTR2 value. */ + GPIO_PADREGG_PAD27FNCSEL_M2SCK = 5, /*!< M2SCK : Configure as SPI clock output signal from IOMSTR2 value. */ +} GPIO_PADREGG_PAD27FNCSEL_Enum; + +/* =========================================== GPIO PADREGG PAD27STRNG [26..26] ============================================ */ +typedef enum { /*!< GPIO_PADREGG_PAD27STRNG */ + GPIO_PADREGG_PAD27STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGG_PAD27STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGG_PAD27STRNG_Enum; + +/* =========================================== GPIO PADREGG PAD27INPEN [25..25] ============================================ */ +typedef enum { /*!< GPIO_PADREGG_PAD27INPEN */ + GPIO_PADREGG_PAD27INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGG_PAD27INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGG_PAD27INPEN_Enum; + +/* ============================================ GPIO PADREGG PAD27PULL [24..24] ============================================ */ +typedef enum { /*!< GPIO_PADREGG_PAD27PULL */ + GPIO_PADREGG_PAD27PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGG_PAD27PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGG_PAD27PULL_Enum; + +/* =========================================== GPIO PADREGG PAD26FNCSEL [19..21] =========================================== */ +typedef enum { /*!< GPIO_PADREGG_PAD26FNCSEL */ + GPIO_PADREGG_PAD26FNCSEL_EXTHF = 0, /*!< EXTHF : Configure as the external HFRC oscillator input value. */ + GPIO_PADREGG_PAD26FNCSEL_NCE26 = 1, /*!< NCE26 : IOM/MSPI nCE group 26 value. */ + GPIO_PADREGG_PAD26FNCSEL_CT3 = 2, /*!< CT3 : CTIMER connection 3 value. */ + GPIO_PADREGG_PAD26FNCSEL_GPIO26 = 3, /*!< GPIO26 : Configure as GPIO26 value. */ + GPIO_PADREGG_PAD26FNCSEL_SCCRST = 4, /*!< SCCRST : SCARD reset output value. */ + GPIO_PADREGG_PAD26FNCSEL_MSPI1 = 5, /*!< MSPI1 : MSPI data connection 1 value. */ + GPIO_PADREGG_PAD26FNCSEL_UART0TX = 6, /*!< UART0TX : Configure as UART0 TX output signal value. */ + GPIO_PADREGG_PAD26FNCSEL_UA1CTS = 7, /*!< UA1CTS : Configure as UART1 CTS input signal value. */ +} GPIO_PADREGG_PAD26FNCSEL_Enum; + +/* =========================================== GPIO PADREGG PAD26STRNG [18..18] ============================================ */ +typedef enum { /*!< GPIO_PADREGG_PAD26STRNG */ + GPIO_PADREGG_PAD26STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGG_PAD26STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGG_PAD26STRNG_Enum; + +/* =========================================== GPIO PADREGG PAD26INPEN [17..17] ============================================ */ +typedef enum { /*!< GPIO_PADREGG_PAD26INPEN */ + GPIO_PADREGG_PAD26INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGG_PAD26INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGG_PAD26INPEN_Enum; + +/* ============================================ GPIO PADREGG PAD26PULL [16..16] ============================================ */ +typedef enum { /*!< GPIO_PADREGG_PAD26PULL */ + GPIO_PADREGG_PAD26PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGG_PAD26PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGG_PAD26PULL_Enum; + +/* ============================================ GPIO PADREGG PAD25RSEL [14..15] ============================================ */ +typedef enum { /*!< GPIO_PADREGG_PAD25RSEL */ + GPIO_PADREGG_PAD25RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ + GPIO_PADREGG_PAD25RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ + GPIO_PADREGG_PAD25RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ + GPIO_PADREGG_PAD25RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ +} GPIO_PADREGG_PAD25RSEL_Enum; + +/* =========================================== GPIO PADREGG PAD25FNCSEL [11..13] =========================================== */ +typedef enum { /*!< GPIO_PADREGG_PAD25FNCSEL */ + GPIO_PADREGG_PAD25FNCSEL_UART1RX = 0, /*!< UART1RX : Configure as UART1 RX input signal value. */ + GPIO_PADREGG_PAD25FNCSEL_NCE25 = 1, /*!< NCE25 : IOM/MSPI nCE group 25 value. */ + GPIO_PADREGG_PAD25FNCSEL_CT1 = 2, /*!< CT1 : CTIMER connection 1 value. */ + GPIO_PADREGG_PAD25FNCSEL_GPIO25 = 3, /*!< GPIO25 : Configure as GPIO25 value. */ + GPIO_PADREGG_PAD25FNCSEL_M2SDAWIR3 = 4, /*!< M2SDAWIR3 : Configure as the IOMSTR2 I2C SDA or SPI WIR3 signal + value. */ + GPIO_PADREGG_PAD25FNCSEL_M2MISO = 5, /*!< M2MISO : Configure as the IOMSTR2 SPI MISO input signal value. */ +} GPIO_PADREGG_PAD25FNCSEL_Enum; + +/* =========================================== GPIO PADREGG PAD25STRNG [10..10] ============================================ */ +typedef enum { /*!< GPIO_PADREGG_PAD25STRNG */ + GPIO_PADREGG_PAD25STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGG_PAD25STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGG_PAD25STRNG_Enum; + +/* ============================================ GPIO PADREGG PAD25INPEN [9..9] ============================================= */ +typedef enum { /*!< GPIO_PADREGG_PAD25INPEN */ + GPIO_PADREGG_PAD25INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGG_PAD25INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGG_PAD25INPEN_Enum; + +/* ============================================= GPIO PADREGG PAD25PULL [8..8] ============================================= */ +typedef enum { /*!< GPIO_PADREGG_PAD25PULL */ + GPIO_PADREGG_PAD25PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGG_PAD25PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGG_PAD25PULL_Enum; + +/* ============================================ GPIO PADREGG PAD24FNCSEL [3..5] ============================================ */ +typedef enum { /*!< GPIO_PADREGG_PAD24FNCSEL */ + GPIO_PADREGG_PAD24FNCSEL_UART1TX = 0, /*!< UART1TX : Configure as UART1 TX output signal value. */ + GPIO_PADREGG_PAD24FNCSEL_NCE24 = 1, /*!< NCE24 : IOM/MSPI nCE group 24 value. */ + GPIO_PADREGG_PAD24FNCSEL_MSPI8 = 2, /*!< MSPI8 : MSPI data connection 8 value. */ + GPIO_PADREGG_PAD24FNCSEL_GPIO24 = 3, /*!< GPIO24 : Configure as GPIO24 value. */ + GPIO_PADREGG_PAD24FNCSEL_UA0CTS = 4, /*!< UA0CTS : Configure as UART0 CTS input signal value. */ + GPIO_PADREGG_PAD24FNCSEL_CT21 = 5, /*!< CT21 : CTIMER connection 21 value. */ + GPIO_PADREGG_PAD24FNCSEL_32kHzXT = 6, /*!< 32kHzXT : Configure as the 32kHz crystal output signal value. */ + GPIO_PADREGG_PAD24FNCSEL_SWO = 7, /*!< SWO : Configure as the serial trace data output signal value. */ +} GPIO_PADREGG_PAD24FNCSEL_Enum; + +/* ============================================ GPIO PADREGG PAD24STRNG [2..2] ============================================= */ +typedef enum { /*!< GPIO_PADREGG_PAD24STRNG */ + GPIO_PADREGG_PAD24STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGG_PAD24STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGG_PAD24STRNG_Enum; + +/* ============================================ GPIO PADREGG PAD24INPEN [1..1] ============================================= */ +typedef enum { /*!< GPIO_PADREGG_PAD24INPEN */ + GPIO_PADREGG_PAD24INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGG_PAD24INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGG_PAD24INPEN_Enum; + +/* ============================================= GPIO PADREGG PAD24PULL [0..0] ============================================= */ +typedef enum { /*!< GPIO_PADREGG_PAD24PULL */ + GPIO_PADREGG_PAD24PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGG_PAD24PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGG_PAD24PULL_Enum; + +/* ======================================================== PADREGH ======================================================== */ +/* =========================================== GPIO PADREGH PAD31FNCSEL [27..29] =========================================== */ +typedef enum { /*!< GPIO_PADREGH_PAD31FNCSEL */ + GPIO_PADREGH_PAD31FNCSEL_ADCSE3 = 0, /*!< ADCSE3 : Configure as the analog input for ADC single ended + input 3 value. */ + GPIO_PADREGH_PAD31FNCSEL_NCE31 = 1, /*!< NCE31 : IOM/MSPI nCE group 31 value. */ + GPIO_PADREGH_PAD31FNCSEL_CT13 = 2, /*!< CT13 : CTIMER connection 13 value. */ + GPIO_PADREGH_PAD31FNCSEL_GPIO31 = 3, /*!< GPIO31 : Configure as GPIO31 value. */ + GPIO_PADREGH_PAD31FNCSEL_UART0RX = 4, /*!< UART0RX : Configure as the UART0 RX input signal value. */ + GPIO_PADREGH_PAD31FNCSEL_SCCCLK = 5, /*!< SCCCLK : SCARD serial clock output value. */ + GPIO_PADREGH_PAD31FNCSEL_UA1RTS = 7, /*!< UA1RTS : Configure as UART1 RTS output signal value. */ +} GPIO_PADREGH_PAD31FNCSEL_Enum; + +/* =========================================== GPIO PADREGH PAD31STRNG [26..26] ============================================ */ +typedef enum { /*!< GPIO_PADREGH_PAD31STRNG */ + GPIO_PADREGH_PAD31STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGH_PAD31STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGH_PAD31STRNG_Enum; + +/* =========================================== GPIO PADREGH PAD31INPEN [25..25] ============================================ */ +typedef enum { /*!< GPIO_PADREGH_PAD31INPEN */ + GPIO_PADREGH_PAD31INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGH_PAD31INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGH_PAD31INPEN_Enum; + +/* ============================================ GPIO PADREGH PAD31PULL [24..24] ============================================ */ +typedef enum { /*!< GPIO_PADREGH_PAD31PULL */ + GPIO_PADREGH_PAD31PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGH_PAD31PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGH_PAD31PULL_Enum; + +/* =========================================== GPIO PADREGH PAD30FNCSEL [19..21] =========================================== */ +typedef enum { /*!< GPIO_PADREGH_PAD30FNCSEL */ + GPIO_PADREGH_PAD30FNCSEL_ANATEST1 = 0, /*!< ANATEST1 : Configure as the ANATEST1 I/O signal value. */ + GPIO_PADREGH_PAD30FNCSEL_NCE30 = 1, /*!< NCE30 : IOM/MSPI nCE group 30 value. */ + GPIO_PADREGH_PAD30FNCSEL_CT11 = 2, /*!< CT11 : CTIMER connection 11 value. */ + GPIO_PADREGH_PAD30FNCSEL_GPIO30 = 3, /*!< GPIO30 : Configure as GPIO30 value. */ + GPIO_PADREGH_PAD30FNCSEL_UART0TX = 4, /*!< UART0TX : Configure as UART0 TX output signal value. */ + GPIO_PADREGH_PAD30FNCSEL_UA1RTS = 5, /*!< UA1RTS : Configure as UART1 RTS output signal value. */ + GPIO_PADREGH_PAD30FNCSEL_I2S_DAT = 7, /*!< I2S_DAT : Configure as the PDM I2S Data output signal value. */ +} GPIO_PADREGH_PAD30FNCSEL_Enum; + +/* =========================================== GPIO PADREGH PAD30STRNG [18..18] ============================================ */ +typedef enum { /*!< GPIO_PADREGH_PAD30STRNG */ + GPIO_PADREGH_PAD30STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGH_PAD30STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGH_PAD30STRNG_Enum; + +/* =========================================== GPIO PADREGH PAD30INPEN [17..17] ============================================ */ +typedef enum { /*!< GPIO_PADREGH_PAD30INPEN */ + GPIO_PADREGH_PAD30INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGH_PAD30INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGH_PAD30INPEN_Enum; + +/* ============================================ GPIO PADREGH PAD30PULL [16..16] ============================================ */ +typedef enum { /*!< GPIO_PADREGH_PAD30PULL */ + GPIO_PADREGH_PAD30PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGH_PAD30PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGH_PAD30PULL_Enum; + +/* =========================================== GPIO PADREGH PAD29FNCSEL [11..13] =========================================== */ +typedef enum { /*!< GPIO_PADREGH_PAD29FNCSEL */ + GPIO_PADREGH_PAD29FNCSEL_ADCSE1 = 0, /*!< ADCSE1 : Configure as the analog input for ADC single ended + input 1 value. */ + GPIO_PADREGH_PAD29FNCSEL_NCE29 = 1, /*!< NCE29 : IOM/MSPI nCE group 29 value. */ + GPIO_PADREGH_PAD29FNCSEL_CT9 = 2, /*!< CT9 : CTIMER connection 9 value. */ + GPIO_PADREGH_PAD29FNCSEL_GPIO29 = 3, /*!< GPIO29 : Configure as GPIO29 value. */ + GPIO_PADREGH_PAD29FNCSEL_UA0CTS = 4, /*!< UA0CTS : Configure as the UART0 CTS input signal value. */ + GPIO_PADREGH_PAD29FNCSEL_UA1CTS = 5, /*!< UA1CTS : Configure as the UART1 CTS input signal value. */ + GPIO_PADREGH_PAD29FNCSEL_UART0RX = 6, /*!< UART0RX : Configure as the UART0 RX input signal value. */ + GPIO_PADREGH_PAD29FNCSEL_PDM_DATA = 7, /*!< PDM_DATA : Configure as PDM DATA input value. */ +} GPIO_PADREGH_PAD29FNCSEL_Enum; + +/* =========================================== GPIO PADREGH PAD29STRNG [10..10] ============================================ */ +typedef enum { /*!< GPIO_PADREGH_PAD29STRNG */ + GPIO_PADREGH_PAD29STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGH_PAD29STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGH_PAD29STRNG_Enum; + +/* ============================================ GPIO PADREGH PAD29INPEN [9..9] ============================================= */ +typedef enum { /*!< GPIO_PADREGH_PAD29INPEN */ + GPIO_PADREGH_PAD29INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGH_PAD29INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGH_PAD29INPEN_Enum; + +/* ============================================= GPIO PADREGH PAD29PULL [8..8] ============================================= */ +typedef enum { /*!< GPIO_PADREGH_PAD29PULL */ + GPIO_PADREGH_PAD29PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGH_PAD29PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGH_PAD29PULL_Enum; + +/* ============================================ GPIO PADREGH PAD28FNCSEL [3..5] ============================================ */ +typedef enum { /*!< GPIO_PADREGH_PAD28FNCSEL */ + GPIO_PADREGH_PAD28FNCSEL_I2S_WCLK = 0, /*!< I2S_WCLK : Configure as the PDM I2S Word Clock input value. */ + GPIO_PADREGH_PAD28FNCSEL_NCE28 = 1, /*!< NCE28 : IOM/MSPI nCE group 28 value. */ + GPIO_PADREGH_PAD28FNCSEL_CT7 = 2, /*!< CT7 : CTIMER connection 7 value. */ + GPIO_PADREGH_PAD28FNCSEL_GPIO28 = 3, /*!< GPIO28 : Configure as GPIO28 value. */ + GPIO_PADREGH_PAD28FNCSEL_M2MOSI = 5, /*!< M2MOSI : Configure as the IOMSTR2 SPI MOSI output signal value. */ + GPIO_PADREGH_PAD28FNCSEL_UART0TX = 6, /*!< UART0TX : Configure as the UART0 TX output signal value. */ +} GPIO_PADREGH_PAD28FNCSEL_Enum; + +/* ============================================ GPIO PADREGH PAD28STRNG [2..2] ============================================= */ +typedef enum { /*!< GPIO_PADREGH_PAD28STRNG */ + GPIO_PADREGH_PAD28STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGH_PAD28STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGH_PAD28STRNG_Enum; + +/* ============================================ GPIO PADREGH PAD28INPEN [1..1] ============================================= */ +typedef enum { /*!< GPIO_PADREGH_PAD28INPEN */ + GPIO_PADREGH_PAD28INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGH_PAD28INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGH_PAD28INPEN_Enum; + +/* ============================================= GPIO PADREGH PAD28PULL [0..0] ============================================= */ +typedef enum { /*!< GPIO_PADREGH_PAD28PULL */ + GPIO_PADREGH_PAD28PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGH_PAD28PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGH_PAD28PULL_Enum; + +/* ======================================================== PADREGI ======================================================== */ +/* =========================================== GPIO PADREGI PAD35FNCSEL [27..29] =========================================== */ +typedef enum { /*!< GPIO_PADREGI_PAD35FNCSEL */ + GPIO_PADREGI_PAD35FNCSEL_ADCSE7 = 0, /*!< ADCSE7 : Configure as the analog input for ADC single ended + input 7 value. */ + GPIO_PADREGI_PAD35FNCSEL_NCE35 = 1, /*!< NCE35 : IOM/MSPI nCE group 35 value. */ + GPIO_PADREGI_PAD35FNCSEL_UART1TX = 2, /*!< UART1TX : Configure as the UART1 TX signal value. */ + GPIO_PADREGI_PAD35FNCSEL_GPIO35 = 3, /*!< GPIO35 : Configure as GPIO35 value. */ + GPIO_PADREGI_PAD35FNCSEL_I2SDAT = 4, /*!< I2SDAT : I2S serial data output value. */ + GPIO_PADREGI_PAD35FNCSEL_CT27 = 5, /*!< CT27 : CTIMER connection 27 value. */ + GPIO_PADREGI_PAD35FNCSEL_UA0RTS = 6, /*!< UA0RTS : Configure as the UART0 RTS output value. */ +} GPIO_PADREGI_PAD35FNCSEL_Enum; + +/* =========================================== GPIO PADREGI PAD35STRNG [26..26] ============================================ */ +typedef enum { /*!< GPIO_PADREGI_PAD35STRNG */ + GPIO_PADREGI_PAD35STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGI_PAD35STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGI_PAD35STRNG_Enum; + +/* =========================================== GPIO PADREGI PAD35INPEN [25..25] ============================================ */ +typedef enum { /*!< GPIO_PADREGI_PAD35INPEN */ + GPIO_PADREGI_PAD35INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGI_PAD35INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGI_PAD35INPEN_Enum; + +/* ============================================ GPIO PADREGI PAD35PULL [24..24] ============================================ */ +typedef enum { /*!< GPIO_PADREGI_PAD35PULL */ + GPIO_PADREGI_PAD35PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGI_PAD35PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGI_PAD35PULL_Enum; + +/* =========================================== GPIO PADREGI PAD34FNCSEL [19..21] =========================================== */ +typedef enum { /*!< GPIO_PADREGI_PAD34FNCSEL */ + GPIO_PADREGI_PAD34FNCSEL_ADCSE6 = 0, /*!< ADCSE6 : Configure as the analog input for ADC single ended + input 6 value. */ + GPIO_PADREGI_PAD34FNCSEL_NCE34 = 1, /*!< NCE34 : IOM/MSPI nCE group 34 value. */ + GPIO_PADREGI_PAD34FNCSEL_UA1RTS = 2, /*!< UA1RTS : Configure as the UART1 RTS output value. */ + GPIO_PADREGI_PAD34FNCSEL_GPIO34 = 3, /*!< GPIO34 : Configure as GPIO34 value. */ + GPIO_PADREGI_PAD34FNCSEL_CMPRF2 = 4, /*!< CMPRF2 : Configure as the analog comparator reference 2 signal + value. */ + GPIO_PADREGI_PAD34FNCSEL_UA0RTS = 5, /*!< UA0RTS : Configure as the UART0 RTS output value. */ + GPIO_PADREGI_PAD34FNCSEL_UART0RX = 6, /*!< UART0RX : Configure as the UART0 RX input value. */ + GPIO_PADREGI_PAD34FNCSEL_PDMDATA = 7, /*!< PDMDATA : PDM serial data input value. */ +} GPIO_PADREGI_PAD34FNCSEL_Enum; + +/* =========================================== GPIO PADREGI PAD34STRNG [18..18] ============================================ */ +typedef enum { /*!< GPIO_PADREGI_PAD34STRNG */ + GPIO_PADREGI_PAD34STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGI_PAD34STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGI_PAD34STRNG_Enum; + +/* =========================================== GPIO PADREGI PAD34INPEN [17..17] ============================================ */ +typedef enum { /*!< GPIO_PADREGI_PAD34INPEN */ + GPIO_PADREGI_PAD34INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGI_PAD34INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGI_PAD34INPEN_Enum; + +/* ============================================ GPIO PADREGI PAD34PULL [16..16] ============================================ */ +typedef enum { /*!< GPIO_PADREGI_PAD34PULL */ + GPIO_PADREGI_PAD34PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGI_PAD34PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGI_PAD34PULL_Enum; + +/* =========================================== GPIO PADREGI PAD33FNCSEL [11..13] =========================================== */ +typedef enum { /*!< GPIO_PADREGI_PAD33FNCSEL */ + GPIO_PADREGI_PAD33FNCSEL_ADCSE5 = 0, /*!< ADCSE5 : Configure as the analog ADC single ended port 5 input + signal value. */ + GPIO_PADREGI_PAD33FNCSEL_NCE33 = 1, /*!< NCE33 : IOM/MSPI nCE group 33 value. */ + GPIO_PADREGI_PAD33FNCSEL_32kHzXT = 2, /*!< 32kHzXT : Configure as the 32kHz crystal output signal value. */ + GPIO_PADREGI_PAD33FNCSEL_GPIO33 = 3, /*!< GPIO33 : Configure as GPIO33 value. */ + GPIO_PADREGI_PAD33FNCSEL_UA0CTS = 5, /*!< UA0CTS : Configure as the UART0 CTS input value. */ + GPIO_PADREGI_PAD33FNCSEL_CT23 = 6, /*!< CT23 : CTIMER connection 23 value. */ + GPIO_PADREGI_PAD33FNCSEL_SWO = 7, /*!< SWO : Configure as the serial trace data output signal value. */ +} GPIO_PADREGI_PAD33FNCSEL_Enum; + +/* =========================================== GPIO PADREGI PAD33STRNG [10..10] ============================================ */ +typedef enum { /*!< GPIO_PADREGI_PAD33STRNG */ + GPIO_PADREGI_PAD33STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGI_PAD33STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGI_PAD33STRNG_Enum; + +/* ============================================ GPIO PADREGI PAD33INPEN [9..9] ============================================= */ +typedef enum { /*!< GPIO_PADREGI_PAD33INPEN */ + GPIO_PADREGI_PAD33INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGI_PAD33INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGI_PAD33INPEN_Enum; + +/* ============================================= GPIO PADREGI PAD33PULL [8..8] ============================================= */ +typedef enum { /*!< GPIO_PADREGI_PAD33PULL */ + GPIO_PADREGI_PAD33PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGI_PAD33PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGI_PAD33PULL_Enum; + +/* ============================================ GPIO PADREGI PAD32FNCSEL [3..5] ============================================ */ +typedef enum { /*!< GPIO_PADREGI_PAD32FNCSEL */ + GPIO_PADREGI_PAD32FNCSEL_ADCSE4 = 0, /*!< ADCSE4 : Configure as the analog input for ADC single ended + input 4 value. */ + GPIO_PADREGI_PAD32FNCSEL_NCE32 = 1, /*!< NCE32 : IOM/MSPI nCE group 32 value. */ + GPIO_PADREGI_PAD32FNCSEL_CT15 = 2, /*!< CT15 : CTIMER connection 15 value. */ + GPIO_PADREGI_PAD32FNCSEL_GPIO32 = 3, /*!< GPIO32 : Configure as GPIO32 value. */ + GPIO_PADREGI_PAD32FNCSEL_SCCIO = 4, /*!< SCCIO : SCARD serial data input/output value. */ + GPIO_PADREGI_PAD32FNCSEL_EXTLF = 5, /*!< EXTLF : External input to the LFRC oscillator value. */ + GPIO_PADREGI_PAD32FNCSEL_UA1CTS = 7, /*!< UA1CTS : Configure as the UART1 CTS input value. */ +} GPIO_PADREGI_PAD32FNCSEL_Enum; + +/* ============================================ GPIO PADREGI PAD32STRNG [2..2] ============================================= */ +typedef enum { /*!< GPIO_PADREGI_PAD32STRNG */ + GPIO_PADREGI_PAD32STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGI_PAD32STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGI_PAD32STRNG_Enum; + +/* ============================================ GPIO PADREGI PAD32INPEN [1..1] ============================================= */ +typedef enum { /*!< GPIO_PADREGI_PAD32INPEN */ + GPIO_PADREGI_PAD32INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGI_PAD32INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGI_PAD32INPEN_Enum; + +/* ============================================= GPIO PADREGI PAD32PULL [0..0] ============================================= */ +typedef enum { /*!< GPIO_PADREGI_PAD32PULL */ + GPIO_PADREGI_PAD32PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGI_PAD32PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGI_PAD32PULL_Enum; + +/* ======================================================== PADREGJ ======================================================== */ +/* ============================================ GPIO PADREGJ PAD39RSEL [30..31] ============================================ */ +typedef enum { /*!< GPIO_PADREGJ_PAD39RSEL */ + GPIO_PADREGJ_PAD39RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ + GPIO_PADREGJ_PAD39RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ + GPIO_PADREGJ_PAD39RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ + GPIO_PADREGJ_PAD39RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ +} GPIO_PADREGJ_PAD39RSEL_Enum; + +/* =========================================== GPIO PADREGJ PAD39FNCSEL [27..29] =========================================== */ +typedef enum { /*!< GPIO_PADREGJ_PAD39FNCSEL */ + GPIO_PADREGJ_PAD39FNCSEL_UART0TX = 0, /*!< UART0TX : Configure as the UART0 TX output signal value. */ + GPIO_PADREGJ_PAD39FNCSEL_UART1TX = 1, /*!< UART1TX : Configure as the UART1 TX output signal value. */ + GPIO_PADREGJ_PAD39FNCSEL_CT25 = 2, /*!< CT25 : CTIMER connection 25 value. */ + GPIO_PADREGJ_PAD39FNCSEL_GPIO39 = 3, /*!< GPIO39 : Configure as GPIO39 value. */ + GPIO_PADREGJ_PAD39FNCSEL_M4SCL = 4, /*!< M4SCL : Configure as the IOMSTR4 I2C SCL signal value. */ + GPIO_PADREGJ_PAD39FNCSEL_M4SCK = 5, /*!< M4SCK : Configure as the IOMSTR4 SPI SCK signal value. */ +} GPIO_PADREGJ_PAD39FNCSEL_Enum; + +/* =========================================== GPIO PADREGJ PAD39STRNG [26..26] ============================================ */ +typedef enum { /*!< GPIO_PADREGJ_PAD39STRNG */ + GPIO_PADREGJ_PAD39STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGJ_PAD39STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGJ_PAD39STRNG_Enum; + +/* =========================================== GPIO PADREGJ PAD39INPEN [25..25] ============================================ */ +typedef enum { /*!< GPIO_PADREGJ_PAD39INPEN */ + GPIO_PADREGJ_PAD39INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGJ_PAD39INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGJ_PAD39INPEN_Enum; + +/* ============================================ GPIO PADREGJ PAD39PULL [24..24] ============================================ */ +typedef enum { /*!< GPIO_PADREGJ_PAD39PULL */ + GPIO_PADREGJ_PAD39PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGJ_PAD39PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGJ_PAD39PULL_Enum; + +/* =========================================== GPIO PADREGJ PAD38FNCSEL [19..21] =========================================== */ +typedef enum { /*!< GPIO_PADREGJ_PAD38FNCSEL */ + GPIO_PADREGJ_PAD38FNCSEL_TRIG3 = 0, /*!< TRIG3 : Configure as the ADC Trigger 3 signal value. */ + GPIO_PADREGJ_PAD38FNCSEL_NCE38 = 1, /*!< NCE38 : IOM/MSPI nCE group 38 value. */ + GPIO_PADREGJ_PAD38FNCSEL_UA0CTS = 2, /*!< UA0CTS : Configure as the UART0 CTS signal value. */ + GPIO_PADREGJ_PAD38FNCSEL_GPIO38 = 3, /*!< GPIO38 : Configure as GPIO38 value. */ + GPIO_PADREGJ_PAD38FNCSEL_M3MOSI = 5, /*!< M3MOSI : Configure as the IOMSTR3 SPI MOSI output signal value. */ + GPIO_PADREGJ_PAD38FNCSEL_UART1RX = 6, /*!< UART1RX : Configure as the UART1 RX input signal value. */ +} GPIO_PADREGJ_PAD38FNCSEL_Enum; + +/* =========================================== GPIO PADREGJ PAD38STRNG [18..18] ============================================ */ +typedef enum { /*!< GPIO_PADREGJ_PAD38STRNG */ + GPIO_PADREGJ_PAD38STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGJ_PAD38STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGJ_PAD38STRNG_Enum; + +/* =========================================== GPIO PADREGJ PAD38INPEN [17..17] ============================================ */ +typedef enum { /*!< GPIO_PADREGJ_PAD38INPEN */ + GPIO_PADREGJ_PAD38INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGJ_PAD38INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGJ_PAD38INPEN_Enum; + +/* ============================================ GPIO PADREGJ PAD38PULL [16..16] ============================================ */ +typedef enum { /*!< GPIO_PADREGJ_PAD38PULL */ + GPIO_PADREGJ_PAD38PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGJ_PAD38PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGJ_PAD38PULL_Enum; + +/* =========================================== GPIO PADREGJ PAD37PWRDN [15..15] ============================================ */ +typedef enum { /*!< GPIO_PADREGJ_PAD37PWRDN */ + GPIO_PADREGJ_PAD37PWRDN_DIS = 0, /*!< DIS : Power switch disabled value. */ + GPIO_PADREGJ_PAD37PWRDN_EN = 1, /*!< EN : Power switch enabled (switch to GND) value. */ +} GPIO_PADREGJ_PAD37PWRDN_Enum; + +/* =========================================== GPIO PADREGJ PAD37FNCSEL [11..13] =========================================== */ +typedef enum { /*!< GPIO_PADREGJ_PAD37FNCSEL */ + GPIO_PADREGJ_PAD37FNCSEL_TRIG2 = 0, /*!< TRIG2 : Configure as the ADC Trigger 2 signal value. */ + GPIO_PADREGJ_PAD37FNCSEL_NCE37 = 1, /*!< NCE37 : IOM/MSPI nCE group 37 value. */ + GPIO_PADREGJ_PAD37FNCSEL_UA0RTS = 2, /*!< UA0RTS : Configure as the UART0 RTS output signal value. */ + GPIO_PADREGJ_PAD37FNCSEL_GPIO37 = 3, /*!< GPIO37 : Configure as GPIO37 value. */ + GPIO_PADREGJ_PAD37FNCSEL_SCCIO = 4, /*!< SCCIO : SCARD serial data input/output value. */ + GPIO_PADREGJ_PAD37FNCSEL_UART1TX = 5, /*!< UART1TX : Configure as the UART1 TX output signal value. */ + GPIO_PADREGJ_PAD37FNCSEL_PDMCLK = 6, /*!< PDMCLK : Configure as the PDM CLK output signal value. */ + GPIO_PADREGJ_PAD37FNCSEL_CT29 = 7, /*!< CT29 : CTIMER connection 29 value. */ +} GPIO_PADREGJ_PAD37FNCSEL_Enum; + +/* =========================================== GPIO PADREGJ PAD37STRNG [10..10] ============================================ */ +typedef enum { /*!< GPIO_PADREGJ_PAD37STRNG */ + GPIO_PADREGJ_PAD37STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGJ_PAD37STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGJ_PAD37STRNG_Enum; + +/* ============================================ GPIO PADREGJ PAD37INPEN [9..9] ============================================= */ +typedef enum { /*!< GPIO_PADREGJ_PAD37INPEN */ + GPIO_PADREGJ_PAD37INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGJ_PAD37INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGJ_PAD37INPEN_Enum; + +/* ============================================= GPIO PADREGJ PAD37PULL [8..8] ============================================= */ +typedef enum { /*!< GPIO_PADREGJ_PAD37PULL */ + GPIO_PADREGJ_PAD37PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGJ_PAD37PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGJ_PAD37PULL_Enum; + +/* ============================================ GPIO PADREGJ PAD36PWRUP [6..6] ============================================= */ +typedef enum { /*!< GPIO_PADREGJ_PAD36PWRUP */ + GPIO_PADREGJ_PAD36PWRUP_DIS = 0, /*!< DIS : Power switch disabled value. */ + GPIO_PADREGJ_PAD36PWRUP_EN = 1, /*!< EN : Power switch enabled (switched to VDD) value. */ +} GPIO_PADREGJ_PAD36PWRUP_Enum; + +/* ============================================ GPIO PADREGJ PAD36FNCSEL [3..5] ============================================ */ +typedef enum { /*!< GPIO_PADREGJ_PAD36FNCSEL */ + GPIO_PADREGJ_PAD36FNCSEL_TRIG1 = 0, /*!< TRIG1 : Configure as the ADC Trigger 1 signal value. */ + GPIO_PADREGJ_PAD36FNCSEL_NCE36 = 1, /*!< NCE36 : IOM/MSPI nCE group 36 value. */ + GPIO_PADREGJ_PAD36FNCSEL_UART1RX = 2, /*!< UART1RX : Configure as the UART1 RX input signal value. */ + GPIO_PADREGJ_PAD36FNCSEL_GPIO36 = 3, /*!< GPIO36 : Configure as GPIO36 value. */ + GPIO_PADREGJ_PAD36FNCSEL_32kHzXT = 4, /*!< 32kHzXT : Configure as the 32kHz output clock from the crystal + value. */ + GPIO_PADREGJ_PAD36FNCSEL_UA1CTS = 5, /*!< UA1CTS : Configure as the UART1 CTS input signal value. */ + GPIO_PADREGJ_PAD36FNCSEL_UA0CTS = 6, /*!< UA0CTS : Configure as the UART0 CTS input signal value. */ + GPIO_PADREGJ_PAD36FNCSEL_PDMDATA = 7, /*!< PDMDATA : PDM serial data input value. */ +} GPIO_PADREGJ_PAD36FNCSEL_Enum; + +/* ============================================ GPIO PADREGJ PAD36STRNG [2..2] ============================================= */ +typedef enum { /*!< GPIO_PADREGJ_PAD36STRNG */ + GPIO_PADREGJ_PAD36STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGJ_PAD36STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGJ_PAD36STRNG_Enum; + +/* ============================================ GPIO PADREGJ PAD36INPEN [1..1] ============================================= */ +typedef enum { /*!< GPIO_PADREGJ_PAD36INPEN */ + GPIO_PADREGJ_PAD36INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGJ_PAD36INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGJ_PAD36INPEN_Enum; + +/* ============================================= GPIO PADREGJ PAD36PULL [0..0] ============================================= */ +typedef enum { /*!< GPIO_PADREGJ_PAD36PULL */ + GPIO_PADREGJ_PAD36PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGJ_PAD36PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGJ_PAD36PULL_Enum; + +/* ======================================================== PADREGK ======================================================== */ +/* ============================================ GPIO PADREGK PAD43RSEL [30..31] ============================================ */ +typedef enum { /*!< GPIO_PADREGK_PAD43RSEL */ + GPIO_PADREGK_PAD43RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ + GPIO_PADREGK_PAD43RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ + GPIO_PADREGK_PAD43RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ + GPIO_PADREGK_PAD43RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ +} GPIO_PADREGK_PAD43RSEL_Enum; + +/* =========================================== GPIO PADREGK PAD43FNCSEL [27..29] =========================================== */ +typedef enum { /*!< GPIO_PADREGK_PAD43FNCSEL */ + GPIO_PADREGK_PAD43FNCSEL_UART1RX = 0, /*!< UART1RX : Configure as the UART1 RX input signal value. */ + GPIO_PADREGK_PAD43FNCSEL_NCE43 = 1, /*!< NCE43 : IOM/MSPI nCE group 43 value. */ + GPIO_PADREGK_PAD43FNCSEL_CT18 = 2, /*!< CT18 : CTIMER connection 18 value. */ + GPIO_PADREGK_PAD43FNCSEL_GPIO43 = 3, /*!< GPIO43 : Configure as GPIO43 value. */ + GPIO_PADREGK_PAD43FNCSEL_M3SDAWIR3 = 4, /*!< M3SDAWIR3 : Configure as the IOMSTR3 I2C SDA or SPI WIR3 signal + value. */ + GPIO_PADREGK_PAD43FNCSEL_M3MISO = 5, /*!< M3MISO : Configure as the IOMSTR3 SPI MISO signal value. */ +} GPIO_PADREGK_PAD43FNCSEL_Enum; + +/* =========================================== GPIO PADREGK PAD43STRNG [26..26] ============================================ */ +typedef enum { /*!< GPIO_PADREGK_PAD43STRNG */ + GPIO_PADREGK_PAD43STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGK_PAD43STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGK_PAD43STRNG_Enum; + +/* =========================================== GPIO PADREGK PAD43INPEN [25..25] ============================================ */ +typedef enum { /*!< GPIO_PADREGK_PAD43INPEN */ + GPIO_PADREGK_PAD43INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGK_PAD43INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGK_PAD43INPEN_Enum; + +/* ============================================ GPIO PADREGK PAD43PULL [24..24] ============================================ */ +typedef enum { /*!< GPIO_PADREGK_PAD43PULL */ + GPIO_PADREGK_PAD43PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGK_PAD43PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGK_PAD43PULL_Enum; + +/* ============================================ GPIO PADREGK PAD42RSEL [22..23] ============================================ */ +typedef enum { /*!< GPIO_PADREGK_PAD42RSEL */ + GPIO_PADREGK_PAD42RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ + GPIO_PADREGK_PAD42RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ + GPIO_PADREGK_PAD42RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ + GPIO_PADREGK_PAD42RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ +} GPIO_PADREGK_PAD42RSEL_Enum; + +/* =========================================== GPIO PADREGK PAD42FNCSEL [19..21] =========================================== */ +typedef enum { /*!< GPIO_PADREGK_PAD42FNCSEL */ + GPIO_PADREGK_PAD42FNCSEL_UART1TX = 0, /*!< UART1TX : Configure as the UART1 TX output signal value. */ + GPIO_PADREGK_PAD42FNCSEL_NCE42 = 1, /*!< NCE42 : IOM/MSPI nCE group 42 value. */ + GPIO_PADREGK_PAD42FNCSEL_CT16 = 2, /*!< CT16 : CTIMER connection 16 value. */ + GPIO_PADREGK_PAD42FNCSEL_GPIO42 = 3, /*!< GPIO42 : Configure as GPIO42 value. */ + GPIO_PADREGK_PAD42FNCSEL_M3SCL = 4, /*!< M3SCL : Configure as the IOMSTR3 I2C SCL clock I/O signal value. */ + GPIO_PADREGK_PAD42FNCSEL_M3SCK = 5, /*!< M3SCK : Configure as the IOMSTR3 SPI SCK output value. */ +} GPIO_PADREGK_PAD42FNCSEL_Enum; + +/* =========================================== GPIO PADREGK PAD42STRNG [18..18] ============================================ */ +typedef enum { /*!< GPIO_PADREGK_PAD42STRNG */ + GPIO_PADREGK_PAD42STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGK_PAD42STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGK_PAD42STRNG_Enum; + +/* =========================================== GPIO PADREGK PAD42INPEN [17..17] ============================================ */ +typedef enum { /*!< GPIO_PADREGK_PAD42INPEN */ + GPIO_PADREGK_PAD42INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGK_PAD42INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGK_PAD42INPEN_Enum; + +/* ============================================ GPIO PADREGK PAD42PULL [16..16] ============================================ */ +typedef enum { /*!< GPIO_PADREGK_PAD42PULL */ + GPIO_PADREGK_PAD42PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGK_PAD42PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGK_PAD42PULL_Enum; + +/* =========================================== GPIO PADREGK PAD41PWRDN [15..15] ============================================ */ +typedef enum { /*!< GPIO_PADREGK_PAD41PWRDN */ + GPIO_PADREGK_PAD41PWRDN_DIS = 0, /*!< DIS : Power switch disabled value. */ + GPIO_PADREGK_PAD41PWRDN_EN = 1, /*!< EN : Power switch enabled (Switch pad to VSS) value. */ +} GPIO_PADREGK_PAD41PWRDN_Enum; + +/* =========================================== GPIO PADREGK PAD41FNCSEL [11..13] =========================================== */ +typedef enum { /*!< GPIO_PADREGK_PAD41FNCSEL */ + GPIO_PADREGK_PAD41FNCSEL_NCE41 = 0, /*!< NCE41 : IOM/MSPI nCE group 41 value. */ + GPIO_PADREGK_PAD41FNCSEL_SWO = 2, /*!< SWO : Configure as the serial wire debug SWO signal value. */ + GPIO_PADREGK_PAD41FNCSEL_GPIO41 = 3, /*!< GPIO41 : Configure as GPIO41 value. */ + GPIO_PADREGK_PAD41FNCSEL_I2SWCLK = 4, /*!< I2SWCLK : I2S word clock input value. */ + GPIO_PADREGK_PAD41FNCSEL_UA1RTS = 5, /*!< UA1RTS : Configure as the UART1 RTS output signal value. */ + GPIO_PADREGK_PAD41FNCSEL_UART0TX = 6, /*!< UART0TX : Configure as the UART0 TX output signal value. */ + GPIO_PADREGK_PAD41FNCSEL_UA0RTS = 7, /*!< UA0RTS : Configure as the UART0 RTS output signal value. */ +} GPIO_PADREGK_PAD41FNCSEL_Enum; + +/* =========================================== GPIO PADREGK PAD41STRNG [10..10] ============================================ */ +typedef enum { /*!< GPIO_PADREGK_PAD41STRNG */ + GPIO_PADREGK_PAD41STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGK_PAD41STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGK_PAD41STRNG_Enum; + +/* ============================================ GPIO PADREGK PAD41INPEN [9..9] ============================================= */ +typedef enum { /*!< GPIO_PADREGK_PAD41INPEN */ + GPIO_PADREGK_PAD41INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGK_PAD41INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGK_PAD41INPEN_Enum; + +/* ============================================= GPIO PADREGK PAD41PULL [8..8] ============================================= */ +typedef enum { /*!< GPIO_PADREGK_PAD41PULL */ + GPIO_PADREGK_PAD41PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGK_PAD41PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGK_PAD41PULL_Enum; + +/* ============================================= GPIO PADREGK PAD40RSEL [6..7] ============================================= */ +typedef enum { /*!< GPIO_PADREGK_PAD40RSEL */ + GPIO_PADREGK_PAD40RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ + GPIO_PADREGK_PAD40RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ + GPIO_PADREGK_PAD40RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ + GPIO_PADREGK_PAD40RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ +} GPIO_PADREGK_PAD40RSEL_Enum; + +/* ============================================ GPIO PADREGK PAD40FNCSEL [3..5] ============================================ */ +typedef enum { /*!< GPIO_PADREGK_PAD40FNCSEL */ + GPIO_PADREGK_PAD40FNCSEL_UART0RX = 0, /*!< UART0RX : Configure as the UART0 RX input signal value. */ + GPIO_PADREGK_PAD40FNCSEL_UART1RX = 1, /*!< UART1RX : Configure as the UART1 RX input signal value. */ + GPIO_PADREGK_PAD40FNCSEL_TRIG0 = 2, /*!< TRIG0 : Configure as the ADC Trigger 0 signal value. */ + GPIO_PADREGK_PAD40FNCSEL_GPIO40 = 3, /*!< GPIO40 : Configure as GPIO40 value. */ + GPIO_PADREGK_PAD40FNCSEL_M4SDAWIR3 = 4, /*!< M4SDAWIR3 : Configure as the IOMSTR4 I2C SDA or SPI WIR3 signal + value. */ + GPIO_PADREGK_PAD40FNCSEL_M4MISO = 5, /*!< M4MISO : Configure as the IOMSTR4 SPI MISO input signal value. */ +} GPIO_PADREGK_PAD40FNCSEL_Enum; + +/* ============================================ GPIO PADREGK PAD40STRNG [2..2] ============================================= */ +typedef enum { /*!< GPIO_PADREGK_PAD40STRNG */ + GPIO_PADREGK_PAD40STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGK_PAD40STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGK_PAD40STRNG_Enum; + +/* ============================================ GPIO PADREGK PAD40INPEN [1..1] ============================================= */ +typedef enum { /*!< GPIO_PADREGK_PAD40INPEN */ + GPIO_PADREGK_PAD40INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGK_PAD40INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGK_PAD40INPEN_Enum; + +/* ============================================= GPIO PADREGK PAD40PULL [0..0] ============================================= */ +typedef enum { /*!< GPIO_PADREGK_PAD40PULL */ + GPIO_PADREGK_PAD40PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGK_PAD40PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGK_PAD40PULL_Enum; + +/* ======================================================== PADREGL ======================================================== */ +/* =========================================== GPIO PADREGL PAD47FNCSEL [27..29] =========================================== */ +typedef enum { /*!< GPIO_PADREGL_PAD47FNCSEL */ + GPIO_PADREGL_PAD47FNCSEL_32kHzXT = 0, /*!< 32kHzXT : Configure as the 32kHz output clock from the crystal + value. */ + GPIO_PADREGL_PAD47FNCSEL_NCE47 = 1, /*!< NCE47 : IOM/MSPI nCE group 47 value. */ + GPIO_PADREGL_PAD47FNCSEL_CT26 = 2, /*!< CT26 : CTIMER connection 26 value. */ + GPIO_PADREGL_PAD47FNCSEL_GPIO47 = 3, /*!< GPIO47 : Configure as GPIO47 value. */ + GPIO_PADREGL_PAD47FNCSEL_M5MOSI = 5, /*!< M5MOSI : Configure as the IOMSTR5 SPI MOSI output signal value. */ + GPIO_PADREGL_PAD47FNCSEL_UART1RX = 6, /*!< UART1RX : Configure as the UART1 RX input signal value. */ +} GPIO_PADREGL_PAD47FNCSEL_Enum; + +/* =========================================== GPIO PADREGL PAD47STRNG [26..26] ============================================ */ +typedef enum { /*!< GPIO_PADREGL_PAD47STRNG */ + GPIO_PADREGL_PAD47STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGL_PAD47STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGL_PAD47STRNG_Enum; + +/* =========================================== GPIO PADREGL PAD47INPEN [25..25] ============================================ */ +typedef enum { /*!< GPIO_PADREGL_PAD47INPEN */ + GPIO_PADREGL_PAD47INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGL_PAD47INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGL_PAD47INPEN_Enum; + +/* ============================================ GPIO PADREGL PAD47PULL [24..24] ============================================ */ +typedef enum { /*!< GPIO_PADREGL_PAD47PULL */ + GPIO_PADREGL_PAD47PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGL_PAD47PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGL_PAD47PULL_Enum; + +/* =========================================== GPIO PADREGL PAD46FNCSEL [19..21] =========================================== */ +typedef enum { /*!< GPIO_PADREGL_PAD46FNCSEL */ + GPIO_PADREGL_PAD46FNCSEL_32khz_XT = 0, /*!< 32khz_XT : Configure as the 32kHz output clock from the crystal + value. */ + GPIO_PADREGL_PAD46FNCSEL_NCE46 = 1, /*!< NCE46 : IOM/MSPI nCE group 46 value. */ + GPIO_PADREGL_PAD46FNCSEL_CT24 = 2, /*!< CT24 : CTIMER connection 24 value. */ + GPIO_PADREGL_PAD46FNCSEL_GPIO46 = 3, /*!< GPIO46 : Configure as GPIO46 value. */ + GPIO_PADREGL_PAD46FNCSEL_SCCRST = 4, /*!< SCCRST : SCARD reset output value. */ + GPIO_PADREGL_PAD46FNCSEL_PDMCLK = 5, /*!< PDMCLK : PDM serial clock output value. */ + GPIO_PADREGL_PAD46FNCSEL_UART1TX = 6, /*!< UART1TX : Configure as the UART1 TX output signal value. */ + GPIO_PADREGL_PAD46FNCSEL_SWO = 7, /*!< SWO : Configure as the serial wire debug SWO signal value. */ +} GPIO_PADREGL_PAD46FNCSEL_Enum; + +/* =========================================== GPIO PADREGL PAD46STRNG [18..18] ============================================ */ +typedef enum { /*!< GPIO_PADREGL_PAD46STRNG */ + GPIO_PADREGL_PAD46STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGL_PAD46STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGL_PAD46STRNG_Enum; + +/* =========================================== GPIO PADREGL PAD46INPEN [17..17] ============================================ */ +typedef enum { /*!< GPIO_PADREGL_PAD46INPEN */ + GPIO_PADREGL_PAD46INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGL_PAD46INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGL_PAD46INPEN_Enum; + +/* ============================================ GPIO PADREGL PAD46PULL [16..16] ============================================ */ +typedef enum { /*!< GPIO_PADREGL_PAD46PULL */ + GPIO_PADREGL_PAD46PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGL_PAD46PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGL_PAD46PULL_Enum; + +/* =========================================== GPIO PADREGL PAD45FNCSEL [11..13] =========================================== */ +typedef enum { /*!< GPIO_PADREGL_PAD45FNCSEL */ + GPIO_PADREGL_PAD45FNCSEL_UA1CTS = 0, /*!< UA1CTS : Configure as the UART1 CTS input signal value. */ + GPIO_PADREGL_PAD45FNCSEL_NCE45 = 1, /*!< NCE45 : IOM/MSPI nCE group 45 value. */ + GPIO_PADREGL_PAD45FNCSEL_CT22 = 2, /*!< CT22 : CTIMER connection 22 value. */ + GPIO_PADREGL_PAD45FNCSEL_GPIO45 = 3, /*!< GPIO45 : Configure as GPIO45 value. */ + GPIO_PADREGL_PAD45FNCSEL_I2SDAT = 4, /*!< I2SDAT : I2S serial data output value. */ + GPIO_PADREGL_PAD45FNCSEL_PDMDATA = 5, /*!< PDMDATA : PDM serial data input value. */ + GPIO_PADREGL_PAD45FNCSEL_UART0RX = 6, /*!< UART0RX : Configure as the SPI channel 5 nCE signal from IOMSTR5 + value. */ + GPIO_PADREGL_PAD45FNCSEL_SWO = 7, /*!< SWO : Configure as the serial wire debug SWO signal value. */ +} GPIO_PADREGL_PAD45FNCSEL_Enum; + +/* =========================================== GPIO PADREGL PAD45STRNG [10..10] ============================================ */ +typedef enum { /*!< GPIO_PADREGL_PAD45STRNG */ + GPIO_PADREGL_PAD45STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGL_PAD45STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGL_PAD45STRNG_Enum; + +/* ============================================ GPIO PADREGL PAD45INPEN [9..9] ============================================= */ +typedef enum { /*!< GPIO_PADREGL_PAD45INPEN */ + GPIO_PADREGL_PAD45INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGL_PAD45INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGL_PAD45INPEN_Enum; + +/* ============================================= GPIO PADREGL PAD45PULL [8..8] ============================================= */ +typedef enum { /*!< GPIO_PADREGL_PAD45PULL */ + GPIO_PADREGL_PAD45PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGL_PAD45PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGL_PAD45PULL_Enum; + +/* ============================================ GPIO PADREGL PAD44FNCSEL [3..5] ============================================ */ +typedef enum { /*!< GPIO_PADREGL_PAD44FNCSEL */ + GPIO_PADREGL_PAD44FNCSEL_UA1RTS = 0, /*!< UA1RTS : Configure as the UART1 RTS output signal value. */ + GPIO_PADREGL_PAD44FNCSEL_NCE44 = 1, /*!< NCE44 : IOM/MSPI nCE group 44 value. */ + GPIO_PADREGL_PAD44FNCSEL_CT20 = 2, /*!< CT20 : CTIMER connection 20 value. */ + GPIO_PADREGL_PAD44FNCSEL_GPIO44 = 3, /*!< GPIO44 : Configure as GPIO44 value. */ + GPIO_PADREGL_PAD44FNCSEL_M4MOSI = 5, /*!< M4MOSI : Configure as the IOMSTR4 SPI MOSI signal value. */ + GPIO_PADREGL_PAD44FNCSEL_M5nCE6 = 6, /*!< M5nCE6 : Configure as the SPI channel 6 nCE signal from IOMSTR5 + value. */ +} GPIO_PADREGL_PAD44FNCSEL_Enum; + +/* ============================================ GPIO PADREGL PAD44STRNG [2..2] ============================================= */ +typedef enum { /*!< GPIO_PADREGL_PAD44STRNG */ + GPIO_PADREGL_PAD44STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGL_PAD44STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGL_PAD44STRNG_Enum; + +/* ============================================ GPIO PADREGL PAD44INPEN [1..1] ============================================= */ +typedef enum { /*!< GPIO_PADREGL_PAD44INPEN */ + GPIO_PADREGL_PAD44INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGL_PAD44INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGL_PAD44INPEN_Enum; + +/* ============================================= GPIO PADREGL PAD44PULL [0..0] ============================================= */ +typedef enum { /*!< GPIO_PADREGL_PAD44PULL */ + GPIO_PADREGL_PAD44PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGL_PAD44PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGL_PAD44PULL_Enum; + +/* ======================================================== PADREGM ======================================================== */ +/* ============================================ GPIO PADREGM PAD49RSEL [14..15] ============================================ */ +typedef enum { /*!< GPIO_PADREGM_PAD49RSEL */ + GPIO_PADREGM_PAD49RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ + GPIO_PADREGM_PAD49RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ + GPIO_PADREGM_PAD49RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ + GPIO_PADREGM_PAD49RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ +} GPIO_PADREGM_PAD49RSEL_Enum; + +/* =========================================== GPIO PADREGM PAD49FNCSEL [11..13] =========================================== */ +typedef enum { /*!< GPIO_PADREGM_PAD49FNCSEL */ + GPIO_PADREGM_PAD49FNCSEL_UART0RX = 0, /*!< UART0RX : Configure as the UART0 RX input signal value. */ + GPIO_PADREGM_PAD49FNCSEL_NCE49 = 1, /*!< NCE49 : IOM/MSPPI nCE group 49 value. */ + GPIO_PADREGM_PAD49FNCSEL_CT30 = 2, /*!< CT30 : CTIMER connection 30 value. */ + GPIO_PADREGM_PAD49FNCSEL_GPIO49 = 3, /*!< GPIO49 : Configure as GPIO49 value. */ + GPIO_PADREGM_PAD49FNCSEL_M5SDAWIR3 = 4, /*!< M5SDAWIR3 : Configure as the IOMSTR5 I2C SDA or SPI WIR3 signal + value. */ + GPIO_PADREGM_PAD49FNCSEL_M5MISO = 5, /*!< M5MISO : Configure as the IOMSTR5 SPI MISO input signal value. */ +} GPIO_PADREGM_PAD49FNCSEL_Enum; + +/* =========================================== GPIO PADREGM PAD49STRNG [10..10] ============================================ */ +typedef enum { /*!< GPIO_PADREGM_PAD49STRNG */ + GPIO_PADREGM_PAD49STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGM_PAD49STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGM_PAD49STRNG_Enum; + +/* ============================================ GPIO PADREGM PAD49INPEN [9..9] ============================================= */ +typedef enum { /*!< GPIO_PADREGM_PAD49INPEN */ + GPIO_PADREGM_PAD49INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGM_PAD49INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGM_PAD49INPEN_Enum; + +/* ============================================= GPIO PADREGM PAD49PULL [8..8] ============================================= */ +typedef enum { /*!< GPIO_PADREGM_PAD49PULL */ + GPIO_PADREGM_PAD49PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGM_PAD49PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGM_PAD49PULL_Enum; + +/* ============================================= GPIO PADREGM PAD48RSEL [6..7] ============================================= */ +typedef enum { /*!< GPIO_PADREGM_PAD48RSEL */ + GPIO_PADREGM_PAD48RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ + GPIO_PADREGM_PAD48RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ + GPIO_PADREGM_PAD48RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ + GPIO_PADREGM_PAD48RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ +} GPIO_PADREGM_PAD48RSEL_Enum; + +/* ============================================ GPIO PADREGM PAD48FNCSEL [3..5] ============================================ */ +typedef enum { /*!< GPIO_PADREGM_PAD48FNCSEL */ + GPIO_PADREGM_PAD48FNCSEL_UART0TX = 0, /*!< UART0TX : Configure as the UART0 TX output signal value. */ + GPIO_PADREGM_PAD48FNCSEL_NCE48 = 1, /*!< NCE48 : IOM/MSPI nCE group 48 value. */ + GPIO_PADREGM_PAD48FNCSEL_CT28 = 2, /*!< CT28 : CTIMER conenction 28 value. */ + GPIO_PADREGM_PAD48FNCSEL_GPIO48 = 3, /*!< GPIO48 : Configure as GPIO48 value. */ + GPIO_PADREGM_PAD48FNCSEL_M5SCL = 4, /*!< M5SCL : Configure as the IOMSTR5 I2C SCL clock I/O signal value. */ + GPIO_PADREGM_PAD48FNCSEL_M5SCK = 5, /*!< M5SCK : Configure as the IOMSTR5 SPI SCK output value. */ +} GPIO_PADREGM_PAD48FNCSEL_Enum; + +/* ============================================ GPIO PADREGM PAD48STRNG [2..2] ============================================= */ +typedef enum { /*!< GPIO_PADREGM_PAD48STRNG */ + GPIO_PADREGM_PAD48STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ + GPIO_PADREGM_PAD48STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ +} GPIO_PADREGM_PAD48STRNG_Enum; + +/* ============================================ GPIO PADREGM PAD48INPEN [1..1] ============================================= */ +typedef enum { /*!< GPIO_PADREGM_PAD48INPEN */ + GPIO_PADREGM_PAD48INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ + GPIO_PADREGM_PAD48INPEN_EN = 1, /*!< EN : Pad input enabled value. */ +} GPIO_PADREGM_PAD48INPEN_Enum; + +/* ============================================= GPIO PADREGM PAD48PULL [0..0] ============================================= */ +typedef enum { /*!< GPIO_PADREGM_PAD48PULL */ + GPIO_PADREGM_PAD48PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ + GPIO_PADREGM_PAD48PULL_EN = 1, /*!< EN : Pullup enabled value. */ +} GPIO_PADREGM_PAD48PULL_Enum; + +/* ========================================================= CFGA ========================================================== */ +/* ============================================= GPIO CFGA GPIO7INTD [31..31] ============================================== */ +typedef enum { /*!< GPIO_CFGA_GPIO7INTD */ + GPIO_CFGA_GPIO7INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x0 - nCE polarity active low value. */ + GPIO_CFGA_GPIO7INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x0 - nCE polarity active high value. */ +} GPIO_CFGA_GPIO7INTD_Enum; + +/* ============================================ GPIO CFGA GPIO7OUTCFG [29..30] ============================================= */ +typedef enum { /*!< GPIO_CFGA_GPIO7OUTCFG */ + GPIO_CFGA_GPIO7OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGA_GPIO7OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGA_GPIO7OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGA_GPIO7OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGA_GPIO7OUTCFG_Enum; + +/* ============================================= GPIO CFGA GPIO7INCFG [28..28] ============================================= */ +typedef enum { /*!< GPIO_CFGA_GPIO7INCFG */ + GPIO_CFGA_GPIO7INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGA_GPIO7INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGA_GPIO7INCFG_Enum; + +/* ============================================= GPIO CFGA GPIO6INTD [27..27] ============================================== */ +typedef enum { /*!< GPIO_CFGA_GPIO6INTD */ + GPIO_CFGA_GPIO6INTD_INTDIS = 0, /*!< INTDIS : INCFG = 1 - No interrupt on GPIO transition value. */ + GPIO_CFGA_GPIO6INTD_INTBOTH = 1, /*!< INTBOTH : INCFG = 1 - Interrupt on either low to high or high + to low GPIO transition value. */ +} GPIO_CFGA_GPIO6INTD_Enum; + +/* ============================================ GPIO CFGA GPIO6OUTCFG [25..26] ============================================= */ +typedef enum { /*!< GPIO_CFGA_GPIO6OUTCFG */ + GPIO_CFGA_GPIO6OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGA_GPIO6OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGA_GPIO6OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGA_GPIO6OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGA_GPIO6OUTCFG_Enum; + +/* ============================================= GPIO CFGA GPIO6INCFG [24..24] ============================================= */ +typedef enum { /*!< GPIO_CFGA_GPIO6INCFG */ + GPIO_CFGA_GPIO6INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGA_GPIO6INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGA_GPIO6INCFG_Enum; + +/* ============================================= GPIO CFGA GPIO5INTD [23..23] ============================================== */ +typedef enum { /*!< GPIO_CFGA_GPIO5INTD */ + GPIO_CFGA_GPIO5INTD_INTDIS = 0, /*!< INTDIS : INCFG = 1 - No interrupt on GPIO transition value. */ + GPIO_CFGA_GPIO5INTD_INTBOTH = 1, /*!< INTBOTH : INCFG = 1 - Interrupt on either low to high or high + to low GPIO transition value. */ +} GPIO_CFGA_GPIO5INTD_Enum; + +/* ============================================ GPIO CFGA GPIO5OUTCFG [21..22] ============================================= */ +typedef enum { /*!< GPIO_CFGA_GPIO5OUTCFG */ + GPIO_CFGA_GPIO5OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGA_GPIO5OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGA_GPIO5OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGA_GPIO5OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGA_GPIO5OUTCFG_Enum; + +/* ============================================= GPIO CFGA GPIO5INCFG [20..20] ============================================= */ +typedef enum { /*!< GPIO_CFGA_GPIO5INCFG */ + GPIO_CFGA_GPIO5INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGA_GPIO5INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGA_GPIO5INCFG_Enum; + +/* ============================================= GPIO CFGA GPIO4INTD [19..19] ============================================== */ +typedef enum { /*!< GPIO_CFGA_GPIO4INTD */ + GPIO_CFGA_GPIO4INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x2 - nCE polarity active low value. */ + GPIO_CFGA_GPIO4INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x2 - nCE polarity active high value. */ +} GPIO_CFGA_GPIO4INTD_Enum; + +/* ============================================ GPIO CFGA GPIO4OUTCFG [17..18] ============================================= */ +typedef enum { /*!< GPIO_CFGA_GPIO4OUTCFG */ + GPIO_CFGA_GPIO4OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGA_GPIO4OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGA_GPIO4OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGA_GPIO4OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGA_GPIO4OUTCFG_Enum; + +/* ============================================= GPIO CFGA GPIO4INCFG [16..16] ============================================= */ +typedef enum { /*!< GPIO_CFGA_GPIO4INCFG */ + GPIO_CFGA_GPIO4INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGA_GPIO4INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGA_GPIO4INCFG_Enum; + +/* ============================================= GPIO CFGA GPIO3INTD [15..15] ============================================== */ +typedef enum { /*!< GPIO_CFGA_GPIO3INTD */ + GPIO_CFGA_GPIO3INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x2 - nCE polarity active low value. */ + GPIO_CFGA_GPIO3INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x2 - nCE polarity active high value. */ +} GPIO_CFGA_GPIO3INTD_Enum; + +/* ============================================ GPIO CFGA GPIO3OUTCFG [13..14] ============================================= */ +typedef enum { /*!< GPIO_CFGA_GPIO3OUTCFG */ + GPIO_CFGA_GPIO3OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGA_GPIO3OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGA_GPIO3OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGA_GPIO3OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGA_GPIO3OUTCFG_Enum; + +/* ============================================= GPIO CFGA GPIO3INCFG [12..12] ============================================= */ +typedef enum { /*!< GPIO_CFGA_GPIO3INCFG */ + GPIO_CFGA_GPIO3INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGA_GPIO3INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGA_GPIO3INCFG_Enum; + +/* ============================================= GPIO CFGA GPIO2INTD [11..11] ============================================== */ +typedef enum { /*!< GPIO_CFGA_GPIO2INTD */ + GPIO_CFGA_GPIO2INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x7 - nCE polarity active low value. */ + GPIO_CFGA_GPIO2INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x7 - nCE polarity active high value. */ +} GPIO_CFGA_GPIO2INTD_Enum; + +/* ============================================= GPIO CFGA GPIO2OUTCFG [9..10] ============================================= */ +typedef enum { /*!< GPIO_CFGA_GPIO2OUTCFG */ + GPIO_CFGA_GPIO2OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGA_GPIO2OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGA_GPIO2OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGA_GPIO2OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGA_GPIO2OUTCFG_Enum; + +/* ============================================== GPIO CFGA GPIO2INCFG [8..8] ============================================== */ +typedef enum { /*!< GPIO_CFGA_GPIO2INCFG */ + GPIO_CFGA_GPIO2INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGA_GPIO2INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGA_GPIO2INCFG_Enum; + +/* ============================================== GPIO CFGA GPIO1INTD [7..7] =============================================== */ +typedef enum { /*!< GPIO_CFGA_GPIO1INTD */ + GPIO_CFGA_GPIO1INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x7 - nCE polarity active low value. */ + GPIO_CFGA_GPIO1INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x7 - nCE polarity active high value. */ +} GPIO_CFGA_GPIO1INTD_Enum; + +/* ============================================= GPIO CFGA GPIO1OUTCFG [5..6] ============================================== */ +typedef enum { /*!< GPIO_CFGA_GPIO1OUTCFG */ + GPIO_CFGA_GPIO1OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGA_GPIO1OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGA_GPIO1OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGA_GPIO1OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGA_GPIO1OUTCFG_Enum; + +/* ============================================== GPIO CFGA GPIO1INCFG [4..4] ============================================== */ +typedef enum { /*!< GPIO_CFGA_GPIO1INCFG */ + GPIO_CFGA_GPIO1INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGA_GPIO1INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGA_GPIO1INCFG_Enum; + +/* ============================================== GPIO CFGA GPIO0INTD [3..3] =============================================== */ +typedef enum { /*!< GPIO_CFGA_GPIO0INTD */ + GPIO_CFGA_GPIO0INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x7 - nCE polarity active low value. */ + GPIO_CFGA_GPIO0INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x7 - nCE polarity active high value. */ +} GPIO_CFGA_GPIO0INTD_Enum; + +/* ============================================= GPIO CFGA GPIO0OUTCFG [1..2] ============================================== */ +typedef enum { /*!< GPIO_CFGA_GPIO0OUTCFG */ + GPIO_CFGA_GPIO0OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGA_GPIO0OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGA_GPIO0OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGA_GPIO0OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGA_GPIO0OUTCFG_Enum; + +/* ============================================== GPIO CFGA GPIO0INCFG [0..0] ============================================== */ +typedef enum { /*!< GPIO_CFGA_GPIO0INCFG */ + GPIO_CFGA_GPIO0INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGA_GPIO0INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGA_GPIO0INCFG_Enum; + +/* ========================================================= CFGB ========================================================== */ +/* ============================================= GPIO CFGB GPIO15INTD [31..31] ============================================= */ +typedef enum { /*!< GPIO_CFGB_GPIO15INTD */ + GPIO_CFGB_GPIO15INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGB_GPIO15INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGB_GPIO15INTD_Enum; + +/* ============================================ GPIO CFGB GPIO15OUTCFG [29..30] ============================================ */ +typedef enum { /*!< GPIO_CFGB_GPIO15OUTCFG */ + GPIO_CFGB_GPIO15OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGB_GPIO15OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGB_GPIO15OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGB_GPIO15OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGB_GPIO15OUTCFG_Enum; + +/* ============================================ GPIO CFGB GPIO15INCFG [28..28] ============================================= */ +typedef enum { /*!< GPIO_CFGB_GPIO15INCFG */ + GPIO_CFGB_GPIO15INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGB_GPIO15INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGB_GPIO15INCFG_Enum; + +/* ============================================= GPIO CFGB GPIO14INTD [27..27] ============================================= */ +typedef enum { /*!< GPIO_CFGB_GPIO14INTD */ + GPIO_CFGB_GPIO14INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGB_GPIO14INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGB_GPIO14INTD_Enum; + +/* ============================================ GPIO CFGB GPIO14OUTCFG [25..26] ============================================ */ +typedef enum { /*!< GPIO_CFGB_GPIO14OUTCFG */ + GPIO_CFGB_GPIO14OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGB_GPIO14OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGB_GPIO14OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGB_GPIO14OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGB_GPIO14OUTCFG_Enum; + +/* ============================================ GPIO CFGB GPIO14INCFG [24..24] ============================================= */ +typedef enum { /*!< GPIO_CFGB_GPIO14INCFG */ + GPIO_CFGB_GPIO14INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGB_GPIO14INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGB_GPIO14INCFG_Enum; + +/* ============================================= GPIO CFGB GPIO13INTD [23..23] ============================================= */ +typedef enum { /*!< GPIO_CFGB_GPIO13INTD */ + GPIO_CFGB_GPIO13INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGB_GPIO13INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGB_GPIO13INTD_Enum; + +/* ============================================ GPIO CFGB GPIO13OUTCFG [21..22] ============================================ */ +typedef enum { /*!< GPIO_CFGB_GPIO13OUTCFG */ + GPIO_CFGB_GPIO13OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGB_GPIO13OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGB_GPIO13OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGB_GPIO13OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGB_GPIO13OUTCFG_Enum; + +/* ============================================ GPIO CFGB GPIO13INCFG [20..20] ============================================= */ +typedef enum { /*!< GPIO_CFGB_GPIO13INCFG */ + GPIO_CFGB_GPIO13INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGB_GPIO13INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGB_GPIO13INCFG_Enum; + +/* ============================================= GPIO CFGB GPIO12INTD [19..19] ============================================= */ +typedef enum { /*!< GPIO_CFGB_GPIO12INTD */ + GPIO_CFGB_GPIO12INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGB_GPIO12INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGB_GPIO12INTD_Enum; + +/* ============================================ GPIO CFGB GPIO12OUTCFG [17..18] ============================================ */ +typedef enum { /*!< GPIO_CFGB_GPIO12OUTCFG */ + GPIO_CFGB_GPIO12OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGB_GPIO12OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGB_GPIO12OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGB_GPIO12OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGB_GPIO12OUTCFG_Enum; + +/* ============================================ GPIO CFGB GPIO12INCFG [16..16] ============================================= */ +typedef enum { /*!< GPIO_CFGB_GPIO12INCFG */ + GPIO_CFGB_GPIO12INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGB_GPIO12INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGB_GPIO12INCFG_Enum; + +/* ============================================= GPIO CFGB GPIO11INTD [15..15] ============================================= */ +typedef enum { /*!< GPIO_CFGB_GPIO11INTD */ + GPIO_CFGB_GPIO11INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGB_GPIO11INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGB_GPIO11INTD_Enum; + +/* ============================================ GPIO CFGB GPIO11OUTCFG [13..14] ============================================ */ +typedef enum { /*!< GPIO_CFGB_GPIO11OUTCFG */ + GPIO_CFGB_GPIO11OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGB_GPIO11OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGB_GPIO11OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGB_GPIO11OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGB_GPIO11OUTCFG_Enum; + +/* ============================================ GPIO CFGB GPIO11INCFG [12..12] ============================================= */ +typedef enum { /*!< GPIO_CFGB_GPIO11INCFG */ + GPIO_CFGB_GPIO11INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGB_GPIO11INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGB_GPIO11INCFG_Enum; + +/* ============================================= GPIO CFGB GPIO10INTD [11..11] ============================================= */ +typedef enum { /*!< GPIO_CFGB_GPIO10INTD */ + GPIO_CFGB_GPIO10INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x2 - nCE polarity active low value. */ + GPIO_CFGB_GPIO10INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x2 - nCE polarity active high value. */ +} GPIO_CFGB_GPIO10INTD_Enum; + +/* ============================================ GPIO CFGB GPIO10OUTCFG [9..10] ============================================= */ +typedef enum { /*!< GPIO_CFGB_GPIO10OUTCFG */ + GPIO_CFGB_GPIO10OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGB_GPIO10OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGB_GPIO10OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGB_GPIO10OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGB_GPIO10OUTCFG_Enum; + +/* ============================================= GPIO CFGB GPIO10INCFG [8..8] ============================================== */ +typedef enum { /*!< GPIO_CFGB_GPIO10INCFG */ + GPIO_CFGB_GPIO10INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGB_GPIO10INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGB_GPIO10INCFG_Enum; + +/* ============================================== GPIO CFGB GPIO9INTD [7..7] =============================================== */ +typedef enum { /*!< GPIO_CFGB_GPIO9INTD */ + GPIO_CFGB_GPIO9INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x2 - nCE polarity active low value. */ + GPIO_CFGB_GPIO9INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x2 - nCE polarity active high value. */ +} GPIO_CFGB_GPIO9INTD_Enum; + +/* ============================================= GPIO CFGB GPIO9OUTCFG [5..6] ============================================== */ +typedef enum { /*!< GPIO_CFGB_GPIO9OUTCFG */ + GPIO_CFGB_GPIO9OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGB_GPIO9OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGB_GPIO9OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGB_GPIO9OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGB_GPIO9OUTCFG_Enum; + +/* ============================================== GPIO CFGB GPIO9INCFG [4..4] ============================================== */ +typedef enum { /*!< GPIO_CFGB_GPIO9INCFG */ + GPIO_CFGB_GPIO9INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGB_GPIO9INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGB_GPIO9INCFG_Enum; + +/* ============================================== GPIO CFGB GPIO8INTD [3..3] =============================================== */ +typedef enum { /*!< GPIO_CFGB_GPIO8INTD */ + GPIO_CFGB_GPIO8INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x2 - nCE polarity active low value. */ + GPIO_CFGB_GPIO8INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x2 - nCE polarity active high value. */ +} GPIO_CFGB_GPIO8INTD_Enum; + +/* ============================================= GPIO CFGB GPIO8OUTCFG [1..2] ============================================== */ +typedef enum { /*!< GPIO_CFGB_GPIO8OUTCFG */ + GPIO_CFGB_GPIO8OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGB_GPIO8OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGB_GPIO8OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGB_GPIO8OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGB_GPIO8OUTCFG_Enum; + +/* ============================================== GPIO CFGB GPIO8INCFG [0..0] ============================================== */ +typedef enum { /*!< GPIO_CFGB_GPIO8INCFG */ + GPIO_CFGB_GPIO8INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGB_GPIO8INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGB_GPIO8INCFG_Enum; + +/* ========================================================= CFGC ========================================================== */ +/* ============================================= GPIO CFGC GPIO23INTD [31..31] ============================================= */ +typedef enum { /*!< GPIO_CFGC_GPIO23INTD */ + GPIO_CFGC_GPIO23INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGC_GPIO23INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGC_GPIO23INTD_Enum; + +/* ============================================ GPIO CFGC GPIO23OUTCFG [29..30] ============================================ */ +typedef enum { /*!< GPIO_CFGC_GPIO23OUTCFG */ + GPIO_CFGC_GPIO23OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGC_GPIO23OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGC_GPIO23OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGC_GPIO23OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGC_GPIO23OUTCFG_Enum; + +/* ============================================ GPIO CFGC GPIO23INCFG [28..28] ============================================= */ +typedef enum { /*!< GPIO_CFGC_GPIO23INCFG */ + GPIO_CFGC_GPIO23INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGC_GPIO23INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGC_GPIO23INCFG_Enum; + +/* ============================================= GPIO CFGC GPIO22INTD [27..27] ============================================= */ +typedef enum { /*!< GPIO_CFGC_GPIO22INTD */ + GPIO_CFGC_GPIO22INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGC_GPIO22INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGC_GPIO22INTD_Enum; + +/* ============================================ GPIO CFGC GPIO22OUTCFG [25..26] ============================================ */ +typedef enum { /*!< GPIO_CFGC_GPIO22OUTCFG */ + GPIO_CFGC_GPIO22OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGC_GPIO22OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGC_GPIO22OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGC_GPIO22OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGC_GPIO22OUTCFG_Enum; + +/* ============================================ GPIO CFGC GPIO22INCFG [24..24] ============================================= */ +typedef enum { /*!< GPIO_CFGC_GPIO22INCFG */ + GPIO_CFGC_GPIO22INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGC_GPIO22INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGC_GPIO22INCFG_Enum; + +/* ============================================= GPIO CFGC GPIO21INTD [23..23] ============================================= */ +typedef enum { /*!< GPIO_CFGC_GPIO21INTD */ + GPIO_CFGC_GPIO21INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGC_GPIO21INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGC_GPIO21INTD_Enum; + +/* ============================================ GPIO CFGC GPIO21OUTCFG [21..22] ============================================ */ +typedef enum { /*!< GPIO_CFGC_GPIO21OUTCFG */ + GPIO_CFGC_GPIO21OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGC_GPIO21OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGC_GPIO21OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGC_GPIO21OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGC_GPIO21OUTCFG_Enum; + +/* ============================================ GPIO CFGC GPIO21INCFG [20..20] ============================================= */ +typedef enum { /*!< GPIO_CFGC_GPIO21INCFG */ + GPIO_CFGC_GPIO21INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGC_GPIO21INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGC_GPIO21INCFG_Enum; + +/* ============================================= GPIO CFGC GPIO20INTD [19..19] ============================================= */ +typedef enum { /*!< GPIO_CFGC_GPIO20INTD */ + GPIO_CFGC_GPIO20INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGC_GPIO20INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGC_GPIO20INTD_Enum; + +/* ============================================ GPIO CFGC GPIO20OUTCFG [17..18] ============================================ */ +typedef enum { /*!< GPIO_CFGC_GPIO20OUTCFG */ + GPIO_CFGC_GPIO20OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGC_GPIO20OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGC_GPIO20OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGC_GPIO20OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGC_GPIO20OUTCFG_Enum; + +/* ============================================ GPIO CFGC GPIO20INCFG [16..16] ============================================= */ +typedef enum { /*!< GPIO_CFGC_GPIO20INCFG */ + GPIO_CFGC_GPIO20INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGC_GPIO20INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGC_GPIO20INCFG_Enum; + +/* ============================================= GPIO CFGC GPIO19INTD [15..15] ============================================= */ +typedef enum { /*!< GPIO_CFGC_GPIO19INTD */ + GPIO_CFGC_GPIO19INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGC_GPIO19INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGC_GPIO19INTD_Enum; + +/* ============================================ GPIO CFGC GPIO19OUTCFG [13..14] ============================================ */ +typedef enum { /*!< GPIO_CFGC_GPIO19OUTCFG */ + GPIO_CFGC_GPIO19OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGC_GPIO19OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGC_GPIO19OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGC_GPIO19OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGC_GPIO19OUTCFG_Enum; + +/* ============================================ GPIO CFGC GPIO19INCFG [12..12] ============================================= */ +typedef enum { /*!< GPIO_CFGC_GPIO19INCFG */ + GPIO_CFGC_GPIO19INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGC_GPIO19INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGC_GPIO19INCFG_Enum; + +/* ============================================= GPIO CFGC GPIO18INTD [11..11] ============================================= */ +typedef enum { /*!< GPIO_CFGC_GPIO18INTD */ + GPIO_CFGC_GPIO18INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGC_GPIO18INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGC_GPIO18INTD_Enum; + +/* ============================================ GPIO CFGC GPIO18OUTCFG [9..10] ============================================= */ +typedef enum { /*!< GPIO_CFGC_GPIO18OUTCFG */ + GPIO_CFGC_GPIO18OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGC_GPIO18OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGC_GPIO18OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGC_GPIO18OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGC_GPIO18OUTCFG_Enum; + +/* ============================================= GPIO CFGC GPIO18INCFG [8..8] ============================================== */ +typedef enum { /*!< GPIO_CFGC_GPIO18INCFG */ + GPIO_CFGC_GPIO18INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGC_GPIO18INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGC_GPIO18INCFG_Enum; + +/* ============================================== GPIO CFGC GPIO17INTD [7..7] ============================================== */ +typedef enum { /*!< GPIO_CFGC_GPIO17INTD */ + GPIO_CFGC_GPIO17INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGC_GPIO17INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGC_GPIO17INTD_Enum; + +/* ============================================= GPIO CFGC GPIO17OUTCFG [5..6] ============================================= */ +typedef enum { /*!< GPIO_CFGC_GPIO17OUTCFG */ + GPIO_CFGC_GPIO17OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGC_GPIO17OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGC_GPIO17OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGC_GPIO17OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGC_GPIO17OUTCFG_Enum; + +/* ============================================= GPIO CFGC GPIO17INCFG [4..4] ============================================== */ +typedef enum { /*!< GPIO_CFGC_GPIO17INCFG */ + GPIO_CFGC_GPIO17INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGC_GPIO17INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGC_GPIO17INCFG_Enum; + +/* ============================================== GPIO CFGC GPIO16INTD [3..3] ============================================== */ +typedef enum { /*!< GPIO_CFGC_GPIO16INTD */ + GPIO_CFGC_GPIO16INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGC_GPIO16INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGC_GPIO16INTD_Enum; + +/* ============================================= GPIO CFGC GPIO16OUTCFG [1..2] ============================================= */ +typedef enum { /*!< GPIO_CFGC_GPIO16OUTCFG */ + GPIO_CFGC_GPIO16OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGC_GPIO16OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGC_GPIO16OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGC_GPIO16OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGC_GPIO16OUTCFG_Enum; + +/* ============================================= GPIO CFGC GPIO16INCFG [0..0] ============================================== */ +typedef enum { /*!< GPIO_CFGC_GPIO16INCFG */ + GPIO_CFGC_GPIO16INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGC_GPIO16INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGC_GPIO16INCFG_Enum; + +/* ========================================================= CFGD ========================================================== */ +/* ============================================= GPIO CFGD GPIO31INTD [31..31] ============================================= */ +typedef enum { /*!< GPIO_CFGD_GPIO31INTD */ + GPIO_CFGD_GPIO31INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGD_GPIO31INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGD_GPIO31INTD_Enum; + +/* ============================================ GPIO CFGD GPIO31OUTCFG [29..30] ============================================ */ +typedef enum { /*!< GPIO_CFGD_GPIO31OUTCFG */ + GPIO_CFGD_GPIO31OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGD_GPIO31OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGD_GPIO31OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGD_GPIO31OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGD_GPIO31OUTCFG_Enum; + +/* ============================================ GPIO CFGD GPIO31INCFG [28..28] ============================================= */ +typedef enum { /*!< GPIO_CFGD_GPIO31INCFG */ + GPIO_CFGD_GPIO31INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGD_GPIO31INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGD_GPIO31INCFG_Enum; + +/* ============================================= GPIO CFGD GPIO30INTD [27..27] ============================================= */ +typedef enum { /*!< GPIO_CFGD_GPIO30INTD */ + GPIO_CFGD_GPIO30INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGD_GPIO30INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGD_GPIO30INTD_Enum; + +/* ============================================ GPIO CFGD GPIO30OUTCFG [25..26] ============================================ */ +typedef enum { /*!< GPIO_CFGD_GPIO30OUTCFG */ + GPIO_CFGD_GPIO30OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGD_GPIO30OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGD_GPIO30OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGD_GPIO30OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGD_GPIO30OUTCFG_Enum; + +/* ============================================ GPIO CFGD GPIO30INCFG [24..24] ============================================= */ +typedef enum { /*!< GPIO_CFGD_GPIO30INCFG */ + GPIO_CFGD_GPIO30INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGD_GPIO30INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGD_GPIO30INCFG_Enum; + +/* ============================================= GPIO CFGD GPIO29INTD [23..23] ============================================= */ +typedef enum { /*!< GPIO_CFGD_GPIO29INTD */ + GPIO_CFGD_GPIO29INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGD_GPIO29INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGD_GPIO29INTD_Enum; + +/* ============================================ GPIO CFGD GPIO29OUTCFG [21..22] ============================================ */ +typedef enum { /*!< GPIO_CFGD_GPIO29OUTCFG */ + GPIO_CFGD_GPIO29OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGD_GPIO29OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGD_GPIO29OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGD_GPIO29OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGD_GPIO29OUTCFG_Enum; + +/* ============================================ GPIO CFGD GPIO29INCFG [20..20] ============================================= */ +typedef enum { /*!< GPIO_CFGD_GPIO29INCFG */ + GPIO_CFGD_GPIO29INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGD_GPIO29INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGD_GPIO29INCFG_Enum; + +/* ============================================= GPIO CFGD GPIO28INTD [19..19] ============================================= */ +typedef enum { /*!< GPIO_CFGD_GPIO28INTD */ + GPIO_CFGD_GPIO28INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGD_GPIO28INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGD_GPIO28INTD_Enum; + +/* ============================================ GPIO CFGD GPIO28OUTCFG [17..18] ============================================ */ +typedef enum { /*!< GPIO_CFGD_GPIO28OUTCFG */ + GPIO_CFGD_GPIO28OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGD_GPIO28OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGD_GPIO28OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGD_GPIO28OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGD_GPIO28OUTCFG_Enum; + +/* ============================================ GPIO CFGD GPIO28INCFG [16..16] ============================================= */ +typedef enum { /*!< GPIO_CFGD_GPIO28INCFG */ + GPIO_CFGD_GPIO28INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGD_GPIO28INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGD_GPIO28INCFG_Enum; + +/* ============================================= GPIO CFGD GPIO27INTD [15..15] ============================================= */ +typedef enum { /*!< GPIO_CFGD_GPIO27INTD */ + GPIO_CFGD_GPIO27INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGD_GPIO27INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGD_GPIO27INTD_Enum; + +/* ============================================ GPIO CFGD GPIO27OUTCFG [13..14] ============================================ */ +typedef enum { /*!< GPIO_CFGD_GPIO27OUTCFG */ + GPIO_CFGD_GPIO27OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGD_GPIO27OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGD_GPIO27OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGD_GPIO27OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGD_GPIO27OUTCFG_Enum; + +/* ============================================ GPIO CFGD GPIO27INCFG [12..12] ============================================= */ +typedef enum { /*!< GPIO_CFGD_GPIO27INCFG */ + GPIO_CFGD_GPIO27INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGD_GPIO27INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGD_GPIO27INCFG_Enum; + +/* ============================================= GPIO CFGD GPIO26INTD [11..11] ============================================= */ +typedef enum { /*!< GPIO_CFGD_GPIO26INTD */ + GPIO_CFGD_GPIO26INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGD_GPIO26INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGD_GPIO26INTD_Enum; + +/* ============================================ GPIO CFGD GPIO26OUTCFG [9..10] ============================================= */ +typedef enum { /*!< GPIO_CFGD_GPIO26OUTCFG */ + GPIO_CFGD_GPIO26OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGD_GPIO26OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGD_GPIO26OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGD_GPIO26OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGD_GPIO26OUTCFG_Enum; + +/* ============================================= GPIO CFGD GPIO26INCFG [8..8] ============================================== */ +typedef enum { /*!< GPIO_CFGD_GPIO26INCFG */ + GPIO_CFGD_GPIO26INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGD_GPIO26INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGD_GPIO26INCFG_Enum; + +/* ============================================== GPIO CFGD GPIO25INTD [7..7] ============================================== */ +typedef enum { /*!< GPIO_CFGD_GPIO25INTD */ + GPIO_CFGD_GPIO25INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGD_GPIO25INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGD_GPIO25INTD_Enum; + +/* ============================================= GPIO CFGD GPIO25OUTCFG [5..6] ============================================= */ +typedef enum { /*!< GPIO_CFGD_GPIO25OUTCFG */ + GPIO_CFGD_GPIO25OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGD_GPIO25OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGD_GPIO25OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGD_GPIO25OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGD_GPIO25OUTCFG_Enum; + +/* ============================================= GPIO CFGD GPIO25INCFG [4..4] ============================================== */ +typedef enum { /*!< GPIO_CFGD_GPIO25INCFG */ + GPIO_CFGD_GPIO25INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGD_GPIO25INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGD_GPIO25INCFG_Enum; + +/* ============================================== GPIO CFGD GPIO24INTD [3..3] ============================================== */ +typedef enum { /*!< GPIO_CFGD_GPIO24INTD */ + GPIO_CFGD_GPIO24INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGD_GPIO24INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGD_GPIO24INTD_Enum; + +/* ============================================= GPIO CFGD GPIO24OUTCFG [1..2] ============================================= */ +typedef enum { /*!< GPIO_CFGD_GPIO24OUTCFG */ + GPIO_CFGD_GPIO24OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGD_GPIO24OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGD_GPIO24OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGD_GPIO24OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGD_GPIO24OUTCFG_Enum; + +/* ============================================= GPIO CFGD GPIO24INCFG [0..0] ============================================== */ +typedef enum { /*!< GPIO_CFGD_GPIO24INCFG */ + GPIO_CFGD_GPIO24INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGD_GPIO24INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGD_GPIO24INCFG_Enum; + +/* ========================================================= CFGE ========================================================== */ +/* ============================================= GPIO CFGE GPIO39INTD [31..31] ============================================= */ +typedef enum { /*!< GPIO_CFGE_GPIO39INTD */ + GPIO_CFGE_GPIO39INTD_INTDIS = 0, /*!< INTDIS : INCFG = 1 - No interrupt on GPIO transition value. */ + GPIO_CFGE_GPIO39INTD_INTBOTH = 1, /*!< INTBOTH : INCFG = 1 - Interrupt on either low to high or high + to low GPIO transition value. */ +} GPIO_CFGE_GPIO39INTD_Enum; + +/* ============================================ GPIO CFGE GPIO39OUTCFG [29..30] ============================================ */ +typedef enum { /*!< GPIO_CFGE_GPIO39OUTCFG */ + GPIO_CFGE_GPIO39OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGE_GPIO39OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGE_GPIO39OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGE_GPIO39OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGE_GPIO39OUTCFG_Enum; + +/* ============================================ GPIO CFGE GPIO39INCFG [28..28] ============================================= */ +typedef enum { /*!< GPIO_CFGE_GPIO39INCFG */ + GPIO_CFGE_GPIO39INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGE_GPIO39INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGE_GPIO39INCFG_Enum; + +/* ============================================= GPIO CFGE GPIO38INTD [27..27] ============================================= */ +typedef enum { /*!< GPIO_CFGE_GPIO38INTD */ + GPIO_CFGE_GPIO38INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGE_GPIO38INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGE_GPIO38INTD_Enum; + +/* ============================================ GPIO CFGE GPIO38OUTCFG [25..26] ============================================ */ +typedef enum { /*!< GPIO_CFGE_GPIO38OUTCFG */ + GPIO_CFGE_GPIO38OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGE_GPIO38OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGE_GPIO38OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGE_GPIO38OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGE_GPIO38OUTCFG_Enum; + +/* ============================================ GPIO CFGE GPIO38INCFG [24..24] ============================================= */ +typedef enum { /*!< GPIO_CFGE_GPIO38INCFG */ + GPIO_CFGE_GPIO38INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGE_GPIO38INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGE_GPIO38INCFG_Enum; + +/* ============================================= GPIO CFGE GPIO37INTD [23..23] ============================================= */ +typedef enum { /*!< GPIO_CFGE_GPIO37INTD */ + GPIO_CFGE_GPIO37INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGE_GPIO37INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGE_GPIO37INTD_Enum; + +/* ============================================ GPIO CFGE GPIO37OUTCFG [21..22] ============================================ */ +typedef enum { /*!< GPIO_CFGE_GPIO37OUTCFG */ + GPIO_CFGE_GPIO37OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGE_GPIO37OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGE_GPIO37OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGE_GPIO37OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGE_GPIO37OUTCFG_Enum; + +/* ============================================ GPIO CFGE GPIO37INCFG [20..20] ============================================= */ +typedef enum { /*!< GPIO_CFGE_GPIO37INCFG */ + GPIO_CFGE_GPIO37INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGE_GPIO37INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGE_GPIO37INCFG_Enum; + +/* ============================================= GPIO CFGE GPIO36INTD [19..19] ============================================= */ +typedef enum { /*!< GPIO_CFGE_GPIO36INTD */ + GPIO_CFGE_GPIO36INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGE_GPIO36INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGE_GPIO36INTD_Enum; + +/* ============================================ GPIO CFGE GPIO36OUTCFG [17..18] ============================================ */ +typedef enum { /*!< GPIO_CFGE_GPIO36OUTCFG */ + GPIO_CFGE_GPIO36OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGE_GPIO36OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGE_GPIO36OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGE_GPIO36OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGE_GPIO36OUTCFG_Enum; + +/* ============================================ GPIO CFGE GPIO36INCFG [16..16] ============================================= */ +typedef enum { /*!< GPIO_CFGE_GPIO36INCFG */ + GPIO_CFGE_GPIO36INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGE_GPIO36INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGE_GPIO36INCFG_Enum; + +/* ============================================= GPIO CFGE GPIO35INTD [15..15] ============================================= */ +typedef enum { /*!< GPIO_CFGE_GPIO35INTD */ + GPIO_CFGE_GPIO35INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGE_GPIO35INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGE_GPIO35INTD_Enum; + +/* ============================================ GPIO CFGE GPIO35OUTCFG [13..14] ============================================ */ +typedef enum { /*!< GPIO_CFGE_GPIO35OUTCFG */ + GPIO_CFGE_GPIO35OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGE_GPIO35OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGE_GPIO35OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGE_GPIO35OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGE_GPIO35OUTCFG_Enum; + +/* ============================================ GPIO CFGE GPIO35INCFG [12..12] ============================================= */ +typedef enum { /*!< GPIO_CFGE_GPIO35INCFG */ + GPIO_CFGE_GPIO35INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGE_GPIO35INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGE_GPIO35INCFG_Enum; + +/* ============================================= GPIO CFGE GPIO34INTD [11..11] ============================================= */ +typedef enum { /*!< GPIO_CFGE_GPIO34INTD */ + GPIO_CFGE_GPIO34INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGE_GPIO34INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGE_GPIO34INTD_Enum; + +/* ============================================ GPIO CFGE GPIO34OUTCFG [9..10] ============================================= */ +typedef enum { /*!< GPIO_CFGE_GPIO34OUTCFG */ + GPIO_CFGE_GPIO34OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGE_GPIO34OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGE_GPIO34OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGE_GPIO34OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGE_GPIO34OUTCFG_Enum; + +/* ============================================= GPIO CFGE GPIO34INCFG [8..8] ============================================== */ +typedef enum { /*!< GPIO_CFGE_GPIO34INCFG */ + GPIO_CFGE_GPIO34INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGE_GPIO34INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGE_GPIO34INCFG_Enum; + +/* ============================================== GPIO CFGE GPIO33INTD [7..7] ============================================== */ +typedef enum { /*!< GPIO_CFGE_GPIO33INTD */ + GPIO_CFGE_GPIO33INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGE_GPIO33INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGE_GPIO33INTD_Enum; + +/* ============================================= GPIO CFGE GPIO33OUTCFG [5..6] ============================================= */ +typedef enum { /*!< GPIO_CFGE_GPIO33OUTCFG */ + GPIO_CFGE_GPIO33OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGE_GPIO33OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGE_GPIO33OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGE_GPIO33OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGE_GPIO33OUTCFG_Enum; + +/* ============================================= GPIO CFGE GPIO33INCFG [4..4] ============================================== */ +typedef enum { /*!< GPIO_CFGE_GPIO33INCFG */ + GPIO_CFGE_GPIO33INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGE_GPIO33INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGE_GPIO33INCFG_Enum; + +/* ============================================== GPIO CFGE GPIO32INTD [3..3] ============================================== */ +typedef enum { /*!< GPIO_CFGE_GPIO32INTD */ + GPIO_CFGE_GPIO32INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGE_GPIO32INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGE_GPIO32INTD_Enum; + +/* ============================================= GPIO CFGE GPIO32OUTCFG [1..2] ============================================= */ +typedef enum { /*!< GPIO_CFGE_GPIO32OUTCFG */ + GPIO_CFGE_GPIO32OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGE_GPIO32OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGE_GPIO32OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGE_GPIO32OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGE_GPIO32OUTCFG_Enum; + +/* ============================================= GPIO CFGE GPIO32INCFG [0..0] ============================================== */ +typedef enum { /*!< GPIO_CFGE_GPIO32INCFG */ + GPIO_CFGE_GPIO32INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGE_GPIO32INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGE_GPIO32INCFG_Enum; + +/* ========================================================= CFGF ========================================================== */ +/* ============================================= GPIO CFGF GPIO47INTD [31..31] ============================================= */ +typedef enum { /*!< GPIO_CFGF_GPIO47INTD */ + GPIO_CFGF_GPIO47INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGF_GPIO47INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGF_GPIO47INTD_Enum; + +/* ============================================ GPIO CFGF GPIO47OUTCFG [29..30] ============================================ */ +typedef enum { /*!< GPIO_CFGF_GPIO47OUTCFG */ + GPIO_CFGF_GPIO47OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGF_GPIO47OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGF_GPIO47OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGF_GPIO47OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGF_GPIO47OUTCFG_Enum; + +/* ============================================ GPIO CFGF GPIO47INCFG [28..28] ============================================= */ +typedef enum { /*!< GPIO_CFGF_GPIO47INCFG */ + GPIO_CFGF_GPIO47INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGF_GPIO47INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGF_GPIO47INCFG_Enum; + +/* ============================================= GPIO CFGF GPIO46INTD [27..27] ============================================= */ +typedef enum { /*!< GPIO_CFGF_GPIO46INTD */ + GPIO_CFGF_GPIO46INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGF_GPIO46INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGF_GPIO46INTD_Enum; + +/* ============================================ GPIO CFGF GPIO46OUTCFG [25..26] ============================================ */ +typedef enum { /*!< GPIO_CFGF_GPIO46OUTCFG */ + GPIO_CFGF_GPIO46OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGF_GPIO46OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGF_GPIO46OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGF_GPIO46OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGF_GPIO46OUTCFG_Enum; + +/* ============================================ GPIO CFGF GPIO46INCFG [24..24] ============================================= */ +typedef enum { /*!< GPIO_CFGF_GPIO46INCFG */ + GPIO_CFGF_GPIO46INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGF_GPIO46INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGF_GPIO46INCFG_Enum; + +/* ============================================= GPIO CFGF GPIO45INTD [23..23] ============================================= */ +typedef enum { /*!< GPIO_CFGF_GPIO45INTD */ + GPIO_CFGF_GPIO45INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGF_GPIO45INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGF_GPIO45INTD_Enum; + +/* ============================================ GPIO CFGF GPIO45OUTCFG [21..22] ============================================ */ +typedef enum { /*!< GPIO_CFGF_GPIO45OUTCFG */ + GPIO_CFGF_GPIO45OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGF_GPIO45OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGF_GPIO45OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGF_GPIO45OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGF_GPIO45OUTCFG_Enum; + +/* ============================================ GPIO CFGF GPIO45INCFG [20..20] ============================================= */ +typedef enum { /*!< GPIO_CFGF_GPIO45INCFG */ + GPIO_CFGF_GPIO45INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGF_GPIO45INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGF_GPIO45INCFG_Enum; + +/* ============================================= GPIO CFGF GPIO44INTD [19..19] ============================================= */ +typedef enum { /*!< GPIO_CFGF_GPIO44INTD */ + GPIO_CFGF_GPIO44INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGF_GPIO44INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGF_GPIO44INTD_Enum; + +/* ============================================ GPIO CFGF GPIO44OUTCFG [17..18] ============================================ */ +typedef enum { /*!< GPIO_CFGF_GPIO44OUTCFG */ + GPIO_CFGF_GPIO44OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGF_GPIO44OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGF_GPIO44OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGF_GPIO44OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGF_GPIO44OUTCFG_Enum; + +/* ============================================ GPIO CFGF GPIO44INCFG [16..16] ============================================= */ +typedef enum { /*!< GPIO_CFGF_GPIO44INCFG */ + GPIO_CFGF_GPIO44INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGF_GPIO44INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGF_GPIO44INCFG_Enum; + +/* ============================================= GPIO CFGF GPIO43INTD [15..15] ============================================= */ +typedef enum { /*!< GPIO_CFGF_GPIO43INTD */ + GPIO_CFGF_GPIO43INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGF_GPIO43INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGF_GPIO43INTD_Enum; + +/* ============================================ GPIO CFGF GPIO43OUTCFG [13..14] ============================================ */ +typedef enum { /*!< GPIO_CFGF_GPIO43OUTCFG */ + GPIO_CFGF_GPIO43OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGF_GPIO43OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGF_GPIO43OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGF_GPIO43OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGF_GPIO43OUTCFG_Enum; + +/* ============================================ GPIO CFGF GPIO43INCFG [12..12] ============================================= */ +typedef enum { /*!< GPIO_CFGF_GPIO43INCFG */ + GPIO_CFGF_GPIO43INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGF_GPIO43INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGF_GPIO43INCFG_Enum; + +/* ============================================= GPIO CFGF GPIO42INTD [11..11] ============================================= */ +typedef enum { /*!< GPIO_CFGF_GPIO42INTD */ + GPIO_CFGF_GPIO42INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGF_GPIO42INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGF_GPIO42INTD_Enum; + +/* ============================================ GPIO CFGF GPIO42OUTCFG [9..10] ============================================= */ +typedef enum { /*!< GPIO_CFGF_GPIO42OUTCFG */ + GPIO_CFGF_GPIO42OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGF_GPIO42OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGF_GPIO42OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGF_GPIO42OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGF_GPIO42OUTCFG_Enum; + +/* ============================================= GPIO CFGF GPIO42INCFG [8..8] ============================================== */ +typedef enum { /*!< GPIO_CFGF_GPIO42INCFG */ + GPIO_CFGF_GPIO42INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGF_GPIO42INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGF_GPIO42INCFG_Enum; + +/* ============================================== GPIO CFGF GPIO41INTD [7..7] ============================================== */ +typedef enum { /*!< GPIO_CFGF_GPIO41INTD */ + GPIO_CFGF_GPIO41INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x0 - nCE polarity active low value. */ + GPIO_CFGF_GPIO41INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x0 - nCE polarity active high value. */ +} GPIO_CFGF_GPIO41INTD_Enum; + +/* ============================================= GPIO CFGF GPIO41OUTCFG [5..6] ============================================= */ +typedef enum { /*!< GPIO_CFGF_GPIO41OUTCFG */ + GPIO_CFGF_GPIO41OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGF_GPIO41OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGF_GPIO41OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGF_GPIO41OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGF_GPIO41OUTCFG_Enum; + +/* ============================================= GPIO CFGF GPIO41INCFG [4..4] ============================================== */ +typedef enum { /*!< GPIO_CFGF_GPIO41INCFG */ + GPIO_CFGF_GPIO41INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGF_GPIO41INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGF_GPIO41INCFG_Enum; + +/* ============================================== GPIO CFGF GPIO40INTD [3..3] ============================================== */ +typedef enum { /*!< GPIO_CFGF_GPIO40INTD */ + GPIO_CFGF_GPIO40INTD_INTDIS = 0, /*!< INTDIS : INCFG = 1 - No interrupt on GPIO transition value. */ + GPIO_CFGF_GPIO40INTD_INTBOTH = 1, /*!< INTBOTH : INCFG = 1 - Interrupt on either low to high or high + to low GPIO transition value. */ +} GPIO_CFGF_GPIO40INTD_Enum; + +/* ============================================= GPIO CFGF GPIO40OUTCFG [1..2] ============================================= */ +typedef enum { /*!< GPIO_CFGF_GPIO40OUTCFG */ + GPIO_CFGF_GPIO40OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGF_GPIO40OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGF_GPIO40OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGF_GPIO40OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGF_GPIO40OUTCFG_Enum; + +/* ============================================= GPIO CFGF GPIO40INCFG [0..0] ============================================== */ +typedef enum { /*!< GPIO_CFGF_GPIO40INCFG */ + GPIO_CFGF_GPIO40INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGF_GPIO40INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGF_GPIO40INCFG_Enum; + +/* ========================================================= CFGG ========================================================== */ +/* ============================================== GPIO CFGG GPIO49INTD [7..7] ============================================== */ +typedef enum { /*!< GPIO_CFGG_GPIO49INTD */ + GPIO_CFGG_GPIO49INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGG_GPIO49INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGG_GPIO49INTD_Enum; + +/* ============================================= GPIO CFGG GPIO49OUTCFG [5..6] ============================================= */ +typedef enum { /*!< GPIO_CFGG_GPIO49OUTCFG */ + GPIO_CFGG_GPIO49OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGG_GPIO49OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGG_GPIO49OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGG_GPIO49OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGG_GPIO49OUTCFG_Enum; + +/* ============================================= GPIO CFGG GPIO49INCFG [4..4] ============================================== */ +typedef enum { /*!< GPIO_CFGG_GPIO49INCFG */ + GPIO_CFGG_GPIO49INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGG_GPIO49INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGG_GPIO49INCFG_Enum; + +/* ============================================== GPIO CFGG GPIO48INTD [3..3] ============================================== */ +typedef enum { /*!< GPIO_CFGG_GPIO48INTD */ + GPIO_CFGG_GPIO48INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ + GPIO_CFGG_GPIO48INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ +} GPIO_CFGG_GPIO48INTD_Enum; + +/* ============================================= GPIO CFGG GPIO48OUTCFG [1..2] ============================================= */ +typedef enum { /*!< GPIO_CFGG_GPIO48OUTCFG */ + GPIO_CFGG_GPIO48OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ + GPIO_CFGG_GPIO48OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ + GPIO_CFGG_GPIO48OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ + GPIO_CFGG_GPIO48OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ +} GPIO_CFGG_GPIO48OUTCFG_Enum; + +/* ============================================= GPIO CFGG GPIO48INCFG [0..0] ============================================== */ +typedef enum { /*!< GPIO_CFGG_GPIO48INCFG */ + GPIO_CFGG_GPIO48INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ + GPIO_CFGG_GPIO48INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ +} GPIO_CFGG_GPIO48INCFG_Enum; + +/* ======================================================== PADKEY ========================================================= */ +/* ============================================== GPIO PADKEY PADKEY [0..31] =============================================== */ +typedef enum { /*!< GPIO_PADKEY_PADKEY */ + GPIO_PADKEY_PADKEY_Key = 115, /*!< Key : Key value. */ +} GPIO_PADKEY_PADKEY_Enum; + +/* ========================================================== RDA ========================================================== */ +/* ========================================================== RDB ========================================================== */ +/* ========================================================== WTA ========================================================== */ +/* ========================================================== WTB ========================================================== */ +/* ========================================================= WTSA ========================================================== */ +/* ========================================================= WTSB ========================================================== */ +/* ========================================================= WTCA ========================================================== */ +/* ========================================================= WTCB ========================================================== */ +/* ========================================================== ENA ========================================================== */ +/* ========================================================== ENB ========================================================== */ +/* ========================================================= ENSA ========================================================== */ +/* ========================================================= ENSB ========================================================== */ +/* ========================================================= ENCA ========================================================== */ +/* ========================================================= ENCB ========================================================== */ +/* ======================================================== STMRCAP ======================================================== */ +/* ============================================= GPIO STMRCAP STPOL3 [30..30] ============================================== */ +typedef enum { /*!< GPIO_STMRCAP_STPOL3 */ + GPIO_STMRCAP_STPOL3_CAPLH = 0, /*!< CAPLH : Capture on low to high GPIO transition value. */ + GPIO_STMRCAP_STPOL3_CAPHL = 1, /*!< CAPHL : Capture on high to low GPIO transition value. */ +} GPIO_STMRCAP_STPOL3_Enum; + +/* ============================================= GPIO STMRCAP STPOL2 [22..22] ============================================== */ +typedef enum { /*!< GPIO_STMRCAP_STPOL2 */ + GPIO_STMRCAP_STPOL2_CAPLH = 0, /*!< CAPLH : Capture on low to high GPIO transition value. */ + GPIO_STMRCAP_STPOL2_CAPHL = 1, /*!< CAPHL : Capture on high to low GPIO transition value. */ +} GPIO_STMRCAP_STPOL2_Enum; + +/* ============================================= GPIO STMRCAP STPOL1 [14..14] ============================================== */ +typedef enum { /*!< GPIO_STMRCAP_STPOL1 */ + GPIO_STMRCAP_STPOL1_CAPLH = 0, /*!< CAPLH : Capture on low to high GPIO transition value. */ + GPIO_STMRCAP_STPOL1_CAPHL = 1, /*!< CAPHL : Capture on high to low GPIO transition value. */ +} GPIO_STMRCAP_STPOL1_Enum; + +/* ============================================== GPIO STMRCAP STPOL0 [6..6] =============================================== */ +typedef enum { /*!< GPIO_STMRCAP_STPOL0 */ + GPIO_STMRCAP_STPOL0_CAPLH = 0, /*!< CAPLH : Capture on low to high GPIO transition value. */ + GPIO_STMRCAP_STPOL0_CAPHL = 1, /*!< CAPHL : Capture on high to low GPIO transition value. */ +} GPIO_STMRCAP_STPOL0_Enum; + +/* ======================================================== IOM0IRQ ======================================================== */ +/* ======================================================== IOM1IRQ ======================================================== */ +/* ======================================================== IOM2IRQ ======================================================== */ +/* ======================================================== IOM3IRQ ======================================================== */ +/* ======================================================== IOM4IRQ ======================================================== */ +/* ======================================================== IOM5IRQ ======================================================== */ +/* ======================================================= BLEIFIRQ ======================================================== */ +/* ======================================================== GPIOOBS ======================================================== */ +/* ====================================================== ALTPADCFGA ======================================================= */ +/* =========================================== GPIO ALTPADCFGA PAD3_SR [28..28] ============================================ */ +typedef enum { /*!< GPIO_ALTPADCFGA_PAD3_SR */ + GPIO_ALTPADCFGA_PAD3_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGA_PAD3_SR_Enum; + +/* =========================================== GPIO ALTPADCFGA PAD2_SR [20..20] ============================================ */ +typedef enum { /*!< GPIO_ALTPADCFGA_PAD2_SR */ + GPIO_ALTPADCFGA_PAD2_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGA_PAD2_SR_Enum; + +/* =========================================== GPIO ALTPADCFGA PAD1_SR [12..12] ============================================ */ +typedef enum { /*!< GPIO_ALTPADCFGA_PAD1_SR */ + GPIO_ALTPADCFGA_PAD1_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGA_PAD1_SR_Enum; + +/* ============================================ GPIO ALTPADCFGA PAD0_SR [4..4] ============================================= */ +typedef enum { /*!< GPIO_ALTPADCFGA_PAD0_SR */ + GPIO_ALTPADCFGA_PAD0_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGA_PAD0_SR_Enum; + +/* ====================================================== ALTPADCFGB ======================================================= */ +/* =========================================== GPIO ALTPADCFGB PAD7_SR [28..28] ============================================ */ +typedef enum { /*!< GPIO_ALTPADCFGB_PAD7_SR */ + GPIO_ALTPADCFGB_PAD7_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGB_PAD7_SR_Enum; + +/* =========================================== GPIO ALTPADCFGB PAD6_SR [20..20] ============================================ */ +typedef enum { /*!< GPIO_ALTPADCFGB_PAD6_SR */ + GPIO_ALTPADCFGB_PAD6_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGB_PAD6_SR_Enum; + +/* =========================================== GPIO ALTPADCFGB PAD5_SR [12..12] ============================================ */ +typedef enum { /*!< GPIO_ALTPADCFGB_PAD5_SR */ + GPIO_ALTPADCFGB_PAD5_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGB_PAD5_SR_Enum; + +/* ============================================ GPIO ALTPADCFGB PAD4_SR [4..4] ============================================= */ +typedef enum { /*!< GPIO_ALTPADCFGB_PAD4_SR */ + GPIO_ALTPADCFGB_PAD4_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGB_PAD4_SR_Enum; + +/* ====================================================== ALTPADCFGC ======================================================= */ +/* =========================================== GPIO ALTPADCFGC PAD11_SR [28..28] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGC_PAD11_SR */ + GPIO_ALTPADCFGC_PAD11_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGC_PAD11_SR_Enum; + +/* =========================================== GPIO ALTPADCFGC PAD10_SR [20..20] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGC_PAD10_SR */ + GPIO_ALTPADCFGC_PAD10_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGC_PAD10_SR_Enum; + +/* =========================================== GPIO ALTPADCFGC PAD9_SR [12..12] ============================================ */ +typedef enum { /*!< GPIO_ALTPADCFGC_PAD9_SR */ + GPIO_ALTPADCFGC_PAD9_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGC_PAD9_SR_Enum; + +/* ============================================ GPIO ALTPADCFGC PAD8_SR [4..4] ============================================= */ +typedef enum { /*!< GPIO_ALTPADCFGC_PAD8_SR */ + GPIO_ALTPADCFGC_PAD8_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGC_PAD8_SR_Enum; + +/* ====================================================== ALTPADCFGD ======================================================= */ +/* =========================================== GPIO ALTPADCFGD PAD15_SR [28..28] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGD_PAD15_SR */ + GPIO_ALTPADCFGD_PAD15_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGD_PAD15_SR_Enum; + +/* =========================================== GPIO ALTPADCFGD PAD14_SR [20..20] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGD_PAD14_SR */ + GPIO_ALTPADCFGD_PAD14_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGD_PAD14_SR_Enum; + +/* =========================================== GPIO ALTPADCFGD PAD13_SR [12..12] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGD_PAD13_SR */ + GPIO_ALTPADCFGD_PAD13_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGD_PAD13_SR_Enum; + +/* ============================================ GPIO ALTPADCFGD PAD12_SR [4..4] ============================================ */ +typedef enum { /*!< GPIO_ALTPADCFGD_PAD12_SR */ + GPIO_ALTPADCFGD_PAD12_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGD_PAD12_SR_Enum; + +/* ====================================================== ALTPADCFGE ======================================================= */ +/* =========================================== GPIO ALTPADCFGE PAD19_SR [28..28] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGE_PAD19_SR */ + GPIO_ALTPADCFGE_PAD19_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGE_PAD19_SR_Enum; + +/* =========================================== GPIO ALTPADCFGE PAD18_SR [20..20] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGE_PAD18_SR */ + GPIO_ALTPADCFGE_PAD18_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGE_PAD18_SR_Enum; + +/* =========================================== GPIO ALTPADCFGE PAD17_SR [12..12] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGE_PAD17_SR */ + GPIO_ALTPADCFGE_PAD17_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGE_PAD17_SR_Enum; + +/* ============================================ GPIO ALTPADCFGE PAD16_SR [4..4] ============================================ */ +typedef enum { /*!< GPIO_ALTPADCFGE_PAD16_SR */ + GPIO_ALTPADCFGE_PAD16_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGE_PAD16_SR_Enum; + +/* ====================================================== ALTPADCFGF ======================================================= */ +/* =========================================== GPIO ALTPADCFGF PAD23_SR [28..28] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGF_PAD23_SR */ + GPIO_ALTPADCFGF_PAD23_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGF_PAD23_SR_Enum; + +/* =========================================== GPIO ALTPADCFGF PAD22_SR [20..20] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGF_PAD22_SR */ + GPIO_ALTPADCFGF_PAD22_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGF_PAD22_SR_Enum; + +/* =========================================== GPIO ALTPADCFGF PAD21_SR [12..12] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGF_PAD21_SR */ + GPIO_ALTPADCFGF_PAD21_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGF_PAD21_SR_Enum; + +/* ============================================ GPIO ALTPADCFGF PAD20_SR [4..4] ============================================ */ +typedef enum { /*!< GPIO_ALTPADCFGF_PAD20_SR */ + GPIO_ALTPADCFGF_PAD20_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGF_PAD20_SR_Enum; + +/* ====================================================== ALTPADCFGG ======================================================= */ +/* =========================================== GPIO ALTPADCFGG PAD27_SR [28..28] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGG_PAD27_SR */ + GPIO_ALTPADCFGG_PAD27_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGG_PAD27_SR_Enum; + +/* =========================================== GPIO ALTPADCFGG PAD26_SR [20..20] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGG_PAD26_SR */ + GPIO_ALTPADCFGG_PAD26_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGG_PAD26_SR_Enum; + +/* =========================================== GPIO ALTPADCFGG PAD25_SR [12..12] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGG_PAD25_SR */ + GPIO_ALTPADCFGG_PAD25_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGG_PAD25_SR_Enum; + +/* ============================================ GPIO ALTPADCFGG PAD24_SR [4..4] ============================================ */ +typedef enum { /*!< GPIO_ALTPADCFGG_PAD24_SR */ + GPIO_ALTPADCFGG_PAD24_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGG_PAD24_SR_Enum; + +/* ====================================================== ALTPADCFGH ======================================================= */ +/* =========================================== GPIO ALTPADCFGH PAD31_SR [28..28] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGH_PAD31_SR */ + GPIO_ALTPADCFGH_PAD31_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGH_PAD31_SR_Enum; + +/* =========================================== GPIO ALTPADCFGH PAD30_SR [20..20] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGH_PAD30_SR */ + GPIO_ALTPADCFGH_PAD30_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGH_PAD30_SR_Enum; + +/* =========================================== GPIO ALTPADCFGH PAD29_SR [12..12] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGH_PAD29_SR */ + GPIO_ALTPADCFGH_PAD29_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGH_PAD29_SR_Enum; + +/* ============================================ GPIO ALTPADCFGH PAD28_SR [4..4] ============================================ */ +typedef enum { /*!< GPIO_ALTPADCFGH_PAD28_SR */ + GPIO_ALTPADCFGH_PAD28_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGH_PAD28_SR_Enum; + +/* ====================================================== ALTPADCFGI ======================================================= */ +/* =========================================== GPIO ALTPADCFGI PAD35_SR [28..28] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGI_PAD35_SR */ + GPIO_ALTPADCFGI_PAD35_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGI_PAD35_SR_Enum; + +/* =========================================== GPIO ALTPADCFGI PAD34_SR [20..20] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGI_PAD34_SR */ + GPIO_ALTPADCFGI_PAD34_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGI_PAD34_SR_Enum; + +/* =========================================== GPIO ALTPADCFGI PAD33_SR [12..12] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGI_PAD33_SR */ + GPIO_ALTPADCFGI_PAD33_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGI_PAD33_SR_Enum; + +/* ============================================ GPIO ALTPADCFGI PAD32_SR [4..4] ============================================ */ +typedef enum { /*!< GPIO_ALTPADCFGI_PAD32_SR */ + GPIO_ALTPADCFGI_PAD32_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGI_PAD32_SR_Enum; + +/* ====================================================== ALTPADCFGJ ======================================================= */ +/* =========================================== GPIO ALTPADCFGJ PAD39_SR [28..28] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGJ_PAD39_SR */ + GPIO_ALTPADCFGJ_PAD39_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGJ_PAD39_SR_Enum; + +/* =========================================== GPIO ALTPADCFGJ PAD38_SR [20..20] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGJ_PAD38_SR */ + GPIO_ALTPADCFGJ_PAD38_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGJ_PAD38_SR_Enum; + +/* =========================================== GPIO ALTPADCFGJ PAD37_SR [12..12] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGJ_PAD37_SR */ + GPIO_ALTPADCFGJ_PAD37_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGJ_PAD37_SR_Enum; + +/* ============================================ GPIO ALTPADCFGJ PAD36_SR [4..4] ============================================ */ +typedef enum { /*!< GPIO_ALTPADCFGJ_PAD36_SR */ + GPIO_ALTPADCFGJ_PAD36_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGJ_PAD36_SR_Enum; + +/* ====================================================== ALTPADCFGK ======================================================= */ +/* =========================================== GPIO ALTPADCFGK PAD43_SR [28..28] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGK_PAD43_SR */ + GPIO_ALTPADCFGK_PAD43_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGK_PAD43_SR_Enum; + +/* =========================================== GPIO ALTPADCFGK PAD42_SR [20..20] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGK_PAD42_SR */ + GPIO_ALTPADCFGK_PAD42_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGK_PAD42_SR_Enum; + +/* =========================================== GPIO ALTPADCFGK PAD41_SR [12..12] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGK_PAD41_SR */ + GPIO_ALTPADCFGK_PAD41_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGK_PAD41_SR_Enum; + +/* ============================================ GPIO ALTPADCFGK PAD40_SR [4..4] ============================================ */ +typedef enum { /*!< GPIO_ALTPADCFGK_PAD40_SR */ + GPIO_ALTPADCFGK_PAD40_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGK_PAD40_SR_Enum; + +/* ====================================================== ALTPADCFGL ======================================================= */ +/* =========================================== GPIO ALTPADCFGL PAD47_SR [28..28] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGL_PAD47_SR */ + GPIO_ALTPADCFGL_PAD47_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGL_PAD47_SR_Enum; + +/* =========================================== GPIO ALTPADCFGL PAD46_SR [20..20] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGL_PAD46_SR */ + GPIO_ALTPADCFGL_PAD46_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGL_PAD46_SR_Enum; + +/* =========================================== GPIO ALTPADCFGL PAD45_SR [12..12] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGL_PAD45_SR */ + GPIO_ALTPADCFGL_PAD45_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGL_PAD45_SR_Enum; + +/* ============================================ GPIO ALTPADCFGL PAD44_SR [4..4] ============================================ */ +typedef enum { /*!< GPIO_ALTPADCFGL_PAD44_SR */ + GPIO_ALTPADCFGL_PAD44_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGL_PAD44_SR_Enum; + +/* ====================================================== ALTPADCFGM ======================================================= */ +/* =========================================== GPIO ALTPADCFGM PAD49_SR [12..12] =========================================== */ +typedef enum { /*!< GPIO_ALTPADCFGM_PAD49_SR */ + GPIO_ALTPADCFGM_PAD49_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGM_PAD49_SR_Enum; + +/* ============================================ GPIO ALTPADCFGM PAD48_SR [4..4] ============================================ */ +typedef enum { /*!< GPIO_ALTPADCFGM_PAD48_SR */ + GPIO_ALTPADCFGM_PAD48_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ +} GPIO_ALTPADCFGM_PAD48_SR_Enum; + +/* ========================================================= SCDET ========================================================= */ +/* ======================================================== CTENCFG ======================================================== */ +/* ============================================== GPIO CTENCFG EN31 [31..31] =============================================== */ +typedef enum { /*!< GPIO_CTENCFG_EN31 */ + GPIO_CTENCFG_EN31_DIS = 1, /*!< DIS : Disable CT31 for output value. */ + GPIO_CTENCFG_EN31_EN = 0, /*!< EN : Enable CT31 for output value. */ +} GPIO_CTENCFG_EN31_Enum; + +/* ============================================== GPIO CTENCFG EN30 [30..30] =============================================== */ +typedef enum { /*!< GPIO_CTENCFG_EN30 */ + GPIO_CTENCFG_EN30_DIS = 1, /*!< DIS : Disable CT30 for output value. */ + GPIO_CTENCFG_EN30_EN = 0, /*!< EN : Enable CT30 for output value. */ +} GPIO_CTENCFG_EN30_Enum; + +/* ============================================== GPIO CTENCFG EN29 [29..29] =============================================== */ +typedef enum { /*!< GPIO_CTENCFG_EN29 */ + GPIO_CTENCFG_EN29_DIS = 1, /*!< DIS : Disable CT29 for output value. */ + GPIO_CTENCFG_EN29_EN = 0, /*!< EN : Enable CT29 for output value. */ +} GPIO_CTENCFG_EN29_Enum; + +/* ============================================== GPIO CTENCFG EN28 [28..28] =============================================== */ +typedef enum { /*!< GPIO_CTENCFG_EN28 */ + GPIO_CTENCFG_EN28_DIS = 1, /*!< DIS : Disable CT28 for output value. */ + GPIO_CTENCFG_EN28_EN = 0, /*!< EN : Enable CT28 for output value. */ +} GPIO_CTENCFG_EN28_Enum; + +/* ============================================== GPIO CTENCFG EN27 [27..27] =============================================== */ +typedef enum { /*!< GPIO_CTENCFG_EN27 */ + GPIO_CTENCFG_EN27_DIS = 1, /*!< DIS : Disable CT27 for output value. */ + GPIO_CTENCFG_EN27_EN = 0, /*!< EN : Enable CT27 for output value. */ +} GPIO_CTENCFG_EN27_Enum; + +/* ============================================== GPIO CTENCFG EN26 [26..26] =============================================== */ +typedef enum { /*!< GPIO_CTENCFG_EN26 */ + GPIO_CTENCFG_EN26_DIS = 1, /*!< DIS : Disable CT26 for output value. */ + GPIO_CTENCFG_EN26_EN = 0, /*!< EN : Enable CT26 for output value. */ +} GPIO_CTENCFG_EN26_Enum; + +/* ============================================== GPIO CTENCFG EN25 [25..25] =============================================== */ +typedef enum { /*!< GPIO_CTENCFG_EN25 */ + GPIO_CTENCFG_EN25_DIS = 1, /*!< DIS : Disable CT25 for output value. */ + GPIO_CTENCFG_EN25_EN = 0, /*!< EN : Enable CT25 for output value. */ +} GPIO_CTENCFG_EN25_Enum; + +/* ============================================== GPIO CTENCFG EN24 [24..24] =============================================== */ +typedef enum { /*!< GPIO_CTENCFG_EN24 */ + GPIO_CTENCFG_EN24_DIS = 1, /*!< DIS : Disable CT24 for output value. */ + GPIO_CTENCFG_EN24_EN = 0, /*!< EN : Enable CT24 for output value. */ +} GPIO_CTENCFG_EN24_Enum; + +/* ============================================== GPIO CTENCFG EN23 [23..23] =============================================== */ +typedef enum { /*!< GPIO_CTENCFG_EN23 */ + GPIO_CTENCFG_EN23_DIS = 1, /*!< DIS : Disable CT23 for output value. */ + GPIO_CTENCFG_EN23_EN = 0, /*!< EN : Enable CT23 for output value. */ +} GPIO_CTENCFG_EN23_Enum; + +/* ============================================== GPIO CTENCFG EN22 [22..22] =============================================== */ +typedef enum { /*!< GPIO_CTENCFG_EN22 */ + GPIO_CTENCFG_EN22_DIS = 1, /*!< DIS : Disable CT22 for output value. */ + GPIO_CTENCFG_EN22_EN = 0, /*!< EN : Enable CT22 for output value. */ +} GPIO_CTENCFG_EN22_Enum; + +/* ============================================== GPIO CTENCFG EN21 [21..21] =============================================== */ +typedef enum { /*!< GPIO_CTENCFG_EN21 */ + GPIO_CTENCFG_EN21_DIS = 1, /*!< DIS : Disable CT21 for output value. */ + GPIO_CTENCFG_EN21_EN = 0, /*!< EN : Enable CT21 for output value. */ +} GPIO_CTENCFG_EN21_Enum; + +/* ============================================== GPIO CTENCFG EN20 [20..20] =============================================== */ +typedef enum { /*!< GPIO_CTENCFG_EN20 */ + GPIO_CTENCFG_EN20_DIS = 1, /*!< DIS : Disable CT20 for output value. */ + GPIO_CTENCFG_EN20_EN = 0, /*!< EN : Enable CT20 for output value. */ +} GPIO_CTENCFG_EN20_Enum; + +/* ============================================== GPIO CTENCFG EN19 [19..19] =============================================== */ +typedef enum { /*!< GPIO_CTENCFG_EN19 */ + GPIO_CTENCFG_EN19_DIS = 1, /*!< DIS : Disable CT19 for output value. */ + GPIO_CTENCFG_EN19_EN = 0, /*!< EN : Enable CT19 for output value. */ +} GPIO_CTENCFG_EN19_Enum; + +/* ============================================== GPIO CTENCFG EN18 [18..18] =============================================== */ +typedef enum { /*!< GPIO_CTENCFG_EN18 */ + GPIO_CTENCFG_EN18_DIS = 1, /*!< DIS : Disable CT18 for output value. */ + GPIO_CTENCFG_EN18_EN = 0, /*!< EN : Enable CT18 for output value. */ +} GPIO_CTENCFG_EN18_Enum; + +/* ============================================== GPIO CTENCFG EN17 [17..17] =============================================== */ +typedef enum { /*!< GPIO_CTENCFG_EN17 */ + GPIO_CTENCFG_EN17_DIS = 1, /*!< DIS : Disable CT17 for output value. */ + GPIO_CTENCFG_EN17_EN = 0, /*!< EN : Enable CT17 for output value. */ +} GPIO_CTENCFG_EN17_Enum; + +/* ============================================== GPIO CTENCFG EN16 [16..16] =============================================== */ +typedef enum { /*!< GPIO_CTENCFG_EN16 */ + GPIO_CTENCFG_EN16_DIS = 1, /*!< DIS : Disable CT16 for output value. */ + GPIO_CTENCFG_EN16_EN = 0, /*!< EN : Enable CT16 for output value. */ +} GPIO_CTENCFG_EN16_Enum; + +/* ============================================== GPIO CTENCFG EN15 [15..15] =============================================== */ +typedef enum { /*!< GPIO_CTENCFG_EN15 */ + GPIO_CTENCFG_EN15_DIS = 1, /*!< DIS : Disable CT15 for output value. */ + GPIO_CTENCFG_EN15_EN = 0, /*!< EN : Enable CT15 for output value. */ +} GPIO_CTENCFG_EN15_Enum; + +/* ============================================== GPIO CTENCFG EN14 [14..14] =============================================== */ +typedef enum { /*!< GPIO_CTENCFG_EN14 */ + GPIO_CTENCFG_EN14_DIS = 1, /*!< DIS : Disable CT14 for output value. */ + GPIO_CTENCFG_EN14_EN = 0, /*!< EN : Enable CT14 for output value. */ +} GPIO_CTENCFG_EN14_Enum; + +/* ============================================== GPIO CTENCFG EN13 [13..13] =============================================== */ +typedef enum { /*!< GPIO_CTENCFG_EN13 */ + GPIO_CTENCFG_EN13_DIS = 1, /*!< DIS : Disable CT13 for output value. */ + GPIO_CTENCFG_EN13_EN = 0, /*!< EN : Enable CT13 for output value. */ +} GPIO_CTENCFG_EN13_Enum; + +/* ============================================== GPIO CTENCFG EN12 [12..12] =============================================== */ +typedef enum { /*!< GPIO_CTENCFG_EN12 */ + GPIO_CTENCFG_EN12_DIS = 1, /*!< DIS : Disable CT12 for output value. */ + GPIO_CTENCFG_EN12_EN = 0, /*!< EN : Enable CT12 for output value. */ +} GPIO_CTENCFG_EN12_Enum; + +/* ============================================== GPIO CTENCFG EN11 [11..11] =============================================== */ +typedef enum { /*!< GPIO_CTENCFG_EN11 */ + GPIO_CTENCFG_EN11_DIS = 1, /*!< DIS : Disable CT11 for output value. */ + GPIO_CTENCFG_EN11_EN = 0, /*!< EN : Enable CT11 for output value. */ +} GPIO_CTENCFG_EN11_Enum; + +/* ============================================== GPIO CTENCFG EN10 [10..10] =============================================== */ +typedef enum { /*!< GPIO_CTENCFG_EN10 */ + GPIO_CTENCFG_EN10_DIS = 1, /*!< DIS : Disable CT10 for output value. */ + GPIO_CTENCFG_EN10_EN = 0, /*!< EN : Enable CT10 for output value. */ +} GPIO_CTENCFG_EN10_Enum; + +/* ================================================ GPIO CTENCFG EN9 [9..9] ================================================ */ +typedef enum { /*!< GPIO_CTENCFG_EN9 */ + GPIO_CTENCFG_EN9_DIS = 0, /*!< DIS : Disable CT9 for output value. */ +} GPIO_CTENCFG_EN9_Enum; + +/* ================================================ GPIO CTENCFG EN8 [8..8] ================================================ */ +typedef enum { /*!< GPIO_CTENCFG_EN8 */ + GPIO_CTENCFG_EN8_DIS = 1, /*!< DIS : Disable CT8 for output value. */ + GPIO_CTENCFG_EN8_EN = 0, /*!< EN : Enable CT8 for output value. */ +} GPIO_CTENCFG_EN8_Enum; + +/* ================================================ GPIO CTENCFG EN7 [7..7] ================================================ */ +typedef enum { /*!< GPIO_CTENCFG_EN7 */ + GPIO_CTENCFG_EN7_DIS = 1, /*!< DIS : Disable CT7 for output value. */ + GPIO_CTENCFG_EN7_EN = 0, /*!< EN : Enable CT7 for output value. */ +} GPIO_CTENCFG_EN7_Enum; + +/* ================================================ GPIO CTENCFG EN6 [6..6] ================================================ */ +typedef enum { /*!< GPIO_CTENCFG_EN6 */ + GPIO_CTENCFG_EN6_DIS = 1, /*!< DIS : Disable CT6 for output value. */ + GPIO_CTENCFG_EN6_EN = 0, /*!< EN : Enable CT6 for output value. */ +} GPIO_CTENCFG_EN6_Enum; + +/* ================================================ GPIO CTENCFG EN5 [5..5] ================================================ */ +typedef enum { /*!< GPIO_CTENCFG_EN5 */ + GPIO_CTENCFG_EN5_DIS = 1, /*!< DIS : Disable CT5 for output value. */ + GPIO_CTENCFG_EN5_EN = 0, /*!< EN : Enable CT5 for output value. */ +} GPIO_CTENCFG_EN5_Enum; + +/* ================================================ GPIO CTENCFG EN4 [4..4] ================================================ */ +typedef enum { /*!< GPIO_CTENCFG_EN4 */ + GPIO_CTENCFG_EN4_DIS = 1, /*!< DIS : Disable CT4 for output value. */ + GPIO_CTENCFG_EN4_EN = 0, /*!< EN : Enable CT4 for output value. */ +} GPIO_CTENCFG_EN4_Enum; + +/* ================================================ GPIO CTENCFG EN3 [3..3] ================================================ */ +typedef enum { /*!< GPIO_CTENCFG_EN3 */ + GPIO_CTENCFG_EN3_DIS = 1, /*!< DIS : Disable CT3 for output value. */ + GPIO_CTENCFG_EN3_EN = 0, /*!< EN : Enable CT3 for output value. */ +} GPIO_CTENCFG_EN3_Enum; + +/* ================================================ GPIO CTENCFG EN2 [2..2] ================================================ */ +typedef enum { /*!< GPIO_CTENCFG_EN2 */ + GPIO_CTENCFG_EN2_DIS = 1, /*!< DIS : Disable CT2 for output value. */ + GPIO_CTENCFG_EN2_EN = 0, /*!< EN : Enable CT2 for output value. */ +} GPIO_CTENCFG_EN2_Enum; + +/* ================================================ GPIO CTENCFG EN1 [1..1] ================================================ */ +typedef enum { /*!< GPIO_CTENCFG_EN1 */ + GPIO_CTENCFG_EN1_DIS = 1, /*!< DIS : Disable CT1 for output value. */ + GPIO_CTENCFG_EN1_EN = 0, /*!< EN : Enable CT1 for output value. */ +} GPIO_CTENCFG_EN1_Enum; + +/* ================================================ GPIO CTENCFG EN0 [0..0] ================================================ */ +typedef enum { /*!< GPIO_CTENCFG_EN0 */ + GPIO_CTENCFG_EN0_DIS = 1, /*!< DIS : Disable CT0 for output value. */ + GPIO_CTENCFG_EN0_EN = 0, /*!< EN : Enable CT0 for output value. */ +} GPIO_CTENCFG_EN0_Enum; + +/* ======================================================== INT0EN ========================================================= */ +/* ======================================================= INT0STAT ======================================================== */ +/* ======================================================== INT0CLR ======================================================== */ +/* ======================================================== INT0SET ======================================================== */ +/* ======================================================== INT1EN ========================================================= */ +/* ======================================================= INT1STAT ======================================================== */ +/* ======================================================== INT1CLR ======================================================== */ +/* ======================================================== INT1SET ======================================================== */ + + +/* =========================================================================================================================== */ +/* ================ IOM0 ================ */ +/* =========================================================================================================================== */ + +/* ========================================================= FIFO ========================================================== */ +/* ======================================================== FIFOPTR ======================================================== */ +/* ======================================================== FIFOTHR ======================================================== */ +/* ======================================================== FIFOPOP ======================================================== */ +/* ======================================================= FIFOPUSH ======================================================== */ +/* ======================================================= FIFOCTRL ======================================================== */ +/* ======================================================== FIFOLOC ======================================================== */ +/* ========================================================= INTEN ========================================================= */ +/* ======================================================== INTSTAT ======================================================== */ +/* ======================================================== INTCLR ========================================================= */ +/* ======================================================== INTSET ========================================================= */ +/* ======================================================== CLKCFG ========================================================= */ +/* ============================================== IOM0 CLKCFG DIVEN [12..12] =============================================== */ +typedef enum { /*!< IOM0_CLKCFG_DIVEN */ + IOM0_CLKCFG_DIVEN_DIS = 0, /*!< DIS : Disable TOTPER division. value. */ + IOM0_CLKCFG_DIVEN_EN = 1, /*!< EN : Enable TOTPER division. value. */ +} IOM0_CLKCFG_DIVEN_Enum; + +/* =============================================== IOM0 CLKCFG DIV3 [11..11] =============================================== */ +typedef enum { /*!< IOM0_CLKCFG_DIV3 */ + IOM0_CLKCFG_DIV3_DIS = 0, /*!< DIS : Select divide by 1. value. */ + IOM0_CLKCFG_DIV3_EN = 1, /*!< EN : Select divide by 3. value. */ +} IOM0_CLKCFG_DIV3_Enum; + +/* =============================================== IOM0 CLKCFG FSEL [8..10] ================================================ */ +typedef enum { /*!< IOM0_CLKCFG_FSEL */ + IOM0_CLKCFG_FSEL_MIN_PWR = 0, /*!< MIN_PWR : Selects the minimum power clock. This setting should + be used whenever the IOM is not active. value. */ + IOM0_CLKCFG_FSEL_HFRC = 1, /*!< HFRC : Selects the HFRC as the input clock. value. */ + IOM0_CLKCFG_FSEL_HFRC_DIV2 = 2, /*!< HFRC_DIV2 : Selects the HFRC / 2 as the input clock. value. */ + IOM0_CLKCFG_FSEL_HFRC_DIV4 = 3, /*!< HFRC_DIV4 : Selects the HFRC / 4 as the input clock. value. */ + IOM0_CLKCFG_FSEL_HFRC_DIV8 = 4, /*!< HFRC_DIV8 : Selects the HFRC / 8 as the input clock. value. */ + IOM0_CLKCFG_FSEL_HFRC_DIV16 = 5, /*!< HFRC_DIV16 : Selects the HFRC / 16 as the input clock. value. */ + IOM0_CLKCFG_FSEL_HFRC_DIV32 = 6, /*!< HFRC_DIV32 : Selects the HFRC / 32 as the input clock. value. */ + IOM0_CLKCFG_FSEL_HFRC_DIV64 = 7, /*!< HFRC_DIV64 : Selects the HFRC / 64 as the input clock. value. */ +} IOM0_CLKCFG_FSEL_Enum; + +/* ====================================================== SUBMODCTRL ======================================================= */ +/* =========================================== IOM0 SUBMODCTRL SMOD1TYPE [5..7] ============================================ */ +typedef enum { /*!< IOM0_SUBMODCTRL_SMOD1TYPE */ + IOM0_SUBMODCTRL_SMOD1TYPE_MSPI = 0, /*!< MSPI : SPI Master submodule value. */ + IOM0_SUBMODCTRL_SMOD1TYPE_I2C_MASTER = 1, /*!< I2C_MASTER : MI2C submodule value. */ + IOM0_SUBMODCTRL_SMOD1TYPE_SSPI = 2, /*!< SSPI : SPI Slave submodule value. */ + IOM0_SUBMODCTRL_SMOD1TYPE_SI2C = 3, /*!< SI2C : I2C Slave submodule value. */ + IOM0_SUBMODCTRL_SMOD1TYPE_NA = 7, /*!< NA : NOT INSTALLED value. */ +} IOM0_SUBMODCTRL_SMOD1TYPE_Enum; + +/* =========================================== IOM0 SUBMODCTRL SMOD0TYPE [1..3] ============================================ */ +typedef enum { /*!< IOM0_SUBMODCTRL_SMOD0TYPE */ + IOM0_SUBMODCTRL_SMOD0TYPE_SPI_MASTER = 0, /*!< SPI_MASTER : MSPI submodule value. */ + IOM0_SUBMODCTRL_SMOD0TYPE_I2C_MASTER = 1, /*!< I2C_MASTER : I2C Master submodule value. */ + IOM0_SUBMODCTRL_SMOD0TYPE_SSPI = 2, /*!< SSPI : SPI Slave submodule value. */ + IOM0_SUBMODCTRL_SMOD0TYPE_SI2C = 3, /*!< SI2C : I2C Slave submodule value. */ + IOM0_SUBMODCTRL_SMOD0TYPE_NA = 7, /*!< NA : NOT INSTALLED value. */ +} IOM0_SUBMODCTRL_SMOD0TYPE_Enum; + +/* ========================================================== CMD ========================================================== */ +/* ================================================== IOM0 CMD CMD [0..4] ================================================== */ +typedef enum { /*!< IOM0_CMD_CMD */ + IOM0_CMD_CMD_WRITE = 1, /*!< WRITE : Write command using count of offset bytes specified + in the OFFSETCNT field value. */ + IOM0_CMD_CMD_READ = 2, /*!< READ : Read command using count of offset bytes specified in + the OFFSETCNT field value. */ + IOM0_CMD_CMD_TMW = 3, /*!< TMW : SPI only. Test mode to do constant write operations. Useful + for debug and power measurements. Will continually send + data in OFFSET field value. */ + IOM0_CMD_CMD_TMR = 4, /*!< TMR : SPI Only. Test mode to do constant read operations. Useful + for debug and power measurements. Will continually read + data from external input value. */ +} IOM0_CMD_CMD_Enum; + +/* ======================================================== CMDRPT ========================================================= */ +/* ======================================================= OFFSETHI ======================================================== */ +/* ======================================================== CMDSTAT ======================================================== */ +/* ============================================== IOM0 CMDSTAT CMDSTAT [5..7] ============================================== */ +typedef enum { /*!< IOM0_CMDSTAT_CMDSTAT */ + IOM0_CMDSTAT_CMDSTAT_ERR = 1, /*!< ERR : Error encountered with command value. */ + IOM0_CMDSTAT_CMDSTAT_ACTIVE = 2, /*!< ACTIVE : Actively processing command value. */ + IOM0_CMDSTAT_CMDSTAT_IDLE = 4, /*!< IDLE : Idle state, no active command, no error value. */ + IOM0_CMDSTAT_CMDSTAT_WAIT = 6, /*!< WAIT : Command in progress, but waiting on data from host value. */ +} IOM0_CMDSTAT_CMDSTAT_Enum; + +/* ======================================================= DMATRIGEN ======================================================= */ +/* ====================================================== DMATRIGSTAT ====================================================== */ +/* ======================================================== DMACFG ========================================================= */ +/* ============================================== IOM0 DMACFG DPWROFF [9..9] =============================================== */ +typedef enum { /*!< IOM0_DMACFG_DPWROFF */ + IOM0_DMACFG_DPWROFF_DIS = 0, /*!< DIS : Power off disabled value. */ + IOM0_DMACFG_DPWROFF_EN = 1, /*!< EN : Power off enabled value. */ +} IOM0_DMACFG_DPWROFF_Enum; + +/* =============================================== IOM0 DMACFG DMAPRI [8..8] =============================================== */ +typedef enum { /*!< IOM0_DMACFG_DMAPRI */ + IOM0_DMACFG_DMAPRI_LOW = 0, /*!< LOW : Low Priority (service as best effort) value. */ + IOM0_DMACFG_DMAPRI_HIGH = 1, /*!< HIGH : High Priority (service immediately) value. */ +} IOM0_DMACFG_DMAPRI_Enum; + +/* =============================================== IOM0 DMACFG DMADIR [1..1] =============================================== */ +typedef enum { /*!< IOM0_DMACFG_DMADIR */ + IOM0_DMACFG_DMADIR_P2M = 0, /*!< P2M : Peripheral to Memory (SRAM) transaction. To be set when + doing IOM read operations, ie reading data from external + devices. value. */ + IOM0_DMACFG_DMADIR_M2P = 1, /*!< M2P : Memory to Peripheral transaction. To be set when doing + IOM write operations, ie writing data to external devices. + value. */ +} IOM0_DMACFG_DMADIR_Enum; + +/* =============================================== IOM0 DMACFG DMAEN [0..0] ================================================ */ +typedef enum { /*!< IOM0_DMACFG_DMAEN */ + IOM0_DMACFG_DMAEN_DIS = 0, /*!< DIS : Disable DMA Function value. */ + IOM0_DMACFG_DMAEN_EN = 1, /*!< EN : Enable DMA Function value. */ +} IOM0_DMACFG_DMAEN_Enum; + +/* ====================================================== DMATOTCOUNT ====================================================== */ +/* ====================================================== DMATARGADDR ====================================================== */ +/* ======================================================== DMASTAT ======================================================== */ +/* ========================================================= CQCFG ========================================================= */ +/* ================================================ IOM0 CQCFG CQPRI [1..1] ================================================ */ +typedef enum { /*!< IOM0_CQCFG_CQPRI */ + IOM0_CQCFG_CQPRI_LOW = 0, /*!< LOW : Low Priority (service as best effort) value. */ + IOM0_CQCFG_CQPRI_HIGH = 1, /*!< HIGH : High Priority (service immediately) value. */ +} IOM0_CQCFG_CQPRI_Enum; + +/* ================================================ IOM0 CQCFG CQEN [0..0] ================================================= */ +typedef enum { /*!< IOM0_CQCFG_CQEN */ + IOM0_CQCFG_CQEN_DIS = 0, /*!< DIS : Disable CQ Function value. */ + IOM0_CQCFG_CQEN_EN = 1, /*!< EN : Enable CQ Function value. */ +} IOM0_CQCFG_CQEN_Enum; + +/* ======================================================== CQADDR ========================================================= */ +/* ======================================================== CQSTAT ========================================================= */ +/* ======================================================== CQFLAGS ======================================================== */ +/* ====================================================== CQSETCLEAR ======================================================= */ +/* ======================================================= CQPAUSEEN ======================================================= */ +/* ============================================= IOM0 CQPAUSEEN CQPEN [0..15] ============================================== */ +typedef enum { /*!< IOM0_CQPAUSEEN_CQPEN */ + IOM0_CQPAUSEEN_CQPEN_IDXEQ = 32768, /*!< IDXEQ : Pauses the command queue when the current index matches + the last index value. */ + IOM0_CQPAUSEEN_CQPEN_BLEXOREN = 16384, /*!< BLEXOREN : Pause command queue when input BLE bit XORed with + SWFLAG4 is '1' value. */ + IOM0_CQPAUSEEN_CQPEN_IOMXOREN = 8192, /*!< IOMXOREN : Pause command queue when input IOM bit XORed with + SWFLAG3 is '1' value. */ + IOM0_CQPAUSEEN_CQPEN_GPIOXOREN = 4096, /*!< GPIOXOREN : Pause command queue when input GPIO irq_bit XORed + with SWFLAG2 is '1' value. */ + IOM0_CQPAUSEEN_CQPEN_MSPI1XNOREN = 2048, /*!< MSPI1XNOREN : Pause command queue when input MSPI1 bit XNORed + with SWFLAG1 is '1' value. */ + IOM0_CQPAUSEEN_CQPEN_MSPI0XNOREN = 1024, /*!< MSPI0XNOREN : Pause command queue when input MSPI0 bit XNORed + with SWFLAG0 is '1' value. */ + IOM0_CQPAUSEEN_CQPEN_MSPI1XOREN = 512, /*!< MSPI1XOREN : Pause command queue when input MSPI1 bit XORed + with SWFLAG1 is '1' value. */ + IOM0_CQPAUSEEN_CQPEN_MSPI0XOREN = 256, /*!< MSPI0XOREN : Pause command queue when input MSPI0 bit XORed + with SWFLAG0 is '1' value. */ + IOM0_CQPAUSEEN_CQPEN_SWFLAGEN7 = 128, /*!< SWFLAGEN7 : Pause the command queue when software flag bit 7 + is '1'. value. */ + IOM0_CQPAUSEEN_CQPEN_SWFLAGEN6 = 64, /*!< SWFLAGEN6 : Pause the command queue when software flag bit 6 + is '1' value. */ + IOM0_CQPAUSEEN_CQPEN_SWFLAGEN5 = 32, /*!< SWFLAGEN5 : Pause the command queue when software flag bit 5 + is '1' value. */ + IOM0_CQPAUSEEN_CQPEN_SWFLAGEN4 = 16, /*!< SWFLAGEN4 : Pause the command queue when software flag bit 4 + is '1' value. */ + IOM0_CQPAUSEEN_CQPEN_SWFLAGEN3 = 8, /*!< SWFLAGEN3 : Pause the command queue when software flag bit 3 + is '1' value. */ + IOM0_CQPAUSEEN_CQPEN_SWFLAGEN2 = 4, /*!< SWFLAGEN2 : Pause the command queue when software flag bit 2 + is '1' value. */ + IOM0_CQPAUSEEN_CQPEN_SWFLAGEN1 = 2, /*!< SWFLAGEN1 : Pause the command queue when software flag bit 1 + is '1' value. */ + IOM0_CQPAUSEEN_CQPEN_SWFLAGEN0 = 1, /*!< SWFLAGEN0 : Pause the command queue when software flag bit 0 + is '1' value. */ +} IOM0_CQPAUSEEN_CQPEN_Enum; + +/* ======================================================= CQCURIDX ======================================================== */ +/* ======================================================= CQENDIDX ======================================================== */ +/* ======================================================== STATUS ========================================================= */ +/* =============================================== IOM0 STATUS IDLEST [2..2] =============================================== */ +typedef enum { /*!< IOM0_STATUS_IDLEST */ + IOM0_STATUS_IDLEST_IDLE = 1, /*!< IDLE : The I/O state machine is in the idle state. value. */ +} IOM0_STATUS_IDLEST_Enum; + +/* =============================================== IOM0 STATUS CMDACT [1..1] =============================================== */ +typedef enum { /*!< IOM0_STATUS_CMDACT */ + IOM0_STATUS_CMDACT_ACTIVE = 1, /*!< ACTIVE : An I/O command is active. Indicates the active module + has an active command and is processing this. De-asserted + when the command is completed. value. */ +} IOM0_STATUS_CMDACT_Enum; + +/* ================================================ IOM0 STATUS ERR [0..0] ================================================= */ +typedef enum { /*!< IOM0_STATUS_ERR */ + IOM0_STATUS_ERR_ERROR = 1, /*!< ERROR : Bit has been deprecated and will always return 0. value. */ +} IOM0_STATUS_ERR_Enum; + +/* ======================================================== MSPICFG ======================================================== */ +/* ============================================= IOM0 MSPICFG SPILSB [23..23] ============================================== */ +typedef enum { /*!< IOM0_MSPICFG_SPILSB */ + IOM0_MSPICFG_SPILSB_MSB = 0, /*!< MSB : Send and receive MSB bit first value. */ + IOM0_MSPICFG_SPILSB_LSB = 1, /*!< LSB : Send and receive LSB bit first value. */ +} IOM0_MSPICFG_SPILSB_Enum; + +/* ============================================= IOM0 MSPICFG RDFCPOL [22..22] ============================================= */ +typedef enum { /*!< IOM0_MSPICFG_RDFCPOL */ + IOM0_MSPICFG_RDFCPOL_HIGH = 0, /*!< HIGH : Flow control signal high creates flow control. value. */ + IOM0_MSPICFG_RDFCPOL_LOW = 1, /*!< LOW : Flow control signal low creates flow control. value. */ +} IOM0_MSPICFG_RDFCPOL_Enum; + +/* ============================================= IOM0 MSPICFG WTFCPOL [21..21] ============================================= */ +typedef enum { /*!< IOM0_MSPICFG_WTFCPOL */ + IOM0_MSPICFG_WTFCPOL_HIGH = 0, /*!< HIGH : Flow control signal high(1) creates flow control and + byte transfers will stop until the flow control signal + goes low. value. */ + IOM0_MSPICFG_WTFCPOL_LOW = 1, /*!< LOW : Flow control signal low(0) creates flow control and byte + transfers will stop until the flow control signal goes + high(1). value. */ +} IOM0_MSPICFG_WTFCPOL_Enum; + +/* ============================================= IOM0 MSPICFG WTFCIRQ [20..20] ============================================= */ +typedef enum { /*!< IOM0_MSPICFG_WTFCIRQ */ + IOM0_MSPICFG_WTFCIRQ_MISO = 0, /*!< MISO : MISO is used as the write mode flow control signal. value. */ + IOM0_MSPICFG_WTFCIRQ_IRQ = 1, /*!< IRQ : IRQ is used as the write mode flow control signal. value. */ +} IOM0_MSPICFG_WTFCIRQ_Enum; + +/* ============================================= IOM0 MSPICFG MOSIINV [18..18] ============================================= */ +typedef enum { /*!< IOM0_MSPICFG_MOSIINV */ + IOM0_MSPICFG_MOSIINV_NORMAL = 0, /*!< NORMAL : MOSI is set to 0 in read mode and 1 in write mode. + value. */ + IOM0_MSPICFG_MOSIINV_INVERT = 1, /*!< INVERT : MOSI is set to 1 in read mode and 0 in write mode. + value. */ +} IOM0_MSPICFG_MOSIINV_Enum; + +/* ============================================== IOM0 MSPICFG RDFC [17..17] =============================================== */ +typedef enum { /*!< IOM0_MSPICFG_RDFC */ + IOM0_MSPICFG_RDFC_DIS = 0, /*!< DIS : Read mode flow control disabled. value. */ + IOM0_MSPICFG_RDFC_EN = 1, /*!< EN : Read mode flow control enabled. value. */ +} IOM0_MSPICFG_RDFC_Enum; + +/* ============================================== IOM0 MSPICFG WTFC [16..16] =============================================== */ +typedef enum { /*!< IOM0_MSPICFG_WTFC */ + IOM0_MSPICFG_WTFC_DIS = 0, /*!< DIS : Write mode flow control disabled. value. */ + IOM0_MSPICFG_WTFC_EN = 1, /*!< EN : Write mode flow control enabled. value. */ +} IOM0_MSPICFG_WTFC_Enum; + +/* =============================================== IOM0 MSPICFG SPHA [1..1] ================================================ */ +typedef enum { /*!< IOM0_MSPICFG_SPHA */ + IOM0_MSPICFG_SPHA_SAMPLE_LEADING_EDGE = 0, /*!< SAMPLE_LEADING_EDGE : Sample on the leading (first) clock edge. + value. */ + IOM0_MSPICFG_SPHA_SAMPLE_TRAILING_EDGE = 1, /*!< SAMPLE_TRAILING_EDGE : Sample on the trailing (second) clock + edge. value. */ +} IOM0_MSPICFG_SPHA_Enum; + +/* =============================================== IOM0 MSPICFG SPOL [0..0] ================================================ */ +typedef enum { /*!< IOM0_MSPICFG_SPOL */ + IOM0_MSPICFG_SPOL_CLK_BASE_0 = 0, /*!< CLK_BASE_0 : The base value of the clock is 0. value. */ + IOM0_MSPICFG_SPOL_CLK_BASE_1 = 1, /*!< CLK_BASE_1 : The base value of the clock is 1. value. */ +} IOM0_MSPICFG_SPOL_Enum; + +/* ======================================================== MI2CCFG ======================================================== */ +/* =============================================== IOM0 MI2CCFG ARBEN [2..2] =============================================== */ +typedef enum { /*!< IOM0_MI2CCFG_ARBEN */ + IOM0_MI2CCFG_ARBEN_ARBEN = 1, /*!< ARBEN : Enable multi-master bus arbitration support for this + i2c master value. */ + IOM0_MI2CCFG_ARBEN_ARBDIS = 0, /*!< ARBDIS : Disable multi-master bus arbitration support for this + i2c master value. */ +} IOM0_MI2CCFG_ARBEN_Enum; + +/* ============================================== IOM0 MI2CCFG I2CLSB [1..1] =============================================== */ +typedef enum { /*!< IOM0_MI2CCFG_I2CLSB */ + IOM0_MI2CCFG_I2CLSB_MSBFIRST = 0, /*!< MSBFIRST : Byte data is transmitted MSB first onto the bus/read + from the bus value. */ + IOM0_MI2CCFG_I2CLSB_LSBFIRST = 1, /*!< LSBFIRST : Byte data is transmitted LSB first onto the bus/read + from the bus value. */ +} IOM0_MI2CCFG_I2CLSB_Enum; + +/* ============================================== IOM0 MI2CCFG ADDRSZ [0..0] =============================================== */ +typedef enum { /*!< IOM0_MI2CCFG_ADDRSZ */ + IOM0_MI2CCFG_ADDRSZ_ADDRSZ7 = 0, /*!< ADDRSZ7 : Use 7b addressing for I2C master transactions value. */ + IOM0_MI2CCFG_ADDRSZ_ADDRSZ10 = 1, /*!< ADDRSZ10 : Use 10b addressing for I2C master transactions value. */ +} IOM0_MI2CCFG_ADDRSZ_Enum; + +/* ======================================================== DEVCFG ========================================================= */ +/* ======================================================== IOMDBG ========================================================= */ + + +/* =========================================================================================================================== */ +/* ================ IOSLAVE ================ */ +/* =========================================================================================================================== */ + +/* ======================================================== FIFOPTR ======================================================== */ +/* ======================================================== FIFOCFG ======================================================== */ +/* ======================================================== FIFOTHR ======================================================== */ +/* ========================================================= FUPD ========================================================== */ +/* ======================================================== FIFOCTR ======================================================== */ +/* ======================================================== FIFOINC ======================================================== */ +/* ========================================================== CFG ========================================================== */ +/* ============================================== IOSLAVE CFG IFCEN [31..31] =============================================== */ +typedef enum { /*!< IOSLAVE_CFG_IFCEN */ + IOSLAVE_CFG_IFCEN_DIS = 0, /*!< DIS : Disable the IOSLAVE value. */ + IOSLAVE_CFG_IFCEN_EN = 1, /*!< EN : Enable the IOSLAVE value. */ +} IOSLAVE_CFG_IFCEN_Enum; + +/* ============================================== IOSLAVE CFG STARTRD [4..4] =============================================== */ +typedef enum { /*!< IOSLAVE_CFG_STARTRD */ + IOSLAVE_CFG_STARTRD_LATE = 0, /*!< LATE : Initiate I/O RAM read late in each transferred byte. + value. */ + IOSLAVE_CFG_STARTRD_EARLY = 1, /*!< EARLY : Initiate I/O RAM read early in each transferred byte. + value. */ +} IOSLAVE_CFG_STARTRD_Enum; + +/* ================================================ IOSLAVE CFG LSB [2..2] ================================================= */ +typedef enum { /*!< IOSLAVE_CFG_LSB */ + IOSLAVE_CFG_LSB_MSB_FIRST = 0, /*!< MSB_FIRST : Data is assumed to be sent and received with MSB + first. value. */ + IOSLAVE_CFG_LSB_LSB_FIRST = 1, /*!< LSB_FIRST : Data is assumed to be sent and received with LSB + first. value. */ +} IOSLAVE_CFG_LSB_Enum; + +/* ================================================ IOSLAVE CFG SPOL [1..1] ================================================ */ +typedef enum { /*!< IOSLAVE_CFG_SPOL */ + IOSLAVE_CFG_SPOL_SPI_MODES_0_3 = 0, /*!< SPI_MODES_0_3 : Polarity 0, handles SPI modes 0 and 3. value. */ + IOSLAVE_CFG_SPOL_SPI_MODES_1_2 = 1, /*!< SPI_MODES_1_2 : Polarity 1, handles SPI modes 1 and 2. value. */ +} IOSLAVE_CFG_SPOL_Enum; + +/* =============================================== IOSLAVE CFG IFCSEL [0..0] =============================================== */ +typedef enum { /*!< IOSLAVE_CFG_IFCSEL */ + IOSLAVE_CFG_IFCSEL_I2C = 0, /*!< I2C : Selects I2C interface for the IO Slave. value. */ + IOSLAVE_CFG_IFCSEL_SPI = 1, /*!< SPI : Selects SPI interface for the IO Slave. value. */ +} IOSLAVE_CFG_IFCSEL_Enum; + +/* ========================================================= PRENC ========================================================= */ +/* ======================================================= IOINTCTL ======================================================== */ +/* ======================================================== GENADD ========================================================= */ +/* ========================================================= INTEN ========================================================= */ +/* ======================================================== INTSTAT ======================================================== */ +/* ======================================================== INTCLR ========================================================= */ +/* ======================================================== INTSET ========================================================= */ +/* ====================================================== REGACCINTEN ====================================================== */ +/* ===================================================== REGACCINTSTAT ===================================================== */ +/* ===================================================== REGACCINTCLR ====================================================== */ +/* ===================================================== REGACCINTSET ====================================================== */ + + +/* =========================================================================================================================== */ +/* ================ MCUCTRL ================ */ +/* =========================================================================================================================== */ + +/* ======================================================== CHIPPN ========================================================= */ +/* ============================================ MCUCTRL CHIPPN PARTNUM [0..31] ============================================= */ +typedef enum { /*!< MCUCTRL_CHIPPN_PARTNUM */ + MCUCTRL_CHIPPN_PARTNUM_APOLLO3 = 100663296,/*!< APOLLO3 : Apollo3 part number is 0x06xxxxxx. value. */ + MCUCTRL_CHIPPN_PARTNUM_APOLLO2 = 50331648,/*!< APOLLO2 : Apollo2 part number is 0x03xxxxxx. value. */ + MCUCTRL_CHIPPN_PARTNUM_APOLLO = 16777216,/*!< APOLLO : Apollo part number is 0x01xxxxxx. value. */ + MCUCTRL_CHIPPN_PARTNUM_PN_M = -16777216,/*!< PN_M : Mask for the part number field. value. */ + MCUCTRL_CHIPPN_PARTNUM_PN_S = 24, /*!< PN_S : Bit position for the part number field. value. */ + MCUCTRL_CHIPPN_PARTNUM_FLASHSIZE_M = 15728640,/*!< FLASHSIZE_M : Mask for the FLASH_SIZE field.Values:0: 16KB1: + 32KB2: 64KB3: 128KB4: 256KB5: 512KB6: 1MB7: 2MB value. */ + MCUCTRL_CHIPPN_PARTNUM_FLASHSIZE_S = 20, /*!< FLASHSIZE_S : Bit position for the FLASH_SIZE field. value. */ + MCUCTRL_CHIPPN_PARTNUM_SRAMSIZE_M = 983040,/*!< SRAMSIZE_M : Mask for the SRAM_SIZE field.Values:0: 16KB1: 32KB2: + 64KB3: 128KB4: 256KB5: 512KB6: 1MB7: 384KB value. */ + MCUCTRL_CHIPPN_PARTNUM_SRAMSIZE_S = 16, /*!< SRAMSIZE_S : Bit position for the SRAM_SIZE field. value. */ + MCUCTRL_CHIPPN_PARTNUM_REV_M = 65280, /*!< REV_M : Mask for the revision field. Bits [15:12] are major + rev, [11:8] are minor rev.Values:0: Major Rev A, Minor + Rev 01: Major Rev B, Minor Rev 1 value. */ + MCUCTRL_CHIPPN_PARTNUM_REV_S = 8, /*!< REV_S : Bit position for the revision field. value. */ + MCUCTRL_CHIPPN_PARTNUM_PKG_M = 192, /*!< PKG_M : Mask for the package field.Values:0: SIP1: QFN2: BGA3: + CSP value. */ + MCUCTRL_CHIPPN_PARTNUM_PKG_S = 6, /*!< PKG_S : Bit position for the package field. value. */ + MCUCTRL_CHIPPN_PARTNUM_PINS_M = 56, /*!< PINS_M : Mask for the pins field.Values:0: 25 pins1: 49 pins2: + 64 pins3: 81 pins value. */ + MCUCTRL_CHIPPN_PARTNUM_PINS_S = 3, /*!< PINS_S : Bit position for the pins field. value. */ + MCUCTRL_CHIPPN_PARTNUM_TEMP_S = 1, /*!< TEMP_S : Bit position for the temperature field. value. */ + MCUCTRL_CHIPPN_PARTNUM_QUAL_S = 0, /*!< QUAL_S : Bit position for the qualified field. value. */ +} MCUCTRL_CHIPPN_PARTNUM_Enum; + +/* ======================================================== CHIPID0 ======================================================== */ +/* ============================================ MCUCTRL CHIPID0 CHIPID0 [0..31] ============================================ */ +typedef enum { /*!< MCUCTRL_CHIPID0_CHIPID0 */ + MCUCTRL_CHIPID0_CHIPID0_APOLLO3 = 0, /*!< APOLLO3 : Apollo3 CHIPID0. value. */ +} MCUCTRL_CHIPID0_CHIPID0_Enum; + +/* ======================================================== CHIPID1 ======================================================== */ +/* ============================================ MCUCTRL CHIPID1 CHIPID1 [0..31] ============================================ */ +typedef enum { /*!< MCUCTRL_CHIPID1_CHIPID1 */ + MCUCTRL_CHIPID1_CHIPID1_APOLLO3 = 0, /*!< APOLLO3 : Apollo3 CHIPID1. value. */ +} MCUCTRL_CHIPID1_CHIPID1_Enum; + +/* ======================================================== CHIPREV ======================================================== */ +/* ============================================= MCUCTRL CHIPREV REVMAJ [4..7] ============================================= */ +typedef enum { /*!< MCUCTRL_CHIPREV_REVMAJ */ + MCUCTRL_CHIPREV_REVMAJ_A = 1, /*!< A : Apollo3 revision A value. */ +} MCUCTRL_CHIPREV_REVMAJ_Enum; + +/* ============================================= MCUCTRL CHIPREV REVMIN [0..3] ============================================= */ +typedef enum { /*!< MCUCTRL_CHIPREV_REVMIN */ + MCUCTRL_CHIPREV_REVMIN_REV1 = 2, /*!< REV1 : Apollo3 minor rev 1. value. */ + MCUCTRL_CHIPREV_REVMIN_REV0 = 1, /*!< REV0 : Apollo3 minor rev 0. Minor revision value, succeeding + minor revisions will increment from this value. value. */ +} MCUCTRL_CHIPREV_REVMIN_Enum; + +/* ======================================================= VENDORID ======================================================== */ +/* =========================================== MCUCTRL VENDORID VENDORID [0..31] =========================================== */ +typedef enum { /*!< MCUCTRL_VENDORID_VENDORID */ + MCUCTRL_VENDORID_VENDORID_AMBIQ = 1095582289,/*!< AMBIQ : Ambiq Vendor ID value. */ +} MCUCTRL_VENDORID_VENDORID_Enum; + +/* ========================================================== SKU ========================================================== */ +/* ===================================================== FEATUREENABLE ===================================================== */ +/* ======================================== MCUCTRL FEATUREENABLE BURSTAVAIL [6..6] ======================================== */ +typedef enum { /*!< MCUCTRL_FEATUREENABLE_BURSTAVAIL */ + MCUCTRL_FEATUREENABLE_BURSTAVAIL_AVAIL = 1, /*!< AVAIL : Burst functionality available value. */ + MCUCTRL_FEATUREENABLE_BURSTAVAIL_NOTAVAIL = 0,/*!< NOTAVAIL : Burst functionality not available value. */ +} MCUCTRL_FEATUREENABLE_BURSTAVAIL_Enum; + +/* ========================================= MCUCTRL FEATUREENABLE BURSTREQ [4..4] ========================================= */ +typedef enum { /*!< MCUCTRL_FEATUREENABLE_BURSTREQ */ + MCUCTRL_FEATUREENABLE_BURSTREQ_EN = 1, /*!< EN : Enable the Burst functionality value. */ + MCUCTRL_FEATUREENABLE_BURSTREQ_DIS = 0, /*!< DIS : Disable the Burst functionality value. */ +} MCUCTRL_FEATUREENABLE_BURSTREQ_Enum; + +/* ========================================= MCUCTRL FEATUREENABLE BLEAVAIL [2..2] ========================================= */ +typedef enum { /*!< MCUCTRL_FEATUREENABLE_BLEAVAIL */ + MCUCTRL_FEATUREENABLE_BLEAVAIL_AVAIL = 1, /*!< AVAIL : BLE functionality available value. */ + MCUCTRL_FEATUREENABLE_BLEAVAIL_NOTAVAIL = 0, /*!< NOTAVAIL : BLE functionality not available value. */ +} MCUCTRL_FEATUREENABLE_BLEAVAIL_Enum; + +/* ========================================== MCUCTRL FEATUREENABLE BLEREQ [0..0] ========================================== */ +typedef enum { /*!< MCUCTRL_FEATUREENABLE_BLEREQ */ + MCUCTRL_FEATUREENABLE_BLEREQ_EN = 1, /*!< EN : Enable the BLE functionality value. */ + MCUCTRL_FEATUREENABLE_BLEREQ_DIS = 0, /*!< DIS : Disable the BLE functionality value. */ +} MCUCTRL_FEATUREENABLE_BLEREQ_Enum; + +/* ======================================================= DEBUGGER ======================================================== */ +/* ======================================================== BODCTRL ======================================================== */ +/* ======================================================= ADCPWRDLY ======================================================= */ +/* ======================================================== ADCCAL ========================================================= */ +/* ========================================== MCUCTRL ADCCAL ADCCALIBRATED [1..1] ========================================== */ +typedef enum { /*!< MCUCTRL_ADCCAL_ADCCALIBRATED */ + MCUCTRL_ADCCAL_ADCCALIBRATED_FALSE = 0, /*!< FALSE : ADC is not calibrated value. */ + MCUCTRL_ADCCAL_ADCCALIBRATED_TRUE = 1, /*!< TRUE : ADC is calibrated value. */ +} MCUCTRL_ADCCAL_ADCCALIBRATED_Enum; + +/* =========================================== MCUCTRL ADCCAL CALONPWRUP [0..0] ============================================ */ +typedef enum { /*!< MCUCTRL_ADCCAL_CALONPWRUP */ + MCUCTRL_ADCCAL_CALONPWRUP_DIS = 0, /*!< DIS : Disable automatic calibration on initial power up value. */ + MCUCTRL_ADCCAL_CALONPWRUP_EN = 1, /*!< EN : Enable automatic calibration on initial power up value. */ +} MCUCTRL_ADCCAL_CALONPWRUP_Enum; + +/* ====================================================== ADCBATTLOAD ====================================================== */ +/* ========================================== MCUCTRL ADCBATTLOAD BATTLOAD [0..0] ========================================== */ +typedef enum { /*!< MCUCTRL_ADCBATTLOAD_BATTLOAD */ + MCUCTRL_ADCBATTLOAD_BATTLOAD_DIS = 0, /*!< DIS : Battery load is disconnected value. */ + MCUCTRL_ADCBATTLOAD_BATTLOAD_EN = 1, /*!< EN : Battery load is enabled value. */ +} MCUCTRL_ADCBATTLOAD_BATTLOAD_Enum; + +/* ======================================================== ADCTRIM ======================================================== */ +/* ====================================================== ADCREFCOMP ======================================================= */ +/* ======================================================= XTALCTRL ======================================================== */ +/* ========================================== MCUCTRL XTALCTRL PWDBODXTAL [5..5] =========================================== */ +typedef enum { /*!< MCUCTRL_XTALCTRL_PWDBODXTAL */ + MCUCTRL_XTALCTRL_PWDBODXTAL_PWRUPBOD = 0, /*!< PWRUPBOD : Power up xtal on BOD value. */ + MCUCTRL_XTALCTRL_PWDBODXTAL_PWRDNBOD = 1, /*!< PWRDNBOD : Power down XTAL on BOD. value. */ +} MCUCTRL_XTALCTRL_PWDBODXTAL_Enum; + +/* ========================================= MCUCTRL XTALCTRL PDNBCMPRXTAL [4..4] ========================================== */ +typedef enum { /*!< MCUCTRL_XTALCTRL_PDNBCMPRXTAL */ + MCUCTRL_XTALCTRL_PDNBCMPRXTAL_PWRUPCOMP = 1, /*!< PWRUPCOMP : Power up XTAL oscillator comparator. value. */ + MCUCTRL_XTALCTRL_PDNBCMPRXTAL_PWRDNCOMP = 0, /*!< PWRDNCOMP : Power down XTAL oscillator comparator. value. */ +} MCUCTRL_XTALCTRL_PDNBCMPRXTAL_Enum; + +/* ========================================= MCUCTRL XTALCTRL PDNBCOREXTAL [3..3] ========================================== */ +typedef enum { /*!< MCUCTRL_XTALCTRL_PDNBCOREXTAL */ + MCUCTRL_XTALCTRL_PDNBCOREXTAL_PWRUPCORE = 1, /*!< PWRUPCORE : Power up XTAL oscillator core. value. */ + MCUCTRL_XTALCTRL_PDNBCOREXTAL_PWRDNCORE = 0, /*!< PWRDNCORE : Power down XTAL oscillator core. value. */ +} MCUCTRL_XTALCTRL_PDNBCOREXTAL_Enum; + +/* ========================================== MCUCTRL XTALCTRL BYPCMPRXTAL [2..2] ========================================== */ +typedef enum { /*!< MCUCTRL_XTALCTRL_BYPCMPRXTAL */ + MCUCTRL_XTALCTRL_BYPCMPRXTAL_USECOMP = 0, /*!< USECOMP : Use the XTAL oscillator comparator. value. */ + MCUCTRL_XTALCTRL_BYPCMPRXTAL_BYPCOMP = 1, /*!< BYPCOMP : Bypass the XTAL oscillator comparator. value. */ +} MCUCTRL_XTALCTRL_BYPCMPRXTAL_Enum; + +/* ========================================= MCUCTRL XTALCTRL FDBKDSBLXTAL [1..1] ========================================== */ +typedef enum { /*!< MCUCTRL_XTALCTRL_FDBKDSBLXTAL */ + MCUCTRL_XTALCTRL_FDBKDSBLXTAL_EN = 0, /*!< EN : Enable XTAL oscillator comparator. value. */ + MCUCTRL_XTALCTRL_FDBKDSBLXTAL_DIS = 1, /*!< DIS : Disable XTAL oscillator comparator. value. */ +} MCUCTRL_XTALCTRL_FDBKDSBLXTAL_Enum; + +/* ============================================ MCUCTRL XTALCTRL XTALSWE [0..0] ============================================ */ +typedef enum { /*!< MCUCTRL_XTALCTRL_XTALSWE */ + MCUCTRL_XTALCTRL_XTALSWE_OVERRIDE_DIS = 0, /*!< OVERRIDE_DIS : XTAL Software Override Disable. value. */ + MCUCTRL_XTALCTRL_XTALSWE_OVERRIDE_EN = 1, /*!< OVERRIDE_EN : XTAL Software Override Enable. value. */ +} MCUCTRL_XTALCTRL_XTALSWE_Enum; + +/* ====================================================== XTALGENCTRL ====================================================== */ +/* ========================================== MCUCTRL XTALGENCTRL ACWARMUP [0..1] ========================================== */ +typedef enum { /*!< MCUCTRL_XTALGENCTRL_ACWARMUP */ + MCUCTRL_XTALGENCTRL_ACWARMUP_SEC1 = 0, /*!< SEC1 : Warmup period of 1-2 seconds value. */ + MCUCTRL_XTALGENCTRL_ACWARMUP_SEC2 = 1, /*!< SEC2 : Warmup period of 2-4 seconds value. */ + MCUCTRL_XTALGENCTRL_ACWARMUP_SEC4 = 2, /*!< SEC4 : Warmup period of 4-8 seconds value. */ + MCUCTRL_XTALGENCTRL_ACWARMUP_SEC8 = 3, /*!< SEC8 : Warmup period of 8-16 seconds value. */ +} MCUCTRL_XTALGENCTRL_ACWARMUP_Enum; + +/* ======================================================= MISCCTRL ======================================================== */ +/* ====================================================== BOOTLOADER ======================================================= */ +/* ======================================= MCUCTRL BOOTLOADER SECBOOTONRST [30..31] ======================================== */ +typedef enum { /*!< MCUCTRL_BOOTLOADER_SECBOOTONRST */ + MCUCTRL_BOOTLOADER_SECBOOTONRST_DISABLED = 0, /*!< DISABLED : Secure boot disabled value. */ + MCUCTRL_BOOTLOADER_SECBOOTONRST_ENABLED = 1, /*!< ENABLED : Secure boot enabled value. */ + MCUCTRL_BOOTLOADER_SECBOOTONRST_ERROR = 2, /*!< ERROR : Error in secure boot configuration value. */ +} MCUCTRL_BOOTLOADER_SECBOOTONRST_Enum; + +/* ========================================== MCUCTRL BOOTLOADER SECBOOT [28..29] ========================================== */ +typedef enum { /*!< MCUCTRL_BOOTLOADER_SECBOOT */ + MCUCTRL_BOOTLOADER_SECBOOT_DISABLED = 0, /*!< DISABLED : Secure boot disabled value. */ + MCUCTRL_BOOTLOADER_SECBOOT_ENABLED = 1, /*!< ENABLED : Secure boot enabled value. */ + MCUCTRL_BOOTLOADER_SECBOOT_ERROR = 2, /*!< ERROR : Error in secure boot configuration value. */ +} MCUCTRL_BOOTLOADER_SECBOOT_Enum; + +/* ====================================== MCUCTRL BOOTLOADER SECBOOTFEATURE [26..27] ======================================= */ +typedef enum { /*!< MCUCTRL_BOOTLOADER_SECBOOTFEATURE */ + MCUCTRL_BOOTLOADER_SECBOOTFEATURE_DISABLED = 0,/*!< DISABLED : Secure boot disabled value. */ + MCUCTRL_BOOTLOADER_SECBOOTFEATURE_ENABLED = 1,/*!< ENABLED : Secure boot enabled value. */ + MCUCTRL_BOOTLOADER_SECBOOTFEATURE_ERROR = 2, /*!< ERROR : Error in secure boot configuration value. */ +} MCUCTRL_BOOTLOADER_SECBOOTFEATURE_Enum; + +/* ========================================== MCUCTRL BOOTLOADER PROTLOCK [2..2] =========================================== */ +typedef enum { /*!< MCUCTRL_BOOTLOADER_PROTLOCK */ + MCUCTRL_BOOTLOADER_PROTLOCK_LOCK = 1, /*!< LOCK : Enable the secure boot lock value. */ +} MCUCTRL_BOOTLOADER_PROTLOCK_Enum; + +/* =========================================== MCUCTRL BOOTLOADER SBLOCK [1..1] ============================================ */ +typedef enum { /*!< MCUCTRL_BOOTLOADER_SBLOCK */ + MCUCTRL_BOOTLOADER_SBLOCK_LOCK = 1, /*!< LOCK : Enable the secure boot lock value. */ +} MCUCTRL_BOOTLOADER_SBLOCK_Enum; + +/* ======================================== MCUCTRL BOOTLOADER BOOTLOADERLOW [0..0] ======================================== */ +typedef enum { /*!< MCUCTRL_BOOTLOADER_BOOTLOADERLOW */ + MCUCTRL_BOOTLOADER_BOOTLOADERLOW_ADDR0 = 1, /*!< ADDR0 : Bootloader code at 0x00000000. value. */ +} MCUCTRL_BOOTLOADER_BOOTLOADERLOW_Enum; + +/* ====================================================== SHADOWVALID ====================================================== */ +/* ======================================== MCUCTRL SHADOWVALID INFO0_VALID [2..2] ========================================= */ +typedef enum { /*!< MCUCTRL_SHADOWVALID_INFO0_VALID */ + MCUCTRL_SHADOWVALID_INFO0_VALID_VALID = 1, /*!< VALID : Flash info0 (customer) space contains valid data. value. */ +} MCUCTRL_SHADOWVALID_INFO0_VALID_Enum; + +/* ========================================== MCUCTRL SHADOWVALID BLDSLEEP [1..1] ========================================== */ +typedef enum { /*!< MCUCTRL_SHADOWVALID_BLDSLEEP */ + MCUCTRL_SHADOWVALID_BLDSLEEP_DEEPSLEEP = 1, /*!< DEEPSLEEP : Bootloader will go to deep sleep if no flash image + loaded value. */ +} MCUCTRL_SHADOWVALID_BLDSLEEP_Enum; + +/* =========================================== MCUCTRL SHADOWVALID VALID [0..0] ============================================ */ +typedef enum { /*!< MCUCTRL_SHADOWVALID_VALID */ + MCUCTRL_SHADOWVALID_VALID_VALID = 1, /*!< VALID : Flash information space contains valid data. value. */ +} MCUCTRL_SHADOWVALID_VALID_Enum; + +/* ======================================================= SCRATCH0 ======================================================== */ +/* ======================================================= SCRATCH1 ======================================================== */ +/* ==================================================== ICODEFAULTADDR ===================================================== */ +/* ==================================================== DCODEFAULTADDR ===================================================== */ +/* ===================================================== SYSFAULTADDR ====================================================== */ +/* ====================================================== FAULTSTATUS ====================================================== */ +/* ========================================== MCUCTRL FAULTSTATUS SYSFAULT [2..2] ========================================== */ +typedef enum { /*!< MCUCTRL_FAULTSTATUS_SYSFAULT */ + MCUCTRL_FAULTSTATUS_SYSFAULT_NOFAULT = 0, /*!< NOFAULT : No bus fault has been detected. value. */ + MCUCTRL_FAULTSTATUS_SYSFAULT_FAULT = 1, /*!< FAULT : Bus fault detected. value. */ +} MCUCTRL_FAULTSTATUS_SYSFAULT_Enum; + +/* ========================================= MCUCTRL FAULTSTATUS DCODEFAULT [1..1] ========================================= */ +typedef enum { /*!< MCUCTRL_FAULTSTATUS_DCODEFAULT */ + MCUCTRL_FAULTSTATUS_DCODEFAULT_NOFAULT = 0, /*!< NOFAULT : No DCODE fault has been detected. value. */ + MCUCTRL_FAULTSTATUS_DCODEFAULT_FAULT = 1, /*!< FAULT : DCODE fault detected. value. */ +} MCUCTRL_FAULTSTATUS_DCODEFAULT_Enum; + +/* ========================================= MCUCTRL FAULTSTATUS ICODEFAULT [0..0] ========================================= */ +typedef enum { /*!< MCUCTRL_FAULTSTATUS_ICODEFAULT */ + MCUCTRL_FAULTSTATUS_ICODEFAULT_NOFAULT = 0, /*!< NOFAULT : No ICODE fault has been detected. value. */ + MCUCTRL_FAULTSTATUS_ICODEFAULT_FAULT = 1, /*!< FAULT : ICODE fault detected. value. */ +} MCUCTRL_FAULTSTATUS_ICODEFAULT_Enum; + +/* ==================================================== FAULTCAPTUREEN ===================================================== */ +/* ===================================== MCUCTRL FAULTCAPTUREEN FAULTCAPTUREEN [0..0] ====================================== */ +typedef enum { /*!< MCUCTRL_FAULTCAPTUREEN_FAULTCAPTUREEN */ + MCUCTRL_FAULTCAPTUREEN_FAULTCAPTUREEN_DIS = 0,/*!< DIS : Disable fault capture. value. */ + MCUCTRL_FAULTCAPTUREEN_FAULTCAPTUREEN_EN = 1, /*!< EN : Enable fault capture. value. */ +} MCUCTRL_FAULTCAPTUREEN_FAULTCAPTUREEN_Enum; + +/* ========================================================= DBGR1 ========================================================= */ +/* ========================================================= DBGR2 ========================================================= */ +/* ======================================================= PMUENABLE ======================================================= */ +/* ============================================ MCUCTRL PMUENABLE ENABLE [0..0] ============================================ */ +typedef enum { /*!< MCUCTRL_PMUENABLE_ENABLE */ + MCUCTRL_PMUENABLE_ENABLE_DIS = 0, /*!< DIS : Disable MCU power management. value. */ + MCUCTRL_PMUENABLE_ENABLE_EN = 1, /*!< EN : Enable MCU power management. value. */ +} MCUCTRL_PMUENABLE_ENABLE_Enum; + +/* ======================================================= TPIUCTRL ======================================================== */ +/* ============================================ MCUCTRL TPIUCTRL CLKSEL [8..10] ============================================ */ +typedef enum { /*!< MCUCTRL_TPIUCTRL_CLKSEL */ + MCUCTRL_TPIUCTRL_CLKSEL_LOWPWR = 0, /*!< LOWPWR : Low power state. value. */ + MCUCTRL_TPIUCTRL_CLKSEL_HFRCDIV2 = 1, /*!< HFRCDIV2 : Selects HFRC divided by 2 as the source TPIU clk + value. */ + MCUCTRL_TPIUCTRL_CLKSEL_HFRCDIV8 = 2, /*!< HFRCDIV8 : Selects HFRC divided by 8 as the source TPIU clk + value. */ + MCUCTRL_TPIUCTRL_CLKSEL_HFRCDIV16 = 3, /*!< HFRCDIV16 : Selects HFRC divided by 16 as the source TPIU clk + value. */ + MCUCTRL_TPIUCTRL_CLKSEL_HFRCDIV32 = 4, /*!< HFRCDIV32 : Selects HFRC divided by 32 as the source TPIU clk + value. */ +} MCUCTRL_TPIUCTRL_CLKSEL_Enum; + +/* ============================================ MCUCTRL TPIUCTRL ENABLE [0..0] ============================================= */ +typedef enum { /*!< MCUCTRL_TPIUCTRL_ENABLE */ + MCUCTRL_TPIUCTRL_ENABLE_DIS = 0, /*!< DIS : Disable the TPIU. value. */ + MCUCTRL_TPIUCTRL_ENABLE_EN = 1, /*!< EN : Enable the TPIU. value. */ +} MCUCTRL_TPIUCTRL_ENABLE_Enum; + +/* ====================================================== OTAPOINTER ======================================================= */ +/* ====================================================== APBDMACTRL ======================================================= */ +/* ========================================= MCUCTRL APBDMACTRL DECODEABORT [1..1] ========================================= */ +typedef enum { /*!< MCUCTRL_APBDMACTRL_DECODEABORT */ + MCUCTRL_APBDMACTRL_DECODEABORT_DISABLE = 0, /*!< DISABLE : Bus operations to powered down peripherals are quietly + discarded value. */ + MCUCTRL_APBDMACTRL_DECODEABORT_ENABLE = 1, /*!< ENABLE : Bus operations to powered down peripherals result in + a bus fault. value. */ +} MCUCTRL_APBDMACTRL_DECODEABORT_Enum; + +/* ========================================= MCUCTRL APBDMACTRL DMA_ENABLE [0..0] ========================================== */ +typedef enum { /*!< MCUCTRL_APBDMACTRL_DMA_ENABLE */ + MCUCTRL_APBDMACTRL_DMA_ENABLE_DISABLE = 0, /*!< DISABLE : DMA operations disabled value. */ + MCUCTRL_APBDMACTRL_DMA_ENABLE_ENABLE = 1, /*!< ENABLE : DMA operations enabled value. */ +} MCUCTRL_APBDMACTRL_DMA_ENABLE_Enum; + +/* ======================================================= SRAMMODE ======================================================== */ +/* ====================================================== KEXTCLKSEL ======================================================= */ +/* ========================================= MCUCTRL KEXTCLKSEL KEXTCLKSEL [0..31] ========================================= */ +typedef enum { /*!< MCUCTRL_KEXTCLKSEL_KEXTCLKSEL */ + MCUCTRL_KEXTCLKSEL_KEXTCLKSEL_Key = 83, /*!< Key : Key value. */ +} MCUCTRL_KEXTCLKSEL_KEXTCLKSEL_Enum; + +/* ======================================================= SIMOBUCK4 ======================================================= */ +/* ======================================================= BLEBUCK2 ======================================================== */ +/* ====================================================== FLASHWPROT0 ====================================================== */ +/* ====================================================== FLASHWPROT1 ====================================================== */ +/* ====================================================== FLASHRPROT0 ====================================================== */ +/* ====================================================== FLASHRPROT1 ====================================================== */ +/* ================================================= DMASRAMWRITEPROTECT0 ================================================== */ +/* ================================================= DMASRAMWRITEPROTECT1 ================================================== */ +/* ================================================== DMASRAMREADPROTECT0 ================================================== */ +/* ================================================== DMASRAMREADPROTECT1 ================================================== */ + + +/* =========================================================================================================================== */ +/* ================ MSPI ================ */ +/* =========================================================================================================================== */ + +/* ========================================================= CTRL ========================================================== */ +/* ========================================================== CFG ========================================================== */ +/* ================================================ MSPI CFG CPOL [17..17] ================================================= */ +typedef enum { /*!< MSPI_CFG_CPOL */ + MSPI_CFG_CPOL_LOW = 0, /*!< LOW : Clock inactive state is low. value. */ + MSPI_CFG_CPOL_HIGH = 1, /*!< HIGH : Clock inactive state is high. value. */ +} MSPI_CFG_CPOL_Enum; + +/* ================================================ MSPI CFG CPHA [16..16] ================================================= */ +typedef enum { /*!< MSPI_CFG_CPHA */ + MSPI_CFG_CPHA_MIDDLE = 0, /*!< MIDDLE : Clock toggles in middle of data bit. value. */ + MSPI_CFG_CPHA_START = 1, /*!< START : Clock toggles at start of data bit. value. */ +} MSPI_CFG_CPHA_Enum; + +/* ================================================ MSPI CFG DEVCFG [0..3] ================================================= */ +typedef enum { /*!< MSPI_CFG_DEVCFG */ + MSPI_CFG_DEVCFG_SERIAL0 = 1, /*!< SERIAL0 : Single bit SPI flash on chip select 0 value. */ + MSPI_CFG_DEVCFG_SERIAL1 = 2, /*!< SERIAL1 : Single bit SPI flash on chip select 1 value. */ + MSPI_CFG_DEVCFG_DUAL0 = 5, /*!< DUAL0 : Dual SPI flash on chip select 0 value. */ + MSPI_CFG_DEVCFG_DUAL1 = 6, /*!< DUAL1 : Dual bit SPI flash on chip select 1 value. */ + MSPI_CFG_DEVCFG_QUAD0 = 9, /*!< QUAD0 : Quad SPI flash on chip select 0 value. */ + MSPI_CFG_DEVCFG_QUAD1 = 10, /*!< QUAD1 : Quad SPI flash on chip select 1 value. */ + MSPI_CFG_DEVCFG_OCTAL0 = 13, /*!< OCTAL0 : Octal SPI flash on chip select 0 value. */ + MSPI_CFG_DEVCFG_OCTAL1 = 14, /*!< OCTAL1 : Octal SPI flash on chip select 1 value. */ + MSPI_CFG_DEVCFG_QUADPAIRED = 15, /*!< QUADPAIRED : Dual Quad SPI flash on chip selects 0/1. value. */ + MSPI_CFG_DEVCFG_QUADPAIRED_SERIAL = 3, /*!< QUADPAIRED_SERIAL : Dual Quad SPI flash on chip selects 0/1, + but transmit in serial mode for initialization operations + value. */ +} MSPI_CFG_DEVCFG_Enum; + +/* ========================================================= ADDR ========================================================== */ +/* ========================================================= INSTR ========================================================= */ +/* ======================================================== TXFIFO ========================================================= */ +/* ======================================================== RXFIFO ========================================================= */ +/* ======================================================= TXENTRIES ======================================================= */ +/* ======================================================= RXENTRIES ======================================================= */ +/* ======================================================= THRESHOLD ======================================================= */ +/* ======================================================== MSPICFG ======================================================== */ +/* ============================================== MSPI MSPICFG CLKDIV [8..13] ============================================== */ +typedef enum { /*!< MSPI_MSPICFG_CLKDIV */ + MSPI_MSPICFG_CLKDIV_CLK24 = 2, /*!< CLK24 : 24 MHz MSPI clock value. */ + MSPI_MSPICFG_CLKDIV_CLK12 = 4, /*!< CLK12 : 12 MHz MSPI clock value. */ + MSPI_MSPICFG_CLKDIV_CLK6 = 8, /*!< CLK6 : 6 MHz MSPI clock value. */ + MSPI_MSPICFG_CLKDIV_CLK3 = 16, /*!< CLK3 : 3 MHz MSPI clock value. */ + MSPI_MSPICFG_CLKDIV_CLK1_5 = 32, /*!< CLK1_5 : 1.5 MHz MSPI clock value. */ +} MSPI_MSPICFG_CLKDIV_Enum; + +/* ============================================== MSPI MSPICFG IOMSEL [4..6] =============================================== */ +typedef enum { /*!< MSPI_MSPICFG_IOMSEL */ + MSPI_MSPICFG_IOMSEL_IOM0 = 0, /*!< IOM0 : ERROR: desc VALUE MISSING value. */ + MSPI_MSPICFG_IOMSEL_IOM1 = 1, /*!< IOM1 : ERROR: desc VALUE MISSING value. */ + MSPI_MSPICFG_IOMSEL_IOM2 = 2, /*!< IOM2 : ERROR: desc VALUE MISSING value. */ + MSPI_MSPICFG_IOMSEL_IOM3 = 3, /*!< IOM3 : ERROR: desc VALUE MISSING value. */ + MSPI_MSPICFG_IOMSEL_IOM4 = 4, /*!< IOM4 : ERROR: desc VALUE MISSING value. */ + MSPI_MSPICFG_IOMSEL_IOM5 = 5, /*!< IOM5 : ERROR: desc VALUE MISSING value. */ + MSPI_MSPICFG_IOMSEL_DISABLED = 7, /*!< DISABLED : No IOM selected. Signals always zero. value. */ +} MSPI_MSPICFG_IOMSEL_Enum; + +/* =============================================== MSPI MSPICFG TXNEG [3..3] =============================================== */ +typedef enum { /*!< MSPI_MSPICFG_TXNEG */ + MSPI_MSPICFG_TXNEG_NORMAL = 0, /*!< NORMAL : TX launched from posedge internal clock value. */ + MSPI_MSPICFG_TXNEG_NEGEDGE = 1, /*!< NEGEDGE : TX data launched from negedge of internal clock value. */ +} MSPI_MSPICFG_TXNEG_Enum; + +/* =============================================== MSPI MSPICFG RXNEG [2..2] =============================================== */ +typedef enum { /*!< MSPI_MSPICFG_RXNEG */ + MSPI_MSPICFG_RXNEG_NORMAL = 0, /*!< NORMAL : RX data sampled on posedge of internal clock value. */ + MSPI_MSPICFG_RXNEG_NEGEDGE = 1, /*!< NEGEDGE : RX data sampled on negedge of internal clock value. */ +} MSPI_MSPICFG_RXNEG_Enum; + +/* =============================================== MSPI MSPICFG RXCAP [1..1] =============================================== */ +typedef enum { /*!< MSPI_MSPICFG_RXCAP */ + MSPI_MSPICFG_RXCAP_NORMAL = 0, /*!< NORMAL : RX Capture phase aligns with CPHA setting value. */ + MSPI_MSPICFG_RXCAP_DELAY = 1, /*!< DELAY : RX Capture phase is delayed from CPHA setting by one + clock edge value. */ +} MSPI_MSPICFG_RXCAP_Enum; + +/* ============================================== MSPI MSPICFG APBCLK [0..0] =============================================== */ +typedef enum { /*!< MSPI_MSPICFG_APBCLK */ + MSPI_MSPICFG_APBCLK_DIS = 0, /*!< DIS : Disable continuous clock. value. */ + MSPI_MSPICFG_APBCLK_EN = 1, /*!< EN : Enable continuous clock. value. */ +} MSPI_MSPICFG_APBCLK_Enum; + +/* ======================================================== PADCFG ========================================================= */ +/* ======================================================= PADOUTEN ======================================================== */ +/* ============================================== MSPI PADOUTEN OUTEN [0..8] =============================================== */ +typedef enum { /*!< MSPI_PADOUTEN_OUTEN */ + MSPI_PADOUTEN_OUTEN_QUAD0 = 271, /*!< QUAD0 : Quad0 (4 data + 1 clock) value. */ + MSPI_PADOUTEN_OUTEN_QUAD1 = 496, /*!< QUAD1 : Quad1 (4 data + 1 clock) value. */ + MSPI_PADOUTEN_OUTEN_OCTAL = 511, /*!< OCTAL : Octal (8 data + 1 clock) value. */ + MSPI_PADOUTEN_OUTEN_SERIAL0 = 259, /*!< SERIAL0 : Serial (2 data + 1 clock) value. */ +} MSPI_PADOUTEN_OUTEN_Enum; + +/* ========================================================= FLASH ========================================================= */ +/* =============================================== MSPI FLASH XIPACK [2..3] ================================================ */ +typedef enum { /*!< MSPI_FLASH_XIPACK */ + MSPI_FLASH_XIPACK_NOACK = 0, /*!< NOACK : No acknowledege sent. Data IOs are tristated the first + turnaround cycle value. */ + MSPI_FLASH_XIPACK_ACK = 2, /*!< ACK : Positive acknowledege sent. Data IOs are driven to 0 the + first turnaround cycle to acknowledge XIP mode value. */ + MSPI_FLASH_XIPACK_TERMINATE = 3, /*!< TERMINATE : Negative acknowledege sent. Data IOs are driven + to 1 the first turnaround cycle to terminate XIP mode. + XIPSENDI should be reenabled for the next transfer value. */ +} MSPI_FLASH_XIPACK_Enum; + +/* ====================================================== SCRAMBLING ======================================================= */ +/* ========================================================= INTEN ========================================================= */ +/* ======================================================== INTSTAT ======================================================== */ +/* ======================================================== INTCLR ========================================================= */ +/* ======================================================== INTSET ========================================================= */ +/* ======================================================== DMACFG ========================================================= */ +/* =============================================== MSPI DMACFG DMAPRI [3..4] =============================================== */ +typedef enum { /*!< MSPI_DMACFG_DMAPRI */ + MSPI_DMACFG_DMAPRI_LOW = 0, /*!< LOW : Low Priority (service as best effort) value. */ + MSPI_DMACFG_DMAPRI_HIGH = 1, /*!< HIGH : High Priority (service immediately) value. */ + MSPI_DMACFG_DMAPRI_AUTO = 2, /*!< AUTO : Auto Priority (priority raised once TX FIFO empties or + RX FIFO fills) value. */ +} MSPI_DMACFG_DMAPRI_Enum; + +/* =============================================== MSPI DMACFG DMADIR [2..2] =============================================== */ +typedef enum { /*!< MSPI_DMACFG_DMADIR */ + MSPI_DMACFG_DMADIR_P2M = 0, /*!< P2M : Peripheral to Memory (SRAM) transaction value. */ + MSPI_DMACFG_DMADIR_M2P = 1, /*!< M2P : Memory to Peripheral transaction value. */ +} MSPI_DMACFG_DMADIR_Enum; + +/* =============================================== MSPI DMACFG DMAEN [0..1] ================================================ */ +typedef enum { /*!< MSPI_DMACFG_DMAEN */ + MSPI_DMACFG_DMAEN_DIS = 0, /*!< DIS : Disable DMA Function value. */ + MSPI_DMACFG_DMAEN_EN = 3, /*!< EN : Enable HW controlled DMA Function to manage DMA to flash + devices. HW will automatically handle issuance of instruction/address + bytes based on settings in the FLASH register. value. */ +} MSPI_DMACFG_DMAEN_Enum; + +/* ======================================================== DMASTAT ======================================================== */ +/* ====================================================== DMATARGADDR ====================================================== */ +/* ====================================================== DMADEVADDR ======================================================= */ +/* ====================================================== DMATOTCOUNT ====================================================== */ +/* ======================================================= DMABCOUNT ======================================================= */ +/* ======================================================= DMATHRESH ======================================================= */ +/* ========================================================= CQCFG ========================================================= */ +/* ================================================ MSPI CQCFG CQPRI [1..1] ================================================ */ +typedef enum { /*!< MSPI_CQCFG_CQPRI */ + MSPI_CQCFG_CQPRI_LOW = 0, /*!< LOW : Low Priority (service as best effort) value. */ + MSPI_CQCFG_CQPRI_HIGH = 1, /*!< HIGH : High Priority (service immediately) value. */ +} MSPI_CQCFG_CQPRI_Enum; + +/* ================================================ MSPI CQCFG CQEN [0..0] ================================================= */ +typedef enum { /*!< MSPI_CQCFG_CQEN */ + MSPI_CQCFG_CQEN_DIS = 0, /*!< DIS : Disable CQ Function value. */ + MSPI_CQCFG_CQEN_EN = 1, /*!< EN : Enable CQ Function value. */ +} MSPI_CQCFG_CQEN_Enum; + +/* ======================================================== CQADDR ========================================================= */ +/* ======================================================== CQSTAT ========================================================= */ +/* ======================================================== CQFLAGS ======================================================== */ +/* ============================================= MSPI CQFLAGS CQFLAGS [0..15] ============================================== */ +typedef enum { /*!< MSPI_CQFLAGS_CQFLAGS */ + MSPI_CQFLAGS_CQFLAGS_STOP = 32768, /*!< STOP : CQ Stop Flag. When set, CQ processing will complete. + value. */ + MSPI_CQFLAGS_CQFLAGS_CQIDX = 16384, /*!< CQIDX : CQ Index Pointers (CURIDX/ENDIDX) match. value. */ + MSPI_CQFLAGS_CQFLAGS_DMACPL = 2048, /*!< DMACPL : DMA Complete Status (hardwired DMACPL bit in DMASTAT) + value. */ + MSPI_CQFLAGS_CQFLAGS_CMDCPL = 1024, /*!< CMDCPL : PIO Operation completed (STATUS bit in CTRL register) + value. */ + MSPI_CQFLAGS_CQFLAGS_IOM1READY = 512, /*!< IOM1READY : IOM Buffer 1 Ready Status (from selected IOM). This + status is the result of XOR'ing the IOM0START with the + incoming status from the IOM. When high, MSPI can send + to the buffer. value. */ + MSPI_CQFLAGS_CQFLAGS_IOM0READY = 256, /*!< IOM0READY : IOM Buffer 0 Ready Status (from selected IOM). This + status is the result of XOR'ing the IOM0START with the + incoming status from the IOM. When high, MSPI can send + to the buffer. value. */ + MSPI_CQFLAGS_CQFLAGS_SWFLAG7 = 128, /*!< SWFLAG7 : Software flag 7. Can be used by software to start/pause + operations value. */ + MSPI_CQFLAGS_CQFLAGS_SWFLAG6 = 64, /*!< SWFLAG6 : Software flag 6. Can be used by software to start/pause + operatoins value. */ + MSPI_CQFLAGS_CQFLAGS_SWFLAG5 = 32, /*!< SWFLAG5 : Software flag 5. Can be used by software to start/pause + operations value. */ + MSPI_CQFLAGS_CQFLAGS_SWFLAG4 = 16, /*!< SWFLAG4 : Software flag 4. Can be used by software to start/pause + operatoins value. */ + MSPI_CQFLAGS_CQFLAGS_SWFLAG3 = 8, /*!< SWFLAG3 : Software flag 3. Can be used by software to start/pause + operations value. */ + MSPI_CQFLAGS_CQFLAGS_SWFLAG2 = 4, /*!< SWFLAG2 : Software flag 2. Can be used by software to start/pause + operatoins value. */ + MSPI_CQFLAGS_CQFLAGS_SWFLAG1 = 2, /*!< SWFLAG1 : Software flag 1. Can be used by software to start/pause + operations value. */ + MSPI_CQFLAGS_CQFLAGS_SWFLAG0 = 1, /*!< SWFLAG0 : Software flag 0. Can be used by software to start/pause + operatoins value. */ +} MSPI_CQFLAGS_CQFLAGS_Enum; + +/* ====================================================== CQSETCLEAR ======================================================= */ +/* ======================================================== CQPAUSE ======================================================== */ +/* ============================================== MSPI CQPAUSE CQMASK [0..15] ============================================== */ +typedef enum { /*!< MSPI_CQPAUSE_CQMASK */ + MSPI_CQPAUSE_CQMASK_STOP = 32768, /*!< STOP : CQ Stop Flag. When set, CQ processing will complete. + value. */ + MSPI_CQPAUSE_CQMASK_CQIDX = 16384, /*!< CQIDX : CQ Index Pointers (CURIDX/ENDIDX) match. value. */ + MSPI_CQPAUSE_CQMASK_DMACPL = 2048, /*!< DMACPL : DMA Complete Status (hardwired DMACPL bit in DMASTAT) + value. */ + MSPI_CQPAUSE_CQMASK_CMDCPL = 1024, /*!< CMDCPL : PIO Operation completed (STATUS bit in CTRL register) + value. */ + MSPI_CQPAUSE_CQMASK_IOM1READY = 512, /*!< IOM1READY : IOM Buffer 1 Ready Status (from selected IOM). This + status is the result of XOR'ing the IOM0START with the + incoming status from the IOM. When high, MSPI can send + to the buffer. value. */ + MSPI_CQPAUSE_CQMASK_IOM0READY = 256, /*!< IOM0READY : IOM Buffer 0 Ready Status (from selected IOM). This + status is the result of XOR'ing the IOM0START with the + incoming status from the IOM. When high, MSPI can send + to the buffer. value. */ + MSPI_CQPAUSE_CQMASK_SWFLAG7 = 128, /*!< SWFLAG7 : Software flag 7. Can be used by software to start/pause + operations value. */ + MSPI_CQPAUSE_CQMASK_SWFLAG6 = 64, /*!< SWFLAG6 : Software flag 6. Can be used by software to start/pause + operatoins value. */ + MSPI_CQPAUSE_CQMASK_SWFLAG5 = 32, /*!< SWFLAG5 : Software flag 5. Can be used by software to start/pause + operations value. */ + MSPI_CQPAUSE_CQMASK_SWFLAG4 = 16, /*!< SWFLAG4 : Software flag 4. Can be used by software to start/pause + operatoins value. */ + MSPI_CQPAUSE_CQMASK_SWFLAG3 = 8, /*!< SWFLAG3 : Software flag 3. Can be used by software to start/pause + operations value. */ + MSPI_CQPAUSE_CQMASK_SWFLAG2 = 4, /*!< SWFLAG2 : Software flag 2. Can be used by software to start/pause + operatoins value. */ + MSPI_CQPAUSE_CQMASK_SWFLAG1 = 2, /*!< SWFLAG1 : Software flag 1. Can be used by software to start/pause + operations value. */ + MSPI_CQPAUSE_CQMASK_SWFLAG0 = 1, /*!< SWFLAG0 : Software flag 0. Can be used by software to start/pause + operatoins value. */ +} MSPI_CQPAUSE_CQMASK_Enum; + +/* ======================================================= CQCURIDX ======================================================== */ +/* ======================================================= CQENDIDX ======================================================== */ + + +/* =========================================================================================================================== */ +/* ================ PDM ================ */ +/* =========================================================================================================================== */ + +/* ========================================================= PCFG ========================================================== */ +/* =============================================== PDM PCFG LRSWAP [31..31] ================================================ */ +typedef enum { /*!< PDM_PCFG_LRSWAP */ + PDM_PCFG_LRSWAP_EN = 1, /*!< EN : Swap left and right channels (FIFO Read RIGHT_LEFT). value. */ + PDM_PCFG_LRSWAP_NOSWAP = 0, /*!< NOSWAP : No channel swapping (IFO Read LEFT_RIGHT). value. */ +} PDM_PCFG_LRSWAP_Enum; + +/* ============================================== PDM PCFG PGARIGHT [26..30] =============================================== */ +typedef enum { /*!< PDM_PCFG_PGARIGHT */ + PDM_PCFG_PGARIGHT_P405DB = 31, /*!< P405DB : 40.5 db gain. value. */ + PDM_PCFG_PGARIGHT_P390DB = 30, /*!< P390DB : 39.0 db gain. value. */ + PDM_PCFG_PGARIGHT_P375DB = 29, /*!< P375DB : 37.5 db gain. value. */ + PDM_PCFG_PGARIGHT_P360DB = 28, /*!< P360DB : 36.0 db gain. value. */ + PDM_PCFG_PGARIGHT_P345DB = 27, /*!< P345DB : 34.5 db gain. value. */ + PDM_PCFG_PGARIGHT_P330DB = 26, /*!< P330DB : 33.0 db gain. value. */ + PDM_PCFG_PGARIGHT_P315DB = 25, /*!< P315DB : 31.5 db gain. value. */ + PDM_PCFG_PGARIGHT_P300DB = 24, /*!< P300DB : 30.0 db gain. value. */ + PDM_PCFG_PGARIGHT_P285DB = 23, /*!< P285DB : 28.5 db gain. value. */ + PDM_PCFG_PGARIGHT_P270DB = 22, /*!< P270DB : 27.0 db gain. value. */ + PDM_PCFG_PGARIGHT_P255DB = 21, /*!< P255DB : 25.5 db gain. value. */ + PDM_PCFG_PGARIGHT_P240DB = 20, /*!< P240DB : 24.0 db gain. value. */ + PDM_PCFG_PGARIGHT_P225DB = 19, /*!< P225DB : 22.5 db gain. value. */ + PDM_PCFG_PGARIGHT_P210DB = 18, /*!< P210DB : 21.0 db gain. value. */ + PDM_PCFG_PGARIGHT_P195DB = 17, /*!< P195DB : 19.5 db gain. value. */ + PDM_PCFG_PGARIGHT_P180DB = 16, /*!< P180DB : 18.0 db gain. value. */ + PDM_PCFG_PGARIGHT_P165DB = 15, /*!< P165DB : 16.5 db gain. value. */ + PDM_PCFG_PGARIGHT_P150DB = 14, /*!< P150DB : 15.0 db gain. value. */ + PDM_PCFG_PGARIGHT_P135DB = 13, /*!< P135DB : 13.5 db gain. value. */ + PDM_PCFG_PGARIGHT_P120DB = 12, /*!< P120DB : 12.0 db gain. value. */ + PDM_PCFG_PGARIGHT_P105DB = 11, /*!< P105DB : 10.5 db gain. value. */ + PDM_PCFG_PGARIGHT_P90DB = 10, /*!< P90DB : 9.0 db gain. value. */ + PDM_PCFG_PGARIGHT_P75DB = 9, /*!< P75DB : 7.5 db gain. value. */ + PDM_PCFG_PGARIGHT_P60DB = 8, /*!< P60DB : 6.0 db gain. value. */ + PDM_PCFG_PGARIGHT_P45DB = 7, /*!< P45DB : 4.5 db gain. value. */ + PDM_PCFG_PGARIGHT_P30DB = 6, /*!< P30DB : 3.0 db gain. value. */ + PDM_PCFG_PGARIGHT_P15DB = 5, /*!< P15DB : 1.5 db gain. value. */ + PDM_PCFG_PGARIGHT_0DB = 4, /*!< 0DB : 0.0 db gain. value. */ + PDM_PCFG_PGARIGHT_M15DB = 3, /*!< M15DB : -1.5 db gain. value. */ + PDM_PCFG_PGARIGHT_M300DB = 2, /*!< M300DB : -3.0 db gain. value. */ + PDM_PCFG_PGARIGHT_M45DB = 1, /*!< M45DB : -4.5 db gain. value. */ + PDM_PCFG_PGARIGHT_M60DB = 0, /*!< M60DB : -6.0 db gain. value. */ +} PDM_PCFG_PGARIGHT_Enum; + +/* =============================================== PDM PCFG PGALEFT [21..25] =============================================== */ +typedef enum { /*!< PDM_PCFG_PGALEFT */ + PDM_PCFG_PGALEFT_P405DB = 31, /*!< P405DB : 40.5 db gain. value. */ + PDM_PCFG_PGALEFT_P390DB = 30, /*!< P390DB : 39.0 db gain. value. */ + PDM_PCFG_PGALEFT_P375DB = 29, /*!< P375DB : 37.5 db gain. value. */ + PDM_PCFG_PGALEFT_P360DB = 28, /*!< P360DB : 36.0 db gain. value. */ + PDM_PCFG_PGALEFT_P345DB = 27, /*!< P345DB : 34.5 db gain. value. */ + PDM_PCFG_PGALEFT_P330DB = 26, /*!< P330DB : 33.0 db gain. value. */ + PDM_PCFG_PGALEFT_P315DB = 25, /*!< P315DB : 31.5 db gain. value. */ + PDM_PCFG_PGALEFT_P300DB = 24, /*!< P300DB : 30.0 db gain. value. */ + PDM_PCFG_PGALEFT_P285DB = 23, /*!< P285DB : 28.5 db gain. value. */ + PDM_PCFG_PGALEFT_P270DB = 22, /*!< P270DB : 27.0 db gain. value. */ + PDM_PCFG_PGALEFT_P255DB = 21, /*!< P255DB : 25.5 db gain. value. */ + PDM_PCFG_PGALEFT_P240DB = 20, /*!< P240DB : 24.0 db gain. value. */ + PDM_PCFG_PGALEFT_P225DB = 19, /*!< P225DB : 22.5 db gain. value. */ + PDM_PCFG_PGALEFT_P210DB = 18, /*!< P210DB : 21.0 db gain. value. */ + PDM_PCFG_PGALEFT_P195DB = 17, /*!< P195DB : 19.5 db gain. value. */ + PDM_PCFG_PGALEFT_P180DB = 16, /*!< P180DB : 18.0 db gain. value. */ + PDM_PCFG_PGALEFT_P165DB = 15, /*!< P165DB : 16.5 db gain. value. */ + PDM_PCFG_PGALEFT_P150DB = 14, /*!< P150DB : 15.0 db gain. value. */ + PDM_PCFG_PGALEFT_P135DB = 13, /*!< P135DB : 13.5 db gain. value. */ + PDM_PCFG_PGALEFT_P120DB = 12, /*!< P120DB : 12.0 db gain. value. */ + PDM_PCFG_PGALEFT_P105DB = 11, /*!< P105DB : 10.5 db gain. value. */ + PDM_PCFG_PGALEFT_P90DB = 10, /*!< P90DB : 9.0 db gain. value. */ + PDM_PCFG_PGALEFT_P75DB = 9, /*!< P75DB : 7.5 db gain. value. */ + PDM_PCFG_PGALEFT_P60DB = 8, /*!< P60DB : 6.0 db gain. value. */ + PDM_PCFG_PGALEFT_P45DB = 7, /*!< P45DB : 4.5 db gain. value. */ + PDM_PCFG_PGALEFT_P30DB = 6, /*!< P30DB : 3.0 db gain. value. */ + PDM_PCFG_PGALEFT_P15DB = 5, /*!< P15DB : 1.5 db gain. value. */ + PDM_PCFG_PGALEFT_0DB = 4, /*!< 0DB : 0.0 db gain. value. */ + PDM_PCFG_PGALEFT_M15DB = 3, /*!< M15DB : -1.5 db gain. value. */ + PDM_PCFG_PGALEFT_M300DB = 2, /*!< M300DB : -3.0 db gain. value. */ + PDM_PCFG_PGALEFT_M45DB = 1, /*!< M45DB : -4.5 db gain. value. */ + PDM_PCFG_PGALEFT_M60DB = 0, /*!< M60DB : -6.0 db gain. value. */ +} PDM_PCFG_PGALEFT_Enum; + +/* =============================================== PDM PCFG MCLKDIV [17..18] =============================================== */ +typedef enum { /*!< PDM_PCFG_MCLKDIV */ + PDM_PCFG_MCLKDIV_MCKDIV4 = 3, /*!< MCKDIV4 : Divide input clock by 4 value. */ + PDM_PCFG_MCLKDIV_MCKDIV3 = 2, /*!< MCKDIV3 : Divide input clock by 3 value. */ + PDM_PCFG_MCLKDIV_MCKDIV2 = 1, /*!< MCKDIV2 : Divide input clock by 2 value. */ + PDM_PCFG_MCLKDIV_MCKDIV1 = 0, /*!< MCKDIV1 : Divide input clock by 1 value. */ +} PDM_PCFG_MCLKDIV_Enum; + +/* ================================================ PDM PCFG ADCHPD [9..9] ================================================= */ +typedef enum { /*!< PDM_PCFG_ADCHPD */ + PDM_PCFG_ADCHPD_EN = 1, /*!< EN : Enable high pass filter. value. */ + PDM_PCFG_ADCHPD_DIS = 0, /*!< DIS : Disable high pass filter. value. */ +} PDM_PCFG_ADCHPD_Enum; + +/* =============================================== PDM PCFG SOFTMUTE [1..1] ================================================ */ +typedef enum { /*!< PDM_PCFG_SOFTMUTE */ + PDM_PCFG_SOFTMUTE_EN = 1, /*!< EN : Enable Soft Mute. value. */ + PDM_PCFG_SOFTMUTE_DIS = 0, /*!< DIS : Disable Soft Mute. value. */ +} PDM_PCFG_SOFTMUTE_Enum; + +/* =============================================== PDM PCFG PDMCOREEN [0..0] =============================================== */ +typedef enum { /*!< PDM_PCFG_PDMCOREEN */ + PDM_PCFG_PDMCOREEN_EN = 1, /*!< EN : Enable Data Streaming. value. */ + PDM_PCFG_PDMCOREEN_DIS = 0, /*!< DIS : Disable Data Streaming. value. */ +} PDM_PCFG_PDMCOREEN_Enum; + +/* ========================================================= VCFG ========================================================== */ +/* =============================================== PDM VCFG IOCLKEN [31..31] =============================================== */ +typedef enum { /*!< PDM_VCFG_IOCLKEN */ + PDM_VCFG_IOCLKEN_DIS = 0, /*!< DIS : Disable FIFO read. value. */ + PDM_VCFG_IOCLKEN_EN = 1, /*!< EN : Enable FIFO read. value. */ +} PDM_VCFG_IOCLKEN_Enum; + +/* ================================================ PDM VCFG RSTB [30..30] ================================================= */ +typedef enum { /*!< PDM_VCFG_RSTB */ + PDM_VCFG_RSTB_RESET = 0, /*!< RESET : Reset the core. value. */ + PDM_VCFG_RSTB_NORM = 1, /*!< NORM : Enable the core. value. */ +} PDM_VCFG_RSTB_Enum; + +/* ============================================== PDM VCFG PDMCLKSEL [27..29] ============================================== */ +typedef enum { /*!< PDM_VCFG_PDMCLKSEL */ + PDM_VCFG_PDMCLKSEL_DISABLE = 0, /*!< DISABLE : Static value. value. */ + PDM_VCFG_PDMCLKSEL_12MHz = 1, /*!< 12MHz : PDM clock is 12 MHz. value. */ + PDM_VCFG_PDMCLKSEL_6MHz = 2, /*!< 6MHz : PDM clock is 6 MHz. value. */ + PDM_VCFG_PDMCLKSEL_3MHz = 3, /*!< 3MHz : PDM clock is 3 MHz. value. */ + PDM_VCFG_PDMCLKSEL_1_5MHz = 4, /*!< 1_5MHz : PDM clock is 1.5 MHz. value. */ + PDM_VCFG_PDMCLKSEL_750KHz = 5, /*!< 750KHz : PDM clock is 750 KHz. value. */ + PDM_VCFG_PDMCLKSEL_375KHz = 6, /*!< 375KHz : PDM clock is 375 KHz. value. */ + PDM_VCFG_PDMCLKSEL_187KHz = 7, /*!< 187KHz : PDM clock is 187.5 KHz. value. */ +} PDM_VCFG_PDMCLKSEL_Enum; + +/* ============================================== PDM VCFG PDMCLKEN [26..26] =============================================== */ +typedef enum { /*!< PDM_VCFG_PDMCLKEN */ + PDM_VCFG_PDMCLKEN_DIS = 0, /*!< DIS : Disable serial clock. value. */ + PDM_VCFG_PDMCLKEN_EN = 1, /*!< EN : Enable serial clock. value. */ +} PDM_VCFG_PDMCLKEN_Enum; + +/* ================================================ PDM VCFG I2SEN [20..20] ================================================ */ +typedef enum { /*!< PDM_VCFG_I2SEN */ + PDM_VCFG_I2SEN_DIS = 0, /*!< DIS : Disable I2S interface. value. */ + PDM_VCFG_I2SEN_EN = 1, /*!< EN : Enable I2S interface. value. */ +} PDM_VCFG_I2SEN_Enum; + +/* =============================================== PDM VCFG BCLKINV [19..19] =============================================== */ +typedef enum { /*!< PDM_VCFG_BCLKINV */ + PDM_VCFG_BCLKINV_INV = 0, /*!< INV : BCLK inverted. value. */ + PDM_VCFG_BCLKINV_NORM = 1, /*!< NORM : BCLK not inverted. value. */ +} PDM_VCFG_BCLKINV_Enum; + +/* ============================================== PDM VCFG DMICKDEL [17..17] =============================================== */ +typedef enum { /*!< PDM_VCFG_DMICKDEL */ + PDM_VCFG_DMICKDEL_0CYC = 0, /*!< 0CYC : No delay. value. */ + PDM_VCFG_DMICKDEL_1CYC = 1, /*!< 1CYC : 1 cycle delay. value. */ +} PDM_VCFG_DMICKDEL_Enum; + +/* ================================================ PDM VCFG SELAP [16..16] ================================================ */ +typedef enum { /*!< PDM_VCFG_SELAP */ + PDM_VCFG_SELAP_I2S = 1, /*!< I2S : Clock source from I2S BCLK. value. */ + PDM_VCFG_SELAP_INTERNAL = 0, /*!< INTERNAL : Clock source from internal clock generator. value. */ +} PDM_VCFG_SELAP_Enum; + +/* ================================================ PDM VCFG PCMPACK [8..8] ================================================ */ +typedef enum { /*!< PDM_VCFG_PCMPACK */ + PDM_VCFG_PCMPACK_DIS = 0, /*!< DIS : Disable PCM packing. value. */ + PDM_VCFG_PCMPACK_EN = 1, /*!< EN : Enable PCM packing. value. */ +} PDM_VCFG_PCMPACK_Enum; + +/* ================================================= PDM VCFG CHSET [3..4] ================================================= */ +typedef enum { /*!< PDM_VCFG_CHSET */ + PDM_VCFG_CHSET_DIS = 0, /*!< DIS : Channel disabled. value. */ + PDM_VCFG_CHSET_LEFT = 1, /*!< LEFT : Mono left channel. value. */ + PDM_VCFG_CHSET_RIGHT = 2, /*!< RIGHT : Mono right channel. value. */ + PDM_VCFG_CHSET_STEREO = 3, /*!< STEREO : Stereo channels. value. */ +} PDM_VCFG_CHSET_Enum; + +/* ======================================================= VOICESTAT ======================================================= */ +/* ======================================================= FIFOREAD ======================================================== */ +/* ======================================================= FIFOFLUSH ======================================================= */ +/* ======================================================== FIFOTHR ======================================================== */ +/* ========================================================= INTEN ========================================================= */ +/* ======================================================== INTSTAT ======================================================== */ +/* ======================================================== INTCLR ========================================================= */ +/* ======================================================== INTSET ========================================================= */ +/* ======================================================= DMATRIGEN ======================================================= */ +/* ====================================================== DMATRIGSTAT ====================================================== */ +/* ======================================================== DMACFG ========================================================= */ +/* =============================================== PDM DMACFG DMAPRI [8..8] ================================================ */ +typedef enum { /*!< PDM_DMACFG_DMAPRI */ + PDM_DMACFG_DMAPRI_LOW = 0, /*!< LOW : Low Priority (service as best effort) value. */ + PDM_DMACFG_DMAPRI_HIGH = 1, /*!< HIGH : High Priority (service immediately) value. */ +} PDM_DMACFG_DMAPRI_Enum; + +/* =============================================== PDM DMACFG DMADIR [2..2] ================================================ */ +typedef enum { /*!< PDM_DMACFG_DMADIR */ + PDM_DMACFG_DMADIR_P2M = 0, /*!< P2M : Peripheral to Memory (SRAM) transaction. THe PDM module + will only DMA to memory. value. */ + PDM_DMACFG_DMADIR_M2P = 1, /*!< M2P : Memory to Peripheral transaction. Not available for PDM + module value. */ +} PDM_DMACFG_DMADIR_Enum; + +/* ================================================ PDM DMACFG DMAEN [0..0] ================================================ */ +typedef enum { /*!< PDM_DMACFG_DMAEN */ + PDM_DMACFG_DMAEN_DIS = 0, /*!< DIS : Disable DMA Function value. */ + PDM_DMACFG_DMAEN_EN = 1, /*!< EN : Enable DMA Function value. */ +} PDM_DMACFG_DMAEN_Enum; + +/* ====================================================== DMATOTCOUNT ====================================================== */ +/* ====================================================== DMATARGADDR ====================================================== */ +/* ======================================================== DMASTAT ======================================================== */ + + +/* =========================================================================================================================== */ +/* ================ PWRCTRL ================ */ +/* =========================================================================================================================== */ + +/* ======================================================= SUPPLYSRC ======================================================= */ +/* ========================================== PWRCTRL SUPPLYSRC BLEBUCKEN [0..0] =========================================== */ +typedef enum { /*!< PWRCTRL_SUPPLYSRC_BLEBUCKEN */ + PWRCTRL_SUPPLYSRC_BLEBUCKEN_EN = 1, /*!< EN : Enable the BLE Buck. value. */ + PWRCTRL_SUPPLYSRC_BLEBUCKEN_DIS = 0, /*!< DIS : Disable the BLE Buck. value. */ +} PWRCTRL_SUPPLYSRC_BLEBUCKEN_Enum; + +/* ===================================================== SUPPLYSTATUS ====================================================== */ +/* ========================================= PWRCTRL SUPPLYSTATUS BLEBUCKON [1..1] ========================================= */ +typedef enum { /*!< PWRCTRL_SUPPLYSTATUS_BLEBUCKON */ + PWRCTRL_SUPPLYSTATUS_BLEBUCKON_LDO = 0, /*!< LDO : Indicates the the LDO is supplying the BLE/Burst power + domain value. */ + PWRCTRL_SUPPLYSTATUS_BLEBUCKON_BUCK = 1, /*!< BUCK : Indicates the the Buck is supplying the BLE/Burst power + domain value. */ +} PWRCTRL_SUPPLYSTATUS_BLEBUCKON_Enum; + +/* ======================================== PWRCTRL SUPPLYSTATUS SIMOBUCKON [0..0] ========================================= */ +typedef enum { /*!< PWRCTRL_SUPPLYSTATUS_SIMOBUCKON */ + PWRCTRL_SUPPLYSTATUS_SIMOBUCKON_OFF = 0, /*!< OFF : Indicates the the SIMO Buck is OFF. value. */ + PWRCTRL_SUPPLYSTATUS_SIMOBUCKON_ON = 1, /*!< ON : Indicates the the SIMO Buck is ON. value. */ +} PWRCTRL_SUPPLYSTATUS_SIMOBUCKON_Enum; + +/* ======================================================= DEVPWREN ======================================================== */ +/* =========================================== PWRCTRL DEVPWREN PWRBLEL [13..13] =========================================== */ +typedef enum { /*!< PWRCTRL_DEVPWREN_PWRBLEL */ + PWRCTRL_DEVPWREN_PWRBLEL_EN = 1, /*!< EN : Power up BLE controller value. */ + PWRCTRL_DEVPWREN_PWRBLEL_DIS = 0, /*!< DIS : Power down BLE controller value. */ +} PWRCTRL_DEVPWREN_PWRBLEL_Enum; + +/* =========================================== PWRCTRL DEVPWREN PWRPDM [12..12] ============================================ */ +typedef enum { /*!< PWRCTRL_DEVPWREN_PWRPDM */ + PWRCTRL_DEVPWREN_PWRPDM_EN = 1, /*!< EN : Power up PDM value. */ + PWRCTRL_DEVPWREN_PWRPDM_DIS = 0, /*!< DIS : Power down PDM value. */ +} PWRCTRL_DEVPWREN_PWRPDM_Enum; + +/* =========================================== PWRCTRL DEVPWREN PWRMSPI [11..11] =========================================== */ +typedef enum { /*!< PWRCTRL_DEVPWREN_PWRMSPI */ + PWRCTRL_DEVPWREN_PWRMSPI_EN = 1, /*!< EN : Power up MSPI value. */ + PWRCTRL_DEVPWREN_PWRMSPI_DIS = 0, /*!< DIS : Power down MSPI value. */ +} PWRCTRL_DEVPWREN_PWRMSPI_Enum; + +/* ========================================== PWRCTRL DEVPWREN PWRSCARD [10..10] =========================================== */ +typedef enum { /*!< PWRCTRL_DEVPWREN_PWRSCARD */ + PWRCTRL_DEVPWREN_PWRSCARD_EN = 1, /*!< EN : Power up SCARD value. */ + PWRCTRL_DEVPWREN_PWRSCARD_DIS = 0, /*!< DIS : Power down SCARD value. */ +} PWRCTRL_DEVPWREN_PWRSCARD_Enum; + +/* ============================================ PWRCTRL DEVPWREN PWRADC [9..9] ============================================= */ +typedef enum { /*!< PWRCTRL_DEVPWREN_PWRADC */ + PWRCTRL_DEVPWREN_PWRADC_EN = 1, /*!< EN : Power up ADC value. */ + PWRCTRL_DEVPWREN_PWRADC_DIS = 0, /*!< DIS : Power Down ADC value. */ +} PWRCTRL_DEVPWREN_PWRADC_Enum; + +/* =========================================== PWRCTRL DEVPWREN PWRUART1 [8..8] ============================================ */ +typedef enum { /*!< PWRCTRL_DEVPWREN_PWRUART1 */ + PWRCTRL_DEVPWREN_PWRUART1_EN = 1, /*!< EN : Power up UART 1 value. */ + PWRCTRL_DEVPWREN_PWRUART1_DIS = 0, /*!< DIS : Power down UART 1 value. */ +} PWRCTRL_DEVPWREN_PWRUART1_Enum; + +/* =========================================== PWRCTRL DEVPWREN PWRUART0 [7..7] ============================================ */ +typedef enum { /*!< PWRCTRL_DEVPWREN_PWRUART0 */ + PWRCTRL_DEVPWREN_PWRUART0_EN = 1, /*!< EN : Power up UART 0 value. */ + PWRCTRL_DEVPWREN_PWRUART0_DIS = 0, /*!< DIS : Power down UART 0 value. */ +} PWRCTRL_DEVPWREN_PWRUART0_Enum; + +/* ============================================ PWRCTRL DEVPWREN PWRIOM5 [6..6] ============================================ */ +typedef enum { /*!< PWRCTRL_DEVPWREN_PWRIOM5 */ + PWRCTRL_DEVPWREN_PWRIOM5_EN = 1, /*!< EN : Power up IO Master 5 value. */ + PWRCTRL_DEVPWREN_PWRIOM5_DIS = 0, /*!< DIS : Power down IO Master 5 value. */ +} PWRCTRL_DEVPWREN_PWRIOM5_Enum; + +/* ============================================ PWRCTRL DEVPWREN PWRIOM4 [5..5] ============================================ */ +typedef enum { /*!< PWRCTRL_DEVPWREN_PWRIOM4 */ + PWRCTRL_DEVPWREN_PWRIOM4_EN = 1, /*!< EN : Power up IO Master 4 value. */ + PWRCTRL_DEVPWREN_PWRIOM4_DIS = 0, /*!< DIS : Power down IO Master 4 value. */ +} PWRCTRL_DEVPWREN_PWRIOM4_Enum; + +/* ============================================ PWRCTRL DEVPWREN PWRIOM3 [4..4] ============================================ */ +typedef enum { /*!< PWRCTRL_DEVPWREN_PWRIOM3 */ + PWRCTRL_DEVPWREN_PWRIOM3_EN = 1, /*!< EN : Power up IO Master 3 value. */ + PWRCTRL_DEVPWREN_PWRIOM3_DIS = 0, /*!< DIS : Power down IO Master 3 value. */ +} PWRCTRL_DEVPWREN_PWRIOM3_Enum; + +/* ============================================ PWRCTRL DEVPWREN PWRIOM2 [3..3] ============================================ */ +typedef enum { /*!< PWRCTRL_DEVPWREN_PWRIOM2 */ + PWRCTRL_DEVPWREN_PWRIOM2_EN = 1, /*!< EN : Power up IO Master 2 value. */ + PWRCTRL_DEVPWREN_PWRIOM2_DIS = 0, /*!< DIS : Power down IO Master 2 value. */ +} PWRCTRL_DEVPWREN_PWRIOM2_Enum; + +/* ============================================ PWRCTRL DEVPWREN PWRIOM1 [2..2] ============================================ */ +typedef enum { /*!< PWRCTRL_DEVPWREN_PWRIOM1 */ + PWRCTRL_DEVPWREN_PWRIOM1_EN = 1, /*!< EN : Power up IO Master 1 value. */ + PWRCTRL_DEVPWREN_PWRIOM1_DIS = 0, /*!< DIS : Power down IO Master 1 value. */ +} PWRCTRL_DEVPWREN_PWRIOM1_Enum; + +/* ============================================ PWRCTRL DEVPWREN PWRIOM0 [1..1] ============================================ */ +typedef enum { /*!< PWRCTRL_DEVPWREN_PWRIOM0 */ + PWRCTRL_DEVPWREN_PWRIOM0_EN = 1, /*!< EN : Power up IO Master 0 value. */ + PWRCTRL_DEVPWREN_PWRIOM0_DIS = 0, /*!< DIS : Power down IO Master 0 value. */ +} PWRCTRL_DEVPWREN_PWRIOM0_Enum; + +/* ============================================ PWRCTRL DEVPWREN PWRIOS [0..0] ============================================= */ +typedef enum { /*!< PWRCTRL_DEVPWREN_PWRIOS */ + PWRCTRL_DEVPWREN_PWRIOS_EN = 1, /*!< EN : Power up IO slave value. */ + PWRCTRL_DEVPWREN_PWRIOS_DIS = 0, /*!< DIS : Power down IO slave value. */ +} PWRCTRL_DEVPWREN_PWRIOS_Enum; + +/* ===================================================== MEMPWDINSLEEP ===================================================== */ +/* ====================================== PWRCTRL MEMPWDINSLEEP CACHEPWDSLP [31..31] ======================================= */ +typedef enum { /*!< PWRCTRL_MEMPWDINSLEEP_CACHEPWDSLP */ + PWRCTRL_MEMPWDINSLEEP_CACHEPWDSLP_EN = 1, /*!< EN : Power down cache in deep sleep value. */ + PWRCTRL_MEMPWDINSLEEP_CACHEPWDSLP_DIS = 0, /*!< DIS : Retain cache in deep sleep value. */ +} PWRCTRL_MEMPWDINSLEEP_CACHEPWDSLP_Enum; + +/* ====================================== PWRCTRL MEMPWDINSLEEP FLASH1PWDSLP [14..14] ====================================== */ +typedef enum { /*!< PWRCTRL_MEMPWDINSLEEP_FLASH1PWDSLP */ + PWRCTRL_MEMPWDINSLEEP_FLASH1PWDSLP_EN = 1, /*!< EN : Flash1 is powered down during deepsleep value. */ + PWRCTRL_MEMPWDINSLEEP_FLASH1PWDSLP_DIS = 0, /*!< DIS : Flash1 is kept powered on during deepsleep value. */ +} PWRCTRL_MEMPWDINSLEEP_FLASH1PWDSLP_Enum; + +/* ====================================== PWRCTRL MEMPWDINSLEEP FLASH0PWDSLP [13..13] ====================================== */ +typedef enum { /*!< PWRCTRL_MEMPWDINSLEEP_FLASH0PWDSLP */ + PWRCTRL_MEMPWDINSLEEP_FLASH0PWDSLP_EN = 1, /*!< EN : Flash0 is powered down during deepsleep value. */ + PWRCTRL_MEMPWDINSLEEP_FLASH0PWDSLP_DIS = 0, /*!< DIS : Flash0 is kept powered on during deepsleep value. */ +} PWRCTRL_MEMPWDINSLEEP_FLASH0PWDSLP_Enum; + +/* ======================================= PWRCTRL MEMPWDINSLEEP SRAMPWDSLP [3..12] ======================================== */ +typedef enum { /*!< PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP */ + PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_NONE = 0, /*!< NONE : All banks retained value. */ + PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_GROUP0 = 1, /*!< GROUP0 : SRAM GROUP0 powered down (64KB-96KB) value. */ + PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_GROUP1 = 2, /*!< GROUP1 : SRAM GROUP1 powered down (96KB-128KB) value. */ + PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_GROUP2 = 4, /*!< GROUP2 : SRAM GROUP2 powered down (128KB-160KB) value. */ + PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_GROUP3 = 8, /*!< GROUP3 : SRAM GROUP3 powered down (160KB-192KB) value. */ + PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_GROUP4 = 16, /*!< GROUP4 : SRAM GROUP4 powered down (192KB-224KB) value. */ + PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_GROUP5 = 32, /*!< GROUP5 : SRAM GROUP5 powered down (224KB-256KB) value. */ + PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_GROUP6 = 64, /*!< GROUP6 : SRAM GROUP6 powered down (256KB-288KB) value. */ + PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_GROUP7 = 128,/*!< GROUP7 : SRAM GROUP7 powered down (288KB-320KB) value. */ + PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_GROUP8 = 256,/*!< GROUP8 : SRAM GROUP8 powered down (320KB-352KB) value. */ + PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_GROUP9 = 512,/*!< GROUP9 : SRAM GROUP9 powered down (352KB-384KB) value. */ + PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_SRAM64K = 3, /*!< SRAM64K : Powerdown lower 64k SRAM (64KB-128KB) value. */ + PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_SRAM128K = 15,/*!< SRAM128K : Powerdown lower 128k SRAM (64KB-192KB) value. */ + PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_ALLBUTLOWER32K = 1022,/*!< ALLBUTLOWER32K : All SRAM banks but lower 32k powered down (96KB-384KB). + value. */ + PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_ALLBUTLOWER64K = 1020,/*!< ALLBUTLOWER64K : All banks but lower 64k powered down. value. */ + PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_ALLBUTLOWER128K = 1008,/*!< ALLBUTLOWER128K : All banks but lower 128k powered down. value. */ + PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_ALL = 1023, /*!< ALL : All banks powered down. value. */ +} PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_Enum; + +/* ======================================== PWRCTRL MEMPWDINSLEEP DTCMPWDSLP [0..2] ======================================== */ +typedef enum { /*!< PWRCTRL_MEMPWDINSLEEP_DTCMPWDSLP */ + PWRCTRL_MEMPWDINSLEEP_DTCMPWDSLP_NONE = 0, /*!< NONE : All DTCM retained value. */ + PWRCTRL_MEMPWDINSLEEP_DTCMPWDSLP_GROUP0DTCM0 = 1,/*!< GROUP0DTCM0 : Group0_DTCM0 powered down in deep sleep (0KB-8KB) + value. */ + PWRCTRL_MEMPWDINSLEEP_DTCMPWDSLP_GROUP0DTCM1 = 2,/*!< GROUP0DTCM1 : Group0_DTCM1 powered down in deep sleep (8KB-32KB) + value. */ + PWRCTRL_MEMPWDINSLEEP_DTCMPWDSLP_GROUP0 = 3, /*!< GROUP0 : Both DTCMs in group0 are powered down in deep sleep + (0KB-32KB) value. */ + PWRCTRL_MEMPWDINSLEEP_DTCMPWDSLP_ALLBUTGROUP0DTCM0 = 6,/*!< ALLBUTGROUP0DTCM0 : Group1 and Group0_DTCM1 are powered down + in deep sleep (8KB-64KB) value. */ + PWRCTRL_MEMPWDINSLEEP_DTCMPWDSLP_GROUP1 = 4, /*!< GROUP1 : Group1 DTCM powered down in deep sleep (32KB-64KB) + value. */ + PWRCTRL_MEMPWDINSLEEP_DTCMPWDSLP_ALL = 7, /*!< ALL : All DTCMs powered down in deep sleep (0KB-64KB) value. */ +} PWRCTRL_MEMPWDINSLEEP_DTCMPWDSLP_Enum; + +/* ======================================================= MEMPWREN ======================================================== */ +/* =========================================== PWRCTRL MEMPWREN CACHEB2 [31..31] =========================================== */ +typedef enum { /*!< PWRCTRL_MEMPWREN_CACHEB2 */ + PWRCTRL_MEMPWREN_CACHEB2_EN = 1, /*!< EN : Power up Cache Bank 2 value. */ + PWRCTRL_MEMPWREN_CACHEB2_DIS = 0, /*!< DIS : Power down Cache Bank 2 value. */ +} PWRCTRL_MEMPWREN_CACHEB2_Enum; + +/* =========================================== PWRCTRL MEMPWREN CACHEB0 [30..30] =========================================== */ +typedef enum { /*!< PWRCTRL_MEMPWREN_CACHEB0 */ + PWRCTRL_MEMPWREN_CACHEB0_EN = 1, /*!< EN : Power up Cache Bank 0 value. */ + PWRCTRL_MEMPWREN_CACHEB0_DIS = 0, /*!< DIS : Power down Cache Bank 0 value. */ +} PWRCTRL_MEMPWREN_CACHEB0_Enum; + +/* =========================================== PWRCTRL MEMPWREN FLASH1 [14..14] ============================================ */ +typedef enum { /*!< PWRCTRL_MEMPWREN_FLASH1 */ + PWRCTRL_MEMPWREN_FLASH1_EN = 1, /*!< EN : Power up Flash1 value. */ + PWRCTRL_MEMPWREN_FLASH1_DIS = 0, /*!< DIS : Power down Flash1 value. */ +} PWRCTRL_MEMPWREN_FLASH1_Enum; + +/* =========================================== PWRCTRL MEMPWREN FLASH0 [13..13] ============================================ */ +typedef enum { /*!< PWRCTRL_MEMPWREN_FLASH0 */ + PWRCTRL_MEMPWREN_FLASH0_EN = 1, /*!< EN : Power up Flash0 value. */ + PWRCTRL_MEMPWREN_FLASH0_DIS = 0, /*!< DIS : Power down Flash0 value. */ +} PWRCTRL_MEMPWREN_FLASH0_Enum; + +/* ============================================= PWRCTRL MEMPWREN SRAM [3..12] ============================================= */ +typedef enum { /*!< PWRCTRL_MEMPWREN_SRAM */ + PWRCTRL_MEMPWREN_SRAM_NONE = 0, /*!< NONE : Do not power ON any of the SRAM banks value. */ + PWRCTRL_MEMPWREN_SRAM_GROUP0 = 1, /*!< GROUP0 : Power ON only SRAM group0 (0KB-32KB) value. */ + PWRCTRL_MEMPWREN_SRAM_GROUP1 = 2, /*!< GROUP1 : Power ON only SRAM group1 (32KB-64KB) value. */ + PWRCTRL_MEMPWREN_SRAM_GROUP2 = 4, /*!< GROUP2 : Power ON only SRAM group2 (64KB-96KB) value. */ + PWRCTRL_MEMPWREN_SRAM_GROUP3 = 8, /*!< GROUP3 : Power ON only SRAM group3 (96KB-128KB) value. */ + PWRCTRL_MEMPWREN_SRAM_GROUP4 = 16, /*!< GROUP4 : Power ON only SRAM group4 (128KB-160KB) value. */ + PWRCTRL_MEMPWREN_SRAM_GROUP5 = 32, /*!< GROUP5 : Power ON only SRAM group5 (160KB-192KB) value. */ + PWRCTRL_MEMPWREN_SRAM_GROUP6 = 64, /*!< GROUP6 : Power ON only SRAM group6 (192KB-224KB) value. */ + PWRCTRL_MEMPWREN_SRAM_GROUP7 = 128, /*!< GROUP7 : Power ON only SRAM group7 (224KB-256KB) value. */ + PWRCTRL_MEMPWREN_SRAM_GROUP8 = 256, /*!< GROUP8 : Power ON only SRAM group8 (256KB-288KB) value. */ + PWRCTRL_MEMPWREN_SRAM_GROUP9 = 512, /*!< GROUP9 : Power ON only SRAM group9 (288KB-320KB) value. */ + PWRCTRL_MEMPWREN_SRAM_SRAM64K = 3, /*!< SRAM64K : Power ON only lower 64k value. */ + PWRCTRL_MEMPWREN_SRAM_SRAM128K = 15, /*!< SRAM128K : Power ON only lower 128k value. */ + PWRCTRL_MEMPWREN_SRAM_SRAM256K = 255, /*!< SRAM256K : Power ON only lower 256k value. */ + PWRCTRL_MEMPWREN_SRAM_ALL = 1023, /*!< ALL : All SRAM banks (320K) powered ON value. */ +} PWRCTRL_MEMPWREN_SRAM_Enum; + +/* ============================================= PWRCTRL MEMPWREN DTCM [0..2] ============================================== */ +typedef enum { /*!< PWRCTRL_MEMPWREN_DTCM */ + PWRCTRL_MEMPWREN_DTCM_NONE = 0, /*!< NONE : Do not enable power to any DTCMs value. */ + PWRCTRL_MEMPWREN_DTCM_GROUP0DTCM0 = 1, /*!< GROUP0DTCM0 : Power ON only GROUP0_DTCM0 value. */ + PWRCTRL_MEMPWREN_DTCM_GROUP0DTCM1 = 2, /*!< GROUP0DTCM1 : Power ON only GROUP0_DTCM1 value. */ + PWRCTRL_MEMPWREN_DTCM_GROUP0 = 3, /*!< GROUP0 : Power ON only DTCMs in group0 value. */ + PWRCTRL_MEMPWREN_DTCM_GROUP1 = 4, /*!< GROUP1 : Power ON only DTCMs in group1 value. */ + PWRCTRL_MEMPWREN_DTCM_ALL = 7, /*!< ALL : Power ON all DTCMs value. */ +} PWRCTRL_MEMPWREN_DTCM_Enum; + +/* ===================================================== MEMPWRSTATUS ====================================================== */ +/* ===================================================== DEVPWRSTATUS ====================================================== */ +/* ======================================================= SRAMCTRL ======================================================== */ +/* ======================================== PWRCTRL SRAMCTRL SRAMLIGHTSLEEP [8..19] ======================================== */ +typedef enum { /*!< PWRCTRL_SRAMCTRL_SRAMLIGHTSLEEP */ + PWRCTRL_SRAMCTRL_SRAMLIGHTSLEEP_ALL = 255, /*!< ALL : Enable LIGHT SLEEP for ALL SRAMs value. */ + PWRCTRL_SRAMCTRL_SRAMLIGHTSLEEP_DIS = 0, /*!< DIS : Disables LIGHT SLEEP for ALL SRAMs value. */ +} PWRCTRL_SRAMCTRL_SRAMLIGHTSLEEP_Enum; + +/* ======================================= PWRCTRL SRAMCTRL SRAMMASTERCLKGATE [2..2] ======================================= */ +typedef enum { /*!< PWRCTRL_SRAMCTRL_SRAMMASTERCLKGATE */ + PWRCTRL_SRAMCTRL_SRAMMASTERCLKGATE_EN = 1, /*!< EN : Enable Master SRAM Clock Gate value. */ + PWRCTRL_SRAMCTRL_SRAMMASTERCLKGATE_DIS = 0, /*!< DIS : Disables Master SRAM Clock Gating value. */ +} PWRCTRL_SRAMCTRL_SRAMMASTERCLKGATE_Enum; + +/* ========================================== PWRCTRL SRAMCTRL SRAMCLKGATE [1..1] ========================================== */ +typedef enum { /*!< PWRCTRL_SRAMCTRL_SRAMCLKGATE */ + PWRCTRL_SRAMCTRL_SRAMCLKGATE_EN = 1, /*!< EN : Enable Individual SRAM Clock Gating value. */ + PWRCTRL_SRAMCTRL_SRAMCLKGATE_DIS = 0, /*!< DIS : Disables Individual SRAM Clock Gating value. */ +} PWRCTRL_SRAMCTRL_SRAMCLKGATE_Enum; + +/* ======================================================= ADCSTATUS ======================================================= */ +/* ========================================================= MISC ========================================================== */ +/* ============================================ PWRCTRL MISC MEMVRLPBLE [6..6] ============================================= */ +typedef enum { /*!< PWRCTRL_MISC_MEMVRLPBLE */ + PWRCTRL_MISC_MEMVRLPBLE_EN = 1, /*!< EN : Mem VR can go to lp mode even when BLE is powered on. value. */ + PWRCTRL_MISC_MEMVRLPBLE_DIS = 0, /*!< DIS : Mem VR will stay in active mode when BLE is powered on. + value. */ +} PWRCTRL_MISC_MEMVRLPBLE_Enum; + +/* =========================================== PWRCTRL MISC FORCEMEMVRADC [4..5] =========================================== */ +typedef enum { /*!< PWRCTRL_MISC_FORCEMEMVRADC */ + PWRCTRL_MISC_FORCEMEMVRADC_ACT = 2, /*!< ACT : In this mode if all the other domains but ADC are powered + down, mem VR will stay in ACT mode. value. */ + PWRCTRL_MISC_FORCEMEMVRADC_LP = 1, /*!< LP : In this mode if all the other domains but ADC are powered + down, mem VR will stay in LP mode. value. */ + PWRCTRL_MISC_FORCEMEMVRADC_DIS = 0, /*!< DIS : In this mode if all the other domains but ADC are powered + down, mem VR will duty cycle between active and LP modes + depending on ADC sampling. value. */ +} PWRCTRL_MISC_FORCEMEMVRADC_Enum; + +/* ============================================ PWRCTRL MISC SIMOBUCKEN [0..0] ============================================= */ +typedef enum { /*!< PWRCTRL_MISC_SIMOBUCKEN */ + PWRCTRL_MISC_SIMOBUCKEN_EN = 1, /*!< EN : Enable the SIMO Buck value. */ + PWRCTRL_MISC_SIMOBUCKEN_DIS = 0, /*!< DIS : Disable the SIMO Buck value. */ +} PWRCTRL_MISC_SIMOBUCKEN_Enum; + +/* ===================================================== DEVPWREVENTEN ===================================================== */ +/* ======================================= PWRCTRL DEVPWREVENTEN BURSTEVEN [31..31] ======================================== */ +typedef enum { /*!< PWRCTRL_DEVPWREVENTEN_BURSTEVEN */ + PWRCTRL_DEVPWREVENTEN_BURSTEVEN_EN = 1, /*!< EN : Enable BURST status event value. */ + PWRCTRL_DEVPWREVENTEN_BURSTEVEN_DIS = 0, /*!< DIS : Disable BURST status event value. */ +} PWRCTRL_DEVPWREVENTEN_BURSTEVEN_Enum; + +/* ==================================== PWRCTRL DEVPWREVENTEN BURSTFEATUREEVEN [30..30] ==================================== */ +typedef enum { /*!< PWRCTRL_DEVPWREVENTEN_BURSTFEATUREEVEN */ + PWRCTRL_DEVPWREVENTEN_BURSTFEATUREEVEN_EN = 1,/*!< EN : Enable BURSTFEATURE status event value. */ + PWRCTRL_DEVPWREVENTEN_BURSTFEATUREEVEN_DIS = 0,/*!< DIS : Disable BURSTFEATURE status event value. */ +} PWRCTRL_DEVPWREVENTEN_BURSTFEATUREEVEN_Enum; + +/* ===================================== PWRCTRL DEVPWREVENTEN BLEFEATUREEVEN [29..29] ===================================== */ +typedef enum { /*!< PWRCTRL_DEVPWREVENTEN_BLEFEATUREEVEN */ + PWRCTRL_DEVPWREVENTEN_BLEFEATUREEVEN_EN = 1, /*!< EN : Enable BLEFEATURE status event value. */ + PWRCTRL_DEVPWREVENTEN_BLEFEATUREEVEN_DIS = 0, /*!< DIS : Disable BLEFEATURE status event value. */ +} PWRCTRL_DEVPWREVENTEN_BLEFEATUREEVEN_Enum; + +/* ========================================= PWRCTRL DEVPWREVENTEN BLELEVEN [8..8] ========================================= */ +typedef enum { /*!< PWRCTRL_DEVPWREVENTEN_BLELEVEN */ + PWRCTRL_DEVPWREVENTEN_BLELEVEN_EN = 1, /*!< EN : Enable BLE power-on status event value. */ + PWRCTRL_DEVPWREVENTEN_BLELEVEN_DIS = 0, /*!< DIS : Disable BLE power-on status event value. */ +} PWRCTRL_DEVPWREVENTEN_BLELEVEN_Enum; + +/* ========================================= PWRCTRL DEVPWREVENTEN PDMEVEN [7..7] ========================================== */ +typedef enum { /*!< PWRCTRL_DEVPWREVENTEN_PDMEVEN */ + PWRCTRL_DEVPWREVENTEN_PDMEVEN_EN = 1, /*!< EN : Enable PDM power-on status event value. */ + PWRCTRL_DEVPWREVENTEN_PDMEVEN_DIS = 0, /*!< DIS : Disable PDM power-on status event value. */ +} PWRCTRL_DEVPWREVENTEN_PDMEVEN_Enum; + +/* ========================================= PWRCTRL DEVPWREVENTEN MSPIEVEN [6..6] ========================================= */ +typedef enum { /*!< PWRCTRL_DEVPWREVENTEN_MSPIEVEN */ + PWRCTRL_DEVPWREVENTEN_MSPIEVEN_EN = 1, /*!< EN : Enable MSPI power-on status event value. */ + PWRCTRL_DEVPWREVENTEN_MSPIEVEN_DIS = 0, /*!< DIS : Disable MSPI power-on status event value. */ +} PWRCTRL_DEVPWREVENTEN_MSPIEVEN_Enum; + +/* ========================================= PWRCTRL DEVPWREVENTEN ADCEVEN [5..5] ========================================== */ +typedef enum { /*!< PWRCTRL_DEVPWREVENTEN_ADCEVEN */ + PWRCTRL_DEVPWREVENTEN_ADCEVEN_EN = 1, /*!< EN : Enable ADC power-on status event value. */ + PWRCTRL_DEVPWREVENTEN_ADCEVEN_DIS = 0, /*!< DIS : Disable ADC power-on status event value. */ +} PWRCTRL_DEVPWREVENTEN_ADCEVEN_Enum; + +/* ========================================= PWRCTRL DEVPWREVENTEN HCPCEVEN [4..4] ========================================= */ +typedef enum { /*!< PWRCTRL_DEVPWREVENTEN_HCPCEVEN */ + PWRCTRL_DEVPWREVENTEN_HCPCEVEN_EN = 1, /*!< EN : Enable HCPC power-on status event value. */ + PWRCTRL_DEVPWREVENTEN_HCPCEVEN_DIS = 0, /*!< DIS : Disable HCPC power-on status event value. */ +} PWRCTRL_DEVPWREVENTEN_HCPCEVEN_Enum; + +/* ========================================= PWRCTRL DEVPWREVENTEN HCPBEVEN [3..3] ========================================= */ +typedef enum { /*!< PWRCTRL_DEVPWREVENTEN_HCPBEVEN */ + PWRCTRL_DEVPWREVENTEN_HCPBEVEN_EN = 1, /*!< EN : Enable HCPB power-on status event value. */ + PWRCTRL_DEVPWREVENTEN_HCPBEVEN_DIS = 0, /*!< DIS : Disable HCPB power-on status event value. */ +} PWRCTRL_DEVPWREVENTEN_HCPBEVEN_Enum; + +/* ========================================= PWRCTRL DEVPWREVENTEN HCPAEVEN [2..2] ========================================= */ +typedef enum { /*!< PWRCTRL_DEVPWREVENTEN_HCPAEVEN */ + PWRCTRL_DEVPWREVENTEN_HCPAEVEN_EN = 1, /*!< EN : Enable HCPA power-on status event value. */ + PWRCTRL_DEVPWREVENTEN_HCPAEVEN_DIS = 0, /*!< DIS : Disable HCPA power-on status event value. */ +} PWRCTRL_DEVPWREVENTEN_HCPAEVEN_Enum; + +/* ========================================= PWRCTRL DEVPWREVENTEN MCUHEVEN [1..1] ========================================= */ +typedef enum { /*!< PWRCTRL_DEVPWREVENTEN_MCUHEVEN */ + PWRCTRL_DEVPWREVENTEN_MCUHEVEN_EN = 1, /*!< EN : Enable MCHU power-on status event value. */ + PWRCTRL_DEVPWREVENTEN_MCUHEVEN_DIS = 0, /*!< DIS : Disable MCUH power-on status event value. */ +} PWRCTRL_DEVPWREVENTEN_MCUHEVEN_Enum; + +/* ========================================= PWRCTRL DEVPWREVENTEN MCULEVEN [0..0] ========================================= */ +typedef enum { /*!< PWRCTRL_DEVPWREVENTEN_MCULEVEN */ + PWRCTRL_DEVPWREVENTEN_MCULEVEN_EN = 1, /*!< EN : Enable MCUL power-on status event value. */ + PWRCTRL_DEVPWREVENTEN_MCULEVEN_DIS = 0, /*!< DIS : Disable MCUL power-on status event value. */ +} PWRCTRL_DEVPWREVENTEN_MCULEVEN_Enum; + +/* ===================================================== MEMPWREVENTEN ===================================================== */ +/* ======================================= PWRCTRL MEMPWREVENTEN CACHEB2EN [31..31] ======================================== */ +typedef enum { /*!< PWRCTRL_MEMPWREVENTEN_CACHEB2EN */ + PWRCTRL_MEMPWREVENTEN_CACHEB2EN_EN = 1, /*!< EN : Enable CACHE BANK 2 status event value. */ + PWRCTRL_MEMPWREVENTEN_CACHEB2EN_DIS = 0, /*!< DIS : Disable CACHE BANK 2 status event value. */ +} PWRCTRL_MEMPWREVENTEN_CACHEB2EN_Enum; + +/* ======================================= PWRCTRL MEMPWREVENTEN CACHEB0EN [30..30] ======================================== */ +typedef enum { /*!< PWRCTRL_MEMPWREVENTEN_CACHEB0EN */ + PWRCTRL_MEMPWREVENTEN_CACHEB0EN_EN = 1, /*!< EN : Enable CACHE BANK 0 status event value. */ + PWRCTRL_MEMPWREVENTEN_CACHEB0EN_DIS = 0, /*!< DIS : Disable CACHE BANK 0 status event value. */ +} PWRCTRL_MEMPWREVENTEN_CACHEB0EN_Enum; + +/* ======================================== PWRCTRL MEMPWREVENTEN FLASH1EN [14..14] ======================================== */ +typedef enum { /*!< PWRCTRL_MEMPWREVENTEN_FLASH1EN */ + PWRCTRL_MEMPWREVENTEN_FLASH1EN_EN = 1, /*!< EN : Enable FLASH status event value. */ + PWRCTRL_MEMPWREVENTEN_FLASH1EN_DIS = 0, /*!< DIS : Disables FLASH status event value. */ +} PWRCTRL_MEMPWREVENTEN_FLASH1EN_Enum; + +/* ======================================== PWRCTRL MEMPWREVENTEN FLASH0EN [13..13] ======================================== */ +typedef enum { /*!< PWRCTRL_MEMPWREVENTEN_FLASH0EN */ + PWRCTRL_MEMPWREVENTEN_FLASH0EN_EN = 1, /*!< EN : Enable FLASH status event value. */ + PWRCTRL_MEMPWREVENTEN_FLASH0EN_DIS = 0, /*!< DIS : Disables FLASH status event value. */ +} PWRCTRL_MEMPWREVENTEN_FLASH0EN_Enum; + +/* ========================================= PWRCTRL MEMPWREVENTEN SRAMEN [3..12] ========================================== */ +typedef enum { /*!< PWRCTRL_MEMPWREVENTEN_SRAMEN */ + PWRCTRL_MEMPWREVENTEN_SRAMEN_NONE = 0, /*!< NONE : Disable SRAM power-on status event value. */ + PWRCTRL_MEMPWREVENTEN_SRAMEN_GROUP0EN = 1, /*!< GROUP0EN : Enable SRAM group0 (0KB-32KB) power on status event + value. */ + PWRCTRL_MEMPWREVENTEN_SRAMEN_GROUP1EN = 2, /*!< GROUP1EN : Enable SRAM group1 (32KB-64KB) power on status event + value. */ + PWRCTRL_MEMPWREVENTEN_SRAMEN_GROUP2EN = 4, /*!< GROUP2EN : Enable SRAM group2 (64KB-96KB) power on status event + value. */ + PWRCTRL_MEMPWREVENTEN_SRAMEN_GROUP3EN = 8, /*!< GROUP3EN : Enable SRAM group3 (96KB-128KB) power on status event + value. */ + PWRCTRL_MEMPWREVENTEN_SRAMEN_GROUP4EN = 16, /*!< GROUP4EN : Enable SRAM group4 (128KB-160KB) power on status + event value. */ + PWRCTRL_MEMPWREVENTEN_SRAMEN_GROUP5EN = 32, /*!< GROUP5EN : Enable SRAM group5 (160KB-192KB) power on status + event value. */ + PWRCTRL_MEMPWREVENTEN_SRAMEN_GROUP6EN = 64, /*!< GROUP6EN : Enable SRAM group6 (192KB-224KB) power on status + event value. */ + PWRCTRL_MEMPWREVENTEN_SRAMEN_GROUP7EN = 128, /*!< GROUP7EN : Enable SRAM group7 (224KB-256KB) power on status + event value. */ + PWRCTRL_MEMPWREVENTEN_SRAMEN_GROUP8EN = 256, /*!< GROUP8EN : Enable SRAM group8 (256KB-288KB) power on status + event value. */ + PWRCTRL_MEMPWREVENTEN_SRAMEN_GROUP9EN = 512, /*!< GROUP9EN : Enable SRAM group9 (288KB-320KB) power on status + event value. */ +} PWRCTRL_MEMPWREVENTEN_SRAMEN_Enum; + +/* ========================================== PWRCTRL MEMPWREVENTEN DTCMEN [0..2] ========================================== */ +typedef enum { /*!< PWRCTRL_MEMPWREVENTEN_DTCMEN */ + PWRCTRL_MEMPWREVENTEN_DTCMEN_NONE = 0, /*!< NONE : Do not enable DTCM power-on status event value. */ + PWRCTRL_MEMPWREVENTEN_DTCMEN_GROUP0DTCM0EN = 1,/*!< GROUP0DTCM0EN : Enable GROUP0_DTCM0 power on status event value. */ + PWRCTRL_MEMPWREVENTEN_DTCMEN_GROUP0DTCM1EN = 2,/*!< GROUP0DTCM1EN : Enable GROUP0_DTCM1 power on status event value. */ + PWRCTRL_MEMPWREVENTEN_DTCMEN_GROUP0EN = 3, /*!< GROUP0EN : Enable DTCMs in group0 power on status event value. */ + PWRCTRL_MEMPWREVENTEN_DTCMEN_GROUP1EN = 4, /*!< GROUP1EN : Enable DTCMs in group1 power on status event value. */ + PWRCTRL_MEMPWREVENTEN_DTCMEN_ALL = 7, /*!< ALL : Enable all DTCM power on status event value. */ +} PWRCTRL_MEMPWREVENTEN_DTCMEN_Enum; + + + +/* =========================================================================================================================== */ +/* ================ RSTGEN ================ */ +/* =========================================================================================================================== */ + +/* ========================================================== CFG ========================================================== */ +/* ========================================================= SWPOI ========================================================= */ +/* ============================================= RSTGEN SWPOI SWPOIKEY [0..7] ============================================== */ +typedef enum { /*!< RSTGEN_SWPOI_SWPOIKEY */ + RSTGEN_SWPOI_SWPOIKEY_KEYVALUE = 27, /*!< KEYVALUE : Writing 0x1B key value generates a software POI reset. + value. */ +} RSTGEN_SWPOI_SWPOIKEY_Enum; + +/* ========================================================= SWPOR ========================================================= */ +/* ============================================= RSTGEN SWPOR SWPORKEY [0..7] ============================================== */ +typedef enum { /*!< RSTGEN_SWPOR_SWPORKEY */ + RSTGEN_SWPOR_SWPORKEY_KEYVALUE = 212, /*!< KEYVALUE : Writing 0xD4 key value generates a software POR reset. + value. */ +} RSTGEN_SWPOR_SWPORKEY_Enum; + +/* ======================================================== TPIURST ======================================================== */ +/* ========================================================= INTEN ========================================================= */ +/* ======================================================== INTSTAT ======================================================== */ +/* ======================================================== INTCLR ========================================================= */ +/* ======================================================== INTSET ========================================================= */ +/* ========================================================= STAT ========================================================== */ + + +/* =========================================================================================================================== */ +/* ================ RTC ================ */ +/* =========================================================================================================================== */ + +/* ======================================================== CTRLOW ========================================================= */ +/* ========================================================= CTRUP ========================================================= */ +/* =============================================== RTC CTRUP CTERR [31..31] ================================================ */ +typedef enum { /*!< RTC_CTRUP_CTERR */ + RTC_CTRUP_CTERR_NOERR = 0, /*!< NOERR : No read error occurred value. */ + RTC_CTRUP_CTERR_RDERR = 1, /*!< RDERR : Read error occurred value. */ +} RTC_CTRUP_CTERR_Enum; + +/* ================================================ RTC CTRUP CEB [28..28] ================================================= */ +typedef enum { /*!< RTC_CTRUP_CEB */ + RTC_CTRUP_CEB_DIS = 0, /*!< DIS : Disable the Century bit from changing value. */ + RTC_CTRUP_CEB_EN = 1, /*!< EN : Enable the Century bit to change value. */ +} RTC_CTRUP_CEB_Enum; + +/* ================================================= RTC CTRUP CB [27..27] ================================================= */ +typedef enum { /*!< RTC_CTRUP_CB */ + RTC_CTRUP_CB_2000 = 0, /*!< 2000 : Century is 2000s value. */ + RTC_CTRUP_CB_1900_2100 = 1, /*!< 1900_2100 : Century is 1900s/2100s value. */ +} RTC_CTRUP_CB_Enum; + +/* ======================================================== ALMLOW ========================================================= */ +/* ========================================================= ALMUP ========================================================= */ +/* ======================================================== RTCCTL ========================================================= */ +/* =============================================== RTC RTCCTL HR1224 [5..5] ================================================ */ +typedef enum { /*!< RTC_RTCCTL_HR1224 */ + RTC_RTCCTL_HR1224_24HR = 0, /*!< 24HR : Hours in 24 hour mode value. */ + RTC_RTCCTL_HR1224_12HR = 1, /*!< 12HR : Hours in 12 hour mode value. */ +} RTC_RTCCTL_HR1224_Enum; + +/* ================================================ RTC RTCCTL RSTOP [4..4] ================================================ */ +typedef enum { /*!< RTC_RTCCTL_RSTOP */ + RTC_RTCCTL_RSTOP_RUN = 0, /*!< RUN : Allow the RTC input clock to run value. */ + RTC_RTCCTL_RSTOP_STOP = 1, /*!< STOP : Stop the RTC input clock value. */ +} RTC_RTCCTL_RSTOP_Enum; + +/* ================================================= RTC RTCCTL RPT [1..3] ================================================= */ +typedef enum { /*!< RTC_RTCCTL_RPT */ + RTC_RTCCTL_RPT_DIS = 0, /*!< DIS : Alarm interrupt disabled value. */ + RTC_RTCCTL_RPT_YEAR = 1, /*!< YEAR : Interrupt every year value. */ + RTC_RTCCTL_RPT_MONTH = 2, /*!< MONTH : Interrupt every month value. */ + RTC_RTCCTL_RPT_WEEK = 3, /*!< WEEK : Interrupt every week value. */ + RTC_RTCCTL_RPT_DAY = 4, /*!< DAY : Interrupt every day value. */ + RTC_RTCCTL_RPT_HR = 5, /*!< HR : Interrupt every hour value. */ + RTC_RTCCTL_RPT_MIN = 6, /*!< MIN : Interrupt every minute value. */ + RTC_RTCCTL_RPT_SEC = 7, /*!< SEC : Interrupt every second/10th/100th value. */ +} RTC_RTCCTL_RPT_Enum; + +/* ================================================ RTC RTCCTL WRTC [0..0] ================================================= */ +typedef enum { /*!< RTC_RTCCTL_WRTC */ + RTC_RTCCTL_WRTC_DIS = 0, /*!< DIS : Counter writes are disabled value. */ + RTC_RTCCTL_WRTC_EN = 1, /*!< EN : Counter writes are enabled value. */ +} RTC_RTCCTL_WRTC_Enum; + +/* ========================================================= INTEN ========================================================= */ +/* ======================================================== INTSTAT ======================================================== */ +/* ======================================================== INTCLR ========================================================= */ +/* ======================================================== INTSET ========================================================= */ + + +/* =========================================================================================================================== */ +/* ================ SCARD ================ */ +/* =========================================================================================================================== */ + +/* ========================================================== SR =========================================================== */ +/* ========================================================== DR =========================================================== */ +/* ========================================================== SR1 ========================================================== */ +/* ====================================================== RETXCNTRMI ======================================================= */ +/* ======================================================== CLKCTRL ======================================================== */ + + +/* =========================================================================================================================== */ +/* ================ SECURITY ================ */ +/* =========================================================================================================================== */ + +/* ========================================================= CTRL ========================================================== */ +/* ============================================= SECURITY CTRL FUNCTION [4..7] ============================================= */ +typedef enum { /*!< SECURITY_CTRL_FUNCTION */ + SECURITY_CTRL_FUNCTION_CRC32 = 0, /*!< CRC32 : Perform CRC32 operation value. */ +} SECURITY_CTRL_FUNCTION_Enum; + +/* ======================================================== SRCADDR ======================================================== */ +/* ========================================================== LEN ========================================================== */ +/* ======================================================== RESULT ========================================================= */ +/* ======================================================= LOCKCTRL ======================================================== */ +/* ============================================ SECURITY LOCKCTRL SELECT [0..7] ============================================ */ +typedef enum { /*!< SECURITY_LOCKCTRL_SELECT */ + SECURITY_LOCKCTRL_SELECT_CUSTOMER_KEY = 1, /*!< CUSTOMER_KEY : Unlock Customer Key (access to top half of info0) + value. */ + SECURITY_LOCKCTRL_SELECT_NONE = 0, /*!< NONE : Lock Control should be set to NONE when not in use. value. */ +} SECURITY_LOCKCTRL_SELECT_Enum; + +/* ======================================================= LOCKSTAT ======================================================== */ +/* =========================================== SECURITY LOCKSTAT STATUS [0..31] ============================================ */ +typedef enum { /*!< SECURITY_LOCKSTAT_STATUS */ + SECURITY_LOCKSTAT_STATUS_CUSTOMER_KEY = 1, /*!< CUSTOMER_KEY : Customer Key is unlocked (access is granted to + top half of info0) value. */ + SECURITY_LOCKSTAT_STATUS_NONE = 0, /*!< NONE : No resources are unlocked value. */ +} SECURITY_LOCKSTAT_STATUS_Enum; + +/* ========================================================= KEY0 ========================================================== */ +/* ========================================================= KEY1 ========================================================== */ +/* ========================================================= KEY2 ========================================================== */ +/* ========================================================= KEY3 ========================================================== */ + + +/* =========================================================================================================================== */ +/* ================ UART0 ================ */ +/* =========================================================================================================================== */ + +/* ========================================================== DR =========================================================== */ +/* =============================================== UART0 DR OEDATA [11..11] ================================================ */ +typedef enum { /*!< UART0_DR_OEDATA */ + UART0_DR_OEDATA_NOERR = 0, /*!< NOERR : No error on UART OEDATA, overrun error indicator. value. */ + UART0_DR_OEDATA_ERR = 1, /*!< ERR : Error on UART OEDATA, overrun error indicator. value. */ +} UART0_DR_OEDATA_Enum; + +/* =============================================== UART0 DR BEDATA [10..10] ================================================ */ +typedef enum { /*!< UART0_DR_BEDATA */ + UART0_DR_BEDATA_NOERR = 0, /*!< NOERR : No error on UART BEDATA, break error indicator. value. */ + UART0_DR_BEDATA_ERR = 1, /*!< ERR : Error on UART BEDATA, break error indicator. value. */ +} UART0_DR_BEDATA_Enum; + +/* ================================================ UART0 DR PEDATA [9..9] ================================================= */ +typedef enum { /*!< UART0_DR_PEDATA */ + UART0_DR_PEDATA_NOERR = 0, /*!< NOERR : No error on UART PEDATA, parity error indicator. value. */ + UART0_DR_PEDATA_ERR = 1, /*!< ERR : Error on UART PEDATA, parity error indicator. value. */ +} UART0_DR_PEDATA_Enum; + +/* ================================================ UART0 DR FEDATA [8..8] ================================================= */ +typedef enum { /*!< UART0_DR_FEDATA */ + UART0_DR_FEDATA_NOERR = 0, /*!< NOERR : No error on UART FEDATA, framing error indicator. value. */ + UART0_DR_FEDATA_ERR = 1, /*!< ERR : Error on UART FEDATA, framing error indicator. value. */ +} UART0_DR_FEDATA_Enum; + +/* ========================================================== RSR ========================================================== */ +/* ================================================ UART0 RSR OESTAT [3..3] ================================================ */ +typedef enum { /*!< UART0_RSR_OESTAT */ + UART0_RSR_OESTAT_NOERR = 0, /*!< NOERR : No error on UART OESTAT, overrun error indicator. value. */ + UART0_RSR_OESTAT_ERR = 1, /*!< ERR : Error on UART OESTAT, overrun error indicator. value. */ +} UART0_RSR_OESTAT_Enum; + +/* ================================================ UART0 RSR BESTAT [2..2] ================================================ */ +typedef enum { /*!< UART0_RSR_BESTAT */ + UART0_RSR_BESTAT_NOERR = 0, /*!< NOERR : No error on UART BESTAT, break error indicator. value. */ + UART0_RSR_BESTAT_ERR = 1, /*!< ERR : Error on UART BESTAT, break error indicator. value. */ +} UART0_RSR_BESTAT_Enum; + +/* ================================================ UART0 RSR PESTAT [1..1] ================================================ */ +typedef enum { /*!< UART0_RSR_PESTAT */ + UART0_RSR_PESTAT_NOERR = 0, /*!< NOERR : No error on UART PESTAT, parity error indicator. value. */ + UART0_RSR_PESTAT_ERR = 1, /*!< ERR : Error on UART PESTAT, parity error indicator. value. */ +} UART0_RSR_PESTAT_Enum; + +/* ================================================ UART0 RSR FESTAT [0..0] ================================================ */ +typedef enum { /*!< UART0_RSR_FESTAT */ + UART0_RSR_FESTAT_NOERR = 0, /*!< NOERR : No error on UART FESTAT, framing error indicator. value. */ + UART0_RSR_FESTAT_ERR = 1, /*!< ERR : Error on UART FESTAT, framing error indicator. value. */ +} UART0_RSR_FESTAT_Enum; + +/* ========================================================== FR =========================================================== */ +/* ================================================= UART0 FR TXFE [7..7] ================================================== */ +typedef enum { /*!< UART0_FR_TXFE */ + UART0_FR_TXFE_XMTFIFO_EMPTY = 1, /*!< XMTFIFO_EMPTY : Transmit fifo is empty. value. */ +} UART0_FR_TXFE_Enum; + +/* ================================================= UART0 FR RXFF [6..6] ================================================== */ +typedef enum { /*!< UART0_FR_RXFF */ + UART0_FR_RXFF_RCVFIFO_FULL = 1, /*!< RCVFIFO_FULL : Receive fifo is full. value. */ +} UART0_FR_RXFF_Enum; + +/* ================================================= UART0 FR TXFF [5..5] ================================================== */ +typedef enum { /*!< UART0_FR_TXFF */ + UART0_FR_TXFF_XMTFIFO_FULL = 1, /*!< XMTFIFO_FULL : Transmit fifo is full. value. */ +} UART0_FR_TXFF_Enum; + +/* ================================================= UART0 FR RXFE [4..4] ================================================== */ +typedef enum { /*!< UART0_FR_RXFE */ + UART0_FR_RXFE_RCVFIFO_EMPTY = 1, /*!< RCVFIFO_EMPTY : Receive fifo is empty. value. */ +} UART0_FR_RXFE_Enum; + +/* ================================================= UART0 FR BUSY [3..3] ================================================== */ +typedef enum { /*!< UART0_FR_BUSY */ + UART0_FR_BUSY_BUSY = 1, /*!< BUSY : UART busy indicator. value. */ +} UART0_FR_BUSY_Enum; + +/* ================================================== UART0 FR DCD [2..2] ================================================== */ +typedef enum { /*!< UART0_FR_DCD */ + UART0_FR_DCD_DETECTED = 1, /*!< DETECTED : Data carrier detect detected. value. */ +} UART0_FR_DCD_Enum; + +/* ================================================== UART0 FR DSR [1..1] ================================================== */ +typedef enum { /*!< UART0_FR_DSR */ + UART0_FR_DSR_READY = 1, /*!< READY : Data set ready. value. */ +} UART0_FR_DSR_Enum; + +/* ================================================== UART0 FR CTS [0..0] ================================================== */ +typedef enum { /*!< UART0_FR_CTS */ + UART0_FR_CTS_CLEARTOSEND = 1, /*!< CLEARTOSEND : Clear to send is indicated. value. */ +} UART0_FR_CTS_Enum; + +/* ========================================================= ILPR ========================================================== */ +/* ========================================================= IBRD ========================================================== */ +/* ========================================================= FBRD ========================================================== */ +/* ========================================================= LCRH ========================================================== */ +/* ========================================================== CR =========================================================== */ +/* ================================================ UART0 CR CLKSEL [4..6] ================================================= */ +typedef enum { /*!< UART0_CR_CLKSEL */ + UART0_CR_CLKSEL_NOCLK = 0, /*!< NOCLK : No UART clock. This is the low power default. value. */ + UART0_CR_CLKSEL_24MHZ = 1, /*!< 24MHZ : 24 MHz clock. value. */ + UART0_CR_CLKSEL_12MHZ = 2, /*!< 12MHZ : 12 MHz clock. value. */ + UART0_CR_CLKSEL_6MHZ = 3, /*!< 6MHZ : 6 MHz clock. value. */ + UART0_CR_CLKSEL_3MHZ = 4, /*!< 3MHZ : 3 MHz clock. value. */ +} UART0_CR_CLKSEL_Enum; + +/* ========================================================= IFLS ========================================================== */ +/* ========================================================== IER ========================================================== */ +/* ========================================================== IES ========================================================== */ +/* ========================================================== MIS ========================================================== */ +/* ========================================================== IEC ========================================================== */ + + +/* =========================================================================================================================== */ +/* ================ VCOMP ================ */ +/* =========================================================================================================================== */ + +/* ========================================================== CFG ========================================================== */ +/* =============================================== VCOMP CFG LVLSEL [16..19] =============================================== */ +typedef enum { /*!< VCOMP_CFG_LVLSEL */ + VCOMP_CFG_LVLSEL_0P58V = 0, /*!< 0P58V : Set Reference input to 0.58 Volts. value. */ + VCOMP_CFG_LVLSEL_0P77V = 1, /*!< 0P77V : Set Reference input to 0.77 Volts. value. */ + VCOMP_CFG_LVLSEL_0P97V = 2, /*!< 0P97V : Set Reference input to 0.97 Volts. value. */ + VCOMP_CFG_LVLSEL_1P16V = 3, /*!< 1P16V : Set Reference input to 1.16 Volts. value. */ + VCOMP_CFG_LVLSEL_1P35V = 4, /*!< 1P35V : Set Reference input to 1.35 Volts. value. */ + VCOMP_CFG_LVLSEL_1P55V = 5, /*!< 1P55V : Set Reference input to 1.55 Volts. value. */ + VCOMP_CFG_LVLSEL_1P74V = 6, /*!< 1P74V : Set Reference input to 1.74 Volts. value. */ + VCOMP_CFG_LVLSEL_1P93V = 7, /*!< 1P93V : Set Reference input to 1.93 Volts. value. */ + VCOMP_CFG_LVLSEL_2P13V = 8, /*!< 2P13V : Set Reference input to 2.13 Volts. value. */ + VCOMP_CFG_LVLSEL_2P32V = 9, /*!< 2P32V : Set Reference input to 2.32 Volts. value. */ + VCOMP_CFG_LVLSEL_2P51V = 10, /*!< 2P51V : Set Reference input to 2.51 Volts. value. */ + VCOMP_CFG_LVLSEL_2P71V = 11, /*!< 2P71V : Set Reference input to 2.71 Volts. value. */ + VCOMP_CFG_LVLSEL_2P90V = 12, /*!< 2P90V : Set Reference input to 2.90 Volts. value. */ + VCOMP_CFG_LVLSEL_3P09V = 13, /*!< 3P09V : Set Reference input to 3.09 Volts. value. */ + VCOMP_CFG_LVLSEL_3P29V = 14, /*!< 3P29V : Set Reference input to 3.29 Volts. value. */ + VCOMP_CFG_LVLSEL_3P48V = 15, /*!< 3P48V : Set Reference input to 3.48 Volts. value. */ +} VCOMP_CFG_LVLSEL_Enum; + +/* ================================================= VCOMP CFG NSEL [8..9] ================================================= */ +typedef enum { /*!< VCOMP_CFG_NSEL */ + VCOMP_CFG_NSEL_VREFEXT1 = 0, /*!< VREFEXT1 : Use external reference 1 for reference input. value. */ + VCOMP_CFG_NSEL_VREFEXT2 = 1, /*!< VREFEXT2 : Use external reference 2 for reference input. value. */ + VCOMP_CFG_NSEL_VREFEXT3 = 2, /*!< VREFEXT3 : Use external reference 3 for reference input. value. */ + VCOMP_CFG_NSEL_DAC = 3, /*!< DAC : Use DAC output selected by LVLSEL for reference input. + value. */ +} VCOMP_CFG_NSEL_Enum; + +/* ================================================= VCOMP CFG PSEL [0..1] ================================================= */ +typedef enum { /*!< VCOMP_CFG_PSEL */ + VCOMP_CFG_PSEL_VDDADJ = 0, /*!< VDDADJ : Use VDDADJ for the positive input. value. */ + VCOMP_CFG_PSEL_VTEMP = 1, /*!< VTEMP : Use the temperature sensor output for the positive input. + Note: If this channel is selected for PSEL, the bandap + circuit required for temperature comparisons will automatically + turn on. The bandgap circuit requires 11us to stabalize. + value. */ + VCOMP_CFG_PSEL_VEXT1 = 2, /*!< VEXT1 : Use external voltage 0 for positive input. value. */ + VCOMP_CFG_PSEL_VEXT2 = 3, /*!< VEXT2 : Use external voltage 1 for positive input. value. */ +} VCOMP_CFG_PSEL_Enum; + +/* ========================================================= STAT ========================================================== */ +/* =============================================== VCOMP STAT PWDSTAT [1..1] =============================================== */ +typedef enum { /*!< VCOMP_STAT_PWDSTAT */ + VCOMP_STAT_PWDSTAT_POWERED_DOWN = 1, /*!< POWERED_DOWN : The voltage comparator is powered down. value. */ +} VCOMP_STAT_PWDSTAT_Enum; + +/* =============================================== VCOMP STAT CMPOUT [0..0] ================================================ */ +typedef enum { /*!< VCOMP_STAT_CMPOUT */ + VCOMP_STAT_CMPOUT_VOUT_LOW = 0, /*!< VOUT_LOW : The negative input of the comparator is greater than + the positive input. value. */ + VCOMP_STAT_CMPOUT_VOUT_HIGH = 1, /*!< VOUT_HIGH : The positive input of the comparator is greater + than the negative input. value. */ +} VCOMP_STAT_CMPOUT_Enum; + +/* ======================================================== PWDKEY ========================================================= */ +/* ============================================== VCOMP PWDKEY PWDKEY [0..31] ============================================== */ +typedef enum { /*!< VCOMP_PWDKEY_PWDKEY */ + VCOMP_PWDKEY_PWDKEY_Key = 55, /*!< Key : Key value. */ +} VCOMP_PWDKEY_PWDKEY_Enum; + +/* ========================================================= INTEN ========================================================= */ +/* ======================================================== INTSTAT ======================================================== */ +/* ======================================================== INTCLR ========================================================= */ +/* ======================================================== INTSET ========================================================= */ + + +/* =========================================================================================================================== */ +/* ================ WDT ================ */ +/* =========================================================================================================================== */ + +/* ========================================================== CFG ========================================================== */ +/* ================================================ WDT CFG CLKSEL [24..26] ================================================ */ +typedef enum { /*!< WDT_CFG_CLKSEL */ + WDT_CFG_CLKSEL_OFF = 0, /*!< OFF : Low Power Mode. This setting disables the watch dog timer. + value. */ + WDT_CFG_CLKSEL_128HZ = 1, /*!< 128HZ : 128 Hz LFRC clock. value. */ + WDT_CFG_CLKSEL_16HZ = 2, /*!< 16HZ : 16 Hz LFRC clock. value. */ + WDT_CFG_CLKSEL_1HZ = 3, /*!< 1HZ : 1 Hz LFRC clock. value. */ + WDT_CFG_CLKSEL_1_16HZ = 4, /*!< 1_16HZ : 1/16th Hz LFRC clock. value. */ +} WDT_CFG_CLKSEL_Enum; + +/* ========================================================= RSTRT ========================================================= */ +/* ================================================ WDT RSTRT RSTRT [0..7] ================================================= */ +typedef enum { /*!< WDT_RSTRT_RSTRT */ + WDT_RSTRT_RSTRT_KEYVALUE = 178, /*!< KEYVALUE : This is the key value to write to WDTRSTRT to restart + the WDT. This is a write only register. value. */ +} WDT_RSTRT_RSTRT_Enum; + +/* ========================================================= LOCK ========================================================== */ +/* ================================================= WDT LOCK LOCK [0..7] ================================================== */ +typedef enum { /*!< WDT_LOCK_LOCK */ + WDT_LOCK_LOCK_KEYVALUE = 58, /*!< KEYVALUE : This is the key value to write to WDTLOCK to lock + the WDT. value. */ +} WDT_LOCK_LOCK_Enum; + +/* ========================================================= COUNT ========================================================= */ +/* ========================================================= INTEN ========================================================= */ +/* ======================================================== INTSTAT ======================================================== */ +/* ======================================================== INTCLR ========================================================= */ +/* ======================================================== INTSET ========================================================= */ + +/** @} */ /* End of group EnumValue_peripherals */ + + +#ifdef __cplusplus +} +#endif + +#endif /* APOLLO3_H */ + + +/** @} */ /* End of group apollo3 */ + +/** @} */ /* End of group Ambiq Micro */ diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/captured_data_to_wav.py b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/captured_data_to_wav.py new file mode 100644 index 0000000000..84b6259452 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/captured_data_to_wav.py @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# 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 +# +# 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. + +import numpy as np +import re +import struct +import matplotlib.pyplot as plt +import soundfile as sf + +def new_data_to_array(fn): + vals = [] + with open(fn) as f: + for n, line in enumerate(f): + if n is not 0: + vals.extend([int(v, 16) for v in line.split()]) + b = ''.join(map(chr, vals)) + y = struct.unpack('<'+'h'*int(len(b)/2), b) + + return y + + +data = 'captured_data.txt' +vals = np.array(new_data_to_array(data)).astype(float) + +#plt.plot(vals, 'o-') +#plt.show(block=False) + +wav = vals/np.max(np.abs(vals)) +sf.write('captured_data.wav', wav, 16000) diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/compare_1k.py b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/compare_1k.py new file mode 100644 index 0000000000..9c91560d50 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/compare_1k.py @@ -0,0 +1,153 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# 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 +# +# 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. + +import numpy as np +import re +import struct +import matplotlib.pyplot as plt +import soundfile as sf + +def new_data_to_array(fn, datatype='int16'): + vals = [] + with open(fn) as f: + for n, line in enumerate(f): + if n is not 0: + vals.extend([int(v, 16) for v in line.split()]) + b = ''.join(map(chr, vals)) + + if datatype == 'int8': + typestr = 'b' + arraylen = int(len(b)) + elif datatype == 'int16': + typestr = 'h' + arraylen = int(len(b)//2) + elif datatype == 'int32': + typestr = 'i' + arraylen = int(len(b)//4) + if datatype == 'uint8': + typestr = 'B' + arraylen = int(len(b)) + elif datatype == 'uint16': + typestr = 'H' + arraylen = int(len(b)//2) + elif datatype == 'uint32': + typestr = 'I' + arraylen = int(len(b)//4) + + y = np.array(struct.unpack('<'+typestr*arraylen, b)) + + return y + +# x is the fixed-point input in Qm.n format +def to_float(x, n): + return x.astype(float)*2**(-n) + +micro_windowed_input = new_data_to_array('micro_windowed_input.txt', datatype='int32') +cmsis_windowed_input = new_data_to_array('cmsis_windowed_input.txt', datatype='int16') + +micro_dft = new_data_to_array('micro_dft.txt', datatype='int32') +cmsis_dft = new_data_to_array('cmsis_dft.txt', datatype='int16') +py_dft = np.fft.rfft(to_float(cmsis_windowed_input,15), n=512) +py_result = np.empty((2*py_dft.size), dtype=np.float) +py_result[0::2] = np.real(py_dft) +py_result[1::2] = np.imag(py_dft) + +micro_power = new_data_to_array('micro_power.txt', datatype='int32') +cmsis_power = new_data_to_array('cmsis_power.txt', datatype='int16') +py_power = np.square(np.abs(py_dft)) + +micro_power_avg = new_data_to_array('micro_power_avg.txt', datatype='uint8') +cmsis_power_avg = new_data_to_array('cmsis_power_avg.txt', datatype='uint8') + +plt.figure(1) +plt.subplot(311) +plt.plot(micro_windowed_input, label='Micro fixed') +plt.legend() +plt.subplot(312) +plt.plot(cmsis_windowed_input, label='CMSIS fixed') +plt.legend() +plt.subplot(313) +plt.plot(to_float(micro_windowed_input, 30), label='Micro to float') +plt.plot(to_float(cmsis_windowed_input, 15), label='CMSIS to float') +plt.legend() + +plt.figure(2) +plt.subplot(311) +plt.plot(micro_dft, label='Micro fixed') +plt.legend() +plt.subplot(312) +plt.plot(cmsis_dft, label='CMSIS fixed') +plt.legend() +plt.subplot(313) +plt.plot(to_float(micro_dft, 22), label='Micro to float') +# CMSIS result has 6 fractionanl bits (not 7) due to documentation error (see README.md) +plt.plot(to_float(cmsis_dft, 6), label='CMSIS to float') +plt.plot(py_result, label='Python result') +plt.legend() + +plt.figure(3) +plt.subplot(311) +plt.plot(micro_power, label='Micro fixed') +plt.legend() +plt.subplot(312) +plt.plot(cmsis_power[0:256], label='CMSIS fixed') +plt.legend() +plt.subplot(313) +plt.plot(to_float(micro_power, 22), label='Micro to float') +plt.plot(to_float(cmsis_power[0:256], 6), label='CMSIS to float') +plt.plot(py_power, label='Python result') +plt.legend() + +plt.figure(4) +plt.plot(micro_power_avg, label='Micro fixed') +plt.plot(cmsis_power_avg, label='CMSIS fixed') +plt.legend() +plt.show() + +#t = np.arange(16000.*0.03)/16000. +#sin1k = 0.1*np.sin(2*np.pi*1000*t) # Factor of 10 because micro preprocessing overflows otherwise +# +#plt.figure(1) +#plt.subplot(511) +#plt.plot(sin1k) +#plt.title('Input sine') +# +#plt.subplot(512) +#plt.plot(to_float(micro_windowed_input, 30), label='Micro-Lite') +#plt.plot(to_float(cmsis_windowed_input, 15), label='CMSIS') +#plt.title('Windowed sine') +#plt.legend(loc='center right') +# +#plt.subplot(513) +#plt.plot(to_float(micro_dft, 22), label='Micro-Lite') +#plt.plot(to_float(cmsis_dft, 6), label='CMSIS') +#plt.title('FFT') +#plt.legend(loc='center') +# +#plt.subplot(514) +#plt.plot(to_float(micro_power, 22), label='Micro-Lite') +#plt.plot(to_float(cmsis_power[0:256], 6), label='CMSIS') +#plt.title('|FFT|^2') +#plt.legend(loc='center right') +# +#plt.subplot(515) +#plt.plot(micro_power_avg, label='Micro-Lite') +#plt.plot(cmsis_power_avg, label='CMSIS') +#plt.title('Averaged |FFT|^2') +#plt.legend(loc='center right') +# +#plt.tight_layout(pad=0, w_pad=0.2, h_pad=0.2) +# +#plt.show() +# diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k.cc new file mode 100644 index 0000000000..31fae1f2dc --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k.cc @@ -0,0 +1,57 @@ +/* This file is a modification of the Tensorflow Micro Lite file preprocessor.cc + * We have retained the original copyright and header information, in + * accordance with the Apache 2.0 license terms. + */ + +/* Copyright 2018 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/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.h" +#include "tensorflow/lite/c/c_api_internal.h" +#include "tensorflow/lite/experimental/micro/micro_error_reporter.h" +#include "tensorflow/lite/experimental/micro/testing/micro_test.h" + +extern "C" { +#include "tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/system_apollo3.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/apollo3.h" +} + +#define output_data_size 43 +int count; + +extern TfLiteStatus Preprocess(tflite::ErrorReporter* error_reporter, + const int16_t* input, int input_size, int output_size, + uint8_t* output); + +TF_LITE_MICRO_TESTS_BEGIN +CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; +//DWT->LAR = 0xC5ACCE55; +DWT->CYCCNT = 0; +DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; + +TF_LITE_MICRO_TEST(TestPreprocessor) { + tflite::MicroErrorReporter micro_error_reporter; + tflite::ErrorReporter* error_reporter = µ_error_reporter; + + uint8_t calculated_data[output_data_size]; + TfLiteStatus yes_status = Preprocess( + error_reporter, g_sin_1k, g_sin_1k_size, + output_data_size, calculated_data); + count = DWT->CYCCNT; + TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, yes_status); + +} + +TF_LITE_MICRO_TESTS_END diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k_cmsis_test.cmd b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k_cmsis_test.cmd new file mode 100644 index 0000000000..0f701660a8 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k_cmsis_test.cmd @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# 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 +# +# 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. + +# Needs to be compiled with -O0 +file ../../../tools/make/gen/apollo3evb_cortex-m4/bin/preprocessor_1k_cmsis_test +target remote localhost:2331 +load ../../../tools/make/gen/apollo3evb_cortex-m4/bin/preprocessor_1k_cmsis_test +monitor reset +break preprocessor.cc:70 +commands +dump verilog value cmsis_windowed_input.txt bufB +c +end +break preprocessor.cc:77 +commands +dump verilog value cmsis_dft.txt bufA +c +end +break preprocessor.cc:82 +commands +dump verilog value cmsis_power.txt bufB +c +end +break preprocessor.cc:84 +commands +dump verilog memory cmsis_power_avg.txt output output+42 +c +end +break preprocessor_1k.cc:53 +commands +print count +end +c diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k_micro_test.cmd b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k_micro_test.cmd new file mode 100644 index 0000000000..35c602d78c --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k_micro_test.cmd @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# 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 +# +# 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. + +# Needs to be run when compiled with -O0 +file ../../../tools/make/gen/apollo3evb_cortex-m4/bin/preprocessor_1k_micro_test +target remote localhost:2331 +load ../../../tools/make/gen/apollo3evb_cortex-m4/bin/preprocessor_1k_micro_test +monitor reset +break preprocessor.cc:211 +commands +dump verilog value micro_windowed_input.txt fixed_input +dump verilog value micro_dft.txt fourier_values +dump verilog value micro_power.txt power_spectrum +dump verilog memory micro_power_avg.txt output output+42 +c +end +break preprocessor_1k.cc:53 +commands +print count +end +c diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_test.cmd b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_test.cmd new file mode 100644 index 0000000000..4458af17d6 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_test.cmd @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# 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 +# +# 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. + +file ../../gen/apollo3evb_cortex-m4/bin/preprocessor_test +target remote localhost:2331 +load ../../gen/apollo3evb_cortex-m4/bin/preprocessor_test +monitor reset diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_cmsis_scores.cmd b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_cmsis_scores.cmd new file mode 100644 index 0000000000..db299f7277 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_cmsis_scores.cmd @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# 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 +# +# 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. + +file ../../../tools/make/gen/apollo3evb_cortex-m4/bin/pushbutton_cmsis_speech_test +target remote localhost:2331 +load ../../../tools/make/gen/apollo3evb_cortex-m4/bin/pushbutton_cmsis_speech_test +monitor reset +break pushbutton_main.c:325 +commands +printf "Silence score: %d\n", g_silence_score +printf "Unknown score: %d\n", g_unknown_score +printf "Yes score: %d\n", g_yes_score +printf "No score: %d\n", g_no_score +printf "g_scores[0]: %d\n", g_scores[0] +printf "g_scores[1]: %d\n", g_scores[1] +printf "g_scores[2]: %d\n", g_scores[2] +printf "g_scores[3]: %d\n", g_scores[3] +printf "max_score: %d\n", max_score +printf "max_score_index: %d\n", max_score_index +c +end +c diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_cmsis_voice.cmd b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_cmsis_voice.cmd new file mode 100644 index 0000000000..8cd1a7e90e --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_cmsis_voice.cmd @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# 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 +# +# 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. + +file ../../../tools/make/gen/apollo3evb_cortex-m4/bin/pushbutton_cmsis_speech_test +target remote localhost:2331 +load ../../../tools/make/gen/apollo3evb_cortex-m4/bin/pushbutton_cmsis_speech_test +monitor reset +break pushbutton_main.c:316 +commands +dump verilog value captured_data.txt captured_data +c +end +c diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_main.c b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_main.c new file mode 100644 index 0000000000..82337e63da --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_main.c @@ -0,0 +1,339 @@ +/* This file is a modification of the Tensorflow Micro Lite file _main.c + * We have retained the original copyright and header information, in + * accordance with the Apache 2.0 license terms. + */ + +/* Copyright 2018 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 +#include "am_mcu_apollo.h" // Defines AM_CMSIS_REGS +#include "am_bsp.h" +#include "am_util.h" + +#define ARM_MATH_CM4 +#include + +//***************************************************************************** +// Parameters +// +// Total number of bytes transferred = 320*50*2 = 32000 +//***************************************************************************** + +#define FRAME_SIZE 320 // Capture one 320-sample (20-ms) frame at a time +#define NUM_FRAMES 50 // Number of frames in 1 second + +//***************************************************************************** +// GLOBALS +//***************************************************************************** + +volatile int16_t g_numFramesCaptured = 0; +volatile bool g_bPDMDataReady = false; +int16_t captured_data[FRAME_SIZE*NUM_FRAMES]; // Location of 1-second data buffer +extern uint8_t g_silence_score; +extern uint8_t g_unknown_score; +extern uint8_t g_yes_score; +extern uint8_t g_no_score; +q7_t g_scores[4] = {0}; + + +//***************************************************************************** +// The entry point for the application. +//***************************************************************************** +extern int main(int argc, char**argv); + +void DebugLog(const char* s) { am_util_stdio_printf( "%s", s); } +void DebugLogInt32(int32_t i) { am_util_stdio_printf( "%d", i); } +void DebugLogUInt32(uint32_t i) { am_util_stdio_printf( "%d", i); } +void DebugLogHex(uint32_t i) { am_util_stdio_printf( "0x%8x", i); } +void DebugLogFloat(float i) { am_util_stdio_printf( "%f", i); } + +//***************************************************************************** +// PDM configuration information. +//***************************************************************************** +void *PDMHandle; + +am_hal_pdm_config_t g_sPdmConfig = +{ + .eClkDivider = AM_HAL_PDM_MCLKDIV_1, + .eLeftGain = AM_HAL_PDM_GAIN_P225DB, + .eRightGain = AM_HAL_PDM_GAIN_P225DB, + .ui32DecimationRate = 48, // OSR = 1500/16 = 96 = 2*SINCRATE --> SINC_RATE = 48 + .bHighPassEnable = 0, + .ui32HighPassCutoff = 0xB, + .ePDMClkSpeed = AM_HAL_PDM_CLK_1_5MHZ, + .bInvertI2SBCLK = 0, + .ePDMClkSource = AM_HAL_PDM_INTERNAL_CLK, + .bPDMSampleDelay = 0, + .bDataPacking = 1, + .ePCMChannels = AM_HAL_PDM_CHANNEL_RIGHT, + .bLRSwap = 0, +}; + + +//***************************************************************************** +// BUTTON0 pin configuration settings. +//***************************************************************************** +const am_hal_gpio_pincfg_t g_deepsleep_button0 = +{ + .uFuncSel = 3, + .eIntDir = AM_HAL_GPIO_PIN_INTDIR_LO2HI, + .eGPInput = AM_HAL_GPIO_PIN_INPUT_ENABLE, +}; + +//***************************************************************************** +// PDM initialization. +//***************************************************************************** +void pdm_init(void) +{ + // + // Initialize, power-up, and configure the PDM. + // + am_hal_pdm_initialize(0, &PDMHandle); + am_hal_pdm_power_control(PDMHandle, AM_HAL_PDM_POWER_ON, false); + am_hal_pdm_configure(PDMHandle, &g_sPdmConfig); + am_hal_pdm_enable(PDMHandle); + + // + // Configure the necessary pins. + // + am_hal_gpio_pincfg_t sPinCfg = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +// ARPIT 181019 + //sPinCfg.uFuncSel = AM_HAL_PIN_10_PDMCLK; + //am_hal_gpio_pinconfig(10, sPinCfg); + sPinCfg.uFuncSel = AM_HAL_PIN_12_PDMCLK; + am_hal_gpio_pinconfig(12, sPinCfg); + + sPinCfg.uFuncSel = AM_HAL_PIN_11_PDMDATA; + am_hal_gpio_pinconfig(11, sPinCfg); + + //am_hal_gpio_state_write(14, AM_HAL_GPIO_OUTPUT_CLEAR); + //am_hal_gpio_pinconfig(14, g_AM_HAL_GPIO_OUTPUT); + + // + // Configure and enable PDM interrupts (set up to trigger on DMA + // completion). + // + am_hal_pdm_interrupt_enable(PDMHandle, (AM_HAL_PDM_INT_DERR + | AM_HAL_PDM_INT_DCMP + | AM_HAL_PDM_INT_UNDFL + | AM_HAL_PDM_INT_OVF)); + +#if AM_CMSIS_REGS + NVIC_EnableIRQ(PDM_IRQn); +#else + am_hal_interrupt_enable(AM_HAL_INTERRUPT_PDM); +#endif +} + +//***************************************************************************** +// +// Start a transaction to get some number of bytes from the PDM interface. +// +//***************************************************************************** +void pdm_data_get(void) +{ + // + // Configure DMA and target address. + // + am_hal_pdm_transfer_t sTransfer; + sTransfer.ui32TargetAddr = (uint32_t ) (&captured_data[FRAME_SIZE*g_numFramesCaptured]); + sTransfer.ui32TotalCount = 2*FRAME_SIZE; // Each sample is 2 bytes + + // + // Start the data transfer. + // + am_hal_pdm_dma_start(PDMHandle, &sTransfer); +} + + +//***************************************************************************** +// +// PDM interrupt handler. +// +//***************************************************************************** +void am_pdm_isr(void) +{ + uint32_t ui32Status; + // + // Read the interrupt status. + // + am_hal_pdm_interrupt_status_get(PDMHandle, &ui32Status, true); + am_hal_pdm_interrupt_clear(PDMHandle, ui32Status); + + // + // Once our DMA transaction completes, send a flag to the main routine + // + if (ui32Status & AM_HAL_PDM_INT_DCMP) + g_bPDMDataReady = true; +} + + +//***************************************************************************** +// GPIO ISR +// Will enable the PDM, set number of frames transferred to 0, and turn on LED +//***************************************************************************** +void +am_gpio_isr(void) +{ + // + // Delay for debounce. + // + am_util_delay_ms(200); + + // + // Clear the GPIO Interrupt (write to clear). + // + am_hal_gpio_interrupt_clear(AM_HAL_GPIO_BIT(AM_BSP_GPIO_BUTTON0)); + + // Start audio transfer + am_hal_pdm_fifo_flush(PDMHandle); + pdm_data_get(); + am_hal_pdm_enable(PDMHandle); + + // + // Turn on LED 0 + // + am_devices_led_on(am_bsp_psLEDs, 0); +} + +int _main(void) +{ + am_util_id_t sIdDevice; + uint32_t ui32StrBuf; + + // + // Set the clock frequency. + // + am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_SYSCLK_MAX, 0); + + // + // Set the default cache configuration + // + am_hal_cachectrl_config(&am_hal_cachectrl_defaults); + am_hal_cachectrl_enable(); + + // + // Configure the board for low power operation. + // + am_bsp_low_power_init(); + + +#if defined(AM_BSP_NUM_BUTTONS) && defined(AM_BSP_NUM_LEDS) + // + // Configure the button pin. + // + am_hal_gpio_pinconfig(AM_BSP_GPIO_BUTTON0, g_deepsleep_button0); + + // + // Clear the GPIO Interrupt (write to clear). + // + am_hal_gpio_interrupt_clear(AM_HAL_GPIO_BIT(AM_BSP_GPIO_BUTTON0)); + + // + // Enable the GPIO/button interrupt. + // + am_hal_gpio_interrupt_enable(AM_HAL_GPIO_BIT(AM_BSP_GPIO_BUTTON0)); + + // + // Configure the LEDs. + // + am_devices_led_array_init(am_bsp_psLEDs, AM_BSP_NUM_LEDS); + + // + // Turn the LEDs off + // + for (int ix = 0; ix < AM_BSP_NUM_LEDS; ix++) + { + am_devices_led_off(am_bsp_psLEDs, ix); + } + +// am_devices_led_on(am_bsp_psLEDs, 1); +#endif // defined(AM_BSP_NUM_BUTTONS) && defined(AM_BSP_NUM_LEDS) + +#if AM_CMSIS_REGS + NVIC_EnableIRQ(GPIO_IRQn); +#else // AM_CMSIS_REGS + am_hal_interrupt_enable(AM_HAL_INTERRUPT_GPIO); +#endif // AM_CMSIS_REGS + + // + // Enable interrupts to the core. + // + am_hal_interrupt_master_enable(); + + // Turn on PDM + pdm_init(); + + // + // Initialize the printf interface for UART output + // + am_bsp_uart_printf_enable(); + + // + // Print the banner. + // + am_util_stdio_terminal_clear(); + am_util_stdio_printf("Starting streaming test\n\n"); + + // Score variables + q7_t max_score = 0; + uint32_t max_score_index = 0; + + while(1) + { + + am_hal_interrupt_master_disable(); + + if (g_bPDMDataReady) + { + g_bPDMDataReady = false; + g_numFramesCaptured++; + + if (g_numFramesCaptured < NUM_FRAMES) { + pdm_data_get(); // Start converting the next set of PCM samples. + } + + else + { + g_numFramesCaptured = 0; + //am_hal_pdm_disable(PDMHandle); + am_devices_led_off(am_bsp_psLEDs, 0); + + main(0, NULL); + + g_scores[0] = (q7_t) g_silence_score - 128; + g_scores[1] = (q7_t) g_unknown_score - 128; + g_scores[2] = (q7_t) g_yes_score - 128; + g_scores[3] = (q7_t) g_no_score - 128; + + am_devices_led_off(am_bsp_psLEDs, max_score_index+1); // Turn off LED for previous max score + arm_max_q7(g_scores, 4, &max_score, &max_score_index); + am_devices_led_on(am_bsp_psLEDs, max_score_index+1); // Turn on LED for new max score + } + } + + // + // Go to Deep Sleep. + // + am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP); + + am_hal_interrupt_master_enable(); + + } + + //main(0, NULL); +} diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_test.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_test.cc new file mode 100644 index 0000000000..ce4de4dbd8 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_test.cc @@ -0,0 +1,120 @@ +/* This file is a modification of the Tensorflow Micro Lite file micro_speech_test.cc + * We have retained the original copyright and header information, in + * accordance with the Apache 2.0 license terms. + */ + +/* Copyright 2018 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/lite/experimental/micro/examples/micro_speech/preprocessor.h" +#include "tensorflow/lite/c/c_api_internal.h" +#include "tensorflow/lite/experimental/micro/micro_error_reporter.h" +#include "tensorflow/lite/experimental/micro/testing/micro_test.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/tiny_conv_model_data.h" +#include "tensorflow/lite/experimental/micro/kernels/all_ops_resolver.h" +#include "tensorflow/lite/experimental/micro/micro_interpreter.h" +#include "tensorflow/lite/schema/schema_generated.h" +#include "tensorflow/lite/version.h" + +extern int16_t captured_data[16000]; +uint8_t g_silence_score = 0; +uint8_t g_unknown_score = 0; +uint8_t g_yes_score = 0; +uint8_t g_no_score = 0; + +TF_LITE_MICRO_TESTS_BEGIN + +TF_LITE_MICRO_TEST(TestPreprocessor) { + tflite::MicroErrorReporter micro_error_reporter; + tflite::ErrorReporter* error_reporter = µ_error_reporter; + + uint8_t preprocessed_data[43*49]; + TfLiteStatus preprocess_1sec_status = Preprocess_1sec( + error_reporter, captured_data, preprocessed_data); + TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, preprocess_1sec_status); + + + // Map the model into a usable data structure. This doesn't involve any + // copying or parsing, it's a very lightweight operation. + const tflite::Model* model = ::tflite::GetModel(g_tiny_conv_model_data); + if (model->version() != TFLITE_SCHEMA_VERSION) { + error_reporter->Report( + "Model provided is schema version %d not equal " + "to supported version %d.\n", + model->version(), TFLITE_SCHEMA_VERSION); + } + + // This pulls in all the operation implementations we need. + tflite::ops::micro::AllOpsResolver resolver; + + // Create an area of memory to use for input, output, and intermediate arrays. + const int tensor_arena_size = 10 * 1024; + uint8_t tensor_arena[tensor_arena_size]; + tflite::SimpleTensorAllocator tensor_allocator(tensor_arena, + tensor_arena_size); + + // Build an interpreter to run the model with. + tflite::MicroInterpreter interpreter(model, resolver, &tensor_allocator, + error_reporter); + + // Get information about the memory area to use for the model's input. + TfLiteTensor* input = interpreter.input(0); + + // Make sure the input has the properties we expect. + TF_LITE_MICRO_EXPECT_NE(nullptr, input); + TF_LITE_MICRO_EXPECT_EQ(4, input->dims->size); + TF_LITE_MICRO_EXPECT_EQ(1, input->dims->data[0]); + TF_LITE_MICRO_EXPECT_EQ(49, input->dims->data[1]); + TF_LITE_MICRO_EXPECT_EQ(43, input->dims->data[2]); + TF_LITE_MICRO_EXPECT_EQ(kTfLiteUInt8, input->type); + + // Copy a spectrogram created from a .wav audio file of someone saying "Yes", + // into the memory area used for the input. + for (int i = 0; i < input->bytes; ++i) { + input->data.uint8[i] = preprocessed_data[i]; + } + + // Run the model on this input and make sure it succeeds. + TfLiteStatus invoke_status = interpreter.Invoke(); + if (invoke_status != kTfLiteOk) { + error_reporter->Report("Invoke failed\n"); + } + TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, invoke_status); + + // Get the output from the model, and make sure it's the expected size and + // type. + TfLiteTensor* output = interpreter.output(0); + TF_LITE_MICRO_EXPECT_EQ(2, output->dims->size); + TF_LITE_MICRO_EXPECT_EQ(1, output->dims->data[0]); + TF_LITE_MICRO_EXPECT_EQ(4, output->dims->data[1]); + TF_LITE_MICRO_EXPECT_EQ(kTfLiteUInt8, output->type); + + // There are four possible classes in the output, each with a score. + const int kSilenceIndex = 0; + const int kUnknownIndex = 1; + const int kYesIndex = 2; + const int kNoIndex = 3; + + // Make sure that the expected "Yes" score is higher than the other classes. + g_silence_score = output->data.uint8[kSilenceIndex]; + g_unknown_score = output->data.uint8[kUnknownIndex]; + g_yes_score = output->data.uint8[kYesIndex]; + g_no_score = output->data.uint8[kNoIndex]; + + error_reporter->Report("Ran successfully\n"); + +} + +TF_LITE_MICRO_TESTS_END diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/system_apollo3.c b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/system_apollo3.c new file mode 100755 index 0000000000..c7f1573563 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/system_apollo3.c @@ -0,0 +1,116 @@ +//***************************************************************************** +// +//! @file system_apollo3.c +//! +//! @brief Ambiq Micro Apollo3 MCU specific functions. +// +//***************************************************************************** + +/* + * Copyright (C) 2015-2017, Ambiq Micro + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of itscontributors may be used to endorse + * or promote products derived from thissoftware without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @file apollo3.h + * @brief CMSIS HeaderFile + * @version 1.0 + * @date 10. August 2018 + * @note Generated by SVDConv V3.3.18 on Friday, 10.08.2018 16:52:09 + * from File 'apollo3.svd', + * last modified on Friday, 10.08.2018 20:01:31 + */ + + +#include +#include "system_apollo3.h" +#include "apollo3.h" + +//***************************************************************************** +// +// Defines +// +//***************************************************************************** + +// +// Clocks +// +#define __HSI (6000000UL) +#define __XTAL (32768UL) // Crystal Oscillator frequency +#define __SYS_OSC_CLK (48000000) // Main oscillator frequency +#define __SYSTEM_CLOCK (1*__SYS_OSC_CLK) + +// +// Initialize SystemCoreClock with the system core clock frequency value +// achieved after system intitialization. +// This means system core clock frequency after call to SystemInit() +// +uint32_t SystemCoreClock = __SYSTEM_CLOCK; // System Clock Frequency (Core Clock) + +//***************************************************************************** +// +//! @brief Set the global clock frequncy. +//! +//! This function sets the global clock frequency. +//! +//! @return None. +// +//***************************************************************************** +void +SystemCoreClockUpdate(void) +{ + // + // Calculate the system frequency based upon the current register settings. + // This function can be used to retrieve the system core clock frequeny + // after user changed register sittings. + // + SystemCoreClock = __SYS_OSC_CLK / (CLKGEN->CCTRL_b.CORESEL + 1); +} + +//***************************************************************************** +// +//! @brief Initialize the system. +//! +//! This function sets up the microcontroller system. +//! +//! @return None. +// +//***************************************************************************** +void +SystemInit(void) +{ + // + // Initialize the system + // Do not use global variables because this function is called before + // reaching pre-main. RW section maybe overwritten afterwards. + // + SystemCoreClock = __SYSTEM_CLOCK; + + CLKGEN->CLKKEY = 0x47; // Enable write to CCTRL + CLKGEN->CCTRL_b.CORESEL = 0; // Div by 1 for 48MHz + CLKGEN->CLKKEY = 0; // Disable write to CCTRL +} + diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/system_apollo3.h b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/system_apollo3.h new file mode 100755 index 0000000000..7fd9b51d5a --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/system_apollo3.h @@ -0,0 +1,72 @@ +//***************************************************************************** +// +//! @file system_Apollo3.h +//! +//! @brief Ambiq Micro Apollo3 MCU specific functions. +// +//***************************************************************************** + +/* + * Copyright (C) 2015-2017, Ambiq Micro + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of itscontributors may be used to endorse + * or promote products derived from thissoftware without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @file apollo3.h + * @brief CMSIS HeaderFile + * @version 1.0 + * @date 10. August 2018 + * @note Generated by SVDConv V3.3.18 on Friday, 10.08.2018 16:52:09 + * from File 'apollo3.svd', + * last modified on Friday, 10.08.2018 20:01:31 + */ + + +#ifndef SYSTEM_APOLLO3_H +#define SYSTEM_APOLLO3_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +extern uint32_t SystemCoreClock; // System Clock Frequency (Core Clock) + +//***************************************************************************** +// +// External function definitions +// +//***************************************************************************** +extern void SystemInit (void); +extern void SystemCoreClockUpdate (void); + +#ifdef __cplusplus +} +#endif + +#endif // SYSTEM_APOLLO3_H + diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.cc new file mode 100644 index 0000000000..c0365d5690 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.cc @@ -0,0 +1,33 @@ +/* Copyright 2018 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/lite/experimental/micro/examples/micro_speech/audio_provider.h" + +#include "tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h" + +namespace { +int16_t g_dummy_audio_data[kMaxAudioSampleSize]; +} // namespace + +TfLiteStatus GetAudioSamples(tflite::ErrorReporter* error_reporter, + int start_ms, int duration_ms, + int* audio_samples_size, int16_t** audio_samples) { + for (int i = 0; i < kMaxAudioSampleSize; ++i) { + g_dummy_audio_data[i] = 0; + } + *audio_samples_size = kMaxAudioSampleSize; + *audio_samples = g_dummy_audio_data; + return kTfLiteOk; +} diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.h b/tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.h new file mode 100644 index 0000000000..7e2442a5e8 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.h @@ -0,0 +1,36 @@ +/* Copyright 2018 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_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_AUDIO_PROVIDER_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_AUDIO_PROVIDER_H_ + +#include "tensorflow/lite/c/c_api_internal.h" +#include "tensorflow/lite/experimental/micro/micro_error_reporter.h" + +// This is an abstraction around an audio source like a microphone, and is +// expected to return 16-bit PCM sample data for a given point in time. The +// sample data itself should be used as quickly as possible by the caller, since +// to allow memory optimizations there are no guarantees that the samples won't +// be overwritten by new data in the future. In practice, implementations should +// ensure that there's a reasonable time allowed for clients to access the data +// before any reuse. +// The reference implementation can have no platform-specific dependencies, so +// it just returns an array filled with zeros. For real applications, you should +// ensure there's a specialized implementation that accesses hardware APIs. +TfLiteStatus GetAudioSamples(tflite::ErrorReporter* error_reporter, + int start_ms, int duration_ms, + int* audio_samples_size, int16_t** audio_samples); + +#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_AUDIO_PROVIDER_H_ diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider_test.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider_test.cc new file mode 100644 index 0000000000..5f7c7605f0 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider_test.cc @@ -0,0 +1,44 @@ +/* Copyright 2018 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/lite/experimental/micro/examples/micro_speech/audio_provider.h" +#include "tensorflow/lite/c/c_api_internal.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h" +#include "tensorflow/lite/experimental/micro/micro_error_reporter.h" +#include "tensorflow/lite/experimental/micro/testing/micro_test.h" + +TF_LITE_MICRO_TESTS_BEGIN + +TF_LITE_MICRO_TEST(TestAudioProvider) { + tflite::MicroErrorReporter micro_error_reporter; + tflite::ErrorReporter* error_reporter = µ_error_reporter; + + int audio_samples_size = 0; + int16_t* audio_samples = nullptr; + TfLiteStatus get_status = + GetAudioSamples(error_reporter, 0, kFeatureSliceDurationMs, + &audio_samples_size, &audio_samples); + TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, get_status); + TF_LITE_MICRO_EXPECT_LE(audio_samples_size, kMaxAudioSampleSize); + TF_LITE_MICRO_EXPECT_NE(audio_samples, nullptr); + + // Make sure we can read all of the returned memory locations. + int total = 0; + for (int i = 0; i < audio_samples_size; ++i) { + total += audio_samples[i]; + } +} + +TF_LITE_MICRO_TESTS_END diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider.cc new file mode 100644 index 0000000000..c4c52ac0ff --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider.cc @@ -0,0 +1,121 @@ +/* Copyright 2018 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/lite/experimental/micro/examples/micro_speech/feature_provider.h" + +#include "tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/timer.h" + +namespace { +// Stores the timestamp for the previous fetch of audio data, so that we can +// avoid recalculating all the features from scratch if some earlier timeslices +// are still present. +int32_t g_last_time_in_ms = 0; +// Make sure we don't try to use cached information if this is the first call +// into the provider. +bool g_is_first_run = true; +} // namespace + +FeatureProvider::FeatureProvider(int feature_size, uint8_t* feature_data) + : feature_size_(feature_size), feature_data_(feature_data) { + // Initialize the feature data to default values. + for (int n = 0; n < feature_size_; ++n) { + feature_data_[n] = 0; + } +} + +FeatureProvider::~FeatureProvider() {} + +TfLiteStatus FeatureProvider::PopulateFeatureData( + tflite::ErrorReporter* error_reporter, int* how_many_new_slices) { + if (feature_size_ != kFeatureElementCount) { + error_reporter->Report("Requested feature_data_ size %d doesn't match %d", + feature_size_, kFeatureElementCount); + return kTfLiteError; + } + + const int32_t time_in_ms = TimeInMilliseconds(); + // Quantize the time into steps as long as each window stride, so we can + // figure out which audio data we need to fetch. + const int last_step = (g_last_time_in_ms / kFeatureSliceStrideMs); + const int current_step = (time_in_ms / kFeatureSliceStrideMs); + g_last_time_in_ms = time_in_ms; + + int slices_needed = current_step - last_step; + // If this is the first call, make sure we don't use any cached information. + if (g_is_first_run) { + g_is_first_run = false; + slices_needed = kFeatureSliceCount; + } + if (slices_needed > kFeatureSliceCount) { + slices_needed = kFeatureSliceCount; + } + *how_many_new_slices = slices_needed; + + const int slices_to_keep = kFeatureSliceCount - slices_needed; + const int slices_to_drop = kFeatureSliceCount - slices_to_keep; + // If we can avoid recalculating some slices, just move the existing data + // up in the spectrogram, to perform something like this: + // last time = 80ms current time = 120ms + // +-----------+ +-----------+ + // | data@20ms | --> | data@60ms | + // +-----------+ -- +-----------+ + // | data@40ms | -- --> | data@80ms | + // +-----------+ -- -- +-----------+ + // | data@60ms | -- -- | | + // +-----------+ -- +-----------+ + // | data@80ms | -- | | + // +-----------+ +-----------+ + if (slices_to_keep > 0) { + for (int dest_slice = 0; dest_slice < slices_to_keep; ++dest_slice) { + uint8_t* dest_slice_data = + feature_data_ + (dest_slice * kFeatureSliceSize); + const int src_slice = dest_slice + slices_to_drop; + const uint8_t* src_slice_data = + feature_data_ + (src_slice * kFeatureSliceSize); + for (int i = 0; i < kFeatureSliceSize; ++i) { + dest_slice_data[i] = src_slice_data[i]; + } + } + } + // Any slices that need to be filled in with feature data have their + // appropriate audio data pulled, and features calculated for that slice. + if (slices_needed > 0) { + for (int new_slice = slices_to_keep; new_slice < kFeatureSliceCount; + ++new_slice) { + const int new_step = (current_step - kFeatureSliceCount + 1) + new_slice; + const int32_t slice_start_ms = (new_step * kFeatureSliceStrideMs); + int16_t* audio_samples = nullptr; + int audio_samples_size = 0; + GetAudioSamples(error_reporter, slice_start_ms, kFeatureSliceDurationMs, + &audio_samples_size, &audio_samples); + if (audio_samples_size < kMaxAudioSampleSize) { + error_reporter->Report("Audio data size %d too small, want %d", + audio_samples_size, kMaxAudioSampleSize); + return kTfLiteError; + } + uint8_t* new_slice_data = feature_data_ + (new_slice * kFeatureSliceSize); + TfLiteStatus preprocess_status = + Preprocess(error_reporter, audio_samples, audio_samples_size, + kFeatureSliceSize, new_slice_data); + if (preprocess_status != kTfLiteOk) { + return preprocess_status; + } + } + } + return kTfLiteOk; +} diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider.h b/tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider.h new file mode 100644 index 0000000000..a86c56ebf0 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider.h @@ -0,0 +1,48 @@ +/* Copyright 2018 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_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_FEATURE_PROVIDER_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_FEATURE_PROVIDER_H_ + +#include "tensorflow/lite/c/c_api_internal.h" +#include "tensorflow/lite/experimental/micro/micro_error_reporter.h" + +// Binds itself to an area of memory intended to hold the input features for an +// audio-recognition neural network model, and fills that data area with the +// features representing the current audio input, for example from a microphone. +// The audio features themselves are a two-dimensional array, made up of +// horizontal slices representing the frequencies at one point in time, stacked +// on top of each other to form a spectrogram showing how those frequencies +// changed over time. +class FeatureProvider { + public: + // Create the provider, and bind it to an area of memory. This memory should + // remain accessible for the lifetime of the provider object, since subsequent + // calls will fill it with feature data. The provider does no memory + // management of this data. + FeatureProvider(int feature_size, uint8_t* feature_data); + ~FeatureProvider(); + + // Fills the feature data with information from audio inputs, and returns how + // many feature slices were updated. + TfLiteStatus PopulateFeatureData(tflite::ErrorReporter* error_reporter, + int* how_many_new_slices); + + private: + int feature_size_; + uint8_t* feature_data_; +}; + +#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_FEATURE_PROVIDER_H_ diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider_test.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider_test.cc new file mode 100644 index 0000000000..1e52aec8d2 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider_test.cc @@ -0,0 +1,38 @@ +/* Copyright 2018 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/lite/experimental/micro/examples/micro_speech/feature_provider.h" +#include "tensorflow/lite/c/c_api_internal.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h" +#include "tensorflow/lite/experimental/micro/micro_error_reporter.h" +#include "tensorflow/lite/experimental/micro/testing/micro_test.h" + +TF_LITE_MICRO_TESTS_BEGIN + +TF_LITE_MICRO_TEST(TestFeatureProvider) { + tflite::MicroErrorReporter micro_error_reporter; + tflite::ErrorReporter* error_reporter = µ_error_reporter; + + uint8_t feature_data[kFeatureElementCount]; + FeatureProvider feature_provider(kFeatureElementCount, feature_data); + + int how_many_new_slices = 0; + TfLiteStatus populate_status = feature_provider.PopulateFeatureData( + error_reporter, &how_many_new_slices); + TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, populate_status); + TF_LITE_MICRO_EXPECT_EQ(kFeatureSliceCount, how_many_new_slices); +} + +TF_LITE_MICRO_TESTS_END diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/fixed_point/preprocessor.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/fixed_point/preprocessor.cc index de60c982f3..b623d8d11b 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/fixed_point/preprocessor.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/fixed_point/preprocessor.cc @@ -31,6 +31,8 @@ limitations under the License. #include +#include "tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h" + namespace { // q format notation: qx.y => 1 sign bit, x-1 integer bits, y fraction bits. @@ -66,13 +68,6 @@ inline int32_t FloatToFixed_Q2_30(float input) { return static_cast(roundf(input * (1 << 30))); } -// These constants allow us to allocate fixed-sized arrays on the stack for our -// working memory. -constexpr int kInputSize = 512; -constexpr int kAverageWindowSize = 6; -constexpr int kOutputSize = - ((kInputSize / 2) + (kAverageWindowSize - 1)) / kAverageWindowSize; - // Performs a discrete Fourier transform on the real inputs. This corresponds to // rdft() in the FFT package at http://www.kurims.kyoto-u.ac.jp/~ooura/fft.html, // and to kiss_fftr() in KISSFFT at https://github.com/mborgerding/kissfft. @@ -127,14 +122,14 @@ TfLiteStatus Preprocess(tflite::ErrorReporter* error_reporter, const int16_t* input, int input_size, int output_size, uint8_t* output) { // Ensure our input and output data arrays are valid. - if (input_size > kInputSize) { + if (input_size > kMaxAudioSampleSize) { error_reporter->Report("Input size %d larger than %d", input_size, - kInputSize); + kMaxAudioSampleSize); return kTfLiteError; } - if (output_size != kOutputSize) { + if (output_size != kFeatureSliceSize) { error_reporter->Report("Requested output size %d doesn't match %d", - output_size, kOutputSize); + output_size, kFeatureSliceSize); return kTfLiteError; } @@ -142,18 +137,17 @@ TfLiteStatus Preprocess(tflite::ErrorReporter* error_reporter, // In a real application, we'd calculate this table once in an initialization // function and store it for repeated reuse. // q1.15 format. - int16_t window_function[kInputSize]; + int16_t window_function[kMaxAudioSampleSize]; CalculatePeriodicHann(input_size, window_function); // Apply the window function to our time series input, and pad it with zeroes // to the next power of two. - int32_t fixed_input[kInputSize]; - for (int i = 0; i < kInputSize; ++i) { + int32_t fixed_input[kMaxAudioSampleSize]; + for (int i = 0; i < kMaxAudioSampleSize; ++i) { if (i < input_size) { // input is int16_t. Treat as q1.15 fixed point value in range [-1,1) // window_function is also q1.15 fixed point number - fixed_input[i] = - Q1_15_FixedMultiply_Q2_30(input[i], window_function[i]); + fixed_input[i] = Q1_15_FixedMultiply_Q2_30(input[i], window_function[i]); } else { fixed_input[i] = 0; } @@ -161,31 +155,31 @@ TfLiteStatus Preprocess(tflite::ErrorReporter* error_reporter, // Pull the frequency data from the time series sample. // Calculated in q10.22 format from q2.30 inputs. - int32_t fourier_values[kInputSize]; - CalculateDiscreteFourierTransform(fixed_input, kInputSize, fourier_values); + int32_t fourier_values[kMaxAudioSampleSize]; + CalculateDiscreteFourierTransform(fixed_input, kMaxAudioSampleSize, + fourier_values); // We have the complex numbers giving us information about each frequency // band, but all we want to know is how strong each frequency is, so calculate // the squared magnitude by adding together the squares of each component. - int32_t power_spectrum[kInputSize / 2]; - for (int i = 0; i < (kInputSize / 2); ++i) { + int32_t power_spectrum[kMaxAudioSampleSize / 2]; + for (int i = 0; i < (kMaxAudioSampleSize / 2); ++i) { const int32_t real = fourier_values[(i * 2) + 0]; const int32_t imaginary = fourier_values[(i * 2) + 1]; // q10.22 results - power_spectrum[i] = - Q10_22_FixedMultiply_Q10_22(real, real) + - Q10_22_FixedMultiply_Q10_22(imaginary, imaginary); + power_spectrum[i] = Q10_22_FixedMultiply_Q10_22(real, real) + + Q10_22_FixedMultiply_Q10_22(imaginary, imaginary); } // Finally, reduce the size of the output by averaging together six adjacent // frequencies into each slot, producing an array of 43 values. // Power_spectrum numbers are q10.22. Divide by kAverageWindowSize inside // loop to prevent overflow. - for (int i = 0; i < kOutputSize; ++i) { + for (int i = 0; i < kFeatureSliceSize; ++i) { int32_t average = 0; for (int j = 0; j < kAverageWindowSize; ++j) { const int index = (i * kAverageWindowSize) + j; - if (index < (kInputSize / 2)) { + if (index < (kMaxAudioSampleSize / 2)) { average += power_spectrum[index] / kAverageWindowSize; } } diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/main.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/main.cc new file mode 100644 index 0000000000..1890c25cf2 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/main.cc @@ -0,0 +1,112 @@ +/* Copyright 2018 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/lite/experimental/micro/examples/micro_speech/feature_provider.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/tiny_conv_model_data.h" +#include "tensorflow/lite/experimental/micro/kernels/all_ops_resolver.h" +#include "tensorflow/lite/experimental/micro/micro_error_reporter.h" +#include "tensorflow/lite/experimental/micro/micro_interpreter.h" +#include "tensorflow/lite/schema/schema_generated.h" +#include "tensorflow/lite/version.h" + +int main(int argc, char* argv[]) { + // Set up logging. + tflite::MicroErrorReporter micro_error_reporter; + tflite::ErrorReporter* error_reporter = µ_error_reporter; + + // Map the model into a usable data structure. This doesn't involve any + // copying or parsing, it's a very lightweight operation. + const tflite::Model* model = ::tflite::GetModel(g_tiny_conv_model_data); + if (model->version() != TFLITE_SCHEMA_VERSION) { + error_reporter->Report( + "Model provided is schema version %d not equal " + "to supported version %d.\n", + model->version(), TFLITE_SCHEMA_VERSION); + return 1; + } + + // This pulls in all the operation implementations we need. + tflite::ops::micro::AllOpsResolver resolver; + + // Create an area of memory to use for input, output, and intermediate arrays. + // The size of this will depend on the model you're using, and may need to be + // determined by experimentation. + const int tensor_arena_size = 10 * 1024; + uint8_t tensor_arena[tensor_arena_size]; + tflite::SimpleTensorAllocator tensor_allocator(tensor_arena, + tensor_arena_size); + + // Build an interpreter to run the model with. + tflite::MicroInterpreter interpreter(model, resolver, &tensor_allocator, + error_reporter); + + // Get information about the memory area to use for the model's input. + TfLiteTensor* model_input = interpreter.input(0); + if ((model_input->dims->size != 4) || (model_input->dims->data[0] != 1) || + (model_input->dims->data[1] != kFeatureSliceCount) || + (model_input->dims->data[2] != kFeatureSliceSize) || + (model_input->type != kTfLiteUInt8)) { + error_reporter->Report("Bad input tensor parameters in model"); + return 1; + } + + // Prepare to access the audio spectrograms from a microphone or other source + // that will provide the inputs to the neural network. + FeatureProvider feature_provider(kFeatureElementCount, + model_input->data.uint8); + + // Keep reading and analysing audio data in an infinite loop. + while (true) { + // Fetch the spectrogram for the current time. + int how_many_new_slices = 0; + TfLiteStatus feature_status = feature_provider.PopulateFeatureData( + error_reporter, &how_many_new_slices); + if (feature_status != kTfLiteOk) { + error_reporter->Report("Feature generation failed"); + return 1; + } + // If no new audio samples have been received since last time, don't bother + // running the network model. + if (how_many_new_slices == 0) { + continue; + } + + // Run the model on the spectrogram input and make sure it succeeds. + TfLiteStatus invoke_status = interpreter.Invoke(); + if (invoke_status != kTfLiteOk) { + error_reporter->Report("Invoke failed"); + return 1; + } + + // The output from the model is a vector containing the scores for each + // kind of prediction, so figure out what the highest scoring category was. + TfLiteTensor* output = interpreter.output(0); + uint8_t top_category_score = 0; + int top_category_index = 0; + for (int category_index = 0; category_index < kCategoryCount; + ++category_index) { + const uint8_t category_score = output->data.uint8[category_index]; + if (category_score > top_category_score) { + top_category_score = category_score; + top_category_index = category_index; + } + } + + error_reporter->Report("Heard %s", kCategoryLabels[top_category_index]); + } + + return 0; +} diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.cc similarity index 73% rename from tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.cc rename to tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.cc index a3e561a8b5..b9b8fb37b1 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.cc @@ -13,8 +13,11 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -// See the header for documentation on the meaning of this data. +#include "tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h" -#include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.h" - -const uint8_t g_no_power_spectrum_data[g_no_power_spectrum_data_size] = {233,6,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,40}; +const char* kCategoryLabels[kCategoryCount] = { + "silence", + "unknown", + "yes", + "no", +}; diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h b/tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h new file mode 100644 index 0000000000..1d8f3123a5 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h @@ -0,0 +1,42 @@ +/* Copyright 2018 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_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MODEL_SETTINGS_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MODEL_SETTINGS_H_ + +// Keeping these as constant expressions allow us to allocate fixed-sized arrays +// on the stack for our working memory. + +// The size of the input time series data we pass to the FFT to produce the +// frequency information. This has to be a power of two, and since we're dealing +// with 30ms of 16KHz inputs, which means 480 samples, this is the next value. +constexpr int kMaxAudioSampleSize = 512; + +// All of these values are derived from the values used during model training, +// if you change your model you'll need to update these constants. +constexpr int kAverageWindowSize = 6; +constexpr int kFeatureSliceSize = + ((kMaxAudioSampleSize / 2) + (kAverageWindowSize - 1)) / kAverageWindowSize; +constexpr int kFeatureSliceCount = 49; +constexpr int kFeatureElementCount = (kFeatureSliceSize * kFeatureSliceCount); +constexpr int kFeatureSliceStrideMs = 20; +constexpr int kFeatureSliceDurationMs = 30; + +constexpr int kCategoryCount = 4; +constexpr int kSilenceIndex = 0; +constexpr int kUnknownIndex = 1; +extern const char* kCategoryLabels[kCategoryCount]; + +#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MODEL_SETTINGS_H_ diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.cc index 12f9e22038..743d229224 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.cc @@ -28,14 +28,9 @@ limitations under the License. #include -namespace { +#include "tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h" -// These constants allow us to allocate fixed-sized arrays on the stack for our -// working memory. -constexpr int kInputSize = 512; -constexpr int kAverageWindowSize = 6; -constexpr int kOutputSize = - ((kInputSize / 2) + (kAverageWindowSize - 1)) / kAverageWindowSize; +namespace { // Performs a discrete Fourier transform on the real inputs. This corresponds to // rdft() in the FFT package at http://www.kurims.kyoto-u.ac.jp/~ooura/fft.html, @@ -78,27 +73,27 @@ TfLiteStatus Preprocess(tflite::ErrorReporter* error_reporter, const int16_t* input, int input_size, int output_size, uint8_t* output) { // Ensure our input and output data arrays are valid. - if (input_size > kInputSize) { + if (input_size > kMaxAudioSampleSize) { error_reporter->Report("Input size %d larger than %d", input_size, - kInputSize); + kMaxAudioSampleSize); return kTfLiteError; } - if (output_size != kOutputSize) { + if (output_size != kFeatureSliceSize) { error_reporter->Report("Requested output size %d doesn't match %d", - output_size, kOutputSize); + output_size, kFeatureSliceSize); return kTfLiteError; } // Pre-calculate the window function we'll be applying to the input data. // In a real application, we'd calculate this table once in an initialization // function and store it for repeated reuse. - float window_function[kInputSize]; + float window_function[kMaxAudioSampleSize]; CalculatePeriodicHann(input_size, window_function); // Apply the window function to our time series input, and pad it with zeroes // to the next power of two. - float float_input[kInputSize]; - for (int i = 0; i < kInputSize; ++i) { + float float_input[kMaxAudioSampleSize]; + for (int i = 0; i < kMaxAudioSampleSize; ++i) { if (i < input_size) { float_input[i] = (input[i] * window_function[i]) / static_cast(1 << 15); @@ -108,14 +103,15 @@ TfLiteStatus Preprocess(tflite::ErrorReporter* error_reporter, } // Pull the frequency data from the time series sample. - float fourier_values[kInputSize]; - CalculateDiscreteFourierTransform(float_input, kInputSize, fourier_values); + float fourier_values[kMaxAudioSampleSize]; + CalculateDiscreteFourierTransform(float_input, kMaxAudioSampleSize, + fourier_values); // We have the complex numbers giving us information about each frequency // band, but all we want to know is how strong each frequency is, so calculate // the squared magnitude by adding together the squares of each component. - float power_spectrum[kInputSize / 2]; - for (int i = 0; i < (kInputSize / 2); ++i) { + float power_spectrum[kMaxAudioSampleSize / 2]; + for (int i = 0; i < (kMaxAudioSampleSize / 2); ++i) { const float real = fourier_values[(i * 2) + 0]; const float imaginary = fourier_values[(i * 2) + 1]; power_spectrum[i] = (real * real) + (imaginary * imaginary); @@ -123,11 +119,11 @@ TfLiteStatus Preprocess(tflite::ErrorReporter* error_reporter, // Finally, reduce the size of the output by averaging together six adjacent // frequencies into each slot, producing an array of 43 values. - for (int i = 0; i < kOutputSize; ++i) { + for (int i = 0; i < kFeatureSliceSize; ++i) { float total = 0.0f; for (int j = 0; j < kAverageWindowSize; ++j) { const int index = (i * kAverageWindowSize) + j; - if (index < (kInputSize / 2)) { + if (index < (kMaxAudioSampleSize / 2)) { total += power_spectrum[index]; } } @@ -147,3 +143,13 @@ TfLiteStatus Preprocess(tflite::ErrorReporter* error_reporter, } return kTfLiteOk; } + +TfLiteStatus Preprocess_1sec(tflite::ErrorReporter* error_reporter, + const int16_t* input, uint8_t* output) { + int i; + for(i=0; i<49; i++) { + Preprocess(error_reporter, input+i*320, 480, 43, output+i*43); + } + return kTfLiteOk; +} + diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.h b/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.h index dede2a8642..0057b4505f 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.h +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.h @@ -19,8 +19,16 @@ limitations under the License. #include "tensorflow/lite/c/c_api_internal.h" #include "tensorflow/lite/experimental/micro/micro_error_reporter.h" +// Converts audio sample data into a more compact form that's appropriate for +// feeding into a neural network. There are reference implementations that use +// both floating point and fixed point available, but because the calculations +// involved can be time-consuming, it's recommended that you use or write +// specialized versions for your platform. TfLiteStatus Preprocess(tflite::ErrorReporter* error_reporter, const int16_t* input, int input_size, int output_size, uint8_t* output); +TfLiteStatus Preprocess_1sec(tflite::ErrorReporter* error_reporter, + const int16_t* input, uint8_t* output); + #endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_PREPROCESSOR_H_ diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor_test.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor_test.cc index e8b49f67e3..d9b0c48ba3 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor_test.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor_test.cc @@ -22,6 +22,7 @@ limitations under the License. #include "tensorflow/lite/experimental/micro/micro_error_reporter.h" #include "tensorflow/lite/experimental/micro/testing/micro_test.h" + TF_LITE_MICRO_TESTS_BEGIN TF_LITE_MICRO_TEST(TestPreprocessor) { diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/timer.cc similarity index 73% rename from tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.cc rename to tensorflow/lite/experimental/micro/examples/micro_speech/timer.cc index 89edb51040..6c96a61ab5 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/timer.cc @@ -13,8 +13,10 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -// See the header for documentation on the meaning of this data. +#include "tensorflow/lite/experimental/micro/examples/micro_speech/timer.h" -#include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.h" - -const uint8_t g_yes_power_spectrum_data[g_yes_power_spectrum_data_size] = {8,88,8,0,0,0,0,0,0,0,0,3,12,0,5,22,19,5,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,9,1}; +int32_t TimeInMilliseconds() { + static int current_time = 0; + current_time += 100; + return current_time; +} diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/timer.h b/tensorflow/lite/experimental/micro/examples/micro_speech/timer.h new file mode 100644 index 0000000000..162952844a --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/timer.h @@ -0,0 +1,31 @@ +/* Copyright 2018 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_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_TIMER_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_TIMER_H_ + +#include + +// Returns the time in milliseconds. There's no contract about what time zero +// represents, the accuracy, or the granularity of the result. Subsequent calls +// will generally not return a lower value, but even that's not guaranteed if +// there's an overflow wraparound. +// The reference implementation of this function just returns a constantly +// incrementing value for each call, since it would need a non-portable platform +// call to access time information. For real applications, you'll need to write +// your own platform-specific implementation. +int32_t TimeInMilliseconds(); + +#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_TIMER_H_ diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/timer_test.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/timer_test.cc new file mode 100644 index 0000000000..0487a12b25 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/timer_test.cc @@ -0,0 +1,49 @@ +/* Copyright 2018 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/lite/experimental/micro/examples/micro_speech/timer.h" + +#include + +#include "tensorflow/lite/c/c_api_internal.h" +#include "tensorflow/lite/experimental/micro/micro_error_reporter.h" +#include "tensorflow/lite/experimental/micro/testing/micro_test.h" + +TF_LITE_MICRO_TESTS_BEGIN + +TF_LITE_MICRO_TEST(TestTimer) { + // Make sure that the technically-undefined overflow behavior we rely on below + // works on this platform. It's still not guaranteed, but at least this is a + // sanity check. Turn off when running with ASan, as it will complain about + // the following undefined behavior. +#ifndef ADDRESS_SANITIZER + int32_t overflow_value = std::numeric_limits::max(); + overflow_value += 1; + TF_LITE_MICRO_EXPECT_EQ(std::numeric_limits::min(), overflow_value); +#endif + + const int32_t first_time = TimeInMilliseconds(); + const int32_t second_time = TimeInMilliseconds(); + + // It's possible that the timer may have wrapped around from +BIG_NUM to + // -BIG_NUM between the first and second calls, since we're storing + // milliseconds in a 32-bit integer. It's not reasonable that the call itself + // would have taken more than 2^31 milliseconds though, so look at the + // difference and rely on integer overflow to ensure it's accurate. + const int32_t time_delta = (second_time - first_time); + TF_LITE_MICRO_EXPECT_LE(0, time_delta); +} + +TF_LITE_MICRO_TESTS_END diff --git a/tensorflow/lite/experimental/micro/testing/micro_test.h b/tensorflow/lite/experimental/micro/testing/micro_test.h index 10bab05fae..2f20dd5ac7 100644 --- a/tensorflow/lite/experimental/micro/testing/micro_test.h +++ b/tensorflow/lite/experimental/micro/testing/micro_test.h @@ -153,4 +153,22 @@ extern tflite::ErrorReporter* reporter; } \ } while (false) +#define TF_LITE_MICRO_EXPECT_GE(x, y) \ + do { \ + if ((x) < (y)) { \ + micro_test::reporter->Report(#x " >= " #y " failed at %s:%d", __FILE__, \ + __LINE__); \ + micro_test::did_test_fail = true; \ + } \ + } while (false) + +#define TF_LITE_MICRO_EXPECT_LE(x, y) \ + do { \ + if ((x) > (y)) { \ + micro_test::reporter->Report(#x " <= " #y " failed at %s:%d", __FILE__, \ + __LINE__); \ + micro_test::did_test_fail = true; \ + } \ + } while (false) + #endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_TESTING_MICRO_TEST_H_ diff --git a/tensorflow/lite/experimental/micro/tools/make/Makefile b/tensorflow/lite/experimental/micro/tools/make/Makefile index 463709a6e5..3f6f927358 100644 --- a/tensorflow/lite/experimental/micro/tools/make/Makefile +++ b/tensorflow/lite/experimental/micro/tools/make/Makefile @@ -59,13 +59,27 @@ tensorflow/lite/experimental/micro/examples/micro_speech/tiny_conv_model_data.cc tensorflow/lite/experimental/micro/examples/micro_speech/no_features_data.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/yes_features_data.cc +# Test binary for the streaming microcontroller speech model. +PUSHBUTTON_MICRO_SPEECH_TEST_SRCS := \ +tensorflow/lite/experimental/micro/examples/micro_speech/micro_speech_test.cc \ +tensorflow/lite/experimental/micro/examples/micro_speech/tiny_conv_model_data.cc \ +tensorflow/lite/experimental/micro/examples/micro_speech/no_features_data.cc \ +tensorflow/lite/experimental/micro/examples/micro_speech/yes_features_data.cc + +# Test binary for the streaming microcontroller speech model. +PUSHBUTTON_CMSIS_SPEECH_TEST_SRCS := \ +tensorflow/lite/experimental/micro/examples/micro_speech/micro_speech_test.cc \ +tensorflow/lite/experimental/micro/examples/micro_speech/tiny_conv_model_data.cc + # Test binary for the microcontroller speech model. PREPROCESSOR_TEST_SRCS := \ tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor_test.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/no_30ms_sample_data.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/yes_30ms_sample_data.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/no_power_spectrum_data.cc \ -tensorflow/lite/experimental/micro/examples/micro_speech/yes_power_spectrum_data.cc +tensorflow/lite/experimental/micro/examples/micro_speech/yes_power_spectrum_data.cc \ +tensorflow/lite/experimental/micro/examples/micro_speech/yes_waveform.cc \ +tensorflow/lite/experimental/micro/examples/micro_speech/yes_features_data.cc PREPROCESSOR_REFERENCE_TEST_SRCS = \ $(PREPROCESSOR_TEST_SRCS) \ @@ -75,6 +89,11 @@ PREPROCESSOR_FIXED_TEST_SRCS += \ $(PREPROCESSOR_TEST_SRCS) \ tensorflow/lite/experimental/micro/examples/micro_speech/fixed_point/preprocessor.cc +PREPROCESSOR_1K_SRCS := \ +tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k.cc \ +tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.cc + + MICROLITE_TEST_SRCS := \ $(wildcard tensorflow/lite/experimental/micro/*test.cc) \ $(wildcard tensorflow/lite/experimental/micro/kernels/*test.cc) @@ -101,6 +120,8 @@ ALL_SRCS := \ $(PREPROCESSOR_REFERENCE_TEST_SRCS) \ $(PREPROCESSOR_FIXED_TEST_SRCS) \ $(MICROLITE_CC_SRCS) \ + $(PREPROCESSOR_1K_MICRO_TEST_SRCS) \ + $(PREPROCESSOR_1K_CMSIS_TEST_SRCS) \ $(MICROLITE_TEST_SRCS) # Where compiled objects are stored. @@ -112,8 +133,12 @@ LIBDIR := $(GENDIR)lib/ MICROLITE_LIB_PATH := $(LIBDIR)$(MICROLITE_LIB_NAME) MICRO_SPEECH_TEST_BINARY := $(BINDIR)micro_speech_test +PUSHBUTTON_MICRO_SPEECH_TEST_BINARY := $(BINDIR)pushbutton_micro_speech_test +PUSHBUTTON_CMSIS_SPEECH_TEST_BINARY := $(BINDIR)pushbutton_cmsis_speech_test PREPROCESSOR_REFERENCE_TEST_BINARY := $(BINDIR)preprocessor_reference_test PREPROCESSOR_FIXED_TEST_BINARY := $(BINDIR)preprocessor_fixed_test +PREPROCESSOR_1K_MICRO_TEST_BINARY := $(BINDIR)preprocessor_1k_micro_test +PREPROCESSOR_1K_CMSIS_TEST_BINARY := $(BINDIR)preprocessor_1k_cmsis_test CXX := $(CC_PREFIX)${TARGET_TOOLCHAIN_PREFIX}g++ CC := $(CC_PREFIX)${TARGET_TOOLCHAIN_PREFIX}gcc @@ -128,6 +153,13 @@ $(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(PREPROCESSOR_REFERENCE_TEST_SRCS)))) PREPROCESSOR_FIXED_TEST_OBJS := $(addprefix $(OBJDIR), \ $(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(PREPROCESSOR_FIXED_TEST_SRCS)))) +PREPROCESSOR_1K_MICRO_TEST_OBJS := $(addprefix $(OBJDIR), \ +$(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(PREPROCESSOR_1K_MICRO_TEST_SRCS)))) + +PREPROCESSOR_1K_CMSIS_TEST_OBJS := $(addprefix $(OBJDIR), \ +$(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(PREPROCESSOR_1K_CMSIS_TEST_SRCS))) \ +arm_bitreversal2.o) + MICROLITE_LIB_OBJS := $(addprefix $(OBJDIR), \ $(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(MICROLITE_CC_SRCS)))) @@ -194,6 +226,35 @@ preprocessor_fixed_test_bin: $(PREPROCESSOR_FIXED_TEST_BINARY).bin test_preprocessor_fixed: $(PREPROCESSOR_FIXED_TEST_BINARY) $(TEST_SCRIPT) $(PREPROCESSOR_FIXED_TEST_BINARY) '~~~ALL TESTS PASSED~~~' +$(PREPROCESSOR_1K_MICRO_TEST_BINARY): $(PREPROCESSOR_1K_MICRO_TEST_OBJS) $(MICROLITE_LIB_PATH) + @mkdir -p $(dir $@) + $(CXX) $(CXXFLAGS) $(INCLUDES) \ + -o $(PREPROCESSOR_1K_MICRO_TEST_BINARY) $(PREPROCESSOR_1K_MICRO_TEST_OBJS) \ + $(LIBFLAGS) $(MICROLITE_LIB_PATH) $(LDFLAGS) $(MICROLITE_LIBS) + +preprocessor_1k_micro_test: $(PREPROCESSOR_1K_MICRO_TEST_BINARY) +preprocessor_1k_micro_test_bin: $(PREPROCESSOR_1K_MICRO_TEST_BINARY).bin + +test_preprocessor_1k_micro: $(PREPROCESSOR_1K_MICRO_TEST_BINARY) + $(TEST_SCRIPT) $(PREPROCESSOR_1K_MICRO_TEST_BINARY) '~~~ALL TESTS PASSED~~~' + + +$(PREPROCESSOR_1K_CMSIS_TEST_BINARY): $(PREPROCESSOR_1K_CMSIS_TEST_OBJS) $(MICROLITE_LIB_PATH) + @mkdir -p $(dir $@) + $(CXX) $(CXXFLAGS) $(INCLUDES) \ + -o $(PREPROCESSOR_1K_CMSIS_TEST_BINARY) $(PREPROCESSOR_1K_CMSIS_TEST_OBJS) \ + $(LIBFLAGS) $(MICROLITE_LIB_PATH) $(LDFLAGS) $(MICROLITE_LIBS) + +preprocessor_1k_cmsis_test: $(PREPROCESSOR_1K_CMSIS_TEST_BINARY) +preprocessor_1k_cmsis_test_bin: $(PREPROCESSOR_1K_CMSIS_TEST_BINARY).bin + +test_preprocessor_1k_cmsis: $(PREPROCESSOR_1K_CMSIS_TEST_BINARY) + $(TEST_SCRIPT) $(PREPROCESSOR_1K_CMSIS_TEST_BINARY) '~~~ALL TESTS PASSED~~~' + + +$(OBJDIR)arm_bitreversal2.o: + $(CXX) $(CXXFLAGS) $(INCLUDES) -c $(CMSIS_SRC_DIR)/TransformFunctions/arm_bitreversal2.S -o $(OBJDIR)arm_bitreversal2.o + $(BINDIR)%_test : $(OBJDIR)%_test.o $(MICROLITE_LIB_PATH) @mkdir -p $(dir $@) $(CXX) $(CXXFLAGS) $(INCLUDES) \ diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/README.md b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/README.md deleted file mode 100644 index fec4923e0e..0000000000 --- a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/README.md +++ /dev/null @@ -1,13 +0,0 @@ -Follow these steps to get the preprocessor test working on Apollo 3: - -1. Download the SDK to the be at the same level as tensorflow.git -2. Copy and prepare files by running tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/prep_apollo3_files.sh -3. Recompile libarm_cortexM4lf_math.a with the softfp option, and call it libarm_cartexM4lf_math_softfp.a. The original version was compiled with the hard option, and this caused conflicts with existing software. We might be able to fix this in the future -4. Install Segger JLink tools from https://www.segger.com/downloads/jlink/ -5. Compile the preprocessor_test_bin project with the following command: make -f tensorflow/lite/experimental/micro/tools/make/Makefile TARGET=apollo3evb VENDORLIB=cmsis-dsp preprocessor_test_bin -6. Download to the target with JFlashLiteExe with the following settings: - 1. Device = AMA3B1KK-KBR - 2. Interface = SWD at 1000 kHz - 3. Data file = tensorflow/lite/experimental/micro/tools/make/gen/apollo3evb_cortex-m4/bin/preprocessor_test.bin - 4. Prog Addr = 0x0000C000 -7. Connect to device via serial port (115200 baud) and press reset button. Should see all tests passed. Seeing a discrepance between Windows and Linux testing --> need to debug diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/get_yesno_data.cmd b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/get_yesno_data.cmd deleted file mode 100644 index 71d5389ee3..0000000000 --- a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/get_yesno_data.cmd +++ /dev/null @@ -1,10 +0,0 @@ -file ../../gen/apollo3evb_cortex-m4/bin/preprocessor_test -target remote localhost:2331 -load ../../gen/apollo3evb_cortex-m4/bin/preprocessor_test -monitor reset -break preprocessor_test.cc:35 -break preprocessor_test.cc:51 -c -dump verilog value yes_calculated_data.txt yes_calculated_data -c -dump verilog value no_calculated_data.txt no_calculated_data diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/prep_apollo3_files.sh b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/prep_apollo3_files.sh index 79ce18f11f..7ef2309502 100755 --- a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/prep_apollo3_files.sh +++ b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/prep_apollo3_files.sh @@ -14,19 +14,22 @@ # limitations under the License. # ============================================================================== -if [ ! -d "../Apollo3-SDK-2018.08.13" ]; then +AP3_DIR="tensorflow/lite/experimental/micro/tools/make/downloads/Apollo3-SDK-2018.08.13" +if [ ! -d $AP3_DIR ]; then echo "Apollo 3 SDK does not exist" - echo "Either the SDK has not been downloaded, or this script is not being done from the root of the repository" + echo "Either the SDK has not been downloaded, or this script is not being run from the root of the repository" else DEST_DIR="tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb" - AP3_DIR="../Apollo3-SDK-2018.08.13" cp "$AP3_DIR/boards/apollo3_evb/examples/hello_world/gcc/startup_gcc.c" "$DEST_DIR" - cp "$AP3_DIR/utils/am_util_delay.c" "$DEST_DIR" - cp "$AP3_DIR/utils/am_util_faultisr.c" "$DEST_DIR" - cp "$AP3_DIR/utils/am_util_id.c" "$DEST_DIR" - cp "$AP3_DIR/utils/am_util_stdio.c" "$DEST_DIR" - cp "$AP3_DIR/boards/apollo3_evb/bsp/gcc/bin/libam_bsp.a" "$DEST_DIR" - cp "$AP3_DIR/mcu/apollo3/hal/gcc/bin/libam_hal.a" "$DEST_DIR" + cp "$AP3_DIR/boards/apollo3_evb/examples/hello_world/gcc/hello_world.ld" "$DEST_DIR/apollo3evb.ld" sed -i -e '131s/1024/1024\*20/g' "$DEST_DIR/startup_gcc.c" sed -i -e 's/main/_main/g' "$DEST_DIR/startup_gcc.c" + sed -i -e '3s/hello_world.ld/apollo3evb.ld/g' "$DEST_DIR/apollo3evb.ld" + sed -i -e '3s/startup_gnu/startup_gcc/g' "$DEST_DIR/apollo3evb.ld" + sed -i -e '6s/am_reset_isr/Reset_Handler/g' "$DEST_DIR/apollo3evb.ld" + sed -i -e '22s/\*(.text\*)/\*(.text\*)\n\n\t\/\* These are the C++ global constructors. Stick them all here and\n\t \* then walk through the array in main() calling them all.\n\t \*\/\n\t_init_array_start = .;\n\tKEEP (\*(SORT(.init_array\*)))\n\t_init_array_end = .;\n\n\t\/\* XXX Currently not doing anything for global destructors. \*\/\n/g' "$DEST_DIR/apollo3evb.ld" + sed -i -e "70s/} > SRAM/} > SRAM\n \/\* Add this to satisfy reference to symbol 'end' from libnosys.a(sbrk.o)\n \* to denote the HEAP start.\n \*\/\n end = .;/g" "$DEST_DIR/apollo3evb.ld" + echo "Finished preparing Apollo3 files" + + fi diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/preprocessor_test.cmd b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/preprocessor_test.cmd deleted file mode 100644 index 1b1db457fe..0000000000 --- a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/preprocessor_test.cmd +++ /dev/null @@ -1,4 +0,0 @@ -file ../../gen/apollo3evb_cortex-m4/bin/preprocessor_test -target remote localhost:2331 -load ../../gen/apollo3evb_cortex-m4/bin/preprocessor_test -monitor reset diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/replace_calculated_data.py b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/replace_calculated_data.py deleted file mode 100644 index 3c1b0110fc..0000000000 --- a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/replace_calculated_data.py +++ /dev/null @@ -1,33 +0,0 @@ -import numpy as np -import re - -# This should be run from make/targets/apollo3evb - -def new_data_to_array(fn): - vals = [] - with open(fn) as f: - for n, line in enumerate(f): - if n is not 0: - vals.extend([str(int(v, 16)) for v in line.split()]) - - return ','.join(vals) - -def replace_data(fn_old, new_data): - patt = '(?<=\{).+?(?=\})' - with open(fn_old,'r') as f: - str_old = f.read() - str_new = re.sub(patt, new_data, str_old, flags=re.DOTALL) - with open(fn_old,'w') as f: - f.write(str_new) - - -yes_old = '../../../../examples/micro_speech/CMSIS/yes_power_spectrum_data.cc' -no_old = '../../../../examples/micro_speech/CMSIS/no_power_spectrum_data.cc' -yes_new = 'yes_calculated_data.txt' -no_new = 'no_calculated_data.txt' - -yes_new_vals = new_data_to_array(yes_new) -no_new_vals = new_data_to_array(no_new) - -replace_data(yes_old, yes_new_vals) -replace_data(no_old, no_new_vals) diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc index 153a19fcd4..3db4367a17 100644 --- a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc +++ b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc @@ -4,10 +4,10 @@ ifeq ($(TARGET), apollo3evb) TARGET_TOOLCHAIN_PREFIX := arm-none-eabi- # Download the Ambiq Apollo3 SDK and set this variable to find the header # files: - APOLLO3_SDK := ../Apollo3-SDK-2018.08.13 + APOLLO3_SDK := $(MAKEFILE_DIR)/downloads/Apollo3-SDK-2018.08.13 # Need a pointer to the GNU ARM toolchain for crtbegin.o for the fp functions # with the softfp interfaces. - GCC_ARM := ../gcc-arm-none-eabi-7-2018-q2-update/ + GCC_ARM := $(MAKEFILE_DIR)/downloads/gcc-arm-none-eabi-7-2018-q2-update/ PLATFORM_FLAGS = \ -DPART_apollo3 \ @@ -17,6 +17,7 @@ ifeq ($(TARGET), apollo3evb) -DTF_LITE_STATIC_MEMORY \ -DTF_LITE_MCU_DEBUG_LOG \ -D __FPU_PRESENT=1 \ + -DARM_MATH_CM4 \ -fno-rtti \ -fmessage-length=0 \ -fno-exceptions \ @@ -42,7 +43,7 @@ ifeq ($(TARGET), apollo3evb) -fomit-frame-pointer \ -fpermissive \ -nostdlib \ - -g \ + -ggdb \ -O3 CXXFLAGS += $(PLATFORM_FLAGS) CCFLAGS += $(PLATFORM_FLAGS) @@ -56,11 +57,9 @@ ifeq ($(TARGET), apollo3evb) -Wl,-T,$(MAKEFILE_DIR)/targets/apollo3evb/apollo3evb.ld \ -Wl,-Map=$(MAKEFILE_DIR)/gen/$(TARGET).map,--cref BUILD_TYPE := micro - # The apollo3evb libs should be copied from the SDK after building them. MICROLITE_LIBS := \ - $(MAKEFILE_DIR)/targets/apollo3evb/libam_bsp.a \ - $(MAKEFILE_DIR)/targets/apollo3evb/libam_hal.a \ - $(MAKEFILE_DIR)/downloads/cmsis/CMSIS/Lib/GCC/libarm_cortexM4lf_math_softfp.a \ + $(APOLLO3_SDK)/boards/apollo3_evb/bsp/gcc/bin/libam_bsp.a \ + $(APOLLO3_SDK)/mcu/apollo3/hal/gcc/bin/libam_hal.a \ $(GCC_ARM)/lib/gcc/arm-none-eabi/7.3.1/thumb/v7e-m/fpv4-sp/softfp/crtbegin.o \ -lm INCLUDES += \ @@ -83,11 +82,63 @@ ifeq ($(TARGET), apollo3evb) # of the DebugLog interfaces. MICROLITE_CC_SRCS += \ $(MAKEFILE_DIR)/targets/apollo3evb/startup_gcc.c \ - $(MAKEFILE_DIR)/targets/apollo3evb/_main.c \ - $(MAKEFILE_DIR)/targets/apollo3evb/am_util_delay.c \ - $(MAKEFILE_DIR)/targets/apollo3evb/am_util_faultisr.c \ - $(MAKEFILE_DIR)/targets/apollo3evb/am_util_id.c \ - $(MAKEFILE_DIR)/targets/apollo3evb/am_util_stdio.c + $(APOLLO3_SDK)/utils/am_util_delay.c \ + $(APOLLO3_SDK)/utils/am_util_faultisr.c \ + $(APOLLO3_SDK)/utils/am_util_id.c \ + $(APOLLO3_SDK)/utils/am_util_stdio.c + + CMSIS_SRC_DIR := tensorflow/lite/experimental/micro/tools/make/downloads/cmsis/CMSIS/DSP/Source + CMSIS_SRCS := \ + $(CMSIS_SRC_DIR)/BasicMathFunctions/arm_mult_q15.c \ + $(CMSIS_SRC_DIR)/TransformFunctions/arm_rfft_init_q15.c \ + $(CMSIS_SRC_DIR)/TransformFunctions/arm_rfft_q15.c \ + $(CMSIS_SRC_DIR)/TransformFunctions/arm_cfft_q15.c \ + $(CMSIS_SRC_DIR)/TransformFunctions/arm_cfft_radix4_q15.c \ + $(CMSIS_SRC_DIR)/CommonTables/arm_const_structs.c \ + $(CMSIS_SRC_DIR)/CommonTables/arm_common_tables.c \ + $(CMSIS_SRC_DIR)/StatisticsFunctions/arm_mean_q15.c \ + $(CMSIS_SRC_DIR)/StatisticsFunctions/arm_max_q7.c + + AP3_MICRO_DIR := tensorflow/lite/experimental/micro/examples/micro_speech/apollo3 + CMSIS_DIR := tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS + + MICRO_SPEECH_TEST_SRCS += \ + $(AP3_MICRO_DIR)/_main.c + + PREPROCESSOR_1K_CMSIS_TEST_SRCS := \ + $(PREPROCESSOR_1K_SRCS) \ + $(CMSIS_DIR)/preprocessor.cc \ + $(CMSIS_DIR)/arm_cmplx_mag_squared_q10p6.c \ + $(CMSIS_DIR)/hanning.c \ + $(AP3_MICRO_DIR)/system_apollo3.c \ + $(AP3_MICRO_DIR)/_main.c \ + $(CMSIS_SRCS) + + PREPROCESSOR_1K_MICRO_TEST_SRCS := \ + $(PREPROCESSOR_1K_SRCS) \ + tensorflow/lite/experimental/micro/examples/micro_speech/fixed_point/preprocessor.cc \ + tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/system_apollo3.c \ + $(AP3_MICRO_DIR)/_main.c + + PUSHBUTTON_MICRO_SPEECH_TEST_SRCS := \ + $(AP3_MICRO_DIR)/../preprocessor.cc \ + $(AP3_MICRO_DIR)/pushbutton_main.c \ + $(AP3_MICRO_DIR)/pushbutton_test.cc \ + $(AP3_MICRO_DIR)/../tiny_conv_model_data.cc \ + $(APOLLO3_SDK)/devices/am_devices_led.c + + PUSHBUTTON_CMSIS_SPEECH_TEST_SRCS := \ + $(AP3_MICRO_DIR)/pushbutton_main.c \ + $(AP3_MICRO_DIR)/pushbutton_test.cc \ + $(AP3_MICRO_DIR)/../tiny_conv_model_data.cc \ + $(CMSIS_DIR)/preprocessor.cc \ + $(CMSIS_DIR)/arm_cmplx_mag_squared_q10p6.c \ + $(CMSIS_DIR)/hanning.c \ + $(APOLLO3_SDK)/devices/am_devices_led.c \ + $(CMSIS_SRCS) + + PREPROCESSOR_TEST_SRCS += \ + $(AP3_MICRO_DIR)/_main.c TEST_SCRIPT := tensorflow/lite/experimental/log_test/test_apollo3evb_binary.sh # These are tests that don't currently work on the blue pill. @@ -96,4 +147,5 @@ ifeq ($(TARGET), apollo3evb) tensorflow/lite/experimental/micro/simple_tensor_allocator_test.cc MICROLITE_TEST_SRCS := $(filter-out $(EXCLUDED_TESTS), $(MICROLITE_TEST_SRCS)) + endif diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/cmsis-dsp_makefile.inc b/tensorflow/lite/experimental/micro/tools/make/targets/cmsis-dsp_makefile.inc deleted file mode 100644 index 54b3393229..0000000000 --- a/tensorflow/lite/experimental/micro/tools/make/targets/cmsis-dsp_makefile.inc +++ /dev/null @@ -1,9 +0,0 @@ -ifeq ($(VENDORLIB), cmsis-dsp) -PREPROCESSOR_TEST_SRCS := \ -tensorflow/lite/experimental/micro/examples/micro_speech/no_30ms_sample_data.cc \ -tensorflow/lite/experimental/micro/examples/micro_speech/yes_30ms_sample_data.cc \ -tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.cc \ -tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor_test.cc \ -tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/no_power_spectrum_data.cc \ -tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/yes_power_spectrum_data.cc -endif diff --git a/tensorflow/lite/experimental/microfrontend/python/kernel_tests/audio_microfrontend_op_test.py b/tensorflow/lite/experimental/microfrontend/python/kernel_tests/audio_microfrontend_op_test.py index 020d40bc13..561f5f7a50 100644 --- a/tensorflow/lite/experimental/microfrontend/python/kernel_tests/audio_microfrontend_op_test.py +++ b/tensorflow/lite/experimental/microfrontend/python/kernel_tests/audio_microfrontend_op_test.py @@ -110,7 +110,7 @@ class AudioFeatureGenerationTest(tf.test.TestCase): left_context=1, right_context=1) self.assertAllEqual( - filterbanks.eval(), + self.evaluate(filterbanks), [[479, 425, 479, 425, 436, 378], [479, 425, 436, 378, 410, 350], [436, 378, 410, 350, 391, 325], [410, 350, 391, 325, 391, 325]]) @@ -153,7 +153,7 @@ class AudioFeatureGenerationTest(tf.test.TestCase): frame_stride=3, zero_padding=True) self.assertAllEqual( - filterbanks.eval(), + self.evaluate(filterbanks), [[0, 0, 0, 0, 479, 425], [436, 378, 410, 350, 391, 325], [374, 308, 362, 292, 352, 275]]) diff --git a/tensorflow/lite/experimental/writer/BUILD b/tensorflow/lite/experimental/writer/BUILD index 506c668cf2..57ce636367 100644 --- a/tensorflow/lite/experimental/writer/BUILD +++ b/tensorflow/lite/experimental/writer/BUILD @@ -1,6 +1,9 @@ -package(default_visibility = [ - "//visibility:public", -]) +package( + default_visibility = [ + "//visibility:public", + ], + features = ["-parse_headers"], +) licenses(["notice"]) # Apache 2.0 diff --git a/tensorflow/lite/experimental/writer/option_writer_generator.cc b/tensorflow/lite/experimental/writer/option_writer_generator.cc index 036809e94a..fa360a2f47 100644 --- a/tensorflow/lite/experimental/writer/option_writer_generator.cc +++ b/tensorflow/lite/experimental/writer/option_writer_generator.cc @@ -56,6 +56,7 @@ static const char* param_structs[] = {"TfLiteConvParams", "TfLiteTransposeParams", "TfLiteReducerParams", "TfLiteSplitParams", + "TfLiteSplitVParams", "TfLiteSqueezeParams", "TfLiteStridedSliceParams", "TfLiteArgMaxParams", @@ -66,6 +67,8 @@ static const char* param_structs[] = {"TfLiteConvParams", "TfLiteFakeQuantParams", "TfLitePackParams", "TfLiteOneHotParams", + "TfLiteLeakyReluParams", + "TfLiteMirrorPaddingParams", nullptr}; } // namespace @@ -152,6 +155,7 @@ class OpOptionData { op_to_option_["BIDIRECTIONAL_SEQUENCE_RNN"] = "SequenceRNNOptions"; op_to_option_["UNIDIRECTIONAL_SEQUENCE_RNN"] = "SequenceRNNOptions"; op_to_option_["UNIDIRECTIONAL_SEQUENCE_RNN"] = "SequenceRNNOptions"; + op_to_option_["MIRROR_PAD"] = ""; // TODO(karimnosseir): MirrorPadOptions. // Manually specified mappings between ops and options (none) op_to_option_["EMBEDDING_LOOKUP"] = ""; // TODO(aselle): maybe something else. diff --git a/tensorflow/lite/g3doc/_book.yaml b/tensorflow/lite/g3doc/_book.yaml index ab0d186848..a51c7a667f 100644 --- a/tensorflow/lite/g3doc/_book.yaml +++ b/tensorflow/lite/g3doc/_book.yaml @@ -3,11 +3,11 @@ upper_tabs: - include: /_upper_tabs_left.yaml - include: /api_docs/_upper_tabs_api.yaml # Dropdown menu -- name: Ecosystem - path: /ecosystem +- name: Resources + path: /resources is_default: true menu: - - include: /ecosystem/_menu_toc.yaml + - include: /resources/_menu_toc.yaml lower_tabs: # Subsite tabs other: diff --git a/tensorflow/lite/g3doc/_index.yaml b/tensorflow/lite/g3doc/_index.yaml index 093f86b542..1b3f1d616a 100644 --- a/tensorflow/lite/g3doc/_index.yaml +++ b/tensorflow/lite/g3doc/_index.yaml @@ -189,7 +189,7 @@ landing_page: - label: Read more path: https://cloud.google.com/blog/products/ai-machine-learning/ai-motion-designing-simple-system-see-understand-and-react-real-world-part-ii - heading: "Introducing the Model Optimization Toolkit" - image_path: /ecosystem/images/tf-logo-card-16x9.png + image_path: /resources/images/tf-logo-card-16x9.png path: https://medium.com/tensorflow/introducing-the-model-optimization-toolkit-for-tensorflow-254aca1ba0a3 buttons: - label: Read on TensorFlow blog @@ -205,7 +205,7 @@ landing_page: background: grey items: - heading: "Using TensorFlow Lite on Android" - image_path: /ecosystem/images/tf-logo-card-16x9.png + image_path: /resources/images/tf-logo-card-16x9.png path: https://medium.com/tensorflow/using-tensorflow-lite-on-android-9bbc9cb7d69d buttons: - label: Read on TensorFlow blog @@ -216,7 +216,7 @@ landing_page: - label: Watch the video path: https://www.youtube.com/watch?v=FAMfy7izB6A - heading: "TensorFlow Lite on GitHub" - image_path: /ecosystem/images/github-card-16x9.png + image_path: /resources/images/github-card-16x9.png path: https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite buttons: - label: View on GitHub diff --git a/tensorflow/lite/g3doc/convert/index.md b/tensorflow/lite/g3doc/convert/index.md index bc92a1c1a1..60fa265c29 100644 --- a/tensorflow/lite/g3doc/convert/index.md +++ b/tensorflow/lite/g3doc/convert/index.md @@ -6,14 +6,20 @@ file used by the TensorFlow Lite interpreter. ## From model training to device deployment After a TensorFlow model is trained, the TensorFlow Lite converter uses that -model to generate a TensorFlow Lite [FlatBuffer](https://google.github.io/flatbuffers/) -file (`.tflite`). The converter supports as input: +model to generate a TensorFlow Lite +[FlatBuffer](https://google.github.io/flatbuffers/) file (`.tflite`). The +converter supports as input: [SavedModels](https://www.tensorflow.org/guide/saved_model#using_savedmodel_with_estimators), frozen graphs (models generated by [freeze_graph.py](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/tools/freeze_graph.py)), -and `tf.keras` models. The TensorFlow Lite `FlatBuffer` file is deployed to a -client device (generally a mobile or embedded device), and the TensorFlow Lite +and `tf.keras` HDF5 models. The TensorFlow Lite `FlatBuffer` file is deployed to +a client device (generally a mobile or embedded device), and the TensorFlow Lite interpreter uses the compressed model for on-device inference. This conversion process is shown in the diagram below: ![TFLite converter workflow](../images/convert/workflow.svg) + +The TensorFlow Lite Converter can be used either from [Python](python_api.md) or +from the [command line](cmdline_examples.md). This allows you to integrate the +conversion step into the model design workflow, ensuring the model is easy to +convert to a mobile inference graph. diff --git a/tensorflow/lite/g3doc/convert/python_api.md b/tensorflow/lite/g3doc/convert/python_api.md index 4bdf0d8cbe..b914a34fa8 100644 --- a/tensorflow/lite/g3doc/convert/python_api.md +++ b/tensorflow/lite/g3doc/convert/python_api.md @@ -3,10 +3,9 @@ This page provides examples on how to use the TensorFlow Lite Converter and the TensorFlow Lite interpreter using the Python API. -Note: TFLite recently moved from `tf.contrib.lite` to `tf.lite`. If you are -using tensorflow `r1.12` or earlier you will need to add `.contrib` to the -commands below. `tf.lite` works with newer builds, like the nightly build, -which can be installed with: `pip install tf-nightly` +Note: These docs describe the converter in the TensorFlow nightly release, +installed using `pip install tf-nightly`. For docs describing older versions +reference ["Converting models from TensorFlow 1.12"](#pre_tensorflow_1.12). [TOC] @@ -24,11 +23,6 @@ The API for converting TensorFlow models to TensorFlow Lite as of TensorFlow 1.9 is `tf.lite.TFLiteConverter`. The API for calling the Python intepreter is `tf.lite.Interpreter`. -Note: Reference "Additional Instructions" sections for converting TensorFlow -models to TensorFlow Lite -[in TensorFlow 1.9 to TensorFlow 1.11](#pre_tensorflow_1.11) and -[prior to TensorFlow 1.9](#pre_tensorflow_1.9) - `TFLiteConverter` provides class methods based on the original format of the model. `TFLiteConverter.from_session()` is available for GraphDefs. `TFLiteConverter.from_saved_model()` is available for SavedModels. @@ -250,14 +244,13 @@ either install the nightly build with [Docker](https://www.tensorflow.org/install/docker), or [build the pip package from source](https://www.tensorflow.org/install/source). -### Converting models in TensorFlow 1.9 to TensorFlow 1.11 - -To convert TensorFlow models to TensorFlow Lite in TensorFlow 1.9 through -TensorFlow 1.11, use `TocoConverter`. `TocoConverter` is semantically -identically to `TFLiteConverter`. +### Converting models from TensorFlow 1.12 -### Converting models prior to TensorFlow 1.9 +Reference the following table to convert TensorFlow models to TensorFlow Lite in +and before TensorFlow 1.12. Run `help()` to get details of each API. -To convert TensorFlow models to TensorFlow Lite in TensorFlow 1.7 and TensorFlow -1.8, use the `toco_convert` function. Run `help(tf.lite.toco_convert)` -to get details about accepted parameters. +TensorFlow Version | Python API +------------------ | --------------------------------- +1.12 | `tf.contrib.lite.TFLiteConverter` +1.9-1.11 | `tf.contrib.lite.TocoConverter` +1.7-1.8 | `tf.contrib.lite.toco_convert` diff --git a/tensorflow/lite/g3doc/devguide.md b/tensorflow/lite/g3doc/devguide.md index 270cb8ce37..fdd02638f9 100644 --- a/tensorflow/lite/g3doc/devguide.md +++ b/tensorflow/lite/g3doc/devguide.md @@ -35,7 +35,7 @@ by suggesting contextually relevant messages. The model is built specifically fo memory constrained devices, such as watches and phones, and has been successfully used in Smart Replies on Android Wear. Currently, this model is Android-specific. -These pre-trained models are [available for download](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/g3doc/models.md) +These pre-trained models are [available for download](models.md). ### Re-train Inception-V3 or MobileNet for a custom data set @@ -57,51 +57,59 @@ A developer may choose to train a custom model using Tensorflow (see the [TensorFlow tutorials](../tutorials/) for examples of building and training models). If you have already written a model, the first step is to export this to a `tf.GraphDef` file. This is required because some formats do not store the -model structure outside the code, and we must communicate with other parts of the -framework. See -[Exporting the Inference Graph](https://github.com/tensorflow/models/blob/master/research/slim/README.md) -to create .pb file for the custom model. +model structure outside the code, and we must communicate with other parts of +the framework. See +[Exporting the Inference Graph](https://www.tensorflow.org/tutorials/keras/save_and_restore_models#save_the_entire_model) +to create file for the custom model. -TensorFlow Lite currently supports a subset of TensorFlow operators. Refer to the -[TensorFlow Lite & TensorFlow Compatibility Guide](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/g3doc/tf_ops_compatibility.md) +TensorFlow Lite currently supports a subset of TensorFlow operators. Refer to +the [TensorFlow Lite & TensorFlow Compatibility Guide](tf_ops_compatibility.md) for supported operators and their usage. This set of operators will continue to grow in future Tensorflow Lite releases. - ## 2. Convert the model format -The model generated (or downloaded) in the previous step is a *standard* -Tensorflow model and you should now have a .pb or .pbtxt `tf.GraphDef` file. -Models generated with transfer learning (re-training) or custom models must be -converted—but, we must first freeze the graph to convert the model to the -Tensorflow Lite format. This process uses several model formats: - -* `tf.GraphDef` (.pb) —A protobuf that represents the TensorFlow training or - computation graph. It contains operators, tensors, and variables definitions. -* *CheckPoint* (.ckpt) —Serialized variables from a TensorFlow graph. Since this - does not contain a graph structure, it cannot be interpreted by itself. -* `FrozenGraphDef` —A subclass of `GraphDef` that does not contain - variables. A `GraphDef` can be converted to a `FrozenGraphDef` by taking a - CheckPoint and a `GraphDef`, and converting each variable into a constant - using the value retrieved from the CheckPoint. -* `SavedModel` —A `GraphDef` and CheckPoint with a signature that labels - input and output arguments to a model. A `GraphDef` and CheckPoint can be - extracted from a `SavedModel`. -* *TensorFlow Lite model* (.tflite) —A serialized - [FlatBuffer](https://google.github.io/flatbuffers/) that contains TensorFlow - Lite operators and tensors for the TensorFlow Lite interpreter, similar to a - `FrozenGraphDef`. - -### Freeze Graph - -To use the `GraphDef` .pb file with TensorFlow Lite, you must have checkpoints -that contain trained weight parameters. The .pb file only contains the structure -of the graph. The process of merging the checkpoint values with the graph -structure is called *freezing the graph*. - -You should have a checkpoints folder or download them for a pre-trained model -(for example, -[MobileNets](https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet_v1.md)). +The [TensorFlow Lite Converter](convert/index.md) accepts the following file +formats: + +* `SavedModel` — A `GraphDef` and checkpoint with a signature that labels + input and output arguments to a model. See the documentation for converting + SavedModels using [Python](convert/python_api.md#basic_savedmodel) or using + the [command line](convert/cmdline_examples.md#savedmodel). +* `tf.keras` - A HDF5 file containing a model with weights and input and + output arguments generated by `tf.Keras`. See the documentation for + converting HDF5 models using + [Python](convert/python_api.md#basic_keras_file) or using the + [command line](convert/cmdline_examples.md#keras). +* `frozen tf.GraphDef` — A subclass of `tf.GraphDef` that does not contain + variables. A `GraphDef` can be converted to a `frozen GraphDef` by taking a + checkpoint and a `GraphDef`, and converting each variable into a constant + using the value retrieved from the checkpoint. Instructions on converting a + `tf.GraphDef` to a TensorFlow Lite model are described in the next + subsection. + +### Converting a tf.GraphDef + +TensorFlow models may be saved as a .pb or .pbtxt `tf.GraphDef` file. In order +to convert the `tf.GraphDef` file to TensorFlow Lite, the model must first be +frozen. This process invovles several file formats including the `frozen +GraphDef`: + +* `tf.GraphDef` (.pb or .pbtxt) — A protobuf that represents the TensorFlow + training or computation graph. It contains operators, tensors, and variables + definitions. +* *checkpoint* (.ckpt) — Serialized variables from a TensorFlow graph. Since + this does not contain a graph structure, it cannot be interpreted by itself. +* *TensorFlow Lite model* (.tflite) — A serialized + [FlatBuffer](https://google.github.io/flatbuffers/) that contains TensorFlow + Lite operators and tensors for the TensorFlow Lite interpreter. + +You must have checkpoints that contain trained weights. The `tf.GraphDef` file +only contains the structure of the graph. The process of merging the checkpoint +values with the graph structure is called *freezing the graph*. + +`tf.GraphDef` and checkpoint files for MobileNet models are available +[here](https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet_v1.md). To freeze the graph, use the following command (changing the arguments): @@ -113,69 +121,53 @@ freeze_graph --input_graph=/tmp/mobilenet_v1_224.pb \ --output_node_names=MobileNetV1/Predictions/Reshape_1 ``` -The `input_binary` flag must be enabled so the protobuf is read and written in -a binary format. Set the `input_graph` and `input_checkpoint` files. +Set the `input_binary` flag to `True` when reading a binary protobuf, a `.pb` +file. Set to `False` for a `.pbtxt` file. -The `output_node_names` may not be obvious outside of the code that built the -model. The easiest way to find them is to visualize the graph, either with -[TensorBoard](https://codelabs.developers.google.com/codelabs/tensorflow-for-poets-2/#3) -or `graphviz`. +Set `input_graph` and `input_checkpoint` to the respective filenames. The +`output_node_names` may not be obvious outside of the code that built the model. +The easiest way to find them is to visualize the graph, either with +[TensorBoard](https://www.tensorflow.org/guide/summaries_and_tensorboard) or +`graphviz`. The frozen `GraphDef` is now ready for conversion to the `FlatBuffer` format -(.tflite) for use on Android or iOS devices. For Android, the Tensorflow -Optimizing Converter tool supports both float and quantized models. To convert -the frozen `GraphDef` to the .tflite format: +(.tflite) for use on Android or iOS devices. For Android, the TensorFlow Lite +Converter tool supports both float and quantized models. To convert the frozen +`GraphDef` to the .tflite format use a command similar to the following: ``` -toco --input_file=$(pwd)/mobilenet_v1_1.0_224/frozen_graph.pb \ - --input_format=TENSORFLOW_GRAPHDEF \ - --output_format=TFLITE \ +tflite_convert \ --output_file=/tmp/mobilenet_v1_1.0_224.tflite \ - --inference_type=FLOAT \ - --input_type=FLOAT \ + --graph_def_file=/tmp/mobilenet_v1_0.50_128/frozen_graph.pb \ --input_arrays=input \ - --output_arrays=MobilenetV1/Predictions/Reshape_1 \ - --input_shapes=1,224,224,3 + --output_arrays=MobilenetV1/Predictions/Reshape_1 ``` -The `input_file` argument should reference the frozen `GraphDef` file -containing the model architecture. The [frozen_graph.pb](https://storage.googleapis.com/download.tensorflow.org/models/mobilenet_v1_1.0_224_frozen.tgz) -file used here is available for download. `output_file` is where the TensorFlow -Lite model will get generated. The `input_type` and `inference_type` -arguments should be set to `FLOAT`, unless converting a -quantized model. -Setting the `input_array`, `output_array`, and `input_shape` arguments are not as -straightforward. The easiest way to find these values is to explore the graph -using Tensorboard. Reuse the arguments for specifying the output nodes for -inference in the `freeze_graph` step. - -It is also possible to use the Tensorflow Optimizing Converter with protobufs -from either Python or from the command line (see the -[toco_from_protos.py](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/toco/python/toco_from_protos.py) -example). This allows you to integrate the conversion step into the model design -workflow, ensuring the model is easily convertible to a mobile inference graph. -For example: - -```python -import tensorflow as tf - -img = tf.placeholder(name="img", dtype=tf.float32, shape=(1, 64, 64, 3)) -val = img + tf.constant([1., 2., 3.]) + tf.constant([1., 4., 4.]) -out = tf.identity(val, name="out") - -with tf.Session() as sess: - tflite_model = tf.lite.toco_convert(sess.graph_def, [img], [out]) - open("converteds_model.tflite", "wb").write(tflite_model) -``` +The +[frozen_graph.pb](https://storage.googleapis.com/download.tensorflow.org/models/mobilenet_v1_1.0_224_frozen.tgz) +file used here is available for download. Setting the `input_array` and +`output_array` arguments is not straightforward. The easiest way to find these +values is to explore the graph using +[TensorBoard](https://www.tensorflow.org/guide/summaries_and_tensorboard). Reuse +the arguments for specifying the output nodes for inference in the +`freeze_graph` step. + +### Full converter reference -For usage, see the Tensorflow Optimizing Converter -[command-line examples](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/toco/g3doc/cmdline_examples.md). +The [TensorFlow Lite Converter](convert/index.md) can be +[Python](convert/python_api.md) or from the +[command line](convert/cmdline_examples.md). This allows you to integrate the +conversion step into the model design workflow, ensuring the model is easy to +convert to a mobile inference graph. -Refer to the -[Ops compatibility guide](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/g3doc/tf_ops_compatibility.md) -for troubleshooting help, and if that doesn't help, please +### Ops compatibility + +Refer to the [ops compatibility guide](tf_ops_compatibility.md) for +troubleshooting help, and if that doesn't help, please [file an issue](https://github.com/tensorflow/tensorflow/issues). +### Graph vizualization tool + The [development repo](https://github.com/tensorflow/tensorflow) contains a tool to visualize TensorFlow Lite models after conversion. To build the [visualize.py](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/tools/visualize.py) @@ -212,8 +204,8 @@ installing TensorFlow on Android and setting up `bazel` and Android Studio. ### iOS To integrate a TensorFlow model in an iOS app, see the -[TensorFlow Lite for iOS](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/g3doc/ios.md) -guide and iOS demo guide. +[TensorFlow Lite for iOS](ios.md) guide and iOS demo +guide. #### Core ML support @@ -227,6 +219,5 @@ devices. To use the converter, refer to the ### Raspberry Pi Compile Tensorflow Lite for a Raspberry Pi by following the -[RPi build instructions](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/g3doc/rpi.md) -This compiles a static library file (`.a`) used to build your app. There are -plans for Python bindings and a demo app. +[RPi build instructions](rpi.md) This compiles a static library file (`.a`) used +to build your app. There are plans for Python bindings and a demo app. diff --git a/tensorflow/lite/g3doc/models.md b/tensorflow/lite/g3doc/models.md index 537e285490..62b3f17c79 100644 --- a/tensorflow/lite/g3doc/models.md +++ b/tensorflow/lite/g3doc/models.md @@ -76,8 +76,11 @@ Mobilenet_V1_1.0_128_quant | [paper](https://arxiv.org/pdf/1712.05877.pdf), [tf Mobilenet_V1_1.0_160_quant | [paper](https://arxiv.org/pdf/1712.05877.pdf), [tflite&pb](http://download.tensorflow.org/models/mobilenet_v1_2018_08_02/mobilenet_v1_1.0_160_quant.tgz) | 4.3 Mb | 66.9% | 86.7% | 37.4 ms Mobilenet_V1_1.0_192_quant | [paper](https://arxiv.org/pdf/1712.05877.pdf), [tflite&pb](http://download.tensorflow.org/models/mobilenet_v1_2018_08_02/mobilenet_v1_1.0_192_quant.tgz) | 4.3 Mb | 69.1% | 88.1% | 51.9 ms Mobilenet_V1_1.0_224_quant | [paper](https://arxiv.org/pdf/1712.05877.pdf), [tflite&pb](http://download.tensorflow.org/models/mobilenet_v1_2018_08_02/mobilenet_v1_1.0_224_quant.tgz) | 4.3 Mb | 70.0% | 89.0% | 70.2 ms -Mobilenet_v2_1.0_224_quant | [paper](https://arxiv.org/abs/1806.08342), [tflite&pb](http://download.tensorflow.org/models/tflite_11_05_08/mobilenet_v2_1.0_224_quant.tgz) | 3.4 Mb | 70.8% | 89.9% | 80.3 ms -Inception_v3_quant | [paper](https://arxiv.org/abs/1806.08342),[tflite&pb](http://download.tensorflow.org/models/tflite_11_05_08/inception_v3_quant.tgz) | 23 Mb | 77.5% | 93.7% | 637 ms +Mobilenet_V2_1.0_224_quant | [paper](https://arxiv.org/abs/1806.08342), [tflite&pb](http://download.tensorflow.org/models/tflite_11_05_08/mobilenet_v2_1.0_224_quant.tgz) | 3.4 Mb | 70.8% | 89.9% | 80.3 ms +Inception_V1_quant | [paper](https://arxiv.org/abs/1409.4842), [tflite&pb](http://download.tensorflow.org/models/inception_v1_224_quant_20181026.tgz) | 6.4 Mb | 70.1% | 89.8% | 154.5 ms +Inception_V2_quant | [paper](https://arxiv.org/abs/1512.00567), [tflite&pb](http://download.tensorflow.org/models/inception_v2_224_quant_20181026.tgz) | 11 Mb | 73.5% | 91.4% | 235.0 ms +Inception_V3_quant | [paper](https://arxiv.org/abs/1806.08342),[tflite&pb](http://download.tensorflow.org/models/tflite_11_05_08/inception_v3_quant.tgz) | 23 Mb | 77.5% | 93.7% | 637 ms +Inception_V4_quant | [paper](https://arxiv.org/abs/1602.07261), [tflite&pb](http://download.tensorflow.org/models/inception_v4_299_quant_20181026.tgz) | 41 Mb | 79.5% | 93.9% | 1250.8 ms ## Other models diff --git a/tensorflow/lite/g3doc/tf_ops_compatibility.md b/tensorflow/lite/g3doc/tf_ops_compatibility.md index b0dfb0fed1..dcfda72137 100644 --- a/tensorflow/lite/g3doc/tf_ops_compatibility.md +++ b/tensorflow/lite/g3doc/tf_ops_compatibility.md @@ -1,4 +1,3 @@ - # TensorFlow Lite & TensorFlow Compatibility Guide TensorFlow Lite supports a number of TensorFlow operations used in common @@ -75,6 +74,7 @@ counterparts: 0D tensor* * [tf.squeeze](https://www.tensorflow.org/api_docs/python/tf/squeeze) - *as long as axis is not provided* +* [tf.squared_difference](https://www.tensorflow.org/versions/master/api_docs/python/tf/squared_difference) * [tf.strided_slice](https://www.tensorflow.org/api_docs/python/tf/strided_slice) - *as long as ellipsis_mask and new_axis_mask are not used* * [tf.transpose](https://www.tensorflow.org/versions/master/api_docs/python/tf/transpose) - @@ -139,6 +139,17 @@ following common ops are not supported at the moment: The following TensorFlow Lite operations are fully supported and used in place of the TensorFlow operations listed above: +**ABS** + +``` +Inputs { + 0: a tensor +} +Outputs { + 0: elementwise abs of the input +} +``` + **ADD** ``` @@ -154,6 +165,30 @@ Options { } ``` +**ARG_MAX** + +``` +Inputs { + 0: a tensor + 1: a tensor +} +Outputs { + 0: A tensor of indices of maximum values. +} +``` + +**ARG_MIN** + +``` +Inputs { + 0: a tensor + 1: a tensor +} +Outputs { + 0: A tensor of indices of minium values. +} +``` + **AVERAGE_POOL_2D** ``` @@ -280,6 +315,18 @@ Outputs { } ``` +**FILL** + +``` +Inputs { + 0: a 1D tensor + 1: a 0D (scalar) tensor +} +Outputs { + 0: A tensor of shape `tensor 0` filled with the value in `tensor 1`. +} +``` + **FLOOR** ``` @@ -291,6 +338,30 @@ outputs: { } ``` +**FLOOR_DIV** + +``` +Inputs { + 0: a tensor + 1: a tensor +} +Outputs { + 0: result of computing element-wise floor of `tensor 0` divided by `tensor 1`. +} +``` + +**FLOOR_MOD** + +``` +Inputs { + 0: a tensor + 1: a tensor +} +Outputs { + 0: result of computing element-wise floor of `tensor 0` modulo `tensor 1`. +} +``` + **FULLY_CONNECTED** ``` @@ -378,6 +449,34 @@ Options { } ``` +**LEAKY_RELU** + +``` +Inputs { + 0: a tensor +} +Outputs { + 0: a tensor equivalent to max(input, input * alpha) +} +Options { + alpha: slope of the activation at x < 0 (provided alpha <= 1) +} +``` + +**LEAKY_RELU** + +``` +Inputs { + 0: a tensor +} +Outputs { + 0: a tensor equivalent to max(input, input * alpha) +} +Options { + alpha +} +``` + **LESS** ``` @@ -421,6 +520,18 @@ Options { } ``` +**LOGICAL_OR** + +``` +Inputs { + 0: a list of tensors. + 1: a list of tensors. +} +Outputs { + 0: A tensor of logical_or output tensors. +} +``` + **LOGISTIC** ``` @@ -498,6 +609,18 @@ Outputs { } ``` +**PACK** + +``` +Inputs { + 0: a list of tensors. + 1: an integer. +} +Outputs { + 0: A tensor of stacked tensors. +} +``` + **PAD** ``` @@ -539,6 +662,35 @@ Outputs { } ``` +**POW** + +``` +Inputs { + 0: a tensor + 1: a tensor +} +Outputs { + 0: elementwise pow of the input tensors +} +``` + +**RANGE** + +``` +Inputs { + 0: a 0D (scalar) tensor + 1: a 0D (scalar) tensor + 2: a 0D (scalar) tensor +} +Outputs { + 0: A 1D tensor of type `dtype` defined by a sequence where `tensor 0` is the + start, `tensor 1` is the limit, and `tensor 2` is the delta. +} +Options { + dtype +} +``` + **RELU** ``` @@ -587,6 +739,22 @@ Options { } ``` +**RESIZE_NEAREST_NEIGHBOR** + +``` +Inputs { + 0: a 4D tensor + 1: a 1D tensor with 2 elements +} +Outputs { + 0: A tensor of type `tensor 0` resized according to `tensor 1` heigh/width values + using nearest neighbors interpolation. +} +Options { + align_corners +} +``` + **RSQRT** ``` @@ -698,6 +866,22 @@ Options { } ``` +**SPLIT_V** + +``` +Inputs { + 0: tensor (input) + 1: 1-D tensor (size_splits) + 2: 0-D tensor (axis) +} +Outputs { + 0-N: subtensors built from the input tensors +} +Options { + num_splits: Specifies number of outputs +} +``` + **SQRT** ``` @@ -781,66 +965,6 @@ Outputs { } ``` -**POW** - -``` -Inputs { - 0: a tensor - 1: a tensor -} -Outputs { - 0: elementwise pow of the input tensors -} -``` - -**ARG_MAX** - -``` -Inputs { - 0: a tensor - 1: a tensor -} -Outputs { - 0: A tensor of indices of maximum values. -} -``` - -**ARG_MIN** - -``` -Inputs { - 0: a tensor - 1: a tensor -} -Outputs { - 0: A tensor of indices of minium values. -} -``` - -**PACK** - -``` -Inputs { - 0: a list of tensors. - 1: an integer. -} -Outputs { - 0: A tensor of stacked tensors. -} -``` - -**LOGICAL_OR** - -``` -Inputs { - 0: a list of tensors. - 1: a list of tensors. -} -Outputs { - 0: A tensor of logical_or output tensors. -} -``` - **UNPACK** ``` @@ -854,26 +978,26 @@ Outputs { } ``` -**FLOOR_DIV** +**ZEROS_LIKE** ``` Inputs { - 0: a list of tensors. - 1: a list of tensors. + 0: a tensor } Outputs { - 0: A tensor of floor_div output tensors. + 0: A tensor of the same shape and type as x but filled with zeros } ``` -**ZEROS_LIKE** +**FILL** ``` Inputs { - 0: a tensor + 0: A Tensor. Must be one of the following types: int32, int64. 1-D. Represents the shape of the output tensor. + 1: A Tensor. 0-D (scalar). Value to fill the returned tensor. } Outputs { - 0: A tensor of the same shape and type as x but filled with zeros + 0: A tensor of the same type as value (input1). } ``` diff --git a/tensorflow/lite/g3doc/using_select_tf_ops.md b/tensorflow/lite/g3doc/using_select_tf_ops.md new file mode 100644 index 0000000000..aa51f58baa --- /dev/null +++ b/tensorflow/lite/g3doc/using_select_tf_ops.md @@ -0,0 +1,249 @@ +# [Experimental] Using TensorFlow Lite with select TensorFlow ops + +The TensorFlow Lite builtin op library has grown rapidly, and will continue to +grow, but there remains a long tail of TensorFlow ops that are not yet natively +supported by TensorFlow Lite . These unsupported ops can be a point of friction +in the TensorFlow Lite model conversion process. To that end, the team has +recently been working on an experimental mechanism for reducing this friction. + +This document outlines how to use TensorFlow Lite with select TensorFlow ops. +*Note that this feature is experimental and is under active development.* As you +use this feature, keep in mind the [known limitations](#known-limitations), and +please send feedback about models that work and issues you are facing to +tflite@tensorflow.org. + +TensorFlow Lite will continue to have +[TensorFlow Lite builtin ops](tf_ops_compatibility.md) optimized for mobile and +embedded devices. However, TensorFlow Lite models can now use a subset of +TensorFlow ops when TFLite builtin ops are not sufficient. + +Models converted with TensorFlow ops will require a TensorFlow Lite interpreter +that has a larger binary size than the interpreter with only TFLite builtin ops. +Additionally, performance optimizations will not be available for any TensorFlow +ops in the TensorFlow Lite model. + +This document outlines how to [convert](#converting-the-model) and +[run](#running-the-model) a TFLite model with TensorFlow ops on your platform of +choice. It also discusses some [known limitations](#known-limitations), the +[future plans](#future-plans) for this feature, and basic +[performance and size metrics](#metrics). + +## Converting the model + +To convert a TensorFlow model to a TensorFlow Lite model with TensorFlow ops, +use the `target_ops` argument in the +[TensorFlow Lite converter](https://www.tensorflow.org/lite/convert/). The +following values are valid options for `target_ops`: + +* `TFLITE_BUILTINS` - Converts models using TensorFlow Lite builtin ops. +* `SELECT_TF_OPS` - Converts models using TensorFlow ops. The exact subset of + supported ops can be found in the whitelist at + `lite/toco/tflite/whitelisted_flex_ops.cc`. + +The recommended approach is to convert the model with `TFLITE_BUILTINS`, then +with both `TFLITE_BUILTINS,SELECT_TF_OPS`, and finally with only +`SELECT_TF_OPS`. Using both options (i.e. `TFLITE_BUILTINS,SELECT_TF_OPS`) +creates models with TensorFlow Lite ops where possible. Using only +`SELECT_TF_OPS` is useful when the model contains TensorFlow ops that are only +partially supported by TensorFlow Lite, and one would like to avoid those +limitations. + +The following example shows how to use `target_ops` in the +[`TFLiteConverter`](https://www.tensorflow.org/lite/convert/python_api) Python +API. + +``` +import tensorflow as tf + +converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir) +converter.target_ops = [tf.lite.OpsSet.TFLITE_BUILTINS, + tf.lite.OpsSet.SELECT_TF_OPS] +tflite_model = converter.convert() +open("converted_model.tflite", "wb").write(tflite_model) +``` + +The following example shows how to use `target_ops` in the +[`tflite_convert`](https://www.tensorflow.org/lite/convert/cmdline_examples) +command line tool. + +``` +tflite_convert \ + --output_file=/tmp/foo.tflite \ + --graph_def_file=/tmp/foo.pb \ + --input_arrays=input \ + --output_arrays=MobilenetV1/Predictions/Reshape_1 \ + --target_ops=TFLITE_BUILTINS,SELECT_TF_OPS +``` + +When building and running `tflite_convert` directly with `bazel`, please pass +`--define=with_select_tf_ops=true` as an additional argument. + +``` +bazel run --define=with_select_tf_ops=true tflite_convert -- \ + --output_file=/tmp/foo.tflite \ + --graph_def_file=/tmp/foo.pb \ + --input_arrays=input \ + --output_arrays=MobilenetV1/Predictions/Reshape_1 \ + --target_ops=TFLITE_BUILTINS,SELECT_TF_OPS +``` + +## Running the model + +When using a TensorFlow Lite model that has been converted with support for +select TensorFlow ops, the client must also use a TensorFlow Lite runtime that +includes the necessary library of TensorFlow ops. + +### Android AAR + +A new Android AAR target with select TensorFlow ops has been added for +convenience. Assuming a working TensorFlow Lite +build environment, build the Android AAR with select TensorFlow ops as +follows: + +```sh +bazel build --cxxopt='--std=c++11' -c opt \ + --config=android_arm --config=monolithic \ + //tensorflow/lite/java:tensorflow-lite-with-select-tf-ops +``` + +This will generate an AAR file in `bazel-genfiles/tensorflow/lite/java/`. From +there, you can either import the AAR directly into your project, or publish the +custom AAR to your local Maven repository: + +```sh +mvn install:install-file \ + -Dfile=bazel-genfiles/tensorflow/lite/java/tensorflow-lite-with-select-tf-ops.aar \ + -DgroupId=org.tensorflow \ + -DartifactId=tensorflow-lite-with-select-tf-ops -Dversion=0.1.100 -Dpackaging=aar +``` + +Finally, in your app's `build.gradle`, ensure you have the `mavenLocal()` +dependency and replace the standard TensorFlow Lite dependency with the one that +has support for select TensorFlow ops: + +``` +allprojects { + repositories { + jcenter() + mavenLocal() + } +} + +dependencies { + compile 'org.tensorflow:tensorflow-lite-with-select-tf-ops:0.1.100' +} +``` + +### iOS + +With XCode Command Line Tools installed, TensorFlow Lite with select TensorFlow +ops support can be built with the following command: + +```sh +tensorflow/contrib/makefile/build_all_ios_with_tflite.sh +``` + +This will generate the required static linking libraries in the +`tensorflow/contrib/makefile/gen/lib/` directory. + +The TensorFlow Lite camera example app can be used to test this. A new +TensorFlow Lite XCode project with support for select TensorFlow ops has been +added to +`tensorflow/lite/examples/ios/camera/tflite_camera_example_with_select_tf_ops.xcodeproj`. + +To use this feature in a your own project, either clone the example project or +set the project settings for a new or existing project to the following: + +* In Build Phases -> Link Binary With Libraries, add the static libraries + under `tensorflow/contrib/makefile/gen/lib/` directory: + * `libtensorflow-lite.a` + * `libprotobuf.a` + * `nsync.a` +* In Build Settings -> Header Search Paths, add the following directories: + * `tensorflow/lite/` + * `tensorflow/contrib/makefile/downloads/flatbuffer/include` + * `tensorflow/contrib/makefile/downloads/eigen` +* In Build Settings -> Other Linker Flags, add `-force_load + tensorflow/contrib/makefile/gen/lib/libtensorflow-lite.a`. + +A CocoaPod with support for select TensorFlow ops will also be released in the +future. + +### C++ + +When building TensorFlow Lite libraries using the bazel pipeline, the additional +TensorFlow ops library can be included and enabled as follows: + +* Enable monolithic builds if necessary by adding the `--config=monolithic` + build flag. +* Do one of the following: + * Include the `--define=with_select_tf_ops=true` build flag in the `bazel + build` invocation when building TensorFlow Lite. + * Add the TensorFlow ops delegate library dependency to the build + dependencies: `tensorflow/lite/delegates/flex:delegate`. + +Note that the necessary `TfLiteDelegate` will be installed automatically when +creating the interpreter at runtime as long as the delegate is linked into the +client library. It is not necessary to explicitly install the delegate instance +as is typically required with other delegate types. + +### Python pip Package + +Python support is actively under development. + +## Metrics + +### Performance + +When using a mixture of both builtin and select TensorFlow ops, all of the same +TensorFlow Lite optimizations and optimized builtin kernels will be be available +and usable with the converted model. For the TensorFlow ops, performance should +generally be comparable to that of +[TensorFlow Mobile](https://www.tensorflow.org/lite/tfmobile/). + +The following table describes the average time taken to run inference on +MobileNet on a Pixel 2. The listed times are an average of 100 runs. These +targets were built for Android using the flags: `--config=android_arm64 -c opt`. + +Build | Time (milliseconds) +------------------------------------ | ------------------- +Only built-in ops (`TFLITE_BUILTIN`) | 260.7 +Using only TF ops (`SELECT_TF_OPS`) | 264.5 + +### Binary Size + +The following table describes the binary size of TensorFlow Lite for each build. +These targets were built for Android using `--config=android_arm -c opt`. + +Build | C++ Binary Size | Android APK Size +--------------------- | --------------- | ---------------- +Only built-in ops | 796 KB | 561 KB +Built-in ops + TF ops | 23.0 MB | 8.0 MB + +## Known Limitations + +The following is a list of some of the known limitations: + +* Control flow ops are not yet supported. +* The + [`post_training_quantization`](https://www.tensorflow.org/performance/post_training_quantization) + flag is currently not supported for TensorFlow ops so it will not quantize + weights for any TensorFlow ops. In models with both TensorFlow Lite builtin + ops and TensorFlow ops, the weights for the builtin ops will be quantized. +* Ops that require explicit initialization from resources, like HashTableV2, + are not yet supported. +* Certain TensorFlow ops may not support the full set of input/output types + that are typically available on stock TensorFlow. + +## Future Plans + +The following is a list of improvements to this pipeline that are in progress: + +* *Selective registration* - There is work being done to make it simple to + generate TFLite interpreter binaries that only contain the TensorFlow ops + required for a particular set of models. +* *Improved usability* - The conversion process will be simplified to only + require a single pass through the converter. Additionally, pre-built Android + AAR and iOS CocoaPod binaries will be provided. +* *Improved performance* - There is work being done to ensure TensorFlow Lite + with TensorFlow ops has performance parity to TensorFlow Mobile. diff --git a/tensorflow/lite/interpreter.cc b/tensorflow/lite/interpreter.cc index c90fc3be87..e2129ed46d 100644 --- a/tensorflow/lite/interpreter.cc +++ b/tensorflow/lite/interpreter.cc @@ -20,7 +20,6 @@ limitations under the License. #include #include -#include "tensorflow/lite/arena_planner.h" #include "tensorflow/lite/c/c_api_internal.h" #include "tensorflow/lite/context_util.h" #include "tensorflow/lite/core/api/error_reporter.h" @@ -32,110 +31,15 @@ limitations under the License. #include "tensorflow/lite/util.h" namespace tflite { -namespace { - -TfLiteStatus ReportOpError(TfLiteContext* context, const TfLiteNode& node, - const TfLiteRegistration& registration, - int node_index, const char* message) { - context->ReportError( - context, "Node number %d (%s) %s.\n", node_index, - registration.custom_name - ? registration.custom_name - : EnumNameBuiltinOperator( - static_cast(registration.builtin_code)), - message); - return kTfLiteError; -} - -// Stub method which returns kTfLiteError when the function is forbidden. -// We're registrating this function to several different function to save -// compiled binary size. Please note the restrictions: -// * The type of first parameter have to be `TfLiteContext*`. -// * All paramteters must be trivailly destructible. (E.g. No C++ class) -TfLiteStatus ForbiddenContextFunction(TfLiteContext* context, ...) { - context->ReportError(context, - "The function is forbidden if not calling in delegate."); - return kTfLiteError; -} - -// Set the ForbiddenContextFunction to a compatible function pointer. -template -void SetForbiddenContextFunction(FunctionType* func) { - *func = reinterpret_cast(ForbiddenContextFunction); -} - -// Returns true if at least one tensor in the given list is kTfLiteDynamic. -template -bool HasDynamicTensorImpl(const TfLiteContext& context, - const TensorIntArray& int_array) { - for (int i : int_array) { - const TfLiteTensor& tensor = context.tensors[i]; - if (tensor.allocation_type == kTfLiteDynamic) { - return true; - } - } - return false; -} - -} // namespace - -// A trivial implementation of GraphInfo around the Interpreter. -// NOTE: this interpreter info represents the subset of the -// graph that is executed according to execution plan. Thus, -// the indices are execution plan indices rather than raw node -// indices. -class InterpreterInfo : public GraphInfo { - public: - explicit InterpreterInfo(Interpreter* interpreter) - : interpreter_(interpreter) {} - - size_t num_tensors() const override { return interpreter_->tensors_size(); } - TfLiteTensor* tensor(size_t index) override { - return interpreter_->tensor(index); - } - size_t num_nodes() const override { - return interpreter_->execution_plan().size(); - } - const TfLiteNode& node(size_t index) const override { - int node_index = interpreter_->execution_plan()[index]; - return interpreter_->node_and_registration(node_index)->first; - } - const std::vector& inputs() const override { - return interpreter_->inputs(); - } - const std::vector& outputs() const override { - return interpreter_->outputs(); - } - const std::vector& variables() const override { - return interpreter_->variables(); - } - - public: - Interpreter* interpreter_; -}; Interpreter::Interpreter(ErrorReporter* error_reporter) : error_reporter_(error_reporter ? error_reporter : DefaultErrorReporter()) { - context_.impl_ = static_cast(this); - context_.ResizeTensor = ResizeTensor; - context_.ReportError = ReportError; - context_.AddTensors = AddTensors; - context_.tensors = nullptr; - context_.tensors_size = 0; - context_.allow_fp32_relax_to_fp16 = false; - context_.recommended_num_threads = -1; - context_.GetExternalContext = GetExternalContext; - context_.SetExternalContext = SetExternalContext; - - // Invalid to call these these except from TfLiteDelegate - SwitchToKernelContext(); + // There's always at least 1 subgraph which is the primary subgraph. + AddSubgraphs(1); + context_ = primary_subgraph().context(); // Reserve some space for the tensors to avoid excessive resizing. - tensors_.reserve(kTensorsReservedCapacity); - nodes_and_registration_.reserve(kTensorsReservedCapacity); - next_execution_plan_index_to_prepare_ = 0; - for (int i = 0; i < kTfLiteMaxExternalContexts; ++i) { external_contexts_[i] = nullptr; } @@ -143,670 +47,88 @@ Interpreter::Interpreter(ErrorReporter* error_reporter) UseNNAPI(false); } -Interpreter::~Interpreter() { - for (auto& nodeAndReg : nodes_and_registration_) { - TfLiteNode& node = nodeAndReg.first; - TfLiteIntArrayFree(node.inputs); - TfLiteIntArrayFree(node.outputs); - TfLiteIntArrayFree(node.temporaries); - if (node.builtin_data) free(node.builtin_data); - OpFree(nodeAndReg.second, node.user_data); - node.builtin_data = nullptr; - } - - for (size_t i = 0; i < context_.tensors_size; i++) { - TfLiteTensor* tensor = &context_.tensors[i]; - if (tensor->buffer_handle != kTfLiteNullBufferHandle && - tensor->delegate->FreeBufferHandle != nullptr) { - tensor->delegate->FreeBufferHandle(&context_, tensor->delegate, - &tensor->buffer_handle); - } - TfLiteTensorFree(tensor); - } -} - -TfLiteStatus Interpreter::ReplaceNodeSubsetsWithDelegateKernels( - TfLiteContext* context, TfLiteRegistration registration, - const TfLiteIntArray* nodes_to_replace, TfLiteDelegate* delegate) { - return static_cast(context->impl_) - ->ReplaceNodeSubsetsWithDelegateKernels(registration, nodes_to_replace, - delegate); -} - -namespace { - -// Copy a std::vector to an existing TfLiteIntArray. -// This is a low-level data manipulation function, and it's caller's -// responsibility to ensure TfLiteIntArray has enough size. -void CopyVectorToTfLiteIntArray(const std::vector& vec, - TfLiteIntArray* arr) { - arr->size = vec.size(); - memcpy(arr->data, vec.data(), sizeof(int) * arr->size); -} - -// This function allocates a continuous memory space that contains a -// TfLiteDelegateParams followed by a several TfLiteIntArray. -// When calling `free` at TfLiteDelegateParams*, all the allocated space -// will be freed together. -// -// +-----------------------------------+ -// | TfLiteDelegateParams | -// | TfLiteDelegate* delegate; | -// | TfLiteIntArray* nodes_to_replace; |--\ -// | TfLiteIntArray* input_tensors; |--+--\ -// | TfLiteIntArray* output_tensors; |--+--+--\ -// +-----------------------------------+ | | | -// | TfLiteIntArray (variable size) |<-/ | | -// +-----------------------------------+ | | -// | TfLiteIntArray (variable size) |<----/ | -// +-----------------------------------+ | -// | TfLiteIntArray (variable size) |<-------/ -// +-----------------------------------+ -TfLiteDelegateParams* CreateDelegateParams(TfLiteDelegate* delegate, - const NodeSubset& node_subset) { - // Step 1: Calculate the allocation size. - int allocation_size = sizeof(TfLiteDelegateParams); - - int nodes_to_replace_size = - TfLiteIntArrayGetSizeInBytes(node_subset.nodes.size()); - allocation_size += nodes_to_replace_size; - - int input_tensors_size = - TfLiteIntArrayGetSizeInBytes(node_subset.input_tensors.size()); - allocation_size += input_tensors_size; - - int output_tensors_size = - TfLiteIntArrayGetSizeInBytes(node_subset.output_tensors.size()); - allocation_size += output_tensors_size; - - // Step 2: Allocate the memory. - // Use `char*` for conveniently step through the allocated space by bytes. - char* allocation = reinterpret_cast(malloc(allocation_size)); - - // Step 3: Fill all data structures structures. - TfLiteDelegateParams* params = - reinterpret_cast(allocation); - params->delegate = delegate; - allocation += sizeof(TfLiteDelegateParams); - - params->nodes_to_replace = reinterpret_cast(allocation); - CopyVectorToTfLiteIntArray(node_subset.nodes, params->nodes_to_replace); - allocation += nodes_to_replace_size; - - params->input_tensors = reinterpret_cast(allocation); - CopyVectorToTfLiteIntArray(node_subset.input_tensors, params->input_tensors); - allocation += input_tensors_size; - - params->output_tensors = reinterpret_cast(allocation); - CopyVectorToTfLiteIntArray(node_subset.output_tensors, - params->output_tensors); - allocation += output_tensors_size; - - return params; -} - -} // namespace - -TfLiteStatus Interpreter::ReplaceNodeSubsetsWithDelegateKernels( - TfLiteRegistration registration, const TfLiteIntArray* nodes_to_replace, - TfLiteDelegate* delegate) { - // Annotate the registration as DELEGATE op. - registration.builtin_code = BuiltinOperator_DELEGATE; - - // Analyze the graph to find all independent node_subsets that are either - // fully not-this-delegate or this-delegate computation. - InterpreterInfo info(this); - std::vector node_subsets; - PartitionGraphIntoIndependentNodeSubsets(&info, nodes_to_replace, - &node_subsets); - - execution_plan_.clear(); - for (auto& node_subset : node_subsets) { - // Subsets calimed by the delegate should have a "macro" op created, the - // other node_subsets (kTfNonPartition) just have their nodes added back to - // the execution plan. - switch (node_subset.type) { - case NodeSubset::kTfNonPartition: - for (auto it = node_subset.nodes.begin(); it != node_subset.nodes.end(); - ++it) { - execution_plan_.push_back(*it); - } - break; - case NodeSubset::kTfPartition: { - int node_index; - - TfLiteDelegateParams* params = - CreateDelegateParams(delegate, node_subset); - TF_LITE_ENSURE_STATUS(AddNodeWithParameters( - node_subset.input_tensors, node_subset.output_tensors, nullptr, 0, - params, ®istration, &node_index)); - - // Initialize the output tensors's delegate-related fields. - for (int tensor_index : node_subset.output_tensors) { - TfLiteTensor* tensor = &tensors_[tensor_index]; - TF_LITE_ENSURE(&context_, tensor->delegate == nullptr || - tensor->delegate == delegate); - tensor->delegate = delegate; - } - - // Associate the node with the delegate. - TfLiteNode* node = &nodes_and_registration_[node_index].first; - node->delegate = delegate; - } break; - case NodeSubset::kTfUnexplored: - return kTfLiteError; - break; - } - } - return kTfLiteOk; -} - -TfLiteExternalContext* Interpreter::GetExternalContext( - TfLiteExternalContextType type) { - if (type >= 0 && type < kTfLiteMaxExternalContexts) { - return external_contexts_[type]; - } - return nullptr; -} - -TfLiteExternalContext* Interpreter::GetExternalContext( - struct TfLiteContext* context, TfLiteExternalContextType type) { - return static_cast(context->impl_)->GetExternalContext(type); -} +Interpreter::~Interpreter() {} void Interpreter::SetExternalContext(TfLiteExternalContextType type, TfLiteExternalContext* ctx) { - if (type >= 0 && type < kTfLiteMaxExternalContexts) { - external_contexts_[type] = ctx; - } -} - -void Interpreter::SetExternalContext(struct TfLiteContext* context, - TfLiteExternalContextType type, - TfLiteExternalContext* ctx) { - return static_cast(context->impl_) - ->SetExternalContext(type, ctx); -} - -// Gets an TfLiteIntArray* representing the execution plan. The interpreter owns -// this memory and it is only guaranteed to exist during the invocation of the -// delegate prepare. -TfLiteStatus Interpreter::GetExecutionPlan(TfLiteIntArray** execution_plan) { - // TODO(aselle): Do not make a copy here - plan_cache_.reset(TfLiteIntArrayCreate(execution_plan_.size())); - *execution_plan = plan_cache_.get(); - static_assert(sizeof(plan_cache_->data[0]) == sizeof(execution_plan_[0]), - "TfLiteIntArray and execution_plan do not contain same type."); - std::memcpy(plan_cache_->data, execution_plan_.data(), - sizeof(plan_cache_->data[0]) * execution_plan_.size()); - return kTfLiteOk; -} - -// WARNING: This is an experimental interface that is subject to change. -// Entry point for C node plugin API to get the execution plan -TfLiteStatus Interpreter::GetExecutionPlan(struct TfLiteContext* context, - TfLiteIntArray** execution_plan) { - return static_cast(context->impl_) - ->GetExecutionPlan(execution_plan); + primary_subgraph().SetExternalContext(type, ctx); } TfLiteStatus Interpreter::SetInputs(std::vector inputs) { - TF_LITE_ENSURE_OK(&context_, - CheckTensorIndices("inputs", inputs.data(), inputs.size())); - inputs_ = std::move(inputs); - return kTfLiteOk; + return primary_subgraph().SetInputs(inputs); } TfLiteStatus Interpreter::SetOutputs(std::vector outputs) { - TF_LITE_ENSURE_OK( - &context_, CheckTensorIndices("outputs", outputs.data(), outputs.size())); - outputs_ = std::move(outputs); - return kTfLiteOk; + return primary_subgraph().SetOutputs(outputs); } TfLiteStatus Interpreter::SetVariables(std::vector variables) { - TF_LITE_ENSURE_OK(&context_, CheckTensorIndices("variables", variables.data(), - variables.size())); - variables_ = std::move(variables); - return kTfLiteOk; -} - -TfLiteStatus Interpreter::CheckTensorIndices(const char* label, - const int* indices, int length) { - // Making sure kOptionalTensor is not re-defined to something other than -1. - static_assert(kOptionalTensor == -1, "kOptionalTensor should be defined -1"); - - for (int i = 0; i < length; i++) { - int index = indices[i]; - // Continue if index == kOptionalTensor before additional comparisons below, - // size_t(-1) is always >= context_tensors_size. - if (index == kOptionalTensor) { - continue; - } - if (index < 0 || static_cast(index) >= context_.tensors_size) { - ReportError(&context_, "Invalid tensor index %d in %s\n", index, label); - consistent_ = false; - return kTfLiteError; - } - } - return kTfLiteOk; -} - -TfLiteStatus Interpreter::BytesRequired(TfLiteType type, const int* dims, - size_t dims_size, size_t* bytes) { - // TODO(aselle): Check for overflow here using overflow.h in TensorFlow - // MultiplyWithoutOverflow. - TF_LITE_ENSURE(&context_, bytes != nullptr); - size_t count = 1; - for (int k = 0; k < dims_size; k++) count *= dims[k]; - switch (type) { - case kTfLiteFloat32: - *bytes = sizeof(float) * count; - break; - case kTfLiteInt16: - *bytes = sizeof(int16_t) * count; - break; - case kTfLiteInt32: - *bytes = sizeof(int32_t) * count; - break; - case kTfLiteUInt8: - *bytes = sizeof(uint8_t) * count; - break; - case kTfLiteInt64: - *bytes = sizeof(int64_t) * count; - break; - case kTfLiteBool: - *bytes = sizeof(bool) * count; - break; - case kTfLiteComplex64: - *bytes = sizeof(std::complex) * count; - break; - default: - ReportError(&context_, - "Only float32, int16, int32, int64, uint8, bool, complex64 " - "supported currently."); - return kTfLiteError; - } - return kTfLiteOk; + return primary_subgraph().SetVariables(variables); } TfLiteStatus Interpreter::AllocateTensors() { - if (!consistent_) { - ReportError(&context_, "AllocateTensors() called on inconsistent model."); - return kTfLiteError; - } - - // Explicit (re)allocation is necessary if nodes have been changed or tensors - // have been resized. For inputs marked as dynamic, we can't short-circuit the - // allocation as the client may have done the resize manually. - if (state_ != kStateUninvokable && !HasDynamicTensorImpl(context_, inputs_)) { - return kTfLiteOk; - } - - next_execution_plan_index_to_prepare_ = 0; - if (memory_planner_) { - TF_LITE_ENSURE_STATUS(memory_planner_->ResetAllocations()); - } - - TF_LITE_ENSURE_STATUS(PrepareOpsAndTensors()); - - state_ = kStateInvokable; - - // Reset the variable tensors to zero after (re)allocating the tensors. - // Developers shouldn't rely on the side effect of this function to reset - // variable tesnsors. They should call `ResetVariableTensors` directly - // instead. - ResetVariableTensors(); - - return kTfLiteOk; + return primary_subgraph().AllocateTensors(); } -// TODO(ycling): Support non-zero default values. -TfLiteStatus Interpreter::ResetVariableTensors() { - for (auto& tensor : tensors_) { - if (!tensor.is_variable) { - continue; - } +void Interpreter::ReserveNodes(int count) { + primary_subgraph().nodes_and_registration().reserve(count); +} - // Variable tensors have to be `kTfLiteArenaRwPersistent`, and must be - // allocated after the initial `PrepareOpsAndTensors()` is called. - TF_LITE_ENSURE_EQ(&context_, tensor.allocation_type, - kTfLiteArenaRwPersistent); - TF_LITE_ENSURE(&context_, tensor.data.raw != nullptr); +void Interpreter::AddSubgraphs(int subgraphs_to_add, + int* first_new_subgraph_index) { + const size_t base_index = subgraphs_.size(); + if (first_new_subgraph_index) *first_new_subgraph_index = base_index; - memset(tensor.data.raw, 0, tensor.bytes); + subgraphs_.reserve(base_index + subgraphs_to_add); + for (int i = 0; i < subgraphs_to_add; ++i) { + Subgraph* subgraph = + new Subgraph(error_reporter_, external_contexts_, &subgraphs_); + subgraphs_.emplace_back(subgraph); } - return kTfLiteOk; -} - -void Interpreter::ReserveNodes(int count) { - nodes_and_registration_.reserve(count); } TfLiteStatus Interpreter::AddNodeWithParameters( const std::vector& inputs, const std::vector& outputs, const char* init_data, size_t init_data_size, void* builtin_data, const TfLiteRegistration* registration, int* node_index) { - if (state_ == kStateInvokableAndImmutable) { - ReportError(&context_, - "AddNodeWithParameters is disallowed when graph is immutable."); - return kTfLiteError; - } - state_ = kStateUninvokable; - - std::unique_ptr builtin_data_deleter(builtin_data, - free); - - TF_LITE_ENSURE_OK(&context_, CheckTensorIndices("node inputs", inputs.data(), - inputs.size())); - TF_LITE_ENSURE_OK( - &context_, - CheckTensorIndices("node outputs", outputs.data(), outputs.size())); - - int new_node_index = nodes_and_registration_.size(); - if (node_index) *node_index = new_node_index; - nodes_and_registration_.resize(nodes_and_registration_.size() + 1); - auto& node_and_reg = nodes_and_registration_.back(); - TfLiteNode& node = node_and_reg.first; - if (node.inputs) TfLiteIntArrayFree(node.inputs); - if (node.outputs) TfLiteIntArrayFree(node.outputs); - if (node.temporaries) TfLiteIntArrayFree(node.temporaries); - - // NOTE, here we are not using move semantics yet, since our internal - // representation isn't std::vector, but in the future we would like to avoid - // copies, so we want the interface to take r-value references now. - node.inputs = ConvertVectorToTfLiteIntArray(inputs); - node.outputs = ConvertVectorToTfLiteIntArray(outputs); - node.temporaries = TfLiteIntArrayCreate(0); - if (init_data) { - node.user_data = OpInit(*registration, init_data, init_data_size); - } else { - node.user_data = - OpInit(*registration, - reinterpret_cast(builtin_data_deleter.get()), 0); - } - - node.builtin_data = builtin_data_deleter.release(); - // TODO(ycling): Filling `custom_initial_data` and `custom_initial_data_size` - // properly for nodes generated by ReplaceNodeSubsetsWithDelegateKernels. - - if (registration->builtin_code == BuiltinOperator_CUSTOM) { - // When it's a CUSTOM op, the `custom_options` field in the Flatbuffer - // `Operator` table is passed in. - node.custom_initial_data = init_data; - node.custom_initial_data_size = init_data_size; - } else { - node.custom_initial_data = nullptr; - node.custom_initial_data_size = 0; - } - - node.delegate = nullptr; - node_and_reg.second = *registration; - execution_plan_.push_back(new_node_index); - return kTfLiteOk; + return primary_subgraph().AddNodeWithParameters(inputs, outputs, init_data, + init_data_size, builtin_data, + registration, node_index); } TfLiteStatus Interpreter::ResizeInputTensor(int tensor_index, const std::vector& dims) { - if (state_ == kStateInvokableAndImmutable) { - ReportError(&context_, - "ResizeInputTensor is disallowed when graph is immutable."); - return kTfLiteError; - } - - // TODO(aselle): All bounds checks can be implemented as one-sided bounds - // checks by casting to unsigned for efficiency. Profile before doing this. - TF_LITE_ENSURE(&context_, - tensor_index < context_.tensors_size && tensor_index >= 0); - TfLiteTensor* tensor = &context_.tensors[tensor_index]; - - // Short-circuit the state change if the dimensions don't change, avoiding - // unnecessary (re)allocations. - if (EqualArrayAndTfLiteIntArray(tensor->dims, dims.size(), dims.data())) { - return kTfLiteOk; - } - - state_ = kStateUninvokable; - return ResizeTensorImpl(tensor, ConvertVectorToTfLiteIntArray(dims)); -} - -bool HasDynamicTensor(const TfLiteContext& context, - const TfLiteIntArray* int_array) { - return HasDynamicTensorImpl(context, TfLiteIntArrayView{int_array}); -} - -TfLiteStatus Interpreter::PrepareOpsStartingAt( - int first_execution_plan_index, int* last_execution_plan_index_prepared) { - for (int execution_plan_index = first_execution_plan_index; - execution_plan_index < execution_plan_.size(); execution_plan_index++) { - int node_index = execution_plan_[execution_plan_index]; - TfLiteNode& node = nodes_and_registration_[node_index].first; - const TfLiteRegistration& registration = - nodes_and_registration_[node_index].second; - EnsureTensorsVectorCapacity(); - if (OpPrepare(registration, &node) == kTfLiteError) { - return ReportOpError(&context_, node, registration, node_index, - "failed to prepare"); - } - - *last_execution_plan_index_prepared = execution_plan_index; - - // Discontinue if the node has dynamic outputs. Note that we don't - // stop for dynamic temporary tensors since they won't affect the - // sizes of other tensors in the graph. - if (HasDynamicTensor(context_, node.outputs)) { - break; - } - } - return kTfLiteOk; -} - -TfLiteStatus Interpreter::PrepareOpsAndTensors() { - if (!memory_planner_) { - memory_planner_.reset(new ArenaPlanner( - &context_, std::unique_ptr(new InterpreterInfo(this)), - /*preserve_inputs=*/true, /*preserve_intermediates*/ false)); - memory_planner_->PlanAllocations(); - } - - int last_exec_plan_index_prepared = 0; - - TF_LITE_ENSURE_STATUS(PrepareOpsStartingAt( - next_execution_plan_index_to_prepare_, &last_exec_plan_index_prepared)); - TF_LITE_ENSURE_STATUS(memory_planner_->ExecuteAllocations( - next_execution_plan_index_to_prepare_, last_exec_plan_index_prepared)); - - next_execution_plan_index_to_prepare_ = last_exec_plan_index_prepared + 1; - return kTfLiteOk; + return primary_subgraph().ResizeInputTensor(tensor_index, dims); } TfLiteStatus Interpreter::Invoke() { - if (!consistent_) { - ReportError(&context_, "Invoke called on model that is not consistent."); - return kTfLiteError; - } - if (state_ == kStateUninvokable) { - ReportError(&context_, "Invoke called on model that is not ready."); - return kTfLiteError; - } - - TfLiteStatus status = kTfLiteOk; - if (nnapi_delegate_) { - if (next_execution_plan_index_to_prepare_ == execution_plan_.size()) { - TF_LITE_ENSURE_OK(&context_, nnapi_delegate_->Invoke(this)); - return kTfLiteOk; - } else { - // TODO(aselle): In the future, we would like this to be an - // automatic tflite CPU fallback. - ReportError(&context_, - "NNAPI was requested, but dependent sized tensors " - "being used.\n"); - return kTfLiteError; - } - } - - // Invocations are always done in node order. - // Note that calling Invoke repeatedly will cause the original memory plan to - // be reused, unless either ResizeInputTensor() or AllocateTensors() has been - // called. - for (int execution_plan_index = 0; - execution_plan_index < execution_plan_.size(); execution_plan_index++) { - if (execution_plan_index == next_execution_plan_index_to_prepare_) { - TF_LITE_ENSURE_STATUS(PrepareOpsAndTensors()); - TF_LITE_ENSURE(&context_, next_execution_plan_index_to_prepare_ >= - execution_plan_index); - } - int node_index = execution_plan_[execution_plan_index]; - TfLiteNode& node = nodes_and_registration_[node_index].first; - const TfLiteRegistration& registration = - nodes_and_registration_[node_index].second; - SCOPED_OPERATOR_PROFILE(profiler_, node_index); - - // TODO(ycling): This is an extra loop through inputs to check if the data - // need to be copied from Delegate buffer to raw memory, which is often not - // needed. We may want to cache this in prepare to know if this needs to be - // done for a node or not. - for (int i = 0; i < node.inputs->size; ++i) { - int tensor_index = node.inputs->data[i]; - if (tensor_index == kOptionalTensor) { - continue; - } - TfLiteTensor* tensor = &tensors_[tensor_index]; - if (tensor->delegate && tensor->delegate != node.delegate && - tensor->data_is_stale) { - EnsureTensorDataIsReadable(tensor_index); - } - } - - EnsureTensorsVectorCapacity(); - tensor_resized_since_op_invoke_ = false; - if (OpInvoke(registration, &node) == kTfLiteError) { - status = ReportOpError(&context_, node, registration, node_index, - "failed to invoke"); - } - - // Force execution prep for downstream ops if the latest op triggered the - // resize of a dynamic tensor. - if (tensor_resized_since_op_invoke_ && - HasDynamicTensor(context_, node.outputs)) { - next_execution_plan_index_to_prepare_ = execution_plan_index + 1; - } - } + TfLiteStatus status = primary_subgraph().Invoke(); if (!allow_buffer_handle_output_) { - for (int tensor_index : outputs_) { - EnsureTensorDataIsReadable(tensor_index); + for (int tensor_index : outputs()) { + primary_subgraph().EnsureTensorDataIsReadable(tensor_index); } } return status; } -TfLiteStatus Interpreter::ResizeTensor(TfLiteContext* context, - TfLiteTensor* tensor, - TfLiteIntArray* new_size) { - // Note here that context->impl_ is recovering the this pointer for an - // instance of Interpreter to call into the member function ResizeTensorImpl - // (this function is static). - return static_cast(context->impl_) - ->ResizeTensorImpl(tensor, new_size); -} - -void Interpreter::ReportErrorImpl(const char* format, va_list args) { - error_reporter_->Report(format, args); -} - -void Interpreter::ReportError(TfLiteContext* context, const char* format, ...) { - va_list args; - va_start(args, format); - auto* f = static_cast(context->impl_); - // Note here that context->impl_ is recovering the this pointer for an - // instance of Interpreter to call into the member function ReportErrorImpl - // (this function is static). - f->ReportErrorImpl(format, args); - va_end(args); -} - TfLiteStatus Interpreter::AddTensors(int tensors_to_add, int* first_new_tensor_index) { - const size_t base_index = tensors_.size(); - if (first_new_tensor_index) *first_new_tensor_index = base_index; - tensors_.resize(tensors_.size() + tensors_to_add); - for (size_t i = base_index; i < tensors_.size(); i++) { - memset(&tensors_[i], 0, sizeof(tensors_[i])); - tensors_[i].buffer_handle = kTfLiteNullBufferHandle; - } - context_.tensors = tensors_.data(); - context_.tensors_size = tensors_.size(); - return kTfLiteOk; -} - -TfLiteStatus Interpreter::AddTensors(TfLiteContext* context, int tensors_to_add, - int* first_new_tensor_index) { - // Note here that context->impl_ is recovering the this pointer for an - // instance of Interpreter to call into the member function AddTensors - // (this function is static). - return static_cast(context->impl_) - ->AddTensors(tensors_to_add, first_new_tensor_index); -} - -TfLiteStatus Interpreter::GetNodeAndRegistration( - int node_index, TfLiteNode** node, TfLiteRegistration** registration) { - TF_LITE_ENSURE(&context_, node_index >= 0); - TF_LITE_ENSURE(&context_, static_cast(node_index) < nodes_size()); - TF_LITE_ENSURE(&context_, node != nullptr && registration != nullptr); - *node = &nodes_and_registration_[node_index].first; - *registration = &nodes_and_registration_[node_index].second; - return kTfLiteOk; + return primary_subgraph().AddTensors(tensors_to_add, first_new_tensor_index); } -TfLiteStatus Interpreter::GetNodeAndRegistration( - struct TfLiteContext* context, int node_index, TfLiteNode** node, - TfLiteRegistration** registration) { - return static_cast(context->impl_) - ->GetNodeAndRegistration(node_index, node, registration); +TfLiteStatus Interpreter::ResetVariableTensors() { + return primary_subgraph().ResetVariableTensors(); } TfLiteStatus Interpreter::SetTensorParametersReadOnly( int tensor_index, TfLiteType type, const char* name, const size_t rank, const int* dims, TfLiteQuantizationParams quantization, const char* buffer, size_t bytes, const Allocation* allocation) { - if (state_ == kStateInvokableAndImmutable) { - ReportError( - &context_, - "SetTensorParametersReadOnly is disallowed when graph is immutable."); - return kTfLiteError; - } - - TF_LITE_ENSURE(&context_, - tensor_index < context_.tensors_size && tensor_index >= 0); - // For most tensors we know exactly how much memory is necessary so we can - // ensure the buffer is large enough. However, we need to skip string tensors - // because their sizes change with the contents of the individual strings. - if (type != kTfLiteString) { - size_t required_bytes; - TF_LITE_ENSURE_OK(&context_, - BytesRequired(type, dims, rank, &required_bytes)); - TF_LITE_ENSURE_EQ(&context_, required_bytes, bytes); - } - - TfLiteTensor& tensor = context_.tensors[tensor_index]; - if (type == tensor.type && - EqualArrayAndTfLiteIntArray(tensor.dims, rank, dims)) { - // Fast path which does not invalidate the invokable property. - TfLiteTensorDataFree(&tensor); - tensor.data.raw = const_cast(buffer); - if (!tensor.dims) tensor.dims = ConvertArrayToTfLiteIntArray(rank, dims); - tensor.params = quantization; - tensor.allocation_type = kTfLiteMmapRo; - tensor.allocation = allocation; - } else { - state_ = kStateUninvokable; - TfLiteTensorReset(type, name, ConvertArrayToTfLiteIntArray(rank, dims), - quantization, const_cast(buffer), bytes, - kTfLiteMmapRo, allocation, false, &tensor); - } - return kTfLiteOk; + return primary_subgraph().SetTensorParametersReadOnly( + tensor_index, type, name, rank, dims, quantization, buffer, bytes, + allocation); } // Set description of inputs/outputs/data/fptrs for node `node_index`. @@ -816,186 +138,52 @@ TfLiteStatus Interpreter::SetTensorParametersReadOnly( TfLiteStatus Interpreter::SetTensorParametersReadWrite( int tensor_index, TfLiteType type, const char* name, const size_t rank, const int* dims, TfLiteQuantizationParams quantization, bool is_variable) { - if (state_ == kStateInvokableAndImmutable) { - ReportError( - &context_, - "SetTensorParametersReadWrite is disallowed when graph is immutable."); - return kTfLiteError; - } - TF_LITE_ENSURE(&context_, - tensor_index < context_.tensors_size && tensor_index >= 0); - size_t required_bytes = 0; - if (type != kTfLiteString) { - // These types will be allocated in our arena so we need to record how - // many bytes we will need based on the dimensions. String tensors are - // allocated dynamically and we can't know ahead of time how much space - // they will require. - TF_LITE_ENSURE_OK(&context_, - BytesRequired(type, dims, rank, &required_bytes)); - } - - TfLiteAllocationType allocation_type = kTfLiteArenaRw; - if (type == kTfLiteString) { - if (is_variable) { - // We don't have a real use case for string variable tensor. - ReportError(&context_, "String variable tensor isn't supported."); - return kTfLiteError; - } - allocation_type = kTfLiteDynamic; - } else if (is_variable) { - allocation_type = kTfLiteArenaRwPersistent; - } - - TfLiteTensorReset(type, name, ConvertArrayToTfLiteIntArray(rank, dims), - quantization, - /*buffer=*/nullptr, required_bytes, allocation_type, - nullptr, is_variable, &context_.tensors[tensor_index]); - return kTfLiteOk; + return primary_subgraph().SetTensorParametersReadWrite( + tensor_index, type, name, rank, dims, quantization, is_variable); } TfLiteStatus Interpreter::SetExecutionPlan(const std::vector& new_plan) { - for (int node_index : new_plan) { - TF_LITE_ENSURE(&context_, node_index >= 0 && node_index < nodes_size()); - } - execution_plan_ = new_plan; - return kTfLiteOk; -} - -TfLiteStatus Interpreter::ResizeTensorImpl(TfLiteTensor* tensor, - TfLiteIntArray* new_size) { - // Note that in theory we could resize kTfLiteArenaRwPersistent tensors too. - if (tensor->allocation_type == kTfLiteArenaRw || - tensor->allocation_type == kTfLiteDynamic || - tensor->allocation_type == kTfLiteArenaRwPersistent) { - tensor_resized_since_op_invoke_ |= - TfLiteIntArrayEqual(tensor->dims, new_size) == 0; - if (tensor->type != kTfLiteString) { - size_t bytesRequired; - TfLiteStatus status = BytesRequired(tensor->type, new_size->data, - new_size->size, &bytesRequired); - if (status != kTfLiteOk) { - TfLiteIntArrayFree(new_size); - return kTfLiteError; - } - - // Realloc space for kTfLiteDynamic tensors. - TfLiteTensorRealloc(bytesRequired, tensor); - tensor->bytes = bytesRequired; - } - if (tensor->dims) TfLiteIntArrayFree(tensor->dims); - tensor->dims = new_size; - - if (tensor->allocation_type != kTfLiteDynamic) { - tensor->data.raw = nullptr; - } - } else { - // kTfLiteMmapRo tensors are stored in the flatbuffer and are therefore - // of fixed size. - TfLiteIntArrayFree(new_size); - ReportError(&context_, "Attempting to resize a fixed-size tensor."); - return kTfLiteError; - } - return kTfLiteOk; + return primary_subgraph().SetExecutionPlan(new_plan); } -void Interpreter::UseNNAPI(bool enable) { - // TODO(aselle): This is a workaround for finding if NNAPI exists. - // We also need to make sure getLibraryHandle() is renamed to be NNAPI - // prefixed. - if (!NNAPIDelegate::IsSupported()) enable = false; - if (!enable) { - nnapi_delegate_.reset(); - } else if (!nnapi_delegate_) { - nnapi_delegate_.reset(new NNAPIDelegate); - } -} +void Interpreter::UseNNAPI(bool enable) { primary_subgraph().UseNNAPI(enable); } void Interpreter::SetNumThreads(int num_threads) { - context_.recommended_num_threads = num_threads; + for (auto& subgraph : subgraphs_) { + subgraph->context()->recommended_num_threads = num_threads; + } for (int i = 0; i < kTfLiteMaxExternalContexts; ++i) { auto* c = external_contexts_[i]; if (c && c->Refresh) { - c->Refresh(&context_); + c->Refresh(context_); } } } -void Interpreter::SwitchToDelegateContext() { - context_.GetNodeAndRegistration = GetNodeAndRegistration; - context_.ReplaceNodeSubsetsWithDelegateKernels = - ReplaceNodeSubsetsWithDelegateKernels; - context_.GetExecutionPlan = GetExecutionPlan; -} - -void Interpreter::SwitchToKernelContext() { - SetForbiddenContextFunction(&context_.GetNodeAndRegistration); - SetForbiddenContextFunction(&context_.ReplaceNodeSubsetsWithDelegateKernels); - SetForbiddenContextFunction(&context_.GetExecutionPlan); +void Interpreter::SetAllowFp16PrecisionForFp32(bool allow) { + for (auto& subgraph : subgraphs_) { + subgraph->context()->allow_fp32_relax_to_fp16 = allow; + } } TfLiteStatus Interpreter::ModifyGraphWithDelegate(TfLiteDelegate* delegate) { - if (!(delegate->flags & kTfLiteDelegateFlagsAllowDynamicTensors)) { - int last_execution_plan_index_prepared; - TF_LITE_ENSURE_OK(&context_, PrepareOpsStartingAt( - 0, &last_execution_plan_index_prepared)); - - bool has_dynamic_tensors = true; - // Dynamic tensors exist if not all nodes can be prepared. - if (last_execution_plan_index_prepared + 1 == execution_plan_.size()) { - // If all the nodes can be prepared, check if the last node has dynamic - // tensors. - int node_index = execution_plan_[last_execution_plan_index_prepared]; - TfLiteNode& node = nodes_and_registration_[node_index].first; - if (!HasDynamicTensor(context_, node.outputs)) { - has_dynamic_tensors = false; - } - } - if (has_dynamic_tensors) { - ReportError( - &context_, - "Attempting to use a delegate that only supports static-sized " - "tensors with a graph that has dynamic-sized tensors."); - return kTfLiteError; - } - } - - // TODO(aselle): Consider if it is worth storing pointers to delegates. - // Setup additional context interface. - SwitchToDelegateContext(); - - TfLiteStatus status = delegate->Prepare(&context_, delegate); - - // Remove additional context info. - SwitchToKernelContext(); - - TF_LITE_ENSURE_OK(&context_, status); - - if (!(delegate->flags & kTfLiteDelegateFlagsAllowDynamicTensors)) { - // Reset the state to force tensor/op reallocation. - state_ = kStateUninvokable; - TF_LITE_ENSURE_OK(&context_, AllocateTensors()); - TF_LITE_ENSURE_EQ(&context_, state_, kStateInvokable); - // After using a delegate which doesn't support dynamic tensors, make the - // entire graph immutable. - state_ = kStateInvokableAndImmutable; - } - - return status; + return primary_subgraph().ModifyGraphWithDelegate(delegate); } TfLiteStatus Interpreter::SetBufferHandle(int tensor_index, TfLiteBufferHandle buffer_handle, TfLiteDelegate* delegate) { - TF_LITE_ENSURE(&context_, tensor_index < tensors_size()); - TfLiteTensor* tensor = &tensors_[tensor_index]; + TF_LITE_ENSURE(context_, tensor_index < tensors_size()); + std::vector& tensors = primary_subgraph().tensors(); + TfLiteTensor* tensor = &tensors[tensor_index]; - TF_LITE_ENSURE(&context_, + TF_LITE_ENSURE(context_, tensor->delegate == nullptr || tensor->delegate == delegate); tensor->delegate = delegate; if (tensor->buffer_handle != kTfLiteNullBufferHandle) { - TF_LITE_ENSURE(&context_, tensor->delegate->FreeBufferHandle != nullptr); - tensor->delegate->FreeBufferHandle(&context_, tensor->delegate, + TF_LITE_ENSURE(context_, tensor->delegate->FreeBufferHandle != nullptr); + tensor->delegate->FreeBufferHandle(context_, tensor->delegate, &tensor->buffer_handle); } tensor->buffer_handle = buffer_handle; @@ -1006,8 +194,9 @@ TfLiteStatus Interpreter::SetBufferHandle(int tensor_index, TfLiteStatus Interpreter::GetBufferHandle(int tensor_index, TfLiteBufferHandle* buffer_handle, TfLiteDelegate** delegate) { - TF_LITE_ENSURE(&context_, tensor_index < tensors_size()); - TfLiteTensor* tensor = &tensors_[tensor_index]; + TF_LITE_ENSURE(context_, tensor_index < tensors_size()); + std::vector& tensors = primary_subgraph().tensors(); + TfLiteTensor* tensor = &tensors[tensor_index]; *delegate = tensor->delegate; *buffer_handle = tensor->buffer_handle; @@ -1015,4 +204,12 @@ TfLiteStatus Interpreter::GetBufferHandle(int tensor_index, return kTfLiteOk; } +void Interpreter::SetProfiler(profiling::Profiler* profiler) { + for (auto& subgraph : subgraphs_) subgraph->SetProfiler(profiler); +} + +profiling::Profiler* Interpreter::GetProfiler() { + return primary_subgraph().GetProfiler(); +} + } // namespace tflite diff --git a/tensorflow/lite/interpreter.h b/tensorflow/lite/interpreter.h index 415c5f0979..6192d56ca2 100644 --- a/tensorflow/lite/interpreter.h +++ b/tensorflow/lite/interpreter.h @@ -25,6 +25,7 @@ limitations under the License. #include "tensorflow/lite/allocation.h" #include "tensorflow/lite/c/c_api_internal.h" #include "tensorflow/lite/core/api/error_reporter.h" +#include "tensorflow/lite/core/subgraph.h" #include "tensorflow/lite/memory_planner.h" #include "tensorflow/lite/profiling/profiler.h" #include "tensorflow/lite/stderr_reporter.h" @@ -57,6 +58,10 @@ constexpr TfLiteType typeToTfLiteType() { return kTfLiteUInt8; } template <> +constexpr TfLiteType typeToTfLiteType() { + return kTfLiteInt8; +} +template <> constexpr TfLiteType typeToTfLiteType() { return kTfLiteBool; } @@ -69,9 +74,6 @@ constexpr TfLiteType typeToTfLiteType() { return kTfLiteString; } -// Forward declare since NNAPIDelegate uses Interpreter. -class NNAPIDelegate; - // An interpreter for a graph of nodes that input and output from tensors. // Each node of the graph processes a set of input tensors and produces a // set of output Tensors. All inputs/output tensors are referenced by index. @@ -100,12 +102,6 @@ class NNAPIDelegate; // foo.Invoke(); // -struct TfLiteIntArrayDeleter { - void operator()(TfLiteIntArray* a) { - if (a) TfLiteIntArrayFree(a); - } -}; - class Interpreter { public: // Instantiate an interpreter. All errors associated with reading and @@ -117,6 +113,7 @@ class Interpreter { ~Interpreter(); + // Interpreters are not copyable as they have non-trivial memory semantics. Interpreter(const Interpreter&) = delete; Interpreter& operator=(const Interpreter&) = delete; @@ -197,34 +194,40 @@ class Interpreter { // Functions to access tensor data // Read only access to list of inputs. - const std::vector& inputs() const { return inputs_; } + const std::vector& inputs() const { return primary_subgraph().inputs(); } // Return the name of a given input. The given index must be between 0 and // inputs().size(). const char* GetInputName(int index) const { - return context_.tensors[inputs_[index]].name; + return context_->tensors[inputs()[index]].name; } // Read only access to list of outputs. - const std::vector& outputs() const { return outputs_; } + const std::vector& outputs() const { + return primary_subgraph().outputs(); + } // Read only access to list of variable tensors. - const std::vector& variables() const { return variables_; } + const std::vector& variables() const { + return primary_subgraph().variables(); + } // Return the name of a given output. The given index must be between 0 and // outputs().size(). const char* GetOutputName(int index) const { - return context_.tensors[outputs_[index]].name; + return context_->tensors[outputs()[index]].name; } // Return the number of tensors in the model. - size_t tensors_size() const { return context_.tensors_size; } + size_t tensors_size() const { return context_->tensors_size; } // Return the number of ops in the model. - size_t nodes_size() const { return nodes_and_registration_.size(); } + size_t nodes_size() const { return primary_subgraph().nodes_size(); } // WARNING: Experimental interface, subject to change - const std::vector& execution_plan() const { return execution_plan_; } + const std::vector& execution_plan() const { + return primary_subgraph().execution_plan(); + } // WARNING: Experimental interface, subject to change // Overrides execution plan. This bounds checks indices sent in. @@ -234,27 +237,18 @@ class Interpreter { // TODO(aselle): Create a safe ArrayHandle interface to avoid exposing this // read/write access to structure TfLiteTensor* tensor(int tensor_index) { - if (tensor_index < 0 || - static_cast(tensor_index) >= context_.tensors_size) - return nullptr; - return &context_.tensors[tensor_index]; + return primary_subgraph().tensor(tensor_index); } // Get an immutable tensor data structure. const TfLiteTensor* tensor(int tensor_index) const { - if (tensor_index < 0 || - static_cast(tensor_index) >= context_.tensors_size) - return nullptr; - return &context_.tensors[tensor_index]; + return primary_subgraph().tensor(tensor_index); } // Get a pointer to an operation and registration data structure if in bounds. const std::pair* node_and_registration( int node_index) const { - if (node_index < 0 || - static_cast(node_index) >= nodes_and_registration_.size()) - return nullptr; - return &nodes_and_registration_[node_index]; + return primary_subgraph().node_and_registration(node_index); } // Perform a checked cast to the appropriate tensor type (mutable pointer @@ -285,28 +279,28 @@ class Interpreter { // index must be between 0 and inputs().size(). template T* typed_input_tensor(int index) { - return typed_tensor(inputs_[index]); + return typed_tensor(inputs()[index]); } // Return an immutable pointer into the data of a given input tensor. The // given index must be between 0 and inputs().size(). template const T* typed_input_tensor(int index) const { - return typed_tensor(inputs_[index]); + return typed_tensor(inputs()[index]); } // Return a mutable pointer into the data of a given output tensor. The given // index must be between 0 and outputs().size(). template T* typed_output_tensor(int index) { - return typed_tensor(outputs_[index]); + return typed_tensor(outputs()[index]); } // Return an immutable pointer into the data of a given output tensor. The // given index must be between 0 and outputs().size(). template const T* typed_output_tensor(int index) const { - return typed_tensor(outputs_[index]); + return typed_tensor(outputs()[index]); } // Change the dimensionality of a given tensor. Note, this is only acceptable @@ -321,7 +315,6 @@ class Interpreter { // Update allocations for all tensors. This will redim dependent tensors using // the input tensor dimensionality as given. This is relatively expensive. // If you know that your sizes are not changing, you need not call this. - // Returns status of success or failure. TfLiteStatus AllocateTensors(); @@ -342,14 +335,12 @@ class Interpreter { // Allow float16 precision for FP32 calculation when possible. // default: not allow. // WARNING: This is an experimental API and subject to change. - void SetAllowFp16PrecisionForFp32(bool allow) { - context_.allow_fp32_relax_to_fp16 = allow; - } + void SetAllowFp16PrecisionForFp32(bool allow); // Get the half precision flag. // WARNING: This is an experimental API and subject to change. bool GetAllowFp16PrecisionForFp32() const { - return context_.allow_fp32_relax_to_fp16; + return context_->allow_fp32_relax_to_fp16; } // Owning handle to a TfLiteDelegate instance. @@ -366,18 +357,7 @@ class Interpreter { // it might require to copy the data from delegate buffer to raw memory. // WARNING: This is an experimental API and subject to change. TfLiteStatus EnsureTensorDataIsReadable(int tensor_index) { - TfLiteTensor* t = tensor(tensor_index); - TF_LITE_ENSURE(&context_, t != nullptr); - if (t->data_is_stale) { - TF_LITE_ENSURE(&context_, t->delegate != nullptr); - TF_LITE_ENSURE(&context_, t->buffer_handle != kTfLiteNullBufferHandle); - // This can be null if the delegate doesn't use its own buffer. - TF_LITE_ENSURE(&context_, t->delegate->CopyFromBufferHandle != nullptr); - t->delegate->CopyFromBufferHandle( - &context_, t->delegate, t->buffer_handle, t->data.raw, t->bytes); - t->data_is_stale = false; - } - return kTfLiteOk; + return primary_subgraph().EnsureTensorDataIsReadable(tensor_index); } // Set the delegate buffer handle to a tensor. It can be called in the @@ -400,9 +380,9 @@ class Interpreter { TfLiteBufferHandle* buffer_handle, TfLiteDelegate** delegate); - void SetProfiler(profiling::Profiler* profiler) { profiler_ = profiler; } + void SetProfiler(profiling::Profiler* profiler); - profiling::Profiler* GetProfiler() { return profiler_; } + profiling::Profiler* GetProfiler(); // The default capacity of `tensors_` vector. static constexpr int kTensorsReservedCapacity = 128; @@ -435,142 +415,47 @@ class Interpreter { const char* OpProfilingString(const TfLiteRegistration& op_reg, const TfLiteNode* node) const { if (op_reg.profiling_string == nullptr) return nullptr; - return op_reg.profiling_string(&context_, node); + return op_reg.profiling_string(context_, node); } // Set the value of an external context. void SetExternalContext(TfLiteExternalContextType type, TfLiteExternalContext* ctx); - private: - friend class InterpreterBuilder; - friend class InterpreterTest; - - // Prevent 'context_' from accessing functions that are only available to - // delegated kernels. - void SwitchToKernelContext(); - - // Add delegate-only functions to 'context_'. - void SwitchToDelegateContext(); + // Adds `subgraphs_to_add` subgraphs, preserving pre-existing Subgraph + // entries. The value pointed to by `first_new_subgraph_index` will be set to + // the index of the first new subgraph if `first_new_subgraph_index` is + // non-null. + // WARNING: This is an experimental API and subject to change. + void AddSubgraphs(int subgraphs_to_add, + int* first_new_subgraph_index = nullptr); - // Give 'op_reg' a chance to initialize itself using the contents of - // 'buffer'. - void* OpInit(const TfLiteRegistration& op_reg, const char* buffer, - size_t length) { - if (op_reg.init == nullptr) return nullptr; - return op_reg.init(&context_, buffer, length); - } + // Return the number of subgraphs in the model. + // WARNING: This is an experimental API and subject to change. + size_t subgraphs_size() const { return subgraphs_.size(); } - // Let 'op_reg' release any memory it might have allocated via 'OpInit'. - void OpFree(const TfLiteRegistration& op_reg, void* buffer) { - if (op_reg.free == nullptr) return; - if (buffer) { - op_reg.free(&context_, buffer); - } + // Get a pointer to a subgraph if in bounds. + // WARNING: This is an experimental API and subject to change. + Subgraph* subgraph(int subgraph_index) { + if (subgraph_index < 0 || + static_cast(subgraph_index) >= subgraphs_size()) + return nullptr; + return &*subgraphs_[subgraph_index]; } - // Prepare the given 'node' for execution. - TfLiteStatus OpPrepare(const TfLiteRegistration& op_reg, TfLiteNode* node) { - if (op_reg.prepare == nullptr) return kTfLiteOk; - return op_reg.prepare(&context_, node); + // WARNING: Experimental interface, subject to change + Subgraph& primary_subgraph() { + return *subgraphs_.front(); // Safe as subgraphs_ always has 1 entry. } - // Invoke the operator represented by 'node'. - TfLiteStatus OpInvoke(const TfLiteRegistration& op_reg, TfLiteNode* node) { - if (op_reg.invoke == nullptr) return kTfLiteError; - return op_reg.invoke(&context_, node); + // WARNING: Experimental interface, subject to change + const Subgraph& primary_subgraph() const { + return *subgraphs_.front(); // Safe as subgraphs_ always has 1 entry. } - // Call OpPrepare() for as many ops as possible, allocating memory for their - // tensors. If an op containing dynamic tensors is found, preparation will be - // postponed until this function is called again. This allows the interpreter - // to wait until Invoke() to resolve the sizes of dynamic tensors. - TfLiteStatus PrepareOpsAndTensors(); - - // Call OpPrepare() for all ops starting at 'first_node'. Stop when a - // dynamic tensors is found or all ops have been prepared. Fill - // 'last_node_prepared' with the id of the op containing dynamic tensors, or - // the last in the graph. - TfLiteStatus PrepareOpsStartingAt(int first_execution_plan_index, - int* last_execution_plan_index_prepared); - - // Tensors needed by the interpreter. Use `AddTensors` to add more blank - // tensor entries. Note, `tensors_.data()` needs to be synchronized to the - // `context_` whenever this std::vector is reallocated. Currently this - // only happens in `AddTensors()`. - std::vector tensors_; - - // Check if an array of tensor indices are valid with respect to the Tensor - // array. - // NOTE: this changes consistent_ to be false if indices are out of bounds. - TfLiteStatus CheckTensorIndices(const char* label, const int* indices, - int length); - - // Compute the number of bytes required to represent a tensor with dimensions - // specified by the array dims (of length dims_size). Returns the status code - // and bytes. - TfLiteStatus BytesRequired(TfLiteType type, const int* dims, size_t dims_size, - size_t* bytes); - - // Request an tensor be resized implementation. If the given tensor is of - // type kTfLiteDynamic it will also be allocated new memory. - TfLiteStatus ResizeTensorImpl(TfLiteTensor* tensor, TfLiteIntArray* new_size); - - // Report a detailed error string (will be printed to stderr). - // TODO(aselle): allow user of class to provide alternative destinations. - void ReportErrorImpl(const char* format, va_list args); - - // Entry point for C node plugin API to request an tensor be resized. - static TfLiteStatus ResizeTensor(TfLiteContext* context, TfLiteTensor* tensor, - TfLiteIntArray* new_size); - // Entry point for C node plugin API to report an error. - static void ReportError(TfLiteContext* context, const char* format, ...); - - // Entry point for C node plugin API to add new tensors. - static TfLiteStatus AddTensors(TfLiteContext* context, int tensors_to_add, - int* first_new_tensor_index); - - // WARNING: This is an experimental API and subject to change. - // Entry point for C API ReplaceNodeSubsetsWithDelegateKernels - static TfLiteStatus ReplaceNodeSubsetsWithDelegateKernels( - TfLiteContext* context, TfLiteRegistration registration, - const TfLiteIntArray* nodes_to_replace, TfLiteDelegate* delegate); - - // Update the execution graph to replace some of the nodes with stub - // nodes. Specifically any node index that has `nodes[index]==1` will be - // slated for replacement with a delegate kernel specified by registration. - // Ownership of 'nodes_to_replace' and 'delegate' remains with the caller. - // WARNING: This is an experimental interface that is subject to change. - TfLiteStatus ReplaceNodeSubsetsWithDelegateKernels( - TfLiteRegistration registration, const TfLiteIntArray* nodes_to_replace, - TfLiteDelegate* delegate); - - // WARNING: This is an experimental interface that is subject to change. - // Gets the internal pointer to a TensorFlow lite node by node_index. - TfLiteStatus GetNodeAndRegistration(int node_index, TfLiteNode** node, - TfLiteRegistration** registration); - - // WARNING: This is an experimental interface that is subject to change. - // Entry point for C node plugin API to get a node by index. - static TfLiteStatus GetNodeAndRegistration(struct TfLiteContext*, - int node_index, TfLiteNode** node, - TfLiteRegistration** registration); - - // WARNING: This is an experimental interface that is subject to change. - // Gets an TfLiteIntArray* representing the execution plan. The interpreter - // owns this memory and it is only guaranteed to exist during the invocation - // of the delegate prepare. - TfLiteStatus GetExecutionPlan(TfLiteIntArray** execution_plan); - - // WARNING: This is an experimental interface that is subject to change. - // Entry point for C node plugin API to get the execution plan. - static TfLiteStatus GetExecutionPlan(struct TfLiteContext* context, - TfLiteIntArray** execution_plan); - - // Retrieve an existing external context by type. - TfLiteExternalContext* GetExternalContext(TfLiteExternalContextType type); - static TfLiteExternalContext* GetExternalContext( - struct TfLiteContext* context, TfLiteExternalContextType type); + private: + friend class InterpreterBuilder; + friend class InterpreterTest; // Set the value of an external context. static void SetExternalContext(struct TfLiteContext* context, @@ -587,105 +472,28 @@ class Interpreter { return ModifyGraphWithDelegate(owned_delegates_.back().get()); } - // Ensures that `tensors_` has at least `kTensorsCapacityHeadroom` extra - // capacity. Calling this function may invalidate existing pointers to - // tensors. After calling this function, adding `kTensorsCapacityHeadroom` - // more tensors won't invalidate the pointer to existing tensors. - void EnsureTensorsVectorCapacity() { - const size_t required_capacity = tensors_size() + kTensorsCapacityHeadroom; - if (required_capacity > tensors_.capacity()) { - tensors_.reserve(required_capacity); - context_.tensors = tensors_.data(); - } - } - - // The state of the Interpreter. - enum State { - // The interpreter isn't ready to be invoked. - // `AllocateTensor` need to be called to enter an invokable state. - kStateUninvokable = 0, - // The interpreter is ready to be invoked. - kStateInvokable, - // The interpreter is ready to be invoked, and graph can't be further - // modified. The interpreter will enter this state when calling - // `ModifyGraphWithDelegate` with `allow_dynamic_tensors=false`. - kStateInvokableAndImmutable, - }; - State state_ = kStateUninvokable; - // A pure C data structure used to communicate with the pure C plugin // interface. To avoid copying tensor metadata, this is also the definitive // structure to store tensors. - TfLiteContext context_; - - // Node inputs/outputs are stored in TfLiteNode and TfLiteRegistration stores - // function pointers to actual implementation. - std::vector> - nodes_and_registration_; - - // Whether the model is consistent. That is to say if the inputs and outputs - // of every node and the global inputs and outputs are valid indexes into - // the tensor array. - bool consistent_ = true; - - // Array of indices representing the tensors that are inputs to the - // interpreter. - std::vector inputs_; - - // Array of indices representing the tensors that are outputs to the - // interpreter. - std::vector outputs_; - - // Array of indices representing the tensors that are variable tensors. - std::vector variables_; + // This is the primary subgraph context. + TfLiteContext* context_; // The error reporter delegate that tflite will forward queries errors to. ErrorReporter* error_reporter_; - // Index of the next node to prepare. - // During Invoke(), Interpreter will allocate input tensors first, which are - // known to be fixed size. Then it will allocate outputs from nodes as many - // as possible. When there is a node that produces dynamic sized tensor. - // Interpreter will stop allocating tensors, set the value of next allocate - // node id, and execute the node to generate the output tensor before continue - // to allocate successors. This process repeats until all nodes are executed. - // NOTE: this relies on the order of nodes that is in topological order. - int next_execution_plan_index_to_prepare_; - - // WARNING: This is an experimental interface that is subject to change. - // This is a list of node indices (to index into nodes_and_registration). - // This represents a valid topological sort (dependency ordered) execution - // plan. In particular, it is valid for this ordering to contain only a - // subset of the node indices. - std::vector execution_plan_; - - // In the future, we'd like a TfLiteIntArray compatible representation. - // TODO(aselle): replace execution_plan_ with this. - std::unique_ptr plan_cache_; - - // Whether to delegate to NN API - std::unique_ptr nnapi_delegate_; - // List of delegates that have been installed and are owned by this // interpreter instance. Useful if client delegate ownership is burdensome. // WARNING: This is an experimental API and subject to change. // TODO(b/116667551): Use TfLiteExternalContext for storing state. std::vector owned_delegates_; - std::unique_ptr memory_planner_; - bool allow_buffer_handle_output_ = false; - // Tracking bit for whether a tensor was resized in the course of an op - // invocation. This is a useful hint to ensure that dynamic tensor outputs - // trigger downstream reallocation after op invocation. - bool tensor_resized_since_op_invoke_ = false; - - // Profiler for this interpreter instance. - profiling::Profiler* profiler_ = nullptr; - // List of active external contexts. TfLiteExternalContext* external_contexts_[kTfLiteMaxExternalContexts]; + + // Subgraphs + std::vector> subgraphs_; }; } // namespace tflite diff --git a/tensorflow/lite/interpreter_test.cc b/tensorflow/lite/interpreter_test.cc index 7f03c3ceba..78b5d1b887 100644 --- a/tensorflow/lite/interpreter_test.cc +++ b/tensorflow/lite/interpreter_test.cc @@ -38,7 +38,7 @@ class InterpreterTest : public ::testing::Test { } protected: - TfLiteContext* GetInterpreterContext() { return &interpreter_.context_; } + TfLiteContext* GetInterpreterContext() { return interpreter_.context_; } Interpreter interpreter_; }; @@ -566,7 +566,7 @@ TEST(BasicInterpreter, ThreeStepAllocate) { DynamicBuffer buf; StringRef str_ref = GetString(input, 0); buf.AddString(str_ref); - buf.WriteToTensor(output); + buf.WriteToTensorAsVector(output); return kTfLiteOk; }; @@ -1090,17 +1090,17 @@ class TestDelegate : public ::testing::Test { TfLiteIntArrayFree(nodes_to_separate); return kTfLiteOk; }; - delegate_.CopyToBufferHandle = - [](TfLiteContext* context, TfLiteDelegate* delegate, - TfLiteBufferHandle buffer_handle, void* data, - size_t size) -> TfLiteStatus { + delegate_.CopyToBufferHandle = [](TfLiteContext* context, + TfLiteDelegate* delegate, + TfLiteBufferHandle buffer_handle, + TfLiteTensor* tensor) -> TfLiteStatus { // TODO(ycling): Implement tests to test buffer copying logic. return kTfLiteOk; }; delegate_.CopyFromBufferHandle = [](TfLiteContext* context, TfLiteDelegate* delegate, - TfLiteBufferHandle buffer_handle, void* data, - size_t size) -> TfLiteStatus { + TfLiteBufferHandle buffer_handle, + TfLiteTensor* output) -> TfLiteStatus { // TODO(ycling): Implement tests to test buffer copying logic. return kTfLiteOk; }; diff --git a/tensorflow/lite/java/src/main/java/org/tensorflow/lite/DataType.java b/tensorflow/lite/java/src/main/java/org/tensorflow/lite/DataType.java index 41093e8ffe..bd47574f71 100644 --- a/tensorflow/lite/java/src/main/java/org/tensorflow/lite/DataType.java +++ b/tensorflow/lite/java/src/main/java/org/tensorflow/lite/DataType.java @@ -27,7 +27,10 @@ public enum DataType { UINT8(3), /** 64-bit signed integer. */ - INT64(4); + INT64(4), + + /** Strings. */ + STRING(5); private final int value; @@ -46,6 +49,8 @@ public enum DataType { return 1; case INT64: return 8; + case STRING: + return -1; } throw new IllegalArgumentException( "DataType error: DataType " + this + " is not supported yet"); @@ -82,6 +87,8 @@ public enum DataType { return "byte"; case INT64: return "long"; + case STRING: + return "string"; } throw new IllegalArgumentException( "DataType error: DataType " + this + " is not supported yet"); diff --git a/tensorflow/lite/java/src/main/java/org/tensorflow/lite/Tensor.java b/tensorflow/lite/java/src/main/java/org/tensorflow/lite/Tensor.java index 6ca47aa3ed..7aa24b4198 100644 --- a/tensorflow/lite/java/src/main/java/org/tensorflow/lite/Tensor.java +++ b/tensorflow/lite/java/src/main/java/org/tensorflow/lite/Tensor.java @@ -162,6 +162,8 @@ public final class Tensor { return DataType.UINT8; } else if (long.class.equals(c)) { return DataType.INT64; + } else if (String.class.equals(c)) { + return DataType.STRING; } } throw new IllegalArgumentException( diff --git a/tensorflow/lite/java/src/main/native/BUILD b/tensorflow/lite/java/src/main/native/BUILD index 2abba24345..8f95f14518 100644 --- a/tensorflow/lite/java/src/main/native/BUILD +++ b/tensorflow/lite/java/src/main/native/BUILD @@ -43,6 +43,7 @@ cc_library( "//tensorflow/lite:context", "//tensorflow/lite:framework", "//tensorflow/lite:schema_fbs_version", + "//tensorflow/lite:string_util", ], alwayslink = 1, ) diff --git a/tensorflow/lite/java/src/main/native/nativeinterpreterwrapper_jni.cc b/tensorflow/lite/java/src/main/native/nativeinterpreterwrapper_jni.cc index c7389c5811..1e98f94250 100644 --- a/tensorflow/lite/java/src/main/native/nativeinterpreterwrapper_jni.cc +++ b/tensorflow/lite/java/src/main/native/nativeinterpreterwrapper_jni.cc @@ -78,6 +78,8 @@ int getDataType(TfLiteType data_type) { return 3; case kTfLiteInt64: return 4; + case kTfLiteString: + return 5; default: return -1; } diff --git a/tensorflow/lite/java/src/main/native/tensor_jni.cc b/tensorflow/lite/java/src/main/native/tensor_jni.cc index 1d813d50da..82d2679de9 100644 --- a/tensorflow/lite/java/src/main/native/tensor_jni.cc +++ b/tensorflow/lite/java/src/main/native/tensor_jni.cc @@ -16,8 +16,10 @@ limitations under the License. #include "tensorflow/lite/java/src/main/native/tensor_jni.h" #include #include +#include "tensorflow/lite/c/c_api_internal.h" #include "tensorflow/lite/interpreter.h" #include "tensorflow/lite/java/src/main/native/exception_jni.h" +#include "tensorflow/lite/string_util.h" namespace { @@ -48,7 +50,7 @@ TfLiteTensor* GetTensorFromHandle(JNIEnv* env, jlong handle) { return reinterpret_cast(handle)->tensor(); } -size_t elementByteSize(TfLiteType data_type) { +size_t ElementByteSize(TfLiteType data_type) { // The code in this file makes the assumption that the // TensorFlow TF_DataTypes and the Java primitive types // have the same byte sizes. Validate that: @@ -77,11 +79,11 @@ size_t elementByteSize(TfLiteType data_type) { } } -size_t writeOneDimensionalArray(JNIEnv* env, jobject object, TfLiteType type, +size_t WriteOneDimensionalArray(JNIEnv* env, jobject object, TfLiteType type, void* dst, size_t dst_size) { jarray array = static_cast(object); const int num_elements = env->GetArrayLength(array); - size_t to_copy = num_elements * elementByteSize(type); + size_t to_copy = num_elements * ElementByteSize(type); if (to_copy > dst_size) { throwException(env, kIllegalStateException, "Internal error: cannot write Java array of %d bytes to " @@ -126,10 +128,10 @@ size_t writeOneDimensionalArray(JNIEnv* env, jobject object, TfLiteType type, } } -size_t readOneDimensionalArray(JNIEnv* env, TfLiteType data_type, +size_t ReadOneDimensionalArray(JNIEnv* env, TfLiteType data_type, const void* src, size_t src_size, jarray dst) { const int len = env->GetArrayLength(dst); - const size_t size = len * elementByteSize(data_type); + const size_t size = len * ElementByteSize(data_type); if (size > src_size) { throwException( env, kIllegalStateException, @@ -170,17 +172,17 @@ size_t readOneDimensionalArray(JNIEnv* env, TfLiteType data_type, return 0; } -size_t readMultiDimensionalArray(JNIEnv* env, TfLiteType data_type, char* src, +size_t ReadMultiDimensionalArray(JNIEnv* env, TfLiteType data_type, char* src, size_t src_size, int dims_left, jarray dst) { if (dims_left == 1) { - return readOneDimensionalArray(env, data_type, src, src_size, dst); + return ReadOneDimensionalArray(env, data_type, src, src_size, dst); } else { jobjectArray ndarray = static_cast(dst); int len = env->GetArrayLength(ndarray); size_t size = 0; for (int i = 0; i < len; ++i) { jarray row = static_cast(env->GetObjectArrayElement(ndarray, i)); - size += readMultiDimensionalArray(env, data_type, src + size, + size += ReadMultiDimensionalArray(env, data_type, src + size, src_size - size, dims_left - 1, row); env->DeleteLocalRef(row); if (env->ExceptionCheck()) return size; @@ -189,10 +191,43 @@ size_t readMultiDimensionalArray(JNIEnv* env, TfLiteType data_type, char* src, } } -size_t writeMultiDimensionalArray(JNIEnv* env, jobject src, TfLiteType type, +// Returns the total number of strings read. +int ReadMultiDimensionalStringArray(JNIEnv* env, TfLiteTensor* tensor, + int dims_left, int start_str_index, + jarray dst) { + jobjectArray object_array = static_cast(dst); + int len = env->GetArrayLength(object_array); + int num_strings_read = 0; + + // If dst is a 1-dimensional array, copy the strings into it. Else + // recursively call ReadMultiDimensionalStringArray over sub-dimensions. + if (dims_left == 1) { + for (int i = 0; i < len; ++i) { + const tflite::StringRef strref = + tflite::GetString(tensor, start_str_index + num_strings_read); + jstring string_dest = env->NewStringUTF(strref.str); + env->SetObjectArrayElement(object_array, i, string_dest); + env->DeleteLocalRef(string_dest); + ++num_strings_read; + } + } else { + for (int i = 0; i < len; ++i) { + jarray row = + static_cast(env->GetObjectArrayElement(object_array, i)); + num_strings_read += ReadMultiDimensionalStringArray( + env, tensor, dims_left - 1, start_str_index + num_strings_read, row); + env->DeleteLocalRef(row); + if (env->ExceptionCheck()) return num_strings_read; + } + } + + return num_strings_read; +} + +size_t WriteMultiDimensionalArray(JNIEnv* env, jobject src, TfLiteType type, int dims_left, char** dst, int dst_size) { if (dims_left <= 1) { - return writeOneDimensionalArray(env, src, type, *dst, dst_size); + return WriteOneDimensionalArray(env, src, type, *dst, dst_size); } else { jobjectArray ndarray = static_cast(src); int len = env->GetArrayLength(ndarray); @@ -200,7 +235,7 @@ size_t writeMultiDimensionalArray(JNIEnv* env, jobject src, TfLiteType type, for (int i = 0; i < len; ++i) { jobject row = env->GetObjectArrayElement(ndarray, i); char* next_dst = *dst + sz; - sz += writeMultiDimensionalArray(env, row, type, dims_left - 1, &next_dst, + sz += WriteMultiDimensionalArray(env, row, type, dims_left - 1, &next_dst, dst_size - sz); env->DeleteLocalRef(row); if (env->ExceptionCheck()) return sz; @@ -209,6 +244,44 @@ size_t writeMultiDimensionalArray(JNIEnv* env, jobject src, TfLiteType type, } } +void PopulateStringDynamicBuffer(JNIEnv* env, jobject src, + tflite::DynamicBuffer* dst_buffer, + int dims_left) { + jobjectArray object_array = static_cast(src); + const int num_elements = env->GetArrayLength(object_array); + + // If src is a 1-dimensional array, add the strings into dst_buffer. Else + // recursively call populateStringDynamicBuffer over sub-dimensions. + if (dims_left <= 1) { + for (int i = 0; i < num_elements; ++i) { + jstring string_obj = + static_cast(env->GetObjectArrayElement(object_array, i)); + const char* chars = env->GetStringUTFChars(string_obj, nullptr); + // + 1 for terminating character. + const int byte_len = env->GetStringUTFLength(string_obj) + 1; + dst_buffer->AddString(chars, byte_len); + env->ReleaseStringUTFChars(string_obj, chars); + env->DeleteLocalRef(string_obj); + } + } else { + for (int i = 0; i < num_elements; ++i) { + jobject row = env->GetObjectArrayElement(object_array, i); + PopulateStringDynamicBuffer(env, row, dst_buffer, dims_left - 1); + env->DeleteLocalRef(row); + if (env->ExceptionCheck()) return; + } + } +} + +void WriteMultiDimensionalStringArray(JNIEnv* env, jobject src, + TfLiteTensor* tensor) { + tflite::DynamicBuffer dst_buffer; + PopulateStringDynamicBuffer(env, src, &dst_buffer, tensor->dims->size); + if (!env->ExceptionCheck()) { + dst_buffer.WriteToTensor(tensor, /*new_shape=*/nullptr); + } +} + } // namespace JNIEXPORT jlong JNICALL Java_org_tensorflow_lite_Tensor_create( @@ -266,8 +339,14 @@ Java_org_tensorflow_lite_Tensor_readMultiDimensionalArray(JNIEnv* env, "Internal error: Cannot copy empty/scalar Tensors."); return; } - readMultiDimensionalArray(env, tensor->type, tensor->data.raw, tensor->bytes, - num_dims, static_cast(value)); + if (tensor->type == kTfLiteString) { + ReadMultiDimensionalStringArray(env, tensor, num_dims, 0, + static_cast(value)); + } else { + ReadMultiDimensionalArray(env, tensor->type, tensor->data.raw, + tensor->bytes, num_dims, + static_cast(value)); + } } JNIEXPORT void JNICALL @@ -277,7 +356,7 @@ Java_org_tensorflow_lite_Tensor_writeMultiDimensionalArray(JNIEnv* env, jobject src) { TfLiteTensor* tensor = GetTensorFromHandle(env, handle); if (tensor == nullptr) return; - if (tensor->data.raw == nullptr) { + if (tensor->type != kTfLiteString && tensor->data.raw == nullptr) { throwException(env, kIllegalArgumentException, "Internal error: Target Tensor hasn't been allocated."); return; @@ -287,8 +366,12 @@ Java_org_tensorflow_lite_Tensor_writeMultiDimensionalArray(JNIEnv* env, "Internal error: Cannot copy empty/scalar Tensors."); return; } - writeMultiDimensionalArray(env, src, tensor->type, tensor->dims->size, - &tensor->data.raw, tensor->bytes); + if (tensor->type == kTfLiteString) { + WriteMultiDimensionalStringArray(env, src, tensor); + } else { + WriteMultiDimensionalArray(env, src, tensor->type, tensor->dims->size, + &tensor->data.raw, tensor->bytes); + } } JNIEXPORT jint JNICALL Java_org_tensorflow_lite_Tensor_dtype(JNIEnv* env, diff --git a/tensorflow/lite/java/src/test/java/org/tensorflow/lite/DataTypeTest.java b/tensorflow/lite/java/src/test/java/org/tensorflow/lite/DataTypeTest.java index 6d6417f895..8412ec0e9d 100644 --- a/tensorflow/lite/java/src/test/java/org/tensorflow/lite/DataTypeTest.java +++ b/tensorflow/lite/java/src/test/java/org/tensorflow/lite/DataTypeTest.java @@ -30,6 +30,7 @@ public final class DataTypeTest { assertThat(DataType.INT32.byteSize()).isEqualTo(4); assertThat(DataType.UINT8.byteSize()).isEqualTo(1); assertThat(DataType.INT64.byteSize()).isEqualTo(8); + assertThat(DataType.STRING.byteSize()).isEqualTo(-1); } @Test diff --git a/tensorflow/lite/java/src/test/java/org/tensorflow/lite/NativeInterpreterWrapperTest.java b/tensorflow/lite/java/src/test/java/org/tensorflow/lite/NativeInterpreterWrapperTest.java index 07d334c33b..b00efa77cb 100644 --- a/tensorflow/lite/java/src/test/java/org/tensorflow/lite/NativeInterpreterWrapperTest.java +++ b/tensorflow/lite/java/src/test/java/org/tensorflow/lite/NativeInterpreterWrapperTest.java @@ -43,6 +43,9 @@ public final class NativeInterpreterWrapperTest { private static final String BYTE_MODEL_PATH = "tensorflow/lite/java/src/testdata/uint8.bin"; + private static final String STRING_MODEL_PATH = + "tensorflow/lite/java/src/testdata/string.bin"; + private static final String QUANTIZED_MODEL_PATH = "tensorflow/lite/java/src/testdata/quantized.bin"; @@ -224,6 +227,50 @@ public final class NativeInterpreterWrapperTest { wrapper.close(); } + @Test + public void testRunWithString() { + NativeInterpreterWrapper wrapper = new NativeInterpreterWrapper(STRING_MODEL_PATH); + String[] oneD = {"s1", "s22", "s333"}; + String[][] twoD = {oneD, oneD, oneD, oneD, oneD, oneD, oneD, oneD}; + String[][][] threeD = {twoD, twoD, twoD, twoD, twoD, twoD, twoD, twoD}; + String[][][][] fourD = {threeD, threeD}; + Object[] inputs = {fourD}; + String[][][][] parsedOutputs = new String[2][4][4][12]; + Map outputs = new HashMap<>(); + outputs.put(0, parsedOutputs); + wrapper.run(inputs, outputs); + String[] outputOneD = parsedOutputs[0][0][0]; + String[] expected = { + "s1", "s22", "s333", "s1", "s22", "s333", "s1", "s22", "s333", "s1", "s22", "s333" + }; + assertThat(outputOneD).isEqualTo(expected); + wrapper.close(); + } + + @Test + public void testRunWithString_wrongShapeError() { + NativeInterpreterWrapper wrapper = new NativeInterpreterWrapper(STRING_MODEL_PATH); + String[] oneD = {"s1", "s22", "s333"}; + String[][] twoD = {oneD, oneD, oneD, oneD, oneD, oneD, oneD, oneD}; + String[][][] threeD = {twoD, twoD, twoD, twoD, twoD, twoD, twoD, twoD}; + String[][][][] fourD = {threeD, threeD}; + Object[] inputs = {fourD}; + String[][][][] parsedOutputs = new String[2][4][4][10]; + Map outputs = new HashMap<>(); + outputs.put(0, parsedOutputs); + try { + wrapper.run(inputs, outputs); + fail(); + } catch (IllegalArgumentException e) { + assertThat(e) + .hasMessageThat() + .contains( + "Cannot copy between a TensorFlowLite tensor with shape [2, 4, 4, 12] and " + + "a Java object with shape [2, 4, 4, 10]"); + } + wrapper.close(); + } + @Test public void testRunWithByteBufferHavingBytes() { NativeInterpreterWrapper wrapper = new NativeInterpreterWrapper(BYTE_MODEL_PATH); diff --git a/tensorflow/lite/java/src/testdata/string.bin b/tensorflow/lite/java/src/testdata/string.bin new file mode 100644 index 0000000000000000000000000000000000000000..36a2509acdfa17841d0c128674e7b4e382ad00fc GIT binary patch literal 584 zcmb1OU|3JeSkj0_A6 z2@DJj{2+b)&i)F{`FUljMJ1^zdJHTK3=Ad=3=BF95IrFNihuwAhyDBiAH)`5U|?Wk zU;wKF@j>RE`1k++nScNP^Dv0OZ3l^i>=I#MVBlb2V31*i*bA~7gh6J2+*ZKAz_0^q zUflox{}~tAXYL2@ATi%W_!^U~up^9o8!7{K=Mfb9pV0hs~P z2eONSq23unGcX*2*ueyn2bl??RiI%4QqRl4zz~#LoRL_Nst+c?Zes)c7ZSc44D3kZ z3v(~Xe2{w$!0Iu>2xM-4X-Pq8Nqk9aUU7aASRD)4T##N+9Evc2NKovtGl1O1z#zf^ z3O@!020;)XB&)%|09FNZI|zgP4bl(7AT|R78v_Re3j-V2d`LWj)Pej4!k};isRhvx Mwd`QE3=9ln00k8^3;+NC literal 0 HcmV?d00001 diff --git a/tensorflow/lite/kernels/BUILD b/tensorflow/lite/kernels/BUILD index 010ba83466..00d9d1feae 100644 --- a/tensorflow/lite/kernels/BUILD +++ b/tensorflow/lite/kernels/BUILD @@ -183,6 +183,7 @@ cc_library( "exp.cc", "expand_dims.cc", "fake_quant.cc", + "fill.cc", "floor.cc", "floor_div.cc", "floor_mod.cc", @@ -197,6 +198,7 @@ cc_library( "lstm.cc", "maximum_minimum.cc", "mfcc.cc", + "mirror_pad.cc", "mul.cc", "neg.cc", "one_hot.cc", @@ -219,6 +221,8 @@ cc_library( "sparse_output_fully_connected.cc", "sparse_to_dense.cc", "split.cc", + "split_v.cc", + "squared_difference.cc", "squeeze.cc", "strided_slice.cc", "sub.cc", @@ -1064,6 +1068,22 @@ tf_cc_test( ], ) +tf_cc_test( + name = "split_v_test", + size = "small", + srcs = ["split_v_test.cc"], + tags = [ + "no_oss", + "tflite_not_portable_ios", + ], + deps = [ + ":builtin_ops", + "//tensorflow/lite:framework", + "//tensorflow/lite/kernels:test_util", + "@com_google_googletest//:gtest", + ], +) + tf_cc_test( name = "squeeze_test", size = "small", @@ -1379,6 +1399,32 @@ tf_cc_test( ], ) +tf_cc_test( + name = "squared_difference_test", + size = "small", + srcs = ["squared_difference_test.cc"], + tags = ["tflite_not_portable_ios"], + deps = [ + ":builtin_ops", + "//tensorflow/lite:framework", + "//tensorflow/lite/kernels:test_util", + "@com_google_googletest//:gtest", + ], +) + +tf_cc_test( + name = "fill_test", + size = "small", + srcs = ["fill_test.cc"], + tags = ["tflite_not_portable_ios"], + deps = [ + ":builtin_ops", + "//tensorflow/lite:framework", + "//tensorflow/lite/kernels:test_util", + "@com_google_googletest//:gtest", + ], +) + filegroup( name = "all_files", srcs = glob( @@ -1392,3 +1438,14 @@ filegroup( ) tflite_portable_test_suite() + +tf_cc_test( + name = "mirror_pad_test", + srcs = ["mirror_pad_test.cc"], + deps = [ + ":builtin_ops", + ":test_util", + "//tensorflow/lite:framework", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/tensorflow/lite/kernels/activations.cc b/tensorflow/lite/kernels/activations.cc index 9c525d9640..a766542560 100644 --- a/tensorflow/lite/kernels/activations.cc +++ b/tensorflow/lite/kernels/activations.cc @@ -45,6 +45,11 @@ struct LogSoftmaxOpData : public OpData { int32_t reverse_scaling_right_shift = 0; }; +struct PreluOpData : public OpData { + int32_t output_multiplier = 0; + int output_shift = 0; +}; + void* Init(TfLiteContext* context, const char* buffer, size_t length) { // This is a builtin op, so we don't use the contents in 'buffer', if any. // Instead, we allocate a new object to carry information from Prepare() to @@ -57,6 +62,10 @@ void* LogSoftmaxInit(TfLiteContext* context, const char* buffer, return new LogSoftmaxOpData; } +void* PreluInit(TfLiteContext* context, const char* buffer, size_t length) { + return new PreluOpData; +} + void Free(TfLiteContext* context, void* buffer) { delete reinterpret_cast(buffer); } @@ -65,6 +74,10 @@ void LogSoftmaxFree(TfLiteContext* context, void* buffer) { delete reinterpret_cast(buffer); } +void PreluFree(TfLiteContext* context, void* buffer) { + delete reinterpret_cast(buffer); +} + TfLiteStatus GenericPrepare(TfLiteContext* context, TfLiteNode* node) { TF_LITE_ENSURE_EQ(context, NumInputs(node), 1); TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1); @@ -253,13 +266,18 @@ TfLiteStatus PreluPrepare(TfLiteContext* context, TfLiteNode* node) { const TfLiteTensor* input = GetInput(context, node, 0); TfLiteTensor* output = GetOutput(context, node, 0); const TfLiteTensor* alpha = GetInput(context, node, 1); + PreluOpData* data = reinterpret_cast(node->user_data); - // Currently only Float32 is supported - // TODO(ycling): Support other data types. - TF_LITE_ENSURE_EQ(context, input->type, kTfLiteFloat32); - TF_LITE_ENSURE_EQ(context, alpha->type, kTfLiteFloat32); + TF_LITE_ENSURE_EQ(context, input->type, alpha->type); output->type = input->type; + if (output->type == kTfLiteUInt8 || output->type == kTfLiteInt16) { + double real_multiplier = + input->params.scale * alpha->params.scale / output->params.scale; + QuantizeMultiplierSmallerThanOneExp( + real_multiplier, &data->output_multiplier, &data->output_shift); + } + // PRelu (parameteric Relu) shares the same alpha value on "shared axis". // This means it's always required to "broadcast" alpha values in PRelu. TfLiteIntArray* output_size = nullptr; @@ -288,8 +306,8 @@ TfLiteStatus ReluEval(TfLiteContext* context, TfLiteNode* node) { return kTfLiteOk; } break; default: - context->ReportError(context, "Only float32 supported currently, got %d.", - input->type); + context->ReportError(context, "Only float32 supported currently, got %s.", + TfLiteTypeGetName(input->type)); return kTfLiteError; } } @@ -309,8 +327,8 @@ TfLiteStatus Relu1Eval(TfLiteContext* context, TfLiteNode* node) { return kTfLiteOk; } break; default: - context->ReportError(context, "Only float32 supported currently, got %d.", - input->type); + context->ReportError(context, "Only float32 supported currently, got %s.", + TfLiteTypeGetName(input->type)); return kTfLiteError; } } @@ -327,9 +345,24 @@ TfLiteStatus Relu6Eval(TfLiteContext* context, TfLiteNode* node) { for (; in < in_end; in++, out++) *out = std::min(std::max(0.f, *in), 6.f); return kTfLiteOk; } break; + case kTfLiteUInt8: { + ActivationParams params; + params.activation_type = FusedActivationFunctionType::kRelu6; + params.quantized_activation_min = std::max( + 0, output->params.zero_point + + static_cast(roundf(0.f / output->params.scale))); + params.quantized_activation_max = std::min( + 255, output->params.zero_point + + static_cast(roundf(6.f / output->params.scale))); + optimized_ops::ReluX(params, GetTensorShape(input), + GetTensorData(input), GetTensorShape(output), + GetTensorData(output)); + return kTfLiteOk; + } break; default: - context->ReportError(context, "Only float32 supported currently, got %d.", - input->type); + context->ReportError( + context, "Only float32 and uint8 supported currently, got %s.", + TfLiteTypeGetName(input->type)); return kTfLiteError; } } @@ -367,8 +400,8 @@ TfLiteStatus TanhEval(TfLiteContext* context, TfLiteNode* node) { return kTfLiteOk; } break; default: - context->ReportError(context, "Only float32 supported currently, got %d.", - input->type); + context->ReportError(context, "Only float32 supported currently, got %s.", + TfLiteTypeGetName(input->type)); return kTfLiteError; } } @@ -407,9 +440,8 @@ TfLiteStatus SigmoidEval(TfLiteContext* context, TfLiteNode* node) { break; } default: - context->ReportError(context, "Only float32 supported currently, got %d.", - input->type); - return kTfLiteError; + context->ReportError(context, "Only float32 supported currently, got %s.", + TfLiteTypeGetName(input->type)); } return kTfLiteOk; } @@ -604,8 +636,8 @@ TfLiteStatus SoftmaxEval(TfLiteContext* context, TfLiteNode* node) { } default: context->ReportError( - context, "Only float32 and uint8_t supported currently, got %d.", - input->type); + context, "Only float32 and uint8_t supported currently, got %s.", + TfLiteTypeGetName(input->type)); return kTfLiteError; } } @@ -636,8 +668,8 @@ TfLiteStatus LogSoftmaxEval(TfLiteContext* context, TfLiteNode* node) { return kTfLiteOk; } default: - context->ReportError(context, "Only float32 supported currently., got %d", - input->type); + context->ReportError(context, "Only float32 supported currently., got %s", + TfLiteTypeGetName(input->type)); return kTfLiteError; } } @@ -651,16 +683,57 @@ TfLiteStatus PreluEval(TfLiteContext* context, TfLiteNode* node) { const TfLiteTensor* input = GetInput(context, node, 0); const TfLiteTensor* alpha = GetInput(context, node, 1); TfLiteTensor* output = GetOutput(context, node, 0); - if (input->type != kTfLiteFloat32) { - context->ReportError(context, "Only float32 supported currently, got %d.", - input->type); - return kTfLiteError; + const PreluOpData* data = reinterpret_cast(node->user_data); + switch (input->type) { + case kTfLiteFloat32: { + reference_ops::BroadcastBinaryFunction4DSlow( + GetTensorShape(input), GetTensorData(input), + GetTensorShape(alpha), GetTensorData(alpha), + GetTensorShape(output), GetTensorData(output), + ApplyPrelu); + return kTfLiteOk; + } break; + case kTfLiteUInt8: { + PreluParams op_params; + op_params.input_offset = -input->params.zero_point; + op_params.alpha_offset = -alpha->params.zero_point; + op_params.output_offset = output->params.zero_point; + op_params.output_multiplier = data->output_multiplier; + op_params.output_shift = data->output_shift; + reference_ops::BroadcastPrelu4DSlow( + op_params, GetTensorShape(input), GetTensorData(input), + GetTensorShape(alpha), GetTensorData(alpha), + GetTensorShape(output), GetTensorData(output)); + return kTfLiteOk; + } break; + default: + context->ReportError(context, + "Only float32, uint8 supported currently, got %d.", + TfLiteTypeGetName(input->type)); + return kTfLiteError; + } +} + +TfLiteStatus LeakyReluEval(TfLiteContext* context, TfLiteNode* node) { + const TfLiteTensor* input = GetInput(context, node, 0); + TfLiteTensor* output = GetOutput(context, node, 0); + const auto* params = + reinterpret_cast(node->builtin_data); + + LeakyReluParams op_params; + op_params.alpha = params->alpha; + switch (input->type) { + case kTfLiteFloat32: { + optimized_ops::LeakyRelu( + op_params, GetTensorShape(input), GetTensorData(input), + GetTensorShape(output), GetTensorData(output)); + return kTfLiteOk; + } break; + default: + context->ReportError(context, "Only float32 supported currently, got %s.", + TfLiteTypeGetName(input->type)); + return kTfLiteError; } - reference_ops::BroadcastBinaryFunction4DSlow( - GetTensorShape(input), GetTensorData(input), GetTensorShape(alpha), - GetTensorData(alpha), GetTensorShape(output), - GetTensorData(output), ApplyPrelu); - return kTfLiteOk; } } // namespace activations @@ -715,12 +788,19 @@ TfLiteRegistration* Register_LOG_SOFTMAX() { } TfLiteRegistration* Register_PRELU() { - static TfLiteRegistration r = {/*init=*/nullptr, /*free=*/nullptr, + static TfLiteRegistration r = {activations::PreluInit, activations::PreluFree, activations::PreluPrepare, activations::PreluEval}; return &r; } +TfLiteRegistration* Register_LEAKY_RELU() { + static TfLiteRegistration r = {/*init=*/nullptr, /*free=*/nullptr, + activations::GenericPrepare, + activations::LeakyReluEval}; + return &r; +} + } // namespace builtin } // namespace ops } // namespace tflite diff --git a/tensorflow/lite/kernels/activations_test.cc b/tensorflow/lite/kernels/activations_test.cc index fff4121dc0..67f137baff 100644 --- a/tensorflow/lite/kernels/activations_test.cc +++ b/tensorflow/lite/kernels/activations_test.cc @@ -170,6 +170,29 @@ TEST(FloatActivationsOpTest, Tanh) { }))); } +TEST(QuantizedActivationsOpTest, Relu6) { + const float kMin = -1; + const float kMax = 127.f / 128.f; + QuantizedActivationsOpModel m( + BuiltinOperator_RELU6, + /*input=*/{TensorType_UINT8, {1, 2, 4, 1}, 8 * kMin, 8 * kMax}, + /*output=*/{TensorType_UINT8, {1, 2, 4, 1}, 8 * kMin, 8 * kMax}); + m.SetInput({ + 0, -6, 2, 4, // + 3, -2, 10, 1, // + }); + m.Invoke(); + EXPECT_THAT(m.GetDequantizedOutput(), + ElementsAreArray(ArrayFloatNear( + { + 0, 0, 2, 4, // + 3, 0, 6, 1, // + }, + kQuantizedTolerance))); + EXPECT_THAT(m.GetOutput(), + ElementsAreArray({128, 128, 160, 192, 176, 128, 224, 144})); +} + TEST(QuantizedActivationsOpTest, Tanh) { const float kMin = -1; const float kMax = 127.f / 128.f; @@ -563,15 +586,29 @@ TEST(QuantizedActivationsOpTest, LogSoftmax) { ElementsAreArray({189, 93, 221, 253, 142, 63, 255, 111})); } -class PReluOpModel : public SingleOpModel { +// A base class of PRelu op model. It provides the constructor for +// FloatPReluOpModel and QuantizedPReluOpModel. +class BasePReluOpModel : public SingleOpModel { public: - PReluOpModel(const TensorData& input, const TensorData& alpha) { + BasePReluOpModel(const TensorData& input, const TensorData& alpha) { input_ = AddInput(input); alpha_ = AddInput(alpha); - output_ = AddOutput(input); + output_ = AddOutput({input.type, input.shape, input.min, input.max}); SetBuiltinOp(BuiltinOperator_PRELU, BuiltinOptions_NONE, 0); BuildInterpreter({GetShape(input_), GetShape(alpha_)}); } + + protected: + int input_; + int alpha_; + int output_; +}; + +// The FloatPReluOpModel class handles float input and output. +class FloatPReluOpModel : public BasePReluOpModel { + public: + using BasePReluOpModel::BasePReluOpModel; + void SetInput(std::initializer_list data) { PopulateTensor(input_, data); } @@ -579,16 +616,35 @@ class PReluOpModel : public SingleOpModel { PopulateTensor(alpha_, data); } std::vector GetOutput() { return ExtractVector(output_); } +}; - protected: - int input_; - int alpha_; - int output_; +// The QuantizedPReluOpModel class handles quantized input and output. +class QuantizedPReluOpModel : public BasePReluOpModel { + public: + using BasePReluOpModel::BasePReluOpModel; + + template + void SetInput(std::initializer_list data) { + QuantizeAndPopulate(input_, data); + } + template + void SetAlpha(std::initializer_list data) { + QuantizeAndPopulate(alpha_, data); + } + template + std::vector GetOutput() { + return ExtractVector(output_); + } + template + std::vector GetDequantizedOutput() { + return Dequantize(ExtractVector(output_), GetScale(output_), + GetZeroPoint(output_)); + } }; TEST(FloatActivationsOpTest, PRelu) { - PReluOpModel m({TensorType_FLOAT32, {1, 2, 2, 3}}, - {TensorType_FLOAT32, {1, 1, 3}}); + FloatPReluOpModel m({TensorType_FLOAT32, {1, 2, 2, 3}}, + {TensorType_FLOAT32, {1, 1, 3}}); m.SetInput({ 0.0f, 0.0f, 0.0f, // Row 1, Column 1 @@ -606,6 +662,69 @@ TEST(FloatActivationsOpTest, PRelu) { })); } +TEST(QuantizedActivationsOpTest, PRelu) { + const float kMin = -1; + const float kMax = 127.f / 128.f; + QuantizedPReluOpModel m({TensorType_UINT8, {1, 2, 2, 3}, kMin, kMax}, + {TensorType_UINT8, {1, 1, 3}, kMin, kMax}); + m.SetInput({ + 0.0f, 0.0f, 0.0f, // Row 1, Column 1 + 0.5f, 0.5f, 0.5f, // Row 1, Column 2 + -1.0f, -1.0f, -1.0f, // Row 2, Column 1 + -0.25f, -0.25f, -0.25f, // Row 1, Column 2 + }); + m.SetAlpha({0.0f, 0.5f, -0.5f}); + m.Invoke(); + EXPECT_THAT(m.GetDequantizedOutput(), + ElementsAreArray(ArrayFloatNear( + { + 0.0f, 0.0f, 0.0f, // Row 1, Column 1 + 0.5f, 0.5f, 0.5f, // Row 1, Column 2 + 0.0f, -0.5f, 0.5f, // Row 2, Column 1 + 0.0f, -0.125f, 0.125f, // Row 1, Column 2 + }, + kQuantizedTolerance))); + EXPECT_THAT(m.GetOutput(), ElementsAreArray({ + 128, 128, 128, // Row 1, Column 1 + 192, 192, 192, // Row 1, Column 2 + 128, 64, 192, // Row 2, Column 1 + 128, 112, 144, // Row 1, Column 2 + })); +} + +class LeakyReluOpModel : public SingleOpModel { + public: + LeakyReluOpModel(const TensorData& input, float alpha) { + input_ = AddInput(input); + output_ = AddOutput(input); + SetBuiltinOp(BuiltinOperator_LEAKY_RELU, BuiltinOptions_LeakyReluOptions, + CreateLeakyReluOptions(builder_, alpha).Union()); + BuildInterpreter({GetShape(input_)}); + } + void SetInput(std::initializer_list data) { + PopulateTensor(input_, data); + } + std::vector GetOutput() { return ExtractVector(output_); } + + protected: + int input_; + int output_; +}; + +TEST(FloatActivationsOpTest, LeakyRelu) { + LeakyReluOpModel m({TensorType_FLOAT32, {2, 3}}, 0.5f); + + m.SetInput({ + 0.0f, 1.0f, 3.0f, // Row 1 + 1.0f, -1.0f, -2.0f, // Row 2 + }); + m.Invoke(); + EXPECT_THAT(m.GetOutput(), ElementsAreArray({ + 0.0f, 1.0f, 3.0f, // Row 1 + 1.0f, -0.5f, -1.0f, // Row 2 + })); +} + } // namespace } // namespace tflite diff --git a/tensorflow/lite/kernels/conv.cc b/tensorflow/lite/kernels/conv.cc index 0c14b9eb65..1fd870be93 100644 --- a/tensorflow/lite/kernels/conv.cc +++ b/tensorflow/lite/kernels/conv.cc @@ -24,7 +24,6 @@ limitations under the License. #include "tensorflow/lite/c/c_api_internal.h" #include "tensorflow/lite/kernels/eigen_support.h" #include "tensorflow/lite/kernels/gemm_support.h" -#include "tensorflow/lite/kernels/internal/optimized/cblas_conv.h" #include "tensorflow/lite/kernels/internal/optimized/multithreaded_conv.h" #include "tensorflow/lite/kernels/internal/optimized/optimized_ops.h" #include "tensorflow/lite/kernels/internal/quantization_util.h" @@ -491,11 +490,10 @@ void EvalFloat(TfLiteContext* context, TfLiteNode* node, CalculateActivationRange(params->activation, &output_activation_min, &output_activation_max); KernelType effective_kernel_type; - if ((kernel_type == kMultithreadOptimized || - kernel_type == kCblasOptimized) && + if ((kernel_type == kMultithreadOptimized) && (params->dilation_width_factor != 1 || params->dilation_height_factor != 1)) { - // kMultithreadOptimized and kCblasOptimized do not support dilation. + // kMultithreadOptimized does not support dilation. // Therefore, fallback to optimized. effective_kernel_type = kGenericOptimized; } else { @@ -521,6 +519,7 @@ void EvalFloat(TfLiteContext* context, TfLiteNode* node, GetTensorData(im2col)); break; } + case kCblasOptimized: case kGenericOptimized: { optimized_ops::Conv(op_params, GetTensorShape(input), GetTensorData(input), GetTensorShape(filter), @@ -546,15 +545,6 @@ void EvalFloat(TfLiteContext* context, TfLiteNode* node, GetTensorData(im2col)); break; } - case kCblasOptimized: { - cblas_ops::Conv(op_params, GetTensorShape(input), - GetTensorData(input), GetTensorShape(filter), - GetTensorData(filter), GetTensorShape(bias), - GetTensorData(bias), GetTensorShape(output), - GetTensorData(output), GetTensorShape(im2col), - GetTensorData(im2col)); - break; - } } } diff --git a/tensorflow/lite/kernels/eigen_support.cc b/tensorflow/lite/kernels/eigen_support.cc index 44e0086ad8..bad5975a7c 100644 --- a/tensorflow/lite/kernels/eigen_support.cc +++ b/tensorflow/lite/kernels/eigen_support.cc @@ -34,6 +34,15 @@ static_assert( "kDefaultArenaAlignment doesn't comply with Eigen alignment requirement."); #endif // EIGEN_DONT_ALIGN +// Helper routine for updating the global Eigen thread count used for OpenMP. +void SetEigenNbThreads(int threads) { +#if defined(EIGEN_HAS_OPENMP) + // The global Eigen thread count is only used when OpenMP is enabled. As this + // call causes problems with tsan, make it only when OpenMP is available. + Eigen::setNbThreads(context->recommended_num_threads); +#endif // defined(EIGEN_HAS_OPENMP) +} + // We have a single global threadpool for all convolution operations. This means // that inferences started from different threads may block each other, but // since the underlying resource of CPU cores should be consumed by the @@ -78,7 +87,7 @@ void InitDevice(TfLiteContext* context, RefCountedEigenContext* ptr) { } TfLiteStatus Refresh(TfLiteContext* context) { - Eigen::setNbThreads(context->recommended_num_threads); + SetEigenNbThreads(context->recommended_num_threads); auto* ptr = GetEigenContext(context); if (ptr != nullptr) { @@ -94,7 +103,7 @@ void IncrementUsageCounter(TfLiteContext* context) { auto* ptr = GetEigenContext(context); if (ptr == nullptr) { if (context->recommended_num_threads != -1) { - Eigen::setNbThreads(context->recommended_num_threads); + SetEigenNbThreads(context->recommended_num_threads); } ptr = new RefCountedEigenContext; ptr->type = kTfLiteEigenContext; diff --git a/tensorflow/lite/kernels/elementwise.cc b/tensorflow/lite/kernels/elementwise.cc index 416a69eb0e..a79388b900 100644 --- a/tensorflow/lite/kernels/elementwise.cc +++ b/tensorflow/lite/kernels/elementwise.cc @@ -15,6 +15,7 @@ limitations under the License. #include #include "tensorflow/lite/c/c_api_internal.h" +#include "tensorflow/lite/kernels/internal/reference/reference_ops.h" #include "tensorflow/lite/kernels/internal/tensor.h" #include "tensorflow/lite/kernels/kernel_util.h" @@ -74,6 +75,10 @@ inline TfLiteStatus EvalLogical(TfLiteContext* context, TfLiteNode* node, return EvalImpl(context, node, bool_func, kTfLiteBool); } +TfLiteStatus AbsEval(TfLiteContext* context, TfLiteNode* node) { + return EvalNumeric(context, node, std::abs); +} + TfLiteStatus SinEval(TfLiteContext* context, TfLiteNode* node) { return EvalNumeric(context, node, std::sin); } @@ -101,6 +106,14 @@ TfLiteStatus LogicalNotEval(TfLiteContext* context, TfLiteNode* node) { } // namespace } // namespace elementwise +TfLiteRegistration* Register_ABS() { + static TfLiteRegistration r = { + /*init=*/nullptr, /*free=*/nullptr, + elementwise::GenericPrepare, + elementwise::AbsEval}; + return &r; +} + TfLiteRegistration* Register_SIN() { static TfLiteRegistration r = { /*init=*/nullptr, /*free=*/nullptr, diff --git a/tensorflow/lite/kernels/elementwise_test.cc b/tensorflow/lite/kernels/elementwise_test.cc index 52df8dc3cc..7d24320081 100644 --- a/tensorflow/lite/kernels/elementwise_test.cc +++ b/tensorflow/lite/kernels/elementwise_test.cc @@ -74,6 +74,19 @@ TEST(ElementWise, Log) { EXPECT_THAT(m.GetTensorShape(m.output()), ElementsAreArray({1, 1, 4, 1})); } +TEST(FloatActivationsOpTest, Abs) { + ElementWiseOpFloatModel m(BuiltinOperator_ABS, {1, 2, 4, 1}); + m.PopulateTensor(m.input(), { + 0.f, -6.2f, 2.f, 4.f, // + 3.f, -2.f, 10.f, 1.f, // + }); + m.Invoke(); + EXPECT_THAT(m.ExtractVector(m.output()), ElementsAreArray({ + 0.f, 6.2f, 2.f, 4.f, // + 3.f, 2.f, 10.f, 1.f, // + })); +} + TEST(ElementWise, Sqrt) { ElementWiseOpFloatModel m(BuiltinOperator_SQRT, {1, 1, 4, 1}); m.PopulateTensor(m.input(), {0, 1, 2, 4}); diff --git a/tensorflow/lite/kernels/fill.cc b/tensorflow/lite/kernels/fill.cc new file mode 100644 index 0000000000..079ee44f37 --- /dev/null +++ b/tensorflow/lite/kernels/fill.cc @@ -0,0 +1,141 @@ +/* Copyright 2018 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/lite/c/builtin_op_data.h" +#include "tensorflow/lite/c/c_api_internal.h" +#include "tensorflow/lite/kernels/internal/reference/reference_ops.h" +#include "tensorflow/lite/kernels/internal/tensor.h" +#include "tensorflow/lite/kernels/internal/tensor_ctypes.h" +#include "tensorflow/lite/kernels/kernel_util.h" + +namespace tflite { +namespace ops { +namespace builtin { +namespace fill { + +namespace { + +constexpr int kDimsTensor = 0; +constexpr int kValueTensor = 1; +constexpr int kOutputTensor = 0; + +template +TfLiteStatus ResizeOutputImpl(TfLiteContext* context, const TfLiteTensor* dims, + TfLiteTensor* output) { + TfLiteIntArray* output_shape = TfLiteIntArrayCreate(dims->dims->data[0]); + for (int i = 0; i < output_shape->size; ++i) { + T data = GetTensorData(dims)[i]; + if (data < 0) { + context->ReportError(context, "Fill dimensions must be >= 0", dims->type); + return kTfLiteError; + } + output_shape->data[i] = data; + } + return context->ResizeTensor(context, output, output_shape); +} + +TfLiteStatus ResizeOutput(TfLiteContext* context, const TfLiteTensor* dims, + TfLiteTensor* output) { + switch (dims->type) { + case kTfLiteInt32: + return ResizeOutputImpl(context, dims, output); + case kTfLiteInt64: + return ResizeOutputImpl(context, dims, output); + default: + context->ReportError( + context, + "Fill only currently supports int32, int64 for input 0, " + "got %d.", + dims->type); + return kTfLiteError; + } +} + +} // namespace + +TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { + TF_LITE_ENSURE_EQ(context, NumInputs(node), 2); + TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1); + + const TfLiteTensor* dims = GetInput(context, node, kDimsTensor); + const TfLiteTensor* value = GetInput(context, node, kValueTensor); + + // Make sure the 1st input tensor is 1-D. + TF_LITE_ENSURE_EQ(context, NumDimensions(dims), 1); + + // Make sure the 1st input tensor is int32 or int64. + const auto dtype = dims->type; + TF_LITE_ENSURE(context, dtype == kTfLiteInt32 || dtype == kTfLiteInt64); + + // Make sure the 2nd input tensor is a scalar. + TF_LITE_ENSURE_EQ(context, NumDimensions(value), 0); + + TfLiteTensor* output = GetOutput(context, node, kOutputTensor); + output->type = value->type; + + if (IsConstantTensor(dims)) { + TF_LITE_ENSURE_OK(context, ResizeOutput(context, dims, output)); + } else { + SetTensorToDynamic(output); + } + return kTfLiteOk; +} + +TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { + const TfLiteTensor* value = GetInput(context, node, kValueTensor); + + TfLiteTensor* output = GetOutput(context, node, kOutputTensor); + + if (IsDynamicTensor(output)) { + const TfLiteTensor* dims = GetInput(context, node, kDimsTensor); + TF_LITE_ENSURE_OK(context, ResizeOutput(context, dims, output)); + } +#define TF_LITE_FILL(data_type) \ + reference_ops::Fill(GetTensorShape(value), GetTensorData(value), \ + GetTensorShape(output), \ + GetTensorData(output)) + switch (output->type) { + case kTfLiteInt32: + TF_LITE_FILL(int32_t); + break; + case kTfLiteInt64: + TF_LITE_FILL(int64_t); + break; + case kTfLiteFloat32: + TF_LITE_FILL(float); + break; + default: + context->ReportError( + context, + "Fill only currently supports int32, int64, float32 for input 1," + "got %d.", + value->type); + return kTfLiteError; + } +#undef TF_LITE_FILL + return kTfLiteOk; +} + +} // namespace fill + +TfLiteRegistration* Register_FILL() { + static TfLiteRegistration r = {/*init=*/nullptr, /*free=*/nullptr, + fill::Prepare, fill::Eval}; + return &r; +} + +} // namespace builtin +} // namespace ops +} // namespace tflite diff --git a/tensorflow/lite/kernels/fill_test.cc b/tensorflow/lite/kernels/fill_test.cc new file mode 100644 index 0000000000..08044d76f9 --- /dev/null +++ b/tensorflow/lite/kernels/fill_test.cc @@ -0,0 +1,94 @@ +/* Copyright 2018 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 +#include "tensorflow/lite/interpreter.h" +#include "tensorflow/lite/kernels/register.h" +#include "tensorflow/lite/kernels/test_util.h" +#include "tensorflow/lite/model.h" + +namespace tflite { +namespace { + +using ::testing::ElementsAreArray; +using ::testing::IsEmpty; + +class FillOpModel : public SingleOpModel { + public: + explicit FillOpModel(const TensorData& input1, const TensorData& input2) { + input1_ = AddInput(input1); + input2_ = AddInput(input2); + output_ = AddOutput(input1); + SetBuiltinOp(BuiltinOperator_FILL, BuiltinOptions_FillOptions, + CreateFillOptions(builder_).Union()); + BuildInterpreter({GetShape(input1_), GetShape(input2_)}); + } + + int input1() { return input1_; } + int input2() { return input2_; } + int output() { return output_; } + + protected: + int input1_; + int input2_; + int output_; +}; + +TEST(FillOpModel, FillInt32) { + FillOpModel m({TensorType_INT32, {2}}, {TensorType_INT32}); + m.PopulateTensor(m.input1(), {2, 3}); + m.PopulateTensor(m.input2(), {-11}); + m.Invoke(); + EXPECT_THAT(m.ExtractVector(m.output()), + ElementsAreArray({-11, -11, -11, -11, -11, -11})); + EXPECT_THAT(m.GetTensorShape(m.output()), ElementsAreArray({2, 3})); +} + +TEST(FillOpModel, FillInt64) { + FillOpModel m({TensorType_INT32, {2}}, {TensorType_INT64}); + m.PopulateTensor(m.input1(), {2, 4}); + m.PopulateTensor(m.input2(), {2 ^ 45}); + m.Invoke(); + EXPECT_THAT(m.ExtractVector(m.output()), + ElementsAreArray({2 ^ 45, 2 ^ 45, 2 ^ 45, 2 ^ 45, 2 ^ 45, 2 ^ 45, + 2 ^ 45, 2 ^ 45})); + EXPECT_THAT(m.GetTensorShape(m.output()), ElementsAreArray({2, 4})); +} + +TEST(FillOpModel, FillFloat) { + FillOpModel m({TensorType_INT64, {3}}, {TensorType_FLOAT32}); + m.PopulateTensor(m.input1(), {2, 2, 2}); + m.PopulateTensor(m.input2(), {4.0}); + m.Invoke(); + EXPECT_THAT(m.ExtractVector(m.output()), + ElementsAreArray({4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0})); + EXPECT_THAT(m.GetTensorShape(m.output()), ElementsAreArray({2, 2, 2})); +} + +TEST(FillOpModel, FillOutputScalar) { + FillOpModel m({TensorType_INT64, {0}}, {TensorType_FLOAT32}); + m.PopulateTensor(m.input2(), {4.0}); + m.Invoke(); + EXPECT_THAT(m.ExtractVector(m.output()), ElementsAreArray({4.0})); + EXPECT_THAT(m.GetTensorShape(m.output()), IsEmpty()); +} + +} // namespace +} // namespace tflite + +int main(int argc, char** argv) { + ::tflite::LogToStderr(); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tensorflow/lite/kernels/fully_connected.cc b/tensorflow/lite/kernels/fully_connected.cc index 63cca1cf54..a1eecb284a 100644 --- a/tensorflow/lite/kernels/fully_connected.cc +++ b/tensorflow/lite/kernels/fully_connected.cc @@ -117,7 +117,7 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { // Note that quantized inference requires that all tensors have their // parameters set. This is usually done during quantized training. TfLiteType data_type = input->type; - if (data_type != kTfLiteFloat32) { + if (data_type != kTfLiteFloat32 && data_type != kTfLiteInt32) { double real_multiplier = 0.0; TF_LITE_ENSURE_STATUS(GetQuantizedConvolutionMultipler( context, input, filter, bias, output, &real_multiplier)); diff --git a/tensorflow/lite/kernels/gather.cc b/tensorflow/lite/kernels/gather.cc index 61884d6a12..f205daae13 100644 --- a/tensorflow/lite/kernels/gather.cc +++ b/tensorflow/lite/kernels/gather.cc @@ -118,7 +118,7 @@ TfLiteStatus GatherStrings(TfLiteContext* context, const TfLiteTensor* input, const auto string_ref = GetString(input, pos); buffer.AddString(string_ref.str, string_ref.len); } - buffer.WriteToTensor(output); + buffer.WriteToTensorAsVector(output); return kTfLiteOk; } diff --git a/tensorflow/lite/kernels/hashtable_lookup.cc b/tensorflow/lite/kernels/hashtable_lookup.cc index b6ae7a3d1a..da1116cf85 100644 --- a/tensorflow/lite/kernels/hashtable_lookup.cc +++ b/tensorflow/lite/kernels/hashtable_lookup.cc @@ -137,7 +137,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { } } if (output->type == kTfLiteString) { - buf.WriteToTensor(output); + buf.WriteToTensorAsVector(output); } return kTfLiteOk; diff --git a/tensorflow/lite/kernels/internal/BUILD b/tensorflow/lite/kernels/internal/BUILD index 6d9690ea46..7d2653f0a1 100644 --- a/tensorflow/lite/kernels/internal/BUILD +++ b/tensorflow/lite/kernels/internal/BUILD @@ -234,8 +234,6 @@ cc_library( cc_library( name = "optimized", hdrs = [ - "optimized/cblas_conv.h", - "optimized/cblas_reference.h", "optimized/eigen_spatial_convolutions.h", "optimized/eigen_tensor_reduced_instantiations_oss.h", "optimized/multithreaded_conv.h", diff --git a/tensorflow/lite/kernels/internal/optimized/cblas_conv.h b/tensorflow/lite/kernels/internal/optimized/cblas_conv.h deleted file mode 100644 index 5377205050..0000000000 --- a/tensorflow/lite/kernels/internal/optimized/cblas_conv.h +++ /dev/null @@ -1,109 +0,0 @@ -/* 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_LITE_KERNELS_INTERNAL_OPTIMIZED_CBLAS_CONV_H_ -#define TENSORFLOW_LITE_KERNELS_INTERNAL_OPTIMIZED_CBLAS_CONV_H_ - -// The Conv implementation based on CBLAS interface. This is only used on iOS -// for now, utilizing Apple's Accelerate framework. - -#if TFLITE_USE_APPLE_ACCELERATE_FOR_CONV -#include -#else -#include "tensorflow/lite/kernels/internal/optimized/cblas_reference.h" -#endif - -#include "tensorflow/lite/kernels/internal/optimized/multithreaded_conv.h" -#include "tensorflow/lite/kernels/internal/optimized/optimized_ops.h" - -namespace tflite { -namespace cblas_ops { - -inline void Conv(const ConvParams& params, const RuntimeShape& input_shape, - const float* input_data, const RuntimeShape& filter_shape, - const float* filter_data, const RuntimeShape& bias_shape, - const float* bias_data, const RuntimeShape& output_shape, - float* output_data, const RuntimeShape& im2col_shape, - float* im2col_data) { - const int stride_width = params.stride_width; - const int stride_height = params.stride_height; - const int pad_width = params.padding_values.width; - const int pad_height = params.padding_values.height; - const int dilation_width_factor = params.dilation_width_factor; - const int dilation_height_factor = params.dilation_height_factor; - const float output_activation_min = params.float_activation_min; - const float output_activation_max = params.float_activation_max; - TFLITE_DCHECK_EQ(input_shape.DimensionsCount(), 4); - TFLITE_DCHECK_EQ(filter_shape.DimensionsCount(), 4); - TFLITE_DCHECK_EQ(output_shape.DimensionsCount(), 4); - gemmlowp::ScopedProfilingLabel label("Conv/cblas"); - - const float* gemm_input_data = nullptr; - const RuntimeShape* gemm_input_shape = nullptr; - const int filter_width = filter_shape.Dims(2); - const int filter_height = filter_shape.Dims(1); - const bool need_im2col = stride_width != 1 || stride_height != 1 || - filter_width != 1 || filter_height != 1; - if (need_im2col) { - TFLITE_DCHECK(im2col_data); - ConvParams op_params; - op_params.padding_type = PaddingType::kSame; - op_params.padding_values.width = pad_width; - op_params.padding_values.height = pad_height; - op_params.stride_width = stride_width; - op_params.stride_height = stride_height; - op_params.dilation_width_factor = dilation_width_factor; - op_params.dilation_height_factor = dilation_height_factor; - optimized_ops::Im2col(op_params, filter_height, filter_width, 0, - input_shape, input_data, im2col_shape, im2col_data); - - gemm_input_data = im2col_data; - gemm_input_shape = &im2col_shape; - } else { - TFLITE_DCHECK(!im2col_data); - gemm_input_data = input_data; - gemm_input_shape = &input_shape; - } - - // The following code computes matrix multiplication c = a * transponse(b) - // with CBLAS, where: - // * `a` is a matrix with dimensions (m, k). - // * `b` is a matrix with dimensions (n, k), so transpose(b) is (k, n). - // * `c` is a matrix with dimensions (m, n). - // The naming of variables are aligned with CBLAS specification here. - const float* a = gemm_input_data; - const float* b = filter_data; - float* c = output_data; - const int gemm_input_dims = gemm_input_shape->DimensionsCount(); - int m = FlatSizeSkipDim(*gemm_input_shape, gemm_input_dims - 1); - int n = output_shape.Dims(3); - int k = gemm_input_shape->Dims(gemm_input_dims - 1); - // The stride of matrix a, b and c respectively. - int stride_a = k; - int stride_b = k; - int stride_c = n; - - cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans, m, n, k, 1.0f, a, - stride_a, b, stride_b, 0.0f, c, stride_c); - - optimized_ops::AddBiasAndEvalActivationFunction( - output_activation_min, output_activation_max, bias_shape, bias_data, - output_shape, output_data); -} - -} // namespace cblas_ops -} // namespace tflite - -#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_OPTIMIZED_CBLAS_CONV_H_ diff --git a/tensorflow/lite/kernels/internal/optimized/cblas_reference.h b/tensorflow/lite/kernels/internal/optimized/cblas_reference.h deleted file mode 100644 index fa07578612..0000000000 --- a/tensorflow/lite/kernels/internal/optimized/cblas_reference.h +++ /dev/null @@ -1,69 +0,0 @@ -/* 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_LITE_KERNELS_INTERNAL_OPTIMIZED_CBLAS_REFERENCE_H_ -#define TENSORFLOW_LITE_KERNELS_INTERNAL_OPTIMIZED_CBLAS_REFERENCE_H_ - -#include "tensorflow/lite/kernels/internal/compatibility.h" - -// The reference implementation for a small subset of CBLAS interface. -// This is only used for testing CBLAS implementation, and should never be used -// in production code. - -namespace tflite { -namespace cblas_ops { - -// The following code follows the original CBLAS specification, and it might -// conflict with the TensorFlow naming convention. -// TODO(ycling): Find another way to test CBLAS with bazel, without writing -// a reference implementation by ourselves. -enum CBLAS_ORDER { CblasRowMajor = 0, CblasColMajor = 1 }; - -enum CBLAS_TRANSPOSE { CblasNoTrans = 0, CblasTrans = 1, CblasConjTrans = 2 }; - -// A reference implementation for matrix multiplication. -// The following code computes, c = a * transponse(b) matrix multiplication -// with CBLAS, where: -// * `a` is a matrix with dimensions (m, k). -// * `b` is a matrix with dimensions (n, k), so transpose(b) is (k, n). -// * `c` is a matrix with dimensions (m, n). -// The naming of variables is aligned with CBLAS specification here. -void cblas_sgemm(const enum CBLAS_ORDER order, - const enum CBLAS_TRANSPOSE trans_a, - const enum CBLAS_TRANSPOSE trans_b, const int m, const int n, - const int k, const float alpha, const float *a, - const int stride_a, const float *b, const int stride_b, - const float beta, float *c, const int stride_c) { - TFLITE_DCHECK(order == CblasRowMajor); - TFLITE_DCHECK(trans_a == CblasNoTrans); - TFLITE_DCHECK(trans_b == CblasTrans); - TFLITE_DCHECK(beta == 0.0f); - for (int row = 0; row < m; ++row) { - for (int col = 0; col < n; ++col) { - // If `beta` non-zero, multiple it with the original values in output. - // Otherwise, ignore the original value in output completely. - float value = 0.0f; - for (int idx = 0; idx < k; ++idx) { - value += alpha * a[stride_a * row + idx] * b[stride_b * col + idx]; - } - c[stride_c * row + col] = value; - } - } -} - -} // namespace cblas_ops -} // namespace tflite - -#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_OPTIMIZED_CBLAS_REFERENCE_H_ diff --git a/tensorflow/lite/kernels/internal/optimized/depthwiseconv_float.h b/tensorflow/lite/kernels/internal/optimized/depthwiseconv_float.h index 25b66d4b55..c77715de57 100644 --- a/tensorflow/lite/kernels/internal/optimized/depthwiseconv_float.h +++ b/tensorflow/lite/kernels/internal/optimized/depthwiseconv_float.h @@ -793,22 +793,26 @@ void FloatDepthwiseConvAccumRow(int stride, int dilation_factor, int out_x_loop_end_unclampled = 0; if (kAllowStrided) { if (stride == 2) { - out_x_loop_start_unclampled = (pad_width - filter_x + 1) / 2; + out_x_loop_start_unclampled = + (pad_width - dilation_factor * filter_x + 1) / 2; out_x_loop_end_unclampled = - (pad_width + input_width - filter_x + 1) / 2; + (pad_width + input_width - dilation_factor * filter_x + 1) / 2; } else if (stride == 4) { - out_x_loop_start_unclampled = (pad_width - filter_x + 3) / 4; + out_x_loop_start_unclampled = + (pad_width - dilation_factor * filter_x + 3) / 4; out_x_loop_end_unclampled = - (pad_width + input_width - filter_x + 3) / 4; + (pad_width + input_width - dilation_factor * filter_x + 3) / 4; } else { out_x_loop_start_unclampled = - (pad_width - filter_x + stride - 1) / stride; - out_x_loop_end_unclampled = - (pad_width + input_width - filter_x + stride - 1) / stride; + (pad_width - dilation_factor * filter_x + stride - 1) / stride; + out_x_loop_end_unclampled = (pad_width + input_width - + dilation_factor * filter_x + stride - 1) / + stride; } } else { - out_x_loop_start_unclampled = pad_width - filter_x; - out_x_loop_end_unclampled = pad_width + input_width - filter_x; + out_x_loop_start_unclampled = pad_width - dilation_factor * filter_x; + out_x_loop_end_unclampled = + pad_width + input_width - dilation_factor * filter_x; } // The kernel will have to iterate on the segment of the // output row that starts at out_x_loop_start and out_x_loop_end. @@ -819,7 +823,8 @@ void FloatDepthwiseConvAccumRow(int stride, int dilation_factor, float* acc_buffer_ptr = acc_buffer + (out_x_loop_start - out_x_buffer_start) * output_depth; - const int in_x_origin = (out_x_loop_start * stride) - pad_width + filter_x; + const int in_x_origin = + (out_x_loop_start * stride) - pad_width + dilation_factor * filter_x; const float* input_ptr = input_data + in_x_origin * input_depth; const int num_output_pixels = out_x_loop_end - out_x_loop_start; FloatDepthwiseConvKernel; \ diff --git a/tensorflow/lite/kernels/internal/optimized/depthwiseconv_uint8.h b/tensorflow/lite/kernels/internal/optimized/depthwiseconv_uint8.h index 5317cea884..d3dca799a7 100644 --- a/tensorflow/lite/kernels/internal/optimized/depthwiseconv_uint8.h +++ b/tensorflow/lite/kernels/internal/optimized/depthwiseconv_uint8.h @@ -1499,22 +1499,26 @@ void QuantizedDepthwiseConvAccumRow(int stride, int dilation_factor, int out_x_loop_end_unclampled = 0; if (kAllowStrided) { if (stride == 2) { - out_x_loop_start_unclampled = (pad_width - filter_x + 1) / 2; + out_x_loop_start_unclampled = + (pad_width - dilation_factor * filter_x + 1) / 2; out_x_loop_end_unclampled = - (pad_width + input_width - filter_x + 1) / 2; + (pad_width + input_width - dilation_factor * filter_x + 1) / 2; } else if (stride == 4) { - out_x_loop_start_unclampled = (pad_width - filter_x + 3) / 4; + out_x_loop_start_unclampled = + (pad_width - dilation_factor * filter_x + 3) / 4; out_x_loop_end_unclampled = - (pad_width + input_width - filter_x + 3) / 4; + (pad_width + input_width - dilation_factor * filter_x + 3) / 4; } else { out_x_loop_start_unclampled = - (pad_width - filter_x + stride - 1) / stride; - out_x_loop_end_unclampled = - (pad_width + input_width - filter_x + stride - 1) / stride; + (pad_width - dilation_factor * filter_x + stride - 1) / stride; + out_x_loop_end_unclampled = (pad_width + input_width - + dilation_factor * filter_x + stride - 1) / + stride; } } else { - out_x_loop_start_unclampled = pad_width - filter_x; - out_x_loop_end_unclampled = pad_width + input_width - filter_x; + out_x_loop_start_unclampled = pad_width - dilation_factor * filter_x; + out_x_loop_end_unclampled = + pad_width + input_width - dilation_factor * filter_x; } // The kernel will have to iterate on the segment of the // output row that starts at out_x_loop_start and out_x_loop_end. @@ -1525,7 +1529,8 @@ void QuantizedDepthwiseConvAccumRow(int stride, int dilation_factor, int32* acc_buffer_ptr = acc_buffer + (out_x_loop_start - out_x_buffer_start) * output_depth; - const int in_x_origin = (out_x_loop_start * stride) - pad_width + filter_x; + const int in_x_origin = + (out_x_loop_start * stride) - pad_width + dilation_factor * filter_x; const uint8* input_ptr = input_data + in_x_origin * input_depth; const int num_output_pixels = out_x_loop_end - out_x_loop_start; QuantizedDepthwiseConvKernel< @@ -1703,8 +1708,7 @@ inline void DepthwiseConvGeneral( FIXED_DEPTH_MULTIPLIER) \ if (!row_accum_func && (stride_width == 1 || ALLOW_STRIDED) && \ (input_depth == FIXED_INPUT_DEPTH || FIXED_INPUT_DEPTH == 0) && \ - depth_multiplier == FIXED_DEPTH_MULTIPLIER && \ - dilation_width_factor == 1 && dilation_height_factor == 1) { \ + depth_multiplier == FIXED_DEPTH_MULTIPLIER) { \ row_accum_func = \ QuantizedDepthwiseConvAccumRow; \ diff --git a/tensorflow/lite/kernels/internal/optimized/depthwiseconv_uint8_3x3_filter.h b/tensorflow/lite/kernels/internal/optimized/depthwiseconv_uint8_3x3_filter.h index 3f2ed0b1f0..5859bcaed4 100644 --- a/tensorflow/lite/kernels/internal/optimized/depthwiseconv_uint8_3x3_filter.h +++ b/tensorflow/lite/kernels/internal/optimized/depthwiseconv_uint8_3x3_filter.h @@ -23,11 +23,6 @@ limitations under the License. namespace tflite { namespace optimized_ops { -// clang-format gets confused with this file and ends up formatting lines to -// be larger than 80 characters. Turn off here and back on at the end of the -// file. -// clang-format off - // See CategorizeDotProductKernel for definitive taxonomy. enum class DotProduct3x3KernelType { kNone = 0, // Parameter combination is not supported for dot product kernels. @@ -120,42 +115,58 @@ struct DepthwiseConvParams { #define OFFSET_OUTPUT_WIDTH 84 #define OFFSET_OUTPUT_HEIGHT 88 -static_assert(offsetof(DepthwiseConvParams, input_depth) == - OFFSET_INPUT_DEPTH, ""); +static_assert(offsetof(DepthwiseConvParams, input_depth) == OFFSET_INPUT_DEPTH, + ""); static_assert(offsetof(DepthwiseConvParams, input_row_size) == - OFFSET_INPUT_ROW_SIZE, ""); + OFFSET_INPUT_ROW_SIZE, + ""); static_assert(offsetof(DepthwiseConvParams, output_depth) == - OFFSET_OUTPUT_DEPTH, ""); + OFFSET_OUTPUT_DEPTH, + ""); static_assert(offsetof(DepthwiseConvParams, output_row_size) == - OFFSET_OUTPUT_ROW_SIZE, ""); + OFFSET_OUTPUT_ROW_SIZE, + ""); static_assert(offsetof(DepthwiseConvParams, filter_row_size) == - OFFSET_FILTER_ROW_SIZE, ""); + OFFSET_FILTER_ROW_SIZE, + ""); static_assert(offsetof(DepthwiseConvParams, input_offset) == - OFFSET_INPUT_OFFSET, ""); + OFFSET_INPUT_OFFSET, + ""); static_assert(offsetof(DepthwiseConvParams, output_offset) == - OFFSET_OUTPUT_OFFSET, ""); + OFFSET_OUTPUT_OFFSET, + ""); static_assert(offsetof(DepthwiseConvParams, filter_offset) == - OFFSET_FILTER_OFFSET, ""); + OFFSET_FILTER_OFFSET, + ""); static_assert(offsetof(DepthwiseConvParams, output_multiplier) == - OFFSET_OUTPUT_MULTIPLIER, ""); + OFFSET_OUTPUT_MULTIPLIER, + ""); static_assert(offsetof(DepthwiseConvParams, output_activation_min) == - OFFSET_OUTPUT_ACTIVATION_MIN, ""); + OFFSET_OUTPUT_ACTIVATION_MIN, + ""); static_assert(offsetof(DepthwiseConvParams, output_activation_max) == - OFFSET_OUTPUT_ACTIVATION_MAX, ""); + OFFSET_OUTPUT_ACTIVATION_MAX, + ""); static_assert(offsetof(DepthwiseConvParams, output_right_shift) == - OFFSET_OUTPUT_RIGHT_SHIFT, ""); -static_assert(offsetof(DepthwiseConvParams, input_width) == - OFFSET_INPUT_WIDTH, ""); + OFFSET_OUTPUT_RIGHT_SHIFT, + ""); +static_assert(offsetof(DepthwiseConvParams, input_width) == OFFSET_INPUT_WIDTH, + ""); static_assert(offsetof(DepthwiseConvParams, input_height) == - OFFSET_INPUT_HEIGHT, ""); + OFFSET_INPUT_HEIGHT, + ""); static_assert(offsetof(DepthwiseConvParams, stride_width) == - OFFSET_STRIDE_WIDTH, ""); + OFFSET_STRIDE_WIDTH, + ""); static_assert(offsetof(DepthwiseConvParams, stride_height) == - OFFSET_STRIDE_HEIGHT, ""); + OFFSET_STRIDE_HEIGHT, + ""); static_assert(offsetof(DepthwiseConvParams, output_width) == - OFFSET_OUTPUT_WIDTH, ""); + OFFSET_OUTPUT_WIDTH, + ""); static_assert(offsetof(DepthwiseConvParams, output_height) == - OFFSET_OUTPUT_HEIGHT, ""); + OFFSET_OUTPUT_HEIGHT, + ""); template struct DepthwiseConvWindow {}; @@ -164,10 +175,10 @@ template <> struct DepthwiseConvWindow<8, 1, 1> { public: static inline void Run(const uint8* input_ptr, const uint8* filter_ptr, - const int32* bias_ptr, uint8* output_ptr, int64_t input_depth, - int64_t input_row_size, int32 output_window_height, - int32 output_window_width, - const DepthwiseConvParams* params_ptr) { + const int32* bias_ptr, uint8* output_ptr, + int64_t input_depth, int64_t input_row_size, + int32 output_window_height, int32 output_window_width, + const DepthwiseConvParams* params_ptr) { const int64_t input_width_increment = 2 * input_depth; const int64_t input_height_increment = 2 * input_row_size; const int64_t output_height_increment = 2 * params_ptr->output_row_size; @@ -1147,10 +1158,10 @@ struct DepthwiseConvWindow<8, 1, 1> { template <> struct DepthwiseConvWindow<8, 2, 2> { static inline void Run(const uint8* input_ptr, const uint8* filter_ptr, - const int32* bias_ptr, uint8* output_ptr, int64_t input_depth, - int64_t input_row_size, int32 output_window_height, - int32 output_window_width, - const DepthwiseConvParams* params_ptr) { + const int32* bias_ptr, uint8* output_ptr, + int64_t input_depth, int64_t input_row_size, + int32 output_window_height, int32 output_window_width, + const DepthwiseConvParams* params_ptr) { const int64_t input_width_increment = 4 * input_depth; const int64_t input_height_increment = 4 * input_row_size; const int64_t output_height_increment = 2 * params_ptr->output_row_size; @@ -2990,11 +3001,10 @@ struct ShuffleParams { ShuffleParams() = default; ShuffleParams(int32 output_width, int32 output_height, int32 stride_width, int32 stride_height) - : output_width(output_width) - , output_height(output_height) - , input_width(get_shuffle_input_size(stride_width, output_width)) - , input_height(get_shuffle_input_size(stride_height, output_height)) { - } + : output_width(output_width), + output_height(output_height), + input_width(get_shuffle_input_size(stride_width, output_width)), + input_height(get_shuffle_input_size(stride_height, output_height)) {} }; template @@ -3003,10 +3013,10 @@ struct DepthwiseConvThroughDepth { // |start_depth| to |end_depth|. Keep this not inlined to maintain a small // binary size. We use a DepthwiseConvParams struct for read only params // to minimize call overhead. - static __attribute__((noinline)) void Run(const uint8* input_ptr, - const uint8* filter_ptr, const int32* bias_ptr, uint8* output_ptr, - int64_t start_depth, int64_t end_depth, int64_t input_depth, - int64_t input_row_size, int32 output_window_height, + static __attribute__((noinline)) void Run( + const uint8* input_ptr, const uint8* filter_ptr, const int32* bias_ptr, + uint8* output_ptr, int64_t start_depth, int64_t end_depth, + int64_t input_depth, int64_t input_row_size, int32 output_window_height, int32 output_window_width, const DepthwiseConvParams& params) { for (; start_depth <= end_depth - 8; start_depth += 8) { DepthwiseConvWindow<8, kStrideWidth, kStrideHeight>::Run( @@ -3029,12 +3039,15 @@ struct DepthwiseConvMultiRow { uint8* output_data, const DepthwiseConvParams& params, const ShuffleParams& shuffle_params, uint8* shuffle_workspace) { - TFLITE_DCHECK(shuffle_params.input_height == + TFLITE_DCHECK( + shuffle_params.input_height == get_shuffle_input_size(kStrideHeight, shuffle_params.output_height)); - TFLITE_DCHECK(shuffle_params.input_width == + TFLITE_DCHECK( + shuffle_params.input_width == get_shuffle_input_size(kStrideWidth, shuffle_params.output_width)); - TFLITE_DCHECK(64 * shuffle_params.input_width * shuffle_params.input_height - <= DEPTHWISECONV_SHUFFLE_WORKSPACE_SIZE); + TFLITE_DCHECK(64 * shuffle_params.input_width * + shuffle_params.input_height <= + DEPTHWISECONV_SHUFFLE_WORKSPACE_SIZE); int32 out_x = start_x; @@ -3045,7 +3058,7 @@ struct DepthwiseConvMultiRow { if (params.output_depth > 64 || (params.output_depth <= 64 && params.input_width > 150)) { for (; out_x <= (end_x - shuffle_params.output_width); - out_x += shuffle_params.output_width) { + out_x += shuffle_params.output_width) { const uint8* input_ptr = input_data; const int32* bias_ptr = bias_data; const uint8* filter_ptr = filter_data; @@ -3091,8 +3104,8 @@ struct DepthwiseConvMultiRow { } // Handle leftover depth. - ConvKernel::Run(input_ptr, filter_ptr, bias_ptr, output_ptr, - depth, params.output_depth, params.input_depth, + ConvKernel::Run(input_ptr, filter_ptr, bias_ptr, output_ptr, depth, + params.output_depth, params.input_depth, params.input_row_size, shuffle_params.output_height, shuffle_params.output_width, params); @@ -3119,13 +3132,15 @@ struct DepthwiseConvMultiRow { // * Horizontal edges. // * Vertical edges. inline void DepthwiseConvHandlePadding(const uint8* input_data, - const uint8* filter_data, const int32* bias_data, uint8* output_data, - const DepthwiseConvParams& params) { + const uint8* filter_data, + const int32* bias_data, + uint8* output_data, + const DepthwiseConvParams& params) { if (params.input_width == 1 && params.input_height == 1) { - const uint8* filter_ptr = filter_data + params.filter_row_size - + params.output_depth; - DepthwiseConvPartial::Run(input_data, filter_ptr, - bias_data, output_data, ¶ms); + const uint8* filter_ptr = + filter_data + params.filter_row_size + params.output_depth; + DepthwiseConvPartial::Run( + input_data, filter_ptr, bias_data, output_data, ¶ms); return; } @@ -3136,27 +3151,27 @@ inline void DepthwiseConvHandlePadding(const uint8* input_data, // Handle top row. const uint8* input_ptr = input_data; - const uint8* filter_ptr = filter_data + params.filter_row_size - + params.output_depth; + const uint8* filter_ptr = + filter_data + params.filter_row_size + params.output_depth; uint8* output_ptr = output_data; - DepthwiseConvPartial::Run(input_ptr, filter_ptr, - bias_data, output_ptr, ¶ms); + DepthwiseConvPartial::Run( + input_ptr, filter_ptr, bias_data, output_ptr, ¶ms); input_ptr += (params.stride_width - 1) * params.input_depth; filter_ptr = filter_data + params.filter_row_size; output_ptr += params.output_depth; for (int32 out_x = out_x_start_corner + 1; out_x < out_x_end_corner; - out_x++) { + out_x++) { DepthwiseConvPartial::Run( input_ptr, filter_ptr, bias_data, output_ptr, ¶ms); input_ptr += params.stride_width * params.input_depth; output_ptr += params.output_depth; } - DepthwiseConvPartial::Run(input_ptr, filter_ptr, - bias_data, output_ptr, ¶ms); + DepthwiseConvPartial::Run( + input_ptr, filter_ptr, bias_data, output_ptr, ¶ms); // Handle left side. input_ptr = input_data + (params.stride_width - 1) * params.input_row_size; @@ -3164,7 +3179,7 @@ inline void DepthwiseConvHandlePadding(const uint8* input_data, output_ptr = output_data + params.output_row_size; for (int32 out_y = out_y_start_corner + 1; out_y < out_y_end_corner; - out_y++) { + out_y++) { DepthwiseConvPartial::Run( input_ptr, filter_ptr, bias_data, output_ptr, ¶ms); input_ptr += params.stride_width * params.input_row_size; @@ -3172,14 +3187,14 @@ inline void DepthwiseConvHandlePadding(const uint8* input_data, } // Handle right side. - input_ptr = input_data + (params.input_width - 2) * params.input_depth - + (params.stride_width - 1) * params.input_row_size; + input_ptr = input_data + (params.input_width - 2) * params.input_depth + + (params.stride_width - 1) * params.input_row_size; filter_ptr = filter_data; output_ptr = output_data + params.output_row_size + - (params.output_width - 1) * params.output_depth; + (params.output_width - 1) * params.output_depth; for (int32 out_y = out_y_start_corner + 1; out_y < out_y_end_corner; - out_y++) { + out_y++) { DepthwiseConvPartial::Run( input_ptr, filter_ptr, bias_data, output_ptr, ¶ms); input_ptr += params.stride_width * params.input_row_size; @@ -3189,26 +3204,26 @@ inline void DepthwiseConvHandlePadding(const uint8* input_data, // Handle bottom row. input_ptr = input_data + (params.input_height - 2) * params.input_row_size; filter_ptr = filter_data + params.output_depth; - output_ptr = output_data + - (params.output_height - 1) * params.output_row_size; + output_ptr = + output_data + (params.output_height - 1) * params.output_row_size; - DepthwiseConvPartial::Run(input_ptr, filter_ptr, - bias_data, output_ptr, ¶ms); + DepthwiseConvPartial::Run( + input_ptr, filter_ptr, bias_data, output_ptr, ¶ms); input_ptr += (params.stride_width == 1) ? 0 : params.input_depth; filter_ptr = filter_data; output_ptr += params.output_depth; for (int32 out_x = out_x_start_corner + 1; out_x < out_x_end_corner; - out_x++) { + out_x++) { DepthwiseConvPartial::Run( input_ptr, filter_ptr, bias_data, output_ptr, ¶ms); input_ptr += params.stride_width * params.input_depth; output_ptr += params.output_depth; } - DepthwiseConvPartial::Run(input_ptr, filter_ptr, - bias_data, output_ptr, ¶ms); + DepthwiseConvPartial::Run( + input_ptr, filter_ptr, bias_data, output_ptr, ¶ms); } inline bool Fast3x3FilterKernelSupported( @@ -3383,8 +3398,8 @@ inline void DepthwiseConv3x3Filter( const int in_x = (out_x * stride_width) - pad_width; const int in_y = (out_y * stride_height) - pad_height; input_ptr += in_y * params.input_row_size + in_x * params.input_depth; - output_ptr += out_y * params.output_row_size - + out_x * params.output_depth; + output_ptr += + out_y * params.output_row_size + out_x * params.output_depth; } // Shuffling shapes that maximize width over the shuffle workspace size @@ -3439,7 +3454,6 @@ inline void DepthwiseConv3x3Filter( } } } -// clang-format on #endif // __aarch64__ diff --git a/tensorflow/lite/kernels/internal/optimized/optimized_ops.h b/tensorflow/lite/kernels/internal/optimized/optimized_ops.h index 6f2cd4faab..c7691e2763 100644 --- a/tensorflow/lite/kernels/internal/optimized/optimized_ops.h +++ b/tensorflow/lite/kernels/internal/optimized/optimized_ops.h @@ -25,6 +25,10 @@ limitations under the License. #include #include +#if defined(TF_LITE_USE_CBLAS) && defined(__APPLE__) +#include +#endif + #include "third_party/eigen3/Eigen/Core" #include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor" #include "fixedpoint/fixedpoint.h" @@ -60,11 +64,13 @@ using reference_ops::DepthConcatenation; using reference_ops::Dequantize; using reference_ops::Div; using reference_ops::FakeQuant; +using reference_ops::Fill; using reference_ops::Gather; using reference_ops::Greater; using reference_ops::GreaterEqual; using reference_ops::GreaterEqualWithScaling; using reference_ops::GreaterWithScaling; +using reference_ops::LeakyRelu; using reference_ops::Less; using reference_ops::LessEqual; using reference_ops::LessEqualWithScaling; @@ -1867,18 +1873,45 @@ inline void Conv(const ConvParams& params, const RuntimeShape& input_shape, gemm_input_shape = &input_shape; } - const auto im2col_matrix_map = - MapAsMatrixWithLastDimAsRows(gemm_input_data, *gemm_input_shape); - const auto filter_matrix_map = - MapAsMatrixWithFirstDimAsCols(filter_data, filter_shape); - auto output_matrix_map = - MapAsMatrixWithLastDimAsRows(output_data, output_shape); - - Gemm(filter_matrix_map.transpose(), im2col_matrix_map, &output_matrix_map); - - AddBiasAndEvalActivationFunction(output_activation_min, output_activation_max, - bias_shape, bias_data, output_shape, - output_data); + // The following code computes matrix multiplication c = a * transponse(b) + // with CBLAS, where: + // * `a` is a matrix with dimensions (m, k). + // * `b` is a matrix with dimensions (n, k), so transpose(b) is (k, n). + // * `c` is a matrix with dimensions (m, n). + // The naming of variables are aligned with CBLAS specification here. + const float* a = gemm_input_data; + const float* b = filter_data; + float* c = output_data; + const int gemm_input_dims = gemm_input_shape->DimensionsCount(); + int m = FlatSizeSkipDim(*gemm_input_shape, gemm_input_dims - 1); + int n = output_shape.Dims(3); + int k = gemm_input_shape->Dims(gemm_input_dims - 1); + +#if defined(TF_LITE_USE_CBLAS) && defined(__APPLE__) + // The stride of matrix a, b and c respectively. + int stride_a = k; + int stride_b = k; + int stride_c = n; + + cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans, m, n, k, 1.0f, a, + stride_a, b, stride_b, 0.0f, c, stride_c); +#else + // When an optimized CBLAS implementation is not available, fall back + // to using Eigen. + typedef Eigen::Matrix + Matrix; + typedef Eigen::Map MatrixRef; + typedef Eigen::Map ConstMatrixRef; + + MatrixRef matrix_c(c, m, n); + ConstMatrixRef matrix_a(a, m, k); + ConstMatrixRef matrix_b(b, n, k); + matrix_c.noalias() = matrix_a * matrix_b.transpose(); +#endif // defined(TF_LITE_USE_CBLAS) && defined(__APPLE__) + + optimized_ops::AddBiasAndEvalActivationFunction( + output_activation_min, output_activation_max, bias_shape, bias_data, + output_shape, output_data); } inline void HybridConv(const ConvParams& params, float* scaling_factors_ptr, @@ -4292,7 +4325,6 @@ inline void LogSoftmax(const SoftmaxParams& params, using FixedPointScaledDiff = gemmlowp::FixedPoint; using FixedPointAccum = gemmlowp::FixedPoint; - using FixedPoint0 = gemmlowp::FixedPoint; const int trailing_dim = input_shape.DimensionsCount() - 1; const int outer_size = diff --git a/tensorflow/lite/kernels/internal/reference/reference_ops.h b/tensorflow/lite/kernels/internal/reference/reference_ops.h index 1bd9129488..ea3ab06da1 100644 --- a/tensorflow/lite/kernels/internal/reference/reference_ops.h +++ b/tensorflow/lite/kernels/internal/reference/reference_ops.h @@ -558,6 +558,19 @@ inline void ReluX(const tflite::ActivationParams& params, } } +inline void LeakyRelu(const tflite::LeakyReluParams& params, + const RuntimeShape& input_shape, const float* input_data, + const RuntimeShape& output_shape, float* output_data) { + gemmlowp::ScopedProfilingLabel label("LeakyRelu (not fused)"); + const int flat_size = MatchingFlatSize(input_shape, output_shape); + for (int i = 0; i < flat_size; ++i) { + const float val = input_data[i]; + // Note that this implementation matches that of TensorFlow, and corresponds + // to the traditional LeakyRelu equation only for alpha <= 1. + output_data[i] = std::max(val, val * params.alpha); + } +} + inline void L2Normalization(const tflite::L2NormalizationParams& op_params, const RuntimeShape& input_shape, const float* input_data, @@ -2723,7 +2736,6 @@ inline void LogSoftmax(const SoftmaxParams& params, using FixedPointScaledDiff = gemmlowp::FixedPoint; using FixedPointAccum = gemmlowp::FixedPoint; - using FixedPoint0 = gemmlowp::FixedPoint; const int trailing_dim = input_shape.DimensionsCount() - 1; const int outer_size = @@ -3651,8 +3663,10 @@ inline void Mean(const tflite::MeanParams& op_params, const RuntimeShape& unextended_output_shape, T* output_data) { gemmlowp::ScopedProfilingLabel label("Mean"); - TFLITE_DCHECK_LE(unextended_input_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_output_shape.DimensionsCount(), 4); + // Current implementation only supports dimension equals 4 and simultaneous + // reduction over width and height. + TFLITE_CHECK_EQ(unextended_input_shape.DimensionsCount(), 4); + TFLITE_CHECK_LE(unextended_output_shape.DimensionsCount(), 4); const RuntimeShape input_shape = RuntimeShape::ExtendedShape(4, unextended_input_shape); const RuntimeShape output_shape = @@ -3666,8 +3680,6 @@ inline void Mean(const tflite::MeanParams& op_params, const int input_height = input_shape.Dims(1); const int input_width = input_shape.Dims(2); - // The current implementation only supports simultaneous reduction over - // width and height. TFLITE_DCHECK_EQ(op_params.axis_count, 2); TFLITE_DCHECK((op_params.axis[0] == 1 && op_params.axis[1] == 2) || (op_params.axis[0] == 2 && op_params.axis[1] == 1)); @@ -4554,6 +4566,63 @@ inline void ResizeNearestNeighbor( } } +inline void BroadcastPrelu4DSlow(const PreluParams& params, + const RuntimeShape& input_shape, + const uint8* input_data, + const RuntimeShape& alpha_shape, + const uint8* alpha_data, + const RuntimeShape& output_shape, + uint8* output_data) { + TFLITE_DCHECK_LE(input_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(alpha_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(output_shape.DimensionsCount(), 4); + const RuntimeShape extended_output_shape = + RuntimeShape::ExtendedShape(4, output_shape); + NdArrayDesc<4> desc1; + NdArrayDesc<4> desc2; + NdArrayDescsForElementwiseBroadcast(input_shape, alpha_shape, &desc1, &desc2); + + for (int b = 0; b < extended_output_shape.Dims(0); ++b) { + for (int y = 0; y < extended_output_shape.Dims(1); ++y) { + for (int x = 0; x < extended_output_shape.Dims(2); ++x) { + for (int c = 0; c < extended_output_shape.Dims(3); ++c) { + int output_index = Offset(extended_output_shape, b, y, x, c); + int input_index = SubscriptToIndex(desc1, b, y, x, c); + const int32 input_value = + params.input_offset + input_data[input_index]; + if (input_value >= 0) { + output_data[output_index] = input_data[input_index]; + } else { + auto alpha_index = SubscriptToIndex(desc2, b, y, x, c); + const int32 alpha_value = + params.alpha_offset + alpha_data[alpha_index]; + const int32 unclamped_output = + params.output_offset + + MultiplyByQuantizedMultiplierSmallerThanOneExp( + input_value * alpha_value, params.output_multiplier, + params.output_shift); + const int32 quantized_min = std::numeric_limits::min(); + const int32 quantized_max = std::numeric_limits::max(); + const int32 clamped_output = std::min( + quantized_max, std::max(quantized_min, unclamped_output)); + output_data[output_index] = static_cast(clamped_output); + } + } + } + } + } +} + +template +void Fill(const RuntimeShape& value_shape, const T* value_data, + const RuntimeShape& output_shape, T* output_data) { + TFLITE_DCHECK_EQ(value_shape.DimensionsCount(), 0); + const int flat_size = output_shape.FlatSize(); + for (int i = 0; i < flat_size; ++i) { + output_data[i] = *value_data; + } +} + } // namespace reference_ops } // namespace tflite diff --git a/tensorflow/lite/kernels/internal/tensor_ctypes.h b/tensorflow/lite/kernels/internal/tensor_ctypes.h index d24dca9bfb..4a94b703f8 100644 --- a/tensorflow/lite/kernels/internal/tensor_ctypes.h +++ b/tensorflow/lite/kernels/internal/tensor_ctypes.h @@ -53,6 +53,11 @@ inline bool* GetTensorData(TfLiteTensor* tensor) { return tensor != nullptr ? tensor->data.b : nullptr; } +template <> +inline int8_t* GetTensorData(TfLiteTensor* tensor) { + return tensor != nullptr ? tensor->data.int8 : nullptr; +} + template inline const T* GetTensorData(const TfLiteTensor* tensor); @@ -66,6 +71,11 @@ inline const uint8_t* GetTensorData(const TfLiteTensor* tensor) { return tensor != nullptr ? tensor->data.uint8 : nullptr; } +template <> +inline const int8_t* GetTensorData(const TfLiteTensor* tensor) { + return tensor != nullptr ? tensor->data.int8 : nullptr; +} + template <> inline const int16_t* GetTensorData(const TfLiteTensor* tensor) { return tensor != nullptr ? tensor->data.i16 : nullptr; diff --git a/tensorflow/lite/kernels/internal/types.h b/tensorflow/lite/kernels/internal/types.h index a05bd5e003..859ec8c682 100644 --- a/tensorflow/lite/kernels/internal/types.h +++ b/tensorflow/lite/kernels/internal/types.h @@ -904,6 +904,14 @@ struct PadParams { ResizingCategory resizing_category; }; +struct PreluParams { + int32 input_offset; + int32 alpha_offset; + int32 output_offset; + int32 output_multiplier; + int output_shift; +}; + struct PoolParams { FusedActivationFunctionType activation; PaddingType padding_type; @@ -1006,6 +1014,10 @@ struct UnpackParams { int16 axis; }; +struct LeakyReluParams { + float alpha; +}; + template inline void SetActivationParams(float min, float max, P* params) { params->float_activation_min = min; diff --git a/tensorflow/lite/kernels/mirror_pad.cc b/tensorflow/lite/kernels/mirror_pad.cc new file mode 100644 index 0000000000..e74e47f7a3 --- /dev/null +++ b/tensorflow/lite/kernels/mirror_pad.cc @@ -0,0 +1,374 @@ +/* Copyright 2018 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 + +#include "tensorflow/lite/c/builtin_op_data.h" +#include "tensorflow/lite/c/c_api_internal.h" +#include "tensorflow/lite/kernels/internal/optimized/optimized_ops.h" +#include "tensorflow/lite/kernels/internal/quantization_util.h" +#include "tensorflow/lite/kernels/internal/reference/reference_ops.h" +#include "tensorflow/lite/kernels/internal/tensor.h" +#include "tensorflow/lite/kernels/kernel_util.h" +#include "tensorflow/lite/kernels/op_macros.h" + +namespace tflite { +namespace ops { +namespace builtin { +namespace mirror_pad { +namespace { + +// Simple class that represents a mirror padded tensor - which is the output +// from the Op. +struct PaddedTensor { + // If not null that means this is a scalar value. + // Note: This is not owned by default. It will point to the value + // in the input tensor. + const void* value = nullptr; + // If this tensor is not one value, then this vector will have + // all the tensors that belongs to this tensor. + // Pointers are owned. + std::vector> values; + // Pointers to PaddedTensors that are padded on the left of the current + // tensor. + std::vector left_pad_ptrs; + // Pointers to PaddedTensors that are padded on the right of the current + // tensor. + std::vector right_pad_ptrs; + + // Returns mutable pointer to the tensor identified by 'indices'. + PaddedTensor* GetMutable(const std::vector& indices) { + auto* result = this; + for (int i = 0; i < indices.size(); ++i) { + if (indices[i] >= result->values.size()) { + return nullptr; + } + result = result->values[indices[i]].get(); + if (result == nullptr) break; + } + return result; + } +}; + +// Util method to initialize the memory of the padded tensor. +void InitializeTensorMemory(const TfLiteIntArray* const dims, int dim_index, + int dims_size, PaddedTensor* padded_tensor) { + if (dim_index >= dims_size) { + return; + } + padded_tensor->values.reserve(dims->data[dim_index]); + for (int i = 0; i < dims->data[dim_index]; ++i) { + padded_tensor->values.emplace_back(new PaddedTensor()); + InitializeTensorMemory(dims, dim_index + 1, dims_size, + padded_tensor->values.back().get()); + } +} + +// Returns pointer to the value at the specified index in 'data'. +inline const void* GetValuePointerAtIndex(const void* data, int index, + const TfLiteType data_type) { + switch (data_type) { + case kTfLiteFloat32: + return static_cast(data) + index; + case kTfLiteInt32: + return static_cast(data) + index; + case kTfLiteUInt8: + return static_cast(data) + index; + case kTfLiteInt64: + return static_cast(data) + index; + case kTfLiteBool: + return static_cast(data) + index; + case kTfLiteInt16: + return static_cast(data) + index; + case kTfLiteInt8: + return static_cast(data) + index; + // Unsupported types ? + default: + return nullptr; + } + return nullptr; +} + +// Util method that increment index in the N-d array. +void IncrementTensorIndex(const TfLiteIntArray* dims, + std::vector* tensor_index_ptr) { + int dimension_index = dims->size - 1; + auto& tensor_index = *tensor_index_ptr; + tensor_index[dimension_index]++; + while (dimension_index >= 0 && + tensor_index[dimension_index] == dims->data[dimension_index]) { + tensor_index[dimension_index] = 0; + dimension_index--; + if (dimension_index >= 0) tensor_index[dimension_index]++; + } +} + +// Fills the 'padded_tensor' with data from 'input_tensor'. +TfLiteStatus InitFromInputTensor(const TfLiteTensor* input_tensor, + PaddedTensor* padded_tensor) { + const auto* dims = input_tensor->dims; + const auto data_type = input_tensor->type; + const void* data = static_cast(input_tensor->data.raw_const); + // Either invalid input or unsupported type.+ + if (data == nullptr) { + return kTfLiteError; + } + // Index of current processing tensor. + std::vector tensor_index(dims->size, 0); + int flat_index = 0; + const int num_elements = NumElements(input_tensor); + while (flat_index < num_elements) { + auto* tensor = padded_tensor->GetMutable(tensor_index); + if (tensor == nullptr) { + return kTfLiteError; + } + tensor->value = GetValuePointerAtIndex(data, flat_index, data_type); + IncrementTensorIndex(dims, &tensor_index); + ++flat_index; + } + + return kTfLiteOk; +} + +template +inline void GetPadding(const T* data, int offset, int64_t* left_pad, + int64_t* right_pad) { + *left_pad = static_cast(*(data + offset * 2)); + *right_pad = static_cast(*(data + offset * 2 + 1)); +} + +inline TfLiteStatus GetPadding(const TfLiteTensor* padding_matrix, + int dimension, int64_t* left_pad, + int64_t* right_pad) { + switch (padding_matrix->type) { + case kTfLiteInt32: + GetPadding(padding_matrix->data.i32, dimension, left_pad, right_pad); + break; + case kTfLiteInt64: + GetPadding(padding_matrix->data.i64, dimension, left_pad, right_pad); + break; + default: + return kTfLiteError; + } + return kTfLiteOk; +} + +TfLiteStatus ValidateTensor(const TfLiteTensor* padding_matrix, int offset, + int dimension_index, PaddedTensor* padded_tensor, + TfLiteContext* context) { + if (dimension_index >= padding_matrix->dims->data[0]) { + return kTfLiteOk; + } + + int64_t left_pad = 0, right_pad = 0; + TF_LITE_ENSURE_STATUS( + GetPadding(padding_matrix, dimension_index, &left_pad, &right_pad)); + // If we are not going to include border we must have enough values + // to use. + if (left_pad + offset > padded_tensor->values.size()) { + context->ReportError( + context, "Not enough values for Mirror Pad, required %d, available %d.", + left_pad + offset, padded_tensor->values.size()); + return kTfLiteError; + } + if (right_pad + offset > padded_tensor->values.size()) { + context->ReportError( + context, "Not enough values for Mirror Pad, required %d, available %d.", + right_pad + offset, padded_tensor->values.size()); + return kTfLiteError; + } + if (!padded_tensor->values.empty()) { + ValidateTensor(padding_matrix, offset, dimension_index + 1, + padded_tensor->values[0].get(), context); + } + return kTfLiteOk; +} + +// Fills 'padded_tensor' with the padding information based on +// 'padding_matrix'. +// 'dimension_index' represents which dimension the function is operating on. +TfLiteStatus PadTensor(const TfLiteTensor* padding_matrix, int offset, + int dimension_index, PaddedTensor* padded_tensor, + TfLiteContext* context) { + if (dimension_index >= padding_matrix->dims->data[0]) return kTfLiteOk; + + int64_t left_pad = 0, right_pad = 0; + TF_LITE_ENSURE_STATUS( + GetPadding(padding_matrix, dimension_index, &left_pad, &right_pad)); + + for (int i = left_pad + offset - 1; i >= offset && left_pad > 0; + --i, --left_pad) { + padded_tensor->left_pad_ptrs.push_back(padded_tensor->values[i].get()); + } + for (int i = padded_tensor->values.size() - (1 + offset); + i >= 0 && right_pad > 0; --i, --right_pad) { + padded_tensor->right_pad_ptrs.push_back(padded_tensor->values[i].get()); + } + + for (auto& tensor : padded_tensor->values) { + TF_LITE_ENSURE_STATUS(PadTensor(padding_matrix, offset, dimension_index + 1, + tensor.get(), context)); + } + return kTfLiteOk; +} + +// Fills 'output_data' with data from 'padded_tensor'. +// The function does this recursively by setting left padding first then +// original data, followed by the right padding. +template +int FillOutput(const PaddedTensor* padded_tensor, T* output_data, + int index_in_output) { + if (padded_tensor == nullptr || output_data == nullptr) { + return -1; + } + if (padded_tensor->value != nullptr) { + output_data[index_in_output] = *static_cast(padded_tensor->value); + return index_in_output + 1; + } + for (const auto* tensor : padded_tensor->left_pad_ptrs) { + index_in_output = FillOutput(tensor, output_data, index_in_output); + } + for (const auto& tensor : padded_tensor->values) { + index_in_output = FillOutput(tensor.get(), output_data, index_in_output); + } + for (const auto* tensor : padded_tensor->right_pad_ptrs) { + index_in_output = FillOutput(tensor, output_data, index_in_output); + } + return index_in_output; +} + +// Returns the shape of the final output after padding. +std::unique_ptr GetPaddedOutputShape( + const TfLiteTensor* input, const TfLiteTensor* padding_matrix) { + const int input_dims = NumDimensions(input); + std::unique_ptr shape( + TfLiteIntArrayCreate(input_dims), TfLiteIntArrayFree); + + int64_t left_pad = 0, right_pad = 0; + for (int i = 0; i < input_dims; ++i) { + GetPadding(padding_matrix, i, &left_pad, &right_pad); + shape->data[i] = SizeOfDimension(input, i) + left_pad + right_pad; + } + return shape; +} + +} // namespace + +TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { + const TfLiteTensor* input_tensor = GetInput(context, node, 0); + const TfLiteTensor* padding_matrix = GetInput(context, node, 1); + auto* params = + reinterpret_cast(node->builtin_data); + + if (params == nullptr) { + return kTfLiteError; + } + const int input_dims = NumDimensions(input_tensor); + + TfLiteTensor* output_tensor = GetOutput(context, node, 0); + if (IsDynamicTensor(output_tensor)) { + auto output_size = GetPaddedOutputShape(input_tensor, padding_matrix); + if (output_size == nullptr) { + return kTfLiteError; + } + TF_LITE_ENSURE_STATUS( + context->ResizeTensor(context, output_tensor, output_size.release())); + } + + PaddedTensor padded_tensor; + // Initialize memory. + InitializeTensorMemory(input_tensor->dims, 0, input_dims, &padded_tensor); + // Set the values from the input_tensor. + TF_LITE_ENSURE_STATUS(InitFromInputTensor(input_tensor, &padded_tensor)); + + const int offset = + params->mode != TfLiteMirrorPaddingMode::kTfLiteMirrorPaddingReflect ? 0 + : 1; + // Make sure padding values are sufficient and valid to use. + TF_LITE_ENSURE_STATUS( + ValidateTensor(padding_matrix, offset, 0, &padded_tensor, context)); + // Apply padding. + TF_LITE_ENSURE_STATUS( + PadTensor(padding_matrix, offset, 0, &padded_tensor, context)); + + // Fill the output tensor from the padded tensor. + TfLiteStatus status = kTfLiteOk; + +#define TF_LITE_MIRROR_PAD(type) \ + FillOutput(&padded_tensor, GetTensorData(output_tensor), 0); + + switch (output_tensor->type) { + case kTfLiteFloat32: { + TF_LITE_MIRROR_PAD(float); + break; + } + case kTfLiteInt32: { + TF_LITE_MIRROR_PAD(int32_t); + break; + } + case kTfLiteUInt8: { + TF_LITE_MIRROR_PAD(uint8_t); + break; + } + case kTfLiteInt64: { + TF_LITE_MIRROR_PAD(int64_t); + break; + } + default: + status = kTfLiteError; + break; + } +#undef TF_LITE_MIRROR_PAD + return status; +} + +void* Init(TfLiteContext* context, const char* buffer, size_t length) { + return nullptr; +} + +void Free(TfLiteContext* context, void* buffer) {} + +TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { + const TfLiteTensor* input_tensor = GetInput(context, node, 0); + const TfLiteTensor* padding_matrix = GetInput(context, node, 1); + TfLiteTensor* output_tensor = GetOutput(context, node, 0); + + TF_LITE_ENSURE_EQ(context, NumDimensions(padding_matrix), 2); + TF_LITE_ENSURE_EQ(context, SizeOfDimension(padding_matrix, 0), + NumDimensions(input_tensor)); + + if (!IsConstantTensor(padding_matrix)) { + SetTensorToDynamic(output_tensor); + return kTfLiteOk; + } + // We have constant padding, so we can infer output size. + + auto output_size = GetPaddedOutputShape(input_tensor, padding_matrix); + if (output_size == nullptr) { + return kTfLiteError; + } + return context->ResizeTensor(context, output_tensor, output_size.release()); +} + +} // namespace mirror_pad +TfLiteRegistration* Register_MIRROR_PAD() { + static TfLiteRegistration r = {mirror_pad::Init, mirror_pad::Free, + mirror_pad::Prepare, mirror_pad::Eval}; + return &r; +} + +} // namespace builtin +} // namespace ops +} // namespace tflite diff --git a/tensorflow/lite/kernels/mirror_pad_test.cc b/tensorflow/lite/kernels/mirror_pad_test.cc new file mode 100644 index 0000000000..fd09e6e449 --- /dev/null +++ b/tensorflow/lite/kernels/mirror_pad_test.cc @@ -0,0 +1,189 @@ +/* Copyright 2018 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 +#include "tensorflow/lite/interpreter.h" +#include "tensorflow/lite/kernels/register.h" +#include "tensorflow/lite/kernels/test_util.h" +#include "tensorflow/lite/model.h" + +namespace tflite { +namespace { + +using ::testing::ElementsAreArray; + +template +class BaseMirrorPadOpModel : public SingleOpModel { + public: + BaseMirrorPadOpModel(const TensorData& input, + const TensorData& padding_matrix, + const TensorData& output, + const tflite::MirrorPadMode mode) { + input_id_ = AddInput(input); + padding_matrix_id_ = AddInput(padding_matrix); + output_id_ = AddOutput(output); + SetBuiltinOp(BuiltinOperator_MIRROR_PAD, BuiltinOptions_MirrorPadOptions, + CreateMirrorPadOptions(builder_, mode).Union()); + BuildInterpreter({GetShape(input_id_), GetShape(padding_matrix_id_)}); + } + + int input_tensor_id() { return input_id_; } + int padding_matrix_tensor_id() { return padding_matrix_id_; } + + std::vector GetOutput() { return ExtractVector(output_id_); } + + protected: + int input_id_; + int padding_matrix_id_; + int output_id_; +}; + +TEST(MirrorPadTest, EmptyPad) { + BaseMirrorPadOpModel model( + {TensorType_INT32, {2, 3}}, {TensorType_INT32, {2, 2}}, + {TensorType_INT32, {}}, tflite::MirrorPadMode_REFLECT); + model.PopulateTensor(model.input_tensor_id(), {1, 2, 3, 4, 5, 6}); + model.PopulateTensor(model.padding_matrix_tensor_id(), {0, 0, 0, 0}); + model.Invoke(); + EXPECT_THAT(model.GetOutput(), ElementsAreArray({1, 2, 3, 4, 5, 6})); +} + +TEST(MirrorPadTest, PadOneSide_right_Reflect) { + BaseMirrorPadOpModel model( + {TensorType_INT32, {2, 3}}, {TensorType_INT32, {2, 2}}, + {TensorType_INT32, {}}, tflite::MirrorPadMode_REFLECT); + model.PopulateTensor(model.input_tensor_id(), {1, 2, 3, 4, 5, 6}); + model.PopulateTensor(model.padding_matrix_tensor_id(), {0, 1, 0, 1}); + model.Invoke(); + EXPECT_THAT(model.GetOutput(), + ElementsAreArray({1, 2, 3, 2, 4, 5, 6, 5, 1, 2, 3, 2})); +} + +TEST(MirrorPadTest, PadOneSide_left_Reflect) { + BaseMirrorPadOpModel model( + {TensorType_INT32, {2, 3}}, {TensorType_INT32, {2, 2}}, + {TensorType_INT32, {}}, tflite::MirrorPadMode_REFLECT); + model.PopulateTensor(model.input_tensor_id(), {1, 2, 3, 4, 5, 6}); + model.PopulateTensor(model.padding_matrix_tensor_id(), {1, 0, 1, 0}); + model.Invoke(); + EXPECT_THAT(model.GetOutput(), + ElementsAreArray({5, 4, 5, 6, 2, 1, 2, 3, 5, 4, 5, 6})); +} + +TEST(MirrorPadTest, PadOneSide_right_Symmetric) { + BaseMirrorPadOpModel model( + {TensorType_INT32, {2, 3}}, {TensorType_INT32, {2, 2}}, + {TensorType_INT32, {}}, tflite::MirrorPadMode_SYMMETRIC); + model.PopulateTensor(model.input_tensor_id(), {1, 2, 3, 4, 5, 6}); + model.PopulateTensor(model.padding_matrix_tensor_id(), {0, 1, 0, 1}); + model.Invoke(); + EXPECT_THAT(model.GetOutput(), + ElementsAreArray({1, 2, 3, 3, 4, 5, 6, 6, 4, 5, 6, 6})); +} + +TEST(MirrorPadTest, PadOneSide_left_Symmetric) { + BaseMirrorPadOpModel model( + {TensorType_INT32, {2, 3}}, {TensorType_INT32, {2, 2}}, + {TensorType_INT32, {}}, tflite::MirrorPadMode_SYMMETRIC); + model.PopulateTensor(model.input_tensor_id(), {1, 2, 3, 4, 5, 6}); + model.PopulateTensor(model.padding_matrix_tensor_id(), {1, 0, 1, 0}); + model.Invoke(); + EXPECT_THAT(model.GetOutput(), + ElementsAreArray({1, 1, 2, 3, 1, 1, 2, 3, 4, 4, 5, 6})); +} + +TEST(MirrorPadTest, PadBothSides_Symmetric) { + BaseMirrorPadOpModel model( + {TensorType_INT32, {2, 3}}, {TensorType_INT32, {2, 2}}, + {TensorType_INT32, {}}, tflite::MirrorPadMode_SYMMETRIC); + model.PopulateTensor(model.input_tensor_id(), {1, 2, 3, 4, 5, 6}); + model.PopulateTensor(model.padding_matrix_tensor_id(), {1, 1, 1, 1}); + model.Invoke(); + EXPECT_THAT(model.GetOutput(), + ElementsAreArray({1, 1, 2, 3, 3, 1, 1, 2, 3, 3, + 4, 4, 5, 6, 6, 4, 4, 5, 6, 6})); +} + +TEST(MirrorPadTest, PadBothSides_Reflect) { + BaseMirrorPadOpModel model( + {TensorType_INT32, {2, 3}}, {TensorType_INT32, {2, 2}}, + {TensorType_INT32, {}}, tflite::MirrorPadMode_REFLECT); + model.PopulateTensor(model.input_tensor_id(), {1, 2, 3, 4, 5, 6}); + model.PopulateTensor(model.padding_matrix_tensor_id(), {1, 1, 1, 1}); + model.Invoke(); + EXPECT_THAT(model.GetOutput(), + ElementsAreArray({5, 4, 5, 6, 5, 2, 1, 2, 3, 2, + 5, 4, 5, 6, 5, 2, 1, 2, 3, 2})); +} + +TEST(MirrorPadTest, PadBothSides_Symmetric_Whole) { + BaseMirrorPadOpModel model( + {TensorType_INT32, {2, 3}}, {TensorType_INT32, {2, 2}}, + {TensorType_INT32, {}}, tflite::MirrorPadMode_SYMMETRIC); + model.PopulateTensor(model.input_tensor_id(), {1, 2, 3, 4, 5, 6}); + model.PopulateTensor(model.padding_matrix_tensor_id(), {2, 2, 3, 3}); + model.Invoke(); + EXPECT_THAT( + model.GetOutput(), + ElementsAreArray({6, 5, 4, 4, 5, 6, 6, 5, 4, 3, 2, 1, 1, 2, 3, 3, 2, 1, + 3, 2, 1, 1, 2, 3, 3, 2, 1, 6, 5, 4, 4, 5, 6, 6, 5, 4, + 6, 5, 4, 4, 5, 6, 6, 5, 4, 3, 2, 1, 1, 2, 3, 3, 2, 1})); +} + +TEST(MirrorPadTest, PadBothSides_Reflect_Whole) { + BaseMirrorPadOpModel model( + {TensorType_INT32, {2, 3}}, {TensorType_INT32, {2, 2}}, + {TensorType_INT32, {}}, tflite::MirrorPadMode_REFLECT); + model.PopulateTensor(model.input_tensor_id(), {1, 2, 3, 4, 5, 6}); + model.PopulateTensor(model.padding_matrix_tensor_id(), {1, 1, 2, 2}); + model.Invoke(); + EXPECT_THAT(model.GetOutput(), + ElementsAreArray({6, 5, 4, 5, 6, 5, 4, 3, 2, 1, 2, 3, 2, 1, + 6, 5, 4, 5, 6, 5, 4, 3, 2, 1, 2, 3, 2, 1})); +} + +TEST(MirrorPadTest, Pad_Symmetric) { + BaseMirrorPadOpModel model( + {TensorType_INT32, {2, 3}}, {TensorType_INT32, {2, 2}}, + {TensorType_INT32, {}}, tflite::MirrorPadMode_SYMMETRIC); + model.PopulateTensor(model.input_tensor_id(), {1, 2, 3, 4, 5, 6}); + model.PopulateTensor(model.padding_matrix_tensor_id(), {1, 1, 2, 2}); + model.Invoke(); + EXPECT_THAT(model.GetOutput(), + ElementsAreArray({2, 1, 1, 2, 3, 3, 2, 2, 1, 1, 2, 3, 3, 2, + 5, 4, 4, 5, 6, 6, 5, 5, 4, 4, 5, 6, 6, 5})); +} + +TEST(MirrorPadTest, Pad_1D_Reflect) { + BaseMirrorPadOpModel model( + {TensorType_INT32, {3}}, {TensorType_INT32, {1, 2}}, + {TensorType_INT32, {}}, tflite::MirrorPadMode_REFLECT); + model.PopulateTensor(model.input_tensor_id(), {1, 2, 3}); + model.PopulateTensor(model.padding_matrix_tensor_id(), {0, 2}); + model.Invoke(); + EXPECT_THAT(model.GetOutput(), ElementsAreArray({1, 2, 3, 2, 1})); +} + +TEST(MirrorPadTest, Pad_1D_Symmetric) { + BaseMirrorPadOpModel model( + {TensorType_INT32, {3}}, {TensorType_INT32, {1, 2}}, + {TensorType_INT32, {}}, tflite::MirrorPadMode_SYMMETRIC); + model.PopulateTensor(model.input_tensor_id(), {1, 2, 3}); + model.PopulateTensor(model.padding_matrix_tensor_id(), {0, 2}); + model.Invoke(); + EXPECT_THAT(model.GetOutput(), ElementsAreArray({1, 2, 3, 3, 2})); +} + +} // namespace +} // namespace tflite diff --git a/tensorflow/lite/kernels/pooling_test.cc b/tensorflow/lite/kernels/pooling_test.cc index 80eef02509..98777f1c13 100644 --- a/tensorflow/lite/kernels/pooling_test.cc +++ b/tensorflow/lite/kernels/pooling_test.cc @@ -67,6 +67,10 @@ class QuantizedPoolingOpModel : public BasePoolingOpModel { QuantizeAndPopulate(input_, data); } + void SetInput(const std::vector& data) { + QuantizeAndPopulate(input_, data); + } + std::vector GetOutput() { return ExtractVector(output_); } std::vector GetDequantizedOutput() { return Dequantize(ExtractVector(output_), @@ -106,6 +110,45 @@ TEST(QuantizedPoolingOpTest, AveragePool) { EXPECT_THAT(m.GetOutput(), ElementsAreArray({44, 92})); } +// Send in a white image, expect a white pixel. +TEST(QuantizedPoolingOpTest, AveragePoolImageSize16) { + int image_size = 16; + QuantizedPoolingOpModel m( + BuiltinOperator_AVERAGE_POOL_2D, + /*input=*/{TensorType_UINT8, {1, image_size, image_size, 1}, 0, 16}, + /*filter_width=*/image_size, + /*filter_height=*/image_size, + /*output=*/{TensorType_UINT8, {}, 0, 16}); + + std::vector input(image_size * image_size, 16.f); + m.SetInput(input); + m.Invoke(); + + EXPECT_THAT(m.GetOutput(), ::testing::ElementsAre(255)); + EXPECT_THAT(m.GetDequantizedOutput(), ElementsAreArray(ArrayFloatNear({16}))); +} + +// Send in a white image, expect something other than a white pixel, due to +// overflow. +TEST(QuantizedPoolingOpTest, AveragePoolImageSize17) { + int image_size = 17; + QuantizedPoolingOpModel m( + BuiltinOperator_AVERAGE_POOL_2D, + /*input=*/{TensorType_UINT8, {1, image_size, image_size, 1}, 0, 16}, + /*filter_width=*/image_size, + /*filter_height=*/image_size, + /*output=*/{TensorType_UINT8, {}, 0, 16}); + + std::vector input(image_size * image_size, 16.f); + m.SetInput(input); + m.Invoke(); + + // Ordinarily we would see '255' here. However, the optimized version of + // AveragePool uses a uint16 accumulator which causes it to overflow for + // images this large. + EXPECT_THAT(m.GetOutput(), ::testing::ElementsAre(28)); +} + TEST(FloatPoolingOpTest, MaxPool) { FloatPoolingOpModel m(BuiltinOperator_MAX_POOL_2D, /*input=*/{TensorType_FLOAT32, {1, 2, 4, 1}}, diff --git a/tensorflow/lite/kernels/reduce.cc b/tensorflow/lite/kernels/reduce.cc index ed2d475f6d..336e827ca4 100644 --- a/tensorflow/lite/kernels/reduce.cc +++ b/tensorflow/lite/kernels/reduce.cc @@ -20,6 +20,8 @@ limitations under the License. #include "tensorflow/lite/kernels/internal/quantization_util.h" #include "tensorflow/lite/kernels/internal/reference/reference_ops.h" #include "tensorflow/lite/kernels/internal/tensor.h" +#include "tensorflow/lite/kernels/internal/tensor_ctypes.h" +#include "tensorflow/lite/kernels/internal/types.h" #include "tensorflow/lite/kernels/kernel_util.h" #include "tensorflow/lite/kernels/op_macros.h" @@ -229,6 +231,17 @@ TfLiteStatus PrepareMeanOrSum(TfLiteContext* context, TfLiteNode* node) { return ResizeTempSum(context, &op_context, temp_sum); } +void ResolveAxis(const int* axis_data, int axis_count, + tflite::MeanParams* op_params) { + int i = 0; + for (; i < axis_count; ++i) { + op_params->axis[i] = static_cast(axis_data[i]); + } + for (; i < 4; ++i) { + op_params->axis[i] = 1; + } +} + template TfLiteStatus EvalMean(TfLiteContext* context, TfLiteNode* node) { OpContext op_context(context, node); @@ -257,9 +270,23 @@ TfLiteStatus EvalMean(TfLiteContext* context, TfLiteNode* node) { if (kernel_type == kReference) { switch (op_context.input->type) { - case kTfLiteFloat32: - TF_LITE_ENSURE(context, TF_LITE_MEAN(reference_ops, float, float)); - break; + case kTfLiteFloat32: { + tflite::MeanParams op_params; + op_params.axis_count = num_axis; + ResolveAxis(GetTensorData(op_context.axis), num_axis, &op_params); + const TfLiteTensor* input = op_context.input; + if (op_context.params->keep_dims && NumDimensions(input) == 4 && + op_params.axis_count == 2 && + ((op_params.axis[0] == 1 && op_params.axis[1] == 2) || + (op_params.axis[0] == 2 && op_params.axis[1] == 1))) { + reference_ops::Mean(op_params, GetTensorShape(input), + GetTensorData(input), + GetTensorShape(op_context.output), + GetTensorData(op_context.output)); + } else { + TF_LITE_ENSURE(context, TF_LITE_MEAN(reference_ops, float, float)); + } + } break; case kTfLiteInt32: TF_LITE_ENSURE(context, TF_LITE_MEAN(reference_ops, int, int64_t)); break; @@ -286,7 +313,8 @@ TfLiteStatus EvalMean(TfLiteContext* context, TfLiteNode* node) { GetTensorData(op_context.axis), num_axis, op_context.params->keep_dims, GetTensorData(temp_index), GetTensorData(resolved_axis), - GetTensorData(temp_sum), /*compute_sum=*/false)); + GetTensorData(temp_sum), + /*compute_sum=*/false)); } break; default: diff --git a/tensorflow/lite/kernels/register.cc b/tensorflow/lite/kernels/register.cc index c683453767..3c60d281b3 100644 --- a/tensorflow/lite/kernels/register.cc +++ b/tensorflow/lite/kernels/register.cc @@ -31,6 +31,7 @@ TfLiteRegistration* Register_RELU_1(); namespace builtin { +TfLiteRegistration* Register_ABS(); TfLiteRegistration* Register_RELU(); TfLiteRegistration* Register_RELU_N1_TO_1(); TfLiteRegistration* Register_RELU6(); @@ -74,6 +75,7 @@ TfLiteRegistration* Register_GATHER(); TfLiteRegistration* Register_TRANSPOSE(); TfLiteRegistration* Register_MEAN(); TfLiteRegistration* Register_SPLIT(); +TfLiteRegistration* Register_SPLIT_V(); TfLiteRegistration* Register_SQUEEZE(); TfLiteRegistration* Register_STRIDED_SLICE(); TfLiteRegistration* Register_EXP(); @@ -123,6 +125,10 @@ TfLiteRegistration* Register_SQUARE(); TfLiteRegistration* Register_ZEROS_LIKE(); TfLiteRegistration* Register_FLOOR_MOD(); TfLiteRegistration* Register_RANGE(); +TfLiteRegistration* Register_LEAKY_RELU(); +TfLiteRegistration* Register_SQUARED_DIFFERENCE(); +TfLiteRegistration* Register_FILL(); +TfLiteRegistration* Register_MIRROR_PAD(); TfLiteStatus UnsupportedTensorFlowOp(TfLiteContext* context, TfLiteNode* node) { context->ReportError( @@ -152,6 +158,7 @@ const TfLiteRegistration* BuiltinOpResolver::FindOp(const char* op, } BuiltinOpResolver::BuiltinOpResolver() { + AddBuiltin(BuiltinOperator_ABS, Register_ABS()); AddBuiltin(BuiltinOperator_RELU, Register_RELU()); AddBuiltin(BuiltinOperator_RELU_N1_TO_1, Register_RELU_N1_TO_1()); AddBuiltin(BuiltinOperator_RELU6, Register_RELU6()); @@ -207,6 +214,7 @@ BuiltinOpResolver::BuiltinOpResolver() { AddBuiltin(BuiltinOperator_DIV, Register_DIV()); AddBuiltin(BuiltinOperator_SUB, Register_SUB()); AddBuiltin(BuiltinOperator_SPLIT, Register_SPLIT()); + AddBuiltin(BuiltinOperator_SPLIT_V, Register_SPLIT_V()); AddBuiltin(BuiltinOperator_SQUEEZE, Register_SQUEEZE()); AddBuiltin(BuiltinOperator_STRIDED_SLICE, Register_STRIDED_SLICE()); AddBuiltin(BuiltinOperator_EXP, Register_EXP()); @@ -256,6 +264,10 @@ BuiltinOpResolver::BuiltinOpResolver() { AddBuiltin(BuiltinOperator_ZEROS_LIKE, Register_ZEROS_LIKE()); AddBuiltin(BuiltinOperator_FLOOR_MOD, Register_FLOOR_MOD()); AddBuiltin(BuiltinOperator_RANGE, Register_RANGE()); + AddBuiltin(BuiltinOperator_LEAKY_RELU, Register_LEAKY_RELU()); + AddBuiltin(BuiltinOperator_SQUARED_DIFFERENCE, Register_SQUARED_DIFFERENCE()); + AddBuiltin(BuiltinOperator_FILL, Register_FILL()); + AddBuiltin(BuiltinOperator_MIRROR_PAD, Register_MIRROR_PAD()); // TODO(andrewharp, ahentz): Move these somewhere more appropriate so that // custom ops aren't always included by default. diff --git a/tensorflow/lite/kernels/register.h b/tensorflow/lite/kernels/register.h index eb5ce667d4..059c9d165e 100644 --- a/tensorflow/lite/kernels/register.h +++ b/tensorflow/lite/kernels/register.h @@ -15,7 +15,6 @@ limitations under the License. #ifndef TENSORFLOW_LITE_KERNELS_REGISTER_H_ #define TENSORFLOW_LITE_KERNELS_REGISTER_H_ -#include #include "tensorflow/lite/c/c_api_internal.h" #include "tensorflow/lite/model.h" #include "tensorflow/lite/mutable_op_resolver.h" diff --git a/tensorflow/lite/kernels/skip_gram.cc b/tensorflow/lite/kernels/skip_gram.cc index f20719ecaf..265ba18a3e 100644 --- a/tensorflow/lite/kernels/skip_gram.cc +++ b/tensorflow/lite/kernels/skip_gram.cc @@ -107,7 +107,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { // Generate n-grams recursively. tflite::DynamicBuffer buf; if (words.size() < params->ngram_size) { - buf.WriteToTensor(GetOutput(context, node, 0)); + buf.WriteToTensorAsVector(GetOutput(context, node, 0)); return kTfLiteOk; } @@ -145,7 +145,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { } } - buf.WriteToTensor(GetOutput(context, node, 0)); + buf.WriteToTensorAsVector(GetOutput(context, node, 0)); return kTfLiteOk; } } // namespace diff --git a/tensorflow/lite/kernels/split_v.cc b/tensorflow/lite/kernels/split_v.cc new file mode 100644 index 0000000000..060e3c5f79 --- /dev/null +++ b/tensorflow/lite/kernels/split_v.cc @@ -0,0 +1,207 @@ +/* Copyright 2018 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 +#include "tensorflow/lite/c/builtin_op_data.h" +#include "tensorflow/lite/c/c_api_internal.h" +#include "tensorflow/lite/kernels/internal/optimized/optimized_ops.h" +#include "tensorflow/lite/kernels/internal/reference/reference_ops.h" +#include "tensorflow/lite/kernels/internal/tensor.h" +#include "tensorflow/lite/kernels/kernel_util.h" +#include "tensorflow/lite/kernels/op_macros.h" + +namespace tflite { +namespace ops { +namespace builtin { +namespace split_v { + +struct OpContext { + OpContext(TfLiteContext* context, TfLiteNode* node) { + params = reinterpret_cast(node->builtin_data); + input = GetInput(context, node, 0); + size_splits = GetInput(context, node, 1); + axis = GetInput(context, node, 2); + } + TfLiteSplitVParams* params; + const TfLiteTensor* input; + const TfLiteTensor* size_splits; + const TfLiteTensor* axis; +}; + +TfLiteStatus UseDynamicOutputTensors(TfLiteContext* context, TfLiteNode* node) { + for (int i = 0; i < NumOutputs(node); ++i) { + SetTensorToDynamic(GetOutput(context, node, i)); + } + return kTfLiteOk; +} + +template +void GetSizeSplitsVector(const TfLiteTensor* size_splits, + std::vector* size_splits_vector) { + const auto num_elements = NumElements(size_splits); + for (int i = 0; i < num_elements; ++i) { + size_splits_vector->push_back(GetTensorData(size_splits)[i]); + } +} + +TfLiteStatus ResizeOutputTensors(TfLiteContext* context, TfLiteNode* node, + const TfLiteTensor* input, + const TfLiteTensor* size_splits, + const TfLiteTensor* axis) { + int axis_value = GetTensorData(axis)[0]; + if (axis_value < 0) { + axis_value += NumDimensions(input); + } + + std::vector size_splits_vector; + if (size_splits->type == kTfLiteInt32) { + GetSizeSplitsVector(size_splits, &size_splits_vector); + } else if (size_splits->type == kTfLiteInt64) { + GetSizeSplitsVector(size_splits, &size_splits_vector); + } else { + context->ReportError(context, "size_splits only support type int32|int64."); + return kTfLiteError; + } + + int minus_one_index = -1; + int64_t size_splits_sum = 0; + + for (int i = 0; i < size_splits_vector.size(); ++i) { + if (size_splits_vector.at(i) == -1) { + if (minus_one_index == -1) { + minus_one_index = i; + } else { + context->ReportError(context, + "The size_splits contains more than one -1."); + } + } else { + size_splits_sum += size_splits_vector.at(i); + } + } + + const int input_size = SizeOfDimension(input, axis_value); + + if (minus_one_index != -1) { + if (size_splits_sum > input_size) { + context->ReportError( + context, + "The sum of size_splits must be less than the dimension of value."); + } else { + size_splits_vector[minus_one_index] = input_size - size_splits_sum; + } + } else if (size_splits_sum != input_size) { + context->ReportError( + context, + "The size_splits must sum to the dimension of value along axis."); + } + + for (int i = 0; i < NumOutputs(node); ++i) { + TfLiteIntArray* output_dims = TfLiteIntArrayCopy(input->dims); + output_dims->data[axis_value] = size_splits_vector.at(i); + TfLiteTensor* output = GetOutput(context, node, i); + TF_LITE_ENSURE_STATUS(context->ResizeTensor(context, output, output_dims)); + } + + return kTfLiteOk; +} + +TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { + TF_LITE_ENSURE_EQ(context, NumInputs(node), 3); + + OpContext op_context(context, node); + + TF_LITE_ENSURE_EQ(context, NumOutputs(node), op_context.params->num_splits); + + auto input_type = op_context.input->type; + TF_LITE_ENSURE(context, input_type == kTfLiteFloat32 || + input_type == kTfLiteUInt8 || + input_type == kTfLiteInt16); + for (int i = 0; i < NumOutputs(node); ++i) { + GetOutput(context, node, i)->type = input_type; + } + + auto size_splits = op_context.size_splits; + TF_LITE_ENSURE_EQ(context, NumDimensions(size_splits), 1); + TF_LITE_ENSURE_EQ(context, NumOutputs(node), NumElements(size_splits)); + + // If we know the contents of the 'size_splits' tensor and the 'axis' tensor, + // resize all outputs. Otherwise, wait until Eval(). + if (IsConstantTensor(op_context.size_splits) && + IsConstantTensor(op_context.axis)) { + return ResizeOutputTensors(context, node, op_context.input, + op_context.size_splits, op_context.axis); + } else { + return UseDynamicOutputTensors(context, node); + } +} + +TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { + OpContext op_context(context, node); + + // When the 'size_splits' and the 'axis' tensor is non-const we can't resize + // output tensors in Prepare(), and we have to do it now. + if (!IsConstantTensor(op_context.axis) || + !IsConstantTensor(op_context.size_splits)) { + TF_LITE_ENSURE_OK( + context, ResizeOutputTensors(context, node, op_context.input, + op_context.size_splits, op_context.axis)); + } + + int axis_value = GetTensorData(op_context.axis)[0]; + + // Use split function to build the outputs since they share the same logic. +#define TF_LITE_SPLIT_V(scalar) \ + VectorOfTensors all_outputs(*context, *node->outputs); \ + tflite::SplitParams op_params; \ + op_params.num_split = NumOutputs(node); \ + op_params.axis = axis_value; \ + reference_ops::Split(op_params, GetTensorShape(op_context.input), \ + GetTensorData(op_context.input), \ + all_outputs.shapes(), all_outputs.data()); + switch (op_context.input->type) { + case kTfLiteFloat32: { + TF_LITE_SPLIT_V(float); + break; + } + case kTfLiteUInt8: { + TF_LITE_SPLIT_V(uint8_t); + break; + } + case kTfLiteInt16: { + TF_LITE_SPLIT_V(int16_t); + break; + } + default: + context->ReportError( + context, + "Only float32, uint8 and int16 are currently supported, got %d.", + op_context.input->type); + return kTfLiteError; + } +#undef TF_LITE_SPLIT_V + + return kTfLiteOk; +} + +} // namespace split_v + +TfLiteRegistration* Register_SPLIT_V() { + static TfLiteRegistration r = {nullptr, nullptr, split_v::Prepare, + split_v::Eval}; + return &r; +} + +} // namespace builtin +} // namespace ops +} // namespace tflite diff --git a/tensorflow/lite/kernels/split_v_test.cc b/tensorflow/lite/kernels/split_v_test.cc new file mode 100644 index 0000000000..2d1d36d685 --- /dev/null +++ b/tensorflow/lite/kernels/split_v_test.cc @@ -0,0 +1,175 @@ +/* Copyright 2018 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 +#include +#include "tensorflow/lite/interpreter.h" +#include "tensorflow/lite/kernels/register.h" +#include "tensorflow/lite/kernels/test_util.h" +#include "tensorflow/lite/model.h" + +namespace tflite { +namespace { + +using ::testing::ElementsAreArray; + +constexpr int kAxisIsATensor = -1000; + +class SplitVOpModel : public SingleOpModel { + public: + SplitVOpModel(const TensorData& input, const TensorData& size_splits, + int num_splits, int axis) { + input_ = AddInput(input); + size_splits_ = AddInput(size_splits); + if (axis == kAxisIsATensor) { + axis_ = AddInput({TensorType_INT32, {1}}); + } else { + axis_ = AddConstInput(TensorType_INT32, {axis}, {1}); + } + for (int i = 0; i < num_splits; ++i) { + outputs_.push_back(AddOutput(input.type)); + } + SetBuiltinOp(BuiltinOperator_SPLIT_V, BuiltinOptions_SplitVOptions, + CreateSplitVOptions(builder_, num_splits).Union()); + if (axis == kAxisIsATensor) { + BuildInterpreter( + {GetShape(input_), GetShape(size_splits_), GetShape(axis_)}); + } else { + BuildInterpreter({GetShape(input_), GetShape(size_splits_), {}}); + } + } + + void SetInput(std::initializer_list data) { + PopulateTensor(input_, data); + } + void SetSizeSplits(std::initializer_list data) { + PopulateTensor(size_splits_, data); + } + void SetAxis(int axis) { PopulateTensor(axis_, {axis}); } + + std::vector GetOutput(int i) { + return ExtractVector(outputs_[i]); + } + std::vector GetOutputShape(int i) { return GetTensorShape(outputs_[i]); } + + private: + int input_; + int size_splits_; + int axis_; + std::vector outputs_; +}; + +// TODO(ruic): Add tests to test quantized values. b/119638735 +using TensorValues = std::initializer_list; + +void Check(int axis, std::initializer_list input_shape, + std::initializer_list size_splits_shape, + std::vector> output_shapes, + const TensorValues& input_data, + const std::initializer_list& size_splits_data, + const std::vector& output_data) { + int num_splits = size_splits_data.size(); + SplitVOpModel m({TensorType_FLOAT32, input_shape}, + {TensorType_INT32, size_splits_shape}, num_splits, + kAxisIsATensor); + m.SetInput(input_data); + m.SetSizeSplits(size_splits_data); + m.SetAxis(axis); + m.Invoke(); + for (int i = 0; i < num_splits; ++i) { + EXPECT_THAT(m.GetOutput(i), ElementsAreArray(output_data[i])); + EXPECT_THAT(m.GetOutputShape(i), ElementsAreArray(output_shapes[i])); + } + + SplitVOpModel const_m({TensorType_FLOAT32, input_shape}, + {TensorType_INT32, size_splits_shape}, num_splits, + axis); + const_m.SetInput(input_data); + const_m.SetSizeSplits(size_splits_data); + const_m.Invoke(); + for (int i = 0; i < num_splits; ++i) { + EXPECT_THAT(const_m.GetOutput(i), ElementsAreArray(output_data[i])); + EXPECT_THAT(const_m.GetOutputShape(i), ElementsAreArray(output_shapes[i])); + } +} + +TEST(SplitVOpTest, TwoDimensional) { + // Input shape: {4, 3} + // size_splits: {1, 1, 3} + // axis: 0 + // We should have 3 outpus with shapes respectively: + // output 0 : {1, 3} + // output 1 : {1, 3} + // output 1 : {2, 3} + Check(/*axis=*/0, {4, 3}, {3}, {{1, 3}, {1, 3}, {2, 3}}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, {1, 1, 2}, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9, 10, 11, 12}}); +} + +TEST(SplitVOpTest, FourDimensional) { + Check(/*axis=*/0, {2, 2, 2, 2}, {2}, {{1, 2, 2, 2}, {1, 2, 2, 2}}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, {1, 1}, + { + {1, 2, 3, 4, 5, 6, 7, 8}, + {9, 10, 11, 12, 13, 14, 15, 16}, + }); + Check(/*axis=*/1, {2, 2, 2, 2}, {2}, {{2, 1, 2, 2}, {2, 1, 2, 2}}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, {1, -1}, + { + {1, 2, 3, 4, 9, 10, 11, 12}, + {5, 6, 7, 8, 13, 14, 15, 16}, + }); + Check(/*axis=*/2, {2, 2, 2, 2}, {2}, {{2, 2, 1, 2}, {2, 2, 1, 2}}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, {1, 1}, + { + {1, 2, 5, 6, 9, 10, 13, 14}, + {3, 4, 7, 8, 11, 12, 15, 16}, + }); + Check(/*axis=*/3, {2, 2, 2, 2}, {2}, {{2, 2, 2, 1}, {2, 2, 2, 1}}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, {1, 1}, + { + {1, 3, 5, 7, 9, 11, 13, 15}, + {2, 4, 6, 8, 10, 12, 14, 16}, + }); +} + +TEST(SplitVOpTest, OneDimensional) { + Check(/*axis=*/0, {8}, {8}, {{1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}}, + {1, 2, 3, 4, 5, 6, 7, 8}, {1, 1, 1, 1, 1, 1, 1, 1}, + {{1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}}); +} + +TEST(SplitVOpTest, OneDimensional2) { + Check(/*axis=*/0, {8}, {8}, {{1}, {1}, {1}, {1}, {1}, {1}, {2}, {0}}, + {1, 2, 3, 4, 5, 6, 7, 8}, {1, 1, 1, 1, 1, 1, 2, -1}, + {{1}, {2}, {3}, {4}, {5}, {6}, {7, 8}, {}}); +} + +TEST(SplitVOpTest, NegativeAxis) { + Check(/*axis=*/-4, {2, 2, 2, 2}, {2}, {{1, 2, 2, 2}, {1, 2, 2, 2}}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, {1, 1}, + { + {1, 2, 3, 4, 5, 6, 7, 8}, + {9, 10, 11, 12, 13, 14, 15, 16}, + }); +} + +} // namespace +} // namespace tflite + +int main(int argc, char** argv) { + ::tflite::LogToStderr(); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tensorflow/lite/kernels/squared_difference.cc b/tensorflow/lite/kernels/squared_difference.cc new file mode 100644 index 0000000000..59b53a6287 --- /dev/null +++ b/tensorflow/lite/kernels/squared_difference.cc @@ -0,0 +1,129 @@ +/* Copyright 2018 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/lite/c/builtin_op_data.h" +#include "tensorflow/lite/c/c_api_internal.h" +#include "tensorflow/lite/kernels/internal/optimized/optimized_ops.h" +#include "tensorflow/lite/kernels/internal/quantization_util.h" +#include "tensorflow/lite/kernels/internal/reference/reference_ops.h" +#include "tensorflow/lite/kernels/internal/tensor.h" +#include "tensorflow/lite/kernels/kernel_util.h" +#include "tensorflow/lite/kernels/op_macros.h" + +namespace tflite { +namespace ops { +namespace builtin { +namespace squared_difference { + +constexpr int kInputTensor1 = 0; +constexpr int kInputTensor2 = 1; +constexpr int kOutputTensor = 0; + +struct OpData { + bool requires_broadcast; +}; + +template +T SquaredDifference(T input1, T input2) { + const T difference = input1 - input2; + return difference * difference; +} + +void* Init(TfLiteContext* context, const char* buffer, size_t length) { + auto* data = new OpData; + data->requires_broadcast = false; + return data; +} + +void Free(TfLiteContext* context, void* buffer) { + delete reinterpret_cast(buffer); +} + +TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { + OpData* data = reinterpret_cast(node->user_data); + + TF_LITE_ENSURE_EQ(context, NumInputs(node), 2); + TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1); + + const TfLiteTensor* input1 = GetInput(context, node, kInputTensor1); + const TfLiteTensor* input2 = GetInput(context, node, kInputTensor2); + TfLiteTensor* output = GetOutput(context, node, kOutputTensor); + + TF_LITE_ENSURE_EQ(context, input1->type, input2->type); + output->type = input2->type; + + data->requires_broadcast = !HaveSameShapes(input1, input2); + + TfLiteIntArray* output_size = nullptr; + if (data->requires_broadcast) { + TF_LITE_ENSURE_OK(context, CalculateShapeForBroadcast( + context, input1, input2, &output_size)); + } else { + output_size = TfLiteIntArrayCopy(input1->dims); + } + + return context->ResizeTensor(context, output, output_size); +} + +template +void EvalSquaredDifference(TfLiteContext* context, TfLiteNode* node, + const OpData* data, const TfLiteTensor* input1, + const TfLiteTensor* input2, TfLiteTensor* output) { + if (data->requires_broadcast) { + reference_ops::BroadcastBinaryFunction4DSlow( + GetTensorShape(input1), GetTensorData(input1), + GetTensorShape(input2), GetTensorData(input2), + GetTensorShape(output), GetTensorData(output), SquaredDifference); + } else { + reference_ops::BinaryFunction( + GetTensorShape(input1), GetTensorData(input1), + GetTensorShape(input2), GetTensorData(input2), + GetTensorShape(output), GetTensorData(output), SquaredDifference); + } +} + +TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { + OpData* data = reinterpret_cast(node->user_data); + + const TfLiteTensor* input1 = GetInput(context, node, kInputTensor1); + const TfLiteTensor* input2 = GetInput(context, node, kInputTensor2); + TfLiteTensor* output = GetOutput(context, node, kOutputTensor); + + if (output->type == kTfLiteFloat32) { + EvalSquaredDifference(context, node, data, input1, input2, output); + } else if (output->type == kTfLiteInt32) { + EvalSquaredDifference(context, node, data, input1, input2, output); + } else { + context->ReportError(context, + "SquaredDifference only supports FLOAT32, INT32 and " + "quantized UINT8 now, got %d.", + output->type); + return kTfLiteError; + } + + return kTfLiteOk; +} + +} // namespace squared_difference + +TfLiteRegistration* Register_SQUARED_DIFFERENCE() { + static TfLiteRegistration r = { + squared_difference::Init, squared_difference::Free, + squared_difference::Prepare, squared_difference::Eval}; + return &r; +} + +} // namespace builtin +} // namespace ops +} // namespace tflite diff --git a/tensorflow/lite/kernels/squared_difference_test.cc b/tensorflow/lite/kernels/squared_difference_test.cc new file mode 100644 index 0000000000..32bcab3b87 --- /dev/null +++ b/tensorflow/lite/kernels/squared_difference_test.cc @@ -0,0 +1,157 @@ +/* Copyright 2018 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 +#include "tensorflow/lite/interpreter.h" +#include "tensorflow/lite/kernels/register.h" +#include "tensorflow/lite/kernels/test_util.h" +#include "tensorflow/lite/model.h" + +namespace tflite { +namespace { + +using ::testing::ElementsAreArray; + +class BaseSquaredDifferenceOpModel : public SingleOpModel { + public: + BaseSquaredDifferenceOpModel(const TensorData& input1, + const TensorData& input2, + const TensorData& output) { + input1_ = AddInput(input1); + input2_ = AddInput(input2); + output_ = AddOutput(output); + SetBuiltinOp(BuiltinOperator_SQUARED_DIFFERENCE, + BuiltinOptions_SquaredDifferenceOptions, + CreateSquaredDifferenceOptions(builder_).Union()); + BuildInterpreter({GetShape(input1_), GetShape(input2_)}); + } + + int input1() { return input1_; } + int input2() { return input2_; } + + protected: + int input1_; + int input2_; + int output_; +}; + +class FloatSquaredDifferenceOpModel : public BaseSquaredDifferenceOpModel { + public: + using BaseSquaredDifferenceOpModel::BaseSquaredDifferenceOpModel; + + std::vector GetOutput() { return ExtractVector(output_); } +}; + +class IntegerSquaredDifferenceOpModel : public BaseSquaredDifferenceOpModel { + public: + using BaseSquaredDifferenceOpModel::BaseSquaredDifferenceOpModel; + + std::vector GetOutput() { return ExtractVector(output_); } +}; + +TEST(FloatSquaredDifferenceOpTest, FloatType_SameShape) { + FloatSquaredDifferenceOpModel m({TensorType_FLOAT32, {1, 2, 2, 1}}, + {TensorType_FLOAT32, {1, 2, 2, 1}}, + {TensorType_FLOAT32, {}}); + m.PopulateTensor(m.input1(), {-0.2, 0.2, -1.2, 0.8}); + m.PopulateTensor(m.input2(), {0.5, 0.2, -1.5, 0.5}); + m.Invoke(); + EXPECT_THAT(m.GetOutput(), + ElementsAreArray(ArrayFloatNear({0.49, 0.0, 0.09, 0.09}))); +} + +TEST(FloatSquaredDifferenceOpTest, FloatType_VariousInputShapes) { + std::vector> test_shapes = { + {6}, {2, 3}, {2, 1, 3}, {1, 3, 1, 2}}; + for (int i = 0; i < test_shapes.size(); ++i) { + FloatSquaredDifferenceOpModel m({TensorType_FLOAT32, test_shapes[i]}, + {TensorType_FLOAT32, test_shapes[i]}, + {TensorType_FLOAT32, {}}); + m.PopulateTensor(m.input1(), {-2.0, 0.2, 0.3, 0.8, 1.1, -2.0}); + m.PopulateTensor(m.input2(), {1.0, 0.2, 0.6, 0.4, -1.0, -0.0}); + m.Invoke(); + EXPECT_THAT( + m.GetOutput(), + ElementsAreArray(ArrayFloatNear({9.0, 0.0, 0.09, 0.16, 4.41, 4.0}))) + << "With shape number " << i; + } +} + +TEST(FloatSquaredDifferenceOpTest, FloatType_WithBroadcast) { + std::vector> test_shapes = { + {6}, {2, 3}, {2, 1, 3}, {1, 3, 1, 2}}; + for (int i = 0; i < test_shapes.size(); ++i) { + FloatSquaredDifferenceOpModel m( + {TensorType_FLOAT32, test_shapes[i]}, + {TensorType_FLOAT32, {}}, // always a scalar + {TensorType_FLOAT32, {}}); + m.PopulateTensor(m.input1(), {-0.2, 0.2, 0.5, 0.8, 0.11, 1.1}); + m.PopulateTensor(m.input2(), {0.1}); + m.Invoke(); + EXPECT_THAT( + m.GetOutput(), + ElementsAreArray(ArrayFloatNear({0.09, 0.01, 0.16, 0.49, 0.0001, 1.0}))) + << "With shape number " << i; + } +} + +TEST(IntegerSquaredDifferenceOpTest, IntegerType_SameShape) { + IntegerSquaredDifferenceOpModel m({TensorType_INT32, {1, 2, 2, 1}}, + {TensorType_INT32, {1, 2, 2, 1}}, + {TensorType_INT32, {}}); + m.PopulateTensor(m.input1(), {-2, 2, -15, 8}); + m.PopulateTensor(m.input2(), {5, -2, -3, 5}); + m.Invoke(); + EXPECT_THAT(m.GetOutput(), ElementsAreArray({49, 16, 144, 9})); +} + +TEST(IntegerSquaredDifferenceOpTest, IntegerType_VariousInputShapes) { + std::vector> test_shapes = { + {6}, {2, 3}, {2, 1, 3}, {1, 3, 1, 2}}; + for (int i = 0; i < test_shapes.size(); ++i) { + IntegerSquaredDifferenceOpModel m({TensorType_INT32, test_shapes[i]}, + {TensorType_INT32, test_shapes[i]}, + {TensorType_INT32, {}}); + m.PopulateTensor(m.input1(), {-20, 2, 3, 8, 11, -20}); + m.PopulateTensor(m.input2(), {1, 2, 6, 5, -5, -20}); + m.Invoke(); + EXPECT_THAT(m.GetOutput(), ElementsAreArray({441, 0, 9, 9, 256, 0})) + << "With shape number " << i; + } +} + +TEST(IntegerSquaredDifferenceOpTest, IntegerType_WithBroadcast) { + std::vector> test_shapes = { + {6}, {2, 3}, {2, 1, 3}, {1, 3, 1, 2}}; + for (int i = 0; i < test_shapes.size(); ++i) { + IntegerSquaredDifferenceOpModel m( + {TensorType_INT32, test_shapes[i]}, + {TensorType_INT32, {}}, // always a scalar + {TensorType_INT32, {}}); + m.PopulateTensor(m.input1(), {-20, 10, 7, 3, 1, 13}); + m.PopulateTensor(m.input2(), {3}); + m.Invoke(); + EXPECT_THAT(m.GetOutput(), ElementsAreArray({529, 49, 16, 0, 4, 100})) + << "With shape number " << i; + } +} + +} // namespace +} // namespace tflite + +int main(int argc, char** argv) { + ::tflite::LogToStderr(); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tensorflow/lite/kernels/test_util.h b/tensorflow/lite/kernels/test_util.h index 43a5137a94..dadabb86ab 100644 --- a/tensorflow/lite/kernels/test_util.h +++ b/tensorflow/lite/kernels/test_util.h @@ -199,7 +199,7 @@ class SingleOpModel { for (const string& s : content) { buf.AddString(s.data(), s.length()); } - buf.WriteToTensor(tensor); + buf.WriteToTensor(tensor, /*new_shape=*/nullptr); } // Populate the tensor given its index. @@ -307,6 +307,7 @@ class SingleOpModel { if (is_quantized) { if (t.min != 0 || t.max != 0) { + // TODO(b/119422369): Handle signed int8 here. if (t.type == TensorType_UINT8) { std::tie(t.scale, t.zero_point) = QuantizationParams(t.min, t.max); diff --git a/tensorflow/lite/lib_package/create_ios_frameworks.sh b/tensorflow/lite/lib_package/create_ios_frameworks.sh index fa466ed5bc..7901655b7c 100755 --- a/tensorflow/lite/lib_package/create_ios_frameworks.sh +++ b/tensorflow/lite/lib_package/create_ios_frameworks.sh @@ -30,7 +30,7 @@ echo "Creating target Headers directories" mkdir -p $FW_DIR_TFLITE_HDRS echo "Headers, populating: TensorFlow Lite" -cd $TFLITE_DIR/../../.. +cd $TFLITE_DIR/../.. find tensorflow/lite -name '*.h' \ -not -path 'tensorflow/lite/tools/*' \ @@ -51,10 +51,10 @@ cd $FW_DIR_TFLITE_HDRS tar xf tmp.tar rm -f tmp.tar -cd $TFLITE_DIR/../../.. +cd $TFLITE_DIR/../.. echo "Generate master LICENSE file and copy to target" bazel build //tensorflow/tools/lib_package:clicenses_generate -cp $TFLITE_DIR/../../../bazel-genfiles/tensorflow/tools/lib_package/include/tensorflow/c/LICENSE \ +cp $TFLITE_DIR/../../bazel-genfiles/tensorflow/tools/lib_package/include/tensorflow/c/LICENSE \ $FW_DIR_TFLITE echo "Copying static libraries" diff --git a/tensorflow/lite/models/smartreply/demo/app/src/main/java/com/example/android/smartreply/SmartReplyClient.java b/tensorflow/lite/models/smartreply/demo/app/src/main/java/com/example/android/smartreply/SmartReplyClient.java index d5b1ac0ffb..fbd75051e7 100644 --- a/tensorflow/lite/models/smartreply/demo/app/src/main/java/com/example/android/smartreply/SmartReplyClient.java +++ b/tensorflow/lite/models/smartreply/demo/app/src/main/java/com/example/android/smartreply/SmartReplyClient.java @@ -90,29 +90,26 @@ public class SmartReplyClient implements AutoCloseable { } private MappedByteBuffer loadModelFile() throws IOException { - AssetFileDescriptor fileDescriptor = context.getAssets().openFd(MODEL_PATH); - FileInputStream inputStream = new FileInputStream(fileDescriptor.getFileDescriptor()); - try { + try (AssetFileDescriptor fileDescriptor = context.getAssets().openFd(MODEL_PATH); + FileInputStream inputStream = new FileInputStream(fileDescriptor.getFileDescriptor())) { FileChannel fileChannel = inputStream.getChannel(); long startOffset = fileDescriptor.getStartOffset(); long declaredLength = fileDescriptor.getDeclaredLength(); return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength); - } finally { - inputStream.close(); } } private String[] loadBackoffList() throws IOException { List labelList = new ArrayList(); - BufferedReader reader = - new BufferedReader(new InputStreamReader(context.getAssets().open(BACKOFF_PATH))); - String line; - while ((line = reader.readLine()) != null) { - if (!line.isEmpty()) { - labelList.add(line); + try (BufferedReader reader = + new BufferedReader(new InputStreamReader(context.getAssets().open(BACKOFF_PATH)))) { + String line; + while ((line = reader.readLine()) != null) { + if (!line.isEmpty()) { + labelList.add(line); + } } } - reader.close(); String[] ans = new String[labelList.size()]; labelList.toArray(ans); return ans; diff --git a/tensorflow/lite/models/smartreply/ops/normalize.cc b/tensorflow/lite/models/smartreply/ops/normalize.cc index 8480260f27..3cb11cc055 100644 --- a/tensorflow/lite/models/smartreply/ops/normalize.cc +++ b/tensorflow/lite/models/smartreply/ops/normalize.cc @@ -92,7 +92,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { tflite::DynamicBuffer buf; buf.AddString(result.data(), result.length()); - buf.WriteToTensor(GetOutput(context, node, 0)); + buf.WriteToTensorAsVector(GetOutput(context, node, 0)); return kTfLiteOk; } diff --git a/tensorflow/lite/models/smartreply/predictor.cc b/tensorflow/lite/models/smartreply/predictor.cc index 7db2502977..59bf4a3cf1 100644 --- a/tensorflow/lite/models/smartreply/predictor.cc +++ b/tensorflow/lite/models/smartreply/predictor.cc @@ -49,7 +49,7 @@ void ExecuteTfLite(const std::string& sentence, TfLiteTensor* input = interpreter->tensor(interpreter->inputs()[0]); tflite::DynamicBuffer buf; buf.AddString(sentence.data(), sentence.length()); - buf.WriteToTensor(input); + buf.WriteToTensorAsVector(input); interpreter->AllocateTensors(); interpreter->Invoke(); diff --git a/tensorflow/lite/nnapi_delegate.cc b/tensorflow/lite/nnapi_delegate.cc index 950bdb3942..26d75696a1 100644 --- a/tensorflow/lite/nnapi_delegate.cc +++ b/tensorflow/lite/nnapi_delegate.cc @@ -140,13 +140,13 @@ NNAPIDelegate::~NNAPIDelegate() { // ANeuralNetworksShutdown(); } -// Adds the tensors of the interpreter to the NN API model. -TfLiteStatus addTensorOperands(tflite::Interpreter* interpreter, +// Adds the tensors of the subgraph to the NN API model. +TfLiteStatus addTensorOperands(tflite::Subgraph* subgraph, ANeuralNetworksModel* nn_model, uint32_t* no_of_operands_added, std::vector* nnapi_ids) { uint32_t next_id = 0; - for (size_t i = 0; i < interpreter->tensors_size(); i++) { + for (size_t i = 0; i < subgraph->tensors_size(); i++) { // Skip temporaries and RNN back-edges. if ((*nnapi_ids)[i] == kOperandNotNeeded) continue; @@ -156,7 +156,7 @@ TfLiteStatus addTensorOperands(tflite::Interpreter* interpreter, // NNAPI requires 32-bit float scale to be zero, tflite doesn't care float scale = 0.0f; int32_t zeroPoint = 0; - TfLiteTensor* tensor = interpreter->tensor(i); + TfLiteTensor* tensor = subgraph->tensor(i); switch (tensor->type) { case kTfLiteNoType: // Tensors added during initialization of Ops don't have a type yet and @@ -240,12 +240,12 @@ void MapAndAddTensorIds(const int* from_ids_buf, size_t from_ids_count, // Adds the operations and their parameters to the NN API model. // 'next-id' is the operand ID of the next operand of the model. TfLiteStatus AddOpsAndParams( - tflite::Interpreter* interpreter, ANeuralNetworksModel* nn_model, + tflite::Subgraph* subgraph, ANeuralNetworksModel* nn_model, uint32_t next_id, std::vector* model_state_inputs, std::vector* model_state_outputs, const std::vector& tensor_id_to_nnapi_id) { - for (size_t i = 0; i < interpreter->nodes_size(); i++) { - const auto* node_and_registration = interpreter->node_and_registration(i); + for (size_t i = 0; i < subgraph->nodes_size(); i++) { + const auto* node_and_registration = subgraph->node_and_registration(i); const TfLiteNode& node = node_and_registration->first; const TfLiteRegistration& registration = node_and_registration->second; tflite::BuiltinOperator builtin = @@ -291,9 +291,9 @@ TfLiteStatus AddOpsAndParams( // For each state_out tensor, a corresponding state_in operand needs to be // created for NNAPI. auto duplicate_state_tensor_float32 = - [interpreter, &nn_model, &next_id, &augmented_inputs, - &model_state_inputs, &model_state_outputs](int tensor_id) { - const TfLiteTensor* tensor = interpreter->tensor(tensor_id); + [subgraph, &nn_model, &next_id, &augmented_inputs, &model_state_inputs, + &model_state_outputs](int tensor_id) { + const TfLiteTensor* tensor = subgraph->tensor(tensor_id); ANeuralNetworksOperandType operand_type{ ANEURALNETWORKS_TENSOR_FLOAT32, static_cast(tensor->dims->size), @@ -388,11 +388,11 @@ TfLiteStatus AddOpsAndParams( }; // LSTM in NNAPI requires scratch tensor as an output operand. - auto add_lstm_scratch_tensor_float32 = [interpreter, &node, &nn_model, + auto add_lstm_scratch_tensor_float32 = [subgraph, &node, &nn_model, &next_id, &augmented_outputs]() { if (node.temporaries->size == 0) return; int scratch_buffer_index = node.temporaries->data[0]; - const TfLiteTensor* tensor = interpreter->tensor(scratch_buffer_index); + const TfLiteTensor* tensor = subgraph->tensor(scratch_buffer_index); ANeuralNetworksOperandType operand_type{ ANEURALNETWORKS_TENSOR_FLOAT32, static_cast(tensor->dims->size), @@ -584,7 +584,7 @@ TfLiteStatus AddOpsAndParams( // The permutation input tensor value dictates the output dimensions. // TODO(b/110888333): Support dynamically-sized tensors in delegates. if ((node.inputs->size > 1) && - (interpreter->tensor(node.inputs->data[1])->allocation_type != + (subgraph->tensor(node.inputs->data[1])->allocation_type != kTfLiteMmapRo)) { logError("NNAPI does not yet support dynamic tensors."); return kTfLiteError; @@ -601,14 +601,13 @@ TfLiteStatus AddOpsAndParams( return kTfLiteError; } if ((node.inputs->size > 0) && - (interpreter->tensor(node.inputs->data[0])->dims->size != 4)) { + (subgraph->tensor(node.inputs->data[0])->dims->size != 4)) { logError("NNAPI only supports input rank 4 for L2Normalization"); return kTfLiteError; } break; case tflite::BuiltinOperator_HASHTABLE_LOOKUP: - if (interpreter->tensor(node.outputs->data[0])->type != - kTfLiteFloat32) { + if (subgraph->tensor(node.outputs->data[0])->type != kTfLiteFloat32) { logError("NNAPI only support HASHTABLE_LOOKUP with float32 output", builtin); return kTfLiteError; @@ -682,6 +681,11 @@ TfLiteStatus AddOpsAndParams( case tflite::BuiltinOperator_FILL: case tflite::BuiltinOperator_FLOOR_MOD: case tflite::BuiltinOperator_RANGE: + case tflite::BuiltinOperator_LEAKY_RELU: + case tflite::BuiltinOperator_SQUARED_DIFFERENCE: + case tflite::BuiltinOperator_MIRROR_PAD: + case tflite::BuiltinOperator_ABS: + case tflite::BuiltinOperator_SPLIT_V: logError("Op code %d is currently not delegated to NNAPI", builtin); return kTfLiteError; break; @@ -706,7 +710,7 @@ TfLiteStatus AddOpsAndParams( return kTfLiteOk; } -TfLiteStatus NNAPIDelegate::BuildGraph(Interpreter* interpreter) { +TfLiteStatus NNAPIDelegate::BuildGraph(Subgraph* subgraph) { if (nn_model_ && nn_compiled_model_) return model_status_; // TODO(aselle): This is not correct. need to handle resize invalidation. @@ -718,7 +722,7 @@ TfLiteStatus NNAPIDelegate::BuildGraph(Interpreter* interpreter) { // inputs and outputs and mark the mapping in tensor_id_to_nnapi_id with // kOperandIdNotSet. addTensorOperands will replace those with the // corresponding NNAPI operand ids and skip kOperandNotNeeded entries. - std::vector tensor_id_to_nnapi_id(interpreter->tensors_size(), + std::vector tensor_id_to_nnapi_id(subgraph->tensors_size(), kOperandNotNeeded); auto set_ids_to_not_set = [&tensor_id_to_nnapi_id](const int* buf, size_t count) { @@ -729,35 +733,31 @@ TfLiteStatus NNAPIDelegate::BuildGraph(Interpreter* interpreter) { } } }; - for (size_t i = 0; i < interpreter->nodes_size(); i++) { - const auto* node_and_registration = interpreter->node_and_registration(i); + for (size_t i = 0; i < subgraph->nodes_size(); i++) { + const auto* node_and_registration = subgraph->node_and_registration(i); const TfLiteNode& node = node_and_registration->first; set_ids_to_not_set(node.inputs->data, node.inputs->size); set_ids_to_not_set(node.outputs->data, node.outputs->size); } - set_ids_to_not_set(interpreter->inputs().data(), - interpreter->inputs().size()); - set_ids_to_not_set(interpreter->outputs().data(), - interpreter->outputs().size()); + set_ids_to_not_set(subgraph->inputs().data(), subgraph->inputs().size()); + set_ids_to_not_set(subgraph->outputs().data(), subgraph->outputs().size()); uint32_t next_id = 0; RETURN_ERROR_IF_TFLITE_FAILED(addTensorOperands( - interpreter, nn_model_, &next_id, &tensor_id_to_nnapi_id)); + subgraph, nn_model_, &next_id, &tensor_id_to_nnapi_id)); RETURN_ERROR_IF_TFLITE_FAILED( - AddOpsAndParams(interpreter, nn_model_, next_id, &model_states_inputs_, + AddOpsAndParams(subgraph, nn_model_, next_id, &model_states_inputs_, &model_states_outputs_, tensor_id_to_nnapi_id)); std::vector augmented_inputs; - MapAndAddTensorIds(interpreter->inputs().data(), - interpreter->inputs().size(), &augmented_inputs, - tensor_id_to_nnapi_id); + MapAndAddTensorIds(subgraph->inputs().data(), subgraph->inputs().size(), + &augmented_inputs, tensor_id_to_nnapi_id); augmented_inputs.insert(augmented_inputs.end(), model_states_inputs_.begin(), model_states_inputs_.end()); std::vector augmented_outputs; - MapAndAddTensorIds(interpreter->outputs().data(), - interpreter->outputs().size(), &augmented_outputs, - tensor_id_to_nnapi_id); + MapAndAddTensorIds(subgraph->outputs().data(), subgraph->outputs().size(), + &augmented_outputs, tensor_id_to_nnapi_id); MapAndAddTensorIds(model_states_outputs_.data(), model_states_outputs_.size(), &augmented_outputs, tensor_id_to_nnapi_id); @@ -770,7 +770,7 @@ TfLiteStatus NNAPIDelegate::BuildGraph(Interpreter* interpreter) { if (GetAndroidSdkVersionCached() >= 28) { CHECK_NN(ANeuralNetworksModel_relaxComputationFloat32toFloat16( - nn_model_, interpreter->GetAllowFp16PrecisionForFp32())); + nn_model_, subgraph->GetAllowFp16PrecisionForFp32())); } CHECK_NN(ANeuralNetworksModel_finish(nn_model_)); } @@ -781,9 +781,9 @@ TfLiteStatus NNAPIDelegate::BuildGraph(Interpreter* interpreter) { return kTfLiteOk; } -TfLiteStatus NNAPIDelegate::Invoke(Interpreter* interpreter) { +TfLiteStatus NNAPIDelegate::Invoke(Subgraph* subgraph) { if (!nn_model_) { - model_status_ = BuildGraph(interpreter); + model_status_ = BuildGraph(subgraph); if (model_status_ != kTfLiteOk) { logError("Failed to build graph for NNAPI"); } @@ -796,19 +796,19 @@ TfLiteStatus NNAPIDelegate::Invoke(Interpreter* interpreter) { CHECK_NN(ANeuralNetworksExecution_create(nn_compiled_model_, &execution)); // Currently perform deep copy of input buffer - for (size_t i = 0; i < interpreter->inputs().size(); i++) { - int input = interpreter->inputs()[i]; + for (size_t i = 0; i < subgraph->inputs().size(); i++) { + int input = subgraph->inputs()[i]; // TODO(aselle): Is this what we want or do we want input instead? // TODO(aselle): This should be called setInputValue maybe to be cons. - TfLiteTensor* tensor = interpreter->tensor(input); + TfLiteTensor* tensor = subgraph->tensor(input); CHECK_NN(ANeuralNetworksExecution_setInput( execution, i, nullptr, tensor->data.raw, tensor->bytes)); } // Tell nn api where to place final data. - for (size_t i = 0; i < interpreter->outputs().size(); i++) { - int output = interpreter->outputs()[i]; - TfLiteTensor* tensor = interpreter->tensor(output); + for (size_t i = 0; i < subgraph->outputs().size(); i++) { + int output = subgraph->outputs()[i]; + TfLiteTensor* tensor = subgraph->tensor(output); CHECK_NN(ANeuralNetworksExecution_setOutput( execution, i, nullptr, tensor->data.raw, tensor->bytes)); } @@ -817,16 +817,16 @@ TfLiteStatus NNAPIDelegate::Invoke(Interpreter* interpreter) { // current invocation. for (size_t i = 0; i < model_states_outputs_.size(); i++) { int state_tensor_idx = model_states_outputs_[i]; - TfLiteTensor* tensor = interpreter->tensor(state_tensor_idx); + TfLiteTensor* tensor = subgraph->tensor(state_tensor_idx); // Here we are using a deep copy for state_in tensors so that we are not // reading and writing into the same buffer during a invocation. // TODO(miaowang): using double shared buffer to minimize the copies. CHECK_NN(ANeuralNetworksExecution_setInput( - execution, i + interpreter->inputs().size(), nullptr, tensor->data.raw, + execution, i + subgraph->inputs().size(), nullptr, tensor->data.raw, tensor->bytes)); // Tell NNAPI where to output the state_out. CHECK_NN(ANeuralNetworksExecution_setOutput( - execution, i + interpreter->outputs().size(), nullptr, tensor->data.raw, + execution, i + subgraph->outputs().size(), nullptr, tensor->data.raw, tensor->bytes)); } @@ -839,9 +839,9 @@ TfLiteStatus NNAPIDelegate::Invoke(Interpreter* interpreter) { #if 0 printf("From the NN API:\n"); - TfLiteTensor* tensor = interpreter->tensor(interpreter->outputs()[0]); + TfLiteTensor* tensor = subgraph->tensor(subgraph->outputs()[0]); if (float* data = - interpreter->typed_tensor(interpreter->outputs()[0])) { + subgraph->typed_tensor(subgraph->outputs()[0])) { size_t num = tensor->bytes / sizeof(float); for (float* p = data; p < data + num; p++) { printf(" %f", *p); diff --git a/tensorflow/lite/nnapi_delegate.h b/tensorflow/lite/nnapi_delegate.h index 63b408c141..b4f8e4ecf3 100644 --- a/tensorflow/lite/nnapi_delegate.h +++ b/tensorflow/lite/nnapi_delegate.h @@ -18,6 +18,7 @@ limitations under the License. #include "tensorflow/lite/allocation.h" #include "tensorflow/lite/c/c_api_internal.h" #include "tensorflow/lite/core/api/error_reporter.h" +#include "tensorflow/lite/core/subgraph.h" #include "tensorflow/lite/interpreter.h" class ANeuralNetworksModel; @@ -50,10 +51,10 @@ class NNAPIDelegate { ~NNAPIDelegate(); // Convert a tflite graph to NNAPI - TfLiteStatus BuildGraph(Interpreter* interpreter); + TfLiteStatus BuildGraph(Subgraph* subgraph); // Run - TfLiteStatus Invoke(Interpreter* interpreter); + TfLiteStatus Invoke(Subgraph* subgraph); // Whether the current platform supports NNAPI delegation. static bool IsSupported(); diff --git a/tensorflow/lite/nnapi_delegate_disabled.cc b/tensorflow/lite/nnapi_delegate_disabled.cc index 44dc21f1b6..a8f2c0bfe3 100644 --- a/tensorflow/lite/nnapi_delegate_disabled.cc +++ b/tensorflow/lite/nnapi_delegate_disabled.cc @@ -35,13 +35,11 @@ NNAPIDelegate::~NNAPIDelegate() { #undef UNUSED_MEMBER } -TfLiteStatus NNAPIDelegate::BuildGraph(Interpreter* interpreter) { +TfLiteStatus NNAPIDelegate::BuildGraph(Subgraph* subgraph) { return kTfLiteError; } -TfLiteStatus NNAPIDelegate::Invoke(Interpreter* interpreter) { - return kTfLiteError; -} +TfLiteStatus NNAPIDelegate::Invoke(Subgraph* subgraph) { return kTfLiteError; } bool NNAPIDelegate::IsSupported() { return false; } diff --git a/tensorflow/lite/optional_debug_tools.cc b/tensorflow/lite/optional_debug_tools.cc index 5ee1cf6d33..1113bf01b1 100644 --- a/tensorflow/lite/optional_debug_tools.cc +++ b/tensorflow/lite/optional_debug_tools.cc @@ -44,6 +44,8 @@ const char* TensorTypeName(TfLiteType type) { return "kTfLiteInt32"; case kTfLiteUInt8: return "kTfLiteUInt8"; + case kTfLiteInt8: + return "kTfLiteInt8"; case kTfLiteInt64: return "kTfLiteInt64"; case kTfLiteString: diff --git a/tensorflow/lite/python/BUILD b/tensorflow/lite/python/BUILD index 017dd72f78..acf827892b 100644 --- a/tensorflow/lite/python/BUILD +++ b/tensorflow/lite/python/BUILD @@ -89,6 +89,7 @@ py_library( srcs_version = "PY2AND3", deps = [ "//tensorflow/lite/toco:toco_flags_proto_py", + "//tensorflow/python:dtypes", ], ) @@ -103,6 +104,7 @@ py_library( "//tensorflow/lite/toco:toco_flags_proto_py", "//tensorflow/lite/toco/python:tensorflow_wrap_toco", "//tensorflow/lite/toco/python:toco_from_protos", + "//tensorflow/python:dtypes", "//tensorflow/python:platform", ], ) diff --git a/tensorflow/lite/python/convert.py b/tensorflow/lite/python/convert.py index 9991fb2a73..563312e027 100644 --- a/tensorflow/lite/python/convert.py +++ b/tensorflow/lite/python/convert.py @@ -28,6 +28,8 @@ import tempfile as _tempfile from tensorflow.lite.python import lite_constants from tensorflow.lite.toco import model_flags_pb2 as _model_flags_pb2 from tensorflow.lite.toco import toco_flags_pb2 as _toco_flags_pb2 +from tensorflow.lite.toco import types_pb2 as _types_pb2 +from tensorflow.python.framework import dtypes from tensorflow.python.platform import resource_loader as _resource_loader from tensorflow.python.util import deprecation from tensorflow.python.util.lazy_loader import LazyLoader @@ -53,6 +55,18 @@ else: if _toco_from_proto_bin and not _os.path.exists(_toco_from_proto_bin): _toco_from_proto_bin = "toco_from_protos" + +# Map of tf.dtypes to TFLite types_flag_pb2. +_MAP_TF_TO_TFLITE_TYPES = { + dtypes.float32: _types_pb2.FLOAT, + dtypes.int32: _types_pb2.INT32, + dtypes.int64: _types_pb2.INT64, + dtypes.string: _types_pb2.STRING, + dtypes.uint8: _types_pb2.QUANTIZED_UINT8, + dtypes.complex64: _types_pb2.COMPLEX64 +} + + def _try_convert_to_unicode(output): if output is None: return u"" @@ -65,6 +79,24 @@ def _try_convert_to_unicode(output): return output +def convert_dtype_to_tflite_type(tf_dtype): + """Converts tf.dtype to TFLite proto type. + + Args: + tf_dtype: tf.dtype + + Raises: + ValueError: Unsupported tf.dtype. + + Returns: + types_flag_pb2. + """ + result = _MAP_TF_TO_TFLITE_TYPES.get(tf_dtype) + if result is None: + raise ValueError("Unsupported tf.dtype {0}".format(tf_dtype)) + return result + + class OpsSet(enum.Enum): """Enum class defining the sets of ops available to generate TFLite models. @@ -214,10 +246,10 @@ def build_toco_convert_protos(input_tensors, `foo.get_shape()` and `foo.dtype`. output_tensors: List of output tensors (only .name is used from this). inference_type: Target data type of real-number arrays in the output file. - Must be `{FLOAT, QUANTIZED_UINT8}`. (default FLOAT) + Must be `{tf.float32, tf.uint8}`. (default tf.float32) inference_input_type: Target data type of real-number input arrays. Allows for a different type for input arrays in the case of quantization. - Must be `{FLOAT, QUANTIZED_UINT8}`. (default `inference_type`) + Must be `{tf.float32, tf.uint8}`. (default `inference_type`) input_format: Type of data to read Currently must be `{TENSORFLOW_GRAPHDEF}`. (default TENSORFLOW_GRAPHDEF) input_shapes: Input array shape. It needs to be a list of the same length @@ -269,16 +301,19 @@ def build_toco_convert_protos(input_tensors, process. Raises: - ValueError: If the input tensor type is unknown + ValueError: + If the input tensor type is unknown + Missing mean_values or std_dev_values RuntimeError: If TOCO fails to convert (in which case the runtime error's error text will contain the TOCO error log) """ toco = _toco_flags_pb2.TocoFlags() toco.input_format = input_format toco.output_format = output_format - toco.inference_type = inference_type + toco.inference_type = convert_dtype_to_tflite_type(inference_type) if inference_input_type: - toco.inference_input_type = inference_input_type + toco.inference_input_type = convert_dtype_to_tflite_type( + inference_input_type) else: toco.inference_input_type = toco.inference_type toco.drop_control_dependency = drop_control_dependency @@ -302,9 +337,14 @@ def build_toco_convert_protos(input_tensors, model.change_concat_input_ranges = change_concat_input_ranges for idx, input_tensor in enumerate(input_tensors): input_array = model.input_arrays.add() - if toco.inference_input_type == lite_constants.QUANTIZED_UINT8: - input_array.mean_value, input_array.std_value = quantized_input_stats[idx] input_array.name = tensor_name(input_tensor) + input_array.data_type = convert_dtype_to_tflite_type(input_tensor.dtype) + + if toco.inference_input_type == _types_pb2.QUANTIZED_UINT8: + if not quantized_input_stats: + raise ValueError("std_dev and mean must be defined when " + "inference_input_type is QUANTIZED_UINT8.") + input_array.mean_value, input_array.std_value = quantized_input_stats[idx] if input_shapes is None: shape = input_tensor.get_shape() else: @@ -352,7 +392,11 @@ def toco_convert_graph_def(input_data, input_arrays_with_shape, output_arrays, for idx, (name, shape) in enumerate(input_arrays_with_shape): input_array = model_flags.input_arrays.add() - if kwargs["inference_type"] == lite_constants.QUANTIZED_UINT8: + if toco_flags.inference_input_type == _types_pb2.QUANTIZED_UINT8: + if (("quantized_input_stats" not in kwargs) or + (not kwargs["quantized_input_stats"])): + raise ValueError("std_dev and mean must be defined when " + "inference_input_type is QUANTIZED_UINT8.") input_array.mean_value, input_array.std_value = kwargs[ "quantized_input_stats"][idx] input_array.name = name diff --git a/tensorflow/lite/python/convert_saved_model.py b/tensorflow/lite/python/convert_saved_model.py index 3f54d2559c..f8d986b746 100644 --- a/tensorflow/lite/python/convert_saved_model.py +++ b/tensorflow/lite/python/convert_saved_model.py @@ -197,12 +197,27 @@ def set_tensor_shapes(tensors, shapes): tensors: TensorFlow ops.Tensor. shapes: Dict of strings representing input tensor names to list of integers representing input shapes (e.g., {"foo": : [1, 16, 16, 3]}). + + Raises: + ValueError: + `shapes` contains an invalid tensor. + `shapes` contains an invalid shape for a valid tensor. """ if shapes: - for tensor in tensors: - shape = shapes.get(tensor_name(tensor)) + tensor_names_to_tensor = {tensor_name(tensor): tensor for tensor in tensors} + for name, shape in shapes.items(): + if name not in tensor_names_to_tensor: + raise ValueError("Invalid tensor \'{}\' found in tensor shapes " + "map.".format(name)) if shape is not None: - tensor.set_shape(shape) + tensor = tensor_names_to_tensor[name] + try: + tensor.set_shape(shape) + except ValueError as error: + message = ("The shape of tensor '{0}' cannot be changed from {1} to " + "{2}. {3}".format(name, tensor.get_shape(), shape, + str(error))) + raise ValueError(message) def freeze_saved_model(saved_model_dir, input_arrays, input_shapes, diff --git a/tensorflow/lite/python/convert_saved_model_test.py b/tensorflow/lite/python/convert_saved_model_test.py index dff582f1a1..76113853ca 100644 --- a/tensorflow/lite/python/convert_saved_model_test.py +++ b/tensorflow/lite/python/convert_saved_model_test.py @@ -75,12 +75,30 @@ class TensorFunctionsTest(test_util.TensorFlowTestCase): convert_saved_model.set_tensor_shapes([tensor], {"Placeholder": [1, 3, 5]}) self.assertEqual([1, 3, 5], tensor.shape.as_list()) - def testSetTensorShapeInvalid(self): + def testSetTensorShapeArrayInvalid(self): + # Tests set_tensor_shape where the tensor name passed in doesn't exist. tensor = array_ops.placeholder(shape=[None, 3, 5], dtype=dtypes.float32) self.assertEqual([None, 3, 5], tensor.shape.as_list()) - convert_saved_model.set_tensor_shapes([tensor], - {"invalid-input": [5, 3, 5]}) + with self.assertRaises(ValueError) as error: + convert_saved_model.set_tensor_shapes([tensor], + {"invalid-input": [5, 3, 5]}) + self.assertEqual( + "Invalid tensor 'invalid-input' found in tensor shapes map.", + str(error.exception)) + self.assertEqual([None, 3, 5], tensor.shape.as_list()) + + def testSetTensorShapeDimensionInvalid(self): + # Tests set_tensor_shape where the shape passed in is incompatiable. + tensor = array_ops.placeholder(shape=[None, 3, 5], dtype=dtypes.float32) + self.assertEqual([None, 3, 5], tensor.shape.as_list()) + + with self.assertRaises(ValueError) as error: + convert_saved_model.set_tensor_shapes([tensor], + {"Placeholder": [1, 5, 5]}) + self.assertIn( + "The shape of tensor 'Placeholder' cannot be changed from " + "(?, 3, 5) to [1, 5, 5].", str(error.exception)) self.assertEqual([None, 3, 5], tensor.shape.as_list()) def testSetTensorShapeEmpty(self): diff --git a/tensorflow/lite/python/convert_test.py b/tensorflow/lite/python/convert_test.py index 7a0bce921b..2a6f1f634f 100644 --- a/tensorflow/lite/python/convert_test.py +++ b/tensorflow/lite/python/convert_test.py @@ -23,6 +23,7 @@ from tensorflow.lite.python import convert from tensorflow.lite.python import lite_constants from tensorflow.lite.python import op_hint from tensorflow.lite.python.interpreter import Interpreter +from tensorflow.lite.toco import types_pb2 as _types_pb2 from tensorflow.python.client import session from tensorflow.python.framework import dtypes from tensorflow.python.framework import test_util @@ -65,6 +66,21 @@ class ConvertTest(test_util.TensorFlowTestCase): quantized_input_stats=[(0., 1.)]) self.assertTrue(tflite_model) + def testQuantizationInvalid(self): + in_tensor = array_ops.placeholder( + shape=[1, 16, 16, 3], dtype=dtypes.float32) + out_tensor = array_ops.fake_quant_with_min_max_args( + in_tensor + in_tensor, min=0., max=1.) + sess = session.Session() + + with self.assertRaises(ValueError) as error: + convert.toco_convert( + sess.graph_def, [in_tensor], [out_tensor], + inference_type=lite_constants.QUANTIZED_UINT8) + self.assertEqual( + "std_dev and mean must be defined when inference_input_type is " + "QUANTIZED_UINT8.", str(error.exception)) + def testGraphDefBasic(self): in_tensor = array_ops.placeholder( shape=[1, 16, 16, 3], dtype=dtypes.float32, name="input") @@ -138,6 +154,27 @@ class ConvertTest(test_util.TensorFlowTestCase): self.assertTrue(([1, 16, 16, 3] == output_details[0]["shape"]).all()) self.assertTrue(output_details[0]["quantization"][0] > 0) # scale + def testGraphDefQuantizationInvalid(self): + in_tensor_1 = array_ops.placeholder( + shape=[1, 16, 16, 3], dtype=dtypes.float32, name="inputA") + in_tensor_2 = array_ops.placeholder( + shape=[1, 16, 16, 3], dtype=dtypes.float32, name="inputB") + _ = array_ops.fake_quant_with_min_max_args( + in_tensor_1 + in_tensor_2, min=0., max=1., name="output") + sess = session.Session() + + input_arrays_map = [("inputA", [1, 16, 16, 3]), ("inputB", [1, 16, 16, 3])] + output_arrays = ["output"] + with self.assertRaises(ValueError) as error: + convert.toco_convert_graph_def( + sess.graph_def, + input_arrays_map, + output_arrays, + inference_type=lite_constants.QUANTIZED_UINT8) + self.assertEqual( + "std_dev and mean must be defined when inference_input_type is " + "QUANTIZED_UINT8.", str(error.exception)) + class ConvertTestOpHint(test_util.TensorFlowTestCase): """Test the hint to stub functionality.""" @@ -329,6 +366,27 @@ class ConvertTestOpHint(test_util.TensorFlowTestCase): output_nodes=[op_hint._tensor_name_base(output.name)]), set(["agg", "Const", "Identity"])) + def testConvertDtype(self): + self.assertEqual( + convert.convert_dtype_to_tflite_type(lite_constants.FLOAT), + _types_pb2.FLOAT) + self.assertEqual( + convert.convert_dtype_to_tflite_type(dtypes.float32), _types_pb2.FLOAT) + self.assertEqual( + convert.convert_dtype_to_tflite_type(dtypes.int32), _types_pb2.INT32) + self.assertEqual( + convert.convert_dtype_to_tflite_type(dtypes.int64), _types_pb2.INT64) + self.assertEqual( + convert.convert_dtype_to_tflite_type(dtypes.string), _types_pb2.STRING) + self.assertEqual( + convert.convert_dtype_to_tflite_type(dtypes.uint8), + _types_pb2.QUANTIZED_UINT8) + self.assertEqual( + convert.convert_dtype_to_tflite_type(dtypes.complex64), + _types_pb2.COMPLEX64) + with self.assertRaises(ValueError): + convert.convert_dtype_to_tflite_type(dtypes.bool) + if __name__ == "__main__": test.main() diff --git a/tensorflow/lite/python/interpreter_wrapper/interpreter_wrapper.cc b/tensorflow/lite/python/interpreter_wrapper/interpreter_wrapper.cc index e71752fe63..d14af439ec 100644 --- a/tensorflow/lite/python/interpreter_wrapper/interpreter_wrapper.cc +++ b/tensorflow/lite/python/interpreter_wrapper/interpreter_wrapper.cc @@ -124,6 +124,8 @@ int TfLiteTypeToPyArrayType(TfLiteType tf_lite_type) { return NPY_INT16; case kTfLiteUInt8: return NPY_UINT8; + case kTfLiteInt8: + return NPY_INT8; case kTfLiteInt64: return NPY_INT64; case kTfLiteString: @@ -150,6 +152,8 @@ TfLiteType TfLiteTypeFromPyArray(PyArrayObject* array) { return kTfLiteInt16; case NPY_UINT8: return kTfLiteUInt8; + case NPY_INT8: + return kTfLiteInt8; case NPY_INT64: return kTfLiteInt64; case NPY_BOOL: diff --git a/tensorflow/lite/python/lite.py b/tensorflow/lite/python/lite.py index 5810553da2..1b20ff2f92 100644 --- a/tensorflow/lite/python/lite.py +++ b/tensorflow/lite/python/lite.py @@ -25,8 +25,6 @@ EXPERIMENTAL: APIs here are unstable and likely to change without notice. @@convert_op_hints_to_stubs @@build_toco_convert_protos -@@FLOAT -@@QUANTIZED_UINT8 @@TFLITE @@GRAPHVIZ_DOT @@ -78,10 +76,10 @@ class TFLiteConverter(object): Attributes: inference_type: Target data type of real-number arrays in the output file. - Must be `{FLOAT, QUANTIZED_UINT8}`. (default FLOAT) + Must be `{tf.float32, tf.uint8}`. (default tf.float32) inference_input_type: Target data type of real-number input arrays. Allows for a different type for input arrays in the case of quantization. - Must be `{FLOAT, QUANTIZED_UINT8}`. (default `inference_type`) + Must be `{tf.float32, tf.uint8}`. (default `inference_type`) output_format: Output file format. Currently must be `{TFLITE, GRAPHVIZ_DOT}`. (default TFLITE) quantized_input_stats: Dict of strings representing input tensor names @@ -402,15 +400,16 @@ class TFLiteConverter(object): # Checks dimensions in input tensor. if self._has_valid_tensors(): for tensor in self._input_tensors: - if not tensor.get_shape(): + shape = tensor.get_shape() + if not shape or not shape.as_list(): raise ValueError("Provide an input shape for input array " "'{0}'.".format(_tensor_name(tensor))) - shape = tensor.get_shape().as_list() - if None in shape[1:]: + shape_list = shape.as_list() + if None in shape_list[1:]: raise ValueError( "None is only supported in the 1st dimension. Tensor '{0}' has " - "invalid shape '{1}'.".format(_tensor_name(tensor), shape)) - elif shape[0] is None: + "invalid shape '{1}'.".format(_tensor_name(tensor), shape_list)) + elif shape_list[0] is None: self._set_batch_size(batch_size=1) # Get quantization stats. Ensures there is one stat per name if the stats diff --git a/tensorflow/lite/python/lite_constants.py b/tensorflow/lite/python/lite_constants.py index fdefc5e6cf..f5d6d10379 100644 --- a/tensorflow/lite/python/lite_constants.py +++ b/tensorflow/lite/python/lite_constants.py @@ -19,26 +19,25 @@ from __future__ import division from __future__ import print_function from tensorflow.lite.toco import toco_flags_pb2 as _toco_flags_pb2 -from tensorflow.lite.toco import types_pb2 as _types_pb2 +from tensorflow.python.framework import dtypes from tensorflow.python.util.all_util import remove_undocumented from tensorflow.python.util.tf_export import tf_export as _tf_export -# Enum types from the protobuf promoted to the API -FLOAT = _types_pb2.FLOAT -INT32 = _types_pb2.INT32 -INT64 = _types_pb2.INT64 -STRING = _types_pb2.STRING -QUANTIZED_UINT8 = _types_pb2.QUANTIZED_UINT8 -COMPLEX64 = _types_pb2.COMPLEX64 +FLOAT = dtypes.float32 +INT32 = dtypes.int32 +INT64 = dtypes.int64 +STRING = dtypes.string +QUANTIZED_UINT8 = dtypes.uint8 +COMPLEX64 = dtypes.complex64 TENSORFLOW_GRAPHDEF = _toco_flags_pb2.TENSORFLOW_GRAPHDEF TFLITE = _toco_flags_pb2.TFLITE GRAPHVIZ_DOT = _toco_flags_pb2.GRAPHVIZ_DOT -_tf_export("lite.constants.FLOAT").export_constant(__name__, "FLOAT") -_tf_export("lite.constants.INT32").export_constant(__name__, "INT32") -_tf_export("lite.constants.INT64").export_constant(__name__, "INT64") -_tf_export("lite.constants.STRING").export_constant(__name__, "STRING") -_tf_export("lite.constants.QUANTIZED_UINT8").export_constant( +_tf_export(v1=["lite.constants.FLOAT"]).export_constant(__name__, "FLOAT") +_tf_export(v1=["lite.constants.INT32"]).export_constant(__name__, "INT32") +_tf_export(v1=["lite.constants.INT64"]).export_constant(__name__, "INT64") +_tf_export(v1=["lite.constants.STRING"]).export_constant(__name__, "STRING") +_tf_export(v1=["lite.constants.QUANTIZED_UINT8"]).export_constant( __name__, "QUANTIZED_UINT8") _tf_export("lite.constants.TFLITE").export_constant(__name__, "TFLITE") _tf_export("lite.constants.GRAPHVIZ_DOT").export_constant( diff --git a/tensorflow/lite/python/lite_test.py b/tensorflow/lite/python/lite_test.py index 5a5697db92..1ae0d3c3ed 100644 --- a/tensorflow/lite/python/lite_test.py +++ b/tensorflow/lite/python/lite_test.py @@ -182,7 +182,7 @@ class FromSessionTest(test_util.TensorFlowTestCase): out_tensor = in_tensor + in_tensor sess = session.Session() - # Test invalid shape. None after 1st dimension. + # Test None as shape. converter = lite.TFLiteConverter.from_session(sess, [in_tensor], [out_tensor]) with self.assertRaises(ValueError) as error: @@ -190,7 +190,20 @@ class FromSessionTest(test_util.TensorFlowTestCase): self.assertEqual('Provide an input shape for input array \'Placeholder\'.', str(error.exception)) - def testBatchSizeInvalid(self): + def testSizeEmptyInvalid(self): + in_tensor = array_ops.placeholder(dtype=dtypes.float32, shape=[]) + out_tensor = in_tensor + in_tensor + sess = session.Session() + + # Test empty shape. + converter = lite.TFLiteConverter.from_session(sess, [in_tensor], + [out_tensor]) + with self.assertRaises(ValueError) as error: + converter.convert() + self.assertEqual('Provide an input shape for input array \'Placeholder\'.', + str(error.exception)) + + def testSizeInvalid(self): in_tensor = array_ops.placeholder( shape=[1, None, 16, 3], dtype=dtypes.float32) out_tensor = in_tensor + in_tensor @@ -931,12 +944,13 @@ class FromKerasFile(test_util.TensorFlowTestCase): """Test a Sequential tf.keras model testing input shapes argument.""" keras_file = self._getSequentialModel() - # Passing in shape of invalid input array has no impact as long as all input - # arrays have a shape. - converter = lite.TFLiteConverter.from_keras_model_file( - keras_file, input_shapes={'invalid-input': [2, 3]}) - tflite_model = converter.convert() - self.assertTrue(tflite_model) + # Passing in shape of invalid input array raises error. + with self.assertRaises(ValueError) as error: + converter = lite.TFLiteConverter.from_keras_model_file( + keras_file, input_shapes={'invalid-input': [2, 3]}) + self.assertEqual( + "Invalid tensor 'invalid-input' found in tensor shapes map.", + str(error.exception)) # Passing in shape of valid input array. converter = lite.TFLiteConverter.from_keras_model_file( diff --git a/tensorflow/lite/python/op_hint.py b/tensorflow/lite/python/op_hint.py index 3afce1baf2..8d7f9316bf 100644 --- a/tensorflow/lite/python/op_hint.py +++ b/tensorflow/lite/python/op_hint.py @@ -104,9 +104,9 @@ class OpHint(object): that make up the pseudo op. A similar process is done to any output that is to be exported from the current op. - TODO(aselle): When TensorFlow functions functionality works for arbitrary - constructs, this mechanism can be retired and changed to use python defun's. """ + # TODO(aselle): When TensorFlow functions functionality works for arbitrary + # constructs, this mechanism can be retired and changed to use python defun's. # Attr constants that are used for representation in the GraphDef. These # will be used on every Identity op that is involved in a total OpHint. @@ -403,7 +403,7 @@ class _LiteOperand(object): out_graphdef: A graphdef that is ready to have this input added. Returns: - The the output that the stub should use as an input for this operand. + The output that the stub should use as an input for this operand. Raises: RuntimeError: if the method is not implemented. diff --git a/tensorflow/lite/python/tflite_convert.py b/tensorflow/lite/python/tflite_convert.py index 00ea6d722e..341b539bea 100644 --- a/tensorflow/lite/python/tflite_convert.py +++ b/tensorflow/lite/python/tflite_convert.py @@ -25,7 +25,6 @@ import sys from tensorflow.lite.python import lite from tensorflow.lite.python import lite_constants from tensorflow.lite.toco import toco_flags_pb2 as _toco_flags_pb2 -from tensorflow.lite.toco import types_pb2 as _types_pb2 from tensorflow.python.platform import app @@ -41,6 +40,27 @@ def _parse_set(values): return None +def _parse_inference_type(value, flag): + """Converts the inference type to the value of the constant. + + Args: + value: str representing the inference type. + flag: str representing the flag name. + + Returns: + tf.dtype. + + Raises: + ValueError: Unsupported value. + """ + if value == "FLOAT": + return lite_constants.FLOAT + if value == "QUANTIZED_UINT8": + return lite_constants.QUANTIZED_UINT8 + raise ValueError("Unsupported value for --{0}. Only FLOAT and " + "QUANTIZED_UINT8 are supported.".format(flag)) + + def _get_toco_converter(flags): """Makes a TFLiteConverter object based on the flags provided. @@ -101,10 +121,11 @@ def _convert_model(flags): # Create converter. converter = _get_toco_converter(flags) if flags.inference_type: - converter.inference_type = _types_pb2.IODataType.Value(flags.inference_type) + converter.inference_type = _parse_inference_type(flags.inference_type, + "inference_type") if flags.inference_input_type: - converter.inference_input_type = _types_pb2.IODataType.Value( - flags.inference_input_type) + converter.inference_input_type = _parse_inference_type( + flags.inference_input_type, "inference_input_type") if flags.output_format: converter.output_format = _toco_flags_pb2.FileFormat.Value( flags.output_format) @@ -115,7 +136,7 @@ def _convert_model(flags): # In quantized inference, mean_value has to be integer so that the real # value 0.0 is exactly representable. - if flags.inference_type == lite_constants.QUANTIZED_UINT8: + if converter.inference_type == lite_constants.QUANTIZED_UINT8: mean_values = _parse_array(flags.mean_values, type_fn=int) else: mean_values = _parse_array(flags.mean_values, type_fn=float) @@ -156,7 +177,7 @@ def _convert_model(flags): if flags.post_training_quantize: converter.post_training_quantize = flags.post_training_quantize - if flags.inference_type == lite_constants.QUANTIZED_UINT8: + if converter.inference_type == lite_constants.QUANTIZED_UINT8: print("--post_training_quantize quantizes a graph of inference_type " "FLOAT. Overriding inference type QUANTIZED_UINT8 to FLOAT.") converter.inference_type = lite_constants.FLOAT diff --git a/tensorflow/lite/schema/schema.fbs b/tensorflow/lite/schema/schema.fbs index 9b0eae74c3..6436167303 100644 --- a/tensorflow/lite/schema/schema.fbs +++ b/tensorflow/lite/schema/schema.fbs @@ -200,6 +200,11 @@ enum BuiltinOperator : byte { FLOOR_MOD = 95, RANGE = 96, RESIZE_NEAREST_NEIGHBOR = 97, + LEAKY_RELU = 98, + SQUARED_DIFFERENCE = 99, + MIRROR_PAD = 100, + ABS = 101, + SPLIT_V = 102, } // Options for the builtin operators. @@ -278,6 +283,11 @@ union BuiltinOptions { FloorModOptions, RangeOptions, ResizeNearestNeighborOptions, + LeakyReluOptions, + SquaredDifferenceOptions, + MirrorPadOptions, + AbsOptions, + SplitVOptions, } enum Padding : byte { SAME, VALID } @@ -526,6 +536,10 @@ table SplitOptions { num_splits: int; } +table SplitVOptions { + num_splits: int; +} + table StridedSliceOptions { begin_mask: int; end_mask: int; @@ -629,6 +643,10 @@ table OneHotOptions { axis:int; } +table AbsOptions { +} + + table LogicalAndOptions { } @@ -658,6 +676,24 @@ table FloorModOptions { table RangeOptions { } +table LeakyReluOptions { + alpha:float; +} + +table SquaredDifferenceOptions { +} + +enum MirrorPadMode : byte { + // Doesn't include borders. + REFLECT = 0, + // Includes borders. + SYMMETRIC = 1, +} + +table MirrorPadOptions { + mode:MirrorPadMode; +} + // An OperatorCode can be an enum value (BuiltinOperator) if the operator is a // builtin, or a string if the operator is custom. table OperatorCode { diff --git a/tensorflow/lite/schema/schema_generated.h b/tensorflow/lite/schema/schema_generated.h index b7885cfcc5..af8b143364 100755 --- a/tensorflow/lite/schema/schema_generated.h +++ b/tensorflow/lite/schema/schema_generated.h @@ -148,6 +148,9 @@ struct SqueezeOptionsT; struct SplitOptions; struct SplitOptionsT; +struct SplitVOptions; +struct SplitVOptionsT; + struct StridedSliceOptions; struct StridedSliceOptionsT; @@ -226,6 +229,9 @@ struct LogicalOrOptionsT; struct OneHotOptions; struct OneHotOptionsT; +struct AbsOptions; +struct AbsOptionsT; + struct LogicalAndOptions; struct LogicalAndOptionsT; @@ -253,6 +259,15 @@ struct FloorModOptionsT; struct RangeOptions; struct RangeOptionsT; +struct LeakyReluOptions; +struct LeakyReluOptionsT; + +struct SquaredDifferenceOptions; +struct SquaredDifferenceOptionsT; + +struct MirrorPadOptions; +struct MirrorPadOptionsT; + struct OperatorCode; struct OperatorCodeT; @@ -500,11 +515,16 @@ enum BuiltinOperator { BuiltinOperator_FLOOR_MOD = 95, BuiltinOperator_RANGE = 96, BuiltinOperator_RESIZE_NEAREST_NEIGHBOR = 97, + BuiltinOperator_LEAKY_RELU = 98, + BuiltinOperator_SQUARED_DIFFERENCE = 99, + BuiltinOperator_MIRROR_PAD = 100, + BuiltinOperator_ABS = 101, + BuiltinOperator_SPLIT_V = 102, BuiltinOperator_MIN = BuiltinOperator_ADD, - BuiltinOperator_MAX = BuiltinOperator_RESIZE_NEAREST_NEIGHBOR + BuiltinOperator_MAX = BuiltinOperator_SPLIT_V }; -inline const BuiltinOperator (&EnumValuesBuiltinOperator())[97] { +inline const BuiltinOperator (&EnumValuesBuiltinOperator())[102] { static const BuiltinOperator values[] = { BuiltinOperator_ADD, BuiltinOperator_AVERAGE_POOL_2D, @@ -602,7 +622,12 @@ inline const BuiltinOperator (&EnumValuesBuiltinOperator())[97] { BuiltinOperator_FILL, BuiltinOperator_FLOOR_MOD, BuiltinOperator_RANGE, - BuiltinOperator_RESIZE_NEAREST_NEIGHBOR + BuiltinOperator_RESIZE_NEAREST_NEIGHBOR, + BuiltinOperator_LEAKY_RELU, + BuiltinOperator_SQUARED_DIFFERENCE, + BuiltinOperator_MIRROR_PAD, + BuiltinOperator_ABS, + BuiltinOperator_SPLIT_V }; return values; } @@ -707,6 +732,11 @@ inline const char * const *EnumNamesBuiltinOperator() { "FLOOR_MOD", "RANGE", "RESIZE_NEAREST_NEIGHBOR", + "LEAKY_RELU", + "SQUARED_DIFFERENCE", + "MIRROR_PAD", + "ABS", + "SPLIT_V", nullptr }; return names; @@ -793,11 +823,16 @@ enum BuiltinOptions { BuiltinOptions_FloorModOptions = 72, BuiltinOptions_RangeOptions = 73, BuiltinOptions_ResizeNearestNeighborOptions = 74, + BuiltinOptions_LeakyReluOptions = 75, + BuiltinOptions_SquaredDifferenceOptions = 76, + BuiltinOptions_MirrorPadOptions = 77, + BuiltinOptions_AbsOptions = 78, + BuiltinOptions_SplitVOptions = 79, BuiltinOptions_MIN = BuiltinOptions_NONE, - BuiltinOptions_MAX = BuiltinOptions_ResizeNearestNeighborOptions + BuiltinOptions_MAX = BuiltinOptions_SplitVOptions }; -inline const BuiltinOptions (&EnumValuesBuiltinOptions())[75] { +inline const BuiltinOptions (&EnumValuesBuiltinOptions())[80] { static const BuiltinOptions values[] = { BuiltinOptions_NONE, BuiltinOptions_Conv2DOptions, @@ -873,7 +908,12 @@ inline const BuiltinOptions (&EnumValuesBuiltinOptions())[75] { BuiltinOptions_UnidirectionalSequenceLSTMOptions, BuiltinOptions_FloorModOptions, BuiltinOptions_RangeOptions, - BuiltinOptions_ResizeNearestNeighborOptions + BuiltinOptions_ResizeNearestNeighborOptions, + BuiltinOptions_LeakyReluOptions, + BuiltinOptions_SquaredDifferenceOptions, + BuiltinOptions_MirrorPadOptions, + BuiltinOptions_AbsOptions, + BuiltinOptions_SplitVOptions }; return values; } @@ -955,6 +995,11 @@ inline const char * const *EnumNamesBuiltinOptions() { "FloorModOptions", "RangeOptions", "ResizeNearestNeighborOptions", + "LeakyReluOptions", + "SquaredDifferenceOptions", + "MirrorPadOptions", + "AbsOptions", + "SplitVOptions", nullptr }; return names; @@ -1265,6 +1310,26 @@ template<> struct BuiltinOptionsTraits { static const BuiltinOptions enum_value = BuiltinOptions_ResizeNearestNeighborOptions; }; +template<> struct BuiltinOptionsTraits { + static const BuiltinOptions enum_value = BuiltinOptions_LeakyReluOptions; +}; + +template<> struct BuiltinOptionsTraits { + static const BuiltinOptions enum_value = BuiltinOptions_SquaredDifferenceOptions; +}; + +template<> struct BuiltinOptionsTraits { + static const BuiltinOptions enum_value = BuiltinOptions_MirrorPadOptions; +}; + +template<> struct BuiltinOptionsTraits { + static const BuiltinOptions enum_value = BuiltinOptions_AbsOptions; +}; + +template<> struct BuiltinOptionsTraits { + static const BuiltinOptions enum_value = BuiltinOptions_SplitVOptions; +}; + struct BuiltinOptionsUnion { BuiltinOptions type; void *value; @@ -1888,6 +1953,46 @@ struct BuiltinOptionsUnion { return type == BuiltinOptions_ResizeNearestNeighborOptions ? reinterpret_cast(value) : nullptr; } + LeakyReluOptionsT *AsLeakyReluOptions() { + return type == BuiltinOptions_LeakyReluOptions ? + reinterpret_cast(value) : nullptr; + } + const LeakyReluOptionsT *AsLeakyReluOptions() const { + return type == BuiltinOptions_LeakyReluOptions ? + reinterpret_cast(value) : nullptr; + } + SquaredDifferenceOptionsT *AsSquaredDifferenceOptions() { + return type == BuiltinOptions_SquaredDifferenceOptions ? + reinterpret_cast(value) : nullptr; + } + const SquaredDifferenceOptionsT *AsSquaredDifferenceOptions() const { + return type == BuiltinOptions_SquaredDifferenceOptions ? + reinterpret_cast(value) : nullptr; + } + MirrorPadOptionsT *AsMirrorPadOptions() { + return type == BuiltinOptions_MirrorPadOptions ? + reinterpret_cast(value) : nullptr; + } + const MirrorPadOptionsT *AsMirrorPadOptions() const { + return type == BuiltinOptions_MirrorPadOptions ? + reinterpret_cast(value) : nullptr; + } + AbsOptionsT *AsAbsOptions() { + return type == BuiltinOptions_AbsOptions ? + reinterpret_cast(value) : nullptr; + } + const AbsOptionsT *AsAbsOptions() const { + return type == BuiltinOptions_AbsOptions ? + reinterpret_cast(value) : nullptr; + } + SplitVOptionsT *AsSplitVOptions() { + return type == BuiltinOptions_SplitVOptions ? + reinterpret_cast(value) : nullptr; + } + const SplitVOptionsT *AsSplitVOptions() const { + return type == BuiltinOptions_SplitVOptions ? + reinterpret_cast(value) : nullptr; + } }; bool VerifyBuiltinOptions(flatbuffers::Verifier &verifier, const void *obj, BuiltinOptions type); @@ -2085,6 +2190,35 @@ inline const char *EnumNameCombinerType(CombinerType e) { return EnumNamesCombinerType()[index]; } +enum MirrorPadMode { + MirrorPadMode_REFLECT = 0, + MirrorPadMode_SYMMETRIC = 1, + MirrorPadMode_MIN = MirrorPadMode_REFLECT, + MirrorPadMode_MAX = MirrorPadMode_SYMMETRIC +}; + +inline const MirrorPadMode (&EnumValuesMirrorPadMode())[2] { + static const MirrorPadMode values[] = { + MirrorPadMode_REFLECT, + MirrorPadMode_SYMMETRIC + }; + return values; +} + +inline const char * const *EnumNamesMirrorPadMode() { + static const char * const names[] = { + "REFLECT", + "SYMMETRIC", + nullptr + }; + return names; +} + +inline const char *EnumNameMirrorPadMode(MirrorPadMode e) { + const size_t index = static_cast(e); + return EnumNamesMirrorPadMode()[index]; +} + enum CustomOptionsFormat { CustomOptionsFormat_FLEXBUFFERS = 0, CustomOptionsFormat_MIN = CustomOptionsFormat_FLEXBUFFERS, @@ -4935,6 +5069,60 @@ inline flatbuffers::Offset CreateSplitOptions( flatbuffers::Offset CreateSplitOptions(flatbuffers::FlatBufferBuilder &_fbb, const SplitOptionsT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +struct SplitVOptionsT : public flatbuffers::NativeTable { + typedef SplitVOptions TableType; + int32_t num_splits; + SplitVOptionsT() + : num_splits(0) { + } +}; + +struct SplitVOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef SplitVOptionsT NativeTableType; + enum { + VT_NUM_SPLITS = 4 + }; + int32_t num_splits() const { + return GetField(VT_NUM_SPLITS, 0); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_NUM_SPLITS) && + verifier.EndTable(); + } + SplitVOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(SplitVOptionsT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder &_fbb, const SplitVOptionsT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct SplitVOptionsBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_num_splits(int32_t num_splits) { + fbb_.AddElement(SplitVOptions::VT_NUM_SPLITS, num_splits, 0); + } + explicit SplitVOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + SplitVOptionsBuilder &operator=(const SplitVOptionsBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateSplitVOptions( + flatbuffers::FlatBufferBuilder &_fbb, + int32_t num_splits = 0) { + SplitVOptionsBuilder builder_(_fbb); + builder_.add_num_splits(num_splits); + return builder_.Finish(); +} + +flatbuffers::Offset CreateSplitVOptions(flatbuffers::FlatBufferBuilder &_fbb, const SplitVOptionsT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); + struct StridedSliceOptionsT : public flatbuffers::NativeTable { typedef StridedSliceOptions TableType; int32_t begin_mask; @@ -6247,6 +6435,46 @@ inline flatbuffers::Offset CreateOneHotOptions( flatbuffers::Offset CreateOneHotOptions(flatbuffers::FlatBufferBuilder &_fbb, const OneHotOptionsT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +struct AbsOptionsT : public flatbuffers::NativeTable { + typedef AbsOptions TableType; + AbsOptionsT() { + } +}; + +struct AbsOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef AbsOptionsT NativeTableType; + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + verifier.EndTable(); + } + AbsOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(AbsOptionsT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder &_fbb, const AbsOptionsT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct AbsOptionsBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit AbsOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + AbsOptionsBuilder &operator=(const AbsOptionsBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateAbsOptions( + flatbuffers::FlatBufferBuilder &_fbb) { + AbsOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset CreateAbsOptions(flatbuffers::FlatBufferBuilder &_fbb, const AbsOptionsT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); + struct LogicalAndOptionsT : public flatbuffers::NativeTable { typedef LogicalAndOptions TableType; LogicalAndOptionsT() { @@ -6633,6 +6861,154 @@ inline flatbuffers::Offset CreateRangeOptions( flatbuffers::Offset CreateRangeOptions(flatbuffers::FlatBufferBuilder &_fbb, const RangeOptionsT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +struct LeakyReluOptionsT : public flatbuffers::NativeTable { + typedef LeakyReluOptions TableType; + float alpha; + LeakyReluOptionsT() + : alpha(0.0f) { + } +}; + +struct LeakyReluOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef LeakyReluOptionsT NativeTableType; + enum { + VT_ALPHA = 4 + }; + float alpha() const { + return GetField(VT_ALPHA, 0.0f); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_ALPHA) && + verifier.EndTable(); + } + LeakyReluOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(LeakyReluOptionsT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder &_fbb, const LeakyReluOptionsT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct LeakyReluOptionsBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_alpha(float alpha) { + fbb_.AddElement(LeakyReluOptions::VT_ALPHA, alpha, 0.0f); + } + explicit LeakyReluOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + LeakyReluOptionsBuilder &operator=(const LeakyReluOptionsBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateLeakyReluOptions( + flatbuffers::FlatBufferBuilder &_fbb, + float alpha = 0.0f) { + LeakyReluOptionsBuilder builder_(_fbb); + builder_.add_alpha(alpha); + return builder_.Finish(); +} + +flatbuffers::Offset CreateLeakyReluOptions(flatbuffers::FlatBufferBuilder &_fbb, const LeakyReluOptionsT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct SquaredDifferenceOptionsT : public flatbuffers::NativeTable { + typedef SquaredDifferenceOptions TableType; + SquaredDifferenceOptionsT() { + } +}; + +struct SquaredDifferenceOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef SquaredDifferenceOptionsT NativeTableType; + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + verifier.EndTable(); + } + SquaredDifferenceOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(SquaredDifferenceOptionsT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder &_fbb, const SquaredDifferenceOptionsT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct SquaredDifferenceOptionsBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit SquaredDifferenceOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + SquaredDifferenceOptionsBuilder &operator=(const SquaredDifferenceOptionsBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateSquaredDifferenceOptions( + flatbuffers::FlatBufferBuilder &_fbb) { + SquaredDifferenceOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset CreateSquaredDifferenceOptions(flatbuffers::FlatBufferBuilder &_fbb, const SquaredDifferenceOptionsT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct MirrorPadOptionsT : public flatbuffers::NativeTable { + typedef MirrorPadOptions TableType; + MirrorPadMode mode; + MirrorPadOptionsT() + : mode(MirrorPadMode_REFLECT) { + } +}; + +struct MirrorPadOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef MirrorPadOptionsT NativeTableType; + enum { + VT_MODE = 4 + }; + MirrorPadMode mode() const { + return static_cast(GetField(VT_MODE, 0)); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_MODE) && + verifier.EndTable(); + } + MirrorPadOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(MirrorPadOptionsT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder &_fbb, const MirrorPadOptionsT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct MirrorPadOptionsBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_mode(MirrorPadMode mode) { + fbb_.AddElement(MirrorPadOptions::VT_MODE, static_cast(mode), 0); + } + explicit MirrorPadOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + MirrorPadOptionsBuilder &operator=(const MirrorPadOptionsBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateMirrorPadOptions( + flatbuffers::FlatBufferBuilder &_fbb, + MirrorPadMode mode = MirrorPadMode_REFLECT) { + MirrorPadOptionsBuilder builder_(_fbb); + builder_.add_mode(mode); + return builder_.Finish(); +} + +flatbuffers::Offset CreateMirrorPadOptions(flatbuffers::FlatBufferBuilder &_fbb, const MirrorPadOptionsT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); + struct OperatorCodeT : public flatbuffers::NativeTable { typedef OperatorCode TableType; BuiltinOperator builtin_code; @@ -6988,6 +7364,21 @@ struct Operator FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const ResizeNearestNeighborOptions *builtin_options_as_ResizeNearestNeighborOptions() const { return builtin_options_type() == BuiltinOptions_ResizeNearestNeighborOptions ? static_cast(builtin_options()) : nullptr; } + const LeakyReluOptions *builtin_options_as_LeakyReluOptions() const { + return builtin_options_type() == BuiltinOptions_LeakyReluOptions ? static_cast(builtin_options()) : nullptr; + } + const SquaredDifferenceOptions *builtin_options_as_SquaredDifferenceOptions() const { + return builtin_options_type() == BuiltinOptions_SquaredDifferenceOptions ? static_cast(builtin_options()) : nullptr; + } + const MirrorPadOptions *builtin_options_as_MirrorPadOptions() const { + return builtin_options_type() == BuiltinOptions_MirrorPadOptions ? static_cast(builtin_options()) : nullptr; + } + const AbsOptions *builtin_options_as_AbsOptions() const { + return builtin_options_type() == BuiltinOptions_AbsOptions ? static_cast(builtin_options()) : nullptr; + } + const SplitVOptions *builtin_options_as_SplitVOptions() const { + return builtin_options_type() == BuiltinOptions_SplitVOptions ? static_cast(builtin_options()) : nullptr; + } const flatbuffers::Vector *custom_options() const { return GetPointer *>(VT_CUSTOM_OPTIONS); } @@ -7315,6 +7706,26 @@ template<> inline const ResizeNearestNeighborOptions *Operator::builtin_options_ return builtin_options_as_ResizeNearestNeighborOptions(); } +template<> inline const LeakyReluOptions *Operator::builtin_options_as() const { + return builtin_options_as_LeakyReluOptions(); +} + +template<> inline const SquaredDifferenceOptions *Operator::builtin_options_as() const { + return builtin_options_as_SquaredDifferenceOptions(); +} + +template<> inline const MirrorPadOptions *Operator::builtin_options_as() const { + return builtin_options_as_MirrorPadOptions(); +} + +template<> inline const AbsOptions *Operator::builtin_options_as() const { + return builtin_options_as_AbsOptions(); +} + +template<> inline const SplitVOptions *Operator::builtin_options_as() const { + return builtin_options_as_SplitVOptions(); +} + struct OperatorBuilder { flatbuffers::FlatBufferBuilder &fbb_; flatbuffers::uoffset_t start_; @@ -8932,6 +9343,32 @@ inline flatbuffers::Offset CreateSplitOptions(flatbuffers::FlatBuf _num_splits); } +inline SplitVOptionsT *SplitVOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const { + auto _o = new SplitVOptionsT(); + UnPackTo(_o, _resolver); + return _o; +} + +inline void SplitVOptions::UnPackTo(SplitVOptionsT *_o, const flatbuffers::resolver_function_t *_resolver) const { + (void)_o; + (void)_resolver; + { auto _e = num_splits(); _o->num_splits = _e; }; +} + +inline flatbuffers::Offset SplitVOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const SplitVOptionsT* _o, const flatbuffers::rehasher_function_t *_rehasher) { + return CreateSplitVOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset CreateSplitVOptions(flatbuffers::FlatBufferBuilder &_fbb, const SplitVOptionsT *_o, const flatbuffers::rehasher_function_t *_rehasher) { + (void)_rehasher; + (void)_o; + struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const SplitVOptionsT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; + auto _num_splits = _o->num_splits; + return tflite::CreateSplitVOptions( + _fbb, + _num_splits); +} + inline StridedSliceOptionsT *StridedSliceOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const { auto _o = new StridedSliceOptionsT(); UnPackTo(_o, _resolver); @@ -9593,6 +10030,29 @@ inline flatbuffers::Offset CreateOneHotOptions(flatbuffers::FlatB _axis); } +inline AbsOptionsT *AbsOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const { + auto _o = new AbsOptionsT(); + UnPackTo(_o, _resolver); + return _o; +} + +inline void AbsOptions::UnPackTo(AbsOptionsT *_o, const flatbuffers::resolver_function_t *_resolver) const { + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset AbsOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const AbsOptionsT* _o, const flatbuffers::rehasher_function_t *_rehasher) { + return CreateAbsOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset CreateAbsOptions(flatbuffers::FlatBufferBuilder &_fbb, const AbsOptionsT *_o, const flatbuffers::rehasher_function_t *_rehasher) { + (void)_rehasher; + (void)_o; + struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const AbsOptionsT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; + return tflite::CreateAbsOptions( + _fbb); +} + inline LogicalAndOptionsT *LogicalAndOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const { auto _o = new LogicalAndOptionsT(); UnPackTo(_o, _resolver); @@ -9806,6 +10266,81 @@ inline flatbuffers::Offset CreateRangeOptions(flatbuffers::FlatBuf _fbb); } +inline LeakyReluOptionsT *LeakyReluOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const { + auto _o = new LeakyReluOptionsT(); + UnPackTo(_o, _resolver); + return _o; +} + +inline void LeakyReluOptions::UnPackTo(LeakyReluOptionsT *_o, const flatbuffers::resolver_function_t *_resolver) const { + (void)_o; + (void)_resolver; + { auto _e = alpha(); _o->alpha = _e; }; +} + +inline flatbuffers::Offset LeakyReluOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const LeakyReluOptionsT* _o, const flatbuffers::rehasher_function_t *_rehasher) { + return CreateLeakyReluOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset CreateLeakyReluOptions(flatbuffers::FlatBufferBuilder &_fbb, const LeakyReluOptionsT *_o, const flatbuffers::rehasher_function_t *_rehasher) { + (void)_rehasher; + (void)_o; + struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const LeakyReluOptionsT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; + auto _alpha = _o->alpha; + return tflite::CreateLeakyReluOptions( + _fbb, + _alpha); +} + +inline SquaredDifferenceOptionsT *SquaredDifferenceOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const { + auto _o = new SquaredDifferenceOptionsT(); + UnPackTo(_o, _resolver); + return _o; +} + +inline void SquaredDifferenceOptions::UnPackTo(SquaredDifferenceOptionsT *_o, const flatbuffers::resolver_function_t *_resolver) const { + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset SquaredDifferenceOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const SquaredDifferenceOptionsT* _o, const flatbuffers::rehasher_function_t *_rehasher) { + return CreateSquaredDifferenceOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset CreateSquaredDifferenceOptions(flatbuffers::FlatBufferBuilder &_fbb, const SquaredDifferenceOptionsT *_o, const flatbuffers::rehasher_function_t *_rehasher) { + (void)_rehasher; + (void)_o; + struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const SquaredDifferenceOptionsT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; + return tflite::CreateSquaredDifferenceOptions( + _fbb); +} + +inline MirrorPadOptionsT *MirrorPadOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const { + auto _o = new MirrorPadOptionsT(); + UnPackTo(_o, _resolver); + return _o; +} + +inline void MirrorPadOptions::UnPackTo(MirrorPadOptionsT *_o, const flatbuffers::resolver_function_t *_resolver) const { + (void)_o; + (void)_resolver; + { auto _e = mode(); _o->mode = _e; }; +} + +inline flatbuffers::Offset MirrorPadOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const MirrorPadOptionsT* _o, const flatbuffers::rehasher_function_t *_rehasher) { + return CreateMirrorPadOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset CreateMirrorPadOptions(flatbuffers::FlatBufferBuilder &_fbb, const MirrorPadOptionsT *_o, const flatbuffers::rehasher_function_t *_rehasher) { + (void)_rehasher; + (void)_o; + struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const MirrorPadOptionsT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; + auto _mode = _o->mode; + return tflite::CreateMirrorPadOptions( + _fbb, + _mode); +} + inline OperatorCodeT *OperatorCode::UnPack(const flatbuffers::resolver_function_t *_resolver) const { auto _o = new OperatorCodeT(); UnPackTo(_o, _resolver); @@ -10360,6 +10895,26 @@ inline bool VerifyBuiltinOptions(flatbuffers::Verifier &verifier, const void *ob auto ptr = reinterpret_cast(obj); return verifier.VerifyTable(ptr); } + case BuiltinOptions_LeakyReluOptions: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_SquaredDifferenceOptions: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_MirrorPadOptions: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_AbsOptions: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_SplitVOptions: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } default: return false; } } @@ -10674,6 +11229,26 @@ inline void *BuiltinOptionsUnion::UnPack(const void *obj, BuiltinOptions type, c auto ptr = reinterpret_cast(obj); return ptr->UnPack(resolver); } + case BuiltinOptions_LeakyReluOptions: { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_SquaredDifferenceOptions: { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_MirrorPadOptions: { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_AbsOptions: { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_SplitVOptions: { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } default: return nullptr; } } @@ -10976,6 +11551,26 @@ inline flatbuffers::Offset BuiltinOptionsUnion::Pack(flatbuffers::FlatBuff auto ptr = reinterpret_cast(value); return CreateResizeNearestNeighborOptions(_fbb, ptr, _rehasher).Union(); } + case BuiltinOptions_LeakyReluOptions: { + auto ptr = reinterpret_cast(value); + return CreateLeakyReluOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_SquaredDifferenceOptions: { + auto ptr = reinterpret_cast(value); + return CreateSquaredDifferenceOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_MirrorPadOptions: { + auto ptr = reinterpret_cast(value); + return CreateMirrorPadOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_AbsOptions: { + auto ptr = reinterpret_cast(value); + return CreateAbsOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_SplitVOptions: { + auto ptr = reinterpret_cast(value); + return CreateSplitVOptions(_fbb, ptr, _rehasher).Union(); + } default: return 0; } } @@ -11278,6 +11873,26 @@ inline BuiltinOptionsUnion::BuiltinOptionsUnion(const BuiltinOptionsUnion &u) FL value = new ResizeNearestNeighborOptionsT(*reinterpret_cast(u.value)); break; } + case BuiltinOptions_LeakyReluOptions: { + value = new LeakyReluOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_SquaredDifferenceOptions: { + value = new SquaredDifferenceOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_MirrorPadOptions: { + value = new MirrorPadOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_AbsOptions: { + value = new AbsOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_SplitVOptions: { + value = new SplitVOptionsT(*reinterpret_cast(u.value)); + break; + } default: break; } @@ -11655,6 +12270,31 @@ inline void BuiltinOptionsUnion::Reset() { delete ptr; break; } + case BuiltinOptions_LeakyReluOptions: { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_SquaredDifferenceOptions: { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_MirrorPadOptions: { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_AbsOptions: { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_SplitVOptions: { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } default: break; } value = nullptr; diff --git a/tensorflow/lite/string_util.cc b/tensorflow/lite/string_util.cc index 1b33f5bcba..6efa11d60c 100644 --- a/tensorflow/lite/string_util.cc +++ b/tensorflow/lite/string_util.cc @@ -96,8 +96,7 @@ int DynamicBuffer::WriteToBuffer(char** buffer) { return bytes; } -void DynamicBuffer::WriteToTensor(TfLiteTensor* tensor) { - // Set tensor content pointer to tensor_buffer, and release original data. +void DynamicBuffer::WriteToTensorAsVector(TfLiteTensor* tensor) { auto dims = TfLiteIntArrayCreate(1); dims->data[0] = offset_.size() - 1; // Store number of strings. WriteToTensor(tensor, dims); @@ -108,6 +107,10 @@ void DynamicBuffer::WriteToTensor(TfLiteTensor* tensor, char* tensor_buffer; int bytes = WriteToBuffer(&tensor_buffer); + if (new_shape == nullptr) { + new_shape = TfLiteIntArrayCopy(tensor->dims); + } + // Set tensor content pointer to tensor_buffer, and release original data. TfLiteTensorReset(tensor->type, tensor->name, new_shape, tensor->params, tensor_buffer, bytes, kTfLiteDynamic, tensor->allocation, diff --git a/tensorflow/lite/string_util.h b/tensorflow/lite/string_util.h index c9b74482f7..f076db76f2 100644 --- a/tensorflow/lite/string_util.h +++ b/tensorflow/lite/string_util.h @@ -74,12 +74,18 @@ class DynamicBuffer { // The function allocates space for the buffer but does NOT take ownership. int WriteToBuffer(char** buffer); - // Fill content into a string tensor, with the given new_shape. The new - // shape must match the number of strings in this object. + // Fill content into a string tensor, with the given new_shape. The new shape + // must match the number of strings in this object. Caller relinquishes + // ownership of new_shape. If 'new_shape' is nullptr, keep the tensor's + // existing shape. void WriteToTensor(TfLiteTensor* tensor, TfLiteIntArray* new_shape); // Fill content into a string tensor. Set shape to {num_strings}. - void WriteToTensor(TfLiteTensor* tensor); + void WriteToTensorAsVector(TfLiteTensor* tensor); + + // Deprecated. Use WriteToTensorAsVector() or pass in the new shpe. + // TODO(b/120230709): remove when people migrate away. + void WriteToTensor(TfLiteTensor* tensor) { WriteToTensorAsVector(tensor); } private: // Data buffer to store contents of strings, not including headers. diff --git a/tensorflow/lite/string_util_test.cc b/tensorflow/lite/string_util_test.cc index 377cdd77eb..cbf1d7b226 100644 --- a/tensorflow/lite/string_util_test.cc +++ b/tensorflow/lite/string_util_test.cc @@ -55,7 +55,7 @@ TEST(StringUtil, TestStringUtil) { new_shape->data[0] = 2; new_shape->data[1] = 1; buf0.WriteToTensor(t0, new_shape); - buf1.WriteToTensor(t1); + buf1.WriteToTensorAsVector(t1); // Check tensor shapes. EXPECT_EQ(t0->dims->size, 2); @@ -99,7 +99,7 @@ TEST(StringUtil, TestAddJoinedString) { DynamicBuffer buf; buf.AddJoinedString({{s0, 3}, {s1, 4}, {s2, 0}, {s3, 3}}, ' '); - buf.WriteToTensor(t0); + buf.WriteToTensorAsVector(t0); ASSERT_EQ(GetStringCount(t0), 1); StringRef str_ref; @@ -115,12 +115,43 @@ TEST(StringUtil, TestEmptyList) { t0->type = kTfLiteString; t0->allocation_type = kTfLiteDynamic; DynamicBuffer buf; - buf.WriteToTensor(t0); + buf.WriteToTensorAsVector(t0); ASSERT_EQ(GetStringCount(t0), 0); ASSERT_EQ(t0->bytes, 8); } +TEST(StringUtil, TestShapes) { + Interpreter interpreter; + interpreter.AddTensors(1); + TfLiteTensor* t0 = interpreter.tensor(0); + t0->type = kTfLiteString; + t0->allocation_type = kTfLiteDynamic; + t0->dims = TfLiteIntArrayCreate(2); + t0->dims->data[0] = 2; + t0->dims->data[1] = 1; + + // Not setting a new shape: number of strings must match + DynamicBuffer buf; + buf.AddString("ABC", 3); + buf.AddString("X", 1); + buf.WriteToTensor(t0, nullptr); + + ASSERT_EQ(t0->dims->size, 2); + EXPECT_EQ(t0->dims->data[0], 2); + EXPECT_EQ(t0->dims->data[1], 1); + + auto new_shape = TfLiteIntArrayCreate(2); + new_shape->data[0] = 1; + new_shape->data[1] = 2; + + buf.WriteToTensor(t0, new_shape); + + ASSERT_EQ(t0->dims->size, 2); + EXPECT_EQ(t0->dims->data[0], 1); + EXPECT_EQ(t0->dims->data[1], 2); +} + } // namespace tflite int main(int argc, char** argv) { diff --git a/tensorflow/lite/testing/generate_examples.py b/tensorflow/lite/testing/generate_examples.py index 2b129df766..4c731a7d18 100644 --- a/tensorflow/lite/testing/generate_examples.py +++ b/tensorflow/lite/testing/generate_examples.py @@ -103,8 +103,8 @@ KNOWN_BUGS = { r"batch_to_space_nd.*input_shape=\[8,2,2,2,1,1\]": "70594733", # Div will use floordiv. r"div.*int32": "72051395", - # No support for SplitV - r"split.*num_or_size_splits=\[2,2\]": "73377559", + # Constant 1D gather crashes toco. + r"gather_buggy.*input_shape=\[3\].*": "120029508", } @@ -370,7 +370,8 @@ def make_zip_of_tests(zip_path, make_graph, make_test_inputs, extra_toco_options=ExtraTocoOptions(), - use_frozen_graph=False): + use_frozen_graph=False, + expected_tf_success=None): """Helper to make a zip file of a bunch of TensorFlow models. This does a cartestian product of the dictionary of test_parameters and @@ -390,6 +391,8 @@ def make_zip_of_tests(zip_path, `output_tensors` and returns tuple `(input_values, output_values)`. extra_toco_options: Additional toco options. use_frozen_graph: Whether or not freeze graph before toco converter. + expected_tf_success: Number of times tensorflow is supposed to succeed in + executing the input graphs. `None` means "unknown". Raises: RuntimeError: if there are toco errors that can't be ignored. @@ -550,6 +553,11 @@ def make_zip_of_tests(zip_path, " and %d TOCO converted graphs (%.1f%%"), zip_path, total_conversions, tf_success, toco_success, percent) + if expected_tf_success is not None and tf_success != expected_tf_success: + raise RuntimeError( + "Expected TF to succeed %d times, but that happened %d times" % + (expected_tf_success, tf_success)) + if not FLAGS.ignore_toco_errors and toco_errors > 0: raise RuntimeError( "Found %d errors while generating toco models" % toco_errors) @@ -616,6 +624,30 @@ def make_max_pool_tests(zip_path): make_pool_tests(tf.nn.max_pool)(zip_path) +def make_abs_tests(zip_path): + """Make a set of tests to do relu.""" + + # Chose a set of parameters + test_parameters = [{ + "input_shape": [[], [1], [2, 3], [1, 1, 1, 1], [1, 3, 4, 3], + [3, 15, 14, 3], [3, 1, 2, 4, 6], [2, 2, 3, 4, 5, 6]], + }] + + def build_graph(parameters): + input_tensor = tf.placeholder( + dtype=tf.float32, name="input", shape=parameters["input_shape"]) + out = tf.abs(input_tensor) + return [input_tensor], [out] + + def build_inputs(parameters, sess, inputs, outputs): + input_values = create_tensor_data( + np.float32, parameters["input_shape"], min_value=-10, max_value=10) + return [input_values], sess.run( + outputs, feed_dict=dict(zip(inputs, [input_values]))) + + make_zip_of_tests(zip_path, test_parameters, build_graph, build_inputs) + + def make_relu_tests(zip_path): """Make a set of tests to do relu.""" @@ -747,6 +779,34 @@ def make_prelu_tests(zip_path): use_frozen_graph=True) +def make_leaky_relu_tests(zip_path): + """Make a set of tests to do LeakyRelu.""" + + test_parameters = [ + { + "input_shape": [[], [1], [5], [1, 10, 10, 3], [3, 3, 3, 3]], + "alpha": [0.1, 1.0, 2.0, -0.1, -1.0, -2.0], + }, + ] + + def build_graph(parameters): + """Build the graph for the test case.""" + + input_tensor = tf.placeholder( + dtype=tf.float32, name="input", shape=parameters["input_shape"]) + out = tf.nn.leaky_relu(input_tensor, alpha=parameters["alpha"]) + return [input_tensor], [out] + + def build_inputs(parameters, sess, inputs, outputs): + """Build the inputs for the test case.""" + input_values = create_tensor_data( + np.float32, parameters["input_shape"], min_value=-3, max_value=10) + return [input_values], sess.run( + outputs, feed_dict=dict(zip(inputs, [input_values]))) + + make_zip_of_tests(zip_path, test_parameters, build_graph, build_inputs) + + # This function tests various TensorFLow functions that generates Const op, # including `tf.ones`, `tf.zeros` and random functions. def make_constant_tests(zip_path): @@ -755,6 +815,7 @@ def make_constant_tests(zip_path): test_parameters = [{ "dtype": [tf.float32, tf.int32], "input_shape": [[], [1], [2], [1, 1, 1, 1], [2, 2, 2, 2]], + "constant_is_also_output": [True, False], }] def build_graph(parameters): @@ -764,17 +825,19 @@ def make_constant_tests(zip_path): shape=parameters["input_shape"]) constant = tf.constant( create_tensor_data(parameters["dtype"], parameters["input_shape"])) - # This maximum node is here to avoid the situation where a graph output is - # a constant, which is an error in toco. - out = tf.maximum(dummy_input, constant) - return [dummy_input], [out] + out = [tf.maximum(dummy_input, constant)] + if parameters["constant_is_also_output"]: + out.append(constant) + + return [dummy_input], out def build_inputs(parameters, sess, inputs, outputs): dummy_input = np.zeros( parameters["input_shape"], dtype=_TF_TYPE_INFO[parameters["dtype"]][0]) return [dummy_input], sess.run(outputs, feed_dict={inputs[0]: dummy_input}) - make_zip_of_tests(zip_path, test_parameters, build_graph, build_inputs) + make_zip_of_tests(zip_path, test_parameters, build_graph, build_inputs, + expected_tf_success=20) def make_binary_op_tests(zip_path, binary_operator): @@ -869,34 +932,46 @@ def make_reduce_tests(reduce_op, def f(zip_path): """Actual function that generates examples.""" - test_parameters = [{ - "input_dtype": [tf.float32, tf.int32, tf.int64], - "input_shape": [[3, 2, 4]], - "axis": [ - 0, 1, 2, [0, 1], [0, 2], [1, 2], [0, 1, 2], [1, 0], [2, 0], - [2, 1], [2, 1, 0], [2, 0, 1], -1, -2, -3, [1, -1], [0, -1], [-1, 0], - [-1, -2, -3], [0, 0, 0], [2, 2, 0], [1, 0, -3, -3] - ], - "const_axis": [True, False], - "keepdims": [True, False], - }, { - "input_dtype": [tf.float32], - "input_shape": [[1, 8, 8, 3]], - "axis": [ - 0, 1, 2, 3, [1, 2], [0, 3], [1, 2, 3], [0, 1, 2, 3], - [3, 2, 1, 0], [3, 1, 0, 2], [2, 0], [3, 0], [3, 1], [1, 0], -1, -2, - -3, -4, [0, -2], [2, 3, -1, 0], [3, 1, 2, -3], [3, -4], [2, 2, 2], - [2, 2, 3], [-3, -3, -4], [-3, 2, 1] - ], - "const_axis": [True, False], - "keepdims": [True, False], - }, { - "input_dtype": [tf.float32], - "input_shape": [[], [1, 8, 8, 3], [3, 2, 4]], - "axis": [None], - "const_axis": [True], - "keepdims": [True, False], - }] + test_parameters = [ + { + "input_dtype": [tf.float32, tf.int32, tf.int64], + "input_shape": [[3, 3, 2, 4]], + "axis": [ + 0, 1, 2, [0, 1], [0, 2], [1, 2], [0, 1, 2], [1, 0], [2, 0], + [2, 1], [2, 1, 0], [2, 0, 1], -1, -2, -3, [1, -1], [0, -1], + [-1, 0], [-1, -2, -3], [0, 0, 0], [2, 2, 0], [1, 0, -3, -3] + ], + "const_axis": [True, False], + "keepdims": [True, False], + }, + { + "input_dtype": [tf.float32], + "input_shape": [[1, 8, 8, 3]], + "axis": [ + 0, 1, 2, 3, [1, 2], [0, 3], [1, 2, 3], [0, 1, 2, + 3], [3, 2, 1, 0], + [3, 1, 0, 2], [2, 0], [3, 0], [3, 1], [1, 0], -1, -2, -3, -4, + [0, -2], [2, 3, -1, 0], [3, 1, 2, -3], [3, -4], [2, 2, 2], + [2, 2, 3], [-3, -3, -4], [-3, 2, 1] + ], + "const_axis": [True, False], + "keepdims": [True, False], + }, + { + "input_dtype": [tf.float32], + "input_shape": [[], [1, 8, 8, 3], [3, 2, 4]], + "axis": [[]], # shape is: [0] + "const_axis": [False], + "keepdims": [True, False], + }, + { + "input_dtype": [tf.float32], + "input_shape": [[], [1, 8, 8, 3], [3, 2, 4]], + "axis": [None], # shape is: [] + "const_axis": [True], + "keepdims": [True, False], + } + ] def build_graph(parameters): """Build the mean op testing graph.""" @@ -1135,6 +1210,10 @@ def make_floor_mod_tests(zip_path): make_binary_op_tests(zip_path, tf.floormod) +def make_squared_difference_tests(zip_path): + make_binary_op_tests(zip_path, tf.squared_difference) + + def make_gather_tests(zip_path): """Make a set of tests to do gather.""" @@ -1142,9 +1221,9 @@ def make_gather_tests(zip_path): # TODO(mgubin): add string tests when they are supported by Toco. # TODO(mgubin): add tests for Nd indices when they are supported by # TfLite. - "params_dtype": [tf.float32, tf.int32], + "params_dtype": [tf.float32, tf.int32, tf.int64], "params_shape": [[10], [1, 2, 20]], - "indices_dtype": [tf.int32], + "indices_dtype": [tf.int32, tf.int64], "indices_shape": [[3], [5]], "axis": [-1, 0, 1], }] @@ -1172,7 +1251,43 @@ def make_gather_tests(zip_path): return [params, indices], sess.run( outputs, feed_dict=dict(zip(inputs, [params, indices]))) - make_zip_of_tests(zip_path, test_parameters, build_graph, build_inputs) + # Note that TF can't execute with index=1 and params_shape=[10]. + make_zip_of_tests( + zip_path, + test_parameters, + build_graph, + build_inputs, + expected_tf_success=60) + + +def make_gather_buggy_tests(zip_path): + """Make a set of tests to show gather crashes toco.""" + + test_parameters = [{ + "input_shape": [[3]], + "reference_shape": [[2]], + }, { + "input_shape": [[2, 3]], + "reference_shape": [[2, 3]], + }] + + def build_graph(parameters): + """Build a graph where the inputs to Gather are constants.""" + reference = tf.placeholder( + dtype=tf.int32, shape=parameters["reference_shape"]) + gather_input = tf.constant( + create_tensor_data(tf.int32, parameters["input_shape"])) + gather_indices = tf.constant([0, 1], tf.int32) + out = tf.equal(reference, tf.gather(gather_input, gather_indices)) + return [reference], [out] + + def build_inputs(parameters, sess, inputs, outputs): + reference_values = np.zeros(parameters["reference_shape"], dtype=np.int32) + return [reference_values], sess.run( + outputs, feed_dict={inputs[0]: reference_values}) + + make_zip_of_tests(zip_path, test_parameters, build_graph, build_inputs, + expected_tf_success=2) def make_global_batch_norm_tests(zip_path): @@ -1340,23 +1455,27 @@ def make_conv_with_shared_weights_tests(zip_path): input_shape, filter_shape = get_tensor_shapes(parameters) input_tensor = tf.placeholder( dtype=tf.float32, name="input", shape=input_shape) + input_tensors = [input_tensor] # Construct a constant weights tensor which will be used by both Conv2D. filter_tensor = tf.constant( create_tensor_data(np.float32, filter_shape), dtype=tf.float32) - input_tensors = [input_tensor] + + # Ensure that FuseBinaryIntoFollowingAffine works with an input which + # is shared by multiple affine ops. + conv_input = input_tensor + 0.1 # Construct 2 Conv2D operations which use exactly the same input and # weights. result1 = tf.nn.conv2d( - input_tensor, + conv_input, filter_tensor, strides=parameters["strides"], dilations=parameters["dilations"], padding=parameters["padding"], data_format=parameters["data_format"]) result2 = tf.nn.conv2d( - input_tensor, + conv_input, filter_tensor, strides=parameters["strides"], dilations=parameters["dilations"], @@ -1524,7 +1643,7 @@ def make_split_tests(zip_path): test_parameters = [{ "input_shape": [[1, 3, 4, 6], [2, 4, 1], [6, 4], [8]], - "num_or_size_splits": [1, 2, 3, 4, 5, [2, 2]], + "num_or_size_splits": [1, 2, 3, 4, 5], "axis": [0, 1, 2, 3, -4, -3, -2, -1], }] @@ -1542,6 +1661,29 @@ def make_split_tests(zip_path): make_zip_of_tests(zip_path, test_parameters, build_graph, build_inputs) +def make_splitv_tests(zip_path): + """Make a set of tests to do tf.split_v.""" + + test_parameters = [{ + "input_shape": [[1, 3, 4, 6], [2, 4, 1], [6, 4], [8]], + "size_splits": [[2, 2], [1, 3], [4, 2], [5, 3], + [-1, 1], [-1, 2], [-1, 4]], + "axis": [0, 1, 2, 3, -4, -3, -2, -1], + }] + + def build_graph(parameters): + input_tensor = tf.placeholder( + dtype=tf.float32, name="input", shape=parameters["input_shape"]) + out = tf.split(input_tensor, parameters["size_splits"], parameters["axis"]) + return [input_tensor], [out[0]] + + def build_inputs(parameters, sess, inputs, outputs): + values = [create_tensor_data(np.float32, parameters["input_shape"])] + return values, sess.run(outputs, feed_dict=dict(zip(inputs, values))) + + make_zip_of_tests(zip_path, test_parameters, build_graph, build_inputs) + + def make_concat_tests(zip_path): """Make a set of tests to do concatenation.""" @@ -2468,6 +2610,32 @@ def make_strided_slice_1d_exhaustive_tests(zip_path): _make_strided_slice_tests(zip_path, test_parameters) +def make_strided_slice_buggy_tests(zip_path): + """Make a set of tests to show strided_slice yields incorrect results.""" + + test_parameters = [{ + "unused_iteration_counter": [1], + }] + + def build_graph(parameters): + """Build the strided_slice op testing graph.""" + del parameters + input_values = tf.placeholder(dtype=tf.float32, shape=[4, 2]) + data = tf.constant([[0, 1, 2, 3], + [4, 5, 6, 7], + [8, 9, 10, 11], + [12, 13, 14, 15]], tf.float32) + return [input_values], [input_values + data[:, :2]] + + def build_inputs(parameters, sess, inputs, outputs): + del parameters + input_values = np.zeros([4, 2], dtype=np.float32) + return [input_values], sess.run( + outputs, feed_dict={inputs[0]: input_values}) + + make_zip_of_tests(zip_path, test_parameters, build_graph, build_inputs) + + def make_lstm_tests(zip_path): """Make a set of tests to do basic Lstm cell.""" @@ -3121,7 +3289,7 @@ def make_transpose_conv_tests(zip_path): def make_tile_tests(zip_path): """Make a set of tests to do tile.""" test_parameters = [{ - "input_dtype": [tf.float32, tf.int32], + "input_dtype": [tf.float32, tf.int32, tf.bool], "input_shape": [[3, 2, 1], [2, 2, 2]], "multiplier_dtype": [tf.int32, tf.int64], "multiplier_shape": [[3]] @@ -3143,8 +3311,10 @@ def make_tile_tests(zip_path): def build_inputs(parameters, sess, inputs, outputs): input_value = create_tensor_data(parameters["input_dtype"], parameters["input_shape"]) - multipliers_value = create_tensor_data(parameters["multiplier_dtype"], - parameters["multiplier_shape"]) + multipliers_value = create_tensor_data( + parameters["multiplier_dtype"], + parameters["multiplier_shape"], + min_value=0) return [input_value, multipliers_value], sess.run( outputs, feed_dict={ @@ -3365,6 +3535,36 @@ def make_range_tests(zip_path): make_zip_of_tests(zip_path, test_parameters, build_graph, build_inputs) +def make_fill_tests(zip_path): + """Make a set of tests to do fill.""" + + test_parameters = [{ + "dims_dtype": [tf.int32, tf.int64], + "dims_shape": [[], [1], [3], [3, 3]], + "value_dtype": [tf.int32, tf.int64, tf.float32], + }] + + def build_graph(parameters): + """Build the fill op testing graph.""" + input1 = tf.placeholder( + dtype=parameters["dims_dtype"], + name="dims", + shape=parameters["dims_shape"]) + input2 = tf.placeholder( + dtype=parameters["value_dtype"], name="value", shape=[]) + out = tf.fill(input1, input2) + return [input1, input2], [out] + + def build_inputs(parameters, sess, inputs, outputs): + input1 = create_tensor_data(parameters["dims_dtype"], + parameters["dims_shape"], 1) + input2 = create_scalar_data(parameters["value_dtype"]) + return [input1, input2], sess.run( + outputs, feed_dict=dict(zip(inputs, [input1, input2]))) + + make_zip_of_tests(zip_path, test_parameters, build_graph, build_inputs) + + def _make_logical_tests(op): """Make a set of tests to do logical operations.""" @@ -3416,6 +3616,141 @@ def make_logical_xor_tests(zip_path): return _make_logical_tests(tf.logical_xor)(zip_path) +def make_mirror_pad_tests(zip_path): + """Make a set of tests to do mirror_pad.""" + + test_parameters = [ + { + "input_shape": [[2, 3]], + "padding_matrix": [[[1, 1], [2, 1]]], + "mode": ["REFLECT"], + "type": ["const"] + }, + { + "input_shape": [[2, 3]], + "padding_matrix": [[[1, 1], [1, 1]]], + "mode": ["REFLECT"], + "type": ["const"] + }, + { + "input_shape": [[2, 3]], + "padding_matrix": [[[1, 1], [2, 1]]], + "mode": ["SYMMETRIC"], + "type": ["placeholder"] + }, + { + "input_shape": [[2, 3]], + "padding_matrix": [[[1, 1], [2, 1]]], + "mode": ["REFLECT"], + "type": ["placeholder"] + }, + { + "input_shape": [[3]], + "padding_matrix": [[[0, 2]]], + "mode": ["SYMMETRIC"], + "type": ["placeholder"] + }, + { + "input_shape": [[3]], + "padding_matrix": [[[0, 2]]], + "mode": ["SYMMETRIC"], + "type": ["const"] + }, + { + "input_shape": [[3]], + "padding_matrix": [[[0, 2]]], + "mode": ["REFLECT"], + "type": ["const"] + }, + ] + + def build_graph(parameters): + """Build the graph for the test case.""" + + input_tensor = tf.placeholder( + dtype=tf.int32, name="input", shape=parameters["input_shape"]) + if parameters["type"] != "const": + padding_matrix = tf.placeholder( + dtype=tf.int32, + name="padding", + shape=[len(parameters["input_shape"]), 2]) + input_tensors = [input_tensor, padding_matrix] + else: + padding_matrix = tf.constant(np.array(parameters["padding_matrix"])) + input_tensors = [input_tensor] + output = tf.pad( + input_tensor, paddings=padding_matrix, mode=parameters["mode"]) + + return input_tensors, [output] + + def build_inputs(parameters, sess, inputs, outputs): + input_values = [create_tensor_data(tf.int32, parameters["input_shape"])] + if parameters["type"] != "const": + input_values.append(np.array(parameters["padding_matrix"])) + return input_values, sess.run( + outputs, feed_dict=dict(zip(inputs, input_values))) + + make_zip_of_tests( + zip_path, + test_parameters, + build_graph, + build_inputs, + expected_tf_success=7) + + +def make_unroll_batch_matmul_tests(zip_path): + """Make a set of tests to test unroll_batch_matmul.""" + + test_parameters = [{"dtype": [tf.float32], "shape": [[(2, 2, 3), (2, 3, 2)]]}] + + def build_graph(parameters): + """Build the batch_matmul op testing graph.""" + input_tensor1 = tf.placeholder( + dtype=parameters["dtype"], shape=parameters["shape"][0]) + input_tensor2 = tf.placeholder( + dtype=parameters["dtype"], shape=parameters["shape"][1]) + # Should be unrolled and replaced with fully_connected ops in the end. + out = tf.matmul(input_tensor1, input_tensor2) + return [input_tensor1, input_tensor2], [out] + + def build_inputs(parameters, sess, inputs, outputs): + input_value1 = create_tensor_data( + parameters["dtype"], shape=parameters["shape"][0]) + input_value2 = create_tensor_data( + parameters["dtype"], shape=parameters["shape"][1]) + return [input_value1, input_value2], sess.run( + outputs, feed_dict=dict(zip(inputs, [input_value1, input_value2]))) + + make_zip_of_tests(zip_path, test_parameters, build_graph, build_inputs) + + +def make_placeholder_with_default_tests(zip_path): + """Make a set of tests to test placeholder_with_default.""" + + test_parameters = [{ + "dtype": [tf.float32, tf.int32, tf.int64], + }] + + def build_graph(parameters): + """Build the placeholder_with_default testing graph.""" + const_node = tf.constant( + [1, 2, 2, 0], shape=[2, 2], dtype=parameters["dtype"]) + input_tensor = tf.placeholder_with_default( + const_node, shape=[2, 2], name="input") + out = tf.equal(input_tensor, const_node, name="output") + + return [input_tensor], [out] + + def build_inputs(parameters, sess, inputs, outputs): + numpy_type = _TF_TYPE_INFO[parameters["dtype"]][0] + input_value = np.array([[1, 0], [2, 1]], numpy_type) + return [input_value], sess.run( + outputs, feed_dict=dict(zip(inputs, [input_value]))) + + make_zip_of_tests(zip_path, test_parameters, build_graph, build_inputs, + expected_tf_success=3) + + # Toco binary path provided by the generate rule. bin_path = None diff --git a/tensorflow/lite/testing/generated_examples_zip_test.cc b/tensorflow/lite/testing/generated_examples_zip_test.cc index aedea52065..91a4851fb0 100644 --- a/tensorflow/lite/testing/generated_examples_zip_test.cc +++ b/tensorflow/lite/testing/generated_examples_zip_test.cc @@ -101,6 +101,10 @@ std::map kBrokenTests = { {R"(^\/mul.*dtype=tf\.int64)", "119126484"}, {R"(^\/add.*dtype=tf\.int64)", "119126484"}, {R"(^\/floor_div.*dtype=tf\.int64)", "119126484"}, + {R"(^\/squared_difference.*dtype=tf\.int64)", "119126484"}, + + // Strided Slice chooses the wrong dimension. + {R"(^\/strided_slice_buggy)", "119786029"}, }; // Allows test data to be unarchived into a temporary directory and makes diff --git a/tensorflow/lite/testing/tflite_driver.cc b/tensorflow/lite/testing/tflite_driver.cc index 3a0febb780..27e3a3770b 100644 --- a/tensorflow/lite/testing/tflite_driver.cc +++ b/tensorflow/lite/testing/tflite_driver.cc @@ -147,9 +147,10 @@ TfLiteDriver::TfLiteDriver(bool use_nnapi, const string& delegate_name) } TfLiteDriver::~TfLiteDriver() { - for (TfLiteTensor* t : tensors_to_deallocate_) { - free(t->data.raw); + for (auto t : tensors_to_deallocate_) { + DeallocateStringTensor(t.second); } + interpreter_.reset(); } void TfLiteDriver::AllocateTensors() { @@ -242,12 +243,10 @@ void TfLiteDriver::SetInput(int id, const string& csv_values) { case kTfLiteString: { string s = absl::HexStringToBytes(csv_values); - tensor->data.raw = reinterpret_cast(malloc(s.size())); - tensor->bytes = s.size(); + DeallocateStringTensor(tensors_to_deallocate_[id]); + AllocateStringTensor(id, s.size(), tensor); memcpy(tensor->data.raw, s.data(), s.size()); - // We must remember to free the memory we allocated above. - tensors_to_deallocate_.push_back(tensor); break; } default: diff --git a/tensorflow/lite/testing/tflite_driver.h b/tensorflow/lite/testing/tflite_driver.h index d8b40565ba..1da0533c57 100644 --- a/tensorflow/lite/testing/tflite_driver.h +++ b/tensorflow/lite/testing/tflite_driver.h @@ -49,6 +49,18 @@ class TfLiteDriver : public TestRunner { string ReadOutput(int id) override { return "no-op"; } private: + void DeallocateStringTensor(TfLiteTensor* t) { + if (t) { + free(t->data.raw); + t->data.raw = nullptr; + } + } + void AllocateStringTensor(int id, size_t num_bytes, TfLiteTensor* t) { + t->data.raw = reinterpret_cast(malloc(num_bytes)); + t->bytes = num_bytes; + tensors_to_deallocate_[id] = t; + } + void ResetLSTMStateTensors(); class Expectation; @@ -59,7 +71,7 @@ class TfLiteDriver : public TestRunner { std::unique_ptr interpreter_; std::map> expected_output_; bool must_allocate_tensors_ = true; - std::vector tensors_to_deallocate_; + std::map tensors_to_deallocate_; }; } // namespace testing diff --git a/tensorflow/lite/toco/BUILD b/tensorflow/lite/toco/BUILD index 1430287444..82aa1f557e 100644 --- a/tensorflow/lite/toco/BUILD +++ b/tensorflow/lite/toco/BUILD @@ -395,6 +395,28 @@ tf_cc_test( # :toco is the main public command-line tool exposing the functionality # of the :toco_tooling library. +cc_library( + name = "toco_convert", + srcs = ["toco_convert.cc"], + hdrs = ["toco_convert.h"], + visibility = ["//visibility:public"], + deps = [ + ":model", + ":model_cmdline_flags", + ":model_flags_proto_cc", + ":toco_cmdline_flags", + ":toco_flags_proto_cc", + ":toco_port", + ":toco_tooling", + ":types_proto_cc", + "@com_google_absl//absl/strings", + "//tensorflow/core:lib", + # We cannot embed the core:ops dependency directly into :toco_tooling as + # it can conflict with downstream deps when toco is used as a library. + "//tensorflow/core:ops", + ], +) + tf_cc_binary( name = "toco", srcs = ["toco.cc"], @@ -404,6 +426,7 @@ tf_cc_binary( ":model_cmdline_flags", ":model_flags_proto_cc", ":toco_cmdline_flags", + ":toco_convert", ":toco_flags_proto_cc", ":toco_port", ":toco_tooling", @@ -416,6 +439,29 @@ tf_cc_binary( ], ) +tf_cc_test( + name = "toco_convert_test", + srcs = ["toco_convert_test.cc"], + visibility = ["//visibility:public"], + deps = [ + ":model", + ":model_cmdline_flags", + ":model_flags_proto_cc", + ":toco_cmdline_flags", + ":toco_convert", + ":toco_flags_proto_cc", + ":toco_port", + ":toco_tooling", + ":types_proto_cc", + "@com_google_googletest//:gtest_main", + "@com_google_absl//absl/strings", + "//tensorflow/core:lib", + # We cannot embed the core:ops dependency directly into :toco_tooling as + # it can conflict with downstream deps when toco is used as a library. + "//tensorflow/core:ops", + ], +) + tf_cc_test( name = "toco_port_test", srcs = ["toco_port_test.cc"], diff --git a/tensorflow/lite/toco/README.md b/tensorflow/lite/toco/README.md index 91f6f618a3..fe98a90b38 100644 --- a/tensorflow/lite/toco/README.md +++ b/tensorflow/lite/toco/README.md @@ -8,9 +8,9 @@ the usage documentation. Usage information is given in these documents: -* [Command-line glossary](g3doc/cmdline_reference.md) -* [Command-line examples](g3doc/cmdline_examples.md) -* [Python API examples](g3doc/python_api.md) +* [Command-line glossary](../g3doc/convert/cmdline_reference.md) +* [Command-line examples](../g3doc/convert/cmdline_examples.md) +* [Python API examples](../g3doc/convert/python_api.md) ## Where the converter fits in the TensorFlow landscape @@ -26,4 +26,4 @@ to client devices, generally mobile devices, where the TensorFlow Lite interpreter handles them on-device. This flow is represented in the diagram below. -![drawing](g3doc/toco_landscape.svg) +![drawing](../g3doc/images/convert/workflow.svg) diff --git a/tensorflow/lite/toco/export_tensorflow.cc b/tensorflow/lite/toco/export_tensorflow.cc index 1752745aae..9fff001552 100644 --- a/tensorflow/lite/toco/export_tensorflow.cc +++ b/tensorflow/lite/toco/export_tensorflow.cc @@ -48,7 +48,8 @@ using tensorflow::TensorProto; namespace toco { namespace { -tensorflow::DataType GetTensorFlowDataType(ArrayDataType data_type) { +tensorflow::DataType GetTensorFlowDataType(ArrayDataType data_type, + const string& error_location) { switch (data_type) { case ArrayDataType::kBool: return tensorflow::DT_BOOL; @@ -66,14 +67,21 @@ tensorflow::DataType GetTensorFlowDataType(ArrayDataType data_type) { return tensorflow::DT_COMPLEX64; default: case ArrayDataType::kNone: - LOG(FATAL) << "Unsupported data type: " << static_cast(data_type); + LOG(FATAL) << "Unsupported data type '" << ArrayDataTypeName(data_type) + << "' in " << error_location; return tensorflow::DT_INVALID; } } +tensorflow::DataType GetTensorFlowDataTypeForOp(ArrayDataType data_type, + const string& op_name) { + return GetTensorFlowDataType(data_type, "op '" + op_name + "'"); +} + tensorflow::DataType GetTensorFlowDataType(const Model& model, const string& array_name) { - return GetTensorFlowDataType(model.GetArray(array_name).data_type); + return GetTensorFlowDataType(model.GetArray(array_name).data_type, + "array '" + array_name + "'"); } // TensorFlow sometimes forbids what it calls "legacy scalars", @@ -1150,6 +1158,29 @@ void ConvertSplitOperator(const Model& model, tensorflow_graph); } +void ConvertSplitVOperator(const Model& model, + const TensorFlowSplitVOperator& src_op, + GraphDef* tensorflow_graph) { + tensorflow::NodeDef* split_v_op = tensorflow_graph->add_node(); + split_v_op->set_op("SplitV"); + split_v_op->set_name(src_op.outputs[0]); + for (const auto& input : src_op.inputs) { + *split_v_op->add_input() = input; + } + (*split_v_op->mutable_attr())["T"].set_type( + GetTensorFlowDataType(model, src_op.inputs[0])); + (*split_v_op->mutable_attr())["num_split"].set_i(src_op.num_split); + const auto& split_dim_array = model.GetArray(src_op.inputs[1]); + CHECK(split_dim_array.buffer); + CHECK(split_dim_array.data_type == ArrayDataType::kInt32); + const auto& split_dim_data = + split_dim_array.GetBuffer().data; + CHECK_EQ(split_dim_data.size(), 1); + const int split_dim = split_dim_data[0]; + CreateDummyConcatDimTensorConst(src_op.inputs[0], split_dim, + tensorflow_graph); +} + void ConvertCastOperator(const Model& model, const CastOperator& src_op, GraphDef* tensorflow_graph) { tensorflow::NodeDef* cast_op = tensorflow_graph->add_node(); @@ -1285,7 +1316,7 @@ void ConvertRangeOperator(const Model& model, const RangeOperator& src_op, *range_op->add_input() = src_op.inputs[1]; *range_op->add_input() = src_op.inputs[2]; (*range_op->mutable_attr())["Tidx"].set_type( - GetTensorFlowDataType(src_op.dtype)); + GetTensorFlowDataTypeForOp(src_op.dtype, /*op_name=*/src_op.outputs[0])); } void ConvertPackOperator(const Model& model, const PackOperator& src_op, @@ -1298,7 +1329,8 @@ void ConvertPackOperator(const Model& model, const PackOperator& src_op, } (*pack_op->mutable_attr())["axis"].set_i(src_op.axis); (*pack_op->mutable_attr())["N"].set_i(src_op.inputs.size()); - (*pack_op->mutable_attr())["T"].set_type(GetTensorFlowDataType(src_op.dtype)); + (*pack_op->mutable_attr())["T"].set_type( + GetTensorFlowDataTypeForOp(src_op.dtype, src_op.outputs[0])); } void ConvertFillOperator(const Model& model, const FillOperator& src_op, @@ -1887,7 +1919,7 @@ void ConvertRandomUniformOperator(const Model& model, GetTensorFlowDataType(model, src_op.inputs[0]); (*new_op->mutable_attr())["T"].set_type(shape_type); (*new_op->mutable_attr())["dtype"].set_type( - GetTensorFlowDataType(src_op.dtype)); + GetTensorFlowDataTypeForOp(src_op.dtype, src_op.outputs[0])); (*new_op->mutable_attr())["seed"].set_i(src_op.seed); (*new_op->mutable_attr())["seed2"].set_i(src_op.seed2); } @@ -2124,6 +2156,10 @@ void ConvertOperator(const Model& model, const Operator& src_op, ConvertSplitOperator(model, static_cast(src_op), tensorflow_graph); + } else if (src_op.type == OperatorType::kSplitV) { + ConvertSplitVOperator(model, + static_cast(src_op), + tensorflow_graph); } else if (src_op.type == OperatorType::kFakeQuant) { ConvertFakeQuantOperator(static_cast(src_op), tensorflow_graph); diff --git a/tensorflow/lite/toco/graph_transformations/fuse_binary_into_following_affine.cc b/tensorflow/lite/toco/graph_transformations/fuse_binary_into_following_affine.cc index 6b4765b23c..436b639253 100644 --- a/tensorflow/lite/toco/graph_transformations/fuse_binary_into_following_affine.cc +++ b/tensorflow/lite/toco/graph_transformations/fuse_binary_into_following_affine.cc @@ -221,9 +221,8 @@ void FuseMulOrDivParamsIntoFollowingAffine(Model* model, Operator* following_op, Operator* following_op = GetOpWithInput(*model, binary_op->outputs[0]); if (!following_op) { - AddMessageF( - "Not fusing %s because it is not consumed by exactly one other op", - LogName(*binary_op)); + AddMessageF("Not fusing %s because it is not consumed by any op", + LogName(*binary_op)); return ::tensorflow::Status::OK(); } @@ -288,7 +287,10 @@ void FuseMulOrDivParamsIntoFollowingAffine(Model* model, Operator* following_op, AddMessageF("Fusing %s into the following %s", LogName(*binary_op), LogName(*following_op)); - model->EraseArray(binary_op->outputs[0]); + if (CountOpsWithInput(*model, binary_op->outputs[0]) == 1) { + model->EraseArray(binary_op->outputs[0]); + } + following_op->inputs[0] = binary_op->inputs[index_of_variable_input]; const auto& old_constant_param_name = binary_op->inputs[index_of_constant_input]; diff --git a/tensorflow/lite/toco/graph_transformations/propagate_array_data_types.cc b/tensorflow/lite/toco/graph_transformations/propagate_array_data_types.cc index 9a458dccb9..cbae6610d7 100644 --- a/tensorflow/lite/toco/graph_transformations/propagate_array_data_types.cc +++ b/tensorflow/lite/toco/graph_transformations/propagate_array_data_types.cc @@ -86,6 +86,13 @@ void SetDataTypeForAllOutputs(Model* model, Operator* op, SetDataTypeForAllOutputs(model, op, data_type); break; } + case OperatorType::kSplitV: { + // These operators produce output with the same type as its 1st input + CHECK_GE(op->inputs.size(), 3); + const ArrayDataType data_type = model->GetArray(op->inputs[0]).data_type; + SetDataTypeForAllOutputs(model, op, data_type); + break; + } case OperatorType::kTransposeConv: { // These operators produce an output with the same type as their 3rd input CHECK_GE(op->inputs.size(), 3); diff --git a/tensorflow/lite/toco/graph_transformations/propagate_fixed_sizes.cc b/tensorflow/lite/toco/graph_transformations/propagate_fixed_sizes.cc index 78ea54e452..0e653f08a0 100644 --- a/tensorflow/lite/toco/graph_transformations/propagate_fixed_sizes.cc +++ b/tensorflow/lite/toco/graph_transformations/propagate_fixed_sizes.cc @@ -15,6 +15,7 @@ limitations under the License. #include #include #include +#include #include #include #include @@ -786,6 +787,97 @@ void ProcessTensorFlowSplitOperator(Model* model, TensorFlowSplitOperator* op) { } } +void ProcessTensorFlowSplitVOperator(Model* model, + TensorFlowSplitVOperator* op) { + CHECK_EQ(op->inputs.size(), 3); + + const auto& input_array = model->GetArray(op->inputs[0]); + // Yield until input dims have been resolved. + if (!input_array.has_shape()) { + return; + } + const Shape& input_shape = input_array.shape(); + + // Yield until size_splits is constant. + if (!IsConstantParameterArray(*model, op->inputs[1])) { + return; + } + const auto& size_array = model->GetArray(op->inputs[1]); + // Yield until size_splits dims have been resolved. + if (!size_array.has_shape()) { + return; + } + const Shape& size_shape = size_array.shape(); + + CHECK(size_array.data_type == ArrayDataType::kInt32 || + size_array.data_type == ArrayDataType::kInt64) + << "size_splits must be int32, int64"; + CHECK_EQ(size_shape.dimensions_count(), 1) << "size_splits must be 1-D"; + + std::vector size_splits_vector; + if (size_array.data_type == ArrayDataType::kInt32) { + for (const auto each_size : + size_array.GetBuffer().data) { + size_splits_vector.push_back(each_size); + } + } else { + size_splits_vector = size_array.GetBuffer().data; + } + + // Yield until axis is constant. + if (!IsConstantParameterArray(*model, op->inputs[2])) { + return; + } + const auto& axis_array = model->GetArray(op->inputs[2]); + // Yield until axis dims have been resolved. + if (!axis_array.has_shape()) { + return; + } + + CHECK(axis_array.data_type == ArrayDataType::kInt32) + << "Axis array must be int32."; + CHECK_EQ(RequiredBufferSizeForShape(axis_array.shape()), 1) + << "Axis array must be scalar."; + + int axis = axis_array.GetBuffer().data[0]; + if (axis < 0) { + axis += input_shape.dimensions_count(); + } + + CHECK_EQ(op->num_split, size_splits_vector.size()); + + int64_t minus_one_count = 0, size_splits_sum = 0; + for (auto size : size_splits_vector) { + if (size == -1) { + ++minus_one_count; + } else { + size_splits_sum += size; + } + } + + const int input_size = input_shape.dims(axis); + + CHECK_LE(minus_one_count, 1) << "size_splits can contain at most one -1."; + + if (minus_one_count == 1) { + CHECK_LE(size_splits_sum, input_size); + auto iter = + std::find(size_splits_vector.begin(), size_splits_vector.end(), -1); + *iter = input_size - size_splits_sum; + } else { + CHECK_EQ(size_splits_sum, input_size); + } + + CHECK_EQ(op->outputs.size(), op->num_split); + + for (int i = 0; i < op->outputs.size(); ++i) { + const auto& output = op->outputs[i]; + Shape output_shape = input_shape; + (*output_shape.mutable_dims())[axis] = size_splits_vector.at(i); + model->GetArray(output).copy_shape(output_shape); + } +} + void ProcessAveragePoolOperator(Model* model, AveragePoolOperator* op) { const string& input_name = op->inputs[0]; const auto& input_array = model->GetArray(input_name); @@ -1691,6 +1783,51 @@ void ProcessUnpackOperator(Model* model, UnpackOperator* op) { } } +void ProcessMirrorPadOperator(Model* model, MirrorPadOperator* op) { + CHECK_EQ(op->inputs.size(), 2); + const auto& input_array = model->GetArray(op->inputs[0]); + const auto& padding_matrix = model->GetArray(op->inputs[1]); + + // Yield until input dims have been resolved. + if (!input_array.has_shape()) { + return; + } + + auto& output_array = model->GetArray(op->outputs[0]); + // If output already computed or padding matrix is non + // const then return. + if (output_array.has_shape() || + !IsConstantParameterArray(*model, op->inputs[1])) { + return; + } + Shape output_shape = input_array.shape(); + std::vector& dims = *output_shape.mutable_dims(); + + std::vector padding; + if (padding_matrix.data_type == ArrayDataType::kInt32) { + const auto& data = padding_matrix.GetBuffer().data; + for (auto elem : data) { + padding.push_back(static_cast(elem)); + } + } else if (padding_matrix.data_type == ArrayDataType::kInt64) { + const auto& data = padding_matrix.GetBuffer().data; + for (auto elem : data) { + padding.push_back(elem); + } + } else { + CHECK(padding_matrix.data_type == ArrayDataType::kInt64 || + padding_matrix.data_type == ArrayDataType::kInt32); + } + CHECK_EQ(padding_matrix.shape().dimensions_count(), 2); + CHECK_EQ(input_array.shape().dimensions_count(), + padding_matrix.shape().dims(0)); + for (int i = 0; i < input_array.shape().dimensions_count(); ++i) { + dims[i] += padding[i * 2] + padding[i * 2 + 1]; + } + + output_array.copy_shape(output_shape); +} + } // namespace ::tensorflow::Status PropagateFixedSizes::Run(Model* model, @@ -1707,6 +1844,7 @@ void ProcessUnpackOperator(Model* model, UnpackOperator* op) { } switch (op->type) { + case OperatorType::kAbs: case OperatorType::kBatchNormalization: case OperatorType::kL2Normalization: case OperatorType::kDequantize: @@ -1714,6 +1852,7 @@ void ProcessUnpackOperator(Model* model, UnpackOperator* op) { case OperatorType::kRelu1: case OperatorType::kRelu6: case OperatorType::kPRelu: + case OperatorType::kLeakyRelu: case OperatorType::kSoftmax: case OperatorType::kLogSoftmax: case OperatorType::kLog: @@ -1759,6 +1898,7 @@ void ProcessUnpackOperator(Model* model, UnpackOperator* op) { case OperatorType::kEqual: case OperatorType::kNotEqual: case OperatorType::kPow: + case OperatorType::kSquaredDifference: ProcessSimpleBinaryOperator(model, op); break; case OperatorType::kAddN: @@ -1834,6 +1974,10 @@ void ProcessUnpackOperator(Model* model, UnpackOperator* op) { ProcessTensorFlowSplitOperator(model, static_cast(op)); break; + case OperatorType::kSplitV: + ProcessTensorFlowSplitVOperator( + model, static_cast(op)); + break; case OperatorType::kSqueeze: ProcessSqueezeOperator(model, static_cast(op)); break; @@ -1956,6 +2100,9 @@ void ProcessUnpackOperator(Model* model, UnpackOperator* op) { case OperatorType::kUnpack: ProcessUnpackOperator(model, static_cast(op)); break; + case OperatorType::kMirrorPad: + ProcessMirrorPadOperator(model, static_cast(op)); + break; default: // Unimplemented, another graph transformation should drop it. LOG(FATAL) << "Unhandled operator type " << OperatorTypeName(op->type); diff --git a/tensorflow/lite/toco/graph_transformations/quantize.cc b/tensorflow/lite/toco/graph_transformations/quantize.cc index e28b7288f0..1146078c30 100644 --- a/tensorflow/lite/toco/graph_transformations/quantize.cc +++ b/tensorflow/lite/toco/graph_transformations/quantize.cc @@ -64,7 +64,8 @@ bool SupportsQuantization(const Operator& op) { type == OperatorType::kRelu1 || type == OperatorType::kRelu6 || type == OperatorType::kShape || type == OperatorType::kExpandDims || type == OperatorType::kPack || type == OperatorType::kTopK_V2 || - type == OperatorType::kResizeNearestNeighbor; + type == OperatorType::kResizeNearestNeighbor || + type == OperatorType::kPRelu; } // The quantized op allows output arrays of type float using @@ -360,7 +361,7 @@ bool ChooseQuantizationForOperatorOutput( op.type == OperatorType::kSpaceToDepth || op.type == OperatorType::kReshape || op.type == OperatorType::kSplit || op.type == OperatorType::kRelu || op.type == OperatorType::kRelu1 || - op.type == OperatorType::kRelu6) { + op.type == OperatorType::kRelu6 || op.type == OperatorType::kPRelu) { int data_input_index = 0; if (op.type == OperatorType::kSplit) { data_input_index = 1; diff --git a/tensorflow/lite/toco/graph_transformations/resolve_constant_gather.cc b/tensorflow/lite/toco/graph_transformations/resolve_constant_gather.cc index 1149930131..c72135923e 100644 --- a/tensorflow/lite/toco/graph_transformations/resolve_constant_gather.cc +++ b/tensorflow/lite/toco/graph_transformations/resolve_constant_gather.cc @@ -47,6 +47,10 @@ inline void Gather(const Array& input_array, int input_rank, stride *= input_shape.dims(i); } + // Let's make sure we have enough space for all element in the memcpy() + // below, which writes 'stride' elements startng at 'i * stride'. + CHECK_EQ(stride * coords_shape.dims(0), output_data.size()); + for (int i = 0; i < coords_shape.dims(0); ++i) { DCHECK_GE(coords_data[i], 0); DCHECK_LT(coords_data[i], input_shape.dims(rev_input_rank)); diff --git a/tensorflow/lite/toco/graph_transformations/resolve_reduce_attributes.cc b/tensorflow/lite/toco/graph_transformations/resolve_reduce_attributes.cc index ea5d33009b..9ceba45e93 100644 --- a/tensorflow/lite/toco/graph_transformations/resolve_reduce_attributes.cc +++ b/tensorflow/lite/toco/graph_transformations/resolve_reduce_attributes.cc @@ -35,6 +35,11 @@ bool ResolveAttributes(Model* model, T* op) { const Array& indices_array = model->GetArray(op->inputs[1]); if (!indices_array.has_shape()) return false; + + // It is ok for indices_array to have a shape for an empty tensor. In that + // case, we don't bother setting 'axis'. + if (indices_array.buffer->Length() == 0) return false; + op->axis = indices_array.GetBuffer().data; return true; } diff --git a/tensorflow/lite/toco/graph_transformations/unroll_batch_matmul.cc b/tensorflow/lite/toco/graph_transformations/unroll_batch_matmul.cc index d59954fc74..41a735394d 100644 --- a/tensorflow/lite/toco/graph_transformations/unroll_batch_matmul.cc +++ b/tensorflow/lite/toco/graph_transformations/unroll_batch_matmul.cc @@ -117,7 +117,8 @@ namespace toco { auto* slice_b_op = new SliceOperator; slice_b_op->inputs = { batch_op->inputs[1], - CreateInt32Array(model, batch_name + "/slice_b/slice/begin", {0, 0, 0}), + CreateInt32Array(model, batch_name + "/slice_b/slice/begin", + {batch, 0, 0}), CreateInt32Array( model, batch_name + "/slice_b/slice/size", {1, input_array_b.shape().dims(1), input_array_b.shape().dims(2)}), diff --git a/tensorflow/lite/toco/import_tensorflow.cc b/tensorflow/lite/toco/import_tensorflow.cc index 96f3c6a6ab..0b2f810394 100644 --- a/tensorflow/lite/toco/import_tensorflow.cc +++ b/tensorflow/lite/toco/import_tensorflow.cc @@ -935,6 +935,25 @@ tensorflow::Status ConvertSplitOperator( return tensorflow::Status::OK(); } +tensorflow::Status ConvertSplitVOperator( + const NodeDef& node, const TensorFlowImportFlags& tf_import_flags, + Model* model) { + CHECK_EQ(node.op(), "SplitV"); + TF_QCHECK_OK(CheckInputsCount(node, tf_import_flags, 3)); + auto* op = new TensorFlowSplitVOperator; + op->inputs.push_back(node.input(0)); + op->inputs.push_back(node.input(1)); + op->inputs.push_back(node.input(2)); + const int num_split = GetIntAttr(node, "num_split"); + op->outputs.push_back(node.name()); + for (int i = 1; i < num_split; i++) { + op->outputs.push_back(absl::StrCat(node.name(), ":", i)); + } + op->num_split = num_split; + model->operators.emplace_back(op); + return tensorflow::Status::OK(); +} + tensorflow::Status ConvertSwitchOperator( const NodeDef& node, const TensorFlowImportFlags& tf_import_flags, Model* model) { @@ -1134,6 +1153,31 @@ tensorflow::Status ConvertConcatOperator( return tensorflow::Status::OK(); } +tensorflow::Status ConvertMirrorPadOperator( + const NodeDef& node, const TensorFlowImportFlags& tf_import_flags, + Model* model) { + if (node.op() != "MirrorPad") { + LOG(FATAL) << "Expected MirrorPad."; + } + const int num_inputs = GetInputsCount(node, tf_import_flags); + CHECK_EQ(num_inputs, 2); + auto* op = new MirrorPadOperator; + for (int i = 0; i < num_inputs; ++i) { + op->inputs.push_back(node.input(i)); + } + op->outputs.push_back(node.name()); + const auto mode = GetStringAttr(node, "mode"); + if (mode == "REFLECT") { + op->mode = toco::MirrorPadMode::kReflect; + } else if (mode == "SYMMETRIC") { + op->mode = toco::MirrorPadMode::kSymmetric; + } + + model->operators.emplace_back(op); + + return tensorflow::Status::OK(); +} + static constexpr int kAnyNumInputs = -1; enum FlexSupport { kFlexOk, kFlexNotOk }; @@ -1221,7 +1265,7 @@ void GetOutputNamesFromNodeDef(const NodeDef& node, void GetOutputTypesFromNodeDef(const NodeDef& node, const tensorflow::OpDef& op_def, TensorFlowUnsupportedOperator* op) { - // The the given type to the op, or clear the types if invalid. + // The given type to the op, or clear the types if invalid. auto add_type = [&node, op](tensorflow::DataType type) { if (type == tensorflow::DT_INVALID) { LOG(WARNING) << "Op node missing output type attribute: " << node.name(); @@ -2012,13 +2056,13 @@ bool InlineAllFunctions(GraphDef* graphdef) { tensorflow::SessionOptions options; auto* device_count = options.config.mutable_device_count(); device_count->insert({"CPU", 1}); - std::vector devices; + std::vector> devices; TF_CHECK_OK(tensorflow::DeviceFactory::AddDevices( options, "/job:localhost/replica:0/task:0", &devices)); tensorflow::FunctionLibraryDefinition fld(tensorflow::OpRegistry::Global(), graphdef_copy.library()); - tensorflow::DeviceMgr device_mgr(devices); + tensorflow::DeviceMgr device_mgr(std::move(devices)); tensorflow::OptimizerOptions o_opts; tensorflow::ProcessFunctionLibraryRuntime pflr( &device_mgr, tensorflow::Env::Default(), TF_GRAPH_DEF_VERSION, &fld, @@ -2220,6 +2264,21 @@ tensorflow::Status ConvertUnidirectionalSequenceLstm( return tensorflow::Status::OK(); } +tensorflow::Status ConvertLeakyReluOperator( + const NodeDef& node, const TensorFlowImportFlags& tf_import_flags, + Model* model) { + CHECK_EQ(node.op(), "LeakyRelu"); + TF_QCHECK_OK(CheckInputsCount(node, tf_import_flags, 1)); + CHECK_EQ(GetDataTypeAttr(node, "T"), DT_FLOAT); + const auto& input_name = node.input(0); + auto* op = new LeakyReluOperator; + op->inputs.push_back(input_name); + op->outputs.push_back(node.name()); + op->alpha = GetFloatAttr(node, "alpha"); + model->operators.emplace_back(op); + return tensorflow::Status::OK(); +} + } // namespace namespace internal { @@ -2233,12 +2292,14 @@ ConverterMapType GetTensorFlowNodeConverterMapForFlex() { return std::unordered_map({ // We need to let TCO convert Placeholder information into // array data, so that the data types are correct. + {"LegacyFedInput", ConvertPlaceholderOperator}, {"Placeholder", ConvertPlaceholderOperator}, }); } ConverterMapType GetTensorFlowNodeConverterMap() { return std::unordered_map({ + {"Abs", ConvertSimpleOperator}, {"Add", ConvertSimpleOperator}, {"AddN", ConvertSimpleOperatorFlexOk}, {"All", ConvertSimpleOperator}, @@ -2282,6 +2343,7 @@ ConverterMapType GetTensorFlowNodeConverterMap() { ConvertSimpleOperator}, {"Identity", ConvertIdentityOperator}, {"LRN", ConvertLRNOperator}, + {"LeakyRelu", ConvertLeakyReluOperator}, {"LegacyFedInput", ConvertPlaceholderOperator}, {"Less", ConvertSimpleOperator}, {"LessEqual", ConvertSimpleOperator}, @@ -2332,8 +2394,11 @@ ConverterMapType GetTensorFlowNodeConverterMap() { {"SpaceToDepth", ConvertSpaceToDepthOperator}, {"SparseToDense", ConvertSparseToDenseOperator}, {"Split", ConvertSplitOperator}, + {"SplitV", ConvertSplitVOperator}, {"Sqrt", ConvertSimpleOperator}, {"Square", ConvertSimpleOperator}, + {"SquaredDifference", + ConvertSimpleOperator}, {"Squeeze", ConvertSqueezeOperator}, {"StopGradient", ConvertIdentityOperator}, {"StridedSlice", ConvertStridedSliceOperator}, @@ -2349,6 +2414,7 @@ ConverterMapType GetTensorFlowNodeConverterMap() { {"Unpack", ConvertUnpackOperator}, {"ZerosLike", ConvertSimpleOperator}, {"UnidirectionalSequenceLstm", ConvertUnidirectionalSequenceLstm}, + {"MirrorPad", ConvertMirrorPadOperator}, }); } diff --git a/tensorflow/lite/toco/model.h b/tensorflow/lite/toco/model.h index f85e1c2878..f19355968f 100644 --- a/tensorflow/lite/toco/model.h +++ b/tensorflow/lite/toco/model.h @@ -121,8 +121,10 @@ enum class OperatorType : uint8 { kRsqrt, kShape, kSplit, + kSplitV, kSqrt, kSquare, + kSquaredDifference, kSum, kSwitch, kTile, @@ -152,7 +154,10 @@ enum class OperatorType : uint8 { kCTCBeamSearchDecoder, kUnpack, kZerosLike, - kResizeNearestNeighbor + kResizeNearestNeighbor, + kLeakyRelu, + kAbs, + kMirrorPad }; // Helper to deal with TensorFlow arrays using a different ordering of @@ -653,6 +658,17 @@ struct MulOperator : Operator { MulOperator() : Operator(OperatorType::kMul) {} }; +// Element-wise Abs operator: +// x -> abs(x) +// +// Inputs: +// inputs[0]: required: the input array +// +// TensorFlow equivalent: Relu +struct AbsOperator : Operator { + AbsOperator() : Operator(OperatorType::kAbs) {} +}; + // Element-wise Relu operator: // x -> max(0, x) // @@ -699,6 +715,19 @@ struct PReluOperator : Operator { PReluOperator() : Operator(OperatorType::kPRelu) {} }; +// LeakyRelu +// x -> max(x, alpha * x) +// +// Inputs: +// inputs[0]: required: the input array +// +// TensorFlow equivalent: LeakyRelu +struct LeakyReluOperator : Operator { + LeakyReluOperator() : Operator(OperatorType::kLeakyRelu) {} + + float alpha = 0.2f; // 0.2 matches the default value for the TF op attribute. +}; + // Element-wise Logistic operator: // x -> Logistic(x) = 1 / (1 + exp(-x)) // @@ -1289,6 +1318,17 @@ struct TensorFlowSquareOperator : Operator { TensorFlowSquareOperator() : Operator(OperatorType::kSquare) {} }; +// Element-wise squared difference ((x-y)*(x-y)) operator. +// +// Inputs: +// inputs[0]: required: the left-hand side array +// inputs[1]: required: the right-hand side array +// +// TensorFlow equivalent: SquaredDifference +struct SquaredDifferenceOperator : Operator { + SquaredDifferenceOperator() : Operator(OperatorType::kSquaredDifference) {} +}; + // Transposes a tensor. // // By default, this operation performs a regular matrix transpose on 2-D input @@ -1363,6 +1403,12 @@ struct TensorFlowSplitOperator : Operator { int num_split = 0; }; +// TensorFlow SplitV equivalent. Refer to TensorFlow documentation for details. +struct TensorFlowSplitVOperator : Operator { + TensorFlowSplitVOperator() : Operator(OperatorType::kSplitV) {} + int num_split = 0; +}; + // TensorFlow Concat equivalent. Refer to TensorFlow documentation for details. // Not fully supported, just a placeholder to handle TensorFlow graphs and // support graph transformations to other operator types by matching sub-graphs. @@ -1887,6 +1933,23 @@ struct TensorFlowZerosLikeOperator : Operator { TensorFlowZerosLikeOperator() : Operator(OperatorType::kZerosLike) {} }; +enum class MirrorPadMode { kNone, kSymmetric, kReflect }; + +// MirrorPad Operator: +// +// Inputs: +// Inputs[0]: required: input tensor to be padded. +// Inputs[1]: required: 2 Column matrix specifying padding sizes. The number of +// rows must be the same as the rank of the input. +// Inputs[2]: required: REFLECT or SYMMETRIC. +// +// TensorFlow equivalent: MirrorPad. +struct MirrorPadOperator : Operator { + MirrorPadOperator() : Operator(OperatorType::kMirrorPad) {} + // mode is either SYMMETRIC or REFLECT. + MirrorPadMode mode; +}; + // Alloc's are used for transient arrays only. An Alloc specifies which interval // of the "transient_data" workspace buffer passed to inference functions, is to // be used for the transient array at hand. The 'start' and 'end' values are diff --git a/tensorflow/lite/toco/tflite/export.cc b/tensorflow/lite/toco/tflite/export.cc index 489c21295e..8b9448486d 100644 --- a/tensorflow/lite/toco/tflite/export.cc +++ b/tensorflow/lite/toco/tflite/export.cc @@ -126,7 +126,6 @@ OperatorKey::OperatorKey( type_ = builtin_ops.at(name); return; } - // The logic below is all for custom ops or Flex ops. is_custom_op_ = true; type_ = BuiltinOperator_CUSTOM; @@ -332,6 +331,11 @@ Offset>> ExportOperators( std::set* variable_tensor_indices, const ExportParams& params) { variable_tensor_indices->clear(); + auto is_tflite_builtin = [](const BaseOperator* op) { + const auto& tflite_builtins = GetBuiltinOpsMap(); + return (op && tflite_builtins.find(op->name()) != tflite_builtins.end()); + }; + // The operators are in execution order, so we just follow tf.mini order. std::vector> op_vector; for (const auto& op : model.operators) { @@ -360,7 +364,19 @@ Offset>> ExportOperators( auto options = Options::Custom(0); std::vector mutating_input_variables; - if (tflite_op) { + + // It is conceivable that an op is exportable via Serialize() but does not + // have a corresponding TFLITE builtin. In that case, when flex mode is + // enabled we should export it as a flex op, not as a native. + bool export_as_flex_op = !is_tflite_builtin(tflite_op) && + key.is_flex_op() && + !op->tensorflow_node_def.empty(); + if (export_as_flex_op) { + auto fbb = WriteFlexOpOptions(op->tensorflow_node_def); + if (fbb) { + options = Options::Custom(builder->CreateVector(fbb->GetBuffer())); + } + } else if (tflite_op) { options = tflite_op->Serialize(*op, builder); mutating_input_variables = tflite_op->GetMutatingInputVariables(*op); @@ -373,12 +389,13 @@ Offset>> ExportOperators( variable_tensor_indices->insert(variable_tensor_index); } } - } else if (key.is_flex_op() && !op->tensorflow_node_def.empty()) { - auto fbb = WriteFlexOpOptions(op->tensorflow_node_def); - if (fbb) { - options = Options::Custom(builder->CreateVector(fbb->GetBuffer())); - } + } else { + // We don't know much about this op. It doesn't have a serializer and + // it is not supposed to be exported as a flex op. We will treat it as + // a regular custom op: we will still create an operator for it, but it + // will not have any 'options'. } + // The only supported CustomOptionFormat is FLEXBUFFERS now. op_vector.push_back(CreateOperator( *builder, op_index, builder->CreateVector(inputs), diff --git a/tensorflow/lite/toco/tflite/export_test.cc b/tensorflow/lite/toco/tflite/export_test.cc index b6c67772ac..b371296784 100644 --- a/tensorflow/lite/toco/tflite/export_test.cc +++ b/tensorflow/lite/toco/tflite/export_test.cc @@ -46,6 +46,18 @@ class ExportTest : public ::testing::Test { input_model_.operators.emplace_back(new AddOperator); } else if (name == "Sub") { input_model_.operators.emplace_back(new SubOperator); + } else if (name == "Assert") { + auto* op = new TensorFlowAssertOperator; + + // Even though assert is known to TOCO, it doesn't have a tflite + // serializer, so it has to be exported as a custom op. If we attach a + // NodeDef to it, however, it will be exported as a flex op instead. + ::tensorflow::NodeDef node_def; + node_def.set_name("Assert"); + node_def.set_op("Assert"); + node_def.SerializeToString(&op->tensorflow_node_def); + + input_model_.operators.emplace_back(op); } else { auto* op = new TensorFlowUnsupportedOperator; op->tensorflow_op = name; @@ -232,37 +244,38 @@ class OpSetsTest : public ExportTest { TEST_F(OpSetsTest, BuiltinsOnly) { // --target_op_set=TFLITE_BUILTINS SetAllowedOpSets({kTfLiteBuiltins}); - EXPECT_THAT(ImportExport({"Add", "AdjustHue", "UnrollAndFold"}), + EXPECT_THAT(ImportExport({"Add", "AdjustHue", "UnrollAndFold", "Assert"}), ElementsAre()); EXPECT_THAT(ImportExport({"Add"}), ElementsAre("builtin:ADD")); // --target_op_set=TFLITE_BUILTINS --allow_custom_ops SetAllowedOpSets({kTfLiteBuiltins, kCustomOps}); - EXPECT_THAT( - ImportExport({"Add", "AdjustHue", "UnrollAndFold"}), - ElementsAre("builtin:ADD", "custom:AdjustHue", "custom:UnrollAndFold")); + EXPECT_THAT(ImportExport({"Add", "AdjustHue", "UnrollAndFold", "Assert"}), + ElementsAre("builtin:ADD", "custom:AdjustHue", "custom:Assert", + "custom:UnrollAndFold")); } TEST_F(OpSetsTest, TfSelectOnly) { // --target_op_set=SELECT_TF_OPS SetAllowedOpSets({kSelectTfOps}); - EXPECT_THAT( - ImportExport({"Add", "AdjustHue", "RandomUniform", "UnrollAndFold"}), - ElementsAre()); + EXPECT_THAT(ImportExport({"Add", "AdjustHue", "RandomUniform", + "UnrollAndFold", "Assert"}), + ElementsAre()); EXPECT_THAT(ImportExport({"Add"}), ElementsAre("custom:FlexAdd")); // --target_op_set=SELECT_TF_OPS --allow_custom_ops SetAllowedOpSets({kSelectTfOps, kCustomOps}); EXPECT_THAT( - ImportExport({"Add", "AdjustHue", "RandomUniform", "UnrollAndFold"}), - ElementsAre("custom:AdjustHue", "custom:FlexAdd", + ImportExport( + {"Add", "AdjustHue", "RandomUniform", "UnrollAndFold", "Assert"}), + ElementsAre("custom:AdjustHue", "custom:FlexAdd", "custom:FlexAssert", "custom:FlexRandomUniform", "custom:UnrollAndFold")); } TEST_F(OpSetsTest, BuiltinsAndTfSelect) { // --target_op_set=TFLITE_BUILTINS,SELECT_TF_OPS SetAllowedOpSets({kTfLiteBuiltins, kSelectTfOps}); - EXPECT_THAT(ImportExport({"Add", "AdjustHue", "UnrollAndFold"}), + EXPECT_THAT(ImportExport({"Add", "AdjustHue", "UnrollAndFold", "Assert"}), ElementsAre()); EXPECT_THAT(ImportExport({"Add", "RandomUniform"}), ElementsAre("builtin:ADD", "custom:FlexRandomUniform")); @@ -270,9 +283,10 @@ TEST_F(OpSetsTest, BuiltinsAndTfSelect) { // --target_op_set=TFLITE_BUILTINS,SELECT_TF_OPS --allow_custom_ops SetAllowedOpSets({kTfLiteBuiltins, kSelectTfOps, kCustomOps}); EXPECT_THAT( - ImportExport({"Add", "AdjustHue", "RandomUniform", "UnrollAndFold"}), - ElementsAre("builtin:ADD", "custom:AdjustHue", "custom:FlexRandomUniform", - "custom:UnrollAndFold")); + ImportExport( + {"Add", "AdjustHue", "RandomUniform", "UnrollAndFold", "Assert"}), + ElementsAre("builtin:ADD", "custom:AdjustHue", "custom:FlexAssert", + "custom:FlexRandomUniform", "custom:UnrollAndFold")); } // This test is based on a hypothetical scenario that dilation is supported diff --git a/tensorflow/lite/toco/tflite/operator.cc b/tensorflow/lite/toco/tflite/operator.cc index 84ae448246..e0faed4927 100644 --- a/tensorflow/lite/toco/tflite/operator.cc +++ b/tensorflow/lite/toco/tflite/operator.cc @@ -978,6 +978,26 @@ class Split int GetVersion(const Operator& op) const override { return 1; } }; +class SplitV + : public BuiltinOperator { + public: + using BuiltinOperator::BuiltinOperator; + + flatbuffers::Offset WriteOptions( + const TocoOperator& op, + flatbuffers::FlatBufferBuilder* builder) const override { + return ::tflite::CreateSplitVOptions(*builder, op.num_split); + } + + void ReadOptions(const TfLiteOptions& options, + TocoOperator* op) const override { + op->num_split = options.num_splits(); + } + + int GetVersion(const Operator& op) const override { return 1; } +}; + class StridedSlice : public BuiltinOperator { + public: + using BuiltinOperator::BuiltinOperator; + flatbuffers::Offset WriteOptions( + const TocoOperator& op, + flatbuffers::FlatBufferBuilder* builder) const override { + return ::tflite::CreateLeakyReluOptions(*builder, op.alpha); + } + void ReadOptions(const TfLiteOptions& options, + TocoOperator* op) const override { + op->alpha = options.alpha(); + } + + int GetVersion(const Operator& op) const override { return 1; } +}; + +class SquaredDifference + : public BuiltinOperator< + SquaredDifferenceOperator, ::tflite::SquaredDifferenceOptions, + ::tflite::BuiltinOptions_SquaredDifferenceOptions> { + public: + using BuiltinOperator::BuiltinOperator; + + flatbuffers::Offset WriteOptions( + const TocoOperator& op, + flatbuffers::FlatBufferBuilder* builder) const override { + return ::tflite::CreateSquaredDifferenceOptions(*builder); + } + + void ReadOptions(const TfLiteOptions& options, + TocoOperator* op) const override {} + + int GetVersion(const Operator& op) const override { return 1; } +}; + +class MirrorPad + : public BuiltinOperator { + public: + using BuiltinOperator::BuiltinOperator; + flatbuffers::Offset WriteOptions( + const TocoOperator& op, + flatbuffers::FlatBufferBuilder* builder) const override { + return ::tflite::CreateMirrorPadOptions( + *builder, op.mode == MirrorPadMode::kReflect + ? ::tflite::MirrorPadMode::MirrorPadMode_REFLECT + : ::tflite::MirrorPadMode::MirrorPadMode_SYMMETRIC); + } + void ReadOptions(const TfLiteOptions& options, + TocoOperator* op) const override { + op->mode = options.mode() == ::tflite::MirrorPadMode::MirrorPadMode_REFLECT + ? MirrorPadMode::kReflect + : MirrorPadMode::kSymmetric; + } + + int GetVersion(const Operator& op) const override { return 1; } +}; + std::unique_ptr WriteFlexOpOptions( const string& tensorflow_node_def) { auto fbb = absl::make_unique(); @@ -1447,6 +1527,7 @@ std::vector> BuildOperatorList( OperatorType::kMaxPool)); ops.push_back( MakeUnique(::tflite::BuiltinOperator_MUL, OperatorType::kMul)); + ops.push_back( MakeUnique(::tflite::BuiltinOperator_PAD, OperatorType::kPad)); ops.push_back( @@ -1483,6 +1564,8 @@ std::vector> BuildOperatorList( OperatorType::kSqueeze)); ops.push_back( MakeUnique(::tflite::BuiltinOperator_SPLIT, OperatorType::kSplit)); + ops.push_back(MakeUnique(::tflite::BuiltinOperator_SPLIT_V, + OperatorType::kSplitV)); ops.push_back(MakeUnique( ::tflite::BuiltinOperator_STRIDED_SLICE, OperatorType::kStridedSlice)); ops.push_back(MakeUnique(::tflite::BuiltinOperator_TOPK_V2, @@ -1516,6 +1599,13 @@ std::vector> BuildOperatorList( OperatorType::kOneHot)); ops.push_back(MakeUnique(::tflite::BuiltinOperator_UNPACK, OperatorType::kUnpack)); + ops.push_back(MakeUnique(::tflite::BuiltinOperator_LEAKY_RELU, + OperatorType::kLeakyRelu)); + ops.push_back(MakeUnique( + ::tflite::BuiltinOperator_SQUARED_DIFFERENCE, + OperatorType::kSquaredDifference)); + ops.push_back(MakeUnique(::tflite::BuiltinOperator_MIRROR_PAD, + OperatorType::kMirrorPad)); // Custom Operators. ops.push_back( @@ -1600,7 +1690,10 @@ std::vector> BuildOperatorList( "SQUARE", OperatorType::kSquare)); ops.push_back(MakeUnique>( "ZEROS_LIKE", OperatorType::kZerosLike)); - + ops.push_back( + MakeUnique>("ABS", OperatorType::kAbs)); + ops.push_back( + MakeUnique>("FILL", OperatorType::kFill)); return ops; } } // namespace diff --git a/tensorflow/lite/toco/tflite/operator_test.cc b/tensorflow/lite/toco/tflite/operator_test.cc index 8a776cbf0b..14ec89cd73 100644 --- a/tensorflow/lite/toco/tflite/operator_test.cc +++ b/tensorflow/lite/toco/tflite/operator_test.cc @@ -151,6 +151,7 @@ TEST_F(OperatorTest, SimpleOperators) { OperatorType::kZerosLike); CheckSimpleOperator("FLOOR_MOD", OperatorType::kFloorMod); CheckSimpleOperator("RANGE", OperatorType::kRange); + CheckSimpleOperator("FILL", OperatorType::kFill); } TEST_F(OperatorTest, BuiltinAdd) { @@ -310,6 +311,14 @@ TEST_F(OperatorTest, CustomSplit) { EXPECT_EQ(op.num_split, output_toco_op->num_split); } +TEST_F(OperatorTest, CustomSplitV) { + TensorFlowSplitVOperator op; + op.num_split = 123; + auto output_toco_op = SerializeAndDeserialize( + GetOperator("SPLIT_V", OperatorType::kSplitV), op); + EXPECT_EQ(op.num_split, output_toco_op->num_split); +} + TEST_F(OperatorTest, BuiltinAveragePool) { AveragePoolOperator op; op.fused_activation_function = FusedActivationFunctionType::kRelu6; @@ -517,6 +526,21 @@ TEST_F(OperatorTest, BuiltinUnpack) { EXPECT_EQ(op.axis, output_toco_op->axis); } +TEST_F(OperatorTest, BuiltinLeakyRelu) { + LeakyReluOperator op; + op.alpha = 3; + auto output_toco_op = SerializeAndDeserialize( + GetOperator("LEAKY_RELU", OperatorType::kLeakyRelu), op); + EXPECT_EQ(op.alpha, output_toco_op->alpha); +} + +TEST_F(OperatorTest, BuiltinSquaredDifference) { + SquaredDifferenceOperator op; + auto output_toco_op = SerializeAndDeserialize( + GetOperator("SQUARED_DIFFERENCE", OperatorType::kSquaredDifference), op); + ASSERT_NE(nullptr, output_toco_op.get()); +} + TEST_F(OperatorTest, CustomCTCBeamSearchDecoder) { CTCBeamSearchDecoderOperator op; op.beam_width = 3; @@ -592,6 +616,14 @@ TEST_F(OperatorTest, TestShouldExportAsFlexOp) { EXPECT_FALSE(ShouldExportAsFlexOp(true, "RFFT")); } +TEST_F(OperatorTest, BuiltinMirrorPad) { + MirrorPadOperator op; + op.mode = MirrorPadMode::kReflect; + auto output_toco_op = SerializeAndDeserialize( + GetOperator("MIRROR_PAD", OperatorType::kMirrorPad), op); + EXPECT_EQ(op.mode, output_toco_op->mode); +} + } // namespace } // namespace tflite diff --git a/tensorflow/lite/toco/tflite/whitelisted_flex_ops.cc b/tensorflow/lite/toco/tflite/whitelisted_flex_ops.cc index d251589b48..039a918af1 100644 --- a/tensorflow/lite/toco/tflite/whitelisted_flex_ops.cc +++ b/tensorflow/lite/toco/tflite/whitelisted_flex_ops.cc @@ -187,6 +187,7 @@ bool IsWhitelistedFlexOp(const std::string& tensorflow_op_name) { "MirrorPad", "MirrorPadGrad", "Mul", + "Multinomial", "Neg", "NextIteration", "NonMaxSuppression", diff --git a/tensorflow/lite/toco/toco.cc b/tensorflow/lite/toco/toco.cc index 9740015850..4a3d6a5848 100644 --- a/tensorflow/lite/toco/toco.cc +++ b/tensorflow/lite/toco/toco.cc @@ -16,87 +16,9 @@ limitations under the License. #include #include -#include "absl/strings/string_view.h" -#include "tensorflow/lite/toco/model.h" #include "tensorflow/lite/toco/model_cmdline_flags.h" -#include "tensorflow/lite/toco/model_flags.pb.h" #include "tensorflow/lite/toco/toco_cmdline_flags.h" -#include "tensorflow/lite/toco/toco_flags.pb.h" -#include "tensorflow/lite/toco/toco_port.h" -#include "tensorflow/lite/toco/toco_tooling.h" -#include "tensorflow/lite/toco/toco_types.h" -#include "tensorflow/core/lib/core/errors.h" -#include "tensorflow/core/platform/logging.h" - -namespace toco { -namespace { - -// Checks the permissions of the output file to ensure it is writeable. -void CheckOutputFilePermissions(const Arg& output_file) { - QCHECK(output_file.specified()) << "Missing required flag --output_file.\n"; - QCHECK(port::file::Writable(output_file.value()).ok()) - << "Specified output_file is not writable: " << output_file.value() - << ".\n"; -} - -// Checks the permissions of the frozen model file. -void CheckFrozenModelPermissions(const Arg& input_file) { - QCHECK(input_file.specified()) << "Missing required flag --input_file.\n"; - QCHECK(port::file::Exists(input_file.value(), port::file::Defaults()).ok()) - << "Specified input_file does not exist: " << input_file.value() << ".\n"; - QCHECK(port::file::Readable(input_file.value(), port::file::Defaults()).ok()) - << "Specified input_file exists, but is not readable: " - << input_file.value() << ".\n"; -} - -// Reads the contents of the GraphDef from either the frozen graph file or the -// SavedModel directory. If it reads the SavedModel directory, it updates the -// ModelFlags and TocoFlags accordingly. -void ReadInputData(const ParsedTocoFlags& parsed_toco_flags, - const ParsedModelFlags& parsed_model_flags, - TocoFlags* toco_flags, ModelFlags* model_flags, - string* graph_def_contents) { - port::CheckInitGoogleIsDone("InitGoogle is not done yet.\n"); - - // Ensure savedmodel_directory is not set. - QCHECK(!parsed_toco_flags.savedmodel_directory.specified()) - << "Use `tensorflow/lite/python/tflite_convert` script with " - << "SavedModel directories.\n"; - - // Checks the input file permissions and reads the contents. - CheckFrozenModelPermissions(parsed_toco_flags.input_file); - CHECK(port::file::GetContents(parsed_toco_flags.input_file.value(), - graph_def_contents, port::file::Defaults()) - .ok()); -} - -tensorflow::Status ToolMain(const ParsedTocoFlags& parsed_toco_flags, - const ParsedModelFlags& parsed_model_flags) { - ModelFlags model_flags; - ReadModelFlagsFromCommandLineFlags(parsed_model_flags, &model_flags); - - TocoFlags toco_flags; - ReadTocoFlagsFromCommandLineFlags(parsed_toco_flags, &toco_flags); - - string graph_def_contents; - ReadInputData(parsed_toco_flags, parsed_model_flags, &toco_flags, - &model_flags, &graph_def_contents); - CheckOutputFilePermissions(parsed_toco_flags.output_file); - - std::unique_ptr model = - Import(toco_flags, model_flags, graph_def_contents); - Transform(toco_flags, model.get()); - string output_file_contents; - TF_RETURN_IF_ERROR(Export(toco_flags, *model, toco_flags.allow_custom_ops(), - &output_file_contents)); - TF_RETURN_IF_ERROR( - port::file::SetContents(parsed_toco_flags.output_file.value(), - output_file_contents, port::file::Defaults())); - return tensorflow::Status(); -} - -} // namespace -} // namespace toco +#include "tensorflow/lite/toco/toco_convert.h" int main(int argc, char** argv) { toco::string msg; @@ -126,6 +48,6 @@ int main(int argc, char** argv) { return 1; } toco::port::InitGoogle(argv[0], effective_argc, &effective_argv, true); - auto status = toco::ToolMain(parsed_toco_flags, parsed_model_flags); + auto status = toco::Convert(parsed_toco_flags, parsed_model_flags); return status.ok() ? 0 : -1; } diff --git a/tensorflow/lite/toco/toco_convert.cc b/tensorflow/lite/toco/toco_convert.cc new file mode 100644 index 0000000000..28e7b10ecd --- /dev/null +++ b/tensorflow/lite/toco/toco_convert.cc @@ -0,0 +1,108 @@ +/* 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 +#include +#include + +#include "absl/strings/string_view.h" +#include "tensorflow/lite/toco/model.h" +#include "tensorflow/lite/toco/model_cmdline_flags.h" +#include "tensorflow/lite/toco/model_flags.pb.h" +#include "tensorflow/lite/toco/toco_cmdline_flags.h" +#include "tensorflow/lite/toco/toco_flags.pb.h" +#include "tensorflow/lite/toco/toco_port.h" +#include "tensorflow/lite/toco/toco_tooling.h" +#include "tensorflow/lite/toco/toco_types.h" +#include "tensorflow/core/lib/core/errors.h" +#include "tensorflow/core/platform/logging.h" + +namespace toco { +namespace { + +// Checks the permissions of the output file to ensure it is writeable. +void CheckOutputFilePermissions(const Arg& output_file) { + QCHECK(output_file.specified()) << "Missing required flag --output_file.\n"; + QCHECK(port::file::Writable(output_file.value()).ok()) + << "Specified output_file is not writable: " << output_file.value() + << ".\n"; +} + +// Checks the permissions of the frozen model file. +void CheckFrozenModelPermissions(const Arg& input_file) { + QCHECK(input_file.specified()) << "Missing required flag --input_file.\n"; + QCHECK(port::file::Exists(input_file.value(), port::file::Defaults()).ok()) + << "Specified input_file does not exist: " << input_file.value() << ".\n"; + QCHECK(port::file::Readable(input_file.value(), port::file::Defaults()).ok()) + << "Specified input_file exists, but is not readable: " + << input_file.value() << ".\n"; +} + +// Reads the contents of the GraphDef from either the frozen graph file or the +// SavedModel directory. If it reads the SavedModel directory, it updates the +// ModelFlags and TocoFlags accordingly. +void ReadInputData(const ParsedTocoFlags& parsed_toco_flags, + const ParsedModelFlags& parsed_model_flags, + TocoFlags* toco_flags, ModelFlags* model_flags, + string* graph_def_contents) { + port::CheckInitGoogleIsDone("InitGoogle is not done yet.\n"); + + // Ensure savedmodel_directory is not set. + QCHECK(!parsed_toco_flags.savedmodel_directory.specified()) + << "Use `tensorflow/lite/python/tflite_convert` script with " + << "SavedModel directories.\n"; + + // Checks the input file permissions and reads the contents. + CheckFrozenModelPermissions(parsed_toco_flags.input_file); + CHECK(port::file::GetContents(parsed_toco_flags.input_file.value(), + graph_def_contents, port::file::Defaults()) + .ok()); +} +} // namespace + +tensorflow::Status Convert(const string& graph_def_contents, + const TocoFlags& toco_flags, + const ModelFlags& model_flags, + string* output_file_contents) { + std::unique_ptr model = + Import(toco_flags, model_flags, graph_def_contents); + Transform(toco_flags, model.get()); + return Export(toco_flags, *model, toco_flags.allow_custom_ops(), + output_file_contents); +} + +tensorflow::Status Convert(const ParsedTocoFlags& parsed_toco_flags, + const ParsedModelFlags& parsed_model_flags) { + ModelFlags model_flags; + ReadModelFlagsFromCommandLineFlags(parsed_model_flags, &model_flags); + + TocoFlags toco_flags; + ReadTocoFlagsFromCommandLineFlags(parsed_toco_flags, &toco_flags); + + string graph_def_contents; + ReadInputData(parsed_toco_flags, parsed_model_flags, &toco_flags, + &model_flags, &graph_def_contents); + CheckOutputFilePermissions(parsed_toco_flags.output_file); + + string output_file_contents; + TF_RETURN_IF_ERROR(Convert(graph_def_contents, toco_flags, model_flags, + &output_file_contents)); + + TF_RETURN_IF_ERROR( + port::file::SetContents(parsed_toco_flags.output_file.value(), + output_file_contents, port::file::Defaults())); + return tensorflow::Status(); +} + +} // namespace toco diff --git a/tensorflow/lite/toco/toco_convert.h b/tensorflow/lite/toco/toco_convert.h new file mode 100644 index 0000000000..ebbd336d3f --- /dev/null +++ b/tensorflow/lite/toco/toco_convert.h @@ -0,0 +1,34 @@ +/* Copyright 2018 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_LITE_TOCO_TOCO_CONVERT_H_ +#define TENSORFLOW_LITE_TOCO_TOCO_CONVERT_H_ + +#include "tensorflow/core/lib/core/status.h" +#include "tensorflow/lite/toco/args.h" +#include "tensorflow/lite/toco/model_flags.pb.h" +#include "tensorflow/lite/toco/toco_flags.pb.h" + +namespace toco { + +tensorflow::Status Convert(const string& graph_def_contents, + const TocoFlags& toco_flags, + const ModelFlags& model_flags, + string* output_file_contents); + +tensorflow::Status Convert(const ParsedTocoFlags& parsed_toco_flags, + const ParsedModelFlags& parsed_model_flags); +} // namespace toco + +#endif // TENSORFLOW_LITE_TOCO_TOCO_CONVERT_H_ diff --git a/tensorflow/lite/toco/toco_convert_test.cc b/tensorflow/lite/toco/toco_convert_test.cc new file mode 100644 index 0000000000..c3c440db94 --- /dev/null +++ b/tensorflow/lite/toco/toco_convert_test.cc @@ -0,0 +1,173 @@ +/* Copyright 2018 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/lite/toco/toco_convert.h" +#include +#include + +namespace toco { +namespace { + +TEST(TocoTest, MissingInputFile) { + ParsedTocoFlags toco_flags; + ParsedModelFlags model_flags; + EXPECT_DEATH(Convert(toco_flags, model_flags).ok(), + "Missing required flag --input_file"); +} + +TEST(TocoTest, BadInputFormat) { + TocoFlags toco_flags; + ModelFlags model_flags; + + string input; + string output; + + EXPECT_DEATH(Convert(input, toco_flags, model_flags, &output).ok(), + "Unhandled input_format='FILE_FORMAT_UNKNOWN'"); +} + +TEST(TocoTest, MissingOuputArrays) { + TocoFlags toco_flags; + ModelFlags model_flags; + + toco_flags.set_input_format(TENSORFLOW_GRAPHDEF); + string input; + string output; + + EXPECT_DEATH(Convert(input, toco_flags, model_flags, &output).ok(), + "This model does not define output arrays, so a --output_arrays " + "flag must be given on the command-line"); +} + +TEST(TocoTest, BadOutputArray) { + TocoFlags toco_flags; + ModelFlags model_flags; + + toco_flags.set_input_format(TENSORFLOW_GRAPHDEF); + model_flags.add_output_arrays("output1"); + string input; + string output; + + EXPECT_DEATH(Convert(input, toco_flags, model_flags, &output).ok(), + "Specified output array .output1. is not produced by any op " + "in this graph. Is it a typo. To silence this message, pass " + "this flag: allow_nonexistent_arrays"); +} + +TEST(TocoTest, BadOutputFormat) { + TocoFlags toco_flags; + ModelFlags model_flags; + + toco_flags.set_input_format(TENSORFLOW_GRAPHDEF); + model_flags.add_output_arrays("output1"); + string input = R"GraphDef( + node { + name: "output1" + input: "input1" + input: "input2" + op: "Sub" + attr { key: "T" value { type: DT_FLOAT } } + } + )GraphDef"; + + string output; + + EXPECT_DEATH(Convert(input, toco_flags, model_flags, &output).ok(), + "Unhandled output_format='FILE_FORMAT_UNKNOWN'"); +} + +TEST(TocoTest, SimpleFloatModel) { + TocoFlags toco_flags; + ModelFlags model_flags; + + toco_flags.set_input_format(TENSORFLOW_GRAPHDEF); + toco_flags.set_output_format(TENSORFLOW_GRAPHDEF); + + // Inputs are automatically selected (but that might not be a good idea). + model_flags.add_output_arrays("output1"); + string input = R"GraphDef( + node { + name: "input1" + op: "Placeholder" + attr { key: "dtype" value { type: DT_INT64 } } + } + node { + name: "input2" + op: "Placeholder" + attr { key: "dtype" value { type: DT_INT64 } } + } + node { + name: "output1" + input: "input1" + input: "input2" + op: "Sub" + attr { key: "T" value { type: DT_FLOAT } } + } + )GraphDef"; + + string output; + EXPECT_TRUE(Convert(input, toco_flags, model_flags, &output).ok()); + EXPECT_TRUE(!output.empty()); +} + +TEST(TocoTest, TransientStringTensors) { + TocoFlags toco_flags; + ModelFlags model_flags; + + toco_flags.set_input_format(TENSORFLOW_GRAPHDEF); + + // We need to do a couple of things to trigger the transient array + // initialization code: output format must support memory planning, and the + // input array must have a shape. + toco_flags.set_output_format(TFLITE); + + model_flags.add_output_arrays("output1"); + string input = R"GraphDef( + node { + name: "input1" + op: "Placeholder" + attr { key: "dtype" value { type: DT_STRING } } + attr { key: "shape" value { shape { dim { size:1 }}}} + } + node { + name: "indices1" + op: "Placeholder" + attr { key: "dtype" value { type: DT_INT64 } } + } + node { + name: "intermediate1" + op: "Gather" + input: "input1" + input: "indices1" + attr { key: "Tparams" value { type: DT_STRING } } + attr { key: "Tindices" value { type: DT_INT64 } } + } + node { + name: "output1" + op: "Gather" + input: "intermediate1" + input: "indices2" + attr { key: "Tparams" value { type: DT_STRING } } + attr { key: "Tindices" value { type: DT_INT64 } } + } + )GraphDef"; + + string output; + + EXPECT_TRUE(Convert(input, toco_flags, model_flags, &output).ok()); + EXPECT_TRUE(!output.empty()); +} + +} // namespace +} // namespace toco diff --git a/tensorflow/lite/toco/toco_tooling.cc b/tensorflow/lite/toco/toco_tooling.cc index 5f96e833fb..55a454e66d 100644 --- a/tensorflow/lite/toco/toco_tooling.cc +++ b/tensorflow/lite/toco/toco_tooling.cc @@ -210,7 +210,8 @@ std::unique_ptr Import(const TocoFlags& toco_flags, CheckInvariants(*model); break; default: - LOG(FATAL) << "Unhandled input_format"; + LOG(FATAL) << "Unhandled input_format='" + << FileFormat_Name(toco_flags.input_format()) << "'"; } LogDump(kLogLevelModelChanged, "AT IMPORT", *model); @@ -308,6 +309,7 @@ void Transform(const TocoFlags& toco_flags, Model* model) { // Fix any issues with IO edges. This must happen after any transform that // may modify the structure of the edges. FixEdgeArrays(model); + FixOperatorOrdering(model); if (quantize_output) { // If the user specified default min/max ranges we need to set all arrays @@ -424,7 +426,8 @@ tensorflow::Status Export(const TocoFlags& toco_flags, const Model& model, DumpGraphviz(model, output_file_contents); break; default: - LOG(FATAL) << "Unhandled output_format"; + LOG(FATAL) << "Unhandled output_format='" + << FileFormat_Name(toco_flags.output_format()) << "'"; } return tensorflow::Status(); } diff --git a/tensorflow/lite/toco/tooling_util.cc b/tensorflow/lite/toco/tooling_util.cc index e33f7c8452..af4cd386a2 100644 --- a/tensorflow/lite/toco/tooling_util.cc +++ b/tensorflow/lite/toco/tooling_util.cc @@ -308,6 +308,7 @@ const char* OperatorTypeName(OperatorType type) { #define HANDLE_OPERATORTYPENAME_CASE(c) \ case OperatorType::k##c: \ return #c; + HANDLE_OPERATORTYPENAME_CASE(Abs) HANDLE_OPERATORTYPENAME_CASE(Add) HANDLE_OPERATORTYPENAME_CASE(AddN) HANDLE_OPERATORTYPENAME_CASE(AveragePool) @@ -371,6 +372,7 @@ const char* OperatorTypeName(OperatorType type) { HANDLE_OPERATORTYPENAME_CASE(Shape) HANDLE_OPERATORTYPENAME_CASE(Slice) HANDLE_OPERATORTYPENAME_CASE(Split) + HANDLE_OPERATORTYPENAME_CASE(SplitV) HANDLE_OPERATORTYPENAME_CASE(Sqrt) HANDLE_OPERATORTYPENAME_CASE(Square) HANDLE_OPERATORTYPENAME_CASE(Switch) @@ -411,6 +413,9 @@ const char* OperatorTypeName(OperatorType type) { HANDLE_OPERATORTYPENAME_CASE(ZerosLike) HANDLE_OPERATORTYPENAME_CASE(UnidirectionalSequenceLstm) HANDLE_OPERATORTYPENAME_CASE(ResizeNearestNeighbor) + HANDLE_OPERATORTYPENAME_CASE(LeakyRelu) + HANDLE_OPERATORTYPENAME_CASE(SquaredDifference) + HANDLE_OPERATORTYPENAME_CASE(MirrorPad) default: LOG(FATAL) << "Unhandled op type"; #undef HANDLE_OPERATORTYPENAME_CASE @@ -439,6 +444,7 @@ bool OperatorSupportsFusedActivation(OperatorType type) { case OperatorType::kMaxPool: case OperatorType::kMul: case OperatorType::kSub: + case OperatorType::kSquaredDifference: return true; default: return false; @@ -531,12 +537,12 @@ void DumpGraphvizVideoFrame(const Model& model) { if (!dump_hashes.count(hash)) { LOG(INFO) << "DUMPING GRAPHVIZ VIDEO FRAME: " << dump_id; dump_hashes.insert(hash); - CHECK(port::file::SetContents( - port::file::JoinPath( - dump_options.dump_graphviz, - toco::port::StringF("toco_video_%05d.dot", dump_id)), - graphviz_dump, port::file::Defaults()) - .ok()); + const auto result = port::file::SetContents( + port::file::JoinPath( + dump_options.dump_graphviz, + toco::port::StringF("toco_video_%05d.dot", dump_id)), + graphviz_dump, port::file::Defaults()); + QCHECK(result.ok()) << result.error_message(); dump_id++; } } @@ -550,14 +556,13 @@ void LogDump(int log_level, const string& message, const Model& model) { string graphviz_dump; DumpGraphviz(model, &graphviz_dump); - CHECK(port::file::SetContents( - port::file::JoinPath( - dump_options.dump_graphviz, - absl::StrCat("toco_", - absl::StrReplaceAll(message, {{" ", "_"}}), - ".dot")), - graphviz_dump, port::file::Defaults()) - .ok()); + const auto result = port::file::SetContents( + port::file::JoinPath( + dump_options.dump_graphviz, + absl::StrCat("toco_", absl::StrReplaceAll(message, {{" ", "_"}}), + ".dot")), + graphviz_dump, port::file::Defaults()); + QCHECK(result.ok()) << result.error_message(); } if (!VLOG_IS_ON(log_level)) { @@ -894,6 +899,9 @@ void CheckNonExistentIOArrays(const Model& model) { << "\" is not consumed by any op in this graph. " << general_comment; } for (const string& output_array : model.flags.output_arrays()) { + if (IsConstantParameterArray(model, output_array)) { + continue; // It is OK to request that a constant be an output. + } QCHECK(GetOpWithOutput(model, output_array)) << "Specified output array \"" << output_array << "\" is not produced by any op in this graph. " << general_comment; @@ -1032,10 +1040,10 @@ void CheckEachArray(const Model& model) { if (colon_pos != string::npos) { CHECK_EQ(name.substr(colon_pos + 1).find_first_not_of("0123456789"), string::npos) - << "Array name must only have digits after colon"; + << "Array '" << name << "' has non-digit characters after colon."; } - CHECK_GT(colon_pos, 0) - << "First character of array name must not be a colon."; + CHECK_GT(colon_pos, 0) << "Array '" << name + << "' must not start with a colon."; } } @@ -1767,6 +1775,14 @@ bool IsAllocatableTransientArray(const Model& model, const string& array_name) { if (!array->has_shape()) { return false; } + + // The size of string tensors is rarely known ahead of time, so all transient + // tensors of this type will need to be dynamically allocated. + if (array->final_data_type == ArrayDataType::kString || + array->data_type == ArrayDataType::kString) { + return false; + } + return true; } @@ -2207,6 +2223,8 @@ ArrayDataType ConvertIODataTypeToArrayDataType(IODataType type) { return ArrayDataType::kFloat; case QUANTIZED_UINT8: return ArrayDataType::kUint8; + case INT8: + return ArrayDataType::kInt8; case QUANTIZED_INT16: return ArrayDataType::kInt16; case INT32: diff --git a/tensorflow/lite/toco/types.proto b/tensorflow/lite/toco/types.proto index 12f711fd8a..fa911b8a4c 100644 --- a/tensorflow/lite/toco/types.proto +++ b/tensorflow/lite/toco/types.proto @@ -43,4 +43,7 @@ enum IODataType { // Complex64, not quantized COMPLEX64 = 8; + + // Int8, quantized based on QuantizationParameters in schema. + INT8 = 9; } diff --git a/tensorflow/lite/tools/benchmark/benchmark_model.cc b/tensorflow/lite/tools/benchmark/benchmark_model.cc index 05148aea65..e9b485efca 100644 --- a/tensorflow/lite/tools/benchmark/benchmark_model.cc +++ b/tensorflow/lite/tools/benchmark/benchmark_model.cc @@ -51,11 +51,13 @@ using tensorflow::Stat; BenchmarkParams BenchmarkModel::DefaultParams() { BenchmarkParams params; params.AddParam("num_runs", BenchmarkParam::Create(50)); + params.AddParam("min_secs", BenchmarkParam::Create(1.0f)); params.AddParam("run_delay", BenchmarkParam::Create(-1.0f)); params.AddParam("num_threads", BenchmarkParam::Create(1)); params.AddParam("benchmark_name", BenchmarkParam::Create("")); params.AddParam("output_prefix", BenchmarkParam::Create("")); params.AddParam("warmup_runs", BenchmarkParam::Create(1)); + params.AddParam("warmup_min_secs", BenchmarkParam::Create(0.5f)); return params; } @@ -73,19 +75,34 @@ void BenchmarkLoggingListener::OnBenchmarkEnd(const BenchmarkResults &results) { std::vector BenchmarkModel::GetFlags() { return { - CreateFlag("num_runs", ¶ms_, "number of runs"), + CreateFlag("num_runs", ¶ms_, + "minimum number of runs, see also min_secs"), + CreateFlag( + "min_secs", ¶ms_, + "minimum number of seconds to rerun for, potentially making the " + "actual number of runs to be greater than num_runs"), CreateFlag("run_delay", ¶ms_, "delay between runs in seconds"), CreateFlag("num_threads", ¶ms_, "number of threads"), CreateFlag("benchmark_name", ¶ms_, "benchmark name"), CreateFlag("output_prefix", ¶ms_, "benchmark output prefix"), - CreateFlag("warmup_runs", ¶ms_, - "how many runs to initialize model"), + CreateFlag( + "warmup_runs", ¶ms_, + "minimum number of runs performed on initialization, to " + "allow performance characteristics to settle, see also " + "warmup_min_secs"), + CreateFlag( + "warmup_min_secs", ¶ms_, + "minimum number of seconds to rerun for, potentially making the " + "actual number of warm-up runs to be greater than warmup_runs"), }; } void BenchmarkModel::LogParams() { - TFLITE_LOG(INFO) << "Num runs: [" << params_.Get("num_runs") << "]"; + TFLITE_LOG(INFO) << "Min num runs: [" << params_.Get("num_runs") + << "]"; + TFLITE_LOG(INFO) << "Min runs duration (seconds): [" + << params_.Get("min_secs") << "]"; TFLITE_LOG(INFO) << "Inter-run delay (seconds): [" << params_.Get("run_delay") << "]"; TFLITE_LOG(INFO) << "Num threads: [" << params_.Get("num_threads") @@ -94,16 +111,24 @@ void BenchmarkModel::LogParams() { << params_.Get("benchmark_name") << "]"; TFLITE_LOG(INFO) << "Output prefix: [" << params_.Get("output_prefix") << "]"; - TFLITE_LOG(INFO) << "Warmup runs: [" << params_.Get("warmup_runs") - << "]"; + TFLITE_LOG(INFO) << "Min warmup runs: [" + << params_.Get("warmup_runs") << "]"; + TFLITE_LOG(INFO) << "Min warmup runs duration (seconds): [" + << params_.Get("warmup_min_secs") << "]"; } void BenchmarkModel::PrepareInputsAndOutputs() {} -Stat BenchmarkModel::Run(int num_times, RunType run_type) { +Stat BenchmarkModel::Run(int min_num_times, float min_secs, + RunType run_type) { Stat run_stats; - TFLITE_LOG(INFO) << "Running benchmark for " << num_times << " iterations "; - for (int run = 0; run < num_times; run++) { + TFLITE_LOG(INFO) << "Running benchmark for at least " << min_num_times + << " iterations and at least " << min_secs << " seconds"; + int64_t min_finish_us = + profiling::time::NowMicros() + static_cast(min_secs * 1.e6f); + for (int run = 0; + run < min_num_times || profiling::time::NowMicros() < min_finish_us; + run++) { PrepareInputsAndOutputs(); listeners_.OnSingleRunStart(run_type); int64_t start_us = profiling::time::NowMicros(); @@ -145,9 +170,11 @@ void BenchmarkModel::Run() { uint64_t input_bytes = ComputeInputBytes(); Stat warmup_time_us = - Run(params_.Get("warmup_runs"), WARMUP); + Run(params_.Get("warmup_runs"), + params_.Get("warmup_min_secs"), WARMUP); Stat inference_time_us = - Run(params_.Get("num_runs"), REGULAR); + Run(params_.Get("num_runs"), params_.Get("min_secs"), + REGULAR); listeners_.OnBenchmarkEnd( {startup_latency_us, input_bytes, warmup_time_us, inference_time_us}); } diff --git a/tensorflow/lite/tools/benchmark/benchmark_model.h b/tensorflow/lite/tools/benchmark/benchmark_model.h index d8a9b05010..31ee5c92aa 100644 --- a/tensorflow/lite/tools/benchmark/benchmark_model.h +++ b/tensorflow/lite/tools/benchmark/benchmark_model.h @@ -150,7 +150,8 @@ class BenchmarkModel { bool ParseFlags(int argc, char** argv); virtual std::vector GetFlags(); virtual uint64_t ComputeInputBytes() = 0; - virtual tensorflow::Stat Run(int num_times, RunType run_type); + virtual tensorflow::Stat Run(int min_num_times, float min_secs, + RunType run_type); virtual void PrepareInputsAndOutputs(); virtual void RunImpl() = 0; BenchmarkParams params_; diff --git a/tensorflow/lite/tools/benchmark/benchmark_test.cc b/tensorflow/lite/tools/benchmark/benchmark_test.cc index 59d23d9008..8191fbcd73 100644 --- a/tensorflow/lite/tools/benchmark/benchmark_test.cc +++ b/tensorflow/lite/tools/benchmark/benchmark_test.cc @@ -33,6 +33,7 @@ namespace { BenchmarkParams CreateParams() { BenchmarkParams params; params.AddParam("num_runs", BenchmarkParam::Create(2)); + params.AddParam("min_secs", BenchmarkParam::Create(1.0f)); params.AddParam("run_delay", BenchmarkParam::Create(-1.0f)); params.AddParam("num_threads", BenchmarkParam::Create(1)); params.AddParam("benchmark_name", BenchmarkParam::Create("")); @@ -42,6 +43,7 @@ BenchmarkParams CreateParams() { params.AddParam("input_layer", BenchmarkParam::Create("")); params.AddParam("input_layer_shape", BenchmarkParam::Create("")); params.AddParam("use_nnapi", BenchmarkParam::Create(false)); + params.AddParam("warmup_min_secs", BenchmarkParam::Create(0.5f)); return params; } diff --git a/tensorflow/lite/tools/benchmark/benchmark_tflite_model.cc b/tensorflow/lite/tools/benchmark/benchmark_tflite_model.cc index 777d9dde7d..16f70870b6 100644 --- a/tensorflow/lite/tools/benchmark/benchmark_tflite_model.cc +++ b/tensorflow/lite/tools/benchmark/benchmark_tflite_model.cc @@ -181,7 +181,9 @@ bool PopulateInputLayerInfo( return true; } -BenchmarkParams GetDefaultParams() { +} // namespace + +BenchmarkParams BenchmarkTfLiteModel::DefaultParams() { BenchmarkParams default_params = BenchmarkModel::DefaultParams(); default_params.AddParam("graph", BenchmarkParam::Create("")); default_params.AddParam("input_layer", @@ -192,10 +194,8 @@ BenchmarkParams GetDefaultParams() { return default_params; } -} // namespace - BenchmarkTfLiteModel::BenchmarkTfLiteModel() - : BenchmarkTfLiteModel(GetDefaultParams()) {} + : BenchmarkTfLiteModel(DefaultParams()) {} BenchmarkTfLiteModel::BenchmarkTfLiteModel(BenchmarkParams params) : BenchmarkModel(std::move(params)) { @@ -279,7 +279,7 @@ void BenchmarkTfLiteModel::PrepareInputsAndOutputs() { FillRandomString(&buffer, sizes, []() { return "we're have some friends over saturday to hang out in the yard"; }); - buffer.WriteToTensor(interpreter->tensor(i)); + buffer.WriteToTensor(interpreter->tensor(i), /*new_shape=*/nullptr); } else { TFLITE_LOG(FATAL) << "Don't know how to populate tensor " << t->name << " of type " << t->type; @@ -319,6 +319,7 @@ void BenchmarkTfLiteModel::Init() { bool use_nnapi = params_.Get("use_nnapi"); interpreter->UseNNAPI(use_nnapi); + ApplyDelegates(); auto interpreter_inputs = interpreter->inputs(); diff --git a/tensorflow/lite/tools/benchmark/benchmark_tflite_model.h b/tensorflow/lite/tools/benchmark/benchmark_tflite_model.h index 401ab5427d..83599e644d 100644 --- a/tensorflow/lite/tools/benchmark/benchmark_tflite_model.h +++ b/tensorflow/lite/tools/benchmark/benchmark_tflite_model.h @@ -77,11 +77,16 @@ class BenchmarkTfLiteModel : public BenchmarkModel { }; protected: + static BenchmarkParams DefaultParams(); void PrepareInputsAndOutputs() override; - private: + // Allows installation of custom delegates during initialization + virtual void ApplyDelegates() {} + std::unique_ptr model; std::unique_ptr interpreter; + + private: std::vector inputs; ProfilingListener profiling_listener_; GemmlowpProfilingListener gemmlowp_profiling_listener_; diff --git a/tensorflow/lite/tools/benchmark/ios/TFLiteBenchmark/TFLiteBenchmark.xcodeproj/project.pbxproj b/tensorflow/lite/tools/benchmark/ios/TFLiteBenchmark/TFLiteBenchmark.xcodeproj/project.pbxproj index 958936a660..a5f5bfbbda 100644 --- a/tensorflow/lite/tools/benchmark/ios/TFLiteBenchmark/TFLiteBenchmark.xcodeproj/project.pbxproj +++ b/tensorflow/lite/tools/benchmark/ios/TFLiteBenchmark/TFLiteBenchmark.xcodeproj/project.pbxproj @@ -20,7 +20,7 @@ /* Begin PBXFileReference section */ 6FE7579920D59CE500F01636 /* benchmark_params.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = benchmark_params.json; sourceTree = ""; }; - 6FE7579C20D5A5E000F01636 /* benchmark-lib.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "benchmark-lib.a"; path = "$SRCROOT/../../../../../../../tensorflow/lite/tools/make/gen/lib/benchmark-lib.a"; sourceTree = ""; }; + 6FE7579C20D5A5E000F01636 /* benchmark-lib.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "benchmark-lib.a"; path = "$SRCROOT/../../../../../../tensorflow/lite/tools/make/gen/lib/benchmark-lib.a"; sourceTree = ""; }; 6FE7579E20D5A6A700F01636 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; }; 6FE757A020D5AB8000F01636 /* mobilenet_v1_1.0_224.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; path = mobilenet_v1_1.0_224.tflite; sourceTree = ""; }; 6FE93FF820D592D8008C9FE4 /* TFLiteBenchmark.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TFLiteBenchmark.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -309,19 +309,19 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; "HEADER_SEARCH_PATHS[arch=*]" = ( - $SRCROOT/../../../../../../../, - $SRCROOT/../../../../../../../tensorflow/lite/tools/make/downloads/eigen, - $SRCROOT/../../../../../../../tensorflow/lite/tools/make/downloads/gemmlowp, - $SRCROOT/../../../../../../../tensorflow/lite/tools/make/downloads/neon_2_sse, - $SRCROOT/../../../../../../../tensorflow/lite/tools/make/downloads/farmhash/src, - $SRCROOT/../../../../../../../tensorflow/lite/tools/make/downloads/flatbuffers/include, + $SRCROOT/../../../../../../, + $SRCROOT/../../../../../../tensorflow/lite/tools/make/downloads/eigen, + $SRCROOT/../../../../../../tensorflow/lite/tools/make/downloads/gemmlowp, + $SRCROOT/../../../../../../tensorflow/lite/tools/make/downloads/neon_2_sse, + $SRCROOT/../../../../../../tensorflow/lite/tools/make/downloads/farmhash/src, + $SRCROOT/../../../../../../tensorflow/lite/tools/make/downloads/flatbuffers/include, ); INFOPLIST_FILE = TFLiteBenchmark/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - "LIBRARY_SEARCH_PATHS[arch=*]" = $SRCROOT/../../../../../../../tensorflow/lite/tools/make/gen/lib; + "LIBRARY_SEARCH_PATHS[arch=*]" = $SRCROOT/../../../../../../tensorflow/lite/tools/make/gen/lib; PRODUCT_BUNDLE_IDENTIFIER = example.TFLiteBenchmark; PRODUCT_NAME = "$(TARGET_NAME)"; TARGETED_DEVICE_FAMILY = "1,2"; @@ -335,19 +335,19 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; "HEADER_SEARCH_PATHS[arch=*]" = ( - $SRCROOT/../../../../../../../, - $SRCROOT/../../../../../../../tensorflow/lite/tools/make/downloads/eigen, - $SRCROOT/../../../../../../../tensorflow/lite/tools/make/downloads/gemmlowp, - $SRCROOT/../../../../../../../tensorflow/lite/tools/make/downloads/neon_2_sse, - $SRCROOT/../../../../../../../tensorflow/lite/tools/make/downloads/farmhash/src, - $SRCROOT/../../../../../../../tensorflow/lite/tools/make/downloads/flatbuffers/include, + $SRCROOT/../../../../../../, + $SRCROOT/../../../../../../tensorflow/lite/tools/make/downloads/eigen, + $SRCROOT/../../../../../../tensorflow/lite/tools/make/downloads/gemmlowp, + $SRCROOT/../../../../../../tensorflow/lite/tools/make/downloads/neon_2_sse, + $SRCROOT/../../../../../../tensorflow/lite/tools/make/downloads/farmhash/src, + $SRCROOT/../../../../../../tensorflow/lite/tools/make/downloads/flatbuffers/include, ); INFOPLIST_FILE = TFLiteBenchmark/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - "LIBRARY_SEARCH_PATHS[arch=*]" = $SRCROOT/../../../../../../../tensorflow/lite/tools/make/gen/lib; + "LIBRARY_SEARCH_PATHS[arch=*]" = $SRCROOT/../../../../../../tensorflow/lite/tools/make/gen/lib; PRODUCT_BUNDLE_IDENTIFIER = example.TFLiteBenchmark; PRODUCT_NAME = "$(TARGET_NAME)"; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/tensorflow/lite/tools/make/Makefile b/tensorflow/lite/tools/make/Makefile index 8f12355854..363a069d5e 100644 --- a/tensorflow/lite/tools/make/Makefile +++ b/tensorflow/lite/tools/make/Makefile @@ -85,6 +85,7 @@ CORE_CC_ALL_SRCS := \ $(wildcard tensorflow/lite/*.cc) \ $(wildcard tensorflow/lite/*.c) \ $(wildcard tensorflow/lite/c/*.c) \ +$(wildcard tensorflow/lite/core/*.cc) \ $(wildcard tensorflow/lite/core/api/*.cc) ifneq ($(BUILD_TYPE),micro) CORE_CC_ALL_SRCS += \ @@ -113,6 +114,10 @@ ifeq ($(BUILD_TYPE),micro) CORE_CC_EXCLUDE_SRCS += \ tensorflow/lite/mmap_allocation.cc \ tensorflow/lite/nnapi_delegate.cc +else +CORE_CC_EXCLUDE_SRCS += \ +tensorflow/contrib/lite/mmap_allocation_disabled.cc \ +tensorflow/contrib/lite/nnapi_delegate_disabled.cc endif # Filter out all the excluded files. TF_LITE_CC_SRCS := $(filter-out $(CORE_CC_EXCLUDE_SRCS), $(CORE_CC_ALL_SRCS)) @@ -208,6 +213,9 @@ $(BENCHMARK_BINARY) : $(BENCHMARK_LIB) benchmark: $(BENCHMARK_BINARY) +libdir: + @echo $(LIBDIR) + # Gets rid of all generated files. clean: rm -rf $(MAKEFILE_DIR)/gen diff --git a/tensorflow/lite/tools/make/targets/ios_makefile.inc b/tensorflow/lite/tools/make/targets/ios_makefile.inc index 7f36b8ecef..ae9276f9a6 100644 --- a/tensorflow/lite/tools/make/targets/ios_makefile.inc +++ b/tensorflow/lite/tools/make/targets/ios_makefile.inc @@ -22,7 +22,7 @@ ifeq ($(TARGET), ios) TARGET_ARCH := x86_64 CXXFLAGS += -miphoneos-version-min=$(MIN_SDK_VERSION) \ -DGEMMLOWP_ALLOW_SLOW_SCALAR_FALLBACK \ - -DTFLITE_USE_APPLE_ACCELERATE_FOR_CONV \ + -DTF_LITE_USE_CBLAS \ -fembed-bitcode \ -Wno-c++11-narrowing \ -mno-thumb \ diff --git a/tensorflow/lite/tools/optimize/g3doc/quantize_weights.md b/tensorflow/lite/tools/optimize/g3doc/quantize_weights.md index 2517882c84..cea164c38f 100644 --- a/tensorflow/lite/tools/optimize/g3doc/quantize_weights.md +++ b/tensorflow/lite/tools/optimize/g3doc/quantize_weights.md @@ -3,7 +3,7 @@ ## Recommended usage The Quantize Weights transformation is integrated with -[tflite_convert](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/toco/g3doc/cmdline_reference.md#transformation-flags). +[tflite_convert](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/g3doc/convert/cmdline_reference.md#transformation-flags). The recommended way of invoking this tool is by simply adding the `--post_training_quantize` flag to your original tflite_convert invocation. For diff --git a/tensorflow/lite/tools/pip_package/MANIFEST.in b/tensorflow/lite/tools/pip_package/MANIFEST.in new file mode 100644 index 0000000000..bb574e63a3 --- /dev/null +++ b/tensorflow/lite/tools/pip_package/MANIFEST.in @@ -0,0 +1 @@ +recursive-include * *.py diff --git a/tensorflow/lite/tools/pip_package/README.md b/tensorflow/lite/tools/pip_package/README.md new file mode 100644 index 0000000000..8190782c39 --- /dev/null +++ b/tensorflow/lite/tools/pip_package/README.md @@ -0,0 +1,33 @@ +# Building TensorFlow Lite Standalone Pip + +Many users would like to deploy TensorFlow lite interpreter and use it from +Python without requiring the rest of TensorFlow. + +## Steps + +To build a binary wheel run this script: +``` +sudo apt install swig libjpeg-dev zlib1g-dev python3-dev python3-numpy +sh tensorflow/lite/tools/pip_package/build_pip_package.sh +``` +That will print out some output and a .whl file. You can then install that +``` +pip install --upgrade +``` + +Note, unlike tensorflow this will be installed to a tflite_runtime namespace. +You can then use the Tensorflow Lite interpreter as. +``` +import tflite_runtime as tflr +interpreter = tflr.lite.Interpreter(model_path="foo.tflite") +``` + +This currently works to build on Linux machines including Raspberry Pi. In +the future, cross compilation to smaller SOCs like Raspberry Pi from +bigger host will be supported. + +## Caveats + +* You cannot use TensorFlow Select ops, only TensorFlow Lite builtins. +* Currently custom ops and delegates cannot be registered. + diff --git a/tensorflow/lite/tools/pip_package/build_pip_package.sh b/tensorflow/lite/tools/pip_package/build_pip_package.sh new file mode 100644 index 0000000000..2887ce8471 --- /dev/null +++ b/tensorflow/lite/tools/pip_package/build_pip_package.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# Copyright 2018 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. +# ============================================================================== + +set -e + +# Find where this script lives and then the Tensorflow root. +MY_DIRECTORY=`dirname $0` +export TENSORFLOW_SRC_ROOT=`realpath $MY_DIRECTORY/../../../..` + +export TENSORFLOW_VERSION=`grep "_VERSION = " $TENSORFLOW_SRC_ROOT/tensorflow/tools/pip_package/setup.py | cut -d'=' -f 2 | sed "s/[ '-]//g"`; + + +# Build a pip build tree. +BUILD_ROOT=/tmp/tflite_pip +rm -rf $BUILD_ROOT +mkdir -p $BUILD_ROOT/tflite_runtime/lite +mkdir -p $BUILD_ROOT/tflite_runtime/lite/python + +# Build an importable module tree +cat > $BUILD_ROOT/tflite_runtime/__init__.py < $BUILD_ROOT/tflite_runtime/lite/__init__.py < $BUILD_ROOT/tflite_runtime/lite/python/__init__.py < hashes); +struct TfLiteIntArrayDeleter { + void operator()(TfLiteIntArray* a) { + if (a) TfLiteIntArrayFree(a); + } +}; + } // namespace tflite #endif // TENSORFLOW_LITE_UTIL_H_ diff --git a/tensorflow/opensource_only.files b/tensorflow/opensource_only.files new file mode 100644 index 0000000000..8ff1645b98 --- /dev/null +++ b/tensorflow/opensource_only.files @@ -0,0 +1,17 @@ +tensorflow/third_party/toolchains/preconfig/ubuntu14.04/py3/BUILD +tensorflow/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/BUILD +tensorflow/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc/BUILD +tensorflow/third_party/toolchains/preconfig/ubuntu14.04/cuda10.0-cudnn7/cuda/build_defs.bzl +tensorflow/third_party/toolchains/preconfig/ubuntu14.04/cuda10.0-cudnn7/cuda/BUILD +tensorflow/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda10.0/BUILD +tensorflow/third_party/toolchains/preconfig/ubuntu14.04/cuda9.0-cudnn7/cuda/build_defs.bzl +tensorflow/third_party/toolchains/preconfig/ubuntu14.04/cuda9.0-cudnn7/cuda/BUILD +tensorflow/third_party/toolchains/preconfig/ubuntu14.04/nccl2/BUILD +tensorflow/third_party/toolchains/preconfig/generate/workspace.bzl +tensorflow/third_party/toolchains/preconfig/generate/containers.bzl +tensorflow/third_party/toolchains/preconfig/generate/generate.bzl +tensorflow/third_party/toolchains/preconfig/generate/BUILD +tensorflow/third_party/toolchains/preconfig/win_1803/bazel_018/BUILD +tensorflow/third_party/toolchains/preconfig/win_1803/bazel_018/dummy_toolchain.bzl +tensorflow/third_party/toolchains/preconfig/win_1803/py36/BUILD +tensorflow/third_party/toolchains/preconfig/win_1803/BUILD \ No newline at end of file diff --git a/tensorflow/python/BUILD b/tensorflow/python/BUILD index 4fe92262ba..a558045e4a 100644 --- a/tensorflow/python/BUILD +++ b/tensorflow/python/BUILD @@ -102,6 +102,7 @@ py_library( ":framework_for_generated_wrappers", ":functional_ops", ":gradient_checker", + ":gradient_checker_v2", ":graph_util", ":histogram_ops", ":image_ops", @@ -124,7 +125,6 @@ py_library( ":session_ops", ":sets", ":sparse_ops", - ":spectral_ops", ":spectral_ops_test_util", ":standard_ops", ":state_ops", @@ -132,6 +132,7 @@ py_library( ":subscribe", ":summary", ":tensor_array_ops", + ":tensor_forest_ops", ":test_ops", # TODO: Break testing code out into separate rule. ":tf_cluster", ":tf_item", @@ -524,6 +525,17 @@ py_test( ], ) +py_test( + name = "dispatch_test", + srcs = ["util/dispatch_test.py"], + srcs_version = "PY2AND3", + deps = [ + ":client_testlib", + ":platform", + ":util", + ], +) + py_test( name = "keyword_args_test", srcs = ["util/keyword_args_test.py"], @@ -854,7 +866,6 @@ py_library( deps = [ ":c_api_util", ":control_flow_util", - ":cpp_shape_inference_proto_py", ":device", ":dtypes", ":error_interpolation", @@ -862,6 +873,7 @@ py_library( ":platform", ":registry", ":tensor_shape", + ":tf2", ":traceable_stack", ":util", ":versions", @@ -880,6 +892,8 @@ py_library( deps = [ ":auto_control_deps", ":framework_ops", + ":sparse_tensor", + ":tensor_array_ops", "//tensorflow/python/autograph", "//tensorflow/python/eager:context", "//tensorflow/python/eager:graph_only_ops", @@ -894,6 +908,8 @@ py_library( deps = [ ":control_flow_ops", ":framework_ops", + ":sparse_tensor", + ":tensor_array_ops", ":util", ], ) @@ -981,6 +997,7 @@ py_library( srcs_version = "PY2AND3", deps = [ ":dtypes", + ":tf2", ":util", "//tensorflow/core:protos_all_py", ], @@ -994,6 +1011,7 @@ py_library( ":common_shapes", ":dtypes", ":tensor_shape", + ":util", "//third_party/py/numpy", ], ) @@ -1052,6 +1070,7 @@ py_library( ":random_seed", ":resource_variable_ops", ":session", + ":tensor_array_ops", ":training", ":util", ":variables", @@ -1076,10 +1095,13 @@ py_library( srcs_version = "PY2AND3", deps = [ ":client", + ":cond_v2", ":framework_test_lib", ":gradient_checker", + ":gradient_checker_v2", ":platform_test", ":util", + ":while_v2", ], ) @@ -1384,6 +1406,7 @@ py_test( srcs_version = "PY2AND3", tags = ["no_pip"], # test_ops_2 is not available in pip. deps = [ + ":cond_v2", ":control_flow_ops", ":errors", ":framework", @@ -1398,6 +1421,7 @@ py_test( ":util", ":variable_scope", ":variables", + ":while_v2", "//tensorflow/core:protos_all_py", "//tensorflow/python/eager:context", "//tensorflow/python/eager:function", @@ -1618,6 +1642,14 @@ tf_gen_op_wrapper_private_py( ], ) +tf_gen_op_wrapper_private_py( + name = "tensor_forest_ops_gen", + visibility = ["//tensorflow:internal"], + deps = [ + "//tensorflow/core:tensor_forest_ops_op_lib", + ], +) + tf_gen_op_wrapper_private_py( name = "summary_ops_gen", visibility = ["//tensorflow:__subpackages__"], @@ -1837,6 +1869,7 @@ tf_gen_op_wrapper_private_py( tf_gen_op_wrapper_private_py( name = "spectral_ops_gen", + visibility = ["//tensorflow/python/ops/signal:__pkg__"], ) tf_gen_op_wrapper_private_py( @@ -1941,6 +1974,28 @@ py_library( ], ) +py_library( + name = "tensor_forest_ops", + srcs = ["ops/tensor_forest_ops.py"], + srcs_version = "PY2AND3", + deps = [ + ":framework", + ":ops", + ":tensor_forest_ops_gen", + ":training", + "//tensorflow/core/kernels/boosted_trees:boosted_trees_proto_py", + ], +) + +py_library( + name = "optional_grad", + srcs = ["ops/optional_grad.py"], + srcs_version = "PY2AND3", + deps = [ + ":framework_ops", + ], +) + py_library( name = "sets", srcs = [ @@ -2056,7 +2111,6 @@ py_library( srcs = ["ops/control_flow_ops.py"], srcs_version = "PY2AND3", deps = [ - "tensor_shape", ":array_ops", ":array_ops_gen", ":constant_op", @@ -2071,6 +2125,7 @@ py_library( ":resource_variable_ops_gen", ":sparse_tensor", ":tensor_array_ops", + ":tensor_shape", ":tf2", ":tf_should_use", ":util", @@ -2093,7 +2148,9 @@ py_library( srcs = ["ops/control_flow_util_v2.py"], srcs_version = "PY2AND3", deps = [ - "framework_ops", + ":control_flow_util", + ":framework_ops", + "//tensorflow/core:protos_all_py", "//tensorflow/python/eager:context", "//tensorflow/python/eager:function", ], @@ -2118,7 +2175,7 @@ py_library( ":graph_to_function_def", ":pywrap_tensorflow", ":util", - "//tensorflow/core:protos_all_py", + "//tensorflow/python/data/ops:dataset_ops", "//tensorflow/python/eager:function", ], ) @@ -2145,7 +2202,6 @@ py_library( ":tensor_shape", ":tensor_util", ":util", - "//tensorflow/core:protos_all_py", "//tensorflow/python/eager:function", ], ) @@ -2263,10 +2319,10 @@ py_library( ":manip_ops", ":math_grad", ":math_ops", + ":optional_grad", ":platform", ":random_grad", ":resource_variable_ops", - ":spectral_grad", ":tensor_array_ops", ":tensor_util", ":unconnected_gradients", @@ -2508,7 +2564,6 @@ py_library( ":nn_ops_gen", ":sparse_ops_gen", ":sparse_tensor", - ":spectral_ops_gen", ":state_ops", ":state_ops_gen", ":tensor_shape", @@ -2814,33 +2869,34 @@ py_test( ":framework_test_lib", ":sparse_ops", ":sparse_tensor", + "@absl_py//absl/testing:parameterized", ], ) py_library( - name = "spectral_grad", - srcs = ["ops/spectral_grad.py"], + name = "sort_ops", + srcs = ["ops/sort_ops.py"], srcs_version = "PY2AND3", deps = [ ":array_ops", ":framework", - ":framework_for_generated_wrappers", ":math_ops", - ":spectral_ops", + ":nn_ops", "//third_party/py/numpy", ], ) -py_library( - name = "spectral_ops", - srcs = ["ops/spectral_ops.py"], +py_test( + name = "sort_ops_test", + srcs = ["ops/sort_ops_test.py"], srcs_version = "PY2AND3", deps = [ ":array_ops", - ":dtypes", - ":framework_ops", - ":math_ops", - ":spectral_ops_gen", + ":client_testlib", + ":framework", + ":random_ops", + ":sort_ops", + "//third_party/py/numpy", ], ) @@ -2958,10 +3014,10 @@ py_library( ":random_ops", ":script_ops", ":session_ops", + ":sort_ops", ":sparse_grad", ":sparse_ops", ":special_math_ops", - ":spectral_grad", ":state_grad", ":state_ops", ":stateless_random_ops", @@ -2972,6 +3028,7 @@ py_library( ":util", ":variable_scope", ":variables", + "//tensorflow/python/eager:wrap_function", "//tensorflow/python/ops/distributions", "//tensorflow/python/ops/linalg", ], @@ -3066,13 +3123,16 @@ py_library( deps = [ ":array_ops", ":constant_op", + ":control_flow_ops_gen", ":data_flow_ops_gen", ":dtypes", ":errors", ":framework_ops", + ":list_ops", ":math_ops", ":tensor_shape", ":tensor_util", + ":tf2", ":tf_should_use", "//tensorflow/python/eager:context", ], @@ -3131,6 +3191,19 @@ py_library( ], ) +py_library( + name = "gradient_checker_v2", + srcs = ["ops/gradient_checker_v2.py"], + srcs_version = "PY2AND3", + deps = [ + ":array_ops", + ":framework_for_generated_wrappers", + ":gradients", + ":platform", + "//third_party/py/numpy", + ], +) + # This target is deprecated. py_library( name = "ops", @@ -3196,6 +3269,22 @@ cuda_py_test( ], ) +cuda_py_test( + name = "gradient_checker_v2_test", + size = "medium", + srcs = ["ops/gradient_checker_v2_test.py"], + additional_deps = [ + ":array_ops", + ":client_testlib", + ":framework_for_generated_wrappers", + ":math_ops", + ":nn_grad", + ":nn_ops", + ":platform", + "//third_party/py/numpy", + ], +) + cuda_py_test( name = "gradients_test", size = "medium", @@ -3304,6 +3393,9 @@ cuda_py_test( ":client_testlib", ":framework_for_generated_wrappers", ":math_ops", + "//tensorflow/python/eager:backprop", + "//tensorflow/python/eager:context", + "//tensorflow/python/eager:execution_callbacks", "//third_party/py/numpy", ], tags = ["no_windows_gpu"], @@ -3475,6 +3567,7 @@ py_library( "@six_archive//:six", "//tensorflow/core:protos_all_py", "//tensorflow/python/data/ops:dataset_ops", + "//tensorflow/python/distribute:reduce_util", "//tensorflow/python/distribute:distribute_coordinator_context", "//tensorflow/python/eager:backprop", "//tensorflow/python/eager:context", @@ -3486,6 +3579,19 @@ py_library( ], ) +# Dependency added and used by ClusterResolvers to avoid circular dependency between keras, distribute, and training. +py_library( + name = "training_server_lib", + srcs = ["training/server_lib.py"], + srcs_version = "PY2AND3", + deps = [ + ":framework", + ":pywrap_tensorflow", + ":util", + "//tensorflow/core:protos_all_py", + ], +) + py_library( name = "saveable_object", srcs = ["training/saveable_object.py"], @@ -3560,17 +3666,6 @@ py_library( ], ) -py_library( - name = "device_util", - srcs = ["training/device_util.py"], - srcs_version = "PY2AND3", - deps = [ - ":device", - ":framework_ops", - "//tensorflow/python/eager:context", - ], -) - py_library( name = "distribute", srcs = [ @@ -3579,29 +3674,7 @@ py_library( ], srcs_version = "PY2AND3", deps = [ - ":array_ops", - ":control_flow_ops", - ":device_util", - ":framework_ops", - ":platform", - ":resource_variable_ops", - ":state_ops", - ":util", - ":variable_scope", - "//tensorflow/python/data", - "//tensorflow/python/ops/losses", - ], -) - -py_test( - name = "distribute_test", - size = "small", - srcs = ["training/distribute_test.py"], - srcs_version = "PY2AND3", - deps = [ - ":client_testlib", - ":distribute", - ":variable_scope", + "//tensorflow/python/distribute:distribute_lib", ], ) @@ -4108,11 +4181,24 @@ genrule( # Get the import library of _pywrap_tensorflow_internal.dll filegroup( - name = "pywrap_tensorflow_import_lib_file", + name = "get_pywrap_tensorflow_import_lib_file", srcs = [":_pywrap_tensorflow_internal.so"], output_group = "interface_library", ) +# Rename the import library for _pywrap_tensorflow_internal.pyd to _pywrap_tensorflow_internal.lib +# (It was _pywrap_tensorflow_internal.so.if.lib). +genrule( + name = "pywrap_tensorflow_import_lib_file", + srcs = [":get_pywrap_tensorflow_import_lib_file"], + outs = ["_pywrap_tensorflow_internal.lib"], + cmd = select({ + "//tensorflow:windows": "cp -f $< $@", + "//conditions:default": "touch $@", # Just a placeholder for Unix platforms + }), + visibility = ["//visibility:public"], +) + # Create a cc_import rule for the import library of _pywrap_tensorflow_internal.dll # so that custom ops' dynamic libraries can link against it. cc_import( @@ -4590,7 +4676,6 @@ cuda_py_tests( "training/basic_loops_test.py", "training/coordinator_test.py", "training/device_setter_test.py", - "training/device_util_test.py", "training/ftrl_test.py", "training/gradient_descent_test.py", "training/learning_rate_decay_test.py", @@ -4901,7 +4986,7 @@ py_test( ":training", ":variable_scope", ":variables", - "//tensorflow/python/feature_column", + "//tensorflow/python/feature_column:feature_column_py", "//third_party/py/numpy", ], ) diff --git a/tensorflow/python/__init__.py b/tensorflow/python/__init__.py index 5da304e38c..547043030b 100644 --- a/tensorflow/python/__init__.py +++ b/tensorflow/python/__init__.py @@ -86,12 +86,12 @@ from tensorflow.python.ops import image_ops as image from tensorflow.python.ops import manip_ops as manip from tensorflow.python.ops import metrics from tensorflow.python.ops import nn +from tensorflow.python.ops import ragged from tensorflow.python.ops import sets -from tensorflow.python.ops import spectral_ops as spectral from tensorflow.python.ops.distributions import distributions from tensorflow.python.ops.linalg import linalg from tensorflow.python.ops.losses import losses -from tensorflow.python.ops import signal +from tensorflow.python.ops.signal import signal from tensorflow.python.profiler import profiler from tensorflow.python.saved_model import saved_model from tensorflow.python.summary import summary @@ -163,7 +163,7 @@ tf_export('Summary', 'summary.Summary')(Summary) tf_export('summary.SummaryDescription')(SummaryDescription) tf_export('SummaryMetadata')(SummaryMetadata) tf_export('summary.TaggedRunMetadata')(TaggedRunMetadata) -tf_export('TensorInfo')(TensorInfo) +tf_export(v1=['TensorInfo'])(TensorInfo) # pylint: enable=undefined-variable # Special dunders that we choose to export: diff --git a/tensorflow/python/autograph/converters/BUILD b/tensorflow/python/autograph/converters/BUILD index ced2e4796b..3ac446db02 100644 --- a/tensorflow/python/autograph/converters/BUILD +++ b/tensorflow/python/autograph/converters/BUILD @@ -63,7 +63,6 @@ py_test( name = "asserts_test", srcs = ["asserts_test.py"], srcs_version = "PY2AND3", - tags = ["no_windows"], deps = [ ":converters", "//tensorflow/python:client_testlib", @@ -239,7 +238,6 @@ py_test( name = "error_handlers_test", srcs = ["error_handlers_test.py"], srcs_version = "PY2AND3", - tags = ["no_windows"], deps = [ ":converters", "//tensorflow/python:client_testlib", diff --git a/tensorflow/python/autograph/converters/asserts_test.py b/tensorflow/python/autograph/converters/asserts_test.py index eef628aeb6..9ae448892a 100644 --- a/tensorflow/python/autograph/converters/asserts_test.py +++ b/tensorflow/python/autograph/converters/asserts_test.py @@ -23,12 +23,14 @@ from tensorflow.python.autograph.converters import side_effect_guards from tensorflow.python.autograph.core import converter_testing from tensorflow.python.framework import constant_op from tensorflow.python.framework import errors_impl +from tensorflow.python.framework import test_util from tensorflow.python.ops import gen_control_flow_ops from tensorflow.python.platform import test class AssertsTest(converter_testing.TestCase): + @test_util.run_deprecated_v1 def test_basic(self): def test_fn(a): @@ -41,7 +43,7 @@ class AssertsTest(converter_testing.TestCase): op = result.test_fn(constant_op.constant(False)) with self.assertRaisesRegexp(errors_impl.InvalidArgumentError, 'test message'): - sess.run(op) + self.evaluate(op) if __name__ == '__main__': diff --git a/tensorflow/python/autograph/converters/builtin_functions_test.py b/tensorflow/python/autograph/converters/builtin_functions_test.py index 30cfb13233..2683be16ec 100644 --- a/tensorflow/python/autograph/converters/builtin_functions_test.py +++ b/tensorflow/python/autograph/converters/builtin_functions_test.py @@ -24,12 +24,14 @@ from tensorflow.python.autograph.converters import builtin_functions from tensorflow.python.autograph.core import converter_testing from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.platform import test class BuiltinFunctionsTest(converter_testing.TestCase): + @test_util.run_deprecated_v1 def test_len(self): def test_fn(a): @@ -41,6 +43,7 @@ class BuiltinFunctionsTest(converter_testing.TestCase): ops = result.test_fn(p) self.assertEqual(sess.run(ops, {p: [0, 0, 0]}), 3) + @test_util.run_deprecated_v1 def test_print(self): if six.PY2: @@ -54,6 +57,7 @@ class BuiltinFunctionsTest(converter_testing.TestCase): with self.assertPrints('a\n'): sess.run(result.test_fn('a')) + @test_util.run_deprecated_v1 def test_print_multiple_values(self): if six.PY2: diff --git a/tensorflow/python/autograph/converters/call_trees.py b/tensorflow/python/autograph/converters/call_trees.py index 55cea89126..9b85fc8367 100644 --- a/tensorflow/python/autograph/converters/call_trees.py +++ b/tensorflow/python/autograph/converters/call_trees.py @@ -22,7 +22,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -from collections import namedtuple +import collections import gast @@ -35,7 +35,7 @@ from tensorflow.python.autograph.pyct import templates from tensorflow.python.util import tf_inspect -class FunctionInfo(namedtuple('FunctionInfo', ('dtype',))): +class FunctionInfo(collections.namedtuple('FunctionInfo', ('dtype',))): pass @@ -116,12 +116,19 @@ class CallTreeTransformer(converter.Base): def _function_is_compilable(self, target_entity): """Determines whether an entity can be compiled at all.""" # TODO(mdan): Expand. + if target_entity.__module__ is None: # Functions like builtins and NumPy don't expose a module. # Those in general should not be compiled. return False + if inspect_utils.isbuiltin(target_entity): return False + + if inspect_utils.isnamedtuple(target_entity): + # namedtuple doesn't expose its source code, making it uncompilable. + return False + return True def _should_compile(self, node, fqn): @@ -140,6 +147,11 @@ class CallTreeTransformer(converter.Base): if target_entity is not None: + # Currently, lambdas are always converted. + # TODO(mdan): Allow markers of the kind f = ag.do_not_convert(lambda: ...) + if inspect_utils.islambda(target_entity): + return True + # This may be reached when "calling" a callable attribute of an object. # For example: # @@ -296,7 +308,13 @@ class CallTreeTransformer(converter.Base): # safe for graph mode. return node + elif inspect_utils.isnamedtuple(target_entity): + # Although not compilable, we assume they are safe for graph mode. + node = self.generic_visit(node) + return node + else: + # TODO(mdan): Instert dynamic conversion here instead. raise NotImplementedError( 'py_func with return values (unknown function)') else: diff --git a/tensorflow/python/autograph/converters/call_trees_test.py b/tensorflow/python/autograph/converters/call_trees_test.py index 916c736fb4..454d75d755 100644 --- a/tensorflow/python/autograph/converters/call_trees_test.py +++ b/tensorflow/python/autograph/converters/call_trees_test.py @@ -18,6 +18,8 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import collections + import numpy as np from tensorflow.python.autograph.converters import call_trees @@ -85,6 +87,34 @@ class CallTreesTest(converter_testing.TestCase): tc = TestClass() self.assertEquals(3, result.test_fn_2(tc, 1)) + def test_known_called_lambda(self): + + l = lambda x: x + + def test_fn(a): + return l(a) + + ns = {'l': l} + node, ctx = self.prepare(test_fn, ns) + node = call_trees.transform(node, ctx) + + with self.compiled(node, ns) as result: + self.assertEquals(1, result.test_fn(1)) + + def test_known_called_namedtuple(self): + + nt = collections.namedtuple('TestNamedTuple', ['a']) + + def test_fn(a): + return nt(a) + + ns = {'nt': nt} + node, ctx = self.prepare(test_fn, ns) + node = call_trees.transform(node, ctx) + + with self.compiled(node, ns) as result: + self.assertEquals(nt(1), result.test_fn(1)) + def test_py_func_known_function(self): def test_fn(): @@ -94,7 +124,7 @@ class CallTreesTest(converter_testing.TestCase): dtypes.int64) as result: with self.cached_session() as sess: self.assertTrue(isinstance(result.test_fn(), ops.Tensor)) - self.assertIn(sess.run(result.test_fn()), (0, 1, 2)) + self.assertIn(self.evaluate(result.test_fn()), (0, 1, 2)) def test_uncompiled_modules(self): @@ -113,7 +143,7 @@ class CallTreesTest(converter_testing.TestCase): with self.compiled(node, ns) as result: with self.cached_session() as sess: result_tensor = result.test_fn(constant_op.constant(1)) - self.assertEquals(sess.run(result_tensor), 3) + self.assertEquals(self.evaluate(result_tensor), 3) def test_call_to_decorated_function(self): diff --git a/tensorflow/python/autograph/converters/continue_statements.py b/tensorflow/python/autograph/converters/continue_statements.py index 584cdc1efd..05e19e59fc 100644 --- a/tensorflow/python/autograph/converters/continue_statements.py +++ b/tensorflow/python/autograph/converters/continue_statements.py @@ -24,94 +24,93 @@ from tensorflow.python.autograph.pyct import templates from tensorflow.python.autograph.pyct.static_analysis.annos import NodeAnno -# Tags for local state. -CONTROL_VAR_NAME = 'control_var_name' -CONTINUE_USED = 'continue_used' -GUARD_CREATED = 'guard_created' -CREATE_GUARD_NEXT = 'create_guard_next' +class _Continue(object): + + def __init__(self): + self.used = False + self.control_var_name = None + self.create_guard = False + self.guard_created = False + + def __repr__(self): + return 'used: %s, var: %s' % (self.used, self.control_var_name) class ContinueCanonicalizationTransformer(converter.Base): """Canonicalizes continue statements into additional conditionals.""" def visit_Continue(self, node): - self.set_local(CONTINUE_USED, True) + self.state[_Continue].used = True template = """ - var_name = tf.constant(True) + var_name = True """ return templates.replace( - template, var_name=self.get_local(CONTROL_VAR_NAME)) + template, var_name=self.state[_Continue].control_var_name) def _postprocess_statement(self, node): # Example of how the state machine below works: # - # 1| stmt # State: CONTINUE_USED = False + # 1| stmt # State: Continue_.used = False # | # Action: none # 2| if cond: - # 3| continue # State: CONTINUE_USED = True, - # | # GUARD_CREATED = False, - # | # CREATE_GUARD_NEXT = False - # | # Action: set CREATE_GUARD_NEXT = True - # 4| stmt # State: CONTINUE_USED = True, - # | # GUARD_CREATED = False, - # | # CREATE_GUARD_NEXT = True + # 3| continue # State: Continue_.used = True, + # | # Continue_.guard_created = False, + # | # Continue_.create_guard = False + # | # Action: Continue_.create_guard = True + # 4| stmt # State: Continue_.used = True, + # | # Continue_.guard_created = False, + # | # Continue_.create_guard = True # | # Action: create `if not continue_used`, - # | # set GUARD_CREATED = True - # 5| stmt # State: CONTINUE_USED = True, GUARD_CREATED = True + # | # set Continue_.guard_created = True + # 5| stmt # State: Continue_.used = True, + # | # Continue_.guard_created = True # | # Action: none (will be wrapped under previously # | # created if node) - if self.get_local(CONTINUE_USED, False): - if self.get_local(GUARD_CREATED, False): + if self.state[_Continue].used: + if self.state[_Continue].guard_created: return node, None - elif not self.get_local(CREATE_GUARD_NEXT, False): - self.set_local(CREATE_GUARD_NEXT, True) + elif not self.state[_Continue].create_guard: + self.state[_Continue].create_guard = True return node, None else: - self.set_local(GUARD_CREATED, True) + self.state[_Continue].guard_created = True template = """ if not var_name: original_node """ cond, = templates.replace( template, - var_name=self.get_local(CONTROL_VAR_NAME), + var_name=self.state[_Continue].control_var_name, original_node=node) return cond, cond.body return node, None def _visit_loop_body(self, node, nodes): - self.enter_local_scope() + self.state[_Continue].enter() scope = anno.getanno(node, NodeAnno.BODY_SCOPE) continue_var = self.ctx.namer.new_symbol('continue_', scope.referenced) - self.set_local(CONTROL_VAR_NAME, continue_var) + self.state[_Continue].control_var_name = continue_var nodes = self.visit_block(nodes, after_visit=self._postprocess_statement) - if self.get_local(CONTINUE_USED, False): + if self.state[_Continue].used: template = """ - var_name = tf.constant(False) + var_name = False """ control_var_init = templates.replace(template, var_name=continue_var) nodes = control_var_init + nodes - self.exit_local_scope() + self.state[_Continue].exit() return nodes - def _visit_non_loop_body(self, nodes): - self.enter_local_scope(inherit=(CONTROL_VAR_NAME,)) - nodes = self.visit_block(nodes, after_visit=self._postprocess_statement) - continue_used = self.get_local(CONTINUE_USED, False) - self.exit_local_scope(keep=(CONTINUE_USED,)) - return nodes, continue_used - def visit_While(self, node): node.test = self.visit(node.test) node.body = self._visit_loop_body(node, node.body) # A continue in the else clause applies to the containing scope. - node.orelse, _ = self._visit_non_loop_body(node.orelse) + node.orelse = self.visit_block(node.orelse) return node def visit_For(self, node): @@ -119,21 +118,11 @@ class ContinueCanonicalizationTransformer(converter.Base): node.iter = self.generic_visit(node.iter) node.body = self._visit_loop_body(node, node.body) # A continue in the else clause applies to the containing scope. - node.orelse, _ = self._visit_non_loop_body(node.orelse) - return node - - def visit_If(self, node): - node.test = self.generic_visit(node.test) - node.body, continue_used_body = self._visit_non_loop_body(node.body) - node.orelse, continue_used_orelse = self._visit_non_loop_body(node.orelse) - self.set_local(CONTINUE_USED, continue_used_body or continue_used_orelse) - return node - - def visit_With(self, node): - node.items = self.visit_block(node.items) - node.body, _ = self._visit_non_loop_body(node.body) + node.orelse = self.visit_block(node.orelse) return node def transform(node, ctx): - return ContinueCanonicalizationTransformer(ctx).visit(node) + transformer = ContinueCanonicalizationTransformer(ctx) + node = transformer.visit(node) + return node diff --git a/tensorflow/python/autograph/converters/control_flow.py b/tensorflow/python/autograph/converters/control_flow.py index 5853e044c5..bef6cae1bb 100644 --- a/tensorflow/python/autograph/converters/control_flow.py +++ b/tensorflow/python/autograph/converters/control_flow.py @@ -106,14 +106,49 @@ class ControlFlowTransformer(converter.Base): return 'no variables' return ', '.join(map(str, symbol_set)) - def visit_If(self, node): - node = self.generic_visit(node) + def _determine_aliased_symbols(self, scope, node_defined_in, block): + if block: + block_live_in = set(anno.getanno(block[0], anno.Static.LIVE_VARS_IN)) + else: + block_live_in = set() + # For the purpose of aliasing, composite symbols with live owners are live + # as well. Otherwise this would leak tensors from the conditional's body. + # + # For example: + # + # obj = some_obj + # if cond: + # obj.a = val + # + # Thanslating to the code below would be incorrect: + # + # def true_fn(): + # obj.a = val() # Wrong! leaks ops owned by true_fn + # return obj.a + for s in scope.modified: + if s.is_composite(): + live_parents = block_live_in & s.owner_set + if live_parents: + block_live_in.add(s) + return scope.modified & node_defined_in & block_live_in + + def visit_If(self, node): body_scope = anno.getanno(node, annos.NodeAnno.BODY_SCOPE) orelse_scope = anno.getanno(node, annos.NodeAnno.ORELSE_SCOPE) defined_in = anno.getanno(node, anno.Static.DEFINED_VARS_IN) live_out = anno.getanno(node, anno.Static.LIVE_VARS_OUT) + # Note: this information needs to be extracted before the body conversion + # that happens in the call to generic_visit below, because the conversion + # generates nodes that lack static analysis annotations. + need_alias_in_body = self._determine_aliased_symbols( + body_scope, defined_in, node.body) + need_alias_in_orelse = self._determine_aliased_symbols( + orelse_scope, defined_in, node.orelse) + + node = self.generic_visit(node) + modified_in_cond = body_scope.modified | orelse_scope.modified returned_from_cond = set() for s in modified_in_cond: @@ -125,9 +160,6 @@ class ControlFlowTransformer(converter.Base): if live_out & s.owner_set: returned_from_cond.add(s) - need_alias_in_body = body_scope.modified & defined_in - need_alias_in_orelse = orelse_scope.modified & defined_in - created_in_body = body_scope.modified & returned_from_cond - defined_in created_in_orelse = orelse_scope.modified & returned_from_cond - defined_in diff --git a/tensorflow/python/autograph/converters/control_flow_test.py b/tensorflow/python/autograph/converters/control_flow_test.py index 03fdfc804e..034fcbe386 100644 --- a/tensorflow/python/autograph/converters/control_flow_test.py +++ b/tensorflow/python/autograph/converters/control_flow_test.py @@ -23,6 +23,7 @@ from tensorflow.python.autograph.core import converter_testing from tensorflow.python.autograph.pyct import transformer from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.platform import test @@ -36,6 +37,7 @@ class ControlFlowTest(converter_testing.TestCase): with self.cached_session() as sess: self.assertEqual(sess.run(result.test_fn(*inputs)), expected) + @test_util.run_deprecated_v1 def test_while_basic(self): def test_fn(n): @@ -48,6 +50,7 @@ class ControlFlowTest(converter_testing.TestCase): self.assertTransformedResult(test_fn, constant_op.constant(5), (10, 5, 5)) + @test_util.run_deprecated_v1 def test_while_nested(self): def test_fn(n): @@ -66,6 +69,7 @@ class ControlFlowTest(converter_testing.TestCase): self.assertTransformedResult(test_fn, constant_op.constant(5), (25, 5, 0, 5)) + @test_util.run_deprecated_v1 def test_while_single_output(self): def test_fn(n): @@ -86,6 +90,7 @@ class ControlFlowTest(converter_testing.TestCase): with self.assertRaises(NameError): control_flow.transform(node, ctx) + @test_util.run_deprecated_v1 def test_if_basic(self): def test_fn(n): @@ -100,6 +105,7 @@ class ControlFlowTest(converter_testing.TestCase): self.assertTransformedResult(test_fn, constant_op.constant(1), (-1, 0)) self.assertTransformedResult(test_fn, constant_op.constant(-1), (0, -2)) + @test_util.run_deprecated_v1 def test_if_complex_outputs(self): class TestClass(object): @@ -124,6 +130,7 @@ class ControlFlowTest(converter_testing.TestCase): res_obj = result.test_fn(constant_op.constant(-1), TestClass(0, 0)) self.assertEqual(sess.run((res_obj.a, res_obj.b)), (0, -2)) + @test_util.run_deprecated_v1 def test_if_single_output(self): def test_fn(n): @@ -133,6 +140,7 @@ class ControlFlowTest(converter_testing.TestCase): self.assertTransformedResult(test_fn, constant_op.constant(1), -1) + @test_util.run_deprecated_v1 def test_if_semi(self): def test_fn(n): @@ -143,6 +151,7 @@ class ControlFlowTest(converter_testing.TestCase): self.assertTransformedResult(test_fn, constant_op.constant(2), 3) self.assertTransformedResult(test_fn, constant_op.constant(-3), -3) + @test_util.run_deprecated_v1 def test_if_local_var(self): def test_fn(n): @@ -154,6 +163,7 @@ class ControlFlowTest(converter_testing.TestCase): self.assertTransformedResult(test_fn, constant_op.constant(1), 5) self.assertTransformedResult(test_fn, constant_op.constant(-1), -1) + @test_util.run_deprecated_v1 def test_if_no_outputs(self): def test_fn(n): @@ -177,6 +187,7 @@ class ControlFlowTest(converter_testing.TestCase): with self.assertRaises(transformer.AutographParseError): control_flow.transform(node, ctx) + @test_util.run_deprecated_v1 def test_simple_for(self): def test_fn(l): @@ -191,6 +202,7 @@ class ControlFlowTest(converter_testing.TestCase): empty_vector = constant_op.constant([], shape=(0,), dtype=dtypes.int32) self.assertTransformedResult(test_fn, empty_vector, (0, 0)) + @test_util.run_deprecated_v1 def test_for_single_output(self): def test_fn(l): @@ -235,6 +247,7 @@ class ControlFlowTest(converter_testing.TestCase): with self.assertRaises(NameError): control_flow.transform(node, ctx) + @test_util.run_deprecated_v1 def test_for_tuple_unpacking(self): def test_fn(x_list): z = tf.constant(0) # pylint:disable=undefined-variable diff --git a/tensorflow/python/autograph/converters/function_scopes_test.py b/tensorflow/python/autograph/converters/function_scopes_test.py index e5ce03a109..5a1248c801 100644 --- a/tensorflow/python/autograph/converters/function_scopes_test.py +++ b/tensorflow/python/autograph/converters/function_scopes_test.py @@ -22,11 +22,13 @@ from tensorflow.python.autograph.converters import function_scopes from tensorflow.python.autograph.core import converter_testing from tensorflow.python.framework import constant_op from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.platform import test class FunctionBodyTransformerTest(converter_testing.TestCase): + @test_util.run_deprecated_v1 def test_basic(self): def test_fn(l): @@ -40,6 +42,7 @@ class FunctionBodyTransformerTest(converter_testing.TestCase): self.assertIn('test_fn/', result_op.op.name) self.assertEqual('Docstring.', result.test_fn.__doc__) + @test_util.run_deprecated_v1 def test_multiline_docstring(self): tf = None @@ -58,6 +61,7 @@ class FunctionBodyTransformerTest(converter_testing.TestCase): self.assertIn('First sentence.', result.test_fn.__doc__) self.assertIn('Second sentence.', result.test_fn.__doc__) + @test_util.run_deprecated_v1 def test_nested_functions(self): def test_fn(l): @@ -74,6 +78,7 @@ class FunctionBodyTransformerTest(converter_testing.TestCase): self.assertNotIn('inner_fn', first.op.name) self.assertIn('test_fn/inner_fn/', second.op.name) + @test_util.run_deprecated_v1 def test_method(self): class TestClass(object): diff --git a/tensorflow/python/autograph/converters/lists_test.py b/tensorflow/python/autograph/converters/lists_test.py index f6da845fcc..39843c7d74 100644 --- a/tensorflow/python/autograph/converters/lists_test.py +++ b/tensorflow/python/autograph/converters/lists_test.py @@ -68,7 +68,7 @@ class ListTest(converter_testing.TestCase): with self.cached_session() as sess: tl = result.test_fn() r = list_ops.tensor_list_stack(tl, dtypes.int32) - self.assertAllEqual(sess.run(r), [1, 2, 3]) + self.assertAllEqual(self.evaluate(r), [1, 2, 3]) def test_list_pop(self): @@ -91,8 +91,8 @@ class ListTest(converter_testing.TestCase): with self.cached_session() as sess: ts, tl = result.test_fn() r = list_ops.tensor_list_stack(tl, dtypes.int32) - self.assertAllEqual(sess.run(r), [1, 2]) - self.assertAllEqual(sess.run(ts), 3) + self.assertAllEqual(self.evaluate(r), [1, 2]) + self.assertAllEqual(self.evaluate(ts), 3) def test_double_list_pop(self): @@ -123,7 +123,7 @@ class ListTest(converter_testing.TestCase): with self.compiled(node, {}, array_ops.stack, dtypes.int32) as result: with self.cached_session() as sess: - self.assertAllEqual(sess.run(result.test_fn()), [1, 2, 3]) + self.assertAllEqual(self.evaluate(result.test_fn()), [1, 2, 3]) # TODO(mdan): Add a test with tf.stack with axis kwarg. diff --git a/tensorflow/python/autograph/converters/logical_expressions_test.py b/tensorflow/python/autograph/converters/logical_expressions_test.py index 99db04a775..687412750e 100644 --- a/tensorflow/python/autograph/converters/logical_expressions_test.py +++ b/tensorflow/python/autograph/converters/logical_expressions_test.py @@ -21,11 +21,13 @@ from __future__ import print_function from tensorflow.python.autograph.converters import logical_expressions from tensorflow.python.autograph.core import converter_testing from tensorflow.python.framework import constant_op +from tensorflow.python.framework import test_util from tensorflow.python.platform import test class LogicalExpressionTest(converter_testing.TestCase): + @test_util.run_deprecated_v1 def test_equals(self): def test_fn(a, b): @@ -36,6 +38,7 @@ class LogicalExpressionTest(converter_testing.TestCase): self.assertTrue(sess.run(result.test_fn(constant_op.constant(1), 1))) self.assertFalse(sess.run(result.test_fn(constant_op.constant(1), 2))) + @test_util.run_deprecated_v1 def test_bool_ops(self): def test_fn(a, b, c): @@ -48,6 +51,7 @@ class LogicalExpressionTest(converter_testing.TestCase): self.assertFalse( sess.run(result.test_fn(constant_op.constant(True), False, True))) + @test_util.run_deprecated_v1 def test_comparison(self): def test_fn(a, b, c, d): diff --git a/tensorflow/python/autograph/converters/side_effect_guards_test.py b/tensorflow/python/autograph/converters/side_effect_guards_test.py index cef3199169..645267e560 100644 --- a/tensorflow/python/autograph/converters/side_effect_guards_test.py +++ b/tensorflow/python/autograph/converters/side_effect_guards_test.py @@ -23,6 +23,7 @@ from tensorflow.python.autograph.core import converter_testing from tensorflow.python.framework import constant_op from tensorflow.python.framework import errors_impl from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import state_ops from tensorflow.python.ops import variable_scope @@ -34,6 +35,7 @@ tf = None # Will be replaced by a mock. class SideEffectGuardsTest(converter_testing.TestCase): + @test_util.run_deprecated_v1 def test_side_effect_on_return_only_variable(self): def test_fn(a): @@ -48,12 +50,12 @@ class SideEffectGuardsTest(converter_testing.TestCase): with self.compiled(node, {}, state_ops.assign) as result: with self.cached_session() as sess: v = variable_scope.get_variable('test', initializer=2) - sess.run(v.initializer) - sess.run(result.test_fn(v)) + self.evaluate(v.initializer) + self.evaluate(result.test_fn(v)) # TODO(mdan): Add support for this use case. # Right now the variable `a` is not conditioned on the `assign` because # there's no way to add control dependencies to a variable object. - self.assertEqual(2, sess.run(v)) + self.assertEqual(2, self.evaluate(v)) def test_side_effect_on_used_variable(self): @@ -69,12 +71,13 @@ class SideEffectGuardsTest(converter_testing.TestCase): with self.compiled(node, {}, state_ops.assign) as result: with self.cached_session() as sess: v = variable_scope.get_variable('test', initializer=2) - sess.run(v.initializer) - sess.run(result.test_fn(v)) + self.evaluate(v.initializer) + self.evaluate(result.test_fn(v)) # TODO(mdan): Ensure the result of test_fn(v) is also deterministic. # Right now it's 3 or 4 based on whether the read is synchronized. - self.assertEqual(3, sess.run(v)) + self.assertEqual(3, self.evaluate(v)) + @test_util.run_deprecated_v1 def test_side_effect_on_tensor(self): def test_fn(a): @@ -109,10 +112,10 @@ class SideEffectGuardsTest(converter_testing.TestCase): with self.compiled(node, {}, state_ops.assign_add) as result: with self.cached_session() as sess: v = variable_scope.get_variable('test', initializer=2) - sess.run(v.initializer) - sess.run(result.test_fn(v)) + self.evaluate(v.initializer) + self.evaluate(result.test_fn(v)) # TODO(mdan): Ensure the result of test_fn(v) is also deterministic. - self.assertEqual(4, sess.run(v)) + self.assertEqual(4, self.evaluate(v)) def test_multiline_nested_block(self): @@ -130,10 +133,10 @@ class SideEffectGuardsTest(converter_testing.TestCase): with self.compiled(node, {}, state_ops.assign, ops.name_scope) as result: with self.cached_session() as sess: v = variable_scope.get_variable('test', initializer=2) - sess.run(v.initializer) - sess.run(result.test_fn(v)) + self.evaluate(v.initializer) + self.evaluate(result.test_fn(v)) # TODO(mdan): Ensure the result of test_fn(v) is also deterministic. - self.assertEqual(3, sess.run(v)) + self.assertEqual(3, self.evaluate(v)) def test_multiline_block_unsafe(self): @@ -153,10 +156,10 @@ class SideEffectGuardsTest(converter_testing.TestCase): state_ops.assign_add) as result: with self.cached_session() as sess: v = variable_scope.get_variable('test', initializer=2) - sess.run(v.initializer) - sess.run(result.test_fn(v)) + self.evaluate(v.initializer) + self.evaluate(result.test_fn(v)) # TODO(mdan): Ensure the result of test_fn(v) is also deterministic. - self.assertEqual(4, sess.run(v)) + self.assertEqual(4, self.evaluate(v)) if __name__ == '__main__': diff --git a/tensorflow/python/autograph/converters/slices_test.py b/tensorflow/python/autograph/converters/slices_test.py index e190a7cfe8..bd049afdfc 100644 --- a/tensorflow/python/autograph/converters/slices_test.py +++ b/tensorflow/python/autograph/converters/slices_test.py @@ -49,7 +49,7 @@ class SliceTest(converter_testing.TestCase): tl = list_ops.tensor_list_from_tensor( [1, 2], element_shape=constant_op.constant([], dtype=dtypes.int32)) y = result.test_fn(tl) - self.assertEqual(2, sess.run(y)) + self.assertEqual(2, self.evaluate(y)) def test_index_access_multiple_definitions(self): diff --git a/tensorflow/python/autograph/core/converter.py b/tensorflow/python/autograph/core/converter.py index 49e24895a2..e88c4674ee 100644 --- a/tensorflow/python/autograph/core/converter.py +++ b/tensorflow/python/autograph/core/converter.py @@ -82,6 +82,7 @@ from tensorflow.python.autograph.pyct.static_analysis import live_values from tensorflow.python.autograph.pyct.static_analysis import liveness from tensorflow.python.autograph.pyct.static_analysis import reaching_definitions from tensorflow.python.autograph.pyct.static_analysis import type_info +from tensorflow.python.eager import function # TODO(mdan): These contexts can be refactored into first class objects. # For example, we could define Program and Entity abstractions that hold on @@ -96,7 +97,7 @@ class Verbosity(IntEnum): Attributes: * BRIEF: No logging, minimal error messages. * VERBOSE: Detailed logging of generated code, detailed error messages. - """ + """ BRIEF = 0 VERBOSE = 1 @@ -151,7 +152,7 @@ class ConversionOptions(object): optional_features=Feature.ALL): self.recursive = recursive self.verbose = verbose - self.strip_decorators = strip_decorators or () + self._strip_decorators = strip_decorators or () self.force_conversion = force_conversion # TODO(mdan): Rename to conversion_recursion_depth? self.internal_convert_user_code = internal_convert_user_code @@ -161,6 +162,12 @@ class ConversionOptions(object): optional_features = frozenset(optional_features) self.optional_features = optional_features + @property + def strip_decorators(self): + # A few decorators are included by default. + # TODO(mdan): Revert if function.defun becomes a public symbol. + return self._strip_decorators + (function.defun,) + def uses(self, feature): return (Feature.ALL in self.optional_features or feature in self.optional_features) @@ -216,7 +223,7 @@ class ConversionOptions(object): as_qualified_name(ConversionOptions)), recursive_val=parser.parse_expression(str(self.recursive)), verbose_val=parser.parse_expression(str(int(self.verbose))), - strip_decorators_val=list_of_names(self.strip_decorators), + strip_decorators_val=list_of_names(self._strip_decorators), force_conversion_val=parser.parse_expression( str(self.force_conversion)), internal_convert_user_code_val=parser.parse_expression( diff --git a/tensorflow/python/autograph/core/converter_testing.py b/tensorflow/python/autograph/core/converter_testing.py index 7b0608d03f..f1374081d3 100644 --- a/tensorflow/python/autograph/core/converter_testing.py +++ b/tensorflow/python/autograph/core/converter_testing.py @@ -32,6 +32,7 @@ from tensorflow.python.autograph.core import errors from tensorflow.python.autograph.core import function_wrapping from tensorflow.python.autograph.lang import special_functions from tensorflow.python.autograph.pyct import compiler +from tensorflow.python.autograph.pyct import inspect_utils from tensorflow.python.autograph.pyct import origin_info from tensorflow.python.autograph.pyct import parser from tensorflow.python.autograph.pyct import pretty_printer @@ -43,7 +44,7 @@ def imported_decorator(f): return lambda a: f(a) + 1 -# TODO(mdan): We might be able to use the real namer here. +# TODO(mdan): We should use the real namer here. class FakeNamer(object): """A fake namer that uses a global counter to generate unique names.""" @@ -61,7 +62,8 @@ class FakeNamer(object): original_fqn, live_entity=None, owner_type=None): - del live_entity + if inspect_utils.islambda(live_entity): + return None, False if owner_type is not None: return None, False return ('renamed_%s' % '_'.join(original_fqn)), True diff --git a/tensorflow/python/autograph/core/errors_test.py b/tensorflow/python/autograph/core/errors_test.py index aa6c293268..845a28a522 100644 --- a/tensorflow/python/autograph/core/errors_test.py +++ b/tensorflow/python/autograph/core/errors_test.py @@ -22,6 +22,7 @@ from tensorflow.python.autograph.core import errors from tensorflow.python.autograph.pyct import origin_info from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors as tf_errors +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.platform import test from tensorflow.python.util import tf_inspect @@ -47,6 +48,7 @@ class RuntimeErrorsTest(test.TestCase): 'test_comment') return loc, origin + @test_util.run_deprecated_v1 def test_improved_errors_basic(self): loc, origin = self.fake_origin(zero_div, 2) zero_div_caller.ag_source_map = {loc: origin} @@ -55,13 +57,14 @@ class RuntimeErrorsTest(test.TestCase): with self.assertRaises(errors.TfRuntimeError) as cm: with errors.improved_errors(zero_div_caller): with self.cached_session() as sess: - sess.run(ops) + self.evaluate(ops) for frame in cm.exception.custom_traceback: _, _, function_name, _ = frame self.assertNotEqual('zero_div', function_name) self.assertIn(origin.as_frame(), set(cm.exception.custom_traceback)) + @test_util.run_deprecated_v1 def test_improved_errors_no_matching_lineno(self): loc, origin = self.fake_origin(zero_div, -1) zero_div_caller.ag_source_map = {loc: origin} @@ -70,7 +73,7 @@ class RuntimeErrorsTest(test.TestCase): with self.assertRaises(errors.TfRuntimeError) as cm: with errors.improved_errors(zero_div_caller): with self.cached_session() as sess: - sess.run(ops) + self.evaluate(ops) all_function_names = set() for frame in cm.exception.custom_traceback: @@ -79,6 +82,7 @@ class RuntimeErrorsTest(test.TestCase): self.assertNotEqual('test_function_name', function_name) self.assertIn('zero_div', all_function_names) + @test_util.run_deprecated_v1 def test_improved_errors_failures(self): loc, _ = self.fake_origin(zero_div, 2) zero_div_caller.ag_source_map = {loc: 'bogus object'} @@ -87,7 +91,7 @@ class RuntimeErrorsTest(test.TestCase): with self.assertRaises(tf_errors.InvalidArgumentError): with errors.improved_errors(zero_div_caller): with self.cached_session() as sess: - sess.run(ops) + self.evaluate(ops) def test_improved_errors_validation(self): with self.assertRaisesRegexp( diff --git a/tensorflow/python/autograph/core/function_wrapping_test.py b/tensorflow/python/autograph/core/function_wrapping_test.py index 5e217055c7..7e21b979db 100644 --- a/tensorflow/python/autograph/core/function_wrapping_test.py +++ b/tensorflow/python/autograph/core/function_wrapping_test.py @@ -20,11 +20,13 @@ from __future__ import print_function from tensorflow.python.autograph.core import function_wrapping from tensorflow.python.framework import constant_op +from tensorflow.python.framework import test_util from tensorflow.python.platform import test class FunctionWrappingTest(test.TestCase): + @test_util.run_deprecated_v1 def test_function_scope_name(self): with function_wrapping.function_scope('test_name'): t = constant_op.constant(1) diff --git a/tensorflow/python/autograph/core/naming.py b/tensorflow/python/autograph/core/naming.py index 43fcbcfc03..b8d79daeba 100644 --- a/tensorflow/python/autograph/core/naming.py +++ b/tensorflow/python/autograph/core/naming.py @@ -18,8 +18,8 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +from tensorflow.python.autograph.pyct import inspect_utils from tensorflow.python.autograph.pyct import qual_names -from tensorflow.python.util import tf_inspect class Namer(object): @@ -77,8 +77,7 @@ class Namer(object): if not self.recursive: return None, False - if (live_entity is not None and tf_inspect.isfunction(live_entity) and - live_entity.__name__ == ''): + if (live_entity is not None and inspect_utils.islambda(live_entity)): return None, False if owner_type is not None and owner_type not in self.partial_types: diff --git a/tensorflow/python/autograph/impl/BUILD b/tensorflow/python/autograph/impl/BUILD index 2f9037c43b..201a888754 100644 --- a/tensorflow/python/autograph/impl/BUILD +++ b/tensorflow/python/autograph/impl/BUILD @@ -41,7 +41,6 @@ py_test( name = "api_test", srcs = ["api_test.py"], srcs_version = "PY2AND3", - tags = ["no_windows"], deps = [ ":impl", "//tensorflow/python:client_testlib", @@ -54,7 +53,6 @@ py_test( name = "conversion_test", srcs = ["conversion_test.py"], srcs_version = "PY2AND3", - tags = ["no_windows"], deps = [ ":impl", "//tensorflow/python:client_testlib", diff --git a/tensorflow/python/autograph/impl/api.py b/tensorflow/python/autograph/impl/api.py index 69674b2be3..19a472064a 100644 --- a/tensorflow/python/autograph/impl/api.py +++ b/tensorflow/python/autograph/impl/api.py @@ -195,6 +195,17 @@ def converted_call(f, owner, options, *args, **kwargs): if not options.internal_convert_user_code: return f(*args, **kwargs) + # Unwrap functools.partial objects + # TODO(allenl, mdan): Consider sharing unwrapping logic with tf_inspect. + while isinstance(f, functools.partial): + args = f.args + args + new_kwargs = {} + if f.keywords is not None: + new_kwargs.update(f.keywords) + new_kwargs.update(kwargs) + kwargs = new_kwargs + f = f.func + if tf_inspect.isfunction(f) or tf_inspect.ismethod(f): # Regular functions target_entity = f diff --git a/tensorflow/python/autograph/impl/api_test.py b/tensorflow/python/autograph/impl/api_test.py index ef577568c4..66edda5119 100644 --- a/tensorflow/python/autograph/impl/api_test.py +++ b/tensorflow/python/autograph/impl/api_test.py @@ -18,6 +18,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import functools import gc import numpy as np @@ -28,6 +29,7 @@ from tensorflow.python.autograph.impl import api from tensorflow.python.autograph.pyct import parser from tensorflow.python.autograph.utils import py_func from tensorflow.python.framework import constant_op +from tensorflow.python.framework import test_util from tensorflow.python.keras.engine import sequential from tensorflow.python.keras.layers import core from tensorflow.python.ops import variables @@ -43,6 +45,7 @@ class TestResource(str): class ApiTest(test.TestCase): + @test_util.run_deprecated_v1 def test_decorator_recurses(self): class TestClass(object): @@ -63,8 +66,9 @@ class ApiTest(test.TestCase): x = tc.test_method( constant_op.constant([2, 4]), constant_op.constant(1), constant_op.constant(-2)) - self.assertListEqual([0, 1], sess.run(x).tolist()) + self.assertListEqual([0, 1], self.evaluate(x).tolist()) + @test_util.run_deprecated_v1 def test_decorator_does_not_recurse(self): class TestClass(object): @@ -83,8 +87,9 @@ class ApiTest(test.TestCase): x = tc.test_method( constant_op.constant([2, 4]), constant_op.constant(1), constant_op.constant(-2)) - self.assertListEqual([0, 1], sess.run(x).tolist()) + self.assertListEqual([0, 1], self.evaluate(x).tolist()) + @test_util.run_deprecated_v1 def test_decorator_calls_unconverted_graph(self): class TestClass(object): @@ -104,8 +109,9 @@ class ApiTest(test.TestCase): x = tc.test_method( constant_op.constant([2, 4]), constant_op.constant(1), constant_op.constant(-2)) - self.assertListEqual([0, 1], sess.run(x).tolist()) + self.assertListEqual([0, 1], self.evaluate(x).tolist()) + @test_util.run_deprecated_v1 def test_decorator_calls_unconverted_py_func(self): class TestClass(object): @@ -130,8 +136,9 @@ class ApiTest(test.TestCase): x = tc.test_method( constant_op.constant([2, 4]), constant_op.constant(1), constant_op.constant(-2)) - self.assertListEqual([0, 1], sess.run(x).tolist()) + self.assertListEqual([0, 1], self.evaluate(x).tolist()) + @test_util.run_deprecated_v1 def test_decorator_calls_decorated(self): class TestClass(object): @@ -153,7 +160,7 @@ class ApiTest(test.TestCase): x = tc.test_method( constant_op.constant([2, 4]), constant_op.constant(1), constant_op.constant(-2)) - self.assertListEqual([0, 1], sess.run(x).tolist()) + self.assertListEqual([0, 1], self.evaluate(x).tolist()) def test_decorator_preserves_argspec(self): @@ -171,6 +178,7 @@ class ApiTest(test.TestCase): list(tf_inspect.getfullargspec(tc.called_member)), list(tf_inspect.getfullargspec(tc.called_member_converted))) + @test_util.run_deprecated_v1 def test_convert_call_site_decorator(self): class TestClass(object): @@ -192,7 +200,7 @@ class ApiTest(test.TestCase): x = tc.test_method( constant_op.constant([2, 4]), constant_op.constant(1), constant_op.constant(-2)) - self.assertListEqual([0, 1], sess.run(x).tolist()) + self.assertListEqual([0, 1], self.evaluate(x).tolist()) def test_converted_call_builtin(self): x = api.converted_call(range, None, converter.ConversionOptions(), 3) @@ -208,7 +216,27 @@ class ApiTest(test.TestCase): with self.cached_session() as sess: x = api.converted_call(test_fn, None, converter.ConversionOptions(), constant_op.constant(-1)) - self.assertEqual(1, sess.run(x)) + self.assertEqual(1, self.evaluate(x)) + + def test_converted_call_functools_partial(self): + + def test_fn(x, y, z): + if x < 0: + return -x, -y, -z + return x, y, z + + x = api.converted_call( + functools.partial(test_fn, constant_op.constant(-1), z=-3), + None, converter.ConversionOptions(), + constant_op.constant(-2)) + self.assertEqual((1, 2, 3), self.evaluate(x)) + + x = api.converted_call( + functools.partial( + functools.partial(test_fn, constant_op.constant(-1)), z=-3), + None, converter.ConversionOptions(), + constant_op.constant(-2)) + self.assertEqual((1, 2, 3), self.evaluate(x)) def test_converted_call_method_explicit_owner(self): # TODO(mdan): Implement. @@ -234,7 +262,7 @@ class ApiTest(test.TestCase): tc = TestClass(constant_op.constant(-1)) x = api.converted_call(tc.test_method, None, converter.ConversionOptions(), tc) - self.assertEqual(1, sess.run(x)) + self.assertEqual(1, self.evaluate(x)) def test_converted_call_method_by_class(self): @@ -252,7 +280,7 @@ class ApiTest(test.TestCase): tc = TestClass(constant_op.constant(-1)) x = api.converted_call(TestClass.test_method, None, converter.ConversionOptions(), tc) - self.assertEqual(1, sess.run(x)) + self.assertEqual(1, self.evaluate(x)) def test_converted_call_callable_object(self): @@ -269,7 +297,7 @@ class ApiTest(test.TestCase): with self.cached_session() as sess: tc = TestClass(constant_op.constant(-1)) x = api.converted_call(tc, None, converter.ConversionOptions()) - self.assertEqual(1, sess.run(x)) + self.assertEqual(1, self.evaluate(x)) def test_converted_call_constructor(self): @@ -288,7 +316,7 @@ class ApiTest(test.TestCase): constant_op.constant(-1)) # tc is now a converted object. x = tc.test_method() - self.assertEqual(1, sess.run(x)) + self.assertEqual(1, self.evaluate(x)) def test_converted_call_already_converted(self): @@ -298,13 +326,14 @@ class ApiTest(test.TestCase): with self.cached_session() as sess: x = api.converted_call(f, None, converter.ConversionOptions(), constant_op.constant(0)) - self.assertTrue(sess.run(x)) + self.assertTrue(self.evaluate(x)) converted_f = api.to_graph(f) x = api.converted_call(converted_f, None, converter.ConversionOptions(), constant_op.constant(0)) - self.assertTrue(sess.run(x)) + self.assertTrue(self.evaluate(x)) + @test_util.run_deprecated_v1 def test_converted_call_no_user_code(self): def f(x): @@ -334,8 +363,8 @@ class ApiTest(test.TestCase): constant_op.constant([[0.0]]), training=True) with self.cached_session() as sess: - sess.run(variables.global_variables_initializer()) - self.assertAllEqual([[0.0, 0.0]], sess.run(x)) + self.evaluate(variables.global_variables_initializer()) + self.assertAllEqual([[0.0, 0.0]], self.evaluate(x)) def test_converted_call_whitelisted_method_extra_self(self): @@ -349,8 +378,8 @@ class ApiTest(test.TestCase): model, constant_op.constant([[0.0]]), training=True) with self.cached_session() as sess: - sess.run(variables.global_variables_initializer()) - self.assertAllEqual([[0.0, 0.0]], sess.run(x)) + self.evaluate(variables.global_variables_initializer()) + self.assertAllEqual([[0.0, 0.0]], self.evaluate(x)) def test_converted_call_whitelisted_method_via_owner(self): @@ -364,8 +393,8 @@ class ApiTest(test.TestCase): constant_op.constant([[0.0]]), training=True) with self.cached_session() as sess: - sess.run(variables.global_variables_initializer()) - self.assertAllEqual([[0.0, 0.0]], sess.run(x)) + self.evaluate(variables.global_variables_initializer()) + self.assertAllEqual([[0.0, 0.0]], self.evaluate(x)) def test_converted_call_lambda(self): @@ -376,9 +405,10 @@ class ApiTest(test.TestCase): x = api.converted_call(l, None, opts, constant_op.constant(0)) with self.cached_session() as sess: - sess.run(variables.global_variables_initializer()) - self.assertAllEqual(True, sess.run(x)) + self.evaluate(variables.global_variables_initializer()) + self.assertAllEqual(True, self.evaluate(x)) + @test_util.run_deprecated_v1 def test_to_graph_basic(self): def test_fn(x, s): @@ -390,8 +420,9 @@ class ApiTest(test.TestCase): with self.cached_session() as sess: x = compiled_fn(constant_op.constant([4, 8]), 4) - self.assertListEqual([1, 2], sess.run(x).tolist()) + self.assertListEqual([1, 2], self.evaluate(x).tolist()) + @test_util.run_deprecated_v1 def test_to_graph_with_defaults(self): foo = 4 @@ -405,7 +436,7 @@ class ApiTest(test.TestCase): with self.cached_session() as sess: x = compiled_fn(constant_op.constant([4, 8])) - self.assertListEqual([1, 2], sess.run(x).tolist()) + self.assertListEqual([1, 2], self.evaluate(x).tolist()) def test_to_code_basic(self): diff --git a/tensorflow/python/autograph/impl/conversion.py b/tensorflow/python/autograph/impl/conversion.py index 328a4b5fe4..f8decd24e8 100644 --- a/tensorflow/python/autograph/impl/conversion.py +++ b/tensorflow/python/autograph/impl/conversion.py @@ -18,6 +18,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import functools import imp import gast @@ -72,12 +73,31 @@ def is_whitelisted_for_graph(o): Returns: Boolean """ - m = tf_inspect.getmodule(o) + # TODO(b/120224672): Fix this. + if isinstance(o, functools.partial): + # tf_inspect.getmodule(functools.partial(...)) otherwise returns None since + # functools.partial objects do not have a __module__ attribute. + m = functools + else: + m = tf_inspect.getmodule(o) for prefix, in config.DEFAULT_UNCOMPILED_MODULES: if m.__name__.startswith(prefix): return True + if hasattr(o, 'autograph_info__'): return True + + if inspect_utils.isnamedtuple(o): + # Due to the way they're constructed, namedtuple types cannot be converted + # because they don't expose source code. But we assume they are safe for + # graph mode since they are just containers. + if tf_inspect.isclass(o) and len(o.__bases__) > 1: + logging.log_first_n( + logging.level_warning(), + 'Entity {} looks like a namedtuple subclass. If it has any custom' + ' methods, they will not be converted by AutoGraph.'.format(o), 1) + return True + return False @@ -281,11 +301,10 @@ def function_to_graph(f, node, source = parser.parse_entity(f) node = node.body[0] - # In general, the output of inspect.getsource is inexact because it uses crude - # regex matching methods to search the source file. This is particularly - # problematic for lambda functions, where the entire containing lines are - # returned. Certain distributions of CPython may also return the enclosing - # function for local functions. + # In general, the output of inspect.getsource is inexact because it uses + # regex matching to adjust the exact location around the line number that + # CPython records. This is particularly problematic for lambda functions, + # where the entire containing lines are returned. nodes = ast_util.find_matching_definitions(node, f) if len(nodes) != 1: if f.__name__ == '': @@ -298,8 +317,8 @@ def function_to_graph(f, raise ValueError( 'Unable to identify source code of function {}. The source code' ' reported by Python did not include exactly one matching signature:' - '\n{}\nTo avoid ambiguity, use a unique name for each' - ' function.'.format(f, source)) + '\n{}\n. This is an extremely rare occurrence. Please report it to' + ' the TensorFlow team.'.format(f, source)) node, = nodes # TODO(znado): Place inside standard_analysis. diff --git a/tensorflow/python/autograph/lang/special_functions_test.py b/tensorflow/python/autograph/lang/special_functions_test.py index 123ee65b32..8d40f4036c 100644 --- a/tensorflow/python/autograph/lang/special_functions_test.py +++ b/tensorflow/python/autograph/lang/special_functions_test.py @@ -36,7 +36,7 @@ class SpecialFunctionsTest(test.TestCase): python_one = special_functions.match_staging_level(1, 1) with self.cached_session() as sess: self.assertTrue(tensor_util.is_tensor(tensor_one)) - self.assertAllEqual(sess.run(tensor_one), 1) + self.assertAllEqual(self.evaluate(tensor_one), 1) self.assertEqual(python_one, 1) def test_tensor_list_empty_list(self): @@ -45,21 +45,21 @@ class SpecialFunctionsTest(test.TestCase): element_shape=()) sl = list_ops.tensor_list_stack(l, element_dtype=dtypes.int32) with self.cached_session() as sess: - self.assertAllEqual(sess.run(sl), []) + self.assertAllEqual(self.evaluate(sl), []) l = special_functions.tensor_list((), element_dtype=dtypes.int32, element_shape=()) sl = list_ops.tensor_list_stack(l, element_dtype=dtypes.int32) with self.cached_session() as sess: - self.assertAllEqual(sess.run(sl), []) + self.assertAllEqual(self.evaluate(sl), []) def test_tensor_list_tensor(self): l = special_functions.tensor_list( constant_op.constant([], dtype=dtypes.int32)) sl = list_ops.tensor_list_stack(l, element_dtype=dtypes.int32) with self.cached_session() as sess: - self.assertAllEqual(sess.run(sl), []) + self.assertAllEqual(self.evaluate(sl), []) def test_tensor_list_unsupported_initializer(self): with self.assertRaisesRegexp(ValueError, 'unknown type'): @@ -76,7 +76,7 @@ class SpecialFunctionsTest(test.TestCase): l = special_functions.tensor_list(elements) sl = list_ops.tensor_list_stack(l, element_dtype=dtypes.int32) with self.cached_session() as sess: - self.assertAllEqual(sess.run(sl), [[1, 2], [3, 4]]) + self.assertAllEqual(self.evaluate(sl), [[1, 2], [3, 4]]) def test_tensor_list_array_from_elements(self): elements = [constant_op.constant([1, 2]), constant_op.constant([3, 4])] @@ -84,7 +84,7 @@ class SpecialFunctionsTest(test.TestCase): l = special_functions.tensor_list(elements, use_tensor_array=True) sl = l.stack() with self.cached_session() as sess: - self.assertAllEqual(sess.run(sl), [[1, 2], [3, 4]]) + self.assertAllEqual(self.evaluate(sl), [[1, 2], [3, 4]]) def test_stack(self): self.assertEqual(special_functions.stack(1, strict=False), 1) diff --git a/tensorflow/python/autograph/operators/control_flow.py b/tensorflow/python/autograph/operators/control_flow.py index 6eedd695a7..670897744a 100644 --- a/tensorflow/python/autograph/operators/control_flow.py +++ b/tensorflow/python/autograph/operators/control_flow.py @@ -61,7 +61,7 @@ def for_stmt(iter_, extra_test, body, init_state): """ if tensor_util.is_tensor(iter_): return _known_len_for_stmt(iter_, extra_test, body, init_state) - elif isinstance(iter_, dataset_ops.Dataset): + elif isinstance(iter_, dataset_ops.DatasetV2): return _dataset_for_stmt(iter_, extra_test, body, init_state) else: return _py_for_stmt(iter_, extra_test, body, init_state) @@ -123,7 +123,7 @@ def _dataset_for_stmt(ds, extra_test, body, init_state): (dataset_ops.Dataset.from_tensors(tag).repeat(), ds)) ds_with_epoch = epoch_numbers.flat_map(lambda i: tag_with(ds, i)) - iterator = ds_with_epoch.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(ds_with_epoch) with ops.control_dependencies((iterator.initializer,)): epoch_number, iterate = iterator.get_next() diff --git a/tensorflow/python/autograph/operators/control_flow_test.py b/tensorflow/python/autograph/operators/control_flow_test.py index 2dea18dc5f..0a7d4b6402 100644 --- a/tensorflow/python/autograph/operators/control_flow_test.py +++ b/tensorflow/python/autograph/operators/control_flow_test.py @@ -22,12 +22,14 @@ from tensorflow.python.autograph.operators import control_flow from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import math_ops from tensorflow.python.platform import test class ForLoopTest(test.TestCase): + @test_util.run_deprecated_v1 def test_tensor(self): s = control_flow.for_stmt( constant_op.constant([1, 2, 3, 4]), @@ -35,7 +37,7 @@ class ForLoopTest(test.TestCase): body=lambda i, s: (s + i,), init_state=(0,)) with self.cached_session() as sess: - self.assertEqual((10,), sess.run(s)) + self.assertEqual((10,), self.evaluate(s)) def test_python(self): s = control_flow.for_stmt( @@ -45,6 +47,7 @@ class ForLoopTest(test.TestCase): init_state=(0,)) self.assertEqual(10, s) + @test_util.run_deprecated_v1 def test_dataset(self): to_int32 = lambda i: math_ops.cast(i, dtypes.int32) s = control_flow.for_stmt( @@ -53,11 +56,12 @@ class ForLoopTest(test.TestCase): body=lambda i, s: (s + i,), init_state=(0,)) with self.cached_session() as sess: - self.assertEqual((10,), sess.run(s)) + self.assertEqual((10,), self.evaluate(s)) class WhileLoopTest(test.TestCase): + @test_util.run_deprecated_v1 def test_tensor(self): n = constant_op.constant(5) results = control_flow.while_stmt( @@ -66,7 +70,7 @@ class WhileLoopTest(test.TestCase): init_state=(0, 0), extra_deps=(n,)) with self.cached_session() as sess: - self.assertEqual((5, 10), sess.run(results)) + self.assertEqual((5, 10), self.evaluate(results)) def test_python(self): n = 5 @@ -87,23 +91,25 @@ class IfStmtTest(test.TestCase): return control_flow.if_stmt( cond=cond, body=lambda: (1, 2), orelse=lambda: (-1, -2)) + @test_util.run_deprecated_v1 def test_tensor(self): with self.cached_session() as sess: t = self.single_return_if_stmt(constant_op.constant(True)) - self.assertEqual(1, sess.run(t)) + self.assertEqual(1, self.evaluate(t)) t = self.single_return_if_stmt(constant_op.constant(False)) - self.assertEqual(-1, sess.run(t)) + self.assertEqual(-1, self.evaluate(t)) def test_python(self): self.assertEqual(1, self.single_return_if_stmt(True)) self.assertEqual(-1, self.single_return_if_stmt(False)) + @test_util.run_deprecated_v1 def test_tensor_multiple_returns(self): with self.cached_session() as sess: t = self.multi_return_if_stmt(constant_op.constant(True)) - self.assertAllEqual([1, 2], sess.run(t)) + self.assertAllEqual([1, 2], self.evaluate(t)) t = self.multi_return_if_stmt(constant_op.constant(False)) - self.assertAllEqual([-1, -2], sess.run(t)) + self.assertAllEqual([-1, -2], self.evaluate(t)) def test_python_multiple_returns(self): self.assertEqual((1, 2), self.multi_return_if_stmt(True)) diff --git a/tensorflow/python/autograph/operators/data_structures_test.py b/tensorflow/python/autograph/operators/data_structures_test.py index 6039b07982..c5a3a3d1ca 100644 --- a/tensorflow/python/autograph/operators/data_structures_test.py +++ b/tensorflow/python/autograph/operators/data_structures_test.py @@ -22,6 +22,7 @@ from tensorflow.python.autograph.operators import data_structures from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import list_ops from tensorflow.python.ops import tensor_array_ops from tensorflow.python.platform import test @@ -43,7 +44,7 @@ class ListTest(test.TestCase): l = data_structures.tf_tensor_list_new([3, 4, 5]) t = list_ops.tensor_list_stack(l, element_dtype=dtypes.int32) with self.cached_session() as sess: - self.assertAllEqual(sess.run(t), [3, 4, 5]) + self.assertAllEqual(self.evaluate(t), [3, 4, 5]) def test_tf_tensor_list_new_empty(self): l = data_structures.tf_tensor_list_new([], @@ -51,14 +52,15 @@ class ListTest(test.TestCase): element_shape=()) t = list_ops.tensor_list_stack(l, element_dtype=dtypes.int32) with self.cached_session() as sess: - self.assertAllEqual(sess.run(t), []) + self.assertAllEqual(self.evaluate(t), []) def test_tf_tensor_list_new_from_tensor(self): l = data_structures.tf_tensor_list_new(constant_op.constant([3, 4, 5])) t = list_ops.tensor_list_stack(l, element_dtype=dtypes.int32) with self.cached_session() as sess: - self.assertAllEqual(sess.run(t), [3, 4, 5]) + self.assertAllEqual(self.evaluate(t), [3, 4, 5]) + @test_util.run_deprecated_v1 def test_tf_tensor_list_new_illegal_input(self): with self.assertRaises(ValueError): data_structures.tf_tensor_list_new([3, 4.0]) @@ -77,7 +79,7 @@ class ListTest(test.TestCase): l = data_structures.tf_tensor_array_new([3, 4, 5]) t = l.stack() with self.cached_session() as sess: - self.assertAllEqual(sess.run(t), [3, 4, 5]) + self.assertAllEqual(self.evaluate(t), [3, 4, 5]) def test_tf_tensor_array_new_illegal_input(self): with self.assertRaises(ValueError): @@ -102,15 +104,16 @@ class ListTest(test.TestCase): t = list_ops.tensor_list_stack(l, element_dtype=x.dtype) with self.cached_session() as sess: - self.assertAllEqual(sess.run(t), [[1, 2, 3]]) + self.assertAllEqual(self.evaluate(t), [[1, 2, 3]]) + @test_util.run_v1_only("b/117943489") def test_append_tensorarray(self): l = tensor_array_ops.TensorArray(dtypes.int32, size=0, dynamic_size=True) l1 = data_structures.list_append(l, 1) l2 = data_structures.list_append(l1, 2) with self.cached_session() as sess: - self.assertAllEqual(sess.run(l1.stack()), [1]) - self.assertAllEqual(sess.run(l2.stack()), [1, 2]) + self.assertAllEqual(self.evaluate(l1.stack()), [1]) + self.assertAllEqual(self.evaluate(l2.stack()), [1, 2]) def test_append_python(self): l = [] @@ -131,10 +134,10 @@ class ListTest(test.TestCase): with self.cached_session() as sess: l, x = data_structures.list_pop(l, None, opts) - self.assertAllEqual(sess.run(x), [3, 4]) + self.assertAllEqual(self.evaluate(x), [3, 4]) t = list_ops.tensor_list_stack(l, element_dtype=initial_list.dtype) - self.assertAllEqual(sess.run(t), [[1, 2]]) + self.assertAllEqual(self.evaluate(t), [[1, 2]]) def test_pop_python(self): l = [1, 2, 3] @@ -152,12 +155,12 @@ class ListTest(test.TestCase): with self.cached_session() as sess: t = data_structures.list_stack(l, opts) - self.assertAllEqual(sess.run(t), sess.run(initial_list)) + self.assertAllEqual(self.evaluate(t), self.evaluate(initial_list)) + @test_util.run_deprecated_v1 def test_stack_tensor_list_empty(self): l = list_ops.empty_tensor_list( - element_shape=-1, - element_dtype=dtypes.variant) + element_shape=None, element_dtype=dtypes.variant) opts = data_structures.ListStackOpts( element_dtype=dtypes.int32, original_call=None) diff --git a/tensorflow/python/autograph/operators/exceptions_test.py b/tensorflow/python/autograph/operators/exceptions_test.py index 186535d05b..21ba76bb95 100644 --- a/tensorflow/python/autograph/operators/exceptions_test.py +++ b/tensorflow/python/autograph/operators/exceptions_test.py @@ -21,6 +21,7 @@ from __future__ import print_function from tensorflow.python.autograph.operators import exceptions from tensorflow.python.framework import constant_op from tensorflow.python.framework import errors_impl +from tensorflow.python.framework import test_util from tensorflow.python.platform import test @@ -30,8 +31,9 @@ class ExceptionsTest(test.TestCase): with self.cached_session() as sess: t = exceptions.assert_stmt( constant_op.constant(True), lambda: constant_op.constant('ignored')) - sess.run(t) + self.evaluate(t) + @test_util.run_deprecated_v1 def test_assert_tf_triggered(self): with self.cached_session() as sess: t = exceptions.assert_stmt( @@ -40,8 +42,9 @@ class ExceptionsTest(test.TestCase): with self.assertRaisesRegexp(errors_impl.InvalidArgumentError, 'test message'): - sess.run(t) + self.evaluate(t) + @test_util.run_deprecated_v1 def test_assert_tf_multiple_printed_values(self): two_tensors = [ constant_op.constant('test message'), @@ -53,7 +56,7 @@ class ExceptionsTest(test.TestCase): with self.assertRaisesRegexp(errors_impl.InvalidArgumentError, 'test message.*another message'): - sess.run(t) + self.evaluate(t) def test_assert_python_untriggered(self): side_effect_trace = [] diff --git a/tensorflow/python/autograph/operators/logical_test.py b/tensorflow/python/autograph/operators/logical_test.py index d6649f7b2b..e22f39932d 100644 --- a/tensorflow/python/autograph/operators/logical_test.py +++ b/tensorflow/python/autograph/operators/logical_test.py @@ -20,6 +20,7 @@ from __future__ import print_function from tensorflow.python.autograph.operators import logical from tensorflow.python.framework import constant_op +from tensorflow.python.framework import test_util from tensorflow.python.platform import test @@ -42,14 +43,15 @@ class LogicalOperatorsTest(test.TestCase): self.assertFalse(logical.and_(lambda: False, lambda: True)) self.assertFalse(logical.and_(lambda: False, self.assertNotCalled)) + @test_util.run_deprecated_v1 def test_and_tf(self): with self.cached_session() as sess: t = logical.and_(self._tf_true, self._tf_true) - self.assertEqual(sess.run(t), True) + self.assertEqual(self.evaluate(t), True) t = logical.and_(self._tf_true, lambda: True) - self.assertEqual(sess.run(t), True) + self.assertEqual(self.evaluate(t), True) t = logical.and_(self._tf_false, lambda: True) - self.assertEqual(sess.run(t), False) + self.assertEqual(self.evaluate(t), False) # TODO(mdan): Add a test for ops with side effects. def test_or_python(self): @@ -60,14 +62,15 @@ class LogicalOperatorsTest(test.TestCase): self.assertTrue(logical.or_(lambda: False, lambda: True)) self.assertTrue(logical.or_(lambda: True, self.assertNotCalled)) + @test_util.run_deprecated_v1 def test_or_tf(self): with self.cached_session() as sess: t = logical.or_(self._tf_false, self._tf_true) - self.assertEqual(sess.run(t), True) + self.assertEqual(self.evaluate(t), True) t = logical.or_(self._tf_false, lambda: True) - self.assertEqual(sess.run(t), True) + self.assertEqual(self.evaluate(t), True) t = logical.or_(self._tf_true, lambda: True) - self.assertEqual(sess.run(t), True) + self.assertEqual(self.evaluate(t), True) # TODO(mdan): Add a test for ops with side effects. def test_not_python(self): @@ -78,7 +81,7 @@ class LogicalOperatorsTest(test.TestCase): def test_not_tf(self): with self.cached_session() as sess: t = logical.not_(self._tf_false()) - self.assertEqual(sess.run(t), True) + self.assertEqual(self.evaluate(t), True) if __name__ == '__main__': diff --git a/tensorflow/python/autograph/operators/py_builtins_test.py b/tensorflow/python/autograph/operators/py_builtins_test.py index 443e30a475..c856e39d14 100644 --- a/tensorflow/python/autograph/operators/py_builtins_test.py +++ b/tensorflow/python/autograph/operators/py_builtins_test.py @@ -27,6 +27,7 @@ from tensorflow.python.autograph.operators import py_builtins from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors_impl +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import tensor_array_ops from tensorflow.python.platform import test @@ -38,29 +39,29 @@ class PyBuiltinsTest(test.TestCase): self.assertEqual(py_builtins.abs_(-1), 1) with self.cached_session() as sess: t = py_builtins.abs_(constant_op.constant(-1)) - self.assertEqual(sess.run(t), 1) + self.assertEqual(self.evaluate(t), 1) t = py_builtins.abs_(constant_op.constant([-1, 2, -3])) - self.assertAllEqual(sess.run(t), [1, 2, 3]) + self.assertAllEqual(self.evaluate(t), [1, 2, 3]) def test_float(self): self.assertEqual(py_builtins.float_(10), 10.0) self.assertEqual(py_builtins.float_('10.0'), 10.0) with self.cached_session() as sess: t = py_builtins.float_(constant_op.constant(1, dtype=dtypes.int64)) - self.assertEqual(sess.run(t), 1.0) + self.assertEqual(self.evaluate(t), 1.0) st = py_builtins.float_(constant_op.constant('1.0')) - self.assertEqual(sess.run(st), 1.0) + self.assertEqual(self.evaluate(st), 1.0) def test_int(self): self.assertEqual(py_builtins.int_(10.0), 10) self.assertEqual(py_builtins.int_('11', 2), 3) with self.cached_session() as sess: t = py_builtins.int_(constant_op.constant(1, dtype=dtypes.float64)) - self.assertEqual(sess.run(t), 1) + self.assertEqual(self.evaluate(t), 1) st = py_builtins.int_(constant_op.constant('1')) - self.assertEqual(sess.run(st), 1) + self.assertEqual(self.evaluate(st), 1) st = py_builtins.int_(constant_op.constant('1'), 10) - self.assertEqual(sess.run(st), 1) + self.assertEqual(self.evaluate(st), 1) def test_int_unsupported_base(self): t = constant_op.constant(1, dtype=dtypes.float64) @@ -73,14 +74,15 @@ class PyBuiltinsTest(test.TestCase): t = py_builtins.len_(constant_op.constant([[1], [2], [3]])) self.assertEqual(t, 3) ta = py_builtins.len_(tensor_array_ops.TensorArray(dtypes.int32, size=5)) - self.assertEqual(sess.run(ta), 5) + self.assertEqual(self.evaluate(ta), 5) tl = py_builtins.len_(data_structures.tf_tensor_list_new([3, 4, 5])) - self.assertEqual(sess.run(tl), 3) + self.assertEqual(self.evaluate(tl), 3) def test_len_scalar(self): with self.assertRaises(ValueError): py_builtins.len_(constant_op.constant(1)) + @test_util.run_deprecated_v1 def test_len_dynamic_shape(self): with self.cached_session() as sess: p = array_ops.placeholder(dtype=dtypes.int32, shape=None) @@ -91,6 +93,7 @@ class PyBuiltinsTest(test.TestCase): t = py_builtins.len_(p) sess.run(t, {p: 1}) + @test_util.run_deprecated_v1 def test_print_tensors(self): try: out_capturer = six.StringIO() @@ -101,6 +104,7 @@ class PyBuiltinsTest(test.TestCase): finally: sys.stdout = sys.__stdout__ + @test_util.run_deprecated_v1 def test_print_complex(self): try: out_capturer = six.StringIO() @@ -120,18 +124,18 @@ class PyBuiltinsTest(test.TestCase): def test_range_tensor(self): with self.cached_session() as sess: r = py_builtins.range_(constant_op.constant(3)) - self.assertAllEqual(sess.run(r), [0, 1, 2]) + self.assertAllEqual(self.evaluate(r), [0, 1, 2]) r = py_builtins.range_(1, constant_op.constant(3)) - self.assertAllEqual(sess.run(r), [1, 2]) + self.assertAllEqual(self.evaluate(r), [1, 2]) r = py_builtins.range_(2, 0, constant_op.constant(-1)) - self.assertAllEqual(sess.run(r), [2, 1]) + self.assertAllEqual(self.evaluate(r), [2, 1]) def test_range_tensor_empty_range(self): with self.session() as sess: r = py_builtins.range_(constant_op.constant(-3)) - self.assertAllEqual(sess.run(r), []) + self.assertAllEqual(self.evaluate(r), []) r = py_builtins.range_(5, constant_op.constant(2)) - self.assertAllEqual(sess.run(r), []) + self.assertAllEqual(self.evaluate(r), []) if __name__ == '__main__': diff --git a/tensorflow/python/autograph/operators/slices_test.py b/tensorflow/python/autograph/operators/slices_test.py index 9e4865b3c6..d444054fd7 100644 --- a/tensorflow/python/autograph/operators/slices_test.py +++ b/tensorflow/python/autograph/operators/slices_test.py @@ -34,7 +34,7 @@ class SlicesTest(test.TestCase): with self.cached_session() as sess: t = list_ops.tensor_list_stack(l, element_dtype=initial_list.dtype) - self.assertAllEqual(sess.run(t), [[5, 6], [3, 4]]) + self.assertAllEqual(self.evaluate(t), [[5, 6], [3, 4]]) def test_get_item_tensor_list(self): initial_list = constant_op.constant([[1, 2], [3, 4]]) @@ -44,7 +44,7 @@ class SlicesTest(test.TestCase): l, 1, slices.GetItemOpts(element_dtype=initial_list.dtype)) with self.cached_session() as sess: - self.assertAllEqual(sess.run(t), [3, 4]) + self.assertAllEqual(self.evaluate(t), [3, 4]) def test_get_item_tensor_string(self): initial_str = constant_op.constant('abcd') @@ -52,14 +52,14 @@ class SlicesTest(test.TestCase): slices.GetItemOpts(element_dtype=initial_str.dtype)) with self.cached_session() as sess: - self.assertEqual(sess.run(t), b'b') + self.assertEqual(self.evaluate(t), b'b') initial_list_str = constant_op.constant(['abcd', 'bcde']) t = slices.get_item(initial_list_str, 1, slices.GetItemOpts(element_dtype=initial_str.dtype)) with self.cached_session() as sess: - self.assertEqual(sess.run(t), b'bcde') + self.assertEqual(self.evaluate(t), b'bcde') if __name__ == '__main__': diff --git a/tensorflow/python/autograph/pyct/BUILD b/tensorflow/python/autograph/pyct/BUILD index ddadc6b96e..ba8ec27139 100644 --- a/tensorflow/python/autograph/pyct/BUILD +++ b/tensorflow/python/autograph/pyct/BUILD @@ -80,7 +80,6 @@ py_test( name = "compiler_test", srcs = ["compiler_test.py"], srcs_version = "PY2AND3", - tags = ["no_windows"], deps = [ ":pyct", "//tensorflow/python:client_testlib", @@ -154,7 +153,6 @@ py_test( name = "transformer_test", srcs = ["transformer_test.py"], srcs_version = "PY2AND3", - tags = ["no_windows"], deps = [ ":pyct", "//tensorflow/python:client_testlib", diff --git a/tensorflow/python/autograph/pyct/inspect_utils.py b/tensorflow/python/autograph/pyct/inspect_utils.py index 4d56b93671..7c819f364f 100644 --- a/tensorflow/python/autograph/pyct/inspect_utils.py +++ b/tensorflow/python/autograph/pyct/inspect_utils.py @@ -46,6 +46,28 @@ if six.PY2: SPECIAL_BUILTINS['xrange'] = xrange +def islambda(f): + if not tf_inspect.isfunction(f): + return False + if not hasattr(f, '__name__'): + return False + return f.__name__ == '' + + +def isnamedtuple(f): + """Returns True if the argument is a namedtuple-like.""" + if not (tf_inspect.isclass(f) and issubclass(f, tuple)): + return False + if not hasattr(f, '_fields'): + return False + fields = getattr(f, '_fields') + if not isinstance(fields, tuple): + return False + if not all(isinstance(f, str) for f in fields): + return False + return True + + def isbuiltin(f): """Returns True if the argument is a built-in function.""" if f in SPECIAL_BUILTINS.values(): diff --git a/tensorflow/python/autograph/pyct/inspect_utils_test.py b/tensorflow/python/autograph/pyct/inspect_utils_test.py index 622e3bafc0..a2c39056d1 100644 --- a/tensorflow/python/autograph/pyct/inspect_utils_test.py +++ b/tensorflow/python/autograph/pyct/inspect_utils_test.py @@ -18,7 +18,8 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -from functools import wraps +import collections +import functools import imp import types import weakref @@ -46,7 +47,7 @@ def wrapping_decorator(): def replacement(*_): return None - @wraps(f) + @functools.wraps(f) def wrapper(*args, **kwargs): return replacement(*args, **kwargs) return wrapper @@ -95,6 +96,38 @@ def free_factory(): class InspectUtilsTest(test.TestCase): + def test_islambda(self): + def test_fn(): + pass + + self.assertTrue(inspect_utils.islambda(lambda x: x)) + self.assertFalse(inspect_utils.islambda(test_fn)) + + def test_isnamedtuple(self): + nt = collections.namedtuple('TestNamedTuple', ['a', 'b']) + + class NotANamedTuple(tuple): + pass + + self.assertTrue(inspect_utils.isnamedtuple(nt)) + self.assertFalse(inspect_utils.isnamedtuple(NotANamedTuple)) + + def test_isnamedtuple_confounder(self): + """This test highlights false positives when detecting named tuples.""" + + class NamedTupleLike(tuple): + _fields = ('a', 'b') + + self.assertTrue(inspect_utils.isnamedtuple(NamedTupleLike)) + + def test_isnamedtuple_subclass(self): + """This test highlights false positives when detecting named tuples.""" + + class NamedTupleSubclass(collections.namedtuple('Test', ['a', 'b'])): + pass + + self.assertTrue(inspect_utils.isnamedtuple(NamedTupleSubclass)) + def test_getnamespace_globals(self): ns = inspect_utils.getnamespace(factory) self.assertEqual(ns['free_function'], free_function) diff --git a/tensorflow/python/autograph/pyct/static_analysis/BUILD b/tensorflow/python/autograph/pyct/static_analysis/BUILD index 4a4ccdcbd1..5e260c5730 100644 --- a/tensorflow/python/autograph/pyct/static_analysis/BUILD +++ b/tensorflow/python/autograph/pyct/static_analysis/BUILD @@ -38,7 +38,6 @@ py_test( name = "activity_test", srcs = ["activity_test.py"], srcs_version = "PY2AND3", - tags = ["no_windows"], deps = [ ":static_analysis", "//tensorflow/python:client_testlib", @@ -51,7 +50,6 @@ py_test( name = "live_values_test", srcs = ["live_values_test.py"], srcs_version = "PY2AND3", - tags = ["no_windows"], deps = [ ":static_analysis", "//tensorflow/python:client_testlib", diff --git a/tensorflow/python/autograph/pyct/static_analysis/liveness.py b/tensorflow/python/autograph/pyct/static_analysis/liveness.py index 451398f1b7..f8b8d7fa77 100644 --- a/tensorflow/python/autograph/pyct/static_analysis/liveness.py +++ b/tensorflow/python/autograph/pyct/static_analysis/liveness.py @@ -161,6 +161,16 @@ class Annotator(transformer.Base): self.cross_function_analyzer = cross_function_analyzer self.current_analyzer = None + def visit(self, node): + node = super(Annotator, self).visit(node) + if (self.current_analyzer is not None and + isinstance(node, gast.stmt) and + node in self.current_analyzer.graph.index): + cfg_node = self.current_analyzer.graph.index[node] + anno.setanno(node, anno.Static.LIVE_VARS_IN, + frozenset(self.current_analyzer.in_[cfg_node])) + return node + def visit_FunctionDef(self, node): parent_analyzer = self.current_analyzer self.current_analyzer = self.cross_function_analyzer.analyzers[node] @@ -198,6 +208,10 @@ class Annotator(transformer.Base): node = self._block_statement_live_out(node) return self._block_statement_live_in(node, node.test) + def visit_With(self, node): + node = self.generic_visit(node) + return self._block_statement_live_in(node, node.items[0]) + def visit_Expr(self, node): node = self.generic_visit(node) cfg_node = self.current_analyzer.graph.index[node] diff --git a/tensorflow/python/autograph/utils/misc_test.py b/tensorflow/python/autograph/utils/misc_test.py index 8d2b0d6e13..c78df48d62 100644 --- a/tensorflow/python/autograph/utils/misc_test.py +++ b/tensorflow/python/autograph/utils/misc_test.py @@ -19,6 +19,7 @@ from __future__ import division from __future__ import print_function from tensorflow.python.autograph.utils.misc import alias_tensors +from tensorflow.python.framework import test_util from tensorflow.python.framework.constant_op import constant from tensorflow.python.ops.variables import Variable from tensorflow.python.platform import test @@ -26,14 +27,16 @@ from tensorflow.python.platform import test class MiscTest(test.TestCase): + @test_util.run_deprecated_v1 def test_alias_single_tensor(self): a = constant(1) new_a = alias_tensors(a) self.assertFalse(new_a is a) with self.cached_session() as sess: - self.assertEqual(1, sess.run(new_a)) + self.assertEqual(1, self.evaluate(new_a)) + @test_util.run_deprecated_v1 def test_alias_tensors(self): a = constant(1) v = Variable(2) @@ -47,7 +50,7 @@ class MiscTest(test.TestCase): self.assertTrue(new_s is s) self.assertTrue(new_l is l) with self.cached_session() as sess: - self.assertEqual(1, sess.run(new_a)) + self.assertEqual(1, self.evaluate(new_a)) if __name__ == '__main__': diff --git a/tensorflow/python/autograph/utils/py_func_test.py b/tensorflow/python/autograph/utils/py_func_test.py index 1c220d9492..28cefd8c3e 100644 --- a/tensorflow/python/autograph/utils/py_func_test.py +++ b/tensorflow/python/autograph/utils/py_func_test.py @@ -34,13 +34,13 @@ class PyFuncTest(test.TestCase): with self.cached_session() as sess: result = py_func.wrap_py_func(test_fn, dtypes.int64, (1, constant_op.constant(1), 1)) - self.assertEqual(3, sess.run(result)) + self.assertEqual(3, self.evaluate(result)) result = py_func.wrap_py_func(test_fn, dtypes.int64, (1, 1, 1)) - self.assertEqual(3, sess.run(result)) + self.assertEqual(3, self.evaluate(result)) result = py_func.wrap_py_func( test_fn, dtypes.int64, (constant_op.constant(1), 1, constant_op.constant(1))) - self.assertEqual(3, sess.run(result)) + self.assertEqual(3, self.evaluate(result)) def test_wrap_py_func_complex_args(self): @@ -54,10 +54,10 @@ class PyFuncTest(test.TestCase): with self.cached_session() as sess: result = py_func.wrap_py_func(test_fn, dtypes.int64, (7, TestClass())) - self.assertEqual(35, sess.run(result)) + self.assertEqual(35, self.evaluate(result)) result = py_func.wrap_py_func(test_fn, dtypes.int64, (constant_op.constant(7), TestClass())) - self.assertEqual(35, sess.run(result)) + self.assertEqual(35, self.evaluate(result)) def test_wrap_py_func_kwargs(self): @@ -74,13 +74,13 @@ class PyFuncTest(test.TestCase): 'c': 11, 'd': TestClass(13) }) - self.assertEqual(178, sess.run(result)) + self.assertEqual(178, self.evaluate(result)) result = py_func.wrap_py_func(test_fn, dtypes.int64, (constant_op.constant(7), TestClass(5)), { 'c': constant_op.constant(11), 'd': TestClass(13) }) - self.assertEqual(178, sess.run(result)) + self.assertEqual(178, self.evaluate(result)) def test_wrap_py_func_dummy_return(self): @@ -91,11 +91,11 @@ class PyFuncTest(test.TestCase): with self.cached_session() as sess: result = py_func.wrap_py_func(test_fn, None, (5,), use_dummy_return=True) - self.assertEqual(1, sess.run(result)) + self.assertEqual(1, self.evaluate(result)) self.assertEqual([1], side_counter) result = py_func.wrap_py_func( test_fn, None, (constant_op.constant(5),), use_dummy_return=True) - self.assertEqual(1, sess.run(result)) + self.assertEqual(1, self.evaluate(result)) self.assertEqual([2], side_counter) diff --git a/tensorflow/python/autograph/utils/tensor_list_test.py b/tensorflow/python/autograph/utils/tensor_list_test.py index 697c166eb1..bbbc3bf691 100644 --- a/tensorflow/python/autograph/utils/tensor_list_test.py +++ b/tensorflow/python/autograph/utils/tensor_list_test.py @@ -19,10 +19,10 @@ from __future__ import division from __future__ import print_function from tensorflow.python.autograph.utils import tensor_list as tl -from tensorflow.python.client.session import Session from tensorflow.python.eager import context from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.framework.constant_op import constant from tensorflow.python.ops import list_ops from tensorflow.python.ops import tensor_array_ops @@ -34,6 +34,7 @@ class TensorListTest(test.TestCase): def _shape(self, shape_tuple): return constant(shape_tuple, dtypes.int32) + @test_util.run_v1_only("b/117943489") def test_dynamic_list_append(self): l = [] l = tl.dynamic_list_append(l, 1) @@ -42,19 +43,16 @@ class TensorListTest(test.TestCase): l = list_ops.empty_tensor_list(self._shape(()), dtypes.int32) l = tl.dynamic_list_append(l, 1) s = list_ops.tensor_list_stack(l, element_dtype=dtypes.int32) - with self.cached_session() as sess: - self.assertAllEqual(sess.run(s), [1]) + self.assertAllEqual(s, [1]) l = tensor_array_ops.TensorArray(dtypes.int32, size=0, dynamic_size=True) l = tl.dynamic_list_append(l, 1) s = l.stack() - with self.cached_session() as sess: - self.assertAllEqual(sess.run(s), [1]) + self.assertAllEqual(s, [1]) l = tl.TensorList(self._shape(()), dtypes.int32) l = tl.dynamic_list_append(l, 1) - with self.cached_session() as sess: - self.assertAllEqual(sess.run(l[0]), 1) + self.assertAllEqual(l[0], 1) def test_list_append_python(self): with context.eager_mode(): @@ -80,6 +78,7 @@ class TensorListTest(test.TestCase): l[0] = ops.convert_to_tensor(b) self.assertEqual(l[0].numpy(), b.numpy()) + @test_util.run_deprecated_v1 def test_list_append_tf(self): a = constant(3.0) l = tl.TensorList(a.shape, a.dtype) @@ -91,13 +90,12 @@ class TensorListTest(test.TestCase): c3 = l.count() a2 = l.pop() c4 = l.count() - with Session() as sess: - c1, c2, c3, c4, a, a2 = sess.run([c1, c2, c3, c4, a, a2]) - self.assertEqual(c1, 1) - self.assertEqual(c2, 2) - self.assertEqual(c3, 1) - self.assertEqual(c4, 0) - self.assertEqual(a, a2) + c1, c2, c3, c4, a, a2 = self.evaluate([c1, c2, c3, c4, a, a2]) + self.assertEqual(c1, 1) + self.assertEqual(c2, 2) + self.assertEqual(c3, 1) + self.assertEqual(c4, 0) + self.assertEqual(a, a2) def test_list_index_tf(self): a = constant(3.0) @@ -107,10 +105,9 @@ class TensorListTest(test.TestCase): l0 = l[0] l[0] = b l1 = l[0] - with self.cached_session() as sess: - l0, l1, a, b = sess.run([l0, l1, a, b]) - self.assertEqual(l0, a) - self.assertEqual(l1, b) + l0, l1, a, b = self.evaluate([l0, l1, a, b]) + self.assertEqual(l0, a) + self.assertEqual(l1, b) if __name__ == '__main__': diff --git a/tensorflow/python/autograph/utils/type_check.py b/tensorflow/python/autograph/utils/type_check.py index 8748abc47b..ccef7dee03 100644 --- a/tensorflow/python/autograph/utils/type_check.py +++ b/tensorflow/python/autograph/utils/type_check.py @@ -30,4 +30,4 @@ def is_tensor(*args): Returns: True if any *args are TensorFlow types, False if none are. """ - return any([tensor_util.is_tensor(a) for a in args]) + return any(tensor_util.is_tensor(a) for a in args) diff --git a/tensorflow/python/autograph/utils/type_check_test.py b/tensorflow/python/autograph/utils/type_check_test.py index b3d1304e16..2521dc9f92 100644 --- a/tensorflow/python/autograph/utils/type_check_test.py +++ b/tensorflow/python/autograph/utils/type_check_test.py @@ -28,6 +28,7 @@ from tensorflow.python.platform import test class TypeCheckTest(test.TestCase): + @test_util.run_deprecated_v1 def test_checks(self): self.assertTrue(type_check.is_tensor(constant_op.constant([1, 2, 3]))) self.assertTrue( diff --git a/tensorflow/python/client/device_lib.i b/tensorflow/python/client/device_lib.i index 944e855cee..3e579152d5 100644 --- a/tensorflow/python/client/device_lib.i +++ b/tensorflow/python/client/device_lib.i @@ -48,17 +48,14 @@ static std::vector ListDevicesWithSessionConfig( std::vector output; SessionOptions options; options.config = config; - std::vector devices; + std::vector> devices; Status status = DeviceFactory::AddDevices( options, "" /* name_prefix */, &devices); if (!status.ok()) { Set_TF_Status_from_Status(out_status, status); } - std::vector> device_holder(devices.begin(), - devices.end()); - - for (const Device* device : devices) { + for (const std::unique_ptr& device : devices) { const DeviceAttributes& attr = device->attributes(); string attr_serialized; if (!attr.SerializeToString(&attr_serialized)) { diff --git a/tensorflow/python/client/session.py b/tensorflow/python/client/session.py index 06c66dda9f..87a200ed33 100644 --- a/tensorflow/python/client/session.py +++ b/tensorflow/python/client/session.py @@ -828,7 +828,7 @@ class BaseSession(SessionInterface): nested list, tuple, namedtuple, dict, or OrderedDict containing graph elements at its leaves. A graph element can be one of the following types: - * An `tf.Operation`. + * A `tf.Operation`. The corresponding fetched value will be `None`. * A `tf.Tensor`. The corresponding fetched value will be a numpy ndarray containing the @@ -1097,7 +1097,7 @@ class BaseSession(SessionInterface): if isinstance(subfeed_val, ops.Tensor): raise TypeError('The value of a feed cannot be a tf.Tensor object. ' 'Acceptable feed values include Python scalars, ' - 'strings, lists, numpy ndarrays, or TensorHandles.' + 'strings, lists, numpy ndarrays, or TensorHandles. ' 'For reference, the tensor object was ' + str(feed_val) + ' which was passed to the ' 'feed with key ' + str(feed) + '.') diff --git a/tensorflow/python/client/session_clusterspec_prop_test.py b/tensorflow/python/client/session_clusterspec_prop_test.py index df020f88a8..224f880ed1 100644 --- a/tensorflow/python/client/session_clusterspec_prop_test.py +++ b/tensorflow/python/client/session_clusterspec_prop_test.py @@ -62,7 +62,7 @@ class SessionClusterSpecPropagationTest(test_util.TensorFlowTestCase): const = constant_op.constant(17) sess = session.Session(server1.target, config=config) - output = sess.run(const) + output = self.evaluate(const) self.assertEqual(17, output) def testClusterSpecPropagationWorker2Placement(self): @@ -106,7 +106,7 @@ class SessionClusterSpecPropagationTest(test_util.TensorFlowTestCase): with ops.Graph().as_default() as g, ops.device('/job:worker/task:0'): const = constant_op.constant(17) sess = session.Session(server1.target, config=config, graph=g) - output = sess.run(const) + output = self.evaluate(const) self.assertEqual(17, output) def testCanonicalDeviceNames(self): @@ -208,7 +208,7 @@ class SessionClusterSpecPropagationTest(test_util.TensorFlowTestCase): with ops.device('/job:worker/task:0/cpu:0'): sum3 = sum1 + sum2 sess = session.Session(server1.target, config=config, graph=g) - output = sess.run(sum3) + output = self.evaluate(sum3) self.assertEqual(40, output) def testLegacyDeviceNames(self): diff --git a/tensorflow/python/client/session_partial_run_test.py b/tensorflow/python/client/session_partial_run_test.py index 92ca47efa9..a97930635a 100644 --- a/tensorflow/python/client/session_partial_run_test.py +++ b/tensorflow/python/client/session_partial_run_test.py @@ -117,7 +117,7 @@ class PartialRunTest(test_util.TensorFlowTestCase): a = constant_op.constant(2.0, dtypes.float32) b = a * 2 c = b * 3 - r1 = sess.run([b, c]) + r1 = self.evaluate([b, c]) h = sess.partial_run_setup([b, c], []) r2 = sess.partial_run(h, [b, c]) self.assertEqual(r1, r2) @@ -188,6 +188,7 @@ class PartialRunTest(test_util.TensorFlowTestCase): r = sess.partial_run(h, [b], {}) self.assertEqual([6.0], r) + @test_util.run_deprecated_v1 def testInvalidPartialRunSetup(self): sess = session.Session() x = array_ops.placeholder(dtypes.float32, shape=[]) @@ -196,6 +197,7 @@ class PartialRunTest(test_util.TensorFlowTestCase): 'specify at least one target to fetch or execute.'): sess.partial_run_setup(fetches=[], feeds=[x]) + @test_util.run_deprecated_v1 def testPartialRunSetupNoFeedsPassed(self): sess = session.Session() r1 = constant_op.constant([6.0]) @@ -204,80 +206,102 @@ class PartialRunTest(test_util.TensorFlowTestCase): result1 = sess.partial_run(h, r1) self.assertEqual([6.0], result1) + @test_util.run_deprecated_v1 def testPartialRunDirect(self): self.RunTestPartialRun(session.Session()) + @test_util.run_deprecated_v1 def testPartialRunIncompleteDirect(self): self.RunTestPartialRunIncomplete(session.Session()) + @test_util.run_deprecated_v1 def testConcurrentPartialRunDirect(self): self.RunTestConcurrentPartialRun(session.Session()) + @test_util.run_deprecated_v1 def testManyPartialRunDirect(self): self.RunTestManyPartialRun(session.Session()) + @test_util.run_deprecated_v1 def testRunAndPartialRunDirect(self): self.RunTestRunAndPartialRun(session.Session()) + @test_util.run_deprecated_v1 def testPartialRunMissingPlaceholderFeedExceptionDirect(self): self.RunTestPartialRunMissingPlaceholderFeedException(session.Session()) + @test_util.run_deprecated_v1 def testPartialRunUnspecifiedFeedDirect(self): self.RunTestPartialRunUnspecifiedFeed(session.Session()) + @test_util.run_deprecated_v1 def testPartialRunUnspecifiedFetchDirect(self): self.RunTestPartialRunUnspecifiedFetch(session.Session()) + @test_util.run_deprecated_v1 def testPartialRunAlreadyFedDirect(self): self.RunTestPartialRunAlreadyFed(session.Session()) + @test_util.run_deprecated_v1 def testPartialRunAlreadyFetchedDirect(self): self.RunTestPartialRunAlreadyFetched(session.Session()) + @test_util.run_deprecated_v1 def testPartialRunEmptyFetchesDirect(self): self.RunTestPartialRunEmptyFetches(session.Session()) + @test_util.run_deprecated_v1 def testPartialRunDist(self): server = server_lib.Server.create_local_server() self.RunTestPartialRun(session.Session(server.target)) + @test_util.run_deprecated_v1 def testPartialRunIncompleteDist(self): server = server_lib.Server.create_local_server() self.RunTestPartialRunIncomplete(session.Session(server.target)) + @test_util.run_deprecated_v1 def testConcurrentPartialRunDist(self): server = server_lib.Server.create_local_server() self.RunTestConcurrentPartialRun(session.Session(server.target)) + @test_util.run_deprecated_v1 def testManyPartialRunDist(self): server = server_lib.Server.create_local_server() self.RunTestManyPartialRun(session.Session(server.target)) + @test_util.run_deprecated_v1 def testRunAndPartialRunDist(self): server = server_lib.Server.create_local_server() self.RunTestRunAndPartialRun(session.Session(server.target)) + @test_util.run_deprecated_v1 def testPartialRunMissingPlaceholderFeedExceptionDist(self): server = server_lib.Server.create_local_server() self.RunTestPartialRunMissingPlaceholderFeedException( session.Session(server.target)) + @test_util.run_deprecated_v1 def testPartialRunUnspecifiedFeedDist(self): server = server_lib.Server.create_local_server() self.RunTestPartialRunUnspecifiedFeed(session.Session(server.target)) + @test_util.run_deprecated_v1 def testPartialRunUnspecifiedFetchDist(self): server = server_lib.Server.create_local_server() self.RunTestPartialRunUnspecifiedFetch(session.Session(server.target)) + @test_util.run_deprecated_v1 def testPartialRunAlreadyFedDist(self): server = server_lib.Server.create_local_server() self.RunTestPartialRunAlreadyFed(session.Session(server.target)) + @test_util.run_deprecated_v1 def testPartialRunAlreadyFetchedDist(self): server = server_lib.Server.create_local_server() self.RunTestPartialRunAlreadyFetched(session.Session(server.target)) + @test_util.run_deprecated_v1 def testPartialRunEmptyFetchesDist(self): server = server_lib.Server.create_local_server() self.RunTestPartialRunEmptyFetches(session.Session(server.target)) diff --git a/tensorflow/python/client/timeline_test.py b/tensorflow/python/client/timeline_test.py index dfd0147643..61c0da01b8 100644 --- a/tensorflow/python/client/timeline_test.py +++ b/tensorflow/python/client/timeline_test.py @@ -57,6 +57,7 @@ class TimelineTest(test.TestCase): ctf = tl.generate_chrome_trace_format() self._validateTrace(ctf) + @test_util.run_deprecated_v1 def testTimelineCpu(self): run_options = config_pb2.RunOptions( trace_level=config_pb2.RunOptions.FULL_TRACE) @@ -147,7 +148,7 @@ class TimelineTest(test.TestCase): num2 = variables.Variable(2.0, name='num2') with ops.device('/cpu:2'): result = num1 + num2 + num1 * num2 - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) sess.run(result, options=run_options, run_metadata=run_metadata) self.assertTrue(run_metadata.HasField('step_stats')) @@ -176,7 +177,7 @@ class TimelineTest(test.TestCase): num2 = variables.Variable(2.0, name='num2') with ops.device('/cpu:2'): result = num1 + num2 + num1 * num2 - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) sess.run(result, options=run_options, run_metadata=run_metadata) self.assertTrue(run_metadata.HasField('step_stats')) step_stats = run_metadata.step_stats diff --git a/tensorflow/python/client/virtual_gpu_test.py b/tensorflow/python/client/virtual_gpu_test.py index 5892e0fc84..e82ee0666c 100644 --- a/tensorflow/python/client/virtual_gpu_test.py +++ b/tensorflow/python/client/virtual_gpu_test.py @@ -216,7 +216,7 @@ class VirtualGpuTest(test_util.TensorFlowTestCase): for d in self._util.devices: with ops.device(d): var = variables.Variable(random_ops.random_uniform(mat_shape)) - sess.run(var.initializer) + self.evaluate(var.initializer) data.append(var) s = data[0] for i in range(1, len(data)): diff --git a/tensorflow/python/compat/BUILD b/tensorflow/python/compat/BUILD index e0a1c8e057..9f2ce8c676 100644 --- a/tensorflow/python/compat/BUILD +++ b/tensorflow/python/compat/BUILD @@ -9,7 +9,10 @@ py_library( srcs = ["compat.py"], srcs_version = "PY2AND3", visibility = ["//tensorflow:internal"], - deps = ["//tensorflow/python:util"], + deps = [ + "//tensorflow/python:tf2", + "//tensorflow/python:util", + ], ) tf_py_test( diff --git a/tensorflow/python/compat/compat.py b/tensorflow/python/compat/compat.py index 385fd431f4..0b6ff30488 100644 --- a/tensorflow/python/compat/compat.py +++ b/tensorflow/python/compat/compat.py @@ -23,10 +23,16 @@ from __future__ import division from __future__ import print_function import datetime + +from tensorflow.python import tf2 +from tensorflow.python.framework import ops +from tensorflow.python.framework import tensor_shape +from tensorflow.python.ops import variable_scope + from tensorflow.python.util import tf_contextlib from tensorflow.python.util.tf_export import tf_export -_FORWARD_COMPATIBILITY_HORIZON = datetime.date(2018, 11, 13) +_FORWARD_COMPATIBILITY_HORIZON = datetime.date(2018, 12, 4) @tf_export("compat.forward_compatible") @@ -132,3 +138,40 @@ def forward_compatibility_horizon(year, month, day): yield finally: _FORWARD_COMPATIBILITY_HORIZON = old_compat_date + + +@tf_export(v1=["enable_v2_behavior"]) +def enable_v2_behavior(): + """Enables TensorFlow 2.x behaviors. + + This function can be called at the beginning of the program (before `Tensors`, + `Graphs` or other structures have been created, and before devices have been + initialized. It switches all global behaviors that are different between + TensorFlow 1.x and 2.x to behave as intended for 2.x. + + This function is called in the main TensorFlow `__init__.py` file, user should + not need to call it, except during complex migrations. + """ + tf2.enable() # Switches TensorArrayV2 and control flow V2 + ops.enable_eager_execution() + tensor_shape.enable_v2_tensorshape() # Also switched by tf2 + variable_scope.enable_resource_variables() + + +@tf_export(v1=["disable_v2_behavior"]) +def disable_v2_behavior(): + """Enables TensorFlow 2.x behaviors. + + This function can be called at the beginning of the program (before `Tensors`, + `Graphs` or other structures have been created, and before devices have been + initialized. It switches all global behaviors that are different between + TensorFlow 1.x and 2.x to behave as intended for 1.x. + + User can call this function to disable 2.x behavior during complex migrations. + """ + tf2.disable() # Switches TensorArrayV2 and control flow V2 + ops.disable_eager_execution() + tensor_shape.disable_v2_tensorshape() # Also switched by tf2 + variable_scope.disable_resource_variables() + + diff --git a/tensorflow/python/data/__init__.py b/tensorflow/python/data/__init__.py index 7536ba668a..75ba88f303 100644 --- a/tensorflow/python/data/__init__.py +++ b/tensorflow/python/data/__init__.py @@ -24,6 +24,8 @@ from __future__ import print_function # pylint: disable=unused-import from tensorflow.python.data import experimental from tensorflow.python.data.ops.dataset_ops import Dataset +from tensorflow.python.data.ops.dataset_ops import make_initializable_iterator +from tensorflow.python.data.ops.dataset_ops import make_one_shot_iterator from tensorflow.python.data.ops.iterator_ops import Iterator from tensorflow.python.data.ops.readers import FixedLengthRecordDataset from tensorflow.python.data.ops.readers import TextLineDataset diff --git a/tensorflow/python/data/benchmarks/BUILD b/tensorflow/python/data/benchmarks/BUILD index fd723e0d71..5b0500eae1 100644 --- a/tensorflow/python/data/benchmarks/BUILD +++ b/tensorflow/python/data/benchmarks/BUILD @@ -6,6 +6,61 @@ exports_files(["LICENSE"]) load("//tensorflow:tensorflow.bzl", "py_test") +py_test( + name = "batch_benchmark", + srcs = ["batch_benchmark.py"], + srcs_version = "PY2AND3", + deps = [ + "//tensorflow/python:array_ops", + "//tensorflow/python:client_testlib", + "//tensorflow/python:dtypes", + "//tensorflow/python:session", + "//tensorflow/python:sparse_tensor", + "//tensorflow/python/data/ops:dataset_ops", + "//third_party/py/numpy", + ], +) + +py_test( + name = "filter_benchmark", + srcs = ["filter_benchmark.py"], + srcs_version = "PY2AND3", + deps = [ + "//tensorflow/python:array_ops", + "//tensorflow/python:client_testlib", + "//tensorflow/python:framework_ops", + "//tensorflow/python:session", + "//tensorflow/python/data/ops:dataset_ops", + "//third_party/py/numpy", + ], +) + +py_test( + name = "from_tensor_slices_benchmark", + srcs = ["from_tensor_slices_benchmark.py"], + srcs_version = "PY2AND3", + deps = [ + "//tensorflow/python:client_testlib", + "//tensorflow/python:errors", + "//tensorflow/python:session", + "//tensorflow/python/data/ops:dataset_ops", + "//third_party/py/numpy", + ], +) + +py_test( + name = "map_benchmark", + srcs = ["map_benchmark.py"], + srcs_version = "PY2AND3", + deps = [ + "//tensorflow/python:client_testlib", + "//tensorflow/python:framework_ops", + "//tensorflow/python:session", + "//tensorflow/python/data/ops:dataset_ops", + "//third_party/py/numpy", + ], +) + py_test( name = "range_benchmark", srcs = ["range_benchmark.py"], diff --git a/tensorflow/python/data/benchmarks/batch_benchmark.py b/tensorflow/python/data/benchmarks/batch_benchmark.py new file mode 100644 index 0000000000..e063849f70 --- /dev/null +++ b/tensorflow/python/data/benchmarks/batch_benchmark.py @@ -0,0 +1,85 @@ +# Copyright 2018 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. +# ============================================================================== +"""Benchmarks for `tf.data.Dataset.batch()`.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import time + +import numpy as np + +from tensorflow.python.client import session +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import sparse_tensor +from tensorflow.python.ops import array_ops +from tensorflow.python.platform import test + + +# TODO(b/119837791): Add eager benchmarks. +class BatchBenchmark(test.Benchmark): + """Benchmarks for `tf.data.Dataset.batch()`.""" + + def benchmarkBatchSparse(self): + non_zeros_per_row_values = [0, 1, 5, 10, 100] + batch_size_values = [1, 32, 64, 128, 1024] + + sparse_placeholder = array_ops.sparse_placeholder(dtype=dtypes.int64) + batch_size_placeholder = array_ops.placeholder(dtype=dtypes.int64, shape=[]) + + dataset = dataset_ops.Dataset.from_tensors(sparse_placeholder).repeat( + ).batch(batch_size_placeholder) + iterator = dataset_ops.make_initializable_iterator(dataset) + next_element = iterator.get_next() + + for non_zeros_per_row in non_zeros_per_row_values: + + sparse_value = sparse_tensor.SparseTensorValue( + indices=np.arange(non_zeros_per_row, dtype=np.int64)[:, np.newaxis], + values=np.arange(non_zeros_per_row, dtype=np.int64), + dense_shape=[1000]) + + for batch_size in batch_size_values: + + with session.Session() as sess: + sess.run(iterator.initializer, feed_dict={ + sparse_placeholder: sparse_value, + batch_size_placeholder: batch_size}) + # Run five steps to warm up the session caches before taking the + # first measurement. + for _ in range(5): + sess.run(next_element.indices.op) + deltas = [] + for _ in range(100): + start = time.time() + for _ in range(100): + sess.run(next_element.indices.op) + end = time.time() + deltas.append(end - start) + + median_wall_time = np.median(deltas) / 100.0 + + print("Batch sparse dataset non-zeros per row: %d batch_size: %d " + "wall time: %f" + % (non_zeros_per_row, batch_size, median_wall_time)) + self.report_benchmark( + iters=10000, wall_time=median_wall_time, + name="batch_sparse_dataset_nnz_%d_batch_size_%d" % ( + non_zeros_per_row, batch_size)) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/data/benchmarks/filter_benchmark.py b/tensorflow/python/data/benchmarks/filter_benchmark.py new file mode 100644 index 0000000000..a6d86fe221 --- /dev/null +++ b/tensorflow/python/data/benchmarks/filter_benchmark.py @@ -0,0 +1,69 @@ +# 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. +# ============================================================================== +"""Benchmarks for `tf.data.Dataset.filter()`.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import time + +import numpy as np + +from tensorflow.python.client import session +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.framework import ops +from tensorflow.python.ops import array_ops +from tensorflow.python.platform import test + + +# TODO(b/119837791): Add eager benchmarks. +class FilterBenchmark(test.Benchmark): + """Benchmarks for `tf.data.Dataset.filter()`.""" + + def _benchmark(self, predicate, name): + with ops.Graph().as_default(): + dataset = ( + dataset_ops.Dataset.from_tensors(True).repeat(None).filter(predicate)) + iterator = dataset_ops.make_one_shot_iterator(dataset) + next_element = iterator.get_next() + + with session.Session() as sess: + for _ in range(5): + sess.run(next_element.op) + deltas = [] + for _ in range(100): + start = time.time() + for _ in range(100): + sess.run(next_element.op) + end = time.time() + deltas.append(end - start) + + median_wall_time = np.median(deltas) / 100 + print("Filter dataset using %s. Median wall time: %f" % + (name, median_wall_time)) + self.report_benchmark( + iters=100, + wall_time=median_wall_time, + name=name) + + def benchmarkSimpleFunction(self): + self._benchmark(array_ops.identity, "simple_function") + + def benchmarkReturnComponentOptimization(self): + self._benchmark(lambda x: x, "return_component") + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/data/benchmarks/from_tensor_slices_benchmark.py b/tensorflow/python/data/benchmarks/from_tensor_slices_benchmark.py new file mode 100644 index 0000000000..d7f1a4e7af --- /dev/null +++ b/tensorflow/python/data/benchmarks/from_tensor_slices_benchmark.py @@ -0,0 +1,188 @@ +# 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. +# ============================================================================== +"""Benchmarks for `tf.data.Dataset.from_tensor_slices()`.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import time + +import numpy as np + +from tensorflow.python.client import session +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.framework import errors +from tensorflow.python.platform import test + + +# TODO(b/119837791): Add eager benchmarks. +class FromTensorSlicesBenchmark(test.Benchmark): + """Benchmarks for `tf.data.Dataset.from_tensor_slices()`.""" + + def benchmarkSliceRepeatBatch(self): + input_size = 10000 + batch_size = 100 + num_epochs = 100 + + input_data = np.random.randn(input_size) + + dataset = ( + dataset_ops.Dataset.from_tensor_slices(input_data) + .repeat(num_epochs + 1).batch(batch_size)) + iterator = dataset_ops.make_initializable_iterator(dataset) + next_element = iterator.get_next() + + with session.Session() as sess: + sess.run(iterator.initializer) + # Run one whole epoch to burn in the computation. + for _ in range(input_size // batch_size): + sess.run(next_element) + deltas = [] + try: + while True: + start = time.time() + sess.run(next_element) + deltas.append(time.time() - start) + except errors.OutOfRangeError: + pass + + median_wall_time = np.median(deltas) + print("Slice/repeat/batch with sess.run() input size: %d batch size: %d " + "Median wall time per element: %f" % (input_size, batch_size, + median_wall_time)) + self.report_benchmark( + iters=len(deltas), + wall_time=median_wall_time, + name="slice_repeat_batch_input_%d_batch_%d" % (input_size, batch_size)) + + def benchmarkSliceRepeatBatchCallable(self): + input_size = 10000 + batch_size = 100 + num_epochs = 100 + + input_data = np.random.randn(input_size) + + dataset = ( + dataset_ops.Dataset.from_tensor_slices(input_data) + .repeat(num_epochs + 1).batch(batch_size)) + iterator = dataset_ops.make_initializable_iterator(dataset) + next_element = iterator.get_next() + + with session.Session() as sess: + sess.run(iterator.initializer) + get_next_element = sess.make_callable(next_element) + # Run one whole epoch to burn in the computation. + for _ in range(input_size // batch_size): + get_next_element() + deltas = [] + try: + while True: + start = time.time() + get_next_element() + deltas.append(time.time() - start) + except errors.OutOfRangeError: + pass + + median_wall_time = np.median(deltas) + print( + "Slice/repeat/batch with callable input size: %d batch size: %d Median" + " wall time per element: %f" % (input_size, batch_size, + median_wall_time)) + self.report_benchmark( + iters=len(deltas), + wall_time=median_wall_time, + name="slice_repeat_batch_callable_input_%d_batch_%d" % + (input_size, batch_size)) + + def benchmarkReshapeSliceRepeatCallable(self): + input_size = 10000 + batch_size = 100 + num_epochs = 100 + + input_data = np.random.randn(input_size) + + dataset = ( + dataset_ops.Dataset.from_tensor_slices(input_data.reshape(100, 100)) + .repeat(num_epochs + 1)) + iterator = dataset_ops.make_initializable_iterator(dataset) + next_element = iterator.get_next() + + with session.Session() as sess: + sess.run(iterator.initializer) + get_next_element = sess.make_callable(next_element) + # Run one whole epoch to burn in the computation. + for _ in range(input_size // batch_size): + get_next_element() + deltas = [] + try: + while True: + start = time.time() + get_next_element() + deltas.append(time.time() - start) + except errors.OutOfRangeError: + pass + + median_wall_time = np.median(deltas) + print("Reshape/slice/repeat with callable input size: %d batch size: %d " + "Median wall time per element: %f" % (input_size, batch_size, + median_wall_time)) + self.report_benchmark( + iters=len(deltas), + wall_time=median_wall_time, + name="reshape_slice_repeat_callable_input_%d_batch_%d" % + (input_size, batch_size)) + + def benchmarkSliceBatchCacheRepeatCallable(self): + input_size = 10000 + batch_size = 100 + num_epochs = 100 + + input_data = np.random.randn(input_size) + + dataset = ( + dataset_ops.Dataset.from_tensor_slices(input_data).batch(batch_size) + .cache().repeat(num_epochs + 1)) + iterator = dataset_ops.make_initializable_iterator(dataset) + next_element = iterator.get_next() + + with session.Session() as sess: + sess.run(iterator.initializer) + get_next_element = sess.make_callable(next_element) + # Run one whole epoch to burn in the computation. + for _ in range(input_size // batch_size): + get_next_element() + deltas = [] + try: + while True: + start = time.time() + get_next_element() + deltas.append(time.time() - start) + except errors.OutOfRangeError: + pass + + median_wall_time = np.median(deltas) + print( + "Slice/batch/cache/repeat with callable input size: %d batch size: %d " + "Median wall time per element: %f" + % (input_size, batch_size, median_wall_time)) + self.report_benchmark( + iters=len(deltas), + wall_time=median_wall_time, + name="slice_batch_cache_repeat_callable_input_%d_batch_%d" % + (input_size, batch_size)) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/data/benchmarks/map_benchmark.py b/tensorflow/python/data/benchmarks/map_benchmark.py new file mode 100644 index 0000000000..65d945cdae --- /dev/null +++ b/tensorflow/python/data/benchmarks/map_benchmark.py @@ -0,0 +1,135 @@ +# 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. +# ============================================================================== +"""Bechmarks for `tf.data.Dataset.map()`.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import time + +import numpy as np + +from tensorflow.python.client import session +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.framework import ops +from tensorflow.python.platform import test + + +# TODO(b/119837791): Add eager benchmarks. +class MapBenchmark(test.Benchmark): + """Bechmarks for `tf.data.Dataset.map()`.""" + + def benchmarkChainOfMaps(self): + chain_lengths = [0, 1, 2, 5, 10, 20, 50] + for chain_length in chain_lengths: + for mode in ["general", "single-threaded", "short-circuit"]: + if mode == "general": + map_fn = lambda x: x + 1 + use_inter_op_parallelism = True + print_label = "" + benchmark_label = "" + if mode == "single-threaded": + map_fn = lambda x: x + 1 + use_inter_op_parallelism = False + print_label = " (single threaded mode)" + benchmark_label = "_single_threaded" + if mode == "short-circuit": + map_fn = lambda x: x + use_inter_op_parallelism = True # should not have any significance + print_label = " (short circuit mode)" + benchmark_label = "_short_circuit" + + with ops.Graph().as_default(): + dataset = dataset_ops.Dataset.from_tensors(0).repeat(None) + for _ in range(chain_length): + dataset = dataset_ops.MapDataset( + dataset, + map_fn, + use_inter_op_parallelism=use_inter_op_parallelism) + iterator = dataset_ops.make_one_shot_iterator(dataset) + next_element = iterator.get_next() + + with session.Session() as sess: + for _ in range(5): + sess.run(next_element.op) + deltas = [] + for _ in range(100): + start = time.time() + for _ in range(100): + sess.run(next_element.op) + end = time.time() + deltas.append(end - start) + + median_wall_time = np.median(deltas) / 100 + print("Map dataset chain length%s: %d Median wall time: %f" % + (print_label, chain_length, median_wall_time)) + self.report_benchmark( + iters=1000, + wall_time=median_wall_time, + name="map_dataset_chain_length_%d%s" % (chain_length, + benchmark_label)) + + def benchmarkMapFanOut(self): + fan_outs = [1, 2, 5, 10, 20, 50, 100] + for fan_out in fan_outs: + for mode in ["general", "single-threaded", "short-circuit"]: + if mode == "general": + map_fn = lambda *xs: [x + 1 for x in xs] + use_inter_op_parallelism = True + print_label = "" + benchmark_label = "" + if mode == "single-threaded": + map_fn = lambda *xs: [x + 1 for x in xs] + use_inter_op_parallelism = False + print_label = " (single threaded mode)" + benchmark_label = "_single_threaded" + if mode == "short-circuit": + map_fn = lambda *xs: xs + use_inter_op_parallelism = True # should not have any significance + print_label = " (short circuit mode)" + benchmark_label = "_short_circuit" + + with ops.Graph().as_default(): + dataset = dataset_ops.Dataset.from_tensors( + tuple(0 for _ in range(fan_out))).repeat(None) + dataset = dataset_ops.MapDataset( + dataset, + map_fn, + use_inter_op_parallelism=use_inter_op_parallelism) + iterator = dataset_ops.make_one_shot_iterator(dataset) + next_element = iterator.get_next() + + with session.Session() as sess: + for _ in range(5): + sess.run(next_element[0].op) + deltas = [] + for _ in range(100): + start = time.time() + for _ in range(100): + sess.run(next_element[0].op) + end = time.time() + deltas.append(end - start) + + median_wall_time = np.median(deltas) / 100 + print("Map dataset fan out%s: %d Median wall time: %f" % + (print_label, fan_out, median_wall_time)) + self.report_benchmark( + iters=1000, + wall_time=median_wall_time, + name="map_dataset_fan_out_%d%s" % (fan_out, benchmark_label)) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/data/benchmarks/range_benchmark.py b/tensorflow/python/data/benchmarks/range_benchmark.py index 25f63b79a2..a5020e2873 100644 --- a/tensorflow/python/data/benchmarks/range_benchmark.py +++ b/tensorflow/python/data/benchmarks/range_benchmark.py @@ -39,7 +39,7 @@ class RangeBenchmark(test.Benchmark): # costs). dataset = dataset_ops.Dataset.range(num_elements).skip( num_elements - 1).take(1).with_options(options) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) next_element = iterator.get_next() with session.Session() as sess: diff --git a/tensorflow/python/data/experimental/__init__.py b/tensorflow/python/data/experimental/__init__.py index 126c2be442..14dfec37cd 100644 --- a/tensorflow/python/data/experimental/__init__.py +++ b/tensorflow/python/data/experimental/__init__.py @@ -25,6 +25,7 @@ See [Importing Data](https://tensorflow.org/guide/datasets) for an overview. @@Counter @@CheckpointInputPipelineHook @@CsvDataset +@@OptimizationOptions @@Optional @@RandomDataset @@Reducer @@ -32,12 +33,15 @@ See [Importing Data](https://tensorflow.org/guide/datasets) for an overview. @@StatsAggregator @@StatsOptions @@TFRecordWriter +@@ThreadingOptions @@bucket_by_sequence_length +@@cardinality @@choose_from_datasets @@copy_to_device @@dense_to_sparse_batch @@enumerate_dataset +@@filter_for_shard @@get_next_as_optional @@get_single_element @@group_by_reducer @@ -59,6 +63,8 @@ See [Importing Data](https://tensorflow.org/guide/datasets) for an overview. @@unique @@AUTOTUNE +@@INFINITE_CARDINALITY +@@UNKNOWN_CARDINALITY """ from __future__ import absolute_import @@ -70,9 +76,13 @@ from __future__ import print_function from tensorflow.python.data.experimental.ops.batching import dense_to_sparse_batch from tensorflow.python.data.experimental.ops.batching import map_and_batch from tensorflow.python.data.experimental.ops.batching import unbatch +from tensorflow.python.data.experimental.ops.cardinality import cardinality +from tensorflow.python.data.experimental.ops.cardinality import INFINITE as INFINITE_CARDINALITY +from tensorflow.python.data.experimental.ops.cardinality import UNKNOWN as UNKNOWN_CARDINALITY from tensorflow.python.data.experimental.ops.counter import Counter from tensorflow.python.data.experimental.ops.enumerate_ops import enumerate_dataset from tensorflow.python.data.experimental.ops.error_ops import ignore_errors +from tensorflow.python.data.experimental.ops.filter_for_shard_ops import filter_for_shard from tensorflow.python.data.experimental.ops.get_single_element import get_single_element from tensorflow.python.data.experimental.ops.grouping import bucket_by_sequence_length from tensorflow.python.data.experimental.ops.grouping import group_by_reducer @@ -83,10 +93,8 @@ from tensorflow.python.data.experimental.ops.interleave_ops import parallel_inte from tensorflow.python.data.experimental.ops.interleave_ops import sample_from_datasets from tensorflow.python.data.experimental.ops.iterator_ops import CheckpointInputPipelineHook from tensorflow.python.data.experimental.ops.iterator_ops import make_saveable_from_iterator - -# Optimization constant that can be used to enable auto-tuning. from tensorflow.python.data.experimental.ops.optimization import AUTOTUNE - +from tensorflow.python.data.experimental.ops.optimization_options import OptimizationOptions from tensorflow.python.data.experimental.ops.parsing_ops import parse_example_dataset from tensorflow.python.data.experimental.ops.prefetching_ops import copy_to_device from tensorflow.python.data.experimental.ops.prefetching_ops import prefetch_to_device @@ -101,6 +109,7 @@ from tensorflow.python.data.experimental.ops.shuffle_ops import shuffle_and_repe from tensorflow.python.data.experimental.ops.stats_aggregator import StatsAggregator from tensorflow.python.data.experimental.ops.stats_ops import latency_stats from tensorflow.python.data.experimental.ops.stats_options import StatsOptions +from tensorflow.python.data.experimental.ops.threading_options import ThreadingOptions from tensorflow.python.data.experimental.ops.unique import unique from tensorflow.python.data.experimental.ops.writers import TFRecordWriter from tensorflow.python.data.ops.iterator_ops import get_next_as_optional diff --git a/tensorflow/python/data/experimental/benchmarks/BUILD b/tensorflow/python/data/experimental/benchmarks/BUILD index b89fbe7757..8175116c6e 100644 --- a/tensorflow/python/data/experimental/benchmarks/BUILD +++ b/tensorflow/python/data/experimental/benchmarks/BUILD @@ -7,36 +7,118 @@ exports_files(["LICENSE"]) load("//tensorflow:tensorflow.bzl", "cuda_py_test") load("//tensorflow:tensorflow.bzl", "py_test") +py_test( + name = "autotune_benchmark", + srcs = ["autotune_benchmark.py"], + srcs_version = "PY2AND3", + deps = [ + "//tensorflow/python:client_testlib", + "//tensorflow/python:math_ops", + "//tensorflow/python:session", + "//tensorflow/python/data/experimental/ops:batching", + "//tensorflow/python/data/experimental/ops:optimization", + "//tensorflow/python/data/ops:dataset_ops", + "//third_party/py/numpy", + ], +) + +py_test( + name = "csv_dataset_benchmark", + srcs = ["csv_dataset_benchmark.py"], + srcs_version = "PY2AND3", + tags = ["no_pip"], + deps = [ + "//tensorflow/python:client_testlib", + "//tensorflow/python:parsing_ops", + "//tensorflow/python:platform", + "//tensorflow/python:platform_test", + "//tensorflow/python:session", + "//tensorflow/python/data/experimental/ops:readers", + "//tensorflow/python/data/ops:readers", + "//third_party/py/numpy", + ], +) + py_test( name = "map_and_batch_benchmark", - size = "medium", srcs = ["map_and_batch_benchmark.py"], srcs_version = "PY2AND3", deps = [ + "//tensorflow/core:protos_all_py", "//tensorflow/python:array_ops", "//tensorflow/python:client_testlib", - "//tensorflow/python:framework_ops", + "//tensorflow/python:constant_op", + "//tensorflow/python:dtypes", + "//tensorflow/python:math_ops", "//tensorflow/python:random_ops", "//tensorflow/python:session", "//tensorflow/python/data/experimental/ops:batching", - "//tensorflow/python/data/experimental/ops:optimization", "//tensorflow/python/data/ops:dataset_ops", "//third_party/py/numpy", ], ) py_test( - name = "map_benchmark", - size = "medium", - srcs = ["map_benchmark.py"], + name = "map_vectorization_benchmark", + srcs = ["map_vectorization_benchmark.py"], + srcs_version = "PY2AND3", + deps = [ + "//tensorflow/core:protos_all_py", + "//tensorflow/python:array_ops", + "//tensorflow/python:client_testlib", + "//tensorflow/python:constant_op", + "//tensorflow/python:dtypes", + "//tensorflow/python:math_ops", + "//tensorflow/python:parsing_ops", + "//tensorflow/python:session", + "//tensorflow/python/data/ops:dataset_ops", + "//tensorflow/python/data/util:nest", + "//third_party/py/numpy", + ], +) + +py_test( + name = "matching_files_benchmark", + size = "small", + srcs = ["matching_files_benchmark.py"], + srcs_version = "PY2AND3", + deps = [ + "//tensorflow/python:array_ops", + "//tensorflow/python:client_testlib", + "//tensorflow/python:dtypes", + "//tensorflow/python:errors", + "//tensorflow/python:util", + "//tensorflow/python/data/experimental/ops:matching_files", + "//tensorflow/python/data/ops:dataset_ops", + "//third_party/py/numpy", + ], +) + +py_test( + name = "optimize_benchmark", + srcs = ["optimize_benchmark.py"], srcs_version = "PY2AND3", deps = [ "//tensorflow/python:client_testlib", "//tensorflow/python:framework_ops", "//tensorflow/python:math_ops", "//tensorflow/python:session", + "//tensorflow/python/data/ops:dataset_ops", + "//third_party/py/numpy", + ], +) + +py_test( + name = "unbatch_benchmark", + srcs = ["unbatch_benchmark.py"], + srcs_version = "PY2AND3", + deps = [ + "//tensorflow/python:array_ops", + "//tensorflow/python:client_testlib", + "//tensorflow/python:dtypes", + "//tensorflow/python:framework_ops", + "//tensorflow/python:session", "//tensorflow/python/data/experimental/ops:batching", - "//tensorflow/python/data/experimental/ops:optimization", "//tensorflow/python/data/ops:dataset_ops", "//third_party/py/numpy", ], diff --git a/tensorflow/python/data/experimental/benchmarks/autotune_benchmark.py b/tensorflow/python/data/experimental/benchmarks/autotune_benchmark.py new file mode 100644 index 0000000000..b48ef95666 --- /dev/null +++ b/tensorflow/python/data/experimental/benchmarks/autotune_benchmark.py @@ -0,0 +1,187 @@ +# Copyright 2018 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. +# ============================================================================== +"""Benchmarks for autotuning performance knobs.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import time + +import numpy as np + +from tensorflow.python.client import session +from tensorflow.python.data.experimental.ops import batching +from tensorflow.python.data.experimental.ops import optimization +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.platform import test + + +class AutotuneBenchmark(test.Benchmark): + """Benchmarks for autotuning performance knobs.""" + + def benchmarkMap(self): + k = 1024 * 1024 + dataset = dataset_ops.Dataset.from_tensors((np.random.rand(1, 4 * k), + np.random.rand(4 * k, + 1))).repeat() + dataset = dataset.map( + math_ops.matmul, num_parallel_calls=optimization.AUTOTUNE) + iterator = dataset_ops.make_one_shot_iterator(dataset) + get_next = iterator.get_next() + + deltas = [] + with session.Session() as sess: + for _ in range(5): + sess.run(get_next.op) + for _ in range(1000): + start = time.time() + sess.run(get_next.op) + end = time.time() + deltas.append(end - start) + + print("%f (median), %f (mean), %f (stddev), %f (min), %f (max)\n" % + (np.median(deltas), np.mean(deltas), np.std(deltas), np.min(deltas), + np.max(deltas))) + self.report_benchmark( + iters=1000, wall_time=np.median(deltas), name="map_autotune") + + def benchmarkMapAndBatch(self): + self._benchmarkMapAndBatch(numa_aware=False) + self._benchmarkMapAndBatch(numa_aware=True) + + def _benchmarkMapAndBatch(self, numa_aware): + batch_size = 16 + k = 1024 * 1024 + dataset = dataset_ops.Dataset.from_tensors((np.random.rand(1, 4 * k), + np.random.rand(4 * k, + 1))).repeat() + dataset = dataset.apply( + batching.map_and_batch( + math_ops.matmul, + num_parallel_calls=optimization.AUTOTUNE, + batch_size=batch_size)) + options = dataset_ops.Options() + options.experimental_numa_aware = numa_aware + dataset = dataset.with_options(options) + iterator = dataset_ops.make_one_shot_iterator(dataset) + get_next = iterator.get_next() + + deltas = [] + with session.Session() as sess: + for _ in range(5): + sess.run(get_next.op) + for _ in range(100): + start = time.time() + sess.run(get_next.op) + end = time.time() + deltas.append(end - start) + + print("%f (median), %f (mean), %f (stddev), %f (min), %f (max)\n" % + (np.median(deltas), np.mean(deltas), np.std(deltas), np.min(deltas), + np.max(deltas))) + + self.report_benchmark( + iters=100, + wall_time=np.median(deltas), + name=("numa_" if numa_aware else "") + "map_and_batch_autotune") + + def benchmarkInterleave(self): + k = 1024 * 1024 + dataset = dataset_ops.Dataset.from_tensors((np.random.rand(1, 4 * k), + np.random.rand(4 * k, + 1))).repeat() + dataset = dataset.map(math_ops.matmul) + dataset = dataset_ops.Dataset.range(1).repeat().interleave( + lambda _: dataset, + cycle_length=10, + num_parallel_calls=optimization.AUTOTUNE) + iterator = dataset_ops.make_one_shot_iterator(dataset) + get_next = iterator.get_next() + + deltas = [] + with session.Session() as sess: + for _ in range(5): + sess.run(get_next.op) + for _ in range(1000): + start = time.time() + sess.run(get_next.op) + end = time.time() + deltas.append(end - start) + + print("%f (median), %f (mean), %f (stddev), %f (min), %f (max)\n" % + (np.median(deltas), np.mean(deltas), np.std(deltas), np.min(deltas), + np.max(deltas))) + self.report_benchmark( + iters=1000, + wall_time=np.median(deltas), + name="interleave_autotune") + + def benchmarkMapAndInterleave(self): + k = 1024 * 1024 + a = (np.random.rand(1, 8 * k), np.random.rand(8 * k, 1)) + b = (np.random.rand(1, 4 * k), np.random.rand(4 * k, 1)) + c = (np.random.rand(1, 2 * k), np.random.rand(2 * k, 1)) + dataset = dataset_ops.Dataset.from_tensors((a, b, c)).repeat() + + def f1(a, b, c): + x, y = a + return math_ops.matmul(x, y), b, c + + def f2(a, b, c): + x, y = b + return a, math_ops.matmul(x, y), c + + def f3(a, b, c): + x, y = c + return a, b, math_ops.matmul(x, y) + + dataset = dataset.map(f1, num_parallel_calls=optimization.AUTOTUNE) + dataset = dataset_ops.Dataset.range(1).repeat().interleave( + lambda _: dataset, + num_parallel_calls=optimization.AUTOTUNE, + cycle_length=2) + + dataset = dataset.map(f2, num_parallel_calls=optimization.AUTOTUNE) + dataset = dataset_ops.Dataset.range(1).repeat().interleave( + lambda _: dataset, + num_parallel_calls=optimization.AUTOTUNE, + cycle_length=2) + + dataset = dataset.map(f3, num_parallel_calls=optimization.AUTOTUNE) + iterator = dataset_ops.make_one_shot_iterator(dataset) + get_next = iterator.get_next() + + deltas = [] + with session.Session() as sess: + for _ in range(5): + sess.run(get_next) + for _ in range(100): + start = time.time() + sess.run(get_next) + end = time.time() + deltas.append(end - start) + + print("%f (median), %f (mean), %f (stddev), %f (min), %f (max)\n" % + (np.median(deltas), np.mean(deltas), np.std(deltas), np.min(deltas), + np.max(deltas))) + self.report_benchmark( + iters=100, + wall_time=np.median(deltas), + name="map_and_interleave_autotune") + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/data/experimental/benchmarks/csv_dataset_benchmark.py b/tensorflow/python/data/experimental/benchmarks/csv_dataset_benchmark.py new file mode 100644 index 0000000000..03345ce4e6 --- /dev/null +++ b/tensorflow/python/data/experimental/benchmarks/csv_dataset_benchmark.py @@ -0,0 +1,130 @@ +# Copyright 2018 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. +# ============================================================================== +"""Benchmarks for `tf.data.experimental.CsvDataset`.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os +import string +import tempfile +import time + +import numpy as np + +from tensorflow.python.client import session +from tensorflow.python.data.experimental.ops import readers +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.data.ops import readers as core_readers +from tensorflow.python.ops import parsing_ops +from tensorflow.python.platform import gfile +from tensorflow.python.platform import googletest +from tensorflow.python.platform import test + + +class CsvDatasetBenchmark(test.Benchmark): + """Benchmarks for `tf.data.experimental.CsvDataset`.""" + + FLOAT_VAL = '1.23456E12' + STR_VAL = string.ascii_letters * 10 + + def _setUp(self, str_val): + # Since this isn't test.TestCase, have to manually create a test dir + gfile.MakeDirs(googletest.GetTempDir()) + self._temp_dir = tempfile.mkdtemp(dir=googletest.GetTempDir()) + + self._num_cols = [4, 64, 256] + self._num_per_iter = 5000 + self._filenames = [] + for n in self._num_cols: + fn = os.path.join(self._temp_dir, 'file%d.csv' % n) + with open(fn, 'wb') as f: + # Just write 100 rows and use `repeat`... Assumes the cost + # of creating an iterator is not significant + row = ','.join([str_val for _ in range(n)]) + f.write('\n'.join([row for _ in range(100)])) + self._filenames.append(fn) + + def _tearDown(self): + gfile.DeleteRecursively(self._temp_dir) + + def _runBenchmark(self, dataset, num_cols, prefix): + dataset = dataset.skip(self._num_per_iter - 1) + deltas = [] + for _ in range(10): + next_element = dataset_ops.make_one_shot_iterator(dataset).get_next() + with session.Session() as sess: + start = time.time() + # NOTE: This depends on the underlying implementation of skip, to have + # the net effect of calling `GetNext` num_per_iter times on the + # input dataset. We do it this way (instead of a python for loop, or + # batching N inputs in one iter) so that the overhead from session.run + # or batch doesn't dominate. If we eventually optimize skip, this has + # to change. + sess.run(next_element) + end = time.time() + deltas.append(end - start) + # Median wall time per CSV record read and decoded + median_wall_time = np.median(deltas) / self._num_per_iter + print('%s num_cols: %d Median wall time: %f' % (prefix, num_cols, + median_wall_time)) + self.report_benchmark( + iters=self._num_per_iter, + wall_time=median_wall_time, + name='%s_with_cols_%d' % (prefix, num_cols)) + + def benchmarkMapWithFloats(self): + self._setUp(self.FLOAT_VAL) + for i in range(len(self._filenames)): + num_cols = self._num_cols[i] + kwargs = {'record_defaults': [[0.0]] * num_cols} + dataset = core_readers.TextLineDataset(self._filenames[i]).repeat() + dataset = dataset.map(lambda l: parsing_ops.decode_csv(l, **kwargs)) # pylint: disable=cell-var-from-loop + self._runBenchmark(dataset, num_cols, 'csv_float_map_decode_csv') + self._tearDown() + + def benchmarkMapWithStrings(self): + self._setUp(self.STR_VAL) + for i in range(len(self._filenames)): + num_cols = self._num_cols[i] + kwargs = {'record_defaults': [['']] * num_cols} + dataset = core_readers.TextLineDataset(self._filenames[i]).repeat() + dataset = dataset.map(lambda l: parsing_ops.decode_csv(l, **kwargs)) # pylint: disable=cell-var-from-loop + self._runBenchmark(dataset, num_cols, 'csv_strings_map_decode_csv') + self._tearDown() + + def benchmarkCsvDatasetWithFloats(self): + self._setUp(self.FLOAT_VAL) + for i in range(len(self._filenames)): + num_cols = self._num_cols[i] + kwargs = {'record_defaults': [[0.0]] * num_cols} + dataset = core_readers.TextLineDataset(self._filenames[i]).repeat() + dataset = readers.CsvDataset(self._filenames[i], **kwargs).repeat() # pylint: disable=cell-var-from-loop + self._runBenchmark(dataset, num_cols, 'csv_float_fused_dataset') + self._tearDown() + + def benchmarkCsvDatasetWithStrings(self): + self._setUp(self.STR_VAL) + for i in range(len(self._filenames)): + num_cols = self._num_cols[i] + kwargs = {'record_defaults': [['']] * num_cols} + dataset = core_readers.TextLineDataset(self._filenames[i]).repeat() + dataset = readers.CsvDataset(self._filenames[i], **kwargs).repeat() # pylint: disable=cell-var-from-loop + self._runBenchmark(dataset, num_cols, 'csv_strings_fused_dataset') + self._tearDown() + +if __name__ == '__main__': + test.main() diff --git a/tensorflow/python/data/experimental/benchmarks/map_and_batch_benchmark.py b/tensorflow/python/data/experimental/benchmarks/map_and_batch_benchmark.py index a90156cd33..fbd06a5a78 100644 --- a/tensorflow/python/data/experimental/benchmarks/map_and_batch_benchmark.py +++ b/tensorflow/python/data/experimental/benchmarks/map_and_batch_benchmark.py @@ -17,6 +17,8 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import hashlib +import itertools import time import numpy as np @@ -25,11 +27,15 @@ from tensorflow.core.protobuf import config_pb2 from tensorflow.python.client import session from tensorflow.python.data.experimental.ops import batching from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.ops import array_ops +from tensorflow.python.ops import math_ops from tensorflow.python.ops import random_ops from tensorflow.python.platform import test +_NUMPY_RANDOM_SEED = 42 + class MapAndBatchBenchmark(test.Benchmark): """Benchmarks for `tf.data.experimental.map_and_batch()`.""" @@ -48,7 +54,7 @@ class MapAndBatchBenchmark(test.Benchmark): dataset = dataset.apply(batching.map_and_batch( lambda _: dense_value, batch_size_placeholder)) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) next_element = iterator.get_next() for shape in shapes: @@ -89,6 +95,129 @@ class MapAndBatchBenchmark(test.Benchmark): name="benchmark_batch_dense_dataset_nnz_%d_batch_size_%d" % ( np.prod(shape), batch_size)) + def benchmarkMapAndBatchChainingVersusFusing(self): + """Compares the performance of chaining and fusing map and batch. + + NOTE: It is recommended to build the benchmark with + `-c opt --copt=-mavx --copt=-mavx2 --copt=-mfma --copt=-gmlt` + and execute it on a machine with at least 32 CPU cores. + """ + + # Sequential pipeline configurations. + seq_elem_size_series = itertools.product([1], [1], [1, 2, 4, 8], [16]) + seq_batch_size_series = itertools.product([1], [1], [1], [8, 16, 32, 64]) + + # Parallel pipeline configuration. + par_elem_size_series = itertools.product([32], [32], [1, 2, 4, 8], [256]) + par_batch_size_series = itertools.product([32], [32], [1], + [128, 256, 512, 1024]) + par_num_calls_series = itertools.product([8, 16, 32, 64], [32], [1], [512]) + par_inter_op_series = itertools.product([32], [8, 16, 32, 64], [1], [512]) + + def name(method, label, num_calls, inter_op, element_size, batch_size): + return ("%s_id_%s_num_calls_%d_inter_op_%d_elem_size_%d_batch_size_%d" % ( + method, + hashlib.sha1(label).hexdigest()[:8], + num_calls, + inter_op, + element_size, + batch_size, + )) + + def benchmark(label, series): + """Runs benchmark the given series.""" + + print("%s:" % label) + + def make_base_dataset(element_size): + k = 1024 * 1024 + x = constant_op.constant(np.random.rand(element_size, 4 * k)) + y = constant_op.constant(np.random.rand(4 * k, 1)) + return dataset_ops.Dataset.range(1000000000000).map(lambda _: (x, y)) + + for num_calls, inter_op, element_size, batch_size in series: + + num_iters = 1024 // ( + (element_size * batch_size) // min(num_calls, inter_op)) + dataset = make_base_dataset(element_size) + chained_dataset = dataset.map( + math_ops.matmul, + num_parallel_calls=num_calls).batch(batch_size=batch_size) + chained_iterator = dataset_ops.make_one_shot_iterator(chained_dataset) + chained_get_next = chained_iterator.get_next() + + chained_deltas = [] + with session.Session( + config=config_pb2.ConfigProto( + inter_op_parallelism_threads=inter_op, + use_per_session_threads=True)) as sess: + for _ in range(5): + sess.run(chained_get_next.op) + for _ in range(num_iters): + start = time.time() + sess.run(chained_get_next.op) + end = time.time() + chained_deltas.append(end - start) + + fused_dataset = dataset.apply( + batching.map_and_batch( + math_ops.matmul, + num_parallel_calls=num_calls, + batch_size=batch_size)) + fused_iterator = dataset_ops.make_one_shot_iterator(fused_dataset) + fused_get_next = fused_iterator.get_next() + + fused_deltas = [] + with session.Session( + config=config_pb2.ConfigProto( + inter_op_parallelism_threads=inter_op, + use_per_session_threads=True)) as sess: + + for _ in range(5): + sess.run(fused_get_next.op) + for _ in range(num_iters): + start = time.time() + sess.run(fused_get_next.op) + end = time.time() + fused_deltas.append(end - start) + + print( + "batch size: %d, num parallel calls: %d, inter-op parallelism: %d, " + "element size: %d, num iters: %d\nchained wall time: %f (median), " + "%f (mean), %f (stddev), %f (min), %f (max)\n fused wall time: " + "%f (median), %f (mean), %f (stddev), %f (min), %f (max)\n " + "chained/fused: %.2fx (median), %.2fx (mean)" % + (batch_size, num_calls, inter_op, element_size, num_iters, + np.median(chained_deltas), np.mean(chained_deltas), + np.std(chained_deltas), np.min(chained_deltas), + np.max(chained_deltas), np.median(fused_deltas), + np.mean(fused_deltas), np.std(fused_deltas), np.min(fused_deltas), + np.max(fused_deltas), + np.median(chained_deltas) / np.median(fused_deltas), + np.mean(chained_deltas) / np.mean(fused_deltas))) + + self.report_benchmark( + iters=num_iters, + wall_time=np.median(chained_deltas), + name=name("chained", label, num_calls, inter_op, element_size, + batch_size)) + + self.report_benchmark( + iters=num_iters, + wall_time=np.median(fused_deltas), + name=name("fused", label, num_calls, inter_op, element_size, + batch_size)) + + print() + + np.random.seed(_NUMPY_RANDOM_SEED) + benchmark("Sequential element size evaluation", seq_elem_size_series) + benchmark("Sequential batch size evaluation", seq_batch_size_series) + benchmark("Parallel element size evaluation", par_elem_size_series) + benchmark("Parallel batch size evaluation", par_batch_size_series) + benchmark("Transformation parallelism evaluation", par_num_calls_series) + benchmark("Threadpool size evaluation", par_inter_op_series) + if __name__ == "__main__": test.main() diff --git a/tensorflow/python/data/experimental/benchmarks/map_benchmark.py b/tensorflow/python/data/experimental/benchmarks/map_benchmark.py deleted file mode 100644 index ad253cffa5..0000000000 --- a/tensorflow/python/data/experimental/benchmarks/map_benchmark.py +++ /dev/null @@ -1,245 +0,0 @@ -# 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. -# ============================================================================== -"""Tests for the experimental input pipeline ops.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import hashlib -import itertools -import time - -import numpy as np - -from tensorflow.core.protobuf import config_pb2 -from tensorflow.python.client import session -from tensorflow.python.data.experimental.ops import batching -from tensorflow.python.data.experimental.ops import optimization -from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.framework import ops -from tensorflow.python.ops import math_ops -from tensorflow.python.platform import test - -_NUMPY_RANDOM_SEED = 42 - - -class MapDatasetBenchmark(test.Benchmark): - - # The purpose of this benchmark is to compare the performance of chaining vs - # fusing of the map and batch transformations across various configurations. - # - # NOTE: It is recommended to build the benchmark with - # `-c opt --copt=-mavx --copt=-mavx2 --copt=-mfma --copt=-gmlt` - # and execute it on a machine with at least 32 CPU cores. - def benchmarkMapAndBatch(self): - - # Sequential pipeline configurations. - seq_elem_size_series = itertools.product([1], [1], [1, 2, 4, 8], [16]) - seq_batch_size_series = itertools.product([1], [1], [1], [8, 16, 32, 64]) - - # Parallel pipeline configuration. - par_elem_size_series = itertools.product([32], [32], [1, 2, 4, 8], [256]) - par_batch_size_series = itertools.product([32], [32], [1], - [128, 256, 512, 1024]) - par_num_calls_series = itertools.product([8, 16, 32, 64], [32], [1], [512]) - par_inter_op_series = itertools.product([32], [8, 16, 32, 64], [1], [512]) - - def name(method, label, num_calls, inter_op, element_size, batch_size): - return ("%s_id_%s_num_calls_%d_inter_op_%d_elem_size_%d_batch_size_%d" % ( - method, - hashlib.sha1(label).hexdigest(), - num_calls, - inter_op, - element_size, - batch_size, - )) - - def benchmark(label, series): - - print("%s:" % label) - for num_calls, inter_op, element_size, batch_size in series: - - num_iters = 1024 // ( - (element_size * batch_size) // min(num_calls, inter_op)) - k = 1024 * 1024 - dataset = dataset_ops.Dataset.from_tensors((np.random.rand( - element_size, 4 * k), np.random.rand(4 * k, 1))).repeat() - - chained_dataset = dataset.map( - math_ops.matmul, - num_parallel_calls=num_calls).batch(batch_size=batch_size) - chained_iterator = chained_dataset.make_one_shot_iterator() - chained_get_next = chained_iterator.get_next() - - chained_deltas = [] - with session.Session( - config=config_pb2.ConfigProto( - inter_op_parallelism_threads=inter_op, - use_per_session_threads=True)) as sess: - for _ in range(5): - sess.run(chained_get_next.op) - for _ in range(num_iters): - start = time.time() - sess.run(chained_get_next.op) - end = time.time() - chained_deltas.append(end - start) - - fused_dataset = dataset.apply( - batching.map_and_batch( - math_ops.matmul, - num_parallel_calls=num_calls, - batch_size=batch_size)) - fused_iterator = fused_dataset.make_one_shot_iterator() - fused_get_next = fused_iterator.get_next() - - fused_deltas = [] - with session.Session( - config=config_pb2.ConfigProto( - inter_op_parallelism_threads=inter_op, - use_per_session_threads=True)) as sess: - - for _ in range(5): - sess.run(fused_get_next.op) - for _ in range(num_iters): - start = time.time() - sess.run(fused_get_next.op) - end = time.time() - fused_deltas.append(end - start) - - print( - "batch size: %d, num parallel calls: %d, inter-op parallelism: %d, " - "element size: %d, num iters: %d\nchained wall time: %f (median), " - "%f (mean), %f (stddev), %f (min), %f (max)\n fused wall time: " - "%f (median), %f (mean), %f (stddev), %f (min), %f (max)\n " - "chained/fused: %.2fx (median), %.2fx (mean)" % - (batch_size, num_calls, inter_op, element_size, num_iters, - np.median(chained_deltas), np.mean(chained_deltas), - np.std(chained_deltas), np.min(chained_deltas), - np.max(chained_deltas), np.median(fused_deltas), - np.mean(fused_deltas), np.std(fused_deltas), np.min(fused_deltas), - np.max(fused_deltas), - np.median(chained_deltas) / np.median(fused_deltas), - np.mean(chained_deltas) / np.mean(fused_deltas))) - - self.report_benchmark( - iters=num_iters, - wall_time=np.median(chained_deltas), - name=name("chained", label, num_calls, inter_op, element_size, - batch_size)) - - self.report_benchmark( - iters=num_iters, - wall_time=np.median(fused_deltas), - name=name("fused", label, num_calls, inter_op, element_size, - batch_size)) - - print("") - - np.random.seed(_NUMPY_RANDOM_SEED) - benchmark("Sequential element size evaluation", seq_elem_size_series) - benchmark("Sequential batch size evaluation", seq_batch_size_series) - benchmark("Parallel element size evaluation", par_elem_size_series) - benchmark("Parallel batch size evaluation", par_batch_size_series) - benchmark("Transformation parallelism evaluation", par_num_calls_series) - benchmark("Threadpool size evaluation", par_inter_op_series) - - # This benchmark compares the performance of pipeline with multiple chained - # maps with and without map fusion. - def benchmarkChainOfMaps(self): - chain_lengths = [0, 1, 2, 5, 10, 20, 50] - for chain_length in chain_lengths: - self._benchmarkChainOfMaps(chain_length, False) - self._benchmarkChainOfMaps(chain_length, True) - - def _benchmarkChainOfMaps(self, chain_length, optimize_dataset): - with ops.Graph().as_default(): - dataset = dataset_ops.Dataset.from_tensors(0).repeat(None) - for _ in range(chain_length): - dataset = dataset.map(lambda x: x) - if optimize_dataset: - dataset = dataset.apply(optimization.optimize(["map_fusion"])) - - iterator = dataset.make_one_shot_iterator() - next_element = iterator.get_next() - - with session.Session() as sess: - for _ in range(5): - sess.run(next_element.op) - deltas = [] - for _ in range(100): - start = time.time() - for _ in range(100): - sess.run(next_element.op) - end = time.time() - deltas.append(end - start) - - median_wall_time = np.median(deltas) / 100 - opt_mark = "opt" if optimize_dataset else "no-opt" - print("Map dataset {} chain length: {} Median wall time: {}".format( - opt_mark, chain_length, median_wall_time)) - self.report_benchmark( - iters=1000, - wall_time=median_wall_time, - name="benchmark_map_dataset_chain_latency_{}_{}".format( - opt_mark, chain_length)) - - -class MapAndFilterBenchmark(test.Benchmark): - - # This benchmark compares the performance of pipeline with multiple chained - # map + filter with and without map fusion. - def benchmarkMapAndFilter(self): - chain_lengths = [0, 1, 2, 5, 10, 20, 50] - for chain_length in chain_lengths: - self._benchmarkMapAndFilter(chain_length, False) - self._benchmarkMapAndFilter(chain_length, True) - - def _benchmarkMapAndFilter(self, chain_length, optimize_dataset): - with ops.Graph().as_default(): - dataset = dataset_ops.Dataset.from_tensors(0).repeat(None) - for _ in range(chain_length): - dataset = dataset.map(lambda x: x + 5).filter( - lambda x: math_ops.greater_equal(x - 5, 0)) - if optimize_dataset: - dataset = dataset.apply( - optimization.optimize(["map_and_filter_fusion"])) - - iterator = dataset.make_one_shot_iterator() - next_element = iterator.get_next() - - with session.Session() as sess: - for _ in range(10): - sess.run(next_element.op) - deltas = [] - for _ in range(100): - start = time.time() - for _ in range(100): - sess.run(next_element.op) - end = time.time() - deltas.append(end - start) - - median_wall_time = np.median(deltas) / 100 - opt_mark = "opt" if optimize_dataset else "no-opt" - print("Map and filter dataset {} chain length: {} Median wall time: {}". - format(opt_mark, chain_length, median_wall_time)) - self.report_benchmark( - iters=1000, - wall_time=median_wall_time, - name="benchmark_map_and_filter_dataset_chain_latency_{}_{}".format( - opt_mark, chain_length)) - - -if __name__ == "__main__": - test.main() diff --git a/tensorflow/python/data/experimental/benchmarks/map_vectorization_benchmark.py b/tensorflow/python/data/experimental/benchmarks/map_vectorization_benchmark.py new file mode 100644 index 0000000000..47ec6391f7 --- /dev/null +++ b/tensorflow/python/data/experimental/benchmarks/map_vectorization_benchmark.py @@ -0,0 +1,194 @@ +# Copyright 2018 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. +# ============================================================================== +"""Benchmarks for the `MapVectorization` optimization.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import time + +import numpy as np + +from tensorflow.core.example import example_pb2 +from tensorflow.core.example import feature_pb2 +from tensorflow.python.client import session +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.data.util import nest +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import parsing_ops +from tensorflow.python.platform import test + + +def _generate_csv_test_case(): + """Generates a `decode_csv()` test case.""" + + def csv_factory(): + return dataset_ops.Dataset.from_tensor_slices(["1.0:2:a", + "2.4:5:c"]).repeat(5) + + def decode_csv_fn(x): + return parsing_ops.decode_csv( + x, + record_defaults=[ + constant_op.constant([], dtypes.float32), + constant_op.constant([], dtypes.int32), + constant_op.constant([], dtypes.string) + ], + field_delim=":") + + return decode_csv_fn, csv_factory + + +def _generate_parse_single_example_test_case(): + """Generates a `parse_single_example()` test case.""" + + def parse_example_factory(): + """Parse example factory.""" + + def _int64_feature(*values): + return feature_pb2.Feature(int64_list=feature_pb2.Int64List(value=values)) + + def _bytes_feature(*values): + return feature_pb2.Feature( + bytes_list=feature_pb2.BytesList( + value=[v.encode("utf-8") for v in values])) + + return dataset_ops.Dataset.from_tensor_slices( + constant_op.constant([ + example_pb2.Example( + features=feature_pb2.Features( + feature={ + "dense_int": _int64_feature(i), + "dense_str": _bytes_feature(str(i)), + "sparse_int": _int64_feature(i, i * 2, i * 4, i * 8), + "sparse_str": _bytes_feature(*["abc"] * i) + })).SerializeToString() for i in range(10) + ])) + + def parse_single_example_fn(x): + features = { + "dense_int": parsing_ops.FixedLenFeature((), dtypes.int64, 0), + "dense_str": parsing_ops.FixedLenFeature((), dtypes.string, ""), + "sparse_int": parsing_ops.VarLenFeature(dtypes.int64), + "sparse_str": parsing_ops.VarLenFeature(dtypes.string), + } + return parsing_ops.parse_single_example(x, features) + + return parse_single_example_fn, parse_example_factory + + +# TODO(rachelim): Add a benchmark for more expensive transformations, such as +# vgg_preprocessing. +class MapVectorizationBenchmark(test.Benchmark): + """Benchmarks for the `MapVectorization` optimization.""" + + def _run(self, x, num_iters=100, name=None): + deltas = [] + with session.Session() as sess: + for _ in range(5): + # Warm up session... + sess.run(x) + for _ in range(num_iters): + start = time.time() + sess.run(x) + end = time.time() + deltas.append(end - start) + median_time = np.median(deltas) + self.report_benchmark(iters=num_iters, wall_time=median_time, name=name) + return median_time + + def _compare(self, input_dataset, map_fn, batch_size, input_size, str_id): + num_elems = int(np.sum([np.prod(x) for x in input_size])) + name_template = "{}__batch_size_{}_input_element_size_{}_{}" + unoptimized = input_dataset.map(map_fn).batch(batch_size) + unoptimized_op = dataset_ops.make_one_shot_iterator(unoptimized).get_next() + + optimized = input_dataset.map(map_fn).batch(batch_size) + options = dataset_ops.Options() + options.experimental_map_vectorization = True + optimized = optimized.with_options(options) + optimized_op = dataset_ops.make_one_shot_iterator(optimized).get_next() + + unoptimized_time = self._run( + unoptimized_op, + name=name_template.format(str_id, batch_size, num_elems, "unoptimized")) + optimized_time = self._run( + optimized_op, + name=name_template.format(str_id, batch_size, num_elems, "optimized")) + + print("Batch size: {}\n" + "Input element size: {}\n" + "Transformation: {}\n" + "Speedup: {}\n".format(batch_size, input_size, str_id, + (unoptimized_time / optimized_time))) + + # Known cheap functions + def benchmarkIdentity(self): + self._benchmark_helper(lambda *args: [array_ops.identity(x) for x in args], + "identity") + + def benchmarkAddConst(self): + self._benchmark_helper(lambda *args: [x + 1 for x in args], "add_const") + + def benchmarkReturnConst(self): + self._benchmark_helper(lambda *args: [constant_op.constant(2)], "ret_const") + + def benchmarkSelect(self): + self._benchmark_helper(lambda *args: args[0], "select") + + def benchmarkCast(self): + self._benchmark_helper( + lambda *args: [math_ops.cast(x, dtypes.float64) for x in args], "cast") + + def benchmarkReshape(self): + self._benchmark_helper( + lambda *args: [array_ops.reshape(x, (-1, 30)) for x in args], "reshape") + + def benchmarkDecodeCSV(self): + csv_fn, csv_factory = _generate_csv_test_case() + self._benchmark_helper(csv_fn, "decode_csv", lambda: [csv_factory()]) + + def benchmarkParseSingleExample(self): + # NOTE: Since we haven't implemented a vectorizer for "SerializeSparse", + # this function is only naively vectorized. + parse_fn, parse_factory = _generate_parse_single_example_test_case() + + self._benchmark_helper(parse_fn, "parse_single_example", + lambda: [parse_factory()]) + + def _default_dataset_factory(self): + input_sizes = [(10, 10, 3), (10, 100, 300)] + for sz in input_sizes: + yield dataset_ops.Dataset.from_tensor_slices(np.random.rand(*sz)) + + def _benchmark_helper(self, map_fn, str_id, base_dataset_factory=None): + if base_dataset_factory is None: + base_dataset_factory = self._default_dataset_factory + + batch_size = 1000 + for base_dataset in base_dataset_factory(): + base_dataset = base_dataset.repeat() + input_size = [ + tuple(shape.as_list()) + for shape in nest.flatten(base_dataset.output_shapes) + ] + self._compare(base_dataset, map_fn, batch_size, input_size, str_id) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/data/experimental/benchmarks/matching_files_benchmark.py b/tensorflow/python/data/experimental/benchmarks/matching_files_benchmark.py new file mode 100644 index 0000000000..c53f8dd7c5 --- /dev/null +++ b/tensorflow/python/data/experimental/benchmarks/matching_files_benchmark.py @@ -0,0 +1,102 @@ +# Copyright 2018 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. +# ============================================================================== +"""Benchmark for the experimental `MatchingFilesDataset`.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os +import shutil +import tempfile +import time + +import numpy as np + +from tensorflow.python.client import session +from tensorflow.python.data.experimental.ops import matching_files +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.framework import errors +from tensorflow.python.framework import ops +from tensorflow.python.platform import test + + +class MatchingFilesBenchmark(test.Benchmark): + """Benchmark for the experimental `MatchingFilesDataset`.""" + + def benchmarkNestedDirectories(self): + tmp_dir = tempfile.mkdtemp() + width = 500 + depth = 10 + for i in range(width): + for j in range(depth): + new_base = os.path.join(tmp_dir, str(i), + *[str(dir_name) for dir_name in range(j)]) + os.makedirs(new_base) + child_files = ['a.py', 'b.pyc'] if j < depth - 1 else ['c.txt', 'd.log'] + for f in child_files: + filename = os.path.join(new_base, f) + open(filename, 'w').close() + + patterns = [ + os.path.join(tmp_dir, os.path.join(*['**' + for _ in range(depth)]), suffix) + for suffix in ['*.txt', '*.log'] + ] + + deltas = [] + iters = 3 + for _ in range(iters): + with ops.Graph().as_default(): + dataset = matching_files.MatchingFilesDataset(patterns) + next_element = dataset_ops.make_one_shot_iterator(dataset).get_next() + + with session.Session() as sess: + sub_deltas = [] + while True: + try: + start = time.time() + sess.run(next_element) + end = time.time() + sub_deltas.append(end - start) + except errors.OutOfRangeError: + break + deltas.append(sub_deltas) + + median_deltas = np.median(deltas, axis=0) + print('Nested directory size (width*depth): %d*%d Median wall time: ' + '%fs (read first filename), %fs (read second filename), avg %fs' + ' (read %d more filenames)' % + (width, depth, median_deltas[0], median_deltas[1], + np.average(median_deltas[2:]), len(median_deltas) - 2)) + self.report_benchmark( + iters=iters, + wall_time=np.sum(median_deltas), + extras={ + 'read first file:': + median_deltas[0], + 'read second file:': + median_deltas[1], + 'avg time for reading %d more filenames:' % + (len(median_deltas) - 2): + np.average(median_deltas[2:]) + }, + name='dataset_nested_directory(%d*%d)' % + (width, depth)) + + shutil.rmtree(tmp_dir, ignore_errors=True) + + +if __name__ == '__main__': + test.main() diff --git a/tensorflow/python/data/experimental/benchmarks/optimize_benchmark.py b/tensorflow/python/data/experimental/benchmarks/optimize_benchmark.py new file mode 100644 index 0000000000..2f9b89111f --- /dev/null +++ b/tensorflow/python/data/experimental/benchmarks/optimize_benchmark.py @@ -0,0 +1,120 @@ +# 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. +# ============================================================================== +"""Benchmarks for static optimizations.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import time + +import numpy as np + +from tensorflow.python.client import session +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.framework import ops +from tensorflow.python.ops import math_ops +from tensorflow.python.platform import test + + +class OptimizationBenchmark(test.Benchmark): + """Benchmarks for static optimizations.""" + + def benchmarkMapFusion(self): + """Evaluates performance map of fusion.""" + + chain_lengths = [0, 1, 2, 5, 10, 20, 50] + for chain_length in chain_lengths: + self._benchmarkMapFusion(chain_length, False) + self._benchmarkMapFusion(chain_length, True) + + def _benchmarkMapFusion(self, chain_length, optimize_dataset): + with ops.Graph().as_default(): + dataset = dataset_ops.Dataset.from_tensors(0).repeat(None) + for _ in range(chain_length): + dataset = dataset.map(lambda x: x) + if optimize_dataset: + options = dataset_ops.Options() + options.experimental_map_fusion = True + dataset = dataset.with_options(options) + + iterator = dataset_ops.make_one_shot_iterator(dataset) + next_element = iterator.get_next() + + with session.Session() as sess: + for _ in range(5): + sess.run(next_element.op) + deltas = [] + for _ in range(100): + start = time.time() + for _ in range(100): + sess.run(next_element.op) + end = time.time() + deltas.append(end - start) + + median_wall_time = np.median(deltas) / 100 + opt_mark = "opt" if optimize_dataset else "noopt" + print("Map dataset {} chain length: {} Median wall time: {}".format( + opt_mark, chain_length, median_wall_time)) + self.report_benchmark( + iters=100, + wall_time=median_wall_time, + name="map_fusion_{}_chain_length_{}".format( + opt_mark, chain_length)) + + def benchmarkMapAndFilterFusion(self): + """Evaluates performance map of fusion.""" + + chain_lengths = [0, 1, 2, 5, 10, 20, 50] + for chain_length in chain_lengths: + self._benchmarkMapAndFilterFusion(chain_length, False) + self._benchmarkMapAndFilterFusion(chain_length, True) + + def _benchmarkMapAndFilterFusion(self, chain_length, optimize_dataset): + with ops.Graph().as_default(): + dataset = dataset_ops.Dataset.from_tensors(0).repeat(None) + for _ in range(chain_length): + dataset = dataset.map(lambda x: x + 5).filter( + lambda x: math_ops.greater_equal(x - 5, 0)) + if optimize_dataset: + options = dataset_ops.Options() + options.experimental_map_and_filter_fusion = True + dataset = dataset.with_options(options) + iterator = dataset_ops.make_one_shot_iterator(dataset) + next_element = iterator.get_next() + + with session.Session() as sess: + for _ in range(10): + sess.run(next_element.op) + deltas = [] + for _ in range(100): + start = time.time() + for _ in range(100): + sess.run(next_element.op) + end = time.time() + deltas.append(end - start) + + median_wall_time = np.median(deltas) / 100 + opt_mark = "opt" if optimize_dataset else "noopt" + print("Map and filter dataset {} chain length: {} Median wall time: {}" + .format(opt_mark, chain_length, median_wall_time)) + self.report_benchmark( + iters=100, + wall_time=median_wall_time, + name="map_and_filter_fusion_{}_chain_length_{}".format( + opt_mark, chain_length)) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/data/experimental/benchmarks/unbatch_benchmark.py b/tensorflow/python/data/experimental/benchmarks/unbatch_benchmark.py new file mode 100644 index 0000000000..c36a32534d --- /dev/null +++ b/tensorflow/python/data/experimental/benchmarks/unbatch_benchmark.py @@ -0,0 +1,107 @@ +# 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. +# ============================================================================== +"""Tests for `tf.data.experimental.unbatch()`.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import time + +import numpy as np + +from tensorflow.python.client import session +from tensorflow.python.data.experimental.ops import batching +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops +from tensorflow.python.ops import array_ops +from tensorflow.python.platform import test + + +class UnbatchBenchmark(test.Benchmark): + """Benchmarks for `tf.data.experimental.unbatch()`.""" + + def benchmarkNativeUnbatch(self): + batch_sizes = [1, 2, 5, 10, 20, 50] + elems_per_trial = 10000 + with ops.Graph().as_default(): + dataset = dataset_ops.Dataset.from_tensors("element").repeat(None) + batch_size_placeholder = array_ops.placeholder(dtypes.int64, shape=[]) + dataset = dataset.batch(batch_size_placeholder) + dataset = dataset.apply(batching.unbatch()) + dataset = dataset.skip(elems_per_trial) + iterator = dataset_ops.make_initializable_iterator(dataset) + next_element = iterator.get_next() + + with session.Session() as sess: + for batch_size in batch_sizes: + deltas = [] + for _ in range(5): + sess.run( + iterator.initializer, + feed_dict={batch_size_placeholder: batch_size}) + start = time.time() + sess.run(next_element.op) + end = time.time() + deltas.append((end - start) / elems_per_trial) + + median_wall_time = np.median(deltas) + print("Unbatch (native) batch size: %d Median wall time per element:" + " %f microseconds" % (batch_size, median_wall_time * 1e6)) + self.report_benchmark( + iters=10000, + wall_time=median_wall_time, + name="native_batch_size_%d" % + batch_size) + + # Include a benchmark of the previous `unbatch()` implementation that uses + # a composition of more primitive ops. Eventually we'd hope to generate code + # that is as good in both cases. + def benchmarkOldUnbatchImplementation(self): + batch_sizes = [1, 2, 5, 10, 20, 50] + elems_per_trial = 10000 + with ops.Graph().as_default(): + dataset = dataset_ops.Dataset.from_tensors("element").repeat(None) + batch_size_placeholder = array_ops.placeholder(dtypes.int64, shape=[]) + dataset = dataset.batch(batch_size_placeholder) + dataset = dataset.flat_map(dataset_ops.Dataset.from_tensor_slices) + dataset = dataset.skip(elems_per_trial) + iterator = dataset_ops.make_initializable_iterator(dataset) + next_element = iterator.get_next() + + with session.Session() as sess: + for batch_size in batch_sizes: + deltas = [] + for _ in range(5): + sess.run( + iterator.initializer, + feed_dict={batch_size_placeholder: batch_size}) + start = time.time() + sess.run(next_element.op) + end = time.time() + deltas.append((end - start) / elems_per_trial) + + median_wall_time = np.median(deltas) + print("Unbatch (unfused) batch size: %d Median wall time per element:" + " %f microseconds" % (batch_size, median_wall_time * 1e6)) + self.report_benchmark( + iters=10000, + wall_time=median_wall_time, + name="unfused_batch_size_%d" % + batch_size) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/data/experimental/kernel_tests/BUILD b/tensorflow/python/data/experimental/kernel_tests/BUILD index c9b11a2c38..c76e576b5b 100644 --- a/tensorflow/python/data/experimental/kernel_tests/BUILD +++ b/tensorflow/python/data/experimental/kernel_tests/BUILD @@ -72,15 +72,11 @@ py_test( "//tensorflow/python:errors", "//tensorflow/python:framework_test_lib", "//tensorflow/python:parsing_ops", - "//tensorflow/python:platform", - "//tensorflow/python:platform_test", - "//tensorflow/python:session", "//tensorflow/python/data/experimental/ops:error_ops", "//tensorflow/python/data/experimental/ops:readers", "//tensorflow/python/data/kernel_tests:test_base", "//tensorflow/python/data/ops:readers", "//tensorflow/python/eager:context", - "//third_party/py/numpy", ], ) @@ -153,27 +149,6 @@ py_test( ], ) -cuda_py_test( - name = "function_buffering_resource_test", - size = "small", - srcs = ["function_buffering_resource_test.py"], - additional_deps = [ - "//tensorflow/python/data/experimental/ops:prefetching_ops", - "//tensorflow/core:protos_all_py", - "//tensorflow/python:client_testlib", - "//tensorflow/python:constant_op", - "//tensorflow/python/data/kernel_tests:test_base", - "//tensorflow/python/eager:function", - "//tensorflow/python:dtypes", - "//tensorflow/python:framework_ops", - "//tensorflow/python:framework_test_lib", - "//tensorflow/python:resource_variable_ops", - "//tensorflow/python/data/ops:dataset_ops", - "//tensorflow/python/data/ops:iterator_ops", - ], - tags = ["no_windows_gpu"], -) - py_test( name = "get_single_element_test", size = "small", @@ -371,6 +346,37 @@ py_test( ], ) +py_test( + name = "matching_files_test", + size = "small", + srcs = ["matching_files_test.py"], + srcs_version = "PY2AND3", + tags = ["no_pip"], + deps = [ + "//tensorflow/python:array_ops", + "//tensorflow/python:client_testlib", + "//tensorflow/python:dtypes", + "//tensorflow/python:errors", + "//tensorflow/python:util", + "//tensorflow/python/data/experimental/ops:matching_files", + "//tensorflow/python/data/kernel_tests:test_base", + "//tensorflow/python/data/ops:dataset_ops", + "//third_party/py/numpy", + ], +) + +py_test( + name = "cardinality_test", + srcs = ["cardinality_test.py"], + srcs_version = "PY2AND3", + deps = [ + "//tensorflow/python/data/experimental/ops:cardinality", + "//tensorflow/python/data/kernel_tests:test_base", + "//tensorflow/python/data/ops:dataset_ops", + "@absl_py//absl/testing:parameterized", + ], +) + py_test( name = "override_threadpool_test", size = "small", @@ -618,7 +624,9 @@ py_test( size = "medium", srcs = ["stats_dataset_ops_test.py"], srcs_version = "PY2AND3", - tags = ["no_pip"], + tags = [ + "no_pip", + ], deps = [ ":reader_dataset_ops_test_base", ":stats_dataset_test_base", diff --git a/tensorflow/python/data/experimental/kernel_tests/batch_dataset_op_test.py b/tensorflow/python/data/experimental/kernel_tests/batch_dataset_op_test.py deleted file mode 100644 index e896752a26..0000000000 --- a/tensorflow/python/data/experimental/kernel_tests/batch_dataset_op_test.py +++ /dev/null @@ -1,688 +0,0 @@ -# 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. -# ============================================================================== -"""Tests for the experimental input pipeline ops.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import math -import time - -from absl.testing import parameterized -import numpy as np - -from tensorflow.python.client import session -from tensorflow.python.data.experimental.ops import batching -from tensorflow.python.data.kernel_tests import test_base -from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.framework import constant_op -from tensorflow.python.framework import dtypes -from tensorflow.python.framework import errors -from tensorflow.python.framework import ops -from tensorflow.python.framework import sparse_tensor -from tensorflow.python.ops import array_ops -from tensorflow.python.ops import math_ops -from tensorflow.python.ops import script_ops -from tensorflow.python.ops import string_ops -from tensorflow.python.platform import test -from tensorflow.python.util import compat - - -class BatchDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): - - def testDenseToSparseBatchDataset(self): - components = np.random.randint(12, size=(100,)).astype(np.int32) - iterator = ( - dataset_ops.Dataset.from_tensor_slices(components) - .map(lambda x: array_ops.fill([x], x)).apply( - batching.dense_to_sparse_batch(4, - [12])).make_initializable_iterator()) - init_op = iterator.initializer - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(init_op) - - for start in range(0, len(components), 4): - results = sess.run(get_next) - self.assertAllEqual([[i, j] - for i, c in enumerate(components[start:start + 4]) - for j in range(c)], results.indices) - self.assertAllEqual( - [c for c in components[start:start + 4] for _ in range(c)], - results.values) - self.assertAllEqual([min(4, - len(components) - start), 12], - results.dense_shape) - - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testDenseToSparseBatchDatasetWithUnknownShape(self): - components = np.random.randint(5, size=(40,)).astype(np.int32) - iterator = ( - dataset_ops.Dataset.from_tensor_slices(components).map( - lambda x: array_ops.fill([x, x], x)).apply( - batching.dense_to_sparse_batch( - 4, [5, None])).make_initializable_iterator()) - init_op = iterator.initializer - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(init_op) - - for start in range(0, len(components), 4): - results = sess.run(get_next) - self.assertAllEqual([[i, j, z] - for i, c in enumerate(components[start:start + 4]) - for j in range(c) - for z in range(c)], results.indices) - self.assertAllEqual([ - c for c in components[start:start + 4] for _ in range(c) - for _ in range(c) - ], results.values) - self.assertAllEqual([ - min(4, - len(components) - start), 5, - np.max(components[start:start + 4]) - ], results.dense_shape) - - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testDenseToSparseBatchDatasetWithInvalidShape(self): - input_tensor = array_ops.constant([[1]]) - with self.assertRaisesRegexp(ValueError, "Dimension -2 must be >= 0"): - dataset_ops.Dataset.from_tensors(input_tensor).apply( - batching.dense_to_sparse_batch(4, - [-2])).make_initializable_iterator() - - def testDenseToSparseBatchDatasetShapeErrors(self): - input_tensor = array_ops.placeholder(dtypes.int32) - iterator = ( - dataset_ops.Dataset.from_tensors(input_tensor).apply( - batching.dense_to_sparse_batch(4, - [12])).make_initializable_iterator()) - init_op = iterator.initializer - get_next = iterator.get_next() - - with self.cached_session() as sess: - # Initialize with an input tensor of incompatible rank. - sess.run(init_op, feed_dict={input_tensor: [[1]]}) - with self.assertRaisesRegexp(errors.InvalidArgumentError, - "incompatible with the row shape"): - sess.run(get_next) - - # Initialize with an input tensor that is larger than `row_shape`. - sess.run(init_op, feed_dict={input_tensor: range(13)}) - with self.assertRaisesRegexp(errors.DataLossError, - "larger than the row shape"): - sess.run(get_next) - - def testUnbatchWithUnknownRankInput(self): - placeholder = array_ops.placeholder(dtypes.int32) - dataset = dataset_ops.Dataset.from_tensors(placeholder).apply( - batching.unbatch()) - iterator = dataset.make_initializable_iterator() - next_elem = iterator.get_next() - - with self.cached_session() as sess: - sess.run(iterator.initializer, feed_dict={placeholder: [0, 1, 2, 3]}) - for i in range(4): - self.assertEqual(i, sess.run(next_elem)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(next_elem) - - def testUnbatchScalarDataset(self): - data = tuple([math_ops.range(10) for _ in range(3)]) - data = dataset_ops.Dataset.from_tensor_slices(data) - expected_types = (dtypes.int32,) * 3 - data = data.batch(2) - self.assertEqual(expected_types, data.output_types) - data = data.apply(batching.unbatch()) - self.assertEqual(expected_types, data.output_types) - - iterator = data.make_one_shot_iterator() - op = iterator.get_next() - - with self.cached_session() as sess: - for i in range(10): - self.assertEqual((i,) * 3, sess.run(op)) - - with self.assertRaises(errors.OutOfRangeError): - sess.run(op) - - def testUnbatchDatasetWithStrings(self): - data = tuple([math_ops.range(10) for _ in range(3)]) - data = dataset_ops.Dataset.from_tensor_slices(data) - data = data.map(lambda x, y, z: (x, string_ops.as_string(y), z)) - expected_types = (dtypes.int32, dtypes.string, dtypes.int32) - data = data.batch(2) - self.assertEqual(expected_types, data.output_types) - data = data.apply(batching.unbatch()) - self.assertEqual(expected_types, data.output_types) - - iterator = data.make_one_shot_iterator() - op = iterator.get_next() - - with self.cached_session() as sess: - for i in range(10): - self.assertEqual((i, compat.as_bytes(str(i)), i), sess.run(op)) - - with self.assertRaises(errors.OutOfRangeError): - sess.run(op) - - def testUnbatchDatasetWithSparseTensor(self): - st = sparse_tensor.SparseTensorValue( - indices=[[i, i] for i in range(10)], - values=list(range(10)), - dense_shape=[10, 10]) - data = dataset_ops.Dataset.from_tensors(st) - data = data.apply(batching.unbatch()) - data = data.batch(5) - data = data.apply(batching.unbatch()) - iterator = data.make_one_shot_iterator() - next_element = iterator.get_next() - - with self.cached_session() as sess: - for i in range(10): - st_row = sess.run(next_element) - self.assertEqual([i], st_row.indices) - self.assertEqual([i], st_row.values) - self.assertEqual([10], st_row.dense_shape) - with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) - - def testUnbatchDatasetWithDenseAndSparseTensor(self): - st = sparse_tensor.SparseTensorValue( - indices=[[i, i] for i in range(10)], - values=list(range(10)), - dense_shape=[10, 10]) - data = dataset_ops.Dataset.from_tensors((list(range(10)), st)) - data = data.apply(batching.unbatch()) - data = data.batch(5) - data = data.apply(batching.unbatch()) - iterator = data.make_one_shot_iterator() - next_element = iterator.get_next() - - with self.cached_session() as sess: - for i in range(10): - dense_elem, st_row = sess.run(next_element) - self.assertEqual(i, dense_elem) - self.assertEqual([i], st_row.indices) - self.assertEqual([i], st_row.values) - self.assertEqual([10], st_row.dense_shape) - with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) - - def testUnbatchSingleElementTupleDataset(self): - data = tuple([(math_ops.range(10),) for _ in range(3)]) - data = dataset_ops.Dataset.from_tensor_slices(data) - expected_types = ((dtypes.int32,),) * 3 - data = data.batch(2) - self.assertEqual(expected_types, data.output_types) - data = data.apply(batching.unbatch()) - self.assertEqual(expected_types, data.output_types) - - iterator = data.make_one_shot_iterator() - op = iterator.get_next() - - with self.cached_session() as sess: - for i in range(10): - self.assertEqual(((i,),) * 3, sess.run(op)) - - with self.assertRaises(errors.OutOfRangeError): - sess.run(op) - - def testUnbatchMultiElementTupleDataset(self): - data = tuple([(math_ops.range(10 * i, 10 * i + 10), - array_ops.fill([10], "hi")) for i in range(3)]) - data = dataset_ops.Dataset.from_tensor_slices(data) - expected_types = ((dtypes.int32, dtypes.string),) * 3 - data = data.batch(2) - self.assertAllEqual(expected_types, data.output_types) - data = data.apply(batching.unbatch()) - self.assertAllEqual(expected_types, data.output_types) - - iterator = data.make_one_shot_iterator() - op = iterator.get_next() - - with self.cached_session() as sess: - for i in range(10): - self.assertEqual(((i, b"hi"), (10 + i, b"hi"), (20 + i, b"hi")), - sess.run(op)) - - with self.assertRaises(errors.OutOfRangeError): - sess.run(op) - - def testUnbatchEmpty(self): - data = dataset_ops.Dataset.from_tensors( - (constant_op.constant([]), constant_op.constant([], shape=[0, 4]), - constant_op.constant([], shape=[0, 4, 0]))) - data = data.apply(batching.unbatch()) - iterator = data.make_one_shot_iterator() - next_element = iterator.get_next() - - with self.cached_session() as sess: - with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) - - def testUnbatchStaticShapeMismatch(self): - data = dataset_ops.Dataset.from_tensors((np.arange(7), np.arange(8), - np.arange(9))) - with self.assertRaises(ValueError): - data.apply(batching.unbatch()) - - def testUnbatchDynamicShapeMismatch(self): - ph1 = array_ops.placeholder(dtypes.int32, shape=[None]) - ph2 = array_ops.placeholder(dtypes.int32, shape=None) - data = dataset_ops.Dataset.from_tensors((ph1, ph2)) - data = data.apply(batching.unbatch()) - iterator = data.make_initializable_iterator() - next_element = iterator.get_next() - - with self.cached_session() as sess: - # Mismatch in the 0th dimension. - sess.run( - iterator.initializer, - feed_dict={ - ph1: np.arange(7).astype(np.int32), - ph2: np.arange(8).astype(np.int32) - }) - with self.assertRaises(errors.InvalidArgumentError): - sess.run(next_element) - - # No 0th dimension (i.e. scalar value) for one component. - sess.run( - iterator.initializer, - feed_dict={ - ph1: np.arange(7).astype(np.int32), - ph2: 7 - }) - with self.assertRaises(errors.InvalidArgumentError): - sess.run(next_element) - - @parameterized.named_parameters( - ("Default", None, None), - ("SequentialCalls", 1, None), - ("ParallelCalls", 2, None), - ("ParallelBatches", None, 10), - ) - def testMapAndBatch(self, num_parallel_calls, num_parallel_batches): - """Test a dataset that maps a TF function across its input elements.""" - # The pipeline is TensorSliceDataset -> - # RepeatDataset(count) -> MapAndBatchDataset(square_3, batch_size). - components = (np.arange(7), - np.array([[1, 2, 3]]) * np.arange(7)[:, np.newaxis], - np.array(37.0) * np.arange(7)) - - count = array_ops.placeholder(dtypes.int64, shape=[]) - batch_size = array_ops.placeholder(dtypes.int64, shape=[]) - - def _map_fn(x, y, z): - return math_ops.square(x), math_ops.square(y), math_ops.square(z) - - iterator = ( - dataset_ops.Dataset.from_tensor_slices(components).repeat(count).apply( - batching.map_and_batch( - map_func=_map_fn, - batch_size=batch_size, - num_parallel_calls=num_parallel_calls, - num_parallel_batches=num_parallel_batches)) - .make_initializable_iterator()) - init_op = iterator.initializer - get_next = iterator.get_next() - - self.assertEqual([[None] + list(c.shape[1:]) for c in components], - [t.shape.as_list() for t in get_next]) - - with self.cached_session() as sess: - # Batch of a finite input, where the batch_size divides the - # total number of elements. - sess.run(init_op, feed_dict={count: 28, batch_size: 14}) - num_batches = (28 * 7) // 14 - for i in range(num_batches): - result = sess.run(get_next) - for component, result_component in zip(components, result): - for j in range(14): - self.assertAllEqual(component[(i * 14 + j) % 7]**2, - result_component[j]) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Batch of a finite input, where the batch_size does not - # divide the total number of elements. - sess.run(init_op, feed_dict={count: 14, batch_size: 8}) - - # We expect (num_batches - 1) full-sized batches. - num_batches = int(math.ceil((14 * 7) / 8)) - for i in range(num_batches - 1): - result = sess.run(get_next) - for component, result_component in zip(components, result): - for j in range(8): - self.assertAllEqual(component[(i * 8 + j) % 7]**2, - result_component[j]) - result = sess.run(get_next) - for component, result_component in zip(components, result): - for j in range((14 * 7) % 8): - self.assertAllEqual(component[((num_batches - 1) * 8 + j) % 7]**2, - result_component[j]) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Batch of an empty input should fail straight away. - sess.run(init_op, feed_dict={count: 0, batch_size: 8}) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Empty batch should be an initialization time error. - with self.assertRaises(errors.InvalidArgumentError): - sess.run(init_op, feed_dict={count: 14, batch_size: 0}) - - @parameterized.named_parameters( - ("Even", False), - ("Uneven", True), - ) - def testMapAndBatchPartialBatch(self, drop_remainder): - iterator = ( - dataset_ops.Dataset.range(10).apply( - batching.map_and_batch( - lambda x: array_ops.reshape(x * x, [1]), - batch_size=4, - drop_remainder=drop_remainder)).make_one_shot_iterator()) - if drop_remainder: - self.assertEqual([4, 1], iterator.output_shapes.as_list()) - else: - self.assertEqual([None, 1], iterator.output_shapes.as_list()) - next_element = iterator.get_next() - with self.cached_session() as sess: - self.assertAllEqual([[0], [1], [4], [9]], sess.run(next_element)) - self.assertAllEqual([[16], [25], [36], [49]], sess.run(next_element)) - if not drop_remainder: - self.assertAllEqual([[64], [81]], sess.run(next_element)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) - - def testMapAndBatchYieldsPartialBatch(self): - iterator = ( - dataset_ops.Dataset.range(10).apply( - batching.map_and_batch(lambda x: array_ops.reshape(x * x, [1]), - 4)).make_one_shot_iterator()) - self.assertEqual([None, 1], iterator.output_shapes.as_list()) - next_element = iterator.get_next() - with self.cached_session() as sess: - self.assertAllEqual([[0], [1], [4], [9]], sess.run(next_element)) - self.assertAllEqual([[16], [25], [36], [49]], sess.run(next_element)) - self.assertAllEqual([[64], [81]], sess.run(next_element)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) - - def testMapAndBatchParallelGetNext(self): - iterator = ( - dataset_ops.Dataset.range(50000).apply( - batching.map_and_batch(lambda x: x, - batch_size=100)).make_one_shot_iterator()) - elements = [] - for _ in range(100): - elements.append(iterator.get_next()) - with self.cached_session() as sess: - for i in range(5): - got = sess.run(elements) - got.sort(key=lambda x: x[0]) - expected = [] - for j in range(100): - expected.append(range(i * 10000 + j * 100, i * 10000 + (j + 1) * 100)) - self.assertAllEqual(got, expected) - with self.assertRaises(errors.OutOfRangeError): - sess.run(elements) - - def testMapAndBatchParallelGetNextDropRemainder(self): - iterator = ( - dataset_ops.Dataset.range(49999).apply( - batching.map_and_batch( - lambda x: x, batch_size=100, - drop_remainder=True)).make_one_shot_iterator()) - elements = [] - for _ in range(100): - elements.append(iterator.get_next()) - with self.cached_session() as sess: - for i in range(4): - got = sess.run(elements) - got.sort(key=lambda x: x[0]) - expected = [] - for j in range(100): - expected.append(range(i * 10000 + j * 100, i * 10000 + (j + 1) * 100)) - self.assertAllEqual(got, expected) - with self.assertRaises(errors.OutOfRangeError): - sess.run(elements) - - def testMapAndBatchSparse(self): - - def _sparse(i): - return sparse_tensor.SparseTensorValue( - indices=[[0]], values=(i * [1]), dense_shape=[1]) - - iterator = dataset_ops.Dataset.range(10).apply( - batching.map_and_batch(_sparse, 5)).make_initializable_iterator() - init_op = iterator.initializer - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(init_op) - for i in range(2): - actual = sess.run(get_next) - expected = sparse_tensor.SparseTensorValue( - indices=[[0, 0], [1, 0], [2, 0], [3, 0], [4, 0]], - values=[i * 5, i * 5 + 1, i * 5 + 2, i * 5 + 3, i * 5 + 4], - dense_shape=[5, 1]) - self.assertTrue(sparse_tensor.is_sparse(actual)) - self.assertSparseValuesEqual(actual, expected) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testMapAndBatchFails(self): - """Test a dataset that maps a TF function across its input elements.""" - dataset = dataset_ops.Dataset.from_tensors( - array_ops.check_numerics( - constant_op.constant(1.0) / constant_op.constant(0.0), "oops")) - batch_size = array_ops.placeholder(dtypes.int64, shape=[]) - iterator = ( - dataset.apply(batching.map_and_batch( - lambda x: x, batch_size)).make_initializable_iterator()) - init_op = iterator.initializer - with self.cached_session() as sess: - with self.assertRaisesRegexp(errors.InvalidArgumentError, "oops"): - sess.run(init_op, feed_dict={batch_size: 14}) - - def testMapAndBatchShapeMismatch(self): - """Test a dataset that maps a TF function across its input elements.""" - - def generator(): - yield [1] - yield [2] - yield [3] - yield [[4, 5, 6]] - - dataset = dataset_ops.Dataset.from_generator( - generator, output_types=dtypes.int32) - batch_size = 4 - iterator = ( - dataset.apply(batching.map_and_batch( - lambda x: x, batch_size)).make_initializable_iterator()) - init_op = iterator.initializer - get_next = iterator.get_next() - with self.cached_session() as sess: - sess.run(init_op) - with self.assertRaisesRegexp(errors.InvalidArgumentError, - "number of elements does not match"): - sess.run(get_next) - - def testMapAndBatchImplicitDispose(self): - # Tests whether a map and batch dataset will be cleaned up correctly when - # the pipeline does not run it until exhaustion. - # The pipeline is TensorSliceDataset -> RepeatDataset(1000) -> - # MapAndBatchDataset(f=square_3, batch_size=100). - components = (np.arange(1000), - np.array([[1, 2, 3]]) * np.arange(1000)[:, np.newaxis], - np.array(37.0) * np.arange(1000)) - - def _map_fn(x, y, z): - return math_ops.square(x), math_ops.square(y), math_ops.square(z) - - dataset = dataset_ops.Dataset.from_tensor_slices(components).repeat( - 1000).apply(batching.map_and_batch(_map_fn, batch_size=100)) - dataset = dataset.prefetch(5) - iterator = dataset.make_one_shot_iterator() - get_next = iterator.get_next() - - with self.cached_session() as sess: - for _ in range(3): - sess.run(get_next) - - @parameterized.named_parameters( - ("1", 0), - ("2", 5), - ("3", 10), - ("4", 90), - ("5", 95), - ("6", 99), - ) - def testMapAndBatchOutOfRangeError(self, threshold): - - def raising_py_fn(i): - if i >= threshold: - raise StopIteration() - else: - return i - - iterator = ( - dataset_ops.Dataset.range(100).apply( - batching.map_and_batch( - lambda x: script_ops.py_func(raising_py_fn, [x], dtypes.int64), - batch_size=10)).make_one_shot_iterator()) - get_next = iterator.get_next() - - with self.cached_session() as sess: - for i in range(threshold // 10): - self.assertAllEqual([i * 10 + j for j in range(10)], sess.run(get_next)) - if threshold % 10 != 0: - self.assertAllEqual( - [threshold // 10 * 10 + j for j in range(threshold % 10)], - sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - @parameterized.named_parameters( - ("1", False, dtypes.bool), - ("2", -42, dtypes.int8), - ("3", -42, dtypes.int16), - ("4", -42, dtypes.int32), - ("5", -42, dtypes.int64), - ("6", 42, dtypes.uint8), - ("7", 42, dtypes.uint16), - ("8", 42.0, dtypes.float16), - ("9", 42.0, dtypes.float32), - ("10", 42.0, dtypes.float64), - ("11", b"hello", dtypes.string), - ) - def testMapAndBatchTypes(self, element, dtype): - - def gen(): - yield element - - dataset = dataset_ops.Dataset.from_generator(gen, dtype).repeat(100).apply( - batching.map_and_batch(lambda x: x, batch_size=10)) - - get_next = dataset.make_one_shot_iterator().get_next() - - with self.cached_session() as sess: - for _ in range(10): - self.assertAllEqual([element for _ in range(10)], sess.run(get_next)) - - -class UnbatchDatasetBenchmark(test.Benchmark): - - def benchmarkNativeUnbatch(self): - batch_sizes = [1, 2, 5, 10, 20, 50] - elems_per_trial = 10000 - with ops.Graph().as_default(): - dataset = dataset_ops.Dataset.from_tensors("element").repeat(None) - batch_size_placeholder = array_ops.placeholder(dtypes.int64, shape=[]) - dataset = dataset.batch(batch_size_placeholder) - dataset = dataset.apply(batching.unbatch()) - dataset = dataset.skip(elems_per_trial) - iterator = dataset.make_initializable_iterator() - next_element = iterator.get_next() - - with session.Session() as sess: - for batch_size in batch_sizes: - deltas = [] - for _ in range(5): - sess.run( - iterator.initializer, - feed_dict={batch_size_placeholder: batch_size}) - start = time.time() - sess.run(next_element.op) - end = time.time() - deltas.append((end - start) / elems_per_trial) - - median_wall_time = np.median(deltas) - print("Unbatch (native) batch size: %d Median wall time per element:" - " %f microseconds" % (batch_size, median_wall_time * 1e6)) - self.report_benchmark( - iters=10000, - wall_time=median_wall_time, - name="benchmark_unbatch_dataset_native_batch_size_%d" % - batch_size) - - # Include a benchmark of the previous `unbatch()` implementation that uses - # a composition of more primitive ops. Eventually we'd hope to generate code - # that is as good in both cases. - def benchmarkOldUnbatchImplementation(self): - batch_sizes = [1, 2, 5, 10, 20, 50] - elems_per_trial = 10000 - with ops.Graph().as_default(): - dataset = dataset_ops.Dataset.from_tensors("element").repeat(None) - batch_size_placeholder = array_ops.placeholder(dtypes.int64, shape=[]) - dataset = dataset.batch(batch_size_placeholder) - dataset = dataset.flat_map(dataset_ops.Dataset.from_tensor_slices) - dataset = dataset.skip(elems_per_trial) - iterator = dataset.make_initializable_iterator() - next_element = iterator.get_next() - - with session.Session() as sess: - for batch_size in batch_sizes: - deltas = [] - for _ in range(5): - sess.run( - iterator.initializer, - feed_dict={batch_size_placeholder: batch_size}) - start = time.time() - sess.run(next_element.op) - end = time.time() - deltas.append((end - start) / elems_per_trial) - - median_wall_time = np.median(deltas) - print("Unbatch (unfused) batch size: %d Median wall time per element:" - " %f microseconds" % (batch_size, median_wall_time * 1e6)) - self.report_benchmark( - iters=10000, - wall_time=median_wall_time, - name="benchmark_unbatch_dataset_unfused_batch_size_%d" % - batch_size) - - -if __name__ == "__main__": - test.main() diff --git a/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py b/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py index 3903ec49b9..8264dee3c1 100644 --- a/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py @@ -105,14 +105,14 @@ class BucketBySequenceLengthTest(test_base.DatasetTestBase): boundaries, batch_sizes, no_padding=no_padding)) - batch, = dataset.make_one_shot_iterator().get_next() + batch, = dataset_ops.make_one_shot_iterator(dataset).get_next() with self.cached_session() as sess: batches = [] for _ in range(4): - batches.append(sess.run(batch)) + batches.append(self.evaluate(batch)) with self.assertRaises(errors.OutOfRangeError): - sess.run(batch) + self.evaluate(batch) batch_sizes_val = [] lengths_val = [] for batch in batches: @@ -155,14 +155,14 @@ class BucketBySequenceLengthTest(test_base.DatasetTestBase): grouping.bucket_by_sequence_length( element_len, boundaries, batch_sizes, pad_to_bucket_boundary=True)) - batch, = dataset.make_one_shot_iterator().get_next() + batch, = dataset_ops.make_one_shot_iterator(dataset).get_next() with self.cached_session() as sess: batches = [] for _ in range(3): - batches.append(sess.run(batch)) + batches.append(self.evaluate(batch)) with self.assertRaisesOpError("bucket_boundaries"): - sess.run(batch) + self.evaluate(batch) batch_sizes_val = [] lengths_val = [] for batch in batches: @@ -192,14 +192,14 @@ class BucketBySequenceLengthTest(test_base.DatasetTestBase): grouping.bucket_by_sequence_length( element_len, boundaries, batch_sizes, pad_to_bucket_boundary=True)) - batch, = dataset.make_one_shot_iterator().get_next() + batch, = dataset_ops.make_one_shot_iterator(dataset).get_next() with self.cached_session() as sess: batches = [] for _ in range(5): - batches.append(sess.run(batch)) + batches.append(self.evaluate(batch)) with self.assertRaises(errors.OutOfRangeError): - sess.run(batch) + self.evaluate(batch) self.assertAllEqual(batches[0], [[1, 0], [1, 1]]) @@ -295,12 +295,12 @@ class BucketBySequenceLengthTest(test_base.DatasetTestBase): def _compute_batches(dataset): """Computes actual batch outputs of dataset and stores in a set.""" - batch = dataset.make_one_shot_iterator().get_next() + batch = dataset_ops.make_one_shot_iterator(dataset).get_next() all_sparse_tensors = set() with self.cached_session() as sess: with self.assertRaises(errors.OutOfRangeError): while True: - output = sess.run(batch) + output = self.evaluate(batch) sprs_tensor = (tuple([tuple(idx) for idx in output.indices]), tuple(output.values)) all_sparse_tensors.add(sprs_tensor) diff --git a/tensorflow/python/data/experimental/kernel_tests/cardinality_test.py b/tensorflow/python/data/experimental/kernel_tests/cardinality_test.py new file mode 100644 index 0000000000..943f0f1f81 --- /dev/null +++ b/tensorflow/python/data/experimental/kernel_tests/cardinality_test.py @@ -0,0 +1,158 @@ +# Copyright 2018 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 `tf.data.experimental.cardinality()`.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from absl.testing import parameterized + +from tensorflow.python.data.experimental.ops import cardinality +from tensorflow.python.data.kernel_tests import test_base +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.platform import test + + +class NumElementsTest(test_base.DatasetTestBase, parameterized.TestCase): + """Tests for `tf.data.experimental.cardinality()`.""" + + @parameterized.named_parameters( + # pylint: disable=g-long-lambda + ("Batch1", + lambda: dataset_ops.Dataset.range(5).batch(2, drop_remainder=True), 2), + ("Batch2", + lambda: dataset_ops.Dataset.range(5).batch(2, drop_remainder=False), 3), + ("Batch3", + lambda: dataset_ops.Dataset.range(5).filter(lambda _: True).batch(2), + cardinality.UNKNOWN), + ("Batch4", lambda: dataset_ops.Dataset.range(5).repeat().batch(2), + cardinality.INFINITE), + ("Cache1", lambda: dataset_ops.Dataset.range(5).cache(), 5), + ("Cache2", lambda: dataset_ops.Dataset.range(5).cache("foo"), 5), + ("Concatenate1", lambda: dataset_ops.Dataset.range(5).concatenate( + dataset_ops.Dataset.range(5)), 10), + ("Concatenate2", + lambda: dataset_ops.Dataset.range(5).filter(lambda _: True).concatenate( + dataset_ops.Dataset.range(5)), cardinality.UNKNOWN), + ("Concatenate3", lambda: dataset_ops.Dataset.range(5).repeat(). + concatenate(dataset_ops.Dataset.range(5)), + cardinality.INFINITE), + ("Concatenate4", lambda: dataset_ops.Dataset.range(5).concatenate( + dataset_ops.Dataset.range(5).filter(lambda _: True)), + cardinality.UNKNOWN), + ("Concatenate5", + lambda: dataset_ops.Dataset.range(5).filter(lambda _: True).concatenate( + dataset_ops.Dataset.range(5).filter(lambda _: True)), + cardinality.UNKNOWN), + ("Concatenate6", lambda: dataset_ops.Dataset.range(5).repeat(). + concatenate(dataset_ops.Dataset.range(5).filter(lambda _: True)), + cardinality.INFINITE), + ("Concatenate7", lambda: dataset_ops.Dataset.range(5).concatenate( + dataset_ops.Dataset.range(5).repeat()), cardinality.INFINITE), + ("Concatenate8", + lambda: dataset_ops.Dataset.range(5).filter(lambda _: True).concatenate( + dataset_ops.Dataset.range(5).repeat()), cardinality.INFINITE), + ("Concatenate9", + lambda: dataset_ops.Dataset.range(5).repeat().concatenate( + dataset_ops.Dataset.range(5).repeat()), cardinality.INFINITE), + ("FlatMap", lambda: dataset_ops.Dataset.range(5).flat_map( + lambda _: dataset_ops.Dataset.from_tensors(0)), + cardinality.UNKNOWN), + ("Filter", lambda: dataset_ops.Dataset.range(5).filter(lambda _: True), + cardinality.UNKNOWN), + ("FromTensors1", lambda: dataset_ops.Dataset.from_tensors(0), 1), + ("FromTensors2", lambda: dataset_ops.Dataset.from_tensors((0, 1)), 1), + ("FromTensorSlices1", + lambda: dataset_ops.Dataset.from_tensor_slices([0, 0, 0]), 3), + ("FromTensorSlices2", + lambda: dataset_ops.Dataset.from_tensor_slices(([0, 0, 0], [1, 1, 1])), + 3), + ("Interleave1", lambda: dataset_ops.Dataset.range(5).interleave( + lambda _: dataset_ops.Dataset.from_tensors(0), cycle_length=1), + cardinality.UNKNOWN), + ("Interleave2", lambda: dataset_ops.Dataset.range(5).interleave( + lambda _: dataset_ops.Dataset.from_tensors(0), + cycle_length=1, + num_parallel_calls=1), cardinality.UNKNOWN), + ("Map1", lambda: dataset_ops.Dataset.range(5).map(lambda x: x), 5), + ("Map2", lambda: dataset_ops.Dataset.range(5).map( + lambda x: x, num_parallel_calls=1), 5), + ("PaddedBatch1", lambda: dataset_ops.Dataset.range(5).padded_batch( + 2, [], drop_remainder=True), 2), + ("PaddedBatch2", lambda: dataset_ops.Dataset.range(5).padded_batch( + 2, [], drop_remainder=False), 3), + ("PaddedBatch3", lambda: dataset_ops.Dataset.range(5).filter( + lambda _: True).padded_batch(2, []), cardinality.UNKNOWN), + ("PaddedBatch4", + lambda: dataset_ops.Dataset.range(5).repeat().padded_batch(2, []), + cardinality.INFINITE), + ("Prefetch", lambda: dataset_ops.Dataset.range(5).prefetch(buffer_size=1), + 5), + ("Range1", lambda: dataset_ops.Dataset.range(0), 0), + ("Range2", lambda: dataset_ops.Dataset.range(5), 5), + ("Range3", lambda: dataset_ops.Dataset.range(5, 10), 5), + ("Range4", lambda: dataset_ops.Dataset.range(10, 5), 0), + ("Range5", lambda: dataset_ops.Dataset.range(5, 10, 2), 3), + ("Range6", lambda: dataset_ops.Dataset.range(10, 5, -2), 3), + ("Repeat1", lambda: dataset_ops.Dataset.range(0).repeat(0), 0), + ("Repeat2", lambda: dataset_ops.Dataset.range(1).repeat(0), 0), + ("Repeat3", lambda: dataset_ops.Dataset.range(0).repeat(5), 0), + ("Repeat4", lambda: dataset_ops.Dataset.range(1).repeat(5), 5), + ("Repeat5", lambda: dataset_ops.Dataset.range(0).repeat(), 0), + ("Repeat6", lambda: dataset_ops.Dataset.range(1).repeat(), + cardinality.INFINITE), + ("Shuffle", lambda: dataset_ops.Dataset.range(5).shuffle(buffer_size=1), + 5), + ("Skip1", lambda: dataset_ops.Dataset.range(5).skip(2), 3), + ("Skip2", lambda: dataset_ops.Dataset.range(5).skip(8), 0), + ("Skip3", + lambda: dataset_ops.Dataset.range(5).filter(lambda _: True).skip(2), + cardinality.UNKNOWN), + ("Skip4", lambda: dataset_ops.Dataset.range(5).repeat().skip(2), + cardinality.INFINITE), + ("Take1", lambda: dataset_ops.Dataset.range(5).take(2), 2), + ("Take2", lambda: dataset_ops.Dataset.range(5).take(8), 5), + ("Take3", + lambda: dataset_ops.Dataset.range(5).filter(lambda _: True).take(2), + cardinality.UNKNOWN), + ("Take4", lambda: dataset_ops.Dataset.range(5).repeat().take(2), 2), + ("Window1", lambda: dataset_ops.Dataset.range(5).window( + size=2, shift=2, drop_remainder=True), 2), + ("Window2", lambda: dataset_ops.Dataset.range(5).window( + size=2, shift=2, drop_remainder=False), 3), + ("Zip1", lambda: dataset_ops.Dataset.zip(dataset_ops.Dataset.range(5)), + 5), + ("Zip2", lambda: dataset_ops.Dataset.zip( + (dataset_ops.Dataset.range(5), dataset_ops.Dataset.range(3))), 3), + ("Zip3", lambda: dataset_ops.Dataset.zip( + (dataset_ops.Dataset.range(5), + dataset_ops.Dataset.range(3).repeat())), 5), + ("Zip4", lambda: dataset_ops.Dataset.zip( + (dataset_ops.Dataset.range(5).repeat(), + dataset_ops.Dataset.range(3).repeat())), cardinality.INFINITE), + ("Zip5", lambda: dataset_ops.Dataset.zip( + (dataset_ops.Dataset.range(5), + dataset_ops.Dataset.range(3).filter(lambda _: True))), + cardinality.UNKNOWN), + # pylint: enable=g-long-lambda + ) + def testNumElements(self, dataset_fn, expected_result): + with self.cached_session() as sess: + self.assertEqual( + sess.run(cardinality.cardinality(dataset_fn())), expected_result) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/data/experimental/kernel_tests/copy_to_device_test.py b/tensorflow/python/data/experimental/kernel_tests/copy_to_device_test.py index cea8bd6f0b..b8166fe833 100644 --- a/tensorflow/python/data/experimental/kernel_tests/copy_to_device_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/copy_to_device_test.py @@ -35,13 +35,14 @@ from tensorflow.python.util import compat as util_compat class CopyToDeviceTest(test_base.DatasetTestBase): + @test_util.run_deprecated_v1 def testCopyToDevice(self): host_dataset = dataset_ops.Dataset.range(10) device_dataset = host_dataset.apply( prefetching_ops.copy_to_device("/cpu:1")) with ops.device("/cpu:1"): - iterator = device_dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(device_dataset) next_element = iterator.get_next() self.assertEqual(host_dataset.output_types, device_dataset.output_types) @@ -55,19 +56,20 @@ class CopyToDeviceTest(test_base.DatasetTestBase): self.assertEqual([], next_element.shape) worker_config = config_pb2.ConfigProto(device_count={"CPU": 2}) - with self.test_session(config=worker_config) as sess: + with self.test_session(config=worker_config): for i in range(10): - self.assertEqual(i, sess.run(next_element)) + self.assertEqual(i, self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) + @test_util.run_deprecated_v1 def testCopyToDeviceInt32(self): host_dataset = dataset_ops.Dataset.from_tensors([0, 1, 2, 3]) device_dataset = host_dataset.apply( prefetching_ops.copy_to_device("/cpu:1")) with ops.device("/cpu:1"): - iterator = device_dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(device_dataset) next_element = iterator.get_next() self.assertEqual(host_dataset.output_types, device_dataset.output_types) @@ -81,18 +83,19 @@ class CopyToDeviceTest(test_base.DatasetTestBase): self.assertEqual((4,), next_element.shape) worker_config = config_pb2.ConfigProto(device_count={"CPU": 2}) - with self.test_session(config=worker_config) as sess: - self.assertAllEqual([0, 1, 2, 3], sess.run(next_element)) + with self.test_session(config=worker_config): + self.assertAllEqual([0, 1, 2, 3], self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) + @test_util.run_deprecated_v1 def testCopyToSameDevice(self): host_dataset = dataset_ops.Dataset.range(10) device_dataset = host_dataset.apply( prefetching_ops.copy_to_device("/cpu:0")) with ops.device("/cpu:0"): - iterator = device_dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(device_dataset) next_element = iterator.get_next() self.assertEqual(host_dataset.output_types, device_dataset.output_types) @@ -106,19 +109,20 @@ class CopyToDeviceTest(test_base.DatasetTestBase): self.assertEqual([], next_element.shape) worker_config = config_pb2.ConfigProto(device_count={"CPU": 2}) - with self.test_session(config=worker_config) as sess: + with self.test_session(config=worker_config): for i in range(10): - self.assertEqual(i, sess.run(next_element)) + self.assertEqual(i, self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) + @test_util.run_deprecated_v1 def testCopyToDeviceWithPrefetch(self): host_dataset = dataset_ops.Dataset.range(10) device_dataset = host_dataset.apply( prefetching_ops.copy_to_device("/cpu:1")).prefetch(1) with ops.device("/cpu:1"): - iterator = device_dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(device_dataset) next_element = iterator.get_next() self.assertEqual(host_dataset.output_types, device_dataset.output_types) @@ -132,19 +136,20 @@ class CopyToDeviceTest(test_base.DatasetTestBase): self.assertEqual([], next_element.shape) worker_config = config_pb2.ConfigProto(device_count={"CPU": 2}) - with self.test_session(config=worker_config) as sess: + with self.test_session(config=worker_config): for i in range(10): - self.assertEqual(i, sess.run(next_element)) + self.assertEqual(i, self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) + @test_util.run_deprecated_v1 def testCopyDictToDevice(self): host_dataset = dataset_ops.Dataset.range(10).map(lambda x: {"a": x}) device_dataset = host_dataset.apply( prefetching_ops.copy_to_device("/cpu:1")) with ops.device("/cpu:1"): - iterator = device_dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(device_dataset) next_element = iterator.get_next() self.assertEqual(host_dataset.output_types, device_dataset.output_types) @@ -158,19 +163,20 @@ class CopyToDeviceTest(test_base.DatasetTestBase): self.assertEqual([], next_element["a"].shape) worker_config = config_pb2.ConfigProto(device_count={"CPU": 2}) - with self.test_session(config=worker_config) as sess: + with self.test_session(config=worker_config): for i in range(10): - self.assertEqual({"a": i}, sess.run(next_element)) + self.assertEqual({"a": i}, self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) + @test_util.run_deprecated_v1 def testCopyDictToDeviceWithPrefetch(self): host_dataset = dataset_ops.Dataset.range(10).map(lambda x: {"a": x}) device_dataset = host_dataset.apply( prefetching_ops.copy_to_device("/cpu:1")).prefetch(1) with ops.device("/cpu:1"): - iterator = device_dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(device_dataset) next_element = iterator.get_next() self.assertEqual(host_dataset.output_types, device_dataset.output_types) @@ -184,12 +190,13 @@ class CopyToDeviceTest(test_base.DatasetTestBase): self.assertEqual([], next_element["a"].shape) worker_config = config_pb2.ConfigProto(device_count={"CPU": 2}) - with self.test_session(config=worker_config) as sess: + with self.test_session(config=worker_config): for i in range(10): - self.assertEqual({"a": i}, sess.run(next_element)) + self.assertEqual({"a": i}, self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) + @test_util.run_deprecated_v1 def testCopySparseTensorsToDevice(self): def make_tensor(i): @@ -202,7 +209,7 @@ class CopyToDeviceTest(test_base.DatasetTestBase): prefetching_ops.copy_to_device("/cpu:1")) with ops.device("/cpu:1"): - iterator = device_dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(device_dataset) next_element = iterator.get_next() self.assertEqual(host_dataset.output_types, device_dataset.output_types) @@ -215,15 +222,16 @@ class CopyToDeviceTest(test_base.DatasetTestBase): self.assertEqual(dtypes.int64, next_element.dtype) worker_config = config_pb2.ConfigProto(device_count={"CPU": 2}) - with self.test_session(config=worker_config) as sess: + with self.test_session(config=worker_config): for i in range(10): - actual = sess.run(next_element) + actual = self.evaluate(next_element) self.assertAllEqual([i], actual.values) self.assertAllEqual([[0, 0]], actual.indices) self.assertAllEqual([2, 2], actual.dense_shape) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) + @test_util.run_deprecated_v1 def testCopySparseTensorsToDeviceWithPrefetch(self): def make_tensor(i): @@ -236,7 +244,7 @@ class CopyToDeviceTest(test_base.DatasetTestBase): prefetching_ops.copy_to_device("/cpu:1")).prefetch(1) with ops.device("/cpu:1"): - iterator = device_dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(device_dataset) next_element = iterator.get_next() self.assertEqual(host_dataset.output_types, device_dataset.output_types) @@ -249,14 +257,14 @@ class CopyToDeviceTest(test_base.DatasetTestBase): self.assertEqual(dtypes.int64, next_element.dtype) worker_config = config_pb2.ConfigProto(device_count={"CPU": 2}) - with self.test_session(config=worker_config) as sess: + with self.test_session(config=worker_config): for i in range(10): - actual = sess.run(next_element) + actual = self.evaluate(next_element) self.assertAllEqual([i], actual.values) self.assertAllEqual([[0, 0]], actual.indices) self.assertAllEqual([2, 2], actual.dense_shape) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) def testCopyToDeviceGpu(self): if not test_util.is_gpu_available(): @@ -267,15 +275,16 @@ class CopyToDeviceTest(test_base.DatasetTestBase): prefetching_ops.copy_to_device("/gpu:0")) with ops.device("/gpu:0"): - iterator = device_dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(device_dataset) next_element = iterator.get_next() - with self.cached_session() as sess: - sess.run(iterator.initializer) + with self.cached_session( + config=config_pb2.ConfigProto(allow_soft_placement=False)): + self.evaluate(iterator.initializer) for i in range(10): - self.assertEqual(i, sess.run(next_element)) + self.assertEqual(i, self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) def testCopyToDeviceGpuWithPrefetch(self): if not test_util.is_gpu_available(): @@ -286,15 +295,16 @@ class CopyToDeviceTest(test_base.DatasetTestBase): prefetching_ops.copy_to_device("/gpu:0")).prefetch(1) with ops.device("/gpu:0"): - iterator = device_dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(device_dataset) next_element = iterator.get_next() - with self.cached_session() as sess: - sess.run(iterator.initializer) + with self.cached_session( + config=config_pb2.ConfigProto(allow_soft_placement=False)): + self.evaluate(iterator.initializer) for i in range(10): - self.assertEqual(i, sess.run(next_element)) + self.assertEqual(i, self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) def testCopyToDeviceGpuWithMap(self): if not test_util.is_gpu_available(): @@ -319,18 +329,19 @@ class CopyToDeviceTest(test_base.DatasetTestBase): device_dataset = device_dataset.with_options(options) with ops.device("/gpu:0"): - iterator = device_dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(device_dataset) next_element = iterator.get_next() - with self.cached_session() as sess: - sess.run(iterator.initializer) + with self.cached_session( + config=config_pb2.ConfigProto(allow_soft_placement=False)): + self.evaluate(iterator.initializer) for i in range(10): - x, y, z = sess.run(next_element) + x, y, z = self.evaluate(next_element) self.assertEqual(i**2, x) self.assertEqual(float(i**2), y) self.assertEqual(util_compat.as_bytes(str(i)), z) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) def testCopyToDeviceGpuInt32(self): if not test_util.is_gpu_available(): @@ -341,14 +352,15 @@ class CopyToDeviceTest(test_base.DatasetTestBase): prefetching_ops.copy_to_device("/gpu:0")) with ops.device("/gpu:0"): - iterator = device_dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(device_dataset) next_element = iterator.get_next() - with self.cached_session() as sess: - sess.run(iterator.initializer) - self.assertAllEqual([0, 1, 2, 3], sess.run(next_element)) + with self.cached_session( + config=config_pb2.ConfigProto(allow_soft_placement=False)): + self.evaluate(iterator.initializer) + self.assertAllEqual([0, 1, 2, 3], self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) def testCopyToDeviceGpuInt32AndPrefetch(self): if not test_util.is_gpu_available(): @@ -359,14 +371,15 @@ class CopyToDeviceTest(test_base.DatasetTestBase): prefetching_ops.copy_to_device("/gpu:0")).prefetch(1) with ops.device("/gpu:0"): - iterator = device_dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(device_dataset) next_element = iterator.get_next() - with self.cached_session() as sess: - sess.run(iterator.initializer) - self.assertAllEqual([0, 1, 2, 3], sess.run(next_element)) + with self.cached_session( + config=config_pb2.ConfigProto(allow_soft_placement=False)): + self.evaluate(iterator.initializer) + self.assertAllEqual([0, 1, 2, 3], self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) def testCopyToDeviceGpuStrings(self): if not test_util.is_gpu_available(): @@ -377,14 +390,15 @@ class CopyToDeviceTest(test_base.DatasetTestBase): prefetching_ops.copy_to_device("/gpu:0")) with ops.device("/gpu:0"): - iterator = device_dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(device_dataset) next_element = iterator.get_next() - with self.cached_session() as sess: - sess.run(iterator.initializer) - self.assertAllEqual([b"a", b"b", b"c"], sess.run(next_element)) + with self.cached_session( + config=config_pb2.ConfigProto(allow_soft_placement=False)): + self.evaluate(iterator.initializer) + self.assertAllEqual([b"a", b"b", b"c"], self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) def testCopyToDeviceGpuStringsAndPrefetch(self): if not test_util.is_gpu_available(): @@ -395,14 +409,15 @@ class CopyToDeviceTest(test_base.DatasetTestBase): prefetching_ops.copy_to_device("/gpu:0")) with ops.device("/gpu:0"): - iterator = device_dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(device_dataset) next_element = iterator.get_next() - with self.cached_session() as sess: - sess.run(iterator.initializer) - self.assertAllEqual([b"a", b"b", b"c"], sess.run(next_element)) + with self.cached_session( + config=config_pb2.ConfigProto(allow_soft_placement=False)): + self.evaluate(iterator.initializer) + self.assertAllEqual([b"a", b"b", b"c"], self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) def testCopyToDevicePingPongCPUGPU(self): if not test_util.is_gpu_available(): @@ -416,23 +431,25 @@ class CopyToDeviceTest(test_base.DatasetTestBase): prefetching_ops.copy_to_device("/cpu:0", source_device="/gpu:0")) with ops.device("/cpu:0"): - iterator = back_to_cpu_dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(back_to_cpu_dataset) next_element = iterator.get_next() - with self.cached_session() as sess: - sess.run(iterator.initializer) + with self.cached_session( + config=config_pb2.ConfigProto(allow_soft_placement=False)): + self.evaluate(iterator.initializer) for i in range(10): - self.assertEqual(i, sess.run(next_element)) + self.assertEqual(i, self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) + @test_util.run_deprecated_v1 def testCopyToDeviceWithReInit(self): host_dataset = dataset_ops.Dataset.range(10) device_dataset = host_dataset.apply( prefetching_ops.copy_to_device("/cpu:1")) with ops.device("/cpu:1"): - iterator = device_dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(device_dataset) next_element = iterator.get_next() self.assertEqual(host_dataset.output_types, device_dataset.output_types) @@ -446,23 +463,24 @@ class CopyToDeviceTest(test_base.DatasetTestBase): self.assertEqual([], next_element.shape) worker_config = config_pb2.ConfigProto(device_count={"CPU": 2}) - with self.test_session(config=worker_config) as sess: - sess.run(iterator.initializer) + with self.test_session(config=worker_config): + self.evaluate(iterator.initializer) for i in range(5): - self.assertEqual(i, sess.run(next_element)) - sess.run(iterator.initializer) + self.assertEqual(i, self.evaluate(next_element)) + self.evaluate(iterator.initializer) for i in range(10): - self.assertEqual(i, sess.run(next_element)) + self.assertEqual(i, self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) + @test_util.run_deprecated_v1 def testCopyToDeviceWithReInitAndPrefetch(self): host_dataset = dataset_ops.Dataset.range(10) device_dataset = host_dataset.apply( prefetching_ops.copy_to_device("/cpu:1")).prefetch(1) with ops.device("/cpu:1"): - iterator = device_dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(device_dataset) next_element = iterator.get_next() self.assertEqual(host_dataset.output_types, device_dataset.output_types) @@ -476,15 +494,15 @@ class CopyToDeviceTest(test_base.DatasetTestBase): self.assertEqual([], next_element.shape) worker_config = config_pb2.ConfigProto(device_count={"CPU": 2}) - with self.test_session(config=worker_config) as sess: - sess.run(iterator.initializer) + with self.test_session(config=worker_config): + self.evaluate(iterator.initializer) for i in range(5): - self.assertEqual(i, sess.run(next_element)) - sess.run(iterator.initializer) + self.assertEqual(i, self.evaluate(next_element)) + self.evaluate(iterator.initializer) for i in range(10): - self.assertEqual(i, sess.run(next_element)) + self.assertEqual(i, self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) def testCopyToDeviceGpuWithReInit(self): if not test_util.is_gpu_available(): @@ -495,18 +513,19 @@ class CopyToDeviceTest(test_base.DatasetTestBase): prefetching_ops.copy_to_device("/gpu:0")) with ops.device("/gpu:0"): - iterator = device_dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(device_dataset) next_element = iterator.get_next() - with self.cached_session() as sess: - sess.run(iterator.initializer) + with self.cached_session( + config=config_pb2.ConfigProto(allow_soft_placement=False)): + self.evaluate(iterator.initializer) for i in range(5): - self.assertEqual(i, sess.run(next_element)) - sess.run(iterator.initializer) + self.assertEqual(i, self.evaluate(next_element)) + self.evaluate(iterator.initializer) for i in range(10): - self.assertEqual(i, sess.run(next_element)) + self.assertEqual(i, self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) def testCopyToDeviceGpuWithReInitAndPrefetch(self): if not test_util.is_gpu_available(): @@ -517,18 +536,19 @@ class CopyToDeviceTest(test_base.DatasetTestBase): prefetching_ops.copy_to_device("/gpu:0")).prefetch(1) with ops.device("/gpu:0"): - iterator = device_dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(device_dataset) next_element = iterator.get_next() - with self.cached_session() as sess: - sess.run(iterator.initializer) + with self.cached_session( + config=config_pb2.ConfigProto(allow_soft_placement=False)): + self.evaluate(iterator.initializer) for i in range(5): - self.assertEqual(i, sess.run(next_element)) - sess.run(iterator.initializer) + self.assertEqual(i, self.evaluate(next_element)) + self.evaluate(iterator.initializer) for i in range(10): - self.assertEqual(i, sess.run(next_element)) + self.assertEqual(i, self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) def testIteratorGetNextAsOptionalOnGPU(self): if not test_util.is_gpu_available(): @@ -538,33 +558,35 @@ class CopyToDeviceTest(test_base.DatasetTestBase): device_dataset = host_dataset.apply( prefetching_ops.copy_to_device("/gpu:0")) with ops.device("/gpu:0"): - iterator = device_dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(device_dataset) next_elem = iterator_ops.get_next_as_optional(iterator) elem_has_value_t = next_elem.has_value() elem_value_t = next_elem.get_value() - with self.cached_session() as sess: + with self.cached_session( + config=config_pb2.ConfigProto(allow_soft_placement=False)): # Before initializing the iterator, evaluating the optional fails with # a FailedPreconditionError. with self.assertRaises(errors.FailedPreconditionError): - sess.run(elem_has_value_t) + self.evaluate(elem_has_value_t) with self.assertRaises(errors.FailedPreconditionError): - sess.run(elem_value_t) + self.evaluate(elem_value_t) # For each element of the dataset, assert that the optional evaluates to # the expected value. - sess.run(iterator.initializer) + self.evaluate(iterator.initializer) for i in range(3): - elem_has_value, elem_value = sess.run([elem_has_value_t, elem_value_t]) + elem_has_value, elem_value = self.evaluate( + [elem_has_value_t, elem_value_t]) self.assertTrue(elem_has_value) self.assertEqual(i, elem_value) # After exhausting the iterator, `next_elem.has_value()` will evaluate to # false, and attempting to get the value will fail. for _ in range(2): - self.assertFalse(sess.run(elem_has_value_t)) + self.assertFalse(self.evaluate(elem_has_value_t)) with self.assertRaises(errors.InvalidArgumentError): - sess.run(elem_value_t) + self.evaluate(elem_value_t) if __name__ == "__main__": diff --git a/tensorflow/python/data/experimental/kernel_tests/counter_test.py b/tensorflow/python/data/experimental/kernel_tests/counter_test.py index 4e114ac479..49e1f2272b 100644 --- a/tensorflow/python/data/experimental/kernel_tests/counter_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/counter_test.py @@ -19,32 +19,35 @@ from __future__ import print_function from tensorflow.python.data.experimental.ops import counter from tensorflow.python.data.kernel_tests import test_base +from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.platform import test class CounterTest(test_base.DatasetTestBase): + @test_util.run_deprecated_v1 def testCounter(self): """Test dataset construction using `count`.""" - iterator = (counter.Counter(start=3, step=4) - .make_one_shot_iterator()) + iterator = dataset_ops.make_one_shot_iterator( + counter.Counter(start=3, step=4)) get_next = iterator.get_next() self.assertEqual([], get_next.shape.as_list()) self.assertEqual(dtypes.int64, get_next.dtype) - negative_iterator = (counter.Counter(start=0, step=-1) - .make_one_shot_iterator()) + negative_iterator = dataset_ops.make_one_shot_iterator( + counter.Counter(start=0, step=-1)) negative_get_next = negative_iterator.get_next() with self.cached_session() as sess: - self.assertEqual(3, sess.run(get_next)) - self.assertEqual(3 + 4, sess.run(get_next)) - self.assertEqual(3 + 2 * 4, sess.run(get_next)) + self.assertEqual(3, self.evaluate(get_next)) + self.assertEqual(3 + 4, self.evaluate(get_next)) + self.assertEqual(3 + 2 * 4, self.evaluate(get_next)) - self.assertEqual(0, sess.run(negative_get_next)) - self.assertEqual(-1, sess.run(negative_get_next)) - self.assertEqual(-2, sess.run(negative_get_next)) + self.assertEqual(0, self.evaluate(negative_get_next)) + self.assertEqual(-1, self.evaluate(negative_get_next)) + self.assertEqual(-2, self.evaluate(negative_get_next)) if __name__ == "__main__": diff --git a/tensorflow/python/data/experimental/kernel_tests/csv_dataset_test.py b/tensorflow/python/data/experimental/kernel_tests/csv_dataset_test.py index fb75be1fbc..b2f1b43ecf 100644 --- a/tensorflow/python/data/experimental/kernel_tests/csv_dataset_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/csv_dataset_test.py @@ -20,14 +20,8 @@ from __future__ import print_function import gzip import os -import string -import tempfile -import time import zlib -import numpy as np - -from tensorflow.python.client import session from tensorflow.python.data.experimental.ops import error_ops from tensorflow.python.data.experimental.ops import readers from tensorflow.python.data.kernel_tests import test_base @@ -38,8 +32,6 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors from tensorflow.python.framework import test_util from tensorflow.python.ops import parsing_ops -from tensorflow.python.platform import gfile -from tensorflow.python.platform import googletest from tensorflow.python.platform import test @@ -537,96 +529,5 @@ class CsvDatasetTest(test_base.DatasetTestBase): record_defaults=record_defaults) -class CsvDatasetBenchmark(test.Benchmark): - """Benchmarks for the various ways of creating a dataset from CSV files. - """ - FLOAT_VAL = '1.23456E12' - STR_VAL = string.ascii_letters * 10 - - def _setUp(self, str_val): - # Since this isn't test.TestCase, have to manually create a test dir - gfile.MakeDirs(googletest.GetTempDir()) - self._temp_dir = tempfile.mkdtemp(dir=googletest.GetTempDir()) - - self._num_cols = [4, 64, 256] - self._num_per_iter = 5000 - self._filenames = [] - for n in self._num_cols: - fn = os.path.join(self._temp_dir, 'file%d.csv' % n) - with open(fn, 'wb') as f: - # Just write 100 rows and use `repeat`... Assumes the cost - # of creating an iterator is not significant - row = ','.join([str_val for _ in range(n)]) - f.write('\n'.join([row for _ in range(100)])) - self._filenames.append(fn) - - def _tearDown(self): - gfile.DeleteRecursively(self._temp_dir) - - def _runBenchmark(self, dataset, num_cols, prefix): - dataset = dataset.skip(self._num_per_iter - 1) - deltas = [] - for _ in range(10): - next_element = dataset.make_one_shot_iterator().get_next() - with session.Session() as sess: - start = time.time() - # NOTE: This depends on the underlying implementation of skip, to have - # the net effect of calling `GetNext` num_per_iter times on the - # input dataset. We do it this way (instead of a python for loop, or - # batching N inputs in one iter) so that the overhead from session.run - # or batch doesn't dominate. If we eventually optimize skip, this has - # to change. - sess.run(next_element) - end = time.time() - deltas.append(end - start) - # Median wall time per CSV record read and decoded - median_wall_time = np.median(deltas) / self._num_per_iter - print('%s num_cols: %d Median wall time: %f' % (prefix, num_cols, - median_wall_time)) - self.report_benchmark( - iters=self._num_per_iter, - wall_time=median_wall_time, - name='%s_with_cols_%d' % (prefix, num_cols)) - - def benchmarkMapWithFloats(self): - self._setUp(self.FLOAT_VAL) - for i in range(len(self._filenames)): - num_cols = self._num_cols[i] - kwargs = {'record_defaults': [[0.0]] * num_cols} - dataset = core_readers.TextLineDataset(self._filenames[i]).repeat() - dataset = dataset.map(lambda l: parsing_ops.decode_csv(l, **kwargs)) # pylint: disable=cell-var-from-loop - self._runBenchmark(dataset, num_cols, 'csv_float_map_decode_csv') - self._tearDown() - - def benchmarkMapWithStrings(self): - self._setUp(self.STR_VAL) - for i in range(len(self._filenames)): - num_cols = self._num_cols[i] - kwargs = {'record_defaults': [['']] * num_cols} - dataset = core_readers.TextLineDataset(self._filenames[i]).repeat() - dataset = dataset.map(lambda l: parsing_ops.decode_csv(l, **kwargs)) # pylint: disable=cell-var-from-loop - self._runBenchmark(dataset, num_cols, 'csv_strings_map_decode_csv') - self._tearDown() - - def benchmarkCsvDatasetWithFloats(self): - self._setUp(self.FLOAT_VAL) - for i in range(len(self._filenames)): - num_cols = self._num_cols[i] - kwargs = {'record_defaults': [[0.0]] * num_cols} - dataset = core_readers.TextLineDataset(self._filenames[i]).repeat() - dataset = readers.CsvDataset(self._filenames[i], **kwargs).repeat() # pylint: disable=cell-var-from-loop - self._runBenchmark(dataset, num_cols, 'csv_float_fused_dataset') - self._tearDown() - - def benchmarkCsvDatasetWithStrings(self): - self._setUp(self.STR_VAL) - for i in range(len(self._filenames)): - num_cols = self._num_cols[i] - kwargs = {'record_defaults': [['']] * num_cols} - dataset = core_readers.TextLineDataset(self._filenames[i]).repeat() - dataset = readers.CsvDataset(self._filenames[i], **kwargs).repeat() # pylint: disable=cell-var-from-loop - self._runBenchmark(dataset, num_cols, 'csv_strings_fused_dataset') - self._tearDown() - if __name__ == '__main__': test.main() diff --git a/tensorflow/python/data/experimental/kernel_tests/dense_to_sparse_batch_test.py b/tensorflow/python/data/experimental/kernel_tests/dense_to_sparse_batch_test.py index 73be6cbcca..22e057a284 100644 --- a/tensorflow/python/data/experimental/kernel_tests/dense_to_sparse_batch_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/dense_to_sparse_batch_test.py @@ -24,27 +24,28 @@ from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.platform import test class DenseToSparseBatchTest(test_base.DatasetTestBase): + @test_util.run_deprecated_v1 def testDenseToSparseBatchDataset(self): components = np.random.randint(12, size=(100,)).astype(np.int32) - iterator = ( + iterator = dataset_ops.make_initializable_iterator( dataset_ops.Dataset.from_tensor_slices(components) .map(lambda x: array_ops.fill([x], x)).apply( - batching.dense_to_sparse_batch(4, [12])) - .make_initializable_iterator()) + batching.dense_to_sparse_batch(4, [12]))) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: - sess.run(init_op) + self.evaluate(init_op) for start in range(0, len(components), 4): - results = sess.run(get_next) + results = self.evaluate(get_next) self.assertAllEqual([[i, j] for i, c in enumerate(components[start:start + 4]) for j in range(c)], results.indices) @@ -56,23 +57,23 @@ class DenseToSparseBatchTest(test_base.DatasetTestBase): results.dense_shape) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) + @test_util.run_deprecated_v1 def testDenseToSparseBatchDatasetWithUnknownShape(self): components = np.random.randint(5, size=(40,)).astype(np.int32) - iterator = ( + iterator = dataset_ops.make_initializable_iterator( dataset_ops.Dataset.from_tensor_slices(components) .map(lambda x: array_ops.fill([x, x], x)).apply( - batching.dense_to_sparse_batch( - 4, [5, None])).make_initializable_iterator()) + batching.dense_to_sparse_batch(4, [5, None]))) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: - sess.run(init_op) + self.evaluate(init_op) for start in range(0, len(components), 4): - results = sess.run(get_next) + results = self.evaluate(get_next) self.assertAllEqual([[i, j, z] for i, c in enumerate(components[start:start + 4]) for j in range(c) @@ -89,20 +90,22 @@ class DenseToSparseBatchTest(test_base.DatasetTestBase): ], results.dense_shape) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) + @test_util.run_deprecated_v1 def testDenseToSparseBatchDatasetWithInvalidShape(self): input_tensor = array_ops.constant([[1]]) with self.assertRaisesRegexp(ValueError, "Dimension -2 must be >= 0"): - dataset_ops.Dataset.from_tensors(input_tensor).apply( - batching.dense_to_sparse_batch(4, [-2])).make_initializable_iterator() + dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.from_tensors(input_tensor).apply( + batching.dense_to_sparse_batch(4, [-2]))) + @test_util.run_deprecated_v1 def testDenseToSparseBatchDatasetShapeErrors(self): input_tensor = array_ops.placeholder(dtypes.int32) - iterator = ( + iterator = dataset_ops.make_initializable_iterator( dataset_ops.Dataset.from_tensors(input_tensor).apply( - batching.dense_to_sparse_batch(4, [12])) - .make_initializable_iterator()) + batching.dense_to_sparse_batch(4, [12]))) init_op = iterator.initializer get_next = iterator.get_next() @@ -111,13 +114,13 @@ class DenseToSparseBatchTest(test_base.DatasetTestBase): sess.run(init_op, feed_dict={input_tensor: [[1]]}) with self.assertRaisesRegexp(errors.InvalidArgumentError, "incompatible with the row shape"): - sess.run(get_next) + self.evaluate(get_next) # Initialize with an input tensor that is larger than `row_shape`. sess.run(init_op, feed_dict={input_tensor: range(13)}) with self.assertRaisesRegexp(errors.DataLossError, "larger than the row shape"): - sess.run(get_next) + self.evaluate(get_next) if __name__ == "__main__": diff --git a/tensorflow/python/data/experimental/kernel_tests/directed_interleave_dataset_test.py b/tensorflow/python/data/experimental/kernel_tests/directed_interleave_dataset_test.py index 796a692c56..2144342066 100644 --- a/tensorflow/python/data/experimental/kernel_tests/directed_interleave_dataset_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/directed_interleave_dataset_test.py @@ -24,11 +24,13 @@ from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import errors from tensorflow.python.framework import random_seed +from tensorflow.python.framework import test_util from tensorflow.python.platform import test class DirectedInterleaveDatasetTest(test_base.DatasetTestBase): + @test_util.run_deprecated_v1 def testBasic(self): selector_dataset = dataset_ops.Dataset.range(10).repeat(100) input_datasets = [ @@ -36,16 +38,16 @@ class DirectedInterleaveDatasetTest(test_base.DatasetTestBase): ] dataset = interleave_ops._DirectedInterleaveDataset(selector_dataset, input_datasets) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) next_element = iterator.get_next() with self.cached_session() as sess: - sess.run(iterator.initializer) + self.evaluate(iterator.initializer) for _ in range(100): for i in range(10): - self.assertEqual(i, sess.run(next_element)) + self.assertEqual(i, self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) def _normalize(self, vec): return vec / vec.sum() @@ -65,18 +67,19 @@ class DirectedInterleaveDatasetTest(test_base.DatasetTestBase): for i in range(num_datasets) ], weights) dataset = dataset.take(num_samples) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) next_element = iterator.get_next() with self.cached_session() as sess: freqs = np.zeros([num_datasets]) for _ in range(num_samples): - freqs[sess.run(next_element)] += 1 + freqs[self.evaluate(next_element)] += 1 with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) return freqs + @test_util.run_deprecated_v1 def testSampleFromDatasets(self): random_seed.set_random_seed(1619) num_samples = 5000 @@ -96,20 +99,21 @@ class DirectedInterleaveDatasetTest(test_base.DatasetTestBase): freqs = self._testSampleFromDatasetsHelper(probs_ds, classes, num_samples) self.assertLess(self._chi2(probs, freqs / num_samples), 1e-2) + @test_util.run_deprecated_v1 def testSelectFromDatasets(self): words = [b"foo", b"bar", b"baz"] datasets = [dataset_ops.Dataset.from_tensors(w).repeat() for w in words] choice_array = np.random.randint(3, size=(15,), dtype=np.int64) choice_dataset = dataset_ops.Dataset.from_tensor_slices(choice_array) dataset = interleave_ops.choose_from_datasets(datasets, choice_dataset) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) next_element = iterator.get_next() with self.cached_session() as sess: for i in choice_array: - self.assertEqual(words[i], sess.run(next_element)) + self.assertEqual(words[i], self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) def testErrors(self): with self.assertRaisesRegexp(ValueError, diff --git a/tensorflow/python/data/experimental/kernel_tests/enumerate_dataset_test.py b/tensorflow/python/data/experimental/kernel_tests/enumerate_dataset_test.py index e54235d9f8..25742098f1 100644 --- a/tensorflow/python/data/experimental/kernel_tests/enumerate_dataset_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/enumerate_dataset_test.py @@ -24,17 +24,20 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util from tensorflow.python.platform import test class EnumerateDatasetTest(test_base.DatasetTestBase): + @test_util.run_deprecated_v1 def testEnumerateDataset(self): components = (["a", "b"], [1, 2], [37.0, 38]) start = constant_op.constant(20, dtype=dtypes.int64) - iterator = (dataset_ops.Dataset.from_tensor_slices(components).apply( - enumerate_ops.enumerate_dataset(start)).make_initializable_iterator()) + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.from_tensor_slices(components).apply( + enumerate_ops.enumerate_dataset(start))) init_op = iterator.initializer get_next = iterator.get_next() @@ -44,12 +47,12 @@ class EnumerateDatasetTest(test_base.DatasetTestBase): [t.shape for t in get_next[1]]) with self.cached_session() as sess: - sess.run(init_op) - self.assertEqual((20, (b"a", 1, 37.0)), sess.run(get_next)) - self.assertEqual((21, (b"b", 2, 38.0)), sess.run(get_next)) + self.evaluate(init_op) + self.assertEqual((20, (b"a", 1, 37.0)), self.evaluate(get_next)) + self.assertEqual((21, (b"b", 2, 38.0)), self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) if __name__ == "__main__": diff --git a/tensorflow/python/data/experimental/kernel_tests/filter_dataset_op_test.py b/tensorflow/python/data/experimental/kernel_tests/filter_dataset_op_test.py index c6ee88c676..357b5f1b49 100644 --- a/tensorflow/python/data/experimental/kernel_tests/filter_dataset_op_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/filter_dataset_op_test.py @@ -47,17 +47,17 @@ class FilterBenchmark(test.Benchmark): if optimize_dataset: dataset = dataset.apply(optimization.optimize(["filter_fusion"])) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) next_element = iterator.get_next() with session.Session() as sess: for _ in range(10): - sess.run(next_element.op) + self.evaluate(next_element.op) deltas = [] for _ in range(100): start = time.time() for _ in range(100): - sess.run(next_element.op) + self.evaluate(next_element.op) end = time.time() deltas.append(end - start) diff --git a/tensorflow/python/data/experimental/kernel_tests/function_buffering_resource_test.py b/tensorflow/python/data/experimental/kernel_tests/function_buffering_resource_test.py deleted file mode 100644 index d38452e265..0000000000 --- a/tensorflow/python/data/experimental/kernel_tests/function_buffering_resource_test.py +++ /dev/null @@ -1,248 +0,0 @@ -# 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. -# ============================================================================== -"""Tests for the private `FunctionBufferingResource` used in prefetching.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import threading - -from tensorflow.core.protobuf import config_pb2 -from tensorflow.python.data.experimental.ops import prefetching_ops -from tensorflow.python.data.kernel_tests import test_base -from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.data.ops import iterator_ops -from tensorflow.python.eager import function -from tensorflow.python.framework import constant_op -from tensorflow.python.framework import dtypes -from tensorflow.python.framework import errors -from tensorflow.python.framework import ops -from tensorflow.python.framework import tensor_spec -from tensorflow.python.framework import test_util -from tensorflow.python.ops import resource_variable_ops -from tensorflow.python.platform import test - - -class FunctionBufferingResourceTest(test_base.DatasetTestBase): - - def setUp(self): - self._event = threading.Event() - - def _create_ds_and_iterator(self, device0, initializable=False): - - def gen(): - for i in range(1, 10): - yield [float(i)] - if i == 6: - self._event.set() - - with ops.device(device0): - ds = dataset_ops.Dataset.from_generator(gen, (dtypes.float32)) - if initializable: - ds_iterator = ds.make_initializable_iterator() - else: - ds_iterator = ds.make_one_shot_iterator() - return (ds, ds_iterator) - - def _create_ops(self, ds, ds_iterator, buffer_name, device0, device1): - ds_iterator_handle = ds_iterator.string_handle() - - @function.defun(input_signature=[tensor_spec.TensorSpec([], dtypes.string)]) - def _remote_fn(h): - remote_iterator = iterator_ops.Iterator.from_string_handle( - h, ds.output_types, ds.output_shapes) - return remote_iterator.get_next() - - target = constant_op.constant(device0) - with ops.device(device1): - buffer_resource_handle = prefetching_ops.function_buffering_resource( - f=_remote_fn.get_concrete_function(), - output_types=[dtypes.float32], - target_device=target, - string_arg=ds_iterator_handle, - buffer_size=3, - shared_name=buffer_name) - - with ops.device(device1): - prefetch_op = prefetching_ops.function_buffering_resource_get_next( - function_buffer_resource=buffer_resource_handle, - output_types=[dtypes.float32]) - reset_op = prefetching_ops.function_buffering_resource_reset( - function_buffer_resource=buffer_resource_handle) - destroy_op = resource_variable_ops.destroy_resource_op( - buffer_resource_handle, ignore_lookup_error=True) - - return (prefetch_op, reset_op, destroy_op) - - def _prefetch_fn_helper_one_shot(self, buffer_name, device0, device1): - worker_config = config_pb2.ConfigProto(device_count={"CPU": 2}) - - ds, ds_iterator = self._create_ds_and_iterator(device0, initializable=False) - prefetch_op, _, destroy_op = self._create_ops(ds, ds_iterator, buffer_name, - device0, device1) - - with self.test_session(config=worker_config) as sess: - elem = sess.run(prefetch_op) - self.assertEqual(elem, [1.0]) - elem = sess.run(prefetch_op) - self.assertEqual(elem, [2.0]) - elem = sess.run(prefetch_op) - self.assertEqual(elem, [3.0]) - elem = sess.run(prefetch_op) - self.assertEqual(elem, [4.0]) - self._event.wait() - elem = sess.run(prefetch_op) - self.assertEqual(elem, [5.0]) - sess.run(destroy_op) - - def testSameDeviceCPU(self): - self._prefetch_fn_helper_one_shot("same_device_cpu", - "/job:localhost/replica:0/task:0/cpu:0", - "/job:localhost/replica:0/task:0/cpu:0") - - def testDifferentDeviceCPU(self): - self._prefetch_fn_helper_one_shot("diff_device_cpu", - "/job:localhost/replica:0/task:0/cpu:0", - "/job:localhost/replica:0/task:0/cpu:1") - - def testDifferentDeviceCPUGPU(self): - if not test_util.is_gpu_available(): - self.skipTest("No GPU available") - - self._prefetch_fn_helper_one_shot("cpu_gpu", - "/job:localhost/replica:0/task:0/cpu:0", - "/job:localhost/replica:0/task:0/gpu:0") - - def testReinitialization(self): - worker_config = config_pb2.ConfigProto(device_count={"CPU": 2}) - - device0 = "/job:localhost/replica:0/task:0/cpu:0" - device1 = "/job:localhost/replica:0/task:0/cpu:1" - ds, ds_iterator = self._create_ds_and_iterator(device0, initializable=True) - prefetch_op, reset_op, destroy_op = self._create_ops( - ds, ds_iterator, "reinit", device0, device1) - - with self.test_session(config=worker_config) as sess: - sess.run(ds_iterator.initializer) - elem = sess.run(prefetch_op) - self.assertEqual(elem, [1.0]) - elem = sess.run(prefetch_op) - self.assertEqual(elem, [2.0]) - elem = sess.run(prefetch_op) - self.assertEqual(elem, [3.0]) - elem = sess.run(prefetch_op) - self.assertEqual(elem, [4.0]) - self._event.wait() - elem = sess.run(prefetch_op) - self.assertEqual(elem, [5.0]) - # Lets reset the function buffering resource and reinitialize the - # iterator. Should be able to go through this again. - self._event.clear() - sess.run(reset_op) - sess.run(ds_iterator.initializer) - elem = sess.run(prefetch_op) - self.assertEqual(elem, [1.0]) - elem = sess.run(prefetch_op) - self.assertEqual(elem, [2.0]) - elem = sess.run(prefetch_op) - self.assertEqual(elem, [3.0]) - elem = sess.run(prefetch_op) - self.assertEqual(elem, [4.0]) - self._event.wait() - elem = sess.run(prefetch_op) - self.assertEqual(elem, [5.0]) - sess.run(destroy_op) - - def testReinitializationOutOfRange(self): - worker_config = config_pb2.ConfigProto(device_count={"CPU": 2}) - - device0 = "/job:localhost/replica:0/task:0/cpu:0" - device1 = "/job:localhost/replica:0/task:0/cpu:1" - ds, ds_iterator = self._create_ds_and_iterator(device0, initializable=True) - prefetch_op, reset_op, destroy_op = self._create_ops( - ds, ds_iterator, "reinit", device0, device1) - - with self.test_session(config=worker_config) as sess: - sess.run(ds_iterator.initializer) - for i in range(1, 10): - elem = sess.run(prefetch_op) - self.assertEqual(elem, [float(i)]) - # Try fetching after its over twice to test out end of sequence. - with self.assertRaises(errors.OutOfRangeError): - sess.run(prefetch_op) - with self.assertRaises(errors.OutOfRangeError): - sess.run(prefetch_op) - - # Now reset everything and try it out again. - self._event.clear() - sess.run(reset_op) - sess.run(ds_iterator.initializer) - for i in range(1, 10): - elem = sess.run(prefetch_op) - self.assertEqual(elem, [float(i)]) - # Try fetching after its over twice to test out end of sequence. - with self.assertRaises(errors.OutOfRangeError): - sess.run(prefetch_op) - with self.assertRaises(errors.OutOfRangeError): - sess.run(prefetch_op) - - sess.run(destroy_op) - - def testStringsGPU(self): - if not test_util.is_gpu_available(): - self.skipTest("No GPU available") - - device0 = "/job:localhost/replica:0/task:0/cpu:0" - device1 = "/job:localhost/replica:0/task:0/gpu:0" - - ds = dataset_ops.Dataset.from_tensor_slices(["a", "b", "c"]) - ds_iterator = ds.make_one_shot_iterator() - ds_iterator_handle = ds_iterator.string_handle() - - @function.defun(input_signature=[tensor_spec.TensorSpec([], dtypes.string)]) - def _remote_fn(h): - remote_iterator = iterator_ops.Iterator.from_string_handle( - h, ds.output_types, ds.output_shapes) - return remote_iterator.get_next() - - target = constant_op.constant(device0) - with ops.device(device1): - buffer_resource_handle = prefetching_ops.function_buffering_resource( - f=_remote_fn.get_concrete_function(), - output_types=[dtypes.string], - target_device=target, - string_arg=ds_iterator_handle, - buffer_size=3, - shared_name="strings") - - with ops.device(device1): - prefetch_op = prefetching_ops.function_buffering_resource_get_next( - function_buffer_resource=buffer_resource_handle, - output_types=[dtypes.string]) - destroy_op = resource_variable_ops.destroy_resource_op( - buffer_resource_handle, ignore_lookup_error=True) - - with self.cached_session() as sess: - self.assertEqual([b"a"], sess.run(prefetch_op)) - self.assertEqual([b"b"], sess.run(prefetch_op)) - self.assertEqual([b"c"], sess.run(prefetch_op)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(prefetch_op) - - sess.run(destroy_op) - - -if __name__ == "__main__": - test.main() diff --git a/tensorflow/python/data/experimental/kernel_tests/get_single_element_test.py b/tensorflow/python/data/experimental/kernel_tests/get_single_element_test.py index 8c07afbac5..ef576563a1 100644 --- a/tensorflow/python/data/experimental/kernel_tests/get_single_element_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/get_single_element_test.py @@ -25,6 +25,7 @@ from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.platform import test @@ -39,6 +40,7 @@ class GetSingleElementTest(test_base.DatasetTestBase, parameterized.TestCase): ("MoreThanOne", 0, 2, errors.InvalidArgumentError, "Dataset had more than one element."), ) + @test_util.run_deprecated_v1 def testGetSingleElement(self, skip, take, error=None, error_msg=None): skip_t = array_ops.placeholder(dtypes.int64, shape=[]) take_t = array_ops.placeholder(dtypes.int64, shape=[]) @@ -67,6 +69,17 @@ class GetSingleElementTest(test_base.DatasetTestBase, parameterized.TestCase): with self.assertRaisesRegexp(error, error_msg): sess.run(element, feed_dict={skip_t: skip, take_t: take}) + def testWindow(self): + """Test that `get_single_element()` can consume a nested dataset.""" + def flat_map_func(ds): + batched = ds.batch(2) + element = get_single_element.get_single_element(batched) + return dataset_ops.Dataset.from_tensors(element) + + dataset = dataset_ops.Dataset.range(10).window(2).flat_map(flat_map_func) + self.assertDatasetProduces( + dataset, [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9]]) + if __name__ == "__main__": test.main() diff --git a/tensorflow/python/data/experimental/kernel_tests/group_by_reducer_test.py b/tensorflow/python/data/experimental/kernel_tests/group_by_reducer_test.py index 9030328593..8507df3d3a 100644 --- a/tensorflow/python/data/experimental/kernel_tests/group_by_reducer_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/group_by_reducer_test.py @@ -27,6 +27,7 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors from tensorflow.python.framework import sparse_tensor from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops from tensorflow.python.platform import test @@ -36,14 +37,15 @@ class GroupByReducerTest(test_base.DatasetTestBase): def checkResults(self, dataset, shapes, values): self.assertEqual(shapes, dataset.output_shapes) - get_next = dataset.make_one_shot_iterator().get_next() + get_next = dataset_ops.make_one_shot_iterator(dataset).get_next() with self.cached_session() as sess: for expected in values: - got = sess.run(get_next) + got = self.evaluate(get_next) self.assertEqual(got, expected) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) + @test_util.run_deprecated_v1 def testSum(self): reducer = grouping.Reducer( init_func=lambda _: np.int64(0), @@ -55,6 +57,7 @@ class GroupByReducerTest(test_base.DatasetTestBase): self.checkResults( dataset, shapes=tensor_shape.scalar(), values=[(i - 1) * i, i * i]) + @test_util.run_deprecated_v1 def testAverage(self): def reduce_fn(x, y): @@ -72,6 +75,7 @@ class GroupByReducerTest(test_base.DatasetTestBase): self.checkResults( dataset, shapes=tensor_shape.scalar(), values=[i - 1, i]) + @test_util.run_deprecated_v1 def testConcat(self): components = np.array(list("abcdefghijklmnopqrst")).view(np.chararray) reducer = grouping.Reducer( @@ -88,6 +92,7 @@ class GroupByReducerTest(test_base.DatasetTestBase): shapes=tensor_shape.scalar(), values=[b"acegikmoqs" [:i], b"bdfhjlnprt" [:i]]) + @test_util.run_deprecated_v1 def testSparseSum(self): def _sparse(i): return sparse_tensor.SparseTensorValue( @@ -105,6 +110,7 @@ class GroupByReducerTest(test_base.DatasetTestBase): self.checkResults( dataset, shapes=tensor_shape.scalar(), values=[(i - 1) * i, i * i]) + @test_util.run_deprecated_v1 def testChangingStateShape(self): def reduce_fn(x, _): @@ -124,14 +130,14 @@ class GroupByReducerTest(test_base.DatasetTestBase): grouping.group_by_reducer(lambda x: x, reducer)) self.assertEqual([None], dataset.output_shapes[0].as_list()) self.assertIs(None, dataset.output_shapes[1].ndims) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) get_next = iterator.get_next() with self.cached_session() as sess: - x, y = sess.run(get_next) + x, y = self.evaluate(get_next) self.assertAllEqual([0] * (2**i), x) self.assertAllEqual(np.array(1, ndmin=i), y) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) def testTypeMismatch(self): reducer = grouping.Reducer( @@ -188,9 +194,9 @@ class GroupByReducerTest(test_base.DatasetTestBase): dataset = dataset_ops.Dataset.zip( (dataset_ops.Dataset.range(10), dataset_ops.Dataset.range(10))).apply( grouping.group_by_reducer(lambda x, y: np.int64(0), reducer)) - get_next = dataset.make_one_shot_iterator().get_next() + get_next = dataset_ops.make_one_shot_iterator(dataset).get_next() with self.cached_session() as sess: - x, y = sess.run(get_next) + x, y = self.evaluate(get_next) self.assertAllEqual(x, np.asarray([x for x in range(10)])) self.assertEqual(y, 45) diff --git a/tensorflow/python/data/experimental/kernel_tests/group_by_window_test.py b/tensorflow/python/data/experimental/kernel_tests/group_by_window_test.py index 557d56e8b9..cbb79e55f5 100644 --- a/tensorflow/python/data/experimental/kernel_tests/group_by_window_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/group_by_window_test.py @@ -27,6 +27,7 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import string_ops @@ -49,6 +50,7 @@ class GroupByWindowTest(test_base.DatasetTestBase): 32, (tensor_shape.TensorShape([]), tensor_shape.TensorShape( [None]), tensor_shape.TensorShape([3]))))) + @test_util.run_deprecated_v1 def testSingleBucket(self): def _map_fn(v): @@ -63,14 +65,14 @@ class GroupByWindowTest(test_base.DatasetTestBase): lambda x, y, z: 0, lambda k, bucket: self._dynamicPad(k, bucket, 32), 32)) - iterator = bucketed_dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(bucketed_dataset) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: - sess.run(init_op) + self.evaluate(init_op) - which_bucket, bucketed_values = sess.run(get_next) + which_bucket, bucketed_values = self.evaluate(get_next) self.assertEqual(0, which_bucket) @@ -84,6 +86,7 @@ class GroupByWindowTest(test_base.DatasetTestBase): self.assertAllEqual(expected_unk_int64, bucketed_values[1]) self.assertAllEqual(expected_vec3_str, bucketed_values[2]) + @test_util.run_deprecated_v1 def testEvenOddBuckets(self): def _map_fn(v): @@ -98,16 +101,16 @@ class GroupByWindowTest(test_base.DatasetTestBase): lambda x, y, z: math_ops.cast(x % 2, dtypes.int64), lambda k, bucket: self._dynamicPad(k, bucket, 32), 32)) - iterator = bucketed_dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(bucketed_dataset) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: - sess.run(init_op) + self.evaluate(init_op) # Get two minibatches (one containing even values, one containing odds) - which_bucket_even, bucketed_values_even = sess.run(get_next) - which_bucket_odd, bucketed_values_odd = sess.run(get_next) + which_bucket_even, bucketed_values_even = self.evaluate(get_next) + which_bucket_odd, bucketed_values_odd = self.evaluate(get_next) # Count number of bucket_tensors. self.assertEqual(3, len(bucketed_values_even)) @@ -141,6 +144,7 @@ class GroupByWindowTest(test_base.DatasetTestBase): self.assertAllEqual(expected_unk_int64, bucketed_values_odd[1]) self.assertAllEqual(expected_vec3_str, bucketed_values_odd[2]) + @test_util.run_deprecated_v1 def testEvenOddBucketsFilterOutAllOdd(self): def _map_fn(v): @@ -169,16 +173,16 @@ class GroupByWindowTest(test_base.DatasetTestBase): lambda d: math_ops.cast(d["x"] % 2, dtypes.int64), lambda k, bucket: _dynamic_pad_fn(k, bucket, 32), 32)) - iterator = bucketed_dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(bucketed_dataset) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: - sess.run(init_op) + self.evaluate(init_op) # Get two minibatches ([0, 2, ...] and [64, 66, ...]) - which_bucket0, bucketed_values_even0 = sess.run(get_next) - which_bucket1, bucketed_values_even1 = sess.run(get_next) + which_bucket0, bucketed_values_even0 = self.evaluate(get_next) + which_bucket1, bucketed_values_even1 = self.evaluate(get_next) # Ensure that bucket 1 was completely filtered out self.assertAllEqual(0, which_bucket0) @@ -188,6 +192,7 @@ class GroupByWindowTest(test_base.DatasetTestBase): self.assertAllEqual( np.arange(64, 128, 2, dtype=np.int64), bucketed_values_even1["x"]) + @test_util.run_deprecated_v1 def testDynamicWindowSize(self): components = np.arange(100).astype(np.int64) @@ -202,16 +207,16 @@ class GroupByWindowTest(test_base.DatasetTestBase): dataset = dataset_ops.Dataset.from_tensor_slices(components).apply( grouping.group_by_window(lambda x: x % 2, lambda _, xs: xs.batch(20), None, window_size_func)) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: - sess.run(init_op) + self.evaluate(init_op) with self.assertRaises(errors.OutOfRangeError): batches = 0 while True: - result = sess.run(get_next) + result = self.evaluate(get_next) is_even = all(x % 2 == 0 for x in result) is_odd = all(x % 2 == 1 for x in result) self.assertTrue(is_even or is_odd) @@ -221,22 +226,23 @@ class GroupByWindowTest(test_base.DatasetTestBase): self.assertEqual(batches, 15) + @test_util.run_deprecated_v1 def testSimple(self): components = np.random.randint(100, size=(200,)).astype(np.int64) - iterator = ( + iterator = dataset_ops.make_initializable_iterator( dataset_ops.Dataset.from_tensor_slices(components).map(lambda x: x * x) .apply( grouping.group_by_window(lambda x: x % 2, lambda _, xs: xs.batch(4), - 4)).make_initializable_iterator()) + 4))) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: - sess.run(init_op) + self.evaluate(init_op) counts = [] with self.assertRaises(errors.OutOfRangeError): while True: - result = sess.run(get_next) + result = self.evaluate(get_next) self.assertTrue( all(x % 2 == 0 for x in result) or all(x % 2 == 1) @@ -248,61 +254,64 @@ class GroupByWindowTest(test_base.DatasetTestBase): self.assertGreaterEqual(num_full_batches, 24) self.assertTrue(all(c == 4 for c in counts[:num_full_batches])) + @test_util.run_deprecated_v1 def testImmediateOutput(self): components = np.array( [0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 0, 0, 2, 2, 0, 0], dtype=np.int64) - iterator = ( + iterator = dataset_ops.make_initializable_iterator( dataset_ops.Dataset.from_tensor_slices(components).repeat(-1).apply( grouping.group_by_window(lambda x: x % 3, lambda _, xs: xs.batch(4), - 4)).make_initializable_iterator()) + 4))) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: - sess.run(init_op) + self.evaluate(init_op) # The input is infinite, so this test demonstrates that: # 1. We produce output without having to consume the entire input, # 2. Different buckets can produce output at different rates, and # 3. For deterministic input, the output is deterministic. for _ in range(3): - self.assertAllEqual([0, 0, 0, 0], sess.run(get_next)) - self.assertAllEqual([1, 1, 1, 1], sess.run(get_next)) - self.assertAllEqual([2, 2, 2, 2], sess.run(get_next)) - self.assertAllEqual([0, 0, 0, 0], sess.run(get_next)) + self.assertAllEqual([0, 0, 0, 0], self.evaluate(get_next)) + self.assertAllEqual([1, 1, 1, 1], self.evaluate(get_next)) + self.assertAllEqual([2, 2, 2, 2], self.evaluate(get_next)) + self.assertAllEqual([0, 0, 0, 0], self.evaluate(get_next)) + @test_util.run_deprecated_v1 def testSmallGroups(self): components = np.array([0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0], dtype=np.int64) - iterator = ( + iterator = dataset_ops.make_initializable_iterator( dataset_ops.Dataset.from_tensor_slices(components).apply( grouping.group_by_window(lambda x: x % 2, lambda _, xs: xs.batch(4), - 4)).make_initializable_iterator()) + 4))) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: - sess.run(init_op) - self.assertAllEqual([0, 0, 0, 0], sess.run(get_next)) - self.assertAllEqual([1, 1, 1, 1], sess.run(get_next)) + self.evaluate(init_op) + self.assertAllEqual([0, 0, 0, 0], self.evaluate(get_next)) + self.assertAllEqual([1, 1, 1, 1], self.evaluate(get_next)) # The small outputs at the end are deterministically produced in key # order. - self.assertAllEqual([0, 0, 0], sess.run(get_next)) - self.assertAllEqual([1], sess.run(get_next)) + self.assertAllEqual([0, 0, 0], self.evaluate(get_next)) + self.assertAllEqual([1], self.evaluate(get_next)) + @test_util.run_deprecated_v1 def testEmpty(self): - iterator = ( + iterator = dataset_ops.make_initializable_iterator( dataset_ops.Dataset.range(4).apply( - grouping.group_by_window(lambda _: 0, lambda _, xs: xs, 0)) - .make_initializable_iterator()) + grouping.group_by_window(lambda _: 0, lambda _, xs: xs, 0))) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: - sess.run(init_op) + self.evaluate(init_op) with self.assertRaisesRegexp( errors.InvalidArgumentError, "Window size must be greater than zero, but got 0."): - print(sess.run(get_next)) + print(self.evaluate(get_next)) + @test_util.run_deprecated_v1 def testReduceFuncError(self): components = np.random.randint(100, size=(200,)).astype(np.int64) @@ -314,19 +323,19 @@ class GroupByWindowTest(test_base.DatasetTestBase): padded_shapes=(tensor_shape.TensorShape([]), constant_op.constant([5], dtype=dtypes.int64) * -1)) - iterator = ( + iterator = dataset_ops.make_initializable_iterator( dataset_ops.Dataset.from_tensor_slices(components) .map(lambda x: (x, ops.convert_to_tensor([x * x]))).apply( - grouping.group_by_window(lambda x, _: x % 2, reduce_func, - 32)).make_initializable_iterator()) + grouping.group_by_window(lambda x, _: x % 2, reduce_func, 32))) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: - sess.run(init_op) + self.evaluate(init_op) with self.assertRaises(errors.InvalidArgumentError): - sess.run(get_next) + self.evaluate(get_next) + @test_util.run_deprecated_v1 def testConsumeWindowDatasetMoreThanOnce(self): components = np.random.randint(50, size=(200,)).astype(np.int64) @@ -340,22 +349,21 @@ class GroupByWindowTest(test_base.DatasetTestBase): 4, padded_shapes=ops.convert_to_tensor([(key + 1) * 10])), )) - iterator = ( + iterator = dataset_ops.make_initializable_iterator( dataset_ops.Dataset.from_tensor_slices(components) .map(lambda x: array_ops.fill([math_ops.cast(x, dtypes.int32)], x)) .apply(grouping.group_by_window( lambda x: math_ops.cast(array_ops.shape(x)[0] // 10, dtypes.int64), - reduce_func, 4)) - .make_initializable_iterator()) + reduce_func, 4))) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: - sess.run(init_op) + self.evaluate(init_op) counts = [] with self.assertRaises(errors.OutOfRangeError): while True: - tight_result, multiple_of_10_result = sess.run(get_next) + tight_result, multiple_of_10_result = self.evaluate(get_next) self.assertEqual(0, multiple_of_10_result.shape[1] % 10) self.assertAllEqual(tight_result, multiple_of_10_result[:, :tight_result.shape[1]]) diff --git a/tensorflow/python/data/experimental/kernel_tests/ignore_errors_test.py b/tensorflow/python/data/experimental/kernel_tests/ignore_errors_test.py index c0ec1486ab..81f580fccb 100644 --- a/tensorflow/python/data/experimental/kernel_tests/ignore_errors_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/ignore_errors_test.py @@ -25,6 +25,7 @@ from tensorflow.python.data.experimental.ops import error_ops from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import errors +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import io_ops from tensorflow.python.platform import test @@ -35,6 +36,7 @@ _NUMPY_RANDOM_SEED = 42 class IgnoreErrorsTest(test_base.DatasetTestBase): + @test_util.run_deprecated_v1 def testMapIgnoreError(self): components = np.array([1., 2., 3., np.nan, 5.]).astype(np.float32) @@ -42,17 +44,18 @@ class IgnoreErrorsTest(test_base.DatasetTestBase): dataset_ops.Dataset.from_tensor_slices(components) .map(lambda x: array_ops.check_numerics(x, "message")).apply( error_ops.ignore_errors())) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: - sess.run(init_op) + self.evaluate(init_op) for x in [1., 2., 3., 5.]: - self.assertEqual(x, sess.run(get_next)) + self.assertEqual(x, self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) + @test_util.run_deprecated_v1 def testParallelMapIgnoreError(self): components = np.array([1., 2., 3., np.nan, 5.]).astype(np.float32) @@ -60,17 +63,18 @@ class IgnoreErrorsTest(test_base.DatasetTestBase): dataset_ops.Dataset.from_tensor_slices(components).map( lambda x: array_ops.check_numerics(x, "message"), num_parallel_calls=2).prefetch(2).apply(error_ops.ignore_errors())) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: - sess.run(init_op) + self.evaluate(init_op) for x in [1., 2., 3., 5.]: - self.assertEqual(x, sess.run(get_next)) + self.assertEqual(x, self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) + @test_util.run_deprecated_v1 def testReadFileIgnoreError(self): def write_string_to_file(value, filename): @@ -87,28 +91,28 @@ class IgnoreErrorsTest(test_base.DatasetTestBase): dataset_ops.Dataset.from_tensor_slices(filenames).map( io_ops.read_file, num_parallel_calls=2).prefetch(2).apply(error_ops.ignore_errors())) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: # All of the files are present. - sess.run(init_op) + self.evaluate(init_op) for filename in filenames: - self.assertEqual(compat.as_bytes(filename), sess.run(get_next)) + self.assertEqual(compat.as_bytes(filename), self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Delete one of the files. os.remove(filenames[0]) # Attempting to read filenames[0] will fail, but ignore_errors() # will catch the error. - sess.run(init_op) + self.evaluate(init_op) for filename in filenames[1:]: - self.assertEqual(compat.as_bytes(filename), sess.run(get_next)) + self.assertEqual(compat.as_bytes(filename), self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) if __name__ == "__main__": diff --git a/tensorflow/python/data/experimental/kernel_tests/indexed_dataset_ops_test.py b/tensorflow/python/data/experimental/kernel_tests/indexed_dataset_ops_test.py index c93a8353ce..c3c4ccd077 100644 --- a/tensorflow/python/data/experimental/kernel_tests/indexed_dataset_ops_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/indexed_dataset_ops_test.py @@ -24,6 +24,7 @@ from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gen_experimental_dataset_ops as ged_ops from tensorflow.python.platform import test @@ -31,6 +32,7 @@ from tensorflow.python.platform import test class IndexedDatasetOpsTest(test_base.DatasetTestBase): + @test_util.run_deprecated_v1 def testLowLevelIndexedDatasetOps(self): identity = ged_ops.experimental_identity_indexed_dataset( ops.convert_to_tensor(16, dtype=dtypes.uint64)) @@ -46,14 +48,15 @@ class IndexedDatasetOpsTest(test_base.DatasetTestBase): handle, index, output_types=[dtypes.uint64], output_shapes=[[]]) with self.cached_session() as sess: - sess.run(materialize) + self.evaluate(materialize) self.assertEqual([3], sess.run(get_op, feed_dict={index: 3})) + @test_util.run_deprecated_v1 def testIdentityIndexedDataset(self): ds = indexed_dataset_ops.IdentityIndexedDataset(16) materialized = ds.materialize() with self.cached_session() as sess: - sess.run(materialized.initializer) + self.evaluate(materialized.initializer) placeholder = array_ops.placeholder(dtypes.uint64, shape=[]) for i in range(16): output = sess.run( @@ -68,12 +71,13 @@ class IndexedDatasetOpsTest(test_base.DatasetTestBase): itr = ds.make_initializable_iterator() n = itr.get_next() with self.cached_session() as sess: - sess.run(itr.initializer) + self.evaluate(itr.initializer) for i in range(16): - output = sess.run(n) + output = self.evaluate(n) self.assertEqual(i, output) with self.assertRaises(errors.OutOfRangeError): - sess.run(n) + self.evaluate(n) + if __name__ == "__main__": test.main() diff --git a/tensorflow/python/data/experimental/kernel_tests/make_batched_features_dataset_test.py b/tensorflow/python/data/experimental/kernel_tests/make_batched_features_dataset_test.py index 91ae8cb1bd..7c78810494 100644 --- a/tensorflow/python/data/experimental/kernel_tests/make_batched_features_dataset_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/make_batched_features_dataset_test.py @@ -21,11 +21,13 @@ import numpy as np from tensorflow.python.data.experimental.kernel_tests import reader_dataset_ops_test_base from tensorflow.python.data.experimental.ops import readers +from tensorflow.python.data.ops import dataset_ops from tensorflow.python.data.ops import readers as core_readers from tensorflow.python.data.util import nest from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import io_ops from tensorflow.python.ops import parsing_ops from tensorflow.python.platform import test @@ -40,11 +42,12 @@ class MakeBatchedFeaturesDatasetTest( with ops.Graph().as_default() as g: with self.session(graph=g) as sess: # Basic test: read from file 0. - self.outputs = self.make_batch_feature( - filenames=self.test_filenames[0], - label_key="label", - num_epochs=num_epochs, - batch_size=batch_size).make_one_shot_iterator().get_next() + self.outputs = dataset_ops.make_one_shot_iterator( + self.make_batch_feature( + filenames=self.test_filenames[0], + label_key="label", + num_epochs=num_epochs, + batch_size=batch_size)).get_next() self.verify_records( sess, batch_size, @@ -57,11 +60,12 @@ class MakeBatchedFeaturesDatasetTest( with ops.Graph().as_default() as g: with self.session(graph=g) as sess: # Basic test: read from file 1. - self.outputs = self.make_batch_feature( - filenames=self.test_filenames[1], - label_key="label", - num_epochs=num_epochs, - batch_size=batch_size).make_one_shot_iterator().get_next() + self.outputs = dataset_ops.make_one_shot_iterator( + self.make_batch_feature( + filenames=self.test_filenames[1], + label_key="label", + num_epochs=num_epochs, + batch_size=batch_size)).get_next() self.verify_records( sess, batch_size, @@ -74,11 +78,12 @@ class MakeBatchedFeaturesDatasetTest( with ops.Graph().as_default() as g: with self.session(graph=g) as sess: # Basic test: read from both files. - self.outputs = self.make_batch_feature( - filenames=self.test_filenames, - label_key="label", - num_epochs=num_epochs, - batch_size=batch_size).make_one_shot_iterator().get_next() + self.outputs = dataset_ops.make_one_shot_iterator( + self.make_batch_feature( + filenames=self.test_filenames, + label_key="label", + num_epochs=num_epochs, + batch_size=batch_size)).get_next() self.verify_records( sess, batch_size, @@ -90,14 +95,16 @@ class MakeBatchedFeaturesDatasetTest( with ops.Graph().as_default() as g: with self.session(graph=g) as sess: # Basic test: read from both files. - self.outputs = self.make_batch_feature( - filenames=self.test_filenames, - num_epochs=num_epochs, - batch_size=batch_size).make_one_shot_iterator().get_next() + self.outputs = dataset_ops.make_one_shot_iterator( + self.make_batch_feature( + filenames=self.test_filenames, + num_epochs=num_epochs, + batch_size=batch_size)).get_next() self.verify_records(sess, batch_size, num_epochs=num_epochs) with self.assertRaises(errors.OutOfRangeError): self._next_actual_batch(sess) + @test_util.run_deprecated_v1 def testReadWithEquivalentDataset(self): features = { "file": parsing_ops.FixedLenFeature([], dtypes.int64), @@ -107,19 +114,19 @@ class MakeBatchedFeaturesDatasetTest( core_readers.TFRecordDataset(self.test_filenames) .map(lambda x: parsing_ops.parse_single_example(x, features)) .repeat(10).batch(2)) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) init_op = iterator.initializer next_element = iterator.get_next() with self.cached_session() as sess: - sess.run(init_op) + self.evaluate(init_op) for file_batch, _, _, _, record_batch, _ in self._next_expected_batch( range(self._num_files), 2, 10): - actual_batch = sess.run(next_element) + actual_batch = self.evaluate(next_element) self.assertAllEqual(file_batch, actual_batch["file"]) self.assertAllEqual(record_batch, actual_batch["record"]) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) def testReadWithFusedShuffleRepeatDataset(self): num_epochs = 5 @@ -128,18 +135,18 @@ class MakeBatchedFeaturesDatasetTest( # Test that shuffling with same seed produces the same result. with ops.Graph().as_default() as g: with self.session(graph=g) as sess: - outputs1 = self.make_batch_feature( + outputs1 = dataset_ops.make_one_shot_iterator(self.make_batch_feature( filenames=self.test_filenames[0], num_epochs=num_epochs, batch_size=batch_size, shuffle=True, - shuffle_seed=5).make_one_shot_iterator().get_next() - outputs2 = self.make_batch_feature( + shuffle_seed=5)).get_next() + outputs2 = dataset_ops.make_one_shot_iterator(self.make_batch_feature( filenames=self.test_filenames[0], num_epochs=num_epochs, batch_size=batch_size, shuffle=True, - shuffle_seed=5).make_one_shot_iterator().get_next() + shuffle_seed=5)).get_next() for _ in range(total_records // batch_size): batch1 = self._run_actual_batch(outputs1, sess) batch2 = self._run_actual_batch(outputs2, sess) @@ -149,18 +156,18 @@ class MakeBatchedFeaturesDatasetTest( # Test that shuffling with different seeds produces a different order. with ops.Graph().as_default() as g: with self.session(graph=g) as sess: - outputs1 = self.make_batch_feature( + outputs1 = dataset_ops.make_one_shot_iterator(self.make_batch_feature( filenames=self.test_filenames[0], num_epochs=num_epochs, batch_size=batch_size, shuffle=True, - shuffle_seed=5).make_one_shot_iterator().get_next() - outputs2 = self.make_batch_feature( + shuffle_seed=5)).get_next() + outputs2 = dataset_ops.make_one_shot_iterator(self.make_batch_feature( filenames=self.test_filenames[0], num_epochs=num_epochs, batch_size=batch_size, shuffle=True, - shuffle_seed=15).make_one_shot_iterator().get_next() + shuffle_seed=15)).get_next() all_equal = True for _ in range(total_records // batch_size): batch1 = self._run_actual_batch(outputs1, sess) @@ -176,14 +183,14 @@ class MakeBatchedFeaturesDatasetTest( for parser_num_threads in [2, 4]: with ops.Graph().as_default() as g: with self.session(graph=g) as sess: - self.outputs = self.make_batch_feature( - filenames=self.test_filenames, - label_key="label", - num_epochs=num_epochs, - batch_size=batch_size, - reader_num_threads=reader_num_threads, - parser_num_threads=parser_num_threads).make_one_shot_iterator( - ).get_next() + self.outputs = dataset_ops.make_one_shot_iterator( + self.make_batch_feature( + filenames=self.test_filenames, + label_key="label", + num_epochs=num_epochs, + batch_size=batch_size, + reader_num_threads=reader_num_threads, + parser_num_threads=parser_num_threads)).get_next() self.verify_records( sess, batch_size, @@ -195,13 +202,13 @@ class MakeBatchedFeaturesDatasetTest( with ops.Graph().as_default() as g: with self.session(graph=g) as sess: - self.outputs = self.make_batch_feature( - filenames=self.test_filenames, - num_epochs=num_epochs, - batch_size=batch_size, - reader_num_threads=reader_num_threads, - parser_num_threads=parser_num_threads).make_one_shot_iterator( - ).get_next() + self.outputs = dataset_ops.make_one_shot_iterator( + self.make_batch_feature( + filenames=self.test_filenames, + num_epochs=num_epochs, + batch_size=batch_size, + reader_num_threads=reader_num_threads, + parser_num_threads=parser_num_threads)).get_next() self.verify_records( sess, batch_size, @@ -215,12 +222,12 @@ class MakeBatchedFeaturesDatasetTest( for num_epochs in [1, 10]: with ops.Graph().as_default(): # Basic test: read from file 0. - outputs = self.make_batch_feature( + outputs = dataset_ops.make_one_shot_iterator(self.make_batch_feature( filenames=self.test_filenames[0], label_key="label", num_epochs=num_epochs, batch_size=batch_size, - drop_final_batch=True).make_one_shot_iterator().get_next() + drop_final_batch=True)).get_next() for tensor in nest.flatten(outputs): if isinstance(tensor, ops.Tensor): # Guard against SparseTensor. self.assertEqual(tensor.shape[0], batch_size) diff --git a/tensorflow/python/data/experimental/kernel_tests/make_csv_dataset_test.py b/tensorflow/python/data/experimental/kernel_tests/make_csv_dataset_test.py index e4bf089184..e80accee33 100644 --- a/tensorflow/python/data/experimental/kernel_tests/make_csv_dataset_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/make_csv_dataset_test.py @@ -25,11 +25,13 @@ import numpy as np from tensorflow.python.data.experimental.ops import readers from tensorflow.python.data.kernel_tests import test_base +from tensorflow.python.data.ops import dataset_ops from tensorflow.python.data.util import nest from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.platform import test @@ -82,7 +84,7 @@ class MakeCsvDatasetTest(test_base.DatasetTestBase): expected_output, expected_keys, ): - nxt = dataset.make_one_shot_iterator().get_next() + nxt = dataset_ops.make_one_shot_iterator(dataset).get_next() for expected_features in self._next_expected_batch( expected_output, @@ -90,7 +92,7 @@ class MakeCsvDatasetTest(test_base.DatasetTestBase): batch_size, num_epochs, ): - actual_features = sess.run(nxt) + actual_features = self.evaluate(nxt) if label_name is not None: expected_labels = expected_features.pop(label_name) @@ -102,7 +104,7 @@ class MakeCsvDatasetTest(test_base.DatasetTestBase): self.assertAllEqual(expected_features[k], actual_features[k]) with self.assertRaises(errors.OutOfRangeError): - sess.run(nxt) + self.evaluate(nxt) def _test_dataset(self, inputs, @@ -127,6 +129,7 @@ class MakeCsvDatasetTest(test_base.DatasetTestBase): self._verify_output(sess, dataset, batch_size, num_epochs, label_name, expected_output, expected_keys) + @test_util.run_deprecated_v1 def testMakeCSVDataset(self): """Tests making a CSV dataset with keys and defaults provided.""" record_defaults = [ @@ -158,6 +161,7 @@ class MakeCsvDatasetTest(test_base.DatasetTestBase): column_defaults=record_defaults, ) + @test_util.run_deprecated_v1 def testMakeCSVDataset_withBatchSizeAndEpochs(self): """Tests making a CSV dataset with keys and defaults provided.""" record_defaults = [ @@ -189,6 +193,7 @@ class MakeCsvDatasetTest(test_base.DatasetTestBase): column_defaults=record_defaults, ) + @test_util.run_deprecated_v1 def testMakeCSVDataset_withCompressionType(self): """Tests `compression_type` argument.""" record_defaults = [ @@ -257,6 +262,7 @@ class MakeCsvDatasetTest(test_base.DatasetTestBase): label_name="not_a_real_label", column_names=column_names) + @test_util.run_deprecated_v1 def testMakeCSVDataset_withNoLabel(self): """Tests making a CSV dataset with no label provided.""" record_defaults = [ @@ -286,6 +292,7 @@ class MakeCsvDatasetTest(test_base.DatasetTestBase): column_defaults=record_defaults, ) + @test_util.run_deprecated_v1 def testMakeCSVDataset_withNoHeader(self): """Tests that datasets can be created from CSV files with no header line. """ @@ -347,6 +354,7 @@ class MakeCsvDatasetTest(test_base.DatasetTestBase): column_defaults=record_defaults, ) + @test_util.run_deprecated_v1 def testMakeCSVDataset_withNoColNames(self): """Tests that datasets can be created when column names are not specified. @@ -451,6 +459,7 @@ class MakeCsvDatasetTest(test_base.DatasetTestBase): header=True, ) + @test_util.run_deprecated_v1 def testMakeCSVDataset_withSelectCols(self): record_defaults = [ constant_op.constant([], dtypes.int32), @@ -557,6 +566,7 @@ class MakeCsvDatasetTest(test_base.DatasetTestBase): label_name=None, select_columns=["invalid_col_name"]) + @test_util.run_deprecated_v1 def testMakeCSVDataset_withShuffle(self): record_defaults = [ constant_op.constant([], dtypes.int32), @@ -604,11 +614,11 @@ class MakeCsvDatasetTest(test_base.DatasetTestBase): shuffle_seed=5, num_epochs=2, ) - outputs1 = dataset1.make_one_shot_iterator().get_next() - outputs2 = dataset2.make_one_shot_iterator().get_next() + outputs1 = dataset_ops.make_one_shot_iterator(dataset1).get_next() + outputs2 = dataset_ops.make_one_shot_iterator(dataset2).get_next() for _ in range(total_records // batch_size): - batch1 = nest.flatten(sess.run(outputs1)) - batch2 = nest.flatten(sess.run(outputs2)) + batch1 = nest.flatten(self.evaluate(outputs1)) + batch2 = nest.flatten(self.evaluate(outputs2)) for i in range(len(batch1)): self.assertAllEqual(batch1[i], batch2[i]) @@ -635,12 +645,12 @@ class MakeCsvDatasetTest(test_base.DatasetTestBase): shuffle_seed=6, num_epochs=2, ) - outputs1 = dataset1.make_one_shot_iterator().get_next() - outputs2 = dataset2.make_one_shot_iterator().get_next() + outputs1 = dataset_ops.make_one_shot_iterator(dataset1).get_next() + outputs2 = dataset_ops.make_one_shot_iterator(dataset2).get_next() all_equal = False for _ in range(total_records // batch_size): - batch1 = nest.flatten(sess.run(outputs1)) - batch2 = nest.flatten(sess.run(outputs2)) + batch1 = nest.flatten(self.evaluate(outputs1)) + batch2 = nest.flatten(self.evaluate(outputs2)) for i in range(len(batch1)): all_equal = all_equal and np.array_equal(batch1[i], batch2[i]) self.assertFalse(all_equal) diff --git a/tensorflow/python/data/experimental/kernel_tests/make_tf_record_dataset_test.py b/tensorflow/python/data/experimental/kernel_tests/make_tf_record_dataset_test.py index 657cf3c00e..ab2feb6426 100644 --- a/tensorflow/python/data/experimental/kernel_tests/make_tf_record_dataset_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/make_tf_record_dataset_test.py @@ -19,6 +19,7 @@ from __future__ import print_function from tensorflow.python.data.experimental.kernel_tests import reader_dataset_ops_test_base from tensorflow.python.data.experimental.ops import readers +from tensorflow.python.data.ops import dataset_ops from tensorflow.python.data.util import nest from tensorflow.python.framework import errors from tensorflow.python.framework import ops @@ -105,7 +106,7 @@ class MakeTFRecordDatasetTest( for expected_batch in self._next_expected_batch( file_indices, batch_size, num_epochs, interleave_cycle_length, drop_final_batch, use_parser_fn): - actual_batch = sess.run(outputs) + actual_batch = self.evaluate(outputs) self.assertAllEqual(expected_batch, actual_batch) def _read_test(self, batch_size, num_epochs, file_index=None, @@ -122,20 +123,21 @@ class MakeTFRecordDatasetTest( with ops.Graph().as_default() as g: with self.session(graph=g) as sess: - outputs = readers.make_tf_record_dataset( - file_pattern=file_pattern, - num_epochs=num_epochs, - batch_size=batch_size, - parser_fn=fn, - num_parallel_reads=num_parallel_reads, - drop_final_batch=drop_final_batch, - shuffle=False).make_one_shot_iterator().get_next() + outputs = dataset_ops.make_one_shot_iterator( + readers.make_tf_record_dataset( + file_pattern=file_pattern, + num_epochs=num_epochs, + batch_size=batch_size, + parser_fn=fn, + num_parallel_reads=num_parallel_reads, + drop_final_batch=drop_final_batch, + shuffle=False)).get_next() self._verify_records( sess, outputs, batch_size, file_index, num_epochs=num_epochs, interleave_cycle_length=num_parallel_reads, drop_final_batch=drop_final_batch, use_parser_fn=parser_fn) with self.assertRaises(errors.OutOfRangeError): - sess.run(outputs) + self.evaluate(outputs) def testRead(self): for batch_size in [1, 2]: @@ -185,22 +187,22 @@ class MakeTFRecordDatasetTest( num_parallel_reads=num_parallel_reads, shuffle=True, shuffle_seed=seed) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) next_element = iterator.get_next() - sess.run(iterator.initializer) + self.evaluate(iterator.initializer) first_batches = [] try: while True: - first_batches.append(sess.run(next_element)) + first_batches.append(self.evaluate(next_element)) except errors.OutOfRangeError: pass - sess.run(iterator.initializer) + self.evaluate(iterator.initializer) second_batches = [] try: while True: - second_batches.append(sess.run(next_element)) + second_batches.append(self.evaluate(next_element)) except errors.OutOfRangeError: pass diff --git a/tensorflow/python/data/experimental/kernel_tests/map_and_batch_test.py b/tensorflow/python/data/experimental/kernel_tests/map_and_batch_test.py index 5ead6d1c75..e6e24c3db1 100644 --- a/tensorflow/python/data/experimental/kernel_tests/map_and_batch_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/map_and_batch_test.py @@ -29,6 +29,7 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import math_ops @@ -48,6 +49,7 @@ class MapAndBatchTest(test_base.DatasetTestBase, parameterized.TestCase): ("ParallelCallsNUMA", 2, None, True), ("ParallelBatchesNUMA", None, 10, True), ) + @test_util.run_deprecated_v1 def testMapAndBatch(self, num_parallel_calls, num_parallel_batches, numa_aware): """Test a dataset that maps a TF function across its input elements.""" @@ -76,7 +78,7 @@ class MapAndBatchTest(test_base.DatasetTestBase, parameterized.TestCase): options.experimental_numa_aware = True dataset = dataset.with_options(options) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) init_op = iterator.initializer get_next = iterator.get_next() @@ -89,13 +91,13 @@ class MapAndBatchTest(test_base.DatasetTestBase, parameterized.TestCase): sess.run(init_op, feed_dict={count: 28, batch_size: 14}) num_batches = (28 * 7) // 14 for i in range(num_batches): - result = sess.run(get_next) + result = self.evaluate(get_next) for component, result_component in zip(components, result): for j in range(14): self.assertAllEqual(component[(i * 14 + j) % 7]**2, result_component[j]) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Batch of a finite input, where the batch_size does not # divide the total number of elements. @@ -104,23 +106,23 @@ class MapAndBatchTest(test_base.DatasetTestBase, parameterized.TestCase): # We expect (num_batches - 1) full-sized batches. num_batches = int(math.ceil((14 * 7) / 8)) for i in range(num_batches - 1): - result = sess.run(get_next) + result = self.evaluate(get_next) for component, result_component in zip(components, result): for j in range(8): self.assertAllEqual(component[(i * 8 + j) % 7]**2, result_component[j]) - result = sess.run(get_next) + result = self.evaluate(get_next) for component, result_component in zip(components, result): for j in range((14 * 7) % 8): self.assertAllEqual(component[((num_batches - 1) * 8 + j) % 7]**2, result_component[j]) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Batch of an empty input should fail straight away. sess.run(init_op, feed_dict={count: 0, batch_size: 8}) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Empty batch should be an initialization time error. with self.assertRaises(errors.InvalidArgumentError): @@ -132,6 +134,7 @@ class MapAndBatchTest(test_base.DatasetTestBase, parameterized.TestCase): ("EvenNUMA", False, True), ("UnevenNUMA", True, True), ) + @test_util.run_deprecated_v1 def testMapAndBatchPartialBatch(self, drop_remainder, numa_aware): dataset = ( dataset_ops.Dataset.range(10).apply( @@ -144,7 +147,7 @@ class MapAndBatchTest(test_base.DatasetTestBase, parameterized.TestCase): options = dataset_ops.Options() options.experimental_numa_aware = True dataset = dataset.with_options(options) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) if drop_remainder: self.assertEqual([4, 1], iterator.output_shapes.as_list()) @@ -152,17 +155,18 @@ class MapAndBatchTest(test_base.DatasetTestBase, parameterized.TestCase): self.assertEqual([None, 1], iterator.output_shapes.as_list()) next_element = iterator.get_next() with self.cached_session() as sess: - self.assertAllEqual([[0], [1], [4], [9]], sess.run(next_element)) - self.assertAllEqual([[16], [25], [36], [49]], sess.run(next_element)) + self.assertAllEqual([[0], [1], [4], [9]], self.evaluate(next_element)) + self.assertAllEqual([[16], [25], [36], [49]], self.evaluate(next_element)) if not drop_remainder: - self.assertAllEqual([[64], [81]], sess.run(next_element)) + self.assertAllEqual([[64], [81]], self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) @parameterized.named_parameters( ("Normal", False), ("NUMA", True), ) + @test_util.run_deprecated_v1 def testMapAndBatchYieldsPartialBatch(self, numa_aware): dataset = ( dataset_ops.Dataset.range(10).apply( @@ -173,20 +177,21 @@ class MapAndBatchTest(test_base.DatasetTestBase, parameterized.TestCase): options.experimental_numa_aware = True dataset = dataset.with_options(options) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) self.assertEqual([None, 1], iterator.output_shapes.as_list()) next_element = iterator.get_next() with self.cached_session() as sess: - self.assertAllEqual([[0], [1], [4], [9]], sess.run(next_element)) - self.assertAllEqual([[16], [25], [36], [49]], sess.run(next_element)) - self.assertAllEqual([[64], [81]], sess.run(next_element)) + self.assertAllEqual([[0], [1], [4], [9]], self.evaluate(next_element)) + self.assertAllEqual([[16], [25], [36], [49]], self.evaluate(next_element)) + self.assertAllEqual([[64], [81]], self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) @parameterized.named_parameters( ("Normal", False), ("NUMA", True), ) + @test_util.run_deprecated_v1 def testMapAndBatchParallelGetNext(self, numa_aware): dataset = dataset_ops.Dataset.range(50000).apply( batching.map_and_batch(lambda x: x, batch_size=100)) @@ -194,26 +199,27 @@ class MapAndBatchTest(test_base.DatasetTestBase, parameterized.TestCase): options = dataset_ops.Options() options.experimental_numa_aware = True dataset = dataset.with_options(options) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) elements = [] for _ in range(100): elements.append(iterator.get_next()) with self.cached_session() as sess: for i in range(5): - got = sess.run(elements) + got = self.evaluate(elements) got.sort(key=lambda x: x[0]) expected = [] for j in range(100): expected.append(range(i * 10000 + j * 100, i * 10000 + (j + 1) * 100)) self.assertAllEqual(got, expected) with self.assertRaises(errors.OutOfRangeError): - sess.run(elements) + self.evaluate(elements) @parameterized.named_parameters( ("Normal", False), ("NUMA", True), ) + @test_util.run_deprecated_v1 def testMapAndBatchParallelGetNextDropRemainder(self, numa_aware): dataset = dataset_ops.Dataset.range(49999).apply( batching.map_and_batch( @@ -223,26 +229,27 @@ class MapAndBatchTest(test_base.DatasetTestBase, parameterized.TestCase): options = dataset_ops.Options() options.experimental_numa_aware = True dataset = dataset.with_options(options) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) elements = [] for _ in range(100): elements.append(iterator.get_next()) with self.cached_session() as sess: for i in range(4): - got = sess.run(elements) + got = self.evaluate(elements) got.sort(key=lambda x: x[0]) expected = [] for j in range(100): expected.append(range(i * 10000 + j * 100, i * 10000 + (j + 1) * 100)) self.assertAllEqual(got, expected) with self.assertRaises(errors.OutOfRangeError): - sess.run(elements) + self.evaluate(elements) @parameterized.named_parameters( ("Normal", False), ("NUMA", True), ) + @test_util.run_deprecated_v1 def testMapAndBatchSparse(self, numa_aware): def _sparse(i): @@ -255,15 +262,15 @@ class MapAndBatchTest(test_base.DatasetTestBase, parameterized.TestCase): options = dataset_ops.Options() options.experimental_numa_aware = True dataset = dataset.with_options(options) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: - sess.run(init_op) + self.evaluate(init_op) for i in range(2): - actual = sess.run(get_next) + actual = self.evaluate(get_next) expected = sparse_tensor.SparseTensorValue( indices=[[0, 0], [1, 0], [2, 0], [3, 0], [4, 0]], values=[i * 5, i * 5 + 1, i * 5 + 2, i * 5 + 3, i * 5 + 4], @@ -271,12 +278,13 @@ class MapAndBatchTest(test_base.DatasetTestBase, parameterized.TestCase): self.assertTrue(sparse_tensor.is_sparse(actual)) self.assertSparseValuesEqual(actual, expected) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) @parameterized.named_parameters( ("Normal", False), ("NUMA", True), ) + @test_util.run_deprecated_v1 def testMapAndBatchFails(self, numa_aware): """Test a dataset that maps a TF function across its input elements.""" dataset = dataset_ops.Dataset.from_tensors( @@ -288,7 +296,7 @@ class MapAndBatchTest(test_base.DatasetTestBase, parameterized.TestCase): options = dataset_ops.Options() options.experimental_numa_aware = True dataset = dataset.with_options(options) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) init_op = iterator.initializer with self.cached_session() as sess: @@ -299,6 +307,7 @@ class MapAndBatchTest(test_base.DatasetTestBase, parameterized.TestCase): ("Normal", False), ("NUMA", True), ) + @test_util.run_deprecated_v1 def testMapAndBatchShapeMismatch(self, numa_aware): """Test a dataset that maps a TF function across its input elements.""" @@ -316,15 +325,15 @@ class MapAndBatchTest(test_base.DatasetTestBase, parameterized.TestCase): options = dataset_ops.Options() options.experimental_numa_aware = True dataset = dataset.with_options(options) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: - sess.run(init_op) + self.evaluate(init_op) with self.assertRaisesRegexp(errors.InvalidArgumentError, "number of elements does not match"): - sess.run(get_next) + self.evaluate(get_next) @parameterized.named_parameters( ("Normal", False), @@ -349,12 +358,12 @@ class MapAndBatchTest(test_base.DatasetTestBase, parameterized.TestCase): options = dataset_ops.Options() options.experimental_numa_aware = True dataset = dataset.with_options(options) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) get_next = iterator.get_next() with self.cached_session() as sess: for _ in range(3): - sess.run(get_next) + self.evaluate(get_next) @parameterized.named_parameters( ("1", 0, False), @@ -370,6 +379,7 @@ class MapAndBatchTest(test_base.DatasetTestBase, parameterized.TestCase): ("5NUMA", 95, True), ("6NUMA", 99, True), ) + @test_util.run_deprecated_v1 def testMapAndBatchOutOfRangeError(self, threshold, numa_aware): def raising_py_fn(i): @@ -388,18 +398,19 @@ class MapAndBatchTest(test_base.DatasetTestBase, parameterized.TestCase): options = dataset_ops.Options() options.experimental_numa_aware = True dataset = dataset.with_options(options) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) get_next = iterator.get_next() with self.cached_session() as sess: for i in range(threshold // 10): - self.assertAllEqual([i * 10 + j for j in range(10)], sess.run(get_next)) + self.assertAllEqual([i * 10 + j for j in range(10)], + self.evaluate(get_next)) if threshold % 10 != 0: self.assertAllEqual( [threshold // 10 * 10 + j for j in range(threshold % 10)], - sess.run(get_next)) + self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) @parameterized.named_parameters( ("1", False, dtypes.bool, False), @@ -438,11 +449,12 @@ class MapAndBatchTest(test_base.DatasetTestBase, parameterized.TestCase): options.experimental_numa_aware = True dataset = dataset.with_options(options) - get_next = dataset.make_one_shot_iterator().get_next() + get_next = dataset_ops.make_one_shot_iterator(dataset).get_next() with self.cached_session() as sess: for _ in range(10): - self.assertAllEqual([element for _ in range(10)], sess.run(get_next)) + self.assertAllEqual([element for _ in range(10)], + self.evaluate(get_next)) @parameterized.named_parameters( ("Identity", None, lambda x: x, None), @@ -450,10 +462,11 @@ class MapAndBatchTest(test_base.DatasetTestBase, parameterized.TestCase): ("Swap", (None, None), lambda x, y: (y, x), None), ("Project", (None, None), lambda x, y: x, None), ) + @test_util.run_deprecated_v1 def testShortCircuit(self, structure, map_fn, num_parallel_calls): dataset = self.structuredDataset(structure).repeat().apply( batching.map_and_batch(map_fn, batch_size=10)) - get_next = dataset.make_one_shot_iterator().get_next() + get_next = dataset_ops.make_one_shot_iterator(dataset).get_next() with self.cached_session() as sess: if isinstance(structure, tuple): @@ -462,23 +475,25 @@ class MapAndBatchTest(test_base.DatasetTestBase, parameterized.TestCase): else: expected = map_fn( sess.run(self.structuredElement(structure, shape=[10]))) - self.assertAllEqual(expected, sess.run(get_next)) + self.assertAllEqual(expected, self.evaluate(get_next)) + @test_util.run_deprecated_v1 def testShortCircuitCapturedInput(self): captured_t = array_ops.placeholder(dtypes.int64, shape=[]) dataset = self.structuredDataset(None).repeat().apply( batching.map_and_batch(lambda x: captured_t, batch_size=10)) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) get_next = iterator.get_next() with self.cached_session() as sess: sess.run(iterator.initializer, feed_dict={captured_t: 42}) - self.assertAllEqual([42] * 10, sess.run(get_next)) + self.assertAllEqual([42] * 10, self.evaluate(get_next)) @parameterized.named_parameters( ("Normal", False), ("NUMA", True), ) + @test_util.run_deprecated_v1 def testMapAndBatchControlFlow(self, numa_aware): def map_fn(x): @@ -494,20 +509,20 @@ class MapAndBatchTest(test_base.DatasetTestBase, parameterized.TestCase): options = dataset_ops.Options() options.experimental_numa_aware = True dataset = dataset.with_options(options) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) get_next = iterator.get_next() with self.cached_session() as sess: for i in range(10): print("Case %d" % i) if i < 5: self.assertAllEqual([i * 10 + j + 1 for j in range(10)], - sess.run(get_next)) + self.evaluate(get_next)) else: self.assertAllEqual( [((i * 10) + j) * ((i * 10) + j) for j in range(10)], - sess.run(get_next)) + self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) if __name__ == "__main__": diff --git a/tensorflow/python/data/experimental/kernel_tests/map_defun_op_test.py b/tensorflow/python/data/experimental/kernel_tests/map_defun_op_test.py index 11694540fa..6042ca1c63 100644 --- a/tensorflow/python/data/experimental/kernel_tests/map_defun_op_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/map_defun_op_test.py @@ -218,7 +218,7 @@ class MapDefunTest(test_base.DatasetTestBase): def _assert_op_cancelled(self, sess, map_defun_op): with self.assertRaisesRegexp(errors.CancelledError, "was cancelled"): - sess.run(map_defun_op) + self.evaluate(map_defun_op) def testMapDefunWithParentCancellation(self): # Checks that a cancellation of the parent graph is threaded through to @@ -260,10 +260,10 @@ class MapDefunBenchmark(test.Benchmark): with session.Session() as sess: # Warm up the session for _ in range(5): - sess.run(op) + self.evaluate(op) start = time.time() for _ in range(num_iters): - sess.run(op) + self.evaluate(op) end = time.time() mean_us = (end - start) * 1e6 / num_iters self.report_benchmark( diff --git a/tensorflow/python/data/kernel_tests/matching_files_dataset_op_test.py b/tensorflow/python/data/experimental/kernel_tests/matching_files_test.py similarity index 57% rename from tensorflow/python/data/kernel_tests/matching_files_dataset_op_test.py rename to tensorflow/python/data/experimental/kernel_tests/matching_files_test.py index 4d86ec4228..0ee7616d35 100644 --- a/tensorflow/python/data/kernel_tests/matching_files_dataset_op_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/matching_files_test.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Tests for the experimental input pipeline ops.""" +"""Tests for the private `MatchingFilesDataset`.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function @@ -20,20 +20,17 @@ from __future__ import print_function import os import shutil import tempfile -import time -import numpy as np - -from tensorflow.python.client import session +from tensorflow.python.data.experimental.ops import matching_files from tensorflow.python.data.kernel_tests import test_base -from tensorflow.python.data.ops.dataset_ops import MatchingFilesDataset +from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import errors -from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.platform import test from tensorflow.python.util import compat -class MatchingFilesDatasetTest(test_base.DatasetTestBase): +class MatchingFilesTest(test_base.DatasetTestBase): def setUp(self): self.tmp_dir = tempfile.mkdtemp() @@ -45,34 +42,40 @@ class MatchingFilesDatasetTest(test_base.DatasetTestBase): for filename in filenames: open(os.path.join(self.tmp_dir, filename), 'a').close() + @test_util.run_deprecated_v1 def testNonExistingDirectory(self): - """Test the MatchingFiles dataset with a non-existing directory""" + """Test the MatchingFiles dataset with a non-existing directory.""" self.tmp_dir = os.path.join(self.tmp_dir, 'nonexistingdir') - dataset = MatchingFilesDataset(os.path.join(self.tmp_dir, '*')) + dataset = matching_files.MatchingFilesDataset( + os.path.join(self.tmp_dir, '*')) with self.cached_session() as sess: - next_element = dataset.make_one_shot_iterator().get_next() + next_element = dataset_ops.make_one_shot_iterator(dataset).get_next() with self.assertRaises(errors.NotFoundError): sess.run(next_element) + @test_util.run_deprecated_v1 def testEmptyDirectory(self): - """Test the MatchingFiles dataset with an empty directory""" + """Test the MatchingFiles dataset with an empty directory.""" - dataset = MatchingFilesDataset(os.path.join(self.tmp_dir, '*')) + dataset = matching_files.MatchingFilesDataset( + os.path.join(self.tmp_dir, '*')) with self.cached_session() as sess: - next_element = dataset.make_one_shot_iterator().get_next() + next_element = dataset_ops.make_one_shot_iterator(dataset).get_next() with self.assertRaises(errors.NotFoundError): sess.run(next_element) + @test_util.run_deprecated_v1 def testSimpleDirectory(self): - """Test the MatchingFiles dataset with a simple directory""" + """Test the MatchingFiles dataset with a simple directory.""" filenames = ['a', 'b', 'c'] self._touchTempFiles(filenames) - dataset = MatchingFilesDataset(os.path.join(self.tmp_dir, '*')) + dataset = matching_files.MatchingFilesDataset( + os.path.join(self.tmp_dir, '*')) with self.cached_session() as sess: - next_element = dataset.make_one_shot_iterator().get_next() + next_element = dataset_ops.make_one_shot_iterator(dataset).get_next() expected_filenames = [] actual_filenames = [] @@ -85,15 +88,17 @@ class MatchingFilesDatasetTest(test_base.DatasetTestBase): with self.assertRaises(errors.OutOfRangeError): sess.run(next_element) + @test_util.run_deprecated_v1 def testFileSuffixes(self): - """Test the MatchingFiles dataset using the suffixes of filename""" + """Test the MatchingFiles dataset using the suffixes of filename.""" filenames = ['a.txt', 'b.py', 'c.py', 'd.pyc'] self._touchTempFiles(filenames) - dataset = MatchingFilesDataset(os.path.join(self.tmp_dir, '*.py')) + dataset = matching_files.MatchingFilesDataset( + os.path.join(self.tmp_dir, '*.py')) with self.cached_session() as sess: - next_element = dataset.make_one_shot_iterator().get_next() + next_element = dataset_ops.make_one_shot_iterator(dataset).get_next() expected_filenames = [] actual_filenames = [] for filename in filenames[1:-1]: @@ -105,15 +110,17 @@ class MatchingFilesDatasetTest(test_base.DatasetTestBase): with self.assertRaises(errors.OutOfRangeError): sess.run(next_element) + @test_util.run_deprecated_v1 def testFileMiddles(self): - """Test the MatchingFiles dataset using the middles of filename""" + """Test the MatchingFiles dataset using the middles of filename.""" filenames = ['aa.txt', 'bb.py', 'bbc.pyc', 'cc.pyc'] self._touchTempFiles(filenames) - dataset = MatchingFilesDataset(os.path.join(self.tmp_dir, 'b*.py*')) + dataset = matching_files.MatchingFilesDataset( + os.path.join(self.tmp_dir, 'b*.py*')) with self.cached_session() as sess: - next_element = dataset.make_one_shot_iterator().get_next() + next_element = dataset_ops.make_one_shot_iterator(dataset).get_next() expected_filenames = [] actual_filenames = [] for filename in filenames[1:3]: @@ -125,8 +132,9 @@ class MatchingFilesDatasetTest(test_base.DatasetTestBase): with self.assertRaises(errors.OutOfRangeError): sess.run(next_element) + @test_util.run_deprecated_v1 def testNestedDirectories(self): - """Test the MatchingFiles dataset with nested directories""" + """Test the MatchingFiles dataset with nested directories.""" filenames = [] width = 8 @@ -147,9 +155,9 @@ class MatchingFilesDatasetTest(test_base.DatasetTestBase): suffix) for suffix in ['*.txt', '*.log'] ] - dataset = MatchingFilesDataset(patterns) + dataset = matching_files.MatchingFilesDataset(patterns) with self.cached_session() as sess: - next_element = dataset.make_one_shot_iterator().get_next() + next_element = dataset_ops.make_one_shot_iterator(dataset).get_next() expected_filenames = [ compat.as_bytes(filename) for filename in filenames @@ -165,70 +173,5 @@ class MatchingFilesDatasetTest(test_base.DatasetTestBase): self.assertItemsEqual(expected_filenames, actual_filenames) -class MatchingFilesDatasetBenchmark(test.Benchmark): - - def benchmarkNestedDirectories(self): - tmp_dir = tempfile.mkdtemp() - width = 500 - depth = 10 - for i in range(width): - for j in range(depth): - new_base = os.path.join(tmp_dir, str(i), - *[str(dir_name) for dir_name in range(j)]) - os.makedirs(new_base) - child_files = ['a.py', 'b.pyc'] if j < depth - 1 else ['c.txt', 'd.log'] - for f in child_files: - filename = os.path.join(new_base, f) - open(filename, 'w').close() - - patterns = [ - os.path.join(tmp_dir, os.path.join(*['**' - for _ in range(depth)]), suffix) - for suffix in ['*.txt', '*.log'] - ] - - deltas = [] - iters = 3 - for _ in range(iters): - with ops.Graph().as_default(): - dataset = MatchingFilesDataset(patterns) - next_element = dataset.make_one_shot_iterator().get_next() - - with session.Session() as sess: - sub_deltas = [] - while True: - try: - start = time.time() - sess.run(next_element) - end = time.time() - sub_deltas.append(end - start) - except errors.OutOfRangeError: - break - deltas.append(sub_deltas) - - median_deltas = np.median(deltas, axis=0) - print('Nested directory size (width*depth): %d*%d Median wall time: ' - '%fs (read first filename), %fs (read second filename), avg %fs' - ' (read %d more filenames)' % - (width, depth, median_deltas[0], median_deltas[1], - np.average(median_deltas[2:]), len(median_deltas) - 2)) - self.report_benchmark( - iters=iters, - wall_time=np.sum(median_deltas), - extras={ - 'read first file:': - median_deltas[0], - 'read second file:': - median_deltas[1], - 'avg time for reading %d more filenames:' % - (len(median_deltas) - 2): - np.average(median_deltas[2:]) - }, - name='benchmark_matching_files_dataset_nesteddirectory(%d*%d)' % - (width, depth)) - - shutil.rmtree(tmp_dir, ignore_errors=True) - - if __name__ == '__main__': test.main() diff --git a/tensorflow/python/data/experimental/kernel_tests/optimization/BUILD b/tensorflow/python/data/experimental/kernel_tests/optimization/BUILD index 9946ef5a42..f214944254 100644 --- a/tensorflow/python/data/experimental/kernel_tests/optimization/BUILD +++ b/tensorflow/python/data/experimental/kernel_tests/optimization/BUILD @@ -42,6 +42,7 @@ py_test( "//tensorflow/python:errors", "//tensorflow/python:math_ops", "//tensorflow/python/data/experimental/ops:optimization", + "//tensorflow/python/data/experimental/ops:optimization_options", "//tensorflow/python/data/kernel_tests:test_base", "//tensorflow/python/data/ops:dataset_ops", "@absl_py//absl/testing:parameterized", @@ -68,6 +69,7 @@ py_test( "//tensorflow/python:math_ops", "//tensorflow/python:random_ops", "//tensorflow/python/data/experimental/ops:optimization", + "//tensorflow/python/data/experimental/ops:optimization_options", "//tensorflow/python/data/kernel_tests:test_base", "//tensorflow/python/data/ops:dataset_ops", "@absl_py//absl/testing:parameterized", @@ -127,6 +129,7 @@ py_test( "//tensorflow/python:client_testlib", "//tensorflow/python:errors", "//tensorflow/python/data/experimental/ops:optimization", + "//tensorflow/python/data/experimental/ops:optimization_options", "//tensorflow/python/data/kernel_tests:test_base", "//tensorflow/python/data/ops:dataset_ops", ], @@ -148,6 +151,7 @@ py_test( "//tensorflow/python:errors", "//tensorflow/python:math_ops", "//tensorflow/python/data/experimental/ops:optimization", + "//tensorflow/python/data/experimental/ops:optimization_options", "//tensorflow/python/data/kernel_tests:test_base", "//tensorflow/python/data/ops:dataset_ops", "@absl_py//absl/testing:parameterized", @@ -167,6 +171,7 @@ py_test( "//tensorflow/python:client_testlib", "//tensorflow/python:errors", "//tensorflow/python/data/experimental/ops:optimization", + "//tensorflow/python/data/experimental/ops:optimization_options", "//tensorflow/python/data/kernel_tests:test_base", "//tensorflow/python/data/ops:dataset_ops", "@absl_py//absl/testing:parameterized", @@ -192,6 +197,7 @@ py_test( "//tensorflow/python:math_ops", "//tensorflow/python:random_ops", "//tensorflow/python/data/experimental/ops:optimization", + "//tensorflow/python/data/experimental/ops:optimization_options", "//tensorflow/python/data/kernel_tests:test_base", "//tensorflow/python/data/ops:dataset_ops", "@absl_py//absl/testing:parameterized", @@ -202,6 +208,7 @@ py_test( name = "map_vectorization_test", size = "medium", srcs = ["map_vectorization_test.py"], + shard_count = 8, srcs_version = "PY2AND3", tags = [ "no_oss", @@ -220,15 +227,15 @@ py_test( "//tensorflow/python:dtypes", "//tensorflow/python:errors", "//tensorflow/python:framework_ops", + "//tensorflow/python:framework_test_lib", "//tensorflow/python:math_ops", - "//tensorflow/python:nn_ops", + "//tensorflow/python:nn", "//tensorflow/python:parsing_ops", - "//tensorflow/python:session", "//tensorflow/python:sparse_tensor", "//tensorflow/python/data/experimental/ops:optimization", + "//tensorflow/python/data/experimental/ops:optimization_options", "//tensorflow/python/data/kernel_tests:test_base", "//tensorflow/python/data/ops:dataset_ops", - "//tensorflow/python/data/util:nest", "//third_party/py/numpy", "@absl_py//absl/testing:parameterized", ], @@ -248,12 +255,9 @@ py_test( deps = [ "//tensorflow/python:client_testlib", "//tensorflow/python:errors", - "//tensorflow/python:math_ops", - "//tensorflow/python/data/experimental/ops:batching", "//tensorflow/python/data/experimental/ops:optimization", "//tensorflow/python/data/kernel_tests:test_base", "//tensorflow/python/data/ops:dataset_ops", - "//third_party/py/numpy", "@absl_py//absl/testing:parameterized", ], ) @@ -275,6 +279,7 @@ py_test( "//tensorflow/python:errors", "//tensorflow/python:math_ops", "//tensorflow/python/data/experimental/ops:optimization", + "//tensorflow/python/data/experimental/ops:optimization_options", "//tensorflow/python/data/kernel_tests:test_base", "//tensorflow/python/data/ops:dataset_ops", ], @@ -296,10 +301,17 @@ py_test( "//tensorflow/python:dtypes", "//tensorflow/python:errors", "//tensorflow/python:random_ops", + "//tensorflow/python:variable_scope", + "//tensorflow/python/data/experimental/ops:batching", + "//tensorflow/python/data/experimental/ops:grouping", "//tensorflow/python/data/experimental/ops:optimization", + "//tensorflow/python/data/experimental/ops:optimization_options", + "//tensorflow/python/data/experimental/ops:scan_ops", "//tensorflow/python/data/kernel_tests:test_base", "//tensorflow/python/data/ops:dataset_ops", + "//tensorflow/python/eager:context", "//third_party/py/numpy", + "@absl_py//absl/testing:parameterized", ], ) @@ -316,6 +328,7 @@ py_test( "//tensorflow/python:client_testlib", "//tensorflow/python:errors", "//tensorflow/python/data/experimental/ops:optimization", + "//tensorflow/python/data/experimental/ops:optimization_options", "//tensorflow/python/data/kernel_tests:test_base", "//tensorflow/python/data/ops:dataset_ops", ], diff --git a/tensorflow/python/data/experimental/kernel_tests/optimization/assert_next_dataset_test.py b/tensorflow/python/data/experimental/kernel_tests/optimization/assert_next_dataset_test.py index ed719a0ce9..9b8248a78d 100644 --- a/tensorflow/python/data/experimental/kernel_tests/optimization/assert_next_dataset_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/optimization/assert_next_dataset_test.py @@ -21,32 +21,27 @@ from tensorflow.python.data.experimental.ops import optimization from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import errors +from tensorflow.python.framework import test_util from tensorflow.python.platform import test +@test_util.run_all_in_graph_and_eager_modes class AssertNextDatasetTest(test_base.DatasetTestBase): def testAssertNext(self): dataset = dataset_ops.Dataset.from_tensors(0).apply( optimization.assert_next(["Map"])).map(lambda x: x) - iterator = dataset.make_one_shot_iterator() - get_next = iterator.get_next() - - with self.cached_session() as sess: - self.assertEqual(0, sess.run(get_next)) + self.assertDatasetProduces(dataset, expected_output=[0]) def testAssertNextInvalid(self): dataset = dataset_ops.Dataset.from_tensors(0).apply( optimization.assert_next(["Whoops"])).map(lambda x: x) - iterator = dataset.make_one_shot_iterator() - get_next = iterator.get_next() - - with self.cached_session() as sess: - with self.assertRaisesRegexp( - errors.InvalidArgumentError, - "Asserted Whoops transformation at offset 0 but encountered " - "Map transformation instead."): - sess.run(get_next) + self.assertDatasetProduces( + dataset, + expected_error=( + errors.InvalidArgumentError, + "Asserted Whoops transformation at offset 0 but encountered " + "Map transformation instead.")) def testAssertNextShort(self): dataset = dataset_ops.Dataset.from_tensors(0).apply( @@ -54,14 +49,11 @@ class AssertNextDatasetTest(test_base.DatasetTestBase): options = dataset_ops.Options() options.experimental_autotune = False dataset = dataset.with_options(options) - iterator = dataset.make_one_shot_iterator() - get_next = iterator.get_next() - - with self.cached_session() as sess: - with self.assertRaisesRegexp( - errors.InvalidArgumentError, - "Asserted next 2 transformations but encountered only 1."): - sess.run(get_next) + self.assertDatasetProduces( + dataset, + expected_error=( + errors.InvalidArgumentError, + "Asserted next 2 transformations but encountered only 1.")) if __name__ == "__main__": diff --git a/tensorflow/python/data/experimental/kernel_tests/optimization/filter_fusion_test.py b/tensorflow/python/data/experimental/kernel_tests/optimization/filter_fusion_test.py index 80a0d879dc..7371cf31df 100644 --- a/tensorflow/python/data/experimental/kernel_tests/optimization/filter_fusion_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/optimization/filter_fusion_test.py @@ -20,11 +20,12 @@ from __future__ import print_function from absl.testing import parameterized from tensorflow.python.data.experimental.ops import optimization +from tensorflow.python.data.experimental.ops.optimization_options import OptimizationOptions from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes -from tensorflow.python.framework import errors +from tensorflow.python.framework import test_util from tensorflow.python.ops import math_ops from tensorflow.python.platform import test @@ -58,6 +59,7 @@ def _filter_fusion_test_cases(): return tuple(tests) +@test_util.run_all_in_graph_and_eager_modes class FilterFusionTest(test_base.DatasetTestBase, parameterized.TestCase): @parameterized.named_parameters(*_filter_fusion_test_cases()) @@ -70,28 +72,25 @@ class FilterFusionTest(test_base.DatasetTestBase, parameterized.TestCase): dataset = dataset.cache() options = dataset_ops.Options() - options.experimental_filter_fusion = True + options.experimental_optimization = OptimizationOptions() + options.experimental_optimization.filter_fusion = True dataset = dataset.with_options(options) - iterator = dataset.make_one_shot_iterator() - get_next = iterator.get_next() - with self.cached_session() as sess: - for x in range(5): - r = map_function(x) - filtered = False - for predicate in predicates: - if isinstance(r, tuple): - b = predicate(*r) # Pass tuple as multiple arguments. - else: - b = predicate(r) - if not sess.run(b): - filtered = True - break + expected_output = [] + for x in range(5): + r = map_function(x) + filtered = False + for predicate in predicates: + if isinstance(r, tuple): + b = predicate(*r) # Pass tuple as multiple arguments. + else: + b = predicate(r) + if not self.evaluate(b): + filtered = True + break - if not filtered: - result = sess.run(get_next) - self.assertAllEqual(r, result) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + if not filtered: + expected_output.append(r) + self.assertDatasetProduces(dataset, expected_output=expected_output) if __name__ == "__main__": diff --git a/tensorflow/python/data/experimental/kernel_tests/optimization/hoist_random_uniform_test.py b/tensorflow/python/data/experimental/kernel_tests/optimization/hoist_random_uniform_test.py index 9f7fbfeba0..5f3a8683fb 100644 --- a/tensorflow/python/data/experimental/kernel_tests/optimization/hoist_random_uniform_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/optimization/hoist_random_uniform_test.py @@ -20,12 +20,15 @@ from __future__ import print_function from absl.testing import parameterized from tensorflow.python.data.experimental.ops import optimization +from tensorflow.python.data.experimental.ops.optimization_options import OptimizationOptions from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.eager import context from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import random_ops @@ -58,23 +61,29 @@ def _hoist_random_uniform_test_cases(): return tuple(tests) +@test_util.run_all_in_graph_and_eager_modes class HoistRandomUniformTest(test_base.DatasetTestBase, parameterized.TestCase): def _testDataset(self, dataset): - iterator = dataset.make_one_shot_iterator() - get_next = iterator.get_next() previous_result = 0 - with self.cached_session() as sess: - for _ in range(5): - result = sess.run(get_next) - self.assertLessEqual(1, result) - self.assertLessEqual(result, 10) - # This checks if the result is somehow random by checking if we are not - # generating the same values. - self.assertNotEqual(previous_result, result) - previous_result = result - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + if context.executing_eagerly(): + iterator = dataset.__iter__() + get_next = iterator._next_internal # pylint: disable=protected-access + else: + iterator = dataset_ops.make_one_shot_iterator(dataset) + get_next = iterator.get_next + for _ in range(5): + result = self.evaluate(get_next()) + self.assertLessEqual(1, result) + self.assertLessEqual(result, 10) + # This checks if the result is somehow random by checking if we are not + # generating the same values. + self.assertNotEqual(previous_result, result) + previous_result = result + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) @parameterized.named_parameters(*_hoist_random_uniform_test_cases()) def testHoisting(self, function, will_optimize): @@ -83,7 +92,8 @@ class HoistRandomUniformTest(test_base.DatasetTestBase, parameterized.TestCase): ["Zip[0]", "Map"] if will_optimize else ["Map"])).map(function) options = dataset_ops.Options() - options.experimental_hoist_random_uniform = True + options.experimental_optimization = OptimizationOptions() + options.experimental_optimization.hoist_random_uniform = True dataset = dataset.with_options(options) self._testDataset(dataset) @@ -99,7 +109,8 @@ class HoistRandomUniformTest(test_base.DatasetTestBase, parameterized.TestCase): dataset = dataset_ops.Dataset.range(5).apply( optimization.assert_next(["Zip[0]", "Map"])).map(random_with_capture) options = dataset_ops.Options() - options.experimental_hoist_random_uniform = True + options.experimental_optimization = OptimizationOptions() + options.experimental_optimization.hoist_random_uniform = True dataset = dataset.with_options(options) self._testDataset(dataset) diff --git a/tensorflow/python/data/experimental/kernel_tests/optimization/latency_all_edges_test.py b/tensorflow/python/data/experimental/kernel_tests/optimization/latency_all_edges_test.py index 7144d834f9..fc65f52704 100644 --- a/tensorflow/python/data/experimental/kernel_tests/optimization/latency_all_edges_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/optimization/latency_all_edges_test.py @@ -22,10 +22,11 @@ from tensorflow.python.data.experimental.ops import optimization from tensorflow.python.data.experimental.ops import stats_aggregator from tensorflow.python.data.experimental.ops import stats_options from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.framework import errors +from tensorflow.python.framework import test_util from tensorflow.python.platform import test +@test_util.run_all_in_graph_and_eager_modes class LatencyAllEdgesTest(stats_dataset_test_base.StatsDatasetTestBase): def testLatencyStatsOptimization(self): @@ -39,22 +40,18 @@ class LatencyAllEdgesTest(stats_dataset_test_base.StatsDatasetTestBase): options.experimental_stats.latency_all_edges = True options.experimental_stats.aggregator = aggregator dataset = dataset.with_options(options) - iterator = dataset.make_initializable_iterator() - get_next = iterator.get_next() + self.assertDatasetProduces( + dataset, + expected_output=[1], + requires_initialization=True, + num_test_iterations=1) summary_t = aggregator.get_summary() - - with self.cached_session() as sess: - sess.run(iterator.initializer) - self.assertEqual(1 * 1, sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - summary_str = sess.run(summary_t) - self._assertSummaryHasCount(summary_str, - "record_latency_TensorDataset/_1", 1) - self._assertSummaryHasCount(summary_str, "record_latency_MapDataset/_4", - 1) - self._assertSummaryHasCount(summary_str, - "record_latency_PrefetchDataset/_6", 1) + summary_str = self.evaluate(summary_t) + self._assertSummaryHasCount(summary_str, "record_latency_TensorDataset/_1", + 1) + self._assertSummaryHasCount(summary_str, "record_latency_MapDataset/_4", 1) + self._assertSummaryHasCount(summary_str, + "record_latency_PrefetchDataset/_6", 1) def testLatencyStatsOptimizationV2(self): aggregator = stats_aggregator.StatsAggregator() @@ -63,24 +60,21 @@ class LatencyAllEdgesTest(stats_dataset_test_base.StatsDatasetTestBase): ["LatencyStats", "Map", "LatencyStats", "Prefetch", "LatencyStats"])).map(lambda x: x * x).prefetch(1) options = dataset_ops.Options() - options.experimental_stats = stats_options.StatsOptions(aggregator) + options.experimental_stats = stats_options.StatsOptions() + options.experimental_stats.aggregator = aggregator dataset = dataset.with_options(options) - iterator = dataset.make_initializable_iterator() - get_next = iterator.get_next() + self.assertDatasetProduces( + dataset, + expected_output=[1], + requires_initialization=True, + num_test_iterations=1) summary_t = aggregator.get_summary() - - with self.cached_session() as sess: - sess.run(iterator.initializer) - self.assertEqual(1, sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - summary_str = sess.run(summary_t) - self._assertSummaryHasCount(summary_str, - "record_latency_TensorDataset/_1", 1) - self._assertSummaryHasCount(summary_str, "record_latency_MapDataset/_4", - 1) - self._assertSummaryHasCount(summary_str, - "record_latency_PrefetchDataset/_6", 1) + summary_str = self.evaluate(summary_t) + self._assertSummaryHasCount(summary_str, "record_latency_TensorDataset/_1", + 1) + self._assertSummaryHasCount(summary_str, "record_latency_MapDataset/_4", 1) + self._assertSummaryHasCount(summary_str, + "record_latency_PrefetchDataset/_6", 1) if __name__ == "__main__": diff --git a/tensorflow/python/data/experimental/kernel_tests/optimization/make_numa_aware_test.py b/tensorflow/python/data/experimental/kernel_tests/optimization/make_numa_aware_test.py index 6191a7db08..2386dd5f11 100644 --- a/tensorflow/python/data/experimental/kernel_tests/optimization/make_numa_aware_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/optimization/make_numa_aware_test.py @@ -21,10 +21,11 @@ from tensorflow.python.data.experimental.ops import batching from tensorflow.python.data.experimental.ops import optimization from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.framework import errors +from tensorflow.python.framework import test_util from tensorflow.python.platform import test +@test_util.run_all_in_graph_and_eager_modes class MakeNumaAwareTest(test_base.DatasetTestBase): def testMakeNumaAware(self): @@ -34,13 +35,8 @@ class MakeNumaAwareTest(test_base.DatasetTestBase): options = dataset_ops.Options() options.experimental_numa_aware = True dataset = dataset.with_options(options) - iterator = dataset.make_one_shot_iterator() - get_next = iterator.get_next() - - with self.cached_session() as sess: - self.assertAllEqual([x * x for x in range(10)], sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.assertDatasetProduces( + dataset, expected_output=[[x * x for x in range(10)]]) if __name__ == "__main__": diff --git a/tensorflow/python/data/experimental/kernel_tests/optimization/map_and_batch_fusion_test.py b/tensorflow/python/data/experimental/kernel_tests/optimization/map_and_batch_fusion_test.py index ddf3cbbcc3..801f664f09 100644 --- a/tensorflow/python/data/experimental/kernel_tests/optimization/map_and_batch_fusion_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/optimization/map_and_batch_fusion_test.py @@ -18,12 +18,14 @@ from __future__ import division from __future__ import print_function from tensorflow.python.data.experimental.ops import optimization +from tensorflow.python.data.experimental.ops.optimization_options import OptimizationOptions from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.framework import errors +from tensorflow.python.framework import test_util from tensorflow.python.platform import test +@test_util.run_all_in_graph_and_eager_modes class MapAndBatchFusionTest(test_base.DatasetTestBase): def testMapAndBatchFusion(self): @@ -31,15 +33,11 @@ class MapAndBatchFusionTest(test_base.DatasetTestBase): optimization.assert_next( ["MapAndBatch"])).map(lambda x: x * x).batch(10) options = dataset_ops.Options() - options.experimental_map_and_batch_fusion = True + options.experimental_optimization = OptimizationOptions() + options.experimental_optimization.map_and_batch_fusion = True dataset = dataset.with_options(options) - iterator = dataset.make_one_shot_iterator() - get_next = iterator.get_next() - - with self.cached_session() as sess: - self.assertAllEqual([x * x for x in range(10)], sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.assertDatasetProduces( + dataset, expected_output=[[x * x for x in range(10)]]) if __name__ == "__main__": diff --git a/tensorflow/python/data/experimental/kernel_tests/optimization/map_and_filter_fusion_test.py b/tensorflow/python/data/experimental/kernel_tests/optimization/map_and_filter_fusion_test.py index 3b4ca62340..db8f214fbf 100644 --- a/tensorflow/python/data/experimental/kernel_tests/optimization/map_and_filter_fusion_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/optimization/map_and_filter_fusion_test.py @@ -20,11 +20,12 @@ from __future__ import print_function from absl.testing import parameterized from tensorflow.python.data.experimental.ops import optimization +from tensorflow.python.data.experimental.ops.optimization_options import OptimizationOptions from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes -from tensorflow.python.framework import errors +from tensorflow.python.framework import test_util from tensorflow.python.ops import math_ops from tensorflow.python.platform import test @@ -62,23 +63,20 @@ def _map_and_filter_fusion_test_cases(): return tuple(tests) +@test_util.run_all_in_graph_and_eager_modes class MapAndFilterFusionTest(test_base.DatasetTestBase, parameterized.TestCase): def _testMapAndFilter(self, dataset, function, predicate): - iterator = dataset.make_one_shot_iterator() - get_next = iterator.get_next() - with self.cached_session() as sess: - for x in range(10): - r = function(x) - if isinstance(r, tuple): - b = predicate(*r) # Pass tuple as multiple arguments. - else: - b = predicate(r) - if sess.run(b): - result = sess.run(get_next) - self.assertAllEqual(r, result) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + expected_output = [] + for x in range(10): + r = function(x) + if isinstance(r, tuple): + b = predicate(*r) # Pass tuple as multiple arguments. + else: + b = predicate(r) + if self.evaluate(b): + expected_output.append(r) + self.assertDatasetProduces(dataset, expected_output=expected_output) @parameterized.named_parameters(*_map_and_filter_fusion_test_cases()) def testMapFilterFusion(self, function, predicate): @@ -86,7 +84,8 @@ class MapAndFilterFusionTest(test_base.DatasetTestBase, parameterized.TestCase): optimization.assert_next( ["Map", "FilterByLastComponent"])).map(function).filter(predicate) options = dataset_ops.Options() - options.experimental_map_and_filter_fusion = True + options.experimental_optimization = OptimizationOptions() + options.experimental_optimization.map_and_filter_fusion = True dataset = dataset.with_options(options) self._testMapAndFilter(dataset, function, predicate) @@ -104,7 +103,8 @@ class MapAndFilterFusionTest(test_base.DatasetTestBase, parameterized.TestCase): optimization.assert_next(["Map", "Filter"])).map(function).filter(predicate) options = dataset_ops.Options() - options.experimental_map_and_filter_fusion = True + options.experimental_optimization = OptimizationOptions() + options.experimental_optimization.map_and_filter_fusion = True dataset = dataset.with_options(options) self._testMapAndFilter(dataset, function, predicate) diff --git a/tensorflow/python/data/experimental/kernel_tests/optimization/map_fusion_test.py b/tensorflow/python/data/experimental/kernel_tests/optimization/map_fusion_test.py index ec63ad7200..d8d6390374 100644 --- a/tensorflow/python/data/experimental/kernel_tests/optimization/map_fusion_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/optimization/map_fusion_test.py @@ -20,9 +20,10 @@ from __future__ import print_function from absl.testing import parameterized from tensorflow.python.data.experimental.ops import optimization +from tensorflow.python.data.experimental.ops.optimization_options import OptimizationOptions from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.framework import errors +from tensorflow.python.framework import test_util from tensorflow.python.platform import test @@ -62,6 +63,7 @@ def _map_fusion_test_cases(): return tuple(tests) +@test_util.run_all_in_graph_and_eager_modes class MapFusionTest(test_base.DatasetTestBase, parameterized.TestCase): @parameterized.named_parameters(*_map_fusion_test_cases()) @@ -73,23 +75,19 @@ class MapFusionTest(test_base.DatasetTestBase, parameterized.TestCase): dataset = dataset.cache() options = dataset_ops.Options() - options.experimental_map_fusion = True + options.experimental_optimization = OptimizationOptions() + options.experimental_optimization.map_fusion = True dataset = dataset.with_options(options) - iterator = dataset.make_one_shot_iterator() - get_next = iterator.get_next() - with self.cached_session() as sess: - for x in range(5): - result = sess.run(get_next) - r = x - for function in functions: - if isinstance(r, tuple): - r = function(*r) # Pass tuple as multiple arguments. - else: - r = function(r) - self.assertAllEqual(r, result) - - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + expected_output = [] + for x in range(5): + r = x + for function in functions: + if isinstance(r, tuple): + r = function(*r) # Pass tuple as multiple arguments. + else: + r = function(r) + expected_output.append(r) + self.assertDatasetProduces(dataset, expected_output=expected_output) if __name__ == "__main__": diff --git a/tensorflow/python/data/experimental/kernel_tests/optimization/map_parallelization_test.py b/tensorflow/python/data/experimental/kernel_tests/optimization/map_parallelization_test.py index c95f7b2eb1..0ff3fff4f8 100644 --- a/tensorflow/python/data/experimental/kernel_tests/optimization/map_parallelization_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/optimization/map_parallelization_test.py @@ -20,11 +20,12 @@ from __future__ import print_function from absl.testing import parameterized from tensorflow.python.data.experimental.ops import optimization +from tensorflow.python.data.experimental.ops.optimization_options import OptimizationOptions from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import dtypes -from tensorflow.python.framework import errors from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import random_ops @@ -58,6 +59,7 @@ def _map_parallelization_test_cases(): ("AssertWithRandom", assert_with_random, False)) +@test_util.run_all_in_graph_and_eager_modes class MapParallelizationTest(test_base.DatasetTestBase, parameterized.TestCase): @parameterized.named_parameters(*_map_parallelization_test_cases()) @@ -66,23 +68,12 @@ class MapParallelizationTest(test_base.DatasetTestBase, parameterized.TestCase): dataset = dataset_ops.Dataset.range(5).apply( optimization.assert_next(next_nodes)).map(function) options = dataset_ops.Options() - options.experimental_map_parallelization = True + options.experimental_optimization = OptimizationOptions() + options.experimental_optimization.map_parallelization = True dataset = dataset.with_options(options) - iterator = dataset.make_one_shot_iterator() - get_next = iterator.get_next() - - with self.test_session() as sess: - for x in range(5): - result = sess.run(get_next) - # No need to run the pipeline if it was not optimized. Also the results - # might be hard to check because of random. - if not should_optimize: - return - r = function(x) - self.assertAllEqual(r, result) - - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + if should_optimize: + self.assertDatasetProduces( + dataset, expected_output=[function(x) for x in range(5)]) if __name__ == "__main__": diff --git a/tensorflow/python/data/experimental/kernel_tests/optimization/map_vectorization_test.py b/tensorflow/python/data/experimental/kernel_tests/optimization/map_vectorization_test.py index f10b66ff69..c2e08e2cd8 100644 --- a/tensorflow/python/data/experimental/kernel_tests/optimization/map_vectorization_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/optimization/map_vectorization_test.py @@ -17,23 +17,21 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -import time - from absl.testing import parameterized import numpy as np from tensorflow.core.example import example_pb2 from tensorflow.core.example import feature_pb2 -from tensorflow.python.client import session from tensorflow.python.data.experimental.ops import optimization +from tensorflow.python.data.experimental.ops.optimization_options import OptimizationOptions from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.data.util import nest from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors from tensorflow.python.framework import ops from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import bitwise_ops from tensorflow.python.ops import check_ops @@ -319,6 +317,7 @@ def _generate_optimization_test_cases(): } for x in test_cases for num_parallel_calls in (None, 12)] +@test_util.run_all_in_graph_and_eager_modes class MapVectorizationTest(test_base.DatasetTestBase, parameterized.TestCase): def _get_test_datasets(self, @@ -355,7 +354,8 @@ class MapVectorizationTest(test_base.DatasetTestBase, parameterized.TestCase): optimized = _make_dataset(["Batch", map_node_name] if expect_optimized else [map_node_name, "Batch"]) options = dataset_ops.Options() - options.experimental_map_vectorization = True + options.experimental_optimization = OptimizationOptions() + options.experimental_optimization.map_vectorization = True optimized = optimized.with_options(options) return unoptimized, optimized @@ -366,7 +366,8 @@ class MapVectorizationTest(test_base.DatasetTestBase, parameterized.TestCase): num_parallel_calls) self.assertDatasetsEqual(unoptimized, optimized) - def testOptimizationBadMapFn(self): + # TODO(b/117581999): Add eager coverage for the following tests. + def testSkipEagerOptimizationBadMapFn(self): # Test map functions that give an error def map_fn(x): # x has leading dimension 5, this will raise an error @@ -375,7 +376,7 @@ class MapVectorizationTest(test_base.DatasetTestBase, parameterized.TestCase): base_dataset = dataset_ops.Dataset.range(5).repeat(5).batch( 5, drop_remainder=True) _, optimized = self._get_test_datasets(base_dataset, map_fn) - nxt = optimized.make_one_shot_iterator().get_next() + nxt = dataset_ops.make_one_shot_iterator(optimized).get_next() with self.assertRaisesRegexp(errors.InvalidArgumentError, r"indices = 10 is not in \[0, 5\)"): self.evaluate(nxt) @@ -394,7 +395,8 @@ class MapVectorizationTest(test_base.DatasetTestBase, parameterized.TestCase): base_dataset, map_fn, expect_optimized=True) self.assertDatasetsEqual(optimized, unoptimized) - def testOptimizationIgnoreStateful(self): + # TODO(b/117581999): Add eager coverage for the following tests. + def testSkipEagerOptimizationIgnoreStateful(self): def map_fn(x): with ops.control_dependencies([check_ops.assert_equal(x, 0)]): @@ -420,7 +422,8 @@ class MapVectorizationTest(test_base.DatasetTestBase, parameterized.TestCase): base_dataset, map_fn, expect_optimized=False) self.assertDatasetsEqual(unoptimized, optimized) - def testOptimizationIgnoreRaggedMap(self): + # TODO(b/117581999): Add eager coverage for the following tests. + def testSkipEagerOptimizationIgnoreRaggedMap(self): # Don't optimize when the output of the map fn shapes are unknown. def map_fn(x): return array_ops.tile(x, x) @@ -434,102 +437,5 @@ class MapVectorizationTest(test_base.DatasetTestBase, parameterized.TestCase): ("IteratorGetNext", "IteratorGetNext_1", 1)]) -class MapVectorizationBenchmark(test.Benchmark): - # TODO(rachelim): Add a benchmark for more expensive transformations, such as - # vgg_preprocessing. - - def _run(self, x, num_iters=100, name=None): - deltas = [] - with session.Session() as sess: - for _ in range(5): - # Warm up session... - sess.run(x) - for _ in range(num_iters): - start = time.time() - sess.run(x) - end = time.time() - deltas.append(end - start) - median_time = np.median(deltas) - self.report_benchmark(iters=num_iters, wall_time=median_time, name=name) - return median_time - - def _compare(self, input_dataset, map_fn, batch_size, input_size, str_id): - num_elems = int(np.sum([np.prod(x) for x in input_size])) - name_template = "{}__batch_size_{}_input_element_size_{}_{}" - unoptimized = input_dataset.map(map_fn).batch(batch_size) - unoptimized_op = unoptimized.make_one_shot_iterator().get_next() - - optimized = input_dataset.map(map_fn).batch(batch_size) - options = dataset_ops.Options() - options.experimental_map_vectorization = True - optimized = optimized.with_options(options) - optimized_op = optimized.make_one_shot_iterator().get_next() - - unoptimized_time = self._run( - unoptimized_op, - name=name_template.format(str_id, batch_size, num_elems, "unoptimized")) - optimized_time = self._run( - optimized_op, - name=name_template.format(str_id, batch_size, num_elems, "optimized")) - - print("Batch size: {}\n" - "Input element size: {}\n" - "Transformation: {}\n" - "Speedup: {}\n".format(batch_size, input_size, str_id, - (unoptimized_time / optimized_time))) - - # Known cheap functions - def benchmarkIdentity(self): - self._benchmark_helper(lambda *args: [array_ops.identity(x) for x in args], - "identity") - - def benchmarkAddConst(self): - self._benchmark_helper(lambda *args: [x + 1 for x in args], "add_const") - - def benchmarkReturnConst(self): - self._benchmark_helper(lambda *args: [constant_op.constant(2)], "ret_const") - - def benchmarkSelect(self): - self._benchmark_helper(lambda *args: args[0], "select") - - def benchmarkCast(self): - self._benchmark_helper( - lambda *args: [math_ops.cast(x, dtypes.float64) for x in args], "cast") - - def benchmarkReshape(self): - self._benchmark_helper( - lambda *args: [array_ops.reshape(x, (-1, 30)) for x in args], "reshape") - - def benchmarkDecodeCSV(self): - csv_fn, csv_factory = _generate_csv_test_case() - self._benchmark_helper(csv_fn, "decode_csv", lambda: [csv_factory()]) - - def benchmarkParseSingleExample(self): - # NOTE: Since we haven't implemented a vectorizer for "SerializeSparse", - # this function is only naively vectorized. - parse_fn, parse_factory = _generate_parse_single_example_test_case() - - self._benchmark_helper(parse_fn, "parse_single_example", - lambda: [parse_factory()]) - - def _default_dataset_factory(self): - input_sizes = [(10, 10, 3), (10, 100, 300)] - for sz in input_sizes: - yield dataset_ops.Dataset.from_tensor_slices(np.random.rand(*sz)) - - def _benchmark_helper(self, map_fn, str_id, base_dataset_factory=None): - if base_dataset_factory is None: - base_dataset_factory = self._default_dataset_factory - - batch_size = 1000 - for base_dataset in base_dataset_factory(): - base_dataset = base_dataset.repeat() - input_size = [ - tuple(shape.as_list()) - for shape in nest.flatten(base_dataset.output_shapes) - ] - self._compare(base_dataset, map_fn, batch_size, input_size, str_id) - - if __name__ == "__main__": test.main() diff --git a/tensorflow/python/data/experimental/kernel_tests/optimization/model_dataset_test.py b/tensorflow/python/data/experimental/kernel_tests/optimization/model_dataset_test.py index 5b49bdf453..0f0274b41f 100644 --- a/tensorflow/python/data/experimental/kernel_tests/optimization/model_dataset_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/optimization/model_dataset_test.py @@ -17,181 +17,18 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -import time - from absl.testing import parameterized -import numpy as np -from tensorflow.python.data.experimental.ops import batching from tensorflow.python.data.experimental.ops import optimization from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import errors -from tensorflow.python.ops import math_ops from tensorflow.python.platform import test +# TODO(b/117581999): Add eager coverage for the following tests. class ModelDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): - def testModelMap(self): - k = 1024 * 1024 - dataset = dataset_ops.Dataset.from_tensors((np.random.rand(1, 4 * k), - np.random.rand(4 * k, - 1))).repeat() - dataset = dataset.map(math_ops.matmul) - dataset = dataset_ops._ModelDataset(dataset) - iterator = dataset.make_one_shot_iterator() - get_next = iterator.get_next() - - deltas = [] - with self.cached_session() as sess: - for _ in range(5): - sess.run(get_next.op) - for _ in range(100): - start = time.time() - sess.run(get_next.op) - end = time.time() - deltas.append(end - start) - - print("%f (median), %f (mean), %f (stddev), %f (min), %f (max)\n" % - (np.median(deltas), np.mean(deltas), np.std(deltas), np.min(deltas), - np.max(deltas))) - - def testModelParallelMap(self): - k = 1024 * 1024 - dataset = dataset_ops.Dataset.from_tensors((np.random.rand(1, 4 * k), - np.random.rand(4 * k, - 1))).repeat() - dataset = dataset.map( - math_ops.matmul, num_parallel_calls=optimization.AUTOTUNE) - dataset = dataset_ops._ModelDataset(dataset) - iterator = dataset.make_one_shot_iterator() - get_next = iterator.get_next() - - deltas = [] - with self.cached_session() as sess: - for _ in range(5): - sess.run(get_next.op) - for _ in range(1000): - start = time.time() - sess.run(get_next.op) - end = time.time() - deltas.append(end - start) - - print("%f (median), %f (mean), %f (stddev), %f (min), %f (max)\n" % - (np.median(deltas), np.mean(deltas), np.std(deltas), np.min(deltas), - np.max(deltas))) - - @parameterized.named_parameters( - ("Default", False), - ("NUMA", True), - ) - def testModelMapAndBatch(self, numa_aware): - batch_size = 16 - k = 1024 * 1024 - dataset = dataset_ops.Dataset.from_tensors((np.random.rand(1, 4 * k), - np.random.rand(4 * k, - 1))).repeat() - dataset = dataset.apply( - batching.map_and_batch( - math_ops.matmul, - num_parallel_calls=optimization.AUTOTUNE, - batch_size=batch_size)) - dataset = dataset_ops._ModelDataset(dataset) - options = dataset_ops.Options() - options.experimental_numa_aware = numa_aware - dataset = dataset.with_options(options) - iterator = dataset.make_one_shot_iterator() - get_next = iterator.get_next() - - deltas = [] - with self.cached_session() as sess: - for _ in range(5): - sess.run(get_next.op) - for _ in range(10): - start = time.time() - sess.run(get_next.op) - end = time.time() - deltas.append(end - start) - - print("%f (median), %f (mean), %f (stddev), %f (min), %f (max)\n" % - (np.median(deltas), np.mean(deltas), np.std(deltas), np.min(deltas), - np.max(deltas))) - - def testModelParallelInterleave(self): - k = 1024 * 1024 - dataset = dataset_ops.Dataset.from_tensors((np.random.rand(1, 4 * k), - np.random.rand(4 * k, - 1))).repeat() - dataset = dataset.map(math_ops.matmul) - dataset = dataset_ops.Dataset.range(1).repeat().interleave( - lambda _: dataset, - cycle_length=10, - num_parallel_calls=optimization.AUTOTUNE) - dataset = dataset_ops._ModelDataset(dataset) - iterator = dataset.make_one_shot_iterator() - get_next = iterator.get_next() - - deltas = [] - with self.cached_session() as sess: - for _ in range(5): - sess.run(get_next.op) - for _ in range(1000): - start = time.time() - sess.run(get_next.op) - end = time.time() - deltas.append(end - start) - - print("%f (median), %f (mean), %f (stddev), %f (min), %f (max)\n" % - (np.median(deltas), np.mean(deltas), np.std(deltas), np.min(deltas), - np.max(deltas))) - - def testModelNested(self): - k = 1024 * 1024 - a = (np.random.rand(1, 8 * k), np.random.rand(8 * k, 1)) - b = (np.random.rand(1, 4 * k), np.random.rand(4 * k, 1)) - c = (np.random.rand(1, 2 * k), np.random.rand(2 * k, 1)) - dataset = dataset_ops.Dataset.from_tensors((a, b, c)).repeat() - - def f1(a, b, c): - x, y = a - return math_ops.matmul(x, y), b, c - - def f2(a, b, c): - x, y = b - return a, math_ops.matmul(x, y), c - - def f3(a, b, c): - x, y = c - return a, b, math_ops.matmul(x, y) - - dataset = dataset.map(f1, num_parallel_calls=optimization.AUTOTUNE) - dataset = dataset_ops.Dataset.range(1).repeat().interleave( - lambda _: dataset, cycle_length=2) - - dataset = dataset.map(f2, num_parallel_calls=optimization.AUTOTUNE) - dataset = dataset_ops.Dataset.range(1).repeat().interleave( - lambda _: dataset, cycle_length=2) - - dataset = dataset.map(f3, num_parallel_calls=optimization.AUTOTUNE) - dataset = dataset_ops._ModelDataset(dataset) - iterator = dataset.make_one_shot_iterator() - get_next = iterator.get_next() - - deltas = [] - with self.cached_session() as sess: - for _ in range(5): - sess.run(get_next) - for _ in range(100): - start = time.time() - sess.run(get_next) - end = time.time() - deltas.append(end - start) - - print("%f (median), %f (mean), %f (stddev), %f (min), %f (max)\n" % - (np.median(deltas), np.mean(deltas), np.std(deltas), np.min(deltas), - np.max(deltas))) - def testAutotuneOption(self): dataset = dataset_ops.Dataset.from_tensors(0) dataset = dataset.map(lambda x: x).apply( @@ -200,13 +37,13 @@ class ModelDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): options.experimental_autotune = True dataset = dataset.with_options(options) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) get_next = iterator.get_next() with self.cached_session() as sess: - self.assertEqual(0, sess.run(get_next)) + self.assertEqual(0, self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) if __name__ == "__main__": diff --git a/tensorflow/python/data/experimental/kernel_tests/optimization/noop_elimination_test.py b/tensorflow/python/data/experimental/kernel_tests/optimization/noop_elimination_test.py index ddcd7f4da4..ce86bfa4e0 100644 --- a/tensorflow/python/data/experimental/kernel_tests/optimization/noop_elimination_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/optimization/noop_elimination_test.py @@ -18,15 +18,17 @@ from __future__ import division from __future__ import print_function from tensorflow.python.data.experimental.ops import optimization +from tensorflow.python.data.experimental.ops.optimization_options import OptimizationOptions from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes -from tensorflow.python.framework import errors +from tensorflow.python.framework import test_util from tensorflow.python.ops import math_ops from tensorflow.python.platform import test +@test_util.run_all_in_graph_and_eager_modes class NoopEliminationTest(test_base.DatasetTestBase): def testNoopElimination(self): @@ -41,18 +43,10 @@ class NoopEliminationTest(test_base.DatasetTestBase): dataset = dataset.repeat(some_tensor).skip(5).take(-1).skip(0).repeat( 1).prefetch(0).prefetch(1).cache() options = dataset_ops.Options() - options.experimental_noop_elimination = True + options.experimental_optimization = OptimizationOptions() + options.experimental_optimization.noop_elimination = True dataset = dataset.with_options(options) - iterator = dataset.make_one_shot_iterator() - get_next = iterator.get_next() - - with self.test_session() as sess: - for x in range(5): - result = sess.run(get_next) - self.assertAllEqual(result, x) - - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.assertDatasetProduces(dataset, expected_output=range(5)) if __name__ == "__main__": diff --git a/tensorflow/python/data/experimental/kernel_tests/optimization/optimize_dataset_test.py b/tensorflow/python/data/experimental/kernel_tests/optimization/optimize_dataset_test.py index 739b6a9bf4..751be83326 100644 --- a/tensorflow/python/data/experimental/kernel_tests/optimization/optimize_dataset_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/optimization/optimize_dataset_test.py @@ -17,54 +17,125 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import warnings + +from absl.testing import parameterized import numpy as np +from tensorflow.python.data.experimental.ops import batching +from tensorflow.python.data.experimental.ops import grouping from tensorflow.python.data.experimental.ops import optimization +from tensorflow.python.data.experimental.ops import optimization_options +from tensorflow.python.data.experimental.ops import scan_ops from tensorflow.python.data.experimental.ops import threadpool from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors +from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import random_ops +from tensorflow.python.ops import variable_scope from tensorflow.python.platform import test -class OptimizeDatasetTest(test_base.DatasetTestBase): +def _generate_captured_refvar_test_cases(): + """Generates testcases. + + Returns: + A list of tuples of (testcase_name, make_dataset_fn). make_dataset_fn takes + a tf.Variable as input and creates a test dataset that uses that variable. + """ + + def make_map_dataset(var): + return dataset_ops.Dataset.from_tensors(0).map(lambda x: x + var) + + def make_flat_map_dataset(var): + return dataset_ops.Dataset.from_tensors( + 0).flat_map(lambda _: dataset_ops.Dataset.from_tensors(var)) + + def make_filter_dataset(var): + return dataset_ops.Dataset.from_tensors(0).filter(lambda x: x < var) + + def make_map_and_batch_dataset(var): + + def map_fn(x): + return x + var + + return dataset_ops.Dataset.from_tensors(0).apply( + batching.map_and_batch(map_fn, 1)) + + def make_group_by_reducer_dataset(var): + reducer = grouping.Reducer( + init_func=lambda _: 0, + reduce_func=lambda x, y: x, + finalize_func=lambda _: var) + return dataset_ops.Dataset.range(5).apply( + grouping.group_by_reducer(lambda x: x % 2, reducer)) + + def make_group_by_window_dataset(var): + + def reduce_fn(key, bucket): + del key, bucket + return dataset_ops.Dataset.from_tensors(var) + + return dataset_ops.Dataset.from_tensors(0).repeat(10).apply( + grouping.group_by_window(lambda _: 0, reduce_fn, 10)) + + def make_scan_dataset(var): + return dataset_ops.Dataset.from_tensors(0).apply( + scan_ops.scan( + 0, lambda old_state, elem: (old_state + 1, elem + old_state + var))) + + return [ + # Core datasets + ("Map", make_map_dataset), + ("FlatMap", make_flat_map_dataset), + ("Filter", make_filter_dataset), + # Experimental datasets + ("MapAndBatch", make_map_and_batch_dataset), + ("GroupByReducer", make_group_by_reducer_dataset), + ("GroupByWindow", make_group_by_window_dataset), + ("Scan", make_scan_dataset) + ] + + +@test_util.run_all_in_graph_and_eager_modes +class OptimizeDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): def testOptimizationStatefulFunction(self): - dataset = dataset_ops.Dataset.range(10).map( - lambda _: random_ops.random_uniform([])).batch(10) + dataset = dataset_ops.Dataset.range( + 10).map(lambda _: random_ops.random_uniform([])).batch(10) dataset = dataset_ops._OptimizeDataset(dataset, []) - iterator = dataset.make_one_shot_iterator() - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(get_next) + get_next = self.getNext(dataset) + self.evaluate(get_next()) - def testOptimizationLargeInputFromTensor(self): + # TODO(b/117581999): Add eager coverage for the following tests. + def testSkipEagerOptimizationLargeInputFromTensor(self): input_t = array_ops.placeholder(dtypes.int32, (None, None, None)) dataset = dataset_ops.Dataset.from_tensors(input_t) dataset = dataset_ops._OptimizeDataset(dataset, []) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: sess.run(init_op, {input_t: np.ones([512, 1024, 1025], np.int32)}) - sess.run(get_next) + self.evaluate(get_next) - def testOptimizationLargeInputFromTensorSlices(self): + # TODO(b/117581999): Add eager coverage for the following tests. + def testSkipEagerOptimizationLargeInputFromTensorSlices(self): input_t = array_ops.placeholder(dtypes.int32, (None, None, None, None)) dataset = dataset_ops.Dataset.from_tensor_slices(input_t) dataset = dataset_ops._OptimizeDataset(dataset, []) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: sess.run(init_op, {input_t: np.ones([1, 512, 1024, 1025], np.int32)}) - sess.run(get_next) + self.evaluate(get_next) def testOptimizationNestedDataset(self): @@ -78,13 +149,22 @@ class OptimizeDatasetTest(test_base.DatasetTestBase): dataset = dataset_ops.Dataset.range(1) dataset = dataset.flat_map(flat_map_fn) dataset = dataset_ops._OptimizeDataset(dataset, ["noop_elimination"]) - iterator = dataset.make_one_shot_iterator() - get_next = iterator.get_next() + self.assertDatasetProduces(dataset, expected_output=[0]) - with self.cached_session() as sess: - self.assertEquals(0, sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + def testOptimizationNestedDatasetWithModifiedRetval(self): + + def flat_map_fn(_): + dataset = dataset_ops.Dataset.from_tensors(0) + dataset = dataset.apply(optimization.assert_next(["MapAndBatch"])) + # Should be fused by map and batch fusion + dataset = dataset.map(lambda x: x) + dataset = dataset.batch(1) + return dataset + + dataset = dataset_ops.Dataset.range(1) + dataset = dataset.flat_map(flat_map_fn) + dataset = dataset_ops._OptimizeDataset(dataset, ["map_and_batch_fusion"]) + self.assertDatasetProduces(dataset, expected_output=[[0]]) def testOptimizationThreadPoolDataset(self): dataset = dataset_ops.Dataset.range(10).batch(10) @@ -95,14 +175,10 @@ class OptimizeDatasetTest(test_base.DatasetTestBase): 2, display_name="private_thread_pool_%d" % 2)) dataset = dataset_ops._OptimizeDataset(dataset, []) - iterator = dataset.make_initializable_iterator() - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(iterator.initializer) - self.assertAllEqual(list(range(10)), sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.assertDatasetProduces( + dataset, + expected_output=[list(range(10))], + requires_initialization=True) def testOptimizationNonSerializable(self): dataset = dataset_ops.Dataset.from_tensors(0) @@ -113,26 +189,61 @@ class OptimizeDatasetTest(test_base.DatasetTestBase): dataset = dataset.skip(0) # Should be removed by noop elimination dataset = dataset.cache() dataset = dataset_ops._OptimizeDataset(dataset, ["noop_elimination"]) - iterator = dataset.make_one_shot_iterator() - get_next = iterator.get_next() - - with self.cached_session() as sess: - self.assertEquals(0, sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.assertDatasetProduces(dataset, expected_output=[0]) def testOptimizationNonSerializableAsDirectInput(self): - """Tests that non-serializable dataset can be OptimizeDataset's input. - """ + """Tests that non-serializable dataset can be OptimizeDataset's input.""" dataset = dataset_ops.Dataset.from_tensors(0) dataset = dataset.apply(optimization.non_serializable()) dataset = dataset_ops._OptimizeDataset(dataset, ["noop_elimination"]) - iterator = dataset.make_one_shot_iterator() - get_next = iterator.get_next() - with self.cached_session() as sess: - self.assertEquals(0, sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.assertDatasetProduces(dataset, expected_output=[0]) + + @parameterized.named_parameters(_generate_captured_refvar_test_cases()) + # Skip eager because RefVariables are not supported in eager mode. + def testSkipEagerOptimizationWithCapturedRefVar(self, dataset_fn): + """Tests that default optimizations are disabled with ref variables.""" + variable = variable_scope.get_variable( + "v", initializer=0, use_resource=False) + assign_op = variable.assign_add(1) + + unoptimized_dataset = dataset_fn(variable) + + options = dataset_ops.Options() + opt_options = optimization_options.OptimizationOptions() + opt_options.noop_elimination = True + opt_options.map_and_batch_fusion = True + options.experimental_optimization = opt_options + optimized_dataset = unoptimized_dataset.with_options(options) + + # Check that warning is logged. + warnings.simplefilter("always") + with warnings.catch_warnings(record=True) as w: + optimized_it = optimized_dataset.make_initializable_iterator() + + self.assertGreaterEqual(len(w), 1) + expected = ("tf.data static optimizations are not compatible with " + "tf.Variable. The following optimizations will be disabled: " + "map_and_batch_fusion, noop_elimination. To enable " + "optimizations, use resource variables instead by calling " + "`tf.enable_resource_variables()` at the start of the program.") + self.assertTrue(any([expected in str(warning) for warning in w])) + + # Check that outputs are the same in the optimized and unoptimized cases, + # when the variable value is changing. + unoptimized_it = unoptimized_dataset.make_initializable_iterator() + with ops.control_dependencies([assign_op]): + unoptimized_output = unoptimized_it.get_next() + optimized_output = optimized_it.get_next() + + self.evaluate(variable.initializer) + self.evaluate((unoptimized_it.initializer, optimized_it.initializer)) + while True: + try: + unoptimized, optimized = self.evaluate((unoptimized_output, + optimized_output)) + self.assertEqual(unoptimized, optimized) + except errors.OutOfRangeError: + break if __name__ == "__main__": diff --git a/tensorflow/python/data/experimental/kernel_tests/optimization/shuffle_and_repeat_fusion_test.py b/tensorflow/python/data/experimental/kernel_tests/optimization/shuffle_and_repeat_fusion_test.py index 36582f449f..5f746ec63a 100644 --- a/tensorflow/python/data/experimental/kernel_tests/optimization/shuffle_and_repeat_fusion_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/optimization/shuffle_and_repeat_fusion_test.py @@ -18,31 +18,35 @@ from __future__ import division from __future__ import print_function from tensorflow.python.data.experimental.ops import optimization +from tensorflow.python.data.experimental.ops.optimization_options import OptimizationOptions from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import errors +from tensorflow.python.framework import test_util from tensorflow.python.platform import test +@test_util.run_all_in_graph_and_eager_modes class ShuffleAndRepeatFusionTest(test_base.DatasetTestBase): def testShuffleAndRepeatFusion(self): dataset = dataset_ops.Dataset.range(10).apply( optimization.assert_next(["ShuffleAndRepeat"])).shuffle(10).repeat(2) options = dataset_ops.Options() - options.experimental_shuffle_and_repeat_fusion = True + options.experimental_optimization = OptimizationOptions() + options.experimental_optimization.shuffle_and_repeat_fusion = True dataset = dataset.with_options(options) - iterator = dataset.make_one_shot_iterator() - get_next = iterator.get_next() + get_next = self.getNext(dataset) - with self.cached_session() as sess: - for _ in range(2): - results = [] - for _ in range(10): - results.append(sess.run(get_next)) - self.assertAllEqual([x for x in range(10)], sorted(results)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + for _ in range(2): + results = [] + for _ in range(10): + results.append(self.evaluate(get_next())) + self.assertAllEqual([x for x in range(10)], sorted(results)) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) if __name__ == "__main__": diff --git a/tensorflow/python/data/experimental/kernel_tests/override_threadpool_test.py b/tensorflow/python/data/experimental/kernel_tests/override_threadpool_test.py index 5e419a9b2f..aa81663a18 100644 --- a/tensorflow/python/data/experimental/kernel_tests/override_threadpool_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/override_threadpool_test.py @@ -22,12 +22,15 @@ import threading from absl.testing import parameterized import numpy as np +from tensorflow.core.framework import graph_pb2 +from tensorflow.python.data.experimental.ops import threading_options from tensorflow.python.data.experimental.ops import threadpool from tensorflow.python.data.experimental.ops import unique from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors +from tensorflow.python.framework import test_util from tensorflow.python.ops import script_ops from tensorflow.python.platform import test @@ -35,18 +38,7 @@ from tensorflow.python.platform import test class OverrideThreadpoolTest(test_base.DatasetTestBase, parameterized.TestCase): - @parameterized.named_parameters( - ("1", 1, None), - ("2", 2, None), - ("3", 4, None), - ("4", 8, None), - ("5", 16, None), - ("6", 4, -1), - ("7", 4, 0), - ("8", 4, 1), - ("9", 4, 4), - ) - def testNumThreads(self, num_threads, max_intra_op_parallelism): + def _testNumThreadsHelper(self, num_threads, override_threadpool_fn): def get_thread_id(_): # Python creates a dummy thread object to represent the current @@ -60,32 +52,86 @@ class OverrideThreadpoolTest(test_base.DatasetTestBase, dataset_ops.Dataset.range(1000).map( lambda x: script_ops.py_func(get_thread_id, [x], dtypes.int64), num_parallel_calls=32).apply(unique.unique())) - - dataset = threadpool.override_threadpool( - dataset, - threadpool.PrivateThreadPool( - num_threads, - max_intra_op_parallelism=max_intra_op_parallelism, - display_name="private_thread_pool_%d" % num_threads)) - - iterator = dataset.make_initializable_iterator() + dataset = override_threadpool_fn(dataset) + iterator = dataset_ops.make_initializable_iterator(dataset) next_element = iterator.get_next() - with self.cached_session() as sess: - sess.run(iterator.initializer) - thread_ids = [] - try: - while True: - thread_ids.append(sess.run(next_element)) - except errors.OutOfRangeError: - pass - self.assertEqual(len(thread_ids), len(set(thread_ids))) - self.assertGreater(len(thread_ids), 0) + self.evaluate(iterator.initializer) + thread_ids = [] + try: + while True: + thread_ids.append(self.evaluate(next_element)) + except errors.OutOfRangeError: + pass + self.assertLen(thread_ids, len(set(thread_ids))) + self.assertNotEmpty(thread_ids) + if num_threads: # NOTE(mrry): We don't control the thread pool scheduling, and # so cannot guarantee that all of the threads in the pool will # perform work. self.assertLessEqual(len(thread_ids), num_threads) + @parameterized.named_parameters( + ("1", 1, None), + ("2", 2, None), + ("3", 4, None), + ("4", 8, None), + ("5", 16, None), + ("6", 4, -1), + ("7", 4, 0), + ("8", 4, 1), + ("9", 4, 4), + ) + @test_util.run_deprecated_v1 + def testNumThreadsDeprecated(self, num_threads, max_intra_op_parallelism): + + def override_threadpool_fn(dataset): + return threadpool.override_threadpool( + dataset, + threadpool.PrivateThreadPool( + num_threads, + max_intra_op_parallelism=max_intra_op_parallelism, + display_name="private_thread_pool_%d" % num_threads)) + + self._testNumThreadsHelper(num_threads, override_threadpool_fn) + + @parameterized.named_parameters( + ("1", 1, None), + ("2", 2, None), + ("3", 4, None), + ("4", 8, None), + ("5", 16, None), + ("6", None, 0), + ("7", None, 1), + ("8", None, 4), + ("9", 4, 0), + ("10", 4, 1), + ("11", 4, 4), + ("12", None, None), + ) + @test_util.run_deprecated_v1 + def testNumThreads(self, num_threads, max_intra_op_parallelism): + + def override_threadpool_fn(dataset): + t_options = threading_options.ThreadingOptions() + if max_intra_op_parallelism is not None: + t_options.max_intra_op_parallelism = max_intra_op_parallelism + if num_threads is not None: + t_options.private_threadpool_size = num_threads + options = dataset_ops.Options() + options.experimental_threading = t_options + return dataset.with_options(options) + + self._testNumThreadsHelper(num_threads, override_threadpool_fn) + + def testMaxIntraOpParallelismAsGraphDefInternal(self): + dataset = dataset_ops.Dataset.from_tensors(0) + dataset = dataset_ops._MaxIntraOpParallelismDataset(dataset, 1) + graph = graph_pb2.GraphDef().FromString( + self.evaluate(dataset._as_serialized_graph())) + self.assertTrue( + any([node.op != "MaxIntraOpParallelismDataset" for node in graph.node])) + if __name__ == "__main__": test.main() diff --git a/tensorflow/python/data/experimental/kernel_tests/parallel_interleave_test.py b/tensorflow/python/data/experimental/kernel_tests/parallel_interleave_test.py index 90ac250df7..113326c028 100644 --- a/tensorflow/python/data/experimental/kernel_tests/parallel_interleave_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/parallel_interleave_test.py @@ -86,7 +86,7 @@ class ParallelInterleaveTest(test_base.DatasetTestBase): self.block_length, self.sloppy, self.buffer_output_elements, self.prefetch_input_elements))) - self.iterator = self.dataset.make_initializable_iterator() + self.iterator = dataset_ops.make_initializable_iterator(self.dataset) self.init_op = self.iterator.initializer self.next_element = self.iterator.get_next() @@ -195,9 +195,9 @@ class ParallelInterleaveTest(test_base.DatasetTestBase): [[4] * 4, [5] * 5, [6] * 6] * self.repeat_count, 1, 1): self.write_coordination_events[expected_element].set() self.assertEqual(expected_element * expected_element, - sess.run(self.next_element)) + self.evaluate(self.next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(self.next_element) + self.evaluate(self.next_element) def testSingleThreaded(self): self._testSingleThreaded() @@ -235,10 +235,10 @@ class ParallelInterleaveTest(test_base.DatasetTestBase): for expected_element in self._interleave( [[3] * 3, [7] * 7, [4] * 4] * self.repeat_count, 2, 1): self.write_coordination_events[expected_element].set() - output = sess.run(self.next_element) + output = self.evaluate(self.next_element) self.assertEqual(expected_element * expected_element, output) with self.assertRaises(errors.OutOfRangeError): - sess.run(self.next_element) + self.evaluate(self.next_element) def _testTwoThreadsNoContention(self, sloppy=False): # num_threads > 1. @@ -262,7 +262,7 @@ class ParallelInterleaveTest(test_base.DatasetTestBase): self.write_coordination_events[expected_element].set() if done_first_event: # First event starts the worker threads. self.read_coordination_events[expected_element].acquire() - actual_element = sess.run(self.next_element) + actual_element = self.evaluate(self.next_element) if not done_first_event: self.read_coordination_events[expected_element].acquire() done_first_event = True @@ -270,7 +270,7 @@ class ParallelInterleaveTest(test_base.DatasetTestBase): "At index %s: %s expected, got: %s" % (i, expected_element, actual_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(self.next_element) + self.evaluate(self.next_element) def testTwoThreadsNoContention(self): self._testTwoThreadsNoContention() @@ -309,7 +309,7 @@ class ParallelInterleaveTest(test_base.DatasetTestBase): else: self.write_coordination_events[expected_element].set() time.sleep(0.5) # Sleep to consistently "avoid" the race condition. - actual_element = sess.run(self.next_element) + actual_element = self.evaluate(self.next_element) if not done_first_event: done_first_event = True self.assertTrue( @@ -318,7 +318,7 @@ class ParallelInterleaveTest(test_base.DatasetTestBase): "At index %s: %s expected, got: %s" % (i, expected_element, actual_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(self.next_element) + self.evaluate(self.next_element) def testTwoThreadsNoContentionWithRaces(self): self._testTwoThreadsNoContentionWithRaces() @@ -348,7 +348,7 @@ class ParallelInterleaveTest(test_base.DatasetTestBase): self.write_coordination_events[expected_element].set() if done_first_event: # First event starts the worker threads. self.read_coordination_events[expected_element].acquire() - actual_element = sess.run(self.next_element) + actual_element = self.evaluate(self.next_element) if not done_first_event: done_first_event = True self.read_coordination_events[expected_element].acquire() @@ -356,7 +356,7 @@ class ParallelInterleaveTest(test_base.DatasetTestBase): "At index %s: %s expected, got: %s" % (i, expected_element, actual_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(self.next_element) + self.evaluate(self.next_element) def testTwoThreadsNoContentionBlockLength(self): self._testTwoThreadsNoContentionBlockLength() @@ -396,7 +396,7 @@ class ParallelInterleaveTest(test_base.DatasetTestBase): else: self.write_coordination_events[expected_element].set() time.sleep(0.5) # Sleep to consistently "avoid" the race condition. - actual_element = sess.run(self.next_element) + actual_element = self.evaluate(self.next_element) if not done_first_event: done_first_event = True self.assertTrue( @@ -405,7 +405,7 @@ class ParallelInterleaveTest(test_base.DatasetTestBase): "At index %s: %s expected, got: %s" % (i, expected_element, actual_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(self.next_element) + self.evaluate(self.next_element) def testTwoThreadsNoContentionWithRacesAndBlocking(self): self._testTwoThreadsNoContentionWithRacesAndBlocking() @@ -428,7 +428,7 @@ class ParallelInterleaveTest(test_base.DatasetTestBase): self.prefetch_input_elements: 0, }) with self.assertRaises(errors.OutOfRangeError): - sess.run(self.next_element) + self.evaluate(self.next_element) def testEmptyInput(self): self._testEmptyInput() @@ -451,7 +451,7 @@ class ParallelInterleaveTest(test_base.DatasetTestBase): self.prefetch_input_elements: 0, }) with self.assertRaises(errors.OutOfRangeError): - sess.run(self.next_element) + self.evaluate(self.next_element) def testNonEmptyInputIntoEmptyOutputs(self): self._testNonEmptyInputIntoEmptyOutputs() @@ -484,7 +484,7 @@ class ParallelInterleaveTest(test_base.DatasetTestBase): # presence of finishing iterators. if done_first_event and not (sloppy and (i in race_indices)): self.read_coordination_events[expected_element].acquire() - actual_element = sess.run(self.next_element) + actual_element = self.evaluate(self.next_element) if not done_first_event or (sloppy and (i in race_indices)): done_first_event = True self.read_coordination_events[expected_element].acquire() @@ -520,10 +520,10 @@ class ParallelInterleaveTest(test_base.DatasetTestBase): ] for element in mis_ordering: self.write_coordination_events[element].set() - self.assertEqual(element * element, sess.run(self.next_element)) + self.assertEqual(element * element, self.evaluate(self.next_element)) self.assertTrue(self.read_coordination_events[element].acquire(False)) with self.assertRaises(errors.OutOfRangeError): - sess.run(self.next_element) + self.evaluate(self.next_element) def testBlockLengthWithContentionSloppy(self): with self.cached_session() as sess: @@ -549,7 +549,7 @@ class ParallelInterleaveTest(test_base.DatasetTestBase): self.write_coordination_events[expected_element].set() if done_first_event: # First event starts the worker threads. self.read_coordination_events[expected_element].acquire() - actual_element = sess.run(self.next_element) + actual_element = self.evaluate(self.next_element) if not done_first_event: self.read_coordination_events[expected_element].acquire() done_first_event = True @@ -557,7 +557,7 @@ class ParallelInterleaveTest(test_base.DatasetTestBase): "At index %s: %s expected, got: %s" % (i, expected_element, actual_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(self.next_element) + self.evaluate(self.next_element) def _testEarlyExit(self, sloppy=False): # Exiting without consuming all input should not block @@ -575,7 +575,7 @@ class ParallelInterleaveTest(test_base.DatasetTestBase): }) for i in range(4, 7): self.write_coordination_events[i].set() - elem = sess.run(self.next_element) # Start all workers + elem = self.evaluate(self.next_element) # Start all workers # Allow the one successful worker to progress beyond the py_func again. elem = int(math.sqrt(elem)) self.write_coordination_events[elem].set() @@ -603,12 +603,12 @@ class ParallelInterleaveTest(test_base.DatasetTestBase): dataset = dataset.apply( interleave_ops.parallel_interleave( interleave_fn, cycle_length=16, block_length=2, sloppy=sloppy)) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) with self.cached_session() as sess: output_values = [] for _ in range(30): - output_values.append(sess.run(iterator.get_next())) + output_values.append(self.evaluate(iterator.get_next())) expected_values = self._interleave( [[4] * 4, [5] * 5, [6] * 6] * self.repeat_count, 1, 2) @@ -630,20 +630,19 @@ class ParallelInterleaveTest(test_base.DatasetTestBase): sparse_ops.sparse_to_dense(x.indices, x.dense_shape, x.values)) dataset = dataset_ops.Dataset.range(10).map(_map_fn) - iterator = dataset.apply( - interleave_ops.parallel_interleave( - _interleave_fn, cycle_length=1)).make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset.apply( + interleave_ops.parallel_interleave(_interleave_fn, cycle_length=1))) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: - sess.run(init_op) + self.evaluate(init_op) for i in range(10): for j in range(2): expected = [i, 0] if j % 2 == 0 else [0, -i] - self.assertAllEqual(expected, sess.run(get_next)) + self.assertAllEqual(expected, self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) def testErrorsInOutputFn(self): with self.cached_session() as sess: @@ -668,15 +667,15 @@ class ParallelInterleaveTest(test_base.DatasetTestBase): self.error = ValueError() self.write_coordination_events[expected_element].set() with self.assertRaises(errors.InvalidArgumentError): - sess.run(self.next_element) + self.evaluate(self.next_element) else: self.write_coordination_events[expected_element].set() - actual_element = sess.run(self.next_element) + actual_element = self.evaluate(self.next_element) self.assertEqual(expected_element * expected_element, actual_element, "At index %s: %s expected, got: %s" % (i, expected_element, actual_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(self.next_element) + self.evaluate(self.next_element) def testErrorsInInputFn(self): @@ -701,7 +700,7 @@ class ParallelInterleaveTest(test_base.DatasetTestBase): self.buffer_output_elements, self.prefetch_input_elements))) - self.iterator = self.dataset.make_initializable_iterator() + self.iterator = dataset_ops.make_initializable_iterator(self.dataset) self.init_op = self.iterator.initializer self.next_element = self.iterator.get_next() @@ -720,14 +719,14 @@ class ParallelInterleaveTest(test_base.DatasetTestBase): self._interleave([[4] * 4, [5], [6] * 6] * self.repeat_count, 2, 1)): if expected_element == 5: with self.assertRaises(errors.InvalidArgumentError): - sess.run(self.next_element) + self.evaluate(self.next_element) else: - actual_element = sess.run(self.next_element) + actual_element = self.evaluate(self.next_element) self.assertEqual(expected_element, actual_element, "At index %s: %s expected, got: %s" % (i, expected_element, actual_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(self.next_element) + self.evaluate(self.next_element) def testErrorsInInterleaveFn(self): @@ -750,7 +749,7 @@ class ParallelInterleaveTest(test_base.DatasetTestBase): self.buffer_output_elements, self.prefetch_input_elements))) - self.iterator = self.dataset.make_initializable_iterator() + self.iterator = dataset_ops.make_initializable_iterator(self.dataset) self.init_op = self.iterator.initializer self.next_element = self.iterator.get_next() @@ -769,14 +768,14 @@ class ParallelInterleaveTest(test_base.DatasetTestBase): self._interleave([[4] * 4, [5], [6] * 6] * self.repeat_count, 2, 1)): if expected_element == 5: with self.assertRaises(errors.InvalidArgumentError): - sess.run(self.next_element) + self.evaluate(self.next_element) else: - actual_element = sess.run(self.next_element) + actual_element = self.evaluate(self.next_element) self.assertEqual(expected_element, actual_element, "At index %s: %s expected, got: %s" % (i, expected_element, actual_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(self.next_element) + self.evaluate(self.next_element) def testShutdownRace(self): dataset = dataset_ops.Dataset.range(20) @@ -789,17 +788,17 @@ class ParallelInterleaveTest(test_base.DatasetTestBase): buffer_output_elements=1, prefetch_input_elements=0)) dataset = dataset.batch(32) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) next_element = iterator.get_next() results = [] with self.cached_session() as sess: for _ in range(2): elements = [] - sess.run(iterator.initializer) + self.evaluate(iterator.initializer) try: while True: - elements.extend(sess.run(next_element)) + elements.extend(self.evaluate(next_element)) except errors.OutOfRangeError: pass results.append(elements) diff --git a/tensorflow/python/data/experimental/kernel_tests/parse_example_dataset_test.py b/tensorflow/python/data/experimental/kernel_tests/parse_example_dataset_test.py index c74f754fef..76e0d4d72a 100644 --- a/tensorflow/python/data/experimental/kernel_tests/parse_example_dataset_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/parse_example_dataset_test.py @@ -144,6 +144,7 @@ class ParseExampleDatasetTest(test_base.DatasetTestBase): expected_values=expected_output, create_iterator_twice=True) + @test_util.run_deprecated_v1 def testEmptySerializedWithoutDefaultsShouldFail(self): input_features = { "st_a": @@ -177,6 +178,7 @@ class ParseExampleDatasetTest(test_base.DatasetTestBase): expected_err=(errors_impl.InvalidArgumentError, "Feature: c \\(data type: float\\) is required")) + @test_util.run_deprecated_v1 def testDenseNotMatchingShapeShouldFail(self): original = [ example(features=features({ @@ -669,6 +671,7 @@ class ParseExampleDatasetTest(test_base.DatasetTestBase): for batch_size in (1, 10, 20, 100, 256): self._testSerializedContainingVarLenDenseLargerBatch(batch_size) + @test_util.run_deprecated_v1 def testSkipEagerSerializedShapeMismatch(self): aname = "a" bname = "b" @@ -706,6 +709,7 @@ class ParseExampleDatasetTest(test_base.DatasetTestBase): expected_err=(ValueError, "Cannot reshape a tensor with 0 elements to shape")) + @test_util.run_deprecated_v1 def testSerializedContainingVarLenDense(self): aname = "a" bname = "b" diff --git a/tensorflow/python/data/experimental/kernel_tests/prefetch_to_device_test.py b/tensorflow/python/data/experimental/kernel_tests/prefetch_to_device_test.py index f73725366c..80bd43e9ad 100644 --- a/tensorflow/python/data/experimental/kernel_tests/prefetch_to_device_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/prefetch_to_device_test.py @@ -31,17 +31,15 @@ from tensorflow.python.platform import test class PrefetchToDeviceTest(test_base.DatasetTestBase): + @test_util.run_deprecated_v1 def testPrefetchToDevice(self): host_dataset = dataset_ops.Dataset.range(10) device_dataset = host_dataset.apply( prefetching_ops.prefetch_to_device("/cpu:1")) - # NOTE(mrry): This device block creates the "host" dataset and iterator on - # /cpu:0, and ensures that the prefetching is across devices. In typical use - # this would not be necessary, because the GPU device would not support any - # of the dataset-related ops. - with ops.device("/cpu:0"): - iterator = device_dataset.make_one_shot_iterator() + with ops.device("/cpu:1"): + iterator = dataset_ops.make_one_shot_iterator(device_dataset) + next_element = iterator.get_next() self.assertEqual(host_dataset.output_types, device_dataset.output_types) self.assertEqual(host_dataset.output_types, iterator.output_types) @@ -50,29 +48,26 @@ class PrefetchToDeviceTest(test_base.DatasetTestBase): self.assertEqual(host_dataset.output_classes, device_dataset.output_classes) self.assertEqual(host_dataset.output_classes, iterator.output_classes) - next_element = iterator.get_next() self.assertEqual(dtypes.int64, next_element.dtype) self.assertEqual([], next_element.shape) worker_config = config_pb2.ConfigProto(device_count={"CPU": 2}) - with self.test_session(config=worker_config) as sess: + with self.test_session(config=worker_config): for i in range(10): - self.assertEqual(i, sess.run(next_element)) + self.assertEqual(i, self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) + @test_util.run_deprecated_v1 def testPrefetchToSameDevice(self): host_dataset = dataset_ops.Dataset.range(10) device_dataset = host_dataset.apply( prefetching_ops.prefetch_to_device( "/job:localhost/replica:0/task:0/device:CPU:0")) - # NOTE(mrry): This device block creates the "host" dataset and iterator on - # /cpu:0, and ensures that the prefetching is across devices. In typical use - # this would not be necessary, because the GPU device would not support any - # of the dataset-related ops. - with ops.device("/cpu:0"): - iterator = device_dataset.make_one_shot_iterator() + with ops.device("/cpu:1"): + iterator = dataset_ops.make_one_shot_iterator(device_dataset) + next_element = iterator.get_next() self.assertEqual(host_dataset.output_types, device_dataset.output_types) self.assertEqual(host_dataset.output_types, iterator.output_types) @@ -81,27 +76,24 @@ class PrefetchToDeviceTest(test_base.DatasetTestBase): self.assertEqual(host_dataset.output_classes, device_dataset.output_classes) self.assertEqual(host_dataset.output_classes, iterator.output_classes) - next_element = iterator.get_next() self.assertEqual(dtypes.int64, next_element.dtype) self.assertEqual([], next_element.shape) - with self.cached_session() as sess: + with self.cached_session(): for i in range(10): - self.assertEqual(i, sess.run(next_element)) + self.assertEqual(i, self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) + @test_util.run_deprecated_v1 def testPrefetchDictToDevice(self): host_dataset = dataset_ops.Dataset.range(10).map(lambda x: {"a": x}) device_dataset = host_dataset.apply( prefetching_ops.prefetch_to_device("/cpu:1")) - # NOTE(mrry): This device block creates the "host" dataset and iterator on - # /cpu:0, and ensures that the prefetching is across devices. In typical use - # this would not be necessary, because the GPU device would not support any - # of the dataset-related ops. - with ops.device("/cpu:0"): - iterator = device_dataset.make_one_shot_iterator() + with ops.device("/cpu:1"): + iterator = dataset_ops.make_one_shot_iterator(device_dataset) + next_element = iterator.get_next() self.assertEqual(host_dataset.output_types, device_dataset.output_types) self.assertEqual(host_dataset.output_types, iterator.output_types) @@ -110,17 +102,17 @@ class PrefetchToDeviceTest(test_base.DatasetTestBase): self.assertEqual(host_dataset.output_classes, device_dataset.output_classes) self.assertEqual(host_dataset.output_classes, iterator.output_classes) - next_element = iterator.get_next() self.assertEqual(dtypes.int64, next_element["a"].dtype) self.assertEqual([], next_element["a"].shape) worker_config = config_pb2.ConfigProto(device_count={"CPU": 2}) - with self.test_session(config=worker_config) as sess: + with self.test_session(config=worker_config): for i in range(10): - self.assertEqual({"a": i}, sess.run(next_element)) + self.assertEqual({"a": i}, self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) + @test_util.run_deprecated_v1 def testPrefetchSparseTensorsToDevice(self): def make_tensor(i): return sparse_tensor.SparseTensorValue( @@ -130,12 +122,9 @@ class PrefetchToDeviceTest(test_base.DatasetTestBase): device_dataset = host_dataset.apply( prefetching_ops.prefetch_to_device("/cpu:1")) - # NOTE(mrry): This device block creates the "host" dataset and iterator on - # /cpu:0, and ensures that the prefetching is across devices. In typical use - # this would not be necessary, because the GPU device would not support any - # of the dataset-related ops. - with ops.device("/cpu:0"): - iterator = device_dataset.make_one_shot_iterator() + with ops.device("/cpu:1"): + iterator = dataset_ops.make_one_shot_iterator(device_dataset) + next_element = iterator.get_next() self.assertEqual(host_dataset.output_types, device_dataset.output_types) self.assertEqual(host_dataset.output_types, iterator.output_types) @@ -144,18 +133,17 @@ class PrefetchToDeviceTest(test_base.DatasetTestBase): self.assertEqual(host_dataset.output_classes, device_dataset.output_classes) self.assertEqual(host_dataset.output_classes, iterator.output_classes) - next_element = iterator.get_next() self.assertEqual(dtypes.int64, next_element.dtype) worker_config = config_pb2.ConfigProto(device_count={"CPU": 2}) - with self.test_session(config=worker_config) as sess: + with self.test_session(config=worker_config): for i in range(10): - actual = sess.run(next_element) + actual = self.evaluate(next_element) self.assertAllEqual([i], actual.values) self.assertAllEqual([[0, 0]], actual.indices) self.assertAllEqual([2, 2], actual.dense_shape) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) def testPrefetchToDeviceGpu(self): if not test_util.is_gpu_available(): @@ -165,26 +153,26 @@ class PrefetchToDeviceTest(test_base.DatasetTestBase): device_dataset = host_dataset.apply( prefetching_ops.prefetch_to_device("/gpu:0")) - iterator = device_dataset.make_one_shot_iterator() + iterator = dataset_ops.make_initializable_iterator(device_dataset) next_element = iterator.get_next() - with self.cached_session() as sess: + with self.cached_session( + config=config_pb2.ConfigProto(allow_soft_placement=False)): + self.evaluate(iterator.initializer) for i in range(10): - self.assertEqual(i, sess.run(next_element)) + self.assertEqual(i, self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) + @test_util.run_deprecated_v1 def testPrefetchToDeviceWithReInit(self): host_dataset = dataset_ops.Dataset.range(10) device_dataset = host_dataset.apply( prefetching_ops.prefetch_to_device("/cpu:1")) - # NOTE(mrry): This device block creates the "host" dataset and iterator on - # /cpu:0, and ensures that the prefetching is across devices. In typical use - # this would not be necessary, because the GPU device would not support any - # of the dataset-related ops. - with ops.device("/cpu:0"): - iterator = device_dataset.make_initializable_iterator() + with ops.device("/cpu:1"): + iterator = dataset_ops.make_initializable_iterator(device_dataset) + next_element = iterator.get_next() self.assertEqual(host_dataset.output_types, device_dataset.output_types) self.assertEqual(host_dataset.output_types, iterator.output_types) @@ -193,20 +181,19 @@ class PrefetchToDeviceTest(test_base.DatasetTestBase): self.assertEqual(host_dataset.output_classes, device_dataset.output_classes) self.assertEqual(host_dataset.output_classes, iterator.output_classes) - next_element = iterator.get_next() self.assertEqual(dtypes.int64, next_element.dtype) self.assertEqual([], next_element.shape) worker_config = config_pb2.ConfigProto(device_count={"CPU": 2}) - with self.test_session(config=worker_config) as sess: - sess.run(iterator.initializer) + with self.test_session(config=worker_config): + self.evaluate(iterator.initializer) for i in range(5): - self.assertEqual(i, sess.run(next_element)) - sess.run(iterator.initializer) + self.assertEqual(i, self.evaluate(next_element)) + self.evaluate(iterator.initializer) for i in range(10): - self.assertEqual(i, sess.run(next_element)) + self.assertEqual(i, self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) def testPrefetchToDeviceGpuWithReInit(self): if not test_util.is_gpu_available(): @@ -216,18 +203,19 @@ class PrefetchToDeviceTest(test_base.DatasetTestBase): device_dataset = host_dataset.apply( prefetching_ops.prefetch_to_device("/gpu:0")) - iterator = device_dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(device_dataset) next_element = iterator.get_next() - with self.cached_session() as sess: - sess.run(iterator.initializer) + with self.cached_session( + config=config_pb2.ConfigProto(allow_soft_placement=False)): + self.evaluate(iterator.initializer) for i in range(5): - self.assertEqual(i, sess.run(next_element)) - sess.run(iterator.initializer) + self.assertEqual(i, self.evaluate(next_element)) + self.evaluate(iterator.initializer) for i in range(10): - self.assertEqual(i, sess.run(next_element)) + self.assertEqual(i, self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) if __name__ == "__main__": diff --git a/tensorflow/python/data/experimental/kernel_tests/rejection_resample_test.py b/tensorflow/python/data/experimental/kernel_tests/rejection_resample_test.py index 4c879dbae6..76f68f50c8 100644 --- a/tensorflow/python/data/experimental/kernel_tests/rejection_resample_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/rejection_resample_test.py @@ -28,6 +28,7 @@ from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors +from tensorflow.python.framework import test_util from tensorflow.python.ops import math_ops from tensorflow.python.ops import random_ops from tensorflow.python.ops import string_ops @@ -47,7 +48,7 @@ def _time_resampling( initial_dist=init_dist, seed=142)) - get_next = dataset.make_one_shot_iterator().get_next() + get_next = dataset_ops.make_one_shot_iterator(dataset).get_next() with test_obj.test_session() as sess: start_time = time.time() @@ -63,6 +64,7 @@ class RejectionResampleTest(test_base.DatasetTestBase, parameterized.TestCase): @parameterized.named_parameters( ("InitialDistributionKnown", True), ("InitialDistributionUnknown", False)) + @test_util.run_deprecated_v1 def testDistribution(self, initial_known): classes = np.random.randint(5, size=(20000,)) # Uniformly sampled target_dist = [0.9, 0.05, 0.05, 0.0, 0.0] @@ -71,12 +73,12 @@ class RejectionResampleTest(test_base.DatasetTestBase, parameterized.TestCase): dataset = dataset_ops.Dataset.from_tensor_slices(classes).shuffle( 200, seed=21).map(lambda c: (c, string_ops.as_string(c))).repeat() - get_next = dataset.apply( + get_next = dataset_ops.make_one_shot_iterator(dataset.apply( resampling.rejection_resample( target_dist=target_dist, initial_dist=initial_dist, class_func=lambda c, _: c, - seed=27)).make_one_shot_iterator().get_next() + seed=27))).get_next() with self.cached_session() as sess: returned = [] @@ -97,6 +99,7 @@ class RejectionResampleTest(test_base.DatasetTestBase, parameterized.TestCase): @parameterized.named_parameters( ("OnlyInitial", True), ("NotInitial", False)) + @test_util.run_deprecated_v1 def testEdgeCasesSampleFromInitialDataset(self, only_initial_dist): init_dist = [0.5, 0.5] target_dist = [0.5, 0.5] if only_initial_dist else [0.0, 1.0] @@ -114,7 +117,7 @@ class RejectionResampleTest(test_base.DatasetTestBase, parameterized.TestCase): target_dist=target_dist, initial_dist=init_dist)) - get_next = dataset.make_one_shot_iterator().get_next() + get_next = dataset_ops.make_one_shot_iterator(dataset).get_next() with self.cached_session() as sess: returned = [] @@ -122,6 +125,7 @@ class RejectionResampleTest(test_base.DatasetTestBase, parameterized.TestCase): while True: returned.append(sess.run(get_next)) + @test_util.run_deprecated_v1 def testRandomClasses(self): init_dist = [0.25, 0.25, 0.25, 0.25] target_dist = [0.0, 0.0, 0.0, 1.0] @@ -145,7 +149,7 @@ class RejectionResampleTest(test_base.DatasetTestBase, parameterized.TestCase): target_dist=target_dist, initial_dist=init_dist)) - get_next = dataset.make_one_shot_iterator().get_next() + get_next = dataset_ops.make_one_shot_iterator(dataset).get_next() with self.cached_session() as sess: returned = [] diff --git a/tensorflow/python/data/experimental/kernel_tests/restructured_dataset_test.py b/tensorflow/python/data/experimental/kernel_tests/restructured_dataset_test.py index 516e489d04..658e6120cf 100644 --- a/tensorflow/python/data/experimental/kernel_tests/restructured_dataset_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/restructured_dataset_test.py @@ -22,12 +22,14 @@ from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops from tensorflow.python.data.util import nest from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.platform import test class RestructuredDatasetTest(test_base.DatasetTestBase): + @test_util.run_deprecated_v1 def testRestructureDataset(self): components = (array_ops.placeholder(dtypes.int32), (array_ops.placeholder(dtypes.int32, shape=[None]), diff --git a/tensorflow/python/data/experimental/kernel_tests/scan_test.py b/tensorflow/python/data/experimental/kernel_tests/scan_test.py index 0730455431..03af7ecd2f 100644 --- a/tensorflow/python/data/experimental/kernel_tests/scan_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/scan_test.py @@ -40,6 +40,7 @@ class ScanTest(test_base.DatasetTestBase): return dataset_ops.Dataset.from_tensors(0).repeat().apply( scan_ops.scan(start, scan_fn)) + @test_util.run_deprecated_v1 def testCount(self): def make_scan_fn(step): return lambda state, _: (state + step, state) @@ -47,8 +48,8 @@ class ScanTest(test_base.DatasetTestBase): start = array_ops.placeholder(dtypes.int32, shape=[]) step = array_ops.placeholder(dtypes.int32, shape=[]) take = array_ops.placeholder(dtypes.int64, shape=[]) - iterator = self._counting_dataset( - start, make_scan_fn(step)).take(take).make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(self._counting_dataset( + start, make_scan_fn(step)).take(take)) next_element = iterator.get_next() with self.cached_session() as sess: @@ -60,15 +61,15 @@ class ScanTest(test_base.DatasetTestBase): feed_dict={start: start_val, step: step_val, take: take_val}) for expected, _ in zip( itertools.count(start_val, step_val), range(take_val)): - self.assertEqual(expected, sess.run(next_element)) + self.assertEqual(expected, self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) @test_util.run_in_graph_and_eager_modes def testFibonacci(self): - iterator = dataset_ops.Dataset.from_tensors(1).repeat(None).apply( - scan_ops.scan([0, 1], lambda a, _: ([a[1], a[0] + a[1]], a[1])) - ).make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator( + dataset_ops.Dataset.from_tensors(1).repeat(None).apply( + scan_ops.scan([0, 1], lambda a, _: ([a[1], a[0] + a[1]], a[1])))) if context.executing_eagerly(): next_element = iterator.get_next @@ -83,6 +84,7 @@ class ScanTest(test_base.DatasetTestBase): self.assertEqual(5, self.evaluate(next_element())) self.assertEqual(8, self.evaluate(next_element())) + @test_util.run_deprecated_v1 def testSparseCount(self): def _sparse(i): return sparse_tensor.SparseTensorValue( @@ -96,9 +98,8 @@ class ScanTest(test_base.DatasetTestBase): start = array_ops.placeholder(dtypes.int32, shape=[]) step = array_ops.placeholder(dtypes.int32, shape=[]) take = array_ops.placeholder(dtypes.int64, shape=[]) - iterator = self._counting_dataset( - _sparse(start), - make_scan_fn(step)).take(take).make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(self._counting_dataset( + _sparse(start), make_scan_fn(step)).take(take)) next_element = iterator.get_next() with self.cached_session() as sess: @@ -110,10 +111,11 @@ class ScanTest(test_base.DatasetTestBase): feed_dict={start: start_val, step: step_val, take: take_val}) for expected, _ in zip( itertools.count(start_val, step_val), range(take_val)): - self.assertEqual(expected, sess.run(next_element).values[0]) + self.assertEqual(expected, self.evaluate(next_element).values[0]) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) + @test_util.run_deprecated_v1 def testChangingStateShape(self): # Test the fixed-point shape invariant calculations: start with # initial values with known shapes, and use a scan function that @@ -131,16 +133,16 @@ class ScanTest(test_base.DatasetTestBase): self.assertIs(None, dataset.output_shapes[0][1].ndims) self.assertEqual([], dataset.output_shapes[1].as_list()) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) next_element = iterator.get_next() with self.cached_session() as sess: for i in range(5): - (longer_vector_val, larger_rank_val), _ = sess.run(next_element) + (longer_vector_val, larger_rank_val), _ = self.evaluate(next_element) self.assertAllEqual([0] * (2**i), longer_vector_val) self.assertAllEqual(np.array(1, ndmin=i), larger_rank_val) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) def testIncorrectStateType(self): diff --git a/tensorflow/python/data/experimental/kernel_tests/serialization/BUILD b/tensorflow/python/data/experimental/kernel_tests/serialization/BUILD index 2cfb575903..4a2e28f496 100644 --- a/tensorflow/python/data/experimental/kernel_tests/serialization/BUILD +++ b/tensorflow/python/data/experimental/kernel_tests/serialization/BUILD @@ -74,7 +74,11 @@ py_test( size = "small", srcs = ["checkpoint_input_pipeline_hook_test.py"], srcs_version = "PY2AND3", - tags = ["no_pip"], + tags = [ + "no_pip", + "no_windows", + "notsan", + ], deps = [ "//tensorflow/python:client_testlib", "//tensorflow/python:constant_op", @@ -313,6 +317,7 @@ py_test( srcs_version = "PY2AND3", tags = [ "no_pip", + "no_windows", "notap", ], deps = [ @@ -355,9 +360,13 @@ py_test( size = "small", srcs = ["matching_files_dataset_serialization_test.py"], srcs_version = "PY2AND3", + tags = [ + "no_windows", + ], deps = [ ":dataset_serialization_test_base", "//tensorflow/python:client_testlib", + "//tensorflow/python/data/experimental/ops:matching_files", "//tensorflow/python/data/ops:dataset_ops", "//third_party/py/numpy", ], diff --git a/tensorflow/python/data/experimental/kernel_tests/serialization/checkpoint_input_pipeline_hook_test.py b/tensorflow/python/data/experimental/kernel_tests/serialization/checkpoint_input_pipeline_hook_test.py index 94393d6d4b..8cc66d0c29 100644 --- a/tensorflow/python/data/experimental/kernel_tests/serialization/checkpoint_input_pipeline_hook_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/serialization/checkpoint_input_pipeline_hook_test.py @@ -21,17 +21,18 @@ from __future__ import print_function from tensorflow.python.data.experimental.ops import iterator_ops from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.estimator import estimator -from tensorflow.python.estimator import model_fn from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import variables from tensorflow.python.platform import test from tensorflow.python.training import checkpoint_management from tensorflow.python.training import saver as saver_lib from tensorflow.python.training import training_util +from tensorflow_estimator.python.estimator import estimator +from tensorflow_estimator.python.estimator import model_fn class CheckpointInputPipelineHookTest(test_base.DatasetTestBase): @@ -68,6 +69,7 @@ class CheckpointInputPipelineHookTest(test_base.DatasetTestBase): def _build_iterator_saver_hook(self, est): return iterator_ops.CheckpointInputPipelineHook(est) + @test_util.run_deprecated_v1 def testReturnDatasetFromInputFn(self): def _input_fn(): @@ -80,6 +82,7 @@ class CheckpointInputPipelineHookTest(test_base.DatasetTestBase): est.train(_input_fn, steps=2, hooks=[self._build_iterator_saver_hook(est)]) self.assertSequenceEqual(self._read_vars(est.model_dir), (4, 3)) + @test_util.run_deprecated_v1 def testBuildIteratorInInputFn(self): def _input_fn(): @@ -94,6 +97,7 @@ class CheckpointInputPipelineHookTest(test_base.DatasetTestBase): est.train(_input_fn, steps=2, hooks=[self._build_iterator_saver_hook(est)]) self.assertSequenceEqual(self._read_vars(est.model_dir), (4, 3)) + @test_util.run_deprecated_v1 def testDoNotRestore(self): def _input_fn(): diff --git a/tensorflow/python/data/experimental/kernel_tests/serialization/dataset_serialization_test_base.py b/tensorflow/python/data/experimental/kernel_tests/serialization/dataset_serialization_test_base.py index 7f435b8239..e65aa44d06 100644 --- a/tensorflow/python/data/experimental/kernel_tests/serialization/dataset_serialization_test_base.py +++ b/tensorflow/python/data/experimental/kernel_tests/serialization/dataset_serialization_test_base.py @@ -23,6 +23,7 @@ import os import numpy as np from tensorflow.python.data.experimental.ops import iterator_ops as contrib_iterator_ops +from tensorflow.python.data.ops import dataset_ops from tensorflow.python.data.ops import iterator_ops from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors @@ -578,7 +579,7 @@ class DatasetSerializationTestBase(test.TestCase): return np.linspace(0, num_outputs, num_samples, dtype=int) def _build_graph(self, ds_fn, sparse_tensors=False): - iterator = ds_fn().make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(ds_fn()) saveable = contrib_iterator_ops.make_saveable_from_iterator(iterator) ops.add_to_collection(ops.GraphKeys.SAVEABLE_OBJECTS, saveable) diff --git a/tensorflow/python/data/experimental/kernel_tests/serialization/filter_dataset_serialization_test.py b/tensorflow/python/data/experimental/kernel_tests/serialization/filter_dataset_serialization_test.py index 225f6cbac0..e3ba8ad231 100644 --- a/tensorflow/python/data/experimental/kernel_tests/serialization/filter_dataset_serialization_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/serialization/filter_dataset_serialization_test.py @@ -17,8 +17,6 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -import numpy as np - from tensorflow.python.data.experimental.kernel_tests.serialization import dataset_serialization_test_base from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import sparse_tensor @@ -35,7 +33,7 @@ class FilterDatasetSerializationTest( def testFilterCore(self): div = 3 - num_outputs = np.sum([x % 3 != 2 for x in range(100)]) + num_outputs = sum(x % 3 != 2 for x in range(100)) self.run_core_tests(lambda: self._build_filter_range_graph(div), lambda: self._build_filter_range_graph(div * 2), num_outputs) @@ -47,7 +45,7 @@ class FilterDatasetSerializationTest( lambda d: d["foo"] + d["bar"]) def testFilterDictCore(self): - num_outputs = np.sum([(x**2) % 2 == 0 for x in range(10)]) + num_outputs = sum((x**2) % 2 == 0 for x in range(10)) self.run_core_tests(self._build_filter_dict_graph, None, num_outputs) def _build_sparse_filter(self): diff --git a/tensorflow/python/data/experimental/kernel_tests/serialization/matching_files_dataset_serialization_test.py b/tensorflow/python/data/experimental/kernel_tests/serialization/matching_files_dataset_serialization_test.py index 7edb200d2e..c026e97835 100644 --- a/tensorflow/python/data/experimental/kernel_tests/serialization/matching_files_dataset_serialization_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/serialization/matching_files_dataset_serialization_test.py @@ -22,7 +22,7 @@ import shutil import tempfile from tensorflow.python.data.experimental.kernel_tests.serialization import dataset_serialization_test_base -from tensorflow.python.data.ops.dataset_ops import MatchingFilesDataset +from tensorflow.python.data.experimental.ops import matching_files from tensorflow.python.platform import test @@ -30,7 +30,7 @@ class MatchingFilesDatasetSerializationTest( dataset_serialization_test_base.DatasetSerializationTestBase): def _build_iterator_graph(self, test_patterns): - return MatchingFilesDataset(test_patterns) + return matching_files.MatchingFilesDataset(test_patterns) def testMatchingFilesCore(self): tmp_dir = tempfile.mkdtemp() diff --git a/tensorflow/python/data/experimental/kernel_tests/serialization/range_dataset_serialization_test.py b/tensorflow/python/data/experimental/kernel_tests/serialization/range_dataset_serialization_test.py index ef99d01c73..34419a3149 100644 --- a/tensorflow/python/data/experimental/kernel_tests/serialization/range_dataset_serialization_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/serialization/range_dataset_serialization_test.py @@ -56,8 +56,8 @@ class RangeDatasetSerializationTest( def testSaveRestore(self): def _build_graph(start, stop): - iterator = dataset_ops.Dataset.range(start, - stop).make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.range(start, stop)) init_op = iterator.initializer get_next = iterator.get_next() save_op = self._save_op(iterator._iterator_resource) @@ -71,36 +71,36 @@ class RangeDatasetSerializationTest( with ops.Graph().as_default() as g: init_op, get_next, save_op, _ = _build_graph(start, stop) with self.session(graph=g) as sess: - sess.run(variables.global_variables_initializer()) - sess.run(init_op) + self.evaluate(variables.global_variables_initializer()) + self.evaluate(init_op) for i in range(start, break_point): - self.assertEqual(i, sess.run(get_next)) - sess.run(save_op) + self.assertEqual(i, self.evaluate(get_next)) + self.evaluate(save_op) with ops.Graph().as_default() as g: init_op, get_next, _, restore_op = _build_graph(start, stop) with self.session(graph=g) as sess: - sess.run(init_op) - sess.run(restore_op) + self.evaluate(init_op) + self.evaluate(restore_op) for i in range(break_point, stop): - self.assertEqual(i, sess.run(get_next)) + self.assertEqual(i, self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Saving and restoring in same session. with ops.Graph().as_default() as g: init_op, get_next, save_op, restore_op = _build_graph(start, stop) with self.session(graph=g) as sess: - sess.run(variables.global_variables_initializer()) - sess.run(init_op) + self.evaluate(variables.global_variables_initializer()) + self.evaluate(init_op) for i in range(start, break_point): - self.assertEqual(i, sess.run(get_next)) - sess.run(save_op) - sess.run(restore_op) + self.assertEqual(i, self.evaluate(get_next)) + self.evaluate(save_op) + self.evaluate(restore_op) for i in range(break_point, stop): - self.assertEqual(i, sess.run(get_next)) + self.assertEqual(i, self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) def _build_range_dataset(self, start, stop): return dataset_ops.Dataset.range(start, stop) diff --git a/tensorflow/python/data/experimental/kernel_tests/serialization/serialization_integration_test.py b/tensorflow/python/data/experimental/kernel_tests/serialization/serialization_integration_test.py index 88d5c896c9..12fa0989d0 100644 --- a/tensorflow/python/data/experimental/kernel_tests/serialization/serialization_integration_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/serialization/serialization_integration_test.py @@ -60,9 +60,9 @@ class SerializationIntegrationTest(test.TestCase): init_ops, get_next_ops, saver = self._build_graph(num_pipelines, num_outputs) with self.session(graph=g) as sess: - sess.run(init_ops) + self.evaluate(init_ops) for _ in range(break_point): - output = sess.run(get_next_ops) + output = self.evaluate(get_next_ops) for i in range(num_pipelines): all_outputs[i].append(output[i]) saver.save(sess, self._ckpt_path()) @@ -73,7 +73,7 @@ class SerializationIntegrationTest(test.TestCase): with self.session(graph=g) as sess: saver.restore(sess, self._ckpt_path()) for _ in range(num_outputs - break_point): - output = sess.run(get_next_ops) + output = self.evaluate(get_next_ops) for i in range(num_pipelines): all_outputs[i].append(output[i]) diff --git a/tensorflow/python/data/experimental/kernel_tests/serialization/shuffle_dataset_serialization_test.py b/tensorflow/python/data/experimental/kernel_tests/serialization/shuffle_dataset_serialization_test.py index a04f1ddafc..e753a7a15b 100644 --- a/tensorflow/python/data/experimental/kernel_tests/serialization/shuffle_dataset_serialization_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/serialization/shuffle_dataset_serialization_test.py @@ -138,9 +138,9 @@ class ShuffleDatasetSerializationTest( saver = saver_lib.Saver(allow_empty=True) with self.session(graph=g) as sess: self._save(sess, saver) - expected = [sess.run(get_next_ops) for _ in range(num_outputs)] + expected = [self.evaluate(get_next_ops) for _ in range(num_outputs)] self._restore(saver, sess) - actual = [sess.run(get_next_ops) for _ in range(num_outputs)] + actual = [self.evaluate(get_next_ops) for _ in range(num_outputs)] self.match(expected, actual) diff --git a/tensorflow/python/data/experimental/kernel_tests/shuffle_and_repeat_test.py b/tensorflow/python/data/experimental/kernel_tests/shuffle_and_repeat_test.py index c208963a86..9528f83291 100644 --- a/tensorflow/python/data/experimental/kernel_tests/shuffle_and_repeat_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/shuffle_and_repeat_test.py @@ -24,6 +24,7 @@ from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import errors from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.platform import test @@ -34,16 +35,17 @@ class ShuffleAndRepeatTest(test_base.DatasetTestBase): shuffle_ops.shuffle_and_repeat(buffer_size=5, count=count, seed=seed)) def _gen_outputs(self, ds_fn, num_outputs, verify_exhausted=True): - get_next = ds_fn().make_one_shot_iterator().get_next() + get_next = dataset_ops.make_one_shot_iterator(ds_fn()).get_next() outputs = [] with self.cached_session() as sess: for _ in range(num_outputs): - outputs.append(sess.run(get_next)) + outputs.append(self.evaluate(get_next)) if verify_exhausted: with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) return outputs + @test_util.run_deprecated_v1 def testCorrectOutput(self): output = self._gen_outputs(lambda: self._build_ds(10), 100) self.assertSequenceEqual( @@ -52,6 +54,7 @@ class ShuffleAndRepeatTest(test_base.DatasetTestBase): for i in range(5): self.assertSequenceEqual(sorted(output[i * 20:(i + 1) * 20]), range(20)) + @test_util.run_deprecated_v1 def testReshuffling(self): # Check that the output orders of different epochs are indeed different. output = self._gen_outputs(lambda: self._build_ds(10), 100) @@ -60,17 +63,20 @@ class ShuffleAndRepeatTest(test_base.DatasetTestBase): epoch2 = output[(i + 1) * 20:(i + 2) * 20] self.assertNotEqual(epoch1, epoch2) + @test_util.run_deprecated_v1 def testSameOrderForSameSeeds(self): output1 = self._gen_outputs(lambda: self._build_ds(10), 100) output2 = self._gen_outputs(lambda: self._build_ds(10), 100) self.assertEqual(output1, output2) + @test_util.run_deprecated_v1 def testDifferentOrderForDifferentSeeds(self): output1 = self._gen_outputs(lambda: self._build_ds(10), 100) output2 = self._gen_outputs(lambda: self._build_ds(20), 100) self.assertNotEqual(output1, output2) self.assertEqual(sorted(output1), sorted(output2)) + @test_util.run_deprecated_v1 def testCountNone(self): output1 = self._gen_outputs( lambda: self._build_ds(10, count=None), 100, verify_exhausted=False) @@ -79,6 +85,7 @@ class ShuffleAndRepeatTest(test_base.DatasetTestBase): self.assertNotEqual(output1, output2) self.assertEqual(sorted(output1), sorted(output2)) + @test_util.run_deprecated_v1 def testCountMinusOne(self): output1 = self._gen_outputs( lambda: self._build_ds(10, count=-1), 100, verify_exhausted=False) @@ -108,7 +115,7 @@ class ShuffleAndRepeatTest(test_base.DatasetTestBase): shuffle_ops.shuffle_and_repeat(buffer_size=21)) get_next_op = ds.make_one_shot_iterator().get_next() with self.session(graph=g) as sess: - sess.run(get_next_op) + self.evaluate(get_next_op) if __name__ == "__main__": diff --git a/tensorflow/python/data/experimental/kernel_tests/sleep_test.py b/tensorflow/python/data/experimental/kernel_tests/sleep_test.py index bf53acc82a..46b22f80b6 100644 --- a/tensorflow/python/data/experimental/kernel_tests/sleep_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/sleep_test.py @@ -23,6 +23,7 @@ from tensorflow.python.data.experimental.ops import sleep from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import errors +from tensorflow.python.framework import test_util from tensorflow.python.platform import test _NUMPY_RANDOM_SEED = 42 @@ -30,22 +31,23 @@ _NUMPY_RANDOM_SEED = 42 class SleepTest(test_base.DatasetTestBase): + @test_util.run_deprecated_v1 def testSleep(self): sleep_microseconds = 100 dataset = dataset_ops.Dataset.range(10).apply( sleep.sleep(sleep_microseconds)) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) next_element = iterator.get_next() with self.cached_session() as sess: - sess.run(iterator.initializer) + self.evaluate(iterator.initializer) start_time = time.time() for i in range(10): - self.assertEqual(i, sess.run(next_element)) + self.assertEqual(i, self.evaluate(next_element)) end_time = time.time() self.assertGreater(end_time - start_time, (10 * sleep_microseconds) / 1e6) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) if __name__ == "__main__": diff --git a/tensorflow/python/data/experimental/kernel_tests/sql_dataset_test.py b/tensorflow/python/data/experimental/kernel_tests/sql_dataset_test.py index a2c1169638..eb66927ee5 100644 --- a/tensorflow/python/data/experimental/kernel_tests/sql_dataset_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/sql_dataset_test.py @@ -39,10 +39,11 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): "ORDER BY first_name DESC" }) for _ in range(2): # Dataset is repeated. See setUp. - self.assertEqual((b"John", b"Doe", b"Hi!"), sess.run(get_next)) - self.assertEqual((b"Jane", b"Moe", b"Hi again!"), sess.run(get_next)) + self.assertEqual((b"John", b"Doe", b"Hi!"), self.evaluate(get_next)) + self.assertEqual((b"Jane", b"Moe", b"Hi again!"), + self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Test that SqlDataset works on a join query. def testReadResultSetJoinQuery(self): @@ -58,9 +59,10 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): "ON students.first_name = people.first_name " "AND students.last_name = people.last_name" }) - self.assertEqual((b"John", b"California", b"Hi!"), sess.run(get_next)) + self.assertEqual((b"John", b"California", b"Hi!"), + self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Test that SqlDataset can read a database entry with a null-terminator # in the middle of the text and place the entry in a `string` tensor. @@ -75,10 +77,11 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): "SELECT first_name, last_name, favorite_nonsense_word " "FROM students ORDER BY first_name DESC" }) - self.assertEqual((b"John", b"Doe", b"n\0nsense"), sess.run(get_next)) - self.assertEqual((b"Jane", b"Moe", b"nonsense\0"), sess.run(get_next)) + self.assertEqual((b"John", b"Doe", b"n\0nsense"), self.evaluate(get_next)) + self.assertEqual((b"Jane", b"Moe", b"nonsense\0"), + self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Test that SqlDataset works when used on two different queries. # Because the output types of the dataset must be determined at graph-creation @@ -93,21 +96,22 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): self.query: "SELECT first_name, last_name, motto FROM students " "ORDER BY first_name DESC" }) - self.assertEqual((b"John", b"Doe", b"Hi!"), sess.run(get_next)) - self.assertEqual((b"Jane", b"Moe", b"Hi again!"), sess.run(get_next)) + self.assertEqual((b"John", b"Doe", b"Hi!"), self.evaluate(get_next)) + self.assertEqual((b"Jane", b"Moe", b"Hi again!"), self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) sess.run( init_op, feed_dict={ self.query: "SELECT first_name, last_name, state FROM people " "ORDER BY first_name DESC" }) - self.assertEqual((b"John", b"Doe", b"California"), sess.run(get_next)) + self.assertEqual((b"John", b"Doe", b"California"), + self.evaluate(get_next)) self.assertEqual((b"Benjamin", b"Franklin", b"Pennsylvania"), - sess.run(get_next)) + self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Test that an `OutOfRangeError` is raised on the first call to # `get_next_str_only` if result set is empty. @@ -122,7 +126,7 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): "WHERE first_name = 'Nonexistent'" }) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Test that an error is raised when `driver_name` is invalid. def testReadResultSetWithInvalidDriverName(self): @@ -151,7 +155,7 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): "ORDER BY first_name DESC" }) with self.assertRaises(errors.UnknownError): - sess.run(get_next) + self.evaluate(get_next) # Test that an error is raised when there is a syntax error in `query`. def testReadResultSetOfQueryWithSyntaxError(self): @@ -166,7 +170,7 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): "ORDER BY first_name DESC" }) with self.assertRaises(errors.UnknownError): - sess.run(get_next) + self.evaluate(get_next) # Test that an error is raised when the number of columns in `query` # does not match the length of `output_types`. @@ -181,7 +185,7 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): "ORDER BY first_name DESC" }) with self.assertRaises(errors.InvalidArgumentError): - sess.run(get_next) + self.evaluate(get_next) # Test that no results are returned when `query` is an insert query rather # than a select query. In particular, the error refers to the number of @@ -199,7 +203,7 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): "VALUES ('Foo', 'Bar', 'Baz'), ('Fizz', 'Buzz', 'Fizzbuzz')" }) with self.assertRaises(errors.InvalidArgumentError): - sess.run(get_next) + self.evaluate(get_next) # Test that `SqlDataset` can read an integer from a SQLite database table and # place it in an `int8` tensor. @@ -212,10 +216,10 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): self.query: "SELECT first_name, desk_number FROM students " "ORDER BY first_name DESC" }) - self.assertEqual((b"John", 9), sess.run(get_next)) - self.assertEqual((b"Jane", 127), sess.run(get_next)) + self.assertEqual((b"John", 9), self.evaluate(get_next)) + self.assertEqual((b"Jane", 127), self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Test that `SqlDataset` can read a negative or 0-valued integer from a # SQLite database table and place it in an `int8` tensor. @@ -230,9 +234,9 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): "FROM students " "WHERE first_name = 'John' ORDER BY first_name DESC" }) - self.assertEqual((b"John", 0, -2), sess.run(get_next)) + self.assertEqual((b"John", 0, -2), self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Test that `SqlDataset` can read a large (positive or negative) integer from # a SQLite database table and place it in an `int8` tensor. @@ -246,11 +250,11 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): "SELECT desk_number, favorite_negative_number FROM students " "ORDER BY first_name DESC" }) - self.assertEqual((9, -2), sess.run(get_next)) + self.assertEqual((9, -2), self.evaluate(get_next)) # Max and min values of int8 - self.assertEqual((127, -128), sess.run(get_next)) + self.assertEqual((127, -128), self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Test that `SqlDataset` can read an integer from a SQLite database table and # place it in an `int16` tensor. @@ -263,10 +267,10 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): self.query: "SELECT first_name, desk_number FROM students " "ORDER BY first_name DESC" }) - self.assertEqual((b"John", 9), sess.run(get_next)) - self.assertEqual((b"Jane", 127), sess.run(get_next)) + self.assertEqual((b"John", 9), self.evaluate(get_next)) + self.assertEqual((b"Jane", 127), self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Test that `SqlDataset` can read a negative or 0-valued integer from a # SQLite database table and place it in an `int16` tensor. @@ -281,9 +285,9 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): "FROM students " "WHERE first_name = 'John' ORDER BY first_name DESC" }) - self.assertEqual((b"John", 0, -2), sess.run(get_next)) + self.assertEqual((b"John", 0, -2), self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Test that `SqlDataset` can read a large (positive or negative) integer from # a SQLite database table and place it in an `int16` tensor. @@ -297,11 +301,11 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): "FROM students ORDER BY first_name DESC" }) # Max value of int16 - self.assertEqual((b"John", 32767), sess.run(get_next)) + self.assertEqual((b"John", 32767), self.evaluate(get_next)) # Min value of int16 - self.assertEqual((b"Jane", -32768), sess.run(get_next)) + self.assertEqual((b"Jane", -32768), self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Test that `SqlDataset` can read an integer from a SQLite database table and # place it in an `int32` tensor. @@ -314,8 +318,8 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): self.query: "SELECT first_name, desk_number FROM students " "ORDER BY first_name DESC" }) - self.assertEqual((b"John", 9), sess.run(get_next)) - self.assertEqual((b"Jane", 127), sess.run(get_next)) + self.assertEqual((b"John", 9), self.evaluate(get_next)) + self.assertEqual((b"Jane", 127), self.evaluate(get_next)) # Test that `SqlDataset` can read a negative or 0-valued integer from a # SQLite database table and place it in an `int32` tensor. @@ -328,10 +332,10 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): self.query: "SELECT first_name, income FROM students " "ORDER BY first_name DESC" }) - self.assertEqual((b"John", 0), sess.run(get_next)) - self.assertEqual((b"Jane", -20000), sess.run(get_next)) + self.assertEqual((b"John", 0), self.evaluate(get_next)) + self.assertEqual((b"Jane", -20000), self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Test that `SqlDataset` can read a large (positive or negative) integer from # a SQLite database table and place it in an `int32` tensor. @@ -345,11 +349,11 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): "ORDER BY first_name DESC" }) # Max value of int32 - self.assertEqual((b"John", 2147483647), sess.run(get_next)) + self.assertEqual((b"John", 2147483647), self.evaluate(get_next)) # Min value of int32 - self.assertEqual((b"Jane", -2147483648), sess.run(get_next)) + self.assertEqual((b"Jane", -2147483648), self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Test that `SqlDataset` can read a numeric `varchar` from a SQLite database # table and place it in an `int32` tensor. @@ -362,10 +366,10 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): self.query: "SELECT first_name, school_id FROM students " "ORDER BY first_name DESC" }) - self.assertEqual((b"John", 123), sess.run(get_next)) - self.assertEqual((b"Jane", 1000), sess.run(get_next)) + self.assertEqual((b"John", 123), self.evaluate(get_next)) + self.assertEqual((b"Jane", 1000), self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Test that `SqlDataset` can read an integer from a SQLite database table # and place it in an `int64` tensor. @@ -378,10 +382,10 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): self.query: "SELECT first_name, desk_number FROM students " "ORDER BY first_name DESC" }) - self.assertEqual((b"John", 9), sess.run(get_next)) - self.assertEqual((b"Jane", 127), sess.run(get_next)) + self.assertEqual((b"John", 9), self.evaluate(get_next)) + self.assertEqual((b"Jane", 127), self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Test that `SqlDataset` can read a negative or 0-valued integer from a # SQLite database table and place it in an `int64` tensor. @@ -394,10 +398,10 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): self.query: "SELECT first_name, income FROM students " "ORDER BY first_name DESC" }) - self.assertEqual((b"John", 0), sess.run(get_next)) - self.assertEqual((b"Jane", -20000), sess.run(get_next)) + self.assertEqual((b"John", 0), self.evaluate(get_next)) + self.assertEqual((b"Jane", -20000), self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Test that `SqlDataset` can read a large (positive or negative) integer from # a SQLite database table and place it in an `int64` tensor. @@ -412,11 +416,11 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): "ORDER BY first_name DESC" }) # Max value of int64 - self.assertEqual((b"John", 9223372036854775807), sess.run(get_next)) + self.assertEqual((b"John", 9223372036854775807), self.evaluate(get_next)) # Min value of int64 - self.assertEqual((b"Jane", -9223372036854775808), sess.run(get_next)) + self.assertEqual((b"Jane", -9223372036854775808), self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Test that `SqlDataset` can read an integer from a SQLite database table and # place it in a `uint8` tensor. @@ -429,10 +433,10 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): self.query: "SELECT first_name, desk_number FROM students " "ORDER BY first_name DESC" }) - self.assertEqual((b"John", 9), sess.run(get_next)) - self.assertEqual((b"Jane", 127), sess.run(get_next)) + self.assertEqual((b"John", 9), self.evaluate(get_next)) + self.assertEqual((b"Jane", 127), self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Test that `SqlDataset` can read the minimum and maximum uint8 values from a # SQLite database table and place them in `uint8` tensors. @@ -446,11 +450,11 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): "ORDER BY first_name DESC" }) # Min value of uint8 - self.assertEqual((b"John", 0), sess.run(get_next)) + self.assertEqual((b"John", 0), self.evaluate(get_next)) # Max value of uint8 - self.assertEqual((b"Jane", 255), sess.run(get_next)) + self.assertEqual((b"Jane", 255), self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Test that `SqlDataset` can read an integer from a SQLite database table # and place it in a `uint16` tensor. @@ -463,10 +467,10 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): self.query: "SELECT first_name, desk_number FROM students " "ORDER BY first_name DESC" }) - self.assertEqual((b"John", 9), sess.run(get_next)) - self.assertEqual((b"Jane", 127), sess.run(get_next)) + self.assertEqual((b"John", 9), self.evaluate(get_next)) + self.assertEqual((b"Jane", 127), self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Test that `SqlDataset` can read the minimum and maximum uint16 values from a # SQLite database table and place them in `uint16` tensors. @@ -480,11 +484,11 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): "ORDER BY first_name DESC" }) # Min value of uint16 - self.assertEqual((b"John", 0), sess.run(get_next)) + self.assertEqual((b"John", 0), self.evaluate(get_next)) # Max value of uint16 - self.assertEqual((b"Jane", 65535), sess.run(get_next)) + self.assertEqual((b"Jane", 65535), self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Test that `SqlDataset` can read a 0-valued and 1-valued integer from a # SQLite database table and place them as `True` and `False` respectively @@ -499,10 +503,10 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): "SELECT first_name, registration_complete FROM students " "ORDER BY first_name DESC" }) - self.assertEqual((b"John", True), sess.run(get_next)) - self.assertEqual((b"Jane", False), sess.run(get_next)) + self.assertEqual((b"John", True), self.evaluate(get_next)) + self.assertEqual((b"Jane", False), self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Test that `SqlDataset` can read an integer that is not 0-valued or 1-valued # from a SQLite database table and place it as `True` in a `bool` tensor. @@ -515,10 +519,10 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): self.query: "SELECT first_name, favorite_medium_sized_number " "FROM students ORDER BY first_name DESC" }) - self.assertEqual((b"John", True), sess.run(get_next)) - self.assertEqual((b"Jane", True), sess.run(get_next)) + self.assertEqual((b"John", True), self.evaluate(get_next)) + self.assertEqual((b"Jane", True), self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Test that `SqlDataset` can read a float from a SQLite database table # and place it in a `float64` tensor. @@ -533,10 +537,11 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): "SELECT first_name, last_name, victories FROM townspeople " "ORDER BY first_name" }) - self.assertEqual((b"George", b"Washington", 20.0), sess.run(get_next)) - self.assertEqual((b"John", b"Adams", -19.95), sess.run(get_next)) + self.assertEqual((b"George", b"Washington", 20.0), + self.evaluate(get_next)) + self.assertEqual((b"John", b"Adams", -19.95), self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Test that `SqlDataset` can read a float from a SQLite database table beyond # the precision of 64-bit IEEE, without throwing an error. Test that @@ -555,13 +560,13 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): self.assertEqual( (b"George", b"Washington", 1331241.321342132321324589798264627463827647382647382643874), - sess.run(get_next)) + self.evaluate(get_next)) self.assertEqual( (b"John", b"Adams", 1331241321342132321324589798264627463827647382647382643874.0), - sess.run(get_next)) + self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) # Test that `SqlDataset` can read a float from a SQLite database table, # representing the largest integer representable as a 64-bit IEEE float @@ -579,11 +584,11 @@ class SqlDatasetTest(sql_dataset_test_base.SqlDatasetTestBase): "ORDER BY first_name" }) self.assertNotEqual((b"George", b"Washington", 9007199254740992.0), - sess.run(get_next)) + self.evaluate(get_next)) self.assertNotEqual((b"John", b"Adams", 9007199254740991.0), - sess.run(get_next)) + self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + self.evaluate(get_next) if __name__ == "__main__": diff --git a/tensorflow/python/data/experimental/kernel_tests/sql_dataset_test_base.py b/tensorflow/python/data/experimental/kernel_tests/sql_dataset_test_base.py index 6aaaa90c65..809e09c804 100644 --- a/tensorflow/python/data/experimental/kernel_tests/sql_dataset_test_base.py +++ b/tensorflow/python/data/experimental/kernel_tests/sql_dataset_test_base.py @@ -24,6 +24,7 @@ import sqlite3 from tensorflow.python.data.experimental.ops import readers from tensorflow.python.data.kernel_tests import test_base +from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import dtypes from tensorflow.python.ops import array_ops from tensorflow.python.platform import test @@ -35,7 +36,7 @@ class SqlDatasetTestBase(test_base.DatasetTestBase): def _createSqlDataset(self, output_types, num_repeats=1): dataset = readers.SqlDataset(self.driver_name, self.data_source_name, self.query, output_types).repeat(num_repeats) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) init_op = iterator.initializer get_next = iterator.get_next() return init_op, get_next diff --git a/tensorflow/python/data/experimental/kernel_tests/stats_dataset_ops_test.py b/tensorflow/python/data/experimental/kernel_tests/stats_dataset_ops_test.py index 83028937d3..8a300364f9 100644 --- a/tensorflow/python/data/experimental/kernel_tests/stats_dataset_ops_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/stats_dataset_ops_test.py @@ -30,6 +30,7 @@ from tensorflow.python.data.experimental.ops import stats_options from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import errors from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops from tensorflow.python.platform import test @@ -45,84 +46,84 @@ def function_set_stats_aggregator(dataset, def function_apply_options(dataset, aggregator, prefix="", counter_prefix=""): options = dataset_ops.Options() - options.experimental_stats = stats_options.StatsOptions(aggregator) + options.experimental_stats = stats_options.StatsOptions() + options.experimental_stats.aggregator = aggregator + options.experimental_stats.prefix = prefix + options.experimental_stats.counter_prefix = counter_prefix options.experimental_stats.latency_all_edges = False - if prefix: - options.experimental_stats.prefix = prefix - if counter_prefix: - options.experimental_stats.counter_prefix = counter_prefix return dataset.with_options(options) @parameterized.named_parameters( - dict( - testcase_name="SetStatsAggregator", - dataset_transformation=function_set_stats_aggregator), - dict( - testcase_name="StatsOptions", - dataset_transformation=function_apply_options)) + ("SetStatsAggregator", function_set_stats_aggregator), + ("StatsOptions", function_apply_options), +) class StatsDatasetTest(stats_dataset_test_base.StatsDatasetTestBase): + @test_util.run_deprecated_v1 def testBytesProduced(self, dataset_transformation): aggregator = stats_aggregator.StatsAggregator() dataset = dataset_ops.Dataset.range(100).map( lambda x: array_ops.tile([x], ops.convert_to_tensor([x]))).apply( stats_ops.bytes_produced_stats("bytes_produced")) dataset = dataset_transformation(dataset, aggregator) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) next_element = iterator.get_next() summary_t = aggregator.get_summary() with self.cached_session() as sess: - sess.run(iterator.initializer) + self.evaluate(iterator.initializer) expected_sum = 0.0 for i in range(100): self.assertAllEqual( - np.array([i] * i, dtype=np.int64), sess.run(next_element)) - summary_str = sess.run(summary_t) + np.array([i] * i, dtype=np.int64), self.evaluate(next_element)) + summary_str = self.evaluate(summary_t) self._assertSummaryHasCount(summary_str, "bytes_produced", float(i + 1)) expected_sum += i * 8.0 self._assertSummaryHasSum(summary_str, "bytes_produced", expected_sum) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) - summary_str = sess.run(summary_t) + self.evaluate(next_element) + summary_str = self.evaluate(summary_t) self._assertSummaryHasCount(summary_str, "bytes_produced", 100.0) self._assertSummaryHasSum(summary_str, "bytes_produced", expected_sum) + @test_util.run_deprecated_v1 def testLatencyStats(self, dataset_transformation): aggregator = stats_aggregator.StatsAggregator() dataset = dataset_ops.Dataset.range(100).apply( stats_ops.latency_stats("record_latency")) dataset = dataset_transformation(dataset, aggregator) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) next_element = iterator.get_next() summary_t = aggregator.get_summary() with self.cached_session() as sess: - sess.run(iterator.initializer) + self.evaluate(iterator.initializer) for i in range(100): - self.assertEqual(i, sess.run(next_element)) + self.assertEqual(i, self.evaluate(next_element)) self._assertSummaryHasCount( - sess.run(summary_t), "record_latency", float(i + 1)) + self.evaluate(summary_t), "record_latency", float(i + 1)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) - self._assertSummaryHasCount(sess.run(summary_t), "record_latency", 100.0) + self.evaluate(next_element) + self._assertSummaryHasCount( + self.evaluate(summary_t), "record_latency", 100.0) + @test_util.run_deprecated_v1 def testPrefetchBufferUtilization(self, dataset_transformation): aggregator = stats_aggregator.StatsAggregator() dataset = dataset_ops.Dataset.range(100).map( lambda x: array_ops.tile([x], ops.convert_to_tensor([x]))).prefetch(-1) dataset = dataset_transformation(dataset, aggregator) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) next_element = iterator.get_next() summary_t = aggregator.get_summary() with self.cached_session() as sess: - sess.run(iterator.initializer) + self.evaluate(iterator.initializer) for i in range(100): self.assertAllEqual( - np.array([i] * i, dtype=np.int64), sess.run(next_element)) - summary_str = sess.run(summary_t) + np.array([i] * i, dtype=np.int64), self.evaluate(next_element)) + summary_str = self.evaluate(summary_t) self._assertSummaryHasCount(summary_str, "Prefetch::buffer_utilization", float(i + 1)) self._assertSummaryContains(summary_str, "Prefetch::buffer_capacity") @@ -130,58 +131,62 @@ class StatsDatasetTest(stats_dataset_test_base.StatsDatasetTestBase): self._assertSummaryHasRange(summary_str, "Prefetch::buffer_utilization", 0, 1) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) - summary_str = sess.run(summary_t) + self.evaluate(next_element) + summary_str = self.evaluate(summary_t) self._assertSummaryHasCount(summary_str, "Prefetch::buffer_utilization", 100) + @test_util.run_deprecated_v1 def testPrefetchBufferScalars(self, dataset_transformation): aggregator = stats_aggregator.StatsAggregator() dataset = dataset_ops.Dataset.range(10).map( lambda x: array_ops.tile([x], ops.convert_to_tensor([x]))).prefetch(0) dataset = dataset_transformation(dataset, aggregator) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) next_element = iterator.get_next() summary_t = aggregator.get_summary() with self.cached_session() as sess: - sess.run(iterator.initializer) + self.evaluate(iterator.initializer) for i in range(10): self.assertAllEqual( - np.array([i] * i, dtype=np.int64), sess.run(next_element)) - summary_str = sess.run(summary_t) + np.array([i] * i, dtype=np.int64), self.evaluate(next_element)) + summary_str = self.evaluate(summary_t) self._assertSummaryHasScalarValue(summary_str, "Prefetch::buffer_capacity", 0) self._assertSummaryHasScalarValue(summary_str, "Prefetch::buffer_size", 0) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) + @test_util.run_deprecated_v1 def testFilteredElementsStats(self, dataset_transformation): aggregator = stats_aggregator.StatsAggregator() dataset = dataset_ops.Dataset.range(101).filter( lambda x: math_ops.equal(math_ops.mod(x, 3), 0)) dataset = dataset_transformation(dataset, aggregator) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) next_element = iterator.get_next() summary_t = aggregator.get_summary() with self.test_session() as sess: - sess.run(iterator.initializer) + self.evaluate(iterator.initializer) for i in range(34): - self.assertEqual(i * 3, sess.run(next_element)) + self.assertEqual(i * 3, self.evaluate(next_element)) if i is not 0: self._assertSummaryHasScalarValue( - sess.run(summary_t), "Filter::dropped_elements", float(i * 2)) + self.evaluate(summary_t), "Filter::dropped_elements", + float(i * 2)) self._assertSummaryHasScalarValue( - sess.run(summary_t), "Filter::filtered_elements", float(i + 1)) + self.evaluate(summary_t), "Filter::filtered_elements", float(i + 1)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) self._assertSummaryHasScalarValue( - sess.run(summary_t), "Filter::dropped_elements", 67.0) + self.evaluate(summary_t), "Filter::dropped_elements", 67.0) self._assertSummaryHasScalarValue( - sess.run(summary_t), "Filter::filtered_elements", 34.0) + self.evaluate(summary_t), "Filter::filtered_elements", 34.0) + @test_util.run_deprecated_v1 def testMapBufferUtilization(self, dataset_transformation): def dataset_fn(): @@ -196,6 +201,7 @@ class StatsDatasetTest(stats_dataset_test_base.StatsDatasetTestBase): dataset_transformation, function_processing_time=True) + @test_util.run_deprecated_v1 def testMapAutoTuneBufferUtilization(self, dataset_transformation): def dataset_fn(): @@ -213,6 +219,7 @@ class StatsDatasetTest(stats_dataset_test_base.StatsDatasetTestBase): dataset_transformation, function_processing_time=True) + @test_util.run_deprecated_v1 def testInterleaveAutoTuneBufferUtilization(self, dataset_transformation): def dataset_fn(): @@ -229,6 +236,7 @@ class StatsDatasetTest(stats_dataset_test_base.StatsDatasetTestBase): self._testParallelCallsStats(dataset_fn, "ParallelInterleaveV2", 10, dataset_transformation) + @test_util.run_deprecated_v1 def testMapAndBatchAutoTuneBufferUtilization(self, dataset_transformation): def dataset_fn(): @@ -250,104 +258,114 @@ class StatsDatasetTest(stats_dataset_test_base.StatsDatasetTestBase): check_elements=False, function_processing_time=True) + @test_util.run_deprecated_v1 def testReinitialize(self, dataset_transformation): aggregator = stats_aggregator.StatsAggregator() dataset = dataset_ops.Dataset.range(100).apply( stats_ops.latency_stats("record_latency")) dataset = dataset_transformation(dataset, aggregator) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) next_element = iterator.get_next() summary_t = aggregator.get_summary() with self.cached_session() as sess: for j in range(5): - sess.run(iterator.initializer) + self.evaluate(iterator.initializer) for i in range(100): - self.assertEqual(i, sess.run(next_element)) + self.assertEqual(i, self.evaluate(next_element)) self._assertSummaryHasCount( - sess.run(summary_t), "record_latency", float((j * 100) + i + 1)) + self.evaluate(summary_t), "record_latency", + float((j * 100) + i + 1)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) self._assertSummaryHasCount( - sess.run(summary_t), "record_latency", (j + 1) * 100.0) + self.evaluate(summary_t), "record_latency", (j + 1) * 100.0) + @test_util.run_deprecated_v1 def testNoAggregatorRegistered(self, dataset_transformation): dataset = dataset_ops.Dataset.range(100).apply( stats_ops.latency_stats("record_latency")) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) next_element = iterator.get_next() with self.cached_session() as sess: - sess.run(iterator.initializer) + self.evaluate(iterator.initializer) for i in range(100): - self.assertEqual(i, sess.run(next_element)) + self.assertEqual(i, self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) + @test_util.run_deprecated_v1 def testMultipleTags(self, dataset_transformation): aggregator = stats_aggregator.StatsAggregator() dataset = dataset_ops.Dataset.range(100).apply( stats_ops.latency_stats("record_latency")).apply( stats_ops.latency_stats("record_latency_2")) dataset = dataset_transformation(dataset, aggregator) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) next_element = iterator.get_next() summary_t = aggregator.get_summary() with self.cached_session() as sess: - sess.run(iterator.initializer) + self.evaluate(iterator.initializer) for i in range(100): - self.assertEqual(i, sess.run(next_element)) + self.assertEqual(i, self.evaluate(next_element)) self._assertSummaryHasCount( - sess.run(summary_t), "record_latency", float(i + 1)) + self.evaluate(summary_t), "record_latency", float(i + 1)) self._assertSummaryHasCount( - sess.run(summary_t), "record_latency_2", float(i + 1)) + self.evaluate(summary_t), "record_latency_2", float(i + 1)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) - self._assertSummaryHasCount(sess.run(summary_t), "record_latency", 100.0) + self.evaluate(next_element) + self._assertSummaryHasCount( + self.evaluate(summary_t), "record_latency", 100.0) self._assertSummaryHasCount( - sess.run(summary_t), "record_latency_2", 100.0) + self.evaluate(summary_t), "record_latency_2", 100.0) + @test_util.run_deprecated_v1 def testRepeatedTags(self, dataset_transformation): aggregator = stats_aggregator.StatsAggregator() dataset = dataset_ops.Dataset.range(100).apply( stats_ops.latency_stats("record_latency")).apply( stats_ops.latency_stats("record_latency")) dataset = dataset_transformation(dataset, aggregator) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) next_element = iterator.get_next() summary_t = aggregator.get_summary() with self.cached_session() as sess: - sess.run(iterator.initializer) + self.evaluate(iterator.initializer) for i in range(100): - self.assertEqual(i, sess.run(next_element)) + self.assertEqual(i, self.evaluate(next_element)) self._assertSummaryHasCount( - sess.run(summary_t), "record_latency", float(2 * (i + 1))) + self.evaluate(summary_t), "record_latency", float(2 * (i + 1))) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) - self._assertSummaryHasCount(sess.run(summary_t), "record_latency", 200.0) + self.evaluate(next_element) + self._assertSummaryHasCount( + self.evaluate(summary_t), "record_latency", 200.0) + @test_util.run_deprecated_v1 def testMultipleIteratorsSameAggregator(self, dataset_transformation): aggregator = stats_aggregator.StatsAggregator() dataset = dataset_ops.Dataset.range(100).apply( stats_ops.latency_stats("record_latency")) dataset = dataset_transformation(dataset, aggregator) - iterator_0 = dataset.make_initializable_iterator() - iterator_1 = dataset.make_initializable_iterator() + iterator_0 = dataset_ops.make_initializable_iterator(dataset) + iterator_1 = dataset_ops.make_initializable_iterator(dataset) next_element = iterator_0.get_next() + iterator_1.get_next() summary_t = aggregator.get_summary() with self.cached_session() as sess: - sess.run([iterator_0.initializer, iterator_1.initializer]) + self.evaluate([iterator_0.initializer, iterator_1.initializer]) for i in range(100): - self.assertEqual(i * 2, sess.run(next_element)) + self.assertEqual(i * 2, self.evaluate(next_element)) self._assertSummaryHasCount( - sess.run(summary_t), "record_latency", float(2 * (i + 1))) + self.evaluate(summary_t), "record_latency", float(2 * (i + 1))) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) - self._assertSummaryHasCount(sess.run(summary_t), "record_latency", 200.0) + self.evaluate(next_element) + self._assertSummaryHasCount( + self.evaluate(summary_t), "record_latency", 200.0) + @test_util.run_deprecated_v1 def testMultipleDatasetWithPrefixes(self, dataset_transformation): aggregator = stats_aggregator.StatsAggregator() dataset = dataset_ops.Dataset.range(100).apply( @@ -356,25 +374,25 @@ class StatsDatasetTest(stats_dataset_test_base.StatsDatasetTestBase): dataset2 = dataset_ops.Dataset.range(100).apply( stats_ops.latency_stats("record_latency")) dataset2 = dataset_transformation(dataset2, aggregator, prefix="dataset2") - iterator_0 = dataset.make_initializable_iterator() - iterator_1 = dataset2.make_initializable_iterator() + iterator_0 = dataset_ops.make_initializable_iterator(dataset) + iterator_1 = dataset_ops.make_initializable_iterator(dataset2) next_element = iterator_0.get_next() + iterator_1.get_next() summary_t = aggregator.get_summary() with self.test_session() as sess: - sess.run([iterator_0.initializer, iterator_1.initializer]) + self.evaluate([iterator_0.initializer, iterator_1.initializer]) for i in range(100): - self.assertEqual(i * 2, sess.run(next_element)) + self.assertEqual(i * 2, self.evaluate(next_element)) self._assertSummaryHasCount( - sess.run(summary_t), "dataset1_record_latency", float(i + 1)) + self.evaluate(summary_t), "dataset1_record_latency", float(i + 1)) self._assertSummaryHasCount( - sess.run(summary_t), "dataset2_record_latency", float(i + 1)) + self.evaluate(summary_t), "dataset2_record_latency", float(i + 1)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) self._assertSummaryHasCount( - sess.run(summary_t), "dataset1_record_latency", 100.0) + self.evaluate(summary_t), "dataset1_record_latency", 100.0) self._assertSummaryHasCount( - sess.run(summary_t), "dataset2_record_latency", 100.0) + self.evaluate(summary_t), "dataset2_record_latency", 100.0) @parameterized.named_parameters( @@ -388,6 +406,7 @@ class FeatureStatsDatasetTest( stats_dataset_test_base.StatsDatasetTestBase, reader_dataset_ops_test_base.MakeBatchedFeaturesDatasetTestBase): + @test_util.run_deprecated_v1 def testFeaturesStats(self, dataset_transformation): num_epochs = 5 total_records = num_epochs * self._num_records @@ -416,25 +435,26 @@ class FeatureStatsDatasetTest( dataset = dataset_transformation( dataset_fn(), aggregator, prefix="record_stats") - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) next_element = iterator.get_next() summary_t = aggregator.get_summary() with self.test_session() as sess: - sess.run(iterator.initializer) + self.evaluate(iterator.initializer) for _ in range(num_output): - sess.run(next_element) + self.evaluate(next_element) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) self._assertSummaryHasCount( - sess.run(summary_t), "record_stats_features", total_records) + self.evaluate(summary_t), "record_stats_features", total_records) self._assertSummaryHasCount( - sess.run(summary_t), "record_stats_feature-values", total_records) + self.evaluate(summary_t), "record_stats_feature-values", + total_records) self._assertSummaryHasSum( - sess.run(summary_t), "record_stats_features", total_records * 4) + self.evaluate(summary_t), "record_stats_features", total_records * 4) self._assertSummaryHasSum( - sess.run(summary_t), "record_stats_feature-values", + self.evaluate(summary_t), "record_stats_feature-values", self._sum_keywords(1) * num_epochs + 3 * total_records) diff --git a/tensorflow/python/data/experimental/kernel_tests/stats_dataset_test_base.py b/tensorflow/python/data/experimental/kernel_tests/stats_dataset_test_base.py index c5bf926759..ab1d1c3028 100644 --- a/tensorflow/python/data/experimental/kernel_tests/stats_dataset_test_base.py +++ b/tensorflow/python/data/experimental/kernel_tests/stats_dataset_test_base.py @@ -22,6 +22,7 @@ import numpy as np from tensorflow.core.framework import summary_pb2 from tensorflow.python.data.experimental.ops import stats_aggregator from tensorflow.python.data.kernel_tests import test_base +from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import errors @@ -93,7 +94,7 @@ class StatsDatasetTestBase(test_base.DatasetTestBase): aggregator = stats_aggregator.StatsAggregator() dataset = dataset_fn() dataset = dataset_transformation(dataset, aggregator) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) next_element = iterator.get_next() summary_t = aggregator.get_summary() diff --git a/tensorflow/python/data/experimental/kernel_tests/unbatch_test.py b/tensorflow/python/data/experimental/kernel_tests/unbatch_test.py index 0278a208cb..cef5e8d269 100644 --- a/tensorflow/python/data/experimental/kernel_tests/unbatch_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/unbatch_test.py @@ -17,20 +17,18 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -import time from absl.testing import parameterized import numpy as np -from tensorflow.python.client import session from tensorflow.python.data.experimental.ops import batching from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors -from tensorflow.python.framework import ops from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import string_ops @@ -40,20 +38,22 @@ from tensorflow.python.util import compat class UnbatchTest(test_base.DatasetTestBase, parameterized.TestCase): + @test_util.run_deprecated_v1 def testUnbatchWithUnknownRankInput(self): placeholder = array_ops.placeholder(dtypes.int32) dataset = dataset_ops.Dataset.from_tensors(placeholder).apply( batching.unbatch()) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) next_elem = iterator.get_next() with self.cached_session() as sess: sess.run(iterator.initializer, feed_dict={placeholder: [0, 1, 2, 3]}) for i in range(4): - self.assertEqual(i, sess.run(next_elem)) + self.assertEqual(i, self.evaluate(next_elem)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_elem) + self.evaluate(next_elem) + @test_util.run_deprecated_v1 def testUnbatchScalarDataset(self): data = tuple([math_ops.range(10) for _ in range(3)]) data = dataset_ops.Dataset.from_tensor_slices(data) @@ -63,16 +63,17 @@ class UnbatchTest(test_base.DatasetTestBase, parameterized.TestCase): data = data.apply(batching.unbatch()) self.assertEqual(expected_types, data.output_types) - iterator = data.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(data) op = iterator.get_next() with self.cached_session() as sess: for i in range(10): - self.assertEqual((i,) * 3, sess.run(op)) + self.assertEqual((i,) * 3, self.evaluate(op)) with self.assertRaises(errors.OutOfRangeError): - sess.run(op) + self.evaluate(op) + @test_util.run_deprecated_v1 def testUnbatchDatasetWithStrings(self): data = tuple([math_ops.range(10) for _ in range(3)]) data = dataset_ops.Dataset.from_tensor_slices(data) @@ -83,16 +84,17 @@ class UnbatchTest(test_base.DatasetTestBase, parameterized.TestCase): data = data.apply(batching.unbatch()) self.assertEqual(expected_types, data.output_types) - iterator = data.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(data) op = iterator.get_next() with self.cached_session() as sess: for i in range(10): - self.assertEqual((i, compat.as_bytes(str(i)), i), sess.run(op)) + self.assertEqual((i, compat.as_bytes(str(i)), i), self.evaluate(op)) with self.assertRaises(errors.OutOfRangeError): - sess.run(op) + self.evaluate(op) + @test_util.run_deprecated_v1 def testUnbatchDatasetWithSparseTensor(self): st = sparse_tensor.SparseTensorValue( indices=[[i, i] for i in range(10)], @@ -102,18 +104,19 @@ class UnbatchTest(test_base.DatasetTestBase, parameterized.TestCase): data = data.apply(batching.unbatch()) data = data.batch(5) data = data.apply(batching.unbatch()) - iterator = data.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(data) next_element = iterator.get_next() with self.cached_session() as sess: for i in range(10): - st_row = sess.run(next_element) + st_row = self.evaluate(next_element) self.assertEqual([i], st_row.indices) self.assertEqual([i], st_row.values) self.assertEqual([10], st_row.dense_shape) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) + @test_util.run_deprecated_v1 def testUnbatchDatasetWithDenseAndSparseTensor(self): st = sparse_tensor.SparseTensorValue( indices=[[i, i] for i in range(10)], @@ -123,19 +126,20 @@ class UnbatchTest(test_base.DatasetTestBase, parameterized.TestCase): data = data.apply(batching.unbatch()) data = data.batch(5) data = data.apply(batching.unbatch()) - iterator = data.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(data) next_element = iterator.get_next() with self.cached_session() as sess: for i in range(10): - dense_elem, st_row = sess.run(next_element) + dense_elem, st_row = self.evaluate(next_element) self.assertEqual(i, dense_elem) self.assertEqual([i], st_row.indices) self.assertEqual([i], st_row.values) self.assertEqual([10], st_row.dense_shape) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) + @test_util.run_deprecated_v1 def testUnbatchSingleElementTupleDataset(self): data = tuple([(math_ops.range(10),) for _ in range(3)]) data = dataset_ops.Dataset.from_tensor_slices(data) @@ -145,16 +149,17 @@ class UnbatchTest(test_base.DatasetTestBase, parameterized.TestCase): data = data.apply(batching.unbatch()) self.assertEqual(expected_types, data.output_types) - iterator = data.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(data) op = iterator.get_next() with self.cached_session() as sess: for i in range(10): - self.assertEqual(((i,),) * 3, sess.run(op)) + self.assertEqual(((i,),) * 3, self.evaluate(op)) with self.assertRaises(errors.OutOfRangeError): - sess.run(op) + self.evaluate(op) + @test_util.run_deprecated_v1 def testUnbatchMultiElementTupleDataset(self): data = tuple([(math_ops.range(10 * i, 10 * i + 10), array_ops.fill([10], "hi")) for i in range(3)]) @@ -165,28 +170,29 @@ class UnbatchTest(test_base.DatasetTestBase, parameterized.TestCase): data = data.apply(batching.unbatch()) self.assertAllEqual(expected_types, data.output_types) - iterator = data.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(data) op = iterator.get_next() with self.cached_session() as sess: for i in range(10): self.assertEqual(((i, b"hi"), (10 + i, b"hi"), (20 + i, b"hi")), - sess.run(op)) + self.evaluate(op)) with self.assertRaises(errors.OutOfRangeError): - sess.run(op) + self.evaluate(op) + @test_util.run_deprecated_v1 def testUnbatchEmpty(self): data = dataset_ops.Dataset.from_tensors( (constant_op.constant([]), constant_op.constant([], shape=[0, 4]), constant_op.constant([], shape=[0, 4, 0]))) data = data.apply(batching.unbatch()) - iterator = data.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(data) next_element = iterator.get_next() with self.cached_session() as sess: with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) def testUnbatchStaticShapeMismatch(self): data = dataset_ops.Dataset.from_tensors((np.arange(7), np.arange(8), @@ -194,12 +200,13 @@ class UnbatchTest(test_base.DatasetTestBase, parameterized.TestCase): with self.assertRaises(ValueError): data.apply(batching.unbatch()) + @test_util.run_deprecated_v1 def testUnbatchDynamicShapeMismatch(self): ph1 = array_ops.placeholder(dtypes.int32, shape=[None]) ph2 = array_ops.placeholder(dtypes.int32, shape=None) data = dataset_ops.Dataset.from_tensors((ph1, ph2)) data = data.apply(batching.unbatch()) - iterator = data.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(data) next_element = iterator.get_next() with self.cached_session() as sess: @@ -211,7 +218,7 @@ class UnbatchTest(test_base.DatasetTestBase, parameterized.TestCase): ph2: np.arange(8).astype(np.int32) }) with self.assertRaises(errors.InvalidArgumentError): - sess.run(next_element) + self.evaluate(next_element) # No 0th dimension (i.e. scalar value) for one component. sess.run( @@ -221,79 +228,7 @@ class UnbatchTest(test_base.DatasetTestBase, parameterized.TestCase): ph2: 7 }) with self.assertRaises(errors.InvalidArgumentError): - sess.run(next_element) - - -class UnbatchBenchmark(test.Benchmark): - - def benchmarkNativeUnbatch(self): - batch_sizes = [1, 2, 5, 10, 20, 50] - elems_per_trial = 10000 - with ops.Graph().as_default(): - dataset = dataset_ops.Dataset.from_tensors("element").repeat(None) - batch_size_placeholder = array_ops.placeholder(dtypes.int64, shape=[]) - dataset = dataset.batch(batch_size_placeholder) - dataset = dataset.apply(batching.unbatch()) - dataset = dataset.skip(elems_per_trial) - iterator = dataset.make_initializable_iterator() - next_element = iterator.get_next() - - with session.Session() as sess: - for batch_size in batch_sizes: - deltas = [] - for _ in range(5): - sess.run( - iterator.initializer, - feed_dict={batch_size_placeholder: batch_size}) - start = time.time() - sess.run(next_element.op) - end = time.time() - deltas.append((end - start) / elems_per_trial) - - median_wall_time = np.median(deltas) - print("Unbatch (native) batch size: %d Median wall time per element:" - " %f microseconds" % (batch_size, median_wall_time * 1e6)) - self.report_benchmark( - iters=10000, - wall_time=median_wall_time, - name="benchmark_unbatch_dataset_native_batch_size_%d" % - batch_size) - - # Include a benchmark of the previous `unbatch()` implementation that uses - # a composition of more primitive ops. Eventually we'd hope to generate code - # that is as good in both cases. - def benchmarkOldUnbatchImplementation(self): - batch_sizes = [1, 2, 5, 10, 20, 50] - elems_per_trial = 10000 - with ops.Graph().as_default(): - dataset = dataset_ops.Dataset.from_tensors("element").repeat(None) - batch_size_placeholder = array_ops.placeholder(dtypes.int64, shape=[]) - dataset = dataset.batch(batch_size_placeholder) - dataset = dataset.flat_map(dataset_ops.Dataset.from_tensor_slices) - dataset = dataset.skip(elems_per_trial) - iterator = dataset.make_initializable_iterator() - next_element = iterator.get_next() - - with session.Session() as sess: - for batch_size in batch_sizes: - deltas = [] - for _ in range(5): - sess.run( - iterator.initializer, - feed_dict={batch_size_placeholder: batch_size}) - start = time.time() - sess.run(next_element.op) - end = time.time() - deltas.append((end - start) / elems_per_trial) - - median_wall_time = np.median(deltas) - print("Unbatch (unfused) batch size: %d Median wall time per element:" - " %f microseconds" % (batch_size, median_wall_time * 1e6)) - self.report_benchmark( - iters=10000, - wall_time=median_wall_time, - name="benchmark_unbatch_dataset_unfused_batch_size_%d" % - batch_size) + self.evaluate(next_element) if __name__ == "__main__": diff --git a/tensorflow/python/data/experimental/kernel_tests/unique_test.py b/tensorflow/python/data/experimental/kernel_tests/unique_test.py index 847cff26b0..1d9941d7f4 100644 --- a/tensorflow/python/data/experimental/kernel_tests/unique_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/unique_test.py @@ -22,6 +22,7 @@ from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors +from tensorflow.python.framework import test_util from tensorflow.python.platform import test from tensorflow.python.util import compat @@ -43,20 +44,21 @@ class UniqueTest(test_base.DatasetTestBase): current_test_case = [] dataset = dataset_ops.Dataset.from_generator(lambda: current_test_case, dtype).apply(unique.unique()) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) next_element = iterator.get_next() with self.cached_session() as sess: for test_case, expected in test_cases: current_test_case = test_case - sess.run(iterator.initializer) + self.evaluate(iterator.initializer) for element in expected: if dtype == dtypes.string: element = compat.as_bytes(element) - self.assertAllEqual(element, sess.run(next_element)) + self.assertAllEqual(element, self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) + @test_util.run_deprecated_v1 def testSimpleInt(self): for dtype in [dtypes.int32, dtypes.int64]: self._testSimpleHelper(dtype, [ @@ -69,6 +71,7 @@ class UniqueTest(test_base.DatasetTestBase): ([[1, 1], [1, 1], [2, 2], [3, 3], [1, 1]], [[1, 1], [2, 2], [3, 3]]), ]) + @test_util.run_deprecated_v1 def testSimpleString(self): self._testSimpleHelper(dtypes.string, [ ([], []), diff --git a/tensorflow/python/data/experimental/ops/BUILD b/tensorflow/python/data/experimental/ops/BUILD index 170fda90b6..43f43182f6 100644 --- a/tensorflow/python/data/experimental/ops/BUILD +++ b/tensorflow/python/data/experimental/ops/BUILD @@ -4,6 +4,16 @@ licenses(["notice"]) # Apache 2.0 exports_files(["LICENSE"]) +py_library( + name = "cardinality", + srcs = ["cardinality.py"], + srcs_version = "PY2AND3", + deps = [ + "//tensorflow/python:experimental_dataset_ops_gen", + "//tensorflow/python:tensor_util", + ], +) + py_library( name = "counter", srcs = ["counter.py"], @@ -54,8 +64,8 @@ py_library( srcs_version = "PY2AND3", deps = [ "//tensorflow/python:constant_op", - "//tensorflow/python:dataset_ops_gen", "//tensorflow/python:dtypes", + "//tensorflow/python:experimental_dataset_ops_gen", "//tensorflow/python:framework_ops", "//tensorflow/python:random_seed", "//tensorflow/python:tensor_shape", @@ -125,6 +135,7 @@ py_library( "//tensorflow/python/data/util:convert", "//tensorflow/python/data/util:nest", "//tensorflow/python/data/util:sparse", + "//tensorflow/python/data/util:structure", "//third_party/py/numpy", ], ) @@ -139,6 +150,18 @@ py_library( ], ) +py_library( + name = "filter_for_shard_ops", + srcs = ["filter_for_shard_ops.py"], + srcs_version = "PY2AND3", + deps = [ + "//tensorflow/python:dtypes", + "//tensorflow/python:math_ops", + "//tensorflow/python:ops", + "//tensorflow/python:tensor_util", + ], +) + py_library( name = "error_ops", srcs = ["error_ops.py"], @@ -165,7 +188,7 @@ py_library( "//tensorflow/python:tensor_shape", "//tensorflow/python/data/ops:dataset_ops", "//tensorflow/python/data/util:nest", - "//tensorflow/python/data/util:sparse", + "//tensorflow/python/data/util:structure", ], ) @@ -188,6 +211,28 @@ py_library( ], ) +py_library( + name = "map_defun", + srcs = ["map_defun.py"], + srcs_version = "PY2AND3", + deps = [ + "//tensorflow/python:dataset_ops_gen", + "//tensorflow/python:framework_ops", + "//tensorflow/python:tensor_shape", + ], +) + +py_library( + name = "matching_files", + srcs = ["matching_files.py"], + srcs_version = "PY2AND3", + deps = [ + "//tensorflow/python:dataset_ops_gen", + "//tensorflow/python:framework_ops", + "//tensorflow/python:tensor_shape", + ], +) + py_library( name = "optimization", srcs = ["optimization.py"], @@ -201,6 +246,16 @@ py_library( ], ) +py_library( + name = "optimization_options", + srcs = ["optimization_options.py"], + srcs_version = "PY2AND3", + deps = [ + "//tensorflow/python:util", + "//tensorflow/python/data/util:options", + ], +) + py_library( name = "parsing_ops", srcs = ["parsing_ops.py"], @@ -217,17 +272,6 @@ py_library( ], ) -py_library( - name = "map_defun", - srcs = ["map_defun.py"], - srcs_version = "PY2AND3", - deps = [ - "//tensorflow/python:dataset_ops_gen", - "//tensorflow/python:framework_ops", - "//tensorflow/python:tensor_shape", - ], -) - py_library( name = "resampling", srcs = ["resampling.py"], @@ -253,7 +297,7 @@ py_library( srcs = ["scan_ops.py"], srcs_version = "PY2AND3", deps = [ - "//tensorflow/python:dataset_ops_gen", + "//tensorflow/python:experimental_dataset_ops_gen", "//tensorflow/python:framework_ops", "//tensorflow/python:function", "//tensorflow/python/data/ops:dataset_ops", @@ -303,6 +347,18 @@ py_library( srcs_version = "PY2AND3", deps = [ ":stats_aggregator", + "//tensorflow/python:util", + "//tensorflow/python/data/util:options", + ], +) + +py_library( + name = "threading_options", + srcs = ["threading_options.py"], + srcs_version = "PY2AND3", + deps = [ + "//tensorflow/python:util", + "//tensorflow/python/data/util:options", ], ) @@ -313,9 +369,8 @@ py_library( deps = [ "//tensorflow/python:experimental_dataset_ops_gen", "//tensorflow/python:resource_variable_ops", + "//tensorflow/python:util", "//tensorflow/python/data/ops:dataset_ops", - "//tensorflow/python/data/util:nest", - "//tensorflow/python/data/util:sparse", "//tensorflow/python/eager:context", ], ) @@ -378,14 +433,17 @@ py_library( name = "dataset_ops", deps = [ ":batching", + ":cardinality", ":counter", ":enumerate_ops", ":error_ops", + ":filter_for_shard_ops", ":get_single_element", ":grouping", ":indexed_dataset_ops", ":interleave_ops", ":map_defun", + ":matching_files", ":optimization", ":prefetching_ops", ":readers", diff --git a/tensorflow/python/data/experimental/ops/batching.py b/tensorflow/python/data/experimental/ops/batching.py index d8985fd13b..668bf3e800 100644 --- a/tensorflow/python/data/experimental/ops/batching.py +++ b/tensorflow/python/data/experimental/ops/batching.py @@ -30,11 +30,12 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import sparse_tensor from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import tensor_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import check_ops from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import gen_array_ops -from tensorflow.python.ops import gen_dataset_ops +from tensorflow.python.ops import gen_experimental_dataset_ops as ged_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import sparse_ops from tensorflow.python.util.tf_export import tf_export @@ -365,7 +366,7 @@ class _UnbatchDataset(dataset_ops.UnaryDataset): self._input_dataset = input_dataset def _as_variant_tensor(self): - return gen_dataset_ops.unbatch_dataset( + return ged_ops.experimental_unbatch_dataset( self._input_dataset._as_variant_tensor(), # pylint: disable=protected-access **dataset_ops.flat_structure(self)) @@ -455,7 +456,7 @@ class _DenseToSparseBatchDataset(dataset_ops.UnaryDataset): self._row_shape = row_shape def _as_variant_tensor(self): - return gen_dataset_ops.dense_to_sparse_batch_dataset( + return ged_ops.experimental_dense_to_sparse_batch_dataset( self._input_dataset._as_variant_tensor(), # pylint: disable=protected-access self._batch_size, row_shape=convert.partial_shape_to_tensor(self._row_shape), @@ -569,13 +570,16 @@ class _RestructuredDataset(dataset_ops.UnaryDataset): return self._output_shapes -class _MapAndBatchDataset(dataset_ops.MapDataset): +class _MapAndBatchDataset(dataset_ops.UnaryDataset): """A `Dataset` that maps a function over a batch of elements.""" def __init__(self, input_dataset, map_func, batch_size, num_parallel_calls, drop_remainder): """See `Dataset.map()` for details.""" - super(_MapAndBatchDataset, self).__init__(input_dataset, map_func) + super(_MapAndBatchDataset, self).__init__(input_dataset) + self._input_dataset = input_dataset + self._map_func = dataset_ops.StructuredFunctionWrapper( + map_func, "tf.data.experimental.map_and_batch()", dataset=input_dataset) self._batch_size_t = ops.convert_to_tensor( batch_size, dtype=dtypes.int64, name="batch_size") self._num_parallel_calls_t = ops.convert_to_tensor( @@ -583,36 +587,40 @@ class _MapAndBatchDataset(dataset_ops.MapDataset): self._drop_remainder_t = ops.convert_to_tensor( drop_remainder, dtype=dtypes.bool, name="drop_remainder") - self._batch_size = batch_size - self._drop_remainder = drop_remainder + constant_drop_remainder = tensor_util.constant_value(self._drop_remainder_t) + if constant_drop_remainder: + # NOTE(mrry): `constant_drop_remainder` may be `None` (unknown statically) + # or `False` (explicitly retaining the remainder). + self._output_structure = self._map_func.output_structure._batch( # pylint: disable=protected-access + tensor_util.constant_value(self._batch_size_t)) + else: + self._output_structure = self._map_func.output_structure._batch(None) # pylint: disable=protected-access + + def _functions(self): + return [self._map_func] def _as_variant_tensor(self): # pylint: disable=protected-access - input_resource = self._input_dataset._as_variant_tensor() - return gen_dataset_ops.map_and_batch_dataset_v2( - input_resource, - self._map_func.captured_inputs, - f=self._map_func, + return ged_ops.experimental_map_and_batch_dataset( + self._input_dataset._as_variant_tensor(), + self._map_func.function.captured_inputs, + f=self._map_func.function, batch_size=self._batch_size_t, num_parallel_calls=self._num_parallel_calls_t, drop_remainder=self._drop_remainder_t, - **dataset_ops.flat_structure(self)) - # pylint: enable=protected-access + **dataset_ops.flat_structure(structure=self._output_structure)) + + @property + def output_classes(self): + return self._output_structure._to_legacy_output_classes() # pylint: disable=protected-access @property def output_shapes(self): - dim = self._batch_size if self._drop_remainder else None - return nest.pack_sequence_as(self._output_shapes, [ - tensor_shape.vector(dim).concatenate(s) - for s in nest.flatten(self._output_shapes) - ]) + return self._output_structure._to_legacy_output_shapes() # pylint: disable=protected-access @property def output_types(self): - return self._output_types - - def _transformation_name(self): - return "tf.data.experimental.map_and_batch()" + return self._output_structure._to_legacy_output_types() # pylint: disable=protected-access @tf_export("data.experimental.map_and_batch") diff --git a/tensorflow/python/data/experimental/ops/cardinality.py b/tensorflow/python/data/experimental/ops/cardinality.py new file mode 100644 index 0000000000..9cf0a8801e --- /dev/null +++ b/tensorflow/python/data/experimental/ops/cardinality.py @@ -0,0 +1,50 @@ +# Copyright 2018 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. +# ============================================================================== +"""Cardinality analysis of `Dataset` objects.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.ops import gen_experimental_dataset_ops as ged_ops +from tensorflow.python.util.tf_export import tf_export + + +INFINITE = -1 +UNKNOWN = -2 +tf_export("data.experimental.INFINITE_CARDINALITY").export_constant( + __name__, "INFINITE") +tf_export("data.experimental.UNKNOWN_CARDINALITY").export_constant( + __name__, "UNKNOWN") + + +@tf_export("data.experimental.cardinality") +def cardinality(dataset): + """Returns the cardinality of `dataset`, if known. + + The operation returns the cardinality of `dataset`. The operation may return + `tf.data.experimental.INFINITE_CARDINALITY` if `dataset` contains an infinite + number of elements or `tf.data.experimental.UNKNOWN_CARDINALITY` if the + analysis fails to determine the number of elements in `dataset` (e.g. when the + dataset source is a file). + + Args: + dataset: A `tf.data.Dataset` for which to determine cardinality. + + Returns: + A scalar `tf.int64` `Tensor` representing the cardinality of `dataset`. If + the cardinality is infinite or unknown, the operation returns the named + constant `INFINITE_CARDINALITY` and `UNKNOWN_CARDINALITY` respectively. + """ + return ged_ops.experimental_dataset_cardinality(dataset._as_variant_tensor()) # pylint: disable=protected-access diff --git a/tensorflow/python/data/experimental/ops/counter.py b/tensorflow/python/data/experimental/ops/counter.py index 42200eaef9..652eb9d002 100644 --- a/tensorflow/python/data/experimental/ops/counter.py +++ b/tensorflow/python/data/experimental/ops/counter.py @@ -25,8 +25,8 @@ from tensorflow.python.framework import ops from tensorflow.python.util.tf_export import tf_export -@tf_export("data.experimental.Counter") -def Counter(start=0, step=1, dtype=dtypes.int64): +@tf_export("data.experimental.Counter", v1=[]) +def CounterV2(start=0, step=1, dtype=dtypes.int64): """Creates a `Dataset` that counts from `start` in steps of size `step`. For example: @@ -53,3 +53,13 @@ def Counter(start=0, step=1, dtype=dtypes.int64): step = ops.convert_to_tensor(step, dtype=dtype, name="step") return dataset_ops.Dataset.from_tensors(0).repeat(None).apply( scan_ops.scan(start, lambda state, _: (state + step, state))) + + +@tf_export(v1=["data.experimental.Counter"]) +def CounterV1(start=0, step=1, dtype=dtypes.int64): + return dataset_ops.DatasetV1Adapter(CounterV2(start, step, dtype)) +CounterV1.__doc__ = CounterV2.__doc__ + +# TODO(b/119044825): Until all `tf.data` unit tests are converted to V2, keep +# this alias in place. +Counter = CounterV1 # pylint: disable=invalid-name diff --git a/tensorflow/python/data/experimental/ops/enumerate_ops.py b/tensorflow/python/data/experimental/ops/enumerate_ops.py index a1af98f552..f38cab12a7 100644 --- a/tensorflow/python/data/experimental/ops/enumerate_ops.py +++ b/tensorflow/python/data/experimental/ops/enumerate_ops.py @@ -26,9 +26,9 @@ from tensorflow.python.util.tf_export import tf_export @tf_export("data.experimental.enumerate_dataset") def enumerate_dataset(start=0): - """A transformation that enumerate the elements of a dataset. + """A transformation that enumerates the elements of a dataset. - It is Similar to python's `enumerate`. + It is similar to python's `enumerate`. For example: ```python diff --git a/tensorflow/python/data/experimental/ops/error_ops.py b/tensorflow/python/data/experimental/ops/error_ops.py index 82e274b70c..879b13ce09 100644 --- a/tensorflow/python/data/experimental/ops/error_ops.py +++ b/tensorflow/python/data/experimental/ops/error_ops.py @@ -52,7 +52,7 @@ def ignore_errors(): return _apply_fn -class _IgnoreErrorsDataset(dataset_ops.UnaryDataset): +class _IgnoreErrorsDataset(dataset_ops.UnaryUnchangedStructureDataset): """A `Dataset` that silently ignores errors when computing its input.""" def __init__(self, input_dataset): @@ -64,15 +64,3 @@ class _IgnoreErrorsDataset(dataset_ops.UnaryDataset): return gen_experimental_dataset_ops.experimental_ignore_errors_dataset( self._input_dataset._as_variant_tensor(), # pylint: disable=protected-access **dataset_ops.flat_structure(self)) - - @property - def output_classes(self): - return self._input_dataset.output_classes - - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_types(self): - return self._input_dataset.output_types diff --git a/tensorflow/python/data/experimental/ops/filter_for_shard_ops.py b/tensorflow/python/data/experimental/ops/filter_for_shard_ops.py new file mode 100644 index 0000000000..91d3dca3e9 --- /dev/null +++ b/tensorflow/python/data/experimental/ops/filter_for_shard_ops.py @@ -0,0 +1,106 @@ +# Copyright 2018 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. +# ============================================================================== +"""Naive shard dataset transformation.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops +from tensorflow.python.framework import tensor_util +from tensorflow.python.ops import math_ops +from tensorflow.python.util.tf_export import tf_export + + +@tf_export("data.experimental.filter_for_shard") +def filter_for_shard(num_shards, shard_index): + """Creates a `Dataset` that includes only 1/`num_shards` of this dataset. + + This dataset operator is very useful when running distributed training, as + it allows each worker to read a unique subset. + + When reading a single input file, you can skip elements as follows: + + ```python + d = tf.data.TFRecordDataset(FLAGS.input_file) + d = d.apply(tf.data.experimental.naive_shard(FLAGS.num_workers, + FLAGS.worker_index)) + d = d.repeat(FLAGS.num_epochs) + d = d.shuffle(FLAGS.shuffle_buffer_size) + d = d.map(parser_fn, num_parallel_calls=FLAGS.num_map_threads) + ``` + + Important caveats: + + - Be sure to shard before you use any randomizing operator (such as + shuffle). + - Generally it is best if the shard operator is used early in the dataset + pipeline. For example, when reading from a set of TFRecord files, shard + before converting the dataset to input samples. This avoids reading every + file on every worker. The following is an example of an efficient + sharding strategy within a complete pipeline: + + ```python + d = Dataset.list_files(FLAGS.pattern) + d = d.apply(tf.data.experimental.naive_shard(FLAGS.num_workers, + FLAGS.worker_index)) + d = d.repeat(FLAGS.num_epochs) + d = d.shuffle(FLAGS.shuffle_buffer_size) + d = d.interleave(tf.data.TFRecordDataset, + cycle_length=FLAGS.num_readers, block_length=1) + d = d.map(parser_fn, num_parallel_calls=FLAGS.num_map_threads) + ``` + + Args: + num_shards: A `tf.int64` scalar `tf.Tensor`, representing the number of + shards operating in parallel. + shard_index: A `tf.int64` scalar `tf.Tensor`, representing the worker index. + + Returns: + A `Dataset` transformation function, which can be passed to + `tf.data.Dataset.apply`. + + Raises: + ValueError: if `num_shards` or `shard_index` are illegal values. Note: error + checking is done on a best-effort basis, and errors aren't guaranteed to + be caught upon dataset creation. (e.g. providing in a placeholder tensor + bypasses the early checking, and will instead result in an error during + a session.run call.) + """ + num_shards = ops.convert_to_tensor( + num_shards, name="num_shards", dtype=dtypes.int64) + num_shards_static = tensor_util.constant_value(num_shards) + shard_index = ops.convert_to_tensor(shard_index, name="shard_index", + dtype=dtypes.int64) + shard_index_static = tensor_util.constant_value(shard_index) + + if num_shards_static is not None and num_shards_static < 1: + raise ValueError("num_shards must be >= 1; got: %s" % num_shards_static) + if shard_index_static is not None and shard_index_static < 0: + raise ValueError("shard_index must be >= 0; got: %s" % shard_index_static) + if (shard_index_static is not None and num_shards_static is not None and + shard_index_static >= num_shards_static): + raise ValueError("shard_index must be < num_shards; %s is not < %s" % + (shard_index_static, num_shards_static)) + + def filter_fn(elem_index, _): + mod_result = math_ops.mod(elem_index, num_shards) + return math_ops.equal(mod_result, shard_index) + + def _apply_fn(dataset): + # pylint: disable=protected-access + return dataset._enumerate().filter(filter_fn).map(lambda _, elem: elem) + + return _apply_fn diff --git a/tensorflow/python/data/experimental/ops/get_single_element.py b/tensorflow/python/data/experimental/ops/get_single_element.py index 132526166c..73116edf12 100644 --- a/tensorflow/python/data/experimental/ops/get_single_element.py +++ b/tensorflow/python/data/experimental/ops/get_single_element.py @@ -60,7 +60,7 @@ def get_single_element(dataset): InvalidArgumentError (at runtime): if `dataset` does not contain exactly one element. """ - if not isinstance(dataset, dataset_ops.Dataset): + if not isinstance(dataset, dataset_ops.DatasetV2): raise TypeError("`dataset` must be a `tf.data.Dataset` object.") nested_ret = nest.pack_sequence_as( diff --git a/tensorflow/python/data/experimental/ops/grouping.py b/tensorflow/python/data/experimental/ops/grouping.py index 026867d405..ad9fe9a4e8 100644 --- a/tensorflow/python/data/experimental/ops/grouping.py +++ b/tensorflow/python/data/experimental/ops/grouping.py @@ -21,13 +21,14 @@ import numpy as np from tensorflow.python.data.ops import dataset_ops from tensorflow.python.data.util import nest +from tensorflow.python.data.util import structure from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape from tensorflow.python.ops import array_ops from tensorflow.python.ops import check_ops -from tensorflow.python.ops import gen_dataset_ops +from tensorflow.python.ops import gen_experimental_dataset_ops as ged_ops from tensorflow.python.ops import math_ops from tensorflow.python.util.tf_export import tf_export @@ -236,29 +237,6 @@ def bucket_by_sequence_length(element_length_func, return _apply_fn -def _map_x_dataset(map_func): - """A transformation that maps `map_func` across its input. - - This transformation is similar to `tf.data.Dataset.map`, but in addition to - supporting dense and sparse tensor inputs, it also supports dataset inputs. - - Args: - map_func: A function mapping a nested structure of tensors and/or datasets - (having shapes and types defined by `self.output_shapes` and - `self.output_types`) to another nested structure of tensors and/or - datasets. - - Returns: - Dataset: A `Dataset`. - """ - - def _apply_fn(dataset): - """Function from `Dataset` to `Dataset` that applies the transformation.""" - return _MapXDataset(dataset, map_func) - - return _apply_fn - - class _GroupByReducerDataset(dataset_ops.UnaryDataset): """A `Dataset` that groups its input and performs a reduction.""" @@ -284,7 +262,7 @@ class _GroupByReducerDataset(dataset_ops.UnaryDataset): "`key_func` must return a single tf.int64 tensor. " "Got type=%s and shape=%s" % (wrapped_func.output_types, wrapped_func.output_shapes)) - self._key_func = wrapped_func.function + self._key_func = wrapped_func def _make_init_func(self, init_func): """Make wrapping defun for init_func.""" @@ -294,7 +272,7 @@ class _GroupByReducerDataset(dataset_ops.UnaryDataset): input_classes=ops.Tensor, input_shapes=tensor_shape.scalar(), input_types=dtypes.int64) - self._init_func = wrapped_func.function + self._init_func = wrapped_func self._state_classes = wrapped_func.output_classes self._state_shapes = wrapped_func.output_shapes self._state_types = wrapped_func.output_types @@ -356,8 +334,8 @@ class _GroupByReducerDataset(dataset_ops.UnaryDataset): self._state_shapes = nest.pack_sequence_as(self._state_shapes, weakened_state_shapes) - self._reduce_func = wrapped_func.function - self._reduce_func.add_to_graph(ops.get_default_graph()) + self._reduce_func = wrapped_func + self._reduce_func.function.add_to_graph(ops.get_default_graph()) def _make_finalize_func(self, finalize_func): """Make wrapping defun for finalize_func.""" @@ -367,7 +345,7 @@ class _GroupByReducerDataset(dataset_ops.UnaryDataset): input_classes=self._state_classes, input_shapes=self._state_shapes, input_types=self._state_types) - self._finalize_func = wrapped_func.function + self._finalize_func = wrapped_func self._output_classes = wrapped_func.output_classes self._output_shapes = wrapped_func.output_shapes self._output_types = wrapped_func.output_types @@ -384,17 +362,22 @@ class _GroupByReducerDataset(dataset_ops.UnaryDataset): def output_types(self): return self._output_types + def _functions(self): + return [ + self._key_func, self._init_func, self._reduce_func, self._finalize_func + ] + def _as_variant_tensor(self): - return gen_dataset_ops.group_by_reducer_dataset( + return ged_ops.experimental_group_by_reducer_dataset( self._input_dataset._as_variant_tensor(), # pylint: disable=protected-access - self._key_func.captured_inputs, - self._init_func.captured_inputs, - self._reduce_func.captured_inputs, - self._finalize_func.captured_inputs, - key_func=self._key_func, - init_func=self._init_func, - reduce_func=self._reduce_func, - finalize_func=self._finalize_func, + self._key_func.function.captured_inputs, + self._init_func.function.captured_inputs, + self._reduce_func.function.captured_inputs, + self._finalize_func.function.captured_inputs, + key_func=self._key_func.function, + init_func=self._init_func.function, + reduce_func=self._reduce_func.function, + finalize_func=self._finalize_func.function, **dataset_ops.flat_structure(self)) def _transformation_name(self): @@ -430,7 +413,7 @@ class _GroupByWindowDataset(dataset_ops.UnaryDataset): wrapped_func.output_shapes.is_compatible_with(tensor_shape.scalar())): raise ValueError( "`window_size_func` must return a single tf.int64 scalar tensor.") - self._window_size_func = wrapped_func.function + self._window_size_func = wrapped_func def _make_key_func(self, key_func, input_dataset): """Make wrapping defun for key_func.""" @@ -444,25 +427,29 @@ class _GroupByWindowDataset(dataset_ops.UnaryDataset): wrapped_func.output_shapes.is_compatible_with(tensor_shape.scalar())): raise ValueError( "`key_func` must return a single tf.int64 scalar tensor.") - self._key_func = wrapped_func.function + self._key_func = wrapped_func def _make_reduce_func(self, reduce_func, input_dataset): """Make wrapping defun for reduce_func.""" - nested_dataset = dataset_ops._NestedDatasetComponent(input_dataset) # pylint: disable=protected-access + nested_dataset = dataset_ops.DatasetStructure( + structure.Structure._from_legacy_structure( # pylint: disable=protected-access + input_dataset.output_types, input_dataset.output_shapes, + input_dataset.output_classes)) wrapped_func = dataset_ops.StructuredFunctionWrapper( reduce_func, self._transformation_name(), input_classes=(ops.Tensor, nested_dataset), input_shapes=(tensor_shape.scalar(), nested_dataset), - input_types=(dtypes.int64, nested_dataset), - experimental_nested_dataset_support=True) + input_types=(dtypes.int64, nested_dataset)) if not isinstance( - wrapped_func.output_classes, dataset_ops._NestedDatasetComponent): # pylint: disable=protected-access + wrapped_func.output_structure, dataset_ops.DatasetStructure): raise TypeError("`reduce_func` must return a `Dataset` object.") - self._output_classes = wrapped_func.output_classes.output_classes - self._output_types = wrapped_func.output_types.output_types - self._output_shapes = wrapped_func.output_shapes.output_shapes - self._reduce_func = wrapped_func.function + # pylint: disable=protected-access + element_structure = wrapped_func.output_structure._element_structure + self._output_classes = element_structure._to_legacy_output_classes() + self._output_types = element_structure._to_legacy_output_types() + self._output_shapes = element_structure._to_legacy_output_shapes() + self._reduce_func = wrapped_func @property def output_classes(self): @@ -476,15 +463,18 @@ class _GroupByWindowDataset(dataset_ops.UnaryDataset): def output_types(self): return self._output_types + def _functions(self): + return [self._key_func, self._reduce_func, self._window_size_func] + def _as_variant_tensor(self): - return gen_dataset_ops.group_by_window_dataset( + return ged_ops.experimental_group_by_window_dataset( self._input_dataset._as_variant_tensor(), # pylint: disable=protected-access - self._key_func.captured_inputs, - self._reduce_func.captured_inputs, - self._window_size_func.captured_inputs, - key_func=self._key_func, - reduce_func=self._reduce_func, - window_size_func=self._window_size_func, + self._key_func.function.captured_inputs, + self._reduce_func.function.captured_inputs, + self._window_size_func.function.captured_inputs, + key_func=self._key_func.function, + reduce_func=self._reduce_func.function, + window_size_func=self._window_size_func.function, **dataset_ops.flat_structure(self)) def _transformation_name(self): @@ -517,45 +507,3 @@ class Reducer(object): @property def finalize_func(self): return self._finalize_func - - -class _MapXDataset(dataset_ops.UnaryDataset): - """A `Dataset` that maps a function over elements in its input.""" - - def __init__(self, input_dataset, map_func): - """See `map_x_dataset()` for details.""" - super(_MapXDataset, self).__init__(input_dataset) - self._input_dataset = input_dataset - - wrapped_func = dataset_ops.StructuredFunctionWrapper( - map_func, - self._transformation_name(), - dataset=input_dataset, - experimental_nested_dataset_support=True) - self._output_classes = wrapped_func.output_classes - self._output_shapes = wrapped_func.output_shapes - self._output_types = wrapped_func.output_types - self._map_func = wrapped_func.function - - def _as_variant_tensor(self): - input_t = self._input_dataset._as_variant_tensor() # pylint: disable=protected-access - return gen_dataset_ops.map_dataset( - input_t, - self._map_func.captured_inputs, - f=self._map_func, - **dataset_ops.flat_structure(self)) - - @property - def output_classes(self): - return self._output_classes - - @property - def output_shapes(self): - return self._output_shapes - - @property - def output_types(self): - return self._output_types - - def _transformation_name(self): - return "tf.data.experimental.map_x_dataset()" diff --git a/tensorflow/python/data/experimental/ops/indexed_dataset_ops.py b/tensorflow/python/data/experimental/ops/indexed_dataset_ops.py index 9c06474a2f..570f0116f7 100644 --- a/tensorflow/python/data/experimental/ops/indexed_dataset_ops.py +++ b/tensorflow/python/data/experimental/ops/indexed_dataset_ops.py @@ -65,6 +65,7 @@ class MaterializedIndexedDataset(object): sparse.as_dense_types(self._output_shapes, self._output_classes))) +# TODO(saeta): Add a `DatasetV1` wrapper if this is exposed via the public API. class IndexedDataset(dataset_ops.Dataset): """IndexedDataset is highly experimental! """ @@ -149,6 +150,7 @@ class IndexedDataset(dataset_ops.Dataset): raise NotImplementedError("IndexedDataset._as_variant_tensor") +# TODO(saeta): Add a `DatasetV1` wrapper if this is exposed via the public API. class IdentityIndexedDataset(IndexedDataset): """IdentityIndexedDataset is a trivial indexed dataset used for testing. """ diff --git a/tensorflow/python/data/experimental/ops/interleave_ops.py b/tensorflow/python/data/experimental/ops/interleave_ops.py index a3c094859e..8b0fdfce11 100644 --- a/tensorflow/python/data/experimental/ops/interleave_ops.py +++ b/tensorflow/python/data/experimental/ops/interleave_ops.py @@ -133,8 +133,8 @@ class _DirectedInterleaveDataset(dataset_ops.Dataset): return self._data_inputs[0].output_types -@tf_export("data.experimental.sample_from_datasets") -def sample_from_datasets(datasets, weights=None, seed=None): +@tf_export("data.experimental.sample_from_datasets", v1=[]) +def sample_from_datasets_v2(datasets, weights=None, seed=None): """Samples elements at random from the datasets in `datasets`. Args: @@ -158,7 +158,7 @@ def sample_from_datasets(datasets, weights=None, seed=None): length of the `datasets` element. """ num_datasets = len(datasets) - if not isinstance(weights, dataset_ops.Dataset): + if not isinstance(weights, dataset_ops.DatasetV2): if weights is None: # Select inputs with uniform probability. logits = [[1.0] * num_datasets] @@ -217,8 +217,15 @@ def sample_from_datasets(datasets, weights=None, seed=None): return _DirectedInterleaveDataset(selector_input, datasets) -@tf_export("data.experimental.choose_from_datasets") -def choose_from_datasets(datasets, choice_dataset): +@tf_export(v1=["data.experimental.sample_from_datasets"]) +def sample_from_datasets_v1(datasets, weights=None, seed=None): + return dataset_ops.DatasetV1Adapter( + sample_from_datasets_v2(datasets, weights, seed)) +sample_from_datasets_v1.__doc__ = sample_from_datasets_v2.__doc__ + + +@tf_export("data.experimental.choose_from_datasets", v1=[]) +def choose_from_datasets_v2(datasets, choice_dataset): """Creates a dataset that deterministically chooses elements from `datasets`. For example, given the following datasets: @@ -260,3 +267,16 @@ def choose_from_datasets(datasets, choice_dataset): raise TypeError("`choice_dataset` must be a dataset of scalar " "`tf.int64` tensors.") return _DirectedInterleaveDataset(choice_dataset, datasets) + + +@tf_export(v1=["data.experimental.choose_from_datasets"]) +def choose_from_datasets_v1(datasets, choice_dataset): + return dataset_ops.DatasetV1Adapter( + choose_from_datasets_v2(datasets, choice_dataset)) +choose_from_datasets_v1.__doc__ = choose_from_datasets_v2.__doc__ + + +# TODO(b/119044825): Until all `tf.data` unit tests are converted to V2, keep +# these aliases in place. +choose_from_datasets = choose_from_datasets_v1 +sample_from_datasets = sample_from_datasets_v1 diff --git a/tensorflow/python/data/experimental/ops/matching_files.py b/tensorflow/python/data/experimental/ops/matching_files.py new file mode 100644 index 0000000000..8398f86e31 --- /dev/null +++ b/tensorflow/python/data/experimental/ops/matching_files.py @@ -0,0 +1,51 @@ +# Copyright 2018 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. +# ============================================================================== +"""Experimental API for matching input filenames.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops +from tensorflow.python.framework import tensor_shape +from tensorflow.python.ops import gen_experimental_dataset_ops as ged_ops + + +class MatchingFilesDataset(dataset_ops.DatasetSource): + """A `Dataset` that list the files according to the input patterns.""" + + def __init__(self, patterns): + super(MatchingFilesDataset, self).__init__() + self._patterns = ops.convert_to_tensor( + patterns, dtype=dtypes.string, name="patterns") + + def _as_variant_tensor(self): + return ged_ops.experimental_matching_files_dataset(self._patterns) + + @property + def output_classes(self): + return ops.Tensor + + @property + def output_shapes(self): + return tensor_shape.scalar() + + @property + def output_types(self): + return dtypes.string + + diff --git a/tensorflow/python/data/experimental/ops/optimization.py b/tensorflow/python/data/experimental/ops/optimization.py index b744db7f1e..c6c7de9265 100644 --- a/tensorflow/python/data/experimental/ops/optimization.py +++ b/tensorflow/python/data/experimental/ops/optimization.py @@ -100,7 +100,7 @@ def optimize(optimizations=None): return _apply_fn -class _AssertNextDataset(dataset_ops.UnaryDataset): +class _AssertNextDataset(dataset_ops.UnaryUnchangedStructureDataset): """A `Dataset` that asserts which transformations happen next.""" def __init__(self, input_dataset, transformations): @@ -118,20 +118,8 @@ class _AssertNextDataset(dataset_ops.UnaryDataset): self._transformations, **dataset_ops.flat_structure(self)) - @property - def output_classes(self): - return self._input_dataset.output_classes - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_types(self): - return self._input_dataset.output_types - - -class _NonSerializableDataset(dataset_ops.UnaryDataset): +class _NonSerializableDataset(dataset_ops.UnaryUnchangedStructureDataset): """A `Dataset` that performs non-serializable identity transformation.""" def __init__(self, input_dataset): @@ -143,15 +131,3 @@ class _NonSerializableDataset(dataset_ops.UnaryDataset): return gen_experimental_dataset_ops.experimental_non_serializable_dataset( self._input_dataset._as_variant_tensor(), # pylint: disable=protected-access **dataset_ops.flat_structure(self)) - - @property - def output_classes(self): - return self._input_dataset.output_classes - - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_types(self): - return self._input_dataset.output_types diff --git a/tensorflow/python/data/experimental/ops/optimization_options.py b/tensorflow/python/data/experimental/ops/optimization_options.py new file mode 100644 index 0000000000..dc9d319374 --- /dev/null +++ b/tensorflow/python/data/experimental/ops/optimization_options.py @@ -0,0 +1,83 @@ +# Copyright 2018 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. +# ============================================================================== +"""Experimental API for controlling optimizations in `tf.data` pipelines.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + + +from tensorflow.python.data.util import options +from tensorflow.python.util.tf_export import tf_export + + +@tf_export("data.experimental.OptimizationOptions") +class OptimizationOptions(options.OptionsBase): + """Represents options for dataset optimizations. + + You can apply `OptimizationOptions` to a `dataset` object, as follows: + + ```python + options = tf.data.Options() + options.optimization = tf.data.experimental.OptimizationOptions() + options.optimization.map_and_batch_fusion = True + dataset = dataset.with_options(options) + ``` + """ + + filter_fusion = options.create_option( + name="filter_fusion", + ty=bool, + docstring="Whether to fuse filter transformations.") + + hoist_random_uniform = options.create_option( + name="hoist_random_uniform", + ty=bool, + docstring= + "Whether to hoist `tf.random_uniform()` ops out of map transformations.") + + map_and_batch_fusion = options.create_option( + name="map_and_batch_fusion", + ty=bool, + docstring="Whether to fuse map and batch transformations.") + + map_and_filter_fusion = options.create_option( + name="map_and_filter_fusion", + ty=bool, + docstring="Whether to fuse map and filter transformations.") + + map_fusion = options.create_option( + name="map_and_filter_fusion", + ty=bool, + docstring="Whether to fuse map transformations.") + + map_parallelization = options.create_option( + name="map_parallelization", + ty=bool, + docstring="Whether to parallelize stateless map transformations.") + + map_vectorization = options.create_option( + name="map_vectorization", + ty=bool, + docstring="Whether to vectorize map transformations.") + + noop_elimination = options.create_option( + name="noop_elimination", + ty=bool, + docstring="Whether to eliminate no-op transformations.") + + shuffle_and_repeat_fusion = options.create_option( + name="shuffle_and_repeat_fusion", + ty=bool, + docstring="Whether to fuse shuffle and repeat transformations.") diff --git a/tensorflow/python/data/experimental/ops/parsing_ops.py b/tensorflow/python/data/experimental/ops/parsing_ops.py index 6615b9022a..44cb0b8a2c 100644 --- a/tensorflow/python/data/experimental/ops/parsing_ops.py +++ b/tensorflow/python/data/experimental/ops/parsing_ops.py @@ -22,7 +22,7 @@ from tensorflow.python.data.util import nest from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import sparse_tensor -from tensorflow.python.ops import gen_dataset_ops +from tensorflow.python.ops import gen_experimental_dataset_ops from tensorflow.python.ops import parsing_ops from tensorflow.python.util.tf_export import tf_export @@ -80,7 +80,7 @@ class _ParseExampleDataset(dataset_ops.UnaryDataset): ])) def _as_variant_tensor(self): - return gen_dataset_ops.parse_example_dataset( + return gen_experimental_dataset_ops.experimental_parse_example_dataset( self._input_dataset._as_variant_tensor(), # pylint: disable=protected-access self._num_parallel_calls, self._dense_defaults, @@ -138,10 +138,10 @@ def parse_example_dataset(features, num_parallel_calls=1): def _apply_fn(dataset): """Function from `Dataset` to `Dataset` that applies the transformation.""" out_dataset = _ParseExampleDataset(dataset, features, num_parallel_calls) - if any([ + if any( isinstance(feature, parsing_ops.SparseFeature) for _, feature in features.items() - ]): + ): # pylint: disable=protected-access # pylint: disable=g-long-lambda out_dataset = out_dataset.map( diff --git a/tensorflow/python/data/experimental/ops/prefetching_ops.py b/tensorflow/python/data/experimental/ops/prefetching_ops.py index d34f9f25bd..0894dd5f70 100644 --- a/tensorflow/python/data/experimental/ops/prefetching_ops.py +++ b/tensorflow/python/data/experimental/ops/prefetching_ops.py @@ -17,13 +17,10 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -import warnings - from tensorflow.python.data.ops import dataset_ops from tensorflow.python.data.ops import iterator_ops from tensorflow.python.data.util import nest from tensorflow.python.data.util import sparse -from tensorflow.python.eager import context from tensorflow.python.eager import function from tensorflow.python.framework import device as framework_device from tensorflow.python.framework import dtypes @@ -37,304 +34,6 @@ from tensorflow.python.ops import resource_variable_ops from tensorflow.python.util.tf_export import tf_export -def function_buffering_resource(string_arg, - target_device, - f, - buffer_size, - output_types, - container="", - shared_name=None, - name=None): - """Creates a FunctionBufferingResource. - - A FunctionBufferingResource fills up a buffer by calling a function `f` on - `target_device`. `f` should take in only a single string argument as input. - - Args: - string_arg: The single string argument to the function. - target_device: The device to run `f` on. - f: The function to be executed. - buffer_size: Size of the buffer to be populated. - output_types: The output types generated by the function. - container: (Optional) string. Defaults to "". - shared_name: (Optional) string. - name: (Optional) string to name the op. - - Returns: - Handle to a FunctionBufferingResource. - """ - if shared_name is None: - shared_name = "" - return ged_ops.experimental_function_buffering_resource( - string_arg=string_arg, - target_device=target_device, - shared_name=shared_name, - f=f, - buffer_size=buffer_size, - container=container, - name=name, - output_types=output_types) - - -def function_buffering_resource_get_next(function_buffer_resource, - output_types, - name=None): - return ged_ops.experimental_function_buffering_resource_get_next( - function_buffer_resource=function_buffer_resource, - output_types=output_types, - name=name) - - -def function_buffering_resource_reset(function_buffer_resource, name=None): - return ged_ops.experimental_function_buffering_resource_reset( - function_buffer_resource=function_buffer_resource, name=name) - - -# pylint: disable=protected-access -class _PrefetchToDeviceIterator(object): - """A replacement for `tf.data.Iterator` that prefetches to another device. - - Args: - input_dataset: The input dataset - one_shot: If true, we make a one shot iterator that's already initialized. - device: A fully specified device string where we want to prefetch to - buffer_size: Size of the prefetching buffer. - shared_name: (Optional.) If non-empty, the returned iterator will be - shared under the given name across multiple sessions that share the - same devices (e.g. when using a remote server). - - Returns: - An Iterator type object. - """ - - def __init__(self, - input_dataset, - one_shot, - device, - buffer_size, - shared_name=None): - self._input_dataset = input_dataset - self._get_next_call_count = 0 - self._one_shot = one_shot - if shared_name is None: - shared_name = "" - - if self._one_shot: - self._input_iterator = input_dataset.make_one_shot_iterator() - else: - self._input_iterator = iterator_ops.Iterator.from_structure( - self._input_dataset.output_types, self._input_dataset.output_shapes, - shared_name, self._input_dataset.output_classes) - input_iterator_handle = self._input_iterator.string_handle() - - @function.defun(input_signature=[tensor_spec.TensorSpec([], dtypes.string)]) - # handle is a scalar `tf.Tensor` of type `tf.string` - def _prefetch_fn(handle): - """Prefetches one element from `input_iterator`.""" - remote_iterator = iterator_ops.Iterator.from_string_handle( - handle, self._input_iterator.output_types, - self._input_iterator.output_shapes, - self._input_iterator.output_classes) - ret = remote_iterator.get_next() - return nest.flatten(sparse.serialize_sparse_tensors(ret)) - - self._prefetch_fn = _prefetch_fn._get_concrete_function_internal() # pylint: disable=protected-access - - iterator_device = ged_ops.experimental_iterator_get_device( - self._input_iterator._iterator_resource) - - with ops.device(device): - self._buffering_resource = function_buffering_resource( - f=self._prefetch_fn, - target_device=iterator_device, - string_arg=input_iterator_handle, - buffer_size=buffer_size, - shared_name=shared_name, - output_types=nest.flatten( - sparse.as_dense_types(self._input_dataset.output_types, - self._input_dataset.output_classes))) - - if not self._one_shot: - reset_op = function_buffering_resource_reset(self._buffering_resource) - with ops.control_dependencies([reset_op]): - self._initializer = self._input_iterator.make_initializer( - self._input_dataset) - - def get_next(self, name=None): - """See `tf.data.Iterator.get_next`.""" - self._get_next_call_count += 1 - if self._get_next_call_count > iterator_ops.GET_NEXT_CALL_WARNING_THRESHOLD: - warnings.warn(iterator_ops.GET_NEXT_CALL_WARNING_MESSAGE) - - flat_ret = ged_ops.experimental_function_buffering_resource_get_next( - self._buffering_resource, - output_types=nest.flatten( - sparse.as_dense_types(self.output_types, self.output_classes)), - name=name) - - ret = sparse.deserialize_sparse_tensors( - nest.pack_sequence_as(self.output_types, flat_ret), - self.output_types, self.output_shapes, self.output_classes) - - for tensor, shape in zip( - nest.flatten(ret), nest.flatten(self.output_shapes)): - if isinstance(tensor, ops.Tensor): - tensor.set_shape(shape) - - return ret - - @property - def initializer(self): - if self._one_shot: - raise NotImplementedError("Can't initialize a one_shot_iterator") - return self._initializer - - @property - def output_classes(self): - return self._input_dataset.output_classes - - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_types(self): - return self._input_dataset.output_types - - -class _PrefetchToDeviceEagerIterator(iterator_ops.EagerIterator): - """A replacement for `tf.data.Iterator` that prefetches to another device. - - Args: - input_dataset: The input dataset - one_shot: If true, we make a one shot iterator that's already initialized. - device: A fully specified device string where we want to prefetch to - buffer_size: Size of the prefetching buffer. - shared_name: (Optional.) If non-empty, the returned iterator will be - shared under the given name across multiple sessions that share the - same devices (e.g. when using a remote server). - - Returns: - An Iterator type object. - """ - - def __init__(self, - input_dataset, - device, - buffer_size): - with ops.device("/device:CPU:0"): - super(_PrefetchToDeviceEagerIterator, self).__init__(input_dataset) - input_iterator_handle = gen_dataset_ops.iterator_to_string_handle( - self._resource) - - self._device = device - - @function.defun(input_signature=[tensor_spec.TensorSpec([], dtypes.string)]) - def _prefetch_fn(handle): - """Prefetches one element from `input_iterator`.""" - remote_iterator = iterator_ops.Iterator.from_string_handle( - handle, self.output_types, self.output_shapes, self.output_classes) - ret = remote_iterator.get_next() - return nest.flatten(sparse.serialize_sparse_tensors(ret)) - - self._prefetch_fn = _prefetch_fn._get_concrete_function_internal() # pylint: disable=protected-access - - with ops.device(device): - self._buffering_resource = function_buffering_resource( - f=self._prefetch_fn, - output_types=self._flat_output_types, - target_device=ged_ops.experimental_iterator_get_device( - self._resource), - string_arg=input_iterator_handle, - buffer_size=buffer_size, - shared_name=iterator_ops._generate_shared_name( - "function_buffer_resource")) - - def _next_internal(self): - """Returns a nested structure of `tf.Tensor`s containing the next element. - """ - # This runs in sync mode as iterators use an error status to communicate - # that there is no more data to iterate over. - # TODO(b/77291417): Fix - with context.execution_mode(context.SYNC): - with ops.device(self._device): - flat_ret = ged_ops.experimental_function_buffering_resource_get_next( - function_buffer_resource=self._buffering_resource, - output_types=self._flat_output_types) - return self._element_structure._from_tensor_list(flat_ret) -# pylint: enable=protected-access - - -class _PrefetchToDeviceDataset(dataset_ops.UnaryDataset): - """A `Dataset` whose iterator prefetches elements to another device.""" - - def __init__(self, input_dataset, device, buffer_size): - super(_PrefetchToDeviceDataset, self).__init__(input_dataset) - self._input_dataset = input_dataset - self._device = device - self._buffer_size = buffer_size if buffer_size is not None else 1 - - # The static analysis cannot tell that the eager iterator's superclass has - # a `next()` method. - # pylint: disable=non-iterator-returned - def __iter__(self): - """Creates an `Iterator` for enumerating the elements of this dataset. - - The returned iterator implements the Python iterator protocol and therefore - can only be used in eager mode. - - Returns: - An `Iterator` over the elements of this dataset. - - Raises: - RuntimeError: If eager execution is enabled. - """ - if context.executing_eagerly(): - return _PrefetchToDeviceEagerIterator(self._input_dataset, self._device, - self._buffer_size) - else: - raise RuntimeError("dataset.__iter__() is only supported when eager " - "execution is enabled.") - # pylint: enable=non-iterator-returned - - def make_one_shot_iterator(self): - if context.executing_eagerly(): - return _PrefetchToDeviceEagerIterator(self._input_dataset, self._device, - self._buffer_size) - else: - return _PrefetchToDeviceIterator(self._input_dataset, one_shot=True, - device=self._device, - buffer_size=self._buffer_size) - - def make_initializable_iterator(self, shared_name=None): - return _PrefetchToDeviceIterator( - self._input_dataset, - one_shot=False, - device=self._device, - buffer_size=self._buffer_size, - shared_name=shared_name) - - def _as_variant_tensor(self): - # TODO(mrry): Raise this error earlier (e.g. when one of the Dataset - # transformation methods is called. - # TODO(mrry): Investigate support for chaining further transformations after - # the prefetch, including GPU support. - raise NotImplementedError("`prefetch_to_device()` must be the last " - "transformation in a dataset pipeline.") - - @property - def output_types(self): - return self._input_dataset.output_types - - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_classes(self): - return self._input_dataset.output_classes - - @tf_export("data.experimental.prefetch_to_device") def prefetch_to_device(device, buffer_size=None): """A transformation that prefetches dataset values to the given `device`. @@ -352,7 +51,8 @@ def prefetch_to_device(device, buffer_size=None): `tf.data.Dataset.apply`. """ def _apply_fn(dataset): - return _PrefetchToDeviceDataset(dataset, device, buffer_size) + return dataset.apply( + copy_to_device(target_device=device)).prefetch(buffer_size) return _apply_fn @@ -371,8 +71,11 @@ def copy_to_device(target_device, source_device="/cpu:0"): """ def _apply_fn(dataset): + options = dataset_ops.Options() + options.experimental_autotune = False return _CopyToDeviceDataset( - dataset, target_device=target_device, source_device=source_device) + dataset, target_device=target_device, + source_device=source_device).with_options(options) return _apply_fn @@ -380,7 +83,7 @@ def copy_to_device(target_device, source_device="/cpu:0"): # TODO(rohanj): Use the _input_hostmem attr on the RemoteCall ops to indicate # all inputs to the Op are in host memory, thereby avoiding some unnecessary # Sends and Recvs. -class _CopyToDeviceDataset(dataset_ops.UnaryDataset): +class _CopyToDeviceDataset(dataset_ops.UnaryUnchangedStructureDataset): """A `Dataset` that copies elements to another device.""" def __init__(self, input_dataset, target_device, source_device="/cpu:0"): @@ -529,18 +232,6 @@ class _CopyToDeviceDataset(dataset_ops.UnaryDataset): output_types=self._flat_output_types, output_shapes=self._flat_output_shapes) - @property - def output_types(self): - return self._input_dataset.output_types - - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_classes(self): - return self._input_dataset.output_classes - class _MapOnGpuDataset(dataset_ops.UnaryDataset): """A `Dataset` that maps a function over elements in its using a GPU.""" @@ -551,36 +242,35 @@ class _MapOnGpuDataset(dataset_ops.UnaryDataset): self._input_dataset = input_dataset self._use_inter_op_parallelism = use_inter_op_parallelism - wrapped_func = dataset_ops.StructuredFunctionWrapper( + self._map_func = dataset_ops.StructuredFunctionWrapper( map_func, self._transformation_name(), dataset=input_dataset, defun_kwargs={"experimental_ints_on_device": True}) - self._output_classes = wrapped_func.output_classes - self._output_shapes = wrapped_func.output_shapes - self._output_types = wrapped_func.output_types - self._map_func = wrapped_func.function + + def _functions(self): + return [self._map_func] def _as_variant_tensor(self): input_t = self._input_dataset._as_variant_tensor() # pylint: disable=protected-access return ged_ops.experimental_map_dataset( input_t, - self._map_func.captured_inputs, - f=self._map_func, + self._map_func.function.captured_inputs, + f=self._map_func.function, use_inter_op_parallelism=self._use_inter_op_parallelism, **dataset_ops.flat_structure(self)) @property def output_classes(self): - return self._output_classes + return self._map_func.output_classes @property def output_shapes(self): - return self._output_shapes + return self._map_func.output_shapes @property def output_types(self): - return self._output_types + return self._map_func.output_types def _transformation_name(self): return "map_on_gpu()" diff --git a/tensorflow/python/data/experimental/ops/random_ops.py b/tensorflow/python/data/experimental/ops/random_ops.py index e3a2aeab31..a4359f356b 100644 --- a/tensorflow/python/data/experimental/ops/random_ops.py +++ b/tensorflow/python/data/experimental/ops/random_ops.py @@ -17,26 +17,28 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import functools + from tensorflow.python.data.ops import dataset_ops from tensorflow.python.data.util import random_seed from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape -from tensorflow.python.ops import gen_dataset_ops +from tensorflow.python.ops import gen_experimental_dataset_ops from tensorflow.python.util.tf_export import tf_export -@tf_export("data.experimental.RandomDataset") -class RandomDataset(dataset_ops.DatasetSource): +@tf_export("data.experimental.RandomDataset", v1=[]) +class RandomDatasetV2(dataset_ops.DatasetSource): """A `Dataset` of pseudorandom values.""" def __init__(self, seed=None): """A `Dataset` of pseudorandom values.""" - super(RandomDataset, self).__init__() + super(RandomDatasetV2, self).__init__() self._seed, self._seed2 = random_seed.get_seed(seed) def _as_variant_tensor(self): - return gen_dataset_ops.random_dataset( + return gen_experimental_dataset_ops.experimental_random_dataset( seed=self._seed, seed2=self._seed2, **dataset_ops.flat_structure(self)) @@ -52,3 +54,18 @@ class RandomDataset(dataset_ops.DatasetSource): @property def output_types(self): return dtypes.int64 + + +@tf_export(v1=["data.experimental.RandomDataset"]) +class RandomDatasetV1(dataset_ops.DatasetV1Adapter): + """A `Dataset` of pseudorandom values.""" + + @functools.wraps(RandomDatasetV2.__init__) + def __init__(self, seed=None): + wrapped = RandomDatasetV2(seed) + super(RandomDatasetV1, self).__init__(wrapped) + + +# TODO(b/119044825): Until all `tf.data` unit tests are converted to V2, keep +# this alias in place. +RandomDataset = RandomDatasetV1 diff --git a/tensorflow/python/data/experimental/ops/readers.py b/tensorflow/python/data/experimental/ops/readers.py index fe60192586..a4c260dde7 100644 --- a/tensorflow/python/data/experimental/ops/readers.py +++ b/tensorflow/python/data/experimental/ops/readers.py @@ -19,6 +19,7 @@ from __future__ import print_function import collections import csv +import functools import numpy as np @@ -36,7 +37,6 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape from tensorflow.python.lib.io import file_io -from tensorflow.python.ops import gen_dataset_ops from tensorflow.python.ops import gen_experimental_dataset_ops from tensorflow.python.ops import io_ops from tensorflow.python.platform import gfile @@ -307,8 +307,8 @@ def make_tf_record_dataset(file_pattern, return dataset.prefetch(buffer_size=prefetch_buffer_size) -@tf_export("data.experimental.make_csv_dataset") -def make_csv_dataset( +@tf_export("data.experimental.make_csv_dataset", v1=[]) +def make_csv_dataset_v2( file_pattern, batch_size, column_names=None, @@ -507,11 +507,42 @@ def make_csv_dataset( return dataset +@tf_export(v1=["data.experimental.make_csv_dataset"]) +def make_csv_dataset_v1( + file_pattern, + batch_size, + column_names=None, + column_defaults=None, + label_name=None, + select_columns=None, + field_delim=",", + use_quote_delim=True, + na_value="", + header=True, + num_epochs=None, + shuffle=True, + shuffle_buffer_size=10000, + shuffle_seed=None, + prefetch_buffer_size=optimization.AUTOTUNE, + num_parallel_reads=1, + sloppy=False, + num_rows_for_inference=100, + compression_type=None, +): # pylint: disable=missing-docstring + return dataset_ops.DatasetV1Adapter(make_csv_dataset_v2( + file_pattern, batch_size, column_names, column_defaults, label_name, + select_columns, field_delim, use_quote_delim, na_value, header, + num_epochs, shuffle, shuffle_buffer_size, shuffle_seed, + prefetch_buffer_size, num_parallel_reads, sloppy, num_rows_for_inference, + compression_type)) +make_csv_dataset_v1.__doc__ = make_csv_dataset_v2.__doc__ + + _DEFAULT_READER_BUFFER_SIZE_BYTES = 4 * 1024 * 1024 # 4 MB -@tf_export("data.experimental.CsvDataset") -class CsvDataset(dataset_ops.DatasetSource): +@tf_export("data.experimental.CsvDataset", v1=[]) +class CsvDatasetV2(dataset_ops.DatasetSource): """A Dataset comprising lines from one or more CSV files.""" def __init__(self, @@ -541,7 +572,9 @@ class CsvDataset(dataset_ops.DatasetSource): We can construct a CsvDataset from it as follows: ```python - dataset = tf.data.experimental.CsvDataset( + tf.enable_eager_execution() + + dataset = tf.data.experimental.CsvDataset( "my_file*.csv", [tf.float32, # Required field, use dtype or empty tensor tf.constant([0.0], dtype=tf.float32), # Optional field, default to 0.0 @@ -553,13 +586,8 @@ class CsvDataset(dataset_ops.DatasetSource): The expected output of its iterations is: ```python - next_element = dataset.make_one_shot_iterator().get_next() - with tf.Session() as sess: - while True: - try: - print(sess.run(next_element)) - except tf.errors.OutOfRangeError: - break + for element in dataset: + print(element) >> (4.28e10, 5.55e6, 12) >> (-5.3e14, 0.0, 2) @@ -594,7 +622,7 @@ class CsvDataset(dataset_ops.DatasetSource): the input data. If specified, only this subset of columns will be parsed. Defaults to parsing all columns. """ - super(CsvDataset, self).__init__() + super(CsvDatasetV2, self).__init__() self._filenames = ops.convert_to_tensor( filenames, dtype=dtypes.string, name="filenames") self._compression_type = convert.optional_param_to_tensor( @@ -658,22 +686,43 @@ class CsvDataset(dataset_ops.DatasetSource): return self._output_classes -@tf_export("data.experimental.make_batched_features_dataset") -def make_batched_features_dataset(file_pattern, - batch_size, - features, - reader=core_readers.TFRecordDataset, - label_key=None, - reader_args=None, - num_epochs=None, - shuffle=True, - shuffle_buffer_size=10000, - shuffle_seed=None, - prefetch_buffer_size=optimization.AUTOTUNE, - reader_num_threads=1, - parser_num_threads=2, - sloppy_ordering=False, - drop_final_batch=False): +@tf_export(v1=["data.experimental.CsvDataset"]) +class CsvDatasetV1(dataset_ops.DatasetV1Adapter): + """A Dataset comprising lines from one or more CSV files.""" + + @functools.wraps(CsvDatasetV2.__init__) + def __init__(self, + filenames, + record_defaults, + compression_type=None, + buffer_size=None, + header=False, + field_delim=",", + use_quote_delim=True, + na_value="", + select_cols=None): + wrapped = CsvDatasetV2(filenames, record_defaults, compression_type, + buffer_size, header, field_delim, use_quote_delim, + na_value, select_cols) + super(CsvDatasetV1, self).__init__(wrapped) + + +@tf_export("data.experimental.make_batched_features_dataset", v1=[]) +def make_batched_features_dataset_v2(file_pattern, + batch_size, + features, + reader=core_readers.TFRecordDataset, + label_key=None, + reader_args=None, + num_epochs=None, + shuffle=True, + shuffle_buffer_size=10000, + shuffle_seed=None, + prefetch_buffer_size=optimization.AUTOTUNE, + reader_num_threads=1, + parser_num_threads=2, + sloppy_ordering=False, + drop_final_batch=False): """Returns a `Dataset` of feature dictionaries from `Example` protos. If label_key argument is provided, returns a `Dataset` of tuple @@ -819,6 +868,31 @@ def make_batched_features_dataset(file_pattern, return dataset +@tf_export(v1=["data.experimental.make_batched_features_dataset"]) +def make_batched_features_dataset_v1(file_pattern, # pylint: disable=missing-docstring + batch_size, + features, + reader=core_readers.TFRecordDataset, + label_key=None, + reader_args=None, + num_epochs=None, + shuffle=True, + shuffle_buffer_size=10000, + shuffle_seed=None, + prefetch_buffer_size=optimization.AUTOTUNE, + reader_num_threads=1, + parser_num_threads=2, + sloppy_ordering=False, + drop_final_batch=False): + return dataset_ops.DatasetV1Adapter(make_batched_features_dataset_v2( + file_pattern, batch_size, features, reader, label_key, reader_args, + num_epochs, shuffle, shuffle_buffer_size, shuffle_seed, + prefetch_buffer_size, reader_num_threads, parser_num_threads, + sloppy_ordering, drop_final_batch)) +make_batched_features_dataset_v2.__doc__ = ( + make_batched_features_dataset_v1.__doc__) + + def _get_file_names(file_pattern, shuffle): """Parse list of file names from pattern, optionally shuffled. @@ -850,8 +924,8 @@ def _get_file_names(file_pattern, shuffle): return file_names -@tf_export("data.experimental.SqlDataset") -class SqlDataset(dataset_ops.DatasetSource): +@tf_export("data.experimental.SqlDataset", v1=[]) +class SqlDatasetV2(dataset_ops.DatasetSource): """A `Dataset` consisting of the results from a SQL query.""" def __init__(self, driver_name, data_source_name, query, output_types): @@ -861,17 +935,14 @@ class SqlDataset(dataset_ops.DatasetSource): For example: ```python + tf.enable_eager_execution() + dataset = tf.data.experimental.SqlDataset("sqlite", "/foo/bar.sqlite3", "SELECT name, age FROM people", (tf.string, tf.int32)) - iterator = dataset.make_one_shot_iterator() - next_element = iterator.get_next() # Prints the rows of the result set of the above query. - while True: - try: - print(sess.run(next_element)) - except tf.errors.OutOfRangeError: - break + for element in dataset: + print(element) ``` Args: @@ -883,7 +954,7 @@ class SqlDataset(dataset_ops.DatasetSource): output_types: A tuple of `tf.DType` objects representing the types of the columns returned by `query`. """ - super(SqlDataset, self).__init__() + super(SqlDatasetV2, self).__init__() self._driver_name = ops.convert_to_tensor( driver_name, dtype=dtypes.string, name="driver_name") self._data_source_name = ops.convert_to_tensor( @@ -893,10 +964,9 @@ class SqlDataset(dataset_ops.DatasetSource): self._output_types = output_types def _as_variant_tensor(self): - return gen_dataset_ops.sql_dataset(self._driver_name, - self._data_source_name, self._query, - nest.flatten(self.output_types), - nest.flatten(self.output_shapes)) + return gen_experimental_dataset_ops.experimental_sql_dataset( + self._driver_name, self._data_source_name, self._query, + nest.flatten(self.output_types), nest.flatten(self.output_shapes)) @property def output_classes(self): @@ -910,3 +980,21 @@ class SqlDataset(dataset_ops.DatasetSource): @property def output_types(self): return self._output_types + + +@tf_export(v1=["data.experimental.SqlDataset"]) +class SqlDatasetV1(dataset_ops.DatasetV1Adapter): + """A `Dataset` consisting of the results from a SQL query.""" + + @functools.wraps(SqlDatasetV2.__init__) + def __init__(self, driver_name, data_source_name, query, output_types): + wrapped = SqlDatasetV2(driver_name, data_source_name, query, output_types) + super(SqlDatasetV1, self).__init__(wrapped) + + +# TODO(b/119044825): Until all `tf.data` unit tests are converted to V2, keep +# these aliases in place. +CsvDataset = CsvDatasetV1 +SqlDataset = SqlDatasetV1 +make_batched_features_dataset = make_batched_features_dataset_v1 +make_csv_dataset = make_csv_dataset_v1 diff --git a/tensorflow/python/data/experimental/ops/scan_ops.py b/tensorflow/python/data/experimental/ops/scan_ops.py index 1194238e2f..c768373cf4 100644 --- a/tensorflow/python/data/experimental/ops/scan_ops.py +++ b/tensorflow/python/data/experimental/ops/scan_ops.py @@ -24,7 +24,7 @@ from tensorflow.python.data.util import nest from tensorflow.python.data.util import sparse from tensorflow.python.framework import ops from tensorflow.python.framework import sparse_tensor -from tensorflow.python.ops import gen_dataset_ops +from tensorflow.python.ops import gen_experimental_dataset_ops from tensorflow.python.util.tf_export import tf_export @@ -125,16 +125,19 @@ class _ScanDataset(dataset_ops.UnaryDataset): self._state_shapes = nest.pack_sequence_as(self._state_shapes, weakened_state_shapes) - self._scan_func = wrapped_func.function - self._scan_func.add_to_graph(ops.get_default_graph()) + self._scan_func = wrapped_func + self._scan_func.function.add_to_graph(ops.get_default_graph()) + + def _functions(self): + return [self._scan_func] def _as_variant_tensor(self): input_t = self._input_dataset._as_variant_tensor() # pylint: disable=protected-access - return gen_dataset_ops.scan_dataset( + return gen_experimental_dataset_ops.experimental_scan_dataset( input_t, nest.flatten(sparse.serialize_sparse_tensors(self._initial_state)), - self._scan_func.captured_inputs, - f=self._scan_func, + self._scan_func.function.captured_inputs, + f=self._scan_func.function, **dataset_ops.flat_structure(self)) @property diff --git a/tensorflow/python/data/experimental/ops/shuffle_ops.py b/tensorflow/python/data/experimental/ops/shuffle_ops.py index a4307212da..d12328a714 100644 --- a/tensorflow/python/data/experimental/ops/shuffle_ops.py +++ b/tensorflow/python/data/experimental/ops/shuffle_ops.py @@ -26,7 +26,7 @@ from tensorflow.python.ops import gen_dataset_ops from tensorflow.python.util.tf_export import tf_export -class _ShuffleAndRepeatDataset(dataset_ops.UnaryDataset): +class _ShuffleAndRepeatDataset(dataset_ops.UnaryUnchangedStructureDataset): """A `Dataset` that fuses `shuffle` and `repeat`.""" def __init__(self, input_dataset, buffer_size, count=None, seed=None): @@ -53,18 +53,6 @@ class _ShuffleAndRepeatDataset(dataset_ops.UnaryDataset): **dataset_ops.flat_structure(self)) # pylint: enable=protected-access - @property - def output_classes(self): - return self._input_dataset.output_classes - - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_types(self): - return self._input_dataset.output_types - @tf_export("data.experimental.shuffle_and_repeat") def shuffle_and_repeat(buffer_size, count=None, seed=None): diff --git a/tensorflow/python/data/experimental/ops/sleep.py b/tensorflow/python/data/experimental/ops/sleep.py index 7e7d370f70..5e9d021ada 100644 --- a/tensorflow/python/data/experimental/ops/sleep.py +++ b/tensorflow/python/data/experimental/ops/sleep.py @@ -21,7 +21,7 @@ from tensorflow.python.data.ops import dataset_ops from tensorflow.python.ops import gen_experimental_dataset_ops -class _SleepDataset(dataset_ops.UnaryDataset): +class _SleepDataset(dataset_ops.UnaryUnchangedStructureDataset): """A `Dataset` that sleeps before producing each upstream element.""" def __init__(self, input_dataset, sleep_microseconds): diff --git a/tensorflow/python/data/experimental/ops/stats_aggregator.py b/tensorflow/python/data/experimental/ops/stats_aggregator.py index 5274c816a4..d5fcc033ab 100644 --- a/tensorflow/python/data/experimental/ops/stats_aggregator.py +++ b/tensorflow/python/data/experimental/ops/stats_aggregator.py @@ -17,7 +17,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -from tensorflow.python.ops import gen_dataset_ops +from tensorflow.python.ops import gen_experimental_dataset_ops as ged_ops from tensorflow.python.util.tf_export import tf_export @@ -47,7 +47,6 @@ class StatsAggregator(object): options = dataset_ops.Options() options.experimental_stats = tf.data.experimental.StatsOptions(aggregator) dataset = dataset.with_options(options) - iterator = dataset.make_one_shot_iterator() ``` To get a protocol buffer summary of the currently aggregated statistics, @@ -69,7 +68,7 @@ class StatsAggregator(object): def __init__(self): """Creates a `StatsAggregator`.""" - self._resource = gen_dataset_ops.stats_aggregator_handle() + self._resource = ged_ops.experimental_stats_aggregator_handle() # TODO(b/116314787): Update this/add support for V2 summary API. def get_summary(self): @@ -81,4 +80,4 @@ class StatsAggregator(object): Returns: A scalar string `tf.Tensor` that summarizes the aggregated statistics. """ - return gen_dataset_ops.stats_aggregator_summary(self._resource) + return ged_ops.experimental_stats_aggregator_summary(self._resource) diff --git a/tensorflow/python/data/experimental/ops/stats_ops.py b/tensorflow/python/data/experimental/ops/stats_ops.py index ca2f5f2a88..15a9d24546 100644 --- a/tensorflow/python/data/experimental/ops/stats_ops.py +++ b/tensorflow/python/data/experimental/ops/stats_ops.py @@ -20,7 +20,7 @@ from __future__ import print_function from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops -from tensorflow.python.ops import gen_dataset_ops +from tensorflow.python.ops import gen_experimental_dataset_ops from tensorflow.python.util import deprecation from tensorflow.python.util.tf_export import tf_export @@ -66,8 +66,10 @@ def bytes_produced_stats(tag): """ def _apply_fn(dataset): - return _StatsDataset(dataset, gen_dataset_ops.bytes_produced_stats_dataset, - tag) + return _StatsDataset( + dataset, + gen_experimental_dataset_ops.experimental_bytes_produced_stats_dataset, + tag) return _apply_fn @@ -89,12 +91,14 @@ def latency_stats(tag): """ def _apply_fn(dataset): - return _StatsDataset(dataset, gen_dataset_ops.latency_stats_dataset, tag) + return _StatsDataset( + dataset, + gen_experimental_dataset_ops.experimental_latency_stats_dataset, tag) return _apply_fn -class _StatsDataset(dataset_ops.UnaryDataset): +class _StatsDataset(dataset_ops.UnaryUnchangedStructureDataset): """A `Dataset` that acts as an identity, and also records statistics.""" def __init__(self, input_dataset, op_function, tag): @@ -108,15 +112,3 @@ class _StatsDataset(dataset_ops.UnaryDataset): self._input_dataset._as_variant_tensor(), # pylint: disable=protected-access self._tag, **dataset_ops.flat_structure(self)) - - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_types(self): - return self._input_dataset.output_types - - @property - def output_classes(self): - return self._input_dataset.output_classes diff --git a/tensorflow/python/data/experimental/ops/stats_options.py b/tensorflow/python/data/experimental/ops/stats_options.py index c088d3d888..6e884aa08a 100644 --- a/tensorflow/python/data/experimental/ops/stats_options.py +++ b/tensorflow/python/data/experimental/ops/stats_options.py @@ -20,25 +20,24 @@ from __future__ import division from __future__ import print_function from tensorflow.python.data.experimental.ops import stats_aggregator +from tensorflow.python.data.util import options from tensorflow.python.util.tf_export import tf_export @tf_export("data.experimental.StatsOptions") -class StatsOptions(object): +class StatsOptions(options.OptionsBase): """Represents options for collecting dataset stats using `StatsAggregator`. To apply `StatsOptions` with a `tf.data.Dataset` object, use the following pattern: ```python - aggretator = tf.data.experimental.StatsAggregator() + aggregator = tf.data.experimental.StatsAggregator() - options = dataset_ops.Options() + options = tf.data.Options() options.experimental_stats = tf.data.experimental.StatsOptions() options.experimental_stats.aggregator = aggregator dataset = dataset.with_options(options) - - iterator = dataset.make_one_shot_iterator() ``` Note: a `StatsAggregator` object can be attached either duing construction or @@ -52,52 +51,29 @@ class StatsOptions(object): ``` """ - for _name, _ty, _default, _docstring in [ - ("aggregator", stats_aggregator.StatsAggregator, None, - "Associate the given statistics options with the dataset pipeline."), - ("prefix", str, "", - "Prefix to prepend all statistics recorded for the input `dataset` with." - ), - ("counter_prefix", str, "", - "Prefix for the statistics recorded as counter."), - ("latency_all_edges", bool, True, - "Whether to add latency measurements on all edges."), - ]: - - def _make_getter(name): # pylint: disable=no-self-argument - - def getter(self): - return getattr(self, "_" + name) - - return getter - - def _make_setter(name, ty): # pylint: disable=no-self-argument - - def setter(self, value): - if not isinstance(value, ty): - raise TypeError( - "Attempting to set the option %s to incompatible value: %r when " - "it expects %r" % (name, value, ty)) - setattr(self, "_" + name, value) - - return setter - - vars()["_" + _name] = _default - vars()[_name] = property( - _make_getter(_name), _make_setter(_name, _ty), _default, _docstring) - - def __init__(self, aggregator=None): - if aggregator: - self.aggregator = aggregator - - def __eq__(self, other): - if isinstance(other, self.__class__): - return self.__dict__ == other.__dict__ - else: - return False - - def __ne__(self, other): - return not self.__eq__(other) - - def __str__(self): - return str(self.__dict__) + aggregator = options.create_option( + name="aggregator", + ty=stats_aggregator.StatsAggregator, + docstring= + "Associates the given statistics aggregator with the dataset pipeline.") + + prefix = options.create_option( + name="prefix", + ty=str, + docstring= + "Prefix to prepend all statistics recorded for the input `dataset` with.", + default="") + + counter_prefix = options.create_option( + name="counter_prefix", + ty=str, + docstring= + "Prefix for the statistics recorded as counter.", + default="") + + latency_all_edges = options.create_option( + name="latency_all_edges", + ty=bool, + docstring= + "Whether to add latency measurements on all edges.", + default=True) diff --git a/tensorflow/python/data/experimental/ops/threading_options.py b/tensorflow/python/data/experimental/ops/threading_options.py new file mode 100644 index 0000000000..dbf662186f --- /dev/null +++ b/tensorflow/python/data/experimental/ops/threading_options.py @@ -0,0 +1,50 @@ +# Copyright 2018 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. +# ============================================================================== +"""Experimental API for controlling threading in `tf.data` pipelines.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + + +from tensorflow.python.data.util import options +from tensorflow.python.util.tf_export import tf_export + + +@tf_export("data.experimental.ThreadingOptions") +class ThreadingOptions(options.OptionsBase): + """Represents options for dataset threading. + + To apply `ThreadingOptions` to a `dataset` object, use the following pattern: + + ```python + options = tf.data.Options() + options.experimental_threading = tf.data.experimental.ThreadingOptions() + options.experimental_threading.private_threadpool_size = 10 + dataset = dataset.with_options(options) + ``` + """ + + max_intra_op_parallelism = options.create_option( + name="max_intra_op_parallelism", + ty=int, + docstring= + "If set, it overrides the maximum degree of intra-op parallelism.") + + private_threadpool_size = options.create_option( + name="private_threadpool_size", + ty=int, + docstring= + "If set, the dataset will use a private threadpool of the given size.", + default=None) diff --git a/tensorflow/python/data/experimental/ops/threadpool.py b/tensorflow/python/data/experimental/ops/threadpool.py index 3ea017c6e8..69e8829d68 100644 --- a/tensorflow/python/data/experimental/ops/threadpool.py +++ b/tensorflow/python/data/experimental/ops/threadpool.py @@ -60,7 +60,7 @@ class PrivateThreadPool(object): display_name=display_name) -class _ThreadPoolDataset(dataset_ops.UnaryDataset): +class _ThreadPoolDataset(dataset_ops.UnaryUnchangedStructureDataset): """A `Dataset` that acts as an identity, and sets a custom threadpool.""" def __init__(self, input_dataset, thread_pool): @@ -74,18 +74,6 @@ class _ThreadPoolDataset(dataset_ops.UnaryDataset): self._thread_pool._resource, # pylint: disable=protected-access **dataset_ops.flat_structure(self)) - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_types(self): - return self._input_dataset.output_types - - @property - def output_classes(self): - return self._input_dataset.output_classes - # TODO(b/73383364): Properly export in the `tf.data.experimental` API when # stable or make private / remove. diff --git a/tensorflow/python/data/experimental/ops/unique.py b/tensorflow/python/data/experimental/ops/unique.py index 2a7775c456..55ed98d854 100644 --- a/tensorflow/python/data/experimental/ops/unique.py +++ b/tensorflow/python/data/experimental/ops/unique.py @@ -48,7 +48,7 @@ def unique(): return _apply_fn -class _UniqueDataset(dataset_ops.UnaryDataset): +class _UniqueDataset(dataset_ops.UnaryUnchangedStructureDataset): """A `Dataset` contains the unique elements from its input.""" def __init__(self, input_dataset): @@ -65,15 +65,3 @@ class _UniqueDataset(dataset_ops.UnaryDataset): return gen_experimental_dataset_ops.experimental_unique_dataset( self._input_dataset._as_variant_tensor(), # pylint: disable=protected-access **dataset_ops.flat_structure(self)) - - @property - def output_classes(self): - return self._input_dataset.output_classes - - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_types(self): - return self._input_dataset.output_types diff --git a/tensorflow/python/data/experimental/ops/writers.py b/tensorflow/python/data/experimental/ops/writers.py index 994447cb4d..aef6da5140 100644 --- a/tensorflow/python/data/experimental/ops/writers.py +++ b/tensorflow/python/data/experimental/ops/writers.py @@ -22,7 +22,7 @@ from tensorflow.python.data.util import convert from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape -from tensorflow.python.ops import gen_dataset_ops +from tensorflow.python.ops import gen_experimental_dataset_ops from tensorflow.python.util.tf_export import tf_export @@ -48,7 +48,7 @@ class TFRecordWriter(object): Returns: A `tf.Operation` that, when run, writes contents of `dataset` to a file. """ - if not isinstance(dataset, dataset_ops.Dataset): + if not isinstance(dataset, dataset_ops.DatasetV2): raise TypeError("`dataset` must be a `tf.data.Dataset` object.") if (dataset.output_types != dtypes.string or dataset.output_shapes != tensor_shape.scalar()): @@ -56,5 +56,5 @@ class TFRecordWriter(object): "`dataset` must produce scalar `DT_STRING` tensors whereas it " "produces shape {0} and types {1}".format(dataset.output_shapes, dataset.output_types)) - return gen_dataset_ops.dataset_to_tf_record( + return gen_experimental_dataset_ops.experimental_dataset_to_tf_record( dataset._as_variant_tensor(), self._filename, self._compression_type) # pylint: disable=protected-access diff --git a/tensorflow/python/data/kernel_tests/BUILD b/tensorflow/python/data/kernel_tests/BUILD index 21eed2b070..9f7ce99cbc 100644 --- a/tensorflow/python/data/kernel_tests/BUILD +++ b/tensorflow/python/data/kernel_tests/BUILD @@ -10,48 +10,46 @@ load("//tensorflow:tensorflow.bzl", "tf_py_test") load("//tensorflow:tensorflow.bzl", "cuda_py_test") tf_py_test( - name = "batch_dataset_op_test", + name = "batch_test", size = "small", - srcs = ["batch_dataset_op_test.py"], + srcs = ["batch_test.py"], additional_deps = [ ":test_base", "@absl_py//absl/testing:parameterized", "//third_party/py/numpy", + "//tensorflow/python/data/ops:dataset_ops", "//tensorflow/python:array_ops", "//tensorflow/python:client_testlib", - "//tensorflow/python:constant_op", "//tensorflow/python:dtypes", "//tensorflow/python:errors", "//tensorflow/python:math_ops", - "//tensorflow/python:string_ops", - "//tensorflow/python:tensor_shape", - "//tensorflow/python:util", - "//tensorflow/python/data/ops:dataset_ops", + "//tensorflow/python:sparse_tensor", ], ) tf_py_test( - name = "cache_dataset_op_test", + name = "cache_test", size = "small", - srcs = ["cache_dataset_op_test.py"], + srcs = ["cache_test.py"], additional_deps = [ ":test_base", "//third_party/py/numpy", + "//tensorflow/python/data/ops:dataset_ops", + "//tensorflow/python/data/ops:iterator_ops", "//tensorflow/python:array_ops", "//tensorflow/python:client_testlib", "//tensorflow/python:constant_op", "//tensorflow/python:dtypes", "//tensorflow/python:errors", + "//tensorflow/python:framework_ops", "//tensorflow/python:variables", - "//tensorflow/python/data/ops:dataset_ops", - "//tensorflow/python/data/ops:iterator_ops", ], ) tf_py_test( - name = "concatenate_dataset_op_test", + name = "concatenate_test", size = "small", - srcs = ["concatenate_dataset_op_test.py"], + srcs = ["concatenate_test.py"], additional_deps = [ ":test_base", "//third_party/py/numpy", @@ -64,153 +62,257 @@ tf_py_test( ) tf_py_test( - name = "dataset_constructor_op_test", + name = "dataset_checkpoint_test", size = "small", - srcs = ["dataset_constructor_op_test.py"], + srcs = ["dataset_checkpoint_test.py"], additional_deps = [ ":test_base", - "//third_party/py/numpy", - "//tensorflow/core:protos_all_py", - "//tensorflow/python:array_ops", + "//tensorflow/python/data/ops:dataset_ops", + "//tensorflow/python/data/ops:iterator_ops", "//tensorflow/python:client_testlib", + "//tensorflow/python:dataset_ops_gen", "//tensorflow/python:dtypes", "//tensorflow/python:errors", "//tensorflow/python:framework_ops", - "//tensorflow/python:math_ops", - "//tensorflow/python:random_ops", - "//tensorflow/python:resource_variable_ops", - "//tensorflow/python:session", - "//tensorflow/python:sparse_tensor", + "//tensorflow/python:framework_test_lib", + "//tensorflow/python:io_ops", + "//tensorflow/python:parsing_ops", + "//tensorflow/python:platform", "//tensorflow/python:tensor_shape", + "//tensorflow/python:variables", + ], +) + +tf_py_test( + name = "dataset_test", + size = "small", + srcs = ["dataset_test.py"], + additional_deps = [ + ":test_base", + "@absl_py//absl/testing:parameterized", + "//third_party/py/numpy", + "//tensorflow/core:protos_all_py", "//tensorflow/python/data/ops:dataset_ops", + "//tensorflow/python/data/ops:readers", "//tensorflow/python/data/util:nest", - "//tensorflow/python/data/util:sparse", - ], - tags = [ - "manual", - "nomac", # b/62040583 + "//tensorflow/python:client_testlib", + "//tensorflow/python:dtypes", + "//tensorflow/python:sparse_tensor", ], ) tf_py_test( - name = "dataset_from_generator_op_test", - size = "medium", - srcs = ["dataset_from_generator_op_test.py"], + name = "filter_test", + size = "small", + srcs = ["filter_test.py"], additional_deps = [ ":test_base", "//third_party/py/numpy", + "//tensorflow/python:array_ops", "//tensorflow/python:client_testlib", "//tensorflow/python:dtypes", "//tensorflow/python:errors", - "//tensorflow/python:tensor_shape", + "//tensorflow/python:functional_ops", + "//tensorflow/python:math_ops", + "//tensorflow/python:sparse_tensor", "//tensorflow/python/data/ops:dataset_ops", - "//tensorflow/python/data/util:sparse", ], ) tf_py_test( - name = "dataset_ops_test", + name = "fixed_length_record_dataset_test", size = "small", - srcs = ["dataset_ops_test.py"], + srcs = ["fixed_length_record_dataset_test.py"], + additional_deps = [ + ":test_base", + "//tensorflow/python:array_ops", + "//tensorflow/python:client_testlib", + "//tensorflow/python:dataset_ops_gen", + "//tensorflow/python:dtypes", + "//tensorflow/python:errors", + "//tensorflow/python:framework_ops", + "//tensorflow/python:io_ops", + "//tensorflow/python:parsing_ops", + "//tensorflow/python:tensor_shape", + "//tensorflow/python:util", + "//tensorflow/python/data/ops:iterator_ops", + "//tensorflow/python/data/ops:readers", + ], +) + +tf_py_test( + name = "flat_map_test", + size = "medium", + srcs = ["flat_map_test.py"], additional_deps = [ ":test_base", - "@absl_py//absl/testing:parameterized", "//third_party/py/numpy", + "//tensorflow/core:protos_all_py", + "//tensorflow/python/data/ops:readers", + "//tensorflow/python/data/util:nest", "//tensorflow/python:client_testlib", + "//tensorflow/python:errors", + "//tensorflow/python:session", + "//tensorflow/python:sparse_ops", "//tensorflow/python:sparse_tensor", + "//tensorflow/python:training", "//tensorflow/python/data/ops:dataset_ops", ], + grpc_enabled = True, +) + +tf_py_test( + name = "from_generator_test", + size = "medium", + srcs = ["from_generator_test.py"], + additional_deps = [ + ":test_base", + "//third_party/py/numpy", + "//tensorflow/python/data/ops:dataset_ops", + "//tensorflow/python:client_testlib", + "//tensorflow/python:constant_op", + "//tensorflow/python:dtypes", + "//tensorflow/python:errors", + "//tensorflow/python:script_ops", + "//tensorflow/python:session", + ], ) tf_py_test( - name = "filter_dataset_op_test", + name = "from_sparse_tensor_slices_test", size = "small", - srcs = ["filter_dataset_op_test.py"], + srcs = ["from_sparse_tensor_slices_test.py"], additional_deps = [ ":test_base", "//third_party/py/numpy", + "//tensorflow/core:protos_all_py", "//tensorflow/python:array_ops", "//tensorflow/python:client_testlib", "//tensorflow/python:dtypes", "//tensorflow/python:errors", - "//tensorflow/python:functional_ops", + "//tensorflow/python:framework_ops", "//tensorflow/python:math_ops", + "//tensorflow/python:resource_variable_ops", + "//tensorflow/python:session", "//tensorflow/python:sparse_tensor", + "//tensorflow/python:tensor_shape", "//tensorflow/python/data/ops:dataset_ops", + "//tensorflow/python/data/util:nest", ], ) tf_py_test( - name = "flat_map_dataset_op_test", + name = "from_tensors_test", size = "small", - srcs = ["flat_map_dataset_op_test.py"], + srcs = ["from_tensors_test.py"], additional_deps = [ ":test_base", "//third_party/py/numpy", + "//tensorflow/core:protos_all_py", + "//tensorflow/python:array_ops", "//tensorflow/python:client_testlib", + "//tensorflow/python:dtypes", "//tensorflow/python:errors", + "//tensorflow/python:framework_ops", + "//tensorflow/python:math_ops", + "//tensorflow/python:resource_variable_ops", "//tensorflow/python:session", - "//tensorflow/python:sparse_ops", "//tensorflow/python:sparse_tensor", - "//tensorflow/python:training", + "//tensorflow/python:tensor_shape", "//tensorflow/python/data/ops:dataset_ops", + "//tensorflow/python/data/util:nest", + ], + tags = [ + "nomac", # b/62040583 ], - grpc_enabled = True, ) tf_py_test( - name = "list_files_dataset_op_test", + name = "from_tensor_slices_test", size = "small", - srcs = ["list_files_dataset_op_test.py"], + srcs = ["from_tensor_slices_test.py"], additional_deps = [ ":test_base", - "//tensorflow/python:array_ops", + "//third_party/py/numpy", "//tensorflow/python:client_testlib", "//tensorflow/python:dtypes", "//tensorflow/python:errors", - "//tensorflow/python:util", + "//tensorflow/python:sparse_tensor", + "//tensorflow/python:tensor_shape", "//tensorflow/python/data/ops:dataset_ops", ], ) tf_py_test( - name = "inputs_test", - size = "small", - srcs = ["inputs_test.py"], + name = "interleave_test", + size = "medium", + srcs = ["interleave_test.py"], additional_deps = [ ":test_base", "@absl_py//absl/testing:parameterized", "//third_party/py/numpy", + "//tensorflow/core:protos_all_py", + "//tensorflow/python/data/ops:dataset_ops", + "//tensorflow/python:array_ops", "//tensorflow/python:client_testlib", + "//tensorflow/python:errors", + "//tensorflow/python:script_ops", + "//tensorflow/python:sparse_ops", "//tensorflow/python:sparse_tensor", + ], +) + +tf_py_test( + name = "iterator_checkpoint_test", + size = "medium", + srcs = ["iterator_checkpoint_test.py"], + additional_deps = [ + ":test_base", "//tensorflow/python/data/ops:dataset_ops", + "//tensorflow/python/eager:context", + "//tensorflow/python/training/checkpointable:util", + "//tensorflow/python:checkpoint_management", + "//tensorflow/python:client_testlib", + "//tensorflow/python:errors", + "//tensorflow/python:framework_test_lib", + "//tensorflow/python:math_ops", ], + grpc_enabled = True, ) tf_py_test( - name = "interleave_dataset_op_test", + name = "iterator_cluster_test", size = "small", - srcs = ["interleave_dataset_op_test.py"], + srcs = ["iterator_cluster_test.py"], additional_deps = [ - ":test_base", - "@absl_py//absl/testing:parameterized", - "//third_party/py/numpy", + "//tensorflow/core:protos_all_py", + "//tensorflow/python/data/ops:dataset_ops", + "//tensorflow/python/data/ops:iterator_ops", "//tensorflow/python:array_ops", "//tensorflow/python:client_testlib", + "//tensorflow/python:constant_op", "//tensorflow/python:dtypes", "//tensorflow/python:errors", + "//tensorflow/python:framework_ops", + "//tensorflow/python:framework_test_lib", + "//tensorflow/python:function", + "//tensorflow/python:functional_ops", + "//tensorflow/python:lookup_ops", + "//tensorflow/python:math_ops", "//tensorflow/python:session", - "//tensorflow/python:sparse_ops", - "//tensorflow/python:sparse_tensor", - "//tensorflow/python:training", - "//tensorflow/python/data/ops:dataset_ops", + "//tensorflow/python:string_ops", + ], + grpc_enabled = True, + tags = [ + "no_oss", # Test flaky due to port collisions. + "no_windows", ], ) cuda_py_test( - name = "iterator_ops_test", - size = "small", - srcs = ["iterator_ops_test.py"], + name = "iterator_test", + size = "medium", + srcs = ["iterator_test.py"], additional_deps = [ "@absl_py//absl/testing:parameterized", "//third_party/py/numpy", @@ -249,41 +351,30 @@ cuda_py_test( ) tf_py_test( - name = "iterator_ops_cluster_test", + name = "list_files_test", size = "small", - srcs = ["iterator_ops_cluster_test.py"], + srcs = ["list_files_test.py"], additional_deps = [ - "//tensorflow/core:protos_all_py", + ":test_base", "//tensorflow/python:array_ops", "//tensorflow/python:client_testlib", "//tensorflow/python:dtypes", "//tensorflow/python:errors", - "//tensorflow/python:framework_ops", - "//tensorflow/python:framework_test_lib", - "//tensorflow/python:function", - "//tensorflow/python:functional_ops", - "//tensorflow/python:session", + "//tensorflow/python:util", "//tensorflow/python/data/ops:dataset_ops", - "//tensorflow/python/data/ops:iterator_ops", - "//tensorflow/python:constant_op", - "//tensorflow/python:string_ops", - "//tensorflow/python:lookup_ops", - ], - grpc_enabled = True, - tags = [ - "no_oss", # Test flaky due to port collisions. - "no_windows", ], ) tf_py_test( - name = "map_dataset_op_test", - size = "small", - srcs = ["map_dataset_op_test.py"], + name = "map_test", + size = "medium", + srcs = ["map_test.py"], additional_deps = [ ":test_base", "@absl_py//absl/testing:parameterized", "//third_party/py/numpy", + "//tensorflow/core:protos_all_py", + "//tensorflow/python/data/ops:dataset_ops", "//tensorflow/python:array_ops", "//tensorflow/python:client_testlib", "//tensorflow/python:constant_op", @@ -297,27 +388,12 @@ tf_py_test( "//tensorflow/python:math_ops", "//tensorflow/python:random_ops", "//tensorflow/python:script_ops", + "//tensorflow/python:session", "//tensorflow/python:sparse_ops", "//tensorflow/python:sparse_tensor", "//tensorflow/python:string_ops", + "//tensorflow/python:tensor_util", "//tensorflow/python:variable_scope", - "//tensorflow/python/data/ops:dataset_ops", - ], -) - -tf_py_test( - name = "matching_files_dataset_op_test", - size = "small", - srcs = ["matching_files_dataset_op_test.py"], - additional_deps = [ - ":test_base", - "//third_party/py/numpy", - "//tensorflow/python:array_ops", - "//tensorflow/python:client_testlib", - "//tensorflow/python:dtypes", - "//tensorflow/python:errors", - "//tensorflow/python:util", - "//tensorflow/python/data/ops:dataset_ops", ], ) @@ -332,6 +408,7 @@ cuda_py_test( "//tensorflow/python/data/ops:multi_device_iterator_ops", "//tensorflow/python/data/ops:iterator_ops", "//tensorflow/python/data/experimental/ops:optimization", + "//tensorflow/python/data/experimental/ops:optimization_options", "//tensorflow/python:array_ops", "//tensorflow/python:client_testlib", "//tensorflow/python:dtypes", @@ -345,9 +422,9 @@ cuda_py_test( ) cuda_py_test( - name = "optional_ops_test", + name = "optional_test", size = "small", - srcs = ["optional_ops_test.py"], + srcs = ["optional_test.py"], additional_deps = [ ":test_base", "@absl_py//absl/testing:parameterized", @@ -366,70 +443,58 @@ cuda_py_test( ) tf_py_test( - name = "prefetch_dataset_op_test", + name = "padded_batch_test", size = "small", - srcs = ["prefetch_dataset_op_test.py"], + srcs = ["padded_batch_test.py"], additional_deps = [ ":test_base", "@absl_py//absl/testing:parameterized", + "//third_party/py/numpy", + "//tensorflow/python/data/ops:dataset_ops", "//tensorflow/python:array_ops", "//tensorflow/python:client_testlib", - "//tensorflow/python:dataset_ops_gen", + "//tensorflow/python:constant_op", "//tensorflow/python:dtypes", "//tensorflow/python:errors", - "//tensorflow/python/data/ops:dataset_ops", + "//tensorflow/python:sparse_tensor", + "//tensorflow/python:string_ops", + "//tensorflow/python:tensor_shape", + "//tensorflow/python:util", ], ) tf_py_test( - name = "range_dataset_op_test", + name = "prefetch_test", size = "small", - srcs = ["range_dataset_op_test.py"], + srcs = ["prefetch_test.py"], additional_deps = [ ":test_base", + "@absl_py//absl/testing:parameterized", "//tensorflow/python:array_ops", "//tensorflow/python:client_testlib", "//tensorflow/python:dataset_ops_gen", "//tensorflow/python:dtypes", "//tensorflow/python:errors", - "//tensorflow/python:io_ops", - "//tensorflow/python:framework_ops", - "//tensorflow/python:parsing_ops", - "//tensorflow/python:platform", - "//tensorflow/python:tensor_shape", - "//tensorflow/python:variables", - "//tensorflow/python/data/ops:dataset_ops", - "//tensorflow/python/data/ops:iterator_ops", ], ) tf_py_test( - name = "reader_dataset_ops_test", + name = "range_test", size = "small", - srcs = ["reader_dataset_ops_test.py"], + srcs = ["range_test.py"], additional_deps = [ ":test_base", - "//tensorflow/python:array_ops", + "//tensorflow/python/data/ops:dataset_ops", "//tensorflow/python:client_testlib", - "//tensorflow/python:constant_op", - "//tensorflow/python:dataset_ops_gen", - "//tensorflow/python:dtypes", "//tensorflow/python:errors", - "//tensorflow/python:framework_ops", - "//tensorflow/python:io_ops", - "//tensorflow/python:lib", - "//tensorflow/python:parsing_ops", - "//tensorflow/python:tensor_shape", - "//tensorflow/python:util", - "//tensorflow/python/data/ops:iterator_ops", - "//tensorflow/python/data/ops:readers", + "//tensorflow/python:framework_test_lib", ], ) tf_py_test( - name = "reduce_dataset_op_test", + name = "reduce_test", size = "small", - srcs = ["reduce_dataset_op_test.py"], + srcs = ["reduce_test.py"], additional_deps = [ ":test_base", "@absl_py//absl/testing:parameterized", @@ -437,7 +502,6 @@ tf_py_test( "//tensorflow/python:array_ops", "//tensorflow/python:client_testlib", "//tensorflow/python:dtypes", - "//tensorflow/python:errors", "//tensorflow/python:math_ops", "//tensorflow/python:sparse_tensor", "//tensorflow/python/data/ops:dataset_ops", @@ -445,9 +509,9 @@ tf_py_test( ) tf_py_test( - name = "sequence_dataset_op_test", + name = "repeat_test", size = "small", - srcs = ["sequence_dataset_op_test.py"], + srcs = ["repeat_test.py"], additional_deps = [ ":test_base", "//third_party/py/numpy", @@ -460,9 +524,9 @@ tf_py_test( ) tf_py_test( - name = "shard_dataset_op_test", + name = "shard_test", size = "small", - srcs = ["shard_dataset_op_test.py"], + srcs = ["shard_test.py"], additional_deps = [ ":test_base", "//tensorflow/python:client_testlib", @@ -472,9 +536,9 @@ tf_py_test( ) tf_py_test( - name = "shuffle_dataset_op_test", + name = "shuffle_test", size = "small", - srcs = ["shuffle_dataset_op_test.py"], + srcs = ["shuffle_test.py"], additional_deps = [ ":test_base", "@absl_py//absl/testing:parameterized", @@ -491,21 +555,91 @@ tf_py_test( ], ) +tf_py_test( + name = "skip_test", + size = "small", + srcs = ["skip_test.py"], + additional_deps = [ + ":test_base", + "//third_party/py/numpy", + "//tensorflow/python:array_ops", + "//tensorflow/python:client_testlib", + "//tensorflow/python:dtypes", + "//tensorflow/python:errors", + "//tensorflow/python/data/ops:dataset_ops", + ], +) + +tf_py_test( + name = "take_test", + size = "small", + srcs = ["take_test.py"], + additional_deps = [ + ":test_base", + "//third_party/py/numpy", + "//tensorflow/python:array_ops", + "//tensorflow/python:client_testlib", + "//tensorflow/python:dtypes", + "//tensorflow/python:errors", + "//tensorflow/python/data/ops:dataset_ops", + ], +) + +tf_py_test( + name = "text_line_dataset_test", + size = "small", + srcs = ["text_line_dataset_test.py"], + additional_deps = [ + ":test_base", + "//tensorflow/python/data/ops:iterator_ops", + "//tensorflow/python/data/ops:readers", + "//tensorflow/python/eager:context", + "//tensorflow/python:array_ops", + "//tensorflow/python:client_testlib", + "//tensorflow/python:dtypes", + "//tensorflow/python:errors", + "//tensorflow/python:util", + ], +) + +tf_py_test( + name = "tf_record_dataset_test", + size = "small", + srcs = ["tf_record_dataset_test.py"], + additional_deps = [ + ":test_base", + "//tensorflow/python/data/ops:dataset_ops", + "//tensorflow/python/data/ops:iterator_ops", + "//tensorflow/python/data/ops:readers", + "//tensorflow/python:array_ops", + "//tensorflow/python:client_testlib", + "//tensorflow/python:constant_op", + "//tensorflow/python:dtypes", + "//tensorflow/python:errors", + "//tensorflow/python:lib", + "//tensorflow/python:util", + ], +) + py_library( name = "test_base", srcs = ["test_base.py"], deps = [ + "//tensorflow/python:array_ops", "//tensorflow/python:client_testlib", + "//tensorflow/python:dtypes", "//tensorflow/python:errors", "//tensorflow/python:sparse_tensor", + "//tensorflow/python/data/ops:dataset_ops", "//tensorflow/python/data/util:nest", + "//tensorflow/python/eager:context", ], ) tf_py_test( - name = "window_dataset_op_test", - size = "small", - srcs = ["window_dataset_op_test.py"], + name = "window_test", + size = "medium", + srcs = ["window_test.py"], additional_deps = [ ":test_base", "@absl_py//absl/testing:parameterized", @@ -521,9 +655,9 @@ tf_py_test( ) tf_py_test( - name = "zip_dataset_op_test", + name = "zip_test", size = "small", - srcs = ["zip_dataset_op_test.py"], + srcs = ["zip_test.py"], additional_deps = [ ":test_base", "//third_party/py/numpy", diff --git a/tensorflow/python/data/kernel_tests/batch_dataset_op_test.py b/tensorflow/python/data/kernel_tests/batch_dataset_op_test.py deleted file mode 100644 index e8decb9ad0..0000000000 --- a/tensorflow/python/data/kernel_tests/batch_dataset_op_test.py +++ /dev/null @@ -1,515 +0,0 @@ -# -*- coding: utf-8 -*- -# 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. -# ============================================================================== -"""Tests for the experimental input pipeline ops.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import time - -from absl.testing import parameterized -import numpy as np - -from tensorflow.python.client import session -from tensorflow.python.data.kernel_tests import test_base -from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.framework import constant_op -from tensorflow.python.framework import dtypes -from tensorflow.python.framework import errors -from tensorflow.python.framework import sparse_tensor -from tensorflow.python.framework import tensor_shape -from tensorflow.python.ops import array_ops -from tensorflow.python.ops import math_ops -from tensorflow.python.ops import string_ops -from tensorflow.python.platform import test -from tensorflow.python.util import compat - - -class BatchDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): - - @parameterized.named_parameters( - ('even', 28, 14, False), - ('uneven_with_remainder', 28, 15, False), - ('uneven_without_remainder', 28, 15, True), - ('empty', 0, 14, False), - ) - def testBatchDataset(self, count, batch_size, drop_remainder): - """Tests the batch dataset logic for various input configurations. - - Args: - count: the number of input elements - batch_size: the batch size - drop_remainder: whether a smaller batch size should be produced if batch - size does not divide number of inputs evenly - """ - - # The pipeline is TensorSliceDataset -> MapDataset(square_3) -> - # RepeatDataset(count) -> BatchDataset(batch_size). - components = (np.arange(7), - np.array([[1, 2, 3]]) * np.arange(7)[:, np.newaxis], - np.array(37.0) * np.arange(7)) - - count_t = array_ops.placeholder(dtypes.int64, shape=[]) - batch_size_t = array_ops.placeholder(dtypes.int64, shape=[]) - drop_remainder_t = array_ops.placeholder(dtypes.bool, shape=[]) - - def _map_fn(x, y, z): - return math_ops.square(x), math_ops.square(y), math_ops.square(z) - - iterator = ( - dataset_ops.Dataset.from_tensor_slices(components).map(_map_fn) - .repeat(count).batch(batch_size, - drop_remainder).make_initializable_iterator()) - init_op = iterator.initializer - get_next = iterator.get_next() - - if drop_remainder: - dim0 = batch_size - else: - dim0 = None - self.assertEqual([[dim0] + list(c.shape[1:]) for c in components], - [t.shape.as_list() for t in get_next]) - - with self.cached_session() as sess: - sess.run( - init_op, - feed_dict={ - count_t: count, - batch_size_t: batch_size, - drop_remainder_t: drop_remainder - }) - num_full_batches = (count * 7) // batch_size - for i in range(num_full_batches): - result = sess.run(get_next) - for component, result_component in zip(components, result): - for j in range(batch_size): - self.assertAllEqual(component[(i * batch_size + j) % 7]**2, - result_component[j]) - if not drop_remainder and (count * 7) % batch_size > 0: - result = sess.run(get_next) - for component, result_component in zip(components, result): - for j in range((count * 7) % batch_size): - self.assertAllEqual( - component[(num_full_batches * batch_size + j) % 7]**2, - result_component[j]) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testBatchDatasetInvalidBatchSize(self): - iterator = (dataset_ops.Dataset.range(10).batch(0).make_one_shot_iterator()) - get_next = iterator.get_next() - - with self.cached_session() as sess: - with self.assertRaises(errors.InvalidArgumentError): - sess.run(get_next) - - def testBatchSparse(self): - - def _sparse(i): - return sparse_tensor.SparseTensorValue( - indices=[[0]], values=(i * [1]), dense_shape=[1]) - - iterator = dataset_ops.Dataset.range(10).map(_sparse).batch( - 5).make_initializable_iterator() - init_op = iterator.initializer - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(init_op) - for i in range(2): - actual = sess.run(get_next) - expected = sparse_tensor.SparseTensorValue( - indices=[[0, 0], [1, 0], [2, 0], [3, 0], [4, 0]], - values=[i * 5, i * 5 + 1, i * 5 + 2, i * 5 + 3, i * 5 + 4], - dense_shape=[5, 1]) - self.assertTrue(sparse_tensor.is_sparse(actual)) - self.assertSparseValuesEqual(actual, expected) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testBatchSparseWithDifferentDenseShapes(self): - - def _sparse(i): - return sparse_tensor.SparseTensorValue( - indices=array_ops.expand_dims( - math_ops.range(i, dtype=dtypes.int64), 1), - values=array_ops.fill([math_ops.to_int32(i)], i), - dense_shape=[i]) - - iterator = dataset_ops.Dataset.range(10).map(_sparse).batch( - 5).make_initializable_iterator() - init_op = iterator.initializer - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(init_op) - for i in range(2): - actual = sess.run(get_next) - expected_indices = [] - expected_values = [] - for j in range(5): - for k in range(i * 5 + j): - expected_indices.append([j, k]) - expected_values.append(i * 5 + j) - expected = sparse_tensor.SparseTensorValue( - indices=expected_indices, - values=expected_values, - dense_shape=[5, (i + 1) * 5 - 1]) - self.assertTrue(sparse_tensor.is_sparse(actual)) - self.assertSparseValuesEqual(actual, expected) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testNestedBatchSparse(self): - - def _sparse(i): - return sparse_tensor.SparseTensorValue( - indices=[[0]], values=(i * [1]), dense_shape=[1]) - - iterator = dataset_ops.Dataset.range(10).map(_sparse).batch(5).batch( - 2).make_initializable_iterator() - init_op = iterator.initializer - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(init_op) - actual = sess.run(get_next) - expected = sparse_tensor.SparseTensorValue( - indices=[[0, 0, 0], [0, 1, 0], [0, 2, 0], [0, 3, 0], [0, 4, 0], - [1, 0, 0], [1, 1, 0], [1, 2, 0], [1, 3, 0], [1, 4, 0]], - values=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - dense_shape=[2, 5, 1]) - self.assertTrue(sparse_tensor.is_sparse(actual)) - self.assertSparseValuesEqual(actual, expected) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testBatchShapeError(self): - - def generator(): - yield [1.0, 2.0, 3.0] - yield [4.0, 5.0, 6.0] - yield [7.0, 8.0, 9.0, 10.0] - - iterator = ( - dataset_ops.Dataset.from_generator( - generator, dtypes.float32, output_shapes=[None]).batch(3) - .make_initializable_iterator()) - next_element = iterator.get_next() - - with self.cached_session() as sess: - sess.run(iterator.initializer) - with self.assertRaisesRegexp( - errors.InvalidArgumentError, - r'Cannot batch tensors with different shapes in component 0. ' - r'First element had shape \[3\] and element 2 had shape \[4\].'): - sess.run(next_element) - - -def _random_seq_lens(count): - return np.random.randint(20, size=(count,)).astype(np.int32) - - -class PaddedBatchDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): - - @parameterized.named_parameters( - ('default_padding', _random_seq_lens(32), 4, [-1], False), - ('constant_padding', _random_seq_lens(32), 4, [25], False), - ('uneven_with_remainder', _random_seq_lens(34), 4, [-1], False), - ('uneven_without_remainder', _random_seq_lens(34), 4, [-1], True), - ) - def testPaddedBatchDataset(self, seq_lens, batch_size, padded_shapes, - drop_remainder): - """Tests the padded batch dataset logic for various input configurations. - - Args: - seq_lens: the input sequence lengths - batch_size: the batch size - padded_shapes: the padded shapes to use - drop_remainder: whether a smaller batch size should be produced if batch - size does not divide number of inputs evenly - """ - - seq_lens_t = array_ops.placeholder(dtypes.int32, shape=[None]) - batch_size_t = array_ops.placeholder(dtypes.int64, shape=[]) - padded_shapes_t = array_ops.placeholder(dtypes.int64, shape=[1]) - drop_remainder_t = array_ops.placeholder(dtypes.bool, shape=[]) - - iterator = ( - dataset_ops.Dataset.from_tensor_slices(seq_lens_t) - .map(lambda x: array_ops.fill([x], x)).padded_batch( - batch_size=batch_size_t, - drop_remainder=drop_remainder_t, - padded_shapes=padded_shapes_t).make_initializable_iterator()) - - init_op = iterator.initializer - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run( - init_op, - feed_dict={ - seq_lens_t: seq_lens, - batch_size_t: batch_size, - padded_shapes_t: padded_shapes, - drop_remainder_t: drop_remainder, - }) - - num_full_batches = len(seq_lens) // batch_size - - for i in range(num_full_batches): - result = sess.run(get_next) - padded_len = padded_shapes[0] - if padded_len is None or padded_len == -1: - padded_len = np.max(result) if result.size > 0 else 0 - self.assertEqual((batch_size, padded_len), result.shape) - for j in range(batch_size): - seq_len = seq_lens[(i * batch_size) + j] - self.assertAllEqual(result[j, :seq_len], [seq_len] * seq_len) - self.assertAllEqual(result[j, seq_len:], - [0] * (padded_len - seq_len)) - - if not drop_remainder and len(seq_lens) % batch_size > 0: - result = sess.run(get_next) - padded_len = np.max(result) if result.size > 0 else 0 - self.assertEqual((len(seq_lens) % batch_size, padded_len), - result.shape) - for j in range(len(seq_lens) % batch_size): - seq_len = seq_lens[num_full_batches * batch_size + j] - self.assertAllEqual(result[j, :seq_len], [seq_len] * seq_len) - self.assertAllEqual(result[j, seq_len:], - [0] * (padded_len - seq_len)) - - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testPaddedBatchShortPadding(self): - iterator = ( - dataset_ops.Dataset.from_tensor_slices([6, 5, 5, 5, 5]) - .map(lambda x: array_ops.fill([x], x)).padded_batch( - batch_size=4, padded_shapes=[5]).make_one_shot_iterator()) - get_next = iterator.get_next() - - with self.cached_session() as sess: - with self.assertRaises(errors.DataLossError): - sess.run(get_next) - - def testPaddedBatchEmptyTensors(self): - iterator = ( - dataset_ops.Dataset.from_tensor_slices([0, 0, 0, 0]) - .map(lambda x: array_ops.fill([x], x)).padded_batch( - batch_size=4, padded_shapes=[-1]).make_one_shot_iterator()) - get_next = iterator.get_next() - - with self.cached_session() as sess: - result = sess.run(get_next) - self.assertAllEqual([[], [], [], []], result) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testPaddedBatchDatasetNonDefaultPadding(self): - seq_lens = array_ops.placeholder(dtypes.int32, shape=[None]) - padded_shape = array_ops.placeholder(dtypes.int64, shape=[1]) - - def fill_tuple(x): - filled = array_ops.fill([x], x) - return (filled, string_ops.as_string(filled)) - - iterator = ( - dataset_ops.Dataset.from_tensor_slices(seq_lens).map(fill_tuple) - .padded_batch( - 4, - padded_shapes=(padded_shape, padded_shape), - padding_values=(-1, '')).make_initializable_iterator()) - - init_op = iterator.initializer - get_next = iterator.get_next() - - with self.cached_session() as sess: - # Test with random sequence lengths, and max padding. - random_seq_lens = np.random.randint(20, size=(32,)).astype(np.int32) - sess.run( - init_op, feed_dict={ - padded_shape: [-1], - seq_lens: random_seq_lens - }) - for i in range(8): - result = sess.run(get_next) - padded_len = np.max(result[0]) - self.assertEqual((4, padded_len), result[0].shape) - self.assertEqual((4, padded_len), result[1].shape) - for j in range(4): - seq_len = random_seq_lens[(i * 4) + j] - self.assertAllEqual(result[0][j, :seq_len], [seq_len] * seq_len) - self.assertAllEqual(result[0][j, seq_len:], - [-1] * (padded_len - seq_len)) - self.assertAllEqual(result[1][j, :seq_len], - [compat.as_bytes(str(seq_len))] * seq_len) - self.assertAllEqual(result[1][j, seq_len:], - [b''] * (padded_len - seq_len)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testPaddedBatchDatasetUnicode(self): - # See GitHub issue 16149 - def generator(): - data = [[u'Простой', u'тест', u'юникода'], - [u'никогда', u'не', u'бывает', u'простым']] - - for seq in data: - yield seq, [0, 1, 2, 3] - - dataset = dataset_ops.Dataset.from_generator( - generator, (dtypes.string, dtypes.int32), - (tensor_shape.TensorShape([None]), tensor_shape.TensorShape([None]))) - padded_dataset = dataset.padded_batch( - 2, padded_shapes=([None], [None]), padding_values=('', 0)) - with self.cached_session() as sess: - next_element = padded_dataset.make_one_shot_iterator().get_next() - sess.run(next_element) - - def testPaddedBatchDatasetShapeSpecifications(self): - int_placeholder = array_ops.placeholder(dtypes.int32) - float_placeholder = array_ops.placeholder(dtypes.float32) - string_placeholder = array_ops.placeholder(dtypes.string) - input_dataset = dataset_ops.Dataset.from_tensors( - (int_placeholder, float_placeholder, string_placeholder)) - - # Test different ways of specifying the `padded_shapes` argument. - dynamic_padding_from_tensor_shapes = input_dataset.padded_batch( - 32, - padded_shapes=(tensor_shape.TensorShape([None]), - tensor_shape.TensorShape([None, None]), - tensor_shape.TensorShape([37]))) - dynamic_padding_from_lists = input_dataset.padded_batch( - 32, padded_shapes=([None], [None, None], [37])) - dynamic_padding_from_lists_with_minus_one = input_dataset.padded_batch( - 32, padded_shapes=([-1], [-1, -1], [37])) - dynamic_padding_from_tensors = input_dataset.padded_batch( - 32, - padded_shapes=(constant_op.constant([-1], dtype=dtypes.int64), - constant_op.constant([-1, -1], dtype=dtypes.int64), - constant_op.constant([37], dtype=dtypes.int64))) - - for dataset in [ - dynamic_padding_from_tensor_shapes, dynamic_padding_from_lists, - dynamic_padding_from_lists_with_minus_one, dynamic_padding_from_tensors - ]: - self.assertEqual([None, None], dataset.output_shapes[0].as_list()) - self.assertEqual([None, None, None], dataset.output_shapes[1].as_list()) - self.assertEqual([None, 37], dataset.output_shapes[2].as_list()) - - def testPaddedBatchSparseError(self): - - def _map_fn(i): - return sparse_tensor.SparseTensorValue( - indices=[[0, 0]], values=(i * [1]), dense_shape=[1, 1]), i - - with self.assertRaises(TypeError): - _ = dataset_ops.Dataset.range(10).map(_map_fn).padded_batch(10) - - def testPaddedBatchShapeError(self): - with self.assertRaisesRegexp( - ValueError, r'The padded shape \(1,\) is not compatible with the ' - r'corresponding input component shape \(\).'): - _ = dataset_ops.Dataset.range(10).padded_batch(5, padded_shapes=[1]) - - with self.assertRaisesRegexp( - ValueError, r'The padded shape \(1,\) is not compatible with the ' - r'corresponding input component shape \(3,\).'): - _ = dataset_ops.Dataset.from_tensors([1, 2, 3]).padded_batch( - 5, padded_shapes=[1]) - - with self.assertRaisesRegexp( - ValueError, r'Padded shape .* must be a 1-D tensor ' - r'of tf.int64 values, but its shape was \(2, 2\).'): - _ = dataset_ops.Dataset.from_tensors([1, 2, 3]).padded_batch( - 5, padded_shapes=[[1, 1], [1, 1]]) - - with self.assertRaisesRegexp( - TypeError, r'Padded shape .* must be a 1-D tensor ' - r'of tf.int64 values, but its element type was float32.'): - _ = dataset_ops.Dataset.from_tensors([1, 2, 3]).padded_batch( - 5, padded_shapes=constant_op.constant([1., 2., 3.])) - - with self.assertRaisesRegexp( - ValueError, r'The padded shape \(1,\) is not compatible with the ' - r'corresponding input component shape \(\).'): - shape_as_tensor = constant_op.constant([1], dtype=dtypes.int64) - _ = dataset_ops.Dataset.range(10).padded_batch( - 5, padded_shapes=shape_as_tensor) - - with self.assertRaisesRegexp( - ValueError, - r'The padded shape \((\?|None), (\?|None)\) is not compatible with the ' - r'corresponding input component shape \(\).'): - shape_as_tensor = array_ops.placeholder(dtypes.int64, shape=[2]) - _ = dataset_ops.Dataset.range(10).padded_batch( - 5, padded_shapes=shape_as_tensor) - - -class BatchDatasetBenchmark(test.Benchmark): - - def benchmarkBatchSparse(self): - non_zeros_per_row_values = [0, 1, 5, 10, 100] - batch_size_values = [1, 32, 64, 128, 1024] - - sparse_placeholder = array_ops.sparse_placeholder(dtype=dtypes.int64) - batch_size_placeholder = array_ops.placeholder(dtype=dtypes.int64, shape=[]) - - dataset = dataset_ops.Dataset.from_tensors(sparse_placeholder).repeat( - ).batch(batch_size_placeholder) - iterator = dataset.make_initializable_iterator() - next_element = iterator.get_next() - - for non_zeros_per_row in non_zeros_per_row_values: - - sparse_value = sparse_tensor.SparseTensorValue( - indices=np.arange(non_zeros_per_row, dtype=np.int64)[:, np.newaxis], - values=np.arange(non_zeros_per_row, dtype=np.int64), - dense_shape=[1000]) - - for batch_size in batch_size_values: - - with session.Session() as sess: - sess.run(iterator.initializer, feed_dict={ - sparse_placeholder: sparse_value, - batch_size_placeholder: batch_size}) - # Run five steps to warm up the session caches before taking the - # first measurement. - for _ in range(5): - sess.run(next_element.indices.op) - deltas = [] - for _ in range(100): - start = time.time() - for _ in range(100): - sess.run(next_element.indices.op) - end = time.time() - deltas.append(end - start) - - median_wall_time = np.median(deltas) / 100.0 - - print('Batch sparse dataset non-zeros per row: %d batch_size: %d ' - 'wall time: %f' - % (non_zeros_per_row, batch_size, median_wall_time)) - self.report_benchmark( - iters=10000, wall_time=median_wall_time, - name='benchmark_batch_sparse_dataset_nnz_%d_batch_size_%d' % ( - non_zeros_per_row, batch_size)) - - -if __name__ == '__main__': - test.main() diff --git a/tensorflow/python/data/kernel_tests/batch_test.py b/tensorflow/python/data/kernel_tests/batch_test.py new file mode 100644 index 0000000000..5b035e5917 --- /dev/null +++ b/tensorflow/python/data/kernel_tests/batch_test.py @@ -0,0 +1,173 @@ +# -*- coding: utf-8 -*- +# 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. +# ============================================================================== +"""Tests for `tf.data.Dataset.batch()`.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from absl.testing import parameterized +import numpy as np + +from tensorflow.python.data.kernel_tests import test_base +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.data.util import nest +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import errors +from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import test_util +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.platform import test + + +@test_util.run_all_in_graph_and_eager_modes +class BatchTest(test_base.DatasetTestBase, parameterized.TestCase): + + @parameterized.named_parameters( + ('even', 28, 14, False), + ('uneven_with_remainder', 28, 15, False), + ('uneven_without_remainder', 28, 15, True), + ('empty', 0, 14, False), + ) + def testBatchDataset(self, count, batch_size, drop_remainder): + """Tests the batch dataset logic for various input configurations. + + Args: + count: the number of input elements + batch_size: the batch size + drop_remainder: whether a smaller batch size should be produced if batch + size does not divide number of inputs evenly + """ + + # The pipeline is TensorSliceDataset -> MapDataset(square_3) -> + # RepeatDataset(count) -> BatchDataset(batch_size). + components = (np.arange(7), + np.array([[1, 2, 3]]) * np.arange(7)[:, np.newaxis], + np.array(37.0) * np.arange(7)) + + def _map_fn(x, y, z): + return math_ops.square(x), math_ops.square(y), math_ops.square(z) + + dataset = dataset_ops.Dataset.from_tensor_slices(components).map( + _map_fn).repeat(count).batch(batch_size, drop_remainder) + get_next = self.getNext(dataset) + + if drop_remainder: + dim0 = batch_size + else: + dim0 = None + self.assertEqual( + [ts.as_list() for ts in nest.flatten(dataset.output_shapes)], + [[dim0] + list(c.shape[1:]) for c in components]) + + num_full_batches = (count * 7) // batch_size + for i in range(num_full_batches): + result = self.evaluate(get_next()) + for component, result_component in zip(components, result): + for j in range(batch_size): + self.assertAllEqual(component[(i * batch_size + j) % 7]**2, + result_component[j]) + if not drop_remainder and (count * 7) % batch_size > 0: + result = self.evaluate(get_next()) + for component, result_component in zip(components, result): + for j in range((count * 7) % batch_size): + self.assertAllEqual( + component[(num_full_batches * batch_size + j) % 7]**2, + result_component[j]) + with self.assertRaises(errors.OutOfRangeError): + result = self.evaluate(get_next()) + + def testBatchDatasetInvalidBatchSize(self): + dataset = (dataset_ops.Dataset.range(10).batch(0)) + self.assertDatasetProduces( + dataset, expected_error=(errors.InvalidArgumentError, '')) + + def testBatchSparse(self): + + def _sparse(i): + return sparse_tensor.SparseTensorValue( + indices=[[0]], values=(i * [1]), dense_shape=[1]) + + dataset = dataset_ops.Dataset.range(10).map(_sparse).batch(5) + expected_output = [ + sparse_tensor.SparseTensorValue( + indices=[[0, 0], [1, 0], [2, 0], [3, 0], [4, 0]], + values=[i * 5, i * 5 + 1, i * 5 + 2, i * 5 + 3, i * 5 + 4], + dense_shape=[5, 1]) for i in range(2) + ] + self.assertDatasetProduces(dataset, expected_output=expected_output) + + def testBatchSparseWithDifferentDenseShapes(self): + + def _sparse(i): + return sparse_tensor.SparseTensorValue( + indices=array_ops.expand_dims( + math_ops.range(i, dtype=dtypes.int64), 1), + values=array_ops.fill([math_ops.to_int32(i)], i), + dense_shape=[i]) + + dataset = dataset_ops.Dataset.range(10).map(_sparse).batch(5) + expected_output = [] + for i in range(2): + expected_indices = [] + expected_outputs = [] + for j in range(5): + for k in range(i * 5 + j): + expected_indices.append([j, k]) + expected_outputs.append(i * 5 + j) + expected_output.append( + sparse_tensor.SparseTensorValue( + indices=expected_indices, + values=expected_outputs, + dense_shape=[5, (i + 1) * 5 - 1])) + self.assertDatasetProduces(dataset, expected_output=expected_output) + + def testNestedBatchSparse(self): + + def _sparse(i): + return sparse_tensor.SparseTensorValue( + indices=[[0]], values=(i * [1]), dense_shape=[1]) + + dataset = dataset_ops.Dataset.range(10).map(_sparse).batch(5).batch(2) + expected_output = [ + sparse_tensor.SparseTensorValue( + indices=[[0, 0, 0], [0, 1, 0], [0, 2, 0], [0, 3, 0], [0, 4, 0], + [1, 0, 0], [1, 1, 0], [1, 2, 0], [1, 3, 0], [1, 4, 0]], + values=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + dense_shape=[2, 5, 1]) + ] + self.assertDatasetProduces(dataset, expected_output=expected_output) + + def testBatchShapeError(self): + + def generator(): + yield [1.0, 2.0, 3.0] + yield [4.0, 5.0, 6.0] + yield [7.0, 8.0, 9.0, 10.0] + + dataset = ( + dataset_ops.Dataset.from_generator( + generator, dtypes.float32, output_shapes=[None]).batch(3)) + self.assertDatasetProduces( + dataset, + expected_error=( + errors.InvalidArgumentError, + r'Cannot batch tensors with different shapes in component 0. First ' + r'element had shape \[3\] and element 2 had shape \[4\].')) + + +if __name__ == '__main__': + test.main() diff --git a/tensorflow/python/data/kernel_tests/cache_dataset_op_test.py b/tensorflow/python/data/kernel_tests/cache_dataset_op_test.py deleted file mode 100644 index 63625fac03..0000000000 --- a/tensorflow/python/data/kernel_tests/cache_dataset_op_test.py +++ /dev/null @@ -1,318 +0,0 @@ -# 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. -# ============================================================================== -"""Tests for the experimental input pipeline ops.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -from os import path -import shutil -import tempfile - -import numpy as np - -from tensorflow.python.data.kernel_tests import test_base -from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.data.ops import iterator_ops -from tensorflow.python.framework import constant_op -from tensorflow.python.framework import dtypes -from tensorflow.python.framework import errors -from tensorflow.python.framework import ops -from tensorflow.python.ops import array_ops -from tensorflow.python.ops import variables -from tensorflow.python.platform import test - - -class FileCacheDatasetTest(test_base.DatasetTestBase): - - def setUp(self): - self.tmp_dir = tempfile.mkdtemp() - self.cache_prefix = path.join(self.tmp_dir, "cache") - - def tearDown(self): - if self.tmp_dir: - shutil.rmtree(self.tmp_dir, ignore_errors=True) - - def testCacheDatasetPassthrough(self): - components = (np.array([1, 2, 3, 4]), np.array([5, 6, 7, 8]), - np.array([9.0, 10.0, 11.0, 12.0])) - count_placeholder = array_ops.placeholder_with_default( - constant_op.constant(5, dtypes.int64), shape=[]) - filename_placeholder = array_ops.placeholder(dtypes.string, shape=[]) - - repeat_dataset = (dataset_ops.Dataset.from_tensor_slices(components) - .repeat(count_placeholder)) - - cache_dataset = repeat_dataset.cache(filename_placeholder) - - self.assertEqual( - tuple([c.shape[1:] for c in components]), cache_dataset.output_shapes) - - # Create initialization ops for iterators without and with - # caching, respectively. - iterator = iterator_ops.Iterator.from_structure(cache_dataset.output_types, - cache_dataset.output_shapes) - init_fifo_op = iterator.make_initializer(repeat_dataset) - init_cache_op = iterator.make_initializer(cache_dataset) - - get_next = iterator.get_next() - - with self.cached_session() as sess: - # First run without caching to collect the "ground truth". - sess.run(init_fifo_op) - elements = [] - for _ in range(20): - elements.append(sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Assert that the cached dataset has the same elements as the - # "ground truth". - sess.run( - init_cache_op, feed_dict={filename_placeholder: self.cache_prefix}) - cached_elements = [] - for _ in range(20): - cached_elements.append(sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - self.assertAllEqual(elements, cached_elements) - - # Re-initialize with an empty upstream (to throw errors.OutOfRangeError - # if we didn't use the cache). - sess.run( - init_cache_op, - feed_dict={ - count_placeholder: 0, - filename_placeholder: self.cache_prefix - }) - replayed_elements = [] - for _ in range(20): - replayed_elements.append(sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - self.assertEqual(cached_elements, replayed_elements) - - # Re-initialize with an empty upstream and a missing cache file (should - # throw errors.OutOfRangeError immediately). - sess.run( - init_cache_op, - feed_dict={ - count_placeholder: 0, - filename_placeholder: self.cache_prefix + "nonsense" - }) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testConcurrentWriters(self): - components = (np.array([1, 2, 3, 4]), np.array([5, 6, 7, 8]), - np.array([9.0, 10.0, 11.0, 12.0])) - filename_placeholder = array_ops.placeholder(dtypes.string, shape=[]) - - cache_dataset1 = (dataset_ops.Dataset.from_tensor_slices(components) - .cache(filename_placeholder)) - cache_dataset2 = (dataset_ops.Dataset.from_tensor_slices(components) - .cache(filename_placeholder)) - - iterator1 = cache_dataset1.make_initializable_iterator() - iterator2 = cache_dataset2.make_initializable_iterator() - init_cache_op1 = iterator1.initializer - init_cache_op2 = iterator2.initializer - - get_next1 = iterator1.get_next() - get_next2 = iterator2.get_next() - - with self.cached_session() as sess: - sess.run( - init_cache_op1, feed_dict={filename_placeholder: self.cache_prefix}) - sess.run(get_next1) # this should succeed - - sess.run( - init_cache_op2, feed_dict={filename_placeholder: self.cache_prefix}) - with self.assertRaises(errors.AlreadyExistsError): - sess.run(get_next2) - - sess.run(get_next1) # this should continue to succeed - - def testConcurrentReaders(self): - components = (np.array([1, 2, 3, 4]), np.array([5, 6, 7, 8]), - np.array([9.0, 10.0, 11.0, 12.0])) - filename_placeholder = array_ops.placeholder(dtypes.string, shape=[]) - - cache_dataset1 = (dataset_ops.Dataset.from_tensor_slices(components) - .cache(filename_placeholder)) - cache_dataset2 = (dataset_ops.Dataset.from_tensor_slices(components) - .cache(filename_placeholder)) - - iterator1 = cache_dataset1.make_initializable_iterator() - iterator2 = cache_dataset2.make_initializable_iterator() - init_cache_op1 = iterator1.initializer - init_cache_op2 = iterator2.initializer - - get_next1 = iterator1.get_next() - get_next2 = iterator2.get_next() - - with self.cached_session() as sess: - sess.run( - init_cache_op1, feed_dict={filename_placeholder: self.cache_prefix}) - elements = [] - for _ in range(4): - elements.append(sess.run(get_next1)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next1) - - # Re-initialize - sess.run( - init_cache_op1, feed_dict={filename_placeholder: self.cache_prefix}) - sess.run( - init_cache_op2, feed_dict={filename_placeholder: self.cache_prefix}) - - # Reading concurrently should succeed. - elements_itr1 = [] - elements_itr2 = [] - elements_itr2.append(sess.run(get_next2)) - elements_itr1.append(sess.run(get_next1)) - elements_itr2.append(sess.run(get_next2)) - elements_itr1.append(sess.run(get_next1)) - # Intentionally reversing the order - elements_itr1.append(sess.run(get_next1)) - elements_itr2.append(sess.run(get_next2)) - elements_itr1.append(sess.run(get_next1)) - elements_itr2.append(sess.run(get_next2)) - - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next2) - - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next1) - - self.assertAllEqual(elements, elements_itr1) - self.assertAllEqual(elements, elements_itr2) - - -class MemoryCacheDatasetTest(test_base.DatasetTestBase): - - def testCacheDatasetPassthrough(self): - with ops.device("cpu:0"): - repeat_count = variables.Variable(constant_op.constant(10, dtypes.int64)) - dataset = dataset_ops.Dataset.range(3).flat_map( - lambda x: dataset_ops.Dataset.from_tensors(x).repeat(repeat_count)) - - cached_dataset = dataset.cache().repeat(2) - uncached_dataset = dataset.repeat(2) - - # Needs to be initializable to capture the variable. - cached_iterator = cached_dataset.make_initializable_iterator() - cached_next = cached_iterator.get_next() - uncached_iterator = uncached_dataset.make_initializable_iterator() - uncached_next = uncached_iterator.get_next() - - with self.cached_session() as sess: - - sess.run(repeat_count.initializer) - sess.run(cached_iterator.initializer) - sess.run(uncached_iterator.initializer) - - for i in range(3): - for _ in range(10): - self.assertEqual(sess.run(cached_next), i) - self.assertEqual(sess.run(uncached_next), i) - - sess.run(repeat_count.assign(0)) - - # The uncached iterator should now be empty. - with self.assertRaises(errors.OutOfRangeError): - sess.run(uncached_next) - - # The cached iterator replays from cache. - for i in range(3): - for _ in range(10): - self.assertEqual(sess.run(cached_next), i) - - # The cached iterator should now be empty. - with self.assertRaises(errors.OutOfRangeError): - sess.run(cached_next) - - def testEmptyCacheReading(self): - components = (np.array([1, 2, 3, 4]), np.array([5, 6, 7, 8]), - np.array([9.0, 10.0, 11.0, 12.0])) - count_placeholder = array_ops.placeholder_with_default( - constant_op.constant(5, dtypes.int64), shape=[]) - - repeat_dataset = (dataset_ops.Dataset.from_tensor_slices(components) - .repeat(count_placeholder)) - - cache_dataset = repeat_dataset.cache() - - # Create initialization ops for iterators without and with - # caching, respectively. - iterator = cache_dataset.make_initializable_iterator() - init_cache_op = iterator.initializer - - get_next = iterator.get_next() - - with self.cached_session() as sess: - # Initialize with an empty upstream and a missing cache file (should - # throw errors.OutOfRangeError immediately). - sess.run(init_cache_op, feed_dict={count_placeholder: 0}) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testConcurrentReaders(self): - count_placeholder = array_ops.placeholder_with_default( - constant_op.constant(5, dtypes.int64), shape=[]) - dataset = dataset_ops.Dataset.range(count_placeholder).cache() - d1 = dataset.map(lambda x: x + 1) - d2 = dataset.map(lambda x: x + 6) - - i1 = d1.make_initializable_iterator() - i2 = d2.make_initializable_iterator() - - with self.cached_session() as sess: - sess.run(i1.initializer) - - self.assertEqual(1, sess.run(i1.get_next())) - self.assertEqual(2, sess.run(i1.get_next())) - self.assertEqual(3, sess.run(i1.get_next())) - - sess.run(i2.initializer, feed_dict={count_placeholder: 3}) - - self.assertEqual(6, sess.run(i2.get_next())) - self.assertEqual(7, sess.run(i2.get_next())) - self.assertEqual(4, sess.run(i1.get_next())) # interleave execution - self.assertEqual([8, 5], sess.run([i2.get_next(), i1.get_next()])) - - with self.assertRaises(errors.OutOfRangeError): - sess.run(i1.get_next()) - with self.assertRaises(errors.OutOfRangeError): - sess.run(i2.get_next()) - - def testCacheTakeRepeat(self): - dataset = dataset_ops.Dataset.range(10).cache().take(5).repeat(2) - itr = dataset.make_one_shot_iterator() - n = itr.get_next() - - expected_values = [0, 1, 2, 3, 4, 0, 1, 2, 3, 4] - - with self.cached_session() as sess: - for i, expected in enumerate(expected_values): - self.assertEqual(expected, sess.run(n), - "Unexpected value at index %s" % i) - - with self.assertRaises(errors.OutOfRangeError): - sess.run(itr.get_next()) - - -if __name__ == "__main__": - test.main() diff --git a/tensorflow/python/data/kernel_tests/cache_test.py b/tensorflow/python/data/kernel_tests/cache_test.py new file mode 100644 index 0000000000..b561cd58ba --- /dev/null +++ b/tensorflow/python/data/kernel_tests/cache_test.py @@ -0,0 +1,253 @@ +# 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. +# ============================================================================== +"""Tests for `tf.data.Dataset.cache()`.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from os import path +import shutil +import tempfile + +import numpy as np + +from tensorflow.python.data.kernel_tests import test_base +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import errors +from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util +from tensorflow.python.ops import variables +from tensorflow.python.platform import test + + +@test_util.run_all_in_graph_and_eager_modes +class FileCacheTest(test_base.DatasetTestBase): + + def setUp(self): + self.tmp_dir = tempfile.mkdtemp() + self.cache_prefix = path.join(self.tmp_dir, "cache") + + def tearDown(self): + if self.tmp_dir: + shutil.rmtree(self.tmp_dir, ignore_errors=True) + + def testCacheDatasetPassthrough(self): + components = (np.array([1, 2, 3, 4]), np.array([5, 6, 7, 8]), + np.array([9.0, 10.0, 11.0, 12.0])) + + def dataset_fn(count=5, filename=None): + repeat_dataset = ( + dataset_ops.Dataset.from_tensor_slices(components).repeat(count)) + if filename: + return repeat_dataset.cache(filename) + else: + return repeat_dataset + + self.assertEqual( + tuple([c.shape[1:] for c in components]), + dataset_fn().output_shapes) + + get_next = self.getNext(dataset_fn()) + + # First run without caching to collect the "ground truth". + elements = [] + for _ in range(20): + elements.append(self.evaluate(get_next())) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + + # Assert that the cached dataset has the same elements as the + # "ground truth". + get_next = self.getNext(dataset_fn(filename=self.cache_prefix)) + cached_elements = [] + for _ in range(20): + cached_elements.append(self.evaluate(get_next())) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + self.assertAllEqual(elements, cached_elements) + + # Re-initialize with an empty upstream (to throw errors.OutOfRangeError + # if we didn't use the cache). + get_next = self.getNext(dataset_fn(count=0, filename=self.cache_prefix)) + replayed_elements = [] + for _ in range(20): + replayed_elements.append(self.evaluate(get_next())) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + self.assertEqual(cached_elements, replayed_elements) + + # Re-initialize with an empty upstream and a missing cache file (should + # throw errors.OutOfRangeError immediately). + get_next = self.getNext( + dataset_fn(count=0, filename=self.cache_prefix + "nonsense")) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + + def testConcurrentWriters(self): + components = (np.array([1, 2, 3, 4]), np.array([5, 6, 7, 8]), + np.array([9.0, 10.0, 11.0, 12.0])) + + cache_dataset1 = ( + dataset_ops.Dataset.from_tensor_slices(components).cache( + self.cache_prefix)) + cache_dataset2 = ( + dataset_ops.Dataset.from_tensor_slices(components).cache( + self.cache_prefix)) + + get_next1 = self.getNext(cache_dataset1) + get_next2 = self.getNext(cache_dataset2) + + self.evaluate(get_next1()) # this should succeed + + with self.assertRaises(errors.AlreadyExistsError): + self.evaluate(get_next2()) + + self.evaluate(get_next1()) # this should continue to succeed + + def testConcurrentReaders(self): + components = (np.array([1, 2, 3, 4]), np.array([5, 6, 7, 8]), + np.array([9.0, 10.0, 11.0, 12.0])) + + cache_dataset1 = ( + dataset_ops.Dataset.from_tensor_slices(components).cache( + self.cache_prefix)) + cache_dataset2 = ( + dataset_ops.Dataset.from_tensor_slices(components).cache( + self.cache_prefix)) + + get_next1 = self.getNext(cache_dataset1) + get_next2 = self.getNext(cache_dataset2) + + elements = [] + for _ in range(4): + elements.append(self.evaluate(get_next1())) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next1()) + + # Re-initialize + get_next1 = self.getNext(cache_dataset1) + get_next2 = self.getNext(cache_dataset2) + + # Reading concurrently should succeed. + elements_itr1 = [] + elements_itr2 = [] + elements_itr2.append(self.evaluate(get_next2())) + elements_itr1.append(self.evaluate(get_next1())) + elements_itr2.append(self.evaluate(get_next2())) + elements_itr1.append(self.evaluate(get_next1())) + # Intentionally reversing the order + elements_itr1.append(self.evaluate(get_next1())) + elements_itr2.append(self.evaluate(get_next2())) + elements_itr1.append(self.evaluate(get_next1())) + elements_itr2.append(self.evaluate(get_next2())) + + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next2()) + + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next1()) + + self.assertAllEqual(elements, elements_itr1) + self.assertAllEqual(elements, elements_itr2) + + +@test_util.run_all_in_graph_and_eager_modes +class MemoryCacheTest(test_base.DatasetTestBase): + + def testCacheDatasetPassthrough(self): + with ops.device("cpu:0"): + repeat_count = variables.Variable(constant_op.constant(10, dtypes.int64)) + dataset = dataset_ops.Dataset.range(3).flat_map( + lambda x: dataset_ops.Dataset.from_tensors(x).repeat(repeat_count)) + + cached_dataset = dataset.cache().repeat(2) + uncached_dataset = dataset.repeat(2) + + self.evaluate(repeat_count.initializer) + # Needs to be initializable to capture the variable. + cached_next = self.getNext(cached_dataset, requires_initialization=True) + uncached_next = self.getNext( + uncached_dataset, requires_initialization=True) + for i in range(3): + for _ in range(10): + self.assertEqual(self.evaluate(cached_next()), i) + self.assertEqual(self.evaluate(uncached_next()), i) + + self.evaluate(repeat_count.assign(0)) + + # The uncached iterator should now be empty. + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(uncached_next()) + + # The cached iterator replays from cache. + for i in range(3): + for _ in range(10): + self.assertEqual(self.evaluate(cached_next()), i) + + # The cached iterator should now be empty. + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(cached_next()) + + def testEmptyCacheReading(self): + components = (np.array([1, 2, 3, 4]), np.array([5, 6, 7, 8]), + np.array([9.0, 10.0, 11.0, 12.0])) + + repeat_dataset = ( + dataset_ops.Dataset.from_tensor_slices(components).repeat(0)) + cache_dataset = repeat_dataset.cache() + + # Create initialization ops for iterators without and with + # caching, respectively. + self.assertDatasetProduces(cache_dataset, expected_output=[]) + + def testConcurrentReaders(self): + + dataset = dataset_ops.Dataset.range(5).cache() + d1 = dataset.map(lambda x: x + 1) + d2 = dataset.map(lambda x: x + 6) + + get_next1 = self.getNext(d1) + + self.assertEqual(1, self.evaluate(get_next1())) + self.assertEqual(2, self.evaluate(get_next1())) + self.assertEqual(3, self.evaluate(get_next1())) + + get_next2 = self.getNext(d2) + + self.assertEqual(6, self.evaluate(get_next2())) + self.assertEqual(7, self.evaluate(get_next2())) + self.assertEqual(4, self.evaluate(get_next1())) # interleave execution + self.assertEqual([8, 5], + [self.evaluate(get_next2()), + self.evaluate(get_next1())]) + self.assertEqual(9, self.evaluate(get_next2())) + self.assertEqual(10, self.evaluate(get_next2())) + + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next2()) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next1()) + + def testCacheTakeRepeat(self): + dataset = dataset_ops.Dataset.range(10).cache().take(5).repeat(2) + + expected_output = [0, 1, 2, 3, 4, 0, 1, 2, 3, 4] + self.assertDatasetProduces(dataset, expected_output=expected_output) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/data/kernel_tests/concatenate_dataset_op_test.py b/tensorflow/python/data/kernel_tests/concatenate_test.py similarity index 75% rename from tensorflow/python/data/kernel_tests/concatenate_dataset_op_test.py rename to tensorflow/python/data/kernel_tests/concatenate_test.py index 83af31f380..5d8bfdc8f3 100644 --- a/tensorflow/python/data/kernel_tests/concatenate_dataset_op_test.py +++ b/tensorflow/python/data/kernel_tests/concatenate_test.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Tests for the experimental input pipeline ops.""" +"""Tests for `tf.data.Dataset.concatenate().""" from __future__ import absolute_import from __future__ import division from __future__ import print_function @@ -24,10 +24,12 @@ from tensorflow.python.data.ops import dataset_ops from tensorflow.python.data.util import nest from tensorflow.python.framework import errors from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util from tensorflow.python.platform import test -class ConcatenateDatasetTest(test_base.DatasetTestBase): +@test_util.run_all_in_graph_and_eager_modes +class ConcatenateTest(test_base.DatasetTestBase): def testConcatenateDataset(self): input_components = ( @@ -46,23 +48,19 @@ class ConcatenateDatasetTest(test_base.DatasetTestBase): self.assertEqual(concatenated.output_shapes, (tensor_shape.TensorShape( [20]), tensor_shape.TensorShape([15]), tensor_shape.TensorShape([]))) - iterator = concatenated.make_initializable_iterator() - init_op = iterator.initializer - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(init_op) - for i in range(9): - result = sess.run(get_next) - if i < 4: - for component, result_component in zip(input_components, result): - self.assertAllEqual(component[i], result_component) - else: - for component, result_component in zip(to_concatenate_components, - result): - self.assertAllEqual(component[i - 4], result_component) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + get_next = self.getNext(concatenated) + + for i in range(9): + result = self.evaluate(get_next()) + if i < 4: + for component, result_component in zip(input_components, result): + self.assertAllEqual(component[i], result_component) + else: + for component, result_component in zip(to_concatenate_components, + result): + self.assertAllEqual(component[i - 4], result_component) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) def testConcatenateDatasetDifferentShape(self): input_components = ( @@ -79,24 +77,18 @@ class ConcatenateDatasetTest(test_base.DatasetTestBase): self.assertEqual( [ts.as_list() for ts in nest.flatten(concatenated.output_shapes)], [[20], [None]]) - - iterator = concatenated.make_initializable_iterator() - init_op = iterator.initializer - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(init_op) - for i in range(9): - result = sess.run(get_next) - if i < 4: - for component, result_component in zip(input_components, result): - self.assertAllEqual(component[i], result_component) - else: - for component, result_component in zip(to_concatenate_components, - result): - self.assertAllEqual(component[i - 4], result_component) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + get_next = self.getNext(concatenated) + for i in range(9): + result = self.evaluate(get_next()) + if i < 4: + for component, result_component in zip(input_components, result): + self.assertAllEqual(component[i], result_component) + else: + for component, result_component in zip(to_concatenate_components, + result): + self.assertAllEqual(component[i - 4], result_component) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) def testConcatenateDatasetDifferentStructure(self): input_components = ( diff --git a/tensorflow/python/data/kernel_tests/range_dataset_op_test.py b/tensorflow/python/data/kernel_tests/dataset_checkpoint_test.py similarity index 84% rename from tensorflow/python/data/kernel_tests/range_dataset_op_test.py rename to tensorflow/python/data/kernel_tests/dataset_checkpoint_test.py index b71e6b2ea4..6dcd94ea02 100644 --- a/tensorflow/python/data/kernel_tests/range_dataset_op_test.py +++ b/tensorflow/python/data/kernel_tests/dataset_checkpoint_test.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Test RangeDataset.""" +"""Checkpoint tests for `tf.data.Dataset`.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function @@ -26,7 +26,6 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape -from tensorflow.python.framework import test_util from tensorflow.python.ops import gen_dataset_ops from tensorflow.python.ops import io_ops from tensorflow.python.ops import parsing_ops @@ -35,51 +34,7 @@ from tensorflow.python.platform import gfile from tensorflow.python.platform import test -@test_util.run_all_in_graph_and_eager_modes -class RangeDatasetTest(test_base.DatasetTestBase): - - def testStop(self): - dataset = dataset_ops.Dataset.range(5) - self.assertDatasetProduces(dataset, expected_output=range(5)) - - def testStartStop(self): - start, stop = 2, 5 - dataset = dataset_ops.Dataset.range(start, stop) - self.assertDatasetProduces(dataset, expected_output=range(2, 5)) - - def testStartStopStep(self): - start, stop, step = 2, 10, 2 - dataset = dataset_ops.Dataset.range(start, stop, step) - self.assertDatasetProduces(dataset, expected_output=range(2, 10, 2)) - - def testZeroStep(self): - start, stop, step = 2, 10, 0 - dataset = dataset_ops.Dataset.range(start, stop, step) - self.assertDatasetProduces( - dataset, expected_err=(errors.InvalidArgumentError, "")) - - def testNegativeStep(self): - start, stop, step = 2, 10, -1 - dataset = dataset_ops.Dataset.range(start, stop, step) - self.assertDatasetProduces(dataset, expected_output=range(2, 10, -1)) - - def testStopLessThanStart(self): - start, stop = 10, 2 - dataset = dataset_ops.Dataset.range(start, stop) - self.assertDatasetProduces(dataset, expected_output=range(10, 2)) - - def testStopLessThanStartWithPositiveStep(self): - start, stop, step = 10, 2, 2 - dataset = dataset_ops.Dataset.range(start, stop, step) - self.assertDatasetProduces(dataset, expected_output=range(10, 2, 2)) - - def testStopLessThanStartWithNegativeStep(self): - start, stop, step = 10, 2, -1 - dataset = dataset_ops.Dataset.range(start, stop, step) - self.assertDatasetProduces(dataset, expected_output=range(10, 2, -1)) - - -class ExperimentalCheckpointDatasetTest(test_base.DatasetTestBase): +class DatasetCheckpointTest(test_base.DatasetTestBase): def tearDown(self): # Remove all checkpoint files. @@ -109,8 +64,8 @@ class ExperimentalCheckpointDatasetTest(test_base.DatasetTestBase): def testSaveRestore(self): def _build_graph(start, stop): - iterator = dataset_ops.Dataset.range(start, - stop).make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.range(start, stop)) init_op = iterator.initializer get_next = iterator.get_next() save_op = self._save_op(iterator._iterator_resource) @@ -159,7 +114,7 @@ class ExperimentalCheckpointDatasetTest(test_base.DatasetTestBase): def _build_graph(start, stop, num_epochs): dataset = dataset_ops.Dataset.range(start, stop).repeat(num_epochs) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) init_op = iterator.initializer get_next = iterator.get_next() save_op = self._save_op(iterator._iterator_resource) @@ -206,7 +161,7 @@ class ExperimentalCheckpointDatasetTest(test_base.DatasetTestBase): def _build_graph(start, stop): dataset = dataset_ops.Dataset.range(start, stop) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) init_op = iterator.initializer get_next = iterator.get_next() save_op = self._save_op(iterator._iterator_resource) @@ -245,7 +200,7 @@ class ExperimentalCheckpointDatasetTest(test_base.DatasetTestBase): def _build_graph(start, stop): dataset = dataset_ops.Dataset.range(start, stop) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) init_op = iterator.initializer get_next = iterator.get_next() save_op = self._save_op(iterator._iterator_resource) @@ -278,8 +233,8 @@ class ExperimentalCheckpointDatasetTest(test_base.DatasetTestBase): def testMultipleSaves(self): def _build_graph(start, stop): - iterator = dataset_ops.Dataset.range(start, - stop).make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.range(start, stop)) init_op = iterator.initializer get_next = iterator.get_next() save_op = self._save_op(iterator._iterator_resource) @@ -321,8 +276,8 @@ class ExperimentalCheckpointDatasetTest(test_base.DatasetTestBase): def testSaveRestoreWithRepeat(self): def _build_graph(start, stop, num_epochs): - iterator = dataset_ops.Dataset.range( - start, stop).repeat(num_epochs).make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.range(start, stop).repeat(num_epochs)) init_op = iterator.initializer get_next = iterator.get_next() save_op = self._save_op(iterator._iterator_resource) @@ -366,8 +321,8 @@ class ExperimentalCheckpointDatasetTest(test_base.DatasetTestBase): def testSaveRestoreExhaustedIterator(self): def _build_graph(start, stop, num_epochs): - iterator = dataset_ops.Dataset.range( - start, stop).repeat(num_epochs).make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.range(start, stop).repeat(num_epochs)) init_op = iterator.initializer get_next = iterator.get_next() save_op = self._save_op(iterator._iterator_resource) diff --git a/tensorflow/python/data/kernel_tests/dataset_constructor_op_test.py b/tensorflow/python/data/kernel_tests/dataset_constructor_op_test.py deleted file mode 100644 index bc6b36285a..0000000000 --- a/tensorflow/python/data/kernel_tests/dataset_constructor_op_test.py +++ /dev/null @@ -1,650 +0,0 @@ -# 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. -# ============================================================================== -"""Tests for the experimental input pipeline ops.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import time - -import numpy as np - -from tensorflow.core.protobuf import config_pb2 -from tensorflow.python.client import session -from tensorflow.python.data.kernel_tests import test_base -from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.data.util import nest -from tensorflow.python.framework import dtypes -from tensorflow.python.framework import errors -from tensorflow.python.framework import ops -from tensorflow.python.framework import sparse_tensor -from tensorflow.python.framework import tensor_shape -from tensorflow.python.ops import array_ops -from tensorflow.python.ops import math_ops -from tensorflow.python.ops import resource_variable_ops -from tensorflow.python.platform import test - - -class DatasetConstructorTest(test_base.DatasetTestBase): - - def testFromTensors(self): - """Test a dataset that represents a single tuple of tensors.""" - components = (np.array(1), np.array([1, 2, 3]), np.array(37.0)) - - iterator = (dataset_ops.Dataset.from_tensors(components) - .make_initializable_iterator()) - init_op = iterator.initializer - get_next = iterator.get_next() - - self.assertEqual([c.shape for c in components], - [t.shape for t in get_next]) - - with self.cached_session() as sess: - sess.run(init_op) - results = sess.run(get_next) - for component, result_component in zip(components, results): - self.assertAllEqual(component, result_component) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testFromTensorsSparse(self): - """Test a dataset that represents a single tuple of tensors.""" - components = (sparse_tensor.SparseTensorValue( - indices=np.array([[0]]), - values=np.array([0]), - dense_shape=np.array([1])), - sparse_tensor.SparseTensorValue( - indices=np.array([[0, 0], [1, 1]]), - values=np.array([-1, 1]), - dense_shape=np.array([2, 2]))) - - iterator = ( - dataset_ops.Dataset.from_tensors(components) - .make_initializable_iterator()) - init_op = iterator.initializer - get_next = iterator.get_next() - - self.assertEqual( - [tensor_shape.TensorShape(c.dense_shape) for c in components], - [shape for shape in iterator.output_shapes]) - - with self.cached_session() as sess: - sess.run(init_op) - results = sess.run(get_next) - for component, result_component in zip(components, results): - self.assertSparseValuesEqual(component, result_component) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testFromTensorsMixed(self): - """Test an dataset that represents a single tuple of tensors.""" - components = (np.array(1), np.array([1, 2, 3]), np.array(37.0), - sparse_tensor.SparseTensorValue( - indices=np.array([[0]]), - values=np.array([0]), - dense_shape=np.array([1])), - sparse_tensor.SparseTensorValue( - indices=np.array([[0, 0], [1, 1]]), - values=np.array([-1, 1]), - dense_shape=np.array([2, 2]))) - - iterator = ( - dataset_ops.Dataset.from_tensors(components) - .make_initializable_iterator()) - init_op = iterator.initializer - get_next = iterator.get_next() - - self.assertEqual([ - tensor_shape.TensorShape(c.dense_shape) - if sparse_tensor.is_sparse(c) else c.shape for c in components - ], [shape for shape in iterator.output_shapes]) - - with self.cached_session() as sess: - sess.run(init_op) - results = sess.run(get_next) - for component, result_component in zip(components, results): - if sparse_tensor.is_sparse(component): - self.assertSparseValuesEqual(component, result_component) - else: - self.assertAllEqual(component, result_component) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testFromTensorSlices(self): - """Test a dataset that represents the slices from a tuple of tensors.""" - components = ( - np.tile(np.array([[1], [2], [3], [4]]), 20), np.tile( - np.array([[12], [13], [14], [15]]), 22), - np.array([37.0, 38.0, 39.0, 40.0]) - ) - - iterator = (dataset_ops.Dataset.from_tensor_slices(components) - .make_initializable_iterator()) - init_op = iterator.initializer - get_next = iterator.get_next() - - self.assertEqual([c.shape[1:] for c in components], - [t.shape for t in get_next]) - - with self.cached_session() as sess: - sess.run(init_op) - for i in range(4): - results = sess.run(get_next) - for component, result_component in zip(components, results): - self.assertAllEqual(component[i], result_component) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testFromTensorSlicesSparse(self): - """Test a dataset that represents the slices from a tuple of tensors.""" - components = (sparse_tensor.SparseTensorValue( - indices=np.array([[0, 0], [1, 0], [2, 0]]), - values=np.array([0, 0, 0]), - dense_shape=np.array([3, 1])), - sparse_tensor.SparseTensorValue( - indices=np.array([[0, 0], [1, 1], [2, 2]]), - values=np.array([1, 2, 3]), - dense_shape=np.array([3, 3]))) - - iterator = ( - dataset_ops.Dataset.from_tensor_slices(components) - .make_initializable_iterator()) - init_op = iterator.initializer - get_next = iterator.get_next() - - self.assertEqual( - [tensor_shape.TensorShape(c.dense_shape[1:]) for c in components], - [shape for shape in iterator.output_shapes]) - - with self.cached_session() as sess: - sess.run(init_op) - expected = [ - (sparse_tensor.SparseTensorValue( - indices=np.array([[0]]), - values=np.array([0]), - dense_shape=np.array([1])), - sparse_tensor.SparseTensorValue( - indices=np.array([[0]]), - values=np.array([1]), - dense_shape=np.array([3]))), - (sparse_tensor.SparseTensorValue( - indices=np.array([[0]]), - values=np.array([0]), - dense_shape=np.array([1])), - sparse_tensor.SparseTensorValue( - indices=np.array([[1]]), - values=np.array([2]), - dense_shape=np.array([3]))), - (sparse_tensor.SparseTensorValue( - indices=np.array([[0]]), - values=np.array([0]), - dense_shape=np.array([1])), - sparse_tensor.SparseTensorValue( - indices=np.array([[2]]), - values=np.array([3]), - dense_shape=np.array([3]))), - ] - for i in range(3): - results = sess.run(get_next) - for component, result_component in zip(expected[i], results): - self.assertSparseValuesEqual(component, result_component) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testFromTensorSlicesMixed(self): - """Test a dataset that represents the slices from a tuple of tensors.""" - components = (np.tile(np.array([[1], [2], [3]]), 20), - np.tile(np.array([[12], [13], [14]]), 22), - np.array([37.0, 38.0, 39.0]), - sparse_tensor.SparseTensorValue( - indices=np.array([[0, 0], [1, 0], [2, 0]]), - values=np.array([0, 0, 0]), - dense_shape=np.array([3, 1])), - sparse_tensor.SparseTensorValue( - indices=np.array([[0, 0], [1, 1], [2, 2]]), - values=np.array([1, 2, 3]), - dense_shape=np.array([3, 3]))) - - iterator = ( - dataset_ops.Dataset.from_tensor_slices(components) - .make_initializable_iterator()) - init_op = iterator.initializer - get_next = iterator.get_next() - - self.assertEqual([ - tensor_shape.TensorShape(c.dense_shape[1:]) - if sparse_tensor.is_sparse(c) else c.shape[1:] for c in components - ], [shape for shape in iterator.output_shapes]) - - with self.cached_session() as sess: - sess.run(init_op) - expected = [ - (sparse_tensor.SparseTensorValue( - indices=np.array([[0]]), - values=np.array([0]), - dense_shape=np.array([1])), - sparse_tensor.SparseTensorValue( - indices=np.array([[0]]), - values=np.array([1]), - dense_shape=np.array([3]))), - (sparse_tensor.SparseTensorValue( - indices=np.array([[0]]), - values=np.array([0]), - dense_shape=np.array([1])), - sparse_tensor.SparseTensorValue( - indices=np.array([[1]]), - values=np.array([2]), - dense_shape=np.array([3]))), - (sparse_tensor.SparseTensorValue( - indices=np.array([[0]]), - values=np.array([0]), - dense_shape=np.array([1])), - sparse_tensor.SparseTensorValue( - indices=np.array([[2]]), - values=np.array([3]), - dense_shape=np.array([3]))), - ] - for i in range(3): - results = sess.run(get_next) - for component, result_component in zip( - (list(zip(*components[:3]))[i] + expected[i]), results): - if sparse_tensor.is_sparse(component): - self.assertSparseValuesEqual(component, result_component) - else: - self.assertAllEqual(component, result_component) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testFromTensorSlicesWithDict(self): - components = {"foo": [1, 2, 3], "bar": [[4.0], [5.0], [6.0]]} - iterator = (dataset_ops.Dataset.from_tensor_slices(components) - .make_initializable_iterator()) - init_op = iterator.initializer - get_next = iterator.get_next() - - self.assertEqual(dtypes.int32, iterator.output_types["foo"]) - self.assertEqual(dtypes.float32, iterator.output_types["bar"]) - self.assertEqual((), iterator.output_shapes["foo"]) - self.assertEqual((1,), iterator.output_shapes["bar"]) - - with self.cached_session() as sess: - sess.run(init_op) - for i in range(3): - results = sess.run(get_next) - self.assertEqual(components["foo"][i], results["foo"]) - self.assertEqual(components["bar"][i], results["bar"]) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testFromSparseTensorSlices(self): - """Test a dataset based on slices of a `tf.SparseTensor`.""" - st = array_ops.sparse_placeholder(dtypes.float64) - iterator = (dataset_ops.Dataset.from_sparse_tensor_slices(st) - .make_initializable_iterator()) - init_op = iterator.initializer - get_next = sparse_tensor.SparseTensor(*iterator.get_next()) - - with self.cached_session() as sess: - slices = [[1., 2., 3.], [1.], [1.], [1., 2.], [], [1., 2.], [], [], []] - - # Test with sparse tensor in the appropriate order. - indices = np.array( - [[i, j] for i in range(len(slices)) for j in range(len(slices[i]))]) - values = np.array([val for s in slices for val in s]) - dense_shape = np.array([len(slices), max(len(s) for s in slices) + 1]) - sparse_feed = sparse_tensor.SparseTensorValue(indices, values, - dense_shape) - sess.run(init_op, feed_dict={st: sparse_feed}) - for i, s in enumerate(slices): - results = sess.run(get_next) - self.assertAllEqual(s, results.values) - expected_indices = np.array( - [[j] for j in range(len(slices[i]))]).reshape([-1, 1]) - self.assertAllEqual(expected_indices, results.indices) - self.assertAllEqual(dense_shape[1:], results.dense_shape) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Test with sparse tensor in the reverse order, which is not - # currently supported. - reverse_order_indices = indices[::-1, :] - reverse_order_values = values[::-1] - sparse_feed = sparse_tensor.SparseTensorValue( - reverse_order_indices, reverse_order_values, dense_shape) - with self.assertRaises(errors.UnimplementedError): - sess.run(init_op, feed_dict={st: sparse_feed}) - - # Test with an empty sparse tensor. - empty_indices = np.empty((0, 4), dtype=np.int64) - empty_values = np.empty((0,), dtype=np.float64) - empty_dense_shape = [0, 4, 37, 9] - sparse_feed = sparse_tensor.SparseTensorValue(empty_indices, empty_values, - empty_dense_shape) - sess.run(init_op, feed_dict={st: sparse_feed}) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # pylint: disable=g-long-lambda,unnecessary-lambda - def testNestedStructure(self): - components = (np.array([1, 2, 3], dtype=np.int64), - (np.array([4., 5.]), np.array([6., 7.])), - np.array([8, 9, 10], dtype=np.int64)) - - dataset = dataset_ops.Dataset.from_tensors(components) - self.assertEquals((dtypes.int64, (dtypes.float64, dtypes.float64), - dtypes.int64), dataset.output_types) - self.assertEquals(([3], ([2], [2]), [3]), dataset.output_shapes) - - dataset = dataset.shuffle(10, 10) - self.assertEquals((dtypes.int64, (dtypes.float64, dtypes.float64), - dtypes.int64), dataset.output_types) - self.assertEquals(([3], ([2], [2]), [3]), dataset.output_shapes) - - dataset = dataset.repeat(-1) - self.assertEquals((dtypes.int64, (dtypes.float64, dtypes.float64), - dtypes.int64), dataset.output_types) - self.assertEquals(([3], ([2], [2]), [3]), dataset.output_shapes) - - dataset = dataset.filter(lambda x, y, z: True) - self.assertEquals((dtypes.int64, (dtypes.float64, dtypes.float64), - dtypes.int64), dataset.output_types) - self.assertEquals(([3], ([2], [2]), [3]), dataset.output_shapes) - - dataset = dataset.take(5) - self.assertEquals((dtypes.int64, (dtypes.float64, dtypes.float64), - dtypes.int64), dataset.output_types) - self.assertEquals(([3], ([2], [2]), [3]), dataset.output_shapes) - - dataset = dataset.map(lambda x, y, z: ((x, z), (y[0], y[1]))) - self.assertEquals(((dtypes.int64, dtypes.int64), - (dtypes.float64, dtypes.float64)), dataset.output_types) - self.assertEquals((([3], [3]), ([2], [2])), dataset.output_shapes) - - dataset = dataset.flat_map( - lambda x, y: dataset_ops.Dataset.from_tensors(((x[0], x[1]), - (y[0], y[1]))) - ) - self.assertEquals(((dtypes.int64, dtypes.int64), - (dtypes.float64, dtypes.float64)), dataset.output_types) - self.assertEquals((([3], [3]), ([2], [2])), dataset.output_shapes) - - dataset = dataset.batch(32) - self.assertEquals(((dtypes.int64, dtypes.int64), - (dtypes.float64, dtypes.float64)), dataset.output_types) - self.assertEquals((([None, 3], [None, 3]), ([None, 2], [None, 2])), - nest.pack_sequence_as(dataset.output_shapes, [ - s.as_list() - for s in nest.flatten(dataset.output_shapes) - ])) - - iterator = dataset.make_one_shot_iterator() - (w, x), (y, z) = iterator.get_next() - self.assertEquals(dtypes.int64, w.dtype) - self.assertEquals(dtypes.int64, x.dtype) - self.assertEquals(dtypes.float64, y.dtype) - self.assertEquals(dtypes.float64, z.dtype) - self.assertEquals([None, 3], w.shape.as_list()) - self.assertEquals([None, 3], x.shape.as_list()) - self.assertEquals([None, 2], y.shape.as_list()) - self.assertEquals([None, 2], z.shape.as_list()) - - iterator = dataset.make_initializable_iterator() - (w, x), (y, z) = iterator.get_next() - self.assertEquals(dtypes.int64, w.dtype) - self.assertEquals(dtypes.int64, x.dtype) - self.assertEquals(dtypes.float64, y.dtype) - self.assertEquals(dtypes.float64, z.dtype) - self.assertEquals([None, 3], w.shape.as_list()) - self.assertEquals([None, 3], x.shape.as_list()) - self.assertEquals([None, 2], y.shape.as_list()) - self.assertEquals([None, 2], z.shape.as_list()) - - # Define a separate set of components with matching leading - # dimension for the from-slices constructor. - components_for_slices = (np.array([1, 2, 3], dtype=np.int64), - (np.array([4., 5., 6.]), - np.array([7., 8., 9.])), - np.array([10, 11, 12], dtype=np.int64)) - - dataset = dataset_ops.Dataset.from_tensor_slices(components_for_slices) - self.assertEquals((dtypes.int64, (dtypes.float64, dtypes.float64), - dtypes.int64), dataset.output_types) - self.assertEquals(([], ([], []), []), dataset.output_shapes) - - def testNestedDict(self): - components = {"a": {"aa": 1, "ab": [2.0, 2.0]}, "b": [3, 3, 3]} - dataset = dataset_ops.Dataset.from_tensors(components) - self.assertEquals(dtypes.int32, dataset.output_types["a"]["aa"]) - self.assertEquals(dtypes.float32, dataset.output_types["a"]["ab"]) - self.assertEquals(dtypes.int32, dataset.output_types["b"]) - self.assertEquals([], dataset.output_shapes["a"]["aa"]) - self.assertEquals([2], dataset.output_shapes["a"]["ab"]) - self.assertEquals([3], dataset.output_shapes["b"]) - - def testNonSequenceNestedStructure(self): - components = np.array([1, 2, 3], dtype=np.int64) - - dataset = dataset_ops.Dataset.from_tensors(components) - self.assertEquals(dtypes.int64, dataset.output_types) - self.assertEquals([3], dataset.output_shapes) - - dataset = dataset.filter( - lambda x: math_ops.reduce_all(math_ops.equal(x, components))) - self.assertEquals(dtypes.int64, dataset.output_types) - self.assertEquals([3], dataset.output_shapes) - - dataset = dataset.map(lambda x: array_ops.stack([x, x])) - self.assertEquals(dtypes.int64, dataset.output_types) - self.assertEquals([2, 3], dataset.output_shapes) - - dataset = dataset.flat_map( - lambda x: dataset_ops.Dataset.from_tensor_slices(x)) - self.assertEquals(dtypes.int64, dataset.output_types) - self.assertEquals([3], dataset.output_shapes) - - iterator = dataset.make_one_shot_iterator() - get_next = iterator.get_next() - self.assertEquals(dtypes.int64, get_next.dtype) - self.assertEquals([3], get_next.shape) - - def testSplitPipelineFailsWithPlacementError(self): - with session.Session( - target="", - config=config_pb2.ConfigProto(device_count={"CPU": 2})) as sess: - - dataset = dataset_ops.Dataset.from_tensors(0) - - # Define a pipeline that attempts to use variables on two - # different devices. - # - # Initialize the variables before creating to iterator, to avoid the - # placement algorithm overriding the DT_RESOURCE colocation constraints. - with ops.device("/cpu:0"): - var_0 = resource_variable_ops.ResourceVariable(initial_value=0) - dataset = dataset.map(lambda x: x + var_0.read_value()) - sess.run(var_0.initializer) - - with ops.device("/cpu:1"): - var_1 = resource_variable_ops.ResourceVariable(initial_value=0) - dataset = dataset.map(lambda x: x + var_1.read_value()) - sess.run(var_1.initializer) - - iterator = dataset.make_initializable_iterator() - sess.run(iterator.initializer) - - with self.assertRaisesRegexp( - errors.FailedPreconditionError, - "Error while reading resource variable Variable"): - sess.run(iterator.get_next()) - - -class DatasetConstructorBenchmark(test.Benchmark): - - def benchmarkSliceRepeatBatch(self): - input_size = 10000 - batch_size = 100 - num_epochs = 100 - - input_data = np.random.randn(input_size) - - dataset = ( - dataset_ops.Dataset.from_tensor_slices(input_data) - .repeat(num_epochs + 1).batch(batch_size)) - iterator = dataset.make_initializable_iterator() - next_element = iterator.get_next() - - with session.Session() as sess: - sess.run(iterator.initializer) - # Run one whole epoch to burn in the computation. - for _ in range(input_size // batch_size): - sess.run(next_element) - deltas = [] - try: - while True: - start = time.time() - sess.run(next_element) - deltas.append(time.time() - start) - except errors.OutOfRangeError: - pass - - median_wall_time = np.median(deltas) - print("Slice/repeat/batch with sess.run() input size: %d batch size: %d " - "Median wall time per element: %f" % (input_size, batch_size, - median_wall_time)) - self.report_benchmark( - iters=len(deltas), - wall_time=median_wall_time, - name="benchmark_slice_repeat_batch_input_%d_batch_%d" % (input_size, - batch_size)) - - def benchmarkSliceRepeatBatchCallable(self): - input_size = 10000 - batch_size = 100 - num_epochs = 100 - - input_data = np.random.randn(input_size) - - dataset = ( - dataset_ops.Dataset.from_tensor_slices(input_data) - .repeat(num_epochs + 1).batch(batch_size)) - iterator = dataset.make_initializable_iterator() - next_element = iterator.get_next() - - with session.Session() as sess: - sess.run(iterator.initializer) - get_next_element = sess.make_callable(next_element) - # Run one whole epoch to burn in the computation. - for _ in range(input_size // batch_size): - get_next_element() - deltas = [] - try: - while True: - start = time.time() - get_next_element() - deltas.append(time.time() - start) - except errors.OutOfRangeError: - pass - - median_wall_time = np.median(deltas) - print( - "Slice/repeat/batch with callable input size: %d batch size: %d Median" - " wall time per element: %f" % (input_size, batch_size, - median_wall_time)) - self.report_benchmark( - iters=len(deltas), - wall_time=median_wall_time, - name="benchmark_slice_repeat_batch_callable_input_%d_batch_%d" % - (input_size, batch_size)) - - def benchmarkReshapeSliceRepeatCallable(self): - input_size = 10000 - batch_size = 100 - num_epochs = 100 - - input_data = np.random.randn(input_size) - - dataset = ( - dataset_ops.Dataset.from_tensor_slices(input_data.reshape(100, 100)) - .repeat(num_epochs + 1)) - iterator = dataset.make_initializable_iterator() - next_element = iterator.get_next() - - with session.Session() as sess: - sess.run(iterator.initializer) - get_next_element = sess.make_callable(next_element) - # Run one whole epoch to burn in the computation. - for _ in range(input_size // batch_size): - get_next_element() - deltas = [] - try: - while True: - start = time.time() - get_next_element() - deltas.append(time.time() - start) - except errors.OutOfRangeError: - pass - - median_wall_time = np.median(deltas) - print("Reshape/slice/repeat with callable input size: %d batch size: %d " - "Median wall time per element: %f" % (input_size, batch_size, - median_wall_time)) - self.report_benchmark( - iters=len(deltas), - wall_time=median_wall_time, - name="benchmark_reshape_slice_repeat_callable_input_%d_batch_%d" % - (input_size, batch_size)) - - def benchmarkSliceBatchCacheRepeatCallable(self): - input_size = 10000 - batch_size = 100 - num_epochs = 100 - - input_data = np.random.randn(input_size) - - dataset = ( - dataset_ops.Dataset.from_tensor_slices(input_data).batch(batch_size) - .cache().repeat(num_epochs + 1)) - iterator = dataset.make_initializable_iterator() - next_element = iterator.get_next() - - with session.Session() as sess: - sess.run(iterator.initializer) - get_next_element = sess.make_callable(next_element) - # Run one whole epoch to burn in the computation. - for _ in range(input_size // batch_size): - get_next_element() - deltas = [] - try: - while True: - start = time.time() - get_next_element() - deltas.append(time.time() - start) - except errors.OutOfRangeError: - pass - - median_wall_time = np.median(deltas) - print( - "Slice/batch/cache/repeat with callable input size: %d batch size: %d " - "Median wall time per element: %f" - % (input_size, batch_size, median_wall_time)) - self.report_benchmark( - iters=len(deltas), - wall_time=median_wall_time, - name="benchmark_slice_batch_cache_repeat_callable_input_%d_batch_%d" % - (input_size, batch_size)) - - -if __name__ == "__main__": - test.main() diff --git a/tensorflow/python/data/kernel_tests/dataset_ops_test.py b/tensorflow/python/data/kernel_tests/dataset_test.py similarity index 69% rename from tensorflow/python/data/kernel_tests/dataset_ops_test.py rename to tensorflow/python/data/kernel_tests/dataset_test.py index a5324af4d0..2952c08be0 100644 --- a/tensorflow/python/data/kernel_tests/dataset_ops_test.py +++ b/tensorflow/python/data/kernel_tests/dataset_test.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Tests for the input pipeline ops.""" +"""Tests for `tf.data.Dataset`.""" from __future__ import absolute_import from __future__ import division @@ -24,21 +24,26 @@ import numpy as np from tensorflow.core.framework import graph_pb2 from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.data.ops import optional_ops from tensorflow.python.data.ops import readers from tensorflow.python.data.util import nest +from tensorflow.python.data.util import structure +from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util from tensorflow.python.platform import test -class DatasetOpsTest(test_base.DatasetTestBase, parameterized.TestCase): +@test_util.run_all_in_graph_and_eager_modes +class DatasetTest(test_base.DatasetTestBase, parameterized.TestCase): def testAsSerializedGraph(self): dataset = dataset_ops.Dataset.range(10) - with self.cached_session() as sess: - graph = graph_pb2.GraphDef().FromString( - sess.run(dataset._as_serialized_graph())) - self.assertTrue(any([node.op != "RangeDataset" for node in graph.node])) + graph = graph_pb2.GraphDef().FromString( + self.evaluate(dataset._as_serialized_graph())) + self.assertTrue(any([node.op != "RangeDataset" for node in graph.node])) @staticmethod def make_apply_fn(dataset): @@ -76,7 +81,7 @@ class DatasetOpsTest(test_base.DatasetTestBase, parameterized.TestCase): lambda: readers.FixedLengthRecordDataset("", 42)), ("FromGenerator", lambda: dataset_ops.Dataset.from_generator( - DatasetOpsTest.make_gen(), dtypes.int32), + DatasetTest.make_gen(), dtypes.int32), 1), ("FromTensors", lambda: dataset_ops.Dataset.from_tensors([42])), ("FromTensorSlices", lambda: dataset_ops.Dataset.from_tensors([42])), @@ -222,12 +227,12 @@ class DatasetOpsTest(test_base.DatasetTestBase, parameterized.TestCase): options1 = dataset_ops.Options() options1.experimental_autotune = True options2 = dataset_ops.Options() - options2.experimental_filter_fusion = False + options2.experimental_deterministic = False ds = dataset_ops.Dataset.range(0).with_options(options1).with_options( options2) self.assertTrue(ds.options().experimental_autotune) # Explicitly check that flag is False since assertFalse allows None - self.assertIs(ds.options().experimental_filter_fusion, False) + self.assertIs(ds.options().experimental_deterministic, False) def testOptionsTwiceDifferentError(self): options1 = dataset_ops.Options() @@ -235,20 +240,78 @@ class DatasetOpsTest(test_base.DatasetTestBase, parameterized.TestCase): options2 = dataset_ops.Options() options2.experimental_autotune = False with self.assertRaisesRegexp(ValueError, - "Cannot merge incompatible values of option"): + "Cannot merge incompatible values"): dataset_ops.Dataset.range(0).with_options(options1).with_options(options2) def testOptionsMergeOptionsFromMultipleInputs(self): options1 = dataset_ops.Options() options1.experimental_autotune = True options2 = dataset_ops.Options() - options2.experimental_filter_fusion = True + options2.experimental_deterministic = True ds = dataset_ops.Dataset.zip( (dataset_ops.Dataset.range(0).with_options(options1), dataset_ops.Dataset.range(0).with_options(options2))) self.assertTrue(ds.options().experimental_autotune) - self.assertTrue(ds.options().experimental_filter_fusion) + self.assertTrue(ds.options().experimental_deterministic) + # TODO(b/119882922): use-after-free bug in eager mode. + # pylint: disable=g-long-lambda + @parameterized.named_parameters( + ("Tensor", lambda: constant_op.constant(37.0), + structure.TensorStructure(dtypes.float32, [])), + ("SparseTensor", lambda: sparse_tensor.SparseTensor( + indices=[[0]], values=constant_op.constant([0], dtype=dtypes.int32), + dense_shape=[1]), + structure.SparseTensorStructure(dtypes.int32, [1])), + ("Nest", lambda: { + "a": constant_op.constant(37.0), + "b": (constant_op.constant(["Foo"]), constant_op.constant("Bar"))}, + structure.NestedStructure({ + "a": structure.TensorStructure(dtypes.float32, []), + "b": (structure.TensorStructure(dtypes.string, [1]), + structure.TensorStructure(dtypes.string, []))})), + ("Dataset", lambda: dataset_ops.Dataset.from_tensor_slices( + constant_op.constant([1, 2, 3])), + dataset_ops.DatasetStructure( + structure.TensorStructure(dtypes.int32, []))), + ("Optional", lambda: optional_ops.Optional.from_value(37.0), + optional_ops.OptionalStructure( + structure.TensorStructure(dtypes.float32, []))), + ) + def testSkipEagerDatasetStructure(self, tf_value_fn, + expected_element_structure): + dataset = dataset_ops.Dataset.from_tensors(0).map(lambda _: tf_value_fn()) + dataset_structure = structure.Structure.from_value(dataset) + self.assertIsInstance(dataset_structure, dataset_ops.DatasetStructure) + + # TODO(b/110122868): Add a public API to `tf.data.Dataset` for accessing + # the element structure. + self.assertTrue(expected_element_structure.is_compatible_with( + dataset_structure._element_structure)) + self.assertTrue(dataset_structure._element_structure.is_compatible_with( + expected_element_structure)) + + self.assertEqual([dtypes.variant], dataset_structure._flat_types) + self.assertEqual([tensor_shape.scalar()], dataset_structure._flat_shapes) + + # Assert that the `Dataset` survives a round-trip via _from_tensor_list() + # and _to_tensor_list(). + round_trip_dataset = dataset_structure._from_tensor_list( + dataset_structure._to_tensor_list(dataset)) + + value = tf_value_fn() + + if isinstance(value, dataset_ops.Dataset): + self.assertDatasetsEqual(value, dataset.flat_map(lambda x: x)) + elif isinstance(value, optional_ops.Optional): + self.assertDatasetProduces( + round_trip_dataset.map(lambda opt: opt.get_value()), + [self.evaluate(value.get_value())], + requires_initialization=True) + else: + self.assertDatasetProduces( + round_trip_dataset, [self.evaluate(tf_value_fn())], + requires_initialization=True) if __name__ == "__main__": test.main() diff --git a/tensorflow/python/data/kernel_tests/filter_dataset_op_test.py b/tensorflow/python/data/kernel_tests/filter_dataset_op_test.py deleted file mode 100644 index a0c6b37a6d..0000000000 --- a/tensorflow/python/data/kernel_tests/filter_dataset_op_test.py +++ /dev/null @@ -1,220 +0,0 @@ -# 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. -# ============================================================================== -"""Tests for the experimental input pipeline ops.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import time - -import numpy as np - -from tensorflow.python.client import session -from tensorflow.python.data.kernel_tests import test_base -from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.framework import dtypes -from tensorflow.python.framework import errors -from tensorflow.python.framework import ops -from tensorflow.python.framework import sparse_tensor -from tensorflow.python.ops import array_ops -from tensorflow.python.ops import functional_ops -from tensorflow.python.ops import math_ops -from tensorflow.python.platform import test - - -class FilterDatasetTest(test_base.DatasetTestBase): - - def testFilterDataset(self): - components = ( - np.arange(7, dtype=np.int64), - np.array([[1, 2, 3]], dtype=np.int64) * np.arange( - 7, dtype=np.int64)[:, np.newaxis], - np.array(37.0, dtype=np.float64) * np.arange(7) - ) - count = array_ops.placeholder(dtypes.int64, shape=[]) - modulus = array_ops.placeholder(dtypes.int64) - - def _map_fn(x, y, z): - return math_ops.square(x), math_ops.square(y), math_ops.square(z) - - iterator = ( - dataset_ops.Dataset.from_tensor_slices(components).map(_map_fn) - .repeat(count) - .filter(lambda x, _y, _z: math_ops.equal(math_ops.mod(x, modulus), 0)) - .make_initializable_iterator()) - init_op = iterator.initializer - get_next = iterator.get_next() - - self.assertEqual([c.shape[1:] for c in components], - [t.shape for t in get_next]) - - with self.cached_session() as sess: - # Test that we can dynamically feed a different modulus value for each - # iterator. - def do_test(count_val, modulus_val): - sess.run(init_op, feed_dict={count: count_val, modulus: modulus_val}) - for _ in range(count_val): - for i in [x for x in range(7) if x**2 % modulus_val == 0]: - result = sess.run(get_next) - for component, result_component in zip(components, result): - self.assertAllEqual(component[i]**2, result_component) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - do_test(14, 2) - do_test(4, 18) - - # Test an empty dataset. - do_test(0, 1) - - def testFilterRange(self): - dataset = dataset_ops.Dataset.range(100).filter( - lambda x: math_ops.not_equal(math_ops.mod(x, 3), 2)) - iterator = dataset.make_one_shot_iterator() - get_next = iterator.get_next() - - with self.cached_session() as sess: - self.assertEqual(0, sess.run(get_next)) - self.assertEqual(1, sess.run(get_next)) - self.assertEqual(3, sess.run(get_next)) - - def testFilterDict(self): - iterator = (dataset_ops.Dataset.range(10) - .map(lambda x: {"foo": x * 2, "bar": x ** 2}) - .filter(lambda d: math_ops.equal(d["bar"] % 2, 0)) - .map(lambda d: d["foo"] + d["bar"]) - .make_initializable_iterator()) - init_op = iterator.initializer - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(init_op) - for i in range(10): - if (i ** 2) % 2 == 0: - self.assertEqual(i * 2 + i ** 2, sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testUseStepContainerInFilter(self): - input_data = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.int64) - - # Define a predicate that returns true for the first element of - # the sequence and not the second, and uses `tf.map_fn()`. - def _predicate(xs): - squared_xs = functional_ops.map_fn(lambda x: x * x, xs) - summed = math_ops.reduce_sum(squared_xs) - return math_ops.equal(summed, 1 + 4 + 9) - - iterator = ( - dataset_ops.Dataset.from_tensor_slices([[1, 2, 3], [4, 5, 6]]) - .filter(_predicate) - .make_initializable_iterator()) - init_op = iterator.initializer - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(init_op) - self.assertAllEqual(input_data[0], sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testSparse(self): - - def _map_fn(i): - return sparse_tensor.SparseTensorValue( - indices=np.array([[0, 0]]), - values=(i * np.array([1])), - dense_shape=np.array([1, 1])), i - - def _filter_fn(_, i): - return math_ops.equal(i % 2, 0) - - iterator = ( - dataset_ops.Dataset.range(10).map(_map_fn).filter(_filter_fn).map( - lambda x, i: x).make_initializable_iterator()) - init_op = iterator.initializer - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(init_op) - for i in range(5): - actual = sess.run(get_next) - self.assertTrue(isinstance(actual, sparse_tensor.SparseTensorValue)) - self.assertSparseValuesEqual(actual, _map_fn(i * 2)[0]) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testShortCircuit(self): - iterator = ( - dataset_ops.Dataset.zip( - (dataset_ops.Dataset.range(10), - dataset_ops.Dataset.from_tensors(True).repeat(None))) - .filter(lambda x, y: y).make_initializable_iterator()) - init_op = iterator.initializer - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(init_op) - for i in range(10): - self.assertEqual((i, True), sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testParallelFilters(self): - dataset = dataset_ops.Dataset.range(10).filter( - lambda x: math_ops.equal(x % 2, 0)) - iterators = [dataset.make_one_shot_iterator() for _ in range(10)] - next_elements = [iterator.get_next() for iterator in iterators] - with self.cached_session() as sess: - self.assertEqual([0 for _ in range(10)], sess.run(next_elements)) - - -class FilterDatasetBenchmark(test.Benchmark): - - def _benchmark(self, predicate, name): - with ops.Graph().as_default(): - dataset = ( - dataset_ops.Dataset.from_tensors(True).repeat(None).filter(predicate)) - iterator = dataset.make_one_shot_iterator() - next_element = iterator.get_next() - - with session.Session() as sess: - for _ in range(5): - sess.run(next_element.op) - deltas = [] - for _ in range(100): - start = time.time() - for _ in range(100): - sess.run(next_element.op) - end = time.time() - deltas.append(end - start) - - median_wall_time = np.median(deltas) / 100 - print("Filter dataset using %s. Median wall time: %f" % - (name, median_wall_time)) - self.report_benchmark( - iters=100, - wall_time=median_wall_time, - name="benchmark_filter_dataset_%s" % name) - - def benchmarkSimpleFunction(self): - self._benchmark(array_ops.identity, "simple_function") - - def benchmarkReturnComponentOptimization(self): - self._benchmark(lambda x: x, "return_component") - - -if __name__ == "__main__": - test.main() diff --git a/tensorflow/python/data/kernel_tests/filter_test.py b/tensorflow/python/data/kernel_tests/filter_test.py new file mode 100644 index 0000000000..afaf954cbc --- /dev/null +++ b/tensorflow/python/data/kernel_tests/filter_test.py @@ -0,0 +1,128 @@ +# 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. +# ============================================================================== +"""Tests for `tf.data.Dataset.filter()`.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +from tensorflow.python.data.kernel_tests import test_base +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.framework import errors +from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import test_util +from tensorflow.python.ops import functional_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.platform import test + + +@test_util.run_all_in_graph_and_eager_modes +class FilterTest(test_base.DatasetTestBase): + + def testFilterDataset(self): + components = ( + np.arange(7, dtype=np.int64), + np.array([[1, 2, 3]], dtype=np.int64) * np.arange( + 7, dtype=np.int64)[:, np.newaxis], + np.array(37.0, dtype=np.float64) * np.arange(7) + ) + def _map_fn(x, y, z): + return math_ops.square(x), math_ops.square(y), math_ops.square(z) + + def do_test(count, modulus): + dataset = dataset_ops.Dataset.from_tensor_slices(components).map( + _map_fn).repeat(count).filter( + lambda x, _y, _z: math_ops.equal(math_ops.mod(x, modulus), 0)) + self.assertEqual([c.shape[1:] for c in components], + [shape for shape in dataset.output_shapes]) + get_next = self.getNext(dataset) + for _ in range(count): + for i in [x for x in range(7) if x**2 % modulus == 0]: + result = self.evaluate(get_next()) + for component, result_component in zip(components, result): + self.assertAllEqual(component[i]**2, result_component) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + + do_test(14, 2) + do_test(4, 18) + + # Test an empty dataset. + do_test(0, 1) + + def testFilterRange(self): + dataset = dataset_ops.Dataset.range(4).filter( + lambda x: math_ops.not_equal(math_ops.mod(x, 3), 2)) + self.assertDatasetProduces(dataset, expected_output=[0, 1, 3]) + + def testFilterDict(self): + dataset = dataset_ops.Dataset.range(10).map( + lambda x: {"foo": x * 2, "bar": x ** 2}).filter( + lambda d: math_ops.equal(d["bar"] % 2, 0)).map( + lambda d: d["foo"] + d["bar"]) + self.assertDatasetProduces( + dataset, + expected_output=[(i * 2 + i**2) for i in range(10) if not (i**2) % 2]) + + def testUseStepContainerInFilter(self): + input_data = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.int64) + + # Define a predicate that returns true for the first element of + # the sequence and not the second, and uses `tf.map_fn()`. + def _predicate(xs): + squared_xs = functional_ops.map_fn(lambda x: x * x, xs) + summed = math_ops.reduce_sum(squared_xs) + return math_ops.equal(summed, 1 + 4 + 9) + + dataset = dataset_ops.Dataset.from_tensor_slices( + [[1, 2, 3], [4, 5, 6]]).filter(_predicate) + self.assertDatasetProduces(dataset, expected_output=[input_data[0]]) + + def testSparse(self): + + def _map_fn(i): + return sparse_tensor.SparseTensorValue( + indices=np.array([[0, 0]]), + values=(i * np.array([1])), + dense_shape=np.array([1, 1])), i + + def _filter_fn(_, i): + return math_ops.equal(i % 2, 0) + + dataset = dataset_ops.Dataset.range(10).map(_map_fn).filter(_filter_fn).map( + lambda x, i: x) + self.assertDatasetProduces( + dataset, expected_output=[_map_fn(i * 2)[0] for i in range(5)]) + + def testShortCircuit(self): + dataset = dataset_ops.Dataset.zip( + (dataset_ops.Dataset.range(10), + dataset_ops.Dataset.from_tensors(True).repeat(None) + )).filter(lambda x, y: y) + self.assertDatasetProduces( + dataset, expected_output=[(i, True) for i in range(10)]) + + def testParallelFilters(self): + dataset = dataset_ops.Dataset.range(10).filter( + lambda x: math_ops.equal(x % 2, 0)) + next_elements = [self.getNext(dataset) for _ in range(10)] + self.assertEqual([0 for _ in range(10)], + self.evaluate( + [next_element() for next_element in next_elements])) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/data/kernel_tests/fixed_length_record_dataset_test.py b/tensorflow/python/data/kernel_tests/fixed_length_record_dataset_test.py new file mode 100644 index 0000000000..9503e57ca7 --- /dev/null +++ b/tensorflow/python/data/kernel_tests/fixed_length_record_dataset_test.py @@ -0,0 +1,171 @@ +# 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. +# ============================================================================== +"""Tests for `tf.data.FixedLengthRecordDataset`.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import gzip +import os +import zlib + +from tensorflow.python.data.kernel_tests import test_base +from tensorflow.python.data.ops import readers +from tensorflow.python.framework import errors +from tensorflow.python.framework import test_util +from tensorflow.python.platform import test +from tensorflow.python.util import compat + + +@test_util.run_all_in_graph_and_eager_modes +class FixedLengthRecordDatasetTest(test_base.DatasetTestBase): + + def setUp(self): + super(FixedLengthRecordDatasetTest, self).setUp() + self._num_files = 2 + self._num_records = 7 + self._header_bytes = 5 + self._record_bytes = 3 + self._footer_bytes = 2 + + def _record(self, f, r): + return compat.as_bytes(str(f * 2 + r) * self._record_bytes) + + def _createFiles(self, compression_type=None): + filenames = [] + for i in range(self._num_files): + fn = os.path.join(self.get_temp_dir(), "fixed_length_record.%d.txt" % i) + filenames.append(fn) + + contents = [] + contents.append(b"H" * self._header_bytes) + for j in range(self._num_records): + contents.append(self._record(i, j)) + contents.append(b"F" * self._footer_bytes) + contents = b"".join(contents) + + if not compression_type: + with open(fn, "wb") as f: + f.write(contents) + elif compression_type == "GZIP": + with gzip.GzipFile(fn, "wb") as f: + f.write(contents) + elif compression_type == "ZLIB": + contents = zlib.compress(contents) + with open(fn, "wb") as f: + f.write(contents) + else: + raise ValueError("Unsupported compression_type", compression_type) + + return filenames + + def _testFixedLengthRecordDataset(self, compression_type=None): + test_filenames = self._createFiles(compression_type=compression_type) + + def dataset_fn(filenames, num_epochs, batch_size=None): + repeat_dataset = readers.FixedLengthRecordDataset( + filenames, + self._record_bytes, + self._header_bytes, + self._footer_bytes, + compression_type=compression_type).repeat(num_epochs) + if batch_size: + return repeat_dataset.batch(batch_size) + return repeat_dataset + + # Basic test: read from file 0. + self.assertDatasetProduces( + dataset_fn([test_filenames[0]], 1), + expected_output=[ + self._record(0, i) for i in range(self._num_records) + ]) + + # Basic test: read from file 1. + self.assertDatasetProduces( + dataset_fn([test_filenames[1]], 1), + expected_output=[ + self._record(1, i) for i in range(self._num_records) + ]) + + # Basic test: read from both files. + expected_output = [] + for j in range(self._num_files): + expected_output.extend( + [self._record(j, i) for i in range(self._num_records)]) + self.assertDatasetProduces( + dataset_fn(test_filenames, 1), expected_output=expected_output) + + # Test repeated iteration through both files. + get_next = self.getNext(dataset_fn(test_filenames, 10)) + for _ in range(10): + for j in range(self._num_files): + for i in range(self._num_records): + self.assertEqual(self._record(j, i), self.evaluate(get_next())) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + + # Test batched and repeated iteration through both files. + get_next = self.getNext(dataset_fn(test_filenames, 10, self._num_records)) + for _ in range(10): + for j in range(self._num_files): + self.assertAllEqual( + [self._record(j, i) for i in range(self._num_records)], + self.evaluate(get_next())) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + + def testFixedLengthRecordDatasetNoCompression(self): + self._testFixedLengthRecordDataset() + + def testFixedLengthRecordDatasetGzipCompression(self): + self._testFixedLengthRecordDataset(compression_type="GZIP") + + def testFixedLengthRecordDatasetZlibCompression(self): + self._testFixedLengthRecordDataset(compression_type="ZLIB") + + def testFixedLengthRecordDatasetBuffering(self): + test_filenames = self._createFiles() + dataset = readers.FixedLengthRecordDataset( + test_filenames, + self._record_bytes, + self._header_bytes, + self._footer_bytes, + buffer_size=10) + expected_output = [] + for j in range(self._num_files): + expected_output.extend( + [self._record(j, i) for i in range(self._num_records)]) + self.assertDatasetProduces(dataset, expected_output=expected_output) + + def testFixedLengthRecordDatasetWrongSize(self): + test_filenames = self._createFiles() + dataset = readers.FixedLengthRecordDataset( + test_filenames, + self._record_bytes + 1, # Incorrect record length. + self._header_bytes, + self._footer_bytes, + buffer_size=10) + self.assertDatasetProduces( + dataset, + expected_error=( + errors.InvalidArgumentError, + r"Excluding the header \(5 bytes\) and footer \(2 bytes\), input " + r"file \".*fixed_length_record.0.txt\" has body length 21 bytes, " + r"which is not an exact multiple of the record length \(4 bytes\).") + ) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/data/kernel_tests/flat_map_dataset_op_test.py b/tensorflow/python/data/kernel_tests/flat_map_test.py similarity index 57% rename from tensorflow/python/data/kernel_tests/flat_map_dataset_op_test.py rename to tensorflow/python/data/kernel_tests/flat_map_test.py index 68038f9cfc..ff52821b10 100644 --- a/tensorflow/python/data/kernel_tests/flat_map_dataset_op_test.py +++ b/tensorflow/python/data/kernel_tests/flat_map_test.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Tests for the experimental input pipeline ops.""" +"""Tests for `tf.data.Dataset.flat_map()`.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function @@ -26,54 +26,42 @@ from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import errors from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import test_util from tensorflow.python.ops import sparse_ops from tensorflow.python.platform import test from tensorflow.python.training import server_lib -class FlatMapDatasetTest(test_base.DatasetTestBase): +@test_util.run_all_in_graph_and_eager_modes +class FlatMapTest(test_base.DatasetTestBase): # pylint: disable=g-long-lambda def testFlatMapDataset(self): repeats = [1, 2, 3, 4, 5, 0, 1] components = np.array(repeats, dtype=np.int64) - iterator = ( - dataset_ops.Dataset.from_tensor_slices(components) - .flat_map(lambda x: dataset_ops.Dataset.from_tensors([x]).repeat(x)) - .make_initializable_iterator()) - init_op = iterator.initializer - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(init_op) - for i in repeats: - for _ in range(i): - self.assertEqual(i, sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + dataset = dataset_ops.Dataset.from_tensor_slices(components).flat_map( + lambda x: dataset_ops.Dataset.from_tensors([x]).repeat(x)) + expected_output = [] + for i in repeats: + expected_output.extend([[i]] * i) + self.assertDatasetProduces(dataset, expected_output=expected_output) def testNestedFlatMapDataset(self): repeats = [[1, 2], [3, 4], [5, 0], [1, 7]] components = np.array(repeats, dtype=np.int64) - iterator = ( - dataset_ops.Dataset.from_tensor_slices(components) - .flat_map(lambda x: dataset_ops.Dataset.from_tensor_slices(x) - .flat_map(lambda y: dataset_ops.Dataset.from_tensors(y) - .repeat(y))).make_initializable_iterator()) - init_op = iterator.initializer - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(init_op) - for row in repeats: - for i in row: - for _ in range(i): - self.assertEqual(i, sess.run(get_next)) - - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testSharedResourceNestedFlatMapDataset(self): + dataset = dataset_ops.Dataset.from_tensor_slices(components).flat_map( + lambda x: dataset_ops.Dataset.from_tensor_slices(x).flat_map( + lambda y: dataset_ops.Dataset.from_tensors(y).repeat(y)) + ) + expected_output = [] + for row in repeats: + for i in row: + expected_output.extend([i] * i) + self.assertDatasetProduces(dataset, expected_output=expected_output) + + # Note: no eager mode coverage, session specific test. + @test_util.run_deprecated_v1 + def testSkipEagerSharedResourceNestedFlatMapDataset(self): repeats = [[1, 2], [3, 4], [5, 0], [1, 7]] components = np.array(repeats, dtype=np.int64) iterator = ( @@ -106,22 +94,16 @@ class FlatMapDatasetTest(test_base.DatasetTestBase): sess.run(get_next) def testMapDict(self): - iterator = (dataset_ops.Dataset.range(10) - .map(lambda x: {"foo": x * 2, "bar": x ** 2}) - .flat_map(lambda d: dataset_ops.Dataset.from_tensors(d["foo"]) - .repeat(d["bar"])) - .make_initializable_iterator()) - init_op = iterator.initializer - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(init_op) - for i in range(10): - for _ in range(i ** 2): - self.assertEqual(i * 2, sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - # pylint: enable=g-long-lambda + dataset = dataset_ops.Dataset.range(10).map( + lambda x: {"foo": x * 2, "bar": x ** 2}).flat_map( + lambda d: dataset_ops.Dataset.from_tensors( + d["foo"]).repeat(d["bar"])) + get_next = self.getNext(dataset) + for i in range(10): + for _ in range(i**2): + self.assertEqual(i * 2, self.evaluate(get_next())) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) def testSparse(self): def _map_fn(i): @@ -132,20 +114,12 @@ class FlatMapDatasetTest(test_base.DatasetTestBase): return dataset_ops.Dataset.from_tensor_slices( sparse_ops.sparse_to_dense(x.indices, x.dense_shape, x.values)) - iterator = ( - dataset_ops.Dataset.range(10).map(_map_fn).flat_map(_flat_map_fn) - .make_initializable_iterator()) - init_op = iterator.initializer - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(init_op) - for i in range(10): - for j in range(2): - expected = [i, 0] if j % 2 == 0 else [0, -i] - self.assertAllEqual(expected, sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + dataset = dataset_ops.Dataset.range(10).map(_map_fn).flat_map(_flat_map_fn) + expected_output = [] + for i in range(10): + for j in range(2): + expected_output.append([i, 0] if j % 2 == 0 else [0, -i]) + self.assertDatasetProduces(dataset, expected_output=expected_output) if __name__ == "__main__": diff --git a/tensorflow/python/data/kernel_tests/dataset_from_generator_op_test.py b/tensorflow/python/data/kernel_tests/from_generator_test.py similarity index 85% rename from tensorflow/python/data/kernel_tests/dataset_from_generator_op_test.py rename to tensorflow/python/data/kernel_tests/from_generator_test.py index cb8cb9a77d..a6625534e7 100644 --- a/tensorflow/python/data/kernel_tests/dataset_from_generator_op_test.py +++ b/tensorflow/python/data/kernel_tests/from_generator_test.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Tests for the experimental input pipeline ops.""" +"""Tests for tf.data.Dataset.from_generator().""" from __future__ import absolute_import from __future__ import division from __future__ import print_function @@ -27,21 +27,21 @@ from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors +from tensorflow.python.framework import test_util from tensorflow.python.ops import script_ops from tensorflow.python.platform import test -class DatasetConstructorTest(test_base.DatasetTestBase): +class FromGeneratorTest(test_base.DatasetTestBase): def _testFromGenerator(self, generator, elem_sequence, num_repeats, output_types=None): if output_types is None: output_types = dtypes.int64 - iterator = ( + iterator = dataset_ops.make_initializable_iterator( dataset_ops.Dataset.from_generator(generator, output_types=output_types) .repeat(num_repeats) - .prefetch(5) - .make_initializable_iterator()) + .prefetch(5)) init_op = iterator.initializer get_next = iterator.get_next() @@ -55,11 +55,10 @@ class DatasetConstructorTest(test_base.DatasetTestBase): sess.run(get_next) def _testFromGeneratorOneShot(self, generator, elem_sequence, num_repeats): - iterator = ( + iterator = dataset_ops.make_one_shot_iterator( dataset_ops.Dataset.from_generator(generator, output_types=dtypes.int64) .repeat(num_repeats) - .prefetch(5) - .make_one_shot_iterator()) + .prefetch(5)) get_next = iterator.get_next() with self.cached_session() as sess: @@ -69,6 +68,7 @@ class DatasetConstructorTest(test_base.DatasetTestBase): with self.assertRaises(errors.OutOfRangeError): sess.run(get_next) + @test_util.run_deprecated_v1 def testFromGeneratorUsingFunction(self): def generator(): for i in range(1, 100): @@ -79,18 +79,21 @@ class DatasetConstructorTest(test_base.DatasetTestBase): self._testFromGeneratorOneShot(generator, elem_sequence, 1) self._testFromGeneratorOneShot(generator, elem_sequence, 5) + @test_util.run_deprecated_v1 def testFromGeneratorUsingList(self): generator = lambda: [[i] * i for i in range(1, 100)] elem_sequence = list(generator()) self._testFromGenerator(generator, elem_sequence, 1) self._testFromGenerator(generator, elem_sequence, 5) + @test_util.run_deprecated_v1 def testFromGeneratorUsingNdarray(self): generator = lambda: np.arange(100, dtype=np.int64) elem_sequence = list(generator()) self._testFromGenerator(generator, elem_sequence, 1, output_types=np.int64) self._testFromGenerator(generator, elem_sequence, 5, output_types=np.int64) + @test_util.run_deprecated_v1 def testFromGeneratorUsingGeneratorExpression(self): # NOTE(mrry): Generator *expressions* are not repeatable (or in # general reusable), because they eagerly evaluate the `for` @@ -102,6 +105,7 @@ class DatasetConstructorTest(test_base.DatasetTestBase): self._testFromGenerator(generator, elem_sequence, 1) self._testFromGenerator(generator, elem_sequence, 5) + @test_util.run_deprecated_v1 def testFromMultipleConcurrentGenerators(self): num_inner_repeats = 5 num_outer_repeats = 100 @@ -124,11 +128,10 @@ class DatasetConstructorTest(test_base.DatasetTestBase): output_shapes=([None], [3])) .repeat(num_inner_repeats).prefetch(5)) - iterator = ( + iterator = dataset_ops.make_initializable_iterator( dataset_ops.Dataset.range(num_outer_repeats) .interleave(interleave_fn, cycle_length=10, - block_length=len(input_list)) - .make_initializable_iterator()) + block_length=len(input_list))) init_op = iterator.initializer get_next = iterator.get_next() @@ -183,11 +186,10 @@ class DatasetConstructorTest(test_base.DatasetTestBase): return dataset_ops.Dataset.from_generator( generator, output_types=dtypes.int64, output_shapes=[]).prefetch(2) - iterator = ( + iterator = dataset_ops.make_initializable_iterator( dataset_ops.Dataset.range(num_parallel_iterators) .interleave( - interleave_fn, cycle_length=num_parallel_iterators, block_length=1) - .make_initializable_iterator()) + interleave_fn, cycle_length=num_parallel_iterators, block_length=1)) init_op = iterator.initializer get_next = iterator.get_next() @@ -199,6 +201,7 @@ class DatasetConstructorTest(test_base.DatasetTestBase): with self.assertRaises(errors.OutOfRangeError): sess.run(get_next) + @test_util.run_deprecated_v1 def testFromGeneratorImplicitConversion(self): def generator(): yield [1] @@ -206,9 +209,9 @@ class DatasetConstructorTest(test_base.DatasetTestBase): yield [3] for dtype in [dtypes.int8, dtypes.int32, dtypes.int64]: - iterator = (dataset_ops.Dataset.from_generator( - generator, output_types=dtype, output_shapes=[1]) - .make_initializable_iterator()) + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.from_generator( + generator, output_types=dtype, output_shapes=[1])) init_op = iterator.initializer get_next = iterator.get_next() @@ -223,15 +226,16 @@ class DatasetConstructorTest(test_base.DatasetTestBase): with self.assertRaises(errors.OutOfRangeError): sess.run(get_next) + @test_util.run_deprecated_v1 def testFromGeneratorString(self): def generator(): yield "foo" yield b"bar" yield u"baz" - iterator = (dataset_ops.Dataset.from_generator( - generator, output_types=dtypes.string, output_shapes=[]) - .make_initializable_iterator()) + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.from_generator( + generator, output_types=dtypes.string, output_shapes=[])) init_op = iterator.initializer get_next = iterator.get_next() @@ -243,6 +247,7 @@ class DatasetConstructorTest(test_base.DatasetTestBase): with self.assertRaises(errors.OutOfRangeError): sess.run(get_next) + @test_util.run_deprecated_v1 def testFromGeneratorTypeError(self): def generator(): yield np.array([1, 2, 3], dtype=np.int64) @@ -250,9 +255,9 @@ class DatasetConstructorTest(test_base.DatasetTestBase): yield "ERROR" yield np.array([7, 8, 9], dtype=np.int64) - iterator = (dataset_ops.Dataset.from_generator( - generator, output_types=dtypes.int64, output_shapes=[3]) - .make_initializable_iterator()) + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.from_generator( + generator, output_types=dtypes.int64, output_shapes=[3])) init_op = iterator.initializer get_next = iterator.get_next() @@ -266,6 +271,7 @@ class DatasetConstructorTest(test_base.DatasetTestBase): with self.assertRaises(errors.OutOfRangeError): sess.run(get_next) + @test_util.run_deprecated_v1 def testFromGeneratorShapeError(self): def generator(): yield np.array([1, 2, 3], dtype=np.int64) @@ -273,9 +279,9 @@ class DatasetConstructorTest(test_base.DatasetTestBase): yield np.array([7, 8, 9, 10], dtype=np.int64) yield np.array([11, 12, 13], dtype=np.int64) - iterator = (dataset_ops.Dataset.from_generator( - generator, output_types=dtypes.int64, output_shapes=[3]) - .make_initializable_iterator()) + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.from_generator( + generator, output_types=dtypes.int64, output_shapes=[3])) init_op = iterator.initializer get_next = iterator.get_next() @@ -289,6 +295,7 @@ class DatasetConstructorTest(test_base.DatasetTestBase): with self.assertRaises(errors.OutOfRangeError): sess.run(get_next) + @test_util.run_deprecated_v1 def testFromGeneratorStructureError(self): def generator(): yield 1, 2 @@ -297,9 +304,9 @@ class DatasetConstructorTest(test_base.DatasetTestBase): yield 6, 7, 8 yield 9, 10 - iterator = (dataset_ops.Dataset.from_generator( - generator, output_types=(dtypes.int64, dtypes.int64)) - .make_initializable_iterator()) + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.from_generator( + generator, output_types=(dtypes.int64, dtypes.int64))) init_op = iterator.initializer get_next = iterator.get_next() @@ -317,14 +324,15 @@ class DatasetConstructorTest(test_base.DatasetTestBase): with self.assertRaises(errors.OutOfRangeError): sess.run(get_next) + @test_util.run_deprecated_v1 def testFromGeneratorHeterogeneous(self): def generator(): yield 1 yield [2, 3] - iterator = ( + iterator = dataset_ops.make_initializable_iterator( dataset_ops.Dataset.from_generator( - generator, output_types=dtypes.int64).make_initializable_iterator()) + generator, output_types=dtypes.int64)) init_op = iterator.initializer get_next = iterator.get_next() @@ -335,6 +343,7 @@ class DatasetConstructorTest(test_base.DatasetTestBase): with self.assertRaises(errors.OutOfRangeError): sess.run(get_next) + @test_util.run_deprecated_v1 def testFromGeneratorStopShort(self): def generator(): @@ -342,9 +351,9 @@ class DatasetConstructorTest(test_base.DatasetTestBase): yield 1 yield 2 - iterator = ( + iterator = dataset_ops.make_initializable_iterator( dataset_ops.Dataset.from_generator( - generator, output_types=dtypes.int64).make_initializable_iterator()) + generator, output_types=dtypes.int64)) init_op = iterator.initializer get_next = iterator.get_next() @@ -353,6 +362,7 @@ class DatasetConstructorTest(test_base.DatasetTestBase): self.assertAllEqual(0, sess.run(get_next)) self.assertAllEqual(1, sess.run(get_next)) + @test_util.run_deprecated_v1 def testFromGeneratorDestructorCalled(self): # Use an `Event` to signal that the generator has been deleted. event = threading.Event() @@ -371,9 +381,9 @@ class DatasetConstructorTest(test_base.DatasetTestBase): def __del__(self): event.set() - iterator = dataset_ops.Dataset.from_generator( - GeneratorWrapper, - output_types=dtypes.int64).take(2).make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.from_generator( + GeneratorWrapper, output_types=dtypes.int64).take(2)) init_op = iterator.initializer get_next = iterator.get_next() @@ -387,6 +397,7 @@ class DatasetConstructorTest(test_base.DatasetTestBase): # iterator terminates (and the generator iterator is deleted). self.assertTrue(event.is_set()) + @test_util.run_deprecated_v1 def testFromGeneratorWithArgs(self): def flat_map_fn(elem): @@ -399,10 +410,8 @@ class DatasetConstructorTest(test_base.DatasetTestBase): generator_with_arg, output_types=dtypes.int64, output_shapes=(), args=(elem,)) - iterator = (dataset_ops.Dataset - .range(5) - .flat_map(flat_map_fn) - .make_initializable_iterator()) + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.range(5).flat_map(flat_map_fn)) init_op = iterator.initializer get_next = iterator.get_next() @@ -414,6 +423,7 @@ class DatasetConstructorTest(test_base.DatasetTestBase): with self.assertRaises(errors.OutOfRangeError): sess.run(get_next) + @test_util.run_deprecated_v1 def testFromGeneratorWithTwoArgs(self): def flat_map_fn(elem, message): @@ -426,12 +436,11 @@ class DatasetConstructorTest(test_base.DatasetTestBase): generator_with_arg, output_types=(dtypes.int64, dtypes.string), output_shapes=((), ()), args=(elem, message)) - iterator = ( + iterator = dataset_ops.make_initializable_iterator( dataset_ops.Dataset.zip( (dataset_ops.Dataset.range(5), dataset_ops.Dataset.from_tensors("Hi!").repeat(None))) - .flat_map(flat_map_fn) - .make_initializable_iterator()) + .flat_map(flat_map_fn)) init_op = iterator.initializer get_next = iterator.get_next() @@ -446,6 +455,7 @@ class DatasetConstructorTest(test_base.DatasetTestBase): with self.assertRaises(errors.OutOfRangeError): sess.run(get_next) + @test_util.run_deprecated_v1 def testGeneratorDatasetFinalizeFunctionCalled(self): # NOTE(mrry): This test tests the internal `_GeneratorDataset`, # which affords more control over what the finalize function can do than @@ -462,10 +472,9 @@ class DatasetConstructorTest(test_base.DatasetTestBase): stateful=True) dummy = constant_op.constant(37) - iterator = (dataset_ops._GeneratorDataset(dummy, lambda x: x, - lambda x: x, finalize_fn) - .take(2) - .make_initializable_iterator()) + iterator = dataset_ops.make_initializable_iterator( + dataset_ops._GeneratorDataset( + dummy, lambda x: x, lambda x: x, finalize_fn).take(2)) init_op = iterator.initializer get_next = iterator.get_next() diff --git a/tensorflow/python/data/kernel_tests/from_sparse_tensor_slices_test.py b/tensorflow/python/data/kernel_tests/from_sparse_tensor_slices_test.py new file mode 100644 index 0000000000..ef608ebb67 --- /dev/null +++ b/tensorflow/python/data/kernel_tests/from_sparse_tensor_slices_test.py @@ -0,0 +1,86 @@ +# 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. +# ============================================================================== +"""Tests for `tf.data.Dataset.from_sparse_tensor_slices()`.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +from tensorflow.python.data.kernel_tests import test_base +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import errors +from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import test_util +from tensorflow.python.ops import array_ops +from tensorflow.python.platform import test + + +@test_util.run_all_in_graph_and_eager_modes +class FromSparseTensorSlicesTest(test_base.DatasetTestBase): + + @test_util.run_deprecated_v1 + def testSkipEagerFromSparseTensorSlices(self): + """Test a dataset based on slices of a `tf.SparseTensor`.""" + st = array_ops.sparse_placeholder(dtypes.float64) + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.from_sparse_tensor_slices(st)) + init_op = iterator.initializer + get_next = sparse_tensor.SparseTensor(*iterator.get_next()) + + with self.cached_session() as sess: + slices = [[1., 2., 3.], [1.], [1.], [1., 2.], [], [1., 2.], [], [], []] + + # Test with sparse tensor in the appropriate order. + indices = np.array( + [[i, j] for i in range(len(slices)) for j in range(len(slices[i]))]) + values = np.array([val for s in slices for val in s]) + dense_shape = np.array([len(slices), max(len(s) for s in slices) + 1]) + sparse_feed = sparse_tensor.SparseTensorValue(indices, values, + dense_shape) + sess.run(init_op, feed_dict={st: sparse_feed}) + for i, s in enumerate(slices): + results = sess.run(get_next) + self.assertAllEqual(s, results.values) + expected_indices = np.array( + [[j] for j in range(len(slices[i]))]).reshape([-1, 1]) + self.assertAllEqual(expected_indices, results.indices) + self.assertAllEqual(dense_shape[1:], results.dense_shape) + with self.assertRaises(errors.OutOfRangeError): + sess.run(get_next) + + # Test with sparse tensor in the reverse order, which is not + # currently supported. + reverse_order_indices = indices[::-1, :] + reverse_order_values = values[::-1] + sparse_feed = sparse_tensor.SparseTensorValue( + reverse_order_indices, reverse_order_values, dense_shape) + with self.assertRaises(errors.UnimplementedError): + sess.run(init_op, feed_dict={st: sparse_feed}) + + # Test with an empty sparse tensor. + empty_indices = np.empty((0, 4), dtype=np.int64) + empty_values = np.empty((0,), dtype=np.float64) + empty_dense_shape = [0, 4, 37, 9] + sparse_feed = sparse_tensor.SparseTensorValue(empty_indices, empty_values, + empty_dense_shape) + sess.run(init_op, feed_dict={st: sparse_feed}) + with self.assertRaises(errors.OutOfRangeError): + sess.run(get_next) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/data/kernel_tests/from_tensor_slices_test.py b/tensorflow/python/data/kernel_tests/from_tensor_slices_test.py new file mode 100644 index 0000000000..9a480e5678 --- /dev/null +++ b/tensorflow/python/data/kernel_tests/from_tensor_slices_test.py @@ -0,0 +1,177 @@ +# 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. +# ============================================================================== +"""Tests for `tf.data.Dataset.from_tensor_slices().""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +from tensorflow.python.data.kernel_tests import test_base +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import errors +from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util +from tensorflow.python.platform import test + + +@test_util.run_all_in_graph_and_eager_modes +class FromTensorSlicesTest(test_base.DatasetTestBase): + + def testFromTensorSlices(self): + """Test a dataset that represents the slices from a tuple of tensors.""" + components = ( + np.tile(np.array([[1], [2], [3], [4]]), 20), np.tile( + np.array([[12], [13], [14], [15]]), 22), + np.array([37.0, 38.0, 39.0, 40.0]) + ) + + dataset = dataset_ops.Dataset.from_tensor_slices(components) + get_next = self.getNext(dataset) + + self.assertEqual([c.shape[1:] for c in components], + [shape for shape in dataset.output_shapes]) + + for i in range(4): + results = self.evaluate(get_next()) + for component, result_component in zip(components, results): + self.assertAllEqual(component[i], result_component) + with self.assertRaises(errors.OutOfRangeError): + results = self.evaluate(get_next()) + + def testSkipEagerFromTensorSlicesSparse(self): + """Test a dataset that represents the slices from a tuple of tensors.""" + components = (sparse_tensor.SparseTensorValue( + indices=np.array([[0, 0], [1, 0], [2, 0]]), + values=np.array([0, 0, 0]), + dense_shape=np.array([3, 1])), + sparse_tensor.SparseTensorValue( + indices=np.array([[0, 0], [1, 1], [2, 2]]), + values=np.array([1, 2, 3]), + dense_shape=np.array([3, 3]))) + + dataset = dataset_ops.Dataset.from_tensor_slices(components) + + self.assertEqual( + [tensor_shape.TensorShape(c.dense_shape[1:]) for c in components], + [shape for shape in dataset.output_shapes]) + + expected = [ + (sparse_tensor.SparseTensorValue( + indices=np.array([[0]]), + values=np.array([0]), + dense_shape=np.array([1])), + sparse_tensor.SparseTensorValue( + indices=np.array([[0]]), + values=np.array([1]), + dense_shape=np.array([3]))), + (sparse_tensor.SparseTensorValue( + indices=np.array([[0]]), + values=np.array([0]), + dense_shape=np.array([1])), + sparse_tensor.SparseTensorValue( + indices=np.array([[1]]), + values=np.array([2]), + dense_shape=np.array([3]))), + (sparse_tensor.SparseTensorValue( + indices=np.array([[0]]), + values=np.array([0]), + dense_shape=np.array([1])), + sparse_tensor.SparseTensorValue( + indices=np.array([[2]]), + values=np.array([3]), + dense_shape=np.array([3]))), + ] + self.assertDatasetProduces(dataset, expected_output=expected) + + def testFromTensorSlicesMixed(self): + """Test a dataset that represents the slices from a tuple of tensors.""" + components = (np.tile(np.array([[1], [2], [3]]), 20), + np.tile(np.array([[12], [13], [14]]), 22), + np.array([37.0, 38.0, 39.0]), + sparse_tensor.SparseTensorValue( + indices=np.array([[0, 0], [1, 0], [2, 0]]), + values=np.array([0, 0, 0]), + dense_shape=np.array([3, 1])), + sparse_tensor.SparseTensorValue( + indices=np.array([[0, 0], [1, 1], [2, 2]]), + values=np.array([1, 2, 3]), + dense_shape=np.array([3, 3]))) + + dataset = dataset_ops.Dataset.from_tensor_slices(components) + get_next = self.getNext(dataset) + self.assertEqual([ + tensor_shape.TensorShape(c.dense_shape[1:]) + if sparse_tensor.is_sparse(c) else c.shape[1:] for c in components + ], [shape for shape in dataset.output_shapes]) + + expected = [ + (sparse_tensor.SparseTensorValue( + indices=np.array([[0]]), + values=np.array([0]), + dense_shape=np.array([1])), + sparse_tensor.SparseTensorValue( + indices=np.array([[0]]), + values=np.array([1]), + dense_shape=np.array([3]))), + (sparse_tensor.SparseTensorValue( + indices=np.array([[0]]), + values=np.array([0]), + dense_shape=np.array([1])), + sparse_tensor.SparseTensorValue( + indices=np.array([[1]]), + values=np.array([2]), + dense_shape=np.array([3]))), + (sparse_tensor.SparseTensorValue( + indices=np.array([[0]]), + values=np.array([0]), + dense_shape=np.array([1])), + sparse_tensor.SparseTensorValue( + indices=np.array([[2]]), + values=np.array([3]), + dense_shape=np.array([3]))), + ] + for i in range(3): + results = self.evaluate(get_next()) + for component, result_component in zip( + (list(zip(*components[:3]))[i] + expected[i]), results): + if sparse_tensor.is_sparse(component): + self.assertSparseValuesEqual(component, result_component) + else: + self.assertAllEqual(component, result_component) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + + def testFromTensorSlicesWithDict(self): + components = {"foo": [1, 2, 3], "bar": [[4.0], [5.0], [6.0]]} + dataset = dataset_ops.Dataset.from_tensor_slices(components) + get_next = self.getNext(dataset) + + self.assertEqual(dtypes.int32, dataset.output_types["foo"]) + self.assertEqual(dtypes.float32, dataset.output_types["bar"]) + self.assertEqual((), dataset.output_shapes["foo"]) + self.assertEqual((1,), dataset.output_shapes["bar"]) + + for i in range(3): + results = self.evaluate(get_next()) + self.assertEqual(components["foo"][i], results["foo"]) + self.assertEqual(components["bar"][i], results["bar"]) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/data/kernel_tests/from_tensors_test.py b/tensorflow/python/data/kernel_tests/from_tensors_test.py new file mode 100644 index 0000000000..ab3c15263f --- /dev/null +++ b/tensorflow/python/data/kernel_tests/from_tensors_test.py @@ -0,0 +1,259 @@ +# 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. +# ============================================================================== +"""Tests for `tf.data.Dataset.from_tensors().""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +from tensorflow.core.protobuf import config_pb2 +from tensorflow.python.client import session +from tensorflow.python.data.kernel_tests import test_base +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.data.util import nest +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import errors +from tensorflow.python.framework import ops +from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import resource_variable_ops +from tensorflow.python.platform import test + + +@test_util.run_all_in_graph_and_eager_modes +class FromTensorsTest(test_base.DatasetTestBase): + + def testFromTensors(self): + """Test a dataset that represents a single tuple of tensors.""" + components = (np.array(1), np.array([1, 2, 3]), np.array(37.0)) + + dataset = dataset_ops.Dataset.from_tensors(components) + + self.assertEqual([c.shape for c in components], + nest.flatten(dataset.output_shapes)) + + self.assertDatasetProduces(dataset, expected_output=[components]) + + def testSkipEagerFromTensorsSparse(self): + """Test a dataset that represents a single tuple of tensors.""" + components = (sparse_tensor.SparseTensorValue( + indices=np.array([[0]]), + values=np.array([0]), + dense_shape=np.array([1])), + sparse_tensor.SparseTensorValue( + indices=np.array([[0, 0], [1, 1]]), + values=np.array([-1, 1]), + dense_shape=np.array([2, 2]))) + + dataset = dataset_ops.Dataset.from_tensors(components) + + self.assertEqual( + [tensor_shape.TensorShape(c.dense_shape) for c in components], + [shape for shape in dataset.output_shapes]) + self.assertDatasetProduces(dataset, expected_output=[components]) + + def testFromTensorsMixed(self): + """Test an dataset that represents a single tuple of tensors.""" + components = (np.array(1), np.array([1, 2, 3]), np.array(37.0), + sparse_tensor.SparseTensorValue( + indices=np.array([[0]]), + values=np.array([0]), + dense_shape=np.array([1])), + sparse_tensor.SparseTensorValue( + indices=np.array([[0, 0], [1, 1]]), + values=np.array([-1, 1]), + dense_shape=np.array([2, 2]))) + + dataset = dataset_ops.Dataset.from_tensors(components) + self.assertEqual([ + tensor_shape.TensorShape(c.dense_shape) + if sparse_tensor.is_sparse(c) else c.shape for c in components + ], [shape for shape in dataset.output_shapes]) + + self.assertDatasetProduces(dataset, expected_output=[components]) + + # pylint: disable=g-long-lambda,unnecessary-lambda + def testNestedStructure(self): + components = (np.array([1, 2, 3], dtype=np.int64), + (np.array([4., 5.]), np.array([6., 7.])), + np.array([8, 9, 10], dtype=np.int64)) + + dataset = dataset_ops.Dataset.from_tensors(components) + self.assertEquals((dtypes.int64, (dtypes.float64, dtypes.float64), + dtypes.int64), dataset.output_types) + self.assertEquals(([3], ([2], [2]), [3]), dataset.output_shapes) + + dataset = dataset.shuffle(10, 10) + self.assertEquals((dtypes.int64, (dtypes.float64, dtypes.float64), + dtypes.int64), dataset.output_types) + self.assertEquals(([3], ([2], [2]), [3]), dataset.output_shapes) + + dataset = dataset.repeat(-1) + self.assertEquals((dtypes.int64, (dtypes.float64, dtypes.float64), + dtypes.int64), dataset.output_types) + self.assertEquals(([3], ([2], [2]), [3]), dataset.output_shapes) + + dataset = dataset.filter(lambda x, y, z: True) + self.assertEquals((dtypes.int64, (dtypes.float64, dtypes.float64), + dtypes.int64), dataset.output_types) + self.assertEquals(([3], ([2], [2]), [3]), dataset.output_shapes) + + dataset = dataset.take(5) + self.assertEquals((dtypes.int64, (dtypes.float64, dtypes.float64), + dtypes.int64), dataset.output_types) + self.assertEquals(([3], ([2], [2]), [3]), dataset.output_shapes) + + dataset = dataset.map(lambda x, y, z: ((x, z), (y[0], y[1]))) + self.assertEquals(((dtypes.int64, dtypes.int64), + (dtypes.float64, dtypes.float64)), dataset.output_types) + self.assertEquals((([3], [3]), ([2], [2])), dataset.output_shapes) + + dataset = dataset.flat_map( + lambda x, y: dataset_ops.Dataset.from_tensors(((x[0], x[1]), + (y[0], y[1]))) + ) + self.assertEquals(((dtypes.int64, dtypes.int64), + (dtypes.float64, dtypes.float64)), dataset.output_types) + self.assertEquals((([3], [3]), ([2], [2])), dataset.output_shapes) + + dataset = dataset.batch(32) + self.assertEquals(((dtypes.int64, dtypes.int64), + (dtypes.float64, dtypes.float64)), dataset.output_types) + self.assertEquals((([None, 3], [None, 3]), ([None, 2], [None, 2])), + nest.pack_sequence_as(dataset.output_shapes, [ + s.as_list() + for s in nest.flatten(dataset.output_shapes) + ])) + + # Define a separate set of components with matching leading + # dimension for the from-slices constructor. + components_for_slices = (np.array([1, 2, 3], dtype=np.int64), + (np.array([4., 5., 6.]), np.array([7., 8., 9.])), + np.array([10, 11, 12], dtype=np.int64)) + + dataset = dataset_ops.Dataset.from_tensor_slices(components_for_slices) + self.assertEquals((dtypes.int64, + (dtypes.float64, dtypes.float64), dtypes.int64), + dataset.output_types) + self.assertEquals(([], ([], []), []), dataset.output_shapes) + + # TODO(b/117581999): more specific shapes in eager mode. + @test_util.run_deprecated_v1 + def testSkipEagerNestedStructure(self): + components = (np.array([1, 2, 3], dtype=np.int64), (np.array([4., 5.]), + np.array([6., 7.])), + np.array([8, 9, 10], dtype=np.int64)) + + dataset = dataset_ops.Dataset.from_tensors(components) + dataset = dataset.map(lambda x, y, z: ((x, z), (y[0], y[1]))) + + dataset = dataset.flat_map( + lambda x, y: dataset_ops.Dataset.from_tensors( + ((x[0], x[1]), (y[0], y[1])))).batch(32) + + get_next = self.getNext(dataset) + (w, x), (y, z) = get_next() + self.assertEquals(dtypes.int64, w.dtype) + self.assertEquals(dtypes.int64, x.dtype) + self.assertEquals(dtypes.float64, y.dtype) + self.assertEquals(dtypes.float64, z.dtype) + self.assertEquals([None, 3], w.shape.as_list()) + self.assertEquals([None, 3], x.shape.as_list()) + self.assertEquals([None, 2], y.shape.as_list()) + self.assertEquals([None, 2], z.shape.as_list()) + + get_next = self.getNext(dataset) + (w, x), (y, z) = get_next() + self.assertEquals(dtypes.int64, w.dtype) + self.assertEquals(dtypes.int64, x.dtype) + self.assertEquals(dtypes.float64, y.dtype) + self.assertEquals(dtypes.float64, z.dtype) + self.assertEquals([None, 3], w.shape.as_list()) + self.assertEquals([None, 3], x.shape.as_list()) + self.assertEquals([None, 2], y.shape.as_list()) + self.assertEquals([None, 2], z.shape.as_list()) + + def testNestedDict(self): + components = {"a": {"aa": 1, "ab": [2.0, 2.0]}, "b": [3, 3, 3]} + dataset = dataset_ops.Dataset.from_tensors(components) + self.assertEquals(dtypes.int32, dataset.output_types["a"]["aa"]) + self.assertEquals(dtypes.float32, dataset.output_types["a"]["ab"]) + self.assertEquals(dtypes.int32, dataset.output_types["b"]) + self.assertEquals([], dataset.output_shapes["a"]["aa"]) + self.assertEquals([2], dataset.output_shapes["a"]["ab"]) + self.assertEquals([3], dataset.output_shapes["b"]) + + def testNonSequenceNestedStructure(self): + components = np.array([1, 2, 3], dtype=np.int64) + + dataset = dataset_ops.Dataset.from_tensors(components) + self.assertEquals(dtypes.int64, dataset.output_types) + self.assertEquals([3], dataset.output_shapes) + + dataset = dataset.filter( + lambda x: math_ops.reduce_all(math_ops.equal(x, components))) + self.assertEquals(dtypes.int64, dataset.output_types) + self.assertEquals([3], dataset.output_shapes) + + dataset = dataset.map(lambda x: array_ops.stack([x, x])) + self.assertEquals(dtypes.int64, dataset.output_types) + self.assertEquals([2, 3], dataset.output_shapes) + + dataset = dataset.flat_map( + lambda x: dataset_ops.Dataset.from_tensor_slices(x)) + self.assertEquals(dtypes.int64, dataset.output_types) + self.assertEquals([3], dataset.output_shapes) + + get_next = self.getNext(dataset) + self.assertEquals(dtypes.int64, get_next().dtype) + self.assertEquals([3], get_next().shape) + + def testSkipEagerSplitPipelineFailsWithPlacementError(self): + with session.Session( + target="", + config=config_pb2.ConfigProto(device_count={"CPU": 2})) as sess: + + dataset = dataset_ops.Dataset.from_tensors(0) + + # Define a pipeline that attempts to use variables on two + # different devices. + # + # Initialize the variables before creating to iterator, to avoid the + # placement algorithm overriding the DT_RESOURCE colocation constraints. + with ops.device("/cpu:0"): + var_0 = resource_variable_ops.ResourceVariable(initial_value=0) + dataset = dataset.map(lambda x: x + var_0.read_value()) + sess.run(var_0.initializer) + + with ops.device("/cpu:1"): + var_1 = resource_variable_ops.ResourceVariable(initial_value=0) + dataset = dataset.map(lambda x: x + var_1.read_value()) + sess.run(var_1.initializer) + + iterator = dataset_ops.make_initializable_iterator(dataset) + sess.run(iterator.initializer) + + with self.assertRaisesRegexp( + errors.FailedPreconditionError, + "Error while reading resource variable Variable"): + sess.run(iterator.get_next()) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/data/kernel_tests/inputs_test.py b/tensorflow/python/data/kernel_tests/inputs_test.py deleted file mode 100644 index d089b49bcc..0000000000 --- a/tensorflow/python/data/kernel_tests/inputs_test.py +++ /dev/null @@ -1,149 +0,0 @@ -# Copyright 2018 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. -# ============================================================================== -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -from absl.testing import parameterized -import numpy as np - -from tensorflow.python.data.kernel_tests import test_base -from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.data.ops import readers -from tensorflow.python.data.util import nest -from tensorflow.python.framework import dtypes -from tensorflow.python.framework import sparse_tensor -from tensorflow.python.platform import test - - -class InputsTest(test_base.DatasetTestBase, parameterized.TestCase): - - @staticmethod - def make_apply_fn(dataset): - - def apply_fn(dataset): - - def _apply_fn(dataset): - return dataset.cache() - - return dataset.apply(_apply_fn) - - return apply_fn - - @staticmethod - def make_gen(): - - def gen(): - yield 42 - - return gen - - @staticmethod - def make_interleave_fn(dataset, num_parallel_calls=None): - - def interleave_fn(dataset): - return dataset.interleave( - lambda x: dataset_ops.Dataset.range(0), - cycle_length=2, - num_parallel_calls=num_parallel_calls) - - return interleave_fn - - @parameterized.named_parameters( - ("FixedLengthRecord", readers.FixedLengthRecordDataset("", 42)), - ("FromGenerator", - dataset_ops.Dataset.from_generator(make_gen.__func__(), dtypes.int32), - 1), - ("FromSparseTensorSlices", - dataset_ops.Dataset.from_sparse_tensor_slices( - sparse_tensor.SparseTensor( - indices=np.array([[0, 0], [1, 0], [2, 0]]), - values=np.array([0, 0, 0]), - dense_shape=np.array([3, 1])))), - ("FromTensors", dataset_ops.Dataset.from_tensors([42])), - ("FromTensorSlices", dataset_ops.Dataset.from_tensors([42])), - ("Range", dataset_ops.Dataset.range(10)), - ("TextLine", readers.TextLineDataset("")), - ("TFRecord", readers.TFRecordDataset(""), 1), - ) - def testDatasetSourceInputs(self, dataset, num_inputs=0): - self.assertEqual(num_inputs, len(dataset._inputs())) - - @parameterized.named_parameters( - ("Apply", make_apply_fn.__func__(dataset_ops.Dataset.range(0)), - dataset_ops.Dataset.range(0)), - ("Batch", lambda x: x.batch(10), dataset_ops.Dataset.range(0)), - ("Cache", lambda x: x.cache(), dataset_ops.Dataset.range(0)), - ("Filter", lambda x: x.filter(lambda x: True), - dataset_ops.Dataset.range(0)), - ("FlatMap", lambda x: x.flat_map(lambda x: dataset_ops.Dataset.range(0)), - dataset_ops.Dataset.range(0)), - ("Interleave", make_interleave_fn.__func__(dataset_ops.Dataset.range(0)), - dataset_ops.Dataset.range(0)), - ("Map", lambda x: x.map(lambda x: x), dataset_ops.Dataset.range(0)), - ("PaddedBatch", lambda x: x.padded_batch(10, []), - dataset_ops.Dataset.range(0)), - ("ParallelInterleave", - make_interleave_fn.__func__(dataset_ops.Dataset.range(0), 2), - dataset_ops.Dataset.range(0)), - ("ParallelMap", lambda x: x.map(lambda x: x, num_parallel_calls=2), - dataset_ops.Dataset.range(0)), - ("Repeat", lambda x: x.repeat(), dataset_ops.Dataset.range(0)), - ("Shuffle", lambda x: x.shuffle(10), dataset_ops.Dataset.range(0)), - ("Skip", lambda x: x.skip(1), dataset_ops.Dataset.range(0)), - ("Take", lambda x: x.take(1), dataset_ops.Dataset.range(0)), - ("Window", lambda x: x.window(10), dataset_ops.Dataset.range(0)), - ) - def testUnaryTransformationInputs(self, dataset_fn, input_dataset): - self.assertEqual([input_dataset], dataset_fn(input_dataset)._inputs()) - - @parameterized.named_parameters( - ("Concatenate", lambda x, y: x.concatenate(y), - dataset_ops.Dataset.range(0), dataset_ops.Dataset.range(1))) - def testBinaryTransformationInputs(self, dataset_fn, input1, input2): - self.assertEqual([input1, input2], dataset_fn(input1, input2)._inputs()) - - @parameterized.named_parameters( - ("ZipOne", dataset_ops.Dataset.zip, (dataset_ops.Dataset.range(0))), - ("ZipNest", dataset_ops.Dataset.zip, - (dataset_ops.Dataset.range(0), - (dataset_ops.Dataset.range(1), dataset_ops.Dataset.range(2)))), - ("ZipTuple", dataset_ops.Dataset.zip, - (dataset_ops.Dataset.range(0), dataset_ops.Dataset.range(1)))) - def testVariadicTransformationInputs(self, dataset_fn, input_datasets): - self.assertEqual( - nest.flatten(input_datasets), - dataset_fn(input_datasets)._inputs()) - - def testCollectInputs(self): - ds1 = dataset_ops.Dataset.range(0) - ds2 = ds1.concatenate(ds1) - ds3 = dataset_ops.Dataset.zip((ds2, ds1, ds2)) - - inputs = [] - queue = [ds3] - while queue: - ds = queue[0] - queue = queue[1:] - queue.extend(ds._inputs()) - inputs.append(ds) - - self.assertEqual(5, inputs.count(ds1)) - self.assertEqual(2, inputs.count(ds2)) - self.assertEqual(1, inputs.count(ds3)) - - -if __name__ == "__main__": - test.main() diff --git a/tensorflow/python/data/kernel_tests/interleave_dataset_op_test.py b/tensorflow/python/data/kernel_tests/interleave_test.py similarity index 85% rename from tensorflow/python/data/kernel_tests/interleave_dataset_op_test.py rename to tensorflow/python/data/kernel_tests/interleave_test.py index b911c249ce..c3450e6525 100644 --- a/tensorflow/python/data/kernel_tests/interleave_dataset_op_test.py +++ b/tensorflow/python/data/kernel_tests/interleave_test.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Tests for the experimental input pipeline ops.""" +"""Tests for `tf.data.Dataset.interleave()`.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function @@ -27,6 +27,7 @@ from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import errors from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import script_ops from tensorflow.python.ops import sparse_ops @@ -115,7 +116,7 @@ def _make_coordinated_sloppy_dataset(input_values, cycle_length, block_length, dataset = dataset_ops.Dataset.from_tensor_slices(input_values).repeat( 2).interleave(interleave_fn, cycle_length, block_length, num_parallel_calls).with_options(options) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) get_next = iterator.get_next() return get_next, coordination_events @@ -133,7 +134,8 @@ def _repeat(values, count): return [[value] * value for value in np.tile(values, count)] -class InterleaveDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): +@test_util.run_all_in_graph_and_eager_modes +class InterleaveTest(test_base.DatasetTestBase, parameterized.TestCase): @parameterized.named_parameters( ("1", [4, 5, 6], 1, 1, [ @@ -191,16 +193,11 @@ class InterleaveDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): count).interleave( lambda x: dataset_ops.Dataset.from_tensors(x).repeat(x), cycle_length, block_length, num_parallel_calls) - get_next = dataset.make_one_shot_iterator().get_next() - - with self.cached_session() as sess: - for expected_element in _interleave( - _repeat(input_values, count), cycle_length, block_length): - self.assertEqual(expected_element, sess.run(get_next)) - - for _ in range(2): - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + expected_output = [ + element for element in _interleave( + _repeat(input_values, count), cycle_length, block_length) + ] + self.assertDatasetProduces(dataset, expected_output) @parameterized.named_parameters( ("1", np.float32([1., np.nan, 2., np.nan, 3.]), 1, 3, None), @@ -223,17 +220,16 @@ class InterleaveDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): lambda x: array_ops.check_numerics(x, "message")).interleave( dataset_ops.Dataset.from_tensors, cycle_length, block_length, num_parallel_calls) - get_next = dataset.make_one_shot_iterator().get_next() + get_next = self.getNext(dataset) - with self.cached_session() as sess: - for value in input_values: - if np.isnan(value): - with self.assertRaises(errors.InvalidArgumentError): - sess.run(get_next) - else: - self.assertEqual(value, sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + for value in input_values: + if np.isnan(value): + with self.assertRaises(errors.InvalidArgumentError): + self.evaluate(get_next()) + else: + self.assertEqual(value, self.evaluate(get_next())) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) def testInterleaveSparse(self): @@ -245,18 +241,17 @@ class InterleaveDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): return dataset_ops.Dataset.from_tensor_slices( sparse_ops.sparse_to_dense(x.indices, x.dense_shape, x.values)) - iterator = ( - dataset_ops.Dataset.range(10).map(_map_fn).interleave( - _interleave_fn, cycle_length=1).make_one_shot_iterator()) - get_next = iterator.get_next() - - with self.cached_session() as sess: - for i in range(10): - for j in range(2): - expected = [i, 0] if j % 2 == 0 else [0, -i] - self.assertAllEqual(expected, sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + dataset = dataset_ops.Dataset.range(10).map(_map_fn).interleave( + _interleave_fn, cycle_length=1) + get_next = self.getNext(dataset) + for i in range(10): + for j in range(2): + expected = [i, 0] if j % 2 == 0 else [0, -i] + self.assertAllEqual(expected, self.evaluate(get_next())) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) @parameterized.named_parameters( ("1", np.int64([4, 5, 6]), 2, 1, 1), @@ -269,8 +264,8 @@ class InterleaveDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): ("8", np.int64([4, 0, 6]), 2, 3, 1), ("9", np.int64([4, 0, 6]), 2, 3, 2), ) - def testSloppyInterleaveInOrder(self, input_values, cycle_length, - block_length, num_parallel_calls): + def testSkipEagerSloppyInterleaveInOrder(self, input_values, cycle_length, + block_length, num_parallel_calls): get_next, coordination_events = _make_coordinated_sloppy_dataset( input_values, cycle_length, block_length, num_parallel_calls) config = config_pb2.ConfigProto( @@ -281,7 +276,7 @@ class InterleaveDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): _repeat(input_values, 2), cycle_length, block_length): coordination_events[expected_element].set() self.assertEqual(expected_element * expected_element, - sess.run(get_next)) + self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): sess.run(get_next) @@ -291,8 +286,8 @@ class InterleaveDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): ("3", np.int64([4, 5, 6]), 3, 2, 3), ("4", np.int64([4, 0, 6]), 2, 3, 2), ) - def testSloppyInterleaveOutOfOrder(self, input_values, cycle_length, - block_length, num_parallel_calls): + def testSkipEagerSloppyInterleaveOutOfOrder(self, input_values, cycle_length, + block_length, num_parallel_calls): get_next, coordination_events = _make_coordinated_sloppy_dataset( input_values, cycle_length, block_length, num_parallel_calls) config = config_pb2.ConfigProto( @@ -308,7 +303,7 @@ class InterleaveDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): for element in elements: coordination_events[element].set() - self.assertEqual(element * element, sess.run(get_next)) + self.assertEqual(element * element, self.evaluate(get_next)) with self.assertRaises(errors.OutOfRangeError): sess.run(get_next) diff --git a/tensorflow/python/data/kernel_tests/iterator_checkpoint_test.py b/tensorflow/python/data/kernel_tests/iterator_checkpoint_test.py new file mode 100644 index 0000000000..91b356691b --- /dev/null +++ b/tensorflow/python/data/kernel_tests/iterator_checkpoint_test.py @@ -0,0 +1,129 @@ +# 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. +# ============================================================================== +"""Checkpoint tests for `tf.data.Iterator`.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import functools +import os + +from tensorflow.python.data.kernel_tests import test_base +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.eager import context +from tensorflow.python.framework import errors +from tensorflow.python.framework import test_util +from tensorflow.python.ops import math_ops +from tensorflow.python.platform import test +from tensorflow.python.training import checkpoint_management +from tensorflow.python.training.checkpointable import util as checkpointable_utils + + +@test_util.run_all_in_graph_and_eager_modes +class IteratorCheckpointingTest(test_base.DatasetTestBase): + + def testSaveRestoreOneShotIterator(self): + checkpoint_directory = self.get_temp_dir() + checkpoint_prefix = os.path.join(checkpoint_directory, "ckpt") + dataset = dataset_ops.Dataset.from_tensor_slices([1, 2, 3, 4, 5, 6]).map( + math_ops.square).batch(2) + iterator = iter(dataset) if context.executing_eagerly( + ) else dataset_ops.make_one_shot_iterator(dataset) + get_next = iterator.get_next if context.executing_eagerly( + ) else functools.partial(self.evaluate, iterator.get_next()) + checkpoint = checkpointable_utils.Checkpoint(iterator=iterator) + self.assertAllEqual([1, 4], get_next()) + save_path = checkpoint.save(checkpoint_prefix) + self.assertAllEqual([9, 16], get_next()) + self.assertAllEqual([25, 36], get_next()) + checkpoint.restore(save_path).run_restore_ops() + self.assertAllEqual([9, 16], get_next()) + self.assertAllEqual([25, 36], get_next()) + with self.assertRaises(errors.OutOfRangeError): + get_next() + + def testSaveRestoreMultipleIterator(self): + checkpoint_directory = self.get_temp_dir() + checkpoint_prefix = os.path.join(checkpoint_directory, "ckpt") + dataset = dataset_ops.Dataset.from_tensor_slices( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) + dataset = dataset.map(math_ops.square).batch(2) + iterator_1 = iter(dataset) if context.executing_eagerly( + ) else dataset_ops.make_one_shot_iterator(dataset) + get_next_1 = iterator_1.get_next if context.executing_eagerly( + ) else functools.partial(self.evaluate, iterator_1.get_next()) + iterator_2 = iter(dataset) if context.executing_eagerly( + ) else dataset_ops.make_one_shot_iterator(dataset) + get_next_2 = iterator_2.get_next if context.executing_eagerly( + ) else functools.partial(self.evaluate, iterator_2.get_next()) + dataset_2 = dataset_ops.Dataset.range(10) + iterator_3 = iter(dataset_2) if context.executing_eagerly( + ) else dataset_ops.make_one_shot_iterator(dataset_2) + get_next_3 = iterator_3.get_next if context.executing_eagerly( + ) else functools.partial(self.evaluate, iterator_3.get_next()) + checkpoint = checkpointable_utils.Checkpoint( + iterator_1=iterator_1, iterator_2=iterator_2, iterator_3=iterator_3) + self.assertAllEqual([1, 4], get_next_1()) + self.assertAllEqual(0, get_next_3()) + self.assertAllEqual(1, get_next_3()) + self.assertAllEqual(2, get_next_3()) + save_path = checkpoint.save(checkpoint_prefix) + self.assertAllEqual([1, 4], get_next_2()) + self.assertAllEqual([9, 16], get_next_2()) + self.assertAllEqual(3, get_next_3()) + checkpoint.restore(save_path).run_restore_ops() + self.assertAllEqual([9, 16], get_next_1()) + self.assertAllEqual([1, 4], get_next_2()) + self.assertAllEqual(3, get_next_3()) + + def testRestoreExhaustedIterator(self): + checkpoint_directory = self.get_temp_dir() + checkpoint_prefix = os.path.join(checkpoint_directory, "ckpt") + dataset = dataset_ops.Dataset.range(3) + iterator = iter(dataset) if context.executing_eagerly( + ) else dataset_ops.make_one_shot_iterator(dataset) + get_next = iterator.get_next if context.executing_eagerly( + ) else functools.partial(self.evaluate, iterator.get_next()) + checkpoint = checkpointable_utils.Checkpoint(iterator=iterator) + self.assertAllEqual(0, get_next()) + self.assertAllEqual(1, get_next()) + save_path = checkpoint.save(checkpoint_prefix) + self.assertAllEqual(2, get_next()) + checkpoint.restore(save_path).run_restore_ops() + self.assertAllEqual(2, get_next()) + save_path = checkpoint.save(checkpoint_prefix) + checkpoint.restore(save_path).run_restore_ops() + with self.assertRaises(errors.OutOfRangeError): + get_next() + + def testRestoreInReconstructedIteratorInitializable(self): + checkpoint_directory = self.get_temp_dir() + checkpoint_prefix = os.path.join(checkpoint_directory, "ckpt") + dataset = dataset_ops.Dataset.range(10) + iterator = iter(dataset) if context.executing_eagerly( + ) else dataset_ops.make_initializable_iterator(dataset) + get_next = iterator.get_next + checkpoint = checkpointable_utils.Checkpoint(iterator=iterator) + for i in range(5): + checkpoint.restore( + checkpoint_management.latest_checkpoint( + checkpoint_directory)).initialize_or_restore() + for j in range(2): + self.assertEqual(i * 2 + j, self.evaluate(get_next())) + checkpoint.save(file_prefix=checkpoint_prefix) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/data/kernel_tests/iterator_ops_cluster_test.py b/tensorflow/python/data/kernel_tests/iterator_cluster_test.py similarity index 96% rename from tensorflow/python/data/kernel_tests/iterator_ops_cluster_test.py rename to tensorflow/python/data/kernel_tests/iterator_cluster_test.py index bf5fd781d6..728bed20a1 100644 --- a/tensorflow/python/data/kernel_tests/iterator_ops_cluster_test.py +++ b/tensorflow/python/data/kernel_tests/iterator_cluster_test.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Tests for the experimental input pipeline ops that need test_util.""" +"""Tests for `tf.data.Iterator` using distributed sessions.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function @@ -47,7 +47,7 @@ class IteratorClusterTest(test.TestCase): with ops.device("/job:worker/replica:0/task:0/cpu:1"): dataset_3 = dataset_ops.Dataset.from_tensor_slices([1, 2, 3]) - iterator_3 = dataset_3.make_one_shot_iterator() + iterator_3 = dataset_ops.make_one_shot_iterator(dataset_3) iterator_3_handle = iterator_3.string_handle() with ops.device("/job:worker/replica:0/task:0/cpu:0"): @@ -62,7 +62,7 @@ class IteratorClusterTest(test.TestCase): def _testRemoteIteratorHelper(self, device0, device1, target): with ops.device(device1): dataset_3 = dataset_ops.Dataset.from_tensor_slices([1, 2, 3]) - iterator_3 = dataset_3.make_one_shot_iterator() + iterator_3 = dataset_ops.make_one_shot_iterator(dataset_3) iterator_3_handle = iterator_3.string_handle() @function.Defun(dtypes.string) @@ -161,7 +161,7 @@ class IteratorClusterTest(test.TestCase): dataset_ops.Dataset.from_tensor_slices(components).map(_map_fn) .repeat(None).prefetch(10000)) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) init_op = iterator.initializer get_next = iterator.get_next() diff --git a/tensorflow/python/data/kernel_tests/iterator_ops_test.py b/tensorflow/python/data/kernel_tests/iterator_test.py similarity index 83% rename from tensorflow/python/data/kernel_tests/iterator_ops_test.py rename to tensorflow/python/data/kernel_tests/iterator_test.py index a2a3528cc6..916cf8bb45 100644 --- a/tensorflow/python/data/kernel_tests/iterator_ops_test.py +++ b/tensorflow/python/data/kernel_tests/iterator_test.py @@ -12,12 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Tests for the experimental input pipeline ops.""" +"""Tests for `tf.data.Iterator`.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function -import functools import os import warnings @@ -50,24 +49,24 @@ from tensorflow.python.ops import parsing_ops from tensorflow.python.ops import script_ops from tensorflow.python.ops import variables from tensorflow.python.platform import test -from tensorflow.python.training import checkpoint_management from tensorflow.python.training import server_lib -from tensorflow.python.training.checkpointable import util as checkpointable_utils from tensorflow.python.util import compat class IteratorTest(test.TestCase, parameterized.TestCase): + @test_util.run_deprecated_v1 def testNoGradients(self): component = constant_op.constant([1.]) side = constant_op.constant(0.) add = lambda x: x + side dataset = dataset_ops.Dataset.from_tensor_slices(component).map(add) - value = dataset.make_one_shot_iterator().get_next() + value = dataset_ops.make_one_shot_iterator(dataset).get_next() self.assertIsNone(gradients_impl.gradients(value, component)[0]) self.assertIsNone(gradients_impl.gradients(value, side)[0]) self.assertIsNone(gradients_impl.gradients(value, [component, side])[0]) + @test_util.run_deprecated_v1 def testCapturingStateInOneShotRaisesException(self): var = variables.Variable(37.0, name="myvar") dataset = ( @@ -76,8 +75,9 @@ class IteratorTest(test.TestCase, parameterized.TestCase): with self.assertRaisesRegexp( ValueError, r"`Dataset.make_one_shot_iterator\(\)` does not support " "datasets that capture stateful objects.+myvar"): - dataset.make_one_shot_iterator() + dataset_ops.make_one_shot_iterator(dataset) + @test_util.run_deprecated_v1 def testOneShotIterator(self): components = (np.arange(7), np.array([[1, 2, 3]]) * np.arange(7)[:, np.newaxis], @@ -86,9 +86,9 @@ class IteratorTest(test.TestCase, parameterized.TestCase): def _map_fn(x, y, z): return math_ops.square(x), math_ops.square(y), math_ops.square(z) - iterator = ( + iterator = dataset_ops.make_one_shot_iterator( dataset_ops.Dataset.from_tensor_slices(components).map(_map_fn) - .repeat(14).make_one_shot_iterator()) + .repeat(14)) get_next = iterator.get_next() self.assertEqual([c.shape[1:] for c in components], @@ -103,6 +103,7 @@ class IteratorTest(test.TestCase, parameterized.TestCase): with self.assertRaises(errors.OutOfRangeError): sess.run(get_next) + @test_util.run_deprecated_v1 def testOneShotIteratorCaptureByValue(self): components = (np.arange(7), np.array([[1, 2, 3]]) * np.arange(7)[:, np.newaxis], @@ -112,9 +113,9 @@ class IteratorTest(test.TestCase, parameterized.TestCase): def _map_fn(x, y, z): return math_ops.square(x), math_ops.square(y), math_ops.square(z) - iterator = ( + iterator = dataset_ops.make_one_shot_iterator( dataset_ops.Dataset.from_tensor_slices(tensor_components) - .map(_map_fn).repeat(14).make_one_shot_iterator()) + .map(_map_fn).repeat(14)) get_next = iterator.get_next() self.assertEqual([c.shape[1:] for c in components], @@ -139,9 +140,9 @@ class IteratorTest(test.TestCase, parameterized.TestCase): def _map_fn(x, y, z): return math_ops.square(x), math_ops.square(y), math_ops.square(z) - iterator = ( + iterator = dataset_ops.make_one_shot_iterator( dataset_ops.Dataset.from_tensor_slices(components) - .map(_map_fn).repeat(14).make_one_shot_iterator()) + .map(_map_fn).repeat(14)) return iterator.get_next() server = server_lib.Server.create_local_server() @@ -165,9 +166,10 @@ class IteratorTest(test.TestCase, parameterized.TestCase): with self.assertRaises(errors.OutOfRangeError): sess.run(get_next) + @test_util.run_deprecated_v1 def testOneShotIteratorNonBlocking(self): dataset = dataset_ops.Dataset.from_tensors([1, 2, 3]).map(lambda x: x * x) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) next_element = iterator.get_next() # Create a session with a single thread to ensure that the @@ -203,12 +205,13 @@ class IteratorTest(test.TestCase, parameterized.TestCase): len([None for r in results if r is None])) self.assertAllEqual([[1, 4, 9]], [r for r in results if r is not None]) + @test_util.run_deprecated_v1 def testOneShotIteratorInitializerFails(self): # Define a dataset whose initialization will always fail. dataset = dataset_ops.Dataset.from_tensors( array_ops.check_numerics( constant_op.constant(1.0) / constant_op.constant(0.0), "oops")) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) next_element = iterator.get_next() with self.cached_session() as sess: @@ -283,11 +286,11 @@ class IteratorTest(test.TestCase, parameterized.TestCase): with self.assertRaises(errors.OutOfRangeError): sess.run(get_next) + @test_util.run_deprecated_v1 def testNotInitializedError(self): components = (np.array(1), np.array([1, 2, 3]), np.array(37.0)) - iterator = ( - dataset_ops.Dataset.from_tensors(components) - .make_initializable_iterator()) + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.from_tensors(components)) get_next = iterator.get_next() with self.cached_session() as sess: @@ -295,6 +298,7 @@ class IteratorTest(test.TestCase, parameterized.TestCase): "iterator has not been initialized"): sess.run(get_next) + @test_util.run_deprecated_v1 def testReinitializableIterator(self): dataset_3 = dataset_ops.Dataset.from_tensors( constant_op.constant([1, 2, 3])) @@ -334,6 +338,33 @@ class IteratorTest(test.TestCase, parameterized.TestCase): with self.assertRaises(errors.OutOfRangeError): sess.run(get_next) + @test_util.run_deprecated_v1 + def testReinitializableIteratorWithFunctions(self): + + def g(): + for i in range(10): + yield i + + iterator = iterator_ops.Iterator.from_structure(dtypes.int64, []) + next_element = iterator.get_next() + + with self.cached_session() as sess: + dataset_1 = dataset_ops.Dataset.from_generator( + g, output_types=dtypes.int64) + sess.run(iterator.make_initializer(dataset_1)) + for expected in range(10): + self.assertEqual(expected, sess.run(next_element)) + with self.assertRaises(errors.OutOfRangeError): + sess.run(next_element) + + dataset_2 = dataset_ops.Dataset.from_generator( + g, output_types=dtypes.int64) + sess.run(iterator.make_initializer(dataset_2)) + for expected in range(10): + self.assertEqual(expected, sess.run(next_element)) + with self.assertRaises(errors.OutOfRangeError): + sess.run(next_element) + def testReinitializableIteratorStaticErrors(self): # Non-matching structure for types and shapes. with self.assertRaises(TypeError): @@ -367,12 +398,13 @@ class IteratorTest(test.TestCase, parameterized.TestCase): (constant_op.constant([1, 2, 3], dtype=dtypes.int64), constant_op.constant([4., 5., 6., 7.], dtype=dtypes.float64)))) + @test_util.run_deprecated_v1 def testIteratorStringHandle(self): dataset_3 = dataset_ops.Dataset.from_tensor_slices([1, 2, 3]) dataset_4 = dataset_ops.Dataset.from_tensor_slices([10, 20, 30, 40]) - iterator_3 = dataset_3.make_one_shot_iterator() - iterator_4 = dataset_4.make_one_shot_iterator() + iterator_3 = dataset_ops.make_one_shot_iterator(dataset_3) + iterator_4 = dataset_ops.make_one_shot_iterator(dataset_4) handle_placeholder = array_ops.placeholder(dtypes.string, shape=[]) feedable_iterator = iterator_ops.Iterator.from_string_handle( @@ -422,13 +454,14 @@ class IteratorTest(test.TestCase, parameterized.TestCase): sess.run( next_element, feed_dict={handle_placeholder: iterator_4_handle}) + @test_util.run_deprecated_v1 def testIteratorStringHandleFuture(self): with forward_compat.forward_compatibility_horizon(2018, 8, 4): dataset_3 = dataset_ops.Dataset.from_tensor_slices([1, 2, 3]) dataset_4 = dataset_ops.Dataset.from_tensor_slices([10, 20, 30, 40]) - iterator_3 = dataset_3.make_one_shot_iterator() - iterator_4 = dataset_4.make_one_shot_iterator() + iterator_3 = dataset_ops.make_one_shot_iterator(dataset_3) + iterator_4 = dataset_ops.make_one_shot_iterator(dataset_4) handle_placeholder = array_ops.placeholder(dtypes.string, shape=[]) feedable_iterator = iterator_ops.Iterator.from_string_handle( @@ -485,10 +518,11 @@ class IteratorTest(test.TestCase, parameterized.TestCase): sess.run( next_element, feed_dict={handle_placeholder: iterator_4_handle}) + @test_util.run_deprecated_v1 def testIteratorStringHandleReuseTensorObject(self): dataset = dataset_ops.Dataset.from_tensor_slices([1, 2, 3]) - one_shot_iterator = dataset.make_one_shot_iterator() - initializable_iterator = dataset.make_initializable_iterator() + one_shot_iterator = dataset_ops.make_one_shot_iterator(dataset) + initializable_iterator = dataset_ops.make_initializable_iterator(dataset) structure_iterator = iterator_ops.Iterator.from_structure( dataset.output_types) @@ -513,6 +547,7 @@ class IteratorTest(test.TestCase, parameterized.TestCase): self.assertEqual("foo_1", handle_with_same_name.op.name) self.assertIsNot(handle_with_name, handle_with_same_name) + @test_util.run_deprecated_v1 def testIteratorStringHandleError(self): dataset_int_scalar = ( dataset_ops.Dataset.from_tensor_slices([1, 2, 3]).repeat()) @@ -528,10 +563,10 @@ class IteratorTest(test.TestCase, parameterized.TestCase): handle_placeholder, dtypes.int32) with self.cached_session() as sess: - handle_int_scalar = sess.run( - dataset_int_scalar.make_one_shot_iterator().string_handle()) - handle_float_vector = sess.run( - dataset_float_vector.make_one_shot_iterator().string_handle()) + handle_int_scalar = sess.run(dataset_ops.make_one_shot_iterator( + dataset_int_scalar).string_handle()) + handle_float_vector = sess.run(dataset_ops.make_one_shot_iterator( + dataset_float_vector).string_handle()) self.assertEqual(1, sess.run( @@ -553,13 +588,14 @@ class IteratorTest(test.TestCase, parameterized.TestCase): feedable_int_vector.get_next(), feed_dict={handle_placeholder: handle_float_vector})) + @test_util.run_deprecated_v1 def testRemoteIteratorUsingRemoteCallOpDirectSession(self): worker_config = config_pb2.ConfigProto() worker_config.device_count["CPU"] = 3 with ops.device("/job:localhost/replica:0/task:0/cpu:1"): dataset_3 = dataset_ops.Dataset.from_tensor_slices([1, 2, 3]) - iterator_3 = dataset_3.make_one_shot_iterator() + iterator_3 = dataset_ops.make_one_shot_iterator(dataset_3) iterator_3_handle = iterator_3.string_handle() @function.Defun(dtypes.string) @@ -609,6 +645,7 @@ class IteratorTest(test.TestCase, parameterized.TestCase): target_placeholder: "/job:localhost/replica:0/task:0/cpu:1" }) + @test_util.run_deprecated_v1 def testRemoteIteratorUsingRemoteCallOpMultiWorkers(self): s1 = server_lib.Server.create_local_server() s2 = server_lib.Server.create_local_server() @@ -631,7 +668,7 @@ class IteratorTest(test.TestCase, parameterized.TestCase): for device in worker_devices: with ops.device(device): src = dataset_ops.Dataset.from_tensor_slices([device]) - itr = src.make_one_shot_iterator() + itr = dataset_ops.make_one_shot_iterator(src) itr_handles.append(itr.string_handle()) targets = dataset_ops.Dataset.from_tensor_slices(worker_devices) @@ -649,7 +686,7 @@ class IteratorTest(test.TestCase, parameterized.TestCase): with ops.device("/job:client"): client_dataset = dataset_ops.Dataset.zip((targets, handles)).map(map_fn) - itr = client_dataset.make_initializable_iterator() + itr = dataset_ops.make_initializable_iterator(client_dataset) n = itr.get_next() with session.Session(s3.target, config=config) as sess: @@ -667,7 +704,7 @@ class IteratorTest(test.TestCase, parameterized.TestCase): with ops.device("/job:localhost/replica:0/task:0/cpu:0"): dataset_3 = dataset_ops.Dataset.from_tensor_slices([1, 2, 3]) - iterator_3 = dataset_3.make_one_shot_iterator() + iterator_3 = dataset_ops.make_one_shot_iterator(dataset_3) iterator_3_handle = iterator_3.string_handle() def _encode_raw(byte_array): @@ -716,6 +753,7 @@ class IteratorTest(test.TestCase, parameterized.TestCase): target_placeholder: "/job:localhost/replica:0/task:0/cpu:0" }) + @test_util.run_deprecated_v1 def testIncorrectIteratorRestore(self): def _path(): @@ -738,8 +776,8 @@ class IteratorTest(test.TestCase, parameterized.TestCase): def _build_range_dataset_graph(): start = 1 stop = 10 - iterator = dataset_ops.Dataset.range(start, - stop).make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.range(start, stop)) init_op = iterator.initializer get_next = iterator.get_next() save_op = _save_op(iterator._iterator_resource) @@ -748,8 +786,8 @@ class IteratorTest(test.TestCase, parameterized.TestCase): def _build_reader_dataset_graph(): filenames = ["test"] # Does not exist but we don't care in this test. - iterator = readers.FixedLengthRecordDataset( - filenames, 1, 0, 0).make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator( + readers.FixedLengthRecordDataset(filenames, 1, 0, 0)) init_op = iterator.initializer get_next_op = iterator.get_next() save_op = _save_op(iterator._iterator_resource) @@ -774,8 +812,9 @@ class IteratorTest(test.TestCase, parameterized.TestCase): with self.assertRaises(errors.InvalidArgumentError): sess.run(restore_op) + @test_util.run_deprecated_v1 def testRepeatedGetNextWarning(self): - iterator = dataset_ops.Dataset.range(10).make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset_ops.Dataset.range(10)) warnings.simplefilter("always") with warnings.catch_warnings(record=True) as w: for _ in range(100): @@ -818,8 +857,8 @@ class IteratorTest(test.TestCase, parameterized.TestCase): expected_output_classes, expected_output_types, expected_output_shapes): tf_value = tf_value_fn() - iterator = dataset_ops.Dataset.from_tensors( - tf_value).make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator( + dataset_ops.Dataset.from_tensors(tf_value)) self.assertTrue(expected_element_structure.is_compatible_with( iterator._element_structure)) @@ -832,100 +871,11 @@ class IteratorTest(test.TestCase, parameterized.TestCase): def testIteratorGetNextName(self): with ops.Graph().as_default(): - iterator = dataset_ops.Dataset.from_tensors(37.0).make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator( + dataset_ops.Dataset.from_tensors(37.0)) next_element = iterator.get_next(name="overridden_name") self.assertEqual("overridden_name", next_element.op.name) -class IteratorCheckpointingTest(test.TestCase): - - @test_util.run_in_graph_and_eager_modes - def testSaveRestoreOneShotIterator(self): - checkpoint_directory = self.get_temp_dir() - checkpoint_prefix = os.path.join(checkpoint_directory, "ckpt") - dataset = dataset_ops.Dataset.from_tensor_slices([1, 2, 3, 4, 5, 6]).map( - math_ops.square).batch(2) - iterator = dataset.make_one_shot_iterator() - get_next = iterator.get_next if context.executing_eagerly( - ) else functools.partial(self.evaluate, iterator.get_next()) - checkpoint = checkpointable_utils.Checkpoint(iterator=iterator) - self.assertAllEqual([1, 4], get_next()) - save_path = checkpoint.save(checkpoint_prefix) - self.assertAllEqual([9, 16], get_next()) - self.assertAllEqual([25, 36], get_next()) - checkpoint.restore(save_path).run_restore_ops() - self.assertAllEqual([9, 16], get_next()) - self.assertAllEqual([25, 36], get_next()) - with self.assertRaises(errors.OutOfRangeError): - get_next() - - @test_util.run_in_graph_and_eager_modes - def testSaveRestoreMultipleIterator(self): - checkpoint_directory = self.get_temp_dir() - checkpoint_prefix = os.path.join(checkpoint_directory, "ckpt") - dataset = dataset_ops.Dataset.from_tensor_slices( - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) - dataset = dataset.map(math_ops.square).batch(2) - iterator_1 = dataset.make_one_shot_iterator() - get_next_1 = iterator_1.get_next if context.executing_eagerly( - ) else functools.partial(self.evaluate, iterator_1.get_next()) - iterator_2 = dataset.make_one_shot_iterator() - get_next_2 = iterator_2.get_next if context.executing_eagerly( - ) else functools.partial(self.evaluate, iterator_2.get_next()) - dataset_2 = dataset_ops.Dataset.range(10) - iterator_3 = dataset_2.make_one_shot_iterator() - get_next_3 = iterator_3.get_next if context.executing_eagerly( - ) else functools.partial(self.evaluate, iterator_3.get_next()) - checkpoint = checkpointable_utils.Checkpoint( - iterator_1=iterator_1, iterator_2=iterator_2, iterator_3=iterator_3) - self.assertAllEqual([1, 4], get_next_1()) - self.assertAllEqual(0, get_next_3()) - self.assertAllEqual(1, get_next_3()) - self.assertAllEqual(2, get_next_3()) - save_path = checkpoint.save(checkpoint_prefix) - self.assertAllEqual([1, 4], get_next_2()) - self.assertAllEqual([9, 16], get_next_2()) - self.assertAllEqual(3, get_next_3()) - checkpoint.restore(save_path).run_restore_ops() - self.assertAllEqual([9, 16], get_next_1()) - self.assertAllEqual([1, 4], get_next_2()) - self.assertAllEqual(3, get_next_3()) - - @test_util.run_in_graph_and_eager_modes - def testRestoreExhaustedIterator(self): - checkpoint_directory = self.get_temp_dir() - checkpoint_prefix = os.path.join(checkpoint_directory, "ckpt") - dataset = dataset_ops.Dataset.range(3) - iterator = dataset.make_one_shot_iterator() - get_next = iterator.get_next if context.executing_eagerly( - ) else functools.partial(self.evaluate, iterator.get_next()) - checkpoint = checkpointable_utils.Checkpoint(iterator=iterator) - self.assertAllEqual(0, get_next()) - self.assertAllEqual(1, get_next()) - save_path = checkpoint.save(checkpoint_prefix) - self.assertAllEqual(2, get_next()) - checkpoint.restore(save_path).run_restore_ops() - self.assertAllEqual(2, get_next()) - save_path = checkpoint.save(checkpoint_prefix) - checkpoint.restore(save_path).run_restore_ops() - with self.assertRaises(errors.OutOfRangeError): - get_next() - - def testRestoreInReconstructedIteratorInitializable(self): - checkpoint_directory = self.get_temp_dir() - checkpoint_prefix = os.path.join(checkpoint_directory, "ckpt") - dataset = dataset_ops.Dataset.range(10) - iterator = dataset.make_initializable_iterator() - get_next = iterator.get_next() - checkpoint = checkpointable_utils.Checkpoint(iterator=iterator) - for i in range(5): - with self.cached_session() as sess: - checkpoint.restore(checkpoint_management.latest_checkpoint( - checkpoint_directory)).initialize_or_restore(sess) - for j in range(2): - self.assertEqual(i * 2 + j, sess.run(get_next)) - checkpoint.save(file_prefix=checkpoint_prefix) - - if __name__ == "__main__": test.main() diff --git a/tensorflow/python/data/kernel_tests/list_files_dataset_op_test.py b/tensorflow/python/data/kernel_tests/list_files_dataset_op_test.py deleted file mode 100644 index b58c1444da..0000000000 --- a/tensorflow/python/data/kernel_tests/list_files_dataset_op_test.py +++ /dev/null @@ -1,291 +0,0 @@ -# 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. -# ============================================================================== -"""Tests for the experimental input pipeline ops.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -from os import path -import shutil -import tempfile - -from tensorflow.python.data.kernel_tests import test_base -from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.framework import dtypes -from tensorflow.python.framework import errors -from tensorflow.python.ops import array_ops -from tensorflow.python.platform import test -from tensorflow.python.util import compat - - -class ListFilesDatasetOpTest(test_base.DatasetTestBase): - - def setUp(self): - self.tmp_dir = tempfile.mkdtemp() - - def tearDown(self): - shutil.rmtree(self.tmp_dir, ignore_errors=True) - - def _touchTempFiles(self, filenames): - for filename in filenames: - open(path.join(self.tmp_dir, filename), 'a').close() - - def testEmptyDirectory(self): - dataset = dataset_ops.Dataset.list_files(path.join(self.tmp_dir, '*')) - with self.cached_session() as sess: - itr = dataset.make_one_shot_iterator() - next_element = itr.get_next() - with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) - - def testSimpleDirectory(self): - filenames = ['a', 'b', 'c'] - self._touchTempFiles(filenames) - - dataset = dataset_ops.Dataset.list_files(path.join(self.tmp_dir, '*')) - with self.cached_session() as sess: - itr = dataset.make_one_shot_iterator() - next_element = itr.get_next() - - full_filenames = [] - produced_filenames = [] - for filename in filenames: - full_filenames.append( - compat.as_bytes(path.join(self.tmp_dir, filename))) - produced_filenames.append(compat.as_bytes(sess.run(next_element))) - self.assertItemsEqual(full_filenames, produced_filenames) - with self.assertRaises(errors.OutOfRangeError): - sess.run(itr.get_next()) - - def testSimpleDirectoryNotShuffled(self): - filenames = ['b', 'c', 'a'] - self._touchTempFiles(filenames) - - dataset = dataset_ops.Dataset.list_files( - path.join(self.tmp_dir, '*'), shuffle=False) - with self.cached_session() as sess: - itr = dataset.make_one_shot_iterator() - next_element = itr.get_next() - - for filename in sorted(filenames): - self.assertEqual(compat.as_bytes(path.join(self.tmp_dir, filename)), - sess.run(next_element)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(itr.get_next()) - - def testFixedSeedResultsInRepeatableOrder(self): - filenames = ['a', 'b', 'c'] - self._touchTempFiles(filenames) - - dataset = dataset_ops.Dataset.list_files( - path.join(self.tmp_dir, '*'), shuffle=True, seed=37) - with self.cached_session() as sess: - itr = dataset.make_initializable_iterator() - next_element = itr.get_next() - - full_filenames = [compat.as_bytes(path.join(self.tmp_dir, filename)) - for filename in filenames] - - all_produced_filenames = [] - for _ in range(3): - produced_filenames = [] - sess.run(itr.initializer) - try: - while True: - produced_filenames.append(sess.run(next_element)) - except errors.OutOfRangeError: - pass - all_produced_filenames.append(produced_filenames) - - # Each run should produce the same set of filenames, which may be - # different from the order of `full_filenames`. - self.assertItemsEqual(full_filenames, all_produced_filenames[0]) - # However, the different runs should produce filenames in the same order - # as each other. - self.assertEqual(all_produced_filenames[0], all_produced_filenames[1]) - self.assertEqual(all_produced_filenames[0], all_produced_filenames[2]) - - def testEmptyDirectoryInitializer(self): - filename_placeholder = array_ops.placeholder(dtypes.string, shape=[]) - dataset = dataset_ops.Dataset.list_files(filename_placeholder) - - with self.cached_session() as sess: - itr = dataset.make_initializable_iterator() - with self.assertRaisesRegexp( - errors.InvalidArgumentError, 'No files matched pattern: '): - sess.run( - itr.initializer, - feed_dict={filename_placeholder: path.join(self.tmp_dir, '*')}) - - def testSimpleDirectoryInitializer(self): - filenames = ['a', 'b', 'c'] - self._touchTempFiles(filenames) - - filename_placeholder = array_ops.placeholder(dtypes.string, shape=[]) - dataset = dataset_ops.Dataset.list_files(filename_placeholder) - - with self.cached_session() as sess: - itr = dataset.make_initializable_iterator() - next_element = itr.get_next() - sess.run( - itr.initializer, - feed_dict={filename_placeholder: path.join(self.tmp_dir, '*')}) - - full_filenames = [] - produced_filenames = [] - for filename in filenames: - full_filenames.append( - compat.as_bytes(path.join(self.tmp_dir, filename))) - produced_filenames.append(compat.as_bytes(sess.run(next_element))) - - self.assertItemsEqual(full_filenames, produced_filenames) - - with self.assertRaises(errors.OutOfRangeError): - sess.run(itr.get_next()) - - def testFileSuffixes(self): - filenames = ['a.txt', 'b.py', 'c.py', 'd.pyc'] - self._touchTempFiles(filenames) - - filename_placeholder = array_ops.placeholder(dtypes.string, shape=[]) - dataset = dataset_ops.Dataset.list_files(filename_placeholder) - - with self.cached_session() as sess: - itr = dataset.make_initializable_iterator() - next_element = itr.get_next() - sess.run( - itr.initializer, - feed_dict={filename_placeholder: path.join(self.tmp_dir, '*.py')}) - - full_filenames = [] - produced_filenames = [] - for filename in filenames[1:-1]: - full_filenames.append( - compat.as_bytes(path.join(self.tmp_dir, filename))) - produced_filenames.append(compat.as_bytes(sess.run(next_element))) - self.assertItemsEqual(full_filenames, produced_filenames) - - with self.assertRaises(errors.OutOfRangeError): - sess.run(itr.get_next()) - - def testFileMiddles(self): - filenames = ['a.txt', 'b.py', 'c.pyc'] - self._touchTempFiles(filenames) - - filename_placeholder = array_ops.placeholder(dtypes.string, shape=[]) - dataset = dataset_ops.Dataset.list_files(filename_placeholder) - - with self.cached_session() as sess: - itr = dataset.make_initializable_iterator() - next_element = itr.get_next() - sess.run( - itr.initializer, - feed_dict={filename_placeholder: path.join(self.tmp_dir, '*.py*')}) - - full_filenames = [] - produced_filenames = [] - for filename in filenames[1:]: - full_filenames.append( - compat.as_bytes(path.join(self.tmp_dir, filename))) - produced_filenames.append(compat.as_bytes(sess.run(next_element))) - - self.assertItemsEqual(full_filenames, produced_filenames) - - with self.assertRaises(errors.OutOfRangeError): - sess.run(itr.get_next()) - - def testNoShuffle(self): - filenames = ['a', 'b', 'c'] - self._touchTempFiles(filenames) - - # Repeat the list twice and ensure that the order is the same each time. - # NOTE(mrry): This depends on an implementation detail of `list_files()`, - # which is that the list of files is captured when the iterator is - # initialized. Otherwise, or if e.g. the iterator were initialized more than - # once, it's possible that the non-determinism of `tf.matching_files()` - # would cause this test to fail. However, it serves as a useful confirmation - # that the `shuffle=False` argument is working as intended. - # TODO(b/73959787): Provide some ordering guarantees so that this test is - # more meaningful. - dataset = dataset_ops.Dataset.list_files( - path.join(self.tmp_dir, '*'), shuffle=False).repeat(2) - with self.cached_session() as sess: - itr = dataset.make_one_shot_iterator() - next_element = itr.get_next() - - full_filenames = [] - produced_filenames = [] - for filename in filenames * 2: - full_filenames.append( - compat.as_bytes(path.join(self.tmp_dir, filename))) - produced_filenames.append(compat.as_bytes(sess.run(next_element))) - with self.assertRaises(errors.OutOfRangeError): - sess.run(itr.get_next()) - self.assertItemsEqual(full_filenames, produced_filenames) - self.assertEqual(produced_filenames[:len(filenames)], - produced_filenames[len(filenames):]) - - def testMultiplePatternsAsList(self): - filenames = ['a.txt', 'b.py', 'c.py', 'd.pyc'] - self._touchTempFiles(filenames) - - patterns = [path.join(self.tmp_dir, pat) for pat in ['*.py', '*.txt']] - dataset = dataset_ops.Dataset.list_files(patterns) - with self.cached_session() as sess: - itr = dataset.make_one_shot_iterator() - next_element = itr.get_next() - - full_filenames = [] - produced_filenames = [] - for filename in filenames[:-1]: - full_filenames.append( - compat.as_bytes(path.join(self.tmp_dir, filename))) - produced_filenames.append(compat.as_bytes(sess.run(next_element))) - self.assertItemsEqual(full_filenames, produced_filenames) - - with self.assertRaises(errors.OutOfRangeError): - sess.run(itr.get_next()) - - def testMultiplePatternsAsTensor(self): - filenames = ['a.txt', 'b.py', 'c.py', 'd.pyc'] - self._touchTempFiles(filenames) - - filename_placeholder = array_ops.placeholder( - dtypes.string, shape=[ - 2, - ]) - dataset = dataset_ops.Dataset.list_files(filename_placeholder) - - with self.cached_session() as sess: - itr = dataset.make_initializable_iterator() - next_element = itr.get_next() - patterns = [path.join(self.tmp_dir, pat) for pat in ['*.py', '*.txt']] - sess.run(itr.initializer, feed_dict={filename_placeholder: patterns}) - - full_filenames = [] - produced_filenames = [] - for filename in filenames[:-1]: - full_filenames.append( - compat.as_bytes(path.join(self.tmp_dir, filename))) - produced_filenames.append(compat.as_bytes(sess.run(next_element))) - self.assertItemsEqual(full_filenames, produced_filenames) - - with self.assertRaises(errors.OutOfRangeError): - sess.run(itr.get_next()) - - -if __name__ == '__main__': - test.main() diff --git a/tensorflow/python/data/kernel_tests/list_files_test.py b/tensorflow/python/data/kernel_tests/list_files_test.py new file mode 100644 index 0000000000..789f1ab6de --- /dev/null +++ b/tensorflow/python/data/kernel_tests/list_files_test.py @@ -0,0 +1,214 @@ +# 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. +# ============================================================================== +"""Tests for `tf.data.Dataset.list_files()`.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from os import path +import shutil +import tempfile + +from tensorflow.python.data.kernel_tests import test_base +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.framework import errors +from tensorflow.python.framework import test_util +from tensorflow.python.platform import test +from tensorflow.python.util import compat + + +@test_util.run_all_in_graph_and_eager_modes +class ListFilesTest(test_base.DatasetTestBase): + + def setUp(self): + self.tmp_dir = tempfile.mkdtemp() + + def tearDown(self): + shutil.rmtree(self.tmp_dir, ignore_errors=True) + + def _touchTempFiles(self, filenames): + for filename in filenames: + open(path.join(self.tmp_dir, filename), 'a').close() + + # Note: eager mode fails in assertion error same as initializer in graph mode. + @test_util.run_deprecated_v1 + def testSkipEagerEmptyDirectory(self): + dataset = dataset_ops.Dataset.list_files(path.join(self.tmp_dir, '*')) + self.assertDatasetProduces(dataset, expected_output=[]) + + def testSimpleDirectory(self): + filenames = ['a', 'b', 'c'] + self._touchTempFiles(filenames) + + dataset = dataset_ops.Dataset.list_files(path.join(self.tmp_dir, '*')) + self.assertDatasetProduces( + dataset, + expected_output=[ + compat.as_bytes(path.join(self.tmp_dir, filename)) + for filename in filenames + ], + assert_items_equal=True) + + def testSimpleDirectoryNotShuffled(self): + filenames = ['b', 'c', 'a'] + self._touchTempFiles(filenames) + + dataset = dataset_ops.Dataset.list_files( + path.join(self.tmp_dir, '*'), shuffle=False) + self.assertDatasetProduces( + dataset, + expected_output=[ + compat.as_bytes(path.join(self.tmp_dir, filename)) + for filename in sorted(filenames) + ]) + + def testFixedSeedResultsInRepeatableOrder(self): + filenames = ['a', 'b', 'c'] + self._touchTempFiles(filenames) + + dataset = dataset_ops.Dataset.list_files( + path.join(self.tmp_dir, '*'), shuffle=True, seed=37) + + full_filenames = [compat.as_bytes(path.join(self.tmp_dir, filename)) + for filename in filenames] + + all_produced_filenames = [] + for _ in range(3): + produced_filenames = [] + next_element = self.getNext(dataset, requires_initialization=True) + try: + while True: + produced_filenames.append(self.evaluate(next_element())) + except errors.OutOfRangeError: + pass + all_produced_filenames.append(produced_filenames) + + # Each run should produce the same set of filenames, which may be + # different from the order of `full_filenames`. + self.assertItemsEqual(full_filenames, all_produced_filenames[0]) + # However, the different runs should produce filenames in the same order + # as each other. + self.assertEqual(all_produced_filenames[0], all_produced_filenames[1]) + self.assertEqual(all_produced_filenames[0], all_produced_filenames[2]) + + # TODO(b/117581999): eager mode assertion fail wrapped, debug. + def tesSkipEagerEmptyDirectoryInitializer(self): + dataset = dataset_ops.Dataset.list_files(path.join(self.tmp_dir, '*')) + self.assertDatasetProduces( + dataset, + expected_error=(errors.InvalidArgumentError, + 'No files matched pattern'), + requires_initialization=True) + + def testSimpleDirectoryInitializer(self): + filenames = ['a', 'b', 'c'] + self._touchTempFiles(filenames) + + dataset = dataset_ops.Dataset.list_files(path.join(self.tmp_dir, '*')) + self.assertDatasetProduces( + dataset, + expected_output=[ + compat.as_bytes(path.join(self.tmp_dir, filename)) + for filename in filenames + ], + assert_items_equal=True) + + def testFileSuffixes(self): + filenames = ['a.txt', 'b.py', 'c.py', 'd.pyc'] + self._touchTempFiles(filenames) + + dataset = dataset_ops.Dataset.list_files(path.join(self.tmp_dir, '*.py')) + self.assertDatasetProduces( + dataset, + expected_output=[ + compat.as_bytes(path.join(self.tmp_dir, filename)) + for filename in filenames[1:-1] + ], + assert_items_equal=True) + + def testFileMiddles(self): + filenames = ['a.txt', 'b.py', 'c.pyc'] + self._touchTempFiles(filenames) + + dataset = dataset_ops.Dataset.list_files(path.join(self.tmp_dir, '*.py*')) + self.assertDatasetProduces( + dataset, + expected_output=[ + compat.as_bytes(path.join(self.tmp_dir, filename)) + for filename in filenames[1:] + ], + assert_items_equal=True) + + def testNoShuffle(self): + filenames = ['a', 'b', 'c'] + self._touchTempFiles(filenames) + + # Repeat the list twice and ensure that the order is the same each time. + # NOTE(mrry): This depends on an implementation detail of `list_files()`, + # which is that the list of files is captured when the iterator is + # initialized. Otherwise, or if e.g. the iterator were initialized more than + # once, it's possible that the non-determinism of `tf.matching_files()` + # would cause this test to fail. However, it serves as a useful confirmation + # that the `shuffle=False` argument is working as intended. + # TODO(b/73959787): Provide some ordering guarantees so that this test is + # more meaningful. + dataset = dataset_ops.Dataset.list_files( + path.join(self.tmp_dir, '*'), shuffle=False).repeat(2) + next_element = self.getNext(dataset) + + full_filenames = [] + produced_filenames = [] + for filename in filenames * 2: + full_filenames.append(compat.as_bytes(path.join(self.tmp_dir, filename))) + produced_filenames.append(compat.as_bytes(self.evaluate(next_element()))) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(next_element()) + self.assertItemsEqual(full_filenames, produced_filenames) + self.assertEqual(produced_filenames[:len(filenames)], + produced_filenames[len(filenames):]) + + def testMultiplePatternsAsList(self): + filenames = ['a.txt', 'b.py', 'c.py', 'd.pyc'] + self._touchTempFiles(filenames) + + patterns = [path.join(self.tmp_dir, pat) for pat in ['*.py', '*.txt']] + dataset = dataset_ops.Dataset.list_files(patterns) + self.assertDatasetProduces( + dataset, + expected_output=[ + compat.as_bytes(path.join(self.tmp_dir, filename)) + for filename in filenames[:-1] + ], + assert_items_equal=True) + + def testMultiplePatternsAsTensor(self): + filenames = ['a.txt', 'b.py', 'c.py', 'd.pyc'] + self._touchTempFiles(filenames) + + dataset = dataset_ops.Dataset.list_files( + [path.join(self.tmp_dir, pat) for pat in ['*.py', '*.txt']]) + self.assertDatasetProduces( + dataset, + expected_output=[ + compat.as_bytes(path.join(self.tmp_dir, filename)) + for filename in filenames[:-1] + ], + assert_items_equal=True) + + + +if __name__ == '__main__': + test.main() diff --git a/tensorflow/python/data/kernel_tests/map_dataset_op_test.py b/tensorflow/python/data/kernel_tests/map_test.py similarity index 80% rename from tensorflow/python/data/kernel_tests/map_dataset_op_test.py rename to tensorflow/python/data/kernel_tests/map_test.py index 81ef7d16be..fdce791447 100644 --- a/tensorflow/python/data/kernel_tests/map_dataset_op_test.py +++ b/tensorflow/python/data/kernel_tests/map_test.py @@ -19,7 +19,6 @@ from __future__ import print_function from collections import namedtuple import threading -import time import warnings from absl.testing import parameterized @@ -27,7 +26,6 @@ import numpy as np from tensorflow.core.framework import attr_value_pb2 from tensorflow.core.protobuf import config_pb2 -from tensorflow.python.client import session from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import constant_op @@ -79,7 +77,7 @@ def _make_coordinated_sloppy_dataset(num_elements, num_parallel_calls): options.experimental_deterministic = False dataset = dataset_ops.Dataset.range(num_elements).map( map_fn, num_parallel_calls).with_options(options) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) next_element = iterator.get_next() return next_element, coordination_events @@ -102,7 +100,7 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): count = array_ops.placeholder(dtypes.int64, shape=[]) dataset = self._buildMapDataset(components, count) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) init_op = iterator.initializer get_next = iterator.get_next() @@ -168,7 +166,7 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): dataset = self._buildParallelMapDataset( components, count, num_parallel_calls, output_buffer_size) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) init_op = iterator.initializer get_next = iterator.get_next() @@ -237,7 +235,7 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): dataset = self._buildParallelMapDataset(components, 1000, 100, 100) # NOTE(mrry): Also test that the prefetching thread is cancelled correctly. dataset = dataset.prefetch(100) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) init_op = iterator.initializer get_next = iterator.get_next() @@ -252,7 +250,7 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): dataset = (dataset_ops.Dataset.from_tensor_slices(components) .map(lambda x: array_ops.check_numerics(x, "message"), num_parallel_calls=2)) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) init_op = iterator.initializer get_next = iterator.get_next() @@ -267,7 +265,7 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): dataset = (dataset_ops.Dataset.from_tensor_slices(components) .map(lambda x: array_ops.check_numerics(x, "message"), num_parallel_calls=2)) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) init_op = iterator.initializer get_next = iterator.get_next() @@ -288,7 +286,7 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): dataset = (dataset_ops.Dataset.from_tensor_slices(components) .map(lambda x: array_ops.check_numerics(x, "message")) .prefetch(2)) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) init_op = iterator.initializer get_next = iterator.get_next() @@ -314,8 +312,8 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): return dataset_ops.Dataset.range(10).map(_map_fn) def _build_graph(): - captured_iterator = dataset_ops.Dataset.range( - 10).make_initializable_iterator() + captured_iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.range(10)) ds = _build_ds(captured_iterator) iterator = ds.make_initializable_iterator() init_op = iterator.initializer @@ -345,10 +343,9 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): input_sentences = dataset_ops.Dataset.from_tensor_slices( ["brain brain tank salad surgery", "surgery brain"]) - iterator = (input_sentences - .map(lambda x: string_ops.string_split([x]).values) - .map(table.lookup) - .make_initializable_iterator()) + iterator = dataset_ops.make_initializable_iterator( + input_sentences + .map(lambda x: string_ops.string_split([x]).values).map(table.lookup)) init_op = iterator.initializer get_next = iterator.get_next() @@ -365,8 +362,9 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): queue = data_flow_ops.FIFOQueue(200, dtypes.int64, shapes=[]) enqueue_op = queue.enqueue_many(elements) close_op = queue.close() - iterator = (dataset_ops.Dataset.from_tensors(0).repeat(-1) - .map(lambda _: queue.dequeue()).make_initializable_iterator()) + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.from_tensors(0).repeat(-1) + .map(lambda _: queue.dequeue())) init_op = iterator.initializer get_next = iterator.get_next() @@ -389,9 +387,9 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): enqueue_op = queue.enqueue_many(elements) close_op = queue.close() - iterator = (dataset_ops.Dataset.from_tensors(0).repeat(-1) - .map(lambda _: (queue.dequeue(), queue_2.dequeue())) - .make_initializable_iterator()) + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.from_tensors(0).repeat(-1) + .map(lambda _: (queue.dequeue(), queue_2.dequeue()))) init_op = iterator.initializer get_next = iterator.get_next() @@ -408,9 +406,9 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): def testCaptureVariable(self): counter_var = variable_scope.get_variable( "counter", (), dtypes.int32, use_resource=True) - iterator = (dataset_ops.Dataset.from_tensors(0).repeat(10) - .map(lambda _: counter_var.assign_add(1)) - .make_initializable_iterator()) + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.from_tensors(0).repeat(10) + .map(lambda _: counter_var.assign_add(1))) init_op = iterator.initializer get_next = iterator.get_next() @@ -428,9 +426,9 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): def testCaptureUninitializedVariableError(self): counter_var = variable_scope.get_variable( "counter", (), dtypes.int32, use_resource=True) - iterator = (dataset_ops.Dataset.from_tensors(0).repeat(10) - .map(lambda _: counter_var.assign_add(1)) - .make_initializable_iterator()) + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.from_tensors(0).repeat(10) + .map(lambda _: counter_var.assign_add(1))) init_op = iterator.initializer get_next = iterator.get_next() @@ -440,9 +438,9 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): sess.run(get_next) def testSeededStatefulOperatorIsProperlyStateful(self): - iterator = (dataset_ops.Dataset.from_tensors(0).repeat(10) - .map(lambda _: random_ops.random_uniform((), seed=11)).batch(2) - .make_initializable_iterator()) + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.from_tensors(0).repeat(10) + .map(lambda _: random_ops.random_uniform((), seed=11)).batch(2)) init_op = iterator.initializer get_next = iterator.get_next() @@ -464,11 +462,11 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): self.assertAllClose(random_values, random_values_2) def testStatefulMapKeepsStateAcrossIterators(self): - iterator = (dataset_ops.Dataset.from_tensors(0).repeat(10) - .map(lambda _: random_ops.random_uniform((), seed=11)) - .repeat(1000) - .batch(10) - .make_initializable_iterator()) + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.from_tensors(0).repeat(10) + .map(lambda _: random_ops.random_uniform((), seed=11)) + .repeat(1000) + .batch(10)) init_op = iterator.initializer get_next = iterator.get_next() @@ -493,9 +491,8 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): counter_var.assign_add(1) return x - iterator = (dataset_ops.Dataset.range(10) - .map(increment_fn) - .make_initializable_iterator()) + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.range(10).map(increment_fn)) init_op = iterator.initializer get_next = iterator.get_next() @@ -511,17 +508,17 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): self.assertEqual(10, sess.run(counter_var)) def testMapDict(self): - iterator = (dataset_ops.Dataset.range(10) - .map(lambda x: {"foo": x * 2, "bar": x ** 2}) - .map(lambda d: d["foo"] + d["bar"]) - .make_initializable_iterator()) + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.range(10) + .map(lambda x: {"foo": x * 2, "bar": x ** 2}) + .map(lambda d: d["foo"] + d["bar"])) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: sess.run(init_op) for i in range(10): - self.assertEqual(i * 2 + i ** 2, sess.run(get_next)) + self.assertEqual(i * 2 + i**2, sess.run(get_next)) with self.assertRaises(errors.OutOfRangeError): sess.run(get_next) @@ -546,8 +543,9 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): dataset_tuple = dataset_tuple.map(preprocess_tuple) dataset_namedtuple = dataset_namedtuple.map(preprocess_namedtuple) - next_tuple = dataset_tuple.make_one_shot_iterator().get_next() - next_namedtuple = dataset_namedtuple.make_one_shot_iterator().get_next() + next_tuple = dataset_ops.make_one_shot_iterator(dataset_tuple).get_next() + next_namedtuple = dataset_ops.make_one_shot_iterator( + dataset_namedtuple).get_next() # make sure both datasets contain the same data with self.cached_session() as sess: @@ -561,16 +559,15 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): def testUseStepContainerInMap(self): row = np.arange(6) - iterator = ( + iterator = dataset_ops.make_initializable_iterator( dataset_ops.Dataset.from_tensors(row) - .map(lambda elems: functional_ops.map_fn(lambda x: x * x, elems)) - .make_initializable_iterator()) + .map(lambda elems: functional_ops.map_fn(lambda x: x * x, elems))) init_op = iterator.initializer get_next = iterator.get_next() with self.cached_session() as sess: sess.run(init_op) - self.assertAllEqual(row ** 2, sess.run(get_next)) + self.assertAllEqual(row**2, sess.run(get_next)) with self.assertRaises(errors.OutOfRangeError): sess.run(get_next) @@ -600,9 +597,9 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): pred_fn_pairs, default=multiply, exclusive=True) def build_dataset(row, num): - iterator = ( + iterator = dataset_ops.make_initializable_iterator( dataset_ops.Dataset.from_tensor_slices(row).map( - lambda x: control_map_fn(x, num)).make_initializable_iterator()) + lambda x: control_map_fn(x, num))) init_op = iterator.initializer get_next = iterator.get_next() return init_op, get_next @@ -639,11 +636,10 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): def build_dataset(row, num): # pylint: disable=g-long-lambda - iterator = ( + iterator = dataset_ops.make_initializable_iterator( dataset_ops.Dataset.from_tensors(row).map( - lambda elems: functional_ops.map_fn(lambda x: - control_map_fn(x, num), elems) - ).make_initializable_iterator()) + lambda elems: functional_ops.map_fn( + lambda x: control_map_fn(x, num), elems))) init_op = iterator.initializer get_next = iterator.get_next() return init_op, get_next @@ -687,11 +683,10 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): row = np.arange(6) num = 2 # pylint: disable=g-long-lambda - iterator = ( + iterator = dataset_ops.make_initializable_iterator( dataset_ops.Dataset.from_tensors(row).map( - lambda elems: functional_ops.map_fn(lambda x: - control_map_fn(x, num), elems) - ).make_initializable_iterator()) + lambda elems: functional_ops.map_fn( + lambda x: control_map_fn(x, num), elems))) # pylint: enable=g-long-lambda init_op = iterator.initializer get_next = iterator.get_next() @@ -721,11 +716,10 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): return script_ops.py_func(_map_py_func, [x], x.dtype) buffer_size_placeholder = array_ops.placeholder(dtypes.int64, shape=[]) - iterator = ( + iterator = dataset_ops.make_initializable_iterator( dataset_ops.Dataset.range(100) .map(_map_fn) - .prefetch(buffer_size_placeholder) - .make_initializable_iterator()) + .prefetch(buffer_size_placeholder)) init_op = iterator.initializer get_next = iterator.get_next() @@ -761,9 +755,9 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): sess.run(get_next) def testReturnList(self): - iterator = (dataset_ops.Dataset.range(10) - .map(lambda x: [x, constant_op.constant(37.0)]) - .make_initializable_iterator()) + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.range(10) + .map(lambda x: [x, constant_op.constant(37.0)])) init_op = iterator.initializer get_next = iterator.get_next() @@ -782,9 +776,8 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): return script_ops.py_func( _map_py_func, [x_tensor], [dtypes.int64, dtypes.float64]) - iterator = (dataset_ops.Dataset.range(10) - .map(_map_fn) - .make_initializable_iterator()) + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.range(10).map(_map_fn)) init_op = iterator.initializer get_next = iterator.get_next() @@ -803,9 +796,8 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): values=(i * np.array([1])), dense_shape=np.array([1, 1])) - iterator = (dataset_ops.Dataset.range(10) - .map(_sparse) - .make_initializable_iterator()) + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.range(10).map(_sparse)) init_op = iterator.initializer get_next = iterator.get_next() @@ -830,9 +822,8 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): self.assertTrue(sparse_tensor.is_sparse(i)) return sparse_ops.sparse_concat(0, [i, i]) - iterator = ( - dataset_ops.Dataset.range(10).map(_sparse).map(_check) - .make_initializable_iterator()) + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.range(10).map(_sparse).map(_check)) init_op = iterator.initializer get_next = iterator.get_next() @@ -852,11 +843,10 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): else: return i - iterator = ( + iterator = dataset_ops.make_initializable_iterator( dataset_ops.Dataset.range(105) .map(lambda x: script_ops.py_func(raising_py_func, [x], dtypes.int64), - num_parallel_calls=2) - .make_initializable_iterator()) + num_parallel_calls=2)) init_op = iterator.initializer get_next = iterator.get_next() @@ -868,9 +858,8 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): sess.run(get_next) def testConstantOutput(self): - iterator = ( - dataset_ops.Dataset.range(10).map(lambda x: [x, "hello", 10]) - .make_initializable_iterator()) + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.range(10).map(lambda x: [x, "hello", 10])) init_op = iterator.initializer get_next = iterator.get_next() @@ -901,12 +890,14 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): break self.assertTrue(found_warning) - def testNestedDatasetError(self): - dataset = dataset_ops.Dataset.from_tensors([1.0, 2.0, 3.0]) - with self.assertRaisesRegexp( - NotImplementedError, r"The Dataset.map\(\) transformation does not " - "currently support nested datasets as outputs."): - _ = dataset.map(dataset_ops.Dataset.from_tensor_slices) + def testNestedDatasetMap(self): + # TODO(b/110122868): When iterators can yield a `tf.data.Dataset`, remove + # the `get_single_element()` call. + dataset = dataset_ops.Dataset.from_tensors([1.0, 2.0, 3.0]).map( + dataset_ops.Dataset.from_tensor_slices).map( + lambda ds: ds.batch(3)).flat_map(lambda x: x) + + self.assertDatasetProduces(dataset, [[1.0, 2.0, 3.0]]) def testReturnValueError(self): dataset = dataset_ops.Dataset.from_tensors([1.0, 2.0, 3.0]) @@ -939,7 +930,7 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): return const_tensor dataset = dataset.map(broken_function) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) with self.cached_session() as sess: with self.assertRaisesRegexp(errors.InvalidArgumentError, "BrokenConst"): @@ -966,7 +957,7 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): return tids dataset = make_dataset_fn(dataset, _map_fn) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) get_next = iterator.get_next() with self.cached_session() as sess: @@ -987,7 +978,7 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): def testShortCircuit(self, structure, map_fn, num_parallel_calls): dataset = self.structuredDataset(structure).repeat().map( map_fn, num_parallel_calls=num_parallel_calls) - get_next = dataset.make_one_shot_iterator().get_next() + get_next = dataset_ops.make_one_shot_iterator(dataset).get_next() with self.cached_session() as sess: if isinstance(structure, tuple): @@ -1004,7 +995,7 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): captured_t = array_ops.placeholder(dtypes.int64, shape=[]) dataset = self.structuredDataset(None).repeat().map( lambda x: captured_t, num_parallel_calls=num_parallel_calls) - iterator = dataset.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(dataset) get_next = iterator.get_next() with self.cached_session() as sess: @@ -1055,108 +1046,5 @@ class MapDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): sess.run(get_next) -class MapDatasetBenchmark(test.Benchmark): - - def benchmarkChainOfMaps(self): - chain_lengths = [0, 1, 2, 5, 10, 20, 50] - for chain_length in chain_lengths: - for mode in ["general", "single-threaded", "short-circuit"]: - if mode == "general": - map_fn = lambda x: x + 1 - use_inter_op_parallelism = True - print_label = "" - benchmark_label = "" - if mode == "single-threaded": - map_fn = lambda x: x + 1 - use_inter_op_parallelism = False - print_label = " (single threaded mode)" - benchmark_label = "_single_threaded" - if mode == "short-circuit": - map_fn = lambda x: x - use_inter_op_parallelism = True # should not have any significance - print_label = " (short circuit mode)" - benchmark_label = "_short_circuit" - - with ops.Graph().as_default(): - dataset = dataset_ops.Dataset.from_tensors(0).repeat(None) - for _ in range(chain_length): - dataset = dataset_ops.MapDataset( - dataset, - map_fn, - use_inter_op_parallelism=use_inter_op_parallelism) - iterator = dataset.make_one_shot_iterator() - next_element = iterator.get_next() - - with session.Session() as sess: - for _ in range(5): - sess.run(next_element.op) - deltas = [] - for _ in range(100): - start = time.time() - for _ in range(100): - sess.run(next_element.op) - end = time.time() - deltas.append(end - start) - - median_wall_time = np.median(deltas) / 100 - print("Map dataset chain length%s: %d Median wall time: %f" % - (print_label, chain_length, median_wall_time)) - self.report_benchmark( - iters=1000, - wall_time=median_wall_time, - name="benchmark_map_dataset_chain_latency_%d%s" % - (chain_length, benchmark_label)) - - def benchmarkMapFanOut(self): - fan_outs = [1, 2, 5, 10, 20, 50, 100] - for fan_out in fan_outs: - for mode in ["general", "single-threaded", "short-circuit"]: - if mode == "general": - map_fn = lambda *xs: [x + 1 for x in xs] - use_inter_op_parallelism = True - print_label = "" - benchmark_label = "" - if mode == "single-threaded": - map_fn = lambda *xs: [x + 1 for x in xs] - use_inter_op_parallelism = False - print_label = " (single threaded mode)" - benchmark_label = "_single_threaded" - if mode == "short-circuit": - map_fn = lambda *xs: xs - use_inter_op_parallelism = True # should not have any significance - print_label = " (short circuit mode)" - benchmark_label = "_short_circuit" - - with ops.Graph().as_default(): - dataset = dataset_ops.Dataset.from_tensors( - tuple(0 for _ in range(fan_out))).repeat(None) - dataset = dataset_ops.MapDataset( - dataset, - map_fn, - use_inter_op_parallelism=use_inter_op_parallelism) - iterator = dataset.make_one_shot_iterator() - next_element = iterator.get_next() - - with session.Session() as sess: - for _ in range(5): - sess.run(next_element[0].op) - deltas = [] - for _ in range(100): - start = time.time() - for _ in range(100): - sess.run(next_element[0].op) - end = time.time() - deltas.append(end - start) - - median_wall_time = np.median(deltas) / 100 - print("Map dataset fan out%s: %d Median wall time: %f" % - (print_label, fan_out, median_wall_time)) - self.report_benchmark( - iters=1000, - wall_time=median_wall_time, - name="benchmark_map_dataset_fan_out_%d%s" % (fan_out, - benchmark_label)) - - if __name__ == "__main__": test.main() diff --git a/tensorflow/python/data/kernel_tests/multi_device_iterator_test.py b/tensorflow/python/data/kernel_tests/multi_device_iterator_test.py index 42ee1e2186..622ebb55de 100644 --- a/tensorflow/python/data/kernel_tests/multi_device_iterator_test.py +++ b/tensorflow/python/data/kernel_tests/multi_device_iterator_test.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""MultiDeviceIterator tests.""" +"""Tests for `tf.data.MultiDeviceIterator`.""" from __future__ import absolute_import from __future__ import division @@ -20,6 +20,7 @@ from __future__ import print_function from tensorflow.core.protobuf import config_pb2 from tensorflow.python.data.experimental.ops import optimization +from tensorflow.python.data.experimental.ops.optimization_options import OptimizationOptions from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops from tensorflow.python.data.ops import multi_device_iterator_ops @@ -31,6 +32,7 @@ from tensorflow.python.ops import array_ops from tensorflow.python.platform import test +# TODO(b/117581999): Add eager coverage. class MultiDeviceIteratorTest(test_base.DatasetTestBase): def testNoGetNext(self): @@ -40,7 +42,7 @@ class MultiDeviceIteratorTest(test_base.DatasetTestBase): config = config_pb2.ConfigProto(device_count={"CPU": 3}) with self.test_session(config=config) as sess: - sess.run(multi_device_iterator.initializer) + self.evaluate(multi_device_iterator.initializer) def testBasic(self): dataset = dataset_ops.Dataset.range(10) @@ -50,13 +52,13 @@ class MultiDeviceIteratorTest(test_base.DatasetTestBase): config = config_pb2.ConfigProto(device_count={"CPU": 3}) with self.test_session(config=config) as sess: - sess.run(multi_device_iterator.initializer) + self.evaluate(multi_device_iterator.initializer) for i in range(0, 10, 2): - self.assertEqual(i, sess.run(elem_on_1)) - self.assertEqual(i + 1, sess.run(elem_on_2)) + self.assertEqual(i, self.evaluate(elem_on_1)) + self.assertEqual(i + 1, self.evaluate(elem_on_2)) with self.assertRaises(errors.OutOfRangeError): - sess.run(elem_on_1) - sess.run(elem_on_2) + self.evaluate(elem_on_1) + self.evaluate(elem_on_2) def testOneOnSameDevice(self): with ops.device("/cpu:0"): @@ -67,13 +69,13 @@ class MultiDeviceIteratorTest(test_base.DatasetTestBase): config = config_pb2.ConfigProto(device_count={"CPU": 2}) with self.test_session(config=config) as sess: - sess.run(multi_device_iterator.initializer) + self.evaluate(multi_device_iterator.initializer) for i in range(0, 10, 2): - self.assertEqual(i, sess.run(elem_on_1)) - self.assertEqual(i + 1, sess.run(elem_on_2)) + self.assertEqual(i, self.evaluate(elem_on_1)) + self.assertEqual(i + 1, self.evaluate(elem_on_2)) with self.assertRaises(errors.OutOfRangeError): - sess.run(elem_on_1) - sess.run(elem_on_2) + self.evaluate(elem_on_1) + self.evaluate(elem_on_2) def testRepeatDevices(self): with ops.device("/cpu:0"): @@ -85,17 +87,17 @@ class MultiDeviceIteratorTest(test_base.DatasetTestBase): config = config_pb2.ConfigProto(device_count={"CPU": 3}) with self.test_session(config=config) as sess: - sess.run(multi_device_iterator.initializer) + self.evaluate(multi_device_iterator.initializer) for i in range(0, 20, 4): - self.assertEqual(i, sess.run(elem_on_1)) - self.assertEqual(i + 1, sess.run(elem_on_2)) - self.assertEqual(i + 2, sess.run(elem_on_3)) - self.assertEqual(i + 3, sess.run(elem_on_4)) + self.assertEqual(i, self.evaluate(elem_on_1)) + self.assertEqual(i + 1, self.evaluate(elem_on_2)) + self.assertEqual(i + 2, self.evaluate(elem_on_3)) + self.assertEqual(i + 3, self.evaluate(elem_on_4)) with self.assertRaises(errors.OutOfRangeError): - sess.run(elem_on_1) - sess.run(elem_on_2) - sess.run(elem_on_3) - sess.run(elem_on_4) + self.evaluate(elem_on_1) + self.evaluate(elem_on_2) + self.evaluate(elem_on_3) + self.evaluate(elem_on_4) def testNotFullyDivisible(self): dataset = dataset_ops.Dataset.range(9) @@ -105,14 +107,14 @@ class MultiDeviceIteratorTest(test_base.DatasetTestBase): config = config_pb2.ConfigProto(device_count={"CPU": 3}) with self.test_session(config=config) as sess: - sess.run(multi_device_iterator.initializer) + self.evaluate(multi_device_iterator.initializer) for i in range(0, 8, 2): - self.assertEqual(i, sess.run(elem_on_1)) - self.assertEqual(i + 1, sess.run(elem_on_2)) - self.assertEqual(8, sess.run(elem_on_1)) + self.assertEqual(i, self.evaluate(elem_on_1)) + self.assertEqual(i + 1, self.evaluate(elem_on_2)) + self.assertEqual(8, self.evaluate(elem_on_1)) with self.assertRaises(errors.OutOfRangeError): - sess.run(elem_on_1) - sess.run(elem_on_2) + self.evaluate(elem_on_1) + self.evaluate(elem_on_2) def testGetNextAsOptional(self): dataset = dataset_ops.Dataset.range(9) @@ -126,7 +128,7 @@ class MultiDeviceIteratorTest(test_base.DatasetTestBase): config = config_pb2.ConfigProto(device_count={"CPU": 3}) with self.test_session(config=config) as sess: - sess.run(multi_device_iterator.initializer) + self.evaluate(multi_device_iterator.initializer) for i in range(0, 8, 2): elem_on_1_has_value, elem_on_1_value = sess.run( [elem_on_1_has_value_t, elem_on_1_t]) @@ -140,12 +142,12 @@ class MultiDeviceIteratorTest(test_base.DatasetTestBase): [elem_on_1_has_value_t, elem_on_1_t]) self.assertTrue(elem_on_1_has_value) self.assertEqual(8, elem_on_1_value) - self.assertFalse(sess.run(elem_on_1_has_value_t)) - self.assertFalse(sess.run(elem_on_2_has_value_t)) + self.assertFalse(self.evaluate(elem_on_1_has_value_t)) + self.assertFalse(self.evaluate(elem_on_2_has_value_t)) with self.assertRaises(errors.InvalidArgumentError): - sess.run(elem_on_1_t) + self.evaluate(elem_on_1_t) with self.assertRaises(errors.InvalidArgumentError): - sess.run(elem_on_2_t) + self.evaluate(elem_on_2_t) def testUneven(self): dataset = dataset_ops.Dataset.range(10) @@ -155,14 +157,14 @@ class MultiDeviceIteratorTest(test_base.DatasetTestBase): config = config_pb2.ConfigProto(device_count={"CPU": 3}) with self.test_session(config=config) as sess: - sess.run(multi_device_iterator.initializer) + self.evaluate(multi_device_iterator.initializer) for i in range(0, 10, 2): - self.assertEqual(i, sess.run(elem_on_1)) + self.assertEqual(i, self.evaluate(elem_on_1)) for i in range(0, 10, 2): - self.assertEqual(i + 1, sess.run(elem_on_2)) + self.assertEqual(i + 1, self.evaluate(elem_on_2)) with self.assertRaises(errors.OutOfRangeError): - sess.run(elem_on_1) - sess.run(elem_on_2) + self.evaluate(elem_on_1) + self.evaluate(elem_on_2) def testMultipleInitializations(self): with ops.device("/cpu:0"): @@ -179,7 +181,8 @@ class MultiDeviceIteratorTest(test_base.DatasetTestBase): with self.test_session(config=config) as sess: for i in range(1000): sess.run(init_op, feed_dict={epoch: i}) - self.assertEqual([(i, 0), (i, 1)], sess.run([elem_on_1, elem_on_2])) + self.assertEqual([(i, 0), (i, 1)], self.evaluate([elem_on_1, + elem_on_2])) def testBasicGpu(self): if not test_util.is_gpu_available(): @@ -192,13 +195,13 @@ class MultiDeviceIteratorTest(test_base.DatasetTestBase): config = config_pb2.ConfigProto(device_count={"CPU": 2, "GPU": 1}) with self.test_session(config=config) as sess: - sess.run(multi_device_iterator.initializer) + self.evaluate(multi_device_iterator.initializer) for i in range(0, 10, 2): - self.assertEqual(i, sess.run(elem_on_1)) - self.assertEqual(i + 1, sess.run(elem_on_2)) + self.assertEqual(i, self.evaluate(elem_on_1)) + self.assertEqual(i + 1, self.evaluate(elem_on_2)) with self.assertRaises(errors.OutOfRangeError): - sess.run(elem_on_1) - sess.run(elem_on_2) + self.evaluate(elem_on_1) + self.evaluate(elem_on_2) def testUnevenGpu(self): if not test_util.is_gpu_available(): @@ -211,14 +214,14 @@ class MultiDeviceIteratorTest(test_base.DatasetTestBase): config = config_pb2.ConfigProto(device_count={"CPU": 2, "GPU": 1}) with self.test_session(config=config) as sess: - sess.run(multi_device_iterator.initializer) + self.evaluate(multi_device_iterator.initializer) for i in range(0, 10, 2): - self.assertEqual(i, sess.run(elem_on_1)) + self.assertEqual(i, self.evaluate(elem_on_1)) for i in range(0, 10, 2): - self.assertEqual(i + 1, sess.run(elem_on_2)) + self.assertEqual(i + 1, self.evaluate(elem_on_2)) with self.assertRaises(errors.OutOfRangeError): - sess.run(elem_on_1) - sess.run(elem_on_2) + self.evaluate(elem_on_1) + self.evaluate(elem_on_2) def testGetNextAsOptionalGpu(self): if not test_util.is_gpu_available(): @@ -235,7 +238,7 @@ class MultiDeviceIteratorTest(test_base.DatasetTestBase): config = config_pb2.ConfigProto(device_count={"CPU": 2, "GPU": 1}) with self.test_session(config=config) as sess: - sess.run(multi_device_iterator.initializer) + self.evaluate(multi_device_iterator.initializer) for i in range(0, 8, 2): elem_on_1_has_value, elem_on_1_value = sess.run( [elem_on_1_has_value_t, elem_on_1_t]) @@ -249,12 +252,12 @@ class MultiDeviceIteratorTest(test_base.DatasetTestBase): [elem_on_1_has_value_t, elem_on_1_t]) self.assertTrue(elem_on_1_has_value) self.assertEqual(8, elem_on_1_value) - self.assertFalse(sess.run(elem_on_1_has_value_t)) - self.assertFalse(sess.run(elem_on_2_has_value_t)) + self.assertFalse(self.evaluate(elem_on_1_has_value_t)) + self.assertFalse(self.evaluate(elem_on_2_has_value_t)) with self.assertRaises(errors.InvalidArgumentError): - sess.run(elem_on_1_t) + self.evaluate(elem_on_1_t) with self.assertRaises(errors.InvalidArgumentError): - sess.run(elem_on_2_t) + self.evaluate(elem_on_2_t) def testOptimization(self): dataset = dataset_ops.Dataset.range(10) @@ -263,7 +266,8 @@ class MultiDeviceIteratorTest(test_base.DatasetTestBase): dataset = dataset.cache() options = dataset_ops.Options() - options.experimental_noop_elimination = True + options.experimental_optimization = OptimizationOptions() + options.experimental_optimization.noop_elimination = True dataset = dataset.with_options(options) multi_device_iterator = multi_device_iterator_ops.MultiDeviceIterator( @@ -272,13 +276,13 @@ class MultiDeviceIteratorTest(test_base.DatasetTestBase): config = config_pb2.ConfigProto(device_count={"CPU": 3}) with self.test_session(config=config) as sess: - sess.run(multi_device_iterator.initializer) + self.evaluate(multi_device_iterator.initializer) for i in range(0, 10, 2): - self.assertEqual(i, sess.run(elem_on_1)) - self.assertEqual(i + 1, sess.run(elem_on_2)) + self.assertEqual(i, self.evaluate(elem_on_1)) + self.assertEqual(i + 1, self.evaluate(elem_on_2)) with self.assertRaises(errors.OutOfRangeError): - sess.run(elem_on_1) - sess.run(elem_on_2) + self.evaluate(elem_on_1) + self.evaluate(elem_on_2) if __name__ == "__main__": diff --git a/tensorflow/python/data/kernel_tests/optional_ops_test.py b/tensorflow/python/data/kernel_tests/optional_test.py similarity index 64% rename from tensorflow/python/data/kernel_tests/optional_ops_test.py rename to tensorflow/python/data/kernel_tests/optional_test.py index 604e3ad88e..c2c62e9423 100644 --- a/tensorflow/python/data/kernel_tests/optional_ops_test.py +++ b/tensorflow/python/data/kernel_tests/optional_test.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Tests for the Optional data type wrapper.""" +"""Tests for `tf.data.Optional`.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function @@ -33,18 +33,18 @@ from tensorflow.python.framework import sparse_tensor from tensorflow.python.framework import tensor_shape from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops +from tensorflow.python.ops import math_ops from tensorflow.python.platform import test +@test_util.run_all_in_graph_and_eager_modes class OptionalTest(test_base.DatasetTestBase, parameterized.TestCase): - @test_util.run_in_graph_and_eager_modes def testFromValue(self): opt = optional_ops.Optional.from_value(constant_op.constant(37.0)) self.assertTrue(self.evaluate(opt.has_value())) self.assertEqual(37.0, self.evaluate(opt.get_value())) - @test_util.run_in_graph_and_eager_modes def testFromStructuredValue(self): opt = optional_ops.Optional.from_value({ "a": constant_op.constant(37.0), @@ -56,7 +56,6 @@ class OptionalTest(test_base.DatasetTestBase, parameterized.TestCase): "b": ([b"Foo"], b"Bar") }, self.evaluate(opt.get_value())) - @test_util.run_in_graph_and_eager_modes def testFromSparseTensor(self): st_0 = sparse_tensor.SparseTensorValue( indices=np.array([[0]]), @@ -75,7 +74,7 @@ class OptionalTest(test_base.DatasetTestBase, parameterized.TestCase): self.assertAllEqual(expected.dense_shape, self.evaluate(actual.dense_shape)) - @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def testFromNone(self): value_structure = structure.TensorStructure(dtypes.float32, []) opt = optional_ops.Optional.none_from_structure(value_structure) @@ -90,7 +89,90 @@ class OptionalTest(test_base.DatasetTestBase, parameterized.TestCase): with self.assertRaises(errors.InvalidArgumentError): self.evaluate(opt.get_value()) - @test_util.run_in_graph_and_eager_modes + def testAddN(self): + devices = ["/cpu:0"] + if test_util.is_gpu_available(): + devices.append("/gpu:0") + for device in devices: + with ops.device(device): + # With value + opt1 = optional_ops.Optional.from_value((1.0, 2.0)) + opt2 = optional_ops.Optional.from_value((3.0, 4.0)) + + add_tensor = math_ops.add_n([opt1._variant_tensor, + opt2._variant_tensor]) + add_opt = optional_ops._OptionalImpl(add_tensor, opt1.value_structure) + self.assertAllEqual(self.evaluate(add_opt.get_value()), (4.0, 6.0)) + + # Without value + opt_none1 = optional_ops.Optional.none_from_structure( + opt1.value_structure) + opt_none2 = optional_ops.Optional.none_from_structure( + opt2.value_structure) + add_tensor = math_ops.add_n([opt_none1._variant_tensor, + opt_none2._variant_tensor]) + add_opt = optional_ops._OptionalImpl(add_tensor, + opt_none1.value_structure) + self.assertFalse(self.evaluate(add_opt.has_value())) + + def testNestedAddN(self): + devices = ["/cpu:0"] + if test_util.is_gpu_available(): + devices.append("/gpu:0") + for device in devices: + with ops.device(device): + opt1 = optional_ops.Optional.from_value([1, 2.0]) + opt2 = optional_ops.Optional.from_value([3, 4.0]) + opt3 = optional_ops.Optional.from_value((5.0, opt1._variant_tensor)) + opt4 = optional_ops.Optional.from_value((6.0, opt2._variant_tensor)) + + add_tensor = math_ops.add_n([opt3._variant_tensor, + opt4._variant_tensor]) + add_opt = optional_ops._OptionalImpl(add_tensor, opt3.value_structure) + self.assertEqual(self.evaluate(add_opt.get_value()[0]), 11.0) + + inner_add_opt = optional_ops._OptionalImpl(add_opt.get_value()[1], + opt1.value_structure) + self.assertAllEqual(inner_add_opt.get_value(), [4, 6.0]) + + def testZerosLike(self): + devices = ["/cpu:0"] + if test_util.is_gpu_available(): + devices.append("/gpu:0") + for device in devices: + with ops.device(device): + # With value + opt = optional_ops.Optional.from_value((1.0, 2.0)) + zeros_tensor = array_ops.zeros_like(opt._variant_tensor) + zeros_opt = optional_ops._OptionalImpl(zeros_tensor, + opt.value_structure) + self.assertAllEqual(self.evaluate(zeros_opt.get_value()), + (0.0, 0.0)) + + # Without value + opt_none = optional_ops.Optional.none_from_structure( + opt.value_structure) + zeros_tensor = array_ops.zeros_like(opt_none._variant_tensor) + zeros_opt = optional_ops._OptionalImpl(zeros_tensor, + opt_none.value_structure) + self.assertFalse(self.evaluate(zeros_opt.has_value())) + + def testNestedZerosLike(self): + devices = ["/cpu:0"] + if test_util.is_gpu_available(): + devices.append("/gpu:0") + for device in devices: + with ops.device(device): + opt1 = optional_ops.Optional.from_value(1.0) + opt2 = optional_ops.Optional.from_value(opt1._variant_tensor) + + zeros_tensor = array_ops.zeros_like(opt2._variant_tensor) + zeros_opt = optional_ops._OptionalImpl(zeros_tensor, + opt2.value_structure) + inner_zeros_opt = optional_ops._OptionalImpl(zeros_opt.get_value(), + opt1.value_structure) + self.assertEqual(self.evaluate(inner_zeros_opt.get_value()), 0.0) + def testCopyToGPU(self): if not test_util.is_gpu_available(): self.skipTest("No GPU available") @@ -120,6 +202,41 @@ class OptionalTest(test_base.DatasetTestBase, parameterized.TestCase): self.evaluate(gpu_optional_with_value_values)) self.assertFalse(self.evaluate(gpu_optional_none_has_value)) + def testNestedCopyToGPU(self): + if not test_util.is_gpu_available(): + self.skipTest("No GPU available") + + with ops.device("/cpu:0"): + optional_with_value = optional_ops.Optional.from_value( + (constant_op.constant(37.0), constant_op.constant("Foo"), + constant_op.constant(42))) + optional_none = optional_ops.Optional.none_from_structure( + structure.TensorStructure(dtypes.float32, [])) + nested_optional = optional_ops.Optional.from_value( + (optional_with_value._variant_tensor, optional_none._variant_tensor, + 1.0)) + + with ops.device("/gpu:0"): + gpu_nested_optional = optional_ops._OptionalImpl( + array_ops.identity(nested_optional._variant_tensor), + nested_optional.value_structure) + + gpu_nested_optional_has_value = gpu_nested_optional.has_value() + gpu_nested_optional_values = gpu_nested_optional.get_value() + + self.assertTrue(self.evaluate(gpu_nested_optional_has_value)) + + inner_with_value = optional_ops._OptionalImpl( + gpu_nested_optional_values[0], optional_with_value.value_structure) + + inner_none = optional_ops._OptionalImpl( + gpu_nested_optional_values[1], optional_none.value_structure) + + self.assertEqual((37.0, b"Foo", 42), + self.evaluate(inner_with_value.get_value())) + self.assertFalse(self.evaluate(inner_none.has_value())) + self.assertEqual(1.0, self.evaluate(gpu_nested_optional_values[2])) + def _assertElementValueEqual(self, expected, actual): if isinstance(expected, dict): self.assertItemsEqual(list(expected.keys()), list(actual.keys())) @@ -151,7 +268,9 @@ class OptionalTest(test_base.DatasetTestBase, parameterized.TestCase): optional_ops.OptionalStructure( structure.TensorStructure(dtypes.float32, []))), ) - def testOptionalStructure(self, tf_value_fn, expected_value_structure): + @test_util.run_deprecated_v1 + def testSkipEagerOptionalStructure(self, tf_value_fn, + expected_value_structure): tf_value = tf_value_fn() opt = optional_ops.Optional.from_value(tf_value) @@ -205,7 +324,9 @@ class OptionalTest(test_base.DatasetTestBase, parameterized.TestCase): indices=[[0, 1], [1, 0]], values=[37.0, 42.0], dense_shape=[2, 2])}, False), ) - def testIteratorGetNextAsOptional(self, np_value, tf_value_fn, works_on_gpu): + @test_util.run_deprecated_v1 + def testSkipEagerIteratorGetNextAsOptional(self, np_value, tf_value_fn, + works_on_gpu): if not works_on_gpu and test.is_gpu_available(): self.skipTest("Test case not yet supported on GPU.") ds = dataset_ops.Dataset.from_tensors(np_value).repeat(3) diff --git a/tensorflow/python/data/kernel_tests/padded_batch_test.py b/tensorflow/python/data/kernel_tests/padded_batch_test.py new file mode 100644 index 0000000000..dcfb2f507b --- /dev/null +++ b/tensorflow/python/data/kernel_tests/padded_batch_test.py @@ -0,0 +1,243 @@ +# -*- coding: utf-8 -*- +# 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. +# ============================================================================== +"""Tests for `tf.data.Dataset.padded_batch()`.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from absl.testing import parameterized +import numpy as np + +from tensorflow.python.data.kernel_tests import test_base +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import errors +from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import string_ops +from tensorflow.python.platform import test +from tensorflow.python.util import compat + + +def _random_seq_lens(count): + return np.random.randint(20, size=(count,)).astype(np.int32) + + +@test_util.run_all_in_graph_and_eager_modes +class PaddedBatchTest(test_base.DatasetTestBase, parameterized.TestCase): + + @parameterized.named_parameters( + ('default_padding', _random_seq_lens(32), 4, [-1], False), + ('constant_padding', _random_seq_lens(32), 4, [25], False), + ('uneven_with_remainder', _random_seq_lens(34), 4, [-1], False), + ('uneven_without_remainder', _random_seq_lens(34), 4, [-1], True), + ) + def testPaddedBatchDataset(self, seq_lens, batch_size, padded_shapes, + drop_remainder): + """Tests the padded batch dataset logic for various input configurations. + + Args: + seq_lens: the input sequence lengths + batch_size: the batch size + padded_shapes: the padded shapes to use + drop_remainder: whether a smaller batch size should be produced if batch + size does not divide number of inputs evenly + """ + + dataset = dataset_ops.Dataset.from_tensor_slices(seq_lens).map( + lambda x: array_ops.fill([x], x)).padded_batch( + batch_size=batch_size, + drop_remainder=drop_remainder, + padded_shapes=padded_shapes) + + num_full_batches = len(seq_lens) // batch_size + get_next = self.getNext(dataset) + for i in range(num_full_batches): + result = self.evaluate(get_next()) + padded_len = padded_shapes[0] + if padded_len is None or padded_len == -1: + padded_len = np.max(result) if result.size > 0 else 0 + self.assertEqual((batch_size, padded_len), result.shape) + for j in range(batch_size): + seq_len = seq_lens[(i * batch_size) + j] + self.assertAllEqual(result[j, :seq_len], [seq_len] * seq_len) + self.assertAllEqual(result[j, seq_len:], [0] * (padded_len - seq_len)) + + if not drop_remainder and len(seq_lens) % batch_size > 0: + result = self.evaluate(get_next()) + padded_len = np.max(result) if result.size > 0 else 0 + self.assertEqual((len(seq_lens) % batch_size, padded_len), result.shape) + for j in range(len(seq_lens) % batch_size): + seq_len = seq_lens[num_full_batches * batch_size + j] + self.assertAllEqual(result[j, :seq_len], [seq_len] * seq_len) + self.assertAllEqual(result[j, seq_len:], [0] * (padded_len - seq_len)) + + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + + @test_util.run_deprecated_v1 + def testPaddedBatchShortPadding(self): + dataset = ( + dataset_ops.Dataset.from_tensor_slices( + [6, 5, 5, 5, 5]).map(lambda x: array_ops.fill([x], x)).padded_batch( + batch_size=4, padded_shapes=[5])) + self.assertDatasetProduces( + dataset, expected_error=(errors.DataLossError, '')) + + def testPaddedBatchEmptyTensors(self): + dataset = ( + dataset_ops.Dataset.from_tensor_slices( + [0, 0, 0, 0]).map(lambda x: array_ops.fill([x], x)).padded_batch( + batch_size=4, padded_shapes=[-1])) + self.assertDatasetProduces(dataset, expected_output=[[[], [], [], []]]) + + def testPaddedBatchDatasetNonDefaultPadding(self): + + def fill_tuple(x): + filled = array_ops.fill([x], x) + return (filled, string_ops.as_string(filled)) + + random_seq_lens = np.random.randint(20, size=(32,)).astype(np.int32) + dataset = ( + dataset_ops.Dataset.from_tensor_slices(random_seq_lens).map(fill_tuple) + .padded_batch( + 4, padded_shapes=([-1], [-1]), padding_values=(-1, ''))) + + get_next = self.getNext(dataset) + for i in range(8): + result = self.evaluate(get_next()) + padded_len = np.max(result[0]) + self.assertEqual((4, padded_len), result[0].shape) + self.assertEqual((4, padded_len), result[1].shape) + for j in range(4): + seq_len = random_seq_lens[(i * 4) + j] + self.assertAllEqual(result[0][j, :seq_len], [seq_len] * seq_len) + self.assertAllEqual(result[0][j, seq_len:], + [-1] * (padded_len - seq_len)) + self.assertAllEqual(result[1][j, :seq_len], + [compat.as_bytes(str(seq_len))] * seq_len) + self.assertAllEqual(result[1][j, seq_len:], + [b''] * (padded_len - seq_len)) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + + def testPaddedBatchDatasetUnicode(self): + # See GitHub issue 16149 + def generator(): + data = [[u'Простой', u'тест', u'юникода'], + [u'никогда', u'не', u'бывает', u'простым']] + + for seq in data: + yield seq, [0, 1, 2, 3] + + dataset = dataset_ops.Dataset.from_generator( + generator, (dtypes.string, dtypes.int32), + (tensor_shape.TensorShape([None]), tensor_shape.TensorShape([None]))) + padded_dataset = dataset.padded_batch( + 2, padded_shapes=([None], [None]), padding_values=('', 0)) + next_element = self.getNext(padded_dataset) + self.evaluate(next_element()) + + @test_util.run_deprecated_v1 + def testSkipEagerPaddedBatchDatasetShapeSpecifications(self): + int_placeholder = array_ops.placeholder(dtypes.int32) + float_placeholder = array_ops.placeholder(dtypes.float32) + string_placeholder = array_ops.placeholder(dtypes.string) + input_dataset = dataset_ops.Dataset.from_tensors( + (int_placeholder, float_placeholder, string_placeholder)) + + # Test different ways of specifying the `padded_shapes` argument. + dynamic_padding_from_tensor_shapes = input_dataset.padded_batch( + 32, + padded_shapes=(tensor_shape.TensorShape([None]), + tensor_shape.TensorShape([None, None]), + tensor_shape.TensorShape([37]))) + dynamic_padding_from_lists = input_dataset.padded_batch( + 32, padded_shapes=([None], [None, None], [37])) + dynamic_padding_from_lists_with_minus_one = input_dataset.padded_batch( + 32, padded_shapes=([-1], [-1, -1], [37])) + dynamic_padding_from_tensors = input_dataset.padded_batch( + 32, + padded_shapes=(constant_op.constant([-1], dtype=dtypes.int64), + constant_op.constant([-1, -1], dtype=dtypes.int64), + constant_op.constant([37], dtype=dtypes.int64))) + + for dataset in [ + dynamic_padding_from_tensor_shapes, dynamic_padding_from_lists, + dynamic_padding_from_lists_with_minus_one, dynamic_padding_from_tensors + ]: + self.assertEqual([None, None], dataset.output_shapes[0].as_list()) + self.assertEqual([None, None, None], dataset.output_shapes[1].as_list()) + self.assertEqual([None, 37], dataset.output_shapes[2].as_list()) + + def testPaddedBatchSparseError(self): + + def _map_fn(i): + return sparse_tensor.SparseTensorValue( + indices=[[0, 0]], values=(i * [1]), dense_shape=[1, 1]), i + + with self.assertRaises(TypeError): + _ = dataset_ops.Dataset.range(10).map(_map_fn).padded_batch(10) + + def testPaddedBatchShapeError(self): + with self.assertRaisesRegexp( + ValueError, r'The padded shape \(1,\) is not compatible with the ' + r'corresponding input component shape \(\).'): + _ = dataset_ops.Dataset.range(10).padded_batch(5, padded_shapes=[1]) + + with self.assertRaisesRegexp( + ValueError, r'The padded shape \(1,\) is not compatible with the ' + r'corresponding input component shape \(3,\).'): + _ = dataset_ops.Dataset.from_tensors([1, 2, 3]).padded_batch( + 5, padded_shapes=[1]) + + with self.assertRaisesRegexp( + ValueError, r'Padded shape .* must be a 1-D tensor ' + r'of tf.int64 values, but its shape was \(2, 2\).'): + _ = dataset_ops.Dataset.from_tensors([1, 2, 3]).padded_batch( + 5, padded_shapes=[[1, 1], [1, 1]]) + + with self.assertRaisesRegexp( + TypeError, r'Padded shape .* must be a 1-D tensor ' + r'of tf.int64 values, but its element type was float32.'): + _ = dataset_ops.Dataset.from_tensors([1, 2, 3]).padded_batch( + 5, padded_shapes=constant_op.constant([1., 2., 3.])) + + with self.assertRaisesRegexp( + ValueError, r'The padded shape \(1,\) is not compatible with the ' + r'corresponding input component shape \(\).'): + shape_as_tensor = constant_op.constant([1], dtype=dtypes.int64) + _ = dataset_ops.Dataset.range(10).padded_batch( + 5, padded_shapes=shape_as_tensor) + + @test_util.run_deprecated_v1 + def testSkipEagerPaddedBatchShapeError(self): + with self.assertRaisesRegexp( + ValueError, + r'The padded shape \((\?|None), (\?|None)\) is not compatible with the ' + r'corresponding input component shape \(\).'): + shape_as_tensor = array_ops.placeholder(dtypes.int64, shape=[2]) + _ = dataset_ops.Dataset.range(10).padded_batch( + 5, padded_shapes=shape_as_tensor) + + +if __name__ == '__main__': + test.main() diff --git a/tensorflow/python/data/kernel_tests/prefetch_dataset_op_test.py b/tensorflow/python/data/kernel_tests/prefetch_test.py similarity index 52% rename from tensorflow/python/data/kernel_tests/prefetch_dataset_op_test.py rename to tensorflow/python/data/kernel_tests/prefetch_test.py index 76e2697b29..a143ba0ac6 100644 --- a/tensorflow/python/data/kernel_tests/prefetch_dataset_op_test.py +++ b/tensorflow/python/data/kernel_tests/prefetch_test.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Test PrefetchDataset.""" +"""Tests for `tf.data.Dataset.prefetch()`.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function @@ -21,40 +21,24 @@ from absl.testing import parameterized from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors -from tensorflow.python.ops import array_ops +from tensorflow.python.framework import test_util from tensorflow.python.platform import test -class PrefetchDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): +@test_util.run_all_in_graph_and_eager_modes +class PrefetchTest(test_base.DatasetTestBase, parameterized.TestCase): @parameterized.parameters((-1), (0), (5)) def testBufferSize(self, buffer_size): - buffer_size_t = array_ops.placeholder(dtypes.int64, shape=[]) - iterator = dataset_ops.Dataset.range(10).prefetch( - buffer_size=buffer_size_t).make_initializable_iterator() - init_op = iterator.initializer - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(init_op, feed_dict={buffer_size_t: buffer_size}) - for m in range(10): - self.assertEqual(m, sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) + dataset = dataset_ops.Dataset.range(10).prefetch(buffer_size=buffer_size) + self.assertDatasetProduces(dataset, expected_output=range(10)) @parameterized.parameters((-2), (-42)) def testInvalidBufferSize(self, buffer_size): - buffer_size_t = array_ops.placeholder(dtypes.int64, shape=[]) - iterator = dataset_ops.Dataset.range(10).prefetch( - buffer_size=buffer_size_t).make_initializable_iterator() - init_op = iterator.initializer - - with self.assertRaisesRegexp(errors.InvalidArgumentError, "buffer_size"): - with self.cached_session() as sess: - sess.run(init_op, feed_dict={buffer_size_t: buffer_size}) - + dataset = dataset_ops.Dataset.range(10).prefetch(buffer_size=buffer_size) + self.assertDatasetProduces( + dataset, expected_error=(errors.InvalidArgumentError, "buffer_size")) if __name__ == "__main__": test.main() diff --git a/tensorflow/python/data/kernel_tests/range_test.py b/tensorflow/python/data/kernel_tests/range_test.py new file mode 100644 index 0000000000..3f5d25e7f3 --- /dev/null +++ b/tensorflow/python/data/kernel_tests/range_test.py @@ -0,0 +1,72 @@ +# 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. +# ============================================================================== +"""Tests for `tf.data.Dataset.range()`.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.data.kernel_tests import test_base +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.framework import errors +from tensorflow.python.framework import test_util +from tensorflow.python.platform import test + + +@test_util.run_all_in_graph_and_eager_modes +class RangeTest(test_base.DatasetTestBase): + + def testStop(self): + dataset = dataset_ops.Dataset.range(5) + self.assertDatasetProduces(dataset, expected_output=range(5)) + + def testStartStop(self): + start, stop = 2, 5 + dataset = dataset_ops.Dataset.range(start, stop) + self.assertDatasetProduces(dataset, expected_output=range(2, 5)) + + def testStartStopStep(self): + start, stop, step = 2, 10, 2 + dataset = dataset_ops.Dataset.range(start, stop, step) + self.assertDatasetProduces(dataset, expected_output=range(2, 10, 2)) + + def testZeroStep(self): + start, stop, step = 2, 10, 0 + dataset = dataset_ops.Dataset.range(start, stop, step) + self.assertDatasetProduces( + dataset, expected_error=(errors.InvalidArgumentError, "")) + + def testNegativeStep(self): + start, stop, step = 2, 10, -1 + dataset = dataset_ops.Dataset.range(start, stop, step) + self.assertDatasetProduces(dataset, expected_output=range(2, 10, -1)) + + def testStopLessThanStart(self): + start, stop = 10, 2 + dataset = dataset_ops.Dataset.range(start, stop) + self.assertDatasetProduces(dataset, expected_output=range(10, 2)) + + def testStopLessThanStartWithPositiveStep(self): + start, stop, step = 10, 2, 2 + dataset = dataset_ops.Dataset.range(start, stop, step) + self.assertDatasetProduces(dataset, expected_output=range(10, 2, 2)) + + def testStopLessThanStartWithNegativeStep(self): + start, stop, step = 10, 2, -1 + dataset = dataset_ops.Dataset.range(start, stop, step) + self.assertDatasetProduces(dataset, expected_output=range(10, 2, -1)) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/data/kernel_tests/reader_dataset_ops_test.py b/tensorflow/python/data/kernel_tests/reader_dataset_ops_test.py deleted file mode 100644 index 4fef4f30bf..0000000000 --- a/tensorflow/python/data/kernel_tests/reader_dataset_ops_test.py +++ /dev/null @@ -1,846 +0,0 @@ -# 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. -# ============================================================================== -"""Tests for the experimental input pipeline ops.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import gzip -import os -import zlib - -from tensorflow.python.data.kernel_tests import test_base -from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.data.ops import iterator_ops -from tensorflow.python.data.ops import readers -from tensorflow.python.eager import context -from tensorflow.python.framework import constant_op -from tensorflow.python.framework import dtypes -from tensorflow.python.framework import errors -from tensorflow.python.framework import ops -from tensorflow.python.framework import tensor_shape -from tensorflow.python.lib.io import python_io -from tensorflow.python.ops import array_ops -from tensorflow.python.ops import gen_dataset_ops -from tensorflow.python.ops import io_ops -from tensorflow.python.ops import parsing_ops -from tensorflow.python.platform import test -from tensorflow.python.util import compat - - -try: - import psutil # pylint: disable=g-import-not-at-top - psutil_import_succeeded = True -except ImportError: - psutil_import_succeeded = False - - -class TextLineDatasetTest(test_base.DatasetTestBase): - - def _lineText(self, f, l): - return compat.as_bytes("%d: %d" % (f, l)) - - def _createFiles(self, - num_files, - num_lines, - crlf=False, - compression_type=None): - filenames = [] - for i in range(num_files): - fn = os.path.join(self.get_temp_dir(), "text_line.%d.txt" % i) - filenames.append(fn) - contents = [] - for j in range(num_lines): - contents.append(self._lineText(i, j)) - # Always include a newline after the record unless it is - # at the end of the file, in which case we include it - if j + 1 != num_lines or i == 0: - contents.append(b"\r\n" if crlf else b"\n") - contents = b"".join(contents) - - if not compression_type: - with open(fn, "wb") as f: - f.write(contents) - elif compression_type == "GZIP": - with gzip.GzipFile(fn, "wb") as f: - f.write(contents) - elif compression_type == "ZLIB": - contents = zlib.compress(contents) - with open(fn, "wb") as f: - f.write(contents) - else: - raise ValueError("Unsupported compression_type", compression_type) - - return filenames - - def _testTextLineDataset(self, compression_type=None): - test_filenames = self._createFiles( - 2, 5, crlf=True, compression_type=compression_type) - filenames = array_ops.placeholder(dtypes.string, shape=[None]) - num_epochs = array_ops.placeholder(dtypes.int64, shape=[]) - batch_size = array_ops.placeholder(dtypes.int64, shape=[]) - - repeat_dataset = readers.TextLineDataset( - filenames, compression_type=compression_type).repeat(num_epochs) - batch_dataset = repeat_dataset.batch(batch_size) - - iterator = iterator_ops.Iterator.from_structure(batch_dataset.output_types) - init_op = iterator.make_initializer(repeat_dataset) - init_batch_op = iterator.make_initializer(batch_dataset) - get_next = iterator.get_next() - - with self.cached_session() as sess: - # Basic test: read from file 0. - sess.run( - init_op, feed_dict={filenames: [test_filenames[0]], - num_epochs: 1}) - for i in range(5): - self.assertEqual(self._lineText(0, i), sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Basic test: read from file 1. - sess.run( - init_op, feed_dict={filenames: [test_filenames[1]], - num_epochs: 1}) - for i in range(5): - self.assertEqual(self._lineText(1, i), sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Basic test: read from both files. - sess.run(init_op, feed_dict={filenames: test_filenames, num_epochs: 1}) - for j in range(2): - for i in range(5): - self.assertEqual(self._lineText(j, i), sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Test repeated iteration through both files. - sess.run(init_op, feed_dict={filenames: test_filenames, num_epochs: 10}) - for _ in range(10): - for j in range(2): - for i in range(5): - self.assertEqual(self._lineText(j, i), sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Test batched and repeated iteration through both files. - sess.run( - init_batch_op, - feed_dict={filenames: test_filenames, - num_epochs: 10, - batch_size: 5}) - for _ in range(10): - self.assertAllEqual([self._lineText(0, i) for i in range(5)], - sess.run(get_next)) - self.assertAllEqual([self._lineText(1, i) for i in range(5)], - sess.run(get_next)) - - def testTextLineDatasetNoCompression(self): - self._testTextLineDataset() - - def testTextLineDatasetGzipCompression(self): - self._testTextLineDataset(compression_type="GZIP") - - def testTextLineDatasetZlibCompression(self): - self._testTextLineDataset(compression_type="ZLIB") - - def testTextLineDatasetBuffering(self): - test_filenames = self._createFiles(2, 5, crlf=True) - - repeat_dataset = readers.TextLineDataset(test_filenames, buffer_size=10) - iterator = repeat_dataset.make_one_shot_iterator() - - with self.cached_session() as sess: - for j in range(2): - for i in range(5): - self.assertEqual(self._lineText(j, i), sess.run(iterator.get_next())) - with self.assertRaises(errors.OutOfRangeError): - sess.run(iterator.get_next()) - - def testIteratorResourceCleanup(self): - filename = os.path.join(self.get_temp_dir(), "text.txt") - with open(filename, "wt") as f: - for i in range(3): - f.write("%d\n" % (i,)) - with context.eager_mode(): - first_iterator = iter(readers.TextLineDataset(filename)) - self.assertEqual(b"0", next(first_iterator).numpy()) - second_iterator = iter(readers.TextLineDataset(filename)) - self.assertEqual(b"0", next(second_iterator).numpy()) - # Eager kernel caching is based on op attributes, which includes the - # Dataset's output shape. Create a different kernel to test that they - # don't create resources with the same names. - different_kernel_iterator = iter( - readers.TextLineDataset(filename).repeat().batch(16)) - self.assertEqual([16], next(different_kernel_iterator).shape) - # Remove our references to the Python Iterator objects, which (assuming no - # reference cycles) is enough to trigger DestroyResourceOp and close the - # partially-read files. - del first_iterator - del second_iterator - del different_kernel_iterator - if not psutil_import_succeeded: - self.skipTest( - "psutil is required to check that we've closed our files.") - open_files = psutil.Process().open_files() - self.assertNotIn(filename, [open_file.path for open_file in open_files]) - - -class FixedLengthRecordReaderTest(test_base.DatasetTestBase): - - def setUp(self): - super(FixedLengthRecordReaderTest, self).setUp() - self._num_files = 2 - self._num_records = 7 - self._header_bytes = 5 - self._record_bytes = 3 - self._footer_bytes = 2 - - def _record(self, f, r): - return compat.as_bytes(str(f * 2 + r) * self._record_bytes) - - def _createFiles(self, compression_type=None): - filenames = [] - for i in range(self._num_files): - fn = os.path.join(self.get_temp_dir(), "fixed_length_record.%d.txt" % i) - filenames.append(fn) - - contents = [] - contents.append(b"H" * self._header_bytes) - for j in range(self._num_records): - contents.append(self._record(i, j)) - contents.append(b"F" * self._footer_bytes) - contents = b"".join(contents) - - if not compression_type: - with open(fn, "wb") as f: - f.write(contents) - elif compression_type == "GZIP": - with gzip.GzipFile(fn, "wb") as f: - f.write(contents) - elif compression_type == "ZLIB": - contents = zlib.compress(contents) - with open(fn, "wb") as f: - f.write(contents) - else: - raise ValueError("Unsupported compression_type", compression_type) - - return filenames - - def _testFixedLengthRecordDataset(self, compression_type=None): - test_filenames = self._createFiles(compression_type=compression_type) - filenames = array_ops.placeholder(dtypes.string, shape=[None]) - num_epochs = array_ops.placeholder(dtypes.int64, shape=[]) - batch_size = array_ops.placeholder(dtypes.int64, shape=[]) - - repeat_dataset = ( - readers.FixedLengthRecordDataset( - filenames, - self._record_bytes, - self._header_bytes, - self._footer_bytes, - compression_type=compression_type).repeat(num_epochs)) - batch_dataset = repeat_dataset.batch(batch_size) - - iterator = iterator_ops.Iterator.from_structure(batch_dataset.output_types) - init_op = iterator.make_initializer(repeat_dataset) - init_batch_op = iterator.make_initializer(batch_dataset) - get_next = iterator.get_next() - - with self.cached_session() as sess: - # Basic test: read from file 0. - sess.run( - init_op, feed_dict={filenames: [test_filenames[0]], - num_epochs: 1}) - for i in range(self._num_records): - self.assertEqual(self._record(0, i), sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Basic test: read from file 1. - sess.run( - init_op, feed_dict={filenames: [test_filenames[1]], - num_epochs: 1}) - for i in range(self._num_records): - self.assertEqual(self._record(1, i), sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Basic test: read from both files. - sess.run(init_op, feed_dict={filenames: test_filenames, num_epochs: 1}) - for j in range(self._num_files): - for i in range(self._num_records): - self.assertEqual(self._record(j, i), sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Test repeated iteration through both files. - sess.run(init_op, feed_dict={filenames: test_filenames, num_epochs: 10}) - for _ in range(10): - for j in range(self._num_files): - for i in range(self._num_records): - self.assertEqual(self._record(j, i), sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Test batched and repeated iteration through both files. - sess.run( - init_batch_op, - feed_dict={ - filenames: test_filenames, - num_epochs: 10, - batch_size: self._num_records - }) - for _ in range(10): - for j in range(self._num_files): - self.assertAllEqual( - [self._record(j, i) for i in range(self._num_records)], - sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testFixedLengthRecordDatasetNoCompression(self): - self._testFixedLengthRecordDataset() - - def testFixedLengthRecordDatasetGzipCompression(self): - self._testFixedLengthRecordDataset(compression_type="GZIP") - - def testFixedLengthRecordDatasetZlibCompression(self): - self._testFixedLengthRecordDataset(compression_type="ZLIB") - - def testFixedLengthRecordDatasetBuffering(self): - test_filenames = self._createFiles() - dataset = readers.FixedLengthRecordDataset( - test_filenames, - self._record_bytes, - self._header_bytes, - self._footer_bytes, - buffer_size=10) - iterator = dataset.make_one_shot_iterator() - - with self.cached_session() as sess: - for j in range(self._num_files): - for i in range(self._num_records): - self.assertEqual(self._record(j, i), sess.run(iterator.get_next())) - with self.assertRaises(errors.OutOfRangeError): - sess.run(iterator.get_next()) - - def testFixedLengthRecordDatasetWrongSize(self): - test_filenames = self._createFiles() - dataset = readers.FixedLengthRecordDataset( - test_filenames, - self._record_bytes + 1, # Incorrect record length. - self._header_bytes, - self._footer_bytes, - buffer_size=10) - iterator = dataset.make_one_shot_iterator() - - with self.cached_session() as sess: - with self.assertRaisesRegexp( - errors.InvalidArgumentError, - r"Excluding the header \(5 bytes\) and footer \(2 bytes\), input " - r"file \".*fixed_length_record.0.txt\" has body length 21 bytes, " - r"which is not an exact multiple of the record length \(4 bytes\)."): - sess.run(iterator.get_next()) - - def _iterator_checkpoint_path(self): - return os.path.join(self.get_temp_dir(), "iterator") - - def _save_op(self, iterator_resource): - iterator_state_variant = gen_dataset_ops.serialize_iterator( - iterator_resource) - save_op = io_ops.write_file( - self._iterator_checkpoint_path(), - parsing_ops.serialize_tensor(iterator_state_variant)) - return save_op - - def _restore_op(self, iterator_resource): - iterator_state_variant = parsing_ops.parse_tensor( - io_ops.read_file(self._iterator_checkpoint_path()), dtypes.variant) - restore_op = gen_dataset_ops.deserialize_iterator(iterator_resource, - iterator_state_variant) - return restore_op - - def _build_iterator_graph(self, num_epochs): - filenames = self._createFiles() - dataset = (readers.FixedLengthRecordDataset( - filenames, self._record_bytes, self._header_bytes, self._footer_bytes) - .repeat(num_epochs)) - iterator = dataset.make_initializable_iterator() - init_op = iterator.initializer - get_next_op = iterator.get_next() - save_op = self._save_op(iterator._iterator_resource) - restore_op = self._restore_op(iterator._iterator_resource) - return init_op, get_next_op, save_op, restore_op - - def _restore_iterator(self): - output_types = dtypes.string - output_shapes = tensor_shape.scalar() - iterator = iterator_ops.Iterator.from_structure(output_types, output_shapes) - get_next = iterator.get_next() - restore_op = self._restore_op(iterator._iterator_resource) - return restore_op, get_next - - def testSaveRestore(self): - num_epochs = 10 - epoch_break = 5 - file_break = self._num_files // 2 - record_break = self._num_records // 2 - - with ops.Graph().as_default() as g: - init_op, get_next_op, save_op, restore_op = self._build_iterator_graph( - num_epochs=num_epochs) - with self.session(graph=g) as sess: - sess.run(init_op) - # Note: There is no checkpoint saved currently so a NotFoundError is - # raised. - with self.assertRaises(errors.NotFoundError): - sess.run(restore_op) - for epoch in range(num_epochs): - for f in range(self._num_files): - for r in range(self._num_records): - if (epoch == epoch_break and f == file_break and - r == record_break): - sess.run(save_op) - break - self.assertEqual(self._record(f, r), sess.run(get_next_op)) - else: - continue - break - else: - continue - break - else: - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next_op) - - with ops.Graph().as_default() as g: - init_op, get_next_op, save_op, restore_op = self._build_iterator_graph( - num_epochs=num_epochs) - with self.session(graph=g) as sess: - sess.run(restore_op) - for epoch in range(num_epochs): - for f in range(self._num_files): - for r in range(self._num_records): - if (epoch < epoch_break or - (epoch == epoch_break and f < file_break) or - (epoch == epoch_break and f == file_break and - r < record_break)): - continue - self.assertEqual(self._record(f, r), sess.run(get_next_op)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next_op) - - def testInitThenRestore(self): - # Note: Calling init_op before restore_op is redundant. This test just makes - # sure we do not fail if restore is called on an already initialized - # iterator resource. - num_epochs = 10 - epoch_break = 5 - file_break = self._num_files // 2 - record_break = self._num_records // 2 - - with ops.Graph().as_default() as g: - init_op, get_next_op, save_op, restore_op = self._build_iterator_graph( - num_epochs=num_epochs) - with self.session(graph=g) as sess: - sess.run(init_op) - # Note: There is no checkpoint saved currently so a NotFoundError is - # raised. - with self.assertRaises(errors.NotFoundError): - sess.run(restore_op) - for epoch in range(num_epochs): - for f in range(self._num_files): - for r in range(self._num_records): - if (epoch == epoch_break and f == file_break and - r == record_break): - sess.run(save_op) - break - self.assertEqual(self._record(f, r), sess.run(get_next_op)) - else: - continue - break - else: - continue - break - else: - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next_op) - - with ops.Graph().as_default() as g: - init_op, get_next_op, save_op, restore_op = self._build_iterator_graph( - num_epochs=num_epochs) - with self.session(graph=g) as sess: - sess.run(init_op) - sess.run(restore_op) - for epoch in range(num_epochs): - for f in range(self._num_files): - for r in range(self._num_records): - if (epoch < epoch_break or - (epoch == epoch_break and f < file_break) or - (epoch == epoch_break and f == file_break and - r < record_break)): - continue - self.assertEqual(self._record(f, r), sess.run(get_next_op)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next_op) - - def testRestoreInModifiedGraph(self): - num_epochs = 10 - num_epochs_1 = 20 - epoch_break = 5 - file_break = self._num_files // 2 - record_break = self._num_records // 2 - - with ops.Graph().as_default() as g: - init_op, get_next_op, save_op, restore_op = self._build_iterator_graph( - num_epochs=num_epochs) - with self.session(graph=g) as sess: - sess.run(init_op) - # Note: There is no checkpoint saved currently so a NotFoundError is - # raised. - with self.assertRaises(errors.NotFoundError): - sess.run(restore_op) - for epoch in range(num_epochs): - for f in range(self._num_files): - for r in range(self._num_records): - if (epoch == epoch_break and f == file_break and - r == record_break): - sess.run(save_op) - break - self.assertEqual(self._record(f, r), sess.run(get_next_op)) - else: - continue - break - else: - continue - break - else: - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next_op) - - with ops.Graph().as_default() as g: - init_op, get_next_op, save_op, restore_op = self._build_iterator_graph( - num_epochs=num_epochs_1) - with self.session(graph=g) as sess: - sess.run(restore_op) - for epoch in range(num_epochs): - for f in range(self._num_files): - for r in range(self._num_records): - if (epoch < epoch_break or - (epoch == epoch_break and f < file_break) or - (epoch == epoch_break and f == file_break and - r < record_break)): - continue - self.assertEqual(self._record(f, r), sess.run(get_next_op)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next_op) - - def testRestoreWithoutBuildingDatasetGraph(self): - num_epochs = 10 - epoch_break = 5 - file_break = self._num_files // 2 - record_break = self._num_records // 2 - - with ops.Graph().as_default() as g: - init_op, get_next_op, save_op, restore_op = self._build_iterator_graph( - num_epochs=num_epochs) - with self.session(graph=g) as sess: - sess.run(init_op) - # Note: There is no checkpoint saved currently so a NotFoundError is - # raised. - with self.assertRaises(errors.NotFoundError): - sess.run(restore_op) - for epoch in range(num_epochs): - for f in range(self._num_files): - for r in range(self._num_records): - if (epoch == epoch_break and f == file_break and - r == record_break): - sess.run(save_op) - break - self.assertEqual(self._record(f, r), sess.run(get_next_op)) - else: - continue - break - else: - continue - break - else: - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next_op) - - with ops.Graph().as_default() as g: - restore_op, get_next_op = self._restore_iterator() - with self.session(graph=g) as sess: - sess.run(restore_op) - for epoch in range(num_epochs): - for f in range(self._num_files): - for r in range(self._num_records): - if (epoch < epoch_break or - (epoch == epoch_break and f < file_break) or - (epoch == epoch_break and f == file_break and - r < record_break)): - continue - self.assertEqual(self._record(f, r), sess.run(get_next_op)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next_op) - - def testRestoreUnusedIterator(self): - num_epochs = 10 - with ops.Graph().as_default() as g: - init_op, get_next_op, save_op, restore_op = self._build_iterator_graph( - num_epochs=num_epochs) - with self.session(graph=g) as sess: - sess.run(init_op) - # Note: There is no checkpoint saved currently so a NotFoundError is - # raised. - with self.assertRaises(errors.NotFoundError): - sess.run(restore_op) - # Save unused iterator. - sess.run(save_op) - with ops.Graph().as_default() as g: - init_op, get_next_op, save_op, restore_op = self._build_iterator_graph( - num_epochs=num_epochs) - with self.session(graph=g) as sess: - sess.run(restore_op) - for _ in range(num_epochs * self._num_files * self._num_records): - sess.run(get_next_op) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next_op) - - def testRestoreExhaustedIterator(self): - num_epochs = 10 - - with ops.Graph().as_default() as g: - init_op, get_next_op, save_op, restore_op = self._build_iterator_graph( - num_epochs=num_epochs) - with self.session(graph=g) as sess: - sess.run(init_op) - # Note: There is no checkpoint saved currently so a NotFoundError is - # raised. - with self.assertRaises(errors.NotFoundError): - sess.run(restore_op) - for _ in range(num_epochs): - for f in range(self._num_files): - for r in range(self._num_records): - self.assertEqual(self._record(f, r), sess.run(get_next_op)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next_op) - sess.run(save_op) - - with ops.Graph().as_default() as g: - init_op, get_next_op, save_op, restore_op = self._build_iterator_graph( - num_epochs=num_epochs) - with self.session(graph=g) as sess: - sess.run(restore_op) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next_op) - - -class TFRecordDatasetTest(test_base.DatasetTestBase): - - def setUp(self): - super(TFRecordDatasetTest, self).setUp() - self._num_files = 2 - self._num_records = 7 - - self.test_filenames = self._createFiles() - - self.filenames = array_ops.placeholder(dtypes.string, shape=[None]) - self.num_epochs = array_ops.placeholder_with_default( - constant_op.constant(1, dtypes.int64), shape=[]) - self.compression_type = array_ops.placeholder_with_default("", shape=[]) - self.batch_size = array_ops.placeholder(dtypes.int64, shape=[]) - - repeat_dataset = readers.TFRecordDataset(self.filenames, - self.compression_type).repeat( - self.num_epochs) - batch_dataset = repeat_dataset.batch(self.batch_size) - - iterator = iterator_ops.Iterator.from_structure(batch_dataset.output_types) - self.init_op = iterator.make_initializer(repeat_dataset) - self.init_batch_op = iterator.make_initializer(batch_dataset) - self.get_next = iterator.get_next() - - def _record(self, f, r): - return compat.as_bytes("Record %d of file %d" % (r, f)) - - def _createFiles(self): - filenames = [] - for i in range(self._num_files): - fn = os.path.join(self.get_temp_dir(), "tf_record.%d.txt" % i) - filenames.append(fn) - writer = python_io.TFRecordWriter(fn) - for j in range(self._num_records): - writer.write(self._record(i, j)) - writer.close() - return filenames - - def testReadOneEpoch(self): - with self.cached_session() as sess: - # Basic test: read from file 0. - sess.run( - self.init_op, - feed_dict={ - self.filenames: [self.test_filenames[0]], - self.num_epochs: 1 - }) - for i in range(self._num_records): - self.assertAllEqual(self._record(0, i), sess.run(self.get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(self.get_next) - - # Basic test: read from file 1. - sess.run( - self.init_op, - feed_dict={ - self.filenames: [self.test_filenames[1]], - self.num_epochs: 1 - }) - for i in range(self._num_records): - self.assertAllEqual(self._record(1, i), sess.run(self.get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(self.get_next) - - # Basic test: read from both files. - sess.run( - self.init_op, - feed_dict={self.filenames: self.test_filenames, - self.num_epochs: 1}) - for j in range(self._num_files): - for i in range(self._num_records): - self.assertAllEqual(self._record(j, i), sess.run(self.get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(self.get_next) - - def testReadTenEpochs(self): - with self.cached_session() as sess: - sess.run( - self.init_op, - feed_dict={self.filenames: self.test_filenames, - self.num_epochs: 10}) - for _ in range(10): - for j in range(self._num_files): - for i in range(self._num_records): - self.assertAllEqual(self._record(j, i), sess.run(self.get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(self.get_next) - - def testReadTenEpochsOfBatches(self): - with self.cached_session() as sess: - sess.run( - self.init_batch_op, - feed_dict={ - self.filenames: self.test_filenames, - self.num_epochs: 10, - self.batch_size: self._num_records - }) - for _ in range(10): - for j in range(self._num_files): - values = sess.run(self.get_next) - self.assertAllEqual( - [self._record(j, i) for i in range(self._num_records)], values) - with self.assertRaises(errors.OutOfRangeError): - sess.run(self.get_next) - - def testReadZlibFiles(self): - zlib_files = [] - for i, fn in enumerate(self.test_filenames): - with open(fn, "rb") as f: - cdata = zlib.compress(f.read()) - - zfn = os.path.join(self.get_temp_dir(), "tfrecord_%s.z" % i) - with open(zfn, "wb") as f: - f.write(cdata) - zlib_files.append(zfn) - - with self.cached_session() as sess: - sess.run( - self.init_op, - feed_dict={self.filenames: zlib_files, - self.compression_type: "ZLIB"}) - for j in range(self._num_files): - for i in range(self._num_records): - self.assertAllEqual(self._record(j, i), sess.run(self.get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(self.get_next) - - def testReadGzipFiles(self): - gzip_files = [] - for i, fn in enumerate(self.test_filenames): - with open(fn, "rb") as f: - gzfn = os.path.join(self.get_temp_dir(), "tfrecord_%s.gz" % i) - with gzip.GzipFile(gzfn, "wb") as gzf: - gzf.write(f.read()) - gzip_files.append(gzfn) - - with self.cached_session() as sess: - sess.run( - self.init_op, - feed_dict={self.filenames: gzip_files, - self.compression_type: "GZIP"}) - for j in range(self._num_files): - for i in range(self._num_records): - self.assertAllEqual(self._record(j, i), sess.run(self.get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(self.get_next) - - def testReadWithBuffer(self): - one_mebibyte = 2**20 - d = readers.TFRecordDataset(self.test_filenames, buffer_size=one_mebibyte) - iterator = d.make_one_shot_iterator() - next_element = iterator.get_next() - with self.cached_session() as sess: - for j in range(self._num_files): - for i in range(self._num_records): - self.assertAllEqual(self._record(j, i), sess.run(next_element)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) - - def testReadFromDatasetOfFiles(self): - files = dataset_ops.Dataset.from_tensor_slices(self.test_filenames) - d = readers.TFRecordDataset(files) - iterator = d.make_one_shot_iterator() - next_element = iterator.get_next() - with self.cached_session() as sess: - for j in range(self._num_files): - for i in range(self._num_records): - self.assertAllEqual(self._record(j, i), sess.run(next_element)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) - - def testReadTenEpochsFromDatasetOfFilesInParallel(self): - files = dataset_ops.Dataset.from_tensor_slices( - self.test_filenames).repeat(10) - d = readers.TFRecordDataset(files, num_parallel_reads=4) - iterator = d.make_one_shot_iterator() - next_element = iterator.get_next() - expected = [] - actual = [] - with self.cached_session() as sess: - for _ in range(10): - for j in range(self._num_files): - for i in range(self._num_records): - expected.append(self._record(j, i)) - actual.append(sess.run(next_element)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) - self.assertEqual(sorted(expected), sorted(actual)) - - -if __name__ == "__main__": - test.main() diff --git a/tensorflow/python/data/kernel_tests/reduce_dataset_op_test.py b/tensorflow/python/data/kernel_tests/reduce_test.py similarity index 72% rename from tensorflow/python/data/kernel_tests/reduce_dataset_op_test.py rename to tensorflow/python/data/kernel_tests/reduce_test.py index 11e07300b9..14bbc0bf72 100644 --- a/tensorflow/python/data/kernel_tests/reduce_dataset_op_test.py +++ b/tensorflow/python/data/kernel_tests/reduce_test.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Tests for the experimental input pipeline ops.""" +"""Tests for `tf.data.Dataset.reduce()`.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function @@ -22,21 +22,24 @@ import numpy as np from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops from tensorflow.python.platform import test -class ReduceDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): +@test_util.run_all_in_graph_and_eager_modes +class ReduceTest(test_base.DatasetTestBase, parameterized.TestCase): def testSum(self): for i in range(10): ds = dataset_ops.Dataset.range(1, i + 1) - result = ds.reduce(np.int64(0), lambda x, y: x + y) - with self.cached_session() as sess: - self.assertEqual(((i + 1) * i) // 2, sess.run(result)) + result = ds.reduce( + constant_op.constant(0, dtype=dtypes.int64), lambda x, y: x + y) + self.assertEqual(((i + 1) * i) // 2, self.evaluate(result)) def testSumTuple(self): @@ -47,9 +50,8 @@ class ReduceDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): for i in range(10): ds = dataset_ops.Dataset.range(1, i + 1) ds = dataset_ops.Dataset.zip((ds, ds)) - result = ds.reduce(np.int64(0), reduce_fn) - with self.cached_session() as sess: - self.assertEqual(((i + 1) * i), sess.run(result)) + result = ds.reduce(constant_op.constant(0, dtype=dtypes.int64), reduce_fn) + self.assertEqual(((i + 1) * i), self.evaluate(result)) def testSumAndCount(self): @@ -59,13 +61,15 @@ class ReduceDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): for i in range(10): ds = dataset_ops.Dataset.range(1, i + 1) - result = ds.reduce((np.int64(0), np.int64(0)), reduce_fn) - with self.cached_session() as sess: - s, c = sess.run(result) - self.assertEqual(((i + 1) * i) // 2, s) - self.assertEqual(i, c) - - def testSquareUsingPlaceholder(self): + result = ds.reduce((constant_op.constant(0, dtype=dtypes.int64), + constant_op.constant(0, dtype=dtypes.int64)), + reduce_fn) + s, c = self.evaluate(result) + self.assertEqual(((i + 1) * i) // 2, s) + self.assertEqual(i, c) + + @test_util.run_deprecated_v1 + def testSkipEagerSquareUsingPlaceholder(self): delta = array_ops.placeholder(dtype=dtypes.int64) def reduce_fn(state, _): @@ -92,8 +96,7 @@ class ReduceDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): for i in range(10): ds = dataset_ops.Dataset.from_tensors(make_sparse_fn(i+1)) result = ds.reduce(make_sparse_fn(0), reduce_fn) - with self.cached_session() as sess: - self.assertSparseValuesEqual(make_sparse_fn(i+1), sess.run(result)) + self.assertSparseValuesEqual(make_sparse_fn(i + 1), self.evaluate(result)) def testNested(self): @@ -115,10 +118,10 @@ class ReduceDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): for i in range(10): ds = dataset_ops.Dataset.range(1, i + 1).map(map_fn) result = ds.reduce(map_fn(0), reduce_fn) - with self.cached_session() as sess: - result = sess.run(result) - self.assertEqual(((i + 1) * i) // 2, result["dense"]) - self.assertSparseValuesEqual(make_sparse_fn(i), result["sparse"]) + result = self.evaluate(result) + self.assertEqual(((i + 1) * i) // 2, result["dense"]) + self.assertSparseValuesEqual(make_sparse_fn(i), result["sparse"]) + if __name__ == "__main__": test.main() diff --git a/tensorflow/python/data/kernel_tests/repeat_test.py b/tensorflow/python/data/kernel_tests/repeat_test.py new file mode 100644 index 0000000000..4ef2fc1bfc --- /dev/null +++ b/tensorflow/python/data/kernel_tests/repeat_test.py @@ -0,0 +1,84 @@ +# 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. +# ============================================================================== +"""Tests for `tf.data.Dataset.repeat()`.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +from tensorflow.python.data.kernel_tests import test_base +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.framework import test_util +from tensorflow.python.platform import test + + +@test_util.run_all_in_graph_and_eager_modes +class RepeatTest(test_base.DatasetTestBase): + + def testRepeatTensorDataset(self): + """Test a dataset that repeats its input multiple times.""" + components = (np.array(1), np.array([1, 2, 3]), np.array(37.0)) + # This placeholder can be fed when dataset-definition subgraph + # runs (i.e. `init_op` below) to configure the number of + # repetitions used in a particular iterator. + + def do_test(count): + dataset = dataset_ops.Dataset.from_tensors(components).repeat(count) + self.assertEqual([c.shape for c in components], + [shape for shape in dataset.output_shapes]) + self.assertDatasetProduces(dataset, [components] * count) + + # Test a finite repetition. + do_test(3) + + # test a different finite repetition. + do_test(7) + + # Test an empty repetition. + do_test(0) + + # Test an infinite repetition. + # NOTE(mrry): There's not a good way to test that the sequence + # actually is infinite. + dataset = dataset_ops.Dataset.from_tensors(components).repeat(-1) + self.assertEqual([c.shape for c in components], + [shape for shape in dataset.output_shapes]) + get_next = self.getNext(dataset) + for _ in range(17): + results = self.evaluate(get_next()) + for component, result_component in zip(components, results): + self.assertAllEqual(component, result_component) + + def testRepeatRepeatTensorDataset(self): + """Test the composition of repeat datasets.""" + components = (np.array(1), np.array([1, 2, 3]), np.array(37.0)) + inner_count, outer_count = 7, 14 + + dataset = dataset_ops.Dataset.from_tensors(components).repeat( + inner_count).repeat(outer_count) + self.assertEqual([c.shape for c in components], + [shape for shape in dataset.output_shapes]) + self.assertDatasetProduces(dataset, + [components] * (inner_count * outer_count)) + + def testRepeatEmptyDataset(self): + """Test that repeating an empty dataset does not hang.""" + dataset = dataset_ops.Dataset.from_tensors(0).repeat(10).skip(10).repeat(-1) + self.assertDatasetProduces(dataset, []) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/data/kernel_tests/sequence_dataset_op_test.py b/tensorflow/python/data/kernel_tests/sequence_dataset_op_test.py deleted file mode 100644 index e86356dee7..0000000000 --- a/tensorflow/python/data/kernel_tests/sequence_dataset_op_test.py +++ /dev/null @@ -1,210 +0,0 @@ -# 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. -# ============================================================================== -"""Tests for the experimental input pipeline ops.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import numpy as np - -from tensorflow.python.data.kernel_tests import test_base -from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.framework import dtypes -from tensorflow.python.framework import errors -from tensorflow.python.ops import array_ops -from tensorflow.python.platform import test - - -class SequenceDatasetTest(test_base.DatasetTestBase): - - def testRepeatTensorDataset(self): - """Test a dataset that repeats its input multiple times.""" - components = (np.array(1), np.array([1, 2, 3]), np.array(37.0)) - # This placeholder can be fed when dataset-definition subgraph - # runs (i.e. `init_op` below) to configure the number of - # repetitions used in a particular iterator. - count_placeholder = array_ops.placeholder(dtypes.int64, shape=[]) - - iterator = (dataset_ops.Dataset.from_tensors(components) - .repeat(count_placeholder).make_initializable_iterator()) - init_op = iterator.initializer - get_next = iterator.get_next() - - self.assertEqual([c.shape for c in components], - [t.shape for t in get_next]) - - with self.cached_session() as sess: - # Test a finite repetition. - sess.run(init_op, feed_dict={count_placeholder: 3}) - for _ in range(3): - results = sess.run(get_next) - for component, result_component in zip(components, results): - self.assertAllEqual(component, result_component) - - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Test a different finite repetition. - sess.run(init_op, feed_dict={count_placeholder: 7}) - for _ in range(7): - results = sess.run(get_next) - for component, result_component in zip(components, results): - self.assertAllEqual(component, result_component) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Test an empty repetition. - sess.run(init_op, feed_dict={count_placeholder: 0}) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Test an infinite repetition. - # NOTE(mrry): There's not a good way to test that the sequence - # actually is infinite. - sess.run(init_op, feed_dict={count_placeholder: -1}) - for _ in range(17): - results = sess.run(get_next) - for component, result_component in zip(components, results): - self.assertAllEqual(component, result_component) - - def testTakeTensorDataset(self): - components = (np.arange(10),) - count_placeholder = array_ops.placeholder(dtypes.int64, shape=[]) - - iterator = (dataset_ops.Dataset.from_tensor_slices(components) - .take(count_placeholder).make_initializable_iterator()) - init_op = iterator.initializer - get_next = iterator.get_next() - - self.assertEqual([c.shape[1:] for c in components], - [t.shape for t in get_next]) - - with self.cached_session() as sess: - # Take fewer than input size - sess.run(init_op, feed_dict={count_placeholder: 4}) - for i in range(4): - results = sess.run(get_next) - self.assertAllEqual(results, components[0][i:i+1]) - - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Take more than input size - sess.run(init_op, feed_dict={count_placeholder: 25}) - for i in range(10): - results = sess.run(get_next) - self.assertAllEqual(results, components[0][i:i+1]) - - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Take all of input - sess.run(init_op, feed_dict={count_placeholder: -1}) - for i in range(10): - results = sess.run(get_next) - self.assertAllEqual(results, components[0][i:i+1]) - - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Take nothing - sess.run(init_op, feed_dict={count_placeholder: 0}) - - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testSkipTensorDataset(self): - components = (np.arange(10),) - count_placeholder = array_ops.placeholder(dtypes.int64, shape=[]) - - iterator = (dataset_ops.Dataset.from_tensor_slices(components) - .skip(count_placeholder).make_initializable_iterator()) - init_op = iterator.initializer - get_next = iterator.get_next() - - self.assertEqual([c.shape[1:] for c in components], - [t.shape for t in get_next]) - - with self.cached_session() as sess: - # Skip fewer than input size, we should skip - # the first 4 elements and then read the rest. - sess.run(init_op, feed_dict={count_placeholder: 4}) - for i in range(4, 10): - results = sess.run(get_next) - self.assertAllEqual(results, components[0][i:i+1]) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Skip more than input size: get nothing. - sess.run(init_op, feed_dict={count_placeholder: 25}) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Skip exactly input size. - sess.run(init_op, feed_dict={count_placeholder: 10}) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Set -1 for 'count': skip the entire dataset. - sess.run(init_op, feed_dict={count_placeholder: -1}) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Skip nothing - sess.run(init_op, feed_dict={count_placeholder: 0}) - for i in range(0, 10): - results = sess.run(get_next) - self.assertAllEqual(results, components[0][i:i+1]) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testRepeatRepeatTensorDataset(self): - """Test the composition of repeat datasets.""" - components = (np.array(1), np.array([1, 2, 3]), np.array(37.0)) - inner_count = array_ops.placeholder(dtypes.int64, shape=[]) - outer_count = array_ops.placeholder(dtypes.int64, shape=[]) - - iterator = (dataset_ops.Dataset.from_tensors(components).repeat(inner_count) - .repeat(outer_count).make_initializable_iterator()) - init_op = iterator.initializer - get_next = iterator.get_next() - - self.assertEqual([c.shape for c in components], - [t.shape for t in get_next]) - - with self.cached_session() as sess: - sess.run(init_op, feed_dict={inner_count: 7, outer_count: 14}) - for _ in range(7 * 14): - results = sess.run(get_next) - for component, result_component in zip(components, results): - self.assertAllEqual(component, result_component) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testRepeatEmptyDataset(self): - """Test that repeating an empty dataset does not hang.""" - iterator = (dataset_ops.Dataset.from_tensors(0).repeat(10).skip(10) - .repeat(-1).make_initializable_iterator()) - init_op = iterator.initializer - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(init_op) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - -if __name__ == "__main__": - test.main() diff --git a/tensorflow/python/data/kernel_tests/shard_dataset_op_test.py b/tensorflow/python/data/kernel_tests/shard_test.py similarity index 52% rename from tensorflow/python/data/kernel_tests/shard_dataset_op_test.py rename to tensorflow/python/data/kernel_tests/shard_test.py index b9f3c79da5..928550676d 100644 --- a/tensorflow/python/data/kernel_tests/shard_dataset_op_test.py +++ b/tensorflow/python/data/kernel_tests/shard_test.py @@ -12,50 +12,33 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Tests for the experimental input pipeline ops.""" +"""Tests for `tf.data.Dataset.shard()`.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.framework import errors +from tensorflow.python.framework import test_util from tensorflow.python.platform import test -class ShardDatasetOpTest(test_base.DatasetTestBase): +@test_util.run_all_in_graph_and_eager_modes +class ShardTest(test_base.DatasetTestBase): def testSimpleCase(self): dataset = dataset_ops.Dataset.range(10).shard(5, 2) - iterator = dataset.make_one_shot_iterator() - - with self.cached_session() as sess: - self.assertEqual(2, sess.run(iterator.get_next())) - self.assertEqual(7, sess.run(iterator.get_next())) - with self.assertRaises(errors.OutOfRangeError): - sess.run(iterator.get_next()) + self.assertDatasetProduces(dataset, expected_output=[2, 7]) def testNestedData(self): dataset_a = dataset_ops.Dataset.range(10) dataset_b = dataset_ops.Dataset.range(10, 0, -1) dataset = dataset_ops.Dataset.zip((dataset_a, dataset_b)).shard(5, 2) - iterator = dataset.make_one_shot_iterator() - - with self.cached_session() as sess: - self.assertEqual((2, 8), sess.run(iterator.get_next())) - self.assertEqual((7, 3), sess.run(iterator.get_next())) - with self.assertRaises(errors.OutOfRangeError): - sess.run(iterator.get_next()) + self.assertDatasetProduces(dataset, expected_output=[(2, 8), (7, 3)]) def testOffsetZero(self): dataset = dataset_ops.Dataset.range(10).shard(5, 0) - iterator = dataset.make_one_shot_iterator() - - with self.cached_session() as sess: - self.assertEqual(0, sess.run(iterator.get_next())) - self.assertEqual(5, sess.run(iterator.get_next())) - with self.assertRaises(errors.OutOfRangeError): - sess.run(iterator.get_next()) + self.assertDatasetProduces(dataset, expected_output=[0, 5]) def testOffsetGreaterNumShards(self): with self.assertRaises(ValueError): @@ -75,38 +58,19 @@ class ShardDatasetOpTest(test_base.DatasetTestBase): def testIteratorEndsBeforeFirstElem(self): dataset = dataset_ops.Dataset.range(1).shard(5, 2) - iterator = dataset.make_one_shot_iterator() - - with self.cached_session() as sess: - with self.assertRaises(errors.OutOfRangeError): - sess.run(iterator.get_next()) + self.assertDatasetProduces(dataset, expected_output=[]) def testLargerWorkerPool(self): dataset = dataset_ops.Dataset.range(10).shard(7, 5) - iterator = dataset.make_one_shot_iterator() - with self.cached_session() as sess: - self.assertEqual(5, sess.run(iterator.get_next())) - with self.assertRaises(errors.OutOfRangeError): - sess.run(iterator.get_next()) + self.assertDatasetProduces(dataset, expected_output=[5]) def testIndexEqualsNumShards(self): dataset = dataset_ops.Dataset.range(10).shard(5, 4) - iterator = dataset.make_one_shot_iterator() - with self.cached_session() as sess: - self.assertEqual(4, sess.run(iterator.get_next())) - self.assertEqual(9, sess.run(iterator.get_next())) - with self.assertRaises(errors.OutOfRangeError): - sess.run(iterator.get_next()) + self.assertDatasetProduces(dataset, expected_output=[4, 9]) def testIndexEqualsNumShards2(self): dataset = dataset_ops.Dataset.range(10).shard(4, 3) - iterator = dataset.make_one_shot_iterator() - with self.cached_session() as sess: - self.assertEqual(3, sess.run(iterator.get_next())) - self.assertEqual(7, sess.run(iterator.get_next())) - with self.assertRaises(errors.OutOfRangeError): - sess.run(iterator.get_next()) - + self.assertDatasetProduces(dataset, expected_output=[3, 7]) if __name__ == "__main__": test.main() diff --git a/tensorflow/python/data/kernel_tests/shuffle_dataset_op_test.py b/tensorflow/python/data/kernel_tests/shuffle_dataset_op_test.py deleted file mode 100644 index cad28f860e..0000000000 --- a/tensorflow/python/data/kernel_tests/shuffle_dataset_op_test.py +++ /dev/null @@ -1,278 +0,0 @@ -# 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. -# ============================================================================== -"""Tests for the experimental input pipeline ops.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import collections - -from absl.testing import parameterized -import numpy as np - -from tensorflow.python.data.kernel_tests import test_base -from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.data.ops import iterator_ops -from tensorflow.python.framework import constant_op -from tensorflow.python.framework import dtypes -from tensorflow.python.framework import errors -from tensorflow.python.framework import ops -from tensorflow.python.framework import random_seed -from tensorflow.python.ops import array_ops -from tensorflow.python.platform import test - - -class ShuffleDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): - - def testShuffleDataset(self): - components = ( - np.array([1, 2, 3, 4]), np.array([5, 6, 7, 8]), - np.array([9.0, 10.0, 11.0, 12.0]) - ) - count_placeholder = array_ops.placeholder_with_default( - constant_op.constant(5, dtypes.int64), shape=[]) - buffer_size_placeholder = array_ops.placeholder(dtypes.int64, shape=[]) - seed_placeholder = array_ops.placeholder(dtypes.int64, shape=[]) - - repeat_dataset = (dataset_ops.Dataset.from_tensor_slices(components) - .repeat(count_placeholder)) - - shuffle_dataset = repeat_dataset.shuffle(buffer_size_placeholder, - seed_placeholder) - - self.assertEqual(tuple([c.shape[1:] for c in components]), - shuffle_dataset.output_shapes) - - # Create initialization ops for iterators without and with - # shuffling, respectively. - iterator = iterator_ops.Iterator.from_structure( - shuffle_dataset.output_types, shuffle_dataset.output_shapes) - init_fifo_op = iterator.make_initializer(repeat_dataset) - init_shuffle_op = iterator.make_initializer(shuffle_dataset) - - get_next = iterator.get_next() - - with self.cached_session() as sess: - # First run without shuffling to collect the "ground truth". - sess.run(init_fifo_op) - unshuffled_elements = [] - for _ in range(20): - unshuffled_elements.append(sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Assert that the shuffled dataset has the same elements as the - # "ground truth". - sess.run( - init_shuffle_op, - feed_dict={buffer_size_placeholder: 100, - seed_placeholder: 37}) - shuffled_elements = [] - for _ in range(20): - shuffled_elements.append(sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - self.assertAllEqual( - sorted(unshuffled_elements), sorted(shuffled_elements)) - - # Assert that shuffling twice with the same seeds gives the same sequence. - sess.run( - init_shuffle_op, - feed_dict={buffer_size_placeholder: 100, - seed_placeholder: 37}) - reshuffled_elements_same_seed = [] - for _ in range(20): - reshuffled_elements_same_seed.append(sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - self.assertEqual(shuffled_elements, reshuffled_elements_same_seed) - - # Assert that shuffling twice with a different seed gives a different - # permutation of the same elements. - sess.run( - init_shuffle_op, - feed_dict={buffer_size_placeholder: 100, - seed_placeholder: 1037}) - reshuffled_elements_different_seed = [] - for _ in range(20): - reshuffled_elements_different_seed.append(sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - self.assertNotEqual(shuffled_elements, reshuffled_elements_different_seed) - self.assertAllEqual( - sorted(shuffled_elements), sorted(reshuffled_elements_different_seed)) - - # Assert that the shuffled dataset has the same elements as the - # "ground truth" when the buffer size is smaller than the input - # dataset. - sess.run( - init_shuffle_op, - feed_dict={buffer_size_placeholder: 2, - seed_placeholder: 37}) - reshuffled_elements_small_buffer = [] - for _ in range(20): - reshuffled_elements_small_buffer.append(sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - self.assertAllEqual( - sorted(unshuffled_elements), sorted(reshuffled_elements_small_buffer)) - - # Test the case of shuffling an empty dataset. - sess.run(init_shuffle_op, feed_dict={buffer_size_placeholder: 2, - seed_placeholder: 37, - count_placeholder: 0}) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testSeedZero(self): - """Test for same behavior when the seed is a Python or Tensor zero.""" - iterator = ( - dataset_ops.Dataset.range(10).shuffle(10, seed=0) - .make_one_shot_iterator()) - get_next = iterator.get_next() - - elems = [] - with self.cached_session() as sess: - for _ in range(10): - elems.append(sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - seed_placeholder = array_ops.placeholder(dtypes.int64, shape=[]) - iterator = ( - dataset_ops.Dataset.range(10).shuffle(10, seed=seed_placeholder) - .make_initializable_iterator()) - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(iterator.initializer, feed_dict={seed_placeholder: 0}) - for elem in elems: - self.assertEqual(elem, sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testDefaultArguments(self): - components = [0, 1, 2, 3, 4] - iterator = (dataset_ops.Dataset.from_tensor_slices(components).shuffle(5) - .repeat().make_one_shot_iterator()) - - get_next = iterator.get_next() - - with self.cached_session() as sess: - counts = collections.defaultdict(lambda: 0) - for _ in range(10): - for _ in range(5): - counts[sess.run(get_next)] += 1 - - for i in range(5): - self.assertEqual(10, counts[i]) - - def testShuffleNoReshuffleEachIteration(self): - iterator = (dataset_ops.Dataset.range(10) - .shuffle(10, reshuffle_each_iteration=False) - .batch(10) - .repeat(3) - .make_one_shot_iterator()) - next_element = iterator.get_next() - - with self.cached_session() as sess: - initial_permutation = sess.run(next_element) - self.assertAllEqual(initial_permutation, sess.run(next_element)) - self.assertAllEqual(initial_permutation, sess.run(next_element)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) - - def testShuffleReshuffleEachIteration(self): - iterator = (dataset_ops.Dataset.range(10) - .shuffle(10, seed=3, reshuffle_each_iteration=True) - .batch(10) - .repeat(3) - .make_one_shot_iterator()) - next_element = iterator.get_next() - - with self.cached_session() as sess: - initial_permutation = list(sess.run(next_element)) - for _ in range(2): - next_permutation = list(sess.run(next_element)) - self.assertNotEqual(initial_permutation, next_permutation) - self.assertAllEqual( - sorted(initial_permutation), sorted(next_permutation)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) - - @parameterized.named_parameters( - ("ReshuffleGraphLevelSeed", True, 38, None), - ("ReshuffleOpLevelSeed", True, None, 42), - ("ReshuffleGraphAndOpLevelSeed", True, 38, 42), - ("NoReshuffleGraphLevelSeed", False, 38, None), - ("NoReshuffleOpLevelSeed", False, None, 42), - ("NoReshuffleGraphAndOpLevelSeed", False, 38, 42), - ) - def testShuffleSeed(self, reshuffle, graph_level_seed, op_level_seed): - results = [] - for _ in range(2): - with ops.Graph().as_default() as g: - random_seed.set_random_seed(graph_level_seed) - dataset = dataset_ops.Dataset.range(10).shuffle( - 10, seed=op_level_seed, reshuffle_each_iteration=reshuffle).repeat( - 3) - iterator = dataset.make_one_shot_iterator() - next_element = iterator.get_next() - - run_results = [] - with self.session(graph=g) as sess: - for _ in range(30): - run_results.append(sess.run(next_element)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) - results.append(run_results) - - self.assertAllEqual(results[0], results[1]) - - @parameterized.named_parameters( - ("ReshuffleOneShot", True, False), - ("ReshuffleInitializable", True, True), - ("NoReshuffleOneShot", False, False), - ("NoReshuffleInitializable", False, True), - ) - def testMultipleIterators(self, reshuffle, initializable): - with ops.Graph().as_default() as g: - dataset = dataset_ops.Dataset.range(100).shuffle( - 10, reshuffle_each_iteration=reshuffle).repeat(3) - - if initializable: - iterators = [dataset.make_initializable_iterator() for _ in range(2)] - else: - iterators = [dataset.make_one_shot_iterator() for _ in range(2)] - - results = [] - with self.session(graph=g) as sess: - for iterator in iterators: - if initializable: - sess.run(iterator.initializer) - next_element = iterator.get_next() - run_results = [] - for _ in range(300): - run_results.append(sess.run(next_element)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) - - results.append(run_results) - - self.assertNotEqual(results[0], results[1]) - - -if __name__ == "__main__": - test.main() diff --git a/tensorflow/python/data/kernel_tests/shuffle_test.py b/tensorflow/python/data/kernel_tests/shuffle_test.py new file mode 100644 index 0000000000..13df870938 --- /dev/null +++ b/tensorflow/python/data/kernel_tests/shuffle_test.py @@ -0,0 +1,249 @@ +# 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. +# ============================================================================== +"""Tests for `tf.data.Dataset.shuffle()`.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import collections + +from absl.testing import parameterized +import numpy as np + +from tensorflow.python.data.kernel_tests import test_base +from tensorflow.python.data.ops import dataset_ops + +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import errors +from tensorflow.python.framework import ops +from tensorflow.python.framework import random_seed +from tensorflow.python.framework import test_util +from tensorflow.python.ops import array_ops +from tensorflow.python.platform import test + + +@test_util.run_all_in_graph_and_eager_modes +class ShuffleTest(test_base.DatasetTestBase, parameterized.TestCase): + + def testShuffleDataset(self): + components = ( + np.array([1, 2, 3, 4]), np.array([5, 6, 7, 8]), + np.array([9.0, 10.0, 11.0, 12.0]) + ) + + def dataset_fn(count=5, buffer_size=None, seed=0): + repeat_dataset = ( + dataset_ops.Dataset.from_tensor_slices(components).repeat(count)) + if buffer_size: + shuffle_dataset = repeat_dataset.shuffle(buffer_size, seed) + + self.assertEqual( + tuple([c.shape[1:] for c in components]), + shuffle_dataset.output_shapes) + return shuffle_dataset + else: + return repeat_dataset + + # First run without shuffling to collect the "ground truth". + get_next = self.getNext(dataset_fn()) + unshuffled_elements = [] + for _ in range(20): + unshuffled_elements.append(self.evaluate(get_next())) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + + # Assert that the shuffled dataset has the same elements as the + # "ground truth". + get_next = self.getNext(dataset_fn(buffer_size=100, seed=37)) + shuffled_elements = [] + for _ in range(20): + shuffled_elements.append(self.evaluate(get_next())) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + self.assertAllEqual(sorted(unshuffled_elements), sorted(shuffled_elements)) + + # Assert that shuffling twice with the same seeds gives the same sequence. + get_next = self.getNext(dataset_fn(buffer_size=100, seed=37)) + reshuffled_elements_same_seed = [] + for _ in range(20): + reshuffled_elements_same_seed.append(self.evaluate(get_next())) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + self.assertEqual(shuffled_elements, reshuffled_elements_same_seed) + + # Assert that shuffling twice with a different seed gives a different + # permutation of the same elements. + get_next = self.getNext(dataset_fn(buffer_size=100, seed=137)) + reshuffled_elements_different_seed = [] + for _ in range(20): + reshuffled_elements_different_seed.append(self.evaluate(get_next())) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + self.assertNotEqual(shuffled_elements, reshuffled_elements_different_seed) + self.assertAllEqual( + sorted(shuffled_elements), sorted(reshuffled_elements_different_seed)) + + # Assert that the shuffled dataset has the same elements as the + # "ground truth" when the buffer size is smaller than the input + # dataset. + get_next = self.getNext(dataset_fn(buffer_size=2, seed=37)) + reshuffled_elements_small_buffer = [] + for _ in range(20): + reshuffled_elements_small_buffer.append(self.evaluate(get_next())) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + self.assertAllEqual( + sorted(unshuffled_elements), sorted(reshuffled_elements_small_buffer)) + + # Test the case of shuffling an empty dataset. + get_next = self.getNext(dataset_fn(count=0, buffer_size=100, seed=37)) + + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + + @test_util.run_deprecated_v1 + def testSkipEagerSeedZero(self): + """Test for same behavior when the seed is a Python or Tensor zero.""" + iterator = dataset_ops.make_one_shot_iterator( + dataset_ops.Dataset.range(10).shuffle(10, seed=0)) + get_next = iterator.get_next() + + elems = [] + with self.cached_session() as sess: + for _ in range(10): + elems.append(sess.run(get_next)) + with self.assertRaises(errors.OutOfRangeError): + sess.run(get_next) + + seed_placeholder = array_ops.placeholder(dtypes.int64, shape=[]) + iterator = dataset_ops.make_initializable_iterator( + dataset_ops.Dataset.range(10).shuffle(10, seed=seed_placeholder)) + get_next = iterator.get_next() + + with self.cached_session() as sess: + sess.run(iterator.initializer, feed_dict={seed_placeholder: 0}) + for elem in elems: + self.assertEqual(elem, sess.run(get_next)) + with self.assertRaises(errors.OutOfRangeError): + sess.run(get_next) + + def testDefaultArguments(self): + components = [0, 1, 2, 3, 4] + dataset = dataset_ops.Dataset.from_tensor_slices(components).shuffle( + 5).repeat() + get_next = self.getNext(dataset) + counts = collections.defaultdict(lambda: 0) + for _ in range(10): + for _ in range(5): + counts[self.evaluate(get_next())] += 1 + + for i in range(5): + self.assertEqual(10, counts[i]) + + def testShuffleNoReshuffleEachIteration(self): + dataset = dataset_ops.Dataset.range(10).shuffle( + 10, reshuffle_each_iteration=False).batch(10).repeat(3) + next_element = self.getNext(dataset) + + initial_permutation = self.evaluate(next_element()) + self.assertAllEqual(initial_permutation, self.evaluate(next_element())) + self.assertAllEqual(initial_permutation, self.evaluate(next_element())) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(next_element()) + + def testShuffleReshuffleEachIteration(self): + dataset = dataset_ops.Dataset.range(10).shuffle( + 10, seed=3, reshuffle_each_iteration=True).batch(10).repeat(3) + next_element = self.getNext(dataset) + + initial_permutation = list(self.evaluate(next_element())) + for _ in range(2): + next_permutation = list(self.evaluate(next_element())) + self.assertNotEqual(initial_permutation, next_permutation) + self.assertAllEqual(sorted(initial_permutation), sorted(next_permutation)) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(next_element()) + + @parameterized.named_parameters( + ("ReshuffleGraphLevelSeed", True, 38, None), + ("ReshuffleOpLevelSeed", True, None, 42), + ("ReshuffleGraphAndOpLevelSeed", True, 38, 42), + ("NoReshuffleGraphLevelSeed", False, 38, None), + ("NoReshuffleOpLevelSeed", False, None, 42), + ("NoReshuffleGraphAndOpLevelSeed", False, 38, 42), + ) + def testSkipEagerShuffleSeed(self, reshuffle, graph_level_seed, + op_level_seed): + results = [] + for _ in range(2): + with ops.Graph().as_default() as g: + random_seed.set_random_seed(graph_level_seed) + dataset = dataset_ops.Dataset.range(10).shuffle( + 10, seed=op_level_seed, reshuffle_each_iteration=reshuffle).repeat( + 3) + iterator = dataset_ops.make_one_shot_iterator(dataset) + next_element = iterator.get_next() + + run_results = [] + with self.session(graph=g) as sess: + for _ in range(30): + run_results.append(sess.run(next_element)) + with self.assertRaises(errors.OutOfRangeError): + sess.run(next_element) + results.append(run_results) + + self.assertAllEqual(results[0], results[1]) + + # TODO(b/117581999): fails for eager mode with result[0] equal to result[1], + # debug. + @parameterized.named_parameters( + ("ReshuffleOneShot", True, False), + ("ReshuffleInitializable", True, True), + ("NoReshuffleOneShot", False, False), + ("NoReshuffleInitializable", False, True), + ) + def testSkipEagerMultipleIterators(self, reshuffle, initializable): + with ops.Graph().as_default() as g: + dataset = dataset_ops.Dataset.range(100).shuffle( + 10, reshuffle_each_iteration=reshuffle).repeat(3) + + if initializable: + iterators = [dataset_ops.make_initializable_iterator(dataset) + for _ in range(2)] + else: + iterators = [dataset_ops.make_one_shot_iterator(dataset) + for _ in range(2)] + + results = [] + with self.session(graph=g) as sess: + for iterator in iterators: + if initializable: + sess.run(iterator.initializer) + next_element = iterator.get_next() + run_results = [] + for _ in range(300): + run_results.append(sess.run(next_element)) + with self.assertRaises(errors.OutOfRangeError): + sess.run(next_element) + + results.append(run_results) + + self.assertNotEqual(results[0], results[1]) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/data/kernel_tests/skip_test.py b/tensorflow/python/data/kernel_tests/skip_test.py new file mode 100644 index 0000000000..c22be57692 --- /dev/null +++ b/tensorflow/python/data/kernel_tests/skip_test.py @@ -0,0 +1,62 @@ +# 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. +# ============================================================================== +"""Tests for `tf.data.Dataset.skip()`.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +from tensorflow.python.data.kernel_tests import test_base +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.framework import test_util +from tensorflow.python.platform import test + + +@test_util.run_all_in_graph_and_eager_modes +class SkipTest(test_base.DatasetTestBase): + + def testSkipTensorDataset(self): + components = (np.arange(10),) + + def do_test(count): + dataset = dataset_ops.Dataset.from_tensor_slices(components).skip(count) + self.assertEqual([c.shape[1:] for c in components], + [shape for shape in dataset.output_shapes]) + start_range = min(count, 10) if count != -1 else 10 + self.assertDatasetProduces( + dataset, + [tuple(components[0][i:i + 1]) for i in range(start_range, 10)]) + + # Skip fewer than input size, we should skip + # the first 4 elements and then read the rest. + do_test(4) + + # Skip more than input size: get nothing. + do_test(25) + + # Skip exactly input size. + do_test(10) + + # Set -1 for 'count': skip the entire dataset. + do_test(-1) + + # Skip nothing + do_test(0) + + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/data/kernel_tests/take_test.py b/tensorflow/python/data/kernel_tests/take_test.py new file mode 100644 index 0000000000..03a7ece2d8 --- /dev/null +++ b/tensorflow/python/data/kernel_tests/take_test.py @@ -0,0 +1,55 @@ +# 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. +# ============================================================================== +"""Tests for `tf.data.Dataset.take()`.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +from tensorflow.python.data.kernel_tests import test_base +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.framework import test_util +from tensorflow.python.platform import test + + +@test_util.run_all_in_graph_and_eager_modes +class TakeTest(test_base.DatasetTestBase): + + def testTakeTensorDataset(self): + components = (np.arange(10),) + + def do_test(count): + dataset = dataset_ops.Dataset.from_tensor_slices(components).take(count) + self.assertEqual([c.shape[1:] for c in components], + [shape for shape in dataset.output_shapes]) + num_output = min(count, 10) if count != -1 else 10 + self.assertDatasetProduces( + dataset, [tuple(components[0][i:i + 1]) for i in range(num_output)]) + + # Take fewer than input size + do_test(4) + + # Take more than input size + do_test(25) + + # Take all of input + do_test(-1) + + # Take nothing + do_test(0) + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/data/kernel_tests/test_base.py b/tensorflow/python/data/kernel_tests/test_base.py index edb3eff3c1..85f6c9de23 100644 --- a/tensorflow/python/data/kernel_tests/test_base.py +++ b/tensorflow/python/data/kernel_tests/test_base.py @@ -38,56 +38,102 @@ class DatasetTestBase(test.TestCase): self.assertAllEqual(a.values, b.values) self.assertAllEqual(a.dense_shape, b.dense_shape) - def getNext(self, dataset): + def getNext(self, dataset, requires_initialization=False): """Returns a callable that returns the next element of the dataset. Example use: ```python # In both graph and eager modes dataset = ... - nxt = self.getNext(dataset) - result = self.evaluate(nxt()) + get_next = self.getNext(dataset) + result = self.evaluate(get_next()) ``` Args: - dataset: A dataset whose next element is returned - + dataset: A dataset whose elements will be returned. + requires_initialization: Indicates that when the test is executed in graph + mode, it should use an initializable iterator to iterate through the + dataset (e.g. when it contains stateful nodes). Defaults to False. Returns: - A callable that returns the next element of `dataset` + A callable that returns the next element of `dataset`. """ - it = dataset.make_one_shot_iterator() if context.executing_eagerly(): - return it.get_next + iterator = dataset.__iter__() + return iterator._next_internal # pylint: disable=protected-access else: - nxt = it.get_next() - return lambda: nxt - - def _compare_output_to_expected(self, result_values, expected_values): - for i in range(len(result_values)): - if sparse_tensor.is_sparse(result_values[i]): - self.assertSparseValuesEqual(result_values[i], expected_values[i]) + if requires_initialization: + iterator = dataset_ops.make_initializable_iterator(dataset) + self.evaluate(iterator.initializer) else: - self.assertAllEqual(result_values[i], expected_values[i]) + iterator = dataset_ops.make_one_shot_iterator(dataset) + get_next = iterator.get_next() + return lambda: get_next + + def _compareOutputToExpected(self, result_values, expected_values, + assert_items_equal): + if assert_items_equal: + # TODO(shivaniagrawal): add support for nested elements containing sparse + # tensors when needed. + self.assertItemsEqual(result_values, expected_values) + return + for i in range(len(result_values)): + nest.assert_same_structure(result_values[i], expected_values[i]) + for result_value, expected_value in zip( + nest.flatten(result_values[i]), nest.flatten(expected_values[i])): + if sparse_tensor.is_sparse(result_value): + self.assertSparseValuesEqual(result_value, expected_value) + else: + self.assertAllEqual(result_value, expected_value) def assertDatasetProduces(self, - input_dataset, + dataset, expected_output=None, - expected_err=None, - create_iterator_twice=True): + expected_error=None, + requires_initialization=False, + num_test_iterations=1, + assert_items_equal=False): + """Asserts that a dataset produces the expected output / error. - if expected_err: - with self.assertRaisesWithPredicateMatch(expected_err[0], - expected_err[1]): - get_next = self.getNext(input_dataset) + Args: + dataset: A dataset to check for the expected output / error. + expected_output: A list of elements that the dataset is expected to + produce. + expected_error: A tuple `(type, predicate)` identifying the expected error + `dataset` should raise. The `type` should match the expected exception + type, while `predicate` should either be 1) a unary function that inputs + the raised exception and returns a boolean indicator of success or 2) a + regular expression that is expected to match the error message + partially. + requires_initialization: Indicates that when the test is executed in graph + mode, it should use an initializable iterator to iterate through the + dataset (e.g. when it contains stateful nodes). Defaults to False. + num_test_iterations: Number of times `dataset` will be iterated. Defaults + to 2. + assert_items_equal: Tests expected_output has (only) the same elements + regardless of order. + """ + self.assertTrue( + expected_error is not None or expected_output is not None, + "Exactly one of expected_output or expected error should be provided.") + if expected_error: + self.assertTrue( + expected_output is None, + "Exactly one of expected_output or expected error should be provided." + ) + with self.assertRaisesWithPredicateMatch(expected_error[0], + expected_error[1]): + get_next = self.getNext( + dataset, requires_initialization=requires_initialization) self.evaluate(get_next()) return - repeated = 2 if create_iterator_twice else 1 - for _ in range(repeated): - get_next = self.getNext(input_dataset) + self.assertGreater(num_test_iterations, 0) + for _ in range(num_test_iterations): + get_next = self.getNext( + dataset, requires_initialization=requires_initialization) result = [] for _ in range(len(expected_output)): result.append(self.evaluate(get_next())) - self._compare_output_to_expected(result, expected_output) + self._compareOutputToExpected(result, expected_output, assert_items_equal) with self.assertRaises(errors.OutOfRangeError): self.evaluate(get_next()) with self.assertRaises(errors.OutOfRangeError): @@ -132,7 +178,7 @@ class DatasetTestBase(test.TestCase): try: self.evaluate(next1()) raise ValueError( - 'Expected dataset to raise an error of type %s, but it did not.' % + "Expected dataset to raise an error of type %s, but it did not." % repr(exception_class)) except exception_class as e: expected_message = e.message diff --git a/tensorflow/python/data/kernel_tests/text_line_dataset_test.py b/tensorflow/python/data/kernel_tests/text_line_dataset_test.py new file mode 100644 index 0000000000..4db09a9808 --- /dev/null +++ b/tensorflow/python/data/kernel_tests/text_line_dataset_test.py @@ -0,0 +1,165 @@ +# 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. +# ============================================================================== +"""Tests for `tf.data.TextLineDataset`.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import gzip +import os +import zlib + +from tensorflow.python.data.kernel_tests import test_base +from tensorflow.python.data.ops import readers +from tensorflow.python.eager import context +from tensorflow.python.framework import test_util +from tensorflow.python.platform import test +from tensorflow.python.util import compat + + +try: + import psutil # pylint: disable=g-import-not-at-top + psutil_import_succeeded = True +except ImportError: + psutil_import_succeeded = False + + +@test_util.run_all_in_graph_and_eager_modes +class TextLineDatasetTest(test_base.DatasetTestBase): + + def _lineText(self, f, l): + return compat.as_bytes("%d: %d" % (f, l)) + + def _createFiles(self, + num_files, + num_lines, + crlf=False, + compression_type=None): + filenames = [] + for i in range(num_files): + fn = os.path.join(self.get_temp_dir(), "text_line.%d.txt" % i) + filenames.append(fn) + contents = [] + for j in range(num_lines): + contents.append(self._lineText(i, j)) + # Always include a newline after the record unless it is + # at the end of the file, in which case we include it + if j + 1 != num_lines or i == 0: + contents.append(b"\r\n" if crlf else b"\n") + contents = b"".join(contents) + + if not compression_type: + with open(fn, "wb") as f: + f.write(contents) + elif compression_type == "GZIP": + with gzip.GzipFile(fn, "wb") as f: + f.write(contents) + elif compression_type == "ZLIB": + contents = zlib.compress(contents) + with open(fn, "wb") as f: + f.write(contents) + else: + raise ValueError("Unsupported compression_type", compression_type) + + return filenames + + def _testTextLineDataset(self, compression_type=None): + test_filenames = self._createFiles( + 2, 5, crlf=True, compression_type=compression_type) + + def dataset_fn(filenames, num_epochs, batch_size=None): + repeat_dataset = readers.TextLineDataset( + filenames, compression_type=compression_type).repeat(num_epochs) + if batch_size: + return repeat_dataset.batch(batch_size) + return repeat_dataset + + # Basic test: read from file 0. + expected_output = [self._lineText(0, i) for i in range(5)] + self.assertDatasetProduces( + dataset_fn([test_filenames[0]], 1), expected_output=expected_output) + + # Basic test: read from file 1. + self.assertDatasetProduces( + dataset_fn([test_filenames[1]], 1), + expected_output=[self._lineText(1, i) for i in range(5)]) + + # Basic test: read from both files. + expected_output = [self._lineText(0, i) for i in range(5)] + expected_output.extend([self._lineText(1, i) for i in range(5)]) + self.assertDatasetProduces( + dataset_fn(test_filenames, 1), expected_output=expected_output) + + # Test repeated iteration through both files. + expected_output = [self._lineText(0, i) for i in range(5)] + expected_output.extend([self._lineText(1, i) for i in range(5)]) + self.assertDatasetProduces( + dataset_fn(test_filenames, 10), expected_output=expected_output * 10) + + # Test batched and repeated iteration through both files. + self.assertDatasetProduces( + dataset_fn(test_filenames, 10, 5), + expected_output=[[self._lineText(0, i) for i in range(5)], + [self._lineText(1, i) for i in range(5)]] * 10) + + def testTextLineDatasetNoCompression(self): + self._testTextLineDataset() + + def testTextLineDatasetGzipCompression(self): + self._testTextLineDataset(compression_type="GZIP") + + def testTextLineDatasetZlibCompression(self): + self._testTextLineDataset(compression_type="ZLIB") + + def testTextLineDatasetBuffering(self): + test_filenames = self._createFiles(2, 5, crlf=True) + + repeat_dataset = readers.TextLineDataset(test_filenames, buffer_size=10) + expected_output = [] + for j in range(2): + expected_output.extend([self._lineText(j, i) for i in range(5)]) + self.assertDatasetProduces(repeat_dataset, expected_output=expected_output) + + def testIteratorResourceCleanup(self): + filename = os.path.join(self.get_temp_dir(), "text.txt") + with open(filename, "wt") as f: + for i in range(3): + f.write("%d\n" % (i,)) + with context.eager_mode(): + first_iterator = iter(readers.TextLineDataset(filename)) + self.assertEqual(b"0", next(first_iterator).numpy()) + second_iterator = iter(readers.TextLineDataset(filename)) + self.assertEqual(b"0", next(second_iterator).numpy()) + # Eager kernel caching is based on op attributes, which includes the + # Dataset's output shape. Create a different kernel to test that they + # don't create resources with the same names. + different_kernel_iterator = iter( + readers.TextLineDataset(filename).repeat().batch(16)) + self.assertEqual([16], next(different_kernel_iterator).shape) + # Remove our references to the Python Iterator objects, which (assuming no + # reference cycles) is enough to trigger DestroyResourceOp and close the + # partially-read files. + del first_iterator + del second_iterator + del different_kernel_iterator + if not psutil_import_succeeded: + self.skipTest( + "psutil is required to check that we've closed our files.") + open_files = psutil.Process().open_files() + self.assertNotIn(filename, [open_file.path for open_file in open_files]) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/data/kernel_tests/tf_record_dataset_test.py b/tensorflow/python/data/kernel_tests/tf_record_dataset_test.py new file mode 100644 index 0000000000..13a70aa88d --- /dev/null +++ b/tensorflow/python/data/kernel_tests/tf_record_dataset_test.py @@ -0,0 +1,170 @@ +# 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. +# ============================================================================== +"""Tests for `tf.data.TFRecordDataset`.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import gzip +import os +import zlib + +from tensorflow.python.data.kernel_tests import test_base +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.data.ops import readers +from tensorflow.python.framework import test_util +from tensorflow.python.lib.io import python_io +from tensorflow.python.platform import test +from tensorflow.python.util import compat + + +@test_util.run_all_in_graph_and_eager_modes +class TFRecordDatasetTest(test_base.DatasetTestBase): + + def setUp(self): + super(TFRecordDatasetTest, self).setUp() + self._num_files = 2 + self._num_records = 7 + + self.test_filenames = self._createFiles() + + def dataset_fn(self, + filenames, + compression_type="", + num_epochs=1, + batch_size=None): + + repeat_dataset = readers.TFRecordDataset( + filenames, compression_type).repeat(num_epochs) + if batch_size: + return repeat_dataset.batch(batch_size) + return repeat_dataset + + def _record(self, f, r): + return compat.as_bytes("Record %d of file %d" % (r, f)) + + def _createFiles(self): + filenames = [] + for i in range(self._num_files): + fn = os.path.join(self.get_temp_dir(), "tf_record.%d.txt" % i) + filenames.append(fn) + writer = python_io.TFRecordWriter(fn) + for j in range(self._num_records): + writer.write(self._record(i, j)) + writer.close() + return filenames + + def testReadOneEpoch(self): + # Basic test: read from file 0. + dataset = self.dataset_fn(self.test_filenames[0]) + self.assertDatasetProduces( + dataset, + expected_output=[self._record(0, i) for i in range(self._num_records)]) + + # Basic test: read from file 1. + dataset = self.dataset_fn(self.test_filenames[1]) + self.assertDatasetProduces( + dataset, + expected_output=[self._record(1, i) for i in range(self._num_records)]) + + # Basic test: read from both files. + dataset = self.dataset_fn(self.test_filenames) + expected_output = [] + for j in range(self._num_files): + expected_output.extend( + [self._record(j, i) for i in range(self._num_records)]) + self.assertDatasetProduces(dataset, expected_output=expected_output) + + def testReadTenEpochs(self): + dataset = self.dataset_fn(self.test_filenames, num_epochs=10) + expected_output = [] + for j in range(self._num_files): + expected_output.extend( + [self._record(j, i) for i in range(self._num_records)]) + self.assertDatasetProduces(dataset, expected_output=expected_output * 10) + + def testReadTenEpochsOfBatches(self): + dataset = self.dataset_fn( + self.test_filenames, num_epochs=10, batch_size=self._num_records) + expected_output = [] + for j in range(self._num_files): + expected_output.append( + [self._record(j, i) for i in range(self._num_records)]) + self.assertDatasetProduces(dataset, expected_output=expected_output * 10) + + def testReadZlibFiles(self): + zlib_files = [] + for i, fn in enumerate(self.test_filenames): + with open(fn, "rb") as f: + cdata = zlib.compress(f.read()) + + zfn = os.path.join(self.get_temp_dir(), "tfrecord_%s.z" % i) + with open(zfn, "wb") as f: + f.write(cdata) + zlib_files.append(zfn) + expected_output = [] + for j in range(self._num_files): + expected_output.extend( + [self._record(j, i) for i in range(self._num_records)]) + dataset = self.dataset_fn(zlib_files, compression_type="ZLIB") + self.assertDatasetProduces(dataset, expected_output=expected_output) + + def testReadGzipFiles(self): + gzip_files = [] + for i, fn in enumerate(self.test_filenames): + with open(fn, "rb") as f: + gzfn = os.path.join(self.get_temp_dir(), "tfrecord_%s.gz" % i) + with gzip.GzipFile(gzfn, "wb") as gzf: + gzf.write(f.read()) + gzip_files.append(gzfn) + expected_output = [] + for j in range(self._num_files): + expected_output.extend( + [self._record(j, i) for i in range(self._num_records)]) + dataset = self.dataset_fn(gzip_files, compression_type="GZIP") + self.assertDatasetProduces(dataset, expected_output=expected_output) + + def testReadWithBuffer(self): + one_mebibyte = 2**20 + dataset = readers.TFRecordDataset( + self.test_filenames, buffer_size=one_mebibyte) + expected_output = [] + for j in range(self._num_files): + expected_output.extend( + [self._record(j, i) for i in range(self._num_records)]) + self.assertDatasetProduces(dataset, expected_output=expected_output) + + def testReadFromDatasetOfFiles(self): + files = dataset_ops.Dataset.from_tensor_slices(self.test_filenames) + expected_output = [] + for j in range(self._num_files): + expected_output.extend( + [self._record(j, i) for i in range(self._num_records)]) + dataset = readers.TFRecordDataset(files) + self.assertDatasetProduces(dataset, expected_output=expected_output) + + def testReadTenEpochsFromDatasetOfFilesInParallel(self): + files = dataset_ops.Dataset.from_tensor_slices( + self.test_filenames).repeat(10) + expected_output = [] + for j in range(self._num_files): + expected_output.extend( + [self._record(j, i) for i in range(self._num_records)]) + dataset = readers.TFRecordDataset(files, num_parallel_reads=4) + self.assertDatasetProduces( + dataset, expected_output=expected_output * 10, assert_items_equal=True) + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/data/kernel_tests/window_dataset_op_test.py b/tensorflow/python/data/kernel_tests/window_dataset_op_test.py deleted file mode 100644 index 9d06781094..0000000000 --- a/tensorflow/python/data/kernel_tests/window_dataset_op_test.py +++ /dev/null @@ -1,291 +0,0 @@ -# Copyright 2018 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 the experimental input pipeline ops.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -from absl.testing import parameterized -import numpy as np - -from tensorflow.python.data.kernel_tests import test_base -from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.framework import dtypes -from tensorflow.python.framework import errors -from tensorflow.python.framework import sparse_tensor -from tensorflow.python.ops import array_ops -from tensorflow.python.ops import math_ops -from tensorflow.python.platform import test - - -class WindowDatasetTest(test_base.DatasetTestBase, parameterized.TestCase): - - @parameterized.named_parameters( - ("1", 20, 14, 7, 1), - ("2", 20, 17, 9, 1), - ("3", 20, 14, 14, 1), - ("4", 20, 10, 14, 1), - ("5", 20, 14, 19, 1), - ("6", 20, 4, 1, 2), - ("7", 20, 2, 1, 6), - ("8", 20, 4, 7, 2), - ("9", 20, 2, 7, 6), - ("10", 1, 10, 4, 1), - ("11", 0, 10, 4, 1), - ("12", 20, 14, 7, 1, False), - ("13", 20, 17, 9, 1, False), - ("14", 20, 14, 14, 1, False), - ("15", 20, 10, 14, 1, False), - ("16", 20, 14, 19, 1, False), - ("17", 20, 4, 1, 2, False), - ("18", 20, 2, 1, 6, False), - ("19", 20, 4, 7, 2, False), - ("20", 20, 2, 7, 6, False), - ("21", 1, 10, 4, 1, False), - ("22", 0, 10, 4, 1, False), - ) - def testWindowDataset(self, count, size, shift, stride, drop_remainder=True): - """Tests a dataset that slides a window its input elements.""" - components = (np.arange(7), - np.array([[1, 2, 3]]) * np.arange(7)[:, np.newaxis], - np.array(37.0) * np.arange(7)) - - count_t = array_ops.placeholder(dtypes.int64, shape=[]) - size_t = array_ops.placeholder(dtypes.int64, shape=[]) - shift_t = array_ops.placeholder(dtypes.int64, shape=[]) - stride_t = array_ops.placeholder(dtypes.int64, shape=[]) - drop_remainder_t = array_ops.placeholder(dtypes.bool, shape=[]) - - def _map_fn(x, y, z): - return math_ops.square(x), math_ops.square(y), math_ops.square(z) - - def _flat_map_fn(x, y, z): - return dataset_ops.Dataset.zip((x.batch(batch_size=size_t), - y.batch(batch_size=size_t), - z.batch(batch_size=size_t))) - - iterator = dataset_ops.Dataset.from_tensor_slices(components).map( - _map_fn).repeat(count).window( - size=size_t, - shift=shift_t, - stride=stride_t, - drop_remainder=drop_remainder_t).flat_map( - _flat_map_fn).make_initializable_iterator() - init_op = iterator.initializer - get_next = iterator.get_next() - - self.assertEqual([[None] + list(c.shape[1:]) for c in components], - [t.shape.as_list() for t in get_next]) - - with self.cached_session() as sess: - sess.run( - init_op, - feed_dict={ - count_t: count, - size_t: size, - shift_t: shift, - stride_t: stride, - drop_remainder_t: drop_remainder - }) - num_full_batches = max( - 0, (count * 7 - ((size - 1) * stride + 1)) // shift + 1) - for i in range(num_full_batches): - result = sess.run(get_next) - for component, result_component in zip(components, result): - for j in range(size): - self.assertAllEqual(component[(i * shift + j * stride) % 7]**2, - result_component[j]) - if not drop_remainder: - num_partial_batches = (count * 7) // shift + ( - (count * 7) % shift > 0) - num_full_batches - for i in range(num_partial_batches): - result = sess.run(get_next) - for component, result_component in zip(components, result): - remaining = (count * 7) - ((num_full_batches + i) * shift) - num_elements = remaining // stride + ((remaining % stride) > 0) - for j in range(num_elements): - self.assertAllEqual( - component[((num_full_batches + i) * shift + j * stride) % 7] - **2, result_component[j]) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - @parameterized.named_parameters( - ("1", 14, 0, 3, 1), - ("2", 14, 3, 0, 1), - ("3", 14, 3, 3, 0), - ) - def testWindowDatasetInvalid(self, count, size, shift, stride): - count_t = array_ops.placeholder(dtypes.int64, shape=[]) - size_t = array_ops.placeholder(dtypes.int64, shape=[]) - shift_t = array_ops.placeholder(dtypes.int64, shape=[]) - stride_t = array_ops.placeholder(dtypes.int64, shape=[]) - - iterator = dataset_ops.Dataset.range(10).map(lambda x: x).repeat( - count_t).window( - size=size_t, shift=shift_t, - stride=stride_t).flat_map(lambda x: x.batch(batch_size=size_t) - ).make_initializable_iterator() - init_op = iterator.initializer - - with self.cached_session() as sess: - with self.assertRaises(errors.InvalidArgumentError): - sess.run( - init_op, - feed_dict={ - count_t: count, - size_t: size, - shift_t: shift, - stride_t: stride - }) - - def testWindowSparse(self): - - def _sparse(i): - return sparse_tensor.SparseTensorValue( - indices=[[0]], values=(i * [1]), dense_shape=[1]) - - iterator = dataset_ops.Dataset.range(10).map(_sparse).window( - size=5, shift=3, drop_remainder=True).flat_map( - lambda x: x.batch(batch_size=5)).make_initializable_iterator() - init_op = iterator.initializer - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(init_op) - num_batches = (10 - 5) // 3 + 1 - for i in range(num_batches): - actual = sess.run(get_next) - expected = sparse_tensor.SparseTensorValue( - indices=[[0, 0], [1, 0], [2, 0], [3, 0], [4, 0]], - values=[i * 3, i * 3 + 1, i * 3 + 2, i * 3 + 3, i * 3 + 4], - dense_shape=[5, 1]) - self.assertTrue(sparse_tensor.is_sparse(actual)) - self.assertSparseValuesEqual(actual, expected) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testWindowSparseWithDifferentDenseShapes(self): - - def _sparse(i): - return sparse_tensor.SparseTensorValue( - indices=array_ops.expand_dims( - math_ops.range(i, dtype=dtypes.int64), 1), - values=array_ops.fill([math_ops.to_int32(i)], i), - dense_shape=[i]) - - iterator = dataset_ops.Dataset.range(10).map(_sparse).window( - size=5, shift=3, drop_remainder=True).flat_map( - lambda x: x.batch(batch_size=5)).make_initializable_iterator() - init_op = iterator.initializer - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(init_op) - num_batches = (10 - 5) // 3 + 1 - for i in range(num_batches): - actual = sess.run(get_next) - expected_indices = [] - expected_values = [] - for j in range(5): - for k in range(i * 3 + j): - expected_indices.append([j, k]) - expected_values.append(i * 3 + j) - expected = sparse_tensor.SparseTensorValue( - indices=expected_indices, - values=expected_values, - dense_shape=[5, i * 3 + 5 - 1]) - self.assertTrue(sparse_tensor.is_sparse(actual)) - self.assertSparseValuesEqual(actual, expected) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testNestedWindowSparse(self): - - def _sparse(i): - return sparse_tensor.SparseTensorValue( - indices=[[0]], values=(i * [1]), dense_shape=[1]) - - iterator = dataset_ops.Dataset.range(10).map(_sparse).window( - size=4, shift=2, - drop_remainder=True).flat_map(lambda x: x.batch(batch_size=4)).window( - size=3, shift=1, drop_remainder=True).flat_map( - lambda x: x.batch(batch_size=3)).make_initializable_iterator() - init_op = iterator.initializer - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(init_op) - # Slide: 1st batch. - actual = sess.run(get_next) - expected = sparse_tensor.SparseTensorValue( - indices=[[0, 0, 0], [0, 1, 0], [0, 2, 0], [0, 3, 0], [1, 0, 0], - [1, 1, 0], [1, 2, 0], [1, 3, 0], [2, 0, 0], [2, 1, 0], - [2, 2, 0], [2, 3, 0]], - values=[0, 1, 2, 3, 2, 3, 4, 5, 4, 5, 6, 7], - dense_shape=[3, 4, 1]) - self.assertTrue(sparse_tensor.is_sparse(actual)) - self.assertSparseValuesEqual(actual, expected) - # Slide: 2nd batch. - actual = sess.run(get_next) - expected = sparse_tensor.SparseTensorValue( - indices=[[0, 0, 0], [0, 1, 0], [0, 2, 0], [0, 3, 0], [1, 0, 0], - [1, 1, 0], [1, 2, 0], [1, 3, 0], [2, 0, 0], [2, 1, 0], - [2, 2, 0], [2, 3, 0]], - values=[2, 3, 4, 5, 4, 5, 6, 7, 6, 7, 8, 9], - dense_shape=[3, 4, 1]) - self.assertTrue(sparse_tensor.is_sparse(actual)) - self.assertSparseValuesEqual(actual, expected) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testWindowShapeError(self): - - def generator(): - yield [1.0, 2.0, 3.0] - yield [4.0, 5.0, 6.0] - yield [7.0, 8.0, 9.0, 10.0] - - iterator = dataset_ops.Dataset.from_generator( - generator, dtypes.float32, output_shapes=[None]).window( - size=3, shift=1).flat_map( - lambda x: x.batch(batch_size=3)).make_initializable_iterator() - next_element = iterator.get_next() - - with self.cached_session() as sess: - sess.run(iterator.initializer) - with self.assertRaisesRegexp( - errors.InvalidArgumentError, - r"Cannot batch tensors with different shapes in component 0. " - r"First element had shape \[3\] and element 2 had shape \[4\]."): - sess.run(next_element) - - def testWindowIgnoreErrors(self): - input_values = np.float32([1., np.nan, 2., np.nan, 3.]) - dataset = dataset_ops.Dataset.from_tensor_slices(input_values).map( - lambda x: array_ops.check_numerics(x, "message")).window( - size=2, shift=2, stride=2, - drop_remainder=True).flat_map(lambda x: x.batch(batch_size=2)) - get_next = dataset.make_one_shot_iterator().get_next() - - with self.cached_session() as sess: - self.assertAllEqual(np.float32([1., 2.]), sess.run(get_next)) - self.assertAllEqual(np.float32([2., 3.]), sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - -if __name__ == "__main__": - test.main() diff --git a/tensorflow/python/data/kernel_tests/window_test.py b/tensorflow/python/data/kernel_tests/window_test.py new file mode 100644 index 0000000000..d083142ab6 --- /dev/null +++ b/tensorflow/python/data/kernel_tests/window_test.py @@ -0,0 +1,231 @@ +# Copyright 2018 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 `tf.data.Dataset.window()`.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from absl.testing import parameterized +import numpy as np + +from tensorflow.python.data.kernel_tests import test_base +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.data.util import nest +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import errors +from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import test_util +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.platform import test + + +@test_util.run_all_in_graph_and_eager_modes +class WindowTest(test_base.DatasetTestBase, parameterized.TestCase): + + @parameterized.named_parameters( + ("1", 20, 14, 7, 1), + ("2", 20, 17, 9, 1), + ("3", 20, 14, 14, 1), + ("4", 20, 10, 14, 1), + ("5", 20, 14, 19, 1), + ("6", 20, 4, 1, 2), + ("7", 20, 2, 1, 6), + ("8", 20, 4, 7, 2), + ("9", 20, 2, 7, 6), + ("10", 1, 10, 4, 1), + ("11", 0, 10, 4, 1), + ("12", 20, 14, 7, 1, False), + ("13", 20, 17, 9, 1, False), + ("14", 20, 14, 14, 1, False), + ("15", 20, 10, 14, 1, False), + ("16", 20, 14, 19, 1, False), + ("17", 20, 4, 1, 2, False), + ("18", 20, 2, 1, 6, False), + ("19", 20, 4, 7, 2, False), + ("20", 20, 2, 7, 6, False), + ("21", 1, 10, 4, 1, False), + ("22", 0, 10, 4, 1, False), + ) + def testWindowDataset(self, count, size, shift, stride, drop_remainder=True): + """Tests a dataset that slides a window its input elements.""" + components = (np.arange(7), + np.array([[1, 2, 3]]) * np.arange(7)[:, np.newaxis], + np.array(37.0) * np.arange(7)) + + def _map_fn(x, y, z): + return math_ops.square(x), math_ops.square(y), math_ops.square(z) + + def _flat_map_fn(x, y, z): + return dataset_ops.Dataset.zip((x.batch(batch_size=size), + y.batch(batch_size=size), + z.batch(batch_size=size))) + + dataset = dataset_ops.Dataset.from_tensor_slices(components).map( + _map_fn).repeat(count).window( + size=size, + shift=shift, + stride=stride, + drop_remainder=drop_remainder).flat_map(_flat_map_fn) + get_next = self.getNext(dataset) + + self.assertEqual( + [[None] + list(c.shape[1:]) for c in components], + [ts.as_list() for ts in nest.flatten(dataset.output_shapes)]) + + num_full_batches = max(0, + (count * 7 - ((size - 1) * stride + 1)) // shift + 1) + for i in range(num_full_batches): + result = self.evaluate(get_next()) + for component, result_component in zip(components, result): + for j in range(size): + self.assertAllEqual(component[(i * shift + j * stride) % 7]**2, + result_component[j]) + if not drop_remainder: + num_partial_batches = (count * 7) // shift + ( + (count * 7) % shift > 0) - num_full_batches + for i in range(num_partial_batches): + result = self.evaluate(get_next()) + for component, result_component in zip(components, result): + remaining = (count * 7) - ((num_full_batches + i) * shift) + num_elements = remaining // stride + ((remaining % stride) > 0) + for j in range(num_elements): + self.assertAllEqual( + component[((num_full_batches + i) * shift + j * stride) % 7]**2, + result_component[j]) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + + @parameterized.named_parameters( + ("1", 14, 0, 3, 1), + ("2", 14, 3, 0, 1), + ("3", 14, 3, 3, 0), + ) + def testWindowDatasetInvalid(self, count, size, shift, stride): + dataset = dataset_ops.Dataset.range(10).map(lambda x: x).repeat( + count).window( + size=size, shift=shift, + stride=stride).flat_map(lambda x: x.batch(batch_size=size)) + self.assertDatasetProduces( + dataset, expected_error=(errors.InvalidArgumentError, "")) + + def testWindowSparse(self): + + def _sparse(i): + return sparse_tensor.SparseTensorValue( + indices=[[0]], values=(i * [1]), dense_shape=[1]) + + dataset = dataset_ops.Dataset.range(10).map(_sparse).window( + size=5, shift=3, + drop_remainder=True).flat_map(lambda x: x.batch(batch_size=5)) + + num_batches = (10 - 5) // 3 + 1 + expected_output = [ + sparse_tensor.SparseTensorValue( + indices=[[0, 0], [1, 0], [2, 0], [3, 0], [4, 0]], + values=[i * 3, i * 3 + 1, i * 3 + 2, i * 3 + 3, i * 3 + 4], + dense_shape=[5, 1]) for i in range(num_batches) + ] + self.assertDatasetProduces(dataset, expected_output=expected_output) + + def testWindowSparseWithDifferentDenseShapes(self): + + def _sparse(i): + return sparse_tensor.SparseTensorValue( + indices=array_ops.expand_dims( + math_ops.range(i, dtype=dtypes.int64), 1), + values=array_ops.fill([math_ops.to_int32(i)], i), + dense_shape=[i]) + + dataset = dataset_ops.Dataset.range(10).map(_sparse).window( + size=5, shift=3, + drop_remainder=True).flat_map(lambda x: x.batch(batch_size=5)) + + expected_output = [] + num_batches = (10 - 5) // 3 + 1 + for i in range(num_batches): + expected_indices = [] + expected_values = [] + for j in range(5): + for k in range(i * 3 + j): + expected_indices.append([j, k]) + expected_values.append(i * 3 + j) + expected_output.append( + sparse_tensor.SparseTensorValue( + indices=expected_indices, + values=expected_values, + dense_shape=[5, i * 3 + 5 - 1])) + self.assertDatasetProduces(dataset, expected_output=expected_output) + + def testNestedWindowSparse(self): + + def _sparse(i): + return sparse_tensor.SparseTensorValue( + indices=[[0]], values=(i * [1]), dense_shape=[1]) + + dataset = dataset_ops.Dataset.range(10).map(_sparse).window( + size=4, shift=2, + drop_remainder=True).flat_map(lambda x: x.batch(batch_size=4)).window( + size=3, shift=1, + drop_remainder=True).flat_map(lambda x: x.batch(batch_size=3)) + + expected_output = [ + sparse_tensor.SparseTensorValue( + indices=[[0, 0, 0], [0, 1, 0], [0, 2, 0], [0, 3, 0], [1, 0, 0], + [1, 1, 0], [1, 2, 0], [1, 3, 0], [2, 0, 0], [2, 1, 0], + [2, 2, 0], [2, 3, 0]], + values=[0, 1, 2, 3, 2, 3, 4, 5, 4, 5, 6, 7], + dense_shape=[3, 4, 1]), + sparse_tensor.SparseTensorValue( + indices=[[0, 0, 0], [0, 1, 0], [0, 2, 0], [0, 3, 0], [1, 0, 0], + [1, 1, 0], [1, 2, 0], [1, 3, 0], [2, 0, 0], [2, 1, 0], + [2, 2, 0], [2, 3, 0]], + values=[2, 3, 4, 5, 4, 5, 6, 7, 6, 7, 8, 9], + dense_shape=[3, 4, 1]) + ] + self.assertDatasetProduces(dataset, expected_output=expected_output) + + def testWindowShapeError(self): + + def generator(): + yield [1.0, 2.0, 3.0] + yield [4.0, 5.0, 6.0] + yield [7.0, 8.0, 9.0, 10.0] + + dataset = dataset_ops.Dataset.from_generator( + generator, dtypes.float32, output_shapes=[None]).window( + size=3, shift=1).flat_map(lambda x: x.batch(batch_size=3)) + self.assertDatasetProduces( + dataset, + expected_error=( + errors.InvalidArgumentError, + r"Cannot batch tensors with different shapes in component 0. " + r"First element had shape \[3\] and element 2 had shape \[4\].")) + + def testWindowIgnoreErrors(self): + input_values = np.float32([1., np.nan, 2., np.nan, 3.]) + dataset = dataset_ops.Dataset.from_tensor_slices(input_values).map( + lambda x: array_ops.check_numerics(x, "message")).window( + size=2, shift=2, stride=2, + drop_remainder=True).flat_map(lambda x: x.batch(batch_size=2)) + self.assertDatasetProduces( + dataset, expected_output=[np.float32([1., 2.]), + np.float32([2., 3.])]) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/data/kernel_tests/zip_dataset_op_test.py b/tensorflow/python/data/kernel_tests/zip_dataset_op_test.py deleted file mode 100644 index 9d76387a34..0000000000 --- a/tensorflow/python/data/kernel_tests/zip_dataset_op_test.py +++ /dev/null @@ -1,115 +0,0 @@ -# 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. -# ============================================================================== -"""Tests for the experimental input pipeline ops.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import numpy as np - -from tensorflow.python.data.kernel_tests import test_base -from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.framework import dtypes -from tensorflow.python.framework import errors -from tensorflow.python.ops import array_ops -from tensorflow.python.platform import test - - -class ZipDatasetTest(test_base.DatasetTestBase): - - def testZipDataset(self): - component_placeholders = [ - array_ops.placeholder(dtypes.int64), - array_ops.placeholder(dtypes.int64), - array_ops.placeholder(dtypes.float64) - ] - - datasets = tuple([ - dataset_ops.Dataset.from_tensor_slices(component_placeholder) - for component_placeholder in component_placeholders - ]) - zipped = dataset_ops.Dataset.zip(datasets) - - iterator = zipped.make_initializable_iterator() - init_op = iterator.initializer - get_next = iterator.get_next() - - with self.cached_session() as sess: - equal_length_components = [ - np.tile(np.array([[1], [2], [3], [4]]), 20), - np.tile(np.array([[12], [13], [14], [15]]), 22), - np.array([37.0, 38.0, 39.0, 40.0]) - ] - sess.run(init_op, feed_dict={ph: value for ph, value in zip( - component_placeholders, equal_length_components)}) - for i in range(4): - results = sess.run(get_next) - for component, result_component in zip( - equal_length_components, results): - self.assertAllEqual(component[i], result_component) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - variable_length_components = [[1, 2, 3, 4], [1, 2, 3, 4, 5], [1.0, 2.0]] - sess.run(init_op, feed_dict={ph: value for ph, value in zip( - component_placeholders, variable_length_components)}) - for i in range(2): - results = sess.run(get_next) - for component, result_component in zip( - variable_length_components, results): - self.assertAllEqual(component[i], result_component) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def testNestedZipDataset(self): - component_placeholders = [ - array_ops.placeholder(dtypes.int64, shape=[4, 20]), - array_ops.placeholder(dtypes.int64, shape=[4, 22]), - array_ops.placeholder(dtypes.float64, shape=[4]) - ] - - datasets = [ - dataset_ops.Dataset.from_tensor_slices(component_placeholder) - for component_placeholder in component_placeholders - ] - zipped = dataset_ops.Dataset.zip((datasets[0], (datasets[1], datasets[2]))) - - iterator = zipped.make_initializable_iterator() - init_op = iterator.initializer - get_next = iterator.get_next() - - self.assertEqual([20], get_next[0].shape) - self.assertEqual([22], get_next[1][0].shape) - self.assertEqual([], get_next[1][1].shape) - - with self.cached_session() as sess: - equal_length_components = [ - np.tile(np.array([[1], [2], [3], [4]]), 20), - np.tile(np.array([[12], [13], [14], [15]]), 22), - np.array([37.0, 38.0, 39.0, 40.0]) - ] - sess.run(init_op, feed_dict={ph: value for ph, value in zip( - component_placeholders, equal_length_components)}) - for i in range(4): - result1, (result2, result3) = sess.run(get_next) - self.assertAllEqual(equal_length_components[0][i], result1) - self.assertAllEqual(equal_length_components[1][i], result2) - self.assertAllEqual(equal_length_components[2][i], result3) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - -if __name__ == "__main__": - test.main() diff --git a/tensorflow/python/data/kernel_tests/zip_test.py b/tensorflow/python/data/kernel_tests/zip_test.py new file mode 100644 index 0000000000..477c9fa7da --- /dev/null +++ b/tensorflow/python/data/kernel_tests/zip_test.py @@ -0,0 +1,101 @@ +# 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. +# ============================================================================== +"""Tests for `tf.data.Dataset.zip()`.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +from tensorflow.python.data.kernel_tests import test_base +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.framework import errors +from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util +from tensorflow.python.platform import test + + +@test_util.run_all_in_graph_and_eager_modes +class ZipTest(test_base.DatasetTestBase): + + def testZipDataset(self): + + def dataset_fn(components): + datasets = tuple([ + dataset_ops.Dataset.from_tensor_slices(component) + for component in components + ]) + return dataset_ops.Dataset.zip(datasets) + + equal_length_components = [ + np.tile(np.array([[1], [2], [3], [4]]), 20), + np.tile(np.array([[12], [13], [14], [15]]), 22), + np.array([37.0, 38.0, 39.0, 40.0]) + ] + + get_next = self.getNext(dataset_fn(equal_length_components)) + for i in range(4): + results = self.evaluate(get_next()) + for component, result_component in zip(equal_length_components, results): + self.assertAllEqual(component[i], result_component) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + + variable_length_components = [[1, 2, 3, 4], [1, 2, 3, 4, 5], [1.0, 2.0]] + get_next = self.getNext(dataset_fn(variable_length_components)) + for i in range(2): + results = self.evaluate(get_next()) + for component, result_component in zip(variable_length_components, + results): + self.assertAllEqual(component[i], result_component) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + + def testNestedZipDataset(self): + + equal_length_components = [ + np.tile(np.array([[1], [2], [3], [4]]), 20), + np.tile(np.array([[12], [13], [14], [15]]), 22), + np.array([37.0, 38.0, 39.0, 40.0]) + ] + datasets = [ + dataset_ops.Dataset.from_tensor_slices(component) + for component in equal_length_components + ] + dataset = dataset_ops.Dataset.zip((datasets[0], (datasets[1], datasets[2]))) + + self.assertEqual( + dataset.output_shapes, + (tensor_shape.TensorShape([20]), + (tensor_shape.TensorShape([22]), tensor_shape.TensorShape([])))) + + get_next = self.getNext(dataset) + for i in range(4): + result1, (result2, result3) = self.evaluate(get_next()) + self.assertAllEqual(equal_length_components[0][i], result1) + self.assertAllEqual(equal_length_components[1][i], result2) + self.assertAllEqual(equal_length_components[2][i], result3) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/data/ops/BUILD b/tensorflow/python/data/ops/BUILD index 18edc0872d..0c5acda180 100644 --- a/tensorflow/python/data/ops/BUILD +++ b/tensorflow/python/data/ops/BUILD @@ -14,6 +14,7 @@ py_library( "//tensorflow/python:control_flow_ops", "//tensorflow/python:dataset_ops_gen", "//tensorflow/python:dtypes", + "//tensorflow/python:experimental_dataset_ops_gen", "//tensorflow/python:framework_ops", "//tensorflow/python:function", "//tensorflow/python:math_ops", @@ -25,8 +26,12 @@ py_library( "//tensorflow/python:tensor_shape", "//tensorflow/python:tensor_util", "//tensorflow/python:util", + "//tensorflow/python/data/experimental/ops:filter_for_shard_ops", + "//tensorflow/python/data/experimental/ops:optimization_options", "//tensorflow/python/data/experimental/ops:stats_options", + "//tensorflow/python/data/experimental/ops:threading_options", "//tensorflow/python/data/util:nest", + "//tensorflow/python/data/util:options", "//tensorflow/python/data/util:random_seed", "//tensorflow/python/data/util:sparse", "//tensorflow/python/data/util:structure", diff --git a/tensorflow/python/data/ops/dataset_ops.py b/tensorflow/python/data/ops/dataset_ops.py index 3836a68e7d..ba6d20373e 100644 --- a/tensorflow/python/data/ops/dataset_ops.py +++ b/tensorflow/python/data/ops/dataset_ops.py @@ -18,6 +18,7 @@ from __future__ import division from __future__ import print_function import abc +import functools import threading import warnings @@ -25,11 +26,16 @@ import numpy as np import six from tensorflow.python.compat import compat +from tensorflow.python.data.experimental.ops import filter_for_shard_ops +from tensorflow.python.data.experimental.ops import optimization_options from tensorflow.python.data.experimental.ops import stats_options +from tensorflow.python.data.experimental.ops import threading_options from tensorflow.python.data.ops import iterator_ops from tensorflow.python.data.util import nest +from tensorflow.python.data.util import options as options_lib from tensorflow.python.data.util import random_seed from tensorflow.python.data.util import sparse +from tensorflow.python.data.util import structure as structure_lib from tensorflow.python.eager import context from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes @@ -43,6 +49,7 @@ from tensorflow.python.framework import tensor_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import gen_dataset_ops +from tensorflow.python.ops import gen_experimental_dataset_ops as ged_ops from tensorflow.python.ops import gen_io_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import script_ops @@ -52,9 +59,12 @@ from tensorflow.python.util import function_utils from tensorflow.python.util.tf_export import tf_export -@tf_export("data.Dataset") +ops.NotDifferentiable("ReduceDataset") + + +@tf_export("data.Dataset", v1=[]) @six.add_metaclass(abc.ABCMeta) -class Dataset(object): +class DatasetV2(object): """Represents a potentially large set of elements. A `Dataset` can be used to represent an input pipeline as a @@ -62,9 +72,6 @@ class Dataset(object): plan" of transformations that act on those elements. """ - def __init__(self): - pass - def _as_serialized_graph(self): """Produces serialized graph representation of the dataset. @@ -89,6 +96,37 @@ class Dataset(object): raise NotImplementedError("Dataset._inputs") + def _has_captured_ref(self): + """Whether this dataset uses a function that captures ref variables. + + Returns: + A boolean, which if true indicates that the dataset or one of its inputs + uses a function that captures ref variables. + """ + if context.executing_eagerly(): + # RefVariables are not supported in eager mode + return False + + def is_tensor_or_parent_ref(tensor): + if tensor.dtype._is_ref_dtype: # pylint: disable=protected-access + return True + return any([is_tensor_or_parent_ref(x) for x in tensor.op.inputs]) + + for fn in self._functions(): + if any([is_tensor_or_parent_ref(t) for t in fn.function.captured_inputs]): + return True + + return any( + [input_dataset._has_captured_ref() for input_dataset in self._inputs()]) # pylint: disable=protected-access + + def _functions(self): + """Returns a list of functions associated with this dataset. + + Returns: + A list of `StructuredFunctionWrapper` objects. + """ + return [] + def options(self): """Returns the options for this dataset and its inputs. @@ -107,9 +145,26 @@ class Dataset(object): dataset = self options = self.options() + if options.experimental_threading is not None: + t_options = options.experimental_threading + if t_options.private_threadpool_size is not None: + dataset = _PrivateThreadPoolDataset(dataset, + t_options.private_threadpool_size) + if t_options.max_intra_op_parallelism is not None: + dataset = _MaxIntraOpParallelismDataset( + dataset, t_options.max_intra_op_parallelism) static_optimizations = options._static_optimizations() # pylint: disable=protected-access if static_optimizations: - dataset = _OptimizeDataset(dataset, static_optimizations) + if self._has_captured_ref(): + warnings.warn( + "tf.data static optimizations are not compatible with tf.Variable. " + "The following optimizations will be disabled: %s. To enable " + "optimizations, use resource variables instead by calling " + "`tf.enable_resource_variables()` at the start of the program." % + ", ".join(static_optimizations)) + else: + dataset = _OptimizeDataset(dataset, static_optimizations) + if options.experimental_autotune is not False: dataset = _ModelDataset(dataset) if options.experimental_stats and options.experimental_stats.aggregator: # pylint: disable=line-too-long @@ -119,51 +174,6 @@ class Dataset(object): options.experimental_stats.counter_prefix) return dataset - def make_initializable_iterator(self, shared_name=None): - """Creates an `Iterator` for enumerating the elements of this dataset. - - Note: The returned iterator will be in an uninitialized state, - and you must run the `iterator.initializer` operation before using it: - - ```python - dataset = ... - iterator = dataset.make_initializable_iterator() - # ... - sess.run(iterator.initializer) - ``` - - Args: - shared_name: (Optional.) If non-empty, the returned iterator will be - shared under the given name across multiple sessions that share the - same devices (e.g. when using a remote server). - - Returns: - An `Iterator` over the elements of this dataset. - - Raises: - RuntimeError: If eager execution is enabled. - """ - if context.executing_eagerly(): - raise RuntimeError( - "dataset.make_initializable_iterator is not supported when eager " - "execution is enabled.") - dataset = self._apply_options() - if shared_name is None: - shared_name = "" - if compat.forward_compatible(2018, 8, 3): - iterator_resource = gen_dataset_ops.iterator_v2( - container="", shared_name=shared_name, **flat_structure(self)) - else: - iterator_resource = gen_dataset_ops.iterator( - container="", shared_name=shared_name, **flat_structure(self)) - with ops.colocate_with(iterator_resource): - initializer = gen_dataset_ops.make_iterator( - dataset._as_variant_tensor(), # pylint: disable=protected-access - iterator_resource) - return iterator_ops.Iterator(iterator_resource, initializer, - dataset.output_types, dataset.output_shapes, - dataset.output_classes) - def __iter__(self): """Creates an `Iterator` for enumerating the elements of this dataset. @@ -183,55 +193,6 @@ class Dataset(object): raise RuntimeError("dataset.__iter__() is only supported when eager " "execution is enabled.") - def make_one_shot_iterator(self): - """Creates an `Iterator` for enumerating the elements of this dataset. - - Note: The returned iterator will be initialized automatically. - A "one-shot" iterator does not currently support re-initialization. - - Returns: - An `Iterator` over the elements of this dataset. - """ - if context.executing_eagerly(): - dataset = self._apply_options() - return iterator_ops.EagerIterator(dataset) - - graph_level_seed, op_level_seed = core_random_seed.get_seed(None) - - # NOTE(mrry): We capture by value here to ensure that `_make_dataset()` is - # a 0-argument function. - @function.Defun(capture_by_value=True) - def _make_dataset(): - """Factory function for a dataset.""" - # NOTE(mrry): `Defun` does not capture the graph-level seed from the - # enclosing graph, so if a graph-level seed is present we set the local - # graph seed based on a combination of the graph- and op-level seeds. - if graph_level_seed is not None: - assert op_level_seed is not None - core_random_seed.set_random_seed( - (graph_level_seed + 87654321 * op_level_seed) % (2 ** 63 - 1)) - - dataset = self._apply_options() - return dataset._as_variant_tensor() # pylint: disable=protected-access - - try: - _make_dataset.add_to_graph(ops.get_default_graph()) - except ValueError as err: - if "Cannot capture a stateful node" in str(err): - raise ValueError( - "Failed to create a one-shot iterator for a dataset. " - "`Dataset.make_one_shot_iterator()` does not support datasets that " - "capture stateful objects, such as a `Variable` or `LookupTable`. " - "In these cases, use `Dataset.make_initializable_iterator()`. " - "(Original error: %s)" % err) - else: - six.reraise(ValueError, err) - - return iterator_ops.Iterator( - gen_dataset_ops.one_shot_iterator( - dataset_factory=_make_dataset, **flat_structure(self)), - None, self.output_types, self.output_shapes, self.output_classes) - @abc.abstractproperty def output_classes(self): """Returns the class of each component of an element of this dataset. @@ -279,9 +240,10 @@ class Dataset(object): Note that if `tensors` contains a NumPy array, and eager execution is not enabled, the values will be embedded in the graph as one or more `tf.constant` operations. For large datasets (> 1 GB), this can waste - memory and run into byte limits of graph serialization. If tensors contains - one or more large NumPy arrays, consider the alternative described in - [this guide](https://tensorflow.org/guide/datasets#consuming_numpy_arrays). + memory and run into byte limits of graph serialization. If `tensors` + contains one or more large NumPy arrays, consider the alternative described + in [this + guide](https://tensorflow.org/guide/datasets#consuming_numpy_arrays). Args: tensors: A nested structure of tensors. @@ -298,9 +260,10 @@ class Dataset(object): Note that if `tensors` contains a NumPy array, and eager execution is not enabled, the values will be embedded in the graph as one or more `tf.constant` operations. For large datasets (> 1 GB), this can waste - memory and run into byte limits of graph serialization. If tensors contains - one or more large NumPy arrays, consider the alternative described in - [this guide](https://tensorflow.org/guide/datasets#consuming_numpy_arrays). + memory and run into byte limits of graph serialization. If `tensors` + contains one or more large NumPy arrays, consider the alternative described + in [this guide]( + https://tensorflow.org/guide/datasets#consuming_numpy_arrays). Args: tensors: A nested structure of tensors, each having the same size in the @@ -311,19 +274,6 @@ class Dataset(object): """ return TensorSliceDataset(tensors) - @staticmethod - @deprecation.deprecated(None, "Use `tf.data.Dataset.from_tensor_slices()`.") - def from_sparse_tensor_slices(sparse_tensor): - """Splits each rank-N `tf.SparseTensor` in this dataset row-wise. - - Args: - sparse_tensor: A `tf.SparseTensor`. - - Returns: - Dataset: A `Dataset` of rank-(N-1) sparse tensors. - """ - return SparseTensorSliceDataset(sparse_tensor) - class _GeneratorState(object): """Stores outstanding iterators created from a Python generator. @@ -373,17 +323,19 @@ class Dataset(object): ```python import itertools + tf.enable_eager_execution() def gen(): for i in itertools.count(1): yield (i, [1] * i) - ds = Dataset.from_generator( + ds = tf.data.Dataset.from_generator( gen, (tf.int64, tf.int64), (tf.TensorShape([]), tf.TensorShape([None]))) - value = ds.make_one_shot_iterator().get_next() - sess.run(value) # (1, array([1])) - sess.run(value) # (2, array([1, 1])) + for value in ds.take(2): + print value + # (1, array([1])) + # (2, array([1, 1])) ``` NOTE: The current implementation of `Dataset.from_generator()` uses @@ -435,7 +387,7 @@ class Dataset(object): flattened_types = [dtypes.as_dtype(dt) for dt in nest.flatten(output_types)] flattened_shapes = nest.flatten(output_shapes) - generator_state = Dataset._GeneratorState(generator) + generator_state = DatasetV2._GeneratorState(generator) def get_iterator_id_fn(unused_dummy): """Creates a unique `iterator_id` for each pass over the dataset. @@ -580,7 +532,7 @@ class Dataset(object): ``` Args: - *args: follow same semantics as python's xrange. + *args: follows the same semantics as python's xrange. len(args) == 1 -> start = 0, stop = args[0], step = 1 len(args) == 2 -> start = args[0], stop = args[1], step = 1 len(args) == 3 -> start = args[0], stop = args[1, stop = args[2] @@ -820,78 +772,6 @@ class Dataset(object): """ return SkipDataset(self, count) - def shard(self, num_shards, index): - """Creates a `Dataset` that includes only 1/`num_shards` of this dataset. - - This dataset operator is very useful when running distributed training, as - it allows each worker to read a unique subset. - - When reading a single input file, you can skip elements as follows: - - ```python - d = tf.data.TFRecordDataset(FLAGS.input_file) - d = d.shard(FLAGS.num_workers, FLAGS.worker_index) - d = d.repeat(FLAGS.num_epochs) - d = d.shuffle(FLAGS.shuffle_buffer_size) - d = d.map(parser_fn, num_parallel_calls=FLAGS.num_map_threads) - ``` - - Important caveats: - - - Be sure to shard before you use any randomizing operator (such as - shuffle). - - Generally it is best if the shard operator is used early in the dataset - pipeline. For example, when reading from a set of TFRecord files, shard - before converting the dataset to input samples. This avoids reading every - file on every worker. The following is an example of an efficient - sharding strategy within a complete pipeline: - - ```python - d = Dataset.list_files(FLAGS.pattern) - d = d.shard(FLAGS.num_workers, FLAGS.worker_index) - d = d.repeat(FLAGS.num_epochs) - d = d.shuffle(FLAGS.shuffle_buffer_size) - d = d.interleave(tf.data.TFRecordDataset, - cycle_length=FLAGS.num_readers, block_length=1) - d = d.map(parser_fn, num_parallel_calls=FLAGS.num_map_threads) - ``` - - Args: - num_shards: A `tf.int64` scalar `tf.Tensor`, representing the number of - shards operating in parallel. - index: A `tf.int64` scalar `tf.Tensor`, representing the worker index. - - Returns: - Dataset: A `Dataset`. - - Raises: - ValueError: if `num_shards` or `index` are illegal values. Note: error - checking is done on a best-effort basis, and aren't guaranteed to be - caught upon dataset creation. (e.g. providing in a placeholder tensor - bypasses the early checking, and will instead result in an error during - a session.run call.) - """ - num_shards = ops.convert_to_tensor( - num_shards, name="num_shards", dtype=dtypes.int64) - num_shards_static = tensor_util.constant_value(num_shards) - index = ops.convert_to_tensor(index, name="index", dtype=dtypes.int64) - index_static = tensor_util.constant_value(index) - - if num_shards_static is not None and num_shards_static < 1: - raise ValueError("num_shards must be >= 1; got: %s" % num_shards_static) - if index_static is not None and index_static < 0: - raise ValueError("index must be >= 0; got: %s" % index_static) - if (index_static is not None and num_shards_static is not None and - index_static >= num_shards_static): - raise ValueError("index must be <= num_shards; %s is not < %s" % - (index_static, num_shards_static)) - - def filter_fn(elem_index, _): - mod_result = math_ops.mod(elem_index, num_shards) - return math_ops.equal(mod_result, index) - - return self._enumerate().filter(filter_fn).map(lambda _, elem: elem) - def batch(self, batch_size, drop_remainder=False): """Combines consecutive elements of this dataset into batches. @@ -906,7 +786,7 @@ class Dataset(object): batch_size: A `tf.int64` scalar `tf.Tensor`, representing the number of consecutive elements of this dataset to combine in a single batch. drop_remainder: (Optional.) A `tf.bool` scalar `tf.Tensor`, representing - whether the last batch should be dropped in the case its has fewer than + whether the last batch should be dropped in the case it has fewer than `batch_size` elements; the default behavior is not to drop the smaller batch. @@ -963,7 +843,7 @@ class Dataset(object): respective components. Defaults are `0` for numeric types and the empty string for string types. drop_remainder: (Optional.) A `tf.bool` scalar `tf.Tensor`, representing - whether the last batch should be dropped in the case its has fewer than + whether the last batch should be dropped in the case it has fewer than `batch_size` elements; the default behavior is not to drop the smaller batch. @@ -1217,7 +1097,7 @@ class Dataset(object): dataset. """ dataset = transformation_func(self) - if not isinstance(dataset, Dataset): + if not isinstance(dataset, DatasetV2): raise TypeError("`transformation_func` must return a Dataset.") dataset._input_datasets = [self] # pylint: disable=protected-access return dataset @@ -1384,10 +1264,9 @@ class Dataset(object): def with_options(self, options): """Returns a new `tf.data.Dataset` with the given options set. - The options are "global" in the sense they apply to the entire input - pipeline in which the `with_options` transformation is used. If options are - set multiple times, they are merged if possible (see - `tf.data.Options.merge()` for details). + The options are "global" in the sense they apply to the entire dataset. + If options are set multiple times, they are merged as long as different + options do not use different non-default values. Args: options: A `tf.data.Options` that identifies the options the use. @@ -1396,164 +1275,497 @@ class Dataset(object): Dataset: A `Dataset` with the given options. Raises: - ValueError: if options are set more than once + ValueError: when an option is set more than once to a non-default value """ return _OptionsDataset(self, options) -@tf_export("data.Options") -class Options(object): - """Represents options for tf.data.Dataset. +@tf_export(v1=["data.Dataset"]) +class DatasetV1(DatasetV2): + """Represents a potentially large set of elements. - An `Options` object can be for instance used to control which static - optimizations to apply or whether to use performance modeling to dynamically - tune the parallelism of operations such as `tf.data.Dataset.map` or - `tf.data.Dataset.interleave`. + A `Dataset` can be used to represent an input pipeline as a + collection of elements (nested structures of tensors) and a "logical + plan" of transformations that act on those elements. """ - for _name, _ty, _docstring in [ - ("experimental_autotune", bool, - "Whether to dynamically adjust the values of tunable parameters (e.g. " - "degrees of parallelism)."), - ("experimental_deterministic", bool, - "Whether the outputs need to be produced in deterministic order."), - ("experimental_filter_fusion", bool, - "Whether to fuse filter transformations."), - ("experimental_hoist_random_uniform", bool, - "Whether to hoist `tf.random_uniform()` ops out of map transformations." - ), - ("experimental_stats", stats_options.StatsOptions, - "Associate the given statistics options with the dataset pipeline."), - ("experimental_map_and_batch_fusion", bool, - "Whether to fuse map and batch transformations."), - ("experimental_map_and_filter_fusion", bool, - "Whether to fuse map and filter transformations."), - ("experimental_map_fusion", bool, "Whether to fuse map transformations."), - ("experimental_map_parallelization", bool, - "Whether to parallelize stateless map transformations."), - ("experimental_map_vectorization", bool, - "Whether to vectorize map transformations."), - ("experimental_noop_elimination", bool, - "Whether to eliminate no-op transformations."), - ("experimental_shuffle_and_repeat_fusion", bool, - "Whether to fuse shuffle and repeat transformations."), - ("experimental_numa_aware", bool, - "Whether to use NUMA-aware operations."), - ]: - - def _make_getter(name): # pylint: disable=no-self-argument - - def getter(self): - return getattr(self, "_" + name) - - return getter - - def _make_setter(name, ty): # pylint: disable=no-self-argument - - def setter(self, value): - if not isinstance(value, ty): - raise TypeError( - "Attempting to set the option %s to incompatible value: %r when " - "it expects %r" % (name, value, ty)) - setattr(self, "_" + name, value) - - return setter - - vars()["_" + _name] = None - vars()[_name] = property( - _make_getter(_name), _make_setter(_name, _ty), None, _docstring) def __init__(self): pass - def __eq__(self, other): - if isinstance(other, self.__class__): - return self.__dict__ == other.__dict__ - else: - return False + @deprecation.deprecated( + None, "Use `for ... in dataset:` to iterate over a dataset. If using " + "`tf.estimator`, return the `Dataset` object directly from your input " + "function. As a last resort, you can use " + "`tf.compat.v1.data.make_one_shot_iterator(dataset)`.") + def make_one_shot_iterator(self): + """Creates an `Iterator` for enumerating the elements of this dataset. - def __ne__(self, other): - return not self.__eq__(other) + Note: The returned iterator will be initialized automatically. + A "one-shot" iterator does not currently support re-initialization. - def _static_optimizations(self): - """Produces the list of enabled static optimizations.""" - experimental_optimizations = [ - "filter_fusion", - "hoist_random_uniform", - "map_and_batch_fusion", - "map_and_filter_fusion", - "map_fusion", - "map_parallelization", - "map_vectorization", - "noop_elimination", - "shuffle_and_repeat_fusion", - ] - result = [] - for exp_opt in experimental_optimizations: - if getattr(self, "experimental_" + exp_opt): - result.append(exp_opt) + Returns: + An `Iterator` over the elements of this dataset. + """ + if context.executing_eagerly(): + dataset = self._apply_options() + return iterator_ops.EagerIterator(dataset) - if getattr(self, "experimental_numa_aware"): - result.append("make_numa_aware") - if getattr(self, "experimental_deterministic") is False: - result.append("make_sloppy") - experimental_stats_options = getattr(self, "experimental_stats") - if experimental_stats_options and getattr(experimental_stats_options, - "latency_all_edges"): - result.append("latency_all_edges") - return result + graph_level_seed, op_level_seed = core_random_seed.get_seed(None) - def merge(self, options): - """Merges itself with the given `tf.data.Options`. + # NOTE(mrry): We capture by value here to ensure that `_make_dataset()` is + # a 0-argument function. + @function.Defun(capture_by_value=True) + def _make_dataset(): + """Factory function for a dataset.""" + # NOTE(mrry): `Defun` does not capture the graph-level seed from the + # enclosing graph, so if a graph-level seed is present we set the local + # graph seed based on a combination of the graph- and op-level seeds. + if graph_level_seed is not None: + assert op_level_seed is not None + core_random_seed.set_random_seed( + (graph_level_seed + 87654321 * op_level_seed) % (2 ** 63 - 1)) - The given `tf.data.Options` can be merged as long as there does not exist an - attribute that is set to different values in `self` and `options`. + dataset = self._apply_options() + return dataset._as_variant_tensor() # pylint: disable=protected-access - Args: - options: a `tf.data.Options` to merge with + try: + _make_dataset.add_to_graph(ops.get_default_graph()) + except ValueError as err: + if "Cannot capture a stateful node" in str(err): + raise ValueError( + "Failed to create a one-shot iterator for a dataset. " + "`Dataset.make_one_shot_iterator()` does not support datasets that " + "capture stateful objects, such as a `Variable` or `LookupTable`. " + "In these cases, use `Dataset.make_initializable_iterator()`. " + "(Original error: %s)" % err) + else: + six.reraise(ValueError, err) - Raises: - ValueError: if the given `tf.data.Options` cannot be merged + return iterator_ops.Iterator( + gen_dataset_ops.one_shot_iterator( + dataset_factory=_make_dataset, **flat_structure(self)), + None, self.output_types, self.output_shapes, self.output_classes) - Returns: - New `tf.data.Options()` object which is the result of merging self with - the input `tf.data.Options`. - """ - result = Options() - for other in [self, options]: - for name in [ - "experimental_autotune", - "experimental_deterministic", - "experimental_filter_fusion", - "experimental_hoist_random_uniform", - "experimental_map_and_batch_fusion", - "experimental_map_and_filter_fusion", - "experimental_map_fusion", - "experimental_map_parallelization", - "experimental_map_vectorization", - "experimental_noop_elimination", - "experimental_numa_aware", - "experimental_shuffle_and_repeat_fusion", - "experimental_stats", - ]: - this = getattr(result, name) - that = getattr(other, name) - if that is not None: - if this is None: - setattr(result, name, that) - elif this != that: - raise ValueError( - "Cannot merge incompatible values of option: %s" % (name)) - return result + @deprecation.deprecated( + None, "Use `for ... in dataset:` to iterate over a dataset. If using " + "`tf.estimator`, return the `Dataset` object directly from your input " + "function. As a last resort, you can use " + "`tf.compat.v1.data.make_initializable_iterator(dataset)`.") + def make_initializable_iterator(self, shared_name=None): + """Creates an `Iterator` for enumerating the elements of this dataset. + Note: The returned iterator will be in an uninitialized state, + and you must run the `iterator.initializer` operation before using it: -class DatasetSource(Dataset): - """Abstract class representing a dataset with no inputs.""" + ```python + dataset = ... + iterator = dataset.make_initializable_iterator() + # ... + sess.run(iterator.initializer) + ``` + + Args: + shared_name: (Optional.) If non-empty, the returned iterator will be + shared under the given name across multiple sessions that share the + same devices (e.g. when using a remote server). + + Returns: + An `Iterator` over the elements of this dataset. + + Raises: + RuntimeError: If eager execution is enabled. + """ + if context.executing_eagerly(): + raise RuntimeError( + "dataset.make_initializable_iterator is not supported when eager " + "execution is enabled.") + dataset = self._apply_options() + if shared_name is None: + shared_name = "" + if compat.forward_compatible(2018, 8, 3): + iterator_resource = gen_dataset_ops.iterator_v2( + container="", shared_name=shared_name, **flat_structure(self)) + else: + iterator_resource = gen_dataset_ops.iterator( + container="", shared_name=shared_name, **flat_structure(self)) + with ops.colocate_with(iterator_resource): + initializer = gen_dataset_ops.make_iterator( + dataset._as_variant_tensor(), # pylint: disable=protected-access + iterator_resource) + return iterator_ops.Iterator(iterator_resource, initializer, + dataset.output_types, dataset.output_shapes, + dataset.output_classes) + + @staticmethod + @functools.wraps(DatasetV2.from_tensors) + def from_tensors(tensors): + return DatasetV1Adapter(DatasetV2.from_tensors(tensors)) + + @staticmethod + @functools.wraps(DatasetV2.from_tensor_slices) + def from_tensor_slices(tensors): + return DatasetV1Adapter(DatasetV2.from_tensor_slices(tensors)) + + @staticmethod + @deprecation.deprecated(None, "Use `tf.data.Dataset.from_tensor_slices()`.") + def from_sparse_tensor_slices(sparse_tensor): + """Splits each rank-N `tf.SparseTensor` in this dataset row-wise. + + Args: + sparse_tensor: A `tf.SparseTensor`. + + Returns: + Dataset: A `Dataset` of rank-(N-1) sparse tensors. + """ + return DatasetV1Adapter(SparseTensorSliceDataset(sparse_tensor)) + + @staticmethod + @functools.wraps(DatasetV2.from_generator) + def from_generator(generator, output_types, output_shapes=None, args=None): + return DatasetV1Adapter(DatasetV2.from_generator( + generator, output_types, output_shapes, args)) + + @staticmethod + @functools.wraps(DatasetV2.range) + def range(*args): + return DatasetV1Adapter(DatasetV2.range(*args)) + + @staticmethod + @functools.wraps(DatasetV2.zip) + def zip(datasets): + return DatasetV1Adapter(DatasetV2.zip(datasets)) + + @functools.wraps(DatasetV2.concatenate) + def concatenate(self, dataset): + return DatasetV1Adapter(super(DatasetV1, self).concatenate(dataset)) + + @functools.wraps(DatasetV2.prefetch) + def prefetch(self, buffer_size): + return DatasetV1Adapter(super(DatasetV1, self).prefetch(buffer_size)) + + @staticmethod + @functools.wraps(DatasetV2.list_files) + def list_files(file_pattern, shuffle=None, seed=None): + return DatasetV1Adapter(DatasetV2.list_files(file_pattern, shuffle, seed)) + + @functools.wraps(DatasetV2.repeat) + def repeat(self, count=None): + return DatasetV1Adapter(super(DatasetV1, self).repeat(count)) + + @functools.wraps(DatasetV2.shuffle) + def shuffle(self, buffer_size, seed=None, reshuffle_each_iteration=None): + return DatasetV1Adapter(super(DatasetV1, self).shuffle( + buffer_size, seed, reshuffle_each_iteration)) + + @functools.wraps(DatasetV2.cache) + def cache(self, filename=""): + return DatasetV1Adapter(super(DatasetV1, self).cache(filename)) + + @functools.wraps(DatasetV2.take) + def take(self, count): + return DatasetV1Adapter(super(DatasetV1, self).take(count)) + + @functools.wraps(DatasetV2.skip) + def skip(self, count): + return DatasetV1Adapter(super(DatasetV1, self).skip(count)) + + @deprecation.deprecated( + None, "Use `dataset.apply(tf.data.experimental.filter_for_shard(...))`.") + def shard(self, num_shards, index): + """Creates a `Dataset` that includes only 1/`num_shards` of this dataset. + + This dataset operator is very useful when running distributed training, as + it allows each worker to read a unique subset. + + When reading a single input file, you can skip elements as follows: + + ```python + d = tf.data.TFRecordDataset(FLAGS.input_file) + d = d.shard(FLAGS.num_workers, FLAGS.worker_index) + d = d.repeat(FLAGS.num_epochs) + d = d.shuffle(FLAGS.shuffle_buffer_size) + d = d.map(parser_fn, num_parallel_calls=FLAGS.num_map_threads) + ``` + + Important caveats: + + - Be sure to shard before you use any randomizing operator (such as + shuffle). + - Generally it is best if the shard operator is used early in the dataset + pipeline. For example, when reading from a set of TFRecord files, shard + before converting the dataset to input samples. This avoids reading every + file on every worker. The following is an example of an efficient + sharding strategy within a complete pipeline: + + ```python + d = Dataset.list_files(FLAGS.pattern) + d = d.shard(FLAGS.num_workers, FLAGS.worker_index) + d = d.repeat(FLAGS.num_epochs) + d = d.shuffle(FLAGS.shuffle_buffer_size) + d = d.interleave(tf.data.TFRecordDataset, + cycle_length=FLAGS.num_readers, block_length=1) + d = d.map(parser_fn, num_parallel_calls=FLAGS.num_map_threads) + ``` + + Args: + num_shards: A `tf.int64` scalar `tf.Tensor`, representing the number of + shards operating in parallel. + index: A `tf.int64` scalar `tf.Tensor`, representing the worker index. + + Returns: + Dataset: A `Dataset`. + + Raises: + ValueError: if `num_shards` or `index` are illegal values. Note: error + checking is done on a best-effort basis, and errors aren't guaranteed + to be caught upon dataset creation. (e.g. providing in a placeholder + tensor bypasses the early checking, and will instead result in an error + during a session.run call.) + """ + return self.apply(filter_for_shard_ops.filter_for_shard(num_shards, index)) + + @functools.wraps(DatasetV2.batch) + def batch(self, batch_size, drop_remainder=False): + return DatasetV1Adapter(super(DatasetV1, self).batch( + batch_size, drop_remainder)) + + @functools.wraps(DatasetV2.padded_batch) + def padded_batch(self, + batch_size, + padded_shapes, + padding_values=None, + drop_remainder=False): + return DatasetV1Adapter(super(DatasetV1, self).padded_batch( + batch_size, padded_shapes, padding_values, drop_remainder)) + + @functools.wraps(DatasetV2.map) + def map(self, map_func, num_parallel_calls=None): + return DatasetV1Adapter(super(DatasetV1, self).map( + map_func, num_parallel_calls)) + + @functools.wraps(DatasetV2.flat_map) + def flat_map(self, map_func): + return DatasetV1Adapter(super(DatasetV1, self).flat_map(map_func)) + + @functools.wraps(DatasetV2.interleave) + def interleave(self, + map_func, + cycle_length, + block_length=1, + num_parallel_calls=None): + return DatasetV1Adapter(super(DatasetV1, self).interleave( + map_func, cycle_length, block_length, num_parallel_calls)) + + @functools.wraps(DatasetV2.filter) + def filter(self, predicate): + return DatasetV1Adapter(super(DatasetV1, self).filter(predicate)) + + @functools.wraps(DatasetV2.apply) + def apply(self, transformation_func): + return DatasetV1Adapter(super(DatasetV1, self).apply(transformation_func)) + + @functools.wraps(DatasetV2.window) + def window(self, size, shift=None, stride=1, drop_remainder=False): + return DatasetV1Adapter(super(DatasetV1, self).window( + size, shift, stride, drop_remainder)) + + @functools.wraps(DatasetV2.with_options) + def with_options(self, options): + return DatasetV1Adapter(super(DatasetV1, self).with_options(options)) + + +# TODO(b/119044825): Until all `tf.data` unit tests are converted to V2, keep +# this alias in place. +Dataset = DatasetV1 + + +class DatasetV1Adapter(DatasetV1): + """Wraps a V2 `Dataset` object in the `tf.compat.v1.data.Dataset` API.""" + + def __init__(self, dataset): + super(DatasetV1Adapter, self).__init__() + self._dataset = dataset + + def _as_variant_tensor(self): + return self._dataset._as_variant_tensor() # pylint: disable=protected-access + + def _has_captured_ref(self): + return self._dataset._has_captured_ref() # pylint: disable=protected-access + + def _inputs(self): + return self._dataset._inputs() # pylint: disable=protected-access + + def options(self): + return self._dataset.options() + + @property + def output_classes(self): + return self._dataset.output_classes + + @property + def output_shapes(self): + return self._dataset.output_shapes + + @property + def output_types(self): + return self._dataset.output_types + + def __iter__(self): + return iter(self._dataset) + + +@tf_export(v1=["data.make_one_shot_iterator"]) +def make_one_shot_iterator(dataset): + """Creates a `tf.data.Iterator` for enumerating the elements of a dataset. + + Note: The returned iterator will be initialized automatically. + A "one-shot" iterator does not support re-initialization. + + Args: + dataset: A `tf.data.Dataset`. + + Returns: + A `tf.data.Iterator` over the elements of this dataset. + """ + try: + # Call the defined `make_one_shot_iterator()` if there is one, because some + # datasets (e.g. for prefetching) override its behavior. + return dataset.make_one_shot_iterator() + except AttributeError: + return DatasetV1Adapter(dataset).make_one_shot_iterator() + + +@tf_export(v1=["data.make_initializable_iterator"]) +def make_initializable_iterator(dataset): + """Creates a `tf.data.Iterator` for enumerating the elements of a dataset. + + Note: The returned iterator will be in an uninitialized state, + and you must run the `iterator.initializer` operation before using it: + + ```python + dataset = ... + iterator = dataset.make_initializable_iterator() + # ... + sess.run(iterator.initializer) + ``` + + Args: + dataset: A `tf.data.Dataset`. + + Returns: + A `tf.data.Iterator` over the elements of `dataset`. + + Raises: + RuntimeError: If eager execution is enabled. + """ + try: + # Call the defined `make_one_shot_iterator()` if there is one, because some + # datasets (e.g. for prefetching) override its behavior. + return dataset.make_initializable_iterator() + except AttributeError: + return DatasetV1Adapter(dataset).make_initializable_iterator() + + +@tf_export("data.Options") +class Options(options_lib.OptionsBase): + """Represents options for tf.data.Dataset. + + An `Options` object can be, for instance, used to control which static + optimizations to apply or whether to use performance modeling to dynamically + tune the parallelism of operations such as `tf.data.Dataset.map` or + `tf.data.Dataset.interleave`. + """ + + experimental_autotune = options_lib.create_option( + name="experimental_autotune", + ty=bool, + docstring= + "Whether to dynamically adjust the values of tunable parameters (e.g. " + "degrees of parallelism).") + + experimental_deterministic = options_lib.create_option( + name="experimental_deterministic", + ty=bool, + docstring= + "Whether to dynamically adjust the values of tunable parameters (e.g. " + "degrees of parallelism).") + + experimental_numa_aware = options_lib.create_option( + name="experimental_numa_aware", + ty=bool, + docstring="Whether to use NUMA-aware operations.") + + experimental_optimization = options_lib.create_option( + name="experimental_optimization", + ty=optimization_options.OptimizationOptions, + docstring="Associates the given optimization options with the dataset.") + + experimental_stats = options_lib.create_option( + name="experimental_stats", + ty=stats_options.StatsOptions, + docstring="Associates the given statistics options with the dataset.") + + experimental_threading = options_lib.create_option( + name="experimental_threading", + ty=threading_options.ThreadingOptions, + docstring="Associates the given threading options with the dataset.") + + def _static_optimizations(self): + """Produces the list of enabled static optimizations.""" + + result = [] + exp_optimization_options = self.experimental_optimization + if exp_optimization_options: + optimizations = [ + "filter_fusion", + "hoist_random_uniform", + "map_and_batch_fusion", + "map_and_filter_fusion", + "map_fusion", + "map_parallelization", + "map_vectorization", + "noop_elimination", + "shuffle_and_repeat_fusion", + ] + for optimization in optimizations: + if getattr(exp_optimization_options, optimization): + result.append(optimization) + if self.experimental_numa_aware: + result.append("make_numa_aware") + if self.experimental_deterministic is False: + result.append("make_sloppy") + exp_stats_options = self.experimental_stats + if exp_stats_options and exp_stats_options.latency_all_edges: + result.append("latency_all_edges") + return result + + def merge(self, options): + """Merges itself with the given `tf.data.Options`. + + The given `tf.data.Options` can be merged as long as there does not exist an + attribute that is set to different values in `self` and `options`. + + Args: + options: a `tf.data.Options` to merge with + + Raises: + ValueError: if the given `tf.data.Options` cannot be merged + + Returns: + New `tf.data.Options()` object which is the result of merging self with + the input `tf.data.Options`. + """ + return options_lib.merge_options(self, options) + + +class DatasetSource(DatasetV2): + """Abstract class representing a dataset with no inputs.""" def _inputs(self): return [] -class UnaryDataset(Dataset): +class UnaryDataset(DatasetV2): """Abstract class representing a dataset with one input.""" def __init__(self, input_dataset): @@ -1564,6 +1776,22 @@ class UnaryDataset(Dataset): return [self._input_dataset] +class UnaryUnchangedStructureDataset(UnaryDataset): + """Represents a unary dataset with the same input and output structure.""" + + @property + def output_classes(self): + return self._input_dataset.output_classes # pylint: disable=protected-access + + @property + def output_shapes(self): + return self._input_dataset.output_shapes # pylint: disable=protected-access + + @property + def output_types(self): + return self._input_dataset.output_types # pylint: disable=protected-access + + class TensorDataset(DatasetSource): """A `Dataset` with a single element, viz. a nested structure of tensors.""" @@ -1655,92 +1883,36 @@ class SparseTensorSliceDataset(DatasetSource): def __init__(self, sparse_tensor): """See `Dataset.from_sparse_tensor_slices()` for details.""" - super(SparseTensorSliceDataset, self).__init__() - if not isinstance(sparse_tensor, sparse_tensor_lib.SparseTensor): - raise TypeError("`sparse_tensor` must be a `tf.SparseTensor` object.") - self._sparse_tensor = sparse_tensor - - def _as_variant_tensor(self): - return gen_dataset_ops.sparse_tensor_slice_dataset( - self._sparse_tensor.indices, self._sparse_tensor.values, - self._sparse_tensor.dense_shape) - - @property - def output_classes(self): - return (ops.Tensor, ops.Tensor, ops.Tensor) - - @property - def output_shapes(self): - indices_shape = self._sparse_tensor.indices.get_shape() - shape_shape = self._sparse_tensor.dense_shape.get_shape() - rank = (indices_shape.dims[1] - 1).merge_with(shape_shape.dims[0] - 1) - num_values = tensor_shape.Dimension(None) - return (tensor_shape.TensorShape([num_values, rank]), - tensor_shape.TensorShape([num_values]), - tensor_shape.TensorShape([rank])) - - @property - def output_types(self): - return (dtypes.int64, self._sparse_tensor.dtype, dtypes.int64) - - -class _NestedDatasetComponent(object): - """The structure of a `Dataset` nested in a component of another `Dataset`. - - A `StructuredFunctionWrapper` around a function that returns a `Dataset` as - one of its components will have a `NestedDatasetComponent` in the - corresponding position in the `output_classes`, `output_shapes`, and - `output_types` properties. - - NOTE(mrry): This class is not currently exposed via the public API. Support - for nested datasets can be enabled on a function-by-function basis by setting - `experimental_nested_dataset_support=True` in the `StructuredFunctionWrapper` - initializer. - - TODO(b/110122868): Add this class, or something equivalent, to the public API. - We are considering revising the public API for accessing Dataset structure - (`output_classes` etc.) based on experience with nested datasets and other - custom component types. - """ - - def __init__(self, - dataset=None, - output_shapes=None, - output_types=None, - output_classes=None): - if dataset is None: - if (output_classes is None or output_shapes is None or - output_types is None): - raise ValueError( - "Either `dataset`, or all of `output_classes`, " - "`output_shapes`, and `output_types` must be specified.") - self._output_classes = output_classes - self._output_shapes = output_shapes - self._output_types = output_types - else: - if not (output_classes is None and output_shapes is None and - output_types is None): - raise ValueError( - "Either `dataset`, or all of `output_classes`, " - "`output_shapes`, and `output_types` must be specified.") - self._output_classes = dataset.output_classes - self._output_shapes = dataset.output_shapes - self._output_types = dataset.output_types + super(SparseTensorSliceDataset, self).__init__() + if not isinstance(sparse_tensor, sparse_tensor_lib.SparseTensor): + raise TypeError("`sparse_tensor` must be a `tf.SparseTensor` object.") + self._sparse_tensor = sparse_tensor + + def _as_variant_tensor(self): + return gen_dataset_ops.sparse_tensor_slice_dataset( + self._sparse_tensor.indices, self._sparse_tensor.values, + self._sparse_tensor.dense_shape) @property def output_classes(self): - return self._output_classes + return (ops.Tensor, ops.Tensor, ops.Tensor) @property def output_shapes(self): - return self._output_shapes + indices_shape = self._sparse_tensor.indices.get_shape() + shape_shape = self._sparse_tensor.dense_shape.get_shape() + rank = (indices_shape.dims[1] - 1).merge_with(shape_shape.dims[0] - 1) + num_values = tensor_shape.Dimension(None) + return (tensor_shape.TensorShape([num_values, rank]), + tensor_shape.TensorShape([num_values]), + tensor_shape.TensorShape([rank])) @property def output_types(self): - return self._output_types + return (dtypes.int64, self._sparse_tensor.dtype, dtypes.int64) -class _VariantDataset(Dataset): +class _VariantDataset(DatasetV2): """A Dataset wrapper around a `tf.variant`-typed function argument.""" def __init__(self, dataset_variant, structure): @@ -1756,15 +1928,76 @@ class _VariantDataset(Dataset): @property def output_classes(self): - return self._structure.output_classes + return self._structure._to_legacy_output_classes() # pylint: disable=protected-access @property def output_shapes(self): - return self._structure.output_shapes + return self._structure._to_legacy_output_shapes() # pylint: disable=protected-access @property def output_types(self): - return self._structure.output_types + return self._structure._to_legacy_output_types() # pylint: disable=protected-access + + +class DatasetStructure(structure_lib.Structure): + """Represents a `Dataset` of structured values.""" + + def __init__(self, element_structure): + self._element_structure = element_structure + + @property + def _flat_shapes(self): + return [tensor_shape.scalar()] + + @property + def _flat_types(self): + return [dtypes.variant] + + def is_compatible_with(self, other): + # pylint: disable=protected-access + return (isinstance(other, DatasetStructure) and + self._element_structure.is_compatible_with( + other._element_structure)) + + def _to_tensor_list(self, value): + return [value._as_variant_tensor()] # pylint: disable=protected-access + + def _from_tensor_list(self, flat_value): + if (len(flat_value) != 1 or flat_value[0].dtype != dtypes.variant or + not flat_value[0].shape.is_compatible_with(tensor_shape.scalar())): + raise ValueError( + "DatasetStructure corresponds to a single tf.variant scalar.") + return self._from_compatible_tensor_list(flat_value) + + def _from_compatible_tensor_list(self, flat_value): + # pylint: disable=protected-access + return _VariantDataset(flat_value[0], self._element_structure) + + @staticmethod + def from_value(value): + # TODO(b/110122868): We can simplify this when a `Dataset` object has a + # `Structure`-valued property. + element_structure = structure_lib.Structure._from_legacy_structure( + value.output_types, value.output_shapes, value.output_classes) + return DatasetStructure(element_structure) + + def _to_legacy_output_types(self): + return self + + def _to_legacy_output_shapes(self): + return self + + def _to_legacy_output_classes(self): + return self + + def _batch(self, batch_size): + raise NotImplementedError("Batching for `tf.data.Dataset` objects.") + + +# pylint: disable=protected-access +structure_lib.Structure._register_custom_converter(DatasetV2, + DatasetStructure.from_value) +# pylint: enable=protected-access class StructuredFunctionWrapper(object): @@ -1779,7 +2012,6 @@ class StructuredFunctionWrapper(object): input_shapes=None, input_types=None, add_to_graph=True, - experimental_nested_dataset_support=False, defun_kwargs=None): """Creates a new `StructuredFunctionWrapper` for the given function. @@ -1799,8 +2031,6 @@ class StructuredFunctionWrapper(object): argument defines the element types and structure for `func` arguments. add_to_graph: (Optional.) If `True`, the function will be added to the default graph. - experimental_nested_dataset_support: (Optional.) If `True`, the function - will support `tf.data.Dataset` objects as arguments and return values. defun_kwargs: (Optional.) A dictionary mapping string argument names to values. If supplied, will be passed to `function.Defun()` as keyword arguments. @@ -1825,6 +2055,9 @@ class StructuredFunctionWrapper(object): self._input_types = dataset.output_types self._input_classes = dataset.output_classes + self._input_structure = structure_lib.Structure._from_legacy_structure( # pylint: disable=protected-access + self._input_types, self._input_shapes, self._input_classes) + self._transformation_name = transformation_name readable_transformation_name = transformation_name.replace( ".", "_")[:-2] if len(transformation_name) > 2 else "" @@ -1832,39 +2065,18 @@ class StructuredFunctionWrapper(object): readable_transformation_name, function_utils.get_func_name(func), str(ops.uid()) - ]) - # TODO(b/110122868): Enable this support for all `tf.data` functions. - self._nested_dataset_support = experimental_nested_dataset_support - if defun_kwargs is None: defun_kwargs = {} @function.Defun( - *self._defun_args(), func_name=self._func_name, **defun_kwargs) + *self._input_structure._flat_types, func_name=self._func_name, # pylint: disable=protected-access + **defun_kwargs) def tf_data_structured_function_wrapper(*args): """Wrapper for passing nested structures to and from tf.data functions.""" - flat_args = [] - for arg, arg_class, arg_shape, arg_type in zip( - args, - nest.flatten(self._input_classes), - nest.flatten(self._input_shapes), - nest.flatten(self._input_types)): - # TODO(b/110122868): Add a registration mechanism for new component - # types. - if arg_class is sparse_tensor_lib.SparseTensor: - arg = sparse.deserialize_sparse_tensors( - arg, arg_type, arg_shape, arg_class) - arg.indices.set_shape([None, arg_shape.ndims]) - arg.dense_shape.set_shape([arg_shape.ndims]) - elif isinstance(arg_class, _NestedDatasetComponent): - assert self._nested_dataset_support - arg = _VariantDataset(arg, arg_class) - else: - arg.set_shape(arg_shape) - flat_args.append(arg) - nested_args = nest.pack_sequence_as(self._input_classes, flat_args) + # pylint: disable=protected-access + nested_args = self._input_structure._from_compatible_tensor_list(args) if not _should_unpack_args(nested_args): nested_args = (nested_args,) @@ -1882,55 +2094,14 @@ class StructuredFunctionWrapper(object): if isinstance(ret, list): ret = tuple(ret) - # Convert any `SparseTensorValue`s to `SparseTensor`s and all other - # values to tensors. - flat_ret = [] - flat_classes = [] - flat_shapes = [] - flat_types = [] - for t in nest.flatten(ret): - # TODO(b/110122868): Add a registration mechanism for new component - # types. - if sparse_tensor_lib.is_sparse(t): - t = sparse_tensor_lib.SparseTensor.from_value(t) - flat_ret.append(sparse.serialize_sparse_tensors(t)) - flat_classes.append(sparse_tensor_lib.SparseTensor) - flat_shapes.append(t.get_shape()) - flat_types.append(t.dtype) - elif isinstance(t, Dataset): - if not self._nested_dataset_support: - raise NotImplementedError( - "The %s transformation does not currently support nested " - "datasets as outputs." % self._transformation_name) - - flat_ret.append(t._as_variant_tensor()) # pylint: disable=protected-access - component = _NestedDatasetComponent(t) - flat_classes.append(component) - flat_shapes.append(component) - flat_types.append(component) - if t.options() != Options(): - warnings.warn("Encountered a nested dataset with non-default " - "options. These options will not be propagated to " - "the outer dataset.") - else: - try: - t = ops.convert_to_tensor(t) - except (ValueError, TypeError): - raise TypeError("Unsupported return value from function passed to " - "%s: %s." % (transformation_name, t)) - flat_ret.append(t) - flat_classes.append(ops.Tensor) - flat_shapes.append(t.get_shape()) - flat_types.append(t.dtype) - - ret = nest.pack_sequence_as(ret, flat_ret) - self._output_classes = nest.pack_sequence_as(ret, flat_classes) - self._output_shapes = nest.pack_sequence_as(ret, flat_shapes) - self._output_types = nest.pack_sequence_as(ret, flat_types) + try: + self._output_structure = structure_lib.Structure.from_value(ret) + except (ValueError, TypeError): + raise TypeError("Unsupported return value from function passed to " + "%s: %s." % (transformation_name, ret)) _warn_if_collections(transformation_name) - - return flat_ret + return self._output_structure._to_tensor_list(ret) self._function = tf_data_structured_function_wrapper if add_to_graph: @@ -1941,45 +2112,32 @@ class StructuredFunctionWrapper(object): # in case (e.g.) we need to rerun the function. self._function._create_definition_if_needed() # pylint: disable=protected-access - def _defun_args(self): - """Returns a flat list of `tf.DType` for the input element structure.""" - ret = [] - for input_type, input_class in zip(nest.flatten(self._input_types), - nest.flatten(self._input_classes)): - # TODO(b/110122868): Add a registration mechanism for new component types. - if input_class is sparse_tensor_lib.SparseTensor: - ret.append(dtypes.variant) - elif isinstance(input_class, _NestedDatasetComponent): - if not self._nested_dataset_support: - raise NotImplementedError( - "The %s transformation does not currently support nested " - "datasets as inputs." % self._transformation_name) - ret.append(dtypes.variant) - else: - assert isinstance(input_type, dtypes.DType) - ret.append(input_type) - return ret + @property + def output_structure(self): + return self._output_structure @property def output_classes(self): - return self._output_classes + return self._output_structure._to_legacy_output_classes() # pylint: disable=protected-access @property def output_shapes(self): - return self._output_shapes + return self._output_structure._to_legacy_output_shapes() # pylint: disable=protected-access @property def output_types(self): - return self._output_types + return self._output_structure._to_legacy_output_types() # pylint: disable=protected-access @property def function(self): return self._function -def flat_structure(dataset): +def flat_structure(dataset=None, structure=None): """Helper for setting `output_shapes` and `output_types` attrs of Dataset ops. + Either `dataset` or `structure` must be passed to this function. + Most Dataset op constructors expect `output_shapes` and `output_types` arguments that represent the flattened structure of an element. This helper function generates these attrs as a keyword argument dictionary, allowing @@ -1987,36 +2145,20 @@ def flat_structure(dataset): `**flat_structure(self)` to the op constructor. Args: - dataset: A `tf.data.Dataset`. + dataset: (Optional.) A `tf.data.Dataset`. + structure: (Optional.) A `Structure`. Returns: A dictionary of keyword arguments that can be passed to many Dataset op constructors. """ - output_classes = [] - output_shapes = [] - output_types = [] - for output_class, output_shape, output_type in zip( - nest.flatten(dataset.output_classes), nest.flatten(dataset.output_shapes), - nest.flatten(dataset.output_types)): - if isinstance(output_class, _NestedDatasetComponent): - output_classes.append(output_class.output_classes) - output_shapes.append(output_shape.output_shapes) - output_types.append(output_type.output_types) - else: - output_classes.append(output_class) - output_shapes.append(output_shape) - output_types.append(output_type) - - output_classes = nest.pack_sequence_as(dataset.output_classes, output_classes) - output_shapes = nest.pack_sequence_as(dataset.output_shapes, output_shapes) - output_types = nest.pack_sequence_as(dataset.output_types, output_types) - + # pylint: disable=protected-access + if structure is None: + structure = structure_lib.Structure._from_legacy_structure( + dataset.output_types, dataset.output_shapes, dataset.output_classes) return { - "output_shapes": - nest.flatten(sparse.as_dense_shapes(output_shapes, output_classes)), - "output_types": - nest.flatten(sparse.as_dense_types(output_types, output_classes)), + "output_shapes": structure._flat_shapes, + "output_types": structure._flat_types, } @@ -2108,14 +2250,14 @@ class _GeneratorDataset(DatasetSource): return "Dataset.from_generator()" -class ZipDataset(Dataset): +class ZipDataset(DatasetV2): """A `Dataset` that zips its inputs together.""" def __init__(self, datasets): """See `Dataset.zip()` for details.""" super(ZipDataset, self).__init__() for ds in nest.flatten(datasets): - if not isinstance(ds, Dataset): + if not isinstance(ds, DatasetV2): if isinstance(ds, list): message = ("The argument to `Dataset.zip()` must be a nested " "structure of `Dataset` objects. Nested structures do not " @@ -2155,7 +2297,7 @@ class ZipDataset(Dataset): [ds.output_types for ds in nest.flatten(self._datasets)]) -class ConcatenateDataset(Dataset): +class ConcatenateDataset(DatasetV2): """A `Dataset` that concatenates its input with given dataset.""" def __init__(self, input_dataset, dataset_to_concatenate): @@ -2210,7 +2352,7 @@ class ConcatenateDataset(Dataset): return self._output_types -class RepeatDataset(UnaryDataset): +class RepeatDataset(UnaryUnchangedStructureDataset): """A `Dataset` that repeats its input several times.""" def __init__(self, input_dataset, count): @@ -2229,18 +2371,6 @@ class RepeatDataset(UnaryDataset): count=self._count, **flat_structure(self)) - @property - def output_classes(self): - return self._input_dataset.output_classes - - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_types(self): - return self._input_dataset.output_types - class RangeDataset(DatasetSource): """A `Dataset` of a step separated range of values.""" @@ -2290,7 +2420,7 @@ class RangeDataset(DatasetSource): return dtypes.int64 -class CacheDataset(UnaryDataset): +class CacheDataset(UnaryUnchangedStructureDataset): """A `Dataset` that caches elements of its input.""" def __init__(self, input_dataset, filename): @@ -2306,20 +2436,8 @@ class CacheDataset(UnaryDataset): filename=self._filename, **flat_structure(self)) - @property - def output_classes(self): - return self._input_dataset.output_classes - - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_types(self): - return self._input_dataset.output_types - -class ShuffleDataset(UnaryDataset): +class ShuffleDataset(UnaryUnchangedStructureDataset): """A `Dataset` that randomly shuffles the elements of its input.""" def __init__(self, @@ -2367,20 +2485,8 @@ class ShuffleDataset(UnaryDataset): reshuffle_each_iteration=self._reshuffle_each_iteration, **flat_structure(self)) - @property - def output_classes(self): - return self._input_dataset.output_classes - - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_types(self): - return self._input_dataset.output_types - -class TakeDataset(UnaryDataset): +class TakeDataset(UnaryUnchangedStructureDataset): """A `Dataset` containing the first `count` elements from its input.""" def __init__(self, input_dataset, count): @@ -2395,20 +2501,8 @@ class TakeDataset(UnaryDataset): count=self._count, **flat_structure(self)) - @property - def output_classes(self): - return self._input_dataset.output_classes - - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_types(self): - return self._input_dataset.output_types - -class SkipDataset(UnaryDataset): +class SkipDataset(UnaryUnchangedStructureDataset): """A `Dataset` skipping the first `count` elements from its input.""" def __init__(self, input_dataset, count): @@ -2423,18 +2517,6 @@ class SkipDataset(UnaryDataset): count=self._count, **flat_structure(self)) - @property - def output_classes(self): - return self._input_dataset.output_classes - - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_types(self): - return self._input_dataset.output_types - class BatchDataset(UnaryDataset): """A `Dataset` that batches contiguous elements from its input.""" @@ -2448,37 +2530,37 @@ class BatchDataset(UnaryDataset): self._drop_remainder = ops.convert_to_tensor( drop_remainder, dtype=dtypes.bool, name="drop_remainder") - def _as_variant_tensor(self): - # TODO(jsimsa): Switch to using v2 only any time after 6/30/2018. - if smart_cond.smart_constant_value(self._drop_remainder) is False: - return gen_dataset_ops.batch_dataset( - self._input_dataset._as_variant_tensor(), # pylint: disable=protected-access - batch_size=self._batch_size, - **flat_structure(self)) + # pylint: disable=protected-access + input_structure = structure_lib.Structure._from_legacy_structure( + input_dataset.output_types, input_dataset.output_shapes, + input_dataset.output_classes) + constant_drop_remainder = tensor_util.constant_value(self._drop_remainder) + if constant_drop_remainder: + # NOTE(mrry): `constant_drop_remainder` may be `None` (unknown statically) + # or `False` (explicitly retaining the remainder). + self._output_structure = input_structure._batch( + tensor_util.constant_value(self._batch_size)) else: - return gen_dataset_ops.batch_dataset_v2( - self._input_dataset._as_variant_tensor(), # pylint: disable=protected-access - batch_size=self._batch_size, - drop_remainder=self._drop_remainder, - **flat_structure(self)) + self._output_structure = input_structure._batch(None) + + def _as_variant_tensor(self): + return gen_dataset_ops.batch_dataset_v2( + self._input_dataset._as_variant_tensor(), # pylint: disable=protected-access + batch_size=self._batch_size, + drop_remainder=self._drop_remainder, + **flat_structure(structure=self._output_structure)) @property def output_classes(self): - return self._input_dataset.output_classes + return self._output_structure._to_legacy_output_classes() # pylint: disable=protected-access @property def output_shapes(self): - input_shapes = self._input_dataset.output_shapes - return nest.pack_sequence_as(input_shapes, [ - tensor_shape.vector( - tensor_util.constant_value(self._batch_size) if smart_cond. - smart_constant_value(self._drop_remainder) else None).concatenate(s) - for s in nest.flatten(self._input_dataset.output_shapes) - ]) + return self._output_structure._to_legacy_output_shapes() # pylint: disable=protected-access @property def output_types(self): - return self._input_dataset.output_types + return self._output_structure._to_legacy_output_types() # pylint: disable=protected-access def _is_padded_shape_compatible_with(padded_shape, input_component_shape): @@ -2704,66 +2786,37 @@ class MapDataset(UnaryDataset): super(MapDataset, self).__init__(input_dataset) self._input_dataset = input_dataset self._use_inter_op_parallelism = use_inter_op_parallelism - - wrapped_func = StructuredFunctionWrapper( + self._map_func = StructuredFunctionWrapper( map_func, self._transformation_name(), dataset=input_dataset) - self._output_classes = wrapped_func.output_classes - self._output_shapes = wrapped_func.output_shapes - self._output_types = wrapped_func.output_types - self._map_func = wrapped_func.function def _as_variant_tensor(self): input_t = self._input_dataset._as_variant_tensor() # pylint: disable=protected-access return gen_dataset_ops.map_dataset( input_t, - self._map_func.captured_inputs, - f=self._map_func, + self._map_func.function.captured_inputs, + f=self._map_func.function, use_inter_op_parallelism=self._use_inter_op_parallelism, - **flat_structure(self)) + **flat_structure(structure=self._map_func.output_structure)) + + def _functions(self): + return [self._map_func] @property def output_classes(self): - return self._output_classes + return self._map_func.output_classes @property def output_shapes(self): - return self._output_shapes + return self._map_func.output_shapes @property def output_types(self): - return self._output_types + return self._map_func.output_types def _transformation_name(self): return "Dataset.map()" -class MatchingFilesDataset(Dataset): - """A `Dataset` that list the files according to the input patterns.""" - - def __init__(self, patterns): - super(MatchingFilesDataset, self).__init__() - self._patterns = ops.convert_to_tensor( - patterns, dtype=dtypes.string, name="patterns") - - def _as_variant_tensor(self): - return gen_dataset_ops.matching_files_dataset(self._patterns) - - def _inputs(self): - return [] - - @property - def output_classes(self): - return ops.Tensor - - @property - def output_shapes(self): - return tensor_shape.scalar() - - @property - def output_types(self): - return dtypes.string - - class ParallelMapDataset(MapDataset): """A `Dataset` that maps a function over elements in its input in parallel.""" @@ -2780,16 +2833,15 @@ class ParallelMapDataset(MapDataset): num_parallel_calls, dtype=dtypes.int32, name="num_parallel_calls") def _as_variant_tensor(self): - input_t = self._input_dataset._as_variant_tensor() # pylint: disable=protected-access # pylint: disable=protected-access + input_t = self._input_dataset._as_variant_tensor() return gen_dataset_ops.parallel_map_dataset( input_t, - self._map_func.captured_inputs, - f=self._map_func, + self._map_func.function.captured_inputs, + f=self._map_func.function, num_parallel_calls=self._num_parallel_calls, use_inter_op_parallelism=self._use_inter_op_parallelism, - **flat_structure(self)) - # pylint: enable=protected-access + **flat_structure(structure=self._map_func.output_structure)) class FlatMapDataset(UnaryDataset): @@ -2800,36 +2852,33 @@ class FlatMapDataset(UnaryDataset): super(FlatMapDataset, self).__init__(input_dataset) self._input_dataset = input_dataset - wrapped_func = StructuredFunctionWrapper( - map_func, - self._transformation_name(), - dataset=input_dataset, - experimental_nested_dataset_support=True) - if not isinstance(wrapped_func.output_classes, _NestedDatasetComponent): + self._map_func = StructuredFunctionWrapper( + map_func, self._transformation_name(), dataset=input_dataset) + if not isinstance(self._map_func.output_structure, DatasetStructure): raise TypeError("`map_func` must return a `Dataset` object.") - self._output_classes = wrapped_func.output_classes.output_classes - self._output_types = wrapped_func.output_types.output_types - self._output_shapes = wrapped_func.output_shapes.output_shapes - self._map_func = wrapped_func.function + self._output_structure = self._map_func.output_structure._element_structure # pylint: disable=protected-access + + def _functions(self): + return [self._map_func] def _as_variant_tensor(self): return gen_dataset_ops.flat_map_dataset( self._input_dataset._as_variant_tensor(), # pylint: disable=protected-access - self._map_func.captured_inputs, - f=self._map_func, - **flat_structure(self)) + self._map_func.function.captured_inputs, + f=self._map_func.function, + **flat_structure(structure=self._output_structure)) @property def output_classes(self): - return self._output_classes + return self._output_structure._to_legacy_output_classes() # pylint: disable=protected-access @property def output_shapes(self): - return self._output_shapes + return self._output_structure._to_legacy_output_shapes() # pylint: disable=protected-access @property def output_types(self): - return self._output_types + return self._output_structure._to_legacy_output_types() # pylint: disable=protected-access def _transformation_name(self): return "Dataset.flat_map()" @@ -2848,13 +2897,14 @@ class InterleaveDataset(FlatMapDataset): block_length, dtype=dtypes.int64, name="block_length") def _as_variant_tensor(self): + # pylint: disable=protected-access return gen_dataset_ops.interleave_dataset( - self._input_dataset._as_variant_tensor(), # pylint: disable=protected-access - self._map_func.captured_inputs, # pylint: disable=protected-access + self._input_dataset._as_variant_tensor(), + self._map_func.function.captured_inputs, self._cycle_length, self._block_length, - f=self._map_func, # pylint: disable=protected-access - **flat_structure(self)) + f=self._map_func.function, + **flat_structure(structure=self._output_structure)) def _transformation_name(self): return "Dataset.interleave()" @@ -2877,20 +2927,21 @@ class ParallelInterleaveDataset(FlatMapDataset): num_parallel_calls, dtype=dtypes.int64, name="num_parallel_calls") def _as_variant_tensor(self): + # pylint: disable=protected-access return gen_dataset_ops.parallel_interleave_dataset_v2( - self._input_dataset._as_variant_tensor(), # pylint: disable=protected-access - self._map_func.captured_inputs, # pylint: disable=protected-access + self._input_dataset._as_variant_tensor(), + self._map_func.function.captured_inputs, self._cycle_length, self._block_length, self._num_parallel_calls, - f=self._map_func, # pylint: disable=protected-access - **flat_structure(self)) + f=self._map_func.function, + **flat_structure(structure=self._output_structure)) def _transformation_name(self): return "Dataset.interleave()" -class FilterDataset(UnaryDataset): +class FilterDataset(UnaryUnchangedStructureDataset): """A `Dataset` that filters its input according to a predicate function.""" def __init__(self, input_dataset, predicate): @@ -2903,32 +2954,23 @@ class FilterDataset(UnaryDataset): wrapped_func.output_types == dtypes.bool and wrapped_func.output_shapes.is_compatible_with(tensor_shape.scalar())): raise ValueError("`predicate` must return a scalar boolean tensor.") - self._predicate = wrapped_func.function + self._predicate = wrapped_func + + def _functions(self): + return [self._predicate] def _as_variant_tensor(self): return gen_dataset_ops.filter_dataset( self._input_dataset._as_variant_tensor(), # pylint: disable=protected-access - other_arguments=self._predicate.captured_inputs, - predicate=self._predicate, + other_arguments=self._predicate.function.captured_inputs, + predicate=self._predicate.function, **flat_structure(self)) - @property - def output_classes(self): - return self._input_dataset.output_classes - - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_types(self): - return self._input_dataset.output_types - def _transformation_name(self): return "Dataset.filter()" -class PrefetchDataset(UnaryDataset): +class PrefetchDataset(UnaryUnchangedStructureDataset): """A `Dataset` that asynchronously prefetches its input.""" def __init__(self, input_dataset, buffer_size): @@ -2946,18 +2988,6 @@ class PrefetchDataset(UnaryDataset): buffer_size=self._buffer_size, **flat_structure(self)) - @property - def output_classes(self): - return self._input_dataset.output_classes - - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_types(self): - return self._input_dataset.output_types - class WindowDataset(UnaryDataset): """A dataset that creates window datasets from the input elements.""" @@ -2975,10 +3005,9 @@ class WindowDataset(UnaryDataset): self._output_classes = nest.pack_sequence_as( input_dataset.output_classes, [ - _NestedDatasetComponent( # pylint: disable=protected-access - output_classes=output_class, - output_shapes=output_shape, - output_types=output_type) + DatasetStructure( + structure_lib.Structure._from_legacy_structure( # pylint: disable=protected-access + output_type, output_shape, output_class)) for output_class, output_shape, output_type in zip( nest.flatten(input_dataset.output_classes), nest.flatten(input_dataset.output_shapes), @@ -3009,7 +3038,7 @@ class WindowDataset(UnaryDataset): return self._output_types -class _OptionsDataset(UnaryDataset): +class _OptionsDataset(UnaryUnchangedStructureDataset): """An identity `Dataset` that stores options.""" def __init__(self, input_dataset, options): @@ -3027,20 +3056,8 @@ class _OptionsDataset(UnaryDataset): def options(self): return self._options - @property - def output_classes(self): - return self._input_dataset.output_classes - - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_types(self): - return self._input_dataset.output_types - -class _ModelDataset(UnaryDataset): +class _ModelDataset(UnaryUnchangedStructureDataset): """A `Dataset` that acts as an identity, and models performance.""" def __init__(self, input_dataset): @@ -3053,20 +3070,8 @@ class _ModelDataset(UnaryDataset): self._input_dataset._as_variant_tensor(), # pylint: disable=protected-access **flat_structure(self)) - @property - def output_classes(self): - return self._input_dataset.output_classes - - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_types(self): - return self._input_dataset.output_types - -class _OptimizeDataset(UnaryDataset): +class _OptimizeDataset(UnaryUnchangedStructureDataset): """A `Dataset` that acts as an identity, and applies optimizations.""" def __init__(self, input_dataset, optimizations): @@ -3084,21 +3089,9 @@ class _OptimizeDataset(UnaryDataset): self._optimizations, **flat_structure(self)) - @property - def output_classes(self): - return self._input_dataset.output_classes - - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_types(self): - return self._input_dataset.output_types - -class _SetStatsAggregatorDataset(UnaryDataset): - """A `Dataset` that acts as an identity, and sets stats aggregator.""" +class _SetStatsAggregatorDataset(UnaryUnchangedStructureDataset): + """A `Dataset` that acts as an identity, and sets a stats aggregator.""" def __init__(self, input_dataset, aggregator, prefix, counter_prefix): super(_SetStatsAggregatorDataset, self).__init__(input_dataset) @@ -3108,21 +3101,43 @@ class _SetStatsAggregatorDataset(UnaryDataset): self._counter_prefix = counter_prefix def _as_variant_tensor(self): - return gen_dataset_ops.set_stats_aggregator_dataset( + return ged_ops.experimental_set_stats_aggregator_dataset( self._input_dataset._as_variant_tensor(), # pylint: disable=protected-access self._stats_aggregator._resource, # pylint: disable=protected-access self._prefix, self._counter_prefix, **flat_structure(self)) - @property - def output_shapes(self): - return self._input_dataset.output_shapes - @property - def output_types(self): - return self._input_dataset.output_types +class _MaxIntraOpParallelismDataset(UnaryUnchangedStructureDataset): + """A `Dataset` that acts as an identity, overriding intra-op parallelism.""" - @property - def output_classes(self): - return self._input_dataset.output_classes + def __init__(self, input_dataset, max_intra_op_parallelism): + super(_MaxIntraOpParallelismDataset, self).__init__(input_dataset) + self._input_dataset = input_dataset + self._max_intra_op_parallelism = ops.convert_to_tensor( + max_intra_op_parallelism, + dtype=dtypes.int64, + name="max_intra_op_parallelism") + + def _as_variant_tensor(self): + return ged_ops.experimental_max_intra_op_parallelism_dataset( + self._input_dataset._as_variant_tensor(), # pylint: disable=protected-access + self._max_intra_op_parallelism, + **flat_structure(self)) + + +class _PrivateThreadPoolDataset(UnaryUnchangedStructureDataset): + """A `Dataset` that acts as an identity, setting a private threadpool.""" + + def __init__(self, input_dataset, num_threads): + super(_PrivateThreadPoolDataset, self).__init__(input_dataset) + self._input_dataset = input_dataset + self._num_threads = ops.convert_to_tensor( + num_threads, dtype=dtypes.int64, name="num_threads") + + def _as_variant_tensor(self): + return ged_ops.experimental_private_thread_pool_dataset( + self._input_dataset._as_variant_tensor(), # pylint: disable=protected-access + self._num_threads, + **flat_structure(self)) diff --git a/tensorflow/python/data/ops/iterator_ops.py b/tensorflow/python/data/ops/iterator_ops.py index 68b03ba93b..e2ca64c802 100644 --- a/tensorflow/python/data/ops/iterator_ops.py +++ b/tensorflow/python/data/ops/iterator_ops.py @@ -68,7 +68,7 @@ def _device_stack_is_empty(): return not bool(device_stack) -@tf_export("data.Iterator") +@tf_export(v1=["data.Iterator"]) class Iterator(checkpointable.CheckpointableBase): """Represents the state of iterating through a `Dataset`.""" diff --git a/tensorflow/python/data/ops/optional_ops.py b/tensorflow/python/data/ops/optional_ops.py index 91cf883ce9..15ec755c67 100644 --- a/tensorflow/python/data/ops/optional_ops.py +++ b/tensorflow/python/data/ops/optional_ops.py @@ -183,19 +183,17 @@ class OptionalStructure(structure.Structure): return OptionalStructure(value.value_structure) def _to_legacy_output_types(self): - raise NotImplementedError("The `output_types` property is not supported on " - "structured objects containing an `Optional`. " - "Use the corresponding `structure` property.") + return self def _to_legacy_output_shapes(self): - raise NotImplementedError("The `output_shapes` property is not supported on" - " structured objects containing an `Optional`. " - "Use the corresponding `structure` property.") + return self def _to_legacy_output_classes(self): - raise NotImplementedError("The `output_classes` property is not supported " - "on structured objects containing an `Optional`. " - "Use the corresponding `structure` property.") + return self + + def _batch(self, batch_size): + raise NotImplementedError( + "Batching for `tf.data.experimental.Optional` objects.") # pylint: disable=protected-access diff --git a/tensorflow/python/data/ops/readers.py b/tensorflow/python/data/ops/readers.py index 7e165a052d..70a3b1b1cb 100644 --- a/tensorflow/python/data/ops/readers.py +++ b/tensorflow/python/data/ops/readers.py @@ -25,6 +25,7 @@ from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape from tensorflow.python.ops import array_ops from tensorflow.python.ops import gen_dataset_ops +from tensorflow.python.ops import gen_experimental_dataset_ops as ged_ops from tensorflow.python.util.tf_export import tf_export @@ -32,8 +33,8 @@ from tensorflow.python.util.tf_export import tf_export _DEFAULT_READER_BUFFER_SIZE_BYTES = 256 * 1024 # 256 KB -@tf_export("data.TextLineDataset") -class TextLineDataset(dataset_ops.DatasetSource): +@tf_export("data.TextLineDataset", v1=[]) +class TextLineDatasetV2(dataset_ops.DatasetSource): """A `Dataset` comprising lines from one or more text files.""" def __init__(self, filenames, compression_type=None, buffer_size=None): @@ -47,7 +48,7 @@ class TextLineDataset(dataset_ops.DatasetSource): to buffer. A value of 0 results in the default buffering values chosen based on the compression type. """ - super(TextLineDataset, self).__init__() + super(TextLineDatasetV2, self).__init__() self._filenames = ops.convert_to_tensor( filenames, dtype=dtypes.string, name="filenames") self._compression_type = convert.optional_param_to_tensor( @@ -75,6 +76,24 @@ class TextLineDataset(dataset_ops.DatasetSource): return dtypes.string +@tf_export(v1=["data.TextLineDataset"]) +class TextLineDatasetV1(dataset_ops.DatasetV1Adapter): + """A `Dataset` comprising lines from one or more text files.""" + + def __init__(self, filenames, compression_type=None, buffer_size=None): + wrapped = TextLineDatasetV2(filenames, compression_type, buffer_size) + super(TextLineDatasetV1, self).__init__(wrapped) + __init__.__doc__ = TextLineDatasetV2.__init__.__doc__ + + @property + def _filenames(self): + return self._dataset._filenames # pylint: disable=protected-access + + @_filenames.setter + def _filenames(self, value): + self._dataset._filenames = value # pylint: disable=protected-access + + class _TFRecordDataset(dataset_ops.DatasetSource): """A `Dataset` comprising records from one or more TFRecord files.""" @@ -140,29 +159,28 @@ class ParallelInterleaveDataset(dataset_ops.InterleaveDataset): def _as_variant_tensor(self): # pylint: disable=protected-access - return gen_dataset_ops.parallel_interleave_dataset( + return ged_ops.experimental_parallel_interleave_dataset( self._input_dataset._as_variant_tensor(), - self._map_func.captured_inputs, + self._map_func.function.captured_inputs, self._cycle_length, self._block_length, self._sloppy, self._buffer_output_elements, self._prefetch_input_elements, - f=self._map_func, - **dataset_ops.flat_structure(self)) - # pylint: enable=protected-access + f=self._map_func.function, + **dataset_ops.flat_structure(structure=self._output_structure)) def _transformation_name(self): return "tf.data.experimental.parallel_interleave()" -@tf_export("data.TFRecordDataset") -class TFRecordDataset(dataset_ops.Dataset): +@tf_export("data.TFRecordDataset", v1=[]) +class TFRecordDatasetV2(dataset_ops.DatasetV2): """A `Dataset` comprising records from one or more TFRecord files.""" def __init__(self, filenames, compression_type=None, buffer_size=None, num_parallel_reads=None): - """Creates a `TFRecordDataset` to read for one or more TFRecord files. + """Creates a `TFRecordDataset` to read one or more TFRecord files. NOTE: The `num_parallel_reads` argument can be used to improve performance when reading from a remote filesystem. @@ -182,8 +200,8 @@ class TFRecordDataset(dataset_ops.Dataset): TypeError: If any argument does not have the expected type. ValueError: If any argument does not have the expected shape. """ - super(TFRecordDataset, self).__init__() - if isinstance(filenames, dataset_ops.Dataset): + super(TFRecordDatasetV2, self).__init__() + if isinstance(filenames, dataset_ops.DatasetV2): if filenames.output_types != dtypes.string: raise TypeError( "`filenames` must be a `tf.data.Dataset` of `tf.string` elements.") @@ -194,7 +212,7 @@ class TFRecordDataset(dataset_ops.Dataset): else: filenames = ops.convert_to_tensor(filenames, dtype=dtypes.string) filenames = array_ops.reshape(filenames, [-1], name="flat_filenames") - filenames = dataset_ops.Dataset.from_tensor_slices(filenames) + filenames = dataset_ops.DatasetV2.from_tensor_slices(filenames) self._filenames = filenames self._compression_type = compression_type @@ -217,10 +235,10 @@ class TFRecordDataset(dataset_ops.Dataset): compression_type=None, buffer_size=None, num_parallel_reads=None): - return TFRecordDataset(filenames or self._filenames, - compression_type or self._compression_type, - buffer_size or self._buffer_size, - num_parallel_reads or self._num_parallel_reads) + return TFRecordDatasetV2(filenames or self._filenames, + compression_type or self._compression_type, + buffer_size or self._buffer_size, + num_parallel_reads or self._num_parallel_reads) def _as_variant_tensor(self): return self._impl._as_variant_tensor() # pylint: disable=protected-access @@ -241,8 +259,40 @@ class TFRecordDataset(dataset_ops.Dataset): return self._impl.output_types -@tf_export("data.FixedLengthRecordDataset") -class FixedLengthRecordDataset(dataset_ops.DatasetSource): +@tf_export(v1=["data.TFRecordDataset"]) +class TFRecordDatasetV1(dataset_ops.DatasetV1Adapter): + """A `Dataset` comprising records from one or more TFRecord files.""" + + def __init__(self, filenames, compression_type=None, buffer_size=None, + num_parallel_reads=None): + wrapped = TFRecordDatasetV2( + filenames, compression_type, buffer_size, num_parallel_reads) + super(TFRecordDatasetV1, self).__init__(wrapped) + __init__.__doc__ = TFRecordDatasetV2.__init__.__doc__ + + def _clone(self, + filenames=None, + compression_type=None, + buffer_size=None, + num_parallel_reads=None): + # pylint: disable=protected-access + return TFRecordDatasetV1( + filenames or self._dataset._filenames, + compression_type or self._dataset._compression_type, + buffer_size or self._dataset._buffer_size, + num_parallel_reads or self._dataset._num_parallel_reads) + + @property + def _filenames(self): + return self._dataset._filenames # pylint: disable=protected-access + + @_filenames.setter + def _filenames(self, value): + self._dataset._filenames = value # pylint: disable=protected-access + + +@tf_export("data.FixedLengthRecordDataset", v1=[]) +class FixedLengthRecordDatasetV2(dataset_ops.DatasetSource): """A `Dataset` of fixed-length records from one or more binary files.""" def __init__(self, @@ -267,7 +317,7 @@ class FixedLengthRecordDataset(dataset_ops.DatasetSource): compression_type: (Optional.) A `tf.string` scalar evaluating to one of `""` (no compression), `"ZLIB"`, or `"GZIP"`. """ - super(FixedLengthRecordDataset, self).__init__() + super(FixedLengthRecordDatasetV2, self).__init__() self._filenames = ops.convert_to_tensor( filenames, dtype=dtypes.string, name="filenames") self._record_bytes = ops.convert_to_tensor( @@ -296,7 +346,6 @@ class FixedLengthRecordDataset(dataset_ops.DatasetSource): self._filenames, self._header_bytes, self._record_bytes, self._footer_bytes, self._buffer_size) - @property def output_classes(self): return ops.Tensor @@ -308,3 +357,36 @@ class FixedLengthRecordDataset(dataset_ops.DatasetSource): @property def output_types(self): return dtypes.string + + +@tf_export(v1=["data.FixedLengthRecordDataset"]) +class FixedLengthRecordDatasetV1(dataset_ops.DatasetV1Adapter): + """A `Dataset` of fixed-length records from one or more binary files.""" + + def __init__(self, + filenames, + record_bytes, + header_bytes=None, + footer_bytes=None, + buffer_size=None, + compression_type=None): + wrapped = FixedLengthRecordDatasetV2( + filenames, record_bytes, header_bytes, footer_bytes, buffer_size, + compression_type) + super(FixedLengthRecordDatasetV1, self).__init__(wrapped) + __init__.__doc__ = FixedLengthRecordDatasetV2.__init__.__doc__ + + @property + def _filenames(self): + return self._dataset._filenames # pylint: disable=protected-access + + @_filenames.setter + def _filenames(self, value): + self._dataset._filenames = value # pylint: disable=protected-access + + +# TODO(b/119044825): Until all `tf.data` unit tests are converted to V2, keep +# these aliases in place. +FixedLengthRecordDataset = FixedLengthRecordDatasetV1 +TFRecordDataset = TFRecordDatasetV1 +TextLineDataset = TextLineDatasetV1 diff --git a/tensorflow/python/data/util/BUILD b/tensorflow/python/data/util/BUILD index 39082ce370..f15ebc32a8 100644 --- a/tensorflow/python/data/util/BUILD +++ b/tensorflow/python/data/util/BUILD @@ -97,6 +97,23 @@ py_test( ], ) +py_library( + name = "options", + srcs = ["options.py"], + srcs_version = "PY2AND3", +) + +py_test( + name = "options_test", + size = "small", + srcs = ["options_test.py"], + srcs_version = "PY2AND3", + deps = [ + ":options", + "//tensorflow/python:client_testlib", + ], +) + py_library( name = "convert", srcs = ["convert.py"], diff --git a/tensorflow/python/data/util/convert_test.py b/tensorflow/python/data/util/convert_test.py index 89c3afb296..78ca6e9513 100644 --- a/tensorflow/python/data/util/convert_test.py +++ b/tensorflow/python/data/util/convert_test.py @@ -22,6 +22,7 @@ from tensorflow.python.data.util import convert from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util from tensorflow.python.platform import test from tensorflow.python.util import compat @@ -30,47 +31,53 @@ class ConvertTest(test.TestCase): def testInteger(self): resp = convert.optional_param_to_tensor("foo", 3) - with self.cached_session() as sess: - self.assertEqual(3, sess.run(resp)) + self.assertEqual(3, self.evaluate(resp)) def testIntegerDefault(self): resp = convert.optional_param_to_tensor("foo", None) - with self.cached_session() as sess: - self.assertEqual(0, sess.run(resp)) + self.assertEqual(0, self.evaluate(resp)) def testStringDefault(self): resp = convert.optional_param_to_tensor("bar", None, "default", dtypes.string) - with self.cached_session() as sess: - self.assertEqual(compat.as_bytes("default"), sess.run(resp)) + self.assertEqual(compat.as_bytes("default"), self.evaluate(resp)) def testString(self): resp = convert.optional_param_to_tensor("bar", "value", "default", dtypes.string) - with self.cached_session() as sess: - self.assertEqual(compat.as_bytes("value"), sess.run(resp)) + self.assertEqual(compat.as_bytes("value"), self.evaluate(resp)) def testPartialShapeToTensorKnownDimension(self): - with self.cached_session() as sess: - self.assertAllEqual([1], sess.run(convert.partial_shape_to_tensor( - tensor_shape.TensorShape([1])))) - self.assertAllEqual([1], sess.run(convert.partial_shape_to_tensor((1,)))) - self.assertAllEqual([1], sess.run(convert.partial_shape_to_tensor([1]))) - self.assertAllEqual([1], sess.run(convert.partial_shape_to_tensor( - constant_op.constant([1], dtype=dtypes.int64)))) - + self.assertAllEqual([1], + self.evaluate( + convert.partial_shape_to_tensor( + tensor_shape.TensorShape([1])))) + self.assertAllEqual([1], self.evaluate( + convert.partial_shape_to_tensor((1,)))) + self.assertAllEqual([1], self.evaluate( + convert.partial_shape_to_tensor([1]))) + self.assertAllEqual([1], + self.evaluate( + convert.partial_shape_to_tensor( + constant_op.constant([1], dtype=dtypes.int64)))) + + @test_util.run_deprecated_v1 def testPartialShapeToTensorUnknownDimension(self): - with self.cached_session() as sess: - self.assertAllEqual([-1], sess.run(convert.partial_shape_to_tensor( - tensor_shape.TensorShape([None])))) - self.assertAllEqual([-1], sess.run(convert.partial_shape_to_tensor( - (None,)))) - self.assertAllEqual([-1], sess.run(convert.partial_shape_to_tensor( - [None]))) - self.assertAllEqual([-1], sess.run(convert.partial_shape_to_tensor( - [-1]))) - self.assertAllEqual([-1], sess.run(convert.partial_shape_to_tensor( - constant_op.constant([-1], dtype=dtypes.int64)))) + self.assertAllEqual([-1], + self.evaluate( + convert.partial_shape_to_tensor( + tensor_shape.TensorShape([None])))) + self.assertAllEqual([-1], + self.evaluate(convert.partial_shape_to_tensor((None,)))) + self.assertAllEqual([-1], + self.evaluate(convert.partial_shape_to_tensor([None]))) + self.assertAllEqual([-1], + self.evaluate(convert.partial_shape_to_tensor([-1]))) + self.assertAllEqual([-1], + self.evaluate( + convert.partial_shape_to_tensor( + constant_op.constant([-1], + dtype=dtypes.int64)))) with self.assertRaisesRegexp( ValueError, r"The given shape .* must be a 1-D tensor of tf.int64 " @@ -84,42 +91,63 @@ class ConvertTest(test.TestCase): convert.partial_shape_to_tensor(constant_op.constant([1., 1.])) def testPartialShapeToTensorMultipleDimensions(self): - with self.cached_session() as sess: - self.assertAllEqual([3, 6], sess.run(convert.partial_shape_to_tensor( - tensor_shape.TensorShape([3, 6])))) - self.assertAllEqual([3, 6], sess.run(convert.partial_shape_to_tensor( - (3, 6)))) - self.assertAllEqual([3, 6], sess.run(convert.partial_shape_to_tensor( - [3, 6]))) - self.assertAllEqual([3, 6], sess.run(convert.partial_shape_to_tensor( - constant_op.constant([3, 6], dtype=dtypes.int64)))) - - self.assertAllEqual([3, -1], sess.run(convert.partial_shape_to_tensor( - tensor_shape.TensorShape([3, None])))) - self.assertAllEqual([3, -1], sess.run(convert.partial_shape_to_tensor( - (3, None)))) - self.assertAllEqual([3, -1], sess.run(convert.partial_shape_to_tensor( - [3, None]))) - self.assertAllEqual([3, -1], sess.run(convert.partial_shape_to_tensor( - constant_op.constant([3, -1], dtype=dtypes.int64)))) - - self.assertAllEqual([-1, -1], sess.run(convert.partial_shape_to_tensor( - tensor_shape.TensorShape([None, None])))) - self.assertAllEqual([-1, -1], sess.run(convert.partial_shape_to_tensor( - (None, None)))) - self.assertAllEqual([-1, -1], sess.run(convert.partial_shape_to_tensor( - [None, None]))) - self.assertAllEqual([-1, -1], sess.run(convert.partial_shape_to_tensor( - constant_op.constant([-1, -1], dtype=dtypes.int64)))) + self.assertAllEqual([3, 6], + self.evaluate( + convert.partial_shape_to_tensor( + tensor_shape.TensorShape([3, 6])))) + self.assertAllEqual([3, 6], + self.evaluate(convert.partial_shape_to_tensor((3, 6)))) + self.assertAllEqual([3, 6], + self.evaluate(convert.partial_shape_to_tensor([3, 6]))) + self.assertAllEqual([3, 6], + self.evaluate( + convert.partial_shape_to_tensor( + constant_op.constant([3, 6], + dtype=dtypes.int64)))) + + self.assertAllEqual([3, -1], + self.evaluate( + convert.partial_shape_to_tensor( + tensor_shape.TensorShape([3, None])))) + self.assertAllEqual([3, -1], + self.evaluate( + convert.partial_shape_to_tensor((3, None)))) + self.assertAllEqual([3, -1], + self.evaluate( + convert.partial_shape_to_tensor([3, None]))) + self.assertAllEqual([3, -1], + self.evaluate( + convert.partial_shape_to_tensor( + constant_op.constant([3, -1], + dtype=dtypes.int64)))) + + self.assertAllEqual([-1, -1], + self.evaluate( + convert.partial_shape_to_tensor( + tensor_shape.TensorShape([None, None])))) + self.assertAllEqual([-1, -1], + self.evaluate( + convert.partial_shape_to_tensor((None, None)))) + self.assertAllEqual([-1, -1], + self.evaluate( + convert.partial_shape_to_tensor([None, None]))) + self.assertAllEqual([-1, -1], + self.evaluate( + convert.partial_shape_to_tensor( + constant_op.constant([-1, -1], + dtype=dtypes.int64)))) def testPartialShapeToTensorScalar(self): - with self.cached_session() as sess: - self.assertAllEqual([], sess.run(convert.partial_shape_to_tensor( - tensor_shape.TensorShape([])))) - self.assertAllEqual([], sess.run(convert.partial_shape_to_tensor(()))) - self.assertAllEqual([], sess.run(convert.partial_shape_to_tensor([]))) - self.assertAllEqual([], sess.run(convert.partial_shape_to_tensor( - constant_op.constant([], dtype=dtypes.int64)))) + self.assertAllEqual([], + self.evaluate( + convert.partial_shape_to_tensor( + tensor_shape.TensorShape([])))) + self.assertAllEqual([], self.evaluate(convert.partial_shape_to_tensor(()))) + self.assertAllEqual([], self.evaluate(convert.partial_shape_to_tensor([]))) + self.assertAllEqual([], + self.evaluate( + convert.partial_shape_to_tensor( + constant_op.constant([], dtype=dtypes.int64)))) if __name__ == "__main__": diff --git a/tensorflow/python/data/util/options.py b/tensorflow/python/data/util/options.py new file mode 100644 index 0000000000..9badba8e56 --- /dev/null +++ b/tensorflow/python/data/util/options.py @@ -0,0 +1,131 @@ +# Copyright 2018 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. +# ============================================================================== +"""Utilities for tf.data options.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + + +def _internal_attr_name(name): + return "_" + name + + +class OptionsBase(object): + """Base class for representing a set of tf.data options. + + Attributes: + _options: Stores the option values. + """ + + def __init__(self): + self._options = {} + + def __eq__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + for name in set(self._options) | set(other._options): # pylint: disable=protected-access + if getattr(self, name) != getattr(other, name): + return False + return True + + def __ne__(self, other): + if isinstance(other, self.__class__): + return not self.__eq__(other) + else: + return NotImplemented + + +def create_option(name, ty, docstring, default=None): + """Creates a type-checked property. + + Args: + name: the name to use + ty: the type to use + docstring: the docstring to use + default: the default value to use + + Returns: + A type-checked property. + """ + + def get_fn(self): + return self._options.get(name, default) # pylint: disable=protected-access + + def set_fn(self, value): + if not isinstance(value, ty): + raise TypeError("Property \"%s\" must be of type %s, got: %r (type: %r)" % + (name, ty, value, type(value))) + self._options[name] = value # pylint: disable=protected-access + + return property(get_fn, set_fn, None, docstring) + + +def merge_options(*options_list): + """Merges the given options, returning the result as a new options object. + + The input arguments are expected to have a matching type that derives from + `OptionsBase` (and thus each represent a set of options). The method outputs + an object of the same type created by merging the sets of options represented + by the input arguments. + + The sets of options can be merged as long as there does not exist an option + with different non-default values. + + If an option is an instance of `OptionsBase` itself, then this method is + applied recursively to the set of options represented by this option. + + Args: + *options_list: options to merge + + Raises: + TypeError: if the input arguments are incompatible or not derived from + `OptionsBase` + ValueError: if the given options cannot be merged + + Returns: + A new options object which is the result of merging the given options. + """ + if len(options_list) < 1: + raise ValueError("At least one options should be provided") + result_type = type(options_list[0]) + + for options in options_list: + if not isinstance(options, result_type): + raise TypeError("Incompatible options type: %r vs %r" % (type(options), + result_type)) + + if not isinstance(options_list[0], OptionsBase): + raise TypeError("The inputs should inherit from `OptionsBase`") + + default_options = result_type() + result = result_type() + for options in options_list: + # Iterate over all set options and merge the into the result. + for name in options._options: # pylint: disable=protected-access + this = getattr(result, name) + that = getattr(options, name) + default = getattr(default_options, name) + if that == default: + continue + elif this == default: + setattr(result, name, that) + elif isinstance(this, OptionsBase): + setattr(result, name, merge_options(this, that)) + elif this != that: + raise ValueError( + "Cannot merge incompatible values (%r and %r) of option: %s" % + (this, that, name)) + return result diff --git a/tensorflow/python/data/util/options_test.py b/tensorflow/python/data/util/options_test.py new file mode 100644 index 0000000000..c5169835a3 --- /dev/null +++ b/tensorflow/python/data/util/options_test.py @@ -0,0 +1,96 @@ +# Copyright 2018 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 dataset options utilities.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.data.util import options +from tensorflow.python.platform import test + + +class _TestOptions(options.OptionsBase): + x = options.create_option( + name="x", ty=int, docstring="the answer to everything", default=42) + y = options.create_option( + name="y", ty=float, docstring="a tasty pie", default=3.14) + + +class _NestedTestOptions(options.OptionsBase): + opts = options.create_option( + name="opts", ty=_TestOptions, docstring="nested options") + + +class OptionsTest(test.TestCase): + + def testDocumentation(self): + self.assertEqual(_TestOptions.x.__doc__, "the answer to everything") + self.assertEqual(_TestOptions.y.__doc__, "a tasty pie") + + def testCreateOption(self): + opts = _TestOptions() + self.assertEqual(opts.x, 42) + self.assertEqual(opts.y, 3.14) + self.assertIsInstance(opts.x, int) + self.assertIsInstance(opts.y, float) + opts.x = 0 + self.assertEqual(opts.x, 0) + with self.assertRaises(TypeError): + opts.x = 3.14 + opts.y = 0.0 + self.assertEqual(opts.y, 0.0) + with self.assertRaises(TypeError): + opts.y = 42 + + def testMergeOptions(self): + options1, options2 = _TestOptions(), _TestOptions() + with self.assertRaises(ValueError): + options.merge_options() + merged_options = options.merge_options(options1, options2) + self.assertEqual(merged_options.x, 42) + self.assertEqual(merged_options.y, 3.14) + options1.x = 0 + options2.y = 0.0 + merged_options = options.merge_options(options1, options2) + self.assertEqual(merged_options.x, 0) + self.assertEqual(merged_options.y, 0.0) + + def testMergeNestedOptions(self): + options1, options2 = _NestedTestOptions(), _NestedTestOptions() + merged_options = options.merge_options(options1, options2) + self.assertEqual(merged_options.opts, None) + options1.opts = _TestOptions() + merged_options = options.merge_options(options1, options2) + self.assertEqual(merged_options.opts, _TestOptions()) + options2.opts = _TestOptions() + merged_options = options.merge_options(options1, options2) + self.assertEqual(merged_options.opts, _TestOptions()) + options1.opts.x = 0 + options2.opts.y = 0.0 + merged_options = options.merge_options(options1, options2) + self.assertEqual(merged_options.opts.x, 0) + self.assertEqual(merged_options.opts.y, 0.0) + + def testMergeOptionsInvalid(self): + with self.assertRaises(TypeError): + options.merge_options(0) + options1, options2 = _TestOptions(), _NestedTestOptions() + with self.assertRaises(TypeError): + options.merge_options(options1, options2) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/data/util/sparse.py b/tensorflow/python/data/util/sparse.py index 5e6d224709..f2e22fefd3 100644 --- a/tensorflow/python/data/util/sparse.py +++ b/tensorflow/python/data/util/sparse.py @@ -34,7 +34,7 @@ def any_sparse(classes): Returns: `True` if `classes` contains a sparse tensor type and `False` otherwise. """ - return any([c is sparse_tensor.SparseTensor for c in nest.flatten(classes)]) + return any(c is sparse_tensor.SparseTensor for c in nest.flatten(classes)) def as_dense_shapes(shapes, classes): diff --git a/tensorflow/python/data/util/sparse_test.py b/tensorflow/python/data/util/sparse_test.py index 056b32480f..06acf55ab9 100644 --- a/tensorflow/python/data/util/sparse_test.py +++ b/tensorflow/python/data/util/sparse_test.py @@ -25,6 +25,7 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import sparse_tensor from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util from tensorflow.python.platform import test @@ -292,10 +293,11 @@ class SparseTest(test.TestCase): return self.assertTrue(isinstance(b, sparse_tensor.SparseTensor)) with self.cached_session(): - self.assertAllEqual(a.eval().indices, b.eval().indices) - self.assertAllEqual(a.eval().values, b.eval().values) - self.assertAllEqual(a.eval().dense_shape, b.eval().dense_shape) + self.assertAllEqual(a.eval().indices, self.evaluate(b).indices) + self.assertAllEqual(a.eval().values, self.evaluate(b).values) + self.assertAllEqual(a.eval().dense_shape, self.evaluate(b).dense_shape) + @test_util.run_deprecated_v1 def testSerializeDeserialize(self): test_cases = ( (), @@ -325,6 +327,7 @@ class SparseTest(test.TestCase): for a, e in zip(nest.flatten(actual), nest.flatten(expected)): self.assertSparseValuesEqual(a, e) + @test_util.run_deprecated_v1 def testSerializeManyDeserialize(self): test_cases = ( (), diff --git a/tensorflow/python/data/util/structure.py b/tensorflow/python/data/util/structure.py index 9a3118297d..874e8abc6d 100644 --- a/tensorflow/python/data/util/structure.py +++ b/tensorflow/python/data/util/structure.py @@ -144,6 +144,19 @@ class Structure(object): """ return self._from_tensor_list(flat_value) + @abc.abstractmethod + def _batch(self, batch_size): + """Returns a structure representing a batch of objects with this structure. + + Args: + batch_size: An `int` representing the number of elements in a batch, + or `None` if the batch size may vary. + + Returns: + A `Structure` representing a batch of objects with this structure. + """ + raise NotImplementedError("Structure._batch()") + @staticmethod def from_value(value): """Returns a `Structure` that represents the given `value`. @@ -208,14 +221,16 @@ class Structure(object): flat_ret = [] for flat_type, flat_shape, flat_class in zip(flat_types, flat_shapes, flat_classes): - if issubclass(flat_class, sparse_tensor_lib.SparseTensor): + if isinstance(flat_class, Structure): + flat_ret.append(flat_class) + elif issubclass(flat_class, sparse_tensor_lib.SparseTensor): flat_ret.append(SparseTensorStructure(flat_type, flat_shape)) elif issubclass(flat_class, ops.Tensor): flat_ret.append(TensorStructure(flat_type, flat_shape)) else: # NOTE(mrry): Since legacy structures produced by iterators only - # comprise Tensors, SparseTensors, and nests, we do not need to support - # all structure types here. + # comprise Tensors, SparseTensors, and nests, we do not need to + # support all structure types here. raise TypeError( "Could not build a structure for output class %r" % flat_type) @@ -314,15 +329,23 @@ class NestedStructure(Structure): % (len(self._flat_types), len(flat_value))) flat_ret = [] - for sub_value, structure in zip(flat_value, self._flat_nested_structure): - flat_ret.append(structure._from_tensor_list([sub_value])) + i = 0 + for structure in self._flat_nested_structure: + num_flat_values = len(structure._flat_types) + sub_value = flat_value[i:i + num_flat_values] + flat_ret.append(structure._from_tensor_list(sub_value)) + i += num_flat_values return nest.pack_sequence_as(self._nested_structure, flat_ret) def _from_compatible_tensor_list(self, flat_value): flat_ret = [] - for sub_value, structure in zip(flat_value, self._flat_nested_structure): - flat_ret.append(structure._from_compatible_tensor_list([sub_value])) + i = 0 + for structure in self._flat_nested_structure: + num_flat_values = len(structure._flat_types) + sub_value = flat_value[i:i + num_flat_values] + flat_ret.append(structure._from_compatible_tensor_list(sub_value)) + i += num_flat_values return nest.pack_sequence_as(self._nested_structure, flat_ret) @@ -345,6 +368,10 @@ class NestedStructure(Structure): return nest.map_structure( lambda s: s._to_legacy_output_classes(), self._nested_structure) + def _batch(self, batch_size): + return NestedStructure(nest.map_structure( + lambda s: s._batch(batch_size), self._nested_structure)) + class TensorStructure(Structure): """Represents structural information about a `tf.Tensor`.""" @@ -381,6 +408,13 @@ class TensorStructure(Structure): return self._from_compatible_tensor_list(flat_value) def _from_compatible_tensor_list(self, flat_value): + # TODO(b/112266545): It would be cleaner to create a new `ensure_shape()` + # op here and return that, instead of mutating the input's shape using + # `Tensor.set_shape()`. However, that would add extra ops on the arguments + # of each `tf.data` function, which could impact performance. When this + # bug is resolved, we should be able to add the `ensure_shape()` ops and + # optimize them away using contextual shape information. + flat_value[0].set_shape(self._shape) return flat_value[0] @staticmethod @@ -396,6 +430,11 @@ class TensorStructure(Structure): def _to_legacy_output_classes(self): return ops.Tensor + def _batch(self, batch_size): + return TensorStructure( + self._dtype, + tensor_shape.TensorShape([batch_size]).concatenate(self._shape)) + class SparseTensorStructure(Structure): """Represents structural information about a `tf.SparseTensor`.""" @@ -406,7 +445,11 @@ class SparseTensorStructure(Structure): @property def _flat_shapes(self): - return [tensor_shape.vector(3)] + # NOTE(mrry): The default flat shape of a boxed `SparseTensor` is `(3,)`, + # but a `SparseTensorStructure` can also represent a batch of boxed + # `SparseTensor` objects with shape `(?, 3)` (and batches of batches, etc.), + # so the flat shape must be unknown. + return [tensor_shape.unknown_shape(None)] @property def _flat_types(self): @@ -428,8 +471,11 @@ class SparseTensorStructure(Structure): return self._from_compatible_tensor_list(flat_value) def _from_compatible_tensor_list(self, flat_value): - return sparse_ops.deserialize_sparse( + ret = sparse_ops.deserialize_sparse( flat_value[0], dtype=self._dtype, rank=self._dense_shape.ndims) + ret.indices.set_shape([None, self._dense_shape.ndims]) + ret.dense_shape.set_shape([self._dense_shape.ndims]) + return ret @staticmethod def from_value(value): @@ -446,3 +492,8 @@ class SparseTensorStructure(Structure): def _to_legacy_output_classes(self): return sparse_tensor_lib.SparseTensor + + def _batch(self, batch_size): + return SparseTensorStructure( + self._dtype, + tensor_shape.TensorShape([batch_size]).concatenate(self._dense_shape)) diff --git a/tensorflow/python/data/util/structure_test.py b/tensorflow/python/data/util/structure_test.py index 630a0c912b..314a459eff 100644 --- a/tensorflow/python/data/util/structure_test.py +++ b/tensorflow/python/data/util/structure_test.py @@ -28,6 +28,7 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import sparse_tensor from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import variables from tensorflow.python.platform import test @@ -44,7 +45,7 @@ class StructureTest(test.TestCase, parameterized.TestCase): [dtypes.float32], [[]]), (lambda: sparse_tensor.SparseTensor( indices=[[3, 4]], values=[-1], dense_shape=[4, 5]), - structure.SparseTensorStructure, [dtypes.variant], [[3]]), + structure.SparseTensorStructure, [dtypes.variant], [None]), (lambda: (constant_op.constant(37.0), constant_op.constant([1, 2, 3])), structure.NestedStructure, [dtypes.float32, dtypes.int32], [[], [3]]), (lambda: { @@ -58,14 +59,17 @@ class StructureTest(test.TestCase, parameterized.TestCase): sparse_tensor.SparseTensor( indices=[[3, 4]], values=[-1], dense_shape=[4, 5])) }, structure.NestedStructure, - [dtypes.float32, dtypes.variant, dtypes.variant], [[], [3], [3]])) + [dtypes.float32, dtypes.variant, dtypes.variant], [[], None, None])) def testFlatStructure(self, value_fn, expected_structure, expected_types, expected_shapes): value = value_fn() s = structure.Structure.from_value(value) self.assertIsInstance(s, expected_structure) self.assertEqual(expected_types, s._flat_types) - self.assertEqual(expected_shapes, s._flat_shapes) + for expected, actual in zip(expected_shapes, s._flat_shapes): + self.assertTrue(actual.is_compatible_with(expected)) + self.assertTrue( + tensor_shape.as_shape(expected).is_compatible_with(actual)) @parameterized.parameters( (lambda: constant_op.constant(37.0), lambda: [ @@ -112,6 +116,7 @@ class StructureTest(test.TestCase, parameterized.TestCase): indices=[[0], [1], [2]], values=[4, 5, 6], dense_shape=[3]) }, (constant_op.constant(15.0), constant_op.constant([4, 5, 6]))]), ) + @test_util.run_deprecated_v1 def testIsCompatibleWithStructure( self, original_value_fn, compatible_values_fn, incompatible_values_fn): original_value = original_value_fn() @@ -354,5 +359,65 @@ class StructureTest(test.TestCase, parameterized.TestCase): self.assertTrue(expected_structure.is_compatible_with(actual_structure)) self.assertTrue(actual_structure.is_compatible_with(expected_structure)) + def testNestedNestedStructure(self): + # Although `Structure.from_value()` will not construct one, a nested + # structure containing nested `NestedStructure` objects can occur if a + # structure is constructed manually. + s = structure.NestedStructure( + (structure.TensorStructure(dtypes.int64, []), + structure.NestedStructure( + (structure.TensorStructure(dtypes.float32, []), + structure.TensorStructure(dtypes.string, []))))) + + int64_t = constant_op.constant(37, dtype=dtypes.int64) + float32_t = constant_op.constant(42.0) + string_t = constant_op.constant("Foo") + + nested_tensors = (int64_t, (float32_t, string_t)) + + tensor_list = s._to_tensor_list(nested_tensors) + for expected, actual in zip([int64_t, float32_t, string_t], tensor_list): + self.assertIs(expected, actual) + + (actual_int64_t, (actual_float32_t, actual_string_t)) = s._from_tensor_list( + tensor_list) + self.assertIs(int64_t, actual_int64_t) + self.assertIs(float32_t, actual_float32_t) + self.assertIs(string_t, actual_string_t) + + (actual_int64_t, (actual_float32_t, actual_string_t)) = ( + s._from_compatible_tensor_list(tensor_list)) + self.assertIs(int64_t, actual_int64_t) + self.assertIs(float32_t, actual_float32_t) + self.assertIs(string_t, actual_string_t) + + @parameterized.named_parameters( + ("Tensor", structure.TensorStructure(dtypes.float32, []), 32, + structure.TensorStructure(dtypes.float32, [32])), + ("TensorUnknown", structure.TensorStructure(dtypes.float32, []), None, + structure.TensorStructure(dtypes.float32, [None])), + ("SparseTensor", structure.SparseTensorStructure(dtypes.float32, [None]), + 32, structure.SparseTensorStructure(dtypes.float32, [32, None])), + ("SparseTensorUnknown", + structure.SparseTensorStructure(dtypes.float32, [4]), None, + structure.SparseTensorStructure(dtypes.float32, [None, 4])), + ("Nest", structure.NestedStructure({ + "a": structure.TensorStructure(dtypes.float32, []), + "b": (structure.SparseTensorStructure(dtypes.int32, [2, 2]), + structure.TensorStructure(dtypes.string, []))}), 128, + structure.NestedStructure({ + "a": structure.TensorStructure(dtypes.float32, [128]), + "b": (structure.SparseTensorStructure(dtypes.int32, [128, 2, 2]), + structure.TensorStructure(dtypes.string, [128]))})), + ) + def testBatch(self, element_structure, batch_size, + expected_batched_structure): + batched_structure = element_structure._batch(batch_size) + self.assertTrue( + batched_structure.is_compatible_with(expected_batched_structure)) + self.assertTrue( + expected_batched_structure.is_compatible_with(batched_structure)) + + if __name__ == "__main__": test.main() diff --git a/tensorflow/python/debug/BUILD b/tensorflow/python/debug/BUILD index 7995123209..c6abd476d9 100644 --- a/tensorflow/python/debug/BUILD +++ b/tensorflow/python/debug/BUILD @@ -557,6 +557,7 @@ py_test( ":source_utils", "//tensorflow/core:protos_all_py", "//tensorflow/python:client", + "//tensorflow/python:cond_v2", "//tensorflow/python:constant_op", "//tensorflow/python:control_flow_ops", "//tensorflow/python:framework_ops", @@ -566,6 +567,7 @@ py_test( "//tensorflow/python:resource_variable_ops", "//tensorflow/python:util", "//tensorflow/python:variables", + "//tensorflow/python:while_v2", "//third_party/py/numpy", ], ) diff --git a/tensorflow/python/debug/cli/analyzer_cli_test.py b/tensorflow/python/debug/cli/analyzer_cli_test.py index f197a9e4dc..322ecf9466 100644 --- a/tensorflow/python/debug/cli/analyzer_cli_test.py +++ b/tensorflow/python/debug/cli/analyzer_cli_test.py @@ -645,6 +645,7 @@ class AnalyzerCLISimpleMulAddTest(test_util.TensorFlowTestCase): self.assertEqual(len("Size (B)") + 1, dump_size_col_width) self.assertEqual(len("Op type") + 1, op_type_col_width) + @test_util.run_deprecated_v1 def testMeasureTensorListColumnWidthsGivesRightAnswerForData(self): dump = self._debug_dump.dumped_tensor_data[0] self.assertLess(dump.dump_size_bytes, 1000) @@ -660,6 +661,7 @@ class AnalyzerCLISimpleMulAddTest(test_util.TensorFlowTestCase): # column should be determined by the length of "VariableV2". self.assertEqual(len("VariableV2") + 1, op_type_col_width) + @test_util.run_deprecated_v1 def testListTensors(self): # Use shorthand alias for the command prefix. out = self._registry.dispatch_command("lt", []) @@ -673,6 +675,7 @@ class AnalyzerCLISimpleMulAddTest(test_util.TensorFlowTestCase): # Check the main menu. check_main_menu(self, out, list_tensors_enabled=False) + @test_util.run_deprecated_v1 def testListTensorsInReverseTimeOrderWorks(self): # Use shorthand alias for the command prefix. out = self._registry.dispatch_command("lt", ["-s", "timestamp", "-r"]) @@ -688,6 +691,7 @@ class AnalyzerCLISimpleMulAddTest(test_util.TensorFlowTestCase): reverse=True) check_main_menu(self, out, list_tensors_enabled=False) + @test_util.run_deprecated_v1 def testListTensorsInDumpSizeOrderWorks(self): out = self._registry.dispatch_command("lt", ["-s", "dump_size"]) assert_listed_tensors( @@ -701,6 +705,7 @@ class AnalyzerCLISimpleMulAddTest(test_util.TensorFlowTestCase): sort_by="dump_size") check_main_menu(self, out, list_tensors_enabled=False) + @test_util.run_deprecated_v1 def testListTensorsInReverseDumpSizeOrderWorks(self): out = self._registry.dispatch_command("lt", ["-s", "dump_size", "-r"]) assert_listed_tensors( @@ -720,6 +725,7 @@ class AnalyzerCLISimpleMulAddTest(test_util.TensorFlowTestCase): self.assertIn("ValueError: Unsupported key to sort tensors by: foobar", out.lines) + @test_util.run_deprecated_v1 def testListTensorsInOpTypeOrderWorks(self): # Use shorthand alias for the command prefix. out = self._registry.dispatch_command("lt", ["-s", "op_type"]) @@ -735,6 +741,7 @@ class AnalyzerCLISimpleMulAddTest(test_util.TensorFlowTestCase): reverse=False) check_main_menu(self, out, list_tensors_enabled=False) + @test_util.run_deprecated_v1 def testListTensorsInReverseOpTypeOrderWorks(self): # Use shorthand alias for the command prefix. out = self._registry.dispatch_command("lt", ["-s", "op_type", "-r"]) @@ -750,6 +757,7 @@ class AnalyzerCLISimpleMulAddTest(test_util.TensorFlowTestCase): reverse=True) check_main_menu(self, out, list_tensors_enabled=False) + @test_util.run_deprecated_v1 def testListTensorsInTensorNameOrderWorks(self): # Use shorthand alias for the command prefix. out = self._registry.dispatch_command("lt", ["-s", "tensor_name"]) @@ -765,6 +773,7 @@ class AnalyzerCLISimpleMulAddTest(test_util.TensorFlowTestCase): reverse=False) check_main_menu(self, out, list_tensors_enabled=False) + @test_util.run_deprecated_v1 def testListTensorsInReverseTensorNameOrderWorks(self): # Use shorthand alias for the command prefix. out = self._registry.dispatch_command("lt", ["-s", "tensor_name", "-r"]) @@ -780,6 +789,7 @@ class AnalyzerCLISimpleMulAddTest(test_util.TensorFlowTestCase): reverse=True) check_main_menu(self, out, list_tensors_enabled=False) + @test_util.run_deprecated_v1 def testListTensorsFilterByNodeNameRegex(self): out = self._registry.dispatch_command("list_tensors", ["--node_name_filter", ".*read.*"]) @@ -793,6 +803,7 @@ class AnalyzerCLISimpleMulAddTest(test_util.TensorFlowTestCase): assert_listed_tensors(self, out, [], [], node_name_regex="^read") check_main_menu(self, out, list_tensors_enabled=False) + @test_util.run_deprecated_v1 def testListTensorFilterByOpTypeRegex(self): out = self._registry.dispatch_command("list_tensors", ["--op_type_filter", "Identity"]) @@ -821,6 +832,7 @@ class AnalyzerCLISimpleMulAddTest(test_util.TensorFlowTestCase): op_type_regex="(Add|MatMul)") check_main_menu(self, out, list_tensors_enabled=False) + @test_util.run_deprecated_v1 def testListTensorWithFilterAndNodeNameExclusionWorks(self): # First, create and register the filter. def is_2x1_vector(datum, tensor): @@ -877,6 +889,7 @@ class AnalyzerCLISimpleMulAddTest(test_util.TensorFlowTestCase): out = self._registry.dispatch_command("list_tensors", ["--bar"]) check_syntax_error_output(self, out, "list_tensors") + @test_util.run_deprecated_v1 def testNodeInfoByNodeName(self): node_name = "simple_mul_add/matmul" out = self._registry.dispatch_command("node_info", [node_name]) @@ -901,6 +914,7 @@ class AnalyzerCLISimpleMulAddTest(test_util.TensorFlowTestCase): [(len(out.lines[0]) - len(node_name), len(out.lines[0]), "bold")], out.font_attr_segs[0]) + @test_util.run_deprecated_v1 def testNodeInfoShowAttributes(self): node_name = "simple_mul_add/matmul" out = self._registry.dispatch_command("node_info", ["-a", node_name]) @@ -924,6 +938,7 @@ class AnalyzerCLISimpleMulAddTest(test_util.TensorFlowTestCase): print_tensor_node_name=node_name, list_outputs_node_name=node_name) + @test_util.run_deprecated_v1 def testNodeInfoShowDumps(self): node_name = "simple_mul_add/matmul" out = self._registry.dispatch_command("node_info", ["-d", node_name]) @@ -948,6 +963,7 @@ class AnalyzerCLISimpleMulAddTest(test_util.TensorFlowTestCase): len(out.lines[16]) - len(out.lines[16].strip()), len(out.lines[16]), "pt %s:0 -n 0" % node_name) + @test_util.run_deprecated_v1 def testNodeInfoShowStackTraceUnavailableIsIndicated(self): self._debug_dump.set_python_graph(None) @@ -971,6 +987,7 @@ class AnalyzerCLISimpleMulAddTest(test_util.TensorFlowTestCase): print_tensor_node_name=node_name, list_outputs_node_name=node_name) + @test_util.run_deprecated_v1 def testNodeInfoShowStackTraceAvailableWorks(self): self._debug_dump.set_python_graph(self._sess.graph) @@ -994,6 +1011,7 @@ class AnalyzerCLISimpleMulAddTest(test_util.TensorFlowTestCase): print_tensor_node_name=node_name, list_outputs_node_name=node_name) + @test_util.run_deprecated_v1 def testNodeInfoByTensorName(self): node_name = "simple_mul_add/u/read" tensor_name = node_name + ":0" @@ -1363,6 +1381,7 @@ class AnalyzerCLISimpleMulAddTest(test_util.TensorFlowTestCase): break return index + @test_util.run_deprecated_v1 def testPrintSourceForOpNamesWholeFileWorks(self): self._debug_dump.set_python_graph(self._sess.graph) out = self._registry.dispatch_command( @@ -1415,6 +1434,7 @@ class AnalyzerCLISimpleMulAddTest(test_util.TensorFlowTestCase): self.assertEqual("pt simple_mul_add/add", out.font_attr_segs[index + 1][0][2].content) + @test_util.run_deprecated_v1 def testPrintSourceForTensorNamesWholeFileWorks(self): self._debug_dump.set_python_graph(self._sess.graph) out = self._registry.dispatch_command( @@ -1435,6 +1455,7 @@ class AnalyzerCLISimpleMulAddTest(test_util.TensorFlowTestCase): self.assertEqual("pt simple_mul_add/u:0", out.font_attr_segs[index + 2][0][2].content) + @test_util.run_deprecated_v1 def testPrintSourceForOpNamesStartingAtSpecifiedLineWorks(self): self._debug_dump.set_python_graph(self._sess.graph) out = self._registry.dispatch_command( @@ -1461,6 +1482,7 @@ class AnalyzerCLISimpleMulAddTest(test_util.TensorFlowTestCase): self.assertEqual("pt simple_mul_add/u/read", out.font_attr_segs[index + 3][0][2].content) + @test_util.run_deprecated_v1 def testPrintSourceForOpNameSettingMaximumElementCountWorks(self): self._debug_dump.set_python_graph(self._sess.graph) out = self._registry.dispatch_command( @@ -1505,6 +1527,7 @@ class AnalyzerCLISimpleMulAddTest(test_util.TensorFlowTestCase): self.assertTrue(cli_shared.COLOR_GRAY in attr_seg[2] or attr_seg[2] == cli_shared.COLOR_GRAY) + @test_util.run_deprecated_v1 def testListSourceWithNodeNameFilterWithMatchesWorks(self): self._debug_dump.set_python_graph(self._sess.graph) out = self._registry.dispatch_command("list_source", ["-n", ".*/read"]) @@ -1583,7 +1606,7 @@ class AnalyzerCLISimpleMulAddTest(test_util.TensorFlowTestCase): x = variables.VariableV1([1, 3, 3, 7], name="x") _, idx = array_ops.unique(x, name="x_unique") idx_times_two = math_ops.multiply(idx, 2, name="idx_times_two") - sess.run(x.initializer) + self.evaluate(x.initializer) run_options = config_pb2.RunOptions(output_partition_graphs=True) debug_utils.watch_graph( @@ -1719,6 +1742,7 @@ class AnalyzerCLIControlDepTest(test_util.TensorFlowTestCase): # Tear down temporary dump directory. shutil.rmtree(cls._dump_root) + @test_util.run_deprecated_v1 def testNodeInfoWithControlDependencies(self): # Call node_info on a node with control inputs. out = self._registry.dispatch_command("node_info", @@ -1759,6 +1783,7 @@ class AnalyzerCLIControlDepTest(test_util.TensorFlowTestCase): len(out.lines[z_line]), "ni -a -d -t control_deps/ctrl_dep_z") + @test_util.run_deprecated_v1 def testListInputsNonRecursiveNoControl(self): """List inputs non-recursively, without any control inputs.""" @@ -1801,6 +1826,7 @@ class AnalyzerCLIControlDepTest(test_util.TensorFlowTestCase): len(out.lines[3]) - len("control_deps/ctrl_dep_y"), len(out.lines[3]), "li -c -r control_deps/ctrl_dep_y") + @test_util.run_deprecated_v1 def testListInputsNonRecursiveNoControlUsingTensorName(self): """List inputs using the name of an output tensor of the node.""" @@ -1829,6 +1855,7 @@ class AnalyzerCLIControlDepTest(test_util.TensorFlowTestCase): len(out.lines[3]) - len("control_deps/ctrl_dep_y"), len(out.lines[3]), "li -c -r control_deps/ctrl_dep_y") + @test_util.run_deprecated_v1 def testListInputsNonRecursiveWithControls(self): """List inputs non-recursively, with control inputs.""" node_name = "control_deps/ctrl_dep_z" @@ -1859,6 +1886,7 @@ class AnalyzerCLIControlDepTest(test_util.TensorFlowTestCase): len(out.lines[5]) - len("control_deps/x"), len(out.lines[5]), "li -c -r control_deps/x") + @test_util.run_deprecated_v1 def testListInputsRecursiveWithControls(self): """List inputs recursively, with control inputs.""" node_name = "control_deps/ctrl_dep_z" @@ -1904,6 +1932,7 @@ class AnalyzerCLIControlDepTest(test_util.TensorFlowTestCase): len(out.lines[18]) - len("control_deps/x"), len(out.lines[18]), "li -c -r control_deps/x") + @test_util.run_deprecated_v1 def testListInputsRecursiveWithControlsWithDepthLimit(self): """List inputs recursively, with control inputs and a depth limit.""" node_name = "control_deps/ctrl_dep_z" @@ -1963,6 +1992,7 @@ class AnalyzerCLIControlDepTest(test_util.TensorFlowTestCase): "ERROR: There is no node named \"control_deps/z/foo\" in the " "partition graphs"], out.lines) + @test_util.run_deprecated_v1 def testListRecipientsRecursiveWithControlsWithDepthLimit(self): """List recipients recursively, with control inputs and a depth limit.""" @@ -2034,6 +2064,7 @@ class AnalyzerCLIWhileLoopTest(test_util.TensorFlowTestCase): # Tear down temporary dump directory. shutil.rmtree(cls._dump_root) + @test_util.run_deprecated_v1 def testMultipleDumpsPrintTensorNoNumber(self): output = self._registry.dispatch_command("pt", ["while/Identity:0"]) @@ -2051,6 +2082,7 @@ class AnalyzerCLIWhileLoopTest(test_util.TensorFlowTestCase): self.assertEqual("For example:", output.lines[-2]) self.assertEqual(" print_tensor while/Identity:0 -n 0", output.lines[-1]) + @test_util.run_deprecated_v1 def testMultipleDumpsPrintTensorWithNumber(self): for i in xrange(5): output = self._registry.dispatch_command( @@ -2064,6 +2096,7 @@ class AnalyzerCLIWhileLoopTest(test_util.TensorFlowTestCase): self.assertTrue(output.lines[4].startswith("array(%d" % i)) self.assertTrue(output.lines[4].endswith(")")) + @test_util.run_deprecated_v1 def testMultipleDumpsPrintTensorInvalidNumber(self): output = self._registry.dispatch_command("pt", ["while/Identity:0", "-n", "10"]) diff --git a/tensorflow/python/debug/cli/cli_shared_test.py b/tensorflow/python/debug/cli/cli_shared_test.py index 07b364db9f..d191a234fd 100644 --- a/tensorflow/python/debug/cli/cli_shared_test.py +++ b/tensorflow/python/debug/cli/cli_shared_test.py @@ -118,6 +118,7 @@ class GetRunStartIntroAndDescriptionTest(test_util.TensorFlowTestCase): def tearDown(self): ops.reset_default_graph() + @test_util.run_deprecated_v1 def testSingleFetchNoFeeds(self): run_start_intro = cli_shared.get_run_start_intro(12, self.const_a, None, {}) @@ -181,6 +182,7 @@ class GetRunStartIntroAndDescriptionTest(test_util.TensorFlowTestCase): run_start_intro = cli_shared.get_run_start_intro(1, self.sparse_d, None, {}) self.assertEqual(str(self.sparse_d), run_start_intro.lines[4].strip()) + @test_util.run_deprecated_v1 def testTwoFetchesListNoFeeds(self): fetches = [self.const_a, self.const_b] run_start_intro = cli_shared.get_run_start_intro(1, fetches, None, {}) @@ -197,6 +199,7 @@ class GetRunStartIntroAndDescriptionTest(test_util.TensorFlowTestCase): description = cli_shared.get_run_short_description(1, fetches, None) self.assertEqual("run #1: 2 fetches; 0 feeds", description) + @test_util.run_deprecated_v1 def testNestedListAsFetches(self): fetches = [self.const_c, [self.const_a, self.const_b]] run_start_intro = cli_shared.get_run_start_intro(1, fetches, None, {}) @@ -210,6 +213,7 @@ class GetRunStartIntroAndDescriptionTest(test_util.TensorFlowTestCase): description = cli_shared.get_run_short_description(1, fetches, None) self.assertEqual("run #1: 3 fetches; 0 feeds", description) + @test_util.run_deprecated_v1 def testNestedDictAsFetches(self): fetches = {"c": self.const_c, "ab": {"a": self.const_a, "b": self.const_b}} run_start_intro = cli_shared.get_run_start_intro(1, fetches, None, {}) @@ -227,6 +231,7 @@ class GetRunStartIntroAndDescriptionTest(test_util.TensorFlowTestCase): description = cli_shared.get_run_short_description(1, fetches, None) self.assertEqual("run #1: 3 fetches; 0 feeds", description) + @test_util.run_deprecated_v1 def testTwoFetchesAsTupleNoFeeds(self): fetches = (self.const_a, self.const_b) run_start_intro = cli_shared.get_run_start_intro(1, fetches, None, {}) @@ -243,6 +248,7 @@ class GetRunStartIntroAndDescriptionTest(test_util.TensorFlowTestCase): description = cli_shared.get_run_short_description(1, fetches, None) self.assertEqual("run #1: 2 fetches; 0 feeds", description) + @test_util.run_deprecated_v1 def testTwoFetchesAsNamedTupleNoFeeds(self): fetches_namedtuple = namedtuple("fetches", "x y") fetches = fetches_namedtuple(self.const_b, self.const_c) @@ -260,6 +266,7 @@ class GetRunStartIntroAndDescriptionTest(test_util.TensorFlowTestCase): description = cli_shared.get_run_short_description(1, fetches, None) self.assertEqual("run #1: 2 fetches; 0 feeds", description) + @test_util.run_deprecated_v1 def testWithFeedDict(self): feed_dict = { self.const_a: 10.0, @@ -283,6 +290,7 @@ class GetRunStartIntroAndDescriptionTest(test_util.TensorFlowTestCase): feed_dict) self.assertEqual("run #1: 1 fetch (c:0); 2 feeds", description) + @test_util.run_deprecated_v1 def testTensorFilters(self): feed_dict = {self.const_a: 10.0} tensor_filters = { @@ -313,11 +321,13 @@ class GetRunStartIntroAndDescriptionTest(test_util.TensorFlowTestCase): command_set.add(annot[2].content) self.assertEqual({"run -f filter_a", "run -f filter_b"}, command_set) + @test_util.run_deprecated_v1 def testGetRunShortDescriptionWorksForTensorFeedKey(self): short_description = cli_shared.get_run_short_description( 1, self.const_a, {self.const_a: 42.0}) self.assertEqual("run #1: 1 fetch (a:0); 1 feed (a:0)", short_description) + @test_util.run_deprecated_v1 def testGetRunShortDescriptionWorksForUnicodeFeedKey(self): short_description = cli_shared.get_run_short_description( 1, self.const_a, {u"foo": 42.0}) @@ -332,6 +342,7 @@ class GetErrorIntroTest(test_util.TensorFlowTestCase): def tearDown(self): ops.reset_default_graph() + @test_util.run_deprecated_v1 def testShapeError(self): tf_error = errors.OpError(None, self.var_a.initializer, "foo description", None) diff --git a/tensorflow/python/debug/cli/profile_analyzer_cli_test.py b/tensorflow/python/debug/cli/profile_analyzer_cli_test.py index 60b6047970..effcd500c7 100644 --- a/tensorflow/python/debug/cli/profile_analyzer_cli_test.py +++ b/tensorflow/python/debug/cli/profile_analyzer_cli_test.py @@ -348,6 +348,7 @@ class ProfileAnalyzerPrintSourceTest(test_util.TensorFlowTestCase): ops.reset_default_graph() super(ProfileAnalyzerPrintSourceTest, self).tearDown() + @test_util.run_deprecated_v1 def testPrintSourceForWhileLoop(self): prof_output = self.prof_analyzer.print_source([__file__]) @@ -361,6 +362,7 @@ class ProfileAnalyzerPrintSourceTest(test_util.TensorFlowTestCase): r"\[(\|)+(\s)*\] .*us .*7\(55\) .*L%d.*(\S)+" % self.loop_lineno, prof_output.lines) + @test_util.run_deprecated_v1 def testPrintSourceOutputContainsClickableLinks(self): prof_output = self.prof_analyzer.print_source([__file__]) any_match, line_index = _at_least_one_line_matches( @@ -377,6 +379,7 @@ class ProfileAnalyzerPrintSourceTest(test_util.TensorFlowTestCase): break self.assertTrue(any_menu_item_match) + @test_util.run_deprecated_v1 def testPrintSourceWithNonDefaultTimeUnit(self): prof_output = self.prof_analyzer.print_source([ __file__, "--time_unit", "ms"]) @@ -391,6 +394,7 @@ class ProfileAnalyzerPrintSourceTest(test_util.TensorFlowTestCase): r"\[(\|)+(\s)*\] .*ms .*7\(55\) .*L%d.*(\S)+" % self.loop_lineno, prof_output.lines) + @test_util.run_deprecated_v1 def testPrintSourceWithNodeNameFilter(self): prof_output = self.prof_analyzer.print_source([ __file__, "--node_name_filter", "x$"]) @@ -423,6 +427,7 @@ class ProfileAnalyzerPrintSourceTest(test_util.TensorFlowTestCase): break self.assertTrue(any_menu_item_match) + @test_util.run_deprecated_v1 def testPrintSourceWithOpTypeFilter(self): prof_output = self.prof_analyzer.print_source([ __file__, "--op_type_filter", "Less"]) diff --git a/tensorflow/python/debug/lib/common_test.py b/tensorflow/python/debug/lib/common_test.py index 5af0dafcf9..f6413f6b7b 100644 --- a/tensorflow/python/debug/lib/common_test.py +++ b/tensorflow/python/debug/lib/common_test.py @@ -27,6 +27,7 @@ from tensorflow.python.platform import googletest class CommonTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testOnFeedOneFetch(self): a = constant_op.constant(10.0, name="a") b = constant_op.constant(20.0, name="b") @@ -35,6 +36,7 @@ class CommonTest(test_util.TensorFlowTestCase): self.assertItemsEqual(["a:0"], loaded[0]) self.assertItemsEqual(["b:0"], loaded[1]) + @test_util.run_deprecated_v1 def testGetRunKeyFlat(self): a = constant_op.constant(10.0, name="a") b = constant_op.constant(20.0, name="b") @@ -43,6 +45,7 @@ class CommonTest(test_util.TensorFlowTestCase): self.assertItemsEqual(["a:0"], loaded[0]) self.assertItemsEqual(["a:0", "b:0"], loaded[1]) + @test_util.run_deprecated_v1 def testGetRunKeyNestedFetches(self): a = constant_op.constant(10.0, name="a") b = constant_op.constant(20.0, name="b") diff --git a/tensorflow/python/debug/lib/debug_gradients_test.py b/tensorflow/python/debug/lib/debug_gradients_test.py index 01867fc69d..1c53147863 100644 --- a/tensorflow/python/debug/lib/debug_gradients_test.py +++ b/tensorflow/python/debug/lib/debug_gradients_test.py @@ -54,6 +54,7 @@ class IdentifyGradientTest(test_util.TensorFlowTestCase): ops.reset_default_graph() debug_gradients.clear_gradient_debuggers() + @test_util.run_deprecated_v1 def testIdentifyGradientGivesCorrectTensorObjectWithoutContextManager(self): grad_debugger = debug_gradients.GradientsDebugger() id_grad_w = grad_debugger.identify_gradient(self.w) @@ -84,6 +85,7 @@ class IdentifyGradientTest(test_util.TensorFlowTestCase): self.assertIsInstance(w_grad, ops.Tensor) self.assertAllClose(1.0, self.sess.run(w_grad)) + @test_util.run_deprecated_v1 def testIdentifyGradientGivesCorrectTensorObjectWithTfGradients(self): grad_debugger = debug_gradients.GradientsDebugger() id_grad_w = grad_debugger.identify_gradient(self.w) @@ -115,6 +117,7 @@ class IdentifyGradientTest(test_util.TensorFlowTestCase): self.assertIsInstance(w_grad, ops.Tensor) self.assertAllClose(1.0, self.sess.run(w_grad)) + @test_util.run_deprecated_v1 def testCallingIdentifyGradientTwiceWithTheSameGradientsDebuggerErrors(self): grad_debugger = debug_gradients.GradientsDebugger() grad_debugger.identify_gradient(self.w) @@ -122,6 +125,7 @@ class IdentifyGradientTest(test_util.TensorFlowTestCase): "The graph already contains an op named .*"): grad_debugger.identify_gradient(self.w) + @test_util.run_deprecated_v1 def testIdentifyGradientWorksOnMultipleLosses(self): grad_debugger_1 = debug_gradients.GradientsDebugger() grad_debugger_2 = debug_gradients.GradientsDebugger() @@ -150,6 +154,7 @@ class IdentifyGradientTest(test_util.TensorFlowTestCase): self.assertAllClose(2.0 * 5.0, self.sess.run(dz1_dy)) self.assertAllClose(0.5 * (5.0**-0.5), self.sess.run(dz2_dy)) + @test_util.run_deprecated_v1 def testIdentifyGradientRaisesLookupErrorForUnknownXTensor(self): grad_debugger_1 = debug_gradients.GradientsDebugger() grad_debugger_2 = debug_gradients.GradientsDebugger() @@ -170,6 +175,7 @@ class IdentifyGradientTest(test_util.TensorFlowTestCase): r"This GradientsDebugger has not received any gradient tensor for "): grad_debugger_2.gradient_tensor(self.w) + @test_util.run_deprecated_v1 def testIdentifyGradientRaisesTypeErrorForNonTensorOrTensorNameInput(self): grad_debugger = debug_gradients.GradientsDebugger() with self.assertRaisesRegexp( @@ -178,6 +184,7 @@ class IdentifyGradientTest(test_util.TensorFlowTestCase): r"has type .*Operation.*"): grad_debugger.gradient_tensor(variables.global_variables_initializer()) + @test_util.run_deprecated_v1 def testIdentifyGradientTensorWorksWithGradientDescentOptimizer(self): grad_debugger = debug_gradients.GradientsDebugger() id_grad_w = grad_debugger.identify_gradient(self.w) @@ -193,6 +200,7 @@ class IdentifyGradientTest(test_util.TensorFlowTestCase): self.assertIsInstance(w_grad, ops.Tensor) self.assertAllClose(1.0, self.sess.run(w_grad)) + @test_util.run_deprecated_v1 def testWatchGradientsByXTensorNamesWorks(self): y = math_ops.add(self.w, -1.0, name="y") @@ -219,6 +227,7 @@ class IdentifyGradientTest(test_util.TensorFlowTestCase): self.assertIsInstance(w_grad, ops.Tensor) self.assertAllClose(1.0, self.sess.run(w_grad)) + @test_util.run_deprecated_v1 def testWatchGradientsByXTensorNamesWorksWithoutContextManager(self): y = math_ops.add(self.w, -1.0, name="y") @@ -245,6 +254,7 @@ class IdentifyGradientTest(test_util.TensorFlowTestCase): self.assertIsInstance(w_grad, ops.Tensor) self.assertAllClose(1.0, self.sess.run(w_grad)) + @test_util.run_deprecated_v1 def testWatchGradientsWorksOnRefTensor(self): y = math_ops.add(self.w, -1.0, name="y") @@ -263,6 +273,7 @@ class IdentifyGradientTest(test_util.TensorFlowTestCase): self.assertAllClose(3.0, self.sess.run( grad_debugger.gradient_tensor("u:0"))) + @test_util.run_deprecated_v1 def testWatchGradientsWorksOnMultipleTensors(self): y = math_ops.add(self.w, -1.0, name="y") @@ -283,6 +294,7 @@ class IdentifyGradientTest(test_util.TensorFlowTestCase): self.assertAllClose(3.0, self.sess.run( grad_debugger.gradient_tensor("u:0"))) + @test_util.run_deprecated_v1 def testWatchGradientsByXTensorsWorks(self): y = math_ops.add(self.w, -1.0, name="foo/y") z = math_ops.square(y, name="foo/z") @@ -305,6 +317,7 @@ class IdentifyGradientTest(test_util.TensorFlowTestCase): self.assertAllClose(10.0, self.sess.run(w_grad)) self.assertAllClose(30.0, self.sess.run(u_grad)) + @test_util.run_deprecated_v1 def testWatchGradientsByTensorCanWorkOnMultipleLosses(self): y = math_ops.add(self.w, -1.0, name="y") z1 = math_ops.square(y, name="z1") @@ -330,6 +343,7 @@ class IdentifyGradientTest(test_util.TensorFlowTestCase): self.assertAllClose(2.0 * 5.0, self.sess.run(dz1_dy)) self.assertAllClose(0.5 * (5.0**-0.5), self.sess.run(dz2_dy)) + @test_util.run_deprecated_v1 def testGradientsValuesFromDumpWorks(self): y = math_ops.add(self.w, -1.0, name="y") z = math_ops.square(y, name="z") diff --git a/tensorflow/python/debug/lib/debug_graph_reconstruction_test.py b/tensorflow/python/debug/lib/debug_graph_reconstruction_test.py index 1f67f8a0d4..34030c0adc 100644 --- a/tensorflow/python/debug/lib/debug_graph_reconstruction_test.py +++ b/tensorflow/python/debug/lib/debug_graph_reconstruction_test.py @@ -126,8 +126,8 @@ class ReconstructNonDebugGraphTest(test_util.TensorFlowTestCase): u = variables.Variable([12.0], name="u") v = variables.Variable([30.0], name="v") w = math_ops.add(u, v, name="w") - sess.run(u.initializer) - sess.run(v.initializer) + self.evaluate(u.initializer) + self.evaluate(v.initializer) self._compareOriginalAndReconstructedGraphDefs( sess, w, expected_output=[42.0]) @@ -139,7 +139,7 @@ class ReconstructNonDebugGraphTest(test_util.TensorFlowTestCase): b = math_ops.add(a, a, name="b") with ops.control_dependencies([a, b]): c = math_ops.multiply(b, b, name="c") - sess.run(a.initializer) + self.evaluate(a.initializer) self._compareOriginalAndReconstructedGraphDefs( sess, c, expected_output=400.0) @@ -150,8 +150,8 @@ class ReconstructNonDebugGraphTest(test_util.TensorFlowTestCase): y = variables.Variable(20.0, name="y") cond = control_flow_ops.cond( x > y, lambda: math_ops.add(x, 1), lambda: math_ops.add(y, 1)) - sess.run(x.initializer) - sess.run(y.initializer) + self.evaluate(x.initializer) + self.evaluate(y.initializer) self._compareOriginalAndReconstructedGraphDefs( sess, cond, expected_output=21.0) @@ -173,8 +173,8 @@ class ReconstructNonDebugGraphTest(test_util.TensorFlowTestCase): toy_loss = x * (u - v) train_op = gradient_descent.GradientDescentOptimizer( learning_rate=0.1).minimize(toy_loss, name="train_op") - sess.run(u.initializer) - sess.run(v.initializer) + self.evaluate(u.initializer) + self.evaluate(v.initializer) self._compareOriginalAndReconstructedGraphDefs(sess, train_op) diff --git a/tensorflow/python/debug/lib/debug_utils_test.py b/tensorflow/python/debug/lib/debug_utils_test.py index 23ab98444c..cf59b30e3d 100644 --- a/tensorflow/python/debug/lib/debug_utils_test.py +++ b/tensorflow/python/debug/lib/debug_utils_test.py @@ -185,6 +185,7 @@ class DebugUtilsTest(test_util.TensorFlowTestCase): self.assertEqual(["file:///tmp/tfdbg_1", "file:///tmp/tfdbg_2"], watch_0.debug_urls) + @test_util.run_deprecated_v1 def testWatchGraph_allNodes(self): debug_utils.watch_graph( self._run_options, @@ -216,6 +217,7 @@ class DebugUtilsTest(test_util.TensorFlowTestCase): self.assertTrue("p1" in node_names) self.assertTrue("s" in node_names) + @test_util.run_deprecated_v1 def testWatchGraph_nodeNameWhitelist(self): debug_utils.watch_graph( self._run_options, @@ -230,6 +232,7 @@ class DebugUtilsTest(test_util.TensorFlowTestCase): sorted(["a1_init", "a1", "a1/Assign", "a1/read", "p1"]), sorted(node_names)) + @test_util.run_deprecated_v1 def testWatchGraph_opTypeWhitelist(self): debug_utils.watch_graph( self._run_options, @@ -255,6 +258,7 @@ class DebugUtilsTest(test_util.TensorFlowTestCase): ["DebugIdentity"], ["file:///tmp/tfdbg_1"]) self.assertEqual(["p1"], node_names) + @test_util.run_deprecated_v1 def testWatchGraph_tensorDTypeWhitelist(self): debug_utils.watch_graph( self._run_options, @@ -267,6 +271,7 @@ class DebugUtilsTest(test_util.TensorFlowTestCase): ["DebugIdentity"], ["file:///tmp/tfdbg_1"]) self.assertItemsEqual(["a1", "a1/Assign", "b", "b/Assign"], node_names) + @test_util.run_deprecated_v1 def testWatchGraph_nodeNameAndTensorDTypeWhitelists(self): debug_utils.watch_graph( self._run_options, @@ -280,6 +285,7 @@ class DebugUtilsTest(test_util.TensorFlowTestCase): ["DebugIdentity"], ["file:///tmp/tfdbg_1"]) self.assertItemsEqual(["a1", "a1/Assign"], node_names) + @test_util.run_deprecated_v1 def testWatchGraph_nodeNameBlacklist(self): debug_utils.watch_graph_with_blacklists( self._run_options, @@ -294,6 +300,7 @@ class DebugUtilsTest(test_util.TensorFlowTestCase): sorted(["b_init", "b", "b/Assign", "b/read", "c", "s"]), sorted(node_names)) + @test_util.run_deprecated_v1 def testWatchGraph_opTypeBlacklist(self): debug_utils.watch_graph_with_blacklists( self._run_options, @@ -306,6 +313,7 @@ class DebugUtilsTest(test_util.TensorFlowTestCase): ["DebugIdentity"], ["file:///tmp/tfdbg_1"]) self.assertEqual(sorted(["p1", "s"]), sorted(node_names)) + @test_util.run_deprecated_v1 def testWatchGraph_nodeNameAndOpTypeBlacklists(self): debug_utils.watch_graph_with_blacklists( self._run_options, @@ -319,6 +327,7 @@ class DebugUtilsTest(test_util.TensorFlowTestCase): ["DebugIdentity"], ["file:///tmp/tfdbg_1"]) self.assertEqual(["s"], node_names) + @test_util.run_deprecated_v1 def testWatchGraph_tensorDTypeBlacklists(self): debug_utils.watch_graph_with_blacklists( self._run_options, @@ -335,6 +344,7 @@ class DebugUtilsTest(test_util.TensorFlowTestCase): self.assertNotIn("b/Assign", node_names) self.assertIn("s", node_names) + @test_util.run_deprecated_v1 def testWatchGraph_nodeNameAndTensorDTypeBlacklists(self): debug_utils.watch_graph_with_blacklists( self._run_options, diff --git a/tensorflow/python/debug/lib/session_debug_file_test.py b/tensorflow/python/debug/lib/session_debug_file_test.py index 1874160dd6..f5f9ba29ab 100644 --- a/tensorflow/python/debug/lib/session_debug_file_test.py +++ b/tensorflow/python/debug/lib/session_debug_file_test.py @@ -28,6 +28,7 @@ from tensorflow.python.debug.lib import debug_utils from tensorflow.python.debug.lib import session_debug_testlib from tensorflow.python.framework import constant_op from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import math_ops from tensorflow.python.ops import variables from tensorflow.python.platform import googletest @@ -44,6 +45,7 @@ class SessionDebugFileTest(session_debug_testlib.SessionDebugTestBase): else: return os.path.join(self._dump_root, "run_%d" % run_number) + @test_util.run_deprecated_v1 def testAllowsDifferentWatchesOnDifferentRuns(self): """Test watching different tensors on different runs of the same graph.""" diff --git a/tensorflow/python/debug/lib/session_debug_multi_gpu_test.py b/tensorflow/python/debug/lib/session_debug_multi_gpu_test.py index b0dc25851c..8eef45392f 100644 --- a/tensorflow/python/debug/lib/session_debug_multi_gpu_test.py +++ b/tensorflow/python/debug/lib/session_debug_multi_gpu_test.py @@ -67,7 +67,7 @@ class SessionDebugMultiGPUTest(test_util.TensorFlowTestCase): u1 = math_ops.multiply(v, v, name="u1") w = math_ops.subtract(u1, u0, name="w") - sess.run(v.initializer) + self.evaluate(v.initializer) run_options = config_pb2.RunOptions(output_partition_graphs=True) debug_utils.watch_graph(run_options, sess.graph, diff --git a/tensorflow/python/debug/lib/source_utils_test.py b/tensorflow/python/debug/lib/source_utils_test.py index 4a8d4eaa99..9083297fdb 100644 --- a/tensorflow/python/debug/lib/source_utils_test.py +++ b/tensorflow/python/debug/lib/source_utils_test.py @@ -65,6 +65,7 @@ class GuessIsTensorFlowLibraryTest(test_util.TensorFlowTestCase): self.assertTrue( source_utils.guess_is_tensorflow_py_library(source_utils.__file__)) + @test_util.run_deprecated_v1 def testFileInPythonKernelsPathReturnsTrue(self): x = constant_op.constant(42.0, name="x") self.assertTrue( @@ -109,8 +110,8 @@ class SourceHelperTest(test_util.TensorFlowTestCase): self.w = math_ops.matmul(self.u, self.v, name="w") self.w_line_number = line_number_above() - sess.run(self.u.initializer) - sess.run(self.v.initializer) + self.evaluate(self.u.initializer) + self.evaluate(self.v.initializer) run_options = config_pb2.RunOptions(output_partition_graphs=True) debug_utils.watch_graph( diff --git a/tensorflow/python/debug/wrappers/framework_test.py b/tensorflow/python/debug/wrappers/framework_test.py index 73e08ce7d5..68584b4ede 100644 --- a/tensorflow/python/debug/wrappers/framework_test.py +++ b/tensorflow/python/debug/wrappers/framework_test.py @@ -339,7 +339,7 @@ class DebugWrapperSessionTest(test_util.TensorFlowTestCase): with wrapper.as_default(): foo = constant_op.constant(42, name="foo") - self.assertEqual(42, foo.eval()) + self.assertEqual(42, self.evaluate(foo)) self.assertEqual(foo, self._observer["run_fetches"]) def testWrapperShouldSupportSessionClose(self): diff --git a/tensorflow/python/distribute/BUILD b/tensorflow/python/distribute/BUILD index 1c3d8ea67e..2d9a1764db 100644 --- a/tensorflow/python/distribute/BUILD +++ b/tensorflow/python/distribute/BUILD @@ -7,15 +7,140 @@ licenses(["notice"]) # Apache 2.0 exports_files(["LICENSE"]) load("//tensorflow:tensorflow.bzl", "py_test") +load("//tensorflow:tensorflow.bzl", "tf_py_test") +load("//tensorflow:tensorflow.bzl", "cuda_py_test") py_library( - name = "distribute", + name = "all_reduce", + srcs = [ + "all_reduce.py", + ], srcs_version = "PY2AND3", - visibility = ["//visibility:public"], deps = [ - ":distribute_config", - ":distribute_coordinator", - ":distribute_coordinator_context", + "//tensorflow/python:array_ops", + "//tensorflow/python:framework_ops", + "//tensorflow/python:math_ops", + "//tensorflow/python:nccl_ops", + ], +) + +tf_py_test( + name = "all_reduce_test", + srcs = ["all_reduce_test.py"], + additional_deps = [ + ":all_reduce", + "//third_party/py/numpy", + "//tensorflow/core:protos_all_py", + "//tensorflow/python:array_ops", + "//tensorflow/python:client", + "//tensorflow/python:framework_ops", + "//tensorflow/python:framework_test_lib", + "//tensorflow/python:math_ops", + "//tensorflow/python:constant_op", + "//tensorflow/python:client_testlib", + "//tensorflow/python:platform", + "//tensorflow/python:platform_test", + "//tensorflow/python:state_ops", + ], +) + +py_library( + name = "cross_device_ops", + srcs = ["cross_device_ops.py"], + srcs_version = "PY2AND3", + deps = [ + ":cross_device_utils", + ":device_util", + ":reduce_util", + ":values", + "//tensorflow/python:array_ops", + "//tensorflow/python:device_lib", + "//tensorflow/python:framework_ops", + "//tensorflow/python:math_ops", + "//tensorflow/python:platform", + "//tensorflow/python:resource_variable_ops", + "//tensorflow/python/eager:context", + "@six_archive//:six", + ], +) + +py_library( + name = "cross_device_utils", + srcs = ["cross_device_utils.py"], + srcs_version = "PY2AND3", + deps = [ + ":all_reduce", + ":values", + "//tensorflow/python:array_ops", + "//tensorflow/python:collective_ops", + "//tensorflow/python:device", + "//tensorflow/python:dtypes", + "//tensorflow/python:framework_ops", + "//tensorflow/python:gradients", + "//tensorflow/python:math_ops", + "//tensorflow/python:nccl_ops", + ], +) + +py_library( + name = "device_util", + srcs = ["device_util.py"], + srcs_version = "PY2AND3", + deps = [ + "//tensorflow/python:device", + "//tensorflow/python:framework_ops", + "//tensorflow/python/eager:context", + ], +) + +cuda_py_test( + name = "device_util_test", + srcs = ["device_util_test.py"], + additional_deps = [ + ":device_util", + "//tensorflow/python:client_testlib", + "//tensorflow/python:framework_ops", + ], +) + +py_library( + name = "distribute_lib", + srcs = [ + "distribute_lib.py", + "distribution_strategy_context.py", + ], + srcs_version = "PY2AND3", + deps = [ + ":device_util", + ":reduce_util", + "//tensorflow/python:array_ops", + "//tensorflow/python:constant_op", + "//tensorflow/python:control_flow_ops", + "//tensorflow/python:dtypes", + "//tensorflow/python:framework_ops", + "//tensorflow/python:platform", + "//tensorflow/python:resource_variable_ops", + "//tensorflow/python:state_ops", + "//tensorflow/python:util", + "//tensorflow/python:variable_scope", + "//tensorflow/python/data", + "//tensorflow/python/distribute/cluster_resolver:cluster_resolver_lib", + "//tensorflow/python/ops/losses", + "//tensorflow/tools/docs:doc_controls", + ], +) + +py_test( + name = "distribute_lib_test", + size = "small", + srcs = ["distribute_lib_test.py"], + srcs_version = "PY2AND3", + deps = [ + ":distribute_lib", + "//tensorflow/python:client_testlib", + "//tensorflow/python:constant_op", + "//tensorflow/python:dtypes", + "//tensorflow/python:variable_scope", ], ) @@ -45,7 +170,6 @@ py_library( py_test( name = "distribute_coordinator_test", - size = "large", srcs = ["distribute_coordinator_test.py"], srcs_version = "PY2AND3", tags = [ @@ -76,6 +200,35 @@ py_library( deps = [], ) +py_library( + name = "mirrored_strategy", + srcs = ["mirrored_strategy.py"], + deps = [ + ":cross_device_ops", + ":device_util", + ":distribute_lib", + ":multi_worker_util", + ":reduce_util", + ":shared_variable_creator", + ":values", + "//tensorflow/core:protos_all_py", + "//tensorflow/python:array_ops", + "//tensorflow/python:constant_op", + "//tensorflow/python:control_flow_ops", + "//tensorflow/python:device", + "//tensorflow/python:dtypes", + "//tensorflow/python:framework_ops", + "//tensorflow/python:pywrap_tensorflow", + "//tensorflow/python:tensor_util", + "//tensorflow/python:training", + "//tensorflow/python:util", + "//tensorflow/python:variable_scope", + "//tensorflow/python:variables", + "//tensorflow/python/eager:context", + "//tensorflow/python/eager:tape", + ], +) + py_library( name = "multi_worker_util", srcs = [ @@ -88,6 +241,35 @@ py_library( ], ) +py_library( + name = "input_ops", + srcs = ["input_ops.py"], + deps = [ + "//tensorflow/python:framework_ops", + "//tensorflow/python/data/experimental/ops:filter_for_shard_ops", + "//tensorflow/python/data/util:nest", + ], +) + +cuda_py_test( + name = "input_ops_test", + srcs = ["input_ops_test.py"], + additional_deps = [ + ":input_ops", + "//tensorflow/python/data/ops:dataset_ops", + "//tensorflow/python/data/ops:readers", + "//tensorflow/python:errors", + "//tensorflow/python:client_testlib", + "//tensorflow/python:framework_ops", + "//tensorflow/python:framework_test_lib", + "//tensorflow/python:io_ops", + "//tensorflow/python:util", + ], + tags = [ + "no_pip", + ], +) + py_test( name = "multi_worker_util_test", srcs = ["multi_worker_util_test.py"], @@ -120,3 +302,49 @@ py_library( "//tensorflow/python:training", ], ) + +py_library( + name = "reduce_util", + srcs = ["reduce_util.py"], + deps = [ + "//tensorflow/python:util", + "//tensorflow/python:variable_scope", + ], +) + +py_library( + name = "shared_variable_creator", + srcs = ["shared_variable_creator.py"], +) + +py_test( + name = "shared_variable_creator_test", + srcs = ["shared_variable_creator_test.py"], + srcs_version = "PY2AND3", + deps = [ + ":shared_variable_creator", + "//tensorflow/python:framework_test_lib", + "//tensorflow/python:variable_scope", + "//tensorflow/python/eager:test", + ], +) + +py_library( + name = "values", + srcs = ["values.py"], + deps = [ + ":device_util", + ":distribute_lib", + ":input_ops", + "//tensorflow/python:array_ops", + "//tensorflow/python:control_flow_ops", + "//tensorflow/python:framework_ops", + "//tensorflow/python:resource_variable_ops", + "//tensorflow/python:training", + "//tensorflow/python:util", + "//tensorflow/python/data/ops:multi_device_iterator_ops", + "//tensorflow/python/eager:context", + "//tensorflow/python/training/checkpointable:base", + "@six_archive//:six", + ], +) diff --git a/tensorflow/python/distribute/all_reduce.py b/tensorflow/python/distribute/all_reduce.py new file mode 100644 index 0000000000..bd7c45ae27 --- /dev/null +++ b/tensorflow/python/distribute/all_reduce.py @@ -0,0 +1,860 @@ +# 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. +# ============================================================================== +"""Utilities to construct a TF subgraph implementing distributed All-Reduce.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import collections +import math + +from tensorflow.python.framework import device as device_lib +from tensorflow.python.framework import ops +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import nccl_ops + + +def _flatten_tensors(tensors): + """Check tensors for isomorphism and flatten. + + Args: + tensors: list of T `tf.Tensor` which must all have the same shape. + + Returns: + tensors: a list of T `tf.Tensor` which are flattened (1D) views of tensors + shape: the original shape of each element of input tensors + + Raises: + ValueError: tensors are empty or non-isomorphic or have unknown shape. + """ + if not tensors: + raise ValueError("tensors cannot be empty") + shape = tensors[0].shape + for tensor in tensors: + shape = shape.merge_with(tensor.shape) + if not shape.is_fully_defined(): + raise ValueError("Tensors must have statically known shape.") + if len(shape) != 1: + reshaped = [] + for t in tensors: + with ops.colocate_with(t): + reshaped.append(array_ops.reshape(t, [-1])) + tensors = reshaped + return tensors, shape + + +def _reshape_tensors(tensors, shape): + """Reshape tensors flattened by _flatten_tensors. + + Args: + tensors: list of T `tf.Tensor` of identical length 1D tensors. + shape: list of integers describing the desired shape. Product of + the elements must equal the length of each tensor. + + Returns: + list of T `tf.Tensor` which are the reshaped inputs. + """ + reshaped = [] + for t in tensors: + with ops.colocate_with(t): + reshaped.append(array_ops.reshape(t, shape)) + return reshaped + + +def _padded_split(tensor, pieces): + """Like split for 1D tensors but pads-out case where len % pieces != 0. + + Args: + tensor: T `tf.Tensor` that must be 1D. + pieces: a positive integer specifying the number of pieces into which + tensor should be split. + + Returns: + list of T `tf.Tensor` of length pieces, which hold the values of + thin input tensor, in order. The final tensor may + be zero-padded on the end to make its size equal to those of all + of the other tensors. + + Raises: + ValueError: The input tensor is not 1D. + """ + shape = tensor.shape + if 1 != len(shape): + raise ValueError("input tensor must be 1D") + tensor_len = shape.dims[0].value + with ops.colocate_with(tensor): + if tensor_len % pieces != 0: + # pad to an even length + chunk_size = 1 + tensor_len // pieces + if pieces > tensor_len: + # This is an edge case that should not come up in practice, + # i.e. a different reduction algorithm would be better, + # but we'll make it work just for completeness. + pad_len = pieces - tensor_len + extended_whole = array_ops.concat( + [tensor, array_ops.zeros([pad_len], dtype=tensor.dtype)], 0) + parts = array_ops.split(extended_whole, pieces) + return parts, pad_len + elif (pieces - 1) * chunk_size >= tensor_len: + # Another edge case of limited real interest. + pad_len = (pieces * chunk_size) % tensor_len + extended_whole = array_ops.concat( + [tensor, array_ops.zeros([pad_len], dtype=tensor.dtype)], 0) + parts = array_ops.split(extended_whole, pieces) + return parts, pad_len + else: + last_chunk_size = tensor_len - (pieces - 1) * chunk_size + pad_len = chunk_size - last_chunk_size + piece_lens = [chunk_size for _ in range(pieces - 1)] + [last_chunk_size] + parts = array_ops.split(tensor, piece_lens) + parts[-1] = array_ops.concat( + [parts[-1], array_ops.zeros([pad_len], dtype=tensor.dtype)], 0) + return parts, pad_len + else: + return array_ops.split(tensor, pieces), 0 + + +def _strip_padding(tensors, pad_len): + """Strip the suffix padding added by _padded_split. + + Args: + tensors: list of T `tf.Tensor` of identical length 1D tensors. + pad_len: number of elements to be stripped from the end of each tensor. + + Returns: + list of T `tf.Tensor` which are the stripped inputs. + + Raises: + ValueError: tensors must be a non-empty list of 1D tensors, and + each must be longer than pad_len. + """ + if not tensors: + raise ValueError("tensors cannot be empty") + shape = tensors[0].shape + if len(shape) > 1: + raise ValueError("tensors must be 1D") + prefix_len = int(shape[0] - pad_len) + if prefix_len < 0: + raise ValueError("pad_len longer than tensor") + stripped = [] + for t in tensors: + with ops.colocate_with(t): + stripped.append(array_ops.slice(t, [0], [prefix_len])) + return stripped + + +def _ragged_split(tensor, pieces): + """Like split for 1D tensors but allows case where len % pieces != 0. + + Args: + tensor: T `tf.Tensor` that must be 1D. + pieces: a positive integer specifying the number of pieces into which + tensor should be split. + + Returns: + list of T `tf.Tensor` of length pieces, which hold the values of + the input tensor, in order. The final tensor may be shorter + than the others, which will all be of equal length. + + Raises: + ValueError: input tensor must be 1D. + """ + shape = tensor.shape + if 1 != len(shape): + raise ValueError("input tensor must be 1D") + tensor_len = shape.dims[0].value + chunk_size = tensor_len // pieces + with ops.colocate_with(tensor): + if tensor_len != (pieces * chunk_size): + # last piece will be short + assert pieces > 1 + last_chunk_size = tensor_len - ((pieces - 1) * chunk_size) + assert last_chunk_size > 0 + piece_lens = [chunk_size for _ in range(pieces - 1)] + [last_chunk_size] + return array_ops.split(tensor, piece_lens) + else: + return array_ops.split(tensor, pieces) + + +def _ring_permutations(num_workers, num_subchunks, gpu_perm): + """"Generate an array of device index arrays, one for each subchunk. + + In the basic ring reduction algorithm there are size(T)/num_devices + data chunks and each device process one chunk per tick, i.e. sending + one chunk and receiving one chunk. The idea of subchunking is that + each device processes num_subchunks smaller data regions per tick, + and the ring rank permutation is different for each subchunk index + so that a device is potentially sending to and receiving from + num_subchunks different other devices at each tick. Where multiple + independent data channels exist between devices, this strategy + supplies a method of using them in parallel. + + Args: + num_workers: number of worker tasks + num_subchunks: number of subchunks into which to divide each per-GPU chunk. + gpu_perm: an array of integers in [0, num_gpus-1] giving the default + ring order of GPUs at each worker. Other permutations will be generated + by rotating this array and splicing together per-worker instances. + + Raises: + ValueError: the number of subchunks may not exceed the number of GPUs. + + Returns: + pred_by_s_d: list of lists that maps (by index) from (subchunk, dev) to + preceding device in the permutation for that subchunk. The + device index of GPU i at worker j is i + (j * num_gpus). + rank_by_s_d: list of lists that maps (by index) from (subchunk, dev) to + local rank of device d in the permutation for that subchunk. + """ + num_gpus = len(gpu_perm) + devices = num_workers * num_gpus + if devices == 0: + return [], [] + if num_subchunks > num_gpus: + raise ValueError( + "num_subchunks %d must be <= num_gpus %d" % (num_subchunks, num_gpus)) + rotation_interval = max(1, int(num_gpus / num_subchunks)) + perms_by_s = [] + for s in range(0, num_subchunks): + full_order = [] + offset = s * rotation_interval + for w in range(0, num_workers): + default_order = [(w * num_gpus) + i for i in gpu_perm] + dev_order = default_order[offset:] + default_order[:offset] + full_order += dev_order + perms_by_s.append(full_order) + pred_by_s_d = [[-1 for d in range(0, devices)] + for s in range(0, num_subchunks)] + rank_by_s_d = [[-1 for d in range(0, devices)] + for s in range(0, num_subchunks)] + for s in range(0, num_subchunks): + for d in range(0, devices): + for t in range(0, devices): + if d == perms_by_s[s][t]: + rank_by_s_d[s][d] = t + pred_by_s_d[s][d] = perms_by_s[s][(t + devices - 1) % devices] + break + return (pred_by_s_d, rank_by_s_d) + + +def build_ring_all_reduce(input_tensors, num_workers, num_subchunks, + gpu_perm, red_op, un_op=None): + """Construct a subgraph performing a ring-style all-reduce of input_tensors. + + Args: + input_tensors: a list of T `tf.Tensor` objects, which must all + have the same shape and type. + num_workers: number of worker tasks spanned by input_tensors. + num_subchunks: number of subchunks each device should process in one tick. + gpu_perm: a list of ints giving a ring-wise rank ordering of GPUs at + each worker. All workers must have the same number of + GPUs with the same rank ordering. If NVLINK is available, this should + be a ring order supported by NVLINK edges. + red_op: a binary operator for elementwise reduction. + un_op: an optional unary operator to apply to fully reduced values. + + Raises: + ValueError: empty input_tensors or they don't all have same + size. + + Returns: + a list of T `tf.Tensor` identical sum-reductions of input_tensors. + """ + if len(input_tensors) < 2: + raise ValueError("input_tensors must be length 2 or longer") + input_tensors, shape = _flatten_tensors(input_tensors) + devices = [t.device for t in input_tensors] + (pred_by_s_d, rank_by_s_d) = _ring_permutations( + num_workers, num_subchunks, gpu_perm) + chunks_by_dev, pad_len = _build_ring_gather( + input_tensors, devices, + num_subchunks, pred_by_s_d, rank_by_s_d, red_op) + if un_op: + chunks_by_dev = _apply_unary_to_chunks(un_op, chunks_by_dev) + output_tensors = _build_ring_scatter(pred_by_s_d, rank_by_s_d, + chunks_by_dev) + if pad_len > 0: + output_tensors = _strip_padding(output_tensors, pad_len) + if len(shape) != 1: + output_tensors = _reshape_tensors(output_tensors, shape) + return output_tensors + + +def _build_ring_gather(input_tensors, devices, num_subchunks, + pred_by_s_d, rank_by_s_d, red_op): + """Construct a subgraph for the first (reduction) pass of ring all-reduce. + + Args: + input_tensors: a list of T `tf.Tensor` 1D input tensors of same + shape and type. + devices: array of device name strings + num_subchunks: number of subchunks each device should process in one tick. + pred_by_s_d: as produced by _ring_permutations + rank_by_s_d: as produced by _ring_permutations + red_op: a binary operator for elementwise reduction + + Raises: + ValueError: tensors must all be one dimensional. + + Returns: + list of list of T `tf.Tensor` of (partially) reduced values where + exactly num_subchunks chunks at each device are fully reduced. + """ + num_devices = len(input_tensors) + if num_devices == 0: + return [] + if num_devices == 1: + return input_tensors + shape = input_tensors[0].shape + if 1 != len(shape): + raise ValueError("input tensors must be 1D") + num_chunks = num_devices * num_subchunks + num_ticks = num_devices - 1 + # Initialize chunks_by_dev with splits of the input tensors. + chunks_by_dev = [] + split_pad_len = 0 + for d in range(0, num_devices): + with ops.device(devices[d]): + splits, split_pad_len = _padded_split(input_tensors[d], num_chunks) + chunks_by_dev.append(splits) + # Reduction phase + for tick in range(0, num_ticks): + # One new partial reduction for every chunk + new_partial_reductions = [None for _ in range(0, num_chunks)] + # Compute reductions with respect to last tick's values + for d in range(0, num_devices): + with ops.device(devices[d]): + for s in range(0, num_subchunks): + rank = rank_by_s_d[s][d] + seg_index = (rank + num_devices - (2 + tick)) % num_devices + pred_dev = pred_by_s_d[s][d] + chunk_index = (seg_index * num_subchunks) + s + new_partial_reductions[chunk_index] = red_op( + chunks_by_dev[pred_dev][chunk_index], + chunks_by_dev[d][chunk_index]) + # Update chunks_by_dev with the new values at the end of the tick. + for d in range(0, num_devices): + for s in range(0, num_subchunks): + rank = rank_by_s_d[s][d] + seg_index = (rank + num_devices - (2 + tick)) % num_devices + chunk_index = (seg_index * num_subchunks) + s + chunks_by_dev[d][chunk_index] = new_partial_reductions[chunk_index] + return chunks_by_dev, split_pad_len + + +def _apply_unary_to_chunks(f, chunks_by_dev): + """Apply a unary op to each tensor in chunks_by_dev, on same device. + + Args: + f: a unary function over T `tf.Tensor`. + chunks_by_dev: list of lists of T `tf.Tensor`. + + Returns: + new list of lists of T `tf.Tensor` with the same structure as + chunks_by_dev containing the derived tensors. + """ + output = [] + for x in chunks_by_dev: + with ops.colocate_with(x[0]): + output.append([f(t) for t in x]) + return output + + +def _build_ring_scatter(pred_by_s_d, rank_by_s_d, + chunks_by_dev): + """Construct subgraph for second (scatter) pass of ring all-reduce. + + Args: + pred_by_s_d: as produced by _ring_permutations + rank_by_s_d: as produced by _ring_permutations + chunks_by_dev: list of list of T `tf.Tensor` indexed by ints + (device, chunk) + + Raises: + ValueError: chunks_by_dev is not well-formed + + Returns: + list of T `tf.Tensor` which are the fully reduced tensors, one + at each device corresponding to the outer dimension of chunks_by_dev. + """ + num_devices = len(chunks_by_dev) + num_chunks = len(chunks_by_dev[0]) + if 0 != num_chunks % num_devices: + raise ValueError( + "Expect number of chunks per device to be divisible by num_devices") + num_subchunks = int(num_chunks / num_devices) + num_ticks = num_devices - 1 + for tick in range(0, num_ticks): + passed_values = [None for _ in range(0, num_chunks)] + for d in range(0, num_devices): + with ops.colocate_with(chunks_by_dev[d][0]): + for s in range(0, num_subchunks): + rank = rank_by_s_d[s][d] + seg_index = (rank + num_devices - (1 + tick)) % num_devices + pred_dev = pred_by_s_d[s][d] + chunk_index = (seg_index * num_subchunks) + s + passed_values[chunk_index] = array_ops.identity( + chunks_by_dev[pred_dev][chunk_index]) + for d in range(0, num_devices): + for s in range(0, num_subchunks): + rank = rank_by_s_d[s][d] + seg_index = (rank + num_devices - (1 + tick)) % num_devices + chunk_index = (seg_index * num_subchunks) + s + chunks_by_dev[d][chunk_index] = passed_values[chunk_index] + # Join chunks at each device. + output = [] + for x in chunks_by_dev: + with ops.colocate_with(x[0]): + output.append(array_ops.concat(x, 0)) + return output + + +def build_recursive_hd_all_reduce(input_tensors, red_op, un_op=None): + """Construct a subgraph for recursive halving-doubling all-reduce. + + The recursive halving-doubling algorithm is described in + http://www.mcs.anl.gov/~thakur/papers/ijhpca-coll.pdf + + The concept is to arrange the participating n devices in + a linear sequence where devices exchange data pairwise + with one other device in each round. During the gather + phase there are lg(n) rounds where devices exchange + increasingly smaller sub-tensors with another device + at increasingly greater distances, until at the top + each device has 1/n of the fully reduced values. During the + scatter phase each device exchanges its fully reduced + sub-tensor (which doubles in length at each round) + with one other device at increasingly smaller distances + until each device has all of the fully reduced values. + + Note: this preliminary version requires that len(input_tensors) be a + power of 2. TODO(tucker): relax this restriction. Also, the + number of elements in each tensor must be divisible by 2^h where h + is the number of hops in each phase. This will also be relaxed in + the future with edge-case specific logic. + + Args: + input_tensors: list of T `tf.Tensor` to be elementwise reduced. + red_op: a binary elementwise reduction Op. + un_op: an optional unary elementwise Op to apply to reduced values. + + Returns: + list of T `tf.Tensor` which are the fully reduced tensors, one + at each device of input_tensors. + + Raises: + ValueError: num_devices not a power of 2, or tensor len not divisible + by 2 the proper number of times. + """ + devices = [t.device for t in input_tensors] + input_tensors, shape = _flatten_tensors(input_tensors) + reduced_shards = _build_recursive_hd_gather(input_tensors, devices, red_op) + if un_op: + reduced_shards = [un_op(t) for t in reduced_shards] + output_tensors = _build_recursive_hd_scatter(reduced_shards, devices) + if len(shape) != 1: + output_tensors = _reshape_tensors(output_tensors, shape) + return output_tensors + + +def _build_recursive_hd_gather(input_tensors, devices, red_op): + """Construct the gather phase of recursive halving-doubling all-reduce. + + Args: + input_tensors: list of T `tf.Tensor` to be elementwise reduced. + devices: a list of strings naming the devices hosting input_tensors, + which will also be used to host the (partial) reduction values. + red_op: a binary elementwise reduction Op. + + Returns: + list of T `tf.Tensor` which are the fully reduced tensor shards. + + Raises: + ValueError: num_devices not a power of 2, or tensor len not divisible + by 2 the proper number of times. + """ + num_devices = len(devices) + num_hops = int(math.log(num_devices, 2)) + if num_devices != (2 ** num_hops): + raise ValueError("num_devices must be a power of 2") + chunks = input_tensors + for h in range(0, num_hops): + span = 2 ** h + group_size = span * 2 + new_chunks = [[] for _ in devices] + for d in range(0, num_devices): + if (d % group_size) >= (group_size / 2): + # skip right half of a pair + continue + left_dev = devices[d] + right_dev = devices[d + span] + left_split = array_ops.split(chunks[d], 2) + right_split = array_ops.split(chunks[d+span], 2) + with ops.device(left_dev): + new_chunks[d] = red_op(left_split[0], right_split[0]) + with ops.device(right_dev): + new_chunks[d + span] = red_op(left_split[1], right_split[1]) + chunks = new_chunks + return chunks + + +def _build_recursive_hd_scatter(input_tensors, devices): + """Construct the scatter phase of recursive halving-doublng all-reduce. + + Args: + input_tensors: list of T `tf.Tensor` that are fully-reduced shards. + devices: a list of strings naming the devices on which the reconstituted + full tensors should be placed. + + Returns: + list of T `tf.Tensor` which are the fully reduced tensors. + """ + num_devices = len(devices) + num_hops = int(math.log(num_devices, 2)) + assert num_devices == (2 ** num_hops), "num_devices must be a power of 2" + chunks = input_tensors + for h in reversed(range(0, num_hops)): + span = 2 ** h + group_size = span * 2 + new_chunks = [[] for _ in devices] + for d in range(0, num_devices): + if (d % group_size) >= (group_size / 2): + # skip right half of a pair + continue + left_idx = d + right_idx = d + span + left_dev = devices[left_idx] + right_dev = devices[right_idx] + with ops.device(left_dev): + new_chunks[left_idx] = array_ops.concat([chunks[left_idx], + chunks[right_idx]], 0) + with ops.device(right_dev): + new_chunks[right_idx] = array_ops.concat([chunks[left_idx], + chunks[right_idx]], 0) + chunks = new_chunks + return chunks + + +def build_shuffle_all_reduce(input_tensors, gather_devices, red_op, un_op=None): + """Construct a subgraph for shuffle all-reduce. + + Shuffle reduce is essentially the algorithm implemented when using + parameter servers. Suppose tensor length is n, there are d devices + and g gather shards. Each device sends a n/g length sub-tensor to + each gather shard. The gather shards perform a reduction across d + fragments, then broadcast the result back to each device. The + devices then join the g fully reduced fragments they receive from + the shards. The gather shards could perform d-1 pairwise + reductions, or one d-way reduction. The first is better where + reduction Op time is low compared to transmission time, the second + better in the other case. + + Args: + input_tensors: list of T @(tf.Tensor} values to be reduced. + gather_devices: list of names of devices on which reduction shards + should be placed. + red_op: an n-array elementwise reduction Op + un_op: optional elementwise unary Op to be applied to fully-reduced values. + + Returns: + list of T `tf.Tensor` which are the fully reduced tensors. + """ + input_tensors, shape = _flatten_tensors(input_tensors) + dst_devices = [t.device for t in input_tensors] + reduced_shards = _build_shuffle_gather(input_tensors, gather_devices, + red_op, un_op) + output_tensors = _build_shuffle_scatter(reduced_shards, dst_devices) + if len(shape) != 1: + output_tensors = _reshape_tensors(output_tensors, shape) + return output_tensors + + +def _build_shuffle_gather(input_tensors, gather_devices, red_op, un_op=None): + """Construct the gather (concentrate and reduce) phase of shuffle all-reduce. + + Args: + input_tensors: list of T @(tf.Tensor} values to be reduced. + gather_devices: list of names of devices on which reduction shards + should be placed. + red_op: the binary reduction Op + un_op: optional elementwise unary Op to be applied to fully-reduced values. + + Returns: + list of T `tf.Tensor` which are the fully reduced shards. + + Raises: + ValueError: inputs not well-formed. + """ + num_source_devices = len(input_tensors) + num_gather_devices = len(gather_devices) + shape = input_tensors[0].shape + if len(shape) != 1: + raise ValueError("input_tensors must be 1D") + shards_by_source = [] + for d in range(0, num_source_devices): + with ops.colocate_with(input_tensors[d]): + shards_by_source.append( + _ragged_split(input_tensors[d], num_gather_devices)) + reduced_shards = [] + for d in range(0, num_gather_devices): + with ops.device(gather_devices[d]): + values = [s[d] for s in shards_by_source] + red_shard = red_op(values) + if un_op: + red_shard = un_op(red_shard) + reduced_shards.append(red_shard) + return reduced_shards + + +def _build_shuffle_scatter(reduced_shards, dst_devices): + """Build the scatter phase of shuffle all-reduce. + + Args: + reduced_shards: list of T @(tf.Tensor} fully reduced shards + dst_devices: list of names of devices at which the fully-reduced value + should be reconstituted. + + Returns: + list of T `tf.Tensor` scattered tensors. + """ + num_devices = len(dst_devices) + out_tensors = [] + for d in range(0, num_devices): + with ops.device(dst_devices[d]): + out_tensors.append(array_ops.concat(reduced_shards, 0)) + return out_tensors + + +def _split_by_task(devices, values): + """Partition devices and values by common task. + + Args: + devices: list of device name strings + values: list of T `tf.tensor` of same length as devices. + + Returns: + (per_task_devices, per_task_values) where both values are + lists of lists with isomorphic structure: the outer list is + indexed by task, and the inner list has length of the number + of values belonging to that task. per_task_devices contains + the specific devices to which the values are local, and + per_task_values contains the corresponding values. + + Raises: + ValueError: devices must be same length as values. + """ + num_devices = len(devices) + if num_devices != len(values): + raise ValueError("len(devices) must equal len(values)") + per_task_devices = collections.OrderedDict() + per_task_values = collections.OrderedDict() + for d in range(num_devices): + d_spec = device_lib.DeviceSpec.from_string(devices[d]) + if not hasattr(d_spec, "task") or d_spec.task is None: + assert False, "failed to parse device %s" % devices[d] + index = (d_spec.job or "localhost", d_spec.replica or 0, d_spec.task) + if index not in per_task_devices: + per_task_devices[index] = [] + per_task_values[index] = [] + per_task_devices[index].append(devices[d]) + per_task_values[index].append(values[d]) + + return (list(per_task_devices.values()), list(per_task_values.values())) + + +def build_nccl_all_reduce(input_tensors, red_op, un_op=None): + """Build a subgraph that does one full all-reduce, using NCCL. + + Args: + input_tensors: list of T `tf.Tensor` of same-shape and type values to + be reduced. + red_op: binary elementwise reduction operator. Must be one of + {tf.add} + un_op: optional unary elementwise Op to apply to fully-reduce values. + + Returns: + list of T `tf.Tensor` of reduced values. + + Raises: + ValueError: red_op not supported. + """ + if red_op == math_ops.add: + output_tensors = nccl_ops.all_sum(input_tensors) + else: + raise ValueError("red_op not supported by NCCL all-reduce: ", red_op) + if un_op: + un_op_wrapped = [] + for t in output_tensors: + with ops.colocate_with(t): + un_op_wrapped.append(un_op(t)) + output_tensors = un_op_wrapped + return output_tensors + + +def _build_nccl_hybrid(input_tensors, red_op, upper_level_f): + """Construct a subgraph for NCCL hybrid all-reduce. + + Args: + input_tensors: list of T `tf.Tensor` of same-shape and type values to + be reduced. + red_op: binary elementwise reduction operator. + upper_level_f: function for reducing one value per worker, across + workers. + + Returns: + list of T `tf.Tensor` of reduced values. + + Raises: + ValueError: inputs not well-formed. + """ + input_tensors, shape = _flatten_tensors(input_tensors) + devices = [t.device for t in input_tensors] + per_worker_devices, per_worker_values = _split_by_task(devices, input_tensors) + num_workers = len(per_worker_devices) + up_values = [None for w in range(0, num_workers)] + up_devices = up_values[:] + down_values = up_values[:] + # First stage: reduce within each worker using NCCL + for w in range(0, num_workers): + worker_values = build_nccl_all_reduce(per_worker_values[w], red_op) + # NOTE: these reductions will not run to completion unless + # every output value is used. Since we only need one, we + # need to put control dependencies on the rest. + with ops.control_dependencies(worker_values): + with ops.device(worker_values[0].device): + up_values[w] = array_ops.identity(worker_values[0]) + up_devices[w] = per_worker_devices[w][0] + # Second stage: Apply upper_level_f to reduce across first device at + # each worker + level_2_output = upper_level_f(up_values) + # Third stage: propagate within each worker using NCCL Broadcast + for w in range(0, num_workers): + dst_tensors = [] + with ops.device(per_worker_devices[w][0]): + broadcast_src = nccl_ops.broadcast(array_ops.identity(level_2_output[w])) + for d in per_worker_devices[w]: + with ops.device(d): + dst_tensors.append(array_ops.identity(broadcast_src)) + down_values[w] = dst_tensors + output_tensors = [v for sublist in down_values for v in sublist] + if len(shape) != 1: + output_tensors = _reshape_tensors(output_tensors, shape) + return output_tensors + + +def _reduce_non_singleton(input_tensors, red_f, un_op): + """If len(input_tensors) > 1, apply red_f, else apply un_op.""" + if len(input_tensors) > 1: + return red_f(input_tensors) + else: + if not un_op: + return input_tensors + output_tensors = [] + for t in input_tensors: + with ops.colocate_with(t): + output_tensors.append(un_op(t)) + return output_tensors + + +def build_nccl_then_ring(input_tensors, subdiv, red_op, un_op=None): + """Construct hybrid of NCCL within workers, Ring across workers.""" + def upper_builder(y): + return build_ring_all_reduce(y, len(y), subdiv, [0], red_op, un_op) + def upper_level_f(x): + return _reduce_non_singleton(x, upper_builder, un_op) + return _build_nccl_hybrid(input_tensors, red_op, upper_level_f) + + +def build_nccl_then_recursive_hd(input_tensors, red_op, un_op=None): + """Construct hybrid of NCCL within workers, Recursive-HD across workers.""" + upper_level_f = lambda x: build_recursive_hd_all_reduce(x, red_op, un_op) + return _build_nccl_hybrid(input_tensors, red_op, upper_level_f) + + +def build_nccl_then_shuffle(input_tensors, gather_devices, nccl_red_op, + shuffle_red_op, un_op=None): + """Construct hybrid of NCCL within workers, Shuffle across workers.""" + def upper_level_f(x): + return build_shuffle_all_reduce(x, gather_devices, shuffle_red_op, un_op) + + return _build_nccl_hybrid(input_tensors, nccl_red_op, upper_level_f) + + +def _build_shuffle_hybrid(input_tensors, gather_devices, red_op, upper_level_f): + """Construct a subgraph for Shuffle hybrid all-reduce. + + Args: + input_tensors: list of T `tf.Tensor` of same-shape and type values to + be reduced. + gather_devices: list of device names on which to host gather shards. + red_op: binary elementwise reduction operator. + upper_level_f: function for reducing one value per worker, across + workers. + + Returns: + list of T `tf.Tensor` of reduced values. + + Raises: + ValueError: inputs not well-formed. + """ + input_tensors, shape = _flatten_tensors(input_tensors) + # First stage, reduce across each worker using gather_devices. + devices = [t.device for t in input_tensors] + per_worker_devices, per_worker_values = _split_by_task(devices, input_tensors) + num_workers = len(per_worker_devices) + up_values = [] + if len(gather_devices) != num_workers: + raise ValueError("For shuffle hybrid, gather_devices must contain one " + "device per worker. ") + for w in range(0, num_workers): + reduced_shards = _build_shuffle_gather( + per_worker_values[w], [gather_devices[w]], red_op) + up_values.append(reduced_shards[0]) + # Second stage, apply upper_level_f. + level_2_output = upper_level_f(up_values) + # Third stage, apply shuffle scatter at each worker. + output_tensors = [] + for w in range(0, num_workers): + output_tensors += _build_shuffle_scatter( + [level_2_output[w]], per_worker_devices[w]) + if len(shape) != 1: + output_tensors = _reshape_tensors(output_tensors, shape) + return output_tensors + + +def build_shuffle_then_ring(input_tensors, gather_devices, subdiv, + red_n_op, red_op, un_op=None): + """Construct hybrid of Shuffle within workers, Ring across workers.""" + def upper_builder(tensors): + return build_ring_all_reduce(tensors, len(tensors), subdiv, [0], + red_op, un_op) + def upper_level_f(tensors): + return _reduce_non_singleton(tensors, upper_builder, un_op) + return _build_shuffle_hybrid( + input_tensors, gather_devices, red_n_op, upper_level_f) + + +def build_shuffle_then_shuffle(input_tensors, first_gather_devices, + second_gather_devices, red_op, un_op=None): + """Construct hybrid of Shuffle within workers, Shuffle across workers.""" + def upper_builder(tensors): + return build_shuffle_all_reduce(tensors, second_gather_devices, + red_op, un_op) + def upper_level_f(tensors): + return _reduce_non_singleton(tensors, upper_builder, un_op) + return _build_shuffle_hybrid( + input_tensors, first_gather_devices, red_op, upper_level_f) diff --git a/tensorflow/contrib/all_reduce/python/all_reduce_test.py b/tensorflow/python/distribute/all_reduce_test.py similarity index 97% rename from tensorflow/contrib/all_reduce/python/all_reduce_test.py rename to tensorflow/python/distribute/all_reduce_test.py index 304fd7fb8a..2c6b853124 100644 --- a/tensorflow/contrib/all_reduce/python/all_reduce_test.py +++ b/tensorflow/python/distribute/all_reduce_test.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Tests for tensorflow.contrib.all_reduce.python..all_reduce.""" +"""Tests for all_reduce.""" from __future__ import absolute_import from __future__ import division @@ -22,8 +22,8 @@ import time import numpy as np -from tensorflow.contrib.all_reduce.python import all_reduce as ar from tensorflow.core.framework import types_pb2 +from tensorflow.python.distribute import all_reduce as ar from tensorflow.python.framework import constant_op from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape @@ -37,6 +37,7 @@ from tensorflow.python.platform import tf_logging class AllReduceTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testFlattenTensorsShapesDefined(self): x = array_ops.placeholder(types_pb2.DT_FLOAT, [None]) with self.assertRaisesRegexp(ValueError, @@ -100,6 +101,7 @@ class AllReduceTest(test_util.TensorFlowTestCase): input_tensors.append(array_ops.identity(t8)) return input_tensors, device_names + @test_util.run_deprecated_v1 def testBuildRingGatherPassStructure(self): # 1 worker, 1 device input_tensors, device_names = self._buildInput(1, 1) @@ -159,7 +161,7 @@ class AllReduceTest(test_util.TensorFlowTestCase): output_tensors = build_f(input_tensors, un_op) sum_reduced = math_ops.add_n(output_tensors) sum_reduced.op.run() - self.assertAllClose(sum_reduced.eval(), simple_sum.eval()) + self.assertAllClose(sum_reduced.eval(), self.evaluate(simple_sum)) def _testRingAllReduce(self, num_workers, num_gpus, shape, subdiv): start_time = time.time() @@ -170,6 +172,7 @@ class AllReduceTest(test_util.TensorFlowTestCase): "subdiv=%d elapsed=%f" % (num_workers, num_gpus, shape, subdiv, elapsed)) + @test_util.run_deprecated_v1 def testRingAllReduce(self): self._testRingAllReduce(1, 2, [], 1) self._testRingAllReduce(1, 2, [8], 1) @@ -199,6 +202,7 @@ class AllReduceTest(test_util.TensorFlowTestCase): tf_logging.info("ShuffleAllReduce num_workers=%d num_gpus=%d shape=%s " "elapsed=%f" % (num_workers, num_gpus, shape, elapsed)) + @test_util.run_deprecated_v1 def testShuffleAllReduce(self): self._testShuffleAllReduce(1, 2, [], 1) self._testShuffleAllReduce(1, 2, [8], 1) @@ -225,6 +229,7 @@ class AllReduceTest(test_util.TensorFlowTestCase): "shape=%s elapsed=%f" % (num_workers, num_gpus, shape, elapsed)) + @test_util.run_deprecated_v1 def testRecursiveHDAllReduce(self): self._testRecursiveHDAllReduce(1, 2, [8]) self._testRecursiveHDAllReduce(1, 2, [4, 4]) diff --git a/tensorflow/python/distribute/cluster_resolver/BUILD b/tensorflow/python/distribute/cluster_resolver/BUILD new file mode 100644 index 0000000000..360a2993cd --- /dev/null +++ b/tensorflow/python/distribute/cluster_resolver/BUILD @@ -0,0 +1,180 @@ +# Description: Operations defined for Cluster Resolvers + +load("//tensorflow:tensorflow.bzl", "tf_py_test") + +package( + default_visibility = [ + "//tensorflow:__subpackages__", + ], +) + +licenses(["notice"]) # Apache 2.0 + +py_library( + name = "cluster_resolver_lib", + srcs = [ + "__init__.py", + ], + srcs_version = "PY2AND3", + visibility = ["//visibility:public"], + deps = [ + ":base_cluster_resolver_py", + ":gce_cluster_resolver_py", + ":kubernetes_cluster_resolver_py", + ":slurm_cluster_resolver_py", + ":tfconfig_cluster_resolver_py", + ":tpu_cluster_resolver_py", + "//tensorflow/python:util", + ], +) + +py_library( + name = "base_cluster_resolver_py", + srcs = ["cluster_resolver.py"], + srcs_version = "PY2AND3", + deps = [ + "//tensorflow/python:training_server_lib", + ], +) + +py_library( + name = "gce_cluster_resolver_py", + srcs = ["gce_cluster_resolver.py"], + srcs_version = "PY2AND3", + deps = [ + ":base_cluster_resolver_py", + "//tensorflow/python:training_server_lib", + ], +) + +py_library( + name = "tfconfig_cluster_resolver_py", + srcs = ["tfconfig_cluster_resolver.py"], + srcs_version = "PY2AND3", + deps = [ + ":base_cluster_resolver_py", + "//tensorflow/python:training_server_lib", + ], +) + +py_library( + name = "tpu_cluster_resolver_py", + srcs = ["tpu_cluster_resolver.py"], + srcs_version = "PY2AND3", + deps = [ + ":base_cluster_resolver_py", + "//tensorflow/python:training_server_lib", + ], +) + +py_library( + name = "slurm_cluster_resolver_py", + srcs = ["slurm_cluster_resolver.py"], + srcs_version = "PY2AND3", + deps = [ + ":base_cluster_resolver_py", + "//tensorflow/python:training_server_lib", + ], +) + +py_library( + name = "kubernetes_cluster_resolver_py", + srcs = ["kubernetes_cluster_resolver.py"], + srcs_version = "PY2AND3", + deps = [ + ":base_cluster_resolver_py", + "//tensorflow/python:training_server_lib", + ], +) + +tf_py_test( + name = "base_cluster_resolver_py_test", + srcs = ["cluster_resolver_test.py"], + additional_deps = [ + ":base_cluster_resolver_py", + "//tensorflow/python:client_testlib", + "//tensorflow/python:framework_for_generated_wrappers", + "//tensorflow/python:framework_test_lib", + "//tensorflow/python:platform_test", + "//tensorflow/python:training_server_lib", + ], + main = "cluster_resolver_test.py", +) + +tf_py_test( + name = "gce_cluster_resolver_py_test", + size = "small", + srcs = ["gce_cluster_resolver_test.py"], + additional_deps = [ + ":gce_cluster_resolver_py", + "//tensorflow/python:client_testlib", + "//tensorflow/python:framework_for_generated_wrappers", + "//tensorflow/python:framework_test_lib", + "//tensorflow/python:platform_test", + "//tensorflow/python:training_server_lib", + ], + main = "gce_cluster_resolver_test.py", +) + +tf_py_test( + name = "tfconfig_cluster_resolver_py_test", + size = "small", + srcs = ["tfconfig_cluster_resolver_test.py"], + additional_deps = [ + ":tfconfig_cluster_resolver_py", + "//tensorflow/python:client_testlib", + "//tensorflow/python:framework_for_generated_wrappers", + "//tensorflow/python:framework_test_lib", + "//tensorflow/python:platform_test", + "//tensorflow/python:training_server_lib", + ], + grpc_enabled = True, + main = "tfconfig_cluster_resolver_test.py", +) + +tf_py_test( + name = "tpu_cluster_resolver_py_test", + size = "small", + srcs = ["tpu_cluster_resolver_test.py"], + additional_deps = [ + ":tpu_cluster_resolver_py", + "//tensorflow/python:client_testlib", + "//tensorflow/python:framework_for_generated_wrappers", + "//tensorflow/python:framework_test_lib", + "//tensorflow/python:platform_test", + "//tensorflow/python:training_server_lib", + ], + grpc_enabled = True, + main = "tpu_cluster_resolver_test.py", +) + +tf_py_test( + name = "slurm_cluster_resolver_py_test", + size = "small", + srcs = ["slurm_cluster_resolver_test.py"], + additional_deps = [ + ":slurm_cluster_resolver_py", + "//tensorflow/python:client_testlib", + "//tensorflow/python:framework_for_generated_wrappers", + "//tensorflow/python:framework_test_lib", + "//tensorflow/python:platform_test", + "//tensorflow/python:training_server_lib", + ], + main = "slurm_cluster_resolver_test.py", + tags = [], +) + +tf_py_test( + name = "kubernetes_cluster_resolver_py_test", + size = "small", + srcs = ["kubernetes_cluster_resolver_test.py"], + additional_deps = [ + ":kubernetes_cluster_resolver_py", + "//tensorflow/python:client_testlib", + "//tensorflow/python:framework_for_generated_wrappers", + "//tensorflow/python:framework_test_lib", + "//tensorflow/python:platform_test", + "//tensorflow/python:training_server_lib", + ], + main = "kubernetes_cluster_resolver_test.py", +) diff --git a/tensorflow/contrib/cluster_resolver/README.md b/tensorflow/python/distribute/cluster_resolver/README.md similarity index 100% rename from tensorflow/contrib/cluster_resolver/README.md rename to tensorflow/python/distribute/cluster_resolver/README.md diff --git a/tensorflow/contrib/cluster_resolver/python/training/README.slurm b/tensorflow/python/distribute/cluster_resolver/README.slurm similarity index 100% rename from tensorflow/contrib/cluster_resolver/python/training/README.slurm rename to tensorflow/python/distribute/cluster_resolver/README.slurm diff --git a/tensorflow/python/distribute/cluster_resolver/__init__.py b/tensorflow/python/distribute/cluster_resolver/__init__.py new file mode 100644 index 0000000000..ef87f59b7f --- /dev/null +++ b/tensorflow/python/distribute/cluster_resolver/__init__.py @@ -0,0 +1,57 @@ +# Copyright 2018 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. +# ============================================================================== +"""Library Imports for Cluster Resolvers.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.distribute.cluster_resolver import cluster_resolver +from tensorflow.python.distribute.cluster_resolver import gce_cluster_resolver +from tensorflow.python.distribute.cluster_resolver import kubernetes_cluster_resolver +from tensorflow.python.distribute.cluster_resolver import slurm_cluster_resolver +from tensorflow.python.distribute.cluster_resolver import tfconfig_cluster_resolver +from tensorflow.python.distribute.cluster_resolver import tpu_cluster_resolver + +from tensorflow.python.distribute.cluster_resolver.cluster_resolver import ClusterResolver +from tensorflow.python.distribute.cluster_resolver.cluster_resolver import SimpleClusterResolver +from tensorflow.python.distribute.cluster_resolver.cluster_resolver import UnionClusterResolver +from tensorflow.python.distribute.cluster_resolver.gce_cluster_resolver import GceClusterResolver +from tensorflow.python.distribute.cluster_resolver.kubernetes_cluster_resolver import KubernetesClusterResolver +from tensorflow.python.distribute.cluster_resolver.slurm_cluster_resolver import SlurmClusterResolver +from tensorflow.python.distribute.cluster_resolver.tfconfig_cluster_resolver import TFConfigClusterResolver +from tensorflow.python.distribute.cluster_resolver.tpu_cluster_resolver import TPUClusterResolver + +from tensorflow.python.util.all_util import remove_undocumented + +_allowed_symbols = [ + 'cluster_resolver', + 'gce_cluster_resolver', + 'kubernetes_cluster_resolver', + 'slurm_cluster_resolver', + 'tfconfig_cluster_resolver', + 'tpu_cluster_resolver', + 'ClusterResolver', + 'SimpleClusterResolver', + 'UnionClusterResolver', + 'GceClusterResolver', + 'KubernetesClusterResolver', + 'TFConfigClusterResolver', + 'TPUClusterResolver', + 'SlurmClusterResolver', +] + +remove_undocumented(__name__, _allowed_symbols) + diff --git a/tensorflow/python/distribute/cluster_resolver/cluster_resolver.py b/tensorflow/python/distribute/cluster_resolver/cluster_resolver.py new file mode 100644 index 0000000000..7774ac0e12 --- /dev/null +++ b/tensorflow/python/distribute/cluster_resolver/cluster_resolver.py @@ -0,0 +1,374 @@ +# 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. +# ============================================================================== +"""Cluster Resolvers are used for dynamic cluster IP/hostname resolution.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import abc + +import six + +from tensorflow.python.training.server_lib import ClusterSpec + + +def format_master_url(master, rpc_layer=None): + if rpc_layer: + return '%s://%s' % (rpc_layer, master) + else: + return master + + +@six.add_metaclass(abc.ABCMeta) +class ClusterResolver(object): + """Abstract class for all implementations of ClusterResolvers. + + This defines the skeleton for all implementations of ClusterResolvers. + ClusterResolvers are a way for TensorFlow to communicate with various cluster + management systems (e.g. GCE, AWS, etc...). + + By letting TensorFlow communicate with these systems, we will be able to + automatically discover and resolve IP addresses for various TensorFlow + workers. This will eventually allow us to automatically recover from + underlying machine failures and scale TensorFlow worker clusters up and down. + + Note to Implementors: In addition to these abstract methods, you must also + implement the task_type, task_index, and rpc_layer attributes. You may choose + to implement them either as properties with getters or setters or directly + set the attributes. + + - task_type is the name of the server's current named job (e.g. 'worker', + 'ps' in a distributed parameterized training job). + - task_index is the ordinal index of the server within the task type. + - rpc_layer is the protocol used by TensorFlow to communicate with other + TensorFlow servers in a distributed environment. + """ + + @abc.abstractmethod + def cluster_spec(self): + """Retrieve the current state of the cluster and returns a ClusterSpec. + + Returns: + A ClusterSpec representing the state of the cluster at the moment this + function is called. + + Implementors of this function must take care in ensuring that the + ClusterSpec returned is up-to-date at the time of calling this function. + This usually means retrieving the information from the underlying cluster + management system every time this function is invoked and reconstructing + a cluster_spec, rather than attempting to cache anything. + """ + raise NotImplementedError() + + @abc.abstractmethod + def master(self, task_type=None, task_index=None, rpc_layer=None): + """Retrieves the name or URL of the session master. + + Args: + task_type: (Optional) The type of the TensorFlow task of the master. + task_index: (Optional) The index of the TensorFlow task of the master. + rpc_layer: (Optional) The RPC protocol for the given cluster. + + Returns: + The name or URL of the session master. + + Implementors of this function must take care in ensuring that the master + returned is up-to-date at the time to calling this function. This usually + means retrieving the master every time this function is invoked. + """ + raise NotImplementedError() + + @abc.abstractmethod + def num_accelerators_per_worker(self, session_config=None): + """Returns the number of accelerator cores per worker. + + This returns the number of accelerator cores (such as GPUs and TPUs) + available per worker. If workers only has CPU cores available, then this + should return 0. This method will query the master for this information + if it is not otherwise known. + + Args: + session_config: (Optional) Configuration for starting a new session to + query how many accelerator cores it has. + """ + raise NotImplementedError() + + @abc.abstractproperty + def environment(self): + """Returns the current environment which TensorFlow is running in.""" + raise NotImplementedError() + + +class SimpleClusterResolver(ClusterResolver): + """Simple implementation of ClusterResolver that accepts a ClusterSpec.""" + + def __init__(self, cluster_spec, master='', task_type=None, task_index=None, + environment='', num_accelerators_per_worker=0, + rpc_layer=None): + """Creates a SimpleClusterResolver from a ClusterSpec.""" + super(SimpleClusterResolver, self).__init__() + + self._task_type = task_type + self._task_index = task_index + self._environment = environment + self._num_accelerators_per_worker = num_accelerators_per_worker + self._rpc_layer = rpc_layer + + if not isinstance(cluster_spec, ClusterSpec): + raise TypeError('cluster_spec must be a ClusterSpec.') + self._cluster_spec = cluster_spec + + if not isinstance(master, str): + raise TypeError('master must be a string.') + self._master = master + + def cluster_spec(self): + """Returns the ClusterSpec passed into the constructor.""" + return self._cluster_spec + + def master(self, task_type=None, task_index=None, rpc_layer=None): + """Returns the master address to use when creating a session. + + Args: + task_type: (Optional) The type of the TensorFlow task of the master. + task_index: (Optional) The index of the TensorFlow task of the master. + rpc_layer: (Optional) The RPC used by distributed TensorFlow. + + Returns: + The name or URL of the session master. + + If a task_type and task_index is given, this will override the `master` + string passed into the initialization function. + """ + if task_type is not None and task_index is not None: + master = self.cluster_spec().task_address(task_type, task_index) + else: + master = self._master + + return format_master_url(master, rpc_layer=rpc_layer or self._rpc_layer) + + @property + def task_type(self): + return self._task_type + + @property + def task_index(self): + return self._task_index + + @task_type.setter + def task_type(self, task_type): + self._task_type = task_type + + @task_index.setter + def task_index(self, task_index): + self._task_index = task_index + + @property + def environment(self): + return self._environment + + def num_accelerators_per_worker(self, session_config=None): + """Returns the number of accelerator cores per worker. + + Args: + session_config: Unused. The SimpleClusterResolver does not do automatic + detection of accelerators, so a TensorFlow session will never be + created, and thus a `session_config` is never necessary here, and will + be ignored. + """ + del session_config + return self._num_accelerators_per_worker + + @property + def rpc_layer(self): + return self._rpc_layer + + @rpc_layer.setter + def rpc_layer(self, rpc_layer): + self._rpc_layer = rpc_layer + + +class UnionClusterResolver(ClusterResolver): + """Performs a union on underlying ClusterResolvers. + + This class performs a union given two or more existing ClusterResolvers. It + merges the underlying ClusterResolvers, and returns one unified ClusterSpec + when cluster_spec is called. The details of the merge function is + documented in the cluster_spec function. + + For additional Cluster Resolver properties such as task type, task index, + rpc layer, environment, etc..., we will return the value from the first + ClusterResolver in the union. + """ + + def __init__(self, *args, **kwargs): + """Initializes a UnionClusterResolver with other ClusterResolvers. + + Args: + *args: `ClusterResolver` objects to be unionized. + **kwargs: + rpc_layer - (Optional) Override value for the RPC layer used by + TensorFlow. + task_type - (Optional) Override value for the current task type. + task_index - (Optional) Override value for the current task index. + + Raises: + TypeError: If any argument is not a subclass of `ClusterResolvers`. + ValueError: If there are no arguments passed. + """ + super(UnionClusterResolver, self).__init__() + + self._rpc_layer = kwargs.pop('rpc_layer', None) + self._task_type = kwargs.pop('task_type', None) + self._task_index = kwargs.pop('task_index', None) + + if kwargs: + raise ValueError('Unexpected kwargs provided {!r}'.format(kwargs)) + + if not args: + raise ValueError('At least one ClusterResolver is required.') + + for cluster_resolver in args: + if not isinstance(cluster_resolver, ClusterResolver): + raise TypeError('All arguments must be a sub-class of ' + '`ClusterResolver.`') + self._cluster_resolvers = args + + def cluster_spec(self): + """Returns a union of all the ClusterSpecs from the ClusterResolvers. + + Returns: + A ClusterSpec containing host information merged from all the underlying + ClusterResolvers. + + Raises: + KeyError: If there are conflicting keys detected when merging two or + more dictionaries, this exception is raised. + + Note: If there are multiple ClusterResolvers exposing ClusterSpecs with the + same job name, we will merge the list/dict of workers. + + If *all* underlying ClusterSpecs expose the set of workers as lists, we will + concatenate the lists of workers, starting with the list of workers from + the first ClusterResolver passed into the constructor. + + If *any* of the ClusterSpecs expose the set of workers as a dict, we will + treat all the sets of workers as dicts (even if they are returned as lists) + and will only merge them into a dict if there is no conflicting keys. If + there is a conflicting key, we will raise a `KeyError`. + """ + + merged_cluster = {} + + # We figure out whether it is all lists for a particular job, or whether + # there are dicts inside. + for cluster_resolver in self._cluster_resolvers: + cluster_spec = cluster_resolver.cluster_spec() + cluster_dict = cluster_spec.as_dict() + + for job_name, tasks in cluster_dict.items(): + if job_name in merged_cluster: + # If we see a dict, then we write a dict out regardless. + if isinstance(tasks, dict): + merged_cluster[job_name] = {} + else: + # We take whichever type is present. + if isinstance(tasks, list): + merged_cluster[job_name] = [] + else: + merged_cluster[job_name] = {} + + # We then do the merge as appropriate in merged_cluster[job]. + for cluster_resolver in self._cluster_resolvers: + cluster_spec = cluster_resolver.cluster_spec() + cluster_dict = cluster_spec.as_dict() + + for job_name, tasks in cluster_dict.items(): + if isinstance(merged_cluster[job_name], list): + # We all have lists, we can just concatenate and be done. + merged_cluster[job_name].extend(tasks) + else: + if isinstance(tasks, list): + # We convert to a dictionary if the type is a list. + task_dict = dict(zip(range(0, len(tasks)), tasks)) + else: + # We can simply make a copy (for update) and be done. + task_dict = tasks.copy() + + # We detect if there are duplicates, and raise an error if so. + task_keys = set(task_dict) + merged_keys = set(merged_cluster[job_name].keys()) + intersected_keys = task_keys.intersection(merged_keys) + if intersected_keys: + raise KeyError('Duplicate keys detected when merging two ' + 'ClusterSpecs: %s' % repr(intersected_keys)) + + # We do the merge after all the processing. + merged_cluster[job_name].update(task_dict) + + return ClusterSpec(merged_cluster) + + def master(self, task_type=None, task_index=None, rpc_layer=None): + """Returns the master address to use when creating a session. + + This usually returns the master from the first ClusterResolver passed in, + but you can override this by specifying the task_type and task_index. + + Args: + task_type: (Optional) The type of the TensorFlow task of the master. + task_index: (Optional) The index of the TensorFlow task of the master. + rpc_layer: (Optional) The RPC protocol for the given cluster. + + Returns: + The name or URL of the session master. + """ + if task_type is not None and task_index is not None: + master = self.cluster_spec().task_address(task_type, task_index) + return format_master_url(master, rpc_layer or self._rpc_layer) + + return self._cluster_resolvers[0].master(rpc_layer=rpc_layer) + + @property + def task_type(self): + return self._task_type or self._cluster_resolvers[0].task_type + + @property + def task_index(self): + return self._task_index or self._cluster_resolvers[0].task_index + + @task_type.setter + def task_type(self, task_type): + self._task_type = task_type + + @task_index.setter + def task_index(self, task_index): + self._task_index = task_index + + @property + def environment(self): + return self._cluster_resolvers[0].environment + + def num_accelerators_per_worker(self, session_config=None): + return self._cluster_resolvers[0].num_accelerators_per_worker( + session_config) + + @property + def rpc_layer(self): + return self._rpc_layer or self._cluster_resolvers[0].rpc_layer + + @rpc_layer.setter + def rpc_layer(self, rpc_layer): + self._rpc_layer = rpc_layer diff --git a/tensorflow/contrib/cluster_resolver/python/training/cluster_resolver_test.py b/tensorflow/python/distribute/cluster_resolver/cluster_resolver_test.py similarity index 98% rename from tensorflow/contrib/cluster_resolver/python/training/cluster_resolver_test.py rename to tensorflow/python/distribute/cluster_resolver/cluster_resolver_test.py index b94c9612b5..b5448faec6 100644 --- a/tensorflow/contrib/cluster_resolver/python/training/cluster_resolver_test.py +++ b/tensorflow/python/distribute/cluster_resolver/cluster_resolver_test.py @@ -18,8 +18,8 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -from tensorflow.contrib.cluster_resolver.python.training.cluster_resolver import SimpleClusterResolver -from tensorflow.contrib.cluster_resolver.python.training.cluster_resolver import UnionClusterResolver +from tensorflow.python.distribute.cluster_resolver import SimpleClusterResolver +from tensorflow.python.distribute.cluster_resolver import UnionClusterResolver from tensorflow.python.platform import test from tensorflow.python.training import server_lib diff --git a/tensorflow/python/distribute/cluster_resolver/gce_cluster_resolver.py b/tensorflow/python/distribute/cluster_resolver/gce_cluster_resolver.py new file mode 100644 index 0000000000..b167bc8fc8 --- /dev/null +++ b/tensorflow/python/distribute/cluster_resolver/gce_cluster_resolver.py @@ -0,0 +1,206 @@ +# 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. +# ============================================================================== +"""Implementation of Cluster Resolvers for GCE Instance Groups.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.distribute.cluster_resolver.cluster_resolver import ClusterResolver +from tensorflow.python.training.server_lib import ClusterSpec + +_GOOGLE_API_CLIENT_INSTALLED = True +try: + from googleapiclient import discovery # pylint: disable=g-import-not-at-top + from oauth2client.client import GoogleCredentials # pylint: disable=g-import-not-at-top +except ImportError: + _GOOGLE_API_CLIENT_INSTALLED = False + + +def _format_master_url(master, rpc_layer=None): + return '%s://%s' % (rpc_layer, master) if rpc_layer else master + + +class GceClusterResolver(ClusterResolver): + """Cluster Resolver for Google Compute Engine. + + This is an implementation of cluster resolvers for the Google Compute Engine + instance group platform. By specifying a project, zone, and instance group, + this will retrieve the IP address of all the instances within the instance + group and return a Cluster Resolver object suitable for use for distributed + TensorFlow. + """ + + def __init__(self, + project, + zone, + instance_group, + port, + task_type='worker', + task_index=0, + rpc_layer='grpc', + num_accelerators_per_worker=0, + credentials='default', + service=None): + """Creates a new GceClusterResolver object. + + This takes in a few parameters and creates a GceClusterResolver project. It + will then use these parameters to query the GCE API for the IP addresses of + each instance in the instance group. + + Args: + project: Name of the GCE project. + zone: Zone of the GCE instance group. + instance_group: Name of the GCE instance group. + port: Port of the listening TensorFlow server (default: 8470) + task_type: Name of the TensorFlow job this GCE instance group of VM + instances belong to. + task_index: The task index for this particular VM, within the GCE + instance group. In particular, every single instance should be assigned + a unique ordinal index within an instance group manually so that they + can be distinguished from each other. + rpc_layer: The RPC layer TensorFlow should use to communicate across + instances. + num_accelerators_per_worker: Number of accelerators (GPUs) present per + instance. + credentials: GCE Credentials. If nothing is specified, this defaults to + GoogleCredentials.get_application_default(). + service: The GCE API object returned by the googleapiclient.discovery + function. (Default: discovery.build('compute', 'v1')). If you specify a + custom service object, then the credentials parameter will be ignored. + + Raises: + ImportError: If the googleapiclient is not installed. + """ + self._project = project + self._zone = zone + self._instance_group = instance_group + self._task_type = task_type + self._task_index = task_index + self._rpc_layer = rpc_layer + self._port = port + self._credentials = credentials + + if credentials == 'default': + if _GOOGLE_API_CLIENT_INSTALLED: + self._credentials = GoogleCredentials.get_application_default() + + if service is None: + if not _GOOGLE_API_CLIENT_INSTALLED: + raise ImportError('googleapiclient must be installed before using the ' + 'GCE cluster resolver') + self._service = discovery.build( + 'compute', 'v1', + credentials=self._credentials) + else: + self._service = service + + def cluster_spec(self): + """Returns a ClusterSpec object based on the latest instance group info. + + This returns a ClusterSpec object for use based on information from the + specified instance group. We will retrieve the information from the GCE APIs + every time this method is called. + + Returns: + A ClusterSpec containing host information retrieved from GCE. + """ + request_body = {'instanceState': 'RUNNING'} + request = self._service.instanceGroups().listInstances( + project=self._project, + zone=self._zone, + instanceGroups=self._instance_group, + body=request_body, + orderBy='name') + + worker_list = [] + + while request is not None: + response = request.execute() + + items = response['items'] + for instance in items: + instance_name = instance['instance'].split('/')[-1] + + instance_request = self._service.instances().get( + project=self._project, + zone=self._zone, + instance=instance_name) + + if instance_request is not None: + instance_details = instance_request.execute() + ip_address = instance_details['networkInterfaces'][0]['networkIP'] + instance_url = '%s:%s' % (ip_address, self._port) + worker_list.append(instance_url) + + request = self._service.instanceGroups().listInstances_next( + previous_request=request, + previous_response=response) + + worker_list.sort() + return ClusterSpec({self._task_type: worker_list}) + + def master(self, task_type=None, task_index=None, rpc_layer=None): + task_type = task_type if task_type is not None else self._task_type + task_index = task_index if task_index is not None else self._task_index + + if task_type is not None and task_index is not None: + master = self.cluster_spec().task_address(task_type, task_index) + if rpc_layer or self._rpc_layer: + return '%s://%s' % (rpc_layer or self._rpc_layer, master) + else: + return master + + return '' + + @property + def task_type(self): + return self._task_type + + @property + def task_index(self): + return self._task_index + + @task_type.setter + def task_type(self, task_type): + raise RuntimeError( + 'You cannot reset the task_type of the GceClusterResolver after it has ' + 'been created.') + + @task_index.setter + def task_index(self, task_index): + self._task_index = task_index + + @property + def environment(self): + """Returns the current environment which TensorFlow is running in. + + For users in the GCE environment, the environment property is always an + empty string, and Google users will not use this ClusterResolver for running + on internal systems. + """ + return '' + + @property + def rpc_layer(self): + return self._rpc_layer + + @rpc_layer.setter + def rpc_layer(self, rpc_layer): + self._rpc_layer = rpc_layer + + def num_accelerators_per_worker(self, session_config=None): + del session_config # Unused, since this is set manually in __init__. + return self._num_accelerators_per_worker diff --git a/tensorflow/contrib/cluster_resolver/python/training/gce_cluster_resolver_test.py b/tensorflow/python/distribute/cluster_resolver/gce_cluster_resolver_test.py similarity index 98% rename from tensorflow/contrib/cluster_resolver/python/training/gce_cluster_resolver_test.py rename to tensorflow/python/distribute/cluster_resolver/gce_cluster_resolver_test.py index c691552e86..d4f0660c92 100644 --- a/tensorflow/contrib/cluster_resolver/python/training/gce_cluster_resolver_test.py +++ b/tensorflow/python/distribute/cluster_resolver/gce_cluster_resolver_test.py @@ -18,8 +18,8 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -from tensorflow.contrib.cluster_resolver.python.training.cluster_resolver import UnionClusterResolver -from tensorflow.contrib.cluster_resolver.python.training.gce_cluster_resolver import GceClusterResolver +from tensorflow.python.distribute.cluster_resolver import GceClusterResolver +from tensorflow.python.distribute.cluster_resolver import UnionClusterResolver from tensorflow.python.platform import test from tensorflow.python.training import server_lib diff --git a/tensorflow/python/distribute/cluster_resolver/kubernetes_cluster_resolver.py b/tensorflow/python/distribute/cluster_resolver/kubernetes_cluster_resolver.py new file mode 100644 index 0000000000..041c081540 --- /dev/null +++ b/tensorflow/python/distribute/cluster_resolver/kubernetes_cluster_resolver.py @@ -0,0 +1,173 @@ +# Copyright 2018 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. +# ============================================================================== +"""Implementation of Cluster Resolvers for Kubernetes.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.client import device_lib +from tensorflow.python.distribute.cluster_resolver.cluster_resolver import ClusterResolver +from tensorflow.python.distribute.cluster_resolver.cluster_resolver import format_master_url +from tensorflow.python.training import server_lib + +_KUBERNETES_API_CLIENT_INSTALLED = True +try: + from kubernetes import client as k8sclient # pylint: disable=g-import-not-at-top + from kubernetes import config as k8sconfig # pylint: disable=g-import-not-at-top +except ImportError: + _KUBERNETES_API_CLIENT_INSTALLED = False + + +class KubernetesClusterResolver(ClusterResolver): + """Cluster Resolver for Kubernetes. + + This is an implementation of cluster resolvers for Kubernetes. When given the + the Kubernetes namespace and label selector for pods, we will retrieve the + pod IP addresses of all running pods matching the selector, and return a + ClusterSpec based on that information. + """ + + def __init__(self, + job_to_label_mapping=None, + tf_server_port=8470, + rpc_layer='grpc', + override_client=None): + """Initializes a new KubernetesClusterResolver. + + This initializes a new Kubernetes Cluster Resolver. The Cluster Resolver + will attempt to talk to the Kubernetes master to retrieve all the instances + of pods matching a label selector. + + Args: + job_to_label_mapping: A mapping of TensorFlow jobs to label selectors. + This allows users to specify many TensorFlow jobs in one Cluster + Resolver, and each job can have pods belong with different label + selectors. For example, a sample mapping might be + ``` + {'worker': ['job-name=worker-cluster-a', 'job-name=worker-cluster-b'], + 'ps': ['job-name=ps-1', 'job-name=ps-2']} + ``` + tf_server_port: The port the TensorFlow server is listening on. + rpc_layer: (Optional) The RPC layer TensorFlow should use to communicate + between tasks in Kubernetes. Defaults to 'grpc'. + override_client: The Kubernetes client (usually automatically retrieved + using `from kubernetes import client as k8sclient`). If you pass this + in, you are responsible for setting Kubernetes credentials manually. + + Raises: + ImportError: If the Kubernetes Python client is not installed and no + `override_client` is passed in. + RuntimeError: If autoresolve_task is not a boolean or a callable. + """ + if _KUBERNETES_API_CLIENT_INSTALLED: + k8sconfig.load_kube_config() + + if not job_to_label_mapping: + job_to_label_mapping = {'worker': ['job-name=tensorflow']} + + if not override_client and not _KUBERNETES_API_CLIENT_INSTALLED: + raise ImportError('The Kubernetes Python client must be installed before' + 'using the Kubernetes Cluster Resolver. To install the' + 'Kubernetes Python client, run `pip install ' + 'kubernetes` on your command line.') + + self._job_to_label_mapping = job_to_label_mapping + self._tf_server_port = tf_server_port + self._override_client = override_client + + self.task_type = None + self.task_index = None + self.rpc_layer = rpc_layer + + def master(self, task_type=None, task_index=None, rpc_layer=None): + """Returns the master address to use when creating a session. + + You must have set the task_type and task_index object properties before + calling this function, or pass in the `task_type` and `task_index` + parameters when using this function. If you do both, the function parameters + will override the object properties. + + Args: + task_type: (Optional) The type of the TensorFlow task of the master. + task_index: (Optional) The index of the TensorFlow task of the master. + rpc_layer: (Optional) The RPC protocol for the given cluster. + + Returns: + The name or URL of the session master. + """ + if task_type is not None and task_index is not None: + return format_master_url( + self.cluster_spec().task_address(task_type, task_index), + rpc_layer or self.rpc_layer) + + if self.task_type is not None and self.task_index is not None: + return format_master_url( + self.cluster_spec().task_address(self.task_type, self.task_index), + rpc_layer or self.rpc_layer) + + return '' + + def cluster_spec(self): + """Returns a ClusterSpec object based on the latest info from Kubernetes. + + We retrieve the information from the Kubernetes master every time this + method is called. + + Returns: + A ClusterSpec containing host information returned from Kubernetes. + + Raises: + RuntimeError: If any of the pods returned by the master is not in the + `Running` phase. + """ + if not self._override_client: + k8sconfig.load_kube_config() + + client = self._override_client or k8sclient.CoreV1Api() + cluster_map = {} + + for tf_job in self._job_to_label_mapping: + all_pods = [] + for selector in self._job_to_label_mapping[tf_job]: + ret = client.list_pod_for_all_namespaces(label_selector=selector) + selected_pods = [] + + # Sort the list by the name to make sure it doesn't change call to call. + for pod in sorted(ret.items, key=lambda x: x.metadata.name): + if pod.status.phase == 'Running': + selected_pods.append( + '%s:%s' % (pod.status.host_ip, self._tf_server_port)) + else: + raise RuntimeError('Pod "%s" is not running; phase: "%s"' % + (pod.metadata.name, pod.status.phase)) + all_pods.extend(selected_pods) + cluster_map[tf_job] = all_pods + + return server_lib.ClusterSpec(cluster_map) + + @property + def environment(self): + """Returns the current environment which TensorFlow is running in. + + For users in the Cloud environment, the environment property is always an + empty string, and Google users will not use this ClusterResolver for running + on internal systems. + """ + return '' + + def num_accelerators_per_worker(self, session_config=None): + local_devices = device_lib.list_local_devices(session_config) + return len([d for d in local_devices if d.device_type == 'GPU']) diff --git a/tensorflow/contrib/cluster_resolver/python/training/kubernetes_cluster_resolver_test.py b/tensorflow/python/distribute/cluster_resolver/kubernetes_cluster_resolver_test.py similarity index 87% rename from tensorflow/contrib/cluster_resolver/python/training/kubernetes_cluster_resolver_test.py rename to tensorflow/python/distribute/cluster_resolver/kubernetes_cluster_resolver_test.py index fbb26e803d..a9750fa60b 100644 --- a/tensorflow/contrib/cluster_resolver/python/training/kubernetes_cluster_resolver_test.py +++ b/tensorflow/python/distribute/cluster_resolver/kubernetes_cluster_resolver_test.py @@ -18,7 +18,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -from tensorflow.contrib.cluster_resolver.python.training import KubernetesClusterResolver +from tensorflow.python.distribute.cluster_resolver import KubernetesClusterResolver from tensorflow.python.platform import test from tensorflow.python.training import server_lib @@ -109,6 +109,23 @@ class KubernetesClusterResolverTest(test.TestCase): """ self._verifyClusterSpecEquality(actual_cluster_spec, str(expected_proto)) + def testGetMasterWithOverrideParameters(self): + ret = _create_pod_list( + ('worker-0', 'Running', '10.1.2.3'), + ('worker-1', 'Running', '10.1.2.4'), + ('worker-2', 'Running', '10.1.2.5')) + + cluster_resolver = KubernetesClusterResolver( + override_client=_mock_kubernetes_client( + {'job-name=tensorflow': ret})) + cluster_resolver.task_type = 'worker' + cluster_resolver.task_index = 0 + self.assertEqual(cluster_resolver.task_type, 'worker') + self.assertEqual(cluster_resolver.task_index, 0) + self.assertEqual(cluster_resolver.master(), 'grpc://10.1.2.3:8470') + self.assertEqual(cluster_resolver.master('worker', 2), + 'grpc://10.1.2.5:8470') + def testNonRunningPod(self): ret = _create_pod_list(('tensorflow-abc123', 'Failed', '10.1.2.3'),) diff --git a/tensorflow/python/distribute/cluster_resolver/slurm_cluster_resolver.py b/tensorflow/python/distribute/cluster_resolver/slurm_cluster_resolver.py new file mode 100644 index 0000000000..fd3c6d6a18 --- /dev/null +++ b/tensorflow/python/distribute/cluster_resolver/slurm_cluster_resolver.py @@ -0,0 +1,226 @@ +# Copyright 2018 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. +# ============================================================================== +"""Implementation of Cluster Resolvers for Slurm workload manager.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import collections +import os +import subprocess + +from tensorflow.python.distribute.cluster_resolver.cluster_resolver import ClusterResolver +from tensorflow.python.training.server_lib import ClusterSpec + + +class SlurmClusterResolver(ClusterResolver): + """Cluster Resolver for system with Slurm workload manager. + + This is an implementation of cluster resolvers for Slurm clusters. This allows + the specification of jobs and task counts, number of tasks per node, number of + GPUs on each node and number of GPUs for each task, It retrieves system + attributes by Slurm environment variables, resolves allocated computing node + names, construct a cluster and return a Cluster Resolver object which an be + use for distributed TensorFlow. + """ + + def _resolve_hostnames(self): + """Resolve host names of nodes allocated in current jobs. + + Returns: + A list of node names as strings. + """ + hostlist = (subprocess.check_output(['scontrol', 'show', 'hostname']). + decode('utf-8').strip().split('\n')) + return hostlist + + def __init__(self, + jobs, + port_base=8888, + gpus_per_node=1, + gpus_per_task=1, + tasks_per_node=None, + auto_set_gpu=True, + rpc_layer='grpc'): + """Creates a new SlurmClusterResolver object. + + This takes in parameters and creates a SlurmClusterResolver object. It uses + those parameters to check which nodes will processes reside and resolves + their hostnames. With the number of the GPUs on each node and number of GPUs + for each task it offsets the port number for each processes and allocate + GPUs to tasks by setting environment variables. The resolver currently + supports homogeneous tasks and default Slurm process allocation. + + Args: + jobs: Dictionary with job names as key and number of tasks in the job as + value + port_base: The first port number to start with for processes on a node. + gpus_per_node: Number of GPUs available on each node. + gpus_per_task: Number of GPUs to be used for each task. + tasks_per_node: Number of tasks to run on each node, if not set defaults + to Slurm's output environment variable SLURM_NTASKS_PER_NODE. + auto_set_gpu: Set the visible CUDA devices automatically while resolving + the cluster by setting CUDA_VISIBLE_DEVICES environment variable. + Defaults to True. + rpc_layer: (Optional) The protocol TensorFlow uses to communicate between + nodes. Defaults to 'grpc'. + + Returns: + A ClusterResolver object which can be used with distributed TensorFlow. + + Raises: + RuntimeError: If requested more GPUs per node then available or requested + more tasks then assigned tasks. + """ + + # check if launched by mpirun + if 'OMPI_COMM_WORLD_RANK' in os.environ: + self._rank = int(os.environ['OMPI_COMM_WORLD_RANK']) + num_tasks = int(os.environ['OMPI_COMM_WORLD_SIZE']) + else: + self._rank = int(os.environ['SLURM_PROCID']) + num_tasks = int(os.environ['SLURM_NTASKS']) + + self._jobs = collections.OrderedDict(sorted(jobs.items())) + self._port_base = port_base + + # user specification overrides SLURM specification + if tasks_per_node is not None: + self._tasks_per_node = tasks_per_node + elif tasks_per_node is None and 'SLURM_NTASKS_PER_NODE' in os.environ: + self._tasks_per_node = int(os.environ['SLURM_NTASKS_PER_NODE']) + else: + raise RuntimeError('Neither `tasks_per_node` or ' + 'SLURM_NTASKS_PER_NODE is set.') + + self._gpus_per_node = gpus_per_node + self._gpus_per_task = gpus_per_task + + self._auto_set_gpu = auto_set_gpu + self.task_type = None + self.task_index = None + self.rpc_layer = rpc_layer + + self._gpu_allocation = [] + self._cluster_allocation = {} + + if self._tasks_per_node * self._gpus_per_task > self._gpus_per_node: + raise RuntimeError('Requested more GPUs per node then available.') + + if sum(self._jobs.values()) != num_tasks: + raise RuntimeError('Requested more tasks then assigned tasks.') + + def cluster_spec(self): + """Returns a ClusterSpec object based on the latest instance group info. + + This returns a ClusterSpec object for use based on information from the + specified initialization parameters and Slurm environment variables. The + cluster specification is resolved each time this function is called. The + resolver extract hostnames of nodes by scontrol and pack tasks in that + order until a node a has number of tasks that is equal to specification. + GPUs on nodes are allocated to tasks by specification through setting + CUDA_VISIBLE_DEVICES environment variable. + + Returns: + A ClusterSpec containing host information retrieved from Slurm's + environment variables. + """ + hostlist = self._resolve_hostnames() + + task_list = [] + self._gpu_allocation = [] + self._cluster_allocation = {} + + for host in hostlist: + for port_offset, gpu_offset in zip( + range(self._tasks_per_node), + range(0, self._gpus_per_node, self._gpus_per_task)): + + host_addr = '%s:%d' % (host, self._port_base + port_offset) + task_list.append(host_addr) + gpu_id_list = [] + + for gpu_id in range(gpu_offset, gpu_offset + self._gpus_per_task): + gpu_id_list.append(str(gpu_id)) + + self._gpu_allocation.append(','.join(gpu_id_list)) + + cluster_rank_offset_start = 0 + cluster_rank_offset_end = 0 + + for task_type, num_tasks in self._jobs.items(): + cluster_rank_offset_end = cluster_rank_offset_start + num_tasks + + self._cluster_allocation[task_type] = ( + task_list[cluster_rank_offset_start:cluster_rank_offset_end]) + + if cluster_rank_offset_start <= self._rank < cluster_rank_offset_end: + self.task_type = task_type + self.task_index = self._rank - cluster_rank_offset_start + + cluster_rank_offset_start = cluster_rank_offset_end + + if self._auto_set_gpu is True: + os.environ['CUDA_VISIBLE_DEVICES'] = self._gpu_allocation[self._rank] + + return ClusterSpec(self._cluster_allocation) + + def get_task_info(self): + """Returns job name and task_index for the process which calls this. + + This returns the job name and task index for the process which calls this + function according to its rank and cluster specification. The job name and + task index are set after a cluster is constructed by cluster_spec otherwise + defaults to None. + + Returns: + A string specifying job name the process belongs to and an integner + specifying the task index the process belongs to in that job. + """ + return self.task_type, self.task_index + + def master(self, task_type=None, task_index=None, rpc_layer=None): + """Returns the master string for connecting to a TensorFlow master. + + Args: + task_type: (Optional) Overrides the default auto-selected task type. + task_index: (Optional) Overrides the default auto-slected task index. + rpc_layer: (Optional) Overrides the default RPC protocol TensorFlow uses + to communicate across nodes. + + Returns: + A connection string for connecting to a TensorFlow master. + """ + task_type = task_type if task_type is not None else self.task_type + task_index = task_index if task_index is not None else self.task_index + rpc_layer = rpc_layer or self.rpc_layer + master = self.cluster_spec().task_address(task_type, task_index) + + return '%s://%s' % (rpc_layer, master) if rpc_layer else master + + @property + def environment(self): + """Returns the current environment which TensorFlow is running in. + + For users in the Slurm environment, the environment property is always an + empty string, and Google users will not use this ClusterResolver for running + on internal systems. + """ + return '' + + def num_accelerators_per_worker(self, session_config=None): + del session_config # Unused, since this is set in __init__ manually. + return self._gpus_per_node diff --git a/tensorflow/contrib/cluster_resolver/python/training/slurm_cluster_resolver_test.py b/tensorflow/python/distribute/cluster_resolver/slurm_cluster_resolver_test.py similarity index 85% rename from tensorflow/contrib/cluster_resolver/python/training/slurm_cluster_resolver_test.py rename to tensorflow/python/distribute/cluster_resolver/slurm_cluster_resolver_test.py index 9aa7df745e..076539d16f 100644 --- a/tensorflow/contrib/cluster_resolver/python/training/slurm_cluster_resolver_test.py +++ b/tensorflow/python/distribute/cluster_resolver/slurm_cluster_resolver_test.py @@ -20,7 +20,7 @@ from __future__ import print_function import os -from tensorflow.contrib.cluster_resolver.python.training.slurm_cluster_resolver import SlurmClusterResolver +from tensorflow.python.distribute.cluster_resolver import SlurmClusterResolver from tensorflow.python.platform import test from tensorflow.python.training import server_lib @@ -67,6 +67,31 @@ class SlurmClusterResolverTest(test.TestCase): """ self._verifyClusterSpecEquality(actual_cluster_spec, expected_proto) + @mock.patch.dict(os.environ, {'SLURM_PROCID': '0', 'SLURM_NTASKS': '3'}) + @mock.patch.object(SlurmClusterResolver, '_resolve_hostnames', + mock_resolve_hostnames_output) + def testSimpleMasterRetrieval(self): + slurm_cluster_resolver = SlurmClusterResolver( + jobs={ + 'ps': 1, + 'worker': 2 + }, + port_base=8888, + tasks_per_node=1, + gpus_per_node=1, + gpus_per_task=1, + auto_set_gpu=False) + + slurm_cluster_resolver.task_type = 'worker' + slurm_cluster_resolver.task_index = 1 + self.assertEqual(slurm_cluster_resolver.master(), 'grpc://t02n43:8888') + + slurm_cluster_resolver.rpc_layer = 'ab' + self.assertEqual(slurm_cluster_resolver.master('ps', 0), 'ab://t02n13:8888') + self.assertEqual( + slurm_cluster_resolver.master('ps', 0, rpc_layer='test'), + 'test://t02n13:8888') + @mock.patch.dict(os.environ, { 'SLURM_PROCID': '0', 'SLURM_NTASKS': '3', diff --git a/tensorflow/python/distribute/cluster_resolver/tfconfig_cluster_resolver.py b/tensorflow/python/distribute/cluster_resolver/tfconfig_cluster_resolver.py new file mode 100644 index 0000000000..a3246e77f4 --- /dev/null +++ b/tensorflow/python/distribute/cluster_resolver/tfconfig_cluster_resolver.py @@ -0,0 +1,171 @@ +# Copyright 2018 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. +# ============================================================================== +"""Implementation of Cluster Resolvers for TF_CONFIG Environment Variables.""" + + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import json +import os + +from tensorflow.python.distribute.cluster_resolver.cluster_resolver import ClusterResolver +from tensorflow.python.training.server_lib import ClusterSpec + +_TF_CONFIG_ENV = 'TF_CONFIG' +_SESSION_MASTER_KEY = 'session_master' +_RPC_LAYER_KEY = 'rpc_layer' +_TASK_KEY = 'task' + + +def format_master_url(master, rpc_layer=None): + if rpc_layer: + return '%s://%s' % (rpc_layer, master) + else: + return master + + +def _load_tf_config(): + return json.loads(os.environ.get(_TF_CONFIG_ENV, '{}')) + + +def _get_value_in_tfconfig(key, default=None): + tf_config = _load_tf_config() + return tf_config[key] if key in tf_config else default + + +class TFConfigClusterResolver(ClusterResolver): + """Implementation of a ClusterResolver which reads the TF_CONFIG EnvVar.""" + + def __init__(self, + task_type=None, + task_index=None, + rpc_layer=None, + environment=None, + num_accelerators_per_worker=0): + """Creates a new TFConfigClusterResolver. + + Args: + task_type: (String, optional) Overrides the task type specified in the + TF_CONFIG environment variable. + task_index: (Integer, optional) Overrides the task index specified in the + TF_CONFIG environment variable. + rpc_layer: (String, optional) Overrides the rpc layer TensorFlow uses. + environment: (String, optional) Overrides the environment TensorFlow + operates in. + num_accelerators_per_worker: (Integer, optional) Specifies the number of + accelerators (e.g. GPUs, TPUs, others) that each node has. + """ + + self._task_type = task_type + self._task_index = task_index + self._rpc_layer = rpc_layer + self._environment = environment + self._num_accelerators_per_worker = num_accelerators_per_worker + + @property + def task_type(self): + if self._task_type is None: + task_info = _get_value_in_tfconfig(_TASK_KEY, {}) + return task_info['type'] if 'type' in task_info else None + else: + return self._task_type + + @property + def task_index(self): + if self._task_type is None: + task_info = _get_value_in_tfconfig(_TASK_KEY, {}) + return task_info['index'] if 'index' in task_info else None + else: + return self._task_index + + @task_type.setter + def task_type(self, task_type): + self._task_type = task_type + + @task_index.setter + def task_index(self, task_index): + self._task_index = task_index + + @property + def environment(self): + return self._environment + + @property + def rpc_layer(self): + if self._rpc_layer is None: + return _get_value_in_tfconfig(_RPC_LAYER_KEY) + else: + return self._rpc_layer + + @rpc_layer.setter + def rpc_layer(self, rpc_layer): + self._rpc_layer = rpc_layer + + def num_accelerators_per_worker(self, session_config=None): + # TODO(frankchn): Connect to server (w/ session_config) in the future. + del session_config # Unused, we do not connect to another server here. + return self._num_accelerators_per_worker + + def cluster_spec(self): + """Returns a ClusterSpec based on the TF_CONFIG environment variable. + + Returns: + A ClusterSpec with information from the TF_CONFIG environment variable. + """ + tf_config = _load_tf_config() + if 'cluster' not in tf_config: + return ClusterSpec({}) + return ClusterSpec(tf_config['cluster']) + + def master(self, task_type=None, task_index=None, rpc_layer=None): + """Returns the master address to use when creating a TensorFlow session. + + Args: + task_type: (String, optional) Overrides and sets the task_type of the + master. + task_index: (Integer, optional) Overrides and sets the task id of the + master. + rpc_layer: (String, optional) Overrides and sets the protocol over which + TensorFlow nodes communicate with each other. + + Returns: + The address of the master. + + Raises: + RuntimeError: If the task_type or task_id is not specified and the + `TF_CONFIG` environment variable does not contain a task section. + """ + + # If `session_master` is set, just use that. + session_master = _get_value_in_tfconfig(_SESSION_MASTER_KEY) + if session_master is not None: + return session_master + + # Return an empty string if we are the only job in the ClusterSpec. + cluster_spec = self.cluster_spec() + if (not cluster_spec.jobs or + (len(cluster_spec.jobs) == 1 and + len(cluster_spec.job_tasks(cluster_spec.jobs[0])) == 1)): + return '' + + # We try to auto-detect the task type and id, but uses the user-supplied one + # where available + task_type = task_type if task_type is not None else self.task_type + task_index = task_index if task_index is not None else self.task_index + + return format_master_url(cluster_spec.task_address(task_type, task_index), + self.rpc_layer) diff --git a/tensorflow/contrib/cluster_resolver/python/training/tfconfig_cluster_resolver_test.py b/tensorflow/python/distribute/cluster_resolver/tfconfig_cluster_resolver_test.py similarity index 72% rename from tensorflow/contrib/cluster_resolver/python/training/tfconfig_cluster_resolver_test.py rename to tensorflow/python/distribute/cluster_resolver/tfconfig_cluster_resolver_test.py index 468161d2aa..c20e51bc0b 100644 --- a/tensorflow/contrib/cluster_resolver/python/training/tfconfig_cluster_resolver_test.py +++ b/tensorflow/python/distribute/cluster_resolver/tfconfig_cluster_resolver_test.py @@ -20,7 +20,7 @@ from __future__ import print_function import os -from tensorflow.contrib.cluster_resolver.python.training.tfconfig_cluster_resolver import TFConfigClusterResolver +from tensorflow.python.distribute.cluster_resolver import TFConfigClusterResolver from tensorflow.python.platform import test from tensorflow.python.training import server_lib @@ -133,6 +133,58 @@ class TFConfigClusterResolverTest(test.TestCase): cluster_resolver = TFConfigClusterResolver() self.assertEqual('grpc://ps0:2222', cluster_resolver.master()) + def testTaskTypeIndexRpcRead(self): + os.environ['TF_CONFIG'] = """ + { + "cluster": { + "ps": ["ps0:2222", "ps1:2222"], + "worker": ["worker0:2222", "worker1:2222", "worker2:2222"] + }, + "rpc_layer": "grpc", + "task": { + "type": "ps", + "index": 0 + } + } + """ + + cluster_resolver = TFConfigClusterResolver() + self.assertEqual('ps', cluster_resolver.task_type) + self.assertEqual(0, cluster_resolver.task_index) + self.assertEqual('grpc', cluster_resolver.rpc_layer) + + def testParameterOverrides(self): + os.environ['TF_CONFIG'] = """ + { + "cluster": { + "ps": ["ps0:2222", "ps1:2222"], + "worker": ["worker0:2222", "worker1:2222", "worker2:2222"] + }, + "rpc_layer": "grpc", + "task": { + "type": "ps", + "index": 1 + } + } + """ + + cluster_resolver = TFConfigClusterResolver(task_type='ps', task_index=0, + num_accelerators_per_worker=8) + + self.assertEqual('grpc://ps0:2222', cluster_resolver.master()) + self.assertEqual('ps', cluster_resolver.task_type) + self.assertEqual(0, cluster_resolver.task_index) + self.assertEqual(8, cluster_resolver.num_accelerators_per_worker()) + + cluster_resolver.task_type = 'worker' + cluster_resolver.task_index = 1 + cluster_resolver.rpc_layer = 'test' + + self.assertEqual('test://worker1:2222', cluster_resolver.master()) + self.assertEqual('worker', cluster_resolver.task_type) + self.assertEqual(1, cluster_resolver.task_index) + self.assertEqual('test', cluster_resolver.rpc_layer) + def testZeroItemsInClusterSpecMasterRead(self): os.environ['TF_CONFIG'] = """ {} diff --git a/tensorflow/python/distribute/cluster_resolver/tpu_cluster_resolver.py b/tensorflow/python/distribute/cluster_resolver/tpu_cluster_resolver.py new file mode 100644 index 0000000000..1956bd75a8 --- /dev/null +++ b/tensorflow/python/distribute/cluster_resolver/tpu_cluster_resolver.py @@ -0,0 +1,423 @@ +# 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. +# ============================================================================== +"""Implementation of Cluster Resolvers for Cloud TPUs.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os + +from six.moves.urllib.request import Request +from six.moves.urllib.request import urlopen + +from tensorflow.python.distribute.cluster_resolver.cluster_resolver import ClusterResolver +from tensorflow.python.distribute.cluster_resolver.cluster_resolver import format_master_url +from tensorflow.python.training import server_lib +from tensorflow.python.util import compat + +_GOOGLE_API_CLIENT_INSTALLED = True +try: + from googleapiclient import discovery # pylint: disable=g-import-not-at-top + from oauth2client.client import GoogleCredentials # pylint: disable=g-import-not-at-top +except ImportError: + _GOOGLE_API_CLIENT_INSTALLED = False + + +_GKE_ENV_VARIABLE = 'KUBE_GOOGLE_CLOUD_TPU_ENDPOINTS' +_ENDPOINTS_SEPARATOR = ',' +_DEFAULT_ENV_VARIABLE = 'TPU_NAME' +_DISCOVERY_SERVICE_URL_ENV_VARIABLE = 'TPU_API_DISCOVERY_URL' + + +class TPUClusterResolver(ClusterResolver): + """Cluster Resolver for Google Cloud TPUs. + + This is an implementation of cluster resolvers for the Google Cloud TPU + service. As Cloud TPUs are in alpha, you will need to specify a API definition + file for this to consume, in addition to a list of Cloud TPUs in your Google + Cloud Platform project. + """ + + def _tpuService(self): + """Creates a new Cloud TPU API object. + + This works around an issue where the underlying HTTP connection sometimes + times out when the script has been running for too long. Other methods in + this object calls this method to get a new API object whenever they need + to communicate with the Cloud API. + + Returns: + A Google Cloud TPU API object. + """ + if self._service: + return self._service + + credentials = self._credentials + if credentials is None or credentials == 'default': + credentials = GoogleCredentials.get_application_default() + + if self._discovery_url: + return discovery.build( + 'tpu', 'v1alpha1', + credentials=credentials, + discoveryServiceUrl=self._discovery_url) + else: + return discovery.build( + 'tpu', 'v1alpha1', + credentials=credentials) + + def _requestComputeMetadata(self, path): + req = Request('http://metadata/computeMetadata/v1/%s' % path, + headers={'Metadata-Flavor': 'Google'}) + resp = urlopen(req) + return compat.as_bytes(resp.read()) + + def _shouldResolve(self): + if isinstance(self._should_resolve_override, bool): + return self._should_resolve_override + if (self._tpu == compat.as_bytes('') or + self._tpu == compat.as_bytes('local') or + self._tpu.startswith(compat.as_bytes('/bns')) or + self._tpu.startswith(compat.as_bytes('localhost:')) or + self._tpu.startswith(compat.as_bytes('grpc://'))): + return False + return True + + @staticmethod + def _inGke(): + """When running in GKE, the environment variable will be set.""" + return _GKE_ENV_VARIABLE in os.environ + + @staticmethod + def _gkeEndpoints(): + return os.environ[_GKE_ENV_VARIABLE] + + @staticmethod + def _envVarFallback(): + if _DEFAULT_ENV_VARIABLE in os.environ: + return os.environ[_DEFAULT_ENV_VARIABLE] + return None + + @staticmethod + def _environmentDiscoveryUrl(): + return os.environ.get(_DISCOVERY_SERVICE_URL_ENV_VARIABLE) + + def __init__(self, + tpu=None, + zone=None, + project=None, + job_name='worker', + coordinator_name=None, + coordinator_address=None, + credentials='default', + service=None, + discovery_url=None): + """Creates a new TPUClusterResolver object. + + The ClusterResolver will then use the parameters to query the Cloud TPU APIs + for the IP addresses and ports of each Cloud TPU listed. + + Args: + tpu: Either a string, or a list of strings corresponding to the TPUs to + use. If the single string is the empty string, the string 'local', or a + string that begins with 'grpc://' or '/bns', then it is assumed to not + correspond with a Cloud TPU and will instead be passed as the session + master and no ClusterSpec propagation will be done. + zone: Zone where the TPUs are located. If omitted or empty, we will assume + that the zone of the TPU is the same as the zone of the GCE VM, which we + will try to discover from the GCE metadata service. + project: Name of the GCP project containing Cloud TPUs. If omitted or + empty, we will try to discover the project name of the GCE VM from the + GCE metadata service. + job_name: Name of the TensorFlow job the TPUs belong to. + coordinator_name: The name to use for the coordinator. Set to None if the + coordinator should not be included in the computed ClusterSpec. + coordinator_address: The address of the coordinator (typically an ip:port + pair). If set to None, a TF server will be started. If coordinator_name + is None, a TF server will not be started even if coordinator_address is + None. + credentials: GCE Credentials. If None, then we use default credentials + from the oauth2client + service: The GCE API object returned by the googleapiclient.discovery + function. If you specify a custom service object, then the credentials + parameter will be ignored. + discovery_url: A URL template that points to the location of + the discovery service. It should have two parameters {api} and + {apiVersion} that when filled in produce an absolute URL to the + discovery document for that service. The environment variable + 'TPU_API_DISCOVERY_URL' will override this. + + Raises: + ImportError: If the googleapiclient is not installed. + ValueError: If no TPUs are specified. + """ + if isinstance(tpu, list): + if not tpu: + raise ValueError('At least one TPU must be specified.') + if len(tpu) != 1: + raise NotImplementedError( + 'Using multiple TPUs in a single session is not yet implemented') + tpu = tpu[0] + + in_gke = self._inGke() + # When using GKE with Cloud TPUs, the env variable will be set. + if tpu is None: + if in_gke: + tpu = self._gkeEndpoints() + else: + tpu = self._envVarFallback() + + if tpu is None: + raise ValueError('Please provide a TPU Name to connect to.') + + self._tpu = compat.as_bytes(tpu) # self._tpu is always bytes + + # By default the task_type is 'worker` and the task_index is 0 (which is the + # first worker in the task). + self.task_type = job_name + self.task_index = 0 + + if tpu.startswith('grpc://'): + # Cloud environment, where we are using GRPC to communicate to TPUs. + self._environment = '' + elif tpu == 'local' or not tpu: + # Google environment, where the TPU is attached to the host. + self._environment = 'google' + elif tpu.startswith('/bns'): + # Google environment, where we reach the TPU through BNS. + self._environment = 'google' + + # If TPU is in the Google environment or exists locally, we don't use any + # RPC layer. + if tpu.startswith('/bns') or tpu == 'local' or not tpu: + self.rpc_layer = None + else: + self.rpc_layer = 'grpc' + + # Setting this overrides the return value of self._shouldResolve() + self._should_resolve_override = None + + # We strip out the protocol if it is included, and override the + # shouldResolve function to never resolve. We are adding the protocol back + # in later in self.master(). + if self.rpc_layer is not None and tpu.startswith(self.rpc_layer + '://'): + tpu = tpu[len(self.rpc_layer + '://'):] + self._tpu = tpu + self._should_resolve_override = False + + # Whether we should actually attempt to contact Cloud APIs + should_resolve = self._shouldResolve() + + # We error out if we are in a non-Cloud environment which cannot talk to the + # Cloud APIs using the standard class and a special object is not passed in. + self._service = service + if (self._service is None and should_resolve and + not _GOOGLE_API_CLIENT_INSTALLED): + raise ImportError('googleapiclient and oauth2client must be installed ' + 'before using the TPU cluster resolver. Execute: ' + '`pip install --upgrade google-api-python-client` ' + 'and `pip install --upgrade oauth2client` to ' + 'install with pip.') + + # We save user-passed credentials, unless the user didn't pass in anything. + self._credentials = credentials + if (credentials == 'default' and should_resolve and + _GOOGLE_API_CLIENT_INSTALLED): + self._credentials = None + + # Automatically detect project and zone if unspecified. + if not project and should_resolve: + project = compat.as_str( + self._requestComputeMetadata('project/project-id')) + if not zone and should_resolve: + zone_path = compat.as_str(self._requestComputeMetadata('instance/zone')) + zone = zone_path.split('/')[-1] + self._project = project + self._zone = zone + + self._discovery_url = self._environmentDiscoveryUrl() or discovery_url + + self._coordinator_name = coordinator_name + if (coordinator_name and not coordinator_address and + (should_resolve or in_gke)): + self._start_local_server() + else: + self._coordinator_address = coordinator_address + + def master(self, task_type=None, task_index=None, rpc_layer=None): + """Get the Master string to be used for the session. + + In the normal case, this returns the grpc path (grpc://1.2.3.4:8470) of + first instance in the ClusterSpec returned by the cluster_spec function. + + If a non-TPU name is used when constructing a TPUClusterResolver, that will + be returned instead (e.g. If the tpus argument's value when constructing + this TPUClusterResolver was 'grpc://10.240.1.2:8470', + 'grpc://10.240.1.2:8470' will be returned). + + Args: + task_type: (Optional, string) The type of the TensorFlow task of the + master. + task_index: (Optional, integer) The index of the TensorFlow task of the + master. + rpc_layer: (Optional, string) The RPC protocol TensorFlow should use to + communicate with TPUs. + + Returns: + string, the connection string to use when creating a session. + + Raises: + ValueError: If none of the TPUs specified exists. + """ + if self._shouldResolve(): + # We are going to communicate with the Cloud TPU APIs to get a Cluster. + cluster_spec = self.cluster_spec() + if task_type is not None and task_index is not None: + # task_type and task_index is from the function parameter + master = cluster_spec.task_address(task_type, task_index) + elif self.task_type is not None and self.task_index is not None: + # task_type and task_index is from the object + master = cluster_spec.task_address(self.task_type, self.task_index) + else: + # by default we take the first item in the cluster with the right name + job_tasks = cluster_spec.job_tasks(self.task_type) + if not job_tasks: + raise ValueError('No TPUs with the specified names exist.') + master = job_tasks[0] + else: + if isinstance(self._tpu, (bytes, bytearray)): + master = self._tpu.split(compat.as_bytes(_ENDPOINTS_SEPARATOR))[0] + else: + master = self._tpu.split(_ENDPOINTS_SEPARATOR)[0] + return format_master_url(master, rpc_layer or self.rpc_layer) + + def get_master(self): + return self.master() + + def get_job_name(self): + if self._shouldResolve(): + return self.task_type + + def cluster_spec(self): + """Returns a ClusterSpec object based on the latest TPU information. + + We retrieve the information from the GCE APIs every time this method is + called. + + Returns: + A ClusterSpec containing host information returned from Cloud TPUs. + + Raises: + RuntimeError: If the provided TPU is not healthy. + """ + ############################################################################ + # There are 5 potential cases this code must handle: + # 1. [Normal case.] We should resolve the TPU name to a set of tasks, and + # a. Create a ClusterSpec that includes the coordinator job + # b. Create a ClusterSpec without the coordinator job. + # 2. [GKE / No API Access.] We should not resolve the TPU name to a set of + # tasks and + # a. Create a ClusterSpec with the coordinator + # b. Create a ClusterSpec without the coordinator + # 3. [Other (legacy non-gRPC).] We should return an empty ClusterSpec. + ############################################################################ + + if self._shouldResolve(): + # Case 1. + full_name = 'projects/%s/locations/%s/nodes/%s' % ( + self._project, self._zone, compat.as_text(self._tpu)) + service = self._tpuService() + request = service.projects().locations().nodes().get(name=full_name) + response = request.execute() + + if 'state' in response and response['state'] != 'READY': + raise RuntimeError('TPU "%s" is not yet ready; state: "%s"' % + (compat.as_text(self._tpu), response['state'])) + + if 'health' in response and response['health'] != 'HEALTHY': + raise RuntimeError('TPU "%s" is unhealthy: "%s"' % + (compat.as_text(self._tpu), response['health'])) + + if 'networkEndpoints' in response: + worker_list = [ + '%s:%s' % (endpoint['ipAddress'], endpoint['port']) + for endpoint in response['networkEndpoints'] + ] + else: + # Fall back to the deprecated response format + instance_url = '%s:%s' % (response['ipAddress'], response['port']) + worker_list = [instance_url] + + cluster_spec = {self.task_type: worker_list} + else: + if self.rpc_layer is None: + # Case 3. + return None + # Case 2. + tpus = [] + for tpu in self._tpu.split(_ENDPOINTS_SEPARATOR): + # We are working around the fact that GKE environment variable that is + # supplied to us has the protocol string embedded in it, but we want + # to strip it out for the ClusterSpec. + if (self.rpc_layer is not None and + tpu.startswith(self.rpc_layer + '://')): + tpus.append(tpu[len(self.rpc_layer + '://'):]) + else: + tpus.append(tpu) + cluster_spec = {self.task_type: tpus} + + if self._coordinator_address: + # {1, 2}.a + cluster_spec[self._coordinator_name] = [self._coordinator_address] + + return server_lib.ClusterSpec(cluster_spec) + + def num_accelerators_per_worker(self, session_config=None): + """Returns the number of TPU cores per worker. + + This defaults to 8 for all current TPU configurations, and we do not need + to query any remote systems for this. + + Args: + session_config: Unused. Not currently necessary to query anything as this + number is 8 for all TPU configurations. + """ + del session_config # Unused. Not necessary to query anything. + return 8 + + @property + def environment(self): + """Returns the current environment which TensorFlow is running in.""" + return self._environment + + def _start_local_server(self): + address = self._requestComputeMetadata('instance/network-interfaces/0/ip') + self._server = server_lib.Server( + { + 'local': ['0.0.0.0:0'] + }, protocol='grpc', config=None, start=True) + # self._server.target is of the form: grpc://ipaddress:port + target = compat.as_bytes(self._server.target) + splits = target.split(compat.as_bytes(':')) + assert len(splits) == 3, self._server.target + assert splits[0] == compat.as_bytes('grpc'), self._server.target + self._coordinator_port = compat.as_text(splits[2]) + self._coordinator_address = '%s:%s' % ( + address, compat.as_text(self._coordinator_port)) + + def __deepcopy__(self, memo): + # TODO(b/73668574): Remove this once RunConfig avoids performing deepcopy. + return self diff --git a/tensorflow/contrib/cluster_resolver/python/training/tpu_cluster_resolver_test.py b/tensorflow/python/distribute/cluster_resolver/tpu_cluster_resolver_test.py similarity index 86% rename from tensorflow/contrib/cluster_resolver/python/training/tpu_cluster_resolver_test.py rename to tensorflow/python/distribute/cluster_resolver/tpu_cluster_resolver_test.py index 478c82967b..0f22ede3d9 100644 --- a/tensorflow/contrib/cluster_resolver/python/training/tpu_cluster_resolver_test.py +++ b/tensorflow/python/distribute/cluster_resolver/tpu_cluster_resolver_test.py @@ -20,7 +20,7 @@ from __future__ import print_function import os -from tensorflow.contrib.cluster_resolver.python.training.tpu_cluster_resolver import TPUClusterResolver +from tensorflow.python.distribute.cluster_resolver import TPUClusterResolver from tensorflow.python.platform import test from tensorflow.python.training import server_lib from tensorflow.python.util import compat @@ -132,6 +132,7 @@ class TPUClusterResolverTest(test.TestCase): } """ % tpu_cluster_resolver._coordinator_port self._verifyClusterSpecEquality(actual_cluster_spec, str(expected_proto)) + self.assertEqual(tpu_cluster_resolver.master(), 'grpc://10.1.2.3:8470') @mock.patch.object(TPUClusterResolver, '_requestComputeMetadata', mock_request_compute_metadata) @@ -157,6 +158,7 @@ class TPUClusterResolverTest(test.TestCase): job { name: 'worker' tasks { key: 0 value: '10.1.2.3:8470' } } """ self._verifyClusterSpecEquality(actual_cluster_spec, expected_proto) + self.assertEqual(tpu_cluster_resolver.master(), 'grpc://10.1.2.3:8470') @mock.patch.object(TPUClusterResolver, '_requestComputeMetadata', mock_request_compute_metadata) @@ -226,6 +228,7 @@ class TPUClusterResolverTest(test.TestCase): job { name: 'worker' tasks { key: 0 value: '10.1.2.3:8470' } } """ self._verifyClusterSpecEquality(actual_cluster_spec, expected_proto) + self.assertEqual(tpu_cluster_resolver.master(), 'grpc://10.1.2.3:8470') def testNewNetworkEndpointFormat(self): tpu_map = { @@ -304,6 +307,7 @@ class TPUClusterResolverTest(test.TestCase): } """ % tpu_cluster_resolver._coordinator_port self._verifyClusterSpecEquality(actual_cluster_spec, str(expected_proto)) + self.assertEqual(tpu_cluster_resolver.master(), 'grpc://10.2.3.4:8470') def testPodResolutionNoCoordinator(self): tpu_map = { @@ -350,6 +354,7 @@ class TPUClusterResolverTest(test.TestCase): } """ self._verifyClusterSpecEquality(actual_cluster_spec, expected_proto) + self.assertEqual(tpu_cluster_resolver.master(), 'grpc://10.2.3.4:8470') def testGetMasterNoEntries(self): tpu_map = {} @@ -464,5 +469,62 @@ class TPUClusterResolverTest(test.TestCase): self.assertEqual('https://{api}.internal/{apiVersion}', TPUClusterResolver._environmentDiscoveryUrl()) + def testEnvironmentAndRpcDetectionForGoogle(self): + tpu_cluster_resolver = TPUClusterResolver(tpu='/bns/ab/cd/ef') + self.assertEqual(tpu_cluster_resolver.environment, 'google') + self.assertEqual(tpu_cluster_resolver.rpc_layer, None) + + def testEnvironmentAndRpcDetectionForGrpcString(self): + tpu_cluster_resolver = TPUClusterResolver(tpu='grpc://10.1.2.3:8470') + self.assertEqual(tpu_cluster_resolver.environment, '') + self.assertEqual(tpu_cluster_resolver.rpc_layer, 'grpc') + self.assertEqual(tpu_cluster_resolver.master(), 'grpc://10.1.2.3:8470') + + def testOverrideTaskTypeAndIndexAndGetMaster(self): + tpu_map = { + 'projects/test-project/locations/us-central1-c/nodes/test-tpu-1': { + 'health': + 'HEALTHY', + 'networkEndpoints': [ + { + 'ipAddress': '10.2.3.4', + 'port': 8470, + }, + { + 'ipAddress': '10.2.3.5', + 'port': 8470, + }, + { + 'ipAddress': '10.2.3.6', + 'port': 8470, + }, + { + 'ipAddress': '10.2.3.7', + 'port': 8470, + }, + ] + } + } + + tpu_cluster_resolver = TPUClusterResolver( + project='test-project', + zone='us-central1-c', + tpu='test-tpu-1', + coordinator_name=None, + credentials=None, + service=self.mock_service_client(tpu_map=tpu_map)) + + self.assertEqual(tpu_cluster_resolver.master(), 'grpc://10.2.3.4:8470') + + tpu_cluster_resolver.task_type = 'worker' + tpu_cluster_resolver.task_index = 3 + self.assertEqual(tpu_cluster_resolver.master(), 'grpc://10.2.3.7:8470') + + self.assertEqual( + tpu_cluster_resolver.master( + task_type='worker', task_index=2, rpc_layer='test'), + 'test://10.2.3.6:8470') + + if __name__ == '__main__': test.main() diff --git a/tensorflow/contrib/distribute/python/cross_tower_ops.py b/tensorflow/python/distribute/cross_device_ops.py similarity index 86% rename from tensorflow/contrib/distribute/python/cross_tower_ops.py rename to tensorflow/python/distribute/cross_device_ops.py index b5b349aa64..a88ed62533 100644 --- a/tensorflow/contrib/distribute/python/cross_tower_ops.py +++ b/tensorflow/python/distribute/cross_device_ops.py @@ -21,17 +21,17 @@ from __future__ import print_function import collections import six -from tensorflow.contrib.distribute.python import cross_tower_utils -from tensorflow.contrib.distribute.python import values as value_lib from tensorflow.python.client import device_lib +from tensorflow.python.distribute import cross_device_utils +from tensorflow.python.distribute import device_util +from tensorflow.python.distribute import reduce_util +from tensorflow.python.distribute import values as value_lib from tensorflow.python.eager import context from tensorflow.python.framework import ops from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import resource_variable_ops -from tensorflow.python.ops import variable_scope as vs from tensorflow.python.platform import tf_logging as logging -from tensorflow.python.training import device_util def check_destinations(destinations): @@ -103,10 +103,10 @@ def _validate_value_destination_pairs(value_destination_pairs): # pylint: disable=g-missing-docstring if not value_destination_pairs: return False if not isinstance(value_destination_pairs, (list, tuple)): return False - if not all([isinstance(pair, tuple) for pair in value_destination_pairs]): + if not all(isinstance(pair, tuple) for pair in value_destination_pairs): return False - if not all([isinstance(v[0], value_lib.PerReplica) - for v in value_destination_pairs]): + if not all(isinstance(v[0], value_lib.PerReplica) + for v in value_destination_pairs): return False return True @@ -132,10 +132,10 @@ def _devices_match(left, right): def _all_devices_match(value_destination_pairs): - if not all([_devices_match(v, d) for v, d in value_destination_pairs]): + if not all(_devices_match(v, d) for v, d in value_destination_pairs): return False - if not all([_devices_match(v, value_destination_pairs[0][0]) - for v, _ in value_destination_pairs[1:]]): + if not all(_devices_match(v, value_destination_pairs[0][0]) + for v, _ in value_destination_pairs[1:]): return False return True @@ -144,13 +144,13 @@ def _simple_broadcast(value, destinations): index = {} devices = get_devices_from(destinations) for d in devices: - index[d] = cross_tower_utils.copy_tensor_or_indexed_slices_to_device( + index[d] = cross_device_utils.copy_tensor_or_indexed_slices_to_device( value, d) return value_lib.Mirrored(index) def _simple_reduce(per_replica_value, reduce_to_device, accumulation_fn, - aggregation): + reduce_op): # pylint: disable=g-missing-docstring all_values = [] count = 0 @@ -162,14 +162,13 @@ def _simple_reduce(per_replica_value, reduce_to_device, accumulation_fn, with ops.device(reduce_to_device): with context.context().device_policy(context.DEVICE_PLACEMENT_SILENT): - reduced = cross_tower_utils.aggregate_tensors_or_indexed_slices( + reduced = cross_device_utils.aggregate_tensors_or_indexed_slices( all_values, accumulation_fn) - if aggregation == vs.VariableAggregation.MEAN: - reduced = cross_tower_utils.divide_by_n_tensors_or_indexed_slices( + if reduce_op == reduce_util.ReduceOp.MEAN: + reduced = cross_device_utils.divide_by_n_tensors_or_indexed_slices( reduced, count) - elif aggregation != vs.VariableAggregation.SUM: - raise ValueError("`aggregation` must be VariableAggregation.SUM " - "or VariableAggregation.MEAN.") + elif reduce_op != reduce_util.ReduceOp.SUM: + raise ValueError("`reduce_op` must be Reduce.SUM or Reduce.MEAN.") return reduced @@ -179,15 +178,15 @@ class CrossDeviceOps(object): def __init__(self): pass - def reduce(self, aggregation, per_replica_value, destinations): + def reduce(self, reduce_op, per_replica_value, destinations): """Reduce `per_replica_value` to `destinations`. - It runs the reduction operation defined by `aggregation` and put the + It runs the reduction operation defined by `reduce_op` and put the result on `destinations`. Args: - aggregation: Indicates how a variable will be aggregated. Accepted values - are `tf.VariableAggregation.SUM`, `tf.VariableAggregation.MEAN`. + reduce_op: Indicates how per_replica_value will be reduced. Accepted + values are `tf.distribute.ReduceOp.SUM`, `tf.distribute.ReduceOp.MEAN`. per_replica_value: a PerReplica object or a tensor with device set. destinations: the reduction destinations. @@ -201,17 +200,17 @@ class CrossDeviceOps(object): per_replica_value = _make_tensor_into_per_replica(per_replica_value) validate_destinations(destinations) - return self._reduce(aggregation, per_replica_value, destinations) + return self._reduce(reduce_op, per_replica_value, destinations) - def batch_reduce(self, aggregation, value_destination_pairs): + def batch_reduce(self, reduce_op, value_destination_pairs): """Reduce PerReplica objects in a batch. Reduce each first element in `value_destination_pairs` to each second element which indicates the destinations. Args: - aggregation: Indicates how a variable will be aggregated. Accepted values - are `tf.VariableAggregation.SUM`, `tf.VariableAggregation.MEAN`. + reduce_op: Indicates how per_replica_value will be reduced. Accepted + values are `tf.distribute.ReduceOp.SUM`, `tf.distribute.ReduceOp.MEAN`. value_destination_pairs: a list or a tuple of tuples of PerReplica objects (or tensors with device set if there is one device) and destinations. @@ -231,7 +230,7 @@ class CrossDeviceOps(object): for _, d in value_destination_pairs: validate_destinations(d) - return self._batch_reduce(aggregation, value_destination_pairs) + return self._batch_reduce(reduce_op, value_destination_pairs) def broadcast(self, tensor, destinations): """Broadcast the `tensor` to destinations. @@ -246,11 +245,11 @@ class CrossDeviceOps(object): validate_destinations(destinations) return self._broadcast(tensor, destinations) - def _reduce(self, aggregation, per_replica_value, destinations): + def _reduce(self, reduce_op, per_replica_value, destinations): raise NotImplementedError( "_reduce method must be implemented in descendants.") - def _batch_reduce(self, aggregation, value_destination_pairs): + def _batch_reduce(self, reduce_op, value_destination_pairs): raise NotImplementedError( "_batch_reduce method must be implemented in descendants.") @@ -276,19 +275,19 @@ class ReductionToOneDeviceCrossDeviceOps(CrossDeviceOps): self.accumulation_fn = accumulation_fn super(ReductionToOneDeviceCrossDeviceOps, self).__init__() - def _reduce(self, aggregation, per_replica_value, destinations): + def _reduce(self, reduce_op, per_replica_value, destinations): if check_destinations(destinations): devices = get_devices_from(destinations) else: devices = get_devices_from(per_replica_value) reduce_to_device = self.reduce_to_device or devices[0] reduced = _simple_reduce(per_replica_value, reduce_to_device, - self.accumulation_fn, aggregation) + self.accumulation_fn, reduce_op) return self.broadcast(reduced, devices) - def _batch_reduce(self, aggregation, value_destination_pairs): + def _batch_reduce(self, reduce_op, value_destination_pairs): return [ - self._reduce(aggregation, t, destinations=v) + self._reduce(reduce_op, t, destinations=v) for t, v in value_destination_pairs ] @@ -323,20 +322,20 @@ def _group_value_by_device(per_replica_values): def _ungroup_and_make_mirrored(grouped_reduced, destinations, - aggregation, + reduce_op, num_between_graph_workers=1): """Ungroup results from all-reduce and make Mirrored objects. Each all-reduce result will be divided by the number of destinations before - Mirrored objects are created if aggregation is "mean". + Mirrored objects are created if reduce_op is "mean". Args: grouped_reduced: a list of lists, each sublist has components for each device, paired with a None. It is the result from - cross_tower_utils.aggregate_gradients_using*. + cross_device_utils.aggregate_gradients_using*. destinations: a list of device strings for returned Mirrored objects. - aggregation: Indicates how a variable will be aggregated. Accepted values - are `tf.VariableAggregation.SUM`, `tf.VariableAggregation.MEAN`. + reduce_op: Indicates how values will be aggregated. Accepted values + are `tf.distribute.ReduceOp.SUM`, `tf.distribute.ReduceOp.MEAN`. num_between_graph_workers: number of workers in the between-graph replication. @@ -346,7 +345,7 @@ def _ungroup_and_make_mirrored(grouped_reduced, index = [{} for _ in range(len(grouped_reduced[0]))] for d, per_replica_reduced in enumerate(grouped_reduced): for i, (v, _) in enumerate(per_replica_reduced): - if aggregation == vs.VariableAggregation.MEAN: + if reduce_op == reduce_util.ReduceOp.MEAN: index[i][destinations[d]] = v / ( len(destinations) * num_between_graph_workers) else: @@ -402,7 +401,7 @@ class ConcatAndSplitPacker(object): # all gradient shapes are defined, we use another method to get the # total size. # TODO(yuefengz): move this logic to array_ops.size. - if all([g.shape.is_fully_defined() for g, _ in device_grads_and_vars]): + if all(g.shape.is_fully_defined() for g, _ in device_grads_and_vars): total_grad_size = sum( [g.shape.num_elements() for g, _ in device_grads_and_vars]) else: @@ -486,7 +485,7 @@ class AggregateSmallTensorPacker(object): """Aggregate small tensors.""" if (self.agg_small_grads_max_bytes > 0 and self.agg_small_grads_max_group > 0): - device_grads, self.packing = cross_tower_utils.pack_small_tensors( + device_grads, self.packing = cross_device_utils.pack_small_tensors( grouped_grads_and_vars, max_bytes=self.agg_small_grads_max_bytes, max_group=self.agg_small_grads_max_group) @@ -494,8 +493,8 @@ class AggregateSmallTensorPacker(object): def unpack(self, summed_device_grad_packs): """Reverse the aggregation process.""" - return cross_tower_utils.unpack_small_tensors(summed_device_grad_packs, - self.packing) + return cross_device_utils.unpack_small_tensors(summed_device_grad_packs, + self.packing) def _pack_tensors(device_grads, @@ -557,13 +556,13 @@ class AllReduceCrossDeviceOps(CrossDeviceOps): self._agg_small_grads_max_group = agg_small_grads_max_group super(AllReduceCrossDeviceOps, self).__init__() - def _reduce(self, aggregation, per_replica_value, destinations): - contains_indexed_slices = cross_tower_utils.contains_indexed_slices( + def _reduce(self, reduce_op, per_replica_value, destinations): + contains_indexed_slices = cross_device_utils.contains_indexed_slices( per_replica_value) if (_devices_match(per_replica_value, destinations) and not context.executing_eagerly() and not contains_indexed_slices): - return self._batch_all_reduce(aggregation, [per_replica_value])[0] + return self._batch_all_reduce(reduce_op, [per_replica_value])[0] else: if contains_indexed_slices: logging.log_first_n( @@ -576,16 +575,16 @@ class AllReduceCrossDeviceOps(CrossDeviceOps): devices = get_devices_from(per_replica_value) reduce_to_device = devices[0] reduced = _simple_reduce(per_replica_value, reduce_to_device, - math_ops.add_n, aggregation) + math_ops.add_n, reduce_op) return self.broadcast(reduced, devices) - def _batch_reduce(self, aggregation, value_destination_pairs): + def _batch_reduce(self, reduce_op, value_destination_pairs): all_devices_match = _all_devices_match(value_destination_pairs) - contains_indexed_slices = cross_tower_utils.contains_indexed_slices( + contains_indexed_slices = cross_device_utils.contains_indexed_slices( value_destination_pairs) if (all_devices_match and not context.executing_eagerly() and not contains_indexed_slices): - return self._batch_all_reduce(aggregation, + return self._batch_all_reduce(reduce_op, [v[0] for v in value_destination_pairs]) else: if not all_devices_match: @@ -595,11 +594,11 @@ class AllReduceCrossDeviceOps(CrossDeviceOps): 10) return [ - self._reduce(aggregation, t, destinations=v) + self._reduce(reduce_op, t, destinations=v) for t, v in value_destination_pairs ] - def _batch_all_reduce(self, aggregation, per_replica_values): + def _batch_all_reduce(self, reduce_op, per_replica_values): """All reduce algorithm in a batch.""" logging.log_first_n( logging.INFO, "batch_all_reduce invoked for batches size = %d with " @@ -619,18 +618,18 @@ class AllReduceCrossDeviceOps(CrossDeviceOps): # the balance on num_splits. if self._all_reduce_alg == "nccl": # TODO(yuefengz): merge this into the all-reduce library. - reduced = cross_tower_utils.aggregate_gradients_using_nccl( + reduced = cross_device_utils.aggregate_gradients_using_nccl( device_grad_packs) else: # TODO(yuefengz): check that gpu ids in `destinations` are in ascending # order. reduced = ( - cross_tower_utils.aggregate_gradients_using_hierarchical_copy( + cross_device_utils.aggregate_gradients_using_hierarchical_copy( destinations, device_grad_packs)) reduced = _unpack_tensors(reduced, tensor_packer) return _ungroup_and_make_mirrored(reduced, per_replica_values[0].devices, - aggregation) + reduce_op) # For compatibility with code using the old name of `AllReduceCrossDeviceOps`. @@ -713,7 +712,7 @@ class MultiWorkerAllReduce(AllReduceCrossDeviceOps): validate_and_complete_spec(spec) for spec in all_reduce_spec ] - def _batch_all_reduce(self, aggregation, per_replica_values): + def _batch_all_reduce(self, reduce_op, per_replica_values): """All reduce algorithm in a batch.""" logging.log_first_n( logging.INFO, @@ -741,13 +740,13 @@ class MultiWorkerAllReduce(AllReduceCrossDeviceOps): this_grads = remaining_grads remaining_grads = [] else: - (this_grads, remaining_grads) = cross_tower_utils.split_grads_by_size( + (this_grads, remaining_grads) = cross_device_utils.split_grads_by_size( spec_tuple.limit, remaining_grads) if this_grads: device_grad_packs, tensor_packer = _pack_tensors( this_grads, self._num_packs, self._agg_small_grads_max_bytes, self._agg_small_grads_max_group) - range_agg_grads = cross_tower_utils.sum_gradients_all_reduce( + range_agg_grads = cross_device_utils.sum_gradients_all_reduce( self._worker_devices, device_grad_packs, len(self._worker_devices), spec_tuple.alg, spec_tuple.shards, range(self._num_gpus_per_worker)) range_agg_grads = _unpack_tensors(range_agg_grads, tensor_packer) @@ -761,7 +760,7 @@ class MultiWorkerAllReduce(AllReduceCrossDeviceOps): assert not remaining_grads return _ungroup_and_make_mirrored(aggregated_grads, destinations, - aggregation) + reduce_op) # TODO(yuefengz): support in-graph collective all-reduce. @@ -790,20 +789,20 @@ class CollectiveAllReduce(CrossDeviceOps): self._num_workers = num_workers self._num_gpus_per_worker = num_gpus_per_worker self._all_reduce_merge_scope = all_reduce_merge_scope - self._collective_keys = collective_keys or cross_tower_utils.CollectiveKeys( - ) + self._collective_keys = (collective_keys or + cross_device_utils.CollectiveKeys()) super(CollectiveAllReduce, self).__init__() # TODO(yuefengz, tucker): is indexed slices supported by collective ops? - def _reduce(self, aggregation, per_replica_value, destinations): - if cross_tower_utils.contains_indexed_slices(per_replica_value): + def _reduce(self, reduce_op, per_replica_value, destinations): + if cross_device_utils.contains_indexed_slices(per_replica_value): raise ValueError( "`IndexSlices` is not supported for Collective All-Reduce.") if context.executing_eagerly(): raise ValueError( "Eager execution is not supported for Collective All-Reduce") - all_reduced = self._batch_all_reduce(aggregation, [per_replica_value])[0] + all_reduced = self._batch_all_reduce(reduce_op, [per_replica_value])[0] if _devices_match(per_replica_value, destinations): return all_reduced else: @@ -819,8 +818,8 @@ class CollectiveAllReduce(CrossDeviceOps): return value_lib.Mirrored(index) - def _batch_reduce(self, aggregation, value_destination_pairs): - if cross_tower_utils.contains_indexed_slices(value_destination_pairs): + def _batch_reduce(self, reduce_op, value_destination_pairs): + if cross_device_utils.contains_indexed_slices(value_destination_pairs): raise ValueError( "`IndexSlices` is not supported for Collective All-Reduce.") if context.executing_eagerly(): @@ -829,7 +828,7 @@ class CollectiveAllReduce(CrossDeviceOps): all_devices_match = _all_devices_match(value_destination_pairs) if all_devices_match: - return self._batch_all_reduce(aggregation, + return self._batch_all_reduce(reduce_op, [v[0] for v in value_destination_pairs]) else: if not all_devices_match: @@ -838,11 +837,11 @@ class CollectiveAllReduce(CrossDeviceOps): "destinations are different.", 10) return [ - self._reduce(aggregation, t, destinations=v) + self._reduce(reduce_op, t, destinations=v) for t, v in value_destination_pairs ] - def _batch_all_reduce(self, aggregation, per_replica_values): + def _batch_all_reduce(self, reduce_op, per_replica_values): """All-reduce across all workers in a batch.""" if context.executing_eagerly(): raise ValueError( @@ -871,7 +870,7 @@ class CollectiveAllReduce(CrossDeviceOps): with ops.name_scope("allreduce"): for grad_and_vars in chunk: scaled_grads = [g for g, _ in grad_and_vars] - collective_reduced = cross_tower_utils.build_collective_reduce( + collective_reduced = cross_device_utils.build_collective_reduce( scaled_grads, self._num_workers, self._collective_keys, "Add", "Id") result = [] @@ -883,7 +882,7 @@ class CollectiveAllReduce(CrossDeviceOps): return _ungroup_and_make_mirrored( new_device_grads, per_replica_values[0].devices, - aggregation, + reduce_op, num_between_graph_workers=self._num_workers) @@ -917,15 +916,15 @@ def _choose_all_reduce_algorithm(device_links): def choose_the_best(devices, session_config=None): - """Find the best subclass of CrossDeviceOps given a tensorflow session. + """Find the best subclass of CrossDeviceOps given a session config. Args: - devices: a list of devices passed for distribute strategy. - session_config: a tensorflow session config or None. If None, it will make - deciesion based on all local devices. + devices: a list of devices passed to `tf.distribute.Strategy`. + session_config: a `tf.ConfigProto` or `None`. If `None`, it will make + decision based on all local devices. Returns: - a subclass of CrossDeviceOps. + A subclass of `CrossDeviceOps`. """ requested_devices = set([device_util.canonicalize(d) for d in devices]) machine_devices = device_lib.list_local_devices(session_config=session_config) @@ -938,13 +937,13 @@ def choose_the_best(devices, session_config=None): "Device is available but not used by distribute strategy: %s", d.name) if len(using_devices) != len(requested_devices): - logging.warning("Not all devices in distribute strategy are visible by " - "TensorFlow sessions.") + logging.warning("Not all devices in `tf.distribute.Strategy` are visible " + "to TensorFlow.") return ReductionToOneDeviceCrossDeviceOps() - if any([d.device_type.lower() != "gpu" for d in using_devices]): - logging.warning("Not all devices in DistributionStrategy are visible to " - "TensorFlow session.") + if any(d.device_type.lower() != "gpu" for d in using_devices): + logging.warning("Not all devices in `tf.distribute.Strategy` are visible " + "to TensorFlow.") return ReductionToOneDeviceCrossDeviceOps() device_links = [[] for _ in range(len(using_devices))] diff --git a/tensorflow/contrib/distribute/python/cross_tower_utils.py b/tensorflow/python/distribute/cross_device_utils.py similarity index 99% rename from tensorflow/contrib/distribute/python/cross_tower_utils.py rename to tensorflow/python/distribute/cross_device_utils.py index 50b3cf31e5..0faadd7e0c 100644 --- a/tensorflow/contrib/distribute/python/cross_tower_utils.py +++ b/tensorflow/python/distribute/cross_device_utils.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Utilities for cross_tower_ops.""" +"""Utilities for cross_device_ops.""" from __future__ import absolute_import from __future__ import division @@ -21,8 +21,8 @@ from __future__ import print_function import collections as pycoll import threading -from tensorflow.contrib.all_reduce.python import all_reduce -from tensorflow.contrib.distribute.python import values as value_lib +from tensorflow.python.distribute import all_reduce +from tensorflow.python.distribute import values as value_lib from tensorflow.python.framework import device as pydev from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops @@ -420,7 +420,7 @@ def sum_gradients_all_reduce(dev_prefixes, replica_grads, num_workers, alg, Returns: list of reduced tensors """ - alg_contains_shuffle = any([n in alg for n in ['pscpu', 'psgpu']]) + alg_contains_shuffle = any(n in alg for n in ['pscpu', 'psgpu']) is_hierarchical = '/' in alg if 'pscpu' in alg: aux_devices = [prefix + '/cpu:0' for prefix in dev_prefixes] diff --git a/tensorflow/python/training/device_util.py b/tensorflow/python/distribute/device_util.py similarity index 98% rename from tensorflow/python/training/device_util.py rename to tensorflow/python/distribute/device_util.py index 70e1ca4b5d..34474582ad 100644 --- a/tensorflow/python/training/device_util.py +++ b/tensorflow/python/distribute/device_util.py @@ -50,7 +50,7 @@ def canonicalize(d, default=None): # Fill in missing device fields using defaults. result = tf_device.DeviceSpec( replica=0, task=0, device_type="CPU", device_index=0) - if context.executing_eagerly(): + if ops.executing_eagerly_outside_functions(): result.job = "localhost" if default: result.merge_from(tf_device.DeviceSpec.from_string(default)) diff --git a/tensorflow/python/training/device_util_test.py b/tensorflow/python/distribute/device_util_test.py similarity index 95% rename from tensorflow/python/training/device_util_test.py rename to tensorflow/python/distribute/device_util_test.py index cdbb08229d..2f0d7ed3b3 100644 --- a/tensorflow/python/training/device_util_test.py +++ b/tensorflow/python/distribute/device_util_test.py @@ -18,14 +18,16 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +from tensorflow.python.distribute import device_util from tensorflow.python.eager import context from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.platform import test -from tensorflow.python.training import device_util class DeviceUtilTest(test.TestCase): + @test_util.run_deprecated_v1 def testCurrentDeviceWithGlobalGraph(self): with ops.device("/cpu:0"): self.assertEqual(device_util.current(), "/device:CPU:0") @@ -49,6 +51,7 @@ class DeviceUtilTest(test.TestCase): self.assertEqual(device_util.current(), "/job:localhost/replica:0/task:0/device:CPU:0") + @test_util.run_deprecated_v1 def testCanonicalizeWithoutDefaultDevice(self): self.assertEqual( device_util.canonicalize("/cpu:0"), diff --git a/tensorflow/python/distribute/distribute_coordinator.py b/tensorflow/python/distribute/distribute_coordinator.py index 520413102b..c0f9b8a1fd 100644 --- a/tensorflow/python/distribute/distribute_coordinator.py +++ b/tensorflow/python/distribute/distribute_coordinator.py @@ -245,7 +245,7 @@ class _WorkerContext(object): else: session_config = self._session_config - if not self._strategy or self._strategy.should_init: + if not self._strategy or self._strategy.extended.experimental_should_init: logging.info("Creating chief session creator with config: %r", config) return monitored_session.ChiefSessionCreator( scaffold, @@ -261,6 +261,10 @@ class _WorkerContext(object): config=session_config, max_wait_secs=max_wait_secs) + @property + def session_config(self): + return copy.deepcopy(self._session_config) + @property def has_barrier(self): """Whether the barrier is set or not.""" @@ -301,15 +305,20 @@ class _WorkerContext(object): """Returns number of workers in the cluster, including chief.""" return self._num_workers + @property + def experimental_should_init(self): + """Whether to run init ops.""" + return self._strategy.extended.experimental_should_init + @property def should_checkpoint(self): """Whether to save checkpoint.""" - return self._strategy.should_checkpoint + return self._strategy.extended.should_checkpoint @property def should_save_summary(self): """Whether to save summaries.""" - return self._strategy.should_save_summary + return self._strategy.extended.should_save_summary def _run_single_worker(worker_fn, @@ -623,10 +632,10 @@ def run_distribute_coordinator(worker_fn, The `strategy` object is expected to be a DistributionStrategy object which has implemented methods needed by distributed coordinator such as `configure(session_config, cluster_spec, task_type, task_id)` which configures - the strategy object for a specific task and `should_init` property which - instructs the distribute coordinator whether to run init ops for a task. The - distribute coordinator will make a copy of the `strategy` object, call its - `configure` method and pass it to `worker_fn` as an argument. + the strategy object for a specific task and `experimental_should_init` + property which instructs the distribute coordinator whether to run init ops + for a task. The distribute coordinator will make a copy of the `strategy` + object, call its `configure` method and pass it to `worker_fn` as an argument. The `worker_fn` defines the training logic and is called under a its own worker context which can be accessed to via `get_current_worker_context`. A @@ -749,7 +758,7 @@ def run_distribute_coordinator(worker_fn, # The client must know the cluster but servers in the cluster don't have to # know the client. if task_type in [_TaskType.CLIENT, None]: - if strategy.between_graph: + if strategy.extended.experimental_between_graph: return _run_between_graph_client(worker_fn, strategy, eval_fn, eval_strategy, cluster_spec, session_config, rpc_layer) @@ -795,7 +804,7 @@ def run_distribute_coordinator(worker_fn, environment=environment) if task_type in [_TaskType.CHIEF, _TaskType.WORKER]: - if strategy.between_graph: + if strategy.extended.experimental_between_graph: # All jobs run `worker_fn` if between-graph. _run_single_worker(worker_fn, strategy, cluster_spec, task_type, task_id, session_config, rpc_layer) diff --git a/tensorflow/python/distribute/distribute_coordinator_test.py b/tensorflow/python/distribute/distribute_coordinator_test.py index 5d336648ce..f2cb950aad 100644 --- a/tensorflow/python/distribute/distribute_coordinator_test.py +++ b/tensorflow/python/distribute/distribute_coordinator_test.py @@ -47,6 +47,7 @@ from tensorflow.python.ops import variable_scope from tensorflow.python.ops import variables from tensorflow.python.platform import test from tensorflow.python.training import monitored_session +from tensorflow.python.training import session_manager CHIEF = distribute_coordinator._TaskType.CHIEF @@ -78,46 +79,53 @@ def _strip_protocol(target): return target -class MockStrategy(object): +class MockExtended(object): def __init__(self, between_graph=False, should_init=None, should_checkpoint=None, should_save_summary=None): - self._between_graph = between_graph - self._should_init = should_init - self._should_checkpoint = should_checkpoint - self._should_save_summary = should_save_summary + self.experimental_between_graph = between_graph + self.experimental_should_init = should_init + self.should_checkpoint = should_checkpoint + self.should_save_summary = should_save_summary - @property - def between_graph(self): - return self._between_graph + +class MockStrategy(object): + + def __init__(self, + between_graph=False, + should_init=None, + should_checkpoint=None, + should_save_summary=None): + self.extended = MockExtended(between_graph, should_init, should_checkpoint, + should_save_summary) def configure(self, session_config=None, cluster_spec=None, task_type=None, task_id=None): - if self._should_init is None: + if self.extended.experimental_should_init is None: if task_id == 0: - self._should_init = True + self.extended.experimental_should_init = True else: - self._should_init = False - if self._should_checkpoint is None: + self.extended.experimental_should_init = False + if self.extended.should_checkpoint is None: if task_id == 0: - self._should_checkpoint = True + self.extended.should_checkpoint = True else: - self._should_checkpoint = False - if self._should_save_summary is None: + self.extended.should_checkpoint = False + if self.extended.should_save_summary is None: if task_id == 0: - self._should_save_summary = True + self.extended.should_save_summary = True else: - self._should_save_summary = False + self.extended.should_save_summary = False if session_config: if (cluster_spec and task_type and task_id is not None and - self._between_graph): + self.extended.experimental_between_graph): session_config.intra_op_parallelism_threads += 1 if task_type in ["chief", "worker"]: session_config.device_filters.extend( @@ -126,18 +134,6 @@ class MockStrategy(object): session_config.inter_op_parallelism_threads += 1 session_config.device_filters.append("/job:somejob") - @property - def should_init(self): - return self._should_init - - @property - def should_checkpoint(self): - return self._should_checkpoint - - @property - def should_save_summary(self): - return self._should_save_summary - class MockServer(object): @@ -372,9 +368,12 @@ class DistributeCoordinatorTestBase(test.TestCase): context = distribute_coordinator_context.get_current_worker_context() self.assertTrue(context is not None) - self.assertEqual(context._strategy.should_init, strategy.should_init) - self.assertEqual(context.should_checkpoint, strategy.should_checkpoint) - self.assertEqual(context.should_save_summary, strategy.should_save_summary) + self.assertEqual(context._strategy.extended.experimental_should_init, + strategy.extended.experimental_should_init) + self.assertEqual(context.should_checkpoint, + strategy.extended.should_checkpoint) + self.assertEqual(context.should_save_summary, + strategy.extended.should_save_summary) task_type = str(context.task_type) task_id = context.task_id or 0 @@ -384,7 +383,8 @@ class DistributeCoordinatorTestBase(test.TestCase): while len(self._strategy_property[task_type]) <= task_id: self._strategy_property[task_type].append(None) self._strategy_property[task_type][task_id] = ( - context._strategy.should_init, context.should_checkpoint, + context._strategy.extended.experimental_should_init, + context.should_checkpoint, context.should_save_summary) def _run_mock_std_server(self, @@ -930,4 +930,14 @@ class RunStandardTensorflowServerTest(test.TestCase): if __name__ == "__main__": # TODO(yuefengz): find a smart way to terminite std server threads. with test.mock.patch.object(sys, "exit", os._exit): + # Reduce `recovery_wait_secs` from 30 seconds so the test completes quickly. + orig_init = session_manager.SessionManager.__init__ + + def new_init(*args, **kwargs): + kwargs.pop("recovery_wait_secs", None) + kwargs["recovery_wait_secs"] = 0.5 + orig_init(*args, **kwargs) + + session_manager.SessionManager.__init__ = new_init + test.main() diff --git a/tensorflow/python/distribute/distribute_lib.py b/tensorflow/python/distribute/distribute_lib.py new file mode 100644 index 0000000000..eddd6ff8b1 --- /dev/null +++ b/tensorflow/python/distribute/distribute_lib.py @@ -0,0 +1,1682 @@ +# Copyright 2018 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. +# ============================================================================== +"""Library for running a computation across multiple devices.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import copy +import threading +import weakref +import enum + +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.distribute import device_util +from tensorflow.python.distribute import distribution_strategy_context +from tensorflow.python.distribute import reduce_util +from tensorflow.python.eager import context as eager_context +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import control_flow_ops +from tensorflow.python.ops import resource_variable_ops +from tensorflow.python.ops import variable_scope +from tensorflow.python.ops.losses import losses_impl +from tensorflow.python.platform import tf_logging +from tensorflow.python.util import nest +from tensorflow.python.util.tf_export import tf_export +from tensorflow.tools.docs import doc_controls + + +# ------------------------------------------------------------------------------ +# Context tracking whether in a strategy.update() or .update_non_slot() call. + + +_update_device = threading.local() + + +def get_update_device(): + """Get the current device if in a `tf.distribute.Strategy.update()` call.""" + try: + return _update_device.current + except AttributeError: + return None + + +class UpdateContext(object): + """Context manager when you are in `update()` or `update_non_slot()`.""" + + def __init__(self, device): + self._device = device + self._old_device = None + + def __enter__(self): + self._old_device = get_update_device() + _update_device.current = self._device + + def __exit__(self, exception_type, exception_value, traceback): + del exception_type, exception_value, traceback + _update_device.current = self._old_device + + +# ------------------------------------------------------------------------------ +# Public utility functions. + + +@tf_export("distribute.get_loss_reduction") +def get_loss_reduction(): + """`tf.distribute.ReduceOp` corresponding to the last loss reduction.""" + loss_reduction = ops.get_default_graph()._last_loss_reduction # pylint: disable=protected-access + if (loss_reduction == losses_impl.Reduction.SUM or + loss_reduction == losses_impl.ReductionV2.SUM): + return reduce_util.ReduceOp.SUM + return reduce_util.ReduceOp.MEAN + + +# ------------------------------------------------------------------------------ +# Internal API for validating the current thread mode + + +def _require_cross_replica_context_extended(extended): + """Verify in cross-replica context.""" + context = _get_per_thread_mode() + cross_replica = context.cross_replica_context + if cross_replica is not None and cross_replica.extended is extended: + return + strategy = extended._container_strategy() # pylint: disable=protected-access + # We have an error to report, figure out the right message. + if context.distribution_strategy is not strategy: + _wrong_strategy_scope(strategy, context) + assert cross_replica is None + raise RuntimeError("Method requires being in cross-replica context, use " + "get_replica_context().merge_call()") + + +def _wrong_strategy_scope(strategy, context): + # Figure out the right error message. + if not distribution_strategy_context.has_distribution_strategy(): + raise RuntimeError( + 'Need to be inside "with strategy.scope()" for %s' % + (strategy,)) + else: + raise RuntimeError( + "Mixing different tf.distribute.Strategy objects: %s is not %s" % + (context.distribution_strategy, strategy)) + + +def require_replica_context(replica_ctx): + """Verify in `replica_ctx` replica context.""" + context = _get_per_thread_mode() + if context.replica_context is replica_ctx: return + # We have an error to report, figure out the right message. + if context.replica_context is None: + raise RuntimeError("Need to be inside `call_for_each_replica()`") + if context.distribution_strategy is replica_ctx.distribution_strategy: + # Two different ReplicaContexts with the same tf.distribute.Strategy. + raise RuntimeError("Mismatching ReplicaContext.") + raise RuntimeError( + "Mismatching tf.distribute.Strategy objects: %s is not %s." % + (context.distribution_strategy, replica_ctx.distribution_strategy)) + + +def _require_distribution_strategy_scope_strategy(strategy): + """Verify in a `strategy.scope()` in this thread.""" + context = _get_per_thread_mode() + if context.distribution_strategy is strategy: return + _wrong_strategy_scope(strategy, context) + + +def _require_distribution_strategy_scope_extended(extended): + """Verify in a `distribution_strategy.scope()` in this thread.""" + context = _get_per_thread_mode() + if context.distribution_strategy.extended is extended: return + # Report error. + strategy = extended._container_strategy() # pylint: disable=protected-access + _wrong_strategy_scope(strategy, context) + + +# ------------------------------------------------------------------------------ +# Internal context managers used to implement the DistributionStrategy +# base class + + +class _CurrentDistributionContext(object): + """Context manager setting the current `tf.distribute.Strategy`. + + Also: overrides the variable creator and optionally the current device. + """ + + def __init__(self, + strategy, + var_creator_scope, + var_scope=None, + default_device=None): + self._context = distribution_strategy_context._CrossReplicaThreadMode( # pylint: disable=protected-access + strategy) + self._var_creator_scope = var_creator_scope + self._var_scope = var_scope + if default_device: + self._device_scope = ops.device(default_device) + else: + self._device_scope = None + + def __enter__(self): + _push_per_thread_mode(self._context) + if self._var_scope: + self._var_scope.__enter__() + self._var_creator_scope.__enter__() + if self._device_scope: + self._device_scope.__enter__() + return self._context.distribution_strategy + + def __exit__(self, exception_type, exception_value, traceback): + if self._device_scope: + self._device_scope.__exit__(exception_type, exception_value, traceback) + self._var_creator_scope.__exit__(exception_type, exception_value, traceback) + if self._var_scope: + self._var_scope.__exit__(exception_type, exception_value, traceback) + _pop_per_thread_mode() + + +class _SameScopeAgainContext(object): + """Trivial context manager when you are already in `scope()`.""" + + def __init__(self, strategy): + self._distribution_strategy = strategy + + def __enter__(self): + return self._distribution_strategy + + def __exit__(self, exception_type, exception_value, traceback): + del exception_type, exception_value, traceback + + +# TODO(yuefengz): add more replication modes. +@tf_export("distribute.InputReplicationMode") +class InputReplicationMode(enum.Enum): + """Replication mode for input function.""" + + # The input function will be called on each worker independently, creating as + # many input pipelines as number of workers. Replicas will dequeue from the + # local Dataset on their worker. Distribution Strategy doesn't manage any + # state sharing between such separate input pipelines. + PER_WORKER = "PER_WORKER" + + +@tf_export("distribute.InputContext") +class InputContext(object): + """A class wrapping information needed by an input function. + + This is a context class that is passed to the user's input fn and contains + information about the compute replicas and input pipelines. The number of + compute replicas (in sync training) helps compute per input pipeline batch + size from the desired global batch size. Input pipeline information can be + used to return a different subset of the input in each input pipeline (for + e.g. shard the input pipeline, use a different input source etc). + """ + + def __init__(self, + num_input_pipelines=1, + input_pipeline_id=0, + num_replicas_in_sync=1): + """Initializes an InputContext object. + + Args: + num_input_pipelines: the number of input pipelines in a cluster. + input_pipeline_id: the current input pipeline id, should be an int in + [0,`num_input_pipelines`). + num_replicas_in_sync: the number of replicas that are in sync. + """ + self._num_input_pipelines = num_input_pipelines + self._input_pipeline_id = input_pipeline_id + self._num_replicas_in_sync = num_replicas_in_sync + + @property + def num_replicas_in_sync(self): + """Returns the number of compute replicas in sync.""" + return self._num_replicas_in_sync + + @property + def input_pipeline_id(self): + """Returns the input pipeline ID.""" + return self._input_pipeline_id + + @property + def num_input_pipelines(self): + """Returns the number of input pipelines.""" + return self._num_input_pipelines + + def get_per_replica_batch_size(self, global_batch_size): + """Returns the per-replica batch size. + + Args: + global_batch_size: the global batch size which should be divisible by + `num_replicas_in_sync`. + + Returns: + the per-replica batch size. + + Raises: + ValueError: if `global_batch_size` not divisible by + `num_replicas_in_sync`. + """ + if global_batch_size % self._num_replicas_in_sync != 0: + raise ValueError("The `global_batch_size` %r is not divisible by " + "`num_replicas_in_sync` %r " % + (global_batch_size, self._num_replicas_in_sync)) + return global_batch_size // self._num_replicas_in_sync + + +# ------------------------------------------------------------------------------ +# Base classes for all distribution strategies. + + +@tf_export("distribute.Strategy") +class DistributionStrategy(object): + """A list of devices with a state & compute distribution policy. + + See [tensorflow/contrib/distribute/README.md]( + https://www.tensorflow.org/code/tensorflow/contrib/distribute/README.md) + for overview and examples. + """ + + # TODO(josh11b): Raise an exception if variable partitioning requested before + # we add support. + # TODO(josh11b): Also `parameter_device_index` property? + # TODO(josh11b): `map()` + # TODO(josh11b): ClusterSpec/ClusterResolver + # TODO(josh11b): Partitioned computations, state; sharding + # TODO(josh11b): Model parallelism: "replicas" with multiple devices; shuffling + # TODO(josh11b): List of replicas with their worker and parameter devices + # (where the parameter devices may overlap in the ps case). + + def __init__(self, extended): + self._extended = extended + + @property + def extended(self): + """`tf.distribute.StrategyExtended` with additional methods.""" + return self._extended + + def scope(self): + """Returns a context manager selecting this Strategy as current. + + Inside a `with strategy.scope():` code block, this thread + will use a variable creator set by `strategy`, and will + enter its "cross-replica context". + + Returns: + A context manager. + """ + return self._extended._scope(self) # pylint: disable=protected-access + + @doc_controls.do_not_generate_docs # DEPRECATED, moving to `extended` + def read_var(self, v): + """DEPRECATED: use extended.read_var() instead.""" + return self._extended.read_var(v) + + @doc_controls.do_not_generate_docs # DEPRECATED, moving to `extended` + def colocate_vars_with(self, colocate_with_variable): + """DEPRECATED: use extended.colocate_vars_with() instead.""" + return self._extended.colocate_vars_with(colocate_with_variable) + + @doc_controls.do_not_generate_docs # DEPRECATED + def distribute_dataset(self, dataset_fn): + """Return a `dataset` split across all replicas. DEPRECATED. + + DEPRECATED: Please use `make_dataset_iterator` or + `make_input_fn_iterator` instead. + + Suitable for providing input to `extended.call_for_each_replica()` by + creating an iterator: + + ``` + def dataset_fn(): + return tf.data.Dataset.from_tensors([[1.]]).repeat() + + with strategy.scope(): + distributed_dataset = strategy.distribute_dataset(dataset_fn) + iterator = distributed_dataset.make_initializable_iterator() + replica_results = strategy.extended.call_for_each_replica( + replica_fn, args=(iterator.get_next(),)) + ``` + + Args: + dataset_fn: A function that returns a `tf.data.Dataset`. + + Returns: + A `PerReplicaDataset` that will produce data for each replica. + """ + return self._extended._distribute_dataset(dataset_fn) # pylint: disable=protected-access + + def make_dataset_iterator(self, dataset): + """Makes an iterator for input provided via input_dataset. + + Data from the given dataset will be distributed evenly across all the + compute replicas. We will assume that the input dataset is batched by the + global batch size. With this assumption, we will make a best effort to + divide each batch across all the replicas (one or more workers). + If this effort fails, an error will be thrown, and the user should instead + use `make_input_fn_iterator` which provides more control to the user, and + does not try to divide a batch across replicas. + + The user could also use `make_input_fn_iterator` if they want to + customize which input is fed to which replica/worker etc. + + Args: + dataset: `tf.data.Dataset` that will be distributed evenly across all + replicas. + + Returns: + An `tf.distribute.InputIterator` which returns inputs for each step of the + computation. User should call `initialize` on the returned iterator. + """ + return self._extended._make_dataset_iterator(dataset) # pylint: disable=protected-access + + def make_input_fn_iterator(self, + input_fn, + replication_mode=InputReplicationMode.PER_WORKER): + """Returns an iterator split across replicas created from an input function. + + The `input_fn` should take an `tf.distribute.InputContext` object where + information about input sharding can be accessed: + + ``` + def input_fn(input_context): + d = tf.data.Dataset.from_tensors([[1.]]).repeat() + return d.shard(input_context.num_input_pipelines, + input_context.input_pipeline_id) + with strategy.scope(): + iterator = strategy.make_input_fn_iterator( + input_fn) + replica_results = strategy.extended.call_for_each_replica( + replica_fn, iterator.get_next()) + ``` + + Args: + input_fn: A function that returns a `tf.data.Dataset`. This function is + expected to take an `tf.distribute.InputContext` object. + replication_mode: an enum value of `tf.distribute.InputReplicationMode`. + Only `PER_WORKER` is supported currently. + + Returns: + An iterator object that can be initialized and fetched next element. + """ + if replication_mode != InputReplicationMode.PER_WORKER: + raise ValueError( + "Input replication mode not supported: %r" % replication_mode) + return self.extended._make_input_fn_iterator( # pylint: disable=protected-access + input_fn, replication_mode=replication_mode) + + @doc_controls.do_not_generate_docs # DEPRECATED, moving to `extended` + def broadcast(self, tensor, destinations=None): + """DEPRECATED: use extended.broadcast_to() instead.""" + return self._extended.broadcast_to(tensor, destinations) + + @doc_controls.do_not_generate_docs # Use experimental_initialize() instead. + def initialize(self): + """DEPRECATED: Use `experimental_initialize()` instead.""" + return self._extended._initialize() # pylint: disable=protected-access + + def experimental_initialize(self): + """Any initialization to be done before running any computations. + + In eager mode, it executes any initialization as a side effect. + In graph mode, it creates the initialization ops and returns them. + + For example, TPU initialize_system ops. + + Returns: + A list of ops to execute. + """ + return self._extended._initialize() # pylint: disable=protected-access + + @doc_controls.do_not_generate_docs # Use experimental_finalize() instead. + def finalize(self): + """DEPRECATED: Use `experimental_finalize()` instead.""" + return self._extended._finalize() # pylint: disable=protected-access + + def experimental_finalize(self): + """Any final actions to be done at the end of all computations. + + In eager mode, it executes any finalize actions as a side effect. + In graph mode, it creates the finalize ops and returns them. + + For example, TPU shutdown ops. + + Returns: + A list of ops to execute. + """ + return self._extended._finalize() # pylint: disable=protected-access + + @doc_controls.do_not_generate_docs # DEPRECATED, moving to `extended` + def run_steps_on_dataset(self, fn, iterator, iterations=1, + initial_loop_values=None): + """DEPRECATED: use extended.experimental_run_steps_on_iterator() instead.""" + return self._extended.experimental_run_steps_on_iterator( + fn, iterator, iterations, initial_loop_values) + + @doc_controls.do_not_generate_docs # DEPRECATED, moving to `extended` + def call_for_each_replica(self, fn, *args, **kwargs): + """DEPRECATED: use extended.call_for_each_replica() instead.""" + # Handle old *args, **kwargs, and new args=(...), kwargs={...}, to + # allow transition. + a = kwargs.pop("args", None) + if a is not None: + if args: + raise ValueError( + "Can't pass *args and args=... to call_for_each_replica") + args = a + k = kwargs.pop("kwargs", None) + if k is not None: + if kwargs: + raise ValueError( + "Can't pass **kwargs and kwargs=... to call_for_each_replica") + kwargs = k + kwargs.pop("run_concurrently", None) # Ignore old option. + return self._extended.call_for_each_replica(fn, args, kwargs) + + def reduce(self, reduce_op, value): + """Reduce `value` across replicas. + + Args: + reduce_op: A `tf.distribute.ReduceOp` value specifying how values should + be combined. + value: A "per replica" value to be combined into a single tensor. + + Returns: + A `Tensor`. + """ + _require_cross_replica_context_extended(self._extended) + return self._extended._reduce(reduce_op, value) # pylint: disable=protected-access + + @doc_controls.do_not_generate_docs # DEPRECATED, moving to `extended` + def batch_reduce(self, aggregation, value_destination_pairs): + """DEPRECATED: use extended.batch_reduce_to() instead.""" + return self._extended.batch_reduce_to(aggregation, value_destination_pairs) + + @doc_controls.do_not_generate_docs # DEPRECATED, moving to `extended` + def update(self, var, fn, *args, **kwargs): + """DEPRECATED: use extended.update() instead.""" + group = kwargs.pop("group", True) + # We temporarily support "grouped" in addition to "group" for backward- + # compatibility. + group = kwargs.pop("grouped", True) and group + # Handle old *args, **kwargs, and new args=(...), kwargs={...}, to + # allow transition. + a = kwargs.pop("args", None) + if a is not None: + if args: + raise ValueError( + "Can't pass *args and args=... to update") + args = a + k = kwargs.pop("kwargs", None) + if k is not None: + if kwargs: + raise ValueError( + "Can't pass **kwargs and kwargs=... to update") + kwargs = k + return self._extended.update(var, fn, args, kwargs, group) + + @doc_controls.do_not_generate_docs # DEPRECATED, moving to `extended` + def update_non_slot(self, colocate_with, fn, *args, **kwargs): + """DEPRECATED: use extended.update_non_slot() instead.""" + group = kwargs.pop("group", True) + # We temporarily support "grouped" in addition to "group" for backward- + # compatibility. + group = kwargs.pop("grouped", True) and group + # Handle old *args, **kwargs, and new args=(...), kwargs={...}, to + # allow transition. + a = kwargs.pop("args", None) + if a is not None: + if args: + raise ValueError( + "Can't pass *args and args=... to update_non_slot") + args = a + k = kwargs.pop("kwargs", None) + if k is not None: + if kwargs: + raise ValueError( + "Can't pass **kwargs and kwargs=... to update_non_slot") + kwargs = k + return self._extended.update_non_slot( + colocate_with, fn, args, kwargs, group) + + @doc_controls.do_not_generate_docs # DEPRECATED, -> `DistributedValues` + def unwrap(self, value): + """Returns the list of all per-replica values contained in `value`. + + Args: + value: A value returned by `extended.call_for_each_replica()` or a + variable created in `scope`. + + Returns: + A list of values contained in `value`. If `value` represents a single + value, this returns `[value].` + """ + return self._extended._unwrap(value) # pylint: disable=protected-access + + @doc_controls.do_not_generate_docs # DEPRECATED, moving to `extended` + def value_container(self, value): + """DEPRECATED: use extended.value_container() instead.""" + return self._extended.value_container(value) + + @doc_controls.do_not_generate_docs # DEPRECATED, -> `DistributedValues` + def group(self, value, name=None): + """Shortcut for `tf.group(self.unwrap(value))`.""" + return self._extended._group(value, name) # pylint: disable=protected-access + + @property + @doc_controls.do_not_generate_docs # DEPRECATED, moving to `extended` + def require_static_shapes(self): + """DEPRECATED: use extended.require_static_shapes instead.""" + return self._extended.experimental_require_static_shapes + + @property + def num_replicas_in_sync(self): + """Returns number of replicas over which gradients are aggregated.""" + return self._extended._num_replicas_in_sync # pylint: disable=protected-access + + @property + @doc_controls.do_not_generate_docs # DEPRECATED, moving to `extended` + def worker_devices(self): + """DEPRECATED: use extended.worker_devices instead.""" + return self._extended.worker_devices + + @property + @doc_controls.do_not_generate_docs # DEPRECATED, moving to `extended` + def parameter_devices(self): + """DEPRECATED: use extended.parameter_devices instead.""" + return self._extended.parameter_devices + + @doc_controls.do_not_generate_docs # DEPRECATED, moving to `extended` + def non_slot_devices(self, var_list): + """DEPRECATED: use extended.non_slot_devices instead.""" + return self._extended.non_slot_devices(var_list) + + @property + @doc_controls.do_not_generate_docs # DEPRECATED, moving to `extended` + def between_graph(self): + """DEPRECATED: use extended.experimental_between_graph instead.""" + return self._extended.experimental_between_graph + + @doc_controls.do_not_generate_docs # DEPRECATED, being replaced by a new API. + def configure(self, + session_config=None, + cluster_spec=None, + task_type=None, + task_id=None): + # pylint: disable=g-doc-return-or-yield,g-doc-args + """DEPRECATED: use `update_config_proto` instead. + + Configures the strategy class. + + DEPRECATED: This method's functionality has been split into the strategy + constructor and `update_config_proto`. In the future, we will allow passing + cluster and config_proto to the constructor to configure the strategy. And + `update_config_proto` can be used to update the config_proto based on the + specific strategy. + """ + return self._extended._configure( # pylint: disable=protected-access + session_config, cluster_spec, task_type, task_id) + + def update_config_proto(self, config_proto): + """Returns a copy of `config_proto` modified for use with this strategy. + + The updated config has something needed to run a strategy, e.g. + configuration to run collective ops, or device filters to improve + distributed training performance. + + Args: + config_proto: a `tf.ConfigProto` object. + + Returns: + The updated copy of the `config_proto`. + """ + return self._extended._update_config_proto(config_proto) # pylint: disable=protected-access + + @property + @doc_controls.do_not_generate_docs # DEPRECATED, moving to `extended` + def should_init(self): + """DEPRECATED: use extended.should_init instead.""" + return self._extended.experimental_should_init + + @property + @doc_controls.do_not_generate_docs # DEPRECATED, moving to `extended` + def should_checkpoint(self): + """DEPRECATED: use extended.should_checkpoint instead.""" + return self._extended.should_checkpoint + + @property + @doc_controls.do_not_generate_docs # DEPRECATED, moving to `extended` + def should_save_summary(self): + """DEPRECATED: use extended.should_save_summary instead.""" + return self._extended.should_save_summary + + def __deepcopy__(self, memo): + # First do a regular deepcopy of `self`. + cls = self.__class__ + result = cls.__new__(cls) + memo[id(self)] = result + for k, v in self.__dict__.items(): + setattr(result, k, copy.deepcopy(v, memo)) + # One little fix-up: we want `result._extended` to reference `result` + # instead of `self`. + result._extended._container_strategy_weakref = weakref.ref(result) # pylint: disable=protected-access + return result + + def __copy__(self): + raise RuntimeError("Must only deepcopy DistributionStrategy.") + + +@tf_export("distribute.StrategyExtended") +class DistributionStrategyExtended(object): + """Additional APIs for algorithms that need to be distribution-aware. + + The intent is that you can write an algorithm in a stylized way and + it will be usable with a variety of different + `tf.distribute.Strategy` + implementations. Each descendant will implement a different strategy + for distributing the algorithm across multiple devices/machines. + Furthermore, these changes can be hidden inside the specific layers + and other library classes that need special treatment to run in a + distributed setting, so that most users' model definition code can + run unchanged. The `tf.distribute.Strategy` API works the same way + with eager and graph execution. + + First let's introduce a few high-level concepts: + + * _Data parallelism_ is where we run multiple copies of the model + on different slices of the input data. This is in contrast to + _model parallelism_ where we divide up a single copy of a model + across multiple devices. + Note: we only support data parallelism for now, but + hope to add support for model parallelism in the future. + * A _replica_ is one copy of the model, running on one slice of the + input data. + * _Synchronous_, or more commonly _sync_, training is where the + updates from each replica are aggregated together before updating + the model variables. This is in contrast to _asynchronous_, or + _async_ training, where each replica updates the model variables + independently. + * Furthermore you might run your computation on multiple devices + on one machine (or "host"), or on multiple machines/hosts. + If you are running on multiple machines, you might have a + single master host that drives computation across all of them, + or you might have multiple clients driving the computation + asynchronously. + + To distribute an algorithm, we might use some of these ingredients: + + * Parameter servers: These are hosts that hold a single copy of + parameters/variables. All replicas that want to operate on a variable + retrieve it at the beginning of a step and send an update to be + applied at the end of the step. Can support either sync or async + training. + * Mirrored variables: These are variables that are copied to multiple + devices, where we keep the copies in sync by applying the same + updates to every copy. Normally would only be used with sync training. + * Reductions and Allreduce: A _reduction_ is some method of + aggregating multiple values into one value, like "sum" or + "mean". If doing sync training, we will perform a reduction on the + gradients to a parameter from all replicas before applying the + update. Allreduce is an algorithm for performing a reduction on + values from multiple devices and making the result available on + all of those devices. + * In the future we will have support for TensorFlow's partitioned + variables, where a single variable is split across multiple + devices. + + We have then a few approaches we want to support: + + * Code written (as if) with no knowledge of class `tf.distribute.Strategy`. + This code should work as before, even if some of the layers, etc. + used by that code are written to be distribution-aware. This is done + by having a default `tf.distribute.Strategy` that gives ordinary behavior, + and by default being in a single replica context. + * Ordinary model code that you want to run using a specific + `tf.distribute.Strategy`. This can be as simple as: + + ``` + with my_strategy.scope(): + iterator = my_strategy.make_dataset_iterator(dataset) + session.run(iterator.initialize()) + replica_train_ops = my_strategy.extended.call_for_each_replica( + replica_fn, args=(iterator.get_next(),)) + train_op = my_strategy.group(replica_train_ops) + ``` + + This takes an ordinary `dataset` and `replica_fn` and runs it + distributed using a particular `tf.distribute.Strategy` in + `my_strategy`. Any variables created in `replica_fn` are created + using `my_strategy`'s policy, and library functions called by + `replica_fn` can use the `get_replica_context()` API to get enhanced + behavior in this case. + + * If you want to write a distributed algorithm, you may use any of + the `tf.distribute.Strategy` APIs inside a + `with my_strategy.scope():` block of code. + + Lower-level concepts: + + * Wrapped values: In order to represent values parallel across devices + (either replicas or the devices associated with a particular value), we + wrap them in a "PerReplica" or "Mirrored" object that contains a map + from device to values. "PerReplica" is used when the value may be + different across replicas, and "Mirrored" when the value are the same. + * Unwrapping and merging: Consider calling a function `fn` on multiple + replicas, like `extended.call_for_each_replica(fn, args=[w])` with an + argument `w` that is a wrapped value. This means `w` will have a map taking + replica device `d0` to `w0`, replica device `d1` to `w1`, + etc. `extended.call_for_each_replica()` unwraps `w` before calling `fn`, so + it calls `fn(w0)` on `d0`, `fn(w1)` on `d1`, etc. It then merges the return + values from `fn()`, which can possibly result in wrapped values. For + example, let's say `fn()` returns a tuple with three components: `(x, a, + v0)` from replica 0, `(x, b, v1)` on replica 1, etc. If the first component + is the same object `x` from every replica, then the first component of the + merged result will also be `x`. If the second component is different (`a`, + `b`, ...) from each replica, then the merged value will have a wrapped map + from replica device to the different values. If the third component is the + members of a mirrored variable (`v` maps `d0` to `v0`, `d1` to `v1`, etc.), + then the merged result will be that mirrored variable (`v`). + * Replica context vs. Cross-replica context: _replica context_ is when we + are in some function that is being called once for each replica. + Otherwise we are in cross-replica context, which is useful for + calling `tf.distribute.Strategy` methods which operate across the + replicas (like `reduce_to()`). By default you start in a replica context + (the default "single replica context") and then some methods can + switch you back and forth, as described below. + * Worker devices vs. parameter devices: Most replica computations will + happen on worker devices. Since we don't yet support model + parallelism, there will be one worker device per replica. When using + parameter servers (see above), the set of devices holding + variables may be different, otherwise the parameter devices might + match the worker devices. + * Non-slot devices are some subset of the parameter devices where we + put all the non-slot variables. We need to ensure that all + non-slot variables are allocated on the same device, or mirrored + across the same set of devices. If you have some variable you want + to colocate all the non-slot variables with, you can use + `colocate_vars_with()` to get the remaining non-slot variables on + the same device. Otherwise you can use `non_slot_devices()` to + pick a consistent set of devices to pass to both + `colocate_vars_with()` and `update_non_slot()`. + + When using a `tf.distribute.Strategy`, we have a new type dimension + called _locality_ that says what values are compatible with which + APIs: + + * T: different value for each replica (e.g. a PerReplica-wrapped value). + * M: value is "mirrored" across replicas, i.e. there are copies with the + same value on each replica (e.g. a Mirrored-wrapped value). + * V(`v`): value is "mirrored" across all the devices which have a + copy of variable `v` (also a Mirrored-wrapped value, but over + parameter devices instead of worker devices). + * N: value is "mirrored" across all the "non-slot" devices + + Rules for methods with respect to locality and single-replica vs. + cross-replica context: + + * `with d.scope()`: default single-replica context -> cross-replica context + for `d` + * `with d.extended.colocate_vars_with(v)`: in replica/cross-replica context, + variables will be created with locality V(`v`). That is, if we write + `with d.extended.colocate_vars_with(v1): v2 = tf.get_variable(...)`, + then `v2` will have locality V(`v1`), i.e. locality V(`v2`) will equal + V(`v1`). + * `with d.extended.colocate_vars_with(d.extended.non_slot_devices(...))`: in + replica/cross-replica context, variables will be created with locality N + * `v = tf.get_variable(...)`: in replica/cross-replica context, creates + a variable (which by definition will have locality V(`v`), though + will match another locality if inside a `colocate_vars_with` + scope). + * `d.make_dataset_iterator(dataset)` (or the deprecated + `d.distribute_dataset(dataset).make_one_shot_iterator()`): in cross-replica + context, produces an iterator with locality T + * `d.extended.broadcast_to(t)`: in cross-replica context, produces a value + with locality M + * `d.extended.broadcast_to(t, v)`: in cross-replica context, produces a value + with locality V(`v`) + * `d.extended.call_for_each_replica(fn, ...)`: in cross-replica context, runs + `fn()` in a replica context (and so may call `get_replica_context()` and + use its API, including `merge_call()` to get back to cross-replica + context), once for each replica. May use values with locality T or + M, and any variable. + * `d.extended.reduce_to(m, t, t)`: in cross-replica context, accepts t with + locality T and produces a value with locality M. + * `d.extended.reduce_to(m, t, v)`: in cross-replica context, accepts t with + locality T and produces a value with locality V(`v`). + * `d.extended.batch_reduce_to(m, [(t, v)]): see `d.extended.reduce_to()` + * `d.extended.update(v, fn, ...)`: in cross-replica context, runs `fn()` once + for each device `v` is copied to, all inputs should have locality + V(`v`), output will have locality V(`v`) as well. + * `d.extended.update_non_slot(d.extended.non_slot_devices(), fn)`: in + cross-replica context, like `d.extended.update()` except with locality N. + * `d.extended.read_var(v)`: Gets the (read-only) value of the variable `v` (on + the device determined by the current device scope), aggregating + across replicas for replica-local variables. Frequently, this will be + done automatically when using `v` in an expression or fetching it in + a cross-replica context, but this function can be used to force that + conversion happens at a particular point in time (for example, to + add the result of the conversion to a graph collection). + + The standard pattern for updating variables is to: + + 1. Create an input iterator with `d.make_dataset_iterator()`. + 2. Define each replica `d.extended.call_for_each_replica()` up to the point of + getting a list of gradient, variable pairs. + 3. Call `d.extended.reduce_to(VariableAggregation.SUM, t, v)` or + `d.extended.batch_reduce_to()` to sum the gradients (with locality T) + into values with locality V(`v`). + 4. Call `d.extended.update(v)` for each variable to update its value. + + Steps 3 and 4 are done automatically by class `Optimizer` if you call + its `apply_gradients` method in a replica context. Otherwise you can + manually call its `_distributed_apply` method in a cross-replica context. + + Another thing you might want to do in the middle of your replica function is + an all-reduce of some intermediate value, using `d.extended.reduce_to()` or + `d.extended.batch_reduce_to()`. You simply provide the same tensor as the + input and destination. + + Layers should expect to be called in a replica context, and can use + the `tf.distribute.get_replica_context` function to get a + `tf.distribute.ReplicaContext` object. The + `ReplicaContext` object has a `merge_call()` method for entering + cross-replica context where you can use `reduce_to()` (or + `batch_reduce_to()`) and then optionally `update()` to update state. + + You may use this API whether or not a `tf.distribute.Strategy` is + being used, since there is a default implementation of + `ReplicaContext` and `tf.distribute.Strategy`. + + NOTE for new `tf.distribute.Strategy` implementations: Please put all logic + in a subclass of `tf.distribute.StrategyExtended`. The only code needed for + the `tf.distribute.Strategy` subclass is for instantiating your subclass of + `tf.distribute.StrategyExtended` in the `__init__` method. + """ + + def __init__(self, container_strategy): + self._container_strategy_weakref = weakref.ref(container_strategy) + self._default_device = None + # This property is used to determine if we should set drop_remainder=True + # when creating Datasets from numpy array inputs. + self._require_static_shapes = False + + def _container_strategy(self): + """Get the containing `DistributionStrategy`. + + This should not generally be needed except when creating a new + `ReplicaContext` and to validate that the caller is in the correct + `scope()`. + + Returns: + The `DistributionStrategy` such that `strategy.extended` is `self`. + """ + container_strategy = self._container_strategy_weakref() + assert container_strategy is not None + return container_strategy + + def _scope(self, strategy): + """Implementation of DistributionStrategy.scope().""" + if distribution_strategy_context.has_distribution_strategy(): + _require_cross_replica_context_extended(self) + return _SameScopeAgainContext(strategy) + + def creator_with_resource_vars(*args, **kwargs): + _require_distribution_strategy_scope_extended(self) + kwargs["use_resource"] = True + return self._create_variable(*args, **kwargs) + + def distributed_getter(getter, *args, **kwargs): + if not self._allow_variable_partition(): + if kwargs.pop("partitioner", None) is not None: + tf_logging.log_first_n( + tf_logging.WARN, "Partitioned variables are disabled when using " + "current tf.distribute.Strategy.", 1) + return getter(*args, **kwargs) + + return _CurrentDistributionContext( + strategy, + variable_scope.variable_creator_scope(creator_with_resource_vars), + variable_scope.variable_scope( + variable_scope.get_variable_scope(), + custom_getter=distributed_getter), self._default_device) + + def _allow_variable_partition(self): + return False + + def _create_variable(self, next_creator, *args, **kwargs): + # Note: should support "colocate_with" argument. + raise NotImplementedError("must be implemented in descendants") + + def read_var(self, v): + """Reads the value of a variable. + + Returns the aggregate value of a replica-local variable, or the + (read-only) value of any other variable. + + Args: + v: A variable allocated within the scope of this `tf.distribute.Strategy`. + + Returns: + A tensor representing the value of `v`, aggregated across replicas if + necessary. + """ + raise NotImplementedError("must be implemented in descendants") + + def colocate_vars_with(self, colocate_with_variable): + """Scope that controls which devices variables will be created on. + + No operations should be added to the graph inside this scope, it + should only be used when creating variables (some implementations + work by changing variable creation, others work by using a + tf.colocate_with() scope). + + This may only be used inside `self.scope()`. + + Example usage: + + ``` + with strategy.scope(): + var1 = tf.get_variable(...) + with strategy.extended.colocate_vars_with(v1): + # var2 and var3 will be created on the same device(s) as var1 + var2 = tf.get_variable(...) + var3 = tf.get_variable(...) + + def fn(v1, v2, v3): + # operates on v1 from var1, v2 from var2, and v3 from var3 + + # `fn` runs on every device `v1` is on, `v2` and `v3` will be there too. + strategy.extended.update(v1, fn, args=(v2, v3)) + ``` + + Args: + colocate_with_variable: A created in `self.scope()`. Variables created + while in the returned context manager will be on the same set of + devices as `colocate_with_variable`. + + Returns: + A context manager. + """ + def create_colocated_variable(next_creator, *args, **kwargs): + _require_distribution_strategy_scope_extended(self) + kwargs["use_resource"] = True + kwargs["colocate_with"] = colocate_with_variable + return next_creator(*args, **kwargs) + + _require_distribution_strategy_scope_extended(self) + return variable_scope.variable_creator_scope(create_colocated_variable) + + def _call_dataset_fn(self, dataset_fn): + """Call the `dataset_fn` with `input_context` as argument.""" + result = dataset_fn() + if not isinstance(result, dataset_ops.DatasetV2): + raise ValueError( + "dataset_fn() must return a tf.data.Dataset when using a " + "tf.distribute.Strategy.") + return result + + # TODO(josh11b): `PerReplicaDataset` currently only implements a few methods of + # Dataset API such as make_one_shot_iterator and make_initializable_iterator. + # Extend to implement more functionality of datasets. + def _distribute_dataset(self, dataset_fn): + raise NotImplementedError("must be implemented in descendants") + + def _make_dataset_iterator(self, dataset): + raise NotImplementedError("must be implemented in descendants") + + def _make_input_fn_iterator(self, input_fn, replication_mode): + raise NotImplementedError("must be implemented in descendants") + + def broadcast_to(self, tensor, destinations): + """Mirror a tensor on one device to all worker devices. + + Args: + tensor: A Tensor value to broadcast. + destinations: A mirrored variable or device string specifying the + destination devices to copy `tensor` to. + + Returns: + A value mirrored to `destinations` devices. + """ + # TODO(josh11b): More docstring + _require_cross_replica_context_extended(self) + assert not isinstance(destinations, (list, tuple)) + return self._broadcast_to(tensor, destinations) + + def _broadcast_to(self, tensor, destinations): + raise NotImplementedError("must be implemented in descendants") + + def _initialize(self): + return [] + + def _finalize(self): + return [] + + def experimental_run_steps_on_iterator(self, fn, iterator, iterations=1, + initial_loop_values=None): + """Run `fn` with input from `iterator` for `iterations` times. + + This method can be used to run a step function for training a number of + times using input from a dataset. + + Args: + fn: function to run using this distribution strategy. The function must + have the following signature: `def fn(context, inputs)`. + `context` is an instance of `MultiStepContext` that will be passed when + `fn` is run. `context` can be used to specify the outputs to be returned + from `fn` by calling `context.set_last_step_output`. It can also be used + to capture non tensor outputs by `context.set_non_tensor_output`. + See `MultiStepContext` documentation for more information. + `inputs` will have same type/structure as `iterator.get_next()`. + Typically, `fn` will use `call_for_each_replica` method of the strategy + to distribute the computation over multiple replicas. + iterator: Iterator of a dataset that represents the input for `fn`. The + caller is responsible for initializing the iterator as needed. + iterations: (Optional) Number of iterations that `fn` should be run. + Defaults to 1. + initial_loop_values: (Optional) Initial values to be passed into the + loop that runs `fn`. Defaults to `None`. # TODO(priyag): Remove + initial_loop_values argument when we have a mechanism to infer the + outputs of `fn`. + + Returns: + Returns the `MultiStepContext` object which has the following properties, + among other things: + - run_op: An op that runs `fn` `iterations` times. + - last_step_outputs: A dictionary containing tensors set using + `context.set_last_step_output`. Evaluating this returns the value of + the tensors after the last iteration. + - non_tensor_outputs: A dictionatry containing anything that was set by + `fn` by calling `context.set_non_tensor_output`. + """ + _require_cross_replica_context_extended(self) + return self._experimental_run_steps_on_iterator( + fn, iterator, iterations, initial_loop_values) + + def _experimental_run_steps_on_iterator(self, fn, iterator, iterations, + initial_loop_values): + raise NotImplementedError("must be implemented in descendants") + + def call_for_each_replica(self, fn, args=(), kwargs=None): + """Run `fn` once per replica. + + `fn` may call `tf.get_replica_context()` to access methods such as + `replica_id_in_sync_group` and `merge_call()`. + + `merge_call()` is used to communicate between the replicas and + re-enter the cross-replica context. All replicas pause their execution + having encountered a `merge_call()` call. After that the + `merge_fn`-function is executed. Its results are then unwrapped and + given back to each replica call. After that execution resumes until + `fn` is complete or encounters another `merge_call()`. Example: + + ```python + # Called once in "cross-replica" context. + def merge_fn(distribution, three_plus_replica_id): + # sum the values across replicas + return sum(distribution.unwrap(three_plus_replica_id)) + + # Called once per replica in `distribution`, in a "replica" context. + def fn(three): + replica_ctx = tf.get_replica_context() + v = three + replica_ctx.replica_id_in_sync_group + # Computes the sum of the `v` values across all replicas. + s = replica_ctx.merge_call(merge_fn, args=(v,)) + return s + v + + with distribution.scope(): + # in "cross-replica" context + ... + merged_results = distribution.call_for_each_replica(fn, args=[3]) + # merged_results has the values from every replica execution of `fn`. + print(distribution.unwrap(merged_results)) # Prints a list + ``` + + Args: + fn: function to run (will be run once per replica). + args: Tuple or list with positional arguments for `fn`. + kwargs: Dict with keyword arguments for `fn`. + + Returns: + Merged return value of `fn` across all replicas. + """ + _require_cross_replica_context_extended(self) + if kwargs is None: + kwargs = {} + return self._call_for_each_replica(fn, args, kwargs) + + def _call_for_each_replica(self, fn, args, kwargs): + raise NotImplementedError("must be implemented in descendants") + + def _reduce(self, reduce_op, value): + # Default implementation until we have an implementation for each strategy. + return self._unwrap(self._reduce_to( + reduce_op, value, device_util.current() or "/device:CPU:0"))[0] + + def reduce_to(self, reduce_op, value, destinations): + """Combine (via e.g. sum or mean) values across replicas. + + Args: + reduce_op: Reduction type, an instance of `tf.distribute.ReduceOp` enum. + DEPRECATED but still accepted values: + `tf.VariableAggregation.SUM`, + `tf.VariableAggregation.MEAN`, + value: A per-replica value with one value per replica. + destinations: A mirrored variable, a per-replica tensor, or a device + string. The return value will be copied to all destination devices (or + all the devices where the `destinations` value resides). To perform an + all-reduction, pass `value` to `destinations`. + + Returns: + A value mirrored to `destinations`. + """ + # TODO(josh11b): More docstring + _require_cross_replica_context_extended(self) + assert not isinstance(destinations, (list, tuple)) + + # TODO(priyag): Remove this when all callers have been updated. + if isinstance(reduce_op, variable_scope.VariableAggregation): + assert reduce_op in ( + variable_scope.VariableAggregation.SUM, + variable_scope.VariableAggregation.MEAN, + ) + reduce_op = reduce_util.ReduceOp.from_variable_aggregation(reduce_op) + assert (reduce_op == reduce_util.ReduceOp.SUM or + reduce_op == reduce_util.ReduceOp.MEAN) + return self._reduce_to(reduce_op, value, destinations) + + def _reduce_to(self, reduce_op, value, destinations): + raise NotImplementedError("must be implemented in descendants") + + def batch_reduce_to(self, reduce_op, value_destination_pairs): + """Combine multiple `reduce_to` calls into one for faster execution. + + Args: + reduce_op: Reduction type, an instance of `tf.distribute.ReduceOp` enum. + DEPRECATED but still accepted values: + `tf.VariableAggregation.SUM`, + `tf.VariableAggregation.MEAN`, + value_destination_pairs: A sequence of (value, destinations) + pairs. See `reduce_to()` for a description. + + Returns: + A list of mirrored values, one per pair in `value_destination_pairs`. + """ + # TODO(josh11b): More docstring + _require_cross_replica_context_extended(self) + + # TODO(priyag): Remove this when all callers have been updated. + if isinstance(reduce_op, variable_scope.VariableAggregation): + assert reduce_op in [ + variable_scope.VariableAggregation.SUM, + variable_scope.VariableAggregation.MEAN, + ] + reduce_op = reduce_util.ReduceOp.from_variable_aggregation(reduce_op) + return self._batch_reduce_to(reduce_op, value_destination_pairs) + + def _batch_reduce_to(self, reduce_op, value_destination_pairs): + return [ + self.reduce_to(reduce_op, t, destinations=v) + for t, v in value_destination_pairs + ] + + def update(self, var, fn, args=(), kwargs=None, group=True): + """Run `fn` to update `var` using inputs mirrored to the same devices. + + If `var` is mirrored across multiple devices, then this implements + logic like: + + ``` + results = {} + for device, v in var: + with tf.device(device): + # args and kwargs will be unwrapped if they are mirrored. + results[device] = fn(v, *args, **kwargs) + return merged(results) + ``` + + Otherwise this returns `fn(var, *args, **kwargs)` colocated with `var`. + + Neither `args` nor `kwargs` may contain per-replica values. + If they contain mirrored values, they will be unwrapped before + calling `fn`. + + Args: + var: Variable, possibly mirrored to multiple devices, to operate on. + fn: Function to call. Should take the variable as the first argument. + args: Tuple or list. Additional positional arguments to pass to `fn()`. + kwargs: Dict with keyword arguments to pass to `fn()`. + group: Boolean. Defaults to True. If False, the return value will be + unwrapped. + + Returns: + By default, the merged return value of `fn` across all replicas. The + merged result has dependencies to make sure that if it is evaluated at + all, the side effects (updates) will happen on every replica. If instead + "group=False" is specified, this function will return a nest of lists + where each list has an element per replica, and the caller is responsible + for ensuring all elements are executed. + """ + _require_cross_replica_context_extended(self) + if kwargs is None: + kwargs = {} + return self._update(var, fn, args, kwargs, group) + + def _update(self, var, fn, args, kwargs, group): + raise NotImplementedError("must be implemented in descendants") + + def update_non_slot( + self, colocate_with, fn, args=(), kwargs=None, group=True): + """Runs `fn(*args, **kwargs)` on `colocate_with` devices. + + Args: + colocate_with: The return value of `non_slot_devices()`. + fn: Function to execute. + args: Tuple or list. Positional arguments to pass to `fn()`. + kwargs: Dict with keyword arguments to pass to `fn()`. + group: Boolean. Defaults to True. If False, the return value will be + unwrapped. + + Returns: + Return value of `fn`, possibly merged across devices. + """ + _require_cross_replica_context_extended(self) + if kwargs is None: + kwargs = {} + return self._update_non_slot(colocate_with, fn, args, kwargs, group) + + def _update_non_slot(self, colocate_with, fn, args, kwargs, group): + raise NotImplementedError("must be implemented in descendants") + + def _unwrap(self, distributed_value): + raise NotImplementedError("must be implemented in descendants") + + def value_container(self, value): + """Returns the container that this per-replica `value` belongs to. + + Args: + value: A value returned by `call_for_each_replica()` or a variable + created in `scope()`. + + Returns: + A container that `value` belongs to. + If value does not belong to any container (including the case of + container having been destroyed), returns the value itself. + `value in unwrap(value_container(value))` will always be true. + """ + raise NotImplementedError("must be implemented in descendants") + + def _group(self, value, name=None): + """Shortcut for `tf.group(distribution.unwrap(value))`.""" + value = nest.flatten(self._unwrap(value)) + + if len(value) != 1 or name is not None: + return control_flow_ops.group(value, name=name) + # Special handling for the common case of one op. + v, = value + if hasattr(v, "op"): + v = v.op + return v + + @property + def experimental_require_static_shapes(self): + return self._require_static_shapes + + @property + def _num_replicas_in_sync(self): + """Returns number of replicas over which gradients are aggregated.""" + raise NotImplementedError("must be implemented in descendants") + + @property + def worker_devices(self): + """Returns the list of devices used to run `call_for_each_replica()` calls. + """ + # TODO(josh11b): More docstring + raise NotImplementedError("must be implemented in descendants") + + @property + def parameter_devices(self): + """Returns the list of devices used for variable and `update` placement.""" + # TODO(josh11b): More docstring + raise NotImplementedError("must be implemented in descendants") + + def non_slot_devices(self, var_list): + """Device(s) for non-slot variables. + + Create variables on these devices in a + `with colocate_vars_with(non_slot_devices(...)):` block. + Update those using `update_non_slot()`. + + Args: + var_list: The list of variables being optimized, needed with the + default `tf.distribute.Strategy`. + """ + raise NotImplementedError("must be implemented in descendants") + + @property + def experimental_between_graph(self): + """Whether the strategy uses between-graph replication or not. + + This is expected to return a constant value that will not be changed + throughout its life cycle. + """ + raise NotImplementedError("must be implemented in descendants") + + def _configure(self, + session_config=None, + cluster_spec=None, + task_type=None, + task_id=None): + """Configures the strategy class.""" + del session_config, cluster_spec, task_type, task_id + + def _update_config_proto(self, config_proto): + return copy.deepcopy(config_proto) + + @property + def experimental_should_init(self): + """Whether initialization is needed.""" + raise NotImplementedError("must be implemented in descendants") + + @property + def should_checkpoint(self): + """Whether checkpointing is needed.""" + raise NotImplementedError("must be implemented in descendants") + + @property + def should_save_summary(self): + """Whether saving summaries is needed.""" + raise NotImplementedError("must be implemented in descendants") + + +# A note about the difference between the context managers +# `ReplicaContext` (defined here) and `_CurrentDistributionContext` +# (defined above) used by `DistributionStrategy.scope()`: +# +# * a ReplicaContext is only present during a `call_for_each_replica()` +# call (except during a `merge_run` call) and in such a scope it +# will be returned by calls to `get_replica_context()`. Implementers of new +# DistributionStrategy descendants will frequently also need to +# define a descendant of ReplicaContext, and are responsible for +# entering and exiting this context. +# +# * DistributionStrategy.scope() sets up a variable_creator scope that +# changes variable creation calls (e.g. to make mirrored +# variables). This is intended as an outer scope that users enter once +# around their model creation and graph definition. There is no +# anticipated need to define descendants of _CurrentDistributionContext. +# It sets the current DistributionStrategy for purposes of +# `get_strategy()` and `has_strategy()` +# and switches the thread mode to a "cross-replica context". +@tf_export("distribute.ReplicaContext") +class ReplicaContext(object): + """`tf.distribute.Strategy` API when in a replica context. + + To be used inside your replicated step function, such as in a + `tf.distribute.StrategyExtended.call_for_each_replica` call. + """ + + def __init__(self, strategy, replica_id_in_sync_group): + self._distribution_strategy = strategy + self._thread_context = distribution_strategy_context._InReplicaThreadMode( # pylint: disable=protected-access + self) + self._replica_id_in_sync_group = replica_id_in_sync_group + + def __enter__(self): + _push_per_thread_mode(self._thread_context) + + def __exit__(self, exception_type, exception_value, traceback): + _pop_per_thread_mode() + + def merge_call(self, merge_fn, args=(), kwargs=None): + """Merge args across replicas and run `merge_fn` in a cross-replica context. + + This allows communication and coordination when there are multiple calls + to a model function triggered by a call to + `strategy.extended.call_for_each_replica(model_fn, ...)`. + + See `tf.distribute.StrategyExtended.call_for_each_replica` for an + explanation. + + If not inside a distributed scope, this is equivalent to: + + ``` + strategy = tf.distribute.get_strategy() + with cross-replica-context(strategy): + return merge_fn(strategy, *args, **kwargs) + ``` + + Args: + merge_fn: function that joins arguments from threads that are given as + PerReplica. It accepts `tf.distribute.Strategy` object as + the first argument. + args: List or tuple with positional per-thread arguments for `merge_fn`. + kwargs: Dict with keyword per-thread arguments for `merge_fn`. + + Returns: + The return value of `merge_fn`, except for `PerReplica` values which are + unpacked. + """ + require_replica_context(self) + if kwargs is None: + kwargs = {} + return self._merge_call(merge_fn, args, kwargs) + + def _merge_call(self, merge_fn, args, kwargs): + """Default implementation for single replica.""" + _push_per_thread_mode( # thread-local, so not needed with multiple threads + distribution_strategy_context._CrossReplicaThreadMode( # pylint: disable=protected-access + self._distribution_strategy)) + try: + return merge_fn(self._distribution_strategy, *args, **kwargs) + finally: + _pop_per_thread_mode() + + @property + def num_replicas_in_sync(self): + """Returns number of replicas over which gradients are aggregated.""" + return self._distribution_strategy.num_replicas_in_sync + + @property + def replica_id_in_sync_group(self): + """Which replica is being defined, from 0 to `num_replicas_in_sync - 1`.""" + require_replica_context(self) + return self._replica_id_in_sync_group + + @property + @doc_controls.do_not_generate_docs # DEPRECATED, use `strategy` + def distribution_strategy(self): + """DEPRECATED: use `self.stratgey` instead.""" + return self._distribution_strategy + + @property + def strategy(self): + """The current `tf.distribute.Strategy` object.""" + return self._distribution_strategy + + @property + def devices(self): + """The devices this replica is to be executed on, as a list of strings.""" + require_replica_context(self) + return [device_util.current()] + + # TODO(josh11b): Implement `start_all_reduce(method, t)` for efficient + # all-reduce. It would return a function returning the result of reducing `t` + # across all replicas. The caller would wait to call this function until they + # needed the reduce result, allowing an efficient implementation: + # * With eager execution, the reduction could be performed asynchronously + # in the background, not blocking until the result was needed. + # * When constructing a graph, it could batch up all reduction requests up + # to that point that the first result is needed. Most likely this can be + # implemented in terms of `merge_call()` and `batch_reduce_to()`. + +# ------------------------------------------------------------------------------ + + +class _DefaultDistributionStrategy(DistributionStrategy): + """Default `tf.distribute.Strategy` if none is explicitly selected.""" + + def __init__(self): + super(_DefaultDistributionStrategy, self).__init__( + _DefaultDistributionExtended(self)) + + +class _DefaultDistributionExtended(DistributionStrategyExtended): + """Implementation of _DefaultDistributionStrategy.""" + + def _scope(self, strategy): + """Context manager setting a variable creator and `self` as current.""" + if distribution_strategy_context.has_distribution_strategy(): + raise RuntimeError("Must not nest tf.distribute.Strategy scopes.") + + def creator(next_creator, *args, **kwargs): + _require_distribution_strategy_scope_strategy(strategy) + return next_creator(*args, **kwargs) + + return _CurrentDistributionContext( + strategy, variable_scope.variable_creator_scope(creator)) + + def colocate_vars_with(self, colocate_with_variable): + """Does not require `self.scope`.""" + _require_distribution_strategy_scope_extended(self) + return ops.colocate_with(colocate_with_variable) + + def _distribute_dataset(self, dataset_fn): + return self._call_dataset_fn(dataset_fn) + + def _make_dataset_iterator(self, dataset): + return _DefaultDistributionExtended.DefaultInputIterator(dataset) + + def _make_input_fn_iterator(self, + input_fn, + replication_mode=InputReplicationMode.PER_WORKER): + return input_fn(InputContext()).make_initializable_iterator() + + def _broadcast_to(self, tensor, destinations): + if destinations is None: + return tensor + else: + raise NotImplementedError("TODO") + + def _call_for_each_replica(self, fn, args, kwargs): + with ReplicaContext( + self._container_strategy(), + replica_id_in_sync_group=constant_op.constant(0, dtypes.int32)): + return fn(*args, **kwargs) + + def _reduce_to(self, reduce_op, value, destinations): + # TODO(josh11b): Use destinations? + del reduce_op, destinations + return value + + def _update(self, var, fn, args, kwargs, group): + # The implementations of _update() and _update_non_slot() are identical + # except _update() passes `var` as the first argument to `fn()`. + return self._update_non_slot(var, fn, (var,) + tuple(args), kwargs, group) + + def _update_non_slot(self, colocate_with, fn, args, kwargs, should_group): + # TODO(josh11b): Figure out what we should be passing to UpdateContext() + # once that value is used for something. + with ops.colocate_with(colocate_with), UpdateContext(colocate_with): + result = fn(*args, **kwargs) + if should_group: + return result + else: + return nest.map_structure(self._unwrap, result) + + def read_var(self, replica_local_var): + return array_ops.identity(replica_local_var) + + def _unwrap(self, distributed_value): + return [distributed_value] + + def value_container(self, value): + return value + + @property + def _num_replicas_in_sync(self): + return 1 + + @property + def worker_devices(self): + raise RuntimeError("worker_devices() method unsupported by default " + "tf.distribute.Strategy.") + + @property + def parameter_devices(self): + raise RuntimeError("parameter_devices() method unsupported by default " + "tf.distribute.Strategy.") + + def non_slot_devices(self, var_list): + return min(var_list, key=lambda x: x.name) + + # TODO(priyag): This should inherit from `InputIterator`, once dependency + # issues have been resolved. + class DefaultInputIterator(object): + """Default implementation of `InputIterator` for default strategy.""" + + def __init__(self, dataset): + self._dataset = dataset + if eager_context.executing_eagerly(): + self._iterator = dataset.make_one_shot_iterator() + else: + self._iterator = dataset.make_initializable_iterator() + + def get_next(self): + return self._iterator.get_next() + + def initialize(self): + if eager_context.executing_eagerly(): + self._iterator = self._dataset.make_one_shot_iterator() + return [] + else: + return [self._iterator.initializer] + + # TODO(priyag): Delete this once all strategies use global batch size. + @property + def _global_batch_size(self): + return True + + +# ------------------------------------------------------------------------------ +# We haven't yet implemented deserialization for DistributedVariables. +# So here we catch any attempts to deserialize variables +# when using distribution strategies. +# pylint: disable=protected-access +_original_from_proto = resource_variable_ops._from_proto_fn + + +def _from_proto_fn(v, import_scope=None): + if distribution_strategy_context.has_distribution_strategy(): + raise NotImplementedError( + "Deserialization of variables is not yet supported when using a " + "tf.distribute.Strategy.") + else: + return _original_from_proto(v, import_scope=import_scope) + +resource_variable_ops._from_proto_fn = _from_proto_fn +# pylint: enable=protected-access + + +#------------------------------------------------------------------------------- +# Shorthand for some methods from distribution_strategy_context. +_push_per_thread_mode = distribution_strategy_context._push_per_thread_mode # pylint: disable=protected-access +_get_per_thread_mode = distribution_strategy_context._get_per_thread_mode # pylint: disable=protected-access +_pop_per_thread_mode = distribution_strategy_context._pop_per_thread_mode # pylint: disable=protected-access diff --git a/tensorflow/python/training/distribute_test.py b/tensorflow/python/distribute/distribute_lib_test.py similarity index 75% rename from tensorflow/python/training/distribute_test.py rename to tensorflow/python/distribute/distribute_lib_test.py index 0a7bbd5687..d63d1fe3c3 100644 --- a/tensorflow/python/training/distribute_test.py +++ b/tensorflow/python/distribute/distribute_lib_test.py @@ -18,13 +18,15 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +from tensorflow.python.distribute import distribute_lib +from tensorflow.python.distribute import distribution_strategy_context +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes from tensorflow.python.ops import variable_scope from tensorflow.python.platform import test -from tensorflow.python.training import distribute -from tensorflow.python.training import distribution_strategy_context -class _TestReplicaContext(distribute.ReplicaContext): +class _TestReplicaContext(distribute_lib.ReplicaContext): def merge_call(self, fn, *args, **kwargs): return kwargs["test_arg"] @@ -38,10 +40,18 @@ def _get_test_variable(name, synchronization, aggregation): } -class _TestStrategy(distribute.DistributionStrategy): +class _TestStrategy(distribute_lib.DistributionStrategy): + + def __init__(self): + super(_TestStrategy, self).__init__(_TestExtended(self)) + + +class _TestExtended(distribute_lib.DistributionStrategyExtended): def _call_for_each_replica(self, fn, args, kwargs): - with _TestReplicaContext(self, replica_id=0): + with _TestReplicaContext( + self._container_strategy(), + replica_id_in_sync_group=constant_op.constant(0, dtypes.int32)): return fn(*args, **kwargs) def _create_variable(self, next_creator, *args, **kwargs): @@ -53,6 +63,7 @@ def _assert_in_default_state(t): t.assertIs(distribution_strategy_context._get_default_replica_context(), distribution_strategy_context.get_replica_context()) t.assertIs(None, distribution_strategy_context.get_cross_replica_context()) + t.assertFalse(distribution_strategy_context.in_cross_replica_context()) t.assertIs(distribution_strategy_context._get_default_distribution_strategy(), distribution_strategy_context.get_distribution_strategy()) t.assertFalse(distribution_strategy_context.has_distribution_strategy()) @@ -69,6 +80,7 @@ class TestStrategyTest(test.TestCase): self.assertTrue(replica_context is not None) self.assertIs(None, distribution_strategy_context.get_cross_replica_context()) + self.assertFalse(distribution_strategy_context.in_cross_replica_context()) self.assertTrue(distribution_strategy_context.has_distribution_strategy()) self.assertIs(dist, distribution_strategy_context.get_distribution_strategy()) @@ -80,9 +92,9 @@ class TestStrategyTest(test.TestCase): variable_scope.variable(1.0, name="bar")) with self.assertRaises(RuntimeError): - dist.call_for_each_replica(run_fn) + dist.extended.call_for_each_replica(run_fn) with dist.scope(): - dist.call_for_each_replica(run_fn) + dist.extended.call_for_each_replica(run_fn) _assert_in_default_state(self) def testScope(self): @@ -92,6 +104,7 @@ class TestStrategyTest(test.TestCase): self.assertIs(None, distribution_strategy_context.get_replica_context()) self.assertIs(dist, distribution_strategy_context.get_cross_replica_context()) + self.assertTrue(distribution_strategy_context.in_cross_replica_context()) self.assertTrue(distribution_strategy_context.has_distribution_strategy()) self.assertIs(dist, distribution_strategy_context.get_distribution_strategy()) @@ -131,6 +144,7 @@ class DefaultDistributionStrategyTest(test.TestCase): self.assertIs(None, distribution_strategy_context.get_replica_context()) self.assertIs(dist, distribution_strategy_context.get_cross_replica_context()) + self.assertTrue(distribution_strategy_context.in_cross_replica_context()) self.assertIs(dist, distribution_strategy_context.get_distribution_strategy()) self.assertFalse( @@ -140,9 +154,26 @@ class DefaultDistributionStrategyTest(test.TestCase): replica_ctx = distribution_strategy_context.get_replica_context() self.assertIs(distribution_strategy_context._get_default_replica_context(), replica_ctx) - self.assertEqual("foo_bar", replica_ctx.merge_call(merge_fn, "bar")) + self.assertEqual("foo_bar", replica_ctx.merge_call(merge_fn, args=("bar",))) _assert_in_default_state(self) +class InputContextTest(test.TestCase): + + def testProperties(self): + input_context = distribute_lib.InputContext( + num_input_pipelines=2, input_pipeline_id=1, num_replicas_in_sync=6) + self.assertEqual(6, input_context.num_replicas_in_sync) + self.assertEqual(1, input_context.input_pipeline_id) + self.assertEqual(2, input_context.num_input_pipelines) + + def testPerReplicaBatchSize(self): + input_context = distribute_lib.InputContext( + num_input_pipelines=2, input_pipeline_id=1, num_replicas_in_sync=6) + self.assertEqual(2, input_context.get_per_replica_batch_size(12)) + with self.assertRaises(ValueError): + input_context.get_per_replica_batch_size(13) + + if __name__ == "__main__": test.main() diff --git a/tensorflow/python/distribute/distribution_strategy_context.py b/tensorflow/python/distribute/distribution_strategy_context.py new file mode 100644 index 0000000000..78e096e286 --- /dev/null +++ b/tensorflow/python/distribute/distribution_strategy_context.py @@ -0,0 +1,236 @@ +# Copyright 2018 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. +# ============================================================================== +"""Utility to get distribution strategy related contexts.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.framework import ops +from tensorflow.python.util.lazy_loader import LazyLoader +from tensorflow.python.util.tf_export import tf_export + + +# There is a circular dependency between this and `distribute` module. So we +# load it lazily to workaround this. +distribute_lib = LazyLoader( + "distribute_lib", globals(), + "tensorflow.python.distribute.distribute_lib") + +# ------------------------------------------------------------------------------ +# Internal API for setting the current thread mode as being either in a +# replica or cross-replica context for a particular distribution strategy. + + +class _ThreadMode(object): + + def __init__(self, dist, cross, replica): + self.distribution_strategy = dist + self.cross_replica_context = cross + self.replica_context = replica + + +class _CrossReplicaThreadMode(_ThreadMode): + + def __init__(self, distribution_strategy): + _ThreadMode.__init__( + self, distribution_strategy, distribution_strategy, None) + + +class _InReplicaThreadMode(_ThreadMode): + + def __init__(self, replica_ctx): + _ThreadMode.__init__( + self, replica_ctx.distribution_strategy, None, replica_ctx) + + +def _push_per_thread_mode(context): + ops.get_default_graph()._distribution_strategy_stack.append(context) # pylint: disable=protected-access + + +def _pop_per_thread_mode(): + ops.get_default_graph()._distribution_strategy_stack.pop(-1) # pylint: disable=protected-access + + +class _DefaultReplicaThreadMode(_ThreadMode): + """Type of default value returned by `_get_per_thread_mode()`. + + Used when the thread-local stack is empty. + """ + + def __init__(self): + _ThreadMode.__init__(self, _get_default_distribution_strategy(), None, + _get_default_replica_context()) + + +def _get_per_thread_mode(): + try: + return ops.get_default_graph()._distribution_strategy_stack[-1] # pylint: disable=protected-access + except (AttributeError, IndexError): + return _get_default_replica_mode() + + +# ------------------------------------------------------------------------------ +# Public API for accessing the current thread mode + + +@tf_export("distribute.get_replica_context") +def get_replica_context(): + """Returns the current `tf.distribute.ReplicaContext` or `None`. + + Returns `None` if in a cross-replica context. + + Note that execution: + + 1. starts in the default (single-replica) replica context (this function + will return the default `ReplicaContext` object); + 2. switches to cross-replica context (in which case this will return + `None`) when entering a `with tf.distribute.Strategy.scope():` block; + 3. switches to a (non-default) replica context inside + `extended.call_for_each_replica(fn, ...)`; + 4. if `fn` calls `get_replica_context().merge_call(merge_fn, ...)`, then + inside `merge_fn` you are back in the cross-replica context (and again + this function will return `None`). + + Note that you can also go directly from step 1 to 4 to switch to a + cross-replica context for the default `tf.distribute.Strategy`. You may + also switch from the cross-replica context of 4 to a replica context by + calling `extended.call_for_each_replica()`, jumping back to step 3. + + Most `tf.distribute.Strategy` methods may only be executed in + a cross-replica context, in a replica context you should use the + `ReplicaContext` API instead. + + Returns: + The current `ReplicaContext` object when in a replica context scope, + else `None`. + + Within a particular block, exactly one of these two things will be true: + + * `get_replica_context()` returns non-`None`, or + * `tf.distribute.is_cross_replica_context()` returns True. + """ + return _get_per_thread_mode().replica_context + + +def get_cross_replica_context(): + """Returns the current tf.distribute.Strategy if in a cross-replica context. + + DEPRECATED: Please use `in_cross_replica_context()` and + `get_distribution_strategy()` instead. + + Note that execution: + + 1. starts in the default (single-replica) replica context; + 2. switches to cross-replica context when entering a + `with tf.distribute.Strategy.scope():` block; + 3. switches to a (non-default) replica context inside + `call_for_each_replica(fn, ...)`; + 4. if `fn` calls `get_replica_context()->merge_call(merge_fn, ...)`, then + inside `merge_fn` you are back in the cross-replica context. + + Note that you can also go directly from step 1 to 4 to switch to a + cross-replica context for the default `tf.distribute.Strategy`. You may + also switch from the cross-replica context of 4 to a replica context by + calling `call_for_each_replica()`, jumping back to step 3. + + Most `tf.distribute.Strategy` methods may only be executed in + a cross-replica context. + + Returns: + Returns the current `tf.distribute.Strategy` object in a cross-replica + context, or `None`. + + Exactly one of `get_replica_context()` and `get_cross_replica_context()` + will return `None` in a particular block. + """ + return _get_per_thread_mode().cross_replica_context + + +@tf_export("distribute.in_cross_replica_context") +def in_cross_replica_context(): + """Returns True if in a cross-replica context. + + See `tf.distribute.get_replica_context` for details. + + Returns: + True if in a cross-replica context (`get_replica_context()` returns + `None`), or False if in a replica context (`get_replica_context()` returns + non-`None`). + """ + return _get_per_thread_mode().cross_replica_context is not None + + +@tf_export("distribute.get_strategy") +def get_distribution_strategy(): + """Returns the current `tf.distribute.Strategy` object. + + Typically only used in a cross-replica context: + + ``` + if tf.distribute.in_cross_replica_context(): + strategy = tf.distribute.get_strategy() + ... + ``` + + Returns: + A `tf.distribute.Strategy` object. Inside a + `with distribution_strategy.scope()` block, it returns + `distribution_strategy`, otherwise it returns the default + (single-replica) `tf.distribute.Strategy` object. + """ + return _get_per_thread_mode().distribution_strategy + + +@tf_export("distribute.has_strategy") +def has_distribution_strategy(): + """Return if there is a current non-default `tf.distribute.Strategy`. + + Returns: + True if inside a `with strategy.scope():`. + """ + return get_distribution_strategy() is not _get_default_distribution_strategy() + + +# ------------------------------------------------------------------------------ +# Defaults that are used when no distribution strategy is explicitly created. +# We create them lazily in a function so that we can workaround the circular +# dependency on distribute_lib. See lazy loader at the top of this file. + +_defaults = { + "distribution_strategy": None, + "replica_context": None, + "replica_mode": None +} + + +def _get_default_distribution_strategy(): + if _defaults["distribution_strategy"] is None: + _defaults["distribution_strategy"] = ( + distribute_lib._DefaultDistributionStrategy()) # pylint: disable=protected-access + return _defaults["distribution_strategy"] + + +def _get_default_replica_context(): + if _defaults["replica_context"] is None: + _defaults["replica_context"] = distribute_lib.ReplicaContext( + _get_default_distribution_strategy(), replica_id_in_sync_group=0) + return _defaults["replica_context"] + + +def _get_default_replica_mode(): + if _defaults["replica_mode"] is None: + _defaults["replica_mode"] = _DefaultReplicaThreadMode() + return _defaults["replica_mode"] diff --git a/tensorflow/python/distribute/estimator_training.py b/tensorflow/python/distribute/estimator_training.py index 227b00fb3e..549fa8fb8a 100644 --- a/tensorflow/python/distribute/estimator_training.py +++ b/tensorflow/python/distribute/estimator_training.py @@ -308,7 +308,7 @@ def estimator_train(estimator, train_distributed_fn, hooks): raise ValueError('Only `STANDALONE_CLIENT` mode is supported when you call ' '`estimator.train`') - if estimator._config._train_distribute.between_graph: + if estimator._config._train_distribute.extended.experimental_between_graph: # TODO(yuefengz): remove this limitation once we figure out how to merge # return values from `_worker_fn`s. raise ValueError('`Estimator.train` API is not supported for %s with ' @@ -356,7 +356,7 @@ def estimator_evaluate(estimator, evaluate_distributed_fn, hooks): raise ValueError('Only `STANDALONE_CLIENT` mode is supported when you call ' '`Estimator.train`') - if estimator._config._eval_distribute.between_graph: + if estimator._config._eval_distribute.extended.experimental_between_graph: # TODO(yuefengz): remove this limitation once we figure out how to merge # return values from `_worker_fn`s. raise ValueError('`Estimator.evaluate` API is not supported for %s with ' diff --git a/tensorflow/contrib/distribute/python/input_ops.py b/tensorflow/python/distribute/input_ops.py similarity index 89% rename from tensorflow/contrib/distribute/python/input_ops.py rename to tensorflow/python/distribute/input_ops.py index ac1ccd64b3..2ded209701 100644 --- a/tensorflow/contrib/distribute/python/input_ops.py +++ b/tensorflow/python/distribute/input_ops.py @@ -18,6 +18,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +from tensorflow.python.data.experimental.ops import filter_for_shard_ops from tensorflow.python.data.ops import dataset_ops from tensorflow.python.data.ops import readers from tensorflow.python.data.util import nest @@ -41,7 +42,8 @@ def auto_shard_dataset(dataset, num_shards, index): dataset: A `tf.data.Dataset` instance, typically the result of a bunch of dataset transformations. num_shards: A `tf.int64` scalar `tf.Tensor`, representing the number of - shards operating in parallel. Same usage as in `Dataset.shard`. + shards operating in parallel. Same usage as in + `tf.data.experimental.filter_for_shard`. index: A `tf.int64` scalar `tf.Tensor`, representing the worker index. Same usage as in `Dataset.shard`. @@ -74,13 +76,15 @@ def auto_shard_dataset(dataset, num_shards, index): # constructor. Eventually we will change all cases to clone datasets # instead of updating in-place. return dataset._clone( - filenames=dataset._filenames.shard(num_shards, index)) + filenames=dataset._filenames.apply( + filter_for_shard_ops.filter_for_shard(num_shards, index))) elif isinstance(dataset, dataset_ops.RangeDataset): - return dataset.shard(num_shards, index) + return dataset.apply( + filter_for_shard_ops.filter_for_shard(num_shards, index)) elif hasattr(dataset, "_map_func"): # TODO(priyag): Make this check more robust by enforcing some common # property on all map/flatmap/interleave datasets. - map_func_def = dataset._map_func.definition + map_func_def = dataset._map_func.function.definition for node in map_func_def.node_def: if node.op in _READER_DATASET_OPS: found_reader_op = True @@ -102,6 +106,11 @@ def auto_shard_dataset(dataset, num_shards, index): dataset._input_dataset, found_reader_op) return dataset + if isinstance(dataset, dataset_ops.DatasetV1Adapter): + dataset._dataset = _auto_shard_impl( + dataset._dataset, found_reader_op) + return dataset + # TODO(priyag): Make _input_dataset(s) a common property of all datasets to # make this check more robust. if hasattr(dataset, "_input_dataset"): @@ -137,6 +146,7 @@ def auto_shard_dataset(dataset, num_shards, index): # TODO(priyag): This will shard the filenames before any shuffling of the # filename dataset. It might be desirable to shard after shuffling # filenames? If so, how do we achieve that? - return dataset.shard(num_shards, index) + return dataset.apply( + filter_for_shard_ops.filter_for_shard(num_shards, index)) return _auto_shard_impl(dataset=dataset, found_reader_op=False) diff --git a/tensorflow/contrib/distribute/python/input_ops_test.py b/tensorflow/python/distribute/input_ops_test.py similarity index 89% rename from tensorflow/contrib/distribute/python/input_ops_test.py rename to tensorflow/python/distribute/input_ops_test.py index 559de97bb1..dcf946ba47 100644 --- a/tensorflow/contrib/distribute/python/input_ops_test.py +++ b/tensorflow/python/distribute/input_ops_test.py @@ -20,10 +20,11 @@ from __future__ import print_function import os -from tensorflow.contrib.distribute.python import input_ops from tensorflow.python.data.ops import dataset_ops from tensorflow.python.data.ops import readers +from tensorflow.python.distribute import input_ops from tensorflow.python.framework import errors +from tensorflow.python.framework import test_util from tensorflow.python.lib.io import python_io from tensorflow.python.platform import test from tensorflow.python.util import compat @@ -92,10 +93,11 @@ class AutoShardDatasetTest(test.TestCase): with self.cached_session() as sess: for f in range(self._shard_index, self._num_files, self._num_shards): for r in range(self._num_records): - self.assertAllEqual(record_fn(r, f), sess.run(next_element)) + self.assertAllEqual(record_fn(r, f), self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) + @test_util.run_deprecated_v1 def testTFRecordDataset(self): dataset = readers.TFRecordDataset(self._createTFRecordFiles()) dataset = input_ops.auto_shard_dataset( @@ -103,6 +105,7 @@ class AutoShardDatasetTest(test.TestCase): self._verifySimpleShardingOutput(dataset, self._record) + @test_util.run_deprecated_v1 def testFlatMap(self): dataset = dataset_ops.Dataset.from_tensor_slices( self._createTFRecordFiles()) @@ -112,6 +115,7 @@ class AutoShardDatasetTest(test.TestCase): self._verifySimpleShardingOutput(dataset, self._record) + @test_util.run_deprecated_v1 def testInterleave(self): dataset = dataset_ops.Dataset.from_tensor_slices( self._createTFRecordFiles()) @@ -124,9 +128,10 @@ class AutoShardDatasetTest(test.TestCase): # contain records in order of files. self._verifySimpleShardingOutput(dataset, self._record) + @test_util.run_deprecated_v1 def testListfiles(self): filenames = self._createTFRecordFiles() - file_pattern = filenames[0].rsplit("/", 1)[0] + "/tf_record.*.txt" + file_pattern = filenames[0].rsplit(os.sep, 1)[0] + "/tf_record.*.txt" dataset = dataset_ops.Dataset.list_files(file_pattern, shuffle=False) dataset = dataset.flat_map(readers.TFRecordDataset) dataset = input_ops.auto_shard_dataset( @@ -138,12 +143,13 @@ class AutoShardDatasetTest(test.TestCase): actual, expected = [], [] for f in range(self._shard_index, self._num_files, self._num_shards): for r in range(self._num_records): - actual.append(sess.run(next_element)) + actual.append(self.evaluate(next_element)) expected.append(self._record(r, f)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) self.assertAllEqual(expected, actual) + @test_util.run_deprecated_v1 def testComplexPipeline(self): # Setup a complex input pipeline. batch_size = 2 @@ -171,9 +177,9 @@ class AutoShardDatasetTest(test.TestCase): num_iterations = (self._num_files * self._num_records * num_epochs) // ( self._num_shards * batch_size) for _ in range(num_iterations): - actual.extend(sess.run(next_element)) + actual.extend(self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) expected = [] for f in range(0, self._num_files, self._num_shards): @@ -183,6 +189,7 @@ class AutoShardDatasetTest(test.TestCase): self.assertAllEqual(sorted(expected), sorted(actual)) + @test_util.run_deprecated_v1 def testZip(self): dataset1 = readers.TFRecordDataset(self._createTFRecordFiles()) dataset2 = readers.TextLineDataset(self._createTextFiles()) @@ -193,6 +200,7 @@ class AutoShardDatasetTest(test.TestCase): record_fn = lambda r, f: (self._record(r, f), self._text_line(r, f)) self._verifySimpleShardingOutput(dataset, record_fn) + @test_util.run_deprecated_v1 def testConcat(self): dataset1 = readers.TFRecordDataset(self._createTFRecordFiles()) dataset2 = readers.TextLineDataset(self._createTextFiles()) @@ -205,13 +213,15 @@ class AutoShardDatasetTest(test.TestCase): with self.cached_session() as sess: for f in range(self._shard_index, self._num_files, self._num_shards): for r in range(self._num_records): - self.assertAllEqual(self._record(r, f), sess.run(next_element)) + self.assertAllEqual(self._record(r, f), self.evaluate(next_element)) for f in range(self._shard_index, self._num_files, self._num_shards): for r in range(self._num_records): - self.assertAllEqual(self._text_line(r, f), sess.run(next_element)) + self.assertAllEqual( + self._text_line(r, f), self.evaluate(next_element)) with self.assertRaises(errors.OutOfRangeError): - sess.run(next_element) + self.evaluate(next_element) + @test_util.run_deprecated_v1 def testTextLineReader(self): dataset = readers.TextLineDataset(self._createTextFiles()) dataset = input_ops.auto_shard_dataset( @@ -219,6 +229,7 @@ class AutoShardDatasetTest(test.TestCase): self._verifySimpleShardingOutput(dataset, self._text_line) + @test_util.run_deprecated_v1 def testTextLineReaderWithFlatMap(self): dataset = dataset_ops.Dataset.from_tensor_slices(self._createTextFiles()) dataset = dataset.flat_map(readers.TextLineDataset) @@ -227,6 +238,7 @@ class AutoShardDatasetTest(test.TestCase): self._verifySimpleShardingOutput(dataset, self._text_line) + @test_util.run_deprecated_v1 def testFixedLengthReader(self): dataset = readers.FixedLengthRecordDataset( self._createFixedLengthRecordFiles(), self._record_bytes) @@ -235,6 +247,7 @@ class AutoShardDatasetTest(test.TestCase): self._verifySimpleShardingOutput(dataset, self._fixed_length_record) + @test_util.run_deprecated_v1 def testFixedLengthReaderWithFlatMap(self): dataset = dataset_ops.Dataset.from_tensor_slices( self._createFixedLengthRecordFiles()) diff --git a/tensorflow/python/distribute/mirrored_strategy.py b/tensorflow/python/distribute/mirrored_strategy.py new file mode 100644 index 0000000000..0775920a7d --- /dev/null +++ b/tensorflow/python/distribute/mirrored_strategy.py @@ -0,0 +1,908 @@ +# Copyright 2018 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. +# ============================================================================== +"""Class MirroredStrategy implementing DistributionStrategy.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import contextlib +import copy +import functools +import threading + +from tensorflow.python import pywrap_tensorflow +from tensorflow.python.distribute import cross_device_ops as cross_device_ops_lib +from tensorflow.python.distribute import device_util +from tensorflow.python.distribute import distribute_lib +from tensorflow.python.distribute import multi_worker_util +from tensorflow.python.distribute import reduce_util +from tensorflow.python.distribute import shared_variable_creator +from tensorflow.python.distribute import values +from tensorflow.python.eager import context +from tensorflow.python.eager import tape +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import device as tf_device +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops +from tensorflow.python.framework import tensor_util +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import control_flow_ops +from tensorflow.python.ops import variable_scope +from tensorflow.python.training import coordinator +from tensorflow.python.util import nest + + +# TODO(josh11b): Replace asserts in this file with if ...: raise ... + + +@contextlib.contextmanager +def _enter_graph(g): + if context.executing_eagerly(): + with g.as_default(), context.eager_mode(): + yield + else: + with g.as_default(): + yield + + +def _cpu_device(device): + cpu_device = tf_device.DeviceSpec.from_string(device) + cpu_device.merge_from(tf_device.DeviceSpec(device_type="CPU", device_index=0)) + return cpu_device.to_string() + + +class _RequestedStop(Exception): # pylint: disable=g-bad-exception-name + pass + + +# _call_for_each_replica and _reduce_non_distributed_value are not members of +# MirroredStrategy so that they are generally not allowed to use anything +# specific to MirroredStrategy and thus can be shared with other distribution +# strategies. + + +# TODO(yuefengz): maybe create a common class for those who need to call this +# _call_for_each_replica. +def _call_for_each_replica(distribution, fn, args, kwargs): + """Run `fn` in separate threads, once per replica/worker device. + + Args: + distribution: the DistributionStrategy object. + fn: function to run (will be run once per device, each in its own thread). + args: positional arguments for `fn` + kwargs: keyword arguments for `fn`. + + Returns: + Merged return value of `fn` across all replicas. + + Raises: + RuntimeError: If fn() calls get_replica_context().merge_call() a different + number of times from the available devices. + """ + # TODO(josh11b): Add this option once we add synchronization to variable + # creation. Until then, this is pretty unsafe to use. + run_concurrently = False + if not context.executing_eagerly(): + # Needed for per-thread device, etc. contexts in graph mode. + ops.get_default_graph().switch_to_thread_local() + + coord = coordinator.Coordinator(clean_stop_exception_types=(_RequestedStop,)) + + shared_variable_store = {} + + # TODO(isaprykin): Create these threads once instead of during every run() + # call. + threads = [] + for index, d in enumerate(distribution.extended.worker_devices): + variable_creator_fn = shared_variable_creator.make_fn( + shared_variable_store, index) + t = MirroredExtended._MirroredReplicaThread( # pylint: disable=protected-access + distribution, coord, d, variable_creator_fn, fn, + *values.select_device(d, args), **values.select_device(d, kwargs)) + threads.append(t) + + for t in threads: + t.start() + + # When `fn` starts `should_run` event is set on _MirroredReplicaThread + # (`MRT`) threads. The execution waits until + # `MRT.has_paused` is set, which indicates that either `fn` is + # complete or a `get_replica_context().merge_call()` is called. If `fn` is + # complete, then `MRT.done` is set to True. Otherwise, arguments + # of `get_replica_context().merge_call` from all paused threads are grouped + # and the `merge_fn` is performed. Results of the + # `get_replica_context().merge_call` are then set to `MRT.merge_result`. + # Each such `get_replica_context().merge_call` call returns the + # `MRT.merge_result` for that thread when `MRT.should_run` event + # is reset again. Execution of `fn` resumes. + + try: + with coord.stop_on_exception(): + all_done = False + while not all_done and not coord.should_stop(): + done = [] + if run_concurrently: + for t in threads: + t.should_run.set() + for t in threads: + t.has_paused.wait() + t.has_paused.clear() + if coord.should_stop(): + return None + done.append(t.done) + else: + for t in threads: + t.should_run.set() + t.has_paused.wait() + t.has_paused.clear() + if coord.should_stop(): + return None + done.append(t.done) + if coord.should_stop(): + return None + all_done = all(done) + if not all_done: + if any(done): + raise RuntimeError("Some replicas made a different number of " + "replica_context().merge_call() calls.") + # get_replica_context().merge_call() case + merge_args = values.regroup({t.device: t.merge_args for t in threads}) + merge_kwargs = values.regroup( + {t.device: t.merge_kwargs for t in threads}) + # We capture the name_scope of the MRT when we call merge_fn + # to ensure that if we have opened a name scope in the MRT, + # it will be respected when executing the merge function. We only + # capture the name_scope from the first MRT and assume it is + # the same for all other MRTs. + mtt_captured_name_scope = threads[0].captured_name_scope + with ops.name_scope(mtt_captured_name_scope): + merge_result = threads[0].merge_fn(distribution, *merge_args, + **merge_kwargs) + for t in threads: + t.merge_result = values.select_device(t.device, merge_result) + finally: + for t in threads: + t.should_run.set() + coord.join(threads) + + return values.regroup({t.device: t.main_result for t in threads}) + + +def _reduce_non_distributed_value(extended, reduce_op, value, destinations): + """Reduce a non-DistributedValue `value` to `destinations`.""" + if isinstance(value, values.DistributedValues): + raise ValueError("You are passing a `DistributedValue` to " + "`_reduce_non_distributed_value`, which is not allowed.") + + # If the same value is present on all replicas then the PerReplica value will + # be a single value. We also handle the case when `value` is a single value + # and equal to 0. + if value == 0: + return 0 + # If there is only a single value and the reduce op is MEAN, + # that value should be on all destinations. + if reduce_op == reduce_util.ReduceOp.MEAN: + return value + + cross_device_ops_lib.validate_destinations(destinations) + # We do not support a reduce op of SUM if the value is the same across + # all replicas. We call this as part of assign functions for MirroredVariables + # and summing up identical values across replicas is not clearly defined. + if (len(extended.worker_devices) != 1 or + not cross_device_ops_lib.check_destinations(destinations)): + raise ValueError("A non-DistributedValues value %s cannot be reduced with " + "the given reduce op %s." % (value, reduce_op)) + # TODO(anjalisridhar): Moves these methods to a device utility file? + devices = cross_device_ops_lib.get_devices_from(destinations) + if len(devices) == 1: + with ops.device(devices[0]): + return array_ops.identity(value) + else: + value_updates = {} + for d in devices: + with ops.device(d): + value_updates[d] = array_ops.identity(value) + return values.Mirrored(value_updates) + + +def _create_mirrored_variable(devices, real_mirrored_creator, *args, **kwargs): # pylint: disable=g-missing-docstring + # Figure out what collections this variable should be added to. + # We'll add the MirroredVariable to those collections instead. + collections = kwargs.pop("collections", None) + if collections is None: + collections = [ops.GraphKeys.GLOBAL_VARIABLES] + kwargs["collections"] = [] + + # Get synchronization value + synchronization = kwargs.get("synchronization", + variable_scope.VariableSynchronization.ON_WRITE) + if synchronization == variable_scope.VariableSynchronization.NONE: + raise ValueError("`NONE` variable synchronization mode is not " + "supported with `Mirrored` distribution strategy. Please" + " change the `synchronization` for variable: " + + kwargs["name"]) + elif synchronization == variable_scope.VariableSynchronization.ON_READ: + # Variables that are to be synced on read are replica local. + is_replica_local = True + kwargs["trainable"] = False + elif (synchronization == variable_scope.VariableSynchronization.ON_WRITE or + synchronization == variable_scope.VariableSynchronization.AUTO): + # `AUTO` synchronization for `MirroredStrategy` is `ON_WRITE`. + is_replica_local = False + else: + raise ValueError("Invalid variable synchronization mode: " + + synchronization + " for variable: " + kwargs["name"]) + + # Get aggregation value + aggregation = kwargs.pop("aggregation", + variable_scope.VariableAggregation.NONE) + if aggregation not in ( + variable_scope.VariableAggregation.NONE, + variable_scope.VariableAggregation.SUM, + variable_scope.VariableAggregation.MEAN, + variable_scope.VariableAggregation.ONLY_FIRST_REPLICA + ): + raise ValueError("Invalid variable aggregation mode: " + aggregation + + " for variable: " + kwargs["name"]) + + # Ignore user-specified caching device, not needed for mirrored variables. + kwargs.pop("caching_device", None) + + # TODO(josh11b,apassos): It would be better if variable initialization + # was never recorded on the tape instead of having to do this manually + # here. + with tape.stop_recording(): + index = real_mirrored_creator(devices, *args, **kwargs) + + if is_replica_local: + result = values.ReplicaLocalVariable( + index, index[devices[0]], aggregation) + else: + result = values.MirroredVariable(index, index[devices[0]], aggregation) + + # Add the wrapped variable to the requested collections. + # The handling of eager mode and the global step matches + # ResourceVariable._init_from_args(). + if not context.executing_eagerly(): + g = ops.get_default_graph() + # If "trainable" is True, next_creator() will add the member variables + # to the TRAINABLE_VARIABLES collection, so we manually remove + # them and replace with the MirroredVariable. We can't set + # "trainable" to False for next_creator() since that causes functions + # like implicit_gradients to skip those variables. + if kwargs.get("trainable", True): + collections.append(ops.GraphKeys.TRAINABLE_VARIABLES) + l = g.get_collection_ref(ops.GraphKeys.TRAINABLE_VARIABLES) + for v in index.values(): + if v in l: + l.remove(v) + g.add_to_collections(collections, result) + elif ops.GraphKeys.GLOBAL_STEP in collections: + ops.add_to_collections(ops.GraphKeys.GLOBAL_STEP, result) + + return result + + +def _is_device_list_local(devices): + """Checks whether the devices list is for local or multi-worker. + + Args: + devices: a list of device strings, either local for remote devices. + + Returns: + a boolean indicating whether these device strings are for local or for + remote. + + Raises: + ValueError: if device strings are not consistent. + """ + all_local = None + for d in devices: + d_spec = tf_device.DeviceSpec().parse_from_string(d) + is_local = d_spec.job in (None, "localhost") + + if all_local is None: # Determine all_local from first device. + all_local = is_local + + if all_local: + if not is_local: + raise ValueError("Local device string cannot have job specified other " + "than 'localhost'") + else: + if is_local: + raise ValueError("Remote device string must have job specified.") + if d_spec.task is None: + raise ValueError("Remote device string must have task specified.") + return all_local + + +def _cluster_spec_to_device_list(cluster_spec, num_gpus_per_worker): + """Returns a device list given a cluster spec.""" + cluster_spec = multi_worker_util.normalize_cluster_spec(cluster_spec) + devices = [] + for task_type in ("chief", "worker"): + for task_id in range(len(cluster_spec.as_dict().get(task_type, []))): + if num_gpus_per_worker is 0: + devices.append("/job:%s/task:%d" % (task_type, task_id)) + else: + devices.extend([ + "/job:%s/task:%d/device:GPU:%i" % (task_type, task_id, gpu_id) + for gpu_id in range(num_gpus_per_worker) + ]) + return devices + + +def _group_device_list(devices): + """Groups the devices list by task_type and task_id. + + Args: + devices: a list of device strings for remote devices. + + Returns: + a dict of list of device strings mapping from task_type to a list of devices + for the task_type in the asceding order of task_id. + """ + assert not _is_device_list_local(devices) + device_dict = {} + + for d in devices: + d_spec = tf_device.DeviceSpec().parse_from_string(d) + + # Create an entry for the task_type. + if d_spec.job not in device_dict: + device_dict[d_spec.job] = [] + + # Fill the device list for task_type until it covers the task_id. + while len(device_dict[d_spec.job]) <= d_spec.task: + device_dict[d_spec.job].append([]) + + device_dict[d_spec.job][d_spec.task].append(d) + + return device_dict + + +def _infer_num_gpus_per_worker(devices): + """Infers the number of GPUs on each worker. + + Currently to make multi-worker cross device ops work, we need all workers to + have the same number of GPUs. + + Args: + devices: a list of device strings, can be either local devices or remote + devices. + + Returns: + number of GPUs per worker. + + Raises: + ValueError if workers have different number of GPUs or GPU indices are not + consecutive and starting from 0. + """ + if _is_device_list_local(devices): + return len([d for d in devices if "GPU" in d.upper()]) + else: + device_dict = _group_device_list(devices) + num_gpus = None + for _, devices_in_task in device_dict.items(): + for device_in_task in devices_in_task: + if num_gpus is None: + num_gpus = len([d for d in device_in_task if "GPU" in d.upper()]) + + # Verify other workers have the same number of GPUs. + elif ( + num_gpus != len([d for d in device_in_task if "GPU" in d.upper()])): + raise ValueError("All workers should have the same number of GPUs.") + + for d in device_in_task: + d_spec = tf_device.DeviceSpec().parse_from_string(d) + if (d_spec.device_type.upper() == "GPU" and + d_spec.device_index >= num_gpus): + raise ValueError("Device_index on a worker should be consecutive " + "and start from 0.") + return num_gpus + + +def all_local_devices(num_gpus=None): + if num_gpus is None: + num_gpus = context.num_gpus() + return (tuple("/device:GPU:%d" % i for i in range(num_gpus)) or + ("/device:CPU:0",)) + + +class MirroredStrategy(distribute_lib.DistributionStrategy): + """Mirrors vars to distribute across multiple devices and machines. + + This strategy uses one replica per device and sync replication for its + multi-GPU version. + + The multi-worker version will be added in the fture. + + Args: + devices: a list of device strings. + cross_device_ops: optional, a descedant of `CrossDeviceOps`. If this is not + set, nccl will be use by default. + """ + + def __init__(self, devices=None, cross_device_ops=None): + extended = MirroredExtended( + self, devices=devices, cross_device_ops=cross_device_ops) + super(MirroredStrategy, self).__init__(extended) + + +class MirroredExtended(distribute_lib.DistributionStrategyExtended): + """Implementation of MirroredStrategy.""" + + def __init__(self, container_strategy, devices=None, cross_device_ops=None): + super(MirroredExtended, self).__init__(container_strategy) + if devices is None: + devices = all_local_devices() + if not devices: + raise ValueError("Got an empty `devices` list. Please make sure the " + "`devices` you pass in is not empty.") + self._cross_device_ops = cross_device_ops + self._initialize_strategy(devices) + + def _initialize_strategy(self, devices): + # The _initialize_strategy method is intended to be used by distribute + # coordinator as well. + if _is_device_list_local(devices): + self._initialize_local(devices) + else: + self._initialize_multi_worker(devices) + + def _initialize_local(self, devices): + """Initializes the object for local training.""" + self._local_mode = True + assert devices, "Must specify at least one device." + assert len(set(devices)) == len(devices), ( + "No duplicates allowed in `devices` argument.") + # TODO(josh11b): Require at least 2 devices? + self._devices = [device_util.resolve(d) for d in devices] + self._canonical_device_set = set(self._devices) + self._device_index = values.PerReplica( + {d: i for i, d in enumerate(devices)}) + + self._inferred_cross_device_ops = cross_device_ops_lib.choose_the_best( + devices) + + def _initialize_multi_worker(self, devices): + """Initializes the object for multi-worker training.""" + self._local_mode = False + + assert devices, "Must specify at least one device." + assert len(set(devices)) == len(devices), ( + "No duplicates allowed in `devices` argument.") + # TODO(josh11b): Require at least 2 devices? + self._devices = [device_util.resolve(d) for d in devices] + self._canonical_device_set = set(self._devices) + self._device_index = values.PerReplica( + {d: i for i, d in enumerate(devices)}) + + device_dict = _group_device_list(devices) + self._workers = [] + self._worker_devices = [] + for job in ["chief", "worker"]: + for task in range(len(device_dict.get(job, []))): + worker = "/job:%s/task:%d" % (job, task) + self._workers.append(worker) + self._worker_devices.append((worker, device_dict[job][task])) + + # Setting `_default_device` will add a device scope in the + # distribution.scope. We set the default device to the first worker. When + # users specify device under distribution.scope by + # with tf.device("/cpu:0"): + # ... + # their ops will end up on the cpu device of its first worker, e.g. + # "/job:worker/task:0/device:CPU:0". Note this is not used in replica mode. + self._default_device = self._workers[0] + + self._inferred_cross_device_ops = cross_device_ops_lib.MultiWorkerAllReduce( + self._workers, _infer_num_gpus_per_worker(self._devices)) + + def _create_variable(self, next_creator, *args, **kwargs): + """Create a mirrored variable. See `DistributionStrategy.scope`.""" + colocate_with = kwargs.pop("colocate_with", None) + devices = self._get_devices_from(colocate_with) + + def _real_mirrored_creator(devices, *args, **kwargs): # pylint: disable=g-missing-docstring + index = {} + for i, d in enumerate(devices): + with ops.init_scope(), ops.device(d): + if i > 0: + # Give replicas meaningful distinct names: + var0name = index[devices[0]].name.split(":")[0] + # We append a / to variable names created on replicas with id > 0 to + # ensure that we ignore the name scope and instead use the given + # name as the absolute name of the variable. + kwargs["name"] = "%s/replica_%d/" % (var0name, i) + # Initialize replicas with the same value: + def initial_value_fn(device=d): + if context.executing_eagerly(): + init_value = index[devices[0]].value() + return array_ops.identity(init_value) + else: + with ops.device(device): + init_value = index[devices[0]].initial_value + return array_ops.identity(init_value) + kwargs["initial_value"] = initial_value_fn + with context.context().device_policy(context.DEVICE_PLACEMENT_SILENT): + # Don't record operations (e.g. other variable reads) during + # variable creation. + with tape.stop_recording(): + v = next_creator(*args, **kwargs) + assert not isinstance(v, values.DistributedVariable) + index[d] = v + return index + + return _create_mirrored_variable(devices, _real_mirrored_creator, *args, + **kwargs) + + def _distribute_dataset(self, dataset_fn): + if self._local_mode: + return values.PerReplicaDataset( + self._call_dataset_fn(dataset_fn), self._devices) + else: + return values.MultiWorkerDataset( + functools.partial(self._call_dataset_fn, dataset_fn), + self._worker_devices, + auto_shard=False) + + def _make_dataset_iterator(self, dataset): + if self._local_mode: + worker = device_util.canonicalize("/device:CPU:0") + worker_device_pairs = [(worker, self._devices)] + else: + worker_device_pairs = self._worker_devices + + return values.DatasetIterator(dataset, worker_device_pairs, + self._num_replicas_in_sync) + + def _make_input_fn_iterator( + self, + input_fn, + replication_mode=distribute_lib.InputReplicationMode.PER_WORKER): + input_contexts = [] + if self._local_mode: + num_workers = 1 + worker = device_util.canonicalize("/device:CPU:0") + worker_device_pairs = [(worker, self._devices)] + else: + num_workers = len(self._worker_devices) + worker_device_pairs = self._worker_devices + + for i in range(num_workers): + input_contexts.append(distribute_lib.InputContext( + num_input_pipelines=num_workers, + input_pipeline_id=i, + num_replicas_in_sync=self._num_replicas_in_sync)) + return values.InputFunctionIterator( + input_fn, worker_device_pairs, input_contexts) + + # TODO(priyag): Deal with OutOfRange errors once b/111349762 is fixed. + def _experimental_run_steps_on_iterator(self, fn, iterator, iterations, + initial_loop_values=None): + if initial_loop_values is None: + initial_loop_values = {} + initial_loop_values = nest.flatten(initial_loop_values) + + ctx = values.MultiStepContext() + def body(i, *args): + """A wrapper around `fn` to create the while loop body.""" + del args + fn_inputs = iterator.get_next() + if not isinstance(fn_inputs, tuple): + fn_inputs = (fn_inputs,) + fn_result = fn(ctx, fn_inputs) + for (name, output) in ctx.last_step_outputs.items(): + # Convert all outputs to tensors, potentially from `DistributedValues`. + ctx.last_step_outputs[name] = self._unwrap(output) + flat_last_step_outputs = nest.flatten(ctx.last_step_outputs) + with ops.control_dependencies([fn_result]): + return [i + 1] + flat_last_step_outputs + + # We capture the control_flow_context at this point, before we run `fn` + # inside a while_loop. This is useful in cases where we might need to exit + # these contexts and get back to the outer context to do some things, for + # e.g. create an op which should be evaluated only once at the end of the + # loop on the host. One such usage is in creating metrics' value op. + self._outer_control_flow_context = ( + ops.get_default_graph()._get_control_flow_context()) # pylint: disable=protected-access + + cond = lambda i, *args: i < iterations + i = constant_op.constant(0) + loop_result = control_flow_ops.while_loop( + cond, body, [i] + initial_loop_values, name="", + parallel_iterations=1, back_prop=False, swap_memory=False, + return_same_structure=True) + del self._outer_control_flow_context + + ctx.run_op = control_flow_ops.group(loop_result) + + # Convert the last_step_outputs from a list to the original dict structure + # of last_step_outputs. + last_step_tensor_outputs = loop_result[1:] + last_step_tensor_outputs_dict = nest.pack_sequence_as( + ctx.last_step_outputs, last_step_tensor_outputs) + + for name, reduce_op in ctx._last_step_outputs_reduce_ops.items(): # pylint: disable=protected-access + output = last_step_tensor_outputs_dict[name] + # For outputs that have already been reduced, wrap them in a Mirrored + # container, else in a PerReplica container. + if reduce_op is None: + last_step_tensor_outputs_dict[name] = values.regroup( + {d: t for d, t in zip(self._devices, output)}, values.PerReplica) + else: + assert len(output) == 1 + last_step_tensor_outputs_dict[name] = output[0] + + ctx._set_last_step_outputs(last_step_tensor_outputs_dict) # pylint: disable=protected-access + return ctx + + def _broadcast_to(self, tensor, destinations): + # This is both a fast path for Python constants, and a way to delay + # converting Python values to a tensor until we know what type it + # should be converted to. Otherwise we have trouble with: + # global_step.assign_add(1) + # since the `1` gets broadcast as an int32 but global_step is int64. + if isinstance(tensor, (float, int)): + return tensor + # TODO(josh11b): In eager mode, use one thread per device, or async mode. + return self._get_cross_device_ops().broadcast( + tensor, destinations or self._devices) + + def _call_for_each_replica(self, fn, args, kwargs): + return _call_for_each_replica(self._container_strategy(), fn, args, kwargs) + + def _configure(self, + session_config=None, + cluster_spec=None, + task_type=None, + task_id=None): + del task_type, task_id + + if session_config: + session_config.CopyFrom(self._update_config_proto(session_config)) + + if cluster_spec: + # TODO(yuefengz): remove the following code once cluster_resolver is + # added. + num_gpus_per_worker = _infer_num_gpus_per_worker(self._devices) + multi_worker_devices = _cluster_spec_to_device_list( + cluster_spec, num_gpus_per_worker) + self._initialize_multi_worker(multi_worker_devices) + + def _update_config_proto(self, config_proto): + updated_config = copy.deepcopy(config_proto) + updated_config.isolate_session_state = True + return updated_config + + def _get_cross_device_ops(self): + return self._cross_device_ops or self._inferred_cross_device_ops + + def _reduce_to(self, reduce_op, value, destinations): + assert not isinstance(value, values.Mirrored) + if not isinstance(value, values.DistributedValues): + # This function handles reducing values that are not PerReplica or + # Mirrored values. For example, the same value could be present on all + # replicas in which case `value` would be a single value or value could + # be 0. + return _reduce_non_distributed_value(self, reduce_op, value, + destinations) + return self._get_cross_device_ops().reduce( + reduce_op, value, destinations=destinations) + + def _batch_reduce_to(self, reduce_op, value_destination_pairs): + return self._get_cross_device_ops().batch_reduce(reduce_op, + value_destination_pairs) + + def _update(self, var, fn, args, kwargs, group): + # TODO(josh11b): In eager mode, use one thread per device. + assert isinstance(var, values.DistributedVariable) + updates = {} + for d, v in var._index.items(): # pylint: disable=protected-access + name = "update_%d" % self._device_index.get(d) + with ops.device(d), distribute_lib.UpdateContext(d), ops.name_scope(name): + # If args and kwargs are not mirrored, the value is returned as is. + updates[d] = fn(v, + *values.select_device_mirrored(d, args), + **values.select_device_mirrored(d, kwargs)) + return values.update_regroup(self, updates, group) + + def _update_non_slot(self, colocate_with, fn, args, kwargs, group): + assert isinstance(colocate_with, list) + # TODO(josh11b): In eager mode, use one thread per device. + updates = {} + for d in colocate_with: + name = "update_%d" % self._device_index.get(d) + with ops.device(d), distribute_lib.UpdateContext(d), ops.name_scope(name): + updates[d] = fn(*values.select_device_mirrored(d, args), + **values.select_device_mirrored(d, kwargs)) + return values.update_regroup(self, updates, group) + + def read_var(self, replica_local_var): + """Read the aggregate value of a replica-local variable.""" + if isinstance(replica_local_var, values.ReplicaLocalVariable): + return replica_local_var._get_cross_replica() # pylint: disable=protected-access + assert isinstance(replica_local_var, values.Mirrored) + return array_ops.identity(replica_local_var.get()) + + def _unwrap(self, val): + if isinstance(val, values.DistributedValues): + # Return in a deterministic order. + if set(val.devices) == self._canonical_device_set: + return [val.get(device=d) for d in self._devices] + return [val.get(device=d) for d in sorted(val.devices)] + return [val] + + def value_container(self, val): + return values.value_container(val) + + @property + def _num_replicas_in_sync(self): + return len(self._devices) + + @property + def worker_devices(self): + # Make a copy to prevent users from accidentally mutating our copy. + return list(self._devices) + + @property + def parameter_devices(self): + return list(self._devices) + + @property + def experimental_between_graph(self): + return False + + @property + def experimental_should_init(self): + return True + + @property + def should_checkpoint(self): + return True + + @property + def should_save_summary(self): + return True + + def non_slot_devices(self, var_list): + del var_list + return list(self._devices) + + def _get_devices_from(self, colocate_with=None): + if colocate_with is None: + return self._devices + else: + return cross_device_ops_lib.get_devices_from(colocate_with) + + # TODO(priyag): Delete this once all strategies use global batch size. + @property + def _global_batch_size(self): + return True + + class _MirroredReplicaThread(threading.Thread): + """A thread that runs() a function on a device.""" + + def __init__(self, dist, coord, device, variable_creator_fn, fn, *args, + **kwargs): + super(MirroredExtended._MirroredReplicaThread, self).__init__() # pylint: disable=protected-access + self.coord = coord + self.distribution = dist + self.device = device + self.replica_id = dist.extended.worker_devices.index(device) + self.variable_creator_fn = variable_creator_fn + # State needed to run and return the results of `fn`. + self.main_fn = fn + self.main_args = args + self.main_kwargs = kwargs + self.main_result = None + self.done = False + # State needed to run the next merge_call() (if any) requested via + # ReplicaContext. + self.merge_fn = None + self.merge_args = None + self.merge_kwargs = None + self.merge_result = None + self.captured_name_scope = None + # We use a thread.Event for the main thread to signal when this + # thread should start running (`should_run`), and another for + # this thread to transfer control back to the main thread + # (`has_paused`, either when it gets to a + # `get_replica_context().merge_call` or when `fn` returns). In + # either case the event starts cleared, is signaled by calling + # set(). The receiving thread waits for the signal by calling + # wait() and then immediately clearing the event using clear(). + self.should_run = threading.Event() + self.has_paused = threading.Event() + # These fields have to do with inheriting various contexts from the + # parent thread: + # pylint: disable=protected-access + self.context_mode = context.context()._eager_context.mode + if not context.context()._context_handle: + context.context()._initialize_handle_and_devices() + self.context_device_policy = ( + pywrap_tensorflow.TFE_ContextGetDevicePlacementPolicy( + context.context()._context_handle)) + self.graph = ops.get_default_graph() + self._variable_creator_stack = self.graph._variable_creator_stack[:] + self._captured_var_scope = variable_scope.get_variable_scope() + # Adding a "/" at end lets us re-enter this scope later. + self._name_scope = self.graph.get_name_scope() + if self._name_scope: + self._name_scope += "/" + if self.replica_id > 0: + if not self._name_scope: + self._name_scope = "" + self._name_scope += "replica_%d/" % self.replica_id + + def run(self): + # pylint: disable=protected-access + self.graph._variable_creator_stack = self._variable_creator_stack + self.should_run.wait() + self.should_run.clear() + try: + if self.coord.should_stop(): + return + with self.coord.stop_on_exception(), \ + context.context()._mode(self.context_mode), \ + context.context().device_policy(self.context_device_policy), \ + _enter_graph(self.graph), \ + MirroredReplicaContext(self.distribution, constant_op.constant( + self.replica_id, dtypes.int32)), \ + ops.device(self.device), \ + ops.name_scope(self._name_scope), \ + variable_scope.variable_scope( + self._captured_var_scope, reuse=self.replica_id > 0), \ + variable_scope.variable_creator_scope(self.variable_creator_fn): + self.main_result = self.main_fn(*self.main_args, **self.main_kwargs) + self.done = True + finally: + self.has_paused.set() + + +class MirroredReplicaContext(distribute_lib.ReplicaContext): + """ReplicaContext used in MirroredStrategy.call_for_each_replica(). + + Opened in `_MirroredReplicaThread`, to allow the user to invoke + `MirroredStrategy`'s specific implementation of `merge_call()`, + which works by delegating the function and its arguments to + the main thread (the one that invoked + `MirroredStrategy.call_for_each_replica()`). + """ + + def _merge_call(self, fn, args, kwargs): + """Delegate to the main thread to actually perform merge_call().""" + t = threading.current_thread() # a _MirroredReplicaThread + t.merge_fn = fn + t.merge_args = args + t.merge_kwargs = kwargs + t.captured_name_scope = t.graph.get_name_scope() + # Adding a "/" at end lets us re-enter this scope later. + if t.captured_name_scope: + t.captured_name_scope += "/" + t.has_paused.set() + t.should_run.wait() + t.should_run.clear() + if t.coord.should_stop(): + raise _RequestedStop() + return t.merge_result + + @property + def devices(self): + distribute_lib.require_replica_context(self) + replica_id = tensor_util.constant_value(self._replica_id_in_sync_group) + return [self._distribution_strategy.extended.worker_devices[replica_id]] diff --git a/tensorflow/python/distribute/multi_worker_util.py b/tensorflow/python/distribute/multi_worker_util.py index 360733eff6..2986a6726a 100644 --- a/tensorflow/python/distribute/multi_worker_util.py +++ b/tensorflow/python/distribute/multi_worker_util.py @@ -45,6 +45,33 @@ def normalize_cluster_spec(cluster_spec): return cluster_spec +# TODO(yuefengz): add more validations. +def _validate_cluster_spec(cluster_spec, task_type, task_id): + """Validates `cluster_spec`. + + It checks + 1) whether there is such a task type as `task_type` in the + `cluster_spec`. + 2) whether there is at most one "chief" job. + 3) whether the `task_id` is smaller than the number of `task_type`. + + Args: + cluster_spec: a dict, `ClusterDef` or `ClusterSpec` object to be validated. + task_type: string indicating the type of the task. + task_id: task_id: the id of the `task_type` in this cluster. + Throws: + ValueError: if `cluster_spec` fails any check. + """ + cluster_spec = normalize_cluster_spec(cluster_spec).as_dict() + if task_type and task_type not in cluster_spec: + raise ValueError("`task_type` %r not found in cluster_spec." % task_type) + if len(cluster_spec.get("chief", [])) > 1: + raise ValueError("There must be at most one 'chief' job.") + if task_id >= len(cluster_spec[task_type]): + raise ValueError( + "The `task_id` %d exceeds the maximum id of %s." % (task_id, task_type)) + + def is_chief(cluster_spec, task_type, task_id): """Returns whether the given task is chief in the cluster. @@ -61,20 +88,73 @@ def is_chief(cluster_spec, task_type, task_id): ValueError: if `task_type` is not in the `cluster_spec` or `task_id` exceeds the maximum id of the `task_type`. """ - cluster_spec = normalize_cluster_spec(cluster_spec) - if task_type not in cluster_spec.jobs: - raise ValueError( - "The task_type \"%s\" is not in the `cluster_spec`." % task_type) - if task_id >= cluster_spec.num_tasks(task_type): - raise ValueError("The `task_id` %d exceeds the maximum id of %s." % ( - task_id, task_type)) + _validate_cluster_spec(cluster_spec, task_type, task_id) + cluster_spec = normalize_cluster_spec(cluster_spec).as_dict() if task_type == "chief": return True # If chief not in the cluster_spec, use the first worker as chief. This is # common in CollectiveAllReduceStrategy. - if ("chief" not in cluster_spec.jobs and task_type == "worker" and - task_id == 0): + if ("chief" not in cluster_spec and task_type == "worker" and task_id == 0): return True return False + + +def worker_count(cluster_spec, task_type): + """Returns the number of workers in the cluster.""" + _validate_cluster_spec(cluster_spec, task_type, task_id=0) + cluster_spec = normalize_cluster_spec(cluster_spec).as_dict() + + # Other jobs such as "ps" shouldn't call this function. + if task_type not in ["chief", "worker", "evaluator"]: + raise ValueError("Unexpected `task_type` %r" % task_type) + + if task_type == "evaluator": + # The "evaluator" is in its own cluster or its own partition of a cluster. + # So we don't have to count "chief" or "worker" if the current task is an + # "evaluator". + return len(cluster_spec["evaluator"]) + else: + # In the non-evaluator case, we return the total number of "chief" and + # "worker" tasks as the "chief" is also a worker. + return (len(cluster_spec.get("chief", [])) + len( + cluster_spec.get("worker", []))) + + +def id_in_cluster(cluster_spec, task_type, task_id): + """Returns a unique id for the task in the `task_type`'s cluster. + + It returns an id ranging from [0, `worker_count(task_type, task_id)`). + + Note: this function assumes that "evaluate" job is in its own cluster or its + own partition of a cluster. + + Args: + cluster_spec: a dict, `ClusterDef` or `ClusterSpec` object to be validated. + task_type: string indicating the type of the task. + task_id: the id of the `task_type` in this cluster. + + Returns: + an int indicating the unique id. + + Throws: + ValueError: if `task_type` is not "chief", "worker" or "evaluator". + """ + _validate_cluster_spec(cluster_spec, task_type, task_id) + cluster_spec = normalize_cluster_spec(cluster_spec).as_dict() + + # The "chief" job has always id 0 and there is at most one and "worker" jobs + # come after it. + if task_type == "chief": + return 0 + + if task_type == "worker": + return task_id + len(cluster_spec.get("chief", [])) + + # The "evaluator" is in its own cluster or its own partition of a cluster. + if task_type == "evaluator": + return task_id + + # We currently don't assign ids to other tasks. + raise ValueError("There is no id for task_type %r" % task_type) diff --git a/tensorflow/python/distribute/multi_worker_util_test.py b/tensorflow/python/distribute/multi_worker_util_test.py index bdc49725c7..9e1596eefd 100644 --- a/tensorflow/python/distribute/multi_worker_util_test.py +++ b/tensorflow/python/distribute/multi_worker_util_test.py @@ -95,7 +95,7 @@ class IsChiefTest(test.TestCase): self.assertFalse(multi_worker_util.is_chief(cluster_spec, "worker", 1)) with self.assertRaisesRegexp( - ValueError, "The task_type \"chief\" is not in the `cluster_spec`."): + ValueError, "`task_type` 'chief' not found in cluster_spec."): multi_worker_util.is_chief(cluster_spec, "chief", 0) with self.assertRaisesRegexp( @@ -103,5 +103,94 @@ class IsChiefTest(test.TestCase): multi_worker_util.is_chief(cluster_spec, "worker", 2) +class NumWorkersTest(test.TestCase): + + def testCountWorker(self): + cluster_spec = { + "chief": ["127.0.0.1:1234"], + "worker": ["127.0.0.1:8964", "127.0.0.1:2333"], + "ps": ["127.0.0.1:1926", "127.0.0.1:3141"] + } + self.assertEqual( + multi_worker_util.worker_count(cluster_spec, task_type="chief"), 3) + self.assertEqual( + multi_worker_util.worker_count(cluster_spec, task_type="worker"), 3) + + def testCountEvaluator(self): + cluster_spec = { + "chief": ["127.0.0.1:1234"], + "worker": ["127.0.0.1:8964", "127.0.0.1:2333"], + "evaluator": ["127.0.0.1:7566"] + } + self.assertEqual( + multi_worker_util.worker_count(cluster_spec, task_type="evaluator"), 1) + + def testTaskTypeNotFound(self): + cluster_spec = {} + with self.assertRaisesRegexp( + ValueError, "`task_type` 'worker' not found in cluster_spec."): + multi_worker_util.worker_count(cluster_spec, task_type="worker") + + def testCountPs(self): + cluster_spec = { + "chief": ["127.0.0.1:1234"], + "ps": ["127.0.0.1:1926", "127.0.0.1:3141"] + } + # A "ps" job shouldn't call this method. + with self.assertRaisesRegexp(ValueError, "Unexpected `task_type` 'ps'"): + multi_worker_util.worker_count(cluster_spec, task_type="ps") + + +class IdInClusterTest(test.TestCase): + + def testChiefId(self): + cluster_spec = { + "chief": ["127.0.0.1:1234"], + "worker": ["127.0.0.1:8964", "127.0.0.1:2333"], + "ps": ["127.0.0.1:1926", "127.0.0.1:3141"] + } + self.assertEqual( + multi_worker_util.id_in_cluster(cluster_spec, "chief", 0), 0) + + def testWorkerId(self): + cluster_spec = { + "chief": ["127.0.0.1:1234"], + "worker": ["127.0.0.1:8964", "127.0.0.1:2333"], + "ps": ["127.0.0.1:1926", "127.0.0.1:3141"] + } + self.assertEqual( + multi_worker_util.id_in_cluster(cluster_spec, "worker", 1), 2) + + cluster_spec = { + "worker": ["127.0.0.1:8964", "127.0.0.1:2333"], + "ps": ["127.0.0.1:1926", "127.0.0.1:3141"] + } + self.assertEqual( + multi_worker_util.id_in_cluster(cluster_spec, "worker", 1), 1) + + def testEvaluatorId(self): + cluster_spec = { + "chief": ["127.0.0.1:1234"], + "worker": ["127.0.0.1:8964", "127.0.0.1:2333"], + "evaluator": ["127.0.0.1:7566"] + } + self.assertEqual( + multi_worker_util.id_in_cluster(cluster_spec, "evaluator", 0), 0) + + def testPsId(self): + cluster_spec = {"chief": ["127.0.0.1:1234"], "ps": ["127.0.0.1:7566"]} + with self.assertRaisesRegexp(ValueError, + "There is no id for task_type 'ps'"): + multi_worker_util.id_in_cluster(cluster_spec, "ps", 0) + + def testMultipleChiefs(self): + cluster_spec = { + "chief": ["127.0.0.1:8258", "127.0.0.1:7566"], + } + with self.assertRaisesRegexp(ValueError, + "There must be at most one 'chief' job."): + multi_worker_util.id_in_cluster(cluster_spec, "chief", 0) + + if __name__ == "__main__": test.main() diff --git a/tensorflow/python/distribute/reduce_util.py b/tensorflow/python/distribute/reduce_util.py new file mode 100644 index 0000000000..2b2a4e9dba --- /dev/null +++ b/tensorflow/python/distribute/reduce_util.py @@ -0,0 +1,53 @@ +# Copyright 2018 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. +# ============================================================================== +"""Utilites for reduce operations.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import enum + +from tensorflow.python.ops import variable_scope +from tensorflow.python.util.tf_export import tf_export + + +@tf_export("distribute.ReduceOp") +class ReduceOp(enum.Enum): + """Indicates how a set of values should be reduced. + + * `SUM`: Add all the values. + * `MEAN`: Take the arithmetic mean ("average") of the values. + + TODO(priyag): Add the following types: + * `MIN`: Return the minimum of all values. + * `MAX`: Return the maximum of all values. + """ + + SUM = "SUM" + MEAN = "MEAN" + + @staticmethod + def from_variable_aggregation(aggregation): + mapping = { + variable_scope.VariableAggregation.SUM: ReduceOp.SUM, + variable_scope.VariableAggregation.MEAN: ReduceOp.MEAN, + } + + reduce_op = mapping.get(aggregation) + if not reduce_op: + raise ValueError("Could not convert from `tf.VariableAggregation` %s to" + "`tf.distribute.ReduceOp` type" % aggregation) + return reduce_op diff --git a/tensorflow/contrib/distribute/python/shared_variable_creator.py b/tensorflow/python/distribute/shared_variable_creator.py similarity index 100% rename from tensorflow/contrib/distribute/python/shared_variable_creator.py rename to tensorflow/python/distribute/shared_variable_creator.py diff --git a/tensorflow/contrib/distribute/python/shared_variable_creator_test.py b/tensorflow/python/distribute/shared_variable_creator_test.py similarity index 97% rename from tensorflow/contrib/distribute/python/shared_variable_creator_test.py rename to tensorflow/python/distribute/shared_variable_creator_test.py index 2a9ab51fcf..4ddc29f256 100644 --- a/tensorflow/contrib/distribute/python/shared_variable_creator_test.py +++ b/tensorflow/python/distribute/shared_variable_creator_test.py @@ -18,7 +18,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -from tensorflow.contrib.distribute.python import shared_variable_creator +from tensorflow.python.distribute import shared_variable_creator from tensorflow.python.eager import test from tensorflow.python.framework import test_util from tensorflow.python.ops import variable_scope diff --git a/tensorflow/contrib/distribute/python/values.py b/tensorflow/python/distribute/values.py similarity index 77% rename from tensorflow/contrib/distribute/python/values.py rename to tensorflow/python/distribute/values.py index a162973535..01a1680a24 100644 --- a/tensorflow/contrib/distribute/python/values.py +++ b/tensorflow/python/distribute/values.py @@ -23,11 +23,18 @@ from __future__ import print_function import collections import contextlib +import operator import weakref import six -from tensorflow.contrib.distribute.python import input_ops +from tensorflow.python.data.experimental.ops import batching +from tensorflow.python.data.ops import dataset_ops from tensorflow.python.data.ops import multi_device_iterator_ops +from tensorflow.python.distribute import device_util +from tensorflow.python.distribute import distribute_lib +from tensorflow.python.distribute import distribution_strategy_context +from tensorflow.python.distribute import input_ops +from tensorflow.python.distribute import reduce_util from tensorflow.python.eager import context from tensorflow.python.eager import tape from tensorflow.python.framework import device as tf_device @@ -38,10 +45,6 @@ from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import gen_resource_variable_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import variable_scope as vs -from tensorflow.python.ops import variables as variables_lib -from tensorflow.python.training import device_util -from tensorflow.python.training import distribute as distribute_lib -from tensorflow.python.training import distribution_strategy_context from tensorflow.python.training import saver from tensorflow.python.training.checkpointable import base as checkpointable from tensorflow.python.util import nest @@ -97,10 +100,21 @@ class DistributedValues(object): # DistributionStrategy implementations. +# NOTE(josh11b,apassos): It would be great if we could inspect the values this was +# initialized with and use that to generate the overloaded operators here. +# Unfortunately, Python's rules for special methods don't allow this, see +# https://docs.python.org/3/reference/datamodel.html#special-method-names +# "if a class defines a method named __getitem__(), and x is an instance of +# this class, then x[i] is roughly equivalent to type(x).__getitem__(x, i)." +# In particular, these special methods don't go through __getattr__, and +# it will only use those methods if they are defined in the class, not the +# object. class DistributedDelegate(DistributedValues): """A map from device to values; acts as the same type as the values.""" def __getattr__(self, name): + # TODO(priyag): This needs to be made robust against pitfalls from mix use + # __getattr__ and @property. See b/120402273. return getattr(self.get(), name) # pylint: disable=multiple-statements @@ -316,6 +330,14 @@ class DistributedVariable(DistributedDelegate): ops.register_dense_tensor_like_type(DistributedVariable) +def _apply_aggregation(strategy, value, aggregation, destinations): + if aggregation == vs.VariableAggregation.ONLY_FIRST_REPLICA: + return strategy.broadcast(strategy.unwrap(value)[0], + destinations=destinations) + reduce_op = reduce_util.ReduceOp.from_variable_aggregation(aggregation) + return strategy.extended.reduce_to(reduce_op, value, destinations) + + class _MirroredSaveable(saver.BaseSaverBuilder.ResourceVariableSaveable): """Class for defining how to restore a MirroredVariable.""" @@ -373,14 +395,11 @@ class MirroredVariable(DistributedVariable, Mirrored, "MirroredVariable in Replica Context.") def merge_fn(strategy, value, *other_args, **other_kwargs): - return strategy.update( - self, f, - strategy.reduce( - aggregation=self._aggregation, value=value, destinations=self), - *other_args, **other_kwargs) + v = _apply_aggregation(strategy, value, self._aggregation, self) + return strategy.update(self, f, v, *other_args, **other_kwargs) return distribution_strategy_context.get_replica_context().merge_call( - merge_fn, *args, **kwargs) + merge_fn, args=args, kwargs=kwargs) def assign_sub(self, *args, **kwargs): assign_sub_fn = lambda var, *a, **kw: var.assign_sub(*a, **kw) @@ -614,14 +633,11 @@ class TPUMirroredVariable(checkpointable.CheckpointableBase): "TPUMirroredVariable in Replica Context.") def merge_fn(strategy, value, *other_args, **other_kwargs): - return strategy.update( - self, f, - strategy.reduce( - aggregation=self._aggregation, value=value, destinations=self), - *other_args, **other_kwargs) + v = _apply_aggregation(strategy, value, self._aggregation, self) + return strategy.update(self, f, v, *other_args, **other_kwargs) return distribution_strategy_context.get_replica_context().merge_call( - merge_fn, *args, **kwargs) + merge_fn, args=args, kwargs=kwargs) @contextlib.contextmanager def _handle_graph(self, handle): @@ -1058,18 +1074,18 @@ def select_device_mirrored(device, structured): return nest.map_structure(_get_mirrored, structured) -def update_regroup(strategy, updates, should_group): +def update_regroup(extended, updates, group): """Regroup for an update, with dependencies to ensure all updates execute.""" regrouped = regroup(updates, Mirrored) - if not should_group: - return nest.map_structure(strategy.unwrap, regrouped) + if not group: + return nest.map_structure(extended._unwrap, regrouped) # pylint: disable=protected-access grouped_flat = [] for u in nest.flatten(regrouped): if isinstance(u, DistributedValues): - g = strategy.group(u) + g = extended._group(u) # pylint: disable=protected-access if u.is_tensor_like: # Make sure we run all updates. Without this, something like - # session.run(strategy.update(...)) may only update one replica. + # session.run(extended.update(...)) may only update one replica. index = {} for d in u.devices: with ops.device(d), ops.control_dependencies([g]): @@ -1155,7 +1171,7 @@ class PerReplicaDataset(object): # Eager mode prefetching would error out in constructor. Only remaining # case is non-prefetching in eager mode. We delegate to # PerReplicaDataIterator to handle that case. - dataset_iterator = self._dataset.make_one_shot_iterator() + dataset_iterator = dataset_ops.make_one_shot_iterator(self._dataset) return PerReplicaDataIterator( dataset_iterator, self._devices, prefetch_on_device=False) @@ -1170,7 +1186,7 @@ class PerReplicaDataset(object): dataset_iterator = multi_device_iterator_ops.MultiDeviceIterator( self._dataset, self._devices) else: - dataset_iterator = self._dataset.make_initializable_iterator() + dataset_iterator = dataset_ops.make_initializable_iterator(self._dataset) return PerReplicaDataIterator( dataset_iterator, self._devices, @@ -1252,22 +1268,34 @@ class MultiWorkerDataset(object): """Initialize the MultiWorkerDataset object. Args: - dataset_fn: a function that returns a `tf.data.Dataset`. + dataset_fn: a function or a list of functions that returns a + `tf.data.Dataset`. worker_device_pairs: a list of (worker, list of devices on that worker) - pairs. + pairs; it must have same length with `dataset_fn` if `dataset_fn` is a + list. prefetch_on_device: whether to prefetch to devices. auto_shard: whether to auto-shard the dataset. """ + if isinstance(dataset_fn, list): + if len(dataset_fn) != len(worker_device_pairs): + raise ValueError("If `dataset_fn` is a list, it must have same length " + "as `worker_device_pairs`") + if auto_shard: + raise ValueError( + "If `dataset_fn` is a list, `auto_shard` is not supported.") self._worker_device_pairs = worker_device_pairs self._datasets = [] # TODO(yuefengz, priyag): support different set of jobs for input # processing. for i, (worker, worker_devices) in enumerate(worker_device_pairs): with ops.device(worker): - worker_input = dataset_fn() - if auto_shard: - worker_input = input_ops.auto_shard_dataset( - worker_input, len(worker_device_pairs), i) + if isinstance(dataset_fn, list): + worker_input = dataset_fn[i]() + else: + worker_input = dataset_fn() + if auto_shard: + worker_input = input_ops.auto_shard_dataset( + worker_input, len(worker_device_pairs), i) dataset = PerReplicaDataset( worker_input, worker_devices, prefetch_on_device=prefetch_on_device) self._datasets.append((worker, dataset)) @@ -1276,36 +1304,337 @@ class MultiWorkerDataset(object): iterators = [] for worker, dataset in self._datasets: with ops.device(worker): - iterators.append((worker, dataset.make_one_shot_iterator())) + iterators.append((worker, dataset_ops.make_one_shot_iterator(dataset))) return MultiWorkerDataIterator(iterators, self._worker_device_pairs) def make_initializable_iterator(self): iterators = [] for worker, dataset in self._datasets: with ops.device(worker): - iterators.append((worker, dataset.make_initializable_iterator())) + iterators.append( + (worker, dataset_ops.make_initializable_iterator(dataset))) return MultiWorkerDataIterator(iterators, self._worker_device_pairs) +class InputIterator(object): + """An input iterator, intended to be passed to `DistributionStrategy.run`.""" + + def get_next(self): + """Returns the next inputs for all replicas.""" + raise NotImplementedError("must be implemented in descendants") + + def initialize(self): + """Initialize the underlying input dataset, when applicable. + + In eager mode, this will create a new iterator and return it. + In graph mode, this will initialize the same underlying iterator(s). + + Users are required to call this if + - This iterator was returned from a call to `make_input_fn_iterator` with an + input function that returns a dataset. + - Or this iterator was returned from a call to `make_dataset_iterator`. + + Returns: + A list of initialization ops to be executed. + """ + raise NotImplementedError("must be implemented in descendants") + + +class InputIteratorImpl(InputIterator): + """Common implementation for all input iterators.""" + + def __init__(self, worker_device_pairs, iterators): + if not worker_device_pairs: + raise ValueError("Should have at least one worker for input iterator.") + + self._iterators = iterators + self._worker_device_pairs = worker_device_pairs + self._is_eager = context.executing_eagerly() + + def get_next(self, name=None): + """Returns the next input from the iterator for all replicas.""" + assert self._is_eager == context.executing_eagerly(), ( + "Iterator should be created and used in same execution mode.") + + index = {} + for i, (worker, worker_devices) in enumerate(self._worker_device_pairs): + if name is not None: + d = tf_device.DeviceSpec.from_string(worker) + new_name = "%s_%s_%d" % (name, d.job, d.task) + else: + new_name = None + with ops.device(worker): + data_per_worker = self._iterators[i].get_next(new_name) + + # Ungroup these per-replica value so as to get a flat map from devices to + # values. + for d in worker_devices: + v = select_device(d, data_per_worker) + if d in index: + raise ValueError("Duplicated devices in worker_device_pairs: %r" % v) + index[d] = v + + return regroup(index) + + def initialize(self): + """Initialze underlying iterators. + + Returns: + A list of any initializer ops that should be run. + """ + assert self._is_eager == context.executing_eagerly(), ( + "Iterator should be created and used in same execution mode.") + + init_ops = [] + for it in self._iterators: + init_ops.extend(it.initialize()) + return init_ops + + # TODO(priyag): Remove when we switch to using `MultiDeviceIterator` for TPUs. + @property + def output_classes(self): + return self._iterators[0].output_classes + + # TODO(priyag): Remove when we switch to using `MultiDeviceIterator` for TPUs. + @property + def output_shapes(self): + return self._iterators[0].output_shapes + + # TODO(priyag): Remove when we switch to using `MultiDeviceIterator` for TPUs. + @property + def output_types(self): + return self._iterators[0].output_types + + # TODO(priyag): Remove when we switch to using `MultiDeviceIterator` for TPUs. + def get_iterator(self, worker): + for i, (w, _) in enumerate(self._worker_device_pairs): + if worker == w: + return self._iterators[i] + return None + + +class InputFunctionIterator(InputIteratorImpl): + """Iterator created from input function.""" + + def __init__(self, input_fn, worker_device_pairs, input_contexts): + """Make an iterator for input provided via an input function. + + Currently implements PER_WORKER mode, in which the `input_fn` is called + once on each worker. + + TODO(priyag): Add other replication modes. + TODO(priyag): Allow taking input function that returns a callable that + returns nest of tensors. + + Args: + input_fn: Input function that returns a `tf.data.Dataset` object. + worker_device_pairs: A list of (worker, list of devices on that worker) + pairs. + input_contexts: A list of `InputContext` instances to be passed to call(s) + to `input_fn`. Length and order should match worker order in + `worker_device_pairs`. + """ + if len(worker_device_pairs) != len(input_contexts): + raise ValueError( + "Number of worker_device_pairs (%d) is not same as number of" + "input_contexts (%d)" % ( + len(worker_device_pairs), len(input_contexts))) + + iterators = [] + for (worker, devices), ctx in zip(worker_device_pairs, input_contexts): + # TODO(priyag): We should probably explicitly specify CPU device on worker. + with ops.device(worker): + result = input_fn(ctx) + if not isinstance(result, dataset_ops.DatasetV2): + raise ValueError("input_fn must return a tf.data.Dataset.") + iterator = _SingleWorkerDatasetIterator(result, worker, devices) + iterators.append(iterator) + + super(InputFunctionIterator, self).__init__( + worker_device_pairs, iterators) + + +class DatasetIterator(InputIteratorImpl): + """Iterator created from input dataset.""" + + def __init__(self, dataset, worker_device_pairs, split_batch_by=None): + """Make an iterator for the dataset on given devices. + + If `split_batch_by` is not None, we "split" each batch of the + dataset by `split_batch_by` value. To achieve this, we first unbatch the + input dataset and then rebatch it with the per replica batch size that is + calculated using `global_batch_size // split_batch_by`. + The currently supported datasets are as follows: + `dataset.batch()` is the last operation on the dataset OR + `dataset.apply(map_and_batch)` is the last operation on the dataset OR + `dataset.batch().prefetch()` are the last 2 operations on the dataset OR + `dataset.apply(map_and_batch).prefetch()` are the last 2 operations. + + TODO(priyag): Support multi worker / host cases properly by cloning + and sharding the dataset on each worker. Current setup will only work in + some cases, such as in-graph multi worker GPU case. If the input pipeline + has random shuffling (with a different seed on each worker), each worker + will see random input from the same overall dataset in each step. Otherwise, + each worker will see the same input in each step. + + Args: + dataset: `tf.data.Dataset` that will be used as the input source. + worker_device_pairs: A list of (worker, list of devices on that worker) + pairs. + split_batch_by: Optional integer. If present, we "split" each batch of the + dataset by `split_batch_by` value. + """ + if split_batch_by: + dataset = _split_dataset_batch(dataset, split_batch_by) + + iterators = [] + for worker, worker_devices in worker_device_pairs: + with ops.device(worker): + iterator = _SingleWorkerDatasetIterator(dataset, worker, worker_devices) + iterators.append(iterator) + + super(DatasetIterator, self).__init__(worker_device_pairs, iterators) + + +class _SingleWorkerDatasetIterator(object): + """Iterator for a single `tf.data.Dataset`.""" + + def __init__(self, dataset, worker, devices): + """Create iterator for the `dataset` to fetch data to worker's `devices` . + + `MultiDeviceIterator` is used to prefetch input to the devices on the + given worker. `MultiDeviceIterator` doesn't work in eager mode yet. + + Args: + dataset: A `tf.data.Dataset` instance. + worker: Worker on which ops should be created. + devices: Distribute data from `dataset` to these devices. + """ + self._dataset = dataset + self._worker = worker + self._devices = devices + self._is_eager = context.executing_eagerly() + self._make_iterator() + + def _make_iterator(self): + """Make appropriate iterator on the dataset.""" + with ops.device(self._worker): + if self._is_eager: + # TODO(rohanj): Enable prefetching in eager mode. + # TODO(priyag): Measure the performance of this approach vs calling + # get_next on the original dataset N times. + dataset = self._dataset.batch(len(self._devices), drop_remainder=True) + iterator = dataset_ops.make_one_shot_iterator(dataset) + else: + iterator = multi_device_iterator_ops.MultiDeviceIterator( + self._dataset, self._devices) + self._iterator = iterator + + def get_next(self, name=None): + """Get next element from the underlying iterator.""" + with ops.device(self._worker): + if self._is_eager: + # Batched dataset case. + batch = self._iterator.get_next(name=name) + index = {} + for i, d in enumerate(self._devices): + index[d] = nest.map_structure(operator.itemgetter(i), batch) + with ops.device(d): + index[d] = nest.map_structure(array_ops.identity, index[d]) + else: + # MultiDeviceIterator case. + data_list = self._iterator.get_next() + index = dict(zip(self._devices, data_list)) + + return regroup(index) + + def initialize(self): + """Initialze underlying iterator. + + In eager execution, this simply recreates the underlying iterator. + In graph execution, it returns the initializer ops for the underlying + iterator. + + Returns: + A list of any initializer ops that should be run. + """ + if self._is_eager: + self._make_iterator() + return [] + else: + return [self._iterator.initializer] + + @property + def output_classes(self): + return self._iterator.output_classes + + @property + def output_shapes(self): + return self._iterator.output_shapes + + @property + def output_types(self): + return self._iterator.output_types + + +def _split_dataset_batch(dataset, split_batch_by): + """Divide a batch-ed dataset's batches into smaller batches.""" + # TODO(sourabhbajaj): Remove this in lieu of distributed datasets + # pylint: disable=protected-access + def _get_batch_dataset(d): + """Get the underlying batch dataset from the dataset object.""" + if isinstance(d, dataset_ops.DatasetV1Adapter): + d = d._dataset + + if isinstance(d, (dataset_ops.BatchDataset, batching._MapAndBatchDataset)): + return d + elif isinstance(d, dataset_ops.PrefetchDataset): + return _get_batch_dataset(d._input_dataset) + raise ValueError( + "Unable to get batched dataset from the input dataset. `batch` " + "`map_and_batch` need to be the last operations on the dataset. " + "The batch operations can be followed by a prefetch.") + + batched_dataset = _get_batch_dataset(dataset) + batch_size = batched_dataset._batch_size + drop_remainder = batched_dataset._drop_remainder + # pylint: enable=protected-access + + if tensor_util.is_tensor(batch_size): + batch_size = tensor_util.constant_value(batch_size) + + if tensor_util.is_tensor(drop_remainder): + drop_remainder = tensor_util.constant_value(drop_remainder) + + if batch_size % split_batch_by: + raise ValueError( + "Batch size %s cannot be sharded evenly across replicas %s" % ( + batch_size, split_batch_by)) + new_batch_size = batch_size // split_batch_by + + dataset = dataset.apply(batching.unbatch()) + return dataset.batch(new_batch_size, drop_remainder=drop_remainder) + + class MultiStepContext(object): """A context object that can be used to capture things when running steps. This context object is useful when running multiple steps at a time using the - `run_steps_on_dataset` API. For e.g. it allows the user's step function to - specify which outputs to emit at what frequency. Currently it supports - capturing output from the last step, as well as capturing non tensor outputs. - In the future it will be augmented to support other use cases such as output - each N steps. + `experimental_run_steps_on_iterator` API. For e.g. it allows the user's step + function to specify which outputs to emit at what frequency. Currently it + supports capturing output from the last step, as well as capturing non tensor + outputs. In the future it will be augmented to support other use cases such + as output each N steps. """ def __init__(self): - """Initializes an output context. + """Initialize an output context. Returns: A context object. """ self._last_step_outputs = {} - self._last_step_outputs_aggregations = {} + self._last_step_outputs_reduce_ops = {} self._non_tensor_outputs = {} @property @@ -1315,8 +1644,8 @@ class MultiStepContext(object): Keys in the dictionary are names of tensors to be captured, as specified when `set_last_step_output` is called. Values in the dictionary are the tensors themselves. If - `set_last_step_output` was called with an `aggregation` for this output, - then the value is the aggregated value. + `set_last_step_output` was called with a `reduce_op` for this output, + then the value is the reduced value. Returns: A dictionary with last step outputs. @@ -1329,8 +1658,7 @@ class MultiStepContext(object): raise ValueError("Need a dictionary to set last_step_outputs.") self._last_step_outputs = outputs - def set_last_step_output(self, name, output, - aggregation=variables_lib.VariableAggregation.NONE): + def set_last_step_output(self, name, output, reduce_op=None): """Set `output` with `name` to be outputted from the last step. Args: @@ -1338,39 +1666,36 @@ class MultiStepContext(object): name. output: The tensors that should be outputted with `name`. See below for actual types supported. - aggregation: Aggregation method to use to aggregate outputs from multiple + reduce_op: Reduction method to use to reduce outputs from multiple replicas. Required if `set_last_step_output` is called in a replica context. Optional in cross_replica_context. - When present, the outputs from all the replicas are aggregated using the + When present, the outputs from all the replicas are reduced using the current distribution strategy's `reduce` method. Hence, the type of `output` must be what's supported by the corresponding `reduce` method. - For e.g. if using MirroredStrategy and aggregation is set, output + For e.g. if using MirroredStrategy and reduction is set, output must be a `PerReplica` value. - The aggregation method is also recorded in a dictionary - `_last_step_outputs_aggregations` for later interpreting of the + The reduce method is also recorded in a dictionary + `_last_step_outputs_reduce_ops` for later interpreting of the outputs as already reduced or not. - """ if distribution_strategy_context.get_cross_replica_context(): - self._last_step_outputs_aggregations[name] = aggregation - if aggregation is variables_lib.VariableAggregation.NONE: + self._last_step_outputs_reduce_ops[name] = reduce_op + if reduce_op is None: self._last_step_outputs[name] = output else: distribution = distribution_strategy_context.get_distribution_strategy() - self._last_step_outputs[name] = distribution.reduce( - aggregation, output, destinations="/device:CPU:0") + self._last_step_outputs[name] = distribution.reduce(reduce_op, output) else: - assert aggregation is not variables_lib.VariableAggregation.NONE + assert reduce_op is not None def merge_fn(distribution, value): - self._last_step_outputs[name] = distribution.reduce( - aggregation, value, destinations="/device:CPU:0") + self._last_step_outputs[name] = distribution.reduce(reduce_op, value) # Setting this inside the `merge_fn` because all replicas share the same # context object, so it's more robust to set it only once (even if all # the replicas are trying to set the same value). - self._last_step_outputs_aggregations[name] = aggregation + self._last_step_outputs_reduce_ops[name] = reduce_op distribution_strategy_context.get_replica_context().merge_call( - merge_fn, output) + merge_fn, args=(output,)) @property def non_tensor_outputs(self): @@ -1384,10 +1709,10 @@ class MultiStepContext(object): else: def merge_fn(distribution, value): # NOTE(priyag): For non tensor outputs, we simply return all the values - # in a list as aggregation doesn't make sense on non tensors. + # in a list as reduction doesn't make sense on non tensors. self._non_tensor_outputs[name] = distribution.unwrap(value) distribution_strategy_context.get_replica_context().merge_call( - merge_fn, output) + merge_fn, args=(output,)) def value_container(val): @@ -1452,14 +1777,11 @@ class AggregatingVariable(checkpointable.CheckpointableBase): "a variable in Replica Context.") def merge_fn(strategy, value, *other_args, **other_kwargs): - return strategy.update( - self, f, - strategy.reduce( - aggregation=self._aggregation, value=value, destinations=self), - *other_args, **other_kwargs) + v = _apply_aggregation(strategy, value, self._aggregation, self) + return strategy.update(self, f, v, *other_args, **other_kwargs) return distribution_strategy_context.get_replica_context().merge_call( - merge_fn, *args, **kwargs) + merge_fn, args=args, kwargs=kwargs) def assign_sub(self, *args, **kwargs): assign_sub_fn = lambda var, *a, **kw: var.assign_sub(*a, **kw) diff --git a/tensorflow/python/eager/BUILD b/tensorflow/python/eager/BUILD index 362e8e3b83..f43cf9327a 100644 --- a/tensorflow/python/eager/BUILD +++ b/tensorflow/python/eager/BUILD @@ -174,6 +174,23 @@ cuda_py_test( ], ) +cuda_py_test( + name = "function_gradients_test", + size = "medium", + srcs = ["function_gradients_test.py"], + additional_deps = [ + ":backprop", + ":context", + ":def_function", + ":function", + ":test", + "@absl_py//absl/testing:parameterized", + "//tensorflow/python:math_ops", + "//tensorflow/python:resource_variable_ops", + ], + shard_count = 5, +) + cuda_py_test( name = "function_test", size = "medium", @@ -193,7 +210,7 @@ cuda_py_test( "//tensorflow/python:math_ops", "//tensorflow/python:resource_variable_ops", ], - shard_count = 20, + shard_count = 15, ) py_library( @@ -238,6 +255,18 @@ py_library( ], ) +py_test( + name = "execution_callbacks_test", + srcs = ["execution_callbacks_test.py"], + srcs_version = "PY2AND3", + deps = [ + ":execution_callbacks", + "//tensorflow/python:client_testlib", + "//tensorflow/python:framework_ops", + "//tensorflow/python:math_ops", + ], +) + py_library( name = "graph_only_ops", srcs = ["graph_only_ops.py"], @@ -318,6 +347,7 @@ py_library( "//tensorflow/python/eager:context", "//tensorflow/python/eager:execute", "//tensorflow/python/eager:tape", + "//tensorflow/python/ops/parallel_for:control_flow_ops", "@six_archive//:six", ], ) diff --git a/tensorflow/python/eager/backprop.py b/tensorflow/python/eager/backprop.py index 844c9b52e7..29f9b2cda3 100644 --- a/tensorflow/python/eager/backprop.py +++ b/tensorflow/python/eager/backprop.py @@ -20,6 +20,7 @@ from __future__ import print_function import functools import operator +import sys import six @@ -33,6 +34,7 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape from tensorflow.python.ops import array_ops +from tensorflow.python.ops import check_ops from tensorflow.python.ops import gen_array_ops from tensorflow.python.ops import gen_math_ops from tensorflow.python.ops import math_ops @@ -42,9 +44,20 @@ from tensorflow.python.platform import tf_logging as logging from tensorflow.python.util import nest from tensorflow.python.util import tf_contextlib from tensorflow.python.util import tf_inspect +from tensorflow.python.util.lazy_loader import LazyLoader from tensorflow.python.util.tf_export import tf_export +# Note that we need to lazy load the following two modules to avoid creating +# circular dependencies. +# TODO(b/119775953): fix the circular dependencies. +pfor_ops = LazyLoader( + "pfor_ops", globals(), + "tensorflow.python.ops.parallel_for.control_flow_ops") + +function = LazyLoader("function", globals(), + "tensorflow.python.eager.function") + _op_attr_type_cache = {} @@ -536,11 +549,11 @@ def _aggregate_grads(gradients): if len(gradients) == 1: return gradients[0] - if all([isinstance(g, ops.Tensor) for g in gradients]): + if all(isinstance(g, ops.Tensor) for g in gradients): return gen_math_ops.add_n(gradients) else: - assert all([isinstance(g, (ops.Tensor, ops.IndexedSlices)) - for g in gradients]) + assert all(isinstance(g, (ops.Tensor, ops.IndexedSlices)) + for g in gradients) indexed_slices_list = [] for grad in gradients: # TODO(xpan): Support nested IndexedSlices and core IndexedSlices @@ -776,6 +789,8 @@ class GradientTape(object): context.context().end_step() except AttributeError: pass + except TypeError: + pass def watch(self, tensor): """Ensures that `tensor` is being traced by this tape. @@ -935,3 +950,213 @@ class GradientTape(object): grad = nest.pack_sequence_as(sources, flat_grad) return grad + + def jacobian(self, + target, + sources, + unconnected_gradients=UnconnectedGradients.NONE, + parallel_iterations=None, + experimental_use_pfor=True): + """Computes the jacobian using operations recorded in context of this tape. + + See http://en.wikipedia.org/wiki/jacobian_matrix_and_determinant for the + definition of a Jacobian. + + Example usage: + + with tf.GradientTape() as g: + x = tf.constant([1.0, 2.0]) + g.watch(x) + y = x * x + jacobian = g.jacobian(y, x) + # jacobian value is [[2., 0.], [0., 4.]] + + Args: + target: Tensor to be differentiated. + sources: a list or nested structure of Tensors or Variables. `target` + will be differentiated against elements in `sources`. + unconnected_gradients: a value which can either hold 'none' or 'zero' and + alters the value which will be returned if the target and sources are + unconnected. The possible values and effects are detailed in + 'UnconnectedGradients' and it defaults to 'none'. + parallel_iterations: A knob to control how many iterations are dispatched + in parallel. This knob can be used to control the total memory usage. + experimental_use_pfor: If true, vectorizes the jacobian computation. Else + falls back to a sequential while_loop. Vectorization can sometimes fail + or lead to excessive memory usage. This option can be used to disable + vectorization in such cases. + + Returns: + a list or nested structure of Tensors (or IndexedSlices, or None), + one for each element in `sources`. Returned structure is the same as + the structure of `sources`. + + Raises: + RuntimeError: If called on a non-persistent tape with eager execution + enabled and without enabling experimental_use_pfor. + ValueError: If vectorization of jacobian computation fails. + """ + flat_sources = nest.flatten(sources) + target_static_shape = target.shape + target_shape = array_ops.shape(target) + # Note that we push and pop the tape here and below. This is needed since we + # need gradients through the enclosed operations. + self._push_tape() + target = array_ops.reshape(target, [-1]) + self._pop_tape() + + def loop_fn(i): + self._push_tape() + y = array_ops.gather(target, i) + self._pop_tape() + return self.gradient(y, flat_sources, + unconnected_gradients=unconnected_gradients) + + try: + target_size = int(target.shape[0]) + except TypeError: + target_size = array_ops.shape(target)[0] + + if experimental_use_pfor: + try: + output = pfor_ops.pfor(loop_fn, target_size, + parallel_iterations=parallel_iterations) + except ValueError as err: + six.reraise( + ValueError, + ValueError( + str(err) + "\nEncountered an exception while vectorizing the " + "jacobian computation. Vectorization can be disabled by setting" + " experimental_use_pfor to False."), + sys.exc_info()[2]) + else: + if context.executing_eagerly() and not self._persistent: + raise RuntimeError( + "GradientTape must be created with persistent=True" + " to compute the jacobian with eager execution enabled and with " + " experimental_use_pfor set to False.") + output = pfor_ops.for_loop( + loop_fn, [target.dtype] * len(flat_sources), target_size, + parallel_iterations=parallel_iterations) + + for i, out in enumerate(output): + if out is not None: + new_shape = array_ops.concat( + [target_shape, array_ops.shape(out)[1:]], axis=0) + out = array_ops.reshape(out, new_shape) + if context.executing_eagerly(): + out.set_shape(target_static_shape.concatenate(flat_sources[i].shape)) + output[i] = out + + return nest.pack_sequence_as(sources, output) + + def batch_jacobian(self, + target, + source, + unconnected_gradients=UnconnectedGradients.NONE, + parallel_iterations=None, + experimental_use_pfor=True): + """Computes and stacks per-example jacobians. + + See http://en.wikipedia.org/wiki/jacobian_matrix_and_determinant for the + definition of a Jacobian. This function is essentially an efficient + implementation of the following: + `tf.stack([self.jacobian(y[i], x[i]) for i in range(x.shape[0])])`. + + Note that compared to `GradientTape.jacobian` which computes gradient of + each output value w.r.t each input value, this function is useful when + `target[i,...] is independent of `source[j,...]` for `j != i`. This + independence assumption allows more efficient computation as compared to + `GradientTape.jacobian`. The output, as well as intermediate activations, + are lower dimensional and avoid a bunch of redundant zeros which would + result in the jacobian computation given the independence assumption. + + Example usage: + with tf.GradientTape() as g: + x = tf.constant([[1, 2], [3, 4]], dtype=tf.float32) + g.watch(x) + y = x * x + batch_jacobian = g.batch_jacobian(y, x) + # batch_jacobian is [[[2, 0], [0, 4]], [[6, 0], [0, 8]]] + + Args: + target: A tensor with rank 2 or higher and with shape [b, y1, ..., y_n]. + `target[i,...]` should only depend on `source[i,...]`. + source: A tensor with rank 2 or higher and with shape [b, x1, ..., x_m]. + unconnected_gradients: a value which can either hold 'none' or 'zero' and + alters the value which will be returned if the target and sources are + unconnected. The possible values and effects are detailed in + 'UnconnectedGradients' and it defaults to 'none'. + parallel_iterations: A knob to control how many iterations are dispatched + in parallel. This knob can be used to control the total memory usage. + experimental_use_pfor: If true, uses pfor for computing the Jacobian. Else + uses a tf.while_loop. + + Returns: + A tensor `t` with shape [b, y_1, ..., y_n, x1, ..., x_m] where `t[i, ...]` + is the jacobian of `target[i, ...]` w.r.t. `source[i, ...]`, i.e. stacked + per-example jacobians. + + Raises: + RuntimeError: If called on a non-persistent tape with eager execution + enabled and without enabling experimental_use_pfor. + ValueError: If vectorization of jacobian computation fails or if first + dimension of `target` and `source` do not match. + """ + target_shape = target.shape + if not target_shape.with_rank_at_least(2)[0].is_compatible_with( + source.shape.with_rank_at_least(2)[0]): + raise ValueError( + "Need first dimension of target shape (%s) and " + "source shape (%s) to match." % (target.shape, source.shape)) + if target_shape.is_fully_defined(): + batch_size = int(target_shape[0]) + target_row_size = target_shape.num_elements() // batch_size + else: + target_shape = array_ops.shape(target) + batch_size = target_shape[0] + target_row_size = array_ops.size(target) // batch_size + source_shape = array_ops.shape(source) + # Flatten target to 2-D. + # Note that we push and pop the tape here and below. This is needed since we + # need gradients through the enclosed operations. + self._push_tape() + with ops.control_dependencies( + [check_ops.assert_equal(batch_size, source_shape[0])]): + target = array_ops.reshape(target, [batch_size, target_row_size]) + self._pop_tape() + + def loop_fn(i): + self._push_tape() + y = array_ops.gather(target, i, axis=1) + self._pop_tape() + return self.gradient(y, source, + unconnected_gradients=unconnected_gradients) + + if experimental_use_pfor: + try: + output = pfor_ops.pfor(loop_fn, target_row_size, + parallel_iterations=parallel_iterations) + except ValueError as err: + six.reraise( + ValueError, + ValueError( + str(err) + "\nEncountered an exception while vectorizing the " + "batch_jacobian computation. Vectorization can be disabled by " + "setting experimental_use_pfor to False."), + sys.exc_info()[2]) + else: + if context.executing_eagerly() and not self._persistent: + raise RuntimeError( + "GradientTape must be created with persistent=True" + " to compute the batch_jacobian with eager execution enabled and " + " with experimental_use_pfor set to False.") + output = pfor_ops.for_loop(loop_fn, target.dtype, target_row_size, + parallel_iterations=parallel_iterations) + if output is None: + return None + output = array_ops.reshape(output, + [target_row_size, batch_size, -1]) + output = array_ops.transpose(output, [1, 0, 2]) + new_shape = array_ops.concat([target_shape, source_shape[1:]], axis=0) + return array_ops.reshape(output, new_shape) diff --git a/tensorflow/python/eager/backprop_test.py b/tensorflow/python/eager/backprop_test.py index 274d5320df..3cec40a48f 100644 --- a/tensorflow/python/eager/backprop_test.py +++ b/tensorflow/python/eager/backprop_test.py @@ -74,7 +74,7 @@ class BackpropTest(test.TestCase): tf_g1 = embedding_ops.embedding_lookup(tf_var, tf_ind1) tf_g2 = embedding_ops.embedding_lookup(tf_var, tf_ind2) tf_g3 = embedding_ops.embedding_lookup(tf_var, tf_ind3) - tf_g4 = math_ops.reduce_sum(tf_var * 2.0, reduction_indices=(0, 1)) + tf_g4 = math_ops.reduce_sum(tf_var * 2.0, axis=(0, 1)) tf_y = tf_g1 * tf_g2 * tf_g3 * tf_g4 tf_grad = gradients.gradients(tf_y, [tf_var])[0] @@ -215,7 +215,7 @@ class BackpropTest(test.TestCase): self.assertAllClose(tf_grad.values.eval(), grad.values) tf_opt.apply_gradients([(tf_grad, tf_embedding)]).run() - expected = tf_embedding.eval() + expected = self.evaluate(tf_embedding) opt.apply_gradients([(grad, embedding)]) self.assertAllClose(expected, embedding.read_value()) @@ -233,6 +233,68 @@ class BackpropTest(test.TestCase): self.assertTrue(ordered_variables[0] is v0) self.assertTrue(ordered_variables[1] is v1) + def testTapeNoOpGradient(self): + x = constant_op.constant(3.0) + with backprop.GradientTape() as t: + t.watch(x) + y = x + self.assertEqual(t.gradient(y, x).numpy(), 1.0) + + def testTapeIdentityGradientIsIdentity(self): + x = constant_op.constant(3.0) + with backprop.GradientTape() as t: + t.watch(x) + y = array_ops.identity(x) + self.assertEqual(t.gradient(y, x).numpy(), 1.0) + + def testTapeGradientMultiTargetOneIsSource(self): + x = constant_op.constant(2.0) + with backprop.GradientTape() as t: + t.watch(x) + y = x*x + self.assertEqual(t.gradient([x, y], x).numpy(), 5.0) + + def testTapeNoOpGradientWithMultiTargetAllSource(self): + x = constant_op.constant(3.0) + with backprop.GradientTape() as t: + t.watch(x) + y = x + self.assertEqual(t.gradient([y, y], x).numpy(), 2.0) + + def testTapeNoOpGradientWithMultiTargetMultiSource(self): + x = constant_op.constant(3.0) + y = constant_op.constant(5.0) + with backprop.GradientTape() as t: + t.watch(x) + t.watch(y) + z = y * y + self.assertAllEqual(t.gradient([x, y, z], [x, y]), [1.0, 11.0]) + + def testTapeNoOpOnVariableIsIdentity(self): + v0 = resource_variable_ops.ResourceVariable(1.0) + with backprop.GradientTape() as t: + y = v0.read_value() + self.assertEqual(t.gradient(y, v0).numpy(), 1.0) + + @test_util.assert_no_new_tensors + @test_util.assert_no_garbage_created + def testTapeNoOpGradient2By2(self): + a_2_by_2 = constant_op.constant(2.0, shape=[2, 2]) + with backprop.GradientTape(persistent=True) as tape: + tape.watch(a_2_by_2) + dy_dy = tape.gradient(a_2_by_2, [a_2_by_2])[0] + self.assertAllEqual(dy_dy.numpy(), + constant_op.constant(1.0, shape=[2, 2]).numpy()) + + @test_util.assert_no_new_pyobjects_executing_eagerly + def testTapeNoOpGradientMultiTarget2By2(self): + a_2_by_2 = constant_op.constant(2.0, shape=[2, 2]) + with backprop.GradientTape(persistent=True) as tape: + tape.watch(a_2_by_2) + dy_dy = tape.gradient([a_2_by_2, a_2_by_2], [a_2_by_2])[0] + self.assertAllEqual(dy_dy.numpy(), + constant_op.constant(2.0, shape=[2, 2]).numpy()) + def testTapeStopRecording(self): with backprop.GradientTape() as t: x = resource_variable_ops.ResourceVariable(1.0) @@ -1165,5 +1227,192 @@ class BackpropTest(test.TestCase): self.assertAllEqual(da[0], tf_da[0].eval()) +@test_util.run_all_in_graph_and_eager_modes +class JacobianTest(test.TestCase): + + def _jacobian(self, experimental_use_pfor): + persistent = context.executing_eagerly and not experimental_use_pfor + with backprop.GradientTape(persistent=persistent) as g: + x = constant_op.constant([1., 2.]) + y = constant_op.constant([3., 4.]) + g.watch(x) + g.watch(y) + z = x * x * y + jacobian = g.jacobian(z, [x, y], + experimental_use_pfor=experimental_use_pfor) + answer = [array_ops.diag(2 * x * y), array_ops.diag(x * x)] + return jacobian, answer + + def testPfor(self): + jacobian, answer = self._jacobian(experimental_use_pfor=True) + for j, a in zip(jacobian, answer): + self.assertAllEqual(a, j) + + def testWhileLoop(self): + jacobian, answer = self._jacobian(experimental_use_pfor=False) + for j, a in zip(jacobian, answer): + self.assertAllEqual(a, j) + + def testPforDefun(self): + + @function.defun + def _f(): + return self._jacobian(experimental_use_pfor=True) + + jacobian, answer = _f() + for j, a in zip(jacobian, answer): + self.assertAllEqual(a, j) + + def testWhileLoopDefun(self): + + @function.defun + def _f(): + return self._jacobian(experimental_use_pfor=False) + + jacobian, answer = _f() + for j, a in zip(jacobian, answer): + self.assertAllEqual(a, j) + + def testPersistentTape(self): + if not context.executing_eagerly(): + return + with backprop.GradientTape() as g: + x = constant_op.constant([1.0, 2.0]) + g.watch(x) + y = x * x + with self.assertRaisesRegexp(RuntimeError, 'persistent'): + g.jacobian(y, x, experimental_use_pfor=False) + + def testPforException(self): + var = variables.Variable([1.]) + + @custom_gradient.custom_gradient + def op(x): + def grad(_): + # Note that we perform a stateful operation here that will not be + # compatible with parallel for construct. + with ops.control_dependencies( + [var.assign(random_ops.random_uniform([1]))]): + return constant_op.constant(1.) + return x, grad + + with backprop.GradientTape() as g: + x = constant_op.constant([1., 2.]) + g.watch(x) + y = op(x) + with self.assertRaisesRegexp(ValueError, 'No converter'): + g.jacobian(y, x, experimental_use_pfor=True) + + def test_parallel_iterations(self): + with backprop.GradientTape(persistent=True) as g: + x = constant_op.constant([[1., 2], [3, 4]]) + g.watch(x) + y = math_ops.matmul(x, x) + self.assertAllClose(g.jacobian(y, x, parallel_iterations=2), + g.jacobian(y, x, parallel_iterations=3)) + + +@test_util.run_all_in_graph_and_eager_modes +class BatchJacobianTest(test.TestCase): + + def _batch_jacobian(self, experimental_use_pfor): + persistent = context.executing_eagerly and not experimental_use_pfor + with backprop.GradientTape(persistent=persistent) as g: + x = constant_op.constant([[1., 2.], [3., 4.]]) + y = constant_op.constant([[3., 4.], [5., 6.]]) + g.watch(x) + z = x * x * y + batch_jacobian = g.batch_jacobian( + z, x, experimental_use_pfor=experimental_use_pfor) + answer = array_ops.stack([array_ops.diag(2 * x[0] * y[0]), + array_ops.diag(2 * x[1] * y[1])]) + return batch_jacobian, answer + + def testPfor(self): + batch_jacobian, answer = self._batch_jacobian(experimental_use_pfor=True) + self.assertAllEqual(answer, batch_jacobian) + + def testWhileLoop(self): + batch_jacobian, answer = self._batch_jacobian(experimental_use_pfor=False) + self.assertAllEqual(answer, batch_jacobian) + + def testPforDefun(self): + + @function.defun + def _f(): + return self._batch_jacobian(experimental_use_pfor=True) + + batch_jacobian, answer = _f() + self.assertAllEqual(answer, batch_jacobian) + + def testWhileLoopDefun(self): + + @function.defun + def _f(): + return self._batch_jacobian(experimental_use_pfor=False) + + batch_jacobian, answer = _f() + self.assertAllEqual(answer, batch_jacobian) + + def testPersistentTape(self): + if not context.executing_eagerly(): + return + with backprop.GradientTape() as g: + x = constant_op.constant([[1.0, 2.0]]) + g.watch(x) + y = x * x + with self.assertRaisesRegexp(RuntimeError, 'persistent'): + g.batch_jacobian(y, x, experimental_use_pfor=False) + + def testBadShape(self): + x = random_ops.random_uniform([2, 3]) + with backprop.GradientTape() as g: + y = array_ops.concat([x, x], axis=0) + with self.assertRaisesRegexp(ValueError, 'Need first dimension'): + g.batch_jacobian(y, x) + + def testBadInputRank(self): + x = random_ops.random_uniform([2]) + with backprop.GradientTape() as g: + y = random_ops.random_uniform([2, 2]) + with self.assertRaisesRegexp(ValueError, 'must have rank at least 2'): + g.batch_jacobian(y, x) + + def testBadOutputRank(self): + x = random_ops.random_uniform([2, 2]) + with backprop.GradientTape() as g: + y = random_ops.random_uniform([2]) + with self.assertRaisesRegexp(ValueError, 'must have rank at least 2'): + g.batch_jacobian(y, x) + + def testPforException(self): + var = variables.Variable([1.]) + + @custom_gradient.custom_gradient + def op(x): + def grad(_): + # Note that we perform a stateful operation here that will not be + # compatible with parallel for construct. + with ops.control_dependencies( + [var.assign(random_ops.random_uniform([1]))]): + return constant_op.constant(1.) + return x, grad + + with backprop.GradientTape() as g: + x = constant_op.constant([[1.], [2.]]) + g.watch(x) + y = op(x) + with self.assertRaisesRegexp(ValueError, 'No converter'): + g.batch_jacobian(y, x, experimental_use_pfor=True) + + def test_parallel_iterations(self): + with backprop.GradientTape(persistent=True) as g: + x = constant_op.constant([[1., 2], [3, 4]]) + g.watch(x) + w = constant_op.constant([[1., 2, 3, 4], [5, 6, 7, 8]]) + y = math_ops.matmul(x, w) + self.assertAllClose(g.batch_jacobian(y, x, parallel_iterations=2), + g.batch_jacobian(y, x, parallel_iterations=3)) + if __name__ == '__main__': test.main() diff --git a/tensorflow/python/eager/benchmarks_test.py b/tensorflow/python/eager/benchmarks_test.py index 886715867c..31a7efca82 100644 --- a/tensorflow/python/eager/benchmarks_test.py +++ b/tensorflow/python/eager/benchmarks_test.py @@ -80,7 +80,6 @@ class SubclassedKerasModel(keras.Model): def __init__(self, initializer="ones"): super(SubclassedKerasModel, self).__init__() - self._can_use_graph_functions = True self.layer_a = keras.layers.Dense( 64, kernel_initializer=initializer, bias_initializer="zeros") self.layer_b = keras.layers.Dense( @@ -733,38 +732,38 @@ class MicroBenchmarks(test.Benchmark): assert np.equal(func(), make_keras_model()(data)).all() self._run(func, 30000) - def _benchmark_keras_model_fit(self, model): + def _benchmark_keras_model_fit(self, model, run_eagerly=False): data = random_ops.random_uniform((10, 10), minval=-1, maxval=1) labels = random_ops.random_uniform((10, 10), minval=-1, maxval=1) dataset = dataset_ops.Dataset.from_tensors((data, labels)).repeat() model.compile( gradient_descent.GradientDescentOptimizer(learning_rate=0.001), - loss="mse") + loss="mse", run_eagerly=run_eagerly) func = lambda: model.fit(dataset, epochs=1, steps_per_epoch=1000, verbose=0) # First call is more expensive (creates variables etc.), discount that. model.fit(dataset, epochs=1, steps_per_epoch=1, verbose=0) self._run(func, 1) - def _benchmark_keras_model_evaluate(self, model): + def _benchmark_keras_model_evaluate(self, model, run_eagerly=False): data = random_ops.random_uniform((10, 10), minval=-1, maxval=1) labels = random_ops.random_uniform((10, 10), minval=-1, maxval=1) dataset = dataset_ops.Dataset.from_tensors((data, labels)).repeat() model.compile( gradient_descent.GradientDescentOptimizer(learning_rate=0.001), - loss="mse") + loss="mse", run_eagerly=run_eagerly) func = lambda: model.evaluate(dataset, steps=1000, verbose=0) # First call is more expensive (creates variables etc.), discount that. model.evaluate(dataset, steps=1, verbose=0) self._run(func, 1) - def _benchmark_keras_model_predict(self, model): + def _benchmark_keras_model_predict(self, model, run_eagerly=False): data = random_ops.random_uniform((10, 10), minval=-1, maxval=1) dataset = dataset_ops.Dataset.from_tensors(tuple([data])).repeat() model.compile( gradient_descent.GradientDescentOptimizer(learning_rate=0.001), - loss="mse") + loss="mse", run_eagerly=run_eagerly) func = lambda: model.predict(dataset, steps=1000, verbose=0) # First call is more expensive (creates variables etc.), discount that. model.predict(dataset, steps=1, verbose=0) @@ -780,10 +779,9 @@ class MicroBenchmarks(test.Benchmark): model = SubclassedKerasModel(initializer="glorot_uniform") self._benchmark_keras_model_fit(model) - def benchmark_keras_model_subclassed_fit_disable_defun(self): + def benchmark_keras_model_subclassed_fit_run_model_eagerly(self): model = SubclassedKerasModel(initializer="glorot_uniform") - model._can_use_graph_functions = False - self._benchmark_keras_model_fit(model) + self._benchmark_keras_model_fit(model, run_eagerly=True) def benchmark_keras_model_functional_fit(self): model = make_keras_model(initializer="glorot_uniform") @@ -794,10 +792,9 @@ class MicroBenchmarks(test.Benchmark): model = make_keras_model(initializer="glorot_uniform") self._benchmark_keras_model_fit(model) - def benchmark_keras_model_functional_fit_disable_defun(self): + def benchmark_keras_model_functional_fit_run_model_eagerly(self): model = make_keras_model(initializer="glorot_uniform") - model._can_use_graph_functions = False - self._benchmark_keras_model_fit(model) + self._benchmark_keras_model_fit(model, run_eagerly=True) def benchmark_keras_model_sequential_fit(self): model = make_sequential_keras_model(initializer="glorot_uniform") @@ -808,64 +805,57 @@ class MicroBenchmarks(test.Benchmark): model = make_sequential_keras_model(initializer="glorot_uniform") self._benchmark_keras_model_fit(model) - def benchmark_keras_model_sequential_fit_disable_defun(self): + def benchmark_keras_model_sequential_fit_run_model_eagerly(self): model = make_sequential_keras_model(initializer="glorot_uniform") - model._can_use_graph_functions = False - self._benchmark_keras_model_fit(model) + self._benchmark_keras_model_fit(model, run_eagerly=True) def benchmark_keras_model_subclassed_evaluate(self): model = SubclassedKerasModel(initializer="glorot_uniform") self._benchmark_keras_model_evaluate(model) - def benchmark_keras_model_subclassed_evaluate_disable_defun(self): + def benchmark_keras_model_subclassed_evaluate_run_model_eagerly(self): model = SubclassedKerasModel(initializer="glorot_uniform") - model._can_use_graph_functions = False - self._benchmark_keras_model_evaluate(model) + self._benchmark_keras_model_evaluate(model, run_eagerly=True) def benchmark_keras_model_functional_evaluate(self): model = make_keras_model(initializer="glorot_uniform") self._benchmark_keras_model_evaluate(model) - def benchmark_keras_model_functional_evaluate_disable_defun(self): + def benchmark_keras_model_functional_evaluate_run_model_eagerly(self): model = make_keras_model(initializer="glorot_uniform") - model._can_use_graph_functions = False - self._benchmark_keras_model_evaluate(model) + self._benchmark_keras_model_evaluate(model, run_eagerly=True) def benchmark_keras_model_sequential_evaluate(self): model = make_sequential_keras_model(initializer="glorot_uniform") self._benchmark_keras_model_evaluate(model) - def benchmark_keras_model_sequential_evaluate_disable_defun(self): + def benchmark_keras_model_sequential_evaluate_run_model_eagerly(self): model = make_sequential_keras_model(initializer="glorot_uniform") - model._can_use_graph_functions = False - self._benchmark_keras_model_evaluate(model) + self._benchmark_keras_model_evaluate(model, run_eagerly=True) def benchmark_keras_model_subclassed_predict(self): model = SubclassedKerasModel(initializer="glorot_uniform") self._benchmark_keras_model_predict(model) - def benchmark_keras_model_subclassed_predict_disable_defun(self): + def benchmark_keras_model_subclassed_predict_run_model_eagerly(self): model = SubclassedKerasModel(initializer="glorot_uniform") - model._can_use_graph_functions = False - self._benchmark_keras_model_predict(model) + self._benchmark_keras_model_predict(model, run_eagerly=True) def benchmark_keras_model_functional_predict(self): model = make_keras_model(initializer="glorot_uniform") self._benchmark_keras_model_predict(model) - def benchmark_keras_model_functional_predict_disable_defun(self): + def benchmark_keras_model_functional_predict_run_model_eagerly(self): model = make_keras_model(initializer="glorot_uniform") - model._can_use_graph_functions = False - self._benchmark_keras_model_predict(model) + self._benchmark_keras_model_predict(model, run_eagerly=True) def benchmark_keras_model_sequential_predict(self): model = make_sequential_keras_model(initializer="glorot_uniform") self._benchmark_keras_model_predict(model) - def benchmark_keras_model_sequential_predict_disable_defun(self): + def benchmark_keras_model_sequential_predict_run_model_eagerly(self): model = make_sequential_keras_model(initializer="glorot_uniform") - model._can_use_graph_functions = False - self._benchmark_keras_model_predict(model) + self._benchmark_keras_model_predict(model, run_eagerly=True) def benchmarkScan(self): elems = math_ops.range(1600) diff --git a/tensorflow/python/eager/context.py b/tensorflow/python/eager/context.py index e3fef524bf..2f6b038dda 100644 --- a/tensorflow/python/eager/context.py +++ b/tensorflow/python/eager/context.py @@ -25,7 +25,6 @@ import random import threading from tensorflow.core.protobuf import config_pb2 -from tensorflow.core.protobuf import rewriter_config_pb2 from tensorflow.python import pywrap_tensorflow from tensorflow.python import tf2 from tensorflow.python.framework import c_api_util @@ -86,21 +85,21 @@ class FunctionCallOptions(object): Eager functions are functions decorated with tf.contrib.eager.defun. """ - def __init__(self, executor_type=None, rewriter_config=None): + def __init__(self, executor_type=None, config_proto=None): """Constructor. Args: executor_type: (optional) name of the executor to be used to execute the eager function. If None or an empty string, the default Tensorflow executor will be used. - rewriter_config: (optional) a rewriter_config_pb2.RewriterConfig proto or + config_proto: (optional) a `config_pb2.ConfigProto` proto or a serialized string of that proto. The config used by Grappler when optimizing the function graph. Each concrete function is optimized the first time is called. Changing - rewriter_config after the first call has no effect. - If rewriter_config is None, an empty RewriterConfig will be used. + config_proto after the first call has no effect. + If config_proto is None, an empty RewriterConfig will be used. """ - self.rewriter_config_serialized = rewriter_config + self.config_proto_serialized = config_proto self.executor_type = executor_type @property @@ -112,24 +111,22 @@ class FunctionCallOptions(object): self._executor_type = executor_type @property - def rewriter_config_serialized(self): - return self._rewriter_config_serialized + def config_proto_serialized(self): + return self._config_proto_serialized - @rewriter_config_serialized.setter - def rewriter_config_serialized(self, config): - if isinstance(config, rewriter_config_pb2.RewriterConfig): - self._rewriter_config_serialized = config.SerializeToString() + @config_proto_serialized.setter + def config_proto_serialized(self, config): + if isinstance(config, config_pb2.ConfigProto): + self._config_proto_serialized = config.SerializeToString() elif isinstance(config, str): - self._rewriter_config_serialized = config + self._config_proto_serialized = config elif config is None: - self._rewriter_config_serialized = rewriter_config_pb2.RewriterConfig( - ).SerializeToString() + self._config_proto_serialized = ( + config_pb2.ConfigProto().SerializeToString()) else: - raise ValueError( - "the rewriter config must be either a " - "rewriter_config_pb2.RewriterConfig, or a serialized string of that " - "proto or None. got: {}" - .format(type(config))) + raise ValueError("the rewriter config must be either a " + "config_pb2.ConfigProto, or a serialized string of that " + "proto or None. got: {}".format(type(config))) # TODO(agarwal): better name ? @@ -152,14 +149,12 @@ class _EagerContext(threading.local): # Default rewriter config corresponds to turning all default grappler # optimizations on. - base_config = rewriter_config_pb2.RewriterConfig() + base_config = config_pb2.ConfigProto() - if config is not None and config.HasField( - "graph_options") and config.graph_options.HasField("rewrite_options"): - base_config.Merge(config.graph_options.rewrite_options) + if config is not None: + base_config.MergeFrom(config) - self.function_call_options = FunctionCallOptions( - rewriter_config=base_config) + self.function_call_options = FunctionCallOptions(config_proto=base_config) ContextSwitch = collections.namedtuple( @@ -897,21 +892,21 @@ def export_run_metadata(): return context().export_run_metadata() -def function_rewriter_config(rewriter_config): +def function_config_proto(config_proto): """Context manager for setting the grappler rewrite config. This config is used by Grappler when optimizing the function graph. Args: - rewriter_config: a rewriter_config_pb2.RewriterConfig proto or + config_proto: a `config_pb2.ConfigProto` proto or a serialized string of that proto or None. If None, the default instance - of rewriter_config_pb2.RewriterConfig will be used. + of `config_pb2.ConfigProto` will be used. Returns: A context manager. """ def _set_options_func(options): - options.rewriter_config_serialized = rewriter_config + options.config_proto_serialized = config_proto return context().function_call_options(_set_options_func) diff --git a/tensorflow/python/eager/def_function.py b/tensorflow/python/eager/def_function.py index cad6721c70..6bacd7a962 100644 --- a/tensorflow/python/eager/def_function.py +++ b/tensorflow/python/eager/def_function.py @@ -214,7 +214,7 @@ class PolymorphicFunction(object): python_function, name, input_signature=None, - autograph=False, + autograph=True, experimental_autograph_options=None): """Initializes a polymorphic function. @@ -503,7 +503,7 @@ class PolymorphicFunction(object): @tf_export("function", v1=[]) def function(func=None, input_signature=None, - autograph=False, + autograph=True, experimental_autograph_options=None): """Creates a callable TensorFlow graph from a Python function. @@ -552,9 +552,9 @@ def function(func=None, return x + tf.to_float(c) assert int(c) == 0 - assert f(1.0) == 3.0 + assert f(1.0) == 2.0 assert int(c) == 1 - assert f(1.0) == 4.0 + assert f(1.0) == 3.0 assert int(c) == 2 ``` diff --git a/tensorflow/python/eager/def_function_test.py b/tensorflow/python/eager/def_function_test.py index f0f71a219e..4100a10044 100644 --- a/tensorflow/python/eager/def_function_test.py +++ b/tensorflow/python/eager/def_function_test.py @@ -17,6 +17,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import functools from tensorflow.python.eager import backprop from tensorflow.python.eager import def_function @@ -149,9 +150,9 @@ class DefFunctionTest(test.TestCase): result = fn(3.0) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) self.assertAllEqual(sess.run(state[0]), 2.0) - self.assertAllEqual(sess.run(result), 6.0) + self.assertAllEqual(self.evaluate(result), 6.0) def testLegacyGraphModeVariablesNonTrivialInitializer(self): with ops.Graph().as_default(), self.test_session() as sess: @@ -168,9 +169,9 @@ class DefFunctionTest(test.TestCase): result = fn(3.0) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) self.assertAllEqual(sess.run(state[0]), 6.0) - self.assertAllEqual(sess.run(result), 18.0) + self.assertAllEqual(self.evaluate(result), 18.0) def testLegacyGraphModeInputDependentInitializerFails(self): with ops.Graph().as_default(): @@ -207,6 +208,18 @@ class DefFunctionTest(test.TestCase): m1 = MyModel() self.assertAllEqual(m1.apply(3.0), 6.0) + def test_functools_partial(self): + self.assertAllClose( + 3., + def_function.function(functools.partial(lambda x, y: x + y, 1.))( + constant_op.constant(2.))) + + def test_unspecified_default_argument(self): + wrapped = def_function.function( + lambda x, y=2: x + y, + input_signature=[tensor_spec.TensorSpec((), dtypes.int32)]) + self.assertEqual(3, wrapped(constant_op.constant(1)).numpy()) + def test_optimizer(self): x = constant_op.constant([[3., 4.]]) y = constant_op.constant([2.]) diff --git a/tensorflow/python/eager/execution_callbacks.py b/tensorflow/python/eager/execution_callbacks.py index 80ff4459d6..28b6b84a82 100644 --- a/tensorflow/python/eager/execution_callbacks.py +++ b/tensorflow/python/eager/execution_callbacks.py @@ -18,6 +18,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import contextlib import functools import numpy as np @@ -28,8 +29,13 @@ from tensorflow.python.eager import core from tensorflow.python.eager import execute from tensorflow.python.platform import tf_logging as logging -_DEFAULT_CALLBACK_ACTION = "raise" -_VALID_CALLBACK_ACTIONS = (None, "ignore", "print", "raise", "warn") +IGNORE = "ignore" +PRINT = "print" +RAISE = "raise" +WARN = "warn" + +_DEFAULT_CALLBACK_ACTION = RAISE +_VALID_CALLBACK_ACTIONS = (None, IGNORE, PRINT, RAISE, WARN) # TODO(cais): Consider moving this exception class to errors_impl.py. @@ -335,3 +341,38 @@ def seterr(inf_or_nan=None): functools.partial(inf_nan_callback, action=inf_or_nan)) return old_settings + + +@contextlib.contextmanager +def errstate(inf_or_nan=None): + """Context manager setting error state. + + Example: + ``` + c = tf.log(0.) # -inf + + with errstate(inf_or_nan="raise"): + tf.log(0.) # <-- Raises InfOrNanError. + ``` + + Args: + inf_or_nan: Set action for infinity (`inf`) and NaN (`nan`) values. + Possible values: `{IGNORE, PRINT, RAISE, WARN}`. + `IGNORE`: take no action when `inf` values appear. + `PRINT`: print a warning to `stdout`. + `RAISE`: raise an `InfOrNanError`. + `WARN`: print a warning using `tf.logging.warn`. + A value of `None` leads to no change in the action of the condition. + + Yields: + None. + + Raises: + ValueError: If the value of any keyword arguments is invalid. + """ + if not context.executing_eagerly(): + yield + else: + old_settings = seterr(inf_or_nan=inf_or_nan) + yield + seterr(**old_settings) diff --git a/tensorflow/python/eager/execution_callbacks_test.py b/tensorflow/python/eager/execution_callbacks_test.py new file mode 100644 index 0000000000..5594ab5f12 --- /dev/null +++ b/tensorflow/python/eager/execution_callbacks_test.py @@ -0,0 +1,55 @@ +# Copyright 2018 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 eager execution_callbacks.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.eager import execution_callbacks +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import ops +from tensorflow.python.ops import math_ops +from tensorflow.python.platform import test + + +def log_zero(): + """Computes `log(0.0)`.""" + return math_ops.log(constant_op.constant(0.)) + + +class ExecutionCallbacksTest(test.TestCase): + + def test_errstate_inf_raise(self): + with execution_callbacks.errstate(inf_or_nan=execution_callbacks.RAISE): + with self.assertRaises(execution_callbacks.InfOrNanError): + log_zero() + + def test_errstate_inf_ignore(self): + with execution_callbacks.errstate(inf_or_nan=execution_callbacks.IGNORE): + self.assertEqual(-float("inf"), log_zero().numpy()) + + def test_errstate_nesting(self): + with execution_callbacks.errstate(inf_or_nan=execution_callbacks.RAISE): + with execution_callbacks.errstate(inf_or_nan=execution_callbacks.IGNORE): + self.assertEqual(-float("inf"), log_zero().numpy()) + + with self.assertRaises(execution_callbacks.InfOrNanError): + log_zero() + + +if __name__ == "__main__": + ops.enable_eager_execution() + test.main() diff --git a/tensorflow/python/eager/function.py b/tensorflow/python/eager/function.py index c429dd359b..9d05a660b1 100644 --- a/tensorflow/python/eager/function.py +++ b/tensorflow/python/eager/function.py @@ -48,6 +48,7 @@ from tensorflow.python.ops import custom_gradient from tensorflow.python.ops import functional_ops from tensorflow.python.ops import gradients_impl from tensorflow.python.ops import resource_variable_ops +from tensorflow.python.platform import tf_logging as logging from tensorflow.python.util import compat from tensorflow.python.util import nest from tensorflow.python.util import tf_decorator @@ -66,6 +67,11 @@ WHITELIST_FUNCTION_ATTRIBUTE_REGEX = [ BACKWARD_FUNCTION_ATTRIBUTE_NAME ] +CacheKey = collections.namedtuple("CacheKey", [ + "input_signature", "parent_graph", "device_functions", "colocation_stack", + "uses_xla" +]) + def _parse_func_attrs(attributes): """Convert the keyword arguments into function_def attributes. @@ -83,8 +89,8 @@ def _parse_func_attrs(attributes): """ attrs = {} for key, value in attributes.items(): - if not any([re.match(reg, key) - for reg in WHITELIST_FUNCTION_ATTRIBUTE_REGEX]): + if not any(re.match(reg, key) + for reg in WHITELIST_FUNCTION_ATTRIBUTE_REGEX): raise ValueError("Attribute name is not whitelisted. " "Whitelisted: prefix %s, got: %s" % (WHITELIST_FUNCTION_ATTRIBUTE_REGEX, key)) @@ -260,7 +266,7 @@ class _EagerDefinedFunction(object): f=self, tout=self._output_types, executing_eagerly=executing_eagerly, - config=function_call_options.rewriter_config_serialized, + config=function_call_options.config_proto_serialized, executor_type=function_call_options.executor_type) if executing_eagerly: @@ -418,7 +424,10 @@ class Function(object): if (tape.should_record(tensor_inputs) or tape.should_record(self._captured_inputs)): - return self._backprop_call(args) + if context.executing_eagerly(): + return self._eager_backprop_call(args) + else: + return self._backprop_call_with_delayed_rewrite(args) # Only need to override the gradient in graph mode and when we have outputs. if context.executing_eagerly() or not self.outputs: @@ -444,37 +453,34 @@ class Function(object): name: The name to register the gradient as. """ @ops.RegisterGradient(name) - def grad_fn(op, *doutputs): # pylint: disable=unused-variable - """Gradients of this function.""" - if self._backward_graph_function is None: - self._construct_backprop_function() + def _registered_grad_fn(op, *doutputs): # pylint: disable=unused-variable + return self._grad_fn(op, *doutputs) - # pylint: disable=protected-access - self._forward_function.add_to_graph(op.graph) - num_inference_outputs = self._inference_function._num_outputs - - # Rewrite an inference call op to be a forward call op - if op.get_attr("f").name.encode() == self._inference_function.name: - func = attr_value_pb2.AttrValue( - func=attr_value_pb2.NameAttrList( - name=self._forward_function.name)) - op._set_attr("f", func) - types = attr_value_pb2.AttrValue.ListValue( - type=self._forward_function._output_types) - op._set_attr("Tout", attr_value_pb2.AttrValue(list=types)) - for i in range( - num_inference_outputs, len(self._forward_function._output_types)): - t = ops.Tensor(op, i, self._forward_function._output_types[i]) - t.set_shape(self._forward_function._output_shapes[i]) - func_graph_output = self._forward_function._func_graph_outputs[i] - custom_gradient.copy_handle_data(func_graph_output, t) - op._outputs.append(t) - # pylint: enable=protected-access - # Compute the gradients using the side outputs - side_outputs = op.outputs[num_inference_outputs:] - args = list(doutputs[:num_inference_outputs]) + list(side_outputs) - return self._backward_graph_function._call_flat( # pylint: disable=protected-access - (a for a in args if a is not None)) + def _grad_fn(self, op, *doutputs): + """Gradients of this function.""" + if self._backward_graph_function is None: + self._construct_backprop_function() + + # pylint: disable=protected-access + self._forward_function.add_to_graph(op.graph) + num_inference_outputs = self._inference_function._num_outputs + + # Rewrite an inference call op to be a forward call op + if op.get_attr("f").name.encode() == self._inference_function.name: + op._set_func_attr("f", self._forward_function.name) + op._set_type_list_attr("Tout", self._forward_function._output_types) + op._add_outputs( + self._forward_function._output_types[num_inference_outputs:], + self._forward_function._output_shapes[num_inference_outputs:]) + for i in range(num_inference_outputs, len(op.outputs)): + func_graph_output = self._forward_function._func_graph_outputs[i] + custom_gradient.copy_handle_data(func_graph_output, op.outputs[i]) + # pylint: enable=protected-access + # Compute the gradients using the side outputs + side_outputs = op.outputs[num_inference_outputs:] + args = list(doutputs[:num_inference_outputs]) + list(side_outputs) + return self._backward_graph_function._call_flat( # pylint: disable=protected-access + (a for a in args if a is not None)) @property def name(self): @@ -617,10 +623,13 @@ class Function(object): self._func_graph.outputs + backwards_graph_captures, forward_function_attr) - def _backprop_call(self, args): + def _eager_backprop_call(self, args): """Calls the forward function and records the result on a tape. - (Only records results on a tape if the function has outputs) + This method fully constructs the forward and backward functions before + calling the function and recording them on the tape. + + (Only records results on a tape if the function has outputs). Args: args: All inputs to the function, including resolved captured inputs @@ -662,6 +671,46 @@ class Function(object): args, backward_function) return self._build_call_outputs(real_outputs) + def _backprop_call_with_delayed_rewrite(self, args): + """Calls the inference function and records the result on a tape. + + The recorded backwards function will construct the backwards graph and + rewrite the inference function to the forward function. This only happens + if the recorded backwards function ends up being used to compute gradients. + + This approach avoids constructing unnecessary graphs, but it only works if + we are calling this function when not executing eagerly. + + (Only records results on a tape if the function has outputs) + + Args: + args: All inputs to the function, including resolved captured inputs + + Returns: + The call output. + """ + ctx = context.context() + + if not self._gradient_name: + self._gradient_name = "PartitionedCall-%s" % ops.uid() + self._register_gradient(self._gradient_name) + with ops.get_default_graph().gradient_override_map( + {"PartitionedCall": self._gradient_name, + "StatefulPartitionedCall": self._gradient_name}): + outputs = self._inference_function.call(ctx, args) + + if isinstance(outputs, ops.Operation) or outputs is None: + return outputs + + call_op = outputs[0].op + + def backward_function(*args): + return self._grad_fn(call_op, *args) + + tape.record_operation(self._inference_function.signature.name, outputs, + args, backward_function) + return self._build_call_outputs(outputs) + def _build_call_outputs(self, result): """Maps the fdef output list to actual output structure. @@ -724,7 +773,7 @@ class PolymorphicFunction(object): name, input_signature=None, attributes=None, - autograph=False): + autograph=True): """Initializes a polymorphic function. Args: @@ -927,17 +976,17 @@ class PolymorphicFunction(object): """Computes the cache key given inputs and execution context.""" if self._input_signature is None: inputs = (args, kwargs) if kwargs else args - cache_key = pywrap_tensorflow.TFE_Py_EncodeArg(inputs) + input_signature = pywrap_tensorflow.TFE_Py_EncodeArg(inputs) else: del args, kwargs - cache_key = self._flat_input_signature + input_signature = self._flat_input_signature ctx = context.context() with ops.init_scope(): # The graph, or whether we're executing eagerly, should be a part of the # cache key so we don't improperly capture tensors such as variables. executing_eagerly = ctx.executing_eagerly() - execution_context = executing_eagerly or ops.get_default_graph() + parent_graph = None if executing_eagerly else ops.get_default_graph() # pylint: disable=protected-access default_graph = ops.get_default_graph() @@ -966,8 +1015,8 @@ class PolymorphicFunction(object): else: device_functions = () # pylint: enable=protected-access - return (cache_key, execution_context, device_functions, colocation_stack, - uses_xla) + return CacheKey(input_signature, parent_graph, device_functions, + colocation_stack, uses_xla) def _canonicalize_function_inputs(self, *args, **kwargs): """Canonicalizes `args` and `kwargs`. @@ -1039,16 +1088,21 @@ class PolymorphicFunction(object): return inputs, kwargs else: assert not kwargs + signature_relevant_inputs = inputs[:len(self._input_signature)] try: - nest.assert_same_structure(self._input_signature, inputs) + nest.assert_same_structure(self._input_signature, + signature_relevant_inputs) except (ValueError, TypeError): raise ValueError("Structure of Python function inputs does not match " "input_signature.") - if any(not pywrap_tensorflow.IsTensor(arg) for arg in flat_inputs): + signature_inputs_flat = nest.flatten(signature_relevant_inputs) + if any(not pywrap_tensorflow.IsTensor(arg) + for arg in signature_inputs_flat): raise ValueError("When input_signature is provided, all inputs to " "the Python function must be Tensors.") if any(not spec.is_compatible_with(other) - for spec, other in zip(self._flat_input_signature, flat_inputs)): + for spec, other in zip(self._flat_input_signature, + signature_inputs_flat)): raise ValueError("Python inputs incompatible with input_signature: " "inputs (%s), input_signature (%s)" % (str(inputs), str(self._input_signature))) @@ -1083,6 +1137,9 @@ class PolymorphicFunction(object): "must be hashable.") if graph_function is None: + logging.vlog(1, + "Creating new FuncGraph for Python function %r (key: %r)", + self._python_function, cache_key) if self._input_signature is None: arglen = len(args) else: @@ -1137,7 +1194,7 @@ def validate_signature(signature): "a possibly nested sequence of TensorSpec objects.") -def defun(func=None, input_signature=None, autograph=False): +def defun(func=None, input_signature=None, autograph=True): """Compiles a Python function into a callable TensorFlow graph. `defun` (short for "define function") trace-compiles a Python function @@ -1470,7 +1527,7 @@ def defun(func=None, input_signature=None, autograph=False): def defun_with_attributes(func=None, input_signature=None, attributes=None, - autograph=False): + autograph=True): """Compiles a Python function into a callable TensorFlow graph. This function supports adding extra function attributes. See detailed diff --git a/tensorflow/python/eager/function_gradients_test.py b/tensorflow/python/eager/function_gradients_test.py new file mode 100644 index 0000000000..9b83f57089 --- /dev/null +++ b/tensorflow/python/eager/function_gradients_test.py @@ -0,0 +1,756 @@ +# 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. +# ============================================================================== + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from absl.testing import parameterized + +from tensorflow.core.protobuf import config_pb2 +from tensorflow.python.eager import backprop +from tensorflow.python.eager import context +from tensorflow.python.eager import def_function +from tensorflow.python.eager import function +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops +from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import control_flow_ops +from tensorflow.python.ops import gradients_impl +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import resource_variable_ops +from tensorflow.python.ops import variable_scope +from tensorflow.python.ops import variables +from tensorflow.python.platform import test +from tensorflow.python.util import nest + + +class FunctionGradientsTest(test.TestCase, parameterized.TestCase): + + def testGraphModeWithGradients(self): + v = resource_variable_ops.ResourceVariable(1.0, name='v') + + @def_function.function + def step(): + def inner(): + return v * v + + return backprop.implicit_grad(inner)()[0][0] + + self.assertAllEqual(step(), 2.0) + + def testGraphGradientVariable(self): + with ops.Graph().as_default(), self.cached_session(): + v = variables.Variable(1.0) + + @def_function.function + def f(): + return 2.0 * v + + node = f() + grads, = gradients_impl.gradients(node, v) + v.initializer.run() + self.assertAllEqual(grads.eval(), 2.0) + self.assertEqual(grads.shape, v.shape) + + def testSymGradGatherNd(self): + with ops.Graph().as_default(), self.cached_session() as sess: + + @def_function.function + def f(x): + return array_ops.gather_nd(x, [[0]]) + + c = constant_op.constant([[2.]]) + f_c = f(c) + g, = gradients_impl.gradients(f_c, c) + self.assertAllEqual(self.evaluate(g).values, [[1.0]]) + + def testNoSymGradNestedDefun(self): + + @def_function.function + def outer(): + + @def_function.function + def f(x): + return array_ops.gather_nd(x, [[0]]) + + c = constant_op.constant([[2.]]) + f_c = f(c) + g, = gradients_impl.gradients(f_c, c) + self.assertIsInstance(g, ops.IndexedSlices) + + outer() + + def testGraphFunctionWithGradients(self): + v = resource_variable_ops.ResourceVariable(1.0, name='v') + + @def_function.function + def step(): + def inner(): + return v * v + + return backprop.implicit_grad(inner)()[0][0] + + step_op = step.get_concrete_function() + self.assertEqual(step_op.output_dtypes, dtypes.float32) + self.assertEqual(step_op.output_shapes, tensor_shape.TensorShape([])) + self.assertAllEqual(step_op(), 2.0) + + @test_util.run_in_graph_and_eager_modes() + def testDefunCondGradient(self): + + @def_function.function + def f(x): + return control_flow_ops.cond(x > 0.5, lambda: 2 * x, lambda: 3 * x) + + with backprop.GradientTape() as t: + x = constant_op.constant(1.0) + t.watch(x) + y = f(x) + self.assertAllEqual(self.evaluate(t.gradient(y, x)), 2.0) + + @test_util.run_in_graph_and_eager_modes() + def testGraphLoopGradient(self): + + @def_function.function + def f(x): + return control_flow_ops.while_loop(lambda _, i: i < 2, + lambda x, i: (2*x, i + 1), + [x, 0])[0] + + with backprop.GradientTape() as t: + x = constant_op.constant(1.0) + t.watch(x) + y = f(x) + self.assertAllEqual(self.evaluate(t.gradient(y, x)), 4.0) + + def testDefunDifferentiable(self): + v = resource_variable_ops.ResourceVariable(1.0) + + @def_function.function + def f(): + return v * v + + self.assertAllEqual(backprop.implicit_grad(f)()[0][0], 2.0) + + def testDefunCanBeDifferentiatedTwice(self): + v = resource_variable_ops.ResourceVariable(1.0) + + @def_function.function + def f(): + return v * v + + self.assertAllEqual(backprop.implicit_grad(f)()[0][0], 2.0) + # Ensure that v is watched again. + self.assertAllEqual(backprop.implicit_grad(f)()[0][0], 2.0) + + def testSymbolicGradientVariableNoneNotZerosLike(self): + with ops.Graph().as_default(): + v = variables.Variable(1.0) + + @def_function.function + def f(x, v): + v.read_value() + return x * x + + x = constant_op.constant(1.0) + l = f(x, v) + _, dv = gradients_impl.gradients(l, [x, v]) + with self.cached_session(): + v.initializer.run() + self.assertEqual(dv, None) + + def testDefunCallBackprop(self): + + @def_function.function + def f(x): + return math_ops.add(x, x) + + @def_function.function + def g(x): + return backprop.gradients_function(f, [0])(x)[0] + + self.assertAllEqual(2, g(constant_op.constant(2.))) + + @test_util.run_deprecated_v1 + def testGraphModeEagerGradError(self): + with context.graph_mode(): + def f(): + x = variable_scope.get_variable( + 'v', initializer=constant_op.constant(1.0)) + return x * constant_op.constant(2.0) + + with self.assertRaisesRegexp(ValueError, + 'No trainable variables were accessed'): + backprop.implicit_val_and_grad(f)() + + def testDefunCallBackpropUsingSameObjectForMultipleArguments(self): + + @def_function.function + def g(x): + return backprop.gradients_function(math_ops.multiply, [0, 1])(x, x) + + def np_g(x): + return [d.numpy() for d in g(x)] + + x = constant_op.constant(1.) + self.assertAllEqual([1., 1.], np_g(x)) + self.assertAllEqual([1., 1.], np_g(1.)) + + def testGradientTensorConversionWithDefun(self): + three = resource_variable_ops.ResourceVariable(3.0, name='v') + + @def_function.function + def f(x): + return math_ops.add(x, three) + + def g(x): + return f(x) + + g = backprop.implicit_grad(g)(constant_op.constant(1.0))[0][0] + self.assertAllEqual(g, 1.0) + + def testGradient(self): + matmul = def_function.function(math_ops.matmul) + + def sq(x): + return matmul(x, x, transpose_a=True) + + t = constant_op.constant([[1.0, 2.0], [3.0, 4.0]]) + grad_t, = backprop.gradients_function(sq, [0])(t) + self.assertAllEqual(grad_t, [[6, 6], [14, 14]]) + + def testGradientInFunction(self): + + @def_function.function + def f(x): + return backprop.gradients_function(lambda y: y * y, [0])(x)[0] + + self.assertAllEqual(f(constant_op.constant(1.0)), 2.0) + + def testGradientOfGatherWithDefun(self): + v = resource_variable_ops.ResourceVariable([0.0, 1.0, 2.0]) + + def sum_gather(): + return math_ops.reduce_sum(array_ops.gather(v, [1, 2])) + + grad_fn = backprop.implicit_grad(sum_gather) + gradient = grad_fn() + defun_grad_fn = backprop.implicit_grad(def_function.function(sum_gather)) + defun_gradient = defun_grad_fn() + self.assertEqual(len(gradient), len(defun_gradient)) + + gradient = gradient[0][0] + defun_gradient = defun_gradient[0][0] + self.assertAllEqual(gradient.values, defun_gradient.values) + self.assertAllEqual(gradient.indices, defun_gradient.indices) + self.assertAllEqual(gradient.dense_shape, defun_gradient.dense_shape) + + def testDifferentiableFunctionNoneOutputs(self): + + @def_function.function + def my_function(x): + return x, None + + def wrapper(x): + return my_function(x)[0] + + g = backprop.gradients_function(wrapper, [0])(constant_op.constant(0.0)) + self.assertAllEqual(g[0], 1.) + + @def_function.function + def foo(a): + return None, a * a + + x = constant_op.constant(5.0) + with backprop.GradientTape() as tp: + tp.watch(x) + none, r = foo(x) + g = tp.gradient(r, x) + + self.assertIs(none, None) + self.assertAllEqual(r, 25.0) + self.assertAllEqual(g, 2 * 5.0) + + @test_util.run_in_graph_and_eager_modes + def testNestedDifferentiableFunction(self): + @def_function.function + def inner_fn(a, b): + return a * math_ops.add(a, b) + + @def_function.function + def outer_fn(x): + return inner_fn(x, 1.0) + + x = constant_op.constant(5.0) + with backprop.GradientTape() as tp: + tp.watch(x) + result = outer_fn(x) + grad = tp.gradient(result, x) + + self.assertAllEqual(grad, 2 * 5.0 + 1.0) + + @test_util.run_in_graph_and_eager_modes + def testDeeplyNestedDifferentiableFunction(self): + @def_function.function + def inner_inner_fn(a, b): + return math_ops.add(a, b) + + @def_function.function + def inner_fn(a, b): + return inner_inner_fn(a, b) + + @def_function.function + def middle_fn(a, b): + return a * inner_fn(a, b) + + @def_function.function + def outer_fn(x): + return middle_fn(x, 1.0) + + x = constant_op.constant(5.0) + with backprop.GradientTape() as tp: + tp.watch(x) + result = outer_fn(x) + grad = tp.gradient(result, x) + + self.assertAllEqual(grad, 2 * 5.0 + 1.0) + + @test_util.run_in_graph_and_eager_modes + def testDeeplyNestedDifferentiableFunctionWithMultipleGradCalls(self): + @def_function.function + def inner_fn(a, b): + return math_ops.add(a, b) + + @def_function.function + def middle_fn(a, b): + return math_ops.mul(a, inner_fn(a, b)) + + @def_function.function + def outer_fn(x): + return middle_fn(x, 3.0) + + x = constant_op.constant(5.0) + self.assertAllEqual(outer_fn(x), 5.0 * (5.0 + 3.0)) + + with backprop.GradientTape() as tp: + tp.watch(x) + result = outer_fn(x) + grad = tp.gradient(result, x) + + self.assertAllEqual(grad, 2 * 5.0 + 3.0) + self.assertAllEqual(outer_fn(x), 5.0 * (5.0 + 3.0)) + self.assertAllEqual(middle_fn(3.0, x), 3.0 * (3.0 + 5.0)) + + with backprop.GradientTape() as tp: + tp.watch(x) + result = outer_fn(x) + grad = tp.gradient(result, x) + + self.assertAllEqual(grad, 2 * 5.0 + 3.0) + + y = constant_op.constant(4.0) + with backprop.GradientTape() as tp: + tp.watch(y) + result = outer_fn(y) + grad = tp.gradient(result, y) + + self.assertAllEqual(grad, 2 * 4.0 + 3.0) + + with backprop.GradientTape() as tp: + tp.watch(y) + result = inner_fn(y, y) + grad = tp.gradient(result, y) + + self.assertAllEqual(grad, 2.0) + + @test_util.run_in_graph_and_eager_modes + def testDeeplyNestedDifferentiableFunctionGradientTapeInDefun(self): + @def_function.function + def inner_inner_fn(a, b): + return math_ops.add(a, b) + + @def_function.function + def inner_fn(a, b): + return inner_inner_fn(a, b) + + @def_function.function + def middle_fn(a, b): + return a * inner_fn(a, b) + + @def_function.function + def outer_fn(x): + with backprop.GradientTape() as tp: + tp.watch(x) + result = middle_fn(x, 1.0) + grad = tp.gradient(result, x) + return grad + + x = constant_op.constant(5.0) + grad = outer_fn(x) + self.assertAllEqual(grad, 2 * 5.0 + 1.0) + + @test_util.run_in_graph_and_eager_modes + def testDeeplyNestedDifferentiableFunctionGradientTapeInNestedDefun(self): + @def_function.function + def inner_inner_fn(a, b): + return math_ops.add(a, b) + + @def_function.function + def inner_fn(a, b): + return inner_inner_fn(a, b) + + @def_function.function + def middle_fn(a, b): + return a * inner_fn(a, b) + + @def_function.function + def almost_outer_fn(x): + with backprop.GradientTape() as tp: + tp.watch(x) + result = middle_fn(x, 1.0) + grad = tp.gradient(result, x) + return grad + + @def_function.function + def outer_fn(x): + return almost_outer_fn(x) + + x = constant_op.constant(5.0) + grad = outer_fn(x) + self.assertAllEqual(grad, 2 * 5.0 + 1.0) + + @test_util.run_in_graph_and_eager_modes + def testDeeplyNestedDifferentiableFunctionGradientTapeInMultNestedDefun(self): + @def_function.function + def inner_inner_fn(a, b): + return math_ops.add(a, b) + + @def_function.function + def inner_fn(a, b): + return inner_inner_fn(a, b) + + @def_function.function + def middle_fn(a, b): + return a * inner_fn(a, b) + + @def_function.function + def almost_outer_fn(x): + with backprop.GradientTape() as tp: + tp.watch(x) + result = middle_fn(x, 1.0) + grad = tp.gradient(result, x) + return grad + + @def_function.function + def outer_fn(x): + return almost_outer_fn(x) + + @def_function.function + def outer_outer_fn(x): + return outer_fn(x) + + x = constant_op.constant(5.0) + grad = outer_outer_fn(x) + self.assertAllEqual(grad, 2 * 5.0 + 1.0) + + @test_util.run_in_graph_and_eager_modes + def testDeeplyNestedDifferentiableFunctionTFGradientInDefun(self): + @def_function.function + def inner_inner_fn(a, b): + return math_ops.add(a, b) + + @def_function.function + def inner_fn(a, b): + return inner_inner_fn(a, b) + + @def_function.function + def middle_fn(a, b): + return a * inner_fn(a, b) + + @def_function.function + def outer_fn(x): + result = middle_fn(x, 1.0) + return gradients_impl.gradients(result, [x])[0] + + x = constant_op.constant(5.0) + grad = outer_fn(x) + self.assertAllEqual(grad, 2 * 5.0 + 1.0) + + @test_util.run_in_graph_and_eager_modes + def testDeeplyNestedDifferentiableFunctionTFGradientInNestedDefun(self): + @def_function.function + def inner_inner_fn(a, b): + return math_ops.add(a, b) + + @def_function.function + def inner_fn(a, b): + return inner_inner_fn(a, b) + + @def_function.function + def middle_fn(a, b): + return a * inner_fn(a, b) + + @def_function.function + def almost_outer_fn(x): + result = middle_fn(x, 1.0) + return gradients_impl.gradients(result, [x])[0] + + @def_function.function + def outer_fn(x): + return almost_outer_fn(x) + + x = constant_op.constant(5.0) + grad = outer_fn(x) + self.assertAllEqual(grad, 2 * 5.0 + 1.0) + + @test_util.run_in_graph_and_eager_modes + def testDeeplyNestedDifferentiableFunctionTFGradientInMultNestedDefun(self): + @def_function.function + def inner_inner_fn(a, b): + return math_ops.add(a, b) + + @def_function.function + def inner_fn(a, b): + return inner_inner_fn(a, b) + + @def_function.function + def middle_fn(a, b): + return a * inner_fn(a, b) + + @def_function.function + def almost_outer_fn(x): + result = middle_fn(x, 1.0) + return gradients_impl.gradients(result, [x])[0] + + @def_function.function + def outer_fn(x): + return almost_outer_fn(x) + + @def_function.function + def outer_outer_fn(x): + return outer_fn(x) + + x = constant_op.constant(5.0) + grad = outer_outer_fn(x) + self.assertAllEqual(grad, 2 * 5.0 + 1.0) + + def testDeeplyNestedDifferentiableFunctionWithVariable(self): + var = variables.Variable(constant_op.constant(1.0)) + + @def_function.function + def inner_fn(a, b): + return math_ops.add(a, b) + + @def_function.function + def middle_fn(a, b): + return a * inner_fn(a, b) + + @def_function.function + def outer_fn(x): + return middle_fn(x, var) + + x = constant_op.constant(5.0) + with backprop.GradientTape() as tp: + tp.watch(x) + result = outer_fn(x) + grad = tp.gradient(result, x) + + self.assertAllEqual(grad, 2 * 5.0 + 1.0) + + def testDeeplyNestedDifferentiableFunctionWithVariableMultipleGradCalls(self): + v = variables.Variable(constant_op.constant(3.0)) + + @def_function.function + def inner_fn(a, b): + return math_ops.add(a, b) + + @def_function.function + def middle_fn(a, b): + return math_ops.mul(a, inner_fn(a, b)) + + @def_function.function + def outer_fn(x): + return middle_fn(x, v) + + x = constant_op.constant(5.0) + self.assertAllEqual(outer_fn(x), 5.0 * (5.0 + 3.0)) + + with backprop.GradientTape() as tp: + tp.watch(x) + result = outer_fn(x) + grad = tp.gradient(result, x) + + self.assertAllEqual(grad, 2 * 5.0 + 3.0) + self.assertAllEqual(outer_fn(x), 5.0 * (5.0 + 3.0)) + self.assertAllEqual(middle_fn(v, x), 3.0 * (3.0 + 5.0)) + + with backprop.GradientTape() as tp: + tp.watch(x) + result = outer_fn(x) + grad = tp.gradient(result, x) + + self.assertAllEqual(grad, 2 * 5.0 + 3.0) + + y = constant_op.constant(4.0) + with backprop.GradientTape() as tp: + tp.watch(y) + result = outer_fn(y) + grad = tp.gradient(result, y) + + self.assertAllEqual(grad, 2 * 4.0 + 3.0) + + v.assign(constant_op.constant(1.5)) + with backprop.GradientTape() as tp: + tp.watch(y) + result = outer_fn(y) + grad = tp.gradient(result, y) + + self.assertAllEqual(grad, 2 * 4.0 + 1.5) + + with backprop.GradientTape() as tp: + tp.watch(y) + result = inner_fn(y, v) + grad = tp.gradient(result, y) + + self.assertAllEqual(grad, 1.0) + + def testDeeplyNestedDifferentiableFunctionWithVariableMultipleTFGrads(self): + with context.graph_mode(), self.cached_session(): + v = resource_variable_ops.ResourceVariable(3.0) + v.initializer.run() + + @def_function.function + def inner_fn(a, b): + return math_ops.add(a, b) + + @def_function.function + def middle_fn(a, b): + return math_ops.mul(a, inner_fn(a, b)) + + @def_function.function + def outer_fn(x): + return middle_fn(x, v) + + x = constant_op.constant(5.0) + self.assertAllEqual(outer_fn(x).eval(), 5.0 * (5.0 + 3.0)) + + grad, = gradients_impl.gradients(outer_fn(x), x) + + self.assertAllEqual(grad, 2 * 5.0 + 3.0) + self.assertAllEqual(outer_fn(x), 5.0 * (5.0 + 3.0)) + self.assertAllEqual(middle_fn(v, x), 3.0 * (3.0 + 5.0)) + + grad, = gradients_impl.gradients(outer_fn(x), x) + + self.assertAllEqual(grad, 2 * 5.0 + 3.0) + + y = constant_op.constant(4.0) + grad, = gradients_impl.gradients(outer_fn(y), y) + self.assertAllEqual(grad, 2 * 4.0 + 3.0) + + self.evaluate(v.assign(constant_op.constant(1.5))) + grad, = gradients_impl.gradients(outer_fn(y), y) + + self.assertAllEqual(grad, 2 * 4.0 + 1.5) + + grad, = gradients_impl.gradients(inner_fn(y, v), y) + self.assertAllEqual(grad, 1.0) + + def testNestedDifferentiableFunctionNoneOutputs(self): + @def_function.function + def foo(a, b): + return None, a * math_ops.add(a, b), None, 2*a + + @def_function.function + def bar(x): + return foo(x, 1.0) + + x = constant_op.constant(5.0) + with backprop.GradientTape(persistent=True) as tp: + tp.watch(x) + none1, r1, none2, r2 = bar(x) + g1 = tp.gradient(r1, x) + g2 = tp.gradient(r2, x) + + self.assertAllEqual(r1, 30.0) + self.assertAllEqual(r2, 10.0) + self.assertIs(none1, None) + self.assertIs(none2, None) + self.assertAllEqual(g1, 2 * 5.0 + 1.0) + self.assertAllEqual(g2, 2.0) + + def testGradientWithKeywordArguments(self): + matmul = def_function.function(math_ops.matmul) + + def sq(x): + return matmul(a=x, b=x, transpose_a=True) + + t = constant_op.constant([[1.0, 2.0], [3.0, 4.0]]) + grad_t, = backprop.gradients_function(sq, [0])(t) + self.assertAllEqual(grad_t, [[6, 6], [14, 14]]) + + with backprop.GradientTape(persistent=True) as tape: + tape.watch(t) + one = matmul(t, b=t, transpose_a=True) + two = matmul(b=t, a=t, transpose_a=True) + three = matmul(a=t, b=t, transpose_a=True) + + for output in [one, two, three]: + self.assertAllEqual(tape.gradient(output, t), [[6, 6], [14, 14]]) + + def testGradientInFunctionWithKeywordArguments(self): + + @def_function.function + def f(x): + return backprop.gradients_function(lambda y: y * y, [0])(x)[0] + + self.assertAllEqual(f(x=constant_op.constant(1.0)), 2.0) + + @test_util.run_in_graph_and_eager_modes + def testBackwardNone(self): + model = variables.Variable(1.0, name='model') + count = variables.Variable(0) + + @function.defun + def forward_pass(value): + count.assign_add(1) + residuals = value - model + loss = 0.5 * math_ops.reduce_mean(math_ops.pow(residuals, 2)) + # Note: count is an integer, so its doutput will be None + return loss, count + + def reduce_fn(x): + if context.executing_eagerly(): + with backprop.GradientTape() as t: + loss, count = forward_pass(x) + return t.gradient(loss, model), count + loss, count = forward_pass(x) + grad_only = gradients_impl.gradients(loss, model) + return grad_only, count + + g, _ = reduce_fn(constant_op.constant([7.0])) + + self.evaluate(variables.global_variables_initializer()) + self.assertAllEqual(nest.flatten(self.evaluate(g)), [-6.0]) + + +if __name__ == '__main__': + ops.enable_eager_execution( + config=config_pb2.ConfigProto(device_count={'CPU': 4})) + test.main() diff --git a/tensorflow/python/eager/function_test.py b/tensorflow/python/eager/function_test.py index 2af08689f8..71afbd24d8 100644 --- a/tensorflow/python/eager/function_test.py +++ b/tensorflow/python/eager/function_test.py @@ -29,7 +29,6 @@ import numpy from tensorflow.core.protobuf import config_pb2 from tensorflow.core.protobuf import rewriter_config_pb2 from tensorflow.python import keras -from tensorflow.python.eager import backprop from tensorflow.python.eager import context from tensorflow.python.eager import def_function from tensorflow.python.eager import function @@ -48,7 +47,6 @@ from tensorflow.python.layers import convolutional from tensorflow.python.ops import array_ops from tensorflow.python.ops import clip_ops from tensorflow.python.ops import control_flow_ops -from tensorflow.python.ops import gradients_impl from tensorflow.python.ops import init_ops from tensorflow.python.ops import list_ops from tensorflow.python.ops import math_ops @@ -102,10 +100,10 @@ class FunctionTest(test.TestCase, parameterized.TestCase): _ = x * y return x + y - # The default config allows everything. - rewrites = rewriter_config_pb2.RewriterConfig() + # The default config allows all rewrites. + config_proto = config_pb2.ConfigProto() - with context.function_rewriter_config(rewrites): + with context.function_config_proto(config_proto): t = constant_op.constant(1.0) self.assertAllEqual(add(t, t).numpy(), 2.0) @@ -149,31 +147,22 @@ class FunctionTest(test.TestCase, parameterized.TestCase): out = a_times_b(pair({'a': t}, {'b': t})) self.assertAllEqual(out, math_ops.matmul(t, t).numpy()) - def testGraphModeWithGradients(self): - v = resource_variable_ops.ResourceVariable(1.0, name='v') - - @def_function.function - def step(): - def inner(): - return v * v - - return backprop.implicit_grad(inner)()[0][0] + def testNestedOutputsGraphMode(self): + matmul = def_function.function(math_ops.matmul) - self.assertAllEqual(step(), 2.0) + pair = collections.namedtuple('pair', ['a', 'b']) - def testGraphGradientVariable(self): - with ops.Graph().as_default(), self.cached_session(): - v = variables.Variable(1.0) + @def_function.function() + def pairs_mul(pair_a, pair_b): + return pair(matmul(pair_a.a, pair_b.a), matmul(pair_a.b, pair_b.b)) - @def_function.function - def f(): - return 2.0 * v + a = constant_op.constant([[1.0, 2.0], [1.0, 2.0]]) + b = constant_op.constant([[3.0, 4.0], [3.0, 4.0]]) - node = f() - grads, = gradients_impl.gradients(node, v) - v.initializer.run() - self.assertAllEqual(grads.eval(), 2.0) - self.assertEqual(grads.shape, v.shape) + out = pairs_mul(pair(a, b), pair(b, a)) + expected = pair(math_ops.matmul(a, b).numpy(), + math_ops.matmul(b, a).numpy()) + self.assertAllClose(out, expected) def testGraphEagerIsolation(self): @@ -314,34 +303,6 @@ class FunctionTest(test.TestCase, parameterized.TestCase): random_seed.set_random_seed(1) self.assertAllEqual(f(), x) - def testSymGradGatherNd(self): - with ops.Graph().as_default(), self.cached_session() as sess: - - @def_function.function - def f(x): - return array_ops.gather_nd(x, [[0]]) - - c = constant_op.constant([[2.]]) - f_c = f(c) - g, = gradients_impl.gradients(f_c, c) - self.assertAllEqual(sess.run(g).values, [[1.0]]) - - def testNoSymGradNestedDefun(self): - - @def_function.function - def outer(): - - @def_function.function - def f(x): - return array_ops.gather_nd(x, [[0]]) - - c = constant_op.constant([[2.]]) - f_c = f(c) - g, = gradients_impl.gradients(f_c, c) - self.assertIsInstance(g, ops.IndexedSlices) - - outer() - def testNestedInputsGraphFunction(self): matmul = def_function.function(math_ops.matmul) @@ -378,21 +339,6 @@ class FunctionTest(test.TestCase, parameterized.TestCase): self.assertAllEqual(a, math_ops.matmul(t, t).numpy()) self.assertAllEqual(b['b'].numpy(), 1.0) - def testGraphFunctionWithGradients(self): - v = resource_variable_ops.ResourceVariable(1.0, name='v') - - @def_function.function - def step(): - def inner(): - return v * v - - return backprop.implicit_grad(inner)()[0][0] - - step_op = step.get_concrete_function() - self.assertEqual(step_op.output_dtypes, dtypes.float32) - self.assertEqual(step_op.output_shapes, tensor_shape.TensorShape([])) - self.assertAllEqual(step_op(), 2.0) - def testGraphFunctionNoneOutput(self): @def_function.function def fn(unused_a, unused_b): @@ -404,34 +350,6 @@ class FunctionTest(test.TestCase, parameterized.TestCase): self.assertEqual(fn_op.output_shapes, None) self.assertAllEqual(fn_op(x, x), None) - @test_util.run_in_graph_and_eager_modes() - def testDefunCondGradient(self): - - @def_function.function - def f(x): - return control_flow_ops.cond(x > 0.5, lambda: 2 * x, lambda: 3 * x) - - with backprop.GradientTape() as t: - x = constant_op.constant(1.0) - t.watch(x) - y = f(x) - self.assertAllEqual(self.evaluate(t.gradient(y, x)), 2.0) - - @test_util.run_in_graph_and_eager_modes() - def testGraphLoopGradient(self): - - @def_function.function - def f(x): - return control_flow_ops.while_loop(lambda _, i: i < 2, - lambda x, i: (2*x, i + 1), - [x, 0])[0] - - with backprop.GradientTape() as t: - x = constant_op.constant(1.0) - t.watch(x) - y = f(x) - self.assertAllEqual(self.evaluate(t.gradient(y, x)), 4.0) - def testDefunNumpyArraysConvertedToTensors(self): def f(x): @@ -625,27 +543,7 @@ class FunctionTest(test.TestCase, parameterized.TestCase): self.assertIsInstance( self.v, resource_variable_ops.ResourceVariable) - def testDefunDifferentiable(self): - v = resource_variable_ops.ResourceVariable(1.0) - - @def_function.function - def f(): - return v * v - - self.assertAllEqual(backprop.implicit_grad(f)()[0][0], 2.0) - - def testDefunCanBeDifferentiatedTwice(self): - v = resource_variable_ops.ResourceVariable(1.0) - - @def_function.function - def f(): - return v * v - - self.assertAllEqual(backprop.implicit_grad(f)()[0][0], 2.0) - # Ensure that v is watched again. - self.assertAllEqual(backprop.implicit_grad(f)()[0][0], 2.0) - - def testRunMetadata(self): + def disabled_testRunMetadata(self): @def_function.function def f(x): @@ -683,23 +581,7 @@ class FunctionTest(test.TestCase, parameterized.TestCase): variables.global_variables_initializer().run() call = def_function.function(o.call) op = call() - self.assertAllEqual(sess.run(op), 2.0) - - def testSymbolicGradientVariableNoneNotZerosLike(self): - with ops.Graph().as_default(): - v = variables.Variable(1.0) - - @def_function.function - def f(x, v): - v.read_value() - return x * x - - x = constant_op.constant(1.0) - l = f(x, v) - _, dv = gradients_impl.gradients(l, [x, v]) - with self.cached_session(): - v.initializer.run() - self.assertEqual(dv, None) + self.assertAllEqual(self.evaluate(op), 2.0) def testGraphModeManyFunctions(self): with ops.Graph().as_default(), self.cached_session(): @@ -742,42 +624,6 @@ class FunctionTest(test.TestCase, parameterized.TestCase): self.assertAllEqual(8, g(constant_op.constant(2))) - def testDefunCallBackprop(self): - - @def_function.function - def f(x): - return math_ops.add(x, x) - - @def_function.function - def g(x): - return backprop.gradients_function(f, [0])(x)[0] - - self.assertAllEqual(2, g(constant_op.constant(2.))) - - def testGraphModeEagerGradError(self): - with context.graph_mode(): - def f(): - x = variable_scope.get_variable( - 'v', initializer=constant_op.constant(1.0)) - return x * constant_op.constant(2.0) - - with self.assertRaisesRegexp(ValueError, - 'No trainable variables were accessed'): - backprop.implicit_val_and_grad(f)() - - def testDefunCallBackpropUsingSameObjectForMultipleArguments(self): - - @def_function.function - def g(x): - return backprop.gradients_function(math_ops.multiply, [0, 1])(x, x) - - def np_g(x): - return [d.numpy() for d in g(x)] - - x = constant_op.constant(1.) - self.assertAllEqual([1., 1.], np_g(x)) - self.assertAllEqual([1., 1.], np_g(1.)) - def testCallShape(self): @def_function.function @@ -808,37 +654,6 @@ class FunctionTest(test.TestCase, parameterized.TestCase): g(three) - def testGradientTensorConversionWithDefun(self): - three = resource_variable_ops.ResourceVariable(3.0, name='v') - - @def_function.function - def f(x): - return math_ops.add(x, three) - - def g(x): - return f(x) - - g = backprop.implicit_grad(g)(constant_op.constant(1.0))[0][0] - self.assertAllEqual(g, 1.0) - - def testGradient(self): - matmul = def_function.function(math_ops.matmul) - - def sq(x): - return matmul(x, x, transpose_a=True) - - t = constant_op.constant([[1.0, 2.0], [3.0, 4.0]]) - grad_t, = backprop.gradients_function(sq, [0])(t) - self.assertAllEqual(grad_t, [[6, 6], [14, 14]]) - - def testGradientInFunction(self): - - @def_function.function - def f(x): - return backprop.gradients_function(lambda y: y * y, [0])(x)[0] - - self.assertAllEqual(f(constant_op.constant(1.0)), 2.0) - def testGatherResourceWithDefun(self): with ops.device('cpu:0'): v = resource_variable_ops.ResourceVariable([0.0, 1.0, 2.0]) @@ -849,24 +664,6 @@ class FunctionTest(test.TestCase, parameterized.TestCase): defined = def_function.function(sum_gather) self.assertAllEqual(sum_gather(), defined()) - def testGradientOfGatherWithDefun(self): - v = resource_variable_ops.ResourceVariable([0.0, 1.0, 2.0]) - - def sum_gather(): - return math_ops.reduce_sum(array_ops.gather(v, [1, 2])) - - grad_fn = backprop.implicit_grad(sum_gather) - gradient = grad_fn() - defun_grad_fn = backprop.implicit_grad(def_function.function(sum_gather)) - defun_gradient = defun_grad_fn() - self.assertEqual(len(gradient), len(defun_gradient)) - - gradient = gradient[0][0] - defun_gradient = defun_gradient[0][0] - self.assertAllEqual(gradient.values, defun_gradient.values) - self.assertAllEqual(gradient.indices, defun_gradient.indices) - self.assertAllEqual(gradient.dense_shape, defun_gradient.dense_shape) - def testReturningIndexedSlicesWithDefun(self): def validate(indexed_slice): @@ -1012,440 +809,6 @@ class FunctionTest(test.TestCase, parameterized.TestCase): shape = constant_op.constant([2, 1]).gpu() reshape(value, shape) # No error is raised - def testDifferentiableFunctionNoneOutputs(self): - - @def_function.function - def my_function(x): - return x, None - - def wrapper(x): - return my_function(x)[0] - - g = backprop.gradients_function(wrapper, [0])(constant_op.constant(0.0)) - self.assertAllEqual(g[0], 1.) - - @def_function.function - def foo(a): - return None, a * a - - x = constant_op.constant(5.0) - with backprop.GradientTape() as tp: - tp.watch(x) - none, r = foo(x) - g = tp.gradient(r, x) - - self.assertIs(none, None) - self.assertAllEqual(r, 25.0) - self.assertAllEqual(g, 2 * 5.0) - - @test_util.run_in_graph_and_eager_modes - def testNestedDifferentiableFunction(self): - @def_function.function - def inner_fn(a, b): - return a * math_ops.add(a, b) - - @def_function.function - def outer_fn(x): - return inner_fn(x, 1.0) - - x = constant_op.constant(5.0) - with backprop.GradientTape() as tp: - tp.watch(x) - result = outer_fn(x) - grad = tp.gradient(result, x) - - self.assertAllEqual(grad, 2 * 5.0 + 1.0) - - @test_util.run_in_graph_and_eager_modes - def testDeeplyNestedDifferentiableFunction(self): - @def_function.function - def inner_inner_fn(a, b): - return math_ops.add(a, b) - - @def_function.function - def inner_fn(a, b): - return inner_inner_fn(a, b) - - @def_function.function - def middle_fn(a, b): - return a * inner_fn(a, b) - - @def_function.function - def outer_fn(x): - return middle_fn(x, 1.0) - - x = constant_op.constant(5.0) - with backprop.GradientTape() as tp: - tp.watch(x) - result = outer_fn(x) - grad = tp.gradient(result, x) - - self.assertAllEqual(grad, 2 * 5.0 + 1.0) - - @test_util.run_in_graph_and_eager_modes - def testDeeplyNestedDifferentiableFunctionWithMultipleGradCalls(self): - @def_function.function - def inner_fn(a, b): - return math_ops.add(a, b) - - @def_function.function - def middle_fn(a, b): - return math_ops.mul(a, inner_fn(a, b)) - - @def_function.function - def outer_fn(x): - return middle_fn(x, 3.0) - - x = constant_op.constant(5.0) - self.assertAllEqual(outer_fn(x), 5.0 * (5.0 + 3.0)) - - with backprop.GradientTape() as tp: - tp.watch(x) - result = outer_fn(x) - grad = tp.gradient(result, x) - - self.assertAllEqual(grad, 2 * 5.0 + 3.0) - self.assertAllEqual(outer_fn(x), 5.0 * (5.0 + 3.0)) - self.assertAllEqual(middle_fn(3.0, x), 3.0 * (3.0 + 5.0)) - - with backprop.GradientTape() as tp: - tp.watch(x) - result = outer_fn(x) - grad = tp.gradient(result, x) - - self.assertAllEqual(grad, 2 * 5.0 + 3.0) - - y = constant_op.constant(4.0) - with backprop.GradientTape() as tp: - tp.watch(y) - result = outer_fn(y) - grad = tp.gradient(result, y) - - self.assertAllEqual(grad, 2 * 4.0 + 3.0) - - with backprop.GradientTape() as tp: - tp.watch(y) - result = inner_fn(y, y) - grad = tp.gradient(result, y) - - self.assertAllEqual(grad, 2.0) - - @test_util.run_in_graph_and_eager_modes - def testDeeplyNestedDifferentiableFunctionGradientTapeInDefun(self): - @def_function.function - def inner_inner_fn(a, b): - return math_ops.add(a, b) - - @def_function.function - def inner_fn(a, b): - return inner_inner_fn(a, b) - - @def_function.function - def middle_fn(a, b): - return a * inner_fn(a, b) - - @def_function.function - def outer_fn(x): - with backprop.GradientTape() as tp: - tp.watch(x) - result = middle_fn(x, 1.0) - grad = tp.gradient(result, x) - return grad - - x = constant_op.constant(5.0) - grad = outer_fn(x) - self.assertAllEqual(grad, 2 * 5.0 + 1.0) - - @test_util.run_in_graph_and_eager_modes - def testDeeplyNestedDifferentiableFunctionGradientTapeInNestedDefun(self): - @def_function.function - def inner_inner_fn(a, b): - return math_ops.add(a, b) - - @def_function.function - def inner_fn(a, b): - return inner_inner_fn(a, b) - - @def_function.function - def middle_fn(a, b): - return a * inner_fn(a, b) - - @def_function.function - def almost_outer_fn(x): - with backprop.GradientTape() as tp: - tp.watch(x) - result = middle_fn(x, 1.0) - grad = tp.gradient(result, x) - return grad - - @def_function.function - def outer_fn(x): - return almost_outer_fn(x) - - x = constant_op.constant(5.0) - grad = outer_fn(x) - self.assertAllEqual(grad, 2 * 5.0 + 1.0) - - @test_util.run_in_graph_and_eager_modes - def testDeeplyNestedDifferentiableFunctionGradientTapeInMultNestedDefun(self): - @def_function.function - def inner_inner_fn(a, b): - return math_ops.add(a, b) - - @def_function.function - def inner_fn(a, b): - return inner_inner_fn(a, b) - - @def_function.function - def middle_fn(a, b): - return a * inner_fn(a, b) - - @def_function.function - def almost_outer_fn(x): - with backprop.GradientTape() as tp: - tp.watch(x) - result = middle_fn(x, 1.0) - grad = tp.gradient(result, x) - return grad - - @def_function.function - def outer_fn(x): - return almost_outer_fn(x) - - @def_function.function - def outer_outer_fn(x): - return outer_fn(x) - - x = constant_op.constant(5.0) - grad = outer_outer_fn(x) - self.assertAllEqual(grad, 2 * 5.0 + 1.0) - - @test_util.run_in_graph_and_eager_modes - def testDeeplyNestedDifferentiableFunctionTFGradientInDefun(self): - @def_function.function - def inner_inner_fn(a, b): - return math_ops.add(a, b) - - @def_function.function - def inner_fn(a, b): - return inner_inner_fn(a, b) - - @def_function.function - def middle_fn(a, b): - return a * inner_fn(a, b) - - @def_function.function - def outer_fn(x): - result = middle_fn(x, 1.0) - return gradients_impl.gradients(result, [x])[0] - - x = constant_op.constant(5.0) - grad = outer_fn(x) - self.assertAllEqual(grad, 2 * 5.0 + 1.0) - - @test_util.run_in_graph_and_eager_modes - def testDeeplyNestedDifferentiableFunctionTFGradientInNestedDefun(self): - @def_function.function - def inner_inner_fn(a, b): - return math_ops.add(a, b) - - @def_function.function - def inner_fn(a, b): - return inner_inner_fn(a, b) - - @def_function.function - def middle_fn(a, b): - return a * inner_fn(a, b) - - @def_function.function - def almost_outer_fn(x): - result = middle_fn(x, 1.0) - return gradients_impl.gradients(result, [x])[0] - - @def_function.function - def outer_fn(x): - return almost_outer_fn(x) - - x = constant_op.constant(5.0) - grad = outer_fn(x) - self.assertAllEqual(grad, 2 * 5.0 + 1.0) - - @test_util.run_in_graph_and_eager_modes - def testDeeplyNestedDifferentiableFunctionTFGradientInMultNestedDefun(self): - @def_function.function - def inner_inner_fn(a, b): - return math_ops.add(a, b) - - @def_function.function - def inner_fn(a, b): - return inner_inner_fn(a, b) - - @def_function.function - def middle_fn(a, b): - return a * inner_fn(a, b) - - @def_function.function - def almost_outer_fn(x): - result = middle_fn(x, 1.0) - return gradients_impl.gradients(result, [x])[0] - - @def_function.function - def outer_fn(x): - return almost_outer_fn(x) - - @def_function.function - def outer_outer_fn(x): - return outer_fn(x) - - x = constant_op.constant(5.0) - grad = outer_outer_fn(x) - self.assertAllEqual(grad, 2 * 5.0 + 1.0) - - def testDeeplyNestedDifferentiableFunctionWithVariable(self): - var = variables.Variable(constant_op.constant(1.0)) - - @def_function.function - def inner_fn(a, b): - return math_ops.add(a, b) - - @def_function.function - def middle_fn(a, b): - return a * inner_fn(a, b) - - @def_function.function - def outer_fn(x): - return middle_fn(x, var) - - x = constant_op.constant(5.0) - with backprop.GradientTape() as tp: - tp.watch(x) - result = outer_fn(x) - grad = tp.gradient(result, x) - - self.assertAllEqual(grad, 2 * 5.0 + 1.0) - - def testDeeplyNestedDifferentiableFunctionWithVariableMultipleGradCalls(self): - v = variables.Variable(constant_op.constant(3.0)) - - @def_function.function - def inner_fn(a, b): - return math_ops.add(a, b) - - @def_function.function - def middle_fn(a, b): - return math_ops.mul(a, inner_fn(a, b)) - - @def_function.function - def outer_fn(x): - return middle_fn(x, v) - - x = constant_op.constant(5.0) - self.assertAllEqual(outer_fn(x), 5.0 * (5.0 + 3.0)) - - with backprop.GradientTape() as tp: - tp.watch(x) - result = outer_fn(x) - grad = tp.gradient(result, x) - - self.assertAllEqual(grad, 2 * 5.0 + 3.0) - self.assertAllEqual(outer_fn(x), 5.0 * (5.0 + 3.0)) - self.assertAllEqual(middle_fn(v, x), 3.0 * (3.0 + 5.0)) - - with backprop.GradientTape() as tp: - tp.watch(x) - result = outer_fn(x) - grad = tp.gradient(result, x) - - self.assertAllEqual(grad, 2 * 5.0 + 3.0) - - y = constant_op.constant(4.0) - with backprop.GradientTape() as tp: - tp.watch(y) - result = outer_fn(y) - grad = tp.gradient(result, y) - - self.assertAllEqual(grad, 2 * 4.0 + 3.0) - - v.assign(constant_op.constant(1.5)) - with backprop.GradientTape() as tp: - tp.watch(y) - result = outer_fn(y) - grad = tp.gradient(result, y) - - self.assertAllEqual(grad, 2 * 4.0 + 1.5) - - with backprop.GradientTape() as tp: - tp.watch(y) - result = inner_fn(y, v) - grad = tp.gradient(result, y) - - self.assertAllEqual(grad, 1.0) - - def testDeeplyNestedDifferentiableFunctionWithVariableMultipleTFGrads(self): - with context.graph_mode(), self.cached_session(): - v = resource_variable_ops.ResourceVariable(3.0) - v.initializer.run() - - @def_function.function - def inner_fn(a, b): - return math_ops.add(a, b) - - @def_function.function - def middle_fn(a, b): - return math_ops.mul(a, inner_fn(a, b)) - - @def_function.function - def outer_fn(x): - return middle_fn(x, v) - - x = constant_op.constant(5.0) - self.assertAllEqual(outer_fn(x).eval(), 5.0 * (5.0 + 3.0)) - - grad, = gradients_impl.gradients(outer_fn(x), x) - - self.assertAllEqual(grad, 2 * 5.0 + 3.0) - self.assertAllEqual(outer_fn(x), 5.0 * (5.0 + 3.0)) - self.assertAllEqual(middle_fn(v, x), 3.0 * (3.0 + 5.0)) - - grad, = gradients_impl.gradients(outer_fn(x), x) - - self.assertAllEqual(grad, 2 * 5.0 + 3.0) - - y = constant_op.constant(4.0) - grad, = gradients_impl.gradients(outer_fn(y), y) - self.assertAllEqual(grad, 2 * 4.0 + 3.0) - - self.evaluate(v.assign(constant_op.constant(1.5))) - grad, = gradients_impl.gradients(outer_fn(y), y) - - self.assertAllEqual(grad, 2 * 4.0 + 1.5) - - grad, = gradients_impl.gradients(inner_fn(y, v), y) - self.assertAllEqual(grad, 1.0) - - def testNestedDifferentiableFunctionNoneOutputs(self): - @def_function.function - def foo(a, b): - return None, a * math_ops.add(a, b), None, 2*a - - @def_function.function - def bar(x): - return foo(x, 1.0) - - x = constant_op.constant(5.0) - with backprop.GradientTape(persistent=True) as tp: - tp.watch(x) - none1, r1, none2, r2 = bar(x) - g1 = tp.gradient(r1, x) - g2 = tp.gradient(r2, x) - - self.assertAllEqual(r1, 30.0) - self.assertAllEqual(r2, 10.0) - self.assertIs(none1, None) - self.assertIs(none2, None) - self.assertAllEqual(g1, 2 * 5.0 + 1.0) - self.assertAllEqual(g2, 2.0) - def testNoneOutput(self): @def_function.function @@ -1925,8 +1288,9 @@ class FunctionTest(test.TestCase, parameterized.TestCase): defined(array_ops.ones([2, 1])) # Wrong number of arguments. - with self.assertRaisesRegexp(ValueError, - 'Structure of Python function inputs.*'): + with self.assertRaisesRegexp( + ValueError, + 'Arguments and signature arguments do not match.*'): defined(array_ops.ones([2]), array_ops.ones([2])) with self.assertRaisesRegexp(ValueError, 'Structure of Python function inputs.*'): @@ -1945,10 +1309,16 @@ class FunctionTest(test.TestCase, parameterized.TestCase): else: return -1.0 * a - signature = [tensor_spec.TensorSpec([], dtypes.float32)] * 2 + signature = [ + tensor_spec.TensorSpec([], dtypes.float32), + tensor_spec.TensorSpec([], dtypes.bool), + ] defined = def_function.function(foo, input_signature=signature) a = constant_op.constant(1.0) - with self.assertRaises(TypeError): + with self.assertRaisesRegexp( + ValueError, + 'When input_signature is provided, all inputs to ' + 'the Python function must be Tensors.'): defined(a, training=True) def testInputSignatureWithKeywordPositionalArgs(self): @@ -2039,33 +1409,6 @@ class FunctionTest(test.TestCase, parameterized.TestCase): self.assertAllEqual(six, 2.0) self.assertAllEqual(seven, 2.0) - def testGradientWithKeywordArguments(self): - matmul = def_function.function(math_ops.matmul) - - def sq(x): - return matmul(a=x, b=x, transpose_a=True) - - t = constant_op.constant([[1.0, 2.0], [3.0, 4.0]]) - grad_t, = backprop.gradients_function(sq, [0])(t) - self.assertAllEqual(grad_t, [[6, 6], [14, 14]]) - - with backprop.GradientTape(persistent=True) as tape: - tape.watch(t) - one = matmul(t, b=t, transpose_a=True) - two = matmul(b=t, a=t, transpose_a=True) - three = matmul(a=t, b=t, transpose_a=True) - - for output in [one, two, three]: - self.assertAllEqual(tape.gradient(output, t), [[6, 6], [14, 14]]) - - def testGradientInFunctionWithKeywordArguments(self): - - @def_function.function - def f(x): - return backprop.gradients_function(lambda y: y * y, [0])(x)[0] - - self.assertAllEqual(f(x=constant_op.constant(1.0)), 2.0) - def testDefuningInstanceMethod(self): integer = constant_op.constant(2, dtypes.int64) @@ -2339,33 +1682,6 @@ class FunctionTest(test.TestCase, parameterized.TestCase): # pylint: disable=protected-access self.assertEqual(len(graph._functions), 3) - @test_util.run_in_graph_and_eager_modes - def testBackwardNone(self): - model = variables.Variable(1.0, name='model') - count = variables.Variable(0) - - @function.defun - def forward_pass(value): - count.assign_add(1) - residuals = value - model - loss = 0.5 * math_ops.reduce_mean(math_ops.pow(residuals, 2)) - # Note: count is an integer, so its doutput will be None - return loss, count - - def reduce_fn(x): - if context.executing_eagerly(): - with backprop.GradientTape() as t: - loss, count = forward_pass(x) - return t.gradient(loss, model), count - loss, count = forward_pass(x) - grad_only = gradients_impl.gradients(loss, model) - return grad_only, count - - g, _ = reduce_fn(constant_op.constant([7.0])) - - self.evaluate(variables.global_variables_initializer()) - self.assertAllEqual(nest.flatten(self.evaluate(g)), [-6.0]) - def testCallingFunctionWithDifferentVariables(self): @function.defun @@ -2403,8 +1719,7 @@ class FunctionTest(test.TestCase, parameterized.TestCase): 'be Tensors;.*'): graph_function('Not a Tensor.') - # TODO(scottzhu): Revive the test once the grappler plugin is updated. - def disabled_testSwapImplementationWithGrapplerPlugin(self): + def testSwapImplementationWithGrapplerPlugin(self): rewrites = rewriter_config_pb2.RewriterConfig() # function_optimizer has to be turn off, otherwise it will delete the # registered function if it does not get called. @@ -2441,7 +1756,7 @@ class FunctionTest(test.TestCase, parameterized.TestCase): function.register(cpu_boost, x) y = gpu_boost(x) - y_value = sess.run(y) + y_value = self.evaluate(y) if test.is_gpu_available(): self.assertEqual(y_value, 5.0) diff --git a/tensorflow/python/eager/graph_only_ops_test.py b/tensorflow/python/eager/graph_only_ops_test.py index 3cf3a61a62..914b4d9a95 100644 --- a/tensorflow/python/eager/graph_only_ops_test.py +++ b/tensorflow/python/eager/graph_only_ops_test.py @@ -29,12 +29,14 @@ from tensorflow.python.platform import test class GraphOnlyOpsTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testGraphZerosLike(self): x = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.int32) z_tf = graph_only_ops.graph_zeros_like(x) with self.cached_session(): - self.assertAllClose(np.zeros((2, 3)), z_tf.eval()) + self.assertAllClose(np.zeros((2, 3)), self.evaluate(z_tf)) + @test_util.run_deprecated_v1 def testGraphPlaceholder(self): x_tf = graph_only_ops.graph_placeholder(dtypes.int32, shape=(1,)) y_tf = math_ops.square(x_tf) diff --git a/tensorflow/python/eager/pywrap_tensor.cc b/tensorflow/python/eager/pywrap_tensor.cc index 55f0896e3b..206b96eef6 100644 --- a/tensorflow/python/eager/pywrap_tensor.cc +++ b/tensorflow/python/eager/pywrap_tensor.cc @@ -220,6 +220,14 @@ TFE_TensorHandle* ConvertToEagerTensor(PyObject* value, PyObject* dtype) { return nullptr; } } + tensorflow::Safe_PyObjectPtr value_decrefer; + if (PyArray_CheckAnyScalarExact(value)) { + // Convert numpy scalars to numpy arrays. + value = PyArray_FromScalar(value, nullptr); + // The returned value needs to be DECREF'd, but the original value was + // created in python code, and doesn't need to be DECREF'd. + value_decrefer.reset(value); + } if (PyArray_Check(value)) { int desired_np_dtype = -1; if (desired_dtype >= 0) { @@ -439,8 +447,8 @@ int EagerTensor_init(EagerTensor* self, PyObject* args, PyObject* kwds) { PyErr_SetString( PyExc_TypeError, tensorflow::strings::StrCat( - "Cannot convert value ", TFE_GetPythonString(value_str.get()), - " to EagerTensor with requested dtype: ", + "Cannot convert provided value to EagerTensor. Provided value: ", + TFE_GetPythonString(value_str.get()), " Requested dtype: ", tensorflow::DataTypeString( static_cast(desired_dtype))) .c_str()); @@ -672,11 +680,29 @@ static PyObject* EagerTensor_device(EagerTensor* self) { #endif } +// Getter `backing_device`. +static PyObject* EagerTensor_backing_device(EagerTensor* self) { + const char* device = + TFE_TensorHandleBackingDeviceName(self->handle, self->status); + if (MaybeRaiseExceptionFromTFStatus(self->status, PyExc_ValueError)) { + // Cleanup self->status before returning. + TF_SetStatus(self->status, TF_OK, ""); + return nullptr; + } +#if PY_MAJOR_VERSION >= 3 + return PyUnicode_FromString(device); +#else + return PyBytes_FromString(device); +#endif +} + static PyGetSetDef EagerTensor_getseters[] = { {const_cast("_id"), (getter)EagerTensor_getid, nullptr, const_cast("_id"), nullptr}, {const_cast("device"), (getter)EagerTensor_device, nullptr, const_cast("device"), nullptr}, + {const_cast("backing_device"), (getter)EagerTensor_backing_device, + nullptr, const_cast("backing_device"), nullptr}, {const_cast("_handle_data"), (getter)EagerTensor_tensor_handle, (setter)EagerTensor_settensor_handle, const_cast("_tensor_handle"), nullptr}, diff --git a/tensorflow/python/eager/pywrap_tfe_src.cc b/tensorflow/python/eager/pywrap_tfe_src.cc index 6ca8eadbde..9ce500bc08 100644 --- a/tensorflow/python/eager/pywrap_tfe_src.cc +++ b/tensorflow/python/eager/pywrap_tfe_src.cc @@ -1645,6 +1645,29 @@ PyObject* TFE_Py_TapeGradient(PyObject* tape, PyObject* target, if (PyErr_Occurred()) { return nullptr; } + tensorflow::gtl::FlatSet sources_set(sources_vec.begin(), + sources_vec.end()); + + tensorflow::Safe_PyObjectPtr seq = + tensorflow::make_safe(PySequence_Fast(target, "expected a sequence")); + int len = PySequence_Fast_GET_SIZE(seq.get()); + tensorflow::gtl::FlatMap + source_tensors_that_are_targets; + for (int i = 0; i < len; ++i) { + tensorflow::int64 target_id = target_vec[i]; + if (sources_set.find(target_id) != sources_set.end()) { + auto tensor = PySequence_Fast_GET_ITEM(seq.get(), i); + source_tensors_that_are_targets.insert( + std::make_pair(target_id, TapeTensorFromTensor(tensor))); + } + if (PyErr_Occurred()) { + return nullptr; + } + } + if (PyErr_Occurred()) { + return nullptr; + } + std::vector outgrad_vec; if (output_gradients != Py_None) { outgrad_vec = MakeTensorList(output_gradients); @@ -1659,7 +1682,8 @@ PyObject* TFE_Py_TapeGradient(PyObject* tape, PyObject* target, } std::vector result; status->status = tape_obj->tape->ComputeGradient( - *py_vspace, target_vec, sources_vec, outgrad_vec, &result); + *py_vspace, target_vec, sources_vec, source_tensors_that_are_targets, + outgrad_vec, &result); if (!status->status.ok()) { if (PyErr_Occurred()) { // Do not propagate the erroneous status as that would swallow the @@ -2279,8 +2303,10 @@ bool ConvertToTensor( PyErr_SetString( PyExc_TypeError, tensorflow::strings::StrCat( - "Cannot convert value ", TFE_GetPythonString(input_str.get()), - " to EagerTensor with requested dtype: ", desired_dtype) + "Cannot convert provided value to EagerTensor. Provided value: ", + TFE_GetPythonString(input_str.get()), " Requested dtype: ", + tensorflow::DataTypeString( + static_cast(desired_dtype))) .c_str()); return false; } diff --git a/tensorflow/python/eager/tape.py b/tensorflow/python/eager/tape.py index 1326f09713..e501b403a3 100644 --- a/tensorflow/python/eager/tape.py +++ b/tensorflow/python/eager/tape.py @@ -63,7 +63,7 @@ def watch_variable(tape, variable): """Marks this variable to be watched by the given tape.""" strategy = distribution_strategy_context.get_distribution_strategy() if distribution_strategy_context.get_replica_context(): - variables = [strategy.value_container(variable)] + variables = [strategy.extended.value_container(variable)] else: variables = strategy.unwrap(variable) for var in variables: @@ -78,7 +78,7 @@ def variable_accessed(variable): """ strategy = distribution_strategy_context.get_distribution_strategy() if distribution_strategy_context.get_replica_context(): - variables = [strategy.value_container(variable)] + variables = [strategy.extended.value_container(variable)] else: variables = strategy.unwrap(variable) for var in variables: diff --git a/tensorflow/python/eager/tape_test.py b/tensorflow/python/eager/tape_test.py index acd0e569f1..48d3b8ac6e 100644 --- a/tensorflow/python/eager/tape_test.py +++ b/tensorflow/python/eager/tape_test.py @@ -80,8 +80,8 @@ class TapeTest(test.TestCase): tf_e = tf_d + tf_f tf_da, tf_db = gradients_impl.gradients(tf_e, [tf_a, tf_b]) - self.assertAllEqual(da, tf_da.eval()) - self.assertAllEqual(db, tf_db.eval()) + self.assertAllEqual(da, self.evaluate(tf_da)) + self.assertAllEqual(db, self.evaluate(tf_db)) def testBasicFunctional(self): @@ -142,8 +142,8 @@ class TapeTest(test.TestCase): tf_rr = 2 * math_ops.reduce_sum(tf_mm) tf_da, tf_db = gradients_impl.gradients(tf_rr, [tf_a, tf_b]) - self.assertAllEqual(da, tf_da.eval()) - self.assertAllEqual(db, tf_db.eval()) + self.assertAllEqual(da, self.evaluate(tf_da)) + self.assertAllEqual(db, self.evaluate(tf_db)) def testGcTwoOutputs(self): diff --git a/tensorflow/python/eager/tensor_test.py b/tensorflow/python/eager/tensor_test.py index f61d847817..0ee2ff68c2 100644 --- a/tensorflow/python/eager/tensor_test.py +++ b/tensorflow/python/eager/tensor_test.py @@ -95,6 +95,18 @@ class TFETensorTest(test_util.TensorFlowTestCase): t = _create_tensor(values) self.assertAllEqual(values, t) + @test_util.assert_no_new_pyobjects_executing_eagerly + def testNumpyDtypeSurvivesThroughTensorConversion(self): + scalar_creators = [np.int32, np.int64, np.float32, np.float64] + conversion_functions = [ops.convert_to_tensor, constant_op.constant] + + for scalar_creator in scalar_creators: + for conversion_function in conversion_functions: + np_val = scalar_creator(3) + tensor_val = conversion_function(np_val) + self.assertEqual(tensor_val.numpy().dtype, np_val.dtype) + self.assertEqual(tensor_val.numpy(), np_val) + def testNumpyValueWithCast(self): values = np.array([3.0], dtype=np.float32) t = _create_tensor(values, dtype=dtypes.float64) @@ -128,6 +140,23 @@ class TFETensorTest(test_util.TensorFlowTestCase): tensor = constant_op.constant(numpy_tensor) self.assertAllEqual(numpy_tensor.ndim, tensor.ndim) + def testLenAgreesWithNumpy(self): + numpy_tensor = np.asarray(1.0) + tensor = constant_op.constant(numpy_tensor) + with self.assertRaises(TypeError): + len(numpy_tensor) + with self.assertRaisesRegexp( + TypeError, r"Scalar tensor has no `len[(][)]`"): + len(tensor) + + numpy_tensor = np.asarray([1.0, 2.0, 3.0]) + tensor = constant_op.constant(numpy_tensor) + self.assertAllEqual(len(numpy_tensor), len(tensor)) + + numpy_tensor = np.asarray([[1.0, 2.0, 3.0], [1.0, 2.0, 3.0]]) + tensor = constant_op.constant(numpy_tensor) + self.assertAllEqual(len(numpy_tensor), len(tensor)) + def testCopy(self): t = constant_op.constant(1.0) tt = copy.copy(t) @@ -158,9 +187,13 @@ class TFETensorTest(test_util.TensorFlowTestCase): self.assertEqual(dtypes.float64, t.dtype) def testBool(self): - t = _create_tensor(False) - if t: - self.assertFalse(True) + self.assertFalse(bool(_create_tensor(False))) + self.assertFalse(bool(_create_tensor([False]))) + self.assertFalse(bool(_create_tensor([[False]]))) + self.assertFalse(bool(_create_tensor([0]))) + self.assertFalse(bool(_create_tensor([0.]))) + self.assertTrue(bool(_create_tensor([1]))) + self.assertTrue(bool(_create_tensor([1.]))) def testIntDowncast(self): t = _create_tensor(3) @@ -306,6 +339,14 @@ class TFETensorTest(test_util.TensorFlowTestCase): def testConvertToTensorAllowsOverflow(self): _ = ops.convert_to_tensor(123456789, dtype=dtypes.uint8) + def testEagerTensorError(self): + with self.assertRaisesRegexp( + TypeError, + "Cannot convert provided value to EagerTensor. " + "Provided value.*Requested dtype.*"): + _ = ops.convert_to_tensor(1., dtype=dtypes.int32) + + class TFETensorUtilTest(test_util.TensorFlowTestCase): diff --git a/tensorflow/python/eager/test.py b/tensorflow/python/eager/test.py index 33ee797678..a45deac962 100644 --- a/tensorflow/python/eager/test.py +++ b/tensorflow/python/eager/test.py @@ -24,6 +24,6 @@ from tensorflow.python.platform.test import * # pylint: disable=wildcard-import # TODO(akshayka): Do away with this file. -def main(argv=None): +def main(argv=None): # pylint: disable=function-redefined _ops.enable_eager_execution() _test.main(argv) diff --git a/tensorflow/python/eager/wrap_function.py b/tensorflow/python/eager/wrap_function.py index 48266437ef..2b39e99a4e 100644 --- a/tensorflow/python/eager/wrap_function.py +++ b/tensorflow/python/eager/wrap_function.py @@ -26,6 +26,7 @@ from tensorflow.python.framework import ops from tensorflow.python.ops import array_ops from tensorflow.python.ops import variable_scope from tensorflow.python.util import nest +from tensorflow.python.util.tf_export import tf_export class VariableHolder(object): @@ -45,6 +46,7 @@ class VariableHolder(object): return self._fn(*args, **kwargs) +# TODO(allenl): make this checkpointable class WrappedFunction(function.Function): """Wraps a tf V1 piece of code in a function.""" @@ -77,6 +79,7 @@ class WrappedFunction(function.Function): return pruned_fn +@tf_export(v1=["wrap_function"]) def wrap_function(fn, signature, name=None): """Wraps the TF 1.x function fn into a graph function. @@ -109,6 +112,21 @@ def wrap_function(fn, signature, name=None): assert float(f_sub(1.0)) == 3.0 ``` + Both `tf.compat.v1.wrap_function` and `tf.function` create a callable + TensorFlow graph. But while `tf.function` runs all stateful operations + (e.g. `tf.print`) and sequences operations to provide the same semantics as + eager execution, `wrap_function` is closer to the behavior of `session.run` in + TensorFlow 1.x. It will not run any operations unless they are required to + compute the function's outputs, either through a data dependency or a control + dependency. Nor will it sequence operations. + + Unlike `tf.function`, `wrap_function` will only trace the Python function + once. As with placeholders in TF 1.x, shapes and dtypes must be provided to + `wrap_function`'s `signature` argument. + + Since it is only traced once, variables and state may be created inside the + function and owned by the function wrapper object. + Args: fn: python function to be wrapped signature: the placeholder and python arguments to be passed to the diff --git a/tensorflow/python/feature_column/feature_column.py b/tensorflow/python/feature_column/feature_column.py index b7a6a88535..a858d92608 100644 --- a/tensorflow/python/feature_column/feature_column.py +++ b/tensorflow/python/feature_column/feature_column.py @@ -230,7 +230,7 @@ def _internal_input_layer(features, return _get_logits() -@tf_export('feature_column.input_layer') +@tf_export(v1=['feature_column.input_layer']) def input_layer(features, feature_columns, weight_collections=None, @@ -365,7 +365,7 @@ class InputLayer(object): return self._input_layer_template.weights -@tf_export('feature_column.linear_model') +@tf_export(v1=['feature_column.linear_model']) def linear_model(features, feature_columns, units=1, @@ -746,7 +746,7 @@ def _transform_features(features, feature_columns): return outputs -@tf_export('feature_column.make_parse_example_spec') +@tf_export(v1=['feature_column.make_parse_example_spec']) def make_parse_example_spec(feature_columns): """Creates parsing spec dictionary from input feature_columns. @@ -807,11 +807,14 @@ def make_parse_example_spec(feature_columns): return result -@tf_export('feature_column.embedding_column') -def embedding_column( - categorical_column, dimension, combiner='mean', initializer=None, - ckpt_to_load_from=None, tensor_name_in_ckpt=None, max_norm=None, - trainable=True): +def _embedding_column(categorical_column, + dimension, + combiner='mean', + initializer=None, + ckpt_to_load_from=None, + tensor_name_in_ckpt=None, + max_norm=None, + trainable=True): """`_DenseColumn` that converts from sparse, categorical input. Use this when your inputs are sparse, but you want to convert them to a dense @@ -919,178 +922,11 @@ def embedding_column( trainable=trainable) -@tf_export('feature_column.shared_embedding_columns') -def shared_embedding_columns( - categorical_columns, dimension, combiner='mean', initializer=None, - shared_embedding_collection_name=None, ckpt_to_load_from=None, - tensor_name_in_ckpt=None, max_norm=None, trainable=True): - """List of dense columns that convert from sparse, categorical input. - - This is similar to `embedding_column`, except that it produces a list of - embedding columns that share the same embedding weights. - - Use this when your inputs are sparse and of the same type (e.g. watched and - impression video IDs that share the same vocabulary), and you want to convert - them to a dense representation (e.g., to feed to a DNN). - - Inputs must be a list of categorical columns created by any of the - `categorical_column_*` function. They must all be of the same type and have - the same arguments except `key`. E.g. they can be - categorical_column_with_vocabulary_file with the same vocabulary_file. Some or - all columns could also be weighted_categorical_column. - - Here is an example embedding of two features for a DNNClassifier model: - - ```python - watched_video_id = categorical_column_with_vocabulary_file( - 'watched_video_id', video_vocabulary_file, video_vocabulary_size) - impression_video_id = categorical_column_with_vocabulary_file( - 'impression_video_id', video_vocabulary_file, video_vocabulary_size) - columns = shared_embedding_columns( - [watched_video_id, impression_video_id], dimension=10) - - estimator = tf.estimator.DNNClassifier(feature_columns=columns, ...) - - label_column = ... - def input_fn(): - features = tf.parse_example( - ..., features=make_parse_example_spec(columns + [label_column])) - labels = features.pop(label_column.name) - return features, labels - - estimator.train(input_fn=input_fn, steps=100) - ``` - - Here is an example using `shared_embedding_columns` with model_fn: - - ```python - def model_fn(features, ...): - watched_video_id = categorical_column_with_vocabulary_file( - 'watched_video_id', video_vocabulary_file, video_vocabulary_size) - impression_video_id = categorical_column_with_vocabulary_file( - 'impression_video_id', video_vocabulary_file, video_vocabulary_size) - columns = shared_embedding_columns( - [watched_video_id, impression_video_id], dimension=10) - dense_tensor = input_layer(features, columns) - # Form DNN layers, calculate loss, and return EstimatorSpec. - ... - ``` - - Args: - categorical_columns: List of categorical columns created by a - `categorical_column_with_*` function. These columns produce the sparse IDs - that are inputs to the embedding lookup. All columns must be of the same - type and have the same arguments except `key`. E.g. they can be - categorical_column_with_vocabulary_file with the same vocabulary_file. - Some or all columns could also be weighted_categorical_column. - dimension: An integer specifying dimension of the embedding, must be > 0. - combiner: A string specifying how to reduce if there are multiple entries - in a single row. Currently 'mean', 'sqrtn' and 'sum' are supported, with - 'mean' the default. 'sqrtn' often achieves good accuracy, in particular - with bag-of-words columns. Each of this can be thought as example level - normalizations on the column. For more information, see - `tf.embedding_lookup_sparse`. - initializer: A variable initializer function to be used in embedding - variable initialization. If not specified, defaults to - `tf.truncated_normal_initializer` with mean `0.0` and standard deviation - `1/sqrt(dimension)`. - shared_embedding_collection_name: Optional name of the collection where - shared embedding weights are added. If not given, a reasonable name will - be chosen based on the names of `categorical_columns`. This is also used - in `variable_scope` when creating shared embedding weights. - ckpt_to_load_from: String representing checkpoint name/pattern from which to - restore column weights. Required if `tensor_name_in_ckpt` is not `None`. - tensor_name_in_ckpt: Name of the `Tensor` in `ckpt_to_load_from` from - which to restore the column weights. Required if `ckpt_to_load_from` is - not `None`. - max_norm: If not `None`, each embedding is clipped if its l2-norm is - larger than this value, before combining. - trainable: Whether or not the embedding is trainable. Default is True. - - Returns: - A list of dense columns that converts from sparse input. The order of - results follows the ordering of `categorical_columns`. - - Raises: - ValueError: if `dimension` not > 0. - ValueError: if any of the given `categorical_columns` is of different type - or has different arguments than the others. - ValueError: if exactly one of `ckpt_to_load_from` and `tensor_name_in_ckpt` - is specified. - ValueError: if `initializer` is specified and is not callable. - RuntimeError: if eager execution is enabled. - """ - if context.executing_eagerly(): - raise RuntimeError('shared_embedding_columns are not supported when eager ' - 'execution is enabled.') - - if (dimension is None) or (dimension < 1): - raise ValueError('Invalid dimension {}.'.format(dimension)) - if (ckpt_to_load_from is None) != (tensor_name_in_ckpt is None): - raise ValueError('Must specify both `ckpt_to_load_from` and ' - '`tensor_name_in_ckpt` or none of them.') - - if (initializer is not None) and (not callable(initializer)): - raise ValueError('initializer must be callable if specified.') - if initializer is None: - initializer = init_ops.truncated_normal_initializer( - mean=0.0, stddev=1. / math.sqrt(dimension)) - - # Sort the columns so the default collection name is deterministic even if the - # user passes columns from an unsorted collection, such as dict.values(). - sorted_columns = sorted(categorical_columns, key=lambda x: x.name) - - c0 = sorted_columns[0] - num_buckets = c0._num_buckets # pylint: disable=protected-access - if not isinstance(c0, _CategoricalColumn): - raise ValueError( - 'All categorical_columns must be subclasses of _CategoricalColumn. ' - 'Given: {}, of type: {}'.format(c0, type(c0))) - if isinstance(c0, _WeightedCategoricalColumn): - c0 = c0.categorical_column - for c in sorted_columns[1:]: - if isinstance(c, _WeightedCategoricalColumn): - c = c.categorical_column - if not isinstance(c, type(c0)): - raise ValueError( - 'To use shared_embedding_column, all categorical_columns must have ' - 'the same type, or be weighted_categorical_column of the same type. ' - 'Given column: {} of type: {} does not match given column: {} of ' - 'type: {}'.format(c0, type(c0), c, type(c))) - if num_buckets != c._num_buckets: # pylint: disable=protected-access - raise ValueError( - 'To use shared_embedding_column, all categorical_columns must have ' - 'the same number of buckets. Given column: {} with buckets: {} does ' - 'not match column: {} with buckets: {}'.format( - c0, num_buckets, c, c._num_buckets)) # pylint: disable=protected-access - - if not shared_embedding_collection_name: - shared_embedding_collection_name = '_'.join(c.name for c in sorted_columns) - shared_embedding_collection_name += '_shared_embedding' - - result = [] - for column in categorical_columns: - result.append( - _SharedEmbeddingColumn( - categorical_column=column, - initializer=initializer, - dimension=dimension, - combiner=combiner, - shared_embedding_collection_name=shared_embedding_collection_name, - ckpt_to_load_from=ckpt_to_load_from, - tensor_name_in_ckpt=tensor_name_in_ckpt, - max_norm=max_norm, - trainable=trainable)) - - return result - - -@tf_export('feature_column.numeric_column') -def numeric_column(key, - shape=(1,), - default_value=None, - dtype=dtypes.float32, - normalizer_fn=None): +def _numeric_column(key, + shape=(1,), + default_value=None, + dtype=dtypes.float32, + normalizer_fn=None): """Represents real valued or numerical features. Example: @@ -1161,8 +997,7 @@ def numeric_column(key, normalizer_fn=normalizer_fn) -@tf_export('feature_column.bucketized_column') -def bucketized_column(source_column, boundaries): +def _bucketized_column(source_column, boundaries): """Represents discretized dense input. Buckets include the left boundary, and exclude the right boundary. Namely, @@ -1258,10 +1093,9 @@ def _assert_key_is_string(key): type(key), key)) -@tf_export('feature_column.categorical_column_with_hash_bucket') -def categorical_column_with_hash_bucket(key, - hash_bucket_size, - dtype=dtypes.string): +def _categorical_column_with_hash_bucket(key, + hash_bucket_size, + dtype=dtypes.string): """Represents sparse feature where ids are set by hashing. Use this when your sparse features are in string or integer format, and you @@ -1317,13 +1151,12 @@ def categorical_column_with_hash_bucket(key, return _HashedCategoricalColumn(key, hash_bucket_size, dtype) -@tf_export('feature_column.categorical_column_with_vocabulary_file') -def categorical_column_with_vocabulary_file(key, - vocabulary_file, - vocabulary_size=None, - num_oov_buckets=0, - default_value=None, - dtype=dtypes.string): +def _categorical_column_with_vocabulary_file(key, + vocabulary_file, + vocabulary_size=None, + num_oov_buckets=0, + default_value=None, + dtype=dtypes.string): """A `_CategoricalColumn` with a vocabulary file. Use this when your inputs are in string or integer format, and you have a @@ -1437,9 +1270,11 @@ def categorical_column_with_vocabulary_file(key, dtype=dtype) -@tf_export('feature_column.categorical_column_with_vocabulary_list') -def categorical_column_with_vocabulary_list( - key, vocabulary_list, dtype=None, default_value=-1, num_oov_buckets=0): +def _categorical_column_with_vocabulary_list(key, + vocabulary_list, + dtype=None, + default_value=-1, + num_oov_buckets=0): """A `_CategoricalColumn` with in-memory vocabulary. Use this when your inputs are in string or integer format, and you have an @@ -1548,8 +1383,7 @@ def categorical_column_with_vocabulary_list( default_value=default_value, num_oov_buckets=num_oov_buckets) -@tf_export('feature_column.categorical_column_with_identity') -def categorical_column_with_identity(key, num_buckets, default_value=None): +def _categorical_column_with_identity(key, num_buckets, default_value=None): """A `_CategoricalColumn` that returns identity values. Use this when your inputs are integers in the range `[0, num_buckets)`, and @@ -1616,8 +1450,7 @@ def categorical_column_with_identity(key, num_buckets, default_value=None): key=key, num_buckets=num_buckets, default_value=default_value) -@tf_export('feature_column.indicator_column') -def indicator_column(categorical_column): +def _indicator_column(categorical_column): """Represents multi-hot representation of given categorical column. - For DNN model, `indicator_column` can be used to wrap any @@ -1651,9 +1484,9 @@ def indicator_column(categorical_column): return _IndicatorColumn(categorical_column) -@tf_export('feature_column.weighted_categorical_column') -def weighted_categorical_column( - categorical_column, weight_feature_key, dtype=dtypes.float32): +def _weighted_categorical_column(categorical_column, + weight_feature_key, + dtype=dtypes.float32): """Applies weight values to a `_CategoricalColumn`. Use this when each of your sparse inputs has both an ID and a value. For @@ -1726,8 +1559,7 @@ def weighted_categorical_column( dtype=dtype) -@tf_export('feature_column.crossed_column') -def crossed_column(keys, hash_bucket_size, hash_key=None): +def _crossed_column(keys, hash_bucket_size, hash_key=None): """Returns a column for performing crosses of categorical features. Crossed features will be hashed according to `hash_bucket_size`. Conceptually, diff --git a/tensorflow/python/feature_column/feature_column_lib.py b/tensorflow/python/feature_column/feature_column_lib.py index 3b818f18b5..68a2712425 100644 --- a/tensorflow/python/feature_column/feature_column_lib.py +++ b/tensorflow/python/feature_column/feature_column_lib.py @@ -20,4 +20,5 @@ from __future__ import print_function # pylint: disable=unused-import,line-too-long,wildcard-import from tensorflow.python.feature_column.feature_column import * +from tensorflow.python.feature_column.feature_column_v2 import * # pylint: enable=unused-import,line-too-long diff --git a/tensorflow/python/feature_column/feature_column_test.py b/tensorflow/python/feature_column/feature_column_test.py index 1ae510250c..daa0a3b3a4 100644 --- a/tensorflow/python/feature_column/feature_column_test.py +++ b/tensorflow/python/feature_column/feature_column_test.py @@ -30,7 +30,8 @@ from tensorflow.core.protobuf import rewriter_config_pb2 from tensorflow.python.client import session from tensorflow.python.eager import backprop from tensorflow.python.eager import context -from tensorflow.python.feature_column import feature_column_lib as fc +from tensorflow.python.feature_column import feature_column as fc +from tensorflow.python.feature_column import feature_column_v2 as fc_new from tensorflow.python.feature_column.feature_column import _CategoricalColumn from tensorflow.python.feature_column.feature_column import _DenseColumn from tensorflow.python.feature_column.feature_column import _FeatureColumn @@ -169,6 +170,7 @@ class LazyColumnTest(test.TestCase): TypeError, '"key" must be either a "str" or "_FeatureColumn".'): builder.get(NotAFeatureColumn()) + @test_util.run_deprecated_v1 def test_expand_dim_rank_1_sparse_tensor_empty_batch(self): # empty 1-D sparse tensor: builder = _LazyBuilder(features={'a': sparse_tensor.SparseTensor( @@ -184,8 +186,9 @@ class LazyColumnTest(test.TestCase): class NumericColumnTest(test.TestCase): + @test_util.run_deprecated_v1 def test_defaults(self): - a = fc.numeric_column('aaa') + a = fc._numeric_column('aaa') self.assertEqual('aaa', a.key) self.assertEqual('aaa', a.name) self.assertEqual('aaa', a._var_scope_name) @@ -196,53 +199,53 @@ class NumericColumnTest(test.TestCase): def test_key_should_be_string(self): with self.assertRaisesRegexp(ValueError, 'key must be a string.'): - fc.numeric_column(key=('aaa',)) + fc._numeric_column(key=('aaa',)) def test_shape_saved_as_tuple(self): - a = fc.numeric_column('aaa', shape=[1, 2], default_value=[[3, 2.]]) + a = fc._numeric_column('aaa', shape=[1, 2], default_value=[[3, 2.]]) self.assertEqual((1, 2), a.shape) def test_default_value_saved_as_tuple(self): - a = fc.numeric_column('aaa', default_value=4.) + a = fc._numeric_column('aaa', default_value=4.) self.assertEqual((4.,), a.default_value) - a = fc.numeric_column('aaa', shape=[1, 2], default_value=[[3, 2.]]) + a = fc._numeric_column('aaa', shape=[1, 2], default_value=[[3, 2.]]) self.assertEqual(((3., 2.),), a.default_value) def test_shape_and_default_value_compatibility(self): - fc.numeric_column('aaa', shape=[2], default_value=[1, 2.]) + fc._numeric_column('aaa', shape=[2], default_value=[1, 2.]) with self.assertRaisesRegexp(ValueError, 'The shape of default_value'): - fc.numeric_column('aaa', shape=[2], default_value=[1, 2, 3.]) - fc.numeric_column( + fc._numeric_column('aaa', shape=[2], default_value=[1, 2, 3.]) + fc._numeric_column( 'aaa', shape=[3, 2], default_value=[[2, 3], [1, 2], [2, 3.]]) with self.assertRaisesRegexp(ValueError, 'The shape of default_value'): - fc.numeric_column( + fc._numeric_column( 'aaa', shape=[3, 1], default_value=[[2, 3], [1, 2], [2, 3.]]) with self.assertRaisesRegexp(ValueError, 'The shape of default_value'): - fc.numeric_column( + fc._numeric_column( 'aaa', shape=[3, 3], default_value=[[2, 3], [1, 2], [2, 3.]]) def test_default_value_type_check(self): - fc.numeric_column( + fc._numeric_column( 'aaa', shape=[2], default_value=[1, 2.], dtype=dtypes.float32) - fc.numeric_column( + fc._numeric_column( 'aaa', shape=[2], default_value=[1, 2], dtype=dtypes.int32) with self.assertRaisesRegexp(TypeError, 'must be compatible with dtype'): - fc.numeric_column( + fc._numeric_column( 'aaa', shape=[2], default_value=[1, 2.], dtype=dtypes.int32) with self.assertRaisesRegexp(TypeError, 'default_value must be compatible with dtype'): - fc.numeric_column('aaa', default_value=['string']) + fc._numeric_column('aaa', default_value=['string']) def test_shape_must_be_positive_integer(self): with self.assertRaisesRegexp(TypeError, 'shape dimensions must be integer'): - fc.numeric_column( + fc._numeric_column( 'aaa', shape=[ 1.0, ]) with self.assertRaisesRegexp(ValueError, 'shape dimensions must be greater than 0'): - fc.numeric_column( + fc._numeric_column( 'aaa', shape=[ 0, ]) @@ -250,20 +253,21 @@ class NumericColumnTest(test.TestCase): def test_dtype_is_convertible_to_float(self): with self.assertRaisesRegexp(ValueError, 'dtype must be convertible to float'): - fc.numeric_column('aaa', dtype=dtypes.string) + fc._numeric_column('aaa', dtype=dtypes.string) def test_scalar_default_value_fills_the_shape(self): - a = fc.numeric_column('aaa', shape=[2, 3], default_value=2.) + a = fc._numeric_column('aaa', shape=[2, 3], default_value=2.) self.assertEqual(((2., 2., 2.), (2., 2., 2.)), a.default_value) def test_parse_spec(self): - a = fc.numeric_column('aaa', shape=[2, 3], dtype=dtypes.int32) + a = fc._numeric_column('aaa', shape=[2, 3], dtype=dtypes.int32) self.assertEqual({ 'aaa': parsing_ops.FixedLenFeature((2, 3), dtype=dtypes.int32) }, a._parse_example_spec) + @test_util.run_deprecated_v1 def test_parse_example_no_default_value(self): - price = fc.numeric_column('price', shape=[2]) + price = fc._numeric_column('price', shape=[2]) data = example_pb2.Example(features=feature_pb2.Features( feature={ 'price': @@ -277,8 +281,9 @@ class NumericColumnTest(test.TestCase): with self.cached_session(): self.assertAllEqual([[20., 110.]], features['price'].eval()) + @test_util.run_deprecated_v1 def test_parse_example_with_default_value(self): - price = fc.numeric_column('price', shape=[2], default_value=11.) + price = fc._numeric_column('price', shape=[2], default_value=11.) data = example_pb2.Example(features=feature_pb2.Features( feature={ 'price': @@ -301,29 +306,31 @@ class NumericColumnTest(test.TestCase): def test_normalizer_fn_must_be_callable(self): with self.assertRaisesRegexp(TypeError, 'must be a callable'): - fc.numeric_column('price', normalizer_fn='NotACallable') + fc._numeric_column('price', normalizer_fn='NotACallable') + @test_util.run_deprecated_v1 def test_normalizer_fn_transform_feature(self): def _increment_two(input_tensor): return input_tensor + 2. - price = fc.numeric_column('price', shape=[2], normalizer_fn=_increment_two) + price = fc._numeric_column('price', shape=[2], normalizer_fn=_increment_two) output = _transform_features({'price': [[1., 2.], [5., 6.]]}, [price]) with self.cached_session(): self.assertAllEqual([[3., 4.], [7., 8.]], output[price].eval()) + @test_util.run_deprecated_v1 def test_get_dense_tensor(self): def _increment_two(input_tensor): return input_tensor + 2. - price = fc.numeric_column('price', shape=[2], normalizer_fn=_increment_two) + price = fc._numeric_column('price', shape=[2], normalizer_fn=_increment_two) builder = _LazyBuilder({'price': [[1., 2.], [5., 6.]]}) self.assertEqual(builder.get(price), price._get_dense_tensor(builder)) def test_sparse_tensor_not_supported(self): - price = fc.numeric_column('price') + price = fc._numeric_column('price') builder = _LazyBuilder({ 'price': sparse_tensor.SparseTensor( @@ -332,109 +339,113 @@ class NumericColumnTest(test.TestCase): with self.assertRaisesRegexp(ValueError, 'must be a Tensor'): price._transform_feature(builder) + @test_util.run_deprecated_v1 def test_deep_copy(self): - a = fc.numeric_column('aaa', shape=[1, 2], default_value=[[3., 2.]]) + a = fc._numeric_column('aaa', shape=[1, 2], default_value=[[3., 2.]]) a_copy = copy.deepcopy(a) self.assertEqual(a_copy.name, 'aaa') self.assertEqual(a_copy.shape, (1, 2)) self.assertEqual(a_copy.default_value, ((3., 2.),)) def test_numpy_default_value(self): - a = fc.numeric_column( + a = fc._numeric_column( 'aaa', shape=[1, 2], default_value=np.array([[3., 2.]])) self.assertEqual(a.default_value, ((3., 2.),)) + @test_util.run_deprecated_v1 def test_linear_model(self): - price = fc.numeric_column('price') + price = fc._numeric_column('price') with ops.Graph().as_default(): features = {'price': [[1.], [5.]]} predictions = fc.linear_model(features, [price]) bias = get_linear_model_bias() price_var = get_linear_model_column_var(price) with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) - self.assertAllClose([[0.]], price_var.eval()) - self.assertAllClose([[0.], [0.]], predictions.eval()) + self.assertAllClose([0.], self.evaluate(bias)) + self.assertAllClose([[0.]], self.evaluate(price_var)) + self.assertAllClose([[0.], [0.]], self.evaluate(predictions)) sess.run(price_var.assign([[10.]])) - self.assertAllClose([[10.], [50.]], predictions.eval()) + self.assertAllClose([[10.], [50.]], self.evaluate(predictions)) + @test_util.run_deprecated_v1 def test_keras_linear_model(self): - price = fc.numeric_column('price') + price = fc._numeric_column('price') with ops.Graph().as_default(): features = {'price': [[1.], [5.]]} predictions = get_keras_linear_model_predictions(features, [price]) bias = get_linear_model_bias() price_var = get_linear_model_column_var(price) with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) - self.assertAllClose([[0.]], price_var.eval()) - self.assertAllClose([[0.], [0.]], predictions.eval()) + self.assertAllClose([0.], self.evaluate(bias)) + self.assertAllClose([[0.]], self.evaluate(price_var)) + self.assertAllClose([[0.], [0.]], self.evaluate(predictions)) sess.run(price_var.assign([[10.]])) - self.assertAllClose([[10.], [50.]], predictions.eval()) + self.assertAllClose([[10.], [50.]], self.evaluate(predictions)) class BucketizedColumnTest(test.TestCase): def test_invalid_source_column_type(self): - a = fc.categorical_column_with_hash_bucket('aaa', hash_bucket_size=10) + a = fc._categorical_column_with_hash_bucket('aaa', hash_bucket_size=10) with self.assertRaisesRegexp( ValueError, 'source_column must be a column generated with numeric_column'): - fc.bucketized_column(a, boundaries=[0, 1]) + fc._bucketized_column(a, boundaries=[0, 1]) def test_invalid_source_column_shape(self): - a = fc.numeric_column('aaa', shape=[2, 3]) + a = fc._numeric_column('aaa', shape=[2, 3]) with self.assertRaisesRegexp( ValueError, 'source_column must be one-dimensional column'): - fc.bucketized_column(a, boundaries=[0, 1]) + fc._bucketized_column(a, boundaries=[0, 1]) def test_invalid_boundaries(self): - a = fc.numeric_column('aaa') + a = fc._numeric_column('aaa') with self.assertRaisesRegexp( ValueError, 'boundaries must be a sorted list'): - fc.bucketized_column(a, boundaries=None) + fc._bucketized_column(a, boundaries=None) with self.assertRaisesRegexp( ValueError, 'boundaries must be a sorted list'): - fc.bucketized_column(a, boundaries=1.) + fc._bucketized_column(a, boundaries=1.) with self.assertRaisesRegexp( ValueError, 'boundaries must be a sorted list'): - fc.bucketized_column(a, boundaries=[1, 0]) + fc._bucketized_column(a, boundaries=[1, 0]) with self.assertRaisesRegexp( ValueError, 'boundaries must be a sorted list'): - fc.bucketized_column(a, boundaries=[1, 1]) + fc._bucketized_column(a, boundaries=[1, 1]) def test_name(self): - a = fc.numeric_column('aaa', dtype=dtypes.int32) - b = fc.bucketized_column(a, boundaries=[0, 1]) + a = fc._numeric_column('aaa', dtype=dtypes.int32) + b = fc._bucketized_column(a, boundaries=[0, 1]) self.assertEqual('aaa_bucketized', b.name) def test_var_scope_name(self): - a = fc.numeric_column('aaa', dtype=dtypes.int32) - b = fc.bucketized_column(a, boundaries=[0, 1]) + a = fc._numeric_column('aaa', dtype=dtypes.int32) + b = fc._bucketized_column(a, boundaries=[0, 1]) self.assertEqual('aaa_bucketized', b._var_scope_name) def test_parse_spec(self): - a = fc.numeric_column('aaa', shape=[2], dtype=dtypes.int32) - b = fc.bucketized_column(a, boundaries=[0, 1]) + a = fc._numeric_column('aaa', shape=[2], dtype=dtypes.int32) + b = fc._bucketized_column(a, boundaries=[0, 1]) self.assertEqual({ 'aaa': parsing_ops.FixedLenFeature((2,), dtype=dtypes.int32) }, b._parse_example_spec) def test_variable_shape(self): - a = fc.numeric_column('aaa', shape=[2], dtype=dtypes.int32) - b = fc.bucketized_column(a, boundaries=[0, 1]) + a = fc._numeric_column('aaa', shape=[2], dtype=dtypes.int32) + b = fc._bucketized_column(a, boundaries=[0, 1]) # Column 'aaa` has shape [2] times three buckets -> variable_shape=[2, 3]. self.assertAllEqual((2, 3), b._variable_shape) def test_num_buckets(self): - a = fc.numeric_column('aaa', shape=[2], dtype=dtypes.int32) - b = fc.bucketized_column(a, boundaries=[0, 1]) + a = fc._numeric_column('aaa', shape=[2], dtype=dtypes.int32) + b = fc._bucketized_column(a, boundaries=[0, 1]) # Column 'aaa` has shape [2] times three buckets -> num_buckets=6. self.assertEqual(6, b._num_buckets) + @test_util.run_deprecated_v1 def test_parse_example(self): - price = fc.numeric_column('price', shape=[2]) - bucketized_price = fc.bucketized_column(price, boundaries=[0, 50]) + price = fc._numeric_column('price', shape=[2]) + bucketized_price = fc._bucketized_column(price, boundaries=[0, 50]) data = example_pb2.Example(features=feature_pb2.Features( feature={ 'price': @@ -448,9 +459,10 @@ class BucketizedColumnTest(test.TestCase): with self.cached_session(): self.assertAllEqual([[20., 110.]], features['price'].eval()) + @test_util.run_deprecated_v1 def test_transform_feature(self): - price = fc.numeric_column('price', shape=[2]) - bucketized_price = fc.bucketized_column(price, boundaries=[0, 2, 4, 6]) + price = fc._numeric_column('price', shape=[2]) + bucketized_price = fc._bucketized_column(price, boundaries=[0, 2, 4, 6]) with ops.Graph().as_default(): transformed_tensor = _transform_features({ 'price': [[-1., 1.], [5., 6.]] @@ -461,24 +473,22 @@ class BucketizedColumnTest(test.TestCase): def test_get_dense_tensor_one_input_value(self): """Tests _get_dense_tensor() for input with shape=[1].""" - price = fc.numeric_column('price', shape=[1]) - bucketized_price = fc.bucketized_column(price, boundaries=[0, 2, 4, 6]) + price = fc._numeric_column('price', shape=[1]) + bucketized_price = fc._bucketized_column(price, boundaries=[0, 2, 4, 6]) with ops.Graph().as_default(): builder = _LazyBuilder({'price': [[-1.], [1.], [5.], [6.]]}) with _initialized_session(): bucketized_price_tensor = bucketized_price._get_dense_tensor(builder) self.assertAllClose( # One-hot tensor. - [[[1., 0., 0., 0., 0.]], - [[0., 1., 0., 0., 0.]], - [[0., 0., 0., 1., 0.]], - [[0., 0., 0., 0., 1.]]], - bucketized_price_tensor.eval()) + [[[1., 0., 0., 0., 0.]], [[0., 1., 0., 0., 0.]], + [[0., 0., 0., 1., 0.]], [[0., 0., 0., 0., 1.]]], + self.evaluate(bucketized_price_tensor)) def test_get_dense_tensor_two_input_values(self): """Tests _get_dense_tensor() for input with shape=[2].""" - price = fc.numeric_column('price', shape=[2]) - bucketized_price = fc.bucketized_column(price, boundaries=[0, 2, 4, 6]) + price = fc._numeric_column('price', shape=[2]) + bucketized_price = fc._bucketized_column(price, boundaries=[0, 2, 4, 6]) with ops.Graph().as_default(): builder = _LazyBuilder({'price': [[-1., 1.], [5., 6.]]}) with _initialized_session(): @@ -487,12 +497,12 @@ class BucketizedColumnTest(test.TestCase): # One-hot tensor. [[[1., 0., 0., 0., 0.], [0., 1., 0., 0., 0.]], [[0., 0., 0., 1., 0.], [0., 0., 0., 0., 1.]]], - bucketized_price_tensor.eval()) + self.evaluate(bucketized_price_tensor)) def test_get_sparse_tensors_one_input_value(self): """Tests _get_sparse_tensors() for input with shape=[1].""" - price = fc.numeric_column('price', shape=[1]) - bucketized_price = fc.bucketized_column(price, boundaries=[0, 2, 4, 6]) + price = fc._numeric_column('price', shape=[1]) + bucketized_price = fc._bucketized_column(price, boundaries=[0, 2, 4, 6]) with ops.Graph().as_default(): builder = _LazyBuilder({'price': [[-1.], [1.], [5.], [6.]]}) with _initialized_session() as sess: @@ -506,8 +516,8 @@ class BucketizedColumnTest(test.TestCase): def test_get_sparse_tensors_two_input_values(self): """Tests _get_sparse_tensors() for input with shape=[2].""" - price = fc.numeric_column('price', shape=[2]) - bucketized_price = fc.bucketized_column(price, boundaries=[0, 2, 4, 6]) + price = fc._numeric_column('price', shape=[2]) + bucketized_price = fc._bucketized_column(price, boundaries=[0, 2, 4, 6]) with ops.Graph().as_default(): builder = _LazyBuilder({'price': [[-1., 1.], [5., 6.]]}) with _initialized_session() as sess: @@ -522,8 +532,8 @@ class BucketizedColumnTest(test.TestCase): self.assertAllEqual([2, 2], id_tensor_value.dense_shape) def test_sparse_tensor_input_not_supported(self): - price = fc.numeric_column('price') - bucketized_price = fc.bucketized_column(price, boundaries=[0, 1]) + price = fc._numeric_column('price') + bucketized_price = fc._bucketized_column(price, boundaries=[0, 1]) builder = _LazyBuilder({ 'price': sparse_tensor.SparseTensor( @@ -532,9 +542,10 @@ class BucketizedColumnTest(test.TestCase): with self.assertRaisesRegexp(ValueError, 'must be a Tensor'): bucketized_price._transform_feature(builder) + @test_util.run_deprecated_v1 def test_deep_copy(self): - a = fc.numeric_column('aaa', shape=[2]) - a_bucketized = fc.bucketized_column(a, boundaries=[0, 1]) + a = fc._numeric_column('aaa', shape=[2]) + a_bucketized = fc._bucketized_column(a, boundaries=[0, 1]) a_bucketized_copy = copy.deepcopy(a_bucketized) self.assertEqual(a_bucketized_copy.name, 'aaa_bucketized') self.assertAllEqual(a_bucketized_copy._variable_shape, (2, 3)) @@ -542,45 +553,48 @@ class BucketizedColumnTest(test.TestCase): def test_linear_model_one_input_value(self): """Tests linear_model() for input with shape=[1].""" - price = fc.numeric_column('price', shape=[1]) - bucketized_price = fc.bucketized_column(price, boundaries=[0, 2, 4, 6]) + price = fc._numeric_column('price', shape=[1]) + bucketized_price = fc._bucketized_column(price, boundaries=[0, 2, 4, 6]) with ops.Graph().as_default(): features = {'price': [[-1.], [1.], [5.], [6.]]} predictions = fc.linear_model(features, [bucketized_price]) bias = get_linear_model_bias() bucketized_price_var = get_linear_model_column_var(bucketized_price) with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) + self.assertAllClose([0.], self.evaluate(bias)) # One weight variable per bucket, all initialized to zero. - self.assertAllClose( - [[0.], [0.], [0.], [0.], [0.]], bucketized_price_var.eval()) - self.assertAllClose([[0.], [0.], [0.], [0.]], predictions.eval()) + self.assertAllClose([[0.], [0.], [0.], [0.], [0.]], + self.evaluate(bucketized_price_var)) + self.assertAllClose([[0.], [0.], [0.], [0.]], + self.evaluate(predictions)) sess.run(bucketized_price_var.assign( [[10.], [20.], [30.], [40.], [50.]])) # price -1. is in the 0th bucket, whose weight is 10. # price 1. is in the 1st bucket, whose weight is 20. # price 5. is in the 3rd bucket, whose weight is 40. # price 6. is in the 4th bucket, whose weight is 50. - self.assertAllClose([[10.], [20.], [40.], [50.]], predictions.eval()) + self.assertAllClose([[10.], [20.], [40.], [50.]], + self.evaluate(predictions)) sess.run(bias.assign([1.])) - self.assertAllClose([[11.], [21.], [41.], [51.]], predictions.eval()) + self.assertAllClose([[11.], [21.], [41.], [51.]], + self.evaluate(predictions)) def test_linear_model_two_input_values(self): """Tests linear_model() for input with shape=[2].""" - price = fc.numeric_column('price', shape=[2]) - bucketized_price = fc.bucketized_column(price, boundaries=[0, 2, 4, 6]) + price = fc._numeric_column('price', shape=[2]) + bucketized_price = fc._bucketized_column(price, boundaries=[0, 2, 4, 6]) with ops.Graph().as_default(): features = {'price': [[-1., 1.], [5., 6.]]} predictions = fc.linear_model(features, [bucketized_price]) bias = get_linear_model_bias() bucketized_price_var = get_linear_model_column_var(bucketized_price) with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) + self.assertAllClose([0.], self.evaluate(bias)) # One weight per bucket per input column, all initialized to zero. self.assertAllClose( [[0.], [0.], [0.], [0.], [0.], [0.], [0.], [0.], [0.], [0.]], - bucketized_price_var.eval()) - self.assertAllClose([[0.], [0.]], predictions.eval()) + self.evaluate(bucketized_price_var)) + self.assertAllClose([[0.], [0.]], self.evaluate(predictions)) sess.run(bucketized_price_var.assign( [[10.], [20.], [30.], [40.], [50.], [60.], [70.], [80.], [90.], [100.]])) @@ -590,14 +604,14 @@ class BucketizedColumnTest(test.TestCase): # 2nd example: # price 5. is in the 3rd bucket, whose weight is 40. # price 6. is in the 9th bucket, whose weight is 100. - self.assertAllClose([[80.], [140.]], predictions.eval()) + self.assertAllClose([[80.], [140.]], self.evaluate(predictions)) sess.run(bias.assign([1.])) - self.assertAllClose([[81.], [141.]], predictions.eval()) + self.assertAllClose([[81.], [141.]], self.evaluate(predictions)) def test_keras_linear_model_one_input_value(self): """Tests _LinearModel for input with shape=[1].""" - price = fc.numeric_column('price', shape=[1]) - bucketized_price = fc.bucketized_column(price, boundaries=[0, 2, 4, 6]) + price = fc._numeric_column('price', shape=[1]) + bucketized_price = fc._bucketized_column(price, boundaries=[0, 2, 4, 6]) with ops.Graph().as_default(): features = {'price': [[-1.], [1.], [5.], [6.]]} predictions = get_keras_linear_model_predictions(features, @@ -605,25 +619,28 @@ class BucketizedColumnTest(test.TestCase): bias = get_linear_model_bias() bucketized_price_var = get_linear_model_column_var(bucketized_price) with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) + self.assertAllClose([0.], self.evaluate(bias)) # One weight variable per bucket, all initialized to zero. self.assertAllClose([[0.], [0.], [0.], [0.], [0.]], - bucketized_price_var.eval()) - self.assertAllClose([[0.], [0.], [0.], [0.]], predictions.eval()) + self.evaluate(bucketized_price_var)) + self.assertAllClose([[0.], [0.], [0.], [0.]], + self.evaluate(predictions)) sess.run( bucketized_price_var.assign([[10.], [20.], [30.], [40.], [50.]])) # price -1. is in the 0th bucket, whose weight is 10. # price 1. is in the 1st bucket, whose weight is 20. # price 5. is in the 3rd bucket, whose weight is 40. # price 6. is in the 4th bucket, whose weight is 50. - self.assertAllClose([[10.], [20.], [40.], [50.]], predictions.eval()) + self.assertAllClose([[10.], [20.], [40.], [50.]], + self.evaluate(predictions)) sess.run(bias.assign([1.])) - self.assertAllClose([[11.], [21.], [41.], [51.]], predictions.eval()) + self.assertAllClose([[11.], [21.], [41.], [51.]], + self.evaluate(predictions)) def test_keras_linear_model_two_input_values(self): """Tests _LinearModel for input with shape=[2].""" - price = fc.numeric_column('price', shape=[2]) - bucketized_price = fc.bucketized_column(price, boundaries=[0, 2, 4, 6]) + price = fc._numeric_column('price', shape=[2]) + bucketized_price = fc._bucketized_column(price, boundaries=[0, 2, 4, 6]) with ops.Graph().as_default(): features = {'price': [[-1., 1.], [5., 6.]]} predictions = get_keras_linear_model_predictions(features, @@ -631,12 +648,12 @@ class BucketizedColumnTest(test.TestCase): bias = get_linear_model_bias() bucketized_price_var = get_linear_model_column_var(bucketized_price) with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) + self.assertAllClose([0.], self.evaluate(bias)) # One weight per bucket per input column, all initialized to zero. self.assertAllClose( [[0.], [0.], [0.], [0.], [0.], [0.], [0.], [0.], [0.], [0.]], - bucketized_price_var.eval()) - self.assertAllClose([[0.], [0.]], predictions.eval()) + self.evaluate(bucketized_price_var)) + self.assertAllClose([[0.], [0.]], self.evaluate(predictions)) sess.run( bucketized_price_var.assign([[10.], [20.], [30.], [40.], [50.], [60.], [70.], [80.], [90.], [100.]])) @@ -646,15 +663,16 @@ class BucketizedColumnTest(test.TestCase): # 2nd example: # price 5. is in the 3rd bucket, whose weight is 40. # price 6. is in the 9th bucket, whose weight is 100. - self.assertAllClose([[80.], [140.]], predictions.eval()) + self.assertAllClose([[80.], [140.]], self.evaluate(predictions)) sess.run(bias.assign([1.])) - self.assertAllClose([[81.], [141.]], predictions.eval()) + self.assertAllClose([[81.], [141.]], self.evaluate(predictions)) class HashedCategoricalColumnTest(test.TestCase): + @test_util.run_deprecated_v1 def test_defaults(self): - a = fc.categorical_column_with_hash_bucket('aaa', 10) + a = fc._categorical_column_with_hash_bucket('aaa', 10) self.assertEqual('aaa', a.name) self.assertEqual('aaa', a._var_scope_name) self.assertEqual('aaa', a.key) @@ -663,25 +681,26 @@ class HashedCategoricalColumnTest(test.TestCase): def test_key_should_be_string(self): with self.assertRaisesRegexp(ValueError, 'key must be a string.'): - fc.categorical_column_with_hash_bucket(('key',), 10) + fc._categorical_column_with_hash_bucket(('key',), 10) def test_bucket_size_should_be_given(self): with self.assertRaisesRegexp(ValueError, 'hash_bucket_size must be set.'): - fc.categorical_column_with_hash_bucket('aaa', None) + fc._categorical_column_with_hash_bucket('aaa', None) def test_bucket_size_should_be_positive(self): with self.assertRaisesRegexp(ValueError, 'hash_bucket_size must be at least 1'): - fc.categorical_column_with_hash_bucket('aaa', 0) + fc._categorical_column_with_hash_bucket('aaa', 0) def test_dtype_should_be_string_or_integer(self): - fc.categorical_column_with_hash_bucket('aaa', 10, dtype=dtypes.string) - fc.categorical_column_with_hash_bucket('aaa', 10, dtype=dtypes.int32) + fc._categorical_column_with_hash_bucket('aaa', 10, dtype=dtypes.string) + fc._categorical_column_with_hash_bucket('aaa', 10, dtype=dtypes.int32) with self.assertRaisesRegexp(ValueError, 'dtype must be string or integer'): - fc.categorical_column_with_hash_bucket('aaa', 10, dtype=dtypes.float32) + fc._categorical_column_with_hash_bucket('aaa', 10, dtype=dtypes.float32) + @test_util.run_deprecated_v1 def test_deep_copy(self): - original = fc.categorical_column_with_hash_bucket('aaa', 10) + original = fc._categorical_column_with_hash_bucket('aaa', 10) for column in (original, copy.deepcopy(original)): self.assertEqual('aaa', column.name) self.assertEqual(10, column.hash_bucket_size) @@ -689,19 +708,20 @@ class HashedCategoricalColumnTest(test.TestCase): self.assertEqual(dtypes.string, column.dtype) def test_parse_spec_string(self): - a = fc.categorical_column_with_hash_bucket('aaa', 10) + a = fc._categorical_column_with_hash_bucket('aaa', 10) self.assertEqual({ 'aaa': parsing_ops.VarLenFeature(dtypes.string) }, a._parse_example_spec) def test_parse_spec_int(self): - a = fc.categorical_column_with_hash_bucket('aaa', 10, dtype=dtypes.int32) + a = fc._categorical_column_with_hash_bucket('aaa', 10, dtype=dtypes.int32) self.assertEqual({ 'aaa': parsing_ops.VarLenFeature(dtypes.int32) }, a._parse_example_spec) + @test_util.run_deprecated_v1 def test_parse_example(self): - a = fc.categorical_column_with_hash_bucket('aaa', 10) + a = fc._categorical_column_with_hash_bucket('aaa', 10) data = example_pb2.Example(features=feature_pb2.Features( feature={ 'aaa': @@ -721,8 +741,9 @@ class HashedCategoricalColumnTest(test.TestCase): dense_shape=[1, 2]), features['aaa'].eval()) + @test_util.run_deprecated_v1 def test_strings_should_be_hashed(self): - hashed_sparse = fc.categorical_column_with_hash_bucket('wire', 10) + hashed_sparse = fc._categorical_column_with_hash_bucket('wire', 10) wire_tensor = sparse_tensor.SparseTensor( values=['omar', 'stringer', 'marlo'], indices=[[0, 0], [1, 0], [1, 1]], @@ -739,11 +760,11 @@ class HashedCategoricalColumnTest(test.TestCase): output.dense_shape.eval()) def test_tensor_dtype_should_be_string_or_integer(self): - string_fc = fc.categorical_column_with_hash_bucket( + string_fc = fc._categorical_column_with_hash_bucket( 'a_string', 10, dtype=dtypes.string) - int_fc = fc.categorical_column_with_hash_bucket( + int_fc = fc._categorical_column_with_hash_bucket( 'a_int', 10, dtype=dtypes.int32) - float_fc = fc.categorical_column_with_hash_bucket( + float_fc = fc._categorical_column_with_hash_bucket( 'a_float', 10, dtype=dtypes.string) int_tensor = sparse_tensor.SparseTensor( values=[101], @@ -768,7 +789,7 @@ class HashedCategoricalColumnTest(test.TestCase): builder.get(float_fc) def test_dtype_should_match_with_tensor(self): - hashed_sparse = fc.categorical_column_with_hash_bucket( + hashed_sparse = fc._categorical_column_with_hash_bucket( 'wire', 10, dtype=dtypes.int64) wire_tensor = sparse_tensor.SparseTensor( values=['omar'], indices=[[0, 0]], dense_shape=[1, 1]) @@ -776,8 +797,9 @@ class HashedCategoricalColumnTest(test.TestCase): with self.assertRaisesRegexp(ValueError, 'dtype must be compatible'): builder.get(hashed_sparse) + @test_util.run_deprecated_v1 def test_ints_should_be_hashed(self): - hashed_sparse = fc.categorical_column_with_hash_bucket( + hashed_sparse = fc._categorical_column_with_hash_bucket( 'wire', 10, dtype=dtypes.int64) wire_tensor = sparse_tensor.SparseTensor( values=[101, 201, 301], @@ -790,8 +812,9 @@ class HashedCategoricalColumnTest(test.TestCase): with self.cached_session(): self.assertAllEqual(expected_values, output.values.eval()) + @test_util.run_deprecated_v1 def test_int32_64_is_compatible(self): - hashed_sparse = fc.categorical_column_with_hash_bucket( + hashed_sparse = fc._categorical_column_with_hash_bucket( 'wire', 10, dtype=dtypes.int64) wire_tensor = sparse_tensor.SparseTensor( values=constant_op.constant([101, 201, 301], dtype=dtypes.int32), @@ -804,8 +827,9 @@ class HashedCategoricalColumnTest(test.TestCase): with self.cached_session(): self.assertAllEqual(expected_values, output.values.eval()) + @test_util.run_deprecated_v1 def test_get_sparse_tensors(self): - hashed_sparse = fc.categorical_column_with_hash_bucket('wire', 10) + hashed_sparse = fc._categorical_column_with_hash_bucket('wire', 10) builder = _LazyBuilder({ 'wire': sparse_tensor.SparseTensor( @@ -818,7 +842,7 @@ class HashedCategoricalColumnTest(test.TestCase): self.assertEqual(builder.get(hashed_sparse), id_weight_pair.id_tensor) def test_get_sparse_tensors_weight_collections(self): - column = fc.categorical_column_with_hash_bucket('aaa', 10) + column = fc._categorical_column_with_hash_bucket('aaa', 10) inputs = sparse_tensor.SparseTensor( values=['omar', 'stringer', 'marlo'], indices=[[0, 0], [1, 0], [1, 1]], @@ -832,15 +856,17 @@ class HashedCategoricalColumnTest(test.TestCase): [], ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES)) self.assertItemsEqual([], ops.get_collection('my_weights')) + @test_util.run_deprecated_v1 def test_get_sparse_tensors_dense_input(self): - hashed_sparse = fc.categorical_column_with_hash_bucket('wire', 10) + hashed_sparse = fc._categorical_column_with_hash_bucket('wire', 10) builder = _LazyBuilder({'wire': (('omar', ''), ('stringer', 'marlo'))}) id_weight_pair = hashed_sparse._get_sparse_tensors(builder) self.assertIsNone(id_weight_pair.weight_tensor) self.assertEqual(builder.get(hashed_sparse), id_weight_pair.id_tensor) + @test_util.run_deprecated_v1 def test_linear_model(self): - wire_column = fc.categorical_column_with_hash_bucket('wire', 4) + wire_column = fc._categorical_column_with_hash_bucket('wire', 4) self.assertEqual(4, wire_column._num_buckets) with ops.Graph().as_default(): predictions = fc.linear_model({ @@ -852,16 +878,18 @@ class HashedCategoricalColumnTest(test.TestCase): bias = get_linear_model_bias() wire_var = get_linear_model_column_var(wire_column) with _initialized_session(): - self.assertAllClose((0.,), bias.eval()) - self.assertAllClose(((0.,), (0.,), (0.,), (0.,)), wire_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) + self.assertAllClose((0.,), self.evaluate(bias)) + self.assertAllClose(((0.,), (0.,), (0.,), (0.,)), + self.evaluate(wire_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) wire_var.assign(((1.,), (2.,), (3.,), (4.,))).eval() # 'marlo' -> 3: wire_var[3] = 4 # 'skywalker' -> 2, 'omar' -> 2: wire_var[2] + wire_var[2] = 3+3 = 6 - self.assertAllClose(((4.,), (6.,)), predictions.eval()) + self.assertAllClose(((4.,), (6.,)), self.evaluate(predictions)) + @test_util.run_deprecated_v1 def test_keras_linear_model(self): - wire_column = fc.categorical_column_with_hash_bucket('wire', 4) + wire_column = fc._categorical_column_with_hash_bucket('wire', 4) self.assertEqual(4, wire_column._num_buckets) with ops.Graph().as_default(): predictions = get_keras_linear_model_predictions({ @@ -874,13 +902,14 @@ class HashedCategoricalColumnTest(test.TestCase): bias = get_linear_model_bias() wire_var = get_linear_model_column_var(wire_column) with _initialized_session(): - self.assertAllClose((0.,), bias.eval()) - self.assertAllClose(((0.,), (0.,), (0.,), (0.,)), wire_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) + self.assertAllClose((0.,), self.evaluate(bias)) + self.assertAllClose(((0.,), (0.,), (0.,), (0.,)), + self.evaluate(wire_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) wire_var.assign(((1.,), (2.,), (3.,), (4.,))).eval() # 'marlo' -> 3: wire_var[3] = 4 # 'skywalker' -> 2, 'omar' -> 2: wire_var[2] + wire_var[2] = 3+3 = 6 - self.assertAllClose(((4.,), (6.,)), predictions.eval()) + self.assertAllClose(((4.,), (6.,)), self.evaluate(predictions)) class CrossedColumnTest(test.TestCase): @@ -888,100 +917,102 @@ class CrossedColumnTest(test.TestCase): def test_keys_empty(self): with self.assertRaisesRegexp( ValueError, 'keys must be a list with length > 1'): - fc.crossed_column([], 10) + fc._crossed_column([], 10) def test_keys_length_one(self): with self.assertRaisesRegexp( ValueError, 'keys must be a list with length > 1'): - fc.crossed_column(['a'], 10) + fc._crossed_column(['a'], 10) def test_key_type_unsupported(self): with self.assertRaisesRegexp(ValueError, 'Unsupported key type'): - fc.crossed_column(['a', fc.numeric_column('c')], 10) + fc._crossed_column(['a', fc._numeric_column('c')], 10) with self.assertRaisesRegexp( ValueError, 'categorical_column_with_hash_bucket is not supported'): - fc.crossed_column( - ['a', fc.categorical_column_with_hash_bucket('c', 10)], 10) + fc._crossed_column( + ['a', fc._categorical_column_with_hash_bucket('c', 10)], 10) def test_hash_bucket_size_negative(self): with self.assertRaisesRegexp( ValueError, 'hash_bucket_size must be > 1'): - fc.crossed_column(['a', 'c'], -1) + fc._crossed_column(['a', 'c'], -1) def test_hash_bucket_size_zero(self): with self.assertRaisesRegexp( ValueError, 'hash_bucket_size must be > 1'): - fc.crossed_column(['a', 'c'], 0) + fc._crossed_column(['a', 'c'], 0) def test_hash_bucket_size_none(self): with self.assertRaisesRegexp( ValueError, 'hash_bucket_size must be > 1'): - fc.crossed_column(['a', 'c'], None) + fc._crossed_column(['a', 'c'], None) def test_name(self): - a = fc.numeric_column('a', dtype=dtypes.int32) - b = fc.bucketized_column(a, boundaries=[0, 1]) - crossed1 = fc.crossed_column(['d1', 'd2'], 10) + a = fc._numeric_column('a', dtype=dtypes.int32) + b = fc._bucketized_column(a, boundaries=[0, 1]) + crossed1 = fc._crossed_column(['d1', 'd2'], 10) - crossed2 = fc.crossed_column([b, 'c', crossed1], 10) + crossed2 = fc._crossed_column([b, 'c', crossed1], 10) self.assertEqual('a_bucketized_X_c_X_d1_X_d2', crossed2.name) def test_name_ordered_alphabetically(self): """Tests that the name does not depend on the order of given columns.""" - a = fc.numeric_column('a', dtype=dtypes.int32) - b = fc.bucketized_column(a, boundaries=[0, 1]) - crossed1 = fc.crossed_column(['d1', 'd2'], 10) + a = fc._numeric_column('a', dtype=dtypes.int32) + b = fc._bucketized_column(a, boundaries=[0, 1]) + crossed1 = fc._crossed_column(['d1', 'd2'], 10) - crossed2 = fc.crossed_column([crossed1, 'c', b], 10) + crossed2 = fc._crossed_column([crossed1, 'c', b], 10) self.assertEqual('a_bucketized_X_c_X_d1_X_d2', crossed2.name) def test_name_leaf_keys_ordered_alphabetically(self): """Tests that the name does not depend on the order of given columns.""" - a = fc.numeric_column('a', dtype=dtypes.int32) - b = fc.bucketized_column(a, boundaries=[0, 1]) - crossed1 = fc.crossed_column(['d2', 'c'], 10) + a = fc._numeric_column('a', dtype=dtypes.int32) + b = fc._bucketized_column(a, boundaries=[0, 1]) + crossed1 = fc._crossed_column(['d2', 'c'], 10) - crossed2 = fc.crossed_column([crossed1, 'd1', b], 10) + crossed2 = fc._crossed_column([crossed1, 'd1', b], 10) self.assertEqual('a_bucketized_X_c_X_d1_X_d2', crossed2.name) def test_var_scope_name(self): - a = fc.numeric_column('a', dtype=dtypes.int32) - b = fc.bucketized_column(a, boundaries=[0, 1]) - crossed1 = fc.crossed_column(['d1', 'd2'], 10) + a = fc._numeric_column('a', dtype=dtypes.int32) + b = fc._bucketized_column(a, boundaries=[0, 1]) + crossed1 = fc._crossed_column(['d1', 'd2'], 10) - crossed2 = fc.crossed_column([b, 'c', crossed1], 10) + crossed2 = fc._crossed_column([b, 'c', crossed1], 10) self.assertEqual('a_bucketized_X_c_X_d1_X_d2', crossed2._var_scope_name) def test_parse_spec(self): - a = fc.numeric_column('a', shape=[2], dtype=dtypes.int32) - b = fc.bucketized_column(a, boundaries=[0, 1]) - crossed = fc.crossed_column([b, 'c'], 10) + a = fc._numeric_column('a', shape=[2], dtype=dtypes.int32) + b = fc._bucketized_column(a, boundaries=[0, 1]) + crossed = fc._crossed_column([b, 'c'], 10) self.assertEqual({ 'a': parsing_ops.FixedLenFeature((2,), dtype=dtypes.int32), 'c': parsing_ops.VarLenFeature(dtypes.string), }, crossed._parse_example_spec) def test_num_buckets(self): - a = fc.numeric_column('a', shape=[2], dtype=dtypes.int32) - b = fc.bucketized_column(a, boundaries=[0, 1]) - crossed = fc.crossed_column([b, 'c'], 15) + a = fc._numeric_column('a', shape=[2], dtype=dtypes.int32) + b = fc._bucketized_column(a, boundaries=[0, 1]) + crossed = fc._crossed_column([b, 'c'], 15) self.assertEqual(15, crossed._num_buckets) + @test_util.run_deprecated_v1 def test_deep_copy(self): - a = fc.numeric_column('a', dtype=dtypes.int32) - b = fc.bucketized_column(a, boundaries=[0, 1]) - crossed1 = fc.crossed_column(['d1', 'd2'], 10) - crossed2 = fc.crossed_column([b, 'c', crossed1], 15, hash_key=5) + a = fc._numeric_column('a', dtype=dtypes.int32) + b = fc._bucketized_column(a, boundaries=[0, 1]) + crossed1 = fc._crossed_column(['d1', 'd2'], 10) + crossed2 = fc._crossed_column([b, 'c', crossed1], 15, hash_key=5) crossed2_copy = copy.deepcopy(crossed2) self.assertEqual('a_bucketized_X_c_X_d1_X_d2', crossed2_copy.name,) self.assertEqual(15, crossed2_copy.hash_bucket_size) self.assertEqual(5, crossed2_copy.hash_key) + @test_util.run_deprecated_v1 def test_parse_example(self): - price = fc.numeric_column('price', shape=[2]) - bucketized_price = fc.bucketized_column(price, boundaries=[0, 50]) - price_cross_wire = fc.crossed_column([bucketized_price, 'wire'], 10) + price = fc._numeric_column('price', shape=[2]) + bucketized_price = fc._bucketized_column(price, boundaries=[0, 50]) + price_cross_wire = fc._crossed_column([bucketized_price, 'wire'], 10) data = example_pb2.Example(features=feature_pb2.Features( feature={ 'price': @@ -1004,12 +1035,13 @@ class CrossedColumnTest(test.TestCase): self.assertAllEqual([b'omar', b'stringer'], wire_sparse.values.eval()) self.assertAllEqual([1, 2], wire_sparse.dense_shape.eval()) + @test_util.run_deprecated_v1 def test_transform_feature(self): - price = fc.numeric_column('price', shape=[2]) - bucketized_price = fc.bucketized_column(price, boundaries=[0, 50]) + price = fc._numeric_column('price', shape=[2]) + bucketized_price = fc._bucketized_column(price, boundaries=[0, 50]) hash_bucket_size = 10 - price_cross_wire = fc.crossed_column( - [bucketized_price, 'wire'], hash_bucket_size) + price_cross_wire = fc._crossed_column([bucketized_price, 'wire'], + hash_bucket_size) features = { 'price': constant_op.constant([[1., 2.], [5., 6.]]), 'wire': sparse_tensor.SparseTensor( @@ -1020,18 +1052,19 @@ class CrossedColumnTest(test.TestCase): outputs = _transform_features(features, [price_cross_wire]) output = outputs[price_cross_wire] with self.cached_session() as sess: - output_val = sess.run(output) + output_val = self.evaluate(output) self.assertAllEqual( [[0, 0], [0, 1], [1, 0], [1, 1], [1, 2], [1, 3]], output_val.indices) for val in output_val.values: self.assertIn(val, list(range(hash_bucket_size))) self.assertAllEqual([2, 4], output_val.dense_shape) + @test_util.run_deprecated_v1 def test_get_sparse_tensors(self): - a = fc.numeric_column('a', dtype=dtypes.int32, shape=(2,)) - b = fc.bucketized_column(a, boundaries=(0, 1)) - crossed1 = fc.crossed_column(['d1', 'd2'], 10) - crossed2 = fc.crossed_column([b, 'c', crossed1], 15, hash_key=5) + a = fc._numeric_column('a', dtype=dtypes.int32, shape=(2,)) + b = fc._bucketized_column(a, boundaries=(0, 1)) + crossed1 = fc._crossed_column(['d1', 'd2'], 10) + crossed2 = fc._crossed_column([b, 'c', crossed1], 15, hash_key=5) with ops.Graph().as_default(): builder = _LazyBuilder({ 'a': @@ -1069,9 +1102,9 @@ class CrossedColumnTest(test.TestCase): def test_get_sparse_tensors_simple(self): """Same as test_get_sparse_tensors, but with simpler values.""" - a = fc.numeric_column('a', dtype=dtypes.int32, shape=(2,)) - b = fc.bucketized_column(a, boundaries=(0, 1)) - crossed = fc.crossed_column([b, 'c'], hash_bucket_size=5, hash_key=5) + a = fc._numeric_column('a', dtype=dtypes.int32, shape=(2,)) + b = fc._bucketized_column(a, boundaries=(0, 1)) + crossed = fc._crossed_column([b, 'c'], hash_bucket_size=5, hash_key=5) with ops.Graph().as_default(): builder = _LazyBuilder({ 'a': @@ -1094,14 +1127,15 @@ class CrossedColumnTest(test.TestCase): self.assertAllEqual(expected_values, id_tensor_eval.values) self.assertAllEqual((2, 4), id_tensor_eval.dense_shape) + @test_util.run_deprecated_v1 def test_linear_model(self): """Tests linear_model. Uses data from test_get_sparse_tesnsors_simple. """ - a = fc.numeric_column('a', dtype=dtypes.int32, shape=(2,)) - b = fc.bucketized_column(a, boundaries=(0, 1)) - crossed = fc.crossed_column([b, 'c'], hash_bucket_size=5, hash_key=5) + a = fc._numeric_column('a', dtype=dtypes.int32, shape=(2,)) + b = fc._bucketized_column(a, boundaries=(0, 1)) + crossed = fc._crossed_column([b, 'c'], hash_bucket_size=5, hash_key=5) with ops.Graph().as_default(): predictions = fc.linear_model({ 'a': constant_op.constant(((-1., .5), (.5, 1.))), @@ -1113,15 +1147,15 @@ class CrossedColumnTest(test.TestCase): bias = get_linear_model_bias() crossed_var = get_linear_model_column_var(crossed) with _initialized_session() as sess: - self.assertAllClose((0.,), bias.eval()) - self.assertAllClose( - ((0.,), (0.,), (0.,), (0.,), (0.,)), crossed_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) + self.assertAllClose((0.,), self.evaluate(bias)) + self.assertAllClose(((0.,), (0.,), (0.,), (0.,), (0.,)), + self.evaluate(crossed_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) sess.run(crossed_var.assign(((1.,), (2.,), (3.,), (4.,), (5.,)))) # Expected ids after cross = (1, 0, 1, 3, 4, 2) - self.assertAllClose(((3.,), (14.,)), predictions.eval()) + self.assertAllClose(((3.,), (14.,)), self.evaluate(predictions)) sess.run(bias.assign((.1,))) - self.assertAllClose(((3.1,), (14.1,)), predictions.eval()) + self.assertAllClose(((3.1,), (14.1,)), self.evaluate(predictions)) def test_linear_model_with_weights(self): class _TestColumnWithWeights(_CategoricalColumn): @@ -1155,7 +1189,7 @@ class CrossedColumnTest(test.TestCase): id_tensor=ids_and_weights[0], weight_tensor=ids_and_weights[1]) t = _TestColumnWithWeights() - crossed = fc.crossed_column([t, 'c'], hash_bucket_size=5, hash_key=5) + crossed = fc._crossed_column([t, 'c'], hash_bucket_size=5, hash_key=5) with ops.Graph().as_default(): with self.assertRaisesRegexp( ValueError, @@ -1175,14 +1209,15 @@ class CrossedColumnTest(test.TestCase): dense_shape=(2, 2)), }, (crossed,)) + @test_util.run_deprecated_v1 def test_keras_linear_model(self): """Tests _LinearModel. Uses data from test_get_sparse_tesnsors_simple. """ - a = fc.numeric_column('a', dtype=dtypes.int32, shape=(2,)) - b = fc.bucketized_column(a, boundaries=(0, 1)) - crossed = fc.crossed_column([b, 'c'], hash_bucket_size=5, hash_key=5) + a = fc._numeric_column('a', dtype=dtypes.int32, shape=(2,)) + b = fc._bucketized_column(a, boundaries=(0, 1)) + crossed = fc._crossed_column([b, 'c'], hash_bucket_size=5, hash_key=5) with ops.Graph().as_default(): predictions = get_keras_linear_model_predictions({ 'a': @@ -1196,15 +1231,15 @@ class CrossedColumnTest(test.TestCase): bias = get_linear_model_bias() crossed_var = get_linear_model_column_var(crossed) with _initialized_session() as sess: - self.assertAllClose((0.,), bias.eval()) + self.assertAllClose((0.,), self.evaluate(bias)) self.assertAllClose(((0.,), (0.,), (0.,), (0.,), (0.,)), - crossed_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) + self.evaluate(crossed_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) sess.run(crossed_var.assign(((1.,), (2.,), (3.,), (4.,), (5.,)))) # Expected ids after cross = (1, 0, 1, 3, 4, 2) - self.assertAllClose(((3.,), (14.,)), predictions.eval()) + self.assertAllClose(((3.,), (14.,)), self.evaluate(predictions)) sess.run(bias.assign((.1,))) - self.assertAllClose(((3.1,), (14.1,)), predictions.eval()) + self.assertAllClose(((3.1,), (14.1,)), self.evaluate(predictions)) def test_keras_linear_model_with_weights(self): @@ -1242,7 +1277,7 @@ class CrossedColumnTest(test.TestCase): id_tensor=ids_and_weights[0], weight_tensor=ids_and_weights[1]) t = _TestColumnWithWeights() - crossed = fc.crossed_column([t, 'c'], hash_bucket_size=5, hash_key=5) + crossed = fc._crossed_column([t, 'c'], hash_bucket_size=5, hash_key=5) with ops.Graph().as_default(): with self.assertRaisesRegexp( ValueError, @@ -1331,31 +1366,31 @@ class LinearModelTest(test.TestCase): with self.assertRaisesRegexp( ValueError, 'Expected feature_columns to be iterable, found dict.'): fc.linear_model( - features={'a': [[0]]}, feature_columns={'a': fc.numeric_column('a')}) + features={'a': [[0]]}, feature_columns={'a': fc._numeric_column('a')}) def test_raises_if_duplicate_name(self): with self.assertRaisesRegexp( ValueError, 'Duplicate feature column name found for columns'): fc.linear_model( features={'a': [[0]]}, - feature_columns=[fc.numeric_column('a'), - fc.numeric_column('a')]) + feature_columns=[fc._numeric_column('a'), + fc._numeric_column('a')]) def test_dense_bias(self): - price = fc.numeric_column('price') + price = fc._numeric_column('price') with ops.Graph().as_default(): features = {'price': [[1.], [5.]]} predictions = fc.linear_model(features, [price]) bias = get_linear_model_bias() price_var = get_linear_model_column_var(price) with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) + self.assertAllClose([0.], self.evaluate(bias)) sess.run(price_var.assign([[10.]])) sess.run(bias.assign([5.])) - self.assertAllClose([[15.], [55.]], predictions.eval()) + self.assertAllClose([[15.], [55.]], self.evaluate(predictions)) def test_sparse_bias(self): - wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) + wire_cast = fc._categorical_column_with_hash_bucket('wire_cast', 4) with ops.Graph().as_default(): wire_tensor = sparse_tensor.SparseTensor( values=['omar', 'stringer', 'marlo'], # hashed to = [2, 0, 3] @@ -1366,15 +1401,16 @@ class LinearModelTest(test.TestCase): bias = get_linear_model_bias() wire_cast_var = get_linear_model_column_var(wire_cast) with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) - self.assertAllClose([[0.], [0.], [0.], [0.]], wire_cast_var.eval()) + self.assertAllClose([0.], self.evaluate(bias)) + self.assertAllClose([[0.], [0.], [0.], [0.]], + self.evaluate(wire_cast_var)) sess.run(wire_cast_var.assign([[10.], [100.], [1000.], [10000.]])) sess.run(bias.assign([5.])) - self.assertAllClose([[1005.], [10015.]], predictions.eval()) + self.assertAllClose([[1005.], [10015.]], self.evaluate(predictions)) def test_dense_and_sparse_bias(self): - wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) - price = fc.numeric_column('price') + wire_cast = fc._categorical_column_with_hash_bucket('wire_cast', 4) + price = fc._numeric_column('price') with ops.Graph().as_default(): wire_tensor = sparse_tensor.SparseTensor( values=['omar', 'stringer', 'marlo'], # hashed to = [2, 0, 3] @@ -1389,7 +1425,7 @@ class LinearModelTest(test.TestCase): sess.run(wire_cast_var.assign([[10.], [100.], [1000.], [10000.]])) sess.run(bias.assign([5.])) sess.run(price_var.assign([[10.]])) - self.assertAllClose([[1015.], [10065.]], predictions.eval()) + self.assertAllClose([[1015.], [10065.]], self.evaluate(predictions)) def test_dense_and_sparse_column(self): """When the column is both dense and sparse, uses sparse tensors.""" @@ -1442,25 +1478,25 @@ class LinearModelTest(test.TestCase): sess.run(dense_and_sparse_column_var.assign( [[10.], [100.], [1000.], [10000.]])) sess.run(bias.assign([5.])) - self.assertAllClose([[1005.], [10015.]], predictions.eval()) + self.assertAllClose([[1005.], [10015.]], self.evaluate(predictions)) def test_dense_multi_output(self): - price = fc.numeric_column('price') + price = fc._numeric_column('price') with ops.Graph().as_default(): features = {'price': [[1.], [5.]]} predictions = fc.linear_model(features, [price], units=3) bias = get_linear_model_bias() price_var = get_linear_model_column_var(price) with _initialized_session() as sess: - self.assertAllClose(np.zeros((3,)), bias.eval()) - self.assertAllClose(np.zeros((1, 3)), price_var.eval()) + self.assertAllClose(np.zeros((3,)), self.evaluate(bias)) + self.assertAllClose(np.zeros((1, 3)), self.evaluate(price_var)) sess.run(price_var.assign([[10., 100., 1000.]])) sess.run(bias.assign([5., 6., 7.])) self.assertAllClose([[15., 106., 1007.], [55., 506., 5007.]], - predictions.eval()) + self.evaluate(predictions)) def test_sparse_multi_output(self): - wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) + wire_cast = fc._categorical_column_with_hash_bucket('wire_cast', 4) with ops.Graph().as_default(): wire_tensor = sparse_tensor.SparseTensor( values=['omar', 'stringer', 'marlo'], # hashed to = [2, 0, 3] @@ -1471,29 +1507,29 @@ class LinearModelTest(test.TestCase): bias = get_linear_model_bias() wire_cast_var = get_linear_model_column_var(wire_cast) with _initialized_session() as sess: - self.assertAllClose(np.zeros((3,)), bias.eval()) - self.assertAllClose(np.zeros((4, 3)), wire_cast_var.eval()) + self.assertAllClose(np.zeros((3,)), self.evaluate(bias)) + self.assertAllClose(np.zeros((4, 3)), self.evaluate(wire_cast_var)) sess.run( wire_cast_var.assign([[10., 11., 12.], [100., 110., 120.], [ 1000., 1100., 1200. ], [10000., 11000., 12000.]])) sess.run(bias.assign([5., 6., 7.])) self.assertAllClose([[1005., 1106., 1207.], [10015., 11017., 12019.]], - predictions.eval()) + self.evaluate(predictions)) def test_dense_multi_dimension(self): - price = fc.numeric_column('price', shape=2) + price = fc._numeric_column('price', shape=2) with ops.Graph().as_default(): features = {'price': [[1., 2.], [5., 6.]]} predictions = fc.linear_model(features, [price]) price_var = get_linear_model_column_var(price) with _initialized_session() as sess: - self.assertAllClose([[0.], [0.]], price_var.eval()) + self.assertAllClose([[0.], [0.]], self.evaluate(price_var)) sess.run(price_var.assign([[10.], [100.]])) - self.assertAllClose([[210.], [650.]], predictions.eval()) + self.assertAllClose([[210.], [650.]], self.evaluate(predictions)) def test_sparse_multi_rank(self): - wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) + wire_cast = fc._categorical_column_with_hash_bucket('wire_cast', 4) with ops.Graph().as_default(): wire_tensor = array_ops.sparse_placeholder(dtypes.string) wire_value = sparse_tensor.SparseTensorValue( @@ -1504,7 +1540,7 @@ class LinearModelTest(test.TestCase): predictions = fc.linear_model(features, [wire_cast]) wire_cast_var = get_linear_model_column_var(wire_cast) with _initialized_session() as sess: - self.assertAllClose(np.zeros((4, 1)), wire_cast_var.eval()) + self.assertAllClose(np.zeros((4, 1)), self.evaluate(wire_cast_var)) self.assertAllClose( np.zeros((2, 1)), predictions.eval(feed_dict={wire_tensor: wire_value})) @@ -1514,7 +1550,7 @@ class LinearModelTest(test.TestCase): predictions.eval(feed_dict={wire_tensor: wire_value})) def test_sparse_combiner(self): - wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) + wire_cast = fc._categorical_column_with_hash_bucket('wire_cast', 4) with ops.Graph().as_default(): wire_tensor = sparse_tensor.SparseTensor( values=['omar', 'stringer', 'marlo'], # hashed to = [2, 0, 3] @@ -1528,11 +1564,11 @@ class LinearModelTest(test.TestCase): with _initialized_session() as sess: sess.run(wire_cast_var.assign([[10.], [100.], [1000.], [10000.]])) sess.run(bias.assign([5.])) - self.assertAllClose([[1005.], [5010.]], predictions.eval()) + self.assertAllClose([[1005.], [5010.]], self.evaluate(predictions)) def test_sparse_combiner_with_negative_weights(self): - wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) - wire_cast_weights = fc.weighted_categorical_column(wire_cast, 'weights') + wire_cast = fc._categorical_column_with_hash_bucket('wire_cast', 4) + wire_cast_weights = fc._weighted_categorical_column(wire_cast, 'weights') with ops.Graph().as_default(): wire_tensor = sparse_tensor.SparseTensor( @@ -1550,25 +1586,25 @@ class LinearModelTest(test.TestCase): with _initialized_session() as sess: sess.run(wire_cast_var.assign([[10.], [100.], [1000.], [10000.]])) sess.run(bias.assign([5.])) - self.assertAllClose([[1005.], [-9985.]], predictions.eval()) + self.assertAllClose([[1005.], [-9985.]], self.evaluate(predictions)) def test_dense_multi_dimension_multi_output(self): - price = fc.numeric_column('price', shape=2) + price = fc._numeric_column('price', shape=2) with ops.Graph().as_default(): features = {'price': [[1., 2.], [5., 6.]]} predictions = fc.linear_model(features, [price], units=3) bias = get_linear_model_bias() price_var = get_linear_model_column_var(price) with _initialized_session() as sess: - self.assertAllClose(np.zeros((3,)), bias.eval()) - self.assertAllClose(np.zeros((2, 3)), price_var.eval()) + self.assertAllClose(np.zeros((3,)), self.evaluate(bias)) + self.assertAllClose(np.zeros((2, 3)), self.evaluate(price_var)) sess.run(price_var.assign([[1., 2., 3.], [10., 100., 1000.]])) sess.run(bias.assign([2., 3., 4.])) self.assertAllClose([[23., 205., 2007.], [67., 613., 6019.]], - predictions.eval()) + self.evaluate(predictions)) def test_raises_if_shape_mismatch(self): - price = fc.numeric_column('price', shape=2) + price = fc._numeric_column('price', shape=2) with ops.Graph().as_default(): features = {'price': [[1.], [5.]]} with self.assertRaisesRegexp( @@ -1577,22 +1613,22 @@ class LinearModelTest(test.TestCase): fc.linear_model(features, [price]) def test_dense_reshaping(self): - price = fc.numeric_column('price', shape=[1, 2]) + price = fc._numeric_column('price', shape=[1, 2]) with ops.Graph().as_default(): features = {'price': [[[1., 2.]], [[5., 6.]]]} predictions = fc.linear_model(features, [price]) bias = get_linear_model_bias() price_var = get_linear_model_column_var(price) with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) - self.assertAllClose([[0.], [0.]], price_var.eval()) - self.assertAllClose([[0.], [0.]], predictions.eval()) + self.assertAllClose([0.], self.evaluate(bias)) + self.assertAllClose([[0.], [0.]], self.evaluate(price_var)) + self.assertAllClose([[0.], [0.]], self.evaluate(predictions)) sess.run(price_var.assign([[10.], [100.]])) - self.assertAllClose([[210.], [650.]], predictions.eval()) + self.assertAllClose([[210.], [650.]], self.evaluate(predictions)) def test_dense_multi_column(self): - price1 = fc.numeric_column('price1', shape=2) - price2 = fc.numeric_column('price2') + price1 = fc._numeric_column('price1', shape=2) + price2 = fc._numeric_column('price2') with ops.Graph().as_default(): features = { 'price1': [[1., 2.], [5., 6.]], @@ -1603,18 +1639,18 @@ class LinearModelTest(test.TestCase): price1_var = get_linear_model_column_var(price1) price2_var = get_linear_model_column_var(price2) with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) - self.assertAllClose([[0.], [0.]], price1_var.eval()) - self.assertAllClose([[0.]], price2_var.eval()) - self.assertAllClose([[0.], [0.]], predictions.eval()) + self.assertAllClose([0.], self.evaluate(bias)) + self.assertAllClose([[0.], [0.]], self.evaluate(price1_var)) + self.assertAllClose([[0.]], self.evaluate(price2_var)) + self.assertAllClose([[0.], [0.]], self.evaluate(predictions)) sess.run(price1_var.assign([[10.], [100.]])) sess.run(price2_var.assign([[1000.]])) sess.run(bias.assign([7.])) - self.assertAllClose([[3217.], [4657.]], predictions.eval()) + self.assertAllClose([[3217.], [4657.]], self.evaluate(predictions)) def test_fills_cols_to_vars(self): - price1 = fc.numeric_column('price1', shape=2) - price2 = fc.numeric_column('price2') + price1 = fc._numeric_column('price1', shape=2) + price2 = fc._numeric_column('price2') with ops.Graph().as_default(): features = {'price1': [[1., 2.], [5., 6.]], 'price2': [[3.], [4.]]} cols_to_vars = {} @@ -1627,8 +1663,8 @@ class LinearModelTest(test.TestCase): self.assertAllEqual(cols_to_vars[price2], [price2_var]) def test_fills_cols_to_vars_partitioned_variables(self): - price1 = fc.numeric_column('price1', shape=2) - price2 = fc.numeric_column('price2', shape=3) + price1 = fc._numeric_column('price1', shape=2) + price2 = fc._numeric_column('price2', shape=3) with ops.Graph().as_default(): features = { 'price1': [[1., 2.], [6., 7.]], @@ -1653,13 +1689,13 @@ class LinearModelTest(test.TestCase): # Provide three _DenseColumn's to input_layer: a _NumericColumn, a # _BucketizedColumn, and an _EmbeddingColumn. Only the _EmbeddingColumn # creates a Variable. - apple_numeric_column = fc.numeric_column('apple_numeric_column') - banana_dense_feature = fc.numeric_column('banana_dense_feature') - banana_dense_feature_bucketized = fc.bucketized_column( + apple_numeric_column = fc._numeric_column('apple_numeric_column') + banana_dense_feature = fc._numeric_column('banana_dense_feature') + banana_dense_feature_bucketized = fc._bucketized_column( banana_dense_feature, boundaries=[0.]) - cherry_sparse_column = fc.categorical_column_with_hash_bucket( + cherry_sparse_column = fc._categorical_column_with_hash_bucket( 'cherry_sparse_feature', hash_bucket_size=5) - dragonfruit_embedding_column = fc.embedding_column( + dragonfruit_embedding_column = fc._embedding_column( cherry_sparse_column, dimension=10) with ops.Graph().as_default(): features = { @@ -1684,7 +1720,7 @@ class LinearModelTest(test.TestCase): self.assertItemsEqual(input_layer_inputs, output_tensors) def test_dense_collection(self): - price = fc.numeric_column('price') + price = fc._numeric_column('price') with ops.Graph().as_default() as g: features = {'price': [[1.], [5.]]} fc.linear_model(features, [price], weight_collections=['my-vars']) @@ -1695,7 +1731,7 @@ class LinearModelTest(test.TestCase): self.assertIn(price_var, my_vars) def test_sparse_collection(self): - wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) + wire_cast = fc._categorical_column_with_hash_bucket('wire_cast', 4) with ops.Graph().as_default() as g: wire_tensor = sparse_tensor.SparseTensor( values=['omar'], indices=[[0, 0]], dense_shape=[1, 1]) @@ -1709,7 +1745,7 @@ class LinearModelTest(test.TestCase): self.assertIn(wire_cast_var, my_vars) def test_dense_trainable_default(self): - price = fc.numeric_column('price') + price = fc._numeric_column('price') with ops.Graph().as_default() as g: features = {'price': [[1.], [5.]]} fc.linear_model(features, [price]) @@ -1720,7 +1756,7 @@ class LinearModelTest(test.TestCase): self.assertIn(price_var, trainable_vars) def test_sparse_trainable_default(self): - wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) + wire_cast = fc._categorical_column_with_hash_bucket('wire_cast', 4) with ops.Graph().as_default() as g: wire_tensor = sparse_tensor.SparseTensor( values=['omar'], indices=[[0, 0]], dense_shape=[1, 1]) @@ -1733,7 +1769,7 @@ class LinearModelTest(test.TestCase): self.assertIn(wire_cast_var, trainable_vars) def test_dense_trainable_false(self): - price = fc.numeric_column('price') + price = fc._numeric_column('price') with ops.Graph().as_default() as g: features = {'price': [[1.], [5.]]} fc.linear_model(features, [price], trainable=False) @@ -1741,7 +1777,7 @@ class LinearModelTest(test.TestCase): self.assertEqual([], trainable_vars) def test_sparse_trainable_false(self): - wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) + wire_cast = fc._categorical_column_with_hash_bucket('wire_cast', 4) with ops.Graph().as_default() as g: wire_tensor = sparse_tensor.SparseTensor( values=['omar'], indices=[[0, 0]], dense_shape=[1, 1]) @@ -1751,9 +1787,9 @@ class LinearModelTest(test.TestCase): self.assertEqual([], trainable_vars) def test_column_order(self): - price_a = fc.numeric_column('price_a') - price_b = fc.numeric_column('price_b') - wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) + price_a = fc._numeric_column('price_a') + price_b = fc._numeric_column('price_b') + wire_cast = fc._categorical_column_with_hash_bucket('wire_cast', 4) with ops.Graph().as_default() as g: features = { 'price_a': [[1.]], @@ -1787,8 +1823,8 @@ class LinearModelTest(test.TestCase): self.assertIn('wire_cast', my_vars[2].name) def test_static_batch_size_mismatch(self): - price1 = fc.numeric_column('price1') - price2 = fc.numeric_column('price2') + price1 = fc._numeric_column('price1') + price2 = fc._numeric_column('price2') with ops.Graph().as_default(): features = { 'price1': [[1.], [5.], [7.]], # batchsize = 3 @@ -1800,9 +1836,9 @@ class LinearModelTest(test.TestCase): fc.linear_model(features, [price1, price2]) def test_subset_of_static_batch_size_mismatch(self): - price1 = fc.numeric_column('price1') - price2 = fc.numeric_column('price2') - price3 = fc.numeric_column('price3') + price1 = fc._numeric_column('price1') + price2 = fc._numeric_column('price2') + price3 = fc._numeric_column('price3') with ops.Graph().as_default(): features = { 'price1': array_ops.placeholder(dtype=dtypes.int64), # batchsize = 3 @@ -1815,8 +1851,8 @@ class LinearModelTest(test.TestCase): fc.linear_model(features, [price1, price2, price3]) def test_runtime_batch_size_mismatch(self): - price1 = fc.numeric_column('price1') - price2 = fc.numeric_column('price2') + price1 = fc._numeric_column('price1') + price2 = fc._numeric_column('price2') with ops.Graph().as_default(): features = { 'price1': array_ops.placeholder(dtype=dtypes.int64), # batchsize = 3 @@ -1830,8 +1866,8 @@ class LinearModelTest(test.TestCase): predictions, feed_dict={features['price1']: [[1.], [5.], [7.]]}) def test_runtime_batch_size_matches(self): - price1 = fc.numeric_column('price1') - price2 = fc.numeric_column('price2') + price1 = fc._numeric_column('price1') + price2 = fc._numeric_column('price2') with ops.Graph().as_default(): features = { 'price1': array_ops.placeholder(dtype=dtypes.int64), # batchsize = 2 @@ -1846,10 +1882,16 @@ class LinearModelTest(test.TestCase): features['price2']: [[1.], [5.]], }) + @test_util.run_deprecated_v1 def test_with_1d_sparse_tensor(self): - price = fc.numeric_column('price') - price_buckets = fc.bucketized_column(price, boundaries=[0., 10., 100.,]) - body_style = fc.categorical_column_with_vocabulary_list( + price = fc._numeric_column('price') + price_buckets = fc._bucketized_column( + price, boundaries=[ + 0., + 10., + 100., + ]) + body_style = fc._categorical_column_with_vocabulary_list( 'body-style', vocabulary_list=['hardtop', 'wagon', 'sedan']) # Provides 1-dim tensor and dense tensor. @@ -1873,14 +1915,21 @@ class LinearModelTest(test.TestCase): sess.run(body_style_var.assign([[-10.], [-100.], [-1000.]])) sess.run(bias.assign([5.])) - self.assertAllClose([[10 - 1000 + 5.], [1000 - 10 + 5.]], sess.run(net)) + self.assertAllClose([[10 - 1000 + 5.], [1000 - 10 + 5.]], + self.evaluate(net)) + @test_util.run_deprecated_v1 def test_with_1d_unknown_shape_sparse_tensor(self): - price = fc.numeric_column('price') - price_buckets = fc.bucketized_column(price, boundaries=[0., 10., 100.,]) - body_style = fc.categorical_column_with_vocabulary_list( + price = fc._numeric_column('price') + price_buckets = fc._bucketized_column( + price, boundaries=[ + 0., + 10., + 100., + ]) + body_style = fc._categorical_column_with_vocabulary_list( 'body-style', vocabulary_list=['hardtop', 'wagon', 'sedan']) - country = fc.categorical_column_with_vocabulary_list( + country = fc._categorical_column_with_vocabulary_list( 'country', vocabulary_list=['US', 'JP', 'CA']) # Provides 1-dim tensor and dense tensor. @@ -1917,8 +1966,9 @@ class LinearModelTest(test.TestCase): features['country']: country_data })) + @test_util.run_deprecated_v1 def test_with_rank_0_feature(self): - price = fc.numeric_column('price') + price = fc._numeric_column('price') features = { 'price': constant_op.constant(0), } @@ -1939,7 +1989,7 @@ class LinearModelTest(test.TestCase): sess.run(net, feed_dict={features['price']: np.array(1)}) def test_multiple_linear_models(self): - price = fc.numeric_column('price') + price = fc._numeric_column('price') with ops.Graph().as_default(): features1 = {'price': [[1.], [5.]]} features2 = {'price': [[2.], [10.]]} @@ -1950,14 +2000,14 @@ class LinearModelTest(test.TestCase): price_var1 = get_linear_model_column_var(price, name='linear_model') price_var2 = get_linear_model_column_var(price, name='linear_model_1') with _initialized_session() as sess: - self.assertAllClose([0.], bias1.eval()) + self.assertAllClose([0.], self.evaluate(bias1)) sess.run(price_var1.assign([[10.]])) sess.run(bias1.assign([5.])) - self.assertAllClose([[15.], [55.]], predictions1.eval()) - self.assertAllClose([0.], bias2.eval()) + self.assertAllClose([[15.], [55.]], self.evaluate(predictions1)) + self.assertAllClose([0.], self.evaluate(bias2)) sess.run(price_var2.assign([[10.]])) sess.run(bias2.assign([5.])) - self.assertAllClose([[25.], [105.]], predictions2.eval()) + self.assertAllClose([[25.], [105.]], self.evaluate(predictions2)) class _LinearModelTest(test.TestCase): @@ -1996,31 +2046,31 @@ class _LinearModelTest(test.TestCase): with self.assertRaisesRegexp( ValueError, 'Expected feature_columns to be iterable, found dict.'): fc.linear_model( - features={'a': [[0]]}, feature_columns={'a': fc.numeric_column('a')}) + features={'a': [[0]]}, feature_columns={'a': fc._numeric_column('a')}) def test_raises_if_duplicate_name(self): with self.assertRaisesRegexp( ValueError, 'Duplicate feature column name found for columns'): get_keras_linear_model_predictions( features={'a': [[0]]}, - feature_columns=[fc.numeric_column('a'), - fc.numeric_column('a')]) + feature_columns=[fc._numeric_column('a'), + fc._numeric_column('a')]) def test_dense_bias(self): - price = fc.numeric_column('price') + price = fc._numeric_column('price') with ops.Graph().as_default(): features = {'price': [[1.], [5.]]} predictions = get_keras_linear_model_predictions(features, [price]) bias = get_linear_model_bias() price_var = get_linear_model_column_var(price) with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) + self.assertAllClose([0.], self.evaluate(bias)) sess.run(price_var.assign([[10.]])) sess.run(bias.assign([5.])) - self.assertAllClose([[15.], [55.]], predictions.eval()) + self.assertAllClose([[15.], [55.]], self.evaluate(predictions)) def test_sparse_bias(self): - wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) + wire_cast = fc._categorical_column_with_hash_bucket('wire_cast', 4) with ops.Graph().as_default(): wire_tensor = sparse_tensor.SparseTensor( values=['omar', 'stringer', 'marlo'], # hashed to = [2, 0, 3] @@ -2031,15 +2081,16 @@ class _LinearModelTest(test.TestCase): bias = get_linear_model_bias() wire_cast_var = get_linear_model_column_var(wire_cast) with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) - self.assertAllClose([[0.], [0.], [0.], [0.]], wire_cast_var.eval()) + self.assertAllClose([0.], self.evaluate(bias)) + self.assertAllClose([[0.], [0.], [0.], [0.]], + self.evaluate(wire_cast_var)) sess.run(wire_cast_var.assign([[10.], [100.], [1000.], [10000.]])) sess.run(bias.assign([5.])) - self.assertAllClose([[1005.], [10015.]], predictions.eval()) + self.assertAllClose([[1005.], [10015.]], self.evaluate(predictions)) def test_dense_and_sparse_bias(self): - wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) - price = fc.numeric_column('price') + wire_cast = fc._categorical_column_with_hash_bucket('wire_cast', 4) + price = fc._numeric_column('price') with ops.Graph().as_default(): wire_tensor = sparse_tensor.SparseTensor( values=['omar', 'stringer', 'marlo'], # hashed to = [2, 0, 3] @@ -2055,7 +2106,7 @@ class _LinearModelTest(test.TestCase): sess.run(wire_cast_var.assign([[10.], [100.], [1000.], [10000.]])) sess.run(bias.assign([5.])) sess.run(price_var.assign([[10.]])) - self.assertAllClose([[1015.], [10065.]], predictions.eval()) + self.assertAllClose([[1015.], [10065.]], self.evaluate(predictions)) def test_dense_and_sparse_column(self): """When the column is both dense and sparse, uses sparse tensors.""" @@ -2114,10 +2165,10 @@ class _LinearModelTest(test.TestCase): dense_and_sparse_column_var.assign([[10.], [100.], [1000.], [10000.]])) sess.run(bias.assign([5.])) - self.assertAllClose([[1005.], [10015.]], predictions.eval()) + self.assertAllClose([[1005.], [10015.]], self.evaluate(predictions)) def test_dense_multi_output(self): - price = fc.numeric_column('price') + price = fc._numeric_column('price') with ops.Graph().as_default(): features = {'price': [[1.], [5.]]} predictions = get_keras_linear_model_predictions( @@ -2125,15 +2176,15 @@ class _LinearModelTest(test.TestCase): bias = get_linear_model_bias() price_var = get_linear_model_column_var(price) with _initialized_session() as sess: - self.assertAllClose(np.zeros((3,)), bias.eval()) - self.assertAllClose(np.zeros((1, 3)), price_var.eval()) + self.assertAllClose(np.zeros((3,)), self.evaluate(bias)) + self.assertAllClose(np.zeros((1, 3)), self.evaluate(price_var)) sess.run(price_var.assign([[10., 100., 1000.]])) sess.run(bias.assign([5., 6., 7.])) self.assertAllClose([[15., 106., 1007.], [55., 506., 5007.]], - predictions.eval()) + self.evaluate(predictions)) def test_sparse_multi_output(self): - wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) + wire_cast = fc._categorical_column_with_hash_bucket('wire_cast', 4) with ops.Graph().as_default(): wire_tensor = sparse_tensor.SparseTensor( values=['omar', 'stringer', 'marlo'], # hashed to = [2, 0, 3] @@ -2145,29 +2196,29 @@ class _LinearModelTest(test.TestCase): bias = get_linear_model_bias() wire_cast_var = get_linear_model_column_var(wire_cast) with _initialized_session() as sess: - self.assertAllClose(np.zeros((3,)), bias.eval()) - self.assertAllClose(np.zeros((4, 3)), wire_cast_var.eval()) + self.assertAllClose(np.zeros((3,)), self.evaluate(bias)) + self.assertAllClose(np.zeros((4, 3)), self.evaluate(wire_cast_var)) sess.run( wire_cast_var.assign([[10., 11., 12.], [100., 110., 120.], [1000., 1100., 1200.], [10000., 11000., 12000.]])) sess.run(bias.assign([5., 6., 7.])) self.assertAllClose([[1005., 1106., 1207.], [10015., 11017., 12019.]], - predictions.eval()) + self.evaluate(predictions)) def test_dense_multi_dimension(self): - price = fc.numeric_column('price', shape=2) + price = fc._numeric_column('price', shape=2) with ops.Graph().as_default(): features = {'price': [[1., 2.], [5., 6.]]} predictions = get_keras_linear_model_predictions(features, [price]) price_var = get_linear_model_column_var(price) with _initialized_session() as sess: - self.assertAllClose([[0.], [0.]], price_var.eval()) + self.assertAllClose([[0.], [0.]], self.evaluate(price_var)) sess.run(price_var.assign([[10.], [100.]])) - self.assertAllClose([[210.], [650.]], predictions.eval()) + self.assertAllClose([[210.], [650.]], self.evaluate(predictions)) def test_sparse_multi_rank(self): - wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) + wire_cast = fc._categorical_column_with_hash_bucket('wire_cast', 4) with ops.Graph().as_default(): wire_tensor = array_ops.sparse_placeholder(dtypes.string) wire_value = sparse_tensor.SparseTensorValue( @@ -2178,7 +2229,7 @@ class _LinearModelTest(test.TestCase): predictions = get_keras_linear_model_predictions(features, [wire_cast]) wire_cast_var = get_linear_model_column_var(wire_cast) with _initialized_session() as sess: - self.assertAllClose(np.zeros((4, 1)), wire_cast_var.eval()) + self.assertAllClose(np.zeros((4, 1)), self.evaluate(wire_cast_var)) self.assertAllClose( np.zeros((2, 1)), predictions.eval(feed_dict={wire_tensor: wire_value})) @@ -2188,7 +2239,7 @@ class _LinearModelTest(test.TestCase): predictions.eval(feed_dict={wire_tensor: wire_value})) def test_sparse_combiner(self): - wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) + wire_cast = fc._categorical_column_with_hash_bucket('wire_cast', 4) with ops.Graph().as_default(): wire_tensor = sparse_tensor.SparseTensor( values=['omar', 'stringer', 'marlo'], # hashed to = [2, 0, 3] @@ -2202,10 +2253,10 @@ class _LinearModelTest(test.TestCase): with _initialized_session() as sess: sess.run(wire_cast_var.assign([[10.], [100.], [1000.], [10000.]])) sess.run(bias.assign([5.])) - self.assertAllClose([[1005.], [5010.]], predictions.eval()) + self.assertAllClose([[1005.], [5010.]], self.evaluate(predictions)) def test_dense_multi_dimension_multi_output(self): - price = fc.numeric_column('price', shape=2) + price = fc._numeric_column('price', shape=2) with ops.Graph().as_default(): features = {'price': [[1., 2.], [5., 6.]]} predictions = get_keras_linear_model_predictions( @@ -2213,15 +2264,15 @@ class _LinearModelTest(test.TestCase): bias = get_linear_model_bias() price_var = get_linear_model_column_var(price) with _initialized_session() as sess: - self.assertAllClose(np.zeros((3,)), bias.eval()) - self.assertAllClose(np.zeros((2, 3)), price_var.eval()) + self.assertAllClose(np.zeros((3,)), self.evaluate(bias)) + self.assertAllClose(np.zeros((2, 3)), self.evaluate(price_var)) sess.run(price_var.assign([[1., 2., 3.], [10., 100., 1000.]])) sess.run(bias.assign([2., 3., 4.])) self.assertAllClose([[23., 205., 2007.], [67., 613., 6019.]], - predictions.eval()) + self.evaluate(predictions)) def test_raises_if_shape_mismatch(self): - price = fc.numeric_column('price', shape=2) + price = fc._numeric_column('price', shape=2) with ops.Graph().as_default(): features = {'price': [[1.], [5.]]} with self.assertRaisesRegexp( @@ -2230,22 +2281,22 @@ class _LinearModelTest(test.TestCase): get_keras_linear_model_predictions(features, [price]) def test_dense_reshaping(self): - price = fc.numeric_column('price', shape=[1, 2]) + price = fc._numeric_column('price', shape=[1, 2]) with ops.Graph().as_default(): features = {'price': [[[1., 2.]], [[5., 6.]]]} predictions = get_keras_linear_model_predictions(features, [price]) bias = get_linear_model_bias() price_var = get_linear_model_column_var(price) with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) - self.assertAllClose([[0.], [0.]], price_var.eval()) - self.assertAllClose([[0.], [0.]], predictions.eval()) + self.assertAllClose([0.], self.evaluate(bias)) + self.assertAllClose([[0.], [0.]], self.evaluate(price_var)) + self.assertAllClose([[0.], [0.]], self.evaluate(predictions)) sess.run(price_var.assign([[10.], [100.]])) - self.assertAllClose([[210.], [650.]], predictions.eval()) + self.assertAllClose([[210.], [650.]], self.evaluate(predictions)) def test_dense_multi_column(self): - price1 = fc.numeric_column('price1', shape=2) - price2 = fc.numeric_column('price2') + price1 = fc._numeric_column('price1', shape=2) + price2 = fc._numeric_column('price2') with ops.Graph().as_default(): features = {'price1': [[1., 2.], [5., 6.]], 'price2': [[3.], [4.]]} predictions = get_keras_linear_model_predictions(features, @@ -2254,18 +2305,18 @@ class _LinearModelTest(test.TestCase): price1_var = get_linear_model_column_var(price1) price2_var = get_linear_model_column_var(price2) with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) - self.assertAllClose([[0.], [0.]], price1_var.eval()) - self.assertAllClose([[0.]], price2_var.eval()) - self.assertAllClose([[0.], [0.]], predictions.eval()) + self.assertAllClose([0.], self.evaluate(bias)) + self.assertAllClose([[0.], [0.]], self.evaluate(price1_var)) + self.assertAllClose([[0.]], self.evaluate(price2_var)) + self.assertAllClose([[0.], [0.]], self.evaluate(predictions)) sess.run(price1_var.assign([[10.], [100.]])) sess.run(price2_var.assign([[1000.]])) sess.run(bias.assign([7.])) - self.assertAllClose([[3217.], [4657.]], predictions.eval()) + self.assertAllClose([[3217.], [4657.]], self.evaluate(predictions)) def test_fills_cols_to_vars(self): - price1 = fc.numeric_column('price1', shape=2) - price2 = fc.numeric_column('price2') + price1 = fc._numeric_column('price1', shape=2) + price2 = fc._numeric_column('price2') with ops.Graph().as_default(): features = {'price1': [[1., 2.], [5., 6.]], 'price2': [[3.], [4.]]} cols_to_vars = {} @@ -2279,8 +2330,8 @@ class _LinearModelTest(test.TestCase): self.assertAllEqual(cols_to_vars[price2], [price2_var]) def test_fills_cols_to_vars_partitioned_variables(self): - price1 = fc.numeric_column('price1', shape=2) - price2 = fc.numeric_column('price2', shape=3) + price1 = fc._numeric_column('price1', shape=2) + price2 = fc._numeric_column('price2', shape=3) with ops.Graph().as_default(): features = { 'price1': [[1., 2.], [6., 7.]], @@ -2303,7 +2354,7 @@ class _LinearModelTest(test.TestCase): self.assertAllEqual([[0.]], cols_to_vars[price2][1].eval()) def test_dense_collection(self): - price = fc.numeric_column('price') + price = fc._numeric_column('price') with ops.Graph().as_default() as g: features = {'price': [[1.], [5.]]} get_keras_linear_model_predictions( @@ -2315,7 +2366,7 @@ class _LinearModelTest(test.TestCase): self.assertIn(price_var, my_vars) def test_sparse_collection(self): - wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) + wire_cast = fc._categorical_column_with_hash_bucket('wire_cast', 4) with ops.Graph().as_default() as g: wire_tensor = sparse_tensor.SparseTensor( values=['omar'], indices=[[0, 0]], dense_shape=[1, 1]) @@ -2329,7 +2380,7 @@ class _LinearModelTest(test.TestCase): self.assertIn(wire_cast_var, my_vars) def test_dense_trainable_default(self): - price = fc.numeric_column('price') + price = fc._numeric_column('price') with ops.Graph().as_default() as g: features = {'price': [[1.], [5.]]} get_keras_linear_model_predictions(features, [price]) @@ -2340,7 +2391,7 @@ class _LinearModelTest(test.TestCase): self.assertIn(price_var, trainable_vars) def test_sparse_trainable_default(self): - wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) + wire_cast = fc._categorical_column_with_hash_bucket('wire_cast', 4) with ops.Graph().as_default() as g: wire_tensor = sparse_tensor.SparseTensor( values=['omar'], indices=[[0, 0]], dense_shape=[1, 1]) @@ -2353,7 +2404,7 @@ class _LinearModelTest(test.TestCase): self.assertIn(wire_cast_var, trainable_vars) def test_dense_trainable_false(self): - price = fc.numeric_column('price') + price = fc._numeric_column('price') with ops.Graph().as_default() as g: features = {'price': [[1.], [5.]]} get_keras_linear_model_predictions(features, [price], trainable=False) @@ -2361,7 +2412,7 @@ class _LinearModelTest(test.TestCase): self.assertEqual([], trainable_vars) def test_sparse_trainable_false(self): - wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) + wire_cast = fc._categorical_column_with_hash_bucket('wire_cast', 4) with ops.Graph().as_default() as g: wire_tensor = sparse_tensor.SparseTensor( values=['omar'], indices=[[0, 0]], dense_shape=[1, 1]) @@ -2371,9 +2422,9 @@ class _LinearModelTest(test.TestCase): self.assertEqual([], trainable_vars) def test_column_order(self): - price_a = fc.numeric_column('price_a') - price_b = fc.numeric_column('price_b') - wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) + price_a = fc._numeric_column('price_a') + price_b = fc._numeric_column('price_b') + wire_cast = fc._categorical_column_with_hash_bucket('wire_cast', 4) with ops.Graph().as_default() as g: features = { 'price_a': [[1.]], @@ -2407,8 +2458,8 @@ class _LinearModelTest(test.TestCase): self.assertIn('wire_cast', my_vars[2].name) def test_static_batch_size_mismatch(self): - price1 = fc.numeric_column('price1') - price2 = fc.numeric_column('price2') + price1 = fc._numeric_column('price1') + price2 = fc._numeric_column('price2') with ops.Graph().as_default(): features = { 'price1': [[1.], [5.], [7.]], # batchsize = 3 @@ -2420,9 +2471,9 @@ class _LinearModelTest(test.TestCase): get_keras_linear_model_predictions(features, [price1, price2]) def test_subset_of_static_batch_size_mismatch(self): - price1 = fc.numeric_column('price1') - price2 = fc.numeric_column('price2') - price3 = fc.numeric_column('price3') + price1 = fc._numeric_column('price1') + price2 = fc._numeric_column('price2') + price3 = fc._numeric_column('price3') with ops.Graph().as_default(): features = { 'price1': array_ops.placeholder(dtype=dtypes.int64), # batchsize = 3 @@ -2435,8 +2486,8 @@ class _LinearModelTest(test.TestCase): get_keras_linear_model_predictions(features, [price1, price2, price3]) def test_runtime_batch_size_mismatch(self): - price1 = fc.numeric_column('price1') - price2 = fc.numeric_column('price2') + price1 = fc._numeric_column('price1') + price2 = fc._numeric_column('price2') with ops.Graph().as_default(): features = { 'price1': array_ops.placeholder(dtype=dtypes.int64), # batchsize = 3 @@ -2451,8 +2502,8 @@ class _LinearModelTest(test.TestCase): predictions, feed_dict={features['price1']: [[1.], [5.], [7.]]}) def test_runtime_batch_size_matches(self): - price1 = fc.numeric_column('price1') - price2 = fc.numeric_column('price2') + price1 = fc._numeric_column('price1') + price2 = fc._numeric_column('price2') with ops.Graph().as_default(): features = { 'price1': array_ops.placeholder(dtype=dtypes.int64), # batchsize = 2 @@ -2468,15 +2519,16 @@ class _LinearModelTest(test.TestCase): features['price2']: [[1.], [5.]], }) + @test_util.run_deprecated_v1 def test_with_1d_sparse_tensor(self): - price = fc.numeric_column('price') - price_buckets = fc.bucketized_column( + price = fc._numeric_column('price') + price_buckets = fc._bucketized_column( price, boundaries=[ 0., 10., 100., ]) - body_style = fc.categorical_column_with_vocabulary_list( + body_style = fc._categorical_column_with_vocabulary_list( 'body-style', vocabulary_list=['hardtop', 'wagon', 'sedan']) # Provides 1-dim tensor and dense tensor. @@ -2506,19 +2558,21 @@ class _LinearModelTest(test.TestCase): sess.run(body_style_var.assign([[-10.], [-100.], [-1000.]])) sess.run(bias.assign([5.])) - self.assertAllClose([[10 - 1000 + 5.], [1000 - 10 + 5.]], sess.run(net)) + self.assertAllClose([[10 - 1000 + 5.], [1000 - 10 + 5.]], + self.evaluate(net)) + @test_util.run_deprecated_v1 def test_with_1d_unknown_shape_sparse_tensor(self): - price = fc.numeric_column('price') - price_buckets = fc.bucketized_column( + price = fc._numeric_column('price') + price_buckets = fc._bucketized_column( price, boundaries=[ 0., 10., 100., ]) - body_style = fc.categorical_column_with_vocabulary_list( + body_style = fc._categorical_column_with_vocabulary_list( 'body-style', vocabulary_list=['hardtop', 'wagon', 'sedan']) - country = fc.categorical_column_with_vocabulary_list( + country = fc._categorical_column_with_vocabulary_list( 'country', vocabulary_list=['US', 'JP', 'CA']) # Provides 1-dim tensor and dense tensor. @@ -2554,8 +2608,9 @@ class _LinearModelTest(test.TestCase): features['country']: country_data })) + @test_util.run_deprecated_v1 def test_with_rank_0_feature(self): - price = fc.numeric_column('price') + price = fc._numeric_column('price') features = { 'price': constant_op.constant(0), } @@ -2581,7 +2636,7 @@ class InputLayerTest(test.TestCase): @test_util.run_in_graph_and_eager_modes def test_retrieving_input(self): features = {'a': [0.]} - input_layer = InputLayer(fc.numeric_column('a')) + input_layer = InputLayer(fc._numeric_column('a')) inputs = self.evaluate(input_layer(features)) self.assertAllClose([[0.]], inputs) @@ -2593,8 +2648,8 @@ class InputLayerTest(test.TestCase): dense_shape=(3, 3)) # Create feature columns (categorical and embedding). - categorical_column = fc.categorical_column_with_identity(key='a', - num_buckets=3) + categorical_column = fc._categorical_column_with_identity( + key='a', num_buckets=3) embedding_dimension = 2 def _embedding_column_initializer(shape, dtype, partition_info): del shape # unused @@ -2605,7 +2660,8 @@ class InputLayerTest(test.TestCase): (0, 1), # id 1 (1, 1)) # id 2 return embedding_values - embedding_column = fc.embedding_column( + + embedding_column = fc._embedding_column( categorical_column, dimension=embedding_dimension, initializer=_embedding_column_initializer) @@ -2636,8 +2692,8 @@ class InputLayerTest(test.TestCase): dense_shape=(3, 3)) # Create feature columns (categorical and embedding). - categorical_column = fc.categorical_column_with_identity(key='a', - num_buckets=3) + categorical_column = fc._categorical_column_with_identity( + key='a', num_buckets=3) embedding_dimension = 2 def _embedding_column_initializer(shape, dtype, partition_info): @@ -2650,7 +2706,7 @@ class InputLayerTest(test.TestCase): (1, 1)) # id 2 return embedding_values - embedding_column = fc.embedding_column( + embedding_column = fc._embedding_column( categorical_column, dimension=embedding_dimension, initializer=_embedding_column_initializer) @@ -2687,56 +2743,56 @@ class FunctionalInputLayerTest(test.TestCase): fc.input_layer( features={'a': [[0]]}, feature_columns=[ - fc.categorical_column_with_hash_bucket('wire_cast', 4) + fc._categorical_column_with_hash_bucket('wire_cast', 4) ]) def test_does_not_support_dict_columns(self): with self.assertRaisesRegexp( ValueError, 'Expected feature_columns to be iterable, found dict.'): fc.input_layer( - features={'a': [[0]]}, feature_columns={'a': fc.numeric_column('a')}) + features={'a': [[0]]}, feature_columns={'a': fc._numeric_column('a')}) def test_bare_column(self): with ops.Graph().as_default(): features = features = {'a': [0.]} - net = fc.input_layer(features, fc.numeric_column('a')) + net = fc.input_layer(features, fc._numeric_column('a')) with _initialized_session(): - self.assertAllClose([[0.]], net.eval()) + self.assertAllClose([[0.]], self.evaluate(net)) def test_column_generator(self): with ops.Graph().as_default(): features = features = {'a': [0.], 'b': [1.]} - columns = (fc.numeric_column(key) for key in features) + columns = (fc._numeric_column(key) for key in features) net = fc.input_layer(features, columns) with _initialized_session(): - self.assertAllClose([[0., 1.]], net.eval()) + self.assertAllClose([[0., 1.]], self.evaluate(net)) def test_raises_if_duplicate_name(self): with self.assertRaisesRegexp( ValueError, 'Duplicate feature column name found for columns'): fc.input_layer( features={'a': [[0]]}, - feature_columns=[fc.numeric_column('a'), - fc.numeric_column('a')]) + feature_columns=[fc._numeric_column('a'), + fc._numeric_column('a')]) def test_one_column(self): - price = fc.numeric_column('price') + price = fc._numeric_column('price') with ops.Graph().as_default(): features = {'price': [[1.], [5.]]} net = fc.input_layer(features, [price]) with _initialized_session(): - self.assertAllClose([[1.], [5.]], net.eval()) + self.assertAllClose([[1.], [5.]], self.evaluate(net)) def test_multi_dimension(self): - price = fc.numeric_column('price', shape=2) + price = fc._numeric_column('price', shape=2) with ops.Graph().as_default(): features = {'price': [[1., 2.], [5., 6.]]} net = fc.input_layer(features, [price]) with _initialized_session(): - self.assertAllClose([[1., 2.], [5., 6.]], net.eval()) + self.assertAllClose([[1., 2.], [5., 6.]], self.evaluate(net)) def test_raises_if_shape_mismatch(self): - price = fc.numeric_column('price', shape=2) + price = fc._numeric_column('price', shape=2) with ops.Graph().as_default(): features = {'price': [[1.], [5.]]} with self.assertRaisesRegexp( @@ -2745,16 +2801,16 @@ class FunctionalInputLayerTest(test.TestCase): fc.input_layer(features, [price]) def test_reshaping(self): - price = fc.numeric_column('price', shape=[1, 2]) + price = fc._numeric_column('price', shape=[1, 2]) with ops.Graph().as_default(): features = {'price': [[[1., 2.]], [[5., 6.]]]} net = fc.input_layer(features, [price]) with _initialized_session(): - self.assertAllClose([[1., 2.], [5., 6.]], net.eval()) + self.assertAllClose([[1., 2.], [5., 6.]], self.evaluate(net)) def test_multi_column(self): - price1 = fc.numeric_column('price1', shape=2) - price2 = fc.numeric_column('price2') + price1 = fc._numeric_column('price1', shape=2) + price2 = fc._numeric_column('price2') with ops.Graph().as_default(): features = { 'price1': [[1., 2.], [5., 6.]], @@ -2762,19 +2818,19 @@ class FunctionalInputLayerTest(test.TestCase): } net = fc.input_layer(features, [price1, price2]) with _initialized_session(): - self.assertAllClose([[1., 2., 3.], [5., 6., 4.]], net.eval()) + self.assertAllClose([[1., 2., 3.], [5., 6., 4.]], self.evaluate(net)) def test_fills_cols_to_vars(self): # Provide three _DenseColumn's to input_layer: a _NumericColumn, a # _BucketizedColumn, and an _EmbeddingColumn. Only the _EmbeddingColumn # creates a Variable. - price1 = fc.numeric_column('price1') - dense_feature = fc.numeric_column('dense_feature') - dense_feature_bucketized = fc.bucketized_column( + price1 = fc._numeric_column('price1') + dense_feature = fc._numeric_column('dense_feature') + dense_feature_bucketized = fc._bucketized_column( dense_feature, boundaries=[0.]) - some_sparse_column = fc.categorical_column_with_hash_bucket( + some_sparse_column = fc._categorical_column_with_hash_bucket( 'sparse_feature', hash_bucket_size=5) - some_embedding_column = fc.embedding_column( + some_embedding_column = fc._embedding_column( some_sparse_column, dimension=10) with ops.Graph().as_default(): features = { @@ -2793,24 +2849,25 @@ class FunctionalInputLayerTest(test.TestCase): variables_lib.Variable) self.assertAllEqual(cols_to_vars[some_embedding_column][0].shape, [5, 10]) + @test_util.run_deprecated_v1 def test_fills_cols_to_vars_shared_embedding(self): # Provide 5 DenseColumn's to input_layer: a NumericColumn, a # BucketizedColumn, an EmbeddingColumn, two SharedEmbeddingColumns. The # EmbeddingColumn creates a Variable and the two SharedEmbeddingColumns # shared one variable. - price1 = fc.numeric_column('price1') - dense_feature = fc.numeric_column('dense_feature') - dense_feature_bucketized = fc.bucketized_column( + price1 = fc._numeric_column('price1') + dense_feature = fc._numeric_column('dense_feature') + dense_feature_bucketized = fc._bucketized_column( dense_feature, boundaries=[0.]) - some_sparse_column = fc.categorical_column_with_hash_bucket( + some_sparse_column = fc._categorical_column_with_hash_bucket( 'sparse_feature', hash_bucket_size=5) - some_embedding_column = fc.embedding_column( + some_embedding_column = fc._embedding_column( some_sparse_column, dimension=10) - categorical_column_a = fc.categorical_column_with_identity( + categorical_column_a = fc._categorical_column_with_identity( key='aaa', num_buckets=3) - categorical_column_b = fc.categorical_column_with_identity( + categorical_column_b = fc._categorical_column_with_identity( key='bbb', num_buckets=3) - shared_embedding_a, shared_embedding_b = fc.shared_embedding_columns( + shared_embedding_a, shared_embedding_b = fc_new.shared_embedding_columns( [categorical_column_a, categorical_column_b], dimension=2) with ops.Graph().as_default(): features = { @@ -2850,13 +2907,13 @@ class FunctionalInputLayerTest(test.TestCase): self.assertAllEqual(cols_to_vars[shared_embedding_a][0].shape, [3, 2]) def test_fills_cols_to_vars_partitioned_variables(self): - price1 = fc.numeric_column('price1') - dense_feature = fc.numeric_column('dense_feature') - dense_feature_bucketized = fc.bucketized_column( + price1 = fc._numeric_column('price1') + dense_feature = fc._numeric_column('dense_feature') + dense_feature_bucketized = fc._bucketized_column( dense_feature, boundaries=[0.]) - some_sparse_column = fc.categorical_column_with_hash_bucket( + some_sparse_column = fc._categorical_column_with_hash_bucket( 'sparse_feature', hash_bucket_size=5) - some_embedding_column = fc.embedding_column( + some_embedding_column = fc._embedding_column( some_sparse_column, dimension=10) with ops.Graph().as_default(): features = { @@ -2883,8 +2940,8 @@ class FunctionalInputLayerTest(test.TestCase): self.assertAllEqual(cols_to_vars[some_embedding_column][2].shape, [1, 10]) def test_column_order(self): - price_a = fc.numeric_column('price_a') - price_b = fc.numeric_column('price_b') + price_a = fc._numeric_column('price_a') + price_b = fc._numeric_column('price_b') with ops.Graph().as_default(): features = { 'price_a': [[1.]], @@ -2893,11 +2950,11 @@ class FunctionalInputLayerTest(test.TestCase): net1 = fc.input_layer(features, [price_a, price_b]) net2 = fc.input_layer(features, [price_b, price_a]) with _initialized_session(): - self.assertAllClose([[1., 3.]], net1.eval()) - self.assertAllClose([[1., 3.]], net2.eval()) + self.assertAllClose([[1., 3.]], self.evaluate(net1)) + self.assertAllClose([[1., 3.]], self.evaluate(net2)) def test_fails_for_categorical_column(self): - animal = fc.categorical_column_with_identity('animal', num_buckets=4) + animal = fc._categorical_column_with_identity('animal', num_buckets=4) with ops.Graph().as_default(): features = { 'animal': @@ -2908,8 +2965,8 @@ class FunctionalInputLayerTest(test.TestCase): fc.input_layer(features, [animal]) def test_static_batch_size_mismatch(self): - price1 = fc.numeric_column('price1') - price2 = fc.numeric_column('price2') + price1 = fc._numeric_column('price1') + price2 = fc._numeric_column('price2') with ops.Graph().as_default(): features = { 'price1': [[1.], [5.], [7.]], # batchsize = 3 @@ -2921,9 +2978,9 @@ class FunctionalInputLayerTest(test.TestCase): fc.input_layer(features, [price1, price2]) def test_subset_of_static_batch_size_mismatch(self): - price1 = fc.numeric_column('price1') - price2 = fc.numeric_column('price2') - price3 = fc.numeric_column('price3') + price1 = fc._numeric_column('price1') + price2 = fc._numeric_column('price2') + price3 = fc._numeric_column('price3') with ops.Graph().as_default(): features = { 'price1': array_ops.placeholder(dtype=dtypes.int64), # batchsize = 3 @@ -2936,8 +2993,8 @@ class FunctionalInputLayerTest(test.TestCase): fc.input_layer(features, [price1, price2, price3]) def test_runtime_batch_size_mismatch(self): - price1 = fc.numeric_column('price1') - price2 = fc.numeric_column('price2') + price1 = fc._numeric_column('price1') + price2 = fc._numeric_column('price2') with ops.Graph().as_default(): features = { 'price1': array_ops.placeholder(dtype=dtypes.int64), # batchsize = 3 @@ -2950,8 +3007,8 @@ class FunctionalInputLayerTest(test.TestCase): sess.run(net, feed_dict={features['price1']: [[1.], [5.], [7.]]}) def test_runtime_batch_size_matches(self): - price1 = fc.numeric_column('price1') - price2 = fc.numeric_column('price2') + price1 = fc._numeric_column('price1') + price2 = fc._numeric_column('price2') with ops.Graph().as_default(): features = { 'price1': array_ops.placeholder(dtype=dtypes.int64), # batchsize = 2 @@ -2967,9 +3024,9 @@ class FunctionalInputLayerTest(test.TestCase): }) def test_multiple_layers_with_same_embedding_column(self): - some_sparse_column = fc.categorical_column_with_hash_bucket( + some_sparse_column = fc._categorical_column_with_hash_bucket( 'sparse_feature', hash_bucket_size=5) - some_embedding_column = fc.embedding_column( + some_embedding_column = fc._embedding_column( some_sparse_column, dimension=10) with ops.Graph().as_default(): @@ -2990,13 +3047,14 @@ class FunctionalInputLayerTest(test.TestCase): expected_var_names, [v.name for v in ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES)]) + @test_util.run_deprecated_v1 def test_multiple_layers_with_same_shared_embedding_column(self): - categorical_column_a = fc.categorical_column_with_identity( + categorical_column_a = fc._categorical_column_with_identity( key='aaa', num_buckets=3) - categorical_column_b = fc.categorical_column_with_identity( + categorical_column_b = fc._categorical_column_with_identity( key='bbb', num_buckets=3) embedding_dimension = 2 - embedding_column_b, embedding_column_a = fc.shared_embedding_columns( + embedding_column_b, embedding_column_a = fc_new.shared_embedding_columns( [categorical_column_b, categorical_column_a], dimension=embedding_dimension) @@ -3023,13 +3081,14 @@ class FunctionalInputLayerTest(test.TestCase): ['input_layer/aaa_bbb_shared_embedding/embedding_weights:0'], [v.name for v in ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES)]) + @test_util.run_deprecated_v1 def test_multiple_layers_with_same_shared_embedding_column_diff_graphs(self): - categorical_column_a = fc.categorical_column_with_identity( + categorical_column_a = fc._categorical_column_with_identity( key='aaa', num_buckets=3) - categorical_column_b = fc.categorical_column_with_identity( + categorical_column_b = fc._categorical_column_with_identity( key='bbb', num_buckets=3) embedding_dimension = 2 - embedding_column_b, embedding_column_a = fc.shared_embedding_columns( + embedding_column_b, embedding_column_a = fc_new.shared_embedding_columns( [categorical_column_b, categorical_column_a], dimension=embedding_dimension) all_cols = [embedding_column_a, embedding_column_b] @@ -3074,6 +3133,7 @@ class FunctionalInputLayerTest(test.TestCase): ['input_layer/aaa_bbb_shared_embedding/embedding_weights:0'], [v.name for v in ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES)]) + @test_util.run_deprecated_v1 def test_with_1d_sparse_tensor(self): embedding_values = ( (1., 2., 3., 4., 5.), # id 0 @@ -3085,18 +3145,18 @@ class FunctionalInputLayerTest(test.TestCase): return embedding_values # price has 1 dimension in input_layer - price = fc.numeric_column('price') + price = fc._numeric_column('price') # one_hot_body_style has 3 dims in input_layer. - body_style = fc.categorical_column_with_vocabulary_list( + body_style = fc._categorical_column_with_vocabulary_list( 'body-style', vocabulary_list=['hardtop', 'wagon', 'sedan']) - one_hot_body_style = fc.indicator_column(body_style) + one_hot_body_style = fc._indicator_column(body_style) # embedded_body_style has 5 dims in input_layer. - country = fc.categorical_column_with_vocabulary_list( + country = fc._categorical_column_with_vocabulary_list( 'country', vocabulary_list=['US', 'JP', 'CA']) - embedded_country = fc.embedding_column(country, dimension=5, - initializer=_initializer) + embedded_country = fc._embedding_column( + country, dimension=5, initializer=_initializer) # Provides 1-dim tensor and dense tensor. features = { @@ -3124,6 +3184,7 @@ class FunctionalInputLayerTest(test.TestCase): [1., 0., 0., 1., 2., 3., 4., 5., 12.]], sess.run(net)) + @test_util.run_deprecated_v1 def test_with_1d_unknown_shape_sparse_tensor(self): embedding_values = ( (1., 2.), # id 0 @@ -3135,17 +3196,17 @@ class FunctionalInputLayerTest(test.TestCase): return embedding_values # price has 1 dimension in input_layer - price = fc.numeric_column('price') + price = fc._numeric_column('price') # one_hot_body_style has 3 dims in input_layer. - body_style = fc.categorical_column_with_vocabulary_list( + body_style = fc._categorical_column_with_vocabulary_list( 'body-style', vocabulary_list=['hardtop', 'wagon', 'sedan']) - one_hot_body_style = fc.indicator_column(body_style) + one_hot_body_style = fc._indicator_column(body_style) # embedded_body_style has 5 dims in input_layer. - country = fc.categorical_column_with_vocabulary_list( + country = fc._categorical_column_with_vocabulary_list( 'country', vocabulary_list=['US', 'JP', 'CA']) - embedded_country = fc.embedding_column( + embedded_country = fc._embedding_column( country, dimension=2, initializer=_initializer) # Provides 1-dim tensor and dense tensor. @@ -3183,9 +3244,10 @@ class FunctionalInputLayerTest(test.TestCase): features['country']: country_data })) + @test_util.run_deprecated_v1 def test_with_rank_0_feature(self): # price has 1 dimension in input_layer - price = fc.numeric_column('price') + price = fc._numeric_column('price') features = { 'price': constant_op.constant(0), } @@ -3313,8 +3375,9 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): 'python/feature_column/testdata/wire_vocabulary.txt') self._wire_vocabulary_size = 3 + @test_util.run_deprecated_v1 def test_defaults(self): - column = fc.categorical_column_with_vocabulary_file( + column = fc._categorical_column_with_vocabulary_file( key='aaa', vocabulary_file='path_to_file', vocabulary_size=3) self.assertEqual('aaa', column.name) self.assertEqual('aaa', column._var_scope_name) @@ -3326,22 +3389,30 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): def test_key_should_be_string(self): with self.assertRaisesRegexp(ValueError, 'key must be a string.'): - fc.categorical_column_with_vocabulary_file( + fc._categorical_column_with_vocabulary_file( key=('aaa',), vocabulary_file='path_to_file', vocabulary_size=3) + @test_util.run_deprecated_v1 def test_all_constructor_args(self): - column = fc.categorical_column_with_vocabulary_file( - key='aaa', vocabulary_file='path_to_file', vocabulary_size=3, - num_oov_buckets=4, dtype=dtypes.int32) + column = fc._categorical_column_with_vocabulary_file( + key='aaa', + vocabulary_file='path_to_file', + vocabulary_size=3, + num_oov_buckets=4, + dtype=dtypes.int32) self.assertEqual(7, column._num_buckets) self.assertEqual({ 'aaa': parsing_ops.VarLenFeature(dtypes.int32) }, column._parse_example_spec) + @test_util.run_deprecated_v1 def test_deep_copy(self): - original = fc.categorical_column_with_vocabulary_file( - key='aaa', vocabulary_file='path_to_file', vocabulary_size=3, - num_oov_buckets=4, dtype=dtypes.int32) + original = fc._categorical_column_with_vocabulary_file( + key='aaa', + vocabulary_file='path_to_file', + vocabulary_size=3, + num_oov_buckets=4, + dtype=dtypes.int32) for column in (original, copy.deepcopy(original)): self.assertEqual('aaa', column.name) self.assertEqual(7, column._num_buckets) @@ -3351,16 +3422,17 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): def test_vocabulary_file_none(self): with self.assertRaisesRegexp(ValueError, 'Missing vocabulary_file'): - fc.categorical_column_with_vocabulary_file( + fc._categorical_column_with_vocabulary_file( key='aaa', vocabulary_file=None, vocabulary_size=3) def test_vocabulary_file_empty_string(self): with self.assertRaisesRegexp(ValueError, 'Missing vocabulary_file'): - fc.categorical_column_with_vocabulary_file( + fc._categorical_column_with_vocabulary_file( key='aaa', vocabulary_file='', vocabulary_size=3) + @test_util.run_deprecated_v1 def test_invalid_vocabulary_file(self): - column = fc.categorical_column_with_vocabulary_file( + column = fc._categorical_column_with_vocabulary_file( key='aaa', vocabulary_file='file_does_not_exist', vocabulary_size=10) inputs = sparse_tensor.SparseTensorValue( indices=((0, 0), (1, 0), (1, 1)), @@ -3373,16 +3445,19 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): def test_invalid_vocabulary_size(self): with self.assertRaisesRegexp(ValueError, 'Invalid vocabulary_size'): - fc.categorical_column_with_vocabulary_file( - key='aaa', vocabulary_file=self._wire_vocabulary_file_name, + fc._categorical_column_with_vocabulary_file( + key='aaa', + vocabulary_file=self._wire_vocabulary_file_name, vocabulary_size=-1) with self.assertRaisesRegexp(ValueError, 'Invalid vocabulary_size'): - fc.categorical_column_with_vocabulary_file( - key='aaa', vocabulary_file=self._wire_vocabulary_file_name, + fc._categorical_column_with_vocabulary_file( + key='aaa', + vocabulary_file=self._wire_vocabulary_file_name, vocabulary_size=0) + @test_util.run_deprecated_v1 def test_too_large_vocabulary_size(self): - column = fc.categorical_column_with_vocabulary_file( + column = fc._categorical_column_with_vocabulary_file( key='aaa', vocabulary_file=self._wire_vocabulary_file_name, vocabulary_size=self._wire_vocabulary_size + 1) @@ -3397,20 +3472,24 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): def test_invalid_num_oov_buckets(self): with self.assertRaisesRegexp(ValueError, 'Invalid num_oov_buckets'): - fc.categorical_column_with_vocabulary_file( - key='aaa', vocabulary_file='path', vocabulary_size=3, + fc._categorical_column_with_vocabulary_file( + key='aaa', + vocabulary_file='path', + vocabulary_size=3, num_oov_buckets=-1) def test_invalid_dtype(self): with self.assertRaisesRegexp(ValueError, 'dtype must be string or integer'): - fc.categorical_column_with_vocabulary_file( - key='aaa', vocabulary_file='path', vocabulary_size=3, + fc._categorical_column_with_vocabulary_file( + key='aaa', + vocabulary_file='path', + vocabulary_size=3, dtype=dtypes.float64) def test_invalid_buckets_and_default_value(self): with self.assertRaisesRegexp( ValueError, 'both num_oov_buckets and default_value'): - fc.categorical_column_with_vocabulary_file( + fc._categorical_column_with_vocabulary_file( key='aaa', vocabulary_file=self._wire_vocabulary_file_name, vocabulary_size=self._wire_vocabulary_size, @@ -3418,7 +3497,7 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): default_value=2) def test_invalid_input_dtype_int32(self): - column = fc.categorical_column_with_vocabulary_file( + column = fc._categorical_column_with_vocabulary_file( key='aaa', vocabulary_file=self._wire_vocabulary_file_name, vocabulary_size=self._wire_vocabulary_size, @@ -3431,7 +3510,7 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): column._get_sparse_tensors(_LazyBuilder({'aaa': inputs})) def test_invalid_input_dtype_string(self): - column = fc.categorical_column_with_vocabulary_file( + column = fc._categorical_column_with_vocabulary_file( key='aaa', vocabulary_file=self._warriors_vocabulary_file_name, vocabulary_size=self._warriors_vocabulary_size, @@ -3443,8 +3522,9 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): with self.assertRaisesRegexp(ValueError, 'dtype must be compatible'): column._get_sparse_tensors(_LazyBuilder({'aaa': inputs})) + @test_util.run_deprecated_v1 def test_parse_example(self): - a = fc.categorical_column_with_vocabulary_file( + a = fc._categorical_column_with_vocabulary_file( key='aaa', vocabulary_file='path_to_file', vocabulary_size=3) data = example_pb2.Example(features=feature_pb2.Features( feature={ @@ -3465,8 +3545,9 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): dense_shape=[1, 2]), features['aaa'].eval()) + @test_util.run_deprecated_v1 def test_get_sparse_tensors(self): - column = fc.categorical_column_with_vocabulary_file( + column = fc._categorical_column_with_vocabulary_file( key='aaa', vocabulary_file=self._wire_vocabulary_file_name, vocabulary_size=self._wire_vocabulary_size) @@ -3485,8 +3566,9 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): dense_shape=inputs.dense_shape), id_weight_pair.id_tensor.eval()) + @test_util.run_deprecated_v1 def test_get_sparse_tensors_none_vocabulary_size(self): - column = fc.categorical_column_with_vocabulary_file( + column = fc._categorical_column_with_vocabulary_file( key='aaa', vocabulary_file=self._wire_vocabulary_file_name) inputs = sparse_tensor.SparseTensorValue( indices=((0, 0), (1, 0), (1, 1)), @@ -3503,8 +3585,9 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): dense_shape=inputs.dense_shape), id_weight_pair.id_tensor.eval()) + @test_util.run_deprecated_v1 def test_transform_feature(self): - column = fc.categorical_column_with_vocabulary_file( + column = fc._categorical_column_with_vocabulary_file( key='aaa', vocabulary_file=self._wire_vocabulary_file_name, vocabulary_size=self._wire_vocabulary_size) @@ -3514,16 +3597,15 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): dense_shape=(2, 2)) id_tensor = _transform_features({'aaa': inputs}, [column])[column] with _initialized_session(): - _assert_sparse_tensor_value(self, - sparse_tensor.SparseTensorValue( - indices=inputs.indices, - values=np.array( - (2, -1, 0), dtype=np.int64), - dense_shape=inputs.dense_shape), - id_tensor.eval()) + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=inputs.indices, + values=np.array((2, -1, 0), dtype=np.int64), + dense_shape=inputs.dense_shape), self.evaluate(id_tensor)) def test_get_sparse_tensors_weight_collections(self): - column = fc.categorical_column_with_vocabulary_file( + column = fc._categorical_column_with_vocabulary_file( key='aaa', vocabulary_file=self._wire_vocabulary_file_name, vocabulary_size=self._wire_vocabulary_size) @@ -3540,8 +3622,9 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): [], ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES)) self.assertItemsEqual([], ops.get_collection('my_weights')) + @test_util.run_deprecated_v1 def test_get_sparse_tensors_dense_input(self): - column = fc.categorical_column_with_vocabulary_file( + column = fc._categorical_column_with_vocabulary_file( key='aaa', vocabulary_file=self._wire_vocabulary_file_name, vocabulary_size=self._wire_vocabulary_size) @@ -3559,8 +3642,9 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): dense_shape=(2, 2)), id_weight_pair.id_tensor.eval()) + @test_util.run_deprecated_v1 def test_get_sparse_tensors_default_value_in_vocabulary(self): - column = fc.categorical_column_with_vocabulary_file( + column = fc._categorical_column_with_vocabulary_file( key='aaa', vocabulary_file=self._wire_vocabulary_file_name, vocabulary_size=self._wire_vocabulary_size, @@ -3580,8 +3664,9 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): dense_shape=inputs.dense_shape), id_weight_pair.id_tensor.eval()) + @test_util.run_deprecated_v1 def test_get_sparse_tensors_with_oov_buckets(self): - column = fc.categorical_column_with_vocabulary_file( + column = fc._categorical_column_with_vocabulary_file( key='aaa', vocabulary_file=self._wire_vocabulary_file_name, vocabulary_size=self._wire_vocabulary_size, @@ -3601,11 +3686,12 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): dense_shape=inputs.dense_shape), id_weight_pair.id_tensor.eval()) + @test_util.run_deprecated_v1 def test_get_sparse_tensors_small_vocabulary_size(self): # 'marlo' is the last entry in our vocabulary file, so be setting # `vocabulary_size` to 1 less than number of entries in file, we take # 'marlo' out of the vocabulary. - column = fc.categorical_column_with_vocabulary_file( + column = fc._categorical_column_with_vocabulary_file( key='aaa', vocabulary_file=self._wire_vocabulary_file_name, vocabulary_size=self._wire_vocabulary_size - 1) @@ -3624,8 +3710,9 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): dense_shape=inputs.dense_shape), id_weight_pair.id_tensor.eval()) + @test_util.run_deprecated_v1 def test_get_sparse_tensors_int32(self): - column = fc.categorical_column_with_vocabulary_file( + column = fc._categorical_column_with_vocabulary_file( key='aaa', vocabulary_file=self._warriors_vocabulary_file_name, vocabulary_size=self._warriors_vocabulary_size, @@ -3645,9 +3732,10 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): dense_shape=inputs.dense_shape), id_weight_pair.id_tensor.eval()) + @test_util.run_deprecated_v1 def test_get_sparse_tensors_int32_dense_input(self): default_value = -100 - column = fc.categorical_column_with_vocabulary_file( + column = fc._categorical_column_with_vocabulary_file( key='aaa', vocabulary_file=self._warriors_vocabulary_file_name, vocabulary_size=self._warriors_vocabulary_size, @@ -3667,8 +3755,9 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): dense_shape=(3, 3)), id_weight_pair.id_tensor.eval()) + @test_util.run_deprecated_v1 def test_get_sparse_tensors_int32_with_oov_buckets(self): - column = fc.categorical_column_with_vocabulary_file( + column = fc._categorical_column_with_vocabulary_file( key='aaa', vocabulary_file=self._warriors_vocabulary_file_name, vocabulary_size=self._warriors_vocabulary_size, @@ -3689,8 +3778,9 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): dense_shape=inputs.dense_shape), id_weight_pair.id_tensor.eval()) + @test_util.run_deprecated_v1 def test_linear_model(self): - wire_column = fc.categorical_column_with_vocabulary_file( + wire_column = fc._categorical_column_with_vocabulary_file( key='wire', vocabulary_file=self._wire_vocabulary_file_name, vocabulary_size=self._wire_vocabulary_size, @@ -3706,16 +3796,18 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): bias = get_linear_model_bias() wire_var = get_linear_model_column_var(wire_column) with _initialized_session(): - self.assertAllClose((0.,), bias.eval()) - self.assertAllClose(((0.,), (0.,), (0.,), (0.,)), wire_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) + self.assertAllClose((0.,), self.evaluate(bias)) + self.assertAllClose(((0.,), (0.,), (0.,), (0.,)), + self.evaluate(wire_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) wire_var.assign(((1.,), (2.,), (3.,), (4.,))).eval() # 'marlo' -> 2: wire_var[2] = 3 # 'skywalker' -> 3, 'omar' -> 0: wire_var[3] + wire_var[0] = 4+1 = 5 - self.assertAllClose(((3.,), (5.,)), predictions.eval()) + self.assertAllClose(((3.,), (5.,)), self.evaluate(predictions)) + @test_util.run_deprecated_v1 def test_keras_linear_model(self): - wire_column = fc.categorical_column_with_vocabulary_file( + wire_column = fc._categorical_column_with_vocabulary_file( key='wire', vocabulary_file=self._wire_vocabulary_file_name, vocabulary_size=self._wire_vocabulary_size, @@ -3732,19 +3824,20 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): bias = get_linear_model_bias() wire_var = get_linear_model_column_var(wire_column) with _initialized_session(): - self.assertAllClose((0.,), bias.eval()) - self.assertAllClose(((0.,), (0.,), (0.,), (0.,)), wire_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) + self.assertAllClose((0.,), self.evaluate(bias)) + self.assertAllClose(((0.,), (0.,), (0.,), (0.,)), + self.evaluate(wire_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) wire_var.assign(((1.,), (2.,), (3.,), (4.,))).eval() # 'marlo' -> 2: wire_var[2] = 3 # 'skywalker' -> 3, 'omar' -> 0: wire_var[3] + wire_var[0] = 4+1 = 5 - self.assertAllClose(((3.,), (5.,)), predictions.eval()) + self.assertAllClose(((3.,), (5.,)), self.evaluate(predictions)) class VocabularyListCategoricalColumnTest(test.TestCase): def test_defaults_string(self): - column = fc.categorical_column_with_vocabulary_list( + column = fc._categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=('omar', 'stringer', 'marlo')) self.assertEqual('aaa', column.name) self.assertEqual('aaa', column.key) @@ -3756,11 +3849,11 @@ class VocabularyListCategoricalColumnTest(test.TestCase): def test_key_should_be_string(self): with self.assertRaisesRegexp(ValueError, 'key must be a string.'): - fc.categorical_column_with_vocabulary_list( + fc._categorical_column_with_vocabulary_list( key=('aaa',), vocabulary_list=('omar', 'stringer', 'marlo')) def test_defaults_int(self): - column = fc.categorical_column_with_vocabulary_list( + column = fc._categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=(12, 24, 36)) self.assertEqual('aaa', column.name) self.assertEqual('aaa', column.key) @@ -3770,17 +3863,21 @@ class VocabularyListCategoricalColumnTest(test.TestCase): 'aaa': parsing_ops.VarLenFeature(dtypes.int64) }, column._parse_example_spec) + @test_util.run_deprecated_v1 def test_all_constructor_args(self): - column = fc.categorical_column_with_vocabulary_list( - key='aaa', vocabulary_list=(12, 24, 36), dtype=dtypes.int32, + column = fc._categorical_column_with_vocabulary_list( + key='aaa', + vocabulary_list=(12, 24, 36), + dtype=dtypes.int32, default_value=-99) self.assertEqual(3, column._num_buckets) self.assertEqual({ 'aaa': parsing_ops.VarLenFeature(dtypes.int32) }, column._parse_example_spec) + @test_util.run_deprecated_v1 def test_deep_copy(self): - original = fc.categorical_column_with_vocabulary_list( + original = fc._categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=(12, 24, 36), dtype=dtypes.int32) for column in (original, copy.deepcopy(original)): self.assertEqual('aaa', column.name) @@ -3791,65 +3888,65 @@ class VocabularyListCategoricalColumnTest(test.TestCase): def test_invalid_dtype(self): with self.assertRaisesRegexp(ValueError, 'dtype must be string or integer'): - fc.categorical_column_with_vocabulary_list( - key='aaa', vocabulary_list=('omar', 'stringer', 'marlo'), + fc._categorical_column_with_vocabulary_list( + key='aaa', + vocabulary_list=('omar', 'stringer', 'marlo'), dtype=dtypes.float32) def test_invalid_mapping_dtype(self): with self.assertRaisesRegexp( ValueError, r'vocabulary dtype must be string or integer'): - fc.categorical_column_with_vocabulary_list( + fc._categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=(12., 24., 36.)) def test_mismatched_int_dtype(self): with self.assertRaisesRegexp( ValueError, r'dtype.*and vocabulary dtype.*do not match'): - fc.categorical_column_with_vocabulary_list( - key='aaa', vocabulary_list=('omar', 'stringer', 'marlo'), + fc._categorical_column_with_vocabulary_list( + key='aaa', + vocabulary_list=('omar', 'stringer', 'marlo'), dtype=dtypes.int32) def test_mismatched_string_dtype(self): with self.assertRaisesRegexp( ValueError, r'dtype.*and vocabulary dtype.*do not match'): - fc.categorical_column_with_vocabulary_list( + fc._categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=(12, 24, 36), dtype=dtypes.string) def test_none_mapping(self): with self.assertRaisesRegexp( ValueError, r'vocabulary_list.*must be non-empty'): - fc.categorical_column_with_vocabulary_list( + fc._categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=None) def test_empty_mapping(self): with self.assertRaisesRegexp( ValueError, r'vocabulary_list.*must be non-empty'): - fc.categorical_column_with_vocabulary_list( + fc._categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=tuple([])) def test_duplicate_mapping(self): with self.assertRaisesRegexp(ValueError, 'Duplicate keys'): - fc.categorical_column_with_vocabulary_list( + fc._categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=(12, 24, 12)) def test_invalid_num_oov_buckets(self): with self.assertRaisesRegexp(ValueError, 'Invalid num_oov_buckets'): - fc.categorical_column_with_vocabulary_list( - key='aaa', vocabulary_list=(12, 24, 36), - num_oov_buckets=-1) + fc._categorical_column_with_vocabulary_list( + key='aaa', vocabulary_list=(12, 24, 36), num_oov_buckets=-1) def test_invalid_buckets_and_default_value(self): with self.assertRaisesRegexp( ValueError, 'both num_oov_buckets and default_value'): - fc.categorical_column_with_vocabulary_list( + fc._categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=(12, 24, 36), num_oov_buckets=100, default_value=2) def test_invalid_input_dtype_int32(self): - column = fc.categorical_column_with_vocabulary_list( - key='aaa', - vocabulary_list=('omar', 'stringer', 'marlo')) + column = fc._categorical_column_with_vocabulary_list( + key='aaa', vocabulary_list=('omar', 'stringer', 'marlo')) inputs = sparse_tensor.SparseTensorValue( indices=((0, 0), (1, 0), (1, 1)), values=(12, 24, 36), @@ -3858,9 +3955,8 @@ class VocabularyListCategoricalColumnTest(test.TestCase): column._get_sparse_tensors(_LazyBuilder({'aaa': inputs})) def test_invalid_input_dtype_string(self): - column = fc.categorical_column_with_vocabulary_list( - key='aaa', - vocabulary_list=(12, 24, 36)) + column = fc._categorical_column_with_vocabulary_list( + key='aaa', vocabulary_list=(12, 24, 36)) inputs = sparse_tensor.SparseTensorValue( indices=((0, 0), (1, 0), (1, 1)), values=('omar', 'stringer', 'marlo'), @@ -3868,8 +3964,9 @@ class VocabularyListCategoricalColumnTest(test.TestCase): with self.assertRaisesRegexp(ValueError, 'dtype must be compatible'): column._get_sparse_tensors(_LazyBuilder({'aaa': inputs})) + @test_util.run_deprecated_v1 def test_parse_example_string(self): - a = fc.categorical_column_with_vocabulary_list( + a = fc._categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=('omar', 'stringer', 'marlo')) data = example_pb2.Example(features=feature_pb2.Features( feature={ @@ -3890,8 +3987,9 @@ class VocabularyListCategoricalColumnTest(test.TestCase): dense_shape=[1, 2]), features['aaa'].eval()) + @test_util.run_deprecated_v1 def test_parse_example_int(self): - a = fc.categorical_column_with_vocabulary_list( + a = fc._categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=(11, 21, 31)) data = example_pb2.Example(features=feature_pb2.Features( feature={ @@ -3912,10 +4010,10 @@ class VocabularyListCategoricalColumnTest(test.TestCase): dense_shape=[1, 2]), features['aaa'].eval()) + @test_util.run_deprecated_v1 def test_get_sparse_tensors(self): - column = fc.categorical_column_with_vocabulary_list( - key='aaa', - vocabulary_list=('omar', 'stringer', 'marlo')) + column = fc._categorical_column_with_vocabulary_list( + key='aaa', vocabulary_list=('omar', 'stringer', 'marlo')) inputs = sparse_tensor.SparseTensorValue( indices=((0, 0), (1, 0), (1, 1)), values=('marlo', 'skywalker', 'omar'), @@ -3931,10 +4029,10 @@ class VocabularyListCategoricalColumnTest(test.TestCase): dense_shape=inputs.dense_shape), id_weight_pair.id_tensor.eval()) + @test_util.run_deprecated_v1 def test_transform_feature(self): - column = fc.categorical_column_with_vocabulary_list( - key='aaa', - vocabulary_list=('omar', 'stringer', 'marlo')) + column = fc._categorical_column_with_vocabulary_list( + key='aaa', vocabulary_list=('omar', 'stringer', 'marlo')) inputs = sparse_tensor.SparseTensorValue( indices=((0, 0), (1, 0), (1, 1)), values=('marlo', 'skywalker', 'omar'), @@ -3946,13 +4044,11 @@ class VocabularyListCategoricalColumnTest(test.TestCase): sparse_tensor.SparseTensorValue( indices=inputs.indices, values=np.array((2, -1, 0), dtype=np.int64), - dense_shape=inputs.dense_shape), - id_tensor.eval()) + dense_shape=inputs.dense_shape), self.evaluate(id_tensor)) def test_get_sparse_tensors_weight_collections(self): - column = fc.categorical_column_with_vocabulary_list( - key='aaa', - vocabulary_list=('omar', 'stringer', 'marlo')) + column = fc._categorical_column_with_vocabulary_list( + key='aaa', vocabulary_list=('omar', 'stringer', 'marlo')) inputs = sparse_tensor.SparseTensor( values=['omar', 'stringer', 'marlo'], indices=[[0, 0], [1, 0], [1, 1]], @@ -3966,10 +4062,10 @@ class VocabularyListCategoricalColumnTest(test.TestCase): [], ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES)) self.assertItemsEqual([], ops.get_collection('my_weights')) + @test_util.run_deprecated_v1 def test_get_sparse_tensors_dense_input(self): - column = fc.categorical_column_with_vocabulary_list( - key='aaa', - vocabulary_list=('omar', 'stringer', 'marlo')) + column = fc._categorical_column_with_vocabulary_list( + key='aaa', vocabulary_list=('omar', 'stringer', 'marlo')) id_weight_pair = column._get_sparse_tensors( _LazyBuilder({ 'aaa': (('marlo', ''), ('skywalker', 'omar')) @@ -3984,8 +4080,9 @@ class VocabularyListCategoricalColumnTest(test.TestCase): dense_shape=(2, 2)), id_weight_pair.id_tensor.eval()) + @test_util.run_deprecated_v1 def test_get_sparse_tensors_default_value_in_vocabulary(self): - column = fc.categorical_column_with_vocabulary_list( + column = fc._categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=('omar', 'stringer', 'marlo'), default_value=2) @@ -4004,8 +4101,9 @@ class VocabularyListCategoricalColumnTest(test.TestCase): dense_shape=inputs.dense_shape), id_weight_pair.id_tensor.eval()) + @test_util.run_deprecated_v1 def test_get_sparse_tensors_with_oov_buckets(self): - column = fc.categorical_column_with_vocabulary_list( + column = fc._categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=('omar', 'stringer', 'marlo'), num_oov_buckets=100) @@ -4024,8 +4122,9 @@ class VocabularyListCategoricalColumnTest(test.TestCase): dense_shape=inputs.dense_shape), id_weight_pair.id_tensor.eval()) + @test_util.run_deprecated_v1 def test_get_sparse_tensors_int32(self): - column = fc.categorical_column_with_vocabulary_list( + column = fc._categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=np.array((30, 35, 11, 23, 22), dtype=np.int32), dtype=dtypes.int32) @@ -4044,9 +4143,10 @@ class VocabularyListCategoricalColumnTest(test.TestCase): dense_shape=inputs.dense_shape), id_weight_pair.id_tensor.eval()) + @test_util.run_deprecated_v1 def test_get_sparse_tensors_int32_dense_input(self): default_value = -100 - column = fc.categorical_column_with_vocabulary_list( + column = fc._categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=np.array((30, 35, 11, 23, 22), dtype=np.int32), dtype=dtypes.int32, @@ -4067,8 +4167,9 @@ class VocabularyListCategoricalColumnTest(test.TestCase): dense_shape=(3, 3)), id_weight_pair.id_tensor.eval()) + @test_util.run_deprecated_v1 def test_get_sparse_tensors_int32_with_oov_buckets(self): - column = fc.categorical_column_with_vocabulary_list( + column = fc._categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=np.array((30, 35, 11, 23, 22), dtype=np.int32), dtype=dtypes.int32, @@ -4088,8 +4189,9 @@ class VocabularyListCategoricalColumnTest(test.TestCase): dense_shape=inputs.dense_shape), id_weight_pair.id_tensor.eval()) + @test_util.run_deprecated_v1 def test_linear_model(self): - wire_column = fc.categorical_column_with_vocabulary_list( + wire_column = fc._categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=('omar', 'stringer', 'marlo'), num_oov_buckets=1) @@ -4104,16 +4206,18 @@ class VocabularyListCategoricalColumnTest(test.TestCase): bias = get_linear_model_bias() wire_var = get_linear_model_column_var(wire_column) with _initialized_session(): - self.assertAllClose((0.,), bias.eval()) - self.assertAllClose(((0.,), (0.,), (0.,), (0.,)), wire_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) + self.assertAllClose((0.,), self.evaluate(bias)) + self.assertAllClose(((0.,), (0.,), (0.,), (0.,)), + self.evaluate(wire_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) wire_var.assign(((1.,), (2.,), (3.,), (4.,))).eval() # 'marlo' -> 2: wire_var[2] = 3 # 'skywalker' -> 3, 'omar' -> 0: wire_var[3] + wire_var[0] = 4+1 = 5 - self.assertAllClose(((3.,), (5.,)), predictions.eval()) + self.assertAllClose(((3.,), (5.,)), self.evaluate(predictions)) + @test_util.run_deprecated_v1 def test_keras_linear_model(self): - wire_column = fc.categorical_column_with_vocabulary_list( + wire_column = fc._categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=('omar', 'stringer', 'marlo'), num_oov_buckets=1) @@ -4129,19 +4233,20 @@ class VocabularyListCategoricalColumnTest(test.TestCase): bias = get_linear_model_bias() wire_var = get_linear_model_column_var(wire_column) with _initialized_session(): - self.assertAllClose((0.,), bias.eval()) - self.assertAllClose(((0.,), (0.,), (0.,), (0.,)), wire_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) + self.assertAllClose((0.,), self.evaluate(bias)) + self.assertAllClose(((0.,), (0.,), (0.,), (0.,)), + self.evaluate(wire_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) wire_var.assign(((1.,), (2.,), (3.,), (4.,))).eval() # 'marlo' -> 2: wire_var[2] = 3 # 'skywalker' -> 3, 'omar' -> 0: wire_var[3] + wire_var[0] = 4+1 = 5 - self.assertAllClose(((3.,), (5.,)), predictions.eval()) + self.assertAllClose(((3.,), (5.,)), self.evaluate(predictions)) class IdentityCategoricalColumnTest(test.TestCase): def test_constructor(self): - column = fc.categorical_column_with_identity(key='aaa', num_buckets=3) + column = fc._categorical_column_with_identity(key='aaa', num_buckets=3) self.assertEqual('aaa', column.name) self.assertEqual('aaa', column.key) self.assertEqual('aaa', column._var_scope_name) @@ -4152,10 +4257,11 @@ class IdentityCategoricalColumnTest(test.TestCase): def test_key_should_be_string(self): with self.assertRaisesRegexp(ValueError, 'key must be a string.'): - fc.categorical_column_with_identity(key=('aaa',), num_buckets=3) + fc._categorical_column_with_identity(key=('aaa',), num_buckets=3) + @test_util.run_deprecated_v1 def test_deep_copy(self): - original = fc.categorical_column_with_identity(key='aaa', num_buckets=3) + original = fc._categorical_column_with_identity(key='aaa', num_buckets=3) for column in (original, copy.deepcopy(original)): self.assertEqual('aaa', column.name) self.assertEqual(3, column._num_buckets) @@ -4165,24 +4271,24 @@ class IdentityCategoricalColumnTest(test.TestCase): def test_invalid_num_buckets_zero(self): with self.assertRaisesRegexp(ValueError, 'num_buckets 0 < 1'): - fc.categorical_column_with_identity(key='aaa', num_buckets=0) + fc._categorical_column_with_identity(key='aaa', num_buckets=0) def test_invalid_num_buckets_negative(self): with self.assertRaisesRegexp(ValueError, 'num_buckets -1 < 1'): - fc.categorical_column_with_identity(key='aaa', num_buckets=-1) + fc._categorical_column_with_identity(key='aaa', num_buckets=-1) def test_invalid_default_value_too_small(self): with self.assertRaisesRegexp(ValueError, 'default_value -1 not in range'): - fc.categorical_column_with_identity( + fc._categorical_column_with_identity( key='aaa', num_buckets=3, default_value=-1) def test_invalid_default_value_too_big(self): with self.assertRaisesRegexp(ValueError, 'default_value 3 not in range'): - fc.categorical_column_with_identity( + fc._categorical_column_with_identity( key='aaa', num_buckets=3, default_value=3) def test_invalid_input_dtype(self): - column = fc.categorical_column_with_identity(key='aaa', num_buckets=3) + column = fc._categorical_column_with_identity(key='aaa', num_buckets=3) inputs = sparse_tensor.SparseTensorValue( indices=((0, 0), (1, 0), (1, 1)), values=('omar', 'stringer', 'marlo'), @@ -4190,8 +4296,9 @@ class IdentityCategoricalColumnTest(test.TestCase): with self.assertRaisesRegexp(ValueError, 'Invalid input, not integer'): column._get_sparse_tensors(_LazyBuilder({'aaa': inputs})) + @test_util.run_deprecated_v1 def test_parse_example(self): - a = fc.categorical_column_with_identity(key='aaa', num_buckets=30) + a = fc._categorical_column_with_identity(key='aaa', num_buckets=30) data = example_pb2.Example(features=feature_pb2.Features( feature={ 'aaa': @@ -4211,8 +4318,9 @@ class IdentityCategoricalColumnTest(test.TestCase): dense_shape=[1, 2]), features['aaa'].eval()) + @test_util.run_deprecated_v1 def test_get_sparse_tensors(self): - column = fc.categorical_column_with_identity(key='aaa', num_buckets=3) + column = fc._categorical_column_with_identity(key='aaa', num_buckets=3) inputs = sparse_tensor.SparseTensorValue( indices=((0, 0), (1, 0), (1, 1)), values=(0, 1, 0), @@ -4228,8 +4336,9 @@ class IdentityCategoricalColumnTest(test.TestCase): dense_shape=inputs.dense_shape), id_weight_pair.id_tensor.eval()) + @test_util.run_deprecated_v1 def test_transform_feature(self): - column = fc.categorical_column_with_identity(key='aaa', num_buckets=3) + column = fc._categorical_column_with_identity(key='aaa', num_buckets=3) inputs = sparse_tensor.SparseTensorValue( indices=((0, 0), (1, 0), (1, 1)), values=(0, 1, 0), @@ -4241,11 +4350,10 @@ class IdentityCategoricalColumnTest(test.TestCase): sparse_tensor.SparseTensorValue( indices=inputs.indices, values=np.array((0, 1, 0), dtype=np.int64), - dense_shape=inputs.dense_shape), - id_tensor.eval()) + dense_shape=inputs.dense_shape), self.evaluate(id_tensor)) def test_get_sparse_tensors_weight_collections(self): - column = fc.categorical_column_with_identity(key='aaa', num_buckets=3) + column = fc._categorical_column_with_identity(key='aaa', num_buckets=3) inputs = sparse_tensor.SparseTensorValue( indices=((0, 0), (1, 0), (1, 1)), values=(0, 1, 0), @@ -4259,8 +4367,9 @@ class IdentityCategoricalColumnTest(test.TestCase): [], ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES)) self.assertItemsEqual([], ops.get_collection('my_weights')) + @test_util.run_deprecated_v1 def test_get_sparse_tensors_dense_input(self): - column = fc.categorical_column_with_identity(key='aaa', num_buckets=3) + column = fc._categorical_column_with_identity(key='aaa', num_buckets=3) id_weight_pair = column._get_sparse_tensors( _LazyBuilder({ 'aaa': ((0, -1), (1, 0)) @@ -4275,8 +4384,9 @@ class IdentityCategoricalColumnTest(test.TestCase): dense_shape=(2, 2)), id_weight_pair.id_tensor.eval()) + @test_util.run_deprecated_v1 def test_get_sparse_tensors_with_inputs_too_small(self): - column = fc.categorical_column_with_identity(key='aaa', num_buckets=3) + column = fc._categorical_column_with_identity(key='aaa', num_buckets=3) inputs = sparse_tensor.SparseTensorValue( indices=((0, 0), (1, 0), (1, 1)), values=(1, -1, 0), @@ -4288,8 +4398,9 @@ class IdentityCategoricalColumnTest(test.TestCase): errors.OpError, 'assert_greater_or_equal_0'): id_weight_pair.id_tensor.eval() + @test_util.run_deprecated_v1 def test_get_sparse_tensors_with_inputs_too_big(self): - column = fc.categorical_column_with_identity(key='aaa', num_buckets=3) + column = fc._categorical_column_with_identity(key='aaa', num_buckets=3) inputs = sparse_tensor.SparseTensorValue( indices=((0, 0), (1, 0), (1, 1)), values=(1, 99, 0), @@ -4301,8 +4412,9 @@ class IdentityCategoricalColumnTest(test.TestCase): errors.OpError, 'assert_less_than_num_buckets'): id_weight_pair.id_tensor.eval() + @test_util.run_deprecated_v1 def test_get_sparse_tensors_with_default_value(self): - column = fc.categorical_column_with_identity( + column = fc._categorical_column_with_identity( key='aaa', num_buckets=4, default_value=3) inputs = sparse_tensor.SparseTensorValue( indices=((0, 0), (1, 0), (1, 1)), @@ -4319,8 +4431,9 @@ class IdentityCategoricalColumnTest(test.TestCase): dense_shape=inputs.dense_shape), id_weight_pair.id_tensor.eval()) + @test_util.run_deprecated_v1 def test_get_sparse_tensors_with_default_value_and_placeholder_inputs(self): - column = fc.categorical_column_with_identity( + column = fc._categorical_column_with_identity( key='aaa', num_buckets=4, default_value=3) input_indices = array_ops.placeholder(dtype=dtypes.int64) input_values = array_ops.placeholder(dtype=dtypes.int32) @@ -4344,8 +4457,9 @@ class IdentityCategoricalColumnTest(test.TestCase): input_shape: (2, 2), })) + @test_util.run_deprecated_v1 def test_linear_model(self): - column = fc.categorical_column_with_identity(key='aaa', num_buckets=3) + column = fc._categorical_column_with_identity(key='aaa', num_buckets=3) self.assertEqual(3, column._num_buckets) with ops.Graph().as_default(): predictions = fc.linear_model({ @@ -4357,16 +4471,17 @@ class IdentityCategoricalColumnTest(test.TestCase): bias = get_linear_model_bias() weight_var = get_linear_model_column_var(column) with _initialized_session(): - self.assertAllClose((0.,), bias.eval()) - self.assertAllClose(((0.,), (0.,), (0.,)), weight_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) + self.assertAllClose((0.,), self.evaluate(bias)) + self.assertAllClose(((0.,), (0.,), (0.,)), self.evaluate(weight_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) weight_var.assign(((1.,), (2.,), (3.,))).eval() # weight_var[0] = 1 # weight_var[2] + weight_var[1] = 3+2 = 5 - self.assertAllClose(((1.,), (5.,)), predictions.eval()) + self.assertAllClose(((1.,), (5.,)), self.evaluate(predictions)) + @test_util.run_deprecated_v1 def test_keras_linear_model(self): - column = fc.categorical_column_with_identity(key='aaa', num_buckets=3) + column = fc._categorical_column_with_identity(key='aaa', num_buckets=3) self.assertEqual(3, column._num_buckets) with ops.Graph().as_default(): predictions = get_keras_linear_model_predictions({ @@ -4379,13 +4494,13 @@ class IdentityCategoricalColumnTest(test.TestCase): bias = get_linear_model_bias() weight_var = get_linear_model_column_var(column) with _initialized_session(): - self.assertAllClose((0.,), bias.eval()) - self.assertAllClose(((0.,), (0.,), (0.,)), weight_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) + self.assertAllClose((0.,), self.evaluate(bias)) + self.assertAllClose(((0.,), (0.,), (0.,)), self.evaluate(weight_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) weight_var.assign(((1.,), (2.,), (3.,))).eval() # weight_var[0] = 1 # weight_var[2] + weight_var[1] = 3+2 = 5 - self.assertAllClose(((1.,), (5.,)), predictions.eval()) + self.assertAllClose(((1.,), (5.,)), self.evaluate(predictions)) class TransformFeaturesTest(test.TestCase): @@ -4393,9 +4508,9 @@ class TransformFeaturesTest(test.TestCase): # All transform tests are distributed in column test. # Here we only test multi column case and naming def transform_multi_column(self): - bucketized_price = fc.bucketized_column( - fc.numeric_column('price'), boundaries=[0, 2, 4, 6]) - hashed_sparse = fc.categorical_column_with_hash_bucket('wire', 10) + bucketized_price = fc._bucketized_column( + fc._numeric_column('price'), boundaries=[0, 2, 4, 6]) + hashed_sparse = fc._categorical_column_with_hash_bucket('wire', 10) with ops.Graph().as_default(): features = { 'price': [[-1.], [5.]], @@ -4452,32 +4567,33 @@ class TransformFeaturesTest(test.TestCase): class IndicatorColumnTest(test.TestCase): def test_indicator_column(self): - a = fc.categorical_column_with_hash_bucket('a', 4) - indicator_a = fc.indicator_column(a) + a = fc._categorical_column_with_hash_bucket('a', 4) + indicator_a = fc._indicator_column(a) self.assertEqual(indicator_a.categorical_column.name, 'a') self.assertEqual(indicator_a.name, 'a_indicator') self.assertEqual(indicator_a._var_scope_name, 'a_indicator') self.assertEqual(indicator_a._variable_shape, [1, 4]) - b = fc.categorical_column_with_hash_bucket('b', hash_bucket_size=100) - indicator_b = fc.indicator_column(b) + b = fc._categorical_column_with_hash_bucket('b', hash_bucket_size=100) + indicator_b = fc._indicator_column(b) self.assertEqual(indicator_b.categorical_column.name, 'b') self.assertEqual(indicator_b.name, 'b_indicator') self.assertEqual(indicator_b._var_scope_name, 'b_indicator') self.assertEqual(indicator_b._variable_shape, [1, 100]) def test_1D_shape_succeeds(self): - animal = fc.indicator_column( - fc.categorical_column_with_hash_bucket('animal', 4)) + animal = fc._indicator_column( + fc._categorical_column_with_hash_bucket('animal', 4)) builder = _LazyBuilder({'animal': ['fox', 'fox']}) output = builder.get(animal) with self.cached_session(): - self.assertAllEqual([[0., 0., 1., 0.], [0., 0., 1., 0.]], output.eval()) + self.assertAllEqual([[0., 0., 1., 0.], [0., 0., 1., 0.]], + self.evaluate(output)) def test_2D_shape_succeeds(self): # TODO(ispir/cassandrax): Swith to categorical_column_with_keys when ready. - animal = fc.indicator_column( - fc.categorical_column_with_hash_bucket('animal', 4)) + animal = fc._indicator_column( + fc._categorical_column_with_hash_bucket('animal', 4)) builder = _LazyBuilder({ 'animal': sparse_tensor.SparseTensor( @@ -4487,11 +4603,12 @@ class IndicatorColumnTest(test.TestCase): }) output = builder.get(animal) with self.cached_session(): - self.assertAllEqual([[0., 0., 1., 0.], [0., 0., 1., 0.]], output.eval()) + self.assertAllEqual([[0., 0., 1., 0.], [0., 0., 1., 0.]], + self.evaluate(output)) def test_multi_hot(self): - animal = fc.indicator_column( - fc.categorical_column_with_identity('animal', num_buckets=4)) + animal = fc._indicator_column( + fc._categorical_column_with_identity('animal', num_buckets=4)) builder = _LazyBuilder({ 'animal': @@ -4500,11 +4617,11 @@ class IndicatorColumnTest(test.TestCase): }) output = builder.get(animal) with self.cached_session(): - self.assertAllEqual([[0., 2., 0., 0.]], output.eval()) + self.assertAllEqual([[0., 2., 0., 0.]], self.evaluate(output)) def test_multi_hot2(self): - animal = fc.indicator_column( - fc.categorical_column_with_identity('animal', num_buckets=4)) + animal = fc._indicator_column( + fc._categorical_column_with_identity('animal', num_buckets=4)) builder = _LazyBuilder({ 'animal': sparse_tensor.SparseTensor( @@ -4512,20 +4629,22 @@ class IndicatorColumnTest(test.TestCase): }) output = builder.get(animal) with self.cached_session(): - self.assertAllEqual([[0., 1., 1., 0.]], output.eval()) + self.assertAllEqual([[0., 1., 1., 0.]], self.evaluate(output)) + @test_util.run_deprecated_v1 def test_deep_copy(self): - a = fc.categorical_column_with_hash_bucket('a', 4) - column = fc.indicator_column(a) + a = fc._categorical_column_with_hash_bucket('a', 4) + column = fc._indicator_column(a) column_copy = copy.deepcopy(column) self.assertEqual(column_copy.categorical_column.name, 'a') self.assertEqual(column.name, 'a_indicator') self.assertEqual(column._variable_shape, [1, 4]) + @test_util.run_deprecated_v1 def test_parse_example(self): - a = fc.categorical_column_with_vocabulary_list( + a = fc._categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=('omar', 'stringer', 'marlo')) - a_indicator = fc.indicator_column(a) + a_indicator = fc._indicator_column(a) data = example_pb2.Example(features=feature_pb2.Features( feature={ 'aaa': @@ -4545,10 +4664,11 @@ class IndicatorColumnTest(test.TestCase): dense_shape=[1, 2]), features['aaa'].eval()) + @test_util.run_deprecated_v1 def test_transform(self): - a = fc.categorical_column_with_vocabulary_list( + a = fc._categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=('omar', 'stringer', 'marlo')) - a_indicator = fc.indicator_column(a) + a_indicator = fc._indicator_column(a) features = { 'aaa': sparse_tensor.SparseTensorValue( indices=((0, 0), (1, 0), (1, 1)), @@ -4557,51 +4677,56 @@ class IndicatorColumnTest(test.TestCase): } indicator_tensor = _transform_features(features, [a_indicator])[a_indicator] with _initialized_session(): - self.assertAllEqual([[0, 0, 1], [1, 0, 0]], indicator_tensor.eval()) + self.assertAllEqual([[0, 0, 1], [1, 0, 0]], + self.evaluate(indicator_tensor)) + @test_util.run_deprecated_v1 def test_transform_with_weighted_column(self): # Github issue 12557 - ids = fc.categorical_column_with_vocabulary_list( + ids = fc._categorical_column_with_vocabulary_list( key='ids', vocabulary_list=('a', 'b', 'c')) - weights = fc.weighted_categorical_column(ids, 'weights') - indicator = fc.indicator_column(weights) + weights = fc._weighted_categorical_column(ids, 'weights') + indicator = fc._indicator_column(weights) features = { 'ids': constant_op.constant([['c', 'b', 'a', 'c']]), 'weights': constant_op.constant([[2., 4., 6., 1.]]) } indicator_tensor = _transform_features(features, [indicator])[indicator] with _initialized_session(): - self.assertAllEqual([[6., 4., 3.]], indicator_tensor.eval()) + self.assertAllEqual([[6., 4., 3.]], self.evaluate(indicator_tensor)) + @test_util.run_deprecated_v1 def test_transform_with_missing_value_in_weighted_column(self): # Github issue 12583 - ids = fc.categorical_column_with_vocabulary_list( + ids = fc._categorical_column_with_vocabulary_list( key='ids', vocabulary_list=('a', 'b', 'c')) - weights = fc.weighted_categorical_column(ids, 'weights') - indicator = fc.indicator_column(weights) + weights = fc._weighted_categorical_column(ids, 'weights') + indicator = fc._indicator_column(weights) features = { 'ids': constant_op.constant([['c', 'b', 'unknown']]), 'weights': constant_op.constant([[2., 4., 6.]]) } indicator_tensor = _transform_features(features, [indicator])[indicator] with _initialized_session(): - self.assertAllEqual([[0., 4., 2.]], indicator_tensor.eval()) + self.assertAllEqual([[0., 4., 2.]], self.evaluate(indicator_tensor)) + @test_util.run_deprecated_v1 def test_transform_with_missing_value_in_categorical_column(self): # Github issue 12583 - ids = fc.categorical_column_with_vocabulary_list( + ids = fc._categorical_column_with_vocabulary_list( key='ids', vocabulary_list=('a', 'b', 'c')) - indicator = fc.indicator_column(ids) + indicator = fc._indicator_column(ids) features = { 'ids': constant_op.constant([['c', 'b', 'unknown']]), } indicator_tensor = _transform_features(features, [indicator])[indicator] with _initialized_session(): - self.assertAllEqual([[0., 1., 1.]], indicator_tensor.eval()) + self.assertAllEqual([[0., 1., 1.]], self.evaluate(indicator_tensor)) + @test_util.run_deprecated_v1 def test_linear_model(self): - animal = fc.indicator_column( - fc.categorical_column_with_identity('animal', num_buckets=4)) + animal = fc._indicator_column( + fc._categorical_column_with_identity('animal', num_buckets=4)) with ops.Graph().as_default(): features = { 'animal': @@ -4613,14 +4738,15 @@ class IndicatorColumnTest(test.TestCase): weight_var = get_linear_model_column_var(animal) with _initialized_session(): # All should be zero-initialized. - self.assertAllClose([[0.], [0.], [0.], [0.]], weight_var.eval()) - self.assertAllClose([[0.]], predictions.eval()) + self.assertAllClose([[0.], [0.], [0.], [0.]], self.evaluate(weight_var)) + self.assertAllClose([[0.]], self.evaluate(predictions)) weight_var.assign([[1.], [2.], [3.], [4.]]).eval() - self.assertAllClose([[2. + 3.]], predictions.eval()) + self.assertAllClose([[2. + 3.]], self.evaluate(predictions)) + @test_util.run_deprecated_v1 def test_keras_linear_model(self): - animal = fc.indicator_column( - fc.categorical_column_with_identity('animal', num_buckets=4)) + animal = fc._indicator_column( + fc._categorical_column_with_identity('animal', num_buckets=4)) with ops.Graph().as_default(): features = { 'animal': @@ -4632,14 +4758,15 @@ class IndicatorColumnTest(test.TestCase): weight_var = get_linear_model_column_var(animal) with _initialized_session(): # All should be zero-initialized. - self.assertAllClose([[0.], [0.], [0.], [0.]], weight_var.eval()) - self.assertAllClose([[0.]], predictions.eval()) + self.assertAllClose([[0.], [0.], [0.], [0.]], self.evaluate(weight_var)) + self.assertAllClose([[0.]], self.evaluate(predictions)) weight_var.assign([[1.], [2.], [3.], [4.]]).eval() - self.assertAllClose([[2. + 3.]], predictions.eval()) + self.assertAllClose([[2. + 3.]], self.evaluate(predictions)) + @test_util.run_deprecated_v1 def test_input_layer(self): - animal = fc.indicator_column( - fc.categorical_column_with_identity('animal', num_buckets=4)) + animal = fc._indicator_column( + fc._categorical_column_with_identity('animal', num_buckets=4)) with ops.Graph().as_default(): features = { 'animal': @@ -4648,16 +4775,17 @@ class IndicatorColumnTest(test.TestCase): } net = fc.input_layer(features, [animal]) with _initialized_session(): - self.assertAllClose([[0., 1., 1., 0.]], net.eval()) + self.assertAllClose([[0., 1., 1., 0.]], self.evaluate(net)) class EmbeddingColumnTest(test.TestCase): + @test_util.run_deprecated_v1 def test_defaults(self): - categorical_column = fc.categorical_column_with_identity( + categorical_column = fc._categorical_column_with_identity( key='aaa', num_buckets=3) embedding_dimension = 2 - embedding_column = fc.embedding_column( + embedding_column = fc._embedding_column( categorical_column, dimension=embedding_dimension) self.assertIs(categorical_column, embedding_column.categorical_column) self.assertEqual(embedding_dimension, embedding_column.dimension) @@ -4674,15 +4802,20 @@ class EmbeddingColumnTest(test.TestCase): 'aaa': parsing_ops.VarLenFeature(dtypes.int64) }, embedding_column._parse_example_spec) + @test_util.run_deprecated_v1 def test_all_constructor_args(self): - categorical_column = fc.categorical_column_with_identity( + categorical_column = fc._categorical_column_with_identity( key='aaa', num_buckets=3) embedding_dimension = 2 - embedding_column = fc.embedding_column( - categorical_column, dimension=embedding_dimension, - combiner='my_combiner', initializer=lambda: 'my_initializer', - ckpt_to_load_from='my_ckpt', tensor_name_in_ckpt='my_ckpt_tensor', - max_norm=42., trainable=False) + embedding_column = fc._embedding_column( + categorical_column, + dimension=embedding_dimension, + combiner='my_combiner', + initializer=lambda: 'my_initializer', + ckpt_to_load_from='my_ckpt', + tensor_name_in_ckpt='my_ckpt_tensor', + max_norm=42., + trainable=False) self.assertIs(categorical_column, embedding_column.categorical_column) self.assertEqual(embedding_dimension, embedding_column.dimension) self.assertEqual('my_combiner', embedding_column.combiner) @@ -4698,15 +4831,20 @@ class EmbeddingColumnTest(test.TestCase): 'aaa': parsing_ops.VarLenFeature(dtypes.int64) }, embedding_column._parse_example_spec) + @test_util.run_deprecated_v1 def test_deep_copy(self): - categorical_column = fc.categorical_column_with_identity( + categorical_column = fc._categorical_column_with_identity( key='aaa', num_buckets=3) embedding_dimension = 2 - original = fc.embedding_column( - categorical_column, dimension=embedding_dimension, - combiner='my_combiner', initializer=lambda: 'my_initializer', - ckpt_to_load_from='my_ckpt', tensor_name_in_ckpt='my_ckpt_tensor', - max_norm=42., trainable=False) + original = fc._embedding_column( + categorical_column, + dimension=embedding_dimension, + combiner='my_combiner', + initializer=lambda: 'my_initializer', + ckpt_to_load_from='my_ckpt', + tensor_name_in_ckpt='my_ckpt_tensor', + max_norm=42., + trainable=False) for embedding_column in (original, copy.deepcopy(original)): self.assertEqual('aaa', embedding_column.categorical_column.name) self.assertEqual(3, embedding_column.categorical_column._num_buckets) @@ -4727,16 +4865,19 @@ class EmbeddingColumnTest(test.TestCase): 'aaa': parsing_ops.VarLenFeature(dtypes.int64) }, embedding_column._parse_example_spec) + @test_util.run_deprecated_v1 def test_invalid_initializer(self): - categorical_column = fc.categorical_column_with_identity( + categorical_column = fc._categorical_column_with_identity( key='aaa', num_buckets=3) with self.assertRaisesRegexp(ValueError, 'initializer must be callable'): - fc.embedding_column(categorical_column, dimension=2, initializer='not_fn') + fc._embedding_column( + categorical_column, dimension=2, initializer='not_fn') + @test_util.run_deprecated_v1 def test_parse_example(self): - a = fc.categorical_column_with_vocabulary_list( + a = fc._categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=('omar', 'stringer', 'marlo')) - a_embedded = fc.embedding_column(a, dimension=2) + a_embedded = fc._embedding_column(a, dimension=2) data = example_pb2.Example(features=feature_pb2.Features( feature={ 'aaa': @@ -4756,9 +4897,10 @@ class EmbeddingColumnTest(test.TestCase): dense_shape=[1, 2]), features['aaa'].eval()) + @test_util.run_deprecated_v1 def test_transform_feature(self): - a = fc.categorical_column_with_identity(key='aaa', num_buckets=3) - a_embedded = fc.embedding_column(a, dimension=2) + a = fc._categorical_column_with_identity(key='aaa', num_buckets=3) + a_embedded = fc._embedding_column(a, dimension=2) features = { 'aaa': sparse_tensor.SparseTensor( indices=((0, 0), (1, 0), (1, 1)), @@ -4769,9 +4911,10 @@ class EmbeddingColumnTest(test.TestCase): output_a = outputs[a] output_embedded = outputs[a_embedded] with _initialized_session(): - _assert_sparse_tensor_value( - self, output_a.eval(), output_embedded.eval()) + _assert_sparse_tensor_value(self, self.evaluate(output_a), + self.evaluate(output_embedded)) + @test_util.run_deprecated_v1 def test_get_dense_tensor(self): # Inputs. vocabulary_size = 3 @@ -4810,10 +4953,11 @@ class EmbeddingColumnTest(test.TestCase): ) # Build columns. - categorical_column = fc.categorical_column_with_identity( + categorical_column = fc._categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - embedding_column = fc.embedding_column( - categorical_column, dimension=embedding_dimension, + embedding_column = fc._embedding_column( + categorical_column, + dimension=embedding_dimension, initializer=_initializer) # Provide sparse input and get dense result. @@ -4828,8 +4972,9 @@ class EmbeddingColumnTest(test.TestCase): tuple([v.name for v in global_vars])) with _initialized_session(): self.assertAllEqual(embedding_values, global_vars[0].eval()) - self.assertAllEqual(expected_lookups, embedding_lookup.eval()) + self.assertAllEqual(expected_lookups, self.evaluate(embedding_lookup)) + @test_util.run_deprecated_v1 def test_get_dense_tensor_3d(self): # Inputs. vocabulary_size = 4 @@ -4870,10 +5015,11 @@ class EmbeddingColumnTest(test.TestCase): ) # Build columns. - categorical_column = fc.categorical_column_with_identity( + categorical_column = fc._categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - embedding_column = fc.embedding_column( - categorical_column, dimension=embedding_dimension, + embedding_column = fc._embedding_column( + categorical_column, + dimension=embedding_dimension, initializer=_initializer) # Provide sparse input and get dense result. @@ -4888,8 +5034,9 @@ class EmbeddingColumnTest(test.TestCase): tuple([v.name for v in global_vars])) with _initialized_session(): self.assertAllEqual(embedding_values, global_vars[0].eval()) - self.assertAllEqual(expected_lookups, embedding_lookup.eval()) + self.assertAllEqual(expected_lookups, self.evaluate(embedding_lookup)) + @test_util.run_deprecated_v1 def test_get_dense_tensor_weight_collections(self): sparse_input = sparse_tensor.SparseTensorValue( # example 0, ids [2] @@ -4901,9 +5048,9 @@ class EmbeddingColumnTest(test.TestCase): dense_shape=(4, 5)) # Build columns. - categorical_column = fc.categorical_column_with_identity( + categorical_column = fc._categorical_column_with_identity( key='aaa', num_buckets=3) - embedding_column = fc.embedding_column(categorical_column, dimension=2) + embedding_column = fc._embedding_column(categorical_column, dimension=2) # Provide sparse input and get dense result. embedding_column._get_dense_tensor( @@ -4919,6 +5066,7 @@ class EmbeddingColumnTest(test.TestCase): self.assertItemsEqual( ('embedding_weights:0',), tuple([v.name for v in my_vars])) + @test_util.run_deprecated_v1 def test_get_dense_tensor_placeholder_inputs(self): # Inputs. vocabulary_size = 3 @@ -4957,10 +5105,11 @@ class EmbeddingColumnTest(test.TestCase): ) # Build columns. - categorical_column = fc.categorical_column_with_identity( + categorical_column = fc._categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - embedding_column = fc.embedding_column( - categorical_column, dimension=embedding_dimension, + embedding_column = fc._embedding_column( + categorical_column, + dimension=embedding_dimension, initializer=_initializer) # Provide sparse input and get dense result. @@ -4989,6 +5138,7 @@ class EmbeddingColumnTest(test.TestCase): input_shape: sparse_input.dense_shape, })) + @test_util.run_deprecated_v1 def test_get_dense_tensor_restore_from_ckpt(self): # Inputs. vocabulary_size = 3 @@ -5025,10 +5175,11 @@ class EmbeddingColumnTest(test.TestCase): ) # Build columns. - categorical_column = fc.categorical_column_with_identity( + categorical_column = fc._categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - embedding_column = fc.embedding_column( - categorical_column, dimension=embedding_dimension, + embedding_column = fc._embedding_column( + categorical_column, + dimension=embedding_dimension, ckpt_to_load_from=ckpt_path, tensor_name_in_ckpt=ckpt_tensor) @@ -5044,8 +5195,9 @@ class EmbeddingColumnTest(test.TestCase): ('embedding_weights:0',), tuple([v.name for v in global_vars])) with _initialized_session(): self.assertAllEqual(embedding_values, global_vars[0].eval()) - self.assertAllEqual(expected_lookups, embedding_lookup.eval()) + self.assertAllEqual(expected_lookups, self.evaluate(embedding_lookup)) + @test_util.run_deprecated_v1 def test_linear_model(self): # Inputs. batch_size = 4 @@ -5070,10 +5222,11 @@ class EmbeddingColumnTest(test.TestCase): return zeros_embedding_values # Build columns. - categorical_column = fc.categorical_column_with_identity( + categorical_column = fc._categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - embedding_column = fc.embedding_column( - categorical_column, dimension=embedding_dimension, + embedding_column = fc._embedding_column( + categorical_column, + dimension=embedding_dimension, initializer=_initializer) with ops.Graph().as_default(): @@ -5100,11 +5253,13 @@ class EmbeddingColumnTest(test.TestCase): 'linear_model/aaa_embedding/weights:0'] with _initialized_session(): # Predictions with all zero weights. - self.assertAllClose(np.zeros((1,)), bias.eval()) - self.assertAllClose(zeros_embedding_values, embedding_weights.eval()) + self.assertAllClose(np.zeros((1,)), self.evaluate(bias)) + self.assertAllClose(zeros_embedding_values, + self.evaluate(embedding_weights)) + self.assertAllClose( + np.zeros((embedding_dimension, 1)), self.evaluate(linear_weights)) self.assertAllClose( - np.zeros((embedding_dimension, 1)), linear_weights.eval()) - self.assertAllClose(np.zeros((batch_size, 1)), predictions.eval()) + np.zeros((batch_size, 1)), self.evaluate(predictions)) # Predictions with all non-zero weights. embedding_weights.assign(( @@ -5119,8 +5274,10 @@ class EmbeddingColumnTest(test.TestCase): # example 3, ids [1], embedding[3] = [3, 5] # sum(embeddings * linear_weights) # = [4*7 + 6*11, 4*2 + 6*3.5, 4*0 + 6*0, 4*3 + 6*5] = [94, 29, 0, 42] - self.assertAllClose(((94.,), (29.,), (0.,), (42.,)), predictions.eval()) + self.assertAllClose(((94.,), (29.,), (0.,), (42.,)), + self.evaluate(predictions)) + @test_util.run_deprecated_v1 def test_keras_linear_model(self): # Inputs. batch_size = 4 @@ -5146,9 +5303,9 @@ class EmbeddingColumnTest(test.TestCase): return zeros_embedding_values # Build columns. - categorical_column = fc.categorical_column_with_identity( + categorical_column = fc._categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - embedding_column = fc.embedding_column( + embedding_column = fc._embedding_column( categorical_column, dimension=embedding_dimension, initializer=_initializer) @@ -5176,11 +5333,13 @@ class EmbeddingColumnTest(test.TestCase): linear_weights = trainable_vars['linear_model/aaa_embedding/weights:0'] with _initialized_session(): # Predictions with all zero weights. - self.assertAllClose(np.zeros((1,)), bias.eval()) - self.assertAllClose(zeros_embedding_values, embedding_weights.eval()) + self.assertAllClose(np.zeros((1,)), self.evaluate(bias)) + self.assertAllClose(zeros_embedding_values, + self.evaluate(embedding_weights)) self.assertAllClose( - np.zeros((embedding_dimension, 1)), linear_weights.eval()) - self.assertAllClose(np.zeros((batch_size, 1)), predictions.eval()) + np.zeros((embedding_dimension, 1)), self.evaluate(linear_weights)) + self.assertAllClose( + np.zeros((batch_size, 1)), self.evaluate(predictions)) # Predictions with all non-zero weights. embedding_weights.assign(( @@ -5195,8 +5354,10 @@ class EmbeddingColumnTest(test.TestCase): # example 3, ids [1], embedding[3] = [3, 5] # sum(embeddings * linear_weights) # = [4*7 + 6*11, 4*2 + 6*3.5, 4*0 + 6*0, 4*3 + 6*5] = [94, 29, 0, 42] - self.assertAllClose(((94.,), (29.,), (0.,), (42.,)), predictions.eval()) + self.assertAllClose(((94.,), (29.,), (0.,), (42.,)), + self.evaluate(predictions)) + @test_util.run_deprecated_v1 def test_input_layer(self): # Inputs. vocabulary_size = 3 @@ -5235,10 +5396,11 @@ class EmbeddingColumnTest(test.TestCase): ) # Build columns. - categorical_column = fc.categorical_column_with_identity( + categorical_column = fc._categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - embedding_column = fc.embedding_column( - categorical_column, dimension=embedding_dimension, + embedding_column = fc._embedding_column( + categorical_column, + dimension=embedding_dimension, initializer=_initializer) # Provide sparse input and get dense result. @@ -5255,8 +5417,9 @@ class EmbeddingColumnTest(test.TestCase): tuple([v.name for v in trainable_vars])) with _initialized_session(): self.assertAllEqual(embedding_values, trainable_vars[0].eval()) - self.assertAllEqual(expected_lookups, input_layer.eval()) + self.assertAllEqual(expected_lookups, self.evaluate(input_layer)) + @test_util.run_deprecated_v1 def test_input_layer_not_trainable(self): # Inputs. vocabulary_size = 3 @@ -5295,11 +5458,13 @@ class EmbeddingColumnTest(test.TestCase): ) # Build columns. - categorical_column = fc.categorical_column_with_identity( + categorical_column = fc._categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - embedding_column = fc.embedding_column( - categorical_column, dimension=embedding_dimension, - initializer=_initializer, trainable=False) + embedding_column = fc._embedding_column( + categorical_column, + dimension=embedding_dimension, + initializer=_initializer, + trainable=False) # Provide sparse input and get dense result. input_layer = fc.input_layer({'aaa': sparse_input}, (embedding_column,)) @@ -5313,18 +5478,19 @@ class EmbeddingColumnTest(test.TestCase): [], ops.get_collection(ops.GraphKeys.TRAINABLE_VARIABLES)) with _initialized_session(): self.assertAllEqual(embedding_values, global_vars[0].eval()) - self.assertAllEqual(expected_lookups, input_layer.eval()) + self.assertAllEqual(expected_lookups, self.evaluate(input_layer)) class SharedEmbeddingColumnTest(test.TestCase): + @test_util.run_deprecated_v1 def test_defaults(self): - categorical_column_a = fc.categorical_column_with_identity( + categorical_column_a = fc._categorical_column_with_identity( key='aaa', num_buckets=3) - categorical_column_b = fc.categorical_column_with_identity( + categorical_column_b = fc._categorical_column_with_identity( key='bbb', num_buckets=3) embedding_dimension = 2 - embedding_column_b, embedding_column_a = fc.shared_embedding_columns( + embedding_column_b, embedding_column_a = fc_new.shared_embedding_columns( [categorical_column_b, categorical_column_a], dimension=embedding_dimension) self.assertIs(categorical_column_a, embedding_column_a.categorical_column) @@ -5362,13 +5528,14 @@ class SharedEmbeddingColumnTest(test.TestCase): 'bbb': parsing_ops.VarLenFeature(dtypes.int64) }, embedding_column_b._parse_example_spec) + @test_util.run_deprecated_v1 def test_all_constructor_args(self): - categorical_column_a = fc.categorical_column_with_identity( + categorical_column_a = fc._categorical_column_with_identity( key='aaa', num_buckets=3) - categorical_column_b = fc.categorical_column_with_identity( + categorical_column_b = fc._categorical_column_with_identity( key='bbb', num_buckets=3) embedding_dimension = 2 - embedding_column_a, embedding_column_b = fc.shared_embedding_columns( + embedding_column_a, embedding_column_b = fc_new.shared_embedding_columns( [categorical_column_a, categorical_column_b], dimension=embedding_dimension, combiner='my_combiner', @@ -5413,13 +5580,14 @@ class SharedEmbeddingColumnTest(test.TestCase): 'bbb': parsing_ops.VarLenFeature(dtypes.int64) }, embedding_column_b._parse_example_spec) + @test_util.run_deprecated_v1 def test_deep_copy(self): - categorical_column_a = fc.categorical_column_with_identity( + categorical_column_a = fc._categorical_column_with_identity( key='aaa', num_buckets=3) - categorical_column_b = fc.categorical_column_with_identity( + categorical_column_b = fc._categorical_column_with_identity( key='bbb', num_buckets=3) embedding_dimension = 2 - original_a, _ = fc.shared_embedding_columns( + original_a, _ = fc_new.shared_embedding_columns( [categorical_column_a, categorical_column_b], dimension=embedding_dimension, combiner='my_combiner', @@ -5427,7 +5595,8 @@ class SharedEmbeddingColumnTest(test.TestCase): shared_embedding_collection_name='shared_embedding_collection_name', ckpt_to_load_from='my_ckpt', tensor_name_in_ckpt='my_ckpt_tensor', - max_norm=42., trainable=False) + max_norm=42., + trainable=False) for embedding_column_a in (original_a, copy.deepcopy(original_a)): self.assertEqual('aaa', embedding_column_a.categorical_column.name) self.assertEqual(3, embedding_column_a.categorical_column._num_buckets) @@ -5450,55 +5619,60 @@ class SharedEmbeddingColumnTest(test.TestCase): 'aaa': parsing_ops.VarLenFeature(dtypes.int64) }, embedding_column_a._parse_example_spec) + @test_util.run_deprecated_v1 def test_invalid_initializer(self): - categorical_column_a = fc.categorical_column_with_identity( + categorical_column_a = fc._categorical_column_with_identity( key='aaa', num_buckets=3) - categorical_column_b = fc.categorical_column_with_identity( + categorical_column_b = fc._categorical_column_with_identity( key='bbb', num_buckets=3) with self.assertRaisesRegexp(ValueError, 'initializer must be callable'): - fc.shared_embedding_columns( - [categorical_column_a, categorical_column_b], dimension=2, + fc_new.shared_embedding_columns( + [categorical_column_a, categorical_column_b], + dimension=2, initializer='not_fn') + @test_util.run_deprecated_v1 def test_incompatible_column_type(self): - categorical_column_a = fc.categorical_column_with_identity( + categorical_column_a = fc._categorical_column_with_identity( key='aaa', num_buckets=3) - categorical_column_b = fc.categorical_column_with_identity( + categorical_column_b = fc._categorical_column_with_identity( key='bbb', num_buckets=3) - categorical_column_c = fc.categorical_column_with_hash_bucket( + categorical_column_c = fc._categorical_column_with_hash_bucket( key='ccc', hash_bucket_size=3) with self.assertRaisesRegexp( ValueError, 'all categorical_columns must have the same type.*' '_IdentityCategoricalColumn.*_HashedCategoricalColumn'): - fc.shared_embedding_columns( + fc_new.shared_embedding_columns( [categorical_column_a, categorical_column_b, categorical_column_c], dimension=2) + @test_util.run_deprecated_v1 def test_weighted_categorical_column_ok(self): - categorical_column_a = fc.categorical_column_with_identity( + categorical_column_a = fc._categorical_column_with_identity( key='aaa', num_buckets=3) - weighted_categorical_column_a = fc.weighted_categorical_column( + weighted_categorical_column_a = fc._weighted_categorical_column( categorical_column_a, weight_feature_key='aaa_weights') - categorical_column_b = fc.categorical_column_with_identity( + categorical_column_b = fc._categorical_column_with_identity( key='bbb', num_buckets=3) - weighted_categorical_column_b = fc.weighted_categorical_column( + weighted_categorical_column_b = fc._weighted_categorical_column( categorical_column_b, weight_feature_key='bbb_weights') - fc.shared_embedding_columns( + fc_new.shared_embedding_columns( [weighted_categorical_column_a, categorical_column_b], dimension=2) - fc.shared_embedding_columns( + fc_new.shared_embedding_columns( [categorical_column_a, weighted_categorical_column_b], dimension=2) - fc.shared_embedding_columns( + fc_new.shared_embedding_columns( [weighted_categorical_column_a, weighted_categorical_column_b], dimension=2) + @test_util.run_deprecated_v1 def test_parse_example(self): - a = fc.categorical_column_with_vocabulary_list( + a = fc._categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=('omar', 'stringer', 'marlo')) - b = fc.categorical_column_with_vocabulary_list( + b = fc._categorical_column_with_vocabulary_list( key='bbb', vocabulary_list=('omar', 'stringer', 'marlo')) - a_embedded, b_embedded = fc.shared_embedding_columns( - [a, b], dimension=2) + a_embedded, b_embedded = fc_new.shared_embedding_columns([a, b], + dimension=2) data = example_pb2.Example(features=feature_pb2.Features( feature={ 'aaa': @@ -5529,11 +5703,12 @@ class SharedEmbeddingColumnTest(test.TestCase): dense_shape=[1, 2]), features['bbb'].eval()) + @test_util.run_deprecated_v1 def test_transform_feature(self): - a = fc.categorical_column_with_identity(key='aaa', num_buckets=3) - b = fc.categorical_column_with_identity(key='bbb', num_buckets=3) - a_embedded, b_embedded = fc.shared_embedding_columns( - [a, b], dimension=2) + a = fc._categorical_column_with_identity(key='aaa', num_buckets=3) + b = fc._categorical_column_with_identity(key='bbb', num_buckets=3) + a_embedded, b_embedded = fc_new.shared_embedding_columns([a, b], + dimension=2) features = { 'aaa': sparse_tensor.SparseTensor( indices=((0, 0), (1, 0), (1, 1)), @@ -5550,11 +5725,12 @@ class SharedEmbeddingColumnTest(test.TestCase): output_b = outputs[b] output_b_embedded = outputs[b_embedded] with _initialized_session(): - _assert_sparse_tensor_value( - self, output_a.eval(), output_a_embedded.eval()) - _assert_sparse_tensor_value( - self, output_b.eval(), output_b_embedded.eval()) + _assert_sparse_tensor_value(self, self.evaluate(output_a), + self.evaluate(output_a_embedded)) + _assert_sparse_tensor_value(self, self.evaluate(output_b), + self.evaluate(output_b_embedded)) + @test_util.run_deprecated_v1 def test_get_dense_tensor(self): # Inputs. vocabulary_size = 3 @@ -5598,13 +5774,14 @@ class SharedEmbeddingColumnTest(test.TestCase): ) # Build columns. - categorical_column_a = fc.categorical_column_with_identity( + categorical_column_a = fc._categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - categorical_column_b = fc.categorical_column_with_identity( + categorical_column_b = fc._categorical_column_with_identity( key='bbb', num_buckets=vocabulary_size) - embedding_column_a, embedding_column_b = fc.shared_embedding_columns( + embedding_column_a, embedding_column_b = fc_new.shared_embedding_columns( [categorical_column_a, categorical_column_b], - dimension=embedding_dimension, initializer=_initializer) + dimension=embedding_dimension, + initializer=_initializer) # Provide sparse input and get dense result. embedding_lookup_a = embedding_column_a._get_dense_tensor( @@ -5618,10 +5795,11 @@ class SharedEmbeddingColumnTest(test.TestCase): tuple([v.name for v in global_vars])) embedding_var = global_vars[0] with _initialized_session(): - self.assertAllEqual(embedding_values, embedding_var.eval()) - self.assertAllEqual(expected_lookups_a, embedding_lookup_a.eval()) - self.assertAllEqual(expected_lookups_b, embedding_lookup_b.eval()) + self.assertAllEqual(embedding_values, self.evaluate(embedding_var)) + self.assertAllEqual(expected_lookups_a, self.evaluate(embedding_lookup_a)) + self.assertAllEqual(expected_lookups_b, self.evaluate(embedding_lookup_b)) + @test_util.run_deprecated_v1 def test_get_dense_tensor_weight_collections(self): # Inputs. vocabulary_size = 3 @@ -5651,11 +5829,11 @@ class SharedEmbeddingColumnTest(test.TestCase): return embedding_values # Build columns. - categorical_column_a = fc.categorical_column_with_identity( + categorical_column_a = fc._categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - categorical_column_b = fc.categorical_column_with_identity( + categorical_column_b = fc._categorical_column_with_identity( key='bbb', num_buckets=vocabulary_size) - embedding_column_a, embedding_column_b = fc.shared_embedding_columns( + embedding_column_a, embedding_column_b = fc_new.shared_embedding_columns( [categorical_column_a, categorical_column_b], dimension=embedding_dimension, initializer=_initializer) @@ -5674,6 +5852,7 @@ class SharedEmbeddingColumnTest(test.TestCase): ('input_layer/aaa_bbb_shared_embedding/embedding_weights:0',), tuple(v.name for v in my_vars)) + @test_util.run_deprecated_v1 def test_get_dense_tensor_placeholder_inputs(self): # Inputs. vocabulary_size = 3 @@ -5712,13 +5891,14 @@ class SharedEmbeddingColumnTest(test.TestCase): return embedding_values # Build columns. - categorical_column_a = fc.categorical_column_with_identity( + categorical_column_a = fc._categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - categorical_column_b = fc.categorical_column_with_identity( + categorical_column_b = fc._categorical_column_with_identity( key='bbb', num_buckets=vocabulary_size) - embedding_column_a, embedding_column_b = fc.shared_embedding_columns( + embedding_column_a, embedding_column_b = fc_new.shared_embedding_columns( [categorical_column_a, categorical_column_b], - dimension=embedding_dimension, initializer=_initializer) + dimension=embedding_dimension, + initializer=_initializer) # Provide sparse input and get dense result. embedding_lookup_a = embedding_column_a._get_dense_tensor( @@ -5729,6 +5909,7 @@ class SharedEmbeddingColumnTest(test.TestCase): with _initialized_session() as sess: sess.run([embedding_lookup_a, embedding_lookup_b], feed_dict=feed_dict) + @test_util.run_deprecated_v1 def test_linear_model(self): # Inputs. batch_size = 2 @@ -5752,13 +5933,14 @@ class SharedEmbeddingColumnTest(test.TestCase): return zeros_embedding_values # Build columns. - categorical_column_a = fc.categorical_column_with_identity( + categorical_column_a = fc._categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - categorical_column_b = fc.categorical_column_with_identity( + categorical_column_b = fc._categorical_column_with_identity( key='bbb', num_buckets=vocabulary_size) - embedding_column_a, embedding_column_b = fc.shared_embedding_columns( + embedding_column_a, embedding_column_b = fc_new.shared_embedding_columns( [categorical_column_a, categorical_column_b], - dimension=embedding_dimension, initializer=_initializer) + dimension=embedding_dimension, + initializer=_initializer) with ops.Graph().as_default(): predictions = fc.linear_model({ @@ -5790,13 +5972,15 @@ class SharedEmbeddingColumnTest(test.TestCase): 'linear_model/aaa_bbb_shared_embedding_1/weights:0'] with _initialized_session(): # Predictions with all zero weights. - self.assertAllClose(np.zeros((1,)), bias.eval()) - self.assertAllClose(zeros_embedding_values, embedding_weights.eval()) + self.assertAllClose(np.zeros((1,)), self.evaluate(bias)) + self.assertAllClose(zeros_embedding_values, + self.evaluate(embedding_weights)) + self.assertAllClose( + np.zeros((embedding_dimension, 1)), self.evaluate(linear_weights_a)) self.assertAllClose( - np.zeros((embedding_dimension, 1)), linear_weights_a.eval()) + np.zeros((embedding_dimension, 1)), self.evaluate(linear_weights_b)) self.assertAllClose( - np.zeros((embedding_dimension, 1)), linear_weights_b.eval()) - self.assertAllClose(np.zeros((batch_size, 1)), predictions.eval()) + np.zeros((batch_size, 1)), self.evaluate(predictions)) # Predictions with all non-zero weights. embedding_weights.assign(( @@ -5814,8 +5998,9 @@ class SharedEmbeddingColumnTest(test.TestCase): # example 1, ids [], embedding[1] = 0, 0] # sum(embeddings * linear_weights) # = [3*1 + 5*2, 3*0 +5*0] = [13, 0] - self.assertAllClose([[94. + 13.], [29.]], predictions.eval()) + self.assertAllClose([[94. + 13.], [29.]], self.evaluate(predictions)) + @test_util.run_deprecated_v1 def test_keras_linear_model(self): # Inputs. batch_size = 2 @@ -5842,11 +6027,11 @@ class SharedEmbeddingColumnTest(test.TestCase): return zeros_embedding_values # Build columns. - categorical_column_a = fc.categorical_column_with_identity( + categorical_column_a = fc._categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - categorical_column_b = fc.categorical_column_with_identity( + categorical_column_b = fc._categorical_column_with_identity( key='bbb', num_buckets=vocabulary_size) - embedding_column_a, embedding_column_b = fc.shared_embedding_columns( + embedding_column_a, embedding_column_b = fc_new.shared_embedding_columns( [categorical_column_a, categorical_column_b], dimension=embedding_dimension, initializer=_initializer) @@ -5881,13 +6066,15 @@ class SharedEmbeddingColumnTest(test.TestCase): 'linear_model/aaa_bbb_shared_embedding_1/weights:0'] with _initialized_session(): # Predictions with all zero weights. - self.assertAllClose(np.zeros((1,)), bias.eval()) - self.assertAllClose(zeros_embedding_values, embedding_weights.eval()) + self.assertAllClose(np.zeros((1,)), self.evaluate(bias)) + self.assertAllClose(zeros_embedding_values, + self.evaluate(embedding_weights)) self.assertAllClose( - np.zeros((embedding_dimension, 1)), linear_weights_a.eval()) + np.zeros((embedding_dimension, 1)), self.evaluate(linear_weights_a)) self.assertAllClose( - np.zeros((embedding_dimension, 1)), linear_weights_b.eval()) - self.assertAllClose(np.zeros((batch_size, 1)), predictions.eval()) + np.zeros((embedding_dimension, 1)), self.evaluate(linear_weights_b)) + self.assertAllClose( + np.zeros((batch_size, 1)), self.evaluate(predictions)) # Predictions with all non-zero weights. embedding_weights.assign(( @@ -5905,7 +6092,7 @@ class SharedEmbeddingColumnTest(test.TestCase): # example 1, ids [], embedding[1] = 0, 0] # sum(embeddings * linear_weights) # = [3*1 + 5*2, 3*0 +5*0] = [13, 0] - self.assertAllClose([[94. + 13.], [29.]], predictions.eval()) + self.assertAllClose([[94. + 13.], [29.]], self.evaluate(predictions)) def _test_input_layer(self, trainable=True): # Inputs. @@ -5949,13 +6136,14 @@ class SharedEmbeddingColumnTest(test.TestCase): ) # Build columns. - categorical_column_a = fc.categorical_column_with_identity( + categorical_column_a = fc._categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) - categorical_column_b = fc.categorical_column_with_identity( + categorical_column_b = fc._categorical_column_with_identity( key='bbb', num_buckets=vocabulary_size) - embedding_column_a, embedding_column_b = fc.shared_embedding_columns( + embedding_column_a, embedding_column_b = fc_new.shared_embedding_columns( [categorical_column_a, categorical_column_b], - dimension=embedding_dimension, initializer=_initializer, + dimension=embedding_dimension, + initializer=_initializer, trainable=trainable) # Provide sparse input and get dense result. @@ -5978,20 +6166,23 @@ class SharedEmbeddingColumnTest(test.TestCase): shared_embedding_vars = global_vars with _initialized_session(): self.assertAllEqual(embedding_values, shared_embedding_vars[0].eval()) - self.assertAllEqual(expected_lookups, input_layer.eval()) + self.assertAllEqual(expected_lookups, self.evaluate(input_layer)) + @test_util.run_deprecated_v1 def test_input_layer(self): self._test_input_layer() + @test_util.run_deprecated_v1 def test_input_layer_no_trainable(self): self._test_input_layer(trainable=False) class WeightedCategoricalColumnTest(test.TestCase): + @test_util.run_deprecated_v1 def test_defaults(self): - column = fc.weighted_categorical_column( - categorical_column=fc.categorical_column_with_identity( + column = fc._weighted_categorical_column( + categorical_column=fc._categorical_column_with_identity( key='ids', num_buckets=3), weight_feature_key='values') self.assertEqual('ids_weighted_by_values', column.name) @@ -6002,10 +6193,11 @@ class WeightedCategoricalColumnTest(test.TestCase): 'values': parsing_ops.VarLenFeature(dtypes.float32) }, column._parse_example_spec) + @test_util.run_deprecated_v1 def test_deep_copy(self): """Tests deepcopy of categorical_column_with_hash_bucket.""" - original = fc.weighted_categorical_column( - categorical_column=fc.categorical_column_with_identity( + original = fc._weighted_categorical_column( + categorical_column=fc._categorical_column_with_identity( key='ids', num_buckets=3), weight_feature_key='values') for column in (original, copy.deepcopy(original)): @@ -6018,23 +6210,23 @@ class WeightedCategoricalColumnTest(test.TestCase): def test_invalid_dtype_none(self): with self.assertRaisesRegexp(ValueError, 'is not convertible to float'): - fc.weighted_categorical_column( - categorical_column=fc.categorical_column_with_identity( + fc._weighted_categorical_column( + categorical_column=fc._categorical_column_with_identity( key='ids', num_buckets=3), weight_feature_key='values', dtype=None) def test_invalid_dtype_string(self): with self.assertRaisesRegexp(ValueError, 'is not convertible to float'): - fc.weighted_categorical_column( - categorical_column=fc.categorical_column_with_identity( + fc._weighted_categorical_column( + categorical_column=fc._categorical_column_with_identity( key='ids', num_buckets=3), weight_feature_key='values', dtype=dtypes.string) def test_invalid_input_dtype(self): - column = fc.weighted_categorical_column( - categorical_column=fc.categorical_column_with_identity( + column = fc._weighted_categorical_column( + categorical_column=fc._categorical_column_with_identity( key='ids', num_buckets=3), weight_feature_key='values') strings = sparse_tensor.SparseTensorValue( @@ -6046,14 +6238,14 @@ class WeightedCategoricalColumnTest(test.TestCase): def test_column_name_collision(self): with self.assertRaisesRegexp(ValueError, r'Parse config.*already exists'): - fc.weighted_categorical_column( - categorical_column=fc.categorical_column_with_identity( + fc._weighted_categorical_column( + categorical_column=fc._categorical_column_with_identity( key='aaa', num_buckets=3), weight_feature_key='aaa')._parse_example_spec() def test_missing_weights(self): - column = fc.weighted_categorical_column( - categorical_column=fc.categorical_column_with_identity( + column = fc._weighted_categorical_column( + categorical_column=fc._categorical_column_with_identity( key='ids', num_buckets=3), weight_feature_key='values') inputs = sparse_tensor.SparseTensorValue( @@ -6064,10 +6256,12 @@ class WeightedCategoricalColumnTest(test.TestCase): ValueError, 'values is not in features dictionary'): _transform_features({'ids': inputs}, (column,)) + @test_util.run_deprecated_v1 def test_parse_example(self): - a = fc.categorical_column_with_vocabulary_list( + a = fc._categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=('omar', 'stringer', 'marlo')) - a_weighted = fc.weighted_categorical_column(a, weight_feature_key='weights') + a_weighted = fc._weighted_categorical_column( + a, weight_feature_key='weights') data = example_pb2.Example(features=feature_pb2.Features( feature={ 'aaa': @@ -6098,9 +6292,10 @@ class WeightedCategoricalColumnTest(test.TestCase): dense_shape=[1, 2]), features['weights'].eval()) + @test_util.run_deprecated_v1 def test_transform_features(self): - column = fc.weighted_categorical_column( - categorical_column=fc.categorical_column_with_identity( + column = fc._weighted_categorical_column( + categorical_column=fc._categorical_column_with_identity( key='ids', num_buckets=3), weight_feature_key='values') inputs = sparse_tensor.SparseTensorValue( @@ -6121,19 +6316,18 @@ class WeightedCategoricalColumnTest(test.TestCase): sparse_tensor.SparseTensorValue( indices=inputs.indices, values=np.array(inputs.values, dtype=np.int64), - dense_shape=inputs.dense_shape), - id_tensor.eval()) + dense_shape=inputs.dense_shape), self.evaluate(id_tensor)) _assert_sparse_tensor_value( self, sparse_tensor.SparseTensorValue( indices=weights.indices, values=np.array(weights.values, dtype=np.float32), - dense_shape=weights.dense_shape), - weight_tensor.eval()) + dense_shape=weights.dense_shape), self.evaluate(weight_tensor)) + @test_util.run_deprecated_v1 def test_transform_features_dense_input(self): - column = fc.weighted_categorical_column( - categorical_column=fc.categorical_column_with_identity( + column = fc._weighted_categorical_column( + categorical_column=fc._categorical_column_with_identity( key='ids', num_buckets=3), weight_feature_key='values') weights = sparse_tensor.SparseTensorValue( @@ -6150,19 +6344,18 @@ class WeightedCategoricalColumnTest(test.TestCase): sparse_tensor.SparseTensorValue( indices=((0, 0), (1, 0), (1, 1)), values=np.array((0, 1, 0), dtype=np.int64), - dense_shape=(2, 2)), - id_tensor.eval()) + dense_shape=(2, 2)), self.evaluate(id_tensor)) _assert_sparse_tensor_value( self, sparse_tensor.SparseTensorValue( indices=weights.indices, values=np.array(weights.values, dtype=np.float32), - dense_shape=weights.dense_shape), - weight_tensor.eval()) + dense_shape=weights.dense_shape), self.evaluate(weight_tensor)) + @test_util.run_deprecated_v1 def test_transform_features_dense_weights(self): - column = fc.weighted_categorical_column( - categorical_column=fc.categorical_column_with_identity( + column = fc._weighted_categorical_column( + categorical_column=fc._categorical_column_with_identity( key='ids', num_buckets=3), weight_feature_key='values') inputs = sparse_tensor.SparseTensorValue( @@ -6179,19 +6372,18 @@ class WeightedCategoricalColumnTest(test.TestCase): sparse_tensor.SparseTensorValue( indices=inputs.indices, values=np.array(inputs.values, dtype=np.int64), - dense_shape=inputs.dense_shape), - id_tensor.eval()) + dense_shape=inputs.dense_shape), self.evaluate(id_tensor)) _assert_sparse_tensor_value( self, sparse_tensor.SparseTensorValue( indices=((0, 0), (1, 0), (1, 1)), values=np.array((.5, 1., .1), dtype=np.float32), - dense_shape=(2, 2)), - weight_tensor.eval()) + dense_shape=(2, 2)), self.evaluate(weight_tensor)) + @test_util.run_deprecated_v1 def test_keras_linear_model(self): - column = fc.weighted_categorical_column( - categorical_column=fc.categorical_column_with_identity( + column = fc._weighted_categorical_column( + categorical_column=fc._categorical_column_with_identity( key='ids', num_buckets=3), weight_feature_key='values') with ops.Graph().as_default(): @@ -6210,18 +6402,18 @@ class WeightedCategoricalColumnTest(test.TestCase): bias = get_linear_model_bias() weight_var = get_linear_model_column_var(column) with _initialized_session(): - self.assertAllClose((0.,), bias.eval()) - self.assertAllClose(((0.,), (0.,), (0.,)), weight_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) + self.assertAllClose((0.,), self.evaluate(bias)) + self.assertAllClose(((0.,), (0.,), (0.,)), self.evaluate(weight_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) weight_var.assign(((1.,), (2.,), (3.,))).eval() # weight_var[0] * weights[0, 0] = 1 * .5 = .5 # weight_var[2] * weights[1, 0] + weight_var[1] * weights[1, 1] # = 3*1 + 2*.1 = 3+.2 = 3.2 - self.assertAllClose(((.5,), (3.2,)), predictions.eval()) + self.assertAllClose(((.5,), (3.2,)), self.evaluate(predictions)) def test_keras_linear_model_mismatched_shape(self): - column = fc.weighted_categorical_column( - categorical_column=fc.categorical_column_with_identity( + column = fc._weighted_categorical_column( + categorical_column=fc._categorical_column_with_identity( key='ids', num_buckets=3), weight_feature_key='values') with ops.Graph().as_default(): @@ -6241,8 +6433,8 @@ class WeightedCategoricalColumnTest(test.TestCase): }, (column,)) def test_keras_linear_model_mismatched_dense_values(self): - column = fc.weighted_categorical_column( - categorical_column=fc.categorical_column_with_identity( + column = fc._weighted_categorical_column( + categorical_column=fc._categorical_column_with_identity( key='ids', num_buckets=3), weight_feature_key='values') with ops.Graph().as_default(): @@ -6263,11 +6455,11 @@ class WeightedCategoricalColumnTest(test.TestCase): rewriter_config_pb2.RewriterConfig.OFF) with _initialized_session(config): with self.assertRaisesRegexp(errors.OpError, 'Incompatible shapes'): - predictions.eval() + self.evaluate(predictions) def test_keras_linear_model_mismatched_dense_shape(self): - column = fc.weighted_categorical_column( - categorical_column=fc.categorical_column_with_identity( + column = fc._weighted_categorical_column( + categorical_column=fc._categorical_column_with_identity( key='ids', num_buckets=3), weight_feature_key='values') with ops.Graph().as_default(): @@ -6282,18 +6474,19 @@ class WeightedCategoricalColumnTest(test.TestCase): bias = get_linear_model_bias() weight_var = get_linear_model_column_var(column) with _initialized_session(): - self.assertAllClose((0.,), bias.eval()) - self.assertAllClose(((0.,), (0.,), (0.,)), weight_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) + self.assertAllClose((0.,), self.evaluate(bias)) + self.assertAllClose(((0.,), (0.,), (0.,)), self.evaluate(weight_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) weight_var.assign(((1.,), (2.,), (3.,))).eval() # weight_var[0] * weights[0, 0] = 1 * .5 = .5 # weight_var[2] * weights[1, 0] + weight_var[1] * weights[1, 1] # = 3*1 + 2*.1 = 3+.2 = 3.2 - self.assertAllClose(((.5,), (3.2,)), predictions.eval()) + self.assertAllClose(((.5,), (3.2,)), self.evaluate(predictions)) + @test_util.run_deprecated_v1 def test_linear_model(self): - column = fc.weighted_categorical_column( - categorical_column=fc.categorical_column_with_identity( + column = fc._weighted_categorical_column( + categorical_column=fc._categorical_column_with_identity( key='ids', num_buckets=3), weight_feature_key='values') with ops.Graph().as_default(): @@ -6310,18 +6503,18 @@ class WeightedCategoricalColumnTest(test.TestCase): bias = get_linear_model_bias() weight_var = get_linear_model_column_var(column) with _initialized_session(): - self.assertAllClose((0.,), bias.eval()) - self.assertAllClose(((0.,), (0.,), (0.,)), weight_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) + self.assertAllClose((0.,), self.evaluate(bias)) + self.assertAllClose(((0.,), (0.,), (0.,)), self.evaluate(weight_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) weight_var.assign(((1.,), (2.,), (3.,))).eval() # weight_var[0] * weights[0, 0] = 1 * .5 = .5 # weight_var[2] * weights[1, 0] + weight_var[1] * weights[1, 1] # = 3*1 + 2*.1 = 3+.2 = 3.2 - self.assertAllClose(((.5,), (3.2,)), predictions.eval()) + self.assertAllClose(((.5,), (3.2,)), self.evaluate(predictions)) def test_linear_model_mismatched_shape(self): - column = fc.weighted_categorical_column( - categorical_column=fc.categorical_column_with_identity( + column = fc._weighted_categorical_column( + categorical_column=fc._categorical_column_with_identity( key='ids', num_buckets=3), weight_feature_key='values') with ops.Graph().as_default(): @@ -6339,8 +6532,8 @@ class WeightedCategoricalColumnTest(test.TestCase): }, (column,)) def test_linear_model_mismatched_dense_values(self): - column = fc.weighted_categorical_column( - categorical_column=fc.categorical_column_with_identity( + column = fc._weighted_categorical_column( + categorical_column=fc._categorical_column_with_identity( key='ids', num_buckets=3), weight_feature_key='values') with ops.Graph().as_default(): @@ -6361,11 +6554,11 @@ class WeightedCategoricalColumnTest(test.TestCase): rewriter_config_pb2.RewriterConfig.OFF) with _initialized_session(config): with self.assertRaisesRegexp(errors.OpError, 'Incompatible shapes'): - predictions.eval() + self.evaluate(predictions) def test_linear_model_mismatched_dense_shape(self): - column = fc.weighted_categorical_column( - categorical_column=fc.categorical_column_with_identity( + column = fc._weighted_categorical_column( + categorical_column=fc._categorical_column_with_identity( key='ids', num_buckets=3), weight_feature_key='values') with ops.Graph().as_default(): @@ -6379,14 +6572,14 @@ class WeightedCategoricalColumnTest(test.TestCase): bias = get_linear_model_bias() weight_var = get_linear_model_column_var(column) with _initialized_session(): - self.assertAllClose((0.,), bias.eval()) - self.assertAllClose(((0.,), (0.,), (0.,)), weight_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) + self.assertAllClose((0.,), self.evaluate(bias)) + self.assertAllClose(((0.,), (0.,), (0.,)), self.evaluate(weight_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) weight_var.assign(((1.,), (2.,), (3.,))).eval() # weight_var[0] * weights[0, 0] = 1 * .5 = .5 # weight_var[2] * weights[1, 0] + weight_var[1] * weights[1, 1] # = 3*1 + 2*.1 = 3+.2 = 3.2 - self.assertAllClose(((.5,), (3.2,)), predictions.eval()) + self.assertAllClose(((.5,), (3.2,)), self.evaluate(predictions)) # TODO(ptucker): Add test with embedding of weighted categorical. diff --git a/tensorflow/python/feature_column/feature_column_v2.py b/tensorflow/python/feature_column/feature_column_v2.py index 9b4a7e882f..6308926494 100644 --- a/tensorflow/python/feature_column/feature_column_v2.py +++ b/tensorflow/python/feature_column/feature_column_v2.py @@ -165,6 +165,7 @@ from tensorflow.python.training import checkpoint_utils from tensorflow.python.training.checkpointable import tracking from tensorflow.python.util import deprecation from tensorflow.python.util import nest +from tensorflow.python.util.tf_export import tf_export _FEATURE_COLUMN_DEPRECATION_DATE = '2018-11-30' @@ -258,7 +259,7 @@ class StateManager(object): class _StateManagerImpl(StateManager): - """Manages the state of FeatureLayer and LinearModel.""" + """Manages the state of DenseFeatures and LinearLayer.""" def __init__(self, layer, trainable): """Creates an _StateManagerImpl object. @@ -302,7 +303,8 @@ class _StateManagerImpl(StateManager): raise ValueError('Variable does not exist.') -class FeatureLayer(Layer): +@tf_export('keras.layers.DenseFeatures', v1=[]) +class DenseFeatures(Layer): """A layer that produces a dense `Tensor` based on given `feature_columns`. Generally a single example in training data is described with FeatureColumns. @@ -318,7 +320,7 @@ class FeatureLayer(Layer): keywords_embedded = embedding_column( categorical_column_with_hash_bucket("keywords", 10K), dimensions=16) columns = [price, keywords_embedded, ...] - feature_layer = FeatureLayer(columns) + feature_layer = DenseFeatures(columns) features = tf.parse_example(..., features=make_parse_example_spec(columns)) dense_tensor = feature_layer(features) @@ -333,7 +335,7 @@ class FeatureLayer(Layer): trainable=True, name=None, **kwargs): - """Constructs a FeatureLayer. + """Constructs a DenseFeatures. Args: feature_columns: An iterable containing the FeatureColumns to use as @@ -344,13 +346,14 @@ class FeatureLayer(Layer): `indicator_column`. trainable: If `True` also add the variable to the graph collection `GraphKeys.TRAINABLE_VARIABLES` (see `tf.Variable`). - name: Name to give to the FeatureLayer. + name: Name to give to the DenseFeatures. **kwargs: Keyword arguments to construct a layer. Raises: ValueError: if an item in `feature_columns` is not a `DenseColumn`. """ - super(FeatureLayer, self).__init__(name=name, trainable=trainable, **kwargs) + super(DenseFeatures, self).__init__( + name=name, trainable=trainable, **kwargs) self._feature_columns = _normalize_feature_columns(feature_columns) self._feature_columns = sorted(self._feature_columns, key=lambda x: x.name) @@ -371,7 +374,7 @@ class FeatureLayer(Layer): with variable_scope._pure_variable_scope(self.name): # pylint: disable=protected-access with variable_scope._pure_variable_scope(column.name): # pylint: disable=protected-access column.create_state(self._state_manager) - super(FeatureLayer, self).build(None) + super(DenseFeatures, self).build(None) def call(self, features, cols_to_output_tensors=None): """Returns a dense tensor corresponding to the `feature_columns`. @@ -515,6 +518,7 @@ class _LinearModelLayer(Layer): return predictions +@tf_export('keras.layers.LinearModel', v1=[]) class LinearModel(training.Model): """Produces a linear prediction `Tensor` based on given `feature_columns`. @@ -522,7 +526,7 @@ class LinearModel(training.Model): Weighted sum refers to logits in classification problems. It refers to the prediction itself for linear regression problems. - Note on supported columns: `LinearModel` treats categorical columns as + Note on supported columns: `LinearLayer` treats categorical columns as `indicator_column`s. To be specific, assume the input as `SparseTensor` looks like: @@ -547,7 +551,7 @@ class LinearModel(training.Model): keywords = categorical_column_with_hash_bucket("keywords", 10K) keywords_price = crossed_column('keywords', price_buckets, ...) columns = [price_buckets, keywords, keywords_price ...] - linear_model = LinearModel(columns) + linear_model = LinearLayer(columns) features = tf.parse_example(..., features=make_parse_example_spec(columns)) prediction = linear_model(features) @@ -561,7 +565,7 @@ class LinearModel(training.Model): trainable=True, name=None, **kwargs): - """Constructs a LinearModel. + """Constructs a LinearLayer. Args: feature_columns: An iterable containing the FeatureColumns to use as @@ -650,7 +654,7 @@ class LinearModel(training.Model): return self.layer.bias -def _transform_features(features, feature_columns, state_manager): +def _transform_features_v2(features, feature_columns, state_manager): """Returns transformed features based on features columns passed in. Please note that most probably you would not need to use this function. Please @@ -695,7 +699,8 @@ def _transform_features(features, feature_columns, state_manager): return outputs -def make_parse_example_spec(feature_columns): +@tf_export('feature_column.make_parse_example_spec', v1=[]) +def make_parse_example_spec_v2(feature_columns): """Creates parsing spec dictionary from input feature_columns. The returned dictionary can be used as arg 'features' in `tf.parse_example`. @@ -754,10 +759,15 @@ def make_parse_example_spec(feature_columns): return result -def embedding_column( - categorical_column, dimension, combiner='mean', initializer=None, - ckpt_to_load_from=None, tensor_name_in_ckpt=None, max_norm=None, - trainable=True): +@tf_export('feature_column.embedding_column') +def embedding_column(categorical_column, + dimension, + combiner='mean', + initializer=None, + ckpt_to_load_from=None, + tensor_name_in_ckpt=None, + max_norm=None, + trainable=True): """`DenseColumn` that converts from sparse, categorical input. Use this when your inputs are sparse, but you want to convert them to a dense @@ -854,6 +864,180 @@ def embedding_column( trainable=trainable) +@tf_export(v1=['feature_column.shared_embedding_columns']) +def shared_embedding_columns(categorical_columns, + dimension, + combiner='mean', + initializer=None, + shared_embedding_collection_name=None, + ckpt_to_load_from=None, + tensor_name_in_ckpt=None, + max_norm=None, + trainable=True): + """List of dense columns that convert from sparse, categorical input. + + This is similar to `embedding_column`, except that it produces a list of + embedding columns that share the same embedding weights. + + Use this when your inputs are sparse and of the same type (e.g. watched and + impression video IDs that share the same vocabulary), and you want to convert + them to a dense representation (e.g., to feed to a DNN). + + Inputs must be a list of categorical columns created by any of the + `categorical_column_*` function. They must all be of the same type and have + the same arguments except `key`. E.g. they can be + categorical_column_with_vocabulary_file with the same vocabulary_file. Some or + all columns could also be weighted_categorical_column. + + Here is an example embedding of two features for a DNNClassifier model: + + ```python + watched_video_id = categorical_column_with_vocabulary_file( + 'watched_video_id', video_vocabulary_file, video_vocabulary_size) + impression_video_id = categorical_column_with_vocabulary_file( + 'impression_video_id', video_vocabulary_file, video_vocabulary_size) + columns = shared_embedding_columns( + [watched_video_id, impression_video_id], dimension=10) + + estimator = tf.estimator.DNNClassifier(feature_columns=columns, ...) + + label_column = ... + def input_fn(): + features = tf.parse_example( + ..., features=make_parse_example_spec(columns + [label_column])) + labels = features.pop(label_column.name) + return features, labels + + estimator.train(input_fn=input_fn, steps=100) + ``` + + Here is an example using `shared_embedding_columns` with model_fn: + + ```python + def model_fn(features, ...): + watched_video_id = categorical_column_with_vocabulary_file( + 'watched_video_id', video_vocabulary_file, video_vocabulary_size) + impression_video_id = categorical_column_with_vocabulary_file( + 'impression_video_id', video_vocabulary_file, video_vocabulary_size) + columns = shared_embedding_columns( + [watched_video_id, impression_video_id], dimension=10) + dense_tensor = input_layer(features, columns) + # Form DNN layers, calculate loss, and return EstimatorSpec. + ... + ``` + + Args: + categorical_columns: List of categorical columns created by a + `categorical_column_with_*` function. These columns produce the sparse IDs + that are inputs to the embedding lookup. All columns must be of the same + type and have the same arguments except `key`. E.g. they can be + categorical_column_with_vocabulary_file with the same vocabulary_file. + Some or all columns could also be weighted_categorical_column. + dimension: An integer specifying dimension of the embedding, must be > 0. + combiner: A string specifying how to reduce if there are multiple entries in + a single row. Currently 'mean', 'sqrtn' and 'sum' are supported, with + 'mean' the default. 'sqrtn' often achieves good accuracy, in particular + with bag-of-words columns. Each of this can be thought as example level + normalizations on the column. For more information, see + `tf.embedding_lookup_sparse`. + initializer: A variable initializer function to be used in embedding + variable initialization. If not specified, defaults to + `tf.truncated_normal_initializer` with mean `0.0` and standard deviation + `1/sqrt(dimension)`. + shared_embedding_collection_name: Optional name of the collection where + shared embedding weights are added. If not given, a reasonable name will + be chosen based on the names of `categorical_columns`. This is also used + in `variable_scope` when creating shared embedding weights. + ckpt_to_load_from: String representing checkpoint name/pattern from which to + restore column weights. Required if `tensor_name_in_ckpt` is not `None`. + tensor_name_in_ckpt: Name of the `Tensor` in `ckpt_to_load_from` from which + to restore the column weights. Required if `ckpt_to_load_from` is not + `None`. + max_norm: If not `None`, each embedding is clipped if its l2-norm is larger + than this value, before combining. + trainable: Whether or not the embedding is trainable. Default is True. + + Returns: + A list of dense columns that converts from sparse input. The order of + results follows the ordering of `categorical_columns`. + + Raises: + ValueError: if `dimension` not > 0. + ValueError: if any of the given `categorical_columns` is of different type + or has different arguments than the others. + ValueError: if exactly one of `ckpt_to_load_from` and `tensor_name_in_ckpt` + is specified. + ValueError: if `initializer` is specified and is not callable. + RuntimeError: if eager execution is enabled. + """ + if context.executing_eagerly(): + raise RuntimeError('shared_embedding_columns are not supported when eager ' + 'execution is enabled.') + + if (dimension is None) or (dimension < 1): + raise ValueError('Invalid dimension {}.'.format(dimension)) + if (ckpt_to_load_from is None) != (tensor_name_in_ckpt is None): + raise ValueError('Must specify both `ckpt_to_load_from` and ' + '`tensor_name_in_ckpt` or none of them.') + + if (initializer is not None) and (not callable(initializer)): + raise ValueError('initializer must be callable if specified.') + if initializer is None: + initializer = init_ops.truncated_normal_initializer( + mean=0.0, stddev=1. / math.sqrt(dimension)) + + # Sort the columns so the default collection name is deterministic even if the + # user passes columns from an unsorted collection, such as dict.values(). + sorted_columns = sorted(categorical_columns, key=lambda x: x.name) + + c0 = sorted_columns[0] + num_buckets = c0._num_buckets # pylint: disable=protected-access + if not isinstance(c0, fc_old._CategoricalColumn): # pylint: disable=protected-access + raise ValueError( + 'All categorical_columns must be subclasses of _CategoricalColumn. ' + 'Given: {}, of type: {}'.format(c0, type(c0))) + if isinstance(c0, + (fc_old._WeightedCategoricalColumn, WeightedCategoricalColumn)): # pylint: disable=protected-access + c0 = c0.categorical_column + for c in sorted_columns[1:]: + if isinstance( + c, (fc_old._WeightedCategoricalColumn, WeightedCategoricalColumn)): # pylint: disable=protected-access + c = c.categorical_column + if not isinstance(c, type(c0)): + raise ValueError( + 'To use shared_embedding_column, all categorical_columns must have ' + 'the same type, or be weighted_categorical_column of the same type. ' + 'Given column: {} of type: {} does not match given column: {} of ' + 'type: {}'.format(c0, type(c0), c, type(c))) + if num_buckets != c._num_buckets: # pylint: disable=protected-access + raise ValueError( + 'To use shared_embedding_column, all categorical_columns must have ' + 'the same number of buckets. Given column: {} with buckets: {} does ' + 'not match column: {} with buckets: {}'.format( + c0, num_buckets, c, c._num_buckets)) # pylint: disable=protected-access + + if not shared_embedding_collection_name: + shared_embedding_collection_name = '_'.join(c.name for c in sorted_columns) + shared_embedding_collection_name += '_shared_embedding' + + result = [] + for column in categorical_columns: + result.append( + fc_old._SharedEmbeddingColumn( # pylint: disable=protected-access + categorical_column=column, + initializer=initializer, + dimension=dimension, + combiner=combiner, + shared_embedding_collection_name=shared_embedding_collection_name, + ckpt_to_load_from=ckpt_to_load_from, + tensor_name_in_ckpt=tensor_name_in_ckpt, + max_norm=max_norm, + trainable=trainable)) + + return result + + +@tf_export('feature_column.shared_embedding_columns', v1=[]) def shared_embedding_columns_v2(categorical_columns, dimension, combiner='mean', @@ -1019,6 +1203,7 @@ def shared_embedding_columns_v2(categorical_columns, return result +@tf_export('feature_column.numeric_column') def numeric_column(key, shape=(1,), default_value=None, @@ -1094,6 +1279,7 @@ def numeric_column(key, normalizer_fn=normalizer_fn) +@tf_export('feature_column.bucketized_column') def bucketized_column(source_column, boundaries): """Represents discretized dense input. @@ -1190,6 +1376,7 @@ def _assert_key_is_string(key): type(key), key)) +@tf_export('feature_column.categorical_column_with_hash_bucket') def categorical_column_with_hash_bucket(key, hash_bucket_size, dtype=dtypes.string): @@ -1248,6 +1435,7 @@ def categorical_column_with_hash_bucket(key, return HashedCategoricalColumn(key, hash_bucket_size, dtype) +@tf_export(v1=['feature_column.categorical_column_with_vocabulary_file']) def categorical_column_with_vocabulary_file(key, vocabulary_file, vocabulary_size=None, @@ -1325,6 +1513,97 @@ def categorical_column_with_vocabulary_file(key, Returns: A `CategoricalColumn` with a vocabulary file. + Raises: + ValueError: `vocabulary_file` is missing or cannot be opened. + ValueError: `vocabulary_size` is missing or < 1. + ValueError: `num_oov_buckets` is a negative integer. + ValueError: `num_oov_buckets` and `default_value` are both specified. + ValueError: `dtype` is neither string nor integer. + """ + return categorical_column_with_vocabulary_file_v2( + key, vocabulary_file, vocabulary_size, + dtype, default_value, + num_oov_buckets) + + +@tf_export('feature_column.categorical_column_with_vocabulary_file', v1=[]) +def categorical_column_with_vocabulary_file_v2(key, + vocabulary_file, + vocabulary_size=None, + dtype=dtypes.string, + default_value=None, + num_oov_buckets=0): + """A `CategoricalColumn` with a vocabulary file. + + Use this when your inputs are in string or integer format, and you have a + vocabulary file that maps each value to an integer ID. By default, + out-of-vocabulary values are ignored. Use either (but not both) of + `num_oov_buckets` and `default_value` to specify how to include + out-of-vocabulary values. + + For input dictionary `features`, `features[key]` is either `Tensor` or + `SparseTensor`. If `Tensor`, missing values can be represented by `-1` for int + and `''` for string, which will be dropped by this feature column. + + Example with `num_oov_buckets`: + File '/us/states.txt' contains 50 lines, each with a 2-character U.S. state + abbreviation. All inputs with values in that file are assigned an ID 0-49, + corresponding to its line number. All other values are hashed and assigned an + ID 50-54. + + ```python + states = categorical_column_with_vocabulary_file( + key='states', vocabulary_file='/us/states.txt', vocabulary_size=50, + num_oov_buckets=5) + columns = [states, ...] + features = tf.parse_example(..., features=make_parse_example_spec(columns)) + linear_prediction = linear_model(features, columns) + ``` + + Example with `default_value`: + File '/us/states.txt' contains 51 lines - the first line is 'XX', and the + other 50 each have a 2-character U.S. state abbreviation. Both a literal 'XX' + in input, and other values missing from the file, will be assigned ID 0. All + others are assigned the corresponding line number 1-50. + + ```python + states = categorical_column_with_vocabulary_file( + key='states', vocabulary_file='/us/states.txt', vocabulary_size=51, + default_value=0) + columns = [states, ...] + features = tf.parse_example(..., features=make_parse_example_spec(columns)) + linear_prediction, _, _ = linear_model(features, columns) + ``` + + And to make an embedding with either: + + ```python + columns = [embedding_column(states, 3),...] + features = tf.parse_example(..., features=make_parse_example_spec(columns)) + dense_tensor = input_layer(features, columns) + ``` + + Args: + key: A unique string identifying the input feature. It is used as the + column name and the dictionary key for feature parsing configs, feature + `Tensor` objects, and feature columns. + vocabulary_file: The vocabulary file name. + vocabulary_size: Number of the elements in the vocabulary. This must be no + greater than length of `vocabulary_file`, if less than length, later + values are ignored. If None, it is set to the length of `vocabulary_file`. + dtype: The type of features. Only string and integer types are supported. + default_value: The integer ID value to return for out-of-vocabulary feature + values, defaults to `-1`. This can not be specified with a positive + `num_oov_buckets`. + num_oov_buckets: Non-negative integer, the number of out-of-vocabulary + buckets. All out-of-vocabulary inputs will be assigned IDs in the range + `[vocabulary_size, vocabulary_size+num_oov_buckets)` based on a hash of + the input value. A positive `num_oov_buckets` can not be specified with + `default_value`. + + Returns: + A `CategoricalColumn` with a vocabulary file. + Raises: ValueError: `vocabulary_file` is missing or cannot be opened. ValueError: `vocabulary_size` is missing or < 1. @@ -1367,8 +1646,12 @@ def categorical_column_with_vocabulary_file(key, dtype=dtype) -def categorical_column_with_vocabulary_list( - key, vocabulary_list, dtype=None, default_value=-1, num_oov_buckets=0): +@tf_export('feature_column.categorical_column_with_vocabulary_list') +def categorical_column_with_vocabulary_list(key, + vocabulary_list, + dtype=None, + default_value=-1, + num_oov_buckets=0): """A `CategoricalColumn` with in-memory vocabulary. Use this when your inputs are in string or integer format, and you have an @@ -1480,6 +1763,7 @@ def categorical_column_with_vocabulary_list( num_oov_buckets=num_oov_buckets) +@tf_export('feature_column.categorical_column_with_identity') def categorical_column_with_identity(key, num_buckets, default_value=None): """A `CategoricalColumn` that returns identity values. @@ -1547,6 +1831,7 @@ def categorical_column_with_identity(key, num_buckets, default_value=None): key=key, number_buckets=num_buckets, default_value=default_value) +@tf_export('feature_column.indicator_column') def indicator_column(categorical_column): """Represents multi-hot representation of given categorical column. @@ -1581,8 +1866,10 @@ def indicator_column(categorical_column): return IndicatorColumn(categorical_column) -def weighted_categorical_column( - categorical_column, weight_feature_key, dtype=dtypes.float32): +@tf_export('feature_column.weighted_categorical_column') +def weighted_categorical_column(categorical_column, + weight_feature_key, + dtype=dtypes.float32): """Applies weight values to a `CategoricalColumn`. Use this when each of your sparse inputs has both an ID and a value. For @@ -1655,6 +1942,7 @@ def weighted_categorical_column( dtype=dtype) +@tf_export('feature_column.crossed_column') def crossed_column(keys, hash_bucket_size, hash_key=None): """Returns a column for performing crosses of categorical features. @@ -2120,7 +2408,7 @@ def _create_categorical_column_weighted_sum( weight_tensor = sparse_ops.sparse_reshape( weight_tensor, [array_ops.shape(weight_tensor)[0], -1]) - return _safe_embedding_lookup_sparse( + return embedding_ops.safe_embedding_lookup_sparse( weight_var, id_tensor, sparse_weights=weight_tensor, @@ -2731,7 +3019,7 @@ class EmbeddingColumn( }) # Return embedding lookup result. - return _safe_embedding_lookup_sparse( + return embedding_ops.safe_embedding_lookup_sparse( embedding_weights=embedding_weights, sparse_ids=sparse_ids, sparse_weights=sparse_weights, @@ -2890,7 +3178,7 @@ class EmbeddingColumn( def _raise_shared_embedding_column_error(): raise ValueError('SharedEmbeddingColumns are not supported in ' '`linear_model` or `input_layer`. Please use ' - '`FeatureLayer` or `LinearModel` instead.') + '`DenseFeatures` or `LinearModel` instead.') class SharedEmbeddingColumnCreator(tracking.Checkpointable): @@ -3002,7 +3290,7 @@ class SharedEmbeddingColumn( embedding_weights = self.shared_embedding_column_creator.embedding_weights # Return embedding lookup result. - return _safe_embedding_lookup_sparse( + return embedding_ops.safe_embedding_lookup_sparse( embedding_weights=embedding_weights, sparse_ids=sparse_ids, sparse_weights=sparse_weights, @@ -3687,9 +3975,13 @@ class WeightedCategoricalColumn( def transform_feature(self, transformation_cache, state_manager): """Applies weights to tensor generated from `categorical_column`'.""" + print('WeightedCategoricalColumn.transform_feature: ', self.name) + print('Weight feature key: ', self.weight_feature_key) weight_tensor = transformation_cache.get(self.weight_feature_key, state_manager) + print('Weight tensor before: ', weight_tensor) weight_tensor = self._transform_weight_tensor(weight_tensor) + print('Weight tensor after: ', weight_tensor) return (transformation_cache.get(self.categorical_column, state_manager), weight_tensor) @@ -3703,7 +3995,9 @@ class WeightedCategoricalColumn( def get_sparse_tensors(self, transformation_cache, state_manager): """See `CategoricalColumn` base class.""" + print('WeightedCategoricalColumn.get_sparse_tensors: ', self.name) tensors = transformation_cache.get(self, state_manager) + print('tensors[1]: ', tensors[1]) return CategoricalColumn.IdWeightPair(tensors[0], tensors[1]) @deprecation.deprecated(_FEATURE_COLUMN_DEPRECATION_DATE, @@ -3898,142 +4192,6 @@ def _collect_leaf_level_keys(cross): return leaf_level_keys -# TODO(zakaria): Move this to embedding_ops and make it public. -def _safe_embedding_lookup_sparse(embedding_weights, - sparse_ids, - sparse_weights=None, - combiner='mean', - default_id=None, - name=None, - partition_strategy='div', - max_norm=None): - """Lookup embedding results, accounting for invalid IDs and empty features. - - The partitioned embedding in `embedding_weights` must all be the same shape - except for the first dimension. The first dimension is allowed to vary as the - vocabulary size is not necessarily a multiple of `P`. `embedding_weights` - may be a `PartitionedVariable` as returned by using `tf.get_variable()` with a - partitioner. - - Invalid IDs (< 0) are pruned from input IDs and weights, as well as any IDs - with non-positive weight. For an entry with no features, the embedding vector - for `default_id` is returned, or the 0-vector if `default_id` is not supplied. - - The ids and weights may be multi-dimensional. Embeddings are always aggregated - along the last dimension. - - Args: - embedding_weights: A list of `P` float `Tensor`s or values representing - partitioned embedding `Tensor`s. Alternatively, a `PartitionedVariable` - created by partitioning along dimension 0. The total unpartitioned - shape should be `[e_0, e_1, ..., e_m]`, where `e_0` represents the - vocab size and `e_1, ..., e_m` are the embedding dimensions. - sparse_ids: `SparseTensor` of shape `[d_0, d_1, ..., d_n]` containing the - ids. `d_0` is typically batch size. - sparse_weights: `SparseTensor` of same shape as `sparse_ids`, containing - float weights corresponding to `sparse_ids`, or `None` if all weights - are be assumed to be 1.0. - combiner: A string specifying how to combine embedding results for each - entry. Currently "mean", "sqrtn" and "sum" are supported, with "mean" - the default. - default_id: The id to use for an entry with no features. - name: A name for this operation (optional). - partition_strategy: A string specifying the partitioning strategy. - Currently `"div"` and `"mod"` are supported. Default is `"div"`. - max_norm: If not `None`, all embeddings are l2-normalized to max_norm before - combining. - - - Returns: - Dense `Tensor` of shape `[d_0, d_1, ..., d_{n-1}, e_1, ..., e_m]`. - - Raises: - ValueError: if `embedding_weights` is empty. - """ - if embedding_weights is None: - raise ValueError('Missing embedding_weights %s.' % embedding_weights) - if isinstance(embedding_weights, variables.PartitionedVariable): - embedding_weights = list(embedding_weights) # get underlying Variables. - if not isinstance(embedding_weights, list): - embedding_weights = [embedding_weights] - if len(embedding_weights) < 1: - raise ValueError('Missing embedding_weights %s.' % embedding_weights) - - dtype = sparse_weights.dtype if sparse_weights is not None else None - # TODO(rohanj): Look into removing this convert_to_tensor call. - embedding_weights = [ - ops.convert_to_tensor(w, dtype=dtype) for w in embedding_weights - ] - - with ops.name_scope(name, 'embedding_lookup', - embedding_weights + [sparse_ids, - sparse_weights]) as scope: - # Reshape higher-rank sparse ids and weights to linear segment ids. - original_shape = sparse_ids.dense_shape - original_rank_dim = tensor_shape.dimension_value( - sparse_ids.dense_shape.get_shape()[0]) - original_rank = ( - array_ops.size(original_shape) - if original_rank_dim is None - else original_rank_dim) - sparse_ids = sparse_ops.sparse_reshape(sparse_ids, [ - math_ops.reduce_prod( - array_ops.slice(original_shape, [0], [original_rank - 1])), - array_ops.gather(original_shape, original_rank - 1)]) - if sparse_weights is not None: - sparse_weights = sparse_tensor_lib.SparseTensor( - sparse_ids.indices, - sparse_weights.values, sparse_ids.dense_shape) - - # Prune invalid ids and weights. - sparse_ids, sparse_weights = _prune_invalid_ids(sparse_ids, sparse_weights) - if combiner != 'sum': - sparse_ids, sparse_weights = _prune_invalid_weights( - sparse_ids, sparse_weights) - - # Fill in dummy values for empty features, if necessary. - sparse_ids, is_row_empty = sparse_ops.sparse_fill_empty_rows(sparse_ids, - default_id or - 0) - if sparse_weights is not None: - sparse_weights, _ = sparse_ops.sparse_fill_empty_rows(sparse_weights, 1.0) - - result = embedding_ops.embedding_lookup_sparse( - embedding_weights, - sparse_ids, - sparse_weights, - combiner=combiner, - partition_strategy=partition_strategy, - name=None if default_id is None else scope, - max_norm=max_norm) - - if default_id is None: - # Broadcast is_row_empty to the same shape as embedding_lookup_result, - # for use in Select. - is_row_empty = array_ops.tile( - array_ops.reshape(is_row_empty, [-1, 1]), - array_ops.stack([1, array_ops.shape(result)[1]])) - - result = array_ops.where(is_row_empty, - array_ops.zeros_like(result), - result, - name=scope) - - # Reshape back from linear ids back into higher-dimensional dense result. - final_result = array_ops.reshape( - result, - array_ops.concat([ - array_ops.slice( - math_ops.cast(original_shape, dtypes.int32), [0], - [original_rank - 1]), - array_ops.slice(array_ops.shape(result), [1], [-1]) - ], 0)) - final_result.set_shape(tensor_shape.unknown_shape( - (tensor_shape.Dimension(original_rank_dim) - 1).value).concatenate( - result.get_shape()[1:])) - return final_result - - def _prune_invalid_ids(sparse_ids, sparse_weights): """Prune invalid IDs (< 0) from the input ids and weights.""" is_id_valid = math_ops.greater_equal(sparse_ids.values, 0) @@ -4089,10 +4247,14 @@ class IndicatorColumn( sp_ids=id_tensor, sp_values=weight_tensor, vocab_size=int(self._variable_shape[-1])) - # Remove (?, -1) index + # Remove (?, -1) index. weighted_column = sparse_ops.sparse_slice(weighted_column, [0, 0], weighted_column.dense_shape) - return sparse_ops.sparse_tensor_to_dense(weighted_column) + # Use scatter_nd to merge duplicated indices if existed, + # instead of sparse_tensor_to_dense. + return array_ops.scatter_nd(weighted_column.indices, + weighted_column.values, + weighted_column.dense_shape) dense_id_tensor = sparse_ops.sparse_tensor_to_dense( id_tensor, default_value=-1) @@ -4534,7 +4696,10 @@ def deserialize_feature_column(config, 'Expected FeatureColumn class, instead found: {}'.format(cls)) # Always deserialize the FeatureColumn, in order to get the name. - new_instance = cls._from_config(cls_config, columns_by_name=columns_by_name) # pylint: disable=protected-access + new_instance = cls._from_config( # pylint: disable=protected-access + cls_config, + custom_objects=custom_objects, + columns_by_name=columns_by_name) # If the name already exists, re-use the column from columns_by_name, # (new_instance remains unused). diff --git a/tensorflow/python/feature_column/feature_column_v2_test.py b/tensorflow/python/feature_column/feature_column_v2_test.py index a26b860056..0755c0b6ac 100644 --- a/tensorflow/python/feature_column/feature_column_v2_test.py +++ b/tensorflow/python/feature_column/feature_column_v2_test.py @@ -31,7 +31,6 @@ from tensorflow.python import keras from tensorflow.python.client import session from tensorflow.python.eager import backprop from tensorflow.python.eager import context -from tensorflow.python.estimator.inputs import numpy_io from tensorflow.python.feature_column import feature_column as fc_old from tensorflow.python.feature_column import feature_column_v2 as fc from tensorflow.python.framework import constant_op @@ -50,6 +49,7 @@ from tensorflow.python.platform import test from tensorflow.python.training import coordinator from tensorflow.python.training import queue_runner_impl from tensorflow.python.training import rmsprop +from tensorflow_estimator.python.estimator.inputs import numpy_io def _initialized_session(config=None): @@ -218,6 +218,7 @@ class LazyColumnTest(test.TestCase): TypeError, '"key" must be either a "str" or "FeatureColumn".'): transformation_cache.get(NotAFeatureColumn(), None) + @test_util.run_deprecated_v1 def test_expand_dim_rank_1_sparse_tensor_empty_batch(self): # empty 1-D sparse tensor: transformation_cache = fc.FeatureTransformationCache( @@ -228,15 +229,16 @@ class LazyColumnTest(test.TestCase): dense_shape=[0], values=np.array([])) }) - with self.cached_session(): - spv = transformation_cache.get('a', None).eval() - self.assertAllEqual(np.array([0, 1], dtype=np.int64), spv.dense_shape) - self.assertAllEqual( - np.reshape(np.array([], dtype=np.int64), (0, 2)), spv.indices) + + spv = self.evaluate(transformation_cache.get('a', None)) + self.assertAllEqual(np.array([0, 1], dtype=np.int64), spv.dense_shape) + self.assertAllEqual( + np.reshape(np.array([], dtype=np.int64), (0, 2)), spv.indices) class NumericColumnTest(test.TestCase): + @test_util.run_deprecated_v1 def test_defaults(self): a = fc.numeric_column('aaa') self.assertEqual('aaa', a.key) @@ -315,59 +317,67 @@ class NumericColumnTest(test.TestCase): 'aaa': parsing_ops.FixedLenFeature((2, 3), dtype=dtypes.int32) }, a.parse_example_spec) + @test_util.run_deprecated_v1 def test_parse_example_no_default_value(self): price = fc.numeric_column('price', shape=[2]) - data = example_pb2.Example(features=feature_pb2.Features( - feature={ - 'price': - feature_pb2.Feature(float_list=feature_pb2.FloatList( - value=[20., 110.])) - })) + data = example_pb2.Example( + features=feature_pb2.Features( + feature={ + 'price': + feature_pb2.Feature( + float_list=feature_pb2.FloatList(value=[20., 110.])) + })) features = parsing_ops.parse_example( serialized=[data.SerializeToString()], - features=fc.make_parse_example_spec([price])) + features=fc.make_parse_example_spec_v2([price])) self.assertIn('price', features) - with self.cached_session(): - self.assertAllEqual([[20., 110.]], features['price'].eval()) + self.assertAllEqual([[20., 110.]], self.evaluate(features['price'])) + + @test_util.run_deprecated_v1 def test_parse_example_with_default_value(self): price = fc.numeric_column('price', shape=[2], default_value=11.) - data = example_pb2.Example(features=feature_pb2.Features( - feature={ - 'price': - feature_pb2.Feature(float_list=feature_pb2.FloatList( - value=[20., 110.])) - })) - no_data = example_pb2.Example(features=feature_pb2.Features( - feature={ - 'something_else': - feature_pb2.Feature(float_list=feature_pb2.FloatList( - value=[20., 110.])) - })) + data = example_pb2.Example( + features=feature_pb2.Features( + feature={ + 'price': + feature_pb2.Feature( + float_list=feature_pb2.FloatList(value=[20., 110.])) + })) + no_data = example_pb2.Example( + features=feature_pb2.Features( + feature={ + 'something_else': + feature_pb2.Feature( + float_list=feature_pb2.FloatList(value=[20., 110.])) + })) features = parsing_ops.parse_example( serialized=[data.SerializeToString(), no_data.SerializeToString()], - features=fc.make_parse_example_spec([price])) + features=fc.make_parse_example_spec_v2([price])) self.assertIn('price', features) - with self.cached_session(): - self.assertAllEqual([[20., 110.], [11., 11.]], features['price'].eval()) + + self.assertAllEqual([[20., 110.], [11., 11.]], + self.evaluate(features['price'])) def test_normalizer_fn_must_be_callable(self): with self.assertRaisesRegexp(TypeError, 'must be a callable'): fc.numeric_column('price', normalizer_fn='NotACallable') + @test_util.run_deprecated_v1 def test_normalizer_fn_transform_feature(self): def _increment_two(input_tensor): return input_tensor + 2. price = fc.numeric_column('price', shape=[2], normalizer_fn=_increment_two) - output = fc._transform_features({ + output = fc._transform_features_v2({ 'price': [[1., 2.], [5., 6.]] }, [price], None) - with self.cached_session(): - self.assertAllEqual([[3., 4.], [7., 8.]], output[price].eval()) + self.assertAllEqual([[3., 4.], [7., 8.]], self.evaluate(output[price])) + + @test_util.run_deprecated_v1 def test_get_dense_tensor(self): def _increment_two(input_tensor): @@ -391,6 +401,7 @@ class NumericColumnTest(test.TestCase): with self.assertRaisesRegexp(ValueError, 'must be a Tensor'): price.transform_feature(transformation_cache, None) + @test_util.run_deprecated_v1 def test_deep_copy(self): a = fc.numeric_column('aaa', shape=[1, 2], default_value=[[3., 2.]]) a_copy = copy.deepcopy(a) @@ -403,6 +414,7 @@ class NumericColumnTest(test.TestCase): 'aaa', shape=[1, 2], default_value=np.array([[3., 2.]])) self.assertEqual(a.default_value, ((3., 2.),)) + @test_util.run_deprecated_v1 def test_linear_model(self): price = fc.numeric_column('price') with ops.Graph().as_default(): @@ -411,11 +423,11 @@ class NumericColumnTest(test.TestCase): predictions = model(features) price_var, bias = model.variables with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) - self.assertAllClose([[0.]], price_var.eval()) - self.assertAllClose([[0.], [0.]], predictions.eval()) + self.assertAllClose([0.], self.evaluate(bias)) + self.assertAllClose([[0.]], self.evaluate(price_var)) + self.assertAllClose([[0.], [0.]], self.evaluate(predictions)) sess.run(price_var.assign([[10.]])) - self.assertAllClose([[10.], [50.]], predictions.eval()) + self.assertAllClose([[10.], [50.]], self.evaluate(predictions)) def test_old_linear_model(self): price = fc.numeric_column('price') @@ -425,12 +437,13 @@ class NumericColumnTest(test.TestCase): bias = get_linear_model_bias() price_var = get_linear_model_column_var(price) with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) - self.assertAllClose([[0.]], price_var.eval()) - self.assertAllClose([[0.], [0.]], predictions.eval()) + self.assertAllClose([0.], self.evaluate(bias)) + self.assertAllClose([[0.]], self.evaluate(price_var)) + self.assertAllClose([[0.], [0.]], self.evaluate(predictions)) sess.run(price_var.assign([[10.]])) - self.assertAllClose([[10.], [50.]], predictions.eval()) + self.assertAllClose([[10.], [50.]], self.evaluate(predictions)) + @test_util.run_deprecated_v1 def test_serialization(self): def _increment_two(input_tensor): @@ -471,17 +484,17 @@ class BucketizedColumnTest(test.TestCase): def test_invalid_boundaries(self): a = fc.numeric_column('aaa') - with self.assertRaisesRegexp( - ValueError, 'boundaries must be a sorted list'): + with self.assertRaisesRegexp(ValueError, + 'boundaries must be a sorted list'): fc.bucketized_column(a, boundaries=None) - with self.assertRaisesRegexp( - ValueError, 'boundaries must be a sorted list'): + with self.assertRaisesRegexp(ValueError, + 'boundaries must be a sorted list'): fc.bucketized_column(a, boundaries=1.) - with self.assertRaisesRegexp( - ValueError, 'boundaries must be a sorted list'): + with self.assertRaisesRegexp(ValueError, + 'boundaries must be a sorted list'): fc.bucketized_column(a, boundaries=[1, 0]) - with self.assertRaisesRegexp( - ValueError, 'boundaries must be a sorted list'): + with self.assertRaisesRegexp(ValueError, + 'boundaries must be a sorted list'): fc.bucketized_column(a, boundaries=[1, 1]) def test_name(self): @@ -491,7 +504,7 @@ class BucketizedColumnTest(test.TestCase): self.assertEqual('aaa_bucketized', b.name) def test_is_v2_column_old_numeric(self): - a = fc_old.numeric_column('aaa', dtype=dtypes.int32) + a = fc_old._numeric_column('aaa', dtype=dtypes.int32) b = fc.bucketized_column(a, boundaries=[0, 1]) self.assertFalse(b._is_v2_column) self.assertEqual('aaa_bucketized', b.name) @@ -515,32 +528,38 @@ class BucketizedColumnTest(test.TestCase): # Column 'aaa` has shape [2] times three buckets -> num_buckets=6. self.assertEqual(6, b.num_buckets) + @test_util.run_deprecated_v1 def test_parse_example(self): price = fc.numeric_column('price', shape=[2]) bucketized_price = fc.bucketized_column(price, boundaries=[0, 50]) - data = example_pb2.Example(features=feature_pb2.Features( - feature={ - 'price': - feature_pb2.Feature(float_list=feature_pb2.FloatList( - value=[20., 110.])) - })) + data = example_pb2.Example( + features=feature_pb2.Features( + feature={ + 'price': + feature_pb2.Feature( + float_list=feature_pb2.FloatList(value=[20., 110.])) + })) features = parsing_ops.parse_example( serialized=[data.SerializeToString()], - features=fc.make_parse_example_spec([bucketized_price])) + features=fc.make_parse_example_spec_v2([bucketized_price])) self.assertIn('price', features) - with self.cached_session(): - self.assertAllEqual([[20., 110.]], features['price'].eval()) + self.assertAllEqual([[20., 110.]], self.evaluate(features['price'])) + + @test_util.run_deprecated_v1 def test_transform_feature(self): price = fc.numeric_column('price', shape=[2]) bucketized_price = fc.bucketized_column(price, boundaries=[0, 2, 4, 6]) with ops.Graph().as_default(): - transformed_tensor = fc._transform_features({ + transformed_tensor = fc._transform_features_v2({ 'price': [[-1., 1.], [5., 6.]] }, [bucketized_price], None) - with _initialized_session(): - self.assertAllEqual([[0, 1], [3, 4]], - transformed_tensor[bucketized_price].eval()) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllEqual([[0, 1], [3, 4]], + self.evaluate(transformed_tensor[bucketized_price])) def test_get_dense_tensor_one_input_value(self): """Tests _get_dense_tensor() for input with shape=[1].""" @@ -550,16 +569,17 @@ class BucketizedColumnTest(test.TestCase): transformation_cache = fc.FeatureTransformationCache({ 'price': [[-1.], [1.], [5.], [6.]] }) - with _initialized_session(): - bucketized_price_tensor = bucketized_price.get_dense_tensor( - transformation_cache, None) - self.assertAllClose( - # One-hot tensor. - [[[1., 0., 0., 0., 0.]], - [[0., 1., 0., 0., 0.]], - [[0., 0., 0., 1., 0.]], - [[0., 0., 0., 0., 1.]]], - bucketized_price_tensor.eval()) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + bucketized_price_tensor = bucketized_price.get_dense_tensor( + transformation_cache, None) + self.assertAllClose( + # One-hot tensor. + [[[1., 0., 0., 0., 0.]], [[0., 1., 0., 0., 0.]], + [[0., 0., 0., 1., 0.]], [[0., 0., 0., 0., 1.]]], + self.evaluate(bucketized_price_tensor)) def test_get_dense_tensor_two_input_values(self): """Tests _get_dense_tensor() for input with shape=[2].""" @@ -569,14 +589,17 @@ class BucketizedColumnTest(test.TestCase): transformation_cache = fc.FeatureTransformationCache({ 'price': [[-1., 1.], [5., 6.]] }) - with _initialized_session(): - bucketized_price_tensor = bucketized_price.get_dense_tensor( - transformation_cache, None) - self.assertAllClose( - # One-hot tensor. - [[[1., 0., 0., 0., 0.], [0., 1., 0., 0., 0.]], - [[0., 0., 0., 1., 0.], [0., 0., 0., 0., 1.]]], - bucketized_price_tensor.eval()) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + bucketized_price_tensor = bucketized_price.get_dense_tensor( + transformation_cache, None) + self.assertAllClose( + # One-hot tensor. + [[[1., 0., 0., 0., 0.], [0., 1., 0., 0., 0.]], + [[0., 0., 0., 1., 0.], [0., 0., 0., 0., 1.]]], + self.evaluate(bucketized_price_tensor)) def test_get_sparse_tensors_one_input_value(self): """Tests _get_sparse_tensors() for input with shape=[1].""" @@ -591,8 +614,8 @@ class BucketizedColumnTest(test.TestCase): transformation_cache, None) self.assertIsNone(id_weight_pair.weight_tensor) id_tensor_value = sess.run(id_weight_pair.id_tensor) - self.assertAllEqual( - [[0, 0], [1, 0], [2, 0], [3, 0]], id_tensor_value.indices) + self.assertAllEqual([[0, 0], [1, 0], [2, 0], [3, 0]], + id_tensor_value.indices) self.assertAllEqual([0, 1, 3, 4], id_tensor_value.values) self.assertAllEqual([4, 1], id_tensor_value.dense_shape) @@ -609,8 +632,8 @@ class BucketizedColumnTest(test.TestCase): transformation_cache, None) self.assertIsNone(id_weight_pair.weight_tensor) id_tensor_value = sess.run(id_weight_pair.id_tensor) - self.assertAllEqual( - [[0, 0], [0, 1], [1, 0], [1, 1]], id_tensor_value.indices) + self.assertAllEqual([[0, 0], [0, 1], [1, 0], [1, 1]], + id_tensor_value.indices) # Values 0-4 correspond to the first column of the input price. # Values 5-9 correspond to the second column of the input price. self.assertAllEqual([0, 6, 3, 9], id_tensor_value.values) @@ -627,6 +650,7 @@ class BucketizedColumnTest(test.TestCase): with self.assertRaisesRegexp(ValueError, 'must be a Tensor'): bucketized_price.transform_feature(transformation_cache, None) + @test_util.run_deprecated_v1 def test_deep_copy(self): a = fc.numeric_column('aaa', shape=[2]) a_bucketized = fc.bucketized_column(a, boundaries=[0, 1]) @@ -645,20 +669,23 @@ class BucketizedColumnTest(test.TestCase): predictions = model(features) bucketized_price_var, bias = model.variables with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) + self.assertAllClose([0.], self.evaluate(bias)) # One weight variable per bucket, all initialized to zero. - self.assertAllClose( - [[0.], [0.], [0.], [0.], [0.]], bucketized_price_var.eval()) - self.assertAllClose([[0.], [0.], [0.], [0.]], predictions.eval()) - sess.run(bucketized_price_var.assign( - [[10.], [20.], [30.], [40.], [50.]])) + self.assertAllClose([[0.], [0.], [0.], [0.], [0.]], + self.evaluate(bucketized_price_var)) + self.assertAllClose([[0.], [0.], [0.], [0.]], + self.evaluate(predictions)) + sess.run( + bucketized_price_var.assign([[10.], [20.], [30.], [40.], [50.]])) # price -1. is in the 0th bucket, whose weight is 10. # price 1. is in the 1st bucket, whose weight is 20. # price 5. is in the 3rd bucket, whose weight is 40. # price 6. is in the 4th bucket, whose weight is 50. - self.assertAllClose([[10.], [20.], [40.], [50.]], predictions.eval()) + self.assertAllClose([[10.], [20.], [40.], [50.]], + self.evaluate(predictions)) sess.run(bias.assign([1.])) - self.assertAllClose([[11.], [21.], [41.], [51.]], predictions.eval()) + self.assertAllClose([[11.], [21.], [41.], [51.]], + self.evaluate(predictions)) def test_linear_model_two_input_values(self): """Tests linear_model() for input with shape=[2].""" @@ -670,24 +697,24 @@ class BucketizedColumnTest(test.TestCase): predictions = model(features) bucketized_price_var, bias = model.variables with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) + self.assertAllClose([0.], self.evaluate(bias)) # One weight per bucket per input column, all initialized to zero. self.assertAllClose( [[0.], [0.], [0.], [0.], [0.], [0.], [0.], [0.], [0.], [0.]], - bucketized_price_var.eval()) - self.assertAllClose([[0.], [0.]], predictions.eval()) - sess.run(bucketized_price_var.assign( - [[10.], [20.], [30.], [40.], [50.], - [60.], [70.], [80.], [90.], [100.]])) + self.evaluate(bucketized_price_var)) + self.assertAllClose([[0.], [0.]], self.evaluate(predictions)) + sess.run( + bucketized_price_var.assign([[10.], [20.], [30.], [40.], [50.], + [60.], [70.], [80.], [90.], [100.]])) # 1st example: # price -1. is in the 0th bucket, whose weight is 10. # price 1. is in the 6th bucket, whose weight is 70. # 2nd example: # price 5. is in the 3rd bucket, whose weight is 40. # price 6. is in the 9th bucket, whose weight is 100. - self.assertAllClose([[80.], [140.]], predictions.eval()) + self.assertAllClose([[80.], [140.]], self.evaluate(predictions)) sess.run(bias.assign([1.])) - self.assertAllClose([[81.], [141.]], predictions.eval()) + self.assertAllClose([[81.], [141.]], self.evaluate(predictions)) def test_old_linear_model_one_input_value(self): """Tests linear_model() for input with shape=[1].""" @@ -699,20 +726,23 @@ class BucketizedColumnTest(test.TestCase): bias = get_linear_model_bias() bucketized_price_var = get_linear_model_column_var(bucketized_price) with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) + self.assertAllClose([0.], self.evaluate(bias)) # One weight variable per bucket, all initialized to zero. self.assertAllClose([[0.], [0.], [0.], [0.], [0.]], - bucketized_price_var.eval()) - self.assertAllClose([[0.], [0.], [0.], [0.]], predictions.eval()) + self.evaluate(bucketized_price_var)) + self.assertAllClose([[0.], [0.], [0.], [0.]], + self.evaluate(predictions)) sess.run( bucketized_price_var.assign([[10.], [20.], [30.], [40.], [50.]])) # price -1. is in the 0th bucket, whose weight is 10. # price 1. is in the 1st bucket, whose weight is 20. # price 5. is in the 3rd bucket, whose weight is 40. # price 6. is in the 4th bucket, whose weight is 50. - self.assertAllClose([[10.], [20.], [40.], [50.]], predictions.eval()) + self.assertAllClose([[10.], [20.], [40.], [50.]], + self.evaluate(predictions)) sess.run(bias.assign([1.])) - self.assertAllClose([[11.], [21.], [41.], [51.]], predictions.eval()) + self.assertAllClose([[11.], [21.], [41.], [51.]], + self.evaluate(predictions)) def test_old_linear_model_two_input_values(self): """Tests linear_model() for input with shape=[2].""" @@ -724,12 +754,12 @@ class BucketizedColumnTest(test.TestCase): bias = get_linear_model_bias() bucketized_price_var = get_linear_model_column_var(bucketized_price) with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) + self.assertAllClose([0.], self.evaluate(bias)) # One weight per bucket per input column, all initialized to zero. self.assertAllClose( [[0.], [0.], [0.], [0.], [0.], [0.], [0.], [0.], [0.], [0.]], - bucketized_price_var.eval()) - self.assertAllClose([[0.], [0.]], predictions.eval()) + self.evaluate(bucketized_price_var)) + self.assertAllClose([[0.], [0.]], self.evaluate(predictions)) sess.run( bucketized_price_var.assign([[10.], [20.], [30.], [40.], [50.], [60.], [70.], [80.], [90.], [100.]])) @@ -739,13 +769,13 @@ class BucketizedColumnTest(test.TestCase): # 2nd example: # price 5. is in the 3rd bucket, whose weight is 40. # price 6. is in the 9th bucket, whose weight is 100. - self.assertAllClose([[80.], [140.]], predictions.eval()) + self.assertAllClose([[80.], [140.]], self.evaluate(predictions)) sess.run(bias.assign([1.])) - self.assertAllClose([[81.], [141.]], predictions.eval()) + self.assertAllClose([[81.], [141.]], self.evaluate(predictions)) def test_old_linear_model_one_input_value_old_numeric(self): """Tests linear_model() for input with shape=[1].""" - price = fc_old.numeric_column('price', shape=[1]) + price = fc_old._numeric_column('price', shape=[1]) bucketized_price = fc.bucketized_column(price, boundaries=[0, 2, 4, 6]) with ops.Graph().as_default(): features = {'price': [[-1.], [1.], [5.], [6.]]} @@ -753,21 +783,25 @@ class BucketizedColumnTest(test.TestCase): bias = get_linear_model_bias() bucketized_price_var = get_linear_model_column_var(bucketized_price) with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) + self.assertAllClose([0.], self.evaluate(bias)) # One weight variable per bucket, all initialized to zero. self.assertAllClose([[0.], [0.], [0.], [0.], [0.]], - bucketized_price_var.eval()) - self.assertAllClose([[0.], [0.], [0.], [0.]], predictions.eval()) + self.evaluate(bucketized_price_var)) + self.assertAllClose([[0.], [0.], [0.], [0.]], + self.evaluate(predictions)) sess.run( bucketized_price_var.assign([[10.], [20.], [30.], [40.], [50.]])) # price -1. is in the 0th bucket, whose weight is 10. # price 1. is in the 1st bucket, whose weight is 20. # price 5. is in the 3rd bucket, whose weight is 40. # price 6. is in the 4th bucket, whose weight is 50. - self.assertAllClose([[10.], [20.], [40.], [50.]], predictions.eval()) + self.assertAllClose([[10.], [20.], [40.], [50.]], + self.evaluate(predictions)) sess.run(bias.assign([1.])) - self.assertAllClose([[11.], [21.], [41.], [51.]], predictions.eval()) + self.assertAllClose([[11.], [21.], [41.], [51.]], + self.evaluate(predictions)) + @test_util.run_deprecated_v1 def test_serialization(self): price = fc.numeric_column('price', shape=[2]) bucketized_price = fc.bucketized_column(price, boundaries=[0, 2, 4, 6]) @@ -800,6 +834,7 @@ class BucketizedColumnTest(test.TestCase): class HashedCategoricalColumnTest(test.TestCase): + @test_util.run_deprecated_v1 def test_defaults(self): a = fc.categorical_column_with_hash_bucket('aaa', 10) self.assertEqual('aaa', a.name) @@ -827,6 +862,7 @@ class HashedCategoricalColumnTest(test.TestCase): with self.assertRaisesRegexp(ValueError, 'dtype must be string or integer'): fc.categorical_column_with_hash_bucket('aaa', 10, dtype=dtypes.float32) + @test_util.run_deprecated_v1 def test_deep_copy(self): original = fc.categorical_column_with_hash_bucket('aaa', 10) for column in (original, copy.deepcopy(original)): @@ -847,45 +883,50 @@ class HashedCategoricalColumnTest(test.TestCase): 'aaa': parsing_ops.VarLenFeature(dtypes.int32) }, a.parse_example_spec) + @test_util.run_deprecated_v1 def test_parse_example(self): a = fc.categorical_column_with_hash_bucket('aaa', 10) - data = example_pb2.Example(features=feature_pb2.Features( - feature={ - 'aaa': - feature_pb2.Feature(bytes_list=feature_pb2.BytesList( - value=[b'omar', b'stringer'])) - })) + data = example_pb2.Example( + features=feature_pb2.Features( + feature={ + 'aaa': + feature_pb2.Feature( + bytes_list=feature_pb2.BytesList( + value=[b'omar', b'stringer'])) + })) features = parsing_ops.parse_example( serialized=[data.SerializeToString()], - features=fc.make_parse_example_spec([a])) + features=fc.make_parse_example_spec_v2([a])) self.assertIn('aaa', features) - with self.cached_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=[[0, 0], [0, 1]], - values=np.array([b'omar', b'stringer'], dtype=np.object_), - dense_shape=[1, 2]), - features['aaa'].eval()) + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=[[0, 0], [0, 1]], + values=np.array([b'omar', b'stringer'], dtype=np.object_), + dense_shape=[1, 2]), self.evaluate(features['aaa'])) + + @test_util.run_deprecated_v1 def test_strings_should_be_hashed(self): hashed_sparse = fc.categorical_column_with_hash_bucket('wire', 10) wire_tensor = sparse_tensor.SparseTensor( values=['omar', 'stringer', 'marlo'], indices=[[0, 0], [1, 0], [1, 1]], dense_shape=[2, 2]) - outputs = fc._transform_features({ + outputs = fc._transform_features_v2({ 'wire': wire_tensor }, [hashed_sparse], None) output = outputs[hashed_sparse] # Check exact hashed output. If hashing changes this test will break. expected_values = [6, 4, 1] - with self.cached_session(): - self.assertEqual(dtypes.int64, output.values.dtype) - self.assertAllEqual(expected_values, output.values.eval()) - self.assertAllEqual(wire_tensor.indices.eval(), output.indices.eval()) - self.assertAllEqual(wire_tensor.dense_shape.eval(), - output.dense_shape.eval()) + + self.assertEqual(dtypes.int64, output.values.dtype) + self.assertAllEqual(expected_values, self.evaluate(output.values)) + self.assertAllEqual( + self.evaluate(wire_tensor.indices), self.evaluate(output.indices)) + self.assertAllEqual( + self.evaluate(wire_tensor.dense_shape), + self.evaluate(output.dense_shape)) def test_tensor_dtype_should_be_string_or_integer(self): string_fc = fc.categorical_column_with_hash_bucket( @@ -895,17 +936,11 @@ class HashedCategoricalColumnTest(test.TestCase): float_fc = fc.categorical_column_with_hash_bucket( 'a_float', 10, dtype=dtypes.string) int_tensor = sparse_tensor.SparseTensor( - values=[101], - indices=[[0, 0]], - dense_shape=[1, 1]) + values=[101], indices=[[0, 0]], dense_shape=[1, 1]) string_tensor = sparse_tensor.SparseTensor( - values=['101'], - indices=[[0, 0]], - dense_shape=[1, 1]) + values=['101'], indices=[[0, 0]], dense_shape=[1, 1]) float_tensor = sparse_tensor.SparseTensor( - values=[101.], - indices=[[0, 0]], - dense_shape=[1, 1]) + values=[101.], indices=[[0, 0]], dense_shape=[1, 1]) transformation_cache = fc.FeatureTransformationCache({ 'a_int': int_tensor, 'a_string': string_tensor, @@ -925,6 +960,7 @@ class HashedCategoricalColumnTest(test.TestCase): with self.assertRaisesRegexp(ValueError, 'dtype must be compatible'): transformation_cache.get(hashed_sparse, None) + @test_util.run_deprecated_v1 def test_ints_should_be_hashed(self): hashed_sparse = fc.categorical_column_with_hash_bucket( 'wire', 10, dtype=dtypes.int64) @@ -936,9 +972,10 @@ class HashedCategoricalColumnTest(test.TestCase): output = transformation_cache.get(hashed_sparse, None) # Check exact hashed output. If hashing changes this test will break. expected_values = [3, 7, 5] - with self.cached_session(): - self.assertAllEqual(expected_values, output.values.eval()) + self.assertAllEqual(expected_values, self.evaluate(output.values)) + + @test_util.run_deprecated_v1 def test_int32_64_is_compatible(self): hashed_sparse = fc.categorical_column_with_hash_bucket( 'wire', 10, dtype=dtypes.int64) @@ -950,9 +987,10 @@ class HashedCategoricalColumnTest(test.TestCase): output = transformation_cache.get(hashed_sparse, None) # Check exact hashed output. If hashing changes this test will break. expected_values = [3, 7, 5] - with self.cached_session(): - self.assertAllEqual(expected_values, output.values.eval()) + self.assertAllEqual(expected_values, self.evaluate(output.values)) + + @test_util.run_deprecated_v1 def test_get_sparse_tensors(self): hashed_sparse = fc.categorical_column_with_hash_bucket('wire', 10) transformation_cache = fc.FeatureTransformationCache({ @@ -968,6 +1006,7 @@ class HashedCategoricalColumnTest(test.TestCase): self.assertEqual( transformation_cache.get(hashed_sparse, None), id_weight_pair.id_tensor) + @test_util.run_deprecated_v1 def test_get_sparse_tensors_dense_input(self): hashed_sparse = fc.categorical_column_with_hash_bucket('wire', 10) transformation_cache = fc.FeatureTransformationCache({ @@ -979,6 +1018,7 @@ class HashedCategoricalColumnTest(test.TestCase): self.assertEqual( transformation_cache.get(hashed_sparse, None), id_weight_pair.id_tensor) + @test_util.run_deprecated_v1 def test_linear_model(self): wire_column = fc.categorical_column_with_hash_bucket('wire', 4) self.assertEqual(4, wire_column.num_buckets) @@ -992,14 +1032,17 @@ class HashedCategoricalColumnTest(test.TestCase): dense_shape=(2, 2)) }) wire_var, bias = model.variables - with _initialized_session(): - self.assertAllClose((0.,), bias.eval()) - self.assertAllClose(((0.,), (0.,), (0.,), (0.,)), wire_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) - wire_var.assign(((1.,), (2.,), (3.,), (4.,))).eval() - # 'marlo' -> 3: wire_var[3] = 4 - # 'skywalker' -> 2, 'omar' -> 2: wire_var[2] + wire_var[2] = 3+3 = 6 - self.assertAllClose(((4.,), (6.,)), predictions.eval()) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose((0.,), self.evaluate(bias)) + self.assertAllClose(((0.,), (0.,), (0.,), (0.,)), self.evaluate(wire_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) + self.evaluate(wire_var.assign(((1.,), (2.,), (3.,), (4.,)))) + # 'marlo' -> 3: wire_var[3] = 4 + # 'skywalker' -> 2, 'omar' -> 2: wire_var[2] + wire_var[2] = 3+3 = 6 + self.assertAllClose(((4.,), (6.,)), self.evaluate(predictions)) def test_old_linear_model(self): wire_column = fc.categorical_column_with_hash_bucket('wire', 4) @@ -1014,15 +1057,19 @@ class HashedCategoricalColumnTest(test.TestCase): }, (wire_column,)) bias = get_linear_model_bias() wire_var = get_linear_model_column_var(wire_column) - with _initialized_session(): - self.assertAllClose((0.,), bias.eval()) - self.assertAllClose(((0.,), (0.,), (0.,), (0.,)), wire_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) - wire_var.assign(((1.,), (2.,), (3.,), (4.,))).eval() - # 'marlo' -> 3: wire_var[3] = 4 - # 'skywalker' -> 2, 'omar' -> 2: wire_var[2] + wire_var[2] = 3+3 = 6 - self.assertAllClose(((4.,), (6.,)), predictions.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose((0.,), self.evaluate(bias)) + self.assertAllClose(((0.,), (0.,), (0.,), (0.,)), self.evaluate(wire_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) + self.evaluate(wire_var.assign(((1.,), (2.,), (3.,), (4.,)))) + # 'marlo' -> 3: wire_var[3] = 4 + # 'skywalker' -> 2, 'omar' -> 2: wire_var[2] + wire_var[2] = 3+3 = 6 + self.assertAllClose(((4.,), (6.,)), self.evaluate(predictions)) + + @test_util.run_deprecated_v1 def test_serialization(self): wire_column = fc.categorical_column_with_hash_bucket('wire', 4) self.assertEqual(['wire'], wire_column.parents) @@ -1041,13 +1088,13 @@ class HashedCategoricalColumnTest(test.TestCase): class CrossedColumnTest(test.TestCase): def test_keys_empty(self): - with self.assertRaisesRegexp( - ValueError, 'keys must be a list with length > 1'): + with self.assertRaisesRegexp(ValueError, + 'keys must be a list with length > 1'): fc.crossed_column([], 10) def test_keys_length_one(self): - with self.assertRaisesRegexp( - ValueError, 'keys must be a list with length > 1'): + with self.assertRaisesRegexp(ValueError, + 'keys must be a list with length > 1'): fc.crossed_column(['a'], 10) def test_key_type_unsupported(self): @@ -1060,18 +1107,15 @@ class CrossedColumnTest(test.TestCase): ['a', fc.categorical_column_with_hash_bucket('c', 10)], 10) def test_hash_bucket_size_negative(self): - with self.assertRaisesRegexp( - ValueError, 'hash_bucket_size must be > 1'): + with self.assertRaisesRegexp(ValueError, 'hash_bucket_size must be > 1'): fc.crossed_column(['a', 'c'], -1) def test_hash_bucket_size_zero(self): - with self.assertRaisesRegexp( - ValueError, 'hash_bucket_size must be > 1'): + with self.assertRaisesRegexp(ValueError, 'hash_bucket_size must be > 1'): fc.crossed_column(['a', 'c'], 0) def test_hash_bucket_size_none(self): - with self.assertRaisesRegexp( - ValueError, 'hash_bucket_size must be > 1'): + with self.assertRaisesRegexp(ValueError, 'hash_bucket_size must be > 1'): fc.crossed_column(['a', 'c'], None) def test_name(self): @@ -1085,7 +1129,7 @@ class CrossedColumnTest(test.TestCase): self.assertEqual('a_bucketized_X_c_X_d1_X_d2', crossed2.name) def test_is_v2_column(self): - a = fc_old.numeric_column('a', dtype=dtypes.int32) + a = fc_old._numeric_column('a', dtype=dtypes.int32) b = fc.bucketized_column(a, boundaries=[0, 1]) crossed1 = fc.crossed_column(['d1', 'd2'], 10) self.assertTrue(crossed1._is_v2_column) @@ -1127,65 +1171,76 @@ class CrossedColumnTest(test.TestCase): crossed = fc.crossed_column([b, 'c'], 15) self.assertEqual(15, crossed.num_buckets) + @test_util.run_deprecated_v1 def test_deep_copy(self): a = fc.numeric_column('a', dtype=dtypes.int32) b = fc.bucketized_column(a, boundaries=[0, 1]) crossed1 = fc.crossed_column(['d1', 'd2'], 10) crossed2 = fc.crossed_column([b, 'c', crossed1], 15, hash_key=5) crossed2_copy = copy.deepcopy(crossed2) - self.assertEqual('a_bucketized_X_c_X_d1_X_d2', crossed2_copy.name,) + self.assertEqual( + 'a_bucketized_X_c_X_d1_X_d2', + crossed2_copy.name, + ) self.assertEqual(15, crossed2_copy.hash_bucket_size) self.assertEqual(5, crossed2_copy.hash_key) + @test_util.run_deprecated_v1 def test_parse_example(self): price = fc.numeric_column('price', shape=[2]) bucketized_price = fc.bucketized_column(price, boundaries=[0, 50]) price_cross_wire = fc.crossed_column([bucketized_price, 'wire'], 10) - data = example_pb2.Example(features=feature_pb2.Features( - feature={ - 'price': - feature_pb2.Feature(float_list=feature_pb2.FloatList( - value=[20., 110.])), - 'wire': - feature_pb2.Feature(bytes_list=feature_pb2.BytesList( - value=[b'omar', b'stringer'])), - })) + data = example_pb2.Example( + features=feature_pb2.Features( + feature={ + 'price': + feature_pb2.Feature( + float_list=feature_pb2.FloatList(value=[20., 110.])), + 'wire': + feature_pb2.Feature( + bytes_list=feature_pb2.BytesList( + value=[b'omar', b'stringer'])), + })) features = parsing_ops.parse_example( serialized=[data.SerializeToString()], - features=fc.make_parse_example_spec([price_cross_wire])) + features=fc.make_parse_example_spec_v2([price_cross_wire])) self.assertIn('price', features) self.assertIn('wire', features) - with self.cached_session(): - self.assertAllEqual([[20., 110.]], features['price'].eval()) - wire_sparse = features['wire'] - self.assertAllEqual([[0, 0], [0, 1]], wire_sparse.indices.eval()) - # Use byte constants to pass the open-source test. - self.assertAllEqual([b'omar', b'stringer'], wire_sparse.values.eval()) - self.assertAllEqual([1, 2], wire_sparse.dense_shape.eval()) + self.assertAllEqual([[20., 110.]], self.evaluate(features['price'])) + wire_sparse = features['wire'] + self.assertAllEqual([[0, 0], [0, 1]], self.evaluate(wire_sparse.indices)) + # Use byte constants to pass the open-source test. + self.assertAllEqual([b'omar', b'stringer'], + self.evaluate(wire_sparse.values)) + self.assertAllEqual([1, 2], self.evaluate(wire_sparse.dense_shape)) + + @test_util.run_deprecated_v1 def test_transform_feature(self): price = fc.numeric_column('price', shape=[2]) bucketized_price = fc.bucketized_column(price, boundaries=[0, 50]) hash_bucket_size = 10 - price_cross_wire = fc.crossed_column( - [bucketized_price, 'wire'], hash_bucket_size) + price_cross_wire = fc.crossed_column([bucketized_price, 'wire'], + hash_bucket_size) features = { - 'price': constant_op.constant([[1., 2.], [5., 6.]]), - 'wire': sparse_tensor.SparseTensor( - values=['omar', 'stringer', 'marlo'], - indices=[[0, 0], [1, 0], [1, 1]], - dense_shape=[2, 2]), + 'price': + constant_op.constant([[1., 2.], [5., 6.]]), + 'wire': + sparse_tensor.SparseTensor( + values=['omar', 'stringer', 'marlo'], + indices=[[0, 0], [1, 0], [1, 1]], + dense_shape=[2, 2]), } - outputs = fc._transform_features(features, [price_cross_wire], None) + outputs = fc._transform_features_v2(features, [price_cross_wire], None) output = outputs[price_cross_wire] - with self.cached_session() as sess: - output_val = sess.run(output) - self.assertAllEqual( - [[0, 0], [0, 1], [1, 0], [1, 1], [1, 2], [1, 3]], output_val.indices) - for val in output_val.values: - self.assertIn(val, list(range(hash_bucket_size))) - self.assertAllEqual([2, 4], output_val.dense_shape) - + output_val = self.evaluate(output) + self.assertAllEqual([[0, 0], [0, 1], [1, 0], [1, 1], [1, 2], [1, 3]], + output_val.indices) + for val in output_val.values: + self.assertIn(val, list(range(hash_bucket_size))) + self.assertAllEqual([2, 4], output_val.dense_shape) + + @test_util.run_deprecated_v1 def test_get_sparse_tensors(self): a = fc.numeric_column('a', dtype=dtypes.int32, shape=(2,)) b = fc.bucketized_column(a, boundaries=(0, 1)) @@ -1212,19 +1267,21 @@ class CrossedColumnTest(test.TestCase): dense_shape=(2, 2)), }) id_weight_pair = crossed2.get_sparse_tensors(transformation_cache, None) - with _initialized_session(): - id_tensor_eval = id_weight_pair.id_tensor.eval() - self.assertAllEqual( - ((0, 0), (0, 1), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), - (1, 6), (1, 7), (1, 8), (1, 9), (1, 10), (1, 11), (1, 12), (1, 13), - (1, 14), (1, 15)), - id_tensor_eval.indices) - # Check exact hashed output. If hashing changes this test will break. - # All values are within [0, hash_bucket_size). - expected_values = ( - 6, 14, 0, 13, 8, 8, 10, 12, 2, 0, 1, 9, 8, 12, 2, 0, 10, 11) - self.assertAllEqual(expected_values, id_tensor_eval.values) - self.assertAllEqual((2, 16), id_tensor_eval.dense_shape) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + id_tensor_eval = self.evaluate(id_weight_pair.id_tensor) + self.assertAllEqual( + ((0, 0), (0, 1), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), + (1, 6), (1, 7), (1, 8), (1, 9), (1, 10), (1, 11), (1, 12), (1, 13), + (1, 14), (1, 15)), id_tensor_eval.indices) + # Check exact hashed output. If hashing changes this test will break. + # All values are within [0, hash_bucket_size). + expected_values = (6, 14, 0, 13, 8, 8, 10, 12, 2, 0, 1, 9, 8, 12, 2, 0, + 10, 11) + self.assertAllEqual(expected_values, id_tensor_eval.values) + self.assertAllEqual((2, 16), id_tensor_eval.dense_shape) def test_get_sparse_tensors_simple(self): """Same as test_get_sparse_tensors, but with simpler values.""" @@ -1242,17 +1299,20 @@ class CrossedColumnTest(test.TestCase): dense_shape=(2, 2)), }) id_weight_pair = crossed.get_sparse_tensors(transformation_cache, None) - with _initialized_session(): - id_tensor_eval = id_weight_pair.id_tensor.eval() - self.assertAllEqual( - ((0, 0), (0, 1), (1, 0), (1, 1), (1, 2), (1, 3)), - id_tensor_eval.indices) - # Check exact hashed output. If hashing changes this test will break. - # All values are within [0, hash_bucket_size). - expected_values = (1, 0, 1, 3, 4, 2) - self.assertAllEqual(expected_values, id_tensor_eval.values) - self.assertAllEqual((2, 4), id_tensor_eval.dense_shape) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + id_tensor_eval = self.evaluate(id_weight_pair.id_tensor) + self.assertAllEqual(((0, 0), (0, 1), (1, 0), (1, 1), (1, 2), (1, 3)), + id_tensor_eval.indices) + # Check exact hashed output. If hashing changes this test will break. + # All values are within [0, hash_bucket_size). + expected_values = (1, 0, 1, 3, 4, 2) + self.assertAllEqual(expected_values, id_tensor_eval.values) + self.assertAllEqual((2, 4), id_tensor_eval.dense_shape) + + @test_util.run_deprecated_v1 def test_linear_model(self): """Tests linear_model. @@ -1274,15 +1334,15 @@ class CrossedColumnTest(test.TestCase): }) crossed_var, bias = model.variables with _initialized_session() as sess: - self.assertAllClose((0.,), bias.eval()) - self.assertAllClose( - ((0.,), (0.,), (0.,), (0.,), (0.,)), crossed_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) + self.assertAllClose((0.,), self.evaluate(bias)) + self.assertAllClose(((0.,), (0.,), (0.,), (0.,), (0.,)), + self.evaluate(crossed_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) sess.run(crossed_var.assign(((1.,), (2.,), (3.,), (4.,), (5.,)))) # Expected ids after cross = (1, 0, 1, 3, 4, 2) - self.assertAllClose(((3.,), (14.,)), predictions.eval()) + self.assertAllClose(((3.,), (14.,)), self.evaluate(predictions)) sess.run(bias.assign((.1,))) - self.assertAllClose(((3.1,), (14.1,)), predictions.eval()) + self.assertAllClose(((3.1,), (14.1,)), self.evaluate(predictions)) def test_linear_model_with_weights(self): @@ -1301,10 +1361,11 @@ class CrossedColumnTest(test.TestCase): @property def parse_example_spec(self): return { - self.name: parsing_ops.VarLenFeature(dtypes.int32), - '{}_weights'.format(self.name): parsing_ops.VarLenFeature( - dtypes.float32), - } + self.name: + parsing_ops.VarLenFeature(dtypes.int32), + '{}_weights'.format(self.name): + parsing_ops.VarLenFeature(dtypes.float32), + } @property def num_buckets(self): @@ -1367,15 +1428,15 @@ class CrossedColumnTest(test.TestCase): bias = get_linear_model_bias() crossed_var = get_linear_model_column_var(crossed) with _initialized_session() as sess: - self.assertAllClose((0.,), bias.eval()) + self.assertAllClose((0.,), self.evaluate(bias)) self.assertAllClose(((0.,), (0.,), (0.,), (0.,), (0.,)), - crossed_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) + self.evaluate(crossed_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) sess.run(crossed_var.assign(((1.,), (2.,), (3.,), (4.,), (5.,)))) # Expected ids after cross = (1, 0, 1, 3, 4, 2) - self.assertAllClose(((3.,), (14.,)), predictions.eval()) + self.assertAllClose(((3.,), (14.,)), self.evaluate(predictions)) sess.run(bias.assign((.1,))) - self.assertAllClose(((3.1,), (14.1,)), predictions.eval()) + self.assertAllClose(((3.1,), (14.1,)), self.evaluate(predictions)) def test_old_linear_model_with_weights(self): @@ -1461,7 +1522,7 @@ class CrossedColumnTest(test.TestCase): Uses data from test_get_sparse_tesnsors_simple. """ - a = fc_old.numeric_column('a', dtype=dtypes.int32, shape=(2,)) + a = fc_old._numeric_column('a', dtype=dtypes.int32, shape=(2,)) b = fc.bucketized_column(a, boundaries=(0, 1)) crossed = fc.crossed_column([b, 'c'], hash_bucket_size=5, hash_key=5) with ops.Graph().as_default(): @@ -1477,16 +1538,17 @@ class CrossedColumnTest(test.TestCase): bias = get_linear_model_bias() crossed_var = get_linear_model_column_var(crossed) with _initialized_session() as sess: - self.assertAllClose((0.,), bias.eval()) + self.assertAllClose((0.,), self.evaluate(bias)) self.assertAllClose(((0.,), (0.,), (0.,), (0.,), (0.,)), - crossed_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) + self.evaluate(crossed_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) sess.run(crossed_var.assign(((1.,), (2.,), (3.,), (4.,), (5.,)))) # Expected ids after cross = (1, 0, 1, 3, 4, 2) - self.assertAllClose(((3.,), (14.,)), predictions.eval()) + self.assertAllClose(((3.,), (14.,)), self.evaluate(predictions)) sess.run(bias.assign((.1,))) - self.assertAllClose(((3.1,), (14.1,)), predictions.eval()) + self.assertAllClose(((3.1,), (14.1,)), self.evaluate(predictions)) + @test_util.run_deprecated_v1 def test_serialization(self): a = fc.numeric_column('a', dtype=dtypes.int32, shape=(2,)) b = fc.bucketized_column(a, boundaries=(0, 1)) @@ -1528,7 +1590,6 @@ class CrossedColumnTest(test.TestCase): self.assertIs(b, new_crossed.keys[0]) - class LinearModelTest(test.TestCase): def test_raises_if_empty_feature_columns(self): @@ -1581,7 +1642,7 @@ class LinearModelTest(test.TestCase): features = [[1.], [5.]] model = fc.LinearModel([price]) with self.assertRaisesRegexp(ValueError, 'We expected a dictionary here'): - predictions = model(features) + model(features) def test_dense_bias(self): price = fc.numeric_column('price') @@ -1591,10 +1652,10 @@ class LinearModelTest(test.TestCase): predictions = model(features) price_var, bias = model.variables with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) + self.assertAllClose([0.], self.evaluate(bias)) sess.run(price_var.assign([[10.]])) sess.run(bias.assign([5.])) - self.assertAllClose([[15.], [55.]], predictions.eval()) + self.assertAllClose([[15.], [55.]], self.evaluate(predictions)) def test_sparse_bias(self): wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) @@ -1608,11 +1669,12 @@ class LinearModelTest(test.TestCase): predictions = model(features) wire_cast_var, bias = model.variables with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) - self.assertAllClose([[0.], [0.], [0.], [0.]], wire_cast_var.eval()) + self.assertAllClose([0.], self.evaluate(bias)) + self.assertAllClose([[0.], [0.], [0.], [0.]], + self.evaluate(wire_cast_var)) sess.run(wire_cast_var.assign([[10.], [100.], [1000.], [10000.]])) sess.run(bias.assign([5.])) - self.assertAllClose([[1005.], [10015.]], predictions.eval()) + self.assertAllClose([[1005.], [10015.]], self.evaluate(predictions)) def test_dense_and_sparse_bias(self): wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) @@ -1630,7 +1692,7 @@ class LinearModelTest(test.TestCase): sess.run(wire_cast_var.assign([[10.], [100.], [1000.], [10000.]])) sess.run(bias.assign([5.])) sess.run(price_var.assign([[10.]])) - self.assertAllClose([[1015.], [10065.]], predictions.eval()) + self.assertAllClose([[1015.], [10065.]], self.evaluate(predictions)) def test_dense_and_sparse_column(self): """When the column is both dense and sparse, uses sparse tensors.""" @@ -1682,10 +1744,11 @@ class LinearModelTest(test.TestCase): predictions = model(features) dense_and_sparse_column_var, bias = model.variables with _initialized_session() as sess: - sess.run(dense_and_sparse_column_var.assign( - [[10.], [100.], [1000.], [10000.]])) + sess.run( + dense_and_sparse_column_var.assign([[10.], [100.], [1000.], + [10000.]])) sess.run(bias.assign([5.])) - self.assertAllClose([[1005.], [10015.]], predictions.eval()) + self.assertAllClose([[1005.], [10015.]], self.evaluate(predictions)) def test_dense_multi_output(self): price = fc.numeric_column('price') @@ -1695,12 +1758,12 @@ class LinearModelTest(test.TestCase): predictions = model(features) price_var, bias = model.variables with _initialized_session() as sess: - self.assertAllClose(np.zeros((3,)), bias.eval()) - self.assertAllClose(np.zeros((1, 3)), price_var.eval()) + self.assertAllClose(np.zeros((3,)), self.evaluate(bias)) + self.assertAllClose(np.zeros((1, 3)), self.evaluate(price_var)) sess.run(price_var.assign([[10., 100., 1000.]])) sess.run(bias.assign([5., 6., 7.])) self.assertAllClose([[15., 106., 1007.], [55., 506., 5007.]], - predictions.eval()) + self.evaluate(predictions)) def test_sparse_multi_output(self): wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) @@ -1714,15 +1777,15 @@ class LinearModelTest(test.TestCase): predictions = model(features) wire_cast_var, bias = model.variables with _initialized_session() as sess: - self.assertAllClose(np.zeros((3,)), bias.eval()) - self.assertAllClose(np.zeros((4, 3)), wire_cast_var.eval()) + self.assertAllClose(np.zeros((3,)), self.evaluate(bias)) + self.assertAllClose(np.zeros((4, 3)), self.evaluate(wire_cast_var)) sess.run( - wire_cast_var.assign([[10., 11., 12.], [100., 110., 120.], [ - 1000., 1100., 1200. - ], [10000., 11000., 12000.]])) + wire_cast_var.assign([[10., 11., 12.], [100., 110., 120.], + [1000., 1100., 1200.], + [10000., 11000., 12000.]])) sess.run(bias.assign([5., 6., 7.])) self.assertAllClose([[1005., 1106., 1207.], [10015., 11017., 12019.]], - predictions.eval()) + self.evaluate(predictions)) def test_dense_multi_dimension(self): price = fc.numeric_column('price', shape=2) @@ -1732,9 +1795,9 @@ class LinearModelTest(test.TestCase): predictions = model(features) price_var, _ = model.variables with _initialized_session() as sess: - self.assertAllClose([[0.], [0.]], price_var.eval()) + self.assertAllClose([[0.], [0.]], self.evaluate(price_var)) sess.run(price_var.assign([[10.], [100.]])) - self.assertAllClose([[210.], [650.]], predictions.eval()) + self.assertAllClose([[210.], [650.]], self.evaluate(predictions)) def test_sparse_multi_rank(self): wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) @@ -1749,7 +1812,7 @@ class LinearModelTest(test.TestCase): predictions = model(features) wire_cast_var, _ = model.variables with _initialized_session() as sess: - self.assertAllClose(np.zeros((4, 1)), wire_cast_var.eval()) + self.assertAllClose(np.zeros((4, 1)), self.evaluate(wire_cast_var)) self.assertAllClose( np.zeros((2, 1)), predictions.eval(feed_dict={wire_tensor: wire_value})) @@ -1772,7 +1835,7 @@ class LinearModelTest(test.TestCase): with _initialized_session() as sess: sess.run(wire_cast_var.assign([[10.], [100.], [1000.], [10000.]])) sess.run(bias.assign([5.])) - self.assertAllClose([[1005.], [5010.]], predictions.eval()) + self.assertAllClose([[1005.], [5010.]], self.evaluate(predictions)) def test_sparse_combiner_with_negative_weights(self): wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) @@ -1793,7 +1856,7 @@ class LinearModelTest(test.TestCase): with _initialized_session() as sess: sess.run(wire_cast_var.assign([[10.], [100.], [1000.], [10000.]])) sess.run(bias.assign([5.])) - self.assertAllClose([[1005.], [-9985.]], predictions.eval()) + self.assertAllClose([[1005.], [-9985.]], self.evaluate(predictions)) def test_dense_multi_dimension_multi_output(self): price = fc.numeric_column('price', shape=2) @@ -1803,12 +1866,12 @@ class LinearModelTest(test.TestCase): predictions = model(features) price_var, bias = model.variables with _initialized_session() as sess: - self.assertAllClose(np.zeros((3,)), bias.eval()) - self.assertAllClose(np.zeros((2, 3)), price_var.eval()) + self.assertAllClose(np.zeros((3,)), self.evaluate(bias)) + self.assertAllClose(np.zeros((2, 3)), self.evaluate(price_var)) sess.run(price_var.assign([[1., 2., 3.], [10., 100., 1000.]])) sess.run(bias.assign([2., 3., 4.])) self.assertAllClose([[23., 205., 2007.], [67., 613., 6019.]], - predictions.eval()) + self.evaluate(predictions)) def test_raises_if_shape_mismatch(self): price = fc.numeric_column('price', shape=2) @@ -1828,32 +1891,29 @@ class LinearModelTest(test.TestCase): predictions = model(features) price_var, bias = model.variables with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) - self.assertAllClose([[0.], [0.]], price_var.eval()) - self.assertAllClose([[0.], [0.]], predictions.eval()) + self.assertAllClose([0.], self.evaluate(bias)) + self.assertAllClose([[0.], [0.]], self.evaluate(price_var)) + self.assertAllClose([[0.], [0.]], self.evaluate(predictions)) sess.run(price_var.assign([[10.], [100.]])) - self.assertAllClose([[210.], [650.]], predictions.eval()) + self.assertAllClose([[210.], [650.]], self.evaluate(predictions)) def test_dense_multi_column(self): price1 = fc.numeric_column('price1', shape=2) price2 = fc.numeric_column('price2') with ops.Graph().as_default(): - features = { - 'price1': [[1., 2.], [5., 6.]], - 'price2': [[3.], [4.]] - } + features = {'price1': [[1., 2.], [5., 6.]], 'price2': [[3.], [4.]]} model = fc.LinearModel([price1, price2]) predictions = model(features) price1_var, price2_var, bias = model.variables with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) - self.assertAllClose([[0.], [0.]], price1_var.eval()) - self.assertAllClose([[0.]], price2_var.eval()) - self.assertAllClose([[0.], [0.]], predictions.eval()) + self.assertAllClose([0.], self.evaluate(bias)) + self.assertAllClose([[0.], [0.]], self.evaluate(price1_var)) + self.assertAllClose([[0.]], self.evaluate(price2_var)) + self.assertAllClose([[0.], [0.]], self.evaluate(predictions)) sess.run(price1_var.assign([[10.], [100.]])) sess.run(price2_var.assign([[1000.]])) sess.run(bias.assign([7.])) - self.assertAllClose([[3217.], [4657.]], predictions.eval()) + self.assertAllClose([[3217.], [4657.]], self.evaluate(predictions)) def test_dense_trainable_default(self): price = fc.numeric_column('price') @@ -2046,6 +2106,7 @@ class LinearModelTest(test.TestCase): features['price2']: [[1.], [5.]], }) + @test_util.run_deprecated_v1 def test_with_numpy_input_fn(self): price = fc.numeric_column('price') price_buckets = fc.bucketized_column( @@ -2078,11 +2139,13 @@ class LinearModelTest(test.TestCase): sess.run(body_style_var.assign([[-10.], [-100.], [-1000.]])) sess.run(bias.assign([5.])) - self.assertAllClose([[10 - 1000 + 5.], [100 - 10 + 5.]], sess.run(net)) + self.assertAllClose([[10 - 1000 + 5.], [100 - 10 + 5.]], + self.evaluate(net)) coord.request_stop() coord.join(threads) + @test_util.run_deprecated_v1 def test_with_1d_sparse_tensor(self): price = fc.numeric_column('price') price_buckets = fc.bucketized_column( @@ -2096,11 +2159,16 @@ class LinearModelTest(test.TestCase): # Provides 1-dim tensor and dense tensor. features = { - 'price': constant_op.constant([-1., 12.,]), - 'body-style': sparse_tensor.SparseTensor( - indices=((0,), (1,)), - values=('sedan', 'hardtop'), - dense_shape=(2,)), + 'price': + constant_op.constant([ + -1., + 12., + ]), + 'body-style': + sparse_tensor.SparseTensor( + indices=((0,), (1,)), + values=('sedan', 'hardtop'), + dense_shape=(2,)), } self.assertEqual(1, features['price'].shape.ndims) self.assertEqual(1, features['body-style'].dense_shape.get_shape()[0]) @@ -2114,8 +2182,10 @@ class LinearModelTest(test.TestCase): sess.run(body_style_var.assign([[-10.], [-100.], [-1000.]])) sess.run(bias.assign([5.])) - self.assertAllClose([[10 - 1000 + 5.], [1000 - 10 + 5.]], sess.run(net)) + self.assertAllClose([[10 - 1000 + 5.], [1000 - 10 + 5.]], + self.evaluate(net)) + @test_util.run_deprecated_v1 def test_with_1d_unknown_shape_sparse_tensor(self): price = fc.numeric_column('price') price_buckets = fc.bucketized_column( @@ -2140,9 +2210,7 @@ class LinearModelTest(test.TestCase): price_data = np.array([-1., 12.]) body_style_data = sparse_tensor.SparseTensorValue( - indices=((0,), (1,)), - values=('sedan', 'hardtop'), - dense_shape=(2,)) + indices=((0,), (1,)), values=('sedan', 'hardtop'), dense_shape=(2,)) country_data = np.array(['US', 'CA']) model = fc.LinearModel([price_buckets, body_style, country]) @@ -2162,6 +2230,7 @@ class LinearModelTest(test.TestCase): features['country']: country_data })) + @test_util.run_deprecated_v1 def test_with_rank_0_feature(self): price = fc.numeric_column('price') features = { @@ -2197,14 +2266,14 @@ class LinearModelTest(test.TestCase): price_var1, bias1 = model1.variables price_var2, bias2 = model2.variables with _initialized_session() as sess: - self.assertAllClose([0.], bias1.eval()) + self.assertAllClose([0.], self.evaluate(bias1)) sess.run(price_var1.assign([[10.]])) sess.run(bias1.assign([5.])) - self.assertAllClose([[15.], [55.]], predictions1.eval()) - self.assertAllClose([0.], bias2.eval()) + self.assertAllClose([[15.], [55.]], self.evaluate(predictions1)) + self.assertAllClose([0.], self.evaluate(bias2)) sess.run(price_var2.assign([[10.]])) sess.run(bias2.assign([5.])) - self.assertAllClose([[25.], [105.]], predictions2.eval()) + self.assertAllClose([[25.], [105.]], self.evaluate(predictions2)) class OldLinearModelTest(test.TestCase): @@ -2272,10 +2341,10 @@ class OldLinearModelTest(test.TestCase): bias = get_linear_model_bias() price_var = get_linear_model_column_var(price) with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) + self.assertAllClose([0.], self.evaluate(bias)) sess.run(price_var.assign([[10.]])) sess.run(bias.assign([5.])) - self.assertAllClose([[15.], [55.]], predictions.eval()) + self.assertAllClose([[15.], [55.]], self.evaluate(predictions)) def test_sparse_bias(self): wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) @@ -2289,11 +2358,12 @@ class OldLinearModelTest(test.TestCase): bias = get_linear_model_bias() wire_cast_var = get_linear_model_column_var(wire_cast) with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) - self.assertAllClose([[0.], [0.], [0.], [0.]], wire_cast_var.eval()) + self.assertAllClose([0.], self.evaluate(bias)) + self.assertAllClose([[0.], [0.], [0.], [0.]], + self.evaluate(wire_cast_var)) sess.run(wire_cast_var.assign([[10.], [100.], [1000.], [10000.]])) sess.run(bias.assign([5.])) - self.assertAllClose([[1005.], [10015.]], predictions.eval()) + self.assertAllClose([[1005.], [10015.]], self.evaluate(predictions)) def test_dense_and_sparse_bias(self): wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) @@ -2312,7 +2382,7 @@ class OldLinearModelTest(test.TestCase): sess.run(wire_cast_var.assign([[10.], [100.], [1000.], [10000.]])) sess.run(bias.assign([5.])) sess.run(price_var.assign([[10.]])) - self.assertAllClose([[1015.], [10065.]], predictions.eval()) + self.assertAllClose([[1015.], [10065.]], self.evaluate(predictions)) def test_dense_and_sparse_column(self): """When the column is both dense and sparse, uses sparse tensors.""" @@ -2394,7 +2464,7 @@ class OldLinearModelTest(test.TestCase): dense_and_sparse_column_var.assign([[10.], [100.], [1000.], [10000.]])) sess.run(bias.assign([5.])) - self.assertAllClose([[1005.], [10015.]], predictions.eval()) + self.assertAllClose([[1005.], [10015.]], self.evaluate(predictions)) def test_dense_multi_output(self): price = fc.numeric_column('price') @@ -2404,12 +2474,12 @@ class OldLinearModelTest(test.TestCase): bias = get_linear_model_bias() price_var = get_linear_model_column_var(price) with _initialized_session() as sess: - self.assertAllClose(np.zeros((3,)), bias.eval()) - self.assertAllClose(np.zeros((1, 3)), price_var.eval()) + self.assertAllClose(np.zeros((3,)), self.evaluate(bias)) + self.assertAllClose(np.zeros((1, 3)), self.evaluate(price_var)) sess.run(price_var.assign([[10., 100., 1000.]])) sess.run(bias.assign([5., 6., 7.])) self.assertAllClose([[15., 106., 1007.], [55., 506., 5007.]], - predictions.eval()) + self.evaluate(predictions)) def test_sparse_multi_output(self): wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) @@ -2423,15 +2493,15 @@ class OldLinearModelTest(test.TestCase): bias = get_linear_model_bias() wire_cast_var = get_linear_model_column_var(wire_cast) with _initialized_session() as sess: - self.assertAllClose(np.zeros((3,)), bias.eval()) - self.assertAllClose(np.zeros((4, 3)), wire_cast_var.eval()) + self.assertAllClose(np.zeros((3,)), self.evaluate(bias)) + self.assertAllClose(np.zeros((4, 3)), self.evaluate(wire_cast_var)) sess.run( wire_cast_var.assign([[10., 11., 12.], [100., 110., 120.], [1000., 1100., 1200.], [10000., 11000., 12000.]])) sess.run(bias.assign([5., 6., 7.])) self.assertAllClose([[1005., 1106., 1207.], [10015., 11017., 12019.]], - predictions.eval()) + self.evaluate(predictions)) def test_dense_multi_dimension(self): price = fc.numeric_column('price', shape=2) @@ -2440,9 +2510,9 @@ class OldLinearModelTest(test.TestCase): predictions = fc_old.linear_model(features, [price]) price_var = get_linear_model_column_var(price) with _initialized_session() as sess: - self.assertAllClose([[0.], [0.]], price_var.eval()) + self.assertAllClose([[0.], [0.]], self.evaluate(price_var)) sess.run(price_var.assign([[10.], [100.]])) - self.assertAllClose([[210.], [650.]], predictions.eval()) + self.assertAllClose([[210.], [650.]], self.evaluate(predictions)) def test_sparse_multi_rank(self): wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) @@ -2456,7 +2526,7 @@ class OldLinearModelTest(test.TestCase): predictions = fc_old.linear_model(features, [wire_cast]) wire_cast_var = get_linear_model_column_var(wire_cast) with _initialized_session() as sess: - self.assertAllClose(np.zeros((4, 1)), wire_cast_var.eval()) + self.assertAllClose(np.zeros((4, 1)), self.evaluate(wire_cast_var)) self.assertAllClose( np.zeros((2, 1)), predictions.eval(feed_dict={wire_tensor: wire_value})) @@ -2480,7 +2550,7 @@ class OldLinearModelTest(test.TestCase): with _initialized_session() as sess: sess.run(wire_cast_var.assign([[10.], [100.], [1000.], [10000.]])) sess.run(bias.assign([5.])) - self.assertAllClose([[1005.], [5010.]], predictions.eval()) + self.assertAllClose([[1005.], [5010.]], self.evaluate(predictions)) def test_sparse_combiner_with_negative_weights(self): wire_cast = fc.categorical_column_with_hash_bucket('wire_cast', 4) @@ -2502,7 +2572,7 @@ class OldLinearModelTest(test.TestCase): with _initialized_session() as sess: sess.run(wire_cast_var.assign([[10.], [100.], [1000.], [10000.]])) sess.run(bias.assign([5.])) - self.assertAllClose([[1005.], [-9985.]], predictions.eval()) + self.assertAllClose([[1005.], [-9985.]], self.evaluate(predictions)) def test_dense_multi_dimension_multi_output(self): price = fc.numeric_column('price', shape=2) @@ -2512,12 +2582,12 @@ class OldLinearModelTest(test.TestCase): bias = get_linear_model_bias() price_var = get_linear_model_column_var(price) with _initialized_session() as sess: - self.assertAllClose(np.zeros((3,)), bias.eval()) - self.assertAllClose(np.zeros((2, 3)), price_var.eval()) + self.assertAllClose(np.zeros((3,)), self.evaluate(bias)) + self.assertAllClose(np.zeros((2, 3)), self.evaluate(price_var)) sess.run(price_var.assign([[1., 2., 3.], [10., 100., 1000.]])) sess.run(bias.assign([2., 3., 4.])) self.assertAllClose([[23., 205., 2007.], [67., 613., 6019.]], - predictions.eval()) + self.evaluate(predictions)) def test_raises_if_shape_mismatch(self): price = fc.numeric_column('price', shape=2) @@ -2536,11 +2606,11 @@ class OldLinearModelTest(test.TestCase): bias = get_linear_model_bias() price_var = get_linear_model_column_var(price) with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) - self.assertAllClose([[0.], [0.]], price_var.eval()) - self.assertAllClose([[0.], [0.]], predictions.eval()) + self.assertAllClose([0.], self.evaluate(bias)) + self.assertAllClose([[0.], [0.]], self.evaluate(price_var)) + self.assertAllClose([[0.], [0.]], self.evaluate(predictions)) sess.run(price_var.assign([[10.], [100.]])) - self.assertAllClose([[210.], [650.]], predictions.eval()) + self.assertAllClose([[210.], [650.]], self.evaluate(predictions)) def test_dense_multi_column(self): price1 = fc.numeric_column('price1', shape=2) @@ -2552,14 +2622,14 @@ class OldLinearModelTest(test.TestCase): price1_var = get_linear_model_column_var(price1) price2_var = get_linear_model_column_var(price2) with _initialized_session() as sess: - self.assertAllClose([0.], bias.eval()) - self.assertAllClose([[0.], [0.]], price1_var.eval()) - self.assertAllClose([[0.]], price2_var.eval()) - self.assertAllClose([[0.], [0.]], predictions.eval()) + self.assertAllClose([0.], self.evaluate(bias)) + self.assertAllClose([[0.], [0.]], self.evaluate(price1_var)) + self.assertAllClose([[0.]], self.evaluate(price2_var)) + self.assertAllClose([[0.], [0.]], self.evaluate(predictions)) sess.run(price1_var.assign([[10.], [100.]])) sess.run(price2_var.assign([[1000.]])) sess.run(bias.assign([7.])) - self.assertAllClose([[3217.], [4657.]], predictions.eval()) + self.assertAllClose([[3217.], [4657.]], self.evaluate(predictions)) def test_fills_cols_to_vars(self): price1 = fc.numeric_column('price1', shape=2) @@ -2589,15 +2659,18 @@ class OldLinearModelTest(test.TestCase): partitioner=partitioned_variables.fixed_size_partitioner(2, axis=0)): fc_old.linear_model( features, [price1, price2], cols_to_vars=cols_to_vars) - with _initialized_session(): - self.assertEqual([0.], cols_to_vars['bias'][0].eval()) - # Partitioning shards the [2, 1] price1 var into 2 [1, 1] Variables. - self.assertAllEqual([[0.]], cols_to_vars[price1][0].eval()) - self.assertAllEqual([[0.]], cols_to_vars[price1][1].eval()) - # Partitioning shards the [3, 1] price2 var into a [2, 1] Variable and - # a [1, 1] Variable. - self.assertAllEqual([[0.], [0.]], cols_to_vars[price2][0].eval()) - self.assertAllEqual([[0.]], cols_to_vars[price2][1].eval()) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertEqual([0.], self.evaluate(cols_to_vars['bias'][0])) + # Partitioning shards the [2, 1] price1 var into 2 [1, 1] Variables. + self.assertAllEqual([[0.]], self.evaluate(cols_to_vars[price1][0])) + self.assertAllEqual([[0.]], self.evaluate(cols_to_vars[price1][1])) + # Partitioning shards the [3, 1] price2 var into a [2, 1] Variable and + # a [1, 1] Variable. + self.assertAllEqual([[0.], [0.]], self.evaluate(cols_to_vars[price2][0])) + self.assertAllEqual([[0.]], self.evaluate(cols_to_vars[price2][1])) def test_fills_cols_to_output_tensors(self): # Provide three _DenseColumn's to input_layer: a _NumericColumn, a @@ -2795,6 +2868,7 @@ class OldLinearModelTest(test.TestCase): features['price2']: [[1.], [5.]], }) + @test_util.run_deprecated_v1 def test_with_1d_sparse_tensor(self): price = fc.numeric_column('price') price_buckets = fc.bucketized_column( @@ -2832,8 +2906,10 @@ class OldLinearModelTest(test.TestCase): sess.run(body_style_var.assign([[-10.], [-100.], [-1000.]])) sess.run(bias.assign([5.])) - self.assertAllClose([[10 - 1000 + 5.], [1000 - 10 + 5.]], sess.run(net)) + self.assertAllClose([[10 - 1000 + 5.], [1000 - 10 + 5.]], + self.evaluate(net)) + @test_util.run_deprecated_v1 def test_with_1d_unknown_shape_sparse_tensor(self): price = fc.numeric_column('price') price_buckets = fc.bucketized_column( @@ -2879,6 +2955,7 @@ class OldLinearModelTest(test.TestCase): features['country']: country_data })) + @test_util.run_deprecated_v1 def test_with_rank_0_feature(self): price = fc.numeric_column('price') features = { @@ -2912,26 +2989,27 @@ class OldLinearModelTest(test.TestCase): price_var1 = get_linear_model_column_var(price, name='linear_model') price_var2 = get_linear_model_column_var(price, name='linear_model_1') with _initialized_session() as sess: - self.assertAllClose([0.], bias1.eval()) + self.assertAllClose([0.], self.evaluate(bias1)) sess.run(price_var1.assign([[10.]])) sess.run(bias1.assign([5.])) - self.assertAllClose([[15.], [55.]], predictions1.eval()) - self.assertAllClose([0.], bias2.eval()) + self.assertAllClose([[15.], [55.]], self.evaluate(predictions1)) + self.assertAllClose([0.], self.evaluate(bias2)) sess.run(price_var2.assign([[10.]])) sess.run(bias2.assign([5.])) - self.assertAllClose([[25.], [105.]], predictions2.eval()) + self.assertAllClose([[25.], [105.]], self.evaluate(predictions2)) + @test_util.run_deprecated_v1 def test_linear_model_v1_shared_embedding_all_other_v2(self): price = fc.numeric_column('price') # v2 some_sparse_column = fc.categorical_column_with_hash_bucket( 'sparse_feature', hash_bucket_size=5) # v2 some_embedding_column = fc.embedding_column( some_sparse_column, dimension=10) # v2 - categorical_column_a = fc_old.categorical_column_with_identity( + categorical_column_a = fc.categorical_column_with_identity( key='aaa', num_buckets=3) # v2 - categorical_column_b = fc_old.categorical_column_with_identity( + categorical_column_b = fc.categorical_column_with_identity( key='bbb', num_buckets=3) # v2 - shared_embedding_a, shared_embedding_b = fc_old.shared_embedding_columns( + shared_embedding_a, shared_embedding_b = fc.shared_embedding_columns( [categorical_column_a, categorical_column_b], dimension=2) # v1 all_cols = [ price, some_embedding_column, shared_embedding_a, shared_embedding_b @@ -2954,9 +3032,13 @@ class OldLinearModelTest(test.TestCase): } fc_old.linear_model(features, all_cols) bias = get_linear_model_bias() - with _initialized_session(): - self.assertAllClose([0.], bias.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose([0.], self.evaluate(bias)) + + @test_util.run_deprecated_v1 def test_linear_model_v1_shared_embedding_with_v2_cat_all_other_v2(self): price = fc.numeric_column('price') # v2 some_sparse_column = fc.categorical_column_with_hash_bucket( @@ -2967,7 +3049,7 @@ class OldLinearModelTest(test.TestCase): key='aaa', num_buckets=3) # v2 categorical_column_b = fc.categorical_column_with_identity( key='bbb', num_buckets=3) # v2 - shared_embedding_a, shared_embedding_b = fc_old.shared_embedding_columns( + shared_embedding_a, shared_embedding_b = fc.shared_embedding_columns( [categorical_column_a, categorical_column_b], dimension=2) # v1 all_cols = [ price, some_embedding_column, shared_embedding_a, shared_embedding_b @@ -2990,20 +3072,24 @@ class OldLinearModelTest(test.TestCase): } fc_old.linear_model(features, all_cols) bias = get_linear_model_bias() - with _initialized_session(): - self.assertAllClose([0.], bias.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose([0.], self.evaluate(bias)) + + @test_util.run_deprecated_v1 def test_linear_model_v1_v2_mix(self): price = fc.numeric_column('price') # v2 - some_sparse_column = fc_old.categorical_column_with_hash_bucket( + some_sparse_column = fc.categorical_column_with_hash_bucket( 'sparse_feature', hash_bucket_size=5) # v1 - some_embedding_column = fc_old.embedding_column( + some_embedding_column = fc.embedding_column( some_sparse_column, dimension=10) # v1 - categorical_column_a = fc_old.categorical_column_with_identity( + categorical_column_a = fc.categorical_column_with_identity( key='aaa', num_buckets=3) # v2 - categorical_column_b = fc_old.categorical_column_with_identity( + categorical_column_b = fc.categorical_column_with_identity( key='bbb', num_buckets=3) # v2 - shared_embedding_a, shared_embedding_b = fc_old.shared_embedding_columns( + shared_embedding_a, shared_embedding_b = fc.shared_embedding_columns( [categorical_column_a, categorical_column_b], dimension=2) # v1 all_cols = [ price, some_embedding_column, shared_embedding_a, shared_embedding_b @@ -3026,14 +3112,18 @@ class OldLinearModelTest(test.TestCase): } fc_old.linear_model(features, all_cols) bias = get_linear_model_bias() - with _initialized_session(): - self.assertAllClose([0.], bias.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose([0.], self.evaluate(bias)) + + @test_util.run_deprecated_v1 def test_linear_model_v2_shared_embedding_all_other_v1(self): - price = fc_old.numeric_column('price') # v1 - some_sparse_column = fc_old.categorical_column_with_hash_bucket( + price = fc.numeric_column('price') # v1 + some_sparse_column = fc.categorical_column_with_hash_bucket( 'sparse_feature', hash_bucket_size=5) # v1 - some_embedding_column = fc_old.embedding_column( + some_embedding_column = fc.embedding_column( some_sparse_column, dimension=10) # v1 categorical_column_a = fc.categorical_column_with_identity( key='aaa', num_buckets=3) # v2 @@ -3065,13 +3155,13 @@ class OldLinearModelTest(test.TestCase): fc_old.linear_model(features, all_cols) -class FeatureLayerTest(test.TestCase): +class DenseFeaturesTest(test.TestCase): @test_util.run_in_graph_and_eager_modes() def test_retrieving_input(self): features = {'a': [0.]} - feature_layer = fc.FeatureLayer(fc.numeric_column('a')) - inputs = self.evaluate(feature_layer(features)) + dense_features = fc.DenseFeatures(fc.numeric_column('a')) + inputs = self.evaluate(dense_features(features)) self.assertAllClose([[0.]], inputs) def test_reuses_variables(self): @@ -3085,6 +3175,7 @@ class FeatureLayerTest(test.TestCase): categorical_column = fc.categorical_column_with_identity( key='a', num_buckets=3) embedding_dimension = 2 + def _embedding_column_initializer(shape, dtype, partition_info): del shape # unused del dtype # unused @@ -3100,11 +3191,11 @@ class FeatureLayerTest(test.TestCase): dimension=embedding_dimension, initializer=_embedding_column_initializer) - feature_layer = fc.FeatureLayer([embedding_column]) + dense_features = fc.DenseFeatures([embedding_column]) features = {'a': sparse_input} - inputs = feature_layer(features) - variables = feature_layer.variables + inputs = dense_features(features) + variables = dense_features.variables # Sanity check: test that the inputs are correct. self.assertAllEqual([[1, 0], [0, 1], [1, 1]], inputs) @@ -3112,13 +3203,13 @@ class FeatureLayerTest(test.TestCase): # Check that only one variable was created. self.assertEqual(1, len(variables)) - # Check that invoking feature_layer on the same features does not create + # Check that invoking dense_features on the same features does not create # additional variables - _ = feature_layer(features) + _ = dense_features(features) self.assertEqual(1, len(variables)) - self.assertEqual(variables[0], feature_layer.variables[0]) + self.assertEqual(variables[0], dense_features.variables[0]) - def test_feature_column_feature_layer_gradient(self): + def test_feature_column_dense_features_gradient(self): with context.eager_mode(): sparse_input = sparse_tensor.SparseTensor( indices=((0, 0), (1, 0), (2, 0)), @@ -3145,11 +3236,11 @@ class FeatureLayerTest(test.TestCase): dimension=embedding_dimension, initializer=_embedding_column_initializer) - feature_layer = fc.FeatureLayer([embedding_column]) + dense_features = fc.DenseFeatures([embedding_column]) features = {'a': sparse_input} def scale_matrix(): - matrix = feature_layer(features) + matrix = dense_features(features) return 2 * matrix # Sanity check: Verify that scale_matrix returns the correct output. @@ -3167,11 +3258,11 @@ class FeatureLayerTest(test.TestCase): def test_raises_if_empty_feature_columns(self): with self.assertRaisesRegexp(ValueError, 'feature_columns must not be empty'): - fc.FeatureLayer(feature_columns=[])(features={}) + fc.DenseFeatures(feature_columns=[])(features={}) def test_should_be_dense_column(self): with self.assertRaisesRegexp(ValueError, 'must be a DenseColumn'): - fc.FeatureLayer(feature_columns=[ + fc.DenseFeatures(feature_columns=[ fc.categorical_column_with_hash_bucket('wire_cast', 4) ])( features={ @@ -3181,7 +3272,7 @@ class FeatureLayerTest(test.TestCase): def test_does_not_support_dict_columns(self): with self.assertRaisesRegexp( ValueError, 'Expected feature_columns to be iterable, found dict.'): - fc.FeatureLayer(feature_columns={'a': fc.numeric_column('a')})( + fc.DenseFeatures(feature_columns={'a': fc.numeric_column('a')})( features={ 'a': [[0]] }) @@ -3189,22 +3280,28 @@ class FeatureLayerTest(test.TestCase): def test_bare_column(self): with ops.Graph().as_default(): features = features = {'a': [0.]} - net = fc.FeatureLayer(fc.numeric_column('a'))(features) - with _initialized_session(): - self.assertAllClose([[0.]], net.eval()) + net = fc.DenseFeatures(fc.numeric_column('a'))(features) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose([[0.]], self.evaluate(net)) def test_column_generator(self): with ops.Graph().as_default(): features = features = {'a': [0.], 'b': [1.]} columns = (fc.numeric_column(key) for key in features) - net = fc.FeatureLayer(columns)(features) - with _initialized_session(): - self.assertAllClose([[0., 1.]], net.eval()) + net = fc.DenseFeatures(columns)(features) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose([[0., 1.]], self.evaluate(net)) def test_raises_if_duplicate_name(self): with self.assertRaisesRegexp( ValueError, 'Duplicate feature column name found for columns'): - fc.FeatureLayer( + fc.DenseFeatures( feature_columns=[fc.numeric_column('a'), fc.numeric_column('a')])( features={ @@ -3215,17 +3312,23 @@ class FeatureLayerTest(test.TestCase): price = fc.numeric_column('price') with ops.Graph().as_default(): features = {'price': [[1.], [5.]]} - net = fc.FeatureLayer([price])(features) - with _initialized_session(): - self.assertAllClose([[1.], [5.]], net.eval()) + net = fc.DenseFeatures([price])(features) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose([[1.], [5.]], self.evaluate(net)) def test_multi_dimension(self): price = fc.numeric_column('price', shape=2) with ops.Graph().as_default(): features = {'price': [[1., 2.], [5., 6.]]} - net = fc.FeatureLayer([price])(features) - with _initialized_session(): - self.assertAllClose([[1., 2.], [5., 6.]], net.eval()) + net = fc.DenseFeatures([price])(features) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose([[1., 2.], [5., 6.]], self.evaluate(net)) def test_compute_output_shape(self): price1 = fc.numeric_column('price1', shape=2) @@ -3235,12 +3338,15 @@ class FeatureLayerTest(test.TestCase): 'price1': [[1., 2.], [5., 6.]], 'price2': [[3., 4., 5., 6.], [7., 8., 9., 10.]] } - feature_layer = fc.FeatureLayer([price1, price2]) - self.assertEqual((None, 6), feature_layer.compute_output_shape((None,))) - net = feature_layer(features) - with _initialized_session(): - self.assertAllClose( - [[1., 2., 3., 4., 5., 6.], [5., 6., 7., 8., 9., 10.]], net.eval()) + dense_features = fc.DenseFeatures([price1, price2]) + self.assertEqual((None, 6), dense_features.compute_output_shape((None,))) + net = dense_features(features) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose([[1., 2., 3., 4., 5., 6.], [5., 6., 7., 8., 9., 10.]], + self.evaluate(net)) def test_raises_if_shape_mismatch(self): price = fc.numeric_column('price', shape=2) @@ -3249,27 +3355,30 @@ class FeatureLayerTest(test.TestCase): with self.assertRaisesRegexp( Exception, r'Cannot reshape a tensor with 2 elements to shape \[2,2\]'): - fc.FeatureLayer([price])(features) + fc.DenseFeatures([price])(features) def test_reshaping(self): price = fc.numeric_column('price', shape=[1, 2]) with ops.Graph().as_default(): features = {'price': [[[1., 2.]], [[5., 6.]]]} - net = fc.FeatureLayer([price])(features) - with _initialized_session(): - self.assertAllClose([[1., 2.], [5., 6.]], net.eval()) + net = fc.DenseFeatures([price])(features) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose([[1., 2.], [5., 6.]], self.evaluate(net)) def test_multi_column(self): price1 = fc.numeric_column('price1', shape=2) price2 = fc.numeric_column('price2') with ops.Graph().as_default(): - features = { - 'price1': [[1., 2.], [5., 6.]], - 'price2': [[3.], [4.]] - } - net = fc.FeatureLayer([price1, price2])(features) - with _initialized_session(): - self.assertAllClose([[1., 2., 3.], [5., 6., 4.]], net.eval()) + features = {'price1': [[1., 2.], [5., 6.]], 'price2': [[3.], [4.]]} + net = fc.DenseFeatures([price1, price2])(features) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose([[1., 2., 3.], [5., 6., 4.]], self.evaluate(net)) def test_cols_to_output_tensors(self): price1 = fc.numeric_column('price1', shape=2) @@ -3277,12 +3386,16 @@ class FeatureLayerTest(test.TestCase): with ops.Graph().as_default(): cols_dict = {} features = {'price1': [[1., 2.], [5., 6.]], 'price2': [[3.], [4.]]} - feature_layer = fc.FeatureLayer([price1, price2]) - net = feature_layer(features, cols_dict) - with _initialized_session(): - self.assertAllClose([[1., 2.], [5., 6.]], cols_dict[price1].eval()) - self.assertAllClose([[3.], [4.]], cols_dict[price2].eval()) - self.assertAllClose([[1., 2., 3.], [5., 6., 4.]], net.eval()) + dense_features = fc.DenseFeatures([price1, price2]) + net = dense_features(features, cols_dict) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose([[1., 2.], [5., 6.]], + self.evaluate(cols_dict[price1])) + self.assertAllClose([[3.], [4.]], self.evaluate(cols_dict[price2])) + self.assertAllClose([[1., 2., 3.], [5., 6., 4.]], self.evaluate(net)) def test_column_order(self): price_a = fc.numeric_column('price_a') @@ -3292,11 +3405,14 @@ class FeatureLayerTest(test.TestCase): 'price_a': [[1.]], 'price_b': [[3.]], } - net1 = fc.FeatureLayer([price_a, price_b])(features) - net2 = fc.FeatureLayer([price_b, price_a])(features) - with _initialized_session(): - self.assertAllClose([[1., 3.]], net1.eval()) - self.assertAllClose([[1., 3.]], net2.eval()) + net1 = fc.DenseFeatures([price_a, price_b])(features) + net2 = fc.DenseFeatures([price_b, price_a])(features) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose([[1., 3.]], self.evaluate(net1)) + self.assertAllClose([[1., 3.]], self.evaluate(net2)) def test_fails_for_categorical_column(self): animal = fc.categorical_column_with_identity('animal', num_buckets=4) @@ -3307,7 +3423,7 @@ class FeatureLayerTest(test.TestCase): indices=[[0, 0], [0, 1]], values=[1, 2], dense_shape=[1, 2]) } with self.assertRaisesRegexp(Exception, 'must be a DenseColumn'): - fc.FeatureLayer([animal])(features) + fc.DenseFeatures([animal])(features) def test_static_batch_size_mismatch(self): price1 = fc.numeric_column('price1') @@ -3320,7 +3436,7 @@ class FeatureLayerTest(test.TestCase): with self.assertRaisesRegexp( ValueError, 'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string - fc.FeatureLayer([price1, price2])(features) + fc.DenseFeatures([price1, price2])(features) def test_subset_of_static_batch_size_mismatch(self): price1 = fc.numeric_column('price1') @@ -3335,7 +3451,7 @@ class FeatureLayerTest(test.TestCase): with self.assertRaisesRegexp( ValueError, 'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string - fc.FeatureLayer([price1, price2, price3])(features) + fc.DenseFeatures([price1, price2, price3])(features) def test_runtime_batch_size_mismatch(self): price1 = fc.numeric_column('price1') @@ -3345,7 +3461,7 @@ class FeatureLayerTest(test.TestCase): 'price1': array_ops.placeholder(dtype=dtypes.int64), # batchsize = 3 'price2': [[3.], [4.]] # batchsize = 2 } - net = fc.FeatureLayer([price1, price2])(features) + net = fc.DenseFeatures([price1, price2])(features) with _initialized_session() as sess: with self.assertRaisesRegexp(errors.OpError, 'Dimensions of inputs should match'): @@ -3359,7 +3475,7 @@ class FeatureLayerTest(test.TestCase): 'price1': array_ops.placeholder(dtype=dtypes.int64), # batchsize = 2 'price2': array_ops.placeholder(dtype=dtypes.int64), # batchsize = 2 } - net = fc.FeatureLayer([price1, price2])(features) + net = fc.DenseFeatures([price1, price2])(features) with _initialized_session() as sess: sess.run( net, @@ -3379,19 +3495,20 @@ class FeatureLayerTest(test.TestCase): 'sparse_feature': [['a'], ['x']], } all_cols = [some_embedding_column] - fc.FeatureLayer(all_cols)(features) - fc.FeatureLayer(all_cols)(features) + fc.DenseFeatures(all_cols)(features) + fc.DenseFeatures(all_cols)(features) # Make sure that 2 variables get created in this case. self.assertEqual(2, len( ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES))) expected_var_names = [ - 'feature_layer/sparse_feature_embedding/embedding_weights:0', - 'feature_layer_1/sparse_feature_embedding/embedding_weights:0' + 'dense_features/sparse_feature_embedding/embedding_weights:0', + 'dense_features_1/sparse_feature_embedding/embedding_weights:0' ] self.assertItemsEqual( expected_var_names, [v.name for v in ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES)]) + @test_util.run_deprecated_v1 def test_multiple_layers_with_same_shared_embedding_column(self): categorical_column_a = fc.categorical_column_with_identity( key='aaa', num_buckets=3) @@ -3416,8 +3533,8 @@ class FeatureLayerTest(test.TestCase): dense_shape=(2, 2)), } all_cols = [embedding_column_a, embedding_column_b] - fc.FeatureLayer(all_cols)(features) - fc.FeatureLayer(all_cols)(features) + fc.DenseFeatures(all_cols)(features) + fc.DenseFeatures(all_cols)(features) # Make sure that only 1 variable gets created in this case. self.assertEqual(1, len( ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES))) @@ -3425,6 +3542,7 @@ class FeatureLayerTest(test.TestCase): ['aaa_bbb_shared_embedding:0'], [v.name for v in ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES)]) + @test_util.run_deprecated_v1 def test_multiple_layers_with_same_shared_embedding_column_diff_graphs(self): categorical_column_a = fc.categorical_column_with_identity( key='aaa', num_buckets=3) @@ -3449,7 +3567,7 @@ class FeatureLayerTest(test.TestCase): values=(1, 2, 1), dense_shape=(2, 2)), } - fc.FeatureLayer(all_cols)(features) + fc.DenseFeatures(all_cols)(features) # Make sure that only 1 variable gets created in this case. self.assertEqual(1, len( ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES))) @@ -3468,7 +3586,7 @@ class FeatureLayerTest(test.TestCase): dense_shape=(2, 2)), } - fc.FeatureLayer(all_cols)(features1) + fc.DenseFeatures(all_cols)(features1) # Make sure that only 1 variable gets created in this case. self.assertEqual(1, len( ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES))) @@ -3476,23 +3594,25 @@ class FeatureLayerTest(test.TestCase): ['aaa_bbb_shared_embedding:0'], [v.name for v in ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES)]) + @test_util.run_deprecated_v1 def test_with_numpy_input_fn(self): embedding_values = ( (1., 2., 3., 4., 5.), # id 0 (6., 7., 8., 9., 10.), # id 1 (11., 12., 13., 14., 15.) # id 2 ) + def _initializer(shape, dtype, partition_info): del shape, dtype, partition_info return embedding_values - # price has 1 dimension in feature_layer + # price has 1 dimension in dense_features price = fc.numeric_column('price') body_style = fc.categorical_column_with_vocabulary_list( 'body-style', vocabulary_list=['hardtop', 'wagon', 'sedan']) - # one_hot_body_style has 3 dims in feature_layer. + # one_hot_body_style has 3 dims in dense_features. one_hot_body_style = fc.indicator_column(body_style) - # embedded_body_style has 5 dims in feature_layer. + # embedded_body_style has 5 dims in dense_features. embedded_body_style = fc.embedding_column( body_style, dimension=5, initializer=_initializer) @@ -3504,7 +3624,7 @@ class FeatureLayerTest(test.TestCase): batch_size=2, shuffle=False) features = input_fn() - net = fc.FeatureLayer([price, one_hot_body_style, embedded_body_style])( + net = fc.DenseFeatures([price, one_hot_body_style, embedded_body_style])( features) self.assertEqual(1 + 3 + 5, net.shape[1]) with _initialized_session() as sess: @@ -3513,33 +3633,33 @@ class FeatureLayerTest(test.TestCase): # Each row is formed by concatenating `embedded_body_style`, # `one_hot_body_style`, and `price` in order. - self.assertAllEqual( - [[11., 12., 13., 14., 15., 0., 0., 1., 11.], - [1., 2., 3., 4., 5., 1., 0., 0., 12]], - sess.run(net)) + self.assertAllEqual([[11., 12., 13., 14., 15., 0., 0., 1., 11.], + [1., 2., 3., 4., 5., 1., 0., 0., 12]], sess.run(net)) coord.request_stop() coord.join(threads) + @test_util.run_deprecated_v1 def test_with_1d_sparse_tensor(self): embedding_values = ( (1., 2., 3., 4., 5.), # id 0 (6., 7., 8., 9., 10.), # id 1 (11., 12., 13., 14., 15.) # id 2 ) + def _initializer(shape, dtype, partition_info): del shape, dtype, partition_info return embedding_values - # price has 1 dimension in feature_layer + # price has 1 dimension in dense_features price = fc.numeric_column('price') - # one_hot_body_style has 3 dims in feature_layer. + # one_hot_body_style has 3 dims in dense_features. body_style = fc.categorical_column_with_vocabulary_list( 'body-style', vocabulary_list=['hardtop', 'wagon', 'sedan']) one_hot_body_style = fc.indicator_column(body_style) - # embedded_body_style has 5 dims in feature_layer. + # embedded_body_style has 5 dims in dense_features. country = fc.categorical_column_with_vocabulary_list( 'country', vocabulary_list=['US', 'JP', 'CA']) embedded_country = fc.embedding_column( @@ -3547,49 +3667,56 @@ class FeatureLayerTest(test.TestCase): # Provides 1-dim tensor and dense tensor. features = { - 'price': constant_op.constant([11., 12.,]), - 'body-style': sparse_tensor.SparseTensor( - indices=((0,), (1,)), - values=('sedan', 'hardtop'), - dense_shape=(2,)), + 'price': + constant_op.constant([ + 11., + 12., + ]), + 'body-style': + sparse_tensor.SparseTensor( + indices=((0,), (1,)), + values=('sedan', 'hardtop'), + dense_shape=(2,)), # This is dense tensor for the categorical_column. - 'country': constant_op.constant(['CA', 'US']), + 'country': + constant_op.constant(['CA', 'US']), } self.assertEqual(1, features['price'].shape.ndims) self.assertEqual(1, features['body-style'].dense_shape.get_shape()[0]) self.assertEqual(1, features['country'].shape.ndims) - net = fc.FeatureLayer([price, one_hot_body_style, embedded_country])( + net = fc.DenseFeatures([price, one_hot_body_style, embedded_country])( features) self.assertEqual(1 + 3 + 5, net.shape[1]) with _initialized_session() as sess: # Each row is formed by concatenating `embedded_body_style`, # `one_hot_body_style`, and `price` in order. - self.assertAllEqual( - [[0., 0., 1., 11., 12., 13., 14., 15., 11.], - [1., 0., 0., 1., 2., 3., 4., 5., 12.]], - sess.run(net)) + self.assertAllEqual([[0., 0., 1., 11., 12., 13., 14., 15., 11.], + [1., 0., 0., 1., 2., 3., 4., 5., 12.]], + sess.run(net)) + @test_util.run_deprecated_v1 def test_with_1d_unknown_shape_sparse_tensor(self): embedding_values = ( (1., 2.), # id 0 (6., 7.), # id 1 (11., 12.) # id 2 ) + def _initializer(shape, dtype, partition_info): del shape, dtype, partition_info return embedding_values - # price has 1 dimension in feature_layer + # price has 1 dimension in dense_features price = fc.numeric_column('price') - # one_hot_body_style has 3 dims in feature_layer. + # one_hot_body_style has 3 dims in dense_features. body_style = fc.categorical_column_with_vocabulary_list( 'body-style', vocabulary_list=['hardtop', 'wagon', 'sedan']) one_hot_body_style = fc.indicator_column(body_style) - # embedded_body_style has 5 dims in feature_layer. + # embedded_body_style has 5 dims in dense_features. country = fc.categorical_column_with_vocabulary_list( 'country', vocabulary_list=['US', 'JP', 'CA']) embedded_country = fc.embedding_column( @@ -3608,12 +3735,10 @@ class FeatureLayerTest(test.TestCase): price_data = np.array([11., 12.]) body_style_data = sparse_tensor.SparseTensorValue( - indices=((0,), (1,)), - values=('sedan', 'hardtop'), - dense_shape=(2,)) + indices=((0,), (1,)), values=('sedan', 'hardtop'), dense_shape=(2,)) country_data = np.array([['US'], ['CA']]) - net = fc.FeatureLayer([price, one_hot_body_style, embedded_country])( + net = fc.DenseFeatures([price, one_hot_body_style, embedded_country])( features) self.assertEqual(1 + 3 + 2, net.shape[1]) with _initialized_session() as sess: @@ -3630,8 +3755,9 @@ class FeatureLayerTest(test.TestCase): features['country']: country_data })) + @test_util.run_deprecated_v1 def test_with_rank_0_feature(self): - # price has 1 dimension in feature_layer + # price has 1 dimension in dense_features price = fc.numeric_column('price') features = { 'price': constant_op.constant(0), @@ -3640,13 +3766,13 @@ class FeatureLayerTest(test.TestCase): # Static rank 0 should fail with self.assertRaisesRegexp(ValueError, 'Feature .* cannot have rank 0'): - fc.FeatureLayer([price])(features) + fc.DenseFeatures([price])(features) # Dynamic rank 0 should fail features = { 'price': array_ops.placeholder(dtypes.float32), } - net = fc.FeatureLayer([price])(features) + net = fc.DenseFeatures([price])(features) self.assertEqual(1, net.shape[1]) with _initialized_session() as sess: with self.assertRaisesOpError('Feature .* cannot have rank 0'): @@ -3779,16 +3905,22 @@ class FunctionalInputLayerTest(test.TestCase): with ops.Graph().as_default(): features = features = {'a': [0.]} net = fc_old.input_layer(features, fc.numeric_column('a')) - with _initialized_session(): - self.assertAllClose([[0.]], net.eval()) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose([[0.]], self.evaluate(net)) def test_column_generator(self): with ops.Graph().as_default(): features = features = {'a': [0.], 'b': [1.]} columns = (fc.numeric_column(key) for key in features) net = fc_old.input_layer(features, columns) - with _initialized_session(): - self.assertAllClose([[0., 1.]], net.eval()) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose([[0., 1.]], self.evaluate(net)) def test_raises_if_duplicate_name(self): with self.assertRaisesRegexp( @@ -3803,16 +3935,22 @@ class FunctionalInputLayerTest(test.TestCase): with ops.Graph().as_default(): features = {'price': [[1.], [5.]]} net = fc_old.input_layer(features, [price]) - with _initialized_session(): - self.assertAllClose([[1.], [5.]], net.eval()) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose([[1.], [5.]], self.evaluate(net)) def test_multi_dimension(self): price = fc.numeric_column('price', shape=2) with ops.Graph().as_default(): features = {'price': [[1., 2.], [5., 6.]]} net = fc_old.input_layer(features, [price]) - with _initialized_session(): - self.assertAllClose([[1., 2.], [5., 6.]], net.eval()) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose([[1., 2.], [5., 6.]], self.evaluate(net)) def test_raises_if_shape_mismatch(self): price = fc.numeric_column('price', shape=2) @@ -3828,8 +3966,11 @@ class FunctionalInputLayerTest(test.TestCase): with ops.Graph().as_default(): features = {'price': [[[1., 2.]], [[5., 6.]]]} net = fc_old.input_layer(features, [price]) - with _initialized_session(): - self.assertAllClose([[1., 2.], [5., 6.]], net.eval()) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose([[1., 2.], [5., 6.]], self.evaluate(net)) def test_multi_column(self): price1 = fc.numeric_column('price1', shape=2) @@ -3837,8 +3978,11 @@ class FunctionalInputLayerTest(test.TestCase): with ops.Graph().as_default(): features = {'price1': [[1., 2.], [5., 6.]], 'price2': [[3.], [4.]]} net = fc_old.input_layer(features, [price1, price2]) - with _initialized_session(): - self.assertAllClose([[1., 2., 3.], [5., 6., 4.]], net.eval()) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose([[1., 2., 3.], [5., 6., 4.]], self.evaluate(net)) def test_fills_cols_to_vars(self): # Provide three _DenseColumn's to input_layer: a _NumericColumn, a @@ -3869,6 +4013,7 @@ class FunctionalInputLayerTest(test.TestCase): variables_lib.Variable) self.assertAllEqual(cols_to_vars[some_embedding_column][0].shape, [5, 10]) + @test_util.run_deprecated_v1 def test_fills_cols_to_vars_shared_embedding(self): # Provide 5 DenseColumn's to input_layer: a NumericColumn, a # BucketizedColumn, an EmbeddingColumn, two SharedEmbeddingColumns. The @@ -3882,11 +4027,11 @@ class FunctionalInputLayerTest(test.TestCase): 'sparse_feature', hash_bucket_size=5) some_embedding_column = fc.embedding_column( some_sparse_column, dimension=10) - categorical_column_a = fc_old.categorical_column_with_identity( + categorical_column_a = fc.categorical_column_with_identity( key='aaa', num_buckets=3) - categorical_column_b = fc_old.categorical_column_with_identity( + categorical_column_b = fc.categorical_column_with_identity( key='bbb', num_buckets=3) - shared_embedding_a, shared_embedding_b = fc_old.shared_embedding_columns( + shared_embedding_a, shared_embedding_b = fc.shared_embedding_columns( [categorical_column_a, categorical_column_b], dimension=2) with ops.Graph().as_default(): features = { @@ -3968,9 +4113,12 @@ class FunctionalInputLayerTest(test.TestCase): } net1 = fc_old.input_layer(features, [price_a, price_b]) net2 = fc_old.input_layer(features, [price_b, price_a]) - with _initialized_session(): - self.assertAllClose([[1., 3.]], net1.eval()) - self.assertAllClose([[1., 3.]], net2.eval()) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose([[1., 3.]], self.evaluate(net1)) + self.assertAllClose([[1., 3.]], self.evaluate(net2)) def test_fails_for_categorical_column(self): animal = fc.categorical_column_with_identity('animal', num_buckets=4) @@ -4066,6 +4214,7 @@ class FunctionalInputLayerTest(test.TestCase): expected_var_names, [v.name for v in ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES)]) + @test_util.run_deprecated_v1 def test_with_1d_sparse_tensor(self): embedding_values = ( (1., 2., 3., 4., 5.), # id 0 @@ -4122,6 +4271,7 @@ class FunctionalInputLayerTest(test.TestCase): [1., 0., 0., 1., 2., 3., 4., 5., 12.]], sess.run(net)) + @test_util.run_deprecated_v1 def test_with_1d_unknown_shape_sparse_tensor(self): embedding_values = ( (1., 2.), # id 0 @@ -4180,6 +4330,7 @@ class FunctionalInputLayerTest(test.TestCase): features['country']: country_data })) + @test_util.run_deprecated_v1 def test_with_rank_0_feature(self): # price has 1 dimension in input_layer price = fc.numeric_column('price') @@ -4220,12 +4371,19 @@ class MakeParseExampleSpecTest(test.TestCase): def transform_feature(self, transformation_cache, state_manager): pass + def _transform_feature(self, inputs): + pass + @property def parse_example_spec(self): return self.parse_spec + @property + def _parse_example_spec(self): + return self.parse_spec + def test_no_feature_columns(self): - actual = fc.make_parse_example_spec([]) + actual = fc.make_parse_example_spec_v2([]) self.assertDictEqual({}, actual) def test_invalid_type(self): @@ -4235,15 +4393,17 @@ class MakeParseExampleSpecTest(test.TestCase): with self.assertRaisesRegexp( ValueError, 'All feature_columns must be FeatureColumn instances.*invalid_column'): - fc.make_parse_example_spec( - (self._TestFeatureColumn({key1: parse_spec1}), 'invalid_column')) + fc.make_parse_example_spec_v2((self._TestFeatureColumn({ + key1: parse_spec1 + }), 'invalid_column')) def test_one_feature_column(self): key1 = 'key1' parse_spec1 = parsing_ops.FixedLenFeature( shape=(2,), dtype=dtypes.float32, default_value=0.) - actual = fc.make_parse_example_spec( - (self._TestFeatureColumn({key1: parse_spec1}),)) + actual = fc.make_parse_example_spec_v2((self._TestFeatureColumn({ + key1: parse_spec1 + }),)) self.assertDictEqual({key1: parse_spec1}, actual) def test_two_feature_columns(self): @@ -4252,9 +4412,11 @@ class MakeParseExampleSpecTest(test.TestCase): shape=(2,), dtype=dtypes.float32, default_value=0.) key2 = 'key2' parse_spec2 = parsing_ops.VarLenFeature(dtype=dtypes.string) - actual = fc.make_parse_example_spec( - (self._TestFeatureColumn({key1: parse_spec1}), - self._TestFeatureColumn({key2: parse_spec2}))) + actual = fc.make_parse_example_spec_v2((self._TestFeatureColumn({ + key1: parse_spec1 + }), self._TestFeatureColumn({ + key2: parse_spec2 + }))) self.assertDictEqual({key1: parse_spec1, key2: parse_spec2}, actual) def test_equal_keys_different_parse_spec(self): @@ -4265,17 +4427,21 @@ class MakeParseExampleSpecTest(test.TestCase): with self.assertRaisesRegexp( ValueError, 'feature_columns contain different parse_spec for key key1'): - fc.make_parse_example_spec( - (self._TestFeatureColumn({key1: parse_spec1}), - self._TestFeatureColumn({key1: parse_spec2}))) + fc.make_parse_example_spec_v2((self._TestFeatureColumn({ + key1: parse_spec1 + }), self._TestFeatureColumn({ + key1: parse_spec2 + }))) def test_equal_keys_equal_parse_spec(self): key1 = 'key1' parse_spec1 = parsing_ops.FixedLenFeature( shape=(2,), dtype=dtypes.float32, default_value=0.) - actual = fc.make_parse_example_spec( - (self._TestFeatureColumn({key1: parse_spec1}), - self._TestFeatureColumn({key1: parse_spec1}))) + actual = fc.make_parse_example_spec_v2((self._TestFeatureColumn({ + key1: parse_spec1 + }), self._TestFeatureColumn({ + key1: parse_spec1 + }))) self.assertDictEqual({key1: parse_spec1}, actual) def test_multiple_features_dict(self): @@ -4287,11 +4453,17 @@ class MakeParseExampleSpecTest(test.TestCase): parse_spec2 = parsing_ops.VarLenFeature(dtype=dtypes.string) key3 = 'key3' parse_spec3 = parsing_ops.VarLenFeature(dtype=dtypes.int32) - actual = fc.make_parse_example_spec( - (self._TestFeatureColumn({key1: parse_spec1}), - self._TestFeatureColumn({key2: parse_spec2, key3: parse_spec3}))) - self.assertDictEqual( - {key1: parse_spec1, key2: parse_spec2, key3: parse_spec3}, actual) + actual = fc.make_parse_example_spec_v2((self._TestFeatureColumn({ + key1: parse_spec1 + }), self._TestFeatureColumn({ + key2: parse_spec2, + key3: parse_spec3 + }))) + self.assertDictEqual({ + key1: parse_spec1, + key2: parse_spec2, + key3: parse_spec3 + }, actual) def _assert_sparse_tensor_value(test_case, expected, actual): @@ -4299,7 +4471,8 @@ def _assert_sparse_tensor_value(test_case, expected, actual): test_case.assertAllEqual(expected.indices, actual.indices) test_case.assertEqual( - np.array(expected.values).dtype, np.array(actual.values).dtype) + np.array(expected.values).dtype, + np.array(actual.values).dtype) test_case.assertAllEqual(expected.values, actual.values) test_case.assertEqual(np.int64, np.array(actual.dense_shape).dtype) @@ -4321,6 +4494,7 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): 'python/feature_column/testdata/wire_vocabulary.txt') self._wire_vocabulary_size = 3 + @test_util.run_deprecated_v1 def test_defaults(self): column = fc.categorical_column_with_vocabulary_file( key='aaa', vocabulary_file='path_to_file', vocabulary_size=3) @@ -4337,19 +4511,27 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): fc.categorical_column_with_vocabulary_file( key=('aaa',), vocabulary_file='path_to_file', vocabulary_size=3) + @test_util.run_deprecated_v1 def test_all_constructor_args(self): column = fc.categorical_column_with_vocabulary_file( - key='aaa', vocabulary_file='path_to_file', vocabulary_size=3, - num_oov_buckets=4, dtype=dtypes.int32) + key='aaa', + vocabulary_file='path_to_file', + vocabulary_size=3, + num_oov_buckets=4, + dtype=dtypes.int32) self.assertEqual(7, column.num_buckets) self.assertEqual({ 'aaa': parsing_ops.VarLenFeature(dtypes.int32) }, column.parse_example_spec) + @test_util.run_deprecated_v1 def test_deep_copy(self): original = fc.categorical_column_with_vocabulary_file( - key='aaa', vocabulary_file='path_to_file', vocabulary_size=3, - num_oov_buckets=4, dtype=dtypes.int32) + key='aaa', + vocabulary_file='path_to_file', + vocabulary_size=3, + num_oov_buckets=4, + dtype=dtypes.int32) for column in (original, copy.deepcopy(original)): self.assertEqual('aaa', column.name) self.assertEqual(7, column.num_buckets) @@ -4367,6 +4549,7 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): fc.categorical_column_with_vocabulary_file( key='aaa', vocabulary_file='', vocabulary_size=3) + @test_util.run_deprecated_v1 def test_invalid_vocabulary_file(self): column = fc.categorical_column_with_vocabulary_file( key='aaa', vocabulary_file='file_does_not_exist', vocabulary_size=10) @@ -4379,19 +4562,21 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): 'aaa': inputs }), None) with self.assertRaisesRegexp(errors.OpError, 'file_does_not_exist'): - with self.cached_session(): - lookup_ops.tables_initializer().run() + self.evaluate(lookup_ops.tables_initializer()) def test_invalid_vocabulary_size(self): with self.assertRaisesRegexp(ValueError, 'Invalid vocabulary_size'): fc.categorical_column_with_vocabulary_file( - key='aaa', vocabulary_file=self._wire_vocabulary_file_name, + key='aaa', + vocabulary_file=self._wire_vocabulary_file_name, vocabulary_size=-1) with self.assertRaisesRegexp(ValueError, 'Invalid vocabulary_size'): fc.categorical_column_with_vocabulary_file( - key='aaa', vocabulary_file=self._wire_vocabulary_file_name, + key='aaa', + vocabulary_file=self._wire_vocabulary_file_name, vocabulary_size=0) + @test_util.run_deprecated_v1 def test_too_large_vocabulary_size(self): column = fc.categorical_column_with_vocabulary_file( key='aaa', @@ -4406,24 +4591,27 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): 'aaa': inputs }), None) with self.assertRaisesRegexp(errors.OpError, 'Invalid vocab_size'): - with self.cached_session(): - lookup_ops.tables_initializer().run() + self.evaluate(lookup_ops.tables_initializer()) def test_invalid_num_oov_buckets(self): with self.assertRaisesRegexp(ValueError, 'Invalid num_oov_buckets'): fc.categorical_column_with_vocabulary_file( - key='aaa', vocabulary_file='path', vocabulary_size=3, + key='aaa', + vocabulary_file='path', + vocabulary_size=3, num_oov_buckets=-1) def test_invalid_dtype(self): with self.assertRaisesRegexp(ValueError, 'dtype must be string or integer'): fc.categorical_column_with_vocabulary_file( - key='aaa', vocabulary_file='path', vocabulary_size=3, + key='aaa', + vocabulary_file='path', + vocabulary_size=3, dtype=dtypes.float64) def test_invalid_buckets_and_default_value(self): - with self.assertRaisesRegexp( - ValueError, 'both num_oov_buckets and default_value'): + with self.assertRaisesRegexp(ValueError, + 'both num_oov_buckets and default_value'): fc.categorical_column_with_vocabulary_file( key='aaa', vocabulary_file=self._wire_vocabulary_file_name, @@ -4463,28 +4651,31 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): 'aaa': inputs }), None) + @test_util.run_deprecated_v1 def test_parse_example(self): a = fc.categorical_column_with_vocabulary_file( key='aaa', vocabulary_file='path_to_file', vocabulary_size=3) - data = example_pb2.Example(features=feature_pb2.Features( - feature={ - 'aaa': - feature_pb2.Feature(bytes_list=feature_pb2.BytesList( - value=[b'omar', b'stringer'])) - })) + data = example_pb2.Example( + features=feature_pb2.Features( + feature={ + 'aaa': + feature_pb2.Feature( + bytes_list=feature_pb2.BytesList( + value=[b'omar', b'stringer'])) + })) features = parsing_ops.parse_example( serialized=[data.SerializeToString()], - features=fc.make_parse_example_spec([a])) + features=fc.make_parse_example_spec_v2([a])) self.assertIn('aaa', features) - with self.cached_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=[[0, 0], [0, 1]], - values=np.array([b'omar', b'stringer'], dtype=np.object_), - dense_shape=[1, 2]), - features['aaa'].eval()) + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=[[0, 0], [0, 1]], + values=np.array([b'omar', b'stringer'], dtype=np.object_), + dense_shape=[1, 2]), self.evaluate(features['aaa'])) + + @test_util.run_deprecated_v1 def test_get_sparse_tensors(self): column = fc.categorical_column_with_vocabulary_file( key='aaa', @@ -4499,15 +4690,19 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): 'aaa': inputs }), None) self.assertIsNone(id_weight_pair.weight_tensor) - with _initialized_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=inputs.indices, - values=np.array((2, -1, 0), dtype=np.int64), - dense_shape=inputs.dense_shape), - id_weight_pair.id_tensor.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=inputs.indices, + values=np.array((2, -1, 0), dtype=np.int64), + dense_shape=inputs.dense_shape), + self.evaluate(id_weight_pair.id_tensor)) + + @test_util.run_deprecated_v1 def test_get_sparse_tensors_none_vocabulary_size(self): column = fc.categorical_column_with_vocabulary_file( key='aaa', vocabulary_file=self._wire_vocabulary_file_name) @@ -4520,15 +4715,19 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): 'aaa': inputs }), None) self.assertIsNone(id_weight_pair.weight_tensor) - with _initialized_session(): - _assert_sparse_tensor_value(self, - sparse_tensor.SparseTensorValue( - indices=inputs.indices, - values=np.array( - (2, -1, 0), dtype=np.int64), - dense_shape=inputs.dense_shape), - id_weight_pair.id_tensor.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=inputs.indices, + values=np.array((2, -1, 0), dtype=np.int64), + dense_shape=inputs.dense_shape), + self.evaluate(id_weight_pair.id_tensor)) + + @test_util.run_deprecated_v1 def test_transform_feature(self): column = fc.categorical_column_with_vocabulary_file( key='aaa', @@ -4538,16 +4737,21 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): indices=((0, 0), (1, 0), (1, 1)), values=('marlo', 'skywalker', 'omar'), dense_shape=(2, 2)) - id_tensor = fc._transform_features({'aaa': inputs}, [column], None)[column] - with _initialized_session(): - _assert_sparse_tensor_value(self, - sparse_tensor.SparseTensorValue( - indices=inputs.indices, - values=np.array( - (2, -1, 0), dtype=np.int64), - dense_shape=inputs.dense_shape), - id_tensor.eval()) + id_tensor = fc._transform_features_v2({ + 'aaa': inputs + }, [column], None)[column] + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=inputs.indices, + values=np.array((2, -1, 0), dtype=np.int64), + dense_shape=inputs.dense_shape), self.evaluate(id_tensor)) + + @test_util.run_deprecated_v1 def test_get_sparse_tensors_dense_input(self): column = fc.categorical_column_with_vocabulary_file( key='aaa', @@ -4558,15 +4762,18 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): 'aaa': (('marlo', ''), ('skywalker', 'omar')) }), None) self.assertIsNone(id_weight_pair.weight_tensor) - with _initialized_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=((0, 0), (1, 0), (1, 1)), - values=np.array((2, -1, 0), dtype=np.int64), - dense_shape=(2, 2)), - id_weight_pair.id_tensor.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=((0, 0), (1, 0), (1, 1)), + values=np.array((2, -1, 0), dtype=np.int64), + dense_shape=(2, 2)), self.evaluate(id_weight_pair.id_tensor)) + + @test_util.run_deprecated_v1 def test_get_sparse_tensors_default_value_in_vocabulary(self): column = fc.categorical_column_with_vocabulary_file( key='aaa', @@ -4582,15 +4789,19 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): 'aaa': inputs }), None) self.assertIsNone(id_weight_pair.weight_tensor) - with _initialized_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=inputs.indices, - values=np.array((2, 2, 0), dtype=np.int64), - dense_shape=inputs.dense_shape), - id_weight_pair.id_tensor.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=inputs.indices, + values=np.array((2, 2, 0), dtype=np.int64), + dense_shape=inputs.dense_shape), + self.evaluate(id_weight_pair.id_tensor)) + + @test_util.run_deprecated_v1 def test_get_sparse_tensors_with_oov_buckets(self): column = fc.categorical_column_with_vocabulary_file( key='aaa', @@ -4606,15 +4817,19 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): 'aaa': inputs }), None) self.assertIsNone(id_weight_pair.weight_tensor) - with _initialized_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=inputs.indices, - values=np.array((2, 33, 0, 62), dtype=np.int64), - dense_shape=inputs.dense_shape), - id_weight_pair.id_tensor.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=inputs.indices, + values=np.array((2, 33, 0, 62), dtype=np.int64), + dense_shape=inputs.dense_shape), + self.evaluate(id_weight_pair.id_tensor)) + + @test_util.run_deprecated_v1 def test_get_sparse_tensors_small_vocabulary_size(self): # 'marlo' is the last entry in our vocabulary file, so be setting # `vocabulary_size` to 1 less than number of entries in file, we take @@ -4632,15 +4847,19 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): 'aaa': inputs }), None) self.assertIsNone(id_weight_pair.weight_tensor) - with _initialized_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=inputs.indices, - values=np.array((-1, -1, 0), dtype=np.int64), - dense_shape=inputs.dense_shape), - id_weight_pair.id_tensor.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=inputs.indices, + values=np.array((-1, -1, 0), dtype=np.int64), + dense_shape=inputs.dense_shape), + self.evaluate(id_weight_pair.id_tensor)) + + @test_util.run_deprecated_v1 def test_get_sparse_tensors_int32(self): column = fc.categorical_column_with_vocabulary_file( key='aaa', @@ -4656,15 +4875,19 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): 'aaa': inputs }), None) self.assertIsNone(id_weight_pair.weight_tensor) - with _initialized_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=inputs.indices, - values=np.array((2, -1, 0, 4), dtype=np.int64), - dense_shape=inputs.dense_shape), - id_weight_pair.id_tensor.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=inputs.indices, + values=np.array((2, -1, 0, 4), dtype=np.int64), + dense_shape=inputs.dense_shape), + self.evaluate(id_weight_pair.id_tensor)) + + @test_util.run_deprecated_v1 def test_get_sparse_tensors_int32_dense_input(self): default_value = -100 column = fc.categorical_column_with_vocabulary_file( @@ -4678,15 +4901,18 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): 'aaa': ((11, -1, -1), (100, 30, -1), (-1, -1, 22)) }), None) self.assertIsNone(id_weight_pair.weight_tensor) - with _initialized_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=((0, 0), (1, 0), (1, 1), (2, 2)), - values=np.array((2, default_value, 0, 4), dtype=np.int64), - dense_shape=(3, 3)), - id_weight_pair.id_tensor.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=((0, 0), (1, 0), (1, 1), (2, 2)), + values=np.array((2, default_value, 0, 4), dtype=np.int64), + dense_shape=(3, 3)), self.evaluate(id_weight_pair.id_tensor)) + + @test_util.run_deprecated_v1 def test_get_sparse_tensors_int32_with_oov_buckets(self): column = fc.categorical_column_with_vocabulary_file( key='aaa', @@ -4703,15 +4929,19 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): 'aaa': inputs }), None) self.assertIsNone(id_weight_pair.weight_tensor) - with _initialized_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=inputs.indices, - values=np.array((2, 60, 0, 4), dtype=np.int64), - dense_shape=inputs.dense_shape), - id_weight_pair.id_tensor.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=inputs.indices, + values=np.array((2, 60, 0, 4), dtype=np.int64), + dense_shape=inputs.dense_shape), + self.evaluate(id_weight_pair.id_tensor)) + + @test_util.run_deprecated_v1 def test_linear_model(self): wire_column = fc.categorical_column_with_vocabulary_file( key='wire', @@ -4729,14 +4959,17 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): dense_shape=(2, 2)) }) wire_var, bias = model.variables - with _initialized_session(): - self.assertAllClose((0.,), bias.eval()) - self.assertAllClose(((0.,), (0.,), (0.,), (0.,)), wire_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) - wire_var.assign(((1.,), (2.,), (3.,), (4.,))).eval() - # 'marlo' -> 2: wire_var[2] = 3 - # 'skywalker' -> 3, 'omar' -> 0: wire_var[3] + wire_var[0] = 4+1 = 5 - self.assertAllClose(((3.,), (5.,)), predictions.eval()) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose((0.,), self.evaluate(bias)) + self.assertAllClose(((0.,), (0.,), (0.,), (0.,)), self.evaluate(wire_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) + self.evaluate(wire_var.assign(((1.,), (2.,), (3.,), (4.,)))) + # 'marlo' -> 2: wire_var[2] = 3 + # 'skywalker' -> 3, 'omar' -> 0: wire_var[3] + wire_var[0] = 4+1 = 5 + self.assertAllClose(((3.,), (5.,)), self.evaluate(predictions)) def test_old_linear_model(self): wire_column = fc.categorical_column_with_vocabulary_file( @@ -4755,15 +4988,19 @@ class VocabularyFileCategoricalColumnTest(test.TestCase): }, (wire_column,)) bias = get_linear_model_bias() wire_var = get_linear_model_column_var(wire_column) - with _initialized_session(): - self.assertAllClose((0.,), bias.eval()) - self.assertAllClose(((0.,), (0.,), (0.,), (0.,)), wire_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) - wire_var.assign(((1.,), (2.,), (3.,), (4.,))).eval() - # 'marlo' -> 2: wire_var[2] = 3 - # 'skywalker' -> 3, 'omar' -> 0: wire_var[3] + wire_var[0] = 4+1 = 5 - self.assertAllClose(((3.,), (5.,)), predictions.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose((0.,), self.evaluate(bias)) + self.assertAllClose(((0.,), (0.,), (0.,), (0.,)), self.evaluate(wire_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) + self.evaluate(wire_var.assign(((1.,), (2.,), (3.,), (4.,)))) + # 'marlo' -> 2: wire_var[2] = 3 + # 'skywalker' -> 3, 'omar' -> 0: wire_var[3] + wire_var[0] = 4+1 = 5 + self.assertAllClose(((3.,), (5.,)), self.evaluate(predictions)) + + @test_util.run_deprecated_v1 def test_serialization(self): wire_column = fc.categorical_column_with_vocabulary_file( key='wire', @@ -4815,15 +5052,19 @@ class VocabularyListCategoricalColumnTest(test.TestCase): 'aaa': parsing_ops.VarLenFeature(dtypes.int64) }, column.parse_example_spec) + @test_util.run_deprecated_v1 def test_all_constructor_args(self): column = fc.categorical_column_with_vocabulary_list( - key='aaa', vocabulary_list=(12, 24, 36), dtype=dtypes.int32, + key='aaa', + vocabulary_list=(12, 24, 36), + dtype=dtypes.int32, default_value=-99) self.assertEqual(3, column.num_buckets) self.assertEqual({ 'aaa': parsing_ops.VarLenFeature(dtypes.int32) }, column.parse_example_spec) + @test_util.run_deprecated_v1 def test_deep_copy(self): original = fc.categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=(12, 24, 36), dtype=dtypes.int32) @@ -4837,37 +5078,39 @@ class VocabularyListCategoricalColumnTest(test.TestCase): def test_invalid_dtype(self): with self.assertRaisesRegexp(ValueError, 'dtype must be string or integer'): fc.categorical_column_with_vocabulary_list( - key='aaa', vocabulary_list=('omar', 'stringer', 'marlo'), + key='aaa', + vocabulary_list=('omar', 'stringer', 'marlo'), dtype=dtypes.float32) def test_invalid_mapping_dtype(self): - with self.assertRaisesRegexp( - ValueError, r'vocabulary dtype must be string or integer'): + with self.assertRaisesRegexp(ValueError, + r'vocabulary dtype must be string or integer'): fc.categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=(12., 24., 36.)) def test_mismatched_int_dtype(self): - with self.assertRaisesRegexp( - ValueError, r'dtype.*and vocabulary dtype.*do not match'): + with self.assertRaisesRegexp(ValueError, + r'dtype.*and vocabulary dtype.*do not match'): fc.categorical_column_with_vocabulary_list( - key='aaa', vocabulary_list=('omar', 'stringer', 'marlo'), + key='aaa', + vocabulary_list=('omar', 'stringer', 'marlo'), dtype=dtypes.int32) def test_mismatched_string_dtype(self): - with self.assertRaisesRegexp( - ValueError, r'dtype.*and vocabulary dtype.*do not match'): + with self.assertRaisesRegexp(ValueError, + r'dtype.*and vocabulary dtype.*do not match'): fc.categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=(12, 24, 36), dtype=dtypes.string) def test_none_mapping(self): - with self.assertRaisesRegexp( - ValueError, r'vocabulary_list.*must be non-empty'): + with self.assertRaisesRegexp(ValueError, + r'vocabulary_list.*must be non-empty'): fc.categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=None) def test_empty_mapping(self): - with self.assertRaisesRegexp( - ValueError, r'vocabulary_list.*must be non-empty'): + with self.assertRaisesRegexp(ValueError, + r'vocabulary_list.*must be non-empty'): fc.categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=tuple([])) @@ -4879,12 +5122,11 @@ class VocabularyListCategoricalColumnTest(test.TestCase): def test_invalid_num_oov_buckets(self): with self.assertRaisesRegexp(ValueError, 'Invalid num_oov_buckets'): fc.categorical_column_with_vocabulary_list( - key='aaa', vocabulary_list=(12, 24, 36), - num_oov_buckets=-1) + key='aaa', vocabulary_list=(12, 24, 36), num_oov_buckets=-1) def test_invalid_buckets_and_default_value(self): - with self.assertRaisesRegexp( - ValueError, 'both num_oov_buckets and default_value'): + with self.assertRaisesRegexp(ValueError, + 'both num_oov_buckets and default_value'): fc.categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=(12, 24, 36), @@ -4893,8 +5135,7 @@ class VocabularyListCategoricalColumnTest(test.TestCase): def test_invalid_input_dtype_int32(self): column = fc.categorical_column_with_vocabulary_list( - key='aaa', - vocabulary_list=('omar', 'stringer', 'marlo')) + key='aaa', vocabulary_list=('omar', 'stringer', 'marlo')) inputs = sparse_tensor.SparseTensorValue( indices=((0, 0), (1, 0), (1, 1)), values=(12, 24, 36), @@ -4907,8 +5148,7 @@ class VocabularyListCategoricalColumnTest(test.TestCase): def test_invalid_input_dtype_string(self): column = fc.categorical_column_with_vocabulary_list( - key='aaa', - vocabulary_list=(12, 24, 36)) + key='aaa', vocabulary_list=(12, 24, 36)) inputs = sparse_tensor.SparseTensorValue( indices=((0, 0), (1, 0), (1, 1)), values=('omar', 'stringer', 'marlo'), @@ -4919,54 +5159,56 @@ class VocabularyListCategoricalColumnTest(test.TestCase): 'aaa': inputs }), None) + @test_util.run_deprecated_v1 def test_parse_example_string(self): a = fc.categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=('omar', 'stringer', 'marlo')) - data = example_pb2.Example(features=feature_pb2.Features( - feature={ - 'aaa': - feature_pb2.Feature(bytes_list=feature_pb2.BytesList( - value=[b'omar', b'stringer'])) - })) + data = example_pb2.Example( + features=feature_pb2.Features( + feature={ + 'aaa': + feature_pb2.Feature( + bytes_list=feature_pb2.BytesList( + value=[b'omar', b'stringer'])) + })) features = parsing_ops.parse_example( serialized=[data.SerializeToString()], - features=fc.make_parse_example_spec([a])) + features=fc.make_parse_example_spec_v2([a])) self.assertIn('aaa', features) - with self.cached_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=[[0, 0], [0, 1]], - values=np.array([b'omar', b'stringer'], dtype=np.object_), - dense_shape=[1, 2]), - features['aaa'].eval()) + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=[[0, 0], [0, 1]], + values=np.array([b'omar', b'stringer'], dtype=np.object_), + dense_shape=[1, 2]), self.evaluate(features['aaa'])) + + @test_util.run_deprecated_v1 def test_parse_example_int(self): a = fc.categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=(11, 21, 31)) - data = example_pb2.Example(features=feature_pb2.Features( - feature={ - 'aaa': - feature_pb2.Feature(int64_list=feature_pb2.Int64List( - value=[11, 21])) - })) + data = example_pb2.Example( + features=feature_pb2.Features( + feature={ + 'aaa': + feature_pb2.Feature( + int64_list=feature_pb2.Int64List(value=[11, 21])) + })) features = parsing_ops.parse_example( serialized=[data.SerializeToString()], - features=fc.make_parse_example_spec([a])) + features=fc.make_parse_example_spec_v2([a])) self.assertIn('aaa', features) - with self.cached_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=[[0, 0], [0, 1]], - values=[11, 21], - dense_shape=[1, 2]), - features['aaa'].eval()) + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=[[0, 0], [0, 1]], values=[11, 21], dense_shape=[1, 2]), + self.evaluate(features['aaa'])) + + @test_util.run_deprecated_v1 def test_get_sparse_tensors(self): column = fc.categorical_column_with_vocabulary_list( - key='aaa', - vocabulary_list=('omar', 'stringer', 'marlo')) + key='aaa', vocabulary_list=('omar', 'stringer', 'marlo')) inputs = sparse_tensor.SparseTensorValue( indices=((0, 0), (1, 0), (1, 1)), values=('marlo', 'skywalker', 'omar'), @@ -4976,51 +5218,61 @@ class VocabularyListCategoricalColumnTest(test.TestCase): 'aaa': inputs }), None) self.assertIsNone(id_weight_pair.weight_tensor) - with _initialized_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=inputs.indices, - values=np.array((2, -1, 0), dtype=np.int64), - dense_shape=inputs.dense_shape), - id_weight_pair.id_tensor.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=inputs.indices, + values=np.array((2, -1, 0), dtype=np.int64), + dense_shape=inputs.dense_shape), + self.evaluate(id_weight_pair.id_tensor)) + + @test_util.run_deprecated_v1 def test_transform_feature(self): column = fc.categorical_column_with_vocabulary_list( - key='aaa', - vocabulary_list=('omar', 'stringer', 'marlo')) + key='aaa', vocabulary_list=('omar', 'stringer', 'marlo')) inputs = sparse_tensor.SparseTensorValue( indices=((0, 0), (1, 0), (1, 1)), values=('marlo', 'skywalker', 'omar'), dense_shape=(2, 2)) - id_tensor = fc._transform_features({'aaa': inputs}, [column], None)[column] - with _initialized_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=inputs.indices, - values=np.array((2, -1, 0), dtype=np.int64), - dense_shape=inputs.dense_shape), - id_tensor.eval()) + id_tensor = fc._transform_features_v2({ + 'aaa': inputs + }, [column], None)[column] + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=inputs.indices, + values=np.array((2, -1, 0), dtype=np.int64), + dense_shape=inputs.dense_shape), self.evaluate(id_tensor)) + + @test_util.run_deprecated_v1 def test_get_sparse_tensors_dense_input(self): column = fc.categorical_column_with_vocabulary_list( - key='aaa', - vocabulary_list=('omar', 'stringer', 'marlo')) + key='aaa', vocabulary_list=('omar', 'stringer', 'marlo')) id_weight_pair = column.get_sparse_tensors( fc.FeatureTransformationCache({ 'aaa': (('marlo', ''), ('skywalker', 'omar')) }), None) self.assertIsNone(id_weight_pair.weight_tensor) - with _initialized_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=((0, 0), (1, 0), (1, 1)), - values=np.array((2, -1, 0), dtype=np.int64), - dense_shape=(2, 2)), - id_weight_pair.id_tensor.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=((0, 0), (1, 0), (1, 1)), + values=np.array((2, -1, 0), dtype=np.int64), + dense_shape=(2, 2)), self.evaluate(id_weight_pair.id_tensor)) + + @test_util.run_deprecated_v1 def test_get_sparse_tensors_default_value_in_vocabulary(self): column = fc.categorical_column_with_vocabulary_list( key='aaa', @@ -5035,15 +5287,19 @@ class VocabularyListCategoricalColumnTest(test.TestCase): 'aaa': inputs }), None) self.assertIsNone(id_weight_pair.weight_tensor) - with _initialized_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=inputs.indices, - values=np.array((2, 2, 0), dtype=np.int64), - dense_shape=inputs.dense_shape), - id_weight_pair.id_tensor.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=inputs.indices, + values=np.array((2, 2, 0), dtype=np.int64), + dense_shape=inputs.dense_shape), + self.evaluate(id_weight_pair.id_tensor)) + + @test_util.run_deprecated_v1 def test_get_sparse_tensors_with_oov_buckets(self): column = fc.categorical_column_with_vocabulary_list( key='aaa', @@ -5058,15 +5314,19 @@ class VocabularyListCategoricalColumnTest(test.TestCase): 'aaa': inputs }), None) self.assertIsNone(id_weight_pair.weight_tensor) - with _initialized_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=inputs.indices, - values=np.array((2, 33, 0, 62), dtype=np.int64), - dense_shape=inputs.dense_shape), - id_weight_pair.id_tensor.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=inputs.indices, + values=np.array((2, 33, 0, 62), dtype=np.int64), + dense_shape=inputs.dense_shape), + self.evaluate(id_weight_pair.id_tensor)) + + @test_util.run_deprecated_v1 def test_get_sparse_tensors_int32(self): column = fc.categorical_column_with_vocabulary_list( key='aaa', @@ -5081,15 +5341,19 @@ class VocabularyListCategoricalColumnTest(test.TestCase): 'aaa': inputs }), None) self.assertIsNone(id_weight_pair.weight_tensor) - with _initialized_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=inputs.indices, - values=np.array((2, -1, 0, 4), dtype=np.int64), - dense_shape=inputs.dense_shape), - id_weight_pair.id_tensor.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=inputs.indices, + values=np.array((2, -1, 0, 4), dtype=np.int64), + dense_shape=inputs.dense_shape), + self.evaluate(id_weight_pair.id_tensor)) + + @test_util.run_deprecated_v1 def test_get_sparse_tensors_int32_dense_input(self): default_value = -100 column = fc.categorical_column_with_vocabulary_list( @@ -5104,15 +5368,18 @@ class VocabularyListCategoricalColumnTest(test.TestCase): dtype=np.int32) }), None) self.assertIsNone(id_weight_pair.weight_tensor) - with _initialized_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=((0, 0), (1, 0), (1, 1), (2, 2)), - values=np.array((2, default_value, 0, 4), dtype=np.int64), - dense_shape=(3, 3)), - id_weight_pair.id_tensor.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=((0, 0), (1, 0), (1, 1), (2, 2)), + values=np.array((2, default_value, 0, 4), dtype=np.int64), + dense_shape=(3, 3)), self.evaluate(id_weight_pair.id_tensor)) + + @test_util.run_deprecated_v1 def test_get_sparse_tensors_int32_with_oov_buckets(self): column = fc.categorical_column_with_vocabulary_list( key='aaa', @@ -5128,15 +5395,19 @@ class VocabularyListCategoricalColumnTest(test.TestCase): 'aaa': inputs }), None) self.assertIsNone(id_weight_pair.weight_tensor) - with _initialized_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=inputs.indices, - values=np.array((2, 60, 0, 4), dtype=np.int64), - dense_shape=inputs.dense_shape), - id_weight_pair.id_tensor.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=inputs.indices, + values=np.array((2, 60, 0, 4), dtype=np.int64), + dense_shape=inputs.dense_shape), + self.evaluate(id_weight_pair.id_tensor)) + + @test_util.run_deprecated_v1 def test_linear_model(self): wire_column = fc.categorical_column_with_vocabulary_list( key='aaa', @@ -5153,14 +5424,17 @@ class VocabularyListCategoricalColumnTest(test.TestCase): dense_shape=(2, 2)) }) wire_var, bias = model.variables - with _initialized_session(): - self.assertAllClose((0.,), bias.eval()) - self.assertAllClose(((0.,), (0.,), (0.,), (0.,)), wire_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) - wire_var.assign(((1.,), (2.,), (3.,), (4.,))).eval() - # 'marlo' -> 2: wire_var[2] = 3 - # 'skywalker' -> 3, 'omar' -> 0: wire_var[3] + wire_var[0] = 4+1 = 5 - self.assertAllClose(((3.,), (5.,)), predictions.eval()) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose((0.,), self.evaluate(bias)) + self.assertAllClose(((0.,), (0.,), (0.,), (0.,)), self.evaluate(wire_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) + self.evaluate(wire_var.assign(((1.,), (2.,), (3.,), (4.,)))) + # 'marlo' -> 2: wire_var[2] = 3 + # 'skywalker' -> 3, 'omar' -> 0: wire_var[3] + wire_var[0] = 4+1 = 5 + self.assertAllClose(((3.,), (5.,)), self.evaluate(predictions)) def test_old_linear_model(self): wire_column = fc.categorical_column_with_vocabulary_list( @@ -5178,15 +5452,19 @@ class VocabularyListCategoricalColumnTest(test.TestCase): }, (wire_column,)) bias = get_linear_model_bias() wire_var = get_linear_model_column_var(wire_column) - with _initialized_session(): - self.assertAllClose((0.,), bias.eval()) - self.assertAllClose(((0.,), (0.,), (0.,), (0.,)), wire_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) - wire_var.assign(((1.,), (2.,), (3.,), (4.,))).eval() - # 'marlo' -> 2: wire_var[2] = 3 - # 'skywalker' -> 3, 'omar' -> 0: wire_var[3] + wire_var[0] = 4+1 = 5 - self.assertAllClose(((3.,), (5.,)), predictions.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose((0.,), self.evaluate(bias)) + self.assertAllClose(((0.,), (0.,), (0.,), (0.,)), self.evaluate(wire_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) + self.evaluate(wire_var.assign(((1.,), (2.,), (3.,), (4.,)))) + # 'marlo' -> 2: wire_var[2] = 3 + # 'skywalker' -> 3, 'omar' -> 0: wire_var[3] + wire_var[0] = 4+1 = 5 + self.assertAllClose(((3.,), (5.,)), self.evaluate(predictions)) + + @test_util.run_deprecated_v1 def test_serialization(self): wire_column = fc.categorical_column_with_vocabulary_list( key='aaa', @@ -5208,7 +5486,6 @@ class VocabularyListCategoricalColumnTest(test.TestCase): fc.VocabularyListCategoricalColumn._from_config(config)) - class IdentityCategoricalColumnTest(test.TestCase): def test_constructor(self): @@ -5225,6 +5502,7 @@ class IdentityCategoricalColumnTest(test.TestCase): with self.assertRaisesRegexp(ValueError, 'key must be a string.'): fc.categorical_column_with_identity(key=('aaa',), num_buckets=3) + @test_util.run_deprecated_v1 def test_deep_copy(self): original = fc.categorical_column_with_identity(key='aaa', num_buckets=3) for column in (original, copy.deepcopy(original)): @@ -5264,63 +5542,70 @@ class IdentityCategoricalColumnTest(test.TestCase): 'aaa': inputs }), None) + @test_util.run_deprecated_v1 def test_parse_example(self): a = fc.categorical_column_with_identity(key='aaa', num_buckets=30) - data = example_pb2.Example(features=feature_pb2.Features( - feature={ - 'aaa': - feature_pb2.Feature(int64_list=feature_pb2.Int64List( - value=[11, 21])) - })) + data = example_pb2.Example( + features=feature_pb2.Features( + feature={ + 'aaa': + feature_pb2.Feature( + int64_list=feature_pb2.Int64List(value=[11, 21])) + })) features = parsing_ops.parse_example( serialized=[data.SerializeToString()], - features=fc.make_parse_example_spec([a])) + features=fc.make_parse_example_spec_v2([a])) self.assertIn('aaa', features) - with self.cached_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=[[0, 0], [0, 1]], - values=np.array([11, 21], dtype=np.int64), - dense_shape=[1, 2]), - features['aaa'].eval()) + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=[[0, 0], [0, 1]], + values=np.array([11, 21], dtype=np.int64), + dense_shape=[1, 2]), self.evaluate(features['aaa'])) + + @test_util.run_deprecated_v1 def test_get_sparse_tensors(self): column = fc.categorical_column_with_identity(key='aaa', num_buckets=3) inputs = sparse_tensor.SparseTensorValue( - indices=((0, 0), (1, 0), (1, 1)), - values=(0, 1, 0), - dense_shape=(2, 2)) + indices=((0, 0), (1, 0), (1, 1)), values=(0, 1, 0), dense_shape=(2, 2)) id_weight_pair = column.get_sparse_tensors( fc.FeatureTransformationCache({ 'aaa': inputs }), None) self.assertIsNone(id_weight_pair.weight_tensor) - with _initialized_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=inputs.indices, - values=np.array((0, 1, 0), dtype=np.int64), - dense_shape=inputs.dense_shape), - id_weight_pair.id_tensor.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=inputs.indices, + values=np.array((0, 1, 0), dtype=np.int64), + dense_shape=inputs.dense_shape), + self.evaluate(id_weight_pair.id_tensor)) + + @test_util.run_deprecated_v1 def test_transform_feature(self): column = fc.categorical_column_with_identity(key='aaa', num_buckets=3) inputs = sparse_tensor.SparseTensorValue( - indices=((0, 0), (1, 0), (1, 1)), - values=(0, 1, 0), - dense_shape=(2, 2)) - id_tensor = fc._transform_features({'aaa': inputs}, [column], None)[column] - with _initialized_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=inputs.indices, - values=np.array((0, 1, 0), dtype=np.int64), - dense_shape=inputs.dense_shape), - id_tensor.eval()) - + indices=((0, 0), (1, 0), (1, 1)), values=(0, 1, 0), dense_shape=(2, 2)) + id_tensor = fc._transform_features_v2({ + 'aaa': inputs + }, [column], None)[column] + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=inputs.indices, + values=np.array((0, 1, 0), dtype=np.int64), + dense_shape=inputs.dense_shape), self.evaluate(id_tensor)) + + @test_util.run_deprecated_v1 def test_get_sparse_tensors_dense_input(self): column = fc.categorical_column_with_identity(key='aaa', num_buckets=3) id_weight_pair = column.get_sparse_tensors( @@ -5328,47 +5613,53 @@ class IdentityCategoricalColumnTest(test.TestCase): 'aaa': ((0, -1), (1, 0)) }), None) self.assertIsNone(id_weight_pair.weight_tensor) - with _initialized_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=((0, 0), (1, 0), (1, 1)), - values=np.array((0, 1, 0), dtype=np.int64), - dense_shape=(2, 2)), - id_weight_pair.id_tensor.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=((0, 0), (1, 0), (1, 1)), + values=np.array((0, 1, 0), dtype=np.int64), + dense_shape=(2, 2)), self.evaluate(id_weight_pair.id_tensor)) + + @test_util.run_deprecated_v1 def test_get_sparse_tensors_with_inputs_too_small(self): column = fc.categorical_column_with_identity(key='aaa', num_buckets=3) inputs = sparse_tensor.SparseTensorValue( - indices=((0, 0), (1, 0), (1, 1)), - values=(1, -1, 0), - dense_shape=(2, 2)) + indices=((0, 0), (1, 0), (1, 1)), values=(1, -1, 0), dense_shape=(2, 2)) id_weight_pair = column.get_sparse_tensors( fc.FeatureTransformationCache({ 'aaa': inputs }), None) self.assertIsNone(id_weight_pair.weight_tensor) - with _initialized_session(): - with self.assertRaisesRegexp( - errors.OpError, 'assert_greater_or_equal_0'): - id_weight_pair.id_tensor.eval() + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + with self.assertRaisesRegexp(errors.OpError, 'assert_greater_or_equal_0'): + self.evaluate(id_weight_pair.id_tensor) + + @test_util.run_deprecated_v1 def test_get_sparse_tensors_with_inputs_too_big(self): column = fc.categorical_column_with_identity(key='aaa', num_buckets=3) inputs = sparse_tensor.SparseTensorValue( - indices=((0, 0), (1, 0), (1, 1)), - values=(1, 99, 0), - dense_shape=(2, 2)) + indices=((0, 0), (1, 0), (1, 1)), values=(1, 99, 0), dense_shape=(2, 2)) id_weight_pair = column.get_sparse_tensors( fc.FeatureTransformationCache({ 'aaa': inputs }), None) self.assertIsNone(id_weight_pair.weight_tensor) - with _initialized_session(): - with self.assertRaisesRegexp( - errors.OpError, 'assert_less_than_num_buckets'): - id_weight_pair.id_tensor.eval() + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + with self.assertRaisesRegexp(errors.OpError, + 'assert_less_than_num_buckets'): + self.evaluate(id_weight_pair.id_tensor) + + @test_util.run_deprecated_v1 def test_get_sparse_tensors_with_default_value(self): column = fc.categorical_column_with_identity( key='aaa', num_buckets=4, default_value=3) @@ -5381,15 +5672,19 @@ class IdentityCategoricalColumnTest(test.TestCase): 'aaa': inputs }), None) self.assertIsNone(id_weight_pair.weight_tensor) - with _initialized_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=inputs.indices, - values=np.array((1, 3, 3), dtype=np.int64), - dense_shape=inputs.dense_shape), - id_weight_pair.id_tensor.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=inputs.indices, + values=np.array((1, 3, 3), dtype=np.int64), + dense_shape=inputs.dense_shape), + self.evaluate(id_weight_pair.id_tensor)) + + @test_util.run_deprecated_v1 def test_get_sparse_tensors_with_default_value_and_placeholder_inputs(self): column = fc.categorical_column_with_identity( key='aaa', num_buckets=4, default_value=3) @@ -5397,14 +5692,15 @@ class IdentityCategoricalColumnTest(test.TestCase): input_values = array_ops.placeholder(dtype=dtypes.int32) input_shape = array_ops.placeholder(dtype=dtypes.int64) inputs = sparse_tensor.SparseTensorValue( - indices=input_indices, - values=input_values, - dense_shape=input_shape) + indices=input_indices, values=input_values, dense_shape=input_shape) id_weight_pair = column.get_sparse_tensors( fc.FeatureTransformationCache({ 'aaa': inputs }), None) self.assertIsNone(id_weight_pair.weight_tensor) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) with _initialized_session(): _assert_sparse_tensor_value( self, @@ -5412,12 +5708,14 @@ class IdentityCategoricalColumnTest(test.TestCase): indices=np.array(((0, 0), (1, 0), (1, 1)), dtype=np.int64), values=np.array((1, 3, 3), dtype=np.int64), dense_shape=np.array((2, 2), dtype=np.int64)), - id_weight_pair.id_tensor.eval(feed_dict={ - input_indices: ((0, 0), (1, 0), (1, 1)), - input_values: (1, -1, 99), - input_shape: (2, 2), - })) + id_weight_pair.id_tensor.eval( + feed_dict={ + input_indices: ((0, 0), (1, 0), (1, 1)), + input_values: (1, -1, 99), + input_shape: (2, 2), + })) + @test_util.run_deprecated_v1 def test_linear_model(self): column = fc.categorical_column_with_identity(key='aaa', num_buckets=3) self.assertEqual(3, column.num_buckets) @@ -5431,14 +5729,17 @@ class IdentityCategoricalColumnTest(test.TestCase): dense_shape=(2, 2)) }) weight_var, bias = model.variables - with _initialized_session(): - self.assertAllClose((0.,), bias.eval()) - self.assertAllClose(((0.,), (0.,), (0.,)), weight_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) - weight_var.assign(((1.,), (2.,), (3.,))).eval() - # weight_var[0] = 1 - # weight_var[2] + weight_var[1] = 3+2 = 5 - self.assertAllClose(((1.,), (5.,)), predictions.eval()) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose((0.,), self.evaluate(bias)) + self.assertAllClose(((0.,), (0.,), (0.,)), self.evaluate(weight_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) + self.evaluate(weight_var.assign(((1.,), (2.,), (3.,)))) + # weight_var[0] = 1 + # weight_var[2] + weight_var[1] = 3+2 = 5 + self.assertAllClose(((1.,), (5.,)), self.evaluate(predictions)) def test_old_linear_model(self): column = fc.categorical_column_with_identity(key='aaa', num_buckets=3) @@ -5453,15 +5754,19 @@ class IdentityCategoricalColumnTest(test.TestCase): }, (column,)) bias = get_linear_model_bias() weight_var = get_linear_model_column_var(column) - with _initialized_session(): - self.assertAllClose((0.,), bias.eval()) - self.assertAllClose(((0.,), (0.,), (0.,)), weight_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) - weight_var.assign(((1.,), (2.,), (3.,))).eval() - # weight_var[0] = 1 - # weight_var[2] + weight_var[1] = 3+2 = 5 - self.assertAllClose(((1.,), (5.,)), predictions.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose((0.,), self.evaluate(bias)) + self.assertAllClose(((0.,), (0.,), (0.,)), self.evaluate(weight_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) + self.evaluate(weight_var.assign(((1.,), (2.,), (3.,)))) + # weight_var[0] = 1 + # weight_var[2] + weight_var[1] = 3+2 = 5 + self.assertAllClose(((1.,), (5.,)), self.evaluate(predictions)) + + @test_util.run_deprecated_v1 def test_serialization(self): column = fc.categorical_column_with_identity(key='aaa', num_buckets=3) @@ -5494,13 +5799,18 @@ class TransformFeaturesTest(test.TestCase): indices=[[0, 0], [1, 0], [1, 1]], dense_shape=[2, 2]) } - transformed = fc._transform_features( + transformed = fc._transform_features_v2( features, [bucketized_price, hashed_sparse], None) - with _initialized_session(): - self.assertIn(bucketized_price.name, transformed[bucketized_price].name) - self.assertAllEqual([[0], [3]], transformed[bucketized_price].eval()) - self.assertIn(hashed_sparse.name, transformed[hashed_sparse].name) - self.assertAllEqual([6, 4, 1], transformed[hashed_sparse].values.eval()) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertIn(bucketized_price.name, transformed[bucketized_price].name) + self.assertAllEqual([[0], [3]], + self.evaluate(transformed[bucketized_price])) + self.assertIn(hashed_sparse.name, transformed[hashed_sparse].name) + self.assertAllEqual([6, 4, 1], + self.evaluate(transformed[hashed_sparse].values)) def test_column_order(self): """When the column is both dense and sparse, uses sparse tensors.""" @@ -5531,12 +5841,12 @@ class TransformFeaturesTest(test.TestCase): column1 = _LoggerColumn('1') column2 = _LoggerColumn('2') call_logger = {'count': 0} - fc._transform_features({}, [column1, column2], None) + fc._transform_features_v2({}, [column1, column2], None) self.assertEqual(0, column1.call_order) self.assertEqual(1, column2.call_order) call_logger = {'count': 0} - fc._transform_features({}, [column2, column1], None) + fc._transform_features_v2({}, [column2, column1], None) self.assertEqual(0, column1.call_order) self.assertEqual(1, column2.call_order) @@ -5551,7 +5861,7 @@ class IndicatorColumnTest(test.TestCase): self.assertEqual(indicator_a.variable_shape, [1, 4]) self.assertTrue(indicator_a._is_v2_column) - b = fc_old.categorical_column_with_hash_bucket('b', hash_bucket_size=100) + b = fc_old._categorical_column_with_hash_bucket('b', hash_bucket_size=100) indicator_b = fc.indicator_column(b) self.assertEqual(indicator_b.categorical_column.name, 'b') self.assertEqual(indicator_b.name, 'b_indicator') @@ -5565,8 +5875,9 @@ class IndicatorColumnTest(test.TestCase): 'animal': ['fox', 'fox'] }) output = transformation_cache.get(animal, None) - with self.cached_session(): - self.assertAllEqual([[0., 0., 1., 0.], [0., 0., 1., 0.]], output.eval()) + + self.assertAllEqual([[0., 0., 1., 0.], [0., 0., 1., 0.]], + self.evaluate(output)) def test_2D_shape_succeeds(self): # TODO(ispir/cassandrax): Swith to categorical_column_with_keys when ready. @@ -5580,8 +5891,9 @@ class IndicatorColumnTest(test.TestCase): dense_shape=[2, 1]) }) output = transformation_cache.get(animal, None) - with self.cached_session(): - self.assertAllEqual([[0., 0., 1., 0.], [0., 0., 1., 0.]], output.eval()) + + self.assertAllEqual([[0., 0., 1., 0.], [0., 0., 1., 0.]], + self.evaluate(output)) def test_multi_hot(self): animal = fc.indicator_column( @@ -5593,8 +5905,8 @@ class IndicatorColumnTest(test.TestCase): indices=[[0, 0], [0, 1]], values=[1, 1], dense_shape=[1, 2]) }) output = transformation_cache.get(animal, None) - with self.cached_session(): - self.assertAllEqual([[0., 2., 0., 0.]], output.eval()) + + self.assertAllEqual([[0., 2., 0., 0.]], self.evaluate(output)) def test_multi_hot2(self): animal = fc.indicator_column( @@ -5605,9 +5917,10 @@ class IndicatorColumnTest(test.TestCase): indices=[[0, 0], [0, 1]], values=[1, 2], dense_shape=[1, 2]) }) output = transformation_cache.get(animal, None) - with self.cached_session(): - self.assertAllEqual([[0., 1., 1., 0.]], output.eval()) + self.assertAllEqual([[0., 1., 1., 0.]], self.evaluate(output)) + + @test_util.run_deprecated_v1 def test_deep_copy(self): a = fc.categorical_column_with_hash_bucket('a', 4) column = fc.indicator_column(a) @@ -5616,44 +5929,52 @@ class IndicatorColumnTest(test.TestCase): self.assertEqual(column.name, 'a_indicator') self.assertEqual(column.variable_shape, [1, 4]) + @test_util.run_deprecated_v1 def test_parse_example(self): a = fc.categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=('omar', 'stringer', 'marlo')) a_indicator = fc.indicator_column(a) - data = example_pb2.Example(features=feature_pb2.Features( - feature={ - 'aaa': - feature_pb2.Feature(bytes_list=feature_pb2.BytesList( - value=[b'omar', b'stringer'])) - })) + data = example_pb2.Example( + features=feature_pb2.Features( + feature={ + 'aaa': + feature_pb2.Feature( + bytes_list=feature_pb2.BytesList( + value=[b'omar', b'stringer'])) + })) features = parsing_ops.parse_example( serialized=[data.SerializeToString()], - features=fc.make_parse_example_spec([a_indicator])) + features=fc.make_parse_example_spec_v2([a_indicator])) self.assertIn('aaa', features) - with self.cached_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=[[0, 0], [0, 1]], - values=np.array([b'omar', b'stringer'], dtype=np.object_), - dense_shape=[1, 2]), - features['aaa'].eval()) + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=[[0, 0], [0, 1]], + values=np.array([b'omar', b'stringer'], dtype=np.object_), + dense_shape=[1, 2]), self.evaluate(features['aaa'])) + + @test_util.run_deprecated_v1 def test_transform(self): a = fc.categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=('omar', 'stringer', 'marlo')) a_indicator = fc.indicator_column(a) features = { - 'aaa': sparse_tensor.SparseTensorValue( - indices=((0, 0), (1, 0), (1, 1)), - values=('marlo', 'skywalker', 'omar'), - dense_shape=(2, 2)) + 'aaa': + sparse_tensor.SparseTensorValue( + indices=((0, 0), (1, 0), (1, 1)), + values=('marlo', 'skywalker', 'omar'), + dense_shape=(2, 2)) } - indicator_tensor = fc._transform_features(features, [a_indicator], - None)[a_indicator] - with _initialized_session(): - self.assertAllEqual([[0, 0, 1], [1, 0, 0]], indicator_tensor.eval()) + indicator_tensor = fc._transform_features_v2(features, [a_indicator], + None)[a_indicator] + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllEqual([[0, 0, 1], [1, 0, 0]], self.evaluate(indicator_tensor)) + @test_util.run_deprecated_v1 def test_transform_with_weighted_column(self): # Github issue 12557 ids = fc.categorical_column_with_vocabulary_list( @@ -5661,14 +5982,18 @@ class IndicatorColumnTest(test.TestCase): weights = fc.weighted_categorical_column(ids, 'weights') indicator = fc.indicator_column(weights) features = { - 'ids': constant_op.constant([['c', 'b', 'a']]), - 'weights': constant_op.constant([[2., 4., 6.]]) + 'ids': constant_op.constant([['c', 'b', 'a', 'c']]), + 'weights': constant_op.constant([[2., 4., 6., 1.]]) } - indicator_tensor = fc._transform_features(features, [indicator], - None)[indicator] - with _initialized_session(): - self.assertAllEqual([[6., 4., 2.]], indicator_tensor.eval()) + indicator_tensor = fc._transform_features_v2(features, [indicator], + None)[indicator] + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + self.assertAllEqual([[6., 4., 3.]], self.evaluate(indicator_tensor)) + + @test_util.run_deprecated_v1 def test_transform_with_missing_value_in_weighted_column(self): # Github issue 12583 ids = fc.categorical_column_with_vocabulary_list( @@ -5679,11 +6004,15 @@ class IndicatorColumnTest(test.TestCase): 'ids': constant_op.constant([['c', 'b', 'unknown']]), 'weights': constant_op.constant([[2., 4., 6.]]) } - indicator_tensor = fc._transform_features(features, [indicator], - None)[indicator] - with _initialized_session(): - self.assertAllEqual([[0., 4., 2.]], indicator_tensor.eval()) + indicator_tensor = fc._transform_features_v2(features, [indicator], + None)[indicator] + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + self.assertAllEqual([[0., 4., 2.]], self.evaluate(indicator_tensor)) + + @test_util.run_deprecated_v1 def test_transform_with_missing_value_in_categorical_column(self): # Github issue 12583 ids = fc.categorical_column_with_vocabulary_list( @@ -5692,11 +6021,15 @@ class IndicatorColumnTest(test.TestCase): features = { 'ids': constant_op.constant([['c', 'b', 'unknown']]), } - indicator_tensor = fc._transform_features(features, [indicator], - None)[indicator] - with _initialized_session(): - self.assertAllEqual([[0., 1., 1.]], indicator_tensor.eval()) + indicator_tensor = fc._transform_features_v2(features, [indicator], + None)[indicator] + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + self.assertAllEqual([[0., 1., 1.]], self.evaluate(indicator_tensor)) + + @test_util.run_deprecated_v1 def test_linear_model(self): animal = fc.indicator_column( fc.categorical_column_with_identity('animal', num_buckets=4)) @@ -5710,12 +6043,15 @@ class IndicatorColumnTest(test.TestCase): model = fc.LinearModel([animal]) predictions = model(features) weight_var, _ = model.variables - with _initialized_session(): - # All should be zero-initialized. - self.assertAllClose([[0.], [0.], [0.], [0.]], weight_var.eval()) - self.assertAllClose([[0.]], predictions.eval()) - weight_var.assign([[1.], [2.], [3.], [4.]]).eval() - self.assertAllClose([[2. + 3.]], predictions.eval()) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + # All should be zero-initialized. + self.assertAllClose([[0.], [0.], [0.], [0.]], self.evaluate(weight_var)) + self.assertAllClose([[0.]], self.evaluate(predictions)) + self.evaluate(weight_var.assign([[1.], [2.], [3.], [4.]])) + self.assertAllClose([[2. + 3.]], self.evaluate(predictions)) def test_old_linear_model(self): animal = fc.indicator_column( @@ -5729,16 +6065,19 @@ class IndicatorColumnTest(test.TestCase): predictions = fc_old.linear_model(features, [animal]) weight_var = get_linear_model_column_var(animal) - with _initialized_session(): - # All should be zero-initialized. - self.assertAllClose([[0.], [0.], [0.], [0.]], weight_var.eval()) - self.assertAllClose([[0.]], predictions.eval()) - weight_var.assign([[1.], [2.], [3.], [4.]]).eval() - self.assertAllClose([[2. + 3.]], predictions.eval()) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + # All should be zero-initialized. + self.assertAllClose([[0.], [0.], [0.], [0.]], self.evaluate(weight_var)) + self.assertAllClose([[0.]], self.evaluate(predictions)) + self.evaluate(weight_var.assign([[1.], [2.], [3.], [4.]])) + self.assertAllClose([[2. + 3.]], self.evaluate(predictions)) def test_old_linear_model_old_categorical(self): animal = fc.indicator_column( - fc_old.categorical_column_with_identity('animal', num_buckets=4)) + fc_old._categorical_column_with_identity('animal', num_buckets=4)) with ops.Graph().as_default(): features = { 'animal': @@ -5748,14 +6087,18 @@ class IndicatorColumnTest(test.TestCase): predictions = fc_old.linear_model(features, [animal]) weight_var = get_linear_model_column_var(animal) - with _initialized_session(): - # All should be zero-initialized. - self.assertAllClose([[0.], [0.], [0.], [0.]], weight_var.eval()) - self.assertAllClose([[0.]], predictions.eval()) - weight_var.assign([[1.], [2.], [3.], [4.]]).eval() - self.assertAllClose([[2. + 3.]], predictions.eval()) - - def test_feature_layer(self): + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + # All should be zero-initialized. + self.assertAllClose([[0.], [0.], [0.], [0.]], self.evaluate(weight_var)) + self.assertAllClose([[0.]], self.evaluate(predictions)) + self.evaluate(weight_var.assign([[1.], [2.], [3.], [4.]])) + self.assertAllClose([[2. + 3.]], self.evaluate(predictions)) + + @test_util.run_deprecated_v1 + def test_dense_features(self): animal = fc.indicator_column( fc.categorical_column_with_identity('animal', num_buckets=4)) with ops.Graph().as_default(): @@ -5764,10 +6107,14 @@ class IndicatorColumnTest(test.TestCase): sparse_tensor.SparseTensor( indices=[[0, 0], [0, 1]], values=[1, 2], dense_shape=[1, 2]) } - net = fc.FeatureLayer([animal])(features) - with _initialized_session(): - self.assertAllClose([[0., 1., 1., 0.]], net.eval()) + net = fc.DenseFeatures([animal])(features) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose([[0., 1., 1., 0.]], self.evaluate(net)) + + @test_util.run_deprecated_v1 def test_input_layer(self): animal = fc.indicator_column( fc.categorical_column_with_identity('animal', num_buckets=4)) @@ -5778,12 +6125,15 @@ class IndicatorColumnTest(test.TestCase): indices=[[0, 0], [0, 1]], values=[1, 2], dense_shape=[1, 2]) } net = fc_old.input_layer(features, [animal]) - with _initialized_session(): - self.assertAllClose([[0., 1., 1., 0.]], net.eval()) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose([[0., 1., 1., 0.]], self.evaluate(net)) def test_input_layer_old_categorical(self): animal = fc.indicator_column( - fc_old.categorical_column_with_identity('animal', num_buckets=4)) + fc_old._categorical_column_with_identity('animal', num_buckets=4)) with ops.Graph().as_default(): features = { 'animal': @@ -5791,9 +6141,13 @@ class IndicatorColumnTest(test.TestCase): indices=[[0, 0], [0, 1]], values=[1, 2], dense_shape=[1, 2]) } net = fc_old.input_layer(features, [animal]) - with _initialized_session(): - self.assertAllClose([[0., 1., 1., 0.]], net.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose([[0., 1., 1., 0.]], self.evaluate(net)) + + @test_util.run_deprecated_v1 def test_serialization(self): parent = fc.categorical_column_with_identity('animal', num_buckets=4) animal = fc.indicator_column(parent) @@ -5822,7 +6176,6 @@ class IndicatorColumnTest(test.TestCase): self.assertIs(parent, new_animal.categorical_column) - class _TestStateManager(fc.StateManager): def __init__(self, trainable=True): @@ -5864,6 +6217,7 @@ class _TestStateManager(fc.StateManager): class EmbeddingColumnTest(test.TestCase): + @test_util.run_deprecated_v1 def test_defaults(self): categorical_column = fc.categorical_column_with_identity( key='aaa', num_buckets=3) @@ -5885,22 +6239,27 @@ class EmbeddingColumnTest(test.TestCase): self.assertTrue(embedding_column._is_v2_column) def test_is_v2_column(self): - categorical_column = fc_old.categorical_column_with_identity( + categorical_column = fc_old._categorical_column_with_identity( key='aaa', num_buckets=3) embedding_dimension = 2 embedding_column = fc.embedding_column( categorical_column, dimension=embedding_dimension) self.assertFalse(embedding_column._is_v2_column) + @test_util.run_deprecated_v1 def test_all_constructor_args(self): categorical_column = fc.categorical_column_with_identity( key='aaa', num_buckets=3) embedding_dimension = 2 embedding_column = fc.embedding_column( - categorical_column, dimension=embedding_dimension, - combiner='my_combiner', initializer=lambda: 'my_initializer', - ckpt_to_load_from='my_ckpt', tensor_name_in_ckpt='my_ckpt_tensor', - max_norm=42., trainable=False) + categorical_column, + dimension=embedding_dimension, + combiner='my_combiner', + initializer=lambda: 'my_initializer', + ckpt_to_load_from='my_ckpt', + tensor_name_in_ckpt='my_ckpt_tensor', + max_norm=42., + trainable=False) self.assertIs(categorical_column, embedding_column.categorical_column) self.assertEqual(embedding_dimension, embedding_column.dimension) self.assertEqual('my_combiner', embedding_column.combiner) @@ -5914,15 +6273,20 @@ class EmbeddingColumnTest(test.TestCase): 'aaa': parsing_ops.VarLenFeature(dtypes.int64) }, embedding_column.parse_example_spec) + @test_util.run_deprecated_v1 def test_deep_copy(self): categorical_column = fc.categorical_column_with_identity( key='aaa', num_buckets=3) embedding_dimension = 2 original = fc.embedding_column( - categorical_column, dimension=embedding_dimension, - combiner='my_combiner', initializer=lambda: 'my_initializer', - ckpt_to_load_from='my_ckpt', tensor_name_in_ckpt='my_ckpt_tensor', - max_norm=42., trainable=False) + categorical_column, + dimension=embedding_dimension, + combiner='my_combiner', + initializer=lambda: 'my_initializer', + ckpt_to_load_from='my_ckpt', + tensor_name_in_ckpt='my_ckpt_tensor', + max_norm=42., + trainable=False) for embedding_column in (original, copy.deepcopy(original)): self.assertEqual('aaa', embedding_column.categorical_column.name) self.assertEqual(3, embedding_column.categorical_column.num_buckets) @@ -5942,51 +6306,60 @@ class EmbeddingColumnTest(test.TestCase): 'aaa': parsing_ops.VarLenFeature(dtypes.int64) }, embedding_column.parse_example_spec) + @test_util.run_deprecated_v1 def test_invalid_initializer(self): categorical_column = fc.categorical_column_with_identity( key='aaa', num_buckets=3) with self.assertRaisesRegexp(ValueError, 'initializer must be callable'): fc.embedding_column(categorical_column, dimension=2, initializer='not_fn') + @test_util.run_deprecated_v1 def test_parse_example(self): a = fc.categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=('omar', 'stringer', 'marlo')) a_embedded = fc.embedding_column(a, dimension=2) - data = example_pb2.Example(features=feature_pb2.Features( - feature={ - 'aaa': - feature_pb2.Feature(bytes_list=feature_pb2.BytesList( - value=[b'omar', b'stringer'])) - })) + data = example_pb2.Example( + features=feature_pb2.Features( + feature={ + 'aaa': + feature_pb2.Feature( + bytes_list=feature_pb2.BytesList( + value=[b'omar', b'stringer'])) + })) features = parsing_ops.parse_example( serialized=[data.SerializeToString()], - features=fc.make_parse_example_spec([a_embedded])) + features=fc.make_parse_example_spec_v2([a_embedded])) self.assertIn('aaa', features) - with self.cached_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=[[0, 0], [0, 1]], - values=np.array([b'omar', b'stringer'], dtype=np.object_), - dense_shape=[1, 2]), - features['aaa'].eval()) + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=[[0, 0], [0, 1]], + values=np.array([b'omar', b'stringer'], dtype=np.object_), + dense_shape=[1, 2]), self.evaluate(features['aaa'])) + + @test_util.run_deprecated_v1 def test_transform_feature(self): a = fc.categorical_column_with_identity(key='aaa', num_buckets=3) a_embedded = fc.embedding_column(a, dimension=2) features = { - 'aaa': sparse_tensor.SparseTensor( - indices=((0, 0), (1, 0), (1, 1)), - values=(0, 1, 0), - dense_shape=(2, 2)) + 'aaa': + sparse_tensor.SparseTensor( + indices=((0, 0), (1, 0), (1, 1)), + values=(0, 1, 0), + dense_shape=(2, 2)) } - outputs = fc._transform_features(features, [a, a_embedded], None) + outputs = fc._transform_features_v2(features, [a, a_embedded], None) output_a = outputs[a] output_embedded = outputs[a_embedded] - with _initialized_session(): - _assert_sparse_tensor_value( - self, output_a.eval(), output_embedded.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + _assert_sparse_tensor_value(self, self.evaluate(output_a), + self.evaluate(output_embedded)) + + @test_util.run_deprecated_v1 def test_get_dense_tensor(self): # Inputs. vocabulary_size = 3 @@ -6006,6 +6379,7 @@ class EmbeddingColumnTest(test.TestCase): (3., 5.), # id 1 (7., 11.) # id 2 ) + def _initializer(shape, dtype, partition_info): self.assertAllEqual((vocabulary_size, embedding_dimension), shape) self.assertEqual(dtypes.float32, dtype) @@ -6028,7 +6402,8 @@ class EmbeddingColumnTest(test.TestCase): categorical_column = fc.categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) embedding_column = fc.embedding_column( - categorical_column, dimension=embedding_dimension, + categorical_column, + dimension=embedding_dimension, initializer=_initializer) state_manager = _TestStateManager() embedding_column.create_state(state_manager) @@ -6043,10 +6418,14 @@ class EmbeddingColumnTest(test.TestCase): global_vars = ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES) self.assertItemsEqual(('embedding_weights:0',), tuple([v.name for v in global_vars])) - with _initialized_session(): - self.assertAllEqual(embedding_values, global_vars[0].eval()) - self.assertAllEqual(expected_lookups, embedding_lookup.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllEqual(embedding_values, self.evaluate(global_vars[0])) + self.assertAllEqual(expected_lookups, self.evaluate(embedding_lookup)) + + @test_util.run_deprecated_v1 def test_get_dense_tensor_old_categorical(self): # Inputs. vocabulary_size = 3 @@ -6086,7 +6465,7 @@ class EmbeddingColumnTest(test.TestCase): ) # Build columns. - categorical_column = fc_old.categorical_column_with_identity( + categorical_column = fc_old._categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) embedding_column = fc.embedding_column( categorical_column, @@ -6103,10 +6482,14 @@ class EmbeddingColumnTest(test.TestCase): global_vars = ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES) self.assertItemsEqual(('embedding_weights:0',), tuple([v.name for v in global_vars])) - with _initialized_session(): - self.assertAllEqual(embedding_values, global_vars[0].eval()) - self.assertAllEqual(expected_lookups, embedding_lookup.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllEqual(embedding_values, self.evaluate(global_vars[0])) + self.assertAllEqual(expected_lookups, self.evaluate(embedding_lookup)) + + @test_util.run_deprecated_v1 def test_get_dense_tensor_3d(self): # Inputs. vocabulary_size = 4 @@ -6122,11 +6505,12 @@ class EmbeddingColumnTest(test.TestCase): # Embedding variable. embedding_dimension = 3 embedding_values = ( - (1., 2., 4.), # id 0 - (3., 5., 1.), # id 1 + (1., 2., 4.), # id 0 + (3., 5., 1.), # id 1 (7., 11., 2.), # id 2 - (2., 7., 12.) # id 3 + (2., 7., 12.) # id 3 ) + def _initializer(shape, dtype, partition_info): self.assertAllEqual((vocabulary_size, embedding_dimension), shape) self.assertEqual(dtypes.float32, dtype) @@ -6150,7 +6534,8 @@ class EmbeddingColumnTest(test.TestCase): categorical_column = fc.categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) embedding_column = fc.embedding_column( - categorical_column, dimension=embedding_dimension, + categorical_column, + dimension=embedding_dimension, initializer=_initializer) state_manager = _TestStateManager() embedding_column.create_state(state_manager) @@ -6165,10 +6550,14 @@ class EmbeddingColumnTest(test.TestCase): global_vars = ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES) self.assertItemsEqual(('embedding_weights:0',), tuple([v.name for v in global_vars])) - with _initialized_session(): - self.assertAllEqual(embedding_values, global_vars[0].eval()) - self.assertAllEqual(expected_lookups, embedding_lookup.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllEqual(embedding_values, self.evaluate(global_vars[0])) + self.assertAllEqual(expected_lookups, self.evaluate(embedding_lookup)) + + @test_util.run_deprecated_v1 def test_get_dense_tensor_placeholder_inputs(self): # Inputs. vocabulary_size = 3 @@ -6188,6 +6577,7 @@ class EmbeddingColumnTest(test.TestCase): (3., 5.), # id 1 (7., 11.) # id 2 ) + def _initializer(shape, dtype, partition_info): self.assertAllEqual((vocabulary_size, embedding_dimension), shape) self.assertEqual(dtypes.float32, dtype) @@ -6210,7 +6600,8 @@ class EmbeddingColumnTest(test.TestCase): categorical_column = fc.categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) embedding_column = fc.embedding_column( - categorical_column, dimension=embedding_dimension, + categorical_column, + dimension=embedding_dimension, initializer=_initializer) state_manager = _TestStateManager() embedding_column.create_state(state_manager) @@ -6230,17 +6621,23 @@ class EmbeddingColumnTest(test.TestCase): # Assert expected embedding variable and lookups. global_vars = ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES) - self.assertItemsEqual( - ('embedding_weights:0',), tuple([v.name for v in global_vars])) + self.assertItemsEqual(('embedding_weights:0',), + tuple([v.name for v in global_vars])) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) with _initialized_session(): - self.assertAllEqual(embedding_values, global_vars[0].eval()) - self.assertAllEqual(expected_lookups, embedding_lookup.eval( - feed_dict={ - input_indices: sparse_input.indices, - input_values: sparse_input.values, - input_shape: sparse_input.dense_shape, - })) + self.assertAllEqual(embedding_values, self.evaluate(global_vars[0])) + self.assertAllEqual( + expected_lookups, + embedding_lookup.eval( + feed_dict={ + input_indices: sparse_input.indices, + input_values: sparse_input.values, + input_shape: sparse_input.dense_shape, + })) + @test_util.run_deprecated_v1 def test_get_dense_tensor_restore_from_ckpt(self): # Inputs. vocabulary_size = 3 @@ -6280,7 +6677,8 @@ class EmbeddingColumnTest(test.TestCase): categorical_column = fc.categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) embedding_column = fc.embedding_column( - categorical_column, dimension=embedding_dimension, + categorical_column, + dimension=embedding_dimension, ckpt_to_load_from=ckpt_path, tensor_name_in_ckpt=ckpt_tensor) state_manager = _TestStateManager() @@ -6294,12 +6692,16 @@ class EmbeddingColumnTest(test.TestCase): # Assert expected embedding variable and lookups. global_vars = ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES) - self.assertItemsEqual( - ('embedding_weights:0',), tuple([v.name for v in global_vars])) - with _initialized_session(): - self.assertAllEqual(embedding_values, global_vars[0].eval()) - self.assertAllEqual(expected_lookups, embedding_lookup.eval()) + self.assertItemsEqual(('embedding_weights:0',), + tuple([v.name for v in global_vars])) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllEqual(embedding_values, self.evaluate(global_vars[0])) + self.assertAllEqual(expected_lookups, self.evaluate(embedding_lookup)) + + @test_util.run_deprecated_v1 def test_linear_model(self): # Inputs. batch_size = 4 @@ -6317,6 +6719,7 @@ class EmbeddingColumnTest(test.TestCase): embedding_dimension = 2 embedding_shape = (vocabulary_size, embedding_dimension) zeros_embedding_values = np.zeros(embedding_shape) + def _initializer(shape, dtype, partition_info): self.assertAllEqual(embedding_shape, shape) self.assertEqual(dtypes.float32, dtype) @@ -6343,39 +6746,45 @@ class EmbeddingColumnTest(test.TestCase): expected_var_names, [v.name for v in ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES)]) trainable_vars = { - v.name: v for v in ops.get_collection( - ops.GraphKeys.TRAINABLE_VARIABLES) + v.name: v + for v in ops.get_collection(ops.GraphKeys.TRAINABLE_VARIABLES) } self.assertItemsEqual(expected_var_names, trainable_vars.keys()) bias = trainable_vars['linear_model/bias_weights:0'] embedding_weights = trainable_vars[ 'linear_model/aaa_embedding/embedding_weights:0'] - linear_weights = trainable_vars[ - 'linear_model/aaa_embedding/weights:0'] - with _initialized_session(): - # Predictions with all zero weights. - self.assertAllClose(np.zeros((1,)), bias.eval()) - self.assertAllClose(zeros_embedding_values, embedding_weights.eval()) - self.assertAllClose( - np.zeros((embedding_dimension, 1)), linear_weights.eval()) - self.assertAllClose(np.zeros((batch_size, 1)), predictions.eval()) - - # Predictions with all non-zero weights. - embedding_weights.assign(( - (1., 2.), # id 0 - (3., 5.), # id 1 - (7., 11.) # id 2 - )).eval() - linear_weights.assign(((4.,), (6.,))).eval() - # example 0, ids [2], embedding[0] = [7, 11] - # example 1, ids [0, 1], embedding[1] = mean([1, 2] + [3, 5]) = [2, 3.5] - # example 2, ids [], embedding[2] = [0, 0] - # example 3, ids [1], embedding[3] = [3, 5] - # sum(embeddings * linear_weights) - # = [4*7 + 6*11, 4*2 + 6*3.5, 4*0 + 6*0, 4*3 + 6*5] = [94, 29, 0, 42] - self.assertAllClose(((94.,), (29.,), (0.,), (42.,)), predictions.eval()) - - def test_feature_layer(self): + linear_weights = trainable_vars['linear_model/aaa_embedding/weights:0'] + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + # Predictions with all zero weights. + self.assertAllClose(np.zeros((1,)), self.evaluate(bias)) + self.assertAllClose(zeros_embedding_values, + self.evaluate(embedding_weights)) + self.assertAllClose( + np.zeros((embedding_dimension, 1)), self.evaluate(linear_weights)) + self.assertAllClose(np.zeros((batch_size, 1)), self.evaluate(predictions)) + + # Predictions with all non-zero weights. + self.evaluate( + embedding_weights.assign(( + (1., 2.), # id 0 + (3., 5.), # id 1 + (7., 11.) # id 2 + ))) + self.evaluate(linear_weights.assign(((4.,), (6.,)))) + # example 0, ids [2], embedding[0] = [7, 11] + # example 1, ids [0, 1], embedding[1] = mean([1, 2] + [3, 5]) = [2, 3.5] + # example 2, ids [], embedding[2] = [0, 0] + # example 3, ids [1], embedding[3] = [3, 5] + # sum(embeddings * linear_weights) + # = [4*7 + 6*11, 4*2 + 6*3.5, 4*0 + 6*0, 4*3 + 6*5] = [94, 29, 0, 42] + self.assertAllClose(((94.,), (29.,), (0.,), (42.,)), + self.evaluate(predictions)) + + @test_util.run_deprecated_v1 + def test_dense_features(self): # Inputs. vocabulary_size = 3 sparse_input = sparse_tensor.SparseTensorValue( @@ -6394,6 +6803,7 @@ class EmbeddingColumnTest(test.TestCase): (3., 5.), # id 1 (7., 11.) # id 2 ) + def _initializer(shape, dtype, partition_info): self.assertAllEqual((vocabulary_size, embedding_dimension), shape) self.assertEqual(dtypes.float32, dtype) @@ -6421,23 +6831,27 @@ class EmbeddingColumnTest(test.TestCase): initializer=_initializer) # Provide sparse input and get dense result. - l = fc.FeatureLayer((embedding_column,)) - feature_layer = l({'aaa': sparse_input}) + l = fc.DenseFeatures((embedding_column,)) + dense_features = l({'aaa': sparse_input}) # Assert expected embedding variable and lookups. global_vars = ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES) - self.assertItemsEqual(('feature_layer/aaa_embedding/embedding_weights:0',), + self.assertItemsEqual(('dense_features/aaa_embedding/embedding_weights:0',), tuple([v.name for v in global_vars])) for v in global_vars: self.assertTrue(isinstance(v, variables_lib.RefVariable)) trainable_vars = ops.get_collection(ops.GraphKeys.TRAINABLE_VARIABLES) - self.assertItemsEqual(('feature_layer/aaa_embedding/embedding_weights:0',), + self.assertItemsEqual(('dense_features/aaa_embedding/embedding_weights:0',), tuple([v.name for v in trainable_vars])) - with _initialized_session(): - self.assertAllEqual(embedding_values, trainable_vars[0].eval()) - self.assertAllEqual(expected_lookups, feature_layer.eval()) - def test_feature_layer_not_trainable(self): + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllEqual(embedding_values, self.evaluate(trainable_vars[0])) + self.assertAllEqual(expected_lookups, self.evaluate(dense_features)) + + @test_util.run_deprecated_v1 + def test_dense_features_not_trainable(self): # Inputs. vocabulary_size = 3 sparse_input = sparse_tensor.SparseTensorValue( @@ -6456,6 +6870,7 @@ class EmbeddingColumnTest(test.TestCase): (3., 5.), # id 1 (7., 11.) # id 2 ) + def _initializer(shape, dtype, partition_info): self.assertAllEqual((vocabulary_size, embedding_dimension), shape) self.assertEqual(dtypes.float32, dtype) @@ -6484,18 +6899,24 @@ class EmbeddingColumnTest(test.TestCase): trainable=False) # Provide sparse input and get dense result. - feature_layer = fc.FeatureLayer((embedding_column,))({'aaa': sparse_input}) + dense_features = fc.DenseFeatures((embedding_column,))({ + 'aaa': sparse_input + }) # Assert expected embedding variable and lookups. global_vars = ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES) - self.assertItemsEqual(('feature_layer/aaa_embedding/embedding_weights:0',), + self.assertItemsEqual(('dense_features/aaa_embedding/embedding_weights:0',), tuple([v.name for v in global_vars])) - self.assertItemsEqual( - [], ops.get_collection(ops.GraphKeys.TRAINABLE_VARIABLES)) - with _initialized_session(): - self.assertAllEqual(embedding_values, global_vars[0].eval()) - self.assertAllEqual(expected_lookups, feature_layer.eval()) + self.assertItemsEqual([], + ops.get_collection(ops.GraphKeys.TRAINABLE_VARIABLES)) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllEqual(embedding_values, self.evaluate(global_vars[0])) + self.assertAllEqual(expected_lookups, self.evaluate(dense_features)) + @test_util.run_deprecated_v1 def test_input_layer(self): # Inputs. vocabulary_size = 3 @@ -6554,9 +6975,12 @@ class EmbeddingColumnTest(test.TestCase): trainable_vars = ops.get_collection(ops.GraphKeys.TRAINABLE_VARIABLES) self.assertItemsEqual(('input_layer/aaa_embedding/embedding_weights:0',), tuple([v.name for v in trainable_vars])) - with _initialized_session(): - self.assertAllEqual(embedding_values, trainable_vars[0].eval()) - self.assertAllEqual(expected_lookups, feature_layer.eval()) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllEqual(embedding_values, self.evaluate(trainable_vars[0])) + self.assertAllEqual(expected_lookups, self.evaluate(feature_layer)) def test_old_linear_model(self): # Inputs. @@ -6611,28 +7035,34 @@ class EmbeddingColumnTest(test.TestCase): embedding_weights = trainable_vars[ 'linear_model/aaa_embedding/embedding_weights:0'] linear_weights = trainable_vars['linear_model/aaa_embedding/weights:0'] - with _initialized_session(): - # Predictions with all zero weights. - self.assertAllClose(np.zeros((1,)), bias.eval()) - self.assertAllClose(zeros_embedding_values, embedding_weights.eval()) - self.assertAllClose( - np.zeros((embedding_dimension, 1)), linear_weights.eval()) - self.assertAllClose(np.zeros((batch_size, 1)), predictions.eval()) - - # Predictions with all non-zero weights. - embedding_weights.assign(( - (1., 2.), # id 0 - (3., 5.), # id 1 - (7., 11.) # id 2 - )).eval() - linear_weights.assign(((4.,), (6.,))).eval() - # example 0, ids [2], embedding[0] = [7, 11] - # example 1, ids [0, 1], embedding[1] = mean([1, 2] + [3, 5]) = [2, 3.5] - # example 2, ids [], embedding[2] = [0, 0] - # example 3, ids [1], embedding[3] = [3, 5] - # sum(embeddings * linear_weights) - # = [4*7 + 6*11, 4*2 + 6*3.5, 4*0 + 6*0, 4*3 + 6*5] = [94, 29, 0, 42] - self.assertAllClose(((94.,), (29.,), (0.,), (42.,)), predictions.eval()) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + # Predictions with all zero weights. + self.assertAllClose(np.zeros((1,)), self.evaluate(bias)) + self.assertAllClose(zeros_embedding_values, + self.evaluate(embedding_weights)) + self.assertAllClose( + np.zeros((embedding_dimension, 1)), self.evaluate(linear_weights)) + self.assertAllClose(np.zeros((batch_size, 1)), self.evaluate(predictions)) + + # Predictions with all non-zero weights. + self.evaluate( + embedding_weights.assign(( + (1., 2.), # id 0 + (3., 5.), # id 1 + (7., 11.) # id 2 + ))) + self.evaluate(linear_weights.assign(((4.,), (6.,)))) + # example 0, ids [2], embedding[0] = [7, 11] + # example 1, ids [0, 1], embedding[1] = mean([1, 2] + [3, 5]) = [2, 3.5] + # example 2, ids [], embedding[2] = [0, 0] + # example 3, ids [1], embedding[3] = [3, 5] + # sum(embeddings * linear_weights) + # = [4*7 + 6*11, 4*2 + 6*3.5, 4*0 + 6*0, 4*3 + 6*5] = [94, 29, 0, 42] + self.assertAllClose(((94.,), (29.,), (0.,), (42.,)), + self.evaluate(predictions)) def test_old_linear_model_old_categorical(self): # Inputs. @@ -6659,7 +7089,7 @@ class EmbeddingColumnTest(test.TestCase): return zeros_embedding_values # Build columns. - categorical_column = fc_old.categorical_column_with_identity( + categorical_column = fc_old._categorical_column_with_identity( key='aaa', num_buckets=vocabulary_size) embedding_column = fc.embedding_column( categorical_column, @@ -6687,29 +7117,36 @@ class EmbeddingColumnTest(test.TestCase): embedding_weights = trainable_vars[ 'linear_model/aaa_embedding/embedding_weights:0'] linear_weights = trainable_vars['linear_model/aaa_embedding/weights:0'] - with _initialized_session(): - # Predictions with all zero weights. - self.assertAllClose(np.zeros((1,)), bias.eval()) - self.assertAllClose(zeros_embedding_values, embedding_weights.eval()) - self.assertAllClose( - np.zeros((embedding_dimension, 1)), linear_weights.eval()) - self.assertAllClose(np.zeros((batch_size, 1)), predictions.eval()) - - # Predictions with all non-zero weights. - embedding_weights.assign(( - (1., 2.), # id 0 - (3., 5.), # id 1 - (7., 11.) # id 2 - )).eval() - linear_weights.assign(((4.,), (6.,))).eval() - # example 0, ids [2], embedding[0] = [7, 11] - # example 1, ids [0, 1], embedding[1] = mean([1, 2] + [3, 5]) = [2, 3.5] - # example 2, ids [], embedding[2] = [0, 0] - # example 3, ids [1], embedding[3] = [3, 5] - # sum(embeddings * linear_weights) - # = [4*7 + 6*11, 4*2 + 6*3.5, 4*0 + 6*0, 4*3 + 6*5] = [94, 29, 0, 42] - self.assertAllClose(((94.,), (29.,), (0.,), (42.,)), predictions.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + # Predictions with all zero weights. + self.assertAllClose(np.zeros((1,)), self.evaluate(bias)) + self.assertAllClose(zeros_embedding_values, + self.evaluate(embedding_weights)) + self.assertAllClose( + np.zeros((embedding_dimension, 1)), self.evaluate(linear_weights)) + self.assertAllClose(np.zeros((batch_size, 1)), self.evaluate(predictions)) + + # Predictions with all non-zero weights. + self.evaluate( + embedding_weights.assign(( + (1., 2.), # id 0 + (3., 5.), # id 1 + (7., 11.) # id 2 + ))) + self.evaluate(linear_weights.assign(((4.,), (6.,)))) + # example 0, ids [2], embedding[0] = [7, 11] + # example 1, ids [0, 1], embedding[1] = mean([1, 2] + [3, 5]) = [2, 3.5] + # example 2, ids [], embedding[2] = [0, 0] + # example 3, ids [1], embedding[3] = [3, 5] + # sum(embeddings * linear_weights) + # = [4*7 + 6*11, 4*2 + 6*3.5, 4*0 + 6*0, 4*3 + 6*5] = [94, 29, 0, 42] + self.assertAllClose(((94.,), (29.,), (0.,), (42.,)), + self.evaluate(predictions)) + + @test_util.run_deprecated_v1 def test_serialization(self): def _initializer(shape, dtype, partition_info): @@ -6763,6 +7200,7 @@ class EmbeddingColumnTest(test.TestCase): class SharedEmbeddingColumnTest(test.TestCase): + @test_util.run_deprecated_v1 def test_defaults(self): categorical_column_a = fc.categorical_column_with_identity( key='aaa', num_buckets=3) @@ -6787,6 +7225,7 @@ class SharedEmbeddingColumnTest(test.TestCase): 'bbb': parsing_ops.VarLenFeature(dtypes.int64) }, embedding_column_b.parse_example_spec) + @test_util.run_deprecated_v1 def test_all_constructor_args(self): categorical_column_a = fc.categorical_column_with_identity( key='aaa', num_buckets=3) @@ -6818,6 +7257,7 @@ class SharedEmbeddingColumnTest(test.TestCase): 'bbb': parsing_ops.VarLenFeature(dtypes.int64) }, embedding_column_b.parse_example_spec) + @test_util.run_deprecated_v1 def test_deep_copy(self): categorical_column_a = fc.categorical_column_with_identity( key='aaa', num_buckets=3) @@ -6849,6 +7289,7 @@ class SharedEmbeddingColumnTest(test.TestCase): 'aaa': parsing_ops.VarLenFeature(dtypes.int64) }, embedding_column_a.parse_example_spec) + @test_util.run_deprecated_v1 def test_invalid_initializer(self): categorical_column_a = fc.categorical_column_with_identity( key='aaa', num_buckets=3) @@ -6860,6 +7301,7 @@ class SharedEmbeddingColumnTest(test.TestCase): dimension=2, initializer='not_fn') + @test_util.run_deprecated_v1 def test_incompatible_column_type(self): categorical_column_a = fc.categorical_column_with_identity( key='aaa', num_buckets=3) @@ -6874,6 +7316,7 @@ class SharedEmbeddingColumnTest(test.TestCase): [categorical_column_a, categorical_column_b, categorical_column_c], dimension=2) + @test_util.run_deprecated_v1 def test_weighted_categorical_column_ok(self): categorical_column_a = fc.categorical_column_with_identity( key='aaa', num_buckets=3) @@ -6891,82 +7334,90 @@ class SharedEmbeddingColumnTest(test.TestCase): [weighted_categorical_column_a, weighted_categorical_column_b], dimension=2) + @test_util.run_deprecated_v1 def test_parse_example(self): a = fc.categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=('omar', 'stringer', 'marlo')) b = fc.categorical_column_with_vocabulary_list( key='bbb', vocabulary_list=('omar', 'stringer', 'marlo')) a_embedded, b_embedded = fc.shared_embedding_columns_v2([a, b], dimension=2) - data = example_pb2.Example(features=feature_pb2.Features( - feature={ - 'aaa': - feature_pb2.Feature(bytes_list=feature_pb2.BytesList( - value=[b'omar', b'stringer'])), - 'bbb': - feature_pb2.Feature(bytes_list=feature_pb2.BytesList( - value=[b'stringer', b'marlo'])), - })) + data = example_pb2.Example( + features=feature_pb2.Features( + feature={ + 'aaa': + feature_pb2.Feature( + bytes_list=feature_pb2.BytesList( + value=[b'omar', b'stringer'])), + 'bbb': + feature_pb2.Feature( + bytes_list=feature_pb2.BytesList( + value=[b'stringer', b'marlo'])), + })) features = parsing_ops.parse_example( serialized=[data.SerializeToString()], - features=fc.make_parse_example_spec([a_embedded, b_embedded])) + features=fc.make_parse_example_spec_v2([a_embedded, b_embedded])) self.assertIn('aaa', features) self.assertIn('bbb', features) - with self.cached_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=[[0, 0], [0, 1]], - values=np.array([b'omar', b'stringer'], dtype=np.object_), - dense_shape=[1, 2]), - features['aaa'].eval()) - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=[[0, 0], [0, 1]], - values=np.array([b'stringer', b'marlo'], dtype=np.object_), - dense_shape=[1, 2]), - features['bbb'].eval()) + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=[[0, 0], [0, 1]], + values=np.array([b'omar', b'stringer'], dtype=np.object_), + dense_shape=[1, 2]), self.evaluate(features['aaa'])) + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=[[0, 0], [0, 1]], + values=np.array([b'stringer', b'marlo'], dtype=np.object_), + dense_shape=[1, 2]), self.evaluate(features['bbb'])) + + @test_util.run_deprecated_v1 def test_transform_feature(self): a = fc.categorical_column_with_identity(key='aaa', num_buckets=3) b = fc.categorical_column_with_identity(key='bbb', num_buckets=3) a_embedded, b_embedded = fc.shared_embedding_columns_v2([a, b], dimension=2) features = { - 'aaa': sparse_tensor.SparseTensor( - indices=((0, 0), (1, 0), (1, 1)), - values=(0, 1, 0), - dense_shape=(2, 2)), - 'bbb': sparse_tensor.SparseTensor( - indices=((0, 0), (1, 0), (1, 1)), - values=(1, 2, 1), - dense_shape=(2, 2)), + 'aaa': + sparse_tensor.SparseTensor( + indices=((0, 0), (1, 0), (1, 1)), + values=(0, 1, 0), + dense_shape=(2, 2)), + 'bbb': + sparse_tensor.SparseTensor( + indices=((0, 0), (1, 0), (1, 1)), + values=(1, 2, 1), + dense_shape=(2, 2)), } - outputs = fc._transform_features(features, [a, a_embedded, b, b_embedded], - None) + outputs = fc._transform_features_v2(features, + [a, a_embedded, b, b_embedded], None) output_a = outputs[a] output_a_embedded = outputs[a_embedded] output_b = outputs[b] output_b_embedded = outputs[b_embedded] - with _initialized_session(): - _assert_sparse_tensor_value( - self, output_a.eval(), output_a_embedded.eval()) - _assert_sparse_tensor_value( - self, output_b.eval(), output_b_embedded.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + _assert_sparse_tensor_value(self, self.evaluate(output_a), + self.evaluate(output_a_embedded)) + _assert_sparse_tensor_value(self, self.evaluate(output_b), + self.evaluate(output_b_embedded)) + + @test_util.run_deprecated_v1 def test_get_dense_tensor(self): # Inputs. vocabulary_size = 3 # -1 values are ignored. - input_a = np.array( - [[2, -1, -1], # example 0, ids [2] - [0, 1, -1]]) # example 1, ids [0, 1] - input_b = np.array( - [[0, -1, -1], # example 0, ids [0] - [-1, -1, -1]]) # example 1, ids [] - input_features = { - 'aaa': input_a, - 'bbb': input_b - } + input_a = np.array([ + [2, -1, -1], # example 0, ids [2] + [0, 1, -1] + ]) # example 1, ids [0, 1] + input_b = np.array([ + [0, -1, -1], # example 0, ids [0] + [-1, -1, -1] + ]) # example 1, ids [] + input_features = {'aaa': input_a, 'bbb': input_b} # Embedding variable. embedding_dimension = 2 @@ -6975,6 +7426,7 @@ class SharedEmbeddingColumnTest(test.TestCase): (3., 5.), # id 1 (7., 11.) # id 2 ) + def _initializer(shape, dtype, partition_info): self.assertAllEqual((vocabulary_size, embedding_dimension), shape) self.assertEqual(dtypes.float32, dtype) @@ -7016,21 +7468,27 @@ class SharedEmbeddingColumnTest(test.TestCase): self.assertItemsEqual(('aaa_bbb_shared_embedding:0',), tuple([v.name for v in global_vars])) embedding_var = global_vars[0] - with _initialized_session(): - self.assertAllEqual(embedding_values, embedding_var.eval()) - self.assertAllEqual(expected_lookups_a, embedding_lookup_a.eval()) - self.assertAllEqual(expected_lookups_b, embedding_lookup_b.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllEqual(embedding_values, self.evaluate(embedding_var)) + self.assertAllEqual(expected_lookups_a, self.evaluate(embedding_lookup_a)) + self.assertAllEqual(expected_lookups_b, self.evaluate(embedding_lookup_b)) + + @test_util.run_deprecated_v1 def test_get_dense_tensor_placeholder_inputs(self): # Inputs. vocabulary_size = 3 # -1 values are ignored. - input_a = np.array( - [[2, -1, -1], # example 0, ids [2] - [0, 1, -1]]) # example 1, ids [0, 1] - input_b = np.array( - [[0, -1, -1], # example 0, ids [0] - [-1, -1, -1]]) # example 1, ids [] + input_a = np.array([ + [2, -1, -1], # example 0, ids [2] + [0, 1, -1] + ]) # example 1, ids [0, 1] + input_b = np.array([ + [0, -1, -1], # example 0, ids [0] + [-1, -1, -1] + ]) # example 1, ids [] # Specify shape, because dense input must have rank specified. input_a_placeholder = array_ops.placeholder( dtype=dtypes.int64, shape=[None, 3]) @@ -7052,6 +7510,7 @@ class SharedEmbeddingColumnTest(test.TestCase): (3., 5.), # id 1 (7., 11.) # id 2 ) + def _initializer(shape, dtype, partition_info): self.assertAllEqual((vocabulary_size, embedding_dimension), shape) self.assertEqual(dtypes.float32, dtype) @@ -7077,22 +7536,26 @@ class SharedEmbeddingColumnTest(test.TestCase): with _initialized_session() as sess: sess.run([embedding_lookup_a, embedding_lookup_b], feed_dict=feed_dict) + @test_util.run_deprecated_v1 def test_linear_model(self): # Inputs. batch_size = 2 vocabulary_size = 3 # -1 values are ignored. - input_a = np.array( - [[2, -1, -1], # example 0, ids [2] - [0, 1, -1]]) # example 1, ids [0, 1] - input_b = np.array( - [[0, -1, -1], # example 0, ids [0] - [-1, -1, -1]]) # example 1, ids [] + input_a = np.array([ + [2, -1, -1], # example 0, ids [2] + [0, 1, -1] + ]) # example 1, ids [0, 1] + input_b = np.array([ + [0, -1, -1], # example 0, ids [0] + [-1, -1, -1] + ]) # example 1, ids [] # Embedding variable. embedding_dimension = 2 embedding_shape = (vocabulary_size, embedding_dimension) zeros_embedding_values = np.zeros(embedding_shape) + def _initializer(shape, dtype, partition_info): self.assertAllEqual(embedding_shape, shape) self.assertEqual(dtypes.float32, dtype) @@ -7128,8 +7591,8 @@ class SharedEmbeddingColumnTest(test.TestCase): expected_var_names, [v.name for v in ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES)]) trainable_vars = { - v.name: v for v in ops.get_collection( - ops.GraphKeys.TRAINABLE_VARIABLES) + v.name: v + for v in ops.get_collection(ops.GraphKeys.TRAINABLE_VARIABLES) } self.assertItemsEqual(expected_var_names, trainable_vars.keys()) bias = trainable_vars['linear_model/bias_weights:0'] @@ -7138,35 +7601,40 @@ class SharedEmbeddingColumnTest(test.TestCase): 'linear_model/aaa_shared_embedding/weights:0'] linear_weights_b = trainable_vars[ 'linear_model/bbb_shared_embedding/weights:0'] - with _initialized_session(): - # Predictions with all zero weights. - self.assertAllClose(np.zeros((1,)), bias.eval()) - self.assertAllClose(zeros_embedding_values, embedding_weights.eval()) - self.assertAllClose( - np.zeros((embedding_dimension, 1)), linear_weights_a.eval()) - self.assertAllClose( - np.zeros((embedding_dimension, 1)), linear_weights_b.eval()) - self.assertAllClose(np.zeros((batch_size, 1)), predictions.eval()) - - # Predictions with all non-zero weights. - embedding_weights.assign(( - (1., 2.), # id 0 - (3., 5.), # id 1 - (7., 11.) # id 2 - )).eval() - linear_weights_a.assign(((4.,), (6.,))).eval() - # example 0, ids [2], embedding[0] = [7, 11] - # example 1, ids [0, 1], embedding[1] = mean([1, 2] + [3, 5]) = [2, 3.5] - # sum(embeddings * linear_weights) - # = [4*7 + 6*11, 4*2 + 6*3.5] = [94, 29] - linear_weights_b.assign(((3.,), (5.,))).eval() - # example 0, ids [0], embedding[0] = [1, 2] - # example 1, ids [], embedding[1] = 0, 0] - # sum(embeddings * linear_weights) - # = [3*1 + 5*2, 3*0 +5*0] = [13, 0] - self.assertAllClose([[94. + 13.], [29.]], predictions.eval()) - - def _test_feature_layer(self, trainable=True): + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + # Predictions with all zero weights. + self.assertAllClose(np.zeros((1,)), self.evaluate(bias)) + self.assertAllClose(zeros_embedding_values, + self.evaluate(embedding_weights)) + self.assertAllClose( + np.zeros((embedding_dimension, 1)), self.evaluate(linear_weights_a)) + self.assertAllClose( + np.zeros((embedding_dimension, 1)), self.evaluate(linear_weights_b)) + self.assertAllClose(np.zeros((batch_size, 1)), self.evaluate(predictions)) + + # Predictions with all non-zero weights. + self.evaluate( + embedding_weights.assign(( + (1., 2.), # id 0 + (3., 5.), # id 1 + (7., 11.) # id 2 + ))) + self.evaluate(linear_weights_a.assign(((4.,), (6.,)))) + # example 0, ids [2], embedding[0] = [7, 11] + # example 1, ids [0, 1], embedding[1] = mean([1, 2] + [3, 5]) = [2, 3.5] + # sum(embeddings * linear_weights) + # = [4*7 + 6*11, 4*2 + 6*3.5] = [94, 29] + self.evaluate(linear_weights_b.assign(((3.,), (5.,)))) + # example 0, ids [0], embedding[0] = [1, 2] + # example 1, ids [], embedding[1] = 0, 0] + # sum(embeddings * linear_weights) + # = [3*1 + 5*2, 3*0 +5*0] = [13, 0] + self.assertAllClose([[94. + 13.], [29.]], self.evaluate(predictions)) + + def _test_dense_features(self, trainable=True): # Inputs. vocabulary_size = 3 sparse_input_a = sparse_tensor.SparseTensorValue( @@ -7201,6 +7669,7 @@ class SharedEmbeddingColumnTest(test.TestCase): (3., 5.), # id 1 (7., 11.) # id 2 ) + def _initializer(shape, dtype, partition_info): self.assertAllEqual((vocabulary_size, embedding_dimension), shape) self.assertEqual(dtypes.float32, dtype) @@ -7252,7 +7721,7 @@ class SharedEmbeddingColumnTest(test.TestCase): } # Provide sparse input and get dense result. - feature_layer = fc.FeatureLayer( + dense_features = fc.DenseFeatures( feature_columns=(embedding_column_b, embedding_column_a, embedding_column_c, embedding_column_d))( features) @@ -7272,16 +7741,23 @@ class SharedEmbeddingColumnTest(test.TestCase): else: self.assertItemsEqual([], tuple([v.name for v in trainable_vars])) shared_embedding_vars = global_vars - with _initialized_session(): - self.assertAllEqual(embedding_values, shared_embedding_vars[0].eval()) - self.assertAllEqual(expected_lookups, feature_layer.eval()) - def test_feature_layer(self): - self._test_feature_layer() + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllEqual(embedding_values, + self.evaluate(shared_embedding_vars[0])) + self.assertAllEqual(expected_lookups, self.evaluate(dense_features)) + + @test_util.run_deprecated_v1 + def test_dense_features(self): + self._test_dense_features() - def test_feature_layer_no_trainable(self): - self._test_feature_layer(trainable=False) + @test_util.run_deprecated_v1 + def test_dense_features_no_trainable(self): + self._test_dense_features(trainable=False) + @test_util.run_deprecated_v1 def test_serialization(self): def _initializer(shape, dtype, partition_info): @@ -7302,9 +7778,9 @@ class SharedEmbeddingColumnTest(test.TestCase): # TODO(rohanj): Add tests for (from|get)_config once implemented - class WeightedCategoricalColumnTest(test.TestCase): + @test_util.run_deprecated_v1 def test_defaults(self): column = fc.weighted_categorical_column( categorical_column=fc.categorical_column_with_identity( @@ -7320,11 +7796,12 @@ class WeightedCategoricalColumnTest(test.TestCase): def test_is_v2_column(self): column = fc.weighted_categorical_column( - categorical_column=fc_old.categorical_column_with_identity( + categorical_column=fc_old._categorical_column_with_identity( key='ids', num_buckets=3), weight_feature_key='values') self.assertFalse(column._is_v2_column) + @test_util.run_deprecated_v1 def test_deep_copy(self): """Tests deepcopy of categorical_column_with_hash_bucket.""" original = fc.weighted_categorical_column( @@ -7365,7 +7842,7 @@ class WeightedCategoricalColumnTest(test.TestCase): values=('omar', 'stringer', 'marlo'), dense_shape=(2, 2)) with self.assertRaisesRegexp(ValueError, 'Bad dtype'): - fc._transform_features({ + fc._transform_features_v2({ 'ids': strings, 'values': strings }, (column,), None) @@ -7386,77 +7863,79 @@ class WeightedCategoricalColumnTest(test.TestCase): indices=((0, 0), (1, 0), (1, 1)), values=('omar', 'stringer', 'marlo'), dense_shape=(2, 2)) - with self.assertRaisesRegexp( - ValueError, 'values is not in features dictionary'): - fc._transform_features({'ids': inputs}, (column,), None) + with self.assertRaisesRegexp(ValueError, + 'values is not in features dictionary'): + fc._transform_features_v2({'ids': inputs}, (column,), None) + @test_util.run_deprecated_v1 def test_parse_example(self): a = fc.categorical_column_with_vocabulary_list( key='aaa', vocabulary_list=('omar', 'stringer', 'marlo')) a_weighted = fc.weighted_categorical_column(a, weight_feature_key='weights') - data = example_pb2.Example(features=feature_pb2.Features( - feature={ - 'aaa': - feature_pb2.Feature(bytes_list=feature_pb2.BytesList( - value=[b'omar', b'stringer'])), - 'weights': - feature_pb2.Feature(float_list=feature_pb2.FloatList( - value=[1., 10.])) - })) + data = example_pb2.Example( + features=feature_pb2.Features( + feature={ + 'aaa': + feature_pb2.Feature( + bytes_list=feature_pb2.BytesList( + value=[b'omar', b'stringer'])), + 'weights': + feature_pb2.Feature( + float_list=feature_pb2.FloatList(value=[1., 10.])) + })) features = parsing_ops.parse_example( serialized=[data.SerializeToString()], - features=fc.make_parse_example_spec([a_weighted])) + features=fc.make_parse_example_spec_v2([a_weighted])) self.assertIn('aaa', features) self.assertIn('weights', features) - with self.cached_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=[[0, 0], [0, 1]], - values=np.array([b'omar', b'stringer'], dtype=np.object_), - dense_shape=[1, 2]), - features['aaa'].eval()) - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=[[0, 0], [0, 1]], - values=np.array([1., 10.], dtype=np.float32), - dense_shape=[1, 2]), - features['weights'].eval()) + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=[[0, 0], [0, 1]], + values=np.array([b'omar', b'stringer'], dtype=np.object_), + dense_shape=[1, 2]), self.evaluate(features['aaa'])) + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=[[0, 0], [0, 1]], + values=np.array([1., 10.], dtype=np.float32), + dense_shape=[1, 2]), self.evaluate(features['weights'])) + + @test_util.run_deprecated_v1 def test_transform_features(self): column = fc.weighted_categorical_column( categorical_column=fc.categorical_column_with_identity( key='ids', num_buckets=3), weight_feature_key='values') inputs = sparse_tensor.SparseTensorValue( - indices=((0, 0), (1, 0), (1, 1)), - values=(0, 1, 0), - dense_shape=(2, 2)) + indices=((0, 0), (1, 0), (1, 1)), values=(0, 1, 0), dense_shape=(2, 2)) weights = sparse_tensor.SparseTensorValue( indices=((0, 0), (1, 0), (1, 1)), values=(0.5, 1.0, 0.1), dense_shape=(2, 2)) - id_tensor, weight_tensor = fc._transform_features({ + id_tensor, weight_tensor = fc._transform_features_v2({ 'ids': inputs, 'values': weights, }, (column,), None)[column] - with _initialized_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=inputs.indices, - values=np.array(inputs.values, dtype=np.int64), - dense_shape=inputs.dense_shape), - id_tensor.eval()) - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=weights.indices, - values=np.array(weights.values, dtype=np.float32), - dense_shape=weights.dense_shape), - weight_tensor.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=inputs.indices, + values=np.array(inputs.values, dtype=np.int64), + dense_shape=inputs.dense_shape), self.evaluate(id_tensor)) + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=weights.indices, + values=np.array(weights.values, dtype=np.float32), + dense_shape=weights.dense_shape), self.evaluate(weight_tensor)) + + @test_util.run_deprecated_v1 def test_transform_features_dense_input(self): column = fc.weighted_categorical_column( categorical_column=fc.categorical_column_with_identity( @@ -7466,55 +7945,57 @@ class WeightedCategoricalColumnTest(test.TestCase): indices=((0, 0), (1, 0), (1, 1)), values=(0.5, 1.0, 0.1), dense_shape=(2, 2)) - id_tensor, weight_tensor = fc._transform_features({ + id_tensor, weight_tensor = fc._transform_features_v2({ 'ids': ((0, -1), (1, 0)), 'values': weights, }, (column,), None)[column] - with _initialized_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=((0, 0), (1, 0), (1, 1)), - values=np.array((0, 1, 0), dtype=np.int64), - dense_shape=(2, 2)), - id_tensor.eval()) - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=weights.indices, - values=np.array(weights.values, dtype=np.float32), - dense_shape=weights.dense_shape), - weight_tensor.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=((0, 0), (1, 0), (1, 1)), + values=np.array((0, 1, 0), dtype=np.int64), + dense_shape=(2, 2)), self.evaluate(id_tensor)) + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=weights.indices, + values=np.array(weights.values, dtype=np.float32), + dense_shape=weights.dense_shape), self.evaluate(weight_tensor)) + + @test_util.run_deprecated_v1 def test_transform_features_dense_weights(self): column = fc.weighted_categorical_column( categorical_column=fc.categorical_column_with_identity( key='ids', num_buckets=3), weight_feature_key='values') inputs = sparse_tensor.SparseTensorValue( - indices=((0, 0), (1, 0), (1, 1)), - values=(2, 1, 0), - dense_shape=(2, 2)) - id_tensor, weight_tensor = fc._transform_features({ + indices=((0, 0), (1, 0), (1, 1)), values=(2, 1, 0), dense_shape=(2, 2)) + id_tensor, weight_tensor = fc._transform_features_v2({ 'ids': inputs, 'values': ((.5, 0.), (1., .1)), }, (column,), None)[column] - with _initialized_session(): - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=inputs.indices, - values=np.array(inputs.values, dtype=np.int64), - dense_shape=inputs.dense_shape), - id_tensor.eval()) - _assert_sparse_tensor_value( - self, - sparse_tensor.SparseTensorValue( - indices=((0, 0), (1, 0), (1, 1)), - values=np.array((.5, 1., .1), dtype=np.float32), - dense_shape=(2, 2)), - weight_tensor.eval()) + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=inputs.indices, + values=np.array(inputs.values, dtype=np.int64), + dense_shape=inputs.dense_shape), self.evaluate(id_tensor)) + _assert_sparse_tensor_value( + self, + sparse_tensor.SparseTensorValue( + indices=((0, 0), (1, 0), (1, 1)), + values=np.array((.5, 1., .1), dtype=np.float32), + dense_shape=(2, 2)), self.evaluate(weight_tensor)) + + @test_util.run_deprecated_v1 def test_linear_model(self): column = fc.weighted_categorical_column( categorical_column=fc.categorical_column_with_identity( @@ -7535,15 +8016,18 @@ class WeightedCategoricalColumnTest(test.TestCase): dense_shape=(2, 2)) }) weight_var, bias = model.variables - with _initialized_session(): - self.assertAllClose((0.,), bias.eval()) - self.assertAllClose(((0.,), (0.,), (0.,)), weight_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) - weight_var.assign(((1.,), (2.,), (3.,))).eval() - # weight_var[0] * weights[0, 0] = 1 * .5 = .5 - # weight_var[2] * weights[1, 0] + weight_var[1] * weights[1, 1] - # = 3*1 + 2*.1 = 3+.2 = 3.2 - self.assertAllClose(((.5,), (3.2,)), predictions.eval()) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose((0.,), self.evaluate(bias)) + self.assertAllClose(((0.,), (0.,), (0.,)), self.evaluate(weight_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) + self.evaluate(weight_var.assign(((1.,), (2.,), (3.,)))) + # weight_var[0] * weights[0, 0] = 1 * .5 = .5 + # weight_var[2] * weights[1, 0] + weight_var[1] * weights[1, 1] + # = 3*1 + 2*.1 = 3+.2 = 3.2 + self.assertAllClose(((.5,), (3.2,)), self.evaluate(predictions)) def test_linear_model_mismatched_shape(self): column = fc.weighted_categorical_column( @@ -7589,7 +8073,7 @@ class WeightedCategoricalColumnTest(test.TestCase): rewriter_config_pb2.RewriterConfig.OFF) with _initialized_session(config): with self.assertRaisesRegexp(errors.OpError, 'Incompatible shapes'): - predictions.eval() + self.evaluate(predictions) def test_linear_model_mismatched_dense_shape(self): column = fc.weighted_categorical_column( @@ -7607,15 +8091,18 @@ class WeightedCategoricalColumnTest(test.TestCase): 'values': ((.5,), (1.,), (.1,)) }) weight_var, bias = model.variables - with _initialized_session(): - self.assertAllClose((0.,), bias.eval()) - self.assertAllClose(((0.,), (0.,), (0.,)), weight_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) - weight_var.assign(((1.,), (2.,), (3.,))).eval() - # weight_var[0] * weights[0, 0] = 1 * .5 = .5 - # weight_var[2] * weights[1, 0] + weight_var[1] * weights[1, 1] - # = 3*1 + 2*.1 = 3+.2 = 3.2 - self.assertAllClose(((.5,), (3.2,)), predictions.eval()) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose((0.,), self.evaluate(bias)) + self.assertAllClose(((0.,), (0.,), (0.,)), self.evaluate(weight_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) + self.evaluate(weight_var.assign(((1.,), (2.,), (3.,)))) + # weight_var[0] * weights[0, 0] = 1 * .5 = .5 + # weight_var[2] * weights[1, 0] + weight_var[1] * weights[1, 1] + # = 3*1 + 2*.1 = 3+.2 = 3.2 + self.assertAllClose(((.5,), (3.2,)), self.evaluate(predictions)) def test_old_linear_model(self): column = fc.weighted_categorical_column( @@ -7637,15 +8124,18 @@ class WeightedCategoricalColumnTest(test.TestCase): }, (column,)) bias = get_linear_model_bias() weight_var = get_linear_model_column_var(column) - with _initialized_session(): - self.assertAllClose((0.,), bias.eval()) - self.assertAllClose(((0.,), (0.,), (0.,)), weight_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) - weight_var.assign(((1.,), (2.,), (3.,))).eval() - # weight_var[0] * weights[0, 0] = 1 * .5 = .5 - # weight_var[2] * weights[1, 0] + weight_var[1] * weights[1, 1] - # = 3*1 + 2*.1 = 3+.2 = 3.2 - self.assertAllClose(((.5,), (3.2,)), predictions.eval()) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose((0.,), self.evaluate(bias)) + self.assertAllClose(((0.,), (0.,), (0.,)), self.evaluate(weight_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) + self.evaluate(weight_var.assign(((1.,), (2.,), (3.,)))) + # weight_var[0] * weights[0, 0] = 1 * .5 = .5 + # weight_var[2] * weights[1, 0] + weight_var[1] * weights[1, 1] + # = 3*1 + 2*.1 = 3+.2 = 3.2 + self.assertAllClose(((.5,), (3.2,)), self.evaluate(predictions)) def test_old_linear_model_mismatched_shape(self): column = fc.weighted_categorical_column( @@ -7690,7 +8180,7 @@ class WeightedCategoricalColumnTest(test.TestCase): rewriter_config_pb2.RewriterConfig.OFF) with _initialized_session(config): with self.assertRaisesRegexp(errors.OpError, 'Incompatible shapes'): - predictions.eval() + self.evaluate(predictions) def test_old_linear_model_mismatched_dense_shape(self): column = fc.weighted_categorical_column( @@ -7708,19 +8198,22 @@ class WeightedCategoricalColumnTest(test.TestCase): }, (column,)) bias = get_linear_model_bias() weight_var = get_linear_model_column_var(column) - with _initialized_session(): - self.assertAllClose((0.,), bias.eval()) - self.assertAllClose(((0.,), (0.,), (0.,)), weight_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) - weight_var.assign(((1.,), (2.,), (3.,))).eval() - # weight_var[0] * weights[0, 0] = 1 * .5 = .5 - # weight_var[2] * weights[1, 0] + weight_var[1] * weights[1, 1] - # = 3*1 + 2*.1 = 3+.2 = 3.2 - self.assertAllClose(((.5,), (3.2,)), predictions.eval()) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose((0.,), self.evaluate(bias)) + self.assertAllClose(((0.,), (0.,), (0.,)), self.evaluate(weight_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) + self.evaluate(weight_var.assign(((1.,), (2.,), (3.,)))) + # weight_var[0] * weights[0, 0] = 1 * .5 = .5 + # weight_var[2] * weights[1, 0] + weight_var[1] * weights[1, 1] + # = 3*1 + 2*.1 = 3+.2 = 3.2 + self.assertAllClose(((.5,), (3.2,)), self.evaluate(predictions)) def test_old_linear_model_old_categorical(self): column = fc.weighted_categorical_column( - categorical_column=fc_old.categorical_column_with_identity( + categorical_column=fc_old._categorical_column_with_identity( key='ids', num_buckets=3), weight_feature_key='values') with ops.Graph().as_default(): @@ -7738,18 +8231,22 @@ class WeightedCategoricalColumnTest(test.TestCase): }, (column,)) bias = get_linear_model_bias() weight_var = get_linear_model_column_var(column) - with _initialized_session(): - self.assertAllClose((0.,), bias.eval()) - self.assertAllClose(((0.,), (0.,), (0.,)), weight_var.eval()) - self.assertAllClose(((0.,), (0.,)), predictions.eval()) - weight_var.assign(((1.,), (2.,), (3.,))).eval() - # weight_var[0] * weights[0, 0] = 1 * .5 = .5 - # weight_var[2] * weights[1, 0] + weight_var[1] * weights[1, 1] - # = 3*1 + 2*.1 = 3+.2 = 3.2 - self.assertAllClose(((.5,), (3.2,)), predictions.eval()) + + self.evaluate(variables_lib.global_variables_initializer()) + self.evaluate(lookup_ops.tables_initializer()) + + self.assertAllClose((0.,), self.evaluate(bias)) + self.assertAllClose(((0.,), (0.,), (0.,)), self.evaluate(weight_var)) + self.assertAllClose(((0.,), (0.,)), self.evaluate(predictions)) + self.evaluate(weight_var.assign(((1.,), (2.,), (3.,)))) + # weight_var[0] * weights[0, 0] = 1 * .5 = .5 + # weight_var[2] * weights[1, 0] + weight_var[1] * weights[1, 1] + # = 3*1 + 2*.1 = 3+.2 = 3.2 + self.assertAllClose(((.5,), (3.2,)), self.evaluate(predictions)) # TODO(ptucker): Add test with embedding of weighted categorical. + @test_util.run_deprecated_v1 def test_serialization(self): categorical_column = fc.categorical_column_with_identity( key='ids', num_buckets=3) diff --git a/tensorflow/python/framework/auto_control_deps.py b/tensorflow/python/framework/auto_control_deps.py index 9a9ee46aab..30dc959e9a 100644 --- a/tensorflow/python/framework/auto_control_deps.py +++ b/tensorflow/python/framework/auto_control_deps.py @@ -21,9 +21,11 @@ from __future__ import print_function from tensorflow.python.eager import context from tensorflow.python.framework import dtypes as dtypes_module from tensorflow.python.framework import ops +from tensorflow.python.framework import sparse_tensor from tensorflow.python.ops import array_ops from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import control_flow_util +from tensorflow.python.ops import tensor_array_ops from tensorflow.python.util import nest from tensorflow.python.util import tf_decorator @@ -70,6 +72,17 @@ class AutomaticControlDependencies(object): self._returned_tensors.add(indices) self._returned_tensors.add(values) return ops.IndexedSlices(values, indices, dense_shape=tensor.dense_shape) + elif isinstance(tensor, sparse_tensor.SparseTensor): + values = array_ops.identity(tensor.values) + indices = array_ops.identity(tensor.indices) + self._returned_tensors.add(indices) + self._returned_tensors.add(values) + return sparse_tensor.SparseTensor( + indices, values, dense_shape=tensor.dense_shape) + elif isinstance(tensor, tensor_array_ops.TensorArray): + flow = array_ops.identity(tensor.flow) + self._returned_tensors.add(flow) + return tensor_array_ops.build_ta_with_new_flow(tensor, flow) # We want to make the return values depend on the stateful operations, but # we don't want to introduce a cycle, so we make the return value the result # of a new identity operation that the stateful operations definitely don't diff --git a/tensorflow/python/framework/constant_op.py b/tensorflow/python/framework/constant_op.py index 53d84b2dc7..ade0797dcd 100644 --- a/tensorflow/python/framework/constant_op.py +++ b/tensorflow/python/framework/constant_op.py @@ -114,8 +114,9 @@ def convert_to_eager_tensor(value, ctx, dtype=None): return ops.EagerTensor(value, handle, device, dtype) -@tf_export("constant") -def constant(value, dtype=None, shape=None, name="Const", verify_shape=False): +@tf_export(v1=["constant"]) +def constant_v1( + value, dtype=None, shape=None, name="Const", verify_shape=False): """Creates a constant tensor. The resulting tensor is populated with values of type `dtype`, as @@ -174,6 +175,79 @@ def constant(value, dtype=None, shape=None, name="Const", verify_shape=False): Raises: TypeError: if shape is incorrectly specified or unsupported. """ + return _constant_impl(value, dtype, shape, name, verify_shape=verify_shape, + allow_broadcast=False) + + +@tf_export("constant", v1=[]) +def constant(value, dtype=None, shape=None, name="Const"): + """Creates a constant tensor. + + The resulting tensor is populated with values of type `dtype`, as + specified by arguments `value` and (optionally) `shape` (see examples + below). + + The argument `value` can be a constant value, or a list of values of type + `dtype`. If `value` is a list, then the length of the list must be less + than or equal to the number of elements implied by the `shape` argument (if + specified). In the case where the list length is less than the number of + elements specified by `shape`, the last element in the list will be used + to fill the remaining entries. + + The argument `shape` is optional. If present, it specifies the dimensions of + the resulting tensor. If not present, the shape of `value` is used. + + If the argument `dtype` is not specified, then the type is inferred from + the type of `value`. + + For example: + + ```python + # Constant 1-D Tensor populated with value list. + tensor = tf.constant([1, 2, 3, 4, 5, 6]) => [1 2 3 4 5 6] + + # Constant 1-D Tensor populated with value list. + tensor = tf.constant([1, 2, 3, 4, 5, 6], shape=(2,3)) + => [[1 2 3], [4 5 6]] + + # Constant 2-D tensor populated with scalar value -1. + tensor = tf.constant(-1.0, shape=[2, 3]) => [[-1. -1. -1.] + [-1. -1. -1.]] + ``` + + `tf.constant` differs from `tf.fill` in a few ways: + + * `tf.constant` supports arbitrary constants, not just uniform scalar + Tensors like `tf.fill`. + * `tf.constant` creates a `Const` node in the computation graph with the + exact value at graph construction time. On the other hand, `tf.fill` + creates an Op in the graph that is expanded at runtime. + * Because `tf.constant` only embeds constant values in the graph, it does + not support dynamic shapes based on other runtime Tensors, whereas + `tf.fill` does. + + Args: + value: A constant value (or list) of output type `dtype`. + + dtype: The type of the elements of the resulting tensor. + + shape: Optional dimensions of resulting tensor. + + name: Optional name for the tensor. + + Returns: + A Constant Tensor. + + Raises: + TypeError: if shape is incorrectly specified or unsupported. + """ + return _constant_impl(value, dtype, shape, name, verify_shape=False, + allow_broadcast=True) + + +def _constant_impl( + value, dtype, shape, name, verify_shape, allow_broadcast): + """Implementation of constant.""" ctx = context.context() if ctx.executing_eagerly(): t = convert_to_eager_tensor(value, ctx, dtype) @@ -205,7 +279,8 @@ def constant(value, dtype=None, shape=None, name="Const", verify_shape=False): tensor_value = attr_value_pb2.AttrValue() tensor_value.tensor.CopyFrom( tensor_util.make_tensor_proto( - value, dtype=dtype, shape=shape, verify_shape=verify_shape)) + value, dtype=dtype, shape=shape, verify_shape=verify_shape, + allow_broadcast=allow_broadcast)) dtype_value = attr_value_pb2.AttrValue(type=tensor_value.tensor.dtype) const_tensor = g.create_op( "Const", [], [dtype_value.type], diff --git a/tensorflow/python/framework/device.py b/tensorflow/python/framework/device.py index 7f6e0a75a5..e7ac6444a4 100644 --- a/tensorflow/python/framework/device.py +++ b/tensorflow/python/framework/device.py @@ -23,7 +23,7 @@ import threading from tensorflow.python.util.tf_export import tf_export -@tf_export("DeviceSpec") +@tf_export(v1=["DeviceSpec"]) class DeviceSpec(object): """Represents a (possibly partial) specification for a TensorFlow device. diff --git a/tensorflow/python/framework/dtypes.py b/tensorflow/python/framework/dtypes.py index 48e9f0524e..f7a12d27df 100644 --- a/tensorflow/python/framework/dtypes.py +++ b/tensorflow/python/framework/dtypes.py @@ -18,6 +18,7 @@ from __future__ import division from __future__ import print_function import numpy as np +from six.moves import builtins from tensorflow.core.framework import types_pb2 from tensorflow.python import pywrap_tensorflow @@ -548,8 +549,8 @@ _NP_TO_TF = frozenset([ (np.int8, int8), (np.complex64, complex64), (np.complex128, complex128), - (np.object, string), - (np.bool, bool), + (np.object_, string), + (np.bool_, bool), (_np_qint8, qint8), (_np_quint8, quint8), (_np_qint16, qint16), @@ -658,8 +659,9 @@ tf_export( __name__, "QUANTIZED_DTYPES") _PYTHON_TO_TF = { - float: float32, - bool: bool, + builtins.float: float32, + builtins.bool: bool, + builtins.object: string } diff --git a/tensorflow/python/framework/dtypes_test.py b/tensorflow/python/framework/dtypes_test.py index a873670e04..719fdc0953 100644 --- a/tensorflow/python/framework/dtypes_test.py +++ b/tensorflow/python/framework/dtypes_test.py @@ -81,10 +81,10 @@ class TypesTest(test_util.TensorFlowTestCase): self.assertIs(dtypes.int8, dtypes.as_dtype(np.int8)) self.assertIs(dtypes.complex64, dtypes.as_dtype(np.complex64)) self.assertIs(dtypes.complex128, dtypes.as_dtype(np.complex128)) - self.assertIs(dtypes.string, dtypes.as_dtype(np.object)) + self.assertIs(dtypes.string, dtypes.as_dtype(np.object_)) self.assertIs(dtypes.string, dtypes.as_dtype(np.array(["foo", "bar"]).dtype)) - self.assertIs(dtypes.bool, dtypes.as_dtype(np.bool)) + self.assertIs(dtypes.bool, dtypes.as_dtype(np.bool_)) with self.assertRaises(TypeError): dtypes.as_dtype(np.dtype([("f1", np.uint), ("f2", np.int32)])) diff --git a/tensorflow/python/framework/file_system_test.py b/tensorflow/python/framework/file_system_test.py index 6901715e5d..8687bc5a78 100644 --- a/tensorflow/python/framework/file_system_test.py +++ b/tensorflow/python/framework/file_system_test.py @@ -21,6 +21,7 @@ from __future__ import print_function import os from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.framework import load_library from tensorflow.python.ops import data_flow_ops from tensorflow.python.ops import io_ops @@ -36,13 +37,14 @@ class FileSystemTest(test.TestCase): "test_file_system.so") load_library.load_file_system_library(file_system_library) + @test_util.run_deprecated_v1 def testBasic(self): with self.cached_session() as sess: reader = io_ops.WholeFileReader("test_reader") queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) queue.enqueue_many([["test://foo"]]).run() queue.close().run() - key, value = sess.run(reader.read(queue)) + key, value = self.evaluate(reader.read(queue)) self.assertEqual(key, compat.as_bytes("test://foo")) self.assertEqual(value, compat.as_bytes("AAAAAAAAAA")) diff --git a/tensorflow/python/framework/func_graph.py b/tensorflow/python/framework/func_graph.py index c7a5d1ee20..f74d072e8e 100644 --- a/tensorflow/python/framework/func_graph.py +++ b/tensorflow/python/framework/func_graph.py @@ -26,11 +26,13 @@ from tensorflow.python.eager import context from tensorflow.python.eager import tape from tensorflow.python.eager.graph_only_ops import graph_placeholder from tensorflow.python.framework import ops +from tensorflow.python.framework import sparse_tensor from tensorflow.python.framework import tensor_spec from tensorflow.python.framework.auto_control_deps import AutomaticControlDependencies from tensorflow.python.ops import array_ops from tensorflow.python.ops import custom_gradient from tensorflow.python.ops import resource_variable_ops +from tensorflow.python.ops import tensor_array_ops from tensorflow.python.ops import variable_scope from tensorflow.python.util import compat from tensorflow.python.util import nest @@ -111,7 +113,7 @@ class FuncGraph(ops.Graph): # this stack from the default graph even in eager mode. Maybe it should be # part of the eager context? This would also allow us to remove a # get_default_graph() call from the function cache lookup. - self._distribution_strategy_stack = graph._distribution_strategy_stack + self._distribution_strategy_stack = list(graph._distribution_strategy_stack) # We ignore device placements from any outer scopes while tracing the # function when possible, to avoid hard-coding them in the function # graph. "Default" placements come from the PartitionedCallOp's placement, @@ -372,7 +374,7 @@ def func_graph_from_py_func(name, # captured Operations). with ops.control_dependencies([x]): x = array_ops.identity(op_return_value) - else: + elif not isinstance(x, tensor_array_ops.TensorArray): try: x = ops.convert_to_tensor_or_indexed_slices(x) except (ValueError, TypeError): @@ -395,9 +397,9 @@ def func_graph_from_py_func(name, return autograph.converted_call( original_func, None, autograph.ConversionOptions( - verbose=True, + verbose=autograph.Verbosity.BRIEF, recursive=True, - strip_decorators=(function.defun, def_function.function), + strip_decorators=(def_function.function,), optional_features=(), ), *args, **kwargs) @@ -408,7 +410,8 @@ def func_graph_from_py_func(name, func_outputs = python_func(*func_args, **func_kwargs) - # invariant: `func_outputs` contains only Tensors and `None`s. + # invariant: `func_outputs` contains only Tensors, IndexedSlices, + # SparseTensors, TensorArrays and `None`s. func_outputs = nest.map_structure(convert, func_outputs) check_mutation(func_args_before, func_args) @@ -495,7 +498,17 @@ def check_mutation(n1, n2): def flatten(sequence): - """A wrapper around `nest.flatten` that also unpacks `IndexedSlices`.""" + """Like `nest.flatten` but also unpacks other Tensor-like objects. + + Flattens non-tensor objects into their constituent tensors. + + Args: + sequence: A nested structure of Tensors, IndexedSlices, SparseTensors and + TensorArrays. + + Returns: + A list of tensors. + """ # TODO(akshayka): Support `SparseTensor` in a similar fashion. flat_sequence = nest.flatten(sequence) outputs = [] @@ -505,11 +518,58 @@ def flatten(sequence): outputs.extend([item.values, item.indices, item.dense_shape]) else: outputs.extend([item.values, item.indices]) + elif isinstance(item, sparse_tensor.SparseTensor): + outputs.extend([item.indices, item.values, item.dense_shape]) + elif isinstance(item, tensor_array_ops.TensorArray): + outputs.append(item.flow) else: outputs.append(item) return outputs +def pack_sequence_as(structure, flat_sequence): + """Like `nest.pack_sequence_as` but also packs other Tensor-like objects. + + Args: + structure: The structure to pack into. May contain Tensors, IndexedSlices, + TensorArrays or SparseTensors. + flat_sequence: An iterable containing tensors. + + Returns: + A nested structure. + + Raises: + AssertionError if `structure` and `flat_sequence` are not compatible. + """ + flattened_structure = nest.flatten(structure) + flat_sequence_with_slices_and_tas = [] + index = 0 + for t in flattened_structure: + if isinstance(t, ops.IndexedSlices): + if t.dense_shape is not None: + flat_sequence_with_slices_and_tas.append( + ops.IndexedSlices(*flat_sequence[index:index + 3])) + index += 3 + else: + flat_sequence_with_slices_and_tas.append( + ops.IndexedSlices(*flat_sequence[index:index + 2])) + index += 2 + elif isinstance(t, sparse_tensor.SparseTensor): + flat_sequence_with_slices_and_tas.append( + sparse_tensor.SparseTensor(*flat_sequence[index:index + 3])) + index += 3 + elif isinstance(t, tensor_array_ops.TensorArray): + flow = flat_sequence[index] + ta = tensor_array_ops.build_ta_with_new_flow(t, flow) + flat_sequence_with_slices_and_tas.append(ta) + index += 1 + else: + flat_sequence_with_slices_and_tas.append(flat_sequence[index]) + index += 1 + assert len(flattened_structure) == len(flat_sequence_with_slices_and_tas) + return nest.pack_sequence_as(structure, flat_sequence_with_slices_and_tas) + + def _create_substitute_placeholder(value, name=None, dtype=None): """Creates a placeholder for `value` and propagates shape info to it.""" # Note: setting ops.control_dependencies(None) ensures we always put diff --git a/tensorflow/python/framework/function.py b/tensorflow/python/framework/function.py index 230a554641..622686ce00 100644 --- a/tensorflow/python/framework/function.py +++ b/tensorflow/python/framework/function.py @@ -874,7 +874,7 @@ def func_graph_from_py_func(func, arg_names, arg_types, name=None, # If func only returned one value, make it a tuple. if not isinstance(outputs, (list, tuple)): outputs = (outputs,) - if any([_ is None for _ in outputs]): + if any(_ is None for _ in outputs): raise ValueError("Function %s can not return None." % name) # Ensures each output is a Tensor in the function graph. outputs = [ops.convert_to_tensor(t) for t in outputs] @@ -1190,7 +1190,7 @@ def get_extra_args(): def _type_list_to_str(types): - if any([_ not in _DTYPE_TO_STR for _ in types]): + if any(_ not in _DTYPE_TO_STR for _ in types): raise ValueError("Unsupported dtypes: %s" % types) return "".join([_DTYPE_TO_STR[_] for _ in types]) diff --git a/tensorflow/python/framework/function_def_to_graph_test.py b/tensorflow/python/framework/function_def_to_graph_test.py index b2ef64f873..ddf1a6e74d 100644 --- a/tensorflow/python/framework/function_def_to_graph_test.py +++ b/tensorflow/python/framework/function_def_to_graph_test.py @@ -25,6 +25,7 @@ from tensorflow.python.framework import function_def_to_graph from tensorflow.python.framework import graph_to_function_def from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util from tensorflow.python.framework import test_ops from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops @@ -52,6 +53,7 @@ class FunctionDefToGraphTest(test.TestCase): fdef.signature.name = "_whats_in_a_name" return fdef + @test_util.run_deprecated_v1 def testInputsAndOutputs(self): fdef = self._build_function_def() g = function_def_to_graph.function_def_to_graph(fdef) @@ -186,6 +188,7 @@ class FunctionDefToGraphDefTest(test.TestCase): self.assertEqual(g.node[0].attr["shape"].shape.unknown_rank, False) self.assertFalse("shape" in g.node[2].attr) + @test_util.run_deprecated_v1 def testFunctionCallsFromFunction(self): x = constant_op.constant(5.0) y = constant_op.constant(10.0) diff --git a/tensorflow/python/framework/function_test.py b/tensorflow/python/framework/function_test.py index 13ee6c5d2d..d71f06ea52 100644 --- a/tensorflow/python/framework/function_test.py +++ b/tensorflow/python/framework/function_test.py @@ -35,6 +35,7 @@ from tensorflow.python.framework import function from tensorflow.python.framework import graph_to_function_def from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util from tensorflow.python.framework.errors import InvalidArgumentError from tensorflow.python.ops import array_ops from tensorflow.python.ops import control_flow_ops @@ -102,8 +103,9 @@ class FunctionTest(test.TestCase): call = MyIdentityFunc([18.0]) self.assertEqual("MyIdentity", call.op.name) with session.Session() as sess: - self.assertAllEqual([18.0], sess.run(call)) + self.assertAllEqual([18.0], self.evaluate(call)) + @test_util.run_deprecated_v1 def testIdentityImplicitDeref(self): @function.Defun(dtypes.float32, func_name="MyIdentity") @@ -116,8 +118,8 @@ class FunctionTest(test.TestCase): self.assertEqual("MyIdentity", call.op.name) for cfg in _OptimizerOptions(): with session.Session(config=cfg) as sess: - sess.run(var.initializer) - self.assertAllEqual([18.0], sess.run(call)) + self.evaluate(var.initializer) + self.assertAllEqual([18.0], self.evaluate(call)) def testIdentityOutputName(self): @@ -130,7 +132,7 @@ class FunctionTest(test.TestCase): call = MyIdentityFunc([18.0]) self.assertEqual("MyIdentity", call.op.name) with session.Session() as sess: - self.assertAllEqual([18.0], sess.run(call)) + self.assertAllEqual([18.0], self.evaluate(call)) def testTooManyOutputNames(self): @@ -158,7 +160,7 @@ class FunctionTest(test.TestCase): call = APlus2B([1.0], [2.0]) self.assertEqual("APlus2B", call.op.name) with session.Session() as sess: - self.assertAllEqual([5.0], sess.run(call)) + self.assertAllEqual([5.0], self.evaluate(call)) def testFunctionWithNoOutput(self): @@ -187,7 +189,7 @@ class FunctionTest(test.TestCase): call = APlus2B([1.0], [2.0]) self.assertEqual("APlus2B", call.op.name) with session.Session() as sess: - self.assertAllEqual([5.0], sess.run(call)) + self.assertAllEqual([5.0], self.evaluate(call)) def testDefineFunctionDuplicateOutputs(self): @@ -224,8 +226,8 @@ class FunctionTest(test.TestCase): call_g = XSquarePlusOneGrad([2.0], [0.1]) with session.Session() as sess: - self.assertAllClose([5.0], sess.run(call_f)) - self.assertAllClose([0.4], sess.run(call_g)) + self.assertAllClose([5.0], self.evaluate(call_f)) + self.assertAllClose([0.4], self.evaluate(call_g)) def testTanhSymGrad(self): @@ -322,6 +324,7 @@ class FunctionTest(test.TestCase): self.assertEqual(x.get_shape(), dx.get_shape()) self.assertEqual(y.get_shape(), dy.get_shape()) + @test_util.run_deprecated_v1 def testSymGradAttr(self): @function.Defun(noinline=True) @@ -365,7 +368,7 @@ class FunctionTest(test.TestCase): else: dx, dy = gradients_impl.gradients([z], [x, y]) with session.Session() as sess: - dx_val, dy_val = sess.run([dx, dy]) + dx_val, dy_val = self.evaluate([dx, dy]) self.assertEqual([2.0], dx_val) self.assertEqual([0.0], dy_val) @@ -387,7 +390,7 @@ class FunctionTest(test.TestCase): call = AConstant() self.assertEqual("AConstant", call.op.name) with session.Session() as sess: - self.assertAllEqual([42], sess.run(call)) + self.assertAllEqual([42], self.evaluate(call)) def testDefineFunctionNames(self): @@ -438,6 +441,7 @@ class FunctionTest(test.TestCase): "assertion failed.*-3"): self.assertAllEqual(Foo(constant_op.constant(-3.0)).eval(), 6.0) + @test_util.run_deprecated_v1 def testAssertWrapper(self): @function.Defun(dtypes.float32) @@ -452,6 +456,7 @@ class FunctionTest(test.TestCase): "assertion"): _ = MyFn(100.0).eval() + @test_util.run_deprecated_v1 def testWhileLoopCallsFunc(self): with self.session(use_gpu=True) as sess: @@ -468,9 +473,10 @@ class FunctionTest(test.TestCase): loop = control_flow_ops.while_loop(lambda x: x < 1e5, Body, [1.0]) - ans = sess.run(loop) + ans = self.evaluate(loop) self.assertAllClose(ans, 131072.) + @test_util.run_deprecated_v1 def testControlFlowStrictness(self): """Inlined functions must not execute in a untaken control flow branch.""" @@ -517,6 +523,7 @@ class FunctionTest(test.TestCase): "assertion"): sess.run(loop, {pred: True, x: 3}) + @test_util.run_deprecated_v1 def testVar(self): @function.Defun(dtypes.float32) @@ -532,6 +539,7 @@ class FunctionTest(test.TestCase): variables.global_variables_initializer().run() self.assertAllEqual(z.eval(), 101.) + @test_util.run_deprecated_v1 def testResourceVarAsImplicitInput(self): g = ops.Graph() with g.as_default(), ops.device("cpu:0"): @@ -552,8 +560,8 @@ class FunctionTest(test.TestCase): with self.session(graph=g): v.initializer.run() - self.assertAllEqual(expected_val.eval(), actual_val.eval()) - self.assertAllEqual(expected_shape, actual_shape.eval()) + self.assertAllEqual(expected_val.eval(), self.evaluate(actual_val)) + self.assertAllEqual(expected_shape, self.evaluate(actual_shape)) def testDefineErrors(self): with ops.Graph().as_default(): @@ -650,8 +658,8 @@ class FunctionTest(test.TestCase): # pylint: enable=unexpected-keyword-arg self.assertEqual("next", call2.op.name) with session.Session() as sess: - self.assertAllEqual([1], sess.run(call1)) - self.assertAllEqual([0], sess.run(call2)) + self.assertAllEqual([1], self.evaluate(call1)) + self.assertAllEqual([0], self.evaluate(call2)) def testNestedFunction(self): @@ -707,6 +715,7 @@ class FunctionTest(test.TestCase): gdef = g.as_graph_def() self.assertEqual(0, len(gdef.library.function)) + @test_util.run_deprecated_v1 def testReduction(self): g = ops.Graph() @@ -735,6 +744,7 @@ class FunctionTest(test.TestCase): self.assertAllClose(vals[0], vals[1]) self.assertAllClose(vals[2], vals[3]) + @test_util.run_deprecated_v1 def testCapture(self): g = ops.Graph() with g.as_default(): @@ -781,6 +791,7 @@ class FunctionTest(test.TestCase): # NOTE: We still do not support capturing control deps. _ = Foo(x) + @test_util.run_deprecated_v1 def testCaptureInWhileLoop(self): g = ops.Graph() with g.as_default(): @@ -794,8 +805,9 @@ class FunctionTest(test.TestCase): y = Foo() with self.session(graph=g) as sess: - self.assertEqual(sess.run(y), 10) + self.assertEqual(self.evaluate(y), 10) + @test_util.run_deprecated_v1 def testCaptureInCond(self): g = ops.Graph() with g.as_default(): @@ -809,8 +821,8 @@ class FunctionTest(test.TestCase): z = Foo(False) with self.session(graph=g) as sess: - self.assertEqual(sess.run(y), 1) - self.assertEqual(sess.run(z), 2) + self.assertEqual(self.evaluate(y), 1) + self.assertEqual(self.evaluate(z), 2) def testStableName(self): @@ -825,6 +837,7 @@ class FunctionTest(test.TestCase): self.assertEqual("Foo_aCYSbwBkR5A", Foo.instantiate([dtypes.float32] * 3).name) + @test_util.run_deprecated_v1 def testSignatureHash(self): # Foo.Inner and Bar.Inner have identical function body but have # different signatures. They should be treated as two different functions. @@ -854,7 +867,7 @@ class FunctionTest(test.TestCase): z = Bar(x) with self.session(graph=g) as sess: - v0, v1 = sess.run([y, z]) + v0, v1 = self.evaluate([y, z]) self.assertAllEqual(v0, 20.) self.assertAllEqual(v1, 20.) @@ -877,6 +890,7 @@ class FunctionTest(test.TestCase): y = Bar(array_ops.zeros([1, 2, 3])) self.assertAllEqual(y.get_shape().as_list(), [1, 1, 2, 3]) + @test_util.run_deprecated_v1 def testVariableReuse(self): def LinearWithReuse(input_tensor, reuse=None): @@ -900,11 +914,12 @@ class FunctionTest(test.TestCase): self.assertEqual(global_vars[0].name, "linear/w:0") with session.Session() as sess: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) output_val = sess.run( output_op, feed_dict={input_op: np.random.rand(32, 100)}) self.assertEqual(output_val.shape, (32, 100)) + @test_util.run_deprecated_v1 def testFunctionCallInDifferentVariableScopes(self): @function.Defun(dtypes.float32) @@ -928,7 +943,7 @@ class FunctionTest(test.TestCase): self.assertEqual(global_vars[0].name, "vs1/var:0") with session.Session() as sess: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) out1, out2 = sess.run( [out1_op, out2_op], feed_dict={input_op: np.linspace(1, 10, 10)}) self.assertAllEqual(out1, np.linspace(2, 11, 10)) @@ -968,6 +983,7 @@ class FunctionTest(test.TestCase): self.assertAllClose( np.array([1.0, 0.0]).astype(np.float32), sess.run(dinp, {inp: x})) + @test_util.run_deprecated_v1 def testFunctionMarkedStateful(self): @function.Defun(dtypes.int32, dtypes.float32) @@ -991,10 +1007,11 @@ class FunctionTest(test.TestCase): result_2 = Bar(constant_op.constant(100, dtype=dtypes.int64)) with session.Session() as sess: - self.assertEqual(4.0, sess.run(result_1)) - self.assertEqual(100, sess.run(result_2)) + self.assertEqual(4.0, self.evaluate(result_1)) + self.assertEqual(100, self.evaluate(result_2)) self.assertEqual((4.0, 100), sess.run((result_1, result_2))) + @test_util.run_deprecated_v1 def testStatefulFunction(self): @function.Defun() @@ -1037,6 +1054,7 @@ class FunctionTest(test.TestCase): self.assertFalse(all(val3 == val1)) self.assertFalse(all(val4 == val2)) + @test_util.run_deprecated_v1 def testSameFunctionOnTwoDevices(self): @function.Defun(dtypes.float32) @@ -1052,10 +1070,11 @@ class FunctionTest(test.TestCase): for config in _OptimizerOptions(): config.device_count["CPU"] = 2 with session.Session(config=config) as sess: - self.assertEqual(42.0, sess.run(f_0)) - self.assertEqual(44.0, sess.run(f_1)) + self.assertEqual(42.0, self.evaluate(f_0)) + self.assertEqual(44.0, self.evaluate(f_1)) self.assertEqual((42.0, 44.0), sess.run((f_0, f_1))) + @test_util.run_deprecated_v1 def testGuaranteedConstsAreCaptured(self): var = variables.Variable(1.0) const = array_ops.guarantee_const(var) @@ -1076,9 +1095,10 @@ class FunctionTest(test.TestCase): return output with self.session(use_gpu=False) as sess: - sess.run(var.initializer) + self.evaluate(var.initializer) _ = sess.run(CapturesGuaranteedConst(), {also_not_const: 1.0}) + @test_util.run_deprecated_v1 def testSameFunctionDifferentGrads(self): def PartOne(x): @@ -1127,7 +1147,7 @@ class FunctionTest(test.TestCase): dx2, = gradients_impl.gradients(ys=[y2], xs=[x2]) with self.session(graph=g) as sess: - v0, v1, v2 = sess.run([dx0, dx1, dx2]) + v0, v1, v2 = self.evaluate([dx0, dx1, dx2]) self.assertAllEqual(v0, 2.) self.assertAllEqual(v1, 101.) @@ -1150,6 +1170,7 @@ class FunctionsFromProtos(test.TestCase): self.assertEqual(func.declared_input_types, new_func.declared_input_types) self.assertEqual(func.captured_inputs, new_func.captured_inputs) + @test_util.run_deprecated_v1 def testBasic(self): @function.Defun(dtypes.float32, dtypes.float32) @@ -1359,6 +1380,7 @@ class FunctionsFromProtos(test.TestCase): class FunctionOverloadTest(test.TestCase): + @test_util.run_deprecated_v1 def testBasic(self): @function.Defun() @@ -1411,6 +1433,7 @@ class FunctionOverloadTest(test.TestCase): class FunctionCaptureByValueTest(test.TestCase): + @test_util.run_deprecated_v1 def testCaptureByValue(self): g = ops.Graph() with g.as_default(): @@ -1532,7 +1555,7 @@ class UnrollLSTMTest(test.TestCase): tf_logging.info("time: %f txt size: %d gdef bin size: %d", finish - start, len(str(gdef)), len(gdef.SerializeToString())) with g.as_default(), session.Session(config=cfg) as sess: - return sess.run(m) + return self.evaluate(m) mv0 = RunForward("complete") for cfg in _OptimizerOptions(): @@ -1561,7 +1584,7 @@ class UnrollLSTMTest(test.TestCase): tf_logging.info("time: %f txt size: %d gdef bin size: %d", finish - start, len(str(gdef)), len(gdef.SerializeToString())) with g.as_default(), session.Session(config=cfg) as sess: - return sess.run(dw) + return self.evaluate(dw) d0 = RunForwardBackward("complete") for cfg in _OptimizerOptions(): @@ -1634,6 +1657,7 @@ class FunctionInlineControlTest(test.TestCase): class ModuleFunctionTest(test.TestCase): + @test_util.run_deprecated_v1 def testBasic(self): @function.Defun(*[dtypes.float32] * 3) @@ -1651,8 +1675,8 @@ class ModuleFunctionTest(test.TestCase): y = LinearWithCApi(a, b, c) z = Linear2WithCApi(a, b, c, d, e) with session.Session() as sess: - self.assertAllEqual([[1]], sess.run(y)) - self.assertAllEqual([[5]], sess.run(z)) + self.assertAllEqual([[1]], self.evaluate(y)) + self.assertAllEqual([[5]], self.evaluate(z)) class VariableHoistingTest(test.TestCase): @@ -1704,8 +1728,8 @@ class VariableHoistingTest(test.TestCase): self.assertEqual("Foo/b", b.op.name) with self.session(graph=g) as sess: - sess.run(variables.global_variables_initializer()) - w, b, x, y0, loss, dw, db = sess.run([w, b, x, y0, loss, dw, db]) + self.evaluate(variables.global_variables_initializer()) + w, b, x, y0, loss, dw, db = self.evaluate([w, b, x, y0, loss, dw, db]) self.assertAllEqual(w.shape, (64, 64)) self.assertAllClose(np.sum(w), 2050.44) @@ -1717,10 +1741,12 @@ class VariableHoistingTest(test.TestCase): self.assertAllEqual(db.shape, (64,)) self.assertAllClose(np.sum(db), 0.509, rtol=1e-2) + @test_util.run_deprecated_v1 def testBasic(self): self._testSimpleModel(True) self._testSimpleModel(False) + @test_util.run_deprecated_v1 def testBasicResource(self): self._testSimpleModel(True, use_resource=True) self._testSimpleModel(False, use_resource=True) diff --git a/tensorflow/python/framework/graph_util_test.py b/tensorflow/python/framework/graph_util_test.py index 563a177dd0..4e7408ad49 100644 --- a/tensorflow/python/framework/graph_util_test.py +++ b/tensorflow/python/framework/graph_util_test.py @@ -29,6 +29,7 @@ from tensorflow.python.framework import graph_util from tensorflow.python.framework import importer from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_util +from tensorflow.python.framework import test_util from tensorflow.python.ops import gen_state_ops from tensorflow.python.ops import math_ops # pylint: disable=unused-import from tensorflow.python.ops import math_ops as math_ops_lib @@ -102,6 +103,7 @@ class DeviceFunctionsTest(test.TestCase): self.assertDeviceEqual(var_5.device, "/device:GPU:0") self.assertDeviceEqual(var_6.device, "/device:CPU:0") + @test_util.run_deprecated_v1 def testNestedDeviceFunctions(self): with ops.Graph().as_default(): var_0 = variables.VariableV1(0) @@ -210,8 +212,8 @@ class DeviceFunctionsTest(test.TestCase): with session.Session() as sess: init = variables.variables_initializer([variable_node]) - sess.run(init) - output = sess.run(output_node) + self.evaluate(init) + output = self.evaluate(output_node) self.assertNear(4.0, output, 0.00001) variable_graph_def = sess.graph.as_graph_def() @@ -242,8 +244,8 @@ class DeviceFunctionsTest(test.TestCase): output_node = math_ops_lib.multiply( variable_node, 2.0, name="output_node") with session.Session() as sess: - sess.run(variable_node.initializer) - output = sess.run(output_node) + self.evaluate(variable_node.initializer) + output = self.evaluate(output_node) self.assertNear(2.0, output, 0.00001) variable_graph_def = sess.graph.as_graph_def() # First get the constant_graph_def when variable_names_whitelist is @@ -256,7 +258,7 @@ class DeviceFunctionsTest(test.TestCase): # Then initialize the unused variable, and get another # constant_graph_def when variable_names_whitelist is not set. - sess.run(another_variable.initializer) + self.evaluate(another_variable.initializer) constant_graph_def_without_variable_whitelist = ( graph_util.convert_variables_to_constants( sess, variable_graph_def, ["output_node"])) @@ -295,7 +297,7 @@ class DeviceFunctionsTest(test.TestCase): ["Variable", "VariableV2", "VarHandleOp", "ReadVariableOp"]) with session.Session() as sess: output_node = sess.graph.get_tensor_by_name("output_node:0") - output = sess.run(output_node) + output = self.evaluate(output_node) self.assertNear(2.0, output, 0.00001) def create_node_def(self, op, name, inputs): diff --git a/tensorflow/python/framework/importer.py b/tensorflow/python/framework/importer.py index c9ac27e788..98c7aeccc4 100644 --- a/tensorflow/python/framework/importer.py +++ b/tensorflow/python/framework/importer.py @@ -21,6 +21,7 @@ import contextlib from tensorflow.core.framework import graph_pb2 from tensorflow.python import pywrap_tensorflow as c_api +from tensorflow.python import tf2 from tensorflow.python.framework import c_api_util from tensorflow.python.framework import device as pydev from tensorflow.python.framework import errors @@ -253,7 +254,9 @@ def _ProcessNewOps(graph): # Find any device in the list of colocated ops that have a device, if it # exists. We assume that if multiple ops have devices, they refer to the # same device. Otherwise, a runtime error will occur since the colocation - # property cannot be guaranteed. + # property cannot be guaranteed. Note in TF2 colocations have been removed + # from the public API and will be considered a hint, so there is no runtime + # error. # # One possible improvement is to try to check for compatibility of all # devices in this list at import time here, which would require @@ -262,6 +265,10 @@ def _ProcessNewOps(graph): try: coloc_op = graph._get_operation_by_name_unsafe(coloc_op_name) # pylint: disable=protected-access except KeyError: + # Do not error in TF2 if the colocation cannot be guaranteed + if tf2.enabled(): + continue + raise ValueError('Specified colocation to an op that ' 'does not exist during import: %s in %s' % (coloc_op_name, op.name)) @@ -431,17 +438,16 @@ def import_graph_def(graph_def, # # TODO(skyewm): fetch the TF_Functions directly from the TF_Graph # TODO(skyewm): avoid sending serialized FunctionDefs back to the TF_Graph - # TODO(b/74620627): move this after _ProcessNewOps outside the lock once - # _USE_C_SHAPES is removed. - if graph_def.library and graph_def.library.function: - # pylint: disable=protected-access - functions = function._from_library(graph_def.library) - for f in functions: - f.add_to_graph(graph) - # pylint: enable=protected-access _ProcessNewOps(graph) + if graph_def.library and graph_def.library.function: + # pylint: disable=protected-access + functions = function._from_library(graph_def.library) + for f in functions: + f.add_to_graph(graph) + # pylint: enable=protected-access + # Treat input mappings that don't appear in the graph as an error, because # they are likely to be due to a typo. missing_unused_input_keys = ( diff --git a/tensorflow/python/framework/importer_test.py b/tensorflow/python/framework/importer_test.py index 2b4d8e7299..66e80b5585 100644 --- a/tensorflow/python/framework/importer_test.py +++ b/tensorflow/python/framework/importer_test.py @@ -397,11 +397,11 @@ class ImportGraphDefTest(test.TestCase): # Run the imported graph. # TODO(b/76173421): make this work (currently DCHECKS) # with self.cached_session() as sess: - # sess.run(imported_init) - # self.assertEqual(sess.run(imported_var), 1.0) - # self.assertEqual(sess.run(imported_assign), 2.0) - # self.assertEqual(list(sess.run(imported_shape)), []) - # self.assertEqual(list(sess.run(new_var_shape)), []) + # self.evaluate(imported_init) + # self.assertEqual(self.evaluate(imported_var), 1.0) + # self.assertEqual(self.evaluate(imported_assign), 2.0) + # self.assertEqual(list(self.evaluate(imported_shape)), []) + # self.assertEqual(list(self.evaluate(new_var_shape)), []) def testWhileLoop(self): # Produce GraphDef containing while loop. @@ -418,7 +418,7 @@ class ImportGraphDefTest(test.TestCase): return_elements=[r.name]) self.assertEqual(imported_r.name, "import/" + r.name) with self.cached_session() as sess: - self.assertEqual(sess.run(imported_r), 10) + self.assertEqual(self.evaluate(imported_r), 10) def testImportWhileLoopInCond(self): # Produce GraphDef containing while loop. @@ -458,7 +458,7 @@ class ImportGraphDefTest(test.TestCase): lambda i: i < 2, ImportFn, [0], shape_invariants=[tensor_shape.TensorShape(None)]) with self.cached_session() as sess: - self.assertEqual(sess.run(out), 10) + self.assertEqual(self.evaluate(out), 10) def testTypeMismatchInGraphDef(self): # TODO(skyewm): improve error message @@ -930,7 +930,7 @@ class ImportGraphDefTest(test.TestCase): name="", return_elements=["id:0"]) with self.cached_session(): - self.assertEqual(5.0, t.eval()) + self.assertEqual(5.0, self.evaluate(t)) def testInvalidInputForReturnOperations(self): with ops.Graph().as_default(): @@ -1071,7 +1071,7 @@ class ImportGraphDefTest(test.TestCase): tensor_input = np.ones(input_shape, dtype=np.float32) t = constant_op.constant(tensor_input, shape=input_shape) g = array_ops.identity(t) - g.eval() + self.evaluate(g) def testVersion(self): v0 = versions.GRAPH_DEF_VERSION_MIN_CONSUMER @@ -1255,7 +1255,7 @@ class ImportGraphDefTest(test.TestCase): z = TestFunc() with self.cached_session(): - z_val = z.eval() + z_val = self.evaluate(z) self.assertEqual(z_val, -2.0) def testImportGraphWithFunctionTwice(self): diff --git a/tensorflow/python/framework/load_library.py b/tensorflow/python/framework/load_library.py index 908a5f521e..727f6aa44c 100644 --- a/tensorflow/python/framework/load_library.py +++ b/tensorflow/python/framework/load_library.py @@ -31,6 +31,7 @@ from tensorflow.core.lib.core import error_codes_pb2 # pylint: disable=unused-i from tensorflow.python import pywrap_tensorflow as py_tf from tensorflow.python.lib.io import file_io from tensorflow.python.util import compat +from tensorflow.python.util import deprecation from tensorflow.python.util.tf_export import tf_export @@ -83,7 +84,8 @@ def load_op_library(library_filename): return module -@tf_export('load_file_system_library') +@deprecation.deprecated(date=None, instructions='Use tf.load_library instead.') +@tf_export(v1=['load_file_system_library']) def load_file_system_library(library_filename): """Loads a TensorFlow plugin, containing file system implementation. diff --git a/tensorflow/python/framework/meta_graph_test.py b/tensorflow/python/framework/meta_graph_test.py index fc98b91a01..46ce4616a5 100644 --- a/tensorflow/python/framework/meta_graph_test.py +++ b/tensorflow/python/framework/meta_graph_test.py @@ -63,6 +63,7 @@ def _TestDir(test_name): class SimpleMetaGraphTest(test.TestCase): + @test_util.run_deprecated_v1 def testNoVariables(self): test_dir = _TestDir("no_variables") filename = os.path.join(test_dir, "metafile") @@ -116,6 +117,7 @@ class SimpleMetaGraphTest(test.TestCase): {new_input_tensor: input_feed_value}) self.assertEqual(new_output_value, output_value) + @test_util.run_deprecated_v1 def testStrippedOpListNestedFunctions(self): with self.cached_session(): # Square two levels deep @@ -158,6 +160,7 @@ class SimpleMetaGraphTest(test.TestCase): op_list = meta_graph.stripped_op_list_for_graph(graph) self.assertEqual(["Const"], [op.name for op in op_list.op]) + @test_util.run_deprecated_v1 def testDefaultAttrStripping(self): """Verifies that default attributes are stripped from a graph def.""" @@ -210,6 +213,7 @@ class SimpleMetaGraphTest(test.TestCase): self.assertEqual(node_def.attr["Tout"].type, dtypes.complex128) self.assertTrue(meta_graph_def.meta_info_def.stripped_default_attrs) + @test_util.run_deprecated_v1 def testDefaultAttrStrippingNestedFunctions(self): """Verifies that default attributes are stripped from function node defs.""" with self.cached_session(): @@ -261,6 +265,7 @@ class SimpleMetaGraphTest(test.TestCase): self.assertEqual(node_def.attr["attr_1"].i, 1) self.assertTrue(meta_graph_def.meta_info_def.stripped_default_attrs) + @test_util.run_deprecated_v1 def testVariableObjectsAreSharedAmongCollections(self): with ops.Graph().as_default() as graph1: v = variables.Variable(3.0) @@ -454,6 +459,7 @@ class ScopedMetaGraphTest(test.TestCase): # Verifies that we can export the subgraph under each layer and import # them into new layers in a new graph. + @test_util.run_deprecated_v1 def testScopedExportAndImport(self): test_dir = _TestDir("scoped_export_import") filenames = [ @@ -492,8 +498,8 @@ class ScopedMetaGraphTest(test.TestCase): init_op = variables.global_variables_initializer() grad = gradients_impl.gradients([output], [var]) with session.Session() as sess: - sess.run(init_op) - expected_grad_value = sess.run(grad) + self.evaluate(init_op) + expected_grad_value = self.evaluate(grad) # Restore the MetaGraphDef into a new Graph with an import scope. with ops.Graph().as_default(): @@ -518,10 +524,11 @@ class ScopedMetaGraphTest(test.TestCase): init_op = variables.global_variables_initializer() with session.Session() as sess: - sess.run(init_op) - actual_grad_value = sess.run(grad) + self.evaluate(init_op) + actual_grad_value = self.evaluate(grad) self.assertEqual(expected_grad_value, actual_grad_value) + @test_util.run_deprecated_v1 def testImportWhileLoopInWhileLoop(self): # Create a simple while loop. with ops.Graph().as_default(): @@ -544,9 +551,10 @@ class ScopedMetaGraphTest(test.TestCase): _, x = control_flow_ops.while_loop(lambda i, x: i < 2, body, [0, 0.0], name="") with session.Session() as sess: - sess.run(variables.global_variables_initializer()) - sess.run(x) + self.evaluate(variables.global_variables_initializer()) + self.evaluate(x) + @test_util.run_deprecated_v1 def testScopedImportUnderNameScope(self): graph = ops.Graph() with graph.as_default(): @@ -562,6 +570,7 @@ class ScopedMetaGraphTest(test.TestCase): self.assertEqual(list(imported_variables.values())[0].name, "foo/bar/myvar:0") + @test_util.run_deprecated_v1 def testScopedImportUnderNameScopeNoVarScope(self): graph = ops.Graph() with graph.as_default(): @@ -590,6 +599,7 @@ class ScopedMetaGraphTest(test.TestCase): self.assertEqual(list(imported_variables.values())[0].name, "s" + suffix + "/v:0") + @test_util.run_deprecated_v1 def testScopedImportWithSelectedCollections(self): meta_graph_filename = os.path.join( _TestDir("selected_collections_import"), "meta_graph.pb") @@ -600,11 +610,11 @@ class ScopedMetaGraphTest(test.TestCase): with graph.as_default(): variables.Variable(initial_value=1.0, trainable=True) self.assertTrue( - all([ + all( graph.get_collection(key) for key in [ops.GraphKeys.GLOBAL_VARIABLES, ops.GraphKeys.TRAINABLE_VARIABLES] - ])) + )) meta_graph.export_scoped_meta_graph( filename=meta_graph_filename, graph=graph) @@ -687,6 +697,7 @@ class ScopedMetaGraphTest(test.TestCase): # Verifies that we can export the subgraph containing a FIFOQueue under # "queue1" and import it into "new_queue1" in a new graph. + @test_util.run_deprecated_v1 def testScopedWithQueue(self): test_dir = _TestDir("scoped_with_queue") orig_meta_graph = self._testScopedExportWithQueue(test_dir, @@ -749,12 +760,15 @@ class ScopedMetaGraphTest(test.TestCase): for n, e in zip(nodes, expected): self.assertEqual([e], graph2.get_operation_by_name(n).get_attr("_class")) + @test_util.run_deprecated_v1 def testExportNestedNames(self): self.doTestExportNestedNames(use_resource=False) + @test_util.run_deprecated_v1 def testExportNestedNamesResource(self): self.doTestExportNestedNames(use_resource=True) + @test_util.run_deprecated_v1 def testPotentialCycle(self): graph1 = ops.Graph() with graph1.as_default(): @@ -783,6 +797,7 @@ class ScopedMetaGraphTest(test.TestCase): 4.0, shape=[2, 2]) }) + @test_util.run_deprecated_v1 def testClearDevices(self): graph1 = ops.Graph() with graph1.as_default(): @@ -842,6 +857,7 @@ class ScopedMetaGraphTest(test.TestCase): class MetaGraphWithVariableScopeTest(test.TestCase): + @test_util.run_deprecated_v1 def testMetricsCollection(self): def _enqueue_vector(sess, queue, values, shape=None): @@ -868,8 +884,8 @@ class MetaGraphWithVariableScopeTest(test.TestCase): _, update_op = metrics.mean(values) initializer = variables.local_variables_initializer() - sess.run(initializer) - sess.run(update_op) + self.evaluate(initializer) + self.evaluate(update_op) meta_graph.export_scoped_meta_graph( filename=meta_graph_filename, graph=graph) @@ -880,7 +896,7 @@ class MetaGraphWithVariableScopeTest(test.TestCase): with self.session(graph=graph) as sess: meta_graph.import_scoped_meta_graph(meta_graph_filename) initializer = variables.local_variables_initializer() - sess.run(initializer) + self.evaluate(initializer) # Verifies that importing an old meta_graph where "local_variables" # collection is of node_list type works, but cannot build initializer @@ -899,6 +915,7 @@ class MetaGraphWithVariableScopeTest(test.TestCase): class ExportImportAcrossScopesTest(test.TestCase): + @test_util.run_deprecated_v1 def testPartionedVariables(self): def make_graph_with_partitioned_variables(use_resource): diff --git a/tensorflow/python/framework/op_def_library.py b/tensorflow/python/framework/op_def_library.py index 9955a9a2cd..2318b32ef1 100644 --- a/tensorflow/python/framework/op_def_library.py +++ b/tensorflow/python/framework/op_def_library.py @@ -570,7 +570,7 @@ class OpDefLibrary(object): "than minimum length %d." % (input_name, op_type_name, len(values), num_attr.minimum)) # All tensors must have the same base type. - if any([bt != base_types[0] for bt in base_types]): + if any(bt != base_types[0] for bt in base_types): raise TypeError( "All tensors passed to '%s' of '%s' Op " "must have the same type." % diff --git a/tensorflow/python/framework/ops.py b/tensorflow/python/framework/ops.py index aaa12bf71f..1a26984809 100644 --- a/tensorflow/python/framework/ops.py +++ b/tensorflow/python/framework/ops.py @@ -36,14 +36,13 @@ from tensorflow.core.framework import op_def_pb2 from tensorflow.core.framework import versions_pb2 from tensorflow.core.protobuf import config_pb2 from tensorflow.python import pywrap_tensorflow as c_api +from tensorflow.python import tf2 from tensorflow.python.eager import context from tensorflow.python.eager import core from tensorflow.python.eager import tape from tensorflow.python.framework import c_api_util -from tensorflow.python.framework import cpp_shape_inference_pb2 from tensorflow.python.framework import device as pydev from tensorflow.python.framework import dtypes -from tensorflow.python.framework import error_interpolation from tensorflow.python.framework import errors from tensorflow.python.framework import op_def_registry from tensorflow.python.framework import registry @@ -318,22 +317,13 @@ class Tensor(_TensorLike): self._op = op self._value_index = value_index self._dtype = dtypes.as_dtype(dtype) - # This will be set by self._as_tf_output(). self._tf_output = None - # This will be set by self.shape(). self._shape_val = None - # List of operations that use this Tensor as input. We maintain this list # to easily navigate a computation graph. self._consumers = [] - - if not _USE_C_SHAPES: - # Attributes used for C++ shape inference. Not inspected, only forwarded. - # If set, will be a HandleData object from cpp_shape_inference.proto. - self._handle_data = None - self._id = uid() @property @@ -408,17 +398,7 @@ class Tensor(_TensorLike): """ if self._shape_val is None: - if _USE_C_SHAPES: - self._shape_val = self._c_api_shape() - else: - # Call set_shape_and_handle_data_for_outputs in topological order on all - # ops that are needed to compute self.op's shape. We do this instead of - # having set_shape_and_handle_data_for_outputs recursively call - # Operation.shape on self.op.inputs to overflowing the call stack. - need_shapes = self._get_input_ops_without_shapes(self.op) - need_shapes.sort(key=lambda op: op._id) - for op in need_shapes: - set_shape_and_handle_data_for_outputs(op) + self._shape_val = self._c_api_shape() return self._shape_val def _get_input_ops_without_shapes(self, target_op): @@ -533,14 +513,10 @@ class Tensor(_TensorLike): ValueError: If `shape` is not compatible with the current shape of this tensor. """ - if _USE_C_SHAPES: # pylint: disable=protected-access - # Reset cached shape. - self._shape_val = None - else: - self._shape_val = self.shape.merge_with(shape) + # Reset cached shape. + self._shape_val = None - # Update C shape even if _USE_C_SHAPES = False, since we still want - # set_shape to be reflected in the C API graph for when we run it. + # We want set_shape to be reflected in the C API graph for when we run it. if not isinstance(shape, tensor_shape.TensorShape): shape = tensor_shape.TensorShape(shape) dim_list = [] @@ -634,10 +610,7 @@ class Tensor(_TensorLike): return id(self) == id(other) def __copy__(self): - # Make sure _shape_val is computed before we copy. # TODO(b/77597810): get rid of Tensor copies. - if self._shape_val is None: - set_shape_and_handle_data_for_outputs(self.op) cls = self.__class__ result = cls.__new__(cls) result.__dict__.update(self.__dict__) @@ -774,6 +747,18 @@ class _EagerTensorBase(Tensor): def _numpy(self): raise NotImplementedError() + @property + def backing_device(self): + """Returns the name of the device holding this tensor's memory. + + `.backing_device` is usually the same as `.device`, which returns + the device on which the kernel of the operation that produced this tensor + ran. However, some operations can produce tensors on a different device + (e.g., an operation that executes on the GPU but produces output tensors + in host memory). + """ + raise NotImplementedError() + def __copy__(self): # Eager Tensors are immutable so it's safe to return themselves as a copy. return self @@ -890,6 +875,12 @@ class _EagerTensorBase(Tensor): """Returns the number of Tensor dimensions.""" return self.shape.ndims + def __len__(self): + """Returns the length of the first dimension in the Tensor.""" + if not self.shape.ndims: + raise TypeError("Scalar tensor has no `len()`") + return self._shape_tuple()[0] + def _cpu_nograd(self): """A copy of this Tensor with contents backed by host memory. @@ -918,13 +909,7 @@ class _EagerTensorBase(Tensor): return self._copy(context.context(), "GPU:" + str(gpu_index)) def __bool__(self): - if self._shape_tuple() != (): # pylint: disable=g-explicit-bool-comparison - raise ValueError( - "Non-scalar tensor %s cannot be converted to boolean." % repr(self)) - if self.dtype != dtypes.bool: - raise ValueError( - "Non-boolean tensor %s cannot be converted to boolean." % repr(self)) - return bool(self.cpu().numpy()) + return bool(self.numpy()) def __nonzero__(self): return self.__bool__() @@ -1044,12 +1029,12 @@ def convert_to_tensor(value, dtype=None, name=None, preferred_dtype=None): `preferred_dtype` is not possible, this argument has no effect. Returns: - An `Output` based on `value`. + An `Tensor` based on `value`. Raises: - TypeError: If no conversion function is registered for `value`. + TypeError: If no conversion function is registered for `value` to `dtype`. RuntimeError: If a registered conversion function returns an invalid value. - + ValueError: If the `value` is a tensor not of given `dtype` in graph mode. """ return convert_to_tensor_v2(value, dtype, preferred_dtype, name) @@ -1097,12 +1082,12 @@ def convert_to_tensor_v2(value, dtype=None, dtype_hint=None, name=None): name: Optional name to use if a new `Tensor` is created. Returns: - An `Output` based on `value`. + An `Tensor` based on `value`. Raises: - TypeError: If no conversion function is registered for `value`. + TypeError: If no conversion function is registered for `value` to `dtype`. RuntimeError: If a registered conversion function returns an invalid value. - + ValueError: If the `value` is a tensor not of given `dtype` in graph mode. """ return internal_convert_to_tensor( value=value, @@ -1123,49 +1108,13 @@ def internal_convert_to_tensor(value, preferred_dtype=None, ctx=None, accept_symbolic_tensors=True): - """Converts the given `value` to an `Tensor`. - - This function converts Python objects of various types to `Tensor` - objects. It accepts `Tensor` objects, numpy arrays, Python lists, - and Python scalars. For example: - - This function can be useful when composing a new operation in Python - All standard Python op constructors apply this function to each of their - Tensor-valued inputs, which allows those ops to accept numpy arrays, Python - lists, and scalars in addition to `Tensor` objects. - - Args: - value: An object whose type has a registered `Tensor` conversion function. - dtype: Optional element type for the returned tensor. If missing, the - type is inferred from the type of `value`. - name: Optional name to use if a new `Tensor` is created. - as_ref: True if we want the mutable view of Variables, if applicable. - preferred_dtype: Optional element type for the returned tensor, - used when dtype is None. In some cases, a caller may not have a - dtype in mind when converting to a tensor, so preferred_dtype - can be used as a soft preference. If the conversion to - `preferred_dtype` is not possible, this argument has no effect. - ctx: Optional: The value of context.context(). - accept_symbolic_tensors: Whether Keras graph tensors should be accepted as - a valid tensor type during eager execution. - If False, this function will raise an exception if it is passed such - a tensor during eager eager execution. - - Returns: - A `Tensor` based on `value`. - - Raises: - TypeError: If no conversion function is registered for `value`. - RuntimeError: If a registered conversion function returns an invalid value. - - """ + """Implementation of the public convert_to_tensor.""" if ctx is None: ctx = context.context() if isinstance(value, EagerTensor): if ctx.executing_eagerly(): - # Fast path for EagerTensors that don't need any conversion. - # Note that we don't check that value's dtype matches the dtype - # argument. We expect that the C runtime will do that checking - # when we execute the kernel. + if dtype is not None: + dtype = dtypes.as_dtype(dtype) + value = _TensorTensorConversionFunction(value, dtype=dtype) return value else: graph = get_default_graph() @@ -2129,12 +2078,6 @@ class Operation(object): raise TypeError("tensor must be a Tensor: %s" % tensor) _assert_same_graph(self, tensor) - # Make sure output shapes are already computed for this op in case we create - # a cycle (we cannot compute shapes for cycles). Usually shapes are computed - # lazily upon request. - if not _USE_C_SHAPES: - set_shape_and_handle_data_for_outputs(self) - # Reset cached inputs. self._inputs_val = None c_api.UpdateEdge( @@ -2142,6 +2085,31 @@ class Operation(object): tensor._as_tf_output(), # pylint: disable=protected-access self._tf_input(index)) + def _add_while_inputs(self, tensors): + """See AddWhileInputHack in python_api.h. + + NOTE: This is for TF internal use only. Please don't use it. + + Args: + tensors: list of Tensors + + Raises: + TypeError: if tensor is not a Tensor, + or if input tensor type is not convertible to dtype. + ValueError: if the Tensor is from a different graph. + """ + for tensor in tensors: + if not isinstance(tensor, Tensor): + raise TypeError("tensor must be a Tensor: %s" % tensor) + _assert_same_graph(self, tensor) + + # Reset cached inputs. + self._inputs_val = None + c_api.AddWhileInputHack( + self._graph._c_graph, # pylint: disable=protected-access + tensor._as_tf_output(), # pylint: disable=protected-access + self._c_op) + def _add_control_inputs(self, ops): """Add a list of new control inputs to this operation. @@ -2175,6 +2143,23 @@ class Operation(object): """Removes any control inputs to this operation.""" c_api.RemoveAllControlInputs(self._graph._c_graph, self._c_op) # pylint: disable=protected-access + def _add_outputs(self, types, shapes): + """Adds new Tensors to self.outputs. + + Note: this is generally unsafe to use. This is used in certain situations in + conjunction with _set_type_list_attr. + + Arguments: + types: list of DTypes + shapes: list of TensorShapes + """ + assert len(types) == len(shapes) + orig_num_outputs = len(self.outputs) + for i in range(len(types)): + t = Tensor(self, orig_num_outputs + i, types[i]) + self._outputs.append(t) + t.set_shape(shapes[i]) + def __str__(self): return str(self.node_def) @@ -2387,6 +2372,25 @@ class Operation(object): finally: c_api.TF_DeleteBuffer(buf) + def _set_func_attr(self, attr_name, func_name): + """Private method used to set a function attribute in the node_def.""" + func = attr_value_pb2.NameAttrList(name=func_name) + self._set_attr(attr_name, attr_value_pb2.AttrValue(func=func)) + + def _set_type_list_attr(self, attr_name, types): + """Private method used to set a function attribute in the node_def.""" + if not types: return + if isinstance(types[0], dtypes.DType): + types = [dt.as_datatype_enum for dt in types] + types_list = attr_value_pb2.AttrValue.ListValue(type=types) + self._set_attr(attr_name, attr_value_pb2.AttrValue(list=types_list)) + + def _set_shape_list_attr(self, attr_name, shapes): + """Private method used to set a function attribute in the node_def.""" + shapes = [s.as_proto() for s in shapes] + shapes_list = attr_value_pb2.AttrValue.ListValue(shape=shapes) + self._set_attr(attr_name, attr_value_pb2.AttrValue(list=shapes_list)) + def get_attr(self, name): """Returns the value of the attr of this op with the given `name`. @@ -2399,7 +2403,7 @@ class Operation(object): Raises: ValueError: If this op does not have an attr with the given `name`. """ - fields = ["s", "i", "f", "b", "type", "shape", "tensor", "func"] + fields = ("s", "i", "f", "b", "type", "shape", "tensor", "func") try: with c_api_util.tf_buffer() as buf: c_api.TF_OperationGetAttrValueProto(self._c_op, name, buf) @@ -2410,25 +2414,21 @@ class Operation(object): x = attr_value_pb2.AttrValue() x.ParseFromString(data) - # Treat an empty oneof value as an empty list. - if not x.WhichOneof("value"): + oneof_value = x.WhichOneof("value") + if oneof_value is None: return [] - if x.HasField("list"): + if oneof_value == "list": for f in fields: if getattr(x.list, f): if f == "type": - return [dtypes.as_dtype(x) for x in list(getattr(x.list, f))] + return [dtypes.as_dtype(t) for t in x.list.type] else: return list(getattr(x.list, f)) return [] - else: - for f in fields: - if x.HasField(f): - if f == "type": - return dtypes.as_dtype(getattr(x, f)) - else: - return getattr(x, f) - assert False, "Unsupported field type in " + str(x) + if oneof_value == "type": + return dtypes.as_dtype(x.type) + assert oneof_value in fields, "Unsupported field type in " + str(x) + return getattr(x, oneof_value) def run(self, feed_dict=None, session=None): """Runs this operation in a `Session`. @@ -2608,72 +2608,9 @@ class RegisterShape(object): return f -# TODO(b/74620627): remove when _USE_C_SHAPES is removed -def _set_shape_and_handle_data_for_outputs_c_api(op): - """Set shapes and resource handle data using info from the C API.""" - assert not _USE_C_SHAPES - for output in op.outputs: - output._shape_val = output._c_api_shape() - # Set the resource handle data for compatibility with the Python shape - # inference code. - serialized = c_api.GetHandleShapeAndType(op._graph._c_graph, # pylint: disable=protected-access - output._as_tf_output()) - if serialized: - output._handle_data = ( - cpp_shape_inference_pb2.CppShapeInferenceResult.HandleData - .FromString(compat.as_bytes(serialized))) - else: - output._handle_data = None - - -# TODO(b/74620627): remove when _USE_C_SHAPES is removed -def set_shape_and_handle_data_for_outputs(op): - """Set the shapes and resource handle data for op's outputs. - - When _USE_C_SHAPES = False, this is lazily called when a tensor's shape is - first requested. Usually this should work automatically, but some edge cases - may require manually calling this first to make sure Tensor._shape_val and - Tensor._handle_data are set (e.g. manually overriding _handle_data, copying a - Tensor). - """ - if _USE_C_SHAPES: return - - if op.graph._is_function(op.type): - for output in op.outputs: - output._shape_val = tensor_shape.unknown_shape() - return - - try: - shape_func = _shape_registry.lookup(op.type) - except LookupError: - try: - shape_func = _default_shape_function_registry.lookup(op.type) - except LookupError: - shape_func = _call_cpp_shape_fn_and_require_op - - shapes = shape_func(op) - if shapes is None: - raise RuntimeError( - "Shape function for op %s did not return any shapes" % op) - elif isinstance(shapes, dict): - # Returned by call_cpp_shape_fn - shapes_dict = shapes - shapes = shapes_dict["shapes"] - handle_datas = shapes_dict["handle_data"] - for output, handle_data in zip(op.outputs, handle_datas): - # Don't override any existing handle data that may have been manually set. - # pylint: disable=protected-access - if output._handle_data is None: - output._handle_data = handle_data - # pylint: enable=protected-access - - if len(op.outputs) != len(shapes): - raise RuntimeError( - "Shape function for op %s returned %d shapes but expected %d %s %s" % - (op, len(shapes), len(op.outputs), shape_func.__name__, str(shapes))) - for output, s in zip(op.outputs, shapes): - output._shape_val = tensor_shape.unknown_shape() - output._shape_val = output._shape_val.merge_with(s) +def set_shape_and_handle_data_for_outputs(_): + """No op. TODO(b/74620627): Remove this.""" + pass class OpStats(object): @@ -2901,8 +2838,8 @@ class Graph(object): self._stack_state_is_thread_local = False self._thread_local = threading.local() # Functions that will be applied to choose a device if none is specified. - # After switch_to_thread_local(), self._thread_local._device_function_stack - # is used instead. + # In TF2.x or after switch_to_thread_local(), + # self._thread_local._device_function_stack is used instead. self._graph_device_function_stack = traceable_stack.TraceableStack() # Default original_op applied to new ops. self._default_original_op = None @@ -2910,7 +2847,7 @@ class Graph(object): # WhileContext defined in ops/control_flow_ops.py self._control_flow_context = None # A new node will depend of the union of all of the nodes in the stack. - # After switch_to_thread_local(), + # In TF2.x or after switch_to_thread_local(), # self._thread_local._control_dependencies_stack is used instead. self._graph_control_dependencies_stack = [] # Arbitrary collections of objects. @@ -2934,7 +2871,7 @@ class Graph(object): producer=versions.GRAPH_DEF_VERSION, min_consumer=versions.GRAPH_DEF_VERSION_MIN_CONSUMER) self._building_function = False - # Stack of colocate_with ops. After switch_to_thread_local(), + # Stack of colocate_with ops. In TF2.x or after switch_to_thread_local(), # self._thread_local._colocation_stack is used instead. self._graph_colocation_stack = traceable_stack.TraceableStack() # Set of tensors that are dangerous to feed! @@ -2967,6 +2904,8 @@ class Graph(object): # requirement (many custom ops do not have shape functions, and we don't # want to break these existing cases). c_api.SetRequireShapeInferenceFns(self._c_graph, False) + if tf2.enabled(): + self.switch_to_thread_local() # Note: this method is private because the API of tf.Graph() is public and # frozen, and this functionality is still not ready for public visibility. @@ -3391,36 +3330,6 @@ class Graph(object): self._create_op_helper(ret, compute_device=compute_device) return ret - def _make_colocation_conflict_message(self, op, colocation_op): - """Return detailed error message about device conflict due to colocation.""" - # Example error message: - # Tried to colocate op 'a' (defined at file1.py:149) having device - # '/device:GPU:0' with op 'b' (defined at file2:96) which had an - # incompatible device '/device:CPU:0'. - # - # No node-device colocations were active during op 'a' creation. - # Device assignments active during op 'a' creation: - # with tf.device(/device:GPU:0): file1.py:148> - # - # Node-device colocations active during op 'b' creation: - # with tf.colocate_with(a): file2.py:93> - # Device assignments active during op 'b' creation: - # with tf.device(/cpu:0): file2.py:94 - op_info = error_interpolation.compute_field_dict(op) - coloc_op_info = error_interpolation.compute_field_dict(colocation_op) - msg = ("Tried to colocate op '{op_name}'{op_loc} having device '{op_dev}' " - "with op '{coloc_op_name}'{coloc_op_loc} which had an incompatible " - "device '{coloc_op_dev}'.\n\n{op_summary}\n\n{coloc_op_summary}" - .format(op_name=op.name, - op_loc=op_info["defined_at"], - op_dev=op.device, - op_summary=op_info["devs_and_colocs"], - coloc_op_name=colocation_op.name, - coloc_op_loc=coloc_op_info["defined_at"], - coloc_op_dev=colocation_op.device, - coloc_op_summary=coloc_op_info["devs_and_colocs"])) - return msg - def _create_op_helper(self, op, compute_device=True): """Common logic for creating an op in this graph.""" # Apply any additional attributes requested. Do not overwrite any existing @@ -3473,12 +3382,9 @@ class Graph(object): for colocation_op in self._colocation_stack.peek_objs(): all_colocation_groups.extend(colocation_op.colocation_groups()) if colocation_op.device: - if (op.device and pydev.canonical_name(op.device) != - pydev.canonical_name(colocation_op.device)): - msg = self._make_colocation_conflict_message(op, colocation_op) - logging.warning(msg) - else: - op._set_device(colocation_op.device) # pylint: disable=protected-access + # pylint: disable=protected-access + op._set_device(colocation_op.device) + # pylint: enable=protected-access all_colocation_groups = sorted(set(all_colocation_groups)) # pylint: disable=protected-access @@ -3526,11 +3432,6 @@ class Graph(object): # pylint: disable=protected-access for op in new_ops: - # Operations created by the C API always retrieve shapes from the C API so - # we preserve the shapes of ops created in import_graph_def (from the - # "_output_shapes" attr of the imported NodeDef). - if not _USE_C_SHAPES: - _set_shape_and_handle_data_for_outputs_c_api(op) new_control_inputs = self._control_dependencies_for_inputs(op.inputs) op._add_control_inputs(new_control_inputs) op._control_flow_post_processing() @@ -5482,7 +5383,7 @@ def inside_function(): return get_default_graph().building_function -@tf_export("enable_eager_execution") +@tf_export(v1=["enable_eager_execution"]) def enable_eager_execution(config=None, device_policy=None, execution_mode=None): @@ -5553,6 +5454,17 @@ def enable_eager_execution(config=None, server_def=None) +@tf_export(v1=["disable_eager_execution"]) +def disable_eager_execution(): + """Disables eager execution. + + This function can only be called before any Graphs, Ops, or Tensors have been + created. It can be used at the beginning of the program for complex migration + projects from TensorFlow 1.x to 2.x. + """ + context.default_execution_mode = context.GRAPH_MODE + + def enable_eager_execution_internal(config=None, device_policy=None, execution_mode=None, @@ -5560,6 +5472,7 @@ def enable_eager_execution_internal(config=None, """Enables eager execution for the lifetime of this program. Most of the doc string for enable_eager_execution is relevant here as well. + Args: config: See enable_eager_execution doc string device_policy: See enable_eager_execution doc string @@ -5652,7 +5565,7 @@ def eager_run(main=None, argv=None): app.run(main, argv) -@tf_export("reset_default_graph") +@tf_export(v1=["reset_default_graph"]) def reset_default_graph(): """Clears the default graph stack and resets the global default graph. @@ -5671,7 +5584,7 @@ def reset_default_graph(): _default_graph_stack.reset() -@tf_export("get_default_graph") +@tf_export(v1=["get_default_graph"]) def get_default_graph(): """Returns the default graph for the current thread. @@ -5798,7 +5711,7 @@ def _get_graph_from_inputs(op_input_list, graph=None): return graph or get_default_graph() -@tf_export("GraphKeys") +@tf_export(v1=["GraphKeys"]) class GraphKeys(object): """Standard names to use for graph collections. @@ -6004,7 +5917,7 @@ def add_to_collections(names, value): get_default_graph().add_to_collections(names, value) -@tf_export("get_collection_ref") +@tf_export(v1=["get_collection_ref"]) def get_collection_ref(key): """Wrapper for `Graph.get_collection_ref()` using the default graph. @@ -6028,7 +5941,7 @@ def get_collection_ref(key): return get_default_graph().get_collection_ref(key) -@tf_export("get_collection") +@tf_export(v1=["get_collection"]) def get_collection(key, scope=None): """Wrapper for `Graph.get_collection()` using the default graph. diff --git a/tensorflow/python/framework/ops_test.py b/tensorflow/python/framework/ops_test.py index 0fb17081e7..7baa02b446 100644 --- a/tensorflow/python/framework/ops_test.py +++ b/tensorflow/python/framework/ops_test.py @@ -57,11 +57,13 @@ ops._set_call_cpp_shape_fn(common_shapes.call_cpp_shape_fn) class ResourceTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testBuildGraph(self): with self.cached_session(): pt = test_ops.stub_resource_handle_op(container="a", shared_name="b") test_ops.resource_create_op(pt).run() + @test_util.run_deprecated_v1 def testInitialize(self): with self.cached_session(): handle = test_ops.stub_resource_handle_op(container="a", shared_name="b") @@ -106,6 +108,7 @@ class TensorAndShapeTest(test_util.TensorFlowTestCase): c = a + b self.assertEqual([2, 3], c.shape) + @test_util.run_deprecated_v1 def testUnknownDim(self): with self.cached_session(): a = array_ops.placeholder(dtype=dtypes.float32, shape=[2, None, 3]) @@ -113,6 +116,7 @@ class TensorAndShapeTest(test_util.TensorFlowTestCase): c = a + b self.assertEqual([2, None, 3], c.shape.as_list()) + @test_util.run_deprecated_v1 def testUnknownShape(self): with self.cached_session(): a = array_ops.placeholder(dtype=dtypes.float32, shape=None) @@ -120,6 +124,7 @@ class TensorAndShapeTest(test_util.TensorFlowTestCase): c = a + b self.assertEqual(tensor_shape.unknown_shape(), c.shape) + @test_util.run_deprecated_v1 def testScalarShape(self): with self.cached_session(): a = array_ops.placeholder(dtype=dtypes.float32, shape=[]) @@ -127,6 +132,7 @@ class TensorAndShapeTest(test_util.TensorFlowTestCase): c = a + b self.assertEqual(tensor_shape.scalar(), c.shape) + @test_util.run_deprecated_v1 def testShapeFunctionError(self): with self.cached_session(): a = array_ops.ones([1, 2, 3]) @@ -140,15 +146,16 @@ class TensorAndShapeTest(test_util.TensorFlowTestCase): class IndexedSlicesTest(test_util.TensorFlowTestCase): + @test_util.run_in_graph_and_eager_modes def testToTensor(self): - with self.cached_session(): - values = constant_op.constant([2, 3, 5, 7], shape=[2, 2]) - indices = constant_op.constant([0, 2]) - dense_shape = constant_op.constant([3, 2]) - x = ops.IndexedSlices(values, indices, dense_shape) - tensor = ops.convert_to_tensor(x, name="tensor") - self.assertAllEqual(tensor.eval(), [[2, 3], [0, 0], [5, 7]]) - + values = constant_op.constant([2, 3, 5, 7], shape=[2, 2]) + indices = constant_op.constant([0, 2]) + dense_shape = constant_op.constant([3, 2]) + x = ops.IndexedSlices(values, indices, dense_shape) + tensor = ops.convert_to_tensor(x, name="tensor") + self.assertAllEqual(self.evaluate(tensor), [[2, 3], [0, 0], [5, 7]]) + + @test_util.run_deprecated_v1 def testNegation(self): with self.cached_session(): values = constant_op.constant([2, 3, 5, 7], shape=[2, 2]) @@ -157,6 +164,7 @@ class IndexedSlicesTest(test_util.TensorFlowTestCase): self.assertAllEqual(x.values.eval(), [[-2, -3], [-5, -7]]) self.assertAllEqual(x.indices.eval(), [0, 2]) + @test_util.run_deprecated_v1 def testScalarMul(self): with self.cached_session(): values = constant_op.constant([2, 3, 5, 7], shape=[2, 2]) @@ -190,6 +198,7 @@ def _apply_op(g, *args, **kwargs): class OperationTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testNoInputs(self): op = test_ops.float_output_string_output(name="myop").a.op self.assertEqual(2, len(op.values())) @@ -212,6 +221,7 @@ class OperationTest(test_util.TensorFlowTestCase): self.assertProtoEquals("op:'FloatOutputStringOutput' name:'myop'", op.node_def) + @test_util.run_deprecated_v1 def testNoOutputs(self): op1 = test_ops.float_output(name="myop1").op float_t, = op1.values() @@ -227,6 +237,7 @@ class OperationTest(test_util.TensorFlowTestCase): self.assertProtoEquals("op:'FloatInput' name:'myop2' input:'myop1'", op2.node_def) + @test_util.run_deprecated_v1 def testInputsAndOutputs(self): op1 = test_ops.float_output(name="myop1").op self.assertEqual(1, len(op1.values())) @@ -308,16 +319,17 @@ class OperationTest(test_util.TensorFlowTestCase): with self.assertRaises(ValueError): ops.Operation(ops._NodeDef("op", "invalid:0"), g) + @test_util.run_deprecated_v1 def testNoShapeFunction(self): op = test_ops.a() self.assertEqual(tensor_shape.unknown_shape(), op.get_shape()) + @test_util.run_in_graph_and_eager_modes def testConvertToTensorNestedArray(self): - with self.cached_session(): - values = [[2], [3], [5], [7]] - tensor = ops.convert_to_tensor(values) - self.assertAllEqual((4, 1), tensor.get_shape().as_list()) - self.assertAllEqual(values, tensor.eval()) + values = [[2], [3], [5], [7]] + tensor = ops.convert_to_tensor(values) + self.assertAllEqual((4, 1), tensor.get_shape().as_list()) + self.assertAllEqual(values, self.evaluate(tensor)) def testShapeTuple(self): with self.cached_session(): @@ -333,57 +345,63 @@ class OperationTest(test_util.TensorFlowTestCase): converted = ops.convert_to_tensor(1) self.assertTrue(isinstance(converted, ops.EagerTensor)) + @test_util.run_in_graph_and_eager_modes def testConvertToTensorNestedTuple(self): - with self.cached_session(): - values = ((2,), (3,), (5,), (7,)) - tensor = ops.convert_to_tensor(values) - self.assertAllEqual((4, 1), tensor.get_shape().as_list()) - self.assertAllEqual(values, ops.convert_to_tensor(values).eval()) + values = ((2,), (3,), (5,), (7,)) + tensor = ops.convert_to_tensor(values) + self.assertAllEqual((4, 1), tensor.get_shape().as_list()) + self.assertAllEqual(values, self.evaluate(ops.convert_to_tensor(values))) + @test_util.run_in_graph_and_eager_modes def testConvertToTensorNestedTensors(self): - with self.cached_session(): - values = ((2,), (3,), (5,), (7,)) - tensor = ops.convert_to_tensor( - [constant_op.constant(row) for row in values]) - self.assertAllEqual((4, 1), tensor.get_shape().as_list()) - self.assertAllEqual(values, tensor.eval()) - tensor = ops.convert_to_tensor( - [[constant_op.constant(v) for v in row] for row in values]) - self.assertAllEqual((4, 1), tensor.get_shape().as_list()) - self.assertAllEqual(values, tensor.eval()) + values = ((2,), (3,), (5,), (7,)) + tensor = ops.convert_to_tensor( + [constant_op.constant(row) for row in values]) + self.assertAllEqual((4, 1), tensor.get_shape().as_list()) + self.assertAllEqual(values, self.evaluate(tensor)) + tensor = ops.convert_to_tensor( + [[constant_op.constant(v) for v in row] for row in values]) + self.assertAllEqual((4, 1), tensor.get_shape().as_list()) + self.assertAllEqual(values, self.evaluate(tensor)) + @test_util.run_in_graph_and_eager_modes def testConvertToTensorNestedMix(self): - with self.cached_session(): - values = ([2], (3,), [constant_op.constant(5)], constant_op.constant([7])) - tensor = ops.convert_to_tensor(values) - self.assertAllEqual((4, 1), tensor.get_shape().as_list()) - self.assertAllEqual(((2,), (3,), (5,), (7,)), tensor.eval()) + values = ([2], (3,), [constant_op.constant(5)], constant_op.constant([7])) + tensor = ops.convert_to_tensor(values) + self.assertAllEqual((4, 1), tensor.get_shape().as_list()) + self.assertAllEqual(((2,), (3,), (5,), (7,)), self.evaluate(tensor)) + @test_util.run_in_graph_and_eager_modes def testConvertToTensorPreferred(self): - with self.cached_session(): - values = [2, 3, 5, 7] - tensor = ops.convert_to_tensor(values, preferred_dtype=dtypes.float32) - self.assertEqual(dtypes.float32, tensor.dtype) + values = [2, 3, 5, 7] + tensor = ops.convert_to_tensor(values, preferred_dtype=dtypes.float32) + self.assertEqual(dtypes.float32, tensor.dtype) - with self.cached_session(): - # Convert empty tensor to anything. - values = [] - tensor = ops.convert_to_tensor(values, preferred_dtype=dtypes.int64) - self.assertEqual(dtypes.int64, tensor.dtype) + # Convert empty tensor to anything. + values = [] + tensor = ops.convert_to_tensor(values, preferred_dtype=dtypes.int64) + self.assertEqual(dtypes.int64, tensor.dtype) - with self.cached_session(): - # The preferred dtype is a type error and will convert to - # float32 instead. - values = [1.23] - tensor = ops.convert_to_tensor(values, preferred_dtype=dtypes.int64) - self.assertEqual(dtypes.float32, tensor.dtype) + # The preferred dtype is a type error and will convert to + # float32 instead. + values = [1.23] + tensor = ops.convert_to_tensor(values, preferred_dtype=dtypes.int64) + self.assertEqual(dtypes.float32, tensor.dtype) + @test_util.run_in_graph_and_eager_modes def testConvertToInvalidTensorType(self): with self.assertRaises(TypeError): # Forcing an invalid dtype should fail with a type error. values = [1.23] - _ = ops.convert_to_tensor(values, dtype=dtypes.int64) + ops.convert_to_tensor(values, dtype=dtypes.int64) + @test_util.run_in_graph_and_eager_modes + def testConvertToTensorFromInvalidTensor(self): + tensor = constant_op.constant(42.0, dtype=dtypes.float32) + with self.assertRaises(ValueError): + ops.convert_to_tensor(tensor, dtype=dtypes.int32) + + @test_util.run_deprecated_v1 def testNoConvert(self): # Operation cannot be converted to Tensor. op = control_flow_ops.no_op() @@ -401,6 +419,7 @@ class OperationTest(test_util.TensorFlowTestCase): ops._NodeDef("None", "op1"), ops.Graph(), [], [dtypes.float32]) self.assertEqual("", repr(op)) + @test_util.run_deprecated_v1 def testGetAttr(self): op = test_ops.default_attrs() self.assertEqual(op.get_attr("string_val"), b"abc") @@ -446,6 +465,7 @@ class OperationTest(test_util.TensorFlowTestCase): # TODO(b/65162920): remove this test when users who are directly mutating the # node_def have been updated to proper usage. + @test_util.run_deprecated_v1 def testSetAttr(self): op = test_ops.int_attr().op op._set_attr("foo", attr_value_pb2.AttrValue(i=2)) @@ -466,6 +486,7 @@ class OperationTest(test_util.TensorFlowTestCase): self.assertEqual(z.control_inputs, [x, y]) self.assertEqual(x._control_outputs, [z]) + @test_util.run_deprecated_v1 def testRemoveAllControlInputs(self): a = constant_op.constant(1) with ops.control_dependencies([a]): @@ -490,6 +511,7 @@ class OperationTest(test_util.TensorFlowTestCase): self.assertEqual(f.op.control_inputs, []) self.assertEqual(list(f.op.inputs), [d, e]) + @test_util.run_deprecated_v1 def testControlInputCycle(self): graph = ops.Graph() with graph.as_default(): @@ -503,7 +525,7 @@ class OperationTest(test_util.TensorFlowTestCase): with self.assertRaisesRegexp( errors.InvalidArgumentError, "Graph is invalid, contains a cycle with 2 nodes"): - sess.run(x) + self.evaluate(x) def testUpdateInput(self): g = ops.Graph() @@ -517,21 +539,21 @@ class OperationTest(test_util.TensorFlowTestCase): self.assertEquals(x.consumers(), []) self.assertEquals(y.consumers(), [z.op, z.op]) with session.Session(graph=g) as sess: - self.assertEquals(sess.run(z), 4) + self.assertEquals(self.evaluate(z), 4) z.op._update_input(0, x) # pylint: disable=protected-access self.assertEquals(list(z.op.inputs), [x, y]) self.assertEquals(x.consumers(), [z.op]) self.assertEquals(y.consumers(), [z.op]) with session.Session(graph=g) as sess: - self.assertEquals(sess.run(z), 3) + self.assertEquals(self.evaluate(z), 3) z.op._update_input(1, y) # pylint: disable=protected-access self.assertEquals(list(z.op.inputs), [x, y]) self.assertEquals(x.consumers(), [z.op]) self.assertEquals(y.consumers(), [z.op]) with session.Session(graph=g) as sess: - self.assertEquals(sess.run(z), 3) + self.assertEquals(self.evaluate(z), 3) def testUpdateInputGraphError(self): g_0 = ops.Graph() @@ -557,7 +579,7 @@ class OperationTest(test_util.TensorFlowTestCase): errors.InvalidArgumentError, "Input 0 of node add was passed string from Const_1:0 incompatible " "with expected int32"): - sess.run(z) + self.evaluate(z) def testUpdateInputShapeError(self): g = ops.Graph() @@ -582,6 +604,32 @@ class OperationTest(test_util.TensorFlowTestCase): ): x.op._update_input(1, x) # pylint: disable=protected-access + @test_util.enable_control_flow_v2 + def testAddWhileInput(self): + @eager_function.defun + def test(): + output = control_flow_ops.while_loop(lambda x: x < 3, lambda x: x + 1, + [1]) + while_op = output.op.inputs[0].op + self.assertEqual(while_op.type, "While") + orig_num_inputs = len(while_op.inputs) + + new_input1 = constant_op.constant(1.0) + new_input2 = constant_op.constant(True) + + while_op._set_type_list_attr("T", + [t.dtype for t in while_op.inputs] + + [new_input1.dtype, new_input2.dtype]) + + while_op._add_while_inputs([new_input1, new_input2]) + # Can't add an edge beyond what's specified by "T" + with self.assertRaises(errors.OutOfRangeError): + while_op._add_while_inputs([new_input2]) + self.assertEqual(len(while_op.inputs), orig_num_inputs + 2) # pylint: disable=g-deprecated-assert + + test() + + @test_util.run_deprecated_v1 def testOpDef(self): x = constant_op.constant(0) y = constant_op.constant(1) @@ -681,6 +729,7 @@ class CreateOpTest(test_util.TensorFlowTestCase): # the low-level behavior. class CreateOpFromTFOperationTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testBasic(self): g = ops.Graph() with g.as_default(): @@ -701,7 +750,6 @@ class CreateOpFromTFOperationTest(test_util.TensorFlowTestCase): self.assertEqual(g.get_operation_by_name("myop"), op) self.assertEqual(g.get_tensor_by_name("myop:0"), op.outputs[0]) - @test_util.enable_c_shapes def testShape(self): g = ops.Graph() with g.as_default(): @@ -732,6 +780,7 @@ class CreateOpFromTFOperationTest(test_util.TensorFlowTestCase): self.assertEqual(op3.name, "myop_2") self.assertEqual(op4.name, "myop_1_1") + @test_util.run_deprecated_v1 def testCond(self): g = ops.Graph() with g.as_default(): @@ -761,6 +810,7 @@ class CreateOpFromTFOperationTest(test_util.TensorFlowTestCase): "cond/cond_text") # pylint: enable=protected-access + @test_util.run_deprecated_v1 def testWhileLoop(self): g = ops.Graph() with g.as_default(): @@ -790,6 +840,7 @@ class CreateOpFromTFOperationTest(test_util.TensorFlowTestCase): "myloop/while_context") # pylint: enable=protected-access + @test_util.run_deprecated_v1 def testWhileLoopWithInternalControlDep(self): g = ops.Graph() with g.as_default(): @@ -813,6 +864,7 @@ class CreateOpFromTFOperationTest(test_util.TensorFlowTestCase): # Internal control dep is preserved self.assertEqual(op.control_inputs, [c]) + @test_util.run_deprecated_v1 def testWhileLoopWithExternalControlDep(self): g = ops.Graph() with g.as_default(): @@ -946,6 +998,7 @@ class NameStackTest(test_util.TensorFlowTestCase): self.assertEqual("bar_2", g.unique_name("bar", mark_as_used=False)) self.assertEqual("bar_2", g.unique_name("bar")) + @test_util.run_deprecated_v1 def testNameAndVariableScope(self): with self.cached_session() as sess: with sess.graph.name_scope("l0"): @@ -1076,6 +1129,13 @@ class DeviceTest(test_util.TensorFlowTestCase): node { name: "FloatOutput" op: "FloatOutput" } """, gd) + def testEagerBackingDevice(self): + with context.eager_mode(): + with ops.device("/device:CPU:0"): + t = constant_op.constant(1.0) + self.assertRegexpMatches(t.device, "/device:CPU:0") + self.assertRegexpMatches(t.backing_device, "/device:CPU:0") + def testDevicePartialString(self): g = ops.Graph() with g.device("/job:worker/replica:2"): @@ -1665,6 +1725,7 @@ def _CopyOverrideGrad(op, x_grad): # pylint: disable=invalid-name class RegistrationTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testRegisterGradients(self): x = test_ops.float_output() y = test_ops.copy_op(x) @@ -1704,6 +1765,7 @@ class ComparisonTest(test_util.TensorFlowTestCase): class ControlDependenciesTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testBasic(self): g = ops.Graph() with g.as_default(): @@ -1947,6 +2009,7 @@ class OpScopeTest(test_util.TensorFlowTestCase): with ops.name_scope(None, "default2") as scope2: self.assertEqual(scope2, "default/default2/") + @test_util.run_deprecated_v1 def testNoScopeName(self): g0 = ops.Graph() values = [ @@ -1960,6 +2023,7 @@ class OpScopeTest(test_util.TensorFlowTestCase): with ops.name_scope(None, None, values): pass + @test_util.run_deprecated_v1 def testEmptyScopeName(self): g0 = ops.Graph() a = g0.create_op("A", [], [dtypes.float32]) @@ -1971,6 +2035,7 @@ class OpScopeTest(test_util.TensorFlowTestCase): self.assertEqual("", scope) self.assertEqual(g0, ops.get_default_graph()) + @test_util.run_deprecated_v1 def testDefaultScopeName(self): g0 = ops.Graph() a = g0.create_op("A", [], [dtypes.float32]) @@ -1995,12 +2060,14 @@ class OpScopeTest(test_util.TensorFlowTestCase): with ops.name_scope(scope_name, values=graph_elements + [a]): pass + @test_util.run_deprecated_v1 def testTensor(self): g0 = ops.Graph() a = g0.create_op("A", [], [dtypes.float32]) b = g0.create_op("B", [], [dtypes.float32]) self._testGraphElements([a, b]) + @test_util.run_deprecated_v1 def testSparseTensor(self): g0 = ops.Graph() a = g0.create_op("A", [], [dtypes.float32]) @@ -2011,6 +2078,7 @@ class OpScopeTest(test_util.TensorFlowTestCase): _apply_op(g0, "Int64Output", [], [dtypes.int64])) self._testGraphElements([a, sparse, b]) + @test_util.run_deprecated_v1 def testVariable(self): g0 = ops.Graph() with g0.as_default(): @@ -2215,6 +2283,7 @@ class InitScopeTest(test_util.TensorFlowTestCase): self.assertEqual(4, int(compiled_outer(inner=compiled_inner))) self.assertEqual(7, int(compiled_outer(inner=compiled_inner))) + @test_util.run_deprecated_v1 def testFallsBackToGlobalGraphWhenAllGraphsAreBuildingFunctions(self): with context.graph_mode(): ops.reset_default_graph() @@ -2351,6 +2420,7 @@ class GraphTest(test_util.TensorFlowTestCase): g.prevent_feeding(a) self.assertFalse(g.is_feedable(a)) + @test_util.run_deprecated_v1 def testPreventFetching(self): g = ops.Graph() a = constant_op.constant(2.0) @@ -2391,7 +2461,7 @@ class GraphTest(test_util.TensorFlowTestCase): c = math_ops.add(a, b) # Create a session we can delete with session.Session(graph=g) as sess: - sess.run(c) + self.evaluate(c) # Delete all references and trigger gc del g del a @@ -2407,7 +2477,7 @@ class GraphTest(test_util.TensorFlowTestCase): math_ops.add([1, 2], [1, 2, 3]) a = constant_op.constant(1) with session.Session() as sess: - sess.run(a) + self.evaluate(a) def testRunnableAfterInvalidShapeWithKernelLabelMap(self): g = ops.Graph() @@ -2417,7 +2487,7 @@ class GraphTest(test_util.TensorFlowTestCase): test_ops.kernel_label_required(1) a = constant_op.constant(1) with session.Session() as sess: - sess.run(a) + self.evaluate(a) class AttrScopeTest(test_util.TensorFlowTestCase): @@ -2434,10 +2504,12 @@ class AttrScopeTest(test_util.TensorFlowTestCase): b = None return (a, b) + @test_util.run_deprecated_v1 def testNoLabel(self): with self.cached_session(): self.assertAllEqual((None, None), self._get_test_attrs()) + @test_util.run_deprecated_v1 def testLabelMap(self): with self.cached_session() as sess: a1 = self._get_test_attrs() @@ -2472,11 +2544,13 @@ ops.RegisterShape("KernelLabel")(common_shapes.scalar_shape) class KernelLabelTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testNoLabel(self): with self.cached_session(): self.assertAllEqual(b"My label is: default", test_ops.kernel_label().eval()) + @test_util.run_deprecated_v1 def testLabelMap(self): with self.cached_session() as sess: default_1 = test_ops.kernel_label() @@ -2491,12 +2565,14 @@ class KernelLabelTest(test_util.TensorFlowTestCase): # pylint: enable=protected-access default_3 = test_ops.kernel_label() - self.assertAllEqual(b"My label is: default", default_1.eval()) - self.assertAllEqual(b"My label is: default", default_2.eval()) - self.assertAllEqual(b"My label is: default", default_3.eval()) - self.assertAllEqual(b"My label is: overload_1", overload_1_1.eval()) - self.assertAllEqual(b"My label is: overload_1", overload_1_2.eval()) - self.assertAllEqual(b"My label is: overload_2", overload_2.eval()) + self.assertAllEqual(b"My label is: default", self.evaluate(default_1)) + self.assertAllEqual(b"My label is: default", self.evaluate(default_2)) + self.assertAllEqual(b"My label is: default", self.evaluate(default_3)) + self.assertAllEqual(b"My label is: overload_1", + self.evaluate(overload_1_1)) + self.assertAllEqual(b"My label is: overload_1", + self.evaluate(overload_1_2)) + self.assertAllEqual(b"My label is: overload_2", self.evaluate(overload_2)) class AsGraphDefTest(test_util.TensorFlowTestCase): @@ -2591,6 +2667,7 @@ class StatisticsTest(test_util.TensorFlowTestCase): class DeviceStackTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testBasicDeviceAssignmentMetadata(self): def device_func(unused_op): @@ -2622,6 +2699,7 @@ class DeviceStackTest(test_util.TensorFlowTestCase): expected_regex = r"device_func<.*ops_test.py, [0-9]+" self.assertRegexpMatches(func_description, expected_regex) + @test_util.run_deprecated_v1 def testDeviceAssignmentMetadataForGraphDeviceAndTfDeviceFunctions(self): with ops.device("/cpu"): @@ -2641,6 +2719,7 @@ class DeviceStackTest(test_util.TensorFlowTestCase): class ColocationGroupTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testBasic(self): a = constant_op.constant([2.0], name="a") with ops.colocate_with(a.op): @@ -2651,6 +2730,7 @@ class ColocationGroupTest(test_util.TensorFlowTestCase): with self.assertRaises(ValueError): c.op.get_attr("_class") + @test_util.run_deprecated_v1 def testBasicColocationMetadata(self): const_two = constant_op.constant([2.0], name="two") with ops.colocate_with(const_two.op): @@ -2663,6 +2743,7 @@ class ColocationGroupTest(test_util.TensorFlowTestCase): # colocation statement. self.assertEqual("ops_test.py", os.path.basename(metadata.filename)) + @test_util.run_deprecated_v1 def testColocationDeviceInteraction(self): with ops.device("/cpu:0"): with ops.device("/device:GPU:0"): @@ -2675,6 +2756,7 @@ class ColocationGroupTest(test_util.TensorFlowTestCase): self.assertEqual([b"loc:@a"], b.op.colocation_groups()) self.assertEqual(a.op.device, b.op.device) + @test_util.run_deprecated_v1 def testColocationCanonicalization(self): with ops.device("/device:GPU:0"): _ = constant_op.constant(2.0) @@ -2690,6 +2772,7 @@ class ColocationGroupTest(test_util.TensorFlowTestCase): # inherits B's device name, after canonicalizing the names. self.assertEqual(b.op.device, c.op.device) + @test_util.run_deprecated_v1 def testLocationOverrides(self): with ops.device("/cpu:0"): with ops.device("/device:GPU:0"): @@ -2711,6 +2794,7 @@ class ColocationGroupTest(test_util.TensorFlowTestCase): self.assertEqual("/device:GPU:0", c.op.device) self.assertEqual("/device:CPU:0", d.op.device) + @test_util.run_deprecated_v1 def testNestedColocateWith(self): a = constant_op.constant([2.0], name="a") with ops.colocate_with(a.op): @@ -2720,6 +2804,7 @@ class ColocationGroupTest(test_util.TensorFlowTestCase): self.assertEqual([b"loc:@a"], b.op.colocation_groups()) self.assertEqual([b"loc:@a"], c.op.colocation_groups()) + @test_util.run_deprecated_v1 def testMultiColocationGroups(self): a = constant_op.constant([2.0], name="a") b = constant_op.constant(3.0, name="b") @@ -2728,6 +2813,7 @@ class ColocationGroupTest(test_util.TensorFlowTestCase): c = constant_op.constant(4.0) self.assertEqual(set([b"loc:@a", b"loc:@b"]), set(c.op.colocation_groups())) + @test_util.run_deprecated_v1 def testColocationIgnoreStack(self): a = constant_op.constant([2.0], name="a") b = constant_op.constant(3.0, name="b") @@ -2736,6 +2822,7 @@ class ColocationGroupTest(test_util.TensorFlowTestCase): c = constant_op.constant(4.0) self.assertEqual(set([b"loc:@b"]), set(c.op.colocation_groups())) + @test_util.run_deprecated_v1 def testColocateWithReset(self): a = constant_op.constant([2.0], name="a") with ops.colocate_with(a.op): @@ -2745,6 +2832,7 @@ class ColocationGroupTest(test_util.TensorFlowTestCase): self.assertEqual([b"loc:@a"], b.op.colocation_groups()) self.assertEqual([b"loc:@c"], c.op.colocation_groups()) + @test_util.run_deprecated_v1 def testColocateWithInitialNoneThenNested(self): a = constant_op.constant([2.0], name="a") with ops.colocate_with(a.op): @@ -2755,47 +2843,13 @@ class ColocationGroupTest(test_util.TensorFlowTestCase): self.assertEqual([b"loc:@b"], b.op.colocation_groups()) self.assertEqual([b"loc:@b"], c.op.colocation_groups()) + @test_util.run_deprecated_v1 def testColocateVariables(self): a = variables.Variable([2.0], name="a") with ops.colocate_with(a.op): b = variables.Variable([3.0], name="b") self.assertEqual([b"loc:@a"], b.op.colocation_groups()) - def testInconsistentDeviceWithinColocate(self): - with ops.device("/device:GPU:0"): - a = constant_op.constant([2.0], name="a") - with ops.colocate_with(a.op): - # This is allowed due to legacy but clearly wrong, since we - # should really be colocating with 'a'. We allow devices to - # override colocate_with, but we log warnings to suggest that - # this is probably unintentional or misguided. - with ops.device("/cpu:0"): - b = constant_op.constant([3.0], name="b") - - self.assertEqual("/device:CPU:0", b.device) - - def testMakeColocationConflictMessage(self): - """Test that provides an example of a complicated error message.""" - # We could test the message with any ops, but this test will be more - # instructive with a real colocation conflict. - with ops.device("/device:GPU:0"): - a = constant_op.constant([2.0], name="a") - with ops.colocate_with(a.op): - with ops.device("/cpu:0"): - b = constant_op.constant([3.0], name="b") - # The definition-location of the nodes will be wrong because of running - # from within a TF unittest. The rest of the info should be correct. - message = ops.get_default_graph()._make_colocation_conflict_message(a.op, - b.op) - self.assertRegexpMatches(message, - r"Tried to colocate op 'a' \(defined at.*\)") - self.assertRegexpMatches(message, "No node-device.*'a'") - self.assertRegexpMatches(message, "Device assignments active.*'a'") - self.assertRegexpMatches(message, "GPU:0") - self.assertRegexpMatches(message, "Node-device colocations active.*'b'") - self.assertRegexpMatches(message, "Device assignments active.*'b'") - self.assertRegexpMatches(message, "cpu:0") - class DeprecatedTest(test_util.TensorFlowTestCase): @@ -2918,6 +2972,7 @@ class NameScopeTest(test_util.TensorFlowTestCase): class TracebackTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testTracebackWithStartLines(self): with self.cached_session() as sess: a = constant_op.constant(2.0) @@ -2939,6 +2994,7 @@ class TracebackTest(test_util.TensorFlowTestCase): class EnableEagerExecutionTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testBadArgumentsToEnableEagerExecution(self): with self.assertRaisesRegexp(TypeError, "config must be a tf.ConfigProto"): ops.enable_eager_execution(context.DEVICE_PLACEMENT_SILENT) diff --git a/tensorflow/python/framework/python_op_gen.cc b/tensorflow/python/framework/python_op_gen.cc index 465016b808..d91f7b0bdd 100644 --- a/tensorflow/python/framework/python_op_gen.cc +++ b/tensorflow/python/framework/python_op_gen.cc @@ -142,6 +142,7 @@ class GenEagerPythonOp : public python_op_gen_internal::GenPythonOp { void AddEagerAttrs(const string& indentation); void AddEagerExecute(const string& indentation, const string& num_outputs_expr); + void AddDispatch(const string& prefix); void AddAttrForArg(const string& attr, int arg_index) { gtl::InsertIfNotPresent(&inferred_attrs_, attr, @@ -356,9 +357,14 @@ string GenEagerPythonOp::Code() { void GenEagerPythonOp::HandleGraphMode(const string& function_setup) { strings::StrAppend(&result_, " # Add nodes to the TensorFlow graph.\n"); - strings::StrAppend(&result_, function_setup, - " _, _, _op = _op_def_lib._apply_op_helper(\n"); - AddBodyNoReturn(" "); + strings::StrAppend(&result_, function_setup); + if (api_def_.visibility() == ApiDef::VISIBLE) { + strings::StrAppend(&result_, " try:\n "); + } + strings::StrAppend(&result_, " _, _, _op = _op_def_lib._apply_op_helper(\n"); + AddBodyNoReturn(strings::StrCat(" \"", op_def_.name(), "\", ")); + AddDispatch(" "); + if (num_outs_ > 0) { strings::StrAppend(&result_, " _result = _op.outputs[:]\n"); // Special case handling for stateful op with single list output @@ -628,6 +634,7 @@ void GenEagerPythonOp::AddEagerFunctionTeardown( bool GenEagerPythonOp::AddEagerFastPathAndGraphCode( const string& parameters, const std::vector& output_sizes, const string& eager_not_allowed_error) { + strings::StrAppend(&result_, "@_dispatch.add_dispatch_list\n"); AddExport(); AddDefLine(function_name_, parameters); AddDocStringDescription(); @@ -758,6 +765,7 @@ void GenEagerPythonOp::AddEagerFastPathExecute() { strings::StrAppend(&result_, " except _core._SymbolicException:\n"); strings::StrAppend(&result_, " pass # Add nodes to the TensorFlow graph.\n"); + AddDispatch(" "); // Any errors thrown from execute need to be unwrapped from // _NotOkStatusException. @@ -898,6 +906,19 @@ void GenEagerPythonOp::AddEagerExecute(const string& indentation, WordWrap(return_prefix, return_args, kRightMargin), "\n"); } +void GenEagerPythonOp::AddDispatch(const string& prefix) { + if (api_def_.visibility() != ApiDef::VISIBLE) return; + + strings::StrAppend(&result_, prefix, "except (TypeError, ValueError):\n"); + strings::StrAppend(&result_, prefix, " result = _dispatch.dispatch(\n"); + AddBodyNoReturn(strings::StrCat(prefix, " ", function_name_, ", ")); + strings::StrAppend(&result_, prefix, + " if result is not " + "_dispatch.OpDispatcher.NOT_SUPPORTED:\n"); + strings::StrAppend(&result_, prefix, " return result\n"); + strings::StrAppend(&result_, prefix, " raise\n"); +} + string GetPythonOps(const OpList& ops, const ApiDefMap& api_defs, const std::vector& hidden_ops, bool require_shapes, const string& source_file_name = "") { @@ -937,6 +958,7 @@ from tensorflow.python.framework import op_def_registry as _op_def_registry from tensorflow.python.framework import ops as _ops from tensorflow.python.framework import op_def_library as _op_def_library from tensorflow.python.util.deprecation import deprecated_endpoints +from tensorflow.python.util import dispatch as _dispatch from tensorflow.python.util.tf_export import tf_export )"); diff --git a/tensorflow/python/framework/python_op_gen_internal.cc b/tensorflow/python/framework/python_op_gen_internal.cc index 65b9ad5c6a..cbdeecfbfb 100644 --- a/tensorflow/python/framework/python_op_gen_internal.cc +++ b/tensorflow/python/framework/python_op_gen_internal.cc @@ -804,8 +804,8 @@ void GenPythonOp::AddDocStringOutputs() { } void GenPythonOp::AddBody(const string& prefix) { - const string apply_prefix = - strings::StrCat(prefix, "_result = _op_def_lib.apply_op("); + const string apply_prefix = strings::StrCat( + prefix, "_result = _op_def_lib.apply_op(\"", op_def_.name(), "\", "); AddBodyNoReturn(apply_prefix); if (num_outs_ > 1) { strings::StrAppend(&result_, prefix, "_result = _", op_def_.name(), @@ -815,7 +815,7 @@ void GenPythonOp::AddBody(const string& prefix) { } void GenPythonOp::AddBodyNoReturn(const string& apply_prefix) { - string args = strings::StrCat("\"", op_def_.name(), "\", "); + string args; for (size_t i = 0; i < param_names_.size(); ++i) { strings::StrAppend(&args, AvoidPythonReserved(param_names_[i].GetName()), "=", param_names_[i].GetRenameTo(), ", "); diff --git a/tensorflow/python/framework/random_seed.py b/tensorflow/python/framework/random_seed.py index 777bb2fe8c..6b7f56a92c 100644 --- a/tensorflow/python/framework/random_seed.py +++ b/tensorflow/python/framework/random_seed.py @@ -34,7 +34,7 @@ def _truncate_seed(seed): return seed % _MAXINT32 # Truncate to fit into 32-bit integer -@tf_export('random.get_seed', v1=['random.get_seed', 'get_seed']) +@tf_export(v1=['random.get_seed', 'get_seed']) @deprecation.deprecated_endpoints('get_seed') def get_seed(op_seed): """Returns the local seeds an operation should use given an op-specific seed. @@ -45,7 +45,7 @@ def get_seed(op_seed): graph, or for only specific operations. For details on how the graph-level seed interacts with op seeds, see - `tf.set_random_seed`. + `tf.random.set_random_seed`. Args: op_seed: integer. @@ -82,7 +82,7 @@ def get_seed(op_seed): return seeds -@tf_export('random.set_random_seed', 'set_random_seed') +@tf_export(v1=['random.set_random_seed', 'set_random_seed']) def set_random_seed(seed): """Sets the graph-level random seed. @@ -154,7 +154,7 @@ def set_random_seed(seed): sessions, set a graph-level seed: ```python - tf.set_random_seed(1234) + tf.random.set_random_seed(1234) a = tf.random_uniform([1]) b = tf.random_normal([1]) @@ -182,3 +182,103 @@ def set_random_seed(seed): context.set_global_seed(seed) else: ops.get_default_graph().seed = seed + + +@tf_export('random.set_seed', v1=[]) +def set_seed(seed): + """Sets the graph-level random seed. + + Operations that rely on a random seed actually derive it from two seeds: + the graph-level and operation-level seeds. This sets the graph-level seed. + + Its interactions with operation-level seeds is as follows: + + 1. If neither the graph-level nor the operation seed is set: + A random seed is used for this op. + 2. If the graph-level seed is set, but the operation seed is not: + The system deterministically picks an operation seed in conjunction + with the graph-level seed so that it gets a unique random sequence. + 3. If the graph-level seed is not set, but the operation seed is set: + A default graph-level seed and the specified operation seed are used to + determine the random sequence. + 4. If both the graph-level and the operation seed are set: + Both seeds are used in conjunction to determine the random sequence. + + To illustrate the user-visible effects, consider these examples: + + To generate different sequences across sessions, set neither + graph-level nor op-level seeds: + + ```python + a = tf.random_uniform([1]) + b = tf.random_normal([1]) + + print("Session 1") + with tf.Session() as sess1: + print(sess1.run(a)) # generates 'A1' + print(sess1.run(a)) # generates 'A2' + print(sess1.run(b)) # generates 'B1' + print(sess1.run(b)) # generates 'B2' + + print("Session 2") + with tf.Session() as sess2: + print(sess2.run(a)) # generates 'A3' + print(sess2.run(a)) # generates 'A4' + print(sess2.run(b)) # generates 'B3' + print(sess2.run(b)) # generates 'B4' + ``` + + To generate the same repeatable sequence for an op across sessions, set the + seed for the op: + + ```python + a = tf.random_uniform([1], seed=1) + b = tf.random_normal([1]) + + # Repeatedly running this block with the same graph will generate the same + # sequence of values for 'a', but different sequences of values for 'b'. + print("Session 1") + with tf.Session() as sess1: + print(sess1.run(a)) # generates 'A1' + print(sess1.run(a)) # generates 'A2' + print(sess1.run(b)) # generates 'B1' + print(sess1.run(b)) # generates 'B2' + + print("Session 2") + with tf.Session() as sess2: + print(sess2.run(a)) # generates 'A1' + print(sess2.run(a)) # generates 'A2' + print(sess2.run(b)) # generates 'B3' + print(sess2.run(b)) # generates 'B4' + ``` + + To make the random sequences generated by all ops be repeatable across + sessions, set a graph-level seed: + + ```python + tf.random.set_seed(1234) + a = tf.random_uniform([1]) + b = tf.random_normal([1]) + + # Repeatedly running this block with the same graph will generate the same + # sequences of 'a' and 'b'. + print("Session 1") + with tf.Session() as sess1: + print(sess1.run(a)) # generates 'A1' + print(sess1.run(a)) # generates 'A2' + print(sess1.run(b)) # generates 'B1' + print(sess1.run(b)) # generates 'B2' + + print("Session 2") + with tf.Session() as sess2: + print(sess2.run(a)) # generates 'A1' + print(sess2.run(a)) # generates 'A2' + print(sess2.run(b)) # generates 'B1' + print(sess2.run(b)) # generates 'B2' + ``` + + Args: + seed: integer. + """ + # TODO(go/tf2-random): change doc, update to match design doc + set_random_seed(seed) diff --git a/tensorflow/python/framework/registry.py b/tensorflow/python/framework/registry.py index 2e45acb499..4357c76bd6 100644 --- a/tensorflow/python/framework/registry.py +++ b/tensorflow/python/framework/registry.py @@ -23,10 +23,9 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -import traceback - from tensorflow.python.platform import tf_logging as logging from tensorflow.python.util import compat +from tensorflow.python.util import tf_stack # Registry mechanism below is based on mapreduce.python.mrpython.Register. @@ -57,15 +56,17 @@ class Registry(object): if name in self._registry: (filename, line_number, function_name, _) = ( self._registry[name][_LOCATION_TAG]) - raise KeyError("Registering two %s with name '%s' !" + raise KeyError("Registering two %s with name '%s'! " "(Previous registration was in %s %s:%d)" % (self._name, name, function_name, filename, line_number)) logging.vlog(1, "Registering %s (%s) in %s.", name, candidate, self._name) # stack trace is [this_function, Register(), user_function,...] # so the user function is #2. - stack = traceback.extract_stack() - self._registry[name] = {_TYPE_TAG: candidate, _LOCATION_TAG: stack[2]} + stack = tf_stack.extract_stack() + user_function = stack[2] + location_tag = tf_stack.convert_stack([user_function])[0] + self._registry[name] = {_TYPE_TAG: candidate, _LOCATION_TAG: location_tag} def list(self): """Lists registered items. diff --git a/tensorflow/python/framework/registry_test.py b/tensorflow/python/framework/registry_test.py index a821e16f26..1a0d3f200d 100644 --- a/tensorflow/python/framework/registry_test.py +++ b/tensorflow/python/framework/registry_test.py @@ -45,7 +45,9 @@ class RegistryTest(test.TestCase): def testDuplicate(self): myreg = registry.Registry('testbar') myreg.register(bar, 'Bar') - with self.assertRaises(KeyError): + with self.assertRaisesRegexp( + KeyError, r'Registering two testbar with name \'Bar\'! ' + r'\(Previous registration was in [^ ]+ .*.py:[0-9]+\)'): myreg.register(bar, 'Bar') diff --git a/tensorflow/python/framework/smart_cond_test.py b/tensorflow/python/framework/smart_cond_test.py index b8a9672b06..f964c87f02 100644 --- a/tensorflow/python/framework/smart_cond_test.py +++ b/tensorflow/python/framework/smart_cond_test.py @@ -35,6 +35,7 @@ def raise_exception(): class SmartCondTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testTrue(self): with ops.Graph().as_default(): with session.Session(): @@ -44,6 +45,7 @@ class SmartCondTest(test_util.TensorFlowTestCase): lambda: math_ops.multiply(y, 5)) self.assertEqual(z.eval(), 32) + @test_util.run_deprecated_v1 def testFalse(self): with ops.Graph().as_default(): with session.Session(): @@ -99,6 +101,7 @@ class SmartCondTest(test_util.TensorFlowTestCase): class SmartCaseTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testTrue(self): x = array_ops.placeholder(dtype=dtypes.int32, shape=[]) conditions = [(True, lambda: constant_op.constant(1)), @@ -109,9 +112,10 @@ class SmartCaseTest(test_util.TensorFlowTestCase): exclusive=True) with session.Session() as sess: # No feed_dict necessary - self.assertEqual(sess.run(y), 1) - self.assertEqual(sess.run(z), 1) + self.assertEqual(self.evaluate(y), 1) + self.assertEqual(self.evaluate(z), 1) + @test_util.run_deprecated_v1 def testFalse(self): conditions = [(False, raise_exception)] y = smart_cond.smart_case(conditions, @@ -121,9 +125,10 @@ class SmartCaseTest(test_util.TensorFlowTestCase): default=lambda: constant_op.constant(1), exclusive=True) with session.Session() as sess: - self.assertEqual(sess.run(y), 1) - self.assertEqual(sess.run(z), 1) + self.assertEqual(self.evaluate(y), 1) + self.assertEqual(self.evaluate(z), 1) + @test_util.run_deprecated_v1 def testMix(self): x = array_ops.placeholder(dtype=dtypes.int32, shape=[]) y = constant_op.constant(10) diff --git a/tensorflow/python/framework/sparse_tensor_test.py b/tensorflow/python/framework/sparse_tensor_test.py index 22423c4f58..a999c12ca8 100644 --- a/tensorflow/python/framework/sparse_tensor_test.py +++ b/tensorflow/python/framework/sparse_tensor_test.py @@ -46,11 +46,11 @@ class SparseTensorTest(test_util.TensorFlowTestCase): self.assertEqual(sp.get_shape(), (4, 5)) with self.cached_session() as sess: - value = sp.eval() + value = self.evaluate(sp) self.assertAllEqual(indices, value.indices) self.assertAllEqual(values, value.values) self.assertAllEqual(shape, value.dense_shape) - sess_run_value = sess.run(sp) + sess_run_value = self.evaluate(sp) self.assertAllEqual(sess_run_value.indices, value.indices) self.assertAllEqual(sess_run_value.values, value.values) self.assertAllEqual(sess_run_value.dense_shape, value.dense_shape) @@ -65,6 +65,7 @@ class SparseTensorTest(test_util.TensorFlowTestCase): sparse_tensor.is_sparse( sparse_tensor.SparseTensorValue([[0]], [0], [1]))) + @test_util.run_deprecated_v1 def testConsumers(self): sp = sparse_tensor.SparseTensor([[0, 0], [1, 2]], [1.0, 3.0], [3, 4]) w = ops.convert_to_tensor(np.ones([4, 1], np.float32)) @@ -85,8 +86,9 @@ class ConvertToTensorOrSparseTensorTest(test_util.TensorFlowTestCase): value = [42, 43] from_value = sparse_tensor.convert_to_tensor_or_sparse_tensor( value) - self.assertAllEqual(value, from_value.eval()) + self.assertAllEqual(value, self.evaluate(from_value)) + @test_util.run_deprecated_v1 def test_convert_sparse(self): with self.cached_session(): indices = [[0, 1], [1, 0]] diff --git a/tensorflow/python/framework/subscribe_test.py b/tensorflow/python/framework/subscribe_test.py index cab426844d..61c6ea6519 100644 --- a/tensorflow/python/framework/subscribe_test.py +++ b/tensorflow/python/framework/subscribe_test.py @@ -43,6 +43,7 @@ class SubscribeTest(test_util.TensorFlowTestCase): self.assertTrue( all(subscribe._is_subscribed_identity(x) for x in container)) + @test_util.run_deprecated_v1 def testSideEffect(self): a = constant_op.constant(1) b = constant_op.constant(1) @@ -66,15 +67,16 @@ class SubscribeTest(test_util.TensorFlowTestCase): self.assertTrue(c.op in d.op.control_inputs) with self.cached_session() as sess: - c_out = sess.run([c]) - n_out = sess.run([n]) - d_out = sess.run([d]) + c_out = self.evaluate([c]) + n_out = self.evaluate([n]) + d_out = self.evaluate([d]) self.assertEqual(n_out, [-2]) self.assertEqual(c_out, [2]) self.assertEqual(d_out, [42]) self.assertEqual(shared, [2, 2, 2]) + @test_util.run_deprecated_v1 def testSupportedTypes(self): """Confirm that supported types are correctly detected and handled.""" @@ -120,6 +122,7 @@ class SubscribeTest(test_util.TensorFlowTestCase): subscribe.subscribe(c.name, lambda t: script_ops.py_func(sub, [t], [t.dtype])) + @test_util.run_deprecated_v1 def testCaching(self): """Confirm caching of control output is recalculated between calls.""" a = constant_op.constant(1) @@ -145,13 +148,14 @@ class SubscribeTest(test_util.TensorFlowTestCase): lambda t: script_ops.py_func(sub, [t], [t.dtype])) with self.cached_session() as sess: - c_out = sess.run([c]) - d_out = sess.run([d]) + c_out = self.evaluate([c]) + d_out = self.evaluate([d]) self.assertEqual(c_out, [42]) self.assertEqual(d_out, [11]) self.assertEqual(shared, {2: 1, 1: 1}) + @test_util.run_deprecated_v1 def testIsSubscribedIdentity(self): """Confirm subscribed identity ops are correctly detected.""" a = constant_op.constant(1) @@ -165,6 +169,7 @@ class SubscribeTest(test_util.TensorFlowTestCase): self.assertFalse(subscribe._is_subscribed_identity(idop)) self.assertTrue(subscribe._is_subscribed_identity(c_sub)) + @test_util.run_deprecated_v1 def testSubscribeExtend(self): """Confirm side effect are correctly added for different input types.""" a = constant_op.constant(1) @@ -205,11 +210,12 @@ class SubscribeTest(test_util.TensorFlowTestCase): # Expect the three side effect graphs to have been evaluated. with self.cached_session() as sess: - sess.run([c_sub]) + self.evaluate([c_sub]) self.assertIn('graph1', shared) self.assertIn('graph2', shared) self.assertIn('graph3', shared) + @test_util.run_deprecated_v1 def testSubscribeVariable(self): """Confirm that variables can be subscribed.""" v1 = variables.VariableV1(0.0) @@ -229,25 +235,26 @@ class SubscribeTest(test_util.TensorFlowTestCase): with self.cached_session() as sess: # Initialize the variables first. - sess.run([v1.initializer]) - sess.run([v2.initializer]) + self.evaluate([v1.initializer]) + self.evaluate([v2.initializer]) # Expect the side effects to be triggered when evaluating the add op as # it will read the value of the variable. - sess.run([add]) + self.evaluate([add]) self.assertEqual(1, len(shared)) # Expect the side effect not to be triggered when evaluating the assign # op as it will not access the 'read' output of the variable. - sess.run([assign_v1]) + self.evaluate([assign_v1]) self.assertEqual(1, len(shared)) - sess.run([add]) + self.evaluate([add]) self.assertEqual(2, len(shared)) # Make sure the values read from the variable match the expected ones. self.assertEqual([0.0, 3.0], shared) + @test_util.run_deprecated_v1 def testResourceType(self): """Confirm that subscribe correctly handles tensors with 'resource' type.""" tensor_array = tensor_array_ops.TensorArray( @@ -273,9 +280,10 @@ class SubscribeTest(test_util.TensorFlowTestCase): self.assertFalse(subscribe._is_subscribed_identity(tensor_array.handle)) with self.cached_session() as sess: - sess.run([reader]) + self.evaluate([reader]) self.assertEqual(0, len(shared)) + @test_util.run_deprecated_v1 def testMultipleOutputs(self): """Handle subscriptions to multiple outputs from the same op.""" sparse_tensor_1 = sparse_tensor.SparseTensor( @@ -304,11 +312,12 @@ class SubscribeTest(test_util.TensorFlowTestCase): lambda t: script_ops.py_func(sub, [t], [t.dtype])) with self.cached_session() as sess: - sess.run([neg]) + self.evaluate([neg]) # All three ops have been processed. self.assertEqual(3, len(shared)) + @test_util.run_deprecated_v1 def test_subscribe_tensors_on_different_devices(self): """Side effect ops are added with the same device of the subscribed op.""" c1 = constant_op.constant(10) @@ -335,6 +344,7 @@ class SubscribeTest(test_util.TensorFlowTestCase): self.assertEqual(add.device, add_sub.device) self.assertEqual(mul.device, mul_sub.device) + @test_util.run_deprecated_v1 def test_subscribe_tensors_within_control_flow_context(self): """Side effect ops are added with the same control flow context.""" c1 = constant_op.constant(10) @@ -375,7 +385,7 @@ class SubscribeTest(test_util.TensorFlowTestCase): self.assertIsNot(context(subscriptions[0]), context(subscriptions[1])) with self.cached_session() as sess: - sess.run(cond) + self.evaluate(cond) self.assertEqual(3, len(results)) diff --git a/tensorflow/python/framework/tensor_shape.py b/tensorflow/python/framework/tensor_shape.py index 5a58d27148..960a3dad73 100644 --- a/tensorflow/python/framework/tensor_shape.py +++ b/tensorflow/python/framework/tensor_shape.py @@ -169,7 +169,7 @@ def dimension_at_index(shape, index): return shape.dims[index] -@tf_export("Dimension") +@tf_export(v1=["Dimension"]) class Dimension(object): """Represents the value of one dimension in a TensorShape.""" diff --git a/tensorflow/python/framework/tensor_spec.py b/tensorflow/python/framework/tensor_spec.py index fbea930fe0..c44636edc4 100644 --- a/tensorflow/python/framework/tensor_spec.py +++ b/tensorflow/python/framework/tensor_spec.py @@ -24,14 +24,15 @@ from tensorflow.python.framework import common_shapes from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape +from tensorflow.python.util.tf_export import tf_export +@tf_export("TensorSpec") class TensorSpec(object): """Describes a tf.Tensor. - A TensorSpec allows an API to describe the Tensors that it accepts or - returns, before that Tensor exists. This allows dynamic and flexible graph - construction and configuration. + Metadata for describing the `tf.Tensor` objects accepted or returned + by some TensorFlow APIs. """ __slots__ = ["_shape", "_shape_tuple", "_dtype", "_name"] @@ -69,11 +70,6 @@ class TensorSpec(object): else: raise ValueError("`tensor` should be a tf.Tensor") - @classmethod - def is_bounded(cls): - del cls - return False - @property def shape(self): """Returns the `TensorShape` that represents the shape of the tensor.""" @@ -86,21 +82,21 @@ class TensorSpec(object): @property def name(self): - """Returns the name of the described tensor.""" + """Returns the (optionally provided) name of the described tensor.""" return self._name - @property - def is_discrete(self): - """Whether spec is discrete.""" - return self.dtype.is_integer + def is_compatible_with(self, spec_or_tensor): + """Returns True if spec_or_tensor is compatible with this TensorSpec. - @property - def is_continuous(self): - """Whether spec is continuous.""" - return self.dtype.is_floating + Two tensors are considered compatible if they have the same dtype + and their shapes are compatible (see `tf.TensorShape.is_compatible_with`). - def is_compatible_with(self, spec_or_tensor): - """True if the shape and dtype of `spec_or_tensor` are compatible.""" + Args: + spec_or_tensor: A tf.TensorSpec or a tf.Tensor + + Returns: + True if spec_or_tensor is compatible with self. + """ return (self._dtype.is_compatible_with(spec_or_tensor.dtype) and self._shape.is_compatible_with(spec_or_tensor.shape)) @@ -188,11 +184,6 @@ class BoundedTensorSpec(TensorSpec): self._maximum = np.array(maximum, dtype=self.dtype.as_numpy_dtype()) self._maximum.setflags(write=False) - @classmethod - def is_bounded(cls): - del cls - return True - @classmethod def from_spec(cls, spec): dtype = dtypes.as_dtype(spec.dtype) @@ -223,4 +214,3 @@ class BoundedTensorSpec(TensorSpec): def __reduce__(self): return BoundedTensorSpec, (self._shape, self._dtype, self._minimum, self._maximum, self._name) - diff --git a/tensorflow/python/framework/tensor_spec_test.py b/tensorflow/python/framework/tensor_spec_test.py index 40611e5f84..75c197df09 100644 --- a/tensorflow/python/framework/tensor_spec_test.py +++ b/tensorflow/python/framework/tensor_spec_test.py @@ -45,6 +45,7 @@ class TensorSpecTest(test_util.TensorFlowTestCase): desc = tensor_spec.TensorSpec(shape=None, dtype=dtypes.float32) self.assertEqual(desc.shape, tensor_shape.TensorShape(None)) + @test_util.run_deprecated_v1 def testShapeCompatibility(self): unknown = array_ops.placeholder(dtypes.int64) partial = array_ops.placeholder(dtypes.int64, shape=[None, 1]) @@ -75,6 +76,7 @@ class TensorSpecTest(test_util.TensorFlowTestCase): self.assertFalse(desc_rank3.is_compatible_with(full)) self.assertTrue(desc_rank3.is_compatible_with(rank3)) + @test_util.run_deprecated_v1 def testTypeCompatibility(self): floats = array_ops.placeholder(dtypes.float32, shape=[10, 10]) ints = array_ops.placeholder(dtypes.int32, shape=[10, 10]) @@ -106,6 +108,7 @@ class TensorSpecTest(test_util.TensorFlowTestCase): spec_2 = tensor_spec.TensorSpec.from_spec(spec_1) self.assertEqual(spec_1, spec_2) + @test_util.run_deprecated_v1 def testFromTensor(self): zero = constant_op.constant(0) spec = tensor_spec.TensorSpec.from_tensor(zero) @@ -113,6 +116,7 @@ class TensorSpecTest(test_util.TensorFlowTestCase): self.assertEqual(spec.shape, []) self.assertEqual(spec.name, "Const") + @test_util.run_deprecated_v1 def testFromPlaceholder(self): unknown = array_ops.placeholder(dtypes.int64, name="unknown") partial = array_ops.placeholder(dtypes.float32, @@ -134,22 +138,6 @@ class TensorSpecTest(test_util.TensorFlowTestCase): self.assertEqual(bounded_spec.dtype, spec.dtype) self.assertEqual(bounded_spec.name, spec.name) - def testIsDiscrete(self): - discrete_spec = tensor_spec.TensorSpec((1, 2), dtypes.int32) - continuous_spec = tensor_spec.TensorSpec((1, 2), dtypes.float32) - self.assertTrue(discrete_spec.is_discrete) - self.assertFalse(continuous_spec.is_discrete) - - def testIsContinuous(self): - discrete_spec = tensor_spec.TensorSpec((1, 2), dtypes.int32) - continuous_spec = tensor_spec.TensorSpec((1, 2), dtypes.float32) - self.assertFalse(discrete_spec.is_continuous) - self.assertTrue(continuous_spec.is_continuous) - - def testIsBounded(self): - unbounded_spec = tensor_spec.TensorSpec((1, 2), dtypes.int32) - self.assertFalse(unbounded_spec.is_bounded()) - def testSerialization(self): desc = tensor_spec.TensorSpec([1, 5], dtypes.float32, "test") self.assertEqual(pickle.loads(pickle.dumps(desc)), desc) @@ -165,11 +153,6 @@ class BoundedTensorSpecTest(test_util.TensorFlowTestCase): with self.assertRaisesRegexp(ValueError, "not compatible"): tensor_spec.BoundedTensorSpec((3, 5), dtypes.uint8, 0, (1, 1, 1)) - def testIsBounded(self): - bounded_spec = tensor_spec.BoundedTensorSpec( - (1, 2), dtypes.int32, minimum=0, maximum=1) - self.assertTrue(bounded_spec.is_bounded()) - def testMinimumMaximumAttributes(self): spec = tensor_spec.BoundedTensorSpec( (1, 2, 3), dtypes.float32, 0, (5, 5, 5)) diff --git a/tensorflow/python/framework/tensor_util.py b/tensorflow/python/framework/tensor_util.py index 9db94f5288..f98f301b38 100644 --- a/tensorflow/python/framework/tensor_util.py +++ b/tensorflow/python/framework/tensor_util.py @@ -371,8 +371,10 @@ def _AssertCompatible(values, dtype): (dtype.name, repr(mismatch), type(mismatch).__name__)) +# pylint: disable=invalid-name @tf_export(v1=["make_tensor_proto"]) -def make_tensor_proto(values, dtype=None, shape=None, verify_shape=False): +def make_tensor_proto(values, dtype=None, shape=None, verify_shape=False, + allow_broadcast=False): """Create a TensorProto. Args: @@ -380,6 +382,8 @@ def make_tensor_proto(values, dtype=None, shape=None, verify_shape=False): dtype: Optional tensor_pb2 DataType value. shape: List of integers representing the dimensions of tensor. verify_shape: Boolean that enables verification of a shape of values. + allow_broadcast:Boolean that enables allowing scalars and 1 length vector + broadcasting. Cannot be true when verify_shape is true. Returns: A `TensorProto`. Depending on the type, it may contain data in the @@ -416,6 +420,8 @@ def make_tensor_proto(values, dtype=None, shape=None, verify_shape=False): can not have more elements than what "shape" specifies. """ + if allow_broadcast and verify_shape: + raise ValueError("allow_broadcast and verify_shape are not both allowed.") if isinstance(values, tensor_pb2.TensorProto): return values @@ -504,15 +510,22 @@ def make_tensor_proto(values, dtype=None, shape=None, verify_shape=False): shape_size = np.prod(shape, dtype=np.int64) is_same_size = shape_size == nparray.size - if verify_shape: - if not nparray.shape == tuple(shape): + if allow_broadcast: + if nparray.shape == (1,) or nparray.shape == tuple(): + pass + elif nparray.size != shape_size: raise TypeError("Expected Tensor's shape: %s, got %s." % (tuple(shape), nparray.shape)) - if nparray.size > shape_size: - raise ValueError( - "Too many elements provided. Needed at most %d, but received %d" % - (shape_size, nparray.size)) + else: + if verify_shape and nparray.shape != tuple(shape): + raise TypeError("Expected Tensor's shape: %s, got %s." % + (tuple(shape), nparray.shape)) + + if nparray.size > shape_size: + raise ValueError( + "Too many elements provided. Needed at most %d, but received %d" % + (shape_size, nparray.size)) tensor_proto = tensor_pb2.TensorProto( dtype=numpy_dtype.as_datatype_enum, @@ -560,6 +573,7 @@ def make_tensor_proto(values, dtype=None, shape=None, verify_shape=False): append_fn(tensor_proto, proto_values) return tensor_proto +# pylint: enable=invalid-name @tf_export("make_ndarray") diff --git a/tensorflow/python/framework/tensor_util_test.py b/tensorflow/python/framework/tensor_util_test.py index bdf759f220..0033754618 100644 --- a/tensorflow/python/framework/tensor_util_test.py +++ b/tensorflow/python/framework/tensor_util_test.py @@ -758,6 +758,7 @@ class TensorUtilTest(test.TestCase): self.assertFalse(tensor_util.ShapeEquals(t, [1, 4])) self.assertFalse(tensor_util.ShapeEquals(t, [4])) + @test_util.run_deprecated_v1 def testMockArray(self): class MockArray(object): @@ -771,7 +772,7 @@ class TensorUtilTest(test.TestCase): with self.cached_session() as sess: ma = MockArray(np.array([10, 20, 30])) t = ops.convert_to_tensor(ma) - a = sess.run(t) + a = self.evaluate(t) self.assertEquals(np.int64, a.dtype) self.assertAllClose(np.array([10, 20, 30], dtype=np.int64), a) @@ -787,6 +788,7 @@ class ConstantValueTest(test.TestCase): tf_val = constant_op.constant(np_val) self.assertAllClose(np_val, tensor_util.constant_value(tf_val)) + @test_util.run_deprecated_v1 def testUnknown(self): tf_val = gen_state_ops.variable( shape=[3, 4, 7], @@ -815,12 +817,14 @@ class ConstantValueTest(test.TestCase): c_val = tensor_util.constant_value(tf_val) self.assertEqual(6, c_val) + @test_util.run_deprecated_v1 def testSizeOfScalar(self): tf_val = array_ops.size(constant_op.constant(0.0)) c_val = tensor_util.constant_value(tf_val) self.assertEqual(1, c_val) self.assertEqual(np.ndarray, type(c_val)) + @test_util.run_deprecated_v1 def testRank(self): tf_val = array_ops.rank(constant_op.constant(0.0, shape=[1, 2, 3])) c_val = tensor_util.constant_value(tf_val) @@ -852,6 +856,7 @@ class ConstantValueTest(test.TestCase): c_val = tensor_util.constant_value(tf_val) self.assertAllClose(np_val.astype(np.float64), c_val) + @test_util.run_deprecated_v1 def testConcat(self): np_val = np.random.rand(3, 4, 7).astype(np.float32) tf_val = array_ops.concat( @@ -871,6 +876,7 @@ class ConstantValueTest(test.TestCase): c_val = tensor_util.constant_value(tf_val) self.assertIs(None, c_val) + @test_util.run_deprecated_v1 def testPack_Axis0(self): inputs = [np.random.rand(4, 7) for _ in range(3)] np_val = np.array(inputs) @@ -883,6 +889,7 @@ class ConstantValueTest(test.TestCase): c_val = tensor_util.constant_value(tf_val) self.assertIs(None, c_val) + @test_util.run_deprecated_v1 def testPack_Axis1(self): inputs = [np.random.rand(4, 7) for _ in range(3)] tf_val = array_ops.stack(inputs, axis=1) @@ -894,6 +901,7 @@ class ConstantValueTest(test.TestCase): c_val = tensor_util.constant_value(tf_val) self.assertIs(None, c_val) + @test_util.run_deprecated_v1 def testPack_Partial_Axis0(self): input_ = np.random.rand(4, 7) tf_val = array_ops.stack([input_, array_ops.placeholder(dtypes.float32)]) @@ -901,6 +909,7 @@ class ConstantValueTest(test.TestCase): self.assertAllClose(input_, c_val[0]) self.assertIsNone(c_val[1]) + @test_util.run_deprecated_v1 def testPack_Partial_Axis1(self): input_ = np.random.rand(4, 7) tf_val = array_ops.stack([input_, array_ops.placeholder(dtypes.float32)], @@ -966,12 +975,14 @@ class ConstantValueAsShapeTest(test.TestCase): c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual([None, 1, None], c_val.as_list()) + @test_util.run_deprecated_v1 def testPack(self): tf_val = array_ops.stack( [constant_op.constant(16), 37, array_ops.placeholder(dtypes.int32)]) c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual([16, 37, None], c_val.as_list()) + @test_util.run_deprecated_v1 def testConcat(self): tf_val = array_ops.concat( [[16, 37], array_ops.placeholder( @@ -985,6 +996,7 @@ class ConstantValueAsShapeTest(test.TestCase): c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual([16, 37, None, 48], c_val.as_list()) + @test_util.run_deprecated_v1 def testSlice(self): tf_val = array_ops.placeholder(dtypes.int32, shape=(4,))[0:2] c_val = tensor_util.constant_value_as_shape(tf_val) diff --git a/tensorflow/python/framework/test_util.py b/tensorflow/python/framework/test_util.py index fd55ad2af9..fc1a5fbe85 100644 --- a/tensorflow/python/framework/test_util.py +++ b/tensorflow/python/framework/test_util.py @@ -50,6 +50,7 @@ from tensorflow.core.framework import graph_pb2 from tensorflow.core.protobuf import config_pb2 from tensorflow.core.protobuf import rewriter_config_pb2 from tensorflow.python import pywrap_tensorflow +from tensorflow.python import tf2 from tensorflow.python.client import device_lib from tensorflow.python.client import session from tensorflow.python.eager import context @@ -66,6 +67,7 @@ from tensorflow.python.framework import tensor_shape from tensorflow.python.framework import versions from tensorflow.python.ops import array_ops from tensorflow.python.ops import control_flow_ops +from tensorflow.python.ops import tensor_array_ops from tensorflow.python.ops import variables from tensorflow.python.platform import googletest from tensorflow.python.platform import tf_logging as logging @@ -114,8 +116,28 @@ def assert_ops_in_graph(expected_ops, graph): return actual_ops -@tf_export("test.assert_equal_graph_def") -def assert_equal_graph_def(actual, expected, checkpoint_v2=False): +@tf_export("test.assert_equal_graph_def", v1=[]) +def assert_equal_graph_def_v2(actual, expected): + """Asserts that two `GraphDef`s are (mostly) the same. + + Compares two `GraphDef` protos for equality, ignoring versions and ordering of + nodes, attrs, and control inputs. Node names are used to match up nodes + between the graphs, so the naming of nodes must be consistent. This function + ignores randomized attribute values that may appear in V2 checkpoints. + + Args: + actual: The `GraphDef` we have. + expected: The `GraphDef` we expected. + + Raises: + AssertionError: If the `GraphDef`s do not match. + TypeError: If either argument is not a `GraphDef`. + """ + assert_equal_graph_def(actual, expected, checkpoint_v2=True) + + +@tf_export(v1=["test.assert_equal_graph_def"]) +def assert_equal_graph_def_v1(actual, expected, checkpoint_v2=False): """Asserts that two `GraphDef`s are (mostly) the same. Compares two `GraphDef` protos for equality, ignoring versions and ordering of @@ -132,6 +154,10 @@ def assert_equal_graph_def(actual, expected, checkpoint_v2=False): AssertionError: If the `GraphDef`s do not match. TypeError: If either argument is not a `GraphDef`. """ + assert_equal_graph_def(actual, expected, checkpoint_v2) + + +def assert_equal_graph_def(actual, expected, checkpoint_v2=False): if not isinstance(actual, graph_pb2.GraphDef): raise TypeError( "Expected tf.GraphDef for actual, got %s" % type(actual).__name__) @@ -354,53 +380,12 @@ def skip_if(condition): def enable_c_shapes(fn): - """Decorator for enabling C shapes on a test. - - Note this enables the C shapes after running the test class's setup/teardown - methods. - - Args: - fn: the function to be wrapped - - Returns: - The wrapped function - """ - - # pylint: disable=protected-access - def wrapper(*args, **kwargs): - prev_value = ops._USE_C_SHAPES - ops._USE_C_SHAPES = True - try: - fn(*args, **kwargs) - finally: - ops._USE_C_SHAPES = prev_value - - # pylint: enable=protected-access - - return wrapper + """No-op. TODO(b/74620627): Remove this.""" + return fn def with_c_shapes(cls): - """Adds methods that call original methods but with C API shapes enabled. - - Note this enables C shapes in new methods after running the test class's - setup method. - - Args: - cls: class to decorate - - Returns: - cls with new test methods added - """ - # If C shapes are already enabled, don't do anything. Some tests break if the - # same test is run twice, so this allows us to turn on the C shapes by default - # without breaking these tests. - if ops._USE_C_SHAPES: - return cls - - for name, value in cls.__dict__.copy().items(): - if callable(value) and name.startswith("test"): - setattr(cls, name + "WithCShapes", enable_c_shapes(value)) + """No-op. TODO(b/74620627): Remove this.""" return cls @@ -423,13 +408,40 @@ def enable_control_flow_v2(fn): def wrapper(*args, **kwargs): enable_cond_v2_old = control_flow_ops.ENABLE_COND_V2 enable_while_v2_old = control_flow_ops.ENABLE_WHILE_V2 + enable_tensor_array_v2_old = tensor_array_ops.ENABLE_TENSOR_ARRAY_V2 control_flow_ops.ENABLE_COND_V2 = True control_flow_ops.ENABLE_WHILE_V2 = True + tensor_array_ops.ENABLE_TENSOR_ARRAY_V2 = True try: fn(*args, **kwargs) finally: control_flow_ops.ENABLE_COND_V2 = enable_cond_v2_old control_flow_ops.ENABLE_WHILE_V2 = enable_while_v2_old + tensor_array_ops.ENABLE_TENSOR_ARRAY_V2 = enable_tensor_array_v2_old + + return wrapper + + +def enable_tensor_array_v2(fn): + """Decorator for enabling _GraphTensorArrayV2 on a test. + + Note this enables _GraphTensorArrayV2 after running the test class's + setup/teardown methods. + + Args: + fn: the function to be wrapped + + Returns: + The wrapped function + """ + + def wrapper(*args, **kwargs): + enable_tensor_array_v2_old = tensor_array_ops.ENABLE_TENSOR_ARRAY_V2 + tensor_array_ops.ENABLE_TENSOR_ARRAY_V2 = True + try: + fn(*args, **kwargs) + finally: + tensor_array_ops.ENABLE_TENSOR_ARRAY_V2 = enable_tensor_array_v2_old return wrapper @@ -881,8 +893,8 @@ def run_all_in_graph_and_eager_modes(cls): """Execute all test methods in the given class with and without eager.""" base_decorator = run_in_graph_and_eager_modes for name, value in cls.__dict__.copy().items(): - if callable(value) and name.startswith( - "test") and not name.startswith("testSkipEager"): + if callable(value) and name.startswith("test") and not ( + name.startswith("testSkipEager") or name.startswith("test_skip_eager")): setattr(cls, name, base_decorator(value)) return cls @@ -949,7 +961,7 @@ def run_in_graph_and_eager_modes(func=None, def decorator(f): if tf_inspect.isclass(f): raise ValueError( - "`run_test_in_graph_and_eager_modes` only supports test methods. " + "`run_in_graph_and_eager_modes` only supports test methods. " "Did you mean to use `run_all_in_graph_and_eager_modes`?") def decorated(self, *args, **kwargs): @@ -994,6 +1006,174 @@ def run_in_graph_and_eager_modes(func=None, return decorator +def run_deprecated_v1(func=None): + """Execute the decorated test in graph mode. + + This function returns a decorator intended to be applied to tests that have + not been updated to a style that is compatible with both TensorFlow 1.x and + 2.x. When this decorated is applied, the test body will be run in + an environment where API calls construct graphs instead of executing eagerly. + + Args: + func: function to be annotated. If `func` is None, this method returns a + decorator the can be applied to a function. If `func` is not None this + returns the decorator applied to `func`. + Returns: + Returns a decorator that will run the decorated test method in graph mode. + """ + + def decorator(f): + if tf_inspect.isclass(f): + raise ValueError("`run_deprecated_v1` only supports test methods.") + + def decorated(self, *args, **kwargs): + if tf2.enabled(): + with context.graph_mode(): + f(self, *args, **kwargs) + else: + f(self, *args, **kwargs) + + return decorated + + if func is not None: + return decorator(func) + + return decorator + + +def run_v1_only(reason, func=None): + """Execute the decorated test only if running in v1 mode. + + This function is intended to be applied to tests that exercise v1 only + functionality. If the test is run in v2 mode it will simply be skipped. + + Args: + reason: string giving a reason for limiting the test to v1 only. + func: function to be annotated. If `func` is None, this method returns a + decorator the can be applied to a function. If `func` is not None this + returns the decorator applied to `func`. + + Returns: + Returns a decorator that will conditionally skip the decorated test method. + """ + + def decorator(f): + if tf_inspect.isclass(f): + raise ValueError("`run_v1_only` only supports test methods.") + + def decorated(self, *args, **kwargs): + if tf2.enabled(): + self.skipTest(reason) + + f(self, *args, **kwargs) + + return decorated + + if func is not None: + return decorator(func) + + return decorator + + +def run_v2_only(func=None): + """Execute the decorated test only if running in v2 mode. + + This function is intended to be applied to tests that exercise v2 only + functionality. If the test is run in v1 mode it will simply be skipped. + + Args: + func: function to be annotated. If `func` is None, this method returns a + decorator the can be applied to a function. If `func` is not None this + returns the decorator applied to `func`. + + Returns: + Returns a decorator that will conditionally skip the decorated test method. + """ + + def decorator(f): + if tf_inspect.isclass(f): + raise ValueError("`run_v2_only` only supports test methods.") + + def decorated(self, *args, **kwargs): + if not tf2.enabled(): + self.skipTest("Test is only comptaible in v2") + + f(self, *args, **kwargs) + + return decorated + + if func is not None: + return decorator(func) + + return decorator + + +def run_gpu_only(func=None): + """Execute the decorated test only if a GPU is available. + + This function is intended to be applied to tests that require the precense + of a GPU. If a GPU is absent, it will simply be skipped. + + Args: + func: function to be annotated. If `func` is None, this method returns a + decorator the can be applied to a function. If `func` is not None this + returns the decorator applied to `func`. + + Returns: + Returns a decorator that will conditionally skip the decorated test method. + """ + + def decorator(f): + if tf_inspect.isclass(f): + raise ValueError("`run_gpu_only` only supports test methods.") + + def decorated(self, *args, **kwargs): + if not is_gpu_available(): + self.skipTest("Test requires GPU") + + f(self, *args, **kwargs) + + return decorated + + if func is not None: + return decorator(func) + + return decorator + + +def run_cuda_only(func=None): + """Execute the decorated test only if a GPU is available. + + This function is intended to be applied to tests that require the precense + of a CUDA GPU. If a CUDA GPU is absent, it will simply be skipped. + + Args: + func: function to be annotated. If `func` is None, this method returns a + decorator the can be applied to a function. If `func` is not None this + returns the decorator applied to `func`. + + Returns: + Returns a decorator that will conditionally skip the decorated test method. + """ + + def decorator(f): + if tf_inspect.isclass(f): + raise ValueError("`run_cuda_only` only supports test methods.") + + def decorated(self, *args, **kwargs): + if not is_gpu_available(cuda_only=True): + self.skipTest("Test requires CUDA GPU") + + f(self, *args, **kwargs) + + return decorated + + if func is not None: + return decorator(func) + + return decorator + + @tf_export("test.is_gpu_available") def is_gpu_available(cuda_only=False, min_cuda_compute_capability=None): """Returns whether TensorFlow can access a GPU. @@ -1033,7 +1213,7 @@ def is_gpu_available(cuda_only=False, min_cuda_compute_capability=None): return True return False except errors_impl.NotFoundError as e: - if not all([x in str(e) for x in ["CUDA", "not find"]]): + if not all(x in str(e) for x in ["CUDA", "not find"]): raise e else: logging.error(str(e)) @@ -1051,6 +1231,27 @@ def device(use_gpu): yield +@contextlib.contextmanager +def use_gpu(): + """Uses gpu when requested and available.""" + with device(use_gpu=True): + yield + + +@contextlib.contextmanager +def force_gpu(): + """Force the gpu to be used.""" + with ops.device("/device:GPU:0"): + yield + + +@contextlib.contextmanager +def force_cpu(): + """Force the cpu to be used.""" + with ops.device("/device:CPU:0"): + yield + + class CapturedWrites(object): """A utility class to load the captured writes made to a stream.""" diff --git a/tensorflow/python/framework/test_util_test.py b/tensorflow/python/framework/test_util_test.py index cbefe86481..dfdced5a98 100644 --- a/tensorflow/python/framework/test_util_test.py +++ b/tensorflow/python/framework/test_util_test.py @@ -49,6 +49,7 @@ from tensorflow.python.platform import googletest class TestUtilTest(test_util.TensorFlowTestCase, parameterized.TestCase): + @test_util.run_deprecated_v1 def test_assert_ops_in_graph(self): with self.test_session(): constant_op.constant(["hello", "taffy"], name="hello") @@ -60,6 +61,7 @@ class TestUtilTest(test_util.TensorFlowTestCase, parameterized.TestCase): self.assertRaises(ValueError, test_util.assert_ops_in_graph, {"hello": "Variable"}, ops.get_default_graph()) + @test_util.run_deprecated_v1 def test_session_functions(self): with self.test_session() as sess: sess_ref = weakref.ref(sess) @@ -551,6 +553,7 @@ class TestUtilTest(test_util.TensorFlowTestCase, parameterized.TestCase): with self.assertRaises(AssertionError): self.assertAllLessEqual(x, 95.0) + @test_util.run_deprecated_v1 def testAssertAllInRangeWithNonNumericValuesFails(self): s1 = constant_op.constant("Hello, ", name="s1") c = constant_op.constant([1 + 2j, -3 + 5j], name="c") @@ -614,6 +617,7 @@ class TestUtilTest(test_util.TensorFlowTestCase, parameterized.TestCase): with self.assertRaises(AssertionError): self.assertAllInSet(x, (42,)) + @test_util.run_deprecated_v1 def testRandomSeed(self): # Call setUp again for WithCApi case (since it makes a new defeault graph # after setup). @@ -681,7 +685,7 @@ class TestUtilTest(test_util.TensorFlowTestCase, parameterized.TestCase): self.assertIsNone(test_util.get_node_def_from_graph("bar", graph_def)) def test_run_in_eager_and_graph_modes_test_class(self): - msg = "`run_test_in_graph_and_eager_modes` only supports test methods.*" + msg = "`run_in_graph_and_eager_modes` only supports test methods.*" with self.assertRaisesRegexp(ValueError, msg): @test_util.run_in_graph_and_eager_modes() class Foo(object): @@ -706,6 +710,7 @@ class TestUtilTest(test_util.TensorFlowTestCase, parameterized.TestCase): test_util.run_in_graph_and_eager_modes(_test)(self) self.assertEqual(modes, ["graph"]) + @test_util.run_deprecated_v1 def test_run_in_graph_and_eager_modes_setup_in_same_mode(self): modes = [] mode_name = lambda: "eager" if context.executing_eagerly() else "graph" diff --git a/tensorflow/python/grappler/constant_folding_test.py b/tensorflow/python/grappler/constant_folding_test.py index ab1d0ed25b..30c1e14681 100644 --- a/tensorflow/python/grappler/constant_folding_test.py +++ b/tensorflow/python/grappler/constant_folding_test.py @@ -61,7 +61,7 @@ class ConstantFoldingTest(test.TestCase): back_prop=False, parallel_iterations=1) with session.Session() as sess: - y_v = sess.run(y) + y_v = self.evaluate(y) self.assertAllEqual(np.zeros([10, 20, 30]), y_v) diff --git a/tensorflow/python/grappler/cost_analyzer_test.py b/tensorflow/python/grappler/cost_analyzer_test.py index b8225b81a5..ee3e289f65 100644 --- a/tensorflow/python/grappler/cost_analyzer_test.py +++ b/tensorflow/python/grappler/cost_analyzer_test.py @@ -38,6 +38,7 @@ from tensorflow.python.training import adam class CostAnalysisTest(test.TestCase): + @test_util.run_deprecated_v1 def testBasicCost(self): """Make sure arguments can be passed correctly.""" a = constant_op.constant(10, name="a") @@ -62,6 +63,7 @@ class CostAnalysisTest(test.TestCase): # Also print the report to make it easier to debug print("{}".format(report)) + @test_util.run_deprecated_v1 def testVerbose(self): """Make sure the full report is generated with verbose=True.""" a = constant_op.constant(10, name="a") @@ -81,6 +83,7 @@ class CostAnalysisTest(test.TestCase): # Also print the report to make it easier to debug print("{}".format(report)) + @test_util.run_deprecated_v1 def testSmallNetworkCost(self): image = array_ops.placeholder(dtypes.float32, shape=[1, 28, 28, 1]) label = array_ops.placeholder(dtypes.float32, shape=[1, 10]) @@ -96,8 +99,8 @@ class CostAnalysisTest(test.TestCase): b_fc = variables.Variable(random_ops.truncated_normal([10], stddev=0.1)) y_conv = nn_ops.softmax(math_ops.matmul(h_conv_flat, w_fc) + b_fc) - cross_entropy = math_ops.reduce_mean(-math_ops.reduce_sum( - label * math_ops.log(y_conv), reduction_indices=[1])) + cross_entropy = math_ops.reduce_mean( + -math_ops.reduce_sum(label * math_ops.log(y_conv), axis=[1])) _ = adam.AdamOptimizer(1e-4).minimize(cross_entropy) mg = meta_graph.create_meta_graph_def(graph=ops.get_default_graph()) @@ -129,6 +132,7 @@ class CostAnalysisTest(test.TestCase): # self.assertTrue(0 < upper) # self.assertTrue(lower <= upper) + @test_util.run_deprecated_v1 def testBasicMemory(self): """Make sure arguments can be passed correctly.""" with test_util.device(use_gpu=False): diff --git a/tensorflow/python/grappler/cost_analyzer_tool.py b/tensorflow/python/grappler/cost_analyzer_tool.py index e6229e1856..7dbaf449ca 100644 --- a/tensorflow/python/grappler/cost_analyzer_tool.py +++ b/tensorflow/python/grappler/cost_analyzer_tool.py @@ -25,8 +25,8 @@ from google.protobuf import message from google.protobuf import text_format from tensorflow.contrib.fused_conv.ops import gen_fused_conv2d_bias_activation_op # pylint: disable=unused-import from tensorflow.core.framework import graph_pb2 +from tensorflow.core.protobuf import config_pb2 from tensorflow.core.protobuf import meta_graph_pb2 -from tensorflow.core.protobuf import rewriter_config_pb2 from tensorflow.core.protobuf import saved_model_pb2 from tensorflow.python.framework import importer from tensorflow.python.framework import ops @@ -79,10 +79,11 @@ def get_metagraph(): def main(_): metagraph = get_metagraph() - rewriter_config = rewriter_config_pb2.RewriterConfig() + config = config_pb2.ConfigProto() if FLAGS.rewriter_config is not None: - text_format.Merge(FLAGS.rewriter_config, rewriter_config) - optimized_graph = tf_optimizer.OptimizeGraph(rewriter_config, metagraph) + text_format.Merge(FLAGS.rewriter_config, + config.graph_options.rewrite_options) + optimized_graph = tf_optimizer.OptimizeGraph(config, metagraph) metagraph.graph_def.CopyFrom(optimized_graph) report = cost_analyzer.GenerateCostReport(metagraph, FLAGS.per_node_report, diff --git a/tensorflow/python/grappler/datasets_test.py b/tensorflow/python/grappler/datasets_test.py index bd870ad8de..6937301ab2 100644 --- a/tensorflow/python/grappler/datasets_test.py +++ b/tensorflow/python/grappler/datasets_test.py @@ -48,7 +48,7 @@ class GrapplerTest(test.TestCase): for test_case in test_cases: with ops.Graph().as_default() as g: dataset = dataset_ops.Dataset.from_tensors(test_case['tensor']) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) get_next = iterator.get_next() train_op = ops.get_collection_ref(ops.GraphKeys.TRAIN_OP) train_op.append(get_next) @@ -73,7 +73,7 @@ class GrapplerTest(test.TestCase): for test_case in test_cases: with ops.Graph().as_default() as g: dataset = dataset_ops.Dataset.from_tensor_slices(test_case['tensor']) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) get_next = iterator.get_next() train_op = ops.get_collection_ref(ops.GraphKeys.TRAIN_OP) train_op.append(get_next) @@ -109,7 +109,7 @@ class GrapplerTest(test.TestCase): make_generator(test_case['tensor']), dtypes.int64, output_shapes=test_case['shape']) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) get_next = iterator.get_next() train_op = ops.get_collection_ref(ops.GraphKeys.TRAIN_OP) train_op.append(get_next) @@ -122,7 +122,7 @@ class GrapplerTest(test.TestCase): def testRange(self): with ops.Graph().as_default() as g: dataset = dataset_ops.Dataset.range(42) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) get_next = iterator.get_next() train_op = ops.get_collection_ref(ops.GraphKeys.TRAIN_OP) train_op.append(get_next) @@ -148,7 +148,7 @@ class GrapplerTest(test.TestCase): with ops.Graph().as_default() as g: dataset = dataset_ops.Dataset.from_tensors(test_case['tensor']) dataset = fn(dataset, test_case['tensor'], test_case['shape']) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) get_next = iterator.get_next() train_op = ops.get_collection_ref(ops.GraphKeys.TRAIN_OP) train_op.append(get_next) @@ -252,7 +252,7 @@ class GrapplerTest(test.TestCase): with ops.Graph().as_default() as g: dataset = dataset_ops.Dataset.from_tensors(test_case['tensor']) dataset = dataset.batch(42) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) get_next = iterator.get_next() train_op = ops.get_collection_ref(ops.GraphKeys.TRAIN_OP) train_op.append(get_next) @@ -281,7 +281,7 @@ class GrapplerTest(test.TestCase): with ops.Graph().as_default() as g: dataset = dataset_ops.Dataset.from_tensors(test_case['tensor']) dataset = dataset.padded_batch(42, padded_shapes=test_case['shape'][1:]) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) get_next = iterator.get_next() train_op = ops.get_collection_ref(ops.GraphKeys.TRAIN_OP) train_op.append(get_next) @@ -318,7 +318,7 @@ class GrapplerTest(test.TestCase): return dataset_fn dataset = dataset.flat_map(make_dataset(test_case['tensor'])) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) get_next = iterator.get_next() train_op = ops.get_collection_ref(ops.GraphKeys.TRAIN_OP) train_op.append(get_next) @@ -353,7 +353,7 @@ class GrapplerTest(test.TestCase): dataset = dataset.interleave( make_dataset(test_case['tensor']), cycle_length=42) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) get_next = iterator.get_next() train_op = ops.get_collection_ref(ops.GraphKeys.TRAIN_OP) train_op.append(get_next) @@ -382,7 +382,7 @@ class GrapplerTest(test.TestCase): with ops.Graph().as_default() as g: dataset = dataset_ops.Dataset.from_tensors(test_case['tensor']) dataset = dataset.map(array_ops.transpose) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) get_next = iterator.get_next() train_op = ops.get_collection_ref(ops.GraphKeys.TRAIN_OP) train_op.append(get_next) diff --git a/tensorflow/python/grappler/graph_placer.py b/tensorflow/python/grappler/graph_placer.py index 654013b23c..9c05ad8179 100644 --- a/tensorflow/python/grappler/graph_placer.py +++ b/tensorflow/python/grappler/graph_placer.py @@ -19,8 +19,8 @@ from __future__ import division from __future__ import print_function import time +from tensorflow.core.protobuf import config_pb2 from tensorflow.core.protobuf import meta_graph_pb2 -from tensorflow.core.protobuf import rewriter_config_pb2 from tensorflow.python.framework import errors from tensorflow.python.framework import ops as tf_ops from tensorflow.python.grappler import cluster as gcluster @@ -54,9 +54,9 @@ def PlaceGraph(metagraph, cluster = gcluster.Cluster() # Optimize the metagraph to speedup the placement - rewriter_config = rewriter_config_pb2.RewriterConfig() + config = config_pb2.ConfigProto() optimized_graph = tf_optimizer.OptimizeGraph( - rewriter_config, metagraph, verbose=verbose, cluster=cluster) + config, metagraph, verbose=verbose, cluster=cluster) optimized_metagraph = meta_graph_pb2.MetaGraphDef() optimized_metagraph.CopyFrom(metagraph) optimized_metagraph.graph_def.CopyFrom(optimized_graph) diff --git a/tensorflow/python/grappler/item_test.py b/tensorflow/python/grappler/item_test.py index d3d96c646c..78604b259c 100644 --- a/tensorflow/python/grappler/item_test.py +++ b/tensorflow/python/grappler/item_test.py @@ -24,6 +24,7 @@ from tensorflow.python.framework import errors_impl from tensorflow.python.framework import meta_graph from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util from tensorflow.python.grappler import item from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import gen_array_ops @@ -107,6 +108,7 @@ class ItemTest(test.TestCase): newest_tf_item = grappler_item.tf_item self.assertEqual(new_tf_item, newest_tf_item) + @test_util.run_deprecated_v1 def testColocationContraints(self): with ops.Graph().as_default() as g: c = constant_op.constant([10]) diff --git a/tensorflow/python/grappler/layout_optimizer_test.py b/tensorflow/python/grappler/layout_optimizer_test.py index 8cc971c61d..98f2e6d718 100644 --- a/tensorflow/python/grappler/layout_optimizer_test.py +++ b/tensorflow/python/grappler/layout_optimizer_test.py @@ -29,6 +29,7 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import random_seed +from tensorflow.python.framework import test_util from tensorflow.python.grappler import cluster as gcluster from tensorflow.python.grappler import tf_optimizer from tensorflow.python.layers import convolutional as conv_layers @@ -241,7 +242,7 @@ class LayoutOptimizerTest(test.TestCase): if restore: saver.restore(sess, checkpoint_path) else: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) np.random.seed(0) for _ in range(2): @@ -262,7 +263,7 @@ class LayoutOptimizerTest(test.TestCase): output = _two_layer_model(x) with session.Session(config=_get_config(False)) as sess: - output_val_ref = sess.run(output) + output_val_ref = self.evaluate(output) with session.Session(config=_get_config()) as sess: metadata = config_pb2.RunMetadata() @@ -365,7 +366,7 @@ class LayoutOptimizerTest(test.TestCase): output = array_ops.identity(pad) with session.Session(config=_get_config(False)) as sess: - output_val_ref = sess.run(output) + output_val_ref = self.evaluate(output) with session.Session(config=_get_config()) as sess: metadata = config_pb2.RunMetadata() @@ -396,7 +397,7 @@ class LayoutOptimizerTest(test.TestCase): output = array_ops.identity(reduce_sum) with session.Session(config=_get_config(False)) as sess: - output_val_ref = sess.run(output) + output_val_ref = self.evaluate(output) with session.Session(config=_get_config()) as sess: metadata = config_pb2.RunMetadata() @@ -425,7 +426,7 @@ class LayoutOptimizerTest(test.TestCase): output = array_ops.identity(cast) with session.Session(config=_get_config(False)) as sess: - output_val_ref = sess.run(output) + output_val_ref = self.evaluate(output) with session.Session(config=_get_config()) as sess: metadata = config_pb2.RunMetadata() @@ -456,7 +457,7 @@ class LayoutOptimizerTest(test.TestCase): output = array_ops.identity(squeeze) with session.Session(config=_get_config(False)) as sess: - output_val_ref = sess.run(output) + output_val_ref = self.evaluate(output) with session.Session(config=_get_config()) as sess: metadata = config_pb2.RunMetadata() @@ -486,7 +487,7 @@ class LayoutOptimizerTest(test.TestCase): output = array_ops.identity(squeeze) with session.Session(config=_get_config(False)) as sess: - output_val_ref = sess.run(output) + output_val_ref = self.evaluate(output) with session.Session(config=_get_config()) as sess: metadata = config_pb2.RunMetadata() @@ -516,7 +517,7 @@ class LayoutOptimizerTest(test.TestCase): output = array_ops.identity(squeeze) with session.Session(config=_get_config(False)) as sess: - output_val_ref = sess.run(output) + output_val_ref = self.evaluate(output) with session.Session(config=_get_config()) as sess: metadata = config_pb2.RunMetadata() @@ -545,7 +546,7 @@ class LayoutOptimizerTest(test.TestCase): output = array_ops.identity(reduce_sum) with session.Session(config=_get_config(False)) as sess: - output_val_ref = sess.run(output) + output_val_ref = self.evaluate(output) with session.Session(config=_get_config()) as sess: metadata = config_pb2.RunMetadata() @@ -574,7 +575,7 @@ class LayoutOptimizerTest(test.TestCase): output = array_ops.identity(reduce_sum) with session.Session(config=_get_config(False)) as sess: - output_val_ref = sess.run(output) + output_val_ref = self.evaluate(output) with session.Session(config=_get_config()) as sess: metadata = config_pb2.RunMetadata() @@ -603,7 +604,7 @@ class LayoutOptimizerTest(test.TestCase): output = array_ops.identity(reduce_sum) with session.Session(config=_get_config(False)) as sess: - output_val_ref = sess.run(output) + output_val_ref = self.evaluate(output) with session.Session(config=_get_config()) as sess: metadata = config_pb2.RunMetadata() @@ -632,7 +633,7 @@ class LayoutOptimizerTest(test.TestCase): output = array_ops.identity(reduce_sum) with session.Session(config=_get_config(False)) as sess: - output_val_ref = sess.run(output) + output_val_ref = self.evaluate(output) with session.Session(config=_get_config()) as sess: metadata = config_pb2.RunMetadata() @@ -662,7 +663,7 @@ class LayoutOptimizerTest(test.TestCase): output = array_ops.identity(reduce_sum) with session.Session(config=_get_config(False)) as sess: - output_val_ref = sess.run(output) + output_val_ref = self.evaluate(output) with session.Session(config=_get_config()) as sess: metadata = config_pb2.RunMetadata() @@ -691,7 +692,7 @@ class LayoutOptimizerTest(test.TestCase): output = array_ops.identity(reduce_sum) with session.Session(config=_get_config(False)) as sess: - output_val_ref = sess.run(output) + output_val_ref = self.evaluate(output) with session.Session(config=_get_config()) as sess: metadata = config_pb2.RunMetadata() @@ -724,7 +725,7 @@ class LayoutOptimizerTest(test.TestCase): output = array_ops.identity(concat) with session.Session(config=_get_config(False)) as sess: - output_val_ref = sess.run(output) + output_val_ref = self.evaluate(output) with session.Session(config=_get_config()) as sess: metadata = config_pb2.RunMetadata() @@ -835,7 +836,7 @@ class LayoutOptimizerTest(test.TestCase): output = array_ops.identity(reverse) with session.Session(config=_get_config(False)) as sess: - output_val_ref = sess.run(output) + output_val_ref = self.evaluate(output) with session.Session(config=_get_config()) as sess: metadata = config_pb2.RunMetadata() @@ -905,7 +906,7 @@ class LayoutOptimizerTest(test.TestCase): output = array_ops.identity(select) with session.Session(config=_get_config(False)) as sess: - output_val_ref = sess.run(output) + output_val_ref = self.evaluate(output) with session.Session(config=_get_config()) as sess: metadata = config_pb2.RunMetadata() @@ -966,7 +967,7 @@ class LayoutOptimizerTest(test.TestCase): output = array_ops.identity(select) with session.Session(config=_get_config(False)) as sess: - output_val_ref = sess.run(output) + output_val_ref = self.evaluate(output) with session.Session(config=_get_config()) as sess: metadata = config_pb2.RunMetadata() @@ -1179,7 +1180,7 @@ class LayoutOptimizerTest(test.TestCase): output = array_ops.identity(s) with session.Session(config=_get_config(False)) as sess: - output_val_ref = sess.run(output) + output_val_ref = self.evaluate(output) with session.Session(config=_get_config()) as sess: metadata = config_pb2.RunMetadata() @@ -1214,7 +1215,7 @@ class LayoutOptimizerTest(test.TestCase): output = array_ops.identity(s) with session.Session(config=_get_config(False)) as sess: - output_val_ref = sess.run(output) + output_val_ref = self.evaluate(output) with session.Session(config=_get_config()) as sess: metadata = config_pb2.RunMetadata() @@ -1347,7 +1348,7 @@ class LayoutOptimizerTest(test.TestCase): output = _loop() with session.Session(config=_get_config(False)) as sess: - output_val_ref = sess.run(output) + output_val_ref = self.evaluate(output) with session.Session(config=_get_config()) as sess: metadata = config_pb2.RunMetadata() @@ -1374,7 +1375,7 @@ class LayoutOptimizerTest(test.TestCase): output = _loop_with_branch() with session.Session(config=_get_config(False)) as sess: - output_val_ref = sess.run(output) + output_val_ref = self.evaluate(output) with session.Session(config=_get_config()) as sess: metadata = config_pb2.RunMetadata() @@ -1398,7 +1399,7 @@ class LayoutOptimizerTest(test.TestCase): output = _loop_with_vec_and_4d() with session.Session(config=_get_config(False)) as sess: - output_val_ref = sess.run(output) + output_val_ref = self.evaluate(output) with session.Session(config=_get_config()) as sess: metadata = config_pb2.RunMetadata() @@ -1422,7 +1423,7 @@ class LayoutOptimizerTest(test.TestCase): output = _model_with_second_port() with session.Session(config=_get_config(False)) as sess: - output_val_ref = sess.run(output) + output_val_ref = self.evaluate(output) with session.Session(config=_get_config()) as sess: metadata = config_pb2.RunMetadata() @@ -1441,13 +1442,16 @@ class LayoutOptimizerTest(test.TestCase): self._assert_trans_nchw_to_nhwc('Add-0-0', nodes) self.assertAllClose(output_val_ref, output_val, atol=1e-3) + @test_util.run_deprecated_v1 def testGradient(self): meta_graph = _simple_metagraph() - rewrite_options = rewriter_config_pb2.RewriterConfig( - layout_optimizer=rewriter_config_pb2.RewriterConfig.ON, - min_graph_nodes=-1) + config = config_pb2.ConfigProto() + config.graph_options.rewrite_options.CopyFrom( + rewriter_config_pb2.RewriterConfig( + layout_optimizer=rewriter_config_pb2.RewriterConfig.ON, + min_graph_nodes=-1)) optimized_graph = tf_optimizer.OptimizeGraph( - rewrite_options, meta_graph, cluster=_get_cluster()) + config, meta_graph, cluster=_get_cluster()) found = 0 for node in optimized_graph.node: @@ -1456,13 +1460,16 @@ class LayoutOptimizerTest(test.TestCase): self.assertEqual(node.attr['data_format'].s, b'NCHW') self.assertEqual(found, 5) + @test_util.run_deprecated_v1 def testDepthwise(self): meta_graph = _simple_metagraph(depthwise=True) - rewrite_options = rewriter_config_pb2.RewriterConfig( - layout_optimizer=rewriter_config_pb2.RewriterConfig.ON, - min_graph_nodes=-1) + config = config_pb2.ConfigProto() + config.graph_options.rewrite_options.CopyFrom( + rewriter_config_pb2.RewriterConfig( + layout_optimizer=rewriter_config_pb2.RewriterConfig.ON, + min_graph_nodes=-1)) optimized_graph = tf_optimizer.OptimizeGraph( - rewrite_options, meta_graph, cluster=_get_cluster()) + config, meta_graph, cluster=_get_cluster()) found = 0 for node in optimized_graph.node: diff --git a/tensorflow/python/grappler/memory_optimizer_test.py b/tensorflow/python/grappler/memory_optimizer_test.py index 03b42f6453..6eb16fbd39 100644 --- a/tensorflow/python/grappler/memory_optimizer_test.py +++ b/tensorflow/python/grappler/memory_optimizer_test.py @@ -25,6 +25,7 @@ from tensorflow.python.client import session from tensorflow.python.framework import meta_graph from tensorflow.python.framework import ops from tensorflow.python.framework import random_seed +from tensorflow.python.framework import test_util from tensorflow.python.grappler import tf_optimizer from tensorflow.python.ops import math_ops from tensorflow.python.ops import nn @@ -37,6 +38,7 @@ from tensorflow.python.training import training as train class MemoryOptimizerSwapTest(test.TestCase): """Tests the Grappler memory optimizer.""" + @test_util.run_deprecated_v1 def testNoSwapping(self): """Make sure the graph is preserved when there is nothing to swap.""" a = variables.VariableV1(10, name='a') @@ -49,15 +51,18 @@ class MemoryOptimizerSwapTest(test.TestCase): graph_size = len(mg.graph_def.node) nodes = [node.name for node in mg.graph_def.node] - rewriter_config = rewriter_config_pb2.RewriterConfig( - disable_model_pruning=True, - constant_folding=rewriter_config_pb2.RewriterConfig.OFF, - memory_optimization=rewriter_config_pb2.RewriterConfig.MANUAL) - graph = tf_optimizer.OptimizeGraph(rewriter_config, mg) + config = config_pb2.ConfigProto() + config.graph_options.rewrite_options.CopyFrom( + rewriter_config_pb2.RewriterConfig( + disable_model_pruning=True, + constant_folding=rewriter_config_pb2.RewriterConfig.OFF, + memory_optimization=rewriter_config_pb2.RewriterConfig.MANUAL)) + graph = tf_optimizer.OptimizeGraph(config, mg) self.assertEqual(len(graph.node), graph_size) self.assertItemsEqual([node.name for node in graph.node], nodes) + @test_util.run_deprecated_v1 def testSimpleSwap(self): """Check that the swap annotations are followed.""" a = variables.VariableV1(10, name='a') @@ -72,13 +77,15 @@ class MemoryOptimizerSwapTest(test.TestCase): mg = meta_graph.create_meta_graph_def(graph=ops.get_default_graph()) graph_size = len(mg.graph_def.node) - rewriter_config = rewriter_config_pb2.RewriterConfig( - disable_model_pruning=True, - meta_optimizer_iterations=rewriter_config_pb2.RewriterConfig.ONE, - constant_folding=rewriter_config_pb2.RewriterConfig.OFF, - memory_optimization=rewriter_config_pb2.RewriterConfig.MANUAL, - min_graph_nodes=-1) - graph = tf_optimizer.OptimizeGraph(rewriter_config, mg) + config = config_pb2.ConfigProto() + config.graph_options.rewrite_options.CopyFrom( + rewriter_config_pb2.RewriterConfig( + disable_model_pruning=True, + meta_optimizer_iterations=rewriter_config_pb2.RewriterConfig.ONE, + constant_folding=rewriter_config_pb2.RewriterConfig.OFF, + memory_optimization=rewriter_config_pb2.RewriterConfig.MANUAL, + min_graph_nodes=-1)) + graph = tf_optimizer.OptimizeGraph(config, mg) self.assertEqual(len(graph.node), graph_size + 2) self.assertTrue( @@ -127,7 +134,8 @@ class MemoryOptimizerRecomputeTest(test.TestCase): def testRewritingDefaultGradientNames(self): """Tests that rewriting occurs with default gradient names.""" (original_metagraph, _, _, _) = self._GetMetaGraph() - rewritten_graph_def = tf_optimizer.OptimizeGraph( + config = config_pb2.ConfigProto() + config.graph_options.rewrite_options.CopyFrom( rewriter_config_pb2.RewriterConfig( disable_model_pruning=True, constant_folding=rewriter_config_pb2.RewriterConfig.OFF, @@ -135,8 +143,9 @@ class MemoryOptimizerRecomputeTest(test.TestCase): layout_optimizer=rewriter_config_pb2.RewriterConfig.OFF, arithmetic_optimization=rewriter_config_pb2.RewriterConfig.OFF, min_graph_nodes=-1, - memory_optimization=rewriter_config_pb2.RewriterConfig. - RECOMPUTATION_HEURISTICS), original_metagraph) + memory_optimization=( + rewriter_config_pb2.RewriterConfig.RECOMPUTATION_HEURISTICS))) + rewritten_graph_def = tf_optimizer.OptimizeGraph(config, original_metagraph) self.assertGreater( len(rewritten_graph_def.node), len(original_metagraph.graph_def.node)) @@ -153,7 +162,8 @@ class MemoryOptimizerRecomputeTest(test.TestCase): """Tests that rewriting occurs with non-standard gradient names.""" (original_metagraph, _, _, _) = self._GetMetaGraph( optimizer_scope_name='optimizer') - rewritten_graph_def = tf_optimizer.OptimizeGraph( + config = config_pb2.ConfigProto() + config.graph_options.rewrite_options.CopyFrom( rewriter_config_pb2.RewriterConfig( disable_model_pruning=True, constant_folding=rewriter_config_pb2.RewriterConfig.OFF, @@ -161,11 +171,11 @@ class MemoryOptimizerRecomputeTest(test.TestCase): layout_optimizer=rewriter_config_pb2.RewriterConfig.OFF, arithmetic_optimization=rewriter_config_pb2.RewriterConfig.OFF, min_graph_nodes=-1, - memory_optimization=rewriter_config_pb2.RewriterConfig. - RECOMPUTATION_HEURISTICS, + memory_optimization=rewriter_config_pb2.RewriterConfig + .RECOMPUTATION_HEURISTICS, # Checks that name scope "gradients/" also match sub-scope. - memory_optimizer_target_node_name_scope='gradients/'), - original_metagraph) + memory_optimizer_target_node_name_scope='gradients/')) + rewritten_graph_def = tf_optimizer.OptimizeGraph(config, original_metagraph) self.assertGreater( len(rewritten_graph_def.node), len(original_metagraph.graph_def.node)) @@ -182,18 +192,19 @@ class MemoryOptimizerRecomputeTest(test.TestCase): """Tests that rewriting occurs with non-standard gradient names.""" (original_metagraph, _, _, _) = self._GetMetaGraph(optimizer_scope_name='foo/bar') - rewritten_graph_def = tf_optimizer.OptimizeGraph( + config = config_pb2.ConfigProto() + config.graph_options.rewrite_options.CopyFrom( rewriter_config_pb2.RewriterConfig( disable_model_pruning=True, constant_folding=rewriter_config_pb2.RewriterConfig.OFF, dependency_optimization=rewriter_config_pb2.RewriterConfig.OFF, layout_optimizer=rewriter_config_pb2.RewriterConfig.OFF, arithmetic_optimization=rewriter_config_pb2.RewriterConfig.OFF, - memory_optimization=rewriter_config_pb2.RewriterConfig. - RECOMPUTATION_HEURISTICS, + memory_optimization=rewriter_config_pb2.RewriterConfig + .RECOMPUTATION_HEURISTICS, # This should not match anything. - memory_optimizer_target_node_name_scope='r/gradients/'), - original_metagraph) + memory_optimizer_target_node_name_scope='r/gradients/')) + rewritten_graph_def = tf_optimizer.OptimizeGraph(config, original_metagraph) self.assertEqual( len(rewritten_graph_def.node), len(original_metagraph.graph_def.node)) self.assertEqual(0, @@ -223,10 +234,10 @@ class MemoryOptimizerRecomputeTest(test.TestCase): train_op = graph.get_operation_by_name(train_op_name) loss_op = graph.get_tensor_by_name(loss_op_name) with session.Session(config=config, graph=graph) as sess: - sess.run(init_op) - sess.run(train_op) - sess.run(train_op) - return sess.run(loss_op) + self.evaluate(init_op) + self.evaluate(train_op) + self.evaluate(train_op) + return self.evaluate(loss_op) def testRecomputationRewritingNoErrors(self): """Tests that graph output is not significantly different with rewriting.""" @@ -287,8 +298,8 @@ class MemoryOptimizerRecomputeTest(test.TestCase): rewrite_options=manual_memory_config) session_config = config_pb2.ConfigProto(graph_options=graph_options) with session.Session(config=session_config) as sess: - sess.run(init_op) - sess.run(train_op) + self.evaluate(init_op) + self.evaluate(train_op) def testHintDoesRewrite(self): graph = self._annotated_graph()[0] @@ -298,11 +309,12 @@ class MemoryOptimizerRecomputeTest(test.TestCase): 0, len([node for node in metagraph.graph_def.node if 'Recomputed/' in node.name])) - rewritten_graph_def = tf_optimizer.OptimizeGraph( + config = config_pb2.ConfigProto() + config.graph_options.rewrite_options.CopyFrom( rewriter_config_pb2.RewriterConfig( min_graph_nodes=-1, - memory_optimization=rewriter_config_pb2.RewriterConfig.MANUAL), - metagraph) + memory_optimization=rewriter_config_pb2.RewriterConfig.MANUAL)) + rewritten_graph_def = tf_optimizer.OptimizeGraph(config, metagraph) self.assertEqual( 9, len([node for node in rewritten_graph_def.node diff --git a/tensorflow/python/grappler/model_analyzer_test.py b/tensorflow/python/grappler/model_analyzer_test.py index ec172755f1..d000cfa1ba 100644 --- a/tensorflow/python/grappler/model_analyzer_test.py +++ b/tensorflow/python/grappler/model_analyzer_test.py @@ -21,6 +21,7 @@ from __future__ import print_function from tensorflow.python.framework import constant_op from tensorflow.python.framework import meta_graph from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.grappler import model_analyzer from tensorflow.python.ops import math_ops from tensorflow.python.platform import test @@ -28,6 +29,7 @@ from tensorflow.python.platform import test class PyWrapOptimizeGraphTest(test.TestCase): + @test_util.run_deprecated_v1 def testBasic(self): """Make sure arguments can be passed correctly.""" a = constant_op.constant([10, 11], name="a") @@ -49,6 +51,7 @@ class PyWrapOptimizeGraphTest(test.TestCase): # Also print the report to make it easier to debug print("{}".format(report)) + @test_util.run_deprecated_v1 def testDebugMode(self): """Make sure arguments can be passed correctly.""" a = constant_op.constant([10, 11], name="a") diff --git a/tensorflow/python/grappler/tf_optimizer.i b/tensorflow/python/grappler/tf_optimizer.i index 39ca71e99a..b746c3ec26 100644 --- a/tensorflow/python/grappler/tf_optimizer.i +++ b/tensorflow/python/grappler/tf_optimizer.i @@ -34,8 +34,8 @@ limitations under the License. $1 = &temp; } -%typemap(in) const tensorflow::RewriterConfig& ( - tensorflow::RewriterConfig temp) { +%typemap(in) const tensorflow::ConfigProto& ( + tensorflow::ConfigProto temp) { char* c_string; Py_ssize_t py_size; if (PyBytes_AsStringAndSize($input, &c_string, &py_size) == -1) { @@ -46,7 +46,7 @@ limitations under the License. if (!temp.ParseFromString(string(c_string, py_size))) { PyErr_SetString( PyExc_TypeError, - "The RewriterConfig could not be parsed as a valid protocol buffer"); + "The ConfigProto could not be parsed as a valid protocol buffer"); SWIG_fail; } $1 = &temp; @@ -67,20 +67,20 @@ limitations under the License. #include "tensorflow/core/grappler/clusters/utils.h" #include "tensorflow/core/grappler/clusters/virtual_cluster.h" #include "tensorflow/core/grappler/optimizers/meta_optimizer.h" + #include "tensorflow/core/protobuf/config.pb.h" #include "tensorflow/core/protobuf/meta_graph.pb.h" - #include "tensorflow/core/protobuf/rewriter_config.pb.h" #include "tensorflow/core/public/session_options.h" void DetectDevices(std::unordered_map* device_map) { tensorflow::SessionOptions options; - std::vector devices; + std::vector> devices; tensorflow::Status status = tensorflow::DeviceFactory::AddDevices(options, "", &devices); if (!status.ok()) { return; } - for (const tensorflow::Device* device : devices) { + for (const std::unique_ptr& device : devices) { tensorflow::DeviceProperties& prop = (*device_map)[device->name()]; prop = tensorflow::grappler::GetDeviceInfo(device->parsed_name()); @@ -88,13 +88,12 @@ void DetectDevices(std::unordered_map* dev // available device memory. const tensorflow::DeviceAttributes& attr = device->attributes(); prop.set_memory_size(attr.memory_limit()); - delete device; } } PyObject* TF_OptimizeGraph( GCluster cluster, - const tensorflow::RewriterConfig& rewriter_config, + const tensorflow::ConfigProto& config_proto, const tensorflow::MetaGraphDef& metagraph, bool verbose, const string& graph_id, TF_Status* out_status) { tensorflow::grappler::ItemConfig item_config; @@ -110,7 +109,7 @@ PyObject* TF_OptimizeGraph( tensorflow::DeviceBase* cpu_device = nullptr; tensorflow::GraphDef out_graph; - tensorflow::grappler::MetaOptimizer optimizer(cpu_device, rewriter_config); + tensorflow::grappler::MetaOptimizer optimizer(cpu_device, config_proto); tensorflow::Status status = optimizer.Optimize(cluster.get(), *grappler_item, &out_graph); if (verbose) { optimizer.PrintResult(); @@ -127,7 +126,7 @@ PyObject* TF_OptimizeGraph( // Wrap this function PyObject* TF_OptimizeGraph( GCluster cluster, - const tensorflow::RewriterConfig& rewriter_config, + const tensorflow::ConfigProto& config_proto, const tensorflow::MetaGraphDef& metagraph, bool verbose, const string& graph_id, TF_Status* out_status); diff --git a/tensorflow/python/grappler/tf_optimizer.py b/tensorflow/python/grappler/tf_optimizer.py index a73a4a98fc..e72667b6f3 100644 --- a/tensorflow/python/grappler/tf_optimizer.py +++ b/tensorflow/python/grappler/tf_optimizer.py @@ -19,22 +19,26 @@ from __future__ import division from __future__ import print_function from tensorflow.core.framework import graph_pb2 +from tensorflow.core.protobuf import config_pb2 from tensorflow.python import pywrap_tensorflow as tf_opt from tensorflow.python.framework import errors from tensorflow.python.grappler import cluster as gcluster -def OptimizeGraph(rewriter_config, +def OptimizeGraph(config_proto, metagraph, verbose=True, graph_id=b'graph_to_optimize', cluster=None): """Optimize the provided metagraph.""" + if not isinstance(config_proto, config_pb2.ConfigProto): + raise TypeError('Expected config_proto to be a ConfigProto, saw type %s' % + type(config_proto)) with errors.raise_exception_on_not_ok_status() as status: if cluster is None: cluster = gcluster.Cluster() ret_from_swig = tf_opt.TF_OptimizeGraph(cluster.tf_cluster, - rewriter_config.SerializeToString(), + config_proto.SerializeToString(), metagraph.SerializeToString(), verbose, graph_id, status) if ret_from_swig is None: diff --git a/tensorflow/python/grappler/tf_optimizer_test.py b/tensorflow/python/grappler/tf_optimizer_test.py index eca0f67982..06ccaa813f 100644 --- a/tensorflow/python/grappler/tf_optimizer_test.py +++ b/tensorflow/python/grappler/tf_optimizer_test.py @@ -17,12 +17,13 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -from tensorflow.core.protobuf import rewriter_config_pb2 +from tensorflow.core.protobuf import config_pb2 from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import meta_graph from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util from tensorflow.python.grappler import item as gitem from tensorflow.python.grappler import tf_optimizer from tensorflow.python.ops import array_ops @@ -34,6 +35,7 @@ from tensorflow.python.platform import test class PyWrapOptimizeGraphTest(test.TestCase): + @test_util.run_deprecated_v1 def testBasic(self): """Make sure arguments can be passed correctly.""" a = constant_op.constant(10, name='a') @@ -45,15 +47,17 @@ class PyWrapOptimizeGraphTest(test.TestCase): train_op.append(d) mg = meta_graph.create_meta_graph_def(graph=ops.get_default_graph()) - rewriter_config = rewriter_config_pb2.RewriterConfig() + config = config_pb2.ConfigProto() + rewriter_config = config.graph_options.rewrite_options rewriter_config.optimizers.append('constfold') rewriter_config.min_graph_nodes = -1 - graph = tf_optimizer.OptimizeGraph(rewriter_config, mg) + graph = tf_optimizer.OptimizeGraph(config, mg) self.assertEqual(len(graph.node), 1) self.assertItemsEqual([node.name for node in graph.node], ['d']) + @test_util.run_deprecated_v1 def testKeepNodes(self): g = ops.Graph() with g.as_default(): @@ -68,18 +72,21 @@ class PyWrapOptimizeGraphTest(test.TestCase): # Optimize the graph. mg = meta_graph.create_meta_graph_def(graph=g) - rewriter_config = rewriter_config_pb2.RewriterConfig() + config = config_pb2.ConfigProto() + rewriter_config = config.graph_options.rewrite_options rewriter_config.min_graph_nodes = -1 - optimized_graph = tf_optimizer.OptimizeGraph(rewriter_config, mg) + optimized_graph = tf_optimizer.OptimizeGraph(config, mg) # Check that the nodes referenced in various collections have been preserved - self.assertEqual(len(optimized_graph.node), 5) - self.assertEqual(d.op.name, optimized_graph.node[0].name) - self.assertEqual(a1.op.name, optimized_graph.node[1].name) - self.assertEqual('Variable/initial_value', optimized_graph.node[2].name) - self.assertEqual(a2.op.name, optimized_graph.node[3].name) - self.assertEqual('Variable/Assign', optimized_graph.node[4].name) - + optimized_graph_nodes = [node.name for node in optimized_graph.node] + expected_nodes = [ + d.op.name, a1.op.name, a2.op.name, 'Variable/initial_value', + 'Variable/Assign' + ] + self.assertEqual(len(optimized_graph_nodes), len(expected_nodes)) + self.assertAllInSet(optimized_graph_nodes, expected_nodes) + + @test_util.run_deprecated_v1 def testLoops(self): g = ops.Graph() with g.as_default(): @@ -110,9 +117,10 @@ class PyWrapOptimizeGraphTest(test.TestCase): # Optimize the graph. mg = meta_graph.create_meta_graph_def(graph=g) - rewriter_config = rewriter_config_pb2.RewriterConfig() + config = config_pb2.ConfigProto() + rewriter_config = config.graph_options.rewrite_options rewriter_config.min_graph_nodes = -1 - optimized_graph = tf_optimizer.OptimizeGraph(rewriter_config, mg) + optimized_graph = tf_optimizer.OptimizeGraph(config, mg) mg.graph_def.CopyFrom(optimized_graph) # Check that the nodes referenced in various collections have been preserved diff --git a/tensorflow/python/keras/BUILD b/tensorflow/python/keras/BUILD index 6f38d822e7..ab5628b1d2 100755 --- a/tensorflow/python/keras/BUILD +++ b/tensorflow/python/keras/BUILD @@ -3,10 +3,10 @@ licenses(["notice"]) # Apache 2.0 -exports_files(["LICENSE"]) - package(default_visibility = ["//visibility:public"]) +exports_files(["LICENSE"]) + load("//tensorflow:tensorflow.bzl", "py_test") load("//tensorflow:tensorflow.bzl", "cuda_py_test") @@ -122,8 +122,10 @@ py_library( "constraints.py", "engine/__init__.py", "engine/base_layer.py", + "engine/base_layer_utils.py", "engine/distributed_training_utils.py", "engine/input_layer.py", + "engine/input_spec.py", "engine/network.py", "engine/saving.py", "engine/sequential.py", @@ -141,11 +143,14 @@ py_library( "regularizers.py", "utils/data_utils.py", "utils/io_utils.py", + "utils/losses_utils.py", ], srcs_version = "PY2AND3", deps = [ ":backend", "//tensorflow/python/data", + "//tensorflow/python/distribute:reduce_util", + "//tensorflow/python/keras/optimizer_v2", "//tensorflow/python/training/checkpointable:data_structures", "//tensorflow/tools/docs:doc_controls", "@six_archive//:six", @@ -180,7 +185,6 @@ py_library( ":engine", "//tensorflow/python:array_ops", "//tensorflow/python:cudnn_rnn_ops_gen", - "//tensorflow/python:distribute", "//tensorflow/python:dtypes", "//tensorflow/python:embedding_ops", "//tensorflow/python:framework_ops", @@ -194,6 +198,7 @@ py_library( "//tensorflow/python:tensor_array_ops", "//tensorflow/python:tensor_shape", "//tensorflow/python:util", + "//tensorflow/python/distribute:distribute_lib", ], ) @@ -264,6 +269,7 @@ py_test( name = "optimizers_test", size = "medium", srcs = ["optimizers_test.py"], + shard_count = 2, srcs_version = "PY2AND3", tags = ["notsan"], deps = [ @@ -271,6 +277,7 @@ py_test( "//tensorflow/python:client_testlib", "//tensorflow/python:training", "//third_party/py/numpy", + "@absl_py//absl/testing:parameterized", ], ) @@ -300,6 +307,7 @@ py_test( ":keras", "//tensorflow/python:client_testlib", "//third_party/py/numpy", + "@absl_py//absl/testing:parameterized", ], ) @@ -318,7 +326,7 @@ py_test( py_test( name = "advanced_activations_test", - size = "small", + size = "medium", srcs = ["layers/advanced_activations_test.py"], srcs_version = "PY2AND3", deps = [ @@ -369,7 +377,7 @@ cuda_py_test( py_test( name = "pooling_test", - size = "medium", + size = "large", srcs = ["layers/pooling_test.py"], srcs_version = "PY2AND3", deps = [ @@ -491,12 +499,13 @@ py_test( ":keras", "//tensorflow/python:client_testlib", "//third_party/py/numpy", + "@absl_py//absl/testing:parameterized", ], ) py_test( name = "recurrent_test", - size = "medium", + size = "large", srcs = ["layers/recurrent_test.py"], srcs_version = "PY2AND3", deps = [ @@ -506,6 +515,19 @@ py_test( ], ) +cuda_py_test( + name = "unified_lstm_test", + size = "medium", + srcs = ["layers/unified_lstm_test.py"], + additional_deps = [ + ":keras", + "@absl_py//absl/testing:parameterized", + "//third_party/py/numpy", + "//tensorflow/python:client_testlib", + ], + shard_count = 4, +) + py_test( name = "serialization_test", size = "small", @@ -577,6 +599,17 @@ py_test( ], ) +py_test( + name = "tf_utils_test", + size = "small", + srcs = ["utils/tf_utils_test.py"], + srcs_version = "PY2AND3", + deps = [ + ":keras", + "//tensorflow/python:client_testlib", + ], +) + py_test( name = "io_utils_test", size = "small", @@ -706,16 +739,34 @@ py_test( ], ) +py_test( + name = "training_dataset_test", + size = "medium", + srcs = ["engine/training_dataset_test.py"], + srcs_version = "PY2AND3", + deps = [ + ":keras", + "//tensorflow/python:client_testlib", + "//third_party/py/numpy", + "@absl_py//absl/testing:parameterized", + ], +) + py_test( name = "training_generator_test", size = "enormous", srcs = ["engine/training_generator_test.py"], + shard_count = 3, srcs_version = "PY2AND3", - tags = ["notsan"], + tags = [ + "no_oss", + "notsan", + ], deps = [ ":keras", "//tensorflow/python:client_testlib", "//third_party/py/numpy", + "@absl_py//absl/testing:parameterized", ], ) @@ -730,6 +781,7 @@ py_test( "//tensorflow/python:client_testlib", "//tensorflow/python/feature_column:feature_column_py", "//third_party/py/numpy", + "@absl_py//absl/testing:parameterized", ], ) @@ -763,6 +815,7 @@ py_test( name = "model_subclassing_test", size = "medium", srcs = ["model_subclassing_test.py"], + shard_count = 2, srcs_version = "PY2AND3", tags = ["notsan"], deps = [ diff --git a/tensorflow/python/keras/activations_test.py b/tensorflow/python/keras/activations_test.py index ad238cb0a9..6b7bfb698b 100644 --- a/tensorflow/python/keras/activations_test.py +++ b/tensorflow/python/keras/activations_test.py @@ -21,6 +21,7 @@ from __future__ import print_function import numpy as np from tensorflow.python import keras +from tensorflow.python.framework import test_util from tensorflow.python.platform import test @@ -67,6 +68,7 @@ class KerasActivationsTest(test.TestCase): expected = _ref_softmax(test_values[0, 0]) self.assertAllClose(result[0, 0], expected, rtol=1e-05) + @test_util.run_deprecated_v1 def test_selu(self): x = keras.backend.placeholder(ndim=2) f = keras.backend.function([x], [keras.activations.selu(x)]) @@ -124,6 +126,7 @@ class KerasActivationsTest(test.TestCase): expected = sigmoid(test_values) self.assertAllClose(result, expected, rtol=1e-05) + @test_util.run_deprecated_v1 def test_hard_sigmoid(self): def ref_hard_sigmoid(x): x = (x * 0.2) + 0.5 @@ -147,6 +150,7 @@ class KerasActivationsTest(test.TestCase): # No negative values in test values... self.assertAllClose(result, test_values, rtol=1e-05) + @test_util.run_deprecated_v1 def test_elu(self): with self.cached_session(): x = keras.backend.placeholder(ndim=2) diff --git a/tensorflow/python/keras/backend.py b/tensorflow/python/keras/backend.py index dd9b0c07e7..1277746716 100644 --- a/tensorflow/python/keras/backend.py +++ b/tensorflow/python/keras/backend.py @@ -25,6 +25,7 @@ import collections import itertools import json import os +import threading import weakref import numpy as np @@ -73,9 +74,9 @@ py_sum = sum # while executing eagerly (such as the functional API for model-building). _GRAPH = None -# This is the default internal TF session used by Keras. -# It can be set manually via `set_session(sess)`. -_SESSION = None +# This is a thread local object that will hold the default internal TF session +# used by Keras. It can be set manually via `set_session(sess)`. +_SESSION = threading.local() # This dictionary holds a mapping {graph: learning_phase}. # A learning phase is a bool tensor used to run Keras models in @@ -337,7 +338,7 @@ def clear_session(): global _GRAPH_TF_OPTIMIZERS # pylint: disable=global-variable-not-assigned ops.reset_default_graph() reset_uids() - _SESSION = None + _SESSION.session = None graph = get_graph() with graph.as_default(): phase = array_ops.placeholder_with_default( @@ -376,27 +377,22 @@ def learning_phase(): Returns: Learning phase (scalar integer tensor or Python integer). """ - with ops.init_scope(): - # We always check & set the learning phase inside the init_scope, - # otherwise the wrong default_graph will be used to look up the learning - # phase inside of functions & defuns. - # - # This is because functions & defuns (both in graph & in eager mode) - # will always execute non-eagerly using a function-specific default - # subgraph. - if context.executing_eagerly(): - if _DUMMY_EAGER_GRAPH not in _GRAPH_LEARNING_PHASES: - # Fallback to inference mode as default. - return 0 - return _GRAPH_LEARNING_PHASES[_DUMMY_EAGER_GRAPH] + if context.executing_eagerly(): + if _DUMMY_EAGER_GRAPH not in _GRAPH_LEARNING_PHASES: + # Fallback to inference mode as default. + return 0 + return _GRAPH_LEARNING_PHASES[_DUMMY_EAGER_GRAPH] + return symbolic_learning_phase() - graph = get_graph() - with graph.as_default(): - if graph not in _GRAPH_LEARNING_PHASES: - phase = array_ops.placeholder_with_default( - False, shape=(), name='keras_learning_phase') - _GRAPH_LEARNING_PHASES[graph] = phase - return _GRAPH_LEARNING_PHASES[graph] + +def symbolic_learning_phase(): + graph = get_graph() + with graph.as_default(): + if graph not in _GRAPH_LEARNING_PHASES: + phase = array_ops.placeholder_with_default( + False, shape=(), name='keras_learning_phase') + _GRAPH_LEARNING_PHASES[graph] = phase + return _GRAPH_LEARNING_PHASES[graph] @tf_export('keras.backend.set_learning_phase') @@ -449,6 +445,20 @@ def learning_phase_scope(value): _GRAPH_LEARNING_PHASES[get_graph()] = previous_value +def _get_session(): + """Returns the session object for the current thread.""" + global _SESSION + default_session = ops.get_default_session() + if default_session is not None: + session = default_session + else: + if getattr(_SESSION, 'session', None) is None: + _SESSION.session = session_module.Session( + config=get_default_session_config()) + session = _SESSION.session + return session + + @tf_export(v1=['keras.backend.get_session']) def get_session(): """Returns the TF session to be used by the backend. @@ -466,14 +476,7 @@ def get_session(): Returns: A TensorFlow session. """ - global _SESSION - default_session = ops.get_default_session() - if default_session is not None: - session = default_session - else: - if _SESSION is None: - _SESSION = session_module.Session(config=get_default_session_config()) - session = _SESSION + session = _get_session() if not _MANUAL_VAR_INIT: with session.graph.as_default(): _initialize_variables(session) @@ -498,7 +501,7 @@ def set_session(session): session: A TF Session. """ global _SESSION - _SESSION = session + _SESSION.session = session def get_default_session_config(): @@ -2322,7 +2325,7 @@ def concatenate(tensors, axis=-1): else: axis = 0 - if py_all([is_sparse(x) for x in tensors]): + if py_all(is_sparse(x) for x in tensors): return sparse_ops.sparse_concat(axis, tensors) else: return array_ops.concat([to_dense(x) for x in tensors], axis) @@ -2552,7 +2555,7 @@ def arange(start, stop=None, step=1, dtype='int32'): result = cast(result, dtype) return result - +@tf_export('keras.backend.tile') def tile(x, n): """Creates a tensor by tiling `x` by `n`. @@ -3123,20 +3126,20 @@ class EagerExecutionFunction(object): updates_ops.append(update) # We set the update ops to run at the end by conditioning it on output[0] - if updates and not outputs: + if updates and not self.outputs: # Edge case; never happens in practice raise ValueError('Cannot create a Keras backend function with updates' ' but no outputs during eager execution.') with ops.control_dependencies(updates_ops): - outputs[0] = array_ops.identity(outputs[0]) + self.outputs[0] = array_ops.identity(self.outputs[0]) # Prepare graph function # TODO(fchollet): can we restrict `captures` to variables actually used in # the relevant subgraph? - graph.inputs = inputs + list(graph.captures.values()) - graph.outputs = outputs + graph.inputs = self.inputs + list(graph.captures.values()) + graph.outputs = self.outputs graph_fn = eager_function.Function(graph) - graph_fn._num_positional_args = len(inputs) + graph_fn._num_positional_args = len(self.inputs) graph_fn._arg_keywords = [] self._graph_fn = graph_fn @@ -3158,8 +3161,13 @@ class EagerExecutionFunction(object): if value is None: raise ValueError( 'You must feed a value for placeholder %s' % (tensor,)) - converted_inputs.append( - ops.convert_to_tensor(value, dtype=tensor.dtype)) + if not isinstance(value, ops.Tensor): + value = ops.convert_to_tensor(value, dtype=tensor.dtype) + if value.dtype != tensor.dtype: + # Temporary workaround due to `convert_to_tensor` not casting floats. + # See b/119637405 + value = math_ops.cast(value, tensor.dtype) + converted_inputs.append(value) outputs = self._graph_fn(*converted_inputs) return [x.numpy() for x in outputs] @@ -3181,7 +3189,7 @@ def function(inputs, outputs, updates=None, name=None, **kwargs): Raises: ValueError: if invalid kwargs are passed in or if in eager execution. """ - if context.executing_eagerly(): + if ops.executing_eagerly_outside_functions(): if kwargs: raise ValueError('Session keyword arguments are not support during ' 'eager execution. You passed: %s' % (kwargs,)) @@ -3242,7 +3250,8 @@ def rnn(step_function, constants=None, unroll=False, input_length=None, - time_major=False): + time_major=False, + zero_output_for_mask=False): """Iterates over the time dimension of a tensor. Arguments: @@ -3280,7 +3289,9 @@ def rnn(step_function, RNN calculation. However, most TensorFlow data is batch-major, so by default this function accepts input and emits output in batch-major form. - + zero_output_for_mask: Boolean. If True, the output for masked timestep + will be zeros, whereas in the False case, output from previous + timestep is returned. Returns: A tuple, `(last_output, outputs, new_states)`. last_output: the latest output of the rnn, of shape `(samples, ...)` @@ -3332,13 +3343,13 @@ def rnn(step_function, # So we need to broadcast the mask to match the shape of inputs. # That's what the tile call does, it just repeats the mask along its # second dimension n times. - def _expand_mask(mask_t, input_t): + def _expand_mask(mask_t, input_t, fixed_dim=1): assert not nest.is_sequence(mask_t) assert not nest.is_sequence(input_t) rank_diff = len(input_t.shape) - len(mask_t.shape) for _ in range(rank_diff): mask_t = array_ops.expand_dims(mask_t) - expand_dims = [1] + input_t.shape.as_list()[1:] + expand_dims = [1] * fixed_dim + input_t.shape.as_list()[fixed_dim:] return array_ops.tile(mask_t, expand_dims) if unroll: @@ -3397,6 +3408,17 @@ def rnn(step_function, last_output = successive_outputs[-1] new_states = successive_states[-1] outputs = array_ops.stack(successive_outputs) + + if zero_output_for_mask: + last_output = array_ops.where( + _expand_mask(mask_list[-1], last_output), + last_output, + zeros_like(last_output)) + outputs = array_ops.where( + _expand_mask(mask, outputs, fixed_dim=2), + outputs, + zeros_like(outputs)) + else: for i in range(time_steps): inp = _get_input_tensor(i) @@ -3490,11 +3512,12 @@ def rnn(step_function, tuple(states) + tuple(constants)) # mask output flat_output = nest.flatten(output) - flat_previous_output = nest.flatten(prev_output) + flat_mask_output = (flat_zero_output if zero_output_for_mask + else nest.flatten(prev_output)) tiled_mask_t = tuple(_expand_mask(mask_t, o) for o in flat_output) flat_new_output = tuple( - array_ops.where(m, o, po) for m, o, po in zip( - tiled_mask_t, flat_output, flat_previous_output)) + array_ops.where(m, o, zo) for m, o, zo in zip( + tiled_mask_t, flat_output, flat_mask_output)) # mask states flat_state = nest.flatten(states) @@ -3503,8 +3526,8 @@ def rnn(step_function, new_state.set_shape(state.shape) tiled_mask_t = tuple(_expand_mask(mask_t, s) for s in flat_state) flat_final_state = tuple( - array_ops.where(m, o, po) - for m, o, po in zip(tiled_mask_t, flat_new_state, flat_state)) + array_ops.where(m, s, ps) + for m, s, ps in zip(tiled_mask_t, flat_new_state, flat_state)) new_states = nest.pack_sequence_as(new_states, flat_final_state) output_ta_t = tuple( @@ -3552,12 +3575,12 @@ def rnn(step_function, **while_loop_kwargs) new_states = final_outputs[2:] - last_time = final_outputs[0] output_ta = final_outputs[1] outputs = tuple(o.stack() for o in output_ta) + last_output = tuple(o[-1] for o in outputs) + outputs = nest.pack_sequence_as(output_time_zero, outputs) - last_output = tuple(o.read(last_time - 1) for o in output_ta) last_output = nest.pack_sequence_as(output_time_zero, last_output) # static shape inference @@ -3662,13 +3685,13 @@ def in_train_phase(x, alt, training=None): if training is None: training = learning_phase() - if training is 1 or training is True: + if training == 1 or training is True: if callable(x): return x() else: return x - elif training is 0 or training is False: + elif training == 0 or training is False: if callable(alt): return alt() else: diff --git a/tensorflow/python/keras/backend_test.py b/tensorflow/python/keras/backend_test.py index d8aa3e9b52..fdb9b281cb 100644 --- a/tensorflow/python/keras/backend_test.py +++ b/tensorflow/python/keras/backend_test.py @@ -136,7 +136,7 @@ class BackendUtilsTest(test.TestCase): x = keras.Input((3,)) y = keras.layers.BatchNormalization()(x) if not context.executing_eagerly(): - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) sess.run(y, feed_dict={x: np.random.random((2, 3))}) def test_learning_phase_scope(self): @@ -1069,13 +1069,13 @@ class BackendNNOpsTest(test.TestCase, parameterized.TestCase): initial_states, **kwargs) # check static shape inference - self.assertEquals(last_output.get_shape().as_list(), - [num_samples, output_dim]) - self.assertEquals(outputs.get_shape().as_list(), - [num_samples, timesteps, output_dim]) + self.assertEqual(last_output.get_shape().as_list(), + [num_samples, output_dim]) + self.assertEqual(outputs.get_shape().as_list(), + [num_samples, timesteps, output_dim]) for state in new_states: - self.assertEquals(state.get_shape().as_list(), - [num_samples, output_dim]) + self.assertEqual(state.get_shape().as_list(), + [num_samples, output_dim]) last_output_list[i].append(keras.backend.eval(last_output)) outputs_list[i].append(keras.backend.eval(outputs)) @@ -1173,7 +1173,7 @@ class BackendNNOpsTest(test.TestCase, parameterized.TestCase): self.assertEqual(outputs.get_shape().as_list(), [num_samples, timesteps, output_dim]) # for state in new_states: - # self.assertEquals(state.get_shape().as_list(), + # self.assertEqual(state.get_shape().as_list(), # [num_samples, output_dim]) self.assertEqual(new_states[0].get_shape().as_list(), [num_samples, output_dim]) @@ -1223,6 +1223,121 @@ class BackendNNOpsTest(test.TestCase, parameterized.TestCase): for s, u_s in zip(additional_state_list[2], additional_state_list[3]): self.assertAllClose(s, u_s, atol=1e-04) + def test_rnn_output_and_state_masking_independent(self): + num_samples = 2 + num_timesteps = 4 + state_and_io_size = 2 + mask_last_num_timesteps = 2 # for second sample only + + # a step function that just outputs inputs, + # but increments states +1 per timestep + def step_function(inputs, states): + return inputs, [s + 1 for s in states] + + inputs_vals = np.random.random( + (num_samples, num_timesteps, state_and_io_size)) + initial_state_vals = np.random.random((num_samples, state_and_io_size)) + # masking of two last timesteps for second sample only + mask_vals = np.ones((num_samples, num_timesteps)) + mask_vals[1, -mask_last_num_timesteps:] = 0 + + # outputs expected to be same as inputs for the first sample + expected_outputs = inputs_vals.copy() + # but for the second sample all outputs in masked region should be the same + # as last output before masked region + expected_outputs[1, -mask_last_num_timesteps:] = \ + expected_outputs[1, -(mask_last_num_timesteps + 1)] + + expected_last_state = initial_state_vals.copy() + # first state should be incremented for every timestep (no masking) + expected_last_state[0] += num_timesteps + # second state should not be incremented for last two timesteps + expected_last_state[1] += (num_timesteps - mask_last_num_timesteps) + + # verify same expected output for `unroll=true/false` + inputs = keras.backend.variable(inputs_vals) + initial_states = [keras.backend.variable(initial_state_vals)] + mask = keras.backend.variable(mask_vals) + for unroll in [True, False]: + _, outputs, last_states = keras.backend.rnn( + step_function, + inputs, + initial_states, + mask=mask, + unroll=unroll, + input_length=num_timesteps if unroll else None) + + self.assertAllClose(keras.backend.eval(outputs), expected_outputs) + self.assertAllClose(keras.backend.eval( + last_states[0]), expected_last_state) + + def test_rnn_output_num_dim_larger_than_2_masking(self): + num_samples = 3 + num_timesteps = 4 + num_features = 5 + + def step_function(inputs, states): + outputs = keras.backend.tile(keras.backend.expand_dims(inputs), [1, 1, 2]) + return outputs, [keras.backend.identity(s) for s in states] + # Note: cannot just return states (which can be a problem) -> + # tensorflow/python/ops/resource_variable_ops.py", line 824, in set_shape + # NotImplementedError: ResourceVariable does not implement set_shape() + + inputs_vals = np.random.random((num_samples, num_timesteps, num_features)) + initial_state_vals = np.random.random((num_samples, 6)) + mask_vals = np.ones((num_samples, num_timesteps)) + mask_vals[-1, -1] = 0 # final timestep masked for last sample + + expected_outputs = np.repeat(inputs_vals[..., None], repeats=2, axis=-1) + # for the last sample, the final timestep (in masked region) should be the + # same as the second to final output (before masked region) + expected_outputs[-1, -1] = expected_outputs[-1, -2] + + inputs = keras.backend.variable(inputs_vals) + initial_states = [keras.backend.variable(initial_state_vals)] + mask = keras.backend.variable(mask_vals) + for unroll in [True, False]: + _, outputs, _ = keras.backend.rnn( + step_function, + inputs, + initial_states, + mask=mask, + unroll=unroll, + input_length=num_timesteps if unroll else None) + + self.assertAllClose(keras.backend.eval(outputs), expected_outputs) + + def test_rnn_state_num_dim_larger_than_2_masking(self): + num_samples = 3 + num_timesteps = 4 + + def step_function(inputs, states): + return inputs, [s + 1 for s in states] + + inputs_vals = np.random.random((num_samples, num_timesteps, 5)) + initial_state_vals = np.random.random((num_samples, 6, 7)) + mask_vals = np.ones((num_samples, num_timesteps)) + mask_vals[0, -2:] = 0 # final two timesteps masked for first sample + + expected_last_state = initial_state_vals.copy() + expected_last_state[0] += (num_timesteps - 2) + expected_last_state[1:] += num_timesteps + + inputs = keras.backend.variable(inputs_vals) + initial_states = [keras.backend.variable(initial_state_vals)] + mask = keras.backend.variable(mask_vals) + for unroll in [True, False]: + _, _, last_states = keras.backend.rnn( + step_function, + inputs, + initial_states, + mask=mask, + unroll=unroll, + input_length=num_timesteps if unroll else None) + + self.assertAllClose( + keras.backend.eval(last_states[0]), expected_last_state) + def test_normalize_batch_in_training(self): val = np.random.random((10, 3, 10, 10)) x = keras.backend.variable(val) @@ -1307,6 +1422,7 @@ class TestCTC(test.TestCase): decode_truth[i] == keras.backend.eval(decode_pred_tf[i]))) self.assertAllClose(log_prob_truth, log_prob_pred) + @test_util.run_deprecated_v1 def test_ctc_batch_cost(self): with self.cached_session(): label_lens = np.expand_dims(np.asarray([5, 4]), 1) @@ -1392,6 +1508,7 @@ class TestRandomOps(test.TestCase): class BackendGraphTests(test.TestCase): + @test_util.run_deprecated_v1 def test_is_placeholder(self): x = keras.backend.placeholder(shape=(1,)) self.assertEqual(keras.backend.is_placeholder(x), True) @@ -1431,6 +1548,7 @@ class BackendGraphTests(test.TestCase): output_values = f([None, None]) self.assertEqual(output_values, [5., 6.]) + @test_util.run_deprecated_v1 def test_function_tf_feed_symbols(self): # Test Keras backend functions with TF tensor inputs. with self.cached_session(): @@ -1464,6 +1582,7 @@ class BackendGraphTests(test.TestCase): outs = f([y5, y2, None]) self.assertEqual(outs, [11., 2.]) + @test_util.run_deprecated_v1 def test_function_tf_fetches(self): # Additional operations can be passed to tf.Session().run() via its # `fetches` arguments. In contrast to `updates` argument of @@ -1486,6 +1605,7 @@ class BackendGraphTests(test.TestCase): self.assertEqual(keras.backend.get_session().run(fetches=[x, y]), [11., 5.]) + @test_util.run_deprecated_v1 def test_function_tf_feed_dict(self): # Additional substitutions can be passed to `tf.Session().run()` via its # `feed_dict` arguments. Note that the feed_dict is passed once in the @@ -1518,6 +1638,7 @@ class BackendGraphTests(test.TestCase): self.assertEqual(keras.backend.get_session().run(fetches=[x, y]), [30., 40.]) + @test_util.run_deprecated_v1 def test_function_tf_run_options_with_run_metadata(self): with self.cached_session(): x_placeholder = keras.backend.placeholder(shape=()) @@ -1543,6 +1664,7 @@ class BackendGraphTests(test.TestCase): self.assertEqual(output1, [30.]) self.assertEqual(len(run_metadata.partition_graphs), 0) + @test_util.run_deprecated_v1 def test_function_fetch_callbacks(self): class CallbackStub(object): @@ -1579,6 +1701,7 @@ class BackendGraphTests(test.TestCase): x = keras.backend.placeholder(shape=(3, 4), sparse=True) self.assertEqual(x.get_shape().as_list(), [3, 4]) + @test_util.run_deprecated_v1 def test_batch_normalization(self): # No eager CPU kernel. g_val = np.random.random((3,)) diff --git a/tensorflow/python/keras/callbacks.py b/tensorflow/python/keras/callbacks.py index fde17cb6bc..2d7d5a415d 100644 --- a/tensorflow/python/keras/callbacks.py +++ b/tensorflow/python/keras/callbacks.py @@ -24,7 +24,6 @@ import copy import csv import io import json -import math import os import time @@ -35,7 +34,6 @@ from tensorflow.python.data.ops import iterator_ops from tensorflow.python.eager import context from tensorflow.python.framework import dtypes from tensorflow.python.keras import backend as K -from tensorflow.python.keras.engine.training_utils import standardize_input_data from tensorflow.python.keras.utils.data_utils import Sequence from tensorflow.python.keras.utils.generic_utils import Progbar from tensorflow.python.ops import array_ops @@ -54,17 +52,14 @@ except ImportError: requests = None +# pylint: disable=protected-access def configure_callbacks(callbacks, model, do_validation=False, - val_inputs=None, - val_targets=None, - val_sample_weights=None, batch_size=None, epochs=None, steps_per_epoch=None, samples=None, - validation_steps=None, verbose=1, count_mode='steps', mode='train'): @@ -74,17 +69,10 @@ def configure_callbacks(callbacks, callbacks: List of Callbacks. model: Model being trained. do_validation: Whether or not validation loop will be run. - val_inputs: Inputs to Model for validation loop. Can be any - data format Keras accepts. - val_targets: Targets for Model for validation loop. Can be any - data format Keras accepts. - val_sample_weights: Sample weights for Model for validation loop. - Can be any data format Keras accepts. batch_size: Number of samples per batch. epochs: Number of epoch to train. steps_per_epoch: Number of batches to run per training epoch. samples: Number of training samples. - validation_steps: Number of batches to run per validation epoch. verbose: int, 0 or 1. Keras logging verbosity to pass to ProgbarLogger. count_mode: One of 'steps' or 'samples'. Per-batch or per-sample count. mode: String. One of 'train', 'test', or 'predict'. Which loop mode to @@ -114,24 +102,17 @@ def configure_callbacks(callbacks, callback_list = CallbackList(callbacks) # Set callback model - callback_model = model._get_callback_model() # pylint: disable=protected-access - if do_validation and val_inputs and not context.executing_eagerly(): - # Need to create the eval_function before start of the first epoch - # because TensorBoard callback on_epoch_begin adds summary to the - # list of fetches of the eval_function - callback_model._make_eval_function() # pylint: disable=protected-access + callback_model = model._get_callback_model() callback_list.set_model(callback_model) # Set callback parameters callback_metrics = [] # When we have deferred build scenario with iterator input, we will compile # when we standardize first batch of data. - if mode != 'predict' and model._is_compiled: # pylint: disable=protected-access + if mode != 'predict' and hasattr(model, 'metrics_names'): callback_metrics = copy.copy(model.metrics_names) if do_validation: callback_metrics += ['val_' + n for n in model.metrics_names] - if validation_steps is None and isinstance(val_inputs, Sequence): - validation_steps = len(val_inputs) callback_params = { 'batch_size': batch_size, 'epochs': epochs, @@ -140,27 +121,19 @@ def configure_callbacks(callbacks, 'verbose': verbose, 'do_validation': do_validation, 'metrics': callback_metrics, - 'validation_steps': validation_steps } callback_list.set_params(callback_params) - # Pass validation data to callbacks - # TODO(omalleyt): remove this once val hooks are ready. - if not val_inputs: - val_data = [] - elif _is_generator_like(val_inputs): - val_data = val_inputs - else: - val_data = val_inputs + val_targets - if val_sample_weights: - val_data += val_sample_weights - if not isinstance(K.learning_phase(), int): - val_data += [0.] - for cbk in callbacks: - cbk.validation_data = val_data + if (do_validation and not model._distribution_strategy and + not model.run_eagerly): + # Need to create the eval_function before start of the first epoch + # because TensorBoard callback on_epoch_begin adds summary to the + # list of fetches of the eval_function + callback_model._make_eval_function() callback_list.model.stop_training = False return callback_list +# pylint: enable=protected-access def _is_generator_like(data): @@ -491,7 +464,8 @@ class ProgbarLogger(Callback): self.progbar = Progbar( target=self.target, verbose=self.verbose, - stateful_metrics=self.stateful_metrics) + stateful_metrics=self.stateful_metrics, + unit_name='step' if self.use_steps else 'sample') def on_batch_begin(self, batch, logs=None): if self.seen < self.target: @@ -953,6 +927,7 @@ class TensorBoard(Callback): self.batch_size = batch_size self._current_batch = 0 self._total_batches_seen = 0 + self._total_val_batches_seen = 0 self.embeddings_freq = embeddings_freq self.embeddings_layer_names = embeddings_layer_names self.embeddings_metadata = embeddings_metadata @@ -1041,8 +1016,10 @@ class TensorBoard(Callback): # If both embedding_freq and embeddings_data are available, we will # visualize embeddings. if self.embeddings_freq and self.embeddings_data is not None: - self.embeddings_data = standardize_input_data(self.embeddings_data, - model.input_names) + # Avoid circular dependency. + from tensorflow.python.keras.engine import training_utils # pylint: disable=g-import-not-at-top + self.embeddings_data = training_utils.standardize_input_data( + self.embeddings_data, model.input_names) # If embedding_layer_names are not provided, get all of the embedding # layers from the model. @@ -1107,10 +1084,8 @@ class TensorBoard(Callback): projector.visualize_embeddings(self.writer, config) def _fetch_callback(self, summary): - self.writer.add_summary( - summary, - self._epoch + self._current_val_batch / self._validation_batches) - self._current_val_batch += 1 + self.writer.add_summary(summary, self._total_val_batches_seen) + self._total_val_batches_seen += 1 def _write_custom_summaries(self, step, logs=None): """Writes metrics out as custom scalar summaries. @@ -1141,22 +1116,6 @@ class TensorBoard(Callback): self.writer.add_summary(summary, step) self.writer.flush() - def on_train_begin(self, logs=None): - """Checks if histogram summaries can be run.""" - # will never be set when in eager - if self.histogram_freq: - if self.params.get('validation_steps', None) is not None: - self._validation_batches = self.params['validation_steps'] - elif self.validation_data: - self._validation_batches = math.ceil( - self.validation_data[0].shape[0] / self.batch_size) - else: - raise ValueError('If printing histograms, validation data must be ' - 'provided.') - if self._validation_batches == 0: - raise ValueError( - 'If printing histograms, validation data must have length > 0.') - def on_batch_end(self, batch, logs=None): """Writes scalar summaries for metrics on every training batch.""" # Don't output batch_size and batch number as Tensorboard summaries @@ -1177,7 +1136,6 @@ class TensorBoard(Callback): # check if histogram summary should be run for this epoch if self.histogram_freq and epoch % self.histogram_freq == 0: self._epoch = epoch - self._current_val_batch = 0 # pylint: disable=protected-access # add the histogram summary op if it should run this epoch if self.merged not in self.model._eval_function.fetches: diff --git a/tensorflow/python/keras/callbacks_test.py b/tensorflow/python/keras/callbacks_test.py index 9d9ede22c0..6c9a382b32 100644 --- a/tensorflow/python/keras/callbacks_test.py +++ b/tensorflow/python/keras/callbacks_test.py @@ -36,7 +36,6 @@ from tensorflow.python.framework import test_util from tensorflow.python.keras import testing_utils from tensorflow.python.platform import test from tensorflow.python.platform import tf_logging as logging -from tensorflow.python.summary.writer import writer_cache from tensorflow.python.training import adam try: @@ -404,6 +403,7 @@ class KerasCallbacksTest(test.TestCase): float(keras.backend.get_value( model.optimizer.lr)) - 0.01 / 4) < keras.backend.epsilon() + @test_util.run_deprecated_v1 def test_ReduceLROnPlateau(self): with self.cached_session(): np.random.seed(1337) @@ -675,6 +675,7 @@ class KerasCallbacksTest(test.TestCase): self.assertEqual(len(loss), 1) self.assertEqual(loss[0], np.inf) + @test_util.run_deprecated_v1 def test_TensorBoard(self): np.random.seed(1337) @@ -778,78 +779,7 @@ class KerasCallbacksTest(test.TestCase): data_generator(True), len(x_train), epochs=2, callbacks=cbks) assert os.path.exists(temp_dir) - def test_TensorBoard_histogram_freq_must_have_validation_data(self): - np.random.seed(1337) - tmpdir = self.get_temp_dir() - self.addCleanup(shutil.rmtree, tmpdir, ignore_errors=True) - - with self.cached_session(): - filepath = os.path.join(tmpdir, 'logs') - - (x_train, y_train), (x_test, y_test) = testing_utils.get_test_data( - train_samples=TRAIN_SAMPLES, - test_samples=TEST_SAMPLES, - input_shape=(INPUT_DIM,), - num_classes=NUM_CLASSES) - y_test = keras.utils.to_categorical(y_test) - y_train = keras.utils.to_categorical(y_train) - - def data_generator(train): - if train: - max_batch_index = len(x_train) // BATCH_SIZE - else: - max_batch_index = len(x_test) // BATCH_SIZE - i = 0 - while 1: - if train: - # simulate multi-input/output models - yield (x_train[i * BATCH_SIZE: (i + 1) * BATCH_SIZE], - y_train[i * BATCH_SIZE: (i + 1) * BATCH_SIZE]) - else: - yield (x_test[i * BATCH_SIZE: (i + 1) * BATCH_SIZE], - y_test[i * BATCH_SIZE: (i + 1) * BATCH_SIZE]) - i += 1 - i %= max_batch_index - - inp = keras.Input((INPUT_DIM,)) - hidden = keras.layers.Dense(2, activation='relu')(inp) - hidden = keras.layers.Dropout(0.1)(hidden) - output = keras.layers.Dense(NUM_CLASSES, activation='softmax')(hidden) - model = keras.models.Model(inputs=inp, outputs=output) - model.compile(loss='categorical_crossentropy', - optimizer='sgd', - metrics=['accuracy']) - - # we must generate new callbacks for each test, as they aren't stateless - def callbacks_factory(histogram_freq): - return [keras.callbacks.TensorBoard( - log_dir=filepath, - histogram_freq=histogram_freq, - write_images=True, write_grads=True, - batch_size=5)] - - # fit w/o validation data should raise ValueError if histogram_freq > 0 - cbs = callbacks_factory(histogram_freq=1) - with self.assertRaises(ValueError): - model.fit( - x_train, y_train, batch_size=BATCH_SIZE, callbacks=cbs, epochs=3) - - for cb in cbs: - cb.on_train_end() - - # fit generator without validation data should raise ValueError if - # histogram_freq > 0 - cbs = callbacks_factory(histogram_freq=1) - with self.assertRaises(ValueError): - model.fit_generator( - data_generator(True), len(x_train), epochs=2, callbacks=cbs) - - for cb in cbs: - cb.on_train_end() - - # Make sure file writer cache is clear to avoid failures during cleanup. - writer_cache.FileWriterCache.clear() - + @test_util.run_deprecated_v1 def test_TensorBoard_multi_input_output(self): np.random.seed(1337) tmpdir = self.get_temp_dir() @@ -921,6 +851,7 @@ class KerasCallbacksTest(test.TestCase): callbacks=callbacks_factory(histogram_freq=1)) assert os.path.isdir(filepath) + @test_util.run_deprecated_v1 def test_Tensorboard_histogram_summaries_in_test_function(self): class FileWriterStub(object): @@ -996,8 +927,9 @@ class KerasCallbacksTest(test.TestCase): epochs=3, verbose=0) - self.assertAllEqual(tsb.writer.steps_seen, [0, 0.5, 1, 1.5, 2, 2.5]) + self.assertAllEqual(tsb.writer.steps_seen, [0, 1, 2, 3, 4, 5]) + @test_util.run_deprecated_v1 def test_Tensorboard_histogram_summaries_with_generator(self): np.random.seed(1337) tmpdir = self.get_temp_dir() @@ -1129,6 +1061,7 @@ class KerasCallbacksTest(test.TestCase): assert os.path.exists(temp_dir) + @test_util.run_deprecated_v1 def test_Tensorboard_batch_logging(self): class FileWriterStub(object): @@ -1163,6 +1096,7 @@ class KerasCallbacksTest(test.TestCase): self.assertEqual(tb_cbk.writer.summary_values, [0., 1., 2., 3., 4.]) self.assertEqual(tb_cbk.writer.summary_tags, ['batch_acc'] * 5) + @test_util.run_deprecated_v1 def test_Tensorboard_epoch_and_batch_logging(self): class FileWriterStub(object): @@ -1234,6 +1168,7 @@ class KerasCallbacksTest(test.TestCase): self.assertTrue(os.path.exists(temp_dir)) + @test_util.run_deprecated_v1 def test_TensorBoard_update_freq(self): class FileWriterStub(object): @@ -1325,6 +1260,7 @@ class KerasCallbacksTest(test.TestCase): callbacks=cbks, epochs=1) + @test_util.run_deprecated_v1 def test_fit_generator_with_callback(self): class TestCallback(keras.callbacks.Callback): diff --git a/tensorflow/python/keras/engine/__init__.py b/tensorflow/python/keras/engine/__init__.py index 26aed34766..005f6462ff 100644 --- a/tensorflow/python/keras/engine/__init__.py +++ b/tensorflow/python/keras/engine/__init__.py @@ -20,10 +20,10 @@ from __future__ import print_function # TODO(fchollet): Remove hourglass imports once external code is done importing # non-public APIs. -from tensorflow.python.keras.engine.base_layer import InputSpec from tensorflow.python.keras.engine.base_layer import Layer from tensorflow.python.keras.engine.input_layer import Input from tensorflow.python.keras.engine.input_layer import InputLayer +from tensorflow.python.keras.engine.input_spec import InputSpec from tensorflow.python.keras.utils.layer_utils import get_source_inputs del absolute_import diff --git a/tensorflow/python/keras/engine/base_layer.py b/tensorflow/python/keras/engine/base_layer.py index 23419ae150..8e35300342 100644 --- a/tensorflow/python/keras/engine/base_layer.py +++ b/tensorflow/python/keras/engine/base_layer.py @@ -18,8 +18,6 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -import collections as collections_lib -import enum # pylint: disable=g-bad-import-order import functools import inspect # Necessary supplement to tf_inspect to deal with variadic args. @@ -36,13 +34,14 @@ from tensorflow.python.keras import backend from tensorflow.python.keras import constraints from tensorflow.python.keras import initializers from tensorflow.python.keras import regularizers +from tensorflow.python.keras.engine import base_layer_utils +from tensorflow.python.keras.engine import input_spec from tensorflow.python.keras.utils import generic_utils from tensorflow.python.keras.utils import tf_utils # A module that only depends on `keras.layers` import these from here. from tensorflow.python.keras.utils.generic_utils import to_snake_case # pylint: disable=unused-import from tensorflow.python.keras.utils.tf_utils import is_tensor_or_tensor_list # pylint: disable=unused-import from tensorflow.python.ops import array_ops -from tensorflow.python.ops import init_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import variables as tf_variables from tensorflow.python.training.checkpointable import base as checkpointable @@ -54,20 +53,6 @@ from tensorflow.python.util.tf_export import tf_export from tensorflow.tools.docs import doc_controls -class CallConvention(enum.Enum): - """Calling conventions for passing `Layer` inputs to `Layer.call`.""" - # The Layer takes inputs as its first argument, named "inputs" for - # compatibility with the signature of Layer.__call__. This is the mode assumed - # for Layers which are not subclassed Models. - EXPLICIT_INPUTS_ARGUMENT = 1 - # The Layer takes a single positional argument, not named "inputs". It's - # treated like an "inputs" argument. - SINGLE_POSITIONAL_ARGUMENT = 2 - # The Layer has multiple positional arguments to which its inputs should be - # bound. - POSITIONAL_ARGUMENTS_ARE_INPUTS = 3 - - @tf_export('keras.layers.Layer') class Layer(checkpointable.CheckpointableBase): """Base layer class. @@ -102,10 +87,6 @@ class Layer(checkpointable.CheckpointableBase): name: The name of the layer (string). dtype: Default dtype of the layer's weights (default of `None` means use the type of the first input). - trainable_variables: List of trainable variables. - non_trainable_variables: List of non-trainable variables. - variables: List of all variables of this layer, trainable and - non-trainable. updates: List of update ops of this layer. losses: List of losses added by this layer. trainable_weights: List of variables to be included in backprop. @@ -150,9 +131,9 @@ class Layer(checkpointable.CheckpointableBase): self.built = False # Provides information about which inputs are compatible with the layer. self.input_spec = None + self.supports_masking = False self._init_set_name(name) - self._activity_regularizer = kwargs.pop('activity_regularizer', None) self._trainable_weights = [] self._non_trainable_weights = [] @@ -170,29 +151,25 @@ class Layer(checkpointable.CheckpointableBase): # in eager mode or graph mode alternatively, we need to keep track of # eager losses and symbolic losses via separate attributes. self._eager_losses = [] + # A list of metric instances corresponding to the symbolic metric tensors + # added using the `add_metric` API. + self._metrics = [] + # TODO(psv): Remove this property. + # A dictionary that maps metric names to metric result tensors. The results + # are the running averages of metric values over an epoch. + self._metrics_tensors = {} self._dtype = None if dtype is None else dtypes.as_dtype(dtype).name self._call_fn_args = function_utils.fn_args(self.call) self._compute_previous_mask = ('mask' in self._call_fn_args or hasattr(self, 'compute_mask')) - self._call_convention = CallConvention.EXPLICIT_INPUTS_ARGUMENT + self._call_convention = (base_layer_utils + .CallConvention.EXPLICIT_INPUTS_ARGUMENT) # These lists will be filled via successive calls # to self._add_inbound_node(). self._inbound_nodes = [] self._outbound_nodes = [] - self.supports_masking = False - - # Mark if a layer supports using graph functions in the eager - # fit/predict/evaluate loop - # TODO(kaftan): merge this with the _static_graph_friendly flag once - # enough eager function bugs involving control flow / tensorarrays have - # been fixed, and static-graph-friendly layers will almost always work in - # eager graph functions. - # We conservatively make this flag opt-in for now to avoid causing existing - # custom layers to crash. - self._can_use_graph_functions = False - call_argspec = tf_inspect.getfullargspec(self.call) if 'training' in call_argspec.args: self._expects_training_arg = True @@ -200,7 +177,7 @@ class Layer(checkpointable.CheckpointableBase): self._expects_training_arg = False # Whether the `call` method can be used to build a TF graph without issues. - self._static_graph_friendly = True + self._call_is_graph_friendly = True # Manage input shape information if passed. if 'input_shape' in kwargs or 'batch_input_shape' in kwargs: @@ -222,543 +199,348 @@ class Layer(checkpointable.CheckpointableBase): else: self._initial_weights = None - @property - def _is_static_graph_friendly(self): - return self._static_graph_friendly - - @_is_static_graph_friendly.setter - def _is_static_graph_friendly(self, value): - if value not in {True, False}: - raise ValueError('`static_graph_friendly` requires a boolean value. ' - 'Received: {}'.format(value)) - self._static_graph_friendly = value - - def _init_set_name(self, name, zero_based=True): - if not name: - self._name = unique_layer_name( - generic_utils.to_snake_case(self.__class__.__name__), - zero_based=zero_based) - else: - self._name = name - - @property - def dtype(self): - return self._dtype - - @property - def name(self): - return self._name - - @property - def activity_regularizer(self): - """Optional regularizer function for the output of this layer.""" - return self._activity_regularizer - - @activity_regularizer.setter - def activity_regularizer(self, regularizer): - """Optional regularizer function for the output of this layer.""" - self._activity_regularizer = self._no_dependency(regularizer) + def build(self, input_shape): + """Creates the variables of the layer (optional, for subclass implementers). - @property - def trainable_weights(self): - return self._trainable_weights if self.trainable else [] + This is a method that implementers of subclasses of `Layer` or `Model` + can override if they need a state-creation step in-between + layer instantiation and layer call. - @property - def non_trainable_weights(self): - if self.trainable: - return self._non_trainable_weights - else: - return self._trainable_weights + self._non_trainable_weights + This is typically used to create the weights of `Layer` subclasses. - @property - def trainable_variables(self): - return self.trainable_weights + Arguments: + input_shape: Instance of `TensorShape`, or list of instances of + `TensorShape` if the layer expects a list of inputs + (one instance per input). + """ + self.built = True - @property - def non_trainable_variables(self): - return self.non_trainable_weights + @doc_controls.for_subclass_implementers + def call(self, inputs, **kwargs): # pylint: disable=unused-argument + """This is where the layer's logic lives. - @property - def weights(self): - """Returns the list of all layer variables/weights. + Arguments: + inputs: Input tensor, or list/tuple of input tensors. + **kwargs: Additional keyword arguments. Returns: - A list of variables. + A tensor or list/tuple of tensors. """ - return self.trainable_weights + self.non_trainable_weights + return inputs - @property - def variables(self): - """Returns the list of all layer variables/weights. + @doc_controls.for_subclass_implementers + def add_weight(self, + name, + shape, + dtype=None, + initializer=None, + regularizer=None, + trainable=None, + constraint=None, + partitioner=None, + use_resource=None, + synchronization=tf_variables.VariableSynchronization.AUTO, + aggregation=tf_variables.VariableAggregation.NONE, + **kwargs): + """Adds a new variable to the layer, or gets an existing one; returns it. - Returns: - A list of variables. - """ - return self.weights + Arguments: + name: variable name. + shape: variable shape. + dtype: The type of the variable. Defaults to `self.dtype` or `float32`. + initializer: initializer instance (callable). + regularizer: regularizer instance (callable). + trainable: whether the variable should be part of the layer's + "trainable_variables" (e.g. variables, biases) + or "non_trainable_variables" (e.g. BatchNorm mean, stddev). + Note, if the current variable scope is marked as non-trainable + then this parameter is ignored and any added variables are also + marked as non-trainable. `trainable` defaults to `True` unless + `synchronization` is set to `ON_READ`. + constraint: constraint instance (callable). + partitioner: Partitioner to be passed to the `Checkpointable` API. + use_resource: Whether to use `ResourceVariable`. + synchronization: Indicates when a distributed a variable will be + aggregated. Accepted values are constants defined in the class + `tf.VariableSynchronization`. By default the synchronization is set to + `AUTO` and the current `DistributionStrategy` chooses + when to synchronize. If `synchronization` is set to `ON_READ`, + `trainable` must not be set to `True`. + aggregation: Indicates how a distributed variable will be aggregated. + Accepted values are constants defined in the class + `tf.VariableAggregation`. + **kwargs: Additional keyword arguments. Accepted values are `getter` and + `collections`. - @property - def updates(self): - if context.executing_eagerly(): - raise RuntimeError('Layer.updates not supported in Eager mode.') - if not self.trainable and not self.stateful: - return [] - return self._updates + Returns: + The created variable. Usually either a `Variable` or `ResourceVariable` + instance. If `partitioner` is not `None`, a `PartitionedVariable` + instance is returned. - @doc_controls.for_subclass_implementers - def add_update(self, updates, inputs=None): - """Add update op(s), potentially dependent on layer inputs. + Raises: + RuntimeError: If called with partioned variable regularization and + eager execution is enabled. + ValueError: When giving unsupported dtype and no initializer or when + trainable has been set to True with synchronization set as `ON_READ`. + """ + # Validate optional keyword arguments. + for kwarg in kwargs: + if kwarg not in ['getter', 'collections']: + raise TypeError('Unknown keyword argument:', kwarg) + getter = kwargs.pop('getter', None) + collections = kwargs.pop('collections', None) - Weight updates (for instance, the updates of the moving mean and variance - in a BatchNormalization layer) may be dependent on the inputs passed - when calling a layer. Hence, when reusing the same layer on - different inputs `a` and `b`, some entries in `layer.updates` may be - dependent on `a` and some on `b`. This method automatically keeps track - of dependencies. + if dtype is None: + dtype = self.dtype or backend.floatx() + dtype = dtypes.as_dtype(dtype) + initializer = initializers.get(initializer) + regularizer = regularizers.get(regularizer) + constraint = constraints.get(constraint) - The `get_updates_for` method allows to retrieve the updates relevant to a - specific set of inputs. + if synchronization == tf_variables.VariableSynchronization.ON_READ: + if trainable: + raise ValueError( + 'Synchronization value can be set to ' + 'VariableSynchronization.ON_READ only for non-trainable variables. ' + 'You have specified trainable=True and ' + 'synchronization=VariableSynchronization.ON_READ.') + else: + # Set trainable to be false when variable is to be synced on read. + trainable = False + elif trainable is None: + trainable = True - This call is ignored when eager execution is enabled (in that case, variable - updates are run on the fly and thus do not need to be tracked for later - execution). + # Initialize variable when no initializer provided + if initializer is None: + # If dtype is DT_FLOAT, provide a uniform unit scaling initializer + if dtype.is_floating: + initializer = initializers.glorot_uniform() + # If dtype is DT_INT/DT_UINT, provide a default value `zero` + # If dtype is DT_BOOL, provide a default value `FALSE` + elif dtype.is_integer or dtype.is_unsigned or dtype.is_bool: + initializer = initializers.zeros() + # NOTES:Do we need to support for handling DT_STRING and DT_COMPLEX here? + else: + raise ValueError('An initializer for variable %s of type %s is required' + ' for layer %s' % (name, dtype.base_dtype, self.name)) - Arguments: - updates: Update op, or list/tuple of update ops. - inputs: If anything other than None is passed, it signals the updates - are conditional on some of the layer's inputs, - and thus they should only be run where these inputs are available. - This is the case for BatchNormalization updates, for instance. - If None, the updates will be taken into account unconditionally, - and you are responsible for making sure that any dependency they might - have is available at runtime. - A step counter might fall into this category. - """ - if context.executing_eagerly(): - return # Updates already applied when in eager mode. + variable = self._add_variable_with_custom_getter( + name=name, + shape=shape, + # TODO(allenl): a `make_variable` equivalent should be added as a + # `Checkpointable` method. + getter=getter or base_layer_utils.make_variable, + # Manage errors in Layer rather than Checkpointable. + overwrite=True, + initializer=initializer, + dtype=dtype, + constraint=constraint, + trainable=trainable and self.trainable, + partitioner=partitioner, + use_resource=use_resource, + collections=collections, + synchronization=synchronization, + aggregation=aggregation) + backend.track_variable(variable) - def process_update(x): - if isinstance(x, ops.Operation): - return x - elif hasattr(x, 'op'): - return x.op - else: - return ops.convert_to_tensor(x) + if regularizer is not None: + # TODO(fchollet): in the future, this should be handled at the + # level of variable creation, and weight regularization losses + # should be variable attributes. + self._handle_weight_regularization(name, variable, regularizer) - updates = generic_utils.to_list(updates) - updates = [process_update(x) for x in updates] - self._updates += updates - if inputs is None: - for u in updates: - u._unconditional_update = True # pylint: disable=protected-access + if trainable: + self._trainable_weights.append(variable) else: - for u in updates: - u._unconditional_update = False # pylint: disable=protected-access + self._non_trainable_weights.append(variable) + return variable - def get_updates_for(self, inputs): - """Retrieves updates relevant to a specific set of inputs. + def get_config(self): + """Returns the config of the layer. - Arguments: - inputs: Input tensor or list/tuple of input tensors. + A layer config is a Python dictionary (serializable) + containing the configuration of a layer. + The same layer can be reinstantiated later + (without its trained weights) from this configuration. - Returns: - List of update ops of the layer that depend on `inputs`. + The config of a layer does not include connectivity + information, nor the layer class name. These are handled + by `Network` (one layer of abstraction above). - Raises: - RuntimeError: If called in Eager mode. + Returns: + Python dictionary. """ - if context.executing_eagerly(): - raise RuntimeError('`get_updates_for()` not supported in Eager mode.') + config = {'name': self.name, 'trainable': self.trainable} + if hasattr(self, '_batch_input_shape'): + config['batch_input_shape'] = self._batch_input_shape + if hasattr(self, 'dtype'): + config['dtype'] = self.dtype + return config - # Updates disabled if layer is not trainable and not explicitly stateful. - if not self.trainable and not self.stateful: - return [] + @classmethod + def from_config(cls, config): + """Creates a layer from its config. - if inputs is None: - # Requesting unconditional updates. - return [x for x in self.updates if x._unconditional_update] # pylint: disable=protected-access + This method is the reverse of `get_config`, + capable of instantiating the same layer from the config + dictionary. It does not handle layer connectivity + (handled by Network), nor weights (handled by `set_weights`). - # Requesting input-conditional updates. - inputs = nest.flatten(inputs) - reachable = tf_utils.get_reachable_from_inputs(inputs, self.updates) - updates = [] - for update in self.updates: - if update in reachable: - updates.append(update) - return updates + Arguments: + config: A Python dictionary, typically the + output of get_config. - @property - def losses(self): - """Losses which are associated with this `Layer`. + Returns: + A layer instance. + """ + return cls(**config) - Variable regularization tensors are created when this property is accessed, - so it is eager safe: accessing `losses` under a `tf.GradientTape` will - propagate gradients back to the corresponding variables. + def compute_output_shape(self, input_shape): + """Computes the output shape of the layer. + + Assumes that the layer will be built + to match that input shape provided. + + Arguments: + input_shape: Shape tuple (tuple of integers) + or list of shape tuples (one per output tensor of the layer). + Shape tuples can include None for free dimensions, + instead of an integer. Returns: - A list of tensors. + An input shape tuple. """ - collected_losses = [] if context.executing_eagerly(): - collected_losses.extend(self._eager_losses) - else: - collected_losses.extend(self._losses) - for regularizer in self._callable_losses: - loss_tensor = regularizer() - if loss_tensor is not None: - collected_losses.append(loss_tensor) - return collected_losses - - @doc_controls.for_subclass_implementers - def add_loss(self, losses, inputs=None): - """Add loss tensor(s), potentially dependent on layer inputs. - - Some losses (for instance, activity regularization losses) may be dependent - on the inputs passed when calling a layer. Hence, when reusing the same - layer on different inputs `a` and `b`, some entries in `layer.losses` may - be dependent on `a` and some on `b`. This method automatically keeps track - of dependencies. + # In this case we build the model first in order to do shape inference. + # This is acceptable because the framework only calls + # `compute_output_shape` on shape values that the layer would later be + # built for. It would however cause issues in case a user attempts to + # use `compute_output_shape` manually (these users will have to + # implement `compute_output_shape` themselves). + self.build(input_shape) + with context.graph_mode(): + graph = func_graph.FuncGraph('graph') + with graph.as_default(): + if isinstance(input_shape, list): + inputs = [base_layer_utils.generate_placeholders_from_shape(shape) + for shape in input_shape] + else: + inputs = base_layer_utils.generate_placeholders_from_shape( + input_shape) - The `get_losses_for` method allows to retrieve the losses relevant to a - specific set of inputs. + try: + if self._expects_training_arg: + outputs = self(inputs, training=False) + else: + outputs = self(inputs) + except TypeError: + raise NotImplementedError('We could not automatically infer ' + 'the static shape of the layer\'s output.' + ' Please implement the ' + '`compute_output_shape` method on your ' + 'layer (%s).' % self.__class__.__name__) + if isinstance(outputs, list): + return [output.shape for output in outputs] + else: + return outputs.shape + raise NotImplementedError - Note that `add_loss` is not supported when executing eagerly. Instead, - variable regularizers may be added through `add_variable`. Activity - regularization is not supported directly (but such losses may be returned - from `Layer.call()`). + def compute_mask(self, inputs, mask=None): # pylint: disable=unused-argument + """Computes an output mask tensor. Arguments: - losses: Loss tensor, or list/tuple of tensors. Rather than tensors, losses - may also be zero-argument callables which create a loss tensor. - inputs: Ignored when executing eagerly. If anything other than None is - passed, it signals the losses are conditional on some of the layer's - inputs, and thus they should only be run where these inputs are - available. This is the case for activity regularization losses, for - instance. If `None` is passed, the losses are assumed - to be unconditional, and will apply across all dataflows of the layer - (e.g. weight regularization losses). - """ - losses = generic_utils.to_list(losses) - - def _tag_unconditional(loss): - if callable(loss): - loss = loss() - if loss is None: - return None # Will be filtered out when computing the .losses property - if not tensor_util.is_tensor(loss): - loss = ops.convert_to_tensor(loss, dtype=backend.floatx()) - loss._unconditional_loss = (inputs is None) # pylint: disable=protected-access - return loss + inputs: Tensor or list of tensors. + mask: Tensor or list of tensors. - for loss in losses: - if callable(loss): - self._callable_losses.append( - functools.partial(_tag_unconditional, loss)) - else: - if context.executing_eagerly(): - self._eager_losses.append(_tag_unconditional(loss)) + Returns: + None or a tensor (or list of tensors, + one per output tensor of the layer). + """ + if not self.supports_masking: + if mask is not None: + if isinstance(mask, list): + if any(m is not None for m in mask): + raise TypeError('Layer ' + self.name + ' does not support masking, ' + 'but was passed an input_mask: ' + str(mask)) else: - self._losses.append(_tag_unconditional(loss)) + raise TypeError('Layer ' + self.name + ' does not support masking, ' + 'but was passed an input_mask: ' + str(mask)) + # masking not explicitly supported: return None as mask + return None + # if masking is explicitly supported, by default + # carry over the input mask + return mask - def get_losses_for(self, inputs): - """Retrieves losses relevant to a specific set of inputs. + def __call__(self, inputs, *args, **kwargs): + """Wraps `call`, applying pre- and post-processing steps. Arguments: - inputs: Input tensor or list/tuple of input tensors. + inputs: input tensor(s). + *args: additional positional arguments to be passed to `self.call`. + **kwargs: additional keyword arguments to be passed to `self.call`. Returns: - List of loss tensors of the layer that depend on `inputs`. + Output tensor(s). + + Note: + - The following optional keyword arguments are reserved for specific uses: + * `training`: Boolean scalar tensor of Python boolean indicating + whether the `call` is meant for training or inference. + * `mask`: Boolean input mask. + - If the layer's `call` method takes a `mask` argument (as some Keras + layers do), its default value will be set to the mask generated + for `inputs` by the previous layer (if `input` did come from + a layer that generated a corresponding mask, i.e. if it came from + a Keras layer with masking support. Raises: - RuntimeError: If called in Eager mode. + ValueError: if the layer's `call` method returns None (an invalid value). """ - if context.executing_eagerly(): - raise RuntimeError('Layer.get_losses_for not supported in Eager mode.') - - if inputs is None: - # Requesting unconditional losses. - return [x for x in self.losses if x._unconditional_loss] # pylint: disable=protected-access + input_list = nest.flatten(inputs) - # Requesting input-conditional losses. - inputs = nest.flatten(inputs) - # Retrieve the set of tensors in the TF graph that depend on `inputs`. - # The losses we want to return will be part of this set. - # To avoid unnecessary work, we stop the search in case all of - # `self.losses` have been retrieved. - reachable = tf_utils.get_reachable_from_inputs(inputs, self.losses) - losses = [] - for loss in self.losses: - if loss in reachable: - losses.append(loss) - return losses + if context.executing_eagerly(): + # Accept NumPy inputs by converting to Tensors when executing eagerly. + if all(isinstance(x, (np.ndarray, float, int)) for x in input_list): + inputs = nest.map_structure(ops.convert_to_tensor, inputs) + input_list = nest.flatten(inputs) - def _name_scope(self): - return self.name + # We will attempt to build a TF graph if & only if all inputs are symbolic. + # This is always the case in graph mode. It can also be the case in eager + # mode when all inputs can be traced back to `keras.Input()` (when building + # models using the functional API). + build_graph = tf_utils.are_all_symbolic_tensors(input_list) + executing_eagerly = context.executing_eagerly() - def build(self, input_shape): - """Creates the variables of the layer.""" - self.built = True + # Handle Keras mask propagation from previous layer to current layer. + previous_mask = None + if build_graph and (not hasattr(self, '_compute_previous_mask') or + self._compute_previous_mask): + previous_mask = base_layer_utils.collect_previous_mask(inputs) + if not hasattr(self, '_call_fn_args'): + self._call_fn_args = self._no_dependency( + function_utils.fn_args(self.call)) + if ('mask' in self._call_fn_args and 'mask' not in kwargs and + not generic_utils.is_all_none(previous_mask)): + # The previous layer generated a mask, and mask was not explicitly pass + # to __call__, hence we set previous_mask as the default value. + kwargs['mask'] = previous_mask - @doc_controls.for_subclass_implementers - def add_variable(self, *args, **kwargs): - """Alias for `add_weight`.""" - return self.add_weight(*args, **kwargs) + input_shapes = None - @doc_controls.for_subclass_implementers - def add_weight(self, - name, - shape, - dtype=None, - initializer=None, - regularizer=None, - trainable=None, - constraint=None, - partitioner=None, - use_resource=None, - synchronization=tf_variables.VariableSynchronization.AUTO, - aggregation=tf_variables.VariableAggregation.NONE, - **kwargs): - """Adds a new variable to the layer, or gets an existing one; returns it. - - Arguments: - name: variable name. - shape: variable shape. - dtype: The type of the variable. Defaults to `self.dtype` or `float32`. - initializer: initializer instance (callable). - regularizer: regularizer instance (callable). - trainable: whether the variable should be part of the layer's - "trainable_variables" (e.g. variables, biases) - or "non_trainable_variables" (e.g. BatchNorm mean, stddev). - Note, if the current variable scope is marked as non-trainable - then this parameter is ignored and any added variables are also - marked as non-trainable. `trainable` defaults to `True` unless - `synchronization` is set to `ON_READ`. - constraint: constraint instance (callable). - partitioner: Partitioner to be passed to the `Checkpointable` API. - use_resource: Whether to use `ResourceVariable`. - synchronization: Indicates when a distributed a variable will be - aggregated. Accepted values are constants defined in the class - `tf.VariableSynchronization`. By default the synchronization is set to - `AUTO` and the current `DistributionStrategy` chooses - when to synchronize. If `synchronization` is set to `ON_READ`, - `trainable` must not be set to `True`. - aggregation: Indicates how a distributed variable will be aggregated. - Accepted values are constants defined in the class - `tf.VariableAggregation`. - **kwargs: Additional keyword arguments. Accepted values are `getter` and - `collections`. - - Returns: - The created variable. Usually either a `Variable` or `ResourceVariable` - instance. If `partitioner` is not `None`, a `PartitionedVariable` - instance is returned. - - Raises: - RuntimeError: If called with partioned variable regularization and - eager execution is enabled. - ValueError: When giving unsupported dtype and no initializer or when - trainable has been set to True with synchronization set as `ON_READ`. - """ - # Validate optional keyword arguments. - for kwarg in kwargs: - if kwarg not in ['getter', 'collections']: - raise TypeError('Unknown keyword argument:', kwarg) - getter = kwargs.pop('getter', None) - collections = kwargs.pop('collections', None) - - if dtype is None: - dtype = self.dtype or backend.floatx() - dtype = dtypes.as_dtype(dtype) - initializer = initializers.get(initializer) - regularizer = regularizers.get(regularizer) - constraint = constraints.get(constraint) - - if synchronization == tf_variables.VariableSynchronization.ON_READ: - if trainable: - raise ValueError( - 'Synchronization value can be set to ' - 'VariableSynchronization.ON_READ only for non-trainable variables. ' - 'You have specified trainable=True and ' - 'synchronization=VariableSynchronization.ON_READ.') - else: - # Set trainable to be false when variable is to be synced on read. - trainable = False - elif trainable is None: - trainable = True - - # Initialize variable when no initializer provided - if initializer is None: - # If dtype is DT_FLOAT, provide a uniform unit scaling initializer - if dtype.is_floating: - initializer = initializers.glorot_uniform() - # If dtype is DT_INT/DT_UINT, provide a default value `zero` - # If dtype is DT_BOOL, provide a default value `FALSE` - elif dtype.is_integer or dtype.is_unsigned or dtype.is_bool: - initializer = initializers.zeros() - # NOTES:Do we need to support for handling DT_STRING and DT_COMPLEX here? - else: - raise ValueError('An initializer for variable %s of type %s is required' - ' for layer %s' % (name, dtype.base_dtype, self.name)) - - variable = self._add_variable_with_custom_getter( - name=name, - shape=shape, - # TODO(allenl): a `make_variable` equivalent should be added as a - # `Checkpointable` method. - getter=getter or make_variable, - # Manage errors in Layer rather than Checkpointable. - overwrite=True, - initializer=initializer, - dtype=dtype, - constraint=constraint, - trainable=trainable and self.trainable, - partitioner=partitioner, - use_resource=use_resource, - collections=collections, - synchronization=synchronization, - aggregation=aggregation) - backend.track_variable(variable) - - if regularizer is not None: - # TODO(fchollet): in the future, this should be handled at the - # level of variable creation, and weight regularization losses - # should be variable attributes. - self._handle_weight_regularization(name, variable, regularizer) - - if trainable: - self._trainable_weights.append(variable) - else: - self._non_trainable_weights.append(variable) - return variable - - def _handle_weight_regularization(self, name, variable, regularizer): - """Create lambdas which compute regularization losses.""" - - def _loss_for_variable(v): - """Creates a regularization loss `Tensor` for variable `v`.""" - with ops.colocate_with(v): - with ops.name_scope(name + '/Regularizer'): - regularization = regularizer(v) - return regularization - - if isinstance(variable, tf_variables.PartitionedVariable): - for v in variable: - self.add_loss(functools.partial(_loss_for_variable, v)) - else: - self.add_loss(functools.partial(_loss_for_variable, variable)) - - def _handle_activity_regularization(self, inputs, outputs): - # Apply activity regularization. - # Note that it should be applied every time the layer creates a new - # output, since it is output-specific. - if self._activity_regularizer: - output_list = nest.flatten(outputs) - with ops.name_scope('ActivityRegularizer'): - for output in output_list: - activity_loss = self._activity_regularizer(output) - batch_size = math_ops.cast( - array_ops.shape(output)[0], activity_loss.dtype) - # Make activity regularization strength batch-agnostic. - mean_activity_loss = activity_loss / batch_size - self.add_loss(mean_activity_loss, inputs=inputs) - - @doc_controls.for_subclass_implementers - def call(self, inputs, **kwargs): # pylint: disable=unused-argument - """This is where the layer's logic lives. - - Arguments: - inputs: Input tensor, or list/tuple of input tensors. - **kwargs: Additional keyword arguments. - - Returns: - A tensor or list/tuple of tensors. - """ - return inputs - - def __call__(self, inputs, *args, **kwargs): - """Wraps `call`, applying pre- and post-processing steps. - - Arguments: - inputs: input tensor(s). - *args: additional positional arguments to be passed to `self.call`. - **kwargs: additional keyword arguments to be passed to `self.call`. - - Returns: - Output tensor(s). - - Note: - - The following optional keyword arguments are reserved for specific uses: - * `training`: Boolean scalar tensor of Python boolean indicating - whether the `call` is meant for training or inference. - * `mask`: Boolean input mask. - - If the layer's `call` method takes a `mask` argument (as some Keras - layers do), its default value will be set to the mask generated - for `inputs` by the previous layer (if `input` did come from - a layer that generated a corresponding mask, i.e. if it came from - a Keras layer with masking support. - - Raises: - ValueError: if the layer's `call` method returns None (an invalid value). - """ - input_list = nest.flatten(inputs) - - if context.executing_eagerly(): - # Accept NumPy inputs by converting to Tensors when executing eagerly. - if all([isinstance(x, (np.ndarray, float, int)) for x in input_list]): - inputs = nest.map_structure(ops.convert_to_tensor, inputs) - input_list = nest.flatten(inputs) - - # We will attempt to build a TF graph if & only if all inputs are symbolic. - # This is always the case in graph mode. It can also be the case in eager - # mode when all inputs can be traced back to `keras.Input()` (when building - # models using the functional API). - build_graph = tf_utils.are_all_symbolic_tensors(input_list) - executing_eagerly = context.executing_eagerly() - - # Handle Keras mask propagation from previous layer to current layer. - previous_mask = None - if build_graph and (not hasattr(self, '_compute_previous_mask') or - self._compute_previous_mask): - previous_mask = collect_previous_mask(inputs) - if not hasattr(self, '_call_fn_args'): - self._call_fn_args = self._no_dependency( - function_utils.fn_args(self.call)) - if ('mask' in self._call_fn_args and 'mask' not in kwargs and - not generic_utils.is_all_none(previous_mask)): - # The previous layer generated a mask, and mask was not explicitly pass - # to __call__, hence we set previous_mask as the default value. - kwargs['mask'] = previous_mask - - input_shapes = None - - with ops.name_scope(self._name_scope()): - if not self.built: - # Check input assumptions set before layer building, e.g. input rank. - self._assert_input_compatibility(inputs) - if input_list and self._dtype is None: - try: - self._dtype = input_list[0].dtype.base_dtype.name - except AttributeError: - pass - - if all(hasattr(x, 'shape') for x in input_list): - input_shapes = nest.map_structure(lambda x: x.shape, inputs) - - if (not hasattr(self, '_is_graph_network') or - self.__class__.__name__ == 'Sequential' or - not hasattr(self.build, '_is_default')): - # Only if self is a layer, an instance of a sequential model, or - # the user has manually overwritten the build method do we need to - # build it. - self.build(input_shapes) - # We must set self.built since user defined build functions are not - # constrained to set self.built. - self.built = True + with ops.name_scope(self._name_scope()): + if not self.built: + # Build layer if applicable (if the `build` method has been overridden). + self._maybe_build(inputs) + # We must set self.built since user defined build functions are not + # constrained to set self.built. + self.built = True # Check input assumptions set after layer building, e.g. input shape. if build_graph: # Symbolic execution on symbolic tensors. We will attempt to build # the corresponding TF subgraph inside `backend.get_graph()` - self._assert_input_compatibility(inputs) + input_spec.assert_input_compatibility( + self.input_spec, inputs, self.name) graph = backend.get_graph() with graph.as_default(): if not executing_eagerly: @@ -772,10 +554,10 @@ class Layer(checkpointable.CheckpointableBase): # Any issue during graph-building means we will later run the # model in eager mode, whether the issue was related to # graph mode or not. This provides a nice debugging experience. - self._is_static_graph_friendly = False + self._call_is_graph_friendly = False # We will use static shape inference to return symbolic tensors # matching the specifications of the layer outputs. - # Since we have set `self._is_static_graph_friendly = False`, + # Since we have set `self._call_is_graph_friendly = False`, # we will never attempt to run the underlying TF graph (which is # disconnected). # TODO(fchollet): consider py_func as an alternative, which @@ -792,7 +574,7 @@ class Layer(checkpointable.CheckpointableBase): '(layer: ' + self.name + ').') self._handle_activity_regularization(inputs, outputs) self._set_mask_metadata(inputs, outputs, previous_mask) - if have_all_keras_metadata(inputs): + if base_layer_utils.have_all_keras_metadata(inputs): inputs, outputs = self._set_connectivity_metadata_( inputs, outputs, args, kwargs) if hasattr(self, '_set_inputs') and not self.inputs: @@ -815,313 +597,299 @@ class Layer(checkpointable.CheckpointableBase): del self._initial_weights return outputs - def apply(self, inputs, *args, **kwargs): - """Apply the layer on a input. + @property + def dtype(self): + return self._dtype - This simply wraps `self.__call__`. + @property + def name(self): + return self._name - Arguments: - inputs: Input tensor(s). - *args: additional positional arguments to be passed to `self.call`. - **kwargs: additional keyword arguments to be passed to `self.call`. + @property + def activity_regularizer(self): + """Optional regularizer function for the output of this layer.""" + return self._activity_regularizer + + @activity_regularizer.setter + def activity_regularizer(self, regularizer): + """Optional regularizer function for the output of this layer.""" + self._activity_regularizer = self._no_dependency(regularizer) + + @property + def trainable_weights(self): + return self._trainable_weights if self.trainable else [] + + @property + def non_trainable_weights(self): + if self.trainable: + return self._non_trainable_weights + else: + return self._trainable_weights + self._non_trainable_weights + + @property + def weights(self): + """Returns the list of all layer variables/weights. Returns: - Output tensor(s). + A list of variables. """ - return self.__call__(inputs, *args, **kwargs) + return self.trainable_weights + self.non_trainable_weights - def _set_mask_metadata(self, inputs, outputs, previous_mask): - # In some cases the mask of the outputs has already been computed by - # inner layers and does not need to be recomputed by this layer. - mask_already_computed = all( - hasattr(x, '_keras_mask') for x in generic_utils.to_list(outputs)) - if hasattr(self, 'compute_mask') and not mask_already_computed: - output_mask = self.compute_mask(inputs, previous_mask) - else: - output_mask = None - if isinstance(outputs, (list, tuple)): - if output_mask is None: - output_mask = [None for _ in range(len(outputs))] - for x, m in zip(outputs, output_mask): - try: - x._keras_mask = m # pylint: disable=protected-access - except AttributeError: - pass # C type such as dict. Masking not supported in this case. - else: - try: - outputs._keras_mask = output_mask # pylint: disable=protected-access - except AttributeError: - pass # C type such as dict. Masking not supported in this case. + @property + def updates(self): + if not self.trainable and not self.stateful: + return [] + return self._updates - def _set_connectivity_metadata_(self, inputs, outputs, args, kwargs): - call_convention = getattr(self, '_call_convention', - CallConvention.EXPLICIT_INPUTS_ARGUMENT) - if args: - if call_convention == CallConvention.EXPLICIT_INPUTS_ARGUMENT: - raise TypeError( - 'This layer ("{}") takes an `inputs` argument in `call()`, ' - 'and only the `inputs` argument may be specified as a positional ' - 'argument. Pass everything else as a keyword argument ' - '(those arguments will not be tracked ' - 'as inputs to the layer).'.format(self.name)) - elif call_convention == CallConvention.SINGLE_POSITIONAL_ARGUMENT: - raise TypeError( - 'This layer ("{}") takes a single positional argument in `call()`,' - ' which is by convention the `inputs` argument, ' - 'and only this argument may be specified as a positional argument. ' - 'Pass everything else as a keyword argument ' - '(those arguments will not be tracked ' - 'as inputs to the layer).'.format(self.name)) + @property + def losses(self): + """Losses which are associated with this `Layer`. - # If the layer returns tensors from its inputs, unmodified, - # we copy them to avoid loss of tensor metadata. - output_ls = nest.flatten(outputs) - output_ls_copy = [] - for x in output_ls: - if x in nest.flatten(inputs): - with ops.name_scope(self.name): - x = array_ops.identity(x) - output_ls_copy.append(x) - if len(output_ls_copy) == 1: - outputs = output_ls_copy[0] + Variable regularization tensors are created when this property is accessed, + so it is eager safe: accessing `losses` under a `tf.GradientTape` will + propagate gradients back to the corresponding variables. + + Returns: + A list of tensors. + """ + collected_losses = [] + if context.executing_eagerly(): + collected_losses.extend(self._eager_losses) else: - outputs = output_ls_copy + collected_losses.extend(self._losses) + for regularizer in self._callable_losses: + loss_tensor = regularizer() + if loss_tensor is not None: + collected_losses.append(loss_tensor) + return collected_losses - inputs, kwargs = self._inputs_from_call_args( - call_args=(inputs,) + args, call_kwargs=kwargs) - # Add an inbound node to the layer, so it can keep track of this call. - # This updates the layer history of the output tensor(s). - kwargs.pop('mask', None) # `mask` should not be serialized. - self._add_inbound_node( - input_tensors=inputs, output_tensors=outputs, arguments=kwargs) - return inputs, outputs + @doc_controls.for_subclass_implementers + def add_loss(self, losses, inputs=None): + """Add loss tensor(s), potentially dependent on layer inputs. - def _inputs_from_call_args(self, call_args, call_kwargs): - """Get Layer inputs from __call__ *args and **kwargs. + Some losses (for instance, activity regularization losses) may be dependent + on the inputs passed when calling a layer. Hence, when reusing the same + layer on different inputs `a` and `b`, some entries in `layer.losses` may + be dependent on `a` and some on `b`. This method automatically keeps track + of dependencies. - Args: - call_args: The positional arguments passed to __call__. - call_kwargs: The keyword argument dict passed to __call__. + The `get_losses_for` method allows to retrieve the losses relevant to a + specific set of inputs. - Returns: - A tuple of (inputs, non_input_kwargs). These may be the same objects as - were passed in (call_args and call_kwargs). + Note that `add_loss` is not supported when executing eagerly. Instead, + variable regularizers may be added through `add_variable`. Activity + regularization is not supported directly (but such losses may be returned + from `Layer.call()`). + + Arguments: + losses: Loss tensor, or list/tuple of tensors. Rather than tensors, losses + may also be zero-argument callables which create a loss tensor. + inputs: Ignored when executing eagerly. If anything other than None is + passed, it signals the losses are conditional on some of the layer's + inputs, and thus they should only be run where these inputs are + available. This is the case for activity regularization losses, for + instance. If `None` is passed, the losses are assumed + to be unconditional, and will apply across all dataflows of the layer + (e.g. weight regularization losses). """ - call_convention = getattr(self, '_call_convention', - CallConvention.EXPLICIT_INPUTS_ARGUMENT) - if (call_convention in ( - CallConvention.EXPLICIT_INPUTS_ARGUMENT, - CallConvention.SINGLE_POSITIONAL_ARGUMENT)): - assert len(call_args) == 1 # TypeError raised earlier in __call__. - return call_args[0], call_kwargs - else: - call_arg_spec = tf_inspect.getfullargspec(self.call) - # There is no explicit "inputs" argument expected or provided to - # call(). Arguments which have default values are considered non-inputs, - # and arguments without are considered inputs. - if call_arg_spec.defaults: - if call_arg_spec.varargs is not None: - raise TypeError( - 'Layers may not accept both positional arguments and ' - 'arguments with default values (unable to determine which ' - 'are inputs to the layer). ' - 'Issue occurred with layer "%s"' % (self.name)) - keyword_arg_names = set( - call_arg_spec.args[-len(call_arg_spec.defaults):]) + losses = generic_utils.to_list(losses) + + def _tag_unconditional(loss): + if callable(loss): + loss = loss() + if loss is None: + return None # Will be filtered out when computing the .losses property + if not tensor_util.is_tensor(loss): + loss = ops.convert_to_tensor(loss, dtype=backend.floatx()) + loss._unconditional_loss = (inputs is None) # pylint: disable=protected-access + return loss + + for loss in losses: + if callable(loss): + self._callable_losses.append( + functools.partial(_tag_unconditional, loss)) else: - keyword_arg_names = set() - # Training is never an input argument name, to allow signatures like - # call(x, training). - keyword_arg_names.add('training') - _, unwrapped_call = tf_decorator.unwrap(self.call) - bound_args = inspect.getcallargs( - unwrapped_call, *call_args, **call_kwargs) - if call_arg_spec.varkw is not None: - var_kwargs = bound_args.pop(call_arg_spec.varkw) - bound_args.update(var_kwargs) - keyword_arg_names = keyword_arg_names.union(var_kwargs.keys()) - all_args = call_arg_spec.args - if all_args and bound_args[all_args[0]] is self: - # Ignore the 'self' argument of methods - bound_args.pop(call_arg_spec.args[0]) - all_args = all_args[1:] - non_input_arg_values = {} - input_arg_values = [] - remaining_args_are_keyword = False - for argument_name in all_args: - if argument_name in keyword_arg_names: - remaining_args_are_keyword = True - else: - if remaining_args_are_keyword: - raise TypeError( - 'Found a positional argument in a layer call after a non-input ' - 'argument. All arguments after "training" must be keyword ' - 'arguments, and are not tracked as inputs to the layer. ' - 'Issue occurred with layer "%s"' % (self.name)) - if remaining_args_are_keyword: - non_input_arg_values[argument_name] = bound_args[argument_name] + if context.executing_eagerly(): + self._eager_losses.append(_tag_unconditional(loss)) else: - input_arg_values.append(bound_args[argument_name]) - if call_arg_spec.varargs is not None: - input_arg_values.extend(bound_args[call_arg_spec.varargs]) - return input_arg_values, non_input_arg_values + self._losses.append(_tag_unconditional(loss)) - def compute_output_shape(self, input_shape): - """Computes the output shape of the layer. + @doc_controls.for_subclass_implementers + def add_metric(self, value, aggregation=None, name=None): + """Adds metric tensor to the layer. - Assumes that the layer will be built - to match that input shape provided. + Args: + value: Metric tensor. + aggregation: Sample-wise metric reduction function. If `aggregation=None`, + it indicates that the metric tensor provided has been aggregated + already. eg, `model.add_metric(BinaryAccuracy(name='acc')(y_true, + y_pred))`. If aggregation='mean', the given metric tensor will be + sample-wise reduced using `mean` function. eg, `model.add_metric( + tf.reduce_mean(outputs), name='output_mean', aggregation='mean')`. + name: String metric name. - Arguments: - input_shape: Shape tuple (tuple of integers) - or list of shape tuples (one per output tensor of the layer). - Shape tuples can include None for free dimensions, - instead of an integer. + Raises: + ValueError: If `aggregation` is anything other than None or `mean`. + """ + if aggregation is not None and aggregation != 'mean': + raise ValueError( + 'We currently support only `mean` sample-wise metric aggregation. ' + 'You provided aggregation=`%s`' % aggregation) - Returns: - An input shape tuple. + if tf_utils.is_symbolic_tensor(value): + self._symbolic_add_metric(value, aggregation, name) + else: + self._eager_add_metric(value, aggregation, name) + + @doc_controls.for_subclass_implementers + def add_update(self, updates, inputs=None): + """Add update op(s), potentially dependent on layer inputs. + + Weight updates (for instance, the updates of the moving mean and variance + in a BatchNormalization layer) may be dependent on the inputs passed + when calling a layer. Hence, when reusing the same layer on + different inputs `a` and `b`, some entries in `layer.updates` may be + dependent on `a` and some on `b`. This method automatically keeps track + of dependencies. + + The `get_updates_for` method allows to retrieve the updates relevant to a + specific set of inputs. + + This call is ignored when eager execution is enabled (in that case, variable + updates are run on the fly and thus do not need to be tracked for later + execution). + + Arguments: + updates: Update op, or list/tuple of update ops. + inputs: If anything other than None is passed, it signals the updates + are conditional on some of the layer's inputs, + and thus they should only be run where these inputs are available. + This is the case for BatchNormalization updates, for instance. + If None, the updates will be taken into account unconditionally, + and you are responsible for making sure that any dependency they might + have is available at runtime. + A step counter might fall into this category. """ if context.executing_eagerly(): - # In this case we build the model first in order to do shape inference. - # This is acceptable because the framework only calls - # `compute_output_shape` on shape values that the layer would later be - # built for. It would however cause issues in case a user attempts to - # use `compute_output_shape` manually (these users will have to - # implement `compute_output_shape` themselves). - self.build(input_shape) - - with context.graph_mode(): - graph = func_graph.FuncGraph('graph') - with graph.as_default(): - if isinstance(input_shape, list): - inputs = [generate_placeholders_from_shape(shape) - for shape in input_shape] - else: - inputs = generate_placeholders_from_shape(input_shape) + return # Updates already applied when in eager mode. - try: - if self._expects_training_arg: - outputs = self(inputs, training=False) - else: - outputs = self(inputs) - except TypeError: - raise NotImplementedError('We could not automatically infer ' - 'the static shape of the layer\'s output.' - ' Please implement the ' - '`compute_output_shape` method on your ' - 'layer (%s).' % self.__class__.__name__) - if isinstance(outputs, list): - return [output.shape for output in outputs] + def process_update(x): + if isinstance(x, ops.Operation): + return x + elif hasattr(x, 'op'): + return x.op else: - return outputs.shape - raise NotImplementedError + return ops.convert_to_tensor(x) - def compute_mask(self, inputs, mask=None): # pylint: disable=unused-argument - """Computes an output mask tensor. + updates = generic_utils.to_list(updates) + updates = [process_update(x) for x in updates] + self._updates += updates + if inputs is None: + for u in updates: + u._unconditional_update = True # pylint: disable=protected-access + else: + for u in updates: + u._unconditional_update = False # pylint: disable=protected-access + + def set_weights(self, weights): + """Sets the weights of the layer, from Numpy arrays. Arguments: - inputs: Tensor or list of tensors. - mask: Tensor or list of tensors. + weights: a list of Numpy arrays. The number + of arrays and their shape must match + number of the dimensions of the weights + of the layer (i.e. it should match the + output of `get_weights`). + + Raises: + ValueError: If the provided weights list does not match the + layer's specifications. + """ + params = self.weights + if len(params) != len(weights): + raise ValueError('You called `set_weights(weights)` on layer "' + + self.name + '" with a weight list of length ' + + str(len(weights)) + ', but the layer was expecting ' + + str(len(params)) + ' weights. Provided weights: ' + + str(weights)[:50] + '...') + if not params: + return + weight_value_tuples = [] + param_values = backend.batch_get_value(params) + for pv, p, w in zip(param_values, params, weights): + if pv.shape != w.shape: + raise ValueError('Layer weight shape ' + str(pv.shape) + + ' not compatible with ' + 'provided weight shape ' + str(w.shape)) + weight_value_tuples.append((p, w)) + backend.batch_set_value(weight_value_tuples) + + def get_weights(self): + """Returns the current weights of the layer. Returns: - None or a tensor (or list of tensors, - one per output tensor of the layer). + Weights values as a list of numpy arrays. """ - if not self.supports_masking: - if mask is not None: - if isinstance(mask, list): - if any(m is not None for m in mask): - raise TypeError('Layer ' + self.name + ' does not support masking, ' - 'but was passed an input_mask: ' + str(mask)) - else: - raise TypeError('Layer ' + self.name + ' does not support masking, ' - 'but was passed an input_mask: ' + str(mask)) - # masking not explicitly supported: return None as mask - return None - # if masking is explicitly supported, by default - # carry over the input mask - return mask + params = self.weights + return backend.batch_get_value(params) - def _add_inbound_node(self, - input_tensors, - output_tensors, - arguments=None): - """Internal method to create an inbound node for the layer. + def get_updates_for(self, inputs): + """Retrieves updates relevant to a specific set of inputs. Arguments: - input_tensors: list of input tensors. - output_tensors: list of output tensors. - arguments: dictionary of keyword arguments that were passed to the - `call` method of the layer at the call that created the node. - """ - input_tensors = nest.flatten(input_tensors) - output_tensors = nest.flatten(output_tensors) + inputs: Input tensor or list/tuple of input tensors. - # Collect input tensor(s) coordinates. - inbound_layers = [] - node_indices = [] - tensor_indices = [] - for x in input_tensors: - assert hasattr(x, '_keras_history') - inbound_layer, node_index, tensor_index = x._keras_history # pylint: disable=protected-access - inbound_layers.append(inbound_layer) - node_indices.append(node_index) - tensor_indices.append(tensor_index) + Returns: + List of update ops of the layer that depend on `inputs`. - # Create node, add it to inbound nodes. - Node( - self, - inbound_layers=inbound_layers, - node_indices=node_indices, - tensor_indices=tensor_indices, - input_tensors=input_tensors, - output_tensors=output_tensors, - arguments=arguments) + Raises: + RuntimeError: If called in Eager mode. + """ + # Updates disabled if layer is not trainable and not explicitly stateful. + if not self.trainable and not self.stateful: + return [] - # Update tensor history metadata. - for i in range(len(output_tensors)): - # The metadata attribute consists of 1) a layer instance - # 2) a node index for the layer, 3) a tensor index for the node. - # The allows layer reuse (multiple nodes per layer) and multi-output - # or multi-input layers (e.g. a layer can return multiple tensors, - # and each can be sent to a different layer). - output_tensors[i]._keras_history = (self, len(self._inbound_nodes) - 1, i) # pylint: disable=protected-access + if inputs is None: + # Requesting unconditional updates. + return [x for x in self.updates if x._unconditional_update] # pylint: disable=protected-access - def _get_node_attribute_at_index(self, node_index, attr, attr_name): - """Private utility to retrieves an attribute (e.g. inputs) from a node. + # Requesting input-conditional updates. + inputs = nest.flatten(inputs) + reachable = tf_utils.get_reachable_from_inputs(inputs, self.updates) + updates = [] + for update in self.updates: + if update in reachable: + updates.append(update) + return updates - This is used to implement the methods: - - get_input_shape_at - - get_output_shape_at - - get_input_at - etc... + def get_losses_for(self, inputs): + """Retrieves losses relevant to a specific set of inputs. Arguments: - node_index: Integer index of the node from which - to retrieve the attribute. - attr: Exact node attribute name. - attr_name: Human-readable attribute name, for error messages. + inputs: Input tensor or list/tuple of input tensors. Returns: - The layer's attribute `attr` at the node of index `node_index`. + List of loss tensors of the layer that depend on `inputs`. Raises: - RuntimeError: If the layer has no inbound nodes, or if called in Eager - mode. - ValueError: If the index provided does not match any node. + RuntimeError: If called in Eager mode. """ - if not self._inbound_nodes: - raise RuntimeError('The layer has never been called ' - 'and thus has no defined ' + attr_name + '.') - if not len(self._inbound_nodes) > node_index: - raise ValueError('Asked to get ' + attr_name + ' at node ' + - str(node_index) + ', but the layer has only ' + - str(len(self._inbound_nodes)) + ' inbound nodes.') - values = getattr(self._inbound_nodes[node_index], attr) - if len(values) == 1: - return values[0] - else: - return values + if inputs is None: + # Requesting unconditional losses. + return [x for x in self.losses if x._unconditional_loss] # pylint: disable=protected-access + + # Requesting input-conditional losses. + inputs = nest.flatten(inputs) + # Retrieve the set of tensors in the TF graph that depend on `inputs`. + # The losses we want to return will be part of this set. + # To avoid unnecessary work, we stop the search in case all of + # `self.losses` have been retrieved. + reachable = tf_utils.get_reachable_from_inputs(inputs, self.losses) + losses = [] + for loss in self.losses: + if loss in reachable: + losses.append(loss) + return losses def get_input_mask_at(self, node_index): """Retrieves the input mask tensor(s) of a layer at a given node. @@ -1376,8 +1144,7 @@ class Layer(checkpointable.CheckpointableBase): ', but the layer isn\'t built. ' 'You can build it manually via: `' + self.name + '.build(batch_input_shape)`.') - weight_shapes = [w.shape.as_list() for w in self.weights] - return int(sum([np.prod(w) for w in weight_shapes])) + return int(sum(np.prod(w.shape.as_list()) for w in self.weights)) @property def output_shape(self): @@ -1429,231 +1196,401 @@ class Layer(checkpointable.CheckpointableBase): """Deprecated, do NOT use! Only for compatibility with external Keras.""" return self._outbound_nodes - def _assert_input_compatibility(self, inputs): - """Checks compatibility between the layer and provided inputs. + ############################################################################## + # Methods & attributes below are public aliases of other methods. # + ############################################################################## + + def apply(self, inputs, *args, **kwargs): + """Apply the layer on a input. + + This is an alias of `self.__call__`. + + Arguments: + inputs: Input tensor(s). + *args: additional positional arguments to be passed to `self.call`. + **kwargs: additional keyword arguments to be passed to `self.call`. + + Returns: + Output tensor(s). + """ + return self.__call__(inputs, *args, **kwargs) + + @doc_controls.for_subclass_implementers + def add_variable(self, *args, **kwargs): + """Alias for `add_weight`.""" + return self.add_weight(*args, **kwargs) + + @property + def variables(self): + """Returns the list of all layer variables/weights. + + Alias of `self.weights`. + + Returns: + A list of variables. + """ + return self.weights + + @property + def trainable_variables(self): + return self.trainable_weights + + @property + def non_trainable_variables(self): + return self.non_trainable_weights + + ############################################################################## + # Methods & attributes below are all private and only used by the framework. # + ############################################################################## + + def _name_scope(self): + return self.name + + def _init_set_name(self, name, zero_based=True): + if not name: + self._name = base_layer_utils.unique_layer_name( + generic_utils.to_snake_case(self.__class__.__name__), + zero_based=zero_based) + else: + self._name = name + + def _get_existing_metric(self, name=None): + match = [m for m in self._metrics if m.name == name] + if not match: + return + if len(match) > 1: + raise ValueError( + 'Please provide different names for the metrics you have added. ' + 'We found {} metrics with the name: "{}"'.format(len(match), name)) + return match[0] + + def _eager_add_metric(self, value, aggregation=None, name=None): + # If the given metric is available in `metrics` list we just update state + # on it, otherwise we create a new metric instance and + # add it to the `metrics` list. + match = self._get_existing_metric(name) + if match: + match(value) # Update the metric state. + return + else: + if aggregation is None: + raise ValueError('We do not support adding an aggregated metric tensor ' + 'in `call` in eager execution.') + metric_obj, _ = base_layer_utils.create_mean_metric(value, name) + self._metrics.append(metric_obj) + + def _symbolic_add_metric(self, value, aggregation=None, name=None): + if aggregation is None: + # Iterate over the metrics and check if the given metric exists already. + # This can happen when a metric instance is created in subclassed model + # layer `__init__` and we have tracked that instance already in + # model.__setattr__. + match = self._get_existing_metric(name) + if match: + result_tensor = value + if match.name not in self._metrics_tensors: + self._metrics_tensors[match.name] = result_tensor + return + else: + raise ValueError( + 'We currently do not support reusing a metric instance.') + else: + # We track the instance using the metadata on the result tensor. + result_tensor = value + metric_obj = result_tensor._metric_obj + else: + # If a non-aggregated tensor is given as input (ie. `aggregation` is + # explicitly set to `mean`), we wrap the tensor in `Mean` metric. + metric_obj, result_tensor = base_layer_utils.create_mean_metric( + value, name) + self._metrics.append(metric_obj) + self._metrics_tensors[metric_obj.name] = result_tensor + + def _handle_weight_regularization(self, name, variable, regularizer): + """Create lambdas which compute regularization losses.""" + + def _loss_for_variable(v): + """Creates a regularization loss `Tensor` for variable `v`.""" + with ops.colocate_with(v): + with ops.name_scope(name + '/Regularizer'): + regularization = regularizer(v) + return regularization + + if isinstance(variable, tf_variables.PartitionedVariable): + for v in variable: + self.add_loss(functools.partial(_loss_for_variable, v)) + else: + self.add_loss(functools.partial(_loss_for_variable, variable)) + + def _handle_activity_regularization(self, inputs, outputs): + # Apply activity regularization. + # Note that it should be applied every time the layer creates a new + # output, since it is output-specific. + if self._activity_regularizer: + output_list = nest.flatten(outputs) + with ops.name_scope('ActivityRegularizer'): + for output in output_list: + activity_loss = self._activity_regularizer(output) + batch_size = math_ops.cast( + array_ops.shape(output)[0], activity_loss.dtype) + # Make activity regularization strength batch-agnostic. + mean_activity_loss = activity_loss / batch_size + self.add_loss(mean_activity_loss, inputs=inputs) + + def _set_mask_metadata(self, inputs, outputs, previous_mask): + # In some cases the mask of the outputs has already been computed by + # inner layers and does not need to be recomputed by this layer. + mask_already_computed = all( + hasattr(x, '_keras_mask') for x in generic_utils.to_list(outputs)) + if hasattr(self, 'compute_mask') and not mask_already_computed: + output_mask = self.compute_mask(inputs, previous_mask) + else: + output_mask = None + if isinstance(outputs, (list, tuple)): + if output_mask is None: + output_mask = [None for _ in range(len(outputs))] + for x, m in zip(outputs, output_mask): + try: + x._keras_mask = m # pylint: disable=protected-access + except AttributeError: + pass # C type such as dict. Masking not supported in this case. + else: + try: + outputs._keras_mask = output_mask # pylint: disable=protected-access + except AttributeError: + pass # C type such as dict. Masking not supported in this case. + + def _set_connectivity_metadata_(self, inputs, outputs, args, kwargs): + call_convention = getattr( + self, '_call_convention', + base_layer_utils.CallConvention.EXPLICIT_INPUTS_ARGUMENT) + if args: + if call_convention == (base_layer_utils + .CallConvention.EXPLICIT_INPUTS_ARGUMENT): + raise TypeError( + 'This layer ("{}") takes an `inputs` argument in `call()`, ' + 'and only the `inputs` argument may be specified as a positional ' + 'argument. Pass everything else as a keyword argument ' + '(those arguments will not be tracked ' + 'as inputs to the layer).'.format(self.name)) + elif call_convention == (base_layer_utils + .CallConvention.SINGLE_POSITIONAL_ARGUMENT): + raise TypeError( + 'This layer ("{}") takes a single positional argument in `call()`,' + ' which is by convention the `inputs` argument, ' + 'and only this argument may be specified as a positional argument. ' + 'Pass everything else as a keyword argument ' + '(those arguments will not be tracked ' + 'as inputs to the layer).'.format(self.name)) + + # If the layer returns tensors from its inputs, unmodified, + # we copy them to avoid loss of tensor metadata. + output_ls = nest.flatten(outputs) + output_ls_copy = [] + for x in output_ls: + if x in nest.flatten(inputs): + with ops.name_scope(self.name): + x = array_ops.identity(x) + output_ls_copy.append(x) + if len(output_ls_copy) == 1: + outputs = output_ls_copy[0] + else: + outputs = output_ls_copy + + inputs, kwargs = self._inputs_from_call_args( + call_args=(inputs,) + args, call_kwargs=kwargs) + # Add an inbound node to the layer, so it can keep track of this call. + # This updates the layer history of the output tensor(s). + kwargs.pop('mask', None) # `mask` should not be serialized. + self._add_inbound_node( + input_tensors=inputs, output_tensors=outputs, arguments=kwargs) + return inputs, outputs - This checks that the tensor(s) `inputs` verify the input assumptions - of the layer (if any). If not, a clear and actional exception gets raised. + def _inputs_from_call_args(self, call_args, call_kwargs): + """Get Layer inputs from __call__ *args and **kwargs. - Arguments: - inputs: input tensor or list of input tensors. + Args: + call_args: The positional arguments passed to __call__. + call_kwargs: The keyword argument dict passed to __call__. - Raises: - ValueError: in case of mismatch between - the provided inputs and the expectations of the layer. + Returns: + A tuple of (inputs, non_input_kwargs). These may be the same objects as + were passed in (call_args and call_kwargs). """ - if not self.input_spec: - return - if not isinstance(self.input_spec, (list, tuple)): - input_spec = nest.flatten(self.input_spec) + call_convention = getattr( + self, '_call_convention', + base_layer_utils.CallConvention.EXPLICIT_INPUTS_ARGUMENT) + if (call_convention in ( + base_layer_utils.CallConvention.EXPLICIT_INPUTS_ARGUMENT, + base_layer_utils.CallConvention.SINGLE_POSITIONAL_ARGUMENT)): + assert len(call_args) == 1 # TypeError raised earlier in __call__. + return call_args[0], call_kwargs else: - input_spec = self.input_spec - inputs = nest.flatten(inputs) - if len(inputs) != len(input_spec): - raise ValueError('Layer ' + self.name + ' expects ' + - str(len(input_spec)) + ' inputs, ' - 'but it received ' + str(len(inputs)) + - ' input tensors. Inputs received: ' + str(inputs)) - for input_index, (x, spec) in enumerate(zip(inputs, input_spec)): - if spec is None: - continue - - if (spec.ndim is not None or - spec.min_ndim is not None or - spec.max_ndim is not None): - if x.shape.ndims is None: - raise ValueError('Input ' + str(input_index) + ' of layer ' + - self.name + ' is incompatible with the layer: ' - 'its rank is undefined, but the layer requires a ' - 'defined rank.') - - # Check ndim. - if spec.ndim is not None: - ndim = x.shape.ndims - if ndim != spec.ndim: - raise ValueError('Input ' + str(input_index) + ' of layer ' + - self.name + ' is incompatible with the layer: ' - 'expected ndim=' + str(spec.ndim) + ', found ndim=' + - str(ndim) + '. Full shape received: ' + - str(x.shape.as_list())) - if spec.max_ndim is not None: - ndim = x.shape.ndims - if ndim is not None and ndim > spec.max_ndim: - raise ValueError('Input ' + str(input_index) + ' of layer ' + - self.name + ' is incompatible with the layer: ' - 'expected max_ndim=' + str(spec.max_ndim) + - ', found ndim=' + str(ndim)) - if spec.min_ndim is not None: - ndim = x.shape.ndims - if ndim is not None and ndim < spec.min_ndim: - raise ValueError('Input ' + str(input_index) + ' of layer ' + - self.name + ' is incompatible with the layer: ' - ': expected min_ndim=' + str(spec.min_ndim) + - ', found ndim=' + str(ndim) + - '. Full shape received: ' + - str(x.shape.as_list())) - # Check dtype. - if spec.dtype is not None: - if x.dtype != spec.dtype: - raise ValueError('Input ' + str(input_index) + ' of layer ' + - self.name + ' is incompatible with the layer: ' - 'expected dtype=' + str(spec.dtype) + - ', found dtype=' + str(x.dtype)) - # Check specific shape axes. - if spec.axes: - shape = x.shape.as_list() - if shape is not None: - for axis, value in spec.axes.items(): - if hasattr(value, 'value'): - value = value.value - if value is not None and shape[int(axis)] not in {value, None}: - raise ValueError( - 'Input ' + str(input_index) + ' of layer ' + self.name + ' is' - ' incompatible with the layer: expected axis ' + str(axis) + - ' of input shape to have value ' + str(value) + - ' but received input with shape ' + str(shape)) - # Check shape. - if spec.shape is not None: - shape = x.shape.as_list() - if shape is not None: - for spec_dim, dim in zip(spec.shape, shape): - if spec_dim is not None and dim is not None: - if spec_dim != dim: - raise ValueError('Input ' + str(input_index) + - ' is incompatible with layer ' + self.name + - ': expected shape=' + str(spec.shape) + - ', found shape=' + str(shape)) + call_arg_spec = tf_inspect.getfullargspec(self.call) + # There is no explicit "inputs" argument expected or provided to + # call(). Arguments which have default values are considered non-inputs, + # and arguments without are considered inputs. + if call_arg_spec.defaults: + if call_arg_spec.varargs is not None: + raise TypeError( + 'Layers may not accept both positional arguments and ' + 'arguments with default values (unable to determine which ' + 'are inputs to the layer). ' + 'Issue occurred with layer "%s"' % (self.name)) + keyword_arg_names = set( + call_arg_spec.args[-len(call_arg_spec.defaults):]) + else: + keyword_arg_names = set() + # Training is never an input argument name, to allow signatures like + # call(x, training). + keyword_arg_names.add('training') + _, unwrapped_call = tf_decorator.unwrap(self.call) + bound_args = inspect.getcallargs( + unwrapped_call, *call_args, **call_kwargs) + if call_arg_spec.varkw is not None: + var_kwargs = bound_args.pop(call_arg_spec.varkw) + bound_args.update(var_kwargs) + keyword_arg_names = keyword_arg_names.union(var_kwargs.keys()) + all_args = call_arg_spec.args + if all_args and bound_args[all_args[0]] is self: + # Ignore the 'self' argument of methods + bound_args.pop(call_arg_spec.args[0]) + all_args = all_args[1:] + non_input_arg_values = {} + input_arg_values = [] + remaining_args_are_keyword = False + for argument_name in all_args: + if argument_name in keyword_arg_names: + remaining_args_are_keyword = True + else: + if remaining_args_are_keyword: + raise TypeError( + 'Found a positional argument in a layer call after a non-input ' + 'argument. All arguments after "training" must be keyword ' + 'arguments, and are not tracked as inputs to the layer. ' + 'Issue occurred with layer "%s"' % (self.name)) + if remaining_args_are_keyword: + non_input_arg_values[argument_name] = bound_args[argument_name] + else: + input_arg_values.append(bound_args[argument_name]) + if call_arg_spec.varargs is not None: + input_arg_values.extend(bound_args[call_arg_spec.varargs]) + return input_arg_values, non_input_arg_values - def set_weights(self, weights): - """Sets the weights of the layer, from Numpy arrays. + def _add_inbound_node(self, + input_tensors, + output_tensors, + arguments=None): + """Internal method to create an inbound node for the layer. Arguments: - weights: a list of Numpy arrays. The number - of arrays and their shape must match - number of the dimensions of the weights - of the layer (i.e. it should match the - output of `get_weights`). - - Raises: - ValueError: If the provided weights list does not match the - layer's specifications. - """ - params = self.weights - if len(params) != len(weights): - raise ValueError('You called `set_weights(weights)` on layer "' + - self.name + '" with a weight list of length ' + - str(len(weights)) + ', but the layer was expecting ' + - str(len(params)) + ' weights. Provided weights: ' + - str(weights)[:50] + '...') - if not params: - return - weight_value_tuples = [] - param_values = backend.batch_get_value(params) - for pv, p, w in zip(param_values, params, weights): - if pv.shape != w.shape: - raise ValueError('Layer weight shape ' + str(pv.shape) + - ' not compatible with ' - 'provided weight shape ' + str(w.shape)) - weight_value_tuples.append((p, w)) - backend.batch_set_value(weight_value_tuples) - - def get_weights(self): - """Returns the current weights of the layer. - - Returns: - Weights values as a list of numpy arrays. + input_tensors: list of input tensors. + output_tensors: list of output tensors. + arguments: dictionary of keyword arguments that were passed to the + `call` method of the layer at the call that created the node. """ - params = self.weights - return backend.batch_get_value(params) - - def get_config(self): - """Returns the config of the layer. + input_tensors = nest.flatten(input_tensors) + output_tensors = nest.flatten(output_tensors) - A layer config is a Python dictionary (serializable) - containing the configuration of a layer. - The same layer can be reinstantiated later - (without its trained weights) from this configuration. + # Collect input tensor(s) coordinates. + inbound_layers = [] + node_indices = [] + tensor_indices = [] + for x in input_tensors: + assert hasattr(x, '_keras_history') + inbound_layer, node_index, tensor_index = x._keras_history # pylint: disable=protected-access + inbound_layers.append(inbound_layer) + node_indices.append(node_index) + tensor_indices.append(tensor_index) - The config of a layer does not include connectivity - information, nor the layer class name. These are handled - by `Network` (one layer of abstraction above). + # Create node, add it to inbound nodes. + Node( + self, + inbound_layers=inbound_layers, + node_indices=node_indices, + tensor_indices=tensor_indices, + input_tensors=input_tensors, + output_tensors=output_tensors, + arguments=arguments) - Returns: - Python dictionary. - """ - config = {'name': self.name, 'trainable': self.trainable} - if hasattr(self, '_batch_input_shape'): - config['batch_input_shape'] = self._batch_input_shape - if hasattr(self, 'dtype'): - config['dtype'] = self.dtype - return config + # Update tensor history metadata. + for i in range(len(output_tensors)): + # The metadata attribute consists of 1) a layer instance + # 2) a node index for the layer, 3) a tensor index for the node. + # The allows layer reuse (multiple nodes per layer) and multi-output + # or multi-input layers (e.g. a layer can return multiple tensors, + # and each can be sent to a different layer). + output_tensors[i]._keras_history = (self, len(self._inbound_nodes) - 1, i) # pylint: disable=protected-access - @classmethod - def from_config(cls, config): - """Creates a layer from its config. + def _get_node_attribute_at_index(self, node_index, attr, attr_name): + """Private utility to retrieves an attribute (e.g. inputs) from a node. - This method is the reverse of `get_config`, - capable of instantiating the same layer from the config - dictionary. It does not handle layer connectivity - (handled by Network), nor weights (handled by `set_weights`). + This is used to implement the methods: + - get_input_shape_at + - get_output_shape_at + - get_input_at + etc... Arguments: - config: A Python dictionary, typically the - output of get_config. + node_index: Integer index of the node from which + to retrieve the attribute. + attr: Exact node attribute name. + attr_name: Human-readable attribute name, for error messages. Returns: - A layer instance. - """ - return cls(**config) - - -@tf_export( - 'keras.layers.InputSpec', v1=['keras.layers.InputSpec', 'layers.InputSpec']) -class InputSpec(object): - """Specifies the ndim, dtype and shape of every input to a layer. + The layer's attribute `attr` at the node of index `node_index`. - Every layer should expose (if appropriate) an `input_spec` attribute: - a list of instances of InputSpec (one per input tensor). + Raises: + RuntimeError: If the layer has no inbound nodes, or if called in Eager + mode. + ValueError: If the index provided does not match any node. + """ + if not self._inbound_nodes: + raise RuntimeError('The layer has never been called ' + 'and thus has no defined ' + attr_name + '.') + if not len(self._inbound_nodes) > node_index: + raise ValueError('Asked to get ' + attr_name + ' at node ' + + str(node_index) + ', but the layer has only ' + + str(len(self._inbound_nodes)) + ' inbound nodes.') + values = getattr(self._inbound_nodes[node_index], attr) + if len(values) == 1: + return values[0] + else: + return values - A None entry in a shape is compatible with any dimension, - a None shape is compatible with any shape. + @property + def _static_graph_friendly(self): + """Whether the layer can be called to create a static graph. - Arguments: - dtype: Expected DataType of the input. - shape: Shape tuple, expected shape of the input - (may include None for unchecked axes). - ndim: Integer, expected rank of the input. - max_ndim: Integer, maximum rank of the input. - min_ndim: Integer, minimum rank of the input. - axes: Dictionary mapping integer axes to - a specific dimension value. - """ + Because of nesting, there are two components to being "graph-friendly": + 1) all inner layers are graph-friendly + 2) the way they are composed is graph-friendly. + We denote the latter as "_call_is_graph_friendly", and define + "_static_graph_friendly" as being the combination of + "_call_is_graph_friendly" and "all inner layers are _static_graph_friendly". + For atomic layers (no inner layers), this is just "_call_is_graph_friendly". - def __init__(self, - dtype=None, - shape=None, - ndim=None, - max_ndim=None, - min_ndim=None, - axes=None): - self.dtype = dtype - self.shape = shape - if shape is not None: - self.ndim = len(shape) - else: - self.ndim = ndim - self.max_ndim = max_ndim - self.min_ndim = min_ndim - self.axes = axes or {} + Returns: + Boolean. + """ + return self._call_is_graph_friendly - def __repr__(self): - spec = [('dtype=' + str(self.dtype)) if self.dtype else '', - ('shape=' + str(self.shape)) if self.shape else '', - ('ndim=' + str(self.ndim)) if self.ndim else '', - ('max_ndim=' + str(self.max_ndim)) if self.max_ndim else '', - ('min_ndim=' + str(self.min_ndim)) if self.min_ndim else '', - ('axes=' + str(self.axes)) if self.axes else ''] - return 'InputSpec(%s)' % ', '.join(x for x in spec if x) + def _maybe_build(self, inputs): + # Check input assumptions set before layer building, e.g. input rank. + input_spec.assert_input_compatibility( + self.input_spec, inputs, self.name) + input_list = nest.flatten(inputs) + if input_list and self._dtype is None: + try: + self._dtype = input_list[0].dtype.base_dtype.name + except AttributeError: + pass + input_shapes = None + if all(hasattr(x, 'shape') for x in input_list): + input_shapes = nest.map_structure(lambda x: x.shape, inputs) + # Only call `build` if the user has manually overridden the build method. + if not hasattr(self.build, '_is_default'): + self.build(input_shapes) class Node(object): @@ -1768,192 +1705,12 @@ class Node(object): } -def unique_layer_name(name, name_uid_map=None, avoid_names=None, namespace='', - zero_based=False): - """Makes a layer name (or arbitrary string) unique within a TensorFlow graph. - - Arguments: - name: String name to make unique. - name_uid_map: An optional defaultdict(int) to use when creating unique - names. If None (default), uses a per-Graph dictionary. - avoid_names: An optional set or dict with names which should not be used. If - None (default) does not avoid any names. - namespace: Gets a name which is unique within the (graph, namespace). Layers - which are not Networks use a blank namespace and so get graph-global - names. - zero_based: If True, name sequences start with no suffix (e.g. "dense", - "dense_1"). If False, naming is one-based ("dense_1", "dense_2"). - - Returns: - Unique string name. - - Example: - - ```python - _unique_layer_name('dense') # dense_1 - _unique_layer_name('dense') # dense_2 - ``` - """ - if name_uid_map is None: - name_uid_map = get_default_graph_uid_map() - if avoid_names is None: - avoid_names = set() - proposed_name = None - while proposed_name is None or proposed_name in avoid_names: - name_key = (namespace, name) - if zero_based: - number = name_uid_map[name_key] - if number: - proposed_name = name + '_' + str(number) - else: - proposed_name = name - name_uid_map[name_key] += 1 - else: - name_uid_map[name_key] += 1 - proposed_name = name + '_' + str(name_uid_map[name_key]) - return proposed_name - - -def have_all_keras_metadata(iterable_or_element): - if not isinstance(iterable_or_element, (list, tuple)): - iterable = [iterable_or_element] - else: - iterable = nest.flatten(iterable_or_element) - return all([hasattr(x, '_keras_history') for x in iterable]) - - -def collect_previous_mask(input_tensors): - """Retrieves the output mask(s) of the previous node. - - Arguments: - input_tensors: A tensor or list of tensors. - - Returns: - A mask tensor or list of mask tensors. - """ - input_tensors = nest.flatten(input_tensors) - masks = [] - for x in input_tensors: - if hasattr(x, '_keras_mask'): - mask = x._keras_mask # pylint: disable=protected-access - masks.append(mask) - else: - masks.append(None) - if len(masks) == 1: - return masks[0] - return masks - - -def get_default_graph_uid_map(): - # TODO(fchollet): refactor this into backend. - graph = ops.get_default_graph() - name_uid_map = backend.PER_GRAPH_LAYER_NAME_UIDS.get(graph, None) - if name_uid_map is None: - name_uid_map = collections_lib.defaultdict(int) - backend.PER_GRAPH_LAYER_NAME_UIDS[graph] = name_uid_map - return name_uid_map - - -def make_variable(name, - shape=None, - dtype=dtypes.float32, - initializer=None, - partition_info=None, - trainable=None, - caching_device=None, - validate_shape=True, - constraint=None, - use_resource=None, - collections=None, - synchronization=tf_variables.VariableSynchronization.AUTO, - aggregation=tf_variables.VariableAggregation.NONE, - partitioner=None): # pylint: disable=unused-argument - """Temporary util to create a variable (relies on `variable_scope.variable`). - - Some reuse-related technicalities prevent us from using - `variable_scope.get_variable()` directly, so we use a subcomponent - that has fewer constraints (`variable_scope.variable()`). - - In the longer term, it seems like a similar "default variable creator" method - should exist in `CheckpointableBase` instead. When this happens, we can get - rid of this temporary solution. - - TODO(fchollet): remove this method when no longer needed. - TODO(fchollet): handle `partitioner` argument. - - Arguments: - name: Variable name. - shape: Variable shape. - dtype: The type of the variable. Defaults to `self.dtype` or `float32`. - initializer: Initializer instance (callable). - partition_info: Not handled at this time. - trainable: Whether the variable should be part of the layer's - "trainable_variables" (e.g. variables, biases) - or "non_trainable_variables" (e.g. BatchNorm mean, stddev). - Note, if the current variable scope is marked as non-trainable - then this parameter is ignored and any added variables are also - marked as non-trainable. `trainable` defaults to `True` unless - `synchronization` is set to `ON_READ`. - caching_device: Passed to `tf.Variable`. - validate_shape: Passed to `tf.Variable`. - constraint: Constraint instance (callable). - use_resource: Whether to use a `ResourceVariable`. - collections: List of graph collections keys. The new variable is added to - these collections. Defaults to `[GraphKeys.GLOBAL_VARIABLES]`. - synchronization: Indicates when a distributed a variable will be - aggregated. Accepted values are constants defined in the class - `tf.VariableSynchronization`. By default the synchronization is set to - `AUTO` and the current `DistributionStrategy` chooses - when to synchronize. If `synchronization` is set to `ON_READ`, - `trainable` must not be set to `True`. - aggregation: Indicates how a distributed variable will be aggregated. - Accepted values are constants defined in the class - `tf.VariableAggregation`. - partitioner: Not handled at this time. - - Returns: - Variable instance. - """ - initializing_from_value = False - if initializer is not None and not callable(initializer): - initializing_from_value = True - - with ops.init_scope(): - if initializing_from_value: - init_val = initializer - variable_dtype = None - else: - # Instantiate initializer if provided initializer is a type object. - if isinstance(initializer, type(init_ops.Initializer)): - initializer = initializer(dtype=dtype) - init_val = lambda: initializer( # pylint: disable=g-long-lambda - shape, dtype=dtype, partition_info=partition_info) - variable_dtype = dtype.base_dtype - if use_resource is None: - use_resource = True - - # TODO(apassos,rohanj) figure out how to remove collections from here so we - # can remove the V1. - v = tf_variables.VariableV1( - initial_value=init_val, - name=name, - trainable=trainable, - caching_device=caching_device, - dtype=variable_dtype, - validate_shape=validate_shape, - constraint=constraint, - use_resource=use_resource, - collections=collections, - synchronization=synchronization, - aggregation=aggregation) - return v - - def default(method): """Decorates a method to detect overrides in subclasses.""" method._is_default = True return method -def generate_placeholders_from_shape(shape): - return array_ops.placeholder(shape=shape, dtype=backend.floatx()) +# Avoid breaking users who directly import this symbol from this file. +# TODO(fchollet): remove this. +InputSpec = input_spec.InputSpec # pylint:disable=invalid-name diff --git a/tensorflow/python/keras/engine/base_layer_test.py b/tensorflow/python/keras/engine/base_layer_test.py index 704589349a..798775b6a5 100644 --- a/tensorflow/python/keras/engine/base_layer_test.py +++ b/tensorflow/python/keras/engine/base_layer_test.py @@ -81,14 +81,14 @@ class BaseLayerTest(test.TestCase): inputs = keras.Input((3,)) outputs = DynamicLayer1()(inputs) model = keras.Model(inputs, outputs) - self.assertEqual(model._is_static_graph_friendly, False) + self.assertEqual(model._static_graph_friendly, False) model.compile(RMSPropOptimizer(0.001), loss='mse') model.train_on_batch(np.random.random((2, 3)), np.random.random((2, 3))) inputs = keras.Input((3,)) outputs = DynamicLayer2()(inputs) model = keras.Model(inputs, outputs) - self.assertEqual(model._is_static_graph_friendly, False) + self.assertEqual(model._static_graph_friendly, False) model.compile(RMSPropOptimizer(0.001), loss='mse') model.train_on_batch(np.random.random((2, 3)), np.random.random((2, 3))) @@ -102,7 +102,7 @@ class BaseLayerTest(test.TestCase): outputs = inner_model(x) model = keras.Model(inputs, outputs) - self.assertEqual(model._is_static_graph_friendly, False) + self.assertEqual(model._static_graph_friendly, False) model.compile(RMSPropOptimizer(0.001), loss='mse') model.train_on_batch(np.random.random((2, 3)), np.random.random((2, 3))) @@ -116,7 +116,7 @@ class BaseLayerTest(test.TestCase): inputs = keras.Input((3,)) outputs = InvalidLayer()(inputs) model = keras.Model(inputs, outputs) - self.assertEqual(model._is_static_graph_friendly, False) + self.assertEqual(model._static_graph_friendly, False) model.compile(RMSPropOptimizer(0.001), loss='mse') with self.assertRaisesRegexp(ValueError, 'You did something wrong!'): model.train_on_batch(np.random.random((2, 3)), np.random.random((2, 3))) diff --git a/tensorflow/python/keras/engine/base_layer_utils.py b/tensorflow/python/keras/engine/base_layer_utils.py new file mode 100644 index 0000000000..d2f947f177 --- /dev/null +++ b/tensorflow/python/keras/engine/base_layer_utils.py @@ -0,0 +1,236 @@ +# Copyright 2018 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. +# ============================================================================== +"""Contains private utilities used mainly by the base Layer class.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import collections as collections_lib +import enum + +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops +from tensorflow.python.keras import backend +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import init_ops +from tensorflow.python.ops import variables as tf_variables +from tensorflow.python.util import nest + + +class CallConvention(enum.Enum): + """Calling conventions for passing `Layer` inputs to `Layer.call`.""" + # The Layer takes inputs as its first argument, named "inputs" for + # compatibility with the signature of Layer.__call__. This is the mode assumed + # for Layers which are not subclassed Models. + EXPLICIT_INPUTS_ARGUMENT = 1 + # The Layer takes a single positional argument, not named "inputs". It's + # treated like an "inputs" argument. + SINGLE_POSITIONAL_ARGUMENT = 2 + # The Layer has multiple positional arguments to which its inputs should be + # bound. + POSITIONAL_ARGUMENTS_ARE_INPUTS = 3 + + +def create_mean_metric(value, name=None): + # TODO(psv): Remove this import when b/110718070 is fixed. + from tensorflow.python.keras import metrics as metrics_module # pylint: disable=g-import-not-at-top + metric_obj = metrics_module.Mean(name=name) + result = metric_obj(value) + return metric_obj, result + + +def make_variable(name, + shape=None, + dtype=dtypes.float32, + initializer=None, + partition_info=None, + trainable=None, + caching_device=None, + validate_shape=True, + constraint=None, + use_resource=None, + collections=None, + synchronization=tf_variables.VariableSynchronization.AUTO, + aggregation=tf_variables.VariableAggregation.NONE, + partitioner=None): # pylint: disable=unused-argument + """Temporary util to create a variable (relies on `variable_scope.variable`). + + Some reuse-related technicalities prevent us from using + `variable_scope.get_variable()` directly, so we use a subcomponent + that has fewer constraints (`variable_scope.variable()`). + + In the longer term, it seems like a similar "default variable creator" method + should exist in `CheckpointableBase` instead. When this happens, we can get + rid of this temporary solution. + + TODO(fchollet): remove this method when no longer needed. + TODO(fchollet): handle `partitioner` argument. + + Arguments: + name: Variable name. + shape: Variable shape. + dtype: The type of the variable. Defaults to `self.dtype` or `float32`. + initializer: Initializer instance (callable). + partition_info: Not handled at this time. + trainable: Whether the variable should be part of the layer's + "trainable_variables" (e.g. variables, biases) + or "non_trainable_variables" (e.g. BatchNorm mean, stddev). + Note, if the current variable scope is marked as non-trainable + then this parameter is ignored and any added variables are also + marked as non-trainable. `trainable` defaults to `True` unless + `synchronization` is set to `ON_READ`. + caching_device: Passed to `tf.Variable`. + validate_shape: Passed to `tf.Variable`. + constraint: Constraint instance (callable). + use_resource: Whether to use a `ResourceVariable`. + collections: List of graph collections keys. The new variable is added to + these collections. Defaults to `[GraphKeys.GLOBAL_VARIABLES]`. + synchronization: Indicates when a distributed a variable will be + aggregated. Accepted values are constants defined in the class + `tf.VariableSynchronization`. By default the synchronization is set to + `AUTO` and the current `DistributionStrategy` chooses + when to synchronize. If `synchronization` is set to `ON_READ`, + `trainable` must not be set to `True`. + aggregation: Indicates how a distributed variable will be aggregated. + Accepted values are constants defined in the class + `tf.VariableAggregation`. + partitioner: Not handled at this time. + + Returns: + Variable instance. + """ + initializing_from_value = False + if initializer is not None and not callable(initializer): + initializing_from_value = True + + with ops.init_scope(): + if initializing_from_value: + init_val = initializer + variable_dtype = None + else: + # Instantiate initializer if provided initializer is a type object. + if isinstance(initializer, type(init_ops.Initializer)): + initializer = initializer(dtype=dtype) + init_val = lambda: initializer( # pylint: disable=g-long-lambda + shape, dtype=dtype, partition_info=partition_info) + variable_dtype = dtype.base_dtype + if use_resource is None: + use_resource = True + + # TODO(apassos,rohanj) figure out how to remove collections from here so we + # can remove the V1. + v = tf_variables.VariableV1( + initial_value=init_val, + name=name, + trainable=trainable, + caching_device=caching_device, + dtype=variable_dtype, + validate_shape=validate_shape, + constraint=constraint, + use_resource=use_resource, + collections=collections, + synchronization=synchronization, + aggregation=aggregation) + return v + + +def get_default_graph_uid_map(): + # TODO(fchollet): refactor this into backend. + graph = ops.get_default_graph() + name_uid_map = backend.PER_GRAPH_LAYER_NAME_UIDS.get(graph, None) + if name_uid_map is None: + name_uid_map = collections_lib.defaultdict(int) + backend.PER_GRAPH_LAYER_NAME_UIDS[graph] = name_uid_map + return name_uid_map + + +def unique_layer_name(name, name_uid_map=None, avoid_names=None, namespace='', + zero_based=False): + """Makes a layer name (or arbitrary string) unique within a TensorFlow graph. + + Arguments: + name: String name to make unique. + name_uid_map: An optional defaultdict(int) to use when creating unique + names. If None (default), uses a per-Graph dictionary. + avoid_names: An optional set or dict with names which should not be used. If + None (default) does not avoid any names. + namespace: Gets a name which is unique within the (graph, namespace). Layers + which are not Networks use a blank namespace and so get graph-global + names. + zero_based: If True, name sequences start with no suffix (e.g. "dense", + "dense_1"). If False, naming is one-based ("dense_1", "dense_2"). + + Returns: + Unique string name. + + Example: + + ```python + _unique_layer_name('dense') # dense_1 + _unique_layer_name('dense') # dense_2 + ``` + """ + if name_uid_map is None: + name_uid_map = get_default_graph_uid_map() + if avoid_names is None: + avoid_names = set() + proposed_name = None + while proposed_name is None or proposed_name in avoid_names: + name_key = (namespace, name) + if zero_based: + number = name_uid_map[name_key] + if number: + proposed_name = name + '_' + str(number) + else: + proposed_name = name + name_uid_map[name_key] += 1 + else: + name_uid_map[name_key] += 1 + proposed_name = name + '_' + str(name_uid_map[name_key]) + return proposed_name + + +def collect_previous_mask(input_tensors): + """Retrieves the output mask(s) of the previous node. + + Arguments: + input_tensors: A tensor or list of tensors. + + Returns: + A mask tensor or list of mask tensors. + """ + input_tensors = nest.flatten(input_tensors) + masks = [] + for x in input_tensors: + if hasattr(x, '_keras_mask'): + mask = x._keras_mask # pylint: disable=protected-access + masks.append(mask) + else: + masks.append(None) + if len(masks) == 1: + return masks[0] + return masks + + +def have_all_keras_metadata(iterable_or_element): + if not isinstance(iterable_or_element, (list, tuple)): + iterable = [iterable_or_element] + else: + iterable = nest.flatten(iterable_or_element) + return all(hasattr(x, '_keras_history') for x in iterable) + + +def generate_placeholders_from_shape(shape): + return array_ops.placeholder(shape=shape, dtype=backend.floatx()) diff --git a/tensorflow/python/keras/engine/distributed_training_utils.py b/tensorflow/python/keras/engine/distributed_training_utils.py index f939b7565a..d100182381 100644 --- a/tensorflow/python/keras/engine/distributed_training_utils.py +++ b/tensorflow/python/keras/engine/distributed_training_utils.py @@ -22,15 +22,17 @@ import numpy as np from tensorflow.python.client import session as session_module from tensorflow.python.data.ops import dataset_ops from tensorflow.python.data.ops import iterator_ops +from tensorflow.python.distribute import distribute_coordinator_context as dc_context +from tensorflow.python.distribute import distribute_lib from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_util from tensorflow.python.keras import backend as K from tensorflow.python.keras import callbacks +from tensorflow.python.keras.optimizer_v2 import optimizer_v2 from tensorflow.python.ops import array_ops from tensorflow.python.ops import variables from tensorflow.python.platform import tf_logging as logging -from tensorflow.python.training import distribute as distribute_lib from tensorflow.python.util import nest @@ -52,14 +54,18 @@ def set_weights(distribution_strategy, dist_model, weights): num_param = len(layer.weights) layer_weights = weights[:num_param] for sw, w in zip(layer.weights, layer_weights): - assign_ops.append(distribution_strategy.unwrap(sw.assign(w))) - + if ops.executing_eagerly_outside_functions(): + sw.assign(w) + else: + assign_ops.append(distribution_strategy.unwrap(sw.assign(w))) weights = weights[num_param:] - K.get_session().run(assign_ops) + + if not ops.executing_eagerly_outside_functions(): + K.get_session().run(assign_ops) def unwrap_values(distribution_strategy, grouped_inputs, grouped_outputs, - grouped_updates, grouped_session_args, + grouped_updates=None, grouped_session_args=None, with_loss_tensor=False): """Unwrap and return the list of values contained in the PerDevice parameters. @@ -92,11 +98,8 @@ def unwrap_values(distribution_strategy, grouped_inputs, grouped_outputs, grouped_inputs) if with_loss_tensor: # reduce loss tensor before adding it to the list of fetches - loss = distribution_strategy.unwrap( - distribution_strategy.reduce(distribute_lib.get_loss_reduction(), - grouped_outputs[0], - destinations='/device:CPU:0'))[0] - + loss = distribution_strategy.reduce(distribute_lib.get_loss_reduction(), + grouped_outputs[0]) all_outputs = flatten_perdevice_values(distribution_strategy, grouped_outputs[1:]) all_outputs = [loss] + all_outputs @@ -104,20 +107,25 @@ def unwrap_values(distribution_strategy, grouped_inputs, grouped_outputs, all_outputs = flatten_perdevice_values(distribution_strategy, grouped_outputs) - all_updates = flatten_perdevice_values(distribution_strategy, - grouped_updates) + if grouped_updates: + all_updates = flatten_perdevice_values(distribution_strategy, + grouped_updates) + else: + all_updates = None all_session_args = {} - grouped_feed_dict = grouped_session_args.get('feed_dict') - if grouped_feed_dict: - all_session_args['feed_dict'] = flatten_perdevice_values( - distribution_strategy, grouped_feed_dict) - - grouped_fetches = grouped_session_args.get('fetches') - if grouped_fetches: - all_session_args['fetches'] = flatten_perdevice_values( - distribution_strategy, grouped_fetches) - + if grouped_session_args: + grouped_feed_dict = grouped_session_args.get('feed_dict') + if grouped_feed_dict: + all_session_args['feed_dict'] = flatten_perdevice_values( + distribution_strategy, grouped_feed_dict) + + grouped_fetches = grouped_session_args.get('fetches') + if grouped_fetches: + all_session_args['fetches'] = flatten_perdevice_values( + distribution_strategy, grouped_fetches) + + # TODO(priyag): Return only non empty/None values return all_inputs, all_outputs, all_updates, all_session_args @@ -144,11 +152,14 @@ def flatten_perdevice_values(distribution_strategy, perdevice_values): for e in distribution_strategy.unwrap(flattened)] -def validate_callbacks(input_callbacks): +def validate_callbacks(input_callbacks, optimizer, current_strategy): """Validate whether given callbacks are supported by DistributionStrategy. Args: input_callbacks: List of callbacks passed by the user to fit. + optimizer: Optimizer instance used to train the model. + current_strategy: The DistributionStrategy used to distribute training + and validation. Raises: ValueError: If `LearningRateScheduler` or `ReduceLROnPlateau` is one of the @@ -170,12 +181,18 @@ def validate_callbacks(input_callbacks): 'these attributes are not set. You can access each of ' 'the individual distributed models using the ' '`_grouped_model` attribute of your original model.') - if isinstance(callback, callbacks.LearningRateScheduler): - raise ValueError('LearningRateScheduler callback is not supported with ' - 'DistributionStrategy.') - if isinstance(callback, callbacks.ReduceLROnPlateau): - raise ValueError('ReduceLROnPlateau callback is not supported with ' - 'DistributionStrategy.') + if isinstance(callback, (callbacks.LearningRateScheduler, + callbacks.ReduceLROnPlateau)): + strategy_name = current_strategy.__class__.__name__ + # TODO(anjalisridhar): We might need to add a condition for multi + # worker strategy when we support it in Keras. + if is_tpu_strategy(current_strategy): + raise ValueError('%s callback is not supported with %s.' % + (callback, strategy_name)) + + if not isinstance(optimizer, optimizer_v2.OptimizerV2): + raise ValueError('You must specify a Keras Optimizer V2 when using ' + '%s callback with DistributionStrategy.' % callback) # If users want to use the TensorBoard callback they cannot use certain # features of the callback that involve accessing model attributes and @@ -293,19 +310,64 @@ def validate_all_tensor_shapes(x, x_values): ' inputs {}'.format(x)) +def _wait_for_variable_initialization(session): + """Utility to wait for variables to be initialized.""" + all_variables = K._get_variables(K.get_graph()) # pylint: disable=protected-access + candidate_vars = [] + for v in all_variables: + if not getattr(v, '_keras_initialized', False): + candidate_vars.append(v) + + if not candidate_vars: + return + + while True: + is_initialized = session.run( + [variables.is_variable_initialized(v) for v in candidate_vars]) + uninitialized_vars = [] + for flag, v in zip(is_initialized, candidate_vars): + if not flag: + uninitialized_vars.append(v) + v._keras_initialized = True # pylint: disable=protected-access + if not uninitialized_vars: + break + + +def init_restore_or_wait_for_variables(): + """Initialize or restore variables or wait for variables to be initialized.""" + session = K._get_session() # pylint: disable=protected-access + worker_context = dc_context.get_current_worker_context() + if not worker_context or worker_context.experimental_should_init: + # TODO(yuefengz): if checkpoints exit, restore from checkpoint. + K._initialize_variables(session) # pylint: disable=protected-access + else: + _wait_for_variable_initialization(session) + + def configure_and_create_session(distribution_strategy): """Configure session config and create a session with it.""" # TODO(priyag): Throw error if a session already exists. session_config = K.get_default_session_config() - distribution_strategy.configure(session_config) - if distribution_strategy.__class__.__name__ == 'TPUStrategy': - # TODO(priyag): Remove this workaround when Distributed Coordinator is - # integrated with keras and we can create a session from there. - master = distribution_strategy._tpu_cluster_resolver.master() # pylint: disable=protected-access + if is_tpu_strategy(distribution_strategy): + # TODO(priyag, yuefengz): Remove this workaround when Distribute + # Coordinator is integrated with keras and we can create a session from + # there. + distribution_strategy.configure(session_config) + master = distribution_strategy.extended._tpu_cluster_resolver.master() # pylint: disable=protected-access session = session_module.Session(config=session_config, target=master) else: - session = session_module.Session(config=session_config) + worker_context = dc_context.get_current_worker_context() + if worker_context: + dc_session_config = worker_context.session_config + # Merge the default session config to the one from distribute coordinator, + # which is fine for now since they don't have conflicting configurations. + dc_session_config.MergeFrom(session_config) + session = session_module.Session( + config=dc_session_config, target=worker_context.master_target) + else: + distribution_strategy.configure(session_config) + session = session_module.Session(config=session_config) K.set_session(session) @@ -334,11 +396,15 @@ def validate_inputs(x, y, distribution_strategy): 'Iterator. You must pass a `tf.data.Dataset` object or a ' 'numpy array as input.') - if distribution_strategy.__class__.__name__ == 'TPUStrategy': + if is_tpu_strategy(distribution_strategy): for i in [x, y]: - if isinstance(i, dataset_ops.Dataset): + if isinstance(i, dataset_ops.DatasetV2): shapes = nest.flatten(i.output_shapes) - if any([not s.is_fully_defined() for s in shapes]): + try: + s = next(s for s in shapes if not s.is_fully_defined()) + except StopIteration: + continue + else: raise ValueError( 'Using TPUs currently requires fully defined shapes. Either use ' 'set_shape() on the input tensors or use ' @@ -346,37 +412,97 @@ def validate_inputs(x, y, distribution_strategy): 'Found unknown shape {} in input {}.'.format(s, i)) -def get_input_batch_params(first_x_value, batch_size, distribution_strategy): +# TODO(b/118776054): Currently we support global batch size for TPUStrategy and +# core MirroredStrategy only. Remove this check when contrib MirroredStrategy is +# no longer needed. +def global_batch_size_supported(distribution_strategy): + return distribution_strategy.extended._global_batch_size # pylint: disable=protected-access + + +# TODO(sourabhbajaj): Remove this once we use the same API for all strategies. +def is_tpu_strategy(strategy): + """We're executing TPU Strategy.""" + return strategy is not None and strategy.__class__.__name__ == 'TPUStrategy' + + +def get_input_params(distribution_strategy, first_x_value, steps, batch_size, + is_training=False): """Calculate the number of batches and steps/steps_per_epoch. Args: + distribution_strategy: The DistributionStrategy used to compile the model. first_x_value: This is the first input numpy array that is passed in as the model input. - batch_size: The specified batch_size or the default batch_size of 32. - distribution_strategy: The current DistributionStrategy used to compile the - model. + steps: The specified number of steps. + batch_size: The specified batch_size. + is_training: Boolean to relax the constraints on consuming all the training + samples to keep compatibility till we support partial batches. Returns: - The steps or steps_per_epoch argument depending on if a user is - calling `fit`, `evaluate` or `predict`. + steps: The steps or steps_per_epoch argument depending on if a user is + calling `fit`, `evaluate` or `predict`. If the is_training flag is set + we don't require the number of samples to be used completely. + batch_size: The batch size to be used in model iterations. Raises: ValueError: If the number of batches or steps evaluates to 0. """ - num_batches = first_x_value.shape[0] // batch_size - if not num_batches: - raise ValueError('Please specify a batch_size that is smaller than' - 'the number of input samples %d.' % first_x_value.shape[0]) - steps = num_batches // distribution_strategy.num_replicas_in_sync - if not steps: - # TODO(anjalisridhar): Number of replicas in the error message may not - # convey what we want to the user. Is there another terminology that we can - # use that is consistent across different strategies? - raise ValueError('The number of batches %d is smaller than the number ' - 'of replicas %d used for DistributionStrategy. ' % - (num_batches, distribution_strategy.num_replicas_in_sync)) - return steps + num_samples = first_x_value.shape[0] + # TODO(b/118776054): Use global batch size for Keras/DS support. + # Currently this is only supported in TPUStrategy and CoreMirroredStrategy. + use_per_replica_batch = not global_batch_size_supported( + distribution_strategy) + + if steps is None: + if batch_size is None: + # If neither the batch size or number of steps are set. We choose the + # global batch size as the minimum of number of samples and 32. 32 is + # chosen to provide backward compatibility. + global_batch_size = min(num_samples, 32) + else: + # If the user provided the batch size we need to handle the case + # between different strategies that use the global/per-replica batch size + global_batch_size = batch_size + if use_per_replica_batch: + global_batch_size *= distribution_strategy.num_replicas_in_sync + if not is_training and num_samples % global_batch_size: + raise ValueError('The number of samples %s is not divisible by ' + 'batch size %s.' % (num_samples, global_batch_size)) + steps = num_samples // global_batch_size + else: + if batch_size is None: + # We calculate the batch size based on the number of steps specified + if num_samples % steps: + raise ValueError('The number of samples %s is not divisible by ' + 'steps %s. Please change the number of steps to a ' + 'value that can consume all the samples' % ( + num_samples, steps)) + global_batch_size = num_samples // steps + else: + # If the user provided the batch size we need to handle the case + # between different strategies that use the global/per-replica batch size + global_batch_size = batch_size + if use_per_replica_batch: + global_batch_size *= distribution_strategy.num_replicas_in_sync + + if num_samples < (global_batch_size * steps): + raise ValueError('Number of samples %s is less than samples required ' + 'for specified batch_size %s and steps %s' % ( + num_samples, global_batch_size, steps)) + + # We need to return the per replica or global batch size based on the strategy + if use_per_replica_batch: + if global_batch_size % distribution_strategy.num_replicas_in_sync: + raise ValueError( + 'The batch size (%s) could not be sharded evenly across the sync ' + 'replicas (%s) in the distribution strategy.' % ( + global_batch_size, distribution_strategy.num_replicas_in_sync)) + batch_size = global_batch_size // distribution_strategy.num_replicas_in_sync + else: + batch_size = global_batch_size + + return steps, batch_size def get_batch_dimension(iterator): @@ -387,33 +513,6 @@ def get_batch_dimension(iterator): return dims[0] if dims else None -def get_batch_size(num_replicas, num_samples, steps): - """Calculate and return batch size for numpy inputs. - - Args: - num_replicas: Number of devices over which the model input is distributed. - num_samples: Total number of input samples in the input numpy arrays. - steps: Number of steps that we run the model for. - - Returns: - batch size used to create the Dataset object from the input numpy arrays. - - """ - if num_samples % steps != 0: - logging.warning('The number of input samples %d is not evenly ' - 'divisible by the number of steps %d. ' - 'Some samples will not be processed as expected.' % - (num_samples, steps)) - global_batch_size = num_samples // steps - if global_batch_size % num_replicas != 0: - logging.warning('The total number of batches per step %d is not evenly ' - 'divisible by the number of replicas %d used in ' - 'DistributionStrategy. Some samples will not be processed ' - 'as expected.' % - (global_batch_size, num_replicas)) - return global_batch_size // num_replicas - - def get_cpu_device(distribution_strategy): """Returns the CPU device of the TPU host or the default CPU device string. @@ -429,12 +528,12 @@ def get_cpu_device(distribution_strategy): NotImplementedError: We currently don't support copying numpy data to multiple hosts in the case of Cloud TPU pods. """ - if distribution_strategy.__class__.__name__ == 'TPUStrategy': - if distribution_strategy.num_hosts > 1: + if is_tpu_strategy(distribution_strategy): + if distribution_strategy.extended.num_hosts > 1: raise NotImplementedError('TPUDistributionStrategy does not ' 'support numpy inputs when running on Cloud' 'TPU pods.') - return distribution_strategy.get_host_cpu_device(0) + return distribution_strategy.extended.get_host_cpu_device(0) else: # For all strategies except TPUDistributionStrategy # TODO(anjalisridhar): We may need to modify this when we add support for diff --git a/tensorflow/python/keras/engine/feature_columns_integration_test.py b/tensorflow/python/keras/engine/feature_columns_integration_test.py index e0478ee357..b7549e013c 100644 --- a/tensorflow/python/keras/engine/feature_columns_integration_test.py +++ b/tensorflow/python/keras/engine/feature_columns_integration_test.py @@ -18,11 +18,13 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +from absl.testing import parameterized import numpy as np from tensorflow.python import keras from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.feature_column import feature_column_v2 as fc +from tensorflow.python.eager import context +from tensorflow.python.feature_column import feature_column_lib as fc from tensorflow.python.framework import test_util as tf_test_util from tensorflow.python.keras import metrics as metrics_module from tensorflow.python.platform import test @@ -33,7 +35,7 @@ class TestDNNModel(keras.models.Model): def __init__(self, feature_columns, units, name=None, **kwargs): super(TestDNNModel, self).__init__(name=name, **kwargs) - self._input_layer = fc.FeatureLayer(feature_columns, name='input_layer') + self._input_layer = fc.DenseFeatures(feature_columns, name='input_layer') self._dense_layer = keras.layers.Dense(units, name='dense_layer') def call(self, features): @@ -42,7 +44,7 @@ class TestDNNModel(keras.models.Model): return net -class FeatureColumnsIntegrationTest(test.TestCase): +class FeatureColumnsIntegrationTest(test.TestCase, parameterized.TestCase): """Most Sequential model API tests are covered in `training_test.py`. """ @@ -51,7 +53,7 @@ class FeatureColumnsIntegrationTest(test.TestCase): def test_sequential_model(self): columns = [fc.numeric_column('a')] model = keras.models.Sequential([ - fc.FeatureLayer(columns), + fc.DenseFeatures(columns), keras.layers.Dense(64, activation='relu'), keras.layers.Dense(20, activation='softmax') ]) @@ -72,7 +74,7 @@ class FeatureColumnsIntegrationTest(test.TestCase): def test_sequential_model_with_ds_input(self): columns = [fc.numeric_column('a')] model = keras.models.Sequential([ - fc.FeatureLayer(columns), + fc.DenseFeatures(columns), keras.layers.Dense(64, activation='relu'), keras.layers.Dense(20, activation='softmax') ]) @@ -112,8 +114,10 @@ class FeatureColumnsIntegrationTest(test.TestCase): dnn_model.evaluate(x=x, y=y, batch_size=5) dnn_model.predict(x=x, batch_size=5) + @parameterized.parameters(True, False) @tf_test_util.run_in_graph_and_eager_modes - def test_subclassed_model_with_feature_columns_with_ds_input(self): + def test_subclassed_model_with_feature_columns_with_ds_input(self, + run_eagerly): col_a = fc.numeric_column('a') col_b = fc.numeric_column('b') @@ -122,7 +126,8 @@ class FeatureColumnsIntegrationTest(test.TestCase): dnn_model.compile( optimizer=rmsprop.RMSPropOptimizer(learning_rate=0.001), loss='categorical_crossentropy', - metrics=['accuracy']) + metrics=['accuracy'], + run_eagerly=run_eagerly and context.executing_eagerly()) y = np.random.randint(20, size=(100, 1)) y = keras.utils.to_categorical(y, num_classes=20) @@ -140,10 +145,10 @@ class FeatureColumnsIntegrationTest(test.TestCase): col_a = fc.numeric_column('a') col_b = fc.numeric_column('b') - feature_layer = fc.FeatureLayer([col_a, col_b], name='fc') + feature_layer = fc.DenseFeatures([col_a, col_b], name='fc') dense = keras.layers.Dense(4) - # This seems problematic.... We probably need something for FeatureLayer + # This seems problematic.... We probably need something for DenseFeatures # the way Input is for InputLayer. output = dense(feature_layer) @@ -167,11 +172,11 @@ class FeatureColumnsIntegrationTest(test.TestCase): col_b = fc.numeric_column('b') col_c = fc.numeric_column('c') - fc1 = fc.FeatureLayer([col_a, col_b], name='fc1') - fc2 = fc.FeatureLayer([col_b, col_c], name='fc2') + fc1 = fc.DenseFeatures([col_a, col_b], name='fc1') + fc2 = fc.DenseFeatures([col_b, col_c], name='fc2') dense = keras.layers.Dense(4) - # This seems problematic.... We probably need something for FeatureLayer + # This seems problematic.... We probably need something for DenseFeatures # the way Input is for InputLayer. output = dense(fc1) + dense(fc2) diff --git a/tensorflow/python/keras/engine/input_layer.py b/tensorflow/python/keras/engine/input_layer.py index 4e96106004..9874efe2bc 100644 --- a/tensorflow/python/keras/engine/input_layer.py +++ b/tensorflow/python/keras/engine/input_layer.py @@ -19,12 +19,10 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -from tensorflow.python.eager import context from tensorflow.python.framework import tensor_shape from tensorflow.python.keras import backend from tensorflow.python.keras.engine import base_layer from tensorflow.python.keras.utils import tf_utils -from tensorflow.python.ops import array_ops from tensorflow.python.util.tf_export import tf_export @@ -84,7 +82,6 @@ class InputLayer(base_layer.Layer): self.sparse = sparse self.batch_size = batch_size self.supports_masking = True - self._can_use_graph_functions = True if isinstance(input_shape, tensor_shape.TensorShape): input_shape = tuple(input_shape.as_list()) @@ -95,19 +92,19 @@ class InputLayer(base_layer.Layer): else: batch_input_shape = None graph = backend.get_graph() - with context.graph_mode(): - with graph.as_default(): - # In graph mode, create a graph placeholder to call the layer on. - if sparse: - input_tensor = array_ops.sparse_placeholder( - shape=batch_input_shape, - dtype=dtype, - name=self.name) - else: - input_tensor = array_ops.placeholder( - shape=batch_input_shape, - dtype=dtype, - name=self.name) + with graph.as_default(): + # In graph mode, create a graph placeholder to call the layer on. + if sparse: + input_tensor = backend.placeholder( + shape=batch_input_shape, + dtype=dtype, + name=self.name, + sparse=True) + else: + input_tensor = backend.placeholder( + shape=batch_input_shape, + dtype=dtype, + name=self.name) self.is_placeholder = True self._batch_input_shape = batch_input_shape diff --git a/tensorflow/python/keras/engine/input_spec.py b/tensorflow/python/keras/engine/input_spec.py new file mode 100644 index 0000000000..7277c16fe5 --- /dev/null +++ b/tensorflow/python/keras/engine/input_spec.py @@ -0,0 +1,170 @@ +# Copyright 2018 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. +# ============================================================================== +# pylint: disable=protected-access +"""Contains the InputSpec class.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from six.moves import zip # pylint: disable=redefined-builtin + +from tensorflow.python.util import nest +from tensorflow.python.util.tf_export import tf_export + + +@tf_export('keras.layers.InputSpec', + v1=['keras.layers.InputSpec', 'layers.InputSpec']) +class InputSpec(object): + """Specifies the ndim, dtype and shape of every input to a layer. + + Every layer should expose (if appropriate) an `input_spec` attribute: + a list of instances of InputSpec (one per input tensor). + + A None entry in a shape is compatible with any dimension, + a None shape is compatible with any shape. + + Arguments: + dtype: Expected DataType of the input. + shape: Shape tuple, expected shape of the input + (may include None for unchecked axes). + ndim: Integer, expected rank of the input. + max_ndim: Integer, maximum rank of the input. + min_ndim: Integer, minimum rank of the input. + axes: Dictionary mapping integer axes to + a specific dimension value. + """ + + def __init__(self, + dtype=None, + shape=None, + ndim=None, + max_ndim=None, + min_ndim=None, + axes=None): + self.dtype = dtype + self.shape = shape + if shape is not None: + self.ndim = len(shape) + else: + self.ndim = ndim + self.max_ndim = max_ndim + self.min_ndim = min_ndim + self.axes = axes or {} + + def __repr__(self): + spec = [('dtype=' + str(self.dtype)) if self.dtype else '', + ('shape=' + str(self.shape)) if self.shape else '', + ('ndim=' + str(self.ndim)) if self.ndim else '', + ('max_ndim=' + str(self.max_ndim)) if self.max_ndim else '', + ('min_ndim=' + str(self.min_ndim)) if self.min_ndim else '', + ('axes=' + str(self.axes)) if self.axes else ''] + return 'InputSpec(%s)' % ', '.join(x for x in spec if x) + + +def assert_input_compatibility(input_spec, inputs, layer_name): + """Checks compatibility between the layer and provided inputs. + + This checks that the tensor(s) `inputs` verify the input assumptions + of a layer (if any). If not, a clear and actional exception gets raised. + + Arguments: + input_spec: An InputSpec instance, or None. + inputs: Input tensor or list of input tensors. + layer_name: String, name of the layer (for error message formatting). + + Raises: + ValueError: in case of mismatch between + the provided inputs and the expectations of the layer. + """ + if not input_spec: + return + if not isinstance(input_spec, (list, tuple)): + input_spec = nest.flatten(input_spec) + + inputs = nest.flatten(inputs) + if len(inputs) != len(input_spec): + raise ValueError('Layer ' + layer_name + ' expects ' + + str(len(input_spec)) + ' inputs, ' + 'but it received ' + str(len(inputs)) + + ' input tensors. Inputs received: ' + str(inputs)) + for input_index, (x, spec) in enumerate(zip(inputs, input_spec)): + if spec is None: + continue + + if (spec.ndim is not None or + spec.min_ndim is not None or + spec.max_ndim is not None): + if x.shape.ndims is None: + raise ValueError('Input ' + str(input_index) + ' of layer ' + + layer_name + ' is incompatible with the layer: ' + 'its rank is undefined, but the layer requires a ' + 'defined rank.') + + # Check ndim. + if spec.ndim is not None: + ndim = x.shape.ndims + if ndim != spec.ndim: + raise ValueError('Input ' + str(input_index) + ' of layer ' + + layer_name + ' is incompatible with the layer: ' + 'expected ndim=' + str(spec.ndim) + ', found ndim=' + + str(ndim) + '. Full shape received: ' + + str(x.shape.as_list())) + if spec.max_ndim is not None: + ndim = x.shape.ndims + if ndim is not None and ndim > spec.max_ndim: + raise ValueError('Input ' + str(input_index) + ' of layer ' + + layer_name + ' is incompatible with the layer: ' + 'expected max_ndim=' + str(spec.max_ndim) + + ', found ndim=' + str(ndim)) + if spec.min_ndim is not None: + ndim = x.shape.ndims + if ndim is not None and ndim < spec.min_ndim: + raise ValueError('Input ' + str(input_index) + ' of layer ' + + layer_name + ' is incompatible with the layer: ' + ': expected min_ndim=' + str(spec.min_ndim) + + ', found ndim=' + str(ndim) + + '. Full shape received: ' + + str(x.shape.as_list())) + # Check dtype. + if spec.dtype is not None: + if x.dtype != spec.dtype: + raise ValueError('Input ' + str(input_index) + ' of layer ' + + layer_name + ' is incompatible with the layer: ' + 'expected dtype=' + str(spec.dtype) + + ', found dtype=' + str(x.dtype)) + # Check specific shape axes. + if spec.axes: + shape = x.shape.as_list() + if shape is not None: + for axis, value in spec.axes.items(): + if hasattr(value, 'value'): + value = value.value + if value is not None and shape[int(axis)] not in {value, None}: + raise ValueError( + 'Input ' + str(input_index) + ' of layer ' + layer_name + ' is' + ' incompatible with the layer: expected axis ' + str(axis) + + ' of input shape to have value ' + str(value) + + ' but received input with shape ' + str(shape)) + # Check shape. + if spec.shape is not None: + shape = x.shape.as_list() + if shape is not None: + for spec_dim, dim in zip(spec.shape, shape): + if spec_dim is not None and dim is not None: + if spec_dim != dim: + raise ValueError('Input ' + str(input_index) + + ' is incompatible with layer ' + layer_name + + ': expected shape=' + str(spec.shape) + + ', found shape=' + str(shape)) diff --git a/tensorflow/python/keras/engine/network.py b/tensorflow/python/keras/engine/network.py index 9b58180e3d..7e6cc7bfee 100644 --- a/tensorflow/python/keras/engine/network.py +++ b/tensorflow/python/keras/engine/network.py @@ -36,7 +36,9 @@ from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape from tensorflow.python.keras import backend from tensorflow.python.keras.engine import base_layer +from tensorflow.python.keras.engine import base_layer_utils from tensorflow.python.keras.engine import saving +from tensorflow.python.keras.engine import training_utils from tensorflow.python.keras.utils import generic_utils from tensorflow.python.keras.utils import layer_utils from tensorflow.python.keras.utils import tf_utils @@ -112,11 +114,6 @@ class Network(base_layer.Layer): self.trainable = True self._is_compiled = False self._expects_training_arg = False - # A list of "extra" variables assigned to attributes of this class, included - # in self.weights and self.variables. Always empty for graph networks (but - # included in base_init to avoid excessive special casing when retrieving - # the value). - self._extra_variables = [] # In many internal cases one needs to compute both the model's output # and its output mask without relying on `__call__` (which would do both and # set mask metadata), but for models, computing the mask requires to @@ -134,12 +131,19 @@ class Network(base_layer.Layer): self.optimizer = None # Private attributes to implement compatibility with Layer. + self._trainable_weights = [] + self._non_trainable_weights = [] self._updates = [] # Used in symbolic mode only. self._losses = [] self._eager_losses = [] + # A list of metric instances corresponding to the symbolic metric tensors + # added using the `add_metric` API. + self._metrics = [] + # A dictionary that maps metric names to metric result tensors. + self._metrics_tensors = {} self._scope = None # Never used. self._reuse = None # Never used. - self._can_use_graph_functions = False + self._call_is_graph_friendly = True if context.executing_eagerly(): self._graph = None else: @@ -160,7 +164,8 @@ class Network(base_layer.Layer): @checkpointable.no_automatic_dependency_tracking def _init_graph_network(self, inputs, outputs, name=None): - self._call_convention = base_layer.CallConvention.EXPLICIT_INPUTS_ARGUMENT + self._call_convention = (base_layer_utils + .CallConvention.EXPLICIT_INPUTS_ARGUMENT) # Normalize and set self.inputs, self.outputs. if isinstance(inputs, (list, tuple)): self.inputs = list(inputs) # Tensor or list of tensors. @@ -170,43 +175,7 @@ class Network(base_layer.Layer): self.outputs = list(outputs) else: self.outputs = [outputs] - - # Check for redundancy in inputs. - if len(set(self.inputs)) != len(self.inputs): - raise ValueError('The list of inputs passed to the model ' - 'is redundant. ' - 'All inputs should only appear once.' - ' Found: ' + str(self.inputs)) - for x in self.inputs: - # Check that x has appropriate `_keras_history` metadata. - if not hasattr(x, '_keras_history'): - cls_name = self.__class__.__name__ - raise ValueError('Input tensors to a ' + cls_name + ' ' + - 'must come from `tf.keras.Input`. ' - 'Received: ' + str(x) + - ' (missing previous layer metadata).') - # Check that x is an input tensor. - # pylint: disable=protected-access - layer, node_index, tensor_index = x._keras_history - if len(layer._inbound_nodes) > 1 or ( - layer._inbound_nodes and layer._inbound_nodes[0].inbound_layers): - cls_name = self.__class__.__name__ - logging.warning(cls_name + ' inputs must come from ' - '`tf.keras.Input` (thus holding past layer metadata), ' - 'they cannot be the output of ' - 'a previous non-Input layer. ' - 'Here, a tensor specified as ' - 'input to "' + self.name + '" was not an Input tensor, ' - 'it was generated by layer ' + layer.name + '.\n' - 'Note that input tensors are ' - 'instantiated via `tensor = tf.keras.Input(shape)`.\n' - 'The tensor that caused the issue was: ' + str(x.name)) - for x in self.outputs: - if not hasattr(x, '_keras_history'): - cls_name = self.__class__.__name__ - raise ValueError('Output tensors to a ' + cls_name + ' must be ' - 'the output of a TensorFlow `Layer` ' - '(thus holding past layer metadata). Found: ' + str(x)) + self._validate_graph_inputs_and_outputs() self._base_init(name=name) self._compute_previous_mask = ( @@ -258,10 +227,6 @@ class Network(base_layer.Layer): self._track_layers(layers) - # A Graph network supports defun-ed eager loops if all of its layers do. - self._can_use_graph_functions = all( - layer._can_use_graph_functions for layer in layers) - # Create the node linking internal inputs to internal outputs. base_layer.Node( outbound_layer=self, @@ -282,9 +247,7 @@ class Network(base_layer.Layer): if layer.is_placeholder: self._feed_input_names.append(layer.name) self._feed_input_shapes.append(backend.int_shape(self.inputs[i])) - # layer.input gives an error in eager mode - if not context.executing_eagerly(): - self._feed_inputs.append(layer.input) + self._feed_inputs.append(layer.input) for layer in self._output_layers: self.output_names.append(layer.name) @@ -301,16 +264,15 @@ class Network(base_layer.Layer): self.outputs = [] self.inputs = [] self.built = False - self._static_graph_friendly = True @property - def _is_static_graph_friendly(self): + def _static_graph_friendly(self): if self._is_graph_network: - return all(layer._is_static_graph_friendly for layer in self.layers) - return self._static_graph_friendly + return all(layer._static_graph_friendly for layer in self.layers) + return self._call_is_graph_friendly def _determine_call_convention(self, call_argspec): - """Decides how `self.call()` is invoked. See base_layer.CallConvention.""" + """Decides how `self.call()` is invoked. See `CallConvention`.""" if call_argspec.varargs: may_take_single_argument = False else: @@ -342,11 +304,11 @@ class Network(base_layer.Layer): "Model.call() takes a single positional argument (to which " "inputs are passed by convention) and a separate 'inputs' " "argument. Unable to determine which arguments are inputs.") - return base_layer.CallConvention.SINGLE_POSITIONAL_ARGUMENT + return base_layer_utils.CallConvention.SINGLE_POSITIONAL_ARGUMENT if 'inputs' in call_argspec.args: - return base_layer.CallConvention.EXPLICIT_INPUTS_ARGUMENT + return base_layer_utils.CallConvention.EXPLICIT_INPUTS_ARGUMENT else: - return base_layer.CallConvention.POSITIONAL_ARGUMENTS_ARE_INPUTS + return base_layer_utils.CallConvention.POSITIONAL_ARGUMENTS_ARE_INPUTS def _track_layers(self, layers): """Add Checkpointable dependencies on a list of Layers.""" @@ -415,44 +377,26 @@ class Network(base_layer.Layer): # simply by assigning them to attributes. not self._is_graph_network and isinstance(value, variables.Variable)): - self._extra_variables.append(value) + if value.trainable: + # Could already be added via `add_weight`. + if value not in self._trainable_weights: + self._trainable_weights.append(value) + else: + if value not in self._non_trainable_weights: + self._non_trainable_weights.append(value) + + # Keeping track of metric instance created in subclassed model/layer. + # We do this so that we can maintain the correct order of metrics by adding + # the instance to the `metrics` list as soon as it is created. + from tensorflow.python.keras import metrics as metrics_module # pylint: disable=g-import-not-at-top + if isinstance(value, metrics_module.Metric): + self._metrics.append(value) super(Network, self).__setattr__(name, value) - def add_variable(self, name, shape, dtype=None, initializer=None, - regularizer=None, trainable=True, constraint=None): - if self._is_graph_network: - raise NotImplementedError('`add_variable` is not supported on Networks.') - else: - raise NotImplementedError( - '`add_variable` is not supported on Networks. However, you may ' - 'assign variables to attributes and they will show up in the weights ' - 'and variables properties.') - - def add_weight(self, - name, - shape, - dtype=None, - initializer=None, - regularizer=None, - trainable=None, - constraint=None, - partitioner=None, - use_resource=None, - synchronization=variables.VariableSynchronization.AUTO, - aggregation=variables.VariableAggregation.NONE, - **kwargs): - if self._is_graph_network: - raise NotImplementedError('`add_weight` is not supported on Networks.') - else: - raise NotImplementedError( - '`add_weight` is not supported on Networks. However, you may ' - 'assign variables to attributes and they will show up in the weights ' - 'and variables properties.') - @property def stateful(self): - return any([(hasattr(layer, 'stateful') and layer.stateful) - for layer in self.layers]) + return any((hasattr(layer, 'stateful') and layer.stateful) + for layer in self.layers) def reset_states(self): for layer in self.layers: @@ -557,14 +501,13 @@ class Network(base_layer.Layer): @property def _unfiltered_updates(self): - if context.executing_eagerly(): - return [] updates = [] for layer in self.layers: if isinstance(layer, Network): updates += layer._unfiltered_updates else: updates += layer.updates + updates += self._updates return updates @property @@ -641,9 +584,6 @@ class Network(base_layer.Layer): Returns: A list of update ops. """ - if context.executing_eagerly(): - return [] - if not self.trainable and not self.stateful: return [] @@ -659,7 +599,7 @@ class Network(base_layer.Layer): else: relevant_inputs.append(inputs) if not relevant_inputs: - return updates + return list(set(updates)) reachable = tf_utils.get_reachable_from_inputs(relevant_inputs, updates) relevant_conditional_updates = [x for x in updates if x in reachable] @@ -667,8 +607,7 @@ class Network(base_layer.Layer): x for x in updates if x._unconditional_update] # pylint: disable=protected-access # A layer could be used multiple times in a nested structure, # so the updates list must be de-duped. - return list(set( - relevant_conditional_updates + unconditional_updates + self._updates)) + return list(set(relevant_conditional_updates + unconditional_updates)) @property def losses(self): @@ -728,14 +667,38 @@ class Network(base_layer.Layer): return checkpointable_layer_utils.gather_trainable_weights( trainable=self.trainable, sub_layers=self._layers, - extra_variables=self._extra_variables) + extra_variables=self._trainable_weights) @property def non_trainable_weights(self): return checkpointable_layer_utils.gather_non_trainable_weights( trainable=self.trainable, sub_layers=self._layers, - extra_variables=self._extra_variables) + extra_variables=self._non_trainable_weights + self._trainable_weights) + + @property + def metrics(self): + """Returns the network's symbolic metrics. + + Model overrides this function to include the metrics from `compile` API. + """ + metrics = [] + for layer in self.layers: + metrics += layer._metrics # pylint: disable=protected-access + return metrics + self._metrics + + @property + def _all_metrics_tensors(self): + """Returns the network's symbolic metric tensors.""" + # TODO(psv): Remove this property. + metrics_tensors = {} + for layer in self.layers: + if isinstance(layer, Network): + metrics_tensors.update(layer._all_metrics_tensors) + else: + metrics_tensors.update(layer._metrics_tensors) + metrics_tensors.update(self._metrics_tensors) + return metrics_tensors @property def input_spec(self): @@ -771,6 +734,11 @@ class Network(base_layer.Layer): This is to be used for subclassed models, which do not know at instantiation time what their inputs look like. + This method only exists for users who want to call `model.build()` in a + standalone way (as a substitute for calling the model on real data to + build it). It will never be called by the framework (and thus it will + never throw unexpected errors in an unrelated workflow). + Args: input_shape: Single tuple, TensorShape, or list of shapes, where shapes are tuples, integers, or TensorShapes. @@ -807,48 +775,53 @@ class Network(base_layer.Layer): # in a Graph. Since tf.Variable is compatible with both eager execution # and graph building, the variables created after building the model in # a Graph are still valid when executing eagerly. - with context.graph_mode(): - graph = func_graph.FuncGraph('graph') - with graph.as_default(): - if isinstance(input_shape, list): - x = [base_layer.generate_placeholders_from_shape(shape) - for shape in input_shape] + if context.executing_eagerly(): + graph = func_graph.FuncGraph('build_graph') + else: + graph = backend.get_graph() + with graph.as_default(): + if isinstance(input_shape, list): + x = [base_layer_utils.generate_placeholders_from_shape(shape) + for shape in input_shape] + else: + x = base_layer_utils.generate_placeholders_from_shape(input_shape) + + kwargs = {} + call_signature = tf_inspect.getfullargspec(self.call) + call_args = call_signature.args + # Exclude `self`, `inputs`, and any argument with a default value. + if len(call_args) > 2: + if call_signature.defaults: + call_args = call_args[2:-len(call_signature.defaults)] else: - x = base_layer.generate_placeholders_from_shape(input_shape) - - kwargs = {} - num_call_args = len(tf_inspect.getfullargspec(self.call).args) - if self._expects_training_arg and num_call_args == 3: - # Has call signature of call(self, input, training) - kwargs['training'] = False - elif num_call_args > 2: - # Has invalid call signature of call(self, input, *args, **kwargs) - raise ValueError('Currently, you cannot build your model if it has ' - 'positional or keyword arguments that are not ' - 'inputs to the model, but are required for its ' - '`call` method. Instead, in order to instantiate ' - 'and build your model, `call` your model on real ' - 'tensor data with all expected call arguments.') - - try: - self.call(x, **kwargs) - except (errors.InvalidArgumentError, TypeError): - raise ValueError('You cannot build your model by calling `build` ' - 'if your layers do not support float type inputs. ' - 'Instead, in order to instantiate and build your ' - 'model, `call` your model on real tensor data (of ' - 'the correct dtype).') - + call_args = call_args[2:] + for arg in call_args: + if arg == 'training': + # Case where `training` is a positional arg with no default. + kwargs['training'] = False + else: + # Has invalid call signature with unknown positional arguments. + raise ValueError( + 'Currently, you cannot build your model if it has ' + 'positional or keyword arguments that are not ' + 'inputs to the model, but are required for its ' + '`call` method. Instead, in order to instantiate ' + 'and build your model, `call` your model on real ' + 'tensor data with all expected call arguments.') + elif len(call_args) < 2: + # Signature without `inputs`. + raise ValueError('You can only call `build` on a model if its `call` ' + 'method accepts an `inputs` argument.') + try: + self.call(x, **kwargs) + except (errors.InvalidArgumentError, TypeError): + raise ValueError('You cannot build your model by calling `build` ' + 'if your layers do not support float type inputs. ' + 'Instead, in order to instantiate and build your ' + 'model, `call` your model on real tensor data (of ' + 'the correct dtype).') if self._layers: self._track_layers(self._layers) - if self.layers: - for layer in self.layers: - if not layer.built: - raise ValueError('Layer: {} was not built in your model. Calling ' - '`build` manually on a subclassed model is only ' - 'allowed for models with a static topology. ' - 'In this case, you can build your model by ' - 'calling it on real tensor data.'.format(layer)) self.built = True def call(self, inputs, training=None, mask=None): @@ -895,9 +868,7 @@ class Network(base_layer.Layer): def compute_output_shape(self, input_shape): if not self._is_graph_network: - if context.executing_eagerly(): - return super(Network, self).compute_output_shape(input_shape) - raise NotImplementedError + return super(Network, self).compute_output_shape(input_shape) if isinstance(input_shape, list): input_shapes = [] @@ -1686,6 +1657,62 @@ class Network(base_layer.Layer): positions=positions, print_fn=print_fn) + def _validate_graph_inputs_and_outputs(self): + """Validates the inputs and outputs of a Graph Network.""" + # Check for redundancy in inputs. + if len(set(self.inputs)) != len(self.inputs): + raise ValueError('The list of inputs passed to the model ' + 'is redundant. ' + 'All inputs should only appear once.' + ' Found: ' + str(self.inputs)) + + for x in self.inputs: + # Check that x has appropriate `_keras_history` metadata. + if not hasattr(x, '_keras_history'): + cls_name = self.__class__.__name__ + raise ValueError('Input tensors to a ' + cls_name + ' ' + + 'must come from `tf.keras.Input`. ' + 'Received: ' + str(x) + + ' (missing previous layer metadata).') + # Check that x is an input tensor. + # pylint: disable=protected-access + layer, _, _ = x._keras_history + if len(layer._inbound_nodes) > 1 or ( + layer._inbound_nodes and layer._inbound_nodes[0].inbound_layers): + cls_name = self.__class__.__name__ + logging.warning(cls_name + ' inputs must come from ' + '`tf.keras.Input` (thus holding past layer metadata), ' + 'they cannot be the output of ' + 'a previous non-Input layer. ' + 'Here, a tensor specified as ' + 'input to "' + self.name + '" was not an Input tensor, ' + 'it was generated by layer ' + layer.name + '.\n' + 'Note that input tensors are ' + 'instantiated via `tensor = tf.keras.Input(shape)`.\n' + 'The tensor that caused the issue was: ' + str(x.name)) + + # Check compatibility of batch sizes of Input Layers. + input_batch_sizes = [ + training_utils.get_static_batch_size(x._keras_history[0]) + for x in self.inputs + ] + consistent_batch_size = None + for batch_size in input_batch_sizes: + if batch_size is not None: + if (consistent_batch_size is not None and + batch_size != consistent_batch_size): + raise ValueError('The specified batch sizes of the Input Layers' + ' are incompatible. Found batch sizes: {}'.format( + input_batch_sizes)) + consistent_batch_size = batch_size + + for x in self.outputs: + if not hasattr(x, '_keras_history'): + cls_name = self.__class__.__name__ + raise ValueError('Output tensors to a ' + cls_name + ' must be ' + 'the output of a TensorFlow `Layer` ' + '(thus holding past layer metadata). Found: ' + str(x)) + def _is_hdf5_filepath(filepath): return (filepath.endswith('.h5') or filepath.endswith('.keras') or diff --git a/tensorflow/python/keras/engine/saving.py b/tensorflow/python/keras/engine/saving.py index 61bff7fff2..54d9e32fb2 100644 --- a/tensorflow/python/keras/engine/saving.py +++ b/tensorflow/python/keras/engine/saving.py @@ -79,6 +79,10 @@ def save_model(model, filepath, overwrite=True, include_optimizer=True): from tensorflow.python.keras import __version__ as keras_version # pylint: disable=g-import-not-at-top + # TODO(psv) Add warning when we save models that contain non-serializable + # entities like metrics added using `add_metric` and losses added using + # `add_loss.` + if not isinstance(filepath, h5py.File): # If file exists and should not be overwritten. if not overwrite and os.path.isfile(filepath): @@ -126,8 +130,8 @@ def save_model(model, filepath, overwrite=True, include_optimizer=True): 'config': model.optimizer.get_config() }, 'loss': model.loss, - 'metrics': model.metrics, - 'weighted_metrics': model.weighted_metrics, + 'metrics': model._compile_metrics, + 'weighted_metrics': model._compile_weighted_metrics, 'sample_weight_mode': model.sample_weight_mode, 'loss_weights': model.loss_weights, }, @@ -913,7 +917,7 @@ def save_attributes_to_hdf5_group(group, name, data): chunked_data = np.array_split(data_npy, num_chunks) # This will never loop forever thanks to the test above. - while any([x.nbytes > HDF5_OBJECT_HEADER_LIMIT for x in chunked_data]): + while any(x.nbytes > HDF5_OBJECT_HEADER_LIMIT for x in chunked_data): num_chunks += 1 chunked_data = np.array_split(data_npy, num_chunks) diff --git a/tensorflow/python/keras/engine/saving_test.py b/tensorflow/python/keras/engine/saving_test.py index f376f081cf..6d9d9a2fca 100644 --- a/tensorflow/python/keras/engine/saving_test.py +++ b/tensorflow/python/keras/engine/saving_test.py @@ -32,6 +32,7 @@ from tensorflow.python.framework import ops from tensorflow.python.framework import test_util from tensorflow.python.keras.engine import saving from tensorflow.python.keras.engine import training +from tensorflow.python.lib.io import file_io from tensorflow.python.ops import array_ops from tensorflow.python.ops import random_ops from tensorflow.python.platform import test @@ -288,6 +289,7 @@ class TestWeightSavingAndLoading(test.TestCase, parameterized.TestCase): r'element\(s\)\.'): saving.load_weights_from_hdf5_group_by_name(f_model, model.layers) + @test_util.run_deprecated_v1 def test_sequential_weight_loading_group_name_with_incorrect_shape(self): if h5py is None: return @@ -330,6 +332,7 @@ class TestWeightSavingAndLoading(test.TestCase, parameterized.TestCase): class TestWholeModelSaving(test.TestCase): + @test_util.run_deprecated_v1 def test_sequential_model_saving(self): if h5py is None: self.skipTest('h5py required to run this test') @@ -382,6 +385,7 @@ class TestWholeModelSaving(test.TestCase): out2 = new_model.predict(x) self.assertAllClose(out, out2, atol=1e-05) + @test_util.run_deprecated_v1 def test_sequential_model_saving_without_input_shape(self): if h5py is None: self.skipTest('h5py required to run this test') @@ -442,6 +446,7 @@ class TestWholeModelSaving(test.TestCase): out2 = new_model.predict(x) self.assertAllClose(out, out2, atol=1e-05) + @test_util.run_deprecated_v1 def test_sequential_model_saving_2(self): if h5py is None: self.skipTest('h5py required to run this test') @@ -478,6 +483,7 @@ class TestWholeModelSaving(test.TestCase): out2 = model.predict(x) self.assertAllClose(out, out2, atol=1e-05) + @test_util.run_deprecated_v1 def test_functional_model_saving(self): if h5py is None: self.skipTest('h5py required to run this test') @@ -629,6 +635,7 @@ class TestWholeModelSaving(test.TestCase): os.close(fd) os.remove(fname) + @test_util.run_deprecated_v1 def test_saving_model_with_long_weights_names(self): if h5py is None: self.skipTest('h5py required to run this test') @@ -674,6 +681,7 @@ class TestWholeModelSaving(test.TestCase): os.close(fd) os.remove(fname) + @test_util.run_deprecated_v1 def test_model_saving_to_pre_created_h5py_file(self): if h5py is None: self.skipTest('h5py required to run this test') @@ -715,7 +723,6 @@ class TestWholeModelSaving(test.TestCase): os.close(fd) os.remove(fname) - def test_saving_constant_initializer_with_numpy(self): if h5py is None: self.skipTest('h5py required to run this test') @@ -749,6 +756,7 @@ class SubclassedModel(training.Model): class TestWeightSavingAndLoadingTFFormat(test.TestCase): + @test_util.run_deprecated_v1 def test_keras_optimizer_warning(self): graph = ops.Graph() with graph.as_default(), self.session(graph): @@ -992,5 +1000,57 @@ class TestWeightSavingAndLoadingTFFormat(test.TestCase): AssertionError, 'Nothing except the root object matched'): m.load_weights(save_path) + @test_util.run_in_graph_and_eager_modes + def test_directory_passed(self): + m = keras.Model() + v = m.add_weight(name='v', shape=[]) + self.evaluate(v.assign(42.)) + prefix = os.path.join(self.get_temp_dir(), '{}'.format(ops.uid()), 'ckpt/') + m.save_weights(prefix) + self.evaluate(v.assign(2.)) + m.load_weights(prefix) + self.assertEqual(42., self.evaluate(v)) + + @test_util.run_in_graph_and_eager_modes + def test_relative_path(self): + m = keras.Model() + v = m.add_weight(name='v', shape=[]) + os.chdir(self.get_temp_dir()) + + prefix = 'ackpt' + self.evaluate(v.assign(42.)) + m.save_weights(prefix) + self.assertTrue(file_io.file_exists('ackpt.index')) + self.evaluate(v.assign(1.)) + m.load_weights(prefix) + self.assertEqual(42., self.evaluate(v)) + + prefix = 'subdir/ackpt' + self.evaluate(v.assign(43.)) + m.save_weights(prefix) + self.assertTrue(file_io.file_exists('subdir/ackpt.index')) + self.evaluate(v.assign(2.)) + m.load_weights(prefix) + self.assertEqual(43., self.evaluate(v)) + + prefix = 'ackpt/' + self.evaluate(v.assign(44.)) + m.save_weights(prefix) + self.assertTrue(file_io.file_exists('ackpt/.index')) + self.evaluate(v.assign(3.)) + m.load_weights(prefix) + self.assertEqual(44., self.evaluate(v)) + + @test_util.run_in_graph_and_eager_modes + def test_nonexistant_prefix_directory(self): + m = keras.Model() + v = m.add_weight(name='v', shape=[]) + self.evaluate(v.assign(42.)) + prefix = os.path.join(self.get_temp_dir(), '{}'.format(ops.uid()), 'bckpt') + m.save_weights(prefix) + self.evaluate(v.assign(2.)) + m.load_weights(prefix) + self.assertEqual(42., self.evaluate(v)) + if __name__ == '__main__': test.main() diff --git a/tensorflow/python/keras/engine/sequential.py b/tensorflow/python/keras/engine/sequential.py index 5ce4ca4df4..3255613f6a 100644 --- a/tensorflow/python/keras/engine/sequential.py +++ b/tensorflow/python/keras/engine/sequential.py @@ -25,6 +25,7 @@ from tensorflow.python.eager import context from tensorflow.python.framework import ops from tensorflow.python.keras import layers as layer_module from tensorflow.python.keras.engine import base_layer +from tensorflow.python.keras.engine import training_utils from tensorflow.python.keras.engine.input_layer import Input from tensorflow.python.keras.engine.input_layer import InputLayer from tensorflow.python.keras.engine.network import Network @@ -120,8 +121,8 @@ class Sequential(Model): return layers[:] @property - def _is_static_graph_friendly(self): - return all(layer._is_static_graph_friendly for layer in self.layers) + def _static_graph_friendly(self): + return all(layer._static_graph_friendly for layer in self.layers) @checkpointable.no_automatic_dependency_tracking def add(self, layer): @@ -150,7 +151,7 @@ class Sequential(Model): assert len(layer._inbound_nodes[-1].output_tensors) == 1 set_inputs = True else: - batch_shape, dtype = get_input_shape_and_dtype(layer) + batch_shape, dtype = training_utils.get_input_shape_and_dtype(layer) if batch_shape: # Instantiate an input layer. x = Input( @@ -190,8 +191,6 @@ class Sequential(Model): self._layers.append(layer) if self._layers: self._track_layers(self._layers) - self._can_use_graph_functions = all( - layer._can_use_graph_functions for layer in self.layers) @checkpointable.no_automatic_dependency_tracking def pop(self): @@ -213,23 +212,17 @@ class Sequential(Model): self.outputs = [self.layers[-1].output] self._init_graph_network(self.inputs, self.outputs, name=self.name) self.built = True - self._can_use_graph_functions = all( - layer._can_use_graph_functions for layer in self.layers) + @base_layer.default def build(self, input_shape=None): if self._is_graph_network: self._init_graph_network(self.inputs, self.outputs, name=self.name) else: if input_shape is None: raise ValueError('You must provide an `input_shape` argument.') + input_shape = tuple(input_shape) self._build_input_shape = input_shape - shape = input_shape - for layer in self.layers: - if not layer.built: - with ops.name_scope(layer._name_scope()): - layer.build(shape) - layer.built = True - shape = layer.compute_output_shape(shape) + super(Sequential, self).build(input_shape) self.built = True def call(self, inputs, training=None, mask=None): @@ -241,8 +234,8 @@ class Sequential(Model): return outputs def _call_and_compute_mask(self, inputs, training=None, mask=None): - if not self.built: - self.build(inputs.shape) + if not self.built and self._is_graph_network: + self._init_graph_network(self.inputs, self.outputs, name=self.name) x = inputs for layer in self.layers: @@ -255,6 +248,11 @@ class Sequential(Model): if isinstance(layer, Network) and layer._compute_output_and_mask_jointly: x, mask = layer._call_and_compute_mask(x, **kwargs) else: + if not layer.built: + # Build layer if applicable. + with ops.name_scope(layer._name_scope()): + layer._maybe_build(x) + layer.built = True x = layer.call(x, **kwargs) if layer.supports_masking: mask = layer.compute_mask(x, mask) @@ -362,38 +360,3 @@ class Sequential(Model): if self.layers and hasattr(self.layers[0], 'input_spec'): return self.layers[0].input_spec return None - - -def get_input_shape_and_dtype(layer): - """Retrieve input shape and input dtype of layer if applicable. - - Args: - layer: Layer (or model) instance. - - Returns: - Tuple (input_shape, input_dtype). Both could be None if the layer - does not have a defined input shape. - - Raises: - ValueError: in case an empty Sequential or Graph Network is passed. - """ - if ((isinstance(layer, Model) and layer._is_graph_network) - or isinstance(layer, Sequential)): - # We were passed a model as first layer. - # This requires a specific way to figure out the - # input shape and dtype. - if not layer.layers: - raise ValueError('Cannot add an empty model ' - 'to a `Sequential` model.') - # In case of nested models: recover the first layer - # of the deepest model to infer input shape and dtype. - layer = layer.layers[0] - while ((isinstance(layer, Model) and layer._is_graph_network) - or isinstance(layer, Sequential)): - layer = layer.layers[0] - - if hasattr(layer, '_batch_input_shape'): - batch_shape = layer._batch_input_shape - dtype = layer.dtype - return batch_shape, dtype - return None, None diff --git a/tensorflow/python/keras/engine/sequential_test.py b/tensorflow/python/keras/engine/sequential_test.py index ea8fdf675a..b6d2510897 100644 --- a/tensorflow/python/keras/engine/sequential_test.py +++ b/tensorflow/python/keras/engine/sequential_test.py @@ -124,7 +124,7 @@ class TestSequential(test.TestCase, parameterized.TestCase): dataset = dataset_ops.Dataset.from_tensor_slices((x, y)) dataset = dataset.repeat(100) dataset = dataset.batch(10) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) model.fit(iterator, epochs=1, steps_per_epoch=steps_per_epoch) self.assertTrue(model.built) @@ -132,6 +132,7 @@ class TestSequential(test.TestCase, parameterized.TestCase): self.assertFalse(model._is_graph_network) @parameterized.parameters((True,), (False,)) + @tf_test_util.run_deprecated_v1 def test_training_and_eval_methods_on_symbolic_tensors(self, deferred): with self.cached_session(): @@ -219,6 +220,7 @@ class TestSequential(test.TestCase, parameterized.TestCase): inner_model.trainable = True self.assertEqual(len(model.trainable_weights), 4) + @tf_test_util.run_deprecated_v1 def test_sequential_update_disabling(self): val_a = np.random.random((10, 4)) val_out = np.random.random((10, 4)) @@ -294,7 +296,6 @@ class TestSequential(test.TestCase, parameterized.TestCase): model.build((None, 10)) self.assertTrue(model.built) - self.assertTrue(model.layers[-1].built) self.assertEqual(len(model.weights), 8) @tf_test_util.run_in_graph_and_eager_modes @@ -362,29 +363,6 @@ class TestSequentialEagerIntegration(test.TestCase): y = np.random.random((2, 5)) model.fit(x, y, epochs=1) - @tf_test_util.run_in_graph_and_eager_modes - def test_sequential_can_use_graph_functions(self): - model = testing_utils.get_small_sequential_mlp(4, 3) - self.assertTrue(model._can_use_graph_functions) - inner_model = testing_utils.get_small_sequential_mlp(4, 5) - model.add(inner_model) - - self.assertTrue(model._can_use_graph_functions) - - inner_model_two = testing_utils.get_small_sequential_mlp(5, 7) - self.assertTrue(inner_model_two._can_use_graph_functions) - - layer = keras.layers.Lambda(lambda x: x) - layer._can_use_graph_functions = False - inner_model_two.add(layer) - self.assertFalse(inner_model_two._can_use_graph_functions) - - model.add(inner_model_two) - self.assertFalse(model._can_use_graph_functions) - - model.pop() - self.assertTrue(model._can_use_graph_functions) - @tf_test_util.run_in_graph_and_eager_modes def test_sequential_model_fails_with_dict_inputs(self): num_classes = 5 diff --git a/tensorflow/python/keras/engine/topology_test.py b/tensorflow/python/keras/engine/topology_test.py index b4a4babf25..03bfd35589 100644 --- a/tensorflow/python/keras/engine/topology_test.py +++ b/tensorflow/python/keras/engine/topology_test.py @@ -42,6 +42,7 @@ except ImportError: class TopologyConstructionTest(test.TestCase): + @test_util.run_deprecated_v1 def test_get_updates(self): class MyLayer(keras.layers.Layer): @@ -115,6 +116,7 @@ class TopologyConstructionTest(test.TestCase): self.assertEqual(len(layer.get_updates_for(x1)), 2) self.assertEqual(len(layer.get_updates_for(None)), 0) + @test_util.run_deprecated_v1 def test_get_losses(self): class MyLayer(keras.layers.Layer): @@ -268,6 +270,7 @@ class TopologyConstructionTest(test.TestCase): self.assertEqual(test_layer.input_shape, [(None, 32), (None, 32)]) self.assertEqual(test_layer.output_shape, (None, 32)) + @test_util.run_deprecated_v1 def testBasicNetwork(self): # minimum viable network x = input_layer_lib.Input(shape=(32,)) @@ -341,6 +344,7 @@ class TopologyConstructionTest(test.TestCase): self.assertListEqual(model.trainable_weights, []) self.assertListEqual(model.non_trainable_weights, weights) + @test_util.run_deprecated_v1 def test_layer_call_arguments(self): # Test the ability to pass and serialize arguments to `call`. inp = keras.layers.Input(shape=(2,)) @@ -491,6 +495,7 @@ class TopologyConstructionTest(test.TestCase): fn_outputs = fn([input_a_np, input_b_np]) self.assertListEqual([x.shape for x in fn_outputs], [(10, 64), (10, 5)]) + @test_util.run_deprecated_v1 def test_recursion(self): with self.cached_session(): a = keras.layers.Input(shape=(32,), name='input_a') @@ -675,6 +680,7 @@ class TopologyConstructionTest(test.TestCase): with self.assertRaises(Exception): keras.models.Model([j, k], [m, n, 0]) + @test_util.run_deprecated_v1 def test_raw_tf_compatibility(self): # test calling layers/models on TF tensors a = keras.layers.Input(shape=(32,), name='input_a') @@ -719,6 +725,7 @@ class TopologyConstructionTest(test.TestCase): model = keras.models.Model(a, b) self.assertEqual(model.output_mask.get_shape().as_list(), [None, 10]) + @test_util.run_deprecated_v1 def testMaskingSingleInput(self): class MaskedLayer(keras.layers.Layer): @@ -756,6 +763,7 @@ class TopologyConstructionTest(test.TestCase): y_2 = network(x_2) self.assertEqual(y_2.get_shape().as_list(), [None, 32]) + @test_util.run_deprecated_v1 def test_activity_regularization_with_model_composition(self): def reg(x): @@ -825,6 +833,7 @@ class TopologyConstructionTest(test.TestCase): output_val_2 = m2.predict(x_val) self.assertAllClose(output_val, output_val_2, atol=1e-6) + @test_util.run_deprecated_v1 def test_explicit_training_argument(self): with self.cached_session(): a = keras.layers.Input(shape=(2,)) @@ -1145,6 +1154,7 @@ class DefaultShapeInferenceBehaviorTest(test.TestCase): class GraphUtilsTest(test.TestCase): + @test_util.run_deprecated_v1 def testGetReachableFromInputs(self): with self.cached_session(): diff --git a/tensorflow/python/keras/engine/training.py b/tensorflow/python/keras/engine/training.py index cb96e3e5d2..2236bcf27c 100644 --- a/tensorflow/python/keras/engine/training.py +++ b/tensorflow/python/keras/engine/training.py @@ -18,6 +18,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import collections import weakref import numpy as np @@ -26,6 +27,7 @@ from tensorflow.python.data.ops import iterator_ops from tensorflow.python.eager import context from tensorflow.python.framework import errors from tensorflow.python.framework import ops +from tensorflow.python.framework import tensor_shape from tensorflow.python.framework import tensor_util from tensorflow.python.keras import backend as K from tensorflow.python.keras import losses @@ -40,6 +42,8 @@ from tensorflow.python.keras.engine import training_utils from tensorflow.python.keras.engine.network import Network from tensorflow.python.keras.utils import data_utils from tensorflow.python.keras.utils.generic_utils import slice_arrays +from tensorflow.python.keras.utils.losses_utils import squeeze_or_expand_dimensions +from tensorflow.python.ops import math_ops from tensorflow.python.platform import tf_logging as logging from tensorflow.python.training import optimizer as tf_optimizer_module from tensorflow.python.training.checkpointable import base as checkpointable @@ -121,11 +125,8 @@ class Model(Network): # initializing _distribution_strategy here since it is possible to call # predict on a model without compiling it. self._distribution_strategy = None - # This flag must be disabled upon model mutation, such as changing the model - # layers or recompiling the model to use a different optimizer. New function - # definitions are generated whenever this flag is disabled, ensuring that - # internal graph functions are always using the current model structure. - self._built_graph_functions = False + + self.run_eagerly = None def _set_sample_weight_attributes(self, sample_weight_mode, skip_target_weighing_indices): @@ -177,25 +178,66 @@ class Model(Network): metric_name = '%s_%s' % (self.output_names[output_index], metric_name) j = 1 base_metric_name = metric_name - while metric_name in self.metrics_names: + while metric_name in self._compile_metrics_names: metric_name = '%s_%d' % (base_metric_name, j) j += 1 return metric_name + @property + def metrics(self): + """Returns the model's metrics added using `compile`, `add_metric` APIs.""" + metrics = [] + if self._is_compiled: + metrics += self._compile_stateful_metric_functions + return metrics + super(Model, self).metrics + + @property + def metrics_names(self): + """Returns the model's display labels for all outputs.""" + metrics_names = [] + if self._is_compiled: + metrics_names += self._compile_metrics_names # Includes names of losses. + + # Add metric names from layers. + for layer in self.layers: + metrics_names += [m.name for m in layer._metrics] # pylint: disable=protected-access + metrics_names += [m.name for m in self._metrics] + return metrics_names + + @property + def _all_metrics_tensors(self): + """Returns the network's symbolic metric tensors.""" + metrics_tensors = {} + if self._is_compiled: + metrics_tensors.update(self._compile_metrics_tensors) + metrics_tensors.update(super(Model, self)._all_metrics_tensors) + return metrics_tensors + + @property + def _all_stateful_metrics_tensors(self): + """Returns the network's symbolic metric tensors.""" + metrics_tensors = {} + if self._is_compiled: + metrics_tensors.update(self._compile_stateful_metrics_tensors) + metrics_tensors.update(super(Model, self)._all_metrics_tensors) + return metrics_tensors + def _init_metric_attributes(self): """Initialized model metric attributes.""" # List of all metric names in the model. - self.metrics_names = ['loss'] - # List of all aggregated metric result tensors. This includes aggregated - # loss result tensors. - self._stateful_metrics_tensors = [] - # List of all metric result tensors (aggregated or not - based on the - # values given in compile.) - self.metrics_tensors = [] + self._compile_metrics_names = ['loss'] # List of stateful metric functions. Used for resetting metric state during - # training/eval. This includes loss functions. - self.stateful_metric_functions = [] + # training/eval. + # This includes loss functions when there are multiple outputs. + self._compile_stateful_metric_functions = [] + # Dict of all aggregated metric result tensors. This includes aggregated + # loss result tensors when there are multiple outputs. + self._compile_stateful_metrics_tensors = {} + # Dict of all metric result tensors (aggregated or not - based on the + # values given in compile.). This includes aggregated loss result tensors + # when there are multiple outputs. + self._compile_metrics_tensors = {} def _set_per_output_metric_attributes(self, metrics_dict, output_index): """Sets the metric attributes on the model for the given output. @@ -204,24 +246,39 @@ class Model(Network): metrics_dict: A dict with metric names as keys and metric fns as values. output_index: The index of the model output for which the metric attributes are added. + + Returns: + Metrics dict updated with unique metric names as keys. """ - for metric_name, (_, stateful_metric_fn) in metrics_dict.items(): + updated_metrics_dict = collections.OrderedDict() + for metric_name, (metric_fn, stateful_metric_fn) in metrics_dict.items(): metric_name = self._add_unique_metric_name(metric_name, output_index) - # Keep track of metric name. - self.metrics_names.append(metric_name) - - # Keep track of stateful metric function. - self.stateful_metric_functions.append(stateful_metric_fn) + updated_metrics_dict[metric_name] = (metric_fn, stateful_metric_fn) + # Keep track of metric name, function and stateful function. + self._compile_metrics_names.append(metric_name) + self._compile_stateful_metric_functions.append(stateful_metric_fn) + return updated_metrics_dict def _set_metric_attributes(self, outputs, skip_target_indices=None): """Sets the metric attributes on the model for all the model outputs.""" skip_target_indices = skip_target_indices or [] + updated_per_output_metrics = [] + updated_per_output_weighted_metrics = [] for i in range(len(outputs)): if i in skip_target_indices: + updated_per_output_metrics.append(self._per_output_metrics[i]) + updated_per_output_weighted_metrics.append( + self._per_output_weighted_metrics[i]) continue - self._set_per_output_metric_attributes(self._per_output_metrics[i], i) - self._set_per_output_metric_attributes( - self._per_output_weighted_metrics[i], i) + updated_per_output_metrics.append( + self._set_per_output_metric_attributes(self._per_output_metrics[i], + i)) + updated_per_output_weighted_metrics.append( + self._set_per_output_metric_attributes( + self._per_output_weighted_metrics[i], i)) + + self._per_output_metrics = updated_per_output_metrics + self._per_output_weighted_metrics = updated_per_output_weighted_metrics def _handle_per_output_metrics(self, metrics_dict, @@ -256,17 +313,17 @@ class Model(Network): weighted_metric_fn = training_utils.weighted_masked_objective(fn) return weighted_metric_fn(y_true, y_pred, weights=weights, mask=mask) - def _track_metric_tensors(stateless_result, stateful_result): - self.metrics_tensors.append(stateless_result) - self._stateful_metrics_tensors.append(stateful_result) + def _track_metric_tensors(name, stateless_result, stateful_result): + self._compile_metrics_tensors[name] = stateless_result + self._compile_stateful_metrics_tensors[name] = stateful_result if isinstance(metric_fn, metrics_module.Metric): # If the given metric fn is stateful, call the fn and return result. metric_result = _call_stateful_fn(metric_fn) metric_results.append(metric_result) - if not context.executing_eagerly(): - _track_metric_tensors(metric_result, metric_result) - elif context.executing_eagerly(): + if not self.run_eagerly: + _track_metric_tensors(metric_name, metric_result, metric_result) + elif self.run_eagerly: # In eager mode, if the given metric fn is not stateful, we invoke the # given fn or its stateful version based on the given flag. if return_stateful_result: @@ -279,7 +336,8 @@ class Model(Network): # stateless fns. stateful_metric_result = _call_stateful_fn(stateful_fn) metric_result = _call_stateless_fn(metric_fn) - _track_metric_tensors(metric_result, stateful_metric_result) + _track_metric_tensors(metric_name, metric_result, + stateful_metric_result) return metric_results @@ -307,6 +365,7 @@ class Model(Network): skip_target_indices = skip_target_indices or [] metric_results = [] with K.name_scope('metrics'): + # Invoke all metrics added using `compile`. for i in range(len(outputs)): if i in skip_target_indices: continue @@ -328,8 +387,48 @@ class Model(Network): output_mask, weights=sample_weights[i], return_stateful_result=return_stateful_result)) + + # Add metric results from the `add_metric` metrics in eager mode. + if context.executing_eagerly(): + for m in self.metrics: + if m not in self._compile_stateful_metric_functions: + metric_results.append(m.result()) return metric_results + @property + def run_eagerly(self): + """Settable attribute indicating whether the model should run eagerly. + + Running eagerly means that your model will be run step by step, + like Python code. Your model might run slower, but it should become easier + for you to debug it by stepping into individual layer calls. + + By default, we will attempt to compile your model to a static graph to + deliver the best execution performance. + + Returns: + Boolean, whether the model should run eagerly. + """ + if self._run_eagerly is True and not context.executing_eagerly(): + raise ValueError('You can only set `run_eagerly=True` if eager execution ' + 'is enabled.') + if self._static_graph_friendly: + if self._run_eagerly is None: + return False + else: + return self._run_eagerly + else: + if self._run_eagerly is False: + # TODO(fchollet): consider using py_func to enable this. + raise ValueError('Your model contains layers that can only be ' + 'successfully run in eager execution. ' + 'You cannot set `run_eagerly=False`.') + return context.executing_eagerly() + + @run_eagerly.setter + def run_eagerly(self, value): + self._run_eagerly = value + @checkpointable.no_automatic_dependency_tracking def compile(self, optimizer, @@ -391,9 +490,8 @@ class Model(Network): ValueError: In case of invalid arguments for `optimizer`, `loss`, `metrics` or `sample_weight_mode`. """ - # The correct graph function may have changed, - # already-built ones must be updated - self._built_graph_functions = False + run_eagerly = kwargs.pop('run_eagerly', None) + self._run_eagerly = run_eagerly # Validate that arguments passed by the user to `compile` are supported by # DistributionStrategy. @@ -403,9 +501,6 @@ class Model(Network): raise NotImplementedError( 'optimizer must be an instance of ' 'tf.train.Optimizer, not a %s' % type(optimizer)) - if context.executing_eagerly(): - raise NotImplementedError('DistributionStrategy is not supported ' - 'when eager execution is enabled.') if sample_weight_mode: raise NotImplementedError('sample_weight_mode is not supported with ' 'DistributionStrategy.') @@ -417,11 +512,12 @@ class Model(Network): 'DistributionStrategy.') loss = loss or {} - if context.executing_eagerly() and not isinstance( + if self.run_eagerly and not isinstance( optimizer, (tf_optimizer_module.Optimizer, optimizers.TFOptimizer)): raise ValueError( - 'optimizer must be an instance of tf.train.Optimizer, not ' - 'a %s' % type(optimizer)) + 'When running a model in eager execution, the optimizer must be an ' + 'instance of tf.train.Optimizer. Received: ' + '%s' % optimizer) self.optimizer = optimizers.get(optimizer) # We've disabled automatic dependency tracking for this method, but do want @@ -430,12 +526,14 @@ class Model(Network): self._track_checkpointable( self.optimizer, name='optimizer', overwrite=True) self.loss = loss - self.metrics = metrics or [] + self._compile_metrics = metrics or [] self.loss_weights = loss_weights self.sample_weight_mode = sample_weight_mode - self.weighted_metrics = weighted_metrics - if context.executing_eagerly() and target_tensors is not None: - raise ValueError('target_tensors is not supported in Eager mode.') + self._compile_weighted_metrics = weighted_metrics + if self.run_eagerly and target_tensors is not None: + raise ValueError( + 'target_tensors argument is not supported when ' + 'running a model eagerly.') self.target_tensors = target_tensors # Set DistributionStrategy specific parameters. @@ -445,6 +543,8 @@ class Model(Network): if self._distribution_strategy is not None: distributed_training_utils.configure_and_create_session( self._distribution_strategy) + # Initialize model metric attributes. + self._init_metric_attributes() if not self.built: # Model is not compilable because it does not know its number of inputs # and outputs, nor their shapes and names. We will compile after the first @@ -468,16 +568,16 @@ class Model(Network): '" missing from loss dictionary. We assume ' 'this was done on purpose. The fit and evaluate APIs will not be ' 'expecting any data to be passed to "' + name + '".') - loss_functions.append(losses.get(loss.get(name))) + loss_functions.append(training_utils.get_loss_function(loss.get(name))) elif isinstance(loss, list): if len(loss) != len(self.outputs): raise ValueError('When passing a list as loss, ' 'it should have one entry per model outputs. ' 'The model has ' + str(len(self.outputs)) + ' outputs, but you passed loss=' + str(loss)) - loss_functions = [losses.get(l) for l in loss] + loss_functions = [training_utils.get_loss_function(l) for l in loss] else: - loss_function = losses.get(loss) + loss_function = training_utils.get_loss_function(loss) loss_functions = [loss_function for _ in range(len(self.outputs))] self.loss_functions = loss_functions @@ -493,7 +593,7 @@ class Model(Network): skip_target_weighing_indices.append(i) # Prepare output masks. - if not context.executing_eagerly(): + if not self.run_eagerly: masks = [getattr(x, '_keras_mask', None) for x in self.outputs] if not isinstance(masks, list): masks = [masks] @@ -524,11 +624,8 @@ class Model(Network): str(loss_weights) + ' - expected a list of dicts.') self.loss_weights_list = loss_weights_list - # Initialize model metric attributes. - self._init_metric_attributes() - # Initialization for Eager mode execution. - if context.executing_eagerly(): + if self.run_eagerly: # Prepare sample weights. self._set_sample_weight_attributes(sample_weight_mode, skip_target_weighing_indices) @@ -541,7 +638,7 @@ class Model(Network): self.total_loss = None for i in range(len(self.outputs)): if len(self.outputs) > 1: - self.metrics_names.append(self.output_names[i] + '_loss') + self._compile_metrics_names.append(self.output_names[i] + '_loss') # Set metric attributes on model. self._set_metric_attributes( @@ -555,145 +652,167 @@ class Model(Network): self._collected_trainable_weights = self.trainable_weights return - # Prepare targets of model. - self.targets = [] - self._feed_targets = [] - if target_tensors not in (None, []): - if isinstance(target_tensors, list): - if len(target_tensors) != len(self.outputs): - raise ValueError( - 'When passing a list as `target_tensors`, ' - 'it should have one entry per model output. ' - 'The model has ' + str(len(self.outputs)) + - ' outputs, but you passed target_tensors=' + str(target_tensors)) - elif isinstance(target_tensors, dict): - for name in target_tensors: - if name not in self.output_names: + with K.get_graph().as_default(): + # Prepare targets of model. + self.targets = [] + self._feed_targets = [] + if target_tensors not in (None, []): + if isinstance(target_tensors, list): + if len(target_tensors) != len(self.outputs): raise ValueError( - 'Unknown entry in `target_tensors` ' - 'dictionary: "' + name + '". ' - 'Only expected the following keys: ' + str(self.output_names)) - tmp_target_tensors = [] - for name in self.output_names: - tmp_target_tensors.append(target_tensors.get(name, None)) - target_tensors = tmp_target_tensors - elif tensor_util.is_tensor(target_tensors): - target_tensors = [target_tensors] - else: - raise TypeError('Expected `target_tensors` to be a list or tuple or ' - 'dict or a single tensor, but got:', target_tensors) - - for i in range(len(self.outputs)): - if i in skip_target_indices: - self.targets.append(None) - else: - shape = K.int_shape(self.outputs[i]) - name = self.output_names[i] - if target_tensors not in (None, []): - target = target_tensors[i] - else: - target = None - if target is None or K.is_placeholder(target): - if target is None: - target = K.placeholder( - ndim=len(shape), - name=name + '_target', - sparse=K.is_sparse(self.outputs[i]), - dtype=K.dtype(self.outputs[i])) - self._feed_targets.append(target) - self._feed_outputs.append(self.outputs[i]) - self._feed_output_names.append(name) - self._feed_output_shapes.append(shape) - self._feed_loss_fns.append(self.loss_functions[i]) + 'When passing a list as `target_tensors`, ' + 'it should have one entry per model output. ' + 'The model has %s outputs, but you passed target_tensors=%s' % + (len(self.outputs), target_tensors)) + elif isinstance(target_tensors, dict): + for name in target_tensors: + if name not in self.output_names: + raise ValueError( + 'Unknown entry in `target_tensors` ' + 'dictionary: "' + name + '". ' + 'Only expected the following keys: ' + str(self.output_names)) + tmp_target_tensors = [] + for name in self.output_names: + tmp_target_tensors.append(target_tensors.get(name, None)) + target_tensors = tmp_target_tensors + elif tensor_util.is_tensor(target_tensors): + target_tensors = [target_tensors] else: - skip_target_weighing_indices.append(i) - self.targets.append(target) - - # Prepare sample weights. - self._set_sample_weight_attributes(sample_weight_mode, - skip_target_weighing_indices) - # Save all metric attributes per output of the model. - self._cache_output_metric_attributes(metrics, weighted_metrics) - - # Compute total loss. - total_loss = None - with K.name_scope('loss'): + raise TypeError('Expected `target_tensors` to be a list or tuple or ' + 'dict or a single tensor, but got:', target_tensors) + for i in range(len(self.outputs)): if i in skip_target_indices: - continue - y_true = self.targets[i] - y_pred = self.outputs[i] - loss_fn = loss_functions[i] - sample_weight = self.sample_weights[i] - mask = masks[i] - loss_weight = loss_weights_list[i] - with K.name_scope(self.output_names[i] + '_loss'): - weighted_loss = training_utils.weighted_masked_objective(loss_fn) - output_loss = weighted_loss(y_true, y_pred, sample_weight, mask) + self.targets.append(None) + else: + shape = K.int_shape(self.outputs[i]) + name = self.output_names[i] + if target_tensors not in (None, []): + target = target_tensors[i] + else: + target = None + if target is None or K.is_placeholder(target): + if target is None: + target_dtype = losses.LABEL_DTYPES_FOR_LOSSES.get( + self.loss_functions[i], + K.dtype(self.outputs[i])) + + target = K.placeholder( + ndim=len(shape), + name=name + '_target', + sparse=K.is_sparse(self.outputs[i]), + dtype=target_dtype) + self._feed_targets.append(target) + self._feed_outputs.append(self.outputs[i]) + self._feed_output_names.append(name) + self._feed_output_shapes.append(shape) + self._feed_loss_fns.append(self.loss_functions[i]) + else: + skip_target_weighing_indices.append(i) + self.targets.append(target) - if len(self.outputs) > 1: - # Keep track of the un-aggregated loss result tensor. - self.metrics_tensors.append(output_loss) - - # Keep track of stateful result tensor and function for the loss. - mean_wrapped_loss = metrics_module.MeanMetricWrapper( - loss_fn, name=loss_fn.__name__) - result_tensor = training_utils.call_metric_function( - mean_wrapped_loss, - y_true, - y_pred, - weights=sample_weight, - mask=mask) - self._stateful_metrics_tensors.append(result_tensor) - self.stateful_metric_functions.append(mean_wrapped_loss) - - self.metrics_names.append(self.output_names[i] + '_loss') + # Prepare sample weights. + self._set_sample_weight_attributes(sample_weight_mode, + skip_target_weighing_indices) + # Save all metric attributes per output of the model. + self._cache_output_metric_attributes(metrics, weighted_metrics) + + # Compute total loss. + total_loss = None + with K.name_scope('loss'): + for i in range(len(self.outputs)): + if i in skip_target_indices: + continue + y_true = self.targets[i] + y_pred = self.outputs[i] + loss_fn = loss_functions[i] + sample_weight = self.sample_weights[i] + mask = masks[i] + loss_weight = loss_weights_list[i] + with K.name_scope(self.output_names[i] + '_loss'): + if isinstance(loss_fn, losses.Loss): + if mask is not None: + mask = math_ops.cast(mask, y_pred.dtype) + # Update weights with mask. + if sample_weight is None: + sample_weight = mask + else: + # Update dimensions of weights to match with mask if possible. + mask, _, sample_weight = squeeze_or_expand_dimensions( + mask, None, sample_weight) + sample_weight *= mask + output_loss = loss_fn(y_true, y_pred, sample_weight=sample_weight) + else: + weighted_loss = training_utils.weighted_masked_objective(loss_fn) + output_loss = weighted_loss(y_true, y_pred, sample_weight, mask) + + if len(self.outputs) > 1: + # Keep track of the un-aggregated loss result tensor. + self._compile_metrics_tensors[self.output_names[i] + + '_loss'] = output_loss + + # Keep track of stateful result tensor and function for the loss. + loss_name = loss_fn.name if isinstance( + loss_fn, losses.Loss) else loss_fn.__name__ + mean_wrapped_loss = metrics_module.MeanMetricWrapper( + loss_fn, name=loss_name) + result_tensor = training_utils.call_metric_function( + mean_wrapped_loss, + y_true, + y_pred, + weights=sample_weight, + mask=mask) + self._compile_stateful_metrics_tensors[self.output_names[i] + + '_loss'] = result_tensor + self._compile_stateful_metric_functions.append(mean_wrapped_loss) + + self._compile_metrics_names.append(self.output_names[i] + '_loss') + if total_loss is None: + total_loss = loss_weight * output_loss + else: + total_loss += loss_weight * output_loss if total_loss is None: - total_loss = loss_weight * output_loss - else: - total_loss += loss_weight * output_loss - if total_loss is None: - if not self.losses: - raise ValueError('The model cannot be compiled ' - 'because it has no loss to optimize.') - else: - total_loss = 0. - - # Add regularization penalties - # and other layer-specific losses. - for loss_tensor in self.losses: - total_loss += loss_tensor - - # Set metric attributes on model. - self._set_metric_attributes( - self.outputs, - skip_target_indices=skip_target_indices, - ) - # Invoke metric functions for all the outputs. - self._handle_metrics( - self.outputs, - masks=masks, - targets=self.targets, - skip_target_indices=skip_target_indices, - sample_weights=self.sample_weights) - - # Prepare gradient updates and state updates. - self.total_loss = total_loss - - # Functions for train, test and predict will - # be compiled lazily when required. - # This saves time when the user is not using all functions. - self._function_kwargs = kwargs - - self._fit_function = None - self._eval_function = None - self.train_function = None - self.test_function = None - self.predict_function = None - - # Collected trainable weights, sorted in topological order. - trainable_weights = self.trainable_weights - self._collected_trainable_weights = trainable_weights + if not self.losses: + raise ValueError('The model cannot be compiled ' + 'because it has no loss to optimize.') + else: + total_loss = 0. + + # Add regularization penalties + # and other layer-specific losses. + for loss_tensor in self.losses: + total_loss += loss_tensor + + # Set metric attributes on model. + self._set_metric_attributes( + self.outputs, + skip_target_indices=skip_target_indices, + ) + # Invoke metric functions for all the outputs. + self._handle_metrics( + self.outputs, + masks=masks, + targets=self.targets, + skip_target_indices=skip_target_indices, + sample_weights=self.sample_weights) + + # Prepare gradient updates and state updates. + self.total_loss = total_loss + + # Functions for train, test and predict will + # be compiled lazily when required. + # This saves time when the user is not using all functions. + self._function_kwargs = kwargs + + self._fit_function = None + self._eval_function = None + self.train_function = None + self.test_function = None + self.predict_function = None + + # Collected trainable weights, sorted in topological order. + trainable_weights = self.trainable_weights + self._collected_trainable_weights = trainable_weights def _check_trainable_weights_consistency(self): """Check trainable weights count consistency. @@ -721,21 +840,24 @@ class Model(Network): inputs = (self._feed_inputs + self._feed_targets + self._feed_sample_weights) - if not isinstance(K.learning_phase(), int): - inputs += [K.learning_phase()] + if not isinstance(K.symbolic_learning_phase(), int): + inputs += [K.symbolic_learning_phase()] + + with K.get_graph().as_default(): + with K.name_scope('training'): + with K.name_scope(self.optimizer.__class__.__name__): + # Training updates + updates = self.optimizer.get_updates( + params=self._collected_trainable_weights, loss=self.total_loss) + # Unconditional updates + updates += self.get_updates_for(None) + # Conditional updates relevant to this model + updates += self.get_updates_for(self.inputs) + # Add stateful metrics updates. + if metric_updates is not None: + updates += metric_updates with K.name_scope('training'): - with K.name_scope(self.optimizer.__class__.__name__): - # Training updates - updates = self.optimizer.get_updates( - params=self._collected_trainable_weights, loss=self.total_loss) - # Unconditional updates - updates += self.get_updates_for(None) - # Conditional updates relevant to this model - updates += self.get_updates_for(self.inputs) - # Add stateful metrics updates. - if metric_updates is not None: - updates += metric_updates # Gets loss and metrics. Updates weights at each call. fn = K.function( inputs, @@ -746,18 +868,24 @@ class Model(Network): setattr(self, fn_name, fn) def _make_train_function(self): + metrics_tensors = [ + self._all_metrics_tensors[m] for m in self.metrics_names[1:] + ] self._make_train_function_helper('train_function', - [self.total_loss] + self.metrics_tensors) + [self.total_loss] + metrics_tensors) def _make_fit_function(self): # TODO(psv/anjalisridhar): Remove updates after we fix b/118841692 # Stateful metrics updates metric_updates = [] - for m in self.stateful_metric_functions: + for m in self.metrics: metric_updates += m.updates + + metrics_tensors = [ + self._all_stateful_metrics_tensors[m] for m in self.metrics_names[1:] + ] self._make_train_function_helper( - '_fit_function', [self.total_loss] + self._stateful_metrics_tensors, - metric_updates) + '_fit_function', [self.total_loss] + metrics_tensors, metric_updates) def _make_test_function_helper(self, fn_name, outputs, metric_updates=None): if not hasattr(self, fn_name): @@ -766,49 +894,53 @@ class Model(Network): inputs = (self._feed_inputs + self._feed_targets + self._feed_sample_weights) - if not isinstance(K.learning_phase(), int): - inputs += [K.learning_phase()] - updates = self.state_updates - # Add stateful metrics updates. - if metric_updates is not None: - updates += metric_updates - # Return loss and metrics, no gradient updates. - # Does update the network states. - fn = K.function( - inputs, - outputs, - updates=updates, - name='test_function', - **self._function_kwargs) - setattr(self, fn_name, fn) + + with K.name_scope('evaluation'): + updates = self.state_updates + # Add stateful metrics updates. + if metric_updates is not None: + updates += metric_updates + # Return loss and metrics, no gradient updates. + # Does update the network states. + fn = K.function( + inputs, + outputs, + updates=updates, + name='test_function', + **self._function_kwargs) + setattr(self, fn_name, fn) def _make_test_function(self): + metrics_tensors = [ + self._all_metrics_tensors[m] for m in self.metrics_names[1:] + ] self._make_test_function_helper('test_function', - [self.total_loss] + self.metrics_tensors) + [self.total_loss] + metrics_tensors) def _make_eval_function(self): - self._make_test_function_helper( - '_eval_function', [self.total_loss] + self._stateful_metrics_tensors) + metrics_tensors = [ + self._all_stateful_metrics_tensors[m] for m in self.metrics_names[1:] + ] + self._make_test_function_helper('_eval_function', + [self.total_loss] + metrics_tensors) def _make_predict_function(self): if not hasattr(self, 'predict_function'): self.predict_function = None if self.predict_function is None: - if not isinstance(K.learning_phase(), int): - inputs = self._feed_inputs + [K.learning_phase()] - else: - inputs = self._feed_inputs + inputs = self._feed_inputs # Gets network outputs. Does not update weights. # Does update the network states. kwargs = getattr(self, '_function_kwargs', {}) - self.predict_function = K.function( - inputs, - self.outputs, - updates=self.state_updates, - name='predict_function', - **kwargs) + with K.name_scope('predict'): + self.predict_function = K.function( + inputs, + self.outputs, + updates=self.state_updates, + name='predict_function', + **kwargs) - def _get_execution_function(self, mode): + def _make_execution_function(self, mode): if mode == 'train': self._make_fit_function() return self._fit_function @@ -873,7 +1005,8 @@ class Model(Network): 'when using DistributionStrategy.') if (sample_weight is not None and sample_weight.all() and - self._distribution_strategy.__class__.__name__ == 'TPUStrategy'): + distributed_training_utils.is_tpu_strategy( + self._distribution_strategy)): raise NotImplementedError('`sample_weight` is currently not supported ' 'when using TPUStrategy.') @@ -882,18 +1015,13 @@ class Model(Network): # TODO(anjalisridhar): Remove this check once we refactor the # _standardize_user_data code path. This check is already present elsewhere # in the codebase. - if check_steps and isinstance(x, dataset_ops.Dataset) and steps is None: + if check_steps and isinstance(x, dataset_ops.DatasetV2) and steps is None: raise ValueError('When using Datasets as input, ' 'you should specify the `{steps_name}` argument.' .format(steps_name=steps_name)) first_x_value = nest.flatten(x)[0] if isinstance(first_x_value, np.ndarray): - assert steps is not None - x_shape = first_x_value.shape - if batch_size is None: - batch_size = distributed_training_utils.get_batch_size( - self._distribution_strategy.num_replicas_in_sync, x_shape[0], steps) # We need to use the drop_remainder argument to allow for a static # input shape which is required for TPUs. drop_remainder = self._distribution_strategy.require_static_shapes @@ -928,19 +1056,15 @@ class Model(Network): var_x = distributed_training_utils.get_var_for_numpy( self._distribution_strategy, x) x = dataset_ops.Dataset.from_tensor_slices(var_x) - x = x.repeat() x = x.batch(batch_size, drop_remainder=drop_remainder) - assert isinstance(x, dataset_ops.Dataset) + assert isinstance(x, dataset_ops.DatasetV2) - # TODO(anjalisridhar): We want distribute_dataset() to accept a Dataset or a - # function which returns a Dataset. Currently distribute_dataset() only - # accepts a function that returns a Dataset. Once we add support for being - # able to clone a Dataset on multiple workers we can remove this lambda. - result = self._distribution_strategy.distribute_dataset(lambda: x) - iterator = result.make_initializable_iterator() with self._distribution_strategy.scope(): - K.get_session().run(iterator.initializer) + iterator = self._distribution_strategy.make_dataset_iterator(x) + init_op = iterator.initialize() + if not context.executing_eagerly(): + K.get_session().run(init_op) training_utils.validate_iterator_input(x, y, sample_weight, validation_split) @@ -1025,14 +1149,14 @@ class Model(Network): shuffle=shuffle) return iterator, None, None - if isinstance(x, dataset_ops.Dataset): + if isinstance(x, dataset_ops.DatasetV2): if context.executing_eagerly(): - x = x.make_one_shot_iterator() + x = iter(x) else: if x in self._dataset_iterator_cache: x = self._dataset_iterator_cache[x] else: - iterator = x.make_initializable_iterator() + iterator = dataset_ops.make_initializable_iterator(x) self._dataset_iterator_cache[x] = iterator x = iterator K.get_session().run(x.initializer) @@ -1052,7 +1176,7 @@ class Model(Network): # For eager iterators, when we have to process multiple batches of samples, # we will standardize the data when we actually loop over iterator and get # the batches. For now, we just return the iterator as is. - if is_x_eager_iterator and steps is not None: + if is_x_eager_iterator: return x, y, sample_weight # If input data is a dataset iterator in graph mode or if it is an eager @@ -1096,6 +1220,8 @@ class Model(Network): all_inputs = [] is_build_called = False is_compile_called = False + # Whether this is a subclassed model that expects dictionary inputs + # rather than list inputs (e.g. FeatureColumn-based models). dict_inputs = False if not self.inputs: # We need to use `x` to set the model inputs. @@ -1122,9 +1248,16 @@ class Model(Network): # to match the value shapes. if not self.inputs: is_build_called = True - self._set_inputs(x) + cast_inputs = x + if training_utils.has_tensors(x): + cast_inputs = training_utils.cast_if_floating_dtype(x) + self._set_inputs(cast_inputs) else: dict_inputs = isinstance(self.inputs, dict) + if dict_inputs and context.executing_eagerly(): + # No support for graph functions when the model expects dictionary inputs + # (i.e. FeatureColumn-based models). + self.run_eagerly = True if y is not None: if not self.optimizer: @@ -1134,6 +1267,8 @@ class Model(Network): if not self._is_compiled: # On-the-fly compilation of the model. # We need to use `y` to set the model targets. + if training_utils.has_tensors(y): + y = training_utils.cast_if_floating_dtype(y) if isinstance(y, (list, tuple)): if not all(isinstance(v, np.ndarray) or tensor_util.is_tensor(v) for v in y): @@ -1158,19 +1293,22 @@ class Model(Network): 'TensorFlow tensors. ' 'You passed: x=' + str(x) + '; y=' + str(y)) - if context.executing_eagerly(): + if self.run_eagerly: target_tensors = None else: # Handle target tensors if any passed. if not isinstance(y, (list, tuple)): y = [y] - target_tensors = [v for v in y if tensor_util.is_tensor(v)] + target_tensors = [v for v in y if _is_symbolic_tensor(v)] is_compile_called = True - self.compile(optimizer=self.optimizer, - loss=self.loss, - metrics=self.metrics, - loss_weights=self.loss_weights, - target_tensors=target_tensors) + self.compile( + optimizer=self.optimizer, + loss=self.loss, + metrics=self._compile_metrics, + weighted_metrics=self._compile_weighted_metrics, + loss_weights=self.loss_weights, + target_tensors=target_tensors, + run_eagerly=self.run_eagerly) # In graph mode, if we had just set inputs and targets as symbolic tensors # by invoking build and compile on the model respectively, we do not have to @@ -1178,15 +1316,15 @@ class Model(Network): # part of the graph. # Note: in this case, `any` and `all` are equivalent since we disallow # mixed symbolic/value inputs. - if (not context.executing_eagerly() and is_build_called and + if (not self.run_eagerly and is_build_called and is_compile_called and - any(tensor_util.is_tensor(v) for v in all_inputs)): + any(_is_symbolic_tensor(v) for v in all_inputs)): return [], [], [] # What follows is input validation and standardization to list format, # in the case where all inputs are value arrays. - if context.executing_eagerly(): + if self.run_eagerly: # In eager mode, do not do shape validation # since the network has no input nodes (placeholders) to be fed. feed_input_names = self.input_names @@ -1242,7 +1380,9 @@ class Model(Network): y = training_utils.standardize_input_data( y, feed_output_names, - feed_output_shapes, + # Don't enforce target shapes to match output shapes. + # Precise checks will be run in `check_loss_and_target_compatibility`. + shapes=None, check_batch_axis=False, # Don't enforce the batch size. exception_prefix='target') @@ -1260,7 +1400,7 @@ class Model(Network): # Check that all arrays have the same length. if not self._distribution_strategy: training_utils.check_array_lengths(x, y, sample_weights) - if self._is_graph_network and not context.executing_eagerly(): + if self._is_graph_network and not self.run_eagerly: # Additional checks to avoid users mistakenly using improper loss fns. training_utils.check_loss_and_target_compatibility( y, self._feed_loss_fns, feed_output_shapes) @@ -1315,8 +1455,7 @@ class Model(Network): if self.__class__.__name__ == 'Sequential' and not self.built: if tensor_util.is_tensor(inputs): - input_shape = (None,) + tuple(inputs.get_shape().as_list()[1:]) - self.build(input_shape=input_shape) + input_shape = (None,) + tuple(inputs.shape.as_list()[1:]) elif isinstance(inputs, dict): # We assert that the first layer is a FeatureLayer. if not training_utils.is_feature_layer(self.layers[0]): @@ -1324,10 +1463,9 @@ class Model(Network): 'which doesn\'t have FeatureLayer as the first layer' ' is an error.') input_shape = (None,) - self.build(input_shape=input_shape) else: - input_shape = (None,) + inputs.shape[1:] - self.build(input_shape=input_shape) + input_shape = (None,) + tuple(inputs.shape[1:]) + self._build_input_shape = input_shape # On-the-fly setting of symbolic model inputs (either by using the tensor # provided, or by creating a placeholder if Numpy data was provided). @@ -1346,10 +1484,11 @@ class Model(Network): self._feed_input_names.append(k) self._feed_input_shapes.append(K.int_shape(v)) + # TODO(fchollet): consider calling `_maybe_build` before calling the model. + if outputs is None: # Obtain symbolic outputs by calling the model. - graph = K.get_graph() - with graph.as_default(): + with K.get_graph().as_default(): if self._expects_training_arg: outputs = self.call(inputs, training=training) else: @@ -1509,7 +1648,6 @@ class Model(Network): """ # TODO(fchollet): this method may be creating reference cycles, which would # lead to accumulating garbage in memory when called in a loop. Investigate. - if data_utils.is_generator_or_sequence(x): training_utils.check_generator_arguments(y, sample_weight) return self.fit_generator( @@ -1527,9 +1665,6 @@ class Model(Network): shuffle=shuffle, initial_epoch=initial_epoch) - # Backwards compatibility - if batch_size is None and steps_per_epoch is None: - batch_size = 32 # Legacy support if 'nb_epoch' in kwargs: logging.warning( @@ -1541,15 +1676,21 @@ class Model(Network): # Validate and standardize user data. if self._distribution_strategy: - distributed_training_utils.validate_callbacks(callbacks) + distributed_training_utils.validate_callbacks(callbacks, self.optimizer, + self._distribution_strategy) distributed_training_utils.validate_inputs( x, y, self._distribution_strategy) first_x_value = nest.flatten(x)[0] - if not steps_per_epoch and isinstance(first_x_value, np.ndarray): - steps_per_epoch = distributed_training_utils.get_input_batch_params( - first_x_value, batch_size, self._distribution_strategy) + if isinstance(first_x_value, np.ndarray): + steps_per_epoch, batch_size = ( + distributed_training_utils.get_input_params( + self._distribution_strategy, first_x_value, steps_per_epoch, + batch_size, is_training=True)) + + batch_size = self._validate_or_infer_batch_size(batch_size, steps_per_epoch, + x) x, y, sample_weights = self._standardize_user_data( x, @@ -1567,7 +1708,7 @@ class Model(Network): if validation_data: if (isinstance(validation_data, iterator_ops.Iterator) or isinstance(validation_data, iterator_ops.EagerIterator) or - isinstance(validation_data, dataset_ops.Dataset)): + isinstance(validation_data, dataset_ops.DatasetV2)): val_x = validation_data val_y = None val_sample_weight = None @@ -1590,9 +1731,10 @@ class Model(Network): distributed_training_utils.validate_inputs( val_x, val_y, self._distribution_strategy) first_valx_value = nest.flatten(val_x)[0] - if not validation_steps and isinstance(first_valx_value, np.ndarray): - validation_steps = distributed_training_utils.get_input_batch_params( - first_valx_value, batch_size, self._distribution_strategy) + if isinstance(first_valx_value, np.ndarray): + validation_steps, _ = distributed_training_utils.get_input_params( + self._distribution_strategy, first_valx_value, validation_steps, + batch_size) val_x, val_y, val_sample_weights = self._standardize_user_data( val_x, @@ -1622,27 +1764,25 @@ class Model(Network): val_y = None val_sample_weights = None - if context.executing_eagerly(): - return training_eager.fit_loop( - self, - inputs=x, - targets=y, - sample_weights=sample_weights, - class_weight=class_weight, + if (self.run_eagerly or (isinstance(x, iterator_ops.EagerIterator) and + not self._distribution_strategy)): + return training_generator.fit_generator( + self, (x, y, sample_weights), + steps_per_epoch=steps_per_epoch, batch_size=batch_size, epochs=epochs, + shuffle=shuffle, verbose=verbose, callbacks=callbacks, - val_inputs=val_x, - val_targets=val_y, - val_sample_weights=val_sample_weights, - shuffle=shuffle, - initial_epoch=initial_epoch, - steps_per_epoch=steps_per_epoch, - validation_steps=validation_steps) - elif self._distribution_strategy: - return training_distributed.fit_loop( - self, x, + validation_data=validation_data, + validation_steps=validation_steps, + workers=0, + initial_epoch=initial_epoch) + elif distributed_training_utils.is_tpu_strategy( + self._distribution_strategy): + return training_distributed.experimental_fit_loop( + self, + x, epochs=epochs, verbose=verbose, callbacks=callbacks, @@ -1757,19 +1897,16 @@ class Model(Network): max_queue_size=max_queue_size, workers=workers, use_multiprocessing=use_multiprocessing) - - # Backwards compatibility. - if batch_size is None and steps is None: - batch_size = 32 - # Validate and standardize user data. if self._distribution_strategy: distributed_training_utils.validate_inputs( x, y, self._distribution_strategy) first_x_value = nest.flatten(x)[0] - if isinstance(first_x_value, np.ndarray) and not steps: - steps = distributed_training_utils.get_input_batch_params( - first_x_value, batch_size, self._distribution_strategy) + if isinstance(first_x_value, np.ndarray): + steps, batch_size = distributed_training_utils.get_input_params( + self._distribution_strategy, first_x_value, steps, batch_size) + + batch_size = self._validate_or_infer_batch_size(batch_size, steps, x) x, y, sample_weights = self._standardize_user_data( x, @@ -1780,21 +1917,18 @@ class Model(Network): steps_name='steps', steps=steps) - if context.executing_eagerly(): - return training_eager.test_loop( - self, - inputs=x, - targets=y, - sample_weights=sample_weights, + if (self.run_eagerly or (isinstance(x, iterator_ops.EagerIterator) and + not self._distribution_strategy)): + return training_generator.evaluate_generator( + self, (x, y, sample_weights), + steps=steps, batch_size=batch_size, verbose=verbose, - steps=steps) - elif self._distribution_strategy: - return training_distributed.test_loop( - self, - iterator=x, - verbose=verbose, - steps=steps) + workers=0) + elif distributed_training_utils.is_tpu_strategy( + self._distribution_strategy): + return training_distributed.experimental_test_loop( + self, iterator=x, verbose=verbose, steps=steps) else: return training_arrays.test_loop( self, @@ -1868,37 +2002,57 @@ class Model(Network): max_queue_size=max_queue_size, workers=workers, use_multiprocessing=use_multiprocessing) - - # Backwards compatibility. - if batch_size is None and steps is None: - batch_size = 32 - if self._distribution_strategy: distributed_training_utils.validate_inputs( x, None, self._distribution_strategy) first_x_value = nest.flatten(x)[0] - if isinstance(first_x_value, np.ndarray) and not steps: - steps = distributed_training_utils.get_input_batch_params( - first_x_value, batch_size, self._distribution_strategy) + if isinstance(first_x_value, np.ndarray): + steps, batch_size = distributed_training_utils.get_input_params( + self._distribution_strategy, first_x_value, steps, batch_size) - # Validate and standardize user data. - # TODO(anjalisridhar): We don't pass batch_size here for some reason. This - # means that we end up calculating it twice which we should avoid. - x, _, _ = self._standardize_user_data( - x, check_steps=True, steps_name='steps', steps=steps) + batch_size = self._validate_or_infer_batch_size(batch_size, steps, x) - if context.executing_eagerly(): - return training_eager.predict_loop( - self, x, batch_size=batch_size, verbose=verbose, steps=steps) - elif self._distribution_strategy: - results = training_distributed.predict_loop( + # Validate and standardize user data. + if self._distribution_strategy: + x, _, _ = self._standardize_user_data( + x, check_steps=True, steps_name='steps', steps=steps, + batch_size=batch_size) + else: + # TODO(anjalisridhar): We don't pass batch_size here for some reason. This + # means we need to special case distribution strategy which needs the + # batch size. + x, _, _ = self._standardize_user_data( + x, check_steps=True, steps_name='steps', steps=steps) + + if (self.run_eagerly or (isinstance(x, iterator_ops.EagerIterator) and + not self._distribution_strategy)): + return training_generator.predict_generator( + self, + x, + steps=steps, + batch_size=batch_size, + verbose=verbose, + workers=0) + elif distributed_training_utils.is_tpu_strategy( + self._distribution_strategy): + return training_distributed.experimental_predict_loop( self, x, verbose=verbose, steps=steps) - return results else: return training_arrays.predict_loop( self, x, batch_size=batch_size, verbose=verbose, steps=steps) - def train_on_batch(self, x, y=None, sample_weight=None, class_weight=None): + def reset_metrics(self): + """Resets the state of metrics.""" + if hasattr(self, 'metrics'): + for m in self.metrics: + m.reset_states() + + def train_on_batch(self, + x, + y=None, + sample_weight=None, + class_weight=None, + reset_metrics=True): """Runs a single gradient update on a single batch of data. Arguments: @@ -1926,6 +2080,9 @@ class Model(Network): weight (float) to apply to the model's loss for the samples from this class during training. This can be useful to tell the model to "pay more attention" to samples from an under-represented class. + reset_metrics: If `True`, the metrics returned will be only for this + batch. If `False`, the metrics will be statefully accumulated across + batches. Returns: Scalar training loss @@ -1944,23 +2101,30 @@ class Model(Network): x, y, sample_weights = self._standardize_user_data( x, y, sample_weight=sample_weight, class_weight=class_weight) - if context.executing_eagerly(): + if self.run_eagerly: outputs = training_eager.train_on_batch( self, x, y, sample_weights=sample_weights) else: - if not isinstance(K.learning_phase(), int): - ins = x + y + sample_weights + [1] + if not isinstance(K.symbolic_learning_phase(), int): + ins = x + y + sample_weights + [True] else: ins = x + y + sample_weights - self._make_train_function() - outputs = self.train_function(ins) # pylint: disable=not-callable + if reset_metrics: + self._make_train_function() + outputs = self.train_function(ins) # pylint: disable=not-callable + else: + self._make_fit_function() + outputs = self._fit_function(ins) # pylint: disable=not-callable + + if reset_metrics: + self.reset_metrics() if len(outputs) == 1: return outputs[0] return outputs - def test_on_batch(self, x, y=None, sample_weight=None): + def test_on_batch(self, x, y=None, sample_weight=None, reset_metrics=True): """Test the model on a single batch of samples. Arguments: @@ -1986,6 +2150,9 @@ class Model(Network): In this case you should make sure to specify sample_weight_mode="temporal" in compile(). This argument is not supported when `x` is a dataset or a dataset iterator. + reset_metrics: If `True`, the metrics returned will be only for this + batch. If `False`, the metrics will be statefully accumulated across + batches. Returns: Scalar test loss (if the model has a single output and no metrics) @@ -2003,16 +2170,20 @@ class Model(Network): x, y, sample_weights = self._standardize_user_data( x, y, sample_weight=sample_weight) - if context.executing_eagerly(): + if self.run_eagerly: outputs = training_eager.test_on_batch( self, x, y, sample_weights=sample_weights) else: - if not isinstance(K.learning_phase(), int): - ins = x + y + sample_weights + [0] + inputs = x + y + sample_weights + if reset_metrics: + self._make_test_function() + outputs = self.test_function(inputs) # pylint: disable=not-callable else: - ins = x + y + sample_weights - self._make_test_function() - outputs = self.test_function(ins) # pylint: disable=not-callable + self._make_eval_function() + outputs = self._eval_function(inputs) # pylint: disable=not-callable + + if reset_metrics: + self.reset_metrics() if len(outputs) == 1: return outputs[0] @@ -2041,28 +2212,21 @@ class Model(Network): 'models compiled with DistributionStrategy.') # Validate and standardize user data. inputs, _, _ = self._standardize_user_data(x) - if context.executing_eagerly(): - if (isinstance(x, iterator_ops.EagerIterator) or - (isinstance(x, dataset_ops.Dataset) and context.executing_eagerly())): + if self.run_eagerly: + if (isinstance(inputs, iterator_ops.EagerIterator) or + (isinstance(inputs, dataset_ops.DatasetV2))): inputs = training_utils.cast_if_floating_dtype(inputs) - else: + elif isinstance(inputs, collections.Sequence): inputs = [ - ops.convert_to_tensor(val, dtype=K.floatx()) for val in inputs - ] + ops.convert_to_tensor(val, dtype=K.floatx()) for val in inputs] return self(inputs) # pylint: disable=not-callable - if not context.executing_eagerly(): - if not isinstance(K.learning_phase(), int): - ins = inputs + [0] - else: - ins = inputs - - self._make_predict_function() - outputs = self.predict_function(ins) + self._make_predict_function() + outputs = self.predict_function(inputs) - if len(outputs) == 1: - return outputs[0] - return outputs + if len(outputs) == 1: + return outputs[0] + return outputs def fit_generator(self, generator, @@ -2172,11 +2336,6 @@ class Model(Network): if self._distribution_strategy: raise NotImplementedError('`fit_generator` is not supported for ' 'models compiled with DistributionStrategy.') - - if not self.built and not self._is_graph_network: - raise NotImplementedError( - '`fit_generator` is not yet enabled for unbuilt Model subclasses') - return training_generator.fit_generator( self, generator, @@ -2243,12 +2402,6 @@ class Model(Network): if self._distribution_strategy: raise NotImplementedError('`evaluate_generator` is not supported for ' 'models compiled with DistributionStrategy.') - - if not self.built and not self._is_graph_network: - raise NotImplementedError( - '`evaluate_generator` is not yet enabled for ' - 'unbuilt Model subclasses') - return training_generator.evaluate_generator( self, generator, @@ -2300,11 +2453,6 @@ class Model(Network): if self._distribution_strategy: raise NotImplementedError('`predict_generator` is not supported for ' 'models compiled with DistributionStrategy.') - - if not self.built and not self._is_graph_network: - raise NotImplementedError( - '`predict_generator` is not yet enabled for unbuilt Model subclasses') - return training_generator.predict_generator( self, generator, @@ -2336,15 +2484,63 @@ class Model(Network): self._replicated_model = DistributedCallbackModel(first_replicated_model) self._replicated_model.set_original_model(self) + def _validate_or_infer_batch_size(self, batch_size, steps, x): + """Validates that the `batch_size` provided is consistent with InputLayer. + + It's possible that the user specified a static batch size in their + InputLayer. If so, this method checks the provided `batch_size` and `x` + arguments are consistent with this static batch size. Also, if + `batch_size` is `None`, this method will attempt to infer the batch size + from the static batch size of the InputLayer. + + Arguments: + batch_size: The batch_size provided as an argument to + fit/evaluate/predict. + steps: The steps provided as an argument to fit/evaluate/predict. + x: The data passed as `x` to fit/evaluate/predict. + + Returns: + The validated batch_size, auto-inferred from the first layer if not + provided. + """ + first_layer = super(Model, + self).layers[0] # Avoids the override in Sequential. + static_batch_size = training_utils.get_static_batch_size(first_layer) + if static_batch_size is not None: + + # Check `batch_size` argument is consistent with InputLayer. + if batch_size is not None and batch_size != static_batch_size: + raise ValueError('The `batch_size` argument value ' + str(batch_size) + + ' is incompatible with the specified batch size ' + 'of your Input Layer: ' + str(static_batch_size)) + + # Check Dataset/Iterator batch size is consistent with InputLayer. + if isinstance(x, (dataset_ops.DatasetV2, iterator_ops.Iterator, + iterator_ops.EagerIterator)): + ds_batch_size = tensor_shape.as_dimension( + nest.flatten(x.output_shapes)[0][0]).value + if ds_batch_size is not None and ds_batch_size != static_batch_size: + raise ValueError('The batch output shape of your `Dataset` is ' + + str(ds_batch_size) + ' which is incompatible ' + 'with the specified batch size of your Input ' + 'Layer: ' + str(static_batch_size)) + + # Set inferred batch size from the InputLayer. + if steps is None: + batch_size = static_batch_size + + if batch_size is None and steps is None: + # Backwards compatibility + batch_size = 32 + return batch_size + class DistributedCallbackModel(Model): """Model that is used for callbacks with DistributionStrategy.""" def __init__(self, model): super(DistributedCallbackModel, self).__init__() - # TODO(anjalisridhar): Right now the only attributes set are the layer and - # weights. We may need to set additional attributes as needed since we have - # not called compile on this model. + self.optimizer = model.optimizer def set_original_model(self, orig_model): self._original_model = orig_model @@ -2376,3 +2572,7 @@ class DistributedCallbackModel(Model): logging.warning('You are accessing attribute ' + item + ' of the ' 'DistributedCallbackModel that may not have been set ' 'correctly.') + + +def _is_symbolic_tensor(x): + return tensor_util.is_tensor(x) and not isinstance(x, ops.EagerTensor) diff --git a/tensorflow/python/keras/engine/training_arrays.py b/tensorflow/python/keras/engine/training_arrays.py index a2a13b9bd6..e9dfbcbcc0 100644 --- a/tensorflow/python/keras/engine/training_arrays.py +++ b/tensorflow/python/keras/engine/training_arrays.py @@ -23,9 +23,11 @@ import functools import numpy as np +from tensorflow.python.eager import context from tensorflow.python.framework import errors from tensorflow.python.keras import backend as K from tensorflow.python.keras import callbacks as cbks +from tensorflow.python.keras.engine import training_distributed from tensorflow.python.keras.engine import training_utils from tensorflow.python.keras.utils.generic_utils import make_batches from tensorflow.python.keras.utils.generic_utils import slice_arrays @@ -37,91 +39,6 @@ except ImportError: issparse = None -class Aggregator(object): - """Abstract base class used to aggregate batch-level outputs of a loop. - - Arguments: - use_steps: Whether the loop is using `step` or `batch_size`. - num_samples_or_steps: Either `batch_size*num_batches` or `steps`. - """ - - def __init__(self, use_steps, num_samples_or_steps): - self.use_steps = use_steps - self.num_samples_or_steps = num_samples_or_steps - self.results = [] - - def create(self, batch_outs): - """Create the initial results from the first batch outputs. - - Arguments: - batch_outs: A list of batch-level outputs. - """ - raise NotImplementedError - - def aggregate(self, batch_outs, batch_start=None, batch_end=None): - """Aggregate batch-level results into total results. - - Arguments: - batch_outs: A list of batch-level outputs. - batch_start: The start index of this batch. Always `None` if `use_steps` - is `True`. - batch_end: The end index of this batch. Always `None` if `use_steps` is - `True`. - """ - raise NotImplementedError - - def finalize(self): - """Prepare the total results to be returned.""" - raise NotImplementedError - - -class MetricsAggregator(Aggregator): - """Aggregator that calculates loss and metrics info.""" - - def create(self, batch_outs): - self.results = [0.] * len(batch_outs) - - def aggregate(self, batch_outs, batch_start=None, batch_end=None): - # Loss. - if self.use_steps: - self.results[0] += batch_outs[0] - else: - self.results[0] += batch_outs[0] * (batch_end - batch_start) - # Metrics (always stateful, just grab current values.) - self.results[1:] = batch_outs[1:] - - def finalize(self): - self.results[0] /= self.num_samples_or_steps - - -class OutputsAggregator(Aggregator): - """Aggregator that concatenates outputs.""" - - def create(self, batch_outs): - if self.use_steps: - # Cannot pre-allocate the returned NumPy arrays bc - # batch sizes are unknown. Concatenate batches at the end. - for _ in batch_outs: - self.results.append([]) - else: - # Pre-allocate NumPy arrays. - for batch_out in batch_outs: - shape = (self.num_samples_or_steps,) + batch_out.shape[1:] - self.results.append(np.zeros(shape, dtype=batch_out.dtype)) - - def aggregate(self, batch_outs, batch_start=None, batch_end=None): - if self.use_steps: - for i, batch_out in enumerate(batch_outs): - self.results[i].append(batch_out) - else: - for i, batch_out in enumerate(batch_outs): - self.results[i][batch_start:batch_end] = batch_out - - def finalize(self): - if self.use_steps: - self.results = [np.concatenate(result, axis=0) for result in self.results] - - def _get_model_feed(model, mode): if mode == 'predict': feed = model._feed_inputs @@ -151,13 +68,6 @@ def _print_train_info(inputs, val_inputs, steps_per_epoch, verbose): (inputs[0].shape[0], val_inputs[0].shape[0])) -def _get_progbar(model, count_mode): - stateful_metric_names = None - if hasattr(model, 'metrics_names'): - stateful_metric_names = model.metrics_names[1:] # Exclude `loss` - return cbks.ProgbarLogger(count_mode, stateful_metrics=stateful_metric_names) - - def _get_num_samples_or_steps(ins, batch_size, steps_per_epoch): """Returns total number of samples (when training in batch mode) or steps.""" if steps_per_epoch: @@ -166,16 +76,50 @@ def _get_num_samples_or_steps(ins, batch_size, steps_per_epoch): 'steps_per_epoch') -def _make_logs(model, outputs, mode, prefix=''): - """Used to make logs to send to `on_batch_end` methods.""" - logs = {} - # TODO(omalleyt): handle outputs in prediction when Callback - # hooks are ready. - if mode in ['train', 'test']: - if hasattr(model, 'metrics_names'): - for label, output in zip(model.metrics_names, outputs): - logs[prefix + label] = output - return logs +def _prepare_feed_values(model, inputs, targets, sample_weights, mode): + """Prepare feed values to the model execution function. + + Arguments: + model: Model to prepare feed values for. + inputs: List or dict of model inputs. + targets: Optional list of model targets. + sample_weights: Optional list of sample weight arrays. + mode: One of 'train'/'test'/'predict'. + + Returns: + Feed values for the model in the given mode. + """ + if model._distribution_strategy: + def get_distributed_inputs(): + return training_distributed._prepare_feed_values( + model, inputs, targets, sample_weights, mode) + + # In the eager case, we want to call the input method per step, so return + # a lambda from here that can be called. Note that this is applicable only + # in Distribution Strategy case as it follows the same code path for both + # eager and graph modes. + # TODO(priyag,omalleyt): Either we should move the training DS with + # EagerIterator to use training_generator code path, or figure out how to + # set a symbolic Iterator out of a Dataset when in eager mode. + if context.executing_eagerly(): + return get_distributed_inputs + else: + return get_distributed_inputs() + + inputs = training_utils.ModelInputs(inputs).as_list() + targets = targets or [] + sample_weights = sample_weights or [] + ins = inputs + targets + sample_weights + if mode == 'train' and not isinstance(K.symbolic_learning_phase(), int): + ins += [True] + return ins + + +def _make_execution_function(model, mode): + """Makes function to run one step of model execution.""" + if model._distribution_strategy: + return training_distributed._make_execution_function(model, mode) + return model._make_execution_function(mode) def model_iteration(model, @@ -238,19 +182,18 @@ def model_iteration(model, if mode == 'train': _print_train_info(inputs, val_inputs, steps_per_epoch, verbose) + # Enter DistributionStrategy scope. + if model._distribution_strategy: + scope = model._distribution_strategy.scope() + scope.__enter__() + # Get step function and loop type. - f = model._get_execution_function(mode) + f = _make_execution_function(model, mode) use_steps = steps_per_epoch is not None do_validation = val_inputs is not None # Prepare input data. - inputs = training_utils.ModelInputs(inputs).as_list() - targets = targets or [] - sample_weights = sample_weights or [] - learning_phase_input = [] - if not isinstance(K.learning_phase(), int): - learning_phase_input = [1] if mode == 'train' else [0] - ins = inputs + targets + sample_weights + learning_phase_input + ins = _prepare_feed_values(model, inputs, targets, sample_weights, mode) num_samples_or_steps = _get_num_samples_or_steps(ins, batch_size, steps_per_epoch) @@ -260,24 +203,19 @@ def model_iteration(model, callbacks, model, do_validation=do_validation, - val_inputs=val_inputs, - val_targets=val_targets, - val_sample_weights=val_sample_weights, batch_size=batch_size, epochs=epochs, steps_per_epoch=steps_per_epoch, samples=num_samples_or_steps, - validation_steps=validation_steps, verbose=0, # Handle ProgBarLogger separately in this loop. - count_mode=count_mode, mode=mode) # TODO(omalleyt): Handle ProgBar as part of Callbacks once hooks are ready. - progbar = _get_progbar(model, count_mode) + progbar = training_utils.get_progbar(model, count_mode) progbar.params = callbacks.params progbar.params['verbose'] = verbose # Find beforehand arrays that need sparse-to-dense conversion. - if issparse is not None: + if issparse is not None and not use_steps: indices_for_conversion_to_dense = [] feed = _get_model_feed(model, mode) for i, (input_data, feed_tensor) in enumerate(zip(ins, feed)): @@ -286,9 +224,14 @@ def model_iteration(model, # Select aggregation method. if mode == 'predict': - aggregator = OutputsAggregator(use_steps, num_samples_or_steps) + aggregator = training_utils.OutputsAggregator(use_steps, + num_samples_or_steps) else: - aggregator = MetricsAggregator(use_steps, num_samples_or_steps) + aggregator = training_utils.MetricsAggregator(use_steps, + num_samples_or_steps) + + if model._distribution_strategy: + training_distributed._copy_weights_to_distributed_model(model) callbacks.model.stop_training = False callbacks._call_begin_hook(mode) @@ -298,10 +241,9 @@ def model_iteration(model, break # Setup work for each epoch - results = [] epoch_logs = {} - if hasattr(model, 'stateful_metric_functions'): - for m in model.stateful_metric_functions: + if hasattr(model, 'metrics'): + for m in model.metrics: m.reset_states() callbacks.on_epoch_begin(epoch, epoch_logs, mode=mode) progbar.on_epoch_begin(epoch, epoch_logs) @@ -315,26 +257,31 @@ def model_iteration(model, # Get outputs. try: - batch_outs = f(ins) + # `ins` can be callable in DistributionStrategy + eager case. + actual_inputs = ins() if callable(ins) else ins + batch_outs = f(actual_inputs) except errors.OutOfRangeError: logging.warning('Your dataset iterator ran out of data; ' 'interrupting training. Make sure that your dataset ' 'can generate at least `steps_per_epoch * epochs` ' 'batches (in this case, %d batches). You may need to' 'use the repeat() function when building your ' - 'dataset.' % - steps_per_epoch * epochs) + 'dataset.' % steps_per_epoch * epochs) break if not isinstance(batch_outs, list): batch_outs = [batch_outs] + if model._distribution_strategy: + batch_outs = training_distributed._per_device_aggregate_batch( + batch_outs, model, mode) + # Aggregate results. if step == 0: aggregator.create(batch_outs) aggregator.aggregate(batch_outs) # Callbacks batch end. - batch_logs.update(_make_logs(model, batch_outs, mode)) + batch_logs.update(training_utils.make_logs(model, batch_outs, mode)) callbacks._call_batch_hook(mode, 'end', step, batch_logs) progbar.on_batch_end(step, batch_logs) @@ -365,8 +312,9 @@ def model_iteration(model, 'pass shuffle="batch".') # Sparse to dense conversion. - for i in indices_for_conversion_to_dense: - ins_batch[i] = ins_batch[i].toarray() + if issparse is not None: + for i in indices_for_conversion_to_dense: + ins_batch[i] = ins_batch[i].toarray() # Callbacks batch_begin. batch_logs = {'batch': batch_index, 'size': len(batch_ids)} @@ -384,7 +332,7 @@ def model_iteration(model, aggregator.aggregate(batch_outs, batch_start, batch_end) # Callbacks batch end. - batch_logs.update(_make_logs(model, batch_outs, mode)) + batch_logs.update(training_utils.make_logs(model, batch_outs, mode)) callbacks._call_batch_hook(mode, 'end', batch_index, batch_logs) progbar.on_batch_end(batch_index, batch_logs) @@ -393,7 +341,7 @@ def model_iteration(model, aggregator.finalize() results = aggregator.results - epoch_logs.update(_make_logs(model, results, mode)) + epoch_logs.update(training_utils.make_logs(model, results, mode)) if len(results) == 1: results = results[0] @@ -411,12 +359,17 @@ def model_iteration(model, mode='test') if not isinstance(val_results, list): val_results = [val_results] - epoch_logs.update(_make_logs(model, val_results, mode, prefix='val_')) + epoch_logs.update( + training_utils.make_logs(model, val_results, mode, prefix='val_')) callbacks.on_epoch_end(epoch, epoch_logs, mode=mode) progbar.on_epoch_end(epoch, epoch_logs) callbacks._call_end_hook(mode) + if model._distribution_strategy: + training_distributed._copy_weights_to_original_model(model, mode) + scope.__exit__(None, None, None) + if mode == 'train': return model.history return results diff --git a/tensorflow/python/keras/engine/training_dataset_test.py b/tensorflow/python/keras/engine/training_dataset_test.py new file mode 100644 index 0000000000..1b2d0e88e3 --- /dev/null +++ b/tensorflow/python/keras/engine/training_dataset_test.py @@ -0,0 +1,344 @@ +# Copyright 2018 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 training routines.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import logging + +from absl.testing import parameterized + +import numpy as np + +from tensorflow.python import keras +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util as tf_test_util +from tensorflow.python.keras import metrics as metrics_module +from tensorflow.python.keras import testing_utils +from tensorflow.python.ops.losses import losses_impl +from tensorflow.python.platform import test +from tensorflow.python.platform import tf_logging as logging +from tensorflow.python.training.rmsprop import RMSPropOptimizer + + +class TestTrainingWithDatasetIterators(test.TestCase, parameterized.TestCase): + + @parameterized.parameters( + {'model': 'functional'}, + {'model': 'subclass'}, + ) + @tf_test_util.run_in_graph_and_eager_modes + def test_training_and_eval_methods_on_iterators_single_io(self, model): + if model == 'functional': + model = testing_utils.get_small_functional_mlp(1, 4, input_dim=3) + elif model == 'subclass': + model = testing_utils.get_small_sequential_mlp(1, 4) + optimizer = RMSPropOptimizer(learning_rate=0.001) + loss = 'mse' + metrics = ['mae', metrics_module.CategoricalAccuracy()] + model.compile(optimizer, loss, metrics=metrics) + + inputs = np.zeros((10, 3), np.float32) + targets = np.zeros((10, 4), np.float32) + dataset = dataset_ops.Dataset.from_tensor_slices((inputs, targets)) + dataset = dataset.repeat(100) + dataset = dataset.batch(10) + iterator = dataset_ops.make_one_shot_iterator(dataset) + + model.fit(iterator, epochs=1, steps_per_epoch=2, verbose=1) + model.evaluate(iterator, steps=2, verbose=1) + model.predict(iterator, steps=2) + + # Test with validation data + model.fit(iterator, + epochs=1, steps_per_epoch=2, verbose=0, + validation_data=iterator, validation_steps=2) + # Test with validation split + with self.assertRaisesRegexp( + ValueError, '`validation_split` argument is not supported ' + 'when input `x` is a dataset or a dataset iterator'): + model.fit(iterator, + epochs=1, steps_per_epoch=2, verbose=0, + validation_split=0.5, validation_steps=2) + + # Test with sample weight. + sample_weight = np.random.random((10,)) + with self.assertRaisesRegexp( + ValueError, '`sample_weight` argument is not supported ' + 'when input `x` is a dataset or a dataset iterator'): + model.fit( + iterator, + epochs=1, + steps_per_epoch=2, + verbose=0, + sample_weight=sample_weight) + + # Test invalid usage + with self.assertRaisesRegexp(ValueError, + 'you should not specify a target'): + model.fit(iterator, iterator, + epochs=1, steps_per_epoch=2, verbose=0) + + with self.assertRaisesRegexp( + ValueError, 'you should specify the `steps_per_epoch` argument'): + model.fit(iterator, epochs=1, verbose=0) + with self.assertRaisesRegexp(ValueError, + 'you should specify the `steps` argument'): + model.evaluate(iterator, verbose=0) + with self.assertRaisesRegexp(ValueError, + 'you should specify the `steps` argument'): + model.predict(iterator, verbose=0) + + @tf_test_util.run_in_graph_and_eager_modes + def test_get_next_op_created_once(self): + model = testing_utils.get_small_functional_mlp(1, 4, input_dim=3) + optimizer = RMSPropOptimizer(learning_rate=0.001) + loss = 'mse' + metrics = ['mae'] + model.compile(optimizer, loss, metrics=metrics) + + inputs = np.zeros((10, 3), np.float32) + targets = np.zeros((10, 4), np.float32) + dataset = dataset_ops.Dataset.from_tensor_slices((inputs, targets)) + dataset = dataset.repeat(100) + dataset = dataset.batch(10) + iterator = dataset_ops.make_one_shot_iterator(dataset) + + model.fit(iterator, epochs=1, steps_per_epoch=2, verbose=1) + # Finalize graph to make sure we are not appending another iterator + # get_next op in the graph. + ops.get_default_graph().finalize() + model.fit(iterator, epochs=1, steps_per_epoch=2, verbose=1) + + @tf_test_util.run_in_graph_and_eager_modes + def test_iterators_running_out_of_data(self): + model = testing_utils.get_small_functional_mlp(1, 4, input_dim=3) + optimizer = RMSPropOptimizer(learning_rate=0.001) + loss = 'mse' + metrics = ['mae'] + model.compile(optimizer, loss, metrics=metrics) + + inputs = np.zeros((10, 3), np.float32) + targets = np.zeros((10, 4), np.float32) + dataset = dataset_ops.Dataset.from_tensor_slices((inputs, targets)) + dataset = dataset.repeat(2) + dataset = dataset.batch(10) + iterator = dataset_ops.make_one_shot_iterator(dataset) + + with test.mock.patch.object(logging, 'warning') as mock_log: + model.fit(iterator, epochs=1, steps_per_epoch=3, verbose=0) + self.assertRegexpMatches( + str(mock_log.call_args), + 'dataset iterator ran out of data') + + +class TestTrainingWithDataset(test.TestCase, parameterized.TestCase): + + @tf_test_util.run_in_graph_and_eager_modes + def test_calling_model_on_same_dataset(self): + model = testing_utils.get_small_functional_mlp(1, 4, input_dim=3) + optimizer = RMSPropOptimizer(learning_rate=0.001) + loss = 'mse' + metrics = ['mae'] + model.compile(optimizer, loss, metrics=metrics) + + inputs = np.zeros((10, 3), np.float32) + targets = np.zeros((10, 4), np.float32) + dataset = dataset_ops.Dataset.from_tensor_slices((inputs, targets)) + dataset = dataset.repeat(100) + dataset = dataset.batch(10) + + # Call fit with validation data + model.fit(dataset, epochs=1, steps_per_epoch=2, verbose=0, + validation_data=dataset, validation_steps=2) + # Finalize the graph to make sure new ops aren't added when calling on the + # same dataset + ops.get_default_graph().finalize() + model.fit(dataset, epochs=1, steps_per_epoch=2, verbose=0, + validation_data=dataset, validation_steps=2) + + @tf_test_util.run_in_graph_and_eager_modes + def test_training_and_eval_methods_on_dataset(self): + model = testing_utils.get_small_functional_mlp(1, 4, input_dim=3) + optimizer = RMSPropOptimizer(learning_rate=0.001) + loss = 'mse' + metrics = ['mae', metrics_module.CategoricalAccuracy()] + model.compile(optimizer, loss, metrics=metrics) + + inputs = np.zeros((10, 3), np.float32) + targets = np.zeros((10, 4), np.float32) + dataset = dataset_ops.Dataset.from_tensor_slices((inputs, targets)) + dataset = dataset.repeat(100) + dataset = dataset.batch(10) + + model.fit(dataset, epochs=1, steps_per_epoch=2, verbose=1) + model.evaluate(dataset, steps=2, verbose=1) + model.predict(dataset, steps=2) + + # Test with validation data + model.fit(dataset, epochs=1, steps_per_epoch=2, verbose=0, + validation_data=dataset, validation_steps=2) + + # Test with validation split + with self.assertRaisesRegexp( + ValueError, '`validation_split` argument is not supported ' + 'when input `x` is a dataset or a dataset iterator'): + model.fit(dataset, + epochs=1, steps_per_epoch=2, verbose=0, + validation_split=0.5, validation_steps=2) + + # Test with sample weight. + sample_weight = np.random.random((10,)) + with self.assertRaisesRegexp( + ValueError, '`sample_weight` argument is not supported ' + 'when input `x` is a dataset or a dataset iterator'): + model.fit( + dataset, + epochs=1, + steps_per_epoch=2, + verbose=0, + sample_weight=sample_weight) + + # Test invalid usage + with self.assertRaisesRegexp(ValueError, + 'you should not specify a target'): + model.fit(dataset, dataset, + epochs=1, steps_per_epoch=2, verbose=0) + + with self.assertRaisesRegexp( + ValueError, 'you should specify the `steps_per_epoch` argument'): + model.fit(dataset, epochs=1, verbose=0) + with self.assertRaisesRegexp(ValueError, + 'you should specify the `steps` argument'): + model.evaluate(dataset, verbose=0) + with self.assertRaisesRegexp(ValueError, + 'you should specify the `steps` argument'): + model.predict(dataset, verbose=0) + + @tf_test_util.run_in_graph_and_eager_modes + def test_dataset_with_sample_weights(self): + model = testing_utils.get_small_functional_mlp(1, 4, input_dim=3) + optimizer = RMSPropOptimizer(learning_rate=0.001) + loss = 'mse' + metrics = ['mae', metrics_module.CategoricalAccuracy()] + model.compile(optimizer, loss, metrics=metrics) + + inputs = np.zeros((10, 3), np.float32) + targets = np.zeros((10, 4), np.float32) + sample_weights = np.ones((10), np.float32) + dataset = dataset_ops.Dataset.from_tensor_slices((inputs, targets, + sample_weights)) + dataset = dataset.repeat(100) + dataset = dataset.batch(10) + + model.fit(dataset, epochs=1, steps_per_epoch=2, verbose=1) + model.evaluate(dataset, steps=2, verbose=1) + model.predict(dataset, steps=2) + + @parameterized.parameters( + {'model': 'functional'}, + {'model': 'subclass'}, + ) + @tf_test_util.run_in_graph_and_eager_modes + def test_dataset_with_sparse_labels(self, model): + if model == 'functional': + model = testing_utils.get_small_functional_mlp(1, 4, input_dim=3) + elif model == 'subclass': + model = testing_utils.get_small_sequential_mlp(1, 4) + + for loss in ['sparse_categorical_crossentropy', + losses_impl.sparse_softmax_cross_entropy]: + optimizer = RMSPropOptimizer(learning_rate=0.001) + model.compile(optimizer, loss) + + inputs = np.zeros((10, 3), dtype=np.float32) + targets = np.random.randint(0, 4, size=10, dtype=np.int32) + dataset = dataset_ops.Dataset.from_tensor_slices((inputs, targets)) + dataset = dataset.repeat(100) + dataset = dataset.batch(10) + + model.fit(dataset, epochs=1, steps_per_epoch=2, verbose=1) + + @tf_test_util.run_deprecated_v1 + def test_dataset_input_shape_validation(self): + with self.cached_session(): + model = testing_utils.get_small_functional_mlp(1, 4, input_dim=3) + model.compile(optimizer=RMSPropOptimizer(learning_rate=0.001), loss='mse') + + # User forgets to batch the dataset + inputs = np.zeros((10, 3)) + targets = np.zeros((10, 4)) + dataset = dataset_ops.Dataset.from_tensor_slices((inputs, targets)) + dataset = dataset.repeat(100) + + with self.assertRaisesRegexp( + ValueError, + r'expected (.*?) to have shape \(3,\) but got array with shape \(1,\)' + ): + model.train_on_batch(dataset) + + # Wrong input shape + inputs = np.zeros((10, 5)) + targets = np.zeros((10, 4)) + dataset = dataset_ops.Dataset.from_tensor_slices((inputs, targets)) + dataset = dataset.repeat(100) + dataset = dataset.batch(10) + + with self.assertRaisesRegexp(ValueError, + r'expected (.*?) to have shape \(3,\)'): + model.train_on_batch(dataset) + + +class TestMetricsWithDatasetIterators(test.TestCase): + + @tf_test_util.run_in_graph_and_eager_modes + def test_metrics_correctness_with_iterator(self): + model = keras.Sequential() + model.add( + keras.layers.Dense( + 8, activation='relu', input_dim=4, kernel_initializer='ones')) + model.add( + keras.layers.Dense( + 1, activation='sigmoid', kernel_initializer='ones')) + model.compile( + loss='binary_crossentropy', + metrics=['accuracy', metrics_module.BinaryAccuracy()], + optimizer=RMSPropOptimizer(learning_rate=0.001)) + + np.random.seed(123) + x = np.random.randint(10, size=(100, 4)).astype(np.float32) + y = np.random.randint(2, size=(100, 1)).astype(np.float32) + dataset = dataset_ops.Dataset.from_tensor_slices((x, y)) + dataset = dataset.batch(10) + iterator = dataset_ops.make_one_shot_iterator(dataset) + outs = model.evaluate(iterator, steps=10) + self.assertEqual(np.around(outs[1], decimals=1), 0.5) + self.assertEqual(np.around(outs[2], decimals=1), 0.5) + + y = np.zeros((100, 1), dtype=np.float32) + dataset = dataset_ops.Dataset.from_tensor_slices((x, y)) + dataset = dataset.repeat(100) + dataset = dataset.batch(10) + iterator = dataset_ops.make_one_shot_iterator(dataset) + outs = model.evaluate(iterator, steps=10) + self.assertEqual(outs[1], 0.) + self.assertEqual(outs[2], 0.) + + +if __name__ == '__main__': + test.main() diff --git a/tensorflow/python/keras/engine/training_distributed.py b/tensorflow/python/keras/engine/training_distributed.py index 808d7c9f33..473f06ded7 100644 --- a/tensorflow/python/keras/engine/training_distributed.py +++ b/tensorflow/python/keras/engine/training_distributed.py @@ -19,9 +19,12 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -import enum +import enum # pylint: disable=g-bad-import-order import numpy as np +from tensorflow.python.distribute import distribute_lib +from tensorflow.python.distribute import reduce_util as ds_reduce_util +from tensorflow.python.eager import context from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors @@ -34,9 +37,7 @@ from tensorflow.python.keras.engine import distributed_training_utils from tensorflow.python.keras.utils.generic_utils import Progbar from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops -from tensorflow.python.ops import variable_scope from tensorflow.python.platform import tf_logging as logging -from tensorflow.python.training import distribute as distribute_lib from tensorflow.python.util import nest @@ -48,180 +49,15 @@ class _Mode(enum.Enum): # TODO(priyag, sourabhbajaj): Refactor this file to address code duplication. -def fit_loop( - model, - iterator, - epochs=100, - verbose=1, - callbacks=None, - val_iterator=None, - initial_epoch=0, - steps_per_epoch=None, - validation_steps=None): - """Fit loop for training with DistributionStrategy. - - Arguments: - model: Keras Model instance. - iterator: Iterator for input data. - epochs: Number of times to iterate over the data - verbose: Integer, Verbosity mode, 0, 1 or 2 - callbacks: List of callbacks to be called during training - val_iterator: Iterator for validation data. - initial_epoch: Epoch at which to start training - (useful for resuming a previous training run) - steps_per_epoch: Total number of steps (batches of samples) - before declaring one epoch finished and starting the - next epoch. Ignored with the default value of `None`. - validation_steps: Number of steps to run validation for - (only if doing validation from data tensors). - Ignored with the default value of `None`. - - Returns: - `History` object. - - Raises: - ValueError: in case of invalid arguments. - """ - current_strategy = model._distribution_strategy - - # TODO(priyag, sourabhbajaj): Remove this when the codepaths are merged. - if current_strategy.__class__.__name__ == 'TPUStrategy': - return _experimental_fit_loop( - model, iterator, epochs, verbose, callbacks, initial_epoch, - steps_per_epoch, val_iterator, validation_steps) - - if not model._grouped_model: - clone_model_on_replicas(model, current_strategy, make_callback_model=True) - - def _per_device_fit_function(model): - model._make_fit_function() - return (model._fit_function.inputs, model._fit_function.outputs, - model._fit_function.updates_op, model._fit_function.session_kwargs) - - inputs, targets, sample_weights = _get_input_from_iterator(iterator, model) - with current_strategy.scope(): - # Create train ops on each of the devices when we call - # `_per_device_fit_function`. - (grouped_inputs, grouped_outputs, grouped_updates, - grouped_session_args) = current_strategy.call_for_each_replica( - _per_device_fit_function, args=(model._grouped_model,)) - # Unwrap all the per device values returned from `call_for_each_replica`. - # Unwrapping per device values gives you a list of values that can be - # used to construct a new train function that is composed of update ops on - # all the devices over which the model is distributed. - (all_inputs, all_outputs, all_updates, - all_session_args) = distributed_training_utils.unwrap_values( - current_strategy, grouped_inputs, grouped_outputs, - grouped_updates, grouped_session_args, with_loss_tensor=True) - - # Dataset inputs and targets are also per devices values that need to be - # unwrapped. - dataset_inputs = distributed_training_utils.flatten_perdevice_values( - current_strategy, inputs) - dataset_targets = distributed_training_utils.flatten_perdevice_values( - current_strategy, targets) - - # Create a train function that is composed of all the parameters above. - distributed_fit_function = K.function( - all_inputs, - all_outputs, - updates=all_updates, - name='distributed_fit_function', - **all_session_args) - - # We need to set sample_weights to None since there are sample weight - # placeholders that are created with default values. - sample_weights = [None for _ in range( - len(model.outputs) * current_strategy.num_replicas_in_sync)] - if not isinstance(K.learning_phase(), int): - ins = dataset_inputs + dataset_targets + sample_weights + [1] - else: - ins = dataset_inputs + dataset_targets - - do_validation = False - if validation_steps: - do_validation = True - - # Copy the weights from the original model to each of the replicated models. - orig_model_weights = model.get_weights() - distributed_model = current_strategy.unwrap(model._grouped_model)[0] - distributed_training_utils.set_weights( - current_strategy, distributed_model, orig_model_weights) - - callbacks = cbks.configure_callbacks( - callbacks, - model, - do_validation=do_validation, - val_inputs=None, - val_targets=None, - epochs=epochs, - steps_per_epoch=steps_per_epoch, - verbose=verbose) - out_labels = model.metrics_names or [] - callbacks.on_train_begin() - - assert steps_per_epoch is not None - - for epoch in range(initial_epoch, epochs): - # Reset stateful metrics - for m in model.stateful_metric_functions: - m.reset_states() - callbacks.on_epoch_begin(epoch) - epoch_logs = {} - for step_index in range(steps_per_epoch): - batch_logs = {'batch': step_index, 'size': 1} - callbacks.on_batch_begin(step_index, batch_logs) - try: - outs = distributed_fit_function(ins) - except errors.OutOfRangeError: - logging.warning('Your dataset iterator ran out of data; ' - 'interrupting training. Make sure that your dataset ' - 'can generate at least `steps_per_epoch * epochs` ' - 'batches (in this case, %d batches).' % - steps_per_epoch * epochs) - break - - if not isinstance(outs, list): - outs = [outs] - for l, o in zip(out_labels, outs): - batch_logs[l] = o - callbacks.on_batch_end(step_index, batch_logs) - if callbacks.model.stop_training: - break - if do_validation: - val_outs = test_loop( - model, - val_iterator, - steps=validation_steps, - verbose=0) - if not isinstance(val_outs, list): - val_outs = [val_outs] - # Same labels assumed. - for l, o in zip(out_labels, val_outs): - epoch_logs['val_' + l] = o - - callbacks.on_epoch_end(epoch, epoch_logs) - if callbacks.model.stop_training: - break - callbacks.on_train_end() - - # Copy the weights back from the replicated model to the original model. - updated_weights = current_strategy.unwrap( - model._grouped_model)[0].get_weights() - model.set_weights(updated_weights) - return model.history - - -def _experimental_fit_loop( - model, - iterator, - epochs=100, - verbose=1, - callbacks=None, - initial_epoch=0, - steps_per_epoch=None, - val_iterator=None, - validation_steps=None): +def experimental_fit_loop(model, + iterator, + epochs=100, + verbose=1, + callbacks=None, + initial_epoch=0, + steps_per_epoch=None, + val_iterator=None, + validation_steps=None): """Fit loop for training with TPU DistributionStrategy. Arguments: @@ -259,11 +95,12 @@ def _experimental_fit_loop( K.set_learning_phase(1) out_labels = model.metrics_names or [] - def step_fn(ctx, inputs, targets): + def step_fn(ctx, inputs): """Clones the model and calls make_fit_function.""" # TODO(priyag, sourabhbajaj): The model gets cloned every time # fit/test/predict is called. We should look into caching this keyed on # input shapes. + inputs, targets = inputs clone_model_on_replicas( model, current_strategy, @@ -273,7 +110,7 @@ def _experimental_fit_loop( mode=_Mode.TRAIN) (grouped_inputs, grouped_outputs, grouped_updates, - grouped_session_args) = current_strategy.call_for_each_replica( + grouped_session_args) = current_strategy.extended.call_for_each_replica( _per_device_fit_function, args=(model._grouped_model_train,)) (all_inputs, all_outputs, all_updates, all_session_args) = distributed_training_utils.unwrap_values( @@ -288,12 +125,12 @@ def _experimental_fit_loop( for label, output in zip(out_labels, combined_fn.outputs): if label == 'loss': - aggregation = distribute_lib.get_loss_reduction() + reduce_op = distribute_lib.get_loss_reduction() else: - # We aggregate all other metrics using mean for now. This is temporary + # We reduce all other metrics using mean for now. This is temporary # workaround until new metrics are in place. - aggregation = variable_scope.VariableAggregation.MEAN - ctx.set_last_step_output(label, output, aggregation) + reduce_op = ds_reduce_util.ReduceOp.MEAN + ctx.set_last_step_output(label, output, reduce_op) # TODO(priyag, sourabhbajaj): Ignoring these things from the combined_fn: # feed_dict, session kwargs, run options, run_metadata for now. These should @@ -303,19 +140,20 @@ def _experimental_fit_loop( # Add initial dummy values for loss and other metric tensors. initial_loop_values = {} initial_loop_values['loss'] = constant_op.constant(1e7) - for name, tensor in zip(model.metrics_names[1:], model.metrics_tensors): + for name in model.metrics_names[1:]: + tensor = model._all_stateful_metrics_tensors[name] initial_loop_values[name] = array_ops.zeros(tensor.shape, tensor.dtype) if steps_per_epoch is None: raise ValueError('`steps_per_epoch` should be specified when calling ' '`fit` on the model.') steps_per_run = K.variable( - value=min(steps_per_epoch, current_strategy.steps_per_run), + value=min(steps_per_epoch, current_strategy.extended.steps_per_run), dtype='int32', name='steps_per_run') with current_strategy.scope(): - ctx = current_strategy.run_steps_on_dataset( + ctx = current_strategy.extended.experimental_run_steps_on_iterator( step_fn, iterator, iterations=steps_per_run, initial_loop_values=initial_loop_values) @@ -334,17 +172,16 @@ def _experimental_fit_loop( callbacks, model, do_validation=do_validation, - val_inputs=None, - val_targets=None, epochs=epochs, steps_per_epoch=steps_per_epoch, verbose=verbose) # Calculate the steps each time on the device. - steps_to_run = [current_strategy.steps_per_run] * ( - steps_per_epoch // current_strategy.steps_per_run) - if steps_per_epoch % current_strategy.steps_per_run: - steps_to_run.append(steps_per_epoch % current_strategy.steps_per_run) + steps_to_run = [current_strategy.extended.steps_per_run] * ( + steps_per_epoch // current_strategy.extended.steps_per_run) + if steps_per_epoch % current_strategy.extended.steps_per_run: + steps_to_run.append( + steps_per_epoch % current_strategy.extended.steps_per_run) callbacks.on_train_begin() for epoch in range(initial_epoch, epochs): @@ -384,7 +221,7 @@ def _experimental_fit_loop( model._grouped_model_train)[0].get_weights() model.set_weights(updated_weights) - val_outs = _experimental_test_loop( + val_outs = experimental_test_loop( # pylint: disable=undefined-variable model, val_iterator, steps=validation_steps, @@ -411,105 +248,11 @@ def _experimental_fit_loop( return model.history -def test_loop(model, iterator, verbose=0, steps=None): - """Test loop for evaluating with DistributionStrategy. - - Arguments: - model: Keras Model instance. - iterator: Iterator for input data. - verbose: Integer, Verbosity mode 0 or 1. - steps: Total number of steps (batches of samples) - before declaring predictions finished. - Ignored with the default value of `None`. - - Returns: - Scalar loss (if the model has a single output and no metrics) - or list of scalars (if the model has multiple outputs - and/or metrics). The attribute `model.metrics_names` will give you - the display labels for the outputs. - """ - current_strategy = model._distribution_strategy - - # TODO(priyag, sourabhbajaj): Remove this when the codepaths are merged. - if current_strategy.__class__.__name__ == 'TPUStrategy': - return _experimental_test_loop(model, iterator, verbose, steps) - - if not model._grouped_model: - clone_model_on_replicas(model, current_strategy) - - def _per_device_eval_function(model): - model._make_eval_function() - return (model._eval_function.inputs, model._eval_function.outputs, - model._eval_function.updates_op, - model._eval_function.session_kwargs) - - inputs, targets, sample_weights = _get_input_from_iterator(iterator, model) - with current_strategy.scope(): - (grouped_inputs, grouped_outputs, grouped_updates, - grouped_session_args) = current_strategy.call_for_each_replica( - _per_device_eval_function, args=(model._grouped_model,)) - - (all_inputs, all_outputs, all_updates, - all_session_args) = distributed_training_utils.unwrap_values( - current_strategy, grouped_inputs, grouped_outputs, grouped_updates, - grouped_session_args, with_loss_tensor=True) - - dataset_inputs = distributed_training_utils.flatten_perdevice_values( - current_strategy, inputs) - dataset_targets = distributed_training_utils.flatten_perdevice_values( - current_strategy, targets) - - distributed_test_function = K.function( - all_inputs, all_outputs, - updates=all_updates, - name='distributed_test_function', - **all_session_args) - - # We need to set sample_weights to None since there are sample weight - # placeholders that are created with default values. - sample_weights = [None for _ in range( - len(model.outputs) * current_strategy.num_replicas_in_sync)] - if not isinstance(K.learning_phase(), int): - ins = dataset_inputs + dataset_targets + sample_weights + [0] - else: - ins = dataset_inputs + dataset_targets - - for m in model.stateful_metric_functions: - m.reset_states() - - outs = [] - if verbose == 1: - progbar = Progbar(target=steps) - - # Copy the weights from the original model to each of the replicated models. - orig_model_weights = model.get_weights() - distributed_model = current_strategy.unwrap(model._grouped_model)[0] - distributed_training_utils.set_weights( - current_strategy, distributed_model, orig_model_weights) - - assert steps is not None - for step in range(steps): - batch_outs = distributed_test_function(ins) - if isinstance(batch_outs, list): - if step == 0: - outs = [0.] * len(batch_outs) - outs[0] += batch_outs[0] # index 0 = 'loss' - outs[1:] = batch_outs[1:] - else: - if step == 0: - outs.append(0.) - outs[0] += batch_outs # index 0 = 'loss' - if verbose >= 1: - progbar.update(step + 1) - outs[0] /= steps # index 0 = 'loss' - - if len(outs) == 1: - return outs[0] - return outs - - -def _experimental_test_loop(model, iterator, verbose=0, steps=None, - initialize_finalize_strategy=True): +def experimental_test_loop(model, + iterator, + verbose=0, + steps=None, + initialize_finalize_strategy=True): """Test loop for evaluating with TPU DistributionStrategy. Arguments: @@ -541,11 +284,12 @@ def _experimental_test_loop(model, iterator, verbose=0, steps=None, # TODO(priyag, sourabhbajaj): This should likely not be hardcoded here. K.set_learning_phase(0) - def step_fn(ctx, inputs, targets): + def step_fn(ctx, inputs): """Clones the model and calls make_eval_function.""" # TODO(priyag, sourabhbajaj): The model gets cloned every time # fit/test/predict is called. We should look into caching this keyed on # input shapes. + inputs, targets = inputs clone_model_on_replicas( model, current_strategy, @@ -555,7 +299,7 @@ def _experimental_test_loop(model, iterator, verbose=0, steps=None, mode=_Mode.TEST) (grouped_inputs, grouped_outputs, grouped_updates, - grouped_session_args) = current_strategy.call_for_each_replica( + grouped_session_args) = current_strategy.extended.call_for_each_replica( _per_device_eval_function, args=(model._grouped_model_test,)) (all_inputs, all_outputs, all_updates, @@ -571,25 +315,26 @@ def _experimental_test_loop(model, iterator, verbose=0, steps=None, for label, output in zip(model.metrics_names, combined_fn.outputs): if label == 'loss': - aggregation = distribute_lib.get_loss_reduction() + reduce_op = distribute_lib.get_loss_reduction() else: - # We aggregate all other metrics using mean for now. This is temporary + # We reduce all other metrics using mean for now. This is temporary # workaround until new metrics are in place. - aggregation = variable_scope.VariableAggregation.MEAN - ctx.set_last_step_output(label, output, aggregation) + reduce_op = ds_reduce_util.ReduceOp.MEAN + ctx.set_last_step_output(label, output, reduce_op) return combined_fn.updates_op # Add initial dummy values for loss and other metric tensors. initial_loop_values = {} initial_loop_values['loss'] = constant_op.constant(1e7) - for name, tensor in zip(model.metrics_names[1:], model.metrics_tensors): + for name in model.metrics_names[1:]: + tensor = model._all_stateful_metrics_tensors[name] initial_loop_values[name] = array_ops.zeros(tensor.shape, tensor.dtype) with current_strategy.scope(): # TODO(priyag): Use steps_per_run when we use new metrics as they will # allow handling metric computation at each step using variables. - ctx = current_strategy.run_steps_on_dataset( + ctx = current_strategy.extended.experimental_run_steps_on_iterator( step_fn, iterator, iterations=1, initial_loop_values=initial_loop_values) @@ -625,103 +370,7 @@ def _experimental_test_loop(model, iterator, verbose=0, steps=None, return outs -def predict_loop(model, iterator, verbose=0, steps=None): - """Predict loop for predicting with DistributionStrategy. - - Arguments: - model: Keras Model instance. - iterator: Iterator for input data. - verbose: Integer, Verbosity mode 0 or 1. - steps: Total number of steps (batches of samples) - before declaring `_predict_loop` finished. - Ignored with the default value of `None`. - - Returns: - Array of predictions (if the model has a single output) - or list of arrays of predictions - (if the model has multiple outputs). - """ - current_strategy = model._distribution_strategy - - # TODO(priyag, sourabhbajaj): Remove this when the codepaths are merged. - if current_strategy.__class__.__name__ == 'TPUStrategy': - return _experimental_predict_loop(model, iterator, verbose, steps) - - if not model._grouped_model: - clone_model_on_replicas(model, current_strategy) - - def _per_device_predict_function(model): - model._make_predict_function() - return (model.predict_function.inputs, - model.predict_function.outputs, - model.predict_function.updates_op, - model.predict_function.session_kwargs) - - inputs, _, _ = _get_input_from_iterator(iterator, model) - with current_strategy.scope(): - (grouped_inputs, grouped_outputs, grouped_updates, - grouped_session_args) = current_strategy.call_for_each_replica( - _per_device_predict_function, args=(model._grouped_model,)) - - (all_inputs, all_outputs, all_updates, - all_session_args) = distributed_training_utils.unwrap_values( - current_strategy, grouped_inputs, grouped_outputs, grouped_updates, - grouped_session_args) - - dataset_inputs = distributed_training_utils.flatten_perdevice_values( - current_strategy, inputs) - - distributed_predict_function = K.function( - all_inputs, all_outputs, - updates=all_updates, - name='distributed_predict_function', - **all_session_args) - - if not isinstance(K.learning_phase(), int): - ins = dataset_inputs + [0] - else: - ins = dataset_inputs - - if verbose == 1: - progbar = Progbar(target=steps) - - # Copy the weights from the original model to each of the replicated models. - orig_model_weights = model.get_weights() - distributed_model = current_strategy.unwrap(model._grouped_model)[0] - distributed_training_utils.set_weights( - current_strategy, distributed_model, orig_model_weights) - - num_replicas = current_strategy.num_replicas_in_sync - # Since we do not know how many samples we will see, we cannot - # pre-allocate the returned Numpy arrays. Instead, we store one array per - # batch seen and concatenate them upon returning. - unconcatenated_outs = [] - assert steps is not None - for step in range(steps): - batch_outs = distributed_predict_function(ins) - if not isinstance(batch_outs, list): - batch_outs = [batch_outs] - if step == 0: - # batch_outs gives you the number of model outputs. In the distributed - # case this will be number of model_outputs * num_replicas. - for _ in range(len(model.outputs)): - unconcatenated_outs.append([]) - for i in range(len(model.outputs)): - nested_outs = batch_outs[i * num_replicas: - i * num_replicas + num_replicas] - outs = nest.flatten(nested_outs) - unconcatenated_outs[i].extend(outs) - if verbose >= 1: - progbar.update(step + 1) - if len(unconcatenated_outs) == 1: - return np.concatenate(unconcatenated_outs[0], axis=0) - return [ - np.concatenate(unconcatenated_outs[i], axis=0) - for i in range(len(unconcatenated_outs)) - ] - - -def _experimental_predict_loop(model, iterator, verbose=0, steps=None): +def experimental_predict_loop(model, iterator, verbose=0, steps=None): """Predict loop for predicting with TPU DistributionStrategy. Arguments: @@ -750,7 +399,7 @@ def _experimental_predict_loop(model, iterator, verbose=0, steps=None): model.predict_function.updates_op, model.predict_function.session_kwargs) - def step_fn(ctx, *inputs): + def step_fn(ctx, inputs): """Clones the model and calls make_predict_function.""" # TODO(priyag, sourabhbajaj): The model gets cloned every time @@ -764,7 +413,7 @@ def _experimental_predict_loop(model, iterator, verbose=0, steps=None): mode=_Mode.PREDICT) (grouped_inputs, grouped_outputs, grouped_updates, - grouped_session_args) = current_strategy.call_for_each_replica( + grouped_session_args) = current_strategy.extended.call_for_each_replica( _per_device_predict_function, args=(model._grouped_model_predict,)) (all_inputs, all_outputs, all_updates, @@ -795,7 +444,7 @@ def _experimental_predict_loop(model, iterator, verbose=0, steps=None): with current_strategy.scope(): # TODO(priyag, sourabhbajaj): Support steps_per_run if/when we add outfeed. - ctx = current_strategy.run_steps_on_dataset( + ctx = current_strategy.extended.experimental_run_steps_on_iterator( step_fn, iterator, iterations=1, initial_loop_values=initial_loop_values) @@ -835,7 +484,17 @@ def _experimental_predict_loop(model, iterator, verbose=0, steps=None): ] -def _clone_and_build_model(model, inputs=None, targets=None): +def _custom_compile_for_predict(model): + """Custom compile for TPU predict mode.""" + model.total_loss = None + model._fit_function = None + model._eval_function = None + model.train_function = None + model.test_function = None + model.predict_function = None + + +def _clone_and_build_model(model, inputs=None, targets=None, mode=None): """Clone and build the given keras_model.""" # We need to set the import here since we run into a circular dependency # error. @@ -862,23 +521,27 @@ def _clone_and_build_model(model, inputs=None, targets=None): if isinstance(targets, tuple): targets = nest.flatten(targets) - cloned_model.compile( - optimizer, - model.loss, - metrics=metrics_module.clone_metrics(model.metrics), - loss_weights=model.loss_weights, - sample_weight_mode=model.sample_weight_mode, - weighted_metrics=metrics_module.clone_metrics(model.weighted_metrics), - target_tensors=targets) + if mode == _Mode.PREDICT: + _custom_compile_for_predict(cloned_model) + else: + cloned_model.compile( + optimizer, + model.loss, + metrics=metrics_module.clone_metrics(model._compile_metrics), + loss_weights=model.loss_weights, + sample_weight_mode=model.sample_weight_mode, + weighted_metrics=metrics_module.clone_metrics( + model._compile_weighted_metrics), + target_tensors=targets) return cloned_model def clone_model_on_replicas(model, strategy, make_callback_model=False, inputs=None, targets=None, mode=None): """Create a cloned model on each replica.""" - with strategy.scope(): - grouped_model = strategy.call_for_each_replica( - _clone_and_build_model, args=(model, inputs, targets)) + with K.get_graph().as_default(), strategy.scope(): + grouped_model = strategy.extended.call_for_each_replica( + _clone_and_build_model, args=(model, inputs, targets, mode)) if mode is _Mode.TRAIN: model._grouped_model_train = grouped_model elif mode is _Mode.TEST: @@ -915,3 +578,149 @@ def _get_input_from_iterator(iterator, model): model._standardize_weights(x_values, y_values, sample_weight=sample_weights_values) return x, y, sample_weights + + +def _make_execution_function(model, mode): + """Makes function to run one step of distributed model execution.""" + if context.executing_eagerly(): + return _make_eager_execution_function(model, mode) + + strategy = model._distribution_strategy + if not model._grouped_model: + clone_model_on_replicas( + model, strategy, make_callback_model=(mode == 'train')) + + def _per_device_function(model): + f = model._make_execution_function(mode) + return (f.inputs, f.outputs, f.updates_op, f.session_kwargs) + + with strategy.scope(): + # Create train ops on each of the devices when we call + # `_per_device_fit_function`. + (grouped_inputs, grouped_outputs, grouped_updates, + grouped_session_args) = strategy.extended.call_for_each_replica( + _per_device_function, args=(model._grouped_model,)) + + if mode == 'train': + # Initialize the variables in the replicated model. This is necessary for + # multi-worker training because on some workers, initialization is not + # needed. This method does initialization or waiting for initialization + # according to the context object of distribute coordinator. + distributed_training_utils.init_restore_or_wait_for_variables() + + # Unwrap all the per device values returned from `call_for_each_replica`. + # Unwrapping per device values gives you a list of values that can be + # used to construct a new train function that is composed of update ops on + # all the devices over which the model is distributed. + (all_inputs, all_outputs, all_updates, + all_session_args) = distributed_training_utils.unwrap_values( + strategy, + grouped_inputs, + grouped_outputs, + grouped_updates, + grouped_session_args, + with_loss_tensor=(mode != 'predict')) + + return K.function( + all_inputs, + all_outputs, + updates=all_updates, + name='distributed_{}_function'.format(mode), + **all_session_args) + + +def _make_eager_execution_function(model, mode): + """Makes function to run one step of distributed model eager execution.""" + strategy = model._distribution_strategy + if not model._grouped_model: + clone_model_on_replicas( + model, strategy, make_callback_model=(mode == 'train')) + + def _per_device_function(model): + f = model._make_execution_function(mode) + return (f.inputs, f.outputs) + + # NOTE(priyag): Try creating a new FuncGraph within DS scope instead of using + # the global one. + with K.get_graph().as_default(), strategy.scope(): + # Create train ops on each of the devices when we call + # `_per_device_fit_function`. + (grouped_inputs, grouped_outputs) = strategy.call_for_each_replica( + _per_device_function, args=(model._grouped_model,)) + + # Unwrap all the per device values returned from `call_for_each_replica`. + # Unwrapping per device values gives you a list of values that can be + # used to construct a new train function that is composed of inptus/outputs + # on all the devices over which the model is distributed. + (all_inputs, all_outputs, _, _) = distributed_training_utils.unwrap_values( + strategy, + grouped_inputs, + grouped_outputs, + with_loss_tensor=(mode != 'predict')) + + return K.function( + all_inputs, + all_outputs, + name='eager_distributed_{}_function'.format(mode)) + + +def _prepare_feed_values(model, inputs, targets, sample_weights, mode): + """Prepare feed values to the model execution function. + + Arguments: + model: Model to prepare feed values for. + inputs: List or dict of model inputs. + targets: Optional list of model targets. + sample_weights: Optional list of sample weight arrays. + mode: One of 'train'/'test'/'predict'. + + Returns: + Feed values for the model in the given mode. + """ + strategy = model._distribution_strategy + inputs, targets, sample_weights = _get_input_from_iterator(inputs, model) + inputs = distributed_training_utils.flatten_perdevice_values(strategy, inputs) + targets = distributed_training_utils.flatten_perdevice_values( + strategy, targets) + if mode == 'predict': + sample_weights = [] + targets = [] + else: + sample_weights = [ + None for _ in range(len(model.outputs) * strategy.num_replicas_in_sync) + ] + ins = inputs + targets + sample_weights + if mode == 'train' and not isinstance(K.symbolic_learning_phase(), int): + ins += [True] + return ins + + +def _copy_weights_to_distributed_model(model): + """Copies weights from original model to distributed models.""" + if model._distribution_strategy: + # Copy the weights from the original model to each of the replicated models. + orig_model_weights = model.get_weights() + distributed_model = model._distribution_strategy.unwrap( + model._grouped_model)[0] + distributed_training_utils.set_weights( + model._distribution_strategy, distributed_model, orig_model_weights) + + +def _copy_weights_to_original_model(model, mode): + """Copies weights from first distributed model back to original model.""" + if model._distribution_strategy and mode == 'train': + updated_weights = model._distribution_strategy.unwrap( + model._grouped_model)[0].get_weights() + model.set_weights(updated_weights) + + +def _per_device_aggregate_batch(batch_outs, model, mode): + """Aggregates the per-device batch-level outputs from a distributed step.""" + if model._distribution_strategy is not None and mode == 'predict': + total_batch_outs = [] + for i in range(len(model.outputs)): + num_replicas = model._distribution_strategy.num_replicas_in_sync + nested_outs = batch_outs[i * num_replicas:i * num_replicas + num_replicas] + total_batch_outs.append(np.concatenate(nest.flatten(nested_outs))) + return total_batch_outs + return batch_outs diff --git a/tensorflow/python/keras/engine/training_eager.py b/tensorflow/python/keras/engine/training_eager.py index 9131df5cd0..1d1cec1c50 100644 --- a/tensorflow/python/keras/engine/training_eager.py +++ b/tensorflow/python/keras/engine/training_eager.py @@ -19,30 +19,20 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -import copy -import threading +import collections -import numpy as np - -from tensorflow.python.data.ops import iterator_ops -from tensorflow.python.eager import function as eager_function from tensorflow.python.eager.backprop import GradientTape -from tensorflow.python.framework import errors from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_util from tensorflow.python.keras import backend -from tensorflow.python.keras import callbacks as cbks -from tensorflow.python.keras import metrics as metrics_module +from tensorflow.python.keras import losses as losses_module from tensorflow.python.keras.engine import training_utils from tensorflow.python.keras.utils import generic_utils +from tensorflow.python.keras.utils.losses_utils import squeeze_or_expand_dimensions from tensorflow.python.ops import math_ops from tensorflow.python.platform import tf_logging as logging -# A lock for assigning polymorphic functions to models in a thread-safe way -_graph_function_building_lock = threading.Lock() - - def _eager_loss_fn(outputs, targets, loss_fn, output_name): with backend.name_scope(output_name + '_loss'): loss = loss_fn(targets, outputs) @@ -133,11 +123,24 @@ def _model_loss(model, else: weights = None mask = masks[i] - - weighted_masked_fn = training_utils.weighted_masked_objective(loss_fn) with backend.name_scope(model.output_names[i] + '_loss'): - output_loss = weighted_masked_fn( - targets[i], outs[i], weights, mask=mask) + if isinstance(loss_fn, losses_module.Loss): + if mask is not None: + mask = math_ops.cast(mask, outs[i].dtype) + # Update weights with mask. + if weights is None: + weights = mask + else: + # Update dimensions of weights to match with mask if possible. + mask, _, weights = squeeze_or_expand_dimensions( + mask, None, weights) + weights *= mask + output_loss = loss_fn(targets[i], outs[i], sample_weight=weights) + else: + weighted_masked_fn = training_utils.weighted_masked_objective(loss_fn) + output_loss = weighted_masked_fn( + targets[i], outs[i], weights, mask=mask) + # If the number of outputs is 1 then we don't append the loss metric # associated with each model output. When there are multiple outputs # associated with a model, each output's loss is calculated and returned @@ -171,412 +174,6 @@ def _model_loss(model, return outs, total_loss, loss_metrics, aggregated_loss_metrics, masks -def _maybe_build_graph_functions(model): - """Constructs polymorphic functions to use for fit, evaluate and predict.""" - # We lock this function to ensure thread-safety in case users are - # hypothetically trying to call '.predict' on a model in multiple threads - # at once when the graph functions were never previously built. - with _graph_function_building_lock: - if not model._built_graph_functions: - model._eager_process_single_batch_graph_function = eager_function.defun( - _process_single_batch - ) - model._eager_model_loss_graph_function = eager_function.defun(_model_loss) - model._eager_call_graph_function = eager_function.defun(model.call) - model._built_graph_functions = True - - -def _maybe_graph_function_model_loss(model, - inputs, - targets, - output_loss_metrics=None, - sample_weights=None, - training=False): - """Compute model loss, using defun if the model supports it.""" - if model._can_use_graph_functions: - _maybe_build_graph_functions(model) - return model._eager_model_loss_graph_function( - model, - inputs, - targets, - output_loss_metrics=output_loss_metrics, - sample_weights=sample_weights, - training=training) - else: - return _model_loss( - model, - inputs, - targets, - output_loss_metrics=output_loss_metrics, - sample_weights=sample_weights, - training=training) - - -def _maybe_graph_function_model_call(model, *args, **kwargs): - """Compute model loss, using defun if the model supports it.""" - if model._can_use_graph_functions: - _maybe_build_graph_functions(model) - return model._eager_call_graph_function(*args, **kwargs) - else: - return model.call(*args, **kwargs) - - -def iterator_fit_loop(model, - inputs, - class_weight, - steps_per_epoch, - epoch_logs, - val_inputs=None, - val_targets=None, - val_sample_weights=None, - epochs=1, - verbose=1, - callbacks=None, - validation_steps=None, - do_validation=False, - batch_size=None, - output_loss_metrics=None): - """Fit function for eager execution when input is given as dataset iterator. - - Updates the given epoch logs. - - Arguments: - model: Instance of the `Model`. - inputs: Input dataset iterator. - class_weight: Optional class-weight array to weight the importance of - samples in `inputs` based on the class they belong to, as conveyed by - the targets from the `inputs` iterator. - steps_per_epoch: Total number of steps (batches of samples) - before declaring one epoch finished and starting the - next epoch. - epoch_logs: Dictionary of logs from every epoch. - val_inputs: Input data for validation. - val_targets: Target data for validation. - val_sample_weights: Sample weight data for validation. - epochs: Number of times to iterate over the data - verbose: Verbosity mode, 0, 1 or 2 - callbacks: CallbackList instance. Controls callbacks during training. - validation_steps: Number of steps to run validation for (only if doing - validation from data tensors). Ignored with default value of `None`. - do_validation: Boolean value indicating whether we should do validation. - batch_size: int, val_inputs and val_targets will be evaled batch by - batch with size batch_size if they are array. - output_loss_metrics: List of metrics that are used to aggregated output - loss values. - - Raises: - ValueError: In case of mismatch between given number of inputs and - expectations of the model. - """ - assert isinstance(inputs, iterator_ops.EagerIterator) - - # make sure either x,y or x,y,sample_weights is provided - if (not isinstance(inputs.output_shapes, (list, tuple)) or - len(inputs.output_shapes) not in (2, 3)): - raise ValueError('Please provide either inputs and targets ' - 'or inputs, targets, and sample_weights') - - for step_index in range(steps_per_epoch): - batch_logs = {'batch': step_index, 'size': 1} - callbacks.on_batch_begin(step_index, batch_logs) - - # Get data from the iterator. - try: - next_element = inputs.get_next() - except errors.OutOfRangeError: - logging.warning( - 'Your dataset iterator ran out of data; interrupting training. Make ' - 'sure that your dataset can generate at least ' - '`steps_per_epoch * epochs` batches (in this case, %d batches). You ' - 'may need to use the repeat() function when building your ' - 'dataset.' % steps_per_epoch * epochs) - break - - if len(inputs.output_shapes) == 2: - x, y = next_element - sample_weights = None - else: - x, y, sample_weights = next_element - - # Validate and standardize data. - x, y, sample_weights = model._standardize_user_data( - x, y, sample_weight=sample_weights, class_weight=class_weight) - x = training_utils.cast_if_floating_dtype(x) - y = training_utils.cast_if_floating_dtype(y) - if sample_weights: - sample_weights = [ - training_utils.cast_if_floating_dtype( - ops.convert_to_tensor(val, dtype=backend.floatx())) - if val is not None else None for val in sample_weights - ] - - # Set stateful_metrics in callbacks. We do not do this before the - # `steps_per_epoch` loop because model will be compiled only in the first - # iteration of this loop in the deferred build scenario. - if step_index == 0: - for cbk in callbacks: - if (isinstance(cbk, cbks.BaseLogger) or - isinstance(cbk, cbks.ProgbarLogger)): - cbk.stateful_metrics = model.metrics_names[1:] # Exclude `loss` - - if step_index == 0 and not callbacks.params['metrics']: - callback_metrics = copy.copy(model.metrics_names) - if do_validation: - callback_metrics += ['val_' + n for n in model.metrics_names] - callbacks.set_params({ - 'batch_size': batch_size, - 'epochs': epochs, - 'steps': steps_per_epoch, - 'verbose': verbose, - 'do_validation': do_validation, - 'metrics': callback_metrics or [], - 'validation_steps': validation_steps - }) - - # Train model. - outs, loss, _, aggregated_loss_metrics, masks = \ - _maybe_graph_function_process_single_batch( - model, - x, - y, - output_loss_metrics=output_loss_metrics, - sample_weights=sample_weights, - training=True) - outs = generic_utils.to_list(outs) - - # Calculate metrics. - for l, o in zip(model.metrics_names, outs): - batch_logs[l] = o - metrics_results = _eager_metrics_fn( - model, outs, y, sample_weights=sample_weights, masks=masks) - batch_logs['loss'] = tensor_util.constant_value(backend.mean(loss)) - - for k, v in zip( - model.metrics_names, - [backend.mean(loss)] + aggregated_loss_metrics + metrics_results): - batch_logs[k] = tensor_util.constant_value(v) - callbacks.on_batch_end(step_index, batch_logs) - if callbacks.model.stop_training: - break - - if step_index == steps_per_epoch - 1: - if do_validation: - val_outs = test_loop( - model, - val_inputs, - val_targets, - sample_weights=val_sample_weights, - steps=validation_steps, - verbose=0, - batch_size=batch_size) - if not isinstance(val_outs, list): - val_outs = [val_outs] - # Same labels assumed. - for l, o in zip(model.metrics_names, val_outs): - epoch_logs['val_' + l] = o - - -def iterator_test_loop(model, inputs, steps, verbose=0): - """Test function for eager execution when input is given as dataset iterator. - - Arguments: - model: Model instance that is being evaluated in Eager mode. - inputs: Input dataset iterator. - steps: Total number of steps (batches of samples) before declaring - predictions finished. - verbose: Verbosity mode. - - Returns: - Scalar loss (if the model has a single output and no metrics) - or list of scalars (if the model has multiple outputs - and/or metrics). The attribute `model.metrics_names` will give you - the display labels for the scalar outputs. - - Raises: - ValueError: In case of mismatch between given number of inputs and - expectations of the model. - """ - assert isinstance(inputs, iterator_ops.EagerIterator) - # make sure either x,y or x,y,sample_weights is provided - if (not isinstance(inputs.output_shapes, (list, tuple)) or - len(inputs.output_shapes) < 2 or len(inputs.output_shapes) > 3): - raise ValueError('Please provide either inputs and targets' - 'or inputs, targets, and sample_weights') - outs = [] - - # Create metric wrapper for the losses. - output_loss_metrics = [] - for i in range(len(model.outputs)): - loss_fn = model.loss_functions[i] - mean_wrapped_loss = metrics_module.MeanMetricWrapper( - loss_fn, name=loss_fn.__name__) - output_loss_metrics.append(mean_wrapped_loss) - - num_samples = 0 - if verbose == 1: - progbar = generic_utils.Progbar(target=steps) - for step_index in range(steps): - # Get data from the iterator. - try: - next_element = inputs.get_next() - except errors.OutOfRangeError: - logging.warning( - 'Your dataset iterator ran out of data interrupting testing. ' - 'Make sure that your dataset can generate at least `steps` batches ' - '(in this case, %d batches). You may need to use the repeat() ' - 'function when building your dataset.', steps) - break - - if len(inputs.output_shapes) == 2: - x, y = next_element - sample_weights = None - else: - x, y, sample_weights = next_element - - # Validate and standardize data. - x, y, sample_weights = model._standardize_user_data( - x, y, sample_weight=sample_weights) - x = training_utils.cast_if_floating_dtype(x) - y = training_utils.cast_if_floating_dtype(y) - if sample_weights: - sample_weights = [ - training_utils.cast_if_floating_dtype( - ops.convert_to_tensor(val, dtype=backend.floatx())) - if val is not None else None for val in sample_weights - ] - - if step_index == 0: - # Get stateful metrics indices. We do not do this before the `steps` loop - # because model will be compiled only in the first iteration of this loop - # in the deferred build scenario. - if hasattr(model, 'metrics'): - for m in model.stateful_metric_functions: - m.reset_states() - for m in output_loss_metrics: - m.reset_states() - - # Calculate model output, loss values. - loss_outs, loss, _, aggregated_loss_metrics, masks = \ - _maybe_graph_function_model_loss( - model, - x, - y, - output_loss_metrics=output_loss_metrics, - sample_weights=sample_weights, - training=False) - metrics_results = _eager_metrics_fn( - model, loss_outs, y, sample_weights=sample_weights, masks=masks) - batch_outs = [] - for _, v in zip( - model.metrics_names, - [backend.mean(loss)] + aggregated_loss_metrics + metrics_results): - batch_outs.append(tensor_util.constant_value(v)) - - # Get current step size. - if isinstance(x, list): - step_size = x[0].get_shape().as_list()[0] - elif isinstance(x, dict): - step_size = list(x.values())[0].get_shape().as_list()[0] - else: - step_size = x.get_shape().as_list()[0] - - # Accumulate results in output array. - if not isinstance(batch_outs, list): - batch_outs = [batch_outs] - if step_index == 0: - for _ in enumerate(batch_outs): - outs.append(0.) - outs[0] += batch_outs[0] * step_size # index 0 = 'loss' - outs[1:] = batch_outs[1:] - - # Calculate sample size. - num_samples += step_size - if verbose == 1: - progbar.update(step_index + 1) - - outs[0] /= num_samples # index 0 = 'loss' - if len(outs) == 1: - return outs[0] - return outs - - -def iterator_predict_loop(model, inputs, steps, verbose=0): - """Predict function for eager execution when input is dataset iterator. - - Arguments: - model: Instance of `Model`. - inputs: Input dataset iterator. - steps: Total number of steps (batches of samples) before declaring - `_predict_loop` finished. - verbose: Verbosity mode. - - Returns: - Array of predictions (if the model has a single output) - or list of arrays of predictions (if the model has multiple outputs). - - Raises: - ValueError: In case of mismatch between given number of inputs and - expectations of the model. - """ - assert isinstance(inputs, iterator_ops.EagerIterator) - if not isinstance(inputs.output_shapes, - (list, tuple)) or len(inputs.output_shapes) > 3: - raise ValueError( - 'Please provide data as a list or tuple of 1, 2, or 3 elements ' - ' - `(input)`, or `(input, target)`, or `(input, target,' - 'sample_weights)`. Received %s. We do not use the `target` or' - '`sample_weights` value here.' % inputs.output_shapes) - outs = [] - if verbose == 1: - progbar = generic_utils.Progbar(target=steps) - - for step_index in range(steps): - # Get data from the iterator. - try: - next_element = inputs.get_next() - except errors.OutOfRangeError: - logging.warning( - 'Your dataset iterator ran out of data; interrupting prediction. ' - 'Make sure that your dataset can generate at least `steps` batches ' - '(in this case, %d batches). You may need to use the repeat() ' - 'function when building your dataset.', steps) - break - - # expects a tuple, where first element of tuple represents inputs - x = next_element[0] - - # Validate and standardize data. - x, _, _ = model._standardize_user_data(x) - x = training_utils.cast_if_floating_dtype(x) - - if isinstance(x, list) and len(x) == 1: - x = x[0] - - if model._expects_training_arg: - batch_outs = _maybe_graph_function_model_call(model, x, training=False) - else: - batch_outs = _maybe_graph_function_model_call(model, x) - if not isinstance(batch_outs, list): - batch_outs = [batch_outs] - - # We collect the results from every step and then concatenate them once - # in the end. This is an expensive process. We are doing this because we - # do not know the number of samples beforehand. - if step_index == 0: - for _ in batch_outs: - outs.append([]) - for i, batch_out in enumerate(batch_outs): - outs[i].append(backend.get_value(batch_out)) - - if verbose == 1: - progbar.update(step_index + 1) - for i, out in enumerate(outs): - outs[i] = np.concatenate(tuple(out), axis=0) - if len(outs) == 1: - return outs[0] - return outs - - def _process_single_batch(model, inputs, targets, @@ -630,32 +227,6 @@ def _process_single_batch(model, return outs, loss, loss_metrics, aggregated_loss_metrics, masks -def _maybe_graph_function_process_single_batch(model, - inputs, - targets, - output_loss_metrics=None, - sample_weights=None, - training=False): - """Process a single batch, using defun if the model supports it.""" - if model._can_use_graph_functions: - _maybe_build_graph_functions(model) - return model._eager_process_single_batch_graph_function( - model, - inputs, - targets, - output_loss_metrics=output_loss_metrics, - sample_weights=sample_weights, - training=training) - else: - return _process_single_batch( - model, - inputs, - targets, - output_loss_metrics=output_loss_metrics, - sample_weights=sample_weights, - training=training) - - def train_on_batch(model, inputs, targets, sample_weights=None): """Calculates the loss and gradient updates for one input batch. @@ -668,25 +239,25 @@ def train_on_batch(model, inputs, targets, sample_weights=None): Returns: total loss and the loss associated with each output. """ - if len(inputs) and tensor_util.is_tensor(inputs[0]): - inputs = training_utils.cast_if_floating_dtype(inputs) - targets = training_utils.cast_if_floating_dtype(targets) - else: - inputs = [ - ops.convert_to_tensor(val, dtype=backend.floatx()) for val in inputs - ] - targets = [ - ops.convert_to_tensor(val, dtype=backend.floatx()) for val in targets - ] + if isinstance(inputs, collections.Sequence): + if len(inputs) and tensor_util.is_tensor(inputs[0]): + inputs = training_utils.cast_if_floating_dtype(inputs) + targets = training_utils.cast_if_floating_dtype(targets) + else: + inputs = [ + ops.convert_to_tensor(val, dtype=backend.floatx()) for val in inputs + ] + targets = [ + ops.convert_to_tensor(val, dtype=backend.floatx()) for val in targets + ] if sample_weights: sample_weights = [ ops.convert_to_tensor(val, dtype=backend.floatx()) if val is not None else None for val in sample_weights ] - outs, loss, loss_metrics, _, masks = \ - _maybe_graph_function_process_single_batch( - model, inputs, targets, sample_weights=sample_weights, training=True) + outs, loss, loss_metrics, _, masks = _process_single_batch( + model, inputs, targets, sample_weights=sample_weights, training=True) if not isinstance(outs, list): outs = [outs] metrics_results = _eager_metrics_fn( @@ -695,7 +266,7 @@ def train_on_batch(model, inputs, targets, sample_weights=None): targets, sample_weights=sample_weights, masks=masks, - return_stateful_result=False) + return_stateful_result=True) loss = generic_utils.to_list(loss) return [ @@ -716,22 +287,23 @@ def test_on_batch(model, inputs, targets, sample_weights=None): Returns: total loss, loss and metrics associated with each output. """ - if len(inputs) and tensor_util.is_tensor(inputs[0]): - inputs = training_utils.cast_if_floating_dtype(inputs) - targets = training_utils.cast_if_floating_dtype(targets) - else: - inputs = [ - ops.convert_to_tensor(val, dtype=backend.floatx()) for val in inputs - ] - targets = [ - ops.convert_to_tensor(val, dtype=backend.floatx()) for val in targets - ] + if isinstance(inputs, collections.Sequence): + if len(inputs) and tensor_util.is_tensor(inputs[0]): + inputs = training_utils.cast_if_floating_dtype(inputs) + targets = training_utils.cast_if_floating_dtype(targets) + else: + inputs = [ + ops.convert_to_tensor(val, dtype=backend.floatx()) for val in inputs + ] + targets = [ + ops.convert_to_tensor(val, dtype=backend.floatx()) for val in targets + ] if sample_weights: sample_weights = [ ops.convert_to_tensor(val, dtype=backend.floatx()) if val is not None else None for val in sample_weights ] - outs, loss, loss_metrics, _, masks = _maybe_graph_function_model_loss( + outs, loss, loss_metrics, _, masks = _model_loss( model, inputs, targets, sample_weights=sample_weights, training=False) if not isinstance(outs, list): outs = [outs] @@ -741,184 +313,10 @@ def test_on_batch(model, inputs, targets, sample_weights=None): targets, sample_weights=sample_weights, masks=masks, - return_stateful_result=False) + return_stateful_result=True) loss = generic_utils.to_list(loss) return [ tensor_util.constant_value(v) for v in loss + loss_metrics + metrics_results ] - - -def fit_loop(model, - inputs, - targets, - sample_weights=None, - class_weight=None, - val_inputs=None, - val_targets=None, - val_sample_weights=None, - batch_size=None, - epochs=1, - verbose=1, - callbacks=None, - shuffle=True, - initial_epoch=0, - steps_per_epoch=None, - validation_steps=None): - """Fit function for eager execution. - - Arguments: - model: Instance of the model that is being executed in Eager mode. - inputs: List of input arrays. - targets: List of target arrays. - sample_weights: Optional list of sample weight arrays. - class_weight: Optional class-weight array to weight the importance of - samples in `inputs` based on the class they belong to, as conveyed by - `targets`. - val_inputs: Input data for validation. - val_targets: Target data for validation. - val_sample_weights: Sample weight data for validation. - batch_size: Integer batch size or None if unknown. - epochs: Number of times to iterate over the data - verbose: Verbosity mode, 0, 1 or 2 - callbacks: List of callbacks to be called during training - shuffle: Whether to shuffle the data at the beginning of each epoch - initial_epoch: Epoch at which to start training - (useful for resuming a previous training run) - steps_per_epoch: Total number of steps (batches of samples) - before declaring one epoch finished and starting the - next epoch. Ignored with the default value of `None`. - validation_steps: Number of steps to run validation for (only if doing - validation from data tensors). Ignored with default value of `None`. - - Returns: - `History` object. - - Raises: - ValueError: In case of invalid argument values. - """ - # Convert training inputs to an EagerIterator - inputs, steps_per_epoch = training_utils.convert_to_iterator( - x=inputs, - y=targets, - sample_weights=sample_weights, - batch_size=batch_size, - steps_per_epoch=steps_per_epoch, - epochs=epochs, - shuffle=shuffle) - # Required for eager execution - with backend.learning_phase_scope(1): - do_validation = val_inputs is not None - callbacks = cbks.configure_callbacks( - callbacks, - model, - do_validation=do_validation, - batch_size=batch_size, - epochs=epochs, - steps_per_epoch=steps_per_epoch, - val_inputs=val_inputs, - val_targets=val_targets, - val_sample_weights=val_sample_weights, - validation_steps=validation_steps, - verbose=verbose) - - # Create metric wrapper for the losses. - output_loss_metrics = [] - for i in range(len(model.outputs)): - loss_fn = model.loss_functions[i] - mean_wrapped_loss = metrics_module.MeanMetricWrapper( - loss_fn, name=loss_fn.__name__) - output_loss_metrics.append(mean_wrapped_loss) - - callbacks.on_train_begin() - for epoch in range(initial_epoch, epochs): - if model._is_compiled: # Model may not be compiled the first time. - # Reset stateful metrics - for m in model.stateful_metric_functions: - m.reset_states() - - for m in output_loss_metrics: - m.reset_states() - - callbacks.on_epoch_begin(epoch) - epoch_logs = {} - iterator_fit_loop( - model, - inputs, - class_weight, - steps_per_epoch=steps_per_epoch, - epoch_logs=epoch_logs, - val_inputs=val_inputs, - val_targets=val_targets, - val_sample_weights=val_sample_weights, - epochs=epochs, - verbose=verbose, - callbacks=callbacks, - validation_steps=validation_steps, - do_validation=do_validation, - batch_size=batch_size, - output_loss_metrics=output_loss_metrics) - callbacks.on_epoch_end(epoch, epoch_logs) - if callbacks.model.stop_training: - break - callbacks.on_train_end() - return model.history - - -def test_loop(model, inputs, targets, - sample_weights=None, - batch_size=None, - verbose=0, - steps=None): - """Test function for eager execution. - - Arguments: - model: Model instance that is being evaluated in Eager mode. - inputs: List of input arrays. - targets: List of target arrays. - sample_weights: Optional list of sample weight arrays. - batch_size: integer batch size or `None`. - verbose: verbosity mode. - steps: Total number of steps (batches of samples) - before declaring predictions finished. - Ignored with the default value of `None`. - - Returns: - Scalar loss (if the model has a single output and no metrics) - or list of scalars (if the model has multiple outputs - and/or metrics). The attribute `model.metrics_names` will give you - the display labels for the scalar outputs. - """ - inputs, steps = training_utils.convert_to_iterator( - x=inputs, - y=targets, - sample_weights=sample_weights, - batch_size=batch_size, - steps_per_epoch=steps, - is_validation=True) - with backend.learning_phase_scope(0): - return iterator_test_loop(model, inputs, steps, verbose=verbose) - - -def predict_loop(model, inputs, batch_size=32, verbose=0, steps=None): - """Predict function for eager execution. - - Arguments: - model: Instance of `Model`. - inputs: List of input arrays. - batch_size: integer batch size. - verbose: verbosity mode. - steps: Total number of steps (batches of samples) - before declaring `_predict_loop` finished. - Ignored with the default value of `None`. - - Returns: - Array of predictions (if the model has a single output) - or list of arrays of predictions - (if the model has multiple outputs). - """ - with backend.learning_phase_scope(0): - inputs, steps = training_utils.convert_to_iterator( - x=inputs, batch_size=batch_size, steps_per_epoch=steps) - return iterator_predict_loop(model, inputs, steps, verbose=verbose) diff --git a/tensorflow/python/keras/engine/training_eager_test.py b/tensorflow/python/keras/engine/training_eager_test.py index 76aaf1643b..3fabbb17ed 100644 --- a/tensorflow/python/keras/engine/training_eager_test.py +++ b/tensorflow/python/keras/engine/training_eager_test.py @@ -20,10 +20,10 @@ from __future__ import print_function import numpy as np -from tensorflow.python.data.ops import dataset_ops from tensorflow.python import keras +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.eager import context from tensorflow.python.framework import ops -from tensorflow.python.framework import test_util as tf_test_util from tensorflow.python.keras import metrics as metrics_module from tensorflow.python.platform import test from tensorflow.python.training.rmsprop import RMSPropOptimizer @@ -51,6 +51,7 @@ class TrainingTest(test.TestCase): loss, metrics=metrics, loss_weights=loss_weights, + run_eagerly=True, sample_weight_mode=None) input_a = keras.backend.zeros(shape=(10, 3)) @@ -111,7 +112,7 @@ class TrainingTest(test.TestCase): optimizer = RMSPropOptimizer(learning_rate=0.001) loss = 'mse' metrics = ['mae', metrics_module.CategoricalAccuracy()] - model.compile(optimizer, loss, metrics=metrics) + model.compile(optimizer, loss, metrics=metrics, run_eagerly=True) inputs = keras.backend.zeros(shape=(10, 3)) targets = keras.backend.zeros(shape=(10, 4)) @@ -129,29 +130,34 @@ class TrainingTest(test.TestCase): x = keras.layers.Input(shape=(3,), name='input') y = keras.layers.Dense(4, name='dense')(x) model = keras.Model(x, y) - model.compile(optimizer=RMSPropOptimizer(learning_rate=0.001), loss='mse') + model.compile(optimizer=RMSPropOptimizer(learning_rate=0.001), + loss='mse', + run_eagerly=True) x = keras.backend.zeros(shape=(10, 3)) y = keras.backend.zeros(shape=(10, 4)) dataset = dataset_ops.Dataset.from_tensor_slices((x, y)).repeat(10).batch(5) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) validation_dataset = dataset_ops.Dataset.from_tensor_slices( (x, y)).repeat(10).batch(5) - validation_iterator = validation_dataset.make_one_shot_iterator() + validation_iterator = dataset_ops.make_one_shot_iterator(validation_dataset) with self.assertRaisesRegexp( ValueError, r'specify .* `steps_per_epoch`'): model.fit(iterator, epochs=1, verbose=0) - with self.assertRaisesRegexp( - ValueError, r'provide either `batch_size` or `validation_steps`'): - model.fit(iterator, steps_per_epoch=2, epochs=1, verbose=0, - validation_data=(x, y)) - with self.assertRaisesRegexp( - ValueError, r'provide either `batch_size` or `validation_steps`'): + if not context.executing_eagerly(): + # In eager execution, `keras.backend.zeros` returns value tensors + # which can be used for validation without a `validation_steps` argument. + with self.assertRaisesRegexp( + ValueError, r'provide either `batch_size` or `validation_steps`'): + model.fit(iterator, steps_per_epoch=2, epochs=1, verbose=0, + validation_data=(x, y)) + with self.assertRaisesRegexp(ValueError, + 'specify the `validation_steps` argument.'): model.fit(iterator, steps_per_epoch=2, epochs=1, verbose=0, validation_data=validation_dataset) - with self.assertRaisesRegexp( - ValueError, r'provide either `batch_size` or `validation_steps`'): + with self.assertRaisesRegexp(ValueError, + 'specify the `validation_steps` argument.'): model.fit(iterator, steps_per_epoch=2, epochs=1, verbose=0, validation_data=validation_iterator) @@ -160,25 +166,31 @@ class TrainingTest(test.TestCase): model.add(keras.layers.Dense(4, input_shape=(3,))) optimizer = RMSPropOptimizer(learning_rate=0.001) model.compile( - optimizer, 'mse', metrics=['mae', - metrics_module.CategoricalAccuracy()]) + optimizer, + loss='mse', + metrics=['mae', metrics_module.CategoricalAccuracy()], + run_eagerly=True) x = np.random.random((10, 3)) y = np.random.random((10, 4)) - def iterator(): + def numpy_iterator(): while True: yield x, y - model.fit_generator(iterator(), steps_per_epoch=3, epochs=1) - model.evaluate_generator(iterator(), steps=3) - out = model.predict_generator(iterator(), steps=3) + model.fit_generator(numpy_iterator(), steps_per_epoch=3, epochs=1) + model.evaluate_generator(numpy_iterator(), steps=3) + + def inference_numpy_iterator(): + while True: + yield x + + out = model.predict_generator(inference_numpy_iterator(), steps=3) self.assertEqual(out.shape, (30, 4)) class CorrectnessTest(test.TestCase): - @tf_test_util.run_in_graph_and_eager_modes def test_loss_correctness(self): # Test that training loss is the same in eager and graph # (by comparing it to a reference value in a deterministic case) @@ -191,14 +203,14 @@ class CorrectnessTest(test.TestCase): activation='softmax', kernel_initializer='ones')) model.compile(loss='sparse_categorical_crossentropy', - optimizer=RMSPropOptimizer(learning_rate=0.001)) + optimizer=RMSPropOptimizer(learning_rate=0.001), + run_eagerly=False) x = np.ones((100, 4)) np.random.seed(123) y = np.random.randint(0, 1, size=(100, 1)) history = model.fit(x, y, epochs=1, batch_size=10) self.assertAlmostEqual(history.history['loss'][-1], 0.6173, 4) - @tf_test_util.run_in_graph_and_eager_modes def test_loss_correctness_with_iterator(self): # Test that training loss is the same in eager and graph # (by comparing it to a reference value in a deterministic case) @@ -210,14 +222,15 @@ class CorrectnessTest(test.TestCase): keras.layers.Dense(2, activation='softmax', kernel_initializer='ones')) model.compile( loss='sparse_categorical_crossentropy', - optimizer=RMSPropOptimizer(learning_rate=0.001)) + optimizer=RMSPropOptimizer(learning_rate=0.001), + run_eagerly=True) x = np.ones((100, 4), dtype=np.float32) np.random.seed(123) y = np.random.randint(0, 1, size=(100, 1)) dataset = dataset_ops.Dataset.from_tensor_slices((x, y)) dataset = dataset.repeat(100) dataset = dataset.batch(10) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) history = model.fit(iterator, epochs=1, steps_per_epoch=10) self.assertAlmostEqual(history.history['loss'][-1], 0.6173, 4) diff --git a/tensorflow/python/keras/engine/training_generator.py b/tensorflow/python/keras/engine/training_generator.py index b5e3a03976..0abf0b8270 100644 --- a/tensorflow/python/keras/engine/training_generator.py +++ b/tensorflow/python/keras/engine/training_generator.py @@ -19,412 +19,433 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import functools +import math + import numpy as np +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.data.ops import iterator_ops from tensorflow.python.eager import context +from tensorflow.python.framework import errors +from tensorflow.python.keras import backend from tensorflow.python.keras import callbacks as cbks -from tensorflow.python.keras.utils.data_utils import GeneratorEnqueuer -from tensorflow.python.keras.utils.data_utils import iter_sequence_infinite -from tensorflow.python.keras.utils.data_utils import OrderedEnqueuer -from tensorflow.python.keras.utils.data_utils import Sequence -from tensorflow.python.keras.utils.generic_utils import Progbar +from tensorflow.python.keras.engine import training_utils +from tensorflow.python.keras.utils import data_utils +from tensorflow.python.keras.utils import generic_utils from tensorflow.python.platform import tf_logging as logging +from tensorflow.python.util import nest + + +def model_iteration(model, + data, + steps_per_epoch=None, + epochs=1, + verbose=1, + callbacks=None, + validation_data=None, + validation_steps=None, + class_weight=None, + max_queue_size=10, + workers=1, + use_multiprocessing=False, + shuffle=True, + initial_epoch=0, + mode='train', + batch_size=None, + **kwargs): + """Loop function for arrays of data with modes 'train'/'test'/'predict'. + + Arguments: + model: Keras Model instance. + data: Either a tuple of NumPy/Tensor inputs (i.e. `(x,)` or `(x, y)` or + `(x, y, sample_weights)`) or a generator or + `keras.utils.data_utils.Sequence` object or Eager Iterator or Dataset. + steps_per_epoch: Total number of steps (batches of samples) before + declaring one epoch finished and starting the next epoch. Ignored with + the default value of `None`. + epochs: Number of times to iterate over the data. + verbose: Verbosity mode, 0, 1 or 2. + callbacks: List of callbacks to be called during training. + validation_data: Either a tuple of NumPy/Tensor inputs (i.e. `(x,)` or + `(x, y)` or `(x, y, sample_weights)`) or a generator or + `keras.utils.data_utils.Sequence` object or Eager Iterator or Dataset. + validation_steps: Total number of steps (batches of samples) before + declaring validation finished. + class_weight: Dictionary mapping class indices to a weight for the class. + max_queue_size: Integer. Maximum size for the generator queue. If + unspecified, `max_queue_size` will default to 10. + workers: Integer. Maximum number of processes to spin up when using + process-based threading. If unspecified, `workers` will default to 1. If + 0, will execute the generator on the main thread. + use_multiprocessing: Boolean. If `True`, use process-based threading. If + unspecified, `use_multiprocessing` will default to `False`. Note that + because this implementation relies on multiprocessing, you should not + pass non-picklable arguments to the generator as they can't be passed + easily to children processes. + shuffle: Boolean. Whether to shuffle the order of the batches at the + beginning of each epoch. Only used with instances of `Sequence` + (`keras.utils.Sequence`). Has no effect when `steps_per_epoch` is not + `None`. + initial_epoch: Epoch at which to start training (useful for resuming a + previous training run). + mode: One of 'train'/'test'/'predict'. + batch_size: Integer batch size or None if unknown. Will only be used if + `data` is in NumPy/Tensor format. + **kwargs: Additional arguments for backwards compatibility. `steps` is + accepted as an alias for `steps_per_epoch`. + + Returns: + - In 'train' mode: `History` object. + - In 'test' mode: Evaluation metrics. + - In 'predict' mode: Outputs of the Model called on inputs. + + Raises: + ValueError: in case of invalid arguments. + """ + if 'steps' in kwargs: + steps_per_epoch = kwargs['steps'] + + # Convert to a format that supports `next(generator)`. + generator, steps_per_epoch = convert_to_generator_like( + data, + steps_per_epoch=steps_per_epoch, + batch_size=batch_size, + epochs=epochs - initial_epoch, + shuffle=shuffle) + + do_validation = validation_data is not None + should_set_learning_phase = context.executing_eagerly() and model.run_eagerly + is_sequence = isinstance(generator, data_utils.Sequence) + _validate_arguments(is_sequence, use_multiprocessing, workers, + steps_per_epoch, validation_data, validation_steps, mode, + kwargs) + + batch_function = _make_execution_function( + model, mode, class_weight=class_weight) + + # Create the queue for the generator. + output_generator, enqueuer = _make_enqueued_generator( + generator, + workers=workers, + use_multiprocessing=use_multiprocessing, + max_queue_size=max_queue_size, + shuffle=shuffle) + + num_samples_or_steps, use_steps = _get_num_samples_or_steps( + data, steps_per_epoch) + + count_mode = 'steps' if use_steps else 'samples' + callbacks = cbks.configure_callbacks( + callbacks, + model, + do_validation=do_validation, + epochs=epochs, + steps_per_epoch=steps_per_epoch, + batch_size=batch_size, + samples=num_samples_or_steps, + verbose=0, # Handle ProgBar as part of Callbacks once hooks are ready. + mode=mode) + # TODO(omalleyt): Handle ProgBar as part of Callbacks once hooks are ready. + progbar = training_utils.get_progbar(model, count_mode) + progbar.params = callbacks.params + progbar.params['verbose'] = verbose + + if mode == 'predict': + aggregator = training_utils.OutputsAggregator(True, steps_per_epoch) + else: + aggregator = training_utils.MetricsAggregator(True, steps_per_epoch) + if should_set_learning_phase: + old_learning_phase = backend.learning_phase() + backend.set_learning_phase(1 if mode == 'train' else 0) -def fit_generator(model, - generator, - steps_per_epoch=None, - epochs=1, - verbose=1, - callbacks=None, - validation_data=None, - validation_steps=None, - class_weight=None, - max_queue_size=10, - workers=1, - use_multiprocessing=False, - shuffle=True, - initial_epoch=0): - """See docstring for `Model.fit_generator`.""" - epoch = initial_epoch - - do_validation = bool(validation_data) - if not context.executing_eagerly(): - model._make_train_function() - if do_validation: - model._make_test_function() - - is_sequence = isinstance(generator, Sequence) - if not is_sequence and use_multiprocessing and workers > 1: - logging.warning( - UserWarning('Using a generator with `use_multiprocessing=True`' - ' and multiple workers may duplicate your data.' - ' Please consider using the`keras.utils.Sequence' - ' class.')) - if steps_per_epoch is None: - if is_sequence: - steps_per_epoch = len(generator) - else: - raise ValueError('`steps_per_epoch=None` is only valid for a' - ' generator based on the `keras.utils.Sequence`' - ' class. Please specify `steps_per_epoch` or use' - ' the `keras.utils.Sequence` class.') + callbacks.model.stop_training = False + callbacks._call_begin_hook(mode) + progbar.on_train_begin() + for epoch in range(initial_epoch, epochs): + if callbacks.model.stop_training: + break - # python 2 has 'next', 3 has '__next__' - # avoid any explicit version checks - val_gen = ( - hasattr(validation_data, 'next') or - hasattr(validation_data, '__next__') or - isinstance(validation_data, Sequence)) - if (val_gen and not isinstance(validation_data, Sequence) and - not validation_steps): - raise ValueError('`validation_steps=None` is only valid for a' - ' generator based on the `keras.utils.Sequence`' - ' class. Please specify `validation_steps` or use' - ' the `keras.utils.Sequence` class.') + # Setup work for each epoch. + model.reset_metrics() + epoch_logs = {} + callbacks.on_epoch_begin(epoch, epoch_logs, mode=mode) + progbar.on_epoch_begin(epoch, epoch_logs) - enqueuer = None - val_enqueuer = None + for step in range(steps_per_epoch): + batch_data = _get_next_batch(output_generator, mode) + if batch_data is None: + callbacks.model.stop_training = True + break - try: - val_x, val_y, val_sample_weights = validation_data, None, None - if do_validation and not val_gen: - # Prepare data for validation - if len(validation_data) == 2: - val_x, val_y = validation_data # pylint: disable=unpacking-non-sequence - val_sample_weights = None - elif len(validation_data) == 3: - val_x, val_y, val_sample_weights = validation_data # pylint: disable=unpacking-non-sequence - else: - raise ValueError( - '`validation_data` should be a tuple ' - '`(val_x, val_y, val_sample_weight)` ' - 'or `(val_x, val_y)`. Found: ' + str(validation_data)) - val_x, val_y, val_sample_weights = model._standardize_user_data( - val_x, val_y, val_sample_weights) - - callbacks = cbks.configure_callbacks( - callbacks, - model, - do_validation=do_validation, - val_inputs=val_x, - val_targets=val_y, - val_sample_weights=val_sample_weights, - epochs=epochs, - validation_steps=validation_steps, - steps_per_epoch=steps_per_epoch, - verbose=verbose) - - if workers > 0: - if is_sequence: - enqueuer = OrderedEnqueuer( - generator, - use_multiprocessing=use_multiprocessing, - shuffle=shuffle) - else: - enqueuer = GeneratorEnqueuer( - generator, - use_multiprocessing=use_multiprocessing) - enqueuer.start(workers=workers, max_queue_size=max_queue_size) - output_generator = enqueuer.get() - else: - if is_sequence: - output_generator = iter_sequence_infinite(generator) - else: - output_generator = generator + # `batch_size` used for validation data if validation + # data is NumPy/EagerTensors. + batch_size = int(nest.flatten(batch_data)[0].shape[0]) + + # Callbacks batch begin. + batch_logs = {'batch': step, 'size': batch_size} + callbacks._call_batch_hook(mode, 'begin', step, batch_logs) + progbar.on_batch_begin(step, batch_logs) + + batch_outs = batch_function(*batch_data) + if not isinstance(batch_outs, list): + batch_outs = [batch_outs] + + # Aggregate results. + if step == 0: + aggregator.create(batch_outs) + aggregator.aggregate(batch_outs) + + # Callbacks batch end. + batch_logs.update(training_utils.make_logs(model, batch_outs, mode)) + callbacks._call_batch_hook(mode, 'end', step, batch_logs) + progbar.on_batch_end(step, batch_logs) - callbacks.on_train_begin() - # Construct epoch logs. - epoch_logs = {} - while epoch < epochs: - for m in model.stateful_metric_functions: - m.reset_states() - callbacks.on_epoch_begin(epoch) - steps_done = 0 - batch_index = 0 - while steps_done < steps_per_epoch: - generator_output = next(output_generator) - - if not hasattr(generator_output, '__len__'): - raise ValueError('Output of generator should be ' - 'a tuple `(x, y, sample_weight)` ' - 'or `(x, y)`. Found: ' + str(generator_output)) - - if len(generator_output) == 2: - x, y = generator_output - sample_weight = None - elif len(generator_output) == 3: - x, y, sample_weight = generator_output - else: - raise ValueError('Output of generator should be ' - 'a tuple `(x, y, sample_weight)` ' - 'or `(x, y)`. Found: ' + str(generator_output)) - # build batch logs - batch_logs = {} - if isinstance(x, list): - batch_size = x[0].shape[0] - elif isinstance(x, dict): - batch_size = list(x.values())[0].shape[0] - else: - batch_size = x.shape[0] - batch_logs['batch'] = batch_index - batch_logs['size'] = batch_size - callbacks.on_batch_begin(batch_index, batch_logs) - - outs = model.train_on_batch( - x, y, sample_weight=sample_weight, class_weight=class_weight) - - if not isinstance(outs, list): - outs = [outs] - for l, o in zip(model.metrics_names, outs): - batch_logs[l] = o - - callbacks.on_batch_end(batch_index, batch_logs) - - batch_index += 1 - steps_done += 1 - - # Epoch finished. - if steps_done >= steps_per_epoch and do_validation: - if val_gen: - val_outs = evaluate_generator( - model, - validation_data, - validation_steps, - workers=workers, - use_multiprocessing=use_multiprocessing, - max_queue_size=max_queue_size) - else: - # No need for try/except because - # data has already been validated. - val_outs = model.evaluate( - val_x, - val_y, - batch_size=batch_size, - sample_weight=val_sample_weights, - verbose=0) - if not isinstance(val_outs, list): - val_outs = [val_outs] - # Same labels assumed. - for l, o in zip(model.metrics_names, val_outs): - epoch_logs['val_' + l] = o - - if callbacks.model.stop_training: - break - - callbacks.on_epoch_end(epoch, epoch_logs) - epoch += 1 if callbacks.model.stop_training: break - finally: - try: - if enqueuer is not None: - enqueuer.stop() - finally: - if val_enqueuer is not None: - val_enqueuer.stop() - - callbacks.on_train_end() - return model.history - - -def evaluate_generator(model, - generator, - steps=None, - max_queue_size=10, - workers=1, - use_multiprocessing=False, - verbose=0): - """See docstring for `Model.evaluate_generator`.""" - if not context.executing_eagerly(): - model._make_test_function() - - if hasattr(model, 'metrics'): - for m in model.stateful_metric_functions: - m.reset_states() - - steps_done = 0 - all_outs = [] - batch_sizes = [] - is_sequence = isinstance(generator, Sequence) - if not is_sequence and use_multiprocessing and workers > 1: - logging.warning( - UserWarning('Using a generator with `use_multiprocessing=True`' - ' and multiple workers may duplicate your data.' - ' Please consider using the`keras.utils.Sequence' - ' class.')) - if steps is None: - if is_sequence: - steps = len(generator) - else: - raise ValueError('`steps=None` is only valid for a generator' - ' based on the `keras.utils.Sequence` class.' - ' Please specify `steps` or use the' - ' `keras.utils.Sequence` class.') - enqueuer = None - + aggregator.finalize() + results = aggregator.results + epoch_logs.update(training_utils.make_logs(model, results, mode)) + if len(results) == 1: + results = results[0] + + # Run the test loop every epoch during training. + if do_validation and not callbacks.model.stop_training: + val_results = model_iteration( + model, + validation_data, + steps_per_epoch=validation_steps, + batch_size=batch_size, + class_weight=class_weight, + workers=workers, + use_multiprocessing=use_multiprocessing, + max_queue_size=max_queue_size, + mode='test') + + if not isinstance(val_results, list): + val_results = [val_results] + epoch_logs.update( + training_utils.make_logs(model, val_results, mode, prefix='val_')) + + callbacks.on_epoch_end(epoch, epoch_logs, mode=mode) + progbar.on_epoch_end(epoch, epoch_logs) + callbacks._call_end_hook(mode) + + if enqueuer is not None: + enqueuer.stop() + + if should_set_learning_phase: + backend.set_learning_phase(old_learning_phase) + + if mode == 'train': + return model.history + return results + + +# Maintain compatibility with the existing names. +fit_generator = functools.partial(model_iteration, mode='train') +evaluate_generator = functools.partial(model_iteration, mode='test') +predict_generator = functools.partial(model_iteration, mode='predict') + + +def _get_next_batch(output_generator, mode): + """Retrieves the next batch of input data.""" try: - if workers > 0: - if is_sequence: - enqueuer = OrderedEnqueuer( - generator, use_multiprocessing=use_multiprocessing) - else: - enqueuer = GeneratorEnqueuer( - generator, - use_multiprocessing=use_multiprocessing) - enqueuer.start(workers=workers, max_queue_size=max_queue_size) - output_generator = enqueuer.get() + generator_output = next(output_generator) + except (errors.OutOfRangeError, StopIteration): + # Returning `None` will trigger looping to stop. + logging.warning('Your dataset iterator ran out of data.') + return None + if not isinstance(generator_output, tuple): + if mode == 'predict': + # Always wrap in a tuple. + return (generator_output,) else: - if is_sequence: - output_generator = iter_sequence_infinite(generator) - else: - output_generator = generator - - if verbose == 1: - progbar = Progbar(target=steps) - - while steps_done < steps: - generator_output = next(output_generator) - if not hasattr(generator_output, '__len__'): - raise ValueError('Output of generator should be a tuple ' - '(x, y, sample_weight) ' - 'or (x, y). Found: ' + str(generator_output)) - if len(generator_output) == 2: - x, y = generator_output - sample_weight = None - elif len(generator_output) == 3: - x, y, sample_weight = generator_output - else: - raise ValueError('Output of generator should be a tuple ' - '(x, y, sample_weight) ' - 'or (x, y). Found: ' + str(generator_output)) - outs = model.test_on_batch(x, y, sample_weight=sample_weight) - - if isinstance(x, list): - batch_size = x[0].shape[0] - elif isinstance(x, dict): - batch_size = list(x.values())[0].shape[0] - else: - batch_size = x.shape[0] - if batch_size == 0: - raise ValueError('Received an empty batch. ' - 'Batches should at least contain one item.') - all_outs.append(outs) - - steps_done += 1 - batch_sizes.append(batch_size) - if verbose == 1: - progbar.update(steps_done) - - finally: - if enqueuer is not None: - enqueuer.stop() - - if not isinstance(outs, list): - return np.average(np.asarray(all_outs), weights=batch_sizes) - else: - averages = [float(all_outs[-1][0])] # index 0 = 'loss' - averages.extend([ - np.average([out[i] - for out in all_outs], weights=batch_sizes) - for i in range(1, len(outs)) - ]) - return averages - - -def predict_generator(model, - generator, - steps=None, - max_queue_size=10, - workers=1, - use_multiprocessing=False, - verbose=0): - """See docstring for `Model.predict_generator`.""" - if not context.executing_eagerly(): - model._make_predict_function() - - steps_done = 0 - all_outs = [] - is_sequence = isinstance(generator, Sequence) + raise ValueError('Output of generator should be ' + 'a tuple `(x, y, sample_weight)` ' + 'or `(x, y)`. Found: ' + str(generator_output)) + + if len(generator_output) < 1 or len(generator_output) > 3: + raise ValueError('Output of generator should be ' + 'a tuple `(x, y, sample_weight)` ' + 'or `(x, y)` or (x,). Found: ' + str(generator_output)) + return generator_output + + +def _validate_arguments(is_sequence, use_multiprocessing, workers, + steps_per_epoch, validation_data, validation_steps, + mode, kwargs): + """Raises errors if arguments are invalid. + + Arguments: + is_sequence: Boolean, whether data is a `keras.utils.data_utils.Sequence` + instance. + use_multiprocessing: Boolean. If `True`, use process-based threading. If + unspecified, `use_multiprocessing` will default to `False`. Note that + because this implementation relies on multiprocessing, you should not pass + non-picklable arguments to the generator as they can't be passed easily to + children processes. + workers: Integer. Maximum number of processes to spin up when using + process-based threading. If unspecified, `workers` will default to 1. If + 0, will execute the generator on the main thread. + steps_per_epoch: Total number of steps (batches of samples) before declaring + one epoch finished and starting the next epoch. Ignored with the default + value of `None`. + validation_data: Either a tuple of NumPy/Tensor inputs (i.e. `(x,)` or `(x, + y)` or `(x, y, sample_weights)`) or a generator or + `keras.utils.data_utils.Sequence` object or Eager Iterator or Dataset. + validation_steps: Total number of steps (batches of samples) before + declaring validation finished. + mode: One of 'train'/'test'/'predict'. + kwargs: Additional arguments for backwards compatibility. + + Raises: + ValueError: If `steps_per_epoch` or `validation_steps` are not passed + for data types that require them, or if unrecognized keyword + arguments are passed. + """ if not is_sequence and use_multiprocessing and workers > 1: logging.warning( UserWarning('Using a generator with `use_multiprocessing=True`' ' and multiple workers may duplicate your data.' - ' Please consider using the`keras.utils.Sequence' + ' Please consider using the `keras.utils.Sequence`' ' class.')) - if steps is None: - if is_sequence: - steps = len(generator) - else: - raise ValueError('`steps=None` is only valid for a generator' - ' based on the `keras.utils.Sequence` class.' - ' Please specify `steps` or use the' - ' `keras.utils.Sequence` class.') - enqueuer = None - try: - if workers > 0: - if is_sequence: - enqueuer = OrderedEnqueuer( - generator, use_multiprocessing=use_multiprocessing) - else: - enqueuer = GeneratorEnqueuer( - generator, - use_multiprocessing=use_multiprocessing) - enqueuer.start(workers=workers, max_queue_size=max_queue_size) - output_generator = enqueuer.get() + if steps_per_epoch is None: + arg_name = 'steps_per_epoch' if mode == 'train' else 'steps' + raise ValueError('Please specify the number of steps via the ' + '`{}` argument.'.format(arg_name)) + + val_gen = ( + data_utils.is_generator_or_sequence(validation_data) or + isinstance(validation_data, iterator_ops.EagerIterator) or + isinstance(validation_data, dataset_ops.DatasetV2)) + if (val_gen and not isinstance(validation_data, data_utils.Sequence) and + not validation_steps): + raise ValueError('Please specify the `validation_steps` argument.') + + if any(k != 'steps' for k in kwargs): + raise ValueError('Invalid arguments passed: {}'.format( + [k for k in kwargs if k != 'steps'])) + + +def convert_to_generator_like(data, + batch_size=None, + steps_per_epoch=None, + epochs=1, + shuffle=False): + """Make a generator out of NumPy or EagerTensor inputs. + + Arguments: + data: Either a generator or `keras.utils.data_utils.Sequence` object or + `Dataset` or `EagerIterator` or a {1,2,3}-tuple of NumPy arrays or + EagerTensors. If a tuple, the elements represent `(x, y, sample_weights)` + and may be `None` or `[None]`. + batch_size: Used when creating a generator out of tuples of NumPy arrays or + EagerTensors. + steps_per_epoch: Steps of the generator to run each epoch. + epochs: Total number of epochs to run. + shuffle: Whether the data should be shuffled. + + Returns: + - Generator or `keras.utils.data_utils.Sequence` or EagerIterator. + + Raises: + - ValueError: If `batch_size` is not provided for NumPy or EagerTensor + inputs. + """ + if isinstance(data, tuple): + # Scrub `Nones` that might have been passed for `targets`, `sample_weights`. + data = tuple( + ele for ele in data if not all(e is None for e in nest.flatten(ele))) + if len(data) == 1: + data = data[0] + + if data_utils.is_generator_or_sequence(data) or isinstance( + data, iterator_ops.EagerIterator): + if isinstance(data, data_utils.Sequence): + steps_per_epoch = len(data) + return data, steps_per_epoch + if isinstance(data, dataset_ops.DatasetV2): + return dataset_ops.make_one_shot_iterator(data), steps_per_epoch + + # Create generator from NumPy or EagerTensor Input. + num_samples = int(nest.flatten(data)[0].shape[0]) + if batch_size is None: + raise ValueError('You must specify `batch_size`') + steps_per_epoch = int(math.ceil(num_samples / batch_size)) + + def _gen(data): + """Makes a generator out of a structure of NumPy/EagerTensors.""" + index_array = np.arange(num_samples) + for _ in range(epochs): + if shuffle: + np.random.shuffle(index_array) + batches = generic_utils.make_batches(num_samples, batch_size) + for (batch_start, batch_end) in batches: + batch_ids = index_array[batch_start:batch_end] + flat_batch_data = training_utils.slice_arrays( + nest.flatten(data), batch_ids, contiguous=(not shuffle)) + yield nest.pack_sequence_as(data, flat_batch_data) + + return _gen(data), steps_per_epoch + + +def _make_enqueued_generator(generator, + workers=1, + use_multiprocessing=False, + max_queue_size=10, + shuffle=False): + """Create a buffered queue of next elements of the generator.""" + is_sequence = isinstance(generator, data_utils.Sequence) + enqueuer = None + if workers > 0: + if is_sequence: + enqueuer = data_utils.OrderedEnqueuer( + generator, use_multiprocessing=use_multiprocessing, shuffle=shuffle) else: - if is_sequence: - output_generator = iter_sequence_infinite(generator) - else: - output_generator = generator - - if verbose == 1: - progbar = Progbar(target=steps) - - while steps_done < steps: - generator_output = next(output_generator) - if isinstance(generator_output, tuple): - # Compatibility with the generators - # used for training. - if len(generator_output) == 2: - x, _ = generator_output - elif len(generator_output) == 3: - x, _, _ = generator_output - else: - raise ValueError('Output of generator should be ' - 'a tuple `(x, y, sample_weight)` ' - 'or `(x, y)`. Found: ' + str(generator_output)) - else: - # Assumes a generator that only - # yields inputs (not targets and sample weights). - x = generator_output - - outs = model.predict_on_batch(x) - if not isinstance(outs, list): - outs = [outs] - - if not all_outs: - for out in outs: - all_outs.append([]) - - for i, out in enumerate(outs): - all_outs[i].append(out) - steps_done += 1 - if verbose == 1: - progbar.update(steps_done) - - finally: - if enqueuer is not None: - enqueuer.stop() - - if len(all_outs) == 1: - if steps_done == 1: - return all_outs[0][0] + enqueuer = data_utils.GeneratorEnqueuer( + generator, use_multiprocessing=use_multiprocessing) + enqueuer.start(workers=workers, max_queue_size=max_queue_size) + output_generator = enqueuer.get() + else: + if is_sequence: + output_generator = data_utils.iter_sequence_infinite(generator) else: - return np.concatenate(all_outs[0]) - if steps_done == 1: - return [out[0] for out in all_outs] + output_generator = generator + return output_generator, enqueuer + + +def _make_execution_function(model, mode, class_weight=None): + """Makes function to run one step of model execution.""" + if mode == 'train': + if not context.executing_eagerly(): + model._make_fit_function() + f = functools.partial(model.train_on_batch, class_weight=class_weight) + elif mode == 'test': + if not context.executing_eagerly(): + model._make_eval_function() + f = model.test_on_batch else: - return [np.concatenate(out) for out in all_outs] + # Match signature of other modes to allow + # 1, 2, or 3-tuples from generator + def predict_on_batch(x, y=None, sample_weights=None): # pylint: disable=unused-argument + return model.predict_on_batch(x) + + f = predict_on_batch + + # Maintain stateful metrics across batch-level calls. + if mode != 'predict': + f = functools.partial(f, reset_metrics=False) + + return f + + +def _get_num_samples_or_steps(data, steps_per_epoch): + """Returns number of samples or steps, and whether to use steps count mode.""" + flat_inputs = nest.flatten(data) + if hasattr(flat_inputs[0], 'shape'): + return int(flat_inputs[0].shape[0]), False + return steps_per_epoch, True diff --git a/tensorflow/python/keras/engine/training_generator_test.py b/tensorflow/python/keras/engine/training_generator_test.py index 88e8943424..8941428e43 100644 --- a/tensorflow/python/keras/engine/training_generator_test.py +++ b/tensorflow/python/keras/engine/training_generator_test.py @@ -21,220 +21,274 @@ from __future__ import print_function import os import unittest +from absl.testing import parameterized import numpy as np from tensorflow.python import keras +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.data.ops import iterator_ops +from tensorflow.python.eager import context from tensorflow.python.framework import test_util as tf_test_util from tensorflow.python.keras import metrics as metrics_module +from tensorflow.python.keras import testing_utils +from tensorflow.python.keras.engine import training_generator from tensorflow.python.platform import test from tensorflow.python.training.rmsprop import RMSPropOptimizer +from tensorflow.python.util import nest + + +def custom_generator(mode=2): + batch_size = 10 + num_samples = 50 + arr_data = np.random.random((num_samples, 2)) + arr_labels = np.random.random((num_samples, 4)) + arr_weights = np.random.random((num_samples,)) + i = 0 + while True: + batch_index = i * batch_size % num_samples + i += 1 + start = batch_index + end = start + batch_size + x = arr_data[start: end] + y = arr_labels[start: end] + w = arr_weights[start: end] + if mode == 1: + yield x + elif mode == 2: + yield x, y + else: + yield x, y, w + + +@tf_test_util.run_all_in_graph_and_eager_modes +class TestGeneratorMethods(test.TestCase, parameterized.TestCase): - -class TestGeneratorMethods(test.TestCase): + @unittest.skipIf( + os.name == 'nt', + 'use_multiprocessing=True does not work on windows properly.') + @parameterized.parameters('sequential', 'functional') + def test_fit_generator_method(self, model_type): + if model_type == 'sequential': + model = testing_utils.get_small_sequential_mlp( + num_hidden=3, num_classes=4, input_dim=2) + else: + model = testing_utils.get_small_functional_mlp( + num_hidden=3, num_classes=4, input_dim=2) + model.compile( + loss='mse', + optimizer='sgd', + metrics=['mae', metrics_module.CategoricalAccuracy()]) + + model.fit_generator(custom_generator(), + steps_per_epoch=5, + epochs=1, + verbose=1, + max_queue_size=10, + workers=4, + use_multiprocessing=True) + model.fit_generator(custom_generator(), + steps_per_epoch=5, + epochs=1, + verbose=1, + max_queue_size=10, + use_multiprocessing=False) + model.fit_generator(custom_generator(), + steps_per_epoch=5, + epochs=1, + verbose=1, + max_queue_size=10, + use_multiprocessing=False, + validation_data=custom_generator(), + validation_steps=10) + model.fit_generator(custom_generator(), + steps_per_epoch=5, + validation_data=custom_generator(), + validation_steps=1, + workers=0) @unittest.skipIf( os.name == 'nt', 'use_multiprocessing=True does not work on windows properly.') - def test_generator_methods(self): - arr_data = np.random.random((50, 2)) - arr_labels = np.random.random((50,)) + @parameterized.parameters('sequential', 'functional') + def test_evaluate_generator_method(self, model_type): + if model_type == 'sequential': + model = testing_utils.get_small_sequential_mlp( + num_hidden=3, num_classes=4, input_dim=2) + else: + model = testing_utils.get_small_functional_mlp( + num_hidden=3, num_classes=4, input_dim=2) + model.compile( + loss='mse', + optimizer='sgd', + metrics=['mae', metrics_module.CategoricalAccuracy()]) + model.summary() + + model.evaluate_generator(custom_generator(), + steps=5, + max_queue_size=10, + workers=2, + verbose=1, + use_multiprocessing=True) + model.evaluate_generator(custom_generator(), + steps=5, + max_queue_size=10, + use_multiprocessing=False) + model.evaluate_generator(custom_generator(), + steps=5, + max_queue_size=10, + use_multiprocessing=False, + workers=0) - def custom_generator(): - batch_size = 10 - num_samples = 50 - while True: - batch_index = np.random.randint(0, num_samples - batch_size) - start = batch_index - end = start + batch_size - x = arr_data[start: end] - y = arr_labels[start: end] - yield x, y - - with self.cached_session(): - x = keras.Input((2,)) - y = keras.layers.Dense(1)(x) - fn_model = keras.models.Model(x, y) - fn_model.compile( - loss='mse', - optimizer='sgd', - metrics=['mae', metrics_module.CategoricalAccuracy()]) - - seq_model = keras.models.Sequential() - seq_model.add(keras.layers.Dense(1, input_shape=(2,))) - seq_model.compile(loss='mse', optimizer='sgd') - - for model in [fn_model, seq_model]: - model.fit_generator(custom_generator(), - steps_per_epoch=5, - epochs=1, - verbose=1, + @unittest.skipIf( + os.name == 'nt', + 'use_multiprocessing=True does not work on windows properly.') + @parameterized.parameters('sequential', 'functional') + def test_predict_generator_method(self, model_type): + if model_type == 'sequential': + model = testing_utils.get_small_sequential_mlp( + num_hidden=3, num_classes=4, input_dim=2) + else: + model = testing_utils.get_small_functional_mlp( + num_hidden=3, num_classes=4, input_dim=2) + model.compile( + loss='mse', + optimizer='sgd', + metrics=['mae', metrics_module.CategoricalAccuracy()]) + + model.predict_generator(custom_generator(), + steps=5, + max_queue_size=10, + workers=2, + use_multiprocessing=True) + model.predict_generator(custom_generator(), + steps=5, + max_queue_size=10, + use_multiprocessing=False) + model.predict_generator(custom_generator(), + steps=5, + max_queue_size=10, + workers=0) + # Test generator with just inputs (no targets) + model.predict_generator(custom_generator(mode=1), + steps=5, max_queue_size=10, - workers=4, + workers=2, use_multiprocessing=True) - model.fit_generator(custom_generator(), - steps_per_epoch=5, - epochs=1, - verbose=1, + model.predict_generator(custom_generator(mode=1), + steps=5, max_queue_size=10, use_multiprocessing=False) - model.fit_generator(custom_generator(), - steps_per_epoch=5, - epochs=1, - verbose=1, + model.predict_generator(custom_generator(mode=1), + steps=5, max_queue_size=10, - use_multiprocessing=False, - validation_data=custom_generator(), - validation_steps=10) - model.fit_generator(custom_generator(), - steps_per_epoch=5, - validation_data=custom_generator(), - validation_steps=1, workers=0) - model.predict_generator(custom_generator(), - steps=5, - max_queue_size=10, - workers=2, - use_multiprocessing=True) - model.predict_generator(custom_generator(), - steps=5, - max_queue_size=10, - use_multiprocessing=False) - model.predict_generator(custom_generator(), - steps=5, - max_queue_size=10, - workers=0) - model.evaluate_generator(custom_generator(), - steps=5, - max_queue_size=10, - workers=2, - verbose=1, - use_multiprocessing=True) - model.evaluate_generator(custom_generator(), - steps=5, - max_queue_size=10, - use_multiprocessing=False) - model.evaluate_generator(custom_generator(), - steps=5, - max_queue_size=10, - use_multiprocessing=False, - workers=0) def test_generator_methods_with_sample_weights(self): - arr_data = np.random.random((50, 2)) - arr_labels = np.random.random((50,)) - arr_sample_weights = np.random.random((50,)) + model = keras.models.Sequential() + model.add(keras.layers.Dense(4, input_shape=(2,))) + model.compile( + loss='mse', + optimizer='sgd', + metrics=['mae', metrics_module.CategoricalAccuracy()]) + + model.fit_generator(custom_generator(mode=3), + steps_per_epoch=5, + epochs=1, + verbose=1, + max_queue_size=10, + use_multiprocessing=False) + model.fit_generator(custom_generator(mode=3), + steps_per_epoch=5, + epochs=1, + verbose=1, + max_queue_size=10, + use_multiprocessing=False, + validation_data=custom_generator(mode=3), + validation_steps=10) + model.predict_generator(custom_generator(mode=3), + steps=5, + max_queue_size=10, + use_multiprocessing=False) + model.evaluate_generator(custom_generator(mode=3), + steps=5, + max_queue_size=10, + use_multiprocessing=False) - def custom_generator(): - batch_size = 10 - num_samples = 50 - while True: - batch_index = np.random.randint(0, num_samples - batch_size) - start = batch_index - end = start + batch_size - x = arr_data[start: end] - y = arr_labels[start: end] - w = arr_sample_weights[start: end] - yield x, y, w - - with self.cached_session(): - model = keras.models.Sequential() - model.add(keras.layers.Dense(1, input_shape=(2,))) - model.compile( - loss='mse', - optimizer='sgd', - metrics=['mae', metrics_module.CategoricalAccuracy()]) + def test_generator_methods_invalid_use_case(self): - model.fit_generator(custom_generator(), + def invalid_generator(): + while 1: + yield 0 + + model = keras.models.Sequential() + model.add(keras.layers.Dense(4, input_shape=(2,))) + model.compile(loss='mse', optimizer='sgd') + + with self.assertRaises(ValueError): + model.fit_generator(invalid_generator(), steps_per_epoch=5, epochs=1, verbose=1, max_queue_size=10, use_multiprocessing=False) + with self.assertRaises(ValueError): model.fit_generator(custom_generator(), steps_per_epoch=5, epochs=1, verbose=1, max_queue_size=10, use_multiprocessing=False, - validation_data=custom_generator(), + validation_data=invalid_generator(), validation_steps=10) - model.predict_generator(custom_generator(), + with self.assertRaises(AttributeError): + model.predict_generator(invalid_generator(), steps=5, max_queue_size=10, use_multiprocessing=False) - model.evaluate_generator(custom_generator(), + with self.assertRaises(ValueError): + model.evaluate_generator(invalid_generator(), steps=5, max_queue_size=10, use_multiprocessing=False) - def test_generator_methods_invalid_use_case(self): + def test_generator_input_to_fit_eval_predict(self): + val_data = np.ones([10, 10], np.float32), np.ones([10, 1], np.float32) - def custom_generator(): - while 1: - yield 0 + def ones_generator(): + while True: + yield np.ones([10, 10], np.float32), np.ones([10, 1], np.float32) + + inputs = keras.layers.Input(shape=(10,)) + x = keras.layers.Dense(10, activation='relu')(inputs) + outputs = keras.layers.Dense(1, activation='sigmoid')(x) + model = keras.Model(inputs, outputs) - with self.cached_session(): - model = keras.models.Sequential() - model.add(keras.layers.Dense(1, input_shape=(2,))) - model.compile(loss='mse', optimizer='sgd') + model.compile(RMSPropOptimizer(0.001), 'binary_crossentropy') + model.fit( + ones_generator(), + steps_per_epoch=2, + validation_data=val_data, + epochs=2) + model.evaluate(ones_generator(), steps=2) + model.predict(ones_generator(), steps=2) - with self.assertRaises(ValueError): - model.fit_generator(custom_generator(), - steps_per_epoch=5, - epochs=1, - verbose=1, - max_queue_size=10, - use_multiprocessing=False) - with self.assertRaises(ValueError): - model.fit_generator(custom_generator(), - steps_per_epoch=5, - epochs=1, - verbose=1, - max_queue_size=10, - use_multiprocessing=False, - validation_data=custom_generator(), - validation_steps=10) - with self.assertRaises(AttributeError): - model.predict_generator(custom_generator(), - steps=5, - max_queue_size=10, - use_multiprocessing=False) - with self.assertRaises(ValueError): - model.evaluate_generator(custom_generator(), - steps=5, - max_queue_size=10, - use_multiprocessing=False) + +@tf_test_util.run_all_in_graph_and_eager_modes +class TestGeneratorMethodsWithSequences(test.TestCase): def test_training_with_sequences(self): class DummySequence(keras.utils.Sequence): def __getitem__(self, idx): - return np.zeros([10, 2]), np.ones([10]) + return np.zeros([10, 2]), np.ones([10, 4]) def __len__(self): return 10 - arr_data = np.random.random((50, 2)) - arr_labels = np.random.random((50,)) - arr_sample_weights = np.random.random((50,)) - - def custom_generator(): - batch_size = 10 - num_samples = 50 - while True: - batch_index = np.random.randint(0, num_samples - batch_size) - start = batch_index - end = start + batch_size - x = arr_data[start: end] - y = arr_labels[start: end] - w = arr_sample_weights[start: end] - yield x, y, w - - with self.cached_session(): - model = keras.models.Sequential() - model.add(keras.layers.Dense(1, input_shape=(2,))) - model.compile(loss='mse', optimizer='sgd') + model = keras.models.Sequential() + model.add(keras.layers.Dense(4, input_shape=(2,))) + model.compile(loss='mse', optimizer='sgd') model.fit_generator(DummySequence(), steps_per_epoch=10, @@ -251,29 +305,6 @@ class TestGeneratorMethods(test.TestCase): workers=0, use_multiprocessing=False) - @tf_test_util.run_in_graph_and_eager_modes - def test_generator_input_to_fit_eval_predict(self): - val_data = np.ones([10, 10], np.float32), np.ones([10, 1], np.float32) - - def custom_generator(): - while True: - yield np.ones([10, 10], np.float32), np.ones([10, 1], np.float32) - - inputs = keras.layers.Input(shape=(10,)) - x = keras.layers.Dense(10, activation='relu')(inputs) - outputs = keras.layers.Dense(1, activation='sigmoid')(x) - model = keras.Model(inputs, outputs) - - model.compile(RMSPropOptimizer(0.001), 'binary_crossentropy') - model.fit( - custom_generator(), - steps_per_epoch=2, - validation_data=val_data, - epochs=2) - model.evaluate(custom_generator(), steps=2) - model.predict(custom_generator(), steps=2) - - @tf_test_util.run_in_graph_and_eager_modes def test_sequence_input_to_fit_eval_predict(self): val_data = np.ones([10, 10], np.float32), np.ones([10, 1], np.float32) @@ -303,5 +334,56 @@ class TestGeneratorMethods(test.TestCase): model.fit(CustomSequence(), sample_weight=np.ones([10, 1])) +@tf_test_util.run_all_in_graph_and_eager_modes +class TestConvertToGeneratorLike(test.TestCase, parameterized.TestCase): + simple_inputs = (np.ones((10, 10)), np.ones((10, 1))) + nested_inputs = ((np.ones((10, 10)), np.ones((10, 20))), (np.ones((10, 1)), + np.ones((10, 3)))) + + def _make_dataset(self, inputs, batches): + return dataset_ops.DatasetV2.from_tensors(inputs).repeat(batches) + + def _make_iterator(self, inputs, batches): + return dataset_ops.make_one_shot_iterator( + self._make_dataset(inputs, batches)) + + def _make_generator(self, inputs, batches): + + def _gen(): + for _ in range(batches): + yield inputs + + return _gen() + + def _make_numpy(self, inputs, _): + return inputs + + @parameterized.named_parameters( + ('simple_dataset', _make_dataset, simple_inputs), + ('simple_iterator', _make_iterator, simple_inputs), + ('simple_generator', _make_generator, simple_inputs), + ('simple_numpy', _make_numpy, simple_inputs), + ('nested_dataset', _make_dataset, nested_inputs), + ('nested_iterator', _make_iterator, nested_inputs), + ('nested_generator', _make_generator, nested_inputs), + ('nested_numpy', _make_numpy, nested_inputs)) + def test_convert_to_generator_like(self, input_fn, inputs): + expected_batches = 5 + data = input_fn(self, inputs, expected_batches) + + # Dataset and Iterator not supported in Legacy Graph mode. + if (not context.executing_eagerly() and + isinstance(data, (dataset_ops.DatasetV2, iterator_ops.Iterator))): + return + + generator, steps = training_generator.convert_to_generator_like( + data, batch_size=2, steps_per_epoch=expected_batches) + self.assertEqual(steps, expected_batches) + + for _ in range(expected_batches): + outputs = next(generator) + nest.assert_same_structure(outputs, inputs) + + if __name__ == '__main__': test.main() diff --git a/tensorflow/python/keras/engine/training_gpu_test.py b/tensorflow/python/keras/engine/training_gpu_test.py index 596d085f3f..45dcfe4399 100644 --- a/tensorflow/python/keras/engine/training_gpu_test.py +++ b/tensorflow/python/keras/engine/training_gpu_test.py @@ -69,7 +69,7 @@ class TrainingGPUTest(test.TestCase): return simple_model if test.is_gpu_available(cuda_only=True): - with self.session(use_gpu=True): + with test_util.use_gpu(): losses_to_test = ['sparse_categorical_crossentropy', 'categorical_crossentropy', 'binary_crossentropy'] diff --git a/tensorflow/python/keras/engine/training_test.py b/tensorflow/python/keras/engine/training_test.py index 3cb24255d1..8e3b61be0c 100644 --- a/tensorflow/python/keras/engine/training_test.py +++ b/tensorflow/python/keras/engine/training_test.py @@ -37,6 +37,7 @@ from tensorflow.python.keras import testing_utils from tensorflow.python.keras.callbacks import Callback from tensorflow.python.keras.engine.training_utils import weighted_masked_objective from tensorflow.python.ops import array_ops +from tensorflow.python.ops import math_ops from tensorflow.python.ops import sparse_ops from tensorflow.python.ops import variables as variables_lib from tensorflow.python.platform import test @@ -448,6 +449,7 @@ class TrainingTest(test.TestCase): optimizer=keras.optimizers.Adam(lr=0.0001), metrics=['accuracy']) + @tf_test_util.run_deprecated_v1 def test_that_trainable_disables_updates(self): val_a = np.random.random((10, 4)) val_out = np.random.random((10, 4)) @@ -544,6 +546,152 @@ class TrainingTest(test.TestCase): 'val_loss', 'val_weighted_mean_absolute_error' ])) + @tf_test_util.run_in_graph_and_eager_modes + def test_mismatched_output_shape_and_target_shape(self): + model = keras.Sequential([ + keras.layers.Dense(2, input_shape=(3, 4)), + keras.layers.Dense(5), + ]) + model.compile(RMSPropOptimizer(learning_rate=0.001), + loss='sparse_categorical_crossentropy') + # Test with Numpy data + x_train = np.random.random((10, 3, 4)) + y_train = np.random.randint(0, 5, size=(10, 3)) + model.fit(x_train, y_train, batch_size=5, epochs=1) + + # Test with iterator + dataset = dataset_ops.Dataset.from_tensor_slices((x_train, y_train)) + dataset = dataset.repeat(10) + dataset = dataset.batch(10) + iterator = dataset_ops.make_one_shot_iterator(dataset) + model.fit(iterator, epochs=1, steps_per_epoch=2) + + if context.executing_eagerly(): + # Test with eager execution + model.compile(RMSPropOptimizer(learning_rate=0.001), + loss='sparse_categorical_crossentropy', + run_eagerly=True) + model.fit(x_train, y_train, batch_size=5, epochs=1) + + # Test with eager execution and iterator + model.fit(iterator, epochs=1, steps_per_epoch=2) + + def test_losses_in_defun(self): + with context.eager_mode(): + layer = keras.layers.Dense(1, kernel_regularizer='l1') + layer(array_ops.ones([1, 10])) + + @function.defun + def get_losses(): + return layer.losses + + self.assertAllEqual( + self.evaluate(layer.losses), self.evaluate(get_losses())) + + @tf_test_util.run_in_graph_and_eager_modes + def test_logging(self): + mock_stdout = io.BytesIO() if six.PY2 else io.StringIO() + model = keras.models.Sequential() + model.add(keras.layers.Dense(10, activation='relu')) + model.add(keras.layers.Dense(1, activation='sigmoid')) + model.compile( + RMSPropOptimizer(learning_rate=0.001), loss='binary_crossentropy') + with test.mock.patch.object(sys, 'stdout', mock_stdout): + model.fit( + np.ones((10, 10), 'float32'), np.ones((10, 1), 'float32'), epochs=10) + self.assertTrue('Epoch 5/10' in mock_stdout.getvalue()) + + @tf_test_util.run_in_graph_and_eager_modes + def test_training_with_loss_instance(self): + a = keras.layers.Input(shape=(3,), name='input_a') + b = keras.layers.Input(shape=(3,), name='input_b') + + dense = keras.layers.Dense(4, name='dense') + c = dense(a) + d = dense(b) + e = keras.layers.Dropout(0.5, name='dropout')(c) + + model = keras.models.Model([a, b], [d, e]) + loss_weights = [1., 0.5] + model.compile( + RMSPropOptimizer(learning_rate=0.001), + loss=keras.losses.MeanSquaredError(), + metrics=[metrics_module.CategoricalAccuracy(), 'mae'], + loss_weights=loss_weights) + + input_a_np = np.random.random((10, 3)) + input_b_np = np.random.random((10, 3)) + + output_d_np = np.random.random((10, 4)) + output_e_np = np.random.random((10, 4)) + + model.fit([input_a_np, input_b_np], [output_d_np, output_e_np], + epochs=1, + batch_size=5) + + @tf_test_util.run_in_graph_and_eager_modes + def test_static_batch_in_input_layer(self): + + class Counter(keras.callbacks.Callback): + + def __init__(self): + self.batches = 0 + + def on_batch_end(self, batch, logs=None): + self.batches += 1 + + x, y = np.ones((64, 10), 'float32'), np.ones((64, 1), 'float32') + + for batch_size, expected_batches in [(None, 2), (4, 16)]: + inputs = keras.Input(batch_size=batch_size, shape=(10,)) + outputs = keras.layers.Dense(1, activation='sigmoid')(inputs) + model = keras.Model(inputs, outputs) + + model.compile(keras.optimizer_v2.adam.Adam(0.001), 'binary_crossentropy') + counter = Counter() + model.fit(x, y, callbacks=[counter]) + self.assertEqual(counter.batches, expected_batches) + + model = keras.Sequential( + [keras.layers.Dense(1, batch_input_shape=(batch_size, 10))]) + model.compile(keras.optimizer_v2.adam.Adam(0.001), 'binary_crossentropy') + counter = Counter() + model.fit(x, y, callbacks=[counter]) + self.assertEqual(counter.batches, expected_batches) + + @tf_test_util.run_in_graph_and_eager_modes + def test_static_batch_in_input_layer_consistency_checks(self): + x, y = np.ones((64, 10), 'float32'), np.ones((64, 1), 'float32') + + inputs = keras.Input(batch_size=2, shape=(10,)) + outputs = keras.layers.Dense(1, activation='sigmoid')(inputs) + model = keras.Model(inputs, outputs) + model.compile(keras.optimizer_v2.adam.Adam(0.001), 'binary_crossentropy') + with self.assertRaisesRegexp(ValueError, + 'incompatible with the specified batch size'): + model.fit(x, y, batch_size=4) + + data = dataset_ops.DatasetV2.from_tensor_slices((x, y)) + data = data.batch(4, drop_remainder=True) + with self.assertRaisesRegexp(ValueError, + 'incompatible with the specified batch size'): + model.fit(data, steps_per_epoch=16) + + @tf_test_util.run_in_graph_and_eager_modes + def test_compatible_batch_size_functional_model(self): + + class MyLayer(keras.layers.Layer): + + def call(self, inputs): + return array_ops.concat(inputs, axis=0) + + input1 = keras.Input(batch_size=2, shape=(10,)) + input2 = keras.Input(batch_size=3, shape=(10,)) + outputs = MyLayer()([input1, input2]) + with self.assertRaisesRegexp(ValueError, + 'specified batch sizes of the Input Layers'): + keras.Model([input1, input2], outputs) + class TestExceptionsAndWarnings(test.TestCase): @@ -1062,6 +1210,7 @@ class LossMaskingTest(test.TestCase): class TestDynamicTrainability(test.TestCase): + @tf_test_util.run_deprecated_v1 def test_trainable_warning(self): with self.cached_session(): x = np.random.random((5, 3)) @@ -1075,6 +1224,7 @@ class TestDynamicTrainability(test.TestCase): model.train_on_batch(x, y) self.assertRaises(Warning) + @tf_test_util.run_deprecated_v1 def test_trainable_argument(self): with self.cached_session(): x = np.random.random((5, 3)) @@ -1205,6 +1355,7 @@ class TestDynamicTrainability(test.TestCase): class TestTrainingWithDataTensors(test.TestCase): + @tf_test_util.run_deprecated_v1 def test_training_and_eval_methods_on_symbolic_tensors_single_io(self): with self.cached_session(): x = keras.layers.Input(shape=(3,), name='input') @@ -1245,6 +1396,7 @@ class TestTrainingWithDataTensors(test.TestCase): epochs=1, steps_per_epoch=2, verbose=0, validation_data=(inputs, targets), validation_steps=2) + @tf_test_util.run_deprecated_v1 def test_training_and_eval_methods_on_symbolic_tensors_multi_io(self): with self.cached_session(): a = keras.layers.Input(shape=(3,), name='input_a') @@ -1340,6 +1492,7 @@ class TestTrainingWithDataTensors(test.TestCase): model.predict([input_a_tf, input_b_tf], steps=2) model.test_on_batch([input_a_tf, input_b_tf], [output_d_tf, output_e_tf]) + @tf_test_util.run_deprecated_v1 def test_model_with_input_feed_tensor(self): """We test building a model with a TF variable as input. @@ -1518,6 +1671,7 @@ class TestTrainingWithDataTensors(test.TestCase): # evaluate _ = model.evaluate(input_a_np, [output_a_np]) + @tf_test_util.run_deprecated_v1 def test_model_with_external_loss(self): with self.cached_session(): # None loss, only regularization loss. @@ -1713,6 +1867,7 @@ class TestTrainingWithDataTensors(test.TestCase): model.train_on_batch(input_val, None, sample_weight={'dense_a': np.random.random((10,))}) + @tf_test_util.run_deprecated_v1 def test_model_custom_target_tensors(self): with self.cached_session(): a = keras.Input(shape=(3,), name='input_a') @@ -1774,264 +1929,6 @@ class TestTrainingWithDataTensors(test.TestCase): [output_a_np, output_b_np]) -class TestTrainingWithDatasetIterators(test.TestCase): - - @tf_test_util.run_in_graph_and_eager_modes - def test_training_and_eval_methods_on_iterators_single_io(self): - model = testing_utils.get_small_functional_mlp(1, 4, input_dim=3) - optimizer = RMSPropOptimizer(learning_rate=0.001) - loss = 'mse' - metrics = ['mae', metrics_module.CategoricalAccuracy()] - model.compile(optimizer, loss, metrics=metrics) - - inputs = np.zeros((10, 3)) - targets = np.zeros((10, 4)) - dataset = dataset_ops.Dataset.from_tensor_slices((inputs, targets)) - dataset = dataset.repeat(100) - dataset = dataset.batch(10) - iterator = dataset.make_one_shot_iterator() - - model.fit(iterator, epochs=1, steps_per_epoch=2, verbose=1) - model.evaluate(iterator, steps=2, verbose=1) - model.predict(iterator, steps=2) - model.train_on_batch(iterator) - model.test_on_batch(iterator) - model.predict_on_batch(iterator) - - # Test with validation data - model.fit(iterator, - epochs=1, steps_per_epoch=2, verbose=0, - validation_data=iterator, validation_steps=2) - # Test with validation split - with self.assertRaisesRegexp( - ValueError, '`validation_split` argument is not supported ' - 'when input `x` is a dataset or a dataset iterator'): - model.fit(iterator, - epochs=1, steps_per_epoch=2, verbose=0, - validation_split=0.5, validation_steps=2) - - # Test with sample weight. - sample_weight = np.random.random((10,)) - with self.assertRaisesRegexp( - ValueError, '`sample_weight` argument is not supported ' - 'when input `x` is a dataset or a dataset iterator'): - model.fit( - iterator, - epochs=1, - steps_per_epoch=2, - verbose=0, - sample_weight=sample_weight) - - # Test invalid usage - with self.assertRaisesRegexp(ValueError, - 'you should not specify a target'): - model.fit(iterator, iterator, - epochs=1, steps_per_epoch=2, verbose=0) - - with self.assertRaisesRegexp( - ValueError, 'you should specify the `steps_per_epoch` argument'): - model.fit(iterator, epochs=1, verbose=0) - with self.assertRaisesRegexp(ValueError, - 'you should specify the `steps` argument'): - model.evaluate(iterator, verbose=0) - with self.assertRaisesRegexp(ValueError, - 'you should specify the `steps` argument'): - model.predict(iterator, verbose=0) - - @tf_test_util.run_in_graph_and_eager_modes - def test_get_next_op_created_once(self): - model = testing_utils.get_small_functional_mlp(1, 4, input_dim=3) - optimizer = RMSPropOptimizer(learning_rate=0.001) - loss = 'mse' - metrics = ['mae'] - model.compile(optimizer, loss, metrics=metrics) - - inputs = np.zeros((10, 3)) - targets = np.zeros((10, 4)) - dataset = dataset_ops.Dataset.from_tensor_slices((inputs, targets)) - dataset = dataset.repeat(100) - dataset = dataset.batch(10) - iterator = dataset.make_one_shot_iterator() - - model.fit(iterator, epochs=1, steps_per_epoch=2, verbose=1) - # Finalize graph to make sure we are not appending another iterator - # get_next op in the graph. - ops.get_default_graph().finalize() - model.fit(iterator, epochs=1, steps_per_epoch=2, verbose=1) - - @tf_test_util.run_in_graph_and_eager_modes - def test_iterators_running_out_of_data(self): - model = testing_utils.get_small_functional_mlp(1, 4, input_dim=3) - optimizer = RMSPropOptimizer(learning_rate=0.001) - loss = 'mse' - metrics = ['mae'] - model.compile(optimizer, loss, metrics=metrics) - - inputs = np.zeros((10, 3)) - targets = np.zeros((10, 4)) - dataset = dataset_ops.Dataset.from_tensor_slices((inputs, targets)) - dataset = dataset.repeat(2) - dataset = dataset.batch(10) - iterator = dataset.make_one_shot_iterator() - - with test.mock.patch.object(logging, 'warning') as mock_log: - model.fit(iterator, epochs=1, steps_per_epoch=3, verbose=0) - self.assertRegexpMatches( - str(mock_log.call_args), - 'dataset iterator ran out of data') - - -class TestTrainingWithDataset(test.TestCase): - - @tf_test_util.run_in_graph_and_eager_modes - def test_calling_model_on_same_dataset(self): - model = testing_utils.get_small_functional_mlp(1, 4, input_dim=3) - optimizer = RMSPropOptimizer(learning_rate=0.001) - loss = 'mse' - metrics = ['mae'] - model.compile(optimizer, loss, metrics=metrics) - - inputs = np.zeros((10, 3)) - targets = np.zeros((10, 4)) - dataset = dataset_ops.Dataset.from_tensor_slices((inputs, targets)) - dataset = dataset.repeat(100) - dataset = dataset.batch(10) - - # Call fit with validation data - model.fit(dataset, epochs=1, steps_per_epoch=2, verbose=0, - validation_data=dataset, validation_steps=2) - # Finalize the graph to make sure new ops aren't added when calling on the - # same dataset - ops.get_default_graph().finalize() - model.fit(dataset, epochs=1, steps_per_epoch=2, verbose=0, - validation_data=dataset, validation_steps=2) - - @tf_test_util.run_in_graph_and_eager_modes - def test_training_and_eval_methods_on_dataset(self): - model = testing_utils.get_small_functional_mlp(1, 4, input_dim=3) - optimizer = RMSPropOptimizer(learning_rate=0.001) - loss = 'mse' - metrics = ['mae', metrics_module.CategoricalAccuracy()] - model.compile(optimizer, loss, metrics=metrics) - - inputs = np.zeros((10, 3)) - targets = np.zeros((10, 4)) - dataset = dataset_ops.Dataset.from_tensor_slices((inputs, targets)) - dataset = dataset.repeat(100) - dataset = dataset.batch(10) - - model.fit(dataset, epochs=1, steps_per_epoch=2, verbose=1) - model.evaluate(dataset, steps=2, verbose=1) - model.predict(dataset, steps=2) - model.train_on_batch(dataset) - model.predict_on_batch(dataset) - - # Test with validation data - model.fit(dataset, epochs=1, steps_per_epoch=2, verbose=0, - validation_data=dataset, validation_steps=2) - - # Test with validation split - with self.assertRaisesRegexp( - ValueError, '`validation_split` argument is not supported ' - 'when input `x` is a dataset or a dataset iterator'): - model.fit(dataset, - epochs=1, steps_per_epoch=2, verbose=0, - validation_split=0.5, validation_steps=2) - - # Test with sample weight. - sample_weight = np.random.random((10,)) - with self.assertRaisesRegexp( - ValueError, '`sample_weight` argument is not supported ' - 'when input `x` is a dataset or a dataset iterator'): - model.fit( - dataset, - epochs=1, - steps_per_epoch=2, - verbose=0, - sample_weight=sample_weight) - - # Test invalid usage - with self.assertRaisesRegexp(ValueError, - 'you should not specify a target'): - model.fit(dataset, dataset, - epochs=1, steps_per_epoch=2, verbose=0) - - with self.assertRaisesRegexp( - ValueError, 'you should specify the `steps_per_epoch` argument'): - model.fit(dataset, epochs=1, verbose=0) - with self.assertRaisesRegexp(ValueError, - 'you should specify the `steps` argument'): - model.evaluate(dataset, verbose=0) - with self.assertRaisesRegexp(ValueError, - 'you should specify the `steps` argument'): - model.predict(dataset, verbose=0) - - @tf_test_util.run_in_graph_and_eager_modes - def test_dataset_with_sample_weights(self): - model = testing_utils.get_small_functional_mlp(1, 4, input_dim=3) - optimizer = RMSPropOptimizer(learning_rate=0.001) - loss = 'mse' - metrics = ['mae', metrics_module.CategoricalAccuracy()] - model.compile(optimizer, loss, metrics=metrics) - - inputs = np.zeros((10, 3), np.float32) - targets = np.zeros((10, 4), np.float32) - sample_weights = np.ones((10), np.float32) - dataset = dataset_ops.Dataset.from_tensor_slices((inputs, targets, - sample_weights)) - dataset = dataset.repeat(100) - dataset = dataset.batch(10) - - model.fit(dataset, epochs=1, steps_per_epoch=2, verbose=1) - model.evaluate(dataset, steps=2, verbose=1) - model.predict(dataset, steps=2) - model.train_on_batch(dataset) - model.predict_on_batch(dataset) - - @tf_test_util.run_in_graph_and_eager_modes - def test_dataset_with_sparse_labels(self): - model = testing_utils.get_small_functional_mlp(1, 4, input_dim=3) - optimizer = RMSPropOptimizer(learning_rate=0.001) - loss = 'sparse_categorical_crossentropy' - model.compile(optimizer, loss) - - inputs = np.zeros((10, 3)) - targets = np.random.randint(0, 4, size=10, dtype=np.int32) - dataset = dataset_ops.Dataset.from_tensor_slices((inputs, targets)) - dataset = dataset.repeat(100) - dataset = dataset.batch(10) - - model.fit(dataset, epochs=1, steps_per_epoch=2, verbose=1) - - def test_dataset_input_shape_validation(self): - with self.cached_session(): - model = testing_utils.get_small_functional_mlp(1, 4, input_dim=3) - model.compile(optimizer=RMSPropOptimizer(learning_rate=0.001), loss='mse') - - # User forgets to batch the dataset - inputs = np.zeros((10, 3)) - targets = np.zeros((10, 4)) - dataset = dataset_ops.Dataset.from_tensor_slices((inputs, targets)) - dataset = dataset.repeat(100) - - with self.assertRaisesRegexp( - ValueError, - r'expected (.*?) to have shape \(3,\) but got array with shape \(1,\)' - ): - model.train_on_batch(dataset) - - # Wrong input shape - inputs = np.zeros((10, 5)) - targets = np.zeros((10, 4)) - dataset = dataset_ops.Dataset.from_tensor_slices((inputs, targets)) - dataset = dataset.repeat(100) - dataset = dataset.batch(10) - - with self.assertRaisesRegexp(ValueError, - r'expected (.*?) to have shape \(3,\)'): - model.train_on_batch(dataset) - - class TestTrainingWithMetrics(test.TestCase): """Training tests related to metrics.""" @@ -2095,39 +1992,6 @@ class TestTrainingWithMetrics(test.TestCase): self.assertEqual(outs[1], 0.) self.assertEqual(outs[2], 0.) - @tf_test_util.run_in_graph_and_eager_modes - def test_metrics_correctness_with_iterator(self): - model = keras.Sequential() - model.add( - keras.layers.Dense( - 8, activation='relu', input_dim=4, kernel_initializer='ones')) - model.add( - keras.layers.Dense( - 1, activation='sigmoid', kernel_initializer='ones')) - model.compile( - loss='binary_crossentropy', - metrics=['accuracy', metrics_module.BinaryAccuracy()], - optimizer=RMSPropOptimizer(learning_rate=0.001)) - - np.random.seed(123) - x = np.random.randint(10, size=(100, 4)).astype(np.float32) - y = np.random.randint(2, size=(100, 1)).astype(np.float32) - dataset = dataset_ops.Dataset.from_tensor_slices((x, y)) - dataset = dataset.batch(10) - iterator = dataset.make_one_shot_iterator() - outs = model.evaluate(iterator, steps=10) - self.assertEqual(np.around(outs[1], decimals=1), 0.5) - self.assertEqual(np.around(outs[2], decimals=1), 0.5) - - y = np.zeros((100, 1), dtype=np.float32) - dataset = dataset_ops.Dataset.from_tensor_slices((x, y)) - dataset = dataset.repeat(100) - dataset = dataset.batch(10) - iterator = dataset.make_one_shot_iterator() - outs = model.evaluate(iterator, steps=10) - self.assertEqual(outs[1], 0.) - self.assertEqual(outs[2], 0.) - @tf_test_util.run_in_graph_and_eager_modes def test_metrics_correctness_with_weighted_metrics(self): np.random.seed(1337) @@ -2153,7 +2017,7 @@ class TestTrainingWithMetrics(test.TestCase): w = np.array([[3., 4.], [1., 2.]]) outs = model.evaluate(x, y, sample_weight=w) - self.assertArrayNear(outs, [0.3, 0.7, 0.3], .001) + self.assertArrayNear(outs, [0.75, 0.7, 0.3], .001) # Verify that metric value is same with arbitrary weights and batch size. x = np.random.random((50, 2, 1)) @@ -2223,32 +2087,331 @@ class TestTrainingWithMetrics(test.TestCase): # verify that masking is combined with sample weights. w = np.array([3, 2, 4]) scores = model.train_on_batch(x, y, sample_weight=w) - self.assertArrayNear(scores, [0.2, 0.8], 0.1) + self.assertArrayNear(scores, [0.3328, 0.8], 0.001) + + @tf_test_util.run_deprecated_v1 + def test_add_metric_with_tensor_on_model_in_graph_mode(self): + with self.cached_session(): + x = keras.layers.Input(shape=(1,)) + y = keras.layers.Dense(1, kernel_initializer='ones')(x) + model = keras.models.Model(x, y) + model.add_metric( + math_ops.reduce_sum(y), name='metric_1', aggregation='mean') + + # test with a metric which does not have the standard signature: + # (y_true, y_pred, sample_Weight) + model.add_metric(metrics_module.Mean(name='metric_2')(y)) + model.compile('sgd', loss='mse') + + inputs = np.ones(shape=(10, 1)) + targets = np.ones(shape=(10, 1)) + history = model.fit( + inputs, + targets, + epochs=2, + batch_size=5, + validation_data=(inputs, targets)) + self.assertEqual(history.history['metric_1'][-1], 5) + self.assertEqual(history.history['metric_2'][-1], 1) + self.assertEqual(history.history['val_metric_1'][-1], 5) + self.assertEqual(history.history['val_metric_2'][-1], 1) + + eval_results = model.evaluate(inputs, targets, batch_size=5) + self.assertEqual(eval_results[-1], 1) + self.assertEqual(eval_results[-2], 5) + + model.predict(inputs, batch_size=5) + model.train_on_batch(inputs, targets) + model.test_on_batch(inputs, targets) @tf_test_util.run_in_graph_and_eager_modes - def test_logging(self): - mock_stdout = io.BytesIO() if six.PY2 else io.StringIO() - model = keras.models.Sequential() - model.add(keras.layers.Dense(10, activation='relu')) - model.add(keras.layers.Dense(1, activation='sigmoid')) - model.compile( - RMSPropOptimizer(learning_rate=0.001), loss='binary_crossentropy') - with test.mock.patch.object(sys, 'stdout', mock_stdout): - model.fit( - np.ones((10, 10), 'float32'), np.ones((10, 1), 'float32'), epochs=10) - self.assertTrue('Epoch 5/10' in mock_stdout.getvalue()) + def test_add_metric_in_model_call(self): - def test_losses_in_defun(self): + class TestModel(keras.Model): + + def __init__(self): + super(TestModel, self).__init__(name='test_model') + self.dense1 = keras.layers.Dense(2, kernel_initializer='ones') + self.mean = metrics_module.Mean(name='metric_1') + + def call(self, x): + self.add_metric( + math_ops.reduce_sum(x), name='metric_2', aggregation='mean') + # Provide same name as in the instance created in __init__ + # for eager mode + self.add_metric(self.mean(x), name='metric_1') + return self.dense1(x) + + model = TestModel() + model.compile(loss='mse', optimizer=RMSPropOptimizer(0.01)) + + x = np.ones(shape=(10, 1)) + y = np.ones(shape=(10, 2)) + history = model.fit(x, y, epochs=2, batch_size=5, validation_data=(x, y)) + self.assertAlmostEqual(history.history['metric_1'][-1], 1, 0) + self.assertAlmostEqual(history.history['val_metric_1'][-1], 1, 0) + self.assertAlmostEqual(history.history['metric_2'][-1], 5, 0) + self.assertAlmostEqual(history.history['val_metric_2'][-1], 5, 0) + + eval_results = model.evaluate(x, y, batch_size=5) + self.assertAlmostEqual(eval_results[1], 1, 0) + self.assertAlmostEqual(eval_results[2], 5, 0) + + model.predict(x, batch_size=5) + model.train_on_batch(x, y) + model.test_on_batch(x, y) + + def test_add_metric_in_model_call_run_eagerly(self): with context.eager_mode(): - layer = keras.layers.Dense(1, kernel_regularizer='l1') - layer(array_ops.ones([1, 10])) - @function.defun - def get_losses(): - return layer.losses + class TestModel(keras.Model): + + def __init__(self): + super(TestModel, self).__init__(name='test_model') + self.dense1 = keras.layers.Dense(2, kernel_initializer='ones') + self.mean = metrics_module.Mean(name='metric_1') + + def call(self, x): + self.add_metric( + math_ops.reduce_sum(x), name='metric_2', aggregation='mean') + # Provide same name as in the instance created in __init__ + # for eager mode + self.add_metric(self.mean(x), name='metric_1') + return self.dense1(x) + + model = TestModel() + model.compile( + loss='mse', optimizer=RMSPropOptimizer(0.01), run_eagerly=True) + + x = np.ones(shape=(10, 1)) + y = np.ones(shape=(10, 2)) + history = model.fit(x, y, epochs=2, batch_size=5, validation_data=(x, y)) + self.assertAlmostEqual(history.history['metric_1'][-1], 1, 0) + self.assertAlmostEqual(history.history['val_metric_1'][-1], 1, 0) + self.assertAlmostEqual(history.history['metric_2'][-1], 5, 0) + self.assertAlmostEqual(history.history['val_metric_2'][-1], 5, 0) + + eval_results = model.evaluate(x, y, batch_size=5) + self.assertAlmostEqual(eval_results[1], 1, 0) + self.assertAlmostEqual(eval_results[2], 5, 0) + + model.predict(x, batch_size=5) + model.train_on_batch(x, y) + model.test_on_batch(x, y) + + @tf_test_util.run_in_graph_and_eager_modes + def test_add_metric_in_layer_call(self): + + class TestLayer(keras.layers.Layer): + + def build(self, input_shape): + self.a = self.add_variable( + 'a', (1, 1), initializer='ones', trainable=False) + self.built = True + + def call(self, inputs): + self.add_metric( + math_ops.reduce_sum(inputs), name='metric_1', aggregation='mean') + return inputs + 1 + + model = keras.Sequential() + model.add(TestLayer(input_shape=(1,))) + model.add(keras.layers.Dense(2, kernel_initializer='ones')) + model.compile(loss='mse', optimizer=RMSPropOptimizer(0.01)) + + x = np.ones(shape=(10, 1)) + y = np.ones(shape=(10, 2)) + history = model.fit(x, y, epochs=2, batch_size=5, validation_data=(x, y)) + self.assertEqual(history.history['metric_1'][-1], 5) + self.assertAlmostEqual(history.history['val_metric_1'][-1], 5, 0) + + def test_add_metric_in_layer_call_run_eagerly(self): + with context.eager_mode(): + + class TestLayer(keras.layers.Layer): + + def build(self, input_shape): + self.a = self.add_variable( + 'a', (1, 1), initializer='ones', trainable=False) + self.built = True + + def call(self, inputs): + self.add_metric( + math_ops.reduce_sum(inputs), name='metric_1', aggregation='mean') + return inputs + 1 + + model = keras.Sequential() + model.add(TestLayer(input_shape=(1,))) + model.add(keras.layers.Dense(2, kernel_initializer='ones')) + model.compile( + loss='mse', optimizer=RMSPropOptimizer(0.01), run_eagerly=True) + + x = np.ones(shape=(10, 1)) + y = np.ones(shape=(10, 2)) + history = model.fit(x, y, epochs=2, batch_size=5, validation_data=(x, y)) + self.assertEqual(history.history['metric_1'][-1], 5) + self.assertAlmostEqual(history.history['val_metric_1'][-1], 5, 0) + + @tf_test_util.run_deprecated_v1 + def test_model_metrics_list(self): + with self.cached_session(): + x = keras.layers.Input(shape=(1,)) + y = keras.layers.Dense(1, kernel_initializer='ones')(x) + model = keras.models.Model(x, y) + model.add_metric( + math_ops.reduce_sum(y), name='metric_1', aggregation='mean') + model.add_metric(metrics_module.Mean(name='metric_2')(y)) + model.compile('sgd', loss='mse', metrics=['acc']) + + # Verify that the metrics added using `compile` and `add_metric` API are + # included + self.assertEqual(model._compile_metrics, ['acc']) + names = [] + for m in model.metrics: + if isinstance(m, metrics_module.Metric): + names.append(m.name) + else: + names.append(m.__name__) + self.assertEqual(names, ['binary_accuracy', 'metric_1', 'metric_2']) + + def test_model_eager_metrics_list(self): + with context.eager_mode(): + + class TestModel(keras.Model): + + def __init__(self): + super(TestModel, self).__init__(name='test_model') + self.dense1 = keras.layers.Dense(2, kernel_initializer='ones') + + def call(self, x): + self.add_metric( + math_ops.reduce_sum(x), name='metric_1', aggregation='mean') + return self.dense1(x) + + model = TestModel() + model.compile( + loss='mse', + optimizer=RMSPropOptimizer(0.01), + metrics=['acc'], + run_eagerly=True) + x = np.ones(shape=(10, 1)) + y = np.ones(shape=(10, 2)) + model.fit(x, y, epochs=2, batch_size=5, validation_data=(x, y)) + + self.assertEqual(model._compile_metrics, ['acc']) + names = [] + for m in model.metrics: + if isinstance(m, metrics_module.Metric): + names.append(m.name) + else: + names.append(m.__name__) + self.assertEqual(names, ['categorical_accuracy', 'metric_1']) + + @tf_test_util.run_in_graph_and_eager_modes + def test_multiple_add_metric_calls(self): + + class TestModel(keras.Model): + + def __init__(self): + super(TestModel, self).__init__(name='test_model') + self.dense1 = keras.layers.Dense(2, kernel_initializer='ones') + self.mean1 = metrics_module.Mean(name='metric_1') + self.mean2 = metrics_module.Mean(name='metric_2') + + def call(self, x): + self.add_metric(self.mean2(x), name='metric_2') + self.add_metric(self.mean1(x), name='metric_1') + self.add_metric( + math_ops.reduce_sum(x), name='metric_3', aggregation='mean') + return self.dense1(x) + + model = TestModel() + model.compile(loss='mse', optimizer=RMSPropOptimizer(0.01)) + + x = np.ones(shape=(10, 1)) + y = np.ones(shape=(10, 2)) + history = model.fit(x, y, epochs=2, batch_size=5, validation_data=(x, y)) + self.assertAlmostEqual(history.history['metric_1'][-1], 1, 0) + self.assertAlmostEqual(history.history['metric_2'][-1], 1, 0) + self.assertAlmostEqual(history.history['metric_3'][-1], 5, 0) + + eval_results = model.evaluate(x, y, batch_size=5) + self.assertArrayNear(eval_results[1:4], [1, 1, 5], 0.1) + + model.predict(x, batch_size=5) + model.train_on_batch(x, y) + model.test_on_batch(x, y) + + def test_invalid_metric_tensor_in_call(self): + with context.eager_mode(): + + class TestLayer(keras.layers.Layer): + + def call(self, inputs): + self.add_metric(metrics_module.Mean(name='metric_1')(inputs)) + return inputs + 1 + + model = keras.Sequential() + model.add(TestLayer(input_shape=(1,))) + model.add(keras.layers.Dense(2, kernel_initializer='ones')) + model.compile( + loss='mse', optimizer=RMSPropOptimizer(0.01), run_eagerly=True) + + x = np.ones(shape=(10, 1)) + y = np.ones(shape=(10, 2)) + with self.assertRaisesRegexp( + ValueError, + 'We do not support adding an aggregated metric tensor in `call` in ' + 'eager execution.'): + model.fit(x, y, epochs=2, batch_size=5, validation_data=(x, y)) + + @tf_test_util.run_in_graph_and_eager_modes + def test_duplicate_metric_name_in_add_metric(self): + + class TestModel(keras.Model): + + def __init__(self): + super(TestModel, self).__init__(name='test_model') + self.dense1 = keras.layers.Dense(2, kernel_initializer='ones') + self.mean = metrics_module.Mean(name='metric_1') + self.mean2 = metrics_module.Mean(name='metric_1') + + def call(self, x): + self.add_metric(self.mean(x), name='metric_1') + return self.dense1(x) + + model = TestModel() + model.compile(loss='mse', optimizer=RMSPropOptimizer(0.01)) + + x = np.ones(shape=(10, 1)) + y = np.ones(shape=(10, 2)) + with self.assertRaisesRegexp( + ValueError, + 'Please provide different names for the metrics you have added. ' + 'We found 2 metrics with the name: "metric_1"'): + model.fit(x, y, epochs=2, batch_size=5, validation_data=(x, y)) + + @tf_test_util.run_in_graph_and_eager_modes + def test_multiple_no_name_input_to_add_metric(self): + + class TestModel(keras.Model): + + def __init__(self): + super(TestModel, self).__init__(name='test_model') + self.dense1 = keras.layers.Dense(2, kernel_initializer='ones') + + def call(self, x): + self.add_metric(math_ops.reduce_sum(x), aggregation='mean') + self.add_metric(math_ops.reduce_sum(x), aggregation='mean') + return self.dense1(x) + + model = TestModel() + model.compile(loss='mse', optimizer=RMSPropOptimizer(0.01)) + x = np.ones(shape=(10, 1)) + y = np.ones(shape=(10, 2)) + model.fit(x, y, epochs=2, batch_size=5, validation_data=(x, y)) + self.assertEqual([m.name for m in model.metrics], ['mean', 'mean_1']) - self.assertAllEqual(self.evaluate(layer.losses), - self.evaluate(get_losses())) if __name__ == '__main__': test.main() diff --git a/tensorflow/python/keras/engine/training_utils.py b/tensorflow/python/keras/engine/training_utils.py index e563b7a23d..0157fe084c 100644 --- a/tensorflow/python/keras/engine/training_utils.py +++ b/tensorflow/python/keras/engine/training_utils.py @@ -18,163 +18,176 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import abc from collections import OrderedDict import copy -import math import numpy as np import six -from tensorflow.python.data.ops import dataset_ops from tensorflow.python.data.ops import iterator_ops from tensorflow.python.eager import context from tensorflow.python.framework import constant_op from tensorflow.python.framework import ops +from tensorflow.python.framework import tensor_shape from tensorflow.python.framework import tensor_util from tensorflow.python.keras import backend as K +from tensorflow.python.keras import callbacks as cbks from tensorflow.python.keras import losses from tensorflow.python.keras import metrics as metrics_module from tensorflow.python.keras.engine import base_layer +from tensorflow.python.keras.utils import generic_utils +from tensorflow.python.keras.utils.losses_utils import squeeze_or_expand_dimensions from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import weights_broadcast_ops from tensorflow.python.util import nest -def _map_nested(data, func): - """Maps each nested element using func.""" - if isinstance(data, list): - return [_map_nested(nested_data, func) for nested_data in data] - elif isinstance(data, tuple): - return tuple(_map_nested(nested_data, func) for nested_data in data) - elif isinstance(data, dict): - return { - k: _map_nested(nested_data, func) for k, nested_data in data.items() - } - else: - return func(data) +@six.add_metaclass(abc.ABCMeta) +class Aggregator(object): + """Abstract base class used to aggregate batch-level outputs of a loop. + Attributes: + use_steps: Whether the loop is using `step` or `batch_size`. + num_samples_or_steps: Either `batch_size*num_batches` or `steps`. + results: What to return at the end of the aggregation loop. + """ -def _nested_all(data, cond_func): - """Checks if all elements in a nested structure satisfy cond_func.""" - if isinstance(data, (tuple, list)): - return all([_nested_all(nested_data, cond_func) for nested_data in data]) - elif isinstance(data, dict): - return all( - [_nested_all(nested_data, cond_func) for nested_data in data.values()]) - else: - return cond_func(data) + def __init__(self, use_steps, num_samples_or_steps): + self.use_steps = use_steps + self.num_samples_or_steps = num_samples_or_steps + self.results = [] + @abc.abstractmethod + def create(self, batch_outs): + """Creates the initial results from the first batch outputs. -def _nested_any(data, cond_func): - """Checks if any nested_elements in a nested structure satisfy cond_func.""" - if isinstance(data, (tuple, list)): - return any([_nested_any(nested_data, cond_func) for nested_data in data]) - elif isinstance(data, dict): - return any( - [_nested_any(nested_data, cond_func) for nested_data in data.values()]) - else: - return cond_func(data) - - -def _convert_lists_to_tuples(data): - """Converts all lists to tuples, since Datasets expect tuples.""" - if isinstance(data, (tuple, list)): - return tuple(_convert_lists_to_tuples(nested_data) for nested_data in data) - elif isinstance(data, dict): - return { - k: _convert_lists_to_tuples(nested_data) - for k, nested_data in data.items() - } - else: - return data + Arguments: + batch_outs: A list of batch-level outputs. + """ + NotImplementedError('Must be implemented in subclasses.') + @abc.abstractmethod + def aggregate(self, batch_outs, batch_start=None, batch_end=None): + """Aggregates batch-level results into total results. -def _get_batch_axis_size(data): - """Returns batch axis shape for nested data.""" - if isinstance(data, (tuple, list)): - return _get_batch_axis_size(data[0]) - elif isinstance(data, dict): - return _get_batch_axis_size(list(data.values())) - else: - return int(data.shape[0]) + Arguments: + batch_outs: A list of batch-level outputs. + batch_start: The start index of this batch. Always `None` if `use_steps` + is `True`. + batch_end: The end index of this batch. Always `None` if `use_steps` is + `True`. + """ + NotImplementedError('Must be implemented in subclasses.') + @abc.abstractmethod + def finalize(self): + """Prepares the total results to be returned.""" + NotImplementedError('Must be implemented in subclasses.') -def convert_to_iterator(x=None, - y=None, - sample_weights=None, - batch_size=None, - steps_per_epoch=None, - epochs=1, - shuffle=False, - is_validation=False): - """Converts NumPy arrays or EagerTensors to an EagerIterator. - Combines all provided data into a single EagerIterator. +class MetricsAggregator(Aggregator): + """Aggregator that calculates loss and metrics info.""" - Arguments: - x: NumPy array or EagerTensor, or list of Numpy arrays or EagerTensors - representing inputs to a model. - y: Optional. NumPy array or EagerTensor, or list of Numpy arrays or - EagerTensors representing targets of a model. - sample_weights: Optional NumPy array or EagerTensor representing sample - weights. - batch_size: Used to batch data and calculate how many steps EagerIterator - should take per epoch. - steps_per_epoch: If provided, how many steps EagerIterator should take per - epoch. - epochs: Epochs to repeat iterator for. - shuffle: Whether to shuffle data after each epoch. - is_validation: Whether this call is for validation during a training - (e.g., `fit()`) call. This info is used to construct error messages - (if any). + def create(self, batch_outs): + self.results = [0.] * len(batch_outs) - Raises: - ValueError: if steps_per_epoch cannot be calculated from the data - provided. + def aggregate(self, batch_outs, batch_start=None, batch_end=None): + # Loss. + if self.use_steps: + self.results[0] += batch_outs[0] + else: + self.results[0] += batch_outs[0] * (batch_end - batch_start) + # Metrics (always stateful, just grab current values.) + self.results[1:] = batch_outs[1:] - Returns: - (Iterator, steps_per_epoch). + def finalize(self): + self.results[0] /= self.num_samples_or_steps - """ - if isinstance(x, iterator_ops.EagerIterator): - return x, steps_per_epoch - if not _nested_any(sample_weights, lambda x: x is None): - data = (x, y, sample_weights) - elif not _nested_any(y, lambda x: x is None): - data = (x, y) - else: - # always wrap in a tuple, so we know y, sample_weights weren't set - # even when x has multiple elements - data = (x,) - - data = _convert_lists_to_tuples(data) - if steps_per_epoch is None and batch_size is not None: - num_samples = _get_batch_axis_size(data) - steps_per_epoch = int(math.ceil(num_samples / batch_size)) - - if steps_per_epoch is None: - alternative_arg_name = ( - 'validation_steps' if is_validation else 'steps_per_epoch') - raise ValueError( - 'Could not determine how to convert EagerTensors into EagerIterator. ' - 'Please provide either `batch_size` or ' - '`%s`.' % alternative_arg_name) +class OutputsAggregator(Aggregator): + """Aggregator that concatenates outputs.""" + + def create(self, batch_outs): + if self.use_steps: + # Cannot pre-allocate the returned NumPy arrays bc + # batch sizes are unknown. Concatenate batches at the end. + for _ in batch_outs: + self.results.append([]) + else: + # Pre-allocate NumPy arrays. + for batch_out in batch_outs: + shape = (self.num_samples_or_steps,) + batch_out.shape[1:] + self.results.append(np.zeros(shape, dtype=batch_out.dtype)) + + def aggregate(self, batch_outs, batch_start=None, batch_end=None): + if self.use_steps: + for i, batch_out in enumerate(batch_outs): + self.results[i].append(batch_out) + else: + for i, batch_out in enumerate(batch_outs): + self.results[i][batch_start:batch_end] = batch_out - # TODO(omalleyt) for NumPy arrays in graph mode - # placeholder ops should be used - # this is only ideal for eager mode - dataset = dataset_ops.Dataset.from_tensor_slices(data) + def finalize(self): + if self.use_steps: + self.results = [np.concatenate(result, axis=0) for result in self.results] - if batch_size is not None: - dataset = dataset.batch(batch_size) - if shuffle: - dataset = dataset.shuffle(buffer_size=10000) - dataset = dataset.repeat(epochs) - iterator = dataset.make_one_shot_iterator() - return iterator, steps_per_epoch +def make_logs(model, outputs, mode, prefix=''): + """Computes logs for sending to `on_batch_end` methods.""" + logs = {} + # TODO(omalleyt): handle outputs in prediction when Callback + # hooks are ready. + if mode in ['train', 'test']: + if hasattr(model, 'metrics_names'): + for label, output in zip(model.metrics_names, outputs): + logs[prefix + label] = output + return logs + + +def get_progbar(model, count_mode): + """Get Progbar.""" + stateful_metric_names = None + if hasattr(model, 'metrics_names'): + stateful_metric_names = model.metrics_names[1:] # Exclude `loss` + return cbks.ProgbarLogger(count_mode, stateful_metrics=stateful_metric_names) + + +def slice_arrays(arrays, indices, contiguous=True): + """Slices batches out of provided arrays (workaround for eager tensors). + + Unfortunately eager tensors don't have the same slicing behavior as + Numpy arrays (they follow the same slicing behavior as symbolic TF tensors), + hence we cannot use `generic_utils.slice_arrays` directly + and we have to implement this workaround based on `concat`. This has a + performance cost. + + Arguments: + arrays: Single array or list of arrays. + indices: List of indices in the array that should be included in the output + batch. + contiguous: Boolean flag indicating whether the indices are contiguous. + + Returns: + Slice of data (either single array or list of arrays). + """ + converted_to_list = False + if not isinstance(arrays, list): + converted_to_list = True + arrays = [arrays] + if any(tensor_util.is_tensor(x) for x in arrays): + if not contiguous: + entries = [[x[i:i + 1] for i in indices] for x in arrays] + slices = [array_ops.concat(x, axis=0) for x in entries] + else: + slices = [x[indices[0]:indices[-1] + 1] for x in arrays] + else: + slices = generic_utils.slice_arrays(arrays, indices) + + if converted_to_list: + slices = slices[0] + return slices def check_num_samples(ins, @@ -224,9 +237,9 @@ def standardize_single_array(x): return None if x.shape is not None and len(x.shape) == 1: if tensor_util.is_tensor(x): - return array_ops.expand_dims(x, axis=1) + x = array_ops.expand_dims(x, axis=1) else: - return np.expand_dims(x, 1) + x = np.expand_dims(x, 1) return x @@ -629,15 +642,14 @@ def weighted_masked_objective(fn): weights = mask else: # Update dimensions of weights to match with mask if possible. - mask, _, weights = metrics_module.squeeze_or_expand_dimensions( - mask, None, weights) + mask, _, weights = squeeze_or_expand_dimensions(mask, None, weights) weights *= mask # Apply sample weighting. if weights is not None: # Update dimensions of weights to match with values if possible. - score_array, _, weights = metrics_module.squeeze_or_expand_dimensions( + score_array, _, weights = squeeze_or_expand_dimensions( score_array, None, weights) try: # Broadcast weights if possible. @@ -651,7 +663,7 @@ def weighted_masked_objective(fn): score_array = math_ops.multiply(score_array, weights) score_array = math_ops.reduce_sum(score_array) weights = math_ops.reduce_sum(weights) - score_array = metrics_module.safe_div(score_array, weights) + score_array = math_ops.div_no_nan(score_array, weights) return K.mean(score_array) return weighted @@ -835,12 +847,22 @@ def call_metric_function(metric_fn, y_true, y_pred, weights=None, mask=None): return metric_fn(y_true, y_pred, sample_weight=mask) # Update dimensions of weights to match with mask. - mask, _, weights = metrics_module.squeeze_or_expand_dimensions( - mask, None, weights) + mask, _, weights = squeeze_or_expand_dimensions(mask, None, weights) weights *= mask return metric_fn(y_true, y_pred, sample_weight=weights) +def get_loss_function(loss): + """Returns the loss function corresponding to the given loss input.""" + if loss is None or isinstance(loss, losses.Loss): + return loss + + # TODO(psv): After we have added all V2 losses, update this function. + if loss in ['mse', 'MSE', 'mean_squared_error']: + return losses.MeanSquaredError() + return losses.get(loss) + + def validate_iterator_input(x, y, sample_weight, validation_split=None): """Validates user input arguments when a dataset iterator is passed. @@ -1053,9 +1075,11 @@ class ModelInputs(object): self._inputs = inputs self._is_dict = isinstance(self._inputs, dict) self._is_single_input = not isinstance(self._inputs, (list, tuple, dict)) + self._flattened_inputs = [] self._input_names = [] - if isinstance(self._inputs, dict): + + if self._is_dict: for k in sorted(self._inputs.keys()): self._flattened_inputs.append(self._inputs[k]) self._input_names.append(k) @@ -1064,7 +1088,6 @@ class ModelInputs(object): self._input_names = [ 'input_%d' % (i + 1) for i in range(len(self._flattened_inputs)) ] - assert len(self._input_names) == len(self._flattened_inputs) def get_input_names(self): """Returns keys to name inputs by. @@ -1074,56 +1097,29 @@ class ModelInputs(object): """ return self._input_names - def _get(self, return_single_as_list=False): - """Returns provided inputs, potentially transformed. - - Inputs are returned in the same format they were provided i.e. lists - are returned as lists, single entries as single entries (unless - `return_single_as_list` is true), dictionaries as dictionaries. - - Args: - return_single_as_list: Returns a list of size 1 for single entry case. - """ - if self._is_dict: - return dict(zip(self._input_names, self._flattened_inputs)) - if self._is_single_input and not return_single_as_list: - return self._flattened_inputs[0] - return self._flattened_inputs - - def get_input_values(self): - """Returns input values passed in.""" - if context.executing_eagerly(): - for i in range(len(self._flattened_inputs)): - v = self._flattened_inputs[i] - if tensor_util.is_tensor(v): - v = cast_single_tensor(v) - else: - v = ops.convert_to_tensor(v, dtype=K.floatx()) - self._flattened_inputs[i] = v - return self._get(return_single_as_list=False) - def get_symbolic_inputs(self, return_single_as_list=False): """Returns inputs to be set as self.inputs for a model.""" for i in range(len(self._flattened_inputs)): k = self._input_names[i] v = self._flattened_inputs[i] - if context.executing_eagerly(): - v = K.placeholder((None,) + tuple(v.shape[1:]), name=k) - else: - if isinstance(v, list): - v = np.asarray(v) - if v.ndim == 1: - v = np.expand_dims(v, 1) - if isinstance(v, (np.ndarray)): - # We fix the placeholder shape except the batch size. - # This is suboptimal, but it is the best we can do with the info - # we have. The user should call `model._set_inputs(placeholders)` - # to specify custom placeholders if the need arises. - shape = (None,) + v.shape[1:] - v = K.placeholder(shape=shape, name=k) + if isinstance(v, (list, float, int)): + v = np.asarray(v) + if v.ndim == 1: + v = np.expand_dims(v, 1) + if isinstance(v, (np.ndarray, ops.EagerTensor)): + # We fix the placeholder shape except the batch size. + # This is suboptimal, but it is the best we can do with the info + # we have. The user should call `model._set_inputs(placeholders)` + # to specify custom placeholders if the need arises. + shape = (None,) + tuple(v.shape[1:]) + v = K.placeholder(shape=shape, name=k) self._flattened_inputs[i] = v - return self._get(return_single_as_list) + if self._is_dict: + return dict(zip(self._input_names, self._flattened_inputs)) + if self._is_single_input and not return_single_as_list: + return self._flattened_inputs[0] + return self._flattened_inputs def as_dict(self): """An iterable over a dictionary version of inputs.""" @@ -1133,3 +1129,54 @@ class ModelInputs(object): def as_list(self): """Returning the inputs as a list.""" return self._flattened_inputs + + +# Allow use of methods not exposed to the user. +# pylint: disable=protected-access +def get_input_shape_and_dtype(layer): + """Retrieves input shape and input dtype of layer if applicable. + + Args: + layer: Layer (or model) instance. + + Returns: + Tuple (input_shape, input_dtype). Both could be None if the layer + does not have a defined input shape. + + Raises: + ValueError: in case an empty Sequential or Graph Network is passed. + """ + + def _is_graph_model(layer): + return ((hasattr(layer, '_is_graph_network') and layer._is_graph_network) or + layer.__class__.__name__ == 'Sequential') + + # In case of nested models: recover the first layer + # of the deepest model to infer input shape and dtype. + # Subclassed Models may not have been built so can't be checked. + while _is_graph_model(layer): + if not layer.layers: + raise ValueError('An empty Model cannot be used as a Layer.') + layer = layer.layers[0] + + if hasattr(layer, '_batch_input_shape'): + return layer._batch_input_shape, layer.dtype + return None, None + + +# pylint: enable=protected-access + + +def get_static_batch_size(layer): + """Gets the static batch size of a Layer. + + Arguments: + layer: a `Layer` instance. + + Returns: + The static batch size of a Layer. + """ + batch_input_shape, _ = get_input_shape_and_dtype(layer) + if batch_input_shape is not None: + return tensor_shape.as_dimension(batch_input_shape[0]).value + return None diff --git a/tensorflow/python/keras/engine/training_utils_test.py b/tensorflow/python/keras/engine/training_utils_test.py index 7b217cf373..44ea23998f 100644 --- a/tensorflow/python/keras/engine/training_utils_test.py +++ b/tensorflow/python/keras/engine/training_utils_test.py @@ -21,185 +21,39 @@ from __future__ import print_function import numpy as np from tensorflow.python.eager import context -from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_util -from tensorflow.python.framework import test_util from tensorflow.python.keras.engine import training_utils from tensorflow.python.keras.utils import tf_utils from tensorflow.python.platform import test -class TrainingUtilTest(test.TestCase): - - @test_util.run_in_graph_and_eager_modes - def test_convert_to_iterator_single_numpy(self): - batch_size = 2 - a = np.ones([10, 10]) - iterator, steps_per_epoch = training_utils.convert_to_iterator( - x=a, batch_size=batch_size) - self.assertEquals(steps_per_epoch, 5) - - expected_batch = a[:batch_size, :] - actual_batch, = iterator.get_next() - self.assertAllEqual(expected_batch, actual_batch) - - @test_util.run_in_graph_and_eager_modes - def test_convert_to_iterator_single_tensor(self): - batch_size = 2 - a = ops.convert_to_tensor(np.ones([10, 10])) - iterator, steps_per_epoch = training_utils.convert_to_iterator( - x=a, batch_size=batch_size) - self.assertEquals(steps_per_epoch, 5) - - expected_batch = a[:batch_size, :] - actual_batch, = iterator.get_next() - self.assertAllEqual(expected_batch, actual_batch) - - @test_util.run_in_graph_and_eager_modes - def test_convert_to_iterator_y(self): - batch_size = 2 - a = np.ones([10, 100]) - b = np.ones([10, 10]) - iterator, steps_per_epoch = training_utils.convert_to_iterator( - x=a, y=b, batch_size=batch_size) - self.assertEquals(steps_per_epoch, 5) - - expected_x = a[:batch_size, :] - expected_y = b[:batch_size, :] - actual_x, actual_y = iterator.get_next() - self.assertAllEqual(expected_x, actual_x) - self.assertAllEqual(expected_y, actual_y) - - @test_util.run_in_graph_and_eager_modes - def test_convert_to_iterator_sample_weights(self): - batch_size = 2 - a = ops.convert_to_tensor(np.ones([10, 100])) - b = ops.convert_to_tensor(np.ones([10, 10])) - sw = ops.convert_to_tensor(np.ones([10])) - iterator, steps_per_epoch = training_utils.convert_to_iterator( - x=a, y=b, sample_weights=sw, batch_size=batch_size) - self.assertEquals(steps_per_epoch, 5) - - expected_x = a[:batch_size, :] - expected_y = b[:batch_size, :] - expected_sw = sw[:batch_size] - actual_x, actual_y, actual_sw = iterator.get_next() - self.assertAllEqual(expected_x, actual_x) - self.assertAllEqual(expected_y, actual_y) - self.assertAllEqual(expected_sw, actual_sw) - - @test_util.run_in_graph_and_eager_modes - def test_convert_to_iterator_nested(self): - batch_size = 2 - x = {'1': np.ones([10, 100]), '2': [np.zeros([10, 10]), np.ones([10, 20])]} - iterator, steps_per_epoch = training_utils.convert_to_iterator( - x=x, batch_size=batch_size) - self.assertEquals(steps_per_epoch, 5) - - expected_x1 = x['1'][:batch_size, :] - expected_x2_0 = x['2'][0][:batch_size, :] - expected_x2_1 = x['2'][1][:batch_size, :] - - actual_x, = iterator.get_next() - actual_x1 = actual_x['1'][:batch_size, :] - actual_x2_0 = actual_x['2'][0][:batch_size, :] - actual_x2_1 = actual_x['2'][1][:batch_size, :] - - self.assertAllEqual(expected_x1, actual_x1) - self.assertAllEqual(expected_x2_0, actual_x2_0) - self.assertAllEqual(expected_x2_1, actual_x2_1) - - @test_util.run_in_graph_and_eager_modes - def test_convert_to_iterator_epochs(self): - batch_size = 2 - a = np.ones([10, 10]) - iterator, steps_per_epoch = training_utils.convert_to_iterator( - x=a, batch_size=batch_size, epochs=2) - self.assertEquals(steps_per_epoch, 5) - - expected_batch = a[:batch_size, :] - # loop through one whole epoch - for _ in range(6): - actual_batch, = iterator.get_next() - self.assertAllEqual(expected_batch, actual_batch) - - @test_util.run_in_graph_and_eager_modes - def test_convert_to_iterator_insufficient_info(self): - # with batch_size and steps_per_epoch not set - with self.assertRaises(ValueError): - a = np.ones([10, 10]) - _ = training_utils.convert_to_iterator(x=a) - - def test_nested_all(self): - nested_data = {'a': True, 'b': [True, True, (False, True)]} - all_true = training_utils._nested_all(nested_data, lambda x: x) - self.assertEquals(all_true, False) - - nested_data = {'a': True, 'b': [True, True, (True, True)]} - all_true = training_utils._nested_all(nested_data, lambda x: x) - self.assertEquals(all_true, True) - - def test_nested_any(self): - nested_data = [False, {'a': False, 'b': (False, True)}] - any_true = training_utils._nested_any(nested_data, lambda x: x) - self.assertEquals(any_true, True) - - nested_data = [False, {'a': False, 'b': (False, False)}] - any_true = training_utils._nested_any(nested_data, lambda x: x) - self.assertEquals(any_true, False) - - def test_check_array_lengths(self): - training_utils.check_array_lengths(None, None, None) - a_np = np.random.random((4, 3, 3)) - training_utils.check_array_lengths(a_np, a_np, a_np) - training_utils.check_array_lengths( - [a_np, a_np], [a_np, a_np], [a_np, a_np]) - training_utils.check_array_lengths([None], [None], [None]) - - b_np = np.random.random((3, 4)) - with self.assertRaises(ValueError): - training_utils.check_array_lengths([a_np], [b_np], None) - - class ModelInputsTest(test.TestCase): def test_single_thing(self): a = np.ones(10) model_inputs = training_utils.ModelInputs(a) - self.assertEquals(['input_1'], model_inputs.get_input_names()) - vals = model_inputs.get_input_values() - self.assertAllEqual(np.ones(10), vals) - self.assertFalse(tensor_util.is_tensor(vals)) + self.assertEqual(['input_1'], model_inputs.get_input_names()) vals = model_inputs.get_symbolic_inputs() self.assertTrue(tensor_util.is_tensor(vals)) vals = model_inputs.get_symbolic_inputs(return_single_as_list=True) - self.assertEquals(1, len(vals)) + self.assertEqual(1, len(vals)) self.assertTrue(tensor_util.is_tensor(vals[0])) def test_single_thing_eager(self): with context.eager_mode(): a = np.ones(10) model_inputs = training_utils.ModelInputs(a) - self.assertEquals(['input_1'], model_inputs.get_input_names()) - val = model_inputs.get_input_values() - self.assertAllEqual(np.ones(10), val) - self.assertTrue(tensor_util.is_tensor(val)) + self.assertEqual(['input_1'], model_inputs.get_input_names()) val = model_inputs.get_symbolic_inputs() self.assertTrue(tf_utils.is_symbolic_tensor(val)) vals = model_inputs.get_symbolic_inputs(return_single_as_list=True) - self.assertEquals(1, len(vals)) + self.assertEqual(1, len(vals)) self.assertTrue(tf_utils.is_symbolic_tensor(vals[0])) def test_list(self): a = [np.ones(10), np.ones(20)] model_inputs = training_utils.ModelInputs(a) - self.assertEquals(['input_1', 'input_2'], model_inputs.get_input_names()) - vals = model_inputs.get_input_values() - self.assertEqual(2, len(vals)) - self.assertAllEqual(np.ones(10), vals[0]) - self.assertAllEqual(np.ones(20), vals[1]) - self.assertFalse(tensor_util.is_tensor(vals[0])) - self.assertFalse(tensor_util.is_tensor(vals[1])) + self.assertEqual(['input_1', 'input_2'], model_inputs.get_input_names()) vals = model_inputs.get_symbolic_inputs() self.assertTrue(tensor_util.is_tensor(vals[0])) self.assertTrue(tensor_util.is_tensor(vals[1])) @@ -208,13 +62,7 @@ class ModelInputsTest(test.TestCase): with context.eager_mode(): a = [np.ones(10), np.ones(20)] model_inputs = training_utils.ModelInputs(a) - self.assertEquals(['input_1', 'input_2'], model_inputs.get_input_names()) - vals = model_inputs.get_input_values() - self.assertEqual(2, len(vals)) - self.assertAllEqual(np.ones(10), vals[0]) - self.assertAllEqual(np.ones(20), vals[1]) - self.assertTrue(tensor_util.is_tensor(vals[0])) - self.assertTrue(tensor_util.is_tensor(vals[1])) + self.assertEqual(['input_1', 'input_2'], model_inputs.get_input_names()) vals = model_inputs.get_symbolic_inputs() self.assertTrue(tf_utils.is_symbolic_tensor(vals[0])) self.assertTrue(tf_utils.is_symbolic_tensor(vals[1])) @@ -222,12 +70,7 @@ class ModelInputsTest(test.TestCase): def test_dict(self): a = {'b': np.ones(10), 'a': np.ones(20)} model_inputs = training_utils.ModelInputs(a) - self.assertEquals(['a', 'b'], model_inputs.get_input_names()) - vals = model_inputs.get_input_values() - self.assertAllEqual(np.ones(20), vals['a']) - self.assertAllEqual(np.ones(10), vals['b']) - self.assertFalse(tensor_util.is_tensor(vals['a'])) - self.assertFalse(tensor_util.is_tensor(vals['b'])) + self.assertEqual(['a', 'b'], model_inputs.get_input_names()) vals = model_inputs.get_symbolic_inputs() self.assertTrue(tensor_util.is_tensor(vals['a'])) self.assertTrue(tensor_util.is_tensor(vals['b'])) @@ -236,12 +79,7 @@ class ModelInputsTest(test.TestCase): with context.eager_mode(): a = {'b': np.ones(10), 'a': np.ones(20)} model_inputs = training_utils.ModelInputs(a) - self.assertEquals(['a', 'b'], model_inputs.get_input_names()) - vals = model_inputs.get_input_values() - self.assertAllEqual(np.ones(20), vals['a']) - self.assertAllEqual(np.ones(10), vals['b']) - self.assertTrue(tensor_util.is_tensor(vals['a'])) - self.assertTrue(tensor_util.is_tensor(vals['b'])) + self.assertEqual(['a', 'b'], model_inputs.get_input_names()) vals = model_inputs.get_symbolic_inputs() self.assertTrue(tf_utils.is_symbolic_tensor(vals['a'])) self.assertTrue(tf_utils.is_symbolic_tensor(vals['b'])) diff --git a/tensorflow/python/keras/estimator/__init__.py b/tensorflow/python/keras/estimator/__init__.py index b244beb5b5..dcd0600897 100644 --- a/tensorflow/python/keras/estimator/__init__.py +++ b/tensorflow/python/keras/estimator/__init__.py @@ -24,23 +24,54 @@ from tensorflow.python.util.tf_export import tf_export # As long as you depend //third_party/py/tensorflow:tensorflow target # everything will work as normal. -try: - from tensorflow.python.estimator import keras as keras_lib # pylint: disable=g-import-not-at-top - model_to_estimator = tf_export('keras.estimator.model_to_estimator')( - keras_lib.model_to_estimator) -except Exception: # pylint: disable=broad-except - - # pylint: disable=unused-argument - def stub_model_to_estimator(keras_model=None, - keras_model_path=None, - custom_objects=None, - model_dir=None, - config=None): + +# LINT.IfChange +@tf_export('keras.estimator.model_to_estimator') +def model_to_estimator( + keras_model=None, + keras_model_path=None, + custom_objects=None, + model_dir=None, + config=None): + """Constructs an `Estimator` instance from given keras model. + + For usage example, please see: + [Creating estimators from Keras + Models](https://tensorflow.org/guide/estimators#model_to_estimator). + + Args: + keras_model: A compiled Keras model object. This argument is mutually + exclusive with `keras_model_path`. + keras_model_path: Path to a compiled Keras model saved on disk, in HDF5 + format, which can be generated with the `save()` method of a Keras model. + This argument is mutually exclusive with `keras_model`. + custom_objects: Dictionary for custom objects. + model_dir: Directory to save `Estimator` model parameters, graph, summary + files for TensorBoard, etc. + config: `RunConfig` to config `Estimator`. + + Returns: + An Estimator from given keras model. + + Raises: + ValueError: if neither keras_model nor keras_model_path was given. + ValueError: if both keras_model and keras_model_path was given. + ValueError: if the keras_model_path is a GCS URI. + ValueError: if keras_model has not been compiled. + """ + try: + from tensorflow_estimator.python.estimator import keras as keras_lib # pylint: disable=g-import-not-at-top + except ImportError: raise NotImplementedError( 'tf.keras.estimator.model_to_estimator function not available in your ' 'installation.') - # pylint: enable=unused-argument + return keras_lib.model_to_estimator( + keras_model=keras_model, + keras_model_path=keras_model_path, + custom_objects=custom_objects, + model_dir=model_dir, + config=config) + +# LINT.ThenChange(//third_party/tensorflow_estimator/python/estimator/keras.py) - model_to_estimator = tf_export('keras.estimator.model_to_estimator')( - stub_model_to_estimator) diff --git a/tensorflow/python/keras/initializers_test.py b/tensorflow/python/keras/initializers_test.py index 2b758a98f3..4f91bea1e3 100644 --- a/tensorflow/python/keras/initializers_test.py +++ b/tensorflow/python/keras/initializers_test.py @@ -22,6 +22,7 @@ import numpy as np from tensorflow.python import keras from tensorflow.python.ops import init_ops +from tensorflow.python.framework import test_util from tensorflow.python.platform import test @@ -38,6 +39,7 @@ class KerasInitializersTest(test.TestCase): output_2 = keras.backend.get_value(variable) self.assertAllClose(output, output_2, atol=1e-4) + @test_util.run_deprecated_v1 def test_uniform(self): tensor_shape = (9, 6, 7) with self.cached_session(): @@ -47,6 +49,7 @@ class KerasInitializersTest(test.TestCase): tensor_shape, target_mean=0., target_max=1, target_min=-1) + @test_util.run_deprecated_v1 def test_normal(self): tensor_shape = (8, 12, 99) with self.cached_session(): @@ -54,6 +57,7 @@ class KerasInitializersTest(test.TestCase): tensor_shape, target_mean=0., target_std=1) + @test_util.run_deprecated_v1 def test_truncated_normal(self): tensor_shape = (12, 99, 7) with self.cached_session(): @@ -69,6 +73,7 @@ class KerasInitializersTest(test.TestCase): self._runner(keras.initializers.Constant(2), tensor_shape, target_mean=2, target_max=2, target_min=2) + @test_util.run_deprecated_v1 def test_lecun_uniform(self): tensor_shape = (5, 6, 4, 2) with self.cached_session(): @@ -77,6 +82,7 @@ class KerasInitializersTest(test.TestCase): self._runner(keras.initializers.lecun_uniform(seed=123), tensor_shape, target_mean=0., target_std=std) + @test_util.run_deprecated_v1 def test_glorot_uniform(self): tensor_shape = (5, 6, 4, 2) with self.cached_session(): @@ -85,6 +91,7 @@ class KerasInitializersTest(test.TestCase): self._runner(keras.initializers.glorot_uniform(seed=123), tensor_shape, target_mean=0., target_std=std) + @test_util.run_deprecated_v1 def test_he_uniform(self): tensor_shape = (5, 6, 4, 2) with self.cached_session(): @@ -93,6 +100,7 @@ class KerasInitializersTest(test.TestCase): self._runner(keras.initializers.he_uniform(seed=123), tensor_shape, target_mean=0., target_std=std) + @test_util.run_deprecated_v1 def test_lecun_normal(self): tensor_shape = (5, 6, 4, 2) with self.cached_session(): @@ -101,6 +109,7 @@ class KerasInitializersTest(test.TestCase): self._runner(keras.initializers.lecun_normal(seed=123), tensor_shape, target_mean=0., target_std=std) + @test_util.run_deprecated_v1 def test_glorot_normal(self): tensor_shape = (5, 6, 4, 2) with self.cached_session(): @@ -109,6 +118,7 @@ class KerasInitializersTest(test.TestCase): self._runner(keras.initializers.glorot_normal(seed=123), tensor_shape, target_mean=0., target_std=std) + @test_util.run_deprecated_v1 def test_he_normal(self): tensor_shape = (5, 6, 4, 2) with self.cached_session(): @@ -117,6 +127,7 @@ class KerasInitializersTest(test.TestCase): self._runner(keras.initializers.he_normal(seed=123), tensor_shape, target_mean=0., target_std=std) + @test_util.run_deprecated_v1 def test_orthogonal(self): tensor_shape = (20, 20) with self.cached_session(): diff --git a/tensorflow/python/keras/integration_test.py b/tensorflow/python/keras/integration_test.py index 3c0f73b1c3..23f5438505 100644 --- a/tensorflow/python/keras/integration_test.py +++ b/tensorflow/python/keras/integration_test.py @@ -22,6 +22,7 @@ import numpy as np from tensorflow.python import keras from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.keras import testing_utils from tensorflow.python.layers import core as tf_core_layers from tensorflow.python.ops import nn @@ -34,6 +35,7 @@ class KerasIntegrationTest(test.TestCase): def test_version(self): self.assertTrue(keras.__version__.endswith('-tf')) + @test_util.run_deprecated_v1 def test_vector_classification_sequential(self): with self.cached_session(): np.random.seed(1337) @@ -59,6 +61,7 @@ class KerasIntegrationTest(test.TestCase): verbose=2) self.assertGreater(history.history['val_acc'][-1], 0.7) + @test_util.run_deprecated_v1 def test_vector_classification_functional(self): with self.cached_session(): np.random.seed(1337) @@ -83,6 +86,7 @@ class KerasIntegrationTest(test.TestCase): verbose=2) self.assertGreater(history.history['val_acc'][-1], 0.7) + @test_util.run_deprecated_v1 def test_temporal_classification_sequential(self): with self.cached_session(): np.random.seed(1337) @@ -105,6 +109,7 @@ class KerasIntegrationTest(test.TestCase): verbose=2) self.assertGreater(history.history['val_acc'][-1], 0.7) + @test_util.run_deprecated_v1 def test_temporal_classification_sequential_tf_rnn(self): with self.cached_session(): np.random.seed(1337) @@ -163,6 +168,7 @@ class KerasIntegrationTest(test.TestCase): verbose=2) self.assertGreater(history.history['val_acc'][-1], 0.7) + @test_util.run_deprecated_v1 def test_video_classification_functional(self): with self.cached_session(): np.random.seed(1337) @@ -191,6 +197,7 @@ class KerasIntegrationTest(test.TestCase): verbose=2) self.assertGreater(history.history['val_acc'][-1], 0.7) + @test_util.run_deprecated_v1 def test_vector_classification_shared_sequential(self): # Test that Sequential models that feature internal updates # and internal losses can be shared. @@ -225,6 +232,7 @@ class KerasIntegrationTest(test.TestCase): verbose=2) self.assertGreater(history.history['val_acc'][-1], 0.7) + @test_util.run_deprecated_v1 def test_vector_classification_shared_model(self): # Test that functional models that feature internal updates # and internal losses can be shared. @@ -312,6 +320,15 @@ class KerasIntegrationTest(test.TestCase): verbose=0) self.assertGreater(history.history['val_acc'][-1], 0.7) + def test_regularizers_with_get_variable(self): + # Test case for GitHub issue 22470. + with self.cached_session(): + v = variable_scope.get_variable( + "v", + shape=[4, 4], + initializer=keras.initializers.glorot_uniform(), + regularizer=keras.regularizers.l2(0.)) + if __name__ == '__main__': test.main() diff --git a/tensorflow/python/keras/layers/__init__.py b/tensorflow/python/keras/layers/__init__.py index 7268040b02..49990b6bf4 100644 --- a/tensorflow/python/keras/layers/__init__.py +++ b/tensorflow/python/keras/layers/__init__.py @@ -22,7 +22,7 @@ from __future__ import print_function # pylint: disable=g-bad-import-order from tensorflow.python.keras.engine.input_layer import Input from tensorflow.python.keras.engine.input_layer import InputLayer -from tensorflow.python.keras.engine.base_layer import InputSpec +from tensorflow.python.keras.engine.input_spec import InputSpec from tensorflow.python.keras.engine.base_layer import Layer # Advanced activations. diff --git a/tensorflow/python/keras/layers/advanced_activations.py b/tensorflow/python/keras/layers/advanced_activations.py index b0dffced3e..35ac7830b2 100644 --- a/tensorflow/python/keras/layers/advanced_activations.py +++ b/tensorflow/python/keras/layers/advanced_activations.py @@ -22,8 +22,8 @@ from tensorflow.python.keras import backend as K from tensorflow.python.keras import constraints from tensorflow.python.keras import initializers from tensorflow.python.keras import regularizers -from tensorflow.python.keras.engine.base_layer import InputSpec from tensorflow.python.keras.engine.base_layer import Layer +from tensorflow.python.keras.engine.input_spec import InputSpec from tensorflow.python.keras.utils import tf_utils from tensorflow.python.ops import math_ops from tensorflow.python.util.tf_export import tf_export @@ -54,7 +54,6 @@ class LeakyReLU(Layer): super(LeakyReLU, self).__init__(**kwargs) self.supports_masking = True self.alpha = K.cast_to_floatx(alpha) - self._can_use_graph_functions = True def call(self, inputs): return K.relu(inputs, alpha=self.alpha) @@ -118,7 +117,6 @@ class PReLU(Layer): self.shared_axes = [shared_axes] else: self.shared_axes = list(shared_axes) - self._can_use_graph_functions = True @tf_utils.shape_type_conversion def build(self, input_shape): @@ -193,7 +191,6 @@ class ELU(Layer): super(ELU, self).__init__(**kwargs) self.supports_masking = True self.alpha = K.cast_to_floatx(alpha) - self._can_use_graph_functions = True def call(self, inputs): return K.elu(inputs, self.alpha) @@ -233,7 +230,6 @@ class ThresholdedReLU(Layer): super(ThresholdedReLU, self).__init__(**kwargs) self.supports_masking = True self.theta = K.cast_to_floatx(theta) - self._can_use_graph_functions = True def call(self, inputs, mask=None): return inputs * math_ops.cast( @@ -269,7 +265,6 @@ class Softmax(Layer): super(Softmax, self).__init__(**kwargs) self.supports_masking = True self.axis = axis - self._can_use_graph_functions = True def call(self, inputs): return K.softmax(inputs, axis=self.axis) @@ -324,7 +319,6 @@ class ReLU(Layer): self.max_value = max_value self.negative_slope = K.cast_to_floatx(negative_slope) self.threshold = K.cast_to_floatx(threshold) - self._can_use_graph_functions = True def call(self, inputs): # alpha is used for leaky relu slope in activations instead of diff --git a/tensorflow/python/keras/layers/convolutional.py b/tensorflow/python/keras/layers/convolutional.py index 0671a5a36d..6564d6e8fd 100644 --- a/tensorflow/python/keras/layers/convolutional.py +++ b/tensorflow/python/keras/layers/convolutional.py @@ -26,8 +26,8 @@ from tensorflow.python.keras import backend from tensorflow.python.keras import constraints from tensorflow.python.keras import initializers from tensorflow.python.keras import regularizers -from tensorflow.python.keras.engine.base_layer import InputSpec from tensorflow.python.keras.engine.base_layer import Layer +from tensorflow.python.keras.engine.input_spec import InputSpec # imports for backwards namespace compatibility # pylint: disable=unused-import from tensorflow.python.keras.layers.pooling import AveragePooling1D @@ -120,7 +120,6 @@ class Conv(Layer): name=name, activity_regularizer=regularizers.get(activity_regularizer), **kwargs) - self._can_use_graph_functions = True self.rank = rank self.filters = filters self.kernel_size = conv_utils.normalize_tuple( @@ -1916,7 +1915,6 @@ class UpSampling1D(Layer): super(UpSampling1D, self).__init__(**kwargs) self.size = int(size) self.input_spec = InputSpec(ndim=3) - self._can_use_graph_functions = True def compute_output_shape(self, input_shape): input_shape = tensor_shape.TensorShape(input_shape).as_list() @@ -1983,7 +1981,6 @@ class UpSampling2D(Layer): 'or `"bilinear"`.') self.interpolation = interpolation self.input_spec = InputSpec(ndim=4) - self._can_use_graph_functions = True def compute_output_shape(self, input_shape): input_shape = tensor_shape.TensorShape(input_shape).as_list() @@ -2054,7 +2051,6 @@ class UpSampling3D(Layer): self.size = conv_utils.normalize_tuple(size, 3, 'size') self.input_spec = InputSpec(ndim=5) super(UpSampling3D, self).__init__(**kwargs) - self._can_use_graph_functions = True def compute_output_shape(self, input_shape): input_shape = tensor_shape.TensorShape(input_shape).as_list() @@ -2109,7 +2105,6 @@ class ZeroPadding1D(Layer): def __init__(self, padding=1, **kwargs): super(ZeroPadding1D, self).__init__(**kwargs) - self._can_use_graph_functions = True self.padding = conv_utils.normalize_tuple(padding, 2, 'padding') self.input_spec = InputSpec(ndim=3) @@ -2175,7 +2170,6 @@ class ZeroPadding2D(Layer): def __init__(self, padding=(1, 1), data_format=None, **kwargs): super(ZeroPadding2D, self).__init__(**kwargs) - self._can_use_graph_functions = True self.data_format = conv_utils.normalize_data_format(data_format) if isinstance(padding, int): self.padding = ((padding, padding), (padding, padding)) @@ -2280,7 +2274,6 @@ class ZeroPadding3D(Layer): def __init__(self, padding=(1, 1, 1), data_format=None, **kwargs): super(ZeroPadding3D, self).__init__(**kwargs) - self._can_use_graph_functions = True self.data_format = conv_utils.normalize_data_format(data_format) if isinstance(padding, int): self.padding = ((padding, padding), (padding, padding), (padding, @@ -2375,7 +2368,6 @@ class Cropping1D(Layer): super(Cropping1D, self).__init__(**kwargs) self.cropping = conv_utils.normalize_tuple(cropping, 2, 'cropping') self.input_spec = InputSpec(ndim=3) - self._can_use_graph_functions = True def compute_output_shape(self, input_shape): input_shape = tensor_shape.TensorShape(input_shape).as_list() @@ -2475,7 +2467,6 @@ class Cropping2D(Layer): '((top_crop, bottom_crop), (left_crop, right_crop)). ' 'Found: ' + str(cropping)) self.input_spec = InputSpec(ndim=4) - self._can_use_graph_functions = True def compute_output_shape(self, input_shape): input_shape = tensor_shape.TensorShape(input_shape).as_list() @@ -2609,7 +2600,6 @@ class Cropping3D(Layer): ' (left_dim3_crop, right_dim2_crop)). ' 'Found: ' + str(cropping)) self.input_spec = InputSpec(ndim=5) - self._can_use_graph_functions = True def compute_output_shape(self, input_shape): input_shape = tensor_shape.TensorShape(input_shape).as_list() diff --git a/tensorflow/python/keras/layers/convolutional_recurrent.py b/tensorflow/python/keras/layers/convolutional_recurrent.py index 100542129b..cf3861da21 100644 --- a/tensorflow/python/keras/layers/convolutional_recurrent.py +++ b/tensorflow/python/keras/layers/convolutional_recurrent.py @@ -26,8 +26,8 @@ from tensorflow.python.keras import backend as K from tensorflow.python.keras import constraints from tensorflow.python.keras import initializers from tensorflow.python.keras import regularizers -from tensorflow.python.keras.engine.base_layer import InputSpec from tensorflow.python.keras.engine.base_layer import Layer +from tensorflow.python.keras.engine.input_spec import InputSpec from tensorflow.python.keras.layers.recurrent import _generate_dropout_mask from tensorflow.python.keras.layers.recurrent import _standardize_args from tensorflow.python.keras.layers.recurrent import RNN diff --git a/tensorflow/python/keras/layers/core.py b/tensorflow/python/keras/layers/core.py index e5c37be0aa..56dd70558c 100644 --- a/tensorflow/python/keras/layers/core.py +++ b/tensorflow/python/keras/layers/core.py @@ -34,8 +34,8 @@ from tensorflow.python.keras import backend as K from tensorflow.python.keras import constraints from tensorflow.python.keras import initializers from tensorflow.python.keras import regularizers -from tensorflow.python.keras.engine.base_layer import InputSpec from tensorflow.python.keras.engine.base_layer import Layer +from tensorflow.python.keras.engine.input_spec import InputSpec from tensorflow.python.keras.utils import conv_utils from tensorflow.python.keras.utils import generic_utils from tensorflow.python.keras.utils import tf_utils @@ -81,7 +81,6 @@ class Masking(Layer): super(Masking, self).__init__(**kwargs) self.supports_masking = True self.mask_value = mask_value - self._can_use_graph_functions = True def compute_mask(self, inputs, mask=None): return K.any(math_ops.not_equal(inputs, self.mask_value), axis=-1) @@ -125,7 +124,6 @@ class Dropout(Layer): self.noise_shape = noise_shape self.seed = seed self.supports_masking = True - self._can_use_graph_functions = True def _get_noise_shape(self, inputs): # Subclasses of `Dropout` may implement `_get_noise_shape(self, inputs)`, @@ -326,7 +324,6 @@ class Activation(Layer): super(Activation, self).__init__(**kwargs) self.supports_masking = True self.activation = activations.get(activation) - self._can_use_graph_functions = True def call(self, inputs): return self.activation(inputs) @@ -379,7 +376,6 @@ class Reshape(Layer): def __init__(self, target_shape, **kwargs): super(Reshape, self).__init__(**kwargs) self.target_shape = tuple(target_shape) - self._can_use_graph_functions = True def _fix_unknown_dimension(self, input_shape, output_shape): """Find and replace a missing dimension in an output shape. @@ -488,7 +484,6 @@ class Permute(Layer): 'The set of indices in `dims` must be consecutive and start from 1.' % (dims,)) self.input_spec = InputSpec(ndim=len(self.dims) + 1) - self._can_use_graph_functions = True def compute_output_shape(self, input_shape): input_shape = tensor_shape.TensorShape(input_shape).as_list() @@ -540,7 +535,6 @@ class Flatten(Layer): super(Flatten, self).__init__(**kwargs) self.data_format = conv_utils.normalize_data_format(data_format) self.input_spec = InputSpec(min_ndim=2) - self._can_use_graph_functions = True def call(self, inputs): if self.data_format == 'channels_first': @@ -600,7 +594,6 @@ class RepeatVector(Layer): super(RepeatVector, self).__init__(**kwargs) self.n = n self.input_spec = InputSpec(ndim=2) - self._can_use_graph_functions = True def compute_output_shape(self, input_shape): input_shape = tensor_shape.TensorShape(input_shape).as_list() @@ -929,7 +922,6 @@ class Dense(Layer): self.supports_masking = True self.input_spec = InputSpec(min_ndim=2) - self._can_use_graph_functions = True def build(self, input_shape): input_shape = tensor_shape.TensorShape(input_shape) @@ -1029,7 +1021,6 @@ class ActivityRegularization(Layer): self.supports_masking = True self.l1 = l1 self.l2 = l2 - self._can_use_graph_functions = True def compute_output_shape(self, input_shape): return input_shape diff --git a/tensorflow/python/keras/layers/cudnn_recurrent.py b/tensorflow/python/keras/layers/cudnn_recurrent.py index beacdf2515..16692753af 100644 --- a/tensorflow/python/keras/layers/cudnn_recurrent.py +++ b/tensorflow/python/keras/layers/cudnn_recurrent.py @@ -25,7 +25,8 @@ from tensorflow.python.keras import backend as K from tensorflow.python.keras import constraints from tensorflow.python.keras import initializers from tensorflow.python.keras import regularizers -from tensorflow.python.keras.engine.base_layer import InputSpec +from tensorflow.python.keras.engine.input_spec import InputSpec +from tensorflow.python.keras.layers import recurrent from tensorflow.python.keras.layers.recurrent import RNN from tensorflow.python.ops import array_ops from tensorflow.python.ops import gen_cudnn_rnn_ops @@ -80,11 +81,6 @@ class _CuDNNRNN(RNN): self._num_inputs = None self._vector_shape = constant_op.constant([-1]) - def _canonical_to_params(self, weights, biases): - weights = [array_ops.reshape(x, self._vector_shape) for x in weights] - biases = [array_ops.reshape(x, self._vector_shape) for x in biases] - return array_ops.concat(weights + biases, axis=0) - def call(self, inputs, mask=None, training=None, initial_state=None): if isinstance(mask, list): mask = mask[0] @@ -279,7 +275,7 @@ class CuDNNGRU(_CuDNNRNN): input_h = initial_state[0] input_h = array_ops.expand_dims(input_h, axis=0) - params = self._canonical_to_params( + params = recurrent._canonical_to_params( # pylint: disable=protected-access weights=[ self.kernel[:, self.units:self.units * 2], self.kernel[:, :self.units], @@ -296,7 +292,7 @@ class CuDNNGRU(_CuDNNRNN): self.bias[self.units * 3:self.units * 4], self.bias[self.units * 5:], ], - ) + shape=self._vector_shape) outputs, h, _, _ = gen_cudnn_rnn_ops.cudnn_rnn( inputs, @@ -474,7 +470,7 @@ class CuDNNLSTM(_CuDNNRNN): input_h = array_ops.expand_dims(input_h, axis=0) input_c = array_ops.expand_dims(input_c, axis=0) - params = self._canonical_to_params( + params = recurrent._canonical_to_params( # pylint: disable=protected-access weights=[ self.kernel[:, :self.units], self.kernel[:, self.units:self.units * 2], @@ -495,7 +491,7 @@ class CuDNNLSTM(_CuDNNRNN): self.bias[self.units * 6:self.units * 7], self.bias[self.units * 7:], ], - ) + shape=self._vector_shape) outputs, h, c, _ = gen_cudnn_rnn_ops.cudnn_rnn( inputs, diff --git a/tensorflow/python/keras/layers/embeddings.py b/tensorflow/python/keras/layers/embeddings.py index 5d805ea684..e8a8575705 100644 --- a/tensorflow/python/keras/layers/embeddings.py +++ b/tensorflow/python/keras/layers/embeddings.py @@ -45,11 +45,11 @@ class Embedding(Layer): model = Sequential() model.add(Embedding(1000, 64, input_length=10)) # the model will take as input an integer matrix of size (batch, - input_length). + # input_length). # the largest integer (i.e. word index) in the input should be no larger - than 999 (vocabulary size). + # than 999 (vocabulary size). # now model.output_shape == (None, 10, 64), where None is the batch - dimension. + # dimension. input_array = np.random.randint(1000, size=(32, 10)) @@ -116,7 +116,6 @@ class Embedding(Layer): self.mask_zero = mask_zero self.supports_masking = mask_zero self.input_length = input_length - self._can_use_graph_functions = True @tf_utils.shape_type_conversion def build(self, input_shape): diff --git a/tensorflow/python/keras/layers/local.py b/tensorflow/python/keras/layers/local.py index 30b83eaf50..d2c4aaa125 100644 --- a/tensorflow/python/keras/layers/local.py +++ b/tensorflow/python/keras/layers/local.py @@ -23,8 +23,8 @@ from tensorflow.python.keras import backend as K from tensorflow.python.keras import constraints from tensorflow.python.keras import initializers from tensorflow.python.keras import regularizers -from tensorflow.python.keras.engine.base_layer import InputSpec from tensorflow.python.keras.engine.base_layer import Layer +from tensorflow.python.keras.engine.input_spec import InputSpec from tensorflow.python.keras.utils import conv_utils from tensorflow.python.keras.utils import tf_utils from tensorflow.python.util.tf_export import tf_export @@ -154,7 +154,6 @@ class LocallyConnected1D(Layer): self.bias_constraint = constraints.get(bias_constraint) self.implementation = implementation self.input_spec = InputSpec(ndim=3) - self._can_use_graph_functions = True @tf_utils.shape_type_conversion def build(self, input_shape): @@ -430,7 +429,6 @@ class LocallyConnected2D(Layer): self.bias_constraint = constraints.get(bias_constraint) self.implementation = implementation self.input_spec = InputSpec(ndim=4) - self._can_use_graph_functions = True @tf_utils.shape_type_conversion def build(self, input_shape): diff --git a/tensorflow/python/keras/layers/local_test.py b/tensorflow/python/keras/layers/local_test.py index 300e7c9654..e4f4d0a639 100644 --- a/tensorflow/python/keras/layers/local_test.py +++ b/tensorflow/python/keras/layers/local_test.py @@ -28,39 +28,43 @@ from tensorflow.python.training.rmsprop import RMSPropOptimizer class LocallyConnected1DLayersTest(test.TestCase): + # TODO(fchollet): investigate why LocallyConnected1D + # fails inside a graph function in an eager context (fails with error + # "Incompatible shapes between op input and calculated input gradient"). - @tf_test_util.run_in_graph_and_eager_modes + @tf_test_util.run_deprecated_v1 def test_locallyconnected_1d(self): - num_samples = 2 - num_steps = 8 - input_dim = 5 - filter_length = 3 - filters = 4 - - for padding in ['valid', 'same']: - for strides in [1]: - if padding == 'same' and strides != 1: - continue - for data_format in ['channels_first', 'channels_last']: - for implementation in [1, 2]: - kwargs = { - 'filters': filters, - 'kernel_size': filter_length, - 'padding': padding, - 'strides': strides, - 'data_format': data_format, - 'implementation': implementation - } + with self.cached_session(): + num_samples = 2 + num_steps = 8 + input_dim = 5 + filter_length = 3 + filters = 4 - if padding == 'same' and implementation == 1: - self.assertRaises(ValueError, - keras.layers.LocallyConnected1D, - **kwargs) - else: - testing_utils.layer_test( - keras.layers.LocallyConnected1D, - kwargs=kwargs, - input_shape=(num_samples, num_steps, input_dim)) + for padding in ['valid', 'same']: + for strides in [1]: + if padding == 'same' and strides != 1: + continue + for data_format in ['channels_first', 'channels_last']: + for implementation in [1, 2]: + kwargs = { + 'filters': filters, + 'kernel_size': filter_length, + 'padding': padding, + 'strides': strides, + 'data_format': data_format, + 'implementation': implementation + } + + if padding == 'same' and implementation == 1: + self.assertRaises(ValueError, + keras.layers.LocallyConnected1D, + **kwargs) + else: + testing_utils.layer_test( + keras.layers.LocallyConnected1D, + kwargs=kwargs, + input_shape=(num_samples, num_steps, input_dim)) def test_locallyconnected_1d_regularization(self): num_samples = 2 @@ -113,30 +117,63 @@ class LocallyConnected1DLayersTest(test.TestCase): class LocallyConnected2DLayersTest(test.TestCase): + # TODO(fchollet): investigate why LocallyConnected2D + # fails inside a graph function in an eager context (fails with error + # "Incompatible shapes between op input and calculated input gradient"). - @tf_test_util.run_in_graph_and_eager_modes + @tf_test_util.run_deprecated_v1 def test_locallyconnected_2d(self): - num_samples = 8 - filters = 3 - stack_size = 4 - num_row = 6 - num_col = 10 + with self.cached_session(): + num_samples = 8 + filters = 3 + stack_size = 4 + num_row = 6 + num_col = 10 - for padding in ['valid', 'same']: - for strides in [(1, 1), (2, 2)]: - for implementation in [1, 2]: - if padding == 'same' and strides != (1, 1): - continue + for padding in ['valid', 'same']: + for strides in [(1, 1), (2, 2)]: + for implementation in [1, 2]: + if padding == 'same' and strides != (1, 1): + continue + + kwargs = { + 'filters': filters, + 'kernel_size': 3, + 'padding': padding, + 'kernel_regularizer': 'l2', + 'bias_regularizer': 'l2', + 'strides': strides, + 'data_format': 'channels_last', + 'implementation': implementation + } + if padding == 'same' and implementation == 1: + self.assertRaises(ValueError, + keras.layers.LocallyConnected2D, + **kwargs) + else: + testing_utils.layer_test( + keras.layers.LocallyConnected2D, + kwargs=kwargs, + input_shape=(num_samples, num_row, num_col, stack_size)) + + @tf_test_util.run_deprecated_v1 + def test_locallyconnected_2d_channels_first(self): + with self.cached_session(): + num_samples = 8 + filters = 3 + stack_size = 4 + num_row = 6 + num_col = 10 + + for implementation in [1, 2]: + for padding in ['valid', 'same']: kwargs = { 'filters': filters, 'kernel_size': 3, - 'padding': padding, - 'kernel_regularizer': 'l2', - 'bias_regularizer': 'l2', - 'strides': strides, - 'data_format': 'channels_last', - 'implementation': implementation + 'data_format': 'channels_first', + 'implementation': implementation, + 'padding': padding } if padding == 'same' and implementation == 1: @@ -149,34 +186,6 @@ class LocallyConnected2DLayersTest(test.TestCase): kwargs=kwargs, input_shape=(num_samples, num_row, num_col, stack_size)) - @tf_test_util.run_in_graph_and_eager_modes - def test_locallyconnected_2d_channels_first(self): - num_samples = 8 - filters = 3 - stack_size = 4 - num_row = 6 - num_col = 10 - - for implementation in [1, 2]: - for padding in ['valid', 'same']: - kwargs = { - 'filters': filters, - 'kernel_size': 3, - 'data_format': 'channels_first', - 'implementation': implementation, - 'padding': padding - } - - if padding == 'same' and implementation == 1: - self.assertRaises(ValueError, - keras.layers.LocallyConnected2D, - **kwargs) - else: - testing_utils.layer_test( - keras.layers.LocallyConnected2D, - kwargs=kwargs, - input_shape=(num_samples, num_row, num_col, stack_size)) - def test_locallyconnected_2d_regularization(self): num_samples = 2 filters = 3 @@ -226,64 +235,67 @@ class LocallyConnected2DLayersTest(test.TestCase): class LocallyConnectedImplementationModeTest(test.TestCase): - @tf_test_util.run_in_graph_and_eager_modes + @tf_test_util.run_deprecated_v1 def test_locallyconnected_implementation(self): - num_samples = 4 - num_classes = 3 - num_epochs = 2 - - np.random.seed(1) - targets = np.random.randint(0, num_classes, (num_samples,)) - - for width in [1, 6]: - for height in [7]: - for filters in [2]: - for data_format in ['channels_first', 'channels_last']: - inputs = get_inputs( - data_format, filters, height, num_samples, width) - - for kernel_x in [(3,)]: - for kernel_y in [()] if width == 1 else [(2,)]: - for stride_x in [(1,)]: - for stride_y in [()] if width == 1 else [(3,)]: - for layers in [2]: - kwargs = { - 'layers': layers, - 'filters': filters, - 'kernel_size': kernel_x + kernel_y, - 'strides': stride_x + stride_y, - 'data_format': data_format, - 'num_classes': num_classes, - 'input_shape': inputs.shape - } - - model_1 = get_model(implementation=1, **kwargs) - model_2 = get_model(implementation=2, **kwargs) - - copy_model_weights(model_2, model_1) - - # Compare outputs at initialization. - out_1 = model_1.call(inputs) - out_2 = model_2.call(inputs) - self.assertAllCloseAccordingToType(out_1, out_2, - rtol=1e-5, atol=1e-5) - - # Train. - model_1.fit(x=inputs, - y=targets, - epochs=num_epochs, - batch_size=num_samples) - - model_2.fit(x=inputs, - y=targets, - epochs=num_epochs, - batch_size=num_samples) - - # Compare outputs after a few training steps. - out_1 = model_1.call(inputs) - out_2 = model_2.call(inputs) - self.assertAllCloseAccordingToType(out_1, out_2, - rtol=1e-5, atol=1e-5) + with self.cached_session(): + num_samples = 4 + num_classes = 3 + num_epochs = 2 + + np.random.seed(1) + targets = np.random.randint(0, num_classes, (num_samples,)) + + for width in [1, 6]: + for height in [7]: + for filters in [2]: + for data_format in ['channels_first', 'channels_last']: + inputs = get_inputs( + data_format, filters, height, num_samples, width) + + for kernel_x in [(3,)]: + for kernel_y in [()] if width == 1 else [(2,)]: + for stride_x in [(1,)]: + for stride_y in [()] if width == 1 else [(3,)]: + for layers in [2]: + kwargs = { + 'layers': layers, + 'filters': filters, + 'kernel_size': kernel_x + kernel_y, + 'strides': stride_x + stride_y, + 'data_format': data_format, + 'num_classes': num_classes + } + model_1 = get_model(implementation=1, **kwargs) + model_2 = get_model(implementation=2, **kwargs) + + # Build models. + model_1.train_on_batch(inputs, targets) + model_2.train_on_batch(inputs, targets) + + # Copy weights. + copy_model_weights(model_2, model_1) + + # Compare outputs at initialization. + out_1 = model_1.call(inputs) + out_2 = model_2.call(inputs) + self.assertAllCloseAccordingToType(out_1, out_2, + rtol=1e-5, atol=1e-5) + + # Train. + model_1.fit(x=inputs, + y=targets, + epochs=num_epochs, + batch_size=num_samples) + model_2.fit(x=inputs, + y=targets, + epochs=num_epochs, + batch_size=num_samples) + + # Compare outputs after a few training steps. + out_1 = model_1.call(inputs) + out_2 = model_2.call(inputs) + self.assertAllCloseAccordingToType( + out_1, out_2, atol=2e-4) @tf_test_util.run_in_graph_and_eager_modes def test_make_2d(self): @@ -360,8 +372,7 @@ def get_model(implementation, strides, layers, num_classes, - data_format, - input_shape): + data_format): model = keras.Sequential() if len(kernel_size) == 1: @@ -390,7 +401,6 @@ def get_model(implementation, metrics=[keras.metrics.categorical_accuracy], loss=xent ) - model.build(input_shape) return model diff --git a/tensorflow/python/keras/layers/lstm_test.py b/tensorflow/python/keras/layers/lstm_test.py index 9db697871f..3f89cc398e 100644 --- a/tensorflow/python/keras/layers/lstm_test.py +++ b/tensorflow/python/keras/layers/lstm_test.py @@ -18,6 +18,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +from absl.testing import parameterized import numpy as np from tensorflow.python import keras @@ -30,7 +31,7 @@ from tensorflow.python.training.rmsprop import RMSPropOptimizer @tf_test_util.run_all_in_graph_and_eager_modes -class LSTMLayerTest(test.TestCase): +class LSTMLayerTest(test.TestCase, parameterized.TestCase): def test_return_sequences_LSTM(self): num_samples = 2 @@ -56,7 +57,7 @@ class LSTMLayerTest(test.TestCase): layer = keras.layers.LSTM(units, return_sequences=True) model.add(layer) outputs = model.layers[-1].output - self.assertEquals(outputs.get_shape().as_list(), [None, timesteps, units]) + self.assertEqual(outputs.get_shape().as_list(), [None, timesteps, units]) def test_dynamic_behavior_LSTM(self): num_samples = 2 @@ -83,17 +84,17 @@ class LSTMLayerTest(test.TestCase): 'recurrent_dropout': 0.1}, input_shape=(num_samples, timesteps, embedding_dim)) - def test_implementation_mode_LSTM(self): + @parameterized.parameters([0, 1, 2]) + def test_implementation_mode_LSTM(self, implementation_mode): num_samples = 2 timesteps = 3 embedding_dim = 4 units = 2 - for mode in [0, 1, 2]: - testing_utils.layer_test( - keras.layers.LSTM, - kwargs={'units': units, - 'implementation': mode}, - input_shape=(num_samples, timesteps, embedding_dim)) + testing_utils.layer_test( + keras.layers.LSTM, + kwargs={'units': units, + 'implementation': implementation_mode}, + input_shape=(num_samples, timesteps, embedding_dim)) def test_constraints_LSTM(self): embedding_dim = 4 @@ -114,6 +115,7 @@ class LSTMLayerTest(test.TestCase): self.assertEqual(layer.cell.recurrent_kernel.constraint, r_constraint) self.assertEqual(layer.cell.bias.constraint, b_constraint) + @tf_test_util.run_deprecated_v1 def test_with_masking_layer_LSTM(self): layer_class = keras.layers.LSTM inputs = np.random.random((2, 3, 4)) @@ -126,6 +128,7 @@ class LSTMLayerTest(test.TestCase): optimizer=RMSPropOptimizer(0.01)) model.fit(inputs, targets, epochs=1, batch_size=2, verbose=1) + @tf_test_util.run_deprecated_v1 def test_masking_with_stacking_LSTM(self): inputs = np.random.random((2, 3, 4)) targets = np.abs(np.random.random((2, 3, 5))) @@ -311,6 +314,7 @@ class LSTMLayerTest(test.TestCase): class LSTMLayerGraphOnlyTest(test.TestCase): + @tf_test_util.run_deprecated_v1 def test_statefulness_LSTM(self): num_samples = 2 timesteps = 3 @@ -374,6 +378,7 @@ class LSTMLayerGraphOnlyTest(test.TestCase): self.assertAllClose(out7, out6, atol=1e-5) + @tf_test_util.run_deprecated_v1 def test_regularizers_LSTM(self): embedding_dim = 4 layer_class = keras.layers.LSTM diff --git a/tensorflow/python/keras/layers/merge.py b/tensorflow/python/keras/layers/merge.py index 0ded0e42ed..45e705c696 100644 --- a/tensorflow/python/keras/layers/merge.py +++ b/tensorflow/python/keras/layers/merge.py @@ -40,7 +40,6 @@ class _Merge(Layer): def __init__(self, **kwargs): super(_Merge, self).__init__(**kwargs) - self._can_use_graph_functions = True self.supports_masking = True def _merge_function(self, inputs): @@ -213,7 +212,7 @@ class _Merge(Layer): if len(mask) != len(inputs): raise ValueError('The lists `inputs` and `mask` ' 'should have the same length.') - if all([m is None for m in mask]): + if all(m is None for m in mask): return None masks = [array_ops.expand_dims(m, axis=0) for m in mask if m is not None] return K.all(K.concatenate(masks, axis=0), axis=0, keepdims=False) @@ -369,7 +368,6 @@ class Concatenate(_Merge): def __init__(self, axis=-1, **kwargs): super(Concatenate, self).__init__(**kwargs) - self._can_use_graph_functions = True self.axis = axis self.supports_masking = True self._reshape_required = False @@ -380,7 +378,7 @@ class Concatenate(_Merge): if not isinstance(input_shape, list) or len(input_shape) < 2: raise ValueError('A `Concatenate` layer should be called ' 'on a list of at least 2 inputs') - if all([shape is None for shape in input_shape]): + if all(shape is None for shape in input_shape): return reduced_inputs_shapes = [list(shape) for shape in input_shape] shape_set = set() @@ -420,7 +418,7 @@ class Concatenate(_Merge): if len(mask) != len(inputs): raise ValueError('The lists `inputs` and `mask` ' 'should have the same length.') - if all([m is None for m in mask]): + if all(m is None for m in mask): return None # Make a list of masks while making sure # the dimensionality of each mask @@ -467,7 +465,6 @@ class Dot(_Merge): def __init__(self, axes, normalize=False, **kwargs): super(Dot, self).__init__(**kwargs) - self._can_use_graph_functions = True if not isinstance(axes, int): if not isinstance(axes, (list, tuple)): raise TypeError('Invalid type for `axes` - ' diff --git a/tensorflow/python/keras/layers/merge_test.py b/tensorflow/python/keras/layers/merge_test.py index 698c5662b6..fcb161ae20 100644 --- a/tensorflow/python/keras/layers/merge_test.py +++ b/tensorflow/python/keras/layers/merge_test.py @@ -207,6 +207,7 @@ class MergeLayersGraphOnlyTest(test.TestCase): mask = layer.output_mask self.assertListEqual(mask.get_shape().as_list(), [None, 4]) + @tf_test_util.run_deprecated_v1 def test_merge_add_dynamic_shape(self): with self.cached_session(): i1 = array_ops.placeholder(shape=(4, None), dtype='float32') diff --git a/tensorflow/python/keras/layers/noise.py b/tensorflow/python/keras/layers/noise.py index e7c0478513..cb7cee3ebc 100644 --- a/tensorflow/python/keras/layers/noise.py +++ b/tensorflow/python/keras/layers/noise.py @@ -55,7 +55,6 @@ class GaussianNoise(Layer): super(GaussianNoise, self).__init__(**kwargs) self.supports_masking = True self.stddev = stddev - self._can_use_graph_functions = True def call(self, inputs, training=None): @@ -100,7 +99,6 @@ class GaussianDropout(Layer): super(GaussianDropout, self).__init__(**kwargs) self.supports_masking = True self.rate = rate - self._can_use_graph_functions = True def call(self, inputs, training=None): if 0 < self.rate < 1: @@ -155,7 +153,6 @@ class AlphaDropout(Layer): self.noise_shape = noise_shape self.seed = seed self.supports_masking = True - self._can_use_graph_functions = True def _get_noise_shape(self, inputs): return self.noise_shape if self.noise_shape else array_ops.shape(inputs) diff --git a/tensorflow/python/keras/layers/normalization.py b/tensorflow/python/keras/layers/normalization.py index 3d3bf647e6..d958497655 100644 --- a/tensorflow/python/keras/layers/normalization.py +++ b/tensorflow/python/keras/layers/normalization.py @@ -18,6 +18,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +from tensorflow.python import tf2 from tensorflow.python.eager import context from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops @@ -26,8 +27,8 @@ from tensorflow.python.keras import backend as K from tensorflow.python.keras import constraints from tensorflow.python.keras import initializers from tensorflow.python.keras import regularizers -from tensorflow.python.keras.engine.base_layer import InputSpec from tensorflow.python.keras.engine.base_layer import Layer +from tensorflow.python.keras.engine.input_spec import InputSpec from tensorflow.python.keras.utils import tf_utils from tensorflow.python.ops import array_ops from tensorflow.python.ops import init_ops @@ -40,8 +41,8 @@ from tensorflow.python.training import distribution_strategy_context from tensorflow.python.util.tf_export import tf_export -@tf_export('keras.layers.BatchNormalization') -class BatchNormalization(Layer): +@tf_export('keras.layers.BatchNormalization', v1=[]) +class BatchNormalizationV2(Layer): """Batch normalization layer (Ioffe and Szegedy, 2014). Normalize the activations of the previous layer at each batch, @@ -84,8 +85,10 @@ class BatchNormalization(Layer): and should be neither too small (which would add noise) nor too large (which would give stale estimates). Note that `momentum` is still applied to get the means and variances for inference. - fused: if `None` or `True`, use a faster, fused implementation if possible. - If `False`, use the system recommended implementation. + fused: if `True`, use a faster, fused implementation, or raise a ValueError + if the fused implementation cannot be used. If `None`, use the faster + implementation if possible. If False, do not used the fused + implementation. trainable: Boolean, if `True` also add variables to the graph collection `GraphKeys.TRAINABLE_VARIABLES` (see tf.Variable). virtual_batch_size: An `int`. By default, `virtual_batch_size` is `None`, @@ -120,6 +123,9 @@ class BatchNormalization(Layer): Internal Covariate Shift](https://arxiv.org/abs/1502.03167) """ + # The BatchNormalizationV1 subclass sets this to False to use the V1 behavior. + _USE_V2_BEHAVIOR = True + def __init__(self, axis=-1, momentum=0.99, @@ -143,13 +149,15 @@ class BatchNormalization(Layer): adjustment=None, name=None, **kwargs): - super(BatchNormalization, self).__init__( + super(BatchNormalizationV2, self).__init__( name=name, trainable=trainable, **kwargs) - self._can_use_graph_functions = True if isinstance(axis, list): self.axis = axis[:] - else: + elif isinstance(axis, int): self.axis = axis + else: + raise TypeError('axis must be int or list, type given: %s' + % type(self.axis)) self.momentum = momentum self.epsilon = epsilon self.center = center @@ -166,7 +174,14 @@ class BatchNormalization(Layer): self.renorm = renorm self.virtual_batch_size = virtual_batch_size self.adjustment = adjustment - if fused is None: + if self._USE_V2_BEHAVIOR: + if fused: + self._raise_if_fused_cannot_be_used() + # We leave fused as None if self._fused_can_be_used()==True, since we + # still may set it to False in self.build() if the input rank is not 4. + elif fused is None and not self._fused_can_be_used(): + fused = False + elif fused is None: fused = True self.supports_masking = True @@ -182,6 +197,38 @@ class BatchNormalization(Layer): self.renorm_clipping = renorm_clipping self.renorm_momentum = renorm_momentum + def _raise_if_fused_cannot_be_used(self): + """Raises a ValueError if fused implementation cannot be used. + + In addition to the checks done in this function, the input tensors rank must + be 4. The input rank check can only be done once the input shape is known. + """ + # Currently fused batch norm doesn't support renorm. It also only supports a + # channel dimension on axis 1 or 3, when no virtual batch size or adjustment + # is used. + if self.renorm: + raise ValueError('Passing both fused=True and renorm=True is ' + 'unsupported') + axis = [self.axis] if isinstance(self.axis, int) else self.axis + # Axis -3 is equivalent to 1, and axis -1 is equivalent to 3, because the + # input rank is required to be 4 (which is checked later). + if len(axis) > 1 or axis[0] not in (-3, -1, 1, 3): + raise ValueError('Passing fused=True is only supported when axis is 1 ' + 'or 3') + if self.virtual_batch_size is not None: + raise ValueError('Passing fused=True is unsupported when ' + 'virtual_batch_size is specified.') + if self.adjustment is not None: + raise ValueError('Passing fused=True is unsupported when ' + 'adjustment is specified.') + + def _fused_can_be_used(self): + try: + self._raise_if_fused_cannot_be_used() + return True + except ValueError: + return False + def build(self, input_shape): input_shape = tensor_shape.TensorShape(input_shape) if not input_shape.ndims: @@ -192,10 +239,6 @@ class BatchNormalization(Layer): if isinstance(self.axis, int): self.axis = [self.axis] - if not isinstance(self.axis, list): - raise TypeError('axis must be int or list, type given: %s' - % type(self.axis)) - for idx, x in enumerate(self.axis): if x < 0: self.axis[idx] = ndims + x @@ -220,16 +263,18 @@ class BatchNormalization(Layer): raise ValueError('When using virtual_batch_size, adjustment cannot ' 'be specified') - if self.fused: - # Currently fused batch norm doesn't support renorm. It also only supports - # an input tensor of rank 4 and a channel dimension on axis 1 or 3. + if self.fused in (None, True): # TODO(yaozhang): if input is not 4D, reshape it to 4D and reshape the # output back to its original shape accordingly. - self.fused = (not self.renorm and - ndims == 4 and - self.axis in [[1], [3]] and - self.virtual_batch_size is None and - self.adjustment is None) + if self._USE_V2_BEHAVIOR: + if self.fused is None: + self.fused = (ndims == 4) + elif self.fused and ndims != 4: + raise ValueError('Batch normalization layers with fused=True only ' + 'support 4D input tensors.') + else: + assert self.fused is not None + self.fused = (ndims == 4 and self._fused_can_be_used()) # TODO(chrisying): fused batch norm is currently not supported for # multi-axis batch norm and by extension virtual batches. In some cases, # it might be possible to use fused batch norm but would require reshaping @@ -492,6 +537,9 @@ class BatchNormalization(Layer): return (r, d, new_mean, new_variance) + def _moments(self, inputs, reduction_axes, keep_dims): + return nn.moments(inputs, reduction_axes, keep_dims=keep_dims) + def call(self, inputs, training=None): if training is None: training = K.learning_phase() @@ -563,7 +611,8 @@ class BatchNormalization(Layer): # Some of the computations here are not necessary when training==False # but not a constant. However, this makes the code simpler. keep_dims = self.virtual_batch_size is not None or len(self.axis) > 1 - mean, variance = nn.moments(inputs, reduction_axes, keep_dims=keep_dims) + mean, variance = self._moments( + inputs, reduction_axes, keep_dims=keep_dims) moving_mean = self.moving_mean moving_variance = self.moving_variance @@ -669,5 +718,36 @@ class BatchNormalization(Layer): 'layer cannot be serialized and has been omitted from ' 'the layer config. It will not be included when ' 're-creating the layer from the saved config.') - base_config = super(BatchNormalization, self).get_config() + base_config = super(BatchNormalizationV2, self).get_config() return dict(list(base_config.items()) + list(config.items())) + + +def _replace_in_v2_docstring(old, new): + string = BatchNormalizationV2.__doc__ + if old not in string: + raise ValueError('Could not find following string in BatchNormalizationV2 ' + 'docstring: "{}"'.format(old)) + return string.replace(old, new) + + +@tf_export(v1=['keras.layers.BatchNormalization']) # pylint: disable=missing-docstring +class BatchNormalizationV1(BatchNormalizationV2): + + __doc__ = _replace_in_v2_docstring( + ''' + fused: if `True`, use a faster, fused implementation, or raise a ValueError + if the fused implementation cannot be used. If `None`, use the faster + implementation if possible. If False, do not used the fused + implementation.''', + + ''' + fused: if `None` or `True`, use a faster, fused implementation if possible. + If `False`, use the system recommended implementation.''') + + _USE_V2_BEHAVIOR = False + + +if tf2.enabled(): + BatchNormalization = BatchNormalizationV2 +else: + BatchNormalization = BatchNormalizationV1 diff --git a/tensorflow/python/keras/layers/normalization_test.py b/tensorflow/python/keras/layers/normalization_test.py index 92e4128707..9138c0a08a 100644 --- a/tensorflow/python/keras/layers/normalization_test.py +++ b/tensorflow/python/keras/layers/normalization_test.py @@ -23,6 +23,7 @@ import numpy as np from tensorflow.python import keras from tensorflow.python.framework import test_util as tf_test_util from tensorflow.python.keras import testing_utils +from tensorflow.python.keras.layers import normalization from tensorflow.python.platform import test from tensorflow.python.training import gradient_descent @@ -54,6 +55,14 @@ class NormalizationLayersTest(test.TestCase): kwargs={'scale': False, 'center': False}, input_shape=(3, 3)) + testing_utils.layer_test( + normalization.BatchNormalizationV2, + kwargs={'fused': True}, + input_shape=(3, 3, 3, 3)) + testing_utils.layer_test( + normalization.BatchNormalizationV2, + kwargs={'fused': None}, + input_shape=(3, 3, 3)) def test_batchnorm_weights(self): layer = keras.layers.BatchNormalization(scale=False, center=False) @@ -78,15 +87,18 @@ class NormalizationLayersTest(test.TestCase): self.assertEqual(layer.gamma.constraint, max_norm) self.assertEqual(layer.beta.constraint, max_norm) - def test_batchnorm_correctness(self): + def _test_batchnorm_correctness(self, dtype, use_v2=True, fused=False): model = keras.models.Sequential() - norm = keras.layers.BatchNormalization(input_shape=(10,), momentum=0.8) + layer_ctor = (normalization.BatchNormalizationV2 if use_v2 + else normalization.BatchNormalizationV1) + norm = layer_ctor(input_shape=(2, 2, 2), momentum=0.8, fused=fused) model.add(norm) model.compile(loss='mse', optimizer=gradient_descent.GradientDescentOptimizer(0.01)) # centered on 5.0, variance 10.0 - x = np.random.normal(loc=5.0, scale=10.0, size=(1000, 10)) + x = (np.random.normal(loc=5.0, scale=10.0, size=(1000, 2, 2, 2)) + .astype(dtype)) model.fit(x, x, epochs=4, verbose=0) out = model.predict(x) out -= keras.backend.eval(norm.beta) @@ -95,23 +107,15 @@ class NormalizationLayersTest(test.TestCase): np.testing.assert_allclose(out.mean(), 0.0, atol=1e-1) np.testing.assert_allclose(out.std(), 1.0, atol=1e-1) - def test_batchnorm_mixed_precision(self): - model = keras.models.Sequential() - norm = keras.layers.BatchNormalization(input_shape=(10,), momentum=0.8) - model.add(norm) - model.compile(loss='mse', - optimizer=gradient_descent.GradientDescentOptimizer(0.01)) - - # centered on 5.0, variance 10.0 - x = np.random.normal( - loc=5.0, scale=10.0, size=(1000, 10)).astype(np.float16) - model.fit(x, x, epochs=4, verbose=0) - out = model.predict(x) - out -= keras.backend.eval(norm.beta) - out /= keras.backend.eval(norm.gamma) + def test_batchnorm_correctness(self): + self._test_batchnorm_correctness(np.float32) + self._test_batchnorm_correctness(np.float32, fused=True) + self._test_batchnorm_correctness(np.float32, use_v2=False) - np.testing.assert_allclose(out.mean(), 0.0, atol=1e-1) - np.testing.assert_allclose(out.std(), 1.0, atol=1e-1) + def test_batchnorm_mixed_precision(self): + self._test_batchnorm_correctness(np.float16) + self._test_batchnorm_correctness(np.float16, fused=True) + self._test_batchnorm_correctness(np.float16, use_v2=False) def test_batchnorm_convnet(self): if test.is_gpu_available(cuda_only=True): @@ -151,6 +155,77 @@ class NormalizationLayersTest(test.TestCase): np.testing.assert_allclose(np.mean(out, axis=(0, 1, 2)), 0.0, atol=1e-1) np.testing.assert_allclose(np.std(out, axis=(0, 1, 2)), 1.0, atol=1e-1) + def test_v1_fused_attribute(self): + norm = normalization.BatchNormalizationV1() + inp = keras.layers.Input((4, 4, 4)) + norm(inp) + self.assertEqual(norm.fused, True) + + norm = normalization.BatchNormalizationV1(fused=False) + self.assertEqual(norm.fused, False) + inp = keras.layers.Input(shape=(4, 4, 4)) + norm(inp) + self.assertEqual(norm.fused, False) + + norm = normalization.BatchNormalizationV1(virtual_batch_size=2) + self.assertEqual(norm.fused, True) + inp = keras.layers.Input(shape=(2, 2, 2)) + norm(inp) + self.assertEqual(norm.fused, False) + + def test_v2_fused_attribute(self): + norm = normalization.BatchNormalizationV2() + self.assertEqual(norm.fused, None) + inp = keras.layers.Input(shape=(4, 4, 4)) + norm(inp) + self.assertEqual(norm.fused, True) + + norm = normalization.BatchNormalizationV2() + self.assertEqual(norm.fused, None) + inp = keras.layers.Input(shape=(4, 4)) + norm(inp) + self.assertEqual(norm.fused, False) + + norm = normalization.BatchNormalizationV2(virtual_batch_size=2) + self.assertEqual(norm.fused, False) + inp = keras.layers.Input(shape=(4, 4, 4)) + norm(inp) + self.assertEqual(norm.fused, False) + + norm = normalization.BatchNormalizationV2(fused=False) + self.assertEqual(norm.fused, False) + inp = keras.layers.Input(shape=(4, 4, 4)) + norm(inp) + self.assertEqual(norm.fused, False) + + norm = normalization.BatchNormalizationV2(fused=True, axis=[3]) + self.assertEqual(norm.fused, True) + inp = keras.layers.Input(shape=(4, 4, 4)) + norm(inp) + self.assertEqual(norm.fused, True) + + with self.assertRaisesRegexp(ValueError, 'fused.*renorm'): + normalization.BatchNormalizationV2(fused=True, renorm=True) + + with self.assertRaisesRegexp(ValueError, 'fused.*when axis is 1 or 3'): + normalization.BatchNormalizationV2(fused=True, axis=2) + + with self.assertRaisesRegexp(ValueError, 'fused.*when axis is 1 or 3'): + normalization.BatchNormalizationV2(fused=True, axis=[1, 3]) + + with self.assertRaisesRegexp(ValueError, 'fused.*virtual_batch_size'): + normalization.BatchNormalizationV2(fused=True, virtual_batch_size=2) + + with self.assertRaisesRegexp(ValueError, 'fused.*adjustment'): + normalization.BatchNormalizationV2(fused=True, + adjustment=lambda _: (1, 0)) + + norm = normalization.BatchNormalizationV2(fused=True) + self.assertEqual(norm.fused, True) + inp = keras.layers.Input(shape=(4, 4)) + with self.assertRaisesRegexp(ValueError, '4D input tensors'): + norm(inp) + class NormalizationLayersGraphModeOnlyTest(test.TestCase): @@ -226,6 +301,7 @@ class NormalizationLayersGraphModeOnlyTest(test.TestCase): x2 = model.predict(val_a) self.assertAllClose(x1, x2, atol=1e-7) + @tf_test_util.run_deprecated_v1 def test_batchnorm_trainable(self): """Tests that batchnorm layer is trainable when learning phase is enabled. diff --git a/tensorflow/python/keras/layers/pooling.py b/tensorflow/python/keras/layers/pooling.py index b8d6b03664..a0744cddad 100644 --- a/tensorflow/python/keras/layers/pooling.py +++ b/tensorflow/python/keras/layers/pooling.py @@ -22,8 +22,8 @@ import functools from tensorflow.python.framework import tensor_shape from tensorflow.python.keras import backend -from tensorflow.python.keras.engine.base_layer import InputSpec from tensorflow.python.keras.engine.base_layer import Layer +from tensorflow.python.keras.engine.input_spec import InputSpec from tensorflow.python.keras.utils import conv_utils from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops @@ -58,7 +58,6 @@ class Pooling1D(Layer): padding='valid', data_format='channels_last', name=None, **kwargs): super(Pooling1D, self).__init__(name=name, **kwargs) - self._can_use_graph_functions = True if data_format is None: data_format = backend.image_data_format() if strides is None: @@ -231,7 +230,6 @@ class Pooling2D(Layer): padding='valid', data_format=None, name=None, **kwargs): super(Pooling2D, self).__init__(name=name, **kwargs) - self._can_use_graph_functions = True if data_format is None: data_format = backend.image_data_format() if strides is None: @@ -427,7 +425,6 @@ class Pooling3D(Layer): padding='valid', data_format='channels_last', name=None, **kwargs): super(Pooling3D, self).__init__(name=name, **kwargs) - self._can_use_graph_functions = True if data_format is None: data_format = backend.image_data_format() if strides is None: @@ -599,7 +596,6 @@ class GlobalPooling1D(Layer): def __init__(self, data_format='channels_last', **kwargs): super(GlobalPooling1D, self).__init__(**kwargs) - self._can_use_graph_functions = True self.input_spec = InputSpec(ndim=3) self.data_format = conv_utils.normalize_data_format(data_format) @@ -705,7 +701,6 @@ class GlobalPooling2D(Layer): def __init__(self, data_format=None, **kwargs): super(GlobalPooling2D, self).__init__(**kwargs) - self._can_use_graph_functions = True self.data_format = conv_utils.normalize_data_format(data_format) self.input_spec = InputSpec(ndim=4) @@ -804,7 +799,6 @@ class GlobalPooling3D(Layer): def __init__(self, data_format=None, **kwargs): super(GlobalPooling3D, self).__init__(**kwargs) - self._can_use_graph_functions = True self.data_format = conv_utils.normalize_data_format(data_format) self.input_spec = InputSpec(ndim=5) diff --git a/tensorflow/python/keras/layers/recurrent.py b/tensorflow/python/keras/layers/recurrent.py index 979187c719..d9502dfc5b 100644 --- a/tensorflow/python/keras/layers/recurrent.py +++ b/tensorflow/python/keras/layers/recurrent.py @@ -19,20 +19,27 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import uuid + import numpy as np from tensorflow.python.eager import context +from tensorflow.python.eager import function +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape from tensorflow.python.keras import activations from tensorflow.python.keras import backend as K from tensorflow.python.keras import constraints from tensorflow.python.keras import initializers from tensorflow.python.keras import regularizers -from tensorflow.python.keras.engine.base_layer import InputSpec from tensorflow.python.keras.engine.base_layer import Layer +from tensorflow.python.keras.engine.input_spec import InputSpec from tensorflow.python.keras.utils import generic_utils from tensorflow.python.keras.utils import tf_utils from tensorflow.python.ops import array_ops +from tensorflow.python.ops import gen_cudnn_rnn_ops from tensorflow.python.ops import state_ops from tensorflow.python.platform import tf_logging as logging from tensorflow.python.training.checkpointable import base as checkpointable @@ -446,6 +453,9 @@ class RNN(Layer): 'an attribute `state_size` ' '(tuple of integers, ' 'one integer per RNN state).') + # If True, the output for masked timestep will be zeros, whereas in the + # False case, output from previous timestep is returned for masked timestep. + self.zero_output_for_mask = kwargs.pop('zero_output_for_mask', False) super(RNN, self).__init__(**kwargs) self.cell = cell if isinstance(cell, checkpointable.CheckpointableBase): @@ -743,35 +753,12 @@ class RNN(Layer): training=None, initial_state=None, constants=None): - # input shape: `(samples, time (padded with zeros), input_dim)` - # note that the .build() method of subclasses MUST define - # self.input_spec and self.state_spec with complete input shapes. - if isinstance(inputs, list): - # get initial_state from full input spec - # as they could be copied to multiple GPU. - if self._num_constants is None: - initial_state = inputs[1:] - else: - initial_state = inputs[1:-self._num_constants] - constants = inputs[-self._num_constants:] - if len(initial_state) == 0: - initial_state = None - inputs = inputs[0] - if initial_state is not None: - pass - elif self.stateful: - initial_state = self.states - else: - initial_state = self.get_initial_state(inputs) + inputs, initial_state, constants = self._process_inputs( + inputs, initial_state, constants) if isinstance(mask, list): mask = mask[0] - if len(initial_state) != len(self.states): - raise ValueError( - 'Layer has ' + str(len(self.states)) + ' states but was passed ' + - str(len(initial_state)) + ' initial states.') - if nest.is_sequence(inputs): # In the case of nested input, use the first element for shape check. input_shape = K.int_shape(nest.flatten(inputs)[0]) @@ -829,7 +816,8 @@ class RNN(Layer): mask=mask, unroll=self.unroll, input_length=timesteps, - time_major=self.time_major) + time_major=self.time_major, + zero_output_for_mask=self.zero_output_for_mask) if self.stateful: updates = [] for i in range(len(states)): @@ -850,6 +838,34 @@ class RNN(Layer): else: return output + def _process_inputs(self, inputs, initial_state, constants): + # input shape: `(samples, time (padded with zeros), input_dim)` + # note that the .build() method of subclasses MUST define + # self.input_spec and self.state_spec with complete input shapes. + if isinstance(inputs, list): + # get initial_state from full input spec + # as they could be copied to multiple GPU. + if self._num_constants is None: + initial_state = inputs[1:] + else: + initial_state = inputs[1:-self._num_constants] + constants = inputs[-self._num_constants:] + if len(initial_state) == 0: + initial_state = None + inputs = inputs[0] + if initial_state is not None: + pass + elif self.stateful: + initial_state = self.states + else: + initial_state = self.get_initial_state(inputs) + + if len(initial_state) != len(self.states): + raise ValueError('Layer has ' + str(len(self.states)) + + ' states but was passed ' + str(len(initial_state)) + + ' initial states.') + return inputs, initial_state, constants + def reset_states(self, states=None): if not self.stateful: raise AttributeError('Layer must be stateful.') @@ -923,6 +939,8 @@ class RNN(Layer): } if self._num_constants is not None: config['num_constants'] = self._num_constants + if self.zero_output_for_mask: + config['zero_output_for_mask'] = self.zero_output_for_mask cell_config = self.cell.get_config() config['cell'] = { @@ -2515,6 +2533,416 @@ class LSTM(RNN): return cls(**config) +class UnifiedLSTM(LSTM): + """Long Short-Term Memory layer - Hochreiter 1997. + + `UnifiedLSTM` unifies the implementations between standard `LSTM` layer and + `CuDNNLSTM` layer. Based on available runtime hardware and constrains, + `UnifiedLSTM` will choose different implementations to maximize the + performance. For instance, if GPU is available and all the parameters meet the + requirement of CuDNN kernel, `UnifiedLSTM` will use CuDNN kernel for the + calculation. + + Arguments: + units: Positive integer, dimensionality of the output space. + activation: Activation function to use. + Default: hyperbolic tangent (`tanh`). If you pass `None`, no activation + is applied + (ie. "linear" activation: `a(x) = x`). + recurrent_activation: Activation function to use for the recurrent step. + Default: hard sigmoid (`hard_sigmoid`). If you pass `None`, no + activation is applied + (ie. "linear" activation: `a(x) = x`). + use_bias: Boolean, whether the layer uses a bias vector. + kernel_initializer: Initializer for the `kernel` weights matrix, used for + the linear transformation of the inputs.. + recurrent_initializer: Initializer for the `recurrent_kernel` weights + matrix, used for the linear transformation of the recurrent state.. + bias_initializer: Initializer for the bias vector. + unit_forget_bias: Boolean. If True, add 1 to the bias of the forget gate at + initialization. Setting it to true will also force + `bias_initializer="zeros"`. This is recommended in [Jozefowicz et + al.](http://www.jmlr.org/proceedings/papers/v37/jozefowicz15.pdf) + kernel_regularizer: Regularizer function applied to the `kernel` weights + matrix. + recurrent_regularizer: Regularizer function applied to the + `recurrent_kernel` weights matrix. + bias_regularizer: Regularizer function applied to the bias vector. + activity_regularizer: Regularizer function applied to the output of the + layer (its "activation").. + kernel_constraint: Constraint function applied to the `kernel` weights + matrix. + recurrent_constraint: Constraint function applied to the `recurrent_kernel` + weights matrix. + bias_constraint: Constraint function applied to the bias vector. + dropout: Float between 0 and 1. Fraction of the units to drop for the linear + transformation of the inputs. + recurrent_dropout: Float between 0 and 1. Fraction of the units to drop for + the linear transformation of the recurrent state. + implementation: Implementation mode, either 1 or 2. Mode 1 will structure + its operations as a larger number of smaller dot products and additions, + whereas mode 2 will batch them into fewer, larger operations. These modes + will have different performance profiles on different hardware and for + different applications. + return_sequences: Boolean. Whether to return the last output. in the output + sequence, or the full sequence. + return_state: Boolean. Whether to return the last state in addition to the + output. + go_backwards: Boolean (default False). If True, process the input sequence + backwards and return the reversed sequence. + stateful: Boolean (default False). If True, the last state for each sample + at index i in a batch will be used as initial state for the sample of + index i in the following batch. + unroll: Boolean (default False). If True, the network will be unrolled, else + a symbolic loop will be used. Unrolling can speed-up a RNN, although it + tends to be more memory-intensive. Unrolling is only suitable for short + sequences. + """ + + def __init__(self, + units, + activation='tanh', + recurrent_activation='hard_sigmoid', + use_bias=True, + kernel_initializer='glorot_uniform', + recurrent_initializer='orthogonal', + bias_initializer='zeros', + unit_forget_bias=True, + kernel_regularizer=None, + recurrent_regularizer=None, + bias_regularizer=None, + activity_regularizer=None, + kernel_constraint=None, + recurrent_constraint=None, + bias_constraint=None, + dropout=0., + recurrent_dropout=0., + implementation=1, + return_sequences=False, + return_state=False, + go_backwards=False, + stateful=False, + time_major=False, + unroll=False, + **kwargs): + # return_runtime is a flag for testing, which shows the real backend + # implementation chosen by grappler in graph mode. + self.return_runtime = kwargs.pop('return_runtime', False) + + super(UnifiedLSTM, self).__init__( + units, + activation=activation, + recurrent_activation=recurrent_activation, + use_bias=use_bias, + kernel_initializer=kernel_initializer, + recurrent_initializer=recurrent_initializer, + bias_initializer=bias_initializer, + unit_forget_bias=unit_forget_bias, + kernel_regularizer=kernel_regularizer, + recurrent_regularizer=recurrent_regularizer, + bias_regularizer=bias_regularizer, + activity_regularizer=activity_regularizer, + kernel_constraint=kernel_constraint, + recurrent_constraint=recurrent_constraint, + bias_constraint=bias_constraint, + dropout=dropout, + recurrent_dropout=recurrent_dropout, + implementation=implementation, + return_sequences=return_sequences, + return_state=return_state, + go_backwards=go_backwards, + stateful=stateful, + time_major=time_major, + unroll=unroll, + **kwargs) + + self.state_spec = [ + InputSpec(shape=(None, dim)) for dim in (self.units, self.units) + ] + self._num_constants = None + self._num_inputs = None + self.could_use_cudnn = ( + activation == 'tanh' and dropout == 0 and not unroll and use_bias and + unit_forget_bias) + + def build(self, input_shape): + super(UnifiedLSTM, self).build(input_shape) + if self.could_use_cudnn: + # Add a new set of bias for CuDNN implementation only. Standard LSTM only + # has bias for recurrent kernel, while CuDNN LSTM has an extra set for + # input gate as well. + self.cudnn_bias = self.add_weight( + shape=(self.units * 4,), + name='cudnn_bias', + use_resource=True, + initializer=self.bias_initializer, + regularizer=self.bias_regularizer, + constraint=self.bias_constraint) + self.built = True + + def call(self, inputs, mask=None, training=None, initial_state=None): + # LSTM does not support constants. Ignore it during process. + inputs, initial_state, _ = self._process_inputs(inputs, initial_state, None) + + if isinstance(mask, list): + mask = mask[0] + + input_shape = K.int_shape(inputs) + timesteps = input_shape[0] if self.time_major else input_shape[1] + + if mask is not None or not self.could_use_cudnn: + # CuDNN does not support masking, fall back to use the normal LSTM. + kwargs = {'training': training} + + def step(inputs, states): + return self.cell.call(inputs, states, **kwargs) + + last_output, outputs, states = K.rnn( + step, + inputs, + initial_state, + constants=None, + go_backwards=self.go_backwards, + mask=mask, + unroll=self.unroll, + input_length=timesteps, + time_major=self.time_major, + zero_output_for_mask=self.zero_output_for_mask) + runtime = constant_op.constant( + 'unknown', dtype=dtypes.string, name='runtime') + else: + # Use the new defun approach for backend implementation swap. + # Note that different implementations need to have same function + # signature, eg, the tensor parameters need to have same shape and dtypes. + # Since the CuDNN has an extra set of bias, those bias will be passed to + # both normal and CuDNN implementations. + if self.go_backwards: + # Reverse time axis. + inputs = K.reverse(inputs, 1) + + combined_bias = array_ops.concat([self.cudnn_bias, self.cell.bias], 0) + + # Each time a defun function is called, we will give a unique identifiable + # API name, so that the grappler won't get confused when it sees multiple + # LSTM layer added into same graph, and it will be able to pair up the + # different implementations across them. + experimental_api_name = 'lstm_' + str(uuid.uuid4()) + standard_lstm_attributes = { + 'experimental_api_implements': experimental_api_name, + 'experimental_api_preferred_device': 'CPU', + } + cudnn_lstm_attributes = { + 'experimental_api_implements': experimental_api_name, + 'experimental_api_preferred_device': 'GPU', + } + defun_standard_lstm = function.defun_with_attributes( + standard_lstm, attributes=standard_lstm_attributes) + defun_cudnn_lstm = function.defun_with_attributes( + cudnn_lstm, attributes=cudnn_lstm_attributes) + + if ops.executing_eagerly_outside_functions(): + # Under eager context, the device placement is already known. Prefer the + # GPU implementation here. + if context.num_gpus() > 0: + last_output, outputs, new_h, new_c, runtime = defun_cudnn_lstm( + inputs, initial_state[0], initial_state[1], self.cell.kernel, + self.cell.recurrent_kernel, combined_bias, self.time_major) + else: + last_output, outputs, new_h, new_c, runtime = defun_standard_lstm( + inputs, initial_state[0], initial_state[1], self.cell.kernel, + self.cell.recurrent_kernel, combined_bias, self.activation, + self.recurrent_activation, self.time_major) + else: + # Call the normal LSTM impl and register the CuDNN impl function. The + # grappler will kick in during session execution to optimize the graph. + last_output, outputs, new_h, new_c, runtime = defun_standard_lstm( + inputs, initial_state[0], initial_state[1], self.cell.kernel, + self.cell.recurrent_kernel, combined_bias, self.activation, + self.recurrent_activation, self.time_major) + + function.register(defun_cudnn_lstm, inputs, initial_state[0], + initial_state[1], self.cell.kernel, + self.cell.recurrent_kernel, combined_bias, + self.time_major) + states = [new_h, new_c] + + if self.stateful: + updates = [] + for i in range(len(states)): + updates.append(state_ops.assign(self.states[i], states[i])) + self.add_update(updates, inputs) + + if self.return_sequences: + output = outputs + else: + output = last_output + + if self.return_state: + return [output] + states + elif self.return_runtime: + return output, runtime + else: + return output + + @property + def trainable_weights(self): + if self.trainable: + weights = [] + weights += self.cell.trainable_weights + if getattr(self, 'cudnn_bias', None) is not None: + weights += [self.cudnn_bias] + return weights + return [] + + @property + def non_trainable_weights(self): + if not self.trainable: + weights = [] + weights += self.cell.non_trainable_weights + if getattr(self, 'cudnn_bias', None) is not None: + weights += [self.cudnn_bias] + return weights + return [] + + @property + def losses(self): + losses = [] + losses += self.cell.losses + return losses + self._losses + + @property + def updates(self): + updates = [] + updates += self.cell.updates + return updates + self._updates + + def get_weights(self): + weights = [] + weights += self.cell.weights + if getattr(self, 'cudnn_bias', None) is not None: + weights += [self.cudnn_bias] + return K.batch_get_value(weights) + + def set_weights(self, weights): + tuples = [] + cell_weights = weights[:len(self.cell.weights)] + if cell_weights: + tuples.append((self.cell.weights, cell_weights)) + if getattr(self, 'cudnn_bias', None) is not None: + cudnn_bias_weights = weights[len(self.cell.weights):] + if cudnn_bias_weights: + tuples.append((self.cudnn_bias, cudnn_bias_weights)) + K.batch_set_value(tuples) + + +def _canonical_to_params(weights, biases, shape): + """Utility function convert variable to CuDNN compatible parameter.""" + weights = [array_ops.reshape(x, shape) for x in weights] + biases = [array_ops.reshape(x, shape) for x in biases] + return array_ops.concat(weights + biases, axis=0) + + +def standard_lstm(inputs, init_h, init_c, kernel, recurrent_kernel, bias, + activation, recurrent_activation, time_major): + """LSTM with standard kernel implementation. + + This implementation can be run on all types for hardware. + + This implementation lifts out all the layer weights and make them function + parameters. It has same number of tensor input params as the CuDNN + counterpart. The RNN step logic has been simplified, eg dropout and mask is + removed since CuDNN implementation does not support that. + + Note that the first half of the bias tensor should be ignored by this impl. + The CuDNN impl need an extra set of input gate bias. In order to make the both + function take same shape of parameter, that extra set of bias is also feed + here. + + Args: + inputs: input tensor of LSTM layer. + init_h: initial state tensor for the cell output. + init_c: initial state tensor for the cell hidden state. + kernel: weights for cell kernel. + recurrent_kernel: weights for cell recurrent kernel. + bias: weights for cell kernel bias and recurrent bias. Only recurrent bias + is used in this case. + activation: Activation function to use for output. + recurrent_activation: Activation function to use for hidden recurrent state. + time_major: boolean, whether the inputs are in the format of + [time, batch, feature] or [batch, time, feature]. + + Returns: + last_output: output tensor for the last timestep, which has shape + [batch, units]. + outputs: output tensor for all timesteps, which has shape + [batch, time, units]. + state_0: the cell output, which has same shape as init_h. + state_1: the cell hidden state, which has same shape as init_c. + runtime: constant string tensor which indicate real runtime hardware. This + value is for testing purpose and should be used by user. + """ + input_shape = K.int_shape(inputs) + timesteps = input_shape[0] if time_major else input_shape[1] + + # Only use the second half of the bias weights. + _, real_bias = array_ops.split(bias, 2) + + def step(cell_inputs, cell_states): + """Step function that will be used by Keras RNN backend.""" + h_tm1 = cell_states[0] # previous memory state + c_tm1 = cell_states[1] # previous carry state + + z = K.dot(cell_inputs, kernel) + z += K.dot(h_tm1, recurrent_kernel) + z = K.bias_add(z, real_bias) + + z0, z1, z2, z3 = array_ops.split(z, 4, axis=1) + + i = recurrent_activation(z0) + f = recurrent_activation(z1) + c = f * c_tm1 + i * activation(z2) + o = recurrent_activation(z3) + + h = o * activation(c) + return h, [h, c] + + last_output, outputs, new_states = K.rnn( + step, + inputs, [init_h, init_c], + constants=None, + unroll=False, + time_major=time_major, + input_length=timesteps) + return last_output, outputs, new_states[0], new_states[ + 1], constant_op.constant('cpu', dtype=dtypes.string, name='runtime') + + +def cudnn_lstm(inputs, input_h, input_c, kernel, recurrent_kernel, bias, + time_major): + """LSTM with CuDNN implementation which is only available for GPU.""" + if not time_major: + inputs = array_ops.transpose(inputs, perm=(1, 0, 2)) + input_h = array_ops.expand_dims(input_h, axis=0) + input_c = array_ops.expand_dims(input_c, axis=0) + + weights = array_ops.split(kernel, 4, axis=1) + weights += array_ops.split(recurrent_kernel, 4, axis=1) + params = _canonical_to_params( + weights=weights, + biases=array_ops.split(bias, 8), + shape=constant_op.constant([-1])) + + outputs, h, c, _ = gen_cudnn_rnn_ops.cudnn_rnn( + inputs, input_h=input_h, input_c=input_c, params=params) + if not time_major: + outputs = array_ops.transpose(outputs, perm=[1, 0, 2]) + h = h[0] + c = c[0] + last_output = outputs[:, -1, :] + return last_output, outputs, h, c, constant_op.constant( + 'cudnn', dtype=dtypes.string, name='runtime') + + def _generate_dropout_mask(ones, rate, training=None, count=1): def dropped_inputs(): return K.dropout(ones, rate) diff --git a/tensorflow/python/keras/layers/recurrent_test.py b/tensorflow/python/keras/layers/recurrent_test.py index bb14a7a505..b1449069e3 100644 --- a/tensorflow/python/keras/layers/recurrent_test.py +++ b/tensorflow/python/keras/layers/recurrent_test.py @@ -1013,8 +1013,8 @@ class RNNTest(test.TestCase): inputs, _ = cell(inputs, initial_state) output = inputs if not context.executing_eagerly(): - sess.run(variables_lib.global_variables_initializer()) - output = sess.run(output) + self.evaluate(variables_lib.global_variables_initializer()) + output = self.evaluate(output) return output random_seed.set_random_seed(12345) @@ -1079,6 +1079,32 @@ class RNNTest(test.TestCase): # Expect last output to be the same as last output before masking self.assertAllClose(y_np, x_np[:, 1, :]) + def test_zero_output_for_masking(self): + + for unroll in [True, False]: + cell = keras.layers.SimpleRNNCell(5) + x = keras.Input((5, 5)) + mask = keras.layers.Masking() + layer = keras.layers.RNN( + cell, return_sequences=True, zero_output_for_mask=True, unroll=unroll) + masked_input = mask(x) + y = layer(masked_input) + model = keras.models.Model(x, y) + model.compile(optimizer=rmsprop.RMSPropOptimizer(learning_rate=0.001), + loss='mse') + + np_x = np.ones((6, 5, 5)) + result_1 = model.predict(np_x) + + # set the time 4 and 5 for last record to be zero (masked). + np_x[5, 3:] = 0 + result_2 = model.predict(np_x) + + # expect the result_2 has same output, except the time 4,5 for last + # record. + result_1[5, 3:] = 0 + self.assertAllClose(result_1, result_2) + class Minimal2DRNNCell(keras.layers.Layer): """The minimal 2D RNN cell is a simple combination of 2 1-D RNN cell. diff --git a/tensorflow/python/keras/layers/simplernn_test.py b/tensorflow/python/keras/layers/simplernn_test.py index 93456b5e3a..b49b159b71 100644 --- a/tensorflow/python/keras/layers/simplernn_test.py +++ b/tensorflow/python/keras/layers/simplernn_test.py @@ -98,6 +98,7 @@ class SimpleRNNLayerTest(test.TestCase): self.assertEqual(layer.cell.recurrent_kernel.constraint, r_constraint) self.assertEqual(layer.cell.bias.constraint, b_constraint) + @tf_test_util.run_deprecated_v1 def test_with_masking_layer_SimpleRNN(self): layer_class = keras.layers.SimpleRNN inputs = np.random.random((2, 3, 4)) @@ -120,6 +121,7 @@ class SimpleRNNLayerTest(test.TestCase): class SimpleRNNLayerGraphOnlyTest(test.TestCase): + @tf_test_util.run_deprecated_v1 def test_statefulness_SimpleRNN(self): num_samples = 2 timesteps = 3 @@ -183,6 +185,7 @@ class SimpleRNNLayerGraphOnlyTest(test.TestCase): np.testing.assert_allclose(out7, out6, atol=1e-5) + @tf_test_util.run_deprecated_v1 def test_regularizers_SimpleRNN(self): embedding_dim = 4 layer_class = keras.layers.SimpleRNN diff --git a/tensorflow/python/keras/layers/unified_lstm_test.py b/tensorflow/python/keras/layers/unified_lstm_test.py new file mode 100644 index 0000000000..d229d14312 --- /dev/null +++ b/tensorflow/python/keras/layers/unified_lstm_test.py @@ -0,0 +1,724 @@ +# Copyright 2018 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 UnifiedLSTM layer.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import time + +from absl.testing import parameterized +import numpy as np + +from tensorflow.core.protobuf import config_pb2 +from tensorflow.core.protobuf import rewriter_config_pb2 +from tensorflow.python import keras +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util +from tensorflow.python.keras import testing_utils +from tensorflow.python.keras.layers.cudnn_recurrent import CuDNNLSTM +from tensorflow.python.keras.layers.recurrent import UnifiedLSTM +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import control_flow_ops +from tensorflow.python.ops import gen_math_ops +from tensorflow.python.ops import variables +from tensorflow.python.ops.losses import losses +from tensorflow.python.platform import test +from tensorflow.python.platform import tf_logging as logging +from tensorflow.python.training import gradient_descent + + +# Global config for grappler setting that is used for graph mode test. +_rewrites = rewriter_config_pb2.RewriterConfig() +_rewrites.function_optimization = rewriter_config_pb2.RewriterConfig.OFF +_customer_optimizer = _rewrites.custom_optimizers.add() +_customer_optimizer.name = 'ExperimentalImplementationSelector' +_rewrites.min_graph_nodes = -1 +_graph_options = config_pb2.GraphOptions(rewrite_options=_rewrites) +_config = config_pb2.ConfigProto(graph_options=_graph_options) + + +class UnifiedLSTMTest(test.TestCase, parameterized.TestCase): + + @test_util.run_deprecated_v1 + def test_unifiedLSTM(self): + input_shape = 10 + rnn_state_size = 8 + output_shape = 8 + timestep = 4 + batch = 100 + epoch = 1 + + with self.cached_session(config=_config, use_gpu=True) as sess: + (x_train, y_train), _ = testing_utils.get_test_data( + train_samples=batch, + test_samples=0, + input_shape=(timestep, input_shape), + num_classes=output_shape) + y_train = keras.utils.to_categorical(y_train, output_shape) + + layer = UnifiedLSTM(rnn_state_size, return_runtime=True) + + inputs = array_ops.placeholder( + dtypes.float32, shape=(None, timestep, input_shape), name='inputs') + predict = array_ops.placeholder( + dtypes.float32, shape=(None, output_shape), name='predict') + + outputs, runtime = layer(inputs) + loss = losses.softmax_cross_entropy(predict, outputs) + optimizer = gradient_descent.GradientDescentOptimizer(0.001) + train_op = optimizer.minimize(loss) + + sess.run([variables.global_variables_initializer()]) + existing_loss = 0 + for _ in range(epoch): + loss_value, _, runtime_value = sess.run([loss, train_op, runtime], { + inputs: x_train, + predict: y_train + }) + if test.is_gpu_available(): + self.assertEqual(runtime_value, b'cudnn') + else: + self.assertEqual(runtime_value, b'cpu') + # Make sure the loss is updated for every epoch + # (layer weights properly updated). + self.assertNotEqual(existing_loss, loss_value) + existing_loss = loss_value + + @test_util.run_deprecated_v1 + def test_unifiedLSTM_with_cond(self): + # This test is to demonstrate the graph rewrite of grappler plugin under + # the condition that the function returns different number of internal + # states. + input_shape = 10 + rnn_state_size = 8 + output_shape = 8 + timestep = 4 + batch = 100 + epoch = 1 + + with self.cached_session(config=_config, use_gpu=True) as sess: + (x_train, y_train), _ = testing_utils.get_test_data( + train_samples=batch, + test_samples=0, + input_shape=(timestep, input_shape), + num_classes=output_shape) + y_train = keras.utils.to_categorical(y_train, output_shape) + + layer = UnifiedLSTM(rnn_state_size, return_runtime=True) + + inputs = array_ops.placeholder( + dtypes.float32, shape=(None, timestep, input_shape), name='inputs') + predict = array_ops.placeholder( + dtypes.float32, shape=(None, output_shape), name='predict') + + zeros = array_ops.zeros([batch, output_shape]) + dummy_runtime = constant_op.constant( + 'unknown', dtype=dtypes.string, name='runtime') + a = constant_op.constant(0) + b = constant_op.constant(1) + # Will always run the lstm layer. + outputs, runtime = control_flow_ops.cond( + gen_math_ops.less(a, b), + lambda: layer(inputs), + lambda: (zeros, dummy_runtime)) + loss = losses.softmax_cross_entropy(predict, outputs) + optimizer = gradient_descent.GradientDescentOptimizer(0.001) + train_op = optimizer.minimize(loss) + + sess.run([variables.global_variables_initializer()]) + existing_loss = 0 + + for _ in range(epoch): + loss_value, _, runtime_value = sess.run([loss, train_op, runtime], { + inputs: x_train, + predict: y_train + }) + if test.is_gpu_available(): + self.assertEqual(runtime_value, b'cudnn') + else: + self.assertEqual(runtime_value, b'cpu') + # Make sure the loss is updated for every epoch + # (layer weights properly updated). + self.assertNotEqual(existing_loss, loss_value) + existing_loss = loss_value + + @test_util.run_in_graph_and_eager_modes(config=_config) + def test_keras_model_with_lstm(self): + input_shape = 10 + rnn_state_size = 8 + output_shape = 8 + timestep = 4 + batch = 100 + epoch = 10 + + (x_train, y_train), _ = testing_utils.get_test_data( + train_samples=batch, + test_samples=0, + input_shape=(timestep, input_shape), + num_classes=output_shape) + y_train = keras.utils.to_categorical(y_train, output_shape) + + layer = UnifiedLSTM(rnn_state_size) + + inputs = keras.layers.Input( + shape=[timestep, input_shape], dtype=dtypes.float32) + + outputs = layer(inputs) + model = keras.models.Model(inputs, outputs) + model.compile('rmsprop', loss='mse') + model.fit(x_train, y_train, epochs=epoch) + model.evaluate(x_train, y_train) + + @test_util.run_in_graph_and_eager_modes(config=_config) + def test_return_sequences_LSTM(self): + num_samples = 2 + timesteps = 3 + embedding_dim = 4 + units = 2 + testing_utils.layer_test( + UnifiedLSTM, + kwargs={ + 'units': units, + 'return_sequences': True + }, + input_shape=(num_samples, timesteps, embedding_dim)) + + @test_util.run_in_graph_and_eager_modes(config=_config) + def test_static_shape_inference_LSTM(self): + # Github issue: 15165 + timesteps = 3 + embedding_dim = 4 + units = 2 + + model = keras.models.Sequential() + inputs = keras.layers.Dense( + embedding_dim, input_shape=(timesteps, embedding_dim)) + model.add(inputs) + layer = UnifiedLSTM(units, return_sequences=True) + model.add(layer) + outputs = model.layers[-1].output + self.assertEqual(outputs.get_shape().as_list(), [None, timesteps, units]) + + @test_util.run_in_graph_and_eager_modes(config=_config) + def test_dynamic_behavior_LSTM(self): + num_samples = 2 + timesteps = 3 + embedding_dim = 4 + units = 2 + layer = UnifiedLSTM(units, input_shape=(None, embedding_dim)) + model = keras.models.Sequential() + model.add(layer) + model.compile(gradient_descent.GradientDescentOptimizer(0.001), 'mse') + x = np.random.random((num_samples, timesteps, embedding_dim)) + y = np.random.random((num_samples, units)) + model.train_on_batch(x, y) + + @test_util.run_in_graph_and_eager_modes(config=_config) + def test_dropout_LSTM(self): + num_samples = 2 + timesteps = 3 + embedding_dim = 4 + units = 2 + testing_utils.layer_test( + UnifiedLSTM, + kwargs={ + 'units': units, + 'dropout': 0.1, + 'recurrent_dropout': 0.1 + }, + input_shape=(num_samples, timesteps, embedding_dim)) + + @parameterized.parameters([0, 1, 2]) + @test_util.run_in_graph_and_eager_modes(config=_config) + def test_implementation_mode_LSTM(self, implementation_mode): + num_samples = 2 + timesteps = 3 + embedding_dim = 4 + units = 2 + testing_utils.layer_test( + UnifiedLSTM, + kwargs={ + 'units': units, + 'implementation': implementation_mode + }, + input_shape=(num_samples, timesteps, embedding_dim)) + + @test_util.run_in_graph_and_eager_modes(config=_config) + def test_constraints_LSTM(self): + embedding_dim = 4 + layer_class = UnifiedLSTM + k_constraint = keras.constraints.max_norm(0.01) + r_constraint = keras.constraints.max_norm(0.01) + b_constraint = keras.constraints.max_norm(0.01) + layer = layer_class( + 5, + return_sequences=False, + weights=None, + input_shape=(None, embedding_dim), + kernel_constraint=k_constraint, + recurrent_constraint=r_constraint, + bias_constraint=b_constraint) + layer.build((None, None, embedding_dim)) + self.assertEqual(layer.cell.kernel.constraint, k_constraint) + self.assertEqual(layer.cell.recurrent_kernel.constraint, r_constraint) + self.assertEqual(layer.cell.bias.constraint, b_constraint) + + @test_util.run_in_graph_and_eager_modes(config=_config) + def test_with_masking_layer_LSTM(self): + layer_class = UnifiedLSTM + inputs = np.random.random((2, 3, 4)) + targets = np.abs(np.random.random((2, 3, 5))) + targets /= targets.sum(axis=-1, keepdims=True) + model = keras.models.Sequential() + model.add(keras.layers.Masking(input_shape=(3, 4))) + model.add(layer_class(units=5, return_sequences=True, unroll=False)) + model.compile( + loss='categorical_crossentropy', + optimizer=gradient_descent.GradientDescentOptimizer(0.01)) + model.fit(inputs, targets, epochs=1, batch_size=2, verbose=1) + + @test_util.run_in_graph_and_eager_modes(config=_config) + def test_stacking_LSTM(self): + inputs = np.random.random((2, 3, 4)) + targets = np.abs(np.random.random((2, 3, 5))) + targets /= targets.sum(axis=-1, keepdims=True) + model = keras.models.Sequential() + model.add(UnifiedLSTM(10, return_sequences=True, unroll=False)) + model.add(UnifiedLSTM(5, return_sequences=True, unroll=False)) + model.compile( + loss='categorical_crossentropy', + optimizer=gradient_descent.GradientDescentOptimizer(0.01)) + model.fit(inputs, targets, epochs=1, batch_size=2, verbose=1) + + @test_util.run_in_graph_and_eager_modes(config=_config) + def test_masking_with_stacking_LSTM(self): + inputs = np.random.random((2, 3, 4)) + targets = np.abs(np.random.random((2, 3, 5))) + targets /= targets.sum(axis=-1, keepdims=True) + model = keras.models.Sequential() + model.add(keras.layers.Masking(input_shape=(3, 4))) + model.add(UnifiedLSTM(10, return_sequences=True, unroll=False)) + model.add(UnifiedLSTM(5, return_sequences=True, unroll=False)) + model.compile( + loss='categorical_crossentropy', + optimizer=gradient_descent.GradientDescentOptimizer(0.01)) + model.fit(inputs, targets, epochs=1, batch_size=2, verbose=1) + + @test_util.run_in_graph_and_eager_modes(config=_config) + def test_from_config_LSTM(self): + layer_class = UnifiedLSTM + for stateful in (False, True): + l1 = layer_class(units=1, stateful=stateful) + l2 = layer_class.from_config(l1.get_config()) + assert l1.get_config() == l2.get_config() + + @test_util.run_in_graph_and_eager_modes(config=_config) + def test_specify_initial_state_keras_tensor(self): + num_states = 2 + timesteps = 3 + embedding_dim = 4 + units = 3 + num_samples = 2 + + # Test with Keras tensor + inputs = keras.Input((timesteps, embedding_dim)) + initial_state = [keras.Input((units,)) for _ in range(num_states)] + layer = UnifiedLSTM(units) + if len(initial_state) == 1: + output = layer(inputs, initial_state=initial_state[0]) + else: + output = layer(inputs, initial_state=initial_state) + assert initial_state[0] in layer._inbound_nodes[0].input_tensors + + model = keras.models.Model([inputs] + initial_state, output) + model.compile( + loss='categorical_crossentropy', + optimizer=gradient_descent.GradientDescentOptimizer(0.01)) + + inputs = np.random.random((num_samples, timesteps, embedding_dim)) + initial_state = [ + np.random.random((num_samples, units)) for _ in range(num_states) + ] + targets = np.random.random((num_samples, units)) + model.train_on_batch([inputs] + initial_state, targets) + + @test_util.run_in_graph_and_eager_modes(config=_config) + def DISABLED_test_specify_initial_state_non_keras_tensor(self): + num_states = 2 + timesteps = 3 + embedding_dim = 4 + units = 3 + num_samples = 2 + + # Test with non-Keras tensor + inputs = keras.Input((timesteps, embedding_dim)) + initial_state = [ + keras.backend.random_normal_variable((num_samples, units), 0, 1) + for _ in range(num_states) + ] + layer = UnifiedLSTM(units) + output = layer(inputs, initial_state=initial_state) + + model = keras.models.Model(inputs, output) + model.compile( + loss='categorical_crossentropy', + optimizer=gradient_descent.GradientDescentOptimizer(0.01)) + + inputs = np.random.random((num_samples, timesteps, embedding_dim)) + targets = np.random.random((num_samples, units)) + model.train_on_batch(inputs, targets) + + @test_util.run_in_graph_and_eager_modes(config=_config) + def test_reset_states_with_values(self): + num_states = 2 + timesteps = 3 + embedding_dim = 4 + units = 3 + num_samples = 2 + + layer = UnifiedLSTM(units, stateful=True) + layer.build((num_samples, timesteps, embedding_dim)) + layer.reset_states() + assert len(layer.states) == num_states + assert layer.states[0] is not None + self.assertAllClose( + keras.backend.eval(layer.states[0]), + np.zeros(keras.backend.int_shape(layer.states[0])), + atol=1e-4) + state_shapes = [keras.backend.int_shape(state) for state in layer.states] + values = [np.ones(shape) for shape in state_shapes] + if len(values) == 1: + values = values[0] + layer.reset_states(values) + self.assertAllClose( + keras.backend.eval(layer.states[0]), + np.ones(keras.backend.int_shape(layer.states[0])), + atol=1e-4) + + # Test with invalid data + with self.assertRaises(ValueError): + layer.reset_states([1] * (len(layer.states) + 1)) + + @test_util.run_in_graph_and_eager_modes(config=_config) + def test_specify_state_with_masking(self): + num_states = 2 + timesteps = 3 + embedding_dim = 4 + units = 3 + num_samples = 2 + + inputs = keras.Input((timesteps, embedding_dim)) + _ = keras.layers.Masking()(inputs) + initial_state = [keras.Input((units,)) for _ in range(num_states)] + output = UnifiedLSTM(units)(inputs, initial_state=initial_state) + + model = keras.models.Model([inputs] + initial_state, output) + model.compile( + loss='categorical_crossentropy', + optimizer=gradient_descent.GradientDescentOptimizer(0.01)) + + inputs = np.random.random((num_samples, timesteps, embedding_dim)) + initial_state = [ + np.random.random((num_samples, units)) for _ in range(num_states) + ] + targets = np.random.random((num_samples, units)) + model.train_on_batch([inputs] + initial_state, targets) + + @test_util.run_in_graph_and_eager_modes(config=_config) + def test_return_state(self): + num_states = 2 + timesteps = 3 + embedding_dim = 4 + units = 3 + num_samples = 2 + + inputs = keras.Input(batch_shape=(num_samples, timesteps, embedding_dim)) + layer = UnifiedLSTM(units, return_state=True, stateful=True) + outputs = layer(inputs) + state = outputs[1:] + assert len(state) == num_states + model = keras.models.Model(inputs, state[0]) + + inputs = np.random.random((num_samples, timesteps, embedding_dim)) + state = model.predict(inputs) + self.assertAllClose(keras.backend.eval(layer.states[0]), state, atol=1e-4) + + @test_util.run_in_graph_and_eager_modes(config=_config) + def test_state_reuse(self): + timesteps = 3 + embedding_dim = 4 + units = 3 + num_samples = 2 + + inputs = keras.Input(batch_shape=(num_samples, timesteps, embedding_dim)) + layer = UnifiedLSTM(units, return_state=True, return_sequences=True) + outputs = layer(inputs) + output, state = outputs[0], outputs[1:] + output = UnifiedLSTM(units)(output, initial_state=state) + model = keras.models.Model(inputs, output) + + inputs = np.random.random((num_samples, timesteps, embedding_dim)) + model.predict(inputs) + + @test_util.run_in_graph_and_eager_modes(config=_config) + def test_initial_states_as_other_inputs(self): + timesteps = 3 + embedding_dim = 4 + units = 3 + num_samples = 2 + num_states = 2 + layer_class = UnifiedLSTM + + # Test with Keras tensor + main_inputs = keras.Input((timesteps, embedding_dim)) + initial_state = [keras.Input((units,)) for _ in range(num_states)] + inputs = [main_inputs] + initial_state + + layer = layer_class(units) + output = layer(inputs) + assert initial_state[0] in layer._inbound_nodes[0].input_tensors + + model = keras.models.Model(inputs, output) + model.compile( + loss='categorical_crossentropy', + optimizer=gradient_descent.GradientDescentOptimizer(0.01)) + + main_inputs = np.random.random((num_samples, timesteps, embedding_dim)) + initial_state = [ + np.random.random((num_samples, units)) for _ in range(num_states) + ] + targets = np.random.random((num_samples, units)) + model.train_on_batch([main_inputs] + initial_state, targets) + + +class LSTMLayerGraphOnlyTest(test.TestCase): + + def test_statefulness_LSTM(self): + num_samples = 2 + timesteps = 3 + embedding_dim = 4 + units = 2 + layer_class = UnifiedLSTM + with self.cached_session(config=_config): + model = keras.models.Sequential() + model.add( + keras.layers.Embedding( + 4, + embedding_dim, + mask_zero=True, + input_length=timesteps, + batch_input_shape=(num_samples, timesteps))) + layer = layer_class( + units, return_sequences=False, stateful=True, weights=None) + model.add(layer) + model.compile( + optimizer=gradient_descent.GradientDescentOptimizer(0.01), loss='mse') + out1 = model.predict(np.ones((num_samples, timesteps))) + self.assertEqual(out1.shape, (num_samples, units)) + + # train once so that the states change + model.train_on_batch( + np.ones((num_samples, timesteps)), np.ones((num_samples, units))) + out2 = model.predict(np.ones((num_samples, timesteps))) + + # if the state is not reset, output should be different + self.assertNotEqual(out1.max(), out2.max()) + + # check that output changes after states are reset + # (even though the model itself didn't change) + layer.reset_states() + out3 = model.predict(np.ones((num_samples, timesteps))) + self.assertNotEqual(out2.max(), out3.max()) + + # check that container-level reset_states() works + model.reset_states() + out4 = model.predict(np.ones((num_samples, timesteps))) + self.assertAllClose(out3, out4, atol=1e-5) + + # check that the call to `predict` updated the states + out5 = model.predict(np.ones((num_samples, timesteps))) + self.assertNotEqual(out4.max(), out5.max()) + + # Check masking + layer.reset_states() + + left_padded_input = np.ones((num_samples, timesteps)) + left_padded_input[0, :1] = 0 + left_padded_input[1, :2] = 0 + out6 = model.predict(left_padded_input) + + layer.reset_states() + + right_padded_input = np.ones((num_samples, timesteps)) + right_padded_input[0, -1:] = 0 + right_padded_input[1, -2:] = 0 + out7 = model.predict(right_padded_input) + + self.assertAllClose(out7, out6, atol=1e-5) + + def test_regularizers_LSTM(self): + embedding_dim = 4 + layer_class = UnifiedLSTM + with self.cached_session(config=_config): + layer = layer_class( + 5, + return_sequences=False, + weights=None, + input_shape=(None, embedding_dim), + kernel_regularizer=keras.regularizers.l1(0.01), + recurrent_regularizer=keras.regularizers.l1(0.01), + bias_regularizer='l2', + activity_regularizer='l1') + layer.build((None, None, 2)) + self.assertEqual(len(layer.losses), 3) + x = keras.backend.variable(np.ones((2, 3, 2))) + layer(x) + self.assertEqual(len(layer.get_losses_for(x)), 1) + + +class UnifiedLSTMPerformanceTest(test.TestCase): + + def _measure_performance(self, test_config, model, x_train, y_train): + batch = test_config['batch'] + epoch = test_config['epoch'] + warmup_epoch = test_config['warmup_epoch'] + + # warm up the model + model.fit(x_train, y_train, batch_size=batch, epochs=warmup_epoch) + start_time = time.time() + model.fit(x_train, y_train, batch_size=batch, epochs=epoch - warmup_epoch) + end_time = time.time() + return (end_time - start_time) / (epoch - warmup_epoch) + + def _time_performance_run_cudnn_lstm(self, test_config, x_train, y_train): + # Get the performance number for standard Cudnn LSTM + input_shape = test_config['input_shape'] + rnn_state_size = test_config['rnn_state_size'] + timestep = test_config['timestep'] + + cudnn_lstm_layer = CuDNNLSTM(rnn_state_size) + inputs = keras.layers.Input( + shape=[timestep, input_shape], dtype=dtypes.float32) + + outputs = cudnn_lstm_layer(inputs) + model = keras.models.Model(inputs, outputs) + model.compile('sgd', 'mse') + + sec_per_epoch = self._measure_performance( + test_config, model, x_train, y_train) + logging.info('Average performance for %s per epoch is: %s', + 'CuDNN LSTM', sec_per_epoch) + return sec_per_epoch + + def _time_performance_run_unifed_lstm_gpu( + self, test_config, x_train, y_train): + # Get performance number for Unified_LSTM with grappler swap the impl + input_shape = test_config['input_shape'] + rnn_state_size = test_config['rnn_state_size'] + timestep = test_config['timestep'] + + layer = UnifiedLSTM(rnn_state_size) + inputs = keras.layers.Input( + shape=[timestep, input_shape], dtype=dtypes.float32) + + outputs = layer(inputs) + model = keras.models.Model(inputs, outputs) + model.compile('sgd', 'mse') + + sec_per_epoch = self._measure_performance( + test_config, model, x_train, y_train) + logging.info('Average performance for %s per epoch is: %s', + 'Unified LSTM', sec_per_epoch) + return sec_per_epoch + + def _time_performance_run_normal_lstm( + self, test_config, x_train, y_train): + # Get performance number for standard LSTM on GPU. + input_shape = test_config['input_shape'] + rnn_state_size = test_config['rnn_state_size'] + timestep = test_config['timestep'] + + layer = keras.layers.LSTM(rnn_state_size) + inputs = keras.layers.Input( + shape=[timestep, input_shape], dtype=dtypes.float32) + + outputs = layer(inputs) + model = keras.models.Model(inputs, outputs) + model.compile('sgd', 'mse') + + sec_per_epoch = self._measure_performance( + test_config, model, x_train, y_train) + logging.info('Average performance for %s per epoch is: %s', + 'Normal LSTM', sec_per_epoch) + return sec_per_epoch + + @test_util.run_in_graph_and_eager_modes(config=_config, use_gpu=True) + def test_performance_with_standard_cudnn_impl(self): + if not test.is_gpu_available(): + self.skipTest('performance test will only run on GPU') + + batch = 64 + num_batch = 10 + test_config = { + 'input_shape': 128, + 'rnn_state_size': 64, + 'output_shape': 64, + 'timestep': 50, + 'batch': batch, + 'epoch': 20, + # The performance for warmup epoch is ignored. + 'warmup_epoch': 1, + } + (x_train, y_train), _ = testing_utils.get_test_data( + train_samples=(batch * num_batch), + test_samples=0, + input_shape=(test_config['timestep'], test_config['input_shape']), + num_classes=test_config['output_shape']) + y_train = keras.utils.to_categorical(y_train, test_config['output_shape']) + + cudnn_duration = self._time_performance_run_cudnn_lstm( + test_config, x_train, y_train) + unified_lstm_gpu_duration = self._time_performance_run_unifed_lstm_gpu( + test_config, x_train, y_train) + normal_lstm_duration = self._time_performance_run_normal_lstm( + test_config, x_train, y_train) + + cudnn_vs_unified = cudnn_duration / unified_lstm_gpu_duration + unified_vs_normal = normal_lstm_duration / unified_lstm_gpu_duration + + # TODO(scottzhu): reeanble the test after moving it to benchmark test suite. + # The current test has performance flakiness issue. + logging.info('Expect the performance of Unified LSTM is within 80% of ' + 'CuDNN LSTM, got {0:.2f}%'.format(cudnn_vs_unified * 100)) + logging.info('Expect the performance of Unified LSTM is more than 5 times' + ' of normal LSTM, got {0:.2f}'.format(unified_vs_normal)) + + # Assert the performance diff should be within 80% of the native cudnn. + # self.assertGreaterEqual( + # cudnn_vs_unified, 0.80, + # 'Expect the performance of Unified LSTM is within 80% of CuDNN LSTM, ' + # 'but got {0:.2f}%'.format(cudnn_vs_unified * 100)) + # # Assert the performance diff between CPU impl and GPU impl should be more + # # than 5 times. + # self.assertGreaterEqual( + # unified_vs_normal, 5, + # 'Expect the performance of Unified LSTM is more than 5 times of ' + # 'normal LSTM, but got {0:.2f}'.format(unified_vs_normal)) + +if __name__ == '__main__': + test.main() diff --git a/tensorflow/python/keras/layers/wrappers.py b/tensorflow/python/keras/layers/wrappers.py index d40f7a2e80..67b154141e 100644 --- a/tensorflow/python/keras/layers/wrappers.py +++ b/tensorflow/python/keras/layers/wrappers.py @@ -23,8 +23,8 @@ import copy from tensorflow.python.framework import tensor_shape from tensorflow.python.keras import backend as K -from tensorflow.python.keras.engine.base_layer import InputSpec from tensorflow.python.keras.engine.base_layer import Layer +from tensorflow.python.keras.engine.input_spec import InputSpec from tensorflow.python.keras.layers.recurrent import _standardize_args from tensorflow.python.keras.utils import generic_utils from tensorflow.python.keras.utils import tf_utils @@ -389,6 +389,10 @@ class Bidirectional(Wrapper): raise ValueError('Invalid merge mode. ' 'Merge mode should be one of ' '{"sum", "mul", "ave", "concat", None}') + if getattr(layer, 'zero_output_for_mask', None) is not None: + # Force the zero_output_for_mask to be True if it presents. + layer.zero_output_for_mask = True + self.forward_layer = copy.copy(layer) config = layer.get_config() config['go_backwards'] = not config['go_backwards'] diff --git a/tensorflow/python/keras/layers/wrappers_test.py b/tensorflow/python/keras/layers/wrappers_test.py index 9584b0186c..b9196416dd 100644 --- a/tensorflow/python/keras/layers/wrappers_test.py +++ b/tensorflow/python/keras/layers/wrappers_test.py @@ -192,8 +192,8 @@ class TimeDistributedTest(test.TestCase): x = keras.layers.Input(shape=(3, 2)) layer = keras.layers.TimeDistributed(keras.layers.BatchNormalization()) _ = layer(x) - self.assertEquals(len(layer.updates), 2) - self.assertEquals(len(layer.trainable_weights), 2) + self.assertEqual(len(layer.updates), 2) + self.assertEqual(len(layer.trainable_weights), 2) layer.trainable = False assert not layer.updates assert not layer.trainable_weights @@ -201,6 +201,7 @@ class TimeDistributedTest(test.TestCase): assert len(layer.updates) == 2 assert len(layer.trainable_weights) == 2 + @tf_test_util.run_deprecated_v1 def test_TimeDistributed_with_masked_embedding_and_unspecified_shape(self): with self.cached_session(): # test with unspecified shape and Embeddings with mask_zero @@ -233,6 +234,7 @@ class TimeDistributedTest(test.TestCase): self.assertAllEqual(mask_outputs_val[i], ref_mask_val[i]) self.assertIs(mask_outputs[-1], None) # final layer + @tf_test_util.run_deprecated_v1 def test_TimeDistributed_with_masking_layer(self): with self.cached_session(): # test with Masking layer @@ -375,6 +377,7 @@ class BidirectionalTest(test.TestCase): model.compile(loss='mse', optimizer='sgd') model.fit(x, y, epochs=1, batch_size=1) + @tf_test_util.run_deprecated_v1 def test_Bidirectional_merged_value(self): rnn = keras.layers.LSTM samples = 2 @@ -505,6 +508,7 @@ class BidirectionalTest(test.TestCase): layer.trainable = True assert len(layer.trainable_weights) == 6 + @tf_test_util.run_deprecated_v1 def test_Bidirectional_updates(self): with self.cached_session(): x = keras.layers.Input(shape=(3, 2)) @@ -635,6 +639,34 @@ class BidirectionalTest(test.TestCase): y_np_3 = model.predict([x_np, s_fw_np, s_bk_np, c_np]) self.assertAllClose(y_np, y_np_3, atol=1e-4) + def test_Bidirectional_with_masking(self): + rnn = keras.layers.LSTM + samples = 2 + dim = 5 + timesteps = 3 + units = 3 + merge_mode = 'concat' + x = np.random.rand(samples, timesteps, dim) + # clear the first record's timestep 2, and expect the output of timestep 2 + # is also 0s. + x[0, 2] = 0 + + with self.cached_session(): + inputs = keras.Input((timesteps, dim)) + masked_inputs = keras.layers.Masking()(inputs) + wrapped = keras.layers.Bidirectional( + rnn(units, return_sequences=True), + merge_mode=merge_mode) + outputs = _to_list(wrapped(masked_inputs, training=True)) + self.assertEqual(len(outputs), 1) + self.assertEqual(outputs[0].get_shape().as_list(), + [None, timesteps, units * 2]) + + model = keras.Model(inputs, outputs) + y = _to_list(model.predict(x)) + self.assertEqual(len(y), 1) + self.assertAllClose(y[0][0, 2], np.zeros(units * 2)) + def _to_list(ls): if isinstance(ls, list): diff --git a/tensorflow/python/keras/losses.py b/tensorflow/python/keras/losses.py index 9f548bfe04..4c584d0ff0 100644 --- a/tensorflow/python/keras/losses.py +++ b/tensorflow/python/keras/losses.py @@ -19,16 +19,382 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import abc + import six +from tensorflow.python.framework import ops from tensorflow.python.keras import backend as K from tensorflow.python.keras.utils.generic_utils import deserialize_keras_object from tensorflow.python.keras.utils.generic_utils import serialize_keras_object +from tensorflow.python.keras.utils.losses_utils import compute_weighted_loss +from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import nn +from tensorflow.python.ops.losses import losses_impl from tensorflow.python.util.tf_export import tf_export +class Loss(object): + """Loss base class. + + To be implemented by subclasses: + * `call()`: Contains the logic for loss calculation using `y_true`, `y_pred`. + + Example subclass implementation: + ``` + class MeanSquaredError(Loss): + def call(self, y_true, y_pred): + y_pred = ops.convert_to_tensor(y_pred) + y_true = math_ops.cast(y_true, y_pred.dtype) + return K.mean(math_ops.square(y_pred - y_true), axis=-1) + ``` + + Args: + reduction: Type of `tf.losses.Reduction` to apply to loss. Default value is + `SUM_OVER_BATCH_SIZE`. + name: Optional name for the op. + """ + + def __init__(self, + reduction=losses_impl.ReductionV2.SUM_OVER_BATCH_SIZE, + name=None): + self.reduction = reduction + self.name = name + + def __call__(self, y_true, y_pred, sample_weight=None): + """Invokes the `Loss` instance. + + Args: + y_true: Ground truth values. + y_pred: The predicted values. + sample_weight: Optional `Tensor` whose rank is either 0, or the same rank + as `y_true`, or is broadcastable to `y_true`. `sample_weight` acts as a + coefficient for the loss. If a scalar is provided, then the loss is + simply scaled by the given value. If `sample_weight` is a tensor of size + `[batch_size]`, then the total loss for each sample of the batch is + rescaled by the corresponding element in the `sample_weight` vector. If + the shape of `sample_weight` matches the shape of `y_pred`, then the + loss of each measurable element of `y_pred` is scaled by the + corresponding value of `sample_weight`. + + Returns: + Weighted loss float `Tensor`. If `reduction` is `NONE`, this has the same + shape as `y_true`; otherwise, it is scalar. + + Raises: + ValueError: If the shape of `sample_weight` is invalid. + """ + with ops.name_scope(self.name, format(self.__class__.__name__), + (y_pred, y_true, sample_weight)): + losses = self.call(y_true, y_pred) + return compute_weighted_loss( + losses, sample_weight, reduction=self.reduction) + + @classmethod + def from_config(cls, config): + """Instantiates a `Loss` from its config (output of `get_config()`). + + Args: + config: Output of `get_config()`. + + Returns: + A `Loss` instance. + """ + return cls(**config) + + def get_config(self): + return {'reduction': self.reduction, 'name': self.name} + + @abc.abstractmethod + def call(self, y_true, y_pred): + """Invokes the `Loss` instance. + + Args: + y_true: Ground truth values, with the same shape as 'y_pred'. + y_pred: The predicted values. + """ + NotImplementedError('Must be implemented in subclasses.') + + +@tf_export('keras.losses.MeanSquaredError') +class MeanSquaredError(Loss): + """Computes the mean of squares of errors between labels and predictions. + + For example, if `y_true` is [0., 0., 1., 1.] and `y_pred` is [1., 1., 1., 0.] + then the mean squared error value is 3/4 (0.75). + + Usage: + + ```python + mse = tf.keras.losses.MeanSquaredError() + loss = mse([0., 0., 1., 1.], [1., 1., 1., 0.]) + print('Loss: ', loss.numpy()) # Loss: 0.75 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile('sgd', loss=tf.keras.losses.MeanSquaredError()) + ``` + """ + + def call(self, y_true, y_pred): + """Invokes the `MeanSquaredError` instance. + + Args: + y_true: Ground truth values. + y_pred: The predicted values. + + Returns: + Mean squared error losses. + """ + y_pred = ops.convert_to_tensor(y_pred) + y_true = math_ops.cast(y_true, y_pred.dtype) + return mean_squared_error(y_true, y_pred) + + +@tf_export('keras.losses.MeanAbsoluteError') +class MeanAbsoluteError(Loss): + """Computes the mean of absolute difference between labels and predictions. + + For example, if `y_true` is [0., 0., 1., 1.] and `y_pred` is [1., 1., 1., 0.] + then the mean absolute error value is 3/4 (0.75). + + Usage: + + ```python + mae = tf.keras.losses.MeanAbsoluteError() + loss = mae([0., 0., 1., 1.], [1., 1., 1., 0.]) + print('Loss: ', loss.numpy()) # Loss: 0.75 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile('sgd', loss=tf.keras.losses.MeanAbsoluteError()) + ``` + """ + + def call(self, y_true, y_pred): + """Invokes the `MeanAbsoluteError` instance. + + Args: + y_true: Ground truth values. + y_pred: The predicted values. + + Returns: + Mean absolute error losses. + """ + y_pred = ops.convert_to_tensor(y_pred) + y_true = math_ops.cast(y_true, y_pred.dtype) + return mean_absolute_error(y_true, y_pred) + + +@tf_export('keras.losses.MeanAbsolutePercentageError') +class MeanAbsolutePercentageError(Loss): + """Computes the mean absolute percentage error between `y_true` and `y_pred`. + + For example, if `y_true` is [0., 0., 1., 1.] and `y_pred` is [1., 1., 1., 0.] + then the mean absolute percentage error value is 5e+08. + + Usage: + + ```python + mape = tf.keras.losses.MeanAbsolutePercentageError() + loss = mape([0., 0., 1., 1.], [1., 1., 1., 0.]) + print('Loss: ', loss.numpy()) # Loss: 5e+08 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile('sgd', loss=tf.keras.losses.MeanAbsolutePercentageError()) + ``` + """ + + def call(self, y_true, y_pred): + """Invokes the `MeanAbsolutePercentageError` instance. + + Args: + y_true: Ground truth values. + y_pred: The predicted values. + + Returns: + Mean absolute percentage error losses. + """ + y_pred = ops.convert_to_tensor(y_pred) + y_true = math_ops.cast(y_true, y_pred.dtype) + return mean_absolute_percentage_error(y_true, y_pred) + + +@tf_export('keras.losses.MeanSquaredLogarithmicError') +class MeanSquaredLogarithmicError(Loss): + """Computes the mean squared logarithmic error between `y_true` and `y_pred`. + + For example, if `y_true` is [0., 0., 1., 1.] and `y_pred` is [1., 1., 1., 0.] + then the mean squared logarithmic error value is 0.36034. + + Usage: + + ```python + msle = tf.keras.losses.MeanSquaredLogarithmicError() + loss = msle([0., 0., 1., 1.], [1., 1., 1., 0.]) + print('Loss: ', loss.numpy()) # Loss: 0.36034 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile('sgd', loss=tf.keras.losses.MeanSquaredLogarithmicError()) + ``` + """ + + def call(self, y_true, y_pred): + """Invokes the `MeanSquaredLogarithmicError` instance. + + Args: + y_true: Ground truth values. + y_pred: The predicted values. + + Returns: + Mean squared logarithmic error losses. + """ + y_pred = ops.convert_to_tensor(y_pred) + y_true = math_ops.cast(y_true, y_pred.dtype) + return mean_squared_logarithmic_error(y_true, y_pred) + + +@tf_export('keras.losses.BinaryCrossentropy') +class BinaryCrossentropy(Loss): + """Computes the binary cross entropy loss between the labels and predictions. + + Usage: + + ```python + bce = tf.keras.losses.BinaryCrossentropy() + loss = bce([0., 0., 1., 1.], [1., 1., 1., 0.]) + print('Loss: ', loss.numpy()) # Loss: 12.007 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile('sgd', loss=tf.keras.losses.BinaryCrossentropy()) + ```` + + Args: + from_logits: Whether `output` is expected to be a logits tensor. By default, + we consider that `output` encodes a probability distribution. + label_smoothing: If greater than `0` then smooth the labels. + reduction: Type of `tf.losses.Reduction` to apply to loss. Default value is + `SUM_OVER_BATCH_SIZE`. + name: Optional name for the op. + """ + + def __init__(self, + from_logits=False, + label_smoothing=0, + reduction=losses_impl.ReductionV2.SUM_OVER_BATCH_SIZE, + name=None): + super(BinaryCrossentropy, self).__init__(reduction=reduction, name=name) + self.from_logits = from_logits + self.label_smoothing = label_smoothing + + def call(self, y_true, y_pred): + """Invokes the `BinaryCrossentropy` instance. + + Args: + y_true: Ground truth values. + y_pred: The predicted values. + + Returns: + Binary cross entropy losses. + """ + y_pred = ops.convert_to_tensor(y_pred) + y_true = math_ops.cast(y_true, y_pred.dtype) + + if self.label_smoothing > 0: + y_true = y_true * (1 - self.label_smoothing) + 0.5 * self.label_smoothing + + return binary_crossentropy(y_true, y_pred, from_logits=self.from_logits) + + +@tf_export('keras.losses.CategoricalCrossentropy') +class CategoricalCrossentropy(Loss): + """Computes categorical cross entropy loss between the `y_true` and `y_pred`. + + Usage: + + ```python + cce = tf.keras.losses.CategoricalCrossentropy() + loss = cce( + [[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]], + [[.9, .05, .05], [.5, .89, .6], [.05, .01, .94]]) + print('Loss: ', loss.numpy()) # Loss: 0.3239 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile('sgd', loss=tf.keras.losses.CategoricalCrossentropy()) + ```` + + Args: + from_logits: Whether `output` is expected to be a logits tensor. By default, + we consider that `output` encodes a probability distribution. + label_smoothing: If greater than `0` then smooth the labels. This option is + currently not supported when `y_pred` is a sparse input (not one-hot). + reduction: Type of `tf.losses.Reduction` to apply to loss. Default value is + `SUM_OVER_BATCH_SIZE`. + name: Optional name for the op. + """ + + def __init__(self, + from_logits=False, + label_smoothing=0, + reduction=losses_impl.ReductionV2.SUM_OVER_BATCH_SIZE, + name=None): + super(CategoricalCrossentropy, self).__init__( + reduction=reduction, name=name) + self.from_logits = from_logits + self.label_smoothing = label_smoothing + + def call(self, y_true, y_pred): + """Invokes the `CategoricalCrossentropy` instance. + + Args: + y_true: Ground truth values. + y_pred: The predicted values. + + Returns: + Categorical cross entropy losses. + """ + y_pred = ops.convert_to_tensor(y_pred) + y_true = ops.convert_to_tensor(y_true) + is_sparse = y_pred.shape != y_true.shape + + if is_sparse: + return sparse_categorical_crossentropy( + y_true, y_pred, from_logits=self.from_logits) + else: + y_true = math_ops.cast(y_true, y_pred.dtype) + if self.label_smoothing > 0: + num_classes = math_ops.cast(array_ops.shape(y_true)[1], y_pred.dtype) + smooth_positives = 1.0 - self.label_smoothing + smooth_negatives = self.label_smoothing / num_classes + y_true = y_true * smooth_positives + smooth_negatives + + return categorical_crossentropy( + y_true, y_pred, from_logits=self.from_logits) + + @tf_export('keras.metrics.mean_squared_error', 'keras.metrics.mse', 'keras.metrics.MSE', @@ -116,20 +482,22 @@ def logcosh(y_true, y_pred): @tf_export('keras.metrics.categorical_crossentropy', 'keras.losses.categorical_crossentropy') -def categorical_crossentropy(y_true, y_pred): - return K.categorical_crossentropy(y_true, y_pred) +def categorical_crossentropy(y_true, y_pred, from_logits=False): + return K.categorical_crossentropy(y_true, y_pred, from_logits=from_logits) @tf_export('keras.metrics.sparse_categorical_crossentropy', 'keras.losses.sparse_categorical_crossentropy') -def sparse_categorical_crossentropy(y_true, y_pred): - return K.sparse_categorical_crossentropy(y_true, y_pred) +def sparse_categorical_crossentropy(y_true, y_pred, from_logits=False): + return K.sparse_categorical_crossentropy( + y_true, y_pred, from_logits=from_logits) @tf_export('keras.metrics.binary_crossentropy', 'keras.losses.binary_crossentropy') -def binary_crossentropy(y_true, y_pred): - return K.mean(K.binary_crossentropy(y_true, y_pred), axis=-1) +def binary_crossentropy(y_true, y_pred, from_logits=False): + return K.mean( + K.binary_crossentropy(y_true, y_pred, from_logits=from_logits), axis=-1) @tf_export('keras.metrics.kullback_leibler_divergence', @@ -159,6 +527,40 @@ def cosine_proximity(y_true, y_pred): return -math_ops.reduce_sum(y_true * y_pred, axis=-1) +class CosineProximity(Loss): + """Computes the cosine distance between `y_true` and `y_pred`. + + Usage: + + ```python + cosine_loss = tf.losses.CosineProximity() + loss = cosine_loss([0., 1., 1.], [1., 0., 1.]) + print('Loss: ', loss.numpy()) # Loss: -0.5 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile('sgd', loss=tf.losses.CosineProximity()) + ``` + """ + + def call(self, y_true, y_pred): + """Calculates the cosine proximity loss. + + Args: + y_true: Ground truth values. + y_pred: The predicted values. + + Returns: + Cosine distance loss. + """ + y_pred = ops.convert_to_tensor(y_pred) + y_true = math_ops.cast(y_true, y_pred.dtype) + return cosine_proximity(y_true, y_pred) + + # Aliases. mse = MSE = mean_squared_error @@ -197,3 +599,9 @@ def get(identifier): else: raise ValueError('Could not interpret ' 'loss function identifier:', identifier) + + +LABEL_DTYPES_FOR_LOSSES = { + losses_impl.sparse_softmax_cross_entropy: 'int32', + sparse_categorical_crossentropy: 'int32' +} diff --git a/tensorflow/python/keras/losses_test.py b/tensorflow/python/keras/losses_test.py index c7015270ac..d2791cdcd3 100644 --- a/tensorflow/python/keras/losses_test.py +++ b/tensorflow/python/keras/losses_test.py @@ -24,6 +24,11 @@ import shutil import numpy as np from tensorflow.python import keras +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util +from tensorflow.python.ops import array_ops +from tensorflow.python.ops.losses import losses_impl from tensorflow.python.platform import test try: @@ -138,5 +143,633 @@ class KerasLossesTest(test.TestCase): loaded_model.predict(np.random.rand(128, 2)) +@test_util.run_all_in_graph_and_eager_modes +class MeanSquaredErrorTest(test.TestCase): + + def test_config(self): + mse_obj = keras.losses.MeanSquaredError( + reduction=losses_impl.ReductionV2.SUM, name='mse_1') + self.assertEqual(mse_obj.name, 'mse_1') + self.assertEqual(mse_obj.reduction, losses_impl.ReductionV2.SUM) + + def test_all_correct_unweighted(self): + mse_obj = keras.losses.MeanSquaredError() + y_true = constant_op.constant([4, 8, 12, 8, 1, 3], shape=(2, 3)) + loss = mse_obj(y_true, y_true) + self.assertAlmostEqual(self.evaluate(loss), 0.0, 3) + + def test_unweighted(self): + mse_obj = keras.losses.MeanSquaredError() + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3), + dtype=dtypes.float32) + loss = mse_obj(y_true, y_pred) + self.assertAlmostEqual(self.evaluate(loss), 49.5, 3) + + def test_scalar_weighted(self): + mse_obj = keras.losses.MeanSquaredError() + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3), + dtype=dtypes.float32) + loss = mse_obj(y_true, y_pred, sample_weight=2.3) + self.assertAlmostEqual(self.evaluate(loss), 113.85, 3) + + def test_sample_weighted(self): + mse_obj = keras.losses.MeanSquaredError() + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3), + dtype=dtypes.float32) + sample_weight = constant_op.constant([1.2, 3.4], shape=(2, 1)) + loss = mse_obj(y_true, y_pred, sample_weight=sample_weight) + self.assertAlmostEqual(self.evaluate(loss), 767.8 / 6, 3) + + def test_timestep_weighted(self): + mse_obj = keras.losses.MeanSquaredError() + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3, 1)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3, 1), + dtype=dtypes.float32) + sample_weight = constant_op.constant([3, 6, 5, 0, 4, 2], shape=(2, 3)) + loss = mse_obj(y_true, y_pred, sample_weight=sample_weight) + self.assertAlmostEqual(self.evaluate(loss), 587 / 6, 3) + + def test_zero_weighted(self): + mse_obj = keras.losses.MeanSquaredError() + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3), + dtype=dtypes.float32) + loss = mse_obj(y_true, y_pred, sample_weight=0) + self.assertAlmostEqual(self.evaluate(loss), 0.0, 3) + + def test_invalid_sample_weight(self): + mse_obj = keras.losses.MeanSquaredError() + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3, 1)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], shape=(2, 3, 1)) + sample_weight = constant_op.constant([3, 6, 5, 0], shape=(2, 2)) + with self.assertRaisesRegexp( + ValueError, r'Shapes \(2, 2\) and \(2, 3\) are incompatible'): + mse_obj(y_true, y_pred, sample_weight=sample_weight) + + def test_no_reduction(self): + mse_obj = keras.losses.MeanSquaredError( + reduction=losses_impl.ReductionV2.NONE) + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3), + dtype=dtypes.float32) + loss = mse_obj(y_true, y_pred, sample_weight=2.3) + loss = self.evaluate(loss) + self.assertArrayNear(loss, [84.3333, 143.3666], 1e-3) + + def test_sum_reduction(self): + mse_obj = keras.losses.MeanSquaredError( + reduction=losses_impl.ReductionV2.SUM) + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3), + dtype=dtypes.float32) + loss = mse_obj(y_true, y_pred, sample_weight=2.3) + self.assertAlmostEqual(self.evaluate(loss), 227.69998, 3) + + +@test_util.run_all_in_graph_and_eager_modes +class MeanAbsoluteErrorTest(test.TestCase): + + def test_config(self): + mae_obj = keras.losses.MeanAbsoluteError( + reduction=losses_impl.ReductionV2.SUM, name='mae_1') + self.assertEqual(mae_obj.name, 'mae_1') + self.assertEqual(mae_obj.reduction, losses_impl.ReductionV2.SUM) + + def test_all_correct_unweighted(self): + mae_obj = keras.losses.MeanAbsoluteError() + y_true = constant_op.constant([4, 8, 12, 8, 1, 3], shape=(2, 3)) + loss = mae_obj(y_true, y_true) + self.assertAlmostEqual(self.evaluate(loss), 0.0, 3) + + def test_unweighted(self): + mae_obj = keras.losses.MeanAbsoluteError() + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3), + dtype=dtypes.float32) + loss = mae_obj(y_true, y_pred) + self.assertAlmostEqual(self.evaluate(loss), 5.5, 3) + + def test_scalar_weighted(self): + mae_obj = keras.losses.MeanAbsoluteError() + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3), + dtype=dtypes.float32) + loss = mae_obj(y_true, y_pred, sample_weight=2.3) + self.assertAlmostEqual(self.evaluate(loss), 12.65, 3) + + def test_sample_weighted(self): + mae_obj = keras.losses.MeanAbsoluteError() + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3), + dtype=dtypes.float32) + sample_weight = constant_op.constant([1.2, 3.4], shape=(2, 1)) + loss = mae_obj(y_true, y_pred, sample_weight=sample_weight) + self.assertAlmostEqual(self.evaluate(loss), 81.4 / 6, 3) + + def test_timestep_weighted(self): + mae_obj = keras.losses.MeanAbsoluteError() + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3, 1)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3, 1), + dtype=dtypes.float32) + sample_weight = constant_op.constant([3, 6, 5, 0, 4, 2], shape=(2, 3)) + loss = mae_obj(y_true, y_pred, sample_weight=sample_weight) + self.assertAlmostEqual(self.evaluate(loss), 83 / 6, 3) + + def test_zero_weighted(self): + mae_obj = keras.losses.MeanAbsoluteError() + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3), + dtype=dtypes.float32) + loss = mae_obj(y_true, y_pred, sample_weight=0) + self.assertAlmostEqual(self.evaluate(loss), 0.0, 3) + + def test_invalid_sample_weight(self): + mae_obj = keras.losses.MeanAbsoluteError() + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3, 1)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], shape=(2, 3, 1)) + sample_weight = constant_op.constant([3, 6, 5, 0], shape=(2, 2)) + with self.assertRaisesRegexp( + ValueError, r'Shapes \(2, 2\) and \(2, 3\) are incompatible'): + mae_obj(y_true, y_pred, sample_weight=sample_weight) + + def test_no_reduction(self): + mae_obj = keras.losses.MeanAbsoluteError( + reduction=losses_impl.ReductionV2.NONE) + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3), + dtype=dtypes.float32) + loss = mae_obj(y_true, y_pred, sample_weight=2.3) + loss = self.evaluate(loss) + self.assertArrayNear(loss, [10.7333, 14.5666], 1e-3) + + def test_sum_reduction(self): + mae_obj = keras.losses.MeanAbsoluteError( + reduction=losses_impl.ReductionV2.SUM) + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3), + dtype=dtypes.float32) + loss = mae_obj(y_true, y_pred, sample_weight=2.3) + self.assertAlmostEqual(self.evaluate(loss), 25.29999, 3) + + +@test_util.run_all_in_graph_and_eager_modes +class MeanAbsolutePercentageErrorTest(test.TestCase): + + def test_config(self): + mape_obj = keras.losses.MeanAbsolutePercentageError( + reduction=losses_impl.ReductionV2.SUM, name='mape_1') + self.assertEqual(mape_obj.name, 'mape_1') + self.assertEqual(mape_obj.reduction, losses_impl.ReductionV2.SUM) + + def test_unweighted(self): + mape_obj = keras.losses.MeanAbsolutePercentageError() + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3), + dtype=dtypes.float32) + loss = mape_obj(y_true, y_pred) + self.assertAlmostEqual(self.evaluate(loss), 211.8518, 3) + + def test_scalar_weighted(self): + mape_obj = keras.losses.MeanAbsolutePercentageError() + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3), + dtype=dtypes.float32) + loss = mape_obj(y_true, y_pred, sample_weight=2.3) + self.assertAlmostEqual(self.evaluate(loss), 487.259, 3) + + def test_sample_weighted(self): + mape_obj = keras.losses.MeanAbsolutePercentageError() + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3), + dtype=dtypes.float32) + sample_weight = constant_op.constant([1.2, 3.4], shape=(2, 1)) + loss = mape_obj(y_true, y_pred, sample_weight=sample_weight) + self.assertAlmostEqual(self.evaluate(loss), 422.8888, 3) + + def test_timestep_weighted(self): + mape_obj = keras.losses.MeanAbsolutePercentageError() + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3, 1)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3, 1), + dtype=dtypes.float32) + sample_weight = constant_op.constant([3, 6, 5, 0, 4, 2], shape=(2, 3)) + loss = mape_obj(y_true, y_pred, sample_weight=sample_weight) + self.assertAlmostEqual(self.evaluate(loss), 694.4445, 3) + + def test_zero_weighted(self): + mape_obj = keras.losses.MeanAbsolutePercentageError() + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3), + dtype=dtypes.float32) + loss = mape_obj(y_true, y_pred, sample_weight=0) + self.assertAlmostEqual(self.evaluate(loss), 0.0, 3) + + +@test_util.run_all_in_graph_and_eager_modes +class MeanSquaredLogarithmicErrorTest(test.TestCase): + + def test_config(self): + msle_obj = keras.losses.MeanSquaredLogarithmicError( + reduction=losses_impl.ReductionV2.SUM, name='mape_1') + self.assertEqual(msle_obj.name, 'mape_1') + self.assertEqual(msle_obj.reduction, losses_impl.ReductionV2.SUM) + + def test_unweighted(self): + msle_obj = keras.losses.MeanSquaredLogarithmicError() + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3), + dtype=dtypes.float32) + loss = msle_obj(y_true, y_pred) + self.assertAlmostEqual(self.evaluate(loss), 1.4370, 3) + + def test_scalar_weighted(self): + msle_obj = keras.losses.MeanSquaredLogarithmicError() + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3), + dtype=dtypes.float32) + loss = msle_obj(y_true, y_pred, sample_weight=2.3) + self.assertAlmostEqual(self.evaluate(loss), 3.3051, 3) + + def test_sample_weighted(self): + msle_obj = keras.losses.MeanSquaredLogarithmicError() + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3), + dtype=dtypes.float32) + sample_weight = constant_op.constant([1.2, 3.4], shape=(2, 1)) + loss = msle_obj(y_true, y_pred, sample_weight=sample_weight) + self.assertAlmostEqual(self.evaluate(loss), 3.7856, 3) + + def test_timestep_weighted(self): + msle_obj = keras.losses.MeanSquaredLogarithmicError() + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3, 1)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3, 1), + dtype=dtypes.float32) + sample_weight = constant_op.constant([3, 6, 5, 0, 4, 2], shape=(2, 3)) + loss = msle_obj(y_true, y_pred, sample_weight=sample_weight) + self.assertAlmostEqual(self.evaluate(loss), 2.6473, 3) + + def test_zero_weighted(self): + msle_obj = keras.losses.MeanSquaredLogarithmicError() + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3), + dtype=dtypes.float32) + loss = msle_obj(y_true, y_pred, sample_weight=0) + self.assertAlmostEqual(self.evaluate(loss), 0.0, 3) + + +@test_util.run_all_in_graph_and_eager_modes +class CosineProximityTest(test.TestCase): + + def test_config(self): + cosine_obj = keras.losses.CosineProximity( + reduction=losses_impl.ReductionV2.SUM, name='cosine_loss') + self.assertEqual(cosine_obj.name, 'cosine_loss') + self.assertEqual(cosine_obj.reduction, losses_impl.ReductionV2.SUM) + + def test_unweighted(self): + cosine_obj = keras.losses.CosineProximity() + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3), + dtype=dtypes.float32) + loss = cosine_obj(y_true, y_pred) + self.assertAlmostEqual(self.evaluate(loss), -0.18722, 3) + + def test_scalar_weighted(self): + cosine_obj = keras.losses.CosineProximity() + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3), + dtype=dtypes.float32) + loss = cosine_obj(y_true, y_pred, sample_weight=2.3) + self.assertAlmostEqual(self.evaluate(loss), -0.43060, 3) + + def test_sample_weighted(self): + cosine_obj = keras.losses.CosineProximity() + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3), + dtype=dtypes.float32) + sample_weight = constant_op.constant([1.2, 3.4], shape=(2, 1)) + loss = cosine_obj(y_true, y_pred, sample_weight=sample_weight) + self.assertAlmostEqual(self.evaluate(loss), 0.15599, 3) + + def test_timestep_weighted(self): + cosine_obj = keras.losses.CosineProximity() + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3, 1)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3, 1), + dtype=dtypes.float32) + sample_weight = constant_op.constant([3, 6, 5, 0, 4, 2], shape=(2, 3)) + loss = cosine_obj(y_true, y_pred, sample_weight=sample_weight) + self.assertAlmostEqual(self.evaluate(loss), -2.0000, 3) + + def test_zero_weighted(self): + cosine_obj = keras.losses.CosineProximity() + y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) + y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], + shape=(2, 3), + dtype=dtypes.float32) + loss = cosine_obj(y_true, y_pred, sample_weight=0) + self.assertAlmostEqual(self.evaluate(loss), 0., 3) + + +@test_util.run_all_in_graph_and_eager_modes +class BinaryCrossentropyTest(test.TestCase): + + def test_config(self): + bce_obj = keras.losses.BinaryCrossentropy( + reduction=losses_impl.ReductionV2.SUM, name='bce_1') + self.assertEqual(bce_obj.name, 'bce_1') + self.assertEqual(bce_obj.reduction, losses_impl.ReductionV2.SUM) + + def test_all_correct_unweighted(self): + y_true = constant_op.constant([[1, 0, 0], [0, 1, 0], [0, 0, 1]], + dtype=dtypes.float32) + bce_obj = keras.losses.BinaryCrossentropy() + loss = bce_obj(y_true, y_true) + self.assertAlmostEqual(self.evaluate(loss), 0.0, 3) + + # Test with logits. + logits = constant_op.constant([[100.0, -100.0, -100.0], + [-100.0, 100.0, -100.0], + [-100.0, -100.0, 100.0]]) + bce_obj = keras.losses.BinaryCrossentropy(from_logits=True) + loss = bce_obj(y_true, logits) + self.assertAlmostEqual(self.evaluate(loss), 0.0, 3) + + def test_unweighted(self): + bce_obj = keras.losses.BinaryCrossentropy() + y_true = constant_op.constant([1, 0, 1, 0, 0, 1], shape=(2, 3)) + y_pred = constant_op.constant([1, 1, 1, 0, 1, 0], + shape=(2, 3), + dtype=dtypes.float32) + loss = bce_obj(y_true, y_pred) + self.assertAlmostEqual(self.evaluate(loss), 8.0004, 3) + + # Test with logits. + logits = constant_op.constant([10., 10., 10., -10., 10, -10], + shape=(2, 3), + dtype=dtypes.float32) + bce_obj = keras.losses.BinaryCrossentropy(from_logits=True) + loss = bce_obj(y_true, logits) + self.assertAlmostEqual(self.evaluate(loss), 5., 3) + + def test_scalar_weighted(self): + bce_obj = keras.losses.BinaryCrossentropy() + y_true = constant_op.constant([1, 0, 1, 0, 0, 1], shape=(2, 3)) + y_pred = constant_op.constant([1, 1, 1, 0, 1, 0], + shape=(2, 3), + dtype=dtypes.float32) + loss = bce_obj(y_true, y_pred, sample_weight=2.3) + self.assertAlmostEqual(self.evaluate(loss), 18.4010, 3) + + # Test with logits. + y_true = array_ops.ones((32, 1)) + logits = array_ops.ones((32, 1), dtype=dtypes.float32) + bce_obj = keras.losses.BinaryCrossentropy(from_logits=True) + loss = bce_obj(y_true, logits, sample_weight=2.3) + self.assertAlmostEqual(self.evaluate(loss), 0.7205, 3) + + def test_sample_weighted(self): + bce_obj = keras.losses.BinaryCrossentropy() + y_true = constant_op.constant([1, 0, 1, 0, 0, 1], shape=(2, 3)) + y_pred = constant_op.constant([1, 1, 1, 0, 1, 0], + shape=(2, 3), + dtype=dtypes.float64) + sample_weight = constant_op.constant([1.2, 3.4], shape=(2, 1)) + loss = bce_obj(y_true, y_pred, sample_weight=sample_weight) + self.assertAlmostEqual(self.evaluate(loss), 21.4907, 3) + + # Test with logits. + y_true = constant_op.constant([[0, 0, 1], [1, 0, 0], [0, 1, 0]]) + logits = constant_op.constant( + [[100.0, -100.0, -100.0], [-100.0, 100.0, -100.0], + [-100.0, -100.0, 100.0]], + dtype=dtypes.float64) + weights = constant_op.constant([3, 2, 8]) + bce_obj = keras.losses.BinaryCrossentropy(from_logits=True) + loss = bce_obj(y_true, logits, sample_weight=weights) + self.assertAlmostEqual(self.evaluate(loss), 288.8888, 3) + + def test_no_reduction(self): + y_true = constant_op.constant(((1, 0, 1), (1, 1, 0), (0, 1, 1))) + logits = constant_op.constant(((100.0, -100.0, 100.0), + (100.0, -100.0, 100.0), + (100.0, 100.0, -100.0))) + bce_obj = keras.losses.BinaryCrossentropy( + from_logits=True, reduction=losses_impl.ReductionV2.NONE) + loss = bce_obj(y_true, logits) + self.assertAllClose((0., 66.6666, 66.6666), self.evaluate(loss), 3) + + def test_label_smoothing(self): + logits = constant_op.constant([[100.0, -100.0, -100.0]]) + y_true = constant_op.constant([[1, 0, 1]]) + label_smoothing = 0.1 + # Loss: max(x, 0) - x * z + log(1 + exp(-abs(x))) + # Label smoothing: z' = z * (1 - L) + 0.5L + # 1 = 1 - 0.5L + # 0 = 0.5L + # Applying the above two fns to the given input: + # (100 - 100 * (1 - 0.5 L) + 0 + + # 0 + 100 * (0.5 L) + 0 + + # 0 + 100 * (1 - 0.5 L) + 0) * (1/3) + # = (100 + 50L) * 1/3 + bce_obj = keras.losses.BinaryCrossentropy( + from_logits=True, label_smoothing=label_smoothing) + loss = bce_obj(y_true, logits) + expected_value = (100.0 + 50.0 * label_smoothing) / 3.0 + self.assertAlmostEqual(self.evaluate(loss), expected_value, 3) + + +@test_util.run_all_in_graph_and_eager_modes +class CategoricalCrossentropyTest(test.TestCase): + + def test_config(self): + cce_obj = keras.losses.CategoricalCrossentropy( + reduction=losses_impl.ReductionV2.SUM, name='bce_1') + self.assertEqual(cce_obj.name, 'bce_1') + self.assertEqual(cce_obj.reduction, losses_impl.ReductionV2.SUM) + + def test_all_correct_unweighted(self): + y_true = constant_op.constant([[1, 0, 0], [0, 1, 0], [0, 0, 1]], + dtype=dtypes.int64) + y_pred = constant_op.constant([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]], + dtype=dtypes.float32) + cce_obj = keras.losses.CategoricalCrossentropy() + loss = cce_obj(y_true, y_pred) + self.assertAlmostEqual(self.evaluate(loss), 0.0, 3) + + # Test with logits. + logits = constant_op.constant([[10., 0., 0.], [0., 10., 0.], [0., 0., 10.]]) + cce_obj = keras.losses.CategoricalCrossentropy(from_logits=True) + loss = cce_obj(y_true, logits) + self.assertAlmostEqual(self.evaluate(loss), 0.0, 3) + + def test_unweighted(self): + cce_obj = keras.losses.CategoricalCrossentropy() + y_true = constant_op.constant([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) + y_pred = constant_op.constant( + [[.9, .05, .05], [.5, .89, .6], [.05, .01, .94]], dtype=dtypes.float32) + loss = cce_obj(y_true, y_pred) + self.assertAlmostEqual(self.evaluate(loss), .3239, 3) + + # Test with logits. + logits = constant_op.constant([[8., 1., 1.], [0., 9., 1.], [2., 3., 5.]]) + cce_obj = keras.losses.CategoricalCrossentropy(from_logits=True) + loss = cce_obj(y_true, logits) + self.assertAlmostEqual(self.evaluate(loss), .0573, 3) + + def test_scalar_weighted(self): + cce_obj = keras.losses.CategoricalCrossentropy() + y_true = constant_op.constant([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) + y_pred = constant_op.constant( + [[.9, .05, .05], [.5, .89, .6], [.05, .01, .94]], dtype=dtypes.float32) + loss = cce_obj(y_true, y_pred, sample_weight=2.3) + self.assertAlmostEqual(self.evaluate(loss), .7449, 3) + + # Test with logits. + logits = constant_op.constant([[8., 1., 1.], [0., 9., 1.], [2., 3., 5.]]) + cce_obj = keras.losses.CategoricalCrossentropy(from_logits=True) + loss = cce_obj(y_true, logits, sample_weight=2.3) + self.assertAlmostEqual(self.evaluate(loss), .1317, 3) + + def test_sample_weighted(self): + cce_obj = keras.losses.CategoricalCrossentropy() + y_true = constant_op.constant([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) + y_pred = constant_op.constant( + [[.9, .05, .05], [.5, .89, .6], [.05, .01, .94]], dtype=dtypes.float32) + sample_weight = constant_op.constant([[1.2], [3.4], [5.6]], shape=(3, 1)) + loss = cce_obj(y_true, y_pred, sample_weight=sample_weight) + self.assertAlmostEqual(self.evaluate(loss), 1.0696, 3) + + # Test with logits. + logits = constant_op.constant([[8., 1., 1.], [0., 9., 1.], [2., 3., 5.]]) + cce_obj = keras.losses.CategoricalCrossentropy(from_logits=True) + loss = cce_obj(y_true, logits, sample_weight=sample_weight) + self.assertAlmostEqual(self.evaluate(loss), 0.31829, 3) + + def test_no_reduction(self): + y_true = constant_op.constant([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) + logits = constant_op.constant([[8., 1., 1.], [0., 9., 1.], [2., 3., 5.]]) + cce_obj = keras.losses.CategoricalCrossentropy( + from_logits=True, reduction=losses_impl.ReductionV2.NONE) + loss = cce_obj(y_true, logits) + self.assertAllClose((0.001822, 0.000459, 0.169846), self.evaluate(loss), 3) + + def test_label_smoothing(self): + logits = constant_op.constant([[100.0, -100.0, -100.0]]) + y_true = constant_op.constant([[1, 0, 0]]) + label_smoothing = 0.1 + # Softmax Cross Entropy Loss: -\sum_i p_i \log q_i + # where for a softmax activation + # \log q_i = x_i - \log \sum_j \exp x_j + # = x_i - x_max - \log \sum_j \exp (x_j - x_max) + # For our activations, [100, -100, -100] + # \log ( exp(0) + exp(-200) + exp(-200) ) = 0 + # so our log softmaxes become: [0, -200, -200] + # Label smoothing: z' = z * (1 - L) + L/n + # 1 = 1 - L + L/n + # 0 = L/n + # Applying the above two fns to the given input: + # -0 * (1 - L + L/n) + 200 * L/n + 200 * L/n = 400 L/n + cce_obj = keras.losses.CategoricalCrossentropy( + from_logits=True, label_smoothing=label_smoothing) + loss = cce_obj(y_true, logits) + expected_value = 400.0 * label_smoothing / 3.0 + self.assertAlmostEqual(self.evaluate(loss), expected_value, 3) + + def test_all_correct_unweighted_sparse(self): + y_true = constant_op.constant([[0], [1], [2]], dtype=dtypes.int64) + y_pred = constant_op.constant([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]], + dtype=dtypes.float32) + cce_obj = keras.losses.CategoricalCrossentropy() + loss = cce_obj(y_true, y_pred) + self.assertAlmostEqual(self.evaluate(loss), 0.0, 3) + + # Test with logits. + logits = constant_op.constant([[10., 0., 0.], [0., 10., 0.], [0., 0., 10.]]) + cce_obj = keras.losses.CategoricalCrossentropy(from_logits=True) + loss = cce_obj(y_true, logits) + self.assertAlmostEqual(self.evaluate(loss), 0.0, 3) + + def test_unweighted_sparse(self): + cce_obj = keras.losses.CategoricalCrossentropy() + y_true = constant_op.constant([0, 1, 2]) + y_pred = constant_op.constant( + [[.9, .05, .05], [.5, .89, .6], [.05, .01, .94]], dtype=dtypes.float32) + loss = cce_obj(y_true, y_pred) + self.assertAlmostEqual(self.evaluate(loss), .3239, 3) + + # Test with logits. + logits = constant_op.constant([[8., 1., 1.], [0., 9., 1.], [2., 3., 5.]]) + cce_obj = keras.losses.CategoricalCrossentropy(from_logits=True) + loss = cce_obj(y_true, logits) + self.assertAlmostEqual(self.evaluate(loss), .0573, 3) + + def test_scalar_weighted_sparse(self): + cce_obj = keras.losses.CategoricalCrossentropy() + y_true = constant_op.constant([[0], [1], [2]]) + y_pred = constant_op.constant( + [[.9, .05, .05], [.5, .89, .6], [.05, .01, .94]], dtype=dtypes.float32) + loss = cce_obj(y_true, y_pred, sample_weight=2.3) + self.assertAlmostEqual(self.evaluate(loss), .7449, 3) + + # Test with logits. + logits = constant_op.constant([[8., 1., 1.], [0., 9., 1.], [2., 3., 5.]]) + cce_obj = keras.losses.CategoricalCrossentropy(from_logits=True) + loss = cce_obj(y_true, logits, sample_weight=2.3) + self.assertAlmostEqual(self.evaluate(loss), .1317, 3) + + def test_sample_weighted_sparse(self): + cce_obj = keras.losses.CategoricalCrossentropy() + y_true = constant_op.constant([[0], [1], [2]]) + y_pred = constant_op.constant( + [[.9, .05, .05], [.5, .89, .6], [.05, .01, .94]], dtype=dtypes.float32) + sample_weight = constant_op.constant([[1.2], [3.4], [5.6]], shape=(3, 1)) + loss = cce_obj(y_true, y_pred, sample_weight=sample_weight) + self.assertAlmostEqual(self.evaluate(loss), 1.0696, 3) + + # Test with logits. + logits = constant_op.constant([[8., 1., 1.], [0., 9., 1.], [2., 3., 5.]]) + cce_obj = keras.losses.CategoricalCrossentropy(from_logits=True) + loss = cce_obj(y_true, logits, sample_weight=sample_weight) + self.assertAlmostEqual(self.evaluate(loss), 0.31829, 3) + + def test_no_reduction_sparse(self): + y_true = constant_op.constant([[0], [1], [2]]) + logits = constant_op.constant([[8., 1., 1.], [0., 9., 1.], [2., 3., 5.]]) + cce_obj = keras.losses.CategoricalCrossentropy( + from_logits=True, reduction=losses_impl.ReductionV2.NONE) + loss = cce_obj(y_true, logits) + self.assertAllClose((0.001822, 0.000459, 0.169846), self.evaluate(loss), 3) + + if __name__ == '__main__': test.main() diff --git a/tensorflow/python/keras/metrics.py b/tensorflow/python/keras/metrics.py index 2ea6405597..3c2682e4c6 100644 --- a/tensorflow/python/keras/metrics.py +++ b/tensorflow/python/keras/metrics.py @@ -24,9 +24,10 @@ import functools import sys import types import weakref +from enum import Enum +import numpy as np import six -from tensorflow.python.compat import compat from tensorflow.python.eager import context from tensorflow.python.eager import function from tensorflow.python.framework import dtypes @@ -48,8 +49,10 @@ from tensorflow.python.keras.losses import sparse_categorical_crossentropy from tensorflow.python.keras.losses import squared_hinge from tensorflow.python.keras.utils.generic_utils import deserialize_keras_object from tensorflow.python.keras.utils.generic_utils import serialize_keras_object +from tensorflow.python.keras.utils.generic_utils import to_list +from tensorflow.python.keras.utils.losses_utils import squeeze_or_expand_dimensions from tensorflow.python.ops import array_ops -from tensorflow.python.ops import confusion_matrix +from tensorflow.python.ops import check_ops from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import init_ops from tensorflow.python.ops import math_ops @@ -63,13 +66,6 @@ from tensorflow.python.util.tf_export import tf_export from tensorflow.tools.docs import doc_controls -def check_is_tensor_or_operation(x, name): - """Raises type error if the given input is not a tensor or operation.""" - if not (isinstance(x, ops.Tensor) or isinstance(x, ops.Operation)): - raise TypeError('{0} must be a Tensor or Operation, given: {1}'.format( - name, x)) - - def clone_metric(metric): """Returns a clone of the metric if stateful, otherwise returns it as is.""" if isinstance(metric, Metric): @@ -102,8 +98,6 @@ def update_state_wrapper(update_state_fn): update_op = update_state_fn(*args, **kwargs) if update_op is not None: # update_op will be None in eager execution. metric_obj.add_update(update_op, inputs=True) - check_is_tensor_or_operation( - update_op, 'Metric {0}\'s update'.format(metric_obj.name)) return update_op return tf_decorator.make_decorator(update_state_fn, decorated) @@ -128,7 +122,7 @@ def result_wrapper(result_fn): `merge_call()`. """ - def decorated(metric_obj, *args): + def decorated(_, *args): """Decorated function with merge_call.""" replica_context = distribution_strategy_context.get_replica_context() if replica_context is None: # if in cross replica context already @@ -147,9 +141,8 @@ def result_wrapper(result_fn): # Wrapping result in merge_call. merge_call is used when we want to leave # replica mode and compute a value in cross replica mode. - result_t = replica_context.merge_call(merge_fn_wrapper, result_fn, *args) - check_is_tensor_or_operation(result_t, - 'Metric {0}\'s result'.format(metric_obj.name)) + result_t = replica_context.merge_call( + merge_fn_wrapper, args=(result_fn,) + args) return result_t return tf_decorator.make_decorator(result_fn, decorated) @@ -170,108 +163,169 @@ def weakmethod(method): return inner -def safe_div(numerator, denominator): - """Computes a safe divide which returns 0 if the denominator is zero. +class _ConfusionMatrix(Enum): + TRUE_POSITIVES = 'tp' + FALSE_POSITIVES = 'fp' + TRUE_NEGATIVES = 'tn' + FALSE_NEGATIVES = 'fn' - Note that the function contains an additional conditional check that is - necessary for avoiding situations where the loss is zero causing NaNs to - creep into the gradient computation. - Args: - numerator: An arbitrary `Tensor`. - denominator: A `Tensor` whose shape matches `numerator` and whose values are - assumed to be non-negative. +def _assert_thresholds_range(thresholds): + invalid_thresholds = [t for t in thresholds if t < 0 or t > 1] + if any(invalid_thresholds): + raise ValueError('Threshold values must be in [0, 1]. Invalid values: {}' + .format(invalid_thresholds)) - Returns: - The element-wise value of the numerator divided by the denominator. - """ - if compat.forward_compatible(2018, 11, 1): - return math_ops.div_no_nan(numerator, denominator) - return array_ops.where( - math_ops.greater(denominator, 0), - math_ops.div(numerator, - array_ops.where( - math_ops.equal(denominator, 0), - array_ops.ones_like(denominator), denominator)), - array_ops.zeros_like(numerator)) +def _update_confusion_matrix_variables(variables_to_update, + y_true, + y_pred, + thresholds, + sample_weight=None): + """Returns op to update the given confusion matrix variables. + + For every pair of values in y_true and y_pred: -def squeeze_or_expand_dimensions(y_pred, y_true, sample_weight): - """Squeeze or expand last dimension if needed. + true_positive: y_true == True and y_pred > thresholds + false_negatives: y_true == True and y_pred <= thresholds + true_negatives: y_true == False and y_pred <= thresholds + false_positive: y_true == False and y_pred > thresholds - 1. Squeezes last dim of `y_pred` or `y_true` if their rank differs by 1 - (using `confusion_matrix.remove_squeezable_dimensions`). - 2. Squeezes or expands last dim of `sample_weight` if its rank differs by 1 - from the new rank of `y_pred`. - If `sample_weight` is scalar, it is kept scalar. + The results will be weighted and added together. When multiple thresholds are + provided, we will repeat the same for every threshold. - This will use static shape if available. Otherwise, it will add graph - operations, which could result in a performance hit. + For estimation of these metrics over a stream of data, the function creates an + `update_op` operation that updates the given variables. + + If `sample_weight` is `None`, weights default to 1. + Use weights of 0 to mask values. Args: - y_pred: Predicted values, a `Tensor` of arbitrary dimensions. - y_true: Optional label `Tensor` whose dimensions match `y_pred`. - sample_weight: Optional weight scalar or `Tensor` whose dimensions match - `y_pred`. + variables_to_update: Dictionary with 'tp', 'fn', 'tn', 'fp' as valid keys + and corresponding variables to update as values. + y_true: A `Tensor` whose shape matches `y_pred`. Will be cast to `bool`. + y_pred: A floating point `Tensor` of arbitrary shape and whose values are in + the range `[0, 1]`. + thresholds: A float value or a python list or tuple of float thresholds in + `[0, 1]`. + sample_weight: Optional `Tensor` whose rank is either 0, or the same rank as + `y_true`, and must be broadcastable to `y_true` (i.e., all dimensions must + be either `1`, or the same as the corresponding `y_true` dimension). Returns: - Tuple of `y_pred`, `y_true` and `sample_weight`. Each of them possibly has - the last dimension squeezed, - `sample_weight` could be extended by one dimension. + Update op. + + Raises: + ValueError: If `y_pred` and `y_true` have mismatched shapes, or if + `sample_weight` is not `None` and its shape doesn't match `y_pred`, or if + `variables_to_update` contains invalid keys. """ - if y_true is not None: - # squeeze last dim of `y_pred` or `y_true` if their rank differs by 1 - y_true, y_pred = confusion_matrix.remove_squeezable_dimensions( - y_true, y_pred) - - if sample_weight is None: - return y_pred, y_true, None - - sample_weight = ops.convert_to_tensor(sample_weight) - weights_shape = sample_weight.get_shape() - weights_rank = weights_shape.ndims - if weights_rank == 0: # If weights is scalar, do nothing. - return y_pred, y_true, sample_weight - - y_pred_shape = y_pred.get_shape() - y_pred_rank = y_pred_shape.ndims - if (y_pred_rank is not None) and (weights_rank is not None): - # Use static rank. - if weights_rank - y_pred_rank == 1: - sample_weight = array_ops.squeeze(sample_weight, [-1]) - elif y_pred_rank - weights_rank == 1: - sample_weight = array_ops.expand_dims(sample_weight, [-1]) - return y_pred, y_true, sample_weight - - # Use dynamic rank. - weights_rank_tensor = array_ops.rank(sample_weight) - rank_diff = weights_rank_tensor - array_ops.rank(y_pred) - maybe_squeeze_weights = lambda: array_ops.squeeze(sample_weight, [-1]) - - def _maybe_expand_weights(): - return control_flow_ops.cond( - math_ops.equal(rank_diff, - -1), lambda: array_ops.expand_dims(sample_weight, [-1]), - lambda: sample_weight) - - def _maybe_adjust_weights(): - return control_flow_ops.cond( - math_ops.equal(rank_diff, 1), maybe_squeeze_weights, - _maybe_expand_weights) - - # squeeze or expand last dim of `sample_weight` if its rank differs by 1 - # from the new rank of `y_pred`. - sample_weight = control_flow_ops.cond( - math_ops.equal(weights_rank_tensor, 0), lambda: sample_weight, - _maybe_adjust_weights) - return y_pred, y_true, sample_weight + if variables_to_update is None: + return + y_true = ops.convert_to_tensor(y_true) + y_pred = ops.convert_to_tensor(y_pred) + y_pred.shape.assert_is_compatible_with(y_true.shape) + + if not any( + key for key in variables_to_update if key in list(_ConfusionMatrix)): + raise ValueError( + 'Please provide at least one valid confusion matrix ' + 'variable to update. Valid variable key options are: "{}". ' + 'Received: "{}"'.format( + list(_ConfusionMatrix), variables_to_update.keys())) + + invalid_keys = [ + key for key in variables_to_update if key not in list(_ConfusionMatrix) + ] + if invalid_keys: + raise ValueError( + 'Invalid keys: {}. Valid variable key options are: "{}"'.format( + invalid_keys, list(_ConfusionMatrix))) + + with ops.control_dependencies([ + check_ops.assert_greater_equal( + y_pred, + math_ops.cast(0.0, dtype=y_pred.dtype), + message='predictions must be >= 0'), + check_ops.assert_less_equal( + y_pred, + math_ops.cast(1.0, dtype=y_pred.dtype), + message='predictions must be <= 1') + ]): + y_pred, y_true, sample_weight = squeeze_or_expand_dimensions( + math_ops.cast(y_pred, dtype=dtypes.float32), + math_ops.cast(y_true, dtype=dtypes.bool), sample_weight) + + thresholds = to_list(thresholds) + num_thresholds = len(thresholds) + num_predictions = array_ops.size(y_pred) + + # Reshape predictions and labels. + predictions_2d = array_ops.reshape(y_pred, [1, -1]) + labels_2d = array_ops.reshape( + math_ops.cast(y_true, dtype=dtypes.bool), [1, -1]) + + # Tile the thresholds for every prediction. + thresh_tiled = array_ops.tile( + array_ops.expand_dims(array_ops.constant(thresholds), 1), + array_ops.stack([1, num_predictions])) + + # Tile the predictions for every threshold. + preds_tiled = array_ops.tile(predictions_2d, [num_thresholds, 1]) + + # Compare predictions and threshold. + pred_is_pos = math_ops.greater(preds_tiled, thresh_tiled) + + # Tile labels by number of thresholds + label_is_pos = array_ops.tile(labels_2d, [num_thresholds, 1]) + + if sample_weight is not None: + weights = weights_broadcast_ops.broadcast_weights( + math_ops.cast(sample_weight, dtype=dtypes.float32), y_pred) + weights_tiled = array_ops.tile( + array_ops.reshape(weights, [1, -1]), [num_thresholds, 1]) + else: + weights_tiled = None + + update_ops = [] + + def weighted_assign_add(label, pred, weights, var): + label_and_pred = math_ops.cast( + math_ops.logical_and(label, pred), dtype=dtypes.float32) + if weights is not None: + label_and_pred *= weights + return state_ops.assign_add(var, math_ops.reduce_sum(label_and_pred, 1)) + + loop_vars = { + _ConfusionMatrix.TRUE_POSITIVES: (label_is_pos, pred_is_pos), + } + update_tn = _ConfusionMatrix.TRUE_NEGATIVES in variables_to_update + update_fp = _ConfusionMatrix.FALSE_POSITIVES in variables_to_update + update_fn = _ConfusionMatrix.FALSE_NEGATIVES in variables_to_update + + if update_fn or update_tn: + pred_is_neg = math_ops.logical_not(pred_is_pos) + loop_vars[_ConfusionMatrix.FALSE_NEGATIVES] = (label_is_pos, pred_is_neg) + + if update_fp or update_tn: + label_is_neg = math_ops.logical_not(label_is_pos) + loop_vars[_ConfusionMatrix.FALSE_POSITIVES] = (label_is_neg, pred_is_pos) + if update_tn: + loop_vars[_ConfusionMatrix.TRUE_NEGATIVES] = (label_is_neg, pred_is_neg) + + for matrix_cond, (label, pred) in loop_vars.items(): + if matrix_cond in variables_to_update: + update_ops.append( + weighted_assign_add(label, pred, weights_tiled, + variables_to_update[matrix_cond])) + return control_flow_ops.group(update_ops) @six.add_metaclass(abc.ABCMeta) class Metric(Layer): """Encapsulates metric logic and state. - Usage with eager execution: + Usage: ```python m = SomeMetric(...) @@ -280,19 +334,6 @@ class Metric(Layer): print('Final result: ', m.result().numpy()) ``` - Usage with graph execution: - - ```python - m = SomeMetric(...) - init_op = tf.variables_initializer(m.variables) # Initialize variables - with tf.Session() as sess: - sess.run(init_op) - for input in ...: - update_op = m.update_state(input) - sess.run(update_op) - print('Final result: ', sess.run(m.result())) - ``` - Usage with tf.keras API: ```python @@ -388,9 +429,20 @@ class Metric(Layer): Returns: The metric value tensor. """ - update_op = self.update_state(*args, **kwargs) # pylint: disable=not-callable + update_op = self.update_state(*args, **kwargs) with ops.control_dependencies([update_op]): - return self.result() # pylint: disable=not-callable + result_t = self.result() + + # We are adding the metric object as metadata on the result tensor. + # This is required when we want to use a metric with `add_metric` API on + # a Model/Layer in graph mode. This metric instance will later be used + # to reset variable state after each epoch of training. + # Example: + # model = Model() + # model.add_metric(Mean()(values), name='mean') + if not context.executing_eagerly(): + result_t._metric_obj = self # pylint: disable=protected-access + return result_t def reset_states(self): """Resets all of the metric state variables. @@ -459,15 +511,35 @@ class Metric(Layer): ### End: For use by subclasses ### +@tf_export('metrics.Mean', 'keras.metrics.Mean') class Mean(Metric): """Computes the (weighted) mean of the given values. + For example, if values is [1, 3, 5, 7] then the mean is 4. + If the weights were specified as [1, 1, 0, 0] then the mean would be 2. + This metric creates two variables, `total` and `count` that are used to compute the average of `values`. This average is ultimately returned as `mean` which is an idempotent operation that simply divides `total` by `count`. If `sample_weight` is `None`, weights default to 1. Use `sample_weight` of 0 to mask values. + + Usage: + + ```python + m = tf.metrics.Mean() + m.update_state([1, 3, 5, 7]) + print('Final result: ', m.result().numpy()) # Final result: 4.0 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.add_metric(metrics_module.Mean(name='mean_1')(outputs)) + model.compile('sgd', loss='mse') + ``` """ def __init__(self, name='mean', dtype=None): @@ -525,11 +597,10 @@ class Mean(Metric): # updated. update_total_op = state_ops.assign_add(self.total, values) with ops.control_dependencies([update_total_op]): - update_count_op = state_ops.assign_add(self.count, num_values) - return ops.convert_to_tensor(update_count_op) + return state_ops.assign_add(self.count, num_values) def result(self): - return safe_div(self.total, self.count) + return math_ops.div_no_nan(self.total, self.count) class MeanMetricWrapper(Mean): @@ -574,14 +645,62 @@ class MeanMetricWrapper(Mean): matches, sample_weight=sample_weight) def get_config(self): - config = self._fn_kwargs + config = {'fn': self._fn} + config.update(self._fn_kwargs) base_config = super(MeanMetricWrapper, self).get_config() return dict(list(base_config.items()) + list(config.items())) +@tf_export('metrics.Accuracy', 'keras.metrics.Accuracy') +class Accuracy(MeanMetricWrapper): + """Calculates how often predictions matches labels. + + For example, if `y_true` is [1, 2, 3, 4] and `y_pred` is [0, 2, 3, 4] + then the accuracy is 3/4 or .75. If the weights were specified as + [1, 1, 0, 0] then the accuracy would be 1/2 or .5. + + This metric creates two local variables, `total` and `count` that are used to + compute the frequency with which `y_pred` matches `y_true`. This frequency is + ultimately returned as `binary accuracy`: an idempotent operation that simply + divides `total` by `count`. + + If `sample_weight` is `None`, weights default to 1. + Use `sample_weight` of 0 to mask values. + + Usage: + + ```python + m = tf.metrics.Accuracy() + m.update_state([1, 2, 3, 4], [0, 2, 3, 4]) + print('Final result: ', m.result().numpy()) # Final result: 0.75 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile('sgd', loss='mse', metrics=[tf.metrics.Accuracy()]) + ``` + """ + + def __init__(self, name='accuracy', dtype=None): + super(Accuracy, self).__init__(accuracy, name, dtype=dtype) + + @classmethod + def from_config(cls, config): + if 'fn' in config: + config.pop('fn') + return super(Accuracy, cls).from_config(config) + + +@tf_export('metrics.BinaryAccuracy', 'keras.metrics.BinaryAccuracy') class BinaryAccuracy(MeanMetricWrapper): """Calculates how often predictions matches labels. + For example, if `y_true` is [1, 1, 0, 0] and `y_pred` is [0.98, 1, 0, 0.6] + then the binary accuracy is 3/4 or .75. If the weights were specified as + [1, 0, 0, 1] then the binary accuracy would be 1/2 or .5. + This metric creates two local variables, `total` and `count` that are used to compute the frequency with which `y_pred` matches `y_true`. This frequency is ultimately returned as `binary accuracy`: an idempotent operation that simply @@ -589,6 +708,21 @@ class BinaryAccuracy(MeanMetricWrapper): If `sample_weight` is `None`, weights default to 1. Use `sample_weight` of 0 to mask values. + + Usage: + + ```python + m = tf.metrics.BinaryAccuracy() + m.update_state([1, 1, 0, 0], [0.98, 1, 0, 0.6]) + print('Final result: ', m.result().numpy()) # Final result: 0.75 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile('sgd', loss='mse', metrics=[tf.metrics.BinaryAccuracy()]) + ``` """ def __init__(self, name='binary_accuracy', dtype=None, threshold=0.5): @@ -603,17 +737,48 @@ class BinaryAccuracy(MeanMetricWrapper): super(BinaryAccuracy, self).__init__( binary_accuracy, name, dtype=dtype, threshold=threshold) + @classmethod + def from_config(cls, config): + if 'fn' in config: + config.pop('fn') + return super(BinaryAccuracy, cls).from_config(config) + +@tf_export( + 'metrics.CategoricalAccuracy', 'keras.metrics.CategoricalAccuracy') class CategoricalAccuracy(MeanMetricWrapper): """Calculates how often predictions matches labels. + For example, if `y_true` is [[0, 0, 1], [0, 1, 0]] and `y_pred` is + [[0.1, 0.9, 0.8], [0.05, 0.95, 0]] then the categorical accuracy is 1/2 or .5. + If the weights were specified as [0.7, 0.3] then the categorical accuracy + would be .3. + This metric creates two local variables, `total` and `count` that are used to compute the frequency with which `y_pred` matches `y_true`. This frequency is ultimately returned as `categorical accuracy`: an idempotent operation that simply divides `total` by `count`. + `y_pred` and `y_true` should be passed in as vectors of probabilities, rather + than as labels. If necessary, use `tf.one_hot` to expand `y_true` as a vector. + If `sample_weight` is `None`, weights default to 1. Use `sample_weight` of 0 to mask values. + + Usage: + + ```python + m = tf.metrics.CategoricalAccuracy() + m.update_state([[0, 0, 1], [0, 1, 0]], [[0.1, 0.9, 0.8], [0.05, 0.95, 0]]) + print('Final result: ', m.result().numpy()) # Final result: 0.5 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile('sgd', loss='mse', metrics=[tf.metrics.CategoricalAccuracy()]) + ``` """ def __init__(self, name='categorical_accuracy', dtype=None): @@ -626,10 +791,24 @@ class CategoricalAccuracy(MeanMetricWrapper): super(CategoricalAccuracy, self).__init__( categorical_accuracy, name, dtype=dtype) + @classmethod + def from_config(cls, config): + if 'fn' in config: + config.pop('fn') + return super(CategoricalAccuracy, cls).from_config(config) + +@tf_export( + 'metrics.SparseCategoricalAccuracy', + 'keras.metrics.SparseCategoricalAccuracy') class SparseCategoricalAccuracy(MeanMetricWrapper): """Calculates how often predictions matches integer labels. + For example, if `y_true` is [[2], [1]] and `y_pred` is + [[0.1, 0.9, 0.8], [0.05, 0.95, 0]] then the categorical accuracy is 1/2 or .5. + If the weights were specified as [0.7, 0.3] then the categorical accuracy + would be .3. + This metric creates two local variables, `total` and `count` that are used to compute the frequency with which `y_pred` matches `y_true`. This frequency is ultimately returned as `sparse categorical accuracy`: an idempotent operation @@ -637,12 +816,710 @@ class SparseCategoricalAccuracy(MeanMetricWrapper): If `sample_weight` is `None`, weights default to 1. Use `sample_weight` of 0 to mask values. + + Usage: + + ```python + m = tf.metrics.SparseCategoricalAccuracy() + m.update_state([[2], [1]], [[0.1, 0.9, 0.8], [0.05, 0.95, 0]]) + print('Final result: ', m.result().numpy()) # Final result: 0.5 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile( + 'sgd', + loss='mse', + metrics=[tf.metrics.SparseCategoricalAccuracy()]) + ``` """ def __init__(self, name='sparse_categorical_accuracy', dtype=None): super(SparseCategoricalAccuracy, self).__init__( sparse_categorical_accuracy, name, dtype=dtype) + @classmethod + def from_config(cls, config): + if 'fn' in config: + config.pop('fn') + return super(SparseCategoricalAccuracy, cls).from_config(config) + + +class _ConfusionMatrixConditionCount(Metric): + """Calculates the number of the given confusion matrix condition.""" + + def __init__(self, + confusion_matrix_cond, + thresholds=None, + name=None, + dtype=None): + """Creates a `_ConfusionMatrixConditionCount` instance. + + Args: + confusion_matrix_cond: One of `_ConfusionMatrix` conditions. + thresholds: (Optional) Defaults to 0.5. A float value or a python + list/tuple of float threshold values in [0, 1]. A threshold is compared + with prediction values to determine the truth value of predictions + (i.e., above the threshold is `true`, below is `false`). One metric + value is generated for each threshold value. + name: (Optional) string name of the metric instance. + dtype: (Optional) data type of the metric result. + """ + super(_ConfusionMatrixConditionCount, self).__init__(name=name, dtype=dtype) + self._confusion_matrix_cond = confusion_matrix_cond + self.thresholds = 0.5 if thresholds is None else thresholds + thresholds = to_list(thresholds) + _assert_thresholds_range(thresholds) + self.accumulator = self.add_weight( + 'accumulator', + shape=(len(thresholds),), + initializer=init_ops.zeros_initializer) + + def update_state(self, y_true, y_pred, sample_weight=None): + """Accumulates the given confusion matrix condition statistics. + + Args: + y_true: The ground truth values. + y_pred: The predicted values. + sample_weight: Optional weighting of each example. Defaults to 1. Can be a + `Tensor` whose rank is either 0, or the same rank as `y_true`, and must + be broadcastable to `y_true`. + + Returns: + Update op. + """ + return _update_confusion_matrix_variables({ + self._confusion_matrix_cond: self.accumulator + }, y_true, y_pred, self.thresholds, sample_weight) + + def result(self): + if isinstance(self.thresholds, (list, tuple)): + result = self.accumulator + else: + result = self.accumulator[0] + return ops.convert_to_tensor(result) + + def reset_states(self): + num_thresholds = len(to_list(self.thresholds)) + for v in self.variables: + K.set_value(v, np.zeros((num_thresholds,))) + + +@tf_export('metrics.FalsePositives', 'keras.metrics.FalsePositives') +class FalsePositives(_ConfusionMatrixConditionCount): + """Calculates the number of false positives. + + For example, if `y_true` is [0, 1, 0, 0] and `y_pred` is [0, 0, 1, 1] + then the false positives value is 2. If the weights were specified as + [0, 0, 1, 0] then the false positives value would be 1. + + If `sample_weight` is given, calculates the sum of the weights of + false positives. This metric creates one local variable, `accumulator` + that is used to keep track of the number of false positives. + + If `sample_weight` is `None`, weights default to 1. + Use `sample_weight` of 0 to mask values. + + Usage: + + ```python + m = tf.metrics.FalsePositives() + m.update_state([0, 1, 0, 0], [0, 0, 1, 1]) + print('Final result: ', m.result().numpy()) # Final result: 2 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile('sgd', loss='mse', metrics=[tf.metrics.FalsePositives()]) + ``` + """ + + def __init__(self, thresholds=None, name=None, dtype=None): + """Creates a `FalsePositives` instance. + + Args: + thresholds: (Optional) Defaults to 0.5. A float value or a python + list/tuple of float threshold values in [0, 1]. A threshold is compared + with prediction values to determine the truth value of predictions + (i.e., above the threshold is `true`, below is `false`). One metric + value is generated for each threshold value. + name: (Optional) string name of the metric instance. + dtype: (Optional) data type of the metric result. + """ + super(FalsePositives, self).__init__( + confusion_matrix_cond=_ConfusionMatrix.FALSE_POSITIVES, + thresholds=thresholds, + name=name, + dtype=dtype) + + +@tf_export('metrics.FalseNegatives', 'keras.metrics.FalseNegatives') +class FalseNegatives(_ConfusionMatrixConditionCount): + """Calculates the number of false negatives. + + For example, if `y_true` is [0, 1, 1, 1] and `y_pred` is [0, 1, 0, 0] + then the false negatives value is 2. If the weights were specified as + [0, 0, 1, 0] then the false negatives value would be 1. + + If `sample_weight` is given, calculates the sum of the weights of + false negatives. This metric creates one local variable, `accumulator` + that is used to keep track of the number of false negatives. + + If `sample_weight` is `None`, weights default to 1. + Use `sample_weight` of 0 to mask values. + + Usage: + + ```python + m = tf.metrics.FalseNegatives() + m.update_state([0, 1, 1, 1], [0, 1, 0, 0]) + print('Final result: ', m.result().numpy()) # Final result: 2 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile('sgd', loss='mse', metrics=[tf.metrics.FalseNegatives()]) + ``` + """ + + def __init__(self, thresholds=None, name=None, dtype=None): + """Creates a `FalseNegatives` instance. + + Args: + thresholds: (Optional) Defaults to 0.5. A float value or a python + list/tuple of float threshold values in [0, 1]. A threshold is compared + with prediction values to determine the truth value of predictions + (i.e., above the threshold is `true`, below is `false`). One metric + value is generated for each threshold value. + name: (Optional) string name of the metric instance. + dtype: (Optional) data type of the metric result. + """ + super(FalseNegatives, self).__init__( + confusion_matrix_cond=_ConfusionMatrix.FALSE_NEGATIVES, + thresholds=thresholds, + name=name, + dtype=dtype) + + +@tf_export('metrics.TrueNegatives', 'keras.metrics.TrueNegatives') +class TrueNegatives(_ConfusionMatrixConditionCount): + """Calculates the number of true negatives. + + For example, if `y_true` is [0, 1, 0, 0] and `y_pred` is [1, 1, 0, 0] + then the true negatives value is 2. If the weights were specified as + [0, 0, 1, 0] then the true negatives value would be 1. + + If `sample_weight` is given, calculates the sum of the weights of + true negatives. This metric creates one local variable, `accumulator` + that is used to keep track of the number of true negatives. + + If `sample_weight` is `None`, weights default to 1. + Use `sample_weight` of 0 to mask values. + + Usage: + + ```python + m = tf.metrics.TrueNegatives() + m.update_state([0, 1, 0, 0], [1, 1, 0, 0]) + print('Final result: ', m.result().numpy()) # Final result: 2 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile('sgd', loss='mse', metrics=[tf.metrics.TrueNegatives()]) + ``` + """ + + def __init__(self, thresholds=None, name=None, dtype=None): + """Creates a `TrueNegatives` instance. + + Args: + thresholds: (Optional) Defaults to 0.5. A float value or a python + list/tuple of float threshold values in [0, 1]. A threshold is compared + with prediction values to determine the truth value of predictions + (i.e., above the threshold is `true`, below is `false`). One metric + value is generated for each threshold value. + name: (Optional) string name of the metric instance. + dtype: (Optional) data type of the metric result. + """ + super(TrueNegatives, self).__init__( + confusion_matrix_cond=_ConfusionMatrix.TRUE_NEGATIVES, + thresholds=thresholds, + name=name, + dtype=dtype) + + +@tf_export('metrics.TruePositives', 'keras.metrics.TruePositives') +class TruePositives(_ConfusionMatrixConditionCount): + """Calculates the number of true positives. + + For example, if `y_true` is [0, 1, 1, 1] and `y_pred` is [1, 0, 1, 1] + then the true positives value is 2. If the weights were specified as + [0, 0, 1, 0] then the true positives value would be 1. + + If `sample_weight` is given, calculates the sum of the weights of + true positives. This metric creates one local variable, `true_positives` + that is used to keep track of the number of true positives. + + If `sample_weight` is `None`, weights default to 1. + Use `sample_weight` of 0 to mask values. + + Usage: + + ```python + m = tf.metrics.TruePositives() + m.update_state([0, 1, 1, 1], [1, 0, 1, 1]) + print('Final result: ', m.result().numpy()) # Final result: 2 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile('sgd', loss='mse', metrics=[tf.metrics.TruePositives()]) + ``` + """ + + def __init__(self, thresholds=None, name=None, dtype=None): + """Creates a `TruePositives` instance. + + Args: + thresholds: (Optional) Defaults to 0.5. A float value or a python + list/tuple of float threshold values in [0, 1]. A threshold is compared + with prediction values to determine the truth value of predictions + (i.e., above the threshold is `true`, below is `false`). One metric + value is generated for each threshold value. + name: (Optional) string name of the metric instance. + dtype: (Optional) data type of the metric result. + """ + super(TruePositives, self).__init__( + confusion_matrix_cond=_ConfusionMatrix.TRUE_POSITIVES, + thresholds=thresholds, + name=name, + dtype=dtype) + + +@tf_export('metrics.Precision', 'keras.metrics.Precision') +class Precision(Metric): + """Computes the precision of the predictions with respect to the labels. + + For example, if `y_true` is [0, 1, 1, 1] and `y_pred` is [1, 0, 1, 1] + then the precision value is 2/(2+1) ie. 0.66. If the weights were specified as + [0, 0, 1, 0] then the precision value would be 1. + + The metric creates two local variables, `true_positives` and `false_positives` + that are used to compute the precision. This value is ultimately returned as + `precision`, an idempotent operation that simply divides `true_positives` + by the sum of `true_positives` and `false_positives`. + + If `sample_weight` is `None`, weights default to 1. + Use `sample_weight` of 0 to mask values. + + Usage: + + ```python + m = tf.metrics.Precision() + m.update_state([0, 1, 1, 1], [1, 0, 1, 1]) + print('Final result: ', m.result().numpy()) # Final result: 0.66 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile('sgd', loss='mse', metrics=[tf.metrics.Precision()]) + ``` + """ + + def __init__(self, thresholds=None, name=None, dtype=None): + """Creates a `Precision` instance. + + Args: + thresholds: (Optional) Defaults to 0.5. A float value or a python + list/tuple of float threshold values in [0, 1]. A threshold is compared + with prediction values to determine the truth value of predictions + (i.e., above the threshold is `true`, below is `false`). One metric + value is generated for each threshold value. + name: (Optional) string name of the metric instance. + dtype: (Optional) data type of the metric result. + """ + super(Precision, self).__init__(name=name, dtype=dtype) + self.thresholds = 0.5 if thresholds is None else thresholds + thresholds = to_list(thresholds) + _assert_thresholds_range(thresholds) + self.tp = self.add_weight( + 'true_positives', + shape=(len(thresholds),), + initializer=init_ops.zeros_initializer) + self.fp = self.add_weight( + 'false_positives', + shape=(len(thresholds),), + initializer=init_ops.zeros_initializer) + + def update_state(self, y_true, y_pred, sample_weight=None): + """Accumulates true positive and false positive statistics. + + Args: + y_true: The ground truth values. + y_pred: The predicted values. + sample_weight: Optional weighting of each example. Defaults to 1. Can be a + `Tensor` whose rank is either 0, or the same rank as `y_true`, and must + be broadcastable to `y_true`. + + Returns: + Update op. + """ + return _update_confusion_matrix_variables({ + _ConfusionMatrix.TRUE_POSITIVES: self.tp, + _ConfusionMatrix.FALSE_POSITIVES: self.fp + }, y_true, y_pred, self.thresholds, sample_weight) + + def result(self): + result = math_ops.div_no_nan(self.tp, self.tp + self.fp) + return result if isinstance(self.thresholds, (list, tuple)) else result[0] + + def reset_states(self): + num_thresholds = len(to_list(self.thresholds)) + for v in self.variables: + K.set_value(v, np.zeros((num_thresholds,))) + + +@tf_export('metrics.Recall', 'keras.metrics.Recall') +class Recall(Metric): + """Computes the recall of the predictions with respect to the labels. + + For example, if `y_true` is [0, 1, 1, 1] and `y_pred` is [1, 0, 1, 1] + then the recall value is 2/(2+1) ie. 0.66. If the weights were specified as + [0, 0, 1, 0] then the recall value would be 1. + + This metric creates two local variables, `true_positives` and + `false_negatives`, that are used to compute the recall. This value is + ultimately returned as `recall`, an idempotent operation that simply divides + `true_positives` by the sum of `true_positives` and `false_negatives`. + + If `sample_weight` is `None`, weights default to 1. + Use `sample_weight` of 0 to mask values. + + Usage: + + ```python + m = tf.metrics.Recall() + m.update_state([0, 1, 1, 1], [1, 0, 1, 1]) + print('Final result: ', m.result().numpy()) # Final result: 0.66 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile('sgd', loss='mse', metrics=[tf.metrics.Recall()]) + ``` + """ + + def __init__(self, thresholds=None, name=None, dtype=None): + """Creates a `Recall` instance. + + Args: + thresholds: (Optional) Defaults to 0.5. A float value or a python + list/tuple of float threshold values in [0, 1]. A threshold is compared + with prediction values to determine the truth value of predictions + (i.e., above the threshold is `true`, below is `false`). One metric + value is generated for each threshold value. + name: (Optional) string name of the metric instance. + dtype: (Optional) data type of the metric result. + """ + super(Recall, self).__init__(name=name, dtype=dtype) + self.thresholds = 0.5 if thresholds is None else thresholds + thresholds = to_list(thresholds) + _assert_thresholds_range(thresholds) + self.tp = self.add_weight( + 'true_positives', + shape=(len(thresholds),), + initializer=init_ops.zeros_initializer) + self.fn = self.add_weight( + 'false_negatives', + shape=(len(thresholds),), + initializer=init_ops.zeros_initializer) + + def update_state(self, y_true, y_pred, sample_weight=None): + """Accumulates true positive and false negative statistics. + + Args: + y_true: The ground truth values. + y_pred: The predicted values. + sample_weight: Optional weighting of each example. Defaults to 1. Can be a + `Tensor` whose rank is either 0, or the same rank as `y_true`, and must + be broadcastable to `y_true`. + + Returns: + Update op. + """ + return _update_confusion_matrix_variables({ + _ConfusionMatrix.TRUE_POSITIVES: self.tp, + _ConfusionMatrix.FALSE_NEGATIVES: self.fn + }, y_true, y_pred, self.thresholds, sample_weight) + + def result(self): + result = math_ops.div_no_nan(self.tp, self.tp + self.fn) + return result if isinstance(self.thresholds, (list, tuple)) else result[0] + + def reset_states(self): + num_thresholds = len(to_list(self.thresholds)) + for v in self.variables: + K.set_value(v, np.zeros((num_thresholds,))) + + +@six.add_metaclass(abc.ABCMeta) +class SensitivitySpecificityBase(Metric): + """Abstract base class for computing sensitivity and specificity. + + For additional information about specificity and sensitivity, see the + following: https://en.wikipedia.org/wiki/Sensitivity_and_specificity + """ + + def __init__(self, value, num_thresholds=200, name=None, dtype=None): + super(SensitivitySpecificityBase, self).__init__(name=name, dtype=dtype) + if num_thresholds <= 0: + raise ValueError('`num_thresholds` must be > 0.') + self.value = value + self.tp = self.add_weight( + 'true_positives', + shape=(num_thresholds,), + initializer=init_ops.zeros_initializer) + self.tn = self.add_weight( + 'true_negatives', + shape=(num_thresholds,), + initializer=init_ops.zeros_initializer) + self.fp = self.add_weight( + 'false_positives', + shape=(num_thresholds,), + initializer=init_ops.zeros_initializer) + self.fn = self.add_weight( + 'false_negatives', + shape=(num_thresholds,), + initializer=init_ops.zeros_initializer) + + # Compute `num_thresholds` thresholds in [0, 1] + if num_thresholds == 1: + self.thresholds = [0.5] + else: + thresholds = [(i + 1) * 1.0 / (num_thresholds - 1) + for i in range(num_thresholds - 2)] + self.thresholds = [0.0] + thresholds + [1.0] + + def update_state(self, y_true, y_pred, sample_weight=None): + """Accumulates confusion matrix statistics. + + Args: + y_true: The ground truth values. + y_pred: The predicted values. + sample_weight: Optional weighting of each example. Defaults to 1. Can be a + `Tensor` whose rank is either 0, or the same rank as `y_true`, and must + be broadcastable to `y_true`. + + Returns: + Update op. + """ + return _update_confusion_matrix_variables({ + _ConfusionMatrix.TRUE_POSITIVES: self.tp, + _ConfusionMatrix.TRUE_NEGATIVES: self.tn, + _ConfusionMatrix.FALSE_POSITIVES: self.fp, + _ConfusionMatrix.FALSE_NEGATIVES: self.fn, + }, y_true, y_pred, self.thresholds, sample_weight) + + def reset_states(self): + num_thresholds = len(self.thresholds) + for v in self.variables: + K.set_value(v, np.zeros((num_thresholds,))) + + +class SensitivityAtSpecificity(SensitivitySpecificityBase): + """Computes the sensitivity at a given specificity. + + `Sensitivity` measures the proportion of actual positives that are correctly + identified as such (tp / (tp + fn)). + `Specificity` measures the proportion of actual negatives that are correctly + identified as such (tn / (tn + fp)). + + This metric creates four local variables, `true_positives`, `true_negatives`, + `false_positives` and `false_negatives` that are used to compute the + sensitivity at the given specificity. The threshold for the given specificity + value is computed and used to evaluate the corresponding sensitivity. + + If `sample_weight` is `None`, weights default to 1. + Use `sample_weight` of 0 to mask values. + + For additional information about specificity and sensitivity, see the + following: https://en.wikipedia.org/wiki/Sensitivity_and_specificity + + Usage: + + ```python + m = tf.metrics.SensitivityAtSpecificity(0.4, num_thresholds=1) + m.update_state([0, 0, 1, 1], [0, 0.5, 0.3, 0.9]) + print('Final result: ', m.result().numpy()) # Final result: 0.5 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile( + 'sgd', + loss='mse', + metrics=[tf.metrics.SensitivityAtSpecificity()]) + ``` + """ + + def __init__(self, specificity, num_thresholds=200, name=None, dtype=None): + """Creates a `SensitivityAtSpecificity` instance. + + Args: + specificity: A scalar value in range `[0, 1]`. + num_thresholds: (Optional) Defaults to 200. The number of thresholds to + use for matching the given specificity. + name: (Optional) string name of the metric instance. + dtype: (Optional) data type of the metric result. + """ + if specificity < 0 or specificity > 1: + raise ValueError('`specificity` must be in the range [0, 1].') + super(SensitivityAtSpecificity, self).__init__( + specificity, num_thresholds=num_thresholds, name=name, dtype=dtype) + + def result(self): + # Calculate specificities at all the thresholds. + specificities = math_ops.div_no_nan(self.tn, self.tn + self.fp) + + # Find the index of the threshold where the specificity is closest to the + # given specificity. + min_index = math_ops.argmin( + math_ops.abs(specificities - self.value), axis=0) + min_index = math_ops.cast(min_index, dtypes.int32) + + # Compute sensitivity at that index. + return math_ops.div_no_nan(self.tp[min_index], + self.tp[min_index] + self.fn[min_index]) + + +class SpecificityAtSensitivity(SensitivitySpecificityBase): + """Computes the specificity at a given sensitivity. + + `Sensitivity` measures the proportion of actual positives that are correctly + identified as such (tp / (tp + fn)). + `Specificity` measures the proportion of actual negatives that are correctly + identified as such (tn / (tn + fp)). + + This metric creates four local variables, `true_positives`, `true_negatives`, + `false_positives` and `false_negatives` that are used to compute the + specificity at the given sensitivity. The threshold for the given sensitivity + value is computed and used to evaluate the corresponding specificity. + + If `sample_weight` is `None`, weights default to 1. + Use `sample_weight` of 0 to mask values. + + For additional information about specificity and sensitivity, see the + following: https://en.wikipedia.org/wiki/Sensitivity_and_specificity + + Usage: + + ```python + m = tf.metrics.SpecificityAtSensitivity(0.8, num_thresholds=1) + m.update_state([0, 0, 1, 1], [0, 0.5, 0.3, 0.9]) + print('Final result: ', m.result().numpy()) # Final result: 1.0 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile( + 'sgd', + loss='mse', + metrics=[tf.metrics.SpecificityAtSensitivity()]) + ``` + """ + + def __init__(self, sensitivity, num_thresholds=200, name=None, dtype=None): + """Creates a `SpecificityAtSensitivity` instance. + + Args: + sensitivity: A scalar value in range `[0, 1]`. + num_thresholds: (Optional) Defaults to 200. The number of thresholds to + use for matching the given specificity. + name: (Optional) string name of the metric instance. + dtype: (Optional) data type of the metric result. + """ + if sensitivity < 0 or sensitivity > 1: + raise ValueError('`sensitivity` must be in the range [0, 1].') + super(SpecificityAtSensitivity, self).__init__( + sensitivity, num_thresholds=num_thresholds, name=name, dtype=dtype) + + def result(self): + # Calculate sensitivities at all the thresholds. + sensitivities = math_ops.div_no_nan(self.tp, self.tp + self.fn) + + # Find the index of the threshold where the sensitivity is closest to the + # given specificity. + min_index = math_ops.argmin( + math_ops.abs(sensitivities - self.value), axis=0) + min_index = math_ops.cast(min_index, dtypes.int32) + + # Compute specificity at that index. + return math_ops.div_no_nan(self.tn[min_index], + self.tn[min_index] + self.fp[min_index]) + + +class CosineProximity(MeanMetricWrapper): + """Computes the cosine distance between the labels and predictions. + + For example, if `y_true` is [0, 1, 1], and `y_pred` is [1, 0, 1], the cosine + proximity is -0.5. + + This metric keeps the average cosine distance between `predictions` and + `labels` over a stream of data. + + Usage: + ```python + m = tf.metrics.CosineProximity() + m.update_state([0, 1, 1], [1, 0, 1]) + print('Final result: ', m.result().numpy()) # Final result: -0.5 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile( + 'sgd', + loss='mse', + metrics=[tf.metrics.CosineProximity()]) + ``` + """ + + def __init__(self, name='cosine_proximity', dtype=None): + super(CosineProximity, self).__init__(cosine, name, dtype=dtype) + + @classmethod + def from_config(cls, config): + if 'fn' in config: + config.pop('fn') + return super(CosineProximity, cls).from_config(config) + + +def accuracy(y_true, y_pred): + y_pred.get_shape().assert_is_compatible_with(y_true.get_shape()) + if y_true.dtype != y_pred.dtype: + y_pred = math_ops.cast(y_pred, y_true.dtype) + return math_ops.cast(math_ops.equal(y_true, y_pred), K.floatx()) + @tf_export('keras.metrics.binary_accuracy') def binary_accuracy(y_true, y_pred, threshold=0.5): diff --git a/tensorflow/python/keras/metrics_test.py b/tensorflow/python/keras/metrics_test.py index 5f5565d4d5..92398acd8e 100644 --- a/tensorflow/python/keras/metrics_test.py +++ b/tensorflow/python/keras/metrics_test.py @@ -19,22 +19,25 @@ from __future__ import division from __future__ import print_function import os +from absl.testing import parameterized import numpy as np from tensorflow.python.eager import context +from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import test_util from tensorflow.python.keras import backend as K from tensorflow.python.keras import layers from tensorflow.python.keras import metrics -from tensorflow.python.keras.engine.training import Model +from tensorflow.python.keras.models import Sequential from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops -from tensorflow.python.ops import state_ops +from tensorflow.python.ops import random_ops from tensorflow.python.ops import variables from tensorflow.python.platform import test from tensorflow.python.training.checkpointable import util as checkpointable_utils +from tensorflow.python.training.rmsprop import RMSPropOptimizer class KerasMetricsTest(test.TestCase): @@ -47,7 +50,7 @@ class KerasMetricsTest(test.TestCase): output = metric(y_a, y_b) self.assertEqual(K.eval(output).shape, (6,)) - def test_sparse_categorical_accuracy(self): + def test_sparse_categorical_accuracy_int(self): with self.cached_session(): metric = metrics.sparse_categorical_accuracy y_true = K.variable(np.random.randint(0, 7, (6,))) @@ -128,116 +131,6 @@ class KerasMetricsTest(test.TestCase): result = K.eval(metrics.top_k_categorical_accuracy(y_true, y_pred, k=1)) self.assertEqual(result, 0.) - def test_stateful_metrics(self): - with self.cached_session(): - np.random.seed(1334) - - class BinaryTruePositives(layers.Layer): - """Stateful Metric to count the total true positives over all batches. - - Assumes predictions and targets of shape `(samples, 1)`. - - Arguments: - threshold: Float, lower limit on prediction value that counts as a - positive class prediction. - name: String, name for the metric. - """ - - def __init__(self, name='true_positives', **kwargs): - super(BinaryTruePositives, self).__init__(name=name, **kwargs) - self.true_positives = K.variable(value=0, dtype='int32') - self.stateful = True - - def reset_states(self): - K.set_value(self.true_positives, 0) - - def __call__(self, y_true, y_pred): - """Computes the number of true positives in a batch. - - Args: - y_true: Tensor, batch_wise labels - y_pred: Tensor, batch_wise predictions - - Returns: - The total number of true positives seen this epoch at the - completion of the batch. - """ - y_true = math_ops.cast(y_true, 'int32') - y_pred = math_ops.cast(math_ops.round(y_pred), 'int32') - correct_preds = math_ops.cast(math_ops.equal(y_pred, y_true), 'int32') - true_pos = math_ops.cast( - math_ops.reduce_sum(correct_preds * y_true), 'int32') - current_true_pos = self.true_positives * 1 - self.add_update( - state_ops.assign_add(self.true_positives, true_pos), - inputs=[y_true, y_pred]) - return current_true_pos + true_pos - - metric_fn = BinaryTruePositives() - config = metrics.serialize(metric_fn) - metric_fn = metrics.deserialize( - config, custom_objects={'BinaryTruePositives': BinaryTruePositives}) - - # Test on simple model - inputs = layers.Input(shape=(2,)) - outputs = layers.Dense(1, activation='sigmoid')(inputs) - model = Model(inputs, outputs) - model.compile(optimizer='sgd', - loss='binary_crossentropy', - metrics=['acc', metric_fn]) - - # Test fit, evaluate - samples = 100 - x = np.random.random((samples, 2)) - y = np.random.randint(2, size=(samples, 1)) - val_samples = 10 - val_x = np.random.random((val_samples, 2)) - val_y = np.random.randint(2, size=(val_samples, 1)) - - history = model.fit(x, y, - epochs=1, - batch_size=10, - validation_data=(val_x, val_y)) - outs = model.evaluate(x, y, batch_size=10) - preds = model.predict(x) - - def ref_true_pos(y_true, y_pred): - return np.sum(np.logical_and(y_pred > 0.5, y_true == 1)) - - # Test correctness (e.g. updates should have been run) - self.assertAllClose(outs[2], ref_true_pos(y, preds), atol=1e-5) - - # Test correctness of the validation metric computation - val_preds = model.predict(val_x) - val_outs = model.evaluate(val_x, val_y, batch_size=10) - self.assertAllClose( - val_outs[2], ref_true_pos(val_y, val_preds), atol=1e-5) - self.assertAllClose( - val_outs[2], history.history['val_true_positives'][-1], atol=1e-5) - - # Test with generators - gen = [(np.array([x0]), np.array([y0])) for x0, y0 in zip(x, y)] - val_gen = [(np.array([x0]), np.array([y0])) - for x0, y0 in zip(val_x, val_y)] - history = model.fit_generator(iter(gen), - epochs=1, - steps_per_epoch=samples, - validation_data=iter(val_gen), - validation_steps=val_samples) - outs = model.evaluate_generator(iter(gen), steps=samples) - preds = model.predict_generator(iter(gen), steps=samples) - - # Test correctness of the metric results - self.assertAllClose(outs[2], ref_true_pos(y, preds), atol=1e-5) - - # Test correctness of the validation metric computation - val_preds = model.predict_generator(iter(val_gen), steps=val_samples) - val_outs = model.evaluate_generator(iter(val_gen), steps=val_samples) - self.assertAllClose( - val_outs[2], ref_true_pos(val_y, val_preds), atol=1e-5) - self.assertAllClose( - val_outs[2], history.history['val_true_positives'][-1], atol=1e-5) - @test_util.run_in_graph_and_eager_modes(assert_no_eager_garbage=True) def test_mean(self): m = metrics.Mean(name='my_mean') @@ -319,19 +212,19 @@ class KerasMetricsTest(test.TestCase): m = metrics.Mean() v = array_ops.placeholder(dtypes.float32) w = array_ops.placeholder(dtypes.float32) - sess.run(variables.variables_initializer(m.variables)) + self.evaluate(variables.variables_initializer(m.variables)) # check __call__() result_t = m(v, sample_weight=w) result = sess.run(result_t, feed_dict=({v: 100, w: 0.5})) - self.assertEqual(sess.run(m.total), 50) - self.assertEqual(sess.run(m.count), 0.5) + self.assertEqual(self.evaluate(m.total), 50) + self.assertEqual(self.evaluate(m.count), 0.5) self.assertEqual(result, 50 / 0.5) # check update_state() and result() result = sess.run(result_t, feed_dict=({v: [1, 5], w: [1, 0.2]})) - self.assertAlmostEqual(sess.run(m.total), 52, 2) # 50 + 1 + 5 * 0.2 - self.assertAlmostEqual(sess.run(m.count), 1.7, 2) # 0.5 + 1.2 + self.assertAlmostEqual(self.evaluate(m.total), 52, 2) # 50 + 1 + 5 * 0.2 + self.assertAlmostEqual(self.evaluate(m.count), 1.7, 2) # 0.5 + 1.2 self.assertAlmostEqual(result, 52 / 1.7, 2) @test_util.run_in_graph_and_eager_modes @@ -365,6 +258,28 @@ class KerasMetricsTest(test.TestCase): self.assertEqual(200., self.evaluate(restore_mean.result())) self.assertEqual(3, self.evaluate(restore_mean.count)) + @test_util.run_in_graph_and_eager_modes + def test_accuracy(self): + acc_obj = metrics.Accuracy(name='my acc') + + # check config + self.assertEqual(acc_obj.name, 'my acc') + self.assertTrue(acc_obj.stateful) + self.assertEqual(len(acc_obj.variables), 2) + self.assertEqual(acc_obj.dtype, dtypes.float32) + self.evaluate(variables.variables_initializer(acc_obj.variables)) + + # verify that correct value is returned + update_op = acc_obj.update_state([[1], [2], [3], [4]], [[1], [2], [3], [4]]) + self.evaluate(update_op) + result = self.evaluate(acc_obj.result()) + self.assertEqual(result, 1) # 2/2 + + # check with sample_weight + result_t = acc_obj([[2], [1]], [[2], [0]], sample_weight=[[0.5], [0.2]]) + result = self.evaluate(result_t) + self.assertAlmostEqual(result, 0.96, 2) # 4.5/4.7 + @test_util.run_in_graph_and_eager_modes def test_binary_accuracy(self): acc_obj = metrics.BinaryAccuracy(name='my acc') @@ -398,11 +313,6 @@ class KerasMetricsTest(test.TestCase): result = self.evaluate(result_t) self.assertAlmostEqual(result, 0.67, 2) # 4.5/6.7 - # check incompatible shapes - with self.assertRaisesRegexp(ValueError, - r'Shapes \(1,\) and \(2,\) are incompatible'): - acc_obj.update_state([1, 1], [1]) - @test_util.run_in_graph_and_eager_modes def test_binary_accuracy_threshold(self): acc_obj = metrics.BinaryAccuracy(threshold=0.7) @@ -436,47 +346,830 @@ class KerasMetricsTest(test.TestCase): self.assertAlmostEqual(result, 0.93, 2) # 2.5/2.7 @test_util.run_in_graph_and_eager_modes - def test_invalid_result(self): + def test_sparse_categorical_accuracy(self): + acc_obj = metrics.SparseCategoricalAccuracy(name='my acc') + + # check config + self.assertEqual(acc_obj.name, 'my acc') + self.assertTrue(acc_obj.stateful) + self.assertEqual(len(acc_obj.variables), 2) + self.assertEqual(acc_obj.dtype, dtypes.float32) + self.evaluate(variables.variables_initializer(acc_obj.variables)) - class InvalidResult(metrics.Metric): + # verify that correct value is returned + update_op = acc_obj.update_state([[2], [1]], + [[0.1, 0.1, 0.8], [0.05, 0.95, 0]]) + self.evaluate(update_op) + result = self.evaluate(acc_obj.result()) + self.assertEqual(result, 1) # 2/2 - def __init__(self, name='invalid-result', dtype=dtypes.float64): - super(InvalidResult, self).__init__(name=name, dtype=dtype) + # check with sample_weight + result_t = acc_obj([[2], [1]], [[0.1, 0.1, 0.8], [0.05, 0, 0.95]], + [[0.5], [0.2]]) + result = self.evaluate(result_t) + self.assertAlmostEqual(result, 0.93, 2) # 2.5/2.7 - def update_state(self, *args, **kwargs): - pass - def result(self): - return 1 +def _get_simple_sequential_model(compile_metrics): + model = Sequential() + model.add( + layers.Dense( + 3, activation='relu', input_dim=4, kernel_initializer='ones')) + model.add(layers.Dense(1, activation='sigmoid', kernel_initializer='ones')) + model.compile( + loss='mae', + metrics=compile_metrics, + optimizer=RMSPropOptimizer(learning_rate=0.001)) + return model - invalid_result_obj = InvalidResult() - with self.assertRaisesRegexp( - TypeError, - 'Metric invalid-result\'s result must be a Tensor or Operation, given:' - ): - invalid_result_obj.result() - @test_util.run_in_graph_and_eager_modes - def test_invalid_update(self): +@test_util.run_all_in_graph_and_eager_modes +class FalsePositivesTest(test.TestCase): - class InvalidUpdate(metrics.Metric): + def test_config(self): + fp_obj = metrics.FalsePositives(name='my_fp', thresholds=[0.4, 0.9]) + self.assertEqual(fp_obj.name, 'my_fp') + self.assertEqual(len(fp_obj.variables), 1) + self.assertEqual(fp_obj.thresholds, [0.4, 0.9]) - def __init__(self, name='invalid-update', dtype=dtypes.float64): - super(InvalidUpdate, self).__init__(name=name, dtype=dtype) + def test_unweighted(self): + fp_obj = metrics.FalsePositives() + self.evaluate(variables.variables_initializer(fp_obj.variables)) - def update_state(self, *args, **kwargs): - return [1] + y_true = constant_op.constant(((0, 1, 0, 1, 0), (0, 0, 1, 1, 1), + (1, 1, 1, 1, 0), (0, 0, 0, 0, 1))) + y_pred = constant_op.constant(((0, 0, 1, 1, 0), (1, 1, 1, 1, 1), + (0, 1, 0, 1, 0), (1, 1, 1, 1, 1))) - def result(self): - pass + update_op = fp_obj.update_state(y_true, y_pred) + self.evaluate(update_op) + result = fp_obj.result() + self.assertAllClose(7., result) + + def test_weighted(self): + fp_obj = metrics.FalsePositives() + self.evaluate(variables.variables_initializer(fp_obj.variables)) + y_true = constant_op.constant(((0, 1, 0, 1, 0), (0, 0, 1, 1, 1), + (1, 1, 1, 1, 0), (0, 0, 0, 0, 1))) + y_pred = constant_op.constant(((0, 0, 1, 1, 0), (1, 1, 1, 1, 1), + (0, 1, 0, 1, 0), (1, 1, 1, 1, 1))) + sample_weight = constant_op.constant((1., 1.5, 2., 2.5)) + result = fp_obj(y_true, y_pred, sample_weight=sample_weight) + self.assertAllClose(14., self.evaluate(result)) + + def test_unweighted_with_thresholds(self): + fp_obj = metrics.FalsePositives(thresholds=[0.15, 0.5, 0.85]) + self.evaluate(variables.variables_initializer(fp_obj.variables)) + + y_pred = constant_op.constant(((0.9, 0.2, 0.8, 0.1), (0.2, 0.9, 0.7, 0.6), + (0.1, 0.2, 0.4, 0.3), (0, 1, 0.7, 0.3))) + y_true = constant_op.constant(((0, 1, 1, 0), (1, 0, 0, 0), (0, 0, 0, 0), + (1, 1, 1, 1))) + + update_op = fp_obj.update_state(y_true, y_pred) + self.evaluate(update_op) + result = fp_obj.result() + self.assertAllClose([7., 4., 2.], result) - invalid_update_obj = InvalidUpdate() - with self.assertRaisesRegexp( - TypeError, - 'Metric invalid-update\'s update must be a Tensor or Operation, given:' - ): - invalid_update_obj.update_state() + def test_weighted_with_thresholds(self): + fp_obj = metrics.FalsePositives(thresholds=[0.15, 0.5, 0.85]) + self.evaluate(variables.variables_initializer(fp_obj.variables)) + + y_pred = constant_op.constant(((0.9, 0.2, 0.8, 0.1), (0.2, 0.9, 0.7, 0.6), + (0.1, 0.2, 0.4, 0.3), (0, 1, 0.7, 0.3))) + y_true = constant_op.constant(((0, 1, 1, 0), (1, 0, 0, 0), (0, 0, 0, 0), + (1, 1, 1, 1))) + sample_weight = ((1.0, 2.0, 3.0, 5.0), (7.0, 11.0, 13.0, 17.0), + (19.0, 23.0, 29.0, 31.0), (5.0, 15.0, 10.0, 0)) + result = fp_obj(y_true, y_pred, sample_weight=sample_weight) + self.assertAllClose([125., 42., 12.], self.evaluate(result)) + + def test_threshold_limit(self): + with self.assertRaisesRegexp( + ValueError, + r'Threshold values must be in \[0, 1\]. Invalid values: \[-1, 2\]'): + metrics.FalsePositives(thresholds=[-1, 0.5, 2]) + + def test_reset_states(self): + fp_obj = metrics.FalsePositives() + model = _get_simple_sequential_model([fp_obj]) + x = np.ones((100, 4)) + y = np.zeros((100, 1)) + model.evaluate(x, y) + self.assertEqual(self.evaluate(fp_obj.accumulator), 100.) + model.evaluate(x, y) + self.assertEqual(self.evaluate(fp_obj.accumulator), 100.) + + +@test_util.run_all_in_graph_and_eager_modes +class FalseNegativesTest(test.TestCase): + + def test_config(self): + fn_obj = metrics.FalseNegatives(name='my_fn', thresholds=[0.4, 0.9]) + self.assertEqual(fn_obj.name, 'my_fn') + self.assertEqual(len(fn_obj.variables), 1) + self.assertEqual(fn_obj.thresholds, [0.4, 0.9]) + + def test_unweighted(self): + fn_obj = metrics.FalseNegatives() + self.evaluate(variables.variables_initializer(fn_obj.variables)) + + y_true = constant_op.constant(((0, 1, 0, 1, 0), (0, 0, 1, 1, 1), + (1, 1, 1, 1, 0), (0, 0, 0, 0, 1))) + y_pred = constant_op.constant(((0, 0, 1, 1, 0), (1, 1, 1, 1, 1), + (0, 1, 0, 1, 0), (1, 1, 1, 1, 1))) + + update_op = fn_obj.update_state(y_true, y_pred) + self.evaluate(update_op) + result = fn_obj.result() + self.assertAllClose(3., result) + + def test_weighted(self): + fn_obj = metrics.FalseNegatives() + self.evaluate(variables.variables_initializer(fn_obj.variables)) + y_true = constant_op.constant(((0, 1, 0, 1, 0), (0, 0, 1, 1, 1), + (1, 1, 1, 1, 0), (0, 0, 0, 0, 1))) + y_pred = constant_op.constant(((0, 0, 1, 1, 0), (1, 1, 1, 1, 1), + (0, 1, 0, 1, 0), (1, 1, 1, 1, 1))) + sample_weight = constant_op.constant((1., 1.5, 2., 2.5)) + result = fn_obj(y_true, y_pred, sample_weight=sample_weight) + self.assertAllClose(5., self.evaluate(result)) + + def test_unweighted_with_thresholds(self): + fn_obj = metrics.FalseNegatives(thresholds=[0.15, 0.5, 0.85]) + self.evaluate(variables.variables_initializer(fn_obj.variables)) + + y_pred = constant_op.constant(((0.9, 0.2, 0.8, 0.1), (0.2, 0.9, 0.7, 0.6), + (0.1, 0.2, 0.4, 0.3), (0, 1, 0.7, 0.3))) + y_true = constant_op.constant(((0, 1, 1, 0), (1, 0, 0, 0), (0, 0, 0, 0), + (1, 1, 1, 1))) + + update_op = fn_obj.update_state(y_true, y_pred) + self.evaluate(update_op) + result = fn_obj.result() + self.assertAllClose([1., 4., 6.], result) + + def test_weighted_with_thresholds(self): + fn_obj = metrics.FalseNegatives(thresholds=[0.15, 0.5, 0.85]) + self.evaluate(variables.variables_initializer(fn_obj.variables)) + + y_pred = constant_op.constant(((0.9, 0.2, 0.8, 0.1), (0.2, 0.9, 0.7, 0.6), + (0.1, 0.2, 0.4, 0.3), (0, 1, 0.7, 0.3))) + y_true = constant_op.constant(((0, 1, 1, 0), (1, 0, 0, 0), (0, 0, 0, 0), + (1, 1, 1, 1))) + sample_weight = ((3.0,), (5.0,), (7.0,), (4.0,)) + + result = fn_obj(y_true, y_pred, sample_weight=sample_weight) + self.assertAllClose([4., 16., 23.], self.evaluate(result)) + + def test_reset_states(self): + fn_obj = metrics.FalseNegatives() + model = _get_simple_sequential_model([fn_obj]) + x = np.zeros((100, 4)) + y = np.ones((100, 1)) + model.evaluate(x, y) + self.assertEqual(self.evaluate(fn_obj.accumulator), 100.) + model.evaluate(x, y) + self.assertEqual(self.evaluate(fn_obj.accumulator), 100.) + + +@test_util.run_all_in_graph_and_eager_modes +class TrueNegativesTest(test.TestCase): + + def test_config(self): + tn_obj = metrics.TrueNegatives(name='my_tn', thresholds=[0.4, 0.9]) + self.assertEqual(tn_obj.name, 'my_tn') + self.assertEqual(len(tn_obj.variables), 1) + self.assertEqual(tn_obj.thresholds, [0.4, 0.9]) + + def test_unweighted(self): + tn_obj = metrics.TrueNegatives() + self.evaluate(variables.variables_initializer(tn_obj.variables)) + + y_true = constant_op.constant(((0, 1, 0, 1, 0), (0, 0, 1, 1, 1), + (1, 1, 1, 1, 0), (0, 0, 0, 0, 1))) + y_pred = constant_op.constant(((0, 0, 1, 1, 0), (1, 1, 1, 1, 1), + (0, 1, 0, 1, 0), (1, 1, 1, 1, 1))) + + update_op = tn_obj.update_state(y_true, y_pred) + self.evaluate(update_op) + result = tn_obj.result() + self.assertAllClose(3., result) + + def test_weighted(self): + tn_obj = metrics.TrueNegatives() + self.evaluate(variables.variables_initializer(tn_obj.variables)) + y_true = constant_op.constant(((0, 1, 0, 1, 0), (0, 0, 1, 1, 1), + (1, 1, 1, 1, 0), (0, 0, 0, 0, 1))) + y_pred = constant_op.constant(((0, 0, 1, 1, 0), (1, 1, 1, 1, 1), + (0, 1, 0, 1, 0), (1, 1, 1, 1, 1))) + sample_weight = constant_op.constant((1., 1.5, 2., 2.5)) + result = tn_obj(y_true, y_pred, sample_weight=sample_weight) + self.assertAllClose(4., self.evaluate(result)) + + def test_unweighted_with_thresholds(self): + tn_obj = metrics.TrueNegatives(thresholds=[0.15, 0.5, 0.85]) + self.evaluate(variables.variables_initializer(tn_obj.variables)) + + y_pred = constant_op.constant(((0.9, 0.2, 0.8, 0.1), (0.2, 0.9, 0.7, 0.6), + (0.1, 0.2, 0.4, 0.3), (0, 1, 0.7, 0.3))) + y_true = constant_op.constant(((0, 1, 1, 0), (1, 0, 0, 0), (0, 0, 0, 0), + (1, 1, 1, 1))) + + update_op = tn_obj.update_state(y_true, y_pred) + self.evaluate(update_op) + result = tn_obj.result() + self.assertAllClose([2., 5., 7.], result) + + def test_weighted_with_thresholds(self): + tn_obj = metrics.TrueNegatives(thresholds=[0.15, 0.5, 0.85]) + self.evaluate(variables.variables_initializer(tn_obj.variables)) + + y_pred = constant_op.constant(((0.9, 0.2, 0.8, 0.1), (0.2, 0.9, 0.7, 0.6), + (0.1, 0.2, 0.4, 0.3), (0, 1, 0.7, 0.3))) + y_true = constant_op.constant(((0, 1, 1, 0), (1, 0, 0, 0), (0, 0, 0, 0), + (1, 1, 1, 1))) + sample_weight = ((0.0, 2.0, 3.0, 5.0),) + + result = tn_obj(y_true, y_pred, sample_weight=sample_weight) + self.assertAllClose([5., 15., 23.], self.evaluate(result)) + + def test_reset_states(self): + tn_obj = metrics.TrueNegatives() + model = _get_simple_sequential_model([tn_obj]) + x = np.zeros((100, 4)) + y = np.zeros((100, 1)) + model.evaluate(x, y) + self.assertEqual(self.evaluate(tn_obj.accumulator), 100.) + model.evaluate(x, y) + self.assertEqual(self.evaluate(tn_obj.accumulator), 100.) + + +@test_util.run_all_in_graph_and_eager_modes +class TruePositivesTest(test.TestCase): + + def test_config(self): + tp_obj = metrics.TruePositives(name='my_tp', thresholds=[0.4, 0.9]) + self.assertEqual(tp_obj.name, 'my_tp') + self.assertEqual(len(tp_obj.variables), 1) + self.assertEqual(tp_obj.thresholds, [0.4, 0.9]) + + def test_unweighted(self): + tp_obj = metrics.TruePositives() + self.evaluate(variables.variables_initializer(tp_obj.variables)) + + y_true = constant_op.constant(((0, 1, 0, 1, 0), (0, 0, 1, 1, 1), + (1, 1, 1, 1, 0), (0, 0, 0, 0, 1))) + y_pred = constant_op.constant(((0, 0, 1, 1, 0), (1, 1, 1, 1, 1), + (0, 1, 0, 1, 0), (1, 1, 1, 1, 1))) + + update_op = tp_obj.update_state(y_true, y_pred) + self.evaluate(update_op) + result = tp_obj.result() + self.assertAllClose(7., result) + + def test_weighted(self): + tp_obj = metrics.TruePositives() + self.evaluate(variables.variables_initializer(tp_obj.variables)) + y_true = constant_op.constant(((0, 1, 0, 1, 0), (0, 0, 1, 1, 1), + (1, 1, 1, 1, 0), (0, 0, 0, 0, 1))) + y_pred = constant_op.constant(((0, 0, 1, 1, 0), (1, 1, 1, 1, 1), + (0, 1, 0, 1, 0), (1, 1, 1, 1, 1))) + sample_weight = constant_op.constant((1., 1.5, 2., 2.5)) + result = tp_obj(y_true, y_pred, sample_weight=sample_weight) + self.assertAllClose(12., self.evaluate(result)) + + def test_unweighted_with_thresholds(self): + tp_obj = metrics.TruePositives(thresholds=[0.15, 0.5, 0.85]) + self.evaluate(variables.variables_initializer(tp_obj.variables)) + + y_pred = constant_op.constant(((0.9, 0.2, 0.8, 0.1), (0.2, 0.9, 0.7, 0.6), + (0.1, 0.2, 0.4, 0.3), (0, 1, 0.7, 0.3))) + y_true = constant_op.constant(((0, 1, 1, 0), (1, 0, 0, 0), (0, 0, 0, 0), + (1, 1, 1, 1))) + + update_op = tp_obj.update_state(y_true, y_pred) + self.evaluate(update_op) + result = tp_obj.result() + self.assertAllClose([6., 3., 1.], result) + + def test_weighted_with_thresholds(self): + tp_obj = metrics.TruePositives(thresholds=[0.15, 0.5, 0.85]) + self.evaluate(variables.variables_initializer(tp_obj.variables)) + + y_pred = constant_op.constant(((0.9, 0.2, 0.8, 0.1), (0.2, 0.9, 0.7, 0.6), + (0.1, 0.2, 0.4, 0.3), (0, 1, 0.7, 0.3))) + y_true = constant_op.constant(((0, 1, 1, 0), (1, 0, 0, 0), (0, 0, 0, 0), + (1, 1, 1, 1))) + + result = tp_obj(y_true, y_pred, sample_weight=37.) + self.assertAllClose([222., 111., 37.], self.evaluate(result)) + + def test_reset_states(self): + tp_obj = metrics.TruePositives() + model = _get_simple_sequential_model([tp_obj]) + x = np.ones((100, 4)) + y = np.ones((100, 1)) + model.evaluate(x, y) + self.assertEqual(self.evaluate(tp_obj.accumulator), 100.) + model.evaluate(x, y) + self.assertEqual(self.evaluate(tp_obj.accumulator), 100.) + + +@test_util.run_all_in_graph_and_eager_modes +class PrecisionTest(test.TestCase): + + def test_config(self): + p_obj = metrics.Precision(name='my_precision', thresholds=[0.4, 0.9]) + self.assertEqual(p_obj.name, 'my_precision') + self.assertLen(p_obj.variables, 2) + self.assertEqual([v.name for v in p_obj.variables], + ['true_positives:0', 'false_positives:0']) + self.assertEqual(p_obj.thresholds, [0.4, 0.9]) + + def test_value_is_idempotent(self): + p_obj = metrics.Precision(thresholds=[0.3, 0.72]) + y_pred = random_ops.random_uniform(shape=(10, 3)) + y_true = random_ops.random_uniform(shape=(10, 3)) + update_op = p_obj.update_state(y_true, y_pred) + self.evaluate(variables.variables_initializer(p_obj.variables)) + + # Run several updates. + for _ in range(10): + self.evaluate(update_op) + + # Then verify idempotency. + initial_precision = self.evaluate(p_obj.result()) + for _ in range(10): + self.assertArrayNear(initial_precision, self.evaluate(p_obj.result()), + 1e-3) + + def test_unweighted(self): + p_obj = metrics.Precision() + y_pred = constant_op.constant([1, 0, 1, 0], shape=(1, 4)) + y_true = constant_op.constant([0, 1, 1, 0], shape=(1, 4)) + self.evaluate(variables.variables_initializer(p_obj.variables)) + result = p_obj(y_true, y_pred) + self.assertAlmostEqual(0.5, self.evaluate(result)) + + def test_unweighted_all_incorrect(self): + p_obj = metrics.Precision(thresholds=[0.5]) + inputs = np.random.randint(0, 2, size=(100, 1)) + y_pred = constant_op.constant(inputs) + y_true = constant_op.constant(1 - inputs) + self.evaluate(variables.variables_initializer(p_obj.variables)) + result = p_obj(y_true, y_pred) + self.assertAlmostEqual(0, self.evaluate(result)) + + def test_weighted(self): + p_obj = metrics.Precision() + y_pred = constant_op.constant([[1, 0, 1, 0], [1, 0, 1, 0]]) + y_true = constant_op.constant([[0, 1, 1, 0], [1, 0, 0, 1]]) + self.evaluate(variables.variables_initializer(p_obj.variables)) + result = p_obj( + y_true, + y_pred, + sample_weight=constant_op.constant([[1, 2, 3, 4], [4, 3, 2, 1]])) + weighted_tp = 3.0 + 4.0 + weighted_positives = (1.0 + 3.0) + (4.0 + 2.0) + expected_precision = weighted_tp / weighted_positives + self.assertAlmostEqual(expected_precision, self.evaluate(result)) + + def test_div_by_zero(self): + p_obj = metrics.Precision() + y_pred = constant_op.constant([0, 0, 0, 0]) + y_true = constant_op.constant([0, 0, 0, 0]) + self.evaluate(variables.variables_initializer(p_obj.variables)) + result = p_obj(y_true, y_pred) + self.assertEqual(0, self.evaluate(result)) + + def test_unweighted_with_threshold(self): + p_obj = metrics.Precision(thresholds=[0.5, 0.7]) + y_pred = constant_op.constant([1, 0, 0.6, 0], shape=(1, 4)) + y_true = constant_op.constant([0, 1, 1, 0], shape=(1, 4)) + self.evaluate(variables.variables_initializer(p_obj.variables)) + result = p_obj(y_true, y_pred) + self.assertArrayNear([0.5, 0.], self.evaluate(result), 0) + + def test_weighted_with_threshold(self): + p_obj = metrics.Precision(thresholds=[0.5, 1.]) + y_true = constant_op.constant([[0, 1], [1, 0]], shape=(2, 2)) + y_pred = constant_op.constant([[1, 0], [0.6, 0]], + shape=(2, 2), + dtype=dtypes.float32) + weights = constant_op.constant([[4, 0], [3, 1]], + shape=(2, 2), + dtype=dtypes.float32) + self.evaluate(variables.variables_initializer(p_obj.variables)) + result = p_obj(y_true, y_pred, sample_weight=weights) + weighted_tp = 0 + 3. + weighted_positives = (0 + 3.) + (4. + 0.) + expected_precision = weighted_tp / weighted_positives + self.assertArrayNear([expected_precision, 0], self.evaluate(result), 1e-3) + + def test_multiple_updates(self): + p_obj = metrics.Precision(thresholds=[0.5, 1.]) + y_true = constant_op.constant([[0, 1], [1, 0]], shape=(2, 2)) + y_pred = constant_op.constant([[1, 0], [0.6, 0]], + shape=(2, 2), + dtype=dtypes.float32) + weights = constant_op.constant([[4, 0], [3, 1]], + shape=(2, 2), + dtype=dtypes.float32) + self.evaluate(variables.variables_initializer(p_obj.variables)) + update_op = p_obj.update_state(y_true, y_pred, sample_weight=weights) + for _ in range(2): + self.evaluate(update_op) + + weighted_tp = (0 + 3.) + (0 + 3.) + weighted_positives = ((0 + 3.) + (4. + 0.)) + ((0 + 3.) + (4. + 0.)) + expected_precision = weighted_tp / weighted_positives + self.assertArrayNear([expected_precision, 0], self.evaluate(p_obj.result()), + 1e-3) + + def test_reset_states(self): + p_obj = metrics.Precision() + model = _get_simple_sequential_model([p_obj]) + x = np.concatenate((np.ones((50, 4)), np.ones((50, 4)))) + y = np.concatenate((np.ones((50, 1)), np.zeros((50, 1)))) + model.evaluate(x, y) + self.assertEqual(self.evaluate(p_obj.tp), 50.) + self.assertEqual(self.evaluate(p_obj.fp), 50.) + model.evaluate(x, y) + self.assertEqual(self.evaluate(p_obj.tp), 50.) + self.assertEqual(self.evaluate(p_obj.fp), 50.) + + +@test_util.run_all_in_graph_and_eager_modes +class RecallTest(test.TestCase): + + def test_config(self): + r_obj = metrics.Recall(name='my_recall', thresholds=[0.4, 0.9]) + self.assertEqual(r_obj.name, 'my_recall') + self.assertLen(r_obj.variables, 2) + self.assertEqual([v.name for v in r_obj.variables], + ['true_positives:0', 'false_negatives:0']) + self.assertEqual(r_obj.thresholds, [0.4, 0.9]) + + def test_value_is_idempotent(self): + r_obj = metrics.Recall(thresholds=[0.3, 0.72]) + y_pred = random_ops.random_uniform(shape=(10, 3)) + y_true = random_ops.random_uniform(shape=(10, 3)) + update_op = r_obj.update_state(y_true, y_pred) + self.evaluate(variables.variables_initializer(r_obj.variables)) + + # Run several updates. + for _ in range(10): + self.evaluate(update_op) + + # Then verify idempotency. + initial_recall = self.evaluate(r_obj.result()) + for _ in range(10): + self.assertArrayNear(initial_recall, self.evaluate(r_obj.result()), 1e-3) + + def test_unweighted(self): + r_obj = metrics.Recall() + y_pred = constant_op.constant([1, 0, 1, 0], shape=(1, 4)) + y_true = constant_op.constant([0, 1, 1, 0], shape=(1, 4)) + self.evaluate(variables.variables_initializer(r_obj.variables)) + result = r_obj(y_true, y_pred) + self.assertAlmostEqual(0.5, self.evaluate(result)) + + def test_unweighted_all_incorrect(self): + r_obj = metrics.Recall(thresholds=[0.5]) + inputs = np.random.randint(0, 2, size=(100, 1)) + y_pred = constant_op.constant(inputs) + y_true = constant_op.constant(1 - inputs) + self.evaluate(variables.variables_initializer(r_obj.variables)) + result = r_obj(y_true, y_pred) + self.assertAlmostEqual(0, self.evaluate(result)) + + def test_weighted(self): + r_obj = metrics.Recall() + y_pred = constant_op.constant([[1, 0, 1, 0], [0, 1, 0, 1]]) + y_true = constant_op.constant([[0, 1, 1, 0], [1, 0, 0, 1]]) + self.evaluate(variables.variables_initializer(r_obj.variables)) + result = r_obj( + y_true, + y_pred, + sample_weight=constant_op.constant([[1, 2, 3, 4], [4, 3, 2, 1]])) + weighted_tp = 3.0 + 1.0 + weighted_t = (2.0 + 3.0) + (4.0 + 1.0) + expected_recall = weighted_tp / weighted_t + self.assertAlmostEqual(expected_recall, self.evaluate(result)) + + def test_div_by_zero(self): + r_obj = metrics.Recall() + y_pred = constant_op.constant([0, 0, 0, 0]) + y_true = constant_op.constant([0, 0, 0, 0]) + self.evaluate(variables.variables_initializer(r_obj.variables)) + result = r_obj(y_true, y_pred) + self.assertEqual(0, self.evaluate(result)) + + def test_unweighted_with_threshold(self): + r_obj = metrics.Recall(thresholds=[0.5, 0.7]) + y_pred = constant_op.constant([1, 0, 0.6, 0], shape=(1, 4)) + y_true = constant_op.constant([0, 1, 1, 0], shape=(1, 4)) + self.evaluate(variables.variables_initializer(r_obj.variables)) + result = r_obj(y_true, y_pred) + self.assertArrayNear([0.5, 0.], self.evaluate(result), 0) + + def test_weighted_with_threshold(self): + r_obj = metrics.Recall(thresholds=[0.5, 1.]) + y_true = constant_op.constant([[0, 1], [1, 0]], shape=(2, 2)) + y_pred = constant_op.constant([[1, 0], [0.6, 0]], + shape=(2, 2), + dtype=dtypes.float32) + weights = constant_op.constant([[1, 4], [3, 2]], + shape=(2, 2), + dtype=dtypes.float32) + self.evaluate(variables.variables_initializer(r_obj.variables)) + result = r_obj(y_true, y_pred, sample_weight=weights) + weighted_tp = 0 + 3. + weighted_positives = (0 + 3.) + (4. + 0.) + expected_recall = weighted_tp / weighted_positives + self.assertArrayNear([expected_recall, 0], self.evaluate(result), 1e-3) + + def test_multiple_updates(self): + r_obj = metrics.Recall(thresholds=[0.5, 1.]) + y_true = constant_op.constant([[0, 1], [1, 0]], shape=(2, 2)) + y_pred = constant_op.constant([[1, 0], [0.6, 0]], + shape=(2, 2), + dtype=dtypes.float32) + weights = constant_op.constant([[1, 4], [3, 2]], + shape=(2, 2), + dtype=dtypes.float32) + self.evaluate(variables.variables_initializer(r_obj.variables)) + update_op = r_obj.update_state(y_true, y_pred, sample_weight=weights) + for _ in range(2): + self.evaluate(update_op) + + weighted_tp = (0 + 3.) + (0 + 3.) + weighted_positives = ((0 + 3.) + (4. + 0.)) + ((0 + 3.) + (4. + 0.)) + expected_recall = weighted_tp / weighted_positives + self.assertArrayNear([expected_recall, 0], self.evaluate(r_obj.result()), + 1e-3) + + def test_reset_states(self): + r_obj = metrics.Recall() + model = _get_simple_sequential_model([r_obj]) + x = np.concatenate((np.ones((50, 4)), np.zeros((50, 4)))) + y = np.concatenate((np.ones((50, 1)), np.ones((50, 1)))) + model.evaluate(x, y) + self.assertEqual(self.evaluate(r_obj.tp), 50.) + self.assertEqual(self.evaluate(r_obj.fn), 50.) + model.evaluate(x, y) + self.assertEqual(self.evaluate(r_obj.tp), 50.) + self.assertEqual(self.evaluate(r_obj.fn), 50.) + + +@test_util.run_all_in_graph_and_eager_modes +class SensitivityAtSpecificityTest(test.TestCase, parameterized.TestCase): + + def test_config(self): + s_obj = metrics.SensitivityAtSpecificity( + 0.4, num_thresholds=100, name='sensitivity_at_specificity_1') + self.assertEqual(s_obj.name, 'sensitivity_at_specificity_1') + self.assertLen(s_obj.variables, 4) + self.assertEqual(s_obj.value, 0.4) + self.assertLen(s_obj.thresholds, 100) + + def test_value_is_idempotent(self): + s_obj = metrics.SensitivityAtSpecificity(0.7) + y_pred = random_ops.random_uniform((10, 3), + maxval=1, + dtype=dtypes.float32, + seed=1) + y_true = random_ops.random_uniform((10, 3), + maxval=2, + dtype=dtypes.int64, + seed=1) + update_op = s_obj.update_state(y_true, y_pred) + self.evaluate(variables.variables_initializer(s_obj.variables)) + + # Run several updates. + for _ in range(10): + self.evaluate(update_op) + + # Then verify idempotency. + initial_sensitivity = self.evaluate(s_obj.result()) + for _ in range(10): + self.assertAlmostEqual(initial_sensitivity, self.evaluate(s_obj.result()), + 1e-3) + + def test_unweighted_all_correct(self): + s_obj = metrics.SensitivityAtSpecificity(0.7) + inputs = np.random.randint(0, 2, size=(100, 1)) + y_pred = constant_op.constant(inputs, dtype=dtypes.float32) + y_true = constant_op.constant(inputs) + self.evaluate(variables.variables_initializer(s_obj.variables)) + result = s_obj(y_true, y_pred) + self.assertAlmostEqual(1, self.evaluate(result)) + + def test_unweighted_high_specificity(self): + s_obj = metrics.SensitivityAtSpecificity(0.8) + pred_values = [0.0, 0.1, 0.2, 0.3, 0.4, 0.1, 0.45, 0.5, 0.8, 0.9] + label_values = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] + + y_pred = constant_op.constant(pred_values, dtype=dtypes.float32) + y_true = constant_op.constant(label_values) + self.evaluate(variables.variables_initializer(s_obj.variables)) + result = s_obj(y_true, y_pred) + self.assertAlmostEqual(0.8, self.evaluate(result)) + + def test_unweighted_low_specificity(self): + s_obj = metrics.SensitivityAtSpecificity(0.4) + pred_values = [0.0, 0.1, 0.2, 0.3, 0.4, 0.01, 0.02, 0.25, 0.26, 0.26] + label_values = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] + + y_pred = constant_op.constant(pred_values, dtype=dtypes.float32) + y_true = constant_op.constant(label_values) + self.evaluate(variables.variables_initializer(s_obj.variables)) + result = s_obj(y_true, y_pred) + self.assertAlmostEqual(0.6, self.evaluate(result)) + + @parameterized.parameters([dtypes.bool, dtypes.int32, dtypes.float32]) + def test_weighted(self, label_dtype): + s_obj = metrics.SensitivityAtSpecificity(0.4) + pred_values = [0.0, 0.1, 0.2, 0.3, 0.4, 0.01, 0.02, 0.25, 0.26, 0.26] + label_values = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] + weight_values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + + y_pred = constant_op.constant(pred_values, dtype=dtypes.float32) + y_true = math_ops.cast(label_values, dtype=label_dtype) + weights = constant_op.constant(weight_values) + self.evaluate(variables.variables_initializer(s_obj.variables)) + result = s_obj(y_true, y_pred, sample_weight=weights) + self.assertAlmostEqual(0.675, self.evaluate(result)) + + def test_invalid_specificity(self): + with self.assertRaisesRegexp( + ValueError, r'`specificity` must be in the range \[0, 1\].'): + metrics.SensitivityAtSpecificity(-1) + + def test_invalid_num_thresholds(self): + with self.assertRaisesRegexp(ValueError, '`num_thresholds` must be > 0.'): + metrics.SensitivityAtSpecificity(0.4, num_thresholds=-1) + + def test_reset_states(self): + s_obj = metrics.SensitivityAtSpecificity(0.5, num_thresholds=1) + model = _get_simple_sequential_model([s_obj]) + x = np.concatenate((np.ones((25, 4)), np.zeros((25, 4)), np.zeros((25, 4)), + np.ones((25, 4)))) + y = np.concatenate((np.ones((25, 1)), np.zeros((25, 1)), np.ones((25, 1)), + np.zeros((25, 1)))) + model.evaluate(x, y) + self.assertEqual(self.evaluate(s_obj.tp), 25.) + self.assertEqual(self.evaluate(s_obj.fp), 25.) + self.assertEqual(self.evaluate(s_obj.fn), 25.) + self.assertEqual(self.evaluate(s_obj.tn), 25.) + model.evaluate(x, y) + self.assertEqual(self.evaluate(s_obj.tp), 25.) + self.assertEqual(self.evaluate(s_obj.fp), 25.) + self.assertEqual(self.evaluate(s_obj.fn), 25.) + self.assertEqual(self.evaluate(s_obj.tn), 25.) + + +@test_util.run_all_in_graph_and_eager_modes +class SpecificityAtSensitivityTest(test.TestCase, parameterized.TestCase): + + def test_config(self): + s_obj = metrics.SpecificityAtSensitivity( + 0.4, num_thresholds=100, name='specificity_at_sensitivity_1') + self.assertEqual(s_obj.name, 'specificity_at_sensitivity_1') + self.assertLen(s_obj.variables, 4) + self.assertEqual(s_obj.value, 0.4) + self.assertLen(s_obj.thresholds, 100) + + def test_value_is_idempotent(self): + s_obj = metrics.SpecificityAtSensitivity(0.7) + y_pred = random_ops.random_uniform((10, 3), + maxval=1, + dtype=dtypes.float32, + seed=1) + y_true = random_ops.random_uniform((10, 3), + maxval=2, + dtype=dtypes.int64, + seed=1) + update_op = s_obj.update_state(y_true, y_pred) + self.evaluate(variables.variables_initializer(s_obj.variables)) + + # Run several updates. + for _ in range(10): + self.evaluate(update_op) + + # Then verify idempotency. + initial_specificity = self.evaluate(s_obj.result()) + for _ in range(10): + self.assertAlmostEqual(initial_specificity, self.evaluate(s_obj.result()), + 1e-3) + + def test_unweighted_all_correct(self): + s_obj = metrics.SpecificityAtSensitivity(0.7) + inputs = np.random.randint(0, 2, size=(100, 1)) + y_pred = constant_op.constant(inputs, dtype=dtypes.float32) + y_true = constant_op.constant(inputs) + self.evaluate(variables.variables_initializer(s_obj.variables)) + result = s_obj(y_true, y_pred) + self.assertAlmostEqual(1, self.evaluate(result)) + + def test_unweighted_high_sensitivity(self): + s_obj = metrics.SpecificityAtSensitivity(0.8) + pred_values = [0.0, 0.1, 0.2, 0.3, 0.4, 0.1, 0.45, 0.5, 0.8, 0.9] + label_values = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] + + y_pred = constant_op.constant(pred_values, dtype=dtypes.float32) + y_true = constant_op.constant(label_values) + self.evaluate(variables.variables_initializer(s_obj.variables)) + result = s_obj(y_true, y_pred) + self.assertAlmostEqual(0.4, self.evaluate(result)) + + def test_unweighted_low_sensitivity(self): + s_obj = metrics.SpecificityAtSensitivity(0.4) + pred_values = [0.0, 0.1, 0.2, 0.3, 0.4, 0.01, 0.02, 0.25, 0.26, 0.26] + label_values = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] + + y_pred = constant_op.constant(pred_values, dtype=dtypes.float32) + y_true = constant_op.constant(label_values) + self.evaluate(variables.variables_initializer(s_obj.variables)) + result = s_obj(y_true, y_pred) + self.assertAlmostEqual(0.6, self.evaluate(result)) + + @parameterized.parameters([dtypes.bool, dtypes.int32, dtypes.float32]) + def test_weighted(self, label_dtype): + s_obj = metrics.SpecificityAtSensitivity(0.4) + pred_values = [0.0, 0.1, 0.2, 0.3, 0.4, 0.01, 0.02, 0.25, 0.26, 0.26] + label_values = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] + weight_values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + + y_pred = constant_op.constant(pred_values, dtype=dtypes.float32) + y_true = math_ops.cast(label_values, dtype=label_dtype) + weights = constant_op.constant(weight_values) + self.evaluate(variables.variables_initializer(s_obj.variables)) + result = s_obj(y_true, y_pred, sample_weight=weights) + self.assertAlmostEqual(0.4, self.evaluate(result)) + + def test_invalid_sensitivity(self): + with self.assertRaisesRegexp( + ValueError, r'`sensitivity` must be in the range \[0, 1\].'): + metrics.SpecificityAtSensitivity(-1) + + def test_invalid_num_thresholds(self): + with self.assertRaisesRegexp(ValueError, '`num_thresholds` must be > 0.'): + metrics.SpecificityAtSensitivity(0.4, num_thresholds=-1) + + def test_reset_states(self): + s_obj = metrics.SpecificityAtSensitivity(0.5, num_thresholds=1) + model = _get_simple_sequential_model([s_obj]) + x = np.concatenate((np.ones((25, 4)), np.zeros((25, 4)), np.zeros((25, 4)), + np.ones((25, 4)))) + y = np.concatenate((np.ones((25, 1)), np.zeros((25, 1)), np.ones((25, 1)), + np.zeros((25, 1)))) + model.evaluate(x, y) + self.assertEqual(self.evaluate(s_obj.tp), 25.) + self.assertEqual(self.evaluate(s_obj.fp), 25.) + self.assertEqual(self.evaluate(s_obj.fn), 25.) + self.assertEqual(self.evaluate(s_obj.tn), 25.) + model.evaluate(x, y) + self.assertEqual(self.evaluate(s_obj.tp), 25.) + self.assertEqual(self.evaluate(s_obj.fp), 25.) + self.assertEqual(self.evaluate(s_obj.fn), 25.) + self.assertEqual(self.evaluate(s_obj.tn), 25.) + + +@test_util.run_all_in_graph_and_eager_modes +class CosineProximityTest(test.TestCase): + + def test_config(self): + cosine_obj = metrics.CosineProximity(name='my_cos', dtype=dtypes.int32) + self.assertEqual(cosine_obj.name, 'my_cos') + self.assertEqual(cosine_obj._dtype, dtypes.int32) + + def test_unweighted(self): + cosine_obj = metrics.CosineProximity() + self.evaluate(variables.variables_initializer(cosine_obj.variables)) + + y_true = constant_op.constant(((0, 1, 0, 1, 0), (0, 0, 1, 1, 1), + (1, 1, 1, 1, 0), (0, 0, 0, 0, 1))) + y_pred = constant_op.constant(((0, 0, 1, 1, 0), (1, 1, 1, 1, 1), + (0, 1, 0, 1, 0), (1, 1, 1, 1, 1))) + + update_op = cosine_obj.update_state(y_true, y_pred) + self.evaluate(update_op) + result = cosine_obj.result() + self.assertAllClose(-0.60723, result, atol=1e-5) + + def test_weighted(self): + cosine_obj = metrics.CosineProximity() + self.evaluate(variables.variables_initializer(cosine_obj.variables)) + y_true = constant_op.constant(((0, 1, 0, 1, 0), (0, 0, 1, 1, 1), + (1, 1, 1, 1, 0), (0, 0, 0, 0, 1))) + y_pred = constant_op.constant(((0, 0, 1, 1, 0), (1, 1, 1, 1, 1), + (0, 1, 0, 1, 0), (1, 1, 1, 1, 1))) + sample_weight = constant_op.constant((1., 1.5, 2., 2.5)) + result = cosine_obj(y_true, y_pred, sample_weight=sample_weight) + self.assertAllClose(-0.59916, self.evaluate(result), atol=1e-5) if __name__ == '__main__': test.main() diff --git a/tensorflow/python/keras/model_subclassing_test.py b/tensorflow/python/keras/model_subclassing_test.py index aca058b111..620275e50f 100644 --- a/tensorflow/python/keras/model_subclassing_test.py +++ b/tensorflow/python/keras/model_subclassing_test.py @@ -25,6 +25,7 @@ import numpy as np from tensorflow.python import keras from tensorflow.python.data.ops import dataset_ops from tensorflow.python.eager import context +from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops @@ -455,12 +456,12 @@ class ModelSubclassingTest(test.TestCase): model = SimpleTestModel(num_classes=num_classes, use_dp=True, use_bn=True) model.compile(loss='mse', optimizer=RMSPropOptimizer(learning_rate=0.001)) - x = np.ones((num_samples, input_dim)) - y = np.zeros((num_samples, num_classes)) + x = np.ones((num_samples, input_dim), dtype=np.float32) + y = np.zeros((num_samples, num_classes), dtype=np.float32) dataset = dataset_ops.Dataset.from_tensor_slices((x, y)) dataset = dataset.repeat(100) dataset = dataset.batch(10) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) model.fit(iterator, epochs=2, steps_per_epoch=10, verbose=0) _ = model.evaluate(iterator, steps=10, verbose=0) @@ -725,10 +726,41 @@ class ModelSubclassingTest(test.TestCase): _ = model.evaluate(x, y, verbose=0) self.assertEqual(len(model.weights), 16) - self.assertEqual( - len(model.non_trainable_weights), 4) + self.assertEqual(len(model.non_trainable_weights), 4) self.assertEqual(len(model.trainable_weights), 12) + def test_subclass_nested_in_sequential(self): + num_classes = 2 + num_samples = 100 + input_dim = 50 + + class Inner(keras.Model): + + def __init__(self): + super(Inner, self).__init__() + self.dense1 = keras.layers.Dense(32, activation='relu') + self.dense2 = keras.layers.Dense(num_classes, activation='relu') + self.bn = keras.layers.BatchNormalization() + + def call(self, inputs): + x = self.dense1(inputs) + x = self.dense2(x) + return self.bn(x) + + model = keras.Sequential([Inner()]) + model.compile(loss='mse', + optimizer=RMSPropOptimizer(learning_rate=0.001), + metrics=['acc']) + + x = np.ones((num_samples, input_dim)) + y = np.zeros((num_samples, num_classes)) + model.fit(x, y, epochs=2, batch_size=32, verbose=0) + _ = model.evaluate(x, y, verbose=0) + + self.assertEqual(len(model.weights), 8) + self.assertEqual(len(model.non_trainable_weights), 2) + self.assertEqual(len(model.trainable_weights), 6) + def test_support_for_manual_training_arg(self): # In most cases, the `training` argument is left unspecified, in which # case it defaults to value corresponding to the Model method being used @@ -819,9 +851,73 @@ class ModelSubclassingTest(test.TestCase): self.assertEqual([m.dense.kernel, m.dense.bias, m.not_trainable_var], m.non_trainable_variables) + @test_util.run_in_graph_and_eager_modes + def test_add_weight_in_model(self): + + class MyModel(keras.Model): + + def __init__(self): + super(MyModel, self).__init__() + self.b = self.add_weight('bias', (10,)) + self.c = self.add_weight('bias2', (10,), trainable=False) + + def call(self, inputs): + return inputs + self.b + self.c + + x = ops.convert_to_tensor(np.ones((10, 10), 'float32')) + model = MyModel() + model(x) + self.assertEqual(1, len(model.trainable_weights)) + self.assertEqual(1, len(model.non_trainable_weights)) + self.assertEqual(2, len(model.weights)) + + class MyModelCustomBuild(keras.Model): + + def build(self, input_shape): + self.b = self.add_weight('bias', (10,)) + self.c = self.add_weight('bias2', (10,), trainable=False) + + def call(self, inputs): + return inputs + self.b + self.c + + x = ops.convert_to_tensor(np.ones((10, 10), 'float32')) + model = MyModelCustomBuild() + model(x) + self.assertEqual(1, len(model.trainable_weights)) + self.assertEqual(1, len(model.non_trainable_weights)) + self.assertEqual(2, len(model.weights)) + + def test_add_update_in_model(self): + + class MyModel(keras.Model): + + def __init__(self): + super(MyModel, self).__init__() + self.b = self.add_weight('bias', (10,)) + self.c = self.add_weight('bias2', (10,)) + + def call(self, inputs): + # Unconditional + self.add_update(self.b.assign(self.b * 2)) + # Conditional + self.add_update(self.c.assign(inputs[1, :]), inputs) + return inputs + self.b + self.c + + x = ops.convert_to_tensor(np.ones((10, 10), 'float32')) + model = MyModel() + model(x) + + if context.executing_eagerly(): + self.assertEqual(0, len(model.updates)) + else: + self.assertEqual(2, len(model.updates)) + self.assertEqual(1, len(model.get_updates_for(None))) + self.assertEqual(1, len(model.get_updates_for(x))) + class GraphSpecificModelSubclassingTests(test.TestCase): + @test_util.run_deprecated_v1 def test_single_io_workflow_with_tensors(self): num_classes = 2 num_samples = 10 @@ -839,6 +935,7 @@ class GraphSpecificModelSubclassingTests(test.TestCase): model.fit(x, y, epochs=2, steps_per_epoch=10, verbose=0) _ = model.evaluate(steps=10, verbose=0) + @test_util.run_deprecated_v1 def test_multi_io_workflow_with_tensors(self): num_classes = (2, 3) num_samples = 10 @@ -858,6 +955,7 @@ class GraphSpecificModelSubclassingTests(test.TestCase): model.fit([x1, x2], [y1, y2], epochs=2, steps_per_epoch=10, verbose=0) _ = model.evaluate(steps=10, verbose=0) + @test_util.run_deprecated_v1 def test_updates_and_losses_for_nested_models_in_subclassed_model(self): # Case 1: deferred-build sequential nested in subclass. @@ -925,6 +1023,7 @@ class GraphSpecificModelSubclassingTests(test.TestCase): self.assertEqual(len(model.get_updates_for(x)), 2) self.assertEqual(len(model.get_losses_for(x)), 1) + @test_util.run_deprecated_v1 def test_multi_io_workflow_with_numpy_arrays_and_custom_placeholders(self): num_classes = (2, 3) num_samples = 1000 @@ -974,6 +1073,16 @@ class TrainingNoDefaultModel(keras.Model): return self.dense1(x) +class TrainingMaskingModel(keras.Model): + + def __init__(self): + super(TrainingMaskingModel, self).__init__() + self.dense1 = keras.layers.Dense(1) + + def call(self, x, training=False, mask=None): + return self.dense1(x) + + class CustomCallSignatureTests(test.TestCase): @test_util.run_in_graph_and_eager_modes @@ -1003,6 +1112,19 @@ class CustomCallSignatureTests(test.TestCase): 'has been properly built.')) self.assertTrue(model.built, 'Model should be built after calling `build`.') + @test_util.run_in_graph_and_eager_modes + def test_training_and_mask_args_call_build(self): + input_dim = 2 + + model = TrainingMaskingModel() + self.assertFalse(model.built, 'Model should not have been built') + self.assertFalse(model.weights, ('Model should have no weights since it ' + 'has not been built.')) + model.build((None, input_dim)) + self.assertTrue(model.weights, ('Model should have weights now that it ' + 'has been properly built.')) + self.assertTrue(model.built, 'Model should be built after calling `build`.') + @test_util.run_in_graph_and_eager_modes def test_custom_call_kwargs_and_build(self): first_input_shape = (2, 3) diff --git a/tensorflow/python/keras/models.py b/tensorflow/python/keras/models.py index 225c6c6af8..2637191bb7 100644 --- a/tensorflow/python/keras/models.py +++ b/tensorflow/python/keras/models.py @@ -100,17 +100,19 @@ def _clone_functional_model(model, input_tensors=None): input_tensors = list(input_tensors) input_tensors = generic_utils.to_list(input_tensors) input_tensors_ = [] - for i, x in enumerate(input_tensors): - if not K.is_keras_tensor(x): - name = model._input_layers[i].name - input_tensor = Input(tensor=x, name='input_wrapper_for_' + name) + for i in range(len(input_tensors)): + input_tensor = input_tensors[i] + if not K.is_keras_tensor(input_tensor): + original_input_layer = model._input_layers[i] + name = original_input_layer.name + input_tensor = Input(tensor=input_tensor, + name='input_wrapper_for_' + name) input_tensors_.append(input_tensor) # Cache newly created input layer. - original_input_layer = x._keras_history[0] newly_created_input_layer = input_tensor._keras_history[0] layer_map[original_input_layer] = newly_created_input_layer else: - input_tensors_.append(x) + input_tensors_.append(input_tensor) input_tensors = input_tensors_ for x, y in zip(model.inputs, input_tensors): @@ -209,14 +211,17 @@ def _clone_sequential_model(model, input_tensors=None): # Use model._layers to ensure that all layers are cloned. The model's layers # property will exclude the initial InputLayer (if it exists) in the model, # resulting in a different Sequential model structure. - layers = [clone(layer) for layer in model._layers] if input_tensors is None: + layers = [clone(layer) for layer in model._layers] return Sequential(layers=layers, name=model.name) else: # If input tensors are provided, the original model's InputLayer is # overwritten with a different InputLayer. - if isinstance(layers[0], InputLayer): - layers = layers[1:] + layers = [ + clone(layer) + for layer in model._layers + if not isinstance(layer, InputLayer) + ] if len(generic_utils.to_list(input_tensors)) != 1: raise ValueError('To clone a `Sequential` model, we expect ' ' at most one tensor ' @@ -304,8 +309,9 @@ def _in_place_subclassed_model_reset(model): attributes_cache[name] = value assert value in model._layers elif isinstance( - value, (list, tuple)) and name not in ('layers', '_layers', - 'stateful_metric_functions'): + value, + (list, tuple)) and name not in ('layers', '_layers', 'metrics', + '_compile_stateful_metric_functions'): # Handle case: list/tuple of layers (also tracked by the Network API). if value and all(isinstance(val, Layer) for val in value): raise ValueError('We do not support the use of list-of-layers ' @@ -345,9 +351,6 @@ def _in_place_subclassed_model_reset(model): 'targets', '_feed_targets', 'sample_weight_modes', - 'weighted_metrics', - 'metrics_names', - 'metrics_tensors', 'total_loss', 'sample_weights', '_feed_sample_weights', @@ -495,10 +498,11 @@ def clone_and_build_model( clone.compile( optimizer, model.loss, - metrics=metrics_module.clone_metrics(model.metrics), + metrics=metrics_module.clone_metrics(model._compile_metrics), loss_weights=model.loss_weights, sample_weight_mode=model.sample_weight_mode, - weighted_metrics=metrics_module.clone_metrics(model.weighted_metrics), + weighted_metrics=metrics_module.clone_metrics( + model._compile_weighted_metrics), target_tensors=target_tensors) return clone diff --git a/tensorflow/python/keras/models_test.py b/tensorflow/python/keras/models_test.py index bf778f1497..b0872ae3ab 100644 --- a/tensorflow/python/keras/models_test.py +++ b/tensorflow/python/keras/models_test.py @@ -26,10 +26,12 @@ import numpy as np from tensorflow.python import keras from tensorflow.python.eager import context from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops from tensorflow.python.framework import test_util from tensorflow.python.keras import backend as K from tensorflow.python.keras import metrics from tensorflow.python.keras import models +from tensorflow.python.ops import array_ops from tensorflow.python.ops import random_ops from tensorflow.python.ops import resource_variable_ops from tensorflow.python.platform import test @@ -67,6 +69,7 @@ def sequential_model(add_input_layer, include_input_shape=True): class TestModelCloning(test.TestCase): + @test_util.run_deprecated_v1 def test_clone_sequential_model(self): with self.cached_session(): val_a = np.random.random((10, 4)) @@ -81,25 +84,28 @@ class TestModelCloning(test.TestCase): # With placeholder creation new_model = keras.models.clone_model(model) # update ops from batch norm needs to be included - self.assertEquals(len(new_model.get_updates_for(new_model.inputs)), 2) + self.assertEqual(len(new_model.get_updates_for(new_model.inputs)), 2) new_model.compile('rmsprop', 'mse') new_model.train_on_batch(val_a, val_out) # On top of new tensor input_a = keras.Input(shape=(4,)) new_model = keras.models.clone_model(model, input_tensors=input_a) - self.assertEquals(len(new_model.get_updates_for(new_model.inputs)), 2) + self.assertEqual(len(new_model.get_updates_for(new_model.inputs)), 2) new_model.compile('rmsprop', 'mse') new_model.train_on_batch(val_a, val_out) # On top of new, non-Keras tensor input_a = keras.backend.variable(val_a) new_model = keras.models.clone_model(model, input_tensors=input_a) - self.assertEquals(len(new_model.get_updates_for(new_model.inputs)), 2) + self.assertEqual(len(new_model.get_updates_for(new_model.inputs)), 2) new_model.compile('rmsprop', 'mse') new_model.train_on_batch(None, val_out) + @test_util.run_deprecated_v1 def test_clone_sequential_model_input_layer(self): + + @test_util.run_deprecated_v1 def test_input_layer(include_inputs): with self.cached_session(): val_a = np.random.random((10, 4)) @@ -136,6 +142,7 @@ class TestModelCloning(test.TestCase): test_input_layer(True) test_input_layer(False) + @test_util.run_deprecated_v1 def test_clone_functional_model(self): with self.cached_session(): val_a = np.random.random((10, 4)) @@ -161,7 +168,7 @@ class TestModelCloning(test.TestCase): with self.cached_session(): # With placeholder creation new_model = keras.models.clone_model(model) - self.assertEquals(len(new_model.get_updates_for(new_model.inputs)), 2) + self.assertEqual(len(new_model.get_updates_for(new_model.inputs)), 2) new_model.compile('rmsprop', 'mse') new_model.train_on_batch([val_a, val_b], val_out) @@ -170,7 +177,7 @@ class TestModelCloning(test.TestCase): input_b = keras.Input(shape=(4,), name='b') new_model = keras.models.clone_model( model, input_tensors=[input_a, input_b]) - self.assertEquals(len(new_model.get_updates_for(new_model.inputs)), 2) + self.assertEqual(len(new_model.get_updates_for(new_model.inputs)), 2) new_model.compile('rmsprop', 'mse') new_model.train_on_batch([val_a, val_b], val_out) @@ -179,7 +186,7 @@ class TestModelCloning(test.TestCase): input_b = keras.backend.variable(val_b) new_model = keras.models.clone_model( model, input_tensors=[input_a, input_b]) - self.assertEquals(len(new_model.get_updates_for(new_model.inputs)), 2) + self.assertEqual(len(new_model.get_updates_for(new_model.inputs)), 2) new_model.compile('rmsprop', 'mse') new_model.train_on_batch(None, val_out) @@ -219,6 +226,34 @@ class TestModelCloning(test.TestCase): with self.assertRaises(ValueError): keras.models._clone_sequential_model(seq_model, input_tensors=y) + def test_functional_cloning_does_not_create_unnecessary_placeholders(self): + with ops.Graph().as_default(): + x = keras.Input((4,)) + y = keras.layers.Dense(4)(x) + model = keras.models.Model(x, y) + graph = ops.Graph() + with graph.as_default(): + x = array_ops.ones((10, 4)) + _ = keras.models.clone_model(model, input_tensors=[x]) + has_placeholder = _has_placeholder(graph) + self.assertFalse(has_placeholder) + + def test_sequential_cloning_does_not_create_unnecessary_placeholders(self): + with ops.Graph().as_default(): + model = keras.models.Sequential() + model.add(keras.layers.Dense(4, input_shape=(4,))) + graph = ops.Graph() + with graph.as_default(): + x = array_ops.ones((10, 4)) + _ = keras.models.clone_model(model, input_tensors=[x]) + has_placeholder = _has_placeholder(graph) + self.assertFalse(has_placeholder) + + +def _has_placeholder(graph): + ops_types = [op.type for op in graph.get_operations()] + return any('Placeholder' in s for s in ops_types) + class CheckpointingTests(test.TestCase): @@ -331,7 +366,8 @@ class TestCloneAndBuildModel(test.TestCase): self.assertEqual('mse', model.loss) self.assertTrue( isinstance(model.optimizer, keras.optimizers.RMSprop)) - self.assertEqual(['acc', metrics.categorical_accuracy], model.metrics) + self.assertEqual(['acc', metrics.categorical_accuracy], + model._compile_metrics) def _clone_and_build_test_helper(self, model, is_subclassed=False): inp = np.random.random((10, 4)) @@ -366,6 +402,7 @@ class TestCloneAndBuildModel(test.TestCase): new_model.train_on_batch(inp, out) new_model.evaluate(inp, out) + @test_util.run_deprecated_v1 def test_clone_and_build_compiled_sequential_model(self): with self.cached_session(): model = keras.models.Sequential() @@ -378,6 +415,7 @@ class TestCloneAndBuildModel(test.TestCase): self._clone_and_build_test_helper(model) + @test_util.run_deprecated_v1 def test_clone_and_build_functional_model(self): with self.cached_session(): input_a = keras.Input(shape=(4,)) @@ -394,6 +432,7 @@ class TestCloneAndBuildModel(test.TestCase): self._clone_and_build_test_helper(model) + @test_util.run_deprecated_v1 def test_clone_and_build_subclassed_model(self): class SubclassedModel(keras.Model): @@ -442,9 +481,11 @@ class TestCloneAndBuildModel(test.TestCase): def test_replace_tf_optimizer_iterations_variable(self): self.assert_optimizer_iterations_increases(adam.AdamOptimizer(0.01)) + @test_util.run_deprecated_v1 def test_replace_keras_optimizer_iterations_variable(self): self.assert_optimizer_iterations_increases('adam') + @test_util.run_deprecated_v1 def test_clone_and_build_sequential_model_without_inputs_defined(self): with self.cached_session(): model = sequential_model(False, False) diff --git a/tensorflow/python/keras/optimizer_v2/BUILD b/tensorflow/python/keras/optimizer_v2/BUILD index fb43775fdc..b8f0124941 100644 --- a/tensorflow/python/keras/optimizer_v2/BUILD +++ b/tensorflow/python/keras/optimizer_v2/BUILD @@ -7,14 +7,21 @@ licenses(["notice"]) # Apache 2.0 exports_files(["LICENSE"]) +load("//tensorflow:tensorflow.bzl", "py_test") load("//tensorflow:tensorflow.bzl", "cuda_py_test") py_library( name = "optimizer_v2", srcs = [ + "adadelta.py", + "adagrad.py", "adam.py", + "adamax.py", + "ftrl.py", "gradient_descent.py", + "nadam.py", "optimizer_v2.py", + "rmsprop.py", ], srcs_version = "PY2AND3", deps = [ @@ -24,12 +31,107 @@ py_library( "//tensorflow/python:math_ops", "//tensorflow/python:resource_variable_ops", "//tensorflow/python:state_ops", - "//tensorflow/python:training", "//tensorflow/python:variable_scope", "//tensorflow/python:variables", + "//tensorflow/python/distribute:reduce_util", ], ) +cuda_py_test( + name = "adagrad_test", + size = "medium", + srcs = ["adagrad_test.py"], + additional_deps = [ + ":optimizer_v2", + "//tensorflow/python:client_testlib", + "//tensorflow/python:embedding_ops", + "//tensorflow/python:platform_test", + "//tensorflow/python:framework", + "//tensorflow/python:math_ops", + "//tensorflow/python:resource_variable_ops", + "//tensorflow/python:resources", + "//tensorflow/python:variables", + "//tensorflow/python/eager:context", + ], + shard_count = 4, +) + +cuda_py_test( + name = "adam_test", + size = "medium", + srcs = ["adam_test.py"], + additional_deps = [ + ":optimizer_v2", + "//tensorflow/python:client_testlib", + "//tensorflow/python:embedding_ops", + "//tensorflow/python:platform_test", + "//tensorflow/python:framework", + "//tensorflow/python:math_ops", + "//tensorflow/python:resource_variable_ops", + "//tensorflow/python:resources", + "//tensorflow/python:variables", + "//tensorflow/python/eager:context", + ], + shard_count = 4, +) + +cuda_py_test( + name = "adamax_test", + size = "medium", + srcs = ["adamax_test.py"], + additional_deps = [ + ":optimizer_v2", + "//tensorflow/python:client_testlib", + "//tensorflow/python:embedding_ops", + "//tensorflow/python:platform_test", + "//tensorflow/python:framework", + "//tensorflow/python:math_ops", + "//tensorflow/python:resource_variable_ops", + "//tensorflow/python:resources", + "//tensorflow/python:variables", + "//tensorflow/python/eager:context", + ], + shard_count = 4, +) + +cuda_py_test( + name = "adadelta_test", + size = "medium", + srcs = ["adadelta_test.py"], + additional_deps = [ + ":optimizer_v2", + "//tensorflow/python:client_testlib", + "//tensorflow/python:embedding_ops", + "//tensorflow/python:platform_test", + "//tensorflow/python:framework", + "//tensorflow/python:math_ops", + "//tensorflow/python:resource_variable_ops", + "//tensorflow/python:resources", + "//tensorflow/python:variables", + "//tensorflow/python/eager:context", + ], + shard_count = 4, +) + +cuda_py_test( + name = "ftrl_test", + size = "medium", + srcs = ["ftrl_test.py"], + additional_deps = [ + ":optimizer_v2", + "//tensorflow/python:client_testlib", + "//tensorflow/python:embedding_ops", + "//tensorflow/python:platform_test", + "//tensorflow/python:framework", + "//tensorflow/python:math_ops", + "//tensorflow/python:resource_variable_ops", + "//tensorflow/python:resources", + "//tensorflow/python:variables", + "//tensorflow/python/eager:context", + ], + shard_count = 4, +) + cuda_py_test( name = "gradient_descent_test", size = "medium", @@ -50,9 +152,53 @@ cuda_py_test( ) cuda_py_test( - name = "optimizer_v2_test", + name = "nadam_test", size = "medium", + srcs = ["nadam_test.py"], + additional_deps = [ + ":optimizer_v2", + "//tensorflow/python:client_testlib", + "//tensorflow/python:embedding_ops", + "//tensorflow/python:platform_test", + "//tensorflow/python:framework", + "//tensorflow/python:math_ops", + "//tensorflow/python:resource_variable_ops", + "//tensorflow/python:resources", + "//tensorflow/python:variables", + "//tensorflow/python/eager:context", + ], + shard_count = 4, +) + +py_test( + name = "optimizer_v2_test", + size = "large", srcs = ["optimizer_v2_test.py"], + shard_count = 4, + tags = [ + "no_windows", + ], + deps = [ + ":optimizer_v2", + "//tensorflow/python:array_ops", + "//tensorflow/python:client_testlib", + "//tensorflow/python:clip_ops", + "//tensorflow/python:framework", + "//tensorflow/python:framework_test_lib", + "//tensorflow/python:gradients", + "//tensorflow/python:resource_variable_ops", + "//tensorflow/python:state_ops", + "//tensorflow/python:variables", + "//tensorflow/python/eager:def_function", + "//tensorflow/python/keras", + "@absl_py//absl/testing:parameterized", + ], +) + +cuda_py_test( + name = "rmsprop_test", + size = "medium", + srcs = ["rmsprop_test.py"], additional_deps = [ ":optimizer_v2", "//tensorflow/python/eager:def_function", diff --git a/tensorflow/python/keras/optimizer_v2/adadelta.py b/tensorflow/python/keras/optimizer_v2/adadelta.py new file mode 100644 index 0000000000..55b4eba105 --- /dev/null +++ b/tensorflow/python/keras/optimizer_v2/adadelta.py @@ -0,0 +1,148 @@ +# Copyright 2018 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. +# ============================================================================== + +"""Adadelta for TensorFlow.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +from tensorflow.python.keras.optimizer_v2 import optimizer_v2 +from tensorflow.python.training import training_ops + + +class Adadelta(optimizer_v2.OptimizerV2): + r"""Optimizer that implements the Adadelta algorithm. + + Adadelta optimization is a stochastic gradient descent method that is based on + adaptive learning rate per dimension to address two drawbacks: + 1) the continual decay of learning rates throughout training + 2) the need for a manually selected global learning rate + + Two accumulation steps are required: + 1) the accumulation of gradients squared, + 2) the accumulation of updates squared. + + Initialization: + + $$accum_g_0 := 0 \text{(Initialize gradient 2nd order moment vector)}$$ + $$accum_x_0 := 0 \text{(Initialize variable update 2nd order moment vector)}$$ + + $$t := t + 1$$ + $$accum_g_t := rho * accum_g_{t-1} + (1 - rho) * g * g$$ + $$delta = -\sqrt{accum_x_{t-1}} / (\sqrt{accum_g_{t-1}} + \epsilon)$$ + $$accum_x_t := rho * accum_x_{t-1} + (1 - rho) * delta * delta$$ + + References + See [M. D. Zeiler](http://arxiv.org/abs/1212.5701) + ([pdf](http://arxiv.org/pdf/1212.5701v1.pdf)) + + """ + + def __init__(self, + learning_rate=0.001, + rho=0.95, + epsilon=1e-7, + name='Adadelta', + **kwargs): + """Construct a new Adadelta optimizer. + + Adadelta is a more robust extension of Adagrad that adapts learning rates + based on a moving window of gradient updates, instead of accumulating all + past gradients. This way, Adadelta continues learning even when many updates + have been done. Compared to Adagrad, in the original version of Adadelta you + don't have to set an initial learning rate. In this version, initial + learning rate can be set, as in most other Keras optimizers. + + Args: + learning_rate: A `Tensor` or a floating point value. The learning rate. + To match the exact form in the original paper use 1.0. + rho: A `Tensor` or a floating point value. The decay rate. + epsilon: A `Tensor` or a floating point value. A constant epsilon used + to better conditioning the grad update. + name: Optional name prefix for the operations created when applying + gradients. Defaults to "Adadelta". + **kwargs: keyword arguments. Allowed to be {`decay`} + + @compatibility(eager) + When eager execution is enabled, `learning_rate`, `rho`, and `epsilon` can + each be a callable that takes no arguments and returns the actual value to + use. This can be useful for changing these values across different + invocations of optimizer functions. + @end_compatibility + """ + super(Adadelta, self).__init__(name, **kwargs) + self._set_hyper('learning_rate', learning_rate) + self._set_hyper('decay', self._initial_decay) + self._set_hyper('rho', rho) + self._set_hyper('epsilon', epsilon) + + def _create_slots(self, var_list): + # Separate for-loops to respect the ordering of slot variables from v1. + for v in var_list: + self.add_slot(v, 'accum_grad') + for v in var_list: + self.add_slot(v, 'accum_var') + + def set_weights(self, weights): + params = self.weights + # Override set_weights for backward compatibility of Keras V1 optimizer + # since it does not include iteration at head of the weight list. Set + # iteration to 0. + if len(params) == len(weights) + 1: + weights = [np.array(0)] + weights + super(Adadelta, self).set_weights(weights) + + def _resource_apply_dense(self, grad, var): + var_dtype = var.dtype.base_dtype + lr_t = self._decayed_lr(var_dtype) + accum_grad = self.get_slot(var, 'accum_grad') + accum_var = self.get_slot(var, 'accum_var') + return training_ops.resource_apply_adadelta( + var.handle, + accum_grad.handle, + accum_var.handle, + lr_t, + self._get_hyper('rho', var_dtype), + self._get_hyper('epsilon', var_dtype), + grad, + use_locking=self._use_locking) + + def _resource_apply_sparse(self, grad, var, indices): + var_dtype = var.dtype.base_dtype + lr_t = self._decayed_lr(var_dtype) + accum_grad = self.get_slot(var, 'accum_grad') + accum_var = self.get_slot(var, 'accum_var') + return training_ops.resource_sparse_apply_adadelta( + var.handle, + accum_grad.handle, + accum_var.handle, + lr_t, + self._get_hyper('rho', var_dtype), + self._get_hyper('epsilon', var_dtype), + grad, + indices, + use_locking=self._use_locking) + + def get_config(self): + config = super(Adadelta, self).get_config() + config.update({ + 'learning_rate': self._serialize_hyperparameter('learning_rate'), + 'decay': self._serialize_hyperparameter('decay'), + 'rho': self._serialize_hyperparameter('rho'), + 'epsilon': self._serialize_hyperparameter('epsilon'), + }) + return config diff --git a/tensorflow/python/keras/optimizer_v2/adadelta_test.py b/tensorflow/python/keras/optimizer_v2/adadelta_test.py new file mode 100644 index 0000000000..0fb67d0cd1 --- /dev/null +++ b/tensorflow/python/keras/optimizer_v2/adadelta_test.py @@ -0,0 +1,170 @@ +# Copyright 2015 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 Adadelta Optimizer.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +from tensorflow.python.eager import context +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util +from tensorflow.python.keras.optimizer_v2 import adadelta +from tensorflow.python.ops import embedding_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import resource_variable_ops +from tensorflow.python.ops import variables +from tensorflow.python.platform import test + + +class AdadeltaOptimizerTest(test.TestCase): + + def doTestBasic(self, use_resource=False, use_callable_params=False): + num_updates = 4 # number of ADADELTA steps to perform + for dtype in [dtypes.half, dtypes.float32]: + for grad in [0.2, 0.1, 0.01]: + for lr in [1.0, 0.5, 0.1]: + var0_init = [1.0, 2.0] + var1_init = [3.0, 4.0] + if use_resource: + var0 = resource_variable_ops.ResourceVariable( + var0_init, dtype=dtype) + var1 = resource_variable_ops.ResourceVariable( + var1_init, dtype=dtype) + else: + var0 = variables.Variable(var0_init, dtype=dtype) + var1 = variables.Variable(var1_init, dtype=dtype) + + grads = constant_op.constant([grad, grad], dtype=dtype) + + accum = 0.0 + accum_update = 0.0 + + # ADADELTA gradient optimizer + rho = 0.95 + epsilon = 1e-8 + if use_callable_params: + adadelta_opt = adadelta.Adadelta( + learning_rate=lambda: lr, # pylint: disable=cell-var-from-loop + rho=lambda: rho, # pylint: disable=cell-var-from-loop + epsilon=lambda: epsilon) # pylint: disable=cell-var-from-loop + else: + adadelta_opt = adadelta.Adadelta( + learning_rate=lr, rho=rho, epsilon=epsilon) + if not context.executing_eagerly(): + adadelta_update = adadelta_opt.apply_gradients( + zip([grads, grads], [var0, var1])) + self.evaluate(variables.global_variables_initializer()) + + # Assign slots + slot = [None] * 2 + slot_update = [None] * 2 + slot[0] = adadelta_opt.get_slot(var0, "accum_grad") + self.assertEqual(slot[0].get_shape(), var0.get_shape()) + + slot_update[0] = adadelta_opt.get_slot(var0, "accum_var") + self.assertEqual(slot_update[0].get_shape(), var0.get_shape()) + + slot[1] = adadelta_opt.get_slot(var1, "accum_grad") + self.assertEqual(slot[1].get_shape(), var1.get_shape()) + + slot_update[1] = adadelta_opt.get_slot(var1, "accum_var") + self.assertEqual(slot_update[1].get_shape(), var1.get_shape()) + + # Fetch params to validate initial values + self.assertAllClose(var0_init, self.evaluate(var0)) + self.assertAllClose(var1_init, self.evaluate(var1)) + + update = [None] * num_updates + tot_update = 0 + for step in range(num_updates): + # Run adadelta update for comparison + if not context.executing_eagerly(): + self.evaluate(adadelta_update) + else: + adadelta_opt.apply_gradients(zip([grads, grads], [var0, var1])) + + # Perform initial update without previous accum values + accum = accum * rho + (grad**2) * (1 - rho) + update[step] = ( + np.sqrt(accum_update + epsilon) * + (1. / np.sqrt(accum + epsilon)) * grad) + accum_update = ( + accum_update * rho + (update[step]**2) * (1.0 - rho)) + tot_update += update[step] * lr + + if not context.executing_eagerly(): + # Check that the accumulators have been updated + # TODO(lxuechen): This is hard to test in eager mode + for slot_idx in range(2): + self.assertAllCloseAccordingToType( + np.array([accum, accum], dtype=dtype.as_numpy_dtype()), + self.evaluate(slot[slot_idx]), + rtol=1e-5) + + self.assertAllCloseAccordingToType( + np.array( + [accum_update, accum_update], + dtype=dtype.as_numpy_dtype()), + self.evaluate(slot_update[slot_idx]), + rtol=1e-5) + + # Check that the parameters have been updated + self.assertAllCloseAccordingToType( + np.array( + [var0_init[0] - tot_update, var0_init[1] - tot_update], + dtype=dtype.as_numpy_dtype()), + self.evaluate(var0), + rtol=1e-5) + + self.assertAllCloseAccordingToType( + np.array( + [var1_init[0] - tot_update, var1_init[1] - tot_update], + dtype=dtype.as_numpy_dtype()), + self.evaluate(var1), + rtol=1e-5) + + @test_util.run_in_graph_and_eager_modes(reset_test=True) + def testResourceBasic(self): + self.doTestBasic(use_resource=True) + + def testBasicCallableParams(self): + with context.eager_mode(): + self.doTestBasic(use_resource=True, use_callable_params=True) + + @test_util.run_deprecated_v1 + def testMinimizeSparseResourceVariable(self): + for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: + with self.cached_session(): + var0 = resource_variable_ops.ResourceVariable([[1.0, 2.0]], dtype=dtype) + x = constant_op.constant([[4.0], [5.0]], dtype=dtype) + pred = math_ops.matmul(embedding_ops.embedding_lookup([var0], [0]), x) + loss = pred * pred + sgd_op = adadelta.Adadelta(1.0, 1.0, 1.0).minimize( + loss, var_list=[var0]) + variables.global_variables_initializer().run() + # Fetch params to validate initial values + self.assertAllCloseAccordingToType([[1.0, 2.0]], self.evaluate(var0)) + # Run 1 step of sgd + sgd_op.run() + # Validate updated params + self.assertAllCloseAccordingToType([[-111, -138]], self.evaluate(var0)) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/keras/optimizer_v2/adagrad.py b/tensorflow/python/keras/optimizer_v2/adagrad.py new file mode 100644 index 0000000000..670cad70e6 --- /dev/null +++ b/tensorflow/python/keras/optimizer_v2/adagrad.py @@ -0,0 +1,171 @@ +# Copyright 2018 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. +# ============================================================================== + +"""Adagrad for TensorFlow.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +from tensorflow.python.framework import ops +from tensorflow.python.keras.optimizer_v2 import optimizer_v2 +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import init_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import resource_variable_ops +from tensorflow.python.ops import state_ops + + +class Adagrad(optimizer_v2.OptimizerV2): + r"""Optimizer that implements the Adagrad algorithm. + + Adagrad is an optimizer with parameter-specific learning rates, + which are adapted relative to how frequently a parameter gets + updated during training. The more updates a parameter receives, + the smaller the updates. + + Initialization: + + $$accum_g_0 := initial_accumulator_value$$ + + $$t := t + 1$$ + $$accum_g_t := accum_g_{t-1} + g * g$$ + $$theta_t := theta_{t-1} - lr * g / (\sqrt{accum_g_t} + \epsilon)$$ + + References + See [paper] + (http://www.jmlr.org/papers/volume12/duchi11a/duchi11a.pdf) + or this + [intro](https://ppasupat.github.io/a9online/uploads/proximal_notes.pdf). + """ + + def __init__(self, + learning_rate=0.001, + initial_accumulator_value=0.1, + epsilon=1e-7, + name='Adagrad', + **kwargs): + """Construct a new Adagrad optimizer. + + Args: + learning_rate: A `Tensor` or a floating point value. The learning rate. + initial_accumulator_value: A floating point value. + Starting value for the accumulators, must be positive. + epsilon: A floating point value. + Starting value for the accumulators, must be positive. + name: Optional name prefix for the operations created when applying + gradients. Defaults to "Adagrad". + **kwargs: keyword arguments. Allowed to be {`decay`} + + Raises: + ValueError: If the `initial_accumulator_value` or `epsilon` is invalid. + + @compatibility(eager) + When eager execution is enabled, `learning_rate` can be a callable that + takes no arguments and returns the actual value to use. This can be useful + for changing these values across different invocations of optimizer + functions. + @end_compatibility + """ + if initial_accumulator_value < 0.0: + raise ValueError('initial_accumulator_value must be non-negative: %s' % + initial_accumulator_value) + if epsilon < 1e-7: + raise ValueError('epsilon must be larger than 1e-7: %s' % epsilon) + super(Adagrad, self).__init__(name, **kwargs) + self._set_hyper('learning_rate', learning_rate) + self._set_hyper('decay', self._initial_decay) + self._initial_accumulator_value = initial_accumulator_value + self._set_hyper('epsilon', epsilon) + + def _create_slots(self, var_list): + for var in var_list: + dtype = var.dtype.base_dtype + init = init_ops.constant_initializer( + self._initial_accumulator_value, dtype=dtype) + self.add_slot(var, 'accumulator', init) + + def set_weights(self, weights): + params = self.weights + # Override set_weights for backward compatibility of Keras V1 optimizer + # since it does not include iteration at head of the weight list. Set + # iteration to 0. + if len(params) == len(weights) + 1: + weights = [np.array(0)] + weights + super(Adagrad, self).set_weights(weights) + + @classmethod + def from_config(cls, config, custom_objects=None): + """Creates an optimizer from its config. + + This method is the reverse of `get_config`, + capable of instantiating the same optimizer from the config + dictionary. + + Arguments: + config: A Python dictionary, typically the output of get_config. + custom_objects: A Python dictionary mapping names to additional Python + objects used to create this optimizer, such as a function used for a + hyperparameter. + + Returns: + An optimizer instance. + """ + if 'initial_accumulator_value' not in config: + config['initial_accumulator_value'] = 0. + if 'lr' in config: + config['learning_rate'] = config.pop('lr') + return cls(**config) + + def _resource_apply_dense(self, grad, var): + var_dtype = var.dtype.base_dtype + lr_t = self._decayed_lr(var_dtype) + epsilon = self._get_hyper('epsilon', var_dtype) + acc = self.get_slot(var, 'accumulator') + + acc_t = state_ops.assign_add( + acc, math_ops.square(grad), use_locking=self._use_locking) + var_update = state_ops.assign_sub( + var, lr_t * grad / (math_ops.sqrt(acc_t) + epsilon)) + return var_update + + def _resource_apply_sparse(self, grad, var, indices): + + def _resource_scatter_add(x, i, v): + with ops.control_dependencies( + [resource_variable_ops.resource_scatter_add(x.handle, i, v)]): + return x.value() + + var_dtype = var.dtype.base_dtype + lr_t = self._decayed_lr(var_dtype) + epsilon = self._get_hyper('epsilon', var_dtype) + acc = self.get_slot(var, 'accumulator') + + acc_t = _resource_scatter_add(acc, indices, math_ops.square(grad)) + acc_t_slice = array_ops.gather(acc_t, indices) + var_update = _resource_scatter_add( + var, indices, -lr_t * grad / (math_ops.sqrt(acc_t_slice) + epsilon)) + return var_update + + def get_config(self): + config = super(Adagrad, self).get_config() + config.update({ + 'learning_rate': self._serialize_hyperparameter('learning_rate'), + 'decay': self._serialize_hyperparameter('decay'), + 'initial_accumulator_value': self._initial_accumulator_value, + 'epsilon': self._serialize_hyperparameter('epsilon'), + }) + return config diff --git a/tensorflow/python/keras/optimizer_v2/adagrad_test.py b/tensorflow/python/keras/optimizer_v2/adagrad_test.py new file mode 100644 index 0000000000..b2c290178f --- /dev/null +++ b/tensorflow/python/keras/optimizer_v2/adagrad_test.py @@ -0,0 +1,400 @@ +# Copyright 2015 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. +# ============================================================================== +"""Functional tests for aggregate operations.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import copy + +import numpy as np + +from tensorflow.python.eager import context +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util +from tensorflow.python.keras.optimizer_v2 import adagrad +from tensorflow.python.ops import embedding_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import resource_variable_ops +from tensorflow.python.ops import variables +from tensorflow.python.platform import test + + +def adagrad_update_numpy(param, accum, g_t, lr=0.001, epsilon=1e-7): + accum_t = accum + g_t * g_t + param_t = param - lr * g_t / (np.sqrt(accum_t) + epsilon) + return param_t, accum_t + + +def sparse_adagrad_update_numpy(param, + accum, + gindexs, + gvalues, + lr=0.001, + epsilon=1e-7): + accum_t = copy.deepcopy(accum) + param_t = copy.deepcopy(param) + # first loop accumulates repeated indices if necessary. + for i in range(len(gindexs)): + gindex = gindexs[i] + gvalue = gvalues[i] + accum_t[gindex] = accum_t[gindex] + gvalue * gvalue + for i in range(len(gindexs)): + gindex = gindexs[i] + gvalue = gvalues[i] + param_t[gindex] = param_t[gindex] - lr * gvalue / ( + np.sqrt(accum_t[gindex]) + epsilon) + return param_t, accum_t + + +class AdagradOptimizerTest(test.TestCase): + + def doTestBasic(self, use_callable_params=False): + for dtype in [dtypes.float32, dtypes.float64]: + with self.cached_session(): + var0_np = np.array([1.0, 2.0], dtype=dtype.as_numpy_dtype) + var1_np = np.array([3.0, 4.0], dtype=dtype.as_numpy_dtype) + grads0_np = np.array([0.1, 0.1], dtype=dtype.as_numpy_dtype) + grads1_np = np.array([0.01, 0.01], dtype=dtype.as_numpy_dtype) + var0 = resource_variable_ops.ResourceVariable(var0_np) + var1 = resource_variable_ops.ResourceVariable(var1_np) + grads0 = constant_op.constant(grads0_np) + grads1 = constant_op.constant(grads1_np) + + learning_rate = lambda: 3.0 + if not use_callable_params: + learning_rate = learning_rate() + + ada_opt = adagrad.Adagrad(learning_rate) + + accum0_np = np.array([0.1, 0.1], dtype=dtype.as_numpy_dtype) + accum1_np = np.array([0.1, 0.1], dtype=dtype.as_numpy_dtype) + + if not context.executing_eagerly(): + ada_update = ada_opt.apply_gradients( + zip([grads0, grads1], [var0, var1])) + self.evaluate(variables.global_variables_initializer()) + + # Fetch params to validate initial values + v0_val, v1_val = self.evaluate([var0, var1]) + self.assertAllClose([1.0, 2.0], v0_val) + self.assertAllClose([3.0, 4.0], v1_val) + + # Run 3 steps of adagrad + for _ in range(3): + if not context.executing_eagerly(): + self.evaluate(ada_update) + else: + ada_opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + var0_np, accum0_np = adagrad_update_numpy(var0_np, accum0_np, + grads0_np, 3.0) + var1_np, accum1_np = adagrad_update_numpy(var1_np, accum1_np, + grads1_np, 3.0) + self.assertAllCloseAccordingToType(var0_np, self.evaluate(var0)) + self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1)) + + @test_util.run_in_graph_and_eager_modes(reset_test=True) + def testBasic(self): + self.doTestBasic() + + def testBasicCallableParams(self): + with context.eager_mode(): + self.doTestBasic(use_callable_params=True) + + def testBasicWithLearningRateDecay(self): + for dtype in [dtypes.float32, dtypes.float64]: + with self.cached_session(): + var0_np = np.array([1.0, 2.0], dtype=dtype.as_numpy_dtype) + var1_np = np.array([3.0, 4.0], dtype=dtype.as_numpy_dtype) + grads0_np = np.array([0.1, 0.1], dtype=dtype.as_numpy_dtype) + grads1_np = np.array([0.01, 0.01], dtype=dtype.as_numpy_dtype) + var0 = resource_variable_ops.ResourceVariable(var0_np) + var1 = resource_variable_ops.ResourceVariable(var1_np) + grads0 = constant_op.constant(grads0_np) + grads1 = constant_op.constant(grads1_np) + + learning_rate = 3.0 + decay = 0.5 + + ada_opt = adagrad.Adagrad(learning_rate, decay=decay) + + accum0_np = np.array([0.1, 0.1], dtype=dtype.as_numpy_dtype) + accum1_np = np.array([0.1, 0.1], dtype=dtype.as_numpy_dtype) + + if not context.executing_eagerly(): + ada_update = ada_opt.apply_gradients( + zip([grads0, grads1], [var0, var1])) + self.evaluate(variables.global_variables_initializer()) + + # Fetch params to validate initial values + v0_val, v1_val = self.evaluate([var0, var1]) + self.assertAllClose([1.0, 2.0], v0_val) + self.assertAllClose([3.0, 4.0], v1_val) + + # Run 3 steps of adagrad + for t in range(3): + if not context.executing_eagerly(): + self.evaluate(ada_update) + else: + ada_opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + lr_np = learning_rate / (1 + decay * t) + var0_np, accum0_np = adagrad_update_numpy(var0_np, accum0_np, + grads0_np, lr_np) + var1_np, accum1_np = adagrad_update_numpy(var1_np, accum1_np, + grads1_np, lr_np) + self.assertAllCloseAccordingToType(var0_np, self.evaluate(var0)) + self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1)) + + @test_util.run_deprecated_v1 + def testMinimizeSparseResourceVariable(self): + for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: + with self.cached_session(): + var0 = resource_variable_ops.ResourceVariable( + [[1.0, 2.0], [3.0, 4.0]], dtype=dtype) + x = constant_op.constant([[4.0], [5.0]], dtype=dtype) + pred = math_ops.matmul(embedding_ops.embedding_lookup([var0], [0]), x) + loss = pred * pred + sgd_op = adagrad.Adagrad(1.0).minimize(loss, var_list=[var0]) + variables.global_variables_initializer().run() + # Fetch params to validate initial values + self.assertAllCloseAccordingToType( + [[1.0, 2.0], [3.0, 4.0]], var0.eval()) + # Run 1 step of sgd + sgd_op.run() + # Validate updated params + self.assertAllCloseAccordingToType( + [[0, 1], [3, 4]], var0.eval(), atol=0.01) + + @test_util.run_deprecated_v1 + def testTensorLearningRate(self): + for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: + with self.cached_session(): + var0_np = np.array([1.0, 2.0], dtype=dtype.as_numpy_dtype) + var1_np = np.array([3.0, 4.0], dtype=dtype.as_numpy_dtype) + grads0_np = np.array([0.1, 0.1], dtype=dtype.as_numpy_dtype) + grads1_np = np.array([0.01, 0.01], dtype=dtype.as_numpy_dtype) + var0 = resource_variable_ops.ResourceVariable(var0_np) + var1 = resource_variable_ops.ResourceVariable(var1_np) + grads0 = constant_op.constant(grads0_np) + grads1 = constant_op.constant(grads1_np) + + learning_rate = constant_op.constant(3.0) + ada_opt = adagrad.Adagrad(learning_rate) + ada_update = ada_opt.apply_gradients( + zip([grads0, grads1], [var0, var1])) + variables.global_variables_initializer().run() + # Fetch params to validate initial values + self.assertAllClose([1.0, 2.0], var0.eval()) + self.assertAllClose([3.0, 4.0], var1.eval()) + accum0_np = np.array([0.1, 0.1], dtype=dtype.as_numpy_dtype) + accum1_np = np.array([0.1, 0.1], dtype=dtype.as_numpy_dtype) + # Run 3 steps of adagrad + for _ in range(3): + ada_update.run() + var0_np, accum0_np = adagrad_update_numpy(var0_np, accum0_np, + grads0_np, learning_rate) + var1_np, accum1_np = adagrad_update_numpy(var1_np, accum1_np, + grads1_np, learning_rate) + self.assertAllCloseAccordingToType(var0_np, self.evaluate(var0)) + self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1)) + + @test_util.run_deprecated_v1 + def testSparseBasic(self): + for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: + with self.cached_session(): + var0_np = np.array([1.0, 1.0, 2.0], dtype=dtype.as_numpy_dtype) + grads0_np = np.array([0.1, 0, 0.1], dtype=dtype.as_numpy_dtype) + var1_np = np.array([3.0, 3.0, 4.0], dtype=dtype.as_numpy_dtype) + grads1_np = np.array([0.01, 0, 0.01], dtype=dtype.as_numpy_dtype) + + var0 = resource_variable_ops.ResourceVariable(var0_np) + var1 = resource_variable_ops.ResourceVariable(var1_np) + grads0_np_indices = np.array([0, 2], dtype=np.int32) + grads0 = ops.IndexedSlices( + constant_op.constant(grads0_np[grads0_np_indices]), + constant_op.constant(grads0_np_indices), constant_op.constant([3])) + grads1_np_indices = np.array([0, 2], dtype=np.int32) + grads1 = ops.IndexedSlices( + constant_op.constant(grads1_np[grads1_np_indices]), + constant_op.constant(grads1_np_indices), constant_op.constant([3])) + learning_rate = 3.0 + ada_opt = adagrad.Adagrad(learning_rate) + ada_update = ada_opt.apply_gradients( + zip([grads0, grads1], [var0, var1])) + variables.global_variables_initializer().run() + + # Fetch params to validate initial values + self.assertAllClose([1.0, 1.0, 2.0], var0.eval()) + self.assertAllClose([3.0, 3.0, 4.0], var1.eval()) + + accum0_np = np.array([0.1, 0.1, 0.1], dtype=dtype.as_numpy_dtype) + accum1_np = np.array([0.1, 0.1, 0.1], dtype=dtype.as_numpy_dtype) + + # Run 3 step of sgd + for _ in range(3): + ada_update.run() + + var0_np, accum0_np = sparse_adagrad_update_numpy( + var0_np, accum0_np, grads0_np_indices, + grads0_np[grads0_np_indices], learning_rate) + var1_np, accum1_np = sparse_adagrad_update_numpy( + var1_np, accum1_np, grads1_np_indices, + grads1_np[grads1_np_indices], learning_rate) + self.assertAllCloseAccordingToType(var0_np, self.evaluate(var0)) + self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1)) + + @test_util.run_deprecated_v1 + def testSparseRepeatedIndices(self): + for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: + with self.cached_session(): + var_np = np.array([[1.0], [2.0]], dtype=dtype.as_numpy_dtype) + + repeated_index_update_var = resource_variable_ops.ResourceVariable( + var_np, dtype=dtype) + aggregated_update_var = resource_variable_ops.ResourceVariable( + var_np, dtype=dtype) + grad_repeated_index = ops.IndexedSlices( + constant_op.constant( + [0.1, 0.1], shape=[2, 1], dtype=dtype), + constant_op.constant([1, 1]), + constant_op.constant([2, 1])) + grad_aggregated = ops.IndexedSlices( + constant_op.constant( + [0.2], shape=[1, 1], dtype=dtype), + constant_op.constant([1]), + constant_op.constant([2, 1])) + repeated_update = adagrad.Adagrad(3.0).apply_gradients( + [(grad_repeated_index, repeated_index_update_var)]) + aggregated_update = adagrad.Adagrad(3.0).apply_gradients( + [(grad_aggregated, aggregated_update_var)]) + variables.global_variables_initializer().run() + self.assertAllClose(aggregated_update_var.eval(), + repeated_index_update_var.eval()) + for _ in range(3): + repeated_update.run() + aggregated_update.run() + self.assertAllClose(aggregated_update_var.eval(), + repeated_index_update_var.eval()) + + @test_util.run_deprecated_v1 + def testSparseRepeatedIndicesByEmbeddingLookUp(self): + for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: + with self.cached_session(): + var_repeated = resource_variable_ops.ResourceVariable( + [1.0, 2.0], dtype=dtype) + loss_repeated = math_ops.reduce_sum( + embedding_ops.embedding_lookup(var_repeated, [0, 0])) + var_aggregated = resource_variable_ops.ResourceVariable( + [1.0, 2.0], dtype=dtype) + loss_aggregated = 2 * math_ops.reduce_sum( + embedding_ops.embedding_lookup(var_aggregated, [0])) + update_op_repeated = adagrad.Adagrad(2.0).minimize( + loss_repeated, var_list=[var_repeated]) + update_op_aggregated = adagrad.Adagrad(2.0).minimize( + loss_aggregated, var_list=[var_aggregated]) + variables.global_variables_initializer().run() + self.assertAllCloseAccordingToType( + var_repeated.eval(), var_aggregated.eval()) + for _ in range(3): + update_op_repeated.run() + update_op_aggregated.run() + self.assertAllCloseAccordingToType( + var_repeated.eval(), var_aggregated.eval()) + + @test_util.run_deprecated_v1 + def testSparseStability(self): + for dtype in [dtypes.half]: + with self.cached_session(): + shape = [1, 6] + var0_np = np.array([[ + 0.00872496, -0.106952, 0.110467, 0.226505, -0.0147257, -0.0105945 + ]], + dtype=dtype.as_numpy_dtype) + var0 = resource_variable_ops.ResourceVariable(var0_np) + grads0_np = np.array([[ + -5.91278e-05, 5.31673e-05, -2.5779e-06, 4.29153e-05, -8.4877e-05, + -9.48906e-05 + ]], + dtype=dtype.as_numpy_dtype) + grads0 = ops.IndexedSlices( + constant_op.constant(grads0_np), constant_op.constant([0]), + constant_op.constant(shape)) + ada_opt = adagrad.Adagrad(1.0) + ada_update = ada_opt.apply_gradients(zip([grads0], [var0])) + slot0 = ada_opt.get_slot(var0, "accumulator") + init = variables.global_variables_initializer() + for _ in range(100): + init.run() + ada_update.run() + self.assertAllCloseAccordingToType( + np.array([[0.1, 0.1, 0.1, 0.1, 0.1, 0.1]]), slot0.eval()) + self.assertAllCloseAccordingToType( + np.array([[ + 0.00891194, -0.10712013, 0.11047515, 0.22636929, -0.0144573, + -0.01029443 + ]]), var0.eval()) + + @test_util.run_deprecated_v1 + def testSharing(self): + for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: + with self.cached_session(): + var0_np = np.array([1.0, 2.0], dtype=dtype.as_numpy_dtype) + grads0_np = np.array([0.1, 0.1], dtype=dtype.as_numpy_dtype) + var1_np = np.array([3.0, 4.0], dtype=dtype.as_numpy_dtype) + grads1_np = np.array([0.01, 0.01], dtype=dtype.as_numpy_dtype) + + var0 = resource_variable_ops.ResourceVariable(var0_np) + var1 = resource_variable_ops.ResourceVariable(var1_np) + grads0 = constant_op.constant(grads0_np) + grads1 = constant_op.constant(grads1_np) + + learning_rate = 3.0 + ada_opt = adagrad.Adagrad(learning_rate) + # Apply the optimizer twice. Both applications will use + # the same accums. + ada_update1 = ada_opt.apply_gradients( + zip([grads0, grads1], [var0, var1])) + ada_update2 = ada_opt.apply_gradients( + zip([grads0, grads1], [var0, var1])) + slot0 = ada_opt.get_slot(var0, "accumulator") + self.assertEqual(slot0.get_shape(), var0.get_shape()) + slot1 = ada_opt.get_slot(var1, "accumulator") + self.assertEqual(slot1.get_shape(), var1.get_shape()) + variables.global_variables_initializer().run() + + # Fetch params to validate initial values. + self.assertAllClose([1.0, 2.0], var0.eval()) + self.assertAllClose([3.0, 4.0], var1.eval()) + # Mix the first and the second adagrad for 3 steps. + ada_update1.run() + ada_update2.run() + ada_update1.run() + + accum0_np = np.array([0.1, 0.1], dtype=dtype.as_numpy_dtype) + accum1_np = np.array([0.1, 0.1], dtype=dtype.as_numpy_dtype) + for _ in range(3): + var0_np, accum0_np = adagrad_update_numpy(var0_np, accum0_np, + grads0_np, learning_rate) + var1_np, accum1_np = adagrad_update_numpy(var1_np, accum1_np, + grads1_np, learning_rate) + self.assertAllCloseAccordingToType(var0_np, self.evaluate(var0)) + self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1)) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/keras/optimizer_v2/adam.py b/tensorflow/python/keras/optimizer_v2/adam.py index b05811c419..ef3d783f89 100644 --- a/tensorflow/python/keras/optimizer_v2/adam.py +++ b/tensorflow/python/keras/optimizer_v2/adam.py @@ -1,4 +1,4 @@ -# Copyright 2015 The TensorFlow Authors. All Rights Reserved. +# Copyright 2018 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. @@ -17,8 +17,12 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +from tensorflow.python.framework import ops from tensorflow.python.keras.optimizer_v2 import optimizer_v2 +from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import math_ops +from tensorflow.python.ops import resource_variable_ops +from tensorflow.python.ops import state_ops from tensorflow.python.training import training_ops @@ -31,36 +35,62 @@ class Adam(optimizer_v2.OptimizerV2): requirement, invariant to diagonal rescaling of gradients, and is well suited for problems that are large in terms of data/parameters'. + Note, amsgrad is currently not supported and the argument can only be False. + # References See [Kingma et al., 2014](http://arxiv.org/abs/1412.6980) ([pdf](http://arxiv.org/pdf/1412.6980.pdf)). + For AMSGrad see [Reddi et al., 2-18] + (https://openreview.net/pdf?id=ryQu7f-RZ) """ def __init__(self, learning_rate=0.001, beta_1=0.9, beta_2=0.999, - epsilon=1e-8, - name='Adam'): + epsilon=1e-7, + amsgrad=False, + name='Adam', + **kwargs): r"""Construct a new Adam optimizer. - Initialization: + If amsgrad = False: + Initialization: + + $$m_0 := 0 \text{(Initialize initial 1st moment vector)}$$ + $$v_0 := 0 \text{(Initialize initial 2nd moment vector)}$$ + $$t := 0 \text{(Initialize timestep)}$$ + + The update rule for `variable` with gradient `g` uses an optimization + described at the end of section2 of the paper: + + $$t := t + 1$$ + $$lr_t := \text{learning\_rate} * \sqrt{1 - beta_2^t} / (1 - beta_1^t)$$ - $$m_0 := 0 \text{(Initialize initial 1st moment vector)}$$ - $$v_0 := 0 \text{(Initialize initial 2nd moment vector)}$$ - $$t := 0 \text{(Initialize timestep)}$$ + $$m_t := beta_1 * m_{t-1} + (1 - beta_1) * g$$ + $$v_t := beta_2 * v_{t-1} + (1 - beta_2) * g * g$$ + $$variable := variable - lr_t * m_t / (\sqrt{v_t} + \epsilon)$$ - The update rule for `variable` with gradient `g` uses an optimization - described at the end of section2 of the paper: + If amsgrad = True: + Initialization: - $$t := t + 1$$ - $$lr_t := \text{learning\_rate} * \sqrt{1 - beta_2^t} / (1 - beta_1^t)$$ + $$m_0 := 0 \text{(Initialize initial 1st moment vector)}$$ + $$v_0 := 0 \text{(Initialize initial 2nd moment vector)}$$ + $$v_hat_0 := 0 \text{(Initialize initial 2nd moment vector)}$$ + $$t := 0 \text{(Initialize timestep)}$$ - $$m_t := beta_1 * m_{t-1} + (1 - beta_1) * g$$ - $$v_t := beta_2 * v_{t-1} + (1 - beta_2) * g * g$$ - $$variable := variable - lr_t * m_t / (\sqrt{v_t} + \epsilon)$$ + The update rule for `variable` with gradient `g` uses an optimization + described at the end of section2 of the paper: - The default value of 1e-8 for epsilon might not be a good default in + $$t := t + 1$$ + $$lr_t := \text{learning\_rate} * \sqrt{1 - beta_2^t} / (1 - beta_1^t)$$ + + $$m_t := beta_1 * m_{t-1} + (1 - beta_1) * g$$ + $$v_t := beta_2 * v_{t-1} + (1 - beta_2) * g * g$$ + $$v_hat_t := max(v_hat_{t-1}, v_t) + $$variable := variable - lr_t * m_t / (\sqrt{v_hat_t} + \epsilon)$$ + + The default value of 1e-7 for epsilon might not be a good default in general. For example, when training an Inception network on ImageNet a current good choice is 1.0 or 0.1. Note that since AdamOptimizer uses the formulation just before Section 2.1 of the Kingma and Ba paper rather than @@ -85,50 +115,142 @@ class Adam(optimizer_v2.OptimizerV2): epsilon: A small constant for numerical stability. This epsilon is "epsilon hat" in the Kingma and Ba paper (in the formula just before Section 2.1), not the epsilon in Algorithm 1 of the paper. + amsgrad: boolean. Whether to apply AMSGrad variant of this algorithm from + the paper "On the Convergence of Adam and beyond". name: Optional name for the operations created when applying gradients. Defaults to "Adam". @compatibility(eager) When eager execution is enabled, `learning_rate`, `beta_1`, `beta_2`, and `epsilon` can each be a callable that takes no arguments and returns the actual value to use. This can be useful for changing these values across different invocations of optimizer functions. @end_compatibility + **kwargs: keyword arguments. Allowed to be {`decay`} """ - super(Adam, self).__init__(name) + super(Adam, self).__init__(name, **kwargs) self._set_hyper('learning_rate', learning_rate) + self._set_hyper('decay', self._initial_decay) self._set_hyper('beta_1', beta_1) self._set_hyper('beta_2', beta_2) self._set_hyper('epsilon', epsilon) + self._amsgrad = amsgrad def _create_slots(self, var_list): # Create slots for the first and second moments. + # Separate for-loops to respect the ordering of slot variables from v1. for var in var_list: self.add_slot(var, 'm') + for var in var_list: self.add_slot(var, 'v') + if self._amsgrad: + for var in var_list: + self.add_slot(var, 'vhat') + + def set_weights(self, weights): + params = self.weights + # If the weights are generated by Keras V1 optimizer, it includes vhats + # even without amsgrad, i.e, V1 optimizer has 3x + 1 variables, while V2 + # optimizer has 2x + 1 variables. Filter vhats out for compatibility. + num_vars = int((len(params) - 1) / 2) + if len(weights) == 3 * num_vars + 1: + weights = weights[:len(params)] + super(Adam, self).set_weights(weights) def _resource_apply_dense(self, grad, var): + var_dtype = var.dtype.base_dtype + lr_t = self._decayed_lr(var_dtype) m = self.get_slot(var, 'm') v = self.get_slot(var, 'v') - # TODO(tanzheny): let optimizer have its own step counter, and let - # beta1_power and beta2_power depend on it. - return training_ops.resource_apply_adam( - var.handle, - m.handle, - v.handle, - math_ops.cast(self._get_hyper('beta_1'), grad.dtype.base_dtype), - math_ops.cast(self._get_hyper('beta_2'), grad.dtype.base_dtype), - math_ops.cast(self._get_hyper('learning_rate'), grad.dtype.base_dtype), - math_ops.cast(self._get_hyper('beta_1'), grad.dtype.base_dtype), - math_ops.cast(self._get_hyper('beta_2'), grad.dtype.base_dtype), - math_ops.cast(self._get_hyper('epsilon'), grad.dtype.base_dtype), - grad, - use_locking=self._use_locking) + beta_1_t = self._get_hyper('beta_1', var_dtype) + beta_2_t = self._get_hyper('beta_2', var_dtype) + epsilon = self._get_hyper('epsilon', var_dtype) + local_step = math_ops.cast(self.iterations + 1, var_dtype) + beta_1_power = math_ops.pow(beta_1_t, local_step) + beta_2_power = math_ops.pow(beta_2_t, local_step) + if not self._amsgrad: + return training_ops.resource_apply_adam( + var.handle, + m.handle, + v.handle, + beta_1_power, + beta_2_power, + lr_t, + beta_1_t, + beta_2_t, + epsilon, + grad, + use_locking=self._use_locking) + else: + vhat = self.get_slot(var, 'vhat') + return training_ops.resource_apply_adam_with_amsgrad( + var.handle, + m.handle, + v.handle, + vhat.handle, + beta_1_power, + beta_2_power, + lr_t, + beta_1_t, + beta_2_t, + epsilon, + grad, + use_locking=self._use_locking) + + def _resource_apply_sparse(self, grad, var, indices): + var_dtype = var.dtype.base_dtype + lr_t = self._decayed_lr(var_dtype) + beta_1_t = self._get_hyper('beta_1', var_dtype) + beta_2_t = self._get_hyper('beta_2', var_dtype) + local_step = math_ops.cast(self.iterations + 1, var_dtype) + beta_1_power = math_ops.pow(beta_1_t, local_step) + beta_2_power = math_ops.pow(beta_2_t, local_step) + epsilon_t = self._get_hyper('epsilon', var_dtype) + lr = (lr_t * math_ops.sqrt(1 - beta_2_power) / (1 - beta_1_power)) + + # m_t = beta1 * m + (1 - beta1) * g_t + m = self.get_slot(var, 'm') + m_scaled_g_values = grad * (1 - beta_1_t) + m_t = state_ops.assign(m, m * beta_1_t, use_locking=self._use_locking) + with ops.control_dependencies([m_t]): + m_t = self._resource_scatter_add(m, indices, m_scaled_g_values) + + # v_t = beta2 * v + (1 - beta2) * (g_t * g_t) + v = self.get_slot(var, 'v') + v_scaled_g_values = (grad * grad) * (1 - beta_2_t) + v_t = state_ops.assign(v, v * beta_2_t, use_locking=self._use_locking) + with ops.control_dependencies([v_t]): + v_t = self._resource_scatter_add(v, indices, v_scaled_g_values) + + if not self._amsgrad: + v_sqrt = math_ops.sqrt(v_t) + var_update = state_ops.assign_sub( + var, lr * m_t / (v_sqrt + epsilon_t), use_locking=self._use_locking) + return control_flow_ops.group(*[var_update, m_t, v_t]) + else: + v_hat = self.get_slot(var, 'vhat') + v_hat_t = math_ops.maximum(v_hat, v_t) + with ops.control_dependencies([v_hat_t]): + v_hat_t = state_ops.assign( + v_hat, v_hat_t, use_locking=self._use_locking) + v_hat_sqrt = math_ops.sqrt(v_hat_t) + var_update = state_ops.assign_sub( + var, + lr * m_t / (v_hat_sqrt + epsilon_t), + use_locking=self._use_locking) + return control_flow_ops.group(*[var_update, m_t, v_t, v_hat_t]) + + def _resource_scatter_add(self, x, i, v): + with ops.control_dependencies( + [resource_variable_ops.resource_scatter_add(x.handle, i, v)]): + return x.value() def get_config(self): config = super(Adam, self).get_config() config.update({ 'learning_rate': self._serialize_hyperparameter('learning_rate'), + 'decay': self._serialize_hyperparameter('decay'), 'beta_1': self._serialize_hyperparameter('beta_1'), 'beta_2': self._serialize_hyperparameter('beta_2'), 'epsilon': self._serialize_hyperparameter('epsilon'), + 'amsgrad': self._amsgrad, }) return config diff --git a/tensorflow/python/keras/optimizer_v2/adam_test.py b/tensorflow/python/keras/optimizer_v2/adam_test.py new file mode 100644 index 0000000000..3bbafe12f8 --- /dev/null +++ b/tensorflow/python/keras/optimizer_v2/adam_test.py @@ -0,0 +1,508 @@ +# Copyright 2018 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 Adam.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +from tensorflow.python.eager import context +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util +from tensorflow.python.keras import optimizers +from tensorflow.python.keras.optimizer_v2 import adam +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import resource_variable_ops +from tensorflow.python.ops import variables +from tensorflow.python.platform import test + + +def adam_update_numpy(param, + g_t, + t, + m, + v, + lr=0.001, + beta1=0.9, + beta2=0.999, + epsilon=1e-7): + lr_t = lr * np.sqrt(1 - beta2**(t + 1)) / (1 - beta1**(t + 1)) + + m_t = beta1 * m + (1 - beta1) * g_t + v_t = beta2 * v + (1 - beta2) * g_t * g_t + + param_t = param - lr_t * m_t / (np.sqrt(v_t) + epsilon) + return param_t, m_t, v_t + + +def adam_update_numpy_amsgrad(param, + g_t, + t, + m, + v, + vhat, + lr=0.001, + beta1=0.9, + beta2=0.999, + epsilon=1e-7): + lr_t = lr * np.sqrt(1 - beta2**(t + 1)) / (1 - beta1**(t + 1)) + + m_t = beta1 * m + (1 - beta1) * g_t + v_t = beta2 * v + (1 - beta2) * g_t * g_t + vhat_t = np.maximum(vhat, v_t) + + param_t = param - lr_t * m_t / (np.sqrt(vhat_t) + epsilon) + return param_t, m_t, v_t, vhat_t + + +def adam_sparse_update_numpy_amsgrad(param, + indices, + g_t, + t, + m, + v, + vhat, + lr=0.001, + beta1=0.9, + beta2=0.999, + epsilon=1e-7): + m_t, v_t, vhat_t, param_t = (np.copy(m), np.copy(v), np.copy(vhat), + np.copy(param)) + lr_t = lr * np.sqrt(1 - beta2**(t + 1)) / (1 - beta1**(t + 1)) + m_t_slice = beta1 * m[indices] + (1 - beta1) * g_t + v_t_slice = beta2 * v[indices] + (1 - beta2) * g_t * g_t + m_t[indices] = m_t_slice + v_t[indices] = v_t_slice + v_hat_t = np.maximum(vhat_t, v_t) + v_hat_t_slice = v_hat_t[indices] + param_t_slice = param[indices] - ( + lr_t * (m_t_slice / (np.sqrt(v_hat_t_slice) + epsilon))) + param_t[indices] = param_t_slice + return param_t, m_t, v_t, vhat_t + + +def get_beta_accumulators(opt, dtype): + local_step = math_ops.cast(opt.iterations + 1, dtype) + beta_1_t = math_ops.cast(opt._get_hyper("beta_1"), dtype) + beta_1_power = math_ops.pow(beta_1_t, local_step) + beta_2_t = math_ops.cast(opt._get_hyper("beta_2"), dtype) + beta_2_power = math_ops.pow(beta_2_t, local_step) + return (beta_1_power, beta_2_power) + + +class AdamOptimizerTest(test.TestCase): + + @test_util.run_deprecated_v1 + def testSparse(self): + for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: + with self.cached_session(): + # Initialize variables for numpy implementation. + m0, v0, m1, v1 = 0.0, 0.0, 0.0, 0.0 + var0_np = np.array([1.0, 1.0, 2.0], dtype=dtype.as_numpy_dtype) + grads0_np = np.array([0.1, 0.0, 0.1], dtype=dtype.as_numpy_dtype) + var1_np = np.array([3.0, 3.0, 4.0], dtype=dtype.as_numpy_dtype) + grads1_np = np.array([0.01, 0.0, 0.01], dtype=dtype.as_numpy_dtype) + + var0 = resource_variable_ops.ResourceVariable(var0_np) + var1 = resource_variable_ops.ResourceVariable(var1_np) + grads0_np_indices = np.array([0, 2], dtype=np.int32) + grads0 = ops.IndexedSlices( + constant_op.constant(grads0_np[grads0_np_indices]), + constant_op.constant(grads0_np_indices), constant_op.constant([3])) + grads1_np_indices = np.array([0, 2], dtype=np.int32) + grads1 = ops.IndexedSlices( + constant_op.constant(grads1_np[grads1_np_indices]), + constant_op.constant(grads1_np_indices), constant_op.constant([3])) + opt = adam.Adam() + update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + variables.global_variables_initializer().run() + + # Fetch params to validate initial values + self.assertAllClose([1.0, 1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 3.0, 4.0], self.evaluate(var1)) + + beta_1_power, beta_2_power = get_beta_accumulators(opt, dtype) + # Run 3 steps of Adam + for t in range(3): + self.assertAllCloseAccordingToType(0.9**(t + 1), + self.evaluate(beta_1_power)) + self.assertAllCloseAccordingToType(0.999**(t + 1), + self.evaluate(beta_2_power)) + update.run() + + var0_np, m0, v0 = adam_update_numpy(var0_np, grads0_np, t, m0, v0) + var1_np, m1, v1 = adam_update_numpy(var1_np, grads1_np, t, m1, v1) + + # Validate updated params + self.assertAllCloseAccordingToType(var0_np, self.evaluate(var0)) + self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1)) + + @test_util.run_deprecated_v1 + def testSparseDevicePlacement(self): + for index_dtype in [dtypes.int32, dtypes.int64]: + with self.cached_session(force_gpu=test.is_gpu_available()): + # If a GPU is available, tests that all optimizer ops can be placed on + # it (i.e. they have GPU kernels). + var = variables.Variable([[1.0], [2.0]]) + indices = constant_op.constant([0, 1], dtype=index_dtype) + gathered_sum = math_ops.reduce_sum(array_ops.gather(var, indices)) + optimizer = adam.Adam(3.0) + minimize_op = optimizer.minimize(gathered_sum, var_list=[var]) + variables.global_variables_initializer().run() + minimize_op.run() + + @test_util.run_deprecated_v1 + def testSparseRepeatedIndices(self): + for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: + with self.cached_session(): + repeated_index_update_var = variables.Variable( + [[1.0], [2.0]], dtype=dtype) + aggregated_update_var = variables.Variable( + [[1.0], [2.0]], dtype=dtype) + grad_repeated_index = ops.IndexedSlices( + constant_op.constant( + [0.1, 0.1], shape=[2, 1], dtype=dtype), + constant_op.constant([1, 1]), + constant_op.constant([2, 1])) + grad_aggregated = ops.IndexedSlices( + constant_op.constant( + [0.2], shape=[1, 1], dtype=dtype), + constant_op.constant([1]), + constant_op.constant([2, 1])) + repeated_update = adam.Adam().apply_gradients( + [(grad_repeated_index, repeated_index_update_var)]) + aggregated_update = adam.Adam().apply_gradients( + [(grad_aggregated, aggregated_update_var)]) + variables.global_variables_initializer().run() + self.assertAllClose(aggregated_update_var.eval(), + self.evaluate(repeated_index_update_var)) + for _ in range(3): + repeated_update.run() + aggregated_update.run() + self.assertAllClose(aggregated_update_var.eval(), + self.evaluate(repeated_index_update_var)) + + def doTestBasic(self, use_callable_params=False): + for i, dtype in enumerate([dtypes.half, dtypes.float32, dtypes.float64]): + with self.session(graph=ops.Graph()): + # Initialize variables for numpy implementation. + m0, v0, m1, v1 = 0.0, 0.0, 0.0, 0.0 + var0_np = np.array([1.0, 2.0], dtype=dtype.as_numpy_dtype) + grads0_np = np.array([0.1, 0.1], dtype=dtype.as_numpy_dtype) + var1_np = np.array([3.0, 4.0], dtype=dtype.as_numpy_dtype) + grads1_np = np.array([0.01, 0.01], dtype=dtype.as_numpy_dtype) + + var0 = resource_variable_ops.ResourceVariable( + var0_np, name="var0_%d" % i) + var1 = resource_variable_ops.ResourceVariable( + var1_np, name="var1_%d" % i) + grads0 = constant_op.constant(grads0_np) + grads1 = constant_op.constant(grads1_np) + + learning_rate = lambda: 0.001 + beta1 = lambda: 0.9 + beta2 = lambda: 0.999 + epsilon = lambda: 1e-8 + if not use_callable_params: + learning_rate = learning_rate() + beta1 = beta1() + beta2 = beta2() + epsilon = epsilon() + + opt = adam.Adam(learning_rate=learning_rate) + if not context.executing_eagerly(): + update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + + self.evaluate(variables.global_variables_initializer()) + # Run 3 steps of Adam + for t in range(3): + beta_1_power, beta_2_power = get_beta_accumulators(opt, dtype) + self.assertAllCloseAccordingToType(0.9**(t + 1), + self.evaluate(beta_1_power)) + self.assertAllCloseAccordingToType(0.999**(t + 1), + self.evaluate(beta_2_power)) + if not context.executing_eagerly(): + self.evaluate(update) + else: + opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + + var0_np, m0, v0 = adam_update_numpy(var0_np, grads0_np, t, m0, v0) + var1_np, m1, v1 = adam_update_numpy(var1_np, grads1_np, t, m1, v1) + + # Validate updated params + self.assertAllCloseAccordingToType(var0_np, self.evaluate(var0)) + self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1)) + + @test_util.run_in_graph_and_eager_modes(reset_test=True) + def testResourceBasic(self): + self.doTestBasic() + + def testBasicCallableParams(self): + with context.eager_mode(): + self.doTestBasic(use_callable_params=True) + + @test_util.run_in_graph_and_eager_modes(reset_test=True) + def testBasicWithAmsgrad(self): + for i, dtype in enumerate([dtypes.half, dtypes.float32, dtypes.float64]): + with self.session(graph=ops.Graph()): + # Initialize variables for numpy implementation. + m0, v0, v0hat, m1, v1, v1hat = 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 + var0_np = np.array([1.0, 2.0], dtype=dtype.as_numpy_dtype) + grads0_np = np.array([0.1, 0.1], dtype=dtype.as_numpy_dtype) + var1_np = np.array([3.0, 4.0], dtype=dtype.as_numpy_dtype) + grads1_np = np.array([0.01, 0.01], dtype=dtype.as_numpy_dtype) + + var0 = resource_variable_ops.ResourceVariable( + var0_np, name="var0_%d" % i) + var1 = resource_variable_ops.ResourceVariable( + var1_np, name="var1_%d" % i) + grads0 = constant_op.constant(grads0_np) + grads1 = constant_op.constant(grads1_np) + + opt = adam.Adam(amsgrad=True) + if not context.executing_eagerly(): + update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + + self.evaluate(variables.global_variables_initializer()) + # Run 3 steps of Adam + for t in range(3): + beta_1_power, beta_2_power = get_beta_accumulators(opt, dtype) + self.assertAllCloseAccordingToType(0.9**(t + 1), + self.evaluate(beta_1_power)) + self.assertAllCloseAccordingToType(0.999**(t + 1), + self.evaluate(beta_2_power)) + if not context.executing_eagerly(): + self.evaluate(update) + else: + opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + + var0_np, m0, v0, v0hat = adam_update_numpy_amsgrad( + var0_np, grads0_np, t, m0, v0, v0hat) + var1_np, m1, v1, v1hat = adam_update_numpy_amsgrad( + var1_np, grads1_np, t, m1, v1, v1hat) + + # Validate updated params + self.assertAllCloseAccordingToType(var0_np, self.evaluate(var0)) + self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1)) + + @test_util.run_in_graph_and_eager_modes + def testSparseWithAmsgrad(self): + # dtypes.half does not work on gpu + eager. + for dtype in [dtypes.float32, dtypes.float64]: + with self.cached_session(): + m0 = np.array([[0.0], [0.0]]) + v0 = np.array([[0.0], [0.0]]) + v0hat = np.array([[0.0], [0.0]]) + indices_np = np.array([1]) + indices = constant_op.constant(indices_np, dtype=dtypes.int32) + var0_np = np.array([[1.0], [2.0]], dtype=dtype.as_numpy_dtype) + repeated_index_update_var = variables.Variable(var0_np, dtype=dtype) + aggregated_update_var = variables.Variable(var0_np, dtype=dtype) + grads0_np = np.array([[0.2]], dtype=dtype.as_numpy_dtype) + grad_repeated_index = ops.IndexedSlices( + constant_op.constant([0.1, 0.1], shape=[2, 1], dtype=dtype), + constant_op.constant([1, 1]), constant_op.constant([2, 1])) + grad_aggregated = ops.IndexedSlices(grads0_np, indices, + constant_op.constant([2, 1])) + opt_repeated = adam.Adam(amsgrad=True) + opt_aggregated = adam.Adam(amsgrad=True) + if not context.executing_eagerly(): + repeated_update = opt_repeated.apply_gradients( + [(grad_repeated_index, repeated_index_update_var)]) + aggregated_update = opt_aggregated.apply_gradients( + [(grad_aggregated, aggregated_update_var)]) + self.evaluate(variables.global_variables_initializer()) + self.assertAllClose( + self.evaluate(aggregated_update_var), + self.evaluate(repeated_index_update_var)) + for t in range(3): + if not context.executing_eagerly(): + self.evaluate(repeated_update) + self.evaluate(aggregated_update) + else: + opt_repeated.apply_gradients( + [(grad_repeated_index, repeated_index_update_var)]) + opt_aggregated.apply_gradients( + [(grad_aggregated, aggregated_update_var)]) + + var0_np, m0, v0, v0hat = adam_sparse_update_numpy_amsgrad( + var0_np, indices_np, grads0_np, t, m0, v0, v0hat) + + # Validate updated params + self.assertAllCloseAccordingToType( + var0_np, self.evaluate(aggregated_update_var)) + self.assertAllCloseAccordingToType( + self.evaluate(aggregated_update_var), + self.evaluate(repeated_index_update_var)) + + @test_util.run_deprecated_v1 + def testBasicWithLearningRateDecay(self): + for i, dtype in enumerate([dtypes.half, dtypes.float32, dtypes.float64]): + with self.session(graph=ops.Graph()): + # Initialize variables for numpy implementation. + m0, v0, m1, v1 = 0.0, 0.0, 0.0, 0.0 + var0_np = np.array([1.0, 2.0], dtype=dtype.as_numpy_dtype) + grads0_np = np.array([0.1, 0.1], dtype=dtype.as_numpy_dtype) + var1_np = np.array([3.0, 4.0], dtype=dtype.as_numpy_dtype) + grads1_np = np.array([0.01, 0.01], dtype=dtype.as_numpy_dtype) + + var0 = resource_variable_ops.ResourceVariable( + var0_np, name="var0_%d" % i) + var1 = resource_variable_ops.ResourceVariable( + var1_np, name="var1_%d" % i) + grads0 = constant_op.constant(grads0_np) + grads1 = constant_op.constant(grads1_np) + + learning_rate = 0.001 + beta_1 = 0.9 + beta_2 = 0.999 + epsilon = 1e-7 + decay = 0.5 + + opt = adam.Adam( + learning_rate=learning_rate, + beta_1=beta_1, + beta_2=beta_2, + epsilon=epsilon, + decay=decay) + update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + + self.evaluate(variables.global_variables_initializer()) + # Run 3 steps of Adam + for t in range(3): + self.evaluate(update) + lr_np = learning_rate / (1 + decay * t) + + var0_np, m0, v0 = adam_update_numpy( + var0_np, grads0_np, t, m0, v0, lr=lr_np) + var1_np, m1, v1 = adam_update_numpy( + var1_np, grads1_np, t, m1, v1, lr=lr_np) + + # Validate updated params + self.assertAllCloseAccordingToType(var0_np, self.evaluate(var0)) + self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1)) + + @test_util.run_deprecated_v1 + def testTensorLearningRate(self): + for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: + with self.cached_session(): + # Initialize variables for numpy implementation. + m0, v0, m1, v1 = 0.0, 0.0, 0.0, 0.0 + var0_np = np.array([1.0, 2.0], dtype=dtype.as_numpy_dtype) + grads0_np = np.array([0.1, 0.1], dtype=dtype.as_numpy_dtype) + var1_np = np.array([3.0, 4.0], dtype=dtype.as_numpy_dtype) + grads1_np = np.array([0.01, 0.01], dtype=dtype.as_numpy_dtype) + + var0 = variables.Variable(var0_np) + var1 = variables.Variable(var1_np) + grads0 = constant_op.constant(grads0_np) + grads1 = constant_op.constant(grads1_np) + opt = adam.Adam(constant_op.constant(0.001)) + update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + variables.global_variables_initializer().run() + + # Fetch params to validate initial values + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) + + beta_1_power, beta_2_power = get_beta_accumulators(opt, dtype) + # Run 3 steps of Adam + for t in range(3): + self.assertAllCloseAccordingToType(0.9**(t + 1), + self.evaluate(beta_1_power)) + self.assertAllCloseAccordingToType(0.999**(t + 1), + self.evaluate(beta_2_power)) + update.run() + + var0_np, m0, v0 = adam_update_numpy(var0_np, grads0_np, t, m0, v0) + var1_np, m1, v1 = adam_update_numpy(var1_np, grads1_np, t, m1, v1) + + # Validate updated params + self.assertAllCloseAccordingToType(var0_np, self.evaluate(var0)) + self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1)) + + @test_util.run_deprecated_v1 + def testSharing(self): + for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: + with self.cached_session(): + # Initialize variables for numpy implementation. + m0, v0, m1, v1 = 0.0, 0.0, 0.0, 0.0 + var0_np = np.array([1.0, 2.0], dtype=dtype.as_numpy_dtype) + grads0_np = np.array([0.1, 0.1], dtype=dtype.as_numpy_dtype) + var1_np = np.array([3.0, 4.0], dtype=dtype.as_numpy_dtype) + grads1_np = np.array([0.01, 0.01], dtype=dtype.as_numpy_dtype) + + var0 = variables.Variable(var0_np) + var1 = variables.Variable(var1_np) + grads0 = constant_op.constant(grads0_np) + grads1 = constant_op.constant(grads1_np) + opt = adam.Adam() + update1 = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + update2 = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + variables.global_variables_initializer().run() + + beta_1_power, beta_2_power = get_beta_accumulators(opt, dtype) + + # Fetch params to validate initial values + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) + + # Run 3 steps of intertwined Adam1 and Adam2. + for t in range(3): + self.assertAllCloseAccordingToType(0.9**(t + 1), + self.evaluate(beta_1_power)) + self.assertAllCloseAccordingToType(0.999**(t + 1), + self.evaluate(beta_2_power)) + if t % 2 == 0: + update1.run() + else: + update2.run() + + var0_np, m0, v0 = adam_update_numpy(var0_np, grads0_np, t, m0, v0) + var1_np, m1, v1 = adam_update_numpy(var1_np, grads1_np, t, m1, v1) + + # Validate updated params + self.assertAllCloseAccordingToType(var0_np, self.evaluate(var0)) + self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1)) + + def testSlotsUniqueEager(self): + with context.eager_mode(): + v1 = resource_variable_ops.ResourceVariable(1.) + v2 = resource_variable_ops.ResourceVariable(1.) + opt = adam.Adam(1.) + opt.minimize(lambda: v1 + v2, var_list=[v1, v2]) + # There should be iteration, and two unique slot variables for v1 and v2. + self.assertEqual(5, len(set(opt.variables()))) + self.assertEqual( + self.evaluate(opt.variables()[0]), self.evaluate(opt.iterations)) + + def testSetWeightsFromV1AdamWithoutMinimize(self): + keras_v1_adam = optimizers.Adam() + keras_v2_adam = adam.Adam() + keras_v2_adam.set_weights(keras_v1_adam.get_weights()) + keras_v1_iteration = keras_v1_adam.iterations + keras_v2_iteration = keras_v2_adam.iterations + self.evaluate(variables.global_variables_initializer()) + self.assertEqual( + self.evaluate(keras_v1_iteration), self.evaluate(keras_v2_iteration)) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/keras/optimizer_v2/adamax.py b/tensorflow/python/keras/optimizer_v2/adamax.py new file mode 100644 index 0000000000..ddd78584f8 --- /dev/null +++ b/tensorflow/python/keras/optimizer_v2/adamax.py @@ -0,0 +1,159 @@ +# Copyright 2018 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. +# ============================================================================== + +"""Adamax for TensorFlow.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.framework import ops +from tensorflow.python.keras.optimizer_v2 import adam +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import control_flow_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import resource_variable_ops +from tensorflow.python.training import training_ops + + +class Adamax(adam.Adam): + """Optimizer that implements the Adamax algorithm. + + It is a variant of Adam based on the infinity norm. + Default parameters follow those provided in the paper. + Adamax is sometimes superior to adam, specially in models with embeddings. + + References + see Section 7 of [Kingma et al., 2014](http://arxiv.org/abs/1412.6980) + ([pdf](http://arxiv.org/pdf/1412.6980.pdf)). + """ + + def __init__(self, + learning_rate=0.001, + beta_1=0.9, + beta_2=0.999, + epsilon=1e-7, + name='Adamax', + **kwargs): + """Construct a new Adamax optimizer. + + Initialization: + + ``` + m_0 <- 0 (Initialize initial 1st moment vector) + v_0 <- 0 (Initialize the exponentially weighted infinity norm) + t <- 0 (Initialize timestep) + ``` + + The update rule for `variable` with gradient `g` uses an optimization + described at the end of section 7.1 of the paper: + + ``` + t <- t + 1 + + m_t <- beta1 * m_{t-1} + (1 - beta1) * g + v_t <- max(beta2 * v_{t-1}, abs(g)) + variable <- variable - learning_rate / (1 - beta1^t) * m_t / (v_t + epsilon) + ``` + + Similar to AdamOptimizer, the epsilon is added for numerical stability + (especially to get rid of division by zero when v_t = 0). + + Contrast to AdamOptimizer, the sparse implementation of this algorithm + (used when the gradient is an IndexedSlices object, typically because of + `tf.gather` or an embedding lookup in the forward pass) only updates + variable slices and corresponding `m_t`, `v_t` terms when that part of + the variable was used in the forward pass. This means that the sparse + behavior is contrast to the dense behavior (similar to some momentum + implementations which ignore momentum unless a variable slice was actually + used). + + Args: + learning_rate: A Tensor or a floating point value. The learning rate. + beta_1: A float value or a constant float tensor. The exponential decay + rate for the 1st moment estimates. + beta_2: A float value or a constant float tensor. The exponential decay + rate for the exponentially weighted infinity norm. + epsilon: A small constant for numerical stability. + name: Optional name for the operations created when applying gradients. + Defaults to "Adamax". + **kwargs: keyword arguments. Allowed to be {`decay`} + """ + # pylint: disable=useless-super-delegation + super(Adamax, self).__init__( + learning_rate=learning_rate, + beta_1=beta_1, + beta_2=beta_2, + epsilon=epsilon, + amsgrad=False, + name=name, + **kwargs) + # pylint: enable=useless-super-delegation + + def _resource_apply_dense(self, grad, var): + var_dtype = var.dtype.base_dtype + lr_t = self._decayed_lr(var_dtype) + m = self.get_slot(var, 'm') + v = self.get_slot(var, 'v') + beta_1_t = self._get_hyper('beta_1', var_dtype) + beta_2_t = self._get_hyper('beta_2', var_dtype) + local_step = math_ops.cast(self.iterations + 1, var_dtype) + beta_1_power = math_ops.pow(beta_1_t, local_step) + return training_ops.resource_apply_ada_max( + var.handle, + m.handle, + v.handle, + beta_1_power, + lr_t, + beta_1_t, + beta_2_t, + self._get_hyper('epsilon', var_dtype), + grad, + use_locking=self._use_locking) + + def _resource_apply_sparse(self, grad, var, indices): + var_dtype = var.dtype.base_dtype + lr_t = self._decayed_lr(var_dtype) + + beta_1_t = self._get_hyper('beta_1', var_dtype) + beta_2_t = self._get_hyper('beta_2', var_dtype) + local_step = math_ops.cast(self.iterations + 1, var_dtype) + beta_1_power = math_ops.pow(beta_1_t, local_step) + epsilon_t = self._get_hyper('epsilon', var_dtype) + + # m_t = beta1 * m + (1 - beta1) * g_t + m = self.get_slot(var, 'm') + m_slice = array_ops.gather(m, indices) + m_t_slice = m_slice * beta_1_t + grad * (1 - beta_1_t) + with ops.control_dependencies([m_t_slice]): + m_t = self._resource_scatter_update(m, indices, m_t_slice) + + # u_t = max(beta2 * u, abs(g_t)) + v = self.get_slot(var, 'v') + v_slice = array_ops.gather(v, indices) + v_t_slice = math_ops.maximum(v_slice * beta_2_t, math_ops.abs(grad)) + with ops.control_dependencies([v_t_slice]): + v_t = self._resource_scatter_update(v, indices, v_t_slice) + # theta_t = theta - lr / (1 - beta1^t) * m_t / u_t + var_slice = -lr_t / (1 - beta_1_power) * ( + m_t_slice / (v_t_slice + epsilon_t)) + with ops.control_dependencies([var_slice]): + var_update = self._resource_scatter_add(var, indices, var_slice) + return control_flow_ops.group(*[var_update, m_t, v_t]) + + def _resource_scatter_update(self, x, i, v): + with ops.control_dependencies( + [resource_variable_ops.resource_scatter_update( + x.handle, i, v)]): + return x.value() diff --git a/tensorflow/python/keras/optimizer_v2/adamax_test.py b/tensorflow/python/keras/optimizer_v2/adamax_test.py new file mode 100644 index 0000000000..baf131fbb0 --- /dev/null +++ b/tensorflow/python/keras/optimizer_v2/adamax_test.py @@ -0,0 +1,367 @@ +# Copyright 2018 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 Adamax.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +from tensorflow.python.eager import context +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util +from tensorflow.python.keras.optimizer_v2 import adamax +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import resource_variable_ops +from tensorflow.python.ops import variables +from tensorflow.python.platform import test + + +def adamax_update_numpy(param, + g_t, + t, + m, + v, + alpha=0.001, + beta1=0.9, + beta2=0.999, + epsilon=1e-8): + m_t = beta1 * m + (1 - beta1) * g_t + v_t = np.maximum(beta2 * v, np.abs(g_t)) + param_t = param - (alpha / (1 - beta1**(t + 1))) * (m_t / (v_t + epsilon)) + return param_t, m_t, v_t + + +def adamax_sparse_update_numpy(param, + indices, + g_t, + t, + m, + v, + alpha=0.001, + beta1=0.9, + beta2=0.999, + epsilon=1e-8): + m_t, v_t, param_t = np.copy(m), np.copy(v), np.copy(param) + m_t_slice = beta1 * m[indices] + (1 - beta1) * g_t + v_t_slice = np.maximum(beta2 * v[indices], np.abs(g_t)) + param_t_slice = param[indices] - ( + (alpha / (1 - beta1**(t + 1))) * (m_t_slice / (v_t_slice + epsilon))) + m_t[indices] = m_t_slice + v_t[indices] = v_t_slice + param_t[indices] = param_t_slice + return param_t, m_t, v_t + + +def get_beta_accumulators(opt, dtype): + local_step = math_ops.cast(opt.iterations + 1, dtype) + beta_1_t = math_ops.cast(opt._get_hyper("beta_1"), dtype) + beta_1_power = math_ops.pow(beta_1_t, local_step) + return beta_1_power + + +class AdamaxOptimizerTest(test.TestCase): + + def doTestSparse(self, use_resource=False): + for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: + with self.cached_session(): + # Initialize variables for numpy implementation. + zero_slots = lambda: np.zeros((3), dtype=dtype.as_numpy_dtype) # pylint: disable=cell-var-from-loop + m0, v0, m1, v1 = zero_slots(), zero_slots(), zero_slots(), zero_slots() + var0_np = np.array([1.0, 2.0, 3.0], dtype=dtype.as_numpy_dtype) + grads0_np = np.array([0.1, 0.1], dtype=dtype.as_numpy_dtype) + var1_np = np.array([4.0, 5.0, 6.0], dtype=dtype.as_numpy_dtype) + grads1_np = np.array([0.01, 0.01], dtype=dtype.as_numpy_dtype) + + var0 = resource_variable_ops.ResourceVariable(var0_np) + var1 = resource_variable_ops.ResourceVariable(var1_np) + + grads0_np_indices = np.array([0, 1], dtype=np.int32) + grads0 = ops.IndexedSlices( + constant_op.constant(grads0_np), + constant_op.constant(grads0_np_indices), constant_op.constant([3])) + grads1_np_indices = np.array([2, 1], dtype=np.int32) + grads1 = ops.IndexedSlices( + constant_op.constant(grads1_np), + constant_op.constant(grads1_np_indices), constant_op.constant([3])) + opt = adamax.Adamax() + update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + variables.global_variables_initializer().run() + + # Fetch params to validate initial values + self.assertAllClose([1.0, 2.0, 3.0], var0.eval()) + self.assertAllClose([4.0, 5.0, 6.0], var1.eval()) + + beta1_power = get_beta_accumulators(opt, dtype) + + # Run 3 steps of Adamax + for t in range(3): + self.assertAllCloseAccordingToType(0.9**(t + 1), beta1_power.eval()) + update.run() + + var0_np, m0, v0 = adamax_sparse_update_numpy( + var0_np, grads0_np_indices, grads0_np, t, m0, v0) + var1_np, m1, v1 = adamax_sparse_update_numpy( + var1_np, grads1_np_indices, grads1_np, t, m1, v1) + + # Validate updated params + self.assertAllCloseAccordingToType(var0_np, var0.eval()) + self.assertAllCloseAccordingToType(var1_np, var1.eval()) + + @test_util.run_deprecated_v1 + def testResourceSparse(self): + self.doTestSparse(use_resource=True) + + @test_util.run_deprecated_v1 + def testSparseDevicePlacement(self): + for index_dtype in [dtypes.int32, dtypes.int64]: + with self.cached_session(force_gpu=test.is_gpu_available()): + # If a GPU is available, tests that all optimizer ops can be placed on + # it (i.e. they have GPU kernels). + var = variables.Variable([[1.0], [2.0]]) + indices = constant_op.constant([0, 1], dtype=index_dtype) + gathered_sum = math_ops.reduce_sum(array_ops.gather(var, indices)) + optimizer = adamax.Adamax(3.0) + minimize_op = optimizer.minimize(gathered_sum, var_list=[var]) + variables.global_variables_initializer().run() + minimize_op.run() + + @test_util.run_deprecated_v1 + def testSparseRepeatedIndices(self): + for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: + with self.cached_session(): + repeated_index_update_var = variables.Variable( + [[1.0], [2.0]], dtype=dtype) + aggregated_update_var = variables.Variable( + [[1.0], [2.0]], dtype=dtype) + grad_repeated_index = ops.IndexedSlices( + constant_op.constant( + [0.1, 0.1], shape=[2, 1], dtype=dtype), + constant_op.constant([1, 1]), + constant_op.constant([2, 1])) + grad_aggregated = ops.IndexedSlices( + constant_op.constant( + [0.2], shape=[1, 1], dtype=dtype), + constant_op.constant([1]), + constant_op.constant([2, 1])) + repeated_update = adamax.Adamax().apply_gradients( + [(grad_repeated_index, repeated_index_update_var)]) + aggregated_update = adamax.Adamax().apply_gradients( + [(grad_aggregated, aggregated_update_var)]) + variables.global_variables_initializer().run() + self.assertAllClose(aggregated_update_var.eval(), + repeated_index_update_var.eval()) + for _ in range(3): + repeated_update.run() + aggregated_update.run() + self.assertAllClose(aggregated_update_var.eval(), + repeated_index_update_var.eval()) + + @test_util.run_in_graph_and_eager_modes(reset_test=True) + def testBasic(self): + for i, dtype in enumerate([dtypes.half, dtypes.float32, dtypes.float64]): + with self.session(graph=ops.Graph()): + # Initialize variables for numpy implementation. + m0, v0, m1, v1 = 0.0, 0.0, 0.0, 0.0 + var0_np = np.array([1.0, 2.0], dtype=dtype.as_numpy_dtype) + grads0_np = np.array([0.1, 0.1], dtype=dtype.as_numpy_dtype) + var1_np = np.array([3.0, 4.0], dtype=dtype.as_numpy_dtype) + grads1_np = np.array([0.01, 0.01], dtype=dtype.as_numpy_dtype) + + var0 = resource_variable_ops.ResourceVariable( + var0_np, name="var0_%d" % i) + var1 = resource_variable_ops.ResourceVariable( + var1_np, name="var1_%d" % i) + + grads0 = constant_op.constant(grads0_np) + grads1 = constant_op.constant(grads1_np) + + opt = adamax.Adamax() + if not context.executing_eagerly(): + update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + + if not context.executing_eagerly(): + self.evaluate(variables.global_variables_initializer()) + # Fetch params to validate initial values + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) + + # Run 3 steps of Adamax + for t in range(3): + beta_1_power = get_beta_accumulators(opt, dtype) + self.assertAllCloseAccordingToType(0.9**(t + 1), + self.evaluate(beta_1_power)) + if not context.executing_eagerly(): + self.evaluate(update) + else: + opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + + var0_np, m0, v0 = adamax_update_numpy(var0_np, grads0_np, t, m0, v0) + var1_np, m1, v1 = adamax_update_numpy(var1_np, grads1_np, t, m1, v1) + + # Validate updated params + self.assertAllCloseAccordingToType( + var0_np, self.evaluate(var0), rtol=1e-2) + self.assertAllCloseAccordingToType( + var1_np, self.evaluate(var1), rtol=1e-2) + + @test_util.run_in_graph_and_eager_modes(reset_test=True) + def testBasicWithLearningRateDecay(self): + for i, dtype in enumerate([dtypes.half, dtypes.float32, dtypes.float64]): + with self.session(graph=ops.Graph()): + # Initialize variables for numpy implementation. + m0, v0, m1, v1 = 0.0, 0.0, 0.0, 0.0 + var0_np = np.array([1.0, 2.0], dtype=dtype.as_numpy_dtype) + grads0_np = np.array([0.1, 0.1], dtype=dtype.as_numpy_dtype) + var1_np = np.array([3.0, 4.0], dtype=dtype.as_numpy_dtype) + grads1_np = np.array([0.01, 0.01], dtype=dtype.as_numpy_dtype) + + var0 = resource_variable_ops.ResourceVariable( + var0_np, name="var0_%d" % i) + var1 = resource_variable_ops.ResourceVariable( + var1_np, name="var1_%d" % i) + + grads0 = constant_op.constant(grads0_np) + grads1 = constant_op.constant(grads1_np) + + learning_rate = 0.001 + decay = 0.002 + opt = adamax.Adamax(learning_rate=learning_rate, decay=decay) + if not context.executing_eagerly(): + update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + + if not context.executing_eagerly(): + self.evaluate(variables.global_variables_initializer()) + # Fetch params to validate initial values + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) + + # Run 3 steps of Adamax + for t in range(3): + beta_1_power = get_beta_accumulators(opt, dtype) + self.assertAllCloseAccordingToType(0.9**(t + 1), + self.evaluate(beta_1_power)) + if not context.executing_eagerly(): + self.evaluate(update) + else: + opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + + lr = learning_rate / (1 + decay * t) + + var0_np, m0, v0 = adamax_update_numpy( + var0_np, grads0_np, t, m0, v0, alpha=lr) + var1_np, m1, v1 = adamax_update_numpy( + var1_np, grads1_np, t, m1, v1, alpha=lr) + + # Validate updated params + self.assertAllCloseAccordingToType(var0_np, self.evaluate(var0), + rtol=1e-2) + self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1), + rtol=1e-2) + + @test_util.run_deprecated_v1 + def testTensorLearningRate(self): + for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: + with self.cached_session(): + # Initialize variables for numpy implementation. + m0, v0, m1, v1 = 0.0, 0.0, 0.0, 0.0 + var0_np = np.array([1.0, 2.0], dtype=dtype.as_numpy_dtype) + grads0_np = np.array([0.1, 0.1], dtype=dtype.as_numpy_dtype) + var1_np = np.array([3.0, 4.0], dtype=dtype.as_numpy_dtype) + grads1_np = np.array([0.01, 0.01], dtype=dtype.as_numpy_dtype) + + var0 = variables.Variable(var0_np) + var1 = variables.Variable(var1_np) + grads0 = constant_op.constant(grads0_np) + grads1 = constant_op.constant(grads1_np) + opt = adamax.Adamax(constant_op.constant(0.001)) + update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + variables.global_variables_initializer().run() + + # Fetch params to validate initial values + self.assertAllClose([1.0, 2.0], var0.eval()) + self.assertAllClose([3.0, 4.0], var1.eval()) + + beta1_power = get_beta_accumulators(opt, dtype) + + # Run 3 steps of Adamax + for t in range(3): + self.assertAllCloseAccordingToType(0.9**(t + 1), beta1_power.eval()) + update.run() + + var0_np, m0, v0 = adamax_update_numpy(var0_np, grads0_np, t, m0, v0) + var1_np, m1, v1 = adamax_update_numpy(var1_np, grads1_np, t, m1, v1) + + # Validate updated params + self.assertAllCloseAccordingToType(var0_np, var0.eval()) + self.assertAllCloseAccordingToType(var1_np, var1.eval()) + + @test_util.run_deprecated_v1 + def testSharing(self): + for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: + with self.cached_session(): + # Initialize variables for numpy implementation. + m0, v0, m1, v1 = 0.0, 0.0, 0.0, 0.0 + var0_np = np.array([1.0, 2.0], dtype=dtype.as_numpy_dtype) + grads0_np = np.array([0.1, 0.1], dtype=dtype.as_numpy_dtype) + var1_np = np.array([3.0, 4.0], dtype=dtype.as_numpy_dtype) + grads1_np = np.array([0.01, 0.01], dtype=dtype.as_numpy_dtype) + + var0 = variables.Variable(var0_np) + var1 = variables.Variable(var1_np) + grads0 = constant_op.constant(grads0_np) + grads1 = constant_op.constant(grads1_np) + opt = adamax.Adamax() + update1 = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + update2 = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + variables.global_variables_initializer().run() + + beta1_power = get_beta_accumulators(opt, dtype) + + # Fetch params to validate initial values + self.assertAllClose([1.0, 2.0], var0.eval()) + self.assertAllClose([3.0, 4.0], var1.eval()) + + # Run 3 steps of intertwined Adamax1 and Adamax2. + for t in range(3): + self.assertAllCloseAccordingToType(0.9**(t + 1), beta1_power.eval()) + if t % 2 == 0: + update1.run() + else: + update2.run() + + var0_np, m0, v0 = adamax_update_numpy(var0_np, grads0_np, t, m0, v0) + var1_np, m1, v1 = adamax_update_numpy(var1_np, grads1_np, t, m1, v1) + + # Validate updated params + self.assertAllCloseAccordingToType(var0_np, var0.eval()) + self.assertAllCloseAccordingToType(var1_np, var1.eval()) + + def testSlotsUniqueEager(self): + with context.eager_mode(): + v1 = resource_variable_ops.ResourceVariable(1.) + v2 = resource_variable_ops.ResourceVariable(1.) + opt = adamax.Adamax(1.) + opt.minimize(lambda: v1 + v2, var_list=[v1, v2]) + # There should be iteration, and two unique slot variables for v1 and v2. + self.assertEqual(5, len(set(opt.variables()))) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/keras/optimizer_v2/ftrl.py b/tensorflow/python/keras/optimizer_v2/ftrl.py new file mode 100644 index 0000000000..e278e352f5 --- /dev/null +++ b/tensorflow/python/keras/optimizer_v2/ftrl.py @@ -0,0 +1,210 @@ +# Copyright 2018 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. +# ============================================================================== +"""Ftrl-proximal for TensorFlow.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.keras.optimizer_v2 import optimizer_v2 +from tensorflow.python.ops import init_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.training import training_ops + + +class Ftrl(optimizer_v2.OptimizerV2): + """Optimizer that implements the FTRL algorithm. + + See this [paper]( + https://www.eecs.tufts.edu/~dsculley/papers/ad-click-prediction.pdf). + This version has support for both online L2 (the L2 penalty given in the paper + above) and shrinkage-type L2 (which is the addition of an L2 penalty to the + loss function). + """ + + def __init__(self, + learning_rate, + learning_rate_power=-0.5, + initial_accumulator_value=0.1, + l1_regularization_strength=0.0, + l2_regularization_strength=0.0, + name='Ftrl', + l2_shrinkage_regularization_strength=0.0, + **kwargs): + r"""Construct a new FTRL optimizer. + + Args: + learning_rate: A float value or a constant float `Tensor`. + learning_rate_power: A float value, must be less or equal to zero. + Controls how the learning rate decreases during training. Use zero for + a fixed learning rate. + initial_accumulator_value: The starting value for accumulators. + Only zero or positive values are allowed. + l1_regularization_strength: A float value, must be greater than or + equal to zero. + l2_regularization_strength: A float value, must be greater than or + equal to zero. + name: Optional name prefix for the operations created when applying + gradients. Defaults to "Ftrl". + l2_shrinkage_regularization_strength: A float value, must be greater than + or equal to zero. This differs from L2 above in that the L2 above is a + stabilization penalty, whereas this L2 shrinkage is a magnitude penalty. + The FTRL formulation can be written as: + w_{t+1} = argmin_w(\hat{g}_{1:t}w + L1*||w||_1 + L2*||w||_2^2), where + \hat{g} = g + (2*L2_shrinkage*w), and g is the gradient of the loss + function w.r.t. the weights w. + Specifically, in the absence of L1 regularization, it is equivalent to + the following update rule: + w_{t+1} = w_t - lr_t / (1 + 2*L2*lr_t) * g_t - + 2*L2_shrinkage*lr_t / (1 + 2*L2*lr_t) * w_t + where lr_t is the learning rate at t. + When input is sparse shrinkage will only happen on the active weights.\ + **kwargs: keyword arguments. Allowed to be {`decay`} + + Raises: + ValueError: If one of the arguments is invalid. + + References + See [paper] + (https://www.eecs.tufts.edu/~dsculley/papers/ad-click-prediction.pdf) + """ + super(Ftrl, self).__init__(name, **kwargs) + + if initial_accumulator_value < 0.0: + raise ValueError( + 'initial_accumulator_value %f needs to be positive or zero' % + initial_accumulator_value) + if learning_rate_power > 0.0: + raise ValueError('learning_rate_power %f needs to be negative or zero' % + learning_rate_power) + if l1_regularization_strength < 0.0: + raise ValueError( + 'l1_regularization_strength %f needs to be positive or zero' % + l1_regularization_strength) + if l2_regularization_strength < 0.0: + raise ValueError( + 'l2_regularization_strength %f needs to be positive or zero' % + l2_regularization_strength) + if l2_shrinkage_regularization_strength < 0.0: + raise ValueError( + 'l2_shrinkage_regularization_strength %f needs to be positive' + ' or zero' % l2_shrinkage_regularization_strength) + + self._set_hyper('learning_rate', learning_rate) + self._set_hyper('decay', self._initial_decay) + self._set_hyper('learning_rate_power', learning_rate_power) + self._set_hyper('l1_regularization_strength', l1_regularization_strength) + self._set_hyper('l2_regularization_strength', l2_regularization_strength) + self._initial_accumulator_value = initial_accumulator_value + self._l2_shrinkage_regularization_strength = ( + l2_shrinkage_regularization_strength) + + def _create_slots(self, var_list): + # Create the "accum" and "linear" slots. + for var in var_list: + dtype = var.dtype.base_dtype + init = init_ops.constant_initializer( + self._initial_accumulator_value, dtype=dtype) + self.add_slot(var, 'accumulator', init) + self.add_slot(var, 'linear') + + def _resource_apply_dense(self, grad, var): + var_dtype = var.dtype.base_dtype + lr_t = self._decayed_lr(var_dtype) + learning_rate_power = self._get_hyper('learning_rate_power', var_dtype) + l1_regularization_strength = self._get_hyper('l1_regularization_strength', + var_dtype) + l2_regularization_strength = self._get_hyper('l2_regularization_strength', + var_dtype) + accum = self.get_slot(var, 'accumulator') + linear = self.get_slot(var, 'linear') + if self._l2_shrinkage_regularization_strength <= 0.0: + return training_ops.resource_apply_ftrl( + var.handle, + accum.handle, + linear.handle, + grad, + lr_t, + l1_regularization_strength, + l2_regularization_strength, + learning_rate_power, + use_locking=self._use_locking) + else: + return training_ops.resource_apply_ftrl_v2( + var.handle, + accum.handle, + linear.handle, + grad, + lr_t, + l1_regularization_strength, + l2_regularization_strength, + math_ops.cast(self._l2_shrinkage_regularization_strength, var_dtype), + learning_rate_power, + use_locking=self._use_locking) + + def _resource_apply_sparse(self, grad, var, indices): + var_dtype = var.dtype.base_dtype + lr_t = self._decayed_lr(var_dtype) + learning_rate_power = self._get_hyper('learning_rate_power', var_dtype) + l1_regularization_strength = self._get_hyper('l1_regularization_strength', + var_dtype) + l2_regularization_strength = self._get_hyper('l2_regularization_strength', + var_dtype) + accum = self.get_slot(var, 'accumulator') + linear = self.get_slot(var, 'linear') + if self._l2_shrinkage_regularization_strength <= 0.0: + return training_ops.resource_sparse_apply_ftrl( + var.handle, + accum.handle, + linear.handle, + grad, + indices, + lr_t, + l1_regularization_strength, + l2_regularization_strength, + learning_rate_power, + use_locking=self._use_locking) + else: + return training_ops.resource_sparse_apply_ftrl_v2( + var.handle, + accum.handle, + linear.handle, + grad, + indices, + lr_t, + l1_regularization_strength, + l2_regularization_strength, + math_ops.cast(self._l2_shrinkage_regularization_strength, var_dtype), + learning_rate_power, + use_locking=self._use_locking) + + def get_config(self): + config = super(Ftrl, self).get_config() + config.update({ + 'learning_rate': + self._serialize_hyperparameter('learning_rate'), + 'decay': + self._serialize_hyperparameter('decay'), + 'initial_accumulator_value': + self._initial_accumulator_value, + 'learning_rate_power': + self._serialize_hyperparameter('learning_rate_power'), + 'l1_regularization_strength': + self._serializer_hyperparameter('l1_regularization_strength'), + 'l2_regularization_strength': + self._serializer_hyperparameter('l2_regularization_strength'), + 'l2_shrinkage_regularization_strength': + self._l2_shrinkage_regularization_strength, + }) + return config diff --git a/tensorflow/python/keras/optimizer_v2/ftrl_test.py b/tensorflow/python/keras/optimizer_v2/ftrl_test.py new file mode 100644 index 0000000000..bec400e8cb --- /dev/null +++ b/tensorflow/python/keras/optimizer_v2/ftrl_test.py @@ -0,0 +1,440 @@ +# Copyright 2015 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. +# ============================================================================== +"""Functional tests for Ftrl operations.""" + +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 +from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util +from tensorflow.python.keras.optimizer_v2 import ftrl +from tensorflow.python.ops import embedding_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import resource_variable_ops +from tensorflow.python.ops import variables +from tensorflow.python.platform import test +from tensorflow.python.training import adagrad +from tensorflow.python.training import gradient_descent + + +class FtrlOptimizerTest(test.TestCase): + + def doTestFtrlwithoutRegularization(self, use_resource=False): + for dtype in [dtypes.half, dtypes.float32]: + with self.cached_session() as sess: + if use_resource: + var0 = resource_variable_ops.ResourceVariable([0.0, 0.0], dtype=dtype) + var1 = resource_variable_ops.ResourceVariable([0.0, 0.0], dtype=dtype) + else: + var0 = variables.Variable([0.0, 0.0], dtype=dtype) + var1 = variables.Variable([0.0, 0.0], dtype=dtype) + grads0 = constant_op.constant([0.1, 0.2], dtype=dtype) + grads1 = constant_op.constant([0.01, 0.02], dtype=dtype) + opt = ftrl.Ftrl( + 3.0, + initial_accumulator_value=0.1, + l1_regularization_strength=0.0, + l2_regularization_strength=0.0) + update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + variables.global_variables_initializer().run() + + v0_val, v1_val = self.evaluate([var0, var1]) + self.assertAllClose([0.0, 0.0], v0_val) + self.assertAllClose([0.0, 0.0], v1_val) + + # Run 3 steps FTRL + for _ in range(3): + update.run() + + v0_val, v1_val = self.evaluate([var0, var1]) + self.assertAllCloseAccordingToType( + np.array([-2.60260963, -4.29698515]), v0_val) + self.assertAllCloseAccordingToType( + np.array([-0.28432083, -0.56694895]), v1_val) + + @test_util.run_deprecated_v1 + def testFtrlWithoutRegularization(self): + self.doTestFtrlwithoutRegularization(use_resource=False) + + @test_util.run_deprecated_v1 + def testResourceFtrlWithoutRegularization(self): + self.doTestFtrlwithoutRegularization(use_resource=True) + + @test_util.run_deprecated_v1 + def testFtrlwithoutRegularization2(self): + for dtype in [dtypes.half, dtypes.float32]: + with self.cached_session() as sess: + var0 = variables.Variable([1.0, 2.0], dtype=dtype) + var1 = variables.Variable([4.0, 3.0], dtype=dtype) + grads0 = constant_op.constant([0.1, 0.2], dtype=dtype) + grads1 = constant_op.constant([0.01, 0.02], dtype=dtype) + + opt = ftrl.Ftrl( + 3.0, + initial_accumulator_value=0.1, + l1_regularization_strength=0.0, + l2_regularization_strength=0.0) + update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + variables.global_variables_initializer().run() + + v0_val, v1_val = self.evaluate([var0, var1]) + self.assertAllCloseAccordingToType([1.0, 2.0], v0_val) + self.assertAllCloseAccordingToType([4.0, 3.0], v1_val) + + # Run 3 steps FTRL + for _ in range(3): + update.run() + v0_val, v1_val = self.evaluate([var0, var1]) + self.assertAllCloseAccordingToType( + np.array([-2.55607247, -3.98729396]), v0_val) + self.assertAllCloseAccordingToType( + np.array([-0.28232238, -0.56096673]), v1_val) + + @test_util.run_deprecated_v1 + def testMinimizeSparseResourceVariable(self): + for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: + with self.cached_session(): + var0 = resource_variable_ops.ResourceVariable([[1.0, 2.0]], dtype=dtype) + x = constant_op.constant([[4.0], [5.0]], dtype=dtype) + pred = math_ops.matmul(embedding_ops.embedding_lookup([var0], [0]), x) + loss = pred * pred + sgd_op = ftrl.Ftrl(1.0).minimize(loss, var_list=[var0]) + variables.global_variables_initializer().run() + # Fetch params to validate initial values + self.assertAllCloseAccordingToType([[1.0, 2.0]], self.evaluate(var0)) + # Run 1 step of sgd + sgd_op.run() + # Validate updated params + self.assertAllCloseAccordingToType([[0, 1]], + self.evaluate(var0), + atol=0.01) + + @test_util.run_deprecated_v1 + def testFtrlWithL1(self): + for dtype in [dtypes.half, dtypes.float32]: + with self.cached_session() as sess: + var0 = variables.Variable([1.0, 2.0], dtype=dtype) + var1 = variables.Variable([4.0, 3.0], dtype=dtype) + grads0 = constant_op.constant([0.1, 0.2], dtype=dtype) + grads1 = constant_op.constant([0.01, 0.02], dtype=dtype) + + opt = ftrl.Ftrl( + 3.0, + initial_accumulator_value=0.1, + l1_regularization_strength=0.001, + l2_regularization_strength=0.0) + update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + variables.global_variables_initializer().run() + + v0_val, v1_val = self.evaluate([var0, var1]) + self.assertAllCloseAccordingToType([1.0, 2.0], v0_val) + self.assertAllCloseAccordingToType([4.0, 3.0], v1_val) + + # Run 10 steps FTRL + for _ in range(10): + update.run() + v0_val, v1_val = self.evaluate([var0, var1]) + self.assertAllCloseAccordingToType( + np.array([-7.66718769, -10.91273689]), v0_val) + self.assertAllCloseAccordingToType( + np.array([-0.93460727, -1.86147261]), v1_val) + + @test_util.run_deprecated_v1 + def testFtrlWithL1_L2(self): + for dtype in [dtypes.half, dtypes.float32]: + with self.cached_session() as sess: + var0 = variables.Variable([1.0, 2.0], dtype=dtype) + var1 = variables.Variable([4.0, 3.0], dtype=dtype) + grads0 = constant_op.constant([0.1, 0.2], dtype=dtype) + grads1 = constant_op.constant([0.01, 0.02], dtype=dtype) + + opt = ftrl.Ftrl( + 3.0, + initial_accumulator_value=0.1, + l1_regularization_strength=0.001, + l2_regularization_strength=2.0) + update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + variables.global_variables_initializer().run() + + v0_val, v1_val = self.evaluate([var0, var1]) + self.assertAllCloseAccordingToType([1.0, 2.0], v0_val) + self.assertAllCloseAccordingToType([4.0, 3.0], v1_val) + + # Run 10 steps FTRL + for _ in range(10): + update.run() + + v0_val, v1_val = self.evaluate([var0, var1]) + self.assertAllCloseAccordingToType( + np.array([-0.24059935, -0.46829352]), v0_val) + self.assertAllCloseAccordingToType( + np.array([-0.02406147, -0.04830509]), v1_val) + + @test_util.run_deprecated_v1 + def testFtrlWithL1_L2_L2Shrinkage(self): + """Test the new FTRL op with support for l2 shrinkage. + + The addition of this parameter which places a constant pressure on weights + towards the origin causes the gradient descent trajectory to differ. The + weights will tend to have smaller magnitudes with this parameter set. + """ + for dtype in [dtypes.half, dtypes.float32]: + with self.cached_session() as sess: + var0 = variables.Variable([1.0, 2.0], dtype=dtype) + var1 = variables.Variable([4.0, 3.0], dtype=dtype) + grads0 = constant_op.constant([0.1, 0.2], dtype=dtype) + grads1 = constant_op.constant([0.01, 0.02], dtype=dtype) + + opt = ftrl.Ftrl( + 3.0, + initial_accumulator_value=0.1, + l1_regularization_strength=0.001, + l2_regularization_strength=2.0, + l2_shrinkage_regularization_strength=0.1) + update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + variables.global_variables_initializer().run() + + v0_val, v1_val = self.evaluate([var0, var1]) + self.assertAllCloseAccordingToType([1.0, 2.0], v0_val) + self.assertAllCloseAccordingToType([4.0, 3.0], v1_val) + + # Run 10 steps FTRL + for _ in range(10): + update.run() + + v0_val, v1_val = self.evaluate([var0, var1]) + self.assertAllCloseAccordingToType( + np.array([-0.22578995, -0.44345796]), v0_val) + self.assertAllCloseAccordingToType( + np.array([-0.14378493, -0.13229476]), v1_val) + + @test_util.run_deprecated_v1 + def testFtrlWithL1_L2_L2ShrinkageSparse(self): + """Tests the new FTRL op with support for l2 shrinkage on sparse grads.""" + for dtype in [dtypes.half, dtypes.float32]: + with self.cached_session() as sess: + var0 = variables.Variable([[1.0], [2.0]], dtype=dtype) + var1 = variables.Variable([[4.0], [3.0]], dtype=dtype) + grads0 = ops.IndexedSlices( + constant_op.constant([0.1], shape=[1, 1], dtype=dtype), + constant_op.constant([0]), constant_op.constant([2, 1])) + grads1 = ops.IndexedSlices( + constant_op.constant([0.02], shape=[1, 1], dtype=dtype), + constant_op.constant([1]), constant_op.constant([2, 1])) + + opt = ftrl.Ftrl( + 3.0, + initial_accumulator_value=0.1, + l1_regularization_strength=0.001, + l2_regularization_strength=2.0, + l2_shrinkage_regularization_strength=0.1) + update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + variables.global_variables_initializer().run() + + v0_val, v1_val = self.evaluate([var0, var1]) + self.assertAllCloseAccordingToType([[1.0], [2.0]], v0_val) + self.assertAllCloseAccordingToType([[4.0], [3.0]], v1_val) + + # Run 10 steps FTRL + for _ in range(10): + update.run() + + v0_val, v1_val = self.evaluate([var0, var1]) + self.assertAllCloseAccordingToType([[-0.22578995], [2.]], v0_val) + self.assertAllCloseAccordingToType([[4.], [-0.13229476]], v1_val) + + @test_util.run_deprecated_v1 + def testFtrlWithL2ShrinkageDoesNotChangeLrSchedule(self): + """Verifies that l2 shrinkage in FTRL does not change lr schedule.""" + for dtype in [dtypes.half, dtypes.float32]: + with self.cached_session() as sess: + var0 = variables.Variable([1.0, 2.0], dtype=dtype) + var1 = variables.Variable([1.0, 2.0], dtype=dtype) + grads0 = constant_op.constant([0.1, 0.2], dtype=dtype) + grads1 = constant_op.constant([0.1, 0.2], dtype=dtype) + + opt0 = ftrl.Ftrl( + 3.0, + initial_accumulator_value=0.1, + l1_regularization_strength=0.001, + l2_regularization_strength=2.0, + l2_shrinkage_regularization_strength=0.1) + opt1 = ftrl.Ftrl( + 3.0, + initial_accumulator_value=0.1, + l1_regularization_strength=0.001, + l2_regularization_strength=2.0) + update0 = opt0.apply_gradients([(grads0, var0)]) + update1 = opt1.apply_gradients([(grads1, var1)]) + variables.global_variables_initializer().run() + + v0_val, v1_val = self.evaluate([var0, var1]) + self.assertAllCloseAccordingToType([1.0, 2.0], v0_val) + self.assertAllCloseAccordingToType([1.0, 2.0], v1_val) + + # Run 10 steps FTRL + for _ in range(10): + update0.run() + update1.run() + + v0_val, v1_val = self.evaluate([var0, var1]) + # var0 is experiencing L2 shrinkage so it should be smaller than var1 + # in magnitude. + self.assertTrue((v0_val**2 < v1_val**2).all()) + accum0 = sess.run(opt0.get_slot(var0, "accumulator")) + accum1 = sess.run(opt1.get_slot(var1, "accumulator")) + # L2 shrinkage should not change how we update grad accumulator. + self.assertAllCloseAccordingToType(accum0, accum1) + + def applyOptimizer(self, opt, dtype, steps=5, is_sparse=False): + if is_sparse: + var0 = variables.Variable([[0.0], [0.0]], dtype=dtype) + var1 = variables.Variable([[0.0], [0.0]], dtype=dtype) + grads0 = ops.IndexedSlices( + constant_op.constant([0.1], shape=[1, 1], dtype=dtype), + constant_op.constant([0]), constant_op.constant([2, 1])) + grads1 = ops.IndexedSlices( + constant_op.constant([0.02], shape=[1, 1], dtype=dtype), + constant_op.constant([1]), constant_op.constant([2, 1])) + else: + var0 = variables.Variable([0.0, 0.0], dtype=dtype) + var1 = variables.Variable([0.0, 0.0], dtype=dtype) + grads0 = constant_op.constant([0.1, 0.2], dtype=dtype) + grads1 = constant_op.constant([0.01, 0.02], dtype=dtype) + + update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + variables.global_variables_initializer().run() + + sess = ops.get_default_session() + v0_val, v1_val = self.evaluate([var0, var1]) + if is_sparse: + self.assertAllCloseAccordingToType([[0.0], [0.0]], v0_val) + self.assertAllCloseAccordingToType([[0.0], [0.0]], v1_val) + else: + self.assertAllCloseAccordingToType([0.0, 0.0], v0_val) + self.assertAllCloseAccordingToType([0.0, 0.0], v1_val) + + # Run Ftrl for a few steps + for _ in range(steps): + update.run() + + v0_val, v1_val = self.evaluate([var0, var1]) + return v0_val, v1_val + + # When variables are initialized with Zero, FTRL-Proximal has two properties: + # 1. Without L1&L2 but with fixed learning rate, FTRL-Proximal is identical + # with GradientDescent. + # 2. Without L1&L2 but with adaptive learning rate, FTRL-Proximal is identical + # with Adagrad. + # So, basing on these two properties, we test if our implementation of + # FTRL-Proximal performs same updates as Adagrad or GradientDescent. + @test_util.run_deprecated_v1 + def testEquivAdagradwithoutRegularization(self): + for dtype in [dtypes.half, dtypes.float32]: + with self.cached_session(): + val0, val1 = self.applyOptimizer( + ftrl.Ftrl( + 3.0, + # Adagrad learning rate + learning_rate_power=-0.5, + initial_accumulator_value=0.1, + l1_regularization_strength=0.0, + l2_regularization_strength=0.0), + dtype) + + with self.cached_session(): + val2, val3 = self.applyOptimizer( + adagrad.AdagradOptimizer(3.0, initial_accumulator_value=0.1), dtype) + + self.assertAllCloseAccordingToType(val0, val2) + self.assertAllCloseAccordingToType(val1, val3) + + @test_util.run_deprecated_v1 + def testEquivSparseAdagradwithoutRegularization(self): + for dtype in [dtypes.half, dtypes.float32]: + with self.cached_session(): + val0, val1 = self.applyOptimizer( + ftrl.Ftrl( + 3.0, + # Adagrad learning rate + learning_rate_power=-0.5, + initial_accumulator_value=0.1, + l1_regularization_strength=0.0, + l2_regularization_strength=0.0), + dtype, + is_sparse=True) + + with self.cached_session(): + val2, val3 = self.applyOptimizer( + adagrad.AdagradOptimizer(3.0, initial_accumulator_value=0.1), + dtype, + is_sparse=True) + + self.assertAllCloseAccordingToType(val0, val2) + self.assertAllCloseAccordingToType(val1, val3) + + @test_util.run_deprecated_v1 + def testEquivSparseGradientDescentwithoutRegularization(self): + for dtype in [dtypes.half, dtypes.float32]: + with self.cached_session(): + val0, val1 = self.applyOptimizer( + ftrl.Ftrl( + 3.0, + # Fixed learning rate + learning_rate_power=-0.0, + initial_accumulator_value=0.1, + l1_regularization_strength=0.0, + l2_regularization_strength=0.0), + dtype, + is_sparse=True) + + with self.cached_session(): + val2, val3 = self.applyOptimizer( + gradient_descent.GradientDescentOptimizer(3.0), + dtype, + is_sparse=True) + + self.assertAllCloseAccordingToType(val0, val2) + self.assertAllCloseAccordingToType(val1, val3) + + @test_util.run_deprecated_v1 + def testEquivGradientDescentwithoutRegularization(self): + for dtype in [dtypes.half, dtypes.float32]: + with self.cached_session(): + val0, val1 = self.applyOptimizer( + ftrl.Ftrl( + 3.0, + # Fixed learning rate + learning_rate_power=-0.0, + initial_accumulator_value=0.1, + l1_regularization_strength=0.0, + l2_regularization_strength=0.0), + dtype) + + with self.cached_session(): + val2, val3 = self.applyOptimizer( + gradient_descent.GradientDescentOptimizer(3.0), dtype) + + self.assertAllCloseAccordingToType(val0, val2) + self.assertAllCloseAccordingToType(val1, val3) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/keras/optimizer_v2/gradient_descent.py b/tensorflow/python/keras/optimizer_v2/gradient_descent.py index 90106c941c..2b82b5e78d 100644 --- a/tensorflow/python/keras/optimizer_v2/gradient_descent.py +++ b/tensorflow/python/keras/optimizer_v2/gradient_descent.py @@ -19,7 +19,6 @@ from __future__ import print_function from tensorflow.python.framework import ops from tensorflow.python.keras.optimizer_v2 import optimizer_v2 -from tensorflow.python.ops import math_ops from tensorflow.python.ops import resource_variable_ops from tensorflow.python.training import training_ops @@ -62,7 +61,8 @@ class SGD(optimizer_v2.OptimizerV2): learning_rate=0.001, momentum=0.0, nesterov=False, - name="SGD"): + name="SGD", + **kwargs): """Construct a new Stochastic Gradient Descent or Momentum optimizer. Arguments: @@ -72,9 +72,11 @@ class SGD(optimizer_v2.OptimizerV2): nesterov: boolean. Whether to apply Nesterov momentum. name: Optional name prefix for the operations created when applying gradients. Defaults to 'SGD'. + **kwargs: keyword arguments. Allowed to be {`decay`} """ - super(SGD, self).__init__(name) + super(SGD, self).__init__(name, **kwargs) self._set_hyper("learning_rate", learning_rate) + self._set_hyper("decay", self._initial_decay) self._momentum = False if isinstance(momentum, ops.Tensor) or callable(momentum) or momentum > 0: @@ -91,44 +93,44 @@ class SGD(optimizer_v2.OptimizerV2): self.add_slot(var, "momentum") def _resource_apply_dense(self, grad, var): - learning_rate = self._get_hyper("learning_rate") + var_dtype = var.dtype.base_dtype + lr_t = self._decayed_lr(var_dtype) if self._momentum: momentum_var = self.get_slot(var, "momentum") - return training_ops.resource_apply_momentum( + return training_ops.resource_apply_keras_momentum( var.handle, momentum_var.handle, - math_ops.cast(learning_rate, grad.dtype.base_dtype), + lr_t, grad, - math_ops.cast(self._get_hyper("momentum"), grad.dtype.base_dtype), + self._get_hyper("momentum", var_dtype), use_locking=self._use_locking, use_nesterov=self._nesterov) else: return training_ops.resource_apply_gradient_descent( - var.handle, - math_ops.cast(learning_rate, grad.dtype.base_dtype), - grad, - use_locking=self._use_locking) + var.handle, lr_t, grad, use_locking=self._use_locking) def _resource_apply_sparse_duplicate_indices(self, grad, var, indices): if self._momentum: return super(SGD, self)._resource_apply_sparse_duplicate_indices( grad, var, indices) else: - return resource_variable_ops.resource_scatter_add( - var.handle, indices, -grad * math_ops.cast( - self._get_hyper("learning_rate"), grad.dtype.base_dtype)) + var_dtype = var.dtype.base_dtype + lr_t = self._decayed_lr(var_dtype) + return resource_variable_ops.resource_scatter_add(var.handle, indices, + -grad * lr_t) def _resource_apply_sparse(self, grad, var, indices): # This method is only needed for momentum optimization. - learning_rate = self._get_hyper("learning_rate") + var_dtype = var.dtype.base_dtype + lr_t = self._decayed_lr(var_dtype) momentum_var = self.get_slot(var, "momentum") - return training_ops.resource_sparse_apply_momentum( + return training_ops.resource_sparse_apply_keras_momentum( var.handle, momentum_var.handle, - math_ops.cast(learning_rate, grad.dtype.base_dtype), + lr_t, grad, indices, - math_ops.cast(self._get_hyper("momentum"), grad.dtype.base_dtype), + self._get_hyper("momentum", var_dtype), use_locking=self._use_locking, use_nesterov=self._nesterov) @@ -136,6 +138,7 @@ class SGD(optimizer_v2.OptimizerV2): config = super(SGD, self).get_config() config.update({ "learning_rate": self._serialize_hyperparameter("learning_rate"), + "decay": self._serialize_hyperparameter("decay"), "momentum": self._serialize_hyperparameter("momentum"), "nesterov": self._nesterov, }) diff --git a/tensorflow/python/keras/optimizer_v2/gradient_descent_test.py b/tensorflow/python/keras/optimizer_v2/gradient_descent_test.py index b84bf1a6ec..0c64202da8 100644 --- a/tensorflow/python/keras/optimizer_v2/gradient_descent_test.py +++ b/tensorflow/python/keras/optimizer_v2/gradient_descent_test.py @@ -47,7 +47,6 @@ class GradientDescentOptimizerTest(test.TestCase): grads0 = constant_op.constant([0.1, 0.1], dtype=dtype) grads1 = constant_op.constant([0.01, 0.01], dtype=dtype) sgd = gradient_descent.SGD(3.0) - # self.assertFalse(sgd._initial_decay) sgd_op = sgd.apply_gradients(zip([grads0, grads1], [var0, var1])) self.evaluate(variables.global_variables_initializer()) # Run 1 step of sgd @@ -58,6 +57,43 @@ class GradientDescentOptimizerTest(test.TestCase): self.assertAllCloseAccordingToType([3.0 - 3.0 * 0.01, 4.0 - 3.0 * 0.01], self.evaluate(var1)) + @test_util.run_in_graph_and_eager_modes + def testBasicWithLearningRateDecay(self): + for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: + with self.cached_session(): + var0 = resource_variable_ops.ResourceVariable([1.0, 2.0], dtype=dtype) + var1 = resource_variable_ops.ResourceVariable([3.0, 4.0], dtype=dtype) + grads0 = constant_op.constant([0.1, 0.1], dtype=dtype) + grads1 = constant_op.constant([0.01, 0.01], dtype=dtype) + learning_rate = 3.0 + decay = 0.5 + sgd = gradient_descent.SGD(learning_rate=learning_rate, decay=decay) + if not context.executing_eagerly(): + sgd_op = sgd.apply_gradients(zip([grads0, grads1], [var0, var1])) + self.evaluate(variables.global_variables_initializer()) + # Run 2 steps of sgd + if not context.executing_eagerly(): + self.evaluate(sgd_op) + else: + sgd.apply_gradients(zip([grads0, grads1], [var0, var1])) + # Validate updated params + self.assertAllCloseAccordingToType([1.0 - 3.0 * 0.1, 2.0 - 3.0 * 0.1], + self.evaluate(var0)) + self.assertAllCloseAccordingToType([3.0 - 3.0 * 0.01, 4.0 - 3.0 * 0.01], + self.evaluate(var1)) + + if not context.executing_eagerly(): + self.evaluate(sgd_op) + else: + sgd.apply_gradients(zip([grads0, grads1], [var0, var1])) + # Validate updated params + self.assertAllCloseAccordingToType( + [1.0 - 3.0 * 0.1 - 2.0 * 0.1, 2.0 - 3.0 * 0.1 - 2.0 * 0.1], + self.evaluate(var0)) + self.assertAllCloseAccordingToType( + [3.0 - 3.0 * 0.01 - 2.0 * 0.01, 4.0 - 3.0 * 0.01 - 2.0 * 0.01], + self.evaluate(var1)) + @test_util.run_in_graph_and_eager_modes def testBasicCallableParams(self): for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: @@ -98,6 +134,7 @@ class GradientDescentOptimizerTest(test.TestCase): self.evaluate(var0)) self.assertAllCloseAccordingToType([3.0 - 1.0], self.evaluate(var1)) + @test_util.run_deprecated_v1 def testMinimizeSparseResourceVariable(self): for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -137,6 +174,7 @@ class GradientDescentOptimizerTest(test.TestCase): self.assertAllCloseAccordingToType([3.0 - 3.0 * 0.01, 4.0 - 3.0 * 0.01], self.evaluate(var1)) + @test_util.run_deprecated_v1 def testGradWrtRef(self): for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -170,6 +208,37 @@ class GradientDescentOptimizerTest(test.TestCase): self.assertAllCloseAccordingToType([[3.0], [4.0 - 3.0 * 0.01]], self.evaluate(var1)) + @test_util.run_deprecated_v1 + def testSparseBasicWithLearningRateDecay(self): + for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: + with self.cached_session(): + var0 = variables.Variable([[1.0], [2.0]], dtype=dtype) + var1 = variables.Variable([[3.0], [4.0]], dtype=dtype) + grads0 = ops.IndexedSlices( + constant_op.constant([0.1], shape=[1, 1], dtype=dtype), + constant_op.constant([0]), constant_op.constant([2, 1])) + grads1 = ops.IndexedSlices( + constant_op.constant([0.01], shape=[1, 1], dtype=dtype), + constant_op.constant([1]), constant_op.constant([2, 1])) + sgd_op = gradient_descent.SGD( + 3.0, decay=0.5).apply_gradients( + zip([grads0, grads1], [var0, var1])) + self.evaluate(variables.global_variables_initializer()) + # Run 2 steps of sgd + self.evaluate(sgd_op) + # Validate updated params + self.assertAllCloseAccordingToType([[1.0 - 3.0 * 0.1], [2.0]], + self.evaluate(var0)) + self.assertAllCloseAccordingToType([[3.0], [4.0 - 3.0 * 0.01]], + self.evaluate(var1)) + + self.evaluate(sgd_op) + # Validate updated params + self.assertAllCloseAccordingToType( + [[1.0 - 3.0 * 0.1 - 2.0 * 0.1], [2.0]], self.evaluate(var0)) + self.assertAllCloseAccordingToType( + [[3.0], [4.0 - 3.0 * 0.01 - 2.0 * 0.01]], self.evaluate(var1)) + def testCapturingInDefunWhileExecutingEagerly(self): with context.eager_mode(): optimizer = gradient_descent.SGD(1.0) @@ -194,10 +263,8 @@ class GradientDescentOptimizerTest(test.TestCase): class MomentumOptimizerTest(test.TestCase): def _update_nesterov_momentum_numpy(self, var, accum, g, lr, momentum): - var = var + accum * lr * momentum - accum = accum * momentum + g - var = var - lr * accum - var = var - accum * lr * momentum + accum = accum * momentum - g * lr + var += (accum * momentum - g * lr) return var, accum @test_util.run_in_graph_and_eager_modes @@ -222,9 +289,9 @@ class MomentumOptimizerTest(test.TestCase): # Check we have slots slot0 = mom_opt.get_slot(var0, "momentum") - self.assertEquals(slot0.get_shape(), var0.get_shape()) + self.assertEqual(slot0.get_shape(), var0.get_shape()) slot1 = mom_opt.get_slot(var1, "momentum") - self.assertEquals(slot1.get_shape(), var1.get_shape()) + self.assertEqual(slot1.get_shape(), var1.get_shape()) # Step 1: the momentum accumulators where 0. So we should see a normal # update: v -= grad * learning_rate @@ -232,9 +299,9 @@ class MomentumOptimizerTest(test.TestCase): self.evaluate(mom_update) # Check that the momentum accumulators have been updated. self.assertAllCloseAccordingToType( - np.array([0.1, 0.1]), self.evaluate(slot0)) + np.array([-0.2, -0.2]), self.evaluate(slot0)) self.assertAllCloseAccordingToType( - np.array([0.01, 0.01]), self.evaluate(slot1)) + np.array([-0.02, -0.02]), self.evaluate(slot1)) # Check that the parameters have been updated. self.assertAllCloseAccordingToType( np.array([1.0 - (0.1 * 2.0), 2.0 - (0.1 * 2.0)]), @@ -248,11 +315,11 @@ class MomentumOptimizerTest(test.TestCase): mom_opt.apply_gradients(zip([grads0, grads1], [var0, var1])) # Check that the momentum accumulators have been updated. self.assertAllCloseAccordingToType( - np.array([(0.9 * 0.1 + 0.1), (0.9 * 0.1 + 0.1)]), + np.array([(0.9 * (-0.2) - 2.0 * 0.1), (0.9 * (-0.2) - 2.0 * 0.1)]), self.evaluate(slot0)) self.assertAllCloseAccordingToType( - np.array([(0.9 * 0.01 + 0.01), (0.9 * 0.01 + 0.01)]), - self.evaluate(slot1)) + np.array([(0.9 * (-0.02) - 2.0 * 0.01), + (0.9 * (-0.02) - 2.0 * 0.01)]), self.evaluate(slot1)) # Check that the parameters have been updated. self.assertAllCloseAccordingToType( np.array([ @@ -265,6 +332,7 @@ class MomentumOptimizerTest(test.TestCase): 3.98 - ((0.9 * 0.01 + 0.01) * 2.0) ]), self.evaluate(var1)) + @test_util.run_deprecated_v1 def testNesterovMomentum(self): for dtype in [dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -289,9 +357,10 @@ class MomentumOptimizerTest(test.TestCase): var0_np, accum0_np, var0_np * 10, 2.0, 0.9) var1_np, accum1_np = self._update_nesterov_momentum_numpy( var1_np, accum1_np, 3, 2.0, 0.9) - self.assertAllClose(var0_np, var0.eval()) - self.assertAllClose(var1_np, var1.eval()) + self.assertAllClose(var0_np, self.evaluate(var0)) + self.assertAllClose(var1_np, self.evaluate(var1)) + @test_util.run_deprecated_v1 def testSparseNesterovMomentum(self): for dtype in [dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -329,10 +398,11 @@ class MomentumOptimizerTest(test.TestCase): var0_np, accum0_np, var0_np * 10, 2.0, 0.9) var1_np, accum1_np = self._update_nesterov_momentum_numpy( var1_np, accum1_np, 3, 2.0, 0.9) - self.assertAllClose(var0_np, var0.eval()) - self.assertAllClose(var1_np, var1.eval()) + self.assertAllClose(var0_np, self.evaluate(var0)) + self.assertAllClose(var1_np, self.evaluate(var1)) @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def testMinimizeSparseResourceVariable(self): for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: # This test invokes the ResourceSparseApplyMomentum operation, which @@ -386,6 +456,7 @@ class MomentumOptimizerTest(test.TestCase): self.evaluate(sgd_op) self.assertAllCloseAccordingToType([[1, 1], [0, 0]], self.evaluate(var0)) + @test_util.run_deprecated_v1 def testTensorLearningRateAndMomentum(self): for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -401,43 +472,50 @@ class MomentumOptimizerTest(test.TestCase): variables.global_variables_initializer().run() # Check we have slots slot0 = mom_opt.get_slot(var0, "momentum") - self.assertEquals(slot0.get_shape(), var0.get_shape()) + self.assertEqual(slot0.get_shape(), var0.get_shape()) slot1 = mom_opt.get_slot(var1, "momentum") - self.assertEquals(slot1.get_shape(), var1.get_shape()) + self.assertEqual(slot1.get_shape(), var1.get_shape()) # Fetch params to validate initial values - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([3.0, 4.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) # Step 1: the momentum accumulators where 0. So we should see a normal # update: v -= grad * learning_rate mom_update.run() # Check that the momentum accumulators have been updated. - self.assertAllCloseAccordingToType(np.array([0.1, 0.1]), slot0.eval()) - self.assertAllCloseAccordingToType(np.array([0.01, 0.01]), slot1.eval()) + self.assertAllCloseAccordingToType( + np.array([-0.2, -0.2]), self.evaluate(slot0)) + self.assertAllCloseAccordingToType( + np.array([-0.02, -0.02]), self.evaluate(slot1)) # Check that the parameters have been updated. self.assertAllCloseAccordingToType( - np.array([1.0 - (0.1 * 2.0), 2.0 - (0.1 * 2.0)]), var0.eval()) + np.array([1.0 - (0.1 * 2.0), 2.0 - (0.1 * 2.0)]), + self.evaluate(var0)) self.assertAllCloseAccordingToType( - np.array([3.0 - (0.01 * 2.0), 4.0 - (0.01 * 2.0)]), var1.eval()) + np.array([3.0 - (0.01 * 2.0), 4.0 - (0.01 * 2.0)]), + self.evaluate(var1)) # Step 2: the momentum accumulators contain the previous update. mom_update.run() # Check that the momentum accumulators have been updated. self.assertAllCloseAccordingToType( - np.array([(0.9 * 0.1 + 0.1), (0.9 * 0.1 + 0.1)]), slot0.eval()) + np.array([(0.9 * (-0.2) - 2.0 * 0.1), (0.9 * (-0.2) - 2.0 * 0.1)]), + self.evaluate(slot0)) self.assertAllCloseAccordingToType( - np.array([(0.9 * 0.01 + 0.01), (0.9 * 0.01 + 0.01)]), slot1.eval()) + np.array([(0.9 * (-0.02) - 2.0 * 0.01), + (0.9 * (-0.02) - 2.0 * 0.01)]), self.evaluate(slot1)) # Check that the parameters have been updated. self.assertAllCloseAccordingToType( np.array([ 1.0 - (0.1 * 2.0) - ((0.9 * 0.1 + 0.1) * 2.0), 2.0 - (0.1 * 2.0) - ((0.9 * 0.1 + 0.1) * 2.0) - ]), var0.eval()) + ]), self.evaluate(var0)) self.assertAllCloseAccordingToType( np.array([ 2.98 - ((0.9 * 0.01 + 0.01) * 2.0), 3.98 - ((0.9 * 0.01 + 0.01) * 2.0) - ]), var1.eval()) + ]), self.evaluate(var1)) + @test_util.run_deprecated_v1 def testSparse(self): for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -456,57 +534,65 @@ class MomentumOptimizerTest(test.TestCase): # Check we have slots slot0 = mom_opt.get_slot(var0, "momentum") - self.assertEquals(slot0.get_shape(), var0.get_shape()) + self.assertEqual(slot0.get_shape(), var0.get_shape()) slot1 = mom_opt.get_slot(var1, "momentum") - self.assertEquals(slot1.get_shape(), var1.get_shape()) + self.assertEqual(slot1.get_shape(), var1.get_shape()) # Fetch params to validate initial values - self.assertAllClose([0, 0], var0.eval()[0]) - self.assertAllClose([0, 0], var0.eval()[1]) - self.assertAllClose([1, 1], var1.eval()[2]) + self.assertAllClose([0, 0], self.evaluate(var0)[0]) + self.assertAllClose([0, 0], self.evaluate(var0)[1]) + self.assertAllClose([1, 1], self.evaluate(var1)[2]) # Step 1: the momentum accumulators are 0. So we should see a normal # update: v -= grad * learning_rate mom_update.run() # Check that the momentum accumulators have been updated. - self.assertAllCloseAccordingToType(np.array([0, 0]), slot0.eval()[0]) - self.assertAllCloseAccordingToType(np.array([.1, .1]), slot0.eval()[1]) self.assertAllCloseAccordingToType( - np.array([.01, .01]), - slot1.eval()[2]) + np.array([0, 0]), + self.evaluate(slot0)[0]) + self.assertAllCloseAccordingToType( + np.array([-2.0 * .1, -2.0 * .1]), + self.evaluate(slot0)[1]) + self.assertAllCloseAccordingToType( + np.array([-2.0 * .01, -2.0 * .01]), + self.evaluate(slot1)[2]) # Check that the parameters have been updated. - self.assertAllCloseAccordingToType(np.array([0, 0]), var0.eval()[0]) + self.assertAllCloseAccordingToType( + np.array([0, 0]), + self.evaluate(var0)[0]) self.assertAllCloseAccordingToType( np.array([-(0.1 * 2.0), -(0.1 * 2.0)]), - var0.eval()[1]) + self.evaluate(var0)[1]) self.assertAllCloseAccordingToType( np.array([1.0 - (0.01 * 2.0), 1.0 - (0.01 * 2.0)]), - var1.eval()[2]) + self.evaluate(var1)[2]) # Step 2: the momentum accumulators contain the previous update. mom_update.run() # Check that the momentum accumulators have been updated. - self.assertAllClose(np.array([0, 0]), slot0.eval()[0]) + self.assertAllClose(np.array([0, 0]), self.evaluate(slot0)[0]) self.assertAllCloseAccordingToType( - np.array([(0.9 * 0.1 + 0.1), (0.9 * 0.1 + 0.1)]), - slot0.eval()[1]) + np.array([(0.9 * (-0.2) - 2.0 * 0.1), (0.9 * (-0.2) - 2.0 * 0.1)]), + self.evaluate(slot0)[1]) self.assertAllCloseAccordingToType( - np.array([(0.9 * 0.01 + 0.01), (0.9 * 0.01 + 0.01)]), - slot1.eval()[2]) + np.array([(0.9 * (-0.02) - 2.0 * 0.01), + (0.9 * (-0.02) - 2.0 * 0.01)]), + self.evaluate(slot1)[2]) # Check that the parameters have been updated. - self.assertAllClose(np.array([0, 0]), var0.eval()[0]) + self.assertAllClose(np.array([0, 0]), self.evaluate(var0)[0]) self.assertAllCloseAccordingToType( np.array([ -(0.1 * 2.0) - ((0.9 * 0.1 + 0.1) * 2.0), -(0.1 * 2.0) - ((0.9 * 0.1 + 0.1) * 2.0) ]), - var0.eval()[1]) + self.evaluate(var0)[1]) self.assertAllCloseAccordingToType( np.array([ 0.98 - ((0.9 * 0.01 + 0.01) * 2.0), 0.98 - ((0.9 * 0.01 + 0.01) * 2.0) ]), - var1.eval()[2]) + self.evaluate(var1)[2]) + @test_util.run_deprecated_v1 def testSharing(self): for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -522,42 +608,48 @@ class MomentumOptimizerTest(test.TestCase): variables.global_variables_initializer().run() slot0 = mom_opt.get_slot(var0, "momentum") - self.assertEquals(slot0.get_shape(), var0.get_shape()) + self.assertEqual(slot0.get_shape(), var0.get_shape()) slot1 = mom_opt.get_slot(var1, "momentum") - self.assertEquals(slot1.get_shape(), var1.get_shape()) + self.assertEqual(slot1.get_shape(), var1.get_shape()) # Fetch params to validate initial values - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([3.0, 4.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) # Step 1: the momentum accumulators where 0. So we should see a normal # update: v -= grad * learning_rate mom_update1.run() # Check that the momentum accumulators have been updated. - self.assertAllCloseAccordingToType(np.array([0.1, 0.1]), slot0.eval()) - self.assertAllCloseAccordingToType(np.array([0.01, 0.01]), slot1.eval()) + self.assertAllCloseAccordingToType( + np.array([-0.2, -0.2]), self.evaluate(slot0)) + self.assertAllCloseAccordingToType( + np.array([-0.02, -0.02]), self.evaluate(slot1)) # Check that the parameters have been updated. self.assertAllCloseAccordingToType( - np.array([1.0 - (0.1 * 2.0), 2.0 - (0.1 * 2.0)]), var0.eval()) + np.array([1.0 - (0.1 * 2.0), 2.0 - (0.1 * 2.0)]), + self.evaluate(var0)) self.assertAllCloseAccordingToType( - np.array([3.0 - (0.01 * 2.0), 4.0 - (0.01 * 2.0)]), var1.eval()) + np.array([3.0 - (0.01 * 2.0), 4.0 - (0.01 * 2.0)]), + self.evaluate(var1)) # Step 2: the second momentum accumulators contain the previous update. mom_update2.run() # Check that the momentum accumulators have been updated. self.assertAllCloseAccordingToType( - np.array([(0.9 * 0.1 + 0.1), (0.9 * 0.1 + 0.1)]), slot0.eval()) + np.array([(0.9 * (-0.2) - 2.0 * 0.1), (0.9 * (-0.2) - 2.0 * 0.1)]), + self.evaluate(slot0)) self.assertAllCloseAccordingToType( - np.array([(0.9 * 0.01 + 0.01), (0.9 * 0.01 + 0.01)]), slot1.eval()) + np.array([(0.9 * (-0.02) - 2.0 * 0.01), + (0.9 * (-0.02) - 2.0 * 0.01)]), self.evaluate(slot1)) # Check that the parameters have been updated. self.assertAllCloseAccordingToType( np.array([ 1.0 - (0.1 * 2.0) - ((0.9 * 0.1 + 0.1) * 2.0), 2.0 - (0.1 * 2.0) - ((0.9 * 0.1 + 0.1) * 2.0) - ]), var0.eval()) + ]), self.evaluate(var0)) self.assertAllCloseAccordingToType( np.array([ 2.98 - ((0.9 * 0.01 + 0.01) * 2.0), 3.98 - ((0.9 * 0.01 + 0.01) * 2.0) - ]), var1.eval()) + ]), self.evaluate(var1)) @test_util.run_in_graph_and_eager_modes def testConfig(self): diff --git a/tensorflow/python/keras/optimizer_v2/nadam.py b/tensorflow/python/keras/optimizer_v2/nadam.py new file mode 100644 index 0000000000..00b095e0dc --- /dev/null +++ b/tensorflow/python/keras/optimizer_v2/nadam.py @@ -0,0 +1,143 @@ +# Copyright 2018 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. +# ============================================================================== +"""Nadam for TensorFlow.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.framework import ops +from tensorflow.python.keras.optimizer_v2 import adam +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import control_flow_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import state_ops +from tensorflow.python.training import training_ops + + +class Nadam(adam.Adam): + r"""Optimizer that implements the NAdam algorithm. + + Much like Adam is essentially RMSprop with momentum, Nadam is Adam with + Nesterov momentum. + + Initialization: + + $$m_0 := 0 \text{(Initialize initial 1st moment vector)}$$ + $$v_0 := 0 \text{(Initialize initial 2nd moment vector)}$$ + $$t := 0 \text{(Initialize timestep)}$$ + + Computes: + $$t := t + 1$$ + $$lr_t := \text{learning\_rate} * \sqrt{1 - beta_2^t} / (1 - beta_1^t)$$ + $$m_t := beta_1 * m_{t-1} + (1 - beta_1) * g$$ + $$v_t := beta_2 * v_{t-1} + (1 - beta_2) * g * g$$ + $$m_bar_t := beta_1 * v_t + (1 - beta_1) * g$$ + $$theta_t := theta_{t-1} - lr_t * m_bar_t / (\sqrt{v_t} + \epsilon)$$ + + gradient is evaluated at theta(t) + momentum * v(t), and the variables always + store theta + beta_1 * m / sqrt(v) instead of theta. + + References + See [Dozat, T., 2015](http://cs229.stanford.edu/proj2015/054_report.pdf). + """ + + def __init__(self, + learning_rate=0.001, + beta_1=0.9, + beta_2=0.999, + epsilon=1e-7, + name='Nadam', + **kwargs): + """Construct a new Nadam optimizer. + + Args: + learning_rate: A Tensor or a floating point value. The learning rate. + beta_1: A float value or a constant float tensor. The exponential decay + rate for the 1st moment estimates. + beta_2: A float value or a constant float tensor. The exponential decay + rate for the exponentially weighted infinity norm. + epsilon: A small constant for numerical stability. + name: Optional name for the operations created when applying gradients. + Defaults to "Adamax". + **kwargs: keyword arguments. Allowed to be {`decay`} + """ + + # pylint: disable=useless-super-delegation + super(Nadam, self).__init__( + learning_rate=learning_rate, + beta_1=beta_1, + beta_2=beta_2, + epsilon=epsilon, + amsgrad=False, + name=name, + **kwargs) + # pylint: enable=useless-super-delegation + + def _resource_apply_dense(self, grad, var): + var_dtype = var.dtype.base_dtype + lr_t = self._decayed_lr(var_dtype) + m = self.get_slot(var, 'm') + v = self.get_slot(var, 'v') + beta_1_t = self._get_hyper('beta_1', var_dtype) + beta_2_t = self._get_hyper('beta_2', var_dtype) + local_step = math_ops.cast(self.iterations + 1, var_dtype) + beta_1_power = math_ops.pow(beta_1_t, local_step) + beta_2_power = math_ops.pow(beta_2_t, local_step) + return training_ops.resource_apply_adam( + var.handle, + m.handle, + v.handle, + beta_1_power, + beta_2_power, + lr_t, + beta_1_t, + beta_2_t, + self._get_hyper('epsilon', var_dtype), + grad, + use_locking=self._use_locking, + use_nesterov=True) + + def _resource_apply_sparse(self, grad, var, indices): + var_dtype = var.dtype.base_dtype + lr_t = self._decayed_lr(var_dtype) + beta_1_t = self._get_hyper('beta_1', var_dtype) + beta_2_t = self._get_hyper('beta_2', var_dtype) + local_step = math_ops.cast(self.iterations + 1, var_dtype) + beta_1_power = math_ops.pow(beta_1_t, local_step) + beta_2_power = math_ops.pow(beta_2_t, local_step) + epsilon_t = self._get_hyper('epsilon', var_dtype) + lr = (lr_t * math_ops.sqrt(1 - beta_2_power) / (1 - beta_1_power)) + + # m_t = beta1 * m + (1 - beta1) * g_t + m = self.get_slot(var, 'm') + m_scaled_g_values = grad * (1 - beta_1_t) + m_t = state_ops.assign(m, m * beta_1_t, use_locking=self._use_locking) + with ops.control_dependencies([m_t]): + m_t = self._resource_scatter_add(m, indices, m_scaled_g_values) + # m_bar = (1 - beta1) * g_t + beta1 * m_t + m_bar = m_scaled_g_values + beta_1_t * array_ops.gather(m_t, indices) + + # v_t = beta2 * v + (1 - beta2) * (g_t * g_t) + v = self.get_slot(var, 'v') + v_scaled_g_values = (grad * grad) * (1 - beta_2_t) + v_t = state_ops.assign(v, v * beta_2_t, use_locking=self._use_locking) + with ops.control_dependencies([v_t]): + v_t = self._resource_scatter_add(v, indices, v_scaled_g_values) + + v_t_slice = array_ops.gather(v_t, indices) + v_sqrt = math_ops.sqrt(v_t_slice) + var_update = self._resource_scatter_add(var, indices, + -lr * m_bar / (v_sqrt + epsilon_t)) + return control_flow_ops.group(*[var_update, m_bar, v_t]) diff --git a/tensorflow/python/keras/optimizer_v2/nadam_test.py b/tensorflow/python/keras/optimizer_v2/nadam_test.py new file mode 100644 index 0000000000..d991e3117c --- /dev/null +++ b/tensorflow/python/keras/optimizer_v2/nadam_test.py @@ -0,0 +1,213 @@ +# Copyright 2015 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 Nadam.""" + +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 +from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util +from tensorflow.python.keras.optimizer_v2 import nadam +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import resource_variable_ops +from tensorflow.python.ops import variables +from tensorflow.python.platform import test + + +def get_beta_accumulators(opt, dtype): + local_step = math_ops.cast(opt.iterations + 1, dtype) + beta_1_t = math_ops.cast(opt._get_hyper("beta_1"), dtype) + beta_1_power = math_ops.pow(beta_1_t, local_step) + beta_2_t = math_ops.cast(opt._get_hyper("beta_2"), dtype) + beta_2_power = math_ops.pow(beta_2_t, local_step) + return (beta_1_power, beta_2_power) + + +def nadam_update_numpy(param, + g_t, + t, + m, + v, + alpha=0.001, + beta1=0.9, + beta2=0.999, + epsilon=1e-8): + alpha_t = alpha * np.sqrt(1 - beta2**(t + 1)) / (1 - beta1**(t + 1)) + + m_t = beta1 * m + (1 - beta1) * g_t + v_t = beta2 * v + (1 - beta2) * g_t * g_t + + m_bar = (1 - beta1) * g_t + beta1 * m_t + + param_t = param - alpha_t * m_bar / (np.sqrt(v_t) + epsilon) + return param_t, m_t, v_t + + +class NadamOptimizerTest(test.TestCase): + + def doTestSparse(self, use_resource=False): + sparse_epsilon = 1e-7 + for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: + with self.cached_session(): + # Initialize variables for numpy implementation. + m0, v0, m1, v1 = 0.0, 0.0, 0.0, 0.0 + var0_np = np.array([1.0, 1.0, 2.0], dtype=dtype.as_numpy_dtype) + grads0_np = np.array([0.1, 0, 0.1], dtype=dtype.as_numpy_dtype) + var1_np = np.array([3.0, 3.0, 4.0], dtype=dtype.as_numpy_dtype) + grads1_np = np.array([0.01, 0, 0.01], dtype=dtype.as_numpy_dtype) + + if use_resource: + var0 = resource_variable_ops.ResourceVariable(var0_np) + var1 = resource_variable_ops.ResourceVariable(var1_np) + else: + var0 = variables.Variable(var0_np) + var1 = variables.Variable(var1_np) + grads0_np_indices = np.array([0, 2], dtype=np.int32) + grads0 = ops.IndexedSlices( + constant_op.constant(grads0_np[grads0_np_indices]), + constant_op.constant(grads0_np_indices), constant_op.constant([3])) + grads1_np_indices = np.array([0, 2], dtype=np.int32) + grads1 = ops.IndexedSlices( + constant_op.constant(grads1_np[grads1_np_indices]), + constant_op.constant(grads1_np_indices), constant_op.constant([3])) + opt = nadam.Nadam(epsilon=sparse_epsilon) + update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + variables.global_variables_initializer().run() + + # Fetch params to validate initial values + self.assertAllClose([1.0, 1.0, 2.0], var0.eval()) + self.assertAllClose([3.0, 3.0, 4.0], var1.eval()) + + beta1_power, beta2_power = get_beta_accumulators(opt, dtype) + + # Run 3 steps of Nadam + for t in range(3): + self.assertAllCloseAccordingToType(0.9**(t + 1), beta1_power.eval()) + self.assertAllCloseAccordingToType(0.999**(t + 1), beta2_power.eval()) + update.run() + + var0_np, m0, v0 = nadam_update_numpy( + var0_np, grads0_np, t, m0, v0, epsilon=sparse_epsilon) + var1_np, m1, v1 = nadam_update_numpy( + var1_np, grads1_np, t, m1, v1, epsilon=sparse_epsilon) + + # Validate updated params + self.assertAllCloseAccordingToType(var0_np, var0.eval()) + self.assertAllCloseAccordingToType(var1_np, var1.eval()) + + @test_util.run_deprecated_v1 + def testSparse(self): + self.doTestSparse(use_resource=False) + + @test_util.run_deprecated_v1 + def testResourceSparse(self): + self.doTestSparse(use_resource=True) + + def doTestBasic(self, use_resource=False): + for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: + with self.cached_session(): + # Initialize variables for numpy implementation. + m0, v0, m1, v1 = 0.0, 0.0, 0.0, 0.0 + var0_np = np.array([1.0, 2.0], dtype=dtype.as_numpy_dtype) + grads0_np = np.array([0.1, 0.1], dtype=dtype.as_numpy_dtype) + var1_np = np.array([3.0, 4.0], dtype=dtype.as_numpy_dtype) + grads1_np = np.array([0.01, 0.01], dtype=dtype.as_numpy_dtype) + + if use_resource: + var0 = resource_variable_ops.ResourceVariable(var0_np) + var1 = resource_variable_ops.ResourceVariable(var1_np) + else: + var0 = variables.Variable(var0_np) + var1 = variables.Variable(var1_np) + grads0 = constant_op.constant(grads0_np) + grads1 = constant_op.constant(grads1_np) + opt = nadam.Nadam() + update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + variables.global_variables_initializer().run() + + # Fetch params to validate initial values + self.assertAllClose([1.0, 2.0], var0.eval()) + self.assertAllClose([3.0, 4.0], var1.eval()) + + beta1_power, beta2_power = get_beta_accumulators(opt, dtype) + + # Run 3 steps of Nadam + for t in range(3): + self.assertAllCloseAccordingToType(0.9**(t + 1), beta1_power.eval()) + self.assertAllCloseAccordingToType(0.999**(t + 1), beta2_power.eval()) + update.run() + + var0_np, m0, v0 = nadam_update_numpy(var0_np, grads0_np, t, m0, v0) + var1_np, m1, v1 = nadam_update_numpy(var1_np, grads1_np, t, m1, v1) + + # Validate updated params + self.assertAllCloseAccordingToType(var0_np, var0.eval()) + self.assertAllCloseAccordingToType(var1_np, var1.eval()) + + @test_util.run_deprecated_v1 + def testResourceBasic(self): + self.doTestBasic(use_resource=True) + + @test_util.run_deprecated_v1 + def testBasicWithLearningRateDecay(self): + for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: + with self.cached_session(): + # Initialize variables for numpy implementation. + m0, v0, m1, v1 = 0.0, 0.0, 0.0, 0.0 + var0_np = np.array([1.0, 2.0], dtype=dtype.as_numpy_dtype) + grads0_np = np.array([0.1, 0.1], dtype=dtype.as_numpy_dtype) + var1_np = np.array([3.0, 4.0], dtype=dtype.as_numpy_dtype) + grads1_np = np.array([0.01, 0.01], dtype=dtype.as_numpy_dtype) + + var0 = resource_variable_ops.ResourceVariable(var0_np) + var1 = resource_variable_ops.ResourceVariable(var1_np) + grads0 = constant_op.constant(grads0_np) + grads1 = constant_op.constant(grads1_np) + learning_rate = 0.001 + decay = 0.5 + opt = nadam.Nadam(learning_rate=learning_rate, decay=decay) + update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + variables.global_variables_initializer().run() + + # Fetch params to validate initial values + self.assertAllClose([1.0, 2.0], var0.eval()) + self.assertAllClose([3.0, 4.0], var1.eval()) + + beta1_power, beta2_power = get_beta_accumulators(opt, dtype) + + # Run 3 steps of Nadam + for t in range(3): + self.assertAllCloseAccordingToType(0.9**(t + 1), beta1_power.eval()) + self.assertAllCloseAccordingToType(0.999**(t + 1), beta2_power.eval()) + update.run() + + lr = learning_rate / (1 + decay * t) + var0_np, m0, v0 = nadam_update_numpy( + var0_np, grads0_np, t, m0, v0, alpha=lr) + var1_np, m1, v1 = nadam_update_numpy( + var1_np, grads1_np, t, m1, v1, alpha=lr) + + # Validate updated params + self.assertAllCloseAccordingToType(var0_np, var0.eval()) + self.assertAllCloseAccordingToType(var1_np, var1.eval()) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/keras/optimizer_v2/optimizer_v2.py b/tensorflow/python/keras/optimizer_v2/optimizer_v2.py index c6e1d57c5e..b26b3cefc8 100644 --- a/tensorflow/python/keras/optimizer_v2/optimizer_v2.py +++ b/tensorflow/python/keras/optimizer_v2/optimizer_v2.py @@ -24,16 +24,17 @@ import abc import six +from tensorflow.python.distribute import reduce_util as ds_reduce_util from tensorflow.python.eager import backprop from tensorflow.python.eager import context from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.keras import backend from tensorflow.python.keras import initializers -from tensorflow.python.keras.engine import base_layer +from tensorflow.python.keras.engine import base_layer_utils from tensorflow.python.ops import gradients -from tensorflow.python.ops import variable_scope -from tensorflow.python.ops import variables +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import variables as tf_variables from tensorflow.python.platform import tf_logging as logging from tensorflow.python.training import distribution_strategy_context from tensorflow.python.training import optimizer as optimizer_v1 @@ -114,7 +115,7 @@ class OptimizerV2(optimizer_v1.Optimizer): """ - def __init__(self, name): + def __init__(self, name, **kwargs): """Create a new Optimizer. This must be called by the constructors of subclasses. @@ -128,6 +129,7 @@ class OptimizerV2(optimizer_v1.Optimizer): Args: name: A non-empty string. The name to use for accumulators created for the optimizer. + **kwargs: keyword arguments. Allowed to be {`decay`} Raises: ValueError: If name is malformed. @@ -140,6 +142,12 @@ class OptimizerV2(optimizer_v1.Optimizer): # dict: {variable name : {slot name : variable}} self._slots = {} self._weights = [] + + decay = kwargs.pop("decay", 0.0) + if decay < 0.: + raise ValueError("decay cannot be less than 0: {}".format(decay)) + self._initial_decay = decay + self._prepared = False def minimize(self, @@ -296,6 +304,7 @@ class OptimizerV2(optimizer_v1.Optimizer): grads_and_vars = zip(reduced_grads, var_list) with ops.init_scope(): + self._prepare() self._create_slots(var_list) update_ops = [] @@ -317,19 +326,21 @@ class OptimizerV2(optimizer_v1.Optimizer): return update_op with ops.name_scope(name, self._name) as name: - self._prepare() for grad, var in grads_and_vars: scope_name = ("" if ops.executing_eagerly_outside_functions() else "_" + var.op.name) - with ops.name_scope("update" + scope_name), ops.colocate_with(var): + with ops.name_scope("update" + scope_name): update_ops.append(update_grad_to_var(grad, var)) # control dependencies does not work in per replica mode, please change # this once b/118841692 is fixed. # with ops.control_dependencies(update_ops): # apply_updates = self._iterations.assign_add(1).op - apply_updates = merge_update_step(update_ops, self.iteration) + apply_updates = merge_update_step(update_ops, self.iterations) return apply_updates + def get_updates(self, loss, params): + return [self.minimize(loss, params)] + def _set_hyper(self, name, value): """set hyper `name` to value. value can be callable, tensor, numeric.""" if name not in self._hyper: @@ -342,9 +353,14 @@ class OptimizerV2(optimizer_v1.Optimizer): else: backend.set_value(self._hyper[name], value) - def _get_hyper(self, name): + def _get_hyper(self, name, dtype=None): value = self._hyper[name] - return self._call_if_callable(value) + if callable(value): + value = value() + if dtype: + return math_ops.cast(value, dtype) + else: + return value def __getattribute__(self, name): """Overridden to support hyperparameter access.""" @@ -371,12 +387,16 @@ class OptimizerV2(optimizer_v1.Optimizer): else: super(OptimizerV2, self).__setattr__(name, value) - def add_slot(self, var, slot_name): + def add_slot(self, var, slot_name, initializer="zeros"): var_key = _var_key(var) slot_dict = self._slots.setdefault(var_key, {}) if slot_name not in slot_dict: slot_key = _get_slot_key_from_var(var, slot_name) - weight = self.add_weight(name=slot_key, shape=var.shape, dtype=var.dtype) + weight = self.add_weight( + name=slot_key, + shape=var.shape, + dtype=var.dtype, + initializer=initializer) slot_dict[slot_name] = weight self._weights.append(weight) @@ -392,8 +412,10 @@ class OptimizerV2(optimizer_v1.Optimizer): self._iterations = self.add_weight( "iter", shape=[], + dtype=dtypes.int64, trainable=False, - aggregation=variables.VariableAggregation.ONLY_FIRST_REPLICA) + aggregation=tf_variables.VariableAggregation.ONLY_FIRST_REPLICA) + self._weights.append(self._iterations) for name, value in self._hyper.items(): if isinstance(value, ops.Tensor) or callable(value): pass @@ -403,15 +425,24 @@ class OptimizerV2(optimizer_v1.Optimizer): shape=[], trainable=False, initializer=value, - aggregation=variables.VariableAggregation.ONLY_FIRST_REPLICA) + aggregation=tf_variables.VariableAggregation.ONLY_FIRST_REPLICA) self._prepared = True @property - def iteration(self): + def iterations(self): if not self._prepared: self._prepare() return self._iterations + def _decayed_lr(self, var_dtype): + """Get decayed learning rate as a Tensor with dtype=var_dtype.""" + lr_t = self._get_hyper("learning_rate", var_dtype) + if self._initial_decay > 0.: + local_step = math_ops.cast(self.iterations, var_dtype) + decay_t = self._get_hyper("decay", var_dtype) + lr_t = lr_t / (1. + decay_t * local_step) + return lr_t + @abc.abstractmethod def get_config(self): """Returns the config of the optimimizer. @@ -443,6 +474,8 @@ class OptimizerV2(optimizer_v1.Optimizer): Returns: An optimizer instance. """ + if "lr" in config: + config["learning_rate"] = config.pop("lr") return cls(**config) def _serialize_hyperparameter(self, hyperparameter_name): @@ -450,10 +483,14 @@ class OptimizerV2(optimizer_v1.Optimizer): value = self._get_hyper(hyperparameter_name) if callable(value): return value() - if isinstance(value, (ops.Tensor, variables.Variable)): + if isinstance(value, (ops.Tensor, tf_variables.Variable)): return backend.get_value(value) return value + def variables(self): + """Returns variables of this Optimizer based on the order created.""" + return self._weights + @property def weights(self): """Returns variables of this Optimizer based on the order created.""" @@ -490,15 +527,15 @@ class OptimizerV2(optimizer_v1.Optimizer): dtype=None, initializer="zeros", trainable=None, - synchronization=variables.VariableSynchronization.AUTO, - aggregation=variables.VariableAggregation.NONE): + synchronization=tf_variables.VariableSynchronization.AUTO, + aggregation=tf_variables.VariableAggregation.NONE): if dtype is None: dtype = dtypes.float32 if isinstance(initializer, six.string_types) or callable(initializer): initializer = initializers.get(initializer) - if synchronization == variables.VariableSynchronization.ON_READ: + if synchronization == tf_variables.VariableSynchronization.ON_READ: if trainable: raise ValueError( "Synchronization value can be set to " @@ -514,7 +551,7 @@ class OptimizerV2(optimizer_v1.Optimizer): variable = self._add_variable_with_custom_getter( name=name, shape=shape, - getter=base_layer.make_variable, + getter=base_layer_utils.make_variable, overwrite=True, initializer=initializer, dtype=dtype, @@ -522,6 +559,7 @@ class OptimizerV2(optimizer_v1.Optimizer): use_resource=True, synchronization=synchronization, aggregation=aggregation) + backend.track_variable(variable) return variable @@ -561,7 +599,7 @@ def merge_update_step(update_ops, local_step): return incre_op return distribution_strategy_context.get_replica_context().merge_call( - merge_update_step_fn, update_ops, local_step) + merge_update_step_fn, args=(update_ops, local_step)) def merge_grads(grads_and_vars): @@ -569,11 +607,11 @@ def merge_grads(grads_and_vars): def merge_grad_fn(strategy, grads_and_vars): reduced_grads = strategy.batch_reduce( - variable_scope.VariableAggregation.MEAN, grads_and_vars) + ds_reduce_util.ReduceOp.MEAN, grads_and_vars) return reduced_grads return distribution_strategy_context.get_replica_context().merge_call( - merge_grad_fn, grads_and_vars) + merge_grad_fn, args=(grads_and_vars,)) def _var_key(var): diff --git a/tensorflow/python/keras/optimizer_v2/optimizer_v2_test.py b/tensorflow/python/keras/optimizer_v2/optimizer_v2_test.py index 682deda23f..158577fe64 100644 --- a/tensorflow/python/keras/optimizer_v2/optimizer_v2_test.py +++ b/tensorflow/python/keras/optimizer_v2/optimizer_v2_test.py @@ -18,6 +18,13 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import os +import tempfile + +from absl.testing import parameterized +import numpy as np + +from tensorflow.python import keras from tensorflow.python.eager import context from tensorflow.python.eager import def_function from tensorflow.python.eager import function @@ -25,15 +32,27 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import test_util +from tensorflow.python.keras import backend +from tensorflow.python.keras import callbacks +from tensorflow.python.keras import optimizers +from tensorflow.python.keras import testing_utils +from tensorflow.python.keras.engine import input_layer +from tensorflow.python.keras.engine import saving +from tensorflow.python.keras.engine import sequential +from tensorflow.python.keras.engine import training +from tensorflow.python.keras.layers import core from tensorflow.python.keras.optimizer_v2 import adam from tensorflow.python.keras.optimizer_v2 import gradient_descent +from tensorflow.python.keras.optimizer_v2 import optimizer_v2 from tensorflow.python.ops import array_ops from tensorflow.python.ops import clip_ops from tensorflow.python.ops import gradients_impl from tensorflow.python.ops import resource_variable_ops from tensorflow.python.ops import state_ops from tensorflow.python.ops import variables +from tensorflow.python.platform import gfile from tensorflow.python.platform import test +from tensorflow.python.training import momentum class OptimizerTest(test.TestCase): @@ -279,8 +298,8 @@ class OptimizerTest(test.TestCase): def testIterationWithoutMinimize(self): with self.cached_session(): sgd = gradient_descent.SGD(3.0) - self.evaluate(sgd.iteration.initializer) - self.assertEqual(0, self.evaluate(sgd.iteration)) + self.evaluate(sgd.iterations.initializer) + self.assertEqual(0, self.evaluate(sgd.iterations)) @test_util.run_in_graph_and_eager_modes def testSerializationWithinDefun(self): @@ -341,8 +360,8 @@ class OptimizerTest(test.TestCase): opt2.set_weights(weights) self.evaluate([opt_op_1, opt_op_2]) self.assertAllClose(self.evaluate(var1), self.evaluate(var2)) - self.assertEqual(1, self.evaluate(opt1.iteration)) - self.assertEqual(1, self.evaluate(opt2.iteration)) + self.assertEqual(1, self.evaluate(opt1.iterations)) + self.assertEqual(1, self.evaluate(opt2.iterations)) var3 = resource_variable_ops.ResourceVariable([1.0, 2.0, 3.0], dtype=dtypes.float32) @@ -394,7 +413,231 @@ class OptimizerTest(test.TestCase): with self.assertRaises(AttributeError): opt.not_an_attr += 3 - def testOptimizerWithFunction(self): + @test_util.run_in_graph_and_eager_modes + def testOptimizerWithKerasModel(self): + a = input_layer.Input(shape=(3,), name='input_a') + b = input_layer.Input(shape=(3,), name='input_b') + + dense = core.Dense(4, name='dense') + c = dense(a) + d = dense(b) + e = core.Dropout(0.5, name='dropout')(c) + + model = training.Model([a, b], [d, e]) + + optimizer = gradient_descent.SGD(learning_rate=0.001) + loss = 'mse' + model.compile(optimizer, loss, metrics=['mae']) + + input_a_np = np.random.random((10, 3)) + input_b_np = np.random.random((10, 3)) + + output_d_np = np.random.random((10, 4)) + output_e_np = np.random.random((10, 4)) + + model.fit([input_a_np, input_b_np], [output_d_np, output_e_np], + epochs=1, + batch_size=5) + + @test_util.run_in_graph_and_eager_modes + def testOptimizerWithCallbacks(self): + input_np = np.random.random((10, 3)) + output_np = np.random.random((10, 4)) + a = input_layer.Input(shape=(3,), name='input_a') + model = sequential.Sequential() + model.add(core.Dense(4, name='dense')) + model.add(core.Dropout(0.5, name='dropout')) + model(a) + optimizer = gradient_descent.SGD(learning_rate=0.1) + model.compile(optimizer, loss='mse', metrics=['mae']) + # This does not reduce the LR after the first epoch (due to low delta). + cbks = [ + callbacks.ReduceLROnPlateau( + monitor='val_loss', factor=0.1, min_delta=0, patience=1, cooldown=5) + ] + model.fit( + input_np, + output_np, + batch_size=10, + validation_data=(input_np, output_np), + callbacks=cbks, + epochs=5, + verbose=0) + self.assertAllClose( + float(backend.get_value(model.optimizer.lr)), 0.1, atol=1e-4) + + # This should reduce the LR after the first epoch (due to high delta). + cbks = [ + callbacks.ReduceLROnPlateau( + monitor='val_loss', + factor=0.1, + min_delta=10, + patience=1, + cooldown=5) + ] + model.fit( + input_np, + output_np, + batch_size=10, + validation_data=(input_np, output_np), + callbacks=cbks, + epochs=5, + verbose=2) + self.assertAllClose( + float(backend.get_value(model.optimizer.lr)), 0.01, atol=1e-4) + + +class OptimizersCompatibilityTest(test.TestCase, parameterized.TestCase): + + # TODO(tanzheny): remove test_numeric after algorithm for Momentum, Adam and + # NAdam has been unified: currently these three algorithms behave differently. + @parameterized.named_parameters( + ('adadelta', 'adadelta', True, True), ('adagrad', 'adagrad', True, True), + ('adam', 'adam', True, True), ('adamax', 'adamax', True, True), + ('nadam', 'nadam', True, False), ('momentum', 'momentum', True, True), + ('sgd', 'sgd', False, True)) + def testOptimizersCompatibility(self, opt_str, test_weights, test_numeric): + np.random.seed(1331) + with self.cached_session(): + train_samples = 20 + input_dim = 3 + num_classes = 2 + (x, y), _ = testing_utils.get_test_data( + train_samples=train_samples, + test_samples=10, + input_shape=(input_dim,), + num_classes=num_classes) + y = keras.utils.to_categorical(y) + + num_hidden = 5 + model = testing_utils.get_small_sequential_mlp( + num_hidden=num_hidden, num_classes=num_classes, input_dim=input_dim) + + old_mode = os.environ.get('TF2_BEHAVIOR', None) + # Disable tf2 to create V1 optimizer. + disable_tf2() + if opt_str == 'momentum': + opt_v1 = optimizers.SGD(momentum=0.9) + else: + opt_v1 = optimizers.get(opt_str) + + # Test compile and fit with v1 optimizer. + model.compile(opt_v1, loss='categorical_crossentropy', metrics=[]) + model.fit(x, y, batch_size=5, epochs=1) + model_dir = tempfile.mkdtemp() + gfile.MakeDirs(model_dir) + file_name = os.path.join(model_dir, 'model.h5') + model.save(file_name) + + enable_tf2() + # Test load and fit with v2 optimizer. + model_2 = saving.load_model(file_name) + opt_v2 = model_2.optimizer + self.assertIsInstance(opt_v2, optimizer_v2.OptimizerV2) + # set_weights is called inside load_model but exception is swallowed, + # this call checks the weights can be set correctly. + if test_weights: + opt_v2.set_weights(opt_v1.get_weights()) + if test_numeric: + hist_1 = model.fit(x, y, batch_size=5, epochs=1, shuffle=False) + hist_2 = model_2.fit(x, y, batch_size=5, epochs=1, shuffle=False) + self.assertAllClose(model.get_weights(), model_2.get_weights()) + self.assertAllClose(model.get_weights(), model_2.get_weights()) + self.assertAllClose(hist_1.history['loss'], hist_2.history['loss']) + + if old_mode is not None: + os.environ['TF2_BEHAVIOR'] = old_mode + + def testNumericEquivalenceForNesterovMomentum(self): + np.random.seed(1331) + with self.cached_session(): + train_samples = 20 + input_dim = 3 + num_classes = 2 + (x, y), _ = testing_utils.get_test_data( + train_samples=train_samples, + test_samples=10, + input_shape=(input_dim,), + num_classes=num_classes) + y = keras.utils.to_categorical(y) + + num_hidden = 5 + model_k_v1 = testing_utils.get_small_sequential_mlp( + num_hidden=num_hidden, num_classes=num_classes, input_dim=input_dim) + model_k_v2 = testing_utils.get_small_sequential_mlp( + num_hidden=num_hidden, num_classes=num_classes, input_dim=input_dim) + model_k_v2.set_weights(model_k_v1.get_weights()) + model_tf = testing_utils.get_small_sequential_mlp( + num_hidden=num_hidden, num_classes=num_classes, input_dim=input_dim) + model_tf.set_weights(model_k_v2.get_weights()) + + opt_k_v1 = optimizers.SGD(lr=0.001, momentum=0.9, nesterov=True) + opt_k_v2 = gradient_descent.SGD(momentum=0.9, nesterov=True) + opt_tf = momentum.MomentumOptimizer( + learning_rate=0.001, momentum=0.9, use_nesterov=True) + + model_k_v1.compile(opt_k_v1, loss='categorical_crossentropy', metrics=[]) + model_k_v2.compile(opt_k_v2, loss='categorical_crossentropy', metrics=[]) + model_tf.compile(opt_tf, loss='categorical_crossentropy', metrics=[]) + + hist_k_v1 = model_k_v1.fit(x, y, batch_size=5, epochs=10, shuffle=False) + hist_k_v2 = model_k_v2.fit(x, y, batch_size=5, epochs=10, shuffle=False) + hist_tf = model_tf.fit(x, y, batch_size=5, epochs=10, shuffle=False) + + self.assertAllClose(model_k_v1.get_weights(), model_tf.get_weights()) + self.assertAllClose(model_k_v1.get_weights(), model_k_v2.get_weights()) + self.assertAllClose(opt_k_v1.get_weights(), opt_k_v2.get_weights()) + self.assertAllClose(hist_k_v1.history['loss'], hist_tf.history['loss']) + self.assertAllClose(hist_k_v1.history['loss'], hist_k_v2.history['loss']) + + def testNumericEquivalenceForAmsgrad(self): + np.random.seed(1331) + with self.cached_session(): + train_samples = 20 + input_dim = 3 + num_classes = 2 + (x, y), _ = testing_utils.get_test_data( + train_samples=train_samples, + test_samples=10, + input_shape=(input_dim,), + num_classes=num_classes) + y = keras.utils.to_categorical(y) + + num_hidden = 5 + model_k_v1 = testing_utils.get_small_sequential_mlp( + num_hidden=num_hidden, num_classes=num_classes, input_dim=input_dim) + model_k_v2 = testing_utils.get_small_sequential_mlp( + num_hidden=num_hidden, num_classes=num_classes, input_dim=input_dim) + model_k_v2.set_weights(model_k_v1.get_weights()) + + opt_k_v1 = optimizers.Adam(amsgrad=True) + opt_k_v2 = adam.Adam(amsgrad=True) + + model_k_v1.compile(opt_k_v1, loss='categorical_crossentropy', metrics=[]) + model_k_v2.compile(opt_k_v2, loss='categorical_crossentropy', metrics=[]) + + hist_k_v1 = model_k_v1.fit(x, y, batch_size=5, epochs=10, shuffle=False) + hist_k_v2 = model_k_v2.fit(x, y, batch_size=5, epochs=10, shuffle=False) + + self.assertAllClose(model_k_v1.get_weights(), model_k_v2.get_weights()) + self.assertAllClose(opt_k_v1.get_weights(), opt_k_v2.get_weights()) + self.assertAllClose(hist_k_v1.history['loss'], hist_k_v2.history['loss']) + + +def disable_tf2(): + if 'TF2_BEHAVIOR' in os.environ: + del os.environ['TF2_BEHAVIOR'] + + +def enable_tf2(): + os.environ['TF2_BEHAVIOR'] = 'enabled' + + +# Note: These tests are kept in a separate class to avoid bugs in some +# distributions of Python that break AutoGraph which is used by tf.function. +class OptimizerWithFunctionTest(test.TestCase): + + def testBasic(self): with context.eager_mode(): var = resource_variable_ops.ResourceVariable([1.0, 2.0], dtype=dtypes.float32) @@ -406,10 +649,8 @@ class OptimizerTest(test.TestCase): opt.minimize(loss, [var]) return var - self.assertAllClose([0., 1.], fn()) - # This is just to test tf.function. The values needs to be updated - # when adam updates beta_1_power. - self.assertAllClose([-1.343838, -0.343838], fn()) + self.assertAllClose([0., 1.], fn(), atol=1e-4) + self.assertAllClose([-1, 0.], fn(), atol=1e-4) if __name__ == '__main__': diff --git a/tensorflow/python/keras/optimizer_v2/rmsprop.py b/tensorflow/python/keras/optimizer_v2/rmsprop.py new file mode 100644 index 0000000000..6a5b334fc4 --- /dev/null +++ b/tensorflow/python/keras/optimizer_v2/rmsprop.py @@ -0,0 +1,196 @@ +# Copyright 2018 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. +# ============================================================================== +"""RMSprop for TensorFlow.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.framework import ops +from tensorflow.python.keras.optimizer_v2 import optimizer_v2 +from tensorflow.python.training import training_ops + + +class RMSprop(optimizer_v2.OptimizerV2): + r"""Optimizer that implements the RMSprop algorithm. + + A detailed description of rmsprop. + + - maintain a moving (discounted) average of the square of gradients + - divide gradient by the root of this average + + $$mean_square_t = rho * mean_square{t-1} + (1-rho) * gradient ** 2$$ + $$mom_t = momentum * mom_{t-1} + learning_rate * gradient / \sqrt{ / + mean_square_t + \epsilon}$$ + $$variable_t := variable_{t-1} - mom_t + + This implementation of RMSprop uses plain momentum, not Nesterov momentum. + + The centered version additionally maintains a moving average of the + gradients, and uses that average to estimate the variance: + + $$mean_grad_t = rho * mean_grad_{t-1} + (1-rho) * gradient$$ + $$mean_square_t = rho * mean_square_{t-1} + (1-rho) * gradient ** 2$$ + $$mom_t = momentum * mom_{t-1} + learning_rate * gradient / + sqrt(mean_square_t - mean_grad_t**2 + epsilon)$$ + $$variable_t := variable_{t-1} - mom_t + + References + See ([pdf] + http://www.cs.toronto.edu/~tijmen/csc321/slides/lecture_slides_lec6.pdf). + """ + + def __init__(self, + learning_rate=0.001, + rho=0.9, + momentum=0.0, + epsilon=1e-7, + centered=False, + name="RMSprop", + **kwargs): + """Construct a new RMSprop optimizer. + + Note that in the dense implementation of this algorithm, variables and their + corresponding accumulators (momentum, gradient moving average, square + gradient moving average) will be updated even if the gradient is zero + (i.e. accumulators will decay, momentum will be applied). The sparse + implementation (used when the gradient is an `IndexedSlices` object, + typically because of `tf.gather` or an embedding lookup in the forward pass) + will not update variable slices or their accumulators unless those slices + were used in the forward pass (nor is there an "eventual" correction to + account for these omitted updates). This leads to more efficient updates for + large embedding lookup tables (where most of the slices are not accessed in + a particular graph execution), but differs from the published algorithm. + + Args: + learning_rate: A Tensor or a floating point value. The learning rate. + rho: Discounting factor for the history/coming gradient + momentum: A scalar tensor. + epsilon: Small value to avoid zero denominator. + centered: If True, gradients are normalized by the estimated variance of + the gradient; if False, by the uncentered second moment. Setting this to + True may help with training, but is slightly more expensive in terms of + computation and memory. Defaults to False. + name: Optional name prefix for the operations created when applying + gradients. Defaults to "RMSprop". @compatibility(eager) When eager + execution is enabled, `learning_rate`, `decay`, `momentum`, and + `epsilon` can each be a callable that takes no arguments and returns the + actual value to use. This can be useful for changing these values across + different invocations of optimizer functions. @end_compatibility + **kwargs: keyword arguments. Allowed to be {`decay`} + """ + super(RMSprop, self).__init__(name, **kwargs) + self._set_hyper("learning_rate", learning_rate) + self._set_hyper("decay", self._initial_decay) + self._set_hyper("rho", rho) + + self._momentum = False + if isinstance(momentum, ops.Tensor) or callable(momentum) or momentum > 0: + self._momentum = True + if isinstance(momentum, (int, float)) and (momentum < 0 or momentum > 1): + raise ValueError("`momentum` must be between [0, 1].") + self._set_hyper("momentum", momentum) + + self._set_hyper("epsilon", epsilon) + self._centered = centered + + def _create_slots(self, var_list): + for var in var_list: + self.add_slot(var, "rms") + self.add_slot(var, "momentum") + if self._centered: + self.add_slot(var, "mg") + + def _resource_apply_dense(self, grad, var): + var_dtype = var.dtype.base_dtype + lr_t = self._decayed_lr(var_dtype) + rms = self.get_slot(var, "rms") + mom = self.get_slot(var, "momentum") + rho = self._get_hyper("rho", var_dtype) + momentum = self._get_hyper("momentum", var_dtype) + epsilon = self._get_hyper("epsilon", var_dtype) + if self._centered: + mg = self.get_slot(var, "mg") + return training_ops.resource_apply_centered_rms_prop( + var.handle, + mg.handle, + rms.handle, + mom.handle, + lr_t, + rho, + momentum, + epsilon, + grad, + use_locking=self._use_locking) + else: + return training_ops.resource_apply_rms_prop( + var.handle, + rms.handle, + mom.handle, + lr_t, + rho, + momentum, + epsilon, + grad, + use_locking=self._use_locking) + + def _resource_apply_sparse(self, grad, var, indices): + var_dtype = var.dtype.base_dtype + lr_t = self._decayed_lr(var_dtype) + rms = self.get_slot(var, "rms") + mom = self.get_slot(var, "momentum") + rho = self._get_hyper("rho", var_dtype) + momentum = self._get_hyper("momentum", var_dtype) + epsilon = self._get_hyper("epsilon", var_dtype) + if self._centered: + mg = self.get_slot(var, "mg") + return training_ops.resource_sparse_apply_centered_rms_prop( + var.handle, + mg.handle, + rms.handle, + mom.handle, + lr_t, + rho, + momentum, + epsilon, + grad, + indices, + use_locking=self._use_locking) + else: + return training_ops.resource_sparse_apply_rms_prop( + var.handle, + rms.handle, + mom.handle, + lr_t, + rho, + momentum, + epsilon, + grad, + indices, + use_locking=self._use_locking) + + def get_config(self): + config = super(RMSprop, self).get_config() + config.update({ + "learning_rate": self._serialize_hyperparameter("learning_rate"), + "decay": self._serialize_hyperparameter("decay"), + "rho": self._serialize_hyperparameter("rho"), + "momentum": self._serialize_hyperparameter("momentum"), + "epsilon": self._serialize_hyperparameter("epsilon"), + "centered": self._centered, + }) + return config + + +RMSProp = RMSprop diff --git a/tensorflow/python/keras/optimizer_v2/rmsprop_test.py b/tensorflow/python/keras/optimizer_v2/rmsprop_test.py new file mode 100644 index 0000000000..a8658a8550 --- /dev/null +++ b/tensorflow/python/keras/optimizer_v2/rmsprop_test.py @@ -0,0 +1,410 @@ +# Copyright 2018 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 rmsprop.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import copy +import itertools +import math + +import numpy as np + +from tensorflow.python.eager import context +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util +from tensorflow.python.keras.optimizer_v2 import rmsprop +from tensorflow.python.ops import embedding_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import resource_variable_ops +from tensorflow.python.ops import variables +from tensorflow.python.platform import test + +_DATA_TYPES = [dtypes.half, dtypes.float32] + +_TEST_PARAM_VALUES = [ + # learning_rate, rho, momentum, epsilon, centered + [0.05, 0.9, 0.0, 1e-3, True], + [0.05, 0.9, 0.0, 1e-3, False], + [0.1, 0.9, 0.0, 1e-3, True], + [0.01, 0.9, 0.0, 1e-5, True], + [0.01, 0.9, 0.9, 1e-5, True], +] + +_TESTPARAMS = [ + [data_type] + values + for data_type, values in itertools.product(_DATA_TYPES, _TEST_PARAM_VALUES) +] + + +class RMSpropOptimizerTest(test.TestCase): + + def _rmsprop_update_numpy(self, var, g, mg, rms, mom, lr, rho, momentum, + epsilon, centered): + rms_t = rms * rho + (1 - rho) * g * g + denom_t = rms_t + epsilon + if centered: + mg_t = mg * rho + (1 - rho) * g + denom_t -= mg_t * mg_t + else: + mg_t = mg + mom_t = momentum * mom + lr * g / np.sqrt(denom_t, dtype=denom_t.dtype) + var_t = var - mom_t + return var_t, mg_t, rms_t, mom_t + + def _sparse_rmsprop_update_numpy(self, var, gindexs, gvalues, mg, rms, mom, + lr, rho, momentum, epsilon, centered): + mg_t = copy.deepcopy(mg) + rms_t = copy.deepcopy(rms) + mom_t = copy.deepcopy(mom) + var_t = copy.deepcopy(var) + for i in range(len(gindexs)): + gindex = gindexs[i] + gvalue = gvalues[i] + rms_t[gindex] = rms[gindex] * rho + (1 - rho) * gvalue * gvalue + denom_t = rms_t[gindex] + epsilon + if centered: + mg_t[gindex] = mg_t[gindex] * rho + (1 - rho) * gvalue + denom_t -= mg_t[gindex] * mg_t[gindex] + mom_t[gindex] = momentum * mom[gindex] + lr * gvalue / np.sqrt(denom_t) + var_t[gindex] = var[gindex] - mom_t[gindex] + return var_t, mg_t, rms_t, mom_t + + @test_util.run_deprecated_v1 + def testDense(self): + for (dtype, learning_rate, rho, momentum, epsilon, centered) in _TESTPARAMS: + with test_util.use_gpu(): + # Initialize variables for numpy implementation. + var0_np = np.array([1.0, 2.0], dtype=dtype.as_numpy_dtype) + grads0_np = np.array([0.1, 0.2], dtype=dtype.as_numpy_dtype) + var1_np = np.array([3.0, 4.0], dtype=dtype.as_numpy_dtype) + grads1_np = np.array([0.01, 0.2], dtype=dtype.as_numpy_dtype) + + var0 = resource_variable_ops.ResourceVariable(var0_np, dtype=dtype) + var1 = resource_variable_ops.ResourceVariable(var1_np, dtype=dtype) + grads0 = constant_op.constant(grads0_np, dtype=dtype) + grads1 = constant_op.constant(grads1_np, dtype=dtype) + opt = rmsprop.RMSprop( + learning_rate=learning_rate, + rho=rho, + momentum=momentum, + epsilon=epsilon, + centered=centered) + + update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + self.evaluate(variables.global_variables_initializer()) + + if centered: + mg0 = opt.get_slot(var0, "mg") + mg1 = opt.get_slot(var1, "mg") + else: + mg0 = None + mg1 = None + + rms0 = opt.get_slot(var0, "rms") + self.assertTrue(rms0 is not None) + rms1 = opt.get_slot(var1, "rms") + self.assertTrue(rms1 is not None) + mom0 = opt.get_slot(var0, "momentum") + self.assertTrue(mom0 is not None) + mom1 = opt.get_slot(var1, "momentum") + self.assertTrue(mom1 is not None) + + mg0_np = np.array([0.0, 0.0], dtype=dtype.as_numpy_dtype) + mg1_np = np.array([0.0, 0.0], dtype=dtype.as_numpy_dtype) + rms0_np = np.array([0.0, 0.0], dtype=dtype.as_numpy_dtype) + rms1_np = np.array([0.0, 0.0], dtype=dtype.as_numpy_dtype) + mom0_np = np.array([0.0, 0.0], dtype=dtype.as_numpy_dtype) + mom1_np = np.array([0.0, 0.0], dtype=dtype.as_numpy_dtype) + + # Fetch params to validate initial values + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) + + # Run 4 steps of RMSprop + for _ in range(1, 5): + self.evaluate(update) + + var0_np, mg0_np, rms0_np, mom0_np = self._rmsprop_update_numpy( + var0_np, grads0_np, mg0_np, rms0_np, mom0_np, learning_rate, rho, + momentum, epsilon, centered) + var1_np, mg1_np, rms1_np, mom1_np = self._rmsprop_update_numpy( + var1_np, grads1_np, mg1_np, rms1_np, mom1_np, learning_rate, rho, + momentum, epsilon, centered) + + # Validate updated params + if centered: + self.assertAllCloseAccordingToType(mg0_np, self.evaluate(mg0)) + self.assertAllCloseAccordingToType(mg1_np, self.evaluate(mg1)) + self.assertAllCloseAccordingToType(rms0_np, self.evaluate(rms0)) + self.assertAllCloseAccordingToType(rms1_np, self.evaluate(rms1)) + self.assertAllCloseAccordingToType(mom0_np, self.evaluate(mom0)) + self.assertAllCloseAccordingToType(mom1_np, self.evaluate(mom1)) + self.assertAllCloseAccordingToType(var0_np, self.evaluate(var0)) + self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1)) + + @test_util.run_deprecated_v1 + def testDenseWithLearningRateDecay(self): + var0_np = np.array([1.0, 2.0]) + grads0_np = np.array([0.1, 0.2]) + var1_np = np.array([3.0, 4.0]) + grads1_np = np.array([0.01, 0.2]) + + var0 = resource_variable_ops.ResourceVariable(var0_np) + var1 = resource_variable_ops.ResourceVariable(var1_np) + grads0 = constant_op.constant(grads0_np) + grads1 = constant_op.constant(grads1_np) + learning_rate = 0.01 + rho = 0.9 + momentum = 0.0 + epsilon = 1e-7 + centered = False + decay = 0.5 + opt = rmsprop.RMSprop( + learning_rate=learning_rate, + rho=rho, + momentum=momentum, + epsilon=epsilon, + centered=centered, + decay=decay) + + update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + self.evaluate(variables.global_variables_initializer()) + + rms0 = opt.get_slot(var0, "rms") + self.assertTrue(rms0 is not None) + rms1 = opt.get_slot(var1, "rms") + self.assertTrue(rms1 is not None) + mom0 = opt.get_slot(var0, "momentum") + self.assertTrue(mom0 is not None) + mom1 = opt.get_slot(var1, "momentum") + self.assertTrue(mom1 is not None) + + mg0_np = np.array([0.0, 0.0]) + mg1_np = np.array([0.0, 0.0]) + rms0_np = np.array([0.0, 0.0]) + rms1_np = np.array([0.0, 0.0]) + mom0_np = np.array([0.0, 0.0]) + mom1_np = np.array([0.0, 0.0]) + + # Fetch params to validate initial values + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) + + # Run 4 steps of RMSprop + for t in range(2): + self.evaluate(update) + + lr = learning_rate / (1 + decay * t) + var0_np, mg0_np, rms0_np, mom0_np = self._rmsprop_update_numpy( + var0_np, grads0_np, mg0_np, rms0_np, mom0_np, lr, rho, momentum, + epsilon, centered) + var1_np, mg1_np, rms1_np, mom1_np = self._rmsprop_update_numpy( + var1_np, grads1_np, mg1_np, rms1_np, mom1_np, lr, rho, momentum, + epsilon, centered) + + # Validate updated params + self.assertAllCloseAccordingToType(rms0_np, self.evaluate(rms0)) + self.assertAllCloseAccordingToType(rms1_np, self.evaluate(rms1)) + self.assertAllCloseAccordingToType(mom0_np, self.evaluate(mom0)) + self.assertAllCloseAccordingToType(mom1_np, self.evaluate(mom1)) + self.assertAllCloseAccordingToType(var0_np, self.evaluate(var0)) + self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1)) + + @test_util.run_deprecated_v1 + def testMinimizeSparseResourceVariable(self): + for dtype in [dtypes.float32, dtypes.float64]: + with self.cached_session(): + var0 = resource_variable_ops.ResourceVariable([[1.0, 2.0]], dtype=dtype) + x = constant_op.constant([[4.0], [5.0]], dtype=dtype) + pred = math_ops.matmul(embedding_ops.embedding_lookup([var0], [0]), x) + loss = pred * pred + sgd_op = rmsprop.RMSprop( + learning_rate=1.0, + rho=0.0, + momentum=0.0, + epsilon=0.0, + centered=False).minimize( + loss, var_list=[var0]) + self.evaluate(variables.global_variables_initializer()) + # Fetch params to validate initial values + self.assertAllCloseAccordingToType([[1.0, 2.0]], self.evaluate(var0)) + # Run 1 step of sgd + self.evaluate(sgd_op) + # Validate updated params + self.assertAllCloseAccordingToType([[0., 1.]], + self.evaluate(var0), + atol=0.01) + + @test_util.run_deprecated_v1 + def testMinimizeSparseResourceVariableCentered(self): + for dtype in [dtypes.float32, dtypes.float64]: + with self.cached_session(): + var0 = resource_variable_ops.ResourceVariable([[1.0, 2.0]], dtype=dtype) + x = constant_op.constant([[4.0], [5.0]], dtype=dtype) + pred = math_ops.matmul(embedding_ops.embedding_lookup([var0], [0]), x) + loss = pred * pred + sgd_op = rmsprop.RMSprop( + learning_rate=1.0, + rho=0.0, + momentum=0.0, + epsilon=1.0, + centered=True).minimize( + loss, var_list=[var0]) + self.evaluate(variables.global_variables_initializer()) + # Fetch params to validate initial values + self.assertAllCloseAccordingToType([[1.0, 2.0]], self.evaluate(var0)) + # Run 1 step of sgd + self.evaluate(sgd_op) + # Validate updated params + self.assertAllCloseAccordingToType([[-111, -138]], + self.evaluate(var0), + atol=0.01) + + @test_util.run_deprecated_v1 + def testSparse(self): + for (dtype, learning_rate, rho, momentum, epsilon, centered) in _TESTPARAMS: + with test_util.use_gpu(): + # Initialize variables for numpy implementation. + var0_np = np.array([1.0, 2.0], dtype=dtype.as_numpy_dtype) + grads0_np = np.array([0.1], dtype=dtype.as_numpy_dtype) + var1_np = np.array([3.0, 4.0], dtype=dtype.as_numpy_dtype) + grads1_np = np.array([0.01], dtype=dtype.as_numpy_dtype) + + var0 = variables.Variable(var0_np) + var1 = variables.Variable(var1_np) + grads0_np_indices = np.array([0], dtype=np.int32) + grads0 = ops.IndexedSlices( + constant_op.constant(grads0_np), + constant_op.constant(grads0_np_indices), constant_op.constant([1])) + grads1_np_indices = np.array([1], dtype=np.int32) + grads1 = ops.IndexedSlices( + constant_op.constant(grads1_np), + constant_op.constant(grads1_np_indices), constant_op.constant([1])) + opt = rmsprop.RMSprop( + learning_rate=learning_rate, + rho=rho, + momentum=momentum, + epsilon=epsilon, + centered=centered) + update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + self.evaluate(variables.global_variables_initializer()) + + if centered: + mg0 = opt.get_slot(var0, "mg") + self.assertEqual(mg0 is not None, centered) + mg1 = opt.get_slot(var1, "mg") + self.assertEqual(mg1 is not None, centered) + else: + mg0 = None + mg1 = None + rms0 = opt.get_slot(var0, "rms") + self.assertTrue(rms0 is not None) + rms1 = opt.get_slot(var1, "rms") + self.assertTrue(rms1 is not None) + mom0 = opt.get_slot(var0, "momentum") + self.assertTrue(mom0 is not None) + mom1 = opt.get_slot(var1, "momentum") + self.assertTrue(mom1 is not None) + + mg0_np = np.array([0.0, 0.0], dtype=dtype.as_numpy_dtype) + mg1_np = np.array([0.0, 0.0], dtype=dtype.as_numpy_dtype) + rms0_np = np.array([0.0, 0.0], dtype=dtype.as_numpy_dtype) + rms1_np = np.array([0.0, 0.0], dtype=dtype.as_numpy_dtype) + mom0_np = np.array([0.0, 0.0], dtype=dtype.as_numpy_dtype) + mom1_np = np.array([0.0, 0.0], dtype=dtype.as_numpy_dtype) + + # Fetch params to validate initial values + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) + + # Run 4 steps of RMSprop + for _ in range(1, 5): + self.evaluate(update) + + var0_np, mg0_np, rms0_np, mom0_np = self._sparse_rmsprop_update_numpy( + var0_np, grads0_np_indices, grads0_np, mg0_np, rms0_np, mom0_np, + learning_rate, rho, momentum, epsilon, centered) + var1_np, mg1_np, rms1_np, mom1_np = self._sparse_rmsprop_update_numpy( + var1_np, grads1_np_indices, grads1_np, mg1_np, rms1_np, mom1_np, + learning_rate, rho, momentum, epsilon, centered) + + # Validate updated params + if centered: + self.assertAllCloseAccordingToType(mg0_np, self.evaluate(mg0)) + self.assertAllCloseAccordingToType(mg1_np, self.evaluate(mg1)) + self.assertAllCloseAccordingToType(rms0_np, self.evaluate(rms0)) + self.assertAllCloseAccordingToType(rms1_np, self.evaluate(rms1)) + self.assertAllCloseAccordingToType(mom0_np, self.evaluate(mom0)) + self.assertAllCloseAccordingToType(mom1_np, self.evaluate(mom1)) + self.assertAllCloseAccordingToType(var0_np, self.evaluate(var0)) + self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1)) + + def testCallableParams(self): + with context.eager_mode(): + for dtype in [dtypes.half, dtypes.float32]: + var0 = resource_variable_ops.ResourceVariable([1.0, 2.0], dtype=dtype) + var1 = resource_variable_ops.ResourceVariable([3.0, 4.0], dtype=dtype) + grads0 = constant_op.constant([0.1, 0.1], dtype=dtype) + grads1 = constant_op.constant([0.01, 0.01], dtype=dtype) + + learning_rate = lambda: 2.0 + rho = lambda: 0.9 + momentum = lambda: 0.0 + epsilon = lambda: 1.0 + opt = rmsprop.RMSprop(learning_rate, rho, momentum, epsilon) + + # Fetch params to validate initial values + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) + # Step 1: the rms accumulators where 1. So we should see a normal + # update: v -= grad * learning_rate + opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + # Check the parameters. + self.assertAllCloseAccordingToType( + np.array([ + 1.0 - (0.1 * 2.0 / math.sqrt(0.001 + 1.0)), + 2.0 - (0.1 * 2.0 / math.sqrt(0.001 + 1.0)) + ]), self.evaluate(var0)) + self.assertAllCloseAccordingToType( + np.array([ + 3.0 - (0.01 * 2.0 / math.sqrt(0.00001 + 1.0)), + 4.0 - (0.01 * 2.0 / math.sqrt(0.00001 + 1.0)) + ]), self.evaluate(var1)) + # Step 2: the root mean square accumulators contain the previous update. + opt.apply_gradients(zip([grads0, grads1], [var0, var1])) + # Check the parameters. + self.assertAllCloseAccordingToType( + np.array([ + 1.0 - (0.1 * 2.0 / math.sqrt(0.001 + 1.0)) - + (0.1 * 2.0 / math.sqrt(0.001 * 0.9 + 0.001 + 1.0)), + 2.0 - (0.1 * 2.0 / math.sqrt(0.001 + 1.0)) - + (0.1 * 2.0 / math.sqrt(0.001 * 0.9 + 0.001 + 1.0)) + ]), self.evaluate(var0)) + self.assertAllCloseAccordingToType( + np.array([ + 3.0 - (0.01 * 2.0 / math.sqrt(0.00001 + 1.0)) - + (0.01 * 2.0 / math.sqrt(0.00001 * 0.9 + 1e-5 + 1.0)), + 4.0 - (0.01 * 2.0 / math.sqrt(0.00001 + 1.0)) - + (0.01 * 2.0 / math.sqrt(0.00001 * 0.9 + 1e-5 + 1.0)) + ]), self.evaluate(var1)) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/keras/optimizers.py b/tensorflow/python/keras/optimizers.py index 715d80a116..10466eb573 100644 --- a/tensorflow/python/keras/optimizers.py +++ b/tensorflow/python/keras/optimizers.py @@ -22,7 +22,17 @@ from __future__ import print_function import six from six.moves import zip # pylint: disable=redefined-builtin +from tensorflow.python import tf2 +from tensorflow.python.framework import ops from tensorflow.python.keras import backend as K +from tensorflow.python.keras.optimizer_v2 import adadelta as adadelta_v2 +from tensorflow.python.keras.optimizer_v2 import adagrad as adagrad_v2 +from tensorflow.python.keras.optimizer_v2 import adam as adam_v2 +from tensorflow.python.keras.optimizer_v2 import adamax as adamax_v2 +from tensorflow.python.keras.optimizer_v2 import gradient_descent as gradient_descent_v2 +from tensorflow.python.keras.optimizer_v2 import nadam as nadam_v2 +from tensorflow.python.keras.optimizer_v2 import optimizer_v2 +from tensorflow.python.keras.optimizer_v2 import rmsprop as rmsprop_v2 from tensorflow.python.keras.utils.generic_utils import deserialize_keras_object from tensorflow.python.keras.utils.generic_utils import serialize_keras_object from tensorflow.python.ops import clip_ops @@ -473,7 +483,7 @@ class Adam(Optimizer): def get_updates(self, loss, params): grads = self.get_gradients(loss, params) - self.updates = [state_ops.assign_add(self.iterations, 1)] + self.updates = [] lr = self.lr if self.initial_decay > 0: @@ -481,7 +491,8 @@ class Adam(Optimizer): 1. / (1. + self.decay * math_ops.cast(self.iterations, K.dtype(self.decay)))) - t = math_ops.cast(self.iterations, K.floatx()) + 1 + with ops.control_dependencies([state_ops.assign_add(self.iterations, 1)]): + t = math_ops.cast(self.iterations, K.floatx()) lr_t = lr * ( K.sqrt(1. - math_ops.pow(self.beta_2, t)) / (1. - math_ops.pow(self.beta_1, t))) @@ -795,16 +806,27 @@ def deserialize(config, custom_objects=None): Returns: A Keras Optimizer instance. """ - all_classes = { - 'sgd': SGD, - 'rmsprop': RMSprop, - 'adagrad': Adagrad, - 'adadelta': Adadelta, - 'adam': Adam, - 'adamax': Adamax, - 'nadam': Nadam, - 'tfoptimizer': TFOptimizer, - } + if tf2.enabled(): + all_classes = { + 'adadelta': adadelta_v2.Adadelta, + 'adagrad': adagrad_v2.Adagrad, + 'adam': adam_v2.Adam, + 'adamax': adamax_v2.Adamax, + 'nadam': nadam_v2.Nadam, + 'rmsprop': rmsprop_v2.RMSprop, + 'sgd': gradient_descent_v2.SGD + } + else: + all_classes = { + 'adadelta': Adadelta, + 'adagrad': Adagrad, + 'adam': Adam, + 'adamax': Adamax, + 'nadam': Nadam, + 'rmsprop': RMSprop, + 'sgd': SGD, + 'tfoptimizer': TFOptimizer + } # Make deserialization case-insensitive for built-in optimizers. if config['class_name'].lower() in all_classes: config['class_name'] = config['class_name'].lower() @@ -833,17 +855,17 @@ def get(identifier): Raises: ValueError: If `identifier` cannot be interpreted. """ + if isinstance(identifier, (Optimizer, optimizer_v2.OptimizerV2)): + return identifier # Wrap TF optimizer instances - if isinstance(identifier, tf_optimizer_module.Optimizer): + elif isinstance(identifier, tf_optimizer_module.Optimizer): opt = TFOptimizer(identifier) K.track_tf_optimizer(opt) return opt - if isinstance(identifier, dict): + elif isinstance(identifier, dict): return deserialize(identifier) elif isinstance(identifier, six.string_types): config = {'class_name': str(identifier), 'config': {}} return deserialize(config) - if isinstance(identifier, Optimizer): - return identifier else: raise ValueError('Could not interpret optimizer identifier:', identifier) diff --git a/tensorflow/python/keras/optimizers_test.py b/tensorflow/python/keras/optimizers_test.py index 9664f09fff..d3cacb702c 100644 --- a/tensorflow/python/keras/optimizers_test.py +++ b/tensorflow/python/keras/optimizers_test.py @@ -19,11 +19,14 @@ from __future__ import division from __future__ import print_function import gc +import os import weakref +from absl.testing import parameterized import numpy as np from tensorflow.python import keras +from tensorflow.python import tf2 from tensorflow.python.eager import context from tensorflow.python.framework import ops from tensorflow.python.framework import test_util @@ -88,22 +91,26 @@ def _test_optimizer(optimizer, target=0.75): class KerasOptimizersTest(test.TestCase): + @test_util.run_deprecated_v1 def test_sgd(self): with self.cached_session(): _test_optimizer(keras.optimizers.SGD(lr=0.01, momentum=0.9, nesterov=True)) + @test_util.run_deprecated_v1 def test_rmsprop(self): with self.cached_session(): _test_optimizer(keras.optimizers.RMSprop()) _test_optimizer(keras.optimizers.RMSprop(decay=1e-3)) + @test_util.run_deprecated_v1 def test_adagrad(self): with self.cached_session(): _test_optimizer(keras.optimizers.Adagrad()) _test_optimizer(keras.optimizers.Adagrad(decay=1e-3)) + @test_util.run_deprecated_v1 def test_adadelta(self): with self.cached_session(): _test_optimizer(keras.optimizers.Adadelta(), target=0.6) @@ -112,27 +119,32 @@ class KerasOptimizersTest(test.TestCase): # the accuracy. _test_optimizer(keras.optimizers.Adadelta(decay=1e-3), target=0.4) + @test_util.run_deprecated_v1 def test_adam(self): with self.cached_session(): _test_optimizer(keras.optimizers.Adam()) _test_optimizer(keras.optimizers.Adam(decay=1e-3)) _test_optimizer(keras.optimizers.Adam(amsgrad=True)) + @test_util.run_deprecated_v1 def test_adamax(self): with self.cached_session(): _test_optimizer(keras.optimizers.Adamax()) _test_optimizer(keras.optimizers.Adamax(decay=1e-3)) + @test_util.run_deprecated_v1 def test_nadam(self): with self.cached_session(): _test_optimizer(keras.optimizers.Nadam()) + @test_util.run_deprecated_v1 def test_clipnorm(self): with self.cached_session(): _test_optimizer(keras.optimizers.SGD(lr=0.01, momentum=0.9, clipnorm=0.5)) + @test_util.run_deprecated_v1 def test_clipvalue(self): with self.cached_session(): _test_optimizer(keras.optimizers.SGD(lr=0.01, @@ -208,5 +220,40 @@ class KerasOptimizersTest(test.TestCase): _ = keras.optimizers.Adam(clipnorm=-2.0) +@test_util.run_all_in_graph_and_eager_modes +class KerasV2OptimizersTest(test.TestCase, parameterized.TestCase): + + @parameterized.named_parameters( + ('adadelta_tf2', 'adadelta', True), ('adadelta_tf1', 'adadelta', False), + ('adagrad_tf2', 'adagrad', True), ('adagrad_tf1', 'adagrad', False), + ('adam_tf2', 'adam', True), ('adam_tf1', 'adam', False), + ('adamax_tf2', 'adamax', True), ('adamax_tf1', 'adamax', False), + ('sgd_tf2', 'sgd', True), ('sgd_tf1', 'sgd', False), + ('nadam_tf2', 'nadam', True), ('nadam_tf1', 'nadam', False), + ('rmsprop_tf2', 'rmsprop', True), ('rmsprop_tf1', 'rmsprop', False)) + def test_load_from_string(self, optimizer_string, tf2mode): + old_mode = os.environ.get('TF2_BEHAVIOR', None) + if tf2mode: + os.environ['TF2_BEHAVIOR'] = 'enabled' + else: + if 'TF2_BEHAVIOR' in os.environ: + del os.environ['TF2_BEHAVIOR'] + + # Sanity check. + self.assertEqual(tf2.enabled(), tf2mode) + + model = keras.models.Sequential() + model.add(keras.layers.Dense(1, input_shape=(10,))) + model.compile(optimizer_string, 'binary_crossentropy') + + self.assertEqual(optimizer_string, + model.optimizer.__class__.__name__.lower()) + + model.fit(np.ones((10, 10), 'float32'), np.ones((10, 1), 'float32')) + + if old_mode is not None: + os.environ['TF2_BEHAVIOR'] = old_mode + + if __name__ == '__main__': test.main() diff --git a/tensorflow/python/keras/regularizers_test.py b/tensorflow/python/keras/regularizers_test.py index bba4ebb287..3d6b259d87 100644 --- a/tensorflow/python/keras/regularizers_test.py +++ b/tensorflow/python/keras/regularizers_test.py @@ -20,6 +20,7 @@ from __future__ import print_function from tensorflow.python import keras from tensorflow.python.keras import testing_utils +from tensorflow.python.framework import test_util from tensorflow.python.platform import test @@ -61,6 +62,7 @@ class KerasRegularizersTest(test.TestCase): model.fit(x_train, y_train, batch_size=10, epochs=1, verbose=0) + @test_util.run_deprecated_v1 def test_activity_regularization(self): with self.cached_session(): (x_train, y_train), _ = get_data() diff --git a/tensorflow/python/keras/testing_utils.py b/tensorflow/python/keras/testing_utils.py index d342131a52..7827e16627 100644 --- a/tensorflow/python/keras/testing_utils.py +++ b/tensorflow/python/keras/testing_utils.py @@ -149,6 +149,10 @@ def layer_test(layer_cls, kwargs=None, input_shape=None, input_dtype=None, np.testing.assert_allclose(output, actual_output, rtol=1e-3) # test training mode (e.g. useful for dropout tests) + # Rebuild the model to avoid the graph being reused between predict() and + # train(). This was causing some error for layer with Defun as it body. + # See b/120160788 for more details. This should be mitigated after 2.0. + model = keras.models.Model(x, layer(x)) model.compile(RMSPropOptimizer(0.01), 'mse', weighted_metrics=['acc']) model.train_on_batch(input_data, actual_output) diff --git a/tensorflow/python/keras/utils/__init__.py b/tensorflow/python/keras/utils/__init__.py index 8939044f71..61940ad789 100644 --- a/tensorflow/python/keras/utils/__init__.py +++ b/tensorflow/python/keras/utils/__init__.py @@ -34,6 +34,7 @@ from tensorflow.python.keras.utils.generic_utils import serialize_keras_object from tensorflow.python.keras.utils.io_utils import HDF5Matrix from tensorflow.python.keras.utils.layer_utils import convert_all_kernels_in_model from tensorflow.python.keras.utils.layer_utils import get_source_inputs +from tensorflow.python.keras.utils.losses_utils import squeeze_or_expand_dimensions from tensorflow.python.keras.utils.multi_gpu_utils import multi_gpu_model from tensorflow.python.keras.utils.np_utils import normalize from tensorflow.python.keras.utils.np_utils import to_categorical diff --git a/tensorflow/python/keras/utils/generic_utils.py b/tensorflow/python/keras/utils/generic_utils.py index 375bd9d196..c331ce430b 100644 --- a/tensorflow/python/keras/utils/generic_utils.py +++ b/tensorflow/python/keras/utils/generic_utils.py @@ -319,14 +319,16 @@ class Progbar(object): will be displayed as-is. All others will be averaged by the progbar before display. interval: Minimum visual progress update interval (in seconds). + unit_name: Display name for step counts (usually "step" or "sample"). """ def __init__(self, target, width=30, verbose=1, interval=0.05, - stateful_metrics=None): + stateful_metrics=None, unit_name='step'): self.target = target self.width = width self.verbose = verbose self.interval = interval + self.unit_name = unit_name if stateful_metrics: self.stateful_metrics = set(stateful_metrics) else: @@ -425,12 +427,12 @@ class Progbar(object): info = ' - ETA: %s' % eta_format else: - if time_per_unit >= 1: - info += ' %.0fs/step' % time_per_unit + if time_per_unit >= 1 or time_per_unit == 0: + info += ' %.0fs/%s' % (time_per_unit, self.unit_name) elif time_per_unit >= 1e-3: - info += ' %.0fms/step' % (time_per_unit * 1e3) + info += ' %.0fms/%s' % (time_per_unit * 1e3, self.unit_name) else: - info += ' %.0fus/step' % (time_per_unit * 1e6) + info += ' %.0fus/%s' % (time_per_unit * 1e6, self.unit_name) for k in self._values_order: info += ' - %s:' % k diff --git a/tensorflow/python/keras/utils/layer_utils.py b/tensorflow/python/keras/utils/layer_utils.py index 158a9a5e76..60677be735 100644 --- a/tensorflow/python/keras/utils/layer_utils.py +++ b/tensorflow/python/keras/utils/layer_utils.py @@ -77,7 +77,7 @@ def count_params(weights): Returns: The total number of scalars composing the weights """ - return int(np.sum([np.prod(p.get_shape().as_list()) for p in set(weights)])) + return int(sum(np.prod(p.get_shape().as_list()) for p in set(weights))) def print_summary(model, line_length=None, positions=None, print_fn=None): diff --git a/tensorflow/python/keras/utils/losses_utils.py b/tensorflow/python/keras/utils/losses_utils.py new file mode 100644 index 0000000000..fc4b4ac7df --- /dev/null +++ b/tensorflow/python/keras/utils/losses_utils.py @@ -0,0 +1,189 @@ +# Copyright 2018 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. +# ============================================================================== +# pylint: disable=protected-access +"""Utilities related to loss functions.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.framework import ops +from tensorflow.python.keras import backend as K +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import confusion_matrix +from tensorflow.python.ops import control_flow_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import weights_broadcast_ops +from tensorflow.python.ops.losses import losses_impl + + +def squeeze_or_expand_dimensions(y_pred, y_true, sample_weight): + """Squeeze or expand last dimension if needed. + + 1. Squeezes last dim of `y_pred` or `y_true` if their rank differs by 1 + (using `confusion_matrix.remove_squeezable_dimensions`). + 2. Squeezes or expands last dim of `sample_weight` if its rank differs by 1 + from the new rank of `y_pred`. + If `sample_weight` is scalar, it is kept scalar. + + This will use static shape if available. Otherwise, it will add graph + operations, which could result in a performance hit. + + Args: + y_pred: Predicted values, a `Tensor` of arbitrary dimensions. + y_true: Optional label `Tensor` whose dimensions match `y_pred`. + sample_weight: Optional weight scalar or `Tensor` whose dimensions match + `y_pred`. + + Returns: + Tuple of `y_pred`, `y_true` and `sample_weight`. Each of them possibly has + the last dimension squeezed, + `sample_weight` could be extended by one dimension. + """ + if y_true is not None: + # squeeze last dim of `y_pred` or `y_true` if their rank differs by 1 + y_true, y_pred = confusion_matrix.remove_squeezable_dimensions( + y_true, y_pred) + + if sample_weight is None: + return y_pred, y_true, None + + sample_weight = ops.convert_to_tensor(sample_weight) + weights_shape = sample_weight.get_shape() + weights_rank = weights_shape.ndims + if weights_rank == 0: # If weights is scalar, do nothing. + return y_pred, y_true, sample_weight + + y_pred_shape = y_pred.get_shape() + y_pred_rank = y_pred_shape.ndims + if (y_pred_rank is not None) and (weights_rank is not None): + # Use static rank. + if weights_rank - y_pred_rank == 1: + sample_weight = array_ops.squeeze(sample_weight, [-1]) + elif y_pred_rank - weights_rank == 1: + sample_weight = array_ops.expand_dims(sample_weight, [-1]) + return y_pred, y_true, sample_weight + + # Use dynamic rank. + weights_rank_tensor = array_ops.rank(sample_weight) + rank_diff = weights_rank_tensor - array_ops.rank(y_pred) + maybe_squeeze_weights = lambda: array_ops.squeeze(sample_weight, [-1]) + + def _maybe_expand_weights(): + return control_flow_ops.cond( + math_ops.equal(rank_diff, + -1), lambda: array_ops.expand_dims(sample_weight, [-1]), + lambda: sample_weight) + + def _maybe_adjust_weights(): + return control_flow_ops.cond( + math_ops.equal(rank_diff, 1), maybe_squeeze_weights, + _maybe_expand_weights) + + # squeeze or expand last dim of `sample_weight` if its rank differs by 1 + # from the new rank of `y_pred`. + sample_weight = control_flow_ops.cond( + math_ops.equal(weights_rank_tensor, 0), lambda: sample_weight, + _maybe_adjust_weights) + return y_pred, y_true, sample_weight + + +def _safe_mean(losses, num_present): + """Computes a safe mean of the losses. + + Args: + losses: `Tensor` whose elements contain individual loss measurements. + num_present: The number of measurable elements in `losses`. + + Returns: + A scalar representing the mean of `losses`. If `num_present` is zero, + then zero is returned. + """ + total_loss = math_ops.reduce_sum(losses) + return math_ops.div_no_nan(total_loss, num_present, name='value') + + +def _num_elements(losses): + """Computes the number of elements in `losses` tensor.""" + with ops.name_scope(None, 'num_elements', values=[losses]) as scope: + return math_ops.cast(array_ops.size(losses, name=scope), dtype=losses.dtype) + + +def _reduce_weighted_loss( + weighted_losses, reduction=losses_impl.ReductionV2.SUM_OVER_BATCH_SIZE): + """Reduces the individual weighted loss measurements.""" + if reduction == losses_impl.ReductionV2.NONE: + loss = weighted_losses + else: + loss = math_ops.reduce_sum(weighted_losses) + if reduction == losses_impl.ReductionV2.SUM_OVER_BATCH_SIZE: + loss = _safe_mean(loss, _num_elements(weighted_losses)) + return loss + + +def compute_weighted_loss(losses, + sample_weight=None, + reduction=losses_impl.ReductionV2.SUM_OVER_BATCH_SIZE, + name=None): + """Computes the weighted loss. + + Args: + losses: `Tensor` of shape `[batch_size, d1, ... dN]`. + sample_weight: Optional `Tensor` whose rank is either 0, or the same rank as + `losses`, or be broadcastable to `losses`. + reduction: Type of `tf.losses.Reduction` to apply to loss. Default value is + `SUM_OVER_BATCH_SIZE`. + name: Optional name for the op. + + Raises: + ValueError: If the shape of `sample_weight` is not compatible with `losses`. + + Returns: + Weighted loss `Tensor` of the same type as `losses`. If `reduction` is + `NONE`, this has the same shape as `losses`; otherwise, it is scalar. + """ + losses_impl.ReductionV2.validate(reduction) + if sample_weight is None: + sample_weight = 1.0 + with ops.name_scope(name, 'weighted_loss', (losses, sample_weight)): + # Save the `reduction` argument for loss normalization when distributing + # to multiple replicas. + # TODO(josh11b): Associate it with the returned op for more precision. + ops.get_default_graph()._last_loss_reduction = reduction # pylint: disable=protected-access + + # Update dimensions of `sample_weight` to match with `losses` if possible. + losses, _, sample_weight = squeeze_or_expand_dimensions( + losses, None, sample_weight) + losses = ops.convert_to_tensor(losses) + input_dtype = losses.dtype + losses = math_ops.to_float(losses) + sample_weight = math_ops.to_float(sample_weight) + + try: + # Broadcast weights if possible. + sample_weight = weights_broadcast_ops.broadcast_weights( + sample_weight, losses) + except ValueError: + # Reduce values to same ndim as weight array. + ndim = K.ndim(losses) + weight_ndim = K.ndim(sample_weight) + losses = K.mean(losses, axis=list(range(weight_ndim, ndim))) + + sample_weight.get_shape().assert_is_compatible_with(losses.get_shape()) + weighted_losses = math_ops.multiply(losses, sample_weight) + # Apply reduction function to the individual weighted losses. + loss = _reduce_weighted_loss(weighted_losses, reduction) + # Convert the result back to the input type. + loss = math_ops.cast(loss, input_dtype) + return loss diff --git a/tensorflow/python/keras/utils/multi_gpu_utils_test.py b/tensorflow/python/keras/utils/multi_gpu_utils_test.py index 1780ab6587..8c1abd6324 100644 --- a/tensorflow/python/keras/utils/multi_gpu_utils_test.py +++ b/tensorflow/python/keras/utils/multi_gpu_utils_test.py @@ -158,7 +158,7 @@ class TestMultiGPUModel(test.TestCase): dataset = data.Dataset.from_tensor_slices((x_train, y_train)) dataset = dataset.repeat() dataset = dataset.batch(4) - iterator = dataset.make_one_shot_iterator() + iterator = data.make_one_shot_iterator(dataset) inputs, targets = iterator.get_next() diff --git a/tensorflow/python/keras/utils/tf_utils.py b/tensorflow/python/keras/utils/tf_utils.py index 6b7c6c34a2..7b4c9e7239 100644 --- a/tensorflow/python/keras/utils/tf_utils.py +++ b/tensorflow/python/keras/utils/tf_utils.py @@ -161,6 +161,9 @@ def are_all_symbolic_tensors(tensors): return all(is_symbolic_tensor(tensor) for tensor in tensors) +_user_convertible_tensor_types = set() + + def is_symbolic_tensor(tensor): """Returns whether a tensor is symbolic (from a TF graph) or an eager tensor. @@ -176,9 +179,40 @@ def is_symbolic_tensor(tensor): if isinstance(tensor, variables.Variable): return not context.executing_eagerly() if isinstance(tensor, (ops.Tensor, sparse_tensor.SparseTensor)): - try: - _ = tensor.graph - return True - except AttributeError: - return False + return hasattr(tensor, 'graph') + if isinstance(tensor, tuple(_user_convertible_tensor_types)): + return hasattr(ops.convert_to_tensor(tensor), 'graph') return False + + +def register_symbolic_tensor_type(cls): + """Allows users to specify types regarded as symbolic `Tensor`s. + + Used in conjunction with `tf.register_tensor_conversion_function`, calling + `tf.keras.utils.register_symbolic_tensor_type(cls)` allows non-`Tensor` + objects to be plumbed through Keras layers. + + Example: + + ```python + # One-time setup. + class Foo(object): + def __init__(self, input_): + self._input = input_ + def value(self): + return tf.constant(42.) + + tf.register_tensor_conversion_function( + Foo, lambda x, *args, **kwargs: x.value()) + + tf.keras.utils.register_symbolic_tensor_type(Foo) + + # User-land. + layer = tf.keras.layers.Lambda(lambda input_: Foo(input_)) + ``` + + Arguments: + cls: A `class` type which shall be regarded as a symbolic `Tensor`. + """ + global _user_convertible_tensor_types + _user_convertible_tensor_types.add(cls) diff --git a/tensorflow/python/keras/utils/tf_utils_test.py b/tensorflow/python/keras/utils/tf_utils_test.py new file mode 100644 index 0000000000..9833a49299 --- /dev/null +++ b/tensorflow/python/keras/utils/tf_utils_test.py @@ -0,0 +1,134 @@ +# Copyright 2018 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 Keras TF utils.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python import keras +from tensorflow.python.eager import context +from tensorflow.python.framework import ops +from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import test_util +from tensorflow.python.keras.utils import tf_utils +from tensorflow.python.ops import variables +from tensorflow.python.platform import test + + +@test_util.run_all_in_graph_and_eager_modes +class TestIsSymbolicTensor(test.TestCase): + + def test_default_behavior(self): + if context.executing_eagerly(): + self.assertFalse(tf_utils.is_symbolic_tensor( + variables.Variable(name='blah', initial_value=0.))) + self.assertFalse(tf_utils.is_symbolic_tensor( + ops.convert_to_tensor(0.))) + self.assertFalse(tf_utils.is_symbolic_tensor( + sparse_tensor.SparseTensor( + indices=[[0, 0], [1, 2]], values=[1, 2], dense_shape=[3, 4]))) + else: + self.assertTrue(tf_utils.is_symbolic_tensor( + variables.Variable(name='blah', initial_value=0.))) + self.assertTrue(tf_utils.is_symbolic_tensor( + ops.convert_to_tensor(0.))) + self.assertTrue(tf_utils.is_symbolic_tensor( + sparse_tensor.SparseTensor( + indices=[[0, 0], [1, 2]], values=[1, 2], dense_shape=[3, 4]))) + + def test_works_with_registered(self): + + class CustomClass(object): + + def value(self): + return ops.convert_to_tensor(42.) + + ops.register_tensor_conversion_function( + CustomClass, lambda value, **_: value.value()) + + tf_utils.register_symbolic_tensor_type(CustomClass) + + if context.executing_eagerly(): + self.assertFalse(tf_utils.is_symbolic_tensor( + variables.Variable(name='blah', initial_value=0.))) + self.assertFalse(tf_utils.is_symbolic_tensor( + ops.convert_to_tensor(0.))) + self.assertFalse(tf_utils.is_symbolic_tensor( + sparse_tensor.SparseTensor( + indices=[[0, 0], [1, 2]], values=[1, 2], dense_shape=[3, 4]))) + self.assertFalse(tf_utils.is_symbolic_tensor(CustomClass())) + else: + self.assertTrue(tf_utils.is_symbolic_tensor( + variables.Variable(name='blah', initial_value=0.))) + self.assertTrue(tf_utils.is_symbolic_tensor( + ops.convert_to_tensor(0.))) + self.assertTrue(tf_utils.is_symbolic_tensor( + sparse_tensor.SparseTensor( + indices=[[0, 0], [1, 2]], values=[1, 2], dense_shape=[3, 4]))) + self.assertTrue(tf_utils.is_symbolic_tensor(CustomClass())) + + def test_enables_nontensor_plumbing(self): + # Setup. + + class Foo(object): + + def __init__(self, input_): + self._input = input_ + self.value = ops.convert_to_tensor(42.) + + ops.register_tensor_conversion_function( + Foo, lambda x, *args, **kwargs: x.value) + tf_utils.register_symbolic_tensor_type(Foo) + + class PlumbingLayer(keras.layers.Lambda): + + def __init__(self, fn, **kwargs): + def _fn(*fargs, **fkwargs): + d = fn(*fargs, **fkwargs) + x = ops.convert_to_tensor(d) + d.shape = x.shape + d.get_shape = x.get_shape + return d, x + super(PlumbingLayer, self).__init__(_fn, **kwargs) + self._enter_dunder_call = False + + def __call__(self, inputs, *args, **kwargs): + self._enter_dunder_call = True + d, _ = super(PlumbingLayer, self).__call__(inputs, *args, **kwargs) + self._enter_dunder_call = False + return d + + def call(self, inputs, *args, **kwargs): + d, v = super(PlumbingLayer, self).call(inputs, *args, **kwargs) + if self._enter_dunder_call: + return d, v + return d + + # User-land. + model = keras.Sequential([ + keras.layers.InputLayer([]), + PlumbingLayer(Foo), # Makes a `Foo` object. + ]) + # Let's ensure Keras graph history is preserved by composing the models. + model = keras.Model(model.inputs, model(model.outputs)) + # Now we instantiate the model and verify we have a `Foo` object, not a + # `Tensor`. + y = model(ops.convert_to_tensor(7.)) + self.assertIsInstance(y, Foo) + + +if __name__ == '__main__': + test.main() diff --git a/tensorflow/python/kernel_tests/BUILD b/tensorflow/python/kernel_tests/BUILD index 6b990d3a92..97ac21b8ad 100644 --- a/tensorflow/python/kernel_tests/BUILD +++ b/tensorflow/python/kernel_tests/BUILD @@ -121,8 +121,10 @@ cuda_py_test( "@absl_py//absl/testing:parameterized", "//third_party/py/numpy", "//tensorflow/python:array_ops", - "//tensorflow/python:math_ops", + "//tensorflow/python:gradients_impl", "//tensorflow/python:list_ops", + "//tensorflow/python:math_ops", + "//tensorflow/python:tensor_shape", "//tensorflow/python/eager:context", "//tensorflow/python:framework_for_generated_wrappers", "//tensorflow/python:framework_test_lib", @@ -268,7 +270,7 @@ tf_py_test( ], ) -tf_py_test( +cuda_py_test( name = "ctc_loss_op_test", size = "small", srcs = ["ctc_loss_op_test.py"], @@ -659,6 +661,18 @@ cuda_py_test( ], ) +cuda_py_test( + name = "matrix_square_root_op_test", + size = "medium", + srcs = ["matrix_square_root_op_test.py"], + additional_deps = [ + "//third_party/py/numpy", + "//tensorflow/python:client_testlib", + "//tensorflow/python:framework_for_generated_wrappers", + "//tensorflow/python:linalg_ops", + ], +) + cuda_py_test( name = "matrix_solve_op_test", size = "medium", @@ -817,6 +831,7 @@ tf_py_test( "//tensorflow/core:protos_all_py", "//tensorflow/python:client", "//tensorflow/python:client_testlib", + "//tensorflow/python:constant_op", "//tensorflow/python:io_ops", "//tensorflow/python:io_ops_gen", ], @@ -1142,6 +1157,21 @@ cuda_py_test( ], ) +tf_py_test( + name = "unicode_encode_op_test", + size = "small", + srcs = ["unicode_encode_op_test.py"], + additional_deps = [ + "@absl_py//absl/testing:parameterized", + "//third_party/py/numpy", + "//tensorflow/python:client_testlib", + "//tensorflow/python:constant_op", + "//tensorflow/python:errors", + "//tensorflow/python/ops/ragged:ragged_factory_ops", + "//tensorflow/python/ops/ragged:ragged_string_ops", + ], +) + tf_py_test( name = "unicode_transcode_op_test", size = "small", @@ -1154,6 +1184,18 @@ tf_py_test( ], ) +tf_py_test( + name = "unicode_decode_op_test", + size = "small", + srcs = ["unicode_decode_op_test.py"], + additional_deps = [ + "@absl_py//absl/testing:parameterized", + "//tensorflow/python:client_testlib", + "//tensorflow/python:framework_for_generated_wrappers", + "//tensorflow/python:string_ops", + ], +) + tf_py_test( name = "unique_op_test", size = "small", @@ -1338,6 +1380,7 @@ cuda_py_test( "//tensorflow/python:test_ops", "//tensorflow/python:variables", "//tensorflow/python/eager:context", + "//tensorflow/python/eager:def_function", ], shard_count = 10, tags = [ @@ -1449,6 +1492,7 @@ cuda_py_test( additional_deps = [ "//third_party/py/numpy", "//tensorflow/python/eager:context", + "//tensorflow/python/eager:def_function", "//tensorflow/python:array_ops", "//tensorflow/python:check_ops", "//tensorflow/python:math_ops", @@ -1736,9 +1780,11 @@ cuda_py_test( "//tensorflow/python:tensor_array_grad", "//tensorflow/python:variable_scope", "//tensorflow/python:variables", + "//tensorflow/python:while_v2", "//tensorflow/python/data/ops:iterator_ops", ], grpc_enabled = True, + shard_count = 2, tags = ["no_windows"], ) @@ -2371,6 +2417,8 @@ cuda_py_test( "//tensorflow/python:tensor_array_ops", "//tensorflow/python:variables", "//tensorflow/python:variable_scope", + "//tensorflow/python:cond_v2", + "//tensorflow/python:while_v2", "//tensorflow/python/eager:backprop", "//tensorflow/python/eager:context", ], @@ -2614,34 +2662,6 @@ cuda_py_test( tags = ["manual"], ) -cuda_py_test( - name = "dct_ops_test", - srcs = ["dct_ops_test.py"], - additional_deps = [ - "//third_party/py/numpy", - "//tensorflow/python:client_testlib", - "//tensorflow/python:framework_for_generated_wrappers", - "//tensorflow/python:spectral_ops", - "//tensorflow/python:spectral_ops_test_util", - ], -) - -cuda_py_test( - name = "fft_ops_test", - size = "medium", - srcs = ["fft_ops_test.py"], - additional_deps = [ - "//third_party/py/numpy", - "//tensorflow/python:client_testlib", - "//tensorflow/python:framework_for_generated_wrappers", - "//tensorflow/python:math_ops", - "//tensorflow/python:spectral_ops", - "//tensorflow/python:spectral_ops_test_util", - ], - shard_count = 4, - tags = ["optonly"], -) - cuda_py_test( name = "pooling_ops_3d_test", size = "medium", @@ -3330,7 +3350,9 @@ cuda_py_test( "//tensorflow/python:framework", "//tensorflow/python:framework_ops", "//tensorflow/python:gradients", + "//tensorflow/python:tensor_array_ops", "//tensorflow/python:training", + "//tensorflow/python:while_v2", ], grpc_enabled = True, ) diff --git a/tensorflow/python/kernel_tests/accumulate_n_test.py b/tensorflow/python/kernel_tests/accumulate_n_test.py index 7889edc198..5eece9c941 100644 --- a/tensorflow/python/kernel_tests/accumulate_n_test.py +++ b/tensorflow/python/kernel_tests/accumulate_n_test.py @@ -32,6 +32,7 @@ from tensorflow.python.platform import googletest class AccumulateNV2Test(test_util.TensorFlowTestCase): """Tests of the new, differentiable version of accumulate_n.""" + @test_util.run_deprecated_v1 def testFloat(self): np.random.seed(12345) x = [np.random.random((1, 2, 3, 4, 5)) - 0.5 for _ in range(5)] @@ -41,6 +42,7 @@ class AccumulateNV2Test(test_util.TensorFlowTestCase): self.assertAllClose(x[0] * 5, math_ops.accumulate_n([tf_x[0]] * 5).eval()) + @test_util.run_deprecated_v1 def testInt(self): np.random.seed(54321) x = [np.random.randint(-128, 128, (5, 4, 3, 2, 1)) for _ in range(6)] @@ -50,12 +52,14 @@ class AccumulateNV2Test(test_util.TensorFlowTestCase): self.assertAllEqual(x[0] * 6, math_ops.accumulate_n([tf_x[0]] * 6).eval()) + @test_util.run_deprecated_v1 def testUnknownShape(self): with self.session(use_gpu=True): x0 = array_ops.placeholder(dtype=dtypes_lib.int32, shape=[None]) acc = math_ops.accumulate_n([x0, x0], shape=[None]) self.assertAllEqual([2, 4], acc.eval(feed_dict={x0: [1, 2]})) + @test_util.run_deprecated_v1 def testGrad(self): np.random.seed(42) for num_inputs in range(1, 10): @@ -65,7 +69,7 @@ class AccumulateNV2Test(test_util.TensorFlowTestCase): for _ in range(0, num_inputs) ] accum_n = math_ops.accumulate_n(input_vars) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) accum_n_grad = gradients.gradients(accum_n, input_vars) self.assertAllEqual( np.repeat(1.0, num_inputs), # d/dx (x + y + ...) = 1 @@ -88,13 +92,13 @@ class AccumulateNV2Test(test_util.TensorFlowTestCase): np_val = random_arrays[0] for random_array in random_arrays[1:]: np_val += random_array - self.assertAllClose(np_val, tf_val.eval()) + self.assertAllClose(np_val, self.evaluate(tf_val)) def testZeroArgs(self): with self.cached_session(): with self.assertRaises(ValueError): tf_val = math_ops.accumulate_n([]) - tf_val.eval() + self.evaluate(tf_val) def testWrongShape(self): with self.cached_session(): diff --git a/tensorflow/python/kernel_tests/ackermann_test.py b/tensorflow/python/kernel_tests/ackermann_test.py index d267e49752..6c20b19be9 100644 --- a/tensorflow/python/kernel_tests/ackermann_test.py +++ b/tensorflow/python/kernel_tests/ackermann_test.py @@ -20,12 +20,14 @@ from __future__ import print_function import os from tensorflow.python.framework import load_library +from tensorflow.python.framework import test_util from tensorflow.python.platform import resource_loader from tensorflow.python.platform import test class AckermannTest(test.TestCase): + @test_util.run_deprecated_v1 def testBasic(self): library_filename = os.path.join(resource_loader.get_data_files_path(), 'ackermann_op.so') diff --git a/tensorflow/python/kernel_tests/aggregate_ops_test.py b/tensorflow/python/kernel_tests/aggregate_ops_test.py index 0f15319cb5..d9787cc3bf 100644 --- a/tensorflow/python/kernel_tests/aggregate_ops_test.py +++ b/tensorflow/python/kernel_tests/aggregate_ops_test.py @@ -24,6 +24,7 @@ from tensorflow.core.framework import tensor_pb2 from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import logging_ops from tensorflow.python.ops import math_ops @@ -61,12 +62,13 @@ class AddNTest(test.TestCase): for dtype in self._supported_types(): for count in range(1, self._MAX_N + 1): data = [self._buildData((2, 2), dtype) for _ in range(count)] - actual = sess.run(math_ops.add_n(data)) + actual = self.evaluate(math_ops.add_n(data)) expected = np.sum(np.vstack( [np.expand_dims(d, 0) for d in data]), axis=0) tol = 5e-3 if dtype == dtypes.float16 else 5e-7 self.assertAllClose(expected, actual, rtol=tol, atol=tol) + @test_util.run_deprecated_v1 def testUnknownShapes(self): np.random.seed(12345) with self.session(use_gpu=True) as sess: @@ -80,6 +82,7 @@ class AddNTest(test.TestCase): tol = 5e-3 if dtype == dtypes.float16 else 5e-7 self.assertAllClose(expected, actual, rtol=tol, atol=tol) + @test_util.run_deprecated_v1 def testVariant(self): def create_constant_variant(value): diff --git a/tensorflow/python/kernel_tests/argmax_op_test.py b/tensorflow/python/kernel_tests/argmax_op_test.py index fa370c17b4..06ec0948c2 100644 --- a/tensorflow/python/kernel_tests/argmax_op_test.py +++ b/tensorflow/python/kernel_tests/argmax_op_test.py @@ -20,6 +20,7 @@ from __future__ import print_function import numpy as np from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops from tensorflow.python.platform import test @@ -37,14 +38,14 @@ class ArgMaxTest(test.TestCase): with self.session(use_gpu=use_gpu): ans = method(x, axis=axis) if expected_err_re is None: - tf_ans = ans.eval() + tf_ans = self.evaluate(ans) # Defaults to int64 output. self.assertEqual(np.int64, tf_ans.dtype) self.assertAllEqual(tf_ans, expected_values) self.assertShapeEqual(expected_values, ans) else: with self.assertRaisesOpError(expected_err_re): - ans.eval() + self.evaluate(ans) def _testBothArg(self, method, @@ -79,7 +80,7 @@ class ArgMaxTest(test.TestCase): expected_values = x.argmax() with self.session(use_gpu=True): ans = math_ops.argmax(x, axis=0, output_type=dtypes.int32) - tf_ans = ans.eval() + tf_ans = self.evaluate(ans) self.assertEqual(np.int32, tf_ans.dtype) # The values are equal when comparing int32 to int64 because # the values don't have a range that exceeds 32-bit integers. @@ -87,7 +88,7 @@ class ArgMaxTest(test.TestCase): expected_values = x.argmin() with self.session(use_gpu=True): ans = math_ops.argmin(x, axis=0, output_type=dtypes.int32) - tf_ans = ans.eval() + tf_ans = self.evaluate(ans) self.assertEqual(np.int32, tf_ans.dtype) self.assertAllEqual(tf_ans, expected_values) @@ -110,12 +111,14 @@ class ArgMaxTest(test.TestCase): r"Reduction axis 0 is empty in shape \[0\]"): op([], 0).eval() + @test_util.run_deprecated_v1 def testDefaultAxis(self): with self.cached_session(): for op in math_ops.argmin, math_ops.argmax: ans = op([1]).eval() self.assertAllEqual(ans, 0) + @test_util.run_deprecated_v1 def testOutputEmpty(self): with self.cached_session(): for op in math_ops.argmin, math_ops.argmax: diff --git a/tensorflow/python/kernel_tests/array_ops_test.py b/tensorflow/python/kernel_tests/array_ops_test.py index c90794c789..f4c442b7b1 100644 --- a/tensorflow/python/kernel_tests/array_ops_test.py +++ b/tensorflow/python/kernel_tests/array_ops_test.py @@ -25,6 +25,7 @@ import numpy as np from tensorflow.python.client import session from tensorflow.python.eager import context +from tensorflow.python.eager import def_function from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors @@ -32,6 +33,7 @@ from tensorflow.python.framework import errors_impl from tensorflow.python.framework import ops from tensorflow.python.framework import sparse_tensor from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import tensor_spec from tensorflow.python.framework import test_ops from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops @@ -46,24 +48,23 @@ from tensorflow.python.ops import variables from tensorflow.python.platform import test as test_lib +@test_util.run_all_in_graph_and_eager_modes class BatchMatrixTransposeTest(test_util.TensorFlowTestCase): def testNonBatchMatrix(self): matrix = [[1, 2, 3], [4, 5, 6]] # Shape (2, 3) expected_transposed = [[1, 4], [2, 5], [3, 6]] # Shape (3, 2) - with self.cached_session(): - transposed = array_ops.matrix_transpose(matrix) - self.assertEqual((3, 2), transposed.get_shape()) - self.assertAllEqual(expected_transposed, transposed.eval()) + transposed = array_ops.matrix_transpose(matrix) + self.assertEqual((3, 2), transposed.get_shape()) + self.assertAllEqual(expected_transposed, transposed) def testConjugate(self): m = [[1 + 1j, 2 + 2j, 3 + 3j], [4 + 4j, 5 + 5j, 6 + 6j]] expected_transposed = [[1 - 1j, 4 - 4j], [2 - 2j, 5 - 5j], [3 - 3j, 6 - 6j]] - with self.cached_session(): - matrix = ops.convert_to_tensor(m) - transposed = array_ops.matrix_transpose(matrix, conjugate=True) - self.assertEqual((3, 2), transposed.get_shape()) - self.assertAllEqual(expected_transposed, transposed.eval()) + matrix = ops.convert_to_tensor(m) + transposed = array_ops.matrix_transpose(matrix, conjugate=True) + self.assertEqual((3, 2), transposed.get_shape()) + self.assertAllEqual(expected_transposed, transposed) def testBatchMatrix(self): matrix_0 = [[1, 2, 3], [4, 5, 6]] @@ -72,43 +73,44 @@ class BatchMatrixTransposeTest(test_util.TensorFlowTestCase): matrix_1_t = [[11, 44], [22, 55], [33, 66]] batch_matrix = [matrix_0, matrix_1] # Shape (2, 2, 3) expected_transposed = [matrix_0_t, matrix_1_t] # Shape (2, 3, 2) - with self.cached_session(): - transposed = array_ops.matrix_transpose(batch_matrix) - self.assertEqual((2, 3, 2), transposed.get_shape()) - self.assertAllEqual(expected_transposed, transposed.eval()) + transposed = array_ops.matrix_transpose(batch_matrix) + self.assertEqual((2, 3, 2), transposed.get_shape()) + self.assertAllEqual(expected_transposed, transposed) def testNonBatchMatrixDynamicallyDefined(self): - matrix = [[1, 2, 3], [4, 5, 6]] # Shape (2, 3) + # needs explicit `constant` because lists are not automatically + # converted to sensors when applying `transpose` below + matrix = constant_op.constant([[1, 2, 3], [4, 5, 6]]) # Shape (2, 3) expected_transposed = [[1, 4], [2, 5], [3, 6]] # Shape (3, 2) - with self.cached_session(): - matrix_ph = array_ops.placeholder(dtypes.int32) - transposed = array_ops.matrix_transpose(matrix_ph) - self.assertAllEqual( - expected_transposed, transposed.eval(feed_dict={ - matrix_ph: matrix - })) + @def_function.function(input_signature= + [tensor_spec.TensorSpec + (shape=None, dtype=dtypes.int32)]) + def transpose(matrix): + self.assertIs(matrix.shape.ndims, None) + return array_ops.matrix_transpose(matrix) + self.assertAllEqual(expected_transposed, transpose(matrix)) def testBatchMatrixDynamicallyDefined(self): matrix_0 = [[1, 2, 3], [4, 5, 6]] matrix_0_t = [[1, 4], [2, 5], [3, 6]] matrix_1 = [[11, 22, 33], [44, 55, 66]] matrix_1_t = [[11, 44], [22, 55], [33, 66]] - batch_matrix = [matrix_0, matrix_1] # Shape (2, 2, 3) + # needs explicit `constant` because lists are not automatically + # converted to sensors when applying `transpose` below + batch_matrix = constant_op.constant([matrix_0, matrix_1]) # Shape (2, 2, 3) expected_transposed = [matrix_0_t, matrix_1_t] # Shape (2, 3, 2) - with self.cached_session(): - batch_matrix_ph = array_ops.placeholder(dtypes.int32) - transposed = array_ops.matrix_transpose(batch_matrix_ph) - self.assertAllEqual( - expected_transposed, - transposed.eval(feed_dict={ - batch_matrix_ph: batch_matrix - })) + @def_function.function(input_signature= + [tensor_spec.TensorSpec + (shape=None, dtype=dtypes.int32)]) + def transpose(matrix): + self.assertIs(matrix.shape.ndims, None) + return array_ops.matrix_transpose(matrix) + self.assertAllEqual(expected_transposed, transpose(batch_matrix)) def testTensorWithStaticRankLessThanTwoRaisesBecauseNotAMatrix(self): vector = [1, 2, 3] - with self.cached_session(): - with self.assertRaisesRegexp(ValueError, "should be a "): - array_ops.matrix_transpose(vector) + with self.assertRaisesRegexp(ValueError, "should be a "): + array_ops.matrix_transpose(vector) class BooleanMaskTest(test_util.TensorFlowTestCase): @@ -141,36 +143,43 @@ class BooleanMaskTest(test_util.TensorFlowTestCase): self.assertAllClose(masked_arr, masked_tensor.eval()) + @test_util.run_deprecated_v1 def testMaskDim1ArrDim2Axis1(self): ndims_mask = 1 for arr_shape in [(1, 1), (2, 2), (2, 5)]: self.CheckVersusNumpy(ndims_mask, arr_shape, axis=1) + @test_util.run_deprecated_v1 def testMaskDim2ArrDim2Axis1(self): ndims_mask = 2 for arr_shape in [(1, 1), (2, 2), (2, 5)]: self.CheckVersusNumpy(ndims_mask, arr_shape, axis=1) + @test_util.run_deprecated_v1 def testMaskDim1ArrDim1(self): ndims_mask = 1 for arr_shape in [(1,), (2,), (3,), (10,)]: self.CheckVersusNumpy(ndims_mask, arr_shape) + @test_util.run_deprecated_v1 def testMaskDim1ArrDim2(self): ndims_mask = 1 for arr_shape in [(1, 1), (2, 2), (2, 5)]: self.CheckVersusNumpy(ndims_mask, arr_shape) + @test_util.run_deprecated_v1 def testMaskDim2ArrDim2(self): ndims_mask = 2 for arr_shape in [(1, 1), (2, 2), (2, 5)]: self.CheckVersusNumpy(ndims_mask, arr_shape) + @test_util.run_deprecated_v1 def testMaskDim2ArrDim3(self): ndims_mask = 2 for arr_shape in [(1, 1, 1), (1, 2, 2), (2, 2, 1)]: self.CheckVersusNumpy(ndims_mask, arr_shape) + @test_util.run_deprecated_v1 def testEmptyInput2D(self): mask = np.array([True, False]) arr = np.array([[], []]).astype(np.float32) @@ -189,6 +198,7 @@ class BooleanMaskTest(test_util.TensorFlowTestCase): with self.cached_session(): self.assertAllClose(numpy_result, tf_result.eval()) + @test_util.run_deprecated_v1 def testEmptyOutput(self): make_mask = lambda shape: np.zeros(shape, dtype=bool) for ndims_mask in range(1, 4): @@ -197,6 +207,7 @@ class BooleanMaskTest(test_util.TensorFlowTestCase): arr_shape = np.random.randint(1, 5, size=ndims_arr) self.CheckVersusNumpy(ndims_mask, arr_shape, make_mask=make_mask) + @test_util.run_deprecated_v1 def testWorksWithDimensionsEqualToNoneDuringGraphBuild(self): # The rank of the mask tensor must be specified. This is explained # in the docstring as well. @@ -215,6 +226,7 @@ class BooleanMaskTest(test_util.TensorFlowTestCase): }) np.testing.assert_allclose(masked_tensor, arr[mask]) + @test_util.run_deprecated_v1 def testMaskDimensionsSetToNoneRaises(self): # The rank of the mask tensor must be specified. This is explained # in the docstring as well. @@ -281,6 +293,7 @@ class OperatorShapeTest(test_util.TensorFlowTestCase): class ReverseV2Test(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testReverse0DimAuto(self): x_np = 4 for use_gpu in [False, True]: @@ -325,6 +338,7 @@ class ReverseV2Test(test_util.TensorFlowTestCase): # This test covers the axis validation in the shape function # (no eval()) + @test_util.run_deprecated_v1 def testInvalidAxis(self): x_np = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float32) with self.assertRaisesRegexp(ValueError, @@ -343,6 +357,7 @@ class ReverseV2Test(test_util.TensorFlowTestCase): # # Note: this test passes placeholder as constant axis is validated # in shape function (see testInvalidAxis) + @test_util.run_deprecated_v1 def testInvalid(self): x_np = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float32) axis = array_ops.placeholder(dtypes.int32) @@ -357,6 +372,7 @@ class ReverseV2Test(test_util.TensorFlowTestCase): "axis 0 specified more than once"): array_ops.reverse_v2(x_np, axis).eval(feed_dict={axis: [0, -2]}) + @test_util.run_deprecated_v1 def testReverse1DimAuto(self): for dtype in [ np.uint8, np.int8, np.uint16, np.int16, np.int32, np.int64, np.bool, @@ -365,6 +381,7 @@ class ReverseV2Test(test_util.TensorFlowTestCase): ]: self._reverse1DimAuto(dtype) + @test_util.run_deprecated_v1 def testReverse2DimAuto(self): for dtype in [ np.uint8, np.int8, np.uint16, np.int16, np.int32, np.int64, np.bool, @@ -373,6 +390,7 @@ class ReverseV2Test(test_util.TensorFlowTestCase): ]: self._reverse2DimAuto(dtype) + @test_util.run_deprecated_v1 def testUnknownDims(self): reverse_v2 = array_ops.reverse_v2 data_t = array_ops.placeholder(dtypes.float32) @@ -390,6 +408,7 @@ class ReverseV2Test(test_util.TensorFlowTestCase): reverse_2d_t = reverse_v2(data_2d_t, axis_2d_t) self.assertEqual(2, reverse_2d_t.get_shape().ndims) + @test_util.run_deprecated_v1 def testReverseRowsOf3Channels(self): """Tests optimized code for reversing rows with last dim size = 3.""" with self.session(use_gpu=True): @@ -403,6 +422,7 @@ class ReverseV2Test(test_util.TensorFlowTestCase): np_answer = x_np[:, ::-1, :] self.assertAllEqual(x_tf, np_answer) + @test_util.run_deprecated_v1 def testReverseRowsOf4Channels(self): with self.session(use_gpu=True): for reverse_f in [array_ops.reverse_v2, array_ops.reverse]: @@ -452,6 +472,7 @@ class MeshgridTest(test_util.TensorFlowTestCase): for x_np, x_tf in zip(numpy_out, tf_out): self.assertAllEqual(x_np, x_tf.eval()) + @test_util.run_deprecated_v1 def testCompare(self): for t in (np.float16, np.float32, np.float64, np.int32, np.int64, np.complex64, np.complex128): @@ -524,6 +545,7 @@ STRIDED_SLICE_TYPES = [ class StridedSliceTest(test_util.TensorFlowTestCase): """Test the strided slice operation with variants of slices.""" + @test_util.run_deprecated_v1 def test_basic_slice(self): for tensor_type in STRIDED_SLICE_TYPES: with self.cached_session(use_gpu=True): @@ -554,7 +576,8 @@ class StridedSliceTest(test_util.TensorFlowTestCase): def testInt64GPU(self): if not test_util.is_gpu_available(): self.skipTest("No GPU available") - with self.session(use_gpu=True, force_gpu=True): + + with test_util.force_gpu(): x = constant_op.constant([1., 2., 3.]) begin = constant_op.constant([2], dtype=dtypes.int64) end = constant_op.constant([3], dtype=dtypes.int64) @@ -578,6 +601,7 @@ class StridedSliceTest(test_util.TensorFlowTestCase): v = variables.Variable([1., 2.]) v[0] # pylint: disable=pointless-statement + @test_util.run_deprecated_v1 def testDegenerateSlices(self): with self.session(use_gpu=True): checker = StridedSliceChecker(self, StridedSliceChecker.REF_TENSOR) @@ -588,6 +612,7 @@ class StridedSliceTest(test_util.TensorFlowTestCase): # empty interval in every dimension _ = checker[-1:0, 2:2, 2:3:-1] + @test_util.run_deprecated_v1 def testEllipsis(self): with self.session(use_gpu=True): raw = [[[[[1, 2], [3, 4], [5, 6]]], [[[7, 8], [9, 10], [11, 12]]]]] @@ -608,6 +633,7 @@ class StridedSliceTest(test_util.TensorFlowTestCase): with self.assertRaisesRegexp(ValueError, "Multiple ellipses"): _ = checker[..., :, ...].eval() + @test_util.run_deprecated_v1 def testShrink(self): with self.session(use_gpu=True): raw = [[[[[1, 2, 4, 5], [5, 6, 7, 8], [9, 10, 11, 12]]], @@ -618,6 +644,7 @@ class StridedSliceTest(test_util.TensorFlowTestCase): _ = checker[:, 0] _ = checker[:, :, 0] + @test_util.run_deprecated_v1 def testBothNewAxisAndShrink(self): with self.session(use_gpu=True): ones = array_ops.placeholder(shape=[2, 2], dtype=dtypes.int16) @@ -626,6 +653,7 @@ class StridedSliceTest(test_util.TensorFlowTestCase): feed_dict={ones: [[1, 1], [1, 1]]}), [[1, 1]]) + @test_util.run_deprecated_v1 def testTensorIndexing(self): with self.session(use_gpu=True): raw = [[[[[1, 2, 4, 5], [5, 6, 7, 8], [9, 10, 11, 12]]], @@ -636,6 +664,7 @@ class StridedSliceTest(test_util.TensorFlowTestCase): _ = checker[..., bar:bar2] _ = checker[..., bar] _ = checker[..., 3] + _ = checker[..., 2 ** 64 // 2**63] # Test longs in Python 2 def testTensorIndexingTypeError(self): with self.session(use_gpu=True): @@ -650,6 +679,7 @@ class StridedSliceTest(test_util.TensorFlowTestCase): with self.assertRaisesRegexp(TypeError, expected): _ = checker[constant_op.constant(0.0)] + @test_util.run_deprecated_v1 def testExpand(self): with self.session(use_gpu=True): raw = [[[[[1, 2, 4, 5], [5, 6, 7, 8], [9, 10, 11, 12]]], @@ -667,6 +697,7 @@ class StridedSliceTest(test_util.TensorFlowTestCase): # Ellipsis in middle of two newaxis _ = checker[np.newaxis, ..., np.newaxis] + @test_util.run_deprecated_v1 def testExpandVariable(self): with self.session(use_gpu=True): x = variables.Variable(7, dtype=dtypes.int32) @@ -675,6 +706,7 @@ class StridedSliceTest(test_util.TensorFlowTestCase): self.assertEqual(y.shape, (1,)) self.assertAllEqual(y, (7,)) + @test_util.run_deprecated_v1 def testOptimizedCases(self): with self.session(use_gpu=True): checker = StridedSliceChecker(self, @@ -704,6 +736,7 @@ class StridedSliceShapeChecker(object): class StridedSliceShapeTest(test_util.TensorFlowTestCase): """Test the shape inference of StridedSliceShapes.""" + @test_util.run_deprecated_v1 def testUnknown(self): with self.session(use_gpu=True): uncertain_tensor = array_ops.placeholder(dtypes.float32) @@ -715,6 +748,7 @@ class StridedSliceShapeTest(test_util.TensorFlowTestCase): self.assertTrue(x is not None and y is not None or x is None and y is None) self.assertEqual(x.as_list(), y.as_list()) + @test_util.run_deprecated_v1 def testTensorShapeUncertain(self): with self.session(use_gpu=True): uncertain_tensor = array_ops.placeholder( @@ -738,6 +772,7 @@ class StridedSliceShapeTest(test_util.TensorFlowTestCase): self.tensorShapeEqual(a[::-1, :, array_ops.newaxis, ::-2], tensor_shape.TensorShape([5, None, 1, 4])) + @test_util.run_deprecated_v1 def testTensorValuedIndexShape(self): with self.session(use_gpu=True): defined_shape_tensor = array_ops.placeholder( @@ -794,6 +829,7 @@ class GradSliceChecker(object): class StridedSliceGradTest(test_util.TensorFlowTestCase): """Test that strided slice's custom gradient produces correct gradients.""" + @test_util.run_deprecated_v1 def testGradient(self): with self.session(use_gpu=True) as sess: var = variables.Variable( @@ -823,18 +859,20 @@ class StridedSliceGradTest(test_util.TensorFlowTestCase): grad = GradSliceChecker(self, sess, var, np.array(8)) _ = grad[tuple()] + @test_util.run_deprecated_v1 def testInt64Indices(self): with self.session(use_gpu=True) as sess: a = math_ops.range(3, dtype=dtypes.float32) index = constant_op.constant(1, dtype=dtypes.int64) b = 2. * a[index] grad, = gradients_impl.gradients(b, a) - self.assertAllEqual(sess.run(grad), [0., 2., 0.]) + self.assertAllEqual(self.evaluate(grad), [0., 2., 0.]) class StridedSliceGradTypeTest(test_util.TensorFlowTestCase): """Test varied index types and host located memory.""" + @test_util.run_deprecated_v1 def testHostVsDevice(self): with self.session(use_gpu=True) as sess: var2 = variables.Variable( @@ -842,20 +880,21 @@ class StridedSliceGradTypeTest(test_util.TensorFlowTestCase): math_ops.cast(math_ops.range(1, 5, 1), dtypes.float32), shape=(4, 1, 1))) varshape = variables.Variable([6, 4, 4], dtype=dtypes.int32) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) begin = constant_op.constant([0, 0, 0]) end = constant_op.constant([4, 1, 1]) strides = constant_op.constant([1, 1, 1]) foo = array_ops.strided_slice_grad(varshape, begin, end, strides, var2) sess.run(foo) + @test_util.run_deprecated_v1 def testInt64Shape(self): with self.session(use_gpu=True) as sess: original_dy = array_ops.reshape( math_ops.cast(math_ops.range(1, 5, 1), dtypes.float32), shape=(4, 1, 1)) original_shape = constant_op.constant([6, 4, 4], dtype=dtypes.int64) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) begin = constant_op.constant([0, 0, 0], dtype=dtypes.int64) end = constant_op.constant([4, 1, 1], dtype=dtypes.int64) strides = constant_op.constant([1, 1, 1], dtype=dtypes.int64) @@ -863,13 +902,14 @@ class StridedSliceGradTypeTest(test_util.TensorFlowTestCase): original_dy) sess.run(dx) + @test_util.run_deprecated_v1 def testMixedIndexTypes(self): with self.session(use_gpu=True) as sess: original_dy = array_ops.reshape( math_ops.cast(math_ops.range(1, 5, 1), dtypes.float32), shape=(4, 1, 1)) original_shape = constant_op.constant([6, 4, 4], dtype=dtypes.int64) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) begin = constant_op.constant([0, 0, 0], dtype=dtypes.int32) end = constant_op.constant([4, 1, 1], dtype=dtypes.int64) strides = constant_op.constant([1, 1, 1], dtype=dtypes.int64) @@ -971,6 +1011,7 @@ class StridedSliceAssignChecker(object): class SliceAssignTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testInvalidSlice(self): with self.cached_session() as sess: foo = constant_op.constant([1, 2, 3]) @@ -1008,12 +1049,15 @@ class SliceAssignTest(test_util.TensorFlowTestCase): checker2[...] = 6 # ellipsis checker2[None] = [6] # new axis + @test_util.run_deprecated_v1 def testSliceAssign(self): self.doTestSliceAssign(use_resource=False) + @test_util.run_deprecated_v1 def testSliceAssignResource(self): self.doTestSliceAssign(use_resource=True) + @test_util.run_deprecated_v1 def testUninitialized(self): with self.assertRaisesRegexp( errors.FailedPreconditionError, @@ -1032,13 +1076,14 @@ class SliceAssignTest(test_util.TensorFlowTestCase): with self.assertRaises(TypeError): v[:].assign(too_large_val) + @test_util.run_deprecated_v1 def testTypeErrorResource(self): init_val = constant_op.constant([1, 2], dtype=dtypes.int32) too_small_val = constant_op.constant([3, 4], dtype=dtypes.int8) too_large_val = constant_op.constant([3, 4], dtype=dtypes.int64) v = resource_variable_ops.ResourceVariable(init_val) with self.cached_session() as sess: - sess.run(v.initializer) + self.evaluate(v.initializer) with self.assertRaises(ValueError): sess.run(v[:].assign(too_large_val)) with self.assertRaises(ValueError): @@ -1088,6 +1133,7 @@ class SequenceMaskTest(test_util.TensorFlowTestCase): with self.assertRaisesRegexp(ValueError, "maxlen must be scalar"): array_ops.sequence_mask([10, 20], [10, 20]) + @test_util.run_deprecated_v1 def testOneDimensionalWithMaxlen(self): with self.cached_session(): res = array_ops.sequence_mask(constant_op.constant([1, 3, 2]), 5) @@ -1097,7 +1143,7 @@ class SequenceMaskTest(test_util.TensorFlowTestCase): [[True, False, False, False, False], [True, True, True, False, False], [True, True, False, False, False]]) - @test_util.enable_c_shapes + @test_util.run_deprecated_v1 def testOneDimensionalDtypeWithoutMaxlen(self): with self.cached_session(): # test dtype and default maxlen: @@ -1108,7 +1154,7 @@ class SequenceMaskTest(test_util.TensorFlowTestCase): res.eval(), [[0.0, 0.0, 0.0, 0.0], [1.0, 0.0, 0.0, 0.0], [1.0, 1.0, 1.0, 1.0]]) - @test_util.enable_c_shapes + @test_util.run_deprecated_v1 def testOneDimensionalWithoutMaxlen(self): with self.cached_session(): res = array_ops.sequence_mask( @@ -1120,7 +1166,6 @@ class SequenceMaskTest(test_util.TensorFlowTestCase): [True, False, False, False], [True, True, True, True]]) - @test_util.enable_c_shapes def testTwoDimensional(self): with self.cached_session(): res = array_ops.sequence_mask(constant_op.constant([[1, 3, 2]]), 5) @@ -1138,11 +1183,13 @@ class SequenceMaskTest(test_util.TensorFlowTestCase): [[[0.0, 0.0, 0.0, 0.0], [1.0, 0.0, 0.0, 0.0], [1.0, 1.0, 1.0, 1.0]], [[1.0, 0.0, 0.0, 0.0], [1.0, 1.0, 0.0, 0.0], [1.0, 1.0, 1.0, 0.0]]]) + @test_util.run_deprecated_v1 def testUnknownShape(self): lengths = array_ops.placeholder(dtype=dtypes.int32) res = array_ops.sequence_mask(lengths) self.assertEqual(res.shape, None) + @test_util.run_deprecated_v1 def testDtypes(self): def check_dtypes(lengths_dtype, maxlen_dtype): @@ -1165,6 +1212,7 @@ class SequenceMaskTest(test_util.TensorFlowTestCase): class ConcatSliceResourceTest(test_util.TensorFlowTestCase): @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def testConcatSlice(self): r1 = test_ops.stub_resource_handle_op(container="a", shared_name="b") r2 = test_ops.stub_resource_handle_op(container="a", shared_name="c") @@ -1187,18 +1235,18 @@ class IdentityTest(test_util.TensorFlowTestCase): self.assertAllEqual(x.numpy(), y.numpy()) self.assertTrue(device in y.device.lower()) - with ops.device("gpu:0"): + with test_util.force_gpu(): a = constant_op.constant([[2], [3]], dtype=dtypes.float32) - with ops.device("gpu:0"): + with test_util.force_gpu(): b = array_ops.identity(a) _test(a, b, "gpu") - with ops.device("cpu:0"): + with test_util.force_cpu(): c = array_ops.identity(b) _test(b, c, "cpu") - with ops.device("cpu:0"): + with test_util.force_cpu(): d = array_ops.identity(c) _test(c, d, "cpu") - with ops.device("gpu:0"): + with test_util.force_gpu(): e = array_ops.identity(d) _test(d, e, "gpu") @@ -1220,6 +1268,7 @@ class PadTest(test_util.TensorFlowTestCase): class InvertPermutationTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testInvertPermutation(self): for dtype in [dtypes.int32, dtypes.int64]: with self.cached_session(use_gpu=True): @@ -1254,12 +1303,14 @@ class UnravelIndexTest(test_util.TensorFlowTestCase): class GuaranteeConstOpTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testSimple(self): with self.cached_session(): a = array_ops.constant(10) guarantee_a = array_ops.guarantee_const(a) self.assertEqual(10, guarantee_a.eval()) + @test_util.run_deprecated_v1 def testVariables(self): with self.cached_session() as sess: for use_resource in [False, True]: @@ -1268,9 +1319,10 @@ class GuaranteeConstOpTest(test_util.TensorFlowTestCase): initializer=init_ops.constant_initializer(10.0), use_resource=use_resource) guarantee_a = array_ops.guarantee_const(a) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) self.assertEqual(10.0, guarantee_a.eval()) + @test_util.run_deprecated_v1 def testResourceRejection(self): with self.cached_session() as sess: a = variable_scope.get_variable( @@ -1278,7 +1330,7 @@ class GuaranteeConstOpTest(test_util.TensorFlowTestCase): initializer=init_ops.constant_initializer(10.0), use_resource=True) guarantee_a = array_ops.guarantee_const(a.handle) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) with self.assertRaisesWithPredicateMatch(errors.InvalidArgumentError, "cannot be a resource variable"): guarantee_a.eval() @@ -1286,6 +1338,7 @@ class GuaranteeConstOpTest(test_util.TensorFlowTestCase): class SnapshotOpTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testInvertPermutation(self): for dtype in [dtypes.int32, dtypes.int64, dtypes.float32, dtypes.float64]: with self.cached_session(use_gpu=True): diff --git a/tensorflow/python/kernel_tests/as_string_op_test.py b/tensorflow/python/kernel_tests/as_string_op_test.py index dd4a90e5f6..287701a73e 100644 --- a/tensorflow/python/kernel_tests/as_string_op_test.py +++ b/tensorflow/python/kernel_tests/as_string_op_test.py @@ -20,6 +20,7 @@ from __future__ import print_function import numpy as np from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import string_ops from tensorflow.python.platform import test @@ -27,6 +28,7 @@ from tensorflow.python.platform import test class AsStringOpTest(test.TestCase): + @test_util.run_deprecated_v1 def testFloat(self): float_inputs_ = [ 0, 1, -1, 0.5, 0.25, 0.125, float("INF"), float("NAN"), float("-INF") @@ -78,6 +80,7 @@ class AsStringOpTest(test.TestCase): output = string_ops.as_string(input_, fill="ab") output.eval(feed_dict={input_: float_inputs_}) + @test_util.run_deprecated_v1 def testInt(self): # Cannot use values outside -128..127 for test, because we're also # testing int8 @@ -112,6 +115,7 @@ class AsStringOpTest(test.TestCase): output = string_ops.as_string(input_, precision=0) output.eval(feed_dict={input_: int_inputs_}) + @test_util.run_deprecated_v1 def testLargeInt(self): # Cannot use values outside -128..127 for test, because we're also # testing int8 @@ -130,6 +134,7 @@ class AsStringOpTest(test.TestCase): result = output.eval(feed_dict={input_: int_inputs_}) self.assertAllEqual(s(result), ["%d" % x for x in int_inputs_]) + @test_util.run_deprecated_v1 def testHalfInt(self): s = lambda strs: [x.decode("ascii") for x in strs] @@ -140,6 +145,7 @@ class AsStringOpTest(test.TestCase): result = output.eval(feed_dict={input_: int_inputs_}) self.assertAllEqual(s(result), ["%d" % x for x in int_inputs_]) + @test_util.run_deprecated_v1 def testBool(self): bool_inputs_ = [False, True] s = lambda strs: [x.decode("ascii") for x in strs] @@ -152,6 +158,7 @@ class AsStringOpTest(test.TestCase): result = output.eval(feed_dict={input_: bool_inputs_}) self.assertAllEqual(s(result), ["false", "true"]) + @test_util.run_deprecated_v1 def testComplex(self): float_inputs_ = [ 0, 1, -1, 0.5, 0.25, 0.125, complex("INF"), complex("NAN"), diff --git a/tensorflow/python/kernel_tests/atrous_conv2d_test.py b/tensorflow/python/kernel_tests/atrous_conv2d_test.py index 1d82b3d058..a13e325835 100644 --- a/tensorflow/python/kernel_tests/atrous_conv2d_test.py +++ b/tensorflow/python/kernel_tests/atrous_conv2d_test.py @@ -22,6 +22,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import nn_impl @@ -58,6 +59,7 @@ def _upsample_filters(filters, rate): class AtrousConv2DTest(test.TestCase): + @test_util.run_deprecated_v1 def testAtrousConv2DForward(self): with self.session(use_gpu=True): # Input: [batch, height, width, input_depth] @@ -79,8 +81,10 @@ class AtrousConv2DTest(test.TestCase): y1 = nn_ops.atrous_conv2d(x, f, rate, padding=padding) y2 = nn_ops.conv2d( x, f_up, strides=[1, 1, 1, 1], padding=padding) - self.assertAllClose(y1.eval(), y2.eval(), rtol=1e-3, atol=1e-3) + self.assertAllClose( + y1.eval(), self.evaluate(y2), rtol=1e-3, atol=1e-3) + @test_util.run_deprecated_v1 def testAtrousSequence(self): """Tests optimization of sequence of atrous convolutions. @@ -131,8 +135,10 @@ class AtrousConv2DTest(test.TestCase): y2 = nn_ops.conv2d(y2, f, strides=[1, 1, 1, 1], padding=padding) y2 = nn_ops.conv2d(y2, f, strides=[1, 1, 1, 1], padding=padding) y2 = array_ops.batch_to_space(y2, crops=pad, block_size=rate) - self.assertAllClose(y1.eval(), y2.eval(), rtol=1e-2, atol=1e-2) + self.assertAllClose( + y1.eval(), self.evaluate(y2), rtol=1e-2, atol=1e-2) + @test_util.run_deprecated_v1 def testGradient(self): with self.session(use_gpu=True): # Input: [batch, height, width, input_depth] @@ -160,6 +166,7 @@ class AtrousConv2DTest(test.TestCase): class AtrousConv2DTransposeTest(test.TestCase): + @test_util.run_deprecated_v1 def testAtrousConv2DTransposeForward(self): with self.session(use_gpu=True): # Input: [batch, height, width, input_depth] @@ -193,11 +200,13 @@ class AtrousConv2DTransposeTest(test.TestCase): padding) y2 = nn_ops.conv2d_transpose( x, f_up, y_shape, strides=[1, 1, 1, 1], padding=padding) - self.assertAllClose(y1.eval(), y2.eval(), rtol=1e-3, atol=1e-3) + self.assertAllClose( + y1.eval(), self.evaluate(y2), rtol=1e-3, atol=1e-3) class AtrousDepthwiseConv2DTest(test.TestCase): + @test_util.run_deprecated_v1 def testAtrousDepthwiseConv2DForward(self): strides = [1, 1, 1, 1] with self.session(use_gpu=True): @@ -220,7 +229,8 @@ class AtrousDepthwiseConv2DTest(test.TestCase): y1 = nn_impl.depthwise_conv2d( x, f, strides, padding, rate=[rate, rate]) y2 = nn_impl.depthwise_conv2d(x, f_up, strides, padding) - self.assertAllClose(y1.eval(), y2.eval(), rtol=1e-3, atol=1e-3) + self.assertAllClose( + y1.eval(), self.evaluate(y2), rtol=1e-3, atol=1e-3) if __name__ == "__main__": diff --git a/tensorflow/python/kernel_tests/attention_ops_test.py b/tensorflow/python/kernel_tests/attention_ops_test.py index 1e09ba5b65..00dba9996d 100644 --- a/tensorflow/python/kernel_tests/attention_ops_test.py +++ b/tensorflow/python/kernel_tests/attention_ops_test.py @@ -85,7 +85,7 @@ class ExtractGlimpseTest(test.TestCase): # Evaluate the TensorFlow Graph. with self.cached_session() as sess: - value_rows, value_cols = sess.run([glimpse_rows, glimpse_cols]) + value_rows, value_cols = self.evaluate([glimpse_rows, glimpse_cols]) # Check dimensions of returned glimpse. self.assertEqual(value_rows.shape[1], glimpse_sizes[0]) @@ -121,8 +121,7 @@ class ExtractGlimpseTest(test.TestCase): with self.cached_session(): result = image_ops.extract_glimpse(empty_image, [1, 1], offsets) self.assertAllEqual( - np.zeros( - (0, 1, 1, 0), dtype=np.float32), result.eval()) + np.zeros((0, 1, 1, 0), dtype=np.float32), self.evaluate(result)) def testLargeCenterGlimpse(self): self._VerifyValues( diff --git a/tensorflow/python/kernel_tests/barrier_ops_test.py b/tensorflow/python/kernel_tests/barrier_ops_test.py index 4d36b3a465..60fe6f0eec 100644 --- a/tensorflow/python/kernel_tests/barrier_ops_test.py +++ b/tensorflow/python/kernel_tests/barrier_ops_test.py @@ -25,6 +25,7 @@ import numpy as np from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors_impl from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import data_flow_ops from tensorflow.python.platform import test @@ -66,6 +67,7 @@ class BarrierTest(test.TestCase): attr { key: 'shared_name' value: { s: 'B' } } """, b.barrier_ref.op.node_def) + @test_util.run_deprecated_v1 def testInsertMany(self): with self.cached_session(): b = data_flow_ops.Barrier( @@ -90,6 +92,7 @@ class BarrierTest(test.TestCase): data_flow_ops.Barrier( (dtypes.float32, dtypes.float32), shapes=((1,), (0,)), name="B") + @test_util.run_deprecated_v1 def testInsertManyEmptyTensorUnknown(self): with self.cached_session(): b = data_flow_ops.Barrier((dtypes.float32, dtypes.float32), name="B") @@ -102,6 +105,7 @@ class BarrierTest(test.TestCase): ".*Tensors with no elements are not supported.*"): insert_0_op.run() + @test_util.run_deprecated_v1 def testTakeMany(self): with self.cached_session() as sess: b = data_flow_ops.Barrier( @@ -127,6 +131,7 @@ class BarrierTest(test.TestCase): self.assertEqual(values_0_val[idx], v0) self.assertEqual(values_1_val[idx], v1) + @test_util.run_deprecated_v1 def testTakeManySmallBatch(self): with self.cached_session() as sess: b = data_flow_ops.Barrier( @@ -191,6 +196,7 @@ class BarrierTest(test.TestCase): with self.assertRaisesOpError("is closed"): insert_1_3_op.run() + @test_util.run_deprecated_v1 def testUseBarrierWithShape(self): with self.cached_session() as sess: b = data_flow_ops.Barrier( @@ -220,6 +226,7 @@ class BarrierTest(test.TestCase): self.assertAllEqual(values_0_val[idx], v0) self.assertAllEqual(values_1_val[idx], v1) + @test_util.run_deprecated_v1 def testParallelInsertMany(self): with self.cached_session() as sess: b = data_flow_ops.Barrier(dtypes.float32, shapes=()) @@ -229,7 +236,7 @@ class BarrierTest(test.TestCase): insert_ops = [b.insert_many(0, [k], [v]) for k, v in zip(keys, values)] take_t = b.take_many(10) - sess.run(insert_ops) + self.evaluate(insert_ops) self.assertEquals(size_t.eval(), [10]) indices_val, keys_val, values_val = sess.run( @@ -240,6 +247,7 @@ class BarrierTest(test.TestCase): idx = keys_val.tolist().index(k) self.assertEqual(values_val[idx], v) + @test_util.run_deprecated_v1 def testParallelTakeMany(self): with self.cached_session() as sess: b = data_flow_ops.Barrier(dtypes.float32, shapes=()) @@ -274,6 +282,7 @@ class BarrierTest(test.TestCase): self.assertItemsEqual( zip(keys, values), [(k[0], v[0]) for k, v in zip(key_vals, value_vals)]) + @test_util.run_deprecated_v1 def testBlockingTakeMany(self): with self.cached_session() as sess: b = data_flow_ops.Barrier(dtypes.float32, shapes=()) @@ -296,6 +305,7 @@ class BarrierTest(test.TestCase): insert_op.run() t.join() + @test_util.run_deprecated_v1 def testParallelInsertManyTakeMany(self): with self.cached_session() as sess: b = data_flow_ops.Barrier( @@ -375,6 +385,7 @@ class BarrierTest(test.TestCase): 2 + outer_indices_from_keys + inner_indices_from_keys)).T self.assertAllEqual(taken_i["values_1"], expected_values_1) + @test_util.run_deprecated_v1 def testClose(self): with self.cached_session() as sess: b = data_flow_ops.Barrier( @@ -433,6 +444,7 @@ class BarrierTest(test.TestCase): with self.assertRaisesOpError("is closed and has insufficient elements"): sess.run(take_t[0]) + @test_util.run_deprecated_v1 def testCancel(self): with self.cached_session() as sess: b = data_flow_ops.Barrier( @@ -491,10 +503,11 @@ class BarrierTest(test.TestCase): b = data_flow_ops.Barrier( (dtypes.float32, dtypes.float32), shapes=((), ()), name="B") take_t = b.take_many(1, allow_small_batch=True) - sess.run(b.close(cancel)) + self.evaluate(b.close(cancel)) with self.assertRaisesOpError("is closed and has insufficient elements"): - sess.run(take_t) + self.evaluate(take_t) + @test_util.run_deprecated_v1 def testClosedEmptyBarrierTakeManyAllowSmallBatchRaises(self): self._testClosedEmptyBarrierTakeManyAllowSmallBatchRaises(cancel=False) self._testClosedEmptyBarrierTakeManyAllowSmallBatchRaises(cancel=True) @@ -569,9 +582,11 @@ class BarrierTest(test.TestCase): sorted(taken), [0] * (num_iterations // 2) + [10] * (num_iterations // 2)) + @test_util.run_deprecated_v1 def testParallelInsertManyTakeManyCloseHalfwayThrough(self): self._testParallelInsertManyTakeManyCloseHalfwayThrough(cancel=False) + @test_util.run_deprecated_v1 def testParallelInsertManyTakeManyCancelHalfwayThrough(self): self._testParallelInsertManyTakeManyCloseHalfwayThrough(cancel=True) @@ -669,12 +684,15 @@ class BarrierTest(test.TestCase): else: self.assertEqual(taken, [10] * num_iterations) + @test_util.run_deprecated_v1 def testParallelPartialInsertManyTakeManyCloseHalfwayThrough(self): self._testParallelPartialInsertManyTakeManyCloseHalfwayThrough(cancel=False) + @test_util.run_deprecated_v1 def testParallelPartialInsertManyTakeManyCancelHalfwayThrough(self): self._testParallelPartialInsertManyTakeManyCloseHalfwayThrough(cancel=True) + @test_util.run_deprecated_v1 def testIncompatibleSharedBarrierErrors(self): with self.cached_session(): # Do component types and shapes. diff --git a/tensorflow/python/kernel_tests/base64_ops_test.py b/tensorflow/python/kernel_tests/base64_ops_test.py index 1b399942ef..bb903d827f 100644 --- a/tensorflow/python/kernel_tests/base64_ops_test.py +++ b/tensorflow/python/kernel_tests/base64_ops_test.py @@ -93,7 +93,7 @@ class Base64OpsTest(test_util.TensorFlowTestCase): decoded = string_ops.decode_base64(encoded) with self.cached_session() as sess: - encoded_value, decoded_value = sess.run([encoded, decoded]) + encoded_value, decoded_value = self.evaluate([encoded, decoded]) self.assertEqual(encoded_value.shape, msg.shape) self.assertEqual(decoded_value.shape, msg.shape) diff --git a/tensorflow/python/kernel_tests/basic_gpu_test.py b/tensorflow/python/kernel_tests/basic_gpu_test.py index 225c1b35ae..1a8513d022 100644 --- a/tensorflow/python/kernel_tests/basic_gpu_test.py +++ b/tensorflow/python/kernel_tests/basic_gpu_test.py @@ -44,13 +44,13 @@ class GPUBinaryOpsTest(test.TestCase): inx = ops.convert_to_tensor(x) iny = ops.convert_to_tensor(y) out = tf_func(inx, iny) - tf_gpu = sess.run(out) + tf_gpu = self.evaluate(out) with self.cached_session(use_gpu=False) as sess: inx = ops.convert_to_tensor(x) iny = ops.convert_to_tensor(y) out = tf_func(inx, iny) - tf_cpu = sess.run(out) + tf_cpu = self.evaluate(out) self.assertAllClose(tf_cpu, tf_gpu) @@ -96,7 +96,7 @@ class MathBuiltinUnaryTest(test.TestCase): with self.cached_session(use_gpu=use_gpu) as sess: inx = ops.convert_to_tensor(x) ofunc = tf_func(inx) - tf_out = sess.run(ofunc) + tf_out = self.evaluate(ofunc) self.assertAllClose(np_out, tf_out) def _inv(self, x): @@ -148,7 +148,7 @@ class MathBuiltinUnaryTest(test.TestCase): iny = ops.convert_to_tensor(y + 0.1) ofunc = inx / iny out_func2 = math_ops.floor(ofunc) - tf_out = sess.run(out_func2) + tf_out = self.evaluate(out_func2) self.assertAllClose(np_out, tf_out) @@ -159,6 +159,7 @@ class BroadcastSimpleTest(test.TestCase): with self.cached_session(use_gpu=True) as sess: return sess.run(broadcast_gradient_args(xs, ys)) + @test_util.run_deprecated_v1 def testBroadcast(self): r0, r1 = self._GetGradientArgs([2, 3, 5], [1]) self.assertAllEqual(r0, []) @@ -214,11 +215,12 @@ class BroadcastSimpleTest(test.TestCase): inx = ops.convert_to_tensor(x) iny = ops.convert_to_tensor(y) out = tf_func(inx, iny) - tf_gpu = out.eval() + tf_gpu = self.evaluate(out) self.assertAllClose(np_ans, tf_gpu) self.assertShapeEqual(np_ans, out) # TODO(zhifengc/ke): make gradient checker work on GPU. + @test_util.run_deprecated_v1 def testGradient(self): x = (1 + np.linspace(0, 5, np.prod([1, 3, 2]))).astype(np.float32).reshape( [1, 3, 2]) @@ -255,6 +257,7 @@ class GpuMultiSessionMemoryTest(test_util.TensorFlowTestCase): if len(results) != 1: break + @test_util.run_deprecated_v1 def testConcurrentSessions(self): n_threads = 4 threads = [] diff --git a/tensorflow/python/kernel_tests/batch_gather_op_test.py b/tensorflow/python/kernel_tests/batch_gather_op_test.py index 547506d844..7e0b3e1b5e 100644 --- a/tensorflow/python/kernel_tests/batch_gather_op_test.py +++ b/tensorflow/python/kernel_tests/batch_gather_op_test.py @@ -23,6 +23,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.platform import test @@ -52,7 +53,7 @@ class GatherTest(test.TestCase, parameterized.TestCase): gather_t = array_ops.batch_gather(params, indices_tf) expected_result = np.array([3, 7]) np_val = self._buildParams(expected_result, dtype) - gather_val = gather_t.eval() + gather_val = self.evaluate(gather_t) self.assertAllEqual(np_val, gather_val) self.assertEqual(np_val.shape, gather_t.get_shape()) @@ -68,7 +69,7 @@ class GatherTest(test.TestCase, parameterized.TestCase): gather_t = array_ops.batch_gather(params, indices_tf) expected_result = np.array([[3], [15]]) np_val = self._buildParams(expected_result, dtype) - gather_val = gather_t.eval() + gather_val = self.evaluate(gather_t) self.assertAllEqual(np_val, gather_val) self.assertEqual(np_val.shape, gather_t.get_shape()) @@ -81,12 +82,13 @@ class GatherTest(test.TestCase, parameterized.TestCase): params = constant_op.constant(params_np) indices_tf = constant_op.constant(indices) gather_t = array_ops.batch_gather(params, indices_tf) - gather_val = gather_t.eval() + gather_val = self.evaluate(gather_t) expected_result = np.array([[[2, 0], [7, 5]], [[10, 8], [11, 15]]]) np_val = self._buildParams(expected_result, dtype) self.assertAllEqual(np_val, gather_val) self.assertEqual(np_val.shape, gather_t.get_shape()) + @test_util.run_deprecated_v1 def testString(self): params = np.array([[b"asdf", b"zxcv"], [b"qwer", b"uiop"]]) with self.cached_session(): @@ -94,6 +96,7 @@ class GatherTest(test.TestCase, parameterized.TestCase): self.assertAllEqual([[b"qwer", b"uiop"]], array_ops.batch_gather(params, indices_tf).eval()) + @test_util.run_deprecated_v1 def testUnknownIndices(self): params = constant_op.constant([[0, 1, 2]]) indices = array_ops.placeholder(dtypes.int32, shape=[None, None]) @@ -106,6 +109,7 @@ class GatherTest(test.TestCase, parameterized.TestCase): with self.assertRaisesOpError(r"indices\[0\] = 7 is not in \[0, 2\)"): array_ops.batch_gather(params, [7]).eval() + @test_util.run_deprecated_v1 def testEmptySlices(self): with self.session(use_gpu=True): for dtype in _TEST_TYPES: diff --git a/tensorflow/python/kernel_tests/batch_matmul_op_test.py b/tensorflow/python/kernel_tests/batch_matmul_op_test.py index 8f6c089b42..a0ad8151b2 100644 --- a/tensorflow/python/kernel_tests/batch_matmul_op_test.py +++ b/tensorflow/python/kernel_tests/batch_matmul_op_test.py @@ -86,7 +86,7 @@ class BatchMatmulOpTest(test.TestCase): with self.cached_session(use_gpu=is_floating) as sess: if static_shape: z0 = math_ops.matmul(x, y, adjoint_a=adjoint_a, adjoint_b=adjoint_b) - z0_val = z0.eval() + z0_val = self.evaluate(z0) else: x_ph = array_ops.placeholder(x.dtype) y_ph = array_ops.placeholder(y.dtype) diff --git a/tensorflow/python/kernel_tests/batch_scatter_ops_test.py b/tensorflow/python/kernel_tests/batch_scatter_ops_test.py index 742a204883..eefcdc508f 100644 --- a/tensorflow/python/kernel_tests/batch_scatter_ops_test.py +++ b/tensorflow/python/kernel_tests/batch_scatter_ops_test.py @@ -22,6 +22,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import state_ops from tensorflow.python.ops import variables from tensorflow.python.platform import test @@ -73,6 +74,7 @@ class ScatterTest(test.TestCase): tf_scatter(ref, indices, updates).eval() self.assertAllClose(ref.eval(), new) + @test_util.run_deprecated_v1 def testVariableRankUpdate(self): vtypes = [np.float32, np.float64] for vtype in vtypes: @@ -80,6 +82,7 @@ class ScatterTest(test.TestCase): self._VariableRankTest( state_ops.batch_scatter_update, vtype, itype) + @test_util.run_deprecated_v1 def testBooleanScatterUpdate(self): with self.session(use_gpu=False) as session: var = variables.Variable([True, False]) @@ -91,8 +94,9 @@ class ScatterTest(test.TestCase): session.run([update0, update1]) - self.assertAllEqual([False, True], var.eval()) + self.assertAllEqual([False, True], self.evaluate(var)) + @test_util.run_deprecated_v1 def testScatterOutOfRange(self): params = np.array([1, 2, 3, 4, 5, 6]).astype(np.float32) updates = np.array([-3, -4, -5]).astype(np.float32) diff --git a/tensorflow/python/kernel_tests/batchtospace_op_test.py b/tensorflow/python/kernel_tests/batchtospace_op_test.py index 03f3f64353..c422df8806 100644 --- a/tensorflow/python/kernel_tests/batchtospace_op_test.py +++ b/tensorflow/python/kernel_tests/batchtospace_op_test.py @@ -27,6 +27,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gen_array_ops from tensorflow.python.ops import gradient_checker @@ -50,6 +51,7 @@ class CppOpImpl(object): class BatchToSpaceDepthToSpace(test.TestCase, PythonOpImpl): # Verifies that: batch_to_space(x) = transpose(depth_to_space(transpose(x))) + @test_util.run_deprecated_v1 def testDepthToSpaceTranspose(self): x = np.arange(20 * 5 * 8 * 7, dtype=np.float32).reshape([20, 5, 8, 7]) block_size = 2 @@ -70,6 +72,7 @@ class BatchToSpaceDepthToSpaceCpp(BatchToSpaceDepthToSpace, CppOpImpl): class BatchToSpaceErrorHandlingTest(test.TestCase, PythonOpImpl): + @test_util.run_deprecated_v1 def testInputWrongDimMissingBatch(self): # The input is missing the first dimension ("batch") x_np = [[[1], [2]], [[3], [4]]] @@ -78,6 +81,7 @@ class BatchToSpaceErrorHandlingTest(test.TestCase, PythonOpImpl): with self.assertRaises(ValueError): _ = self.batch_to_space(x_np, crops, block_size) + @test_util.run_deprecated_v1 def testBlockSize0(self): # The block size is 0. x_np = [[[[1], [2]], [[3], [4]]]] @@ -87,6 +91,7 @@ class BatchToSpaceErrorHandlingTest(test.TestCase, PythonOpImpl): out_tf = self.batch_to_space(x_np, crops, block_size) out_tf.eval() + @test_util.run_deprecated_v1 def testBlockSizeOne(self): # The block size is 1. The block size needs to be > 1. x_np = [[[[1], [2]], [[3], [4]]]] @@ -96,6 +101,7 @@ class BatchToSpaceErrorHandlingTest(test.TestCase, PythonOpImpl): out_tf = self.batch_to_space(x_np, crops, block_size) out_tf.eval() + @test_util.run_deprecated_v1 def testBlockSizeLarger(self): # The block size is too large for this input. x_np = [[[[1], [2]], [[3], [4]]]] @@ -105,6 +111,7 @@ class BatchToSpaceErrorHandlingTest(test.TestCase, PythonOpImpl): out_tf = self.batch_to_space(x_np, crops, block_size) out_tf.eval() + @test_util.run_deprecated_v1 def testBlockSizeSquaredNotDivisibleBatch(self): # The block size squared does not divide the batch. x_np = [[[[1], [2], [3]], [[3], [4], [7]]]] @@ -113,6 +120,7 @@ class BatchToSpaceErrorHandlingTest(test.TestCase, PythonOpImpl): with self.assertRaises(ValueError): _ = self.batch_to_space(x_np, crops, block_size) + @test_util.run_deprecated_v1 def testUnknownShape(self): t = self.batch_to_space( array_ops.placeholder(dtypes.float32), @@ -160,28 +168,35 @@ class BatchToSpaceNDErrorHandlingTest(test.TestCase): self._testStaticShape(input_shape, block_shape, paddings, error) self._testDynamicShape(input_shape, block_shape, paddings) + @test_util.run_deprecated_v1 def testInputWrongDimMissingBatch(self): self._testShape([2, 2], [2, 2], [[0, 0], [0, 0]], ValueError) self._testShape([2, 2, 3], [2, 2, 3], [[0, 0], [0, 0]], ValueError) + @test_util.run_deprecated_v1 def testBlockSize0(self): # The block size is 0. self._testShape([1, 2, 2, 1], [0, 1], [[0, 0], [0, 0]], ValueError) + @test_util.run_deprecated_v1 def testBlockSizeNegative(self): self._testShape([1, 2, 2, 1], [-1, 1], [[0, 0], [0, 0]], ValueError) + @test_util.run_deprecated_v1 def testNegativePadding(self): self._testShape([1, 2, 2], [1, 1], [[0, -1], [0, 0]], ValueError) + @test_util.run_deprecated_v1 def testCropTooLarge(self): # The amount to crop exceeds the padded size. self._testShape([1 * 2 * 2, 2, 3, 1], [2, 2], [[3, 2], [0, 0]], ValueError) + @test_util.run_deprecated_v1 def testBlockSizeSquaredNotDivisibleBatch(self): # The batch dimension is not divisible by the product of the block_shape. self._testShape([3, 1, 1, 1], [2, 3], [[0, 0], [0, 0]], ValueError) + @test_util.run_deprecated_v1 def testUnknownShape(self): # Verify that input shape and paddings shape can be unknown. _ = array_ops.batch_to_space_nd( @@ -263,18 +278,21 @@ class BatchToSpaceGradientTest(test.TestCase, PythonOpImpl): # Don't use very large numbers as dimensions here as the result is tensor # with cartesian product of the dimensions. + @test_util.run_deprecated_v1 def testSmall(self): block_size = 2 crop_beg = 0 crop_end = 0 self._compare(1, 2, 3, 5, block_size, crop_beg, crop_end) + @test_util.run_deprecated_v1 def testSmall2(self): block_size = 2 crop_beg = 0 crop_end = 0 self._compare(2, 4, 3, 2, block_size, crop_beg, crop_end) + @test_util.run_deprecated_v1 def testSmallCrop1x1(self): block_size = 2 crop_beg = 1 @@ -316,14 +334,17 @@ class BatchToSpaceNDGradientTest(test.TestCase): # Don't use very large numbers as dimensions here as the result is tensor # with cartesian product of the dimensions. + @test_util.run_deprecated_v1 def testSmall(self): for dtype in [dtypes.int64, dtypes.int32]: self._compare([1, 2, 3, 5], [2, 2], [[0, 0], [0, 0]], dtype) + @test_util.run_deprecated_v1 def testSmall2(self): for dtype in [dtypes.int64, dtypes.int32]: self._compare([2, 4, 3, 2], [2, 2], [[0, 0], [0, 0]], dtype) + @test_util.run_deprecated_v1 def testSmallCrop1x1(self): for dtype in [dtypes.int64, dtypes.int32]: self._compare([1, 2, 3, 5], [2, 2], [[1, 1], [1, 1]], dtype) diff --git a/tensorflow/python/kernel_tests/bcast_ops_test.py b/tensorflow/python/kernel_tests/bcast_ops_test.py index 3ec820aead..ae00955ac2 100644 --- a/tensorflow/python/kernel_tests/bcast_ops_test.py +++ b/tensorflow/python/kernel_tests/bcast_ops_test.py @@ -20,6 +20,7 @@ from __future__ import print_function from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops.gen_array_ops import broadcast_args from tensorflow.python.ops.gen_array_ops import broadcast_gradient_args from tensorflow.python.platform import test @@ -35,6 +36,7 @@ class BcastOpsTest(test.TestCase): with self.cached_session() as sess: return sess.run(broadcast_gradient_args(xs, ys)) + @test_util.run_deprecated_v1 def testBasic(self): r = self._GetBroadcastShape([2, 3, 5], [1]) self.assertAllEqual(r, [2, 3, 5]) @@ -66,6 +68,7 @@ class BcastOpsTest(test.TestCase): r = self._GetBroadcastShape([3, 1], [2, 1, 5]) self.assertAllEqual(r, [2, 3, 5]) + @test_util.run_deprecated_v1 def testBasicGradient(self): r0, r1 = self._GetGradientArgs([2, 3, 5], [1]) self.assertAllEqual(r0, []) @@ -107,6 +110,7 @@ class BcastOpsTest(test.TestCase): self.assertAllEqual(r0, [0, 2]) self.assertAllEqual(r1, [1]) + @test_util.run_deprecated_v1 def testZeroDims(self): r = self._GetBroadcastShape([2, 0, 3, 0, 5], [3, 0, 5]) self.assertAllEqual(r, [2, 0, 3, 0, 5]) @@ -120,6 +124,7 @@ class BcastOpsTest(test.TestCase): r = self._GetBroadcastShape([3, 1, 5], [2, 0, 3, 0, 5]) self.assertAllEqual(r, [2, 0, 3, 0, 5]) + @test_util.run_deprecated_v1 def testZeroDimsGradient(self): r0, r1 = self._GetGradientArgs([2, 0, 3, 0, 5], [3, 0, 5]) self.assertAllEqual(r0, []) @@ -137,6 +142,7 @@ class BcastOpsTest(test.TestCase): self.assertAllEqual(r0, [0, 1, 3]) self.assertAllEqual(r1, []) + @test_util.run_deprecated_v1 def testDataTypes(self): for dtype in [dtypes.int32, dtypes.int64]: r = self._GetBroadcastShape( diff --git a/tensorflow/python/kernel_tests/benchmark_test.py b/tensorflow/python/kernel_tests/benchmark_test.py index 5777a5d097..bffa5e6e8f 100644 --- a/tensorflow/python/kernel_tests/benchmark_test.py +++ b/tensorflow/python/kernel_tests/benchmark_test.py @@ -21,9 +21,12 @@ import json import os import random +import numpy as np + from tensorflow.core.util import test_log_pb2 from tensorflow.python.client import session -from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes +from tensorflow.python.ops import array_ops from tensorflow.python.platform import benchmark from tensorflow.python.platform import gfile from tensorflow.python.platform import test @@ -64,11 +67,17 @@ class TestReportingBenchmark(test.Benchmark): "other_key": "string"}) def benchmark_times_an_op(self): + input_size = 5 with session.Session(config=benchmark.benchmark_config()) as sess: - a = constant_op.constant(0.0) + a = array_ops.placeholder(dtype=dtypes.float32, shape=(input_size)) a_plus_a = a + a return self.run_op_benchmark( - sess, a_plus_a, min_iters=1000, store_trace=True, name="op_benchmark") + sess, + a_plus_a, + feed_dict={a: np.arange(input_size)}, + min_iters=1000, + store_trace=True, + name="op_benchmark") class BenchmarkTest(test.TestCase): diff --git a/tensorflow/python/kernel_tests/betainc_op_test.py b/tensorflow/python/kernel_tests/betainc_op_test.py index 92d21462d5..9dc34a6062 100644 --- a/tensorflow/python/kernel_tests/betainc_op_test.py +++ b/tensorflow/python/kernel_tests/betainc_op_test.py @@ -24,6 +24,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import gradients_impl @@ -48,7 +49,7 @@ class BetaincTest(test.TestCase): tf_x_s = constant_op.constant(x_s, dtype=dtype) tf_out_t = math_ops.betainc(tf_a_s, tf_b_s, tf_x_s) with self.cached_session(): - tf_out = tf_out_t.eval() + tf_out = self.evaluate(tf_out_t) scipy_out = special.betainc(a_s, b_s, x_s).astype(np_dt) # the scipy version of betainc uses a double-only implementation. @@ -109,36 +110,42 @@ class BetaincTest(test.TestCase): except ImportError as e: tf_logging.warn("Cannot test special functions: %s" % str(e)) + @test_util.run_deprecated_v1 def testBetaIncFloat(self): a_s = np.abs(np.random.randn(10, 10) * 30) # in (0, infty) b_s = np.abs(np.random.randn(10, 10) * 30) # in (0, infty) x_s = np.random.rand(10, 10) # in (0, 1) self._testBetaInc(a_s, b_s, x_s, dtypes.float32) + @test_util.run_deprecated_v1 def testBetaIncDouble(self): a_s = np.abs(np.random.randn(10, 10) * 30) # in (0, infty) b_s = np.abs(np.random.randn(10, 10) * 30) # in (0, infty) x_s = np.random.rand(10, 10) # in (0, 1) self._testBetaInc(a_s, b_s, x_s, dtypes.float64) + @test_util.run_deprecated_v1 def testBetaIncDoubleVeryLargeValues(self): a_s = np.abs(np.random.randn(10, 10) * 1e15) # in (0, infty) b_s = np.abs(np.random.randn(10, 10) * 1e15) # in (0, infty) x_s = np.random.rand(10, 10) # in (0, 1) self._testBetaInc(a_s, b_s, x_s, dtypes.float64) + @test_util.run_deprecated_v1 def testBetaIncDoubleVerySmallValues(self): a_s = np.abs(np.random.randn(10, 10) * 1e-16) # in (0, infty) b_s = np.abs(np.random.randn(10, 10) * 1e-16) # in (0, infty) x_s = np.random.rand(10, 10) # in (0, 1) self._testBetaInc(a_s, b_s, x_s, dtypes.float64) + @test_util.run_deprecated_v1 def testBetaIncFloatVerySmallValues(self): a_s = np.abs(np.random.randn(10, 10) * 1e-8) # in (0, infty) b_s = np.abs(np.random.randn(10, 10) * 1e-8) # in (0, infty) x_s = np.random.rand(10, 10) # in (0, 1) self._testBetaInc(a_s, b_s, x_s, dtypes.float32) + @test_util.run_deprecated_v1 def testBetaIncFpropAndBpropAreNeverNAN(self): with self.cached_session() as sess: space = np.logspace(-8, 5).tolist() @@ -159,6 +166,7 @@ class BetaincTest(test.TestCase): self.assertAllEqual(np.zeros_like(grads_x).astype(np.bool), np.isnan(grads_x)) + @test_util.run_deprecated_v1 def testBetaIncGrads(self): err_tolerance = 1e-3 with self.cached_session(): diff --git a/tensorflow/python/kernel_tests/bias_op_test.py b/tensorflow/python/kernel_tests/bias_op_test.py index 749d6a791e..66f442dbdd 100644 --- a/tensorflow/python/kernel_tests/bias_op_test.py +++ b/tensorflow/python/kernel_tests/bias_op_test.py @@ -22,6 +22,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import gradients_impl @@ -89,10 +90,12 @@ class BiasAddTest(test.TestCase): self._testBiasNCHW(np_inputs, np_bias, use_gpu=True) + @test_util.run_deprecated_v1 def testInputDims(self): with self.assertRaises(ValueError): nn_ops.bias_add([1, 2], [1]) + @test_util.run_deprecated_v1 def testBiasVec(self): with self.assertRaises(ValueError): nn_ops.bias_add( @@ -101,6 +104,7 @@ class BiasAddTest(test.TestCase): array_ops.reshape( [1, 2], shape=[1, 2])) + @test_util.run_deprecated_v1 def testBiasInputsMatch(self): with self.assertRaises(ValueError): nn_ops.bias_add( @@ -109,23 +113,27 @@ class BiasAddTest(test.TestCase): array_ops.reshape( [1], shape=[1])) + @test_util.run_deprecated_v1 def testIntTypes(self): for t in [np.int8, np.int16, np.int32, np.int64]: self._testAll( np.array([[10, 20, 30], [40, 50, 60]]).astype(t), np.array([1, 2, 3]).astype(t)) + @test_util.run_deprecated_v1 def testFloatTypes(self): for t in [np.float16, np.float32, np.float64]: self._testAll( np.random.rand(4, 3, 3).astype(t), np.random.rand(3).astype(t)) + @test_util.run_deprecated_v1 def test4DFloatTypes(self): for t in [np.float16, np.float32, np.float64]: self._testAll( np.random.rand(4, 3, 2, 3).astype(t), np.random.rand(3).astype(t)) + @test_util.run_deprecated_v1 def test5DFloatTypes(self): for t in [np.float16, np.float32, np.float64]: self._testAll( @@ -187,6 +195,7 @@ class BiasAddTest(test.TestCase): self.assertAllClose(bias_jacob_t, bias_jacob_n, threshold, threshold) self.assertAllClose(grad_jacob_t, grad_jacob_n, threshold, threshold) + @test_util.run_deprecated_v1 def testGradientTensor(self): # TODO(yongtang): BiasAddGrad with NCHW only works 4D. Reenable once # all dimensions are supported. @@ -198,6 +207,7 @@ class BiasAddTest(test.TestCase): bias = np.array([1.3, 2.4], dtype=dtype.as_numpy_dtype) self._testGradient(np_input, bias, dtype, data_format, use_gpu) + @test_util.run_deprecated_v1 def testGradientTensor4D(self): # BiasAddGrad with NCHW support 4D so all are enabled. for (data_format, use_gpu) in [("NHWC", False), ("NHWC", True), @@ -209,11 +219,13 @@ class BiasAddTest(test.TestCase): bias = np.array([1.3, 2.4], dtype=dtype.as_numpy_dtype) self._testGradient(np_input, bias, dtype, data_format, use_gpu) + @test_util.run_deprecated_v1 def testEmpty(self): np.random.seed(7) for shape in (0, 0), (2, 0), (0, 2), (4, 3, 0), (4, 0, 3), (0, 4, 3): self._testAll(np.random.randn(*shape), np.random.randn(shape[-1])) + @test_util.run_deprecated_v1 def testEmptyGradient(self): # TODO(yongtang): BiasAddGrad with NCHW only works 4D. Reenable once # all dimensions are supported. diff --git a/tensorflow/python/kernel_tests/bincount_op_test.py b/tensorflow/python/kernel_tests/bincount_op_test.py index 49eb835847..d064d736cf 100644 --- a/tensorflow/python/kernel_tests/bincount_op_test.py +++ b/tensorflow/python/kernel_tests/bincount_op_test.py @@ -30,6 +30,7 @@ from tensorflow.python.platform import googletest class BincountTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def test_empty(self): with self.session(use_gpu=True): self.assertAllEqual( @@ -43,6 +44,7 @@ class BincountTest(test_util.TensorFlowTestCase): math_ops.bincount([], minlength=3, dtype=np.float64).eval().dtype, np.float64) + @test_util.run_deprecated_v1 def test_values(self): with self.session(use_gpu=True): self.assertAllEqual( @@ -58,12 +60,14 @@ class BincountTest(test_util.TensorFlowTestCase): self.assertAllEqual( math_ops.bincount(np.arange(10000)).eval(), np.ones(10000)) + @test_util.run_deprecated_v1 def test_maxlength(self): with self.session(use_gpu=True): self.assertAllEqual(math_ops.bincount([5], maxlength=3).eval(), [0, 0, 0]) self.assertAllEqual(math_ops.bincount([1], maxlength=3).eval(), [0, 1]) self.assertAllEqual(math_ops.bincount([], maxlength=3).eval(), []) + @test_util.run_deprecated_v1 def test_random_with_weights(self): num_samples = 10000 with self.session(use_gpu=True): @@ -77,6 +81,7 @@ class BincountTest(test_util.TensorFlowTestCase): self.assertAllClose( math_ops.bincount(arr, weights).eval(), np.bincount(arr, weights)) + @test_util.run_deprecated_v1 def test_random_without_weights(self): num_samples = 10000 with self.session(use_gpu=True): @@ -87,6 +92,7 @@ class BincountTest(test_util.TensorFlowTestCase): self.assertAllClose( math_ops.bincount(arr, None).eval(), np.bincount(arr, weights)) + @test_util.run_deprecated_v1 def test_zero_weights(self): with self.session(use_gpu=True): self.assertAllEqual( @@ -99,6 +105,7 @@ class BincountTest(test_util.TensorFlowTestCase): with self.assertRaises(errors.InvalidArgumentError): math_ops.bincount([1, 2, 3, -1, 6, 8]).eval() + @test_util.run_deprecated_v1 def test_shape_function(self): # size must be scalar. with self.assertRaisesRegexp( diff --git a/tensorflow/python/kernel_tests/bitcast_op_test.py b/tensorflow/python/kernel_tests/bitcast_op_test.py index 79e0f36d24..b4f9a21a89 100644 --- a/tensorflow/python/kernel_tests/bitcast_op_test.py +++ b/tensorflow/python/kernel_tests/bitcast_op_test.py @@ -21,6 +21,7 @@ from __future__ import print_function import numpy as np from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.platform import test @@ -28,9 +29,9 @@ from tensorflow.python.platform import test class BitcastTest(test.TestCase): def _testBitcast(self, x, datatype, shape): - with self.session(use_gpu=True): + with test_util.use_gpu(): tf_ans = array_ops.bitcast(x, datatype) - out = tf_ans.eval() + out = self.evaluate(tf_ans) buff_after = memoryview(out).tobytes() buff_before = memoryview(x).tobytes() self.assertEqual(buff_before, buff_after) @@ -59,6 +60,7 @@ class BitcastTest(test.TestCase): shape = [3, 4] self._testBitcast(x, dtypes.int64, shape) + @test_util.run_deprecated_v1 def testErrors(self): x = np.zeros([1, 1], np.int8) datatype = dtypes.int32 @@ -71,6 +73,7 @@ class BitcastTest(test.TestCase): shape = [4] self._testBitcast(x, datatype, shape) + @test_util.run_deprecated_v1 def testUnknown(self): x = array_ops.placeholder(dtypes.float32) datatype = dtypes.int8 diff --git a/tensorflow/python/kernel_tests/boosted_trees/prediction_ops_test.py b/tensorflow/python/kernel_tests/boosted_trees/prediction_ops_test.py index 7cdc67f83f..6b04e8abf4 100644 --- a/tensorflow/python/kernel_tests/boosted_trees/prediction_ops_test.py +++ b/tensorflow/python/kernel_tests/boosted_trees/prediction_ops_test.py @@ -28,6 +28,7 @@ from tensorflow.python.platform import googletest class TrainingPredictionOpsTest(test_util.TensorFlowTestCase): """Tests prediction ops for training.""" + @test_util.run_deprecated_v1 def testCachedPredictionOnEmptyEnsemble(self): """Tests that prediction on a dummy ensemble does not fail.""" with self.cached_session() as session: @@ -61,6 +62,7 @@ class TrainingPredictionOpsTest(test_util.TensorFlowTestCase): self.assertAllClose(cached_node_ids, new_node_ids) self.assertAllClose([[0], [0]], logits_updates) + @test_util.run_deprecated_v1 def testNoCachedPredictionButTreeExists(self): """Tests that predictions are updated once trees are added.""" with self.cached_session() as session: @@ -127,6 +129,7 @@ class TrainingPredictionOpsTest(test_util.TensorFlowTestCase): self.assertAllClose([2, 1], new_node_ids) self.assertAllClose([[0.1 * 8.79], [0.1 * 1.14]], logits_updates) + @test_util.run_deprecated_v1 def testCachedPredictionIsCurrent(self): """Tests that prediction based on previous node in the tree works.""" with self.cached_session() as session: @@ -199,6 +202,7 @@ class TrainingPredictionOpsTest(test_util.TensorFlowTestCase): self.assertAllClose(cached_node_ids, new_node_ids) self.assertAllClose([[0], [0]], logits_updates) + @test_util.run_deprecated_v1 def testCachedPredictionFromTheSameTree(self): """Tests that prediction based on previous node in the tree works.""" with self.cached_session() as session: @@ -313,6 +317,7 @@ class TrainingPredictionOpsTest(test_util.TensorFlowTestCase): # 1.65 and -3.875, and then multiply them by 0.1 (lr) self.assertAllClose([[0.1 * 1.65], [0.1 * -3.875]], logits_updates) + @test_util.run_deprecated_v1 def testCachedPredictionFromPreviousTree(self): """Tests the predictions work when we have cache from previous trees.""" with self.cached_session() as session: @@ -445,6 +450,7 @@ class TrainingPredictionOpsTest(test_util.TensorFlowTestCase): # change= 0.1(1.14+7.0-7.0) self.assertAllClose([[1], [0.114]], logits_updates) + @test_util.run_deprecated_v1 def testCategoricalSplits(self): """Tests the training prediction work for categorical splits.""" with self.cached_session() as session: @@ -517,6 +523,7 @@ class TrainingPredictionOpsTest(test_util.TensorFlowTestCase): self.assertAllClose([3, 4, 2], new_node_ids) self.assertAllClose([[5.], [6.], [7.]], logits_updates) + @test_util.run_deprecated_v1 def testCachedPredictionFromTheSameTreeWithPostPrunedNodes(self): """Tests that prediction based on previous node in the tree works.""" with self.cached_session() as session: @@ -647,6 +654,7 @@ class TrainingPredictionOpsTest(test_util.TensorFlowTestCase): self.assertAllClose([[0.01], [0.01], [0.0553], [0.0783], [0.01], [0.01]], logits_updates + cached_values) + @test_util.run_deprecated_v1 def testCachedPredictionFromThePreviousTreeWithPostPrunedNodes(self): """Tests that prediction based on previous node in the tree works.""" with self.cached_session() as session: @@ -792,6 +800,7 @@ class TrainingPredictionOpsTest(test_util.TensorFlowTestCase): [root + 0.0783], [root + 0.01], [root + 0.01]], logits_updates + cached_values) + @test_util.run_deprecated_v1 def testCachedPredictionTheWholeTreeWasPruned(self): """Tests that prediction based on previous node in the tree works.""" with self.cached_session() as session: @@ -864,6 +873,7 @@ class TrainingPredictionOpsTest(test_util.TensorFlowTestCase): class PredictionOpsTest(test_util.TensorFlowTestCase): """Tests prediction ops for inference.""" + @test_util.run_deprecated_v1 def testPredictionOnEmptyEnsemble(self): """Tests that prediction on a empty ensemble does not fail.""" with self.cached_session() as session: @@ -886,6 +896,7 @@ class PredictionOpsTest(test_util.TensorFlowTestCase): logits = session.run(predict_op) self.assertAllClose(expected_logits, logits) + @test_util.run_deprecated_v1 def testPredictionMultipleTree(self): """Tests the predictions work when we have multiple trees.""" with self.cached_session() as session: @@ -996,6 +1007,7 @@ class PredictionOpsTest(test_util.TensorFlowTestCase): logits = session.run(predict_op) self.assertAllClose(expected_logits, logits) + @test_util.run_deprecated_v1 def testCategoricalSplits(self): """Tests the predictions work for categorical splits.""" with self.cached_session() as session: @@ -1062,6 +1074,7 @@ class PredictionOpsTest(test_util.TensorFlowTestCase): class FeatureContribsOpsTest(test_util.TensorFlowTestCase): """Tests feature contribs ops for model understanding.""" + @test_util.run_deprecated_v1 def testContribsForOnlyABiasNode(self): """Tests case when, after training, only left with a bias node. @@ -1122,6 +1135,7 @@ class FeatureContribsOpsTest(test_util.TensorFlowTestCase): self.assertAllClose(feature_ids, expected_feature_ids) self.assertAllClose(logits_paths, expected_logits_paths) + @test_util.run_deprecated_v1 def testContribsMultipleTreeWhenFirstTreeIsABiasNode(self): """Tests case when, after training, first tree contains only a bias node.""" with self.cached_session() as session: @@ -1219,6 +1233,7 @@ class FeatureContribsOpsTest(test_util.TensorFlowTestCase): self.assertAllClose(feature_ids, expected_feature_ids) self.assertAllClose(logits_paths, expected_logits_paths) + @test_util.run_deprecated_v1 def testContribsMultipleTree(self): """Tests that the contribs work when we have multiple trees.""" with self.cached_session() as session: diff --git a/tensorflow/python/kernel_tests/boosted_trees/quantile_ops_test.py b/tensorflow/python/kernel_tests/boosted_trees/quantile_ops_test.py index 12afb6a2ad..390672febe 100644 --- a/tensorflow/python/kernel_tests/boosted_trees/quantile_ops_test.py +++ b/tensorflow/python/kernel_tests/boosted_trees/quantile_ops_test.py @@ -82,6 +82,7 @@ class QuantileOpsTest(test_util.TensorFlowTestCase): self.max_elements = 1 << 16 self.num_quantiles = constant_op.constant(3, dtype=dtypes.int64) + @test_util.run_deprecated_v1 def testBasicQuantileBucketsSingleResource(self): with self.cached_session() as sess: quantile_accumulator_handle = self.create_resource("floats", self.eps, @@ -98,14 +99,15 @@ class QuantileOpsTest(test_util.TensorFlowTestCase): quantile_accumulator_handle, num_features=2) quantiles = boosted_trees_ops.boosted_trees_bucketize( [self._feature_0, self._feature_1], buckets) - sess.run(summary_op) - sess.run(flush_op) + self.evaluate(summary_op) + self.evaluate(flush_op) self.assertAllClose(self._feature_0_boundaries, buckets[0].eval()) self.assertAllClose(self._feature_1_boundaries, buckets[1].eval()) self.assertAllClose(self._feature_0_quantiles, quantiles[0].eval()) self.assertAllClose(self._feature_1_quantiles, quantiles[1].eval()) + @test_util.run_deprecated_v1 def testBasicQuantileBucketsMultipleResources(self): with self.cached_session() as sess: quantile_accumulator_handle_0 = self.create_resource("float_0", self.eps, @@ -132,14 +134,15 @@ class QuantileOpsTest(test_util.TensorFlowTestCase): quantile_accumulator_handle_1, num_features=1) quantiles = boosted_trees_ops.boosted_trees_bucketize( [self._feature_0, self._feature_1], bucket_0 + bucket_1) - sess.run([summary_op_0, summary_op_1]) - sess.run([flush_op_0, flush_op_1]) + self.evaluate([summary_op_0, summary_op_1]) + self.evaluate([flush_op_0, flush_op_1]) self.assertAllClose(self._feature_0_boundaries, bucket_0[0].eval()) self.assertAllClose(self._feature_1_boundaries, bucket_1[0].eval()) self.assertAllClose(self._feature_0_quantiles, quantiles[0].eval()) self.assertAllClose(self._feature_1_quantiles, quantiles[1].eval()) + @test_util.run_deprecated_v1 def testSaveRestoreAfterFlush(self): save_dir = os.path.join(self.get_temp_dir(), "save_restore") save_path = os.path.join(tempfile.mkdtemp(prefix=save_dir), "hash") @@ -158,7 +161,7 @@ class QuantileOpsTest(test_util.TensorFlowTestCase): self._example_weights) with ops.control_dependencies([summaries]): flush = accumulator.flush() - sess.run(flush) + self.evaluate(flush) self.assertAllClose(self._feature_0_boundaries, buckets[0].eval()) self.assertAllClose(self._feature_1_boundaries, buckets[1].eval()) save.save(sess, save_path) @@ -172,6 +175,7 @@ class QuantileOpsTest(test_util.TensorFlowTestCase): self.assertAllClose(self._feature_0_boundaries, buckets[0].eval()) self.assertAllClose(self._feature_1_boundaries, buckets[1].eval()) + @test_util.run_deprecated_v1 def testSaveRestoreBeforeFlush(self): save_dir = os.path.join(self.get_temp_dir(), "save_restore") save_path = os.path.join(tempfile.mkdtemp(prefix=save_dir), "hash") @@ -185,12 +189,12 @@ class QuantileOpsTest(test_util.TensorFlowTestCase): summaries = accumulator.add_summaries([self._feature_0, self._feature_1], self._example_weights) - sess.run(summaries) + self.evaluate(summaries) buckets = accumulator.get_bucket_boundaries() self.assertAllClose([], buckets[0].eval()) self.assertAllClose([], buckets[1].eval()) save.save(sess, save_path) - sess.run(accumulator.flush()) + self.evaluate(accumulator.flush()) self.assertAllClose(self._feature_0_boundaries, buckets[0].eval()) self.assertAllClose(self._feature_1_boundaries, buckets[1].eval()) diff --git a/tensorflow/python/kernel_tests/boosted_trees/resource_ops_test.py b/tensorflow/python/kernel_tests/boosted_trees/resource_ops_test.py index 65bb9ab55f..0a34277bbd 100644 --- a/tensorflow/python/kernel_tests/boosted_trees/resource_ops_test.py +++ b/tensorflow/python/kernel_tests/boosted_trees/resource_ops_test.py @@ -30,19 +30,21 @@ from tensorflow.python.platform import googletest class ResourceOpsTest(test_util.TensorFlowTestCase): """Tests resource_ops.""" + @test_util.run_deprecated_v1 def testCreate(self): with self.cached_session(): ensemble = boosted_trees_ops.TreeEnsemble('ensemble') resources.initialize_resources(resources.shared_resources()).run() stamp_token = ensemble.get_stamp_token() - self.assertEqual(0, stamp_token.eval()) + self.assertEqual(0, self.evaluate(stamp_token)) (_, num_trees, num_finalized_trees, num_attempted_layers, nodes_range) = ensemble.get_states() - self.assertEqual(0, num_trees.eval()) - self.assertEqual(0, num_finalized_trees.eval()) - self.assertEqual(0, num_attempted_layers.eval()) - self.assertAllEqual([0, 1], nodes_range.eval()) + self.assertEqual(0, self.evaluate(num_trees)) + self.assertEqual(0, self.evaluate(num_finalized_trees)) + self.assertEqual(0, self.evaluate(num_attempted_layers)) + self.assertAllEqual([0, 1], self.evaluate(nodes_range)) + @test_util.run_deprecated_v1 def testCreateWithProto(self): with self.cached_session(): ensemble_proto = boosted_trees_pb2.TreeEnsemble() @@ -154,12 +156,13 @@ class ResourceOpsTest(test_util.TensorFlowTestCase): resources.initialize_resources(resources.shared_resources()).run() (stamp_token, num_trees, num_finalized_trees, num_attempted_layers, nodes_range) = ensemble.get_states() - self.assertEqual(7, stamp_token.eval()) - self.assertEqual(2, num_trees.eval()) - self.assertEqual(1, num_finalized_trees.eval()) - self.assertEqual(6, num_attempted_layers.eval()) - self.assertAllEqual([16, 19], nodes_range.eval()) + self.assertEqual(7, self.evaluate(stamp_token)) + self.assertEqual(2, self.evaluate(num_trees)) + self.assertEqual(1, self.evaluate(num_finalized_trees)) + self.assertEqual(6, self.evaluate(num_attempted_layers)) + self.assertAllEqual([16, 19], self.evaluate(nodes_range)) + @test_util.run_deprecated_v1 def testSerializeDeserialize(self): with self.cached_session(): # Initialize. @@ -167,11 +170,11 @@ class ResourceOpsTest(test_util.TensorFlowTestCase): resources.initialize_resources(resources.shared_resources()).run() (stamp_token, num_trees, num_finalized_trees, num_attempted_layers, nodes_range) = ensemble.get_states() - self.assertEqual(5, stamp_token.eval()) - self.assertEqual(0, num_trees.eval()) - self.assertEqual(0, num_finalized_trees.eval()) - self.assertEqual(0, num_attempted_layers.eval()) - self.assertAllEqual([0, 1], nodes_range.eval()) + self.assertEqual(5, self.evaluate(stamp_token)) + self.assertEqual(0, self.evaluate(num_trees)) + self.assertEqual(0, self.evaluate(num_finalized_trees)) + self.assertEqual(0, self.evaluate(num_attempted_layers)) + self.assertAllEqual([0, 1], self.evaluate(nodes_range)) # Deserialize. ensemble_proto = boosted_trees_pb2.TreeEnsemble() @@ -219,18 +222,18 @@ class ResourceOpsTest(test_util.TensorFlowTestCase): ]): (stamp_token, num_trees, num_finalized_trees, num_attempted_layers, nodes_range) = ensemble.get_states() - self.assertEqual(3, stamp_token.eval()) - self.assertEqual(1, num_trees.eval()) + self.assertEqual(3, self.evaluate(stamp_token)) + self.assertEqual(1, self.evaluate(num_trees)) # This reads from metadata, not really counting the layers. - self.assertEqual(5, num_attempted_layers.eval()) - self.assertEqual(0, num_finalized_trees.eval()) - self.assertAllEqual([3, 7], nodes_range.eval()) + self.assertEqual(5, self.evaluate(num_attempted_layers)) + self.assertEqual(0, self.evaluate(num_finalized_trees)) + self.assertAllEqual([3, 7], self.evaluate(nodes_range)) # Serialize. new_ensemble_proto = boosted_trees_pb2.TreeEnsemble() new_stamp_token, new_serialized = ensemble.serialize() - self.assertEqual(3, new_stamp_token.eval()) + self.assertEqual(3, self.evaluate(new_stamp_token)) new_ensemble_proto.ParseFromString(new_serialized.eval()) self.assertProtoEquals(ensemble_proto, new_ensemble_proto) diff --git a/tensorflow/python/kernel_tests/boosted_trees/stats_ops_test.py b/tensorflow/python/kernel_tests/boosted_trees/stats_ops_test.py index 09e9cfa3af..e2e23486b5 100644 --- a/tensorflow/python/kernel_tests/boosted_trees/stats_ops_test.py +++ b/tensorflow/python/kernel_tests/boosted_trees/stats_ops_test.py @@ -65,16 +65,16 @@ class StatsOpsTest(test_util.TensorFlowTestCase): min_node_weight=0, max_splits=max_splits) - self.assertAllEqual([[1, 2], [1, 2]], sess.run(node_ids_list)) + self.assertAllEqual([[1, 2], [1, 2]], self.evaluate(node_ids_list)) self.assertAllClose([[0.004775, 0.41184], [0.02823, 0.41184]], - sess.run(gains_list)) - self.assertAllEqual([[1, 1], [1, 1]], sess.run(thresholds_list)) + self.evaluate(gains_list)) + self.assertAllEqual([[1, 1], [1, 1]], self.evaluate(thresholds_list)) # The left node contrib will be later added to the previous node value to # make the left node value, and the same for right node contrib. self.assertAllClose([[[-.416667], [.568966]], [[-.6], [-.75]]], - sess.run(left_node_contribs_list)) + self.evaluate(left_node_contribs_list)) self.assertAllClose([[[-.592593], [-.75]], [[-.076923], [.568966]]], - sess.run(right_node_contribs_list)) + self.evaluate(right_node_contribs_list)) def testCalculateBestGainsWithL2(self): """Testing Gain calculation with L2.""" @@ -113,16 +113,16 @@ class StatsOpsTest(test_util.TensorFlowTestCase): min_node_weight=0, max_splits=max_splits) - self.assertAllEqual([[1, 2], [1, 2]], sess.run(node_ids_list)) + self.assertAllEqual([[1, 2], [1, 2]], self.evaluate(node_ids_list)) self.assertAllClose([[0., 0.33931375], [0.01879096, 0.33931375]], - sess.run(gains_list)) - self.assertAllEqual([[0, 1], [1, 1]], sess.run(thresholds_list)) + self.evaluate(gains_list)) + self.assertAllEqual([[0, 1], [1, 1]], self.evaluate(thresholds_list)) # The left node contrib will be later added to the previous node value to # make the left node value, and the same for right node contrib. self.assertAllClose([[[0.], [.485294]], [[-.5], [-.6]]], - sess.run(left_node_contribs_list)) + self.evaluate(left_node_contribs_list)) self.assertAllClose([[[-.424658], [-.6]], [[-.043478], [.485294]]], - sess.run(right_node_contribs_list)) + self.evaluate(right_node_contribs_list)) def testCalculateBestGainsWithL1(self): """Testing Gain calculation with L1.""" @@ -162,18 +162,18 @@ class StatsOpsTest(test_util.TensorFlowTestCase): min_node_weight=0, max_splits=max_splits) - self.assertAllEqual([[0, 1], [1, 1]], sess.run(thresholds_list)) + self.assertAllEqual([[0, 1], [1, 1]], self.evaluate(thresholds_list)) - self.assertAllEqual([[1, 2], [1, 2]], sess.run(node_ids_list)) + self.assertAllEqual([[1, 2], [1, 2]], self.evaluate(node_ids_list)) self.assertAllClose([[[0.0], [0.3965517]], [[-0.4], [-0.5]]], - sess.run(left_node_contribs_list)) + self.evaluate(left_node_contribs_list)) self.assertAllClose([[[-0.3333333], [-0.5]], [[0.0], [0.396552]]], - sess.run(right_node_contribs_list)) + self.evaluate(right_node_contribs_list)) # Gain should also include an adjustment of the gradient by l1. self.assertAllClose([[0.0, 0.191207], [0.01, 0.191207]], - sess.run(gains_list)) + self.evaluate(gains_list)) def testCalculateBestGainsWithTreeComplexity(self): """Testing Gain calculation with L2.""" @@ -214,18 +214,18 @@ class StatsOpsTest(test_util.TensorFlowTestCase): min_node_weight=0, max_splits=max_splits) - self.assertAllEqual([[1, 2], [1, 2]], sess.run(node_ids_list)) + self.assertAllEqual([[1, 2], [1, 2]], self.evaluate(node_ids_list)) self.assertAllClose([[-3., -2.66068625], [-2.98120904, -2.66068625]], - sess.run(gains_list)) + self.evaluate(gains_list)) - self.assertAllEqual([[0, 1], [1, 1]], sess.run(thresholds_list)) + self.assertAllEqual([[0, 1], [1, 1]], self.evaluate(thresholds_list)) # The left node contrib will be later added to the previous node value to # make the left node value, and the same for right node contrib. self.assertAllClose([[[0.], [.485294]], [[-.5], [-.6]]], - sess.run(left_node_contribs_list)) + self.evaluate(left_node_contribs_list)) self.assertAllClose([[[-.424658], [-.6]], [[-.043478], [.485294]]], - sess.run(right_node_contribs_list)) + self.evaluate(right_node_contribs_list)) def testCalculateBestGainsWithMinNodeWeight(self): """Testing Gain calculation without any regularization.""" @@ -266,13 +266,13 @@ class StatsOpsTest(test_util.TensorFlowTestCase): # We can't split node 1 on feature 1 and node 2 on feature 2 because of # the min node weight. - self.assertAllEqual([[2], [1]], sess.run(node_ids_list)) - self.assertAllClose([[0.384314], [0.098013]], sess.run(gains_list)) - self.assertAllEqual([[1], [1]], sess.run(thresholds_list)) + self.assertAllEqual([[2], [1]], self.evaluate(node_ids_list)) + self.assertAllClose([[0.384314], [0.098013]], self.evaluate(gains_list)) + self.assertAllEqual([[1], [1]], self.evaluate(thresholds_list)) self.assertAllClose([[[0.4852941]], [[-.6]]], - sess.run(left_node_contribs_list)) + self.evaluate(left_node_contribs_list)) self.assertAllClose([[[-0.75]], [[-0.014925]]], - sess.run(right_node_contribs_list)) + self.evaluate(right_node_contribs_list)) def testCalculateBestGainsWithMinNodeWeightNoSplitOnFeturePossible(self): """Testing Gain calculation without any regularization.""" @@ -311,9 +311,9 @@ class StatsOpsTest(test_util.TensorFlowTestCase): max_splits=max_splits) # We can't split either of the nodes on the first feature - self.assertEqual(2, len(sess.run(node_ids_list))) - self.assertAllEqual([], sess.run(node_ids_list)[0]) - self.assertAllEqual([1], sess.run(node_ids_list)[1]) + self.assertEqual(2, len(self.evaluate(node_ids_list))) + self.assertAllEqual([], self.evaluate(node_ids_list)[0]) + self.assertAllEqual([1], self.evaluate(node_ids_list)[1]) # Now check when we can't split on any feature (node_ids_list, _, _, _, @@ -325,8 +325,9 @@ class StatsOpsTest(test_util.TensorFlowTestCase): tree_complexity=0.0, min_node_weight=10, max_splits=max_splits) - self.assertAllEqual([[], []], sess.run(node_ids_list)) + self.assertAllEqual([[], []], self.evaluate(node_ids_list)) + @test_util.run_deprecated_v1 def testMakeStatsSummarySimple(self): """Simple test for MakeStatsSummary.""" with self.cached_session(): @@ -359,7 +360,7 @@ class StatsOpsTest(test_util.TensorFlowTestCase): [[0., 0.], [.15, .36], [.06, .07], [.1, .2]], # node 1 [[-.33, .58], [0., 0.], [.3, .4], [0., 0.]], # node 2 ]], - result.eval()) + self.evaluate(result)) def testMakeStatsSummaryMultipleFeatures(self): """Tests that MakeStatsSummary works for multiple features.""" @@ -389,7 +390,7 @@ class StatsOpsTest(test_util.TensorFlowTestCase): [[.3, .4], [0., 0.], [-.4, .5], [.07, .08]], # node 2 ], # feature 1 ], - result.eval()) + self.evaluate(result)) def _verify_precision(self, length): with self.cached_session(): @@ -408,7 +409,7 @@ class StatsOpsTest(test_util.TensorFlowTestCase): node_ids, gradients, hessians, [bucketized_features], max_splits, num_buckets) # shape=[max_splits, num_buckets, num_features, 2] - self.assertAllClose([[[[2., 0.2]]]], result.eval()) + self.assertAllClose([[[[2., 0.2]]]], self.evaluate(result)) def testMakeStatsSummaryNumericalPrecisionSmallBatch(self): """Tests numeric precision.""" diff --git a/tensorflow/python/kernel_tests/boosted_trees/training_ops_test.py b/tensorflow/python/kernel_tests/boosted_trees/training_ops_test.py index ea022820e4..afc0564fc5 100644 --- a/tensorflow/python/kernel_tests/boosted_trees/training_ops_test.py +++ b/tensorflow/python/kernel_tests/boosted_trees/training_ops_test.py @@ -30,6 +30,7 @@ from tensorflow.python.platform import googletest class UpdateTreeEnsembleOpTest(test_util.TensorFlowTestCase): """Tests for growing tree ensemble from split candidates.""" + @test_util.run_deprecated_v1 def testGrowWithEmptyEnsemble(self): """Test growing an empty ensemble.""" with self.cached_session() as session: @@ -139,6 +140,7 @@ class UpdateTreeEnsembleOpTest(test_util.TensorFlowTestCase): self.assertEqual(new_stamp, 1) self.assertProtoEquals(expected_result, tree_ensemble) + @test_util.run_deprecated_v1 def testBiasCenteringOnEmptyEnsemble(self): """Test growing with bias centering on an empty ensemble.""" with self.cached_session() as session: @@ -182,6 +184,7 @@ class UpdateTreeEnsembleOpTest(test_util.TensorFlowTestCase): self.assertEqual(new_stamp, 1) self.assertProtoEquals(expected_result, tree_ensemble) + @test_util.run_deprecated_v1 def testGrowExistingEnsembleTreeNotFinalized(self): """Test growing an existing ensemble with the last tree not finalized.""" with self.cached_session() as session: @@ -366,6 +369,7 @@ class UpdateTreeEnsembleOpTest(test_util.TensorFlowTestCase): self.assertEqual(new_stamp, 1) self.assertProtoEquals(expected_result, tree_ensemble) + @test_util.run_deprecated_v1 def testGrowExistingEnsembleTreeFinalized(self): """Test growing an existing ensemble with the last tree finalized.""" with self.cached_session() as session: @@ -515,6 +519,7 @@ class UpdateTreeEnsembleOpTest(test_util.TensorFlowTestCase): self.assertEqual(new_stamp, 1) self.assertProtoEquals(expected_result, tree_ensemble) + @test_util.run_deprecated_v1 def testPrePruning(self): """Test growing an existing ensemble with pre-pruning.""" with self.cached_session() as session: @@ -671,6 +676,7 @@ class UpdateTreeEnsembleOpTest(test_util.TensorFlowTestCase): self.assertEqual(new_stamp, 1) self.assertProtoEquals(expected_result, tree_ensemble) + @test_util.run_deprecated_v1 def testMetadataWhenCantSplitDueToEmptySplits(self): """Test that the metadata is updated even though we can't split.""" with self.cached_session() as session: @@ -782,6 +788,7 @@ class UpdateTreeEnsembleOpTest(test_util.TensorFlowTestCase): self.assertEqual(new_stamp, 1) self.assertProtoEquals(expected_result, tree_ensemble) + @test_util.run_deprecated_v1 def testMetadataWhenCantSplitDuePrePruning(self): """Test metadata is updated correctly when no split due to prepruning.""" with self.cached_session() as session: @@ -917,6 +924,7 @@ class UpdateTreeEnsembleOpTest(test_util.TensorFlowTestCase): self.assertEqual(new_stamp, 1) self.assertProtoEquals(expected_result, tree_ensemble) + @test_util.run_deprecated_v1 def testPostPruningOfSomeNodes(self): """Test growing an ensemble with post-pruning.""" with self.cached_session() as session: @@ -1251,6 +1259,7 @@ class UpdateTreeEnsembleOpTest(test_util.TensorFlowTestCase): self.assertEqual(new_stamp, 3) self.assertProtoEquals(expected_result, res_ensemble) + @test_util.run_deprecated_v1 def testPostPruningOfAllNodes(self): """Test growing an ensemble with post-pruning, with all nodes are pruned.""" with self.cached_session() as session: @@ -1434,6 +1443,7 @@ class UpdateTreeEnsembleOpTest(test_util.TensorFlowTestCase): } """, res_ensemble) + @test_util.run_deprecated_v1 def testPostPruningChangesNothing(self): """Test growing an ensemble with post-pruning with all gains >0.""" with self.cached_session() as session: diff --git a/tensorflow/python/kernel_tests/broadcast_to_ops_test.py b/tensorflow/python/kernel_tests/broadcast_to_ops_test.py index 233c166405..b9eb2391b4 100644 --- a/tensorflow/python/kernel_tests/broadcast_to_ops_test.py +++ b/tensorflow/python/kernel_tests/broadcast_to_ops_test.py @@ -29,6 +29,7 @@ from tensorflow.python.platform import test as test_lib class BroadcastToTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testBroadcastToBasic(self): for dtype in [np.uint8, np.uint16, np.int8, np.int16, np.int32, np.int64]: with self.session(use_gpu=True): @@ -37,6 +38,7 @@ class BroadcastToTest(test_util.TensorFlowTestCase): v_np = np.broadcast_to(x, [3, 3]) self.assertAllEqual(v_tf.eval(), v_np) + @test_util.run_deprecated_v1 def testBroadcastToString(self): with self.session(use_gpu=True): x = np.array([b"1", b"2", b"3"]) @@ -44,6 +46,7 @@ class BroadcastToTest(test_util.TensorFlowTestCase): v_np = np.broadcast_to(x, [3, 3]) self.assertAllEqual(v_tf.eval(), v_np) + @test_util.run_deprecated_v1 def testBroadcastToBool(self): with self.session(use_gpu=True): x = np.array([True, False, True], dtype=np.bool) @@ -51,6 +54,7 @@ class BroadcastToTest(test_util.TensorFlowTestCase): v_np = np.broadcast_to(x, [3, 3]) self.assertAllEqual(v_tf.eval(), v_np) + @test_util.run_deprecated_v1 def testBroadcastToShape(self): for input_dim in range(1, 6): for output_dim in range(input_dim, 6): @@ -62,6 +66,7 @@ class BroadcastToTest(test_util.TensorFlowTestCase): v_np = np.broadcast_to(x, output_shape) self.assertAllEqual(v_tf.eval(), v_np) + @test_util.run_deprecated_v1 def testBroadcastToScalar(self): with self.session(use_gpu=True): x = np.array(1, dtype=np.int32) @@ -69,6 +74,7 @@ class BroadcastToTest(test_util.TensorFlowTestCase): v_np = np.broadcast_to(x, [3, 3]) self.assertAllEqual(v_tf.eval(), v_np) + @test_util.run_deprecated_v1 def testBroadcastScalarToNonScalar(self): with self.session(use_gpu=True): x = np.array(1.0, dtype=np.float) @@ -76,6 +82,7 @@ class BroadcastToTest(test_util.TensorFlowTestCase): v_np = np.broadcast_to(x, [2, 3, 4]) self.assertAllEqual(v_tf.eval(), v_np) + @test_util.run_deprecated_v1 def testBroadcastToShapeTypeAndInference(self): for dtype in [dtypes.int32, dtypes.int64]: with self.cached_session(use_gpu=True): @@ -89,6 +96,7 @@ class BroadcastToTest(test_util.TensorFlowTestCase): # check shape inference when shape input is constant self.assertAllEqual(shape, v_np.shape) + @test_util.run_deprecated_v1 def testGradientForScalar(self): x = constant_op.constant(1, dtype=dtypes.float32) v = array_ops.broadcast_to(x, [2, 4, 3]) @@ -98,6 +106,7 @@ class BroadcastToTest(test_util.TensorFlowTestCase): out.get_shape()) self.assertLess(err, 1e-4) + @test_util.run_deprecated_v1 def testGradientWithSameRank(self): x = constant_op.constant(np.reshape(np.arange(6), (2, 1, 3)), dtype=dtypes.float32) @@ -108,6 +117,7 @@ class BroadcastToTest(test_util.TensorFlowTestCase): out, out.get_shape()) self.assertLess(err, 1e-4) + @test_util.run_deprecated_v1 def testGradientWithIncreasingRank(self): x = constant_op.constant([[1], [2]], dtype=dtypes.float32) @@ -118,6 +128,7 @@ class BroadcastToTest(test_util.TensorFlowTestCase): out, out.get_shape()) self.assertLess(err, 1e-4) + @test_util.run_deprecated_v1 def testGradientWithBroadcastAllDimensions(self): x = constant_op.constant([[1, 2, 3], [4, 5, 6]], dtype=dtypes.float32) v = array_ops.broadcast_to(x, [5, 4, 6]) diff --git a/tensorflow/python/kernel_tests/bucketize_op_test.py b/tensorflow/python/kernel_tests/bucketize_op_test.py index 57413e6af5..95df694370 100644 --- a/tensorflow/python/kernel_tests/bucketize_op_test.py +++ b/tensorflow/python/kernel_tests/bucketize_op_test.py @@ -20,6 +20,7 @@ from __future__ import print_function from tensorflow.python.framework import constant_op from tensorflow.python.framework import errors_impl +from tensorflow.python.framework import test_util from tensorflow.python.ops import math_ops from tensorflow.python.platform import test @@ -32,7 +33,7 @@ class BucketizationOpTest(test.TestCase): boundaries=[0, 3, 8, 11]) expected_out = [0, 1, 1, 2, 2, 3, 3, 4, 4] with self.session(use_gpu=True) as sess: - self.assertAllEqual(expected_out, sess.run(op)) + self.assertAllEqual(expected_out, self.evaluate(op)) def testFloat(self): op = math_ops._bucketize( @@ -40,7 +41,7 @@ class BucketizationOpTest(test.TestCase): boundaries=[0., 3., 8., 11.]) expected_out = [0, 1, 1, 2, 2, 3, 3, 4, 4] with self.session(use_gpu=True) as sess: - self.assertAllEqual(expected_out, sess.run(op)) + self.assertAllEqual(expected_out, self.evaluate(op)) def test2DInput(self): op = math_ops._bucketize( @@ -48,15 +49,16 @@ class BucketizationOpTest(test.TestCase): boundaries=[0, 3, 8, 11]) expected_out = [[0, 1, 1, 2, 2], [3, 3, 4, 4, 1]] with self.session(use_gpu=True) as sess: - self.assertAllEqual(expected_out, sess.run(op)) + self.assertAllEqual(expected_out, self.evaluate(op)) + @test_util.run_deprecated_v1 def testInvalidBoundariesOrder(self): op = math_ops._bucketize( constant_op.constant([-5, 0]), boundaries=[0, 8, 3, 11]) with self.session(use_gpu=True) as sess: with self.assertRaisesRegexp( errors_impl.InvalidArgumentError, "Expected sorted boundaries"): - sess.run(op) + self.evaluate(op) def testBoundariesNotList(self): with self.assertRaisesRegexp( diff --git a/tensorflow/python/kernel_tests/candidate_sampler_ops_test.py b/tensorflow/python/kernel_tests/candidate_sampler_ops_test.py index b19077db56..fa6eb5c968 100644 --- a/tensorflow/python/kernel_tests/candidate_sampler_ops_test.py +++ b/tensorflow/python/kernel_tests/candidate_sampler_ops_test.py @@ -22,6 +22,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import candidate_sampling_ops from tensorflow.python.ops import math_ops @@ -37,6 +38,7 @@ class RangeSamplerOpsTest(test.TestCase): TRUE_LABELS = [[1, 2], [0, 4], [3, 3]] + @test_util.run_deprecated_v1 def testTrueCandidates(self): with self.cached_session() as sess: indices = constant_op.constant([0, 0, 1, 1, 2, 2]) @@ -55,7 +57,7 @@ class RangeSamplerOpsTest(test.TestCase): [[1, 2], [0, 4], [3, 3]], dtype=dtypes.int64) sampled_candidates, _, _ = candidate_sampling_ops.all_candidate_sampler( true_classes, self.NUM_TRUE, self.NUM_SAMPLED, True) - result = sampled_candidates.eval() + result = self.evaluate(sampled_candidates) expected_ids = [0, 1, 2, 3, 4] self.assertAllEqual(result, expected_ids) @@ -68,7 +70,7 @@ class RangeSamplerOpsTest(test.TestCase): _, true_expected_count, _ = candidate_sampling_ops.all_candidate_sampler( true_classes, self.NUM_TRUE, self.NUM_SAMPLED, True) true_log_expected_count = math_ops.log(true_expected_count) - result = true_log_expected_count.eval() + result = self.evaluate(true_log_expected_count) self.assertAllEqual(result, [[0.0] * self.NUM_TRUE] * self.BATCH_SIZE) self.assertEqual(true_expected_count.get_shape(), @@ -83,7 +85,7 @@ class RangeSamplerOpsTest(test.TestCase): _, _, sampled_expected_count = candidate_sampling_ops.all_candidate_sampler( # pylint: disable=line-too-long true_classes, self.NUM_TRUE, self.NUM_SAMPLED, True) sampled_log_expected_count = math_ops.log(sampled_expected_count) - result = sampled_log_expected_count.eval() + result = self.evaluate(sampled_log_expected_count) self.assertAllEqual(result, [0.0] * self.NUM_SAMPLED) self.assertEqual(sampled_expected_count.get_shape(), [self.NUM_SAMPLED]) @@ -97,7 +99,7 @@ class RangeSamplerOpsTest(test.TestCase): true_classes, self.NUM_TRUE, self.NUM_SAMPLED, True) accidental_hits = candidate_sampling_ops.compute_accidental_hits( true_classes, sampled_candidates, self.NUM_TRUE) - indices, ids, weights = sess.run(accidental_hits) + indices, ids, weights = self.evaluate(accidental_hits) self.assertEqual(1, accidental_hits[0].get_shape().ndims) self.assertEqual(1, accidental_hits[1].get_shape().ndims) @@ -106,6 +108,7 @@ class RangeSamplerOpsTest(test.TestCase): self.assertTrue(id_ in self.TRUE_LABELS[index]) self.assertLess(weight, -1.0e37) + @test_util.run_deprecated_v1 def testSeed(self): def draw(seed): @@ -114,7 +117,7 @@ class RangeSamplerOpsTest(test.TestCase): [[1, 2], [0, 4], [3, 3]], dtype=dtypes.int64) sampled, _, _ = candidate_sampling_ops.log_uniform_candidate_sampler( true_classes, self.NUM_TRUE, self.NUM_SAMPLED, True, 5, seed=seed) - return sampled.eval() + return self.evaluate(sampled) # Non-zero seed. Repeatable. for seed in [1, 12, 123, 1234]: diff --git a/tensorflow/python/kernel_tests/cast_op_test.py b/tensorflow/python/kernel_tests/cast_op_test.py index a5dff5df62..b3187e1637 100644 --- a/tensorflow/python/kernel_tests/cast_op_test.py +++ b/tensorflow/python/kernel_tests/cast_op_test.py @@ -25,6 +25,7 @@ import platform from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import math_ops @@ -90,10 +91,12 @@ class CastOpTest(test.TestCase): if x.dtype == np.float32 or x.dtype == np.float64: self._testTypes(x, use_gpu=True) + @test_util.run_deprecated_v1 def testBasic(self): self._testAll(np.arange(-10, 10).reshape(2, 10)) self._testAll(np.linspace(-10, 10, 17)) + @test_util.run_deprecated_v1 def testSmallValues(self): f4 = np.finfo(np.float32) f8 = np.finfo(np.float64) @@ -107,11 +110,12 @@ class CastOpTest(test.TestCase): a = np.random.uniform(-100, 100, 100).astype(np.float32) with self.cached_session(use_gpu=False): b = math_ops.cast(math_ops.cast(a, dtypes.bfloat16), dtypes.float32) - self.assertAllClose(a, b.eval(), rtol=1 / 128.) + self.assertAllClose(a, self.evaluate(b), rtol=1 / 128.) with self.cached_session(use_gpu=True): b = math_ops.cast(math_ops.cast(a, dtypes.bfloat16), dtypes.float32) - self.assertAllClose(a, b.eval(), rtol=1 / 128.) + self.assertAllClose(a, self.evaluate(b), rtol=1 / 128.) + @test_util.run_deprecated_v1 def testRandom(self): self._testAll(np.random.normal(0, 10, 210).reshape([2, 3, 5, 7])) self._testAll(np.random.normal(0, 1e6, 210).reshape([2, 3, 5, 7])) @@ -124,6 +128,7 @@ class CastOpTest(test.TestCase): self._cast( x, dst_dtype, use_gpu=use_gpu), dst_dtype(expected)) + @test_util.run_deprecated_v1 def testIntToFloatBoundary(self): i4 = np.iinfo(np.int32) i8 = np.iinfo(np.int64) @@ -138,6 +143,7 @@ class CastOpTest(test.TestCase): self._compare(i8.max, np.float64, i8.max, False) # NOTE: GPU does not support int32/int64 for casting. + @test_util.run_deprecated_v1 def testInfNan(self): i4 = np.iinfo(np.int32) i8 = np.iinfo(np.int64) @@ -181,14 +187,16 @@ class CastOpTest(test.TestCase): def testNotImplemented(self): self._OpError(np.arange(0, 10), dtypes.string, "Cast.*int64.*string.*") + @test_util.run_deprecated_v1 def testCastToTypeOfVariable(self): with self.cached_session() as sess: x = variables.Variable(5, dtype=dtypes.float32) y = variables.Variable(True, dtype=dtypes.bool) cast = math_ops.cast(y, x.dtype) variables.global_variables_initializer().run() - self.assertEqual(1.0, sess.run(cast)) + self.assertEqual(1.0, self.evaluate(cast)) + @test_util.run_deprecated_v1 def testGradients(self): t = [dtypes.float32, dtypes.float64, dtypes.complex64, dtypes.complex128] for src_t in t: @@ -203,6 +211,7 @@ class CastOpTest(test.TestCase): class SparseTensorCastTest(test.TestCase): + @test_util.run_deprecated_v1 def testCast(self): indices = constant_op.constant([[0], [1], [2]], dtypes.int64) values = constant_op.constant(np.array([1, 2, 3], np.int64)) @@ -229,7 +238,7 @@ class SaturateCastTest(test.TestCase): [lo, lo + 1, lo // 2, hi // 2, hi - 1, hi], dtype=in_type) y = math_ops.saturate_cast(x, dtype=out_type) self.assertEqual(y.dtype, out_type) - x, y = sess.run([x, y]) + x, y = self.evaluate([x, y]) correct = np.maximum(out_type.min, np.minimum(out_type.max, x)) self.assertAllEqual(correct, y) diff --git a/tensorflow/python/kernel_tests/check_ops_test.py b/tensorflow/python/kernel_tests/check_ops_test.py index 88f5cd6f22..95bac85027 100644 --- a/tensorflow/python/kernel_tests/check_ops_test.py +++ b/tensorflow/python/kernel_tests/check_ops_test.py @@ -25,6 +25,7 @@ from tensorflow.core.protobuf import config_pb2 from tensorflow.core.protobuf import rewriter_config_pb2 from tensorflow.python.client import session from tensorflow.python.eager import context +from tensorflow.python.eager import def_function from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors @@ -39,6 +40,69 @@ from tensorflow.python.ops import random_ops from tensorflow.python.platform import test +class AssertV2Asserts(test.TestCase): + + def test_passes_when_it_should(self): + # This is a v2 test and need to run eagerly + with context.eager_mode(): + c1 = constant_op.constant(-1, name="minus_one", dtype=dtypes.int32) + c2 = constant_op.constant(2, name="two", dtype=dtypes.int32) + c3 = constant_op.constant([3., 3.], name="three", dtype=dtypes.float32) + c4 = constant_op.constant([3., 3.5], name="three_and_a_half", + dtype=dtypes.float32) + scalar = c1 + non_scalar = c3 + integer = c1 + non_integer = c3 + positive = c2 + negative = c1 + cases = [ + (check_ops.assert_equal_v2, (c1, c1), (c1, c2)), + (check_ops.assert_less_v2, (c1, c2), (c1, c1)), + (check_ops.assert_near_v2, (c3, c3), (c3, c4)), + (check_ops.assert_greater_v2, (c2, c1), (c1, c1)), + (check_ops.assert_negative_v2, (negative,), (positive,)), + (check_ops.assert_positive_v2, (positive,), (negative,)), + (check_ops.assert_less_equal_v2, (c1, c1), (c2, c1)), + (check_ops.assert_none_equal_v2, (c1, c2), (c3, c4)), + (check_ops.assert_non_negative_v2, (positive,), (negative,)), + (check_ops.assert_non_positive_v2, (negative,), (positive,)), + (check_ops.assert_greater_equal_v2, (c1, c1), (c1, c2)), + (check_ops.assert_type_v2, (c1, dtypes.int32), (c1, dtypes.float32), + TypeError), + (check_ops.assert_integer_v2, (integer,), (non_integer,), + TypeError), + (check_ops.assert_scalar_v2, (scalar,), (non_scalar,), + ValueError), + (check_ops.assert_rank_v2, (c1, 0), (c3, 2), ValueError), + (check_ops.assert_rank_in_v2, (c1, [0, 1]), (c1, [1, 2]), + ValueError), + (check_ops.assert_rank_at_least_v2, (non_scalar, 1), (scalar, 1), + ValueError), + ] + + for case in cases: + fn = case[0] + passing_args = case[1] + failing_args = case[2] + error = errors.InvalidArgumentError if len(case) < 4 else case[3] + + print("Testing %s passing properly." % fn) + + fn(*passing_args) + + print("Testing %s failing properly." % fn) + + @def_function.function + def failing_fn(): + fn(*failing_args, message="fail") # pylint: disable=cell-var-from-loop + + with self.assertRaisesRegexp(error, "fail"): + failing_fn() + + del failing_fn + + class AssertProperIterableTest(test.TestCase): @test_util.run_in_graph_and_eager_modes @@ -109,6 +173,7 @@ class AssertEqualTest(test.TestCase): assert x is None @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def test_raises_when_greater(self): # Static check static_small = constant_op.constant([1, 2], name="small") @@ -116,6 +181,7 @@ class AssertEqualTest(test.TestCase): with self.assertRaisesRegexp(errors.InvalidArgumentError, "fail"): check_ops.assert_equal(static_big, static_small, message="fail") + @test_util.run_deprecated_v1 def test_raises_when_greater_dynamic(self): with self.cached_session(): small = array_ops.placeholder(dtypes.int32, name="small") @@ -187,6 +253,7 @@ First 2 elements of y: summarize=2) @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def test_raises_when_less(self): # Static check static_small = constant_op.constant([3, 1], name="small") @@ -194,6 +261,7 @@ First 2 elements of y: with self.assertRaisesRegexp(errors.InvalidArgumentError, "fail"): check_ops.assert_equal(static_big, static_small, message="fail") + @test_util.run_deprecated_v1 def test_raises_when_less_dynamic(self): with self.cached_session(): small = array_ops.placeholder(dtypes.int32, name="small") @@ -253,6 +321,7 @@ class AssertNoneEqualTest(test.TestCase): self.evaluate(out) @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def test_raises_when_equal(self): small = constant_op.constant([3, 1], name="small") with self.assertRaisesOpError("x != y did not hold"): @@ -442,6 +511,7 @@ class AssertAllCloseTest(test.TestCase): class AssertLessTest(test.TestCase): @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def test_raises_when_equal(self): small = constant_op.constant([1, 2], name="small") with self.assertRaisesOpError("failure message.*\n*.* x < y did not hold"): @@ -452,6 +522,7 @@ class AssertLessTest(test.TestCase): self.evaluate(out) @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def test_raises_when_greater(self): small = constant_op.constant([1, 2], name="small") big = constant_op.constant([3, 4], name="big") @@ -518,6 +589,7 @@ class AssertLessEqualTest(test.TestCase): self.evaluate(out) @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def test_raises_when_greater(self): small = constant_op.constant([1, 2], name="small") big = constant_op.constant([3, 4], name="big") @@ -573,6 +645,7 @@ class AssertLessEqualTest(test.TestCase): class AssertGreaterTest(test.TestCase): @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def test_raises_when_equal(self): small = constant_op.constant([1, 2], name="small") with self.assertRaisesOpError("fail"): @@ -583,6 +656,7 @@ class AssertGreaterTest(test.TestCase): self.evaluate(out) @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def test_raises_when_less(self): small = constant_op.constant([1, 2], name="small") big = constant_op.constant([3, 4], name="big") @@ -642,6 +716,7 @@ class AssertGreaterEqualTest(test.TestCase): self.evaluate(out) @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def test_raises_when_less(self): small = constant_op.constant([1, 2], name="small") big = constant_op.constant([3, 4], name="big") @@ -706,6 +781,7 @@ class AssertNegativeTest(test.TestCase): self.evaluate(out) @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def test_raises_when_positive(self): doug = constant_op.constant([1, 2], name="doug") with self.assertRaisesOpError("fail"): @@ -716,6 +792,7 @@ class AssertNegativeTest(test.TestCase): self.evaluate(out) @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def test_raises_when_zero(self): claire = constant_op.constant([0], name="claire") with self.assertRaisesOpError("x < 0 did not hold"): @@ -738,6 +815,7 @@ class AssertNegativeTest(test.TestCase): class AssertPositiveTest(test.TestCase): @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def test_raises_when_negative(self): freddie = constant_op.constant([-1, -2], name="freddie") with self.assertRaisesOpError("fail"): @@ -755,6 +833,7 @@ class AssertPositiveTest(test.TestCase): self.evaluate(out) @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def test_raises_when_zero(self): meechum = constant_op.constant([0], name="meechum") with self.assertRaisesOpError("x > 0 did not hold"): @@ -777,26 +856,31 @@ class AssertPositiveTest(test.TestCase): class EnsureShapeTest(test.TestCase): # Static shape inference + @test_util.run_deprecated_v1 def testStaticShape(self): placeholder = array_ops.placeholder(dtypes.int32) ensure_shape_op = check_ops.ensure_shape(placeholder, (3, 3, 3)) self.assertEqual(ensure_shape_op.get_shape(), (3, 3, 3)) + @test_util.run_deprecated_v1 def testStaticShape_MergesShapes(self): placeholder = array_ops.placeholder(dtypes.int32, shape=(None, None, 3)) ensure_shape_op = check_ops.ensure_shape(placeholder, (5, 4, None)) self.assertEqual(ensure_shape_op.get_shape(), (5, 4, 3)) + @test_util.run_deprecated_v1 def testStaticShape_RaisesErrorWhenRankIncompatible(self): placeholder = array_ops.placeholder(dtypes.int32, shape=(None, None, 3)) with self.assertRaises(ValueError): check_ops.ensure_shape(placeholder, (2, 3)) + @test_util.run_deprecated_v1 def testStaticShape_RaisesErrorWhenDimIncompatible(self): placeholder = array_ops.placeholder(dtypes.int32, shape=(None, None, 3)) with self.assertRaises(ValueError): check_ops.ensure_shape(placeholder, (2, 2, 4)) + @test_util.run_deprecated_v1 def testStaticShape_CanSetUnknownShape(self): placeholder = array_ops.placeholder(dtypes.int32) derived = placeholder / 3 @@ -804,6 +888,7 @@ class EnsureShapeTest(test.TestCase): self.assertEqual(ensure_shape_op.get_shape(), None) # Dynamic shape check + @test_util.run_deprecated_v1 def testEnsuresDynamicShape_RaisesError(self): placeholder = array_ops.placeholder(dtypes.int32) derived = math_ops.divide(placeholder, 3, name="MyDivide") @@ -816,6 +901,7 @@ class EnsureShapeTest(test.TestCase): r"expected shape \[3,3,3\]."): sess.run(derived, feed_dict={placeholder: feed_val}) + @test_util.run_deprecated_v1 def testEnsuresDynamicShape_RaisesErrorDimUnknown(self): placeholder = array_ops.placeholder(dtypes.int32) derived = placeholder / 3 @@ -828,6 +914,7 @@ class EnsureShapeTest(test.TestCase): r"expected shape \[\?,\?,3\]."): sess.run(derived, feed_dict={placeholder: feed_val}) + @test_util.run_deprecated_v1 def testEnsuresDynamicShape(self): placeholder = array_ops.placeholder(dtypes.int32) derived = placeholder / 3 @@ -836,6 +923,7 @@ class EnsureShapeTest(test.TestCase): with self.cached_session() as sess: sess.run(derived, feed_dict={placeholder: feed_val}) + @test_util.run_deprecated_v1 def testEnsuresDynamicShape_WithUnknownDims(self): placeholder = array_ops.placeholder(dtypes.int32) derived = placeholder / 3 @@ -844,6 +932,7 @@ class EnsureShapeTest(test.TestCase): with self.cached_session() as sess: sess.run(derived, feed_dict={placeholder: feed_val}) + @test_util.run_deprecated_v1 def testGradient(self): placeholder = array_ops.placeholder(dtypes.float32) derived = check_ops.ensure_shape(placeholder, (None, None)) @@ -939,6 +1028,7 @@ class AssertRankTest(test.TestCase): tensor, desired_rank, message="fail")]): self.evaluate(array_ops.identity(tensor)) + @test_util.run_deprecated_v1 def test_rank_zero_tensor_raises_if_rank_too_small_dynamic_rank(self): with self.cached_session(): tensor = array_ops.placeholder(dtypes.float32, name="my_tensor") @@ -957,6 +1047,7 @@ class AssertRankTest(test.TestCase): [check_ops.assert_rank(tensor, desired_rank)]): self.evaluate(array_ops.identity(tensor)) + @test_util.run_deprecated_v1 def test_rank_zero_tensor_doesnt_raise_if_rank_just_right_dynamic_rank(self): with self.cached_session(): tensor = array_ops.placeholder(dtypes.float32, name="my_tensor") @@ -974,6 +1065,7 @@ class AssertRankTest(test.TestCase): [check_ops.assert_rank(tensor, desired_rank)]): self.evaluate(array_ops.identity(tensor)) + @test_util.run_deprecated_v1 def test_rank_one_tensor_raises_if_rank_too_large_dynamic_rank(self): with self.cached_session(): tensor = array_ops.placeholder(dtypes.float32, name="my_tensor") @@ -991,6 +1083,7 @@ class AssertRankTest(test.TestCase): [check_ops.assert_rank(tensor, desired_rank)]): self.evaluate(array_ops.identity(tensor)) + @test_util.run_deprecated_v1 def test_rank_one_tensor_doesnt_raise_if_rank_just_right_dynamic_rank(self): with self.cached_session(): tensor = array_ops.placeholder(dtypes.float32, name="my_tensor") @@ -1008,6 +1101,7 @@ class AssertRankTest(test.TestCase): [check_ops.assert_rank(tensor, desired_rank)]): self.evaluate(array_ops.identity(tensor)) + @test_util.run_deprecated_v1 def test_rank_one_tensor_raises_if_rank_too_small_dynamic_rank(self): with self.cached_session(): tensor = array_ops.placeholder(dtypes.float32, name="my_tensor") @@ -1023,6 +1117,7 @@ class AssertRankTest(test.TestCase): with self.assertRaisesRegexp(ValueError, "Rank must be a scalar"): check_ops.assert_rank(tensor, np.array([], dtype=np.int32)) + @test_util.run_deprecated_v1 def test_raises_if_rank_is_not_scalar_dynamic(self): with self.cached_session(): tensor = constant_op.constant( @@ -1040,6 +1135,7 @@ class AssertRankTest(test.TestCase): "must be of type "): check_ops.assert_rank(tensor, .5) + @test_util.run_deprecated_v1 def test_raises_if_rank_is_not_integer_dynamic(self): with self.cached_session(): tensor = constant_op.constant( @@ -1063,6 +1159,7 @@ class AssertRankInTest(test.TestCase): check_ops.assert_rank_in(tensor_rank0, (1, 2), message="fail")]): self.evaluate(array_ops.identity(tensor_rank0)) + @test_util.run_deprecated_v1 def test_rank_zero_tensor_raises_if_rank_mismatch_dynamic_rank(self): with self.cached_session(): tensor_rank0 = array_ops.placeholder(dtypes.float32, name="my_tensor") @@ -1079,6 +1176,7 @@ class AssertRankInTest(test.TestCase): check_ops.assert_rank_in(tensor_rank0, desired_ranks)]): self.evaluate(array_ops.identity(tensor_rank0)) + @test_util.run_deprecated_v1 def test_rank_zero_tensor_doesnt_raise_if_rank_matches_dynamic_rank(self): with self.cached_session(): tensor_rank0 = array_ops.placeholder(dtypes.float32, name="my_tensor") @@ -1095,6 +1193,7 @@ class AssertRankInTest(test.TestCase): check_ops.assert_rank_in(tensor_rank1, desired_ranks)]): self.evaluate(array_ops.identity(tensor_rank1)) + @test_util.run_deprecated_v1 def test_rank_one_tensor_doesnt_raise_if_rank_matches_dynamic_rank(self): with self.cached_session(): tensor_rank1 = array_ops.placeholder(dtypes.float32, name="my_tensor") @@ -1113,6 +1212,7 @@ class AssertRankInTest(test.TestCase): check_ops.assert_rank_in(tensor_rank1, (0, 2))]): self.evaluate(array_ops.identity(tensor_rank1)) + @test_util.run_deprecated_v1 def test_rank_one_tensor_raises_if_rank_mismatches_dynamic_rank(self): with self.cached_session(): tensor_rank1 = array_ops.placeholder(dtypes.float32, name="my_tensor") @@ -1132,6 +1232,7 @@ class AssertRankInTest(test.TestCase): with self.assertRaisesRegexp(ValueError, "Rank must be a scalar"): check_ops.assert_rank_in(tensor, desired_ranks) + @test_util.run_deprecated_v1 def test_raises_if_rank_is_not_scalar_dynamic(self): with self.cached_session(): tensor = constant_op.constant( @@ -1154,6 +1255,7 @@ class AssertRankInTest(test.TestCase): "must be of type "): check_ops.assert_rank_in(tensor, (1, .5,)) + @test_util.run_deprecated_v1 def test_raises_if_rank_is_not_integer_dynamic(self): with self.cached_session(): tensor = constant_op.constant( @@ -1177,6 +1279,7 @@ class AssertRankAtLeastTest(test.TestCase): [check_ops.assert_rank_at_least(tensor, desired_rank)]): self.evaluate(array_ops.identity(tensor)) + @test_util.run_deprecated_v1 def test_rank_zero_tensor_raises_if_rank_too_small_dynamic_rank(self): with self.cached_session(): tensor = array_ops.placeholder(dtypes.float32, name="my_tensor") @@ -1194,6 +1297,7 @@ class AssertRankAtLeastTest(test.TestCase): [check_ops.assert_rank_at_least(tensor, desired_rank)]): self.evaluate(array_ops.identity(tensor)) + @test_util.run_deprecated_v1 def test_rank_zero_tensor_doesnt_raise_if_rank_just_right_dynamic_rank(self): with self.cached_session(): tensor = array_ops.placeholder(dtypes.float32, name="my_tensor") @@ -1210,6 +1314,7 @@ class AssertRankAtLeastTest(test.TestCase): [check_ops.assert_rank_at_least(tensor, desired_rank)]): self.evaluate(array_ops.identity(tensor)) + @test_util.run_deprecated_v1 def test_rank_one_ten_doesnt_raise_if_rank_too_large_dynamic_rank(self): with self.cached_session(): tensor = array_ops.placeholder(dtypes.float32, name="my_tensor") @@ -1226,6 +1331,7 @@ class AssertRankAtLeastTest(test.TestCase): [check_ops.assert_rank_at_least(tensor, desired_rank)]): self.evaluate(array_ops.identity(tensor)) + @test_util.run_deprecated_v1 def test_rank_one_tensor_doesnt_raise_if_rank_just_right_dynamic_rank(self): with self.cached_session(): tensor = array_ops.placeholder(dtypes.float32, name="my_tensor") @@ -1243,6 +1349,7 @@ class AssertRankAtLeastTest(test.TestCase): [check_ops.assert_rank_at_least(tensor, desired_rank)]): self.evaluate(array_ops.identity(tensor)) + @test_util.run_deprecated_v1 def test_rank_one_tensor_raises_if_rank_too_small_dynamic_rank(self): with self.cached_session(): tensor = array_ops.placeholder(dtypes.float32, name="my_tensor") @@ -1256,6 +1363,7 @@ class AssertRankAtLeastTest(test.TestCase): class AssertNonNegativeTest(test.TestCase): @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def test_raises_when_negative(self): zoe = constant_op.constant([-1, -2], name="zoe") with self.assertRaisesOpError("x >= 0 did not hold"): @@ -1292,6 +1400,7 @@ class AssertNonPositiveTest(test.TestCase): self.evaluate(out) @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def test_raises_when_positive(self): rachel = constant_op.constant([0, 2], name="rachel") with self.assertRaisesOpError("x <= 0 did not hold"): diff --git a/tensorflow/python/kernel_tests/checkpoint_ops_test.py b/tensorflow/python/kernel_tests/checkpoint_ops_test.py index 51611b75af..b8c8c9edb5 100644 --- a/tensorflow/python/kernel_tests/checkpoint_ops_test.py +++ b/tensorflow/python/kernel_tests/checkpoint_ops_test.py @@ -24,6 +24,7 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import gen_checkpoint_ops from tensorflow.python.ops import partitioned_variables from tensorflow.python.ops import variable_scope @@ -48,6 +49,7 @@ class GenerateVocabRemappingTest(test.TestCase): with open(self.old_vocab_file, 'w') as f: f.write('\n'.join(['knitting', 'eminem', 'MISSING']) + '\n') + @test_util.run_deprecated_v1 def test_generate_remapping_with_no_vocab_changes(self): """Tests where vocab does not change at all.""" remapping, num_present = gen_checkpoint_ops.generate_vocab_remapping( @@ -58,8 +60,8 @@ class GenerateVocabRemappingTest(test.TestCase): expected_remapping = range(0, 3) expected_num_present = 3 with self.cached_session(): - self.assertAllEqual(expected_remapping, remapping.eval()) - self.assertAllEqual(expected_num_present, num_present.eval()) + self.assertAllEqual(expected_remapping, self.evaluate(remapping)) + self.assertAllEqual(expected_num_present, self.evaluate(num_present)) def test_generate_remapping_with_shifted_vocab(self): """Tests where vocab is the same, but shifted / ordered differently.""" @@ -71,8 +73,8 @@ class GenerateVocabRemappingTest(test.TestCase): expected_remapping = [2, 0, 1] expected_num_present = 3 with self.cached_session(): - self.assertAllEqual(expected_remapping, remapping.eval()) - self.assertAllEqual(expected_num_present, num_present.eval()) + self.assertAllEqual(expected_remapping, self.evaluate(remapping)) + self.assertAllEqual(expected_num_present, self.evaluate(num_present)) def test_generate_remapping_with_offset(self): """Tests offset and num_new_vocab logic.""" @@ -84,8 +86,8 @@ class GenerateVocabRemappingTest(test.TestCase): expected_remapping = [0] expected_num_present = 1 with self.cached_session(): - self.assertAllEqual(expected_remapping, remapping.eval()) - self.assertAllEqual(expected_num_present, num_present.eval()) + self.assertAllEqual(expected_remapping, self.evaluate(remapping)) + self.assertAllEqual(expected_num_present, self.evaluate(num_present)) def test_generate_remapping_with_old_vocab_size(self): """Tests where old_vocab_size is specified.""" @@ -99,8 +101,8 @@ class GenerateVocabRemappingTest(test.TestCase): expected_remapping = [-1, 0, 1] expected_num_present = 2 with self.cached_session(): - self.assertAllEqual(expected_remapping, remapping.eval()) - self.assertAllEqual(expected_num_present, num_present.eval()) + self.assertAllEqual(expected_remapping, self.evaluate(remapping)) + self.assertAllEqual(expected_num_present, self.evaluate(num_present)) class LoadAndRemapMatrixTest(test.TestCase): @@ -142,7 +144,7 @@ class LoadAndRemapMatrixTest(test.TestCase): num_cols=self.old_num_cols) with self.cached_session(): self.assertAllClose(self.matrix_value[row_remapping], - remapped_matrix.eval()) + self.evaluate(remapped_matrix)) # No row remapping, new weight matrix has third col, then first col. row_remapping = list(range(self.old_num_rows)) @@ -157,7 +159,7 @@ class LoadAndRemapMatrixTest(test.TestCase): num_cols=len(col_remapping)) with self.cached_session(): self.assertAllClose(self.matrix_value[row_remapping][:, col_remapping], - remapped_matrix.eval()) + self.evaluate(remapped_matrix)) # Both row and column remappings. row_remapping = [1, 0, 4] @@ -172,7 +174,7 @@ class LoadAndRemapMatrixTest(test.TestCase): num_cols=len(col_remapping)) with self.cached_session(): self.assertAllClose(self.matrix_value[row_remapping][:, col_remapping], - remapped_matrix.eval()) + self.evaluate(remapped_matrix)) def test_load_and_remap_with_init(self): """Tests the op's load and remap where there are missing entries.""" @@ -190,7 +192,8 @@ class LoadAndRemapMatrixTest(test.TestCase): [33, init_val, init_val, init_val, 1, init_val], [3, 2]) with self.cached_session(): - self.assertAllClose(expected_remapped_matrix, remapped_matrix.eval()) + self.assertAllClose(expected_remapped_matrix, + self.evaluate(remapped_matrix)) def test_load_and_remap_all_missing_rows(self): """Tests when all the rows are missing and need to be initialized.""" @@ -207,7 +210,7 @@ class LoadAndRemapMatrixTest(test.TestCase): with self.cached_session(): self.assertAllClose( np.reshape(initializing_values, (num_rows, self.old_num_cols)), - remapped_matrix.eval()) + self.evaluate(remapped_matrix)) def test_load_and_remap_all_missing_rows_and_cols(self): """Tests when all the rows & cols are missing and need to be initialized.""" @@ -225,7 +228,7 @@ class LoadAndRemapMatrixTest(test.TestCase): with self.cached_session(): self.assertAllClose( np.reshape(initializing_values, (num_rows, num_cols)), - remapped_matrix.eval()) + self.evaluate(remapped_matrix)) def test_load_and_remap_invalid_remapping(self): """Tests that errors are raised when an ID maps to multiple new IDs. @@ -244,7 +247,7 @@ class LoadAndRemapMatrixTest(test.TestCase): num_rows=len(invalid_remapping), num_cols=self.old_num_cols) with self.cached_session(), self.assertRaises(errors.UnimplementedError): - remapped_matrix.eval() + self.evaluate(remapped_matrix) # Invalid column remapping. remapped_matrix = gen_checkpoint_ops.load_and_remap_matrix( @@ -256,7 +259,7 @@ class LoadAndRemapMatrixTest(test.TestCase): num_rows=self.old_num_rows, num_cols=len(invalid_remapping)) with self.cached_session(), self.assertRaises(errors.UnimplementedError): - remapped_matrix.eval() + self.evaluate(remapped_matrix) def test_load_and_remap_incorrect_initializing_values(self): """Tests that errors are raised with incorrect number of init values.""" @@ -273,7 +276,7 @@ class LoadAndRemapMatrixTest(test.TestCase): num_rows=3, num_cols=2) with self.cached_session(), self.assertRaises(errors.InvalidArgumentError): - remapped_matrix.eval() + self.evaluate(remapped_matrix) remapped_matrix = gen_checkpoint_ops.load_and_remap_matrix( ckpt_path=[self.bundle_file], @@ -285,7 +288,7 @@ class LoadAndRemapMatrixTest(test.TestCase): num_rows=3, num_cols=2) with self.cached_session(), self.assertRaises(errors.InvalidArgumentError): - remapped_matrix.eval() + self.evaluate(remapped_matrix) class LoadAndRemapMatrixWithMaxRowsTest(test.TestCase): @@ -324,7 +327,7 @@ class LoadAndRemapMatrixWithMaxRowsTest(test.TestCase): num_rows=num_rows, num_cols=num_cols, max_rows_in_memory=max_rows_in_memory) - self.assertAllClose(np_value[::-1], remapped_matrix.eval()) + self.assertAllClose(np_value[::-1], self.evaluate(remapped_matrix)) # Tests loading the tensor (except for the first and last rows), with # uninitialized values. Requires num_rows to be at least 3 since we're @@ -348,7 +351,7 @@ class LoadAndRemapMatrixWithMaxRowsTest(test.TestCase): np.vstack([ np.tile(42, [prefix_rows, num_cols]), np_value[1:-1], np.tile(42, [suffix_rows, num_cols]) - ]), remapped_matrix.eval()) + ]), self.evaluate(remapped_matrix)) # Tests when everything is taken from initializing_values. new_rows = 7 @@ -365,8 +368,9 @@ class LoadAndRemapMatrixWithMaxRowsTest(test.TestCase): max_rows_in_memory=max_rows_in_memory) self.assertAllClose( np.reshape(initializing_values, (new_rows, num_cols)), - remapped_matrix.eval()) + self.evaluate(remapped_matrix)) + @test_util.run_deprecated_v1 def test_loading_rows_divisible_by_max_rows(self): """Tests loading normal var when rows are evenly divisible by max_rows.""" self._test_loading_variable_with_max_rows( @@ -375,6 +379,7 @@ class LoadAndRemapMatrixWithMaxRowsTest(test.TestCase): # 9 is evenly divisible by 3. max_rows_in_memory=3) + @test_util.run_deprecated_v1 def test_loading_rows_not_divisible_by_max_rows(self): """Tests loading normal var when rows aren't divisible by max_rows.""" self._test_loading_variable_with_max_rows( @@ -383,6 +388,7 @@ class LoadAndRemapMatrixWithMaxRowsTest(test.TestCase): # 9 is not evenly divisible by 4. max_rows_in_memory=4) + @test_util.run_deprecated_v1 def test_loading_rows_less_than_max_rows(self): """Tests loading normal var as a single slice. @@ -394,6 +400,7 @@ class LoadAndRemapMatrixWithMaxRowsTest(test.TestCase): # 10 > 9. max_rows_in_memory=10) + @test_util.run_deprecated_v1 def test_loading_no_max_rows(self): """Tests loading normal var as a single slice with no valid max_rows.""" self._test_loading_variable_with_max_rows( @@ -401,6 +408,7 @@ class LoadAndRemapMatrixWithMaxRowsTest(test.TestCase): partitioner=None, max_rows_in_memory=-1) + @test_util.run_deprecated_v1 def test_loading_partitions_equals_max_rows(self): """Tests loading partitioned var sliced on partition boundary.""" self._test_loading_variable_with_max_rows( @@ -410,6 +418,7 @@ class LoadAndRemapMatrixWithMaxRowsTest(test.TestCase): # exactly 3 rows. max_rows_in_memory=3) + @test_util.run_deprecated_v1 def test_loading_partitions_greater_than_max_rows(self): """Tests loading partitioned var with more slices than partitions.""" self._test_loading_variable_with_max_rows( @@ -419,6 +428,7 @@ class LoadAndRemapMatrixWithMaxRowsTest(test.TestCase): # row at a time. max_rows_in_memory=1) + @test_util.run_deprecated_v1 def test_loading_partitions_less_than_max_rows(self): """Tests loading partitioned var as a single slice. @@ -429,6 +439,7 @@ class LoadAndRemapMatrixWithMaxRowsTest(test.TestCase): partitioner=partitioned_variables.fixed_size_partitioner(3), max_rows_in_memory=10) + @test_util.run_deprecated_v1 def test_loading_partitions_no_max_rows(self): """Tests loading partitioned var as single slice with no valid max_rows.""" self._test_loading_variable_with_max_rows( diff --git a/tensorflow/python/kernel_tests/cholesky_op_test.py b/tensorflow/python/kernel_tests/cholesky_op_test.py index e96b277266..f3947236b1 100644 --- a/tensorflow/python/kernel_tests/cholesky_op_test.py +++ b/tensorflow/python/kernel_tests/cholesky_op_test.py @@ -26,6 +26,7 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes as dtypes_lib from tensorflow.python.framework import errors_impl from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import gen_linalg_ops @@ -97,7 +98,7 @@ def TriAngInvCompositeGrad(l, grad): class CholeskyOpTest(test.TestCase): def _verifyCholeskyBase(self, sess, x, chol, verification): - chol_np, verification_np = sess.run([chol, verification]) + chol_np, verification_np = self.evaluate([chol, verification]) self.assertAllClose(x, verification_np) self.assertShapeEqual(x, chol) # Check that the cholesky is lower triangular, and has positive diagonal @@ -145,6 +146,7 @@ class CholeskyOpTest(test.TestCase): matrices[i] = np.dot(matrices[i].T.conj(), matrices[i]) self._verifyCholesky(matrices) + @test_util.run_deprecated_v1 def testNonSquareMatrix(self): with self.assertRaises(ValueError): linalg_ops.cholesky(np.array([[1., 2., 3.], [3., 4., 5.]])) @@ -175,6 +177,7 @@ class CholeskyOpTest(test.TestCase): self._verifyCholesky(np.empty([0, 2, 2])) self._verifyCholesky(np.empty([2, 0, 0])) + @test_util.run_deprecated_v1 def testConcurrentExecutesWithoutError(self): with self.session(use_gpu=True) as sess: matrix1 = random_ops.random_normal([5, 5], seed=42) @@ -183,8 +186,8 @@ class CholeskyOpTest(test.TestCase): matrix2 = math_ops.matmul(matrix2, matrix2, adjoint_a=True) c1 = linalg_ops.cholesky(matrix1) c2 = linalg_ops.cholesky(matrix2) - c1_val, c2_val = sess.run([c1, c2]) - self.assertAllEqual(c1_val, c2_val) + c1_val, c2_val = self.evaluate([c1, c2]) + self.assertAllClose(c1_val, c2_val) class CholeskyGradTest(test.TestCase): @@ -193,18 +196,21 @@ class CholeskyGradTest(test.TestCase): def getShapes(self, shapeList): return ((elem, int(np.floor(1.2 * elem))) for elem in shapeList) + @test_util.run_deprecated_v1 def testSmallMatrices(self): np.random.seed(0) shapes = self.getShapes([1, 2, 10]) self.runFiniteDifferences( shapes, dtypes=(dtypes_lib.float32, dtypes_lib.float64)) + @test_util.run_deprecated_v1 def testSmallMatricesComplex(self): np.random.seed(0) shapes = self.getShapes([1, 2, 10]) self.runFiniteDifferences( shapes, dtypes=(dtypes_lib.complex64, dtypes_lib.complex128)) + @test_util.run_deprecated_v1 def testOneBlockMatrices(self): np.random.seed(0) shapes = self.getShapes([self._backprop_block_size + 1]) @@ -213,12 +219,14 @@ class CholeskyGradTest(test.TestCase): dtypes=(dtypes_lib.float32, dtypes_lib.float64), scalarTest=True) + @test_util.run_deprecated_v1 def testTwoBlockMatrixFloat(self): np.random.seed(0) shapes = self.getShapes([2 * self._backprop_block_size + 1]) self.runFiniteDifferences( shapes, dtypes=(dtypes_lib.float32,), scalarTest=True) + @test_util.run_deprecated_v1 def testTwoBlockMatrixDouble(self): np.random.seed(0) shapes = self.getShapes([2 * self._backprop_block_size + 1]) @@ -231,6 +239,7 @@ class CholeskyGradTest(test.TestCase): self.runFiniteDifferences( shapes, dtypes=(dtypes_lib.complex64,), scalarTest=True) + @test_util.run_deprecated_v1 def testTwoBlockMatrixComplexDouble(self): np.random.seed(0) shapes = self.getShapes([2 * self._backprop_block_size + 1]) diff --git a/tensorflow/python/kernel_tests/clip_ops_test.py b/tensorflow/python/kernel_tests/clip_ops_test.py index efd7eee847..45f1e6152a 100644 --- a/tensorflow/python/kernel_tests/clip_ops_test.py +++ b/tensorflow/python/kernel_tests/clip_ops_test.py @@ -24,10 +24,12 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import clip_ops from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import gradients_impl +from tensorflow.python.ops import math_ops from tensorflow.python.platform import test @@ -55,7 +57,7 @@ class ClipTest(test.TestCase): np_ans = [[-4.4, 2.0, 3.0], [4.0, 4.4, 4.4]] clip_value = 4.4 ans = clip_ops.clip_by_value(x, -clip_value, clip_value) - tf_ans = ans.eval() + tf_ans = self.evaluate(ans) self.assertAllClose(np_ans, tf_ans) @@ -71,7 +73,7 @@ class ClipTest(test.TestCase): clip_value_min = 2 clip_value_max = 4 ans = clip_ops.clip_by_value(x, clip_value_min, clip_value_max) - tf_ans = ans.eval() + tf_ans = self.evaluate(ans) self.assertAllClose(np_ans, tf_ans) @@ -88,7 +90,7 @@ class ClipTest(test.TestCase): [2, 2, 2, 3, 3, 3], shape=[2, 3], dtype=dtype) clip_value_max = 4 ans = clip_ops.clip_by_value(x, clip_value_min, clip_value_max) - tf_ans = ans.eval() + tf_ans = self.evaluate(ans) self.assertAllClose(np_ans, tf_ans) @@ -105,7 +107,7 @@ class ClipTest(test.TestCase): clip_value_max = constant_op.constant( [6, 6, 6, 6, 6, 6], shape=[2, 3], dtype=dtype) ans = clip_ops.clip_by_value(x, clip_value_min, clip_value_max) - tf_ans = ans.eval() + tf_ans = self.evaluate(ans) self.assertAllClose(np_ans, tf_ans) @@ -123,7 +125,7 @@ class ClipTest(test.TestCase): clip_value_max = constant_op.constant( [5, 5, 5, 7, 7, 7], shape=[2, 3], dtype=dtype) ans = clip_ops.clip_by_value(x, clip_value_min, clip_value_max) - tf_ans = ans.eval() + tf_ans = self.evaluate(ans) self.assertAllClose(np_ans, tf_ans) @@ -144,7 +146,7 @@ class ClipTest(test.TestCase): np_ans = [float('NaN'), 4.0, -4.0] clip_value = 4.0 ans = clip_ops.clip_by_value(x, -clip_value, clip_value) - tf_ans = ans.eval() + tf_ans = self.evaluate(ans) self.assertAllClose(np_ans, tf_ans) @@ -157,14 +159,15 @@ class ClipTest(test.TestCase): np_ans = [[-2.4, 0.0, 0.0], [3.2, 0.0, 0.0]] clip_norm = 4.0 ans = clip_ops.clip_by_norm(x, clip_norm) - tf_ans = ans.eval() + tf_ans = self.evaluate(ans) ans = clip_ops.clip_by_norm(x, clip_norm) - tf_ans_tensor = ans.eval() + tf_ans_tensor = self.evaluate(ans) self.assertAllClose(np_ans, tf_ans) self.assertAllClose(np_ans, tf_ans_tensor) + @test_util.run_deprecated_v1 def testClipByNormGradientZeros(self): with self.session(use_gpu=True): x = array_ops.zeros([3]) @@ -188,7 +191,7 @@ class ClipTest(test.TestCase): np_ans = [[-3.0, 0.0, 0.0], [4.0, 0.0, 0.0]] clip_norm = 6.0 ans = clip_ops.clip_by_norm(x, clip_norm) - tf_ans = ans.eval() + tf_ans = self.evaluate(ans) self.assertAllClose(np_ans, tf_ans) @@ -200,7 +203,7 @@ class ClipTest(test.TestCase): np_ans = [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] clip_norm = 6.0 ans = clip_ops.clip_by_norm(x, clip_norm) - tf_ans = ans.eval() + tf_ans = self.evaluate(ans) self.assertAllClose(np_ans, tf_ans) @@ -212,7 +215,7 @@ class ClipTest(test.TestCase): np_ans = [[-2.4, 0.0, 0.0], [3.2, 0.0, 3.0]] clip_norm = 4.0 ans = clip_ops.clip_by_norm(x, clip_norm, [0]) - tf_ans = ans.eval() + tf_ans = self.evaluate(ans) self.assertAllClose(np_ans, tf_ans) @@ -224,7 +227,7 @@ class ClipTest(test.TestCase): np_ans = [[-3.0, 0.0, 0.0], [3.2, 0.0, 2.4]] clip_norm = 4.0 ans = clip_ops.clip_by_norm(x, clip_norm, [1]) - tf_ans = ans.eval() + tf_ans = self.evaluate(ans) self.assertAllClose(np_ans, tf_ans) @@ -236,11 +239,12 @@ class ClipTest(test.TestCase): np_ans = [[-3.0, 0.0, 0.0], [4.0, 0.0, 3.0]] clip_norm = 6.0 ans = clip_ops.clip_by_norm(x, clip_norm, [1]) - tf_ans = ans.eval() + tf_ans = self.evaluate(ans) self.assertAllClose(np_ans, tf_ans) # ClipByGlobalNorm tests + @test_util.run_deprecated_v1 def testClipByGlobalNormClipped(self): # Norm clipping when clip_norm < 5 with self.session(use_gpu=True): @@ -256,12 +260,13 @@ class ClipTest(test.TestCase): ans, norm = clip_ops.clip_by_global_norm((x0, x1), clip_norm) tf_ans_1 = ans[0].eval() tf_ans_2 = ans[1].eval() - tf_norm = norm.eval() + tf_norm = self.evaluate(norm) self.assertAllClose(tf_norm, 5.0) self.assertAllClose(np_ans_0, tf_ans_1) self.assertAllClose(np_ans_1, tf_ans_2) + @test_util.run_deprecated_v1 def testClipByGlobalNormClippedTensor(self): # Norm clipping when clip_norm < 5 with self.session(use_gpu=True): @@ -277,12 +282,13 @@ class ClipTest(test.TestCase): ans, norm = clip_ops.clip_by_global_norm((x0, x1), clip_norm) tf_ans_1 = ans[0].eval() tf_ans_2 = ans[1].eval() - tf_norm = norm.eval() + tf_norm = self.evaluate(norm) self.assertAllClose(tf_norm, 5.0) self.assertAllClose(np_ans_0, tf_ans_1) self.assertAllClose(np_ans_1, tf_ans_2) + @test_util.run_deprecated_v1 def testClipByGlobalNormSupportsNone(self): # Norm clipping when clip_norm < 5 with self.session(use_gpu=True): @@ -300,12 +306,13 @@ class ClipTest(test.TestCase): self.assertTrue(ans[3] is None) tf_ans_1 = ans[0].eval() tf_ans_2 = ans[2].eval() - tf_norm = norm.eval() + tf_norm = self.evaluate(norm) self.assertAllClose(tf_norm, 5.0) self.assertAllClose(np_ans_0, tf_ans_1) self.assertAllClose(np_ans_1, tf_ans_2) + @test_util.run_deprecated_v1 def testClipByGlobalNormWithIndexedSlicesClipped(self): # Norm clipping when clip_norm < 5 with self.session(use_gpu=True): @@ -322,7 +329,7 @@ class ClipTest(test.TestCase): ans, norm = clip_ops.clip_by_global_norm([x0, x1], clip_norm) tf_ans_1 = ans[0].eval() tf_ans_2 = ans[1].values.eval() - tf_norm = norm.eval() + tf_norm = self.evaluate(norm) self.assertAllClose(tf_norm, 5.0) self.assertAllClose(np_ans_0, tf_ans_1) @@ -339,6 +346,7 @@ class ClipTest(test.TestCase): self.assertEqual(dense_shape, slices.dense_shape) self.assertEqual(dense_shape, modified_slices.dense_shape) + @test_util.run_deprecated_v1 def testClipByGlobalNormNotClipped(self): # No norm clipping when clip_norm >= 5 with self.session(use_gpu=True): @@ -352,12 +360,13 @@ class ClipTest(test.TestCase): ans, norm = clip_ops.clip_by_global_norm([x0, x1], clip_norm) tf_ans_1 = ans[0].eval() tf_ans_2 = ans[1].eval() - tf_norm = norm.eval() + tf_norm = self.evaluate(norm) self.assertAllClose(tf_norm, 5.0) self.assertAllClose(np_ans_0, tf_ans_1) self.assertAllClose(np_ans_1, tf_ans_2) + @test_util.run_deprecated_v1 def testClipByGlobalNormZero(self): # No norm clipping when norm = 0 with self.session(use_gpu=True): @@ -371,12 +380,13 @@ class ClipTest(test.TestCase): ans, norm = clip_ops.clip_by_global_norm([x0, x1], clip_norm) tf_ans_1 = ans[0].eval() tf_ans_2 = ans[1].eval() - tf_norm = norm.eval() + tf_norm = self.evaluate(norm) self.assertAllClose(tf_norm, 0.0) self.assertAllClose(np_ans_0, tf_ans_1) self.assertAllClose(np_ans_1, tf_ans_2) + @test_util.run_deprecated_v1 def testClipByGlobalNormInf(self): with self.session(use_gpu=True): x0 = constant_op.constant([-2.0, 0.0, np.inf, 4.0, 0.0, 0.0], @@ -386,7 +396,7 @@ class ClipTest(test.TestCase): ans, norm = clip_ops.clip_by_global_norm([x0, x1], clip_norm) with self.assertRaisesRegexp(errors.InvalidArgumentError, "global norm"): - norm.eval() + self.evaluate(norm) with self.assertRaisesRegexp(errors.InvalidArgumentError, "global norm"): ans[0].eval() with self.assertRaisesRegexp(errors.InvalidArgumentError, "global norm"): @@ -400,7 +410,7 @@ class ClipTest(test.TestCase): np_ans = [[-2.88, 0.0, 0.0], [3.84, 0.0, 0.0]] clip_norm = 0.8 ans = clip_ops.clip_by_average_norm(x, clip_norm) - tf_ans = ans.eval() + tf_ans = self.evaluate(ans) self.assertAllClose(np_ans, tf_ans) @@ -412,7 +422,7 @@ class ClipTest(test.TestCase): np_ans = [[-2.88, 0.0, 0.0], [3.84, 0.0, 0.0]] clip_norm = constant_op.constant(0.8) ans = clip_ops.clip_by_average_norm(x, clip_norm) - tf_ans = ans.eval() + tf_ans = self.evaluate(ans) self.assertAllClose(np_ans, tf_ans) @@ -424,7 +434,7 @@ class ClipTest(test.TestCase): np_ans = [[-3.0, 0.0, 0.0], [4.0, 0.0, 0.0]] clip_norm = 0.9 ans = clip_ops.clip_by_average_norm(x, clip_norm) - tf_ans = ans.eval() + tf_ans = self.evaluate(ans) self.assertAllClose(np_ans, tf_ans) @@ -436,10 +446,26 @@ class ClipTest(test.TestCase): np_ans = [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] clip_norm = 0.9 ans = clip_ops.clip_by_average_norm(x, clip_norm) - tf_ans = ans.eval() + tf_ans = self.evaluate(ans) self.assertAllClose(np_ans, tf_ans) + def testClipByAverageNormReplacedWithClipByNorm(self): + # Check clip_by_average_norm(t) is the same as + # clip_by_norm(t, clip_norm * tf.to_float(tf.size(t))) + with self.session(use_gpu=True): + x = constant_op.constant([-3.0, 0.0, 0.0, 4.0, 0.0, 0.0], shape=[2, 3]) + # Average norm of x = sqrt(3^2 + 4^2) / 6 = 0.83333333 + # expected answer [[-2.88, 0.0, 0.0], [3.84, 0.0, 0.0]] + clip_norm = constant_op.constant(0.8) + with_norm = clip_ops.clip_by_average_norm(x, clip_norm) + without_norm = clip_ops.clip_by_norm( + x, clip_norm * math_ops.to_float(array_ops.size(x))) + clip_by_average_norm_ans = self.evaluate(with_norm) + clip_by_norm_ans = self.evaluate(without_norm) + self.assertAllClose(clip_by_average_norm_ans, clip_by_norm_ans) + + @test_util.run_deprecated_v1 def testClipByValueEmptyTensor(self): # Test case for GitHub issue 19337 zero = array_ops.placeholder(dtype=dtypes.float32, shape=None) diff --git a/tensorflow/python/kernel_tests/compare_and_bitpack_op_test.py b/tensorflow/python/kernel_tests/compare_and_bitpack_op_test.py index f27a0fc472..215ea97f36 100644 --- a/tensorflow/python/kernel_tests/compare_and_bitpack_op_test.py +++ b/tensorflow/python/kernel_tests/compare_and_bitpack_op_test.py @@ -20,6 +20,7 @@ from __future__ import print_function import numpy as np +from tensorflow.python.framework import test_util from tensorflow.python.ops import math_ops from tensorflow.python.platform import test @@ -30,15 +31,15 @@ class CompareAndBitpackTest(test.TestCase): x, threshold, truth, expected_err_re=None): - with self.cached_session(use_gpu=True): + with test_util.use_gpu(): ans = math_ops.compare_and_bitpack(x, threshold) if expected_err_re is None: - tf_ans = ans.eval() + tf_ans = self.evaluate(ans) self.assertShapeEqual(truth, ans) self.assertAllEqual(tf_ans, truth) else: with self.assertRaisesOpError(expected_err_re): - ans.eval() + self.evaluate(ans) def _testBasic(self, dtype): rows = 371 diff --git a/tensorflow/python/kernel_tests/concat_op_test.py b/tensorflow/python/kernel_tests/concat_op_test.py index 92d09986e6..474760a93f 100644 --- a/tensorflow/python/kernel_tests/concat_op_test.py +++ b/tensorflow/python/kernel_tests/concat_op_test.py @@ -23,6 +23,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors_impl +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gen_array_ops from tensorflow.python.ops import gradient_checker @@ -34,6 +35,7 @@ from tensorflow.python.platform import test class ConcatOpTest(test.TestCase): + @test_util.run_deprecated_v1 def testHStack(self): with self.session(use_gpu=True): p1 = array_ops.placeholder(dtypes.float32, shape=[4, 4]) @@ -49,6 +51,7 @@ class ConcatOpTest(test.TestCase): self.assertAllEqual(result[:4, :], params[p1]) self.assertAllEqual(result[4:, :], params[p2]) + @test_util.run_deprecated_v1 def testVStack(self): with self.session(use_gpu=True): p1 = array_ops.placeholder(dtypes.float32, shape=[4, 4]) @@ -65,25 +68,25 @@ class ConcatOpTest(test.TestCase): self.assertAllEqual(result[:, 4:], params[p2]) def testInt32GPU(self): - with self.session(use_gpu=True): + with test_util.use_gpu(): p1 = np.random.rand(2, 3).astype("i") p2 = np.random.rand(2, 3).astype("i") x1 = constant_op.constant(p1) x2 = constant_op.constant(p2) c = array_ops.concat([x1, x2], 0) - result = c.eval() + result = self.evaluate(c) self.assertAllEqual(result[:2, :], p1) self.assertAllEqual(result[2:, :], p2) def testRefType(self): - with self.session(use_gpu=True): + with test_util.use_gpu(): p1 = np.random.rand(4, 4).astype("f") p2 = np.random.rand(4, 4).astype("f") v1 = variables.Variable(p1) v2 = variables.Variable(p2) c = array_ops.concat([v1, v2], 0) - variables.global_variables_initializer().run() - result = c.eval() + self.evaluate(variables.global_variables_initializer()) + result = self.evaluate(c) self.assertEqual(result.shape, c.get_shape()) self.assertAllEqual(result[:4, :], p1) @@ -137,6 +140,7 @@ class ConcatOpTest(test.TestCase): else: self.assertAllClose(result[ind], params[p[i]], 0.01) + @test_util.run_deprecated_v1 def testRandom(self): self._testRandom(dtypes.bool) self._testRandom(dtypes.float32) @@ -147,6 +151,7 @@ class ConcatOpTest(test.TestCase): self._testRandom(dtypes.complex64) self._testRandom(dtypes.complex128) + @test_util.run_deprecated_v1 def testInvalidConcatDimTypeAndShape(self): a = variables.Variable(constant_op.constant(1.0, shape=[1])) b = variables.Variable(constant_op.constant(2.0, shape=[1])) @@ -172,7 +177,7 @@ class ConcatOpTest(test.TestCase): # Test both positive and negative concat axis. # -2 and 1 correspond to the same axis for 3-dimensional tensors. for axis in [-2, 1]: - with self.cached_session(use_gpu=True): + with test_util.use_gpu(): inp = [] inp_tensors = [] for x in [1, 2, 6]: @@ -195,15 +200,17 @@ class ConcatOpTest(test.TestCase): grad_inp.flatten(), shape=output_shape) grad = gradients_impl.gradients([c], inp_tensors, [grad_tensor]) concated_grad = array_ops.concat(grad, axis) - result = concated_grad.eval() + result = self.evaluate(concated_grad) self.assertAllEqual(result, grad_inp) + @test_util.run_deprecated_v1 def testGradientsSimple(self): self._testGradientsSimple(dtypes.float32) self._testGradientsSimple(dtypes.complex64) + @test_util.run_deprecated_v1 def testGradientsFirstDim(self): - with self.session(use_gpu=True): + with test_util.use_gpu(): inp = [] inp_tensors = [] for x in [1, 2, 6]: @@ -222,15 +229,16 @@ class ConcatOpTest(test.TestCase): grad_inp.flatten(), shape=output_shape) grad = gradients_impl.gradients([c], inp_tensors, [grad_tensor]) concated_grad = array_ops.concat(grad, 0) - result = concated_grad.eval() + result = self.evaluate(concated_grad) self.assertAllEqual(result, grad_inp) + @test_util.run_deprecated_v1 def testGradientsLastDim(self): # Test both positive and negative concat axis. # -1 and 2 correspond to the same axis for 3-dimensional tensors. for axis in [-1, 2]: - with self.cached_session(use_gpu=True): + with test_util.use_gpu(): inp = [] inp_tensors = [] for x in [1, 2, 6]: @@ -249,7 +257,7 @@ class ConcatOpTest(test.TestCase): grad_inp.flatten(), shape=output_shape) grad = gradients_impl.gradients([c], inp_tensors, [grad_tensor]) concated_grad = array_ops.concat(grad, axis) - result = concated_grad.eval() + result = self.evaluate(concated_grad) self.assertAllEqual(result, grad_inp) @@ -261,7 +269,7 @@ class ConcatOpTest(test.TestCase): # Random dim to concat on concat_dim = np.random.randint(5) concat_dim_sizes = np.random.randint(1, 5, size=num_tensors) - with self.cached_session(use_gpu=True): + with test_util.use_gpu(): inp = [] inp_tensors = [] for x in concat_dim_sizes: @@ -279,14 +287,16 @@ class ConcatOpTest(test.TestCase): grad_tensor = constant_op.constant(grad_inp.flatten(), shape=output_shape) grad = gradients_impl.gradients([c], inp_tensors, [grad_tensor]) concated_grad = array_ops.concat(grad, concat_dim) - result = concated_grad.eval() + result = self.evaluate(concated_grad) self.assertAllEqual(result, grad_inp) + @test_util.run_deprecated_v1 def testGradientsRandom(self): for _ in range(5): self._RunAndVerifyGradientsRandom() + @test_util.run_deprecated_v1 def testGradientWithUnknownInputDim(self): with self.session(use_gpu=True): x = array_ops.placeholder(dtypes.float32) @@ -308,6 +318,7 @@ class ConcatOpTest(test.TestCase): self.assertAllEqual(result, grad_inp) + @test_util.run_deprecated_v1 def testShapeError(self): # Rank doesn't match. with self.assertRaises(ValueError): @@ -337,6 +348,7 @@ class ConcatOpTest(test.TestCase): constant_op.constant(20.0, shape=[4, 4, 4]) ], -4) + @test_util.run_deprecated_v1 def testShapeWithUnknownConcatDim(self): p1 = array_ops.placeholder(dtypes.float32) c1 = constant_op.constant(10.0, shape=[4, 4, 4, 4]) @@ -355,10 +367,11 @@ class ConcatOpTest(test.TestCase): with self.assertRaises(ValueError): array_ops.concat([p1, c1, p2, c3], dim) + @test_util.run_deprecated_v1 def testZeroSize(self): # Verify that concat doesn't crash and burn for zero size inputs np.random.seed(7) - with self.session(use_gpu=True) as sess: + with test_util.use_gpu(): for shape0 in (), (2,): axis = len(shape0) for shape1 in (), (3,): @@ -370,12 +383,13 @@ class ConcatOpTest(test.TestCase): # TODO(irving): Make tf.concat handle map, then drop list(). xs = list(map(constant_op.constant, [x0, x1])) c = array_ops.concat(xs, axis) - self.assertAllEqual(c.eval(), correct) + self.assertAllEqual(self.evaluate(c), correct) # Check gradients dc = np.random.randn(*c.get_shape().as_list()) - dxs = sess.run(gradients_impl.gradients(c, xs, dc)) + dxs = self.evaluate(gradients_impl.gradients(c, xs, dc)) self.assertAllEqual(dc, np.concatenate(dxs, axis=axis)) + @test_util.run_deprecated_v1 def testTensorConcatDim0Grad(self): x_shapes = [[20, 7, 3], [10, 7, 3], [14, 7, 3]] output_shape = [44, 7, 3] @@ -390,6 +404,7 @@ class ConcatOpTest(test.TestCase): output_shape) self.assertLess(err, 1e-11) + @test_util.run_deprecated_v1 def testTensorConcatDim1Grad(self): x_shapes = [[20, 7, 3], [20, 3, 3], [20, 1, 3]] output_shape = [20, 11, 3] @@ -404,6 +419,7 @@ class ConcatOpTest(test.TestCase): output_shape) self.assertLess(err, 1e-11) + @test_util.run_deprecated_v1 def testIndexedSlicesConcatDim0Grad(self): x_shapes = [[20, 7, 3], [10, 7, 3], [14, 7, 3]] output_shape = [4, 7, 3] @@ -419,6 +435,7 @@ class ConcatOpTest(test.TestCase): output_shape) self.assertLess(err, 1e-11) + @test_util.run_deprecated_v1 def testIndexedSlicesConcatDim1Grad(self): x_shapes = [[20, 7, 3], [20, 3, 3], [20, 1, 3]] output_shape = [4, 11, 3] @@ -434,6 +451,7 @@ class ConcatOpTest(test.TestCase): output_shape) self.assertLess(err, 1e-11) + @test_util.run_deprecated_v1 def testIndexedSlicesConcatDim2Grad(self): x_shapes = [[20, 7, 3], [20, 7, 1], [20, 7, 2]] output_shape = [4, 7, 6] @@ -449,6 +467,7 @@ class ConcatOpTest(test.TestCase): output_shape) self.assertLess(err, 1e-11) + @test_util.run_deprecated_v1 def testIndexedSlicesConcatDim1Grad_UnknownInputDim(self): x_shapes = [[20, 7, 3], [20, 3, 3], [20, 1, 3]] output_shape = [4, 11, 3] @@ -473,21 +492,22 @@ class ConcatOpTest(test.TestCase): def testConcatTuple(self): c1 = np.random.rand(4, 4) c2 = np.random.rand(4, 4) - with self.cached_session(): - concat_list_t = array_ops.concat([c1, c2], 0) - concat_tuple_t = array_ops.concat((c1, c2), 0) - self.assertAllEqual(concat_list_t.eval(), concat_tuple_t.eval()) + concat_list_t = array_ops.concat([c1, c2], 0) + concat_tuple_t = array_ops.concat((c1, c2), 0) + self.assertAllEqual( + self.evaluate(concat_list_t), self.evaluate(concat_tuple_t)) + @test_util.run_deprecated_v1 def testConcatNoScalars(self): - with self.cached_session(): - scalar = constant_op.constant(7) - dim = array_ops.placeholder(dtypes.int32) - with self.assertRaisesRegexp( - ValueError, r"Can't concatenate scalars \(use tf\.stack instead\)"): - array_ops.concat([scalar, scalar, scalar], dim) + scalar = constant_op.constant(7) + dim = array_ops.placeholder(dtypes.int32) + with self.assertRaisesRegexp( + ValueError, r"Can't concatenate scalars \(use tf\.stack instead\)"): + array_ops.concat([scalar, scalar, scalar], dim) # important as gpu implementation could fail if # shared memory is not large for all the inputs + @test_util.run_deprecated_v1 def testConcatLargeNumberOfTensors(self): with self.session(use_gpu=True): for concat_dim in range(2): @@ -523,33 +543,34 @@ class ConcatOpTest(test.TestCase): self.assertAllEqual(result[index], params[p[i]]) def testConcatEmpty(self): - with self.session(use_gpu=True): + with test_util.use_gpu(): t1 = [] t2 = [] - output = gen_array_ops.concat_v2([t1, t2], 0).eval() - self.assertFalse(output) # Checks that output is empty + output = gen_array_ops.concat_v2([t1, t2], 0) + self.assertFalse(self.evaluate(output)) # Checks that output is empty + @test_util.run_deprecated_v1 def testConcatInvalidAxis(self): with self.assertRaises(ValueError): - with self.session(use_gpu=True): + with test_util.use_gpu(): t1 = [1] t2 = [2] gen_array_ops.concat_v2([t1, t2], 1).eval() def testConcatNegativeAxis(self): - with self.session(use_gpu=True): + with test_util.use_gpu(): t1 = [[1, 2, 3], [4, 5, 6]] t2 = [[7, 8, 9], [10, 11, 12]] c = gen_array_ops.concat_v2([t1, t2], -2) self.assertEqual([4, 3], c.get_shape().as_list()) - output = c.eval() + output = self.evaluate(c) self.assertAllEqual([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]], output) c = gen_array_ops.concat_v2([t1, t2], -1) self.assertEqual([2, 6], c.get_shape().as_list()) - output = c.eval() + output = self.evaluate(c) self.assertAllEqual([[1, 2, 3, 7, 8, 9], [4, 5, 6, 10, 11, 12]], output) def _testGradientsForAxis( @@ -578,6 +599,7 @@ class ConcatOpTest(test.TestCase): result = concated_grad.eval(feed_dict=feed_dict) self.assertAllEqual(result, grad_inp) + @test_util.run_deprecated_v1 def testGradientsNegativeAxis(self): x1 = [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]] x2 = [[7.0, 8.0, 9.0], [10.0, 11.0, 12.0]] @@ -608,78 +630,78 @@ class ConcatOpTest(test.TestCase): def testConcatAxisType(self): for dtype in [dtypes.int32, dtypes.int64]: - with self.cached_session(use_gpu=True): + with test_util.use_gpu(): t1 = [[1, 2, 3], [4, 5, 6]] t2 = [[7, 8, 9], [10, 11, 12]] c = gen_array_ops.concat_v2([t1, t2], constant_op.constant(1, dtype=dtype)) self.assertEqual([2, 6], c.get_shape().as_list()) - output = c.eval() + output = self.evaluate(c) self.assertAllEqual([[1, 2, 3, 7, 8, 9], [4, 5, 6, 10, 11, 12]], output) class ConcatOffsetTest(test.TestCase): def testBasic(self): - with self.session(use_gpu=True) as sess: + with test_util.use_gpu(): cdim = constant_op.constant(1, dtypes.int32) s0 = constant_op.constant([2, 3, 5], dtypes.int32) s1 = constant_op.constant([2, 7, 5], dtypes.int32) s2 = constant_op.constant([2, 20, 5], dtypes.int32) off = gen_array_ops.concat_offset(cdim, [s0, s1, s2]) - ans = sess.run(off) + ans = self.evaluate(off) self.assertAllEqual(ans, [[0, 0, 0], [0, 3, 0], [0, 10, 0]]) + @test_util.run_deprecated_v1 def testNotVector(self): - with self.cached_session() as sess: - cdim = constant_op.constant(1, dtypes.int32) - s0 = constant_op.constant([[2, 3, 5]], dtypes.int32) - s1 = constant_op.constant([[2, 7, 5]], dtypes.int32) - off = gen_array_ops.concat_offset(cdim, [s0, s1]) - with self.assertRaisesRegexp(errors_impl.InvalidArgumentError, - r"should be a vector"): - sess.run(off) - + cdim = constant_op.constant(1, dtypes.int32) + s0 = constant_op.constant([[2, 3, 5]], dtypes.int32) + s1 = constant_op.constant([[2, 7, 5]], dtypes.int32) + off = gen_array_ops.concat_offset(cdim, [s0, s1]) + with self.assertRaisesRegexp(errors_impl.InvalidArgumentError, + r"should be a vector"): + self.evaluate(off) + + @test_util.run_deprecated_v1 def testConcatDimOutOfRange(self): - with self.cached_session() as sess: - cdim = constant_op.constant(4, dtypes.int32) - s0 = constant_op.constant([2, 3, 5], dtypes.int32) - s1 = constant_op.constant([2, 7, 5], dtypes.int32) - off = gen_array_ops.concat_offset(cdim, [s0, s1]) - with self.assertRaisesRegexp(errors_impl.InvalidArgumentError, - r"Concat dim is out of range: 4 vs. 3"): - sess.run(off) - + cdim = constant_op.constant(4, dtypes.int32) + s0 = constant_op.constant([2, 3, 5], dtypes.int32) + s1 = constant_op.constant([2, 7, 5], dtypes.int32) + off = gen_array_ops.concat_offset(cdim, [s0, s1]) + with self.assertRaisesRegexp(errors_impl.InvalidArgumentError, + r"Concat dim is out of range: 4 vs. 3"): + self.evaluate(off) + + @test_util.run_deprecated_v1 def testDimMismatch(self): - with self.cached_session() as sess: - cdim = constant_op.constant(1, dtypes.int32) - s0 = constant_op.constant([2, 3, 5], dtypes.int32) - s1 = constant_op.constant([2, 7, 5, 10], dtypes.int32) - off = gen_array_ops.concat_offset(cdim, [s0, s1]) - with self.assertRaisesRegexp(errors_impl.InvalidArgumentError, - r"should contain 3 elem"): - sess.run(off) - + cdim = constant_op.constant(1, dtypes.int32) + s0 = constant_op.constant([2, 3, 5], dtypes.int32) + s1 = constant_op.constant([2, 7, 5, 10], dtypes.int32) + off = gen_array_ops.concat_offset(cdim, [s0, s1]) + with self.assertRaisesRegexp(errors_impl.InvalidArgumentError, + r"should contain 3 elem"): + self.evaluate(off) + + @test_util.run_deprecated_v1 def testSizeMismatch(self): - with self.cached_session() as sess: - cdim = constant_op.constant(1, dtypes.int32) - s0 = constant_op.constant([2, 3, 5], dtypes.int32) - s1 = constant_op.constant([2, 7, 10], dtypes.int32) - off = gen_array_ops.concat_offset(cdim, [s0, s1]) - with self.assertRaisesRegexp( - errors_impl.InvalidArgumentError, - r"All dimensions except 1 must match. Input 1 has shape \[2 7 10\] " - r"and doesn't match input 0 with shape \[2 3 5\]."): - sess.run(off) + cdim = constant_op.constant(1, dtypes.int32) + s0 = constant_op.constant([2, 3, 5], dtypes.int32) + s1 = constant_op.constant([2, 7, 10], dtypes.int32) + off = gen_array_ops.concat_offset(cdim, [s0, s1]) + with self.assertRaisesRegexp( + errors_impl.InvalidArgumentError, + r"All dimensions except 1 must match. Input 1 has shape \[2 7 10\] " + r"and doesn't match input 0 with shape \[2 3 5\]."): + self.evaluate(off) def testNegativeDim(self): - with self.session(use_gpu=True) as sess: + with test_util.use_gpu(): cdim = constant_op.constant(-2, dtypes.int32) s0 = constant_op.constant([2, 3, 5], dtypes.int32) s1 = constant_op.constant([2, 7, 5], dtypes.int32) s2 = constant_op.constant([2, 20, 5], dtypes.int32) off = gen_array_ops.concat_offset(cdim, [s0, s1, s2]) - ans = sess.run(off) + ans = self.evaluate(off) self.assertAllEqual(ans, [[0, 0, 0], [0, 3, 0], [0, 10, 0]]) cdim = constant_op.constant(-3, dtypes.int32) @@ -687,7 +709,7 @@ class ConcatOffsetTest(test.TestCase): s1 = constant_op.constant([1, 3, 5], dtypes.int32) s2 = constant_op.constant([3, 3, 5], dtypes.int32) off = gen_array_ops.concat_offset(cdim, [s0, s1, s2]) - ans = sess.run(off) + ans = self.evaluate(off) self.assertAllEqual(ans, [[0, 0, 0], [2, 0, 0], [3, 0, 0]]) diff --git a/tensorflow/python/kernel_tests/cond_v2_test.py b/tensorflow/python/kernel_tests/cond_v2_test.py index b077b853ed..1f4b37ce2a 100644 --- a/tensorflow/python/kernel_tests/cond_v2_test.py +++ b/tensorflow/python/kernel_tests/cond_v2_test.py @@ -20,6 +20,7 @@ from __future__ import division from __future__ import print_function from tensorflow.core.protobuf import config_pb2 +from tensorflow.python.eager import context from tensorflow.python.eager import function from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes @@ -32,6 +33,7 @@ from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import data_flow_ops from tensorflow.python.ops import gradients_impl from tensorflow.python.ops import math_ops +from tensorflow.python.ops import tensor_array_ops from tensorflow.python.ops import variables from tensorflow.python.platform import test from tensorflow.python.training import saver @@ -66,6 +68,7 @@ class CondV2Test(test.TestCase): self.assertEqual(expected_val, actual_val) self.assertEqual(expected_grad_val, actual_grad_val) + @test_util.run_deprecated_v1 def testBasic(self): x = constant_op.constant(1.0, name="x") y = constant_op.constant(2.0, name="y") @@ -80,6 +83,7 @@ class CondV2Test(test.TestCase): self._testCond(true_fn, false_fn, [x, y]) self._testCond(true_fn, false_fn, [y]) + @test_util.run_deprecated_v1 def testMultipleOutputs(self): x = constant_op.constant(1.0, name="x") y = constant_op.constant(3.0, name="y") @@ -94,6 +98,7 @@ class CondV2Test(test.TestCase): self._testCond(true_fn, false_fn, [x, y]) self._testCond(true_fn, false_fn, [y]) + @test_util.run_deprecated_v1 def testBasic2(self): x = constant_op.constant(1.0, name="x") y = constant_op.constant(2.0, name="y") @@ -108,6 +113,7 @@ class CondV2Test(test.TestCase): self._testCond(true_fn, false_fn, [x, y]) self._testCond(true_fn, false_fn, [y]) + @test_util.run_deprecated_v1 def testNoInputs(self): with self.cached_session() as sess: pred = array_ops.placeholder(dtypes.bool, name="pred") @@ -124,7 +130,7 @@ class CondV2Test(test.TestCase): self.assertEqual(sess.run(out, {pred: False}), (2.0,)) def _createCond(self, name): - """Helper function for testDefaultName.""" + """Creates a cond_v2 call and returns the output tensor and the cond op.""" pred = constant_op.constant(True, name="pred") x = constant_op.constant(1.0, name="x") @@ -137,11 +143,11 @@ class CondV2Test(test.TestCase): output = cond_v2.cond_v2(pred, true_fn, false_fn, name=name) cond_op = output.op.inputs[0].op self.assertEqual(cond_op.type, "If") - return cond_op + return output, cond_op def testDefaultName(self): with ops.Graph().as_default(): - cond_op = self._createCond(None) + _, cond_op = self._createCond(None) self.assertEqual(cond_op.name, "cond") self.assertRegexpMatches( cond_op.get_attr("then_branch").name, r"cond_true_\d*") @@ -150,14 +156,14 @@ class CondV2Test(test.TestCase): with ops.Graph().as_default(): with ops.name_scope("foo"): - cond1_op = self._createCond("") + _, cond1_op = self._createCond("") self.assertEqual(cond1_op.name, "foo/cond") self.assertRegexpMatches( cond1_op.get_attr("then_branch").name, r"foo_cond_true_\d*") self.assertRegexpMatches( cond1_op.get_attr("else_branch").name, r"foo_cond_false_\d*") - cond2_op = self._createCond(None) + _, cond2_op = self._createCond(None) self.assertEqual(cond2_op.name, "foo/cond_1") self.assertRegexpMatches( cond2_op.get_attr("then_branch").name, r"foo_cond_1_true_\d*") @@ -538,6 +544,7 @@ class CondV2Test(test.TestCase): pred_inner: False }), [5., 0.]) + @test_util.run_deprecated_v1 def testSecondDerivative(self): with self.cached_session() as sess: pred = array_ops.placeholder(dtypes.bool, name="pred") @@ -610,11 +617,11 @@ class CondV2Test(test.TestCase): def testLowering(self): with ops.Graph().as_default() as g: with self.session(graph=g) as sess: - out_cond = self._createCond("cond") + cond_output, _ = self._createCond("cond") run_options = config_pb2.RunOptions(output_partition_graphs=True) run_metadata = config_pb2.RunMetadata() - sess.run(out_cond, options=run_options, run_metadata=run_metadata) + sess.run(cond_output, options=run_options, run_metadata=run_metadata) # If lowering was enabled, there should be a `Switch` node switch_found = any( @@ -634,17 +641,18 @@ class CondV2Test(test.TestCase): self.assertFalse(if_found, "An `If` op was found, but it should be lowered.") + @test_util.run_deprecated_v1 def testLoweringDisabledInXLA(self): with self.session(graph=ops.Graph()) as sess: # Build the cond_v2 in an XLA context xla_context = control_flow_ops.XLAControlFlowContext() xla_context.Enter() - out_cond = self._createCond("cond") + cond_output, _ = self._createCond("cond") xla_context.Exit() run_options = config_pb2.RunOptions(output_partition_graphs=True) run_metadata = config_pb2.RunMetadata() - sess.run(out_cond, options=run_options, run_metadata=run_metadata) + sess.run(cond_output, options=run_options, run_metadata=run_metadata) # Lowering disabled in XLA, there should be no `Switch` node switch_found = any( @@ -666,6 +674,110 @@ class CondV2Test(test.TestCase): if_found, "An `If` op was not found, but the graph should not be lowered.") + @test_util.run_deprecated_v1 + def testLoweringDisabledWithSingleThreadedExecutorContext(self): + with self.session(graph=ops.Graph()) as sess: + @function.defun + def _add_cond(x): + return cond_v2.cond_v2( + constant_op.constant(True, name="pred"), + lambda: x, + lambda: x + 1) + + x = array_ops.placeholder(shape=None, dtype=dtypes.float32) + with context.function_executor_type("SINGLE_THREADED_EXECUTOR"): + out_cond = _add_cond(x) + + # The fact that sess.run() succeeds means lowering is disabled, because + # the single threaded executor does not support cond v1 ops. + sess.run(out_cond, feed_dict={x: 1.0}) + + @test_util.enable_control_flow_v2 + def testStructuredOutputs(self): + x = constant_op.constant(1.0, name="x") + y = constant_op.constant(3.0, name="y") + + def true_fn(): + return ((x * y,), y) + + def false_fn(): + return ((x,), y * 3.0) + + output = control_flow_ops.cond( + constant_op.constant(False), true_fn, false_fn) + self.assertEqual(self.evaluate(output[0][0]), 1.) + self.assertEqual(self.evaluate(output[1]), 9.) + + @test_util.enable_control_flow_v2 + @test_util.run_deprecated_v1 + def testRaisesOutputStructuresMismatch(self): + x = constant_op.constant(1.0, name="x") + y = constant_op.constant(3.0, name="y") + + def true_fn(): + return x * y, y + + def false_fn(): + return ((x,), y * 3.0) + + with self.assertRaisesRegexp( + ValueError, "Outputs of true_fn and false_fn must" + " have the same structure"): + control_flow_ops.cond(constant_op.constant(False), true_fn, false_fn) + + @test_util.enable_control_flow_v2 + def testCondAndTensorArray(self): + x = math_ops.range(-5, 5) + output = tensor_array_ops.TensorArray(dtype=dtypes.int32, size=x.shape[0]) + + def loop_body(i, output): + + def if_true(): + return output.write(i, x[i]**2) + + def if_false(): + return output.write(i, x[i]) + + output = control_flow_ops.cond(x[i] > 0, if_true, if_false) + return i + 1, output + + _, output = control_flow_ops.while_loop( + lambda i, arr: i < x.shape[0], + loop_body, + loop_vars=(constant_op.constant(0), output)) + output_t = output.stack() + self.assertAllEqual( + self.evaluate(output_t), [-5, -4, -3, -2, -1, 0, 1, 4, 9, 16]) + + @test_util.enable_control_flow_v2 + def testCondAndTensorArrayInDefun(self): + + @function.defun + def f(): + x = math_ops.range(-5, 5) + output = tensor_array_ops.TensorArray(dtype=dtypes.int32, size=x.shape[0]) + + def loop_body(i, output): + + def if_true(): + return output.write(i, x[i]**2) + + def if_false(): + return output.write(i, x[i]) + + output = control_flow_ops.cond(x[i] > 0, if_true, if_false) + return i + 1, output + + _, output = control_flow_ops.while_loop( + lambda i, arr: i < x.shape[0], + loop_body, + loop_vars=(constant_op.constant(0), output)) + return output.stack() + + output_t = f() + self.assertAllEqual( + self.evaluate(output_t), [-5, -4, -3, -2, -1, 0, 1, 4, 9, 16]) + class CondV2CollectionTest(test.TestCase): diff --git a/tensorflow/python/kernel_tests/conditional_accumulator_test.py b/tensorflow/python/kernel_tests/conditional_accumulator_test.py index 97ab23fe49..5847e4639b 100644 --- a/tensorflow/python/kernel_tests/conditional_accumulator_test.py +++ b/tensorflow/python/kernel_tests/conditional_accumulator_test.py @@ -26,6 +26,7 @@ from tensorflow.python.framework import dtypes as dtypes_lib from tensorflow.python.framework import errors_impl from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import data_flow_ops from tensorflow.python.ops import math_ops @@ -79,11 +80,13 @@ class ConditionalAccumulatorTest(test.TestCase): attr { key: 'reduction_type' value {s: 'MEAN'} } """, q.accumulator_ref.op.node_def) + @test_util.run_deprecated_v1 def testAccumulatorSizeEmpty(self): with self.cached_session(): q = data_flow_ops.ConditionalAccumulator(dtypes_lib.float32, name="Q") self.assertEqual(q.num_accumulated().eval(), 0) + @test_util.run_deprecated_v1 def testAccumulatorSetGlobalStep(self): with self.cached_session(): q = data_flow_ops.ConditionalAccumulator( @@ -91,6 +94,7 @@ class ConditionalAccumulatorTest(test.TestCase): set_global_step_op = q.set_global_step(1) set_global_step_op.run() + @test_util.run_deprecated_v1 def testAccumulatorApplyGradFloat32(self): with self.cached_session(): q = data_flow_ops.ConditionalAccumulator( @@ -98,6 +102,7 @@ class ConditionalAccumulatorTest(test.TestCase): accum_op = q.apply_grad((10.0,)) accum_op.run() + @test_util.run_deprecated_v1 def testDtypes(self): with self.cached_session() as sess: dtypes = [dtypes_lib.float16, dtypes_lib.float32, dtypes_lib.float64] @@ -111,10 +116,11 @@ class ConditionalAccumulatorTest(test.TestCase): for e in elems: q.apply_grad((e,)).run() - result = sess.run(q.take_grad(1)) + result = self.evaluate(q.take_grad(1)) self.assertEqual(sum(elems) / len(elems), result) + @test_util.run_deprecated_v1 def testAccumulatorMultipleAccumulators(self): with self.cached_session(): q_f32_0 = data_flow_ops.ConditionalAccumulator( @@ -134,6 +140,7 @@ class ConditionalAccumulatorTest(test.TestCase): result = accums[i].take_grad(1).eval() self.assertEqual(result, i + 10.0) + @test_util.run_deprecated_v1 def testAccumulatorApplyAndTakeGradWithShape(self): with self.cached_session(): q = data_flow_ops.ConditionalAccumulator( @@ -149,12 +156,13 @@ class ConditionalAccumulatorTest(test.TestCase): accum_op.run() is_all_equal = True - val = takeg_t.eval() + val = self.evaluate(takeg_t) for i in range(len(val)): for j in range(len(val[i])): is_all_equal &= (val[i][j] == elems_ave[i][j]) self.assertTrue(is_all_equal) + @test_util.run_deprecated_v1 def testAccumulatorApplyGradWithWrongShape(self): q = data_flow_ops.ConditionalAccumulator( dtypes_lib.float32, name="Q", shape=(3, 2)) @@ -165,6 +173,7 @@ class ConditionalAccumulatorTest(test.TestCase): with self.assertRaises(ValueError): q.apply_grad([[1.0], [2.0], [3.0]]) + @test_util.run_deprecated_v1 def testAccumulatorDynamicShape(self): with self.cached_session() as sess: q = data_flow_ops.ConditionalAccumulator( @@ -184,12 +193,13 @@ class ConditionalAccumulatorTest(test.TestCase): sess.run(accum_op, feed_dict={x: elem}) is_all_equal = True - val = takeg_t.eval() + val = self.evaluate(takeg_t) for i in range(len(val)): for j in range(len(val[i])): is_all_equal &= (val[i][j] == elems_ave[i][j]) self.assertTrue(is_all_equal) + @test_util.run_deprecated_v1 def testAccumulatorWrongDynamicShape(self): with self.cached_session() as sess: q = data_flow_ops.ConditionalAccumulator( @@ -208,6 +218,7 @@ class ConditionalAccumulatorTest(test.TestCase): with self.assertRaises(errors_impl.InvalidArgumentError): sess.run(accum_op, feed_dict={x: [[1.0], [2.0], [3.0]]}) + @test_util.run_deprecated_v1 def testAccumulatorSizeAfterApplyGrad(self): with self.cached_session(): q = data_flow_ops.ConditionalAccumulator( @@ -219,6 +230,7 @@ class ConditionalAccumulatorTest(test.TestCase): accum_op.run() self.assertEqual(q.num_accumulated().eval(), 2) + @test_util.run_deprecated_v1 def testAccumulatorSizeAfterApplyGradAndTakeGrad(self): with self.cached_session(): q = data_flow_ops.ConditionalAccumulator( @@ -247,6 +259,7 @@ class ConditionalAccumulatorTest(test.TestCase): extract_t.op.run() self.assertEqual(q.num_accumulated().eval(), 0) + @test_util.run_deprecated_v1 def testAccumulatorTakeGradMean(self): with self.cached_session(): q = data_flow_ops.ConditionalAccumulator( @@ -259,7 +272,7 @@ class ConditionalAccumulatorTest(test.TestCase): for accum_op in accum_ops: accum_op.run() - val = takeg_t.eval() + val = self.evaluate(takeg_t) self.assertEqual(15.0, val) accum_ops = [q.apply_grad((x,), local_step=1) for x in elems] @@ -268,9 +281,10 @@ class ConditionalAccumulatorTest(test.TestCase): for accum_op in accum_ops: accum_op.run() - val = takeg_t.eval() + val = self.evaluate(takeg_t) self.assertEqual(15.0, val) + @test_util.run_deprecated_v1 def testAccumulatorTakeGradSum(self): with self.cached_session(): q = data_flow_ops.ConditionalAccumulator( @@ -286,7 +300,7 @@ class ConditionalAccumulatorTest(test.TestCase): for accum_op in accum_ops: accum_op.run() - val = takeg_t.eval() + val = self.evaluate(takeg_t) self.assertEqual(30.0, val) accum_ops = [q.apply_grad((x,), local_step=1) for x in elems] @@ -295,9 +309,10 @@ class ConditionalAccumulatorTest(test.TestCase): for accum_op in accum_ops: accum_op.run() - val = takeg_t.eval() + val = self.evaluate(takeg_t) self.assertEqual(30.0, val) + @test_util.run_deprecated_v1 def testAccumulatorTakeGradInvalidReductionType(self): with self.assertRaises(ValueError): data_flow_ops.ConditionalAccumulator( @@ -306,6 +321,7 @@ class ConditionalAccumulatorTest(test.TestCase): shape=tensor_shape.TensorShape([1]), reduction_type="Invalid") + @test_util.run_deprecated_v1 def testAccumulatorInvalidTakeGrad(self): with self.cached_session(): q = data_flow_ops.ConditionalAccumulator( @@ -319,8 +335,9 @@ class ConditionalAccumulatorTest(test.TestCase): accum_op.run() with self.assertRaises(errors_impl.InvalidArgumentError): - takeg_t.eval() + self.evaluate(takeg_t) + @test_util.run_deprecated_v1 def testAccumulatorRepeatedTakeGradMean(self): with self.cached_session(): q = data_flow_ops.ConditionalAccumulator( @@ -334,7 +351,7 @@ class ConditionalAccumulatorTest(test.TestCase): for accum_op in accum_ops: accum_op.run() - val = takeg_t.eval() + val = self.evaluate(takeg_t) self.assertEqual(elems_ave, val) elems = [20.0, 30.0] @@ -345,9 +362,10 @@ class ConditionalAccumulatorTest(test.TestCase): for accum_op in accum_ops: accum_op.run() - val = takeg_t.eval() + val = self.evaluate(takeg_t) self.assertEqual(elems_ave + 0.0, val) + @test_util.run_deprecated_v1 def testAccumulatorRepeatedTakeGradSum(self): with self.cached_session(): q = data_flow_ops.ConditionalAccumulator( @@ -364,7 +382,7 @@ class ConditionalAccumulatorTest(test.TestCase): for accum_op in accum_ops: accum_op.run() - val = takeg_t.eval() + val = self.evaluate(takeg_t) self.assertEqual(elems_sum, val) elems = [20.0, 30.0] @@ -375,9 +393,10 @@ class ConditionalAccumulatorTest(test.TestCase): for accum_op in accum_ops: accum_op.run() - val = takeg_t.eval() + val = self.evaluate(takeg_t) self.assertEqual(elems_sum, val) + @test_util.run_deprecated_v1 def testAccumulatorIncrementGlobalStep(self): with self.cached_session(): q = data_flow_ops.ConditionalAccumulator( @@ -392,8 +411,9 @@ class ConditionalAccumulatorTest(test.TestCase): variables.global_variables_initializer().run() for _ in range(3): set_global_step_op.run() - inc_global_step.eval() + self.evaluate(inc_global_step) + @test_util.run_deprecated_v1 def testAccumulatorSetGlobalStepPreventsAccumulation(self): with self.cached_session(): q = data_flow_ops.ConditionalAccumulator( @@ -410,11 +430,12 @@ class ConditionalAccumulatorTest(test.TestCase): accum_op.run() takeg_t = q.take_grad(1) - val = takeg_t.eval() + val = self.evaluate(takeg_t) self.assertEqual(0.0 + sum(x for x in local_steps if x >= ls) / sum(1 for x in local_steps if x >= ls), val) + @test_util.run_deprecated_v1 def testParallelApplyGrad(self): with self.cached_session() as sess: q = data_flow_ops.ConditionalAccumulator( @@ -424,7 +445,7 @@ class ConditionalAccumulatorTest(test.TestCase): takeg_t = q.take_grad(1) def apply_grad(accum_op): - sess.run(accum_op) + self.evaluate(accum_op) threads = [ self.checkedThread( @@ -436,10 +457,11 @@ class ConditionalAccumulatorTest(test.TestCase): for thread in threads: thread.join() - val = takeg_t.eval() + val = self.evaluate(takeg_t) self.assertEqual(val, sum(elems) / len(elems)) + @test_util.run_deprecated_v1 def testParallelTakeGrad(self): with self.cached_session() as sess: q = data_flow_ops.ConditionalAccumulator( @@ -451,14 +473,14 @@ class ConditionalAccumulatorTest(test.TestCase): def apply_grad(): for accum_op in accum_ops: time.sleep(1.0) - sess.run(accum_op) + self.evaluate(accum_op) apply_grad_thread = self.checkedThread(target=apply_grad) results = [] def take_grad(): - results.append(sess.run(takeg_t)) + results.append(self.evaluate(takeg_t)) threads = [self.checkedThread(target=take_grad) for _ in range(10)] @@ -472,6 +494,7 @@ class ConditionalAccumulatorTest(test.TestCase): self.assertItemsEqual(elems, results) + @test_util.run_deprecated_v1 def testAccumulatorApplyAndBlockingTake(self): with self.cached_session() as sess: q = data_flow_ops.ConditionalAccumulator( @@ -485,12 +508,12 @@ class ConditionalAccumulatorTest(test.TestCase): def apply_grad(): time.sleep(1.0) for accum_op in accum_ops: - sess.run(accum_op) + self.evaluate(accum_op) return_array = [] def take_grad(): - return_array.append(sess.run(takeg_t)) + return_array.append(self.evaluate(takeg_t)) accum_thread = self.checkedThread(target=apply_grad) takeg_thread = self.checkedThread(target=take_grad) @@ -503,8 +526,9 @@ class ConditionalAccumulatorTest(test.TestCase): def _blocking_takeg(self, sess, takeg_op): with self.assertRaisesOpError("was cancelled"): - sess.run(takeg_op) + self.evaluate(takeg_op) + @test_util.run_deprecated_v1 def testAccumulatorCancel(self): with self.cached_session() as sess: q = data_flow_ops.ConditionalAccumulator( diff --git a/tensorflow/python/kernel_tests/confusion_matrix_test.py b/tensorflow/python/kernel_tests/confusion_matrix_test.py index bc24345261..ae13c8e32e 100644 --- a/tensorflow/python/kernel_tests/confusion_matrix_test.py +++ b/tensorflow/python/kernel_tests/confusion_matrix_test.py @@ -71,9 +71,11 @@ class ConfusionMatrixTest(test.TestCase): self._testConfMatrix(labels=labels, predictions=predictions, truth=truth) + @test_util.run_deprecated_v1 def testInt32Basic(self): self._testBasic(dtype=np.int32) + @test_util.run_deprecated_v1 def testInt64Basic(self): self._testBasic(dtype=np.int64) @@ -111,9 +113,11 @@ class ConfusionMatrixTest(test.TestCase): self.assertEqual(cm_out.dtype, np_dtype) self.assertAllClose(cm_out, truth, atol=1e-10) + @test_util.run_deprecated_v1 def testOnTensors_int32(self): self._testConfMatrixOnTensors(dtypes.int32, np.int32) + @test_util.run_deprecated_v1 def testOnTensors_int64(self): self._testConfMatrixOnTensors(dtypes.int64, np.int64) @@ -133,9 +137,11 @@ class ConfusionMatrixTest(test.TestCase): self._testConfMatrix(labels=labels, predictions=predictions, truth=truth) + @test_util.run_deprecated_v1 def testInt32DifferentLabels(self, dtype=np.int32): self._testDifferentLabelsInPredictionAndTarget(dtype) + @test_util.run_deprecated_v1 def testInt64DifferentLabels(self, dtype=np.int64): self._testDifferentLabelsInPredictionAndTarget(dtype) @@ -155,12 +161,15 @@ class ConfusionMatrixTest(test.TestCase): self._testConfMatrix(labels=labels, predictions=predictions, truth=truth) + @test_util.run_deprecated_v1 def testInt32MultipleLabels(self, dtype=np.int32): self._testMultipleLabels(dtype) + @test_util.run_deprecated_v1 def testInt64MultipleLabels(self, dtype=np.int64): self._testMultipleLabels(dtype) + @test_util.run_deprecated_v1 def testWeighted(self): labels = np.arange(5, dtype=np.int32) predictions = np.arange(5, dtype=np.int32) @@ -177,6 +186,7 @@ class ConfusionMatrixTest(test.TestCase): self._testConfMatrix( labels=labels, predictions=predictions, weights=weights, truth=truth) + @test_util.run_deprecated_v1 def testLabelsTooLarge(self): labels = np.asarray([1, 1, 0, 3, 5], dtype=np.int32) predictions = np.asarray([2, 1, 0, 2, 2], dtype=np.int32) @@ -191,6 +201,7 @@ class ConfusionMatrixTest(test.TestCase): self._testConfMatrix( labels=labels, predictions=predictions, num_classes=3, truth=None) + @test_util.run_deprecated_v1 def testPredictionsTooLarge(self): labels = np.asarray([1, 1, 0, 2, 2], dtype=np.int32) predictions = np.asarray([2, 1, 0, 3, 5], dtype=np.int32) @@ -205,6 +216,7 @@ class ConfusionMatrixTest(test.TestCase): self._testConfMatrix( labels=labels, predictions=predictions, num_classes=3, truth=None) + @test_util.run_deprecated_v1 def testInvalidRank_predictionsTooBig(self): labels = np.asarray([1, 2, 3]) predictions = np.asarray([[1, 2, 3]]) @@ -212,6 +224,7 @@ class ConfusionMatrixTest(test.TestCase): confusion_matrix.confusion_matrix, predictions, labels) + @test_util.run_deprecated_v1 def testInvalidRank_predictionsTooSmall(self): labels = np.asarray([[1, 2, 3]]) predictions = np.asarray([1, 2, 3]) @@ -219,6 +232,7 @@ class ConfusionMatrixTest(test.TestCase): confusion_matrix.confusion_matrix, predictions, labels) + @test_util.run_deprecated_v1 def testInputDifferentSize(self): labels = np.asarray([1, 2]) predictions = np.asarray([1, 2, 3]) @@ -232,7 +246,7 @@ class ConfusionMatrixTest(test.TestCase): with self.cached_session(): cm = confusion_matrix.confusion_matrix( labels, predictions, dtype=dtypes.int32) - tf_cm = cm.eval() + tf_cm = self.evaluate(cm) self.assertEqual(tf_cm.dtype, np.int32) def testOutputIsInt64(self): @@ -241,12 +255,13 @@ class ConfusionMatrixTest(test.TestCase): with self.cached_session(): cm = confusion_matrix.confusion_matrix( labels, predictions, dtype=dtypes.int64) - tf_cm = cm.eval() + tf_cm = self.evaluate(cm) self.assertEqual(tf_cm.dtype, np.int64) class RemoveSqueezableDimensionsTest(test.TestCase): + @test_util.run_deprecated_v1 def testBothScalarShape(self): label_values = 1.0 prediction_values = 0.0 @@ -261,8 +276,8 @@ class RemoveSqueezableDimensionsTest(test.TestCase): labels_placeholder, predictions_placeholder)) with self.cached_session(): - self.assertAllEqual(label_values, static_labels.eval()) - self.assertAllEqual(prediction_values, static_predictions.eval()) + self.assertAllEqual(label_values, self.evaluate(static_labels)) + self.assertAllEqual(prediction_values, self.evaluate(static_predictions)) feed_dict = { labels_placeholder: label_values, predictions_placeholder: prediction_values @@ -272,6 +287,7 @@ class RemoveSqueezableDimensionsTest(test.TestCase): self.assertAllEqual( prediction_values, dynamic_predictions.eval(feed_dict=feed_dict)) + @test_util.run_deprecated_v1 def testSameShape(self): label_values = np.ones(shape=(2, 3, 1)) prediction_values = np.zeros_like(label_values) @@ -286,8 +302,8 @@ class RemoveSqueezableDimensionsTest(test.TestCase): labels_placeholder, predictions_placeholder)) with self.cached_session(): - self.assertAllEqual(label_values, static_labels.eval()) - self.assertAllEqual(prediction_values, static_predictions.eval()) + self.assertAllEqual(label_values, self.evaluate(static_labels)) + self.assertAllEqual(prediction_values, self.evaluate(static_predictions)) feed_dict = { labels_placeholder: label_values, predictions_placeholder: prediction_values @@ -297,6 +313,7 @@ class RemoveSqueezableDimensionsTest(test.TestCase): self.assertAllEqual( prediction_values, dynamic_predictions.eval(feed_dict=feed_dict)) + @test_util.run_deprecated_v1 def testSameShapeExpectedRankDiff0(self): label_values = np.ones(shape=(2, 3, 1)) prediction_values = np.zeros_like(label_values) @@ -311,8 +328,8 @@ class RemoveSqueezableDimensionsTest(test.TestCase): labels_placeholder, predictions_placeholder, expected_rank_diff=0)) with self.cached_session(): - self.assertAllEqual(label_values, static_labels.eval()) - self.assertAllEqual(prediction_values, static_predictions.eval()) + self.assertAllEqual(label_values, self.evaluate(static_labels)) + self.assertAllEqual(prediction_values, self.evaluate(static_predictions)) feed_dict = { labels_placeholder: label_values, predictions_placeholder: prediction_values @@ -322,6 +339,7 @@ class RemoveSqueezableDimensionsTest(test.TestCase): self.assertAllEqual( prediction_values, dynamic_predictions.eval(feed_dict=feed_dict)) + @test_util.run_deprecated_v1 def testSqueezableLabels(self): label_values = np.ones(shape=(2, 3, 1)) prediction_values = np.zeros(shape=(2, 3)) @@ -337,8 +355,8 @@ class RemoveSqueezableDimensionsTest(test.TestCase): expected_label_values = np.reshape(label_values, newshape=(2, 3)) with self.cached_session(): - self.assertAllEqual(expected_label_values, static_labels.eval()) - self.assertAllEqual(prediction_values, static_predictions.eval()) + self.assertAllEqual(expected_label_values, self.evaluate(static_labels)) + self.assertAllEqual(prediction_values, self.evaluate(static_predictions)) feed_dict = { labels_placeholder: label_values, predictions_placeholder: prediction_values @@ -348,6 +366,7 @@ class RemoveSqueezableDimensionsTest(test.TestCase): self.assertAllEqual( prediction_values, dynamic_predictions.eval(feed_dict=feed_dict)) + @test_util.run_deprecated_v1 def testSqueezableLabelsExpectedRankDiffPlus1(self): label_values = np.ones(shape=(2, 3, 1)) prediction_values = np.zeros(shape=(2, 3, 5)) @@ -363,8 +382,8 @@ class RemoveSqueezableDimensionsTest(test.TestCase): expected_label_values = np.reshape(label_values, newshape=(2, 3)) with self.cached_session(): - self.assertAllEqual(expected_label_values, static_labels.eval()) - self.assertAllEqual(prediction_values, static_predictions.eval()) + self.assertAllEqual(expected_label_values, self.evaluate(static_labels)) + self.assertAllEqual(prediction_values, self.evaluate(static_predictions)) feed_dict = { labels_placeholder: label_values, predictions_placeholder: prediction_values @@ -374,6 +393,7 @@ class RemoveSqueezableDimensionsTest(test.TestCase): self.assertAllEqual( prediction_values, dynamic_predictions.eval(feed_dict=feed_dict)) + @test_util.run_deprecated_v1 def testSqueezablePredictions(self): label_values = np.ones(shape=(2, 3)) prediction_values = np.zeros(shape=(2, 3, 1)) @@ -389,8 +409,9 @@ class RemoveSqueezableDimensionsTest(test.TestCase): expected_prediction_values = np.reshape(prediction_values, newshape=(2, 3)) with self.cached_session(): - self.assertAllEqual(label_values, static_labels.eval()) - self.assertAllEqual(expected_prediction_values, static_predictions.eval()) + self.assertAllEqual(label_values, self.evaluate(static_labels)) + self.assertAllEqual(expected_prediction_values, + self.evaluate(static_predictions)) feed_dict = { labels_placeholder: label_values, predictions_placeholder: prediction_values @@ -401,6 +422,7 @@ class RemoveSqueezableDimensionsTest(test.TestCase): expected_prediction_values, dynamic_predictions.eval(feed_dict=feed_dict)) + @test_util.run_deprecated_v1 def testSqueezablePredictionsExpectedRankDiffMinus1(self): label_values = np.ones(shape=(2, 3, 5)) prediction_values = np.zeros(shape=(2, 3, 1)) @@ -416,8 +438,9 @@ class RemoveSqueezableDimensionsTest(test.TestCase): expected_prediction_values = np.reshape(prediction_values, newshape=(2, 3)) with self.cached_session(): - self.assertAllEqual(label_values, static_labels.eval()) - self.assertAllEqual(expected_prediction_values, static_predictions.eval()) + self.assertAllEqual(label_values, self.evaluate(static_labels)) + self.assertAllEqual(expected_prediction_values, + self.evaluate(static_predictions)) feed_dict = { labels_placeholder: label_values, predictions_placeholder: prediction_values @@ -428,6 +451,7 @@ class RemoveSqueezableDimensionsTest(test.TestCase): expected_prediction_values, dynamic_predictions.eval(feed_dict=feed_dict)) + @test_util.run_deprecated_v1 def testUnsqueezableLabels(self): label_values = np.ones(shape=(2, 3, 2)) prediction_values = np.zeros(shape=(2, 3)) @@ -453,6 +477,7 @@ class RemoveSqueezableDimensionsTest(test.TestCase): self.assertAllEqual( prediction_values, dynamic_predictions.eval(feed_dict=feed_dict)) + @test_util.run_deprecated_v1 def testUnsqueezablePredictions(self): label_values = np.ones(shape=(2, 3)) prediction_values = np.zeros(shape=(2, 3, 2)) diff --git a/tensorflow/python/kernel_tests/constant_op_test.py b/tensorflow/python/kernel_tests/constant_op_test.py index 38b8c0c146..583082c2aa 100644 --- a/tensorflow/python/kernel_tests/constant_op_test.py +++ b/tensorflow/python/kernel_tests/constant_op_test.py @@ -70,6 +70,7 @@ class ConstantTest(test.TestCase): with self.assertRaises(TypeError): constant_op.constant(dtypes_lib.string, "[,]") + @test_util.run_deprecated_v1 def testBFloat16(self): bfloat16 = dtypes_lib.bfloat16.as_numpy_dtype self._testAll(np.arange(-15, 15).reshape([2, 3, 5]).astype(bfloat16)) @@ -77,36 +78,42 @@ class ConstantTest(test.TestCase): np.random.normal(size=30).reshape([2, 3, 5]).astype(bfloat16)) self._testAll(np.empty((2, 0, 5)).astype(bfloat16)) + @test_util.run_deprecated_v1 def testHalf(self): self._testAll(np.arange(-15, 15).reshape([2, 3, 5]).astype(np.float16)) self._testAll( np.random.normal(size=30).reshape([2, 3, 5]).astype(np.float16)) self._testAll(np.empty((2, 0, 5)).astype(np.float16)) + @test_util.run_deprecated_v1 def testFloat(self): self._testAll(np.arange(-15, 15).reshape([2, 3, 5]).astype(np.float32)) self._testAll( np.random.normal(size=30).reshape([2, 3, 5]).astype(np.float32)) self._testAll(np.empty((2, 0, 5)).astype(np.float32)) + @test_util.run_deprecated_v1 def testDouble(self): self._testAll(np.arange(-15, 15).reshape([2, 3, 5]).astype(np.float64)) self._testAll( np.random.normal(size=30).reshape([2, 3, 5]).astype(np.float64)) self._testAll(np.empty((2, 0, 5)).astype(np.float64)) + @test_util.run_deprecated_v1 def testInt32(self): self._testAll(np.arange(-15, 15).reshape([2, 3, 5]).astype(np.int32)) self._testAll((100 * np.random.normal(size=30)).reshape([2, 3, 5]).astype( np.int32)) self._testAll(np.empty((2, 0, 5)).astype(np.int32)) + @test_util.run_deprecated_v1 def testInt64(self): self._testAll(np.arange(-15, 15).reshape([2, 3, 5]).astype(np.int64)) self._testAll((100 * np.random.normal(size=30)).reshape([2, 3, 5]).astype( np.int64)) self._testAll(np.empty((2, 0, 5)).astype(np.int64)) + @test_util.run_deprecated_v1 def testComplex64(self): self._testAll( np.complex(1, 2) * @@ -116,6 +123,7 @@ class ConstantTest(test.TestCase): np.random.normal(size=30).reshape([2, 3, 5]).astype(np.complex64)) self._testAll(np.empty((2, 0, 5)).astype(np.complex64)) + @test_util.run_deprecated_v1 def testComplex128(self): self._testAll( np.complex(1, 2) * @@ -125,12 +133,14 @@ class ConstantTest(test.TestCase): np.random.normal(size=30).reshape([2, 3, 5]).astype(np.complex128)) self._testAll(np.empty((2, 0, 5)).astype(np.complex128)) + @test_util.run_deprecated_v1 def testString(self): self._testCpu( np.array([compat.as_bytes(str(x)) for x in np.arange(-15, 15)]).reshape( [2, 3, 5])) self._testCpu(np.empty((2, 0, 5)).astype(np.str_)) + @test_util.run_deprecated_v1 def testVariant(self): # TODO(ebrevdo): Re-enable use_gpu=True once non-DMA Variant # copying between CPU and GPU is supported. @@ -161,6 +171,7 @@ class ConstantTest(test.TestCase): message="Variant storing an int, decoded const value:").op logging_const_op.run() + @test_util.run_deprecated_v1 def testStringWithNulls(self): with self.cached_session(): val = ops.convert_to_tensor(b"\0\0\0\0").eval() @@ -219,16 +230,28 @@ class ConstantTest(test.TestCase): def testShapeInconsistent(self): with ops.Graph().as_default(): - c = constant_op.constant([1, 2, 3, 4, 5, 6, 7], shape=[10]) + c = constant_op.constant_v1([1, 2, 3, 4, 5, 6, 7], shape=[10]) + self.assertEqual(c.get_shape(), [10]) + + with ops.Graph().as_default(): + with self.assertRaisesRegexp( + TypeError, "Expected Tensor's shape"): + c = constant_op.constant([1, 2, 3, 4, 5, 6, 7], shape=[10]) + + def testPromotionShapes(self): + with ops.Graph().as_default(): + c = constant_op.constant([7], shape=[10]) + self.assertEqual(c.get_shape(), [10]) + with ops.Graph().as_default(): + c = constant_op.constant(3, shape=[10]) self.assertEqual(c.get_shape(), [10]) # pylint: disable=g-long-lambda def testShapeWrong(self): with ops.Graph().as_default(): - with self.assertRaisesWithPredicateMatch( - ValueError, - lambda e: ("Too many elements provided. Needed at most 5, " - "but received 7" == str(e))): + with self.assertRaisesRegexp(ValueError, "Too many elements provided."): + constant_op.constant_v1([1, 2, 3, 4, 5, 6, 7], shape=[5]) + with self.assertRaisesRegexp(TypeError, "Expected Tensor's shape"): constant_op.constant([1, 2, 3, 4, 5, 6, 7], shape=[5]) # pylint: enable=g-long-lambda @@ -253,6 +276,7 @@ class ConstantTest(test.TestCase): "GraphDef cannot be larger than 2GB."): g.as_graph_def() + @test_util.run_deprecated_v1 def testSparseValuesRaiseErrors(self): with self.assertRaisesRegexp(ValueError, "setting an array element with a sequence"): @@ -282,29 +306,29 @@ class AsTensorTest(test.TestCase): with self.cached_session(): x = ops.convert_to_tensor(tensor_shape.TensorShape([])) self.assertEqual(dtypes_lib.int32, x.dtype) - self.assertAllEqual([], x.eval()) + self.assertAllEqual([], self.evaluate(x)) x = ops.convert_to_tensor(tensor_shape.TensorShape([1, 2, 3])) self.assertEqual(dtypes_lib.int32, x.dtype) - self.assertAllEqual([1, 2, 3], x.eval()) + self.assertAllEqual([1, 2, 3], self.evaluate(x)) x = ops.convert_to_tensor(tensor_shape.TensorShape([2**31-1, 2, 3])) self.assertEqual(dtypes_lib.int32, x.dtype) - self.assertAllEqual([2**31-1, 2, 3], x.eval()) + self.assertAllEqual([2**31 - 1, 2, 3], self.evaluate(x)) x = ops.convert_to_tensor(tensor_shape.TensorShape([2**31-1, 2, 3]), dtype=dtypes_lib.int32) self.assertEqual(dtypes_lib.int32, x.dtype) - self.assertAllEqual([2**31-1, 2, 3], x.eval()) + self.assertAllEqual([2**31 - 1, 2, 3], self.evaluate(x)) x = ops.convert_to_tensor(tensor_shape.TensorShape([2**31, 2, 3])) self.assertEqual(dtypes_lib.int64, x.dtype) - self.assertAllEqual([2**31, 2, 3], x.eval()) + self.assertAllEqual([2**31, 2, 3], self.evaluate(x)) x = ops.convert_to_tensor(tensor_shape.TensorShape([2**31, 2, 3]), dtype=dtypes_lib.int64) self.assertEqual(dtypes_lib.int64, x.dtype) - self.assertAllEqual([2**31, 2, 3], x.eval()) + self.assertAllEqual([2**31, 2, 3], self.evaluate(x)) with self.assertRaisesRegexp( ValueError, "a dimension is too large .2147483648."): @@ -314,11 +338,11 @@ class AsTensorTest(test.TestCase): x = ops.convert_to_tensor( tensor_shape.TensorShape([1, 2, 3]), dtype=dtypes_lib.int64) self.assertEqual(dtypes_lib.int64, x.dtype) - self.assertAllEqual([1, 2, 3], x.eval()) + self.assertAllEqual([1, 2, 3], self.evaluate(x)) x = array_ops.reshape( array_ops.zeros([6]), tensor_shape.TensorShape([2, 3])) - self.assertAllEqual([[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]], x.eval()) + self.assertAllEqual([[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]], self.evaluate(x)) with self.assertRaisesRegexp(ValueError, "partially known"): ops.convert_to_tensor(tensor_shape.TensorShape(None)) @@ -330,16 +354,17 @@ class AsTensorTest(test.TestCase): ops.convert_to_tensor( tensor_shape.TensorShape([1, 2, 3]), dtype=dtypes_lib.float32) + @test_util.run_deprecated_v1 def testAsTensorForDimensionInput(self): with self.cached_session(): x = ops.convert_to_tensor(tensor_shape.TensorShape([1, 2, 3])[1]) self.assertEqual(dtypes_lib.int32, x.dtype) - self.assertAllEqual(2, x.eval()) + self.assertAllEqual(2, self.evaluate(x)) x = ops.convert_to_tensor( tensor_shape.TensorShape([1, 2, 3])[1], dtype=dtypes_lib.int64) self.assertEqual(dtypes_lib.int64, x.dtype) - self.assertAllEqual(2, x.eval()) + self.assertAllEqual(2, self.evaluate(x)) shape = tensor_shape.TensorShape(None) if shape._v2_behavior: @@ -372,7 +397,7 @@ class ZerosTest(test.TestCase): with self.cached_session(): ret = array_ops.zeros(shape) self.assertEqual(shape, ret.get_shape()) - return ret.eval() + return self.evaluate(ret) def testConst(self): self.assertTrue( @@ -383,7 +408,7 @@ class ZerosTest(test.TestCase): self.assertEqual(0, self._Zeros(())) with self.cached_session(): scalar = array_ops.zeros(constant_op.constant([], dtype=dtypes_lib.int32)) - self.assertEqual(0, scalar.eval()) + self.assertEqual(0, self.evaluate(scalar)) def testDynamicSizes(self): np_ans = np.array([[0] * 3] * 2) @@ -392,11 +417,12 @@ class ZerosTest(test.TestCase): d = array_ops.fill([2, 3], 12., name="fill") # Constructs a tensor of zeros of the same dimensions as "d". z = array_ops.zeros(array_ops.shape(d)) - out = z.eval() + out = self.evaluate(z) self.assertAllEqual(np_ans, out) self.assertShapeEqual(np_ans, d) self.assertShapeEqual(np_ans, z) + @test_util.run_deprecated_v1 def testDtype(self): with self.cached_session(): d = array_ops.fill([2, 3], 12., name="fill") @@ -420,13 +446,13 @@ class ZerosTest(test.TestCase): z = array_ops.zeros([2, 3], dtype=dtype) self.assertEqual(z.dtype, dtype) self.assertEqual([2, 3], z.get_shape()) - z_value = z.eval() + z_value = self.evaluate(z) self.assertFalse(np.any(z_value)) self.assertEqual((2, 3), z_value.shape) z = array_ops.zeros(array_ops.shape(d), dtype=dtype) self.assertEqual(z.dtype, dtype) self.assertEqual([2, 3], z.get_shape()) - z_value = z.eval() + z_value = self.evaluate(z) self.assertFalse(np.any(z_value)) self.assertEqual((2, 3), z_value.shape) @@ -465,6 +491,7 @@ class ZerosLikeTest(test.TestCase): self.assertFalse(np.any(z_value)) self.assertEqual((2, 3), z_value.shape) + @test_util.run_deprecated_v1 def testZerosLikeCPU(self): for dtype in [ dtypes_lib.half, dtypes_lib.float32, dtypes_lib.float64, @@ -475,6 +502,7 @@ class ZerosLikeTest(test.TestCase): self._compareZeros(dtype, fully_defined_shape=False, use_gpu=False) self._compareZeros(dtype, fully_defined_shape=True, use_gpu=False) + @test_util.run_deprecated_v1 def testZerosLikeGPU(self): for dtype in [ dtypes_lib.half, dtypes_lib.float32, dtypes_lib.float64, @@ -484,11 +512,13 @@ class ZerosLikeTest(test.TestCase): self._compareZeros(dtype, fully_defined_shape=False, use_gpu=True) self._compareZeros(dtype, fully_defined_shape=True, use_gpu=True) + @test_util.run_deprecated_v1 def testZerosLikePartialShape(self): d = array_ops.placeholder(dtypes_lib.float32, shape=[None, 4, None]) z = array_ops.zeros_like(d) self.assertEqual(d.get_shape().as_list(), z.get_shape().as_list()) + @test_util.run_deprecated_v1 def testZerosLikeDtype(self): # Make sure zeros_like works even for dtypes that cannot be cast between with self.cached_session(): @@ -502,6 +532,7 @@ class ZerosLikeTest(test.TestCase): self.assertEqual(y.shape, shape) self.assertAllEqual(y, np.zeros(shape, dtype=out_type)) + @test_util.run_deprecated_v1 def testZerosLikeVariant(self): # TODO(ebrevdo): Re-enable use_gpu=True once non-DMA Variant # copying between CPU and GPU is supported AND we register a @@ -538,7 +569,7 @@ class OnesTest(test.TestCase): with self.cached_session(): ret = array_ops.ones(shape) self.assertEqual(shape, ret.get_shape()) - return ret.eval() + return self.evaluate(ret) def testConst(self): self.assertTrue(np.array_equal(self._Ones([2, 3]), np.array([[1] * 3] * 2))) @@ -548,7 +579,7 @@ class OnesTest(test.TestCase): self.assertEqual(1, self._Ones(())) with self.cached_session(): scalar = array_ops.ones(constant_op.constant([], dtype=dtypes_lib.int32)) - self.assertEqual(1, scalar.eval()) + self.assertEqual(1, self.evaluate(scalar)) def testDynamicSizes(self): np_ans = np.array([[1] * 3] * 2) @@ -557,11 +588,12 @@ class OnesTest(test.TestCase): d = array_ops.fill([2, 3], 12., name="fill") # Constructs a tensor of ones of the same dimensions as "d". z = array_ops.ones(array_ops.shape(d)) - out = z.eval() + out = self.evaluate(z) self.assertAllEqual(np_ans, out) self.assertShapeEqual(np_ans, d) self.assertShapeEqual(np_ans, z) + @test_util.run_deprecated_v1 def testAutoPack(self): with self.cached_session(): h = array_ops.placeholder(dtypes_lib.int32, shape=[]) @@ -570,6 +602,7 @@ class OnesTest(test.TestCase): out = z.eval(feed_dict={h: 4, w: 16}) self.assertAllEqual(out, np.array([[1] * 16] * 4)) + @test_util.run_deprecated_v1 def testDtype(self): with self.cached_session(): d = array_ops.fill([2, 3], 12., name="fill") @@ -617,12 +650,13 @@ class OnesLikeTest(test.TestCase): z_var = array_ops.ones_like(d) # Test that the type is correct self.assertEqual(z_var.dtype, dtype) - z_value = z_var.eval() + z_value = self.evaluate(z_var) # Test that the value is correct self.assertTrue(np.array_equal(z_value, np.array([[1] * 3] * 2))) self.assertEqual([2, 3], z_var.get_shape()) + @test_util.run_deprecated_v1 def testOnesLikePartialShape(self): d = array_ops.placeholder(dtypes_lib.float32, shape=[None, 4, None]) z = array_ops.ones_like(d) @@ -634,7 +668,7 @@ class FillTest(test.TestCase): def _compare(self, dims, val, np_ans, use_gpu): with self.cached_session(use_gpu=use_gpu): tf_ans = array_ops.fill(dims, val, name="fill") - out = tf_ans.eval() + out = self.evaluate(tf_ans) self.assertAllClose(np_ans, out) # Fill does not set the shape. # self.assertShapeEqual(np_ans, tf_ans) @@ -667,12 +701,14 @@ class FillTest(test.TestCase): np_ans = np.array([[0.15 + 0.3j] * 3] * 2).astype(np.complex128) self._compareAll([2, 3], np_ans[0][0], np_ans) + @test_util.run_deprecated_v1 def testFillString(self): np_ans = np.array([[b"yolo"] * 3] * 2) with self.session(use_gpu=False): tf_ans = array_ops.fill([2, 3], np_ans[0][0], name="fill").eval() self.assertAllEqual(np_ans, tf_ans) + @test_util.run_deprecated_v1 def testFillNegative(self): with self.cached_session(): for shape in (-1,), (2, -1), (-1, 2), (-2), (-3): @@ -686,6 +722,7 @@ class FillTest(test.TestCase): with self.assertRaises(errors_impl.InvalidArgumentError): fill_t.eval({dims: shape}) + @test_util.run_deprecated_v1 def testShapeFunctionEdgeCases(self): # Non-vector dimensions. with self.assertRaises(ValueError): @@ -704,6 +741,7 @@ class FillTest(test.TestCase): dtypes_lib.int32, shape=()), 17], 1.0) self.assertEqual([None, 17], f.get_shape().as_list()) + @test_util.run_deprecated_v1 def testGradient(self): with self.cached_session(): in_v = constant_op.constant(5.0) @@ -716,6 +754,7 @@ class FillTest(test.TestCase): class PlaceholderTest(test.TestCase): + @test_util.run_deprecated_v1 def testDtype(self): with self.cached_session(): p = array_ops.placeholder(dtypes_lib.float32, shape=(10, 10), name="p") @@ -726,8 +765,9 @@ class PlaceholderTest(test.TestCase): with self.assertRaisesOpError( "must feed a value for placeholder tensor 'p' with dtype float"): - p_identity.eval() + self.evaluate(p_identity) + @test_util.run_deprecated_v1 def testShape(self): with self.cached_session(): p = array_ops.placeholder(dtypes_lib.float32, shape=(10, 10), name="p") @@ -739,12 +779,13 @@ class PlaceholderTest(test.TestCase): with self.assertRaisesOpError( "must feed a value for placeholder tensor 'p' with dtype float and " r"shape \[10,10\]"): - p_identity.eval() + self.evaluate(p_identity) with self.assertRaisesWithPredicateMatch( ValueError, lambda e: "Cannot feed value of shape" in str(e)): p_identity.eval(feed_dict={p: feed_array[:5, :5]}) + @test_util.run_deprecated_v1 def testUnknownShape(self): with self.cached_session(): p = array_ops.placeholder(dtypes_lib.float32, shape=None, name="p") @@ -757,12 +798,14 @@ class PlaceholderTest(test.TestCase): self.assertAllClose( p_identity.eval(feed_dict={p: feed_array}), feed_array) + @test_util.run_deprecated_v1 def testScalarShape(self): with self.cached_session(): p = array_ops.placeholder(dtypes_lib.float32, shape=[], name="p") p_identity = array_ops.identity(p) self.assertAllClose(p_identity.eval(feed_dict={p: 5}), 5) + @test_util.run_deprecated_v1 def testPartialShape(self): with self.cached_session(): p = array_ops.placeholder(dtypes_lib.float32, shape=[None, 3], name="p") @@ -775,6 +818,7 @@ class PlaceholderTest(test.TestCase): ValueError, lambda e: "Cannot feed value of shape" in str(e)): p_identity.eval(feed_dict={p: feed_array[:5, :2]}) + @test_util.run_deprecated_v1 def testPartialShapeWhenNotFed(self): with self.cached_session(): p = array_ops.placeholder(dtypes_lib.float32, shape=[None, 3], name="p") @@ -783,8 +827,9 @@ class PlaceholderTest(test.TestCase): # Should trigger an operator error, not a shape error. with self.assertRaisesOpError( "must feed a value for placeholder tensor 'p' with dtype float"): - p_identity.eval() + self.evaluate(p_identity) + @test_util.run_deprecated_v1 def testControlDependency(self): with self.cached_session(): p = array_ops.placeholder(dtypes_lib.int32, shape=[], name="p") @@ -794,10 +839,12 @@ class PlaceholderTest(test.TestCase): val = np.array(2).astype(np.int) self.assertEqual(10, d.eval(feed_dict={p: val})) + @test_util.run_deprecated_v1 def testBadShape(self): with self.assertRaises(ValueError): array_ops.placeholder(dtypes_lib.float32, shape=(-1, 10)) + @test_util.run_deprecated_v1 def testTensorStr(self): a = array_ops.placeholder(dtypes_lib.float32, shape=None, name="a") self.assertEqual(" dtype=float32>", repr(a)) @@ -813,6 +860,7 @@ class PlaceholderTest(test.TestCase): self.assertEqual( "", repr(c)) + @test_util.run_deprecated_v1 def testOldGraph(self): # Load graph generated from earlier version of TF where # placeholder shape was not set. @@ -892,36 +940,40 @@ versions { class PlaceholderWithDefaultTest(test.TestCase): + @test_util.run_deprecated_v1 def testFullShape(self): with self.session(force_gpu=test_util.is_gpu_available()): p = array_ops.placeholder_with_default([[2, 2], [2, 2]], shape=[2, 2]) a = array_ops.identity(p) - self.assertAllEqual([[2, 2], [2, 2]], a.eval()) + self.assertAllEqual([[2, 2], [2, 2]], self.evaluate(a)) self.assertAllEqual( [[3, 3], [3, 3]], a.eval(feed_dict={p: [[3, 3], [3, 3]]})) with self.assertRaises(ValueError): a.eval(feed_dict={p: [[6, 6, 6], [6, 6, 6]]}) + @test_util.run_deprecated_v1 def testPartialShape(self): with self.session(force_gpu=test_util.is_gpu_available()): p = array_ops.placeholder_with_default([1, 2, 3], shape=[None]) a = array_ops.identity(p) - self.assertAllEqual([1, 2, 3], a.eval()) + self.assertAllEqual([1, 2, 3], self.evaluate(a)) self.assertAllEqual([3, 37], a.eval(feed_dict={p: [3, 37]})) with self.assertRaises(ValueError): a.eval(feed_dict={p: [[2, 2], [2, 2]]}) + @test_util.run_deprecated_v1 def testNoShape(self): with self.session(force_gpu=test_util.is_gpu_available()): p = array_ops.placeholder_with_default([17], shape=None) a = array_ops.identity(p) - self.assertAllEqual([17], a.eval()) + self.assertAllEqual([17], self.evaluate(a)) self.assertAllEqual([3, 37], a.eval(feed_dict={p: [3, 37]})) self.assertAllEqual( [[3, 3], [3, 3]], a.eval(feed_dict={p: [[3, 3], [3, 3]]})) + @test_util.run_deprecated_v1 def testGradient(self): with self.session(force_gpu=test_util.is_gpu_available()): x = array_ops.placeholder(dtypes_lib.float32, [5, 7]) diff --git a/tensorflow/python/kernel_tests/control_flow_ops_py_test.py b/tensorflow/python/kernel_tests/control_flow_ops_py_test.py index 19b067e449..c80c95da84 100644 --- a/tensorflow/python/kernel_tests/control_flow_ops_py_test.py +++ b/tensorflow/python/kernel_tests/control_flow_ops_py_test.py @@ -139,8 +139,9 @@ class ControlFlowTest(test.TestCase): self.assertTrue(isinstance(v2, ops.Tensor)) variables.global_variables_initializer().run() - self.assertEqual(9, v2.eval()) + self.assertEqual(9, self.evaluate(v2)) + @test_util.run_deprecated_v1 def testRefEnter(self): with self.cached_session(): v = variables.VariableV1(7) @@ -152,8 +153,9 @@ class ControlFlowTest(test.TestCase): v2 = control_flow_ops.with_dependencies([op], enter_v) v3 = control_flow_ops.exit(v2) variables.global_variables_initializer().run() - self.assertEqual(9, v3.eval()) + self.assertEqual(9, self.evaluate(v3)) + @test_util.run_deprecated_v1 def testRefSwitch(self): with self.cached_session(): v = variables.VariableV1(7) @@ -162,7 +164,7 @@ class ControlFlowTest(test.TestCase): v1 = control_flow_ops._SwitchRefOrTensor(v._ref(), p) # pylint: disable=protected-access v2 = state_ops.assign(v1[1], 9) variables.global_variables_initializer().run() - self.assertEqual(9, v2.eval()) + self.assertEqual(9, self.evaluate(v2)) def testEnterMulExit(self): with self.cached_session(): @@ -173,9 +175,10 @@ class ControlFlowTest(test.TestCase): mul_op = math_ops.multiply(enter_data, enter_five) exit_op = control_flow_ops.exit(mul_op) - result = exit_op.eval() + result = self.evaluate(exit_op) self.assertAllEqual(np.array([x * 5 for x in [1, 2, 3, 4, 5, 6]]), result) + @test_util.run_deprecated_v1 def testEnterShapePropagation(self): with self.cached_session(): v = variables.Variable([0.0, 0.0], dtype=dtypes.float32) @@ -214,7 +217,7 @@ class ControlFlowTest(test.TestCase): with self.assertRaisesWithPredicateMatch( errors_impl.InvalidArgumentError, lambda e: "Retval[0] does not have value" in str(e)): - dead_branch.eval() + self.evaluate(dead_branch) def testSwitchMergeLess(self): with self.cached_session(): @@ -225,7 +228,7 @@ class ControlFlowTest(test.TestCase): switch_op = control_flow_ops.switch(data, less_op) merge_op = control_flow_ops.merge(switch_op)[0] - result = merge_op.eval() + result = self.evaluate(merge_op) self.assertAllEqual(np.arange(1, 7), result) def testSwitchMergeAddIdentity(self): @@ -238,7 +241,7 @@ class ControlFlowTest(test.TestCase): id_op = array_ops.identity(switch_op[1]) merge_op = control_flow_ops.merge([add_op, id_op])[0] - result = merge_op.eval() + result = self.evaluate(merge_op) self.assertAllEqual(np.array([x + 1 for x in [1, 2, 3, 4, 5, 6]]), result) def testSwitchMergeAddMul(self): @@ -252,7 +255,7 @@ class ControlFlowTest(test.TestCase): mul_op = math_ops.multiply(switch_op[1], five) merge_op = control_flow_ops.merge([add_op, mul_op])[0] - result = merge_op.eval() + result = self.evaluate(merge_op) self.assertAllEqual(np.array([x * 5 for x in [1, 2, 3, 4, 5, 6]]), result) def testLoop_false(self): @@ -269,9 +272,10 @@ class ControlFlowTest(test.TestCase): next_n = control_flow_ops.next_iteration(switch_n[0]) merge_n.op._update_input(1, next_n) - result = exit_n.eval() + result = self.evaluate(exit_n) self.assertAllEqual(10, result) + @test_util.run_deprecated_v1 def testLoop_1(self): with self.cached_session(): zero = constant_op.constant(0) @@ -295,7 +299,7 @@ class ControlFlowTest(test.TestCase): merge_i.op._update_input(1, next_i) exit_i = control_flow_ops.exit(switch_i[0]) - result = exit_i.eval() + result = self.evaluate(exit_i) self.assertAllEqual(10, result) def testLoop_2(self): @@ -321,7 +325,7 @@ class ControlFlowTest(test.TestCase): merge_i.op._update_input(1, next_i) exit_i = control_flow_ops.exit(switch_i[0]) - result = exit_i.eval() + result = self.evaluate(exit_i) self.assertAllEqual(10, result) def testDifferentFrame(self): @@ -333,6 +337,7 @@ class ControlFlowTest(test.TestCase): with self.assertRaisesOpError("has inputs from different frames"): res.eval(feed_dict={data: 1.0}) + @test_util.run_deprecated_v1 def testCondBool(self): values = constant_op.constant(10) fn1 = lambda: math_ops.add(values, 1) @@ -340,6 +345,7 @@ class ControlFlowTest(test.TestCase): with self.assertRaisesRegexp(TypeError, "must not be a Python bool"): _ = control_flow_ops.cond(False, fn1, fn2) + @test_util.run_deprecated_v1 def testCondInt(self): p = array_ops.placeholder(dtypes.bool, shape=[]) v = constant_op.constant(10) @@ -389,7 +395,6 @@ class ControlFlowTest(test.TestCase): with self.assertRaisesRegexp(ValueError, "may not be fed"): sess.run(r, feed_dict={t: 3}) - @test_util.disable_control_flow_v2("b/113296180 (IndexedSlices)") def testCondIndexedSlices(self): with self.cached_session(): values = constant_op.constant(10) @@ -405,7 +410,6 @@ class ControlFlowTest(test.TestCase): self.assertAllEqual(11, val) self.assertAllEqual(0, ind) - @test_util.disable_control_flow_v2("b/113296161 (SparseTensors)") def testCondSparseTensor(self): with self.cached_session(): values = constant_op.constant([2.0, 4.0], name="values") @@ -437,6 +441,19 @@ class ControlFlowTest(test.TestCase): self.assertEqual(1.0, control_flow_ops.cond(rv, case, lambda: t).eval()) + def testCondWithTensorArrayGrad(self): + with self.cached_session() as sess: + with ops.device(test.gpu_device_name()): + pred = array_ops.placeholder(dtypes.bool, []) + x = constant_op.constant([1.0, 2.0, 3.0]) + y = control_flow_ops.cond( + pred, lambda: functional_ops.map_fn(lambda z: z * 2.0, x), + lambda: constant_op.constant([1.0, 1.0, 1.0])) + g = gradients_impl.gradients(y, x)[0] + + self.assertAllEqual(sess.run(g, {pred: True}), [2.0, 2.0, 2.0]) + self.assertAllEqual(sess.run(g, {pred: False}), [0.0, 0.0, 0.0]) + @test_util.disable_control_flow_v2("b/113293074") def testCondIndexedSlicesDifferentTypes(self): with self.cached_session(): @@ -478,7 +495,7 @@ class ControlFlowTest(test.TestCase): fn2 = lambda: math_ops.subtract(x, 1) r = control_flow_ops.cond(pred, fn1, fn2) - result = r.eval() + result = self.evaluate(r) self.assertAllEqual(11, result) def testCond_1(self): @@ -494,7 +511,7 @@ class ControlFlowTest(test.TestCase): r = control_flow_ops.cond( math_ops.less(1, 0), lambda: math_ops.add(x, 1), lambda: math_ops.subtract(x, 1)) - result = r.eval() + result = self.evaluate(r) self.assertAllEqual(9, result) def testCond_3(self): @@ -507,7 +524,7 @@ class ControlFlowTest(test.TestCase): fn3 = lambda: math_ops.add(control_flow_ops.cond(pred, fn1, fn2), 1) r = control_flow_ops.cond(pred, fn3, fn2) - result = r.eval() + result = self.evaluate(r) self.assertAllEqual(12, result) @test_util.run_in_graph_and_eager_modes @@ -534,9 +551,9 @@ class ControlFlowTest(test.TestCase): result = f().eval() self.assertEqual(True, result) # Only second cond result was fetched, so v1 assign shouldn't run. - self.assertEqual(7, v1.eval()) - self.assertEqual(2, v2.eval()) - self.assertEqual(7, v3.eval()) + self.assertEqual(7, self.evaluate(v1)) + self.assertEqual(2, self.evaluate(v2)) + self.assertEqual(7, self.evaluate(v3)) result = f_defun() self.assertEqual(True, self.evaluate(result)) @@ -557,10 +574,9 @@ class ControlFlowTest(test.TestCase): for i in range(10): alive, count = body(i) - self.assertAllEqual(4, count.eval()) + self.assertAllEqual(4, self.evaluate(count)) def testCond_6(self): - with self.cached_session(): v1 = variables.Variable([7]) @@ -571,7 +587,7 @@ class ControlFlowTest(test.TestCase): r = control_flow_ops.cond(pred, fn1, fn2) variables.global_variables_initializer().run() - result = r.eval() + result = self.evaluate(r) self.assertAllEqual(np.array([7]), result) def testCond_7(self): @@ -582,8 +598,89 @@ class ControlFlowTest(test.TestCase): fn1 = lambda: [math_ops.add(x, 1), math_ops.add(x, 2)] fn2 = lambda: [y, y] r = control_flow_ops.cond(pred, fn1, fn2) - self.assertAllEqual([11, 12], sess.run(r)) + self.assertAllEqual([11, 12], self.evaluate(r)) + + def testCondListOutput(self): + with self.cached_session() as sess: + x = constant_op.constant(10) + y = constant_op.constant(200) + pred = math_ops.less(1, 2) + fn1 = lambda: [math_ops.add(x, y), math_ops.add(x, y)] + fn2 = lambda: [y, y] + r = control_flow_ops.cond(pred, fn1, fn2) + test_result = self.evaluate(r) + self.assertListEqual([210, 210], test_result) + + def testTupleOutput(self): + with self.cached_session() as sess: + x = constant_op.constant(10) + y = constant_op.constant(200) + pred = math_ops.less(1, 2) + fn1 = lambda: (math_ops.add(x, y), math_ops.add(x, y)) + fn2 = lambda: (y, y) + r = control_flow_ops.cond(pred, fn1, fn2) + test_result = self.evaluate(r) + self.assertTupleEqual((210, 210), test_result) + + def testDictOutput(self): + with self.cached_session() as sess: + x = constant_op.constant(10) + y = constant_op.constant(200) + pred = math_ops.less(1, 2) + fn1 = lambda: {"a": math_ops.add(x, y), "b": math_ops.add(x, y)} + fn2 = lambda: {"a": y, "b": y} + r = control_flow_ops.cond(pred, fn1, fn2) + test_result = self.evaluate(r) + self.assertDictEqual({"a": 210, "b": 210}, test_result) + + @test_util.run_deprecated_v1 + def testEmbeddedListOutput(self): + with self.cached_session() as sess: + x = constant_op.constant(10) + y = constant_op.constant(200) + pred = math_ops.less(1, 2) + fn1 = lambda: [[math_ops.add(x, y), math_ops.add(x, y)]] + fn2 = lambda: [[y, y]] + # Pass strict=True flag as cond_v2 allows for tensors to be + # in nested output structures as singletons + r = control_flow_ops.cond(pred, fn1, fn2, strict=True) + test_result = self.evaluate(r) + self.assertListEqual([[210, 210]], test_result) + + def testEmbeddedTupleOutput(self): + with self.cached_session() as sess: + x = constant_op.constant(10) + y = constant_op.constant(200) + pred = math_ops.less(1, 2) + fn1 = lambda: ((math_ops.add(x, y), math_ops.add(x, y))) + fn2 = lambda: ((y, y)) + r = control_flow_ops.cond(pred, fn1, fn2) + test_result = self.evaluate(r) + self.assertTupleEqual(((210, 210)), test_result) + + def testEmbeddedDictOutput(self): + with self.cached_session() as sess: + x = constant_op.constant(10) + y = constant_op.constant(200) + pred = math_ops.less(1, 2) + fn1 = lambda: {"a": {"c": math_ops.add(x, y)}, "b": {"d": math_ops.add(x, y)}} + fn2 = lambda: {"a": {"c": y}, "b": {"d": y}} + r = control_flow_ops.cond(pred, fn1, fn2) + test_result = self.evaluate(r) + self.assertDictEqual({"a": {"c": 210}, "b": {"d": 210}}, test_result) + + def testCheckNestedOutputStruct(self): + with self.cached_session() as sess: + x = constant_op.constant(10) + y = constant_op.constant(200) + pred = math_ops.less(1, 2) + fn1 = lambda: {"a": math_ops.add(x, y), "b": math_ops.add(x, y)} + fn2 = lambda: {"c": y, "d": y} + with self.assertRaisesRegexp(ValueError, "The two structures don't have the same nested structure"): + r = control_flow_ops.cond(pred, fn1, fn2) + self.evaluate(r) + @test_util.run_deprecated_v1 def testCondRef(self): with self.cached_session(): @@ -596,9 +693,10 @@ class ControlFlowTest(test.TestCase): true_fn = lambda: x false_fn = lambda: constant_op.constant([2.0]) r = control_flow_ops.cond(constant_op.constant(False), true_fn, false_fn) - self.assertAllEqual([2.0], r.eval()) + self.assertAllEqual([2.0], self.evaluate(r)) @test_util.disable_control_flow_v2("b/79881896 (control deps)") + @test_util.run_deprecated_v1 def testCondWithControl(self): with self.cached_session(): control_holder = array_ops.placeholder(dtypes.float32, shape=()) @@ -612,7 +710,7 @@ class ControlFlowTest(test.TestCase): r = control_flow_ops.cond( constant_op.constant(True), true_branch, lambda: constant_op.constant(1)) - self.assertEqual(5, r.eval()) + self.assertEqual(5, self.evaluate(r)) def testUninitializedRefIdentity(self): with self.cached_session() as sess: @@ -636,7 +734,7 @@ class ControlFlowTest(test.TestCase): with ops.control_dependencies([v_t_op]): orig_v = array_ops.identity(v) merged_op = control_flow_ops.merge([assign_v, orig_v]) - self.assertAllEqual([1.0], sess.run(merged_op.output)) + self.assertAllEqual([1.0], self.evaluate(merged_op.output)) def testCondSwitchIdentity(self): # Make sure the recv identity is not removed by optimization. @@ -650,7 +748,7 @@ class ControlFlowTest(test.TestCase): return control_flow_ops.Assert(False, ["Wrong branch!!!"]) r = control_flow_ops.cond(pred, fn1, fn2) - sess.run(r) + self.evaluate(r) def testCondRecvIdentity(self): # Make sure the switch identity is not removed by optimization. @@ -666,7 +764,7 @@ class ControlFlowTest(test.TestCase): return control_flow_ops.Assert(False, ["Wrong branch!!!"]) r = control_flow_ops.cond(pred, fn1, fn2) - sess.run(r) + self.evaluate(r) def testCondGrad_1(self): with self.cached_session(): @@ -677,8 +775,9 @@ class ControlFlowTest(test.TestCase): r = control_flow_ops.cond(pred, fn1, fn2) grad = gradients_impl.gradients(r, [x])[0] - self.assertAllEqual(1.0, grad.eval()) + self.assertAllEqual(1.0, self.evaluate(grad)) + @test_util.run_deprecated_v1 def testCondGrad_2(self): with self.cached_session(): c = array_ops.placeholder(dtypes.int32, shape=[]) @@ -694,6 +793,7 @@ class ControlFlowTest(test.TestCase): @test_util.disable_control_flow_v2( "b/110550782 (gradient w.r.t external variable)") + @test_util.run_deprecated_v1 def testCondGrad_3(self): with self.cached_session(): c = array_ops.placeholder(dtypes.int32, shape=[]) @@ -711,6 +811,35 @@ class ControlFlowTest(test.TestCase): self.assertAllEqual(980.0, r.eval(feed_dict={c: 1})) self.assertAllEqual(30.0, r.eval(feed_dict={c: 3})) + @test_util.run_deprecated_v1 + def testCondGradMultiDevice(self): + config = config_pb2.ConfigProto(device_count={"CPU": 2}, + allow_soft_placement=True) + with self.cached_session(use_gpu=True, config=config) as sess: + pred = array_ops.placeholder(dtypes.bool, []) + x = array_ops.placeholder(dtypes.float32) + y = array_ops.placeholder(dtypes.float32) + + with ops.device("/cpu:0"): + z = control_flow_ops.cond(pred, lambda: x * y * 2.0, lambda: 2.0) + + with ops.device("/cpu:1"): + grad = gradients_impl.gradients(z, x)[0] + + self.assertEqual(sess.run(grad, {pred: True, x: 1.0, y: 2.0}), 4.0) + self.assertEqual(sess.run(grad, {pred: False, x: 1.0, y: 2.0}), 0.0) + + with ops.device("/cpu:0"): + grad_grad = gradients_impl.gradients(grad, x)[0] + + # v1 control flow gets None second derivative for some reason. + if not control_flow_ops.ENABLE_COND_V2: + self.assertIsNone(grad_grad) + return + + self.assertEqual(sess.run(grad_grad, {pred: True, x: 1.0, y: 2.0}), 0.0) + self.assertEqual(sess.run(grad_grad, {pred: False, x: 1.0, y: 2.0}), 0.0) + def testNestedCond_Simple(self): with self.cached_session(): x = constant_op.constant(0., name="X") @@ -718,15 +847,16 @@ class ControlFlowTest(test.TestCase): constant_op.constant(True), lambda: x, lambda: control_flow_ops.cond(x < 1., lambda: x, lambda: x)) result = gradients_impl.gradients(y, x)[0] - self.assertEqual(1.0, result.eval()) + self.assertEqual(1.0, self.evaluate(result)) z = control_flow_ops.cond( constant_op.constant(False), lambda: x, lambda: control_flow_ops.cond(x < 1., lambda: x, lambda: x)) result = gradients_impl.gradients(z, x)[0] - self.assertEqual(1.0, result.eval()) + self.assertEqual(1.0, self.evaluate(result)) @test_util.disable_control_flow_v2("b/113327884") + @test_util.run_deprecated_v1 def testCondGrad_Gather(self): with self.cached_session() as sess: v1 = variables.Variable([1.0, 42.0]) @@ -740,16 +870,26 @@ class ControlFlowTest(test.TestCase): # Should just be [1, 1], but possibly a sparse representation gv, gi = sess.run([grad.values, grad.indices], feed_dict={c: 1}) dense_gv = [ - sum([y for (x, y) in zip(gi, gv) if x == i]) for i in range(2) + sum(y for (x, y) in zip(gi, gv) if x == i) for i in range(2) ] self.assertAllEqual(dense_gv, [1.0, 1.0]) # Should be [0, 2], as the else forwards v1[1] twice gv, gi = sess.run([grad.values, grad.indices], feed_dict={c: 3}) dense_gv = [ - sum([y for (x, y) in zip(gi, gv) if x == i]) for i in range(2) + sum(y for (x, y) in zip(gi, gv) if x == i) for i in range(2) ] self.assertAllEqual(dense_gv, [0.0, 2.0]) + def testCondPredicateTensor(self): + """Regression test for lowering predicate from non-first output of an op.""" + + @eager_function.defun + def foo(): + return constant_op.constant("foo"), constant_op.constant(True) + + r = control_flow_ops.cond(foo()[1], lambda: 1.0, lambda: 2.0) + self.assertEqual(self.evaluate(r), 1.0) + # TODO(b/117945658): reenable @test_util.run_in_graph_and_eager_modes def DISABLED_testCondAutoControlDeps(self): @@ -863,7 +1003,7 @@ class ControlFlowTest(test.TestCase): c = lambda x: math_ops.less(x, 10000) b = lambda x: math_ops.add(x, 1) r = control_flow_ops.while_loop(c, b, [n], parallel_iterations=20) - self.assertEqual(10000, r.eval()) + self.assertEqual(10000, self.evaluate(r)) @test_util.disable_control_flow_v2("b/79881896 (control deps)") def testWhileExternalControlDependencies(self): @@ -894,10 +1034,11 @@ class ControlFlowTest(test.TestCase): result = control_flow_ops.while_loop(cond=lambda i: i < 5, body=body_fn, loop_vars=[0]) - result.eval() + self.evaluate(result) self.assertAllEqual(v.eval(), 1.0) @test_util.disable_control_flow_v2("b/113324949 (RefVariable)") + @test_util.run_deprecated_v1 def testWhileWithRefs_1(self): with self.cached_session() as sess: x = variables.VariableV1(0)._ref() # pylint: disable=protected-access @@ -917,7 +1058,7 @@ class ControlFlowTest(test.TestCase): self.assertEqual(r[0].dtype, dtypes.int32) self.assertEqual(r[1].dtype, dtypes.int32_ref) - value_i, value_x = sess.run(r) + value_i, value_x = self.evaluate(r) self.assertEqual(100, value_i) self.assertEqual(0, value_x) @@ -926,19 +1067,19 @@ class ControlFlowTest(test.TestCase): with self.cached_session(): s = constant_op.constant(0) r = isum(s) - self.assertAllEqual(45, r.eval()) + self.assertAllEqual(45, self.evaluate(r)) def testWhileWithMaximumIterations(self): with self.cached_session(): s = constant_op.constant([1, 2, 3, 4, 5]) r = isum(s, maximum_iterations=3) - self.assertAllEqual([1 + 3, 2 + 3, 3 + 3, 4 + 3, 5 + 3], r.eval()) + self.assertAllEqual([1 + 3, 2 + 3, 3 + 3, 4 + 3, 5 + 3], self.evaluate(r)) def testWhileWithMaximumIterationsAndSingleArgument(self): with self.cached_session(): r = control_flow_ops.while_loop( lambda i: i < 3, lambda i: i + 1, [0], maximum_iterations=1) - self.assertEqual(1, r.eval()) + self.assertEqual(1, self.evaluate(r)) @test_util.disable_control_flow_v2("b/115776323 (max_iters)") def testSingleNestedMaximumIterationsWhileLoopGradientInXLAContext(self): @@ -1137,6 +1278,7 @@ class ControlFlowTest(test.TestCase): # Have more than 10 parallel iterations and hence exercise k-bound # most of the time. + @test_util.run_deprecated_v1 def testWhile_3(self): with self.cached_session(): @@ -1157,6 +1299,7 @@ class ControlFlowTest(test.TestCase): result = r[3].eval() self.assertAllEqual(10100, result) + @test_util.run_deprecated_v1 def testWhile_4(self): with self.cached_session(): @@ -1231,7 +1374,7 @@ class ControlFlowTest(test.TestCase): c = lambda x: math_ops.less(x, 10.0) b = lambda x: math_ops.add(x, 1.0) r = control_flow_ops.while_loop(c, b, [n]) - self.assertAllClose(10.0, r.eval()) + self.assertAllClose(10.0, self.evaluate(r)) def testWhile_Gpu_1(self): self._testWhile_Gpu_1(use_gpu=False) @@ -1247,7 +1390,7 @@ class ControlFlowTest(test.TestCase): return math_ops.add(x, 1.0) r = control_flow_ops.while_loop(c, b, [n]) - self.assertAllClose(10.0, r.eval()) + self.assertAllClose(10.0, self.evaluate(r)) def testWhile_Gpu_2(self): self._testWhile_Gpu_2(use_gpu=False) @@ -1268,15 +1411,16 @@ class ControlFlowTest(test.TestCase): c, _b, [i, m], [i.get_shape(), tensor_shape.unknown_shape()]) r = r[1] * array_ops.ones([8, 8]) - self.assertAllEqual(np.ones((8, 8)), r.eval()) + self.assertAllEqual(np.ones((8, 8)), self.evaluate(r)) + @test_util.run_deprecated_v1 def testWhileWithNonTensorInput_Scalar(self): with self.cached_session(): n = 0 c = lambda x: x < 10000 b = lambda x: x + 1 r = control_flow_ops.while_loop(c, b, [n], parallel_iterations=20) - self.assertEqual(10000, r.eval()) + self.assertEqual(10000, self.evaluate(r)) def testWhileWithNonTensorInput_Vector(self): with self.cached_session(): @@ -1284,7 +1428,7 @@ class ControlFlowTest(test.TestCase): c = lambda x: x[0] < 10000 b = lambda x: array_ops.stack([x[0] + 1]) r = control_flow_ops.while_loop(c, b, [n], parallel_iterations=20) - self.assertEqual([10000], r.eval()) + self.assertEqual([10000], self.evaluate(r)) def testWhileShapeInference(self): with self.cached_session(): @@ -1344,6 +1488,7 @@ class ControlFlowTest(test.TestCase): [i.get_shape(), tensor_shape.TensorShape([5])]) @test_util.disable_control_flow_v2("b/116282023 (IndexedSlices)") + @test_util.run_deprecated_v1 def testWhileShapeInferenceIndexedSlices(self): with self.cached_session(): values = constant_op.constant([[2.0, 4.0], [3.0, 5.0]], name="values") @@ -1396,7 +1541,7 @@ class ControlFlowTest(test.TestCase): c = lambda x: math_ops.less(x, 200) b = lambda x: math_ops.add(x, cpu_sum(n)) r = control_flow_ops.while_loop(c, b, [n]) - self.assertEqual(225, r.eval()) + self.assertEqual(225, self.evaluate(r)) def testNestedWhile_1(self): self._testNestedWhile_1(use_gpu=False) @@ -1428,7 +1573,7 @@ class ControlFlowTest(test.TestCase): r = control_flow_ops.while_loop( outer_c, outer_b, [s0], parallel_iterations=1) - self.assertEqual(1048576.0, r.eval()) + self.assertEqual(1048576.0, self.evaluate(r)) def testNestedWhile_2(self): self._testNestedWhile_2(use_gpu=False) @@ -1450,6 +1595,7 @@ class ControlFlowTest(test.TestCase): condition, body, [n, r], parallel_iterations=1) self.assertAllEqual(12, res[1].eval()) + @test_util.run_deprecated_v1 def testWhileWithControl_2(self): with self.cached_session(): r = constant_op.constant(0) @@ -1462,7 +1608,7 @@ class ControlFlowTest(test.TestCase): res = control_flow_ops.while_loop( condition, body, [r], parallel_iterations=1) - self.assertAllEqual(12, res.eval()) + self.assertAllEqual(12, self.evaluate(res)) def testWhileWithControl_3(self): with self.cached_session() as sess: @@ -1509,7 +1655,7 @@ class ControlFlowTest(test.TestCase): with ops.control_dependencies([control_flow_ops.no_op()]): loop = control_flow_ops.while_loop(cond, body, (constant_op.constant(5),)) - self.assertEqual(0, sess.run(loop)) + self.assertEqual(0, self.evaluate(loop)) @test_util.disable_control_flow_v2("b/113324949 (ref vars)") def testWhileCondWithControl_1(self): @@ -1531,8 +1677,8 @@ class ControlFlowTest(test.TestCase): r = control_flow_ops.while_loop(loop_condition, loop_body, (i0,)) variables.global_variables_initializer().run() - self.assertEqual(4, r.eval()) - self.assertAllClose(65536.0, v.eval()) + self.assertEqual(4, self.evaluate(r)) + self.assertAllClose(65536.0, self.evaluate(v)) @test_util.disable_control_flow_v2("b/113324949 (ref vars)") def testWhileCondExitControl(self): @@ -1556,8 +1702,8 @@ class ControlFlowTest(test.TestCase): constant_op.constant(False), lambda: constant_op.constant(1.0), false_branch) variables.global_variables_initializer().run() - self.assertEqual(6.0, r.eval()) - self.assertEqual(99, v.eval()) + self.assertEqual(6.0, self.evaluate(r)) + self.assertEqual(99, self.evaluate(v)) def testCondWhile_1(self): @@ -1568,7 +1714,7 @@ class ControlFlowTest(test.TestCase): r = control_flow_ops.cond( math_ops.less(0, 1), lambda: control_flow_ops.while_loop(c, b, [n]), lambda: n) - self.assertAllEqual(10, r.eval()) + self.assertAllEqual(10, self.evaluate(r)) def testCondWhile_2(self): @@ -1579,7 +1725,7 @@ class ControlFlowTest(test.TestCase): r = control_flow_ops.cond( math_ops.less(1, 0), lambda: math_ops.add(n, 1), lambda: control_flow_ops.while_loop(c, b, [n])) - self.assertAllEqual(10, r.eval()) + self.assertAllEqual(10, self.evaluate(r)) def _testCondWhile_3(self, use_gpu): with self.cached_session(use_gpu=use_gpu) as sess: @@ -1604,6 +1750,7 @@ class ControlFlowTest(test.TestCase): self.assertEqual([2.0], sess.run(r1, {p: False})) @test_util.disable_control_flow_v2("b/116743589") + @test_util.run_deprecated_v1 def testCondWhile_3(self): self._testCondWhile_3(use_gpu=False) self._testCondWhile_3(use_gpu=True) @@ -1622,7 +1769,7 @@ class ControlFlowTest(test.TestCase): lambda: math_ops.add(x, one), lambda: math_ops.subtract(x, one)) # pylint: enable=undefined-variable r = control_flow_ops.while_loop(c, b, [i]) - self.assertAllEqual(10, r.eval()) + self.assertAllEqual(10, self.evaluate(r)) def testWhileCond_2(self): @@ -1631,7 +1778,7 @@ class ControlFlowTest(test.TestCase): c = lambda x: math_ops.less(x, 10) b = lambda x: control_flow_ops.cond(constant_op.constant(True), lambda: math_ops.add(x, 1), lambda: n) r = control_flow_ops.while_loop(c, b, [n]) - self.assertAllEqual(10, r.eval()) + self.assertAllEqual(10, self.evaluate(r)) def testWhileCond_3(self): @@ -1645,10 +1792,41 @@ class ControlFlowTest(test.TestCase): lambda: math_ops.subtract(x, 1)) # pylint: enable=undefined-variable r = control_flow_ops.while_loop(c, b, [n]) - self.assertAllEqual(10, r.eval()) + self.assertAllEqual(10, self.evaluate(r)) + + @test_util.run_deprecated_v1 + def testWhileCondGradMultiDevice(self): + config = config_pb2.ConfigProto(device_count={"CPU": 2}, + allow_soft_placement=True) + with self.cached_session(use_gpu=True, config=config) as sess: + pred = array_ops.placeholder(dtypes.bool, []) + x_init = constant_op.constant(1.0) + + with ops.device("/cpu:0"): + z = control_flow_ops.while_loop( + lambda i, _: i < 3, + lambda i, x: (i + 1, control_flow_ops.cond( + pred, lambda: x * 2.0, lambda: 10.0)), + [0, x_init]) + + with ops.device("/cpu:1"): + grad = gradients_impl.gradients(z, x_init)[0] + + self.assertEqual(sess.run(grad, {pred: True}), 8.0) + self.assertEqual(sess.run(grad, {pred: False}), 0.0) + + if not control_flow_ops.ENABLE_WHILE_V2: + return + + with ops.device("/cpu:0"): + grad_grad = gradients_impl.gradients(grad, x_init)[0] + + self.assertEqual(sess.run(grad_grad, {pred: True}), 0.0) + self.assertEqual(sess.run(grad_grad, {pred: False}), 0.0) # NOTE: It is ok to have parallel_iterations > 1 @test_util.disable_control_flow_v2("b/113324949 (RefVariable)") + @test_util.run_deprecated_v1 def testWhileUpdateVariable_1(self): with self.cached_session(): select = variables.Variable([3.0, 4.0, 5.0]) @@ -1667,8 +1845,8 @@ class ControlFlowTest(test.TestCase): r = control_flow_ops.while_loop( loop_iterator, loop_body, [n], parallel_iterations=1) variables.global_variables_initializer().run() - self.assertEqual(3, r.eval()) - result = select.eval() + self.assertEqual(3, self.evaluate(r)) + result = self.evaluate(select) self.assertAllClose(np.array([10.0, 10.0, 10.0]), result) @test_util.disable_control_flow_v2("b/113324949 (RefVariable)") @@ -1692,13 +1870,14 @@ class ControlFlowTest(test.TestCase): r = control_flow_ops.while_loop( loop_iterator, loop_body, [n], parallel_iterations=1) variables.global_variables_initializer().run() - self.assertEqual(3, r.eval()) - result1 = select1.eval() + self.assertEqual(3, self.evaluate(r)) + result1 = self.evaluate(select1) self.assertAllClose(np.array([10.0, 10.0, 10.0]), result1) - result2 = select2.eval() + result2 = self.evaluate(select2) self.assertAllClose(np.array([10.0, 10.0, 10.0]), result2) @test_util.disable_control_flow_v2("b/113324949 (RefVariable)") + @test_util.run_deprecated_v1 def testWhileUpdateVariable_3(self): with self.cached_session(): select = variables.Variable([3.0, 4.0, 5.0]) @@ -1721,6 +1900,7 @@ class ControlFlowTest(test.TestCase): self.assertAllClose(np.array([10.0, 10.0, 10.0]), result) @test_util.disable_control_flow_v2("b/113324949 (RefVariable)") + @test_util.run_deprecated_v1 def testWhileUpdateVariable_4(self): with self.cached_session(): var_a = variables.Variable(0, name="a") @@ -1744,11 +1924,12 @@ class ControlFlowTest(test.TestCase): lpa = control_flow_ops.while_loop( pred, loop_body, [c], parallel_iterations=1) - self.assertEqual(0, var_b.eval()) - lpa.eval() # Run the loop - self.assertEqual(10, var_b.eval()) + self.assertEqual(0, self.evaluate(var_b)) + self.evaluate(lpa) # Run the loop + self.assertEqual(10, self.evaluate(var_b)) @test_util.disable_control_flow_v2("b/113324949 (RefVariable)") + @test_util.run_deprecated_v1 def testWhileUpdateVariable_5(self): with self.cached_session(): # Create some variables. @@ -1773,10 +1954,10 @@ class ControlFlowTest(test.TestCase): lpa = control_flow_ops.while_loop( pred, loop_body, [var_b], parallel_iterations=1, name="loop") - self.assertEqual(0, var_b.eval()) - lpa.eval() # Run the loop - self.assertEqual(10, var_a.eval()) - self.assertEqual(10, var_b.eval()) + self.assertEqual(0, self.evaluate(var_b)) + self.evaluate(lpa) # Run the loop + self.assertEqual(10, self.evaluate(var_a)) + self.assertEqual(10, self.evaluate(var_b)) @test_util.disable_control_flow_v2("b/113324949 (RefVariable)") def testWhileUpdateVariable_6(self): @@ -1803,10 +1984,10 @@ class ControlFlowTest(test.TestCase): lpa = control_flow_ops.while_loop( pred, loop_body, [c], parallel_iterations=1, name="loop") - self.assertEqual(0, var_b.eval()) - lpa.eval() # Run the loop - self.assertEqual(55, var_b.eval()) - self.assertEqual(10, var_a.eval()) + self.assertEqual(0, self.evaluate(var_b)) + self.evaluate(lpa) # Run the loop + self.assertEqual(55, self.evaluate(var_b)) + self.assertEqual(10, self.evaluate(var_a)) def testWhileQueue_1(self): with self.cached_session(): @@ -1822,7 +2003,7 @@ class ControlFlowTest(test.TestCase): return ni r = control_flow_ops.while_loop(c, b, [i], parallel_iterations=1) - self.assertEqual([10], r.eval()) + self.assertEqual([10], self.evaluate(r)) for i in xrange(10): self.assertEqual([i], q.dequeue().eval()) @@ -1858,7 +2039,7 @@ class ControlFlowTest(test.TestCase): b1, [r, x], [r.get_shape(), tensor_shape.unknown_shape()], parallel_iterations=1) - self.assertEqual(45, rx.eval()) + self.assertEqual(45, self.evaluate(rx)) def _testWhileGrad_ColocateGradients(self, colocate): gpu_dev_name = test.gpu_device_name() if test.is_gpu_available( @@ -1893,7 +2074,7 @@ class ControlFlowTest(test.TestCase): self.assertFalse(gpu_dev_name in dev) with self.session(graph=graph) as sess: - self.assertAllClose(1024.0, sess.run(r)) + self.assertAllClose(1024.0, self.evaluate(r)) @test_util.disable_control_flow_v2("b/116351701 (colocation)") def testWhileGrad_ColocateGradients(self): @@ -1909,7 +2090,7 @@ class ControlFlowTest(test.TestCase): r = control_flow_ops.cond(math_ops.less(1, 2), lambda: r, lambda: v) r = gradients_impl.gradients(r, v)[0] - self.assertAllClose(1024.0, r.eval()) + self.assertAllClose(1024.0, self.evaluate(r)) def testWhileGrad_Shape(self): with self.cached_session(): @@ -1928,6 +2109,7 @@ class ControlFlowTest(test.TestCase): self.assertEqual([None], r.get_shape().as_list()) self.assertAllClose([810.0, 2560.0], r.eval(feed_dict={x: [3.0, 4.0]})) + @test_util.run_deprecated_v1 def testWhileGrad_BaseShape(self): with self.cached_session() as sess: x = array_ops.placeholder(dtypes.float32, [None]) @@ -1949,7 +2131,7 @@ class ControlFlowTest(test.TestCase): r = math_ops.multiply(r, r) r = gradients_impl.gradients(r, v)[0] - self.assertEqual(524288.0, r.eval()) + self.assertEqual(524288.0, self.evaluate(r)) def testWhileGrad_LoopAdd(self): with self.cached_session(): @@ -1960,7 +2142,7 @@ class ControlFlowTest(test.TestCase): r = math_ops.add(r, r) r = gradients_impl.gradients(r, v)[0] - self.assertAllClose(2048.0, r.eval()) + self.assertAllClose(2048.0, self.evaluate(r)) def _testWhileGrad_Mul(self, use_gpu, p_iters): with self.cached_session(use_gpu=use_gpu) as sess: @@ -1971,11 +2153,12 @@ class ControlFlowTest(test.TestCase): r = control_flow_ops.while_loop(c, b, [v], parallel_iterations=p_iters) grad_a, grad_v = gradients_impl.gradients(r, [a, v]) - grad_a_val, grad_v_val = sess.run([grad_a, grad_v]) + grad_a_val, grad_v_val = self.evaluate([grad_a, grad_v]) self.assertAllClose(216.0, grad_a_val) self.assertAllClose(81.0, grad_v_val) @test_util.disable_control_flow_v2("b/116630618 (parallel_iters: times out)") + @test_util.run_deprecated_v1 def testWhileGrad_Mul(self): self._testWhileGrad_Mul(use_gpu=False, p_iters=1) self._testWhileGrad_Mul(use_gpu=False, p_iters=10) @@ -2003,14 +2186,13 @@ class ControlFlowTest(test.TestCase): r = control_flow_ops.while_loop(c, b, [v]) r = gradients_impl.gradients(r, v)[0] - self.assertAllClose(512.0, r.eval()) + self.assertAllClose(512.0, self.evaluate(r)) + @test_util.run_deprecated_v1 def testNestedWhileCondWhileGrad(self): - if control_flow_ops.ENABLE_WHILE_V2 and test_util.is_gpu_available(): - self.skipTest("b/118459209") self._testNestedWhileCondWhileGrad(use_gpu=False) - @test_util.disable_control_flow_v2("b/118459209") + @test_util.run_deprecated_v1 def testNestedWhileCondWhileGradGpu(self): self._testNestedWhileCondWhileGrad(use_gpu=True) @@ -2026,6 +2208,7 @@ class ControlFlowTest(test.TestCase): variables.global_variables_initializer().run() self.assertAllClose(216.0, r[0].eval()) + @test_util.run_deprecated_v1 def testWhileGrad_ResourceVariable(self): with self.cached_session(): a = resource_variable_ops.ResourceVariable(3.0) @@ -2040,7 +2223,7 @@ class ControlFlowTest(test.TestCase): def testWhileGradInCond(self): - with self.cached_session(): + with self.cached_session() as sess: n = ops.convert_to_tensor(1.0, name="n") x = array_ops.placeholder(dtypes.float32, shape=None) c = lambda n: math_ops.less(n, 10.0) @@ -2051,10 +2234,15 @@ class ControlFlowTest(test.TestCase): [tensor_shape.unknown_shape()]) return gradients_impl.gradients(r, x) - r = control_flow_ops.cond(math_ops.less(1, 2), fn1, lambda: x) - self.assertAllClose(9.0, r.eval(feed_dict={x: 1.0})) + #placed lambda function return tensor in list and set strict flag to True + #as cond_v2 implementation preserves nested output structures even with singeltons + r = control_flow_ops.cond(math_ops.less(1, 2), fn1, lambda: [x], strict=True) + #cannot run eval() on list object so use sess.run() and save output + result = sess.run(r,feed_dict={x: 1.0}) + self.assertAllClose([9.0], result) @test_util.disable_control_flow_v2("b/116340060") + @test_util.run_deprecated_v1 def testGradInWhileWrtInitialLoopVal(self): with self.cached_session(): x = array_ops.placeholder(dtypes.float32, shape=(), name="x") @@ -2102,7 +2290,7 @@ class ControlFlowTest(test.TestCase): i, x = control_flow_ops.while_loop(lambda i, x: i < 3, outer_body, [0, 0.0]) with self.cached_session() as sess: - i_val, x_val = sess.run([i, x]) + i_val, x_val = self.evaluate([i, x]) self.assertEqual(i_val, 3) self.assertAllClose(x_val, 1.0) @@ -2131,7 +2319,7 @@ class ControlFlowTest(test.TestCase): r_flattened = nest.flatten(r) self.assertEqual([100.0, 1.0, 102.0, 3.0, 4.0 + 100 * 2.0], - sess.run(r_flattened)) + self.evaluate(r_flattened)) def testWhile_NestedBadArityFails(self): with self.cached_session(): @@ -2172,6 +2360,7 @@ class ControlFlowTest(test.TestCase): r = gradients_impl.gradients([rx], y) self.assertAllClose(120.0, r[0].eval()) + @test_util.run_deprecated_v1 def testWhileGrad_Dependency(self): with self.cached_session(): i = constant_op.constant(0, name="i") @@ -2223,6 +2412,7 @@ class ControlFlowTest(test.TestCase): variables.global_variables_initializer().run() self.assertAllClose(np.ones([2, 3]), sess.run(grad[0])) + @test_util.run_deprecated_v1 def testWhileGrad_Const(self): with self.cached_session() as sess: c0 = constant_op.constant(0.0, name="c0") @@ -2295,10 +2485,11 @@ class ControlFlowTest(test.TestCase): with ops.control_dependencies([x_f]): y_f_d = array_ops.identity(y_f, name="y_f_d") - self.assertAllClose(2.0, y_f_d.eval()) # y_f_d = 1.0 + 1.0 + self.assertAllClose(2.0, self.evaluate(y_f_d)) # y_f_d = 1.0 + 1.0 g = gradients_impl.gradients([y_f_d], [x])[0] self.assertTrue(g is not None) - self.assertAllClose(1.0, g.eval()) # y_f_d = x + 1.0, dy_f_d/dx = 1.0 + self.assertAllClose(1.0, + self.evaluate(g)) # y_f_d = x + 1.0, dy_f_d/dx = 1.0 def _testNestedWhileGrad_Simple(self, use_gpu): with self.cached_session(use_gpu=use_gpu): @@ -2314,8 +2505,9 @@ class ControlFlowTest(test.TestCase): r = control_flow_ops.while_loop(c, b, [v]) r = gradients_impl.gradients(r, v)[0] - self.assertAllClose(8.0, r.eval()) + self.assertAllClose(8.0, self.evaluate(r)) + @test_util.run_deprecated_v1 def testNestedWhileGrad_Simple(self): self._testNestedWhileGrad_Simple(use_gpu=False) self._testNestedWhileGrad_Simple(use_gpu=True) @@ -2341,8 +2533,9 @@ class ControlFlowTest(test.TestCase): r = control_flow_ops.while_loop(c, b, [v]) r = gradients_impl.gradients(r, v)[0] - self.assertAllClose(256.0, r.eval()) + self.assertAllClose(256.0, self.evaluate(r)) + @test_util.run_deprecated_v1 def testNestedWhileGrad_ParallelInner(self): with self.cached_session(): v = constant_op.constant(1.0) @@ -2364,10 +2557,8 @@ class ControlFlowTest(test.TestCase): r = control_flow_ops.while_loop(c, b, [v]) r = gradients_impl.gradients(r, v)[0] - self.assertAllClose(512.0, r.eval()) + self.assertAllClose(512.0, self.evaluate(r)) - @test_util.disable_control_flow_v2("unsupported: resource creation in body. " - "Enable with new TAs b/117675481") def testNestedWhileGrad_ParallelIterations(self): # Make sure the stack pushes and pops of an inner loop are executed in # the sequential order of the iterations of its outer loop. @@ -2386,9 +2577,9 @@ class ControlFlowTest(test.TestCase): res = outer_loop(inp) optimizer = adam.AdamOptimizer(learning_rate=0.001) train_op = optimizer.minimize(math_ops.reduce_mean(math_ops.square(res))) - sess.run(variables.global_variables_initializer()) - sess.run(train_op) - self.assertAllClose(2.999, var.eval()) + self.evaluate(variables.global_variables_initializer()) + self.evaluate(train_op) + self.assertAllClose(2.999, self.evaluate(var)) def _testWhileCondGrad_Simple(self, use_gpu): with self.cached_session(use_gpu=use_gpu): @@ -2404,14 +2595,16 @@ class ControlFlowTest(test.TestCase): # pylint: enable=undefined-variable r = control_flow_ops.while_loop(c, b, [v]) r = gradients_impl.gradients(r, v)[0] - self.assertAllClose(1024.0, r.eval()) + self.assertAllClose(1024.0, self.evaluate(r)) @test_util.disable_control_flow_v2("b/117519152") + @test_util.run_deprecated_v1 def testWhileCondGrad_Simple(self): self._testWhileCondGrad_Simple(use_gpu=False) self._testWhileCondGrad_Simple(use_gpu=True) @test_util.disable_control_flow_v2("b/117276490") + @test_util.run_deprecated_v1 def testWhileCondGrad_UnknownShape(self): with self.cached_session() as sess: v = array_ops.placeholder(dtypes.float32) @@ -2429,6 +2622,7 @@ class ControlFlowTest(test.TestCase): r = sess.run(r, feed_dict={v: 2.0}) self.assertAllClose(1024.0, r) + @test_util.run_deprecated_v1 def testWhileGrad_Concat(self): with self.cached_session() as sess: x = variable_scope.get_variable("x", initializer=[[1., 2.]]) @@ -2446,11 +2640,11 @@ class ControlFlowTest(test.TestCase): [i0.get_shape(), tensor_shape.TensorShape([None, 2])]) s = math_ops.reduce_sum(h) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) optimizer = gradient_descent.GradientDescentOptimizer(0.01) op = optimizer.minimize(s) - sess.run(op) - self.assertAllClose([[0.98000002, 1.98000002]], sess.run(x)) + self.evaluate(op) + self.assertAllClose([[0.98000002, 1.98000002]], self.evaluate(x)) @test_util.disable_control_flow_v2("b/113324949 (RefVariable)") def testWhileWithRefsWithGradients_1(self): @@ -2501,9 +2695,10 @@ class ControlFlowTest(test.TestCase): _, r = control_flow_ops.while_loop(c, b, [i, x]) r = gradients_impl.gradients(r.values, values)[0] - self.assertAllClose(np.array([1024.0, 1024.0]), r.eval()) + self.assertAllClose(np.array([1024.0, 1024.0]), self.evaluate(r)) @test_util.disable_control_flow_v2("b/116328420 (SparseTensor)") + @test_util.run_deprecated_v1 def testWhileGrad_SparseTensor(self): with self.cached_session(): values = constant_op.constant([2.0, 4.0], name="values") @@ -2524,7 +2719,7 @@ class ControlFlowTest(test.TestCase): _, r = control_flow_ops.while_loop(c, b, [i, x]) r = gradients_impl.gradients(r.values, values)[0] - self.assertAllClose(np.array([1024.0, 1024.0]), r.eval()) + self.assertAllClose(np.array([1024.0, 1024.0]), self.evaluate(r)) @test_util.disable_control_flow_v2("b/115920078 (gradients)") def testCallGradInLoop(self): @@ -2544,10 +2739,9 @@ class ControlFlowTest(test.TestCase): output_grad = control_flow_ops.while_loop( c, b, [i0, constant_op.constant(0.0)]) - self.assertAllClose(600.0, sess.run(output_grad)[1]) + self.assertAllClose(600.0, self.evaluate(output_grad)[1]) - @test_util.disable_control_flow_v2("unsupported: resource creation in body. " - "Enable with new TAs b/117675481") + @test_util.run_deprecated_v1 def testWhileAndTensorArray(self): with self.cached_session() as sess: param = constant_op.constant(2.0) @@ -2565,8 +2759,9 @@ class ControlFlowTest(test.TestCase): r = control_flow_ops.while_loop(c, b, [n0, y0], parallel_iterations=1) r = gradients_impl.gradients(r, param)[0] - self.assertAllClose(107520.0, sess.run(r)) + self.assertAllClose(107520.0, self.evaluate(r)) + @test_util.run_deprecated_v1 def testWhileGrad_StopGrad(self): with self.cached_session(): x = constant_op.constant(3.0, name="x") @@ -2582,9 +2777,9 @@ class ControlFlowTest(test.TestCase): rx, ry = control_flow_ops.while_loop(c, b, [x, y]) r = gradients_impl.gradients(rx, y)[0] - self.assertEqual(136.0, r.eval()) + self.assertEqual(136.0, self.evaluate(r)) r = gradients_impl.gradients(ry, y)[0] - self.assertEqual(32.0, r.eval()) + self.assertEqual(32.0, self.evaluate(r)) r = gradients_impl.gradients(array_ops.stop_gradient(rx), y)[0] self.assertEqual(r, None) @@ -2602,14 +2797,15 @@ class ControlFlowTest(test.TestCase): self.assertEqual(r, None) r = gradients_impl.gradients(math_ops.add(rx, ry), y)[0] - self.assertEqual(168.0, r.eval()) + self.assertEqual(168.0, self.evaluate(r)) r = gradients_impl.gradients( math_ops.add(rx, array_ops.stop_gradient(ry)), y)[0] - self.assertEqual(136.0, r.eval()) + self.assertEqual(136.0, self.evaluate(r)) r = gradients_impl.gradients( math_ops.add(array_ops.stop_gradient(rx), ry), y)[0] - self.assertEqual(32.0, r.eval()) + self.assertEqual(32.0, self.evaluate(r)) + @test_util.run_deprecated_v1 def testWhileGrad_StopGradInside(self): with self.cached_session(): x = constant_op.constant(3.0, name="x") @@ -2625,10 +2821,11 @@ class ControlFlowTest(test.TestCase): rx, _ = control_flow_ops.while_loop(c, b, [x, y]) r = gradients_impl.gradients(rx, y)[0] - self.assertAllClose(0.0, r.eval()) + self.assertAllClose(0.0, self.evaluate(r)) r = gradients_impl.gradients(rx, x)[0] - self.assertAllClose(156.0, r.eval()) + self.assertAllClose(156.0, self.evaluate(r)) + @test_util.run_deprecated_v1 def testWhileGrad_StopGradInsideNoShape(self): with self.cached_session() as sess: x = array_ops.placeholder(dtypes.float32) @@ -2650,9 +2847,10 @@ class ControlFlowTest(test.TestCase): self.assertAllClose([156.0, 400.0], sess.run(r, feed_dict=feed_dict)) name = "gradients/while/stopped_grad" all_ops = x.graph.get_operations() - self.assertFalse(any([name in op.name for op in all_ops])) + self.assertFalse(any(name in op.name for op in all_ops)) @test_util.disable_control_flow_v2("b/117954949") + @test_util.run_deprecated_v1 def testWhileGradGradFail(self): theta = variables.Variable(initial_value=1.) @@ -2667,6 +2865,7 @@ class ControlFlowTest(test.TestCase): grad_theta_stopped = array_ops.stop_gradient(grad_theta) gradients_impl.gradients(grad_theta_stopped, theta) + @test_util.run_deprecated_v1 def testStopGradOnWhileGrad(self): with self.cached_session(): x = constant_op.constant(2.0, name="x") @@ -2681,9 +2880,10 @@ class ControlFlowTest(test.TestCase): r = math_ops.add(math_ops.square(y), rx) r = math_ops.add(r, rg) r = gradients_impl.gradients(r, y)[0] - self.assertEqual(388.0, r.eval()) + self.assertEqual(388.0, self.evaluate(r)) @test_util.disable_control_flow_v2("b/113324949 (RefVariable)") + @test_util.run_deprecated_v1 def testWhileGradientWithNontrainablePath1(self): q = variables.Variable([7., 8.]) @@ -2698,8 +2898,8 @@ class ControlFlowTest(test.TestCase): dy_dq, = gradients_impl.gradients(y, q) self.assertIsNotNone(dy_dq) with self.cached_session() as sess: - sess.run(q.initializer) - self.assertAllClose([0., 0.], sess.run(dy_dq)) + self.evaluate(q.initializer) + self.assertAllClose([0., 0.], self.evaluate(dy_dq)) @test_util.disable_control_flow_v2("b/113324949 (RefVariable)") def testWhileGradientWithNontrainablePath2(self): @@ -2716,8 +2916,8 @@ class ControlFlowTest(test.TestCase): dy_dq, = gradients_impl.gradients(y, q) self.assertIsNotNone(dy_dq) with self.cached_session() as sess: - sess.run(q.initializer) - self.assertAllClose([1., 1.], sess.run(dy_dq)) + self.evaluate(q.initializer) + self.assertAllClose([1., 1.], self.evaluate(dy_dq)) @test_util.disable_control_flow_v2("b/115920078 (gradients)") def testIssue16504(self): @@ -2767,7 +2967,7 @@ class ControlFlowTest(test.TestCase): z = math_ops.add(r, array_ops.stop_gradient(math_ops.reduce_sum(grads))) result = gradients_impl.gradients(z, vars_)[0] variables.global_variables_initializer().run() - self.assertEqual(5.0, result.eval()) + self.assertEqual(5.0, self.evaluate(result)) def testOneValueCond(self): @@ -2785,6 +2985,7 @@ class ControlFlowTest(test.TestCase): # False case: c = 0 is not >= 1 self.assertEqual([2], i.eval(feed_dict={c: 0})) + @test_util.run_deprecated_v1 def testExampleCond(self): with self.cached_session(): @@ -2828,7 +3029,7 @@ class ControlFlowTest(test.TestCase): r4 = control_flow_ops.case( [(x < y, f1), (x < y, f2)], default=f3, exclusive=True) with self.assertRaisesOpError("Input error:"): - r4.eval() + self.evaluate(r4) # Check that the default is called if none of the others are r5 = control_flow_ops.case({x > y: f1}, default=f3) @@ -2874,19 +3075,19 @@ class ControlFlowTest(test.TestCase): ((x > y, a), (x > y, b)), default=c, exclusive=True) variables.global_variables_initializer().run() - self.assertAllEqual(sess.run([v0, v1, v2]), [-1] * 3) - self.assertEqual(2, r2.eval()) - self.assertAllEqual(sess.run([v0, v1, v2]), [-1, -1, 2]) + self.assertAllEqual(self.evaluate([v0, v1, v2]), [-1] * 3) + self.assertEqual(2, self.evaluate(r2)) + self.assertAllEqual(self.evaluate([v0, v1, v2]), [-1, -1, 2]) variables.global_variables_initializer().run() - self.assertAllEqual(sess.run([v0, v1, v2]), [-1] * 3) - self.assertEqual(1, r1.eval()) - self.assertAllEqual(sess.run([v0, v1, v2]), [-1, 1, -1]) + self.assertAllEqual(self.evaluate([v0, v1, v2]), [-1] * 3) + self.assertEqual(1, self.evaluate(r1)) + self.assertAllEqual(self.evaluate([v0, v1, v2]), [-1, 1, -1]) variables.global_variables_initializer().run() - self.assertAllEqual(sess.run([v0, v1, v2]), [-1] * 3) - self.assertEqual(0, r0.eval()) - self.assertAllEqual(sess.run([v0, v1, v2]), [0, -1, -1]) + self.assertAllEqual(self.evaluate([v0, v1, v2]), [-1] * 3) + self.assertEqual(0, self.evaluate(r0)) + self.assertAllEqual(self.evaluate([v0, v1, v2]), [0, -1, -1]) @test_util.disable_control_flow_v2("b/113324949 (ref vars)") def testOneOpCond(self): @@ -2907,15 +3108,15 @@ class ControlFlowTest(test.TestCase): self.assertTrue(isinstance(i, ops.Tensor)) variables.global_variables_initializer().run() - self.assertEqual(0, v.eval()) + self.assertEqual(0, self.evaluate(v)) # True case: c = 2 is >= 1, v is set to 1. self.assertEqual(1, i.eval(feed_dict={c.name: 2})) - self.assertEqual(1, v.eval()) + self.assertEqual(1, self.evaluate(v)) # False case: c = 0 is not >= 1, v is set to 2. self.assertEqual(2, i.eval(feed_dict={c.name: 0})) - self.assertEqual(2, v.eval()) + self.assertEqual(2, self.evaluate(v)) def testWithOpsDependencies(self): with self.cached_session() as sess: @@ -2924,7 +3125,7 @@ class ControlFlowTest(test.TestCase): # Fetching v directly will result in an uninitialized error with self.assertRaisesOpError("Attempting to use uninitialized value"): - sess.run([c, v]) + self.evaluate([c, v]) # Use a control dependency to ensure init_variable is run # while asking for c @@ -2932,7 +3133,7 @@ class ControlFlowTest(test.TestCase): name="real_tensor", output_tensor=v._ref(), # pylint: disable=protected-access dependencies=[v.initializer]) - c_val, real_v_val = sess.run([c, real_v]) + c_val, real_v_val = self.evaluate([c, real_v]) # Ensure the result of 'real_c' is the same as 'c' self.assertAllEqual(10, c_val) @@ -2957,14 +3158,14 @@ class ControlFlowTest(test.TestCase): # Fetching v directly will result in an uninitialized error with self.assertRaisesOpError("Attempting to use uninitialized value"): - v.eval() + self.evaluate(v) # Get the value of 'c2_with_c1_dep', which should cause 'v' # to be initialized. - self.assertAllEqual(20, c2_with_c1_dep.eval()) + self.assertAllEqual(20, self.evaluate(c2_with_c1_dep)) # Ensure that 'v' is initialized - self.assertAllClose(0.0, v.eval()) + self.assertAllClose(0.0, self.evaluate(v)) def testWithIndexedSlicesDependencies(self): with self.cached_session(): @@ -2979,13 +3180,15 @@ class ControlFlowTest(test.TestCase): # Fetching gather_v_at_1 will result in an uninitialized error with self.assertRaisesOpError("Attempting to use uninitialized value"): - gather_v_at_1.eval() + self.evaluate(gather_v_at_1) # Getting gather_v_at_1_after_init will work, and initialize v. - self.assertAllEqual([[10.0, 11.0]], gather_v_at_1_after_init.eval()) + self.assertAllEqual([[10.0, 11.0]], + self.evaluate(gather_v_at_1_after_init)) # Double check that 'v' is initialized - self.assertAllClose([[0.0, 1.0], [10.0, 11.0], [20.0, 21.0]], v.eval()) + self.assertAllClose([[0.0, 1.0], [10.0, 11.0], [20.0, 21.0]], + self.evaluate(v)) def testDependenciesDevice(self): with ops.Graph().as_default(): @@ -3019,11 +3222,11 @@ class ControlFlowTest(test.TestCase): init = control_flow_ops.group(v1.initializer, v2.initializer) # Fetching v1 directly will result in an uninitialized error with self.assertRaisesOpError("Attempting to use uninitialized value"): - v1.eval() + self.evaluate(v1) # Runs "init" before fetching v1 and v2. init.run() - v1_val, v2_val = sess.run([v1, v2]) + v1_val, v2_val = self.evaluate([v1, v2]) # Ensure that v1 and v2 are initialized self.assertAllClose([0.0], v1_val) @@ -3034,6 +3237,7 @@ class ControlFlowTest(test.TestCase): self.assertEqual(op.type, "NoOp") self.assertEqual(op.control_inputs, []) + @test_util.run_deprecated_v1 def testMergeShapes(self): # All inputs unknown. p1 = array_ops.placeholder(dtypes.float32) @@ -3088,6 +3292,7 @@ class ControlFlowTest(test.TestCase): self.assertEqual([None, None], m.get_shape().as_list()) self.assertEqual([], index.get_shape()) + @test_util.run_deprecated_v1 def testRefSelect(self): index = array_ops.placeholder(dtypes.int32) @@ -3121,6 +3326,7 @@ class ControlFlowTest(test.TestCase): s = control_flow_ops.ref_select(index, [v1, v2]) self.assertEqual(None, s.get_shape()) + @test_util.run_deprecated_v1 def testRunLoopTensor(self): with self.cached_session() as sess: tensor_list = [] @@ -3134,7 +3340,7 @@ class ControlFlowTest(test.TestCase): result = control_flow_ops.while_loop(condition, body, [constant_op.constant(4)]) - self.assertEqual(10, sess.run(result)) + self.assertEqual(10, self.evaluate(result)) # Ensure that we cannot run a tensor that escapes the loop body # accidentally. @@ -3184,7 +3390,7 @@ class ControlFlowTest(test.TestCase): cond = constant_op.constant(True, dtypes.bool) v_f, v_t = control_flow_ops.switch(constant_qint, cond) result = control_flow_ops.merge([v_f, v_t]) - sess.run(result) + self.evaluate(result) def testQIntRefSwitchMerge(self): with self.cached_session(use_gpu=test.is_gpu_available()) as sess: @@ -3192,13 +3398,22 @@ class ControlFlowTest(test.TestCase): shape=[1], dtype=dtypes.qint8, name="v", container="", shared_name="") assign_op = state_ops.assign( var_qint, constant_op.constant(np.array([42]), dtypes.qint8)) - sess.run(assign_op) + self.evaluate(assign_op) cond = constant_op.constant(True, dtypes.bool) v_f, v_t = control_flow_ops.ref_switch(var_qint, cond) result = control_flow_ops.ref_merge([v_f, v_t]) - sess.run(result) + self.evaluate(result) + + def testUInt64SwitchMerge(self): + with self.cached_session(force_gpu=test.is_gpu_available()) as sess: + constant_uint64 = constant_op.constant(np.array([42]), dtypes.uint64) + cond = constant_op.constant(True, dtypes.bool) + v_f, v_t = control_flow_ops.switch(constant_uint64, cond) + result = control_flow_ops.merge([v_f, v_t]) + self.evaluate(result) + @test_util.run_deprecated_v1 def testQIntArgAndRet(self): @function.Defun(dtypes.qint8) @@ -3208,7 +3423,7 @@ class ControlFlowTest(test.TestCase): with self.cached_session(force_gpu=test.is_gpu_available()) as sess: qint = constant_op.constant(np.array([42]), dtypes.qint8) result = func(qint) - sess.run(result) + self.evaluate(result) class ControlFlowContextCheckTest(test.TestCase): @@ -3246,6 +3461,7 @@ class ControlFlowContextCheckTest(test.TestCase): "is in a while loop. See info log for more details."): math_ops.add(1, while_tensor) + @test_util.run_deprecated_v1 def testInvalidContextInCond(self): # Accessing a while loop tensor in cond is illegal. while_tensor = self._getWhileTensor() @@ -3314,6 +3530,7 @@ class ControlFlowContextCheckTest(test.TestCase): control_flow_ops.while_loop(lambda i: i < 5, body, [0]) + @test_util.run_deprecated_v1 def testInvalidNestedContexts(self): # Accessing a tensor from a while context in a different while context, all # inside a cond context, is illegal. @@ -3347,21 +3564,22 @@ class TupleTest(test.TestCase): # v1 is not initialized. with self.assertRaisesOpError("Attempting to use uninitialized value"): - v1.eval() + self.evaluate(v1) # v2 is not initialized. with self.assertRaisesOpError("Attempting to use uninitialized value"): - v2.eval() + self.evaluate(v2) if v1_first: # Getting t1 initializes v2. - self.assertAllClose([3.0], t1.eval()) - self.assertAllClose([10.0], v2.eval()) + self.assertAllClose([3.0], self.evaluate(t1)) + self.assertAllClose([10.0], self.evaluate(v2)) else: # Getting t2 initializes v1. - self.assertAllClose([30.0], t2.eval()) - self.assertAllClose([1.0], v1.eval()) + self.assertAllClose([30.0], self.evaluate(t2)) + self.assertAllClose([1.0], self.evaluate(v1)) + @test_util.run_deprecated_v1 def testIndexedSlices(self): for v1_first in [True, False]: with self.cached_session(): @@ -3385,22 +3603,22 @@ class TupleTest(test.TestCase): # v1 is not initialized. with self.assertRaisesOpError("Attempting to use uninitialized value"): - v1.eval() + self.evaluate(v1) # v2 is not initialized. with self.assertRaisesOpError("Attempting to use uninitialized value"): - v2.eval() + self.evaluate(v2) if v1_first: # Getting g1 initializes v2. - self.assertAllClose([[10.0, 11.0]], g1.eval()) + self.assertAllClose([[10.0, 11.0]], self.evaluate(g1)) self.assertAllClose([[0.1, 1.1], [10.1, 11.1], [20.1, 21.1]], - v2.eval()) + self.evaluate(v2)) else: # Getting g2 initializes v1. - self.assertAllClose([[10.1, 11.1]], g2.eval()) + self.assertAllClose([[10.1, 11.1]], self.evaluate(g2)) self.assertAllClose([[0.0, 1.0], [10.0, 11.0], [20.0, 21.0]], - v1.eval()) + self.evaluate(v1)) def testAcceptTensorsAsControlInputs(self): with self.cached_session(): @@ -3410,13 +3628,14 @@ class TupleTest(test.TestCase): [constant_op.constant(0)], control_inputs=[assign]) # Should trigger the assign. - t.eval() + self.evaluate(t) - self.assertEquals(1, var.eval()) + self.assertEquals(1, self.evaluate(var)) class AssertTest(test.TestCase): + @test_util.run_deprecated_v1 def testGuardedAssertDoesNotCopyWhenTrue(self): with self.session(use_gpu=True) as sess: with ops.device(test.gpu_device_name()): @@ -3513,7 +3732,7 @@ class WhileOpBenchmark(test.Benchmark): with session.Session() as sess, ops.device(default_device): # Get the initial id i, input x, and kernel. i, x, kernel = self._getInitVariables() - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) if static_unroll: for _ in xrange(steps): @@ -3532,11 +3751,11 @@ class WhileOpBenchmark(test.Benchmark): for _ in xrange(3): # exclude warm up time - sess.run(r) + self.evaluate(r) start_time = time.time() for _ in xrange(num_iters): - sess.run(r) + self.evaluate(r) return (time.time() - start_time) / num_iters def benchmarkWhileOpCrossDevicePlacement(self): diff --git a/tensorflow/python/kernel_tests/conv1d_test.py b/tensorflow/python/kernel_tests/conv1d_test.py index 8540875d75..e8463323df 100644 --- a/tensorflow/python/kernel_tests/conv1d_test.py +++ b/tensorflow/python/kernel_tests/conv1d_test.py @@ -43,7 +43,7 @@ class Conv1DTest(test.TestCase): with self.cached_session(use_gpu=test.is_gpu_available()): c = nn_ops.conv1d(x, filters, stride, padding="VALID") reduced = array_ops.squeeze(c) - output = reduced.eval() + output = self.evaluate(reduced) if stride == 1: self.assertEqual(len(output), 3) self.assertAllClose(output, @@ -69,7 +69,7 @@ class Conv1DTest(test.TestCase): 1.0, shape=f_shape, name="filter", dtype=dtypes.float32) output = nn_ops.conv1d_transpose( x, f, y_shape, stride=stride, padding="VALID") - value = output.eval() + value = self.evaluate(output) cache_values = np.zeros(y_shape, dtype=np.float32) diff --git a/tensorflow/python/kernel_tests/conv2d_backprop_filter_grad_test.py b/tensorflow/python/kernel_tests/conv2d_backprop_filter_grad_test.py index af6ffc1d19..7b3b560b24 100644 --- a/tensorflow/python/kernel_tests/conv2d_backprop_filter_grad_test.py +++ b/tensorflow/python/kernel_tests/conv2d_backprop_filter_grad_test.py @@ -22,6 +22,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import nn_ops @@ -31,6 +32,7 @@ from tensorflow.python.platform import test class Conv2DBackpropFilterGradTest(test.TestCase): + @test_util.run_deprecated_v1 def testGradient(self): with self.cached_session(): for padding in ["SAME", "VALID"]: diff --git a/tensorflow/python/kernel_tests/conv2d_transpose_test.py b/tensorflow/python/kernel_tests/conv2d_transpose_test.py index 6f9992a317..c603c08630 100644 --- a/tensorflow/python/kernel_tests/conv2d_transpose_test.py +++ b/tensorflow/python/kernel_tests/conv2d_transpose_test.py @@ -53,7 +53,7 @@ class Conv2DTransposeTest(test.TestCase): 1.0, shape=f_shape, name="filter", dtype=dtypes.float32) output = nn_ops.conv2d_transpose( x, f, y_shape, strides=strides, padding="SAME") - value = output.eval() + value = self.evaluate(output) # We count the number of cells being added at the locations in the output. # At the center, #cells=kernel_height * kernel_width @@ -91,7 +91,7 @@ class Conv2DTransposeTest(test.TestCase): 1.0, shape=f_shape, name="filter", dtype=dtypes.float32) output = nn_ops.conv2d_transpose( x, f, y_shape, strides=strides, padding="SAME") - value = output.eval() + value = self.evaluate(output) for n in xrange(x_shape[0]): for k in xrange(f_shape[2]): @@ -124,7 +124,7 @@ class Conv2DTransposeTest(test.TestCase): 1.0, shape=f_shape, name="filter", dtype=dtypes.float32) output = nn_ops.conv2d_transpose( x, f, y_shape, strides=strides, padding="VALID") - value = output.eval() + value = self.evaluate(output) cache_values = np.zeros(y_shape, dtype=np.float32) @@ -155,6 +155,7 @@ class Conv2DTransposeTest(test.TestCase): self.assertAllClose(cache_values, value) + @test_util.run_deprecated_v1 def testGradient(self): x_shape = [2, 6, 4, 3] f_shape = [3, 3, 2, 3] @@ -195,7 +196,7 @@ class Conv2DTransposeTest(test.TestCase): output = nn_ops.conv2d_transpose( x, f, y_shape, strides=strides, padding="SAME", data_format="NCHW") - value = output.eval() + value = self.evaluate(output) for n in xrange(x_shape[0]): for k in xrange(f_shape[2]): for w in xrange(y_shape[3]): @@ -230,7 +231,7 @@ class Conv2DTransposeTest(test.TestCase): output = nn_ops.conv2d_transpose( x, f, y_shape, strides=strides, padding="SAME", data_format="NCHW") - value = output.eval() + value = self.evaluate(output) for n in xrange(x_shape[0]): for k in xrange(f_shape[2]): for w in xrange(y_shape[3]): @@ -265,7 +266,7 @@ class Conv2DTransposeTest(test.TestCase): output = nn_ops.conv2d_transpose( x, f, y_shape, strides=strides, padding="VALID", data_format="NCHW") - value = output.eval() + value = self.evaluate(output) cache_values = np.zeros(y_shape, dtype=np.float32) # The amount of padding added pad = 1 @@ -293,7 +294,6 @@ class Conv2DTransposeTest(test.TestCase): self.assertAllClose(cache_values, value) - @test_util.enable_c_shapes def testConv2DTransposeShapeInference(self): # Test case for 8972 initializer = random_ops.truncated_normal( diff --git a/tensorflow/python/kernel_tests/conv3d_backprop_filter_v2_grad_test.py b/tensorflow/python/kernel_tests/conv3d_backprop_filter_v2_grad_test.py index 89b64068ac..7e913febed 100644 --- a/tensorflow/python/kernel_tests/conv3d_backprop_filter_v2_grad_test.py +++ b/tensorflow/python/kernel_tests/conv3d_backprop_filter_v2_grad_test.py @@ -22,6 +22,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import nn_ops @@ -31,6 +32,7 @@ from tensorflow.python.platform import test class Conv3DBackpropFilterV2GradTest(test.TestCase): + @test_util.run_deprecated_v1 def testGradient(self): with self.cached_session(): for padding in ["SAME", "VALID"]: diff --git a/tensorflow/python/kernel_tests/conv3d_transpose_test.py b/tensorflow/python/kernel_tests/conv3d_transpose_test.py index 2527b83769..22ba5b9037 100644 --- a/tensorflow/python/kernel_tests/conv3d_transpose_test.py +++ b/tensorflow/python/kernel_tests/conv3d_transpose_test.py @@ -23,6 +23,7 @@ from six.moves import xrange # pylint: disable=redefined-builtin from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import nn_ops import tensorflow.python.ops.nn_grad # pylint: disable=unused-import @@ -48,7 +49,7 @@ class Conv3DTransposeTest(test.TestCase): 1.0, shape=f_shape, name="filter", dtype=dtypes.float32) output = nn_ops.conv3d_transpose( x, f, y_shape, strides=strides, padding="SAME") - value = output.eval() + value = self.evaluate(output) # We count the number of cells being added at the locations in the output. # At the center, #cells = kernel_depth * kernel_height * kernel_width @@ -98,7 +99,7 @@ class Conv3DTransposeTest(test.TestCase): 1.0, shape=f_shape, name="filter", dtype=dtypes.float32) output = nn_ops.conv3d_transpose( x, f, y_shape, strides=strides, padding="SAME") - value = output.eval() + value = self.evaluate(output) for n in xrange(x_shape[0]): for k in xrange(f_shape[3]): @@ -119,6 +120,7 @@ class Conv3DTransposeTest(test.TestCase): target = 3.0 self.assertAllClose(target, value[n, d, h, w, k]) + @test_util.run_deprecated_v1 def testConv3DTransposeShapeMismatch(self): # Test case for GitHub issue 18460 x_shape = [2, 2, 3, 4, 3] @@ -146,7 +148,7 @@ class Conv3DTransposeTest(test.TestCase): output = nn_ops.conv3d_transpose( x_value, f_value, constant_op.constant(y_shape, dtype=dtype), strides=strides, padding="SAME") - output.eval() + self.evaluate(output) def testConv3DTransposeValid(self): with self.cached_session(): @@ -165,7 +167,7 @@ class Conv3DTransposeTest(test.TestCase): 1.0, shape=f_shape, name="filter", dtype=dtypes.float32) output = nn_ops.conv3d_transpose( x, f, y_shape, strides=strides, padding="VALID") - value = output.eval() + value = self.evaluate(output) cache_values = np.zeros(y_shape, dtype=np.float32) @@ -201,6 +203,7 @@ class Conv3DTransposeTest(test.TestCase): self.assertAllClose(cache_values, value) + @test_util.run_deprecated_v1 def testGradient(self): x_shape = [2, 3, 4, 3, 2] f_shape = [3, 3, 3, 2, 2] diff --git a/tensorflow/python/kernel_tests/conv_ops_3d_test.py b/tensorflow/python/kernel_tests/conv_ops_3d_test.py index c4a9cdcf8e..4a689b3fdf 100644 --- a/tensorflow/python/kernel_tests/conv_ops_3d_test.py +++ b/tensorflow/python/kernel_tests/conv_ops_3d_test.py @@ -52,11 +52,11 @@ class Conv3DTest(test.TestCase): def _DtypesToTest(self, use_gpu): if use_gpu: if not test_util.CudaSupportsHalfMatMulAndConv(): - return [dtypes.float32] + return [dtypes.float64, dtypes.float32] else: # It is important that float32 comes before float16 here, # as we will be using its gradients as reference for fp16 gradients. - return [dtypes.float32, dtypes.float16] + return [dtypes.float64, dtypes.float32, dtypes.float16] else: return [dtypes.float64, dtypes.float32, dtypes.float16] @@ -109,7 +109,7 @@ class Conv3DTest(test.TestCase): results.append(result) with self.cached_session() as sess: - values = sess.run(results) + values = self.evaluate(results) for value in values: print("expected = ", expected) print("actual = ", value) @@ -184,8 +184,8 @@ class Conv3DTest(test.TestCase): computed_results.append(computed) tolerance = 1e-2 if use_gpu else 1e-5 with self.cached_session() as sess: - expected_values = sess.run(expected_results) - computed_values = sess.run(computed_results) + expected_values = self.evaluate(expected_results) + computed_values = self.evaluate(computed_results) for e_value, c_value in zip(expected_values, computed_values): print("expected = ", e_value) print("actual = ", c_value) @@ -462,6 +462,7 @@ class Conv3DTest(test.TestCase): self._ConstructAndTestGradientForConfig(data_format=data_format, use_gpu=use_gpu, **kwargs) + @test_util.run_deprecated_v1 def testInputGradientValidPaddingStrideOne(self): self.ConstructAndTestGradient( batch=2, @@ -473,6 +474,7 @@ class Conv3DTest(test.TestCase): padding="VALID", test_input=True) + @test_util.run_deprecated_v1 def testFilterGradientValidPaddingStrideOne(self): self.ConstructAndTestGradient( batch=4, @@ -484,6 +486,7 @@ class Conv3DTest(test.TestCase): padding="VALID", test_input=False) + @test_util.run_deprecated_v1 def testInputGradientValidPaddingStrideTwo(self): self.ConstructAndTestGradient( batch=2, @@ -495,6 +498,7 @@ class Conv3DTest(test.TestCase): padding="VALID", test_input=True) + @test_util.run_deprecated_v1 def testFilterGradientValidPaddingStrideTwo(self): self.ConstructAndTestGradient( batch=2, @@ -506,6 +510,7 @@ class Conv3DTest(test.TestCase): padding="VALID", test_input=False) + @test_util.run_deprecated_v1 def testInputGradientValidPaddingStrideThree(self): self.ConstructAndTestGradient( batch=2, @@ -517,6 +522,7 @@ class Conv3DTest(test.TestCase): padding="VALID", test_input=True) + @test_util.run_deprecated_v1 def testFilterGradientValidPaddingStrideThree(self): self.ConstructAndTestGradient( batch=2, @@ -528,6 +534,7 @@ class Conv3DTest(test.TestCase): padding="VALID", test_input=False) + @test_util.run_deprecated_v1 def testInputGradientSamePaddingStrideOne(self): self.ConstructAndTestGradient( batch=2, @@ -539,6 +546,7 @@ class Conv3DTest(test.TestCase): padding="SAME", test_input=True) + @test_util.run_deprecated_v1 def testFilterGradientSamePaddingStrideOne(self): self.ConstructAndTestGradient( batch=2, @@ -550,6 +558,7 @@ class Conv3DTest(test.TestCase): padding="SAME", test_input=False) + @test_util.run_deprecated_v1 def testInputGradientSamePaddingStrideTwo(self): self.ConstructAndTestGradient( batch=2, @@ -561,6 +570,7 @@ class Conv3DTest(test.TestCase): padding="SAME", test_input=True) + @test_util.run_deprecated_v1 def testFilterGradientSamePaddingStrideTwo(self): self.ConstructAndTestGradient( batch=4, @@ -572,6 +582,7 @@ class Conv3DTest(test.TestCase): padding="SAME", test_input=False) + @test_util.run_deprecated_v1 def testInputGradientSamePaddingStrideThree(self): self.ConstructAndTestGradient( batch=2, @@ -583,6 +594,7 @@ class Conv3DTest(test.TestCase): padding="SAME", test_input=True) + @test_util.run_deprecated_v1 def testFilterGradientSamePaddingStrideThree(self): self.ConstructAndTestGradient( batch=2, @@ -594,6 +606,7 @@ class Conv3DTest(test.TestCase): padding="SAME", test_input=False) + @test_util.run_deprecated_v1 def testInputGradientSamePaddingDifferentStrides(self): self.ConstructAndTestGradient( batch=1, @@ -605,6 +618,7 @@ class Conv3DTest(test.TestCase): padding="SAME", test_input=True) + @test_util.run_deprecated_v1 def testFilterGradientKernelSizeMatchesInputSize(self): self.ConstructAndTestGradient( batch=2, @@ -616,6 +630,7 @@ class Conv3DTest(test.TestCase): padding="VALID", test_input=False) + @test_util.run_deprecated_v1 def testInputGradientKernelSizeMatchesInputSize(self): self.ConstructAndTestGradient( batch=2, @@ -640,6 +655,7 @@ class Conv3DTest(test.TestCase): # Test the fast path in gemm_pack_rhs/mkldnn_gemm_pack, when channel # dimension is a multiple of packet size. + @test_util.run_deprecated_v1 def testInputGradientValidPaddingStrideOneFastPath(self): self.ConstructAndTestGradient( batch=2, @@ -651,6 +667,7 @@ class Conv3DTest(test.TestCase): padding="VALID", test_input=True) + @test_util.run_deprecated_v1 def testFilterGradientValidPaddingStrideOneFastPath(self): self.ConstructAndTestGradient( batch=2, @@ -715,8 +732,8 @@ class Conv3DTest(test.TestCase): expected_grad = gradients_impl.gradients(expected, t1 if mode == "input" else t2)[0] # "values" consists of two tensors for two backprops - actual_value = sess.run(actual_grad) - expected_value = sess.run(expected_grad) + actual_value = self.evaluate(actual_grad) + expected_value = self.evaluate(expected_grad) self.assertShapeEqual(actual_value, actual_grad) self.assertShapeEqual(expected_value, expected_grad) print("expected = ", expected_value) diff --git a/tensorflow/python/kernel_tests/conv_ops_test.py b/tensorflow/python/kernel_tests/conv_ops_test.py index 0ccbbf155c..2f6f3bb383 100644 --- a/tensorflow/python/kernel_tests/conv_ops_test.py +++ b/tensorflow/python/kernel_tests/conv_ops_test.py @@ -908,8 +908,8 @@ class Conv2DTest(test.TestCase): conv = gradients_impl.gradients(conv_forward, t1)[0] conv_2 = gradients_impl.gradients(conv_forward_2, t1)[0] # "values" consists of two tensors for two backprops - value = sess.run(conv) - value_2 = sess.run(conv_2) + value = self.evaluate(conv) + value_2 = self.evaluate(conv_2) self.assertShapeEqual(value, conv) self.assertShapeEqual(value_2, conv_2) tf_logging.info("expected = ", value_2) @@ -961,8 +961,8 @@ class Conv2DTest(test.TestCase): conv_forward_2 = test_util.NCHWToNHWC(conv_forward_2) conv = gradients_impl.gradients(conv_forward, t2)[0] conv_2 = gradients_impl.gradients(conv_forward, t2)[0] - value = sess.run(conv) - value_2 = sess.run(conv_2) + value = self.evaluate(conv) + value_2 = self.evaluate(conv_2) self.assertShapeEqual(value, conv) self.assertShapeEqual(value_2, conv_2) tf_logging.info("expected = ", value_2) @@ -1545,7 +1545,7 @@ class DepthwiseConv2DTest(test.TestCase): t2 = constant_op.constant(x2, shape=filter_in_sizes) conv = nn_impl.depthwise_conv2d( t1, t2, strides=[1, stride, stride, 1], padding=padding) - value = sess.run(conv) + value = self.evaluate(conv) tf_logging.info("value = ", value) self.assertArrayNear(expected, np.ravel(value), 1e-5) self.assertShapeEqual(value, conv) @@ -1667,9 +1667,9 @@ class SeparableConv2DTest(test.TestCase): if data_format == "NCHW": conv = array_ops.transpose(conv, [0, 2, 3, 1]) - value = sess.run(conv) + value = self.evaluate(conv) tf_logging.info("value = ", value) - self.assertArrayNear(expected, np.ravel(value), 1e-5) + self.assertArrayNear(expected, np.ravel(value), 1e-3) self.assertShapeEqual(value, conv) def _testSeparableConv2D(self, data_format): @@ -1774,10 +1774,10 @@ class DeepConv2DTest(test.TestCase): conv = nn_ops.conv2d(t1, t2, strides=strides, padding=padding) os.environ["TF_USE_DEEP_CONV2D"] = "0" - values_expect = sess.run([conv]) + values_expect = self.evaluate([conv]) os.environ["TF_USE_DEEP_CONV2D"] = "1" - values_test = sess.run([conv]) + values_test = self.evaluate([conv]) self.assertAllClose(values_expect, values_test, rtol=1e-5, atol=1e-5) diff --git a/tensorflow/python/kernel_tests/cross_grad_test.py b/tensorflow/python/kernel_tests/cross_grad_test.py index 0bd4006d6a..b397133fd7 100644 --- a/tensorflow/python/kernel_tests/cross_grad_test.py +++ b/tensorflow/python/kernel_tests/cross_grad_test.py @@ -18,6 +18,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import math_ops @@ -26,6 +27,7 @@ from tensorflow.python.platform import test class CrossOpTest(test.TestCase): + @test_util.run_deprecated_v1 def testGradientRandomValues(self): with self.cached_session(): us = [2, 3] diff --git a/tensorflow/python/kernel_tests/ctc_decoder_ops_test.py b/tensorflow/python/kernel_tests/ctc_decoder_ops_test.py index d818fbd75c..0d86d13c71 100644 --- a/tensorflow/python/kernel_tests/ctc_decoder_ops_test.py +++ b/tensorflow/python/kernel_tests/ctc_decoder_ops_test.py @@ -25,6 +25,7 @@ from six.moves import zip_longest from tensorflow.python.framework import errors from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import ctc_ops from tensorflow.python.platform import test @@ -94,6 +95,7 @@ class CTCGreedyDecoderTest(test.TestCase): with self.assertRaisesOpError(expected_err_re): sess.run(decoded_unwrapped + [log_probability]) + @test_util.run_deprecated_v1 def testCTCGreedyDecoder(self): """Test two batch entries - best path decoder.""" max_time_steps = 6 @@ -170,6 +172,7 @@ class CTCGreedyDecoderTest(test.TestCase): self._testCTCDecoder(ctc_ops.ctc_greedy_decoder, inputs, seq_lens, log_prob_truth, decode_truth) + @test_util.run_deprecated_v1 def testCTCDecoderBeamSearch(self): """Test one batch, two beams - hibernating beam search.""" # max_time_steps == 8 diff --git a/tensorflow/python/kernel_tests/ctc_loss_op_test.py b/tensorflow/python/kernel_tests/ctc_loss_op_test.py index cfc7cb98aa..e6b5835079 100644 --- a/tensorflow/python/kernel_tests/ctc_loss_op_test.py +++ b/tensorflow/python/kernel_tests/ctc_loss_op_test.py @@ -23,9 +23,16 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors_impl +from tensorflow.python.framework import ops +from tensorflow.python.framework import random_seed from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import test_util +from tensorflow.python.ops import array_ops from tensorflow.python.ops import ctc_ops from tensorflow.python.ops import gradients_impl +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import random_ops +from tensorflow.python.ops import sparse_ops from tensorflow.python.platform import test @@ -52,6 +59,24 @@ def SimpleSparseTensorFrom(x): return sparse_tensor.SparseTensor(x_ix, x_val, x_shape) +def _ctc_loss_v2(labels, inputs, sequence_length, + preprocess_collapse_repeated=False, + ctc_merge_repeated=True, + ignore_longer_outputs_than_inputs=False, + time_major=True): + """Call ctc_loss_v2 with v1 args.""" + assert not preprocess_collapse_repeated + assert ctc_merge_repeated + assert not ignore_longer_outputs_than_inputs + return ctc_ops.ctc_loss_v2( + labels=labels, + logits=inputs, + logit_length=sequence_length, + label_length=None, + blank_index=-1, + logits_time_major=time_major) + + class CTCLossTest(test.TestCase): def _testCTCLoss(self, @@ -66,7 +91,7 @@ class CTCLossTest(test.TestCase): inputs_t = constant_op.constant(inputs) with self.cached_session(use_gpu=False) as sess: - loss = ctc_ops.ctc_loss( + loss = _ctc_loss_v2( inputs=inputs_t, labels=labels, sequence_length=seq_lens) grad = gradients_impl.gradients(loss, [inputs_t])[0] @@ -74,13 +99,14 @@ class CTCLossTest(test.TestCase): self.assertShapeEqual(grad_truth, grad) if expected_err_re is None: - (tf_loss, tf_grad) = sess.run([loss, grad]) + (tf_loss, tf_grad) = self.evaluate([loss, grad]) self.assertAllClose(tf_loss, loss_truth, atol=1e-6) self.assertAllClose(tf_grad, grad_truth, atol=1e-6) else: with self.assertRaisesOpError(expected_err_re): - sess.run([loss, grad]) + self.evaluate([loss, grad]) + @test_util.run_deprecated_v1 def testBasic(self): """Test two batch entries.""" # Input and ground truth from Alex Graves' implementation. @@ -216,6 +242,7 @@ class CTCLossTest(test.TestCase): self._testCTCLoss(inputs, seq_lens, labels, loss_truth, grad_truth) + @test_util.run_deprecated_v1 def test_time_major(self): """Testing time_major param. @@ -234,17 +261,18 @@ class CTCLossTest(test.TestCase): inputs_t_transposed = constant_op.constant(inputs.transpose(1, 0, 2)) with self.session(use_gpu=False) as sess: - loss = ctc_ops.ctc_loss( + loss = _ctc_loss_v2( inputs=inputs_t, labels=labels, sequence_length=seq_lens) - loss_transposed = ctc_ops.ctc_loss( + loss_transposed = _ctc_loss_v2( inputs=inputs_t_transposed, labels=labels, sequence_length=seq_lens, time_major=False) - (tf_loss, tf_loss_transposed) = sess.run([loss, loss_transposed]) + (tf_loss, tf_loss_transposed) = self.evaluate([loss, loss_transposed]) self.assertAllEqual(tf_loss, tf_loss_transposed) + @test_util.run_deprecated_v1 def testInvalidSecondGradient(self): inputs = np.random.randn(2, 2, 3).astype(np.float32) inputs_t = constant_op.constant(inputs) @@ -253,7 +281,7 @@ class CTCLossTest(test.TestCase): v = [1.0] with self.session(use_gpu=False): - loss = ctc_ops.ctc_loss( + loss = _ctc_loss_v2( inputs=inputs_t, labels=labels, sequence_length=seq_lens) # Taking ths second gradient should fail, since it is not # yet supported. @@ -261,6 +289,7 @@ class CTCLossTest(test.TestCase): "explicitly disabled"): _ = gradients_impl._hessian_vector_product(loss, [inputs_t], v) + @test_util.run_deprecated_v1 def testEmptyBatch(self): inputs = constant_op.constant([], dtype=dtypes.float32, shape=(1, 0, 2)) sequence_lengths = constant_op.constant([], dtype=dtypes.int32) @@ -272,7 +301,546 @@ class CTCLossTest(test.TestCase): with self.session(use_gpu=False) as sess: with self.assertRaisesRegexp(errors_impl.InvalidArgumentError, "batch_size must not be 0"): - sess.run(ctc_ops.ctc_loss(labels, inputs, sequence_lengths)) + sess.run(_ctc_loss_v2(labels, inputs, sequence_lengths)) + + +class CTCLossTestV2(test.TestCase): + + @test_util.run_deprecated_v1 + def testCtcLossV2(self): + random_seed.set_random_seed(5) + + batch_size = 8 + num_labels = 6 + max_label_length = 5 + num_frames = 12 + + labels = random_ops.random_uniform( + [batch_size, max_label_length], minval=1, maxval=num_labels, + dtype=dtypes.int64) + logits = random_ops.random_uniform([num_frames, batch_size, num_labels]) + + label_length = random_ops.random_uniform( + [batch_size], minval=2, maxval=max_label_length, dtype=dtypes.int64) + label_mask = array_ops.sequence_mask( + label_length, maxlen=max_label_length, dtype=label_length.dtype) + labels *= label_mask + logit_length = [num_frames] * batch_size + + ref_loss = ctc_ops.ctc_loss_v2( + labels=labels, + logits=logits, + label_length=label_length, + logit_length=logit_length) + ref_grad = gradients_impl.gradients(ref_loss, [logits]) + + sparse_labels = ctc_ops.dense_labels_to_sparse(labels, label_length) + + def assert_same_loss_and_grads(loss): + with self.cached_session() as sess: + self.assertAllClose(*self.evaluate([loss, ref_loss])) + grad = gradients_impl.gradients(loss, [logits]) + self.assertAllClose( + *self.evaluate([grad, ref_grad]), rtol=2e-06, atol=2e-06) + + assert_same_loss_and_grads( + ctc_ops.ctc_loss_v2( + labels=sparse_labels, + logits=logits, + label_length=label_length, + logit_length=logit_length, + blank_index=0)) + + @test_util.run_deprecated_v1 + def testCtcLossDenseIsSameAsCtcLoss(self): + with ops.device("/GPU:0" if test.is_gpu_available() else "/CPU:0"): + random_seed.set_random_seed(5) + + batch_size = 8 + num_labels = 6 + label_length = 5 + num_frames = 12 + logits = random_ops.random_uniform([num_frames, batch_size, num_labels]) + labels = random_ops.random_uniform( + [batch_size, label_length], minval=1, maxval=num_labels, + dtype=dtypes.int64) + + label_lengths = random_ops.random_uniform( + [batch_size], minval=2, maxval=label_length, dtype=dtypes.int64) + label_mask = array_ops.sequence_mask( + label_lengths, maxlen=label_length, dtype=label_lengths.dtype) + labels *= label_mask + + logit_lengths = [num_frames] * batch_size + + ctc_loss = ctc_ops.ctc_loss_dense( + labels=labels, + logits=logits, + label_length=label_lengths, + logit_length=logit_lengths) + ctc_loss_grads = gradients_impl.gradients(ctc_loss, [logits])[0] + + # Shift labels down by one (move blank from 0 to num_labels -1) + tf_ctc_loss_labels = math_ops.cast(labels, dtypes.int32) - 1 + tf_nn_ctc_logits = array_ops.concat([ + logits[:, :, 1:], + logits[:, :, 0:1], + ], axis=2) + + tf_ctc_loss_labels = ctc_ops.dense_labels_to_sparse( + tf_ctc_loss_labels, label_lengths) + + tf_nn_ctc_loss = ctc_ops.ctc_loss( + labels=tf_ctc_loss_labels, + inputs=tf_nn_ctc_logits, + sequence_length=logit_lengths, + time_major=True) + tf_nn_ctc_grads = gradients_impl.gradients(tf_nn_ctc_loss, [logits])[0] + + with self.cached_session() as sess: + for _ in range(32): + self.assertAllClose(*self.evaluate([ctc_loss, tf_nn_ctc_loss])) + self.assertAllClose( + *self.evaluate([ctc_loss_grads, tf_nn_ctc_grads]), + rtol=2e-06, + atol=2e-06) + + @test_util.run_deprecated_v1 + def testCtcLossDenseUniqueFastPathIsSameAsCtcLoss(self): + random_seed.set_random_seed(5) + + batch_size = 8 + num_labels = 6 + label_length = 5 + num_frames = 12 + logits = random_ops.random_uniform([num_frames, batch_size, num_labels]) + labels = random_ops.random_uniform( + [batch_size, label_length], minval=1, maxval=num_labels, + dtype=dtypes.int64) + + label_lengths = random_ops.random_uniform( + [batch_size], minval=2, maxval=label_length, dtype=dtypes.int64) + label_mask = array_ops.sequence_mask( + label_lengths, maxlen=label_length, dtype=label_lengths.dtype) + labels *= label_mask + + logit_lengths = [num_frames] * batch_size + + ctc_loss = ctc_ops.ctc_loss_dense( + labels=labels, + logits=logits, + label_length=label_lengths, + logit_length=logit_lengths, + unique=ctc_ops.ctc_unique_labels(labels)) + ctc_loss_grads = gradients_impl.gradients(ctc_loss, [logits])[0] + + # Shift labels down by one (move blank from 0 to num_labels -1) + tf_ctc_loss_labels = math_ops.cast(labels, dtypes.int32) - 1 + tf_nn_ctc_logits = array_ops.concat([ + logits[:, :, 1:], + logits[:, :, 0:1], + ], axis=2) + + tf_ctc_loss_labels = ctc_ops.dense_labels_to_sparse( + tf_ctc_loss_labels, label_lengths) + + tf_nn_ctc_loss = ctc_ops.ctc_loss( + labels=tf_ctc_loss_labels, + inputs=tf_nn_ctc_logits, + sequence_length=logit_lengths, + time_major=True) + tf_nn_ctc_grads = gradients_impl.gradients(tf_nn_ctc_loss, [logits])[0] + + with self.cached_session() as sess: + for _ in range(32): + self.assertAllClose(*self.evaluate([ctc_loss, tf_nn_ctc_loss])) + self.assertAllClose( + *self.evaluate([ctc_loss_grads, tf_nn_ctc_grads]), + rtol=2e-06, + atol=2e-06) + + @test_util.run_deprecated_v1 + def testCtcLossDenseWithBlankIndexIsSameAsCtcLoss(self): + random_seed.set_random_seed(5) + + batch_size = 8 + num_labels = 6 + label_length = 5 + num_frames = 12 + logits = random_ops.random_uniform([num_frames, batch_size, num_labels]) + labels = random_ops.random_uniform( + [batch_size, label_length], minval=0, maxval=num_labels-1, + dtype=dtypes.int64) + + label_lengths = random_ops.random_uniform( + [batch_size], minval=2, maxval=label_length, dtype=dtypes.int64) + label_mask = array_ops.sequence_mask( + label_lengths, maxlen=label_length, dtype=label_lengths.dtype) + labels *= label_mask + + logit_lengths = [num_frames] * batch_size + + tf_ctc_loss_labels = math_ops.cast(labels, dtypes.int32) + tf_ctc_loss_labels = ctc_ops.dense_labels_to_sparse( + tf_ctc_loss_labels, label_lengths) + + tf_nn_ctc_loss = ctc_ops.ctc_loss( + labels=tf_ctc_loss_labels, + inputs=logits, + sequence_length=logit_lengths, + time_major=True) + tf_nn_ctc_grads = gradients_impl.gradients(tf_nn_ctc_loss, [logits])[0] + + # Shift the blank logits/labels to be somewhere in the middle. + blank_index = 2 + shifted_logits = array_ops.concat([ + logits[:, :, :blank_index], + logits[:, :, -1:], + logits[:, :, blank_index:-1], + ], axis=2) + shifted_labels = array_ops.where(labels < blank_index, labels, labels + 1) + + ctc_loss = ctc_ops.ctc_loss_dense( + labels=shifted_labels, + logits=shifted_logits, + label_length=label_lengths, + logit_length=logit_lengths, + blank_index=blank_index) + ctc_loss_grads = gradients_impl.gradients(ctc_loss, [logits])[0] + + with self.cached_session() as sess: + for _ in range(32): + self.assertAllClose(*self.evaluate([ctc_loss, tf_nn_ctc_loss])) + self.assertAllClose( + *self.evaluate([ctc_loss_grads, tf_nn_ctc_grads]), + rtol=2e-06, + atol=2e-06) + + @test_util.run_deprecated_v1 + def testCtcLossDenseWithNegativeBlankIndexIsSameAsCtcLoss(self): + with ops.device("/GPU:0" if test.is_gpu_available() else "/CPU:0"): + random_seed.set_random_seed(5) + + batch_size = 8 + num_labels = 6 + label_length = 5 + num_frames = 12 + logits = random_ops.random_uniform([num_frames, batch_size, num_labels]) + labels = random_ops.random_uniform( + [batch_size, label_length], minval=0, maxval=num_labels-1, + dtype=dtypes.int64) + + label_lengths = random_ops.random_uniform( + [batch_size], minval=2, maxval=label_length, dtype=dtypes.int64) + label_mask = array_ops.sequence_mask( + label_lengths, maxlen=label_length, dtype=label_lengths.dtype) + labels *= label_mask + + logit_lengths = [num_frames] * batch_size + + ctc_loss = ctc_ops.ctc_loss_dense( + labels=labels, + logits=logits, + label_length=label_lengths, + logit_length=logit_lengths, + blank_index=-1) + ctc_loss_grads = gradients_impl.gradients(ctc_loss, [logits])[0] + + tf_ctc_loss_labels = math_ops.cast(labels, dtypes.int32) + tf_ctc_loss_labels = ctc_ops.dense_labels_to_sparse( + tf_ctc_loss_labels, label_lengths) + + tf_nn_ctc_loss = ctc_ops.ctc_loss( + labels=tf_ctc_loss_labels, + inputs=logits, + sequence_length=logit_lengths, + time_major=True) + tf_nn_ctc_grads = gradients_impl.gradients(tf_nn_ctc_loss, [logits])[0] + + with self.cached_session() as sess: + for _ in range(32): + self.assertAllClose(*self.evaluate([ctc_loss, tf_nn_ctc_loss])) + self.assertAllClose( + *self.evaluate([ctc_loss_grads, tf_nn_ctc_grads]), + rtol=2e-06, + atol=2e-06) + + @test_util.run_deprecated_v1 + def testCollapseRepeated(self): + collapsed, new_seq_lengths = ctc_ops.collapse_repeated( + labels=[[1, 3, 3, 3, 0], + [1, 4, 4, 4, 0], + [4, 2, 2, 9, 4]], + seq_length=[4, 5, 5]) + self.assertAllEqual(new_seq_lengths, [2, 3, 4]) + self.assertAllEqual( + collapsed, + [[1, 3, 0, 0], + [1, 4, 0, 0], + [4, 2, 9, 4]]) + + @test_util.run_deprecated_v1 + def testCollapseRepeatedPreservesDtypes(self): + collapsed, new_seq_lengths = ctc_ops.collapse_repeated( + labels=constant_op.constant( + [[1, 3, 3, 3, 0], + [1, 4, 4, 4, 0], + [4, 2, 2, 9, 4]], + dtype=dtypes.int64), + seq_length=constant_op.constant([4, 5, 5], dtype=dtypes.int64)) + self.assertEqual(new_seq_lengths.dtype, dtypes.int64) + self.assertEqual(collapsed.dtype, dtypes.int64) + self.assertAllEqual(new_seq_lengths, [2, 3, 4]) + self.assertAllEqual( + collapsed, + [[1, 3, 0, 0], + [1, 4, 0, 0], + [4, 2, 9, 4]]) + + @test_util.run_deprecated_v1 + def testCollapseRepeatedExtraPadding(self): + collapsed, new_seq_lengths = ctc_ops.collapse_repeated( + labels=[[1, 3, 3, 3, 0, 0, 0], + [1, 4, 4, 4, 0, 1, 2], + [4, 2, 2, 9, 4, 0, 0]], + seq_length=[4, 5, 5]) + self.assertAllEqual(new_seq_lengths, [2, 3, 4]) + self.assertAllEqual( + collapsed, + [[1, 3, 0, 0], + [1, 4, 0, 0], + [4, 2, 9, 4]]) + + @test_util.run_deprecated_v1 + def testCollapseRepeatedFrontRepeats(self): + collapsed, new_seq_lengths = ctc_ops.collapse_repeated( + labels=[[1, 1, 1, 2, 2], + [1, 1, 1, 2, 2], + [1, 1, 1, 2, 2]], + seq_length=[5, 4, 3]) + self.assertAllEqual(new_seq_lengths, [2, 2, 1]) + self.assertAllEqual( + collapsed, + [[1, 2], + [1, 2], + [1, 0]]) + + @test_util.run_deprecated_v1 + def testCollapseRepeatedAllLabelsTheSame(self): + collapsed, new_seq_lengths = ctc_ops.collapse_repeated( + labels=[[1, 1, 1, 1, 1], + [1, 1, 1, 1, 1], + [1, 1, 1, 1, 1]], + seq_length=[4, 5, 1]) + self.assertAllEqual(new_seq_lengths, [1, 1, 1]) + self.assertAllEqual( + collapsed, + [[1], + [1], + [1]]) + + def testDenseSequencesToSparse(self): + labels = [[1, 3, 3, 3, 0], + [1, 4, 4, 4, 0], + [4, 2, 2, 9, 4]] + length = [4, 5, 5] + sparse = ctc_ops.dense_labels_to_sparse(labels, length) + new_dense = sparse_ops.sparse_tensor_to_dense(sparse) + + self.assertAllEqual(labels, new_dense) + + padded_labels = [[1, 3, 3, 3, 0, 0, 0, 0], + [1, 4, 4, 4, 0, 0, 0, 0], + [4, 2, 2, 9, 4, 0, 0, 0]] + length = [4, 5, 5] + sparse = ctc_ops.dense_labels_to_sparse(padded_labels, length) + padded_dense = sparse_ops.sparse_tensor_to_dense(sparse) + + self.assertAllEqual(padded_dense, new_dense) + + @test_util.run_deprecated_v1 + def testUnique(self): + labels = [ + [3, 4, 4, 3], + [1, 1, 1, 0], + ] + unique, idx = ctc_ops.ctc_unique_labels(labels) + self.assertAllEqual([ + [3, 4, 0, 0], + [1, 0, 0, 0], + ], unique) + self.assertAllEqual([ + [0, 1, 1, 0], + [0, 0, 0, 1], + ], idx) + + @test_util.run_deprecated_v1 + def testSumStates(self): + idx = [ + [0, 1, 0, 1], + [0, 0, 0, 1], + ] + states = math_ops.log([ + [[1.0, 2.0, 3.0, 4.0], + [5.0, 6.0, 7.0, 8.0]], + [[0.1, 0.2, 0.3, 0.4], + [0.5, 0.6, 0.7, 0.8]], + ]) + sum_of_states = math_ops.exp(ctc_ops._sum_states(idx, states)) + self.assertAllClose([ + [[4.0, 6.0, 0.0, 0.0], + [18.0, 8.0, 0.0, 0.0]], + [[0.4, 0.6, 0.0, 0.0], + [1.8, 0.8, 0.0, 0.0]] + ], sum_of_states) + + @test_util.run_deprecated_v1 + def testStateToOlabel(self): + labels = [ + [3, 4, 3, 4], + [1, 1, 1, 0], + ] + num_labels = 8 + + # 3 frames, 2 batch, 10 states (5 label, 5 blank). + states = [ + [[0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.20], + [0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.30]], + [[1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0], + [2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0]], + [[11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0], + [21.0, 22.0, 23.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0]], + ] + labels = ops.convert_to_tensor(labels) + states = math_ops.log(states) + olabel = ctc_ops._state_to_olabel(labels, num_labels, states) + olabel = math_ops.exp(olabel) + blank = olabel[:, :, 0] + self.assertAllClose(blank, [ + [0.16 + 0.17 + 0.18 + 0.19 + 0.20, + 0.26 + 0.27 + 0.28 + 0.29 + 0.30], + [1.6 + 1.7 + 1.8 + 1.9 + 2.0, + 2.6 + 2.7 + 2.8 + 2.9 + 3.0], + [16.0 + 17.0 + 18.0 + 19.0 + 20.0, + 26.0 + 27.0 + 28.0 + 29.0 + 30.0] + ]) + self.assertAllClose(olabel[:, :, 1:], [ + [[0.0, 0.0, 0.12 + 0.14, 0.13 + 0.15, 0.0, 0.0, 0.0], + [0.22 + 0.23 + 0.24, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]], + [[0.0, 0.0, 1.2 + 1.4, 1.3 + 1.5, 0.0, 0.0, 0.0], + [2.2 + 2.3 + 2.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]], + [[0.0, 0.0, 12.0 + 14.0, 13.0 + 15.0, 0.0, 0.0, 0.0], + [22.0 + 23.0 + 24.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]], + ]) + + @test_util.run_deprecated_v1 + def testStateToOlabelUnique(self): + labels = [ + [3, 4, 3, 4], + [1, 1, 1, 0], + ] + num_labels = 8 + + # 3 frames, 2 batch, 10 states (5 label, 5 blank). + states = [ + [[0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.20], + [0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.30]], + [[1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0], + [2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0]], + [[11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0], + [21.0, 22.0, 23.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0]], + ] + labels = ops.convert_to_tensor(labels) + states = math_ops.log(states) + olabel = ctc_ops._state_to_olabel_unique( + labels, num_labels, states, ctc_ops.ctc_unique_labels(labels)) + olabel = math_ops.exp(olabel) + blank = olabel[:, :, 0] + self.assertAllClose(blank, [ + [0.16 + 0.17 + 0.18 + 0.19 + 0.20, + 0.26 + 0.27 + 0.28 + 0.29 + 0.30], + [1.6 + 1.7 + 1.8 + 1.9 + 2.0, + 2.6 + 2.7 + 2.8 + 2.9 + 3.0], + [16.0 + 17.0 + 18.0 + 19.0 + 20.0, + 26.0 + 27.0 + 28.0 + 29.0 + 30.0]]) + self.assertAllClose(olabel[:, :, 1:], [ + [[0.0, 0.0, 0.12 + 0.14, 0.13 + 0.15, 0.0, 0.0, 0.0], + [0.22 + 0.23 + 0.24, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]], + [[0.0, 0.0, 1.2 + 1.4, 1.3 + 1.5, 0.0, 0.0, 0.0], + [2.2 + 2.3 + 2.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]], + [[0.0, 0.0, 12.0 + 14.0, 13.0 + 15.0, 0.0, 0.0, 0.0], + [22.0 + 23.0 + 24.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]], + ]) + + @test_util.run_deprecated_v1 + def testScan(self): + with ops.device("/GPU:0" if test.is_gpu_available() else "/CPU:0"): + out = ctc_ops._scan( + lambda accum, elem: accum + elem, + constant_op.constant([1.0, 2.0, 3.0]), 23.0) + self.assertAllEqual([24.0, 26.0, 29.0], out) + + out = ctc_ops._scan( + lambda a, e: a + e, + constant_op.constant([1.0, 2.0, 3.0]), 23.0, + inclusive=True) + self.assertAllEqual([23.0, 24.0, 26.0, 29.0], out) + + out = ctc_ops._scan( + lambda a, e: a + e, + constant_op.constant([1.0, 2.0, 3.0]), 23.0, + reverse=True) + self.assertAllEqual([29.0, 28.0, 26.0], out) + + out = ctc_ops._scan( + lambda a, e: a + e, + constant_op.constant([1.0, 2.0, 3.0]), 23.0, + reverse=True, + inclusive=True) + self.assertAllEqual([29.0, 28.0, 26.0, 23.0], out) + + out = ctc_ops._scan( + lambda a, e: a + e, + constant_op.constant([[0.0, 1.0], [2.0, 3.0], [4.0, 5.0]]), + constant_op.constant([23.0, 24.0])) + self.assertAllEqual([[23.0, 25.0], [25.0, 28.0], [29.0, 33.0]], out) + + @test_util.run_deprecated_v1 + def testScanCapturesVariables(self): + with self.cached_session() as sess: + x = random_ops.random_uniform([]) + fn = lambda accum, elem: accum + x * elem + out = ctc_ops._scan(fn, constant_op.constant([0.0, 1.0, 2.0]), 23.0) + self.assertAllEqual(*sess.run([ + [23.0 + x * 0.0, 23.0 + x * 1.0, 23.0 + x * 3.0], out + ])) + + @test_util.run_deprecated_v1 + def testScanMultipleAccumulators(self): + with ops.device("/GPU:0" if test.is_gpu_available() else "/CPU:0"): + def fn(accum, elem): + accum_a, accum_b = accum + return accum_a + elem, accum_b * elem + out = ctc_ops._scan( + fn, constant_op.constant([1.0, 2.0, 3.0]), + (23.0, constant_op.constant([1.0, 2.0]))) + a, b = out + self.assertAllEqual([24.0, 26.0, 29.0], a) + self.assertAllEqual([[1.0, 2.0], [2.0, 4.0], [6.0, 12.0]], b) + + @test_util.run_deprecated_v1 + def testScanMultipleElements(self): + with ops.device("/GPU:0" if test.is_gpu_available() else "/CPU:0"): + def fn(accum, elem): + elem_a, elem_b = elem + return accum + (elem_a * elem_b) + elems_a = constant_op.constant([1.0, 2.0, 3.0]) + elems_b = constant_op.constant([[1.0, 2.0], [2.0, 3.0], [3.0, 4.0]]) + out = ctc_ops._scan( + fn, (elems_a, elems_b), + initial=constant_op.constant([0.0, 0.0])) + self.assertAllEqual( + [[1.0, 2.0], [5.0, 8.0], [14.0, 20.0]], out) if __name__ == "__main__": test.main() diff --git a/tensorflow/python/kernel_tests/cwise_ops_binary_test.py b/tensorflow/python/kernel_tests/cwise_ops_binary_test.py index 8028f93a8c..49dbbb125a 100644 --- a/tensorflow/python/kernel_tests/cwise_ops_binary_test.py +++ b/tensorflow/python/kernel_tests/cwise_ops_binary_test.py @@ -77,23 +77,23 @@ class BinaryOpTest(test.TestCase): def _compareCpu(self, x, y, np_func, tf_func, also_compare_variables=False): np_ans = np_func(x, y) - with self.test_session(use_gpu=False): + with test_util.force_cpu(): inx = ops.convert_to_tensor(x) iny = ops.convert_to_tensor(y) out = tf_func(inx, iny) - tf_cpu = out.eval() + tf_cpu = self.evaluate(out) # Test that the op takes precedence over numpy operators. - np_left = tf_func(x, iny).eval() - np_right = tf_func(inx, y).eval() + np_left = self.evaluate(tf_func(x, iny)) + np_right = self.evaluate(tf_func(inx, y)) if also_compare_variables: var_x = variables.Variable(x) var_y = variables.Variable(y) - variables.global_variables_initializer().run() + self.evaluate(variables.global_variables_initializer()) print(type(x), type(y), type(var_x), type(var_y)) print(type(tf_func(x, var_y)), type(tf_func(var_x, y))) - np_var_left = tf_func(x, var_y).eval() - np_var_right = tf_func(var_x, y).eval() + np_var_left = self.evaluate(tf_func(x, var_y)) + np_var_right = self.evaluate(tf_func(var_x, y)) if np_ans.dtype != np.object: self.assertAllClose(np_ans, tf_cpu) @@ -174,11 +174,11 @@ class BinaryOpTest(test.TestCase): def _compareGpu(self, x, y, np_func, tf_func): np_ans = np_func(x, y) - with self.test_session(force_gpu=test_util.is_gpu_available()): + with test_util.use_gpu(): inx = ops.convert_to_tensor(x) iny = ops.convert_to_tensor(y) out = tf_func(inx, iny) - tf_gpu = out.eval() + tf_gpu = self.evaluate(out) self.assertAllClose(np_ans, tf_gpu) self.assertShapeEqual(np_ans, out) # TODO(zhifengc/ke): make gradient checker work on GPU. @@ -196,6 +196,7 @@ class BinaryOpTest(test.TestCase): self._compareGradientY(x, y, np_func, tf_func) self._compareGpu(x, y, np_func, tf_func) + @test_util.run_deprecated_v1 def testFloatBasic(self): x = np.linspace(-5, 20, 15).reshape(1, 3, 5).astype(np.float32) y = np.linspace(20, -5, 15).reshape(1, 3, 5).astype(np.float32) @@ -233,6 +234,7 @@ class BinaryOpTest(test.TestCase): except ImportError as e: tf_logging.warn("Cannot test special functions: %s" % str(e)) + @test_util.run_deprecated_v1 def testFloatDifferentShapes(self): x = np.array([1, 2, 3, 4]).reshape(2, 2).astype(np.float32) y = np.array([1, 2]).reshape(2, 1).astype(np.float32) @@ -252,14 +254,17 @@ class BinaryOpTest(test.TestCase): y = np.array([1, 2]).reshape(2, 1).astype(np.int32) var_x = variables.Variable(x) var_y = variables.Variable(y) + with self.cached_session() as sess: - sess.run([var_x.initializer, var_y.initializer]) - left_result = (var_x * y).eval() - right_result = (x * var_y).eval() + self.evaluate([var_x.initializer, var_y.initializer]) + left_result = self.evaluate(var_x * y) + right_result = self.evaluate(x * var_y) + np_result = x * y self.assertAllEqual(np_result, left_result) self.assertAllEqual(np_result, right_result) + @test_util.run_deprecated_v1 def testDoubleBasic(self): x = np.linspace(-5, 20, 15).reshape(1, 3, 5).astype(np.float64) y = np.linspace(20, -5, 15).reshape(1, 3, 5).astype(np.float64) @@ -351,6 +356,7 @@ class BinaryOpTest(test.TestCase): self._compareBoth(x, y, np.floor_divide, _FLOORDIV) self._compareBoth(x, y, np.mod, _MOD) + @test_util.run_deprecated_v1 def testComplex64Basic(self): x = np.complex(1, 1) * np.linspace(-10, 10, 6).reshape(1, 3, 2).astype( np.complex64) @@ -365,6 +371,7 @@ class BinaryOpTest(test.TestCase): self._compareBoth(x, y, np.multiply, _MUL) self._compareBoth(x, y + 0.1, np.true_divide, _TRUEDIV) + @test_util.run_deprecated_v1 def testComplex128Basic(self): x = np.complex(1, 1) * np.linspace(-10, 10, 6).reshape(1, 3, 2).astype( np.complex128) @@ -382,10 +389,10 @@ class BinaryOpTest(test.TestCase): def testStringComparison(self): x = np.array([["abc", "bh"], ["c", ""]]) y = np.array([["abc", "bh"], ["def", "hi"]]) - with self.test_session(use_gpu=False) as sess: + with test_util.force_cpu(): cmp_eq = math_ops.equal(x, y) cmp_not_eq = math_ops.not_equal(x, y) - values = sess.run([cmp_eq, cmp_not_eq]) + values = self.evaluate([cmp_eq, cmp_not_eq]) self.assertAllEqual([[True, True], [False, False]], values[0]) self.assertAllEqual([[False, False], [True, True]], values[1]) @@ -478,198 +485,263 @@ class BinaryOpTest(test.TestCase): ] self._testBCastByFunc(funcs, xs, ys) + @test_util.run_deprecated_v1 def testBCast_0A(self): self._testBCastA([1, 3, 2], [1]) + @test_util.run_deprecated_v1 def testBCast_0B(self): self._testBCastB([1, 3, 2], [1]) + @test_util.run_deprecated_v1 def testBCast_0C(self): self._testBCastC([1, 3, 2], [1]) + @test_util.run_deprecated_v1 def testBCast_0D(self): self._testBCastD([1, 3, 2], [1]) + @test_util.run_deprecated_v1 def testBCast_1A(self): self._testBCastA([1, 3, 2], [2]) + @test_util.run_deprecated_v1 def testBCast_1B(self): self._testBCastB([1, 3, 2], [2]) + @test_util.run_deprecated_v1 def testBCast_1C(self): self._testBCastC([1, 3, 2], [2]) + @test_util.run_deprecated_v1 def testBCast_1D(self): self._testBCastD([1, 3, 2], [2]) + @test_util.run_deprecated_v1 def testBCast_2A(self): self._testBCastA([1, 3, 2], [3, 2]) + @test_util.run_deprecated_v1 def testBCast_2B(self): self._testBCastB([1, 3, 2], [3, 2]) + @test_util.run_deprecated_v1 def testBCast_2C(self): self._testBCastC([1, 3, 2], [3, 2]) + @test_util.run_deprecated_v1 def testBCast_2D(self): self._testBCastD([1, 3, 2], [3, 2]) + @test_util.run_deprecated_v1 def testBCast_3A(self): self._testBCastA([1, 3, 2], [3, 1]) + @test_util.run_deprecated_v1 def testBCast_3B(self): self._testBCastB([1, 3, 2], [3, 1]) + @test_util.run_deprecated_v1 def testBCast_3C(self): self._testBCastC([1, 3, 2], [3, 1]) + @test_util.run_deprecated_v1 def testBCast_3D(self): self._testBCastD([1, 3, 2], [3, 1]) + @test_util.run_deprecated_v1 def testBCast_4A(self): self._testBCastA([1, 3, 2], [1, 3, 2]) + @test_util.run_deprecated_v1 def testBCast_4B(self): self._testBCastB([1, 3, 2], [1, 3, 2]) + @test_util.run_deprecated_v1 def testBCast_4C(self): self._testBCastC([1, 3, 2], [1, 3, 2]) + @test_util.run_deprecated_v1 def testBCast_4D(self): self._testBCastD([1, 3, 2], [1, 3, 2]) + @test_util.run_deprecated_v1 def testBCast_5A(self): self._testBCastA([1, 3, 2], [2, 3, 1]) + @test_util.run_deprecated_v1 def testBCast_5B(self): self._testBCastB([1, 3, 2], [2, 3, 1]) + @test_util.run_deprecated_v1 def testBCast_5C(self): self._testBCastC([1, 3, 2], [2, 3, 1]) + @test_util.run_deprecated_v1 def testBCast_5D(self): self._testBCastD([1, 3, 2], [2, 3, 1]) + @test_util.run_deprecated_v1 def testBCast_6A(self): self._testBCastA([1, 3, 2], [2, 1, 1]) + @test_util.run_deprecated_v1 def testBCast_6B(self): self._testBCastB([1, 3, 2], [2, 1, 1]) + @test_util.run_deprecated_v1 def testBCast_6C(self): self._testBCastC([1, 3, 2], [2, 1, 1]) + @test_util.run_deprecated_v1 def testBCast_6D(self): self._testBCastD([1, 3, 2], [2, 1, 1]) + @test_util.run_deprecated_v1 def testBCast_7A(self): self._testBCastA([1, 3, 2], [1, 3, 1]) + @test_util.run_deprecated_v1 def testBCast_7B(self): self._testBCastB([1, 3, 2], [1, 3, 1]) + @test_util.run_deprecated_v1 def testBCast_7C(self): self._testBCastC([1, 3, 2], [1, 3, 1]) + @test_util.run_deprecated_v1 def testBCast_7D(self): self._testBCastD([1, 3, 2], [1, 3, 1]) + @test_util.run_deprecated_v1 def testBCast_8A(self): self._testBCastA([2, 1, 5], [2, 3, 1]) + @test_util.run_deprecated_v1 def testBCast_8B(self): self._testBCastB([2, 1, 5], [2, 3, 1]) + @test_util.run_deprecated_v1 def testBCast_8C(self): self._testBCastC([2, 1, 5], [2, 3, 1]) + @test_util.run_deprecated_v1 def testBCast_8D(self): self._testBCastD([2, 1, 5], [2, 3, 1]) + @test_util.run_deprecated_v1 def testBCast_9A(self): self._testBCastA([2, 0, 5], [2, 0, 1]) + @test_util.run_deprecated_v1 def testBCast_9B(self): self._testBCastB([2, 0, 5], [2, 0, 1]) + @test_util.run_deprecated_v1 def testBCast_9C(self): self._testBCastC([2, 0, 5], [2, 0, 1]) + @test_util.run_deprecated_v1 def testBCast_9D(self): self._testBCastD([2, 0, 5], [2, 0, 1]) + @test_util.run_deprecated_v1 def testBCast_10A(self): self._testBCastA([2, 3, 0], [2, 3, 1]) + @test_util.run_deprecated_v1 def testBCast_10B(self): self._testBCastB([2, 3, 0], [2, 3, 1]) + @test_util.run_deprecated_v1 def testBCast_10C(self): self._testBCastC([2, 3, 0], [2, 3, 1]) + @test_util.run_deprecated_v1 def testBCast_10D(self): self._testBCastD([2, 3, 0], [2, 3, 1]) + @test_util.run_deprecated_v1 def testBCast_11A(self): self._testBCastA([1, 3, 2], [1, 3, 2]) + @test_util.run_deprecated_v1 def testBCast_11B(self): self._testBCastB([1, 3, 2], [1, 3, 2]) + @test_util.run_deprecated_v1 def testBCast_11C(self): self._testBCastC([1, 3, 2], [1, 3, 2]) + @test_util.run_deprecated_v1 def testBCast_11D(self): self._testBCastD([1, 3, 2], [1, 3, 2]) + @test_util.run_deprecated_v1 def testBCast_12A(self): self._testBCastA([1, 1, 1, 1, 3, 2], [1, 3, 2]) + @test_util.run_deprecated_v1 def testBCast_12B(self): self._testBCastB([1, 1, 1, 1, 3, 2], [1, 3, 2]) + @test_util.run_deprecated_v1 def testBCast_12C(self): self._testBCastC([1, 1, 1, 1, 3, 2], [1, 3, 2]) + @test_util.run_deprecated_v1 def testBCast_12D(self): self._testBCastD([1, 1, 1, 1, 3, 2], [1, 3, 2]) + @test_util.run_deprecated_v1 def testBCast_13A(self): self._testBCastA([1, 3, 2, 1, 1], [1]) + @test_util.run_deprecated_v1 def testBCast_13B(self): self._testBCastB([1, 3, 2, 1, 1], [1]) + @test_util.run_deprecated_v1 def testBCast_13C(self): self._testBCastC([1, 3, 2, 1, 1], [1]) + @test_util.run_deprecated_v1 def testBCast_13D(self): self._testBCastD([1, 3, 2, 1, 1], [1]) + @test_util.run_deprecated_v1 def testBCast_14A(self): self._testBCastA([2, 3, 1, 1, 5], [1]) + @test_util.run_deprecated_v1 def testBCast_14B(self): self._testBCastB([2, 3, 1, 1, 5], [1]) + @test_util.run_deprecated_v1 def testBCast_14C(self): self._testBCastC([2, 3, 1, 1, 5], [1]) + @test_util.run_deprecated_v1 def testBCast_14D(self): self._testBCastD([2, 3, 1, 1, 5], [1]) + @test_util.run_deprecated_v1 def testBCast_15A(self): self._testBCastA([10, 3, 1, 2], [3, 1, 2]) + @test_util.run_deprecated_v1 def testBCast_15B(self): self._testBCastB([10, 3, 1, 2], [3, 1, 2]) + @test_util.run_deprecated_v1 def testBCast_15C(self): self._testBCastC([10, 3, 1, 2], [3, 1, 2]) + @test_util.run_deprecated_v1 def testBCast_15D(self): self._testBCastD([10, 3, 1, 2], [3, 1, 2]) + @test_util.run_deprecated_v1 def testMismatchedDimensions(self): for func in [ math_ops.add, math_ops.subtract, math_ops.multiply, math_ops.div, _ADD, @@ -681,6 +753,7 @@ class BinaryOpTest(test.TestCase): ops.convert_to_tensor([10.0, 20.0, 30.0]), ops.convert_to_tensor([[40.0, 50.0], [60.0, 70.0]])) + @test_util.run_deprecated_v1 def testZeroPowGrad(self): with self.cached_session(): for dtype in (np.float16, np.float32, np.float64, np.complex64, @@ -691,6 +764,7 @@ class BinaryOpTest(test.TestCase): error = gradient_checker.compute_gradient_error(y, [], z, []) self.assertEqual(error, 0) + @test_util.run_deprecated_v1 def testComplexPowGrad(self): with self.cached_session(): for dtype in np.complex64, np.complex128: @@ -716,39 +790,39 @@ class BinaryOpTest(test.TestCase): def testPowNegativeExponent(self): for dtype in [np.int32, np.int64]: - with self.test_session(use_gpu=False) as sess: + with test_util.force_cpu(): with self.assertRaisesRegexp( errors_impl.InvalidArgumentError, "Integers to negative integer powers are not allowed"): x = np.array([5, 2]).astype(dtype) y = np.array([-2, 3]).astype(dtype) - sess.run(math_ops.pow(x, y)) + self.evaluate(math_ops.pow(x, y)) - with self.test_session(use_gpu=False) as sess: + with test_util.force_cpu(): with self.assertRaisesRegexp( errors_impl.InvalidArgumentError, "Integers to negative integer powers are not allowed"): x = np.array([5, 2]).astype(dtype) y = np.array([2, -3]).astype(dtype) - sess.run(math_ops.pow(x, y)) + self.evaluate(math_ops.pow(x, y)) - with self.test_session(use_gpu=False) as sess: + with test_util.force_cpu(): with self.assertRaisesRegexp( errors_impl.InvalidArgumentError, "Integers to negative integer powers are not allowed"): x = np.array([5, 2]).astype(dtype) y = -3 - sess.run(math_ops.pow(x, y)) + self.evaluate(math_ops.pow(x, y)) class ComparisonOpTest(test.TestCase): def _compareScalar(self, func, x, y, dtype): - with self.test_session(force_gpu=test_util.is_gpu_available()): + with test_util.use_gpu(): out = func( ops.convert_to_tensor(np.array([x]).astype(dtype)), ops.convert_to_tensor(np.array([y]).astype(dtype))) - ret = out.eval() + ret = self.evaluate(out) return ret[0] def testScalarCompareScalar(self): @@ -777,9 +851,9 @@ class ComparisonOpTest(test.TestCase): def _compare(self, x, y, np_func, tf_func): np_ans = np_func(x, y) - with self.test_session(force_gpu=test_util.is_gpu_available()): + with test_util.use_gpu(): out = tf_func(ops.convert_to_tensor(x), ops.convert_to_tensor(y)) - tf_ans = out.eval() + tf_ans = self.evaluate(out) self.assertAllEqual(np_ans, tf_ans) def testTensorCompareTensor(self): @@ -859,6 +933,7 @@ class ComparisonOpTest(test.TestCase): self._testBCastByFunc( np.not_equal, math_ops.not_equal, include_complex=True) + @test_util.run_deprecated_v1 def testShapeMismatch(self): dtypes = [np.float16, np.float32, np.float64, np.int32, np.int64] funcs = [ diff --git a/tensorflow/python/kernel_tests/cwise_ops_test.py b/tensorflow/python/kernel_tests/cwise_ops_test.py index c5311ad834..9bb7d8b8b1 100644 --- a/tensorflow/python/kernel_tests/cwise_ops_test.py +++ b/tensorflow/python/kernel_tests/cwise_ops_test.py @@ -84,11 +84,11 @@ def _default_tolerance(dtype): class ComparisonOpTest(test.TestCase): def _compareScalar(self, func, x, y, dtype): - with self.test_session(force_gpu=test_util.is_gpu_available()): + with test_util.use_gpu(): out = func( ops.convert_to_tensor(np.array([x]).astype(dtype)), ops.convert_to_tensor(np.array([y]).astype(dtype))) - ret = out.eval() + ret = self.evaluate(out) return ret[0] def testScalarCompareScalar(self): @@ -117,9 +117,9 @@ class ComparisonOpTest(test.TestCase): def _compare(self, x, y, np_func, tf_func): np_ans = np_func(x, y) - with self.test_session(force_gpu=test_util.is_gpu_available()): + with test_util.use_gpu(): out = tf_func(ops.convert_to_tensor(x), ops.convert_to_tensor(y)) - tf_ans = out.eval() + tf_ans = self.evaluate(out) self.assertAllEqual(np_ans, tf_ans) def testTensorCompareTensor(self): @@ -199,6 +199,7 @@ class ComparisonOpTest(test.TestCase): self._testBCastByFunc( np.not_equal, math_ops.not_equal, include_complex=True) + @test_util.run_deprecated_v1 def testShapeMismatch(self): dtypes = [np.float16, np.float32, np.float64, np.int32, np.int64] funcs = [ @@ -218,22 +219,20 @@ class LogicalOpTest(test.TestCase): def _compareBinary(self, x, y, np_func, tf_func, use_gpu=False): np_ans = np_func(x, y) - with self.test_session(use_gpu=use_gpu, - force_gpu=use_gpu and test_util.is_gpu_available()): + with test_util.device(use_gpu=use_gpu): inx = ops.convert_to_tensor(x) iny = ops.convert_to_tensor(y) out = tf_func(inx, iny) - tf_val = out.eval() + tf_val = self.evaluate(out) self.assertEqual(out.dtype, dtypes_lib.bool) self.assertAllEqual(np_ans, tf_val) self.assertShapeEqual(np_ans, out) def _not(self, x, use_gpu=False): np_ans = np.logical_not(x) - with self.test_session(use_gpu=use_gpu, - force_gpu=use_gpu and test_util.is_gpu_available()): + with test_util.device(use_gpu=use_gpu): out = math_ops.logical_not(ops.convert_to_tensor(x)) - tf_val = out.eval() + tf_val = self.evaluate(out) self.assertEqual(out.dtype, dtypes_lib.bool) self.assertAllEqual(np_ans, tf_val) self.assertShapeEqual(np_ans, out) @@ -282,6 +281,7 @@ class LogicalOpTest(test.TestCase): self._compareBinary(x, y, np.logical_or, math_ops.logical_or, use_gpu) self._compareBinary(x, y, np.logical_xor, math_ops.logical_xor, use_gpu) + @test_util.run_deprecated_v1 def testShapeMismatch(self): x = np.random.randint(0, 2, 6).astype(np.bool).reshape(1, 3, 2) y = np.random.randint(0, 2, 6).astype(np.bool).reshape(3, 2, 1) @@ -290,6 +290,7 @@ class LogicalOpTest(test.TestCase): ValueError, lambda e: "Dimensions must" in str(e)): f(x, y) + @test_util.run_deprecated_v1 def testUsingAsPythonValueFails(self): # Ensure that we raise an error when the user attempts to treat a # `Tensor` as a Python `bool`. @@ -316,10 +317,9 @@ class SelectOpTest(test.TestCase): def _compare(self, c, x, y, use_gpu): np_ans = np.where(c, x, y) - with self.test_session(use_gpu=use_gpu, - force_gpu=use_gpu and test_util.is_gpu_available()): + with test_util.device(use_gpu=use_gpu): out = array_ops.where(c, x, y) - tf_ans = out.eval() + tf_ans = self.evaluate(out) self.assertAllEqual(np_ans, tf_ans) self.assertShapeEqual(np_ans, out) @@ -399,6 +399,7 @@ class SelectOpTest(test.TestCase): if t in [np.float16, np.float32, np.float64]: self._compare(c, xt, yt, use_gpu=True) + @test_util.run_deprecated_v1 def testGradients(self): c = np.random.randint(0, 2, 6).astype(np.bool).reshape(1, 3, 2) x = np.random.rand(1, 3, 2) * 100 @@ -418,6 +419,7 @@ class SelectOpTest(test.TestCase): self._compareGradientX(c, xt, yt) self._compareGradientY(c, xt, yt) + @test_util.run_deprecated_v1 def testShapeMismatch(self): c = np.random.randint(0, 2, 6).astype(np.bool).reshape(1, 3, 2) x = np.random.rand(1, 3, 2) * 100 @@ -431,6 +433,7 @@ class SelectOpTest(test.TestCase): with self.assertRaises(ValueError): array_ops.where(c, xt, yt) + @test_util.run_deprecated_v1 def testEmptyTensor(self): c = np.random.randint(0, 3, 0).astype(np.bool).reshape(1, 3, 0) x = np.random.rand(1, 3, 0) * 100 @@ -442,6 +445,7 @@ class SelectOpTest(test.TestCase): z = array_ops.where(c, xt, yt).eval() self.assertAllEqual(z_expected, z) + @test_util.run_deprecated_v1 def testNan(self): """Verify that nans don't propagate where they shouldn't.""" with self.cached_session(): @@ -460,10 +464,9 @@ class BatchSelectOpTest(test.TestCase): np_ans = np.dstack( [x_i if c_i else y_i for c_i, x_i, y_i in zip(c, x, y)]).transpose( [2, 0, 1]) - with self.test_session(use_gpu=use_gpu, - force_gpu=use_gpu and test_util.is_gpu_available()): + with test_util.device(use_gpu=use_gpu): out = array_ops.where(c, x, y) - tf_ans = out.eval() + tf_ans = self.evaluate(out) self.assertAllEqual(np_ans, tf_ans) self.assertShapeEqual(np_ans, out) @@ -529,6 +532,7 @@ class BatchSelectOpTest(test.TestCase): if t in [np.float16, np.float32, np.float64]: self._compare(c, xt, yt, use_gpu=True) + @test_util.run_deprecated_v1 def testGradients(self): c = np.random.randint(0, 2, 16).astype(np.bool) x = np.random.rand(16, 2, 8) * 100 @@ -548,6 +552,7 @@ class BatchSelectOpTest(test.TestCase): self._compareGradientX(c, xt, yt) self._compareGradientY(c, xt, yt) + @test_util.run_deprecated_v1 def testShapeMismatch(self): c = np.random.randint(0, 2, 8).astype(np.bool) x = np.random.rand(16, 3, 2) * 100 @@ -566,13 +571,11 @@ class MinMaxOpTest(test.TestCase): def _compare(self, x, y, use_gpu): np_min, np_max = np.minimum(x, y), np.maximum(x, y) - with self.test_session( - use_gpu=use_gpu, - force_gpu=use_gpu and test_util.is_gpu_available()) as sess: + with test_util.device(use_gpu=use_gpu): inx = ops.convert_to_tensor(x) iny = ops.convert_to_tensor(y) omin, omax = math_ops.minimum(inx, iny), math_ops.maximum(inx, iny) - tf_min, tf_max = sess.run([omin, omax]) + tf_min, tf_max = self.evaluate([omin, omax]) self.assertAllEqual(np_min, tf_min) self.assertAllEqual(np_max, tf_max) @@ -628,6 +631,7 @@ class MinMaxOpTest(test.TestCase): elif x.dtype == np.float64: self.assertAllClose(jacob_t, jacob_n, rtol=1e-5, atol=1e-5) + @test_util.run_deprecated_v1 def testGradients(self): x = np.random.rand(1, 3, 2) * 100. # ensure x != y @@ -641,16 +645,16 @@ class MinMaxOpTest(test.TestCase): class MathOpsOverloadTest(test.TestCase): def _computeTensorAndLiteral(self, x, y, dtype, func): - with self.test_session(use_gpu=False): + with test_util.force_cpu(): inx = ops.convert_to_tensor(x, dtype=dtype) z = func(inx, y) # Should use __add__, __sub__, etc. - return z.eval() + return self.evaluate(z) def _computeLiteralAndTensor(self, x, y, dtype, func): - with self.test_session(use_gpu=False): + with test_util.force_cpu(): iny = ops.convert_to_tensor(y, dtype=dtype) z = func(x, iny) # Should use __radd__, __rsub__, etc. - return z.eval() + return self.evaluate(z) def _compareBinary(self, x, y, dtype, np_func, tf_func): np_ans = np_func(x, y).astype(dtype.as_numpy_dtype) @@ -661,9 +665,9 @@ class MathOpsOverloadTest(test.TestCase): def _compareUnary(self, x, dtype, np_func, tf_func): np_ans = np_func(x).astype(dtype.as_numpy_dtype) - with self.test_session(use_gpu=False): - self.assertAllClose(np_ans, - tf_func(ops.convert_to_tensor(x, dtype=dtype)).eval()) + with test_util.force_cpu(): + self.assertAllClose( + np_ans, self.evaluate(tf_func(ops.convert_to_tensor(x, dtype=dtype)))) def testOverload(self): dtypes = [ @@ -730,13 +734,11 @@ class IsFiniteInfNanTest(test.TestCase): def _compare(self, x, use_gpu): np_finite, np_inf, np_nan = np.isfinite(x), np.isinf(x), np.isnan(x) - with self.test_session( - use_gpu=use_gpu, - force_gpu=use_gpu and test_util.is_gpu_available()) as sess: + with test_util.device(use_gpu=use_gpu): inx = ops.convert_to_tensor(x) ofinite, oinf, onan = math_ops.is_finite(inx), math_ops.is_inf( inx), math_ops.is_nan(inx) - tf_finite, tf_inf, tf_nan = sess.run([ofinite, oinf, onan]) + tf_finite, tf_inf, tf_nan = self.evaluate([ofinite, oinf, onan]) self.assertAllEqual(np_inf, tf_inf) self.assertAllEqual(np_nan, tf_nan) self.assertAllEqual(np_finite, tf_finite) @@ -773,31 +775,33 @@ class IsFiniteInfNanTest(test.TestCase): x = np.full((size,), value, dtype=dtype) np_y = np.sqrt(x) np_nan = np.isnan(np_y) - with self.test_session(force_gpu=test_util.is_gpu_available()): + with test_util.use_gpu(): tf_y = math_ops.sqrt(x) tf_nan = math_ops.is_nan(tf_y) if value < 0: - self.assertAllEqual(np_nan, tf_nan.eval()) + self.assertAllEqual(np_nan, self.evaluate(tf_nan)) else: - self.assertAllCloseAccordingToType(np_y, tf_y.eval()) + self.assertAllCloseAccordingToType(np_y, self.evaluate(tf_y)) class RoundingTest(test.TestCase): def _compare_values(self, x, y=None): y = np.rint(x) if y is None else np.asarray(y) - with self.cached_session() as sess: - tf_rint = math_ops.rint(x) - np_rint = sess.run(tf_rint) + + tf_rint = math_ops.rint(x) + np_rint = self.evaluate(tf_rint) + self.assertAllEqual(y, np_rint) self.assertShapeEqual(y, tf_rint) def _compare(self, x): np_floor, np_ceil = np.floor(x), np.ceil(x) - with self.cached_session() as sess: - inx = ops.convert_to_tensor(x) - ofloor, oceil = math_ops.floor(inx), math_ops.ceil(inx) - tf_floor, tf_ceil = sess.run([ofloor, oceil]) + + inx = ops.convert_to_tensor(x) + ofloor, oceil = math_ops.floor(inx), math_ops.ceil(inx) + tf_floor, tf_ceil = self.evaluate([ofloor, oceil]) + self.assertAllEqual(np_floor, tf_floor) self.assertAllEqual(np_ceil, tf_ceil) self.assertShapeEqual(np_floor, ofloor) @@ -828,12 +832,13 @@ class ComplexMakeRealImagTest(test.TestCase): def _compareMake(self, real, imag, use_gpu): np_ans = real + (1j) * imag - with self.test_session(use_gpu=use_gpu, - force_gpu=use_gpu and test_util.is_gpu_available()): + + with test_util.device(use_gpu=use_gpu): real = ops.convert_to_tensor(real) imag = ops.convert_to_tensor(imag) tf_ans = math_ops.complex(real, imag) - out = tf_ans.eval() + out = self.evaluate(tf_ans) + self.assertAllEqual(np_ans, out) self.assertShapeEqual(np_ans, tf_ans) @@ -848,17 +853,17 @@ class ComplexMakeRealImagTest(test.TestCase): def _compareRealImag(self, cplx, use_gpu): np_real, np_imag = np.real(cplx), np.imag(cplx) np_zeros = np_real * 0 - with self.test_session(use_gpu=use_gpu, - force_gpu=use_gpu and test_util.is_gpu_available()): + + with test_util.device(use_gpu=use_gpu): inx = ops.convert_to_tensor(cplx) tf_real = math_ops.real(inx) tf_imag = math_ops.imag(inx) tf_real_real = math_ops.real(tf_real) tf_imag_real = math_ops.imag(tf_real) - self.assertAllEqual(np_real, tf_real.eval()) - self.assertAllEqual(np_imag, tf_imag.eval()) - self.assertAllEqual(np_real, tf_real_real.eval()) - self.assertAllEqual(np_zeros, tf_imag_real.eval()) + self.assertAllEqual(np_real, self.evaluate(tf_real)) + self.assertAllEqual(np_imag, self.evaluate(tf_imag)) + self.assertAllEqual(np_real, self.evaluate(tf_real_real)) + self.assertAllEqual(np_zeros, self.evaluate(tf_imag_real)) def testRealImag64(self): real = (np.arange(-3, 3) / 4.).reshape([1, 3, 2]).astype(np.float32) @@ -876,12 +881,12 @@ class ComplexMakeRealImagTest(test.TestCase): def _compareAngle(self, cplx, use_gpu): np_angle = np.angle(cplx) - with self.test_session( - use_gpu=use_gpu, - force_gpu=use_gpu and test_util.is_gpu_available()) as sess: + + with test_util.device(use_gpu=use_gpu): inx = ops.convert_to_tensor(cplx) tf_angle = math_ops.angle(inx) - tf_angle_val = sess.run(tf_angle) + tf_angle_val = self.evaluate(tf_angle) + self.assertAllEqual(np_angle, tf_angle_val) self.assertShapeEqual(np_angle, tf_angle) @@ -903,6 +908,7 @@ class ComplexMakeRealImagTest(test.TestCase): # build failures on GPU (See #10643 for context). # self._compareAngle(cplx, use_gpu=True) + @test_util.run_deprecated_v1 def testRealReal(self): for dtype in (dtypes_lib.int32, dtypes_lib.int64, dtypes_lib.float32, dtypes_lib.float64): @@ -912,11 +918,10 @@ class ComplexMakeRealImagTest(test.TestCase): def _compareConj(self, cplx, use_gpu): np_ans = np.conj(cplx) - with self.test_session(use_gpu=use_gpu, - force_gpu=use_gpu and test_util.is_gpu_available()): + with test_util.device(use_gpu=use_gpu): inx = ops.convert_to_tensor(cplx) tf_conj = math_ops.conj(inx) - tf_ans = tf_conj.eval() + tf_ans = self.evaluate(tf_conj) self.assertAllEqual(np_ans, tf_ans) self.assertShapeEqual(np_ans, tf_conj) @@ -934,6 +939,7 @@ class ComplexMakeRealImagTest(test.TestCase): self._compareConj(cplx, use_gpu=False) self._compareConj(cplx, use_gpu=True) + @test_util.run_deprecated_v1 def testConjReal(self): for dtype in (dtypes_lib.int32, dtypes_lib.int64, dtypes_lib.float16, dtypes_lib.float32, dtypes_lib.float64): @@ -941,6 +947,7 @@ class ComplexMakeRealImagTest(test.TestCase): y = math_ops.conj(x) self.assertEqual(x, y) + @test_util.run_deprecated_v1 def testConjString(self): x = array_ops.placeholder(dtypes_lib.string) with self.assertRaisesRegexp(TypeError, @@ -977,6 +984,7 @@ class ComplexMakeRealImagTest(test.TestCase): x_, list(x.shape), z, [1], x_init_value=x, delta=epsilon) self.assertAllClose(jacob_t, jacob_n, rtol=epsilon, atol=epsilon) + @test_util.run_deprecated_v1 def testGradient(self): # complex64 data = np.arange(1, 2, 0.10).reshape([5, 2]).astype(np.float32) @@ -1012,6 +1020,7 @@ class ComplexMakeRealImagTest(test.TestCase): inp, list(data.shape), loss, [1], x_init_value=data, delta=epsilon) self.assertAllClose(jacob_t, jacob_n, rtol=epsilon, atol=epsilon) + @test_util.run_deprecated_v1 def testMulGradient(self): data = np.arange(1, 2, 0.125).reshape([2, 4]).astype(np.float32) self._compareMulGradient(data) @@ -1032,13 +1041,13 @@ class AccumulateTest(test.TestCase): np_val = random_arrays[0] for random_array in random_arrays[1:]: np_val += random_array - self.assertAllClose(np_val, tf_val.eval()) + self.assertAllClose(np_val, self.evaluate(tf_val)) def testZeroArgs(self): with self.cached_session(): with self.assertRaises(ValueError): tf_val = math_ops.accumulate_n([]) - tf_val.eval() + self.evaluate(tf_val) def testWrongShape(self): with self.cached_session(): @@ -1070,7 +1079,7 @@ class PolyvalTest(test.TestCase): np_val = np.polyval(coeffs, x) with self.cached_session(): tf_val = math_ops.polyval(coeffs, x) - self.assertAllClose(np_val, tf_val.eval()) + self.assertAllClose(np_val, self.evaluate(tf_val)) def testSimple(self): for dtype in [ @@ -1093,7 +1102,7 @@ class PolyvalTest(test.TestCase): np_val = np.polyval(coeffs, x) with self.cached_session(): tf_val = math_ops.polyval(coeffs, x) - self.assertAllClose(np_val, tf_val.eval()) + self.assertAllClose(np_val, self.evaluate(tf_val)) def testEmpty(self): x = np.random.rand(2, 2).astype(np.float32) @@ -1101,7 +1110,7 @@ class PolyvalTest(test.TestCase): np_val = np.polyval(coeffs, x) with self.cached_session(): tf_val = math_ops.polyval(coeffs, x) - self.assertAllClose(np_val, tf_val.eval()) + self.assertAllClose(np_val, self.evaluate(tf_val)) if __name__ == "__main__": diff --git a/tensorflow/python/kernel_tests/cwise_ops_unary_test.py b/tensorflow/python/kernel_tests/cwise_ops_unary_test.py index 77f182784e..709a20f3d0 100644 --- a/tensorflow/python/kernel_tests/cwise_ops_unary_test.py +++ b/tensorflow/python/kernel_tests/cwise_ops_unary_test.py @@ -76,7 +76,7 @@ class UnaryOpTest(test.TestCase): if grad_atol is None: grad_atol = _default_tolerance(x.dtype) np_ans = np_func(x) - with self.test_session(use_gpu=False): + with self.cached_session(use_gpu=False): inx = ops.convert_to_tensor(x) if x.dtype in (np.float32, np.float64, dtypes_lib.bfloat16.as_numpy_dtype): @@ -84,7 +84,7 @@ class UnaryOpTest(test.TestCase): np_ans *= 1.1 else: y = tf_func(inx) - tf_cpu = y.eval() + tf_cpu = self.evaluate(y) self.assertShapeEqual(np_ans, y) if x.dtype == np.float16: self.assertAllClose(np_ans, tf_cpu, rtol=1e-3, atol=1e-3) @@ -121,26 +121,24 @@ class UnaryOpTest(test.TestCase): def _check(self, result_tensor, result_np, input_sp_t, tol): self.assertTrue(isinstance(result_tensor, sparse_tensor.SparseTensor)) self.assertTrue(isinstance(input_sp_t, sparse_tensor.SparseTensor)) - self.assertAllEqual(input_sp_t.indices.eval(), result_tensor.indices.eval()) - self.assertAllEqual(input_sp_t.dense_shape.eval(), - result_tensor.dense_shape.eval()) + self.assertAllEqual(input_sp_t.indices, result_tensor.indices) + self.assertAllEqual(input_sp_t.dense_shape, result_tensor.dense_shape) if tol is None: - self.assertAllClose(result_np, result_tensor.values.eval()) + self.assertAllClose(result_np, result_tensor.values) else: - self.assertAllClose( - result_np, result_tensor.values.eval(), rtol=tol, atol=tol) + self.assertAllClose(result_np, result_tensor.values, rtol=tol, atol=tol) def _compareSparseCpu(self, x, np_func, tf_func, tol): x_sp, x_sp_vals = _sparsify(x) res_np = np_func(x_sp_vals) - with self.test_session(use_gpu=False): + with test_util.force_cpu(): self._check(tf_func(x_sp), res_np, x_sp, tol) def _compareGpu(self, x, np_func, tf_func): np_ans = np_func(x) - with self.test_session(force_gpu=test_util.is_gpu_available()): + with test_util.use_gpu(): result = tf_func(ops.convert_to_tensor(x)) - tf_gpu = result.eval() + tf_gpu = self.evaluate(result) if x.dtype == np.float16: self.assertAllClose(np_ans, tf_gpu, rtol=1e-3, atol=1e-3) else: @@ -150,7 +148,7 @@ class UnaryOpTest(test.TestCase): def _compareSparseGpu(self, x, np_func, tf_func, tol): x_sp, x_sp_vals = _sparsify(x) res_np = np_func(x_sp_vals) - with self.test_session(force_gpu=test_util.is_gpu_available()): + with test_util.use_gpu(): self._check(tf_func(x_sp), res_np, x_sp, tol) def _compareBoth(self, x, np_func, tf_func): @@ -186,6 +184,7 @@ class UnaryOpTest(test.TestCase): return func + @test_util.run_deprecated_v1 def testFloatBasic(self): x = np.arange(-3, 3).reshape(1, 3, 2).astype(np.float32) w = x - x.min() + 1.02 # all greater than 1 @@ -240,12 +239,14 @@ class UnaryOpTest(test.TestCase): self._compareBothSparse(y, np.sign, math_ops.sign) self._compareBothSparse(x, np.vectorize(math.erf), math_ops.erf) + @test_util.run_deprecated_v1 def testFloatTanhEdge(self): x = np.arange(40, 40 + 6).reshape(6).astype(np.float32) self._compareBoth(x, np.tanh, math_ops.tanh) x = np.arange(-40, -40 + 6).reshape(6).astype(np.float32) self._compareBoth(x, np.tanh, math_ops.tanh) + @test_util.run_deprecated_v1 def testFloatEmpty(self): x = np.empty((2, 0, 5), dtype=np.float32) self._compareBoth(x, np.abs, math_ops.abs) @@ -291,6 +292,7 @@ class UnaryOpTest(test.TestCase): self._compareBothSparse(x, np.sign, math_ops.sign) self._compareBothSparse(x, np.sign, math_ops.erf) + @test_util.run_deprecated_v1 def testDoubleBasic(self): x = np.arange(-3, 3).reshape(1, 3, 2).astype(np.float64) w = x - x.min() + 1.02 # all greater than 1 @@ -344,6 +346,7 @@ class UnaryOpTest(test.TestCase): self._compareBothSparse(y, np.sign, math_ops.sign) self._compareBothSparse(x, np.vectorize(math.erf), math_ops.erf) + @test_util.run_deprecated_v1 def testHalfBasic(self): x = np.arange(-3, 3).reshape(1, 3, 2).astype(np.float16) y = (x + .5).astype(np.float16) # no zero @@ -416,6 +419,7 @@ class UnaryOpTest(test.TestCase): self._compareCpu(x, np.square, math_ops.square) self._compareBothSparse(x, np.square, math_ops.square) + @test_util.run_deprecated_v1 def testComplex64Basic(self): x = np.complex(1, 1) * np.arange(-3, 3).reshape(1, 3, 2).astype( np.complex64) @@ -460,6 +464,7 @@ class UnaryOpTest(test.TestCase): self._compareBoth(y, complex_sign, math_ops.sign) self._compareBothSparse(y, complex_sign, math_ops.sign) + @test_util.run_deprecated_v1 def testComplex128Basic(self): x = np.complex(1, 1) * np.arange(-3, 3).reshape(1, 3, 2).astype( np.complex128) @@ -499,6 +504,7 @@ class UnaryOpTest(test.TestCase): self._compareBoth(y, complex_sign, math_ops.sign) self._compareBothSparse(y, complex_sign, math_ops.sign) + @test_util.run_deprecated_v1 def testGradGrad(self): np.random.seed(7) shape = (5,) diff --git a/tensorflow/python/kernel_tests/decode_bmp_op_test.py b/tensorflow/python/kernel_tests/decode_bmp_op_test.py index eebaffbe13..5e7991382e 100644 --- a/tensorflow/python/kernel_tests/decode_bmp_op_test.py +++ b/tensorflow/python/kernel_tests/decode_bmp_op_test.py @@ -61,7 +61,7 @@ class DecodeBmpOpTest(test.TestCase): decode = array_ops.squeeze(image_ops.decode_bmp(img_in)) with self.cached_session(): - decoded = decode.eval() + decoded = self.evaluate(decode) self.assertAllEqual(decoded, img_bytes) def testGrayscale(self): @@ -136,7 +136,7 @@ class DecodeBmpOpTest(test.TestCase): decode = image_ops.decode_bmp(img_in) with self.cached_session(): - decoded = decode.eval() + decoded = self.evaluate(decode) self.assertAllEqual(decoded, img_bytes) diff --git a/tensorflow/python/kernel_tests/decode_compressed_op_test.py b/tensorflow/python/kernel_tests/decode_compressed_op_test.py index 1cc1c7da30..fd871c0090 100644 --- a/tensorflow/python/kernel_tests/decode_compressed_op_test.py +++ b/tensorflow/python/kernel_tests/decode_compressed_op_test.py @@ -24,6 +24,7 @@ import zlib from six import BytesIO from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import parsing_ops from tensorflow.python.platform import test @@ -42,6 +43,7 @@ class DecodeCompressedOpTest(test.TestCase): f.write(bytes_in) return out.getvalue() + @test_util.run_deprecated_v1 def testDecompress(self): for compression_type in ["ZLIB", "GZIP", ""]: with self.cached_session(): @@ -55,6 +57,7 @@ class DecodeCompressedOpTest(test.TestCase): self._compress(b"bBbb", compression_type)]}) self.assertAllEqual([b"AaAA", b"bBbb"], result) + @test_util.run_deprecated_v1 def testDecompressWithRaw(self): for compression_type in ["ZLIB", "GZIP", ""]: with self.cached_session(): diff --git a/tensorflow/python/kernel_tests/decode_image_op_test.py b/tensorflow/python/kernel_tests/decode_image_op_test.py index 0975f964b5..ba5770001a 100644 --- a/tensorflow/python/kernel_tests/decode_image_op_test.py +++ b/tensorflow/python/kernel_tests/decode_image_op_test.py @@ -23,6 +23,7 @@ import os.path import numpy as np from tensorflow.python.framework import errors_impl +from tensorflow.python.framework import test_util from tensorflow.python.ops import image_ops from tensorflow.python.ops import io_ops import tensorflow.python.ops.nn_grad # pylint: disable=unused-import @@ -40,10 +41,11 @@ class DecodeImageOpTest(test.TestCase): bmp0 = io_ops.read_file(path) image0 = image_ops.decode_image(bmp0) image1 = image_ops.decode_bmp(bmp0) - bmp0, image0, image1 = sess.run([bmp0, image0, image1]) + bmp0, image0, image1 = self.evaluate([bmp0, image0, image1]) self.assertEqual(len(bmp0), 4194) self.assertAllEqual(image0, image1) + @test_util.run_deprecated_v1 def testGif(self): # Read some real GIFs path = os.path.join(prefix_path, "gif", "testdata", "scan.gif") @@ -56,7 +58,7 @@ class DecodeImageOpTest(test.TestCase): gif0 = io_ops.read_file(path) image0 = image_ops.decode_image(gif0) image1 = image_ops.decode_gif(gif0) - gif0, image0, image1 = sess.run([gif0, image0, image1]) + gif0, image0, image1 = self.evaluate([gif0, image0, image1]) self.assertEqual(image0.shape, shape) self.assertAllEqual(image0, image1) @@ -76,8 +78,9 @@ class DecodeImageOpTest(test.TestCase): bad_channels = image_ops.decode_image(gif0, channels=1) with self.assertRaises(errors_impl.InvalidArgumentError): - bad_channels.eval() + self.evaluate(bad_channels) + @test_util.run_deprecated_v1 def testJpeg(self): # Read a real jpeg and verify shape path = os.path.join(prefix_path, "jpeg", "testdata", "jpeg_merge_test1.jpg") @@ -85,14 +88,14 @@ class DecodeImageOpTest(test.TestCase): jpeg0 = io_ops.read_file(path) image0 = image_ops.decode_image(jpeg0) image1 = image_ops.decode_jpeg(jpeg0) - jpeg0, image0, image1 = sess.run([jpeg0, image0, image1]) + jpeg0, image0, image1 = self.evaluate([jpeg0, image0, image1]) self.assertEqual(len(jpeg0), 3771) self.assertEqual(image0.shape, (256, 128, 3)) self.assertAllEqual(image0, image1) bad_channels = image_ops.decode_image(jpeg0, channels=4) with self.assertRaises(errors_impl.InvalidArgumentError): - bad_channels.eval() + self.evaluate(bad_channels) def testPng(self): # Read some real PNGs, converting to different channel numbers @@ -104,16 +107,17 @@ class DecodeImageOpTest(test.TestCase): png0 = io_ops.read_file(path) image0 = image_ops.decode_image(png0, channels=channels) image1 = image_ops.decode_png(png0, channels=channels) - png0, image0, image1 = sess.run([png0, image0, image1]) + png0, image0, image1 = self.evaluate([png0, image0, image1]) self.assertEqual(image0.shape, (26, 51, channels or channels_in)) self.assertAllEqual(image0, image1) + @test_util.run_deprecated_v1 def testInvalidBytes(self): image_bytes = b"ThisIsNotAnImage!" decode = image_ops.decode_image(image_bytes) with self.cached_session(): with self.assertRaises(errors_impl.InvalidArgumentError): - decode.eval() + self.evaluate(decode) if __name__ == "__main__": diff --git a/tensorflow/python/kernel_tests/decode_jpeg_op_test.py b/tensorflow/python/kernel_tests/decode_jpeg_op_test.py index 66b3e0f22f..f8fc28062f 100644 --- a/tensorflow/python/kernel_tests/decode_jpeg_op_test.py +++ b/tensorflow/python/kernel_tests/decode_jpeg_op_test.py @@ -80,7 +80,7 @@ class DecodeJpegBenchmark(test.Benchmark): initializer=image_ops.encode_jpeg(tiled_image)) with session.Session() as sess: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) images = [] for _ in xrange(parallelism): if crop_window is None: @@ -105,11 +105,11 @@ class DecodeJpegBenchmark(test.Benchmark): for _ in xrange(3): # Skip warm up time. - sess.run(r) + self.evaluate(r) start_time = time.time() for _ in xrange(num_iters): - sess.run(r) + self.evaluate(r) end_time = time.time() return end_time - start_time diff --git a/tensorflow/python/kernel_tests/decode_png_op_test.py b/tensorflow/python/kernel_tests/decode_png_op_test.py index 8f36343667..5a0b742a6a 100644 --- a/tensorflow/python/kernel_tests/decode_png_op_test.py +++ b/tensorflow/python/kernel_tests/decode_png_op_test.py @@ -47,7 +47,7 @@ class DecodePngOpTest(test.TestCase): img_in, dtype=dtypes.uint16)) with self.cached_session(): - decoded = decode.eval() + decoded = self.evaluate(decode) self.assertAllEqual(decoded, img_bytes) diff --git a/tensorflow/python/kernel_tests/decode_raw_op_test.py b/tensorflow/python/kernel_tests/decode_raw_op_test.py index dcc984811c..008e59ba3e 100644 --- a/tensorflow/python/kernel_tests/decode_raw_op_test.py +++ b/tensorflow/python/kernel_tests/decode_raw_op_test.py @@ -21,6 +21,7 @@ from __future__ import print_function import numpy as np from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import parsing_ops from tensorflow.python.platform import test @@ -28,6 +29,7 @@ from tensorflow.python.platform import test class DecodeRawOpTest(test.TestCase): + @test_util.run_deprecated_v1 def testToUint8(self): with self.cached_session(): in_bytes = array_ops.placeholder(dtypes.string, shape=[2]) @@ -46,6 +48,7 @@ class DecodeRawOpTest(test.TestCase): "element 1 has size 5 != 6"): decode.eval(feed_dict={in_bytes: ["short", "longer"]}) + @test_util.run_deprecated_v1 def testToInt16(self): with self.cached_session(): in_bytes = array_ops.placeholder(dtypes.string, shape=[None]) @@ -61,6 +64,7 @@ class DecodeRawOpTest(test.TestCase): "size of int16"): decode.eval(feed_dict={in_bytes: ["123", "456"]}) + @test_util.run_deprecated_v1 def testEndianness(self): with self.cached_session(): in_bytes = array_ops.placeholder(dtypes.string, shape=[None]) @@ -73,6 +77,7 @@ class DecodeRawOpTest(test.TestCase): result = decode_be.eval(feed_dict={in_bytes: ["\x01\x02\x03\x04"]}) self.assertAllEqual([[0x01020304]], result) + @test_util.run_deprecated_v1 def testToFloat16(self): with self.cached_session(): in_bytes = array_ops.placeholder(dtypes.string, shape=[None]) @@ -84,6 +89,7 @@ class DecodeRawOpTest(test.TestCase): self.assertAllEqual(expected_result, result) + @test_util.run_deprecated_v1 def testEmptyStringInput(self): with self.cached_session(): in_bytes = array_ops.placeholder(dtypes.string, shape=[None]) @@ -93,6 +99,7 @@ class DecodeRawOpTest(test.TestCase): result = decode.eval(feed_dict={in_bytes: [""] * num_inputs}) self.assertEqual((num_inputs, 0), result.shape) + @test_util.run_deprecated_v1 def testToUInt16(self): with self.cached_session(): in_bytes = array_ops.placeholder(dtypes.string, shape=[None]) diff --git a/tensorflow/python/kernel_tests/denormal_test.py b/tensorflow/python/kernel_tests/denormal_test.py index 71a528c4aa..80a3033ecc 100644 --- a/tensorflow/python/kernel_tests/denormal_test.py +++ b/tensorflow/python/kernel_tests/denormal_test.py @@ -22,6 +22,7 @@ import numpy as np import platform from tensorflow.python.framework import constant_op +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.platform import test @@ -50,10 +51,12 @@ class DenormalTest(test.TestCase): # Make sure the flags don't leak out self.testPythonHasDenormals() + @test_util.run_deprecated_v1 def testFlushDenormalsCPU(self): # On CPUs, the processor flags flush for both single and double precision. self._flushDenormalsTest(use_gpu=False, dtypes=(np.float32, np.float64)) + @test_util.run_deprecated_v1 def testFlushDenormalsGPU(self): # On GPUs, only single precision can flush to zero. self._flushDenormalsTest(use_gpu=True, dtypes=(np.float32,)) diff --git a/tensorflow/python/kernel_tests/dense_update_ops_no_tsan_test.py b/tensorflow/python/kernel_tests/dense_update_ops_no_tsan_test.py index affbaf159d..4f74e1e741 100644 --- a/tensorflow/python/kernel_tests/dense_update_ops_no_tsan_test.py +++ b/tensorflow/python/kernel_tests/dense_update_ops_no_tsan_test.py @@ -20,6 +20,7 @@ from __future__ import print_function import numpy as np +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import state_ops @@ -32,6 +33,7 @@ class AssignOpTest(test.TestCase): # NOTE(mrry): We exclude thess tests from the TSAN TAP target, because they # contain benign and deliberate data races when multiple threads update # the same parameters without a lock. + @test_util.run_deprecated_v1 def testParallelUpdateWithoutLocking(self): with self.cached_session() as sess: ones_t = array_ops.fill([1024, 1024], 1.0) @@ -43,7 +45,7 @@ class AssignOpTest(test.TestCase): variables.global_variables_initializer().run() def run_add(add_op): - sess.run(add_op) + self.evaluate(add_op) threads = [ self.checkedThread( @@ -54,11 +56,12 @@ class AssignOpTest(test.TestCase): for t in threads: t.join() - vals = p.eval() + vals = self.evaluate(p) ones = np.ones((1024, 1024)).astype(np.float32) self.assertTrue((vals >= ones).all()) self.assertTrue((vals <= ones * 20).all()) + @test_util.run_deprecated_v1 def testParallelAssignWithoutLocking(self): with self.cached_session() as sess: ones_t = array_ops.fill([1024, 1024], float(1)) @@ -70,7 +73,7 @@ class AssignOpTest(test.TestCase): variables.global_variables_initializer().run() def run_assign(assign_op): - sess.run(assign_op) + self.evaluate(assign_op) threads = [ self.checkedThread( @@ -81,7 +84,7 @@ class AssignOpTest(test.TestCase): for t in threads: t.join() - vals = p.eval() + vals = self.evaluate(p) # Assert every element is taken from one of the assignments. self.assertTrue((vals > 0).all()) @@ -91,6 +94,7 @@ class AssignOpTest(test.TestCase): # contain non-benign but known data races between the variable assignment and # returning the output tensors. This issue will be resolved with the new # resource variables. + @test_util.run_deprecated_v1 def testParallelUpdateWithLocking(self): with self.cached_session() as sess: zeros_t = array_ops.fill([1024, 1024], 0.0) @@ -103,7 +107,7 @@ class AssignOpTest(test.TestCase): p.initializer.run() def run_add(add_op): - sess.run(add_op) + self.evaluate(add_op) threads = [ self.checkedThread( @@ -114,10 +118,11 @@ class AssignOpTest(test.TestCase): for t in threads: t.join() - vals = p.eval() + vals = self.evaluate(p) ones = np.ones((1024, 1024)).astype(np.float32) self.assertAllEqual(vals, ones * 20) + @test_util.run_deprecated_v1 def testParallelAssignWithLocking(self): with self.cached_session() as sess: zeros_t = array_ops.fill([1024, 1024], 0.0) @@ -131,7 +136,7 @@ class AssignOpTest(test.TestCase): p.initializer.run() def run_assign(assign_op): - sess.run(assign_op) + self.evaluate(assign_op) threads = [ self.checkedThread( @@ -142,7 +147,7 @@ class AssignOpTest(test.TestCase): for t in threads: t.join() - vals = p.eval() + vals = self.evaluate(p) # Assert every element is the same, and taken from one of the assignments. self.assertTrue(vals[0, 0] > 0) diff --git a/tensorflow/python/kernel_tests/dense_update_ops_test.py b/tensorflow/python/kernel_tests/dense_update_ops_test.py index 3e0a03d634..309da88bef 100644 --- a/tensorflow/python/kernel_tests/dense_update_ops_test.py +++ b/tensorflow/python/kernel_tests/dense_update_ops_test.py @@ -21,6 +21,7 @@ from __future__ import print_function import numpy as np from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import state_ops from tensorflow.python.ops import variables @@ -36,8 +37,8 @@ class AssignOpTest(test.TestCase): p = variables.Variable(x) assign = state_ops.assign(p, y) p.initializer.run() - new_value = assign.eval() - return p.eval(), new_value + new_value = self.evaluate(assign) + return self.evaluate(p), new_value def _initAssignAddFetch(self, x, y, use_gpu=False): """Initialize a param to init, and compute param += y.""" @@ -45,8 +46,8 @@ class AssignOpTest(test.TestCase): p = variables.Variable(x) add = state_ops.assign_add(p, y) p.initializer.run() - new_value = add.eval() - return p.eval(), new_value + new_value = self.evaluate(add) + return self.evaluate(p), new_value def _initAssignSubFetch(self, x, y, use_gpu=False): """Initialize a param to init, and compute param -= y.""" @@ -54,8 +55,8 @@ class AssignOpTest(test.TestCase): p = variables.Variable(x) sub = state_ops.assign_sub(p, y) p.initializer.run() - new_value = sub.eval() - return p.eval(), new_value + new_value = self.evaluate(sub) + return self.evaluate(p), new_value def _testTypes(self, vals): for dtype in [np.float32, np.float64, np.int32, np.int64]: @@ -81,23 +82,26 @@ class AssignOpTest(test.TestCase): self.assertAllEqual(x - y, var_value) self.assertAllEqual(x - y, op_value) + @test_util.run_deprecated_v1 def testBasic(self): self._testTypes(np.arange(0, 20).reshape([4, 5])) + @test_util.run_deprecated_v1 def testAssignNonStrictShapeChecking(self): with self.cached_session(): data = array_ops.fill([1024, 1024], 0) p = variables.VariableV1([1]) a = state_ops.assign(p, data, validate_shape=False) a.op.run() - self.assertAllEqual(p.eval(), data.eval()) + self.assertAllEqual(p.eval(), self.evaluate(data)) # Assign to yet another shape data2 = array_ops.fill([10, 10], 1) a2 = state_ops.assign(p, data2, validate_shape=False) a2.op.run() - self.assertAllEqual(p.eval(), data2.eval()) + self.assertAllEqual(p.eval(), self.evaluate(data2)) + @test_util.run_deprecated_v1 def testInitRequiredAssignAdd(self): with self.cached_session(): p = variables.VariableV1(array_ops.fill([1024, 1024], 1), dtypes.int32) @@ -105,6 +109,7 @@ class AssignOpTest(test.TestCase): with self.assertRaisesOpError("use uninitialized"): a.op.run() + @test_util.run_deprecated_v1 def testInitRequiredAssignSub(self): with self.cached_session(): p = variables.VariableV1(array_ops.fill([1024, 1024], 1), dtypes.int32) diff --git a/tensorflow/python/kernel_tests/depthtospace_op_test.py b/tensorflow/python/kernel_tests/depthtospace_op_test.py index 13a28caf1f..96c9b5258e 100644 --- a/tensorflow/python/kernel_tests/depthtospace_op_test.py +++ b/tensorflow/python/kernel_tests/depthtospace_op_test.py @@ -53,12 +53,14 @@ class DepthToSpaceTest(test.TestCase): output_nhwc = test_util.NCHWToNHWC(output_nchw) self.assertAllEqual(output_nhwc.eval(), outputs) + @test_util.run_deprecated_v1 def testBasic(self): x_np = [[[[1, 2, 3, 4]]]] block_size = 2 x_out = [[[[1], [2]], [[3], [4]]]] self._testOne(x_np, block_size, x_out) + @test_util.run_deprecated_v1 def testBasicFloat16(self): x_np = [[[[1, 2, 3, 4]]]] block_size = 2 @@ -67,6 +69,7 @@ class DepthToSpaceTest(test.TestCase): # Tests for larger input dimensions. To make sure elements are # correctly ordered spatially. + @test_util.run_deprecated_v1 def testBlockSize2(self): x_np = [[[[1, 2, 3, 4], [5, 6, 7, 8]], @@ -79,6 +82,7 @@ class DepthToSpaceTest(test.TestCase): [[11], [12], [15], [16]]]] self._testOne(x_np, block_size, x_out) + @test_util.run_deprecated_v1 def testBlockSize2Batch10(self): block_size = 2 def batch_input_elt(i): @@ -106,15 +110,16 @@ class DepthToSpaceTest(test.TestCase): # test NHWC (default) on CPU x_tf = array_ops.depth_to_space(input_nhwc, block_size) self.assertAllEqual(x_tf.shape, x_out.shape) - x_tf.eval() + self.evaluate(x_tf) if test.is_gpu_available(): with self.cached_session(use_gpu=True): # test NHWC (default) on GPU x_tf = array_ops.depth_to_space(input_nhwc, block_size) self.assertAllEqual(x_tf.shape, x_out.shape) - x_tf.eval() + self.evaluate(x_tf) # Tests for different width and height. + @test_util.run_deprecated_v1 def testNonSquare(self): x_np = [[[[1, 10, 2, 20, 3, 30, 4, 40]], [[5, 50, 6, 60, 7, 70, 8, 80]], @@ -130,6 +135,7 @@ class DepthToSpaceTest(test.TestCase): # Tests for larger input dimensions. To make sure elements are # correctly ordered spatially. + @test_util.run_deprecated_v1 def testBlockSize4FlatInput(self): x_np = [[[[1, 2, 5, 6, 3, 4, 7, 8, 9, 10, 13, 14, 11, 12, 15, 16]]]] block_size = 4 @@ -141,6 +147,7 @@ class DepthToSpaceTest(test.TestCase): # Tests for larger input depths. # To make sure elements are properly interleaved in depth. + @test_util.run_deprecated_v1 def testDepthInterleaved(self): x_np = [[[[1, 10, 2, 20, 3, 30, 4, 40]]]] block_size = 2 @@ -150,6 +157,7 @@ class DepthToSpaceTest(test.TestCase): # Tests for larger input depths. Here an odd depth. # To make sure elements are properly interleaved in depth. + @test_util.run_deprecated_v1 def testDepthInterleavedDepth3(self): x_np = [[[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]]]] block_size = 2 @@ -159,6 +167,7 @@ class DepthToSpaceTest(test.TestCase): # Tests for larger input depths. # To make sure elements are properly interleaved in depth. + @test_util.run_deprecated_v1 def testDepthInterleavedLarger(self): x_np = [[[[1, 10, 2, 20, 3, 30, 4, 40], [5, 50, 6, 60, 7, 70, 8, 80]], @@ -175,6 +184,7 @@ class DepthToSpaceTest(test.TestCase): # Tests for a block larger for the depth. In this case should raise an # exception. + @test_util.run_deprecated_v1 def testBlockSizeTooLarge(self): x_np = [[[[1, 2, 3, 4], [5, 6, 7, 8]], @@ -185,18 +195,20 @@ class DepthToSpaceTest(test.TestCase): # divisible by 16. with self.assertRaises(ValueError): out_tf = array_ops.depth_to_space(x_np, block_size) - out_tf.eval() + self.evaluate(out_tf) # Test when the block size is 0. + @test_util.run_deprecated_v1 def testBlockSize0(self): x_np = [[[[1], [2]], [[3], [4]]]] block_size = 0 with self.assertRaises(ValueError): out_tf = array_ops.depth_to_space(x_np, block_size) - out_tf.eval() + self.evaluate(out_tf) # Test when the block size is 1. The block size should be > 1. + @test_util.run_deprecated_v1 def testBlockSizeOne(self): x_np = [[[[1, 1, 1, 1], [2, 2, 2, 2]], @@ -205,8 +217,9 @@ class DepthToSpaceTest(test.TestCase): block_size = 1 with self.assertRaises(ValueError): out_tf = array_ops.depth_to_space(x_np, block_size) - out_tf.eval() + self.evaluate(out_tf) + @test_util.run_deprecated_v1 def testBlockSizeLargerThanInput(self): # The block size is too large for this input. x_np = [[[[1], [2]], @@ -214,8 +227,9 @@ class DepthToSpaceTest(test.TestCase): block_size = 10 with self.assertRaises(ValueError): out_tf = array_ops.space_to_depth(x_np, block_size) - out_tf.eval() + self.evaluate(out_tf) + @test_util.run_deprecated_v1 def testBlockSizeNotDivisibleDepth(self): # The depth is not divisible by the square of the block size. x_np = [[[[1, 1, 1, 1], @@ -226,6 +240,7 @@ class DepthToSpaceTest(test.TestCase): with self.assertRaises(ValueError): _ = array_ops.space_to_depth(x_np, block_size) + @test_util.run_deprecated_v1 def testUnknownShape(self): t = array_ops.depth_to_space( array_ops.placeholder(dtypes.float32), block_size=4) @@ -277,7 +292,7 @@ class DepthToSpaceTest(test.TestCase): actual = array_ops.depth_to_space(t, block_size, data_format=data_format) with self.session(use_gpu=use_gpu) as sess: - actual_vals, expected_vals = sess.run([actual, expected]) + actual_vals, expected_vals = self.evaluate([actual, expected]) self.assertTrue(np.array_equal(actual_vals, expected_vals)) def testAgainstTranspose(self): @@ -343,11 +358,13 @@ class DepthToSpaceGradientTest(test.TestCase): # Don't use very large numbers as dimensions here, as the result is tensor # with cartesian product of the dimensions. + @test_util.run_deprecated_v1 def testSmall(self): block_size = 2 self._compare(3, 2, 5, 3, block_size, "NHWC") self._compare(3, 2, 5, 3, block_size, "NCHW") + @test_util.run_deprecated_v1 def testSmall2(self): block_size = 3 self._compare(1, 2, 3, 2, block_size, "NHWC") diff --git a/tensorflow/python/kernel_tests/depthwise_conv_op_test.py b/tensorflow/python/kernel_tests/depthwise_conv_op_test.py index 77b27c6c7e..f6d834c2f8 100644 --- a/tensorflow/python/kernel_tests/depthwise_conv_op_test.py +++ b/tensorflow/python/kernel_tests/depthwise_conv_op_test.py @@ -162,7 +162,7 @@ class DepthwiseConv2DTest(test.TestCase): conv_native = array_ops.transpose(conv_native, [0, 2, 3, 1]) try: - native_result = sess.run(conv_native) + native_result = self.evaluate(conv_native) except errors.InvalidArgumentError as e: # Grouped convolution kernel is only registered for cuDNN 7. Silently # return when we are running on an earlier version or without GPU. @@ -174,7 +174,7 @@ class DepthwiseConv2DTest(test.TestCase): conv_interface = nn_impl.depthwise_conv2d( t1, t2, strides=[1, stride, stride, 1], padding=padding) - interface_result = sess.run(conv_interface) + interface_result = self.evaluate(conv_interface) tf_logging.info( "data_type: %r, use_gpu: %r, grouped_conv: %r, max diff = %f", @@ -269,7 +269,7 @@ class DepthwiseConv2DTest(test.TestCase): t2 = constant_op.constant(x2, shape=filter_in_sizes) conv = nn_ops.depthwise_conv2d_native( t1, t2, strides=[1, stride, stride, 1], padding=padding) - value = sess.run(conv) + value = self.evaluate(conv) tf_logging.info("value = %r", value) self.assertArrayNear(expected, np.ravel(value), 1e-5) self.assertShapeEqual(value, conv) @@ -528,7 +528,7 @@ class DepthwiseConv2DTest(test.TestCase): t2 = constant_op.constant(x2, shape=output_sizes) backprop = nn_ops.depthwise_conv2d_native_backprop_input( t0, t1, t2, strides=[1, stride, stride, 1], padding=padding) - ret = backprop.eval() + ret = self.evaluate(backprop) self.assertShapeEqual(ret, backprop) return ret @@ -548,7 +548,7 @@ class DepthwiseConv2DTest(test.TestCase): t2 = constant_op.constant(x2, shape=output_sizes) backprop = nn_ops.depthwise_conv2d_native_backprop_input( t0, t1, t2, strides=[1, stride, stride, 1], padding=padding) - ret = backprop.eval() + ret = self.evaluate(backprop) self.assertShapeEqual(ret, backprop) return ret @@ -580,7 +580,7 @@ class DepthwiseConv2DTest(test.TestCase): t2 = constant_op.constant(x2, shape=output_sizes) backprop = nn_ops.depthwise_conv2d_native_backprop_filter( t0, t1, t2, strides=[1, stride, stride, 1], padding=padding) - ret = backprop.eval() + ret = self.evaluate(backprop) self.assertShapeEqual(ret, backprop) return ret @@ -600,7 +600,7 @@ class DepthwiseConv2DTest(test.TestCase): t2 = constant_op.constant(x2, shape=output_sizes) backprop = nn_ops.depthwise_conv2d_native_backprop_filter( t0, t1, t2, strides=[1, stride, stride, 1], padding=padding) - ret = backprop.eval() + ret = self.evaluate(backprop) self.assertShapeEqual(ret, backprop) return ret diff --git a/tensorflow/python/kernel_tests/determinant_op_test.py b/tensorflow/python/kernel_tests/determinant_op_test.py index da33b2848b..d6ef9e70b8 100644 --- a/tensorflow/python/kernel_tests/determinant_op_test.py +++ b/tensorflow/python/kernel_tests/determinant_op_test.py @@ -23,6 +23,7 @@ import numpy as np from tensorflow.python.client import session from tensorflow.python.framework import constant_op from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import gen_linalg_ops from tensorflow.python.ops import linalg_ops @@ -35,7 +36,7 @@ from tensorflow.python.platform import test class DeterminantOpTest(test.TestCase): def _compareDeterminantBase(self, matrix_x, tf_ans): - out = tf_ans.eval() + out = self.evaluate(tf_ans) shape = matrix_x.shape if shape[-1] == 0 and shape[-2] == 0: np_ans = np.ones(shape[:-2]).astype(matrix_x.dtype) @@ -54,15 +55,15 @@ class DeterminantOpTest(test.TestCase): np_ans = np_ans.astype(matrix_x.dtype) self.assertShapeEqual(np_ans, abs_log_det_tf) - sign_tf_val = sign_tf.eval() - abs_log_det_tf_val = abs_log_det_tf.eval() + sign_tf_val = self.evaluate(sign_tf) + abs_log_det_tf_val = self.evaluate(abs_log_det_tf) self.assertAllClose( sign_tf_val * np.exp(abs_log_det_tf_val), np_sign * np.exp(np_ans), atol=5e-5) def _compareDeterminant(self, matrix_x): - with self.cached_session(use_gpu=True): + with test_util.use_gpu(): self._compareDeterminantBase(matrix_x, linalg_ops.matrix_determinant(matrix_x)) self._compareLogDeterminantBase( @@ -155,7 +156,7 @@ class DeterminantOpTest(test.TestCase): matrix2 = random_ops.random_normal([5, 5], seed=42) det1 = linalg_ops.matrix_determinant(matrix1) det2 = linalg_ops.matrix_determinant(matrix2) - det1_val, det2_val = sess.run([det1, det2]) + det1_val, det2_val = self.evaluate([det1, det2]) self.assertEqual(det1_val, det2_val) diff --git a/tensorflow/python/kernel_tests/diag_op_test.py b/tensorflow/python/kernel_tests/diag_op_test.py index 9e43258fa2..ed2a9e8e47 100644 --- a/tensorflow/python/kernel_tests/diag_op_test.py +++ b/tensorflow/python/kernel_tests/diag_op_test.py @@ -22,6 +22,7 @@ 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 ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import gradients_impl @@ -31,6 +32,7 @@ from tensorflow.python.platform import tf_logging class MatrixDiagTest(test.TestCase): + @test_util.run_deprecated_v1 def testVector(self): with self.session(use_gpu=True): v = np.array([1.0, 2.0, 3.0]) @@ -49,6 +51,7 @@ class MatrixDiagTest(test.TestCase): self.assertEqual((2, 3, 3), v_batch_diag.get_shape()) self.assertAllEqual(v_batch_diag.eval(), mat_batch) + @test_util.run_deprecated_v1 def testBatchVector(self): self._testBatchVector(np.float32) self._testBatchVector(np.float64) @@ -56,16 +59,19 @@ class MatrixDiagTest(test.TestCase): self._testBatchVector(np.int64) self._testBatchVector(np.bool) + @test_util.run_deprecated_v1 def testInvalidShape(self): with self.assertRaisesRegexp(ValueError, "must be at least rank 1"): array_ops.matrix_diag(0) + @test_util.run_deprecated_v1 def testInvalidShapeAtEval(self): with self.session(use_gpu=True): v = array_ops.placeholder(dtype=dtypes_lib.float32) with self.assertRaisesOpError("input must be at least 1-dim"): array_ops.matrix_diag(v).eval(feed_dict={v: 0.0}) + @test_util.run_deprecated_v1 def testGrad(self): shapes = ((3,), (7, 4)) with self.session(use_gpu=True): @@ -81,6 +87,7 @@ class MatrixDiagTest(test.TestCase): class MatrixSetDiagTest(test.TestCase): + @test_util.run_deprecated_v1 def testSquare(self): with self.session(use_gpu=True): v = np.array([1.0, 2.0, 3.0]) @@ -89,8 +96,9 @@ class MatrixSetDiagTest(test.TestCase): [1.0, 1.0, 3.0]]) output = array_ops.matrix_set_diag(mat, v) self.assertEqual((3, 3), output.get_shape()) - self.assertAllEqual(mat_set_diag, output.eval()) + self.assertAllEqual(mat_set_diag, self.evaluate(output)) + @test_util.run_deprecated_v1 def testRectangular(self): with self.session(use_gpu=True): v = np.array([3.0, 4.0]) @@ -98,14 +106,14 @@ class MatrixSetDiagTest(test.TestCase): expected = np.array([[3.0, 1.0, 0.0], [1.0, 4.0, 1.0]]) output = array_ops.matrix_set_diag(mat, v) self.assertEqual((2, 3), output.get_shape()) - self.assertAllEqual(expected, output.eval()) + self.assertAllEqual(expected, self.evaluate(output)) v = np.array([3.0, 4.0]) mat = np.array([[0.0, 1.0], [1.0, 0.0], [1.0, 1.0]]) expected = np.array([[3.0, 1.0], [1.0, 4.0], [1.0, 1.0]]) output = array_ops.matrix_set_diag(mat, v) self.assertEqual((3, 2), output.get_shape()) - self.assertAllEqual(expected, output.eval()) + self.assertAllEqual(expected, self.evaluate(output)) def _testSquareBatch(self, dtype): with self.cached_session(use_gpu=True): @@ -121,8 +129,9 @@ class MatrixSetDiagTest(test.TestCase): output = array_ops.matrix_set_diag(mat_batch, v_batch) self.assertEqual((2, 3, 3), output.get_shape()) - self.assertAllEqual(mat_set_diag_batch, output.eval()) + self.assertAllEqual(mat_set_diag_batch, self.evaluate(output)) + @test_util.run_deprecated_v1 def testSquareBatch(self): self._testSquareBatch(np.float32) self._testSquareBatch(np.float64) @@ -130,6 +139,7 @@ class MatrixSetDiagTest(test.TestCase): self._testSquareBatch(np.int64) self._testSquareBatch(np.bool) + @test_util.run_deprecated_v1 def testRectangularBatch(self): with self.session(use_gpu=True): v_batch = np.array([[-1.0, -2.0], [-4.0, -5.0]]) @@ -140,14 +150,16 @@ class MatrixSetDiagTest(test.TestCase): [[-4.0, 0.0, 4.0], [0.0, -5.0, 0.0]]]) output = array_ops.matrix_set_diag(mat_batch, v_batch) self.assertEqual((2, 2, 3), output.get_shape()) - self.assertAllEqual(mat_set_diag_batch, output.eval()) + self.assertAllEqual(mat_set_diag_batch, self.evaluate(output)) + @test_util.run_deprecated_v1 def testInvalidShape(self): with self.assertRaisesRegexp(ValueError, "must be at least rank 2"): array_ops.matrix_set_diag(0, [0]) with self.assertRaisesRegexp(ValueError, "must be at least rank 1"): array_ops.matrix_set_diag([[0]], 0) + @test_util.run_deprecated_v1 def testInvalidShapeAtEval(self): with self.session(use_gpu=True): v = array_ops.placeholder(dtype=dtypes_lib.float32) @@ -157,6 +169,7 @@ class MatrixSetDiagTest(test.TestCase): r"but received input shape: \[1,1\] and diagonal shape: \[\]"): array_ops.matrix_set_diag([[v]], v).eval(feed_dict={v: 0.0}) + @test_util.run_deprecated_v1 def testGrad(self): shapes = ((3, 4, 4), (3, 3, 4), (3, 4, 3), (7, 4, 8, 8)) with self.session(use_gpu=True): @@ -178,6 +191,7 @@ class MatrixSetDiagTest(test.TestCase): y.get_shape().as_list()) self.assertLess(error_x_diag, 1e-4) + @test_util.run_deprecated_v1 def testGradWithNoShapeInformation(self): with self.session(use_gpu=True) as sess: v = array_ops.placeholder(dtype=dtypes_lib.float32) @@ -200,6 +214,7 @@ class MatrixSetDiagTest(test.TestCase): class MatrixDiagPartTest(test.TestCase): + @test_util.run_deprecated_v1 def testSquare(self): with self.session(use_gpu=True): v = np.array([1.0, 2.0, 3.0]) @@ -208,6 +223,7 @@ class MatrixDiagPartTest(test.TestCase): self.assertEqual((3,), mat_diag.get_shape()) self.assertAllEqual(mat_diag.eval(), v) + @test_util.run_deprecated_v1 def testRectangular(self): with self.session(use_gpu=True): mat = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]) @@ -228,6 +244,7 @@ class MatrixDiagPartTest(test.TestCase): self.assertEqual((2, 3), mat_batch_diag.get_shape()) self.assertAllEqual(mat_batch_diag.eval(), v_batch) + @test_util.run_deprecated_v1 def testSquareBatch(self): self._testSquareBatch(np.float32) self._testSquareBatch(np.float64) @@ -235,6 +252,7 @@ class MatrixDiagPartTest(test.TestCase): self._testSquareBatch(np.int64) self._testSquareBatch(np.bool) + @test_util.run_deprecated_v1 def testRectangularBatch(self): with self.session(use_gpu=True): v_batch = np.array([[1.0, 2.0], [4.0, 5.0]]) @@ -245,16 +263,19 @@ class MatrixDiagPartTest(test.TestCase): self.assertEqual((2, 2), mat_batch_diag.get_shape()) self.assertAllEqual(mat_batch_diag.eval(), v_batch) + @test_util.run_deprecated_v1 def testInvalidShape(self): with self.assertRaisesRegexp(ValueError, "must be at least rank 2"): array_ops.matrix_diag_part(0) + @test_util.run_deprecated_v1 def testInvalidShapeAtEval(self): with self.session(use_gpu=True): v = array_ops.placeholder(dtype=dtypes_lib.float32) with self.assertRaisesOpError("input must be at least 2-dim"): array_ops.matrix_diag_part(v).eval(feed_dict={v: 0.0}) + @test_util.run_deprecated_v1 def testGrad(self): shapes = ((3, 3), (2, 3), (3, 2), (5, 3, 3)) with self.session(use_gpu=True): @@ -273,9 +294,9 @@ class DiagTest(test.TestCase): def _diagOp(self, diag, dtype, expected_ans, use_gpu): with self.cached_session(use_gpu=use_gpu): tf_ans = array_ops.diag(ops.convert_to_tensor(diag.astype(dtype))) - out = tf_ans.eval() + out = self.evaluate(tf_ans) tf_ans_inv = array_ops.diag_part(expected_ans) - inv_out = tf_ans_inv.eval() + inv_out = self.evaluate(tf_ans_inv) self.assertAllClose(out, expected_ans) self.assertAllClose(inv_out, diag) self.assertShapeEqual(expected_ans, tf_ans) @@ -407,6 +428,7 @@ class DiagTest(test.TestCase): dtype=dtype) self.diagOp(x, dtype, expected_ans) + @test_util.run_deprecated_v1 def testInvalidRank(self): with self.assertRaisesRegexp(ValueError, "must be at least rank 1"): array_ops.diag(0.0) @@ -421,7 +443,7 @@ class DiagPartOpTest(test.TestCase): with self.cached_session(use_gpu=use_gpu): tensor = ops.convert_to_tensor(tensor.astype(dtype)) tf_ans_inv = array_ops.diag_part(tensor) - inv_out = tf_ans_inv.eval() + inv_out = self.evaluate(tf_ans_inv) self.assertAllClose(inv_out, expected_ans) self.assertShapeEqual(expected_ans, tf_ans_inv) @@ -445,7 +467,7 @@ class DiagPartOpTest(test.TestCase): t = ops.convert_to_tensor(x.astype(np.float32)) t.set_shape(shape) tf_ans = array_ops.diag_part(t) - out = tf_ans.eval() + out = self.evaluate(tf_ans) self.assertAllClose(out, expected_ans) self.assertShapeEqual(expected_ans, tf_ans) @@ -476,6 +498,7 @@ class DiagPartOpTest(test.TestCase): self.diagPartOp(x, np.complex64, expected_ans) self.diagPartOp(x, np.complex128, expected_ans) + @test_util.run_deprecated_v1 def testOddRank(self): w = np.random.rand(2) x = np.random.rand(2, 2, 2) @@ -484,6 +507,7 @@ class DiagPartOpTest(test.TestCase): with self.assertRaises(ValueError): array_ops.diag_part(0.0) + @test_util.run_deprecated_v1 def testUnevenDimensions(self): w = np.random.rand(2, 5) x = np.random.rand(2, 1, 2, 3) @@ -493,6 +517,7 @@ class DiagPartOpTest(test.TestCase): class DiagGradOpTest(test.TestCase): + @test_util.run_deprecated_v1 def testDiagGrad(self): np.random.seed(0) shapes = ((3,), (3, 3), (3, 3, 3)) @@ -513,6 +538,7 @@ class DiagGradOpTest(test.TestCase): class DiagGradPartOpTest(test.TestCase): + @test_util.run_deprecated_v1 def testDiagPartGrad(self): np.random.seed(0) shapes = ((3, 3), (3, 3, 3, 3)) diff --git a/tensorflow/python/kernel_tests/distributions/bernoulli_test.py b/tensorflow/python/kernel_tests/distributions/bernoulli_test.py index 37b35ba51a..e6d560b4bc 100644 --- a/tensorflow/python/kernel_tests/distributions/bernoulli_test.py +++ b/tensorflow/python/kernel_tests/distributions/bernoulli_test.py @@ -151,6 +151,7 @@ class BernoulliTest(test.TestCase): self.assertAllClose(self.evaluate(dist.prob(x)), expected_pmf) self.assertAllClose(self.evaluate(dist.log_prob(x)), np.log(expected_pmf)) + @test_util.run_deprecated_v1 def testPmfCorrectBroadcastDynamicShape(self): with self.cached_session(): p = array_ops.placeholder(dtype=dtypes.float32) @@ -167,6 +168,7 @@ class BernoulliTest(test.TestCase): }), [[0.2, 0.7, 0.4]]) @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def testPmfInvalid(self): p = [0.1, 0.2, 0.7] dist = bernoulli.Bernoulli(probs=p, validate_args=True) @@ -193,6 +195,7 @@ class BernoulliTest(test.TestCase): self.evaluate( bernoulli.Bernoulli(probs=p, validate_args=False).log_prob(samps))) + @test_util.run_deprecated_v1 def testBroadcasting(self): with self.cached_session(): p = array_ops.placeholder(dtypes.float32) @@ -207,6 +210,7 @@ class BernoulliTest(test.TestCase): p: [0.5, 0.5, 0.5] })) + @test_util.run_deprecated_v1 def testPmfShapes(self): with self.cached_session(): p = array_ops.placeholder(dtypes.float32, shape=[None, 1]) @@ -276,6 +280,7 @@ class BernoulliTest(test.TestCase): grad_p = tape.gradient(samples, p) self.assertIsNone(grad_p) + @test_util.run_deprecated_v1 def testSampleActsLikeSampleN(self): with self.cached_session() as sess: p = [0.2, 0.6] diff --git a/tensorflow/python/kernel_tests/distributions/bijector_test.py b/tensorflow/python/kernel_tests/distributions/bijector_test.py index e20f59f48a..a0e0a36fec 100644 --- a/tensorflow/python/kernel_tests/distributions/bijector_test.py +++ b/tensorflow/python/kernel_tests/distributions/bijector_test.py @@ -132,6 +132,7 @@ class BijectorTestEventNdims(test.TestCase): with self.assertRaisesRegexp(ValueError, "Expected scalar"): bij.inverse_log_det_jacobian(1., event_ndims=(1, 2)) + @test_util.run_deprecated_v1 def testBijectorDynamicEventNdims(self): bij = BrokenBijector(validate_args=True) event_ndims = array_ops.placeholder(dtype=np.int32, shape=None) @@ -301,6 +302,7 @@ class BijectorReduceEventDimsTest(test.TestCase): 8., self.evaluate(bij.inverse_log_det_jacobian(x, event_ndims=2))) + @test_util.run_deprecated_v1 def testHandlesNonStaticEventNdims(self): x_ = [[[1., 2.], [3., 4.]]] x = array_ops.placeholder_with_default(x_, shape=None) diff --git a/tensorflow/python/kernel_tests/distributions/categorical_test.py b/tensorflow/python/kernel_tests/distributions/categorical_test.py index c6bb06eab3..ec1d4ed207 100644 --- a/tensorflow/python/kernel_tests/distributions/categorical_test.py +++ b/tensorflow/python/kernel_tests/distributions/categorical_test.py @@ -25,6 +25,7 @@ from tensorflow.python.eager import backprop from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import tensor_util +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradients_impl from tensorflow.python.ops import math_ops @@ -44,6 +45,7 @@ def make_categorical(batch_shape, num_classes, dtype=dtypes.int32): class CategoricalTest(test.TestCase, parameterized.TestCase): + @test_util.run_deprecated_v1 def testP(self): p = [0.2, 0.8] dist = categorical.Categorical(probs=p) @@ -51,6 +53,7 @@ class CategoricalTest(test.TestCase, parameterized.TestCase): self.assertAllClose(p, dist.probs.eval()) self.assertAllEqual([2], dist.logits.get_shape()) + @test_util.run_deprecated_v1 def testLogits(self): p = np.array([0.2, 0.8], dtype=np.float32) logits = np.log(p) - 50. @@ -61,6 +64,7 @@ class CategoricalTest(test.TestCase, parameterized.TestCase): self.assertAllClose(dist.probs.eval(), p) self.assertAllClose(dist.logits.eval(), logits) + @test_util.run_deprecated_v1 def testShapes(self): with self.cached_session(): for batch_shape in ([], [1], [2, 3, 4]): @@ -107,6 +111,7 @@ class CategoricalTest(test.TestCase, parameterized.TestCase): self.assertEqual(dist.dtype, dtype) self.assertEqual(dist.dtype, dist.sample(5).dtype) + @test_util.run_deprecated_v1 def testUnknownShape(self): with self.cached_session(): logits = array_ops.placeholder(dtype=dtypes.float32) @@ -121,18 +126,21 @@ class CategoricalTest(test.TestCase, parameterized.TestCase): feed_dict={logits: [[-1000.0, 1000.0], [1000.0, -1000.0]]}) self.assertAllEqual([1, 0], sample_value_batch) + @test_util.run_deprecated_v1 def testPMFWithBatch(self): histograms = [[0.2, 0.8], [0.6, 0.4]] dist = categorical.Categorical(math_ops.log(histograms) - 50.) with self.cached_session(): self.assertAllClose(dist.prob([0, 1]).eval(), [0.2, 0.4]) + @test_util.run_deprecated_v1 def testPMFNoBatch(self): histograms = [0.2, 0.8] dist = categorical.Categorical(math_ops.log(histograms) - 50.) with self.cached_session(): self.assertAllClose(dist.prob(0).eval(), 0.2) + @test_util.run_deprecated_v1 def testCDFWithDynamicEventShapeKnownNdims(self): """Test that dynamically-sized events with unknown shape work.""" batch_size = 2 @@ -184,6 +192,7 @@ class CategoricalTest(test.TestCase, parameterized.TestCase): actual_cdf = self.evaluate(cdf_op) self.assertAllClose(actual_cdf, expected_cdf) + @test_util.run_deprecated_v1 def testCDFWithBatch(self): histograms = [[0.1, 0.2, 0.3, 0.25, 0.15], [0.0, 0.75, 0.2, 0.05, 0.0]] @@ -195,6 +204,7 @@ class CategoricalTest(test.TestCase, parameterized.TestCase): with self.cached_session(): self.assertAllClose(cdf_op.eval(), expected_cdf) + @test_util.run_deprecated_v1 def testCDFNoBatch(self): histogram = [0.1, 0.2, 0.3, 0.4] event = 2 @@ -205,6 +215,7 @@ class CategoricalTest(test.TestCase, parameterized.TestCase): with self.cached_session(): self.assertAlmostEqual(cdf_op.eval(), expected_cdf) + @test_util.run_deprecated_v1 def testCDFBroadcasting(self): # shape: [batch=2, n_bins=3] histograms = [[0.2, 0.1, 0.7], @@ -287,7 +298,7 @@ class CategoricalTest(test.TestCase, parameterized.TestCase): } with self.cached_session() as sess: - run_result = sess.run(to_run) + run_result = self.evaluate(to_run) self.assertAllEqual(run_result["cat_prob"].shape, run_result["norm_prob"].shape) @@ -298,6 +309,7 @@ class CategoricalTest(test.TestCase, parameterized.TestCase): self.assertAllEqual(run_result["cat_log_cdf"].shape, run_result["norm_log_cdf"].shape) + @test_util.run_deprecated_v1 def testLogPMF(self): logits = np.log([[0.2, 0.8], [0.6, 0.4]]) - 50. dist = categorical.Categorical(logits) @@ -305,6 +317,7 @@ class CategoricalTest(test.TestCase, parameterized.TestCase): self.assertAllClose(dist.log_prob([0, 1]).eval(), np.log([0.2, 0.4])) self.assertAllClose(dist.log_prob([0.0, 1.0]).eval(), np.log([0.2, 0.4])) + @test_util.run_deprecated_v1 def testEntropyNoBatch(self): logits = np.log([0.2, 0.8]) - 50. dist = categorical.Categorical(logits) @@ -312,6 +325,7 @@ class CategoricalTest(test.TestCase, parameterized.TestCase): self.assertAllClose(dist.entropy().eval(), -(0.2 * np.log(0.2) + 0.8 * np.log(0.8))) + @test_util.run_deprecated_v1 def testEntropyWithBatch(self): logits = np.log([[0.2, 0.8], [0.6, 0.4]]) - 50. dist = categorical.Categorical(logits) @@ -321,6 +335,7 @@ class CategoricalTest(test.TestCase, parameterized.TestCase): -(0.6 * np.log(0.6) + 0.4 * np.log(0.4)) ]) + @test_util.run_deprecated_v1 def testEntropyGradient(self): with self.cached_session() as sess: logits = constant_op.constant([[1., 2., 3.], [2., 5., 1.]]) @@ -355,7 +370,7 @@ class CategoricalTest(test.TestCase, parameterized.TestCase): samples = dist.sample(n, seed=123) samples.set_shape([n, 1, 2]) self.assertEqual(samples.dtype, dtypes.int32) - sample_values = samples.eval() + sample_values = self.evaluate(samples) self.assertFalse(np.any(sample_values < 0)) self.assertFalse(np.any(sample_values > 1)) self.assertAllClose( @@ -371,7 +386,7 @@ class CategoricalTest(test.TestCase, parameterized.TestCase): dist = categorical.Categorical(math_ops.log(histograms) - 50.) samples = dist.sample((100, 100), seed=123) prob = dist.prob(samples) - prob_val = prob.eval() + prob_val = self.evaluate(prob) self.assertAllClose( [0.2**2 + 0.8**2], [prob_val[:, :, :, 0].mean()], atol=1e-2) self.assertAllClose( @@ -393,26 +408,26 @@ class CategoricalTest(test.TestCase, parameterized.TestCase): dist = categorical.Categorical(math_ops.log(histograms) - 50.) prob = dist.prob(1) - self.assertAllClose([[0.8, 0.6]], prob.eval()) + self.assertAllClose([[0.8, 0.6]], self.evaluate(prob)) prob = dist.prob([1]) - self.assertAllClose([[0.8, 0.6]], prob.eval()) + self.assertAllClose([[0.8, 0.6]], self.evaluate(prob)) prob = dist.prob([0, 1]) - self.assertAllClose([[0.2, 0.6]], prob.eval()) + self.assertAllClose([[0.2, 0.6]], self.evaluate(prob)) prob = dist.prob([[0, 1]]) - self.assertAllClose([[0.2, 0.6]], prob.eval()) + self.assertAllClose([[0.2, 0.6]], self.evaluate(prob)) prob = dist.prob([[[0, 1]]]) - self.assertAllClose([[[0.2, 0.6]]], prob.eval()) + self.assertAllClose([[[0.2, 0.6]]], self.evaluate(prob)) prob = dist.prob([[1, 0], [0, 1]]) - self.assertAllClose([[0.8, 0.4], [0.2, 0.6]], prob.eval()) + self.assertAllClose([[0.8, 0.4], [0.2, 0.6]], self.evaluate(prob)) prob = dist.prob([[[1, 1], [1, 0]], [[1, 0], [0, 1]]]) self.assertAllClose([[[0.8, 0.6], [0.8, 0.4]], [[0.8, 0.4], [0.2, 0.6]]], - prob.eval()) + self.evaluate(prob)) def testLogPMFShape(self): with self.cached_session(): @@ -440,12 +455,14 @@ class CategoricalTest(test.TestCase, parameterized.TestCase): self.assertEqual(3, log_prob.get_shape().ndims) self.assertAllEqual([2, 2, 2], log_prob.get_shape()) + @test_util.run_deprecated_v1 def testMode(self): with self.cached_session(): histograms = [[[0.2, 0.8], [0.6, 0.4]]] dist = categorical.Categorical(math_ops.log(histograms) - 50.) self.assertAllEqual(dist.mode().eval(), [[1, 0]]) + @test_util.run_deprecated_v1 def testCategoricalCategoricalKL(self): def np_softmax(logits): @@ -462,7 +479,7 @@ class CategoricalTest(test.TestCase, parameterized.TestCase): b = categorical.Categorical(logits=b_logits) kl = kullback_leibler.kl_divergence(a, b) - kl_val = sess.run(kl) + kl_val = self.evaluate(kl) # Make sure KL(a||a) is 0 kl_same = sess.run(kullback_leibler.kl_divergence(a, a)) diff --git a/tensorflow/python/kernel_tests/distributions/dirichlet_multinomial_test.py b/tensorflow/python/kernel_tests/distributions/dirichlet_multinomial_test.py index d558ca09cc..c530037e1e 100644 --- a/tensorflow/python/kernel_tests/distributions/dirichlet_multinomial_test.py +++ b/tensorflow/python/kernel_tests/distributions/dirichlet_multinomial_test.py @@ -22,6 +22,7 @@ from tensorflow.python.eager import backprop from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops.distributions import dirichlet_multinomial @@ -36,6 +37,7 @@ class DirichletMultinomialTest(test.TestCase): def setUp(self): self._rng = np.random.RandomState(42) + @test_util.run_deprecated_v1 def testSimpleShapes(self): with self.cached_session(): alpha = np.random.rand(3) @@ -45,6 +47,7 @@ class DirichletMultinomialTest(test.TestCase): self.assertEqual(tensor_shape.TensorShape([3]), dist.event_shape) self.assertEqual(tensor_shape.TensorShape([]), dist.batch_shape) + @test_util.run_deprecated_v1 def testComplexShapes(self): with self.cached_session(): alpha = np.random.rand(3, 2, 2) @@ -55,6 +58,7 @@ class DirichletMultinomialTest(test.TestCase): self.assertEqual(tensor_shape.TensorShape([2]), dist.event_shape) self.assertEqual(tensor_shape.TensorShape([3, 2]), dist.batch_shape) + @test_util.run_deprecated_v1 def testNproperty(self): alpha = [[1., 2, 3]] n = [[5.]] @@ -63,6 +67,7 @@ class DirichletMultinomialTest(test.TestCase): self.assertEqual([1, 1], dist.total_count.get_shape()) self.assertAllClose(n, dist.total_count.eval()) + @test_util.run_deprecated_v1 def testAlphaProperty(self): alpha = [[1., 2, 3]] with self.cached_session(): @@ -70,6 +75,7 @@ class DirichletMultinomialTest(test.TestCase): self.assertEqual([1, 3], dist.concentration.get_shape()) self.assertAllClose(alpha, dist.concentration.eval()) + @test_util.run_deprecated_v1 def testPmfNandCountsAgree(self): alpha = [[1., 2, 3]] n = [[5.]] @@ -83,6 +89,7 @@ class DirichletMultinomialTest(test.TestCase): "last-dimension must sum to `self.total_count`"): dist.prob([3., 3, 0]).eval() + @test_util.run_deprecated_v1 def testPmfNonIntegerCounts(self): alpha = [[1., 2, 3]] n = [[5.]] @@ -110,7 +117,7 @@ class DirichletMultinomialTest(test.TestCase): counts = [1., 0] dist = ds.DirichletMultinomial(1., alpha) pmf = dist.prob(counts) - self.assertAllClose(1 / 3., pmf.eval()) + self.assertAllClose(1 / 3., self.evaluate(pmf)) self.assertEqual((), pmf.get_shape()) def testPmfBothZeroBatchesNontrivialN(self): @@ -122,7 +129,7 @@ class DirichletMultinomialTest(test.TestCase): counts = [3., 2] dist = ds.DirichletMultinomial(5., alpha) pmf = dist.prob(counts) - self.assertAllClose(1 / 7., pmf.eval()) + self.assertAllClose(1 / 7., self.evaluate(pmf)) self.assertEqual((), pmf.get_shape()) def testPmfBothZeroBatchesMultidimensionalN(self): @@ -134,7 +141,7 @@ class DirichletMultinomialTest(test.TestCase): n = np.full([4, 3], 5., dtype=np.float32) dist = ds.DirichletMultinomial(n, alpha) pmf = dist.prob(counts) - self.assertAllClose([[1 / 7., 1 / 7., 1 / 7.]] * 4, pmf.eval()) + self.assertAllClose([[1 / 7., 1 / 7., 1 / 7.]] * 4, self.evaluate(pmf)) self.assertEqual((4, 3), pmf.get_shape()) def testPmfAlphaStretchedInBroadcastWhenSameRank(self): @@ -145,7 +152,7 @@ class DirichletMultinomialTest(test.TestCase): counts = [[1., 0], [0., 1]] dist = ds.DirichletMultinomial([1.], alpha) pmf = dist.prob(counts) - self.assertAllClose([1 / 3., 2 / 3.], pmf.eval()) + self.assertAllClose([1 / 3., 2 / 3.], self.evaluate(pmf)) self.assertAllEqual([2], pmf.get_shape()) def testPmfAlphaStretchedInBroadcastWhenLowerRank(self): @@ -155,7 +162,7 @@ class DirichletMultinomialTest(test.TestCase): alpha = [1., 2] counts = [[1., 0], [0., 1]] pmf = ds.DirichletMultinomial(1., alpha).prob(counts) - self.assertAllClose([1 / 3., 2 / 3.], pmf.eval()) + self.assertAllClose([1 / 3., 2 / 3.], self.evaluate(pmf)) self.assertAllEqual([2], pmf.get_shape()) def testPmfCountsStretchedInBroadcastWhenSameRank(self): @@ -165,7 +172,7 @@ class DirichletMultinomialTest(test.TestCase): alpha = [[1., 2], [2., 3]] counts = [[1., 0]] pmf = ds.DirichletMultinomial([1., 1.], alpha).prob(counts) - self.assertAllClose([1 / 3., 2 / 5.], pmf.eval()) + self.assertAllClose([1 / 3., 2 / 5.], self.evaluate(pmf)) self.assertAllEqual([2], pmf.get_shape()) def testPmfCountsStretchedInBroadcastWhenLowerRank(self): @@ -175,9 +182,10 @@ class DirichletMultinomialTest(test.TestCase): alpha = [[1., 2], [2., 3]] counts = [1., 0] pmf = ds.DirichletMultinomial(1., alpha).prob(counts) - self.assertAllClose([1 / 3., 2 / 5.], pmf.eval()) + self.assertAllClose([1 / 3., 2 / 5.], self.evaluate(pmf)) self.assertAllEqual([2], pmf.get_shape()) + @test_util.run_deprecated_v1 def testPmfForOneVoteIsTheMeanWithOneRecordInput(self): # The probabilities of one vote falling into class k is the mean for class # k. @@ -194,6 +202,7 @@ class DirichletMultinomialTest(test.TestCase): self.assertAllEqual([3], mean.shape) self.assertAllEqual([], pmf.shape) + @test_util.run_deprecated_v1 def testMeanDoubleTwoVotes(self): # The probabilities of two votes falling into class k for # DirichletMultinomial(2, alpha) is twice as much as the probability of one @@ -215,6 +224,7 @@ class DirichletMultinomialTest(test.TestCase): self.assertAllClose(mean2[class_num], 2 * mean1[class_num]) self.assertAllEqual([3], mean1.shape) + @test_util.run_deprecated_v1 def testCovarianceFromSampling(self): # We will test mean, cov, var, stddev on a DirichletMultinomial constructed # via broadcast between alpha, n. @@ -289,7 +299,7 @@ class DirichletMultinomialTest(test.TestCase): expected_covariance = n * (n + alpha_0) / (1 + alpha_0) * shared_matrix self.assertEqual([2, 2], covariance.get_shape()) - self.assertAllClose(expected_covariance, covariance.eval()) + self.assertAllClose(expected_covariance, self.evaluate(covariance)) def testCovarianceNAlphaBroadcast(self): alpha_v = [1., 2, 3] @@ -327,7 +337,7 @@ class DirichletMultinomialTest(test.TestCase): ns * (ns + alpha_0) / (1 + alpha_0))[..., array_ops.newaxis] self.assertEqual([4, 3, 3], covariance.get_shape()) - self.assertAllClose(expected_covariance, covariance.eval()) + self.assertAllClose(expected_covariance, self.evaluate(covariance)) def testCovarianceMultidimensional(self): alpha = np.random.rand(3, 5, 4).astype(np.float32) @@ -353,7 +363,7 @@ class DirichletMultinomialTest(test.TestCase): with self.cached_session(): dist = ds.DirichletMultinomial(0., alpha) pmf = dist.prob(counts) - self.assertAllClose(1.0, pmf.eval()) + self.assertAllClose(1.0, self.evaluate(pmf)) self.assertEqual((), pmf.get_shape()) def testLargeTauGivesPreciseProbabilities(self): @@ -368,7 +378,7 @@ class DirichletMultinomialTest(test.TestCase): with self.cached_session(): dist = ds.DirichletMultinomial(1., alpha) pmf = dist.prob(counts) - self.assertAllClose(0.8, pmf.eval(), atol=1e-4) + self.assertAllClose(0.8, self.evaluate(pmf), atol=1e-4) self.assertEqual((), pmf.get_shape()) # Two (three sided) coin flips. Prob[coin 3] = 0.8. @@ -376,7 +386,7 @@ class DirichletMultinomialTest(test.TestCase): with self.cached_session(): dist = ds.DirichletMultinomial(2., alpha) pmf = dist.prob(counts) - self.assertAllClose(0.8**2, pmf.eval(), atol=1e-2) + self.assertAllClose(0.8**2, self.evaluate(pmf), atol=1e-2) self.assertEqual((), pmf.get_shape()) # Three (three sided) coin flips. @@ -384,7 +394,7 @@ class DirichletMultinomialTest(test.TestCase): with self.cached_session(): dist = ds.DirichletMultinomial(3., alpha) pmf = dist.prob(counts) - self.assertAllClose(3 * 0.1 * 0.8 * 0.8, pmf.eval(), atol=1e-2) + self.assertAllClose(3 * 0.1 * 0.8 * 0.8, self.evaluate(pmf), atol=1e-2) self.assertEqual((), pmf.get_shape()) def testSmallTauPrefersCorrelatedResults(self): @@ -399,7 +409,7 @@ class DirichletMultinomialTest(test.TestCase): with self.cached_session(): dist = ds.DirichletMultinomial(1., alpha) pmf = dist.prob(counts) - self.assertAllClose(0.5, pmf.eval()) + self.assertAllClose(0.5, self.evaluate(pmf)) self.assertEqual((), pmf.get_shape()) # If there are two draws, it is much more likely that they are the same. @@ -409,9 +419,10 @@ class DirichletMultinomialTest(test.TestCase): dist = ds.DirichletMultinomial(2., alpha) pmf_same = dist.prob(counts_same) pmf_different = dist.prob(counts_different) - self.assertLess(5 * pmf_different.eval(), pmf_same.eval()) + self.assertLess(5 * self.evaluate(pmf_different), self.evaluate(pmf_same)) self.assertEqual((), pmf_same.get_shape()) + @test_util.run_deprecated_v1 def testNonStrictTurnsOffAllChecks(self): # Make totally invalid input. with self.cached_session(): @@ -421,6 +432,7 @@ class DirichletMultinomialTest(test.TestCase): dist = ds.DirichletMultinomial(n, alpha, validate_args=False) dist.prob(counts).eval() # Should not raise. + @test_util.run_deprecated_v1 def testSampleUnbiasedNonScalarBatch(self): with self.cached_session() as sess: dist = ds.DirichletMultinomial( @@ -450,6 +462,7 @@ class DirichletMultinomialTest(test.TestCase): self.assertAllClose( actual_covariance_, sample_covariance_, atol=0., rtol=0.20) + @test_util.run_deprecated_v1 def testSampleUnbiasedScalarBatch(self): with self.cached_session() as sess: dist = ds.DirichletMultinomial( diff --git a/tensorflow/python/kernel_tests/distributions/identity_bijector_test.py b/tensorflow/python/kernel_tests/distributions/identity_bijector_test.py index e35a8e1cdd..62b562387d 100644 --- a/tensorflow/python/kernel_tests/distributions/identity_bijector_test.py +++ b/tensorflow/python/kernel_tests/distributions/identity_bijector_test.py @@ -18,6 +18,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +from tensorflow.python.framework import test_util from tensorflow.python.ops.distributions import bijector_test_util from tensorflow.python.ops.distributions import identity_bijector from tensorflow.python.platform import test @@ -41,6 +42,7 @@ class IdentityBijectorTest(test.TestCase): self.evaluate( bijector.forward_log_det_jacobian(x, event_ndims=3))) + @test_util.run_deprecated_v1 def testScalarCongruency(self): with self.cached_session(): bijector = identity_bijector.Identity() diff --git a/tensorflow/python/kernel_tests/distributions/kullback_leibler_test.py b/tensorflow/python/kernel_tests/distributions/kullback_leibler_test.py index e77e1117d4..1e967de570 100644 --- a/tensorflow/python/kernel_tests/distributions/kullback_leibler_test.py +++ b/tensorflow/python/kernel_tests/distributions/kullback_leibler_test.py @@ -18,6 +18,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops.distributions import kullback_leibler from tensorflow.python.ops.distributions import normal @@ -45,6 +46,7 @@ class KLTest(test.TestCase): a = MyDist(loc=0.0, scale=1.0) self.assertEqual("OK", kullback_leibler.kl_divergence(a, a, name="OK")) + @test_util.run_deprecated_v1 def testDomainErrorExceptions(self): class MyDistException(normal.Normal): @@ -63,17 +65,17 @@ class KLTest(test.TestCase): kl = kullback_leibler.kl_divergence(a, a, allow_nan_stats=False) with self.assertRaisesOpError( "KL calculation between .* and .* returned NaN values"): - kl.eval() + self.evaluate(kl) with self.assertRaisesOpError( "KL calculation between .* and .* returned NaN values"): a.kl_divergence(a).eval() a = MyDistException(loc=0.0, scale=1.0, allow_nan_stats=True) kl_ok = kullback_leibler.kl_divergence(a, a) - self.assertAllEqual([float("nan")], kl_ok.eval()) + self.assertAllEqual([float("nan")], self.evaluate(kl_ok)) self_kl_ok = a.kl_divergence(a) - self.assertAllEqual([float("nan")], self_kl_ok.eval()) + self.assertAllEqual([float("nan")], self.evaluate(self_kl_ok)) cross_ok = a.cross_entropy(a) - self.assertAllEqual([float("nan")], cross_ok.eval()) + self.assertAllEqual([float("nan")], self.evaluate(cross_ok)) def testRegistrationFailures(self): diff --git a/tensorflow/python/kernel_tests/distributions/multinomial_test.py b/tensorflow/python/kernel_tests/distributions/multinomial_test.py index 3840d7331c..b3f3416a52 100644 --- a/tensorflow/python/kernel_tests/distributions/multinomial_test.py +++ b/tensorflow/python/kernel_tests/distributions/multinomial_test.py @@ -127,7 +127,7 @@ class MultinomialTest(test.TestCase): p = [0.5, 0.5] counts = [1., 0] pmf = multinomial.Multinomial(total_count=1., probs=p).prob(counts) - self.assertAllClose(0.5, pmf.eval()) + self.assertAllClose(0.5, self.evaluate(pmf)) self.assertEqual((), pmf.get_shape()) def testPmfBothZeroBatchesNontrivialN(self): @@ -138,7 +138,7 @@ class MultinomialTest(test.TestCase): dist = multinomial.Multinomial(total_count=5., probs=p) pmf = dist.prob(counts) # 5 choose 3 = 5 choose 2 = 10. 10 * (.9)^2 * (.1)^3 = 81/10000. - self.assertAllClose(81. / 10000, pmf.eval()) + self.assertAllClose(81. / 10000, self.evaluate(pmf)) self.assertEqual((), pmf.get_shape()) def testPmfPStretchedInBroadcastWhenSameRank(self): @@ -146,7 +146,7 @@ class MultinomialTest(test.TestCase): p = [[0.1, 0.9]] counts = [[1., 0], [0, 1]] pmf = multinomial.Multinomial(total_count=1., probs=p).prob(counts) - self.assertAllClose([0.1, 0.9], pmf.eval()) + self.assertAllClose([0.1, 0.9], self.evaluate(pmf)) self.assertEqual((2), pmf.get_shape()) def testPmfPStretchedInBroadcastWhenLowerRank(self): @@ -154,7 +154,7 @@ class MultinomialTest(test.TestCase): p = [0.1, 0.9] counts = [[1., 0], [0, 1]] pmf = multinomial.Multinomial(total_count=1., probs=p).prob(counts) - self.assertAllClose([0.1, 0.9], pmf.eval()) + self.assertAllClose([0.1, 0.9], self.evaluate(pmf)) self.assertEqual((2), pmf.get_shape()) def testPmfCountsStretchedInBroadcastWhenSameRank(self): @@ -182,7 +182,7 @@ class MultinomialTest(test.TestCase): # [2] counts = [2., 1] pmf = multinomial.Multinomial(total_count=n, probs=p).prob(counts) - pmf.eval() + self.evaluate(pmf) self.assertEqual(pmf.get_shape(), (2, 2)) def testPmfShapeCountsPStretchedN(self): @@ -191,7 +191,7 @@ class MultinomialTest(test.TestCase): counts = [3., 2] n = np.full([4, 3], 5., dtype=np.float32) pmf = multinomial.Multinomial(total_count=n, probs=p).prob(counts) - pmf.eval() + self.evaluate(pmf) self.assertEqual((4, 3), pmf.get_shape()) def testMultinomialMean(self): diff --git a/tensorflow/python/kernel_tests/distributions/normal_test.py b/tensorflow/python/kernel_tests/distributions/normal_test.py index 6625a88843..f2a193e69b 100644 --- a/tensorflow/python/kernel_tests/distributions/normal_test.py +++ b/tensorflow/python/kernel_tests/distributions/normal_test.py @@ -511,6 +511,7 @@ class NormalTest(test.TestCase): self.assertAllEqual(self.evaluate(normal.event_shape_tensor()), []) self.assertEqual(normal.event_shape, tensor_shape.TensorShape([])) + @test_util.run_deprecated_v1 def testNormalShapeWithPlaceholders(self): mu = array_ops.placeholder(dtype=dtypes.float32) sigma = array_ops.placeholder(dtype=dtypes.float32) diff --git a/tensorflow/python/kernel_tests/distributions/special_math_test.py b/tensorflow/python/kernel_tests/distributions/special_math_test.py index cc43e12168..d97fcfa655 100644 --- a/tensorflow/python/kernel_tests/distributions/special_math_test.py +++ b/tensorflow/python/kernel_tests/distributions/special_math_test.py @@ -104,6 +104,7 @@ class NdtriTest(test.TestCase): x = special_math.ndtri(p) self.assertAllClose(expected_x, self.evaluate(x), atol=0.) + @test_util.run_deprecated_v1 def testNdtriDynamicShape(self): """Verifies that ndtri computation is correct.""" with self.cached_session() as sess: @@ -213,9 +214,11 @@ class NdtrTest(test.TestCase): rtol=error_spec.rtol, atol=error_spec.atol) + @test_util.run_deprecated_v1 def test_float32(self): self._test_grid(np.float32, self._grid32, self._error32) + @test_util.run_deprecated_v1 def test_float64(self): self._test_grid(np.float64, self._grid64, self._error64) @@ -338,10 +341,12 @@ class NdtrGradientTest(test.TestCase): rtol=error_spec.rtol, atol=error_spec.atol) + @test_util.run_deprecated_v1 def test_float32(self): self._test_grad_accuracy(np.float32, self._grid, self._error32) self._test_grad_finite(np.float32) + @test_util.run_deprecated_v1 def test_float64(self): self._test_grad_accuracy(np.float64, self._grid, self._error64) self._test_grad_finite(np.float64) @@ -362,7 +367,7 @@ class ErfInvTest(test.TestCase): expected_x = special.erfinv(x) x = special_math.erfinv(x) - self.assertAllClose(expected_x, x.eval(), atol=0.) + self.assertAllClose(expected_x, self.evaluate(x), atol=0.) def testErfInvIntegerInput(self): with self.cached_session(): @@ -418,6 +423,7 @@ class LogCDFLaplaceTest(test.TestCase): rtol=error_spec.rtol, atol=error_spec.atol) + @test_util.run_deprecated_v1 def test_float32_lower_and_mid_segment_scipy_float32_ok(self): # Choose values mild enough that we can use scipy in float32, which will # allow for a high accuracy match to scipy (since we both use float32). @@ -427,6 +433,7 @@ class LogCDFLaplaceTest(test.TestCase): GridSpec(min=-10, max=self.CUTOFF_FLOAT32_UPPER - 5, shape=[100]), ErrorSpec(rtol=5e-4, atol=0)) + @test_util.run_deprecated_v1 def test_float32_all_segments_with_scipy_float64_ok(self): # Choose values outside the range where scipy float32 works. # Let scipy use float64. This means we @@ -437,6 +444,7 @@ class LogCDFLaplaceTest(test.TestCase): GridSpec(min=-50, max=self.CUTOFF_FLOAT32_UPPER + 5, shape=[100]), ErrorSpec(rtol=0.05, atol=0)) + @test_util.run_deprecated_v1 def test_float32_extreme_values_result_and_gradient_finite_and_nonzero(self): with self.cached_session() as sess: # On the lower branch, log_cdf_laplace(x) = x, so we know this will be @@ -448,7 +456,7 @@ class LogCDFLaplaceTest(test.TestCase): actual = sm.log_cdf_laplace(grid) grad = gradients_impl.gradients(actual, grid)[0] - actual_, grad_ = sess.run([actual, grad]) + actual_, grad_ = self.evaluate([actual, grad]) # isfinite checks for NaN and Inf. self.assertAllTrue(np.isfinite(actual_)) @@ -456,6 +464,7 @@ class LogCDFLaplaceTest(test.TestCase): self.assertFalse(np.any(actual_ == 0)) self.assertFalse(np.any(grad_ == 0)) + @test_util.run_deprecated_v1 def test_float64_extreme_values_result_and_gradient_finite_and_nonzero(self): with self.cached_session() as sess: # On the lower branch, log_cdf_laplace(x) = x, so we know this will be @@ -467,7 +476,7 @@ class LogCDFLaplaceTest(test.TestCase): actual = sm.log_cdf_laplace(grid) grad = gradients_impl.gradients(actual, grid)[0] - actual_, grad_ = sess.run([actual, grad]) + actual_, grad_ = self.evaluate([actual, grad]) # isfinite checks for NaN and Inf. self.assertAllTrue(np.isfinite(actual_)) diff --git a/tensorflow/python/kernel_tests/distributions/util_test.py b/tensorflow/python/kernel_tests/distributions/util_test.py index f4e651b25b..030ad601bf 100644 --- a/tensorflow/python/kernel_tests/distributions/util_test.py +++ b/tensorflow/python/kernel_tests/distributions/util_test.py @@ -59,6 +59,7 @@ def _logit(x): class AssertCloseTest(test.TestCase): + @test_util.run_deprecated_v1 def testAssertIntegerForm(self): # This should only be detected as an integer. x = array_ops.placeholder(dtypes.float32) @@ -112,6 +113,7 @@ class MaybeGetStaticTest(test.TestCase): self.assertAllClose( np.array(2.), du.maybe_get_static_value(x, dtype=np.float64)) + @test_util.run_deprecated_v1 def testGetStaticPlaceholder(self): x = array_ops.placeholder(dtype=dtypes.int32, shape=[1]) self.assertEqual(None, du.maybe_get_static_value(x)) @@ -235,6 +237,7 @@ class GetLogitsAndProbsTest(test.TestCase): probs=p4, multidimensional=True, validate_args=False) self.evaluate(prob) + @test_util.run_deprecated_v1 def testProbsMultidimShape(self): with self.cached_session(): with self.assertRaises(ValueError): @@ -249,6 +252,7 @@ class GetLogitsAndProbsTest(test.TestCase): probs=p, multidimensional=True, validate_args=True) prob.eval(feed_dict={p: np.ones([int(2**11+1)])}) + @test_util.run_deprecated_v1 def testLogitsMultidimShape(self): with self.cached_session(): with self.assertRaises(ValueError): @@ -266,6 +270,7 @@ class GetLogitsAndProbsTest(test.TestCase): class EmbedCheckCategoricalEventShapeTest(test.TestCase): + @test_util.run_deprecated_v1 def testTooSmall(self): with self.cached_session(): with self.assertRaises(ValueError): @@ -280,6 +285,7 @@ class EmbedCheckCategoricalEventShapeTest(test.TestCase): param) checked_param.eval(feed_dict={param: np.ones([1])}) + @test_util.run_deprecated_v1 def testTooLarge(self): with self.cached_session(): with self.assertRaises(ValueError): @@ -305,6 +311,7 @@ class EmbedCheckCategoricalEventShapeTest(test.TestCase): class EmbedCheckIntegerCastingClosedTest(test.TestCase): + @test_util.run_deprecated_v1 def testCorrectlyAssertsNonnegative(self): with self.cached_session(): with self.assertRaisesOpError("Elements must be non-negative"): @@ -313,6 +320,7 @@ class EmbedCheckIntegerCastingClosedTest(test.TestCase): x, target_dtype=dtypes.int16) x_checked.eval(feed_dict={x: np.array([1, -1], dtype=np.float16)}) + @test_util.run_deprecated_v1 def testCorrectlyAssersIntegerForm(self): with self.cached_session(): with self.assertRaisesOpError("Elements must be int16-equivalent."): @@ -321,6 +329,7 @@ class EmbedCheckIntegerCastingClosedTest(test.TestCase): x, target_dtype=dtypes.int16) x_checked.eval(feed_dict={x: np.array([1, 1.5], dtype=np.float16)}) + @test_util.run_deprecated_v1 def testCorrectlyAssertsLargestPossibleInteger(self): with self.cached_session(): with self.assertRaisesOpError("Elements cannot exceed 32767."): @@ -329,6 +338,7 @@ class EmbedCheckIntegerCastingClosedTest(test.TestCase): x, target_dtype=dtypes.int16) x_checked.eval(feed_dict={x: np.array([1, 2**15], dtype=np.int32)}) + @test_util.run_deprecated_v1 def testCorrectlyAssertsSmallestPossibleInteger(self): with self.cached_session(): with self.assertRaisesOpError("Elements cannot be smaller than 0."): @@ -369,6 +379,7 @@ class LogCombinationsTest(test.TestCase): class DynamicShapeTest(test.TestCase): + @test_util.run_deprecated_v1 def testSameDynamicShape(self): with self.cached_session(): scalar = constant_op.constant(2.0) @@ -493,6 +504,7 @@ class RotateTransposeTest(test.TestCase): self._np_rotate_transpose(x, shift), self.evaluate(y)) self.assertAllEqual(np.roll(x.shape, shift), y.get_shape().as_list()) + @test_util.run_deprecated_v1 def testRollDynamic(self): with self.cached_session() as sess: x = array_ops.placeholder(dtypes.float32) @@ -511,6 +523,7 @@ class RotateTransposeTest(test.TestCase): class PickVectorTest(test.TestCase): + @test_util.run_deprecated_v1 def testCorrectlyPicksVector(self): with self.cached_session(): x = np.arange(10, 12) @@ -529,36 +542,42 @@ class PickVectorTest(test.TestCase): class PreferStaticRankTest(test.TestCase): + @test_util.run_deprecated_v1 def testNonEmptyConstantTensor(self): x = array_ops.zeros((2, 3, 4)) rank = du.prefer_static_rank(x) self.assertIsInstance(rank, np.ndarray) self.assertEqual(3, rank) + @test_util.run_deprecated_v1 def testEmptyConstantTensor(self): x = constant_op.constant([]) rank = du.prefer_static_rank(x) self.assertIsInstance(rank, np.ndarray) self.assertEqual(1, rank) + @test_util.run_deprecated_v1 def testScalarTensor(self): x = constant_op.constant(1.) rank = du.prefer_static_rank(x) self.assertIsInstance(rank, np.ndarray) self.assertEqual(0, rank) + @test_util.run_deprecated_v1 def testDynamicRankEndsUpBeingNonEmpty(self): x = array_ops.placeholder(np.float64, shape=None) rank = du.prefer_static_rank(x) with self.cached_session(): self.assertAllEqual(2, rank.eval(feed_dict={x: np.zeros((2, 3))})) + @test_util.run_deprecated_v1 def testDynamicRankEndsUpBeingEmpty(self): x = array_ops.placeholder(np.int32, shape=None) rank = du.prefer_static_rank(x) with self.cached_session(): self.assertAllEqual(1, rank.eval(feed_dict={x: []})) + @test_util.run_deprecated_v1 def testDynamicRankEndsUpBeingScalar(self): x = array_ops.placeholder(np.int32, shape=None) rank = du.prefer_static_rank(x) @@ -568,36 +587,42 @@ class PreferStaticRankTest(test.TestCase): class PreferStaticShapeTest(test.TestCase): + @test_util.run_deprecated_v1 def testNonEmptyConstantTensor(self): x = array_ops.zeros((2, 3, 4)) shape = du.prefer_static_shape(x) self.assertIsInstance(shape, np.ndarray) self.assertAllEqual(np.array([2, 3, 4]), shape) + @test_util.run_deprecated_v1 def testEmptyConstantTensor(self): x = constant_op.constant([]) shape = du.prefer_static_shape(x) self.assertIsInstance(shape, np.ndarray) self.assertAllEqual(np.array([0]), shape) + @test_util.run_deprecated_v1 def testScalarTensor(self): x = constant_op.constant(1.) shape = du.prefer_static_shape(x) self.assertIsInstance(shape, np.ndarray) self.assertAllEqual(np.array([]), shape) + @test_util.run_deprecated_v1 def testDynamicShapeEndsUpBeingNonEmpty(self): x = array_ops.placeholder(np.float64, shape=None) shape = du.prefer_static_shape(x) with self.cached_session(): self.assertAllEqual((2, 3), shape.eval(feed_dict={x: np.zeros((2, 3))})) + @test_util.run_deprecated_v1 def testDynamicShapeEndsUpBeingEmpty(self): x = array_ops.placeholder(np.int32, shape=None) shape = du.prefer_static_shape(x) with self.cached_session(): self.assertAllEqual(np.array([0]), shape.eval(feed_dict={x: []})) + @test_util.run_deprecated_v1 def testDynamicShapeEndsUpBeingScalar(self): x = array_ops.placeholder(np.int32, shape=None) shape = du.prefer_static_shape(x) @@ -607,24 +632,28 @@ class PreferStaticShapeTest(test.TestCase): class PreferStaticValueTest(test.TestCase): + @test_util.run_deprecated_v1 def testNonEmptyConstantTensor(self): x = array_ops.zeros((2, 3, 4)) value = du.prefer_static_value(x) self.assertIsInstance(value, np.ndarray) self.assertAllEqual(np.zeros((2, 3, 4)), value) + @test_util.run_deprecated_v1 def testEmptyConstantTensor(self): x = constant_op.constant([]) value = du.prefer_static_value(x) self.assertIsInstance(value, np.ndarray) self.assertAllEqual(np.array([]), value) + @test_util.run_deprecated_v1 def testScalarTensor(self): x = constant_op.constant(1.) value = du.prefer_static_value(x) self.assertIsInstance(value, np.ndarray) self.assertAllEqual(np.array(1.), value) + @test_util.run_deprecated_v1 def testDynamicValueEndsUpBeingNonEmpty(self): x = array_ops.placeholder(np.float64, shape=None) value = du.prefer_static_value(x) @@ -632,12 +661,14 @@ class PreferStaticValueTest(test.TestCase): self.assertAllEqual(np.zeros((2, 3)), value.eval(feed_dict={x: np.zeros((2, 3))})) + @test_util.run_deprecated_v1 def testDynamicValueEndsUpBeingEmpty(self): x = array_ops.placeholder(np.int32, shape=None) value = du.prefer_static_value(x) with self.cached_session(): self.assertAllEqual(np.array([]), value.eval(feed_dict={x: []})) + @test_util.run_deprecated_v1 def testDynamicValueEndsUpBeingScalar(self): x = array_ops.placeholder(np.int32, shape=None) value = du.prefer_static_value(x) @@ -698,43 +729,55 @@ class FillTriangularTest(test.TestCase): self.assertAllClose(expected, actual_, rtol=1e-8, atol=1e-9) self.assertAllClose(x_, grad_actual_, rtol=1e-8, atol=1e-9) + @test_util.run_deprecated_v1 def testCorrectlyMakes1x1TriLower(self): self._run_test(self._rng.randn(3, int(1*2/2))) + @test_util.run_deprecated_v1 def testCorrectlyMakesNoBatchTriLower(self): self._run_test(self._rng.randn(int(4*5/2))) + @test_util.run_deprecated_v1 def testCorrectlyMakesBatchTriLower(self): self._run_test(self._rng.randn(2, 3, int(3*4/2))) + @test_util.run_deprecated_v1 def testCorrectlyMakesBatchTriLowerUnknownShape(self): self._run_test(self._rng.randn(2, 3, int(3*4/2)), use_deferred_shape=True) + @test_util.run_deprecated_v1 def testCorrectlyMakesBatch7x7TriLowerUnknownShape(self): self._run_test(self._rng.randn(2, 3, int(7*8/2)), use_deferred_shape=True) + @test_util.run_deprecated_v1 def testCorrectlyMakesBatch7x7TriLower(self): self._run_test(self._rng.randn(2, 3, int(7*8/2))) + @test_util.run_deprecated_v1 def testCorrectlyMakes1x1TriUpper(self): self._run_test(self._rng.randn(3, int(1*2/2)), upper=True) + @test_util.run_deprecated_v1 def testCorrectlyMakesNoBatchTriUpper(self): self._run_test(self._rng.randn(int(4*5/2)), upper=True) + @test_util.run_deprecated_v1 def testCorrectlyMakesBatchTriUpper(self): self._run_test(self._rng.randn(2, 2, int(3*4/2)), upper=True) + @test_util.run_deprecated_v1 def testCorrectlyMakesBatchTriUpperUnknownShape(self): self._run_test(self._rng.randn(2, 2, int(3*4/2)), use_deferred_shape=True, upper=True) + @test_util.run_deprecated_v1 def testCorrectlyMakesBatch7x7TriUpperUnknownShape(self): self._run_test(self._rng.randn(2, 3, int(7*8/2)), use_deferred_shape=True, upper=True) + @test_util.run_deprecated_v1 def testCorrectlyMakesBatch7x7TriUpper(self): self._run_test(self._rng.randn(2, 3, int(7*8/2)), upper=True) @@ -773,6 +816,7 @@ class ReduceWeightedLogSumExp(test.TestCase): m = np.squeeze(m, axis=axis) return m + np.log(sgn * sum_), sgn + @test_util.run_deprecated_v1 def testNoWeights(self): logx_ = np.array([[0., -1, 1000.], [0, 1, -1000.], @@ -805,7 +849,7 @@ class ReduceWeightedLogSumExp(test.TestCase): w = constant_op.constant(w_) actual, actual_sgn = du.reduce_weighted_logsumexp( logx, w, axis=-1, return_sign=True) - [actual_, actual_sgn_] = sess.run([actual, actual_sgn]) + [actual_, actual_sgn_] = self.evaluate([actual, actual_sgn]) self.assertAllEqual(expected, actual_) self.assertAllEqual([-1., -1, 1], actual_sgn_) @@ -823,7 +867,7 @@ class ReduceWeightedLogSumExp(test.TestCase): w = constant_op.constant(w_) actual, actual_sgn = du.reduce_weighted_logsumexp( logx, w, axis=-1, return_sign=True, keep_dims=True) - [actual_, actual_sgn_] = sess.run([actual, actual_sgn]) + [actual_, actual_sgn_] = self.evaluate([actual, actual_sgn]) self.assertAllEqual(expected, actual_) self.assertAllEqual([[-1.], [-1], [1]], actual_sgn_) @@ -903,6 +947,7 @@ class SoftplusTest(test.TestCase): self.assertAllEqual(np.ones_like(tf_softplus_inverse).astype(np.bool), np.isfinite(tf_softplus_inverse)) + @test_util.run_deprecated_v1 def testNumbers(self): for t in [np.float16, np.float32, np.float64]: lower = {np.float16: -15, np.float32: -50, np.float64: -50}.get(t, -100) @@ -933,6 +978,7 @@ class SoftplusTest(test.TestCase): ], use_gpu=True) + @test_util.run_deprecated_v1 def testGradient(self): with self.cached_session(): x = constant_op.constant( @@ -949,6 +995,7 @@ class SoftplusTest(test.TestCase): tf_logging.vlog(2, "softplus (float) gradient err = ", err) self.assertLess(err, 1e-4) + @test_util.run_deprecated_v1 def testInverseSoftplusGradientNeverNan(self): with self.cached_session(): # Note that this range contains both zero and inf. @@ -958,6 +1005,7 @@ class SoftplusTest(test.TestCase): # Equivalent to `assertAllFalse` (if it existed). self.assertAllEqual(np.zeros_like(grads).astype(np.bool), np.isnan(grads)) + @test_util.run_deprecated_v1 def testInverseSoftplusGradientFinite(self): with self.cached_session(): # This range of x is all finite, and so is 1 / x. So the diff --git a/tensorflow/python/kernel_tests/division_future_test.py b/tensorflow/python/kernel_tests/division_future_test.py index e477bdc73b..85c85809d3 100644 --- a/tensorflow/python/kernel_tests/division_future_test.py +++ b/tensorflow/python/kernel_tests/division_future_test.py @@ -65,7 +65,7 @@ class DivisionTestCase(test.TestCase): tf_floordiv = tf_x // tf_y check(floordiv, tf_floordiv) # Do only one sess.run for speed - for f, (x, y) in zip(checks, sess.run(tensors)): + for f, (x, y) in zip(checks, self.evaluate(tensors)): f(x, y) diff --git a/tensorflow/python/kernel_tests/division_past_test.py b/tensorflow/python/kernel_tests/division_past_test.py index 63951b5b38..38bb18631a 100644 --- a/tensorflow/python/kernel_tests/division_past_test.py +++ b/tensorflow/python/kernel_tests/division_past_test.py @@ -64,7 +64,7 @@ class DivisionTestCase(test.TestCase): tf_floordiv = tf_x // tf_y check(floordiv, tf_floordiv) # Do only one sess.run for speed - for f, (x, y) in zip(checks, sess.run(tensors)): + for f, (x, y) in zip(checks, self.evaluate(tensors)): f(x, y) diff --git a/tensorflow/python/kernel_tests/draw_bounding_box_op_test.py b/tensorflow/python/kernel_tests/draw_bounding_box_op_test.py index c655876280..6aa757e293 100644 --- a/tensorflow/python/kernel_tests/draw_bounding_box_op_test.py +++ b/tensorflow/python/kernel_tests/draw_bounding_box_op_test.py @@ -87,7 +87,7 @@ class DrawBoundingBoxOpTest(test.TestCase): image = array_ops.expand_dims(image, 0) image = image_ops.draw_bounding_boxes(image, bboxes) with self.cached_session(use_gpu=False) as sess: - op_drawn_image = np.squeeze(sess.run(image), 0) + op_drawn_image = np.squeeze(self.evaluate(image), 0) self.assertAllEqual(test_drawn_image, op_drawn_image) def testDrawBoundingBoxRGBColorCycling(self): diff --git a/tensorflow/python/kernel_tests/duplicate_op_test.py b/tensorflow/python/kernel_tests/duplicate_op_test.py index 654267a582..fef3127d4a 100644 --- a/tensorflow/python/kernel_tests/duplicate_op_test.py +++ b/tensorflow/python/kernel_tests/duplicate_op_test.py @@ -20,6 +20,7 @@ from __future__ import print_function import os from tensorflow.python.framework import load_library +from tensorflow.python.framework import test_util from tensorflow.python.ops import math_ops from tensorflow.python.platform import resource_loader from tensorflow.python.platform import test @@ -27,6 +28,7 @@ from tensorflow.python.platform import test class DuplicateOpTest(test.TestCase): + @test_util.run_deprecated_v1 def testBasic(self): library_filename = os.path.join(resource_loader.get_data_files_path(), 'duplicate_op.so') diff --git a/tensorflow/python/kernel_tests/dynamic_partition_op_test.py b/tensorflow/python/kernel_tests/dynamic_partition_op_test.py index 07da855a01..8c44819407 100644 --- a/tensorflow/python/kernel_tests/dynamic_partition_op_test.py +++ b/tensorflow/python/kernel_tests/dynamic_partition_op_test.py @@ -25,6 +25,7 @@ from six.moves import xrange # pylint: disable=redefined-builtin from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import data_flow_ops from tensorflow.python.ops import gradients_impl @@ -34,13 +35,14 @@ from tensorflow.python.platform import test class DynamicPartitionTest(test.TestCase): + @test_util.run_deprecated_v1 def testSimpleOneDimensional(self): with self.session(use_gpu=True) as sess: data = constant_op.constant([0, 13, 2, 39, 4, 17], dtype=dtypes.float32) indices = constant_op.constant([0, 0, 2, 3, 2, 1]) partitions = data_flow_ops.dynamic_partition( data, indices, num_partitions=4) - partition_vals = sess.run(partitions) + partition_vals = self.evaluate(partitions) self.assertEqual(4, len(partition_vals)) self.assertAllEqual([0, 13], partition_vals[0]) @@ -54,6 +56,7 @@ class DynamicPartitionTest(test.TestCase): self.assertEqual([None], partitions[2].get_shape().as_list()) self.assertEqual([None], partitions[3].get_shape().as_list()) + @test_util.run_deprecated_v1 def testSimpleTwoDimensional(self): with self.session(use_gpu=True) as sess: data = constant_op.constant([[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], @@ -62,7 +65,7 @@ class DynamicPartitionTest(test.TestCase): indices = constant_op.constant([0, 0, 2, 3, 2, 1]) partitions = data_flow_ops.dynamic_partition( data, indices, num_partitions=4) - partition_vals = sess.run(partitions) + partition_vals = self.evaluate(partitions) self.assertEqual(4, len(partition_vals)) self.assertAllEqual([[0, 1, 2], [3, 4, 5]], partition_vals[0]) @@ -87,7 +90,7 @@ class DynamicPartitionTest(test.TestCase): indices = constant_op.constant(indices_list, dtype=dtypes.int32) partitions = data_flow_ops.dynamic_partition( data, indices, num_partitions=2) - partition_vals = sess.run(partitions) + partition_vals = self.evaluate(partitions) self.assertEqual(2, len(partition_vals)) self.assertAllEqual(part1, partition_vals[0]) @@ -109,7 +112,7 @@ class DynamicPartitionTest(test.TestCase): indices = constant_op.constant(indices_list, dtype=dtypes.int32) partitions = data_flow_ops.dynamic_partition( data, indices, num_partitions=num_partitions) - partition_vals = sess.run(partitions) + partition_vals = self.evaluate(partitions) self.assertEqual(num_partitions, len(partition_vals)) for i in range(num_partitions): @@ -125,7 +128,7 @@ class DynamicPartitionTest(test.TestCase): indices = constant_op.constant(indices_list, dtype=dtypes.int32) partitions = data_flow_ops.dynamic_partition( data, indices, num_partitions=2) - partition_vals = sess.run(partitions) + partition_vals = self.evaluate(partitions) self.assertEqual(2, len(partition_vals)) self.assertAllEqual([3 + 4j, 7 + 8j], partition_vals[0]) @@ -138,7 +141,7 @@ class DynamicPartitionTest(test.TestCase): indices = 3 partitions = data_flow_ops.dynamic_partition( data, indices, num_partitions=4) - partition_vals = sess.run(partitions) + partition_vals = self.evaluate(partitions) self.assertEqual(4, len(partition_vals)) self.assertAllEqual(np.array([], dtype=np.float64).reshape(-1, 4), @@ -151,6 +154,7 @@ class DynamicPartitionTest(test.TestCase): dtype=np.float64).reshape(-1, 4), partition_vals[3]) + @test_util.run_deprecated_v1 def testHigherRank(self): np.random.seed(7) with self.session(use_gpu=True) as sess: @@ -164,7 +168,7 @@ class DynamicPartitionTest(test.TestCase): outputs = data_flow_ops.dynamic_partition( data_t, partitions_t, num_partitions=n) self.assertEqual(n, len(outputs)) - outputs_val = sess.run(outputs) + outputs_val = self.evaluate(outputs) for i, output in enumerate(outputs_val): self.assertAllEqual(output, data[partitions == i]) @@ -183,7 +187,7 @@ class DynamicPartitionTest(test.TestCase): indices = constant_op.constant(indices_list, dtype=dtypes.int32) partitions = data_flow_ops.dynamic_partition( data, indices, num_partitions=4) - partition_vals = sess.run(partitions) + partition_vals = self.evaluate(partitions) self.assertEqual(4, len(partition_vals)) self.assertAllEqual([], partition_vals[0]) @@ -199,7 +203,7 @@ class DynamicPartitionTest(test.TestCase): indices = constant_op.constant(indices_list, dtype=dtypes.int32) partitions = data_flow_ops.dynamic_partition( data, indices, num_partitions=3) - partition_vals = sess.run(partitions) + partition_vals = self.evaluate(partitions) self.assertEqual(3, len(partition_vals)) self.assertAllEqual([[]], partition_vals[0]) @@ -215,7 +219,7 @@ class DynamicPartitionTest(test.TestCase): indices = constant_op.constant(indices_list, dtype=dtypes.int32) partitions = data_flow_ops.dynamic_partition( data, indices, num_partitions=2) - partition_vals = sess.run(partitions) + partition_vals = self.evaluate(partitions) self.assertEqual(2, len(partition_vals)) self.assertAllEqual([], partition_vals[0]) @@ -236,7 +240,7 @@ class DynamicPartitionTest(test.TestCase): indices = constant_op.constant(indices_list, dtype=dtypes.int32) partitions = data_flow_ops.dynamic_partition( data, indices, num_partitions=2) - partition_vals = sess.run(partitions) + partition_vals = self.evaluate(partitions) self.assertEqual(2, len(partition_vals)) self.assertAllEqual([6], partition_vals[0]) @@ -257,7 +261,7 @@ class DynamicPartitionTest(test.TestCase): indices = constant_op.constant(indices_list, dtype=dtypes.int32) partitions = data_flow_ops.dynamic_partition( data, indices, num_partitions=5) - partition_vals = sess.run(partitions) + partition_vals = self.evaluate(partitions) self.assertEqual(5, len(partition_vals)) self.assertAllEqual([5], partition_vals[0]) @@ -281,12 +285,13 @@ class DynamicPartitionTest(test.TestCase): indices = constant_op.constant(indices_list, dtype=dtypes.int32) partitions = data_flow_ops.dynamic_partition( data, indices, num_partitions=40) - partition_vals = sess.run(partitions) + partition_vals = self.evaluate(partitions) self.assertEqual(40, len(partition_vals)) for i in range(40): self.assertAllEqual([], partition_vals[i]) + @test_util.run_deprecated_v1 def testErrorIndexOutOfRange(self): with self.cached_session() as sess: data = constant_op.constant([[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], @@ -295,16 +300,18 @@ class DynamicPartitionTest(test.TestCase): partitions = data_flow_ops.dynamic_partition( data, indices, num_partitions=4) with self.assertRaisesOpError(r"partitions\[2\] = 99 is not in \[0, 4\)"): - sess.run(partitions) + self.evaluate(partitions) + @test_util.run_deprecated_v1 def testScalarIndexOutOfRange(self): with self.cached_session() as sess: bad = 17 data = np.zeros(5) partitions = data_flow_ops.dynamic_partition(data, bad, num_partitions=7) with self.assertRaisesOpError(r"partitions = 17 is not in \[0, 7\)"): - sess.run(partitions) + self.evaluate(partitions) + @test_util.run_deprecated_v1 def testHigherRankIndexOutOfRange(self): with self.cached_session() as sess: shape = (2, 3) @@ -320,6 +327,7 @@ class DynamicPartitionTest(test.TestCase): r"partitions\[%d,%d\] = 17 is not in \[0, 7\)" % (i, j)): sess.run(partitions, feed_dict={indices: bad}) + @test_util.run_deprecated_v1 def testErrorWrongDimsIndices(self): data = constant_op.constant([[0], [1], [2]]) indices = constant_op.constant([[0], [0]]) @@ -335,7 +343,7 @@ class DynamicPartitionTest(test.TestCase): self.assertEqual(len(inds), x.shape[0]) partitioned = data_flow_ops.dynamic_partition(x, inds, 16) with self.cached_session() as sess: - res = sess.run(partitioned) + res = self.evaluate(partitioned) self.assertEqual(res[-1].shape[0], 192) diff --git a/tensorflow/python/kernel_tests/dynamic_stitch_op_test.py b/tensorflow/python/kernel_tests/dynamic_stitch_op_test.py index c3f67d29aa..4f338880aa 100644 --- a/tensorflow/python/kernel_tests/dynamic_stitch_op_test.py +++ b/tensorflow/python/kernel_tests/dynamic_stitch_op_test.py @@ -22,6 +22,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import data_flow_ops from tensorflow.python.ops import math_ops @@ -36,18 +37,19 @@ class DynamicStitchTestBase(object): self.stitch_op = stitch_op def testScalar(self): - with self.session(use_gpu=True): + with test_util.use_gpu(): indices = [constant_op.constant(0), constant_op.constant(1)] data = [constant_op.constant(40), constant_op.constant(60)] for step in -1, 1: stitched_t = self.stitch_op(indices[::step], data) - stitched_val = stitched_t.eval() + stitched_val = self.evaluate(stitched_t) self.assertAllEqual([40, 60][::step], stitched_val) # Dimension 0 is max(flatten(indices))+1. self.assertEqual([2], stitched_t.get_shape().as_list()) + @test_util.run_deprecated_v1 def testShapeInferenceForScalarWithNonConstantIndices(self): - with self.session(use_gpu=True): + with test_util.use_gpu(): indices = [ array_ops.placeholder(dtype=dtypes.int32), constant_op.constant(1) @@ -61,7 +63,7 @@ class DynamicStitchTestBase(object): self.assertEqual([None], stitched_t.get_shape().as_list()) def testSimpleOneDimensional(self): - with self.session(use_gpu=True): + with test_util.use_gpu(): # Test various datatypes in the simple case to ensure that the op was # registered under those types. dtypes_to_test = [ @@ -78,23 +80,23 @@ class DynamicStitchTestBase(object): constant_op.constant([10, 60, 20, 30, 50]), dtype=dtype) ] stitched_t = self.stitch_op(indices, data) - stitched_val = stitched_t.eval() + stitched_val = self.evaluate(stitched_t) self.assertAllEqual([0, 10, 20, 30, 40, 50, 60, 70], stitched_val) # Dimension 0 is max(flatten(indices))+1. self.assertEqual([8], stitched_t.get_shape().as_list()) def testOneListOneDimensional(self): - with self.session(use_gpu=True): + with test_util.use_gpu(): indices = [constant_op.constant([1, 6, 2, 3, 5, 0, 4, 7])] data = [constant_op.constant([10, 60, 20, 30, 50, 0, 40, 70])] stitched_t = self.stitch_op(indices, data) - stitched_val = stitched_t.eval() + stitched_val = self.evaluate(stitched_t) self.assertAllEqual([0, 10, 20, 30, 40, 50, 60, 70], stitched_val) # Dimension 0 is max(flatten(indices))+1. self.assertEqual([8], stitched_t.get_shape().as_list()) def testSimpleTwoDimensional(self): - with self.session(use_gpu=True): + with test_util.use_gpu(): indices = [ constant_op.constant([0, 4, 7]), constant_op.constant([1, 6]), @@ -106,14 +108,14 @@ class DynamicStitchTestBase(object): constant_op.constant([[20, 21], [30, 31], [50, 51]]) ] stitched_t = self.stitch_op(indices, data) - stitched_val = stitched_t.eval() + stitched_val = self.evaluate(stitched_t) self.assertAllEqual([[0, 1], [10, 11], [20, 21], [30, 31], [40, 41], [50, 51], [60, 61], [70, 71]], stitched_val) # Dimension 0 is max(flatten(indices))+1. self.assertEqual([8, 2], stitched_t.get_shape().as_list()) def testZeroSizeTensor(self): - with self.session(use_gpu=True): + with test_util.use_gpu(): indices = [ constant_op.constant([0, 4, 7]), constant_op.constant([1, 6]), @@ -127,12 +129,13 @@ class DynamicStitchTestBase(object): array_ops.zeros([0, 2], dtype=dtypes.int32) ] stitched_t = self.stitch_op(indices, data) - stitched_val = stitched_t.eval() + stitched_val = self.evaluate(stitched_t) self.assertAllEqual([[0, 1], [10, 11], [20, 21], [30, 31], [40, 41], [50, 51], [60, 61], [70, 71]], stitched_val) # Dimension 0 is max(flatten(indices))+1. self.assertEqual([8, 2], stitched_t.get_shape().as_list()) + @test_util.run_deprecated_v1 def testHigherRank(self): with self.session(use_gpu=True) as sess: indices = [ @@ -147,7 +150,7 @@ class DynamicStitchTestBase(object): [[1., 2.], [31., 32.]]]) ] stitched_t = self.stitch_op(indices, data) - stitched_val = stitched_t.eval() + stitched_val = self.evaluate(stitched_t) correct = 10. * np.arange(7)[:, None] + [1., 2.] self.assertAllEqual(correct, stitched_val) self.assertEqual([7, 2], stitched_t.get_shape().as_list()) @@ -157,8 +160,9 @@ class DynamicStitchTestBase(object): stitched_grad) self.assertEqual(grads[:3], [None] * 3) # Indices have no gradients for datum, grad in zip(data, sess.run(grads[3:])): - self.assertAllEqual(7. * datum.eval(), grad) + self.assertAllEqual(7. * self.evaluate(datum), grad) + @test_util.run_deprecated_v1 def testErrorIndicesMultiDimensional(self): indices = [ constant_op.constant([0, 4, 7]), @@ -171,6 +175,7 @@ class DynamicStitchTestBase(object): with self.assertRaises(ValueError): self.stitch_op(indices, data) + @test_util.run_deprecated_v1 def testErrorDataNumDimsMismatch(self): indices = [ constant_op.constant([0, 4, 7]), @@ -183,6 +188,7 @@ class DynamicStitchTestBase(object): with self.assertRaises(ValueError): self.stitch_op(indices, data) + @test_util.run_deprecated_v1 def testErrorDataDimSizeMismatch(self): indices = [ constant_op.constant([0, 4, 5]), @@ -195,6 +201,7 @@ class DynamicStitchTestBase(object): with self.assertRaises(ValueError): self.stitch_op(indices, data) + @test_util.run_deprecated_v1 def testErrorDataAndIndicesSizeMismatch(self): indices = [ constant_op.constant([0, 4, 7]), @@ -222,16 +229,17 @@ class ParallelDynamicStitchTest(DynamicStitchTestBase, test.TestCase): DynamicStitchTestBase.__init__(self, data_flow_ops.parallel_dynamic_stitch) def testScalar(self): - with self.session(use_gpu=True): + with test_util.use_gpu(): indices = [constant_op.constant(0), constant_op.constant(1)] data = [constant_op.constant(40.0), constant_op.constant(60.0)] for step in -1, 1: stitched_t = data_flow_ops.dynamic_stitch(indices[::step], data) - stitched_val = stitched_t.eval() + stitched_val = self.evaluate(stitched_t) self.assertAllEqual([40.0, 60.0][::step], stitched_val) # Dimension 0 is max(flatten(indices))+1. self.assertEqual([2], stitched_t.get_shape().as_list()) + @test_util.run_deprecated_v1 def testHigherRank(self): with self.session(use_gpu=True) as sess: indices = [ @@ -246,7 +254,7 @@ class ParallelDynamicStitchTest(DynamicStitchTestBase, test.TestCase): [[[51, 52], [21, 22]], [[1, 2], [31, 32]]], dtype=dtypes.float32) ] stitched_t = data_flow_ops.dynamic_stitch(indices, data) - stitched_val = stitched_t.eval() + stitched_val = self.evaluate(stitched_t) correct = 10 * np.arange(7)[:, None] + [1.0, 2.0] self.assertAllEqual(correct, stitched_val) self.assertEqual([7, 2], stitched_t.get_shape().as_list()) @@ -256,7 +264,7 @@ class ParallelDynamicStitchTest(DynamicStitchTestBase, test.TestCase): stitched_grad) self.assertEqual(grads[:3], [None] * 3) # Indices have no gradients for datum, grad in zip(data, sess.run(grads[3:])): - self.assertAllEqual(7.0 * datum.eval(), grad) + self.assertAllEqual(7.0 * self.evaluate(datum), grad) # GPU version unit tests def testScalarGPU(self): @@ -265,11 +273,12 @@ class ParallelDynamicStitchTest(DynamicStitchTestBase, test.TestCase): data = [constant_op.constant(40.0), constant_op.constant(60.0)] for step in -1, 1: stitched_t = data_flow_ops.dynamic_stitch(indices[::step], data) - stitched_val = stitched_t.eval() + stitched_val = self.evaluate(stitched_t) self.assertAllEqual([40.0, 60.0][::step], stitched_val) # Dimension 0 is max(flatten(indices))+1. self.assertEqual([2], stitched_t.get_shape().as_list()) + @test_util.run_deprecated_v1 def testHigherRankGPU(self): with self.cached_session() as sess: indices = [ @@ -284,7 +293,7 @@ class ParallelDynamicStitchTest(DynamicStitchTestBase, test.TestCase): [[[51, 52], [21, 22]], [[1, 2], [31, 32]]], dtype=dtypes.float32) ] stitched_t = data_flow_ops.dynamic_stitch(indices, data) - stitched_val = stitched_t.eval() + stitched_val = self.evaluate(stitched_t) correct = 10 * np.arange(7)[:, None] + [1.0, 2.0] self.assertAllEqual(correct, stitched_val) self.assertEqual([7, 2], stitched_t.get_shape().as_list()) @@ -294,7 +303,7 @@ class ParallelDynamicStitchTest(DynamicStitchTestBase, test.TestCase): stitched_grad) self.assertEqual(grads[:3], [None] * 3) # Indices have no gradients for datum, grad in zip(data, sess.run(grads[3:])): - self.assertAllEqual(7.0 * datum.eval(), grad) + self.assertAllEqual(7.0 * self.evaluate(datum), grad) if __name__ == "__main__": diff --git a/tensorflow/python/kernel_tests/edit_distance_op_test.py b/tensorflow/python/kernel_tests/edit_distance_op_test.py index dab5eee7f5..4a06ab770a 100644 --- a/tensorflow/python/kernel_tests/edit_distance_op_test.py +++ b/tensorflow/python/kernel_tests/edit_distance_op_test.py @@ -49,11 +49,11 @@ class EditDistanceTest(test.TestCase): if expected_err_re is None: self.assertEqual(edit_distance.get_shape(), expected_shape) - output = edit_distance.eval() + output = self.evaluate(edit_distance) self.assertAllClose(output, expected_output) else: with self.assertRaisesOpError(expected_err_re): - edit_distance.eval() + self.evaluate(edit_distance) def _testEditDistance(self, hypothesis, diff --git a/tensorflow/python/kernel_tests/embedding_ops_test.py b/tensorflow/python/kernel_tests/embedding_ops_test.py index 008d6fbf57..6019245d0f 100644 --- a/tensorflow/python/kernel_tests/embedding_ops_test.py +++ b/tensorflow/python/kernel_tests/embedding_ops_test.py @@ -28,6 +28,7 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import data_flow_ops from tensorflow.python.ops import embedding_ops @@ -76,7 +77,7 @@ class ScatterAddSubTest(test.TestCase): # p = init variables.global_variables_initializer().run() # p += vals - result = p2.eval() + result = self.evaluate(p2) # Compute the expected 'p' using numpy operations. for i, ind in enumerate(indices): if scatter_op == state_ops.scatter_add: @@ -87,16 +88,19 @@ class ScatterAddSubTest(test.TestCase): vals_shape[0], -1)[i, :]) self.assertTrue(all((p_init == result).ravel())) + @test_util.run_deprecated_v1 def testNoRepetitions(self): self._TestCase([2, 2], [1]) self._TestCase([4, 4, 4], [2, 0]) self._TestCase([43, 20, 10, 10], [42, 5, 6, 1, 3, 5, 7, 9]) + @test_util.run_deprecated_v1 def testWithRepetitions(self): self._TestCase([2, 2], [1, 1]) self._TestCase([5, 3, 9, 5], [2, 0, 4, 1, 3, 1, 4, 0, 4, 3]) self._TestCase([32, 4, 4], [31] * 8) + @test_util.run_deprecated_v1 def testRandom(self): # Random shapes of rank 4, random indices for _ in range(5): @@ -104,6 +108,7 @@ class ScatterAddSubTest(test.TestCase): indices = np.random.randint(shape[0], size=2 * shape[0]) self._TestCase(_AsLong(list(shape)), list(indices)) + @test_util.run_deprecated_v1 def testSubRandom(self): # Random shapes of rank 4, random indices for _ in range(5): @@ -111,6 +116,7 @@ class ScatterAddSubTest(test.TestCase): indices = np.random.randint(shape[0], size=2 * shape[0]) self._TestCase(_AsLong(list(shape)), list(indices), state_ops.scatter_sub) + @test_util.run_deprecated_v1 def testWrongShape(self): # Indices and values mismatch. var = variables.Variable( @@ -241,6 +247,7 @@ class EmbeddingLookupTest(test.TestCase): # both the ids are in the first shard, one of the resulting lookup # vector is going to be empty. The subsequent DivOp fails because of that. # TODO(keveman): Disabling the test until the underlying problem is fixed. + @test_util.run_deprecated_v1 def testSimpleSharded(self): with self.cached_session(): num_shards = 2 @@ -257,6 +264,7 @@ class EmbeddingLookupTest(test.TestCase): self.assertAllEqual(np_result, tf_result) self.assertShapeEqual(np_result, embedding) + @test_util.run_deprecated_v1 def testMaxNorm(self): with self.cached_session(): embeddings = constant_op.constant([[2.0]]) @@ -267,6 +275,7 @@ class EmbeddingLookupTest(test.TestCase): self.assertAllEqual(embedding.eval(), [[1.0]]) + @test_util.run_deprecated_v1 def testMaxNormNontrivial(self): with self.cached_session(): embeddings = constant_op.constant([[2.0, 4.0], [3.0, 1.0]]) @@ -278,8 +287,9 @@ class EmbeddingLookupTest(test.TestCase): norms = math_ops.sqrt( math_ops.reduce_sum(embeddings * embeddings, axis=1)) normalized = embeddings / array_ops.stack([norms, norms], axis=1) - self.assertAllEqual(embedding.eval(), 2 * normalized.eval()) + self.assertAllEqual(embedding.eval(), 2 * self.evaluate(normalized)) + @test_util.run_deprecated_v1 def testSimpleShardedPartitionedVariable(self): with self.cached_session() as sess: num_shards = 2 @@ -294,7 +304,7 @@ class EmbeddingLookupTest(test.TestCase): variables.global_variables_initializer().run() params_values = [params[p_i.name] for p_i in p] # Test that the PartitionedVariable components equal the list in p - p_var_val = sess.run(list(p_variable)) + p_var_val = self.evaluate(list(p_variable)) # Actual test tf_result = embedding.eval(feed_dict=feed_dict) np_result, _, _ = _EmbeddingResult(params, id_vals, num_shards, vocab_size) @@ -302,6 +312,7 @@ class EmbeddingLookupTest(test.TestCase): self.assertAllEqual(np_result, tf_result) self.assertShapeEqual(np_result, embedding) + @test_util.run_deprecated_v1 def testSimpleShardedPartitionedResourceVariable(self): with self.cached_session() as sess: num_shards = 2 @@ -316,15 +327,16 @@ class EmbeddingLookupTest(test.TestCase): variables.global_variables_initializer().run() params_values = [params[p_i.name] for p_i in p] # Test that the PartitionedVariable components equal the list in p - p_var_val = sess.run(list(p_variable)) + p_var_val = self.evaluate(list(p_variable)) # Actual test print(ops.get_default_graph().as_graph_def()) - tf_result = embedding.eval() + tf_result = self.evaluate(embedding) np_result, _, _ = _EmbeddingResult(params, id_vals, num_shards, vocab_size) self.assertAllEqual(params_values, p_var_val) self.assertAllEqual(np_result, tf_result) self.assertShapeEqual(np_result, embedding) + @test_util.run_deprecated_v1 def testShardedModPartitioningInt32Ids(self): with self.cached_session(): num_shards = 5 @@ -347,6 +359,7 @@ class EmbeddingLookupTest(test.TestCase): self.assertAllEqual(np_result, tf_result) self.assertShapeEqual(np_result, embedding) + @test_util.run_deprecated_v1 def testShardedModPartitioningInt64Ids(self): with self.cached_session(): num_shards = 5 @@ -369,6 +382,7 @@ class EmbeddingLookupTest(test.TestCase): self.assertAllEqual(np_result, tf_result) self.assertShapeEqual(np_result, embedding) + @test_util.run_deprecated_v1 def testShardedDivPartitioningInt32Ids(self): with self.cached_session(): num_shards = 5 @@ -393,6 +407,7 @@ class EmbeddingLookupTest(test.TestCase): self.assertAllEqual(np_result, tf_result) self.assertShapeEqual(np_result, embedding) + @test_util.run_deprecated_v1 def testShardedDivPartitioningInt32IdsPartitionedVariable(self): with self.cached_session(): num_shards = 5 @@ -418,6 +433,7 @@ class EmbeddingLookupTest(test.TestCase): self.assertAllEqual(np_result, tf_result) self.assertShapeEqual(np_result, embedding) + @test_util.run_deprecated_v1 def testShardedDivPartitioningInt64Ids(self): with self.cached_session(): num_shards = 5 @@ -442,6 +458,7 @@ class EmbeddingLookupTest(test.TestCase): self.assertAllEqual(np_result, tf_result) self.assertShapeEqual(np_result, embedding) + @test_util.run_deprecated_v1 def testShardedDivPartitioningUnknownParamShape(self): with self.cached_session(): num_shards = 5 @@ -468,6 +485,7 @@ class EmbeddingLookupTest(test.TestCase): params, id_vals, num_shards, vocab_size, partition_strategy="div") self.assertAllEqual(np_result, tf_result) + @test_util.run_deprecated_v1 def testGradientsEmbeddingLookup(self): vocab_size = 9 num_ids = 10 @@ -488,6 +506,7 @@ class EmbeddingLookupTest(test.TestCase): x, x_shape, y, y_shape, x_init_value=x_init_value) self.assertLess(err, 1e-4) + @test_util.run_deprecated_v1 def testGradientsEmbeddingLookupWithComputedParams(self): vocab_size = 9 num_ids = 5 @@ -526,6 +545,7 @@ class EmbeddingLookupTest(test.TestCase): ids = constant_op.constant([0, 1, 1, 17], dtype=dtypes.int32) embedding_ops.embedding_lookup(p, ids) + @test_util.run_deprecated_v1 def testHigherRank(self): np.random.seed(8) with self.cached_session(): @@ -546,6 +566,7 @@ class EmbeddingLookupTest(test.TestCase): sharded = embedding_ops.embedding_lookup(split_params, ids).eval() self.assertAllEqual(simple, sharded) + @test_util.run_deprecated_v1 def testHigherRankMaxNorm(self): np.random.seed(8) with self.cached_session(): @@ -574,6 +595,7 @@ class EmbeddingLookupTest(test.TestCase): split_params, ids, max_norm=1.0).eval() self.assertAllEqual(simple, sharded) + @test_util.run_deprecated_v1 def testTransform(self): # This tests all combinations of: # - ids rank 0, 1, >1 @@ -648,6 +670,7 @@ class EmbeddingLookupSparseTest(test.TestCase): index += num_val return grouped_vals + @test_util.run_deprecated_v1 def testEmbeddingLookupSparse(self): vocab_size = 13 batch_size = 10 @@ -706,6 +729,7 @@ class EmbeddingLookupSparseTest(test.TestCase): atol = rtol self.assertAllClose(np_embedding_sum, tf_embedding_sum, rtol, atol) + @test_util.run_deprecated_v1 def testGradientsEmbeddingLookupSparse(self): vocab_size = 12 batch_size = 4 @@ -733,6 +757,7 @@ class EmbeddingLookupSparseTest(test.TestCase): x, x_shape, y, y_shape, x_init_value=x_init_value) self.assertLess(err, 1e-5 if dtype == dtypes.float64 else 2e-3) + @test_util.run_deprecated_v1 def testIncompatibleShapes(self): with self.cached_session(): x, _, _ = _EmbeddingParams(1, 10, dtype=dtypes.float32) @@ -758,11 +783,13 @@ class SafeEmbeddingLookupSparseTest(test.TestCase): assert num_shards > 0 assert num_shards <= vocab_size - embedding_weights = partitioned_variables.create_partitioned_variables( + initializer = init_ops.truncated_normal_initializer( + mean=0.0, stddev=1.0 / math.sqrt(vocab_size), dtype=dtypes.float32) + embedding_weights = list(variable_scope.get_variable( + name="embedding_weights", shape=[vocab_size, embed_dim], - slicing=[num_shards, 1], - initializer=init_ops.truncated_normal_initializer( - mean=0.0, stddev=1.0 / math.sqrt(vocab_size), dtype=dtypes.float32)) + partitioner=partitioned_variables.fixed_size_partitioner(num_shards), + initializer=initializer)) for w in embedding_weights: w.initializer.run() embedding_weights = [w.eval() for w in embedding_weights] @@ -818,6 +845,7 @@ class SafeEmbeddingLookupSparseTest(test.TestCase): return sparse_ids, sparse_weights + @test_util.run_deprecated_v1 def test_safe_embedding_lookup_sparse_return_zero_vector(self): with self.cached_session(): embedding_weights = self._random_weights() @@ -831,6 +859,7 @@ class SafeEmbeddingLookupSparseTest(test.TestCase): [(1.0 * embedding_weights[0][0] + 2.0 * embedding_weights[0][1]) / 3.0, [0] * 4, [0] * 4, embedding_weights[0][2], [0] * 4]) + @test_util.run_deprecated_v1 def test_safe_embedding_lookup_sparse_return_special_vector(self): with self.cached_session(): embedding_weights = self._random_weights() @@ -845,6 +874,7 @@ class SafeEmbeddingLookupSparseTest(test.TestCase): 3.0, embedding_weights[0][3], embedding_weights[0][3], embedding_weights[0][2], embedding_weights[0][3]]) + @test_util.run_deprecated_v1 def test_safe_embedding_lookup_sparse_no_weights(self): with self.cached_session(): embedding_weights = self._random_weights() @@ -859,6 +889,7 @@ class SafeEmbeddingLookupSparseTest(test.TestCase): [0] * 4, embedding_weights[0][2], ( embedding_weights[0][0] + embedding_weights[0][1]) / 2.0]) + @test_util.run_deprecated_v1 def test_safe_embedding_lookup_sparse_partitioned(self): with self.cached_session(): embedding_weights = self._random_weights(num_shards=3) @@ -873,6 +904,7 @@ class SafeEmbeddingLookupSparseTest(test.TestCase): [0] * 4, [0] * 4, embedding_weights[2], (embedding_weights[0] + embedding_weights[1]) / 2.0]) + @test_util.run_deprecated_v1 def test_safe_embedding_lookup_sparse_partitioned_inconsistent_weights(self): with self.cached_session(): embedding_weights = self._random_weights(num_shards=3) @@ -888,6 +920,7 @@ class SafeEmbeddingLookupSparseTest(test.TestCase): self.assertRaises(ValueError, embedding_ops.safe_embedding_lookup_sparse, embedding_weights, sparse_ids, sparse_weights) + @test_util.run_deprecated_v1 def test_safe_embedding_lookup_sparse_3d_return_zero_vector(self): with self.cached_session(): embedding_weights = self._random_weights() @@ -901,6 +934,7 @@ class SafeEmbeddingLookupSparseTest(test.TestCase): [0] * 4, [0] * 4 ], [embedding_weights[0][2], [0] * 4, [0] * 4]]) + @test_util.run_deprecated_v1 def test_safe_embedding_lookup_sparse_3d_return_special_vector(self): with self.cached_session(): embedding_weights = self._random_weights() @@ -917,6 +951,7 @@ class SafeEmbeddingLookupSparseTest(test.TestCase): embedding_weights[0][3] ]]) + @test_util.run_deprecated_v1 def test_safe_embedding_lookup_sparse_3d_no_weights(self): with self.cached_session(): embedding_weights = self._random_weights() @@ -933,6 +968,7 @@ class SafeEmbeddingLookupSparseTest(test.TestCase): (embedding_weights[0][0] + embedding_weights[0][1]) / 2.0, [0] * 4 ]]) + @test_util.run_deprecated_v1 def test_safe_embedding_lookup_sparse_3d_partitioned(self): with self.cached_session(): embedding_weights = self._random_weights(num_shards=3) @@ -949,6 +985,7 @@ class SafeEmbeddingLookupSparseTest(test.TestCase): (embedding_weights[0] + embedding_weights[1]) / 2.0, [0] * 4 ]]) + @test_util.run_deprecated_v1 def test_safe_embedding_lookup_sparse_3d_partitioned_inconsistent_weights( self): with self.cached_session(): @@ -968,6 +1005,7 @@ class SafeEmbeddingLookupSparseTest(test.TestCase): class DynamicStitchOpTest(test.TestCase): + @test_util.run_deprecated_v1 def testCint32Cpu(self): with self.session(use_gpu=False): indices = [ @@ -981,6 +1019,7 @@ class DynamicStitchOpTest(test.TestCase): self.assertAllEqual( data_flow_ops.dynamic_stitch(indices, values).eval(), [12, 23, 1, 2]) + @test_util.run_deprecated_v1 def testCint32Gpu(self): with self.session(use_gpu=True): indices = [ @@ -994,6 +1033,7 @@ class DynamicStitchOpTest(test.TestCase): self.assertAllEqual( data_flow_ops.dynamic_stitch(indices, values).eval(), [12, 23, 1, 2]) + @test_util.run_deprecated_v1 def testInt32Cpu(self): with self.session(use_gpu=False): indices = [ @@ -1007,6 +1047,7 @@ class DynamicStitchOpTest(test.TestCase): self.assertAllEqual( data_flow_ops.dynamic_stitch(indices, values).eval(), [12, 23, 1, 2]) + @test_util.run_deprecated_v1 def testInt32Gpu(self): with self.session(use_gpu=True): indices = [ @@ -1020,6 +1061,7 @@ class DynamicStitchOpTest(test.TestCase): self.assertAllEqual( data_flow_ops.dynamic_stitch(indices, values).eval(), [12, 23, 1, 2]) + @test_util.run_deprecated_v1 def testSumGradArgs(self): with self.session(use_gpu=False): indices = [ @@ -1034,6 +1076,7 @@ class DynamicStitchOpTest(test.TestCase): data_flow_ops.dynamic_stitch(indices, values).eval(), [2, 3, 1, 1]) # We expect that the values are merged in order. + @test_util.run_deprecated_v1 def testStitchOrder(self): with self.cached_session(): indices = [] @@ -1049,6 +1092,7 @@ class DynamicStitchOpTest(test.TestCase): class ParallelDynamicStitchOpTest(test.TestCase): + @test_util.run_deprecated_v1 def testCint32Cpu(self): with self.session(use_gpu=False): indices = [ @@ -1063,6 +1107,7 @@ class ParallelDynamicStitchOpTest(test.TestCase): data_flow_ops.parallel_dynamic_stitch(indices, values).eval(), [12, 23, 1, 2, 34, 3, 45]) + @test_util.run_deprecated_v1 def testInt32Cpu(self): with self.session(use_gpu=False): indices = [ @@ -1077,6 +1122,7 @@ class ParallelDynamicStitchOpTest(test.TestCase): data_flow_ops.parallel_dynamic_stitch(indices, values).eval(), [12, 23, 1, 2, 3, 34, 45, 56]) + @test_util.run_deprecated_v1 def testSimple(self): with self.session(use_gpu=False): indices = [ops.convert_to_tensor([0, 1]), ops.convert_to_tensor([2, 3])] diff --git a/tensorflow/python/kernel_tests/extract_image_patches_grad_test.py b/tensorflow/python/kernel_tests/extract_image_patches_grad_test.py index 7d9d4e5175..7ba2dc6c20 100644 --- a/tensorflow/python/kernel_tests/extract_image_patches_grad_test.py +++ b/tensorflow/python/kernel_tests/extract_image_patches_grad_test.py @@ -23,6 +23,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import random_seed as random_seed_lib +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import gradients_impl @@ -78,6 +79,7 @@ class ExtractImagePatchesGradTest(test.TestCase): }, ] + @test_util.run_deprecated_v1 def testGradient(self): # Set graph seed for determinism. random_seed = 42 @@ -102,6 +104,7 @@ class ExtractImagePatchesGradTest(test.TestCase): print('extract_image_patches gradient err: %.4e' % err) self.assertLess(err, 1e-4) + @test_util.run_deprecated_v1 def testConstructGradientWithLargeImages(self): batch_size = 4 height = 1024 diff --git a/tensorflow/python/kernel_tests/extract_image_patches_op_test.py b/tensorflow/python/kernel_tests/extract_image_patches_op_test.py index 61436f24cf..bb3c0ae806 100644 --- a/tensorflow/python/kernel_tests/extract_image_patches_op_test.py +++ b/tensorflow/python/kernel_tests/extract_image_patches_op_test.py @@ -21,6 +21,7 @@ from __future__ import print_function import numpy as np from tensorflow.python.framework import constant_op +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.platform import test @@ -43,7 +44,7 @@ class ExtractImagePatches(test.TestCase): strides = [1] + strides + [1] rates = [1] + rates + [1] - with self.session(use_gpu=True): + with test_util.use_gpu(): out_tensor = array_ops.extract_image_patches( constant_op.constant(image), ksizes=ksizes, @@ -51,7 +52,7 @@ class ExtractImagePatches(test.TestCase): rates=rates, padding=padding, name="im2col") - self.assertAllClose(patches, out_tensor.eval()) + self.assertAllClose(patches, self.evaluate(out_tensor)) def testKsize1x1Stride1x1Rate1x1(self): """Verifies that for 1x1 kernel the output equals the input.""" diff --git a/tensorflow/python/kernel_tests/extract_volume_patches_op_test.py b/tensorflow/python/kernel_tests/extract_volume_patches_op_test.py index bbb3fef85b..88f7df8fbb 100644 --- a/tensorflow/python/kernel_tests/extract_volume_patches_op_test.py +++ b/tensorflow/python/kernel_tests/extract_volume_patches_op_test.py @@ -21,6 +21,7 @@ from __future__ import print_function import numpy as np from tensorflow.python.framework import constant_op +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.platform import test @@ -45,14 +46,14 @@ class ExtractVolumePatches(test.TestCase): ksizes = [1] + ksizes + [1] strides = [1] + strides + [1] - with self.cached_session(use_gpu=True): + with test_util.use_gpu(): out_tensor = array_ops.extract_volume_patches( constant_op.constant(image), ksizes=ksizes, strides=strides, padding=padding, name="im2col_3d") - self.assertAllClose(patches, out_tensor.eval()) + self.assertAllClose(patches, self.evaluate(out_tensor)) # pylint: disable=bad-whitespace def testKsize1x1x1Stride1x1x1(self): diff --git a/tensorflow/python/kernel_tests/fifo_queue_test.py b/tensorflow/python/kernel_tests/fifo_queue_test.py index 8961c4b13c..9655351a01 100644 --- a/tensorflow/python/kernel_tests/fifo_queue_test.py +++ b/tensorflow/python/kernel_tests/fifo_queue_test.py @@ -159,7 +159,7 @@ class FIFOQueueTest(test.TestCase): # Run one producer thread for each element in elems. def enqueue(enqueue_op): - sess.run(enqueue_op) + self.evaluate(enqueue_op) threads = [ self.checkedThread( @@ -191,7 +191,7 @@ class FIFOQueueTest(test.TestCase): results = [] def dequeue(): - results.append(sess.run(dequeued_t)) + results.append(self.evaluate(dequeued_t)) threads = [self.checkedThread(target=dequeue) for _ in enqueue_ops] for thread in threads: @@ -211,7 +211,7 @@ class FIFOQueueTest(test.TestCase): enqueue_op.run() for i in xrange(len(elems)): - vals = dequeued_t.eval() + vals = self.evaluate(dequeued_t) self.assertEqual([elems[i]], vals) def testDequeueHalf(self): @@ -225,7 +225,7 @@ class FIFOQueueTest(test.TestCase): enqueue_op.run() for i in xrange(len(elems)): - vals = dequeued_t.eval() + vals = self.evaluate(dequeued_t) self.assertEqual([elems[i]], vals) def testEnqueueAndBlockingDequeue(self): @@ -240,13 +240,13 @@ class FIFOQueueTest(test.TestCase): # TODO(mrry): Figure out how to do this without sleeping. time.sleep(0.1) for enqueue_op in enqueue_ops: - sess.run(enqueue_op) + self.evaluate(enqueue_op) results = [] def dequeue(): for _ in xrange(len(elems)): - results.append(sess.run(dequeued_t)) + results.append(self.evaluate(dequeued_t)) enqueue_thread = self.checkedThread(target=enqueue) dequeue_thread = self.checkedThread(target=dequeue) @@ -269,7 +269,7 @@ class FIFOQueueTest(test.TestCase): enqueue_op.run() for i in xrange(len(elems)): - x_val, y_val = sess.run(dequeued_t) + x_val, y_val = self.evaluate(dequeued_t) x, y = elems[i] self.assertEqual([x], x_val) self.assertEqual([y], y_val) @@ -288,9 +288,9 @@ class FIFOQueueTest(test.TestCase): self.assertEqual([], size.get_shape()) enqueue_op.run() - self.assertEqual(1, size.eval()) + self.assertEqual(1, self.evaluate(size)) dequeued_t.op.run() - self.assertEqual(0, size.eval()) + self.assertEqual(0, self.evaluate(size)) def testEnqueueMany(self): with self.cached_session(): @@ -302,7 +302,7 @@ class FIFOQueueTest(test.TestCase): enqueue_op.run() for i in range(8): - vals = dequeued_t.eval() + vals = self.evaluate(dequeued_t) self.assertEqual([elems[i % 4]], vals) def testEmptyEnqueueMany(self): @@ -313,9 +313,9 @@ class FIFOQueueTest(test.TestCase): enqueue_op = q.enqueue_many((empty_t,)) size_t = q.size() - self.assertEqual([0], size_t.eval()) + self.assertEqual([0], self.evaluate(size_t)) enqueue_op.run() - self.assertEqual([0], size_t.eval()) + self.assertEqual([0], self.evaluate(size_t)) def testEmptyDequeueMany(self): with self.cached_session(): @@ -323,9 +323,9 @@ class FIFOQueueTest(test.TestCase): enqueue_op = q.enqueue((10.0,)) dequeued_t = q.dequeue_many(0) - self.assertEqual([], dequeued_t.eval().tolist()) + self.assertEqual([], self.evaluate(dequeued_t).tolist()) enqueue_op.run() - self.assertEqual([], dequeued_t.eval().tolist()) + self.assertEqual([], self.evaluate(dequeued_t).tolist()) def testEmptyDequeueUpTo(self): with self.cached_session(): @@ -333,9 +333,9 @@ class FIFOQueueTest(test.TestCase): enqueue_op = q.enqueue((10.0,)) dequeued_t = q.dequeue_up_to(0) - self.assertEqual([], dequeued_t.eval().tolist()) + self.assertEqual([], self.evaluate(dequeued_t).tolist()) enqueue_op.run() - self.assertEqual([], dequeued_t.eval().tolist()) + self.assertEqual([], self.evaluate(dequeued_t).tolist()) def testEmptyDequeueManyWithNoShape(self): with self.cached_session(): @@ -356,7 +356,7 @@ class FIFOQueueTest(test.TestCase): enqueue_op.run() for i in range(8): - float_val, int_val = sess.run(dequeued_t) + float_val, int_val = self.evaluate(dequeued_t) self.assertEqual(float_elems[i % 4], float_val) self.assertAllEqual(int_elems[i % 4], int_val) @@ -369,8 +369,8 @@ class FIFOQueueTest(test.TestCase): enqueue_op.run() - self.assertAllEqual(elems[0:4], dequeued_t.eval()) - self.assertAllEqual(elems[4:8], dequeued_t.eval()) + self.assertAllEqual(elems[0:4], self.evaluate(dequeued_t)) + self.assertAllEqual(elems[4:8], self.evaluate(dequeued_t)) def testDequeueUpToNoBlocking(self): with self.cached_session(): @@ -381,8 +381,8 @@ class FIFOQueueTest(test.TestCase): enqueue_op.run() - self.assertAllEqual(elems[0:4], dequeued_t.eval()) - self.assertAllEqual(elems[4:8], dequeued_t.eval()) + self.assertAllEqual(elems[0:4], self.evaluate(dequeued_t)) + self.assertAllEqual(elems[4:8], self.evaluate(dequeued_t)) def testMultiDequeueMany(self): with self.cached_session() as sess: @@ -399,17 +399,17 @@ class FIFOQueueTest(test.TestCase): enqueue_op.run() - float_val, int_val = sess.run(dequeued_t) + float_val, int_val = self.evaluate(dequeued_t) self.assertAllEqual(float_elems[0:4], float_val) self.assertAllEqual(int_elems[0:4], int_val) self.assertEqual(float_val.shape, dequeued_t[0].get_shape()) self.assertEqual(int_val.shape, dequeued_t[1].get_shape()) - float_val, int_val = sess.run(dequeued_t) + float_val, int_val = self.evaluate(dequeued_t) self.assertAllEqual(float_elems[4:8], float_val) self.assertAllEqual(int_elems[4:8], int_val) - float_val, int_val = sess.run(dequeued_single_t) + float_val, int_val = self.evaluate(dequeued_single_t) self.assertAllEqual(float_elems[8], float_val) self.assertAllEqual(int_elems[8], int_val) self.assertEqual(float_val.shape, dequeued_single_t[0].get_shape()) @@ -429,13 +429,13 @@ class FIFOQueueTest(test.TestCase): enqueue_op.run() - float_val, int_val = sess.run(dequeued_t) + float_val, int_val = self.evaluate(dequeued_t) self.assertAllEqual(float_elems[0:4], float_val) self.assertAllEqual(int_elems[0:4], int_val) self.assertEqual([None], dequeued_t[0].get_shape().as_list()) self.assertEqual([None, 2], dequeued_t[1].get_shape().as_list()) - float_val, int_val = sess.run(dequeued_t) + float_val, int_val = self.evaluate(dequeued_t) self.assertAllEqual(float_elems[4:8], float_val) self.assertAllEqual(int_elems[4:8], int_val) @@ -518,7 +518,7 @@ class FIFOQueueTest(test.TestCase): r"Expected \[2,3,3\], got \[2,3,4\]"): sess.run([enqueue_op], feed_dict={elems_bad: np.array([1] * 24).reshape((2, 3, 4))}) - dequeued_t.eval() + self.evaluate(dequeued_t) def testParallelEnqueueMany(self): with self.cached_session() as sess: @@ -529,7 +529,7 @@ class FIFOQueueTest(test.TestCase): # Enqueue 100 items in parallel on 10 threads. def enqueue(): - sess.run(enqueue_op) + self.evaluate(enqueue_op) threads = [self.checkedThread(target=enqueue) for _ in range(10)] for thread in threads: @@ -552,7 +552,7 @@ class FIFOQueueTest(test.TestCase): dequeued_elems = [] def dequeue(): - dequeued_elems.extend(sess.run(dequeued_t)) + dequeued_elems.extend(self.evaluate(dequeued_t)) threads = [self.checkedThread(target=dequeue) for _ in range(10)] for thread in threads: @@ -576,7 +576,7 @@ class FIFOQueueTest(test.TestCase): dequeued_elems = [] def dequeue(): - dequeued_elems.extend(sess.run(dequeued_t)) + dequeued_elems.extend(self.evaluate(dequeued_t)) threads = [self.checkedThread(target=dequeue) for _ in range(10)] for thread in threads: @@ -596,11 +596,11 @@ class FIFOQueueTest(test.TestCase): def enqueue(): for _ in xrange(100): - sess.run(enqueue_op) + self.evaluate(enqueue_op) def dequeue(): for _ in xrange(100): - self.assertTrue(sess.run(dequeued_t) in (10.0, 20.0)) + self.assertTrue(self.evaluate(dequeued_t) in (10.0, 20.0)) enqueue_threads = [self.checkedThread(target=enqueue) for _ in range(10)] dequeue_threads = [self.checkedThread(target=dequeue) for _ in range(10)] @@ -632,7 +632,7 @@ class FIFOQueueTest(test.TestCase): def dequeue(): for i in xrange(250): - self.assertEqual(i, sess.run(dequeued_t)) + self.assertEqual(i, self.evaluate(dequeued_t)) dequeue_thread = self.checkedThread(target=dequeue) dequeue_thread.start() @@ -663,7 +663,7 @@ class FIFOQueueTest(test.TestCase): dequeuemany_t = q.dequeue_many(count_placeholder) def enqueue(): - sess.run(enqueue_op) + self.evaluate(enqueue_op) enqueue_thread = self.checkedThread(target=enqueue) enqueue_thread.start() @@ -672,7 +672,7 @@ class FIFOQueueTest(test.TestCase): while elements_dequeued < 250: # With equal probability, run Dequeue or dequeue_many. if random.random() > 0.5: - self.assertEqual(elements_dequeued, dequeued_t.eval()) + self.assertEqual(elements_dequeued, self.evaluate(dequeued_t)) elements_dequeued += 1 else: count = random.randint(0, min(20, 250 - elements_dequeued)) @@ -701,10 +701,10 @@ class FIFOQueueTest(test.TestCase): # The enqueue_op should run after the dequeue op has blocked. # TODO(mrry): Figure out how to do this without sleeping. time.sleep(0.1) - sess.run(enqueue_op) + self.evaluate(enqueue_op) def dequeue(): - dequeued_elems.extend(sess.run(dequeued_t).tolist()) + dequeued_elems.extend(self.evaluate(dequeued_t).tolist()) enqueue_thread = self.checkedThread(target=enqueue) dequeue_thread = self.checkedThread(target=dequeue) @@ -728,10 +728,10 @@ class FIFOQueueTest(test.TestCase): # The enqueue_op should run after the dequeue op has blocked. # TODO(mrry): Figure out how to do this without sleeping. time.sleep(0.1) - sess.run(enqueue_op) + self.evaluate(enqueue_op) def dequeue(): - dequeued_elems.extend(sess.run(dequeued_t).tolist()) + dequeued_elems.extend(self.evaluate(dequeued_t).tolist()) enqueue_thread = self.checkedThread(target=enqueue) dequeue_thread = self.checkedThread(target=dequeue) @@ -778,12 +778,12 @@ class FIFOQueueTest(test.TestCase): enqueue_op.run() close_op.run() for elem in elems: - self.assertEqual([elem], dequeued_t.eval()) + self.assertEqual([elem], self.evaluate(dequeued_t)) # Expect the operation to fail due to the queue being closed. with self.assertRaisesRegexp(errors_impl.OutOfRangeError, "is closed and has insufficient"): - dequeued_t.eval() + self.evaluate(dequeued_t) def testBlockingDequeueFromClosedQueue(self): with self.cached_session() as sess: @@ -797,11 +797,11 @@ class FIFOQueueTest(test.TestCase): def dequeue(): for elem in elems: - self.assertEqual([elem], sess.run(dequeued_t)) + self.assertEqual([elem], self.evaluate(dequeued_t)) # Expect the operation to fail due to the queue being closed. with self.assertRaisesRegexp(errors_impl.OutOfRangeError, "is closed and has insufficient"): - sess.run(dequeued_t) + self.evaluate(dequeued_t) dequeue_thread = self.checkedThread(target=dequeue) dequeue_thread.start() @@ -821,7 +821,7 @@ class FIFOQueueTest(test.TestCase): # Expect the operation to fail due to the queue being closed. with self.assertRaisesRegexp(errors_impl.OutOfRangeError, "is closed and has insufficient"): - sess.run(dequeued_t) + self.evaluate(dequeued_t) dequeue_thread = self.checkedThread(target=dequeue) dequeue_thread.start() @@ -842,11 +842,11 @@ class FIFOQueueTest(test.TestCase): enqueue_op.run() def dequeue(): - self.assertAllEqual(elems, sess.run(dequeued_t)) + self.assertAllEqual(elems, self.evaluate(dequeued_t)) # Expect the operation to fail due to the queue being closed. with self.assertRaisesRegexp(errors_impl.OutOfRangeError, "is closed and has insufficient"): - sess.run(dequeued_t) + self.evaluate(dequeued_t) dequeue_thread = self.checkedThread(target=dequeue) dequeue_thread.start() @@ -867,11 +867,11 @@ class FIFOQueueTest(test.TestCase): enqueue_op.run() def dequeue(): - self.assertAllEqual(elems[:3], sess.run(dequeued_t)) + self.assertAllEqual(elems[:3], self.evaluate(dequeued_t)) # Expect the operation to fail due to the queue being closed. with self.assertRaisesRegexp(errors_impl.OutOfRangeError, "is closed and has insufficient"): - sess.run(dequeued_t) + self.evaluate(dequeued_t) dequeue_thread = self.checkedThread(target=dequeue) dequeue_thread.start() @@ -892,8 +892,8 @@ class FIFOQueueTest(test.TestCase): enqueue_op.run() def dequeue(): - self.assertAllEqual(elems[:3], sess.run(dequeued_t)) - self.assertAllEqual(elems[3:], sess.run(dequeued_t)) + self.assertAllEqual(elems[:3], self.evaluate(dequeued_t)) + self.assertAllEqual(elems[3:], self.evaluate(dequeued_t)) dequeue_thread = self.checkedThread(target=dequeue) dequeue_thread.start() @@ -913,16 +913,16 @@ class FIFOQueueTest(test.TestCase): cleanup_dequeue_t = q.dequeue() def enqueue(): - sess.run(enqueue_op) + self.evaluate(enqueue_op) def dequeue(): - self.assertAllEqual(elems[0:3], sess.run(dequeued_t)) + self.assertAllEqual(elems[0:3], self.evaluate(dequeued_t)) with self.assertRaises(errors_impl.OutOfRangeError): - sess.run(dequeued_t) - self.assertEqual(elems[3], sess.run(cleanup_dequeue_t)) + self.evaluate(dequeued_t) + self.assertEqual(elems[3], self.evaluate(cleanup_dequeue_t)) def close(): - sess.run(close_op) + self.evaluate(close_op) enqueue_thread = self.checkedThread(target=enqueue) enqueue_thread.start() @@ -955,7 +955,7 @@ class FIFOQueueTest(test.TestCase): def dequeue(): with self.assertRaises(errors_impl.OutOfRangeError): - sess.run([dequeued_a_t, dequeued_b_t]) + self.evaluate([dequeued_a_t, dequeued_b_t]) dequeue_thread = self.checkedThread(target=dequeue) dequeue_thread.start() @@ -968,7 +968,7 @@ class FIFOQueueTest(test.TestCase): # Test that the elements in the partially-dequeued batch are # restored in the correct order. for elem_a, elem_b in zip(elems_a, elems_b): - val_a, val_b = sess.run([cleanup_dequeue_a_t, cleanup_dequeue_b_t]) + val_a, val_b = self.evaluate([cleanup_dequeue_a_t, cleanup_dequeue_b_t]) self.assertEqual(elem_a, val_a) self.assertEqual(elem_b, val_b) self.assertEqual(0, q.size().eval()) @@ -983,7 +983,7 @@ class FIFOQueueTest(test.TestCase): # Expect the operation to fail due to the queue being closed. with self.assertRaisesRegexp(errors_impl.OutOfRangeError, "is closed and has insufficient"): - sess.run(dequeued_t) + self.evaluate(dequeued_t) dequeue_thread = self.checkedThread(target=dequeue) dequeue_thread.start() @@ -1003,7 +1003,7 @@ class FIFOQueueTest(test.TestCase): # Expect the operation to fail due to the queue being closed. with self.assertRaisesRegexp(errors_impl.OutOfRangeError, "is closed and has insufficient"): - sess.run(dequeued_t) + self.evaluate(dequeued_t) dequeue_thread = self.checkedThread(target=dequeue) dequeue_thread.start() @@ -1051,7 +1051,7 @@ class FIFOQueueTest(test.TestCase): enqueue_op.run() def blocking_enqueue(): - sess.run(blocking_enqueue_op) + self.evaluate(blocking_enqueue_op) thread = self.checkedThread(target=blocking_enqueue) thread.start() @@ -1059,8 +1059,8 @@ class FIFOQueueTest(test.TestCase): # TODO(mrry): Figure out how to do this without sleeping. time.sleep(0.1) for elem in elems: - self.assertEqual([elem], dequeued_t.eval()) - self.assertEqual([50.0], dequeued_t.eval()) + self.assertEqual([elem], self.evaluate(dequeued_t)) + self.assertEqual([50.0], self.evaluate(dequeued_t)) thread.join() def testBlockingEnqueueManyToFullQueue(self): @@ -1074,7 +1074,7 @@ class FIFOQueueTest(test.TestCase): enqueue_op.run() def blocking_enqueue(): - sess.run(blocking_enqueue_op) + self.evaluate(blocking_enqueue_op) thread = self.checkedThread(target=blocking_enqueue) thread.start() @@ -1082,10 +1082,10 @@ class FIFOQueueTest(test.TestCase): # TODO(mrry): Figure out how to do this without sleeping. time.sleep(0.1) for elem in elems: - self.assertEqual([elem], dequeued_t.eval()) + self.assertEqual([elem], self.evaluate(dequeued_t)) time.sleep(0.01) - self.assertEqual([50.0], dequeued_t.eval()) - self.assertEqual([60.0], dequeued_t.eval()) + self.assertEqual([50.0], self.evaluate(dequeued_t)) + self.assertEqual([60.0], self.evaluate(dequeued_t)) # Make sure the thread finishes before exiting. thread.join() @@ -1103,7 +1103,7 @@ class FIFOQueueTest(test.TestCase): def blocking_enqueue(): # Expect the operation to succeed once the dequeue op runs. - sess.run(blocking_enqueue_op) + self.evaluate(blocking_enqueue_op) enqueue_thread = self.checkedThread(target=blocking_enqueue) enqueue_thread.start() @@ -1113,18 +1113,18 @@ class FIFOQueueTest(test.TestCase): time.sleep(0.1) def close(): - sess.run(close_op) + self.evaluate(close_op) close_thread = self.checkedThread(target=close) close_thread.start() # The dequeue will unblock both threads. - self.assertEqual(10.0, dequeued_t.eval()) + self.assertEqual(10.0, self.evaluate(dequeued_t)) enqueue_thread.join() close_thread.join() for elem in [20.0, 30.0, 40.0, 50.0]: - self.assertEqual(elem, dequeued_t.eval()) + self.assertEqual(elem, self.evaluate(dequeued_t)) self.assertEqual(0, q.size().eval()) def testBlockingEnqueueManyBeforeClose(self): @@ -1138,7 +1138,7 @@ class FIFOQueueTest(test.TestCase): enqueue_op.run() def blocking_enqueue(): - sess.run(blocking_enqueue_op) + self.evaluate(blocking_enqueue_op) enqueue_thread = self.checkedThread(target=blocking_enqueue) enqueue_thread.start() @@ -1148,17 +1148,17 @@ class FIFOQueueTest(test.TestCase): time.sleep(0.1) def close(): - sess.run(close_op) + self.evaluate(close_op) close_thread = self.checkedThread(target=close) close_thread.start() # The dequeue will unblock both threads. - self.assertEqual(10.0, dequeued_t.eval()) + self.assertEqual(10.0, self.evaluate(dequeued_t)) enqueue_thread.join() close_thread.join() for elem in [20.0, 30.0, 50.0, 60.0]: - self.assertEqual(elem, dequeued_t.eval()) + self.assertEqual(elem, self.evaluate(dequeued_t)) def testDoesNotLoseValue(self): with self.cached_session(): @@ -1266,19 +1266,19 @@ class FIFOQueueTest(test.TestCase): def _blockingDequeue(self, sess, dequeue_op): with self.assertRaisesOpError("was cancelled"): - sess.run(dequeue_op) + self.evaluate(dequeue_op) def _blockingDequeueMany(self, sess, dequeue_many_op): with self.assertRaisesOpError("was cancelled"): - sess.run(dequeue_many_op) + self.evaluate(dequeue_many_op) def _blockingEnqueue(self, sess, enqueue_op): with self.assertRaisesOpError("was cancelled"): - sess.run(enqueue_op) + self.evaluate(enqueue_op) def _blockingEnqueueMany(self, sess, enqueue_many_op): with self.assertRaisesOpError("was cancelled"): - sess.run(enqueue_many_op) + self.evaluate(enqueue_many_op) def testResetOfBlockingOperation(self): with self.cached_session() as sess: @@ -1321,7 +1321,7 @@ class FIFOQueueTest(test.TestCase): def blocking_enqueue(): enq_done.append(False) # This will fill the queue and then block until enough dequeues happen. - sess.run(enq) + self.evaluate(enq) enq_done.append(True) thread = self.checkedThread(target=blocking_enqueue) @@ -1331,14 +1331,14 @@ class FIFOQueueTest(test.TestCase): results = [] results.append(deq.eval()) # Will only complete after the enqueue starts. self.assertEqual(len(enq_done), 1) - self.assertEqual(sess.run(size_op), 5) + self.assertEqual(self.evaluate(size_op), 5) for _ in range(3): results.append(deq.eval()) time.sleep(0.1) self.assertEqual(len(enq_done), 1) - self.assertEqual(sess.run(size_op), 5) + self.assertEqual(self.evaluate(size_op), 5) # This dequeue will unblock the thread. results.append(deq.eval()) @@ -1364,7 +1364,7 @@ class FIFOQueueTest(test.TestCase): def blocking_dequeue(): # Will only complete after 4 enqueues complete. - results.extend(sess.run(deq)) + results.extend(self.evaluate(deq)) thread = self.checkedThread(target=blocking_dequeue) thread.start() @@ -1373,7 +1373,7 @@ class FIFOQueueTest(test.TestCase): # TODO(mrry): Figure out how to do this without sleeping. time.sleep(0.1) self.assertEqual(len(results), 0) - sess.run(enq) + self.evaluate(enq) # Enough enqueued to unblock the dequeue thread.join() @@ -1405,7 +1405,7 @@ class FIFOQueueTest(test.TestCase): q.enqueue_many(input_tuple).run() output_tuple_t = q.dequeue_many(32) - output_tuple = sess.run(output_tuple_t) + output_tuple = self.evaluate(output_tuple_t) for (input_elem, output_elem) in zip(input_tuple, output_tuple): self.assertAllEqual(input_elem, output_elem) @@ -1507,10 +1507,10 @@ class FIFOQueueDictTest(test.TestCase): enqueue_op4 = q.enqueue_many({"f": [40.0, 50.0]}) dequeue = q.dequeue() dequeue_2 = q.dequeue_many(2) - sess.run(enqueue_op) - sess.run(enqueue_op2) - sess.run(enqueue_op3) - sess.run(enqueue_op4) + self.evaluate(enqueue_op) + self.evaluate(enqueue_op2) + self.evaluate(enqueue_op3) + self.evaluate(enqueue_op4) f = sess.run(dequeue["f"]) self.assertEqual(10.0, f) f = sess.run(dequeue_2["f"]) @@ -1565,10 +1565,10 @@ class FIFOQueueDictTest(test.TestCase): }) dequeue = q.dequeue() dequeue_2 = q.dequeue_many(2) - sess.run(enqueue_op) - sess.run(enqueue_op2) - sess.run(enqueue_op3) - sess.run(enqueue_op4) + self.evaluate(enqueue_op) + self.evaluate(enqueue_op2) + self.evaluate(enqueue_op3) + self.evaluate(enqueue_op4) i, f, s = sess.run([dequeue["i"], dequeue["f"], dequeue["s"]]) self.assertEqual(123, i) self.assertEqual(10.0, f) @@ -1597,7 +1597,7 @@ class FIFOQueueWithTimeoutTest(test.TestCase): # until operation_timeout_in_ms. with self.assertRaisesRegexp(errors_impl.DeadlineExceededError, "Timed out waiting for notification"): - sess.run(dequeued_t) + self.evaluate(dequeued_t) def testReusableAfterTimeout(self): with self.cached_session() as sess: @@ -1613,8 +1613,8 @@ class FIFOQueueWithTimeoutTest(test.TestCase): "Timed out waiting for notification"): sess.run(dequeued_t, options=config_pb2.RunOptions(timeout_in_ms=10)) - sess.run(enqueue_op) - self.assertEqual(37, sess.run(dequeued_t)) + self.evaluate(enqueue_op) + self.assertEqual(37, self.evaluate(dequeued_t)) class QueueContainerTest(test.TestCase): diff --git a/tensorflow/python/kernel_tests/fractional_avg_pool_op_test.py b/tensorflow/python/kernel_tests/fractional_avg_pool_op_test.py index f89d2062f1..0d5928aefa 100644 --- a/tensorflow/python/kernel_tests/fractional_avg_pool_op_test.py +++ b/tensorflow/python/kernel_tests/fractional_avg_pool_op_test.py @@ -24,6 +24,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gen_nn_ops from tensorflow.python.ops import gradient_checker @@ -37,7 +38,6 @@ class FractionalAvgTest(test.TestCase): # Random number generate with seed. _PRNG = np.random.RandomState(341261000) _SEED = 341261001 - _SEED2 = 341261002 def _AvgPoolAlongRows(self, input_matrix, row_seq, overlapping): """Perform average pool along row of a 2-D matrix based on row_seq. @@ -128,15 +128,13 @@ class FractionalAvgTest(test.TestCase): None """ with self.cached_session() as sess: - p, r, c = nn_ops.fractional_avg_pool( + p, r, c = nn_ops.fractional_avg_pool_v2( input_tensor, pooling_ratio, pseudo_random, overlapping, - deterministic=True, - seed=self._SEED, - seed2=self._SEED2) - actual, row_seq, col_seq = sess.run([p, r, c]) + seed=self._SEED) + actual, row_seq, col_seq = self.evaluate([p, r, c]) expected = self._GetExpectedFractionalAvgPoolResult(input_tensor, row_seq, col_seq, overlapping) self.assertShapeEqual(expected, p) @@ -161,15 +159,13 @@ class FractionalAvgTest(test.TestCase): rand_mat = self._PRNG.randint(10, size=tensor_shape) pooling_ratio = [1, math.sqrt(2), math.sqrt(2), 1] with self.cached_session() as sess: - p, r, c = nn_ops.fractional_avg_pool( + p, r, c = nn_ops.fractional_avg_pool_v2( rand_mat.astype(np.float32), pooling_ratio, pseudo_random, overlapping, - deterministic=True, - seed=self._SEED, - seed2=self._SEED2) - tensor_output, row_seq, col_seq = sess.run([p, r, c]) + seed=self._SEED) + tensor_output, row_seq, col_seq = self.evaluate([p, r, c]) expected_result = self._GetExpectedFractionalAvgPoolResult( rand_mat.astype(np.float32), row_seq, col_seq, overlapping) print("row sequence:") @@ -214,12 +210,6 @@ class FractionalAvgTest(test.TestCase): def testIntegerTensorInput(self): """Test FractionalAvgPool works fine when input tensor is integer type. - - I would have used _ValidateFractionalAvgPoolResult function to automate this - process, however, there's rounding issue. It is caused by numpy.mean cast - integer input to numpy.float64 for intermediate use. While for - fractional_avg_pool, the mean operation is integer division (trucated). So, - for this test case, I will hard code a simple matrix. """ pseudo_random = True overlapping = True @@ -234,29 +224,9 @@ class FractionalAvgTest(test.TestCase): [4, 4, 5, 9, 7, 2] ]) # pyformat: enable - with self.cached_session() as sess: - # Since deterministic = True, seed and seed2 are fixed. Therefore r, and c - # are the same each time. We can have an expected result precomputed. - # r = [0, 2, 4, 6] - # c = [0, 1, 3, 4, 6] - - # pyformat: disable - expected = np.array([ - [6, 5, 3, 5], - [5, 5, 4, 5], - [5, 4, 7, 5] - ]).reshape((1, 3, 4, 1)) - # pyformat: enable - p, unused_r, unused_c = nn_ops.fractional_avg_pool( - mat.reshape(tensor_shape), [1, math.sqrt(3), math.sqrt(2), 1], - pseudo_random, - overlapping, - deterministic=True, - seed=self._SEED, - seed2=self._SEED2) - actual = sess.run(p) - self.assertShapeEqual(expected, p) - self.assertAllClose(expected, actual) + self._ValidateFractionalAvgPoolResult(mat.reshape(tensor_shape), + [1, math.sqrt(3), math.sqrt(2), 1], + pseudo_random, overlapping) def testDifferentTensorShapes(self): """Test different shapes of input tensor. @@ -312,6 +282,7 @@ class FractionalAvgTest(test.TestCase): self._ValidateFractionalAvgPoolResult(rand_mat, [1, 2, 2, 1], pseudo_random, overlapping) + @test_util.run_deprecated_v1 def testDifferentInputTensorShape(self): """Runs the operation in one session with different input tensor shapes.""" with self.cached_session() as sess: @@ -320,14 +291,12 @@ class FractionalAvgTest(test.TestCase): pooling_ratio = [1, 1.5, 1.5, 1] pseudo_random = False overlapping = False - p, r, c = nn_ops.fractional_avg_pool( + p, r, c = nn_ops.fractional_avg_pool_v2( input_holder, pooling_ratio, pseudo_random, overlapping, - deterministic=True, - seed=self._SEED, - seed2=self._SEED2) + seed=self._SEED) # First run. input_a = np.zeros([3, 32, 32, 3]) actual, row_seq, col_seq = sess.run([p, r, c], {input_holder: input_a}) @@ -372,7 +341,6 @@ class FractionalAvgPoolGradTest(test.TestCase): """ _PRNG = np.random.RandomState(341261004) _SEED = 341261005 - _SEED2 = 341261006 def _GenerateRandomInputTensor(self, shape): num_elements = 1 @@ -398,7 +366,7 @@ class FractionalAvgPoolGradTest(test.TestCase): padding = "VALID" output_tensor = nn_ops.avg_pool(input_tensor, window_size, stride_size, padding) - output_data = output_tensor.eval() + output_data = self.evaluate(output_tensor) num_elements = 1 for dim_size in output_data.shape: num_elements *= dim_size @@ -407,7 +375,7 @@ class FractionalAvgPoolGradTest(test.TestCase): input_backprop_tensor = gen_nn_ops.avg_pool_grad( input_tensor.get_shape(), output_backprop, window_size, stride_size, padding) - input_backprop = input_backprop_tensor.eval() + input_backprop = self.evaluate(input_backprop_tensor) row_seq = list(range(0, num_rows + 1, row_window_size)) col_seq = list(range(0, num_cols + 1, col_window_size)) fap_input_backprop_tensor = gen_nn_ops.fractional_avg_pool_grad( @@ -416,7 +384,7 @@ class FractionalAvgPoolGradTest(test.TestCase): row_seq, col_seq, overlapping=False) - fap_input_backprop = fap_input_backprop_tensor.eval() + fap_input_backprop = self.evaluate(fap_input_backprop_tensor) self.assertShapeEqual(input_backprop, fap_input_backprop_tensor) self.assertAllClose(input_backprop, fap_input_backprop) @@ -437,7 +405,7 @@ class FractionalAvgPoolGradTest(test.TestCase): padding = "VALID" output_tensor = nn_ops.avg_pool(input_tensor, window_size, stride_size, padding) - output_data = output_tensor.eval() + output_data = self.evaluate(output_tensor) num_elements = 1 for dim_size in output_data.shape: num_elements *= dim_size @@ -446,7 +414,7 @@ class FractionalAvgPoolGradTest(test.TestCase): input_backprop_tensor = gen_nn_ops.avg_pool_grad( input_tensor.get_shape(), output_backprop, window_size, stride_size, padding) - input_backprop = input_backprop_tensor.eval() + input_backprop = self.evaluate(input_backprop_tensor) row_seq = list(range(0, num_rows, row_window_size - 1)) col_seq = list(range(0, num_cols, col_window_size - 1)) row_seq[-1] += 1 @@ -457,10 +425,11 @@ class FractionalAvgPoolGradTest(test.TestCase): row_seq, col_seq, overlapping=True) - fap_input_backprop = fap_input_backprop_tensor.eval() + fap_input_backprop = self.evaluate(fap_input_backprop_tensor) self.assertShapeEqual(input_backprop, fap_input_backprop_tensor) self.assertAllClose(input_backprop, fap_input_backprop) + @test_util.run_deprecated_v1 def testAllInputOptionsThroughGradientError(self): input_shape = (1, 7, 13, 1) input_data = self._GenerateRandomInputTensor(input_shape) @@ -470,15 +439,13 @@ class FractionalAvgPoolGradTest(test.TestCase): for overlapping in True, False: with self.cached_session() as _: input_tensor = constant_op.constant(input_data, shape=input_shape) - output_tensor, unused_a, unused_b = nn_ops.fractional_avg_pool( + output_tensor, unused_a, unused_b = nn_ops.fractional_avg_pool_v2( input_tensor, pooling_ratio, pseudo_random=pseudo_random, overlapping=overlapping, - deterministic=True, - seed=self._SEED, - seed2=self._SEED2) - output_data = output_tensor.eval() + seed=self._SEED) + output_data = self.evaluate(output_tensor) output_shape = output_data.shape # error_margin and delta setting is similar to avg_pool_grad. error_margin = 1e-4 @@ -491,6 +458,7 @@ class FractionalAvgPoolGradTest(test.TestCase): delta=1e-2) self.assertLess(gradient_error, error_margin) + @test_util.run_deprecated_v1 def testDifferentTensorShapesThroughGradientError(self): pseudo_random = True overlapping = True @@ -503,15 +471,13 @@ class FractionalAvgPoolGradTest(test.TestCase): input_data = self._GenerateRandomInputTensor(input_shape) with self.cached_session() as _: input_tensor = constant_op.constant(input_data, shape=input_shape) - output_tensor, unused_a, unused_b = nn_ops.fractional_avg_pool( + output_tensor, unused_a, unused_b = nn_ops.fractional_avg_pool_v2( input_tensor, pooling_ratio, pseudo_random=pseudo_random, overlapping=overlapping, - deterministic=True, - seed=self._SEED, - seed2=self._SEED2) - output_data = output_tensor.eval() + seed=self._SEED) + output_data = self.evaluate(output_tensor) output_shape = output_data.shape # error_margin and delta setting is similar to avg_pool_grad. error_margin = 1e-4 @@ -524,6 +490,7 @@ class FractionalAvgPoolGradTest(test.TestCase): delta=1e-2) self.assertLess(gradient_error, error_margin) + @test_util.run_deprecated_v1 def testLargePoolingRatioThroughGradientError(self): input_shape = (1, 17, 23, 1) input_data = self._GenerateRandomInputTensor(input_shape) @@ -534,14 +501,12 @@ class FractionalAvgPoolGradTest(test.TestCase): with self.cached_session() as _: input_tensor = constant_op.constant(input_data, shape=input_shape) - output_tensor, unused_a, unused_b = nn_ops.fractional_avg_pool( + output_tensor, unused_a, unused_b = nn_ops.fractional_avg_pool_v2( input_tensor, pooling_ratio, pseudo_random=pseudo_random, overlapping=overlapping, - deterministic=True, - seed=self._SEED, - seed2=self._SEED2) + seed=self._SEED) # error_margin and delta setting is similar to avg_pool_grad. error_margin = 1e-4 gradient_error = gradient_checker.compute_gradient_error( diff --git a/tensorflow/python/kernel_tests/fractional_max_pool_op_test.py b/tensorflow/python/kernel_tests/fractional_max_pool_op_test.py index 9b94ca8554..fa886cc215 100644 --- a/tensorflow/python/kernel_tests/fractional_max_pool_op_test.py +++ b/tensorflow/python/kernel_tests/fractional_max_pool_op_test.py @@ -24,6 +24,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gen_nn_ops from tensorflow.python.ops import gradient_checker @@ -37,7 +38,6 @@ class FractionalMaxPoolTest(test.TestCase): # Random number generate with seed. _PRNG = np.random.RandomState(341261) _SEED = 123456 - _SEED2 = 654321 def _MaxPoolAlongRows(self, input_matrix, row_seq, overlapping): """Perform max pool along row of a 2-D matrix based on row_seq. @@ -128,15 +128,13 @@ class FractionalMaxPoolTest(test.TestCase): None """ with self.cached_session() as sess: - p, r, c = nn_ops.fractional_max_pool( + p, r, c = nn_ops.fractional_max_pool_v2( input_tensor, pooling_ratio, pseudo_random, overlapping, - deterministic=True, - seed=self._SEED, - seed2=self._SEED2) - actual, row_seq, col_seq = sess.run([p, r, c]) + seed=self._SEED) + actual, row_seq, col_seq = self.evaluate([p, r, c]) expected = self._GetExpectedFractionalMaxPoolResult(input_tensor, row_seq, col_seq, overlapping) self.assertShapeEqual(expected, p) @@ -161,15 +159,13 @@ class FractionalMaxPoolTest(test.TestCase): rand_mat = self._PRNG.randint(10, size=tensor_shape) pooling_ratio = [1, math.sqrt(2), math.sqrt(2), 1] with self.cached_session() as sess: - p, r, c = nn_ops.fractional_max_pool( + p, r, c = nn_ops.fractional_max_pool_v2( rand_mat, pooling_ratio, pseudo_random, overlapping, - deterministic=True, - seed=self._SEED, - seed2=self._SEED2) - tensor_output, row_seq, col_seq = sess.run([p, r, c]) + seed=self._SEED) + tensor_output, row_seq, col_seq = self.evaluate([p, r, c]) expected_result = self._GetExpectedFractionalMaxPoolResult(rand_mat, row_seq, col_seq, @@ -283,6 +279,7 @@ class FractionalMaxPoolTest(test.TestCase): self._ValidateFractionalMaxPoolResult(rand_mat, [1, 2, 2, 1], pseudo_random, overlapping) + @test_util.run_deprecated_v1 def testDifferentInputTensorShape(self): """Runs the operation in one session with different input tensor shapes.""" with self.cached_session() as sess: @@ -291,14 +288,12 @@ class FractionalMaxPoolTest(test.TestCase): pooling_ratio = [1, 1.5, 1.5, 1] pseudo_random = False overlapping = False - p, r, c = nn_ops.fractional_max_pool( + p, r, c = nn_ops.fractional_max_pool_v2( input_holder, pooling_ratio, pseudo_random, overlapping, - deterministic=True, - seed=self._SEED, - seed2=self._SEED2) + seed=self._SEED) # First run. input_a = np.zeros([3, 32, 32, 3]) actual, row_seq, col_seq = sess.run([p, r, c], {input_holder: input_a}) @@ -344,7 +339,6 @@ class FractionalMaxPoolGradTest(test.TestCase): _PRNG = np.random.RandomState(341261) _SEED = 123456 - _SEED2 = 654321 def _GenerateUniqueRandomInputTensor(self, shape): """Generate 'unqiue' random input tensor. @@ -382,12 +376,12 @@ class FractionalMaxPoolGradTest(test.TestCase): padding = "VALID" output_tensor = nn_ops.max_pool(input_tensor, window_size, stride_size, padding) - output_data = output_tensor.eval() + output_data = self.evaluate(output_tensor) output_backprop = self._PRNG.randint(100, size=output_data.shape) input_backprop_tensor = gen_nn_ops.max_pool_grad( input_tensor, output_tensor, output_backprop, window_size, stride_size, padding) - input_backprop = input_backprop_tensor.eval() + input_backprop = self.evaluate(input_backprop_tensor) row_seq = list(range(0, num_rows + 1, row_window_size)) col_seq = list(range(0, num_cols + 1, col_window_size)) fmp_input_backprop_tensor = gen_nn_ops.fractional_max_pool_grad( @@ -397,7 +391,7 @@ class FractionalMaxPoolGradTest(test.TestCase): row_seq, col_seq, overlapping=False) - fmp_input_backprop = fmp_input_backprop_tensor.eval() + fmp_input_backprop = self.evaluate(fmp_input_backprop_tensor) self.assertShapeEqual(input_backprop, fmp_input_backprop_tensor) self.assertAllClose(input_backprop, fmp_input_backprop) @@ -417,12 +411,12 @@ class FractionalMaxPoolGradTest(test.TestCase): padding = "VALID" output_tensor = nn_ops.max_pool(input_tensor, window_size, stride_size, padding) - output_data = output_tensor.eval() + output_data = self.evaluate(output_tensor) output_backprop = self._PRNG.randint(100, size=output_data.shape) input_backprop_tensor = gen_nn_ops.max_pool_grad( input_tensor, output_tensor, output_backprop, window_size, stride_size, padding) - input_backprop = input_backprop_tensor.eval() + input_backprop = self.evaluate(input_backprop_tensor) row_seq = list(range(0, num_rows, row_window_size - 1)) col_seq = list(range(0, num_cols, col_window_size - 1)) row_seq[-1] += 1 @@ -434,10 +428,11 @@ class FractionalMaxPoolGradTest(test.TestCase): row_seq, col_seq, overlapping=True) - fmp_input_backprop = fmp_input_backprop_tensor.eval() + fmp_input_backprop = self.evaluate(fmp_input_backprop_tensor) self.assertShapeEqual(input_backprop, fmp_input_backprop_tensor) self.assertAllClose(input_backprop, fmp_input_backprop) + @test_util.run_deprecated_v1 def testAllInputOptionsThroughGradientError(self): input_shape = (1, 7, 13, 1) input_data = self._GenerateUniqueRandomInputTensor(input_shape) @@ -449,15 +444,13 @@ class FractionalMaxPoolGradTest(test.TestCase): for overlapping in True, False: with self.cached_session() as _: input_tensor = constant_op.constant(input_data, shape=input_shape) - output_tensor, unused_a, unused_b = nn_ops.fractional_max_pool( + output_tensor, unused_a, unused_b = nn_ops.fractional_max_pool_v2( input_tensor, pooling_ratio, pseudo_random=pseudo_random, overlapping=overlapping, - deterministic=True, - seed=self._SEED, - seed2=self._SEED2) - output_data = output_tensor.eval() + seed=self._SEED) + output_data = self.evaluate(output_tensor) output_shape = output_data.shape # error_margin and delta setting is similar to max_pool_grad. error_margin = 1e-3 @@ -470,6 +463,7 @@ class FractionalMaxPoolGradTest(test.TestCase): delta=1e-2) self.assertLess(gradient_error, error_margin) + @test_util.run_deprecated_v1 def testDifferentTensorShapesThroughGradientError(self): pseudo_random = True overlapping = True @@ -484,15 +478,13 @@ class FractionalMaxPoolGradTest(test.TestCase): input_data += self._PRNG.random_sample(input_shape) with self.cached_session() as _: input_tensor = constant_op.constant(input_data, shape=input_shape) - output_tensor, unused_a, unused_b = nn_ops.fractional_max_pool( + output_tensor, unused_a, unused_b = nn_ops.fractional_max_pool_v2( input_tensor, pooling_ratio, pseudo_random=pseudo_random, overlapping=overlapping, - deterministic=True, - seed=self._SEED, - seed2=self._SEED2) - output_data = output_tensor.eval() + seed=self._SEED) + output_data = self.evaluate(output_tensor) output_shape = output_data.shape # error_margin and delta setting is similar to max_pool_grad. error_margin = 1e-3 @@ -505,6 +497,7 @@ class FractionalMaxPoolGradTest(test.TestCase): delta=1e-2) self.assertLess(gradient_error, error_margin) + @test_util.run_deprecated_v1 def testLargePoolingRatioThroughGradientError(self): input_shape = (1, 17, 23, 1) input_data = self._GenerateUniqueRandomInputTensor(input_shape) @@ -517,14 +510,12 @@ class FractionalMaxPoolGradTest(test.TestCase): with self.cached_session() as _: input_tensor = constant_op.constant(input_data, shape=input_shape) - output_tensor, unused_a, unused_b = nn_ops.fractional_max_pool( + output_tensor, unused_a, unused_b = nn_ops.fractional_max_pool_v2( input_tensor, pooling_ratio, pseudo_random=pseudo_random, overlapping=overlapping, - deterministic=True, - seed=self._SEED, - seed2=self._SEED2) + seed=self._SEED) # error_margin and delta setting is similar to max_pool_grad. error_margin = 1e-3 gradient_error = gradient_checker.compute_gradient_error( @@ -592,7 +583,7 @@ class FractionalMaxPoolGradTest(test.TestCase): row_seq, col_seq, overlapping=False) - input_backprop_not_overlapping = r.eval() + input_backprop_not_overlapping = self.evaluate(r) self.assertShapeEqual( np.reshape(expected_input_backprop_not_overlapping, input_size), r) self.assertAllClose(expected_input_backprop_not_overlapping, @@ -602,7 +593,7 @@ class FractionalMaxPoolGradTest(test.TestCase): output_data_overlapping, shape=output_size) r = gen_nn_ops.fractional_max_pool_grad( input_tensor, output_tensor, grad, row_seq, col_seq, overlapping=True) - input_backprop_overlapping = r.eval() + input_backprop_overlapping = self.evaluate(r) self.assertShapeEqual( np.reshape(expected_input_backprop_overlapping, input_size), r) self.assertAllClose(expected_input_backprop_overlapping, diff --git a/tensorflow/python/kernel_tests/functional_ops_test.py b/tensorflow/python/kernel_tests/functional_ops_test.py index 04c1032722..c489623fe5 100644 --- a/tensorflow/python/kernel_tests/functional_ops_test.py +++ b/tensorflow/python/kernel_tests/functional_ops_test.py @@ -56,6 +56,7 @@ def simple_scoped_fn(a, x): return math_ops.multiply(math_ops.add(a, x), two) +@test_util.with_control_flow_v2 class FunctionalOpsTest(test.TestCase): @test_util.run_in_graph_and_eager_modes @@ -100,6 +101,7 @@ class FunctionalOpsTest(test.TestCase): (elems, other_elems), initializer) self.assertAllEqual([1.0, 2.0, 3.0], self.evaluate(r)) + @test_util.run_deprecated_v1 def testFoldl_Scoped(self): with self.cached_session() as sess: with variable_scope.variable_scope("root") as varscope: @@ -152,6 +154,7 @@ class FunctionalOpsTest(test.TestCase): initializer) self.assertAllEqual(1, self.evaluate(r)) + @test_util.run_deprecated_v1 def testFoldr_Scoped(self): with self.cached_session() as sess: with variable_scope.variable_scope("root") as varscope: @@ -172,6 +175,7 @@ class FunctionalOpsTest(test.TestCase): self.assertAllEqual(1282, self.evaluate(r)) # pylint: disable=unnecessary-lambda + @test_util.run_deprecated_v1 def testFold_Grad(self): with self.cached_session(): elems = constant_op.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], name="data") @@ -213,6 +217,7 @@ class FunctionalOpsTest(test.TestCase): with self.assertRaisesRegexp(ValueError, "not a scalar"): functional_ops.map_fn(lambda x: x, 1) + @test_util.run_deprecated_v1 def testMap_Scoped(self): with self.cached_session() as sess: @@ -244,6 +249,7 @@ class FunctionalOpsTest(test.TestCase): self.assertEqual(len(variables.trainable_variables()), 1) self.assertAllEqual(doubles, self.evaluate(r)) + @test_util.run_deprecated_v1 def testMap_Grad(self): with self.cached_session(): param = constant_op.constant(2.0) @@ -380,6 +386,7 @@ class FunctionalOpsTest(test.TestCase): ValueError, "two structures don't have the same nested structure"): functional_ops.scan(lambda a, x: (a, -a), elems, initializer) + @test_util.run_deprecated_v1 def testScan_Scoped(self): with self.cached_session() as sess: with variable_scope.variable_scope("root") as varscope: @@ -424,6 +431,7 @@ class FunctionalOpsTest(test.TestCase): # t_1 == 1, b == 4.5, y == 0.5, returns b * y * x = 9 self.assertAllClose([1., 1., 2.25, 9.], self.evaluate(r)) + @test_util.run_deprecated_v1 def testScan_Control(self): with self.cached_session() as sess: s = array_ops.placeholder(dtypes.float32, shape=[None]) @@ -435,6 +443,7 @@ class FunctionalOpsTest(test.TestCase): np.array([1.0, 3.0, 9.0]), sess.run(c, {s: [1, 3, 3], b: True})) + @test_util.run_deprecated_v1 def testScan_Grad(self): with self.cached_session(): elems = constant_op.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], name="data") @@ -447,6 +456,7 @@ class FunctionalOpsTest(test.TestCase): r = gradients_impl.gradients(r, v)[0] self.assertAllEqual(873.0, self.evaluate(r)) + @test_util.run_deprecated_v1 def testScanGradientWithPartStopGradient(self): a = variables.Variable(0.0, name="a") b = variables.Variable(0.0, name="b") @@ -457,7 +467,7 @@ class FunctionalOpsTest(test.TestCase): grad = gradients_impl.gradients(ys=[loss], xs=[a, b]) with self.test_session(use_gpu=True) as sess: variables.global_variables_initializer().run() - sess.run(grad) + self.evaluate(grad) @test_util.run_in_graph_and_eager_modes def testFoldShape(self): @@ -476,12 +486,15 @@ class FunctionalOpsTest(test.TestCase): y = functional_ops.map_fn(lambda e: e, x) self.assertAllEqual(y.get_shape(), self.evaluate(y).shape) + @test_util.run_deprecated_v1 def testMapUnknownShape(self): x = array_ops.placeholder(dtypes.float32) y = functional_ops.map_fn(lambda e: e, x) self.assertIs(None, y.get_shape().dims) + @test_util.disable_control_flow_v2("b/119323354") @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def testMapEmptyScalar(self): map_return = functional_ops.map_fn(lambda x: 1, constant_op.constant([])) self.assertAllEqual([0], map_return.get_shape().dims) @@ -489,6 +502,8 @@ class FunctionalOpsTest(test.TestCase): # TODO(akshayka): this test fails in eager: the iterable is of length 0 so # so the body of the while loop never executes + @test_util.disable_control_flow_v2("b/119323354") + @test_util.run_deprecated_v1 def testMapEmptyTensor(self): with self.cached_session(): map_return = functional_ops.map_fn(lambda x: array_ops.zeros([3, 2]), @@ -509,6 +524,7 @@ class FunctionalOpsTest(test.TestCase): # TODO(akshayka): this test fails in eager: the iterable is of length 0 so # so the body of the while loop never executes + @test_util.run_deprecated_v1 def testScanEmptyTensor(self): with self.cached_session(): x = functional_ops.scan( @@ -516,6 +532,7 @@ class FunctionalOpsTest(test.TestCase): self.assertAllEqual([0, 2, 4], x.get_shape()) self.assertAllEqual(x.get_shape(), self.evaluate(x).shape) + @test_util.run_deprecated_v1 def testScanUnknownShape(self): x = array_ops.placeholder(dtypes.float32) initializer = array_ops.placeholder(dtypes.float32) @@ -526,6 +543,7 @@ class FunctionalOpsTest(test.TestCase): y = functional_ops.scan(fn, x, initializer=initializer) self.assertIs(None, y.get_shape().dims) + @test_util.run_deprecated_v1 def testScanVaryingShape(self): with self.cached_session() as sess: x = array_ops.placeholder(dtype=dtypes.float32, shape=[None, 2]) @@ -542,6 +560,7 @@ class FunctionalOpsTest(test.TestCase): sess.run([result, result_t, result_grad, result_t_grad], feed_dict={x: [[1.0, 2.0]]}) + @test_util.run_deprecated_v1 def testRemoteFunction(self): worker_config = config_pb2.ConfigProto() worker_config.device_count["CPU"] = 2 @@ -564,10 +583,11 @@ class FunctionalOpsTest(test.TestCase): target="/job:worker/replica:0/task:0/cpu:1") with session.Session(worker[0].target) as sess: - sess.run(variables.global_variables_initializer()) - mul = sess.run(remote_op) + self.evaluate(variables.global_variables_initializer()) + mul = self.evaluate(remote_op) self.assertEqual(mul, [6]) + @test_util.run_deprecated_v1 def testRemoteFunctionDirectSession(self): worker_config = config_pb2.ConfigProto() worker_config.device_count["CPU"] = 2 @@ -588,10 +608,11 @@ class FunctionalOpsTest(test.TestCase): target="/job:localhost/replica:0/task:0/cpu:1") with self.test_session(config=worker_config) as sess: - sess.run(variables.global_variables_initializer()) - mul = sess.run(remote_op) + self.evaluate(variables.global_variables_initializer()) + mul = self.evaluate(remote_op) self.assertEqual(mul, [6]) + @test_util.run_deprecated_v1 def testRemoteFunctionSameDeviceDirectSession(self): @function.Defun(dtypes.int32, dtypes.int32) @@ -607,8 +628,8 @@ class FunctionalOpsTest(test.TestCase): args=[a, b], Tout=[dtypes.int32], f=_remote_fn, target="/cpu:0") with self.cached_session() as sess: - sess.run(variables.global_variables_initializer()) - mul = sess.run(remote_op) + self.evaluate(variables.global_variables_initializer()) + mul = self.evaluate(remote_op) self.assertEqual(mul, [6]) def testRemoteFunctionCPUGPU(self): @@ -631,8 +652,8 @@ class FunctionalOpsTest(test.TestCase): target="/job:localhost/replica:0/task:0/device:GPU:0")[0] + 3.0 with self.cached_session() as sess: - sess.run(variables.global_variables_initializer()) - mul = sess.run(remote_op) + self.evaluate(variables.global_variables_initializer()) + mul = self.evaluate(remote_op) self.assertEqual(mul, 9.0) def testRemoteFunctionGPUCPU(self): @@ -655,8 +676,8 @@ class FunctionalOpsTest(test.TestCase): target="/job:localhost/replica:0/task:0/cpu:0")[0] + 3.0 with self.cached_session() as sess: - sess.run(variables.global_variables_initializer()) - mul = sess.run(remote_op) + self.evaluate(variables.global_variables_initializer()) + mul = self.evaluate(remote_op) self.assertEqual(mul, 9.0) def testRemoteFunctionGPUCPUStrings(self): @@ -674,9 +695,10 @@ class FunctionalOpsTest(test.TestCase): args=[a], Tout=[dtypes.string], f=_remote_fn, target="/cpu:0") with self.cached_session() as sess: - ret = sess.run(remote_op) + ret = self.evaluate(remote_op) self.assertAllEqual(ret, [b"a"]) + @test_util.run_deprecated_v1 def testRemoteFunctionCrossProcess(self): workers, _ = test_util.create_local_cluster(2, 1) @@ -696,10 +718,11 @@ class FunctionalOpsTest(test.TestCase): target="/job:worker/replica:0/task:1/cpu:0")[0] + 3.0 with session.Session(workers[0].target) as sess: - sess.run(variables.global_variables_initializer()) - mul = sess.run(remote_op) + self.evaluate(variables.global_variables_initializer()) + mul = self.evaluate(remote_op) self.assertEqual(mul, 9) + @test_util.run_deprecated_v1 def testIf(self): @function.Defun(dtypes.float32) @@ -739,6 +762,7 @@ class FunctionalOpsTest(test.TestCase): self.assertAllEqual(Run(sess, 20.), 210.) self.assertAllEqual(Run(sess, 100.), 5050.) + @test_util.run_deprecated_v1 def testWhileLowering(self): def Run(n, fetch_by_name): @@ -766,13 +790,14 @@ class FunctionalOpsTest(test.TestCase): else: fetch = "my_while:1" with self.session(graph=g, use_gpu=use_gpu) as sess: - return sess.run(fetch) + return self.evaluate(fetch) self.assertAllEqual(Run(20., False), 210.) self.assertAllEqual(Run(20., True), 210.) self.assertAllEqual(Run(100., False), 5050.) self.assertAllEqual(Run(100., True), 5050.) + @test_util.run_deprecated_v1 def testWhileError(self): for use_gpu in (True, False): with ops.Graph().as_default() as g: @@ -854,11 +879,11 @@ class FunctionalOpsTest(test.TestCase): result_binary = functional_ops.While( [1.0, 0., 0.], function.Defun(*[dtypes.float32] * 3)(TestCond), TestBinary) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) assert len(result_unary) == 2 - self.assertEqual([10.0, 54.0], sess.run(result_unary)) + self.assertEqual([10.0, 54.0], self.evaluate(result_unary)) assert len(result_binary) == 3 - self.assertEqual([10.0, 54.0, 9.0], sess.run(result_binary)) + self.assertEqual([10.0, 54.0, 9.0], self.evaluate(result_binary)) def TestCondCapture(n, *args): del args @@ -889,7 +914,7 @@ class FunctionalOpsTest(test.TestCase): 100, 0, -1, [0.], Body, rewrite_with_while=rewrite_with_while) [0], ] - xvals = sess.run(xs) + xvals = self.evaluate(xs) self.assertAllEqual(210, xvals[0]) self.assertAllEqual(5050, xvals[1]) @@ -919,6 +944,7 @@ class FunctionalOpsTest(test.TestCase): self.assertTrue("TestBody_Cond" in names) self.assertTrue("TestBody_Body" in names) + @test_util.run_deprecated_v1 def testForCapturedInputs(self): v = variables.Variable(1.0) @@ -946,16 +972,16 @@ class FunctionalOpsTest(test.TestCase): result_binary = functional_ops.For( 1, 10, 1, [0., 0.], TestBinary, rewrite_with_while=rewrite_with_while) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) assert not result_nullary # The nullary variant doesn't return anything so we can't easily run it. # As a total hack, fetch the operation by name and run it. sess.run(ops.get_default_graph().get_operation_by_name( "While" if rewrite_with_while else "For")) assert len(result_unary) == 1 - self.assertEqual([54.0], sess.run(result_unary)) + self.assertEqual([54.0], self.evaluate(result_unary)) assert len(result_binary) == 2 - self.assertEqual([54.0, 9.0], sess.run(result_binary)) + self.assertEqual([54.0, 9.0], self.evaluate(result_binary)) def _tfMLP(self, xval, wsval, bsval, rewrite_with_while): # On GPU, don't rewrite using a while loop. @@ -974,7 +1000,7 @@ class FunctionalOpsTest(test.TestCase): MLP, rewrite_with_while=rewrite_with_while)[0] - return ret.eval() + return self.evaluate(ret) def _npMLP(self, xval, wsval, bsval): for i in range(wsval.shape[0]): @@ -993,12 +1019,15 @@ class FunctionalOpsTest(test.TestCase): tf_for_ans = self._tfMLP(xval, wsval, bsval, rewrite_with_while) self.assertAllClose(np_ans, tf_for_ans) + @test_util.run_deprecated_v1 def testForMLP(self): self._testForMLP(False) + @test_util.run_deprecated_v1 def testForMLPWhile(self): self._testForMLP(True) + @test_util.run_deprecated_v1 def testForError(self): @function.Defun(dtypes.int32, dtypes.float32) @@ -1021,6 +1050,7 @@ class FunctionalOpsTest(test.TestCase): "For loop body returned 2 arguments. Expected: 1"): functional_ops.For(0, 10, 1, [0.0], ReturnsTooManyArgs)[0].eval() + @test_util.run_deprecated_v1 def testGradient(self): @function.Defun(dtypes.float32) @@ -1038,14 +1068,15 @@ class FunctionalOpsTest(test.TestCase): avals = [Poly(a), Grad(a)] b = constant_op.constant(1.) bvals = [Poly(b), Grad(b)] - self.assertAllEqual(sess.run(avals), [8., 4.]) - self.assertAllEqual(sess.run(bvals), [17., 16.]) + self.assertAllEqual(self.evaluate(avals), [8., 4.]) + self.assertAllEqual(self.evaluate(bvals), [17., 16.]) # TODO(akshayka): Replace `function.Defun` with tf.contrib.eager.defun` in the # below test cases. class PartitionedCallTest(test.TestCase): + @test_util.run_deprecated_v1 def testBasicSingleDevice(self): @function.Defun(*[dtypes.float32] * 2) @@ -1061,6 +1092,7 @@ class PartitionedCallTest(test.TestCase): constant_op.constant(2.)], f=Body)) self.assertEqual(output, 6.) + @test_util.run_deprecated_v1 def testBasicMultiDevice(self): config = config_pb2.ConfigProto(device_count={"CPU": 3}) @@ -1104,6 +1136,7 @@ class PartitionedCallTest(test.TestCase): constant_op.constant(2.)], f=Body)) self.assertEqual(output, 6.) + @test_util.run_deprecated_v1 def testBasicNoDeviceAnnotations(self): @function.Defun(*[dtypes.float32] * 2) @@ -1118,6 +1151,7 @@ class PartitionedCallTest(test.TestCase): constant_op.constant(2.)], f=Body)) self.assertEqual(output, 6.) + @test_util.run_deprecated_v1 def testShardsRunOnRequestedDevices(self): config = config_pb2.ConfigProto(device_count={"CPU": 4}) @@ -1147,6 +1181,7 @@ class PartitionedCallTest(test.TestCase): self.assertIn(compat.as_bytes("CPU:1"), outputs[1]) self.assertIn(compat.as_bytes("CPU:2"), outputs[2]) + @test_util.run_deprecated_v1 def testAssignAddResourceVariable(self): v = resource_variable_ops.ResourceVariable(1.0) @@ -1190,14 +1225,15 @@ class PartitionedCallTest(test.TestCase): allow_soft_placement=False, log_device_placement=True, device_count={"CPU": 2})) as sess: - sess.run(variables.global_variables_initializer()) - expected = sess.run(sum_gather()) + self.evaluate(variables.global_variables_initializer()) + expected = self.evaluate(sum_gather()) result = sess.run( functional_ops.partitioned_call( args=defined.captured_inputs, f=defined)) self.assertAllEqual(expected, result) # Use an invalid executor name to test the plumbing of the executor_type attr. + @test_util.run_deprecated_v1 def testExecutorTypeAttrExecutorNotFound(self): @function.Defun(dtypes.int32) def AddFive(x): diff --git a/tensorflow/python/kernel_tests/gather_nd_op_test.py b/tensorflow/python/kernel_tests/gather_nd_op_test.py index ee761435d8..320ffc9674 100644 --- a/tensorflow/python/kernel_tests/gather_nd_op_test.py +++ b/tensorflow/python/kernel_tests/gather_nd_op_test.py @@ -27,6 +27,7 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradients_impl from tensorflow.python.ops import variables @@ -40,7 +41,7 @@ class GatherNdTest(test.TestCase): params = constant_op.constant(np.array([8, 1, 2, 3, 7, 5], dtype=dtype)) indices = constant_op.constant([[4], [4], [0]]) gather_nd_t = array_ops.gather_nd(params, indices) - gather_nd_val = gather_nd_t.eval() + gather_nd_val = self.evaluate(gather_nd_t) self.assertAllEqual(np.array([7, 7, 8], dtype=dtype), gather_nd_val) self.assertEqual([3], gather_nd_t.get_shape()) @@ -54,26 +55,27 @@ class GatherNdTest(test.TestCase): self._testSimpleDtype(np.complex128) self._testSimpleDtype("|S") # byte strings in python2 + 3 + @test_util.run_deprecated_v1 def testEmptyIndicesAndParamsOKButJustEmptyParamsFails(self): with self.session(use_gpu=True): params = np.ones((3, 3), dtype=np.float32) indices_empty = np.empty((0, 2), dtype=np.int32) gather_nd_ok_t = array_ops.gather_nd(params, indices_empty) - gather_nd_ok_val = gather_nd_ok_t.eval() + gather_nd_ok_val = self.evaluate(gather_nd_ok_t) self.assertEqual([0], gather_nd_ok_t.get_shape()) self.assertAllClose(np.empty((0,), dtype=np.float32), gather_nd_ok_val) indices_empty = np.empty((0, 1), dtype=np.int32) gather_nd_ok_t = array_ops.gather_nd(params, indices_empty) - gather_nd_ok_val = gather_nd_ok_t.eval() + gather_nd_ok_val = self.evaluate(gather_nd_ok_t) self.assertEqual([0, 3], gather_nd_ok_t.get_shape()) self.assertAllClose(np.empty((0, 3), dtype=np.float32), gather_nd_ok_val) params_empty = np.empty((0, 3), dtype=np.float32) indices_empty = np.empty((0, 2), dtype=np.int32) gather_nd_ok_t = array_ops.gather_nd(params_empty, indices_empty) - gather_nd_ok_val = gather_nd_ok_t.eval() + gather_nd_ok_val = self.evaluate(gather_nd_ok_t) self.assertEqual([0], gather_nd_ok_t.get_shape()) self.assertAllClose(np.empty((0,), dtype=np.float32), gather_nd_ok_val) @@ -82,7 +84,7 @@ class GatherNdTest(test.TestCase): gather_nd_break_t = array_ops.gather_nd(params_empty, indices_nonempty) with self.assertRaisesOpError( r"Requested more than 0 entries, but params is empty."): - gather_nd_break_t.eval() + self.evaluate(gather_nd_break_t) self.assertAllClose(np.empty((0,), dtype=np.float32), gather_nd_ok_val) def testIndexScalar(self): @@ -91,7 +93,7 @@ class GatherNdTest(test.TestCase): [[-8, -1, -2, -3, -7, -5], [8, 1, 2, 3, 7, 5]], dtype=np.float32).T indices = constant_op.constant([4, 1]) gather_nd_t = array_ops.gather_nd(params, indices) - gather_nd_val = gather_nd_t.eval() + gather_nd_val = self.evaluate(gather_nd_t) self.assertEqual([], gather_nd_t.get_shape()) self.assertAllEqual(np.array(7), gather_nd_val) @@ -101,7 +103,7 @@ class GatherNdTest(test.TestCase): [[-8, -1, -2, -3, -7, -5], [8, 1, 2, 3, 7, 5]], dtype=np.float32).T indices = constant_op.constant([4]) gather_nd_t = array_ops.gather_nd(params, indices) - gather_nd_val = gather_nd_t.eval() + gather_nd_val = self.evaluate(gather_nd_t) self.assertEqual([2], gather_nd_t.get_shape()) self.assertAllEqual(np.array([-7, 7]), gather_nd_val) @@ -111,7 +113,7 @@ class GatherNdTest(test.TestCase): [[-8, -1, -2, -3, -7, -5], [8, 1, 2, 3, 7, 5]], dtype=np.float32).T indices = constant_op.constant([[4], [4], [0]]) gather_nd_t = array_ops.gather_nd(params, indices) - gather_nd_val = gather_nd_t.eval() + gather_nd_val = self.evaluate(gather_nd_t) self.assertEqual([3, 2], gather_nd_t.get_shape()) self.assertAllEqual(np.array([[-7, 7], [-7, 7], [-8, 8]]), gather_nd_val) @@ -125,7 +127,7 @@ class GatherNdTest(test.TestCase): params_t = constant_op.constant(params) indices = constant_op.constant([[4], [4], [0]]) gather_nd_t = array_ops.gather_nd(params_t, indices) - gather_nd_val = gather_nd_t.eval() + gather_nd_val = self.evaluate(gather_nd_t) self.assertEqual([3, 2, 2], gather_nd_t.get_shape()) self.assertAllEqual(params[[4, 4, 0]], gather_nd_val) @@ -140,7 +142,7 @@ class GatherNdTest(test.TestCase): indices = constant_op.constant( [[], []], dtype=dtypes.int32) # Size (2, 0) gather_nd_t = array_ops.gather_nd(params_t, indices) - gather_nd_val = gather_nd_t.eval() + gather_nd_val = self.evaluate(gather_nd_t) self.assertEqual([2, 6, 2, 2], gather_nd_t.get_shape()) self.assertAllEqual( @@ -156,7 +158,7 @@ class GatherNdTest(test.TestCase): params_t = constant_op.constant(params) indices = constant_op.constant([[[3], [2], [1]], [[4], [4], [0]]]) gather_nd_t = array_ops.gather_nd(params_t, indices) - gather_nd_val = gather_nd_t.eval() + gather_nd_val = self.evaluate(gather_nd_t) self.assertEqual([2, 3, 2, 2], gather_nd_t.get_shape()) self.assertAllEqual(params[[3, 2, 1, 4, 4, 0]].reshape(2, 3, 2, 2), @@ -168,7 +170,7 @@ class GatherNdTest(test.TestCase): params = np.random.rand(*shape) indices = np.vstack([np.random.randint(0, s, size=2000) for s in shape]).T gather_nd_t = array_ops.gather_nd(params, indices) - gather_nd_val = gather_nd_t.eval() + gather_nd_val = self.evaluate(gather_nd_t) expected = params[tuple(indices.T)] self.assertAllEqual(expected, gather_nd_val) @@ -181,7 +183,7 @@ class GatherNdTest(test.TestCase): indices = np.vstack([np.random.randint(0, s, size=2000) for s in shape]).T indices_reshaped = indices.reshape([10, 10, 20, 5]) gather_nd_t = array_ops.gather_nd(params, indices_reshaped) - gather_nd_val = gather_nd_t.eval() + gather_nd_val = self.evaluate(gather_nd_t) expected = params[tuple(indices.T)] self.assertAllEqual(expected.reshape([10, 10, 20]), gather_nd_val) @@ -190,6 +192,7 @@ class GatherNdTest(test.TestCase): def assertIndexedSlices(self, t): self.assertIsInstance(t, ops.IndexedSlices) + @test_util.run_deprecated_v1 def testUnknownIndices(self): params = constant_op.constant([[0, 1, 2]]) indices = array_ops.placeholder(dtypes.int32) @@ -198,6 +201,7 @@ class GatherNdTest(test.TestCase): self.assertEqual(None, shape.ndims) self.assertEqual(None, tensor_shape.dimension_value(shape[0])) + @test_util.run_deprecated_v1 def testBadIndicesCPU(self): with self.session(use_gpu=False): params = [0, 1, 2] @@ -205,7 +209,7 @@ class GatherNdTest(test.TestCase): gather_nd = array_ops.gather_nd(params, indices) with self.assertRaisesOpError( r"indices\[0,1\] = \[7\] does not index into param shape \[3\]"): - gather_nd.eval() + self.evaluate(gather_nd) def _disabledTestBadIndicesGPU(self): # TODO disabled due to different behavior on GPU and CPU @@ -218,8 +222,9 @@ class GatherNdTest(test.TestCase): gather_nd = array_ops.gather_nd(params, indices) with self.assertRaisesOpError( r"indices\[0,1\] = \[7\] does not index into param shape \[3\]"): - gather_nd.eval() + self.evaluate(gather_nd) + @test_util.run_deprecated_v1 def testBadIndicesWithSlicesCPU(self): with self.session(use_gpu=False): params = [[0, 1, 2]] @@ -227,7 +232,7 @@ class GatherNdTest(test.TestCase): gather_nd = array_ops.gather_nd(params, indices) with self.assertRaisesOpError( r"indices\[0,2\] = \[1\] does not index into param shape \[1,3\]"): - gather_nd.eval() + self.evaluate(gather_nd) def _disabledTestBadIndicesWithSlicesGPU(self): # TODO disabled due to different behavior on GPU and CPU @@ -240,8 +245,9 @@ class GatherNdTest(test.TestCase): gather_nd = array_ops.gather_nd(params, indices) with self.assertRaisesOpError( r"indices\[0,2\] = \[1\] does not index into param shape \[1,3\]"): - gather_nd.eval() + self.evaluate(gather_nd) + @test_util.run_deprecated_v1 def testGradientsRank2Elements(self): indices = constant_op.constant([[0, 0], [1, 1]], dtype=dtypes.int32) inputs = constant_op.constant([[1, 2], [3, 4]], dtype=dtypes.float64) @@ -251,8 +257,9 @@ class GatherNdTest(test.TestCase): grads = gradients_impl.gradients([outputs], [inputs], [grad_vals])[0] expected_grads = np.array([[1, 0], [0, 2]], dtype=np.float64) with self.session(use_gpu=True): - assert np.array_equal(expected_grads, grads.eval()) + assert np.array_equal(expected_grads, self.evaluate(grads)) + @test_util.run_deprecated_v1 def testGradientsRank2Slices(self): indices = constant_op.constant([[1], [0]], dtype=dtypes.int32) inputs = constant_op.constant([[1, 2], [3, 4]], dtype=dtypes.float64) @@ -265,6 +272,7 @@ class GatherNdTest(test.TestCase): self.assertIndexedSlices(grads) self.assertAllEqual(expected_grads, ops.convert_to_tensor(grads).eval()) + @test_util.run_deprecated_v1 def testGradientsRank3Elements(self): indices = constant_op.constant( [[[0, 1], [1, 0]], [[0, 0], [1, 1]]], dtype=dtypes.int32) @@ -278,8 +286,9 @@ class GatherNdTest(test.TestCase): expected_grads = np.array( [[[5, 6], [1, 2]], [[3, 4], [7, 8]]], dtype=np.float64) with self.session(use_gpu=True): - self.assertAllEqual(expected_grads, grads.eval()) + self.assertAllEqual(expected_grads, self.evaluate(grads)) + @test_util.run_deprecated_v1 def testGradientsRank7Elements(self): # Shape [1,1,2,1,1,2,2] indices = constant_op.constant( @@ -307,8 +316,9 @@ class GatherNdTest(test.TestCase): [[[[3, 4], [7, 8]]]] ]]], dtype=np.float64) with self.session(use_gpu=True): - self.assertAllEqual(expected_grads, grads.eval()) + self.assertAllEqual(expected_grads, self.evaluate(grads)) + @test_util.run_deprecated_v1 def testGradientsInt64Indices(self): indices = constant_op.constant( [[[0, 1], [1, 0]], [[0, 0], [1, 1]]], dtype=dtypes.int64) @@ -322,8 +332,9 @@ class GatherNdTest(test.TestCase): expected_grads = np.array( [[[5, 6], [1, 2]], [[3, 4], [7, 8]]], dtype=np.float64) with self.session(use_gpu=True): - self.assertAllEqual(expected_grads, grads.eval()) + self.assertAllEqual(expected_grads, self.evaluate(grads)) + @test_util.run_deprecated_v1 def testGradientsRank2SlicesWithEmptySpace(self): indices = constant_op.constant([[2], [0], [5]], dtype=dtypes.int32) inputs = constant_op.constant( @@ -361,10 +372,10 @@ class GatherNdOpBenchmark(test.Benchmark): gather_op = array_ops.gather_nd(t_params, t_indices) variables.global_variables_initializer().run() for _ in range(10): - gather_op.eval() + self.evaluate(gather_op) t1 = time.time() for _ in range(1000): - gather_op.eval() + self.evaluate(gather_op) t2 = time.time() self.report_benchmark(iters=1000, wall_time=(t2 - t1) / 1000.0) diff --git a/tensorflow/python/kernel_tests/gather_op_test.py b/tensorflow/python/kernel_tests/gather_op_test.py index bdafc52ab5..fc86068c3f 100644 --- a/tensorflow/python/kernel_tests/gather_op_test.py +++ b/tensorflow/python/kernel_tests/gather_op_test.py @@ -23,6 +23,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradients_impl from tensorflow.python.platform import test @@ -50,7 +51,7 @@ class GatherTest(test.TestCase): params = constant_op.constant(params_np) indices_tf = constant_op.constant(indices) gather_t = array_ops.gather(params, indices_tf) - gather_val = gather_t.eval() + gather_val = self.evaluate(gather_t) np_val = params_np[indices] self.assertAllEqual(np_val, gather_val) self.assertEqual(np_val.shape, gather_t.get_shape()) @@ -65,7 +66,7 @@ class GatherTest(test.TestCase): params = constant_op.constant(params_np) indices = constant_op.constant(2) gather_t = array_ops.gather(params, indices, axis=axis) - gather_val = gather_t.eval() + gather_val = self.evaluate(gather_t) self.assertAllEqual(np.take(params_np, 2, axis=axis), gather_val) expected_shape = data.shape[:axis] + data.shape[axis + 1:] self.assertEqual(expected_shape, gather_t.get_shape()) @@ -81,12 +82,13 @@ class GatherTest(test.TestCase): # The indices must be in bounds for any axis. indices = constant_op.constant([0, 1, 0, 2]) gather_t = array_ops.gather(params, indices, axis=axis) - gather_val = gather_t.eval() + gather_val = self.evaluate(gather_t) self.assertAllEqual(np.take(params_np, [0, 1, 0, 2], axis=axis), gather_val) expected_shape = data.shape[:axis] + (4,) + data.shape[axis + 1:] self.assertEqual(expected_shape, gather_t.get_shape()) + @test_util.run_deprecated_v1 def testHigherRank(self): # We check that scalar and empty indices shapes work as well shape = (2, 1, 3, 2) @@ -142,9 +144,13 @@ class GatherTest(test.TestCase): source_slice = ((slice(None),) * outer_dims + (source_index,) + (slice(None),) * inner_dims) correct_params_grad[dest_slice] += gather_grad[source_slice] - self.assertAllClose(correct_params_grad, params_grad.eval(), - atol=2e-6, rtol=2e-6) + self.assertAllClose( + correct_params_grad, + self.evaluate(params_grad), + atol=2e-6, + rtol=2e-6) + @test_util.run_deprecated_v1 def testString(self): params = np.array([[b"asdf", b"zxcv"], [b"qwer", b"uiop"]]) with self.cached_session(): @@ -153,6 +159,7 @@ class GatherTest(test.TestCase): self.assertAllEqual([b"asdf", b"qwer"], array_ops.gather(params, 0, axis=1).eval()) + @test_util.run_deprecated_v1 def testUInt32AndUInt64(self): for unsigned_type in (dtypes.uint32, dtypes.uint64): params = self._buildParams( @@ -162,12 +169,14 @@ class GatherTest(test.TestCase): array_ops.gather(params, 1, axis=0).eval()) self.assertAllEqual([1, 7], array_ops.gather(params, 0, axis=1).eval()) + @test_util.run_deprecated_v1 def testUnknownIndices(self): params = constant_op.constant([[0, 1, 2]]) indices = array_ops.placeholder(dtypes.int32) gather_t = array_ops.gather(params, indices) self.assertEqual(None, gather_t.get_shape()) + @test_util.run_deprecated_v1 def testUnknownAxis(self): params = constant_op.constant([[0, 1, 2]]) indices = constant_op.constant([[0, 0], [0, 0]]) @@ -201,6 +210,7 @@ class GatherTest(test.TestCase): with self.assertRaisesOpError(r"indices\[0,0\] = 7 is not in \[0, 3\)"): array_ops.gather(params, [[7]], axis=1).eval() + @test_util.run_deprecated_v1 def testBadAxis(self): with self.session(use_gpu=True): params = [0, 1, 2] @@ -217,6 +227,7 @@ class GatherTest(test.TestCase): array_ops.gather(params_ph, indices, axis=bad_axis).eval( feed_dict={params_ph: params}) + @test_util.run_deprecated_v1 def testEmptySlices(self): with self.session(use_gpu=True): for dtype in _TEST_TYPES: diff --git a/tensorflow/python/kernel_tests/gradient_correctness_test.py b/tensorflow/python/kernel_tests/gradient_correctness_test.py index 291a69ebac..0148de5047 100644 --- a/tensorflow/python/kernel_tests/gradient_correctness_test.py +++ b/tensorflow/python/kernel_tests/gradient_correctness_test.py @@ -22,6 +22,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import gradients_impl from tensorflow.python.ops import math_ops from tensorflow.python.platform import test @@ -29,37 +30,42 @@ from tensorflow.python.platform import test class GradientCorrectnessTest(test.TestCase): + @test_util.run_deprecated_v1 def testMultipleOutputChainedGradients(self): with self.cached_session() as sess: x = constant_op.constant(1.0, dtype=dtypes.float32) yexp = math_ops.exp(x) yexplog = math_ops.log(yexp) grads = gradients_impl.gradients([yexp, yexplog], [x]) - grad_vals = sess.run(grads) + grad_vals = self.evaluate(grads) exp1_plus_one = (1.0 + np.exp(1.0)).astype(np.float32) # [dexp(x)/dx + d(log(exp(x)))/dx] @ x=1 == exp(1) + 1 self.assertAllClose(grad_vals[0], exp1_plus_one) + @test_util.run_deprecated_v1 def testIdentityGradient(self): x = constant_op.constant(3.) dx_dx, = gradients_impl.gradients(x, x) with self.cached_session() as sess: - self.assertAllClose(1., sess.run(dx_dx)) + self.assertAllClose(1., self.evaluate(dx_dx)) + @test_util.run_deprecated_v1 def testIntegerIdentityGradient(self): x = constant_op.constant(3) dx_dx, = gradients_impl.gradients(x, x) with self.cached_session() as sess: - self.assertAllClose(1, sess.run(dx_dx)) + self.assertAllClose(1, self.evaluate(dx_dx)) + @test_util.run_deprecated_v1 def testGradientWithIntegerPath(self): x = constant_op.constant([3.9, 4.1]) k = math_ops.to_float(math_ops.to_int32(x)) y = x * k dy_dx, = gradients_impl.gradients(y, x) with self.cached_session() as sess: - self.assertAllClose([3., 4.], sess.run(dy_dx)) + self.assertAllClose([3., 4.], self.evaluate(dy_dx)) + @test_util.run_deprecated_v1 def testNoIntegerGradient1(self): x = constant_op.constant([3.9, 4.1]) k = math_ops.to_float(math_ops.to_int32(x)) @@ -67,6 +73,7 @@ class GradientCorrectnessTest(test.TestCase): dy_dx, = gradients_impl.gradients(y, x) self.assertIsNone(dy_dx) + @test_util.run_deprecated_v1 def testNoIntegerGradient2(self): k = constant_op.constant([3, 4]) x = math_ops.to_float(k) @@ -74,18 +81,21 @@ class GradientCorrectnessTest(test.TestCase): dy_dk, = gradients_impl.gradients(y, k) self.assertIsNone(dy_dk) + @test_util.run_deprecated_v1 def testNoIntegerGradient3(self): k = constant_op.constant([3, 4]) m = k * k dm_dk, = gradients_impl.gradients(m, k) self.assertIsNone(dm_dk) + @test_util.run_deprecated_v1 def testNoIntegerGradient4(self): k = constant_op.constant([3, 4]) m = k * k * k dm_dk, = gradients_impl.gradients(m, k) self.assertIsNone(dm_dk) + @test_util.run_deprecated_v1 def testNoIntegerGradient5(self): k = constant_op.constant([3, 4]) m = k * k @@ -93,6 +103,7 @@ class GradientCorrectnessTest(test.TestCase): dn_dk, = gradients_impl.gradients(n, k) self.assertIsNone(dn_dk) + @test_util.run_deprecated_v1 def testNoIntegerGradient6(self): k = constant_op.constant(3) x = math_ops.to_float(k) diff --git a/tensorflow/python/kernel_tests/huge_slice_op_test.py b/tensorflow/python/kernel_tests/huge_slice_op_test.py index 8646d74c96..4074946350 100644 --- a/tensorflow/python/kernel_tests/huge_slice_op_test.py +++ b/tensorflow/python/kernel_tests/huge_slice_op_test.py @@ -33,11 +33,11 @@ class SliceTest(test.TestCase): a_large = array_ops.tile( constant_op.constant(np.array([False, True] * 4)), [2**29 + 3]) slice_t = array_ops.slice(a_large, np.asarray([3]).astype(np.int64), [3]) - slice_val = slice_t.eval() + slice_val = self.evaluate(slice_t) self.assertAllEqual([True, False, True], slice_val) slice_t = array_ops.slice( a_large, constant_op.constant([long(2)**32 + 3], dtype=dtypes.int64), [3]) - slice_val = slice_t.eval() + slice_val = self.evaluate(slice_t) self.assertAllEqual([True, False, True], slice_val) diff --git a/tensorflow/python/kernel_tests/identity_n_op_py_test.py b/tensorflow/python/kernel_tests/identity_n_op_py_test.py index 518733cd8e..a1110d640f 100644 --- a/tensorflow/python/kernel_tests/identity_n_op_py_test.py +++ b/tensorflow/python/kernel_tests/identity_n_op_py_test.py @@ -21,12 +21,14 @@ from __future__ import print_function import numpy as np from tensorflow.python.framework import constant_op +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.platform import test class IdentityNOpTest(test.TestCase): + @test_util.run_deprecated_v1 def testInt32String_6(self): with self.cached_session() as sess: [value0, value1] = sess.run( @@ -36,6 +38,7 @@ class IdentityNOpTest(test.TestCase): self.assertAllEqual( np.array([b"a", b"b", b"C", b"d", b"E", b"f", b"g"]), value1) + @test_util.run_deprecated_v1 def testInt32_shapes(self): with self.cached_session() as sess: inp0 = constant_op.constant([10, 20, 30, 40, 50, 60], shape=[2, 3]) @@ -50,6 +53,7 @@ class IdentityNOpTest(test.TestCase): np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12], [13, 14, 15]]), value2) + @test_util.run_deprecated_v1 def testString(self): source = [b"A", b"b", b"C", b"d", b"E", b"f"] with self.cached_session() as sess: diff --git a/tensorflow/python/kernel_tests/identity_op_py_test.py b/tensorflow/python/kernel_tests/identity_op_py_test.py index 88ea10c22a..1a6794e896 100644 --- a/tensorflow/python/kernel_tests/identity_op_py_test.py +++ b/tensorflow/python/kernel_tests/identity_op_py_test.py @@ -22,6 +22,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gen_array_ops from tensorflow.python.ops import variables @@ -30,17 +31,20 @@ from tensorflow.python.platform import test class IdentityOpTest(test.TestCase): + @test_util.run_deprecated_v1 def testInt32_6(self): with self.cached_session(): value = array_ops.identity([1, 2, 3, 4, 5, 6]).eval() self.assertAllEqual(np.array([1, 2, 3, 4, 5, 6]), value) + @test_util.run_deprecated_v1 def testInt32_2_3(self): with self.cached_session(): inp = constant_op.constant([10, 20, 30, 40, 50, 60], shape=[2, 3]) value = array_ops.identity(inp).eval() self.assertAllEqual(np.array([[10, 20, 30], [40, 50, 60]]), value) + @test_util.run_deprecated_v1 def testString(self): source = [b"A", b"b", b"C", b"d", b"E", b"f"] with self.cached_session(): @@ -58,6 +62,7 @@ class IdentityOpTest(test.TestCase): self.assertEquals(shape, array_ops.identity(np.array(array_2x3)).get_shape()) + @test_util.run_deprecated_v1 def testRefIdentityShape(self): with self.cached_session(): shape = [2, 3] diff --git a/tensorflow/python/kernel_tests/in_topk_op_test.py b/tensorflow/python/kernel_tests/in_topk_op_test.py index 6fdb497bc6..507822b314 100644 --- a/tensorflow/python/kernel_tests/in_topk_op_test.py +++ b/tensorflow/python/kernel_tests/in_topk_op_test.py @@ -32,7 +32,7 @@ class InTopKTest(test.TestCase): np_ans = np.array(expected) with self.cached_session(): precision = nn_ops.in_top_k(predictions, target, k) - out = precision.eval() + out = self.evaluate(precision) self.assertAllClose(np_ans, out) self.assertShapeEqual(np_ans, precision) @@ -77,7 +77,7 @@ class InTopKTest(test.TestCase): np_ans = np.array([False, True]) with self.cached_session(): precision = nn_ops.in_top_k(predictions, target, k) - out = precision.eval() + out = self.evaluate(precision) self.assertAllClose(np_ans, out) self.assertShapeEqual(np_ans, precision) diff --git a/tensorflow/python/kernel_tests/init_ops_test.py b/tensorflow/python/kernel_tests/init_ops_test.py index 70bfbf8544..09b9944baa 100644 --- a/tensorflow/python/kernel_tests/init_ops_test.py +++ b/tensorflow/python/kernel_tests/init_ops_test.py @@ -25,6 +25,7 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import random_seed +from tensorflow.python.framework import test_util from tensorflow.python.layers import convolutional from tensorflow.python.ops import array_ops from tensorflow.python.ops import init_ops @@ -106,6 +107,7 @@ def _init_sampler(tc, init, num): class ConstantInitializersTest(test.TestCase): + @test_util.run_deprecated_v1 def testZerosInitializer(self): with self.session(use_gpu=True): shape = [2, 3] @@ -114,6 +116,7 @@ class ConstantInitializersTest(test.TestCase): x.initializer.run() self.assertAllEqual(x.eval(), np.zeros(shape)) + @test_util.run_deprecated_v1 def testOnesInitializer(self): with self.session(use_gpu=True): shape = [2, 3] @@ -122,6 +125,7 @@ class ConstantInitializersTest(test.TestCase): x.initializer.run() self.assertAllEqual(x.eval(), np.ones(shape)) + @test_util.run_deprecated_v1 def testConstantZeroInitializer(self): with self.session(use_gpu=True): shape = [2, 3] @@ -130,6 +134,7 @@ class ConstantInitializersTest(test.TestCase): x.initializer.run() self.assertAllEqual(x.eval(), np.zeros(shape)) + @test_util.run_deprecated_v1 def testConstantOneInitializer(self): with self.session(use_gpu=True): shape = [2, 3] @@ -138,6 +143,7 @@ class ConstantInitializersTest(test.TestCase): x.initializer.run() self.assertAllEqual(x.eval(), np.ones(shape)) + @test_util.run_deprecated_v1 def testConstantIntInitializer(self): with self.session(use_gpu=True): shape = [2, 3] @@ -150,6 +156,7 @@ class ConstantInitializersTest(test.TestCase): self.assertEqual(x.dtype.base_dtype, dtypes.int32) self.assertAllEqual(x.eval(), 7 * np.ones(shape, dtype=np.int32)) + @test_util.run_deprecated_v1 def testConstantTupleInitializer(self): with self.session(use_gpu=True): shape = [3] @@ -173,6 +180,7 @@ class ConstantInitializersTest(test.TestCase): for a, e in zip(actual, expected): self.assertEqual(a, e) + @test_util.run_deprecated_v1 def testNDimConstantInitializer(self): value = [0, 1, 2, 3, 4, 5] shape = [2, 3] @@ -199,6 +207,7 @@ class ConstantInitializersTest(test.TestCase): e = expected[i] if i < len(expected) else expected[-1] self.assertEqual(a, e) + @test_util.run_deprecated_v1 def testNDimConstantInitializerLessValues(self): value = [0, 1, 2, 3, 4, 5] shape = [2, 4] @@ -222,6 +231,7 @@ class ConstantInitializersTest(test.TestCase): shape=shape, initializer=init) + @test_util.run_deprecated_v1 def testNDimConstantInitializerMoreValues(self): value = [0, 1, 2, 3, 4, 5, 6, 7] shape = [2, 3] @@ -243,18 +253,21 @@ class ConstantInitializersTest(test.TestCase): class RandomNormalInitializationTest(test.TestCase): + @test_util.run_deprecated_v1 def testInitializerIdentical(self): for dtype in [dtypes.float32, dtypes.float64]: init1 = init_ops.random_normal_initializer(0.0, 1.0, seed=1, dtype=dtype) init2 = init_ops.random_normal_initializer(0.0, 1.0, seed=1, dtype=dtype) self.assertTrue(identicaltest(self, init1, init2)) + @test_util.run_deprecated_v1 def testInitializerDifferent(self): for dtype in [dtypes.float32, dtypes.float64]: init1 = init_ops.random_normal_initializer(0.0, 1.0, seed=1, dtype=dtype) init2 = init_ops.random_normal_initializer(0.0, 1.0, seed=2, dtype=dtype) self.assertFalse(identicaltest(self, init1, init2)) + @test_util.run_deprecated_v1 def testDuplicatedInitializer(self): init = init_ops.random_normal_initializer(0.0, 1.0) self.assertFalse(duplicated_initializer(self, init, 1)) @@ -270,6 +283,7 @@ class RandomNormalInitializationTest(test.TestCase): class TruncatedNormalInitializationTest(test.TestCase): + @test_util.run_deprecated_v1 def testInitializerIdentical(self): for dtype in [dtypes.float32, dtypes.float64]: init1 = init_ops.truncated_normal_initializer( @@ -278,6 +292,7 @@ class TruncatedNormalInitializationTest(test.TestCase): 0.0, 1.0, seed=1, dtype=dtype) self.assertTrue(identicaltest(self, init1, init2)) + @test_util.run_deprecated_v1 def testInitializerDifferent(self): for dtype in [dtypes.float32, dtypes.float64]: init1 = init_ops.truncated_normal_initializer( @@ -286,6 +301,7 @@ class TruncatedNormalInitializationTest(test.TestCase): 0.0, 1.0, seed=2, dtype=dtype) self.assertFalse(identicaltest(self, init1, init2)) + @test_util.run_deprecated_v1 def testDuplicatedInitializer(self): init = init_ops.truncated_normal_initializer(0.0, 1.0) self.assertFalse(duplicated_initializer(self, init, 1)) @@ -301,18 +317,21 @@ class TruncatedNormalInitializationTest(test.TestCase): class RandomUniformInitializationTest(test.TestCase): + @test_util.run_deprecated_v1 def testInitializerIdentical(self): for dtype in [dtypes.float32, dtypes.float64, dtypes.int64]: init1 = init_ops.random_uniform_initializer(0, 7, seed=1, dtype=dtype) init2 = init_ops.random_uniform_initializer(0, 7, seed=1, dtype=dtype) self.assertTrue(identicaltest(self, init1, init2)) + @test_util.run_deprecated_v1 def testInitializerDifferent(self): for dtype in [dtypes.float32, dtypes.float64, dtypes.int32, dtypes.int64]: init1 = init_ops.random_uniform_initializer(0, 7, seed=1, dtype=dtype) init2 = init_ops.random_uniform_initializer(0, 7, seed=2, dtype=dtype) self.assertFalse(identicaltest(self, init1, init2)) + @test_util.run_deprecated_v1 def testDuplicatedInitializer(self): init = init_ops.random_uniform_initializer(0.0, 1.0) self.assertFalse(duplicated_initializer(self, init, 1)) @@ -320,6 +339,7 @@ class RandomUniformInitializationTest(test.TestCase): class UniformUnitScalingInitializationTest(test.TestCase): + @test_util.run_deprecated_v1 def testInitializerIdentical(self): for dtype in [dtypes.float32, dtypes.float64]: init1 = init_ops.uniform_unit_scaling_initializer(seed=1, dtype=dtype) @@ -331,6 +351,7 @@ class UniformUnitScalingInitializationTest(test.TestCase): 1.5, seed=1, dtype=dtype) self.assertTrue(identicaltest(self, init3, init4)) + @test_util.run_deprecated_v1 def testInitializerDifferent(self): for dtype in [dtypes.float32, dtypes.float64]: init1 = init_ops.uniform_unit_scaling_initializer(seed=1, dtype=dtype) @@ -341,6 +362,7 @@ class UniformUnitScalingInitializationTest(test.TestCase): self.assertFalse(identicaltest(self, init1, init3)) self.assertFalse(identicaltest(self, init2, init3)) + @test_util.run_deprecated_v1 def testZeroSize(self): shape = [0, 2] with self.cached_session(): @@ -349,8 +371,9 @@ class UniformUnitScalingInitializationTest(test.TestCase): shape=shape, initializer=init_ops.uniform_unit_scaling_initializer()) variables.global_variables_initializer().run() - self.assertAllEqual(shape, x.eval().shape) + self.assertAllEqual(shape, self.evaluate(x).shape) + @test_util.run_deprecated_v1 def testDuplicatedInitializer(self): init = init_ops.uniform_unit_scaling_initializer() self.assertFalse(duplicated_initializer(self, init, 1)) @@ -364,6 +387,7 @@ class UniformUnitScalingInitializationTest(test.TestCase): class VarianceScalingInitializationTest(test.TestCase): + @test_util.run_deprecated_v1 def testTruncatedNormalDistribution(self): shape = [100, 100] expect_mean = 0. @@ -381,6 +405,7 @@ class VarianceScalingInitializationTest(test.TestCase): self.assertNear(np.mean(x), expect_mean, err=1e-2) self.assertNear(np.var(x), expect_var, err=1e-2) + @test_util.run_deprecated_v1 def testNormalDistribution(self): shape = [100, 100] expect_mean = 0. @@ -397,6 +422,7 @@ class VarianceScalingInitializationTest(test.TestCase): self.assertNear(np.mean(x), expect_mean, err=1e-2) self.assertNear(np.var(x), expect_var, err=1e-2) + @test_util.run_deprecated_v1 def testUntruncatedNormalDistribution(self): shape = [100, 100] expect_mean = 0. @@ -414,6 +440,7 @@ class VarianceScalingInitializationTest(test.TestCase): self.assertNear(np.mean(x), expect_mean, err=1e-2) self.assertNear(np.var(x), expect_var, err=1e-2) + @test_util.run_deprecated_v1 def testUniformDistribution(self): shape = [100, 100] expect_mean = 0. @@ -435,7 +462,7 @@ class RangeTest(test.TestCase): tf_ans = math_ops.range(start, limit, delta, name="range") self.assertEqual([len(np.arange(start, limit, delta))], tf_ans.get_shape()) - return tf_ans.eval() + return self.evaluate(tf_ans) def testBasic(self): self.assertTrue( @@ -449,6 +476,7 @@ class RangeTest(test.TestCase): self._Range(100, 500, 100), np.array([100, 200, 300, 400]))) self.assertEqual(math_ops.range(0, 5, 1).dtype, dtypes.int32) + @test_util.run_deprecated_v1 def testLimitOnly(self): with self.session(use_gpu=True): self.assertAllEqual(np.arange(5), math_ops.range(5).eval()) @@ -524,7 +552,7 @@ class LinSpaceTest(test.TestCase): with self.session(graph=graph, force_gpu=self.force_gpu): tf_ans = math_ops.linspace(start, stop, num, name="linspace") self.assertEqual([num], tf_ans.get_shape()) - return tf_ans.eval() + return self.evaluate(tf_ans) def testPositive(self): for self.force_gpu in self._gpu_modes(): @@ -583,18 +611,21 @@ class DeviceTest(test.TestCase): class OrthogonalInitializerTest(test.TestCase): + @test_util.run_deprecated_v1 def testInitializerIdentical(self): for dtype in [dtypes.float32, dtypes.float64]: init1 = init_ops.orthogonal_initializer(seed=1, dtype=dtype) init2 = init_ops.orthogonal_initializer(seed=1, dtype=dtype) self.assertTrue(identicaltest(self, init1, init2, (10, 10))) + @test_util.run_deprecated_v1 def testInitializerDifferent(self): for dtype in [dtypes.float32, dtypes.float64]: init1 = init_ops.orthogonal_initializer(seed=1, dtype=dtype) init2 = init_ops.orthogonal_initializer(seed=2, dtype=dtype) self.assertFalse(identicaltest(self, init1, init2, (10, 10))) + @test_util.run_deprecated_v1 def testDuplicatedInitializer(self): init = init_ops.orthogonal_initializer() self.assertFalse(duplicated_initializer(self, init, 1, (10, 10))) @@ -608,6 +639,7 @@ class OrthogonalInitializerTest(test.TestCase): with self.session(graph=ops.Graph(), use_gpu=True): self.assertRaises(ValueError, init1, shape=[5]) + @test_util.run_deprecated_v1 def testGain(self): shape = (10, 10) for dtype in [dtypes.float32, dtypes.float64]: @@ -616,8 +648,9 @@ class OrthogonalInitializerTest(test.TestCase): with self.session(graph=ops.Graph(), use_gpu=True): t1 = init1(shape).eval() t2 = init2(shape).eval() - return np.allclose(t1, t2 / 3.14, rtol=1e-15, atol=1e-15) + self.assertAllClose(t1, t2 / 3.14) + @test_util.run_deprecated_v1 def testShapesValues(self): for dtype in [dtypes.float32, dtypes.float64]: for shape in [(10, 10), (10, 9, 8), (100, 5, 5), (50, 40), (40, 50)]: @@ -639,18 +672,21 @@ class OrthogonalInitializerTest(test.TestCase): class ConvolutionDeltaOrthogonalInitializerTest(test.TestCase): + @test_util.run_deprecated_v1 def testInitializerIdentical(self): for dtype in [dtypes.float32, dtypes.float64]: init1 = init_ops.convolutional_delta_orthogonal(seed=1, dtype=dtype) init2 = init_ops.convolutional_delta_orthogonal(seed=1, dtype=dtype) self.assertTrue(identicaltest(self, init1, init2, (3, 3, 10, 10))) + @test_util.run_deprecated_v1 def testInitializerDifferent(self): for dtype in [dtypes.float32, dtypes.float64]: init1 = init_ops.convolutional_delta_orthogonal(seed=1, dtype=dtype) init2 = init_ops.convolutional_delta_orthogonal(seed=2, dtype=dtype) self.assertFalse(identicaltest(self, init1, init2, (3, 3, 10, 10))) + @test_util.run_deprecated_v1 def testDuplicatedInitializer(self): init = init_ops.convolutional_delta_orthogonal() self.assertFalse(duplicated_initializer(self, init, 1, (3, 3, 10, 10))) @@ -665,6 +701,7 @@ class ConvolutionDeltaOrthogonalInitializerTest(test.TestCase): with self.session(graph=ops.Graph(), use_gpu=True): self.assertRaises(ValueError, init1, shape=[3, 3, 6, 5]) + @test_util.run_deprecated_v1 def testGain(self): shape = (3, 3, 10, 10) for dtype in [dtypes.float32, dtypes.float64]: @@ -674,8 +711,9 @@ class ConvolutionDeltaOrthogonalInitializerTest(test.TestCase): with self.session(graph=ops.Graph(), use_gpu=True): t1 = init1(shape).eval() t2 = init2(shape).eval() - return np.allclose(t1, t2 / 3.14, rtol=1e-15, atol=1e-15) + self.assertAllClose(t1, t2 / 3.14) + @test_util.run_deprecated_v1 def testShapesValues(self): gain = 3.14 for dtype in [dtypes.float32]: @@ -704,14 +742,14 @@ class ConvolutionDeltaOrthogonalInitializerTest(test.TestCase): ratio = outputs_2norm / inputs_2norm my_ops = variables.global_variables_initializer() with self.session(use_gpu=True) as sess: - sess.run(my_ops) + self.evaluate(my_ops) # Check the shape of the outputs - t = outputs.eval() + t = self.evaluate(outputs) self.assertAllEqual(t.shape, outputs_shape) # Check isometry of the delta-orthogonal kernel. - self.assertAllClose(sess.run(ratio), np.sqrt(gain), - rtol=tol, atol=tol) + self.assertAllClose(self.evaluate(ratio), gain, rtol=tol, atol=tol) + @test_util.run_deprecated_v1 def testNonuniformity(self): value = 0 abs_value = 0 @@ -724,7 +762,7 @@ class ConvolutionDeltaOrthogonalInitializerTest(test.TestCase): initializer= init_ops.convolutional_delta_orthogonal) x.initializer.run() - y = x.eval()[1, 1, :, :] + y = self.evaluate(x)[1, 1, :, :] determinant = np.linalg.det(y) value += determinant abs_value += np.abs(determinant) @@ -739,18 +777,21 @@ class ConvolutionDeltaOrthogonalInitializerTest(test.TestCase): class ConvolutionOrthogonal1dInitializerTest(test.TestCase): + @test_util.run_deprecated_v1 def testInitializerIdentical(self): for dtype in [dtypes.float32, dtypes.float64]: init1 = init_ops.convolutional_orthogonal_1d(seed=1, dtype=dtype) init2 = init_ops.convolutional_orthogonal_1d(seed=1, dtype=dtype) self.assertTrue(identicaltest(self, init1, init2, (3, 10, 10))) + @test_util.run_deprecated_v1 def testInitializerDifferent(self): for dtype in [dtypes.float32, dtypes.float64]: init1 = init_ops.convolutional_orthogonal_1d(seed=1, dtype=dtype) init2 = init_ops.convolutional_orthogonal_1d(seed=2, dtype=dtype) self.assertFalse(identicaltest(self, init1, init2, (3, 10, 10))) + @test_util.run_deprecated_v1 def testDuplicatedInitializer(self): init = init_ops.convolutional_orthogonal_1d() self.assertFalse(duplicated_initializer(self, init, 1, (3, 10, 10))) @@ -765,6 +806,7 @@ class ConvolutionOrthogonal1dInitializerTest(test.TestCase): with self.session(graph=ops.Graph(), use_gpu=True): self.assertRaises(ValueError, init1, shape=[3, 6, 5]) + @test_util.run_deprecated_v1 def testGain(self): shape = (3, 10, 10) for dtype in [dtypes.float32, dtypes.float64]: @@ -774,8 +816,9 @@ class ConvolutionOrthogonal1dInitializerTest(test.TestCase): with self.session(graph=ops.Graph(), use_gpu=True): t1 = init1(shape).eval() t2 = init2(shape).eval() - return np.allclose(t1, t2 / 3.14, rtol=1e-15, atol=1e-15) + self.assertAllClose(t1, t2 / 3.14) + @test_util.run_deprecated_v1 def testNonuniformity(self): value = 0 abs_value = 0 @@ -800,6 +843,7 @@ class ConvolutionOrthogonal1dInitializerTest(test.TestCase): # Compute the sum of the absolute values of 'count' determinants self.assertAllClose(abs_value, count, rtol=tol, atol=tol) + @test_util.run_deprecated_v1 def testShapesValues(self): def circular_pad(input_, width, kernel_size): """Pad input_ for computing (circular) convolution. @@ -843,28 +887,31 @@ class ConvolutionOrthogonal1dInitializerTest(test.TestCase): ratio = outputs_2norm / inputs_2norm my_ops = variables.global_variables_initializer() with self.session(use_gpu=True) as sess: - sess.run(my_ops) + self.evaluate(my_ops) # Check the shape of the outputs - t = outputs.eval() + t = self.evaluate(outputs) self.assertAllEqual(t.shape, outputs_shape) # Check isometry of the orthogonal kernel. - self.assertAllClose(sess.run(ratio), np.sqrt(gain), rtol=tol, atol=tol) + self.assertAllClose(self.evaluate(ratio), gain, rtol=tol, atol=tol) class ConvolutionOrthogonal2dInitializerTest(test.TestCase): + @test_util.run_deprecated_v1 def testInitializerIdentical(self): for dtype in [dtypes.float32, dtypes.float64]: init1 = init_ops.convolutional_orthogonal_2d(seed=1, dtype=dtype) init2 = init_ops.convolutional_orthogonal_2d(seed=1, dtype=dtype) self.assertTrue(identicaltest(self, init1, init2, (3, 3, 10, 10))) + @test_util.run_deprecated_v1 def testInitializerDifferent(self): for dtype in [dtypes.float32, dtypes.float64]: init1 = init_ops.convolutional_orthogonal_2d(seed=1, dtype=dtype) init2 = init_ops.convolutional_orthogonal_2d(seed=2, dtype=dtype) self.assertFalse(identicaltest(self, init1, init2, (3, 3, 10, 10))) + @test_util.run_deprecated_v1 def testDuplicatedInitializer(self): init = init_ops.convolutional_orthogonal_2d() self.assertFalse(duplicated_initializer(self, init, 1, (3, 3, 10, 10))) @@ -879,6 +926,7 @@ class ConvolutionOrthogonal2dInitializerTest(test.TestCase): with self.session(graph=ops.Graph(), use_gpu=True): self.assertRaises(ValueError, init1, shape=[3, 3, 6, 5]) + @test_util.run_deprecated_v1 def testGain(self): shape = (3, 3, 10, 10) for dtype in [dtypes.float32, dtypes.float64]: @@ -888,8 +936,9 @@ class ConvolutionOrthogonal2dInitializerTest(test.TestCase): with self.session(graph=ops.Graph(), use_gpu=True): t1 = init1(shape).eval() t2 = init2(shape).eval() - return np.allclose(t1, t2 / 3.14, rtol=1e-15, atol=1e-15) + self.assertAllClose(t1, t2 / 3.14) + @test_util.run_deprecated_v1 def testShapesValues(self): def circular_pad(input_, width, kernel_size): """Pad input_ for computing (circular) convolution. @@ -938,28 +987,31 @@ class ConvolutionOrthogonal2dInitializerTest(test.TestCase): ratio = outputs_2norm / inputs_2norm my_ops = variables.global_variables_initializer() with self.session(use_gpu=True) as sess: - sess.run(my_ops) + self.evaluate(my_ops) # Check the shape of the outputs - t = outputs.eval() + t = self.evaluate(outputs) self.assertAllEqual(t.shape, outputs_shape) # Check isometry of the orthogonal kernel. - self.assertAllClose(sess.run(ratio), np.sqrt(gain), rtol=tol, atol=tol) + self.assertAllClose(self.evaluate(ratio), gain, rtol=tol, atol=tol) class ConvolutionOrthogonal3dInitializerTest(test.TestCase): + @test_util.run_deprecated_v1 def testInitializerIdentical(self): for dtype in [dtypes.float32, dtypes.float64]: init1 = init_ops.convolutional_orthogonal_3d(seed=1, dtype=dtype) init2 = init_ops.convolutional_orthogonal_3d(seed=1, dtype=dtype) self.assertTrue(identicaltest(self, init1, init2, (3, 3, 3, 10, 10))) + @test_util.run_deprecated_v1 def testInitializerDifferent(self): for dtype in [dtypes.float32, dtypes.float64]: init1 = init_ops.convolutional_orthogonal_3d(seed=1, dtype=dtype) init2 = init_ops.convolutional_orthogonal_3d(seed=2, dtype=dtype) self.assertFalse(identicaltest(self, init1, init2, (3, 3, 3, 10, 10))) + @test_util.run_deprecated_v1 def testDuplicatedInitializer(self): init = init_ops.convolutional_orthogonal_3d() self.assertFalse(duplicated_initializer(self, init, 1, (3, 3, 3, 10, 10))) @@ -974,6 +1026,7 @@ class ConvolutionOrthogonal3dInitializerTest(test.TestCase): with self.session(graph=ops.Graph(), use_gpu=True): self.assertRaises(ValueError, init1, shape=[3, 3, 3, 6, 5]) + @test_util.run_deprecated_v1 def testGain(self): shape = (3, 3, 3, 10, 10) for dtype in [dtypes.float32, dtypes.float64]: @@ -983,8 +1036,9 @@ class ConvolutionOrthogonal3dInitializerTest(test.TestCase): with self.session(graph=ops.Graph(), use_gpu=True): t1 = init1(shape).eval() t2 = init2(shape).eval() - return np.allclose(t1, t2 / 3.14, rtol=1e-15, atol=1e-15) + self.assertAllClose(t1, t2 / 3.14) + @test_util.run_deprecated_v1 def testNonuniformity(self): value = 0 abs_value = 0 @@ -1009,6 +1063,7 @@ class ConvolutionOrthogonal3dInitializerTest(test.TestCase): # Compute the sum of the absolute values of 'count' determinants self.assertAllClose(abs_value, count, rtol=tol, atol=tol) + @test_util.run_deprecated_v1 def testShapesValues(self): def circular_pad(input_, width, kernel_size): """Padding input_ for computing circular convolution. @@ -1063,12 +1118,12 @@ class ConvolutionOrthogonal3dInitializerTest(test.TestCase): ratio = outputs_2norm / inputs_2norm my_ops = variables.global_variables_initializer() with self.cached_session(use_gpu=True) as sess: - sess.run(my_ops) + self.evaluate(my_ops) # Check the shape of the outputs - t = outputs.eval() + t = self.evaluate(outputs) self.assertAllEqual(t.shape, outputs_shape) # Check isometry of the orthogonal kernel. - self.assertAllClose(sess.run(ratio), np.sqrt(gain), rtol=tol, atol=tol) + self.assertAllClose(self.evaluate(ratio), gain, rtol=tol, atol=tol) class IdentityInitializerTest(test.TestCase): @@ -1084,12 +1139,14 @@ class IdentityInitializerTest(test.TestCase): self.assertRaises(ValueError, init, shape=[5]) self.assertRaises(ValueError, init, shape=[]) + @test_util.run_deprecated_v1 def testNonSquare(self): init = init_ops.identity_initializer() shape = (10, 5) with self.session(graph=ops.Graph(), use_gpu=True): self.assertAllClose(init(shape).eval(), np.eye(*shape)) + @test_util.run_deprecated_v1 def testGain(self): shape = (10, 10) for dtype in [dtypes.float32, dtypes.float64]: @@ -1100,6 +1157,7 @@ class IdentityInitializerTest(test.TestCase): with self.session(graph=ops.Graph(), use_gpu=True): self.assertAllClose(init_custom(shape).eval(), np.eye(*shape) * 0.9) + @test_util.run_deprecated_v1 def testPartitions(self): shape = (10, 10) init = init_ops.identity_initializer() diff --git a/tensorflow/python/kernel_tests/inplace_ops_test.py b/tensorflow/python/kernel_tests/inplace_ops_test.py index 51d16861dd..9eaaac7a24 100644 --- a/tensorflow/python/kernel_tests/inplace_ops_test.py +++ b/tensorflow/python/kernel_tests/inplace_ops_test.py @@ -31,6 +31,7 @@ from tensorflow.python.platform import test as test_lib class InplaceOpsTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testBasicUpdate(self): for dtype in [dtypes.float32, dtypes.int32, dtypes.int64]: with self.session(use_gpu=True): @@ -48,6 +49,7 @@ class InplaceOpsTest(test_util.TensorFlowTestCase): y[5, :] = 7 self.assertAllClose(x.eval(), y) + @test_util.run_deprecated_v1 def testBasicUpdateBool(self): with self.session(use_gpu=True): x = array_ops.ones([7, 3], dtypes.bool) @@ -65,6 +67,7 @@ class InplaceOpsTest(test_util.TensorFlowTestCase): y[5, :] = False self.assertAllClose(x.eval(), y) + @test_util.run_deprecated_v1 def testBasicAdd(self): for dtype in [dtypes.float32, dtypes.int32, dtypes.int64]: with self.cached_session(use_gpu=True): @@ -84,6 +87,7 @@ class InplaceOpsTest(test_util.TensorFlowTestCase): y[:, :] += 99 self.assertAllClose(x.eval(), y) + @test_util.run_deprecated_v1 def testBasicSub(self): for dtype in [dtypes.float32, dtypes.int32, dtypes.int64]: with self.cached_session(use_gpu=True): @@ -103,6 +107,7 @@ class InplaceOpsTest(test_util.TensorFlowTestCase): y[:, :] -= 99 self.assertAllClose(x.eval(), y) + @test_util.run_deprecated_v1 def testRandom(self): with self.session(use_gpu=True): d0, d1, d2 = 100, 3, 5 @@ -123,6 +128,7 @@ class InplaceOpsTest(test_util.TensorFlowTestCase): y[idx, :] -= val self.assertAllClose(x.eval(), y) + @test_util.run_deprecated_v1 def testRandom1D(self): with self.session(use_gpu=True): d0 = 100 @@ -149,7 +155,7 @@ class InplaceOpsTest(test_util.TensorFlowTestCase): y = inplace_ops.alias_inplace_add(x, [0], [[1, 2, 3]]) with ops.control_dependencies([y]): z = array_ops.identity(x) - _, vy, vz = sess.run([x, y, z]) + _, vy, vz = self.evaluate([x, y, z]) self.assertAllClose(vy, vz) def testError(self): @@ -164,6 +170,7 @@ class InplaceOpsTest(test_util.TensorFlowTestCase): "i and x shape doesn't match"): _ = inplace_ops.inplace_update([[1.]], [0, 1], [[10]]).eval() + @test_util.run_deprecated_v1 def testEmpty(self): for dtype in [ dtypes.float32, dtypes.float64, dtypes.int32, dtypes.int64, dtypes.bool, diff --git a/tensorflow/python/kernel_tests/io_ops_test.py b/tensorflow/python/kernel_tests/io_ops_test.py index afa24195cb..c5df5231bf 100644 --- a/tensorflow/python/kernel_tests/io_ops_test.py +++ b/tensorflow/python/kernel_tests/io_ops_test.py @@ -23,6 +23,7 @@ import os import shutil import tempfile +from tensorflow.python.framework import test_util from tensorflow.python.ops import io_ops from tensorflow.python.platform import test from tensorflow.python.util import compat @@ -30,6 +31,7 @@ from tensorflow.python.util import compat class IoOpsTest(test.TestCase): + @test_util.run_deprecated_v1 def testReadFile(self): cases = ['', 'Some contents', 'Неки садржаји на српском'] for contents in cases: @@ -53,7 +55,7 @@ class IoOpsTest(test.TestCase): pass with self.cached_session() as sess: w = io_ops.write_file(temp.name, contents) - sess.run(w) + self.evaluate(w) with open(temp.name, 'rb') as f: file_contents = f.read() self.assertEqual(file_contents, contents) @@ -67,7 +69,7 @@ class IoOpsTest(test.TestCase): filepath = os.path.join(subdir, 'subdir2', 'filename') with self.cached_session() as sess: w = io_ops.write_file(filepath, contents) - sess.run(w) + self.evaluate(w) with open(filepath, 'rb') as f: file_contents = f.read() self.assertEqual(file_contents, contents) @@ -78,6 +80,7 @@ class IoOpsTest(test.TestCase): compat.as_bytes(files[i].name) for i in range(len(files)) if i in indices) + @test_util.run_deprecated_v1 def testMatchingFiles(self): cases = [ 'ABcDEF.GH', 'ABzDEF.GH', 'ABasdfjklDEF.GH', 'AB3DEF.GH', 'AB4DEF.GH', diff --git a/tensorflow/python/kernel_tests/large_concat_op_test.py b/tensorflow/python/kernel_tests/large_concat_op_test.py index 1b23e74776..bf6fa9ea71 100644 --- a/tensorflow/python/kernel_tests/large_concat_op_test.py +++ b/tensorflow/python/kernel_tests/large_concat_op_test.py @@ -35,7 +35,7 @@ class LargeConcatOpTest(test.TestCase): with self.session(use_gpu=False): # TODO(dga): Add more depth to this test to validate correctness, # not just non-crashingness, once other large tensor fixes have gone in. - _ = onezeros.eval() + _ = self.evaluate(onezeros) if __name__ == "__main__": diff --git a/tensorflow/python/kernel_tests/linalg/BUILD b/tensorflow/python/kernel_tests/linalg/BUILD index be2e31cb5a..ba9e64979a 100644 --- a/tensorflow/python/kernel_tests/linalg/BUILD +++ b/tensorflow/python/kernel_tests/linalg/BUILD @@ -40,6 +40,44 @@ cuda_py_test( ], ) +cuda_py_test( + name = "linear_operator_adjoint_test", + size = "medium", + srcs = ["linear_operator_adjoint_test.py"], + additional_deps = [ + "//tensorflow/python/ops/linalg", + "//third_party/py/numpy", + "//tensorflow/python:array_ops", + "//tensorflow/python:client_testlib", + "//tensorflow/python:framework", + "//tensorflow/python:framework_for_generated_wrappers", + "//tensorflow/python:framework_test_lib", + "//tensorflow/python:math_ops", + "//tensorflow/python:platform_test", + ], + shard_count = 5, + tags = [ + "noasan", # times out, b/63678675 + "optonly", # times out + ], +) + +cuda_py_test( + name = "linear_operator_algebra_test", + size = "small", + srcs = ["linear_operator_algebra_test.py"], + additional_deps = [ + "//tensorflow/python/ops/linalg", + "//third_party/py/numpy", + "//tensorflow/python:array_ops", + "//tensorflow/python:client_testlib", + "//tensorflow/python:framework_for_generated_wrappers", + "//tensorflow/python:framework_test_lib", + "//tensorflow/python:math_ops", + "//tensorflow/python:platform_test", + ], +) + cuda_py_test( name = "linear_operator_block_diag_test", size = "medium", @@ -89,7 +127,6 @@ cuda_py_test( size = "medium", srcs = ["linear_operator_circulant_test.py"], additional_deps = [ - "//tensorflow/python/ops/linalg", "//third_party/py/numpy", "//tensorflow/python:array_ops", "//tensorflow/python:spectral_ops_test_util", @@ -99,6 +136,8 @@ cuda_py_test( "//tensorflow/python:framework_test_lib", "//tensorflow/python:math_ops", "//tensorflow/python:platform_test", + "//tensorflow/python/ops/linalg", + "//tensorflow/python/ops/signal", ], shard_count = 5, tags = [ @@ -150,6 +189,28 @@ cuda_py_test( ], ) +cuda_py_test( + name = "linear_operator_inversion_test", + size = "medium", + srcs = ["linear_operator_inversion_test.py"], + additional_deps = [ + "//tensorflow/python/ops/linalg", + "//third_party/py/numpy", + "//tensorflow/python:array_ops", + "//tensorflow/python:client_testlib", + "//tensorflow/python:framework", + "//tensorflow/python:framework_for_generated_wrappers", + "//tensorflow/python:framework_test_lib", + "//tensorflow/python:math_ops", + "//tensorflow/python:platform_test", + ], + shard_count = 5, + tags = [ + "noasan", # times out, b/63678675 + "optonly", # times out + ], +) + cuda_py_test( name = "linear_operator_full_matrix_test", size = "medium", diff --git a/tensorflow/python/kernel_tests/linalg/linear_operator_addition_test.py b/tensorflow/python/kernel_tests/linalg/linear_operator_addition_test.py index 628ed998c5..627349c69b 100644 --- a/tensorflow/python/kernel_tests/linalg/linear_operator_addition_test.py +++ b/tensorflow/python/kernel_tests/linalg/linear_operator_addition_test.py @@ -19,6 +19,7 @@ from __future__ import print_function import numpy as np +from tensorflow.python.framework import test_util from tensorflow.python.ops import linalg_ops from tensorflow.python.ops.linalg import linalg as linalg_lib from tensorflow.python.ops.linalg import linear_operator_addition @@ -69,6 +70,7 @@ class LinearOperatorAdditionCorrectnessTest(test.TestCase): with self.assertRaisesRegexp(TypeError, "contain only LinearOperator"): add_operators([1, 2]) + @test_util.run_deprecated_v1 def test_two_diag_operators(self): op_a = linalg.LinearOperatorDiag( [1., 1.], is_positive_definite=True, name="A") @@ -89,6 +91,7 @@ class LinearOperatorAdditionCorrectnessTest(test.TestCase): # Enforce particular name for this simple case self.assertEqual("Add/B__A/", op.name) + @test_util.run_deprecated_v1 def test_three_diag_operators(self): op1 = linalg.LinearOperatorDiag( [1., 1.], is_positive_definite=True, name="op1") @@ -109,6 +112,7 @@ class LinearOperatorAdditionCorrectnessTest(test.TestCase): # Positive definite ==> non-singular self.assertTrue(op.is_non_singular) + @test_util.run_deprecated_v1 def test_diag_tril_diag(self): op1 = linalg.LinearOperatorDiag( [1., 1.], is_non_singular=True, name="diag_a") @@ -134,6 +138,7 @@ class LinearOperatorAdditionCorrectnessTest(test.TestCase): # Since no custom hint was provided, we default to None (unknown). self.assertEqual(None, op.is_non_singular) + @test_util.run_deprecated_v1 def test_matrix_diag_tril_diag_uses_custom_name(self): op0 = linalg.LinearOperatorFullMatrix( [[-1., -1.], [-1., -1.]], name="matrix") @@ -217,6 +222,7 @@ class LinearOperatorOrderOfAdditionTest(test.TestCase): self.assertEqual(1, len(op_sum)) self.assertIsInstance(op_sum[0], linalg.LinearOperatorLowerTriangular) + @test_util.run_deprecated_v1 def test_cannot_add_everything_so_return_more_than_one_operator(self): diag1 = linalg.LinearOperatorDiag([1.]) diag2 = linalg.LinearOperatorDiag([2.]) @@ -261,6 +267,7 @@ class AddAndReturnScaledIdentityTest(test.TestCase): def setUp(self): self._adder = linear_operator_addition._AddAndReturnScaledIdentity() + @test_util.run_deprecated_v1 def test_identity_plus_identity(self): id1 = linalg.LinearOperatorIdentity(num_rows=2) id2 = linalg.LinearOperatorIdentity(num_rows=2, batch_shape=[3]) @@ -279,6 +286,7 @@ class AddAndReturnScaledIdentityTest(test.TestCase): self.assertTrue(operator.is_non_singular) self.assertEqual("my_operator", operator.name) + @test_util.run_deprecated_v1 def test_identity_plus_scaled_identity(self): id1 = linalg.LinearOperatorIdentity(num_rows=2, batch_shape=[3]) id2 = linalg.LinearOperatorScaledIdentity(num_rows=2, multiplier=2.2) @@ -297,6 +305,7 @@ class AddAndReturnScaledIdentityTest(test.TestCase): self.assertTrue(operator.is_non_singular) self.assertEqual("my_operator", operator.name) + @test_util.run_deprecated_v1 def test_scaled_identity_plus_scaled_identity(self): id1 = linalg.LinearOperatorScaledIdentity( num_rows=2, multiplier=[2.2, 2.2, 2.2]) @@ -322,6 +331,7 @@ class AddAndReturnDiagTest(test.TestCase): def setUp(self): self._adder = linear_operator_addition._AddAndReturnDiag() + @test_util.run_deprecated_v1 def test_identity_plus_identity_returns_diag(self): id1 = linalg.LinearOperatorIdentity(num_rows=2) id2 = linalg.LinearOperatorIdentity(num_rows=2, batch_shape=[3]) @@ -340,6 +350,7 @@ class AddAndReturnDiagTest(test.TestCase): self.assertTrue(operator.is_non_singular) self.assertEqual("my_operator", operator.name) + @test_util.run_deprecated_v1 def test_diag_plus_diag(self): diag1 = rng.rand(2, 3, 4) diag2 = rng.rand(4) @@ -366,6 +377,7 @@ class AddAndReturnTriLTest(test.TestCase): def setUp(self): self._adder = linear_operator_addition._AddAndReturnTriL() + @test_util.run_deprecated_v1 def test_diag_plus_tril(self): diag = linalg.LinearOperatorDiag([1., 2.]) tril = linalg.LinearOperatorLowerTriangular([[10., 0.], [30., 0.]]) @@ -389,6 +401,7 @@ class AddAndReturnMatrixTest(test.TestCase): def setUp(self): self._adder = linear_operator_addition._AddAndReturnMatrix() + @test_util.run_deprecated_v1 def test_diag_plus_diag(self): diag1 = linalg.LinearOperatorDiag([1., 2.]) diag2 = linalg.LinearOperatorDiag([-1., 3.]) diff --git a/tensorflow/python/kernel_tests/linalg/linear_operator_adjoint_test.py b/tensorflow/python/kernel_tests/linalg/linear_operator_adjoint_test.py new file mode 100644 index 0000000000..1bed4b5268 --- /dev/null +++ b/tensorflow/python/kernel_tests/linalg/linear_operator_adjoint_test.py @@ -0,0 +1,118 @@ +# Copyright 2018 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. +# ============================================================================== + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.framework import dtypes +from tensorflow.python.ops import array_ops +from tensorflow.python.ops.linalg import linalg as linalg_lib +from tensorflow.python.ops.linalg import linear_operator_adjoint +from tensorflow.python.ops.linalg import linear_operator_test_util +from tensorflow.python.platform import test + +linalg = linalg_lib + +LinearOperatorAdjoint = linear_operator_adjoint.LinearOperatorAdjoint # pylint: disable=invalid-name + + +class LinearOperatorAdjointTest( + linear_operator_test_util.SquareLinearOperatorDerivedClassTest): + """Most tests done in the base class LinearOperatorDerivedClassTest.""" + + def setUp(self): + self._atol[dtypes.complex64] = 1e-5 + self._rtol[dtypes.complex64] = 1e-5 + + def _operator_and_matrix(self, + build_info, + dtype, + use_placeholder, + ensure_self_adjoint_and_pd=False): + shape = list(build_info.shape) + + if ensure_self_adjoint_and_pd: + matrix = linear_operator_test_util.random_positive_definite_matrix( + shape, dtype, force_well_conditioned=True) + else: + matrix = linear_operator_test_util.random_tril_matrix( + shape, dtype, force_well_conditioned=True, remove_upper=True) + + lin_op_matrix = matrix + + if use_placeholder: + lin_op_matrix = array_ops.placeholder_with_default(matrix, shape=None) + + if ensure_self_adjoint_and_pd: + operator = LinearOperatorAdjoint( + linalg.LinearOperatorFullMatrix( + lin_op_matrix, is_positive_definite=True, is_self_adjoint=True)) + else: + operator = LinearOperatorAdjoint( + linalg.LinearOperatorLowerTriangular(lin_op_matrix)) + + return operator, linalg.adjoint(matrix) + + def test_base_operator_hint_used(self): + # The matrix values do not effect auto-setting of the flags. + matrix = [[1., 0.], [1., 1.]] + operator = linalg.LinearOperatorFullMatrix( + matrix, + is_positive_definite=True, + is_non_singular=True, + is_self_adjoint=False) + operator_adjoint = LinearOperatorAdjoint(operator) + self.assertTrue(operator_adjoint.is_positive_definite) + self.assertTrue(operator_adjoint.is_non_singular) + self.assertFalse(operator_adjoint.is_self_adjoint) + + def test_supplied_hint_used(self): + # The matrix values do not effect auto-setting of the flags. + matrix = [[1., 0.], [1., 1.]] + operator = linalg.LinearOperatorFullMatrix(matrix) + operator_adjoint = LinearOperatorAdjoint( + operator, + is_positive_definite=True, + is_non_singular=True, + is_self_adjoint=False) + self.assertTrue(operator_adjoint.is_positive_definite) + self.assertTrue(operator_adjoint.is_non_singular) + self.assertFalse(operator_adjoint.is_self_adjoint) + + def test_contradicting_hints_raise(self): + # The matrix values do not effect auto-setting of the flags. + matrix = [[1., 0.], [1., 1.]] + operator = linalg.LinearOperatorFullMatrix( + matrix, is_positive_definite=False) + with self.assertRaisesRegexp(ValueError, "positive-definite"): + LinearOperatorAdjoint(operator, is_positive_definite=True) + + operator = linalg.LinearOperatorFullMatrix(matrix, is_self_adjoint=False) + with self.assertRaisesRegexp(ValueError, "self-adjoint"): + LinearOperatorAdjoint(operator, is_self_adjoint=True) + + def test_name(self): + matrix = [[11., 0.], [1., 8.]] + operator = linalg.LinearOperatorFullMatrix( + matrix, name="my_operator", is_non_singular=True) + + operator = LinearOperatorAdjoint(operator) + + self.assertEqual("my_operator_adjoint", operator.name) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/kernel_tests/linalg/linear_operator_algebra_test.py b/tensorflow/python/kernel_tests/linalg/linear_operator_algebra_test.py new file mode 100644 index 0000000000..8e296c026c --- /dev/null +++ b/tensorflow/python/kernel_tests/linalg/linear_operator_algebra_test.py @@ -0,0 +1,133 @@ +# Copyright 2018 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 registration mechanisms.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.framework import tensor_shape +from tensorflow.python.ops.linalg import cholesky_registrations # pylint: disable=unused-import +from tensorflow.python.ops.linalg import linear_operator +from tensorflow.python.ops.linalg import linear_operator_algebra +from tensorflow.python.ops.linalg import matmul_registrations # pylint: disable=unused-import +from tensorflow.python.platform import test + +# pylint: disable=protected-access +_CHOLESKY_DECOMPS = linear_operator_algebra._CHOLESKY_DECOMPS +_MATMUL = linear_operator_algebra._MATMUL +_registered_cholesky = linear_operator_algebra._registered_cholesky +_registered_matmul = linear_operator_algebra._registered_matmul +# pylint: enable=protected-access + + +class CholeskyTest(test.TestCase): + + def testRegistration(self): + + class CustomLinOp(linear_operator.LinearOperator): + + def _matmul(self, a): + pass + + def _shape(self): + return tensor_shape.TensorShape([1, 1]) + + def _shape_tensor(self): + pass + + # Register Cholesky to a lambda that spits out the name parameter + @linear_operator_algebra.RegisterCholesky(CustomLinOp) + def _cholesky(a): # pylint: disable=unused-argument,unused-variable + return "OK" + + with self.assertRaisesRegexp(ValueError, "positive definite"): + CustomLinOp(dtype=None, is_self_adjoint=True).cholesky() + + with self.assertRaisesRegexp(ValueError, "self adjoint"): + CustomLinOp(dtype=None, is_positive_definite=True).cholesky() + + custom_linop = CustomLinOp( + dtype=None, is_self_adjoint=True, is_positive_definite=True) + self.assertEqual("OK", custom_linop.cholesky()) + + def testRegistrationFailures(self): + + class CustomLinOp(linear_operator.LinearOperator): + pass + + with self.assertRaisesRegexp(TypeError, "must be callable"): + linear_operator_algebra.RegisterCholesky(CustomLinOp)("blah") + + # First registration is OK + linear_operator_algebra.RegisterCholesky(CustomLinOp)(lambda a: None) + + # Second registration fails + with self.assertRaisesRegexp(ValueError, "has already been registered"): + linear_operator_algebra.RegisterCholesky(CustomLinOp)(lambda a: None) + + def testExactCholeskyRegistrationsAllMatch(self): + for (k, v) in _CHOLESKY_DECOMPS.items(): + self.assertEqual(v, _registered_cholesky(k[0])) + + +class MatmulTest(test.TestCase): + + def testRegistration(self): + + class CustomLinOp(linear_operator.LinearOperator): + + def _matmul(self, a): + pass + + def _shape(self): + return tensor_shape.TensorShape([1, 1]) + + def _shape_tensor(self): + pass + + # Register Matmul to a lambda that spits out the name parameter + @linear_operator_algebra.RegisterMatmul(CustomLinOp, CustomLinOp) + def _matmul(a, b): # pylint: disable=unused-argument,unused-variable + return "OK" + + custom_linop = CustomLinOp( + dtype=None, is_self_adjoint=True, is_positive_definite=True) + self.assertEqual("OK", custom_linop.matmul(custom_linop)) + + def testRegistrationFailures(self): + + class CustomLinOp(linear_operator.LinearOperator): + pass + + with self.assertRaisesRegexp(TypeError, "must be callable"): + linear_operator_algebra.RegisterMatmul(CustomLinOp, CustomLinOp)("blah") + + # First registration is OK + linear_operator_algebra.RegisterMatmul( + CustomLinOp, CustomLinOp)(lambda a: None) + + # Second registration fails + with self.assertRaisesRegexp(ValueError, "has already been registered"): + linear_operator_algebra.RegisterMatmul( + CustomLinOp, CustomLinOp)(lambda a: None) + + def testExactMatmulRegistrationsAllMatch(self): + for (k, v) in _MATMUL.items(): + self.assertEqual(v, _registered_matmul(k[0], k[1])) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/kernel_tests/linalg/linear_operator_block_diag_test.py b/tensorflow/python/kernel_tests/linalg/linear_operator_block_diag_test.py index 30951b1b0e..f0cc5d709f 100644 --- a/tensorflow/python/kernel_tests/linalg/linear_operator_block_diag_test.py +++ b/tensorflow/python/kernel_tests/linalg/linear_operator_block_diag_test.py @@ -23,6 +23,7 @@ from tensorflow.python.framework import dtypes from tensorflow.python.ops import array_ops from tensorflow.python.ops.linalg import linalg as linalg_lib from tensorflow.python.ops.linalg import linear_operator_block_diag as block_diag +from tensorflow.python.ops.linalg import linear_operator_lower_triangular as lower_triangular from tensorflow.python.ops.linalg import linear_operator_test_util from tensorflow.python.ops.linalg import linear_operator_util from tensorflow.python.platform import test @@ -78,7 +79,9 @@ class SquareLinearOperatorBlockDiagTest( build_info((2, 1, 5, 5), blocks=[(2, 1, 2, 2), (1, 3, 3)]), ] - def _operator_and_matrix(self, build_info, dtype, use_placeholder): + def _operator_and_matrix( + self, build_info, dtype, use_placeholder, + ensure_self_adjoint_and_pd=False): shape = list(build_info.shape) expected_blocks = ( build_info.__dict__["blocks"] if "blocks" in build_info.__dict__ @@ -98,7 +101,11 @@ class SquareLinearOperatorBlockDiagTest( operator = block_diag.LinearOperatorBlockDiag( [linalg.LinearOperatorFullMatrix( - l, is_square=True) for l in lin_op_matrices]) + l, + is_square=True, + is_self_adjoint=True if ensure_self_adjoint_and_pd else None, + is_positive_definite=True if ensure_self_adjoint_and_pd else None) + for l in lin_op_matrices]) # Should be auto-set. self.assertTrue(operator.is_square) @@ -129,6 +136,40 @@ class SquareLinearOperatorBlockDiagTest( self.assertTrue(operator.is_non_singular) self.assertFalse(operator.is_self_adjoint) + def test_block_diag_cholesky_type(self): + matrix = [[1., 0.], [0., 1.]] + operator = block_diag.LinearOperatorBlockDiag( + [ + linalg.LinearOperatorFullMatrix( + matrix, + is_positive_definite=True, + is_self_adjoint=True, + ), + linalg.LinearOperatorFullMatrix( + matrix, + is_positive_definite=True, + is_self_adjoint=True, + ), + ], + is_positive_definite=True, + is_self_adjoint=True, + ) + cholesky_factor = operator.cholesky() + self.assertTrue(isinstance( + cholesky_factor, + block_diag.LinearOperatorBlockDiag)) + self.assertEqual(2, len(cholesky_factor.operators)) + self.assertTrue( + isinstance( + cholesky_factor.operators[0], + lower_triangular.LinearOperatorLowerTriangular) + ) + self.assertTrue( + isinstance( + cholesky_factor.operators[1], + lower_triangular.LinearOperatorLowerTriangular) + ) + def test_is_non_singular_auto_set(self): # Matrix with two positive eigenvalues, 11 and 8. # The matrix values do not effect auto-setting of the flags. diff --git a/tensorflow/python/kernel_tests/linalg/linear_operator_circulant_test.py b/tensorflow/python/kernel_tests/linalg/linear_operator_circulant_test.py index f1e151ebd8..6366083ac5 100644 --- a/tensorflow/python/kernel_tests/linalg/linear_operator_circulant_test.py +++ b/tensorflow/python/kernel_tests/linalg/linear_operator_circulant_test.py @@ -21,12 +21,14 @@ import contextlib import numpy as np from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import spectral_ops_test_util from tensorflow.python.ops.linalg import linalg from tensorflow.python.ops.linalg import linear_operator_circulant from tensorflow.python.ops.linalg import linear_operator_test_util +from tensorflow.python.ops.signal import fft_ops from tensorflow.python.platform import test rng = np.random.RandomState(0) @@ -75,8 +77,8 @@ class LinearOperatorCirculantBaseTest(object): x = np.zeros([domain_dimension]) # x is a basis vector. x[m] = 1.0 - fft_x = math_ops.fft(x.astype(np.complex64)) - h_convolve_x = math_ops.ifft(spectrum * fft_x) + fft_x = fft_ops.fft(x.astype(np.complex64)) + h_convolve_x = fft_ops.ifft(spectrum * fft_x) matrix_rows.append(h_convolve_x) matrix = array_ops.stack(matrix_rows, axis=-1) return math_ops.cast(matrix, dtype) @@ -97,7 +99,9 @@ class LinearOperatorCirculantTestSelfAdjointOperator( # real, the matrix will not be real. return [dtypes.complex64] - def _operator_and_matrix(self, build_info, dtype, use_placeholder): + def _operator_and_matrix( + self, build_info, dtype, use_placeholder, + ensure_self_adjoint_and_pd=False): shape = build_info.shape # For this test class, we are creating real spectrums. # We also want the spectrum to have eigenvalues bounded away from zero. @@ -105,6 +109,8 @@ class LinearOperatorCirculantTestSelfAdjointOperator( # spectrum is bounded away from zero. spectrum = linear_operator_test_util.random_sign_uniform( shape=self._shape_to_spectrum_shape(shape), minval=1., maxval=2.) + if ensure_self_adjoint_and_pd: + spectrum = math_ops.abs(spectrum) # If dtype is complex, cast spectrum to complex. The imaginary part will be # zero, so the operator will still be self-adjoint. spectrum = math_ops.cast(spectrum, dtype) @@ -115,12 +121,16 @@ class LinearOperatorCirculantTestSelfAdjointOperator( lin_op_spectrum = array_ops.placeholder_with_default(spectrum, shape=None) operator = linalg.LinearOperatorCirculant( - lin_op_spectrum, is_self_adjoint=True, input_output_dtype=dtype) + lin_op_spectrum, + is_self_adjoint=True, + is_positive_definite=True if ensure_self_adjoint_and_pd else None, + input_output_dtype=dtype) mat = self._spectrum_to_circulant_1d(spectrum, shape, dtype=dtype) return operator, mat + @test_util.run_deprecated_v1 def test_simple_hermitian_spectrum_gives_operator_with_zero_imag_part(self): with self.cached_session(): spectrum = math_ops.cast([1., 1j, -1j], dtypes.complex64) @@ -129,7 +139,8 @@ class LinearOperatorCirculantTestSelfAdjointOperator( matrix = operator.to_dense() imag_matrix = math_ops.imag(matrix) eps = np.finfo(np.float32).eps - np.testing.assert_allclose(0, imag_matrix.eval(), rtol=0, atol=eps * 3) + np.testing.assert_allclose( + 0, self.evaluate(imag_matrix), rtol=0, atol=eps * 3) class LinearOperatorCirculantTestHermitianSpectrum( @@ -146,7 +157,9 @@ class LinearOperatorCirculantTestHermitianSpectrum( def _dtypes_to_test(self): return [dtypes.float32, dtypes.complex64] - def _operator_and_matrix(self, build_info, dtype, use_placeholder): + def _operator_and_matrix( + self, build_info, dtype, use_placeholder, + ensure_self_adjoint_and_pd=False): shape = build_info.shape # For this test class, we are creating Hermitian spectrums. # We also want the spectrum to have eigenvalues bounded away from zero. @@ -160,14 +173,14 @@ class LinearOperatorCirculantTestHermitianSpectrum( # = IFFT[EvenPartOf[pre_spectrum]] # is the IFFT of something that is also bounded away from zero. # Therefore, FFT[pre_h] would be a well-conditioned spectrum. - pre_h = math_ops.ifft(pre_spectrum_c) + pre_h = fft_ops.ifft(pre_spectrum_c) # A spectrum is Hermitian iff it is the DFT of a real convolution kernel. # So we will make spectrum = FFT[h], for real valued h. h = math_ops.real(pre_h) h_c = _to_complex(h) - spectrum = math_ops.fft(h_c) + spectrum = fft_ops.fft(h_c) lin_op_spectrum = spectrum @@ -175,12 +188,17 @@ class LinearOperatorCirculantTestHermitianSpectrum( lin_op_spectrum = array_ops.placeholder_with_default(spectrum, shape=None) operator = linalg.LinearOperatorCirculant( - lin_op_spectrum, input_output_dtype=dtype) + lin_op_spectrum, + input_output_dtype=dtype, + is_positive_definite=True if ensure_self_adjoint_and_pd else None, + is_self_adjoint=True if ensure_self_adjoint_and_pd else None, + ) mat = self._spectrum_to_circulant_1d(spectrum, shape, dtype=dtype) return operator, mat + @test_util.run_deprecated_v1 def test_simple_hermitian_spectrum_gives_operator_with_zero_imag_part(self): with self.cached_session(): spectrum = math_ops.cast([1., 1j, -1j], dtypes.complex64) @@ -189,7 +207,8 @@ class LinearOperatorCirculantTestHermitianSpectrum( matrix = operator.to_dense() imag_matrix = math_ops.imag(matrix) eps = np.finfo(np.float32).eps - np.testing.assert_allclose(0, imag_matrix.eval(), rtol=0, atol=eps * 3) + np.testing.assert_allclose( + 0, self.evaluate(imag_matrix), rtol=0, atol=eps * 3) class LinearOperatorCirculantTestNonHermitianSpectrum( @@ -205,7 +224,16 @@ class LinearOperatorCirculantTestNonHermitianSpectrum( def _dtypes_to_test(self): return [dtypes.complex64] - def _operator_and_matrix(self, build_info, dtype, use_placeholder): + # Skip Cholesky since we are explicitly testing non-hermitian + # spectra. + @property + def _tests_to_skip(self): + return ["cholesky"] + + def _operator_and_matrix( + self, build_info, dtype, use_placeholder, + ensure_self_adjoint_and_pd=False): + del ensure_self_adjoint_and_pd shape = build_info.shape # Will be well conditioned enough to get accurate solves. spectrum = linear_operator_test_util.random_sign_uniform( @@ -226,6 +254,7 @@ class LinearOperatorCirculantTestNonHermitianSpectrum( return operator, mat + @test_util.run_deprecated_v1 def test_simple_hermitian_spectrum_gives_operator_with_zero_imag_part(self): with self.cached_session(): spectrum = math_ops.cast([1., 1j, -1j], dtypes.complex64) @@ -234,8 +263,10 @@ class LinearOperatorCirculantTestNonHermitianSpectrum( matrix = operator.to_dense() imag_matrix = math_ops.imag(matrix) eps = np.finfo(np.float32).eps - np.testing.assert_allclose(0, imag_matrix.eval(), rtol=0, atol=eps * 3) + np.testing.assert_allclose( + 0, self.evaluate(imag_matrix), rtol=0, atol=eps * 3) + @test_util.run_deprecated_v1 def test_simple_positive_real_spectrum_gives_self_adjoint_pos_def_oper(self): with self.cached_session() as sess: spectrum = math_ops.cast([6., 4, 2], dtypes.complex64) @@ -248,10 +279,11 @@ class LinearOperatorCirculantTestNonHermitianSpectrum( operator.assert_positive_definite().run() # Should not fail operator.assert_self_adjoint().run() # Should not fail + @test_util.run_deprecated_v1 def test_defining_operator_using_real_convolution_kernel(self): with self.cached_session(): convolution_kernel = [1., 2., 1.] - spectrum = math_ops.fft( + spectrum = fft_ops.fft( math_ops.cast(convolution_kernel, dtypes.complex64)) # spectrum is shape [3] ==> operator is shape [3, 3] @@ -269,15 +301,16 @@ class LinearOperatorCirculantTestNonHermitianSpectrum( # Make spectrum the FFT of a real convolution kernel h. This ensures that # spectrum is Hermitian. h = linear_operator_test_util.random_normal(shape=(3, 4)) - spectrum = math_ops.fft(math_ops.cast(h, dtypes.complex64)) + spectrum = fft_ops.fft(math_ops.cast(h, dtypes.complex64)) operator = linalg.LinearOperatorCirculant( spectrum, input_output_dtype=dtypes.complex64) matrix = operator.to_dense() imag_matrix = math_ops.imag(matrix) eps = np.finfo(np.float32).eps np.testing.assert_allclose( - 0, imag_matrix.eval(), rtol=0, atol=eps * 3 * 4) + 0, self.evaluate(imag_matrix), rtol=0, atol=eps * 3 * 4) + @test_util.run_deprecated_v1 def test_convolution_kernel_same_as_first_row_of_to_dense(self): spectrum = [[3., 2., 1.], [2., 1.5, 1.]] with self.cached_session(): @@ -287,8 +320,9 @@ class LinearOperatorCirculantTestNonHermitianSpectrum( self.assertAllEqual((2, 3), h.get_shape()) self.assertAllEqual((2, 3, 3), c.get_shape()) - self.assertAllClose(h.eval(), c.eval()[:, :, 0]) + self.assertAllClose(h.eval(), self.evaluate(c)[:, :, 0]) + @test_util.run_deprecated_v1 def test_assert_non_singular_fails_for_singular_operator(self): spectrum = math_ops.cast([0, 4, 2j + 2], dtypes.complex64) operator = linalg.LinearOperatorCirculant(spectrum) @@ -296,12 +330,14 @@ class LinearOperatorCirculantTestNonHermitianSpectrum( with self.assertRaisesOpError("Singular operator"): operator.assert_non_singular().run() + @test_util.run_deprecated_v1 def test_assert_non_singular_does_not_fail_for_non_singular_operator(self): spectrum = math_ops.cast([-3j, 4, 2j + 2], dtypes.complex64) operator = linalg.LinearOperatorCirculant(spectrum) with self.cached_session(): operator.assert_non_singular().run() # Should not fail + @test_util.run_deprecated_v1 def test_assert_positive_definite_fails_for_non_positive_definite(self): spectrum = math_ops.cast([6., 4, 2j], dtypes.complex64) operator = linalg.LinearOperatorCirculant(spectrum) @@ -309,6 +345,7 @@ class LinearOperatorCirculantTestNonHermitianSpectrum( with self.assertRaisesOpError("Not positive definite"): operator.assert_positive_definite().run() + @test_util.run_deprecated_v1 def test_assert_positive_definite_does_not_fail_when_pos_def(self): spectrum = math_ops.cast([6., 4, 2j + 2], dtypes.complex64) operator = linalg.LinearOperatorCirculant(spectrum) @@ -397,8 +434,8 @@ class LinearOperatorCirculant2DBaseTest(object): x = np.zeros(block_shape) # x is a basis vector. x[n0, n1] = 1.0 - fft_x = math_ops.fft2d(x.astype(np.complex64)) - h_convolve_x = math_ops.ifft2d(spectrum * fft_x) + fft_x = fft_ops.fft2d(x.astype(np.complex64)) + h_convolve_x = fft_ops.ifft2d(spectrum * fft_x) # We want the flat version of the action of the operator on a basis # vector, not the block version. h_convolve_x = array_ops.reshape(h_convolve_x, shape[:-1]) @@ -421,7 +458,9 @@ class LinearOperatorCirculant2DTestHermitianSpectrum( def _dtypes_to_test(self): return [dtypes.float32, dtypes.complex64] - def _operator_and_matrix(self, build_info, dtype, use_placeholder): + def _operator_and_matrix( + self, build_info, dtype, use_placeholder, + ensure_self_adjoint_and_pd=False): shape = build_info.shape # For this test class, we are creating Hermitian spectrums. # We also want the spectrum to have eigenvalues bounded away from zero. @@ -435,14 +474,14 @@ class LinearOperatorCirculant2DTestHermitianSpectrum( # = IFFT[EvenPartOf[pre_spectrum]] # is the IFFT of something that is also bounded away from zero. # Therefore, FFT[pre_h] would be a well-conditioned spectrum. - pre_h = math_ops.ifft2d(pre_spectrum_c) + pre_h = fft_ops.ifft2d(pre_spectrum_c) # A spectrum is Hermitian iff it is the DFT of a real convolution kernel. # So we will make spectrum = FFT[h], for real valued h. h = math_ops.real(pre_h) h_c = _to_complex(h) - spectrum = math_ops.fft2d(h_c) + spectrum = fft_ops.fft2d(h_c) lin_op_spectrum = spectrum @@ -450,7 +489,10 @@ class LinearOperatorCirculant2DTestHermitianSpectrum( lin_op_spectrum = array_ops.placeholder_with_default(spectrum, shape=None) operator = linalg.LinearOperatorCirculant2D( - lin_op_spectrum, input_output_dtype=dtype) + lin_op_spectrum, + is_positive_definite=True if ensure_self_adjoint_and_pd else None, + is_self_adjoint=True if ensure_self_adjoint_and_pd else None, + input_output_dtype=dtype) mat = self._spectrum_to_circulant_2d(spectrum, shape, dtype=dtype) @@ -470,7 +512,14 @@ class LinearOperatorCirculant2DTestNonHermitianSpectrum( def _dtypes_to_test(self): return [dtypes.complex64] - def _operator_and_matrix(self, build_info, dtype, use_placeholder): + @property + def _tests_to_skip(self): + return ["cholesky"] + + def _operator_and_matrix( + self, build_info, dtype, use_placeholder, + ensure_self_adjoint_and_pd=False): + del ensure_self_adjoint_and_pd shape = build_info.shape # Will be well conditioned enough to get accurate solves. spectrum = linear_operator_test_util.random_sign_uniform( @@ -491,6 +540,7 @@ class LinearOperatorCirculant2DTestNonHermitianSpectrum( return operator, mat + @test_util.run_deprecated_v1 def test_real_hermitian_spectrum_gives_real_symmetric_operator(self): with self.cached_session() as sess: # This is a real and hermitian spectrum. @@ -508,6 +558,7 @@ class LinearOperatorCirculant2DTestNonHermitianSpectrum( np.testing.assert_allclose(0, imag_matrix, atol=1e-6) self.assertAllClose(matrix, matrix_transpose, atol=0) + @test_util.run_deprecated_v1 def test_real_spectrum_gives_self_adjoint_operator(self): with self.cached_session() as sess: # This is a real and hermitian spectrum. @@ -519,9 +570,10 @@ class LinearOperatorCirculant2DTestNonHermitianSpectrum( self.assertEqual(matrix_tensor.dtype, linear_operator_circulant._DTYPE_COMPLEX) matrix_h = linalg.adjoint(matrix_tensor) - matrix, matrix_h = sess.run([matrix_tensor, matrix_h]) + matrix, matrix_h = self.evaluate([matrix_tensor, matrix_h]) self.assertAllClose(matrix, matrix_h, atol=0) + @test_util.run_deprecated_v1 def test_assert_non_singular_fails_for_singular_operator(self): spectrum = math_ops.cast([[0, 4], [2j + 2, 3.]], dtypes.complex64) operator = linalg.LinearOperatorCirculant2D(spectrum) @@ -529,12 +581,14 @@ class LinearOperatorCirculant2DTestNonHermitianSpectrum( with self.assertRaisesOpError("Singular operator"): operator.assert_non_singular().run() + @test_util.run_deprecated_v1 def test_assert_non_singular_does_not_fail_for_non_singular_operator(self): spectrum = math_ops.cast([[-3j, 4], [2j + 2, 3.]], dtypes.complex64) operator = linalg.LinearOperatorCirculant2D(spectrum) with self.cached_session(): operator.assert_non_singular().run() # Should not fail + @test_util.run_deprecated_v1 def test_assert_positive_definite_fails_for_non_positive_definite(self): spectrum = math_ops.cast([[6., 4], [2j, 3.]], dtypes.complex64) operator = linalg.LinearOperatorCirculant2D(spectrum) @@ -542,6 +596,7 @@ class LinearOperatorCirculant2DTestNonHermitianSpectrum( with self.assertRaisesOpError("Not positive definite"): operator.assert_positive_definite().run() + @test_util.run_deprecated_v1 def test_assert_positive_definite_does_not_fail_when_pos_def(self): spectrum = math_ops.cast([[6., 4], [2j + 2, 3.]], dtypes.complex64) operator = linalg.LinearOperatorCirculant2D(spectrum) @@ -580,6 +635,7 @@ class LinearOperatorCirculant3DTest(test.TestCase): with spectral_ops_test_util.fft_kernel_label_map(): yield sess + @test_util.run_deprecated_v1 def test_real_spectrum_gives_self_adjoint_operator(self): with self.cached_session() as sess: # This is a real and hermitian spectrum. @@ -593,16 +649,17 @@ class LinearOperatorCirculant3DTest(test.TestCase): linear_operator_circulant._DTYPE_COMPLEX) matrix_h = linalg.adjoint(matrix_tensor) - matrix, matrix_h = sess.run([matrix_tensor, matrix_h]) + matrix, matrix_h = self.evaluate([matrix_tensor, matrix_h]) self.assertAllEqual((2, 2 * 3 * 5, 2 * 3 * 5), matrix.shape) self.assertAllClose(matrix, matrix_h) + @test_util.run_deprecated_v1 def test_defining_operator_using_real_convolution_kernel(self): with self.cached_session(): convolution_kernel = linear_operator_test_util.random_normal( shape=(2, 2, 3, 5), dtype=dtypes.float32) # Convolution kernel is real ==> spectrum is Hermitian. - spectrum = math_ops.fft3d( + spectrum = fft_ops.fft3d( math_ops.cast(convolution_kernel, dtypes.complex64)) # spectrum is Hermitian ==> operator is real. @@ -615,6 +672,7 @@ class LinearOperatorCirculant3DTest(test.TestCase): self.assertAllEqual((2, 2 * 3 * 5, 2 * 3 * 5), matrix.shape) np.testing.assert_allclose(0, np.imag(matrix), atol=1e-6) + @test_util.run_deprecated_v1 def test_defining_spd_operator_by_taking_real_part(self): with self.cached_session() as sess: # S is real and positive. @@ -634,7 +692,7 @@ class LinearOperatorCirculant3DTest(test.TestCase): # = H1 + H2 # where H1 is real since it is Hermitian, # and H2 is imaginary since it is anti-Hermitian. - ifft_s = math_ops.ifft3d(math_ops.cast(s, dtypes.complex64)) + ifft_s = fft_ops.ifft3d(math_ops.cast(s, dtypes.complex64)) # Throw away H2, keep H1. real_ifft_s = math_ops.real(ifft_s) @@ -642,7 +700,7 @@ class LinearOperatorCirculant3DTest(test.TestCase): # This is the perfect spectrum! # spectrum = DFT[H1] # = S1, - fft_real_ifft_s = math_ops.fft3d( + fft_real_ifft_s = fft_ops.fft3d( math_ops.cast(real_ifft_s, dtypes.complex64)) # S1 is Hermitian ==> operator is real. @@ -665,7 +723,7 @@ class LinearOperatorCirculant3DTest(test.TestCase): # S2 is anti-Hermitian ==> operator is imaginary. # S2 is real ==> operator is self-adjoint. imag_ifft_s = math_ops.imag(ifft_s) - fft_imag_ifft_s = math_ops.fft3d( + fft_imag_ifft_s = fft_ops.fft3d( 1j * math_ops.cast(imag_ifft_s, dtypes.complex64)) operator_imag = linalg.LinearOperatorCirculant3D(fft_imag_ifft_s) diff --git a/tensorflow/python/kernel_tests/linalg/linear_operator_composition_test.py b/tensorflow/python/kernel_tests/linalg/linear_operator_composition_test.py index 02f56db596..214b73aa2f 100644 --- a/tensorflow/python/kernel_tests/linalg/linear_operator_composition_test.py +++ b/tensorflow/python/kernel_tests/linalg/linear_operator_composition_test.py @@ -21,6 +21,7 @@ import numpy as np from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops.linalg import linalg as linalg_lib @@ -42,8 +43,12 @@ class SquareLinearOperatorCompositionTest( self._rtol[dtypes.float32] = 1e-4 self._rtol[dtypes.complex64] = 1e-4 + @property + def _tests_to_skip(self): + # Cholesky not implemented. + return ["cholesky"] + def _operator_and_matrix(self, build_info, dtype, use_placeholder): - sess = ops.get_default_session() shape = list(build_info.shape) # Either 1 or 2 matrices, depending. @@ -175,6 +180,7 @@ class NonSquareLinearOperatorCompositionTest( return operator, mat + @test_util.run_deprecated_v1 def test_static_shapes(self): operators = [ linalg.LinearOperatorFullMatrix(rng.rand(2, 3, 4)), @@ -183,6 +189,7 @@ class NonSquareLinearOperatorCompositionTest( operator = linalg.LinearOperatorComposition(operators) self.assertAllEqual((2, 3, 5), operator.shape) + @test_util.run_deprecated_v1 def test_shape_tensors_when_statically_available(self): operators = [ linalg.LinearOperatorFullMatrix(rng.rand(2, 3, 4)), @@ -192,6 +199,7 @@ class NonSquareLinearOperatorCompositionTest( with self.cached_session(): self.assertAllEqual((2, 3, 5), operator.shape_tensor().eval()) + @test_util.run_deprecated_v1 def test_shape_tensors_when_only_dynamically_available(self): mat_1 = rng.rand(1, 2, 3, 4) mat_2 = rng.rand(1, 2, 4, 5) diff --git a/tensorflow/python/kernel_tests/linalg/linear_operator_diag_test.py b/tensorflow/python/kernel_tests/linalg/linear_operator_diag_test.py index 0758349531..dcbc0dd7c9 100644 --- a/tensorflow/python/kernel_tests/linalg/linear_operator_diag_test.py +++ b/tensorflow/python/kernel_tests/linalg/linear_operator_diag_test.py @@ -17,6 +17,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import linalg_ops from tensorflow.python.ops import math_ops @@ -32,17 +33,26 @@ class LinearOperatorDiagTest( linear_operator_test_util.SquareLinearOperatorDerivedClassTest): """Most tests done in the base class LinearOperatorDerivedClassTest.""" - def _operator_and_matrix(self, build_info, dtype, use_placeholder): + def _operator_and_matrix( + self, build_info, dtype, use_placeholder, + ensure_self_adjoint_and_pd=False): shape = list(build_info.shape) diag = linear_operator_test_util.random_sign_uniform( shape[:-1], minval=1., maxval=2., dtype=dtype) + if ensure_self_adjoint_and_pd: + # Abs on complex64 will result in a float32, so we cast back up. + diag = math_ops.cast(math_ops.abs(diag), dtype=dtype) + lin_op_diag = diag if use_placeholder: lin_op_diag = array_ops.placeholder_with_default(diag, shape=None) - operator = linalg.LinearOperatorDiag(lin_op_diag) + operator = linalg.LinearOperatorDiag( + lin_op_diag, + is_self_adjoint=True if ensure_self_adjoint_and_pd else None, + is_positive_definite=True if ensure_self_adjoint_and_pd else None) matrix = array_ops.matrix_diag(diag) @@ -71,6 +81,7 @@ class LinearOperatorDiagTest( with self.assertRaisesOpError("non-positive real.*not positive definite"): operator.assert_positive_definite().run() + @test_util.run_deprecated_v1 def test_assert_positive_definite_does_not_raise_if_pd_and_complex(self): with self.cached_session(): x = [1., 2.] @@ -87,6 +98,7 @@ class LinearOperatorDiagTest( with self.assertRaisesOpError("Singular operator"): operator.assert_non_singular().run() + @test_util.run_deprecated_v1 def test_assert_non_singular_does_not_raise_for_complex_nonsingular(self): with self.cached_session(): x = [1., 0.] @@ -104,6 +116,7 @@ class LinearOperatorDiagTest( with self.assertRaisesOpError("imaginary.*not self-adjoint"): operator.assert_self_adjoint().run() + @test_util.run_deprecated_v1 def test_assert_self_adjoint_does_not_raise_for_diag_with_zero_imag(self): with self.cached_session(): x = [1., 0.] @@ -138,12 +151,52 @@ class LinearOperatorDiagTest( operator_matmul = operator.matmul(x) mat_matmul = math_ops.matmul(mat, x) self.assertAllEqual(operator_matmul.get_shape(), mat_matmul.get_shape()) - self.assertAllClose(*sess.run([operator_matmul, mat_matmul])) + self.assertAllClose(*self.evaluate([operator_matmul, mat_matmul])) operator_solve = operator.solve(x) mat_solve = linalg_ops.matrix_solve(mat, x) self.assertAllEqual(operator_solve.get_shape(), mat_solve.get_shape()) - self.assertAllClose(*sess.run([operator_solve, mat_solve])) + self.assertAllClose(*self.evaluate([operator_solve, mat_solve])) + + def test_diag_matmul(self): + operator1 = linalg_lib.LinearOperatorDiag([2., 3.]) + operator2 = linalg_lib.LinearOperatorDiag([1., 2.]) + operator3 = linalg_lib.LinearOperatorScaledIdentity( + num_rows=2, multiplier=3.) + operator_matmul = operator1.matmul(operator2) + self.assertTrue(isinstance( + operator_matmul, + linalg_lib.LinearOperatorDiag)) + self.assertAllClose([2., 6.], self.evaluate(operator_matmul.diag)) + + operator_matmul = operator2.matmul(operator1) + self.assertTrue(isinstance( + operator_matmul, + linalg_lib.LinearOperatorDiag)) + self.assertAllClose([2., 6.], self.evaluate(operator_matmul.diag)) + + operator_matmul = operator1.matmul(operator3) + self.assertTrue(isinstance( + operator_matmul, + linalg_lib.LinearOperatorDiag)) + self.assertAllClose([6., 9.], self.evaluate(operator_matmul.diag)) + + operator_matmul = operator3.matmul(operator1) + self.assertTrue(isinstance( + operator_matmul, + linalg_lib.LinearOperatorDiag)) + self.assertAllClose([6., 9.], self.evaluate(operator_matmul.diag)) + + def test_diag_cholesky_type(self): + diag = [1., 3., 5., 8.] + operator = linalg.LinearOperatorDiag( + diag, + is_positive_definite=True, + is_self_adjoint=True, + ) + self.assertTrue(isinstance( + operator.cholesky(), + linalg.LinearOperatorDiag)) if __name__ == "__main__": diff --git a/tensorflow/python/kernel_tests/linalg/linear_operator_full_matrix_test.py b/tensorflow/python/kernel_tests/linalg/linear_operator_full_matrix_test.py index 8c2d2cf077..aff0b1ae14 100644 --- a/tensorflow/python/kernel_tests/linalg/linear_operator_full_matrix_test.py +++ b/tensorflow/python/kernel_tests/linalg/linear_operator_full_matrix_test.py @@ -20,6 +20,7 @@ from __future__ import print_function import numpy as np from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops.linalg import linalg as linalg_lib @@ -33,7 +34,9 @@ class SquareLinearOperatorFullMatrixTest( linear_operator_test_util.SquareLinearOperatorDerivedClassTest): """Most tests done in the base class LinearOperatorDerivedClassTest.""" - def _operator_and_matrix(self, build_info, dtype, use_placeholder): + def _operator_and_matrix( + self, build_info, dtype, use_placeholder, + ensure_self_adjoint_and_pd=False): shape = list(build_info.shape) matrix = linear_operator_test_util.random_positive_definite_matrix( @@ -44,7 +47,12 @@ class SquareLinearOperatorFullMatrixTest( if use_placeholder: lin_op_matrix = array_ops.placeholder_with_default(matrix, shape=None) - operator = linalg.LinearOperatorFullMatrix(lin_op_matrix, is_square=True) + # Set the hints to none to test non-symmetric PD code paths. + operator = linalg.LinearOperatorFullMatrix( + lin_op_matrix, + is_square=True, + is_self_adjoint=True if ensure_self_adjoint_and_pd else None, + is_positive_definite=True if ensure_self_adjoint_and_pd else None) return operator, matrix @@ -62,6 +70,7 @@ class SquareLinearOperatorFullMatrixTest( # Auto-detected. self.assertTrue(operator.is_square) + @test_util.run_deprecated_v1 def test_assert_non_singular_raises_if_cond_too_big_but_finite(self): with self.cached_session(): tril = linear_operator_test_util.random_tril_matrix( @@ -123,7 +132,13 @@ class SquareLinearOperatorFullMatrixSymmetricPositiveDefiniteTest( def _dtypes_to_test(self): return [dtypes.float32, dtypes.float64] - def _operator_and_matrix(self, build_info, dtype, use_placeholder): + def _operator_and_matrix( + self, build_info, dtype, use_placeholder, + ensure_self_adjoint_and_pd=False): + + # Matrix is always symmetric and positive definite in this class. + del ensure_self_adjoint_and_pd + shape = list(build_info.shape) matrix = linear_operator_test_util.random_positive_definite_matrix( @@ -134,7 +149,11 @@ class SquareLinearOperatorFullMatrixSymmetricPositiveDefiniteTest( if use_placeholder: lin_op_matrix = array_ops.placeholder_with_default(matrix, shape=None) - operator = linalg.LinearOperatorFullMatrix(lin_op_matrix, is_square=True) + operator = linalg.LinearOperatorFullMatrix( + lin_op_matrix, + is_square=True, + is_self_adjoint=True, + is_positive_definite=True) return operator, matrix diff --git a/tensorflow/python/kernel_tests/linalg/linear_operator_identity_test.py b/tensorflow/python/kernel_tests/linalg/linear_operator_identity_test.py index 465a8194dd..2da5e712d7 100644 --- a/tensorflow/python/kernel_tests/linalg/linear_operator_identity_test.py +++ b/tensorflow/python/kernel_tests/linalg/linear_operator_identity_test.py @@ -20,8 +20,10 @@ from __future__ import print_function import numpy as np from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import linalg_ops +from tensorflow.python.ops import math_ops from tensorflow.python.ops import random_ops from tensorflow.python.ops.linalg import linalg as linalg_lib from tensorflow.python.ops.linalg import linear_operator_test_util @@ -41,7 +43,12 @@ class LinearOperatorIdentityTest( # 16bit. return [dtypes.float32, dtypes.float64, dtypes.complex64, dtypes.complex128] - def _operator_and_matrix(self, build_info, dtype, use_placeholder): + def _operator_and_matrix( + self, build_info, dtype, use_placeholder, + ensure_self_adjoint_and_pd=False): + # Identity matrix is already Hermitian Positive Definite. + del ensure_self_adjoint_and_pd + shape = list(build_info.shape) assert shape[-1] == shape[-2] @@ -54,16 +61,19 @@ class LinearOperatorIdentityTest( return operator, mat + @test_util.run_deprecated_v1 def test_assert_positive_definite(self): with self.cached_session(): operator = linalg_lib.LinearOperatorIdentity(num_rows=2) operator.assert_positive_definite().run() # Should not fail + @test_util.run_deprecated_v1 def test_assert_non_singular(self): with self.cached_session(): operator = linalg_lib.LinearOperatorIdentity(num_rows=2) operator.assert_non_singular().run() # Should not fail + @test_util.run_deprecated_v1 def test_assert_self_adjoint(self): with self.cached_session(): operator = linalg_lib.LinearOperatorIdentity(num_rows=2) @@ -77,7 +87,7 @@ class LinearOperatorIdentityTest( num_rows=2, dtype=dtypes.float16) x = rng.randn(2, 3).astype(np.float16) y = operator.matmul(x) - self.assertAllClose(x, y.eval()) + self.assertAllClose(x, self.evaluate(y)) def test_non_scalar_num_rows_raises_static(self): with self.assertRaisesRegexp(ValueError, "must be a 0-D Tensor"): @@ -103,6 +113,7 @@ class LinearOperatorIdentityTest( with self.assertRaisesRegexp(ValueError, "must be non-negative"): linalg_lib.LinearOperatorIdentity(num_rows=2, batch_shape=[-2]) + @test_util.run_deprecated_v1 def test_non_scalar_num_rows_raises_dynamic(self): with self.cached_session(): num_rows = array_ops.placeholder(dtypes.int32) @@ -111,6 +122,7 @@ class LinearOperatorIdentityTest( with self.assertRaisesOpError("must be a 0-D Tensor"): operator.to_dense().eval(feed_dict={num_rows: [2]}) + @test_util.run_deprecated_v1 def test_negative_num_rows_raises_dynamic(self): with self.cached_session(): num_rows = array_ops.placeholder(dtypes.int32) @@ -119,6 +131,7 @@ class LinearOperatorIdentityTest( with self.assertRaisesOpError("must be non-negative"): operator.to_dense().eval(feed_dict={num_rows: -2}) + @test_util.run_deprecated_v1 def test_non_1d_batch_shape_raises_dynamic(self): with self.cached_session(): batch_shape = array_ops.placeholder(dtypes.int32) @@ -127,6 +140,7 @@ class LinearOperatorIdentityTest( with self.assertRaisesOpError("must be a 1-D"): operator.to_dense().eval(feed_dict={batch_shape: 2}) + @test_util.run_deprecated_v1 def test_negative_batch_shape_raises_dynamic(self): with self.cached_session(): batch_shape = array_ops.placeholder(dtypes.int32) @@ -141,6 +155,7 @@ class LinearOperatorIdentityTest( with self.assertRaisesRegexp(ValueError, "Dimensions.*not compatible"): operator.matmul(x) + @test_util.run_deprecated_v1 def test_wrong_matrix_dimensions_raises_dynamic(self): num_rows = array_ops.placeholder(dtypes.int32) x = array_ops.placeholder(dtypes.float32) @@ -164,8 +179,9 @@ class LinearOperatorIdentityTest( expected = x self.assertAllEqual(operator_matmul.get_shape(), expected.get_shape()) - self.assertAllClose(*sess.run([operator_matmul, expected])) + self.assertAllClose(*self.evaluate([operator_matmul, expected])) + @test_util.run_deprecated_v1 def test_default_batch_shape_broadcasts_with_everything_dynamic(self): # These cannot be done in the automated (base test class) tests since they # test shapes that tf.batch_matmul cannot handle. @@ -201,8 +217,9 @@ class LinearOperatorIdentityTest( operator_matmul = operator.matmul(x) self.assertAllEqual(operator_matmul.get_shape(), expected.get_shape()) - self.assertAllClose(*sess.run([operator_matmul, expected])) + self.assertAllClose(*self.evaluate([operator_matmul, expected])) + @test_util.run_deprecated_v1 def test_broadcast_matmul_dynamic_shapes(self): # These cannot be done in the automated (base test class) tests since they # test shapes that tf.batch_matmul cannot handle. @@ -242,6 +259,16 @@ class LinearOperatorIdentityTest( is_non_singular=None, ) + def test_identity_cholesky_type(self): + operator = linalg_lib.LinearOperatorIdentity( + num_rows=2, + is_positive_definite=True, + is_self_adjoint=True, + ) + self.assertTrue(isinstance( + operator.cholesky(), + linalg_lib.LinearOperatorIdentity)) + class LinearOperatorScaledIdentityTest( linear_operator_test_util.SquareLinearOperatorDerivedClassTest): @@ -253,7 +280,10 @@ class LinearOperatorScaledIdentityTest( # 16bit. return [dtypes.float32, dtypes.float64, dtypes.complex64, dtypes.complex128] - def _operator_and_matrix(self, build_info, dtype, use_placeholder): + def _operator_and_matrix( + self, build_info, dtype, use_placeholder, + ensure_self_adjoint_and_pd=False): + shape = list(build_info.shape) assert shape[-1] == shape[-2] @@ -266,6 +296,9 @@ class LinearOperatorScaledIdentityTest( multiplier = linear_operator_test_util.random_sign_uniform( shape=batch_shape, minval=1., maxval=2., dtype=dtype) + if ensure_self_adjoint_and_pd: + # Abs on complex64 will result in a float32, so we cast back up. + multiplier = math_ops.cast(math_ops.abs(multiplier), dtype=dtype) # Nothing to feed since LinearOperatorScaledIdentity takes no Tensor args. lin_op_multiplier = multiplier @@ -275,7 +308,10 @@ class LinearOperatorScaledIdentityTest( multiplier, shape=None) operator = linalg_lib.LinearOperatorScaledIdentity( - num_rows, lin_op_multiplier) + num_rows, + lin_op_multiplier, + is_self_adjoint=True if ensure_self_adjoint_and_pd else None, + is_positive_definite=True if ensure_self_adjoint_and_pd else None) multiplier_matrix = array_ops.expand_dims( array_ops.expand_dims(multiplier, -1), -1) @@ -284,6 +320,7 @@ class LinearOperatorScaledIdentityTest( return operator, matrix + @test_util.run_deprecated_v1 def test_assert_positive_definite_does_not_raise_when_positive(self): with self.cached_session(): operator = linalg_lib.LinearOperatorScaledIdentity( @@ -297,6 +334,7 @@ class LinearOperatorScaledIdentityTest( with self.assertRaisesOpError("not positive definite"): operator.assert_positive_definite().run() + @test_util.run_deprecated_v1 def test_assert_non_singular_does_not_raise_when_non_singular(self): with self.cached_session(): operator = linalg_lib.LinearOperatorScaledIdentity( @@ -310,6 +348,7 @@ class LinearOperatorScaledIdentityTest( with self.assertRaisesOpError("was singular"): operator.assert_non_singular().run() + @test_util.run_deprecated_v1 def test_assert_self_adjoint_does_not_raise_when_self_adjoint(self): with self.cached_session(): operator = linalg_lib.LinearOperatorScaledIdentity( @@ -332,7 +371,7 @@ class LinearOperatorScaledIdentityTest( num_rows=2, multiplier=multiplier) x = rng.randn(2, 3).astype(np.float16) y = operator.matmul(x) - self.assertAllClose(multiplier[..., None, None] * x, y.eval()) + self.assertAllClose(multiplier[..., None, None] * x, self.evaluate(y)) def test_non_scalar_num_rows_raises_static(self): # Many "test_...num_rows" tests are performed in LinearOperatorIdentity. @@ -347,6 +386,7 @@ class LinearOperatorScaledIdentityTest( with self.assertRaisesRegexp(ValueError, "Dimensions.*not compatible"): operator.matmul(x) + @test_util.run_deprecated_v1 def test_wrong_matrix_dimensions_raises_dynamic(self): num_rows = array_ops.placeholder(dtypes.int32) x = array_ops.placeholder(dtypes.float32) @@ -378,13 +418,13 @@ class LinearOperatorScaledIdentityTest( expected = x * 2.2 + zeros operator_matmul = operator.matmul(x) self.assertAllEqual(operator_matmul.get_shape(), expected.get_shape()) - self.assertAllClose(*sess.run([operator_matmul, expected])) + self.assertAllClose(*self.evaluate([operator_matmul, expected])) # Test solve expected = x / 2.2 + zeros operator_solve = operator.solve(x) self.assertAllEqual(operator_solve.get_shape(), expected.get_shape()) - self.assertAllClose(*sess.run([operator_solve, expected])) + self.assertAllClose(*self.evaluate([operator_solve, expected])) def test_broadcast_matmul_and_solve_scalar_scale_multiplier(self): # These cannot be done in the automated (base test class) tests since they @@ -404,13 +444,13 @@ class LinearOperatorScaledIdentityTest( expected = x * 2.2 operator_matmul = operator.matmul(x) self.assertAllEqual(operator_matmul.get_shape(), expected.get_shape()) - self.assertAllClose(*sess.run([operator_matmul, expected])) + self.assertAllClose(*self.evaluate([operator_matmul, expected])) # Test solve expected = x / 2.2 operator_solve = operator.solve(x) self.assertAllEqual(operator_solve.get_shape(), expected.get_shape()) - self.assertAllClose(*sess.run([operator_solve, expected])) + self.assertAllClose(*self.evaluate([operator_solve, expected])) def test_is_x_flags(self): operator = linalg_lib.LinearOperatorScaledIdentity( @@ -420,6 +460,41 @@ class LinearOperatorScaledIdentityTest( self.assertTrue(operator.is_non_singular) self.assertTrue(operator.is_self_adjoint is None) + def test_identity_matmul(self): + operator1 = linalg_lib.LinearOperatorIdentity(num_rows=2) + operator2 = linalg_lib.LinearOperatorScaledIdentity( + num_rows=2, multiplier=3.) + self.assertTrue(isinstance( + operator1.matmul(operator1), + linalg_lib.LinearOperatorIdentity)) + + self.assertTrue(isinstance( + operator1.matmul(operator1), + linalg_lib.LinearOperatorIdentity)) + + operator_matmul = operator1.matmul(operator2) + self.assertTrue(isinstance( + operator_matmul, + linalg_lib.LinearOperatorScaledIdentity)) + self.assertAllClose(3., self.evaluate(operator_matmul.multiplier)) + + operator_matmul = operator2.matmul(operator1) + self.assertTrue(isinstance( + operator_matmul, + linalg_lib.LinearOperatorScaledIdentity)) + self.assertAllClose(3., self.evaluate(operator_matmul.multiplier)) + + def test_scaled_identity_cholesky_type(self): + operator = linalg_lib.LinearOperatorScaledIdentity( + num_rows=2, + multiplier=3., + is_positive_definite=True, + is_self_adjoint=True, + ) + self.assertTrue(isinstance( + operator.cholesky(), + linalg_lib.LinearOperatorScaledIdentity)) + if __name__ == "__main__": test.main() diff --git a/tensorflow/python/kernel_tests/linalg/linear_operator_inversion_test.py b/tensorflow/python/kernel_tests/linalg/linear_operator_inversion_test.py new file mode 100644 index 0000000000..9344c526ee --- /dev/null +++ b/tensorflow/python/kernel_tests/linalg/linear_operator_inversion_test.py @@ -0,0 +1,130 @@ +# Copyright 2018 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. +# ============================================================================== + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.framework import dtypes +from tensorflow.python.ops import array_ops +from tensorflow.python.ops.linalg import linalg as linalg_lib +from tensorflow.python.ops.linalg import linear_operator_inversion +from tensorflow.python.ops.linalg import linear_operator_test_util +from tensorflow.python.platform import test + +linalg = linalg_lib + +LinearOperatorInversion = linear_operator_inversion.LinearOperatorInversion # pylint: disable=invalid-name + + +class LinearOperatorInversionTest( + linear_operator_test_util.SquareLinearOperatorDerivedClassTest): + """Most tests done in the base class LinearOperatorDerivedClassTest.""" + + def setUp(self): + self._atol[dtypes.complex64] = 1e-5 + self._rtol[dtypes.complex64] = 1e-5 + + def _operator_and_matrix(self, + build_info, + dtype, + use_placeholder, + ensure_self_adjoint_and_pd=False): + shape = list(build_info.shape) + + if ensure_self_adjoint_and_pd: + matrix = linear_operator_test_util.random_positive_definite_matrix( + shape, dtype, force_well_conditioned=True) + else: + matrix = linear_operator_test_util.random_tril_matrix( + shape, dtype, force_well_conditioned=True, remove_upper=True) + + lin_op_matrix = matrix + + if use_placeholder: + lin_op_matrix = array_ops.placeholder_with_default(matrix, shape=None) + + if ensure_self_adjoint_and_pd: + operator = LinearOperatorInversion( + linalg.LinearOperatorFullMatrix( + lin_op_matrix, is_positive_definite=True, is_self_adjoint=True)) + else: + operator = LinearOperatorInversion( + linalg.LinearOperatorLowerTriangular(lin_op_matrix)) + + return operator, linalg.inv(matrix) + + def test_base_operator_hint_used(self): + # The matrix values do not effect auto-setting of the flags. + matrix = [[1., 0.], [1., 1.]] + operator = linalg.LinearOperatorFullMatrix( + matrix, + is_positive_definite=True, + is_non_singular=True, + is_self_adjoint=False) + operator_inv = LinearOperatorInversion(operator) + self.assertTrue(operator_inv.is_positive_definite) + self.assertTrue(operator_inv.is_non_singular) + self.assertFalse(operator_inv.is_self_adjoint) + + def test_supplied_hint_used(self): + # The matrix values do not effect auto-setting of the flags. + matrix = [[1., 0.], [1., 1.]] + operator = linalg.LinearOperatorFullMatrix(matrix) + operator_inv = LinearOperatorInversion( + operator, + is_positive_definite=True, + is_non_singular=True, + is_self_adjoint=False) + self.assertTrue(operator_inv.is_positive_definite) + self.assertTrue(operator_inv.is_non_singular) + self.assertFalse(operator_inv.is_self_adjoint) + + def test_contradicting_hints_raise(self): + # The matrix values do not effect auto-setting of the flags. + matrix = [[1., 0.], [1., 1.]] + operator = linalg.LinearOperatorFullMatrix( + matrix, is_positive_definite=False) + with self.assertRaisesRegexp(ValueError, "positive-definite"): + LinearOperatorInversion(operator, is_positive_definite=True) + + operator = linalg.LinearOperatorFullMatrix(matrix, is_self_adjoint=False) + with self.assertRaisesRegexp(ValueError, "self-adjoint"): + LinearOperatorInversion(operator, is_self_adjoint=True) + + def test_singular_raises(self): + # The matrix values do not effect auto-setting of the flags. + matrix = [[1., 1.], [1., 1.]] + + operator = linalg.LinearOperatorFullMatrix(matrix, is_non_singular=False) + with self.assertRaisesRegexp(ValueError, "is_non_singular"): + LinearOperatorInversion(operator) + + operator = linalg.LinearOperatorFullMatrix(matrix) + with self.assertRaisesRegexp(ValueError, "is_non_singular"): + LinearOperatorInversion(operator, is_non_singular=False) + + def test_name(self): + matrix = [[11., 0.], [1., 8.]] + operator = linalg.LinearOperatorFullMatrix( + matrix, name="my_operator", is_non_singular=True) + + operator = LinearOperatorInversion(operator) + + self.assertEqual("my_operator_inv", operator.name) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/kernel_tests/linalg/linear_operator_kronecker_test.py b/tensorflow/python/kernel_tests/linalg/linear_operator_kronecker_test.py index f039b60f64..513b246803 100644 --- a/tensorflow/python/kernel_tests/linalg/linear_operator_kronecker_test.py +++ b/tensorflow/python/kernel_tests/linalg/linear_operator_kronecker_test.py @@ -21,9 +21,11 @@ import numpy as np from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops.linalg import linalg as linalg_lib from tensorflow.python.ops.linalg import linear_operator_kronecker as kronecker +from tensorflow.python.ops.linalg import linear_operator_lower_triangular as lower_triangular from tensorflow.python.ops.linalg import linear_operator_test_util from tensorflow.python.ops.linalg import linear_operator_util from tensorflow.python.platform import test @@ -52,6 +54,7 @@ def _kronecker_dense(factors): class KroneckerDenseTest(test.TestCase): + @test_util.run_deprecated_v1 def testKroneckerDenseMatrix(self): x = ops.convert_to_tensor([[2., 3.], [1., 2.]], dtype=dtypes.float32) y = ops.convert_to_tensor([[1., 2.], [5., -1.]], dtype=dtypes.float32) @@ -69,8 +72,8 @@ class KroneckerDenseTest(test.TestCase): [5., 10., -1., -2.]], dtype=dtypes.float32) with self.cached_session(): - self.assertAllClose(_kronecker_dense([x, y]).eval(), z.eval()) - self.assertAllClose(_kronecker_dense([y, x]).eval(), w.eval()) + self.assertAllClose(_kronecker_dense([x, y]).eval(), self.evaluate(z)) + self.assertAllClose(_kronecker_dense([y, x]).eval(), self.evaluate(w)) class SquareLinearOperatorKroneckerTest( @@ -99,7 +102,12 @@ class SquareLinearOperatorKroneckerTest( def _tests_to_skip(self): return ["det", "solve", "solve_with_broadcast"] - def _operator_and_matrix(self, build_info, dtype, use_placeholder): + def _operator_and_matrix( + self, build_info, dtype, use_placeholder, + ensure_self_adjoint_and_pd=False): + # Kronecker products constructed below will be from symmetric + # positive-definite matrices. + del ensure_self_adjoint_and_pd shape = list(build_info.shape) expected_factors = build_info.__dict__["factors"] matrices = [ @@ -116,7 +124,11 @@ class SquareLinearOperatorKroneckerTest( operator = kronecker.LinearOperatorKronecker( [linalg.LinearOperatorFullMatrix( - l, is_square=True) for l in lin_op_matrices]) + l, + is_square=True, + is_self_adjoint=True, + is_positive_definite=True) + for l in lin_op_matrices]) matrices = linear_operator_util.broadcast_matrix_batch_dims(matrices) @@ -180,6 +192,40 @@ class SquareLinearOperatorKroneckerTest( with self.assertRaisesRegexp(ValueError, ">=1 operators"): kronecker.LinearOperatorKronecker([]) + def test_kronecker_cholesky_type(self): + matrix = [[1., 0.], [0., 1.]] + operator = kronecker.LinearOperatorKronecker( + [ + linalg.LinearOperatorFullMatrix( + matrix, + is_positive_definite=True, + is_self_adjoint=True, + ), + linalg.LinearOperatorFullMatrix( + matrix, + is_positive_definite=True, + is_self_adjoint=True, + ), + ], + is_positive_definite=True, + is_self_adjoint=True, + ) + cholesky_factor = operator.cholesky() + self.assertTrue(isinstance( + cholesky_factor, + kronecker.LinearOperatorKronecker)) + self.assertEqual(2, len(cholesky_factor.operators)) + self.assertTrue( + isinstance( + cholesky_factor.operators[0], + lower_triangular.LinearOperatorLowerTriangular) + ) + self.assertTrue( + isinstance( + cholesky_factor.operators[1], + lower_triangular.LinearOperatorLowerTriangular) + ) + if __name__ == "__main__": test.main() diff --git a/tensorflow/python/kernel_tests/linalg/linear_operator_low_rank_update_test.py b/tensorflow/python/kernel_tests/linalg/linear_operator_low_rank_update_test.py index 207e5edf81..2920f3ae7e 100644 --- a/tensorflow/python/kernel_tests/linalg/linear_operator_low_rank_update_test.py +++ b/tensorflow/python/kernel_tests/linalg/linear_operator_low_rank_update_test.py @@ -69,7 +69,8 @@ class BaseLinearOperatorLowRankUpdatetest(object): return linear_operator_test_util.random_uniform( diag_shape, minval=1e-4, maxval=1., dtype=dtype) - def _operator_and_matrix(self, build_info, dtype, use_placeholder): + def _operator_and_matrix(self, build_info, dtype, use_placeholder, + ensure_self_adjoint_and_pd=False): # Recall A = L + UDV^H shape = list(build_info.shape) diag_shape = shape[:-1] @@ -93,7 +94,7 @@ class BaseLinearOperatorLowRankUpdatetest(object): lin_op_v = v # D - if self._is_diag_update_positive: + if self._is_diag_update_positive or ensure_self_adjoint_and_pd: diag_update = self._gen_positive_diag(dtype, diag_update_shape) else: diag_update = linear_operator_test_util.random_normal( @@ -178,6 +179,10 @@ class LinearOperatorLowRankUpdatetestWithDiagCannotUseCholesky( linear_operator_test_util.SquareLinearOperatorDerivedClassTest): """A = L + UDU^H, D !> 0, L > 0 ==> A !> 0 and we cannot use a Cholesky.""" + @property + def _tests_to_skip(self): + return ["cholesky"] + _use_diag_update = True _is_diag_update_positive = False _use_v = False @@ -217,6 +222,10 @@ class LinearOperatorLowRankUpdatetestNoDiagCannotUseCholesky( linear_operator_test_util.SquareLinearOperatorDerivedClassTest): """A = L + UV^H, L > 0 ==> A is not symmetric and we cannot use a Cholesky.""" + @property + def _tests_to_skip(self): + return ["cholesky"] + _use_diag_update = False _is_diag_update_positive = None _use_v = True diff --git a/tensorflow/python/kernel_tests/linalg/linear_operator_lower_triangular_test.py b/tensorflow/python/kernel_tests/linalg/linear_operator_lower_triangular_test.py index e3c8f5cb68..bd41f9ed9d 100644 --- a/tensorflow/python/kernel_tests/linalg/linear_operator_lower_triangular_test.py +++ b/tensorflow/python/kernel_tests/linalg/linear_operator_lower_triangular_test.py @@ -18,6 +18,7 @@ from __future__ import division from __future__ import print_function from tensorflow.python.ops import array_ops +from tensorflow.python.ops import math_ops from tensorflow.python.ops.linalg import linalg as linalg_lib from tensorflow.python.ops.linalg import linear_operator_test_util from tensorflow.python.platform import test @@ -29,6 +30,11 @@ class LinearOperatorLowerTriangularTest( linear_operator_test_util.SquareLinearOperatorDerivedClassTest): """Most tests done in the base class LinearOperatorDerivedClassTest.""" + @property + def _tests_to_skip(self): + # Cholesky does not make sense for triangular matrices. + return ["cholesky"] + def _operator_and_matrix(self, build_info, dtype, use_placeholder): shape = list(build_info.shape) # Upper triangle will be nonzero, but ignored. @@ -71,6 +77,30 @@ class LinearOperatorLowerTriangularTest( with self.assertRaisesRegexp(ValueError, "at least 2 dimensions"): linalg.LinearOperatorLowerTriangular([1.]) + def test_triangular_diag_matmul(self): + operator1 = linalg_lib.LinearOperatorLowerTriangular( + [[1., 0., 0.], [2., 1., 0.], [2., 3., 3.]]) + operator2 = linalg_lib.LinearOperatorDiag([2., 2., 3.]) + operator_matmul = operator1.matmul(operator2) + self.assertTrue(isinstance( + operator_matmul, + linalg_lib.LinearOperatorLowerTriangular)) + self.assertAllClose( + math_ops.matmul( + operator1.to_dense(), + operator2.to_dense()), + self.evaluate(operator_matmul.to_dense())) + + operator_matmul = operator2.matmul(operator1) + self.assertTrue(isinstance( + operator_matmul, + linalg_lib.LinearOperatorLowerTriangular)) + self.assertAllClose( + math_ops.matmul( + operator2.to_dense(), + operator1.to_dense()), + self.evaluate(operator_matmul.to_dense())) + if __name__ == "__main__": test.main() diff --git a/tensorflow/python/kernel_tests/linalg/linear_operator_test.py b/tensorflow/python/kernel_tests/linalg/linear_operator_test.py index 819347343b..8f8b15e8ed 100644 --- a/tensorflow/python/kernel_tests/linalg/linear_operator_test.py +++ b/tensorflow/python/kernel_tests/linalg/linear_operator_test.py @@ -22,6 +22,7 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import linalg_ops from tensorflow.python.ops import math_ops @@ -107,6 +108,7 @@ class LinearOperatorTest(test.TestCase): self.assertAllEqual(4, operator.domain_dimension) self.assertAllEqual(3, operator.range_dimension) + @test_util.run_deprecated_v1 def test_all_shape_methods_defined_by_the_one_method_shape(self): with self.cached_session(): shape = (1, 2, 3, 4) @@ -134,8 +136,9 @@ class LinearOperatorTest(test.TestCase): with self.cached_session(): operator_dense = operator.to_dense() self.assertAllEqual((2, 3, 4), operator_dense.get_shape()) - self.assertAllClose(matrix, operator_dense.eval()) + self.assertAllClose(matrix, self.evaluate(operator_dense)) + @test_util.run_deprecated_v1 def test_generic_to_dense_method_non_square_matrix_tensor(self): matrix = rng.randn(2, 3, 4) matrix_ph = array_ops.placeholder(dtypes.float64) @@ -152,7 +155,7 @@ class LinearOperatorTest(test.TestCase): with self.cached_session(): y = operator.matvec(x) self.assertAllEqual((2,), y.get_shape()) - self.assertAllClose([1., 2.], y.eval()) + self.assertAllClose([1., 2.], self.evaluate(y)) def test_solvevec(self): matrix = [[1., 0], [0., 2.]] @@ -161,7 +164,7 @@ class LinearOperatorTest(test.TestCase): with self.cached_session(): x = operator.solvevec(y) self.assertAllEqual((2,), x.get_shape()) - self.assertAllClose([1., 1 / 2.], x.eval()) + self.assertAllClose([1., 1 / 2.], self.evaluate(x)) def test_is_square_set_to_true_for_square_static_shapes(self): operator = LinearOperatorShape(shape=(2, 4, 4)) @@ -175,6 +178,7 @@ class LinearOperatorTest(test.TestCase): with self.assertRaisesRegexp(ValueError, "but.*was square"): _ = LinearOperatorShape(shape=(2, 4, 4), is_square=False).is_square + @test_util.run_deprecated_v1 def test_is_square_set_inconsistent_with_other_hints_raises(self): with self.assertRaisesRegexp(ValueError, "is always square"): matrix = array_ops.placeholder(dtypes.float32) @@ -185,6 +189,7 @@ class LinearOperatorTest(test.TestCase): LinearOperatorMatmulSolve( matrix, is_positive_definite=True, is_square=False) + @test_util.run_deprecated_v1 def test_non_square_operators_raise_on_determinant_and_solve(self): operator = LinearOperatorShape((2, 3)) with self.assertRaisesRegexp(NotImplementedError, "not be square"): @@ -199,6 +204,7 @@ class LinearOperatorTest(test.TestCase): LinearOperatorMatmulSolve( matrix, is_positive_definite=True, is_square=False) + @test_util.run_deprecated_v1 def test_is_square_manual_set_works(self): matrix = array_ops.placeholder(dtypes.float32) # Default is None. @@ -208,6 +214,80 @@ class LinearOperatorTest(test.TestCase): operator = LinearOperatorMatmulSolve(matrix, is_square=True) self.assertTrue(operator.is_square) + @test_util.run_deprecated_v1 + def test_linear_operator_matmul_hints_closed(self): + matrix = array_ops.placeholder(dtypes.float32) + operator1 = LinearOperatorMatmulSolve(matrix) + + operator_matmul = operator1.matmul(operator1) + + self.assertEqual(None, operator_matmul.is_square) + self.assertEqual(None, operator_matmul.is_non_singular) + self.assertEqual(None, operator_matmul.is_self_adjoint) + self.assertEqual(None, operator_matmul.is_positive_definite) + + operator2 = LinearOperatorMatmulSolve( + matrix, + is_non_singular=True, + is_self_adjoint=True, + is_positive_definite=True, + is_square=True, + ) + + operator_matmul = operator2.matmul(operator2) + + self.assertTrue(operator_matmul.is_square) + self.assertTrue(operator_matmul.is_non_singular) + self.assertTrue(operator_matmul.is_self_adjoint) + self.assertEqual(None, operator_matmul.is_positive_definite) + + @test_util.run_deprecated_v1 + def test_linear_operator_matmul_hints_false(self): + matrix = array_ops.placeholder(dtypes.float32) + operator1 = LinearOperatorMatmulSolve( + matrix, + is_non_singular=False, + is_self_adjoint=False, + is_positive_definite=False, + is_square=True, + ) + + operator_matmul = operator1.matmul(operator1) + + self.assertTrue(operator_matmul.is_square) + self.assertFalse(operator_matmul.is_non_singular) + self.assertEqual(None, operator_matmul.is_self_adjoint) + self.assertEqual(None, operator_matmul.is_positive_definite) + + operator2 = LinearOperatorMatmulSolve( + matrix, + is_non_singular=False, + is_self_adjoint=False, + is_positive_definite=False, + is_square=False, + ) + + operator_matmul = operator2.matmul(operator2) + + self.assertEqual(None, operator_matmul.is_square) + self.assertEqual(None, operator_matmul.is_non_singular) + self.assertEqual(None, operator_matmul.is_self_adjoint) + self.assertEqual(None, operator_matmul.is_positive_definite) + + @test_util.run_deprecated_v1 + def test_linear_operator_matmul_hint_infer_square(self): + matrix1 = array_ops.placeholder(shape=[2, 3], dtype=dtypes.float32) + matrix2 = array_ops.placeholder(shape=[3, 2], dtype=dtypes.float32) + matrix3 = array_ops.placeholder(shape=[3, 4], dtype=dtypes.float32) + + operator1 = LinearOperatorMatmulSolve(matrix1, is_square=False) + operator2 = LinearOperatorMatmulSolve(matrix2, is_square=False) + operator3 = LinearOperatorMatmulSolve(matrix3, is_square=False) + + self.assertTrue(operator1.matmul(operator2).is_square) + self.assertTrue(operator2.matmul(operator1).is_square) + self.assertFalse(operator1.matmul(operator3).is_square) + if __name__ == "__main__": test.main() diff --git a/tensorflow/python/kernel_tests/linalg/linear_operator_util_test.py b/tensorflow/python/kernel_tests/linalg/linear_operator_util_test.py index 31fb19e4a6..d1e6c37e35 100644 --- a/tensorflow/python/kernel_tests/linalg/linear_operator_util_test.py +++ b/tensorflow/python/kernel_tests/linalg/linear_operator_util_test.py @@ -21,6 +21,7 @@ import numpy as np from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import linalg_ops from tensorflow.python.ops import math_ops @@ -32,12 +33,14 @@ rng = np.random.RandomState(0) class AssertZeroImagPartTest(test.TestCase): + @test_util.run_deprecated_v1 def test_real_tensor_doesnt_raise(self): x = ops.convert_to_tensor([0., 2, 3]) with self.cached_session(): # Should not raise. linear_operator_util.assert_zero_imag_part(x, message="ABC123").run() + @test_util.run_deprecated_v1 def test_complex_tensor_with_imag_zero_doesnt_raise(self): x = ops.convert_to_tensor([1., 0, 3]) y = ops.convert_to_tensor([0., 0, 0]) @@ -57,6 +60,7 @@ class AssertZeroImagPartTest(test.TestCase): class AssertNoEntriesWithModulusZeroTest(test.TestCase): + @test_util.run_deprecated_v1 def test_nonzero_real_tensor_doesnt_raise(self): x = ops.convert_to_tensor([1., 2, 3]) with self.cached_session(): @@ -64,6 +68,7 @@ class AssertNoEntriesWithModulusZeroTest(test.TestCase): linear_operator_util.assert_no_entries_with_modulus_zero( x, message="ABC123").run() + @test_util.run_deprecated_v1 def test_nonzero_complex_tensor_doesnt_raise(self): x = ops.convert_to_tensor([1., 0, 3]) y = ops.convert_to_tensor([1., 2, 0]) @@ -102,8 +107,9 @@ class BroadcastMatrixBatchDimsTest(test.TestCase): self.assertTrue(isinstance(tensor, ops.Tensor)) with self.cached_session(): - self.assertAllClose(arr, tensor.eval()) + self.assertAllClose(arr, self.evaluate(tensor)) + @test_util.run_deprecated_v1 def test_static_dims_broadcast(self): # x.batch_shape = [3, 1, 2] # y.batch_shape = [4, 1] @@ -119,7 +125,7 @@ class BroadcastMatrixBatchDimsTest(test.TestCase): with self.cached_session() as sess: self.assertAllEqual(x_bc_expected.shape, x_bc.get_shape()) self.assertAllEqual(y_bc_expected.shape, y_bc.get_shape()) - x_bc_, y_bc_ = sess.run([x_bc, y_bc]) + x_bc_, y_bc_ = self.evaluate([x_bc, y_bc]) self.assertAllClose(x_bc_expected, x_bc_) self.assertAllClose(y_bc_expected, y_bc_) @@ -138,10 +144,11 @@ class BroadcastMatrixBatchDimsTest(test.TestCase): with self.cached_session() as sess: self.assertAllEqual(x_bc_expected.shape, x_bc.get_shape()) self.assertAllEqual(y_bc_expected.shape, y_bc.get_shape()) - x_bc_, y_bc_ = sess.run([x_bc, y_bc]) + x_bc_, y_bc_ = self.evaluate([x_bc, y_bc]) self.assertAllClose(x_bc_expected, x_bc_) self.assertAllClose(y_bc_expected, y_bc_) + @test_util.run_deprecated_v1 def test_dynamic_dims_broadcast_32bit(self): # x.batch_shape = [3, 1, 2] # y.batch_shape = [4, 1] @@ -162,6 +169,7 @@ class BroadcastMatrixBatchDimsTest(test.TestCase): self.assertAllClose(x_bc_expected, x_bc_) self.assertAllClose(y_bc_expected, y_bc_) + @test_util.run_deprecated_v1 def test_dynamic_dims_broadcast_32bit_second_arg_higher_rank(self): # x.batch_shape = [1, 2] # y.batch_shape = [3, 4, 1] @@ -195,6 +203,7 @@ class BroadcastMatrixBatchDimsTest(test.TestCase): class CholeskySolveWithBroadcastTest(test.TestCase): + @test_util.run_deprecated_v1 def test_static_dims_broadcast(self): # batch_shape = [2] chol = rng.rand(3, 3) @@ -205,8 +214,9 @@ class CholeskySolveWithBroadcastTest(test.TestCase): result = linear_operator_util.cholesky_solve_with_broadcast(chol, rhs) self.assertAllEqual((2, 3, 7), result.get_shape()) expected = linalg_ops.cholesky_solve(chol_broadcast, rhs) - self.assertAllClose(expected.eval(), result.eval()) + self.assertAllClose(expected.eval(), self.evaluate(result)) + @test_util.run_deprecated_v1 def test_dynamic_dims_broadcast_64bit(self): # batch_shape = [2, 2] chol = rng.rand(2, 3, 3) @@ -233,6 +243,7 @@ class CholeskySolveWithBroadcastTest(test.TestCase): class MatmulWithBroadcastTest(test.TestCase): + @test_util.run_deprecated_v1 def test_static_dims_broadcast_x_has_extra_dims(self): # batch_shape = [2] # for each batch member, we have a 1x3 matrix times a 3x7 matrix ==> 1x7 @@ -244,8 +255,9 @@ class MatmulWithBroadcastTest(test.TestCase): result = linear_operator_util.matmul_with_broadcast(x, y) self.assertAllEqual((2, 1, 7), result.get_shape()) expected = math_ops.matmul(x, y_broadcast) - self.assertAllClose(expected.eval(), result.eval()) + self.assertAllClose(expected.eval(), self.evaluate(result)) + @test_util.run_deprecated_v1 def test_static_dims_broadcast_y_has_extra_dims(self): # Since the second arg has extra dims, and the domain dim of the first arg # is larger than the number of linear equations, code will "flip" the extra @@ -261,8 +273,9 @@ class MatmulWithBroadcastTest(test.TestCase): result = linear_operator_util.matmul_with_broadcast(x, y) self.assertAllEqual((2, 3, 5, 5), result.get_shape()) expected = math_ops.matmul(x_broadcast, y) - self.assertAllClose(expected.eval(), result.eval()) + self.assertAllClose(expected.eval(), self.evaluate(result)) + @test_util.run_deprecated_v1 def test_static_dims_broadcast_y_has_extra_dims_transpose_a_and_b(self): # Since the second arg has extra dims, and the domain dim of the first arg # is larger than the number of linear equations, code will "flip" the extra @@ -280,8 +293,9 @@ class MatmulWithBroadcastTest(test.TestCase): self.assertAllEqual((2, 3, 5, 1), result.get_shape()) expected = math_ops.matmul( x_broadcast, y, transpose_a=True, transpose_b=True) - self.assertAllClose(expected.eval(), result.eval()) + self.assertAllClose(expected.eval(), self.evaluate(result)) + @test_util.run_deprecated_v1 def test_static_dims_broadcast_y_has_extra_dims_transpose_dynamic(self): # Since the second arg has extra dims, and the domain dim of the first arg # is larger than the number of linear equations, code will "flip" the extra @@ -308,6 +322,7 @@ class MatmulWithBroadcastTest(test.TestCase): y_ph: y })) + @test_util.run_deprecated_v1 def test_dynamic_dims_broadcast_64bit(self): # batch_shape = [2] # for each batch member, we have a 1x3 matrix times a 3x7 matrix ==> 1x7 @@ -333,6 +348,7 @@ class MatmulWithBroadcastTest(test.TestCase): class MatrixSolveWithBroadcastTest(test.TestCase): + @test_util.run_deprecated_v1 def test_static_dims_broadcast_matrix_has_extra_dims(self): # batch_shape = [2] matrix = rng.rand(2, 3, 3) @@ -344,8 +360,9 @@ class MatrixSolveWithBroadcastTest(test.TestCase): matrix, rhs) self.assertAllEqual((2, 3, 7), result.get_shape()) expected = linalg_ops.matrix_solve(matrix, rhs_broadcast) - self.assertAllClose(expected.eval(), result.eval()) + self.assertAllClose(expected.eval(), self.evaluate(result)) + @test_util.run_deprecated_v1 def test_static_dims_broadcast_rhs_has_extra_dims(self): # Since the second arg has extra dims, and the domain dim of the first arg # is larger than the number of linear equations, code will "flip" the extra @@ -362,8 +379,9 @@ class MatrixSolveWithBroadcastTest(test.TestCase): result = linear_operator_util.matrix_solve_with_broadcast(matrix, rhs) self.assertAllEqual((2, 3, 2), result.get_shape()) expected = linalg_ops.matrix_solve(matrix_broadcast, rhs) - self.assertAllClose(expected.eval(), result.eval()) + self.assertAllClose(expected.eval(), self.evaluate(result)) + @test_util.run_deprecated_v1 def test_static_dims_broadcast_rhs_has_extra_dims_dynamic(self): # Since the second arg has extra dims, and the domain dim of the first arg # is larger than the number of linear equations, code will "flip" the extra @@ -385,12 +403,13 @@ class MatrixSolveWithBroadcastTest(test.TestCase): self.assertAllEqual(3, result.shape.ndims) expected = linalg_ops.matrix_solve(matrix_broadcast, rhs) self.assertAllClose( - expected.eval(), + self.evaluate(expected), result.eval(feed_dict={ matrix_ph: matrix, rhs_ph: rhs })) + @test_util.run_deprecated_v1 def test_static_dims_broadcast_rhs_has_extra_dims_and_adjoint(self): # Since the second arg has extra dims, and the domain dim of the first arg # is larger than the number of linear equations, code will "flip" the extra @@ -408,8 +427,9 @@ class MatrixSolveWithBroadcastTest(test.TestCase): matrix, rhs, adjoint=True) self.assertAllEqual((2, 3, 2), result.get_shape()) expected = linalg_ops.matrix_solve(matrix_broadcast, rhs, adjoint=True) - self.assertAllClose(expected.eval(), result.eval()) + self.assertAllClose(expected.eval(), self.evaluate(result)) + @test_util.run_deprecated_v1 def test_dynamic_dims_broadcast_64bit(self): # batch_shape = [2, 2] matrix = rng.rand(2, 3, 3) @@ -436,6 +456,7 @@ class MatrixSolveWithBroadcastTest(test.TestCase): class MatrixTriangularSolveWithBroadcastTest(test.TestCase): + @test_util.run_deprecated_v1 def test_static_dims_broadcast_matrix_has_extra_dims(self): # batch_shape = [2] matrix = rng.rand(2, 3, 3) @@ -447,8 +468,9 @@ class MatrixTriangularSolveWithBroadcastTest(test.TestCase): matrix, rhs) self.assertAllEqual((2, 3, 7), result.get_shape()) expected = linalg_ops.matrix_triangular_solve(matrix, rhs_broadcast) - self.assertAllClose(expected.eval(), result.eval()) + self.assertAllClose(expected.eval(), self.evaluate(result)) + @test_util.run_deprecated_v1 def test_static_dims_broadcast_rhs_has_extra_dims(self): # Since the second arg has extra dims, and the domain dim of the first arg # is larger than the number of linear equations, code will "flip" the extra @@ -466,8 +488,9 @@ class MatrixTriangularSolveWithBroadcastTest(test.TestCase): matrix, rhs) self.assertAllEqual((2, 3, 2), result.get_shape()) expected = linalg_ops.matrix_triangular_solve(matrix_broadcast, rhs) - self.assertAllClose(expected.eval(), result.eval()) + self.assertAllClose(expected.eval(), self.evaluate(result)) + @test_util.run_deprecated_v1 def test_static_dims_broadcast_rhs_has_extra_dims_and_adjoint(self): # Since the second arg has extra dims, and the domain dim of the first arg # is larger than the number of linear equations, code will "flip" the extra @@ -486,8 +509,9 @@ class MatrixTriangularSolveWithBroadcastTest(test.TestCase): self.assertAllEqual((2, 3, 2), result.get_shape()) expected = linalg_ops.matrix_triangular_solve( matrix_broadcast, rhs, adjoint=True) - self.assertAllClose(expected.eval(), result.eval()) + self.assertAllClose(expected.eval(), self.evaluate(result)) + @test_util.run_deprecated_v1 def test_dynamic_dims_broadcast_64bit(self): # batch_shape = [2] matrix = rng.rand(2, 3, 3) @@ -522,6 +546,7 @@ class DomainDimensionStubOperator(object): class AssertCompatibleMatrixDimensionsTest(test.TestCase): + @test_util.run_deprecated_v1 def test_compatible_dimensions_do_not_raise(self): with self.cached_session(): x = ops.convert_to_tensor(rng.rand(2, 3, 4)) diff --git a/tensorflow/python/kernel_tests/linalg/linear_operator_zeros_test.py b/tensorflow/python/kernel_tests/linalg/linear_operator_zeros_test.py index ad97d1a93e..eb0b8ef127 100644 --- a/tensorflow/python/kernel_tests/linalg/linear_operator_zeros_test.py +++ b/tensorflow/python/kernel_tests/linalg/linear_operator_zeros_test.py @@ -20,6 +20,7 @@ from __future__ import print_function import numpy as np from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops.linalg import linalg as linalg_lib from tensorflow.python.ops.linalg import linear_operator_test_util @@ -35,7 +36,7 @@ class LinearOperatorZerosTest( @property def _tests_to_skip(self): - return ["log_abs_det", "solve", "solve_with_broadcast"] + return ["cholesky", "log_abs_det", "solve", "solve_with_broadcast"] @property def _operator_build_infos(self): @@ -46,7 +47,10 @@ class LinearOperatorZerosTest( build_info((3, 4, 4)), build_info((2, 1, 4, 4))] - def _operator_and_matrix(self, build_info, dtype, use_placeholder): + def _operator_and_matrix( + self, build_info, dtype, use_placeholder, + ensure_self_adjoint_and_pd=False): + del ensure_self_adjoint_and_pd del use_placeholder shape = list(build_info.shape) assert shape[-1] == shape[-2] @@ -70,6 +74,7 @@ class LinearOperatorZerosTest( operator = linalg_lib.LinearOperatorZeros(num_rows=2) operator.assert_non_singular() + @test_util.run_deprecated_v1 def test_assert_self_adjoint(self): with self.cached_session(): operator = linalg_lib.LinearOperatorZeros(num_rows=2) @@ -105,6 +110,7 @@ class LinearOperatorZerosTest( with self.assertRaisesRegexp(ValueError, "must be non-negative"): linalg_lib.LinearOperatorZeros(num_rows=2, batch_shape=[-2]) + @test_util.run_deprecated_v1 def test_non_scalar_num_rows_raises_dynamic(self): with self.cached_session(): num_rows = array_ops.placeholder(dtypes.int32) @@ -113,6 +119,7 @@ class LinearOperatorZerosTest( with self.assertRaisesOpError("must be a 0-D Tensor"): operator.to_dense().eval(feed_dict={num_rows: [2]}) + @test_util.run_deprecated_v1 def test_negative_num_rows_raises_dynamic(self): with self.cached_session(): n = array_ops.placeholder(dtypes.int32) @@ -126,6 +133,7 @@ class LinearOperatorZerosTest( with self.assertRaisesOpError("must be non-negative"): operator.to_dense().eval(feed_dict={n: -2}) + @test_util.run_deprecated_v1 def test_non_1d_batch_shape_raises_dynamic(self): with self.cached_session(): batch_shape = array_ops.placeholder(dtypes.int32) @@ -134,6 +142,7 @@ class LinearOperatorZerosTest( with self.assertRaisesOpError("must be a 1-D"): operator.to_dense().eval(feed_dict={batch_shape: 2}) + @test_util.run_deprecated_v1 def test_negative_batch_shape_raises_dynamic(self): with self.cached_session(): batch_shape = array_ops.placeholder(dtypes.int32) @@ -148,6 +157,7 @@ class LinearOperatorZerosTest( with self.assertRaisesRegexp(ValueError, "Dimensions.*not compatible"): operator.matmul(x) + @test_util.run_deprecated_v1 def test_wrong_matrix_dimensions_raises_dynamic(self): num_rows = array_ops.placeholder(dtypes.int32) x = array_ops.placeholder(dtypes.float32) @@ -166,6 +176,17 @@ class LinearOperatorZerosTest( self.assertFalse(operator.is_non_singular) self.assertTrue(operator.is_self_adjoint) + def test_zeros_matmul(self): + operator1 = linalg_lib.LinearOperatorIdentity(num_rows=2) + operator2 = linalg_lib.LinearOperatorZeros(num_rows=2) + self.assertTrue(isinstance( + operator1.matmul(operator2), + linalg_lib.LinearOperatorZeros)) + + self.assertTrue(isinstance( + operator2.matmul(operator1), + linalg_lib.LinearOperatorZeros)) + class LinearOperatorZerosNotSquareTest( linear_operator_test_util.NonSquareLinearOperatorDerivedClassTest): diff --git a/tensorflow/python/kernel_tests/linalg_grad_test.py b/tensorflow/python/kernel_tests/linalg_grad_test.py index 03b640a85a..28e1d7e168 100644 --- a/tensorflow/python/kernel_tests/linalg_grad_test.py +++ b/tensorflow/python/kernel_tests/linalg_grad_test.py @@ -21,6 +21,7 @@ from __future__ import print_function import numpy as np from tensorflow.python.framework import constant_op +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import gradients_impl @@ -39,6 +40,7 @@ def _AddTest(test, op_name, testcase_name, fn): class ShapeTest(test_lib.TestCase): + @test_util.run_deprecated_v1 def testBatchGradientUnknownSize(self): with self.cached_session(): batch_size = constant_op.constant(3) @@ -50,7 +52,7 @@ class ShapeTest(test_lib.TestCase): determinants = linalg_ops.matrix_determinant(batch_identity) reduced = math_ops.reduce_sum(determinants) sum_grad = gradients_impl.gradients(reduced, batch_identity)[0] - self.assertAllClose(batch_identity.eval(), sum_grad.eval()) + self.assertAllClose(batch_identity.eval(), self.evaluate(sum_grad)) class MatrixUnaryFunctorGradientTest(test_lib.TestCase): @@ -69,7 +71,7 @@ def _GetMatrixUnaryFunctorGradientTest(functor_, dtype_, shape_, **kwargs_): if functor_.__name__ == 'matrix_square_root': # Square the input matrix to ensure that its matrix square root exists a = math_ops.matmul(a, a) - a_np = a.eval() + a_np = self.evaluate(a) b = functor_(a, **kwargs_) # Optimal stepsize for central difference is O(epsilon^{1/3}). diff --git a/tensorflow/python/kernel_tests/linalg_ops_test.py b/tensorflow/python/kernel_tests/linalg_ops_test.py index 28391aaa87..028167a786 100644 --- a/tensorflow/python/kernel_tests/linalg_ops_test.py +++ b/tensorflow/python/kernel_tests/linalg_ops_test.py @@ -25,6 +25,7 @@ import numpy as np from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import linalg_ops from tensorflow.python.ops import math_ops @@ -52,6 +53,7 @@ class CholeskySolveTest(test.TestCase): def setUp(self): self.rng = np.random.RandomState(0) + @test_util.run_deprecated_v1 def test_works_with_five_different_random_pos_def_matrices(self): for n in range(1, 6): for np_type, atol in [(np.float32, 0.05), (np.float64, 1e-5)]: @@ -73,6 +75,7 @@ class LogdetTest(test.TestCase): def setUp(self): self.rng = np.random.RandomState(42) + @test_util.run_deprecated_v1 def test_works_with_five_different_random_pos_def_matrices(self): for n in range(1, 6): for np_dtype, atol in [(np.float32, 0.05), (np.float64, 1e-5), @@ -85,7 +88,7 @@ class LogdetTest(test.TestCase): # [_RandomPDMatrix(n, self.rng, np_dtype), # _RandomPDMatrix(n, self.rng, np_dtype)]).astype(np_dtype) logdet_tf = linalg.logdet(matrix) - self.assertAllClose(logdet_np, logdet_tf.eval(), atol=atol) + self.assertAllClose(logdet_np, self.evaluate(logdet_tf), atol=atol) def test_works_with_underflow_case(self): for np_dtype, atol in [(np.float32, 0.05), (np.float64, 1e-5), @@ -94,7 +97,7 @@ class LogdetTest(test.TestCase): _, logdet_np = np.linalg.slogdet(matrix) with self.session(use_gpu=True): logdet_tf = linalg.logdet(matrix) - self.assertAllClose(logdet_np, logdet_tf.eval(), atol=atol) + self.assertAllClose(logdet_np, self.evaluate(logdet_tf), atol=atol) class SlogdetTest(test.TestCase): @@ -102,6 +105,7 @@ class SlogdetTest(test.TestCase): def setUp(self): self.rng = np.random.RandomState(42) + @test_util.run_deprecated_v1 def test_works_with_five_different_random_pos_def_matrices(self): for n in range(1, 6): for np_dtype, atol in [(np.float32, 0.05), (np.float64, 1e-5), @@ -110,8 +114,9 @@ class SlogdetTest(test.TestCase): sign_np, log_abs_det_np = np.linalg.slogdet(matrix) with self.session(use_gpu=True): sign_tf, log_abs_det_tf = linalg.slogdet(matrix) - self.assertAllClose(log_abs_det_np, log_abs_det_tf.eval(), atol=atol) - self.assertAllClose(sign_np, sign_tf.eval(), atol=atol) + self.assertAllClose( + log_abs_det_np, self.evaluate(log_abs_det_tf), atol=atol) + self.assertAllClose(sign_np, self.evaluate(sign_tf), atol=atol) def test_works_with_underflow_case(self): for np_dtype, atol in [(np.float32, 0.05), (np.float64, 1e-5), @@ -120,8 +125,9 @@ class SlogdetTest(test.TestCase): sign_np, log_abs_det_np = np.linalg.slogdet(matrix) with self.session(use_gpu=True): sign_tf, log_abs_det_tf = linalg.slogdet(matrix) - self.assertAllClose(log_abs_det_np, log_abs_det_tf.eval(), atol=atol) - self.assertAllClose(sign_np, sign_tf.eval(), atol=atol) + self.assertAllClose( + log_abs_det_np, self.evaluate(log_abs_det_tf), atol=atol) + self.assertAllClose(sign_np, self.evaluate(sign_tf), atol=atol) class AdjointTest(test.TestCase): @@ -135,7 +141,7 @@ class AdjointTest(test.TestCase): matrix = ops.convert_to_tensor(matrix_np) transposed = linalg.adjoint(matrix) self.assertEqual((3, 2), transposed.get_shape()) - self.assertAllEqual(expected_transposed, transposed.eval()) + self.assertAllEqual(expected_transposed, self.evaluate(transposed)) class EyeTest(parameterized.TestCase, test.TestCase): @@ -230,6 +236,7 @@ class EyeTest(parameterized.TestCase, test.TestCase): dtypes.complex128 ]) ) + @test_util.run_deprecated_v1 def test_eye_with_placeholder( self, num_rows, num_columns, batch_shape, dtype): eye_np = np.eye(num_rows, M=num_columns, dtype=dtype.as_numpy_dtype) diff --git a/tensorflow/python/kernel_tests/list_ops_test.py b/tensorflow/python/kernel_tests/list_ops_test.py index 92552854aa..489f6c9b00 100644 --- a/tensorflow/python/kernel_tests/list_ops_test.py +++ b/tensorflow/python/kernel_tests/list_ops_test.py @@ -29,9 +29,11 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors from tensorflow.python.framework import ops +from tensorflow.python.framework import tensor_shape from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import control_flow_ops +from tensorflow.python.ops import gradients_impl from tensorflow.python.ops import list_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import state_ops @@ -39,17 +41,13 @@ from tensorflow.python.ops import variable_scope as vs from tensorflow.python.platform import test -def scalar_shape(): - return ops.convert_to_tensor([], dtype=dtypes.int32) - - @test_util.run_all_in_graph_and_eager_modes class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): def _testPushPop(self, max_num_elements): l = list_ops.empty_tensor_list( element_dtype=dtypes.float32, - element_shape=scalar_shape(), + element_shape=[], max_num_elements=max_num_elements) l = list_ops.tensor_list_push_back(l, constant_op.constant(1.0)) l, e = list_ops.tensor_list_pop_back(l, element_dtype=dtypes.float32) @@ -68,11 +66,10 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): with context.device("gpu:0"): self._testPushPop(max_num_elements) + @test_util.run_deprecated_v1 def testPushInFullListFails(self): l = list_ops.empty_tensor_list( - element_dtype=dtypes.float32, - element_shape=scalar_shape(), - max_num_elements=1) + element_dtype=dtypes.float32, element_shape=[], max_num_elements=1) l = list_ops.tensor_list_push_back(l, constant_op.constant(1.0)) with self.assertRaisesRegexp(errors.InvalidArgumentError, "Tried to push item into a full list"): @@ -81,10 +78,11 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): @parameterized.named_parameters(("NoMaxNumElements", None), ("WithMaxNumElements", 2)) + @test_util.run_deprecated_v1 def testPopFromEmptyTensorListFails(self, max_num_elements): l = list_ops.empty_tensor_list( element_dtype=dtypes.float32, - element_shape=scalar_shape(), + element_shape=[], max_num_elements=max_num_elements) with self.assertRaisesRegexp(errors.InvalidArgumentError, "Trying to pop from an empty list"): @@ -94,11 +92,13 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): def _testStack(self, max_num_elements): l = list_ops.empty_tensor_list( element_dtype=dtypes.float32, - element_shape=scalar_shape(), + element_shape=[], max_num_elements=max_num_elements) l = list_ops.tensor_list_push_back(l, constant_op.constant(1.0)) l = list_ops.tensor_list_push_back(l, constant_op.constant(2.0)) t = list_ops.tensor_list_stack(l, element_dtype=dtypes.float32) + if not context.executing_eagerly(): + self.assertAllEqual(t.shape.as_list(), [None]) self.assertAllEqual(self.evaluate(t), [1.0, 2.0]) @parameterized.named_parameters(("NoMaxNumElements", None), @@ -116,10 +116,11 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): @parameterized.named_parameters(("NoMaxNumElements", None), ("WithMaxNumElements", 3)) + @test_util.run_deprecated_v1 def testStackWithUnknownElementShape(self, max_num_elements): l = list_ops.empty_tensor_list( element_dtype=dtypes.float32, - element_shape=-1, + element_shape=None, max_num_elements=max_num_elements) l = list_ops.tensor_list_push_back(l, constant_op.constant(1.0)) l = list_ops.tensor_list_push_back(l, constant_op.constant(2.0)) @@ -136,10 +137,11 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): @parameterized.named_parameters(("NoMaxNumElements", None), ("WithMaxNumElements", 3)) + @test_util.run_deprecated_v1 def testStackWithPartiallyDefinedElementShape(self, max_num_elements): l = list_ops.empty_tensor_list( element_dtype=dtypes.float32, - element_shape=[-1], + element_shape=[None], max_num_elements=max_num_elements) l = list_ops.tensor_list_push_back(l, constant_op.constant([1.0])) l = list_ops.tensor_list_push_back(l, constant_op.constant([2.0])) @@ -156,6 +158,7 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): @parameterized.named_parameters(("NoMaxNumElements", None), ("WithMaxNumElements", 2)) + @test_util.run_deprecated_v1 def testStackEmptyList(self, max_num_elements): # Should be able to stack empty lists with fully defined element_shape. l = list_ops.empty_tensor_list( @@ -171,7 +174,7 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): "non-fully-defined"): l = list_ops.empty_tensor_list( element_dtype=dtypes.float32, - element_shape=[-1, 2], + element_shape=[None, 2], max_num_elements=max_num_elements) t = list_ops.tensor_list_stack(l, element_dtype=dtypes.float32) self.evaluate(t) @@ -181,7 +184,7 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): "non-fully-defined"): l = list_ops.empty_tensor_list( element_dtype=dtypes.float32, - element_shape=-1, + element_shape=None, max_num_elements=max_num_elements) t = list_ops.tensor_list_stack(l, element_dtype=dtypes.float32) self.evaluate(t) @@ -192,7 +195,7 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): with backprop.GradientTape() as tape: l = list_ops.empty_tensor_list( element_dtype=dtypes.float32, - element_shape=scalar_shape(), + element_shape=[], max_num_elements=max_num_elements) c0 = constant_op.constant(1.0) tape.watch(c0) @@ -206,10 +209,11 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): @parameterized.named_parameters(("NoMaxNumElements", None), ("WithMaxNumElements", 3)) + @test_util.run_deprecated_v1 def testGatherWithUnknownElementShape(self, max_num_elements): l = list_ops.empty_tensor_list( element_dtype=dtypes.float32, - element_shape=-1, + element_shape=None, max_num_elements=max_num_elements) l = list_ops.tensor_list_push_back(l, constant_op.constant(1.0)) l = list_ops.tensor_list_push_back(l, constant_op.constant(2.0)) @@ -229,10 +233,11 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): @parameterized.named_parameters(("NoMaxNumElements", None), ("WithMaxNumElements", 3)) + @test_util.run_deprecated_v1 def testGatherWithPartiallyDefinedElementShape(self, max_num_elements): l = list_ops.empty_tensor_list( element_dtype=dtypes.float32, - element_shape=[-1], + element_shape=[None], max_num_elements=max_num_elements) l = list_ops.tensor_list_push_back(l, constant_op.constant([1.0])) l = list_ops.tensor_list_push_back(l, constant_op.constant([2.0, 3.0])) @@ -252,6 +257,7 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): @parameterized.named_parameters(("NoMaxNumElements", None), ("WithMaxNumElements", 3)) + @test_util.run_deprecated_v1 def testGatherEmptyList(self, max_num_elements): # Should be able to gather from empty lists with fully defined # element_shape. @@ -268,7 +274,7 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): "non-fully-defined"): l = list_ops.empty_tensor_list( element_dtype=dtypes.float32, - element_shape=[-1, 2], + element_shape=[None, 2], max_num_elements=max_num_elements) t = list_ops.tensor_list_gather(l, [], element_dtype=dtypes.float32) self.evaluate(t) @@ -279,7 +285,7 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): "non-fully-defined"): l = list_ops.empty_tensor_list( element_dtype=dtypes.float32, - element_shape=-1, + element_shape=None, max_num_elements=max_num_elements) t = list_ops.tensor_list_gather(l, [], element_dtype=dtypes.float32) self.evaluate(t) @@ -300,7 +306,7 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): def testTensorListFromTensor(self): t = constant_op.constant([1.0, 2.0]) - l = list_ops.tensor_list_from_tensor(t, element_shape=scalar_shape()) + l = list_ops.tensor_list_from_tensor(t, element_shape=[]) l, e = list_ops.tensor_list_pop_back(l, element_dtype=dtypes.float32) self.assertAllEqual(self.evaluate(e), 2.0) l, e = list_ops.tensor_list_pop_back(l, element_dtype=dtypes.float32) @@ -315,7 +321,7 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): def testGetSetItem(self): t = constant_op.constant([1.0, 2.0]) - l = list_ops.tensor_list_from_tensor(t, element_shape=scalar_shape()) + l = list_ops.tensor_list_from_tensor(t, element_shape=[]) e0 = list_ops.tensor_list_get_item(l, 0, element_dtype=dtypes.float32) self.assertAllEqual(self.evaluate(e0), 1.0) l = list_ops.tensor_list_set_item(l, 0, 3.0) @@ -333,19 +339,16 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): t = constant_op.constant(5.) tape.watch(t) l = list_ops.tensor_list_reserve( - element_dtype=dtypes.float32, - element_shape=scalar_shape(), - num_elements=3) + element_dtype=dtypes.float32, element_shape=[], num_elements=3) l = list_ops.tensor_list_set_item(l, 1, 2. * t) e = list_ops.tensor_list_get_item(l, 1, element_dtype=dtypes.float32) self.assertAllEqual(self.evaluate(e), 10.0) self.assertAllEqual(self.evaluate(tape.gradient(e, t)), 2.0) + @test_util.run_deprecated_v1 def testSetOnEmptyListWithMaxNumElementsFails(self): l = list_ops.empty_tensor_list( - element_dtype=dtypes.float32, - element_shape=scalar_shape(), - max_num_elements=3) + element_dtype=dtypes.float32, element_shape=[], max_num_elements=3) with self.assertRaisesRegexp( errors.InvalidArgumentError, "Trying to modify element 0 in a list with 0 elements."): @@ -354,7 +357,7 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): def testUnknownShape(self): l = list_ops.empty_tensor_list( - element_dtype=dtypes.float32, element_shape=-1) + element_dtype=dtypes.float32, element_shape=None) l = list_ops.tensor_list_push_back(l, constant_op.constant(1.0)) l = list_ops.tensor_list_push_back(l, constant_op.constant([1.0, 2.0])) l, e = list_ops.tensor_list_pop_back(l, element_dtype=dtypes.float32) @@ -366,7 +369,7 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): if not context.num_gpus(): return t = constant_op.constant([1.0, 2.0]) - l = list_ops.tensor_list_from_tensor(t, element_shape=scalar_shape()) + l = list_ops.tensor_list_from_tensor(t, element_shape=[]) with context.device("gpu:0"): l_gpu = array_ops.identity(l) self.assertAllEqual( @@ -383,7 +386,7 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): if not context.num_gpus(): return t = constant_op.constant([1.0, 2.0]) - child_l = list_ops.tensor_list_from_tensor(t, element_shape=scalar_shape()) + child_l = list_ops.tensor_list_from_tensor(t, element_shape=[]) l = list_ops.empty_tensor_list( element_shape=constant_op.constant([], dtype=dtypes.int32), element_dtype=dtypes.variant) @@ -495,9 +498,7 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): with ops.Graph().as_default(), session.Session(target=worker.target): with ops.device("/job:worker"): l = list_ops.tensor_list_reserve( - element_dtype=dtypes.float32, - element_shape=scalar_shape(), - num_elements=2) + element_dtype=dtypes.float32, element_shape=[], num_elements=2) l = list_ops.tensor_list_set_item(l, 0, 1.) with ops.device("/job:ps"): l_ps = array_ops.identity(l) @@ -512,7 +513,7 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): with ops.Graph().as_default(), session.Session(target=worker.target): with ops.device("/job:worker"): t = constant_op.constant([[1.0], [2.0]]) - l = list_ops.tensor_list_from_tensor(t, element_shape=-1) + l = list_ops.tensor_list_from_tensor(t, element_shape=None) with ops.device("/job:ps"): l_ps = array_ops.identity(l) element_shape = list_ops.tensor_list_element_shape( @@ -529,7 +530,9 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): with ops.Graph().as_default(), session.Session(target=worker.target): with ops.device("/job:worker"): l = list_ops.empty_tensor_list( - element_shape=-1, element_dtype=dtypes.float32, max_num_elements=2) + element_shape=None, + element_dtype=dtypes.float32, + max_num_elements=2) l = list_ops.tensor_list_push_back(l, 1.) with ops.device("/job:ps"): l_ps = array_ops.identity(l) @@ -543,8 +546,8 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): def testPushPopGradients(self): with backprop.GradientTape() as tape: - l = list_ops.empty_tensor_list(element_dtype=dtypes.float32, - element_shape=scalar_shape()) + l = list_ops.empty_tensor_list( + element_dtype=dtypes.float32, element_shape=[]) c = constant_op.constant(1.0) tape.watch(c) l = list_ops.tensor_list_push_back(l, c) @@ -556,7 +559,7 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): with backprop.GradientTape() as tape: c = constant_op.constant([1.0, 2.0]) tape.watch(c) - l = list_ops.tensor_list_from_tensor(c, element_shape=scalar_shape()) + l = list_ops.tensor_list_from_tensor(c, element_shape=[]) c2 = list_ops.tensor_list_stack( l, element_dtype=dtypes.float32, num_elements=2) result = c2 * 2.0 @@ -567,7 +570,7 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): with backprop.GradientTape() as tape: c = constant_op.constant([1.0, 2.0]) tape.watch(c) - l = list_ops.tensor_list_from_tensor(c, element_shape=scalar_shape()) + l = list_ops.tensor_list_from_tensor(c, element_shape=[]) c2 = constant_op.constant(3.0) tape.watch(c2) l = list_ops.tensor_list_set_item(l, 0, c2) @@ -578,17 +581,19 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): self.assertAllEqual(self.evaluate(grad_c), [0.0, 4.0]) self.assertAllEqual(self.evaluate(grad_c2), 6.0) + @test_util.run_deprecated_v1 def testSetOutOfBounds(self): c = constant_op.constant([1.0, 2.0]) - l = list_ops.tensor_list_from_tensor(c, element_shape=scalar_shape()) + l = list_ops.tensor_list_from_tensor(c, element_shape=[]) with self.assertRaises(errors.InvalidArgumentError): self.evaluate(list_ops.tensor_list_set_item(l, 20, 3.0)) + @test_util.run_deprecated_v1 def testSkipEagerSetItemWithMismatchedShapeFails(self): with self.cached_session() as sess: ph = array_ops.placeholder(dtypes.float32) c = constant_op.constant([1.0, 2.0]) - l = list_ops.tensor_list_from_tensor(c, element_shape=scalar_shape()) + l = list_ops.tensor_list_from_tensor(c, element_shape=[]) # Set a placeholder with unknown shape to satisfy the shape inference # at graph building time. l = list_ops.tensor_list_set_item(l, 0, ph) @@ -599,7 +604,7 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): def testResourceVariableScatterGather(self): c = constant_op.constant([1.0, 2.0], dtype=dtypes.float32) - l = list_ops.tensor_list_from_tensor(c, element_shape=scalar_shape()) + l = list_ops.tensor_list_from_tensor(c, element_shape=[]) v = vs.get_variable("var", initializer=[l] * 10, use_resource=True) v_r_0_stacked = list_ops.tensor_list_stack(v[0], dtypes.float32) self.evaluate(v.initializer) @@ -607,10 +612,8 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): v_r_sparse_stacked = list_ops.tensor_list_stack( v.sparse_read(0), dtypes.float32) self.assertAllEqual([1.0, 2.0], self.evaluate(v_r_sparse_stacked)) - l_new_0 = list_ops.tensor_list_from_tensor( - [3.0, 4.0], element_shape=scalar_shape()) - l_new_1 = list_ops.tensor_list_from_tensor( - [5.0, 6.0], element_shape=scalar_shape()) + l_new_0 = list_ops.tensor_list_from_tensor([3.0, 4.0], element_shape=[]) + l_new_1 = list_ops.tensor_list_from_tensor([5.0, 6.0], element_shape=[]) updated_v = state_ops.scatter_update(v, [3, 5], [l_new_0, l_new_1]) updated_v_elems = array_ops.unstack(updated_v) updated_v_stacked = [ @@ -620,10 +623,11 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): [[1.0, 2.0]] * 4) self.assertAllEqual(self.evaluate(updated_v_stacked), expected) + @test_util.run_deprecated_v1 def testConcat(self): c = constant_op.constant([1.0, 2.0], dtype=dtypes.float32) - l0 = list_ops.tensor_list_from_tensor(c, element_shape=scalar_shape()) - l1 = list_ops.tensor_list_from_tensor([-1.0], element_shape=scalar_shape()) + l0 = list_ops.tensor_list_from_tensor(c, element_shape=[]) + l1 = list_ops.tensor_list_from_tensor([-1.0], element_shape=[]) l_batch_0 = array_ops.stack([l0, l1]) l_batch_1 = array_ops.stack([l1, l0]) @@ -659,7 +663,7 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): self.evaluate( list_ops.tensor_list_concat_lists( l_batch_0, - list_ops.empty_tensor_list(scalar_shape(), dtypes.float32), + list_ops.empty_tensor_list([], dtypes.float32), element_dtype=dtypes.float32)) with self.assertRaisesRegexp(errors.InvalidArgumentError, @@ -673,16 +677,16 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): with self.assertRaisesRegexp(errors.InvalidArgumentError, r"input_b\[0\].dtype != element_dtype."): l_batch_of_int_tls = array_ops.stack( - [list_ops.tensor_list_from_tensor([1], element_shape=scalar_shape())] - * 2) + [list_ops.tensor_list_from_tensor([1], element_shape=[])] * 2) self.evaluate( list_ops.tensor_list_concat_lists(l_batch_0, l_batch_of_int_tls, element_dtype=dtypes.float32)) + @test_util.run_deprecated_v1 def testPushBackBatch(self): c = constant_op.constant([1.0, 2.0], dtype=dtypes.float32) - l0 = list_ops.tensor_list_from_tensor(c, element_shape=scalar_shape()) - l1 = list_ops.tensor_list_from_tensor([-1.0], element_shape=scalar_shape()) + l0 = list_ops.tensor_list_from_tensor(c, element_shape=[]) + l1 = list_ops.tensor_list_from_tensor([-1.0], element_shape=[]) l_batch = array_ops.stack([l0, l1]) l_push = list_ops.tensor_list_push_back_batch(l_batch, [3.0, 4.0]) l_unstack = array_ops.unstack(l_push) @@ -726,7 +730,7 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): dtypes.float64, dtypes.complex64, dtypes.complex128, dtypes.bool): l_empty = list_ops.empty_tensor_list( - element_dtype=dtype, element_shape=scalar_shape()) + element_dtype=dtype, element_shape=[]) l_empty_zeros = array_ops.zeros_like(l_empty) t_empty_zeros = list_ops.tensor_list_stack( l_empty_zeros, element_dtype=dtype) @@ -750,10 +754,9 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): dtypes.float64, dtypes.complex64, dtypes.complex128, dtypes.bool): l = list_ops.empty_tensor_list( - element_dtype=dtypes.variant, element_shape=scalar_shape()) + element_dtype=dtypes.variant, element_shape=[]) - sub_l = list_ops.empty_tensor_list( - element_dtype=dtype, element_shape=scalar_shape()) + sub_l = list_ops.empty_tensor_list(element_dtype=dtype, element_shape=[]) l = list_ops.tensor_list_push_back(l, sub_l) sub_l = list_ops.tensor_list_push_back(sub_l, math_ops.cast( 1, dtype=dtype)) @@ -786,13 +789,12 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): def testElementShape(self): l = list_ops.empty_tensor_list( - element_dtype=dtypes.float32, element_shape=-1) + element_dtype=dtypes.float32, element_shape=None) shape = list_ops.tensor_list_element_shape(l, shape_type=dtypes.int32) self.assertEqual(self.evaluate(shape), -1) def testZerosLikeUninitialized(self): - l0 = list_ops.tensor_list_reserve( - scalar_shape(), 3, element_dtype=dtypes.float32) + l0 = list_ops.tensor_list_reserve([], 3, element_dtype=dtypes.float32) l1 = list_ops.tensor_list_set_item(l0, 0, 1.) # [1., _, _] zeros_1 = array_ops.zeros_like(l1) # [0., _, _] l2 = list_ops.tensor_list_set_item(l1, 2, 2.) # [1., _, 2.] @@ -808,6 +810,292 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): self.assertAllEqual(self.evaluate(res_1), [0.]) self.assertAllEqual(self.evaluate(res_2), [0., 0.]) + @test_util.run_deprecated_v1 + def testSkipEagerTensorListGetItemGradAggregation(self): + l = list_ops.tensor_list_reserve( + element_shape=[], num_elements=1, element_dtype=dtypes.float32) + x = constant_op.constant(1.0) + l = list_ops.tensor_list_set_item(l, 0, x) + l_read1 = list_ops.tensor_list_get_item(l, 0, element_dtype=dtypes.float32) + l_read2 = list_ops.tensor_list_get_item(l, 0, element_dtype=dtypes.float32) + grad = gradients_impl.gradients([l_read1, l_read2], [x]) + with self.cached_session() as sess: + self.assertSequenceEqual(self.evaluate(grad), [2.]) + + @test_util.run_deprecated_v1 + def testSkipEagerBuildElementShape(self): + fn = list_ops._build_element_shape + # Unknown shape -> -1. + self.assertEqual(fn(None), -1) + self.assertEqual(fn(tensor_shape.unknown_shape()), -1) + # Scalar shape -> [] with type int32. + self.assertEqual(fn([]).dtype, dtypes.int32) + self.assertEqual(fn(tensor_shape.scalar()).dtype, dtypes.int32) + self.assertAllEqual(self.evaluate(fn([])), np.array([], np.int32)) + self.assertAllEqual( + self.evaluate(fn(tensor_shape.scalar())), np.array([], np.int32)) + # Tensor -> Tensor + shape = constant_op.constant(1) + self.assertIs(fn(shape), shape) + # Shape with unknown dims -> shape list with -1's. + shape = [None, 5] + self.assertAllEqual(fn(shape), [-1, 5]) + self.assertAllEqual(fn(tensor_shape.TensorShape(shape)), [-1, 5]) + # Shape with unknown dims and tensor dims -> shape list with -1's and tensor + # dims. + t = array_ops.placeholder(dtypes.int32) + shape = [None, 5, t] + result = fn(shape) + self.assertAllEqual(result[:2], [-1, 5]) + self.assertIs(result[2], t) + + def testAddN(self): + l1 = list_ops.tensor_list_from_tensor([1.0, 2.0], element_shape=[]) + l2 = list_ops.tensor_list_from_tensor([3.0, 4.0], element_shape=[]) + l3 = list_ops.tensor_list_from_tensor([5.0, 6.0], element_shape=[]) + result = math_ops.add_n((l1, l2, l3)) + result_t = list_ops.tensor_list_stack(result, element_dtype=dtypes.float32) + self.assertAllEqual(self.evaluate(result_t), [9., 12.]) + + def testAddNNestedList(self): + l1 = list_ops.tensor_list_from_tensor([1.0, 2.0], element_shape=[]) + l2 = list_ops.tensor_list_from_tensor([3.0, 4.0], element_shape=[]) + l3 = list_ops.tensor_list_from_tensor([5.0, 6.0], element_shape=[]) + l4 = list_ops.tensor_list_from_tensor([7.0, 8.0], element_shape=[]) + a = list_ops.empty_tensor_list( + element_dtype=dtypes.variant, element_shape=[]) + a = list_ops.tensor_list_push_back(a, l1) + a = list_ops.tensor_list_push_back(a, l2) + b = list_ops.empty_tensor_list( + element_dtype=dtypes.variant, element_shape=[]) + b = list_ops.tensor_list_push_back(b, l3) + b = list_ops.tensor_list_push_back(b, l4) + result = math_ops.add_n((a, b)) + result_0 = list_ops.tensor_list_stack( + list_ops.tensor_list_get_item(result, 0, element_dtype=dtypes.variant), + element_dtype=dtypes.float32) + result_1 = list_ops.tensor_list_stack( + list_ops.tensor_list_get_item(result, 1, element_dtype=dtypes.variant), + element_dtype=dtypes.float32) + self.assertAllEqual(self.evaluate(result_0), [6., 8.]) + self.assertAllEqual(self.evaluate(result_1), [10., 12.]) + + @test_util.run_deprecated_v1 + def testSkipEagerConcatShapeInference(self): + + def BuildTensor(element_shape): + l = list_ops.empty_tensor_list( + element_dtype=dtypes.float32, element_shape=element_shape) + return list_ops.tensor_list_concat(l, element_dtype=dtypes.float32) + + self.assertIsNone(BuildTensor(None).shape.rank) + self.assertAllEqual(BuildTensor([None, 2, 3]).shape.as_list(), [None, 2, 3]) + self.assertAllEqual( + BuildTensor([None, 2, None]).shape.as_list(), [None, 2, None]) + self.assertAllEqual(BuildTensor([1, 2, 3]).shape.as_list(), [None, 2, 3]) + + def testConcatWithFullyDefinedElementShape(self): + l = list_ops.empty_tensor_list( + element_dtype=dtypes.float32, element_shape=[2, 2]) + l = list_ops.tensor_list_push_back(l, [[0., 1.], [2., 3.]]) + l = list_ops.tensor_list_push_back(l, [[4., 5.], [6., 7.]]) + t = list_ops.tensor_list_concat(l, element_dtype=dtypes.float32) + self.assertAllEqual( + self.evaluate(t), [[0., 1.], [2., 3.], [4., 5.], [6., 7.]]) + + def testConcatWithNonFullyDefinedElementShape(self): + l = list_ops.empty_tensor_list( + element_dtype=dtypes.float32, element_shape=[None, 2]) + l = list_ops.tensor_list_push_back(l, [[0., 1.]]) + l = list_ops.tensor_list_push_back(l, [[2., 3.], [4., 5.]]) + t = list_ops.tensor_list_concat(l, element_dtype=dtypes.float32) + self.assertAllEqual(self.evaluate(t), [[0., 1.], [2., 3.], [4., 5.]]) + + def testConcatWithMismatchingTensorShapesFails(self): + l = list_ops.empty_tensor_list( + element_dtype=dtypes.float32, element_shape=None) + l = list_ops.tensor_list_push_back(l, [[0., 1.]]) + l = list_ops.tensor_list_push_back(l, [[2.], [4.]]) + with self.assertRaisesRegexp( + errors.InvalidArgumentError, + r"Tried to concat tensors with unequal shapes: " + r"\[2\] vs \[1\]"): + t = list_ops.tensor_list_concat(l, element_dtype=dtypes.float32) + self.evaluate(t) + + def testConcatEmptyListWithFullyDefinedElementShape(self): + l = list_ops.empty_tensor_list( + element_dtype=dtypes.float32, element_shape=[5, 2]) + t = list_ops.tensor_list_concat(l, element_dtype=dtypes.float32) + self.assertAllEqual(self.evaluate(t).shape, (0, 2)) + l = list_ops.empty_tensor_list( + element_dtype=dtypes.float32, element_shape=[None, 2]) + t = list_ops.tensor_list_concat(l, element_dtype=dtypes.float32) + self.assertAllEqual(self.evaluate(t).shape, (0, 2)) + + def testConcatEmptyListWithUnknownElementShapeFails(self): + l = list_ops.empty_tensor_list( + element_dtype=dtypes.float32, element_shape=None) + with self.assertRaisesRegexp( + errors.InvalidArgumentError, + "All except the first dimension must be fully" + " defined when concating an empty tensor list"): + t = list_ops.tensor_list_concat(l, element_dtype=dtypes.float32) + self.evaluate(t) + + def testConcatEmptyListWithPartiallyDefinedElementShapeFails(self): + l = list_ops.empty_tensor_list( + element_dtype=dtypes.float32, element_shape=[2, None]) + with self.assertRaisesRegexp( + errors.InvalidArgumentError, + "All except the first dimension must be fully" + " defined when concating an empty tensor list"): + t = list_ops.tensor_list_concat(l, element_dtype=dtypes.float32) + self.evaluate(t) + + def testConcatListWithScalarElementShapeFails(self): + l = list_ops.empty_tensor_list( + element_dtype=dtypes.float32, element_shape=tensor_shape.scalar()) + with self.assertRaisesRegexp( + errors.InvalidArgumentError, + "Concat requires elements to be at least vectors, " + "found scalars instead"): + t = list_ops.tensor_list_concat(l, element_dtype=dtypes.float32) + self.evaluate(t) + + def testConcatListWithScalarElementsFails(self): + l = list_ops.empty_tensor_list( + element_dtype=dtypes.float32, element_shape=None) + l1 = list_ops.tensor_list_push_back(l, 1.) + with self.assertRaisesRegexp( + errors.InvalidArgumentError, "Concat saw a scalar shape at index 0" + " but requires at least vectors"): + t = list_ops.tensor_list_concat(l1, element_dtype=dtypes.float32) + self.evaluate(t) + l1 = list_ops.tensor_list_push_back(l, [1.]) + l1 = list_ops.tensor_list_push_back(l1, 2.) + with self.assertRaisesRegexp( + errors.InvalidArgumentError, "Concat saw a scalar shape at index 1" + " but requires at least vectors"): + t = list_ops.tensor_list_concat(l1, element_dtype=dtypes.float32) + self.evaluate(t) + + def testEvenSplit(self): + + def RunTest(input_tensor, lengths, expected_stacked_output): + l = list_ops.tensor_list_split( + input_tensor, element_shape=None, lengths=lengths) + self.assertAllEqual( + list_ops.tensor_list_stack(l, element_dtype=dtypes.float32), + expected_stacked_output) + + RunTest([1., 2., 3.], [1, 1, 1], [[1.], [2.], [3.]]) + RunTest([1., 2., 3., 4.], [2, 2], [[1., 2.], [3., 4.]]) + RunTest([[1., 2.], [3., 4.]], [1, 1], [[[1., 2.]], [[3., 4.]]]) + + def testUnevenSplit(self): + l = list_ops.tensor_list_split([1., 2., 3., 4., 5], + element_shape=None, + lengths=[3, 2]) + self.assertAllEqual(list_ops.tensor_list_length(l), 2) + self.assertAllEqual( + list_ops.tensor_list_get_item(l, 0, element_dtype=dtypes.float32), + [1., 2., 3.]) + self.assertAllEqual( + list_ops.tensor_list_get_item(l, 1, element_dtype=dtypes.float32), + [4., 5.]) + + @test_util.run_deprecated_v1 + def testSkipEagerSplitWithInvalidTensorShapeFails(self): + with self.cached_session(): + tensor = array_ops.placeholder(dtype=dtypes.float32) + l = list_ops.tensor_list_split(tensor, element_shape=None, lengths=[1]) + with self.assertRaisesRegexp( + errors.InvalidArgumentError, + r"Tensor must be at least a vector, but saw shape: \[\]"): + l.eval({tensor: 1}) + + @test_util.run_deprecated_v1 + def testSkipEagerSplitWithInvalidLengthsShapeFails(self): + with self.cached_session(): + lengths = array_ops.placeholder(dtype=dtypes.int64) + l = list_ops.tensor_list_split([1., 2.], + element_shape=None, + lengths=lengths) + with self.assertRaisesRegexp( + errors.InvalidArgumentError, + r"Expected lengths to be a vector, received shape: \[\]"): + l.eval({lengths: 1}) + + def testSplitWithInvalidLengthsFails(self): + with self.assertRaisesRegexp(errors.InvalidArgumentError, + r"Invalid value in lengths: -1"): + l = list_ops.tensor_list_split([1., 2.], + element_shape=None, + lengths=[1, -1]) + self.evaluate(l) + with self.assertRaisesRegexp( + errors.InvalidArgumentError, + r"Attempting to slice \[0, 3\] from tensor with length 2"): + l = list_ops.tensor_list_split([1., 2.], element_shape=None, lengths=[3]) + self.evaluate(l) + with self.assertRaisesRegexp( + errors.InvalidArgumentError, + r"Unused values in tensor. Length of tensor: 2 Values used: 1"): + l = list_ops.tensor_list_split([1., 2.], element_shape=None, lengths=[1]) + self.evaluate(l) + + @test_util.run_deprecated_v1 + def testSkipEagerSplitWithScalarElementShapeFails(self): + with self.assertRaisesRegexp(ValueError, + r"Shapes must be equal rank, but are 1 and 0"): + l = list_ops.tensor_list_split([1., 2.], element_shape=[], lengths=[1, 1]) + with self.cached_session(): + with self.assertRaisesRegexp( + errors.InvalidArgumentError, + r"TensorListSplit requires element_shape to be at least of rank 1, " + r"but saw: \[\]"): + element_shape = array_ops.placeholder(dtype=dtypes.int32) + l = list_ops.tensor_list_split([1., 2.], + element_shape=element_shape, + lengths=[1, 1]) + l.eval({element_shape: []}) + + def testEagerOnlySplitWithScalarElementShapeFails(self): + if context.executing_eagerly(): + with self.assertRaisesRegexp( + errors.InvalidArgumentError, + r"TensorListSplit requires element_shape to be at least of rank 1, " + r"but saw: \[\]"): + list_ops.tensor_list_split([1., 2.], element_shape=[], lengths=[1, 1]) + + @test_util.run_deprecated_v1 + def testSkipEagerSplitWithIncompatibleTensorShapeAndElementShapeFails(self): + with self.assertRaisesRegexp(ValueError, + r"Shapes must be equal rank, but are 2 and 1"): + l = list_ops.tensor_list_split([[1.], [2.]], + element_shape=[1], + lengths=[1, 1]) + + with self.cached_session(): + with self.assertRaisesRegexp( + errors.InvalidArgumentError, + r"tensor shape \[2,1\] is not compatible with element_shape \[1\]"): + element_shape = array_ops.placeholder(dtype=dtypes.int32) + l = list_ops.tensor_list_split([[1.], [2.]], + element_shape=element_shape, + lengths=[1, 1]) + l.eval({element_shape: [1]}) + + def testEagerOnlySplitWithIncompatibleTensorShapeAndElementShapeFails(self): + if context.executing_eagerly(): + with self.assertRaisesRegexp( + errors.InvalidArgumentError, + r"tensor shape \[2,1\] is not compatible with element_shape \[1\]"): + list_ops.tensor_list_split([[1.], [2.]], + element_shape=[1], + lengths=[1, 1]) + if __name__ == "__main__": test.main() diff --git a/tensorflow/python/kernel_tests/listdiff_op_test.py b/tensorflow/python/kernel_tests/listdiff_op_test.py index baeb40dd63..2865710798 100644 --- a/tensorflow/python/kernel_tests/listdiff_op_test.py +++ b/tensorflow/python/kernel_tests/listdiff_op_test.py @@ -47,7 +47,7 @@ class ListDiffTest(test.TestCase): y_tensor = ops.convert_to_tensor(y, dtype=dtype) out_tensor, idx_tensor = diff_func(x_tensor, y_tensor, index_dtype=index_dtype) - tf_out, tf_idx = sess.run([out_tensor, idx_tensor]) + tf_out, tf_idx = self.evaluate([out_tensor, idx_tensor]) self.assertAllEqual(tf_out, out) self.assertAllEqual(tf_idx, idx) self.assertEqual(1, out_tensor.get_shape().ndims) diff --git a/tensorflow/python/kernel_tests/logging_ops_test.py b/tensorflow/python/kernel_tests/logging_ops_test.py index 8e9b87f651..85035e5f7d 100644 --- a/tensorflow/python/kernel_tests/logging_ops_test.py +++ b/tensorflow/python/kernel_tests/logging_ops_test.py @@ -39,6 +39,7 @@ from tensorflow.python.platform import test class LoggingOpsTest(test.TestCase): + @test_util.run_deprecated_v1 def testAssertDivideByZero(self): with self.cached_session() as sess: epsilon = ops.convert_to_tensor(1e-20) @@ -52,7 +53,7 @@ class LoggingOpsTest(test.TestCase): math_ops.less(epsilon, y), ["Divide-by-zero"]) ]): out = math_ops.div(z, y) - self.assertAllEqual(2.0, out.eval()) + self.assertAllEqual(2.0, self.evaluate(out)) # assert(epsilon < x) # z / x # @@ -63,7 +64,7 @@ class LoggingOpsTest(test.TestCase): ]): out = math_ops.div(z, x) with self.assertRaisesOpError("less than x"): - out.eval() + self.evaluate(out) class PrintV2Test(test.TestCase): @@ -305,12 +306,14 @@ class PrintV2Test(test.TestCase): tensor, output_stream="unknown") self.evaluate(print_op) + @test_util.run_deprecated_v1 def testPrintOpName(self): with self.cached_session(): tensor = math_ops.range(10) print_op = logging_ops.print_v2(tensor, name="print_name") self.assertEqual(print_op.name, "print_name") + @test_util.run_deprecated_v1 def testNoDuplicateFormatOpGraphModeAfterExplicitFormat(self): with self.cached_session(): tensor = math_ops.range(10) @@ -379,6 +382,7 @@ class PrintGradientTest(test.TestCase): inp_printed = logging_ops.Print(inp, ["hello"]) self.assertEqual(inp.get_shape(), inp_printed.get_shape()) + @test_util.run_deprecated_v1 def testPrintGradient(self): with self.cached_session(): inp = constant_op.constant(2.0, shape=[100, 32], name="in") @@ -387,8 +391,8 @@ class PrintGradientTest(test.TestCase): wx_print = logging_ops.Print(wx, [w, w, w]) wx_grad = gradients_impl.gradients(wx, w)[0] wx_print_grad = gradients_impl.gradients(wx_print, w)[0] - wxg = wx_grad.eval() - wxpg = wx_print_grad.eval() + wxg = self.evaluate(wx_grad) + wxpg = self.evaluate(wx_print_grad) self.assertAllEqual(wxg, wxpg) diff --git a/tensorflow/python/kernel_tests/lookup_ops_test.py b/tensorflow/python/kernel_tests/lookup_ops_test.py index bd93942efb..ad81e0be64 100644 --- a/tensorflow/python/kernel_tests/lookup_ops_test.py +++ b/tensorflow/python/kernel_tests/lookup_ops_test.py @@ -37,6 +37,7 @@ from tensorflow.python.training import server_lib class HashTableOpTest(test.TestCase): + @test_util.run_deprecated_v1 def testHashTable(self): with self.cached_session(): default_val = -1 @@ -52,15 +53,16 @@ class HashTableOpTest(test.TestCase): output = table.lookup(input_string) self.assertAllEqual([3], output.get_shape()) - result = output.eval() + result = self.evaluate(output) self.assertAllEqual([0, 1, -1], result) exported_keys_tensor, exported_values_tensor = table.export() self.assertItemsEqual([b"brain", b"salad", b"surgery"], - exported_keys_tensor.eval()) - self.assertItemsEqual([0, 1, 2], exported_values_tensor.eval()) + self.evaluate(exported_keys_tensor)) + self.assertItemsEqual([0, 1, 2], self.evaluate(exported_values_tensor)) + @test_util.run_deprecated_v1 def testHashTableFindHighRank(self): with self.cached_session(): default_val = -1 @@ -76,9 +78,10 @@ class HashTableOpTest(test.TestCase): [["brain", "salad"], ["tank", "tarkus"]]) output = table.lookup(input_string) - result = output.eval() + result = self.evaluate(output) self.assertAllEqual([[0, 1], [-1, -1]], result) + @test_util.run_deprecated_v1 def testHashTableInitWithPythonArrays(self): with self.cached_session(): default_val = -1 @@ -94,9 +97,10 @@ class HashTableOpTest(test.TestCase): input_string = constant_op.constant(["brain", "salad", "tank"]) output = table.lookup(input_string) - result = output.eval() + result = self.evaluate(output) self.assertAllEqual([0, 1, -1], result) + @test_util.run_deprecated_v1 def testHashTableInitWithNumPyArrays(self): with self.cached_session(): default_val = -1 @@ -111,9 +115,10 @@ class HashTableOpTest(test.TestCase): input_string = constant_op.constant(["brain", "salad", "tank"]) output = table.lookup(input_string) - result = output.eval() + result = self.evaluate(output) self.assertAllEqual([0, 1, -1], result) + @test_util.run_deprecated_v1 def testMultipleHashTables(self): with self.cached_session() as sess: default_val = -1 @@ -137,11 +142,12 @@ class HashTableOpTest(test.TestCase): output2 = table2.lookup(input_string) output3 = table3.lookup(input_string) - out1, out2, out3 = sess.run([output1, output2, output3]) + out1, out2, out3 = self.evaluate([output1, output2, output3]) self.assertAllEqual([0, 1, -1], out1) self.assertAllEqual([0, 1, -1], out2) self.assertAllEqual([0, 1, -1], out3) + @test_util.run_deprecated_v1 def testHashTableWithTensorDefault(self): with self.cached_session(): default_val = constant_op.constant(-1, dtypes.int64) @@ -154,9 +160,10 @@ class HashTableOpTest(test.TestCase): input_string = constant_op.constant(["brain", "salad", "tank"]) output = table.lookup(input_string) - result = output.eval() + result = self.evaluate(output) self.assertAllEqual([0, 1, -1], result) + @test_util.run_deprecated_v1 def testHashTableWithSparseTensorInput(self): with self.cached_session() as sess: default_val = constant_op.constant(-1, dtypes.int64) @@ -174,12 +181,13 @@ class HashTableOpTest(test.TestCase): constant_op.constant(sp_shape, dtypes.int64)) output = table.lookup(input_tensor) - out_indices, out_values, out_shape = sess.run(output) + out_indices, out_values, out_shape = self.evaluate(output) self.assertAllEqual([0, 1, -1], out_values) self.assertAllEqual(sp_indices, out_indices) self.assertAllEqual(sp_shape, out_shape) + @test_util.run_deprecated_v1 def testSignatureMismatch(self): with self.cached_session(): default_val = -1 @@ -210,6 +218,7 @@ class HashTableOpTest(test.TestCase): lookup_ops.KeyValueTensorInitializer(["a"], [1], [dtypes.string], dtypes.int64), default_val) + @test_util.run_deprecated_v1 def testNotInitialized(self): with self.cached_session(): default_val = -1 @@ -221,8 +230,9 @@ class HashTableOpTest(test.TestCase): output = table.lookup(input_string) with self.assertRaisesOpError("Table not initialized"): - output.eval() + self.evaluate(output) + @test_util.run_deprecated_v1 def testInitializeTwice(self): with self.cached_session(): default_val = -1 @@ -235,6 +245,7 @@ class HashTableOpTest(test.TestCase): with self.assertRaisesOpError("Table already initialized"): table.initializer.run() + @test_util.run_deprecated_v1 def testInitializationWithInvalidDimensions(self): with self.cached_session(): default_val = -1 @@ -245,6 +256,7 @@ class HashTableOpTest(test.TestCase): lookup_ops.HashTable( lookup_ops.KeyValueTensorInitializer(keys, values), default_val) + @test_util.run_deprecated_v1 def testMultipleSessions(self): # Start a server server = server_lib.Server( @@ -274,6 +286,7 @@ class HashTableOpTest(test.TestCase): table.initializer.run() self.assertAllEqual(3, table.size().eval()) + @test_util.run_deprecated_v1 def testHashTableInt32String(self): with self.cached_session(): default_val = "n/a" @@ -286,7 +299,7 @@ class HashTableOpTest(test.TestCase): input_tensor = constant_op.constant([0, 1, -1]) output = table.lookup(input_tensor) - result = output.eval() + result = self.evaluate(output) self.assertAllEqual([b"brain", b"salad", b"n/a"], result) @@ -298,6 +311,7 @@ class IndexTableFromFile(test.TestCase): f.write("\n".join(values) + "\n") return vocabulary_file + @test_util.run_deprecated_v1 def test_string_index_table_from_file(self): vocabulary_file = self._createVocabFile("f2i_vocab1.txt") with self.cached_session(): @@ -306,10 +320,11 @@ class IndexTableFromFile(test.TestCase): ids = table.lookup(constant_op.constant(["salad", "surgery", "tarkus"])) with self.assertRaises(errors_impl.OpError): - ids.eval() + self.evaluate(ids) lookup_ops.tables_initializer().run() - self.assertAllEqual((1, 2, 3), ids.eval()) + self.assertAllEqual((1, 2, 3), self.evaluate(ids)) + @test_util.run_deprecated_v1 def test_string_index_table_from_multicolumn_file(self): vocabulary_file = self._createVocabFile( "f2i_vocab1.txt", values=("brain\t300", "salad\t20", "surgery\t1")) @@ -322,10 +337,11 @@ class IndexTableFromFile(test.TestCase): ids = table.lookup(constant_op.constant(["salad", "surgery", "tarkus"])) with self.assertRaises(errors_impl.OpError): - ids.eval() + self.evaluate(ids) lookup_ops.tables_initializer().run() - self.assertAllEqual((1, 2, 3), ids.eval()) + self.assertAllEqual((1, 2, 3), self.evaluate(ids)) + @test_util.run_deprecated_v1 def test_string_index_table_from_multicolumn_file_custom_delimiter(self): vocabulary_file = self._createVocabFile( "f2i_vocab1.txt", values=("brain 300", "salad 20", "surgery 1")) @@ -339,10 +355,11 @@ class IndexTableFromFile(test.TestCase): ids = table.lookup(constant_op.constant(["salad", "surgery", "tarkus"])) with self.assertRaises(errors_impl.OpError): - ids.eval() + self.evaluate(ids) lookup_ops.tables_initializer().run() - self.assertAllEqual((1, 2, 3), ids.eval()) + self.assertAllEqual((1, 2, 3), self.evaluate(ids)) + @test_util.run_deprecated_v1 def test_string_index_table_from_file_tensor_filename(self): vocabulary_file = self._createVocabFile("f2i_vocab1.txt") with self.cached_session(): @@ -352,12 +369,13 @@ class IndexTableFromFile(test.TestCase): ids = table.lookup(constant_op.constant(["salad", "surgery", "tarkus"])) with self.assertRaises(errors_impl.OpError): - ids.eval() + self.evaluate(ids) lookup_ops.tables_initializer().run() - self.assertAllEqual((1, 2, 3), ids.eval()) + self.assertAllEqual((1, 2, 3), self.evaluate(ids)) self.assertEqual(1, len(ops.get_collection(ops.GraphKeys.ASSET_FILEPATHS))) + @test_util.run_deprecated_v1 def test_string_index_table_from_file_placeholder_filename(self): vocabulary_file = self._createVocabFile("f2i_vocab1.txt") with self.cached_session(): @@ -367,14 +385,15 @@ class IndexTableFromFile(test.TestCase): ids = table.lookup(constant_op.constant(["salad", "surgery", "tarkus"])) with self.assertRaises(errors_impl.OpError): - ids.eval() + self.evaluate(ids) feed_dict = {vocabulary_placeholder.name: vocabulary_file} lookup_ops.tables_initializer().run(feed_dict=feed_dict) - self.assertAllEqual((1, 2, 3), ids.eval()) + self.assertAllEqual((1, 2, 3), self.evaluate(ids)) self.assertEqual(0, len(ops.get_collection(ops.GraphKeys.ASSET_FILEPATHS))) + @test_util.run_deprecated_v1 def test_int32_index_table_from_file(self): vocabulary_file = self._createVocabFile( "f2i_vocab2.txt", values=("42", "1", "-1000")) @@ -387,10 +406,11 @@ class IndexTableFromFile(test.TestCase): constant_op.constant((1, -1000, 11), dtype=dtypes.int32)) with self.assertRaises(errors_impl.OpError): - ids.eval() + self.evaluate(ids) lookup_ops.tables_initializer().run() - self.assertAllEqual((1, 2, 3), ids.eval()) + self.assertAllEqual((1, 2, 3), self.evaluate(ids)) + @test_util.run_deprecated_v1 def test_int64_index_table_from_file(self): vocabulary_file = self._createVocabFile( "f2i_vocab3.txt", values=("42", "1", "-1000")) @@ -403,10 +423,11 @@ class IndexTableFromFile(test.TestCase): constant_op.constant((1, -1000, 11), dtype=dtypes.int64)) with self.assertRaises(errors_impl.OpError): - ids.eval() + self.evaluate(ids) lookup_ops.tables_initializer().run() - self.assertAllEqual((1, 2, 3), ids.eval()) + self.assertAllEqual((1, 2, 3), self.evaluate(ids)) + @test_util.run_deprecated_v1 def test_index_table_from_file_with_default_value(self): default_value = -42 vocabulary_file = self._createVocabFile("f2i_vocab4.txt") @@ -416,10 +437,11 @@ class IndexTableFromFile(test.TestCase): ids = table.lookup(constant_op.constant(["salad", "surgery", "tarkus"])) with self.assertRaises(errors_impl.OpError): - ids.eval() + self.evaluate(ids) lookup_ops.tables_initializer().run() - self.assertAllEqual((1, 2, default_value), ids.eval()) + self.assertAllEqual((1, 2, default_value), self.evaluate(ids)) + @test_util.run_deprecated_v1 def test_index_table_from_file_with_oov_buckets(self): vocabulary_file = self._createVocabFile("f2i_vocab5.txt") with self.cached_session(): @@ -429,7 +451,7 @@ class IndexTableFromFile(test.TestCase): constant_op.constant(["salad", "surgery", "tarkus", "toccata"])) with self.assertRaises(errors_impl.OpError): - ids.eval() + self.evaluate(ids) lookup_ops.tables_initializer().run() self.assertAllEqual( ( @@ -437,7 +459,7 @@ class IndexTableFromFile(test.TestCase): 2, # From vocabulary file. 867, # 3 + fingerprint("tarkus") mod 300. 860), # 3 + fingerprint("toccata") mod 300. - ids.eval()) + self.evaluate(ids)) def test_index_table_from_file_fails_with_empty_vocabulary_file_name(self): self.assertRaises( @@ -468,6 +490,7 @@ class IndexTableFromFile(test.TestCase): vocabulary_file=vocabulary_file, vocab_size=0) + @test_util.run_deprecated_v1 def test_index_table_from_file_with_vocab_size_too_small(self): vocabulary_file = self._createVocabFile("f2i_vocab6.txt") with self.cached_session(): @@ -476,11 +499,12 @@ class IndexTableFromFile(test.TestCase): ids = table.lookup(constant_op.constant(["salad", "surgery", "tarkus"])) with self.assertRaises(errors_impl.OpError): - ids.eval() + self.evaluate(ids) lookup_ops.tables_initializer().run() - self.assertAllEqual((1, -1, -1), ids.eval()) + self.assertAllEqual((1, -1, -1), self.evaluate(ids)) self.assertEqual(2, table.size().eval()) + @test_util.run_deprecated_v1 def test_index_table_from_file_with_vocab_size_too_large(self): vocabulary_file = self._createVocabFile("f2i_vocab7.txt") with self.cached_session(): @@ -489,6 +513,7 @@ class IndexTableFromFile(test.TestCase): self.assertRaisesRegexp(errors_impl.InvalidArgumentError, "Invalid vocab_size", table.initializer.run) + @test_util.run_deprecated_v1 def test_index_table_from_file_with_vocab_size(self): vocabulary_file = self._createVocabFile("f2i_vocab8.txt") @@ -504,9 +529,9 @@ class IndexTableFromFile(test.TestCase): ids = table.lookup(constant_op.constant(["salad", "surgery", "tarkus"])) with self.assertRaises(errors_impl.OpError): - ids.eval() + self.evaluate(ids) lookup_ops.tables_initializer().run() - self.assertAllEqual((1, 2, -1), ids.eval()) + self.assertAllEqual((1, 2, -1), self.evaluate(ids)) self.assertEqual(3, table.size().eval()) def test_index_table_from_file_with_invalid_hashers(self): @@ -577,6 +602,7 @@ class KeyValueTensorInitializerTest(test.TestCase): table = lookup_ops.HashTable(init, default_value=-1) table.initializer.run() + @test_util.run_deprecated_v1 def test_int32(self): with ops.Graph().as_default(), self.cached_session(): init = lookup_ops.KeyValueTensorInitializer((42, 1, -1000), (0, 1, 2), @@ -590,6 +616,7 @@ class KeyValueTensorInitializerTest(test.TestCase): class IndexTableFromTensor(test.TestCase): @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def test_index_table_from_tensor_with_tensor_init(self): table = lookup_ops.index_table_from_tensor( vocabulary_list=("brain", "salad", "surgery"), num_oov_buckets=1) @@ -606,6 +633,7 @@ class IndexTableFromTensor(test.TestCase): ids = table.lookup(constant_op.constant(("salad", "surgery", "tarkus"))) self.assertAllEqual((1, 2, 3), self.evaluate(ids)) + @test_util.run_deprecated_v1 def test_int32_index_table_from_tensor_with_tensor_init(self): with self.cached_session(): table = lookup_ops.index_table_from_tensor( @@ -614,10 +642,11 @@ class IndexTableFromTensor(test.TestCase): constant_op.constant((1, -1000, 11), dtype=dtypes.int32)) with self.assertRaises(errors_impl.FailedPreconditionError): - ids.eval() + self.evaluate(ids) lookup_ops.tables_initializer().run() - self.assertAllEqual((1, 2, 3), ids.eval()) + self.assertAllEqual((1, 2, 3), self.evaluate(ids)) + @test_util.run_deprecated_v1 def test_int64_index_table_from_tensor_with_tensor_init(self): with self.cached_session(): table = lookup_ops.index_table_from_tensor( @@ -626,10 +655,11 @@ class IndexTableFromTensor(test.TestCase): constant_op.constant((1, -1000, 11), dtype=dtypes.int64)) with self.assertRaises(errors_impl.FailedPreconditionError): - ids.eval() + self.evaluate(ids) lookup_ops.tables_initializer().run() - self.assertAllEqual((1, 2, 3), ids.eval()) + self.assertAllEqual((1, 2, 3), self.evaluate(ids)) + @test_util.run_deprecated_v1 def test_index_table_from_tensor_with_default_value(self): default_value = -42 with self.cached_session(): @@ -639,9 +669,9 @@ class IndexTableFromTensor(test.TestCase): ids = table.lookup(constant_op.constant(["salad", "surgery", "tarkus"])) with self.assertRaises(errors_impl.FailedPreconditionError): - ids.eval() + self.evaluate(ids) lookup_ops.tables_initializer().run() - self.assertAllEqual((1, 2, default_value), ids.eval()) + self.assertAllEqual((1, 2, default_value), self.evaluate(ids)) def test_index_table_from_tensor_missing_vocabulary_list(self): with self.cached_session(): @@ -650,13 +680,14 @@ class IndexTableFromTensor(test.TestCase): lookup_ops.index_table_from_tensor( vocabulary_list=None, num_oov_buckets=1) + @test_util.run_deprecated_v1 def test_index_table_from_tensor_empty_vocabulary_list(self): with self.cached_session(): table = lookup_ops.index_table_from_tensor( vocabulary_list=np.array([], dtype=np.str_), num_oov_buckets=1) ids = table.lookup(constant_op.constant(["salad", "surgery", "brain"])) with self.assertRaises(errors_impl.OpError): - ids.eval() + self.evaluate(ids) with self.assertRaisesRegexp( errors_impl.OpError, "keys and values cannot be empty"): lookup_ops.tables_initializer().run() @@ -686,6 +717,7 @@ class IndexToStringTableFromFileTest(test.TestCase): f.write("\n".join(values) + "\n") return vocabulary_file + @test_util.run_deprecated_v1 def test_index_to_string_table(self): vocabulary_path = self._createVocabFile("i2f_vocab1.txt") # vocabulary_file supports string and tensor @@ -698,11 +730,12 @@ class IndexToStringTableFromFileTest(test.TestCase): features = table.lookup( constant_op.constant([0, 1, 2, 3], dtypes.int64)) with self.assertRaises(errors_impl.OpError): - features.eval() + self.evaluate(features) lookup_ops.tables_initializer().run() self.assertAllEqual((b"brain", b"salad", b"surgery", b"UNK"), - features.eval()) + self.evaluate(features)) + @test_util.run_deprecated_v1 def test_index_to_string_table_from_multicolumn_file(self): vocabulary_file = self._createVocabFile( "f2i_vocab1.txt", values=("brain\t300", "salad\t20", "surgery\t1")) @@ -713,11 +746,12 @@ class IndexToStringTableFromFileTest(test.TestCase): value_column_index=0) features = table.lookup(constant_op.constant([0, 1, 2, 3], dtypes.int64)) with self.assertRaises(errors_impl.OpError): - features.eval() + self.evaluate(features) lookup_ops.tables_initializer().run() self.assertAllEqual((b"brain", b"salad", b"surgery", b"UNK"), - features.eval()) + self.evaluate(features)) + @test_util.run_deprecated_v1 def test_index_to_string_table_from_multicolumn_file_custom_delimiter(self): vocabulary_file = self._createVocabFile( "f2i_vocab1.txt", values=("brain 300", "salad 20", "surgery 1")) @@ -729,11 +763,12 @@ class IndexToStringTableFromFileTest(test.TestCase): delimiter=" ") features = table.lookup(constant_op.constant([0, 1, 2, 3], dtypes.int64)) with self.assertRaises(errors_impl.OpError): - features.eval() + self.evaluate(features) lookup_ops.tables_initializer().run() self.assertAllEqual((b"brain", b"salad", b"surgery", b"UNK"), - features.eval()) + self.evaluate(features)) + @test_util.run_deprecated_v1 def test_index_to_string_table_with_default_value(self): default_value = b"NONE" vocabulary_file = self._createVocabFile("f2i_vocab2.txt") @@ -742,11 +777,12 @@ class IndexToStringTableFromFileTest(test.TestCase): vocabulary_file=vocabulary_file, default_value=default_value) features = table.lookup(constant_op.constant([1, 2, 4], dtypes.int64)) with self.assertRaises(errors_impl.OpError): - features.eval() + self.evaluate(features) lookup_ops.tables_initializer().run() self.assertAllEqual((b"salad", b"surgery", default_value), - features.eval()) + self.evaluate(features)) + @test_util.run_deprecated_v1 def test_index_to_string_table_with_vocab_size_too_small(self): default_value = b"NONE" vocabulary_file = self._createVocabFile("f2i_vocab2.txt") @@ -757,11 +793,12 @@ class IndexToStringTableFromFileTest(test.TestCase): default_value=default_value) features = table.lookup(constant_op.constant([1, 2, 4], dtypes.int64)) with self.assertRaises(errors_impl.OpError): - features.eval() + self.evaluate(features) lookup_ops.tables_initializer().run() self.assertAllEqual((b"salad", default_value, default_value), - features.eval()) + self.evaluate(features)) + @test_util.run_deprecated_v1 def test_index_to_string_table_with_vocab_size_too_large(self): vocabulary_file = self._createVocabFile("f2i_vocab6.txt") with self.cached_session(): @@ -770,11 +807,12 @@ class IndexToStringTableFromFileTest(test.TestCase): features = table.lookup(constant_op.constant([1, 2, 4], dtypes.int64)) with self.assertRaises(errors_impl.OpError): - features.eval() + self.evaluate(features) init = lookup_ops.tables_initializer() self.assertRaisesRegexp(errors_impl.InvalidArgumentError, "Invalid vocab_size", init.run) + @test_util.run_deprecated_v1 def test_index_to_string_table_with_vocab_size(self): vocabulary_file = self._createVocabFile("f2i_vocab7.txt") with self.cached_session(): @@ -783,13 +821,15 @@ class IndexToStringTableFromFileTest(test.TestCase): features = table.lookup(constant_op.constant([1, 2, 4], dtypes.int64)) with self.assertRaises(errors_impl.OpError): - features.eval() + self.evaluate(features) lookup_ops.tables_initializer().run() - self.assertAllEqual((b"salad", b"surgery", b"UNK"), features.eval()) + self.assertAllEqual((b"salad", b"surgery", b"UNK"), + self.evaluate(features)) class IndexToStringTableFromTensorTest(test.TestCase): + @test_util.run_deprecated_v1 def test_index_to_string_table_from_tensor(self): with self.cached_session(): vocabulary_list = constant_op.constant(["brain", "salad", "surgery"]) @@ -799,12 +839,13 @@ class IndexToStringTableFromTensorTest(test.TestCase): indices = constant_op.constant([0, 1, 2, 3], dtypes.int64) features = table.lookup(indices) with self.assertRaises(errors_impl.OpError): - features.eval() + self.evaluate(features) lookup_ops.tables_initializer().run() self.assertAllEqual((b"brain", b"salad", b"surgery", b"UNK"), - features.eval()) + self.evaluate(features)) + @test_util.run_deprecated_v1 def test_duplicate_entries(self): with self.cached_session(): vocabulary_list = constant_op.constant(["hello", "hello"]) @@ -813,8 +854,9 @@ class IndexToStringTableFromTensorTest(test.TestCase): indices = constant_op.constant([0, 1, 4], dtypes.int64) features = table.lookup(indices) lookup_ops.tables_initializer().run() - self.assertAllEqual((b"hello", b"hello", b"UNK"), features.eval()) + self.assertAllEqual((b"hello", b"hello", b"UNK"), self.evaluate(features)) + @test_util.run_deprecated_v1 def test_index_to_string_with_default_value(self): default_value = b"NONE" with self.cached_session(): @@ -824,11 +866,11 @@ class IndexToStringTableFromTensorTest(test.TestCase): indices = constant_op.constant([1, 2, 4], dtypes.int64) features = table.lookup(indices) with self.assertRaises(errors_impl.OpError): - features.eval() + self.evaluate(features) lookup_ops.tables_initializer().run() self.assertAllEqual((b"salad", b"surgery", default_value), - features.eval()) + self.evaluate(features)) class InitializeTableFromFileOpTest(test.TestCase): @@ -854,6 +896,7 @@ class InitializeTableFromFileOpTest(test.TestCase): result = self.evaluate(output) self.assertAllEqual([0, 1, -1], result) + @test_util.run_deprecated_v1 def testInitializeInt64Table(self): vocabulary_file = self._createVocabFile( "one_column_int64.txt", values=("42", "1", "-1000")) @@ -870,9 +913,10 @@ class InitializeTableFromFileOpTest(test.TestCase): output = table.lookup( constant_op.constant((42, 1, 11), dtype=dtypes.int64)) - result = output.eval() + result = self.evaluate(output) self.assertAllEqual([0, 1, -1], result) + @test_util.run_deprecated_v1 def testInitializeIndexTable(self): vocabulary_file = self._createVocabFile("one_column_2.txt") @@ -889,9 +933,10 @@ class InitializeTableFromFileOpTest(test.TestCase): input_values = constant_op.constant([0, 1, 2, 3], dtypes.int64) output = table.lookup(input_values) - result = output.eval() + result = self.evaluate(output) self.assertAllEqual([b"brain", b"salad", b"surgery", b"UNK"], result) + @test_util.run_deprecated_v1 def testMultiColumn(self): vocabulary_file = os.path.join(self.get_temp_dir(), "three_columns.txt") with open(vocabulary_file, "w") as f: @@ -911,9 +956,10 @@ class InitializeTableFromFileOpTest(test.TestCase): input_string = constant_op.constant(["brain", "salad", "surgery"]) output = table.lookup(input_string) - result = output.eval() + result = self.evaluate(output) self.assertAllEqual([1, 5, 6], result) + @test_util.run_deprecated_v1 def testInvalidDataTypeInMultiColumn(self): vocabulary_file = os.path.join(self.get_temp_dir(), "three_columns.txt") with open(vocabulary_file, "w") as f: @@ -944,6 +990,7 @@ class InitializeTableFromFileOpTest(test.TestCase): key_index, dtypes.string, value_index), default_value) + @test_util.run_deprecated_v1 def testInvalidIndex(self): vocabulary_file = self._createVocabFile("one_column_4.txt") with self.cached_session(): @@ -958,6 +1005,7 @@ class InitializeTableFromFileOpTest(test.TestCase): with self.assertRaisesOpError("Invalid number of columns"): table.initializer.run() + @test_util.run_deprecated_v1 def testInitializeSameTableWithMultipleNodes(self): vocabulary_file = self._createVocabFile("one_column_5.txt") @@ -994,7 +1042,7 @@ class InitializeTableFromFileOpTest(test.TestCase): output2 = table2.lookup(input_string) output3 = table3.lookup(input_string) - out1, out2, out3 = sess.run([output1, output2, output3]) + out1, out2, out3 = self.evaluate([output1, output2, output3]) self.assertAllEqual([0, 1, -1], out1) self.assertAllEqual([0, 1, -1], out2) self.assertAllEqual([0, 1, -1], out3) @@ -1009,6 +1057,7 @@ class InitializeTableFromFileOpTest(test.TestCase): dtypes.int64, lookup_ops.TextFileIndex.LINE_NUMBER), default_value) + @test_util.run_deprecated_v1 def testInitializeWithVocabSize(self): with self.cached_session(): default_value = -1 @@ -1055,6 +1104,7 @@ class InitializeTableFromFileOpTest(test.TestCase): table3.initializer.run() self.assertEquals(vocab_size, table3.size().eval()) + @test_util.run_deprecated_v1 def testFeedVocabularyName(self): vocabulary_file = self._createVocabFile("feed_vocabulary.txt") @@ -1078,9 +1128,10 @@ class InitializeTableFromFileOpTest(test.TestCase): input_string = constant_op.constant(["brain", "salad", "tank"]) output = table.lookup(input_string) - result = output.eval() + result = self.evaluate(output) self.assertAllEqual([0, 1, -1], result) + @test_util.run_deprecated_v1 def testInvalidFilenames(self): vocabulary_file = self._createVocabFile("filename_shape.txt") @@ -1105,6 +1156,7 @@ class InitializeTableFromFileOpTest(test.TestCase): dtypes.int64, lookup_ops.TextFileIndex.LINE_NUMBER), default_value) + @test_util.run_deprecated_v1 def testIdToStringTable(self): vocab_file = self._createVocabFile("feat_to_id_1.txt") with self.cached_session(): @@ -1119,9 +1171,11 @@ class InitializeTableFromFileOpTest(test.TestCase): input_values = constant_op.constant([0, 1, 2, 3], dtypes.int64) out = table.lookup(input_values) - self.assertAllEqual([b"brain", b"salad", b"surgery", b"UNK"], out.eval()) + self.assertAllEqual([b"brain", b"salad", b"surgery", b"UNK"], + self.evaluate(out)) self.assertEquals(vocab_size, table.size().eval()) + @test_util.run_deprecated_v1 def testStringToIdTable(self): vocab_file = self._createVocabFile("feat_to_id_2.txt") with self.cached_session(): @@ -1135,9 +1189,10 @@ class InitializeTableFromFileOpTest(test.TestCase): input_string = constant_op.constant(["brain", "salad", "surgery", "UNK"]) out = table.lookup(input_string) - self.assertAllEqual([0, 1, 2, -1], out.eval()) + self.assertAllEqual([0, 1, 2, -1], self.evaluate(out)) self.assertEquals(vocab_size, table.size().eval()) + @test_util.run_deprecated_v1 def testInt64ToIdTable(self): vocab_file = self._createVocabFile( "feat_to_id_3.txt", values=("42", "1", "-1000")) @@ -1152,7 +1207,7 @@ class InitializeTableFromFileOpTest(test.TestCase): out = table.lookup( constant_op.constant((42, 1, -1000, 11), dtype=dtypes.int64)) - self.assertAllEqual((0, 1, 2, -1), out.eval()) + self.assertAllEqual((0, 1, 2, -1), self.evaluate(out)) self.assertEquals(vocab_size, table.size().eval()) @@ -1164,6 +1219,7 @@ class IdTableWithHashBucketsTest(test.TestCase): f.write("\n".join(values) + "\n") return vocabulary_file + @test_util.run_deprecated_v1 def testStringIdTableWithHashBuckets(self): vocab_file = self._createVocabFile("feat_to_id_1.txt") with self.cached_session(): @@ -1181,9 +1237,10 @@ class IdTableWithHashBucketsTest(test.TestCase): input_string = constant_op.constant(["brain", "salad", "surgery", "UNK"]) out = table.lookup(input_string) - self.assertAllEqual([0, 1, 2, 3], out.eval()) + self.assertAllEqual([0, 1, 2, 3], self.evaluate(out)) self.assertEquals(vocab_size + oov_buckets, table.size().eval()) + @test_util.run_deprecated_v1 def testInt32IdTableWithHashBuckets(self): vocab_file = self._createVocabFile("feat_to_id_2.txt", ("42", "1", "-1000")) with self.cached_session(): @@ -1203,9 +1260,10 @@ class IdTableWithHashBucketsTest(test.TestCase): values = constant_op.constant((42, 1, -1000, 11), dtype=dtypes.int32) out = table.lookup(values) - self.assertAllEqual([0, 1, 2, 3], out.eval()) + self.assertAllEqual([0, 1, 2, 3], self.evaluate(out)) self.assertEquals(vocab_size + oov_buckets, table.size().eval()) + @test_util.run_deprecated_v1 def testInt64IdTableWithHashBuckets(self): vocab_file = self._createVocabFile("feat_to_id_3.txt", ("42", "1", "-1000")) with self.cached_session(): @@ -1223,9 +1281,10 @@ class IdTableWithHashBucketsTest(test.TestCase): values = constant_op.constant((42, 1, -1000, 11), dtype=dtypes.int64) out = table.lookup(values) - self.assertAllEqual([0, 1, 2, 3], out.eval()) + self.assertAllEqual([0, 1, 2, 3], self.evaluate(out)) self.assertEquals(vocab_size + oov_buckets, table.size().eval()) + @test_util.run_deprecated_v1 def testStringIdTableWithOnlyHashBucket(self): with self.cached_session(): oov_buckets = 5 @@ -1244,9 +1303,10 @@ class IdTableWithHashBucketsTest(test.TestCase): 1, # fingerprint("salad") mod 5. 4 # fingerprint("surgery") mod 5 ], - out.eval()) + self.evaluate(out)) self.assertEquals(oov_buckets, table.size().eval()) + @test_util.run_deprecated_v1 def testInt32IdTableWithOnlyHashBucket(self): with self.cached_session(): oov_buckets = 5 @@ -1266,7 +1326,7 @@ class IdTableWithHashBucketsTest(test.TestCase): 4, # fingerprint("1") mod 5. 2 # fingerprint("-1000") mod 5 ], - out.eval()) + self.evaluate(out)) self.assertEquals(oov_buckets, table.size().eval()) def testFloat64IdTableWithOnlyHashBucket(self): @@ -1281,6 +1341,7 @@ class IdTableWithHashBucketsTest(test.TestCase): lookup_ops.IdTableWithHashBuckets( None, num_oov_buckets=5, key_dtype=dtypes.bool) + @test_util.run_deprecated_v1 def testIdTableWithHashBucketsWithMultipleInitializers(self): vocab_file = self._createVocabFile("feat_to_id_4.txt") with self.cached_session() as sess: @@ -1311,7 +1372,7 @@ class IdTableWithHashBucketsTest(test.TestCase): out1 = table1.lookup(input_string) out2 = table2.lookup(input_string) - out1, out2 = sess.run([out1, out2]) + out1, out2 = self.evaluate([out1, out2]) self.assertAllEqual([5, 0, 1, 2, 5], out1) self.assertAllEqual([5, 0, 1, 2, 3], out2) self.assertEquals(vocab_size + oov_buckets, table1.size().eval()) @@ -1321,6 +1382,7 @@ class IdTableWithHashBucketsTest(test.TestCase): "table2_Lookup/hash_bucket": "StringToHashBucketStrong", }, sess.graph) + @test_util.run_deprecated_v1 def testIdTableWithHashBucketsInitializationAcrossSessions(self): vocab_file = self._createVocabFile("feat_to_id_5.txt") shared_name = "across-sessions" @@ -1342,7 +1404,7 @@ class IdTableWithHashBucketsTest(test.TestCase): out1 = table1.lookup(input_string_1) - self.assertAllEqual([0, 1, 2, 3], out1.eval()) + self.assertAllEqual([0, 1, 2, 3], self.evaluate(out1)) self.assertEquals(vocab_size + oov_buckets, table1.size().eval()) with self.cached_session(): @@ -1363,9 +1425,10 @@ class IdTableWithHashBucketsTest(test.TestCase): out2 = table2.lookup(input_string_2) - self.assertAllEqual([3, 1, 3], out2.eval()) + self.assertAllEqual([3, 1, 3], self.evaluate(out2)) self.assertEquals(vocab_size + oov_buckets, table2.size().eval()) + @test_util.run_deprecated_v1 def testIdTableWithHashBucketsWithMultipleInitializersDifferentDefault(self): vocab_file = self._createVocabFile("feat_to_id_6.txt") with self.cached_session() as sess: @@ -1394,12 +1457,13 @@ class IdTableWithHashBucketsTest(test.TestCase): out1 = table1.lookup(input_string_1) out2 = table2.lookup(input_string_2) - out1, out2 = sess.run([out1, out2]) + out1, out2 = self.evaluate([out1, out2]) self.assertAllEqual([0, 1, 2, -1], out1) self.assertAllEqual([-2, 1, -2], out2) self.assertEquals(vocab_size + oov_buckets, table1.size().eval()) self.assertEquals(vocab_size + oov_buckets, table2.size().eval()) + @test_util.run_deprecated_v1 def testSparseTensor(self): vocab_file = self._createVocabFile("feat_to_id_7.txt") input_indices = [[0, 0], [0, 1], [2, 0], [2, 2], [3, 0]] @@ -1428,6 +1492,7 @@ class IdTableWithHashBucketsTest(test.TestCase): self.assertAllEqual([0, 1, 0, 2, 3], sp_ids_val) self.assertAllEqual(input_shape, sp_ids_shape) + @test_util.run_deprecated_v1 def testInt32SparseTensor(self): input_indices = [[0, 0], [0, 1], [2, 0], [2, 2], [3, 0]] input_shape = [4, 4] @@ -1456,6 +1521,7 @@ class IdTableWithHashBucketsTest(test.TestCase): self.assertAllEqual([0, 1, 0, 2, 3], sp_ids_val) self.assertAllEqual(input_shape, sp_ids_shape) + @test_util.run_deprecated_v1 def testInt64SparseTensor(self): input_indices = [[0, 0], [0, 1], [2, 0], [2, 2], [3, 0]] input_shape = [4, 4] diff --git a/tensorflow/python/kernel_tests/losses_test.py b/tensorflow/python/kernel_tests/losses_test.py index b04996f788..abff61f81b 100644 --- a/tensorflow/python/kernel_tests/losses_test.py +++ b/tensorflow/python/kernel_tests/losses_test.py @@ -51,58 +51,62 @@ class AbsoluteDifferenceLossTest(test.TestCase): losses.absolute_difference( self._predictions, self._predictions, weights=None) + @test_util.run_deprecated_v1 def testAllCorrectNoLossWeight(self): loss = losses.absolute_difference(self._predictions, self._predictions) with self.cached_session(): - self.assertAlmostEqual(0.0, loss.eval(), 3) + self.assertAlmostEqual(0.0, self.evaluate(loss), 3) + @test_util.run_deprecated_v1 def testNonZeroLoss(self): loss = losses.absolute_difference(self._labels, self._predictions) with self.cached_session(): - self.assertAlmostEqual(5.5, loss.eval(), 3) + self.assertAlmostEqual(5.5, self.evaluate(loss), 3) + @test_util.run_deprecated_v1 def testNonZeroLossWithPythonScalarWeight(self): weights = 2.3 loss = losses.absolute_difference(self._labels, self._predictions, weights) with self.cached_session(): - self.assertAlmostEqual(5.5 * weights, loss.eval(), 3) + self.assertAlmostEqual(5.5 * weights, self.evaluate(loss), 3) + @test_util.run_deprecated_v1 def testNonZeroLossWithScalarTensorWeight(self): weights = 2.3 loss = losses.absolute_difference(self._labels, self._predictions, constant_op.constant(weights)) with self.cached_session(): - self.assertAlmostEqual(5.5 * weights, loss.eval(), 3) + self.assertAlmostEqual(5.5 * weights, self.evaluate(loss), 3) def testNonZeroLossWithOneDimBatchSpecificWeights(self): weights = constant_op.constant((1.2, 0.0), shape=(2, 1)) loss = losses.absolute_difference(self._labels, self._predictions, weights) with self.cached_session(): - self.assertAlmostEqual(5.6, loss.eval(), 3) + self.assertAlmostEqual(5.6, self.evaluate(loss), 3) def testNonZeroLossWithTwoDimBatchSpecificWeights(self): weights = constant_op.constant([1.2, 0.0], shape=[2, 1]) loss = losses.absolute_difference(self._labels, self._predictions, weights) with self.cached_session(): - self.assertAlmostEqual(5.6, loss.eval(), 3) + self.assertAlmostEqual(5.6, self.evaluate(loss), 3) def testNonZeroLossWithSampleSpecificWeights(self): weights = constant_op.constant([3, 6, 5, 0, 4, 2], shape=[2, 3]) loss = losses.absolute_difference(self._labels, self._predictions, weights) with self.cached_session(): - self.assertAlmostEqual(16.6, loss.eval(), 3) + self.assertAlmostEqual(16.6, self.evaluate(loss), 3) def testNonZeroLossWithSampleSpecificWeightsMostZero(self): weights = constant_op.constant([0, 0, 0, 0, 0, 2], shape=[2, 3]) loss = losses.absolute_difference(self._labels, self._predictions, weights) with self.cached_session(): - self.assertAlmostEqual(6.0, loss.eval(), 3) + self.assertAlmostEqual(6.0, self.evaluate(loss), 3) def testLossWithSampleSpecificWeightsAllZero(self): weights = array_ops.zeros((2, 3)) loss = losses.absolute_difference(self._labels, self._predictions, weights) with self.cached_session(): - self.assertAlmostEqual(0.0, loss.eval(), 3) + self.assertAlmostEqual(0.0, self.evaluate(loss), 3) @test_util.assert_no_new_pyobjects_executing_eagerly def testEagerNoMemoryLeaked(self): @@ -123,6 +127,7 @@ class SoftmaxCrossEntropyLossTest(test.TestCase): with self.assertRaises(ValueError): losses.softmax_cross_entropy(labels, logits, weights=None) + @test_util.run_deprecated_v1 def testAllCorrect(self): with self.cached_session(): logits = constant_op.constant([[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], @@ -132,6 +137,7 @@ class SoftmaxCrossEntropyLossTest(test.TestCase): self.assertEquals('softmax_cross_entropy_loss/value', loss.op.name) self.assertAlmostEqual(loss.eval(), 0.0, 3) + @test_util.run_deprecated_v1 def testAllWrong(self): logits = constant_op.constant([[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 10.0]]) @@ -142,6 +148,7 @@ class SoftmaxCrossEntropyLossTest(test.TestCase): self.assertEquals(loss.op.name, 'softmax_cross_entropy_loss/value') self.assertAlmostEqual(loss.eval(), 10.0, 3) + @test_util.run_deprecated_v1 def testNonZeroLossWithPythonScalarWeight(self): logits = constant_op.constant([[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 10.0]]) @@ -149,8 +156,9 @@ class SoftmaxCrossEntropyLossTest(test.TestCase): weights = 2.3 with self.cached_session(): loss = losses.softmax_cross_entropy(labels, logits, weights) - self.assertAlmostEqual(weights * 10.0, loss.eval(), 3) + self.assertAlmostEqual(weights * 10.0, self.evaluate(loss), 3) + @test_util.run_deprecated_v1 def testNonZeroLossWithScalarTensorWeight(self): logits = constant_op.constant([[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 10.0]]) @@ -159,7 +167,7 @@ class SoftmaxCrossEntropyLossTest(test.TestCase): with self.cached_session(): loss = losses.softmax_cross_entropy(labels, logits, constant_op.constant(weights)) - self.assertAlmostEqual(weights * 10.0, loss.eval(), 3) + self.assertAlmostEqual(weights * 10.0, self.evaluate(loss), 3) def testNonZeroLossWithOneDimBatchSpecificWeights(self): logits = constant_op.constant([[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], @@ -168,7 +176,8 @@ class SoftmaxCrossEntropyLossTest(test.TestCase): weights = constant_op.constant((1.2, 3.4, 5.6)) with self.cached_session(): loss = losses.softmax_cross_entropy(labels, logits, weights) - self.assertAlmostEqual((1.2 + 3.4 + 5.6) * 10.0 / 3.0, loss.eval(), 3) + self.assertAlmostEqual((1.2 + 3.4 + 5.6) * 10.0 / 3.0, + self.evaluate(loss), 3) def testAllWrongAllWeightsMissing(self): logits = constant_op.constant([[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], @@ -177,7 +186,7 @@ class SoftmaxCrossEntropyLossTest(test.TestCase): weights = constant_op.constant([0, 0, 0], shape=[3]) with self.cached_session(): loss = losses.softmax_cross_entropy(labels, logits, weights) - self.assertAlmostEqual(0.0, loss.eval(), 3) + self.assertAlmostEqual(0.0, self.evaluate(loss), 3) def testSomeWeightsMissing(self): logits = constant_op.constant([[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], @@ -186,7 +195,7 @@ class SoftmaxCrossEntropyLossTest(test.TestCase): weights = constant_op.constant([1.2, 0, 0], shape=[3]) with self.cached_session(): loss = losses.softmax_cross_entropy(labels, logits, weights) - self.assertAlmostEqual(12.0, loss.eval(), 3) + self.assertAlmostEqual(12.0, self.evaluate(loss), 3) def testSoftmaxWithMeasurementSpecificWeightsRaisesException(self): with self.cached_session(): @@ -199,6 +208,7 @@ class SoftmaxCrossEntropyLossTest(test.TestCase): with self.assertRaises(ValueError): losses.softmax_cross_entropy(labels, logits, weights=weights).eval() + @test_util.run_deprecated_v1 def testSoftmaxLabelSmoothing(self): with self.cached_session(): # Softmax Cross Entropy Loss is: @@ -231,6 +241,7 @@ class SparseSoftmaxCrossEntropyLossTest(test.TestCase): with self.assertRaises(ValueError): losses.sparse_softmax_cross_entropy(labels, logits, weights=None) + @test_util.run_deprecated_v1 def testAllCorrectInt32Labels(self): with self.cached_session(): logits = constant_op.constant([[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], @@ -247,6 +258,7 @@ class SparseSoftmaxCrossEntropyLossTest(test.TestCase): labels = constant_op.constant([[0], [1], [2]], dtype=dtypes.int32) losses.sparse_softmax_cross_entropy(labels, logits) + @test_util.run_deprecated_v1 def testAllCorrectInt64Labels(self): with self.cached_session(): logits = constant_op.constant([[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], @@ -256,6 +268,7 @@ class SparseSoftmaxCrossEntropyLossTest(test.TestCase): self.assertEquals(loss.op.name, 'sparse_softmax_cross_entropy_loss/value') self.assertAlmostEqual(loss.eval(), 0.0, 3) + @test_util.run_deprecated_v1 def testAllCorrectNonColumnLabels(self): with self.cached_session(): logits = constant_op.constant([[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], @@ -265,6 +278,7 @@ class SparseSoftmaxCrossEntropyLossTest(test.TestCase): self.assertEquals(loss.op.name, 'sparse_softmax_cross_entropy_loss/value') self.assertAlmostEqual(loss.eval(), 0.0, 3) + @test_util.run_deprecated_v1 def testAllWrongInt32Labels(self): logits = constant_op.constant([[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 10.0]]) @@ -275,6 +289,7 @@ class SparseSoftmaxCrossEntropyLossTest(test.TestCase): self.assertEquals(loss.op.name, 'sparse_softmax_cross_entropy_loss/value') self.assertAlmostEqual(loss.eval(), 10.0, 3) + @test_util.run_deprecated_v1 def testAllWrongInt64Labels(self): logits = constant_op.constant([[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 10.0]]) @@ -285,6 +300,7 @@ class SparseSoftmaxCrossEntropyLossTest(test.TestCase): self.assertEquals(loss.op.name, 'sparse_softmax_cross_entropy_loss/value') self.assertAlmostEqual(loss.eval(), 10.0, 3) + @test_util.run_deprecated_v1 def testAllWrongNonColumnLabels(self): logits = constant_op.constant([[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 10.0]]) @@ -295,6 +311,7 @@ class SparseSoftmaxCrossEntropyLossTest(test.TestCase): self.assertEquals(loss.op.name, 'sparse_softmax_cross_entropy_loss/value') self.assertAlmostEqual(loss.eval(), 10.0, 3) + @test_util.run_deprecated_v1 def testNonZeroLossWithPythonScalarWeight(self): logits = constant_op.constant([[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 10.0]]) @@ -302,8 +319,9 @@ class SparseSoftmaxCrossEntropyLossTest(test.TestCase): weights = 2.3 with self.cached_session(): loss = losses.sparse_softmax_cross_entropy(labels, logits, weights) - self.assertAlmostEqual(weights * 10.0, loss.eval(), 3) + self.assertAlmostEqual(weights * 10.0, self.evaluate(loss), 3) + @test_util.run_deprecated_v1 def testNonZeroLossWithScalarTensorWeight(self): logits = constant_op.constant([[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 10.0]]) @@ -312,7 +330,7 @@ class SparseSoftmaxCrossEntropyLossTest(test.TestCase): with self.cached_session(): loss = losses.sparse_softmax_cross_entropy(labels, logits, constant_op.constant(weights)) - self.assertAlmostEqual(weights * 10.0, loss.eval(), 3) + self.assertAlmostEqual(weights * 10.0, self.evaluate(loss), 3) def testNonZeroLossWith1DTensorWeight(self): logits = constant_op.constant([[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], @@ -322,8 +340,9 @@ class SparseSoftmaxCrossEntropyLossTest(test.TestCase): with self.cached_session(): loss = losses.sparse_softmax_cross_entropy( labels, logits, constant_op.constant((weights,))) - self.assertAlmostEqual(weights * 10.0, loss.eval(), 3) + self.assertAlmostEqual(weights * 10.0, self.evaluate(loss), 3) + @test_util.run_deprecated_v1 def testNonZeroLossWithPlaceholderForWeights(self): logits = constant_op.constant([[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], @@ -336,6 +355,7 @@ class SparseSoftmaxCrossEntropyLossTest(test.TestCase): feed_dict={weights: ((1.2,), (3.4,), (5.6,))}) self.assertAlmostEqual((1.2 + 3.4 + 5.6) * 10.0 / 3.0, loss_val, 3) + @test_util.run_deprecated_v1 def testUnknownShapePlaceholderForLogitsLabelsButScalarWeights(self): logits = array_ops.placeholder(dtypes.float32) labels = array_ops.placeholder(dtypes.int32) @@ -351,6 +371,7 @@ class SparseSoftmaxCrossEntropyLossTest(test.TestCase): }) self.assertAlmostEqual((1.0 + 1.0 + 1.0) * 10.0 / 3.0, loss_val, 3) + @test_util.run_deprecated_v1 def testNonZeroLossWithPlaceholderForLogitsLabelsAndWeights(self): logits = array_ops.placeholder(dtypes.float32, shape=(None, 3)) labels = array_ops.placeholder(dtypes.int32, shape=(None, 1)) @@ -374,7 +395,8 @@ class SparseSoftmaxCrossEntropyLossTest(test.TestCase): weights = constant_op.constant([1.2, 3.4, 5.6], shape=(3, 1)) with self.cached_session(): loss = losses.sparse_softmax_cross_entropy(labels, logits, weights) - self.assertAlmostEqual((1.2 + 3.4 + 5.6) * 10.0 / 3.0, loss.eval(), 3) + self.assertAlmostEqual((1.2 + 3.4 + 5.6) * 10.0 / 3.0, + self.evaluate(loss), 3) def testNonZeroLossWithColumnWeights(self): logits = constant_op.constant([[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], @@ -383,7 +405,8 @@ class SparseSoftmaxCrossEntropyLossTest(test.TestCase): weights = constant_op.constant([[1.2], [3.4], [5.6]]) with self.cached_session(): loss = losses.sparse_softmax_cross_entropy(labels, logits, weights) - self.assertAlmostEqual((1.2 + 3.4 + 5.6) * 10.0 / 3.0, loss.eval(), 3) + self.assertAlmostEqual((1.2 + 3.4 + 5.6) * 10.0 / 3.0, + self.evaluate(loss), 3) def testAllWrongAllWeightsMissing(self): logits = constant_op.constant([[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], @@ -392,7 +415,7 @@ class SparseSoftmaxCrossEntropyLossTest(test.TestCase): weights = constant_op.constant([0, 0, 0], shape=(3, 1)) with self.cached_session(): loss = losses.sparse_softmax_cross_entropy(labels, logits, weights) - self.assertAlmostEqual(0.0, loss.eval(), 3) + self.assertAlmostEqual(0.0, self.evaluate(loss), 3) def testSomeWeightsMissing(self): logits = constant_op.constant([[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], @@ -401,8 +424,9 @@ class SparseSoftmaxCrossEntropyLossTest(test.TestCase): weights = constant_op.constant([1.2, 0, 0], shape=(3, 1)) with self.cached_session(): loss = losses.sparse_softmax_cross_entropy(labels, logits, weights) - self.assertAlmostEqual(12.0, loss.eval(), 3) + self.assertAlmostEqual(12.0, self.evaluate(loss), 3) + @test_util.run_deprecated_v1 def testMeasurementSpecificWeightsRaisesException(self): with self.cached_session(): logits = constant_op.constant([[100.0, -100.0, -100.0], @@ -441,6 +465,7 @@ class SparseSoftmaxCrossEntropyLossTest(test.TestCase): losses.sparse_softmax_cross_entropy( labels, logits, weights=weights).eval() + @test_util.run_deprecated_v1 def testInconsistentWeightShapeRaisesException(self): """The weight tensor has incorrect shape.""" with self.cached_session(): @@ -455,6 +480,7 @@ class SparseSoftmaxCrossEntropyLossTest(test.TestCase): losses.sparse_softmax_cross_entropy( labels, logits, weights=weights).eval() + @test_util.run_deprecated_v1 def testInconsistentLabelShapeRaisesException(self): """The label tensor has incorrect shape.""" with self.cached_session(): @@ -472,6 +498,7 @@ class SparseSoftmaxCrossEntropyLossTest(test.TestCase): class SigmoidCrossEntropyLossTest(test.TestCase): + @test_util.run_deprecated_v1 def testAllCorrectSigmoid(self): with self.cached_session(): logits = constant_op.constant([[100.0, -100.0, -100.0], @@ -481,8 +508,9 @@ class SigmoidCrossEntropyLossTest(test.TestCase): loss = losses.sigmoid_cross_entropy(labels, logits) self.assertEquals(logits.dtype, loss.dtype) self.assertEquals('sigmoid_cross_entropy_loss/value', loss.op.name) - self.assertAlmostEqual(0.0, loss.eval(), 3) + self.assertAlmostEqual(0.0, self.evaluate(loss), 3) + @test_util.run_deprecated_v1 def testLossWithSingleDimPlaceholderForLogitsAndWeights1(self): logits = array_ops.placeholder(dtypes.float32, shape=(None, 1)) labels = array_ops.placeholder(dtypes.float32, shape=(None, 1)) @@ -499,6 +527,7 @@ class SigmoidCrossEntropyLossTest(test.TestCase): }) self.assertAlmostEqual(0.313, loss, 3) + @test_util.run_deprecated_v1 def testLossWithSingleDimPlaceholderForLogitsAndWeights2(self): logits = array_ops.placeholder(dtypes.float32, shape=(None, 2)) labels = array_ops.placeholder(dtypes.float32, shape=(None, 2)) @@ -515,6 +544,7 @@ class SigmoidCrossEntropyLossTest(test.TestCase): }) self.assertAlmostEqual(0.313, loss, 3) + @test_util.run_deprecated_v1 def testAllWrongSigmoid(self): with self.cached_session(): logits = constant_op.constant([[100.0, -100.0, -100.0], @@ -526,6 +556,7 @@ class SigmoidCrossEntropyLossTest(test.TestCase): self.assertEquals('sigmoid_cross_entropy_loss/value', loss.op.name) self.assertAlmostEqual(loss.eval(), 600.0 / 9.0, 3) + @test_util.run_deprecated_v1 def testAllWrongSigmoidWithMeasurementSpecificWeights(self): with self.cached_session(): logits = constant_op.constant([[100.0, -100.0, -100.0], @@ -536,8 +567,9 @@ class SigmoidCrossEntropyLossTest(test.TestCase): loss = losses.sigmoid_cross_entropy(labels, logits, weights) self.assertEquals(logits.dtype, loss.dtype) self.assertEquals('sigmoid_cross_entropy_loss/value', loss.op.name) - self.assertAlmostEqual(1700.0 / 7.0, loss.eval(), 3) + self.assertAlmostEqual(1700.0 / 7.0, self.evaluate(loss), 3) + @test_util.run_deprecated_v1 def testMultiCorrectSigmoid(self): logits = constant_op.constant([[100.0, -100.0, 100.0], [100.0, 100.0, -100.0], @@ -548,7 +580,7 @@ class SigmoidCrossEntropyLossTest(test.TestCase): self.assertEquals('sigmoid_cross_entropy_loss/value', loss.op.name) with self.cached_session(): - self.assertAlmostEqual(0.0, loss.eval(), 3) + self.assertAlmostEqual(0.0, self.evaluate(loss), 3) def testSigmoidFloat64(self): logits = constant_op.constant(( @@ -563,7 +595,7 @@ class SigmoidCrossEntropyLossTest(test.TestCase): self.assertEquals(logits.dtype, loss.dtype) with self.cached_session(): - self.assertAlmostEqual(44.444, loss.eval(), 3) + self.assertAlmostEqual(44.444, self.evaluate(loss), 3) def testSigmoidNoReduction(self): logits = constant_op.constant(( @@ -576,12 +608,10 @@ class SigmoidCrossEntropyLossTest(test.TestCase): self.assertEquals(logits.dtype, loss.dtype) with self.cached_session(): - self.assertAllClose(( - (0., 0., 0.), - (0., 100., 100.), - (100., 0., 100.) - ), loss.eval(), 3) + self.assertAllClose(((0., 0., 0.), (0., 100., 100.), (100., 0., 100.)), + self.evaluate(loss), 3) + @test_util.run_deprecated_v1 def testSigmoidLabelSmoothingCorrect(self): with self.cached_session(): logits = constant_op.constant([[100.0, -100.0, -100.0]]) @@ -605,6 +635,7 @@ class SigmoidCrossEntropyLossTest(test.TestCase): expected_value = (100.0 + 50.0 * label_smoothing) / 3.0 self.assertAlmostEqual(loss.eval(), expected_value, 3) + @test_util.run_deprecated_v1 def testSigmoidLabelSmoothingEqualsSoftmaxTwoLabel(self): with self.cached_session(): label_smoothing = 0.1 @@ -619,7 +650,8 @@ class SigmoidCrossEntropyLossTest(test.TestCase): softmax_labels = constant_op.constant([[0, 1], [1, 0], [0, 1]]) softmax_loss = losses.softmax_cross_entropy( softmax_labels, softmax_logits, label_smoothing=label_smoothing) - self.assertAlmostEqual(sigmoid_loss.eval(), softmax_loss.eval(), 3) + self.assertAlmostEqual(sigmoid_loss.eval(), self.evaluate(softmax_loss), + 3) class LogLossTest(test.TestCase): @@ -645,11 +677,13 @@ class LogLossTest(test.TestCase): with self.assertRaises(ValueError): losses.log_loss(self._labels, self._labels, weights=None) + @test_util.run_deprecated_v1 def testAllCorrectNoLossWeight(self): loss = losses.log_loss(self._labels, self._labels) with self.cached_session(): - self.assertAlmostEqual(0.0, loss.eval(), 3) + self.assertAlmostEqual(0.0, self.evaluate(loss), 3) + @test_util.run_deprecated_v1 def testAllCorrectNoLossWeightWithPlaceholder(self): tf_predictions = array_ops.placeholder( dtypes.float32, shape=self._np_labels.shape) @@ -658,27 +692,31 @@ class LogLossTest(test.TestCase): self.assertAlmostEqual( 0.0, loss.eval(feed_dict={tf_predictions: self._np_labels}), 3) + @test_util.run_deprecated_v1 def testNonZeroLoss(self): loss = losses.log_loss(self._labels, self._predictions) with self.cached_session(): self.assertAlmostEqual(-np.sum(self._expected_losses) / 6.0, - loss.eval(), 3) + self.evaluate(loss), 3) + @test_util.run_deprecated_v1 def testNonZeroLossWithPythonScalarWeight(self): weights = 2.3 loss = losses.log_loss(self._labels, self._predictions, weights) with self.cached_session(): self.assertAlmostEqual(weights * -np.sum(self._expected_losses) / 6.0, - loss.eval(), 3) + self.evaluate(loss), 3) + @test_util.run_deprecated_v1 def testNonZeroLossWithScalarTensorWeight(self): weights = 2.3 loss = losses.log_loss(self._labels, self._predictions, constant_op.constant(weights)) with self.cached_session(): self.assertAlmostEqual(weights * -np.sum(self._expected_losses) / 6.0, - loss.eval(), 3) + self.evaluate(loss), 3) + @test_util.run_deprecated_v1 def testNonZeroLossWithScalarTensorWeightAndPlaceholder(self): tf_predictions = array_ops.placeholder( dtypes.float32, shape=self._np_predictions.shape) @@ -690,6 +728,7 @@ class LogLossTest(test.TestCase): self.assertAlmostEqual(weights * -np.sum(self._expected_losses) / 6.0, loss, 3) + @test_util.run_deprecated_v1 def testNonZeroLossWithScalarTensorWeightAndPlaceholderWithRankOnly(self): tf_predictions = array_ops.placeholder(dtypes.float32, shape=[None, None]) weights = 2.3 @@ -707,7 +746,8 @@ class LogLossTest(test.TestCase): np.asarray([1.2, 1.2, 1.2, 3.4, 3.4, 3.4]).reshape((2, 3))) loss = losses.log_loss(self._labels, self._predictions, weights) with self.cached_session(): - self.assertAlmostEqual(-np.sum(expected_losses) / 6.0, loss.eval(), 3) + self.assertAlmostEqual(-np.sum(expected_losses) / 6.0, + self.evaluate(loss), 3) def testNonZeroLossWithOneDimBatchSpecificWeightsSomeZero(self): weights = constant_op.constant((1.2, 0), shape=(2, 1)) @@ -716,7 +756,8 @@ class LogLossTest(test.TestCase): (2, 3))) loss = losses.log_loss(self._labels, self._predictions, weights) with self.cached_session(): - self.assertAlmostEqual(-np.sum(expected_losses) / 3.0, loss.eval(), 3) + self.assertAlmostEqual(-np.sum(expected_losses) / 3.0, + self.evaluate(loss), 3) def testNonZeroLossWithTwoDimBatchSpecificWeightsSomeZero(self): weights = constant_op.constant([1.2, 0], shape=[2, 1]) @@ -725,7 +766,8 @@ class LogLossTest(test.TestCase): (2, 3))) loss = losses.log_loss(self._labels, self._predictions, weights) with self.cached_session(): - self.assertAlmostEqual(-np.sum(expected_losses) / 3.0, loss.eval(), 3) + self.assertAlmostEqual(-np.sum(expected_losses) / 3.0, + self.evaluate(loss), 3) def testWeightsWithSameNumDimsButWrongShapeThrowsException(self): weights = constant_op.constant(np.random.normal(size=(2, 4)), shape=[2, 4]) @@ -743,8 +785,10 @@ class LogLossTest(test.TestCase): constant_op.constant( weights, shape=(2, 3))) with self.cached_session(): - self.assertAlmostEqual(-np.sum(expected_losses) / 5.0, loss.eval(), 3) + self.assertAlmostEqual(-np.sum(expected_losses) / 5.0, + self.evaluate(loss), 3) + @test_util.run_deprecated_v1 def testNonZeroLossWithMeasurementSpecificWeightsWithPlaceholder(self): weights = np.array([3, 6, 5, 0, 4, 2]).reshape((2, 3)) expected_losses = np.multiply(self._expected_losses, weights) @@ -770,8 +814,9 @@ class LogLossTest(test.TestCase): constant_op.constant( weights, shape=(2, 3))) with self.cached_session(): - self.assertAlmostEqual(-np.sum(expected_losses), loss.eval(), 3) + self.assertAlmostEqual(-np.sum(expected_losses), self.evaluate(loss), 3) + @test_util.run_deprecated_v1 def testNonZeroLossWithSampleSpecificWeightsMostZeroWithPlaceholder(self): weights = np.array([0, 0, 0, 0, 0, 2]).reshape((2, 3)) expected_losses = np.multiply(self._expected_losses, weights) @@ -788,7 +833,7 @@ class LogLossTest(test.TestCase): tf_weights = array_ops.zeros(shape=(2, 3)) loss = losses.log_loss(self._labels, self._predictions, tf_weights) with self.cached_session(): - self.assertAlmostEqual(0.0, loss.eval(), 3) + self.assertAlmostEqual(0.0, self.evaluate(loss), 3) class HingeLossTest(test.TestCase): @@ -800,6 +845,7 @@ class HingeLossTest(test.TestCase): with self.assertRaises(ValueError): _ = losses.hinge_loss(labels, logits).eval() + @test_util.run_deprecated_v1 def testAllOutsideMargin(self): with self.cached_session(): logits = constant_op.constant([1.2, -1.4, -1.0, 2.1]) @@ -807,6 +853,7 @@ class HingeLossTest(test.TestCase): loss = losses.hinge_loss(labels, logits) self.assertAllClose(loss.eval(), 0.0, atol=1e-3) + @test_util.run_deprecated_v1 def testSomeInsideMargin(self): with self.cached_session(): logits = constant_op.constant([[-0.7], [-1.4], [1.4], [0.6]]) @@ -816,6 +863,7 @@ class HingeLossTest(test.TestCase): # the margin so they incur some (small) loss. self.assertAllClose(loss.eval(), 0.175, atol=1e-3) + @test_util.run_deprecated_v1 def testSomeMisclassified(self): with self.cached_session(): logits = constant_op.constant([[[1.2], [0.4], [-1.0], [-1.1]]]) @@ -835,6 +883,7 @@ class HuberLossTest(test.TestCase): with self.assertRaises(ValueError): _ = losses.huber_loss(labels, predictions).eval() + @test_util.run_deprecated_v1 def testAllQuadratic(self): with self.cached_session(): predictions = constant_op.constant([1.5, -1.4, -1.0, 0.0]) @@ -843,6 +892,7 @@ class HuberLossTest(test.TestCase): self.assertAllClose(loss.eval(), 0.5 * (0.25 + 0.16 + 1.0 + 0.25) / 4., atol=1e-5) + @test_util.run_deprecated_v1 def testAllLinear(self): with self.cached_session(): predictions = constant_op.constant([1.5, -1.4, -1.0, 0.0]) @@ -851,6 +901,7 @@ class HuberLossTest(test.TestCase): self.assertAllClose(loss.eval(), (1.5 + 2.4 + 1.0 + 1.5) / 4. - 0.5, atol=1e-5) + @test_util.run_deprecated_v1 def testMixedQuadraticLinear(self): with self.cached_session(): predictions = constant_op.constant([[1.5, -1.4, -1.0, 0.0], @@ -870,7 +921,7 @@ class HuberLossTest(test.TestCase): labels = constant_op.constant([1.0, -1.0, 0.0, 0.5]) expected = 0.5 * np.array([0.5**2, 0.4**2, 0.5**2, 0.5**2]).mean() loss = losses.huber_loss(labels, predictions, delta=delta) - self.assertAllClose(expected, loss.eval(), atol=1e-5) + self.assertAllClose(expected, self.evaluate(loss), atol=1e-5) def testAllLinearDelta(self): delta = 0.5 @@ -880,7 +931,7 @@ class HuberLossTest(test.TestCase): expected -= 0.5 * delta**2 loss = losses.huber_loss(labels, predictions, delta=delta) with self.cached_session(): - self.assertAllClose(expected, loss.eval(), atol=1e-5) + self.assertAllClose(expected, self.evaluate(loss), atol=1e-5) class MeanSquaredErrorTest(test.TestCase): @@ -896,6 +947,7 @@ class MeanSquaredErrorTest(test.TestCase): losses.mean_squared_error( self._predictions, self._predictions, weights=None) + @test_util.run_deprecated_v1 def testScalar(self): with self.cached_session(): self.assertEqual( @@ -903,58 +955,62 @@ class MeanSquaredErrorTest(test.TestCase): losses.mean_squared_error(predictions=constant_op.constant(0), labels=constant_op.constant(0)).eval()) + @test_util.run_deprecated_v1 def testAllCorrectNoLossWeight(self): loss = losses.mean_squared_error(self._predictions, self._predictions) with self.cached_session(): - self.assertAlmostEqual(0.0, loss.eval(), 3) + self.assertAlmostEqual(0.0, self.evaluate(loss), 3) + @test_util.run_deprecated_v1 def testNonZeroLoss(self): loss = losses.mean_squared_error(self._labels, self._predictions) with self.cached_session(): - self.assertAlmostEqual(49.5, loss.eval(), 3) + self.assertAlmostEqual(49.5, self.evaluate(loss), 3) + @test_util.run_deprecated_v1 def testNonZeroLossWithPythonScalarWeight(self): weights = 2.3 loss = losses.mean_squared_error(self._labels, self._predictions, weights) with self.cached_session(): - self.assertAlmostEqual(49.5 * weights, loss.eval(), 3) + self.assertAlmostEqual(49.5 * weights, self.evaluate(loss), 3) + @test_util.run_deprecated_v1 def testNonZeroLossWithScalarTensorWeight(self): weights = 2.3 loss = losses.mean_squared_error(self._labels, self._predictions, constant_op.constant(weights)) with self.cached_session(): - self.assertAlmostEqual(49.5 * weights, loss.eval(), 3) + self.assertAlmostEqual(49.5 * weights, self.evaluate(loss), 3) def testNonZeroLossWithOneDimBatchSpecificWeights(self): weights = constant_op.constant([1.2, 3.4], shape=(2, 1)) loss = losses.mean_squared_error(self._labels, self._predictions, weights) with self.cached_session(): - self.assertAlmostEqual(767.8 / 6.0, loss.eval(), 3) + self.assertAlmostEqual(767.8 / 6.0, self.evaluate(loss), 3) def testNonZeroLossWithTwoDimBatchSpecificWeights(self): weights = constant_op.constant([1.2, 3.4], shape=[2, 1]) loss = losses.mean_squared_error(self._labels, self._predictions, weights) with self.cached_session(): - self.assertAlmostEqual(767.8 / 6.0, loss.eval(), 3) + self.assertAlmostEqual(767.8 / 6.0, self.evaluate(loss), 3) def testNonZeroLossWithSampleSpecificWeights(self): weights = constant_op.constant([3, 6, 5, 0, 4, 2], shape=[2, 3]) loss = losses.mean_squared_error(self._labels, self._predictions, weights) with self.cached_session(): - self.assertAlmostEqual(587 / 5.0, loss.eval(), 3) + self.assertAlmostEqual(587 / 5.0, self.evaluate(loss), 3) def testNonZeroLossWithSampleSpecificWeightsMostZero(self): weights = constant_op.constant([0, 0, 0, 0, 0, 2], shape=[2, 3]) loss = losses.mean_squared_error(self._labels, self._predictions, weights) with self.cached_session(): - self.assertAlmostEqual(18.0, loss.eval(), 3) + self.assertAlmostEqual(18.0, self.evaluate(loss), 3) def testLossWithSampleSpecificWeightsAllZero(self): weights = array_ops.zeros((2, 3)) loss = losses.mean_squared_error(self._labels, self._predictions, weights) with self.cached_session(): - self.assertAlmostEqual(0.0, loss.eval(), 3) + self.assertAlmostEqual(0.0, self.evaluate(loss), 3) class MeanPairwiseSquaredErrorTest(test.TestCase): @@ -991,7 +1047,8 @@ class MeanPairwiseSquaredErrorTest(test.TestCase): with self.cached_session(): static_inputs_op = losses.mean_pairwise_squared_error( predictions=predictions, labels=labels, weights=weights) - self.assertAlmostEqual(expected_loss, static_inputs_op.eval(), places=3) + self.assertAlmostEqual( + expected_loss, self.evaluate(static_inputs_op), places=3) predictions_placeholder = array_ops.placeholder( dtypes.float32, shape=np.asarray(predictions.shape)) @@ -1011,10 +1068,12 @@ class MeanPairwiseSquaredErrorTest(test.TestCase): self.assertAlmostEqual( expected_loss, dynamic_inputs_op.eval(feed_dict=feed_dict), places=3) + @test_util.run_deprecated_v1 def testAllCorrectNoLossWeight(self): self._test_valid_weights( self._labels, self._labels, expected_loss=0.0) + @test_util.run_deprecated_v1 def testNonZeroLoss(self): self._test_valid_weights( self._labels, self._predictions, @@ -1040,11 +1099,12 @@ class MeanPairwiseSquaredErrorTest(test.TestCase): init_op = variables.global_variables_initializer() with self.cached_session() as sess: - sess.run(init_op) + self.evaluate(init_op) for grad, _ in gradients_to_variables: - np_grad = sess.run(grad) + np_grad = self.evaluate(grad) self.assertFalse(np.isnan(np_grad).any()) + @test_util.run_deprecated_v1 def testNonZeroLossWithPythonScalarWeight(self): weight = 2.3 self._test_valid_weights( @@ -1052,6 +1112,7 @@ class MeanPairwiseSquaredErrorTest(test.TestCase): expected_loss=weight * np.sum(self._expected_losses), weights=weight) + @test_util.run_deprecated_v1 def testNonZeroLossWithScalarTensorWeight(self): weights = 2.3 loss = losses.mean_pairwise_squared_error( @@ -1060,12 +1121,14 @@ class MeanPairwiseSquaredErrorTest(test.TestCase): weights=constant_op.constant(weights)) with self.cached_session(): self.assertAlmostEqual(weights * np.sum(self._expected_losses), - loss.eval(), 3) + self.evaluate(loss), 3) + @test_util.run_deprecated_v1 def testNonZeroLossWithScalarZeroWeight(self): self._test_valid_weights( self._labels, self._predictions, expected_loss=0.0, weights=0.0) + @test_util.run_deprecated_v1 def test3d(self): labels = np.array([ [[1, 9, 2], [12, 11, 10], [9, 8, 7]], @@ -1077,6 +1140,7 @@ class MeanPairwiseSquaredErrorTest(test.TestCase): ]) self._test_valid_weights(labels, predictions, expected_loss=137.5) + @test_util.run_deprecated_v1 def test3dWeightedScalar(self): labels = np.array([ [[1, 9, 2], [12, 11, 10], [9, 8, 7]], @@ -1115,6 +1179,7 @@ class MeanPairwiseSquaredErrorTest(test.TestCase): weights_placeholder: weights, }) + @test_util.run_deprecated_v1 def testInvalid3dWeighted2x0(self): labels = np.array([ [[1, 9, 2], [12, 11, 10], [9, 8, 7]], @@ -1127,6 +1192,7 @@ class MeanPairwiseSquaredErrorTest(test.TestCase): self._test_invalid_weights( labels, predictions, weights=np.asarray((1.2, 3.4))) + @test_util.run_deprecated_v1 def test3dWeighted2x3x3(self): labels = np.array([ [[1, 9, 2], [12, 11, 10], [9, 8, 7]], @@ -1143,6 +1209,7 @@ class MeanPairwiseSquaredErrorTest(test.TestCase): expected_loss=9 * 137.5, weights=np.ones((2, 3, 3))) + @test_util.run_deprecated_v1 def testLossWithAllZeroBatchSpecificWeights(self): self._test_valid_weights( self._labels, self._predictions, expected_loss=0.0, @@ -1215,7 +1282,7 @@ class CosineDistanceLossTest(test.TestCase): labels=constant_op.constant(self._labels), dim=2) with self.cached_session(): - self.assertAlmostEqual(0, loss.eval(), 5) + self.assertAlmostEqual(0, self.evaluate(loss), 5) def testPartiallyCorrectWithIntegerValues(self): loss = losses.cosine_distance( @@ -1223,7 +1290,7 @@ class CosineDistanceLossTest(test.TestCase): labels=constant_op.constant(self._labels), dim=2) with self.cached_session(): - self.assertAlmostEqual(1, loss.eval(), 5) + self.assertAlmostEqual(1, self.evaluate(loss), 5) def testPartiallyCorrectFloatingPointValues(self): predictions = np.matrix( @@ -1241,7 +1308,7 @@ class CosineDistanceLossTest(test.TestCase): loss = losses.cosine_distance(tf_labels, tf_preds, dim=2) with self.cached_session(): - self.assertAlmostEqual(1.0, loss.eval(), 5) + self.assertAlmostEqual(1.0, self.evaluate(loss), 5) def testSampleSpecificWeights(self): loss = losses.cosine_distance( @@ -1250,7 +1317,7 @@ class CosineDistanceLossTest(test.TestCase): dim=2, weights=np.asarray((1, 0, 0)).reshape((3, 1, 1))) with self.cached_session(): - self.assertEqual(1.0, loss.eval()) + self.assertEqual(1.0, self.evaluate(loss)) def testMeasurementSpecificWeights(self): loss = losses.cosine_distance( @@ -1260,8 +1327,9 @@ class CosineDistanceLossTest(test.TestCase): weights=constant_op.constant( [1, 0, 0, 1, 1, 1], shape=(3, 2, 1))) with self.cached_session(): - self.assertEqual(3.0 / 4.0, loss.eval()) + self.assertEqual(3.0 / 4.0, self.evaluate(loss)) + @test_util.run_deprecated_v1 def testMeasurementSpecificWeightsWithPlaceholderWithShape(self): tf_predictions = array_ops.placeholder( dtypes.float32, shape=self._labels.shape) @@ -1282,7 +1350,7 @@ class CosineDistanceLossTest(test.TestCase): dim=2, weights=array_ops.zeros((3, 1, 1))) with self.cached_session(): - self.assertEqual(0, loss.eval()) + self.assertEqual(0, self.evaluate(loss)) def testZeroLossWhenAllMeasurementSpecificWeightsAreZero(self): loss = losses.cosine_distance( @@ -1291,7 +1359,7 @@ class CosineDistanceLossTest(test.TestCase): dim=2, weights=array_ops.zeros((3, 2, 1))) with self.cached_session(): - self.assertEqual(0, loss.eval()) + self.assertEqual(0, self.evaluate(loss)) class AddLossTest(test.TestCase): @@ -1351,15 +1419,16 @@ class ComputeWeightedLossTest(test.TestCase): with self.session(g): for unweighted_loss in unweighted_losses: if reduction == losses.Reduction.NONE: - self.assertAllClose(self._raw_losses, unweighted_loss.eval()) + self.assertAllClose(self._raw_losses, + self.evaluate(unweighted_loss)) elif reduction == losses.Reduction.SUM: self.assertAllClose( - np.sum(self._raw_losses), unweighted_loss.eval()) + np.sum(self._raw_losses), self.evaluate(unweighted_loss)) else: # reduction one of MEAN, SUM_OVER_NONZERO_WEIGHTS, # SUM_BY_NONZERO_WEIGHTS or SUM_OVER_BATCH_SIZE. self.assertAllClose( - np.mean(self._raw_losses), unweighted_loss.eval()) + np.mean(self._raw_losses), self.evaluate(unweighted_loss)) def testUnweightedFromPlaceholder(self): for reduction in losses.Reduction.all(): @@ -1398,7 +1467,7 @@ class ComputeWeightedLossTest(test.TestCase): self.assertEqual(1, len(util.get_losses())) with self.cached_session(): self.assertAllClose( - np.mean(weight * self._raw_losses), weighted_loss.eval()) + np.mean(weight * self._raw_losses), self.evaluate(weighted_loss)) def _test_invalid_weights(self, weights): with ops.Graph().as_default(): @@ -1470,24 +1539,22 @@ class ComputeWeightedLossTest(test.TestCase): weighted_losses = weights * self._raw_losses weighted_sum = np.sum(weighted_losses) if reduction == losses.Reduction.NONE: - self.assertAllClose(weighted_losses, weighted_loss.eval()) + self.assertAllClose(weighted_losses, self.evaluate(weighted_loss)) elif reduction == losses.Reduction.SUM: - self.assertAllClose(weighted_sum, weighted_loss.eval()) + self.assertAllClose(weighted_sum, self.evaluate(weighted_loss)) else: broadcast_weights = weights * np.ones_like(self._raw_losses) if reduction == losses.Reduction.MEAN: - self.assertAllClose( - weighted_sum / np.sum(broadcast_weights), - weighted_loss.eval()) + self.assertAllClose(weighted_sum / np.sum(broadcast_weights), + self.evaluate(weighted_loss)) elif (reduction == losses.Reduction.SUM_OVER_NONZERO_WEIGHTS or reduction == losses.Reduction.SUM_BY_NONZERO_WEIGHTS): self.assertAllClose( weighted_sum / np.count_nonzero(broadcast_weights), - weighted_loss.eval()) + self.evaluate(weighted_loss)) elif reduction == losses.Reduction.SUM_OVER_BATCH_SIZE: - self.assertAllClose( - weighted_sum / self._raw_losses.size, - weighted_loss.eval()) + self.assertAllClose(weighted_sum / self._raw_losses.size, + self.evaluate(weighted_loss)) def test1x1x1Weight(self): self._test_valid_weights((((17.0,),),)) diff --git a/tensorflow/python/kernel_tests/lrn_op_test.py b/tensorflow/python/kernel_tests/lrn_op_test.py index 7ebeb91d90..fbe628c394 100644 --- a/tensorflow/python/kernel_tests/lrn_op_test.py +++ b/tensorflow/python/kernel_tests/lrn_op_test.py @@ -24,6 +24,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import gradients_impl @@ -92,6 +93,7 @@ class LRNOpTest(test.TestCase): self.assertTrue(err < 1e-2) self.assertShapeEqual(expected, lrn_t) + @test_util.run_deprecated_v1 def testCompute(self): for _ in range(2): self._RunAndVerify(dtypes.float32) @@ -99,6 +101,7 @@ class LRNOpTest(test.TestCase): if not test.is_gpu_available(): self._RunAndVerify(dtypes.float16) + @test_util.run_deprecated_v1 def testGradientsZeroInput(self): with self.session(use_gpu=True): shape = [4, 4, 4, 4] @@ -147,6 +150,7 @@ class LRNOpTest(test.TestCase): else: self.assertLess(err, 1.0) + @test_util.run_deprecated_v1 def testGradients(self): for _ in range(2): self._RunAndVerifyGradients(dtypes.float32) diff --git a/tensorflow/python/kernel_tests/manip_ops_test.py b/tensorflow/python/kernel_tests/manip_ops_test.py index f71857a3cb..5700db4b95 100644 --- a/tensorflow/python/kernel_tests/manip_ops_test.py +++ b/tensorflow/python/kernel_tests/manip_ops_test.py @@ -62,6 +62,7 @@ class RollTest(test_util.TensorFlowTestCase): if np_input.dtype == np.float32: self._testGradient(np_input, shift, axis) + @test_util.run_deprecated_v1 def testIntTypes(self): for t in [np.int32, np.int64]: self._testAll(np.random.randint(-100, 100, (5)).astype(t), 3, 0) @@ -73,6 +74,7 @@ class RollTest(test_util.TensorFlowTestCase): np.random.randint(-100, 100, (4, 2, 1, 3)).astype(t), [0, 1, -2], [1, 2, 3]) + @test_util.run_deprecated_v1 def testFloatTypes(self): for t in [np.float32, np.float64]: self._testAll(np.random.rand(5).astype(t), 2, 0) @@ -80,6 +82,7 @@ class RollTest(test_util.TensorFlowTestCase): self._testAll(np.random.rand(3, 4).astype(t), [1, 2], [1, 0]) self._testAll(np.random.rand(1, 3, 4).astype(t), [1, 0, -3], [0, 1, 2]) + @test_util.run_deprecated_v1 def testComplexTypes(self): for t in [np.complex64, np.complex128]: x = np.random.rand(4, 4).astype(t) @@ -90,6 +93,7 @@ class RollTest(test_util.TensorFlowTestCase): x = np.random.rand(3, 2, 1, 1).astype(t) self._testAll(x + 1j * x, [2, 1, 1, 0], [0, 3, 1, 2]) + @test_util.run_deprecated_v1 def testNegativeAxis(self): self._testAll(np.random.randint(-100, 100, (5)).astype(np.int32), 3, -1) self._testAll(np.random.randint(-100, 100, (4, 4)).astype(np.int32), 3, -2) @@ -100,12 +104,14 @@ class RollTest(test_util.TensorFlowTestCase): manip_ops.roll(np.random.randint(-100, 100, (4, 4)).astype(np.int32), 3, -10).eval() + @test_util.run_deprecated_v1 def testInvalidInputShape(self): # The input should be 1-D or higher, checked in shape function. with self.assertRaisesRegexp( ValueError, "Shape must be at least rank 1 but is rank 0"): manip_ops.roll(7, 1, 0) + @test_util.run_deprecated_v1 def testRollInputMustVectorHigherRaises(self): # The input should be 1-D or higher, checked in kernel. tensor = array_ops.placeholder(dtype=dtypes.int32) @@ -116,12 +122,14 @@ class RollTest(test_util.TensorFlowTestCase): "input must be 1-D or higher"): manip_ops.roll(tensor, shift, axis).eval(feed_dict={tensor: 7}) + @test_util.run_deprecated_v1 def testInvalidAxisShape(self): # The axis should be a scalar or 1-D, checked in shape function. with self.assertRaisesRegexp( ValueError, "Shape must be at most rank 1 but is rank 2"): manip_ops.roll([[1, 2], [3, 4]], 1, [[0, 1]]) + @test_util.run_deprecated_v1 def testRollAxisMustBeScalarOrVectorRaises(self): # The axis should be a scalar or 1-D, checked in kernel. tensor = [[1, 2], [3, 4]] @@ -132,12 +140,14 @@ class RollTest(test_util.TensorFlowTestCase): "axis must be a scalar or a 1-D vector"): manip_ops.roll(tensor, shift, axis).eval(feed_dict={axis: [[0, 1]]}) + @test_util.run_deprecated_v1 def testInvalidShiftShape(self): # The shift should be a scalar or 1-D, checked in shape function. with self.assertRaisesRegexp( ValueError, "Shape must be at most rank 1 but is rank 2"): manip_ops.roll([[1, 2], [3, 4]], [[0, 1]], 1) + @test_util.run_deprecated_v1 def testRollShiftMustBeScalarOrVectorRaises(self): # The shift should be a scalar or 1-D, checked in kernel. tensor = [[1, 2], [3, 4]] @@ -148,11 +158,13 @@ class RollTest(test_util.TensorFlowTestCase): "shift must be a scalar or a 1-D vector"): manip_ops.roll(tensor, shift, axis).eval(feed_dict={shift: [[0, 1]]}) + @test_util.run_deprecated_v1 def testInvalidShiftAndAxisNotEqualShape(self): # The shift and axis must be same size, checked in shape function. with self.assertRaisesRegexp(ValueError, "both shapes must be equal"): manip_ops.roll([[1, 2], [3, 4]], [1], [0, 1]) + @test_util.run_deprecated_v1 def testRollShiftAndAxisMustBeSameSizeRaises(self): # The shift and axis must be same size, checked in kernel. tensor = [[1, 2], [3, 4]] diff --git a/tensorflow/python/kernel_tests/map_stage_op_test.py b/tensorflow/python/kernel_tests/map_stage_op_test.py index d503f3d7c9..dd16fad690 100644 --- a/tensorflow/python/kernel_tests/map_stage_op_test.py +++ b/tensorflow/python/kernel_tests/map_stage_op_test.py @@ -19,6 +19,7 @@ from __future__ import print_function from tensorflow.python.framework import errors from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import data_flow_ops from tensorflow.python.ops import math_ops @@ -29,6 +30,7 @@ TIMEOUT = 1 class MapStageTest(test.TestCase): + @test_util.run_deprecated_v1 def testSimple(self): with ops.Graph().as_default() as G: with ops.device('/cpu:0'): @@ -50,6 +52,7 @@ class MapStageTest(test.TestCase): _, yval = sess.run([stage, y], feed_dict={x: i, pi: i + 1, gi: i}) self.assertAllClose(4 * (i - 1) * (i - 1) * 128, yval, rtol=1e-4) + @test_util.run_deprecated_v1 def testMultiple(self): with ops.Graph().as_default() as G: with ops.device('/cpu:0'): @@ -72,6 +75,7 @@ class MapStageTest(test.TestCase): self.assertAllClose( 4 * (i - 1) * (i - 1) * (i - 1) * 128, yval, rtol=1e-4) + @test_util.run_deprecated_v1 def testDictionary(self): with ops.Graph().as_default() as G: with ops.device('/cpu:0'): @@ -121,6 +125,7 @@ class MapStageTest(test.TestCase): G.finalize() + @test_util.run_deprecated_v1 def testPeek(self): with ops.Graph().as_default() as G: with ops.device('/cpu:0'): @@ -150,6 +155,7 @@ class MapStageTest(test.TestCase): self.assertTrue(sess.run(size) == 10) + @test_util.run_deprecated_v1 def testSizeAndClear(self): with ops.Graph().as_default() as G: with ops.device('/cpu:0'): @@ -176,6 +182,7 @@ class MapStageTest(test.TestCase): sess.run(clear) self.assertEqual(sess.run(size), 0) + @test_util.run_deprecated_v1 def testCapacity(self): capacity = 3 @@ -239,6 +246,7 @@ class MapStageTest(test.TestCase): self.assertTrue(sess.run(size) == 0) + @test_util.run_deprecated_v1 def testMemoryLimit(self): memory_limit = 512 * 1024 # 512K chunk = 200 * 1024 # 256K @@ -303,6 +311,7 @@ class MapStageTest(test.TestCase): self.assertTrue(sess.run(size) == 0) + @test_util.run_deprecated_v1 def testOrdering(self): import six import random @@ -341,6 +350,7 @@ class MapStageTest(test.TestCase): self.assertTrue(sess.run(size) == 0) + @test_util.run_deprecated_v1 def testPartialDictInsert(self): with ops.Graph().as_default() as G: with ops.device('/cpu:0'): @@ -400,6 +410,7 @@ class MapStageTest(test.TestCase): 'v': 3 }]) + @test_util.run_deprecated_v1 def testPartialIndexInsert(self): with ops.Graph().as_default() as G: with ops.device('/cpu:0'): @@ -443,6 +454,7 @@ class MapStageTest(test.TestCase): # We can now obtain tuple associated with key 1 self.assertTrue(sess.run([key, ret], feed_dict={gi: 1}) == [1, [1, 3, 2]]) + @test_util.run_deprecated_v1 def testPartialDictGetsAndPeeks(self): with ops.Graph().as_default() as G: with ops.device('/cpu:0'): @@ -540,6 +552,7 @@ class MapStageTest(test.TestCase): # Nothing is left self.assertTrue(sess.run([size, isize]) == [0, 0]) + @test_util.run_deprecated_v1 def testPartialIndexGets(self): with ops.Graph().as_default() as G: with ops.device('/cpu:0'): diff --git a/tensorflow/python/kernel_tests/matmul_op_test.py b/tensorflow/python/kernel_tests/matmul_op_test.py index 1c2822180a..983f463f5e 100644 --- a/tensorflow/python/kernel_tests/matmul_op_test.py +++ b/tensorflow/python/kernel_tests/matmul_op_test.py @@ -44,7 +44,7 @@ class MatVecTest(test_lib.TestCase): with self.cached_session(): c = math_ops.matvec(a, b) self.assertAllEqual((2,), c.shape) - c_ = c.eval() + c_ = self.evaluate(c) self.assertAllEqual([5 + 2 * 6, 3 * 5 + 4 * 6], c_) @@ -90,7 +90,7 @@ def _GetMatMulTest(a_np_, b_np_, use_static_shape_, **kwargs_): a = constant_op.constant(effective_a_np) b = constant_op.constant(effective_b_np) res = math_ops.matmul(a, b, **kwargs_) - tf_val = res.eval() + tf_val = self.evaluate(res) else: a = array_ops.placeholder(a_np_.dtype) b = array_ops.placeholder(b_np_.dtype) @@ -194,6 +194,7 @@ except AttributeError: class MatMulInfixOperatorTest(test_lib.TestCase): + @test_util.run_deprecated_v1 def testMismatchedShape(self): with self.assertRaisesWithPredicateMatch(ValueError, lambda e: "Shape must" in str(e)): @@ -201,6 +202,7 @@ class MatMulInfixOperatorTest(test_lib.TestCase): ops.convert_to_tensor([10.0, 20.0, 30.0]), ops.convert_to_tensor([[40.0, 50.0], [60.0, 70.0]])) + @test_util.run_deprecated_v1 def testMismatchedDimensions(self): with self.assertRaisesWithPredicateMatch( ValueError, lambda e: "Dimensions must" in str(e)): @@ -208,19 +210,21 @@ class MatMulInfixOperatorTest(test_lib.TestCase): ops.convert_to_tensor([[10.0, 20.0, 30.0]]), ops.convert_to_tensor([[40.0, 50.0], [60.0, 70.0]])) + @test_util.run_deprecated_v1 def testInfixMatmulIsTfMatmul(self): a = ops.convert_to_tensor([[10.0, 20.0, 30.0]]) b = ops.convert_to_tensor([[40.0, 50.0], [60.0, 70.0], [80.0, 90.0]]) c = infix_matmul(a, b) self.assertEqual(c.op.type, "MatMul") + @test_util.run_deprecated_v1 def testInfixMatmulDoesDotProduct(self): a = ops.convert_to_tensor([[10.0, 20.0, 30.0]]) b = ops.convert_to_tensor([[40.0, 50.0], [60.0, 70.0], [80.0, 90.0]]) c = infix_matmul(a, b) d = math_ops.matmul(a, b) with self.cached_session(): - self.assertAllEqual(c.eval(), d.eval()) + self.assertAllEqual(c.eval(), self.evaluate(d)) if __name__ == "__main__": diff --git a/tensorflow/python/kernel_tests/matrix_band_part_op_test.py b/tensorflow/python/kernel_tests/matrix_band_part_op_test.py index 93a668f125..129ea40dfe 100644 --- a/tensorflow/python/kernel_tests/matrix_band_part_op_test.py +++ b/tensorflow/python/kernel_tests/matrix_band_part_op_test.py @@ -62,7 +62,7 @@ def _GetMatrixBandPartTest(dtype_, batch_shape_, shape_): batch_mat, constant_op.constant(lower, index_dtype), constant_op.constant(upper, index_dtype)) - self.assertAllEqual(band_np, band.eval()) + self.assertAllEqual(band_np, self.evaluate(band)) return Test diff --git a/tensorflow/python/kernel_tests/matrix_exponential_op_test.py b/tensorflow/python/kernel_tests/matrix_exponential_op_test.py index 3abdf50ece..372b6dc17f 100644 --- a/tensorflow/python/kernel_tests/matrix_exponential_op_test.py +++ b/tensorflow/python/kernel_tests/matrix_exponential_op_test.py @@ -25,6 +25,7 @@ import numpy as np from tensorflow.python.client import session from tensorflow.python.framework import constant_op from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import random_ops @@ -50,7 +51,7 @@ class ExponentialOpTest(test.TestCase): def _verifyExponential(self, x, np_type): inp = x.astype(np_type) - with self.cached_session(use_gpu=True): + with test_util.use_gpu(): tf_ans = linalg_impl.matrix_exponential(inp) if x.size == 0: np_ans = np.empty(x.shape, dtype=np_type) @@ -61,7 +62,7 @@ class ExponentialOpTest(test.TestCase): np_ans[i] = np_expm(inp[i]) else: np_ans = np_expm(inp) - out = tf_ans.eval() + out = self.evaluate(tf_ans) self.assertAllClose(np_ans, out, rtol=1e-4, atol=1e-3) def _verifyExponentialReal(self, x): @@ -121,12 +122,14 @@ class ExponentialOpTest(test.TestCase): # Complex batch self._verifyExponentialComplex(self._makeBatch(matrix1, matrix2)) + @test_util.run_deprecated_v1 def testNonSquareMatrix(self): # When the exponential of a non-square matrix is attempted we should return # an error with self.assertRaises(ValueError): linalg_impl.matrix_exponential(np.array([[1., 2., 3.], [3., 4., 5.]])) + @test_util.run_deprecated_v1 def testWrongDimensions(self): # The input to the exponential should be at least a 2-dimensional tensor. tensor3 = constant_op.constant([1., 2.]) @@ -137,6 +140,7 @@ class ExponentialOpTest(test.TestCase): self._verifyExponentialReal(np.empty([0, 2, 2])) self._verifyExponentialReal(np.empty([2, 0, 0])) + @test_util.run_deprecated_v1 def testDynamic(self): with self.session(use_gpu=True) as sess: inp = array_ops.placeholder(ops.dtypes.float32) @@ -144,13 +148,14 @@ class ExponentialOpTest(test.TestCase): matrix = np.array([[1., 2.], [3., 4.]]) sess.run(expm, feed_dict={inp: matrix}) + @test_util.run_deprecated_v1 def testConcurrentExecutesWithoutError(self): with self.session(use_gpu=True) as sess: matrix1 = random_ops.random_normal([5, 5], seed=42) matrix2 = random_ops.random_normal([5, 5], seed=42) expm1 = linalg_impl.matrix_exponential(matrix1) expm2 = linalg_impl.matrix_exponential(matrix2) - expm = sess.run([expm1, expm2]) + expm = self.evaluate([expm1, expm2]) self.assertAllEqual(expm[0], expm[1]) diff --git a/tensorflow/python/kernel_tests/matrix_inverse_op_test.py b/tensorflow/python/kernel_tests/matrix_inverse_op_test.py index 2247f1541e..5cef4b79a3 100644 --- a/tensorflow/python/kernel_tests/matrix_inverse_op_test.py +++ b/tensorflow/python/kernel_tests/matrix_inverse_op_test.py @@ -46,7 +46,7 @@ class InverseOpTest(test.TestCase): tiling = list(y.shape) tiling[-2:] = [1, 1] np_ans = np.tile(np_ans, tiling) - out = tf_ans.eval() + out = self.evaluate(tf_ans) self.assertAllClose(np_ans, out, rtol=1e-4, atol=1e-3) self.assertShapeEqual(y, tf_ans) @@ -146,7 +146,7 @@ class InverseOpTest(test.TestCase): inv1 = linalg_ops.matrix_inverse(matrix1, adjoint=adjoint_) inv2 = linalg_ops.matrix_inverse(matrix2, adjoint=adjoint_) all_ops += [inv1, inv2] - inv = sess.run(all_ops) + inv = self.evaluate(all_ops) self.assertAllEqual(inv[0], inv[1]) self.assertAllEqual(inv[2], inv[3]) diff --git a/tensorflow/python/kernel_tests/matrix_logarithm_op_test.py b/tensorflow/python/kernel_tests/matrix_logarithm_op_test.py index 2010a4b2a8..b0bce6a1b9 100644 --- a/tensorflow/python/kernel_tests/matrix_logarithm_op_test.py +++ b/tensorflow/python/kernel_tests/matrix_logarithm_op_test.py @@ -25,6 +25,7 @@ from tensorflow.python.client import session from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import gen_linalg_ops from tensorflow.python.ops import math_ops @@ -39,11 +40,11 @@ class LogarithmOpTest(test.TestCase): def _verifyLogarithm(self, x, np_type): inp = x.astype(np_type) - with self.cached_session(use_gpu=True): + with test_util.use_gpu(): # Verify that expm(logm(A)) == A. tf_ans = linalg_impl.matrix_exponential( gen_linalg_ops.matrix_logarithm(inp)) - out = tf_ans.eval() + out = self.evaluate(tf_ans) self.assertAllClose(inp, out, rtol=1e-4, atol=1e-3) def _verifyLogarithmComplex(self, x): @@ -128,7 +129,7 @@ class LogarithmOpTest(test.TestCase): random_ops.random_normal([5, 5], seed=42), dtypes.complex64) logm1 = gen_linalg_ops.matrix_logarithm(matrix1) logm2 = gen_linalg_ops.matrix_logarithm(matrix2) - logm = sess.run([logm1, logm2]) + logm = self.evaluate([logm1, logm2]) self.assertAllEqual(logm[0], logm[1]) diff --git a/tensorflow/python/kernel_tests/matrix_solve_ls_op_test.py b/tensorflow/python/kernel_tests/matrix_solve_ls_op_test.py index 13a7df7f95..a6f5da9d3d 100644 --- a/tensorflow/python/kernel_tests/matrix_solve_ls_op_test.py +++ b/tensorflow/python/kernel_tests/matrix_solve_ls_op_test.py @@ -24,6 +24,7 @@ from tensorflow.python.client import session from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import linalg_ops @@ -133,6 +134,7 @@ class MatrixSolveLsOpTest(test_lib.TestCase): self.assertEqual(np_ans.shape, tf_ans_val.shape) self.assertAllClose(np_ans, tf_ans_val, atol=2 * tol, rtol=2 * tol) + @test_util.run_deprecated_v1 def testWrongDimensions(self): # The matrix and right-hand sides should have the same number of rows. with self.session(use_gpu=True): @@ -141,6 +143,7 @@ class MatrixSolveLsOpTest(test_lib.TestCase): with self.assertRaises(ValueError): linalg_ops.matrix_solve_ls(matrix, rhs) + @test_util.run_deprecated_v1 def testEmpty(self): full = np.array([[1., 2.], [3., 4.], [5., 6.]]) empty0 = np.empty([3, 0]) @@ -156,6 +159,7 @@ class MatrixSolveLsOpTest(test_lib.TestCase): tf_ans = linalg_ops.matrix_solve_ls(empty1, empty1, fast=fast).eval() self.assertEqual(tf_ans.shape, (2, 2)) + @test_util.run_deprecated_v1 def testBatchResultSize(self): # 3x3x3 matrices, 3x3x1 right-hand sides. matrix = np.array([1., 2., 3., 4., 5., 6., 7., 8., 9.] * 3).reshape(3, 3, 3) diff --git a/tensorflow/python/kernel_tests/matrix_solve_op_test.py b/tensorflow/python/kernel_tests/matrix_solve_op_test.py index 9e30ae1628..db7c4802f6 100644 --- a/tensorflow/python/kernel_tests/matrix_solve_op_test.py +++ b/tensorflow/python/kernel_tests/matrix_solve_op_test.py @@ -24,6 +24,7 @@ from tensorflow.python.client import session from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import linalg_ops @@ -63,7 +64,7 @@ class MatrixSolveOpTest(test.TestCase): out = sess.run(tf_ans, {a_ph: a, b_ph: b}) else: tf_ans = linalg_ops.matrix_solve(a, b, adjoint=adjoint) - out = tf_ans.eval() + out = self.evaluate(tf_ans) self.assertEqual(tf_ans.get_shape(), out.shape) self.assertEqual(np_ans.shape, out.shape) self.assertAllClose(np_ans, out, atol=tol, rtol=tol) @@ -75,6 +76,7 @@ class MatrixSolveOpTest(test.TestCase): [m, n])) return matrix + @test_util.run_deprecated_v1 def testSolve(self): for n in 1, 2, 4, 9: matrix = self._generateMatrix(n, n) @@ -82,6 +84,7 @@ class MatrixSolveOpTest(test.TestCase): rhs = self._generateMatrix(n, nrhs) self._verifySolve(matrix, rhs) + @test_util.run_deprecated_v1 def testSolveBatch(self): for n in 2, 5: matrix = self._generateMatrix(n, n) @@ -90,6 +93,7 @@ class MatrixSolveOpTest(test.TestCase): for batch_dims in [[2], [2, 2], [7, 4]]: self._verifySolve(matrix, rhs, batch_dims=batch_dims) + @test_util.run_deprecated_v1 def testNonSquareMatrix(self): # When the solve of a non-square matrix is attempted we should return # an error @@ -98,6 +102,7 @@ class MatrixSolveOpTest(test.TestCase): matrix = constant_op.constant([[1., 2., 3.], [3., 4., 5.]]) linalg_ops.matrix_solve(matrix, matrix) + @test_util.run_deprecated_v1 def testWrongDimensions(self): # The matrix and right-hand sides should have the same number of rows. with self.session(use_gpu=True): @@ -115,6 +120,7 @@ class MatrixSolveOpTest(test.TestCase): [0., -1., 1.]]) linalg_ops.matrix_solve(matrix, matrix).eval() + @test_util.run_deprecated_v1 def testConcurrent(self): with self.session(use_gpu=True) as sess: all_ops = [] @@ -126,7 +132,7 @@ class MatrixSolveOpTest(test.TestCase): s1 = linalg_ops.matrix_solve(lhs1, rhs1, adjoint=adjoint_) s2 = linalg_ops.matrix_solve(lhs2, rhs2, adjoint=adjoint_) all_ops += [s1, s2] - val = sess.run(all_ops) + val = self.evaluate(all_ops) self.assertAllEqual(val[0], val[1]) self.assertAllEqual(val[2], val[3]) diff --git a/tensorflow/python/kernel_tests/matrix_square_root_op_test.py b/tensorflow/python/kernel_tests/matrix_square_root_op_test.py index 9212580313..1e2109b8c4 100644 --- a/tensorflow/python/kernel_tests/matrix_square_root_op_test.py +++ b/tensorflow/python/kernel_tests/matrix_square_root_op_test.py @@ -21,6 +21,7 @@ from __future__ import print_function import numpy as np from tensorflow.python.framework import constant_op +from tensorflow.python.framework import test_util from tensorflow.python.ops import gen_linalg_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import random_ops @@ -31,7 +32,7 @@ class SquareRootOpTest(test.TestCase): def _verifySquareRoot(self, matrix, np_type): matrix = matrix.astype(np_type) - with self.test_session(use_gpu=True): + with test_util.use_gpu(): # Verify that matmul(sqrtm(A), sqrtm(A)) = A sqrt = gen_linalg_ops.matrix_square_root(matrix) square = math_ops.matmul(sqrt, sqrt) @@ -96,19 +97,20 @@ class SquareRootOpTest(test.TestCase): gen_linalg_ops.matrix_square_root(tensor) def testNotSquare(self): - with self.test_session(): - with self.assertRaises(ValueError): - tensor = constant_op.constant([[1., 0., -1.], [-1., 1., 0.]]) - gen_linalg_ops.matrix_square_root(tensor).eval() + with self.assertRaises(ValueError): + tensor = constant_op.constant([[1., 0., -1.], [-1., 1., 0.]]) + self.evaluate(gen_linalg_ops.matrix_square_root(tensor)) def testConcurrentExecutesWithoutError(self): - with self.test_session(use_gpu=True) as sess: + with test_util.use_gpu(): matrix1 = random_ops.random_normal([5, 5], seed=42) matrix2 = random_ops.random_normal([5, 5], seed=42) - sqrt1 = gen_linalg_ops.matrix_square_root(matrix1) - sqrt2 = gen_linalg_ops.matrix_square_root(matrix2) + square1 = math_ops.matmul(matrix1, matrix1) + square2 = math_ops.matmul(matrix2, matrix2) + sqrt1 = gen_linalg_ops.matrix_square_root(square1) + sqrt2 = gen_linalg_ops.matrix_square_root(square2) all_ops = [sqrt1, sqrt2] - sqrt = sess.run(all_ops) + sqrt = self.evaluate(all_ops) self.assertAllEqual(sqrt[0], sqrt[1]) diff --git a/tensorflow/python/kernel_tests/matrix_triangular_solve_op_test.py b/tensorflow/python/kernel_tests/matrix_triangular_solve_op_test.py index 445faca3ee..dde83f12f3 100644 --- a/tensorflow/python/kernel_tests/matrix_triangular_solve_op_test.py +++ b/tensorflow/python/kernel_tests/matrix_triangular_solve_op_test.py @@ -21,6 +21,7 @@ from __future__ import print_function import numpy as np from tensorflow.python.framework import constant_op +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import linalg_ops from tensorflow.python.platform import test @@ -87,12 +88,13 @@ class MatrixTriangularSolveOpTest(test.TestCase): b_tf = constant_op.constant(b) tf_ans = linalg_ops.matrix_triangular_solve( a_tf, b_tf, lower=lower, adjoint=adjoint) - tf_val = tf_ans.eval() + tf_val = self.evaluate(tf_ans) np_ans = np.linalg.solve(a_np, b) self.assertEqual(np_ans.shape, tf_ans.get_shape()) self.assertEqual(np_ans.shape, tf_val.shape) self.assertAllClose(np_ans, tf_val) + @test_util.run_deprecated_v1 def testSolve(self): # 1x1 matrix, single rhs. matrix = np.array([[0.1]]) @@ -106,6 +108,7 @@ class MatrixTriangularSolveOpTest(test.TestCase): rhs1 = np.array([[1., 0., 1.], [0., 1., 1.]]) self._verifySolveAllWaysReal(matrix, rhs1) + @test_util.run_deprecated_v1 def testSolveComplex(self): # 1x1 matrix, single rhs. matrix = np.array([[0.1 + 1j * 0.1]]) @@ -122,6 +125,7 @@ class MatrixTriangularSolveOpTest(test.TestCase): rhs1 += 1j * rhs1 self._verifySolveAllWaysComplex(matrix, rhs1) + @test_util.run_deprecated_v1 def testSolveBatch(self): matrix = np.array([[1., 2.], [3., 4.]]) rhs = np.array([[1., 0., 1.], [0., 1., 1.]]) @@ -130,6 +134,7 @@ class MatrixTriangularSolveOpTest(test.TestCase): # Batch of 3x2x2x2 matrices, 3x2x2x3 right-hand sides. self._verifySolveAllWaysReal(matrix, rhs, batch_dims=[3, 2]) + @test_util.run_deprecated_v1 def testSolveBatchComplex(self): matrix = np.array([[1., 2.], [3., 4.]]).astype(np.complex64) matrix += 1j * matrix @@ -140,6 +145,7 @@ class MatrixTriangularSolveOpTest(test.TestCase): # Batch of 3x2x2x2 matrices, 3x2x2x3 right-hand sides. self._verifySolveAllWaysComplex(matrix, rhs, batch_dims=[3, 2]) + @test_util.run_deprecated_v1 def testNonSquareMatrix(self): # A non-square matrix should cause an error. matrix = np.array([[1., 2., 3.], [3., 4., 5.]]) @@ -149,6 +155,7 @@ class MatrixTriangularSolveOpTest(test.TestCase): with self.assertRaises(ValueError): self._verifySolve(matrix, matrix, batch_dims=[2, 3]) + @test_util.run_deprecated_v1 def testWrongDimensions(self): # The matrix should have the same number of rows as the # right-hand sides. diff --git a/tensorflow/python/kernel_tests/metrics_test.py b/tensorflow/python/kernel_tests/metrics_test.py index 5dcdb9e420..64dd591455 100644 --- a/tensorflow/python/kernel_tests/metrics_test.py +++ b/tensorflow/python/kernel_tests/metrics_test.py @@ -29,6 +29,7 @@ from tensorflow.python.framework import dtypes as dtypes_lib from tensorflow.python.framework import errors_impl from tensorflow.python.framework import ops from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import data_flow_ops from tensorflow.python.ops import math_ops @@ -175,22 +176,26 @@ class MeanTest(test.TestCase): def setUp(self): ops.reset_default_graph() + @test_util.run_deprecated_v1 def testVars(self): metrics.mean(array_ops.ones([4, 3])) _assert_metric_variables(self, ('mean/count:0', 'mean/total:0')) + @test_util.run_deprecated_v1 def testMetricsCollection(self): my_collection_name = '__metrics__' mean, _ = metrics.mean( array_ops.ones([4, 3]), metrics_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [mean]) + @test_util.run_deprecated_v1 def testUpdatesCollection(self): my_collection_name = '__updates__' _, update_op = metrics.mean( array_ops.ones([4, 3]), updates_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [update_op]) + @test_util.run_deprecated_v1 def testBasic(self): with self.cached_session() as sess: values_queue = data_flow_ops.FIFOQueue( @@ -203,11 +208,12 @@ class MeanTest(test.TestCase): mean, update_op = metrics.mean(values) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) for _ in range(4): - sess.run(update_op) - self.assertAlmostEqual(1.65, sess.run(mean), 5) + self.evaluate(update_op) + self.assertAlmostEqual(1.65, self.evaluate(mean), 5) + @test_util.run_deprecated_v1 def testUpdateOpsReturnsCurrentValue(self): with self.cached_session() as sess: values_queue = data_flow_ops.FIFOQueue( @@ -220,15 +226,16 @@ class MeanTest(test.TestCase): mean, update_op = metrics.mean(values) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) - self.assertAlmostEqual(0.5, sess.run(update_op), 5) - self.assertAlmostEqual(1.475, sess.run(update_op), 5) - self.assertAlmostEqual(12.4 / 6.0, sess.run(update_op), 5) - self.assertAlmostEqual(1.65, sess.run(update_op), 5) + self.assertAlmostEqual(0.5, self.evaluate(update_op), 5) + self.assertAlmostEqual(1.475, self.evaluate(update_op), 5) + self.assertAlmostEqual(12.4 / 6.0, self.evaluate(update_op), 5) + self.assertAlmostEqual(1.65, self.evaluate(update_op), 5) - self.assertAlmostEqual(1.65, sess.run(mean), 5) + self.assertAlmostEqual(1.65, self.evaluate(mean), 5) + @test_util.run_deprecated_v1 def testUnweighted(self): values = _test_values((3, 2, 4, 1)) mean_results = ( @@ -271,37 +278,44 @@ class MeanTest(test.TestCase): self.assertAlmostEqual(expected, update_op.eval(), places=5) self.assertAlmostEqual(expected, mean.eval(), places=5) + @test_util.run_deprecated_v1 def test1x1x1Weighted(self): self._test_3d_weighted( _test_values((3, 2, 4)), weights=np.asarray((5,)).reshape((1, 1, 1))) + @test_util.run_deprecated_v1 def test1x1xNWeighted(self): self._test_3d_weighted( _test_values((3, 2, 4)), weights=np.asarray((5, 7, 11, 3)).reshape((1, 1, 4))) + @test_util.run_deprecated_v1 def test1xNx1Weighted(self): self._test_3d_weighted( _test_values((3, 2, 4)), weights=np.asarray((5, 11)).reshape((1, 2, 1))) + @test_util.run_deprecated_v1 def test1xNxNWeighted(self): self._test_3d_weighted( _test_values((3, 2, 4)), weights=np.asarray((5, 7, 11, 3, 2, 13, 7, 5)).reshape((1, 2, 4))) + @test_util.run_deprecated_v1 def testNx1x1Weighted(self): self._test_3d_weighted( _test_values((3, 2, 4)), weights=np.asarray((5, 7, 11)).reshape((3, 1, 1))) + @test_util.run_deprecated_v1 def testNx1xNWeighted(self): self._test_3d_weighted( _test_values((3, 2, 4)), weights=np.asarray(( 5, 7, 11, 3, 2, 12, 7, 5, 2, 17, 11, 3)).reshape((3, 1, 4))) + @test_util.run_deprecated_v1 def testNxNxNWeighted(self): self._test_3d_weighted( _test_values((3, 2, 4)), @@ -309,6 +323,7 @@ class MeanTest(test.TestCase): 5, 7, 11, 3, 2, 12, 7, 5, 2, 17, 11, 3, 2, 17, 11, 3, 5, 7, 11, 3, 2, 12, 7, 5)).reshape((3, 2, 4))) + @test_util.run_deprecated_v1 def testInvalidWeights(self): values_placeholder = array_ops.placeholder(dtype=dtypes_lib.float32) values = _test_values((3, 2, 4, 1)) @@ -341,23 +356,27 @@ class MeanTensorTest(test.TestCase): def setUp(self): ops.reset_default_graph() + @test_util.run_deprecated_v1 def testVars(self): metrics.mean_tensor(array_ops.ones([4, 3])) _assert_metric_variables(self, ('mean/total_tensor:0', 'mean/count_tensor:0')) + @test_util.run_deprecated_v1 def testMetricsCollection(self): my_collection_name = '__metrics__' mean, _ = metrics.mean_tensor( array_ops.ones([4, 3]), metrics_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [mean]) + @test_util.run_deprecated_v1 def testUpdatesCollection(self): my_collection_name = '__updates__' _, update_op = metrics.mean_tensor( array_ops.ones([4, 3]), updates_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [update_op]) + @test_util.run_deprecated_v1 def testBasic(self): with self.cached_session() as sess: values_queue = data_flow_ops.FIFOQueue( @@ -370,11 +389,12 @@ class MeanTensorTest(test.TestCase): mean, update_op = metrics.mean_tensor(values) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) for _ in range(4): - sess.run(update_op) - self.assertAllClose([[-0.9 / 4., 3.525]], sess.run(mean)) + self.evaluate(update_op) + self.assertAllClose([[-0.9 / 4., 3.525]], self.evaluate(mean)) + @test_util.run_deprecated_v1 def testMultiDimensional(self): with self.cached_session() as sess: values_queue = data_flow_ops.FIFOQueue( @@ -391,11 +411,13 @@ class MeanTensorTest(test.TestCase): mean, update_op = metrics.mean_tensor(values) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) for _ in range(2): - sess.run(update_op) - self.assertAllClose([[[1, 2], [1, 2]], [[2, 3], [5, 6]]], sess.run(mean)) + self.evaluate(update_op) + self.assertAllClose([[[1, 2], [1, 2]], [[2, 3], [5, 6]]], + self.evaluate(mean)) + @test_util.run_deprecated_v1 def testUpdateOpsReturnsCurrentValue(self): with self.cached_session() as sess: values_queue = data_flow_ops.FIFOQueue( @@ -408,15 +430,16 @@ class MeanTensorTest(test.TestCase): mean, update_op = metrics.mean_tensor(values) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) - self.assertAllClose([[0, 1]], sess.run(update_op), 5) - self.assertAllClose([[-2.1, 5.05]], sess.run(update_op), 5) - self.assertAllClose([[2.3 / 3., 10.1 / 3.]], sess.run(update_op), 5) - self.assertAllClose([[-0.9 / 4., 3.525]], sess.run(update_op), 5) + self.assertAllClose([[0, 1]], self.evaluate(update_op), 5) + self.assertAllClose([[-2.1, 5.05]], self.evaluate(update_op), 5) + self.assertAllClose([[2.3 / 3., 10.1 / 3.]], self.evaluate(update_op), 5) + self.assertAllClose([[-0.9 / 4., 3.525]], self.evaluate(update_op), 5) - self.assertAllClose([[-0.9 / 4., 3.525]], sess.run(mean), 5) + self.assertAllClose([[-0.9 / 4., 3.525]], self.evaluate(mean), 5) + @test_util.run_deprecated_v1 def testBinaryWeighted1d(self): with self.cached_session() as sess: # Create the queue that populates the values. @@ -439,11 +462,12 @@ class MeanTensorTest(test.TestCase): mean, update_op = metrics.mean_tensor(values, weights) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) for _ in range(4): - sess.run(update_op) - self.assertAllClose([[3.25, 0.5]], sess.run(mean), 5) + self.evaluate(update_op) + self.assertAllClose([[3.25, 0.5]], self.evaluate(mean), 5) + @test_util.run_deprecated_v1 def testWeighted1d(self): with self.cached_session() as sess: # Create the queue that populates the values. @@ -466,11 +490,12 @@ class MeanTensorTest(test.TestCase): mean, update_op = metrics.mean_tensor(values, weights) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) for _ in range(4): - sess.run(update_op) - self.assertAllClose([[0.8, 3.52]], sess.run(mean), 5) + self.evaluate(update_op) + self.assertAllClose([[0.8, 3.52]], self.evaluate(mean), 5) + @test_util.run_deprecated_v1 def testWeighted2d_1(self): with self.cached_session() as sess: # Create the queue that populates the values. @@ -493,11 +518,12 @@ class MeanTensorTest(test.TestCase): mean, update_op = metrics.mean_tensor(values, weights) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) for _ in range(4): - sess.run(update_op) - self.assertAllClose([[-2.1, 0.5]], sess.run(mean), 5) + self.evaluate(update_op) + self.assertAllClose([[-2.1, 0.5]], self.evaluate(mean), 5) + @test_util.run_deprecated_v1 def testWeighted2d_2(self): with self.cached_session() as sess: # Create the queue that populates the values. @@ -520,10 +546,10 @@ class MeanTensorTest(test.TestCase): mean, update_op = metrics.mean_tensor(values, weights) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) for _ in range(4): - sess.run(update_op) - self.assertAllClose([[0, 0.5]], sess.run(mean), 5) + self.evaluate(update_op) + self.assertAllClose([[0, 0.5]], self.evaluate(mean), 5) class AccuracyTest(test.TestCase): @@ -531,6 +557,7 @@ class AccuracyTest(test.TestCase): def setUp(self): ops.reset_default_graph() + @test_util.run_deprecated_v1 def testVars(self): metrics.accuracy( predictions=array_ops.ones((10, 1)), @@ -539,6 +566,7 @@ class AccuracyTest(test.TestCase): _assert_metric_variables(self, ('my_accuracy/count:0', 'my_accuracy/total:0')) + @test_util.run_deprecated_v1 def testMetricsCollection(self): my_collection_name = '__metrics__' mean, _ = metrics.accuracy( @@ -547,6 +575,7 @@ class AccuracyTest(test.TestCase): metrics_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [mean]) + @test_util.run_deprecated_v1 def testUpdatesCollection(self): my_collection_name = '__updates__' _, update_op = metrics.accuracy( @@ -555,12 +584,14 @@ class AccuracyTest(test.TestCase): updates_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [update_op]) + @test_util.run_deprecated_v1 def testPredictionsAndLabelsOfDifferentSizeRaisesValueError(self): predictions = array_ops.ones((10, 3)) labels = array_ops.ones((10, 4)) with self.assertRaises(ValueError): metrics.accuracy(labels, predictions) + @test_util.run_deprecated_v1 def testPredictionsAndWeightsOfDifferentSizeRaisesValueError(self): predictions = array_ops.ones((10, 3)) labels = array_ops.ones((10, 3)) @@ -568,6 +599,7 @@ class AccuracyTest(test.TestCase): with self.assertRaises(ValueError): metrics.accuracy(labels, predictions, weights) + @test_util.run_deprecated_v1 def testValueTensorIsIdempotent(self): predictions = random_ops.random_uniform( (10, 3), maxval=3, dtype=dtypes_lib.int64, seed=1) @@ -576,17 +608,18 @@ class AccuracyTest(test.TestCase): accuracy, update_op = metrics.accuracy(labels, predictions) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) # Run several updates. for _ in range(10): - sess.run(update_op) + self.evaluate(update_op) # Then verify idempotency. initial_accuracy = accuracy.eval() for _ in range(10): self.assertEqual(initial_accuracy, accuracy.eval()) + @test_util.run_deprecated_v1 def testMultipleUpdates(self): with self.cached_session() as sess: # Create the queue that populates the predictions. @@ -609,32 +642,35 @@ class AccuracyTest(test.TestCase): accuracy, update_op = metrics.accuracy(labels, predictions) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) for _ in xrange(3): - sess.run(update_op) - self.assertEqual(0.5, sess.run(update_op)) + self.evaluate(update_op) + self.assertEqual(0.5, self.evaluate(update_op)) self.assertEqual(0.5, accuracy.eval()) + @test_util.run_deprecated_v1 def testEffectivelyEquivalentSizes(self): predictions = array_ops.ones((40, 1)) labels = array_ops.ones((40,)) with self.cached_session() as sess: accuracy, update_op = metrics.accuracy(labels, predictions) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertEqual(1.0, update_op.eval()) self.assertEqual(1.0, accuracy.eval()) + @test_util.run_deprecated_v1 def testEffectivelyEquivalentSizesWithScalarWeight(self): predictions = array_ops.ones((40, 1)) labels = array_ops.ones((40,)) with self.cached_session() as sess: accuracy, update_op = metrics.accuracy(labels, predictions, weights=2.0) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertEqual(1.0, update_op.eval()) self.assertEqual(1.0, accuracy.eval()) + @test_util.run_deprecated_v1 def testEffectivelyEquivalentSizesWithStaticShapedWeight(self): predictions = ops.convert_to_tensor([1, 1, 1]) # shape 3, labels = array_ops.expand_dims(ops.convert_to_tensor([1, 0, 0]), @@ -645,13 +681,14 @@ class AccuracyTest(test.TestCase): with self.cached_session() as sess: accuracy, update_op = metrics.accuracy(labels, predictions, weights) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) # if streaming_accuracy does not flatten the weight, accuracy would be # 0.33333334 due to an intended broadcast of weight. Due to flattening, # it will be higher than .95 self.assertGreater(update_op.eval(), .95) self.assertGreater(accuracy.eval(), .95) + @test_util.run_deprecated_v1 def testEffectivelyEquivalentSizesWithDynamicallyShapedWeight(self): predictions = ops.convert_to_tensor([1, 1, 1]) # shape 3, labels = array_ops.expand_dims(ops.convert_to_tensor([1, 0, 0]), @@ -666,13 +703,14 @@ class AccuracyTest(test.TestCase): accuracy, update_op = metrics.accuracy(labels, predictions, weights_placeholder) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) # if streaming_accuracy does not flatten the weight, accuracy would be # 0.33333334 due to an intended broadcast of weight. Due to flattening, # it will be higher than .95 self.assertGreater(update_op.eval(feed_dict=feed_dict), .95) self.assertGreater(accuracy.eval(feed_dict=feed_dict), .95) + @test_util.run_deprecated_v1 def testMultipleUpdatesWithWeightedValues(self): with self.cached_session() as sess: # Create the queue that populates the predictions. @@ -704,10 +742,10 @@ class AccuracyTest(test.TestCase): accuracy, update_op = metrics.accuracy(labels, predictions, weights) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) for _ in xrange(3): - sess.run(update_op) - self.assertEqual(1.0, sess.run(update_op)) + self.evaluate(update_op) + self.assertEqual(1.0, self.evaluate(update_op)) self.assertEqual(1.0, accuracy.eval()) @@ -717,12 +755,14 @@ class PrecisionTest(test.TestCase): np.random.seed(1) ops.reset_default_graph() + @test_util.run_deprecated_v1 def testVars(self): metrics.precision( predictions=array_ops.ones((10, 1)), labels=array_ops.ones((10, 1))) _assert_metric_variables(self, ('precision/false_positives/count:0', 'precision/true_positives/count:0')) + @test_util.run_deprecated_v1 def testMetricsCollection(self): my_collection_name = '__metrics__' mean, _ = metrics.precision( @@ -731,6 +771,7 @@ class PrecisionTest(test.TestCase): metrics_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [mean]) + @test_util.run_deprecated_v1 def testUpdatesCollection(self): my_collection_name = '__updates__' _, update_op = metrics.precision( @@ -739,6 +780,7 @@ class PrecisionTest(test.TestCase): updates_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [update_op]) + @test_util.run_deprecated_v1 def testValueTensorIsIdempotent(self): predictions = random_ops.random_uniform( (10, 3), maxval=1, dtype=dtypes_lib.int64, seed=1) @@ -747,17 +789,18 @@ class PrecisionTest(test.TestCase): precision, update_op = metrics.precision(labels, predictions) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) # Run several updates. for _ in range(10): - sess.run(update_op) + self.evaluate(update_op) # Then verify idempotency. initial_precision = precision.eval() for _ in range(10): self.assertEqual(initial_precision, precision.eval()) + @test_util.run_deprecated_v1 def testAllCorrect(self): inputs = np.random.randint(0, 2, size=(100, 1)) @@ -766,10 +809,11 @@ class PrecisionTest(test.TestCase): precision, update_op = metrics.precision(labels, predictions) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) - self.assertAlmostEqual(1, sess.run(update_op)) + self.evaluate(variables.local_variables_initializer()) + self.assertAlmostEqual(1, self.evaluate(update_op)) self.assertAlmostEqual(1, precision.eval()) + @test_util.run_deprecated_v1 def testSomeCorrect_multipleInputDtypes(self): for dtype in (dtypes_lib.bool, dtypes_lib.int32, dtypes_lib.float32): predictions = math_ops.cast( @@ -779,10 +823,11 @@ class PrecisionTest(test.TestCase): precision, update_op = metrics.precision(labels, predictions) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertAlmostEqual(0.5, update_op.eval()) self.assertAlmostEqual(0.5, precision.eval()) + @test_util.run_deprecated_v1 def testWeighted1d(self): predictions = constant_op.constant([[1, 0, 1, 0], [1, 0, 1, 0]]) labels = constant_op.constant([[0, 1, 1, 0], [1, 0, 0, 1]]) @@ -797,6 +842,7 @@ class PrecisionTest(test.TestCase): self.assertAlmostEqual(expected_precision, update_op.eval()) self.assertAlmostEqual(expected_precision, precision.eval()) + @test_util.run_deprecated_v1 def testWeightedScalar_placeholders(self): predictions = array_ops.placeholder(dtype=dtypes_lib.float32) labels = array_ops.placeholder(dtype=dtypes_lib.float32) @@ -816,6 +862,7 @@ class PrecisionTest(test.TestCase): self.assertAlmostEqual( expected_precision, precision.eval(feed_dict=feed_dict)) + @test_util.run_deprecated_v1 def testWeighted1d_placeholders(self): predictions = array_ops.placeholder(dtype=dtypes_lib.float32) labels = array_ops.placeholder(dtype=dtypes_lib.float32) @@ -836,6 +883,7 @@ class PrecisionTest(test.TestCase): self.assertAlmostEqual( expected_precision, precision.eval(feed_dict=feed_dict)) + @test_util.run_deprecated_v1 def testWeighted2d(self): predictions = constant_op.constant([[1, 0, 1, 0], [1, 0, 1, 0]]) labels = constant_op.constant([[0, 1, 1, 0], [1, 0, 0, 1]]) @@ -852,6 +900,7 @@ class PrecisionTest(test.TestCase): self.assertAlmostEqual(expected_precision, update_op.eval()) self.assertAlmostEqual(expected_precision, precision.eval()) + @test_util.run_deprecated_v1 def testWeighted2d_placeholders(self): predictions = array_ops.placeholder(dtype=dtypes_lib.float32) labels = array_ops.placeholder(dtype=dtypes_lib.float32) @@ -874,6 +923,7 @@ class PrecisionTest(test.TestCase): self.assertAlmostEqual( expected_precision, precision.eval(feed_dict=feed_dict)) + @test_util.run_deprecated_v1 def testAllIncorrect(self): inputs = np.random.randint(0, 2, size=(100, 1)) @@ -882,18 +932,19 @@ class PrecisionTest(test.TestCase): precision, update_op = metrics.precision(labels, predictions) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) - sess.run(update_op) + self.evaluate(variables.local_variables_initializer()) + self.evaluate(update_op) self.assertAlmostEqual(0, precision.eval()) + @test_util.run_deprecated_v1 def testZeroTrueAndFalsePositivesGivesZeroPrecision(self): predictions = constant_op.constant([0, 0, 0, 0]) labels = constant_op.constant([0, 0, 0, 0]) precision, update_op = metrics.precision(labels, predictions) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) - sess.run(update_op) + self.evaluate(variables.local_variables_initializer()) + self.evaluate(update_op) self.assertEqual(0.0, precision.eval()) @@ -903,6 +954,7 @@ class RecallTest(test.TestCase): np.random.seed(1) ops.reset_default_graph() + @test_util.run_deprecated_v1 def testVars(self): metrics.recall( predictions=array_ops.ones((10, 1)), labels=array_ops.ones((10, 1))) @@ -910,6 +962,7 @@ class RecallTest(test.TestCase): self, ('recall/false_negatives/count:0', 'recall/true_positives/count:0')) + @test_util.run_deprecated_v1 def testMetricsCollection(self): my_collection_name = '__metrics__' mean, _ = metrics.recall( @@ -918,6 +971,7 @@ class RecallTest(test.TestCase): metrics_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [mean]) + @test_util.run_deprecated_v1 def testUpdatesCollection(self): my_collection_name = '__updates__' _, update_op = metrics.recall( @@ -926,6 +980,7 @@ class RecallTest(test.TestCase): updates_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [update_op]) + @test_util.run_deprecated_v1 def testValueTensorIsIdempotent(self): predictions = random_ops.random_uniform( (10, 3), maxval=1, dtype=dtypes_lib.int64, seed=1) @@ -934,17 +989,18 @@ class RecallTest(test.TestCase): recall, update_op = metrics.recall(labels, predictions) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) # Run several updates. for _ in range(10): - sess.run(update_op) + self.evaluate(update_op) # Then verify idempotency. initial_recall = recall.eval() for _ in range(10): self.assertEqual(initial_recall, recall.eval()) + @test_util.run_deprecated_v1 def testAllCorrect(self): np_inputs = np.random.randint(0, 2, size=(100, 1)) @@ -953,10 +1009,11 @@ class RecallTest(test.TestCase): recall, update_op = metrics.recall(labels, predictions) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) - sess.run(update_op) + self.evaluate(variables.local_variables_initializer()) + self.evaluate(update_op) self.assertEqual(1, recall.eval()) + @test_util.run_deprecated_v1 def testSomeCorrect_multipleInputDtypes(self): for dtype in (dtypes_lib.bool, dtypes_lib.int32, dtypes_lib.float32): predictions = math_ops.cast( @@ -966,10 +1023,11 @@ class RecallTest(test.TestCase): recall, update_op = metrics.recall(labels, predictions) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertAlmostEqual(0.5, update_op.eval()) self.assertAlmostEqual(0.5, recall.eval()) + @test_util.run_deprecated_v1 def testWeighted1d(self): predictions = constant_op.constant([[1, 0, 1, 0], [0, 1, 0, 1]]) labels = constant_op.constant([[0, 1, 1, 0], [1, 0, 0, 1]]) @@ -977,13 +1035,14 @@ class RecallTest(test.TestCase): recall, update_op = metrics.recall(labels, predictions, weights=weights) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) weighted_tp = 2.0 + 5.0 weighted_t = (2.0 + 2.0) + (5.0 + 5.0) expected_precision = weighted_tp / weighted_t self.assertAlmostEqual(expected_precision, update_op.eval()) self.assertAlmostEqual(expected_precision, recall.eval()) + @test_util.run_deprecated_v1 def testWeighted2d(self): predictions = constant_op.constant([[1, 0, 1, 0], [0, 1, 0, 1]]) labels = constant_op.constant([[0, 1, 1, 0], [1, 0, 0, 1]]) @@ -991,13 +1050,14 @@ class RecallTest(test.TestCase): recall, update_op = metrics.recall(labels, predictions, weights=weights) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) weighted_tp = 3.0 + 1.0 weighted_t = (2.0 + 3.0) + (4.0 + 1.0) expected_precision = weighted_tp / weighted_t self.assertAlmostEqual(expected_precision, update_op.eval()) self.assertAlmostEqual(expected_precision, recall.eval()) + @test_util.run_deprecated_v1 def testAllIncorrect(self): np_inputs = np.random.randint(0, 2, size=(100, 1)) @@ -1006,18 +1066,19 @@ class RecallTest(test.TestCase): recall, update_op = metrics.recall(labels, predictions) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) - sess.run(update_op) + self.evaluate(variables.local_variables_initializer()) + self.evaluate(update_op) self.assertEqual(0, recall.eval()) + @test_util.run_deprecated_v1 def testZeroTruePositivesAndFalseNegativesGivesZeroRecall(self): predictions = array_ops.zeros((1, 4)) labels = array_ops.zeros((1, 4)) recall, update_op = metrics.recall(labels, predictions) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) - sess.run(update_op) + self.evaluate(variables.local_variables_initializer()) + self.evaluate(update_op) self.assertEqual(0, recall.eval()) @@ -1027,6 +1088,7 @@ class AUCTest(test.TestCase): np.random.seed(1) ops.reset_default_graph() + @test_util.run_deprecated_v1 def testVars(self): metrics.auc(predictions=array_ops.ones((10, 1)), labels=array_ops.ones((10, 1))) @@ -1034,6 +1096,7 @@ class AUCTest(test.TestCase): ('auc/true_positives:0', 'auc/false_negatives:0', 'auc/false_positives:0', 'auc/true_negatives:0')) + @test_util.run_deprecated_v1 def testMetricsCollection(self): my_collection_name = '__metrics__' mean, _ = metrics.auc(predictions=array_ops.ones((10, 1)), @@ -1041,6 +1104,7 @@ class AUCTest(test.TestCase): metrics_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [mean]) + @test_util.run_deprecated_v1 def testUpdatesCollection(self): my_collection_name = '__updates__' _, update_op = metrics.auc(predictions=array_ops.ones((10, 1)), @@ -1048,6 +1112,7 @@ class AUCTest(test.TestCase): updates_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [update_op]) + @test_util.run_deprecated_v1 def testValueTensorIsIdempotent(self): predictions = random_ops.random_uniform( (10, 3), maxval=1, dtype=dtypes_lib.float32, seed=1) @@ -1056,17 +1121,18 @@ class AUCTest(test.TestCase): auc, update_op = metrics.auc(labels, predictions) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) # Run several updates. for _ in range(10): - sess.run(update_op) + self.evaluate(update_op) # Then verify idempotency. initial_auc = auc.eval() for _ in range(10): self.assertAlmostEqual(initial_auc, auc.eval(), 5) + @test_util.run_deprecated_v1 def testAllCorrect(self): self.allCorrectAsExpected('ROC') @@ -1078,11 +1144,12 @@ class AUCTest(test.TestCase): labels = constant_op.constant(inputs) auc, update_op = metrics.auc(labels, predictions, curve=curve) - sess.run(variables.local_variables_initializer()) - self.assertEqual(1, sess.run(update_op)) + self.evaluate(variables.local_variables_initializer()) + self.assertEqual(1, self.evaluate(update_op)) self.assertEqual(1, auc.eval()) + @test_util.run_deprecated_v1 def testSomeCorrect_multipleLabelDtypes(self): with self.cached_session() as sess: for label_dtype in ( @@ -1093,11 +1160,12 @@ class AUCTest(test.TestCase): constant_op.constant([0, 1, 1, 0], shape=(1, 4)), dtype=label_dtype) auc, update_op = metrics.auc(labels, predictions) - sess.run(variables.local_variables_initializer()) - self.assertAlmostEqual(0.5, sess.run(update_op)) + self.evaluate(variables.local_variables_initializer()) + self.assertAlmostEqual(0.5, self.evaluate(update_op)) self.assertAlmostEqual(0.5, auc.eval()) + @test_util.run_deprecated_v1 def testWeighted1d(self): with self.cached_session() as sess: predictions = constant_op.constant( @@ -1106,11 +1174,12 @@ class AUCTest(test.TestCase): weights = constant_op.constant([2], shape=(1, 1)) auc, update_op = metrics.auc(labels, predictions, weights=weights) - sess.run(variables.local_variables_initializer()) - self.assertAlmostEqual(0.5, sess.run(update_op), 5) + self.evaluate(variables.local_variables_initializer()) + self.assertAlmostEqual(0.5, self.evaluate(update_op), 5) self.assertAlmostEqual(0.5, auc.eval(), 5) + @test_util.run_deprecated_v1 def testWeighted2d(self): with self.cached_session() as sess: predictions = constant_op.constant( @@ -1119,13 +1188,14 @@ class AUCTest(test.TestCase): weights = constant_op.constant([1, 2, 3, 4], shape=(1, 4)) auc, update_op = metrics.auc(labels, predictions, weights=weights) - sess.run(variables.local_variables_initializer()) - self.assertAlmostEqual(0.7, sess.run(update_op), 5) + self.evaluate(variables.local_variables_initializer()) + self.assertAlmostEqual(0.7, self.evaluate(update_op), 5) self.assertAlmostEqual(0.7, auc.eval(), 5) # Regarding the AUC-PR tests: note that the preferred method when # calculating AUC-PR is summation_method='careful_interpolation'. + @test_util.run_deprecated_v1 def testCorrectAUCPRSpecialCase(self): with self.cached_session() as sess: predictions = constant_op.constant( @@ -1134,12 +1204,13 @@ class AUCTest(test.TestCase): auc, update_op = metrics.auc(labels, predictions, curve='PR', summation_method='careful_interpolation') - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) # expected ~= 0.79726744594 expected = 1 - math.log(1.5) / 2 - self.assertAlmostEqual(expected, sess.run(update_op), delta=1e-3) + self.assertAlmostEqual(expected, self.evaluate(update_op), delta=1e-3) self.assertAlmostEqual(expected, auc.eval(), delta=1e-3) + @test_util.run_deprecated_v1 def testCorrectAnotherAUCPRSpecialCase(self): with self.cached_session() as sess: predictions = constant_op.constant( @@ -1150,12 +1221,13 @@ class AUCTest(test.TestCase): auc, update_op = metrics.auc(labels, predictions, curve='PR', summation_method='careful_interpolation') - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) # expected ~= 0.61350593198 expected = (2.5 - 2 * math.log(4./3) - 0.25 * math.log(7./5)) / 3 - self.assertAlmostEqual(expected, sess.run(update_op), delta=1e-3) + self.assertAlmostEqual(expected, self.evaluate(update_op), delta=1e-3) self.assertAlmostEqual(expected, auc.eval(), delta=1e-3) + @test_util.run_deprecated_v1 def testThirdCorrectAUCPRSpecialCase(self): with self.cached_session() as sess: predictions = constant_op.constant( @@ -1166,12 +1238,13 @@ class AUCTest(test.TestCase): auc, update_op = metrics.auc(labels, predictions, curve='PR', summation_method='careful_interpolation') - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) # expected ~= 0.90410597584 expected = 1 - math.log(4./3) / 3 - self.assertAlmostEqual(expected, sess.run(update_op), delta=1e-3) + self.assertAlmostEqual(expected, self.evaluate(update_op), delta=1e-3) self.assertAlmostEqual(expected, auc.eval(), delta=1e-3) + @test_util.run_deprecated_v1 def testIncorrectAUCPRSpecialCase(self): with self.cached_session() as sess: predictions = constant_op.constant( @@ -1180,11 +1253,12 @@ class AUCTest(test.TestCase): auc, update_op = metrics.auc(labels, predictions, curve='PR', summation_method='trapezoidal') - sess.run(variables.local_variables_initializer()) - self.assertAlmostEqual(0.79166, sess.run(update_op), delta=1e-3) + self.evaluate(variables.local_variables_initializer()) + self.assertAlmostEqual(0.79166, self.evaluate(update_op), delta=1e-3) self.assertAlmostEqual(0.79166, auc.eval(), delta=1e-3) + @test_util.run_deprecated_v1 def testAnotherIncorrectAUCPRSpecialCase(self): with self.cached_session() as sess: predictions = constant_op.constant( @@ -1195,11 +1269,12 @@ class AUCTest(test.TestCase): auc, update_op = metrics.auc(labels, predictions, curve='PR', summation_method='trapezoidal') - sess.run(variables.local_variables_initializer()) - self.assertAlmostEqual(0.610317, sess.run(update_op), delta=1e-3) + self.evaluate(variables.local_variables_initializer()) + self.assertAlmostEqual(0.610317, self.evaluate(update_op), delta=1e-3) self.assertAlmostEqual(0.610317, auc.eval(), delta=1e-3) + @test_util.run_deprecated_v1 def testThirdIncorrectAUCPRSpecialCase(self): with self.cached_session() as sess: predictions = constant_op.constant( @@ -1210,11 +1285,12 @@ class AUCTest(test.TestCase): auc, update_op = metrics.auc(labels, predictions, curve='PR', summation_method='trapezoidal') - sess.run(variables.local_variables_initializer()) - self.assertAlmostEqual(0.90277, sess.run(update_op), delta=1e-3) + self.evaluate(variables.local_variables_initializer()) + self.assertAlmostEqual(0.90277, self.evaluate(update_op), delta=1e-3) self.assertAlmostEqual(0.90277, auc.eval(), delta=1e-3) + @test_util.run_deprecated_v1 def testAllIncorrect(self): inputs = np.random.randint(0, 2, size=(100, 1)) @@ -1223,30 +1299,32 @@ class AUCTest(test.TestCase): labels = constant_op.constant(1 - inputs, dtype=dtypes_lib.float32) auc, update_op = metrics.auc(labels, predictions) - sess.run(variables.local_variables_initializer()) - self.assertAlmostEqual(0, sess.run(update_op)) + self.evaluate(variables.local_variables_initializer()) + self.assertAlmostEqual(0, self.evaluate(update_op)) self.assertAlmostEqual(0, auc.eval()) + @test_util.run_deprecated_v1 def testZeroTruePositivesAndFalseNegativesGivesOneAUC(self): with self.cached_session() as sess: predictions = array_ops.zeros([4], dtype=dtypes_lib.float32) labels = array_ops.zeros([4]) auc, update_op = metrics.auc(labels, predictions) - sess.run(variables.local_variables_initializer()) - self.assertAlmostEqual(1, sess.run(update_op), 6) + self.evaluate(variables.local_variables_initializer()) + self.assertAlmostEqual(1, self.evaluate(update_op), 6) self.assertAlmostEqual(1, auc.eval(), 6) + @test_util.run_deprecated_v1 def testRecallOneAndPrecisionOneGivesOnePRAUC(self): with self.cached_session() as sess: predictions = array_ops.ones([4], dtype=dtypes_lib.float32) labels = array_ops.ones([4]) auc, update_op = metrics.auc(labels, predictions, curve='PR') - sess.run(variables.local_variables_initializer()) - self.assertAlmostEqual(1, sess.run(update_op), 6) + self.evaluate(variables.local_variables_initializer()) + self.assertAlmostEqual(1, self.evaluate(update_op), 6) self.assertAlmostEqual(1, auc.eval(), 6) @@ -1277,6 +1355,7 @@ class AUCTest(test.TestCase): tp = np.cumsum(sorted_weights * is_positive) / num_positives return np.sum((sorted_weights * tp)[~is_positive]) / num_negatives + @test_util.run_deprecated_v1 def testWithMultipleUpdates(self): num_samples = 1000 batch_size = 10 @@ -1317,9 +1396,9 @@ class AUCTest(test.TestCase): num_thresholds=500, weights=tf_weights) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) for i in range(num_batches): - sess.run(update_op) + self.evaluate(update_op) # Since this is only approximate, we can't expect a 6 digits match. # Although with higher number of samples/thresholds we should see the @@ -1333,6 +1412,7 @@ class SpecificityAtSensitivityTest(test.TestCase): np.random.seed(1) ops.reset_default_graph() + @test_util.run_deprecated_v1 def testVars(self): metrics.specificity_at_sensitivity( predictions=array_ops.ones((10, 1)), @@ -1344,6 +1424,7 @@ class SpecificityAtSensitivityTest(test.TestCase): 'specificity_at_sensitivity/false_positives:0', 'specificity_at_sensitivity/true_negatives:0')) + @test_util.run_deprecated_v1 def testMetricsCollection(self): my_collection_name = '__metrics__' mean, _ = metrics.specificity_at_sensitivity( @@ -1353,6 +1434,7 @@ class SpecificityAtSensitivityTest(test.TestCase): metrics_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [mean]) + @test_util.run_deprecated_v1 def testUpdatesCollection(self): my_collection_name = '__updates__' _, update_op = metrics.specificity_at_sensitivity( @@ -1362,6 +1444,7 @@ class SpecificityAtSensitivityTest(test.TestCase): updates_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [update_op]) + @test_util.run_deprecated_v1 def testValueTensorIsIdempotent(self): predictions = random_ops.random_uniform( (10, 3), maxval=1, dtype=dtypes_lib.float32, seed=1) @@ -1371,17 +1454,18 @@ class SpecificityAtSensitivityTest(test.TestCase): labels, predictions, sensitivity=0.7) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) # Run several updates. for _ in range(10): - sess.run(update_op) + self.evaluate(update_op) # Then verify idempotency. initial_specificity = specificity.eval() for _ in range(10): self.assertAlmostEqual(initial_specificity, specificity.eval(), 5) + @test_util.run_deprecated_v1 def testAllCorrect(self): inputs = np.random.randint(0, 2, size=(100, 1)) @@ -1391,10 +1475,11 @@ class SpecificityAtSensitivityTest(test.TestCase): labels, predictions, sensitivity=0.7) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) - self.assertEqual(1, sess.run(update_op)) + self.evaluate(variables.local_variables_initializer()) + self.assertEqual(1, self.evaluate(update_op)) self.assertEqual(1, specificity.eval()) + @test_util.run_deprecated_v1 def testSomeCorrectHighSensitivity(self): predictions_values = [0.1, 0.2, 0.4, 0.3, 0.0, 0.1, 0.45, 0.5, 0.8, 0.9] labels_values = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] @@ -1406,10 +1491,11 @@ class SpecificityAtSensitivityTest(test.TestCase): labels, predictions, sensitivity=0.8) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) - self.assertAlmostEqual(1.0, sess.run(update_op)) + self.evaluate(variables.local_variables_initializer()) + self.assertAlmostEqual(1.0, self.evaluate(update_op)) self.assertAlmostEqual(1.0, specificity.eval()) + @test_util.run_deprecated_v1 def testSomeCorrectLowSensitivity(self): predictions_values = [0.1, 0.2, 0.4, 0.3, 0.0, 0.1, 0.2, 0.2, 0.26, 0.26] labels_values = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] @@ -1421,11 +1507,12 @@ class SpecificityAtSensitivityTest(test.TestCase): labels, predictions, sensitivity=0.4) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) - self.assertAlmostEqual(0.6, sess.run(update_op)) + self.assertAlmostEqual(0.6, self.evaluate(update_op)) self.assertAlmostEqual(0.6, specificity.eval()) + @test_util.run_deprecated_v1 def testWeighted1d_multipleLabelDtypes(self): for label_dtype in (dtypes_lib.bool, dtypes_lib.int32, dtypes_lib.float32): predictions_values = [0.1, 0.2, 0.4, 0.3, 0.0, 0.1, 0.2, 0.2, 0.26, 0.26] @@ -1440,11 +1527,12 @@ class SpecificityAtSensitivityTest(test.TestCase): labels, predictions, weights=weights, sensitivity=0.4) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) - self.assertAlmostEqual(0.6, sess.run(update_op)) + self.assertAlmostEqual(0.6, self.evaluate(update_op)) self.assertAlmostEqual(0.6, specificity.eval()) + @test_util.run_deprecated_v1 def testWeighted2d(self): predictions_values = [0.1, 0.2, 0.4, 0.3, 0.0, 0.1, 0.2, 0.2, 0.26, 0.26] labels_values = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] @@ -1458,9 +1546,9 @@ class SpecificityAtSensitivityTest(test.TestCase): labels, predictions, weights=weights, sensitivity=0.4) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) - self.assertAlmostEqual(8.0 / 15.0, sess.run(update_op)) + self.assertAlmostEqual(8.0 / 15.0, self.evaluate(update_op)) self.assertAlmostEqual(8.0 / 15.0, specificity.eval()) @@ -1470,6 +1558,7 @@ class SensitivityAtSpecificityTest(test.TestCase): np.random.seed(1) ops.reset_default_graph() + @test_util.run_deprecated_v1 def testVars(self): metrics.sensitivity_at_specificity( predictions=array_ops.ones((10, 1)), @@ -1481,6 +1570,7 @@ class SensitivityAtSpecificityTest(test.TestCase): 'sensitivity_at_specificity/false_positives:0', 'sensitivity_at_specificity/true_negatives:0')) + @test_util.run_deprecated_v1 def testMetricsCollection(self): my_collection_name = '__metrics__' mean, _ = metrics.sensitivity_at_specificity( @@ -1490,6 +1580,7 @@ class SensitivityAtSpecificityTest(test.TestCase): metrics_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [mean]) + @test_util.run_deprecated_v1 def testUpdatesCollection(self): my_collection_name = '__updates__' _, update_op = metrics.sensitivity_at_specificity( @@ -1499,6 +1590,7 @@ class SensitivityAtSpecificityTest(test.TestCase): updates_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [update_op]) + @test_util.run_deprecated_v1 def testValueTensorIsIdempotent(self): predictions = random_ops.random_uniform( (10, 3), maxval=1, dtype=dtypes_lib.float32, seed=1) @@ -1508,17 +1600,18 @@ class SensitivityAtSpecificityTest(test.TestCase): labels, predictions, specificity=0.7) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) # Run several updates. for _ in range(10): - sess.run(update_op) + self.evaluate(update_op) # Then verify idempotency. initial_sensitivity = sensitivity.eval() for _ in range(10): self.assertAlmostEqual(initial_sensitivity, sensitivity.eval(), 5) + @test_util.run_deprecated_v1 def testAllCorrect(self): inputs = np.random.randint(0, 2, size=(100, 1)) @@ -1528,10 +1621,11 @@ class SensitivityAtSpecificityTest(test.TestCase): labels, predictions, specificity=0.7) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) - self.assertEqual(1, sess.run(update_op)) + self.evaluate(variables.local_variables_initializer()) + self.assertEqual(1, self.evaluate(update_op)) self.assertEqual(1, specificity.eval()) + @test_util.run_deprecated_v1 def testSomeCorrectHighSpecificity(self): predictions_values = [0.0, 0.1, 0.2, 0.3, 0.4, 0.1, 0.45, 0.5, 0.8, 0.9] labels_values = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] @@ -1543,10 +1637,11 @@ class SensitivityAtSpecificityTest(test.TestCase): labels, predictions, specificity=0.8) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) - self.assertAlmostEqual(0.8, sess.run(update_op)) + self.evaluate(variables.local_variables_initializer()) + self.assertAlmostEqual(0.8, self.evaluate(update_op)) self.assertAlmostEqual(0.8, specificity.eval()) + @test_util.run_deprecated_v1 def testSomeCorrectLowSpecificity(self): predictions_values = [0.0, 0.1, 0.2, 0.3, 0.4, 0.01, 0.02, 0.25, 0.26, 0.26] labels_values = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] @@ -1558,10 +1653,11 @@ class SensitivityAtSpecificityTest(test.TestCase): labels, predictions, specificity=0.4) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) - self.assertAlmostEqual(0.6, sess.run(update_op)) + self.evaluate(variables.local_variables_initializer()) + self.assertAlmostEqual(0.6, self.evaluate(update_op)) self.assertAlmostEqual(0.6, specificity.eval()) + @test_util.run_deprecated_v1 def testWeighted_multipleLabelDtypes(self): for label_dtype in (dtypes_lib.bool, dtypes_lib.int32, dtypes_lib.float32): predictions_values = [ @@ -1577,8 +1673,8 @@ class SensitivityAtSpecificityTest(test.TestCase): labels, predictions, weights=weights, specificity=0.4) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) - self.assertAlmostEqual(0.675, sess.run(update_op)) + self.evaluate(variables.local_variables_initializer()) + self.assertAlmostEqual(0.675, self.evaluate(update_op)) self.assertAlmostEqual(0.675, specificity.eval()) @@ -1589,6 +1685,7 @@ class PrecisionRecallThresholdsTest(test.TestCase): np.random.seed(1) ops.reset_default_graph() + @test_util.run_deprecated_v1 def testVars(self): metrics.precision_at_thresholds( predictions=array_ops.ones((10, 1)), @@ -1599,6 +1696,7 @@ class PrecisionRecallThresholdsTest(test.TestCase): 'precision_at_thresholds/false_positives:0', )) + @test_util.run_deprecated_v1 def testMetricsCollection(self): my_collection_name = '__metrics__' prec, _ = metrics.precision_at_thresholds( @@ -1613,6 +1711,7 @@ class PrecisionRecallThresholdsTest(test.TestCase): metrics_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [prec, rec]) + @test_util.run_deprecated_v1 def testUpdatesCollection(self): my_collection_name = '__updates__' _, precision_op = metrics.precision_at_thresholds( @@ -1628,6 +1727,7 @@ class PrecisionRecallThresholdsTest(test.TestCase): self.assertListEqual( ops.get_collection(my_collection_name), [precision_op, recall_op]) + @test_util.run_deprecated_v1 def testValueTensorIsIdempotent(self): predictions = random_ops.random_uniform( (10, 3), maxval=1, dtype=dtypes_lib.float32, seed=1) @@ -1639,18 +1739,19 @@ class PrecisionRecallThresholdsTest(test.TestCase): rec, rec_op = metrics.recall_at_thresholds(labels, predictions, thresholds) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) # Run several updates, then verify idempotency. - sess.run([prec_op, rec_op]) + self.evaluate([prec_op, rec_op]) initial_prec = prec.eval() initial_rec = rec.eval() for _ in range(10): - sess.run([prec_op, rec_op]) + self.evaluate([prec_op, rec_op]) self.assertAllClose(initial_prec, prec.eval()) self.assertAllClose(initial_rec, rec.eval()) # TODO(nsilberman): fix tests (passing but incorrect). + @test_util.run_deprecated_v1 def testAllCorrect(self): inputs = np.random.randint(0, 2, size=(100, 1)) @@ -1663,12 +1764,13 @@ class PrecisionRecallThresholdsTest(test.TestCase): rec, rec_op = metrics.recall_at_thresholds(labels, predictions, thresholds) - sess.run(variables.local_variables_initializer()) - sess.run([prec_op, rec_op]) + self.evaluate(variables.local_variables_initializer()) + self.evaluate([prec_op, rec_op]) self.assertEqual(1, prec.eval()) self.assertEqual(1, rec.eval()) + @test_util.run_deprecated_v1 def testSomeCorrect_multipleLabelDtypes(self): with self.cached_session() as sess: for label_dtype in ( @@ -1683,12 +1785,13 @@ class PrecisionRecallThresholdsTest(test.TestCase): rec, rec_op = metrics.recall_at_thresholds(labels, predictions, thresholds) - sess.run(variables.local_variables_initializer()) - sess.run([prec_op, rec_op]) + self.evaluate(variables.local_variables_initializer()) + self.evaluate([prec_op, rec_op]) self.assertAlmostEqual(0.5, prec.eval()) self.assertAlmostEqual(0.5, rec.eval()) + @test_util.run_deprecated_v1 def testAllIncorrect(self): inputs = np.random.randint(0, 2, size=(100, 1)) @@ -1701,12 +1804,13 @@ class PrecisionRecallThresholdsTest(test.TestCase): rec, rec_op = metrics.recall_at_thresholds(labels, predictions, thresholds) - sess.run(variables.local_variables_initializer()) - sess.run([prec_op, rec_op]) + self.evaluate(variables.local_variables_initializer()) + self.evaluate([prec_op, rec_op]) self.assertAlmostEqual(0, prec.eval()) self.assertAlmostEqual(0, rec.eval()) + @test_util.run_deprecated_v1 def testWeights1d(self): with self.cached_session() as sess: predictions = constant_op.constant( @@ -1729,14 +1833,15 @@ class PrecisionRecallThresholdsTest(test.TestCase): rec_low = array_ops.reshape(rec_low, shape=()) rec_high = array_ops.reshape(rec_high, shape=()) - sess.run(variables.local_variables_initializer()) - sess.run([prec_op, rec_op]) + self.evaluate(variables.local_variables_initializer()) + self.evaluate([prec_op, rec_op]) self.assertAlmostEqual(1.0, prec_low.eval(), places=5) self.assertAlmostEqual(0.0, prec_high.eval(), places=5) self.assertAlmostEqual(1.0, rec_low.eval(), places=5) self.assertAlmostEqual(0.0, rec_high.eval(), places=5) + @test_util.run_deprecated_v1 def testWeights2d(self): with self.cached_session() as sess: predictions = constant_op.constant( @@ -1759,14 +1864,15 @@ class PrecisionRecallThresholdsTest(test.TestCase): rec_low = array_ops.reshape(rec_low, shape=()) rec_high = array_ops.reshape(rec_high, shape=()) - sess.run(variables.local_variables_initializer()) - sess.run([prec_op, rec_op]) + self.evaluate(variables.local_variables_initializer()) + self.evaluate([prec_op, rec_op]) self.assertAlmostEqual(1.0, prec_low.eval(), places=5) self.assertAlmostEqual(0.0, prec_high.eval(), places=5) self.assertAlmostEqual(1.0, rec_low.eval(), places=5) self.assertAlmostEqual(0.0, rec_high.eval(), places=5) + @test_util.run_deprecated_v1 def testExtremeThresholds(self): with self.cached_session() as sess: predictions = constant_op.constant( @@ -1783,14 +1889,15 @@ class PrecisionRecallThresholdsTest(test.TestCase): [rec_low, rec_high] = array_ops.split( value=rec, num_or_size_splits=2, axis=0) - sess.run(variables.local_variables_initializer()) - sess.run([prec_op, rec_op]) + self.evaluate(variables.local_variables_initializer()) + self.evaluate([prec_op, rec_op]) self.assertAlmostEqual(0.75, prec_low.eval()) self.assertAlmostEqual(0.0, prec_high.eval()) self.assertAlmostEqual(1.0, rec_low.eval()) self.assertAlmostEqual(0.0, rec_high.eval()) + @test_util.run_deprecated_v1 def testZeroLabelsPredictions(self): with self.cached_session() as sess: predictions = array_ops.zeros([4], dtype=dtypes_lib.float32) @@ -1801,12 +1908,13 @@ class PrecisionRecallThresholdsTest(test.TestCase): rec, rec_op = metrics.recall_at_thresholds(labels, predictions, thresholds) - sess.run(variables.local_variables_initializer()) - sess.run([prec_op, rec_op]) + self.evaluate(variables.local_variables_initializer()) + self.evaluate([prec_op, rec_op]) self.assertAlmostEqual(0, prec.eval(), 6) self.assertAlmostEqual(0, rec.eval(), 6) + @test_util.run_deprecated_v1 def testWithMultipleUpdates(self): num_samples = 1000 batch_size = 10 @@ -1869,9 +1977,9 @@ class PrecisionRecallThresholdsTest(test.TestCase): rec, rec_op = metrics.recall_at_thresholds(tf_labels, tf_predictions, thresholds) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) for _ in range(int(num_samples / batch_size)): - sess.run([prec_op, rec_op]) + self.evaluate([prec_op, rec_op]) # Since this is only approximate, we can't expect a 6 digits match. # Although with higher number of samples/thresholds we should see the # accuracy improving @@ -1989,6 +2097,7 @@ class SingleLabelPrecisionAtKTest(test.TestCase): self._test_average_precision_at_k = functools.partial( _test_average_precision_at_k, test_case=self) + @test_util.run_deprecated_v1 def test_at_k1_nan(self): for labels in self._labels: # Classes 0,1,2 have 0 predictions, classes -1 and 4 are out of range. @@ -1998,6 +2107,7 @@ class SingleLabelPrecisionAtKTest(test.TestCase): self._test_precision_at_top_k( self._predictions_idx, labels, k=1, expected=NAN, class_id=class_id) + @test_util.run_deprecated_v1 def test_at_k1(self): for labels in self._labels: # Class 3: 1 label, 2 predictions, 1 correct. @@ -2025,6 +2135,7 @@ class MultiLabelPrecisionAtKTest(test.TestCase): self._test_average_precision_at_k = functools.partial( _test_average_precision_at_k, test_case=self) + @test_util.run_deprecated_v1 def test_average_precision(self): # Example 1. # Matches example here: @@ -2100,6 +2211,7 @@ class MultiLabelPrecisionAtKTest(test.TestCase): expected=streaming_average_precision[i], weights=weights) + @test_util.run_deprecated_v1 def test_average_precision_some_labels_out_of_range(self): """Tests that labels outside the [0, n_classes) range are ignored.""" labels_ex1 = (-1, 0, 1, 2, 3, 4, 7) @@ -2119,6 +2231,7 @@ class MultiLabelPrecisionAtKTest(test.TestCase): self._test_average_precision_at_k( predictions, labels, k, expected=avg_precision_ex1[i]) + @test_util.run_deprecated_v1 def test_three_labels_at_k5_no_predictions(self): predictions = [[0.5, 0.1, 0.6, 0.3, 0.8, 0.0, 0.7, 0.2, 0.4, 0.9], [0.3, 0.0, 0.7, 0.2, 0.4, 0.9, 0.5, 0.8, 0.1, 0.6]] @@ -2135,6 +2248,7 @@ class MultiLabelPrecisionAtKTest(test.TestCase): self._test_precision_at_top_k( predictions_idx, labels, k=5, expected=NAN, class_id=class_id) + @test_util.run_deprecated_v1 def test_three_labels_at_k5_no_labels(self): predictions = [[0.5, 0.1, 0.6, 0.3, 0.8, 0.0, 0.7, 0.2, 0.4, 0.9], [0.3, 0.0, 0.7, 0.2, 0.4, 0.9, 0.5, 0.8, 0.1, 0.6]] @@ -2151,6 +2265,7 @@ class MultiLabelPrecisionAtKTest(test.TestCase): self._test_precision_at_top_k( predictions_idx, labels, k=5, expected=0.0, class_id=class_id) + @test_util.run_deprecated_v1 def test_three_labels_at_k5(self): predictions = [[0.5, 0.1, 0.6, 0.3, 0.8, 0.0, 0.7, 0.2, 0.4, 0.9], [0.3, 0.0, 0.7, 0.2, 0.4, 0.9, 0.5, 0.8, 0.1, 0.6]] @@ -2184,6 +2299,7 @@ class MultiLabelPrecisionAtKTest(test.TestCase): self._test_precision_at_top_k( predictions_idx, labels, k=5, expected=3.0 / 10) + @test_util.run_deprecated_v1 def test_three_labels_at_k5_some_out_of_range(self): """Tests that labels outside the [0, n_classes) range are ignored.""" predictions = [[0.5, 0.1, 0.6, 0.3, 0.8, 0.0, 0.7, 0.2, 0.4, 0.9], @@ -2220,6 +2336,7 @@ class MultiLabelPrecisionAtKTest(test.TestCase): self._test_precision_at_top_k( predictions_idx, sp_labels, k=5, expected=3.0 / 10) + @test_util.run_deprecated_v1 def test_3d_nan(self): predictions = [[[0.5, 0.1, 0.6, 0.3, 0.8, 0.0, 0.7, 0.2, 0.4, 0.9], [0.3, 0.0, 0.7, 0.2, 0.4, 0.9, 0.5, 0.8, 0.1, 0.6]], @@ -2238,6 +2355,7 @@ class MultiLabelPrecisionAtKTest(test.TestCase): self._test_precision_at_top_k( predictions_idx, labels, k=5, expected=NAN, class_id=class_id) + @test_util.run_deprecated_v1 def test_3d_no_labels(self): predictions = [[[0.5, 0.1, 0.6, 0.3, 0.8, 0.0, 0.7, 0.2, 0.4, 0.9], [0.3, 0.0, 0.7, 0.2, 0.4, 0.9, 0.5, 0.8, 0.1, 0.6]], @@ -2256,6 +2374,7 @@ class MultiLabelPrecisionAtKTest(test.TestCase): self._test_precision_at_top_k( predictions_idx, labels, k=5, expected=0.0, class_id=class_id) + @test_util.run_deprecated_v1 def test_3d(self): predictions = [[[0.5, 0.1, 0.6, 0.3, 0.8, 0.0, 0.7, 0.2, 0.4, 0.9], [0.3, 0.0, 0.7, 0.2, 0.4, 0.9, 0.5, 0.8, 0.1, 0.6]], @@ -2291,6 +2410,7 @@ class MultiLabelPrecisionAtKTest(test.TestCase): self._test_precision_at_top_k( predictions_idx, labels, k=5, expected=7.0 / 20) + @test_util.run_deprecated_v1 def test_3d_ignore_some(self): predictions = [[[0.5, 0.1, 0.6, 0.3, 0.8, 0.0, 0.7, 0.2, 0.4, 0.9], [0.3, 0.0, 0.7, 0.2, 0.4, 0.9, 0.5, 0.8, 0.1, 0.6]], @@ -2432,6 +2552,7 @@ class SingleLabelRecallAtKTest(test.TestCase): self._test_recall_at_top_k = functools.partial( _test_recall_at_top_k, test_case=self) + @test_util.run_deprecated_v1 def test_at_k1_nan(self): # Classes 0,1 have 0 labels, 0 predictions, classes -1 and 4 are out of # range. @@ -2442,6 +2563,7 @@ class SingleLabelRecallAtKTest(test.TestCase): self._test_recall_at_top_k( self._predictions_idx, labels, k=1, expected=NAN, class_id=class_id) + @test_util.run_deprecated_v1 def test_at_k1_no_predictions(self): for labels in self._labels: # Class 2: 0 predictions. @@ -2450,6 +2572,7 @@ class SingleLabelRecallAtKTest(test.TestCase): self._test_recall_at_top_k( self._predictions_idx, labels, k=1, expected=0.0, class_id=2) + @test_util.run_deprecated_v1 def test_one_label_at_k1(self): for labels in self._labels: # Class 3: 1 label, 2 predictions, 1 correct. @@ -2463,6 +2586,7 @@ class SingleLabelRecallAtKTest(test.TestCase): self._test_recall_at_top_k( self._predictions_idx, labels, k=1, expected=1.0 / 2) + @test_util.run_deprecated_v1 def test_one_label_at_k1_weighted_class_id3(self): predictions = self._predictions predictions_idx = self._predictions_idx @@ -2504,6 +2628,7 @@ class SingleLabelRecallAtKTest(test.TestCase): predictions_idx, labels, k=1, expected=2.0 / 2, class_id=3, weights=(2.0, 3.0)) + @test_util.run_deprecated_v1 def test_one_label_at_k1_weighted(self): predictions = self._predictions predictions_idx = self._predictions_idx @@ -2553,6 +2678,7 @@ class MultiLabel2dRecallAtKTest(test.TestCase): self._test_recall_at_top_k = functools.partial( _test_recall_at_top_k, test_case=self) + @test_util.run_deprecated_v1 def test_at_k5_nan(self): for labels in self._labels: # Classes 0,3,4,6,9 have 0 labels, class 10 is out of range. @@ -2562,6 +2688,7 @@ class MultiLabel2dRecallAtKTest(test.TestCase): self._test_recall_at_top_k( self._predictions_idx, labels, k=5, expected=NAN, class_id=class_id) + @test_util.run_deprecated_v1 def test_at_k5_no_predictions(self): for labels in self._labels: # Class 8: 1 label, no predictions. @@ -2570,6 +2697,7 @@ class MultiLabel2dRecallAtKTest(test.TestCase): self._test_recall_at_top_k( self._predictions_idx, labels, k=5, expected=0.0 / 1, class_id=8) + @test_util.run_deprecated_v1 def test_at_k5(self): for labels in self._labels: # Class 2: 2 labels, both correct. @@ -2595,6 +2723,7 @@ class MultiLabel2dRecallAtKTest(test.TestCase): self._test_recall_at_top_k( self._predictions_idx, labels, k=5, expected=3.0 / 6) + @test_util.run_deprecated_v1 def test_at_k5_some_out_of_range(self): """Tests that labels outside the [0, n_classes) count in denominator.""" labels = sparse_tensor.SparseTensorValue( @@ -2647,6 +2776,7 @@ class MultiLabel3dRecallAtKTest(test.TestCase): self._test_recall_at_top_k = functools.partial( _test_recall_at_top_k, test_case=self) + @test_util.run_deprecated_v1 def test_3d_nan(self): # Classes 0,3,4,6,9 have 0 labels, class 10 is out of range. for class_id in (0, 3, 4, 6, 9, 10): @@ -2656,6 +2786,7 @@ class MultiLabel3dRecallAtKTest(test.TestCase): self._predictions_idx, self._labels, k=5, expected=NAN, class_id=class_id) + @test_util.run_deprecated_v1 def test_3d_no_predictions(self): # Classes 1,8 have 0 predictions, >=1 label. for class_id in (1, 8): @@ -2665,6 +2796,7 @@ class MultiLabel3dRecallAtKTest(test.TestCase): self._predictions_idx, self._labels, k=5, expected=0.0, class_id=class_id) + @test_util.run_deprecated_v1 def test_3d(self): # Class 2: 4 labels, all correct. self._test_recall_at_k( @@ -2693,6 +2825,7 @@ class MultiLabel3dRecallAtKTest(test.TestCase): self._test_recall_at_top_k( self._predictions_idx, self._labels, k=5, expected=7.0 / 12) + @test_util.run_deprecated_v1 def test_3d_ignore_all(self): for class_id in xrange(10): self._test_recall_at_k( @@ -2719,6 +2852,7 @@ class MultiLabel3dRecallAtKTest(test.TestCase): self._predictions_idx, self._labels, k=5, expected=NAN, weights=[[0, 0], [0, 0]]) + @test_util.run_deprecated_v1 def test_3d_ignore_some(self): # Class 2: 2 labels, both correct. self._test_recall_at_k( @@ -2774,12 +2908,14 @@ class MeanAbsoluteErrorTest(test.TestCase): def setUp(self): ops.reset_default_graph() + @test_util.run_deprecated_v1 def testVars(self): metrics.mean_absolute_error( predictions=array_ops.ones((10, 1)), labels=array_ops.ones((10, 1))) _assert_metric_variables( self, ('mean_absolute_error/count:0', 'mean_absolute_error/total:0')) + @test_util.run_deprecated_v1 def testMetricsCollection(self): my_collection_name = '__metrics__' mean, _ = metrics.mean_absolute_error( @@ -2788,6 +2924,7 @@ class MeanAbsoluteErrorTest(test.TestCase): metrics_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [mean]) + @test_util.run_deprecated_v1 def testUpdatesCollection(self): my_collection_name = '__updates__' _, update_op = metrics.mean_absolute_error( @@ -2796,23 +2933,25 @@ class MeanAbsoluteErrorTest(test.TestCase): updates_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [update_op]) + @test_util.run_deprecated_v1 def testValueTensorIsIdempotent(self): predictions = random_ops.random_normal((10, 3), seed=1) labels = random_ops.random_normal((10, 3), seed=2) error, update_op = metrics.mean_absolute_error(labels, predictions) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) # Run several updates. for _ in range(10): - sess.run(update_op) + self.evaluate(update_op) # Then verify idempotency. initial_error = error.eval() for _ in range(10): self.assertEqual(initial_error, error.eval()) + @test_util.run_deprecated_v1 def testSingleUpdateWithErrorAndWeights(self): predictions = constant_op.constant( [2, 4, 6, 8], shape=(1, 4), dtype=dtypes_lib.float32) @@ -2823,8 +2962,8 @@ class MeanAbsoluteErrorTest(test.TestCase): error, update_op = metrics.mean_absolute_error(labels, predictions, weights) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) - self.assertEqual(3, sess.run(update_op)) + self.evaluate(variables.local_variables_initializer()) + self.assertEqual(3, self.evaluate(update_op)) self.assertEqual(3, error.eval()) @@ -2833,6 +2972,7 @@ class MeanRelativeErrorTest(test.TestCase): def setUp(self): ops.reset_default_graph() + @test_util.run_deprecated_v1 def testVars(self): metrics.mean_relative_error( predictions=array_ops.ones((10, 1)), @@ -2841,6 +2981,7 @@ class MeanRelativeErrorTest(test.TestCase): _assert_metric_variables( self, ('mean_relative_error/count:0', 'mean_relative_error/total:0')) + @test_util.run_deprecated_v1 def testMetricsCollection(self): my_collection_name = '__metrics__' mean, _ = metrics.mean_relative_error( @@ -2850,6 +2991,7 @@ class MeanRelativeErrorTest(test.TestCase): metrics_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [mean]) + @test_util.run_deprecated_v1 def testUpdatesCollection(self): my_collection_name = '__updates__' _, update_op = metrics.mean_relative_error( @@ -2859,6 +3001,7 @@ class MeanRelativeErrorTest(test.TestCase): updates_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [update_op]) + @test_util.run_deprecated_v1 def testValueTensorIsIdempotent(self): predictions = random_ops.random_normal((10, 3), seed=1) labels = random_ops.random_normal((10, 3), seed=2) @@ -2867,17 +3010,18 @@ class MeanRelativeErrorTest(test.TestCase): normalizer) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) # Run several updates. for _ in range(10): - sess.run(update_op) + self.evaluate(update_op) # Then verify idempotency. initial_error = error.eval() for _ in range(10): self.assertEqual(initial_error, error.eval()) + @test_util.run_deprecated_v1 def testSingleUpdateNormalizedByLabels(self): np_predictions = np.asarray([2, 4, 6, 8], dtype=np.float32) np_labels = np.asarray([1, 3, 2, 3], dtype=np.float32) @@ -2892,10 +3036,11 @@ class MeanRelativeErrorTest(test.TestCase): labels, predictions, normalizer=labels) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) - self.assertEqual(expected_error, sess.run(update_op)) + self.evaluate(variables.local_variables_initializer()) + self.assertEqual(expected_error, self.evaluate(update_op)) self.assertEqual(expected_error, error.eval()) + @test_util.run_deprecated_v1 def testSingleUpdateNormalizedByZeros(self): np_predictions = np.asarray([2, 4, 6, 8], dtype=np.float32) @@ -2908,8 +3053,8 @@ class MeanRelativeErrorTest(test.TestCase): labels, predictions, normalizer=array_ops.zeros_like(labels)) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) - self.assertEqual(0.0, sess.run(update_op)) + self.evaluate(variables.local_variables_initializer()) + self.assertEqual(0.0, self.evaluate(update_op)) self.assertEqual(0.0, error.eval()) @@ -2918,12 +3063,14 @@ class MeanSquaredErrorTest(test.TestCase): def setUp(self): ops.reset_default_graph() + @test_util.run_deprecated_v1 def testVars(self): metrics.mean_squared_error( predictions=array_ops.ones((10, 1)), labels=array_ops.ones((10, 1))) _assert_metric_variables( self, ('mean_squared_error/count:0', 'mean_squared_error/total:0')) + @test_util.run_deprecated_v1 def testMetricsCollection(self): my_collection_name = '__metrics__' mean, _ = metrics.mean_squared_error( @@ -2932,6 +3079,7 @@ class MeanSquaredErrorTest(test.TestCase): metrics_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [mean]) + @test_util.run_deprecated_v1 def testUpdatesCollection(self): my_collection_name = '__updates__' _, update_op = metrics.mean_squared_error( @@ -2940,23 +3088,25 @@ class MeanSquaredErrorTest(test.TestCase): updates_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [update_op]) + @test_util.run_deprecated_v1 def testValueTensorIsIdempotent(self): predictions = random_ops.random_normal((10, 3), seed=1) labels = random_ops.random_normal((10, 3), seed=2) error, update_op = metrics.mean_squared_error(labels, predictions) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) # Run several updates. for _ in range(10): - sess.run(update_op) + self.evaluate(update_op) # Then verify idempotency. initial_error = error.eval() for _ in range(10): self.assertEqual(initial_error, error.eval()) + @test_util.run_deprecated_v1 def testSingleUpdateZeroError(self): predictions = array_ops.zeros((1, 3), dtype=dtypes_lib.float32) labels = array_ops.zeros((1, 3), dtype=dtypes_lib.float32) @@ -2964,10 +3114,11 @@ class MeanSquaredErrorTest(test.TestCase): error, update_op = metrics.mean_squared_error(labels, predictions) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) - self.assertEqual(0, sess.run(update_op)) + self.evaluate(variables.local_variables_initializer()) + self.assertEqual(0, self.evaluate(update_op)) self.assertEqual(0, error.eval()) + @test_util.run_deprecated_v1 def testSingleUpdateWithError(self): predictions = constant_op.constant( [2, 4, 6], shape=(1, 3), dtype=dtypes_lib.float32) @@ -2977,10 +3128,11 @@ class MeanSquaredErrorTest(test.TestCase): error, update_op = metrics.mean_squared_error(labels, predictions) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) - self.assertEqual(6, sess.run(update_op)) + self.evaluate(variables.local_variables_initializer()) + self.assertEqual(6, self.evaluate(update_op)) self.assertEqual(6, error.eval()) + @test_util.run_deprecated_v1 def testSingleUpdateWithErrorAndWeights(self): predictions = constant_op.constant( [2, 4, 6, 8], shape=(1, 4), dtype=dtypes_lib.float32) @@ -2991,10 +3143,11 @@ class MeanSquaredErrorTest(test.TestCase): error, update_op = metrics.mean_squared_error(labels, predictions, weights) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) - self.assertEqual(13, sess.run(update_op)) + self.evaluate(variables.local_variables_initializer()) + self.assertEqual(13, self.evaluate(update_op)) self.assertEqual(13, error.eval()) + @test_util.run_deprecated_v1 def testMultipleBatchesOfSizeOne(self): with self.cached_session() as sess: # Create the queue that populates the predictions. @@ -3013,12 +3166,13 @@ class MeanSquaredErrorTest(test.TestCase): error, update_op = metrics.mean_squared_error(labels, predictions) - sess.run(variables.local_variables_initializer()) - sess.run(update_op) - self.assertAlmostEqual(208.0 / 6, sess.run(update_op), 5) + self.evaluate(variables.local_variables_initializer()) + self.evaluate(update_op) + self.assertAlmostEqual(208.0 / 6, self.evaluate(update_op), 5) self.assertAlmostEqual(208.0 / 6, error.eval(), 5) + @test_util.run_deprecated_v1 def testMetricsComputedConcurrently(self): with self.cached_session() as sess: # Create the queue that populates one set of predictions. @@ -3054,14 +3208,15 @@ class MeanSquaredErrorTest(test.TestCase): mse1, update_op1 = metrics.mean_squared_error( labels1, predictions1, name='msd1') - sess.run(variables.local_variables_initializer()) - sess.run([update_op0, update_op1]) - sess.run([update_op0, update_op1]) + self.evaluate(variables.local_variables_initializer()) + self.evaluate([update_op0, update_op1]) + self.evaluate([update_op0, update_op1]) - mse0, mse1 = sess.run([mse0, mse1]) + mse0, mse1 = self.evaluate([mse0, mse1]) self.assertAlmostEqual(208.0 / 6, mse0, 5) self.assertAlmostEqual(79.0 / 6, mse1, 5) + @test_util.run_deprecated_v1 def testMultipleMetricsOnMultipleBatchesOfSizeOne(self): with self.cached_session() as sess: # Create the queue that populates the predictions. @@ -3081,9 +3236,9 @@ class MeanSquaredErrorTest(test.TestCase): mae, ma_update_op = metrics.mean_absolute_error(labels, predictions) mse, ms_update_op = metrics.mean_squared_error(labels, predictions) - sess.run(variables.local_variables_initializer()) - sess.run([ma_update_op, ms_update_op]) - sess.run([ma_update_op, ms_update_op]) + self.evaluate(variables.local_variables_initializer()) + self.evaluate([ma_update_op, ms_update_op]) + self.evaluate([ma_update_op, ms_update_op]) self.assertAlmostEqual(32.0 / 6, mae.eval(), 5) self.assertAlmostEqual(208.0 / 6, mse.eval(), 5) @@ -3094,6 +3249,7 @@ class RootMeanSquaredErrorTest(test.TestCase): def setUp(self): ops.reset_default_graph() + @test_util.run_deprecated_v1 def testVars(self): metrics.root_mean_squared_error( predictions=array_ops.ones((10, 1)), labels=array_ops.ones((10, 1))) @@ -3101,6 +3257,7 @@ class RootMeanSquaredErrorTest(test.TestCase): self, ('root_mean_squared_error/count:0', 'root_mean_squared_error/total:0')) + @test_util.run_deprecated_v1 def testMetricsCollection(self): my_collection_name = '__metrics__' mean, _ = metrics.root_mean_squared_error( @@ -3109,6 +3266,7 @@ class RootMeanSquaredErrorTest(test.TestCase): metrics_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [mean]) + @test_util.run_deprecated_v1 def testUpdatesCollection(self): my_collection_name = '__updates__' _, update_op = metrics.root_mean_squared_error( @@ -3117,23 +3275,25 @@ class RootMeanSquaredErrorTest(test.TestCase): updates_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [update_op]) + @test_util.run_deprecated_v1 def testValueTensorIsIdempotent(self): predictions = random_ops.random_normal((10, 3), seed=1) labels = random_ops.random_normal((10, 3), seed=2) error, update_op = metrics.root_mean_squared_error(labels, predictions) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) # Run several updates. for _ in range(10): - sess.run(update_op) + self.evaluate(update_op) # Then verify idempotency. initial_error = error.eval() for _ in range(10): self.assertEqual(initial_error, error.eval()) + @test_util.run_deprecated_v1 def testSingleUpdateZeroError(self): with self.cached_session() as sess: predictions = constant_op.constant( @@ -3142,11 +3302,12 @@ class RootMeanSquaredErrorTest(test.TestCase): rmse, update_op = metrics.root_mean_squared_error(labels, predictions) - sess.run(variables.local_variables_initializer()) - self.assertEqual(0, sess.run(update_op)) + self.evaluate(variables.local_variables_initializer()) + self.assertEqual(0, self.evaluate(update_op)) self.assertEqual(0, rmse.eval()) + @test_util.run_deprecated_v1 def testSingleUpdateWithError(self): with self.cached_session() as sess: predictions = constant_op.constant( @@ -3156,10 +3317,11 @@ class RootMeanSquaredErrorTest(test.TestCase): rmse, update_op = metrics.root_mean_squared_error(labels, predictions) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertAlmostEqual(math.sqrt(6), update_op.eval(), 5) self.assertAlmostEqual(math.sqrt(6), rmse.eval(), 5) + @test_util.run_deprecated_v1 def testSingleUpdateWithErrorAndWeights(self): with self.cached_session() as sess: predictions = constant_op.constant( @@ -3171,8 +3333,8 @@ class RootMeanSquaredErrorTest(test.TestCase): rmse, update_op = metrics.root_mean_squared_error(labels, predictions, weights) - sess.run(variables.local_variables_initializer()) - self.assertAlmostEqual(math.sqrt(13), sess.run(update_op)) + self.evaluate(variables.local_variables_initializer()) + self.assertAlmostEqual(math.sqrt(13), self.evaluate(update_op)) self.assertAlmostEqual(math.sqrt(13), rmse.eval(), 5) @@ -3187,6 +3349,7 @@ class MeanCosineDistanceTest(test.TestCase): def setUp(self): ops.reset_default_graph() + @test_util.run_deprecated_v1 def testVars(self): metrics.mean_cosine_distance( predictions=array_ops.ones((10, 3)), @@ -3197,6 +3360,7 @@ class MeanCosineDistanceTest(test.TestCase): 'mean_cosine_distance/total:0', )) + @test_util.run_deprecated_v1 def testMetricsCollection(self): my_collection_name = '__metrics__' mean, _ = metrics.mean_cosine_distance( @@ -3206,6 +3370,7 @@ class MeanCosineDistanceTest(test.TestCase): metrics_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [mean]) + @test_util.run_deprecated_v1 def testUpdatesCollection(self): my_collection_name = '__updates__' _, update_op = metrics.mean_cosine_distance( @@ -3215,23 +3380,25 @@ class MeanCosineDistanceTest(test.TestCase): updates_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [update_op]) + @test_util.run_deprecated_v1 def testValueTensorIsIdempotent(self): predictions = random_ops.random_normal((10, 3), seed=1) labels = random_ops.random_normal((10, 3), seed=2) error, update_op = metrics.mean_cosine_distance(labels, predictions, dim=1) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) # Run several updates. for _ in range(10): - sess.run(update_op) + self.evaluate(update_op) # Then verify idempotency. initial_error = error.eval() for _ in range(10): self.assertEqual(initial_error, error.eval()) + @test_util.run_deprecated_v1 def testSingleUpdateZeroError(self): np_labels = np.matrix(('1 0 0;' '0 0 1;' '0 1 0')) @@ -3243,10 +3410,11 @@ class MeanCosineDistanceTest(test.TestCase): error, update_op = metrics.mean_cosine_distance(labels, predictions, dim=2) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) - self.assertEqual(0, sess.run(update_op)) + self.evaluate(variables.local_variables_initializer()) + self.assertEqual(0, self.evaluate(update_op)) self.assertEqual(0, error.eval()) + @test_util.run_deprecated_v1 def testSingleUpdateWithError1(self): np_labels = np.matrix(('1 0 0;' '0 0 1;' '0 1 0')) np_predictions = np.matrix(('1 0 0;' '0 0 -1;' '1 0 0')) @@ -3259,10 +3427,11 @@ class MeanCosineDistanceTest(test.TestCase): error, update_op = metrics.mean_cosine_distance(labels, predictions, dim=2) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) - self.assertAlmostEqual(1, sess.run(update_op), 5) + self.evaluate(variables.local_variables_initializer()) + self.assertAlmostEqual(1, self.evaluate(update_op), 5) self.assertAlmostEqual(1, error.eval(), 5) + @test_util.run_deprecated_v1 def testSingleUpdateWithError2(self): np_predictions = np.matrix( ('0.819031913261206 0.567041924552012 0.087465312324590;' @@ -3280,10 +3449,11 @@ class MeanCosineDistanceTest(test.TestCase): error, update_op = metrics.mean_cosine_distance(labels, predictions, dim=2) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) - self.assertAlmostEqual(1.0, sess.run(update_op), 5) + self.evaluate(variables.local_variables_initializer()) + self.assertAlmostEqual(1.0, self.evaluate(update_op), 5) self.assertAlmostEqual(1.0, error.eval(), 5) + @test_util.run_deprecated_v1 def testSingleUpdateWithErrorAndWeights1(self): np_predictions = np.matrix(('1 0 0;' '0 0 -1;' '1 0 0')) np_labels = np.matrix(('1 0 0;' '0 0 1;' '0 1 0')) @@ -3299,10 +3469,11 @@ class MeanCosineDistanceTest(test.TestCase): labels, predictions, dim=2, weights=weights) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) - self.assertEqual(0, sess.run(update_op)) + self.evaluate(variables.local_variables_initializer()) + self.assertEqual(0, self.evaluate(update_op)) self.assertEqual(0, error.eval()) + @test_util.run_deprecated_v1 def testSingleUpdateWithErrorAndWeights2(self): np_predictions = np.matrix(('1 0 0;' '0 0 -1;' '1 0 0')) np_labels = np.matrix(('1 0 0;' '0 0 1;' '0 1 0')) @@ -3318,7 +3489,7 @@ class MeanCosineDistanceTest(test.TestCase): labels, predictions, dim=2, weights=weights) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertEqual(1.5, update_op.eval()) self.assertEqual(1.5, error.eval()) @@ -3328,6 +3499,7 @@ class PcntBelowThreshTest(test.TestCase): def setUp(self): ops.reset_default_graph() + @test_util.run_deprecated_v1 def testVars(self): metrics.percentage_below(values=array_ops.ones((10,)), threshold=2) _assert_metric_variables(self, ( @@ -3335,6 +3507,7 @@ class PcntBelowThreshTest(test.TestCase): 'percentage_below_threshold/total:0', )) + @test_util.run_deprecated_v1 def testMetricsCollection(self): my_collection_name = '__metrics__' mean, _ = metrics.percentage_below( @@ -3343,6 +3516,7 @@ class PcntBelowThreshTest(test.TestCase): metrics_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [mean]) + @test_util.run_deprecated_v1 def testUpdatesCollection(self): my_collection_name = '__updates__' _, update_op = metrics.percentage_below( @@ -3351,6 +3525,7 @@ class PcntBelowThreshTest(test.TestCase): updates_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [update_op]) + @test_util.run_deprecated_v1 def testOneUpdate(self): with self.cached_session() as sess: values = constant_op.constant( @@ -3360,14 +3535,15 @@ class PcntBelowThreshTest(test.TestCase): pcnt1, update_op1 = metrics.percentage_below(values, 7, name='medium') pcnt2, update_op2 = metrics.percentage_below(values, 1, name='low') - sess.run(variables.local_variables_initializer()) - sess.run([update_op0, update_op1, update_op2]) + self.evaluate(variables.local_variables_initializer()) + self.evaluate([update_op0, update_op1, update_op2]) - pcnt0, pcnt1, pcnt2 = sess.run([pcnt0, pcnt1, pcnt2]) + pcnt0, pcnt1, pcnt2 = self.evaluate([pcnt0, pcnt1, pcnt2]) self.assertAlmostEqual(1.0, pcnt0, 5) self.assertAlmostEqual(0.75, pcnt1, 5) self.assertAlmostEqual(0.0, pcnt2, 5) + @test_util.run_deprecated_v1 def testSomePresentOneUpdate(self): with self.cached_session() as sess: values = constant_op.constant( @@ -3382,11 +3558,11 @@ class PcntBelowThreshTest(test.TestCase): pcnt2, update_op2 = metrics.percentage_below( values, 1, weights=weights, name='low') - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertListEqual([1.0, 0.5, 0.0], - sess.run([update_op0, update_op1, update_op2])) + self.evaluate([update_op0, update_op1, update_op2])) - pcnt0, pcnt1, pcnt2 = sess.run([pcnt0, pcnt1, pcnt2]) + pcnt0, pcnt1, pcnt2 = self.evaluate([pcnt0, pcnt1, pcnt2]) self.assertAlmostEqual(1.0, pcnt0, 5) self.assertAlmostEqual(0.5, pcnt1, 5) self.assertAlmostEqual(0.0, pcnt2, 5) @@ -3398,6 +3574,7 @@ class MeanIOUTest(test.TestCase): np.random.seed(1) ops.reset_default_graph() + @test_util.run_deprecated_v1 def testVars(self): metrics.mean_iou( predictions=array_ops.ones([10, 1]), @@ -3405,6 +3582,7 @@ class MeanIOUTest(test.TestCase): num_classes=2) _assert_metric_variables(self, ('mean_iou/total_confusion_matrix:0',)) + @test_util.run_deprecated_v1 def testMetricsCollections(self): my_collection_name = '__metrics__' mean_iou, _ = metrics.mean_iou( @@ -3414,6 +3592,7 @@ class MeanIOUTest(test.TestCase): metrics_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [mean_iou]) + @test_util.run_deprecated_v1 def testUpdatesCollection(self): my_collection_name = '__updates__' _, update_op = metrics.mean_iou( @@ -3423,12 +3602,14 @@ class MeanIOUTest(test.TestCase): updates_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [update_op]) + @test_util.run_deprecated_v1 def testPredictionsAndLabelsOfDifferentSizeRaisesValueError(self): predictions = array_ops.ones([10, 3]) labels = array_ops.ones([10, 4]) with self.assertRaises(ValueError): metrics.mean_iou(labels, predictions, num_classes=2) + @test_util.run_deprecated_v1 def testLabelsAndWeightsOfDifferentSizeRaisesValueError(self): predictions = array_ops.ones([10]) labels = array_ops.ones([10]) @@ -3436,6 +3617,7 @@ class MeanIOUTest(test.TestCase): with self.assertRaises(ValueError): metrics.mean_iou(labels, predictions, num_classes=2, weights=weights) + @test_util.run_deprecated_v1 def testValueTensorIsIdempotent(self): num_classes = 3 predictions = random_ops.random_uniform( @@ -3446,17 +3628,18 @@ class MeanIOUTest(test.TestCase): labels, predictions, num_classes=num_classes) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) # Run several updates. for _ in range(10): - sess.run(update_op) + self.evaluate(update_op) # Then verify idempotency. initial_mean_iou = mean_iou.eval() for _ in range(10): self.assertEqual(initial_mean_iou, mean_iou.eval()) + @test_util.run_deprecated_v1 def testMultipleUpdates(self): num_classes = 3 with self.cached_session() as sess: @@ -3482,12 +3665,13 @@ class MeanIOUTest(test.TestCase): miou, update_op = metrics.mean_iou(labels, predictions, num_classes) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) for _ in range(5): - sess.run(update_op) + self.evaluate(update_op) desired_output = np.mean([1.0 / 2.0, 1.0 / 4.0, 0.]) self.assertEqual(desired_output, miou.eval()) + @test_util.run_deprecated_v1 def testMultipleUpdatesWithWeights(self): num_classes = 2 with self.cached_session() as sess: @@ -3529,10 +3713,11 @@ class MeanIOUTest(test.TestCase): variables.local_variables_initializer().run() for _ in range(6): - sess.run(update_op) + self.evaluate(update_op) desired_output = np.mean([2.0 / 3.0, 1.0 / 2.0]) self.assertAlmostEqual(desired_output, mean_iou.eval()) + @test_util.run_deprecated_v1 def testMultipleUpdatesWithMissingClass(self): # Test the case where there are no predicions and labels for # one class, and thus there is one row and one column with @@ -3563,12 +3748,13 @@ class MeanIOUTest(test.TestCase): miou, update_op = metrics.mean_iou(labels, predictions, num_classes) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) for _ in range(5): - sess.run(update_op) + self.evaluate(update_op) desired_output = np.mean([1.0 / 3.0, 2.0 / 4.0]) self.assertAlmostEqual(desired_output, miou.eval()) + @test_util.run_deprecated_v1 def testUpdateOpEvalIsAccumulatedConfusionMatrix(self): predictions = array_ops.concat( [ @@ -3587,32 +3773,35 @@ class MeanIOUTest(test.TestCase): num_classes = 2 with self.cached_session() as sess: miou, update_op = metrics.mean_iou(labels, predictions, num_classes) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) confusion_matrix = update_op.eval() self.assertAllEqual([[3, 0], [2, 5]], confusion_matrix) desired_miou = np.mean([3. / 5., 5. / 7.]) self.assertAlmostEqual(desired_miou, miou.eval()) + @test_util.run_deprecated_v1 def testAllCorrect(self): predictions = array_ops.zeros([40]) labels = array_ops.zeros([40]) num_classes = 1 with self.cached_session() as sess: miou, update_op = metrics.mean_iou(labels, predictions, num_classes) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertEqual(40, update_op.eval()[0]) self.assertEqual(1.0, miou.eval()) + @test_util.run_deprecated_v1 def testAllWrong(self): predictions = array_ops.zeros([40]) labels = array_ops.ones([40]) num_classes = 2 with self.cached_session() as sess: miou, update_op = metrics.mean_iou(labels, predictions, num_classes) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertAllEqual([[0, 0], [40, 0]], update_op.eval()) self.assertEqual(0., miou.eval()) + @test_util.run_deprecated_v1 def testResultsWithSomeMissing(self): predictions = array_ops.concat( [ @@ -3640,11 +3829,12 @@ class MeanIOUTest(test.TestCase): with self.cached_session() as sess: miou, update_op = metrics.mean_iou( labels, predictions, num_classes, weights=weights) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertAllEqual([[2, 0], [2, 4]], update_op.eval()) desired_miou = np.mean([2. / 4., 4. / 6.]) self.assertAlmostEqual(desired_miou, miou.eval()) + @test_util.run_deprecated_v1 def testMissingClassInLabels(self): labels = constant_op.constant([ [[0, 0, 1, 1, 0, 0], @@ -3659,22 +3849,24 @@ class MeanIOUTest(test.TestCase): num_classes = 3 with self.cached_session() as sess: miou, update_op = metrics.mean_iou(labels, predictions, num_classes) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertAllEqual([[7, 4, 3], [3, 5, 2], [0, 0, 0]], update_op.eval()) self.assertAlmostEqual( 1 / 3 * (7 / (7 + 3 + 7) + 5 / (5 + 4 + 5) + 0 / (0 + 5 + 0)), miou.eval()) + @test_util.run_deprecated_v1 def testMissingClassOverallSmall(self): labels = constant_op.constant([0]) predictions = constant_op.constant([0]) num_classes = 2 with self.cached_session() as sess: miou, update_op = metrics.mean_iou(labels, predictions, num_classes) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertAllEqual([[1, 0], [0, 0]], update_op.eval()) self.assertAlmostEqual(1, miou.eval()) + @test_util.run_deprecated_v1 def testMissingClassOverallLarge(self): labels = constant_op.constant([ [[0, 0, 1, 1, 0, 0], @@ -3689,7 +3881,7 @@ class MeanIOUTest(test.TestCase): num_classes = 3 with self.cached_session() as sess: miou, update_op = metrics.mean_iou(labels, predictions, num_classes) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertAllEqual([[9, 5, 0], [3, 7, 0], [0, 0, 0]], update_op.eval()) self.assertAlmostEqual( 1 / 2 * (9 / (9 + 3 + 5) + 7 / (7 + 5 + 3)), miou.eval()) @@ -3701,6 +3893,7 @@ class MeanPerClassAccuracyTest(test.TestCase): np.random.seed(1) ops.reset_default_graph() + @test_util.run_deprecated_v1 def testVars(self): metrics.mean_per_class_accuracy( predictions=array_ops.ones([10, 1]), @@ -3709,6 +3902,7 @@ class MeanPerClassAccuracyTest(test.TestCase): _assert_metric_variables(self, ('mean_accuracy/count:0', 'mean_accuracy/total:0')) + @test_util.run_deprecated_v1 def testMetricsCollections(self): my_collection_name = '__metrics__' mean_accuracy, _ = metrics.mean_per_class_accuracy( @@ -3719,6 +3913,7 @@ class MeanPerClassAccuracyTest(test.TestCase): self.assertListEqual( ops.get_collection(my_collection_name), [mean_accuracy]) + @test_util.run_deprecated_v1 def testUpdatesCollection(self): my_collection_name = '__updates__' _, update_op = metrics.mean_per_class_accuracy( @@ -3728,12 +3923,14 @@ class MeanPerClassAccuracyTest(test.TestCase): updates_collections=[my_collection_name]) self.assertListEqual(ops.get_collection(my_collection_name), [update_op]) + @test_util.run_deprecated_v1 def testPredictionsAndLabelsOfDifferentSizeRaisesValueError(self): predictions = array_ops.ones([10, 3]) labels = array_ops.ones([10, 4]) with self.assertRaises(ValueError): metrics.mean_per_class_accuracy(labels, predictions, num_classes=2) + @test_util.run_deprecated_v1 def testLabelsAndWeightsOfDifferentSizeRaisesValueError(self): predictions = array_ops.ones([10]) labels = array_ops.ones([10]) @@ -3742,6 +3939,7 @@ class MeanPerClassAccuracyTest(test.TestCase): metrics.mean_per_class_accuracy( labels, predictions, num_classes=2, weights=weights) + @test_util.run_deprecated_v1 def testValueTensorIsIdempotent(self): num_classes = 3 predictions = random_ops.random_uniform( @@ -3752,11 +3950,11 @@ class MeanPerClassAccuracyTest(test.TestCase): labels, predictions, num_classes=num_classes) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) # Run several updates. for _ in range(10): - sess.run(update_op) + self.evaluate(update_op) # Then verify idempotency. initial_mean_accuracy = mean_accuracy.eval() @@ -3788,12 +3986,13 @@ class MeanPerClassAccuracyTest(test.TestCase): mean_accuracy, update_op = metrics.mean_per_class_accuracy( labels, predictions, num_classes) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) for _ in range(5): - sess.run(update_op) + self.evaluate(update_op) desired_output = np.mean([1.0, 1.0 / 3.0, 0.0]) self.assertAlmostEqual(desired_output, mean_accuracy.eval()) + @test_util.run_deprecated_v1 def testMultipleUpdatesWithWeights(self): num_classes = 2 with self.cached_session() as sess: @@ -3835,10 +4034,11 @@ class MeanPerClassAccuracyTest(test.TestCase): variables.local_variables_initializer().run() for _ in range(6): - sess.run(update_op) + self.evaluate(update_op) desired_output = np.mean([2.0 / 2.0, 0.5 / 1.5]) self.assertAlmostEqual(desired_output, mean_accuracy.eval()) + @test_util.run_deprecated_v1 def testMultipleUpdatesWithMissingClass(self): # Test the case where there are no predicions and labels for # one class, and thus there is one row and one column with @@ -3870,12 +4070,13 @@ class MeanPerClassAccuracyTest(test.TestCase): mean_accuracy, update_op = metrics.mean_per_class_accuracy( labels, predictions, num_classes) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) for _ in range(5): - sess.run(update_op) + self.evaluate(update_op) desired_output = np.mean([1.0 / 2.0, 2.0 / 3.0, 0.]) self.assertAlmostEqual(desired_output, mean_accuracy.eval()) + @test_util.run_deprecated_v1 def testAllCorrect(self): predictions = array_ops.zeros([40]) labels = array_ops.zeros([40]) @@ -3883,10 +4084,11 @@ class MeanPerClassAccuracyTest(test.TestCase): with self.cached_session() as sess: mean_accuracy, update_op = metrics.mean_per_class_accuracy( labels, predictions, num_classes) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertEqual(1.0, update_op.eval()[0]) self.assertEqual(1.0, mean_accuracy.eval()) + @test_util.run_deprecated_v1 def testAllWrong(self): predictions = array_ops.zeros([40]) labels = array_ops.ones([40]) @@ -3894,10 +4096,11 @@ class MeanPerClassAccuracyTest(test.TestCase): with self.cached_session() as sess: mean_accuracy, update_op = metrics.mean_per_class_accuracy( labels, predictions, num_classes) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertAllEqual([0.0, 0.0], update_op.eval()) self.assertEqual(0., mean_accuracy.eval()) + @test_util.run_deprecated_v1 def testResultsWithSomeMissing(self): predictions = array_ops.concat([ constant_op.constant(0, shape=[5]), constant_op.constant(1, shape=[5]) @@ -3913,7 +4116,7 @@ class MeanPerClassAccuracyTest(test.TestCase): with self.cached_session() as sess: mean_accuracy, update_op = metrics.mean_per_class_accuracy( labels, predictions, num_classes, weights=weights) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) desired_accuracy = np.array([2. / 2., 4. / 6.], dtype=np.float32) self.assertAllEqual(desired_accuracy, update_op.eval()) desired_mean_accuracy = np.mean(desired_accuracy) @@ -3926,12 +4129,14 @@ class FalseNegativesTest(test.TestCase): np.random.seed(1) ops.reset_default_graph() + @test_util.run_deprecated_v1 def testVars(self): metrics.false_negatives( labels=(0, 1, 0, 1), predictions=(0, 0, 1, 1)) _assert_metric_variables(self, ('false_negatives/count:0',)) + @test_util.run_deprecated_v1 def testUnweighted(self): labels = constant_op.constant(((0, 1, 0, 1, 0), (0, 0, 1, 1, 1), @@ -3945,11 +4150,12 @@ class FalseNegativesTest(test.TestCase): labels=labels, predictions=predictions) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertAllClose(0., tn.eval()) self.assertAllClose(3., tn_update_op.eval()) self.assertAllClose(3., tn.eval()) + @test_util.run_deprecated_v1 def testWeighted(self): labels = constant_op.constant(((0, 1, 0, 1, 0), (0, 0, 1, 1, 1), @@ -3964,7 +4170,7 @@ class FalseNegativesTest(test.TestCase): labels=labels, predictions=predictions, weights=weights) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertAllClose(0., tn.eval()) self.assertAllClose(5., tn_update_op.eval()) self.assertAllClose(5., tn.eval()) @@ -3976,6 +4182,7 @@ class FalseNegativesAtThresholdsTest(test.TestCase): np.random.seed(1) ops.reset_default_graph() + @test_util.run_deprecated_v1 def testVars(self): metrics.false_negatives_at_thresholds( predictions=array_ops.ones((10, 1)), @@ -3983,6 +4190,7 @@ class FalseNegativesAtThresholdsTest(test.TestCase): thresholds=[0.15, 0.5, 0.85]) _assert_metric_variables(self, ('false_negatives/false_negatives:0',)) + @test_util.run_deprecated_v1 def testUnweighted(self): predictions = constant_op.constant(((0.9, 0.2, 0.8, 0.1), (0.2, 0.9, 0.7, 0.6), @@ -3994,11 +4202,12 @@ class FalseNegativesAtThresholdsTest(test.TestCase): predictions=predictions, labels=labels, thresholds=[0.15, 0.5, 0.85]) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertAllEqual((0, 0, 0), fn.eval()) self.assertAllEqual((0, 2, 3), fn_update_op.eval()) self.assertAllEqual((0, 2, 3), fn.eval()) + @test_util.run_deprecated_v1 def testWeighted(self): predictions = constant_op.constant(((0.9, 0.2, 0.8, 0.1), (0.2, 0.9, 0.7, 0.6), @@ -4013,7 +4222,7 @@ class FalseNegativesAtThresholdsTest(test.TestCase): thresholds=[0.15, 0.5, 0.85]) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertAllEqual((0.0, 0.0, 0.0), fn.eval()) self.assertAllEqual((0.0, 8.0, 11.0), fn_update_op.eval()) self.assertAllEqual((0.0, 8.0, 11.0), fn.eval()) @@ -4025,12 +4234,14 @@ class FalsePositivesTest(test.TestCase): np.random.seed(1) ops.reset_default_graph() + @test_util.run_deprecated_v1 def testVars(self): metrics.false_positives( labels=(0, 1, 0, 1), predictions=(0, 0, 1, 1)) _assert_metric_variables(self, ('false_positives/count:0',)) + @test_util.run_deprecated_v1 def testUnweighted(self): labels = constant_op.constant(((0, 1, 0, 1, 0), (0, 0, 1, 1, 1), @@ -4044,11 +4255,12 @@ class FalsePositivesTest(test.TestCase): labels=labels, predictions=predictions) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertAllClose(0., tn.eval()) self.assertAllClose(7., tn_update_op.eval()) self.assertAllClose(7., tn.eval()) + @test_util.run_deprecated_v1 def testWeighted(self): labels = constant_op.constant(((0, 1, 0, 1, 0), (0, 0, 1, 1, 1), @@ -4063,7 +4275,7 @@ class FalsePositivesTest(test.TestCase): labels=labels, predictions=predictions, weights=weights) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertAllClose(0., tn.eval()) self.assertAllClose(14., tn_update_op.eval()) self.assertAllClose(14., tn.eval()) @@ -4075,6 +4287,7 @@ class FalsePositivesAtThresholdsTest(test.TestCase): np.random.seed(1) ops.reset_default_graph() + @test_util.run_deprecated_v1 def testVars(self): metrics.false_positives_at_thresholds( predictions=array_ops.ones((10, 1)), @@ -4082,6 +4295,7 @@ class FalsePositivesAtThresholdsTest(test.TestCase): thresholds=[0.15, 0.5, 0.85]) _assert_metric_variables(self, ('false_positives/false_positives:0',)) + @test_util.run_deprecated_v1 def testUnweighted(self): predictions = constant_op.constant(((0.9, 0.2, 0.8, 0.1), (0.2, 0.9, 0.7, 0.6), @@ -4093,11 +4307,12 @@ class FalsePositivesAtThresholdsTest(test.TestCase): predictions=predictions, labels=labels, thresholds=[0.15, 0.5, 0.85]) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertAllEqual((0, 0, 0), fp.eval()) self.assertAllEqual((7, 4, 2), fp_update_op.eval()) self.assertAllEqual((7, 4, 2), fp.eval()) + @test_util.run_deprecated_v1 def testWeighted(self): predictions = constant_op.constant(((0.9, 0.2, 0.8, 0.1), (0.2, 0.9, 0.7, 0.6), @@ -4114,7 +4329,7 @@ class FalsePositivesAtThresholdsTest(test.TestCase): thresholds=[0.15, 0.5, 0.85]) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertAllEqual((0.0, 0.0, 0.0), fp.eval()) self.assertAllEqual((125.0, 42.0, 12.0), fp_update_op.eval()) self.assertAllEqual((125.0, 42.0, 12.0), fp.eval()) @@ -4126,12 +4341,14 @@ class TrueNegativesTest(test.TestCase): np.random.seed(1) ops.reset_default_graph() + @test_util.run_deprecated_v1 def testVars(self): metrics.true_negatives( labels=(0, 1, 0, 1), predictions=(0, 0, 1, 1)) _assert_metric_variables(self, ('true_negatives/count:0',)) + @test_util.run_deprecated_v1 def testUnweighted(self): labels = constant_op.constant(((0, 1, 0, 1, 0), (0, 0, 1, 1, 1), @@ -4145,11 +4362,12 @@ class TrueNegativesTest(test.TestCase): labels=labels, predictions=predictions) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertAllClose(0., tn.eval()) self.assertAllClose(3., tn_update_op.eval()) self.assertAllClose(3., tn.eval()) + @test_util.run_deprecated_v1 def testWeighted(self): labels = constant_op.constant(((0, 1, 0, 1, 0), (0, 0, 1, 1, 1), @@ -4164,7 +4382,7 @@ class TrueNegativesTest(test.TestCase): labels=labels, predictions=predictions, weights=weights) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertAllClose(0., tn.eval()) self.assertAllClose(4., tn_update_op.eval()) self.assertAllClose(4., tn.eval()) @@ -4176,6 +4394,7 @@ class TrueNegativesAtThresholdsTest(test.TestCase): np.random.seed(1) ops.reset_default_graph() + @test_util.run_deprecated_v1 def testVars(self): metrics.true_negatives_at_thresholds( predictions=array_ops.ones((10, 1)), @@ -4183,6 +4402,7 @@ class TrueNegativesAtThresholdsTest(test.TestCase): thresholds=[0.15, 0.5, 0.85]) _assert_metric_variables(self, ('true_negatives/true_negatives:0',)) + @test_util.run_deprecated_v1 def testUnweighted(self): predictions = constant_op.constant(((0.9, 0.2, 0.8, 0.1), (0.2, 0.9, 0.7, 0.6), @@ -4194,11 +4414,12 @@ class TrueNegativesAtThresholdsTest(test.TestCase): predictions=predictions, labels=labels, thresholds=[0.15, 0.5, 0.85]) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertAllEqual((0, 0, 0), tn.eval()) self.assertAllEqual((2, 5, 7), tn_update_op.eval()) self.assertAllEqual((2, 5, 7), tn.eval()) + @test_util.run_deprecated_v1 def testWeighted(self): predictions = constant_op.constant(((0.9, 0.2, 0.8, 0.1), (0.2, 0.9, 0.7, 0.6), @@ -4213,7 +4434,7 @@ class TrueNegativesAtThresholdsTest(test.TestCase): thresholds=[0.15, 0.5, 0.85]) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertAllEqual((0.0, 0.0, 0.0), tn.eval()) self.assertAllEqual((5.0, 15.0, 23.0), tn_update_op.eval()) self.assertAllEqual((5.0, 15.0, 23.0), tn.eval()) @@ -4225,12 +4446,14 @@ class TruePositivesTest(test.TestCase): np.random.seed(1) ops.reset_default_graph() + @test_util.run_deprecated_v1 def testVars(self): metrics.true_positives( labels=(0, 1, 0, 1), predictions=(0, 0, 1, 1)) _assert_metric_variables(self, ('true_positives/count:0',)) + @test_util.run_deprecated_v1 def testUnweighted(self): labels = constant_op.constant(((0, 1, 0, 1, 0), (0, 0, 1, 1, 1), @@ -4244,11 +4467,12 @@ class TruePositivesTest(test.TestCase): labels=labels, predictions=predictions) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertAllClose(0., tn.eval()) self.assertAllClose(7., tn_update_op.eval()) self.assertAllClose(7., tn.eval()) + @test_util.run_deprecated_v1 def testWeighted(self): labels = constant_op.constant(((0, 1, 0, 1, 0), (0, 0, 1, 1, 1), @@ -4263,7 +4487,7 @@ class TruePositivesTest(test.TestCase): labels=labels, predictions=predictions, weights=weights) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertAllClose(0., tn.eval()) self.assertAllClose(12., tn_update_op.eval()) self.assertAllClose(12., tn.eval()) @@ -4275,6 +4499,7 @@ class TruePositivesAtThresholdsTest(test.TestCase): np.random.seed(1) ops.reset_default_graph() + @test_util.run_deprecated_v1 def testVars(self): metrics.true_positives_at_thresholds( predictions=array_ops.ones((10, 1)), @@ -4282,6 +4507,7 @@ class TruePositivesAtThresholdsTest(test.TestCase): thresholds=[0.15, 0.5, 0.85]) _assert_metric_variables(self, ('true_positives/true_positives:0',)) + @test_util.run_deprecated_v1 def testUnweighted(self): predictions = constant_op.constant(((0.9, 0.2, 0.8, 0.1), (0.2, 0.9, 0.7, 0.6), @@ -4293,11 +4519,12 @@ class TruePositivesAtThresholdsTest(test.TestCase): predictions=predictions, labels=labels, thresholds=[0.15, 0.5, 0.85]) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertAllEqual((0, 0, 0), tp.eval()) self.assertAllEqual((3, 1, 0), tp_update_op.eval()) self.assertAllEqual((3, 1, 0), tp.eval()) + @test_util.run_deprecated_v1 def testWeighted(self): predictions = constant_op.constant(((0.9, 0.2, 0.8, 0.1), (0.2, 0.9, 0.7, 0.6), @@ -4310,7 +4537,7 @@ class TruePositivesAtThresholdsTest(test.TestCase): thresholds=[0.15, 0.5, 0.85]) with self.cached_session() as sess: - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) self.assertAllEqual((0.0, 0.0, 0.0), tp.eval()) self.assertAllEqual((111.0, 37.0, 0.0), tp_update_op.eval()) self.assertAllEqual((111.0, 37.0, 0.0), tp.eval()) diff --git a/tensorflow/python/kernel_tests/morphological_ops_test.py b/tensorflow/python/kernel_tests/morphological_ops_test.py index 6d601554b8..f54aaf30d0 100644 --- a/tensorflow/python/kernel_tests/morphological_ops_test.py +++ b/tensorflow/python/kernel_tests/morphological_ops_test.py @@ -21,6 +21,7 @@ from __future__ import print_function import numpy as np from tensorflow.python.framework import constant_op +from tensorflow.python.framework import test_util from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import nn_ops import tensorflow.python.ops.nn_grad # pylint: disable=unused-import @@ -52,7 +53,7 @@ class DilationTest(test.TestCase): rates=rates, padding=padding, name="dilation2d") - self.assertAllClose(out, out_tensor.eval()) + self.assertAllClose(out, self.evaluate(out_tensor)) def _testDilationValidPadding(self, use_gpu): # [1, 2, 2, 1] @@ -216,7 +217,7 @@ class DilationTest(test.TestCase): rates=rates, padding=padding, name="dilation2d") - out_shape = out_tensor.eval().shape + out_shape = self.evaluate(out_tensor).shape # Small delta is necessary for argmax to remain the same. err = gradient_checker.compute_gradient_error( @@ -291,6 +292,7 @@ class DilationTest(test.TestCase): padding="SAME", use_gpu=use_gpu) + @test_util.run_deprecated_v1 def testDilationGrad(self): for use_gpu in True, False: self._testDilationGradValidPadding_1x1x1(use_gpu) @@ -327,7 +329,7 @@ class ErosionTest(test.TestCase): rates=rates, padding=padding, name="erosion2d") - self.assertAllClose(out, out_tensor.eval()) + self.assertAllClose(out, self.evaluate(out_tensor)) def _testErosionValidPadding(self, use_gpu): # [1, 2, 2, 1] @@ -491,7 +493,7 @@ class ErosionTest(test.TestCase): rates=rates, padding=padding, name="erosion2d") - out_shape = out_tensor.eval().shape + out_shape = self.evaluate(out_tensor).shape # Small delta is necessary for argmax to remain the same. err = gradient_checker.compute_gradient_error( @@ -566,6 +568,7 @@ class ErosionTest(test.TestCase): padding="SAME", use_gpu=use_gpu) + @test_util.run_deprecated_v1 def testErosionGrad(self): for use_gpu in True, False: self._testErosionGradValidPadding_1x1x1(use_gpu) diff --git a/tensorflow/python/kernel_tests/neon_depthwise_conv_op_test.py b/tensorflow/python/kernel_tests/neon_depthwise_conv_op_test.py index 15e3826542..380d2860da 100644 --- a/tensorflow/python/kernel_tests/neon_depthwise_conv_op_test.py +++ b/tensorflow/python/kernel_tests/neon_depthwise_conv_op_test.py @@ -21,6 +21,7 @@ from __future__ import print_function import numpy as np from tensorflow.python.framework import constant_op +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import nn_impl from tensorflow.python.ops import nn_ops @@ -142,8 +143,8 @@ class DepthwiseConv2DTest(test.TestCase): conv_interface = nn_impl.depthwise_conv2d( t1, t2, strides=[1, stride, stride, 1], padding=padding) - native_result = sess.run(conv_native) - interface_result = sess.run(conv_interface) + native_result = self.evaluate(conv_native) + interface_result = self.evaluate(conv_interface) print("depthwise conv_2d: ", tensor_in_sizes, "*", filter_in_sizes, ", stride:", stride, ", padding: ", padding, ", max diff: ", @@ -153,6 +154,7 @@ class DepthwiseConv2DTest(test.TestCase): self.assertShapeEqual(native_result, conv_native) self.assertShapeEqual(native_result, conv_interface) + @test_util.run_deprecated_v1 def testDepthwiseConv2D(self): for index, (input_size, filter_size, _, stride, padding) in enumerate(ConfigsToTest()): @@ -211,11 +213,12 @@ class DepthwiseConv2DTest(test.TestCase): t2 = constant_op.constant(x2, shape=filter_in_sizes) conv = nn_ops.depthwise_conv2d_native( t1, t2, strides=[1, stride, stride, 1], padding=padding) - value = sess.run(conv) + value = self.evaluate(conv) print("value = ", value) self.assertAllClose(expected, np.ravel(value), 1e-5) self.assertShapeEqual(value, conv) + @test_util.run_deprecated_v1 def testConv2D2x2Filter(self): # The inputs look like this (it's a 3 x 2 matrix, each of depth 2): # diff --git a/tensorflow/python/kernel_tests/norm_op_test.py b/tensorflow/python/kernel_tests/norm_op_test.py index e202b6e8a4..5ff0c58bf1 100644 --- a/tensorflow/python/kernel_tests/norm_op_test.py +++ b/tensorflow/python/kernel_tests/norm_op_test.py @@ -70,7 +70,7 @@ def _GetNormOpTest(dtype_, shape_, ord_, axis_, keep_dims_, use_static_shape_): tf_matrix = constant_op.constant(matrix) tf_norm = linalg_ops.norm( tf_matrix, ord=ord_, axis=axis_, keepdims=keep_dims_) - tf_norm_val = sess.run(tf_norm) + tf_norm_val = self.evaluate(tf_norm) else: tf_matrix = array_ops.placeholder(dtype_) tf_norm = linalg_ops.norm( diff --git a/tensorflow/python/kernel_tests/nth_element_op_test.py b/tensorflow/python/kernel_tests/nth_element_op_test.py index 338b6cec01..4be78b2d5c 100644 --- a/tensorflow/python/kernel_tests/nth_element_op_test.py +++ b/tensorflow/python/kernel_tests/nth_element_op_test.py @@ -22,6 +22,7 @@ import numpy as np import tensorflow.python.ops.nn_grad # pylint: disable=unused-import from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import nn_ops from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradients_impl @@ -35,7 +36,7 @@ class NthElementTest(test.TestCase): with self.cached_session(use_gpu=False) as sess: inputs_op = ops.convert_to_tensor(inputs, dtype=dtype) values_op = nn_ops.nth_element(inputs_op, n, reverse=reverse) - values = sess.run(values_op) + values = self.evaluate(values_op) self.assertShapeEqual(np_expected_values, values_op) self.assertAllClose(np_expected_values, values) @@ -111,17 +112,20 @@ class NthElementTest(test.TestCase): self._testEnumerateN([10, 10, 10]) self._testEnumerateN([10, 10, 10, 10]) + @test_util.run_deprecated_v1 def testInvalidInput(self): with self.assertRaisesRegexp(ValueError, "at least rank 1 but is rank 0"): nn_ops.nth_element(5, 0) + @test_util.run_deprecated_v1 def testInvalidInputAtEval(self): with self.session(use_gpu=False): v = array_ops.placeholder(dtype=dtypes.float32) with self.assertRaisesOpError("Input must be >= 1-D"): nn_ops.nth_element(v, 0).eval(feed_dict={v: 5.0}) + @test_util.run_deprecated_v1 def testInvalidN(self): with self.assertRaisesRegexp(ValueError, "non-negative but is -1"): @@ -130,6 +134,7 @@ class NthElementTest(test.TestCase): "scalar but has rank 1"): nn_ops.nth_element([5, 6, 3], [1]) + @test_util.run_deprecated_v1 def testInvalidNAtEval(self): inputs = [[0.1, 0.2], [0.3, 0.4]] with self.session(use_gpu=False): @@ -138,12 +143,14 @@ class NthElementTest(test.TestCase): with self.assertRaisesOpError("Need n >= 0, got -7"): values.eval(feed_dict={n: -7}) + @test_util.run_deprecated_v1 def testNTooLarge(self): inputs = [[0.1, 0.2], [0.3, 0.4]] with self.assertRaisesRegexp(ValueError, "must have last dimension > n = 2"): nn_ops.nth_element(inputs, 2) + @test_util.run_deprecated_v1 def testNTooLargeAtEval(self): inputs = [[0.1, 0.2], [0.3, 0.4]] with self.session(use_gpu=False): @@ -152,6 +159,7 @@ class NthElementTest(test.TestCase): with self.assertRaisesOpError(r"Input must have at least n\+1 columns"): values.eval(feed_dict={n: 2}) + @test_util.run_deprecated_v1 def testGradients(self): with self.session(use_gpu=False) as sess: inputs = array_ops.placeholder(dtypes.float32, shape=[3, 5]) diff --git a/tensorflow/python/kernel_tests/numerics_test.py b/tensorflow/python/kernel_tests/numerics_test.py index 5db591ed30..5751f3fe76 100644 --- a/tensorflow/python/kernel_tests/numerics_test.py +++ b/tensorflow/python/kernel_tests/numerics_test.py @@ -23,6 +23,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import math_ops @@ -35,11 +36,11 @@ class VerifyTensorAllFiniteTest(test.TestCase): def testVerifyTensorAllFiniteSucceeds(self): x_shape = [5, 4] x = np.random.random_sample(x_shape).astype(np.float32) - with self.session(use_gpu=True): + with test_util.use_gpu(): t = constant_op.constant(x, shape=x_shape, dtype=dtypes.float32) t_verified = numerics.verify_tensor_all_finite(t, "Input is not a number.") - self.assertAllClose(x, t_verified.eval()) + self.assertAllClose(x, self.evaluate(t_verified)) def testVerifyTensorAllFiniteFails(self): x_shape = [5, 4] @@ -48,23 +49,24 @@ class VerifyTensorAllFiniteTest(test.TestCase): # Test NaN. x[0] = np.nan - with self.session(use_gpu=True): + with test_util.use_gpu(): with self.assertRaisesOpError(my_msg): t = constant_op.constant(x, shape=x_shape, dtype=dtypes.float32) t_verified = numerics.verify_tensor_all_finite(t, my_msg) - t_verified.eval() + self.evaluate(t_verified) # Test Inf. x[0] = np.inf - with self.session(use_gpu=True): + with test_util.use_gpu(): with self.assertRaisesOpError(my_msg): t = constant_op.constant(x, shape=x_shape, dtype=dtypes.float32) t_verified = numerics.verify_tensor_all_finite(t, my_msg) - t_verified.eval() + self.evaluate(t_verified) class NumericsTest(test.TestCase): + @test_util.run_deprecated_v1 def testInf(self): with self.session(graph=ops.Graph()): t1 = constant_op.constant(1.0) @@ -73,8 +75,9 @@ class NumericsTest(test.TestCase): check = numerics.add_check_numerics_ops() a = control_flow_ops.with_dependencies([check], a) with self.assertRaisesOpError("Inf"): - a.eval() + self.evaluate(a) + @test_util.run_deprecated_v1 def testNaN(self): with self.session(graph=ops.Graph()): t1 = constant_op.constant(0.0) @@ -83,8 +86,9 @@ class NumericsTest(test.TestCase): check = numerics.add_check_numerics_ops() a = control_flow_ops.with_dependencies([check], a) with self.assertRaisesOpError("NaN"): - a.eval() + self.evaluate(a) + @test_util.run_deprecated_v1 def testBoth(self): with self.session(graph=ops.Graph()): t1 = constant_op.constant([1.0, 0.0]) @@ -93,16 +97,17 @@ class NumericsTest(test.TestCase): check = numerics.add_check_numerics_ops() a = control_flow_ops.with_dependencies([check], a) with self.assertRaisesOpError("Inf and NaN"): - a.eval() + self.evaluate(a) def testPassThrough(self): with self.session(graph=ops.Graph()): t1 = constant_op.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3]) checked = array_ops.check_numerics(t1, message="pass through test") - value = checked.eval() + value = self.evaluate(checked) self.assertAllEqual(np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]), value) self.assertEqual([2, 3], checked.get_shape()) + @test_util.run_deprecated_v1 def testControlFlowCond(self): predicate = array_ops.placeholder(dtypes.bool, shape=[]) _ = control_flow_ops.cond(predicate, @@ -115,6 +120,7 @@ class NumericsTest(test.TestCase): r"or `tf.while_loop\(\)`\."): numerics.add_check_numerics_ops() + @test_util.run_deprecated_v1 def testControlFlowWhile(self): predicate = array_ops.placeholder(dtypes.bool, shape=[]) _ = control_flow_ops.while_loop(lambda _: predicate, diff --git a/tensorflow/python/kernel_tests/one_hot_op_test.py b/tensorflow/python/kernel_tests/one_hot_op_test.py index 377d545c9c..856ba7bb7f 100644 --- a/tensorflow/python/kernel_tests/one_hot_op_test.py +++ b/tensorflow/python/kernel_tests/one_hot_op_test.py @@ -41,12 +41,12 @@ class OneHotTest(test.TestCase): else: ans = array_ops.one_hot(**inputs) if expected_err_re is None: - tf_ans = ans.eval() + tf_ans = self.evaluate(ans) self.assertAllEqual(tf_ans, truth) self.assertEqual(tf_ans.shape, ans.get_shape()) else: with self.assertRaisesOpError(expected_err_re): - ans.eval() + self.evaluate(ans) def _testBothOneHot(self, truth, expected_err_re=None, raises=None, **inputs): self._testOneHot(truth, True, expected_err_re, raises, **inputs) diff --git a/tensorflow/python/kernel_tests/pad_op_test.py b/tensorflow/python/kernel_tests/pad_op_test.py index fc302c4141..7b1b054ae0 100644 --- a/tensorflow/python/kernel_tests/pad_op_test.py +++ b/tensorflow/python/kernel_tests/pad_op_test.py @@ -23,6 +23,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradient_checker from tensorflow.python.platform import test @@ -88,7 +89,7 @@ class PadOpTest(test.TestCase): with self.cached_session(use_gpu=True): tf_val = array_ops.pad(np_inputs, paddings, mode=mode, constant_values=constant_values) - out = tf_val.eval() + out = self.evaluate(tf_val) self.assertAllEqual(np_val, out) self.assertShapeEqual(np_val, tf_val) @@ -116,6 +117,7 @@ class PadOpTest(test.TestCase): self._testGradient(np_inputs, paddings, mode=mode, constant_values=constant_values) + @test_util.run_deprecated_v1 def testInputDims(self): with self.session(use_gpu=True): with self.assertRaises(ValueError): @@ -124,6 +126,7 @@ class PadOpTest(test.TestCase): array_ops.reshape( [1, 2], shape=[1, 2])) + @test_util.run_deprecated_v1 def testPaddingsDim(self): with self.session(use_gpu=True): with self.assertRaises(ValueError): @@ -132,6 +135,7 @@ class PadOpTest(test.TestCase): array_ops.reshape( [1, 2], shape=[2])) + @test_util.run_deprecated_v1 def testPaddingsDim2(self): with self.session(use_gpu=True): with self.assertRaises(ValueError): @@ -140,6 +144,7 @@ class PadOpTest(test.TestCase): array_ops.reshape( [1, 2], shape=[2, 1])) + @test_util.run_deprecated_v1 def testPaddingsDim3(self): with self.session(use_gpu=True): with self.assertRaises(ValueError): @@ -148,6 +153,7 @@ class PadOpTest(test.TestCase): array_ops.reshape( [1, 2], shape=[1, 2])) + @test_util.run_deprecated_v1 def testPaddingsDim4(self): with self.session(use_gpu=True): with self.assertRaises(ValueError): @@ -156,6 +162,7 @@ class PadOpTest(test.TestCase): array_ops.reshape( [1, 2, 3, 4, 5, 6], shape=[3, 2])) + @test_util.run_deprecated_v1 def testPaddingsNonNegative(self): with self.session(use_gpu=True): with self.assertRaisesRegexp(ValueError, "must be non-negative"): @@ -164,6 +171,7 @@ class PadOpTest(test.TestCase): constant_op.constant( [-1, 0], shape=[1, 2])) + @test_util.run_deprecated_v1 def testPaddingsNonNegative2(self): with self.session(use_gpu=True): with self.assertRaisesRegexp(ValueError, "must be non-negative"): @@ -208,7 +216,7 @@ class PadOpTest(test.TestCase): constant_op.constant(paddings, padding_dtype), mode=mode, constant_values=0) - out = tf_val.eval() + out = self.evaluate(tf_val) self.assertAllEqual(np_val, out) self.assertShapeEqual(np_val, tf_val) @@ -223,6 +231,7 @@ class PadOpTest(test.TestCase): np.random.randint(-100, 100, (4, 2, 1, 3)).astype(t), [[0, 0], [0, 0], [0, 0], [0, 0]], -123) + @test_util.run_deprecated_v1 def testFloatTypes(self): for t in [np.float32, np.float64]: self._testAll(np.random.rand(2, 5).astype(t), [[1, 0], [2, 0]], 0.0) @@ -250,17 +259,18 @@ class PadOpTest(test.TestCase): symmetric = array_ops.pad(x, [[1, 0], [0, 1]], mode="SYMMETRIC", constant_values="PAD") with self.session(use_gpu=True): - self.assertAllEqual([[b"PAD", b"PAD", b"PAD"], - [b"Hello", b"World", b"PAD"], - [b"Goodnight", b"Moon", b"PAD"]], constant.eval()) + self.assertAllEqual( + [[b"PAD", b"PAD", b"PAD"], [b"Hello", b"World", b"PAD"], + [b"Goodnight", b"Moon", b"PAD"]], self.evaluate(constant)) self.assertAllEqual([[b"Goodnight", b"Moon", b"Goodnight"], [b"Hello", b"World", b"Hello"], [b"Goodnight", b"Moon", b"Goodnight"]], - reflect.eval()) - self.assertAllEqual([[b"Hello", b"World", b"World"], - [b"Hello", b"World", b"World"], - [b"Goodnight", b"Moon", b"Moon"]], symmetric.eval()) + self.evaluate(reflect)) + self.assertAllEqual( + [[b"Hello", b"World", b"World"], [b"Hello", b"World", b"World"], + [b"Goodnight", b"Moon", b"Moon"]], self.evaluate(symmetric)) + @test_util.run_deprecated_v1 def testShapeFunctionEdgeCases(self): # Unknown paddings shape. inp = constant_op.constant(0.0, shape=[4, 4, 4, 4]) @@ -277,6 +287,7 @@ class PadOpTest(test.TestCase): padded = array_ops.pad(inp, array_ops.placeholder(dtypes.int32)) self.assertAllEqual(None, padded.get_shape().ndims) + @test_util.run_deprecated_v1 def testPartialShapeInformation(self): unknown = array_ops.placeholder(dtypes.int32) @@ -327,7 +338,7 @@ class PadOpTest(test.TestCase): inp = np.asarray(7) with self.session(use_gpu=True): tf_val = array_ops.pad(inp, paddings) - out = tf_val.eval() + out = self.evaluate(tf_val) self.assertAllEqual(inp, out) self.assertShapeEqual(inp, tf_val) @@ -337,10 +348,11 @@ class PadOpTest(test.TestCase): inp = np.asarray(7) with self.cached_session(use_gpu=True): tf_val = array_ops.pad(inp, constant_op.constant(paddings, dtype=dtype)) - out = tf_val.eval() + out = self.evaluate(tf_val) self.assertAllEqual(inp, out) self.assertShapeEqual(inp, tf_val) + @test_util.run_deprecated_v1 def testCollapseAdjacentNonPaddedDimensions(self): # pyformat: disable paddings_values = [[[0, 0], [0, 0], [0, 0], [0, 1]], @@ -361,11 +373,12 @@ class PadOpTest(test.TestCase): [paddings_value[i][0] + inp.shape.dims[i].value for i in range(4)], [-1, -1, -1, -1]) with self.cached_session(use_gpu=True): - self.assertAllEqual(inp.eval(), middle.eval()) + self.assertAllEqual(inp.eval(), self.evaluate(middle)) self.assertAllEqual( - np.zeros([row[0] for row in paddings_value]), left.eval()) + np.zeros([row[0] for row in paddings_value]), self.evaluate(left)) self.assertAllEqual( - np.zeros([row[1] for row in paddings_value]), right.eval()) + np.zeros([row[1] for row in paddings_value]), + self.evaluate(right)) if __name__ == "__main__": diff --git a/tensorflow/python/kernel_tests/padding_fifo_queue_test.py b/tensorflow/python/kernel_tests/padding_fifo_queue_test.py index 95f3dcceea..b4818360d5 100644 --- a/tensorflow/python/kernel_tests/padding_fifo_queue_test.py +++ b/tensorflow/python/kernel_tests/padding_fifo_queue_test.py @@ -126,7 +126,7 @@ class PaddingFIFOQueueTest(test.TestCase): # Run one producer thread for each element in elems. def enqueue(enqueue_op): - sess.run(enqueue_op) + self.evaluate(enqueue_op) threads = [ self.checkedThread( @@ -158,7 +158,7 @@ class PaddingFIFOQueueTest(test.TestCase): results = [] def dequeue(): - results.append(sess.run(dequeued_t)) + results.append(self.evaluate(dequeued_t)) threads = [self.checkedThread(target=dequeue) for _ in enqueue_ops] for thread in threads: @@ -178,7 +178,7 @@ class PaddingFIFOQueueTest(test.TestCase): enqueue_op.run() for i in xrange(len(elems)): - vals = dequeued_t.eval() + vals = self.evaluate(dequeued_t) self.assertEqual([elems[i]], vals) def testEnqueueAndBlockingDequeue(self): @@ -193,13 +193,13 @@ class PaddingFIFOQueueTest(test.TestCase): # TODO(mrry): Figure out how to do this without sleeping. time.sleep(0.1) for enqueue_op in enqueue_ops: - sess.run(enqueue_op) + self.evaluate(enqueue_op) results = [] def dequeue(): for _ in xrange(len(elems)): - results.append(sess.run(dequeued_t)) + results.append(self.evaluate(dequeued_t)) enqueue_thread = self.checkedThread(target=enqueue) dequeue_thread = self.checkedThread(target=dequeue) @@ -224,7 +224,7 @@ class PaddingFIFOQueueTest(test.TestCase): enqueue_op.run() for i in xrange(len(elems)): - x_val, y_val = sess.run(dequeued_t) + x_val, y_val = self.evaluate(dequeued_t) x, y = elems[i] self.assertEqual([x], x_val) self.assertEqual([y], y_val) @@ -243,9 +243,9 @@ class PaddingFIFOQueueTest(test.TestCase): self.assertEqual([], size.get_shape()) enqueue_op.run() - self.assertEqual(1, size.eval()) + self.assertEqual(1, self.evaluate(size)) dequeued_t.op.run() - self.assertEqual(0, size.eval()) + self.assertEqual(0, self.evaluate(size)) def testEnqueueMany(self): with self.cached_session(): @@ -257,7 +257,7 @@ class PaddingFIFOQueueTest(test.TestCase): enqueue_op.run() for i in range(8): - vals = dequeued_t.eval() + vals = self.evaluate(dequeued_t) self.assertEqual([elems[i % 4]], vals) def testEmptyEnqueueMany(self): @@ -269,9 +269,9 @@ class PaddingFIFOQueueTest(test.TestCase): enqueue_op = q.enqueue_many((empty_t,)) size_t = q.size() - self.assertEqual([0], size_t.eval()) + self.assertEqual([0], self.evaluate(size_t)) enqueue_op.run() - self.assertEqual([0], size_t.eval()) + self.assertEqual([0], self.evaluate(size_t)) def testEmptyDequeueMany(self): with self.cached_session(): @@ -279,9 +279,9 @@ class PaddingFIFOQueueTest(test.TestCase): enqueue_op = q.enqueue((10.0,)) dequeued_t = q.dequeue_many(0) - self.assertEqual([], dequeued_t.eval().tolist()) + self.assertEqual([], self.evaluate(dequeued_t).tolist()) enqueue_op.run() - self.assertEqual([], dequeued_t.eval().tolist()) + self.assertEqual([], self.evaluate(dequeued_t).tolist()) def testEmptyDequeueManyWithDynamicShape(self): with self.cached_session(): @@ -290,9 +290,9 @@ class PaddingFIFOQueueTest(test.TestCase): enqueue_op = q.enqueue(([10.0],)) dequeued_t = q.dequeue_many(0) - self.assertEqual([], dequeued_t.eval().tolist()) + self.assertEqual([], self.evaluate(dequeued_t).tolist()) enqueue_op.run() - self.assertEqual([], dequeued_t.eval().tolist()) + self.assertEqual([], self.evaluate(dequeued_t).tolist()) def testEmptyDequeueUpToWithDynamicShape(self): with self.cached_session(): @@ -301,9 +301,9 @@ class PaddingFIFOQueueTest(test.TestCase): enqueue_op = q.enqueue(([10.0],)) dequeued_t = q.dequeue_up_to(0) - self.assertEqual([], dequeued_t.eval().tolist()) + self.assertEqual([], self.evaluate(dequeued_t).tolist()) enqueue_op.run() - self.assertEqual([], dequeued_t.eval().tolist()) + self.assertEqual([], self.evaluate(dequeued_t).tolist()) def testConstructPaddingFIFOQueueWithNoShape(self): with self.cached_session(): @@ -327,7 +327,7 @@ class PaddingFIFOQueueTest(test.TestCase): enqueue_op.run() for i in range(8): - float_val, int_val = sess.run(dequeued_t) + float_val, int_val = self.evaluate(dequeued_t) self.assertEqual(float_elems[i % 4], float_val) self.assertAllEqual(int_elems[i % 4], int_val) @@ -344,7 +344,7 @@ class PaddingFIFOQueueTest(test.TestCase): enqueue_op.run() for i in range(8): - float_val, int_val = sess.run(dequeued_t) + float_val, int_val = self.evaluate(dequeued_t) self.assertEqual(float_elems[i % 4], float_val) self.assertAllEqual(int_elems[i % 4], int_val) @@ -357,8 +357,8 @@ class PaddingFIFOQueueTest(test.TestCase): enqueue_op.run() - self.assertAllEqual(elems[0:4], dequeued_t.eval()) - self.assertAllEqual(elems[4:8], dequeued_t.eval()) + self.assertAllEqual(elems[0:4], self.evaluate(dequeued_t)) + self.assertAllEqual(elems[4:8], self.evaluate(dequeued_t)) def testDequeueUpToNoBlocking(self): with self.cached_session(): @@ -369,8 +369,8 @@ class PaddingFIFOQueueTest(test.TestCase): enqueue_op.run() - self.assertAllEqual(elems[0:4], dequeued_t.eval()) - self.assertAllEqual(elems[4:8], dequeued_t.eval()) + self.assertAllEqual(elems[0:4], self.evaluate(dequeued_t)) + self.assertAllEqual(elems[4:8], self.evaluate(dequeued_t)) def testMultiDequeueMany(self): with self.cached_session() as sess: @@ -387,17 +387,17 @@ class PaddingFIFOQueueTest(test.TestCase): enqueue_op.run() - float_val, int_val = sess.run(dequeued_t) + float_val, int_val = self.evaluate(dequeued_t) self.assertAllEqual(float_elems[0:4], float_val) self.assertAllEqual(int_elems[0:4], int_val) self.assertEqual(float_val.shape, dequeued_t[0].get_shape()) self.assertEqual(int_val.shape, dequeued_t[1].get_shape()) - float_val, int_val = sess.run(dequeued_t) + float_val, int_val = self.evaluate(dequeued_t) self.assertAllEqual(float_elems[4:8], float_val) self.assertAllEqual(int_elems[4:8], int_val) - float_val, int_val = sess.run(dequeued_single_t) + float_val, int_val = self.evaluate(dequeued_single_t) self.assertAllEqual(float_elems[8], float_val) self.assertAllEqual(int_elems[8], int_val) self.assertEqual(float_val.shape, dequeued_single_t[0].get_shape()) @@ -418,7 +418,7 @@ class PaddingFIFOQueueTest(test.TestCase): enqueue_op.run() - float_val, int_val = sess.run(dequeued_t) + float_val, int_val = self.evaluate(dequeued_t) self.assertAllEqual(float_elems[0:4], float_val) self.assertAllEqual(int_elems[0:4], int_val) self.assertTrue( @@ -428,11 +428,11 @@ class PaddingFIFOQueueTest(test.TestCase): tensor_shape.TensorShape(int_val.shape).is_compatible_with(dequeued_t[ 1].get_shape())) - float_val, int_val = sess.run(dequeued_t) + float_val, int_val = self.evaluate(dequeued_t) self.assertAllEqual(float_elems[4:8], float_val) self.assertAllEqual(int_elems[4:8], int_val) - float_val, int_val = sess.run(dequeued_single_t) + float_val, int_val = self.evaluate(dequeued_single_t) self.assertAllEqual(float_elems[8], float_val) self.assertAllEqual(int_elems[8], int_val) self.assertTrue( @@ -459,7 +459,7 @@ class PaddingFIFOQueueTest(test.TestCase): for enqueue_op in enqueue_ops: enqueue_op.run() - string_val, int_val = sess.run(dequeued_t) + string_val, int_val = self.evaluate(dequeued_t) self.assertAllEqual([[b"a", b"", b""], [b"ab", b"", b""], [b"abc", b"", b""], [b"abc", b"d", b""], @@ -473,7 +473,7 @@ class PaddingFIFOQueueTest(test.TestCase): tensor_shape.TensorShape(int_val.shape).is_compatible_with(dequeued_t[ 1].get_shape())) - string_val, int_val = sess.run(dequeued_single_t) + string_val, int_val = self.evaluate(dequeued_single_t) self.assertAllEqual([b"abc", b"d", b"e", b"f"], string_val) self.assertAllEqual([[1, 2, 3, 4]], int_val) self.assertTrue( @@ -500,7 +500,7 @@ class PaddingFIFOQueueTest(test.TestCase): for enqueue_op in enqueue_ops: enqueue_op.run() - string_val, int_val = sess.run(dequeued_t) + string_val, int_val = self.evaluate(dequeued_t) self.assertAllEqual([[b"a", b"", b""], [b"ab", b"", b""], [b"abc", b"", b""], [b"abc", b"d", b""], @@ -514,7 +514,7 @@ class PaddingFIFOQueueTest(test.TestCase): tensor_shape.TensorShape(int_val.shape).is_compatible_with(dequeued_t[ 1].get_shape())) - string_val, int_val = sess.run(dequeued_single_t) + string_val, int_val = self.evaluate(dequeued_single_t) self.assertAllEqual([b"abc", b"d", b"e", b"f"], string_val) self.assertAllEqual([[1, 2, 3, 4]], int_val) self.assertTrue( @@ -622,7 +622,7 @@ class PaddingFIFOQueueTest(test.TestCase): r"Expected \[2,\?,3\], got \[2,3,4\]"): sess.run([enqueue_op], feed_dict={elems_bad: np.array([1] * 24).reshape((2, 3, 4))}) - dequeued_t.eval() + self.evaluate(dequeued_t) def testParallelEnqueueMany(self): with self.cached_session() as sess: @@ -633,7 +633,7 @@ class PaddingFIFOQueueTest(test.TestCase): # Enqueue 100 items in parallel on 10 threads. def enqueue(): - sess.run(enqueue_op) + self.evaluate(enqueue_op) threads = [self.checkedThread(target=enqueue) for _ in range(10)] for thread in threads: @@ -656,7 +656,7 @@ class PaddingFIFOQueueTest(test.TestCase): dequeued_elems = [] def dequeue(): - dequeued_elems.extend(sess.run(dequeued_t)) + dequeued_elems.extend(self.evaluate(dequeued_t)) threads = [self.checkedThread(target=dequeue) for _ in range(10)] for thread in threads: @@ -680,7 +680,7 @@ class PaddingFIFOQueueTest(test.TestCase): dequeued_elems = [] def dequeue(): - dequeued_elems.extend(sess.run(dequeued_t)) + dequeued_elems.extend(self.evaluate(dequeued_t)) threads = [self.checkedThread(target=dequeue) for _ in range(10)] for thread in threads: @@ -700,11 +700,11 @@ class PaddingFIFOQueueTest(test.TestCase): def enqueue(): for _ in xrange(100): - sess.run(enqueue_op) + self.evaluate(enqueue_op) def dequeue(): for _ in xrange(100): - self.assertTrue(sess.run(dequeued_t) in (10.0, 20.0)) + self.assertTrue(self.evaluate(dequeued_t) in (10.0, 20.0)) enqueue_threads = [self.checkedThread(target=enqueue) for _ in range(10)] dequeue_threads = [self.checkedThread(target=dequeue) for _ in range(10)] @@ -736,7 +736,7 @@ class PaddingFIFOQueueTest(test.TestCase): def dequeue(): for i in xrange(250): - self.assertEqual(i, sess.run(dequeued_t)) + self.assertEqual(i, self.evaluate(dequeued_t)) dequeue_thread = self.checkedThread(target=dequeue) dequeue_thread.start() @@ -767,7 +767,7 @@ class PaddingFIFOQueueTest(test.TestCase): dequeuemany_t = q.dequeue_many(count_placeholder) def enqueue(): - sess.run(enqueue_op) + self.evaluate(enqueue_op) enqueue_thread = self.checkedThread(target=enqueue) enqueue_thread.start() @@ -776,7 +776,7 @@ class PaddingFIFOQueueTest(test.TestCase): while elements_dequeued < 250: # With equal probability, run Dequeue or dequeue_many. if random.random() > 0.5: - self.assertEqual(elements_dequeued, dequeued_t.eval()) + self.assertEqual(elements_dequeued, self.evaluate(dequeued_t)) elements_dequeued += 1 else: count = random.randint(0, min(20, 250 - elements_dequeued)) @@ -805,10 +805,10 @@ class PaddingFIFOQueueTest(test.TestCase): # The enqueue_op should run after the dequeue op has blocked. # TODO(mrry): Figure out how to do this without sleeping. time.sleep(0.1) - sess.run(enqueue_op) + self.evaluate(enqueue_op) def dequeue(): - dequeued_elems.extend(sess.run(dequeued_t).tolist()) + dequeued_elems.extend(self.evaluate(dequeued_t).tolist()) enqueue_thread = self.checkedThread(target=enqueue) dequeue_thread = self.checkedThread(target=dequeue) @@ -832,10 +832,10 @@ class PaddingFIFOQueueTest(test.TestCase): # The enqueue_op should run after the dequeue op has blocked. # TODO(mrry): Figure out how to do this without sleeping. time.sleep(0.1) - sess.run(enqueue_op) + self.evaluate(enqueue_op) def dequeue(): - dequeued_elems.extend(sess.run(dequeued_t).tolist()) + dequeued_elems.extend(self.evaluate(dequeued_t).tolist()) enqueue_thread = self.checkedThread(target=enqueue) dequeue_thread = self.checkedThread(target=dequeue) @@ -882,12 +882,12 @@ class PaddingFIFOQueueTest(test.TestCase): enqueue_op.run() close_op.run() for elem in elems: - self.assertEqual([elem], dequeued_t.eval()) + self.assertEqual([elem], self.evaluate(dequeued_t)) # Expect the operation to fail due to the queue being closed. with self.assertRaisesRegexp(errors_impl.OutOfRangeError, "is closed and has insufficient"): - dequeued_t.eval() + self.evaluate(dequeued_t) def testBlockingDequeueFromClosedQueue(self): with self.cached_session() as sess: @@ -901,11 +901,11 @@ class PaddingFIFOQueueTest(test.TestCase): def dequeue(): for elem in elems: - self.assertEqual([elem], sess.run(dequeued_t)) + self.assertEqual([elem], self.evaluate(dequeued_t)) # Expect the operation to fail due to the queue being closed. with self.assertRaisesRegexp(errors_impl.OutOfRangeError, "is closed and has insufficient"): - sess.run(dequeued_t) + self.evaluate(dequeued_t) dequeue_thread = self.checkedThread(target=dequeue) dequeue_thread.start() @@ -926,8 +926,8 @@ class PaddingFIFOQueueTest(test.TestCase): enqueue_op.run() def dequeue(): - self.assertAllEqual(elems[:3], sess.run(dequeued_t)) - self.assertAllEqual(elems[3:], sess.run(dequeued_t)) + self.assertAllEqual(elems[:3], self.evaluate(dequeued_t)) + self.assertAllEqual(elems[3:], self.evaluate(dequeued_t)) dequeue_thread = self.checkedThread(target=dequeue) dequeue_thread.start() @@ -947,7 +947,7 @@ class PaddingFIFOQueueTest(test.TestCase): # Expect the operation to fail due to the queue being closed. with self.assertRaisesRegexp(errors_impl.OutOfRangeError, "is closed and has insufficient"): - sess.run(dequeued_t) + self.evaluate(dequeued_t) dequeue_thread = self.checkedThread(target=dequeue) dequeue_thread.start() @@ -968,11 +968,11 @@ class PaddingFIFOQueueTest(test.TestCase): enqueue_op.run() def dequeue(): - self.assertAllEqual(elems, sess.run(dequeued_t)) + self.assertAllEqual(elems, self.evaluate(dequeued_t)) # Expect the operation to fail due to the queue being closed. with self.assertRaisesRegexp(errors_impl.OutOfRangeError, "is closed and has insufficient"): - sess.run(dequeued_t) + self.evaluate(dequeued_t) dequeue_thread = self.checkedThread(target=dequeue) dequeue_thread.start() @@ -993,11 +993,11 @@ class PaddingFIFOQueueTest(test.TestCase): enqueue_op.run() def dequeue(): - self.assertAllEqual(elems[:3], sess.run(dequeued_t)) + self.assertAllEqual(elems[:3], self.evaluate(dequeued_t)) # Expect the operation to fail due to the queue being closed. with self.assertRaisesRegexp(errors_impl.OutOfRangeError, "is closed and has insufficient"): - sess.run(dequeued_t) + self.evaluate(dequeued_t) dequeue_thread = self.checkedThread(target=dequeue) dequeue_thread.start() @@ -1017,16 +1017,16 @@ class PaddingFIFOQueueTest(test.TestCase): cleanup_dequeue_t = q.dequeue() def enqueue(): - sess.run(enqueue_op) + self.evaluate(enqueue_op) def dequeue(): - self.assertAllEqual(elems[0:3], sess.run(dequeued_t)) + self.assertAllEqual(elems[0:3], self.evaluate(dequeued_t)) with self.assertRaises(errors_impl.OutOfRangeError): - sess.run(dequeued_t) - self.assertEqual(elems[3], sess.run(cleanup_dequeue_t)) + self.evaluate(dequeued_t) + self.assertEqual(elems[3], self.evaluate(cleanup_dequeue_t)) def close(): - sess.run(close_op) + self.evaluate(close_op) enqueue_thread = self.checkedThread(target=enqueue) enqueue_thread.start() @@ -1059,7 +1059,7 @@ class PaddingFIFOQueueTest(test.TestCase): def dequeue(): with self.assertRaises(errors_impl.OutOfRangeError): - sess.run([dequeued_a_t, dequeued_b_t]) + self.evaluate([dequeued_a_t, dequeued_b_t]) dequeue_thread = self.checkedThread(target=dequeue) dequeue_thread.start() @@ -1072,7 +1072,7 @@ class PaddingFIFOQueueTest(test.TestCase): # Test that the elements in the partially-dequeued batch are # restored in the correct order. for elem_a, elem_b in zip(elems_a, elems_b): - val_a, val_b = sess.run([cleanup_dequeue_a_t, cleanup_dequeue_b_t]) + val_a, val_b = self.evaluate([cleanup_dequeue_a_t, cleanup_dequeue_b_t]) self.assertEqual(elem_a, val_a) self.assertEqual(elem_b, val_b) self.assertEqual(0, q.size().eval()) @@ -1087,7 +1087,7 @@ class PaddingFIFOQueueTest(test.TestCase): # Expect the operation to fail due to the queue being closed. with self.assertRaisesRegexp(errors_impl.OutOfRangeError, "is closed and has insufficient"): - sess.run(dequeued_t) + self.evaluate(dequeued_t) dequeue_thread = self.checkedThread(target=dequeue) dequeue_thread.start() @@ -1107,7 +1107,7 @@ class PaddingFIFOQueueTest(test.TestCase): # Expect the operation to fail due to the queue being closed. with self.assertRaisesRegexp(errors_impl.OutOfRangeError, "is closed and has insufficient"): - sess.run(dequeued_t) + self.evaluate(dequeued_t) dequeue_thread = self.checkedThread(target=dequeue) dequeue_thread.start() @@ -1155,7 +1155,7 @@ class PaddingFIFOQueueTest(test.TestCase): enqueue_op.run() def blocking_enqueue(): - sess.run(blocking_enqueue_op) + self.evaluate(blocking_enqueue_op) thread = self.checkedThread(target=blocking_enqueue) thread.start() @@ -1163,8 +1163,8 @@ class PaddingFIFOQueueTest(test.TestCase): # TODO(mrry): Figure out how to do this without sleeping. time.sleep(0.1) for elem in elems: - self.assertEqual([elem], dequeued_t.eval()) - self.assertEqual([50.0], dequeued_t.eval()) + self.assertEqual([elem], self.evaluate(dequeued_t)) + self.assertEqual([50.0], self.evaluate(dequeued_t)) thread.join() def testBlockingEnqueueManyToFullQueue(self): @@ -1178,7 +1178,7 @@ class PaddingFIFOQueueTest(test.TestCase): enqueue_op.run() def blocking_enqueue(): - sess.run(blocking_enqueue_op) + self.evaluate(blocking_enqueue_op) thread = self.checkedThread(target=blocking_enqueue) thread.start() @@ -1186,10 +1186,10 @@ class PaddingFIFOQueueTest(test.TestCase): # TODO(mrry): Figure out how to do this without sleeping. time.sleep(0.1) for elem in elems: - self.assertEqual([elem], dequeued_t.eval()) + self.assertEqual([elem], self.evaluate(dequeued_t)) time.sleep(0.01) - self.assertEqual([50.0], dequeued_t.eval()) - self.assertEqual([60.0], dequeued_t.eval()) + self.assertEqual([50.0], self.evaluate(dequeued_t)) + self.assertEqual([60.0], self.evaluate(dequeued_t)) # Make sure the thread finishes before exiting. thread.join() @@ -1207,7 +1207,7 @@ class PaddingFIFOQueueTest(test.TestCase): def blocking_enqueue(): # Expect the operation to succeed once the dequeue op runs. - sess.run(blocking_enqueue_op) + self.evaluate(blocking_enqueue_op) enqueue_thread = self.checkedThread(target=blocking_enqueue) enqueue_thread.start() @@ -1217,18 +1217,18 @@ class PaddingFIFOQueueTest(test.TestCase): time.sleep(0.1) def close(): - sess.run(close_op) + self.evaluate(close_op) close_thread = self.checkedThread(target=close) close_thread.start() # The dequeue will unblock both threads. - self.assertEqual(10.0, dequeued_t.eval()) + self.assertEqual(10.0, self.evaluate(dequeued_t)) enqueue_thread.join() close_thread.join() for elem in [20.0, 30.0, 40.0, 50.0]: - self.assertEqual(elem, dequeued_t.eval()) + self.assertEqual(elem, self.evaluate(dequeued_t)) self.assertEqual(0, q.size().eval()) def testBlockingEnqueueManyBeforeClose(self): @@ -1242,7 +1242,7 @@ class PaddingFIFOQueueTest(test.TestCase): enqueue_op.run() def blocking_enqueue(): - sess.run(blocking_enqueue_op) + self.evaluate(blocking_enqueue_op) enqueue_thread = self.checkedThread(target=blocking_enqueue) enqueue_thread.start() @@ -1252,17 +1252,17 @@ class PaddingFIFOQueueTest(test.TestCase): time.sleep(0.1) def close(): - sess.run(close_op) + self.evaluate(close_op) close_thread = self.checkedThread(target=close) close_thread.start() # The dequeue will unblock both threads. - self.assertEqual(10.0, dequeued_t.eval()) + self.assertEqual(10.0, self.evaluate(dequeued_t)) enqueue_thread.join() close_thread.join() for elem in [20.0, 30.0, 50.0, 60.0]: - self.assertEqual(elem, dequeued_t.eval()) + self.assertEqual(elem, self.evaluate(dequeued_t)) def testDoesNotLoseValue(self): with self.cached_session(): @@ -1379,19 +1379,19 @@ class PaddingFIFOQueueTest(test.TestCase): def _blockingDequeue(self, sess, dequeue_op): with self.assertRaisesOpError("was cancelled"): - sess.run(dequeue_op) + self.evaluate(dequeue_op) def _blockingDequeueMany(self, sess, dequeue_many_op): with self.assertRaisesOpError("was cancelled"): - sess.run(dequeue_many_op) + self.evaluate(dequeue_many_op) def _blockingEnqueue(self, sess, enqueue_op): with self.assertRaisesOpError("was cancelled"): - sess.run(enqueue_op) + self.evaluate(enqueue_op) def _blockingEnqueueMany(self, sess, enqueue_many_op): with self.assertRaisesOpError("was cancelled"): - sess.run(enqueue_many_op) + self.evaluate(enqueue_many_op) def testResetOfBlockingOperation(self): with self.cached_session() as sess: @@ -1434,7 +1434,7 @@ class PaddingFIFOQueueTest(test.TestCase): def blocking_enqueue(): enq_done.append(False) # This will fill the queue and then block until enough dequeues happen. - sess.run(enq) + self.evaluate(enq) enq_done.append(True) thread = self.checkedThread(target=blocking_enqueue) @@ -1444,14 +1444,14 @@ class PaddingFIFOQueueTest(test.TestCase): results = [] results.append(deq.eval()) # Will only complete after the enqueue starts. self.assertEqual(len(enq_done), 1) - self.assertEqual(sess.run(size_op), 5) + self.assertEqual(self.evaluate(size_op), 5) for _ in range(3): results.append(deq.eval()) time.sleep(0.1) self.assertEqual(len(enq_done), 1) - self.assertEqual(sess.run(size_op), 5) + self.assertEqual(self.evaluate(size_op), 5) # This dequeue will unblock the thread. results.append(deq.eval()) @@ -1477,7 +1477,7 @@ class PaddingFIFOQueueTest(test.TestCase): def blocking_dequeue(): # Will only complete after 4 enqueues complete. - results.extend(sess.run(deq)) + results.extend(self.evaluate(deq)) thread = self.checkedThread(target=blocking_dequeue) thread.start() @@ -1486,7 +1486,7 @@ class PaddingFIFOQueueTest(test.TestCase): # TODO(mrry): Figure out how to do this without sleeping. time.sleep(0.1) self.assertEqual(len(results), 0) - sess.run(enq) + self.evaluate(enq) # Enough enqueued to unblock the dequeue thread.join() @@ -1517,7 +1517,7 @@ class PaddingFIFOQueueTest(test.TestCase): q.enqueue_many(input_tuple).run() output_tuple_t = q.dequeue_many(32) - output_tuple = sess.run(output_tuple_t) + output_tuple = self.evaluate(output_tuple_t) for (input_elem, output_elem) in zip(input_tuple, output_tuple): self.assertAllEqual(input_elem, output_elem) diff --git a/tensorflow/python/kernel_tests/parameterized_truncated_normal_op_test.py b/tensorflow/python/kernel_tests/parameterized_truncated_normal_op_test.py index c9221f8c20..f87f517053 100644 --- a/tensorflow/python/kernel_tests/parameterized_truncated_normal_op_test.py +++ b/tensorflow/python/kernel_tests/parameterized_truncated_normal_op_test.py @@ -29,6 +29,7 @@ from tensorflow.core.protobuf import config_pb2 from tensorflow.python.client import session from tensorflow.python.framework import ops from tensorflow.python.framework import random_seed +from tensorflow.python.framework import test_util from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import random_ops from tensorflow.python.platform import test @@ -166,30 +167,39 @@ class ParameterizedTruncatedNormalTest(test.TestCase): except ImportError as e: tf_logging.warn("Cannot test truncated normal op: %s" % str(e)) + @test_util.run_deprecated_v1 def testDefaults(self): self.validateMoments([10**5], 0.0, 1.0, -2.0, 2.0) + @test_util.run_deprecated_v1 def testShifted(self): self.validateMoments([10**5], -1.0, 1.0, -2.0, 2.0) + @test_util.run_deprecated_v1 def testRightTail(self): self.validateMoments([10**5], 0.0, 1.0, 4.0, np.infty) + @test_util.run_deprecated_v1 def testLeftTail(self): self.validateMoments([10**5], 0.0, 1.0, -np.infty, -4.0) + @test_util.run_deprecated_v1 def testLeftTailTwoSidedBounds(self): self.validateMoments([10**5], 0.0, 1.0, -6.0, -3.0) + @test_util.run_deprecated_v1 def testTwoSidedLeftTailShifted(self): self.validateKolmogorovSmirnov([10**5], 6.0, 1.0, -1.0, 1.0) + @test_util.run_deprecated_v1 def testRightTailShifted(self): self.validateMoments([10**5], -5.0, 1.0, 2.0, np.infty) + @test_util.run_deprecated_v1 def testSmallStddev(self): self.validateKolmogorovSmirnov([10**5], 0.0, 0.1, 0.05, 0.10) + @test_util.run_deprecated_v1 def testSamplingWithSmallStdDevFarFromBound(self): sample_op = random_ops.parameterized_truncated_normal( shape=(int(1e5),), means=0.8, stddevs=0.05, minvals=-1., maxvals=1.) @@ -202,6 +212,7 @@ class ParameterizedTruncatedNormalTest(test.TestCase): no_neg_samples = np.sum(samples < 0.) self.assertEqual(no_neg_samples, 0.) + @test_util.run_deprecated_v1 def testSamplingAtRandnSwitchover(self): # The randn sampler is used as the bounds are moved farther from the mean, # and the probability of accepting a sample increases the farther the diff --git a/tensorflow/python/kernel_tests/parse_single_example_op_test.py b/tensorflow/python/kernel_tests/parse_single_example_op_test.py index a84895a287..43c8fa4ab5 100644 --- a/tensorflow/python/kernel_tests/parse_single_example_op_test.py +++ b/tensorflow/python/kernel_tests/parse_single_example_op_test.py @@ -29,6 +29,7 @@ from tensorflow.python.framework import errors_impl from tensorflow.python.framework import ops from tensorflow.python.framework import sparse_tensor from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util from tensorflow.python.ops import parsing_ops from tensorflow.python.platform import test from tensorflow.python.platform import tf_logging @@ -107,7 +108,7 @@ class ParseExampleTest(test.TestCase): for result_dict in [out, out_with_example_name]: result = flatten_values_tensors_or_sparse(result_dict.values()) # Check values. - tf_result = sess.run(result) + tf_result = self.evaluate(result) _compare_output_to_expected(self, result_dict, expected_values, tf_result) @@ -121,6 +122,7 @@ class ParseExampleTest(test.TestCase): self.assertEqual( tuple(out[k].dense_shape.get_shape().as_list()), (1,)) + @test_util.run_deprecated_v1 def testEmptySerializedWithAllDefaults(self): sparse_name = "st_a" a_name = "a" @@ -229,6 +231,7 @@ class ParseExampleTest(test.TestCase): }, expected_err=(ValueError, "Missing shape for feature a")) + @test_util.run_deprecated_v1 def testSerializedContainingSparse(self): original = [ example(features=features({ @@ -552,6 +555,7 @@ class ParseExampleTest(test.TestCase): } }, expected_output) + @test_util.run_deprecated_v1 def testSerializedContainingSparseAndSparseFeatureAndDenseWithNoDefault(self): original = [ example(features=features({ @@ -618,6 +622,7 @@ class ParseExampleTest(test.TestCase): }, expected_output) + @test_util.run_deprecated_v1 def testSerializedContainingSparseAndSparseFeatureWithReuse(self): original = [ example(features=features({ @@ -658,6 +663,7 @@ class ParseExampleTest(test.TestCase): } }, expected_output) + @test_util.run_deprecated_v1 def testSerializedContainingVarLenDense(self): aname = "a" bname = "b" @@ -869,6 +875,7 @@ class ParseSingleExampleTest(test.TestCase): self.assertEqual( tuple(out[k].dense_shape.get_shape().as_list()), (1,)) + @test_util.run_deprecated_v1 def testSingleExampleWithSparseAndSparseFeatureAndDense(self): original = example(features=features({ "c": float_feature([3, 4]), diff --git a/tensorflow/python/kernel_tests/parsing_ops_test.py b/tensorflow/python/kernel_tests/parsing_ops_test.py index 71d8b60d3c..af76e09f39 100644 --- a/tensorflow/python/kernel_tests/parsing_ops_test.py +++ b/tensorflow/python/kernel_tests/parsing_ops_test.py @@ -33,6 +33,7 @@ from tensorflow.python.framework import errors_impl from tensorflow.python.framework import ops from tensorflow.python.framework import sparse_tensor from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util from tensorflow.python.framework import tensor_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import parsing_ops @@ -101,15 +102,15 @@ class ParseExampleTest(test.TestCase): out = parsing_ops.parse_example(**kwargs) result = flatten_values_tensors_or_sparse(out.values()) # Check values. - tf_result = sess.run(result) + tf_result = self.evaluate(result) _compare_output_to_expected(self, out, expected_values, tf_result) # Check shapes; if serialized is a Tensor we need its size to # properly check. serialized = kwargs["serialized"] batch_size = ( - serialized.eval().size if isinstance(serialized, ops.Tensor) else - np.asarray(serialized).size) + self.evaluate(serialized).size if isinstance(serialized, ops.Tensor) + else np.asarray(serialized).size) for k, f in kwargs["features"].items(): if isinstance(f, parsing_ops.FixedLenFeature) and f.shape is not None: self.assertEqual( @@ -121,6 +122,7 @@ class ParseExampleTest(test.TestCase): self.assertEqual( tuple(out[k].dense_shape.get_shape().as_list()), (2,)) + @test_util.run_deprecated_v1 def testEmptySerializedWithAllDefaults(self): sparse_name = "st_a" a_name = "a" @@ -243,6 +245,7 @@ class ParseExampleTest(test.TestCase): }, expected_err=(ValueError, "Missing shape for feature a")) + @test_util.run_deprecated_v1 def testSerializedContainingSparse(self): original = [ example(features=features({ @@ -571,6 +574,7 @@ class ParseExampleTest(test.TestCase): } }, expected_output) + @test_util.run_deprecated_v1 def testSerializedContainingSparseAndSparseFeatureAndDenseWithNoDefault(self): expected_st_a = ( # indices, values, shape np.empty((0, 2), dtype=np.int64), # indices @@ -631,6 +635,7 @@ class ParseExampleTest(test.TestCase): }, expected_output) + @test_util.run_deprecated_v1 def testSerializedContainingSparseAndSparseFeatureWithReuse(self): expected_idx = ( # indices, values, shape np.array([[0, 0], [0, 1], [1, 0], [1, 1]], dtype=np.int64), @@ -740,6 +745,7 @@ class ParseExampleTest(test.TestCase): for batch_size in (1, 10, 20, 100, 256): self._testSerializedContainingVarLenDenseLargerBatch(batch_size) + @test_util.run_deprecated_v1 def testSerializedContainingVarLenDense(self): aname = "a" bname = "b" @@ -962,6 +968,7 @@ class ParseSingleExampleTest(test.TestCase): self.assertEqual( tuple(out[k].dense_shape.get_shape().as_list()), (1,)) + @test_util.run_deprecated_v1 def testSingleExampleWithSparseAndSparseFeatureAndDense(self): original = example( features=features({ @@ -1180,6 +1187,7 @@ class ParseSequenceExampleTest(test.TestCase): expected_err=expected_err, batch=True) + @test_util.run_deprecated_v1 def testSequenceExampleWithSparseAndDenseContext(self): original = sequence_example( context=features({ @@ -1223,6 +1231,7 @@ class ParseSequenceExampleTest(test.TestCase): }, expected_context_values=expected_context_output) + @test_util.run_deprecated_v1 def testSequenceExampleWithMultipleSizeFeatureLists(self): original = sequence_example( feature_lists=feature_lists({ @@ -1286,6 +1295,7 @@ class ParseSequenceExampleTest(test.TestCase): }, expected_feat_list_values=expected_feature_list_output) + @test_util.run_deprecated_v1 def testSequenceExampleWithoutDebugName(self): original = sequence_example( feature_lists=feature_lists({ @@ -1343,6 +1353,7 @@ class ParseSequenceExampleTest(test.TestCase): }, expected_feat_list_values=expected_feature_list_output) + @test_util.run_deprecated_v1 def testSequenceExampleWithSparseAndDenseFeatureLists(self): original = sequence_example( feature_lists=feature_lists({ @@ -1401,6 +1412,7 @@ class ParseSequenceExampleTest(test.TestCase): }, expected_feat_list_values=expected_feature_list_output) + @test_util.run_deprecated_v1 def testSequenceExampleWithEmptyFeatureInFeatureLists(self): original = sequence_example( feature_lists=feature_lists({ @@ -1541,6 +1553,7 @@ class ParseSequenceExampleTest(test.TestCase): " feature_list_dense_missing_assumed_empty or" " feature_list_dense_defaults?")) + @test_util.run_deprecated_v1 def testSequenceExampleBatch(self): first = sequence_example( feature_lists=feature_lists({ @@ -1614,7 +1627,7 @@ class DecodeJSONExampleTest(test.TestCase): shape=examples.shape, dtype=dtypes.string) binary_tensor = parsing_ops.decode_json_example(json_tensor) - binary_val = sess.run(binary_tensor) + binary_val = self.evaluate(binary_tensor) if examples.shape: self.assertShapeEqual(binary_val, json_tensor) @@ -1695,16 +1708,18 @@ class DecodeJSONExampleTest(test.TestCase): })), ]) + @test_util.run_deprecated_v1 def testInvalidSyntax(self): with self.cached_session() as sess: json_tensor = constant_op.constant(["{]"]) binary_tensor = parsing_ops.decode_json_example(json_tensor) with self.assertRaisesOpError("Error while parsing JSON"): - sess.run(binary_tensor) + self.evaluate(binary_tensor) class ParseTensorOpTest(test.TestCase): + @test_util.run_deprecated_v1 def testToFloat32(self): with self.cached_session(): expected = np.random.rand(3, 4, 5).astype(np.float32) @@ -1718,6 +1733,7 @@ class ParseTensorOpTest(test.TestCase): self.assertAllEqual(expected, result) + @test_util.run_deprecated_v1 def testToUint8(self): with self.cached_session(): expected = np.random.rand(3, 4, 5).astype(np.uint8) @@ -1731,6 +1747,7 @@ class ParseTensorOpTest(test.TestCase): self.assertAllEqual(expected, result) + @test_util.run_deprecated_v1 def testTypeMismatch(self): with self.cached_session(): expected = np.random.rand(3, 4, 5).astype(np.uint8) @@ -1744,6 +1761,7 @@ class ParseTensorOpTest(test.TestCase): r"\(uint16\)"): tensor.eval(feed_dict={serialized: tensor_proto.SerializeToString()}) + @test_util.run_deprecated_v1 def testInvalidInput(self): with self.cached_session(): serialized = array_ops.placeholder(dtypes.string) diff --git a/tensorflow/python/kernel_tests/partitioned_variables_test.py b/tensorflow/python/kernel_tests/partitioned_variables_test.py index d1f0c6c2a0..48655391fa 100644 --- a/tensorflow/python/kernel_tests/partitioned_variables_test.py +++ b/tensorflow/python/kernel_tests/partitioned_variables_test.py @@ -26,6 +26,7 @@ from six.moves import xrange # pylint: disable=redefined-builtin from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import init_ops from tensorflow.python.ops import partitioned_variables @@ -322,17 +323,19 @@ class PartitionedVariablesTestCase(test.TestCase): for i in xrange(len(expected_specs)): self.assertEquals(expected_specs[i], slices[i]._save_slice_info.spec) + @test_util.run_deprecated_v1 def testVecConstantInit(self): with self.cached_session(): rnd_par = constant_op.constant([1, 2, 3, 4]) vs = partitioned_variables.create_partitioned_variables([4], [4], rnd_par) variables.global_variables_initializer().run() val = array_ops.concat(vs, 0).eval() - rnd = rnd_par.eval() + rnd = self.evaluate(rnd_par) self.assertAllClose(rnd, val) self.assertEqual([dtypes.int32] * 4, [v.dtype.base_dtype for v in vs]) self._TestSaveSpec(vs, ["4 0,1", "4 1,1", "4 2,1", "4 3,1"]) + @test_util.run_deprecated_v1 def testConstantInit(self): with self.cached_session(): rnd_par = constant_op.constant([[1, 2, 3, 4], [5, 6, 7, 8]]) @@ -340,7 +343,7 @@ class PartitionedVariablesTestCase(test.TestCase): rnd_par) variables.global_variables_initializer().run() val = array_ops.concat(vs, 1).eval() - rnd = rnd_par.eval() + rnd = self.evaluate(rnd_par) self.assertAllClose(rnd, val) self.assertEqual([dtypes.int32] * 2, [v.dtype.base_dtype for v in vs]) self._TestSaveSpec(vs, ["2 4 0,2:0,2", "2 4 0,2:2,2"]) @@ -401,12 +404,15 @@ class PartitionedVariablesTestCase(test.TestCase): self.assertEqual(var2_name + "/part_0:0", vs2[0].name) self.assertEqual(var2_name + "/part_1:0", vs2[1].name) + @test_util.run_deprecated_v1 def testName(self): self._testNameHelper(use_resource=False) + @test_util.run_deprecated_v1 def testResourceName(self): self._testNameHelper(use_resource=True) + @test_util.run_deprecated_v1 def testRandomInitValue(self): with self.cached_session(): rnd = variables.Variable(random_ops.random_uniform([200, 40])) @@ -414,7 +420,7 @@ class PartitionedVariablesTestCase(test.TestCase): rnd.get_shape(), [1, 10], rnd.initialized_value()) variables.global_variables_initializer().run() val = array_ops.concat(vs, 1).eval() - rnd = rnd.eval() + rnd = self.evaluate(rnd) self.assertAllClose(rnd, val) self.assertEqual([dtypes.float32] * 10, [v.dtype.base_dtype for v in vs]) self._TestSaveSpec(vs, [ @@ -424,6 +430,7 @@ class PartitionedVariablesTestCase(test.TestCase): "200 40 0,200:36,4" ]) + @test_util.run_deprecated_v1 def testRandomInitUnevenPartitions(self): with self.cached_session(): rnd = variables.Variable( @@ -434,7 +441,7 @@ class PartitionedVariablesTestCase(test.TestCase): for i in xrange(1, 10) ] variables.global_variables_initializer().run() - rnd_val = rnd.eval() + rnd_val = self.evaluate(rnd) # Only check the slice save specs for the first 5 tf. save_specs = [ # One slice @@ -462,6 +469,7 @@ class PartitionedVariablesTestCase(test.TestCase): if i < len(save_specs): self._TestSaveSpec(vs, save_specs[i]) + @test_util.run_deprecated_v1 def testDegenerate(self): with self.cached_session(): rnd = variables.Variable(random_ops.random_uniform([10, 43])) @@ -469,10 +477,11 @@ class PartitionedVariablesTestCase(test.TestCase): rnd.get_shape(), [1, 1], rnd.initialized_value()) variables.global_variables_initializer().run() val = array_ops.concat(vs, 0).eval() - rnd = rnd.eval() + rnd = self.evaluate(rnd) self.assertAllClose(rnd, val) self._TestSaveSpec(vs, ["10 43 0,10:0,43"]) + @test_util.run_deprecated_v1 def testSliceSizeOne(self): with self.cached_session(): rnd = variables.Variable(random_ops.random_uniform([10, 43])) @@ -480,7 +489,7 @@ class PartitionedVariablesTestCase(test.TestCase): rnd.get_shape(), [10, 1], rnd.initialized_value()) variables.global_variables_initializer().run() val = array_ops.concat(vs, 0).eval() - rnd = rnd.eval() + rnd = self.evaluate(rnd) self.assertAllClose(rnd, val) self._TestSaveSpec(vs, [ "10 43 0,1:0,43", "10 43 1,1:0,43", "10 43 2,1:0,43", @@ -488,6 +497,7 @@ class PartitionedVariablesTestCase(test.TestCase): "10 43 6,1:0,43", "10 43 7,1:0,43", "10 43 8,1:0,43", "10 43 9,1:0,43" ]) + @test_util.run_deprecated_v1 def testIotaInitializer(self): self.assertAllClose([0., 1., 2., 3.], _IotaInitializer([4])) self.assertAllClose([[0., 1.], [0., 10.], [0., 100.], [0., 1000.]], @@ -503,6 +513,7 @@ class PartitionedVariablesTestCase(test.TestCase): self.assertAllClose(slice0 + slice1 + slice2, val) self._TestSaveSpec(vs, ["13 5 0,5:0,5", "13 5 5,4:0,5", "13 5 9,4:0,5"]) + @test_util.run_deprecated_v1 def testRandomInitializer(self): # Sanity check that the slices uses a different seed when using a random # initializer function. @@ -510,7 +521,7 @@ class PartitionedVariablesTestCase(test.TestCase): var0, var1 = partitioned_variables.create_partitioned_variables( [20, 12], [1, 2], init_ops.random_uniform_initializer()) variables.global_variables_initializer().run() - val0, val1 = var0.eval().flatten(), var1.eval().flatten() + val0, val1 = self.evaluate(var0).flatten(), self.evaluate(var1).flatten() self.assertTrue(np.linalg.norm(val0 - val1) > 1e-6) # Negative test that proves that slices have the same values if # the random initializer uses a seed. @@ -518,7 +529,7 @@ class PartitionedVariablesTestCase(test.TestCase): var0, var1 = partitioned_variables.create_partitioned_variables( [20, 12], [1, 2], init_ops.random_uniform_initializer(seed=201)) variables.global_variables_initializer().run() - val0, val1 = var0.eval().flatten(), var1.eval().flatten() + val0, val1 = self.evaluate(var0).flatten(), self.evaluate(var1).flatten() self.assertAllClose(val0, val1) def testSomeErrors(self): @@ -546,6 +557,7 @@ class PartitionedVariablesTestCase(test.TestCase): partitioned_variables.create_partitioned_variables( [10, 43], [1, 50], rnd.initialized_value()) + @test_util.run_deprecated_v1 def testControlDepsNone(self): with self.cached_session() as session: c = constant_op.constant(1.0) @@ -572,6 +584,7 @@ class PartitionedVariablesTestCase(test.TestCase): for op in reading_ops: self.assertEqual([], op.control_inputs) + @test_util.run_deprecated_v1 def testConcat(self): with self.cached_session() as session: var_x = variable_scope.get_variable( diff --git a/tensorflow/python/kernel_tests/pool_test.py b/tensorflow/python/kernel_tests/pool_test.py index 372861297f..78e786f01c 100644 --- a/tensorflow/python/kernel_tests/pool_test.py +++ b/tensorflow/python/kernel_tests/pool_test.py @@ -24,6 +24,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import nn_ops import tensorflow.python.ops.nn_grad # pylint: disable=unused-import @@ -151,7 +152,7 @@ class PoolingTest(test.TestCase): np.prod(input_shape), dtype=np.float32).reshape(input_shape) - 1 y1 = pool_direct(input=x, **kwargs) y2 = nn_ops.pool(input=x, **kwargs) - self.assertAllClose(y1, y2.eval(), rtol=1e-2, atol=1e-2) + self.assertAllClose(y1, self.evaluate(y2), rtol=1e-2, atol=1e-2) def testPoolSimple(self): with self.session(use_gpu=test.is_gpu_available()): @@ -301,6 +302,7 @@ class PoolingTest(test.TestCase): err_tolerance = 1e-2 self.assertLess(err, err_tolerance) + @test_util.run_deprecated_v1 def testGradient1D(self): with self.session(use_gpu=test.is_gpu_available()): for padding in ["SAME", "VALID"]: @@ -327,6 +329,7 @@ class PoolingTest(test.TestCase): dilation_rate=[1], strides=strides) + @test_util.run_deprecated_v1 def testGradient2D(self): with self.session(use_gpu=test.is_gpu_available()): for padding in ["SAME", "VALID"]: @@ -353,6 +356,7 @@ class PoolingTest(test.TestCase): dilation_rate=[1, 1], strides=strides) + @test_util.run_deprecated_v1 def testGradient3D(self): with self.session(use_gpu=test.is_gpu_available()): for padding in ["SAME", "VALID"]: diff --git a/tensorflow/python/kernel_tests/pooling_ops_3d_test.py b/tensorflow/python/kernel_tests/pooling_ops_3d_test.py index e393c7a022..347e092dee 100644 --- a/tensorflow/python/kernel_tests/pooling_ops_3d_test.py +++ b/tensorflow/python/kernel_tests/pooling_ops_3d_test.py @@ -81,7 +81,7 @@ class PoolingTest(test.TestCase): data_format=data_format) if data_format == "NCDHW": t = test_util.NCHWToNHWC(t) - vals = sess.run(t) + vals = self.evaluate(t) # Verifies values. actual = vals.flatten() self.assertAllClose(expected, actual) @@ -253,6 +253,7 @@ class PoolingTest(test.TestCase): ksize = test_util.NHWCToNCHW(ksize) strides = test_util.NHWCToNCHW(strides) t = test_util.NHWCToNCHW(t) + output_sizes = test_util.NHWCToNCHW(output_sizes) t = pool_func( t, @@ -294,6 +295,7 @@ class PoolingTest(test.TestCase): use_gpu=use_gpu, **kwargs) + @test_util.run_deprecated_v1 def testMaxPoolGradValidPadding1_1_3d(self): self._ConstructAndTestGradient( nn_ops.max_pool3d, @@ -303,6 +305,7 @@ class PoolingTest(test.TestCase): strides=(1, 1, 1), padding="VALID") + @test_util.run_deprecated_v1 def testMaxPoolGradValidPadding2_1_6_3d(self): self._ConstructAndTestGradient( nn_ops.max_pool3d, @@ -312,6 +315,7 @@ class PoolingTest(test.TestCase): strides=(1, 1, 1), padding="VALID") + @test_util.run_deprecated_v1 def testMaxPoolGradValidPadding2_1_7_3d(self): self._ConstructAndTestGradient( nn_ops.max_pool3d, @@ -321,6 +325,7 @@ class PoolingTest(test.TestCase): strides=(1, 1, 1), padding="VALID") + @test_util.run_deprecated_v1 def testMaxPoolGradValidPadding1_2_3d(self): self._ConstructAndTestGradient( nn_ops.max_pool3d, @@ -330,6 +335,7 @@ class PoolingTest(test.TestCase): strides=(2, 2, 2), padding="VALID") + @test_util.run_deprecated_v1 def testMaxPoolGradValidPadding2_2_3d(self): self._ConstructAndTestGradient( nn_ops.max_pool3d, @@ -339,6 +345,7 @@ class PoolingTest(test.TestCase): strides=(2, 2, 2), padding="VALID") + @test_util.run_deprecated_v1 def testMaxPoolGradSamePadding1_1_3d(self): self._ConstructAndTestGradient( nn_ops.max_pool3d, @@ -348,6 +355,7 @@ class PoolingTest(test.TestCase): strides=(1, 1, 1), padding="SAME") + @test_util.run_deprecated_v1 def testMaxPoolGradSamePadding1_2_3d(self): self._ConstructAndTestGradient( nn_ops.max_pool3d, @@ -357,6 +365,7 @@ class PoolingTest(test.TestCase): strides=(2, 2, 2), padding="SAME") + @test_util.run_deprecated_v1 def testMaxPoolGradSamePadding2_1_3d(self): self._ConstructAndTestGradient( nn_ops.max_pool3d, @@ -366,6 +375,7 @@ class PoolingTest(test.TestCase): strides=(1, 1, 1), padding="SAME") + @test_util.run_deprecated_v1 def testMaxPoolGradSamePadding2_2_3d(self): self._ConstructAndTestGradient( nn_ops.max_pool3d, @@ -375,6 +385,7 @@ class PoolingTest(test.TestCase): strides=(2, 2, 2), padding="SAME") + @test_util.run_deprecated_v1 def testMaxPoolGradSamePadding3_1_3d(self): self._ConstructAndTestGradient( nn_ops.max_pool3d, @@ -384,6 +395,7 @@ class PoolingTest(test.TestCase): strides=(1, 1, 1), padding="SAME") + @test_util.run_deprecated_v1 def testAvgPoolGradValidPadding1_1_3d(self): self._ConstructAndTestGradient( nn_ops.avg_pool3d, @@ -393,6 +405,7 @@ class PoolingTest(test.TestCase): strides=(1, 1, 1), padding="VALID") + @test_util.run_deprecated_v1 def testAvgPoolGradValidPadding1_2_3d(self): self._ConstructAndTestGradient( nn_ops.avg_pool3d, @@ -402,6 +415,7 @@ class PoolingTest(test.TestCase): strides=(2, 2, 2), padding="VALID") + @test_util.run_deprecated_v1 def testAvgPoolGradValidPadding2_1_3d(self): self._ConstructAndTestGradient( nn_ops.avg_pool3d, @@ -411,6 +425,7 @@ class PoolingTest(test.TestCase): strides=(1, 1, 1), padding="VALID") + @test_util.run_deprecated_v1 def testAvgPoolGradValidPadding2_2_3d(self): self._ConstructAndTestGradient( nn_ops.avg_pool3d, @@ -420,6 +435,7 @@ class PoolingTest(test.TestCase): strides=(2, 2, 2), padding="VALID") + @test_util.run_deprecated_v1 def testAvgPoolGradSamePadding1_1_3d(self): self._ConstructAndTestGradient( nn_ops.avg_pool3d, @@ -429,6 +445,7 @@ class PoolingTest(test.TestCase): strides=(1, 1, 1), padding="SAME") + @test_util.run_deprecated_v1 def testAvgPoolGradSamePadding1_2_3d(self): self._ConstructAndTestGradient( nn_ops.avg_pool3d, @@ -438,6 +455,7 @@ class PoolingTest(test.TestCase): strides=(2, 2, 2), padding="SAME") + @test_util.run_deprecated_v1 def testAvgPoolGradSamePadding2_1_3d(self): self._ConstructAndTestGradient( nn_ops.avg_pool3d, @@ -447,6 +465,7 @@ class PoolingTest(test.TestCase): strides=(1, 1, 1), padding="SAME") + @test_util.run_deprecated_v1 def testAvgPoolGradSamePadding2_2_3d(self): self._ConstructAndTestGradient( nn_ops.avg_pool3d, @@ -456,6 +475,7 @@ class PoolingTest(test.TestCase): strides=(2, 2, 2), padding="SAME") + @test_util.run_deprecated_v1 def testAvgPoolGradSamePadding3_1_3d(self): self._ConstructAndTestGradient( nn_ops.avg_pool3d, diff --git a/tensorflow/python/kernel_tests/pooling_ops_test.py b/tensorflow/python/kernel_tests/pooling_ops_test.py index 53003a7f28..c33b59bb99 100644 --- a/tensorflow/python/kernel_tests/pooling_ops_test.py +++ b/tensorflow/python/kernel_tests/pooling_ops_test.py @@ -166,7 +166,7 @@ class PoolingTest(test.TestCase): strides_placeholder: strides }) else: - actual = t.eval() + actual = self.evaluate(t) self.assertShapeEqual(actual, t) self.assertAllCloseAccordingToType(expected, actual.flatten()) @@ -384,6 +384,7 @@ class PoolingTest(test.TestCase): expected=[], use_gpu=use_gpu) + @test_util.run_deprecated_v1 def testAvgPooling(self): for use_gpu in True, False: self._testAvgPoolValidPadding(use_gpu) @@ -577,6 +578,7 @@ class PoolingTest(test.TestCase): expected=[], use_gpu=use_gpu) + @test_util.run_deprecated_v1 def testMaxPooling(self): for use_gpu in True, False: self._testMaxPoolValidPadding(use_gpu) @@ -588,6 +590,7 @@ class PoolingTest(test.TestCase): self._testMaxPoolEmptyInput(use_gpu) # Tests for DepthwiseMaxPooling on CPU only. + @test_util.run_deprecated_v1 def testDepthwiseMaxPool1x1DepthWindow1(self): # input is: # [1.0, ..., 10.0] along depth, @@ -613,6 +616,7 @@ class PoolingTest(test.TestCase): use_gpu=False, v2=v2) + @test_util.run_deprecated_v1 def testDepthwiseMaxPool2x2DepthWindow3(self): # input is: # @@ -639,6 +643,7 @@ class PoolingTest(test.TestCase): use_gpu=False, v2=v2) + @test_util.run_deprecated_v1 def testKernelSmallerThanStrideValid(self): for use_gpu in [True, False]: self._VerifyValues( @@ -670,6 +675,7 @@ class PoolingTest(test.TestCase): expected=[5, 8, 26, 29], use_gpu=use_gpu) + @test_util.run_deprecated_v1 def testKernelSmallerThanStrideSame(self): for use_gpu in [True, False]: for pool_func in [nn_ops.max_pool, nn_ops.avg_pool]: @@ -750,11 +756,11 @@ class PoolingTest(test.TestCase): with self.cached_session(use_gpu=True): t = constant_op.constant(tensor_input, shape=input_shape) out_op, _ = nn_ops.max_pool_with_argmax(t, ksize, strides, padding) - gpu_val = out_op.eval() + gpu_val = self.evaluate(out_op) with self.cached_session(use_gpu=False): t = constant_op.constant(tensor_input, shape=input_shape) out_op = nn_ops.max_pool(t, ksize, strides, padding) - cpu_val = out_op.eval() + cpu_val = self.evaluate(out_op) self.assertAllCloseAccordingToType(cpu_val, gpu_val) def _CompareMaxPoolingBk(self, input_shape, output_shape, ksize, strides, @@ -767,20 +773,20 @@ class PoolingTest(test.TestCase): with self.cached_session(use_gpu=True): t = constant_op.constant(tensor_input, shape=input_shape) _, argmax_op = nn_ops.max_pool_with_argmax(t, ksize, strides, padding) - argmax = argmax_op.eval() + argmax = self.evaluate(argmax_op) grad_in = constant_op.constant(tensor_output, shape=output_shape) out_op = gen_nn_ops.max_pool_grad_with_argmax(t, grad_in, argmax, ksize, strides, padding) - gpu_val = out_op.eval() + gpu_val = self.evaluate(out_op) self.assertShapeEqual(gpu_val, out_op) with self.cached_session(use_gpu=False): t = constant_op.constant(tensor_input, shape=input_shape) out_op = nn_ops.max_pool(t, ksize, strides, padding) - orig_out = out_op.eval() + orig_out = self.evaluate(out_op) grad_in = constant_op.constant(tensor_output, shape=output_shape) out_op = gen_nn_ops.max_pool_grad(t, orig_out, grad_in, ksize, strides, padding) - cpu_val = out_op.eval() + cpu_val = self.evaluate(out_op) self.assertShapeEqual(cpu_val, out_op) # The CPU version accumulates its gradient on fp16, so it's less # accurate than the GPU version that does the accumulation on fp32 @@ -796,20 +802,20 @@ class PoolingTest(test.TestCase): with self.cached_session(use_gpu=True): t = constant_op.constant(tensor_input, shape=input_shape) _, argmax_op = nn_ops.max_pool_with_argmax(t, ksize, strides, padding) - argmax = argmax_op.eval() + argmax = self.evaluate(argmax_op) grad_in = constant_op.constant(tensor_input, shape=input_shape) out_op = gen_nn_ops.max_pool_grad_grad_with_argmax( t, grad_in, argmax, ksize, strides, padding) - gpu_val = out_op.eval() + gpu_val = self.evaluate(out_op) self.assertShapeEqual(gpu_val, out_op) with self.cached_session(use_gpu=False): t = constant_op.constant(tensor_input, shape=input_shape) out_op = nn_ops.max_pool(t, ksize, strides, padding) - orig_out = out_op.eval() + orig_out = self.evaluate(out_op) grad_in = constant_op.constant(tensor_input, shape=input_shape) out_op = gen_nn_ops.max_pool_grad_grad(t, orig_out, grad_in, ksize, strides, padding) - cpu_val = out_op.eval() + cpu_val = self.evaluate(out_op) self.assertShapeEqual(cpu_val, out_op) # The CPU version accumulates its gradient on fp16, so it's less # accurate than the GPU version that does the accumulation on fp32 @@ -826,7 +832,7 @@ class PoolingTest(test.TestCase): strides=[1, 1, 1, 1], Targmax=dtypes.int64, padding="VALID") - out, argmax = sess.run([out_op, argmax_op]) + out, argmax = self.evaluate([out_op, argmax_op]) self.assertShapeEqual(out, out_op) self.assertShapeEqual(argmax, argmax_op) self.assertAllClose(out.ravel(), [1.0, 1.0, 1.0, 1.0]) @@ -848,7 +854,7 @@ class PoolingTest(test.TestCase): ksize=[1, 2, 2, 1], strides=[1, 1, 1, 1], padding="VALID") - out = out_op.eval().flatten() + out = self.evaluate(out_op).flatten() self.assertAllClose(out, [11.0, 12.0, 0.0, 13.0, 0.0, 14.0, 0.0, 0.0, 0.0]) @@ -871,7 +877,7 @@ class PoolingTest(test.TestCase): ksize=[1, 2, 2, 1], strides=[1, 1, 1, 1], padding="VALID") - out = out_op.eval().flatten() + out = self.evaluate(out_op).flatten() self.assertAllClose(out, [11.0, 12.0, 14.0, 16.0]) def _ConstructAndTestGradient(self, @@ -1167,6 +1173,7 @@ class PoolingTest(test.TestCase): data_format=data_format, use_gpu=use_gpu) + @test_util.run_deprecated_v1 def testMaxPoolGrad(self): for (data_format, use_gpu) in GetTestConfigs(): self._testMaxPoolGradValidPadding1_1(data_format, use_gpu) @@ -1221,12 +1228,12 @@ class PoolingTest(test.TestCase): input_tensor, output_tensor, output_backprop_tensor, window_rows, window_cols, row_stride, col_stride, padding, v2) - actual_input_backprop = input_backprop_tensor.eval() + actual_input_backprop = self.evaluate(input_backprop_tensor) self.assertShapeEqual(actual_input_backprop, input_backprop_tensor) actual_input_backprop = actual_input_backprop.flatten() actual_input_backprop = self._GetNdArray(actual_input_backprop) - actual_output = output_tensor.eval().flatten() + actual_output = self.evaluate(output_tensor).flatten() actual_output = self._GetNdArray(actual_output) self.assertAllClose( @@ -1497,6 +1504,7 @@ class PoolingTest(test.TestCase): else: del os.environ["TF_ENABLE_MAXPOOL_NANPROP"] + @test_util.run_deprecated_v1 def testMaxPoolGradDirect(self): self._testMaxPoolGradDirect1_1() self._testMaxPoolGradDirect1_2() @@ -1616,6 +1624,7 @@ class PoolingTest(test.TestCase): data_format=data_format, use_gpu=use_gpu) + @test_util.run_deprecated_v1 def testMaxPoolGradGrad(self): for (data_format, use_gpu) in GetTestConfigs(): self._testMaxPoolGradGradValidPadding1_1(data_format, use_gpu) @@ -1649,6 +1658,7 @@ class PoolingTest(test.TestCase): orig_input, orig_output, grad, [1, window_rows, window_cols, 1], [1, row_stride, col_stride, 1], padding) + @test_util.run_deprecated_v1 def testAvgPoolGrad(self): for (data_format, use_gpu) in GetTestConfigs(): self._testAvgPoolGradValidPadding1_1(data_format, use_gpu) @@ -1778,6 +1788,7 @@ class PoolingTest(test.TestCase): data_format=data_format, use_gpu=use_gpu) + @test_util.run_deprecated_v1 def testShapeFunctionEdgeCases(self): # All shapes unknown. for pool_func in [nn_ops.max_pool, nn_ops.avg_pool]: @@ -1806,6 +1817,7 @@ class PoolingTest(test.TestCase): strides=[1, 1, 1, 1], padding="SAME") + @test_util.run_deprecated_v1 def testOpEdgeCases(self): with self.session(use_gpu=test.is_gpu_available()) as sess: pool_funcs = [nn_ops.max_pool, nn_ops.avg_pool] diff --git a/tensorflow/python/kernel_tests/priority_queue_test.py b/tensorflow/python/kernel_tests/priority_queue_test.py index 73a9c81638..9be682ea52 100644 --- a/tensorflow/python/kernel_tests/priority_queue_test.py +++ b/tensorflow/python/kernel_tests/priority_queue_test.py @@ -50,7 +50,7 @@ class PriorityQueueTest(test.TestCase): enq.run() deq = q.dequeue_many(100) - deq_elem, deq_value_0, deq_value_1 = sess.run(deq) + deq_elem, deq_value_0, deq_value_1 = self.evaluate(deq) allowed = {} missed = set() @@ -81,7 +81,7 @@ class PriorityQueueTest(test.TestCase): # Run one producer thread for each element in elems. def enqueue(enqueue_op): - sess.run(enqueue_op) + self.evaluate(enqueue_op) dequeue_op = q.dequeue_many(100) @@ -93,7 +93,7 @@ class PriorityQueueTest(test.TestCase): for t in enqueue_threads: t.start() - deq_elem, deq_value_0, deq_value_1 = sess.run(dequeue_op) + deq_elem, deq_value_0, deq_value_1 = self.evaluate(dequeue_op) for t in enqueue_threads: t.join() @@ -132,12 +132,12 @@ class PriorityQueueTest(test.TestCase): # Run one producer thread for each element in elems. def enqueue(enqueue_op): - sess.run(enqueue_op) + self.evaluate(enqueue_op) dequeued = [] def dequeue(dequeue_op): - (dequeue_indices, dequeue_values) = sess.run(dequeue_op) + (dequeue_indices, dequeue_values) = self.evaluate(dequeue_op) self.assertAllEqual(dequeue_indices, dequeue_values) dequeued.extend(dequeue_indices) @@ -184,10 +184,10 @@ class PriorityQueueTest(test.TestCase): # Run one producer thread for each element in elems. def enqueue(enqueue_op): - sess.run(enqueue_op) + self.evaluate(enqueue_op) def dequeue(dequeue_op, dequeued): - (dequeue_indices, dequeue_values) = sess.run(dequeue_op) + (dequeue_indices, dequeue_values) = self.evaluate(dequeue_op) self.assertAllEqual(dequeue_indices, dequeue_values) dequeue_wait.acquire() dequeued.extend(dequeue_indices) @@ -215,7 +215,7 @@ class PriorityQueueTest(test.TestCase): # We can't guarantee full sorting because we can't guarantee # that the dequeued.extend() call runs immediately after the - # sess.run() call. Here we're just happy everything came out. + # self.evaluate() call. Here we're just happy everything came out. self.assertAllEqual(set(dequeued), set(all_enqueued_values)) def testRoundTripInsertManyMultiThreadedReadOnceSorts(self): @@ -236,7 +236,7 @@ class PriorityQueueTest(test.TestCase): # Run one producer thread for each element in elems. def enqueue(enqueue_op): - sess.run(enqueue_op) + self.evaluate(enqueue_op) dequeue_op = q.dequeue_many(100) @@ -248,7 +248,7 @@ class PriorityQueueTest(test.TestCase): for t in enqueue_threads: t.start() - deq_elem, deq_value_0, deq_value_1 = sess.run(dequeue_op) + deq_elem, deq_value_0, deq_value_1 = self.evaluate(dequeue_op) for t in enqueue_threads: t.join() @@ -276,7 +276,7 @@ class PriorityQueueTest(test.TestCase): side_value_1 = np.random.rand(1000).astype(bytes) q.enqueue_many((elem, side_value_0, side_value_1)).run() deq = q.dequeue_many(1000) - deq_elem, deq_value_0, deq_value_1 = sess.run(deq) + deq_elem, deq_value_0, deq_value_1 = self.evaluate(deq) allowed = {} for e, v0, v1 in zip(elem, side_value_0, side_value_1): diff --git a/tensorflow/python/kernel_tests/py_func_test.py b/tensorflow/python/kernel_tests/py_func_test.py index 837f1ec054..1f3f02a9f0 100644 --- a/tensorflow/python/kernel_tests/py_func_test.py +++ b/tensorflow/python/kernel_tests/py_func_test.py @@ -272,7 +272,7 @@ class PyFuncTest(test.TestCase): with self.assertRaisesRegexp(errors.UnimplementedError, "Unsupported numpy type"): - y.eval() + self.evaluate(y) def testBadReturnType(self): with self.cached_session(): @@ -285,7 +285,7 @@ class PyFuncTest(test.TestCase): with self.assertRaisesRegexp(errors.UnimplementedError, "Unsupported object type"): - z.eval() + self.evaluate(z) def testReturnInput(self): with self.cached_session(): @@ -307,9 +307,9 @@ class PyFuncTest(test.TestCase): with session_lib.Session() as sess: producer = iter(range(3)) x, = script_ops.py_func(lambda: next(producer), [], [dtypes.int64]) - self.assertEqual(sess.run(x), 0) - self.assertEqual(sess.run(x), 1) - self.assertEqual(sess.run(x), 2) + self.assertEqual(self.evaluate(x), 0) + self.assertEqual(self.evaluate(x), 1) + self.assertEqual(self.evaluate(x), 2) def testStateless(self): # Not using self.cached_session(), which disables optimization. @@ -317,9 +317,9 @@ class PyFuncTest(test.TestCase): producer = iter(range(3)) x, = script_ops.py_func( lambda: next(producer), [], [dtypes.int64], stateful=False) - self.assertEqual(sess.run(x), 0) - self.assertEqual(sess.run(x), 0) - self.assertEqual(sess.run(x), 0) + self.assertEqual(self.evaluate(x), 0) + self.assertEqual(self.evaluate(x), 0) + self.assertEqual(self.evaluate(x), 0) def testGradientFunction(self): # Input to tf.py_func is necessary, otherwise get_gradient_function() @@ -335,7 +335,7 @@ class PyFuncTest(test.TestCase): val = [[1, 2], [3, 4]] x, = script_ops.py_func(lambda: np.array(val, order="F"), [], [dtypes.int64]) - self.assertAllEqual(val, x.eval()) + self.assertAllEqual(val, self.evaluate(x)) def testParallel(self): # Tests that tf.py_func's can run in parallel if they release the GIL. @@ -390,7 +390,7 @@ class PyFuncTest(test.TestCase): f = script_ops.py_func( do_nothing, [constant_op.constant(3, dtypes.int64)], [], stateful=False) with self.cached_session() as sess: - self.assertEqual(sess.run(f), []) + self.assertEqual(self.evaluate(f), []) def _testExceptionHandling(self, py_exp, tf_exp, eager=False): @@ -514,6 +514,7 @@ class PyFuncTest(test.TestCase): self.assertAllEqual(ret, [[3.0], [3.0], [3.0]]) @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def testEagerExceptionHandling(self): with test_util.device(use_gpu=True): self._testExceptionHandling( @@ -533,6 +534,7 @@ class PyFuncTest(test.TestCase): self._testExceptionHandling(WeirdError, errors.UnknownError, eager=True) @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def testEagerReturningVariableRaisesError(self): def return_variable(): return resource_variable_ops.ResourceVariable(0.0) diff --git a/tensorflow/python/kernel_tests/qr_op_test.py b/tensorflow/python/kernel_tests/qr_op_test.py index a60237fb25..0f2537b371 100644 --- a/tensorflow/python/kernel_tests/qr_op_test.py +++ b/tensorflow/python/kernel_tests/qr_op_test.py @@ -21,6 +21,7 @@ from __future__ import print_function import numpy as np from tensorflow.python.framework import constant_op +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import linalg_ops @@ -49,6 +50,7 @@ class QrOpTest(test.TestCase): "Shape must be at least rank 2 but is rank 1"): linalg_ops.qr(vector) + @test_util.run_deprecated_v1 def testConcurrentExecutesWithoutError(self): with self.session(use_gpu=True) as sess: all_ops = [] @@ -60,7 +62,7 @@ class QrOpTest(test.TestCase): q1, r1 = linalg_ops.qr(matrix1, full_matrices=full_matrices_) q2, r2 = linalg_ops.qr(matrix2, full_matrices=full_matrices_) all_ops += [q1, r1, q2, r2] - val = sess.run(all_ops) + val = self.evaluate(all_ops) for i in range(8): q = 4 * i self.assertAllEqual(val[q], val[q + 2]) # q1 == q2 @@ -110,7 +112,7 @@ def _GetQrOpTest(dtype_, shape_, full_matrices_, use_static_shape_): tol = 1e-5 else: tol = 1e-14 - self.assertAllClose(identity.eval(), xx.eval(), atol=tol) + self.assertAllClose(identity.eval(), self.evaluate(xx), atol=tol) def Test(self): np.random.seed(1) @@ -129,7 +131,7 @@ def _GetQrOpTest(dtype_, shape_, full_matrices_, use_static_shape_): q_tf, r_tf = linalg_ops.qr(x_tf, full_matrices=full_matrices_) if use_static_shape_: - q_tf_val, r_tf_val = sess.run([q_tf, r_tf]) + q_tf_val, r_tf_val = self.evaluate([q_tf, r_tf]) else: q_tf_val, r_tf_val = sess.run([q_tf, r_tf], feed_dict={x_tf: x_np}) diff --git a/tensorflow/python/kernel_tests/random/multinomial_op_big_test.py b/tensorflow/python/kernel_tests/random/multinomial_op_big_test.py index 0023506b77..576720528e 100644 --- a/tensorflow/python/kernel_tests/random/multinomial_op_big_test.py +++ b/tensorflow/python/kernel_tests/random/multinomial_op_big_test.py @@ -23,6 +23,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import random_seed +from tensorflow.python.framework import test_util from tensorflow.python.ops import random_ops from tensorflow.python.platform import test @@ -39,7 +40,7 @@ class MultinomialTest(test.TestCase): num_samples=1000000, seed=15) for _ in range(100): - x = sess.run(samples) + x = self.evaluate(samples) indices, counts = np.unique(x, return_counts=True) for index, count in zip(indices, counts): if index in counts_by_indices.keys(): @@ -57,7 +58,7 @@ class MultinomialTest(test.TestCase): num_samples=1000000, seed=15) for _ in range(100): - x = sess.run(samples) + x = self.evaluate(samples) indices, counts = np.unique(x, return_counts=True) for index, count in zip(indices, counts): if index in counts_by_indices.keys(): @@ -66,6 +67,7 @@ class MultinomialTest(test.TestCase): counts_by_indices[index] = count self.assertEqual(counts_by_indices[0], 100000000) + @test_util.run_deprecated_v1 def testLargeDynamicRange3(self): random_seed.set_random_seed(10) counts_by_indices = {} @@ -79,7 +81,7 @@ class MultinomialTest(test.TestCase): # we'll run out of memory if we try to draw 1e9 samples directly # really should fit in 12GB of memory... for _ in range(100): - x = sess.run(samples) + x = self.evaluate(samples) indices, counts = np.unique(x, return_counts=True) for index, count in zip(indices, counts): if index in counts_by_indices.keys(): diff --git a/tensorflow/python/kernel_tests/random/multinomial_op_test.py b/tensorflow/python/kernel_tests/random/multinomial_op_test.py index bd64d61af8..5d123307a8 100644 --- a/tensorflow/python/kernel_tests/random/multinomial_op_test.py +++ b/tensorflow/python/kernel_tests/random/multinomial_op_test.py @@ -66,12 +66,13 @@ class MultinomialTest(test.TestCase): logits, num_samples, output_dtype=output_dtype)) self.assertAllEqual([[1] * num_samples, [2] * num_samples], samples) + @test_util.run_deprecated_v1 def testOneOpMultipleStepsIndependent(self): - with self.test_session(use_gpu=True) as sess: + with test_util.use_gpu(): sample_op1, _ = self._make_ops(10) # Consecutive runs shouldn't yield identical output. - sample1a = sess.run(sample_op1) - sample1b = sess.run(sample_op1) + sample1a = self.evaluate(sample_op1) + sample1b = self.evaluate(sample_op1) self.assertFalse(np.equal(sample1a, sample1b).all()) def testEagerOneOpMultipleStepsIndependent(self): @@ -81,26 +82,27 @@ class MultinomialTest(test.TestCase): self.assertFalse(np.equal(sample1.numpy(), sample2.numpy()).all()) def testTwoOpsIndependent(self): - with self.test_session(use_gpu=True) as sess: + with test_util.use_gpu(): sample_op1, sample_op2 = self._make_ops(32) - sample1, sample2 = sess.run([sample_op1, sample_op2]) + sample1, sample2 = self.evaluate([sample_op1, sample_op2]) # We expect sample1 and sample2 to be independent. # 1 in 2^32 chance of this assertion failing. self.assertFalse(np.equal(sample1, sample2).all()) + @test_util.run_deprecated_v1 def testTwoOpsSameSeedDrawSameSequences(self): - with self.test_session(use_gpu=True) as sess: + with test_util.use_gpu(): sample_op1, sample_op2 = self._make_ops(1000, seed=1) - sample1, sample2 = sess.run([sample_op1, sample_op2]) + sample1, sample2 = self.evaluate([sample_op1, sample_op2]) self.assertAllEqual(sample1, sample2) def testLargeLogits(self): for neg in [True, False]: - with self.test_session(use_gpu=True): + with test_util.use_gpu(): logits = np.array([[1000.] * 5]) if neg: logits *= -1 - samples = random_ops.multinomial(logits, 10).eval() + samples = self.evaluate(random_ops.multinomial(logits, 10)) # Sampled classes should be in-range. self.assertTrue((samples >= 0).all()) self.assertTrue((samples < 5).all()) @@ -157,10 +159,10 @@ class MultinomialTest(test.TestCase): Returns: Frequencies from sampled classes; shape [batch_size, num_classes]. """ - with self.test_session(use_gpu=True) as sess: + with test_util.use_gpu(): random_seed.set_random_seed(1618) op = sampler(constant_op.constant(logits), num_samples) - d = sess.run(op) + d = self.evaluate(op) batch_size, num_classes = logits.shape freqs_mat = [] @@ -186,25 +188,27 @@ class MultinomialTest(test.TestCase): def testEmpty(self): classes = 5 - with self.test_session(use_gpu=True): + with test_util.use_gpu(): for batch in 0, 3: for samples in 0, 7: - x = random_ops.multinomial( - array_ops.zeros([batch, classes]), samples).eval() + x = self.evaluate( + random_ops.multinomial( + array_ops.zeros([batch, classes]), samples)) self.assertEqual(x.shape, (batch, samples)) + @test_util.run_deprecated_v1 def testEmptyClasses(self): - with self.test_session(use_gpu=True): + with test_util.use_gpu(): x = random_ops.multinomial(array_ops.zeros([5, 0]), 7) with self.assertRaisesOpError("num_classes should be positive"): - x.eval() + self.evaluate(x) def testNegativeMinLogits(self): random_seed.set_random_seed(78844) - with self.test_session(use_gpu=True): + with test_util.use_gpu(): logits = constant_op.constant([[np.finfo(np.float32).min] * 1023 + [0]]) num_samples = 1000 - samples = random_ops.multinomial(logits, num_samples).eval() + samples = self.evaluate(random_ops.multinomial(logits, num_samples)) self.assertAllEqual([[1023] * num_samples], samples) diff --git a/tensorflow/python/kernel_tests/random/random_crop_test.py b/tensorflow/python/kernel_tests/random/random_crop_test.py index 8ded522320..724bee0715 100644 --- a/tensorflow/python/kernel_tests/random/random_crop_test.py +++ b/tensorflow/python/kernel_tests/random/random_crop_test.py @@ -20,12 +20,14 @@ from __future__ import print_function import numpy as np +from tensorflow.python.framework import test_util from tensorflow.python.ops import random_ops from tensorflow.python.platform import test class RandomCropTest(test.TestCase): + @test_util.run_deprecated_v1 def testNoOp(self): # No random cropping is performed since the size is value.shape. for shape in (2, 1, 1), (2, 1, 3), (4, 5, 3): @@ -44,10 +46,11 @@ class RandomCropTest(test.TestCase): for i in range(2) for j in range(3) for k in range(4)) crop = random_ops.random_crop(value, size=target) for _ in range(20): - y = crop.eval() + y = self.evaluate(crop) self.assertAllEqual(y.shape, target) self.assertTrue(tuple(y.ravel()) in value_set) + @test_util.run_deprecated_v1 def testRandomization(self): # Run 1x1 crop num_samples times in an image and ensure that one finds each # pixel 1/size of the time. @@ -61,7 +64,7 @@ class RandomCropTest(test.TestCase): crop = random_ops.random_crop(value, single, seed=7) counts = np.zeros(size, dtype=np.int32) for _ in range(num_samples): - y = crop.eval() + y = self.evaluate(crop) self.assertAllEqual(y.shape, single) counts[y] += 1 diff --git a/tensorflow/python/kernel_tests/random/random_gamma_test.py b/tensorflow/python/kernel_tests/random/random_gamma_test.py index 606e8862c4..a5952a2196 100644 --- a/tensorflow/python/kernel_tests/random/random_gamma_test.py +++ b/tensorflow/python/kernel_tests/random/random_gamma_test.py @@ -26,6 +26,7 @@ from six.moves import xrange # pylint: disable=redefined-builtin from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import random_seed +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import random_ops @@ -48,14 +49,16 @@ class RandomGammaTest(test.TestCase): [num], alpha, beta=beta, dtype=dtype, seed=seed) ret = np.empty([10, num]) for i in xrange(10): - ret[i, :] = sess.run(rng) + ret[i, :] = self.evaluate(rng) return ret return func + @test_util.run_deprecated_v1 def testMomentsFloat32(self): self._testMoments(dtypes.float32) + @test_util.run_deprecated_v1 def testMomentsFloat64(self): self._testMoments(dtypes.float64) @@ -208,6 +211,7 @@ class RandomGammaTest(test.TestCase): sy = self._Sampler(1000, 0.0, 1.0, dt, use_gpu=use_gpu, seed=345) self.assertAllEqual(sx(), sy()) + @test_util.run_deprecated_v1 def testNoCSE(self): """CSE = constant subexpression eliminator. @@ -222,6 +226,7 @@ class RandomGammaTest(test.TestCase): diff = rnd2 - rnd1 self.assertGreater(np.linalg.norm(diff.eval()), 0.1) + @test_util.run_deprecated_v1 def testShape(self): # Fully known shape. rnd = random_ops.random_gamma([150], 2.0) @@ -253,6 +258,7 @@ class RandomGammaTest(test.TestCase): rnd = random_ops.random_gamma([50], array_ops.placeholder(dtypes.float32)) self.assertIs(None, rnd.get_shape().ndims) + @test_util.run_deprecated_v1 def testPositive(self): n = int(10e3) for dt in [dtypes.float16, dtypes.float32, dtypes.float64]: diff --git a/tensorflow/python/kernel_tests/random/random_grad_test.py b/tensorflow/python/kernel_tests/random/random_grad_test.py index d89056c485..aac6eeac06 100644 --- a/tensorflow/python/kernel_tests/random/random_grad_test.py +++ b/tensorflow/python/kernel_tests/random/random_grad_test.py @@ -22,6 +22,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradients_impl from tensorflow.python.ops import math_ops @@ -45,6 +46,7 @@ class AddLeadingUnitDimensionsTest(test.TestCase): ret = random_grad.add_leading_unit_dimensions(1.0, 2) self.assertAllEqual(ret.shape, [1, 1]) + @test_util.run_deprecated_v1 def testUnknownShape(self): x = array_ops.placeholder(dtypes.float32) num_dimensions = array_ops.placeholder(dtypes.int32) @@ -72,6 +74,7 @@ class RandomGammaGradTest(test.TestCase): some statistical properties of the derivative. """ + @test_util.run_deprecated_v1 def testGradientsShape(self): shape = [2, 3] alpha = array_ops.ones([2, 2]) @@ -81,6 +84,7 @@ class RandomGammaGradTest(test.TestCase): self.assertAllEqual(grads_alpha.shape, alpha.shape) self.assertAllEqual(grads_beta.shape, beta.shape) + @test_util.run_deprecated_v1 def testGradientsShapeWithOneSamplePerParameter(self): shape = [] alpha = array_ops.ones([2, 2]) @@ -90,6 +94,7 @@ class RandomGammaGradTest(test.TestCase): self.assertAllEqual(grads_alpha.shape, alpha.shape) self.assertAllEqual(grads_beta.shape, beta.shape) + @test_util.run_deprecated_v1 def testGradientsUnknownShape(self): shape = array_ops.placeholder(dtypes.int32) alpha = array_ops.placeholder(dtypes.float32) @@ -138,9 +143,11 @@ class RandomGammaGradTest(test.TestCase): except ImportError as e: tf_logging.warn("Cannot use special functions in a test: %s" % str(e)) + @test_util.run_deprecated_v1 def testCompareToExplicitDerivativeFloat(self): self._testCompareToExplicitDerivative(dtypes.float32) + @test_util.run_deprecated_v1 def testCompareToExplicitDerivativeDouble(self): self._testCompareToExplicitDerivative(dtypes.float64) @@ -182,12 +189,15 @@ class RandomGammaGradTest(test.TestCase): self.assertAllClose(actual_val, expected_val, rtol=1e-3, atol=1e-3) + @test_util.run_deprecated_v1 def testCompareToImplicitDerivativeFloat(self): self._testCompareToImplicitDerivative(dtypes.float32) + @test_util.run_deprecated_v1 def testCompareToImplicitDerivativeDouble(self): self._testCompareToImplicitDerivative(dtypes.float64) + @test_util.run_deprecated_v1 def testAverageAlphaGradient(self): """Statistical test for the gradient. @@ -207,6 +217,7 @@ class RandomGammaGradTest(test.TestCase): dsample_dalpha_val = self.evaluate(dsample_dalpha) self.assertAllClose(dsample_dalpha_val, [1.0] * 3, atol=1e-1, rtol=1e-1) + @test_util.run_deprecated_v1 def testQuadraticLoss(self): """Statistical test for the gradient. diff --git a/tensorflow/python/kernel_tests/random/random_ops_test.py b/tensorflow/python/kernel_tests/random/random_ops_test.py index 6de894846b..1384c3f446 100644 --- a/tensorflow/python/kernel_tests/random/random_ops_test.py +++ b/tensorflow/python/kernel_tests/random/random_ops_test.py @@ -25,6 +25,7 @@ from tensorflow.python.eager import context from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import random_seed +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import random_ops from tensorflow.python.ops import variables @@ -49,9 +50,9 @@ class RandomOpTestCommon(test.TestCase): random_seed.set_random_seed(graph_seed) x = rng_func([num], min_or_mean, max_or_stddev, dtype=dtype, seed=op_seed) - y = sess.run(x) - z = sess.run(x) - w = sess.run(x) + y = self.evaluate(x) + z = self.evaluate(x) + w = self.evaluate(x) # We use exact equality here. If the random-number generator is producing # the same output, all three outputs will be bitwise identical. @@ -69,7 +70,7 @@ class RandomNormalTest(RandomOpTestCommon): [num], mean=mu, stddev=sigma, dtype=dtype, seed=seed) ret = np.empty([10, num]) for i in xrange(10): - ret[i, :] = sess.run(rng) + ret[i, :] = self.evaluate(rng) return ret return func @@ -92,6 +93,7 @@ class RandomNormalTest(RandomOpTestCommon): # Checks that the CPU and GPU implementation returns the same results, # given the same random seed + @test_util.run_deprecated_v1 def testCPUGPUMatch(self): for dt in dtypes.float16, dtypes.float32, dtypes.float64: results = {} @@ -104,12 +106,14 @@ class RandomNormalTest(RandomOpTestCommon): else: self.assertAllClose(results[False], results[True], rtol=1e-6, atol=1e-6) + @test_util.run_deprecated_v1 def testSeed(self): for dt in dtypes.float16, dtypes.float32, dtypes.float64: sx = self._Sampler(1000, 0.0, 1.0, dt, use_gpu=True, seed=345) sy = self._Sampler(1000, 0.0, 1.0, dt, use_gpu=True, seed=345) self.assertAllEqual(sx(), sy()) + @test_util.run_deprecated_v1 def testNoCSE(self): for use_gpu in [False, True]: with self.session(use_gpu=use_gpu): @@ -119,12 +123,14 @@ class RandomNormalTest(RandomOpTestCommon): diff = rnd2 - rnd1 self.assertTrue(np.linalg.norm(diff.eval()) > 0.1) + @test_util.run_deprecated_v1 def testSingleSessionNotConstant(self): for use_gpu in [False, True]: for dt in dtypes.float16, dtypes.float32, dtypes.float64: self._testSingleSessionNotConstant( random_ops.random_normal, 100, dt, 0.0, 1.0, use_gpu=use_gpu) + @test_util.run_deprecated_v1 def testSingleSessionOpSeedNotConstant(self): for use_gpu in [False, True]: for dt in dtypes.float16, dtypes.float32, dtypes.float64: @@ -137,6 +143,7 @@ class RandomNormalTest(RandomOpTestCommon): use_gpu=use_gpu, op_seed=1345) + @test_util.run_deprecated_v1 def testSingleSessionGraphSeedNotConstant(self): for use_gpu in [False, True]: for dt in dtypes.float16, dtypes.float32, dtypes.float64: @@ -160,7 +167,7 @@ class TruncatedNormalTest(test.TestCase): [num], mean=mu, stddev=sigma, dtype=dtype, seed=seed) ret = np.empty([10, num]) for i in xrange(10): - ret[i, :] = sess.run(rng) + ret[i, :] = self.evaluate(rng) return ret return func @@ -185,6 +192,7 @@ class TruncatedNormalTest(test.TestCase): # Checks that the CPU and GPU implementation returns the same results, # given the same random seed + @test_util.run_deprecated_v1 def testCPUGPUMatch(self): # Skip the test if there is no GPU. if not test.is_gpu_available(): @@ -203,6 +211,7 @@ class TruncatedNormalTest(test.TestCase): else: self.assertAllClose(results[False], results[True], rtol=1e-6, atol=1e-6) + @test_util.run_deprecated_v1 def testSeed(self): for dt in dtypes.float16, dtypes.float32, dtypes.float64: sx = self._Sampler(1000, 0.0, 1.0, dt, use_gpu=True, seed=345) @@ -219,6 +228,7 @@ class TruncatedNormalTest(test.TestCase): print("std(x)", np.std(x), abs(np.std(x) / stddev - 0.85)) self.assertTrue(abs(np.std(x) / stddev - 0.85) < 0.04) + @test_util.run_deprecated_v1 def testLargeShape(self): with self.session(use_gpu=True): v = variables.Variable( @@ -226,6 +236,7 @@ class TruncatedNormalTest(test.TestCase): n = random_ops.truncated_normal(v.shape) self.assertEqual([8589934592, 1], n.shape.as_list()) + @test_util.run_deprecated_v1 def testNoCSE(self): with self.session(use_gpu=True): shape = [2, 3, 4] @@ -256,7 +267,7 @@ class RandomUniformTest(RandomOpTestCommon): [num], minval=minv, maxval=maxv, dtype=dtype, seed=seed) ret = np.empty([10, num]) for i in xrange(10): - ret[i, :] = sess.run(rng) + ret[i, :] = self.evaluate(rng) return ret return func @@ -287,6 +298,7 @@ class RandomUniformTest(RandomOpTestCommon): print("count = ", count) self.assertTrue(count < count_limit) + @test_util.run_deprecated_v1 def testUniformIntsWithInvalidShape(self): for dtype in dtypes.int32, dtypes.int64: with self.assertRaisesRegexp( @@ -299,6 +311,7 @@ class RandomUniformTest(RandomOpTestCommon): [1000], minval=1, maxval=[2, 3], dtype=dtype) # Check that uniform ints actually follow a uniform distribution. + @test_util.run_deprecated_v1 def testUniformInts(self): minv = -2 maxv = 15 @@ -331,6 +344,7 @@ class RandomUniformTest(RandomOpTestCommon): # Checks that the CPU and GPU implementation returns the same results, # given the same random seed + @test_util.run_deprecated_v1 def testCPUGPUMatch(self): for dt in (dtypes.float16, dtypes.float32, dtypes.float64, dtypes.int32, dtypes.int64): @@ -342,6 +356,7 @@ class RandomUniformTest(RandomOpTestCommon): results[use_gpu] = sampler() self.assertAllEqual(results[False], results[True]) + @test_util.run_deprecated_v1 def testSeed(self): for dt in (dtypes.float16, dtypes.float32, dtypes.float64, dtypes.int32, dtypes.int64): @@ -350,6 +365,7 @@ class RandomUniformTest(RandomOpTestCommon): sy = self._Sampler(1000, 0, 17, dtype=dt, use_gpu=True, seed=seed) self.assertAllEqual(sx(), sy()) + @test_util.run_deprecated_v1 def testNoCSE(self): shape = [2, 3, 4] for dtype in dtypes.float16, dtypes.float32, dtypes.int32: @@ -359,6 +375,7 @@ class RandomUniformTest(RandomOpTestCommon): diff = (rnd2 - rnd1).eval() self.assertTrue(np.linalg.norm(diff) > 0.1) + @test_util.run_deprecated_v1 def testSingleSessionNotConstant(self): for use_gpu in [False, True]: for dt in (dtypes.float16, dtypes.float32, dtypes.float64, dtypes.int32, @@ -366,6 +383,7 @@ class RandomUniformTest(RandomOpTestCommon): self._testSingleSessionNotConstant( random_ops.random_uniform, 100, dt, 0, 17, use_gpu=use_gpu) + @test_util.run_deprecated_v1 def testSingleSessionOpSeedNotConstant(self): for use_gpu in [False, True]: for dt in (dtypes.float16, dtypes.float32, dtypes.float64, dtypes.int32, @@ -379,6 +397,7 @@ class RandomUniformTest(RandomOpTestCommon): use_gpu=use_gpu, op_seed=1345) + @test_util.run_deprecated_v1 def testSingleSessionGraphSeedNotConstant(self): for use_gpu in [False, True]: for dt in (dtypes.float16, dtypes.float32, dtypes.float64, dtypes.int32, @@ -395,6 +414,7 @@ class RandomUniformTest(RandomOpTestCommon): class RandomShapeTest(test.TestCase): + @test_util.run_deprecated_v1 def testTruncatedNormal(self): # Fully known shape. rnd1 = random_ops.truncated_normal([1, 2, 3]) @@ -407,6 +427,7 @@ class RandomShapeTest(test.TestCase): rnd3 = random_ops.truncated_normal(array_ops.placeholder(dtypes.int32)) self.assertIs(None, rnd3.get_shape().ndims) + @test_util.run_deprecated_v1 def testRandomNormal(self): # Fully known shape. rnd1 = random_ops.random_normal([1, 2, 3]) @@ -419,6 +440,7 @@ class RandomShapeTest(test.TestCase): rnd3 = random_ops.random_normal(array_ops.placeholder(dtypes.int32)) self.assertIs(None, rnd3.get_shape().ndims) + @test_util.run_deprecated_v1 def testRandomUniform(self): # Fully known shape. rnd1 = random_ops.random_uniform([1, 2, 3]) diff --git a/tensorflow/python/kernel_tests/random/random_poisson_test.py b/tensorflow/python/kernel_tests/random/random_poisson_test.py index 417588f8a3..0a6b004d68 100644 --- a/tensorflow/python/kernel_tests/random/random_poisson_test.py +++ b/tensorflow/python/kernel_tests/random/random_poisson_test.py @@ -23,6 +23,7 @@ from six.moves import xrange # pylint: disable=redefined-builtin from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import random_ops from tensorflow.python.platform import test @@ -43,7 +44,7 @@ class RandomPoissonTest(test.TestCase): rng = random_ops.random_poisson(lam, [num], dtype=dtype, seed=seed) ret = np.empty([10, num]) for i in xrange(10): - ret[i, :] = sess.run(rng) + ret[i, :] = self.evaluate(rng) return ret return func @@ -104,6 +105,7 @@ class RandomPoissonTest(test.TestCase): # Checks that the CPU and GPU implementation returns the same results, # given the same random seed + @test_util.run_deprecated_v1 def testCPUGPUMatch(self): for dt in _SUPPORTED_DTYPES: results = {} @@ -115,12 +117,14 @@ class RandomPoissonTest(test.TestCase): else: self.assertAllClose(results[False], results[True], rtol=1e-6, atol=1e-6) + @test_util.run_deprecated_v1 def testSeed(self): for dt in dtypes.float16, dtypes.float32, dtypes.float64: sx = self._Sampler(1000, 1.0, dt, use_gpu=True, seed=345) sy = self._Sampler(1000, 1.0, dt, use_gpu=True, seed=345) self.assertAllEqual(sx(), sy()) + @test_util.run_deprecated_v1 def testNoCSE(self): """CSE = constant subexpression eliminator. @@ -140,8 +144,9 @@ class RandomPoissonTest(test.TestCase): with self.cached_session(): rnd = random_ops.random_poisson([], [], seed=12345) self.assertEqual([0], rnd.get_shape().as_list()) - self.assertAllClose(np.array([], dtype=np.float32), rnd.eval()) + self.assertAllClose(np.array([], dtype=np.float32), self.evaluate(rnd)) + @test_util.run_deprecated_v1 def testShape(self): # Fully known shape rnd = random_ops.random_poisson(2.0, [150], seed=12345) @@ -184,6 +189,7 @@ class RandomPoissonTest(test.TestCase): seed=12345) self.assertIs(None, rnd.get_shape().ndims) + @test_util.run_deprecated_v1 def testDTypeCombinationsV2(self): """Tests random_poisson_v2() for all supported dtype combinations.""" with self.cached_session(): diff --git a/tensorflow/python/kernel_tests/random/random_shuffle_queue_test.py b/tensorflow/python/kernel_tests/random/random_shuffle_queue_test.py index 0d85a072d4..ed4f5434d9 100644 --- a/tensorflow/python/kernel_tests/random/random_shuffle_queue_test.py +++ b/tensorflow/python/kernel_tests/random/random_shuffle_queue_test.py @@ -84,9 +84,9 @@ class RandomShuffleQueueTest(test.TestCase): dequeue_t = q.dequeue() results = [] for _ in range(2): - a, b = sess.run(dequeue_t) + a, b = self.evaluate(dequeue_t) results.append((a, b)) - a, b = sess.run(q.dequeue_many(3)) + a, b = self.evaluate(q.dequeue_many(3)) for i in range(3): results.append((a[i], b[i])) self.assertItemsEqual([(1, [5]), (2, [6]), (3, [7]), (4, [8]), (9, [10])], @@ -101,7 +101,7 @@ class RandomShuffleQueueTest(test.TestCase): # Run one producer thread for each element in elems. def enqueue(enqueue_op): - sess.run(enqueue_op) + self.evaluate(enqueue_op) threads = [ self.checkedThread( @@ -133,7 +133,7 @@ class RandomShuffleQueueTest(test.TestCase): results = [] def dequeue(): - results.append(sess.run(dequeued_t)) + results.append(self.evaluate(dequeued_t)) threads = [self.checkedThread(target=dequeue) for _ in enqueue_ops] for thread in threads: @@ -167,13 +167,13 @@ class RandomShuffleQueueTest(test.TestCase): # TODO(mrry): Figure out how to do this without sleeping. time.sleep(0.1) for enqueue_op in enqueue_ops: - sess.run(enqueue_op) + self.evaluate(enqueue_op) results = [] def dequeue(): for _ in xrange(len(elems)): - results.append(sess.run(dequeued_t)) + results.append(self.evaluate(dequeued_t)) enqueue_thread = self.checkedThread(target=enqueue) dequeue_thread = self.checkedThread(target=dequeue) @@ -197,7 +197,7 @@ class RandomShuffleQueueTest(test.TestCase): results = [] for _ in xrange(len(elems)): - x, y = sess.run(dequeued_t) + x, y = self.evaluate(dequeued_t) results.append((x, y)) self.assertItemsEqual(elems, results) @@ -215,9 +215,9 @@ class RandomShuffleQueueTest(test.TestCase): self.assertEqual([], size.get_shape()) enqueue_op.run() - self.assertEqual([1], size.eval()) + self.assertEqual([1], self.evaluate(size)) dequeued_t.op.run() - self.assertEqual([0], size.eval()) + self.assertEqual([0], self.evaluate(size)) def testEnqueueMany(self): with self.cached_session(): @@ -241,9 +241,9 @@ class RandomShuffleQueueTest(test.TestCase): enqueue_op = q.enqueue_many((empty_t,)) size_t = q.size() - self.assertEqual(0, size_t.eval()) + self.assertEqual(0, self.evaluate(size_t)) enqueue_op.run() - self.assertEqual(0, size_t.eval()) + self.assertEqual(0, self.evaluate(size_t)) def testEmptyDequeueMany(self): with self.cached_session(): @@ -251,9 +251,9 @@ class RandomShuffleQueueTest(test.TestCase): enqueue_op = q.enqueue((10.0,)) dequeued_t = q.dequeue_many(0) - self.assertEqual([], dequeued_t.eval().tolist()) + self.assertEqual([], self.evaluate(dequeued_t).tolist()) enqueue_op.run() - self.assertEqual([], dequeued_t.eval().tolist()) + self.assertEqual([], self.evaluate(dequeued_t).tolist()) def testEmptyDequeueUpTo(self): with self.cached_session(): @@ -261,9 +261,9 @@ class RandomShuffleQueueTest(test.TestCase): enqueue_op = q.enqueue((10.0,)) dequeued_t = q.dequeue_up_to(0) - self.assertEqual([], dequeued_t.eval().tolist()) + self.assertEqual([], self.evaluate(dequeued_t).tolist()) enqueue_op.run() - self.assertEqual([], dequeued_t.eval().tolist()) + self.assertEqual([], self.evaluate(dequeued_t).tolist()) def testEmptyDequeueManyWithNoShape(self): with self.cached_session(): @@ -275,7 +275,7 @@ class RandomShuffleQueueTest(test.TestCase): # Expect the operation to fail due to the shape not being constrained. with self.assertRaisesOpError( "require the components to have specified shapes"): - dequeued_t.eval() + self.evaluate(dequeued_t) enqueue_op.run() @@ -284,7 +284,7 @@ class RandomShuffleQueueTest(test.TestCase): # elements enqueued. with self.assertRaisesOpError( "require the components to have specified shapes"): - dequeued_t.eval() + self.evaluate(dequeued_t) def testEmptyDequeueUpToWithNoShape(self): with self.cached_session(): @@ -296,7 +296,7 @@ class RandomShuffleQueueTest(test.TestCase): # Expect the operation to fail due to the shape not being constrained. with self.assertRaisesOpError( "require the components to have specified shapes"): - dequeued_t.eval() + self.evaluate(dequeued_t) enqueue_op.run() @@ -305,7 +305,7 @@ class RandomShuffleQueueTest(test.TestCase): # elements enqueued. with self.assertRaisesOpError( "require the components to have specified shapes"): - dequeued_t.eval() + self.evaluate(dequeued_t) def testMultiEnqueueMany(self): with self.cached_session() as sess: @@ -321,7 +321,7 @@ class RandomShuffleQueueTest(test.TestCase): results = [] for _ in range(8): - float_val, int_val = sess.run(dequeued_t) + float_val, int_val = self.evaluate(dequeued_t) results.append((float_val, [int_val[0], int_val[1]])) expected = list(zip(float_elems, int_elems)) * 2 self.assertItemsEqual(expected, results) @@ -335,7 +335,7 @@ class RandomShuffleQueueTest(test.TestCase): enqueue_op.run() - results = dequeued_t.eval().tolist() + results = self.evaluate(dequeued_t).tolist() results.extend(dequeued_t.eval()) self.assertItemsEqual(elems, results) @@ -348,7 +348,7 @@ class RandomShuffleQueueTest(test.TestCase): enqueue_op.run() - results = dequeued_t.eval().tolist() + results = self.evaluate(dequeued_t).tolist() results.extend(dequeued_t.eval()) self.assertItemsEqual(elems, results) @@ -368,20 +368,20 @@ class RandomShuffleQueueTest(test.TestCase): enqueue_op.run() results = [] - float_val, int_val = sess.run(dequeued_t) + float_val, int_val = self.evaluate(dequeued_t) self.assertEqual(float_val.shape, dequeued_t[0].get_shape()) self.assertEqual(int_val.shape, dequeued_t[1].get_shape()) results.extend(zip(float_val, int_val.tolist())) - float_val, int_val = sess.run(dequeued_t) + float_val, int_val = self.evaluate(dequeued_t) results.extend(zip(float_val, int_val.tolist())) - float_val, int_val = sess.run(dequeued_single_t) + float_val, int_val = self.evaluate(dequeued_single_t) self.assertEqual(float_val.shape, dequeued_single_t[0].get_shape()) self.assertEqual(int_val.shape, dequeued_single_t[1].get_shape()) results.append((float_val, int_val.tolist())) - float_val, int_val = sess.run(dequeued_single_t) + float_val, int_val = self.evaluate(dequeued_single_t) results.append((float_val, int_val.tolist())) self.assertItemsEqual(zip(float_elems, int_elems), results) @@ -402,21 +402,21 @@ class RandomShuffleQueueTest(test.TestCase): enqueue_op.run() results = [] - float_val, int_val = sess.run(dequeued_t) + float_val, int_val = self.evaluate(dequeued_t) # dequeue_up_to has undefined shape. self.assertEqual([None], dequeued_t[0].get_shape().as_list()) self.assertEqual([None, 2], dequeued_t[1].get_shape().as_list()) results.extend(zip(float_val, int_val.tolist())) - float_val, int_val = sess.run(dequeued_t) + float_val, int_val = self.evaluate(dequeued_t) results.extend(zip(float_val, int_val.tolist())) - float_val, int_val = sess.run(dequeued_single_t) + float_val, int_val = self.evaluate(dequeued_single_t) self.assertEqual(float_val.shape, dequeued_single_t[0].get_shape()) self.assertEqual(int_val.shape, dequeued_single_t[1].get_shape()) results.append((float_val, int_val.tolist())) - float_val, int_val = sess.run(dequeued_single_t) + float_val, int_val = self.evaluate(dequeued_single_t) results.append((float_val, int_val.tolist())) self.assertItemsEqual(zip(float_elems, int_elems), results) @@ -442,7 +442,7 @@ class RandomShuffleQueueTest(test.TestCase): # Enqueue 100 items in parallel on 10 threads. def enqueue(): - sess.run(enqueue_op) + self.evaluate(enqueue_op) threads = [self.checkedThread(target=enqueue) for _ in range(10)] for thread in threads: @@ -466,7 +466,7 @@ class RandomShuffleQueueTest(test.TestCase): dequeued_elems = [] def dequeue(): - dequeued_elems.extend(sess.run(dequeued_t)) + dequeued_elems.extend(self.evaluate(dequeued_t)) threads = [self.checkedThread(target=dequeue) for _ in range(10)] for thread in threads: @@ -489,7 +489,7 @@ class RandomShuffleQueueTest(test.TestCase): dequeued_elems = [] def dequeue(): - dequeued_elems.extend(sess.run(dequeued_t)) + dequeued_elems.extend(self.evaluate(dequeued_t)) threads = [self.checkedThread(target=dequeue) for _ in range(10)] for thread in threads: @@ -515,7 +515,7 @@ class RandomShuffleQueueTest(test.TestCase): dequeued_elems = [] def dequeue(dequeue_op): - dequeued_elems.extend(sess.run(dequeue_op)) + dequeued_elems.extend(self.evaluate(dequeue_op)) threads = [] for dequeue_op in dequeue_ops: @@ -539,10 +539,10 @@ class RandomShuffleQueueTest(test.TestCase): # The enqueue_op should run after the dequeue op has blocked. # TODO(mrry): Figure out how to do this without sleeping. time.sleep(0.1) - sess.run(enqueue_op) + self.evaluate(enqueue_op) def dequeue(): - dequeued_elems.extend(sess.run(dequeued_t).tolist()) + dequeued_elems.extend(self.evaluate(dequeued_t).tolist()) enqueue_thread = self.checkedThread(target=enqueue) dequeue_thread = self.checkedThread(target=dequeue) @@ -566,10 +566,10 @@ class RandomShuffleQueueTest(test.TestCase): # The enqueue_op should run after the dequeue op has blocked. # TODO(mrry): Figure out how to do this without sleeping. time.sleep(0.1) - sess.run(enqueue_op) + self.evaluate(enqueue_op) def dequeue(): - dequeued_elems.extend(sess.run(dequeued_t).tolist()) + dequeued_elems.extend(self.evaluate(dequeued_t).tolist()) enqueue_thread = self.checkedThread(target=enqueue) dequeue_thread = self.checkedThread(target=dequeue) @@ -649,7 +649,7 @@ class RandomShuffleQueueTest(test.TestCase): # Expect the operation to fail due to the queue being closed. with self.assertRaisesRegexp(errors_impl.OutOfRangeError, "is closed and has insufficient"): - dequeued_t.eval() + self.evaluate(dequeued_t) def testBlockingDequeueFromClosedQueue(self): with self.cached_session() as sess: @@ -665,18 +665,18 @@ class RandomShuffleQueueTest(test.TestCase): results = [] # Manually dequeue until we hit min_size. - results.append(sess.run(dequeued_t)) - results.append(sess.run(dequeued_t)) + results.append(self.evaluate(dequeued_t)) + results.append(self.evaluate(dequeued_t)) def blocking_dequeue(): - results.append(sess.run(dequeued_t)) - results.append(sess.run(dequeued_t)) + results.append(self.evaluate(dequeued_t)) + results.append(self.evaluate(dequeued_t)) self.assertItemsEqual(elems, results) # Expect the operation to fail due to the queue being closed. with self.assertRaisesRegexp(errors_impl.OutOfRangeError, "is closed and has insufficient"): - sess.run(dequeued_t) + self.evaluate(dequeued_t) dequeue_thread = self.checkedThread(target=blocking_dequeue) dequeue_thread.start() @@ -701,7 +701,7 @@ class RandomShuffleQueueTest(test.TestCase): # Expect the operation to fail due to the queue being closed. with self.assertRaisesRegexp(errors_impl.OutOfRangeError, "is closed and has insufficient"): - sess.run(dequeued_t) + self.evaluate(dequeued_t) finished.append(True) dequeue_thread = self.checkedThread(target=dequeue) @@ -727,12 +727,12 @@ class RandomShuffleQueueTest(test.TestCase): progress = [] # Must be mutable def dequeue(): - self.assertItemsEqual(elems, sess.run(dequeued_t)) + self.assertItemsEqual(elems, self.evaluate(dequeued_t)) progress.append(1) # Expect the operation to fail due to the queue being closed. with self.assertRaisesRegexp(errors_impl.OutOfRangeError, "is closed and has insufficient"): - sess.run(dequeued_t) + self.evaluate(dequeued_t) progress.append(2) self.assertEqual(len(progress), 0) @@ -763,9 +763,9 @@ class RandomShuffleQueueTest(test.TestCase): results = [] def dequeue(): - results.extend(sess.run(dequeued_t)) + results.extend(self.evaluate(dequeued_t)) self.assertEquals(3, len(results)) - results.extend(sess.run(dequeued_t)) + results.extend(self.evaluate(dequeued_t)) self.assertEquals(4, len(results)) dequeue_thread = self.checkedThread(target=dequeue) @@ -794,11 +794,11 @@ class RandomShuffleQueueTest(test.TestCase): results = [] def dequeue(): - results.extend(sess.run(dequeued_t)) + results.extend(self.evaluate(dequeued_t)) self.assertEquals(3, len(results)) # min_after_dequeue is 2, we ask for 3 elements, and we end up only # getting the remaining 1. - results.extend(sess.run(dequeued_t)) + results.extend(self.evaluate(dequeued_t)) self.assertEquals(4, len(results)) dequeue_thread = self.checkedThread(target=dequeue) @@ -824,16 +824,16 @@ class RandomShuffleQueueTest(test.TestCase): results = [] def dequeue(): - results.extend(sess.run(dequeued_t)) + results.extend(self.evaluate(dequeued_t)) self.assertEqual(len(results), 3) # Expect the operation to fail due to the queue being closed. with self.assertRaisesRegexp(errors_impl.OutOfRangeError, "is closed and has insufficient"): - sess.run(dequeued_t) + self.evaluate(dequeued_t) # While the last dequeue failed, we want to insure that it returns # any elements that it potentially reserved to dequeue. Thus the # next cleanup should return a single element. - results.extend(sess.run(cleanup_dequeue_t)) + results.extend(self.evaluate(cleanup_dequeue_t)) dequeue_thread = self.checkedThread(target=dequeue) dequeue_thread.start() @@ -854,7 +854,7 @@ class RandomShuffleQueueTest(test.TestCase): # Expect the operation to fail due to the queue being closed. with self.assertRaisesRegexp(errors_impl.OutOfRangeError, "is closed and has insufficient"): - sess.run(dequeued_t) + self.evaluate(dequeued_t) dequeue_thread = self.checkedThread(target=dequeue) dequeue_thread.start() @@ -874,7 +874,7 @@ class RandomShuffleQueueTest(test.TestCase): # Expect the operation to fail due to the queue being closed. with self.assertRaisesRegexp(errors_impl.OutOfRangeError, "is closed and has insufficient"): - sess.run(dequeued_t) + self.evaluate(dequeued_t) dequeue_thread = self.checkedThread(target=dequeue) dequeue_thread.start() @@ -922,7 +922,7 @@ class RandomShuffleQueueTest(test.TestCase): enqueue_op.run() def blocking_enqueue(): - sess.run(blocking_enqueue_op) + self.evaluate(blocking_enqueue_op) thread = self.checkedThread(target=blocking_enqueue) thread.start() @@ -950,7 +950,7 @@ class RandomShuffleQueueTest(test.TestCase): enqueue_op.run() def blocking_enqueue(): - sess.run(blocking_enqueue_op) + self.evaluate(blocking_enqueue_op) thread = self.checkedThread(target=blocking_enqueue) thread.start() @@ -987,11 +987,11 @@ class RandomShuffleQueueTest(test.TestCase): def blocking_enqueue(): # Expect the operation to succeed since it will complete # before the queue is closed. - sess.run(blocking_enqueue_op) + self.evaluate(blocking_enqueue_op) # Expect the operation to fail due to the queue being closed. with self.assertRaisesRegexp(errors_impl.CancelledError, "closed"): - sess.run(blocking_enqueue_op) + self.evaluate(blocking_enqueue_op) thread1 = self.checkedThread(target=blocking_enqueue) thread1.start() @@ -1001,7 +1001,7 @@ class RandomShuffleQueueTest(test.TestCase): time.sleep(0.1) def blocking_close(): - sess.run(close_op) + self.evaluate(close_op) thread2 = self.checkedThread(target=blocking_close) thread2.start() @@ -1032,7 +1032,7 @@ class RandomShuffleQueueTest(test.TestCase): def blocking_enqueue(): # This will block until the dequeue after the close. - sess.run(blocking_enqueue_op) + self.evaluate(blocking_enqueue_op) thread1 = self.checkedThread(target=blocking_enqueue) thread1.start() @@ -1040,7 +1040,7 @@ class RandomShuffleQueueTest(test.TestCase): # First blocking_enqueue_op of blocking_enqueue has enqueued 1 of 2 # elements, and is blocked waiting for one more element to be dequeue. for i in range(50): - queue_size = size_t.eval() + queue_size = self.evaluate(size_t) if queue_size == 4: break elif i == 49: @@ -1050,7 +1050,7 @@ class RandomShuffleQueueTest(test.TestCase): time.sleep(0.1) def blocking_close(): - sess.run(close_op) + self.evaluate(close_op) thread2 = self.checkedThread(target=blocking_close) thread2.start() @@ -1064,7 +1064,7 @@ class RandomShuffleQueueTest(test.TestCase): # At this point the close operation will complete, so the next enqueue # will fail. with self.assertRaisesRegexp(errors_impl.CancelledError, "closed"): - sess.run(blocking_enqueue_op) + self.evaluate(blocking_enqueue_op) def testSharedQueueSameSession(self): with self.cached_session(): @@ -1216,23 +1216,23 @@ class RandomShuffleQueueTest(test.TestCase): def _blockingDequeue(self, sess, dequeue_op): with self.assertRaisesOpError("was cancelled"): - sess.run(dequeue_op) + self.evaluate(dequeue_op) def _blockingDequeueMany(self, sess, dequeue_many_op): with self.assertRaisesOpError("was cancelled"): - sess.run(dequeue_many_op) + self.evaluate(dequeue_many_op) def _blockingDequeueUpTo(self, sess, dequeue_up_to_op): with self.assertRaisesOpError("was cancelled"): - sess.run(dequeue_up_to_op) + self.evaluate(dequeue_up_to_op) def _blockingEnqueue(self, sess, enqueue_op): with self.assertRaisesOpError("was cancelled"): - sess.run(enqueue_op) + self.evaluate(enqueue_op) def _blockingEnqueueMany(self, sess, enqueue_many_op): with self.assertRaisesOpError("was cancelled"): - sess.run(enqueue_many_op) + self.evaluate(enqueue_many_op) def testResetOfBlockingOperation(self): with self.cached_session() as sess: @@ -1383,7 +1383,7 @@ class RandomShuffleQueueTest(test.TestCase): def blocking_enqueue(): enq_done.append(False) # This will fill the queue and then block until enough dequeues happen. - sess.run(enq) + self.evaluate(enq) enq_done.append(True) thread = self.checkedThread(target=blocking_enqueue) @@ -1393,14 +1393,14 @@ class RandomShuffleQueueTest(test.TestCase): results = [] results.append(deq.eval()) # Will only complete after the enqueue starts. self.assertEqual(len(enq_done), 1) - self.assertEqual(sess.run(size_op), 5) + self.assertEqual(self.evaluate(size_op), 5) for _ in range(3): results.append(deq.eval()) time.sleep(0.1) self.assertEqual(len(enq_done), 1) - self.assertEqual(sess.run(size_op), 5) + self.assertEqual(self.evaluate(size_op), 5) # This dequeue will unblock the thread. results.append(deq.eval()) @@ -1426,7 +1426,7 @@ class RandomShuffleQueueTest(test.TestCase): def blocking_dequeue(): # Will only complete after 4 enqueues complete. - results.extend(sess.run(deq)) + results.extend(self.evaluate(deq)) thread = self.checkedThread(target=blocking_dequeue) thread.start() @@ -1435,7 +1435,7 @@ class RandomShuffleQueueTest(test.TestCase): # TODO(mrry): Figure out how to do this without sleeping. time.sleep(0.1) self.assertEqual(len(results), 0) - sess.run(enq) + self.evaluate(enq) # Enough enqueued to unblock the dequeue thread.join() diff --git a/tensorflow/python/kernel_tests/random/stateless_random_ops_test.py b/tensorflow/python/kernel_tests/random/stateless_random_ops_test.py index d57db3c512..898f38444b 100644 --- a/tensorflow/python/kernel_tests/random/stateless_random_ops_test.py +++ b/tensorflow/python/kernel_tests/random/stateless_random_ops_test.py @@ -24,6 +24,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import random_seed +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import random_ops from tensorflow.python.ops import stateless_random_ops as stateless @@ -58,11 +59,11 @@ class StatelessOpsTest(test.TestCase): preseed = invert_philox(key, (seed[0], 0, seed[1], 0)).astype(np.uint64) preseed = preseed[::2] | preseed[1::2] << 32 random_seed.set_random_seed(seed[0]) - with self.test_session(use_gpu=True): + with test_util.use_gpu(): for stateless_op, stateful_op in cases: stateful = stateful_op(seed=seed[1]) pure = stateless_op(seed=preseed) - self.assertAllEqual(stateful.eval(), pure.eval()) + self.assertAllEqual(self.evaluate(stateful), self.evaluate(pure)) def _test_determinism(self, cases): # Stateless values should be equal iff the seeds are equal (roughly) @@ -128,23 +129,29 @@ class StatelessOpsTest(test.TestCase): yield (functools.partial(stateless.stateless_multinomial, **kwds), functools.partial(random_ops.multinomial, **kwds)) + @test_util.run_deprecated_v1 def testMatchFloat(self): self._test_match(self._float_cases()) + @test_util.run_deprecated_v1 def testMatchInt(self): self._test_match(self._int_cases()) + @test_util.run_deprecated_v1 def testMatchMultinomial(self): self._test_match(self._multinomial_cases()) + @test_util.run_deprecated_v1 def testDeterminismFloat(self): self._test_determinism( self._float_cases(shape_dtypes=(dtypes.int32, dtypes.int64))) + @test_util.run_deprecated_v1 def testDeterminismInt(self): self._test_determinism( self._int_cases(shape_dtypes=(dtypes.int32, dtypes.int64))) + @test_util.run_deprecated_v1 def testDeterminismMultinomial(self): self._test_determinism(self._multinomial_cases()) diff --git a/tensorflow/python/kernel_tests/reader_ops_test.py b/tensorflow/python/kernel_tests/reader_ops_test.py index ac9be56d63..43d15817e9 100644 --- a/tensorflow/python/kernel_tests/reader_ops_test.py +++ b/tensorflow/python/kernel_tests/reader_ops_test.py @@ -28,6 +28,7 @@ import zlib from tensorflow.core.protobuf import config_pb2 from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors_impl +from tensorflow.python.framework import test_util from tensorflow.python.lib.io import tf_record from tensorflow.python.ops import data_flow_ops from tensorflow.python.ops import io_ops @@ -140,147 +141,147 @@ class TFCompressionTestCase(test.TestCase): class IdentityReaderTest(test.TestCase): - def _ExpectRead(self, sess, key, value, expected): - k, v = sess.run([key, value]) + def _ExpectRead(self, key, value, expected): + k, v = self.evaluate([key, value]) self.assertAllEqual(expected, k) self.assertAllEqual(expected, v) + @test_util.run_deprecated_v1 def testOneEpoch(self): - with self.cached_session() as sess: - reader = io_ops.IdentityReader("test_reader") - work_completed = reader.num_work_units_completed() - produced = reader.num_records_produced() - queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) - queued_length = queue.size() - key, value = reader.read(queue) + reader = io_ops.IdentityReader("test_reader") + work_completed = reader.num_work_units_completed() + produced = reader.num_records_produced() + queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) + queued_length = queue.size() + key, value = reader.read(queue) - self.assertAllEqual(0, work_completed.eval()) - self.assertAllEqual(0, produced.eval()) - self.assertAllEqual(0, queued_length.eval()) + self.assertAllEqual(0, self.evaluate(work_completed)) + self.assertAllEqual(0, self.evaluate(produced)) + self.assertAllEqual(0, self.evaluate(queued_length)) - queue.enqueue_many([["A", "B", "C"]]).run() - queue.close().run() - self.assertAllEqual(3, queued_length.eval()) + self.evaluate(queue.enqueue_many([["A", "B", "C"]])) + self.evaluate(queue.close()) + self.assertAllEqual(3, self.evaluate(queued_length)) - self._ExpectRead(sess, key, value, b"A") - self.assertAllEqual(1, produced.eval()) + self._ExpectRead(key, value, b"A") + self.assertAllEqual(1, self.evaluate(produced)) - self._ExpectRead(sess, key, value, b"B") + self._ExpectRead(key, value, b"B") - self._ExpectRead(sess, key, value, b"C") - self.assertAllEqual(3, produced.eval()) - self.assertAllEqual(0, queued_length.eval()) + self._ExpectRead(key, value, b"C") + self.assertAllEqual(3, self.evaluate(produced)) + self.assertAllEqual(0, self.evaluate(queued_length)) - with self.assertRaisesOpError("is closed and has insufficient elements " - "\\(requested 1, current size 0\\)"): - sess.run([key, value]) + with self.assertRaisesOpError("is closed and has insufficient elements " + "\\(requested 1, current size 0\\)"): + self.evaluate([key, value]) - self.assertAllEqual(3, work_completed.eval()) - self.assertAllEqual(3, produced.eval()) - self.assertAllEqual(0, queued_length.eval()) + self.assertAllEqual(3, self.evaluate(work_completed)) + self.assertAllEqual(3, self.evaluate(produced)) + self.assertAllEqual(0, self.evaluate(queued_length)) + @test_util.run_deprecated_v1 def testMultipleEpochs(self): - with self.cached_session() as sess: - reader = io_ops.IdentityReader("test_reader") - queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) - enqueue = queue.enqueue_many([["DD", "EE"]]) - key, value = reader.read(queue) - - enqueue.run() - self._ExpectRead(sess, key, value, b"DD") - self._ExpectRead(sess, key, value, b"EE") - enqueue.run() - self._ExpectRead(sess, key, value, b"DD") - self._ExpectRead(sess, key, value, b"EE") - enqueue.run() - self._ExpectRead(sess, key, value, b"DD") - self._ExpectRead(sess, key, value, b"EE") - queue.close().run() - with self.assertRaisesOpError("is closed and has insufficient elements " - "\\(requested 1, current size 0\\)"): - sess.run([key, value]) - + reader = io_ops.IdentityReader("test_reader") + queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) + enqueue = queue.enqueue_many([["DD", "EE"]]) + key, value = reader.read(queue) + + self.evaluate(enqueue) + self._ExpectRead(key, value, b"DD") + self._ExpectRead(key, value, b"EE") + self.evaluate(enqueue) + self._ExpectRead(key, value, b"DD") + self._ExpectRead(key, value, b"EE") + self.evaluate(enqueue) + self._ExpectRead(key, value, b"DD") + self._ExpectRead(key, value, b"EE") + self.evaluate(queue.close()) + with self.assertRaisesOpError("is closed and has insufficient elements " + "\\(requested 1, current size 0\\)"): + self.evaluate([key, value]) + + @test_util.run_deprecated_v1 def testSerializeRestore(self): - with self.cached_session() as sess: - reader = io_ops.IdentityReader("test_reader") - produced = reader.num_records_produced() - queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) - queue.enqueue_many([["X", "Y", "Z"]]).run() - key, value = reader.read(queue) - - self._ExpectRead(sess, key, value, b"X") - self.assertAllEqual(1, produced.eval()) - state = reader.serialize_state().eval() - - self._ExpectRead(sess, key, value, b"Y") - self._ExpectRead(sess, key, value, b"Z") - self.assertAllEqual(3, produced.eval()) - - queue.enqueue_many([["Y", "Z"]]).run() - queue.close().run() - reader.restore_state(state).run() - self.assertAllEqual(1, produced.eval()) - self._ExpectRead(sess, key, value, b"Y") - self._ExpectRead(sess, key, value, b"Z") - with self.assertRaisesOpError("is closed and has insufficient elements " - "\\(requested 1, current size 0\\)"): - sess.run([key, value]) - self.assertAllEqual(3, produced.eval()) - - self.assertEqual(bytes, type(state)) - - with self.assertRaises(ValueError): - reader.restore_state([]) - - with self.assertRaises(ValueError): - reader.restore_state([state, state]) - - with self.assertRaisesOpError( - "Could not parse state for IdentityReader 'test_reader'"): - reader.restore_state(state[1:]).run() - - with self.assertRaisesOpError( - "Could not parse state for IdentityReader 'test_reader'"): - reader.restore_state(state[:-1]).run() - - with self.assertRaisesOpError( - "Could not parse state for IdentityReader 'test_reader'"): - reader.restore_state(state + b"ExtraJunk").run() - - with self.assertRaisesOpError( - "Could not parse state for IdentityReader 'test_reader'"): - reader.restore_state(b"PREFIX" + state).run() - - with self.assertRaisesOpError( - "Could not parse state for IdentityReader 'test_reader'"): - reader.restore_state(b"BOGUS" + state[5:]).run() - + reader = io_ops.IdentityReader("test_reader") + produced = reader.num_records_produced() + queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) + self.evaluate(queue.enqueue_many([["X", "Y", "Z"]])) + key, value = reader.read(queue) + + self._ExpectRead(key, value, b"X") + self.assertAllEqual(1, self.evaluate(produced)) + state = self.evaluate(reader.serialize_state()) + + self._ExpectRead(key, value, b"Y") + self._ExpectRead(key, value, b"Z") + self.assertAllEqual(3, self.evaluate(produced)) + + self.evaluate(queue.enqueue_many([["Y", "Z"]])) + self.evaluate(queue.close()) + self.evaluate(reader.restore_state(state)) + self.assertAllEqual(1, self.evaluate(produced)) + self._ExpectRead(key, value, b"Y") + self._ExpectRead(key, value, b"Z") + with self.assertRaisesOpError("is closed and has insufficient elements " + "\\(requested 1, current size 0\\)"): + self.evaluate([key, value]) + self.assertAllEqual(3, self.evaluate(produced)) + + self.assertEqual(bytes, type(state)) + + with self.assertRaises(ValueError): + reader.restore_state([]) + + with self.assertRaises(ValueError): + reader.restore_state([state, state]) + + with self.assertRaisesOpError( + "Could not parse state for IdentityReader 'test_reader'"): + self.evaluate(reader.restore_state(state[1:])) + + with self.assertRaisesOpError( + "Could not parse state for IdentityReader 'test_reader'"): + self.evaluate(reader.restore_state(state[:-1])) + + with self.assertRaisesOpError( + "Could not parse state for IdentityReader 'test_reader'"): + self.evaluate(reader.restore_state(state + b"ExtraJunk")) + + with self.assertRaisesOpError( + "Could not parse state for IdentityReader 'test_reader'"): + self.evaluate(reader.restore_state(b"PREFIX" + state)) + + with self.assertRaisesOpError( + "Could not parse state for IdentityReader 'test_reader'"): + self.evaluate(reader.restore_state(b"BOGUS" + state[5:])) + + @test_util.run_deprecated_v1 def testReset(self): - with self.cached_session() as sess: - reader = io_ops.IdentityReader("test_reader") - work_completed = reader.num_work_units_completed() - produced = reader.num_records_produced() - queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) - queued_length = queue.size() - key, value = reader.read(queue) + reader = io_ops.IdentityReader("test_reader") + work_completed = reader.num_work_units_completed() + produced = reader.num_records_produced() + queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) + queued_length = queue.size() + key, value = reader.read(queue) - queue.enqueue_many([["X", "Y", "Z"]]).run() - self._ExpectRead(sess, key, value, b"X") - self.assertLess(0, queued_length.eval()) - self.assertAllEqual(1, produced.eval()) + self.evaluate(queue.enqueue_many([["X", "Y", "Z"]])) + self._ExpectRead(key, value, b"X") + self.assertLess(0, self.evaluate(queued_length)) + self.assertAllEqual(1, self.evaluate(produced)) - self._ExpectRead(sess, key, value, b"Y") - self.assertLess(0, work_completed.eval()) - self.assertAllEqual(2, produced.eval()) + self._ExpectRead(key, value, b"Y") + self.assertLess(0, self.evaluate(work_completed)) + self.assertAllEqual(2, self.evaluate(produced)) - reader.reset().run() - self.assertAllEqual(0, work_completed.eval()) - self.assertAllEqual(0, produced.eval()) - self.assertAllEqual(1, queued_length.eval()) - self._ExpectRead(sess, key, value, b"Z") + self.evaluate(reader.reset()) + self.assertAllEqual(0, self.evaluate(work_completed)) + self.assertAllEqual(0, self.evaluate(produced)) + self.assertAllEqual(1, self.evaluate(queued_length)) + self._ExpectRead(key, value, b"Z") - queue.enqueue_many([["K", "L"]]).run() - self._ExpectRead(sess, key, value, b"K") + self.evaluate(queue.enqueue_many([["K", "L"]])) + self._ExpectRead(key, value, b"K") class WholeFileReaderTest(test.TestCase): @@ -301,44 +302,44 @@ class WholeFileReaderTest(test.TestCase): os.remove(fn) super(WholeFileReaderTest, self).tearDown() - def _ExpectRead(self, sess, key, value, index): - k, v = sess.run([key, value]) + def _ExpectRead(self, key, value, index): + k, v = self.evaluate([key, value]) self.assertAllEqual(compat.as_bytes(self._filenames[index]), k) self.assertAllEqual(self._content[index], v) + @test_util.run_deprecated_v1 def testOneEpoch(self): - with self.cached_session() as sess: - reader = io_ops.WholeFileReader("test_reader") - queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) - queue.enqueue_many([self._filenames]).run() - queue.close().run() - key, value = reader.read(queue) + reader = io_ops.WholeFileReader("test_reader") + queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) + self.evaluate(queue.enqueue_many([self._filenames])) + self.evaluate(queue.close()) + key, value = reader.read(queue) - self._ExpectRead(sess, key, value, 0) - self._ExpectRead(sess, key, value, 1) - self._ExpectRead(sess, key, value, 2) + self._ExpectRead(key, value, 0) + self._ExpectRead(key, value, 1) + self._ExpectRead(key, value, 2) - with self.assertRaisesOpError("is closed and has insufficient elements " - "\\(requested 1, current size 0\\)"): - sess.run([key, value]) + with self.assertRaisesOpError("is closed and has insufficient elements " + "\\(requested 1, current size 0\\)"): + self.evaluate([key, value]) + @test_util.run_deprecated_v1 def testInfiniteEpochs(self): - with self.cached_session() as sess: - reader = io_ops.WholeFileReader("test_reader") - queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) - enqueue = queue.enqueue_many([self._filenames]) - key, value = reader.read(queue) - - enqueue.run() - self._ExpectRead(sess, key, value, 0) - self._ExpectRead(sess, key, value, 1) - enqueue.run() - self._ExpectRead(sess, key, value, 2) - self._ExpectRead(sess, key, value, 0) - self._ExpectRead(sess, key, value, 1) - enqueue.run() - self._ExpectRead(sess, key, value, 2) - self._ExpectRead(sess, key, value, 0) + reader = io_ops.WholeFileReader("test_reader") + queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) + enqueue = queue.enqueue_many([self._filenames]) + key, value = reader.read(queue) + + self.evaluate(enqueue) + self._ExpectRead(key, value, 0) + self._ExpectRead(key, value, 1) + self.evaluate(enqueue) + self._ExpectRead(key, value, 2) + self._ExpectRead(key, value, 0) + self._ExpectRead(key, value, 1) + self.evaluate(enqueue) + self._ExpectRead(key, value, 2) + self._ExpectRead(key, value, 0) class TextLineReaderTest(test.TestCase): @@ -366,47 +367,48 @@ class TextLineReaderTest(test.TestCase): return filenames def _testOneEpoch(self, files): - with self.cached_session() as sess: - reader = io_ops.TextLineReader(name="test_reader") - queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) - key, value = reader.read(queue) + reader = io_ops.TextLineReader(name="test_reader") + queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) + key, value = reader.read(queue) - queue.enqueue_many([files]).run() - queue.close().run() - for i in range(self._num_files): - for j in range(self._num_lines): - k, v = sess.run([key, value]) - self.assertAllEqual("%s:%d" % (files[i], j + 1), compat.as_text(k)) - self.assertAllEqual(self._LineText(i, j), v) + self.evaluate(queue.enqueue_many([files])) + self.evaluate(queue.close()) + for i in range(self._num_files): + for j in range(self._num_lines): + k, v = self.evaluate([key, value]) + self.assertAllEqual("%s:%d" % (files[i], j + 1), compat.as_text(k)) + self.assertAllEqual(self._LineText(i, j), v) - with self.assertRaisesOpError("is closed and has insufficient elements " - "\\(requested 1, current size 0\\)"): - k, v = sess.run([key, value]) + with self.assertRaisesOpError("is closed and has insufficient elements " + "\\(requested 1, current size 0\\)"): + k, v = self.evaluate([key, value]) + @test_util.run_deprecated_v1 def testOneEpochLF(self): self._testOneEpoch(self._CreateFiles(crlf=False)) + @test_util.run_deprecated_v1 def testOneEpochCRLF(self): self._testOneEpoch(self._CreateFiles(crlf=True)) + @test_util.run_deprecated_v1 def testSkipHeaderLines(self): files = self._CreateFiles() - with self.cached_session() as sess: - reader = io_ops.TextLineReader(skip_header_lines=1, name="test_reader") - queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) - key, value = reader.read(queue) + reader = io_ops.TextLineReader(skip_header_lines=1, name="test_reader") + queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) + key, value = reader.read(queue) - queue.enqueue_many([files]).run() - queue.close().run() - for i in range(self._num_files): - for j in range(self._num_lines - 1): - k, v = sess.run([key, value]) - self.assertAllEqual("%s:%d" % (files[i], j + 2), compat.as_text(k)) - self.assertAllEqual(self._LineText(i, j + 1), v) + self.evaluate(queue.enqueue_many([files])) + self.evaluate(queue.close()) + for i in range(self._num_files): + for j in range(self._num_lines - 1): + k, v = self.evaluate([key, value]) + self.assertAllEqual("%s:%d" % (files[i], j + 2), compat.as_text(k)) + self.assertAllEqual(self._LineText(i, j + 1), v) - with self.assertRaisesOpError("is closed and has insufficient elements " - "\\(requested 1, current size 0\\)"): - k, v = sess.run([key, value]) + with self.assertRaisesOpError("is closed and has insufficient elements " + "\\(requested 1, current size 0\\)"): + k, v = self.evaluate([key, value]) class FixedLengthRecordReaderTest(TFCompressionTestCase): @@ -522,56 +524,55 @@ class FixedLengthRecordReaderTest(TFCompressionTestCase): # gap_bytes=hop_bytes-record_bytes def _TestOneEpoch(self, files, num_records, gap_bytes, encoding=None): hop_bytes = 0 if gap_bytes == 0 else self._record_bytes + gap_bytes - with self.cached_session() as sess: - reader = io_ops.FixedLengthRecordReader( - header_bytes=self._header_bytes, - record_bytes=self._record_bytes, - footer_bytes=self._footer_bytes, - hop_bytes=hop_bytes, - encoding=encoding, - name="test_reader") - queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) - key, value = reader.read(queue) - - queue.enqueue_many([files]).run() - queue.close().run() - for i in range(self._num_files): - for j in range(num_records): - k, v = sess.run([key, value]) - self.assertAllEqual("%s:%d" % (files[i], j), compat.as_text(k)) - self.assertAllEqual(self._Record(i, j), v) - - with self.assertRaisesOpError("is closed and has insufficient elements " - "\\(requested 1, current size 0\\)"): - k, v = sess.run([key, value]) + reader = io_ops.FixedLengthRecordReader( + header_bytes=self._header_bytes, + record_bytes=self._record_bytes, + footer_bytes=self._footer_bytes, + hop_bytes=hop_bytes, + encoding=encoding, + name="test_reader") + queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) + key, value = reader.read(queue) + + self.evaluate(queue.enqueue_many([files])) + self.evaluate(queue.close()) + for i in range(self._num_files): + for j in range(num_records): + k, v = self.evaluate([key, value]) + self.assertAllEqual("%s:%d" % (files[i], j), compat.as_text(k)) + self.assertAllEqual(self._Record(i, j), v) + + with self.assertRaisesOpError("is closed and has insufficient elements " + "\\(requested 1, current size 0\\)"): + k, v = self.evaluate([key, value]) def _TestOneEpochWithHopBytes(self, files, num_overlapped_records, encoding=None): - with self.cached_session() as sess: - reader = io_ops.FixedLengthRecordReader( - header_bytes=self._header_bytes, - record_bytes=self._record_bytes, - footer_bytes=self._footer_bytes, - hop_bytes=self._hop_bytes, - encoding=encoding, - name="test_reader") - queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) - key, value = reader.read(queue) - - queue.enqueue_many([files]).run() - queue.close().run() - for i in range(self._num_files): - for j in range(num_overlapped_records): - k, v = sess.run([key, value]) - self.assertAllEqual("%s:%d" % (files[i], j), compat.as_text(k)) - self.assertAllEqual(self._OverlappedRecord(i, j), v) - - with self.assertRaisesOpError("is closed and has insufficient elements " - "\\(requested 1, current size 0\\)"): - k, v = sess.run([key, value]) + reader = io_ops.FixedLengthRecordReader( + header_bytes=self._header_bytes, + record_bytes=self._record_bytes, + footer_bytes=self._footer_bytes, + hop_bytes=self._hop_bytes, + encoding=encoding, + name="test_reader") + queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) + key, value = reader.read(queue) + + self.evaluate(queue.enqueue_many([files])) + self.evaluate(queue.close()) + for i in range(self._num_files): + for j in range(num_overlapped_records): + k, v = self.evaluate([key, value]) + self.assertAllEqual("%s:%d" % (files[i], j), compat.as_text(k)) + self.assertAllEqual(self._OverlappedRecord(i, j), v) + + with self.assertRaisesOpError("is closed and has insufficient elements " + "\\(requested 1, current size 0\\)"): + k, v = self.evaluate([key, value]) + @test_util.run_deprecated_v1 def testOneEpoch(self): for num_records in [0, 7]: # gap_bytes=0: hop_bytes=0 @@ -580,6 +581,7 @@ class FixedLengthRecordReaderTest(TFCompressionTestCase): files = self._CreateFiles(num_records, gap_bytes) self._TestOneEpoch(files, num_records, gap_bytes) + @test_util.run_deprecated_v1 def testGzipOneEpoch(self): for num_records in [0, 7]: # gap_bytes=0: hop_bytes=0 @@ -588,6 +590,7 @@ class FixedLengthRecordReaderTest(TFCompressionTestCase): files = self._CreateGzipFiles(num_records, gap_bytes) self._TestOneEpoch(files, num_records, gap_bytes, encoding="GZIP") + @test_util.run_deprecated_v1 def testZlibOneEpoch(self): for num_records in [0, 7]: # gap_bytes=0: hop_bytes=0 @@ -596,17 +599,20 @@ class FixedLengthRecordReaderTest(TFCompressionTestCase): files = self._CreateZlibFiles(num_records, gap_bytes) self._TestOneEpoch(files, num_records, gap_bytes, encoding="ZLIB") + @test_util.run_deprecated_v1 def testOneEpochWithHopBytes(self): for num_overlapped_records in [0, 2]: files = self._CreateOverlappedRecordFiles(num_overlapped_records) self._TestOneEpochWithHopBytes(files, num_overlapped_records) + @test_util.run_deprecated_v1 def testGzipOneEpochWithHopBytes(self): for num_overlapped_records in [0, 2]: files = self._CreateGzipOverlappedRecordFiles(num_overlapped_records,) self._TestOneEpochWithHopBytes( files, num_overlapped_records, encoding="GZIP") + @test_util.run_deprecated_v1 def testZlibOneEpochWithHopBytes(self): for num_overlapped_records in [0, 2]: files = self._CreateZlibOverlappedRecordFiles(num_overlapped_records) @@ -619,90 +625,91 @@ class TFRecordReaderTest(TFCompressionTestCase): def setUp(self): super(TFRecordReaderTest, self).setUp() + @test_util.run_deprecated_v1 def testOneEpoch(self): files = self._CreateFiles() - with self.cached_session() as sess: - reader = io_ops.TFRecordReader(name="test_reader") - queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) - key, value = reader.read(queue) - - queue.enqueue_many([files]).run() - queue.close().run() - for i in range(self._num_files): - for j in range(self._num_records): - k, v = sess.run([key, value]) - self.assertTrue(compat.as_text(k).startswith("%s:" % files[i])) - self.assertAllEqual(self._Record(i, j), v) - - with self.assertRaisesOpError("is closed and has insufficient elements " - "\\(requested 1, current size 0\\)"): - k, v = sess.run([key, value]) + reader = io_ops.TFRecordReader(name="test_reader") + queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) + key, value = reader.read(queue) + + self.evaluate(queue.enqueue_many([files])) + self.evaluate(queue.close()) + for i in range(self._num_files): + for j in range(self._num_records): + k, v = self.evaluate([key, value]) + self.assertTrue(compat.as_text(k).startswith("%s:" % files[i])) + self.assertAllEqual(self._Record(i, j), v) + with self.assertRaisesOpError("is closed and has insufficient elements " + "\\(requested 1, current size 0\\)"): + k, v = self.evaluate([key, value]) + + @test_util.run_deprecated_v1 def testReadUpTo(self): files = self._CreateFiles() - with self.cached_session() as sess: - reader = io_ops.TFRecordReader(name="test_reader") - queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) - batch_size = 3 - key, value = reader.read_up_to(queue, batch_size) - - queue.enqueue_many([files]).run() - queue.close().run() - num_k = 0 - num_v = 0 - - while True: - try: - k, v = sess.run([key, value]) - # Test reading *up to* batch_size records - self.assertLessEqual(len(k), batch_size) - self.assertLessEqual(len(v), batch_size) - num_k += len(k) - num_v += len(v) - except errors_impl.OutOfRangeError: - break - - # Test that we have read everything - self.assertEqual(self._num_files * self._num_records, num_k) - self.assertEqual(self._num_files * self._num_records, num_v) - + reader = io_ops.TFRecordReader(name="test_reader") + queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) + batch_size = 3 + key, value = reader.read_up_to(queue, batch_size) + + self.evaluate(queue.enqueue_many([files])) + self.evaluate(queue.close()) + num_k = 0 + num_v = 0 + + while True: + try: + k, v = self.evaluate([key, value]) + # Test reading *up to* batch_size records + self.assertLessEqual(len(k), batch_size) + self.assertLessEqual(len(v), batch_size) + num_k += len(k) + num_v += len(v) + except errors_impl.OutOfRangeError: + break + + # Test that we have read everything + self.assertEqual(self._num_files * self._num_records, num_k) + self.assertEqual(self._num_files * self._num_records, num_v) + + @test_util.run_deprecated_v1 def testReadZlibFiles(self): options = tf_record.TFRecordOptions(TFRecordCompressionType.ZLIB) files = self._CreateFiles(options) - with self.cached_session() as sess: - reader = io_ops.TFRecordReader(name="test_reader", options=options) - queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) - key, value = reader.read(queue) - - queue.enqueue_many([files]).run() - queue.close().run() - for i in range(self._num_files): - for j in range(self._num_records): - k, v = sess.run([key, value]) - self.assertTrue(compat.as_text(k).startswith("%s:" % files[i])) - self.assertAllEqual(self._Record(i, j), v) + reader = io_ops.TFRecordReader(name="test_reader", options=options) + queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) + key, value = reader.read(queue) + self.evaluate(queue.enqueue_many([files])) + self.evaluate(queue.close()) + for i in range(self._num_files): + for j in range(self._num_records): + k, v = self.evaluate([key, value]) + self.assertTrue(compat.as_text(k).startswith("%s:" % files[i])) + self.assertAllEqual(self._Record(i, j), v) + + @test_util.run_deprecated_v1 def testReadGzipFiles(self): options = tf_record.TFRecordOptions(TFRecordCompressionType.GZIP) files = self._CreateFiles(options) - with self.cached_session() as sess: - reader = io_ops.TFRecordReader(name="test_reader", options=options) - queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) - key, value = reader.read(queue) + reader = io_ops.TFRecordReader(name="test_reader", options=options) + queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) + key, value = reader.read(queue) - queue.enqueue_many([files]).run() - queue.close().run() - for i in range(self._num_files): - for j in range(self._num_records): - k, v = sess.run([key, value]) - self.assertTrue(compat.as_text(k).startswith("%s:" % files[i])) - self.assertAllEqual(self._Record(i, j), v) + self.evaluate(queue.enqueue_many([files])) + self.evaluate(queue.close()) + for i in range(self._num_files): + for j in range(self._num_records): + k, v = self.evaluate([key, value]) + self.assertTrue(compat.as_text(k).startswith("%s:" % files[i])) + self.assertAllEqual(self._Record(i, j), v) class AsyncReaderTest(test.TestCase): + @test_util.run_deprecated_v1 def testNoDeadlockFromQueue(self): """Tests that reading does not block main execution threads.""" config = config_pb2.ConfigProto( @@ -724,7 +731,7 @@ class AsyncReaderTest(test.TestCase): thread_data.append(thread_data_t(t, queue, output)) # Start all readers. They are all blocked waiting for queue entries. - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) for d in thread_data: d.thread.start() @@ -733,7 +740,7 @@ class AsyncReaderTest(test.TestCase): fname = os.path.join(self.get_temp_dir(), "deadlock.%s.txt" % i) with open(fname, "wb") as f: f.write(("file-%s" % i).encode()) - d.queue.enqueue_many([[fname]]).run() + self.evaluate(d.queue.enqueue_many([[fname]])) d.thread.join() self.assertEqual([[("file-%s" % i).encode()]], d.output) @@ -751,24 +758,25 @@ class LMDBReaderTest(test.TestCase): self.db_path = os.path.join(self.get_temp_dir(), "data.mdb") shutil.copy(path, self.db_path) + @test_util.run_deprecated_v1 def testReadFromFile(self): - with self.cached_session() as sess: - reader = io_ops.LMDBReader(name="test_read_from_file") - queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) - key, value = reader.read(queue) - - queue.enqueue([self.db_path]).run() - queue.close().run() - for i in range(10): - k, v = sess.run([key, value]) - self.assertAllEqual(compat.as_bytes(k), compat.as_bytes(str(i))) - self.assertAllEqual( - compat.as_bytes(v), compat.as_bytes(str(chr(ord("a") + i)))) - - with self.assertRaisesOpError("is closed and has insufficient elements " - "\\(requested 1, current size 0\\)"): - k, v = sess.run([key, value]) - + reader = io_ops.LMDBReader(name="test_read_from_file") + queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) + key, value = reader.read(queue) + + self.evaluate(queue.enqueue([self.db_path])) + self.evaluate(queue.close()) + for i in range(10): + k, v = self.evaluate([key, value]) + self.assertAllEqual(compat.as_bytes(k), compat.as_bytes(str(i))) + self.assertAllEqual( + compat.as_bytes(v), compat.as_bytes(str(chr(ord("a") + i)))) + + with self.assertRaisesOpError("is closed and has insufficient elements " + "\\(requested 1, current size 0\\)"): + k, v = self.evaluate([key, value]) + + @test_util.run_deprecated_v1 def testReadFromSameFile(self): with self.cached_session() as sess: reader1 = io_ops.LMDBReader(name="test_read_from_same_file1") @@ -782,30 +790,31 @@ class LMDBReaderTest(test.TestCase): threads = queue_runner_impl.start_queue_runners(sess, coord=coord) for _ in range(3): for _ in range(10): - k1, v1, k2, v2 = sess.run([key1, value1, key2, value2]) + k1, v1, k2, v2 = self.evaluate([key1, value1, key2, value2]) self.assertAllEqual(compat.as_bytes(k1), compat.as_bytes(k2)) self.assertAllEqual(compat.as_bytes(v1), compat.as_bytes(v2)) coord.request_stop() coord.join(threads) + @test_util.run_deprecated_v1 def testReadFromFolder(self): - with self.cached_session() as sess: - reader = io_ops.LMDBReader(name="test_read_from_folder") - queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) - key, value = reader.read(queue) - - queue.enqueue([self.db_path]).run() - queue.close().run() - for i in range(10): - k, v = sess.run([key, value]) - self.assertAllEqual(compat.as_bytes(k), compat.as_bytes(str(i))) - self.assertAllEqual( - compat.as_bytes(v), compat.as_bytes(str(chr(ord("a") + i)))) - - with self.assertRaisesOpError("is closed and has insufficient elements " - "\\(requested 1, current size 0\\)"): - k, v = sess.run([key, value]) - + reader = io_ops.LMDBReader(name="test_read_from_folder") + queue = data_flow_ops.FIFOQueue(99, [dtypes.string], shapes=()) + key, value = reader.read(queue) + + self.evaluate(queue.enqueue([self.db_path])) + self.evaluate(queue.close()) + for i in range(10): + k, v = self.evaluate([key, value]) + self.assertAllEqual(compat.as_bytes(k), compat.as_bytes(str(i))) + self.assertAllEqual( + compat.as_bytes(v), compat.as_bytes(str(chr(ord("a") + i)))) + + with self.assertRaisesOpError("is closed and has insufficient elements " + "\\(requested 1, current size 0\\)"): + k, v = self.evaluate([key, value]) + + @test_util.run_deprecated_v1 def testReadFromFileRepeatedly(self): with self.cached_session() as sess: reader = io_ops.LMDBReader(name="test_read_from_file_repeated") @@ -819,7 +828,7 @@ class LMDBReaderTest(test.TestCase): for _ in range(3): # Go over all 10 records each time. for j in range(10): - k, v = sess.run([key, value]) + k, v = self.evaluate([key, value]) self.assertAllEqual(compat.as_bytes(k), compat.as_bytes(str(j))) self.assertAllEqual( compat.as_bytes(v), compat.as_bytes(str(chr(ord("a") + j)))) diff --git a/tensorflow/python/kernel_tests/record_input_test.py b/tensorflow/python/kernel_tests/record_input_test.py index ebb9872f22..ad8188b372 100644 --- a/tensorflow/python/kernel_tests/record_input_test.py +++ b/tensorflow/python/kernel_tests/record_input_test.py @@ -20,6 +20,7 @@ from __future__ import print_function import os +from tensorflow.python.framework import test_util from tensorflow.python.framework.errors_impl import NotFoundError from tensorflow.python.lib.io import tf_record from tensorflow.python.ops import data_flow_ops @@ -54,7 +55,7 @@ class RecordInputOpTest(test.TestCase): batch_size=1, name="record_input").get_yield_op() - self.assertEqual(sess.run(yield_op), b"0000000000") + self.assertEqual(self.evaluate(yield_op), b"0000000000") def testRecordInputSimpleGzip(self): with self.cached_session() as sess: @@ -73,7 +74,7 @@ class RecordInputOpTest(test.TestCase): compression_type=tf_record.TFRecordCompressionType.GZIP).get_yield_op( ) - self.assertEqual(sess.run(yield_op), b"0000000000") + self.assertEqual(self.evaluate(yield_op), b"0000000000") def testRecordInputSimpleZlib(self): with self.cached_session() as sess: @@ -92,8 +93,9 @@ class RecordInputOpTest(test.TestCase): compression_type=tf_record.TFRecordCompressionType.ZLIB).get_yield_op( ) - self.assertEqual(sess.run(yield_op), b"0000000000") + self.assertEqual(self.evaluate(yield_op), b"0000000000") + @test_util.run_deprecated_v1 def testRecordInputEpochs(self): files = 100 records_per_file = 100 @@ -117,7 +119,7 @@ class RecordInputOpTest(test.TestCase): for _ in range(3): epoch_set = set() for _ in range(int(files * records_per_file / batches)): - op_list = sess.run(yield_op) + op_list = self.evaluate(yield_op) self.assertTrue(len(op_list) is batches) for r in op_list: self.assertTrue(r[0] not in epoch_set) @@ -138,16 +140,18 @@ class RecordInputOpTest(test.TestCase): yield_op = records.get_yield_op() for _ in range(50): - sess.run(yield_op) + self.evaluate(yield_op) + @test_util.run_deprecated_v1 def testEmptyGlob(self): with self.cached_session() as sess: record_input = data_flow_ops.RecordInput(file_pattern="foo") yield_op = record_input.get_yield_op() - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) with self.assertRaises(NotFoundError): - sess.run(yield_op) + self.evaluate(yield_op) + @test_util.run_deprecated_v1 def testBufferTooSmall(self): files = 10 records_per_file = 10 @@ -171,7 +175,7 @@ class RecordInputOpTest(test.TestCase): for _ in range(3): epoch_set = set() for _ in range(int(files * records_per_file / batches)): - op_list = sess.run(yield_op) + op_list = self.evaluate(yield_op) self.assertTrue(len(op_list) is batches) for r in op_list: self.assertTrue(r[0] not in epoch_set) diff --git a/tensorflow/python/kernel_tests/reduce_benchmark_test.py b/tensorflow/python/kernel_tests/reduce_benchmark_test.py index 3a2fb81157..ef9c4c350f 100644 --- a/tensorflow/python/kernel_tests/reduce_benchmark_test.py +++ b/tensorflow/python/kernel_tests/reduce_benchmark_test.py @@ -81,7 +81,7 @@ class ReduceBenchmarks(test.Benchmark): grad, = gradients_impl.gradients(reduction, tensor) def fn(): - sess.run(grad.op) + self.evaluate(grad.op) self._run(fn, 10000) @@ -98,7 +98,7 @@ class ReduceBenchmarks(test.Benchmark): grad, = gradients_impl.gradients(reduction, tensor) def fn(): - sess.run(grad.op) + self.evaluate(grad.op) self._run(fn, 10000) diff --git a/tensorflow/python/kernel_tests/reduce_join_op_test.py b/tensorflow/python/kernel_tests/reduce_join_op_test.py index 3bb4986313..49b6620779 100644 --- a/tensorflow/python/kernel_tests/reduce_join_op_test.py +++ b/tensorflow/python/kernel_tests/reduce_join_op_test.py @@ -25,6 +25,7 @@ from six.moves import xrange # pylint: disable=redefined-builtin from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import string_ops from tensorflow.python.platform import test @@ -119,7 +120,7 @@ class ReduceJoinTest(UnicodeTestCase): axis=axis, keep_dims=keep_dims, separator=separator) - output_array = output.eval() + output_array = self.evaluate(output) self.assertAllEqualUnicode(truth, output_array) self.assertAllEqual(truth_shape, output.get_shape()) @@ -149,10 +150,10 @@ class ReduceJoinTest(UnicodeTestCase): if not axis: truth = constant_op.constant(truth) truth_squeezed = array_ops.squeeze(truth, axis=axis) - output_array = output.eval() - output_keep_dims_array = output_keep_dims.eval() - truth_array = truth.eval() - truth_squeezed_array = truth_squeezed.eval() + output_array = self.evaluate(output) + output_keep_dims_array = self.evaluate(output_keep_dims) + truth_array = self.evaluate(truth) + truth_squeezed_array = self.evaluate(truth_squeezed) self.assertAllEqualUnicode(truth_array, output_keep_dims_array) self.assertAllEqualUnicode(truth_squeezed_array, output_array) self.assertAllEqual(truth.get_shape(), output_keep_dims.get_shape()) @@ -230,6 +231,7 @@ class ReduceJoinTest(UnicodeTestCase): axis=1, separator=" ") + @test_util.run_deprecated_v1 def testUnknownShape(self): input_array = [["a"], ["b"]] truth = ["ab"] @@ -241,6 +243,7 @@ class ReduceJoinTest(UnicodeTestCase): self.assertAllEqualUnicode(truth, output_array) self.assertAllEqual(truth_shape, reduced.get_shape()) + @test_util.run_deprecated_v1 def testUnknownIndices(self): input_array = [["this", "is", "a", "test"], ["please", "do", "not", "panic"]] @@ -297,6 +300,7 @@ class ReduceJoinTest(UnicodeTestCase): for permutation in itertools.permutations(xrange(num_dims), i): self._testMultipleReduceJoin(input_array, axis=permutation) + @test_util.run_deprecated_v1 def testInvalidReductionIndices(self): with self.cached_session(): with self.assertRaisesRegexp(ValueError, "Invalid reduction dim"): @@ -318,13 +322,14 @@ class ReduceJoinTest(UnicodeTestCase): # Reduction that drops the dim of size 0. output = string_ops.reduce_join(inputs=inputs, axis=0) - self.assertAllEqualUnicode([""], output.eval()) + self.assertAllEqualUnicode([""], self.evaluate(output)) # Reduction that keeps the dim of size 0. output = string_ops.reduce_join(inputs=inputs, axis=1) - output_shape = output.eval().shape + output_shape = self.evaluate(output).shape self.assertAllEqual([0], output_shape) + @test_util.run_deprecated_v1 def testInvalidArgsUnknownShape(self): with self.cached_session(): placeholder = array_ops.placeholder(dtypes.string, name="placeholder") @@ -335,6 +340,7 @@ class ReduceJoinTest(UnicodeTestCase): with self.assertRaisesOpError("Duplicate reduction dimension 1"): duplicate_index.eval(feed_dict={placeholder.name: [[""]]}) + @test_util.run_deprecated_v1 def testInvalidArgsUnknownIndices(self): with self.cached_session(): placeholder = array_ops.placeholder(dtypes.int32, name="placeholder") diff --git a/tensorflow/python/kernel_tests/reduction_ops_test.py b/tensorflow/python/kernel_tests/reduction_ops_test.py index 2ac3996e25..67a89461f3 100644 --- a/tensorflow/python/kernel_tests/reduction_ops_test.py +++ b/tensorflow/python/kernel_tests/reduction_ops_test.py @@ -27,6 +27,7 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import math_ops @@ -60,6 +61,7 @@ class ReducedShapeTest(test.TestCase): output = math_ops.reduced_shape(shape, axes=axes) self.assertAllEqual(output.eval(), result) + @test_util.run_deprecated_v1 def testSimple(self): with self.cached_session(): self._check([3], [], [3]) @@ -69,6 +71,7 @@ class ReducedShapeTest(test.TestCase): self._check([5, 3], [1], [5, 1]) self._check([5, 3], [0, 1], [1, 1]) + @test_util.run_deprecated_v1 def testZeros(self): """Check that reduced_shape does the right thing with zero dimensions.""" with self.cached_session(): @@ -83,6 +86,7 @@ class ReducedShapeTest(test.TestCase): self._check([3, 0], [1], [3, 1]) self._check([3, 0], [0, 1], [1, 1]) + @test_util.run_deprecated_v1 def testNegAxes(self): with self.cached_session(): self._check([10, 10, 10], [-1], [10, 10, 1]) @@ -94,6 +98,7 @@ class ReducedShapeTest(test.TestCase): class ReductionUnknownShape(test.TestCase): + @test_util.run_deprecated_v1 def testBasic(self): with self.cached_session(): for dtype, reductions in [(dtypes.float32, @@ -185,9 +190,10 @@ class SumReductionTest(BaseReductionTest): for dtype in [dtypes.int64, dtypes.int32]: with self.cached_session(use_gpu=True) as sess: v = math_ops.reduce_sum([0, 0], constant_op.constant(0, dtype=dtype)) - tf_v = sess.run(v) + tf_v = self.evaluate(v) self.assertAllEqual(tf_v, 0) + @test_util.run_deprecated_v1 def testInfinity(self): for dtype in [np.float32, np.float64]: for special_value_x in [-np.inf, np.inf]: @@ -195,11 +201,13 @@ class SumReductionTest(BaseReductionTest): np_arr = np.array([special_value_x, special_value_y]).astype(dtype) self._compareAll(np_arr, None) + @test_util.run_deprecated_v1 def testInt32(self): for rank in range(1, _MAX_RANK + 1): np_arr = self._makeIncremental((2,) * rank, dtypes.int32) self._compareAllAxes(np_arr) + @test_util.run_deprecated_v1 def testFloat16(self): for rank in range(1, _MAX_RANK + 1): np_arr = self._makeIncremental((2,) * rank, dtypes.float16) @@ -216,9 +224,10 @@ class SumReductionTest(BaseReductionTest): tf_arr = variables.Variable(arr) variables.global_variables_initializer().run() tf_mean = math_ops.reduce_mean(tf_arr, 0, False) - tf_out_mean = sess.run(tf_mean) + tf_out_mean = self.evaluate(tf_mean) self.assertAllClose(tf_out_mean, 1.) + @test_util.run_deprecated_v1 def testFloat32(self): for rank in range(1, _MAX_RANK + 1): np_arr = self._makeIncremental((2,) * rank, dtypes.float32) @@ -238,7 +247,7 @@ class SumReductionTest(BaseReductionTest): with self.session(graph=ops.Graph(), use_gpu=True) as sess: tf_row_sum = self._tf_reduce(arr, 1, False) tf_col_sum = self._tf_reduce(arr, 0, False) - tf_out_row, tf_out_col = sess.run([tf_row_sum, tf_col_sum]) + tf_out_row, tf_out_col = self.evaluate([tf_row_sum, tf_col_sum]) self.assertAllClose(col_sum, tf_out_col) self.assertAllClose(row_sum, tf_out_row) @@ -252,25 +261,29 @@ class SumReductionTest(BaseReductionTest): with self.session(graph=ops.Graph(), use_gpu=True) as sess: tf_sum_xz = self._tf_reduce(arr, [0, 2], False) tf_sum_y = self._tf_reduce(arr, 1, False) - tf_out_sum_xz, tf_out_sum_y = sess.run([tf_sum_xz, tf_sum_y]) + tf_out_sum_xz, tf_out_sum_y = self.evaluate([tf_sum_xz, tf_sum_y]) self.assertAllClose(sum_y, tf_out_sum_y) self.assertAllClose(sum_xz, tf_out_sum_xz) + @test_util.run_deprecated_v1 def testFloat64(self): for rank in range(1, _MAX_RANK + 1): np_arr = self._makeIncremental((2,) * rank, dtypes.float64) self._compareAllAxes(np_arr) + @test_util.run_deprecated_v1 def testComplex64(self): for rank in range(1, _MAX_RANK + 1): np_arr = self._makeIncremental((2,) * rank, dtypes.complex64) self._compareAllAxes(np_arr) + @test_util.run_deprecated_v1 def testComplex128(self): for rank in range(1, _MAX_RANK + 1): np_arr = self._makeIncremental((2,) * rank, dtypes.complex128) self._compareAllAxes(np_arr) + @test_util.run_deprecated_v1 def testInvalidIndex(self): np_arr = np.arange(0, 10).reshape([2, 5]).astype(np.float32) input_tensor = ops.convert_to_tensor(np_arr) @@ -284,6 +297,7 @@ class SumReductionTest(BaseReductionTest): ValueError, lambda e: "Invalid reduction dimension" in str(e)): math_ops.reduce_sum(input_tensor, [0, 2]) + @test_util.run_deprecated_v1 def testPartialShapes(self): np.random.seed(1618) @@ -317,6 +331,7 @@ class SumReductionTest(BaseReductionTest): c_unknown_indices, unknown_indices, keepdims=True) self.assertEqual(2, s_unknown_indices_keep.get_shape().rank) + @test_util.run_deprecated_v1 def testWrongShapeForReductionIndices(self): reduction_axes = [[1], [2]] c_unknown = array_ops.placeholder(dtypes.float32) @@ -326,6 +341,7 @@ class SumReductionTest(BaseReductionTest): # Int64?? + @test_util.run_deprecated_v1 def testGradient(self): for dtype in [ dtypes.float32, dtypes.float64, dtypes.complex64, dtypes.complex128 @@ -333,6 +349,7 @@ class SumReductionTest(BaseReductionTest): x = self._makeIncremental([2, 3, 4, 2], dtype) self._compareGradientAxes(x) + @test_util.run_deprecated_v1 def testHighRank(self): # Do a bunch of random high dimensional reductions np.random.seed(42) @@ -350,11 +367,13 @@ class SumReductionTest(BaseReductionTest): np.arange(1, rank, 2)): self._compareAll(data, axes) + @test_util.run_deprecated_v1 def testExpand(self): # Reduce an empty tensor to a nonempty tensor x = np.zeros((5, 0)) self._compareAll(x, [1]) + @test_util.run_deprecated_v1 def testEmptyGradients(self): with self.session(use_gpu=True): x = array_ops.zeros([0, 3]) @@ -362,6 +381,7 @@ class SumReductionTest(BaseReductionTest): error = gradient_checker.compute_gradient_error(x, [0, 3], y, [0]) self.assertEqual(error, 0) + @test_util.run_deprecated_v1 def testDegenerate(self): with self.session(use_gpu=True): for dtype in (dtypes.float16, dtypes.float32, dtypes.float64, @@ -400,9 +420,10 @@ class MeanReductionTest(BaseReductionTest): for dtype in [dtypes.int64, dtypes.int32]: with self.cached_session(use_gpu=True) as sess: v = math_ops.reduce_mean([0, 0], constant_op.constant(0, dtype=dtype)) - tf_v = sess.run(v) + tf_v = self.evaluate(v) self.assertAllEqual(tf_v, 0) + @test_util.run_deprecated_v1 def testInfinity(self): for dtype in [np.float32, np.float64]: for special_value_x in [-np.inf, np.inf]: @@ -410,37 +431,44 @@ class MeanReductionTest(BaseReductionTest): np_arr = np.array([special_value_x, special_value_y]).astype(dtype) self._compareAll(np_arr, None) + @test_util.run_deprecated_v1 def testInt32(self): for rank in range(1, _MAX_RANK + 1): np_arr = self._makeIncremental((2,) * rank, dtypes.int32) self._compareAllAxes(np_arr) + @test_util.run_deprecated_v1 def testFloat32(self): for rank in range(1, _MAX_RANK + 1): np_arr = self._makeIncremental((2,) * rank, dtypes.float32) self._compareAllAxes(np_arr) + @test_util.run_deprecated_v1 def testFloat64(self): for rank in range(1, _MAX_RANK + 1): np_arr = self._makeIncremental((2,) * rank, dtypes.float64) self._compareAllAxes(np_arr) + @test_util.run_deprecated_v1 def testComplex64(self): for rank in range(1, _MAX_RANK + 1): np_arr = self._makeIncremental((2,) * rank, dtypes.complex64) self._compareAllAxes(np_arr) + @test_util.run_deprecated_v1 def testComplex128(self): for rank in range(1, _MAX_RANK + 1): np_arr = self._makeIncremental((2,) * rank, dtypes.complex128) self._compareAllAxes(np_arr) + @test_util.run_deprecated_v1 def testGradient(self): s = [2, 3, 4, 2] for dtype in [dtypes.float32, dtypes.float64]: x = self._makeIncremental(s, dtype) self._compareGradientAxes(x, rtol=1e-3, atol=1e-3) + @test_util.run_deprecated_v1 def testEmptyGradients(self): with self.session(use_gpu=True): x = array_ops.zeros([0, 3]) @@ -448,6 +476,7 @@ class MeanReductionTest(BaseReductionTest): error = gradient_checker.compute_gradient_error(x, [0, 3], y, [0]) self.assertEqual(error, 0) + @test_util.run_deprecated_v1 def testDegenerate(self): with self.session(use_gpu=True): for dtype in (dtypes.float16, dtypes.float32, dtypes.float64): @@ -473,9 +502,10 @@ class ProdReductionTest(BaseReductionTest): for dtype in [dtypes.int64, dtypes.int32]: with self.cached_session(use_gpu=True) as sess: v = math_ops.reduce_prod([0, 0], constant_op.constant(0, dtype=dtype)) - tf_v = sess.run(v) + tf_v = self.evaluate(v) self.assertAllEqual(tf_v, 0) + @test_util.run_deprecated_v1 def testInfinity(self): for dtype in [np.float32, np.float64]: for special_value_x in [-np.inf, np.inf]: @@ -483,6 +513,7 @@ class ProdReductionTest(BaseReductionTest): np_arr = np.array([special_value_x, special_value_y]).astype(dtype) self._compareAll(np_arr, None) + @test_util.run_deprecated_v1 def testInt32(self): # Numpy automatically upgrades the type of np.prod from int32 to int64, so # Numpy does not overflow an int32 np.prod while TensorFlow does. To avoid @@ -491,26 +522,31 @@ class ProdReductionTest(BaseReductionTest): np_arr = self._makeIncremental((2,) * rank, dtypes.int32) / 2 self._compareAllAxes(np_arr) + @test_util.run_deprecated_v1 def testFloat32(self): for rank in range(1, _MAX_RANK + 1): np_arr = self._makeIncremental((2,) * rank, dtypes.float32) self._compareAllAxes(np_arr) + @test_util.run_deprecated_v1 def testFloat64(self): for rank in range(1, _MAX_RANK + 1): np_arr = self._makeIncremental((2,) * rank, dtypes.float64) self._compareAllAxes(np_arr) + @test_util.run_deprecated_v1 def testComplex64(self): for rank in range(1, _MAX_RANK + 1): np_arr = self._makeIncremental((2,) * rank, dtypes.complex64) self._compareAllAxes(np_arr) + @test_util.run_deprecated_v1 def testComplex128(self): for rank in range(1, _MAX_RANK + 1): np_arr = self._makeIncremental((2,) * rank, dtypes.complex128) self._compareAllAxes(np_arr) + @test_util.run_deprecated_v1 def testGradientWithZeros(self): s = [2, 3, 4, 2] x = self._makeIncremental(s, dtypes.float32) / 20. @@ -533,6 +569,7 @@ class ProdReductionTest(BaseReductionTest): x4[:, :, :, :] = 0 self._compareGradientAxes(x4, rtol=1e-3, atol=1e-3) + @test_util.run_deprecated_v1 def testEmptyGradients(self): with self.session(use_gpu=True): x = array_ops.zeros([0, 3]) @@ -540,6 +577,7 @@ class ProdReductionTest(BaseReductionTest): error = gradient_checker.compute_gradient_error(x, [0, 3], y, [0]) self.assertEqual(error, 0) + @test_util.run_deprecated_v1 def testDegenerate(self): with self.session(use_gpu=True): for dtype in (dtypes.float16, dtypes.float32, dtypes.float64): @@ -562,7 +600,7 @@ class MinReductionTest(test.TestCase): if reduction_axes is not None: reduction_axes = np.array(reduction_axes).astype(np.int32) tf_ans = math_ops.reduce_min(x, reduction_axes, keepdims) - out = tf_ans.eval() + out = self.evaluate(tf_ans) self.assertAllClose(np_ans, out) self.assertShapeEqual(np_ans, tf_ans) @@ -576,9 +614,10 @@ class MinReductionTest(test.TestCase): for dtype in [dtypes.int64, dtypes.int32]: with self.cached_session(use_gpu=True) as sess: v = math_ops.reduce_min([0, 0], constant_op.constant(0, dtype=dtype)) - tf_v = sess.run(v) + tf_v = self.evaluate(v) self.assertAllEqual(tf_v, 0) + @test_util.run_deprecated_v1 def testInfinity(self): for dtype in [np.float32, np.float64]: for special_value_x in [-np.inf, np.inf]: @@ -614,6 +653,7 @@ class MinReductionTest(test.TestCase): self._compareAll(np_arr, [0, 2]) self._compareAll(np_arr, [0, 1, 2]) + @test_util.run_deprecated_v1 def testGradient(self): s = [2, 3, 4, 2] x = np.arange(1.0, 49.0).reshape(s).astype(np.float64) @@ -624,6 +664,7 @@ class MinReductionTest(test.TestCase): t, s, su, [2, 2], x_init_value=x, delta=1) self.assertAllClose(jacob_t, jacob_n, rtol=1e-8, atol=1e-8) + @test_util.run_deprecated_v1 def testGradient2(self): s = [2, 3, 4, 2] x = np.arange(1.0, 49.0).reshape(s).astype(np.float64) @@ -634,6 +675,7 @@ class MinReductionTest(test.TestCase): t, s, su, [2, 4, 2], x_init_value=x, delta=1) self.assertAllClose(jacob_t, jacob_n, rtol=1e-8, atol=1e-8) + @test_util.run_deprecated_v1 def testGradient3(self): s = [2, 3, 4, 2] x = np.arange(1.0, 49.0).reshape(s).astype(np.float64) @@ -644,6 +686,7 @@ class MinReductionTest(test.TestCase): t, s, su, [2, 3, 2], x_init_value=x, delta=1) self.assertAllClose(jacob_t, jacob_n, rtol=1e-8, atol=1e-8) + @test_util.run_deprecated_v1 def testGradient4(self): s = [2, 3, 4, 2] x = np.arange(1.0, 49.0).reshape(s).astype(np.float64) @@ -654,6 +697,7 @@ class MinReductionTest(test.TestCase): t, s, su, [1], x_init_value=x, delta=1) self.assertAllClose(jacob_t, jacob_n, rtol=1e-8, atol=1e-8) + @test_util.run_deprecated_v1 def testEmptyGradients(self): with self.cached_session(): x = array_ops.zeros([0, 3]) @@ -675,7 +719,7 @@ class MaxReductionTest(test.TestCase): if reduction_axes is not None: reduction_axes = np.array(reduction_axes).astype(np.int32) tf_ans = math_ops.reduce_max(x, reduction_axes, keepdims) - out = tf_ans.eval() + out = self.evaluate(tf_ans) self.assertAllClose(np_ans, out) self.assertShapeEqual(np_ans, tf_ans) @@ -689,9 +733,10 @@ class MaxReductionTest(test.TestCase): for dtype in [dtypes.int64, dtypes.int32]: with self.cached_session(use_gpu=True) as sess: v = math_ops.reduce_max([0, 0], constant_op.constant(0, dtype=dtype)) - tf_v = sess.run(v) + tf_v = self.evaluate(v) self.assertAllEqual(tf_v, 0) + @test_util.run_deprecated_v1 def testInfinity(self): for dtype in [np.float32, np.float64]: for special_value_x in [-np.inf, np.inf]: @@ -741,6 +786,7 @@ class MaxReductionTest(test.TestCase): self._compareAll(np_arr, [0, 2]) self._compareAll(np_arr, [0, 1, 2]) + @test_util.run_deprecated_v1 def testGradient(self): s = [2, 3, 4, 2] x = np.arange(-49.0, -1.0).reshape(s).astype(np.float64) @@ -751,6 +797,7 @@ class MaxReductionTest(test.TestCase): t, s, su, [2, 2], x_init_value=x, delta=1) self.assertAllClose(jacob_t, jacob_n, rtol=1e-8, atol=1e-8) + @test_util.run_deprecated_v1 def testGradient2(self): s = [2, 3, 4, 2] x = np.arange(-49.0, -1.0).reshape(s).astype(np.float64) @@ -761,6 +808,7 @@ class MaxReductionTest(test.TestCase): t, s, su, [2, 4, 2], x_init_value=x, delta=1) self.assertAllClose(jacob_t, jacob_n, rtol=1e-8, atol=1e-8) + @test_util.run_deprecated_v1 def testGradient3(self): s = [2, 3, 4, 2] x = np.arange(-49.0, -1.0).reshape(s).astype(np.float64) @@ -771,6 +819,7 @@ class MaxReductionTest(test.TestCase): t, s, su, [2, 3, 2], x_init_value=x, delta=1) self.assertAllClose(jacob_t, jacob_n, rtol=1e-8, atol=1e-8) + @test_util.run_deprecated_v1 def testGradient4(self): s = [2, 3, 4, 2] x = np.arange(-49.0, -1.0).reshape(s).astype(np.float64) @@ -781,6 +830,7 @@ class MaxReductionTest(test.TestCase): t, s, su, [1], x_init_value=x, delta=1) self.assertAllClose(jacob_t, jacob_n, rtol=1e-8, atol=1e-8) + @test_util.run_deprecated_v1 def testEmptyGradients(self): with self.cached_session(): x = array_ops.zeros([0, 3]) @@ -802,7 +852,7 @@ class AllReductionTest(test.TestCase): if reduction_axes is not None: reduction_axes = np.array(reduction_axes).astype(np.int32) tf_ans = math_ops.reduce_all(x, reduction_axes, keepdims) - out = tf_ans.eval() + out = self.evaluate(tf_ans) self.assertAllEqual(np_ans, out) self.assertShapeEqual(np_ans, tf_ans) @@ -817,7 +867,7 @@ class AllReductionTest(test.TestCase): with self.session(use_gpu=True) as sess: v = math_ops.reduce_all([True, True], constant_op.constant(0, dtype=dtype)) - tf_v = sess.run(v) + tf_v = self.evaluate(v) self.assertAllEqual(tf_v, True) def testAll3D(self): @@ -851,7 +901,7 @@ class AnyReductionTest(test.TestCase): if reduction_axes is not None: reduction_axes = np.array(reduction_axes).astype(np.int32) tf_ans = math_ops.reduce_any(x, reduction_axes, keepdims) - out = tf_ans.eval() + out = self.evaluate(tf_ans) self.assertAllEqual(np_ans, out) self.assertShapeEqual(np_ans, tf_ans) @@ -866,7 +916,7 @@ class AnyReductionTest(test.TestCase): with self.session(use_gpu=True) as sess: v = math_ops.reduce_any([True, True], constant_op.constant(0, dtype=dtype)) - tf_v = sess.run(v) + tf_v = self.evaluate(v) self.assertAllEqual(tf_v, True) def testAll3D(self): @@ -913,6 +963,7 @@ class CountNonzeroReductionTest(test.TestCase): self._compare(x, reduction_axes, True, use_gpu=True, feed_dict=feed_dict) self._compare(x, reduction_axes, True, use_gpu=False, feed_dict=feed_dict) + @test_util.run_deprecated_v1 def testBoolReduce1D(self): # Create a 1D array of floats np_arr = np.asarray([False, False, True, False, False, True]) @@ -920,11 +971,13 @@ class CountNonzeroReductionTest(test.TestCase): self._compareAll(np_arr, []) self._compareAll(np_arr, [0]) + @test_util.run_deprecated_v1 def testFloatReduce1D(self): # Create a 1D array of floats np_arr = np.asarray([0.0, 1.0, -1.0, 0.0, 0.0, 3.0]).astype(np.float32) self._compareAll(np_arr, [0]) + @test_util.run_deprecated_v1 def testFloatReduce4D(self): # Create a 4D array of floats and reduce across some # dimensions @@ -944,11 +997,13 @@ class CountNonzeroReductionTest(test.TestCase): self._compareAll(np_arr, [1, 2, 3]) self._compareAll(np_arr, [0, 1, 2, 3]) + @test_util.run_deprecated_v1 def testExpand(self): # Reduce an empty tensor to a nonempty tensor x = np.zeros((5, 0)) self._compareAll(x, [1]) + @test_util.run_deprecated_v1 def testDegenerate(self): for use_gpu in False, True: with self.cached_session(use_gpu=use_gpu): @@ -962,8 +1017,9 @@ class CountNonzeroReductionTest(test.TestCase): # Test case for GitHub issue 18712 with self.cached_session() as sess: v = math_ops.count_nonzero(constant_op.constant(["test"])) - self.assertAllClose(sess.run(v), 1) + self.assertAllClose(self.evaluate(v), 1) + @test_util.run_deprecated_v1 def testStringReduce1D(self): # Create a 1D array of strings x = np.asarray(["", "", "a", "", "", "b"]) @@ -974,6 +1030,7 @@ class CountNonzeroReductionTest(test.TestCase): self._compare(x, [], keepdims=True, zero=np.str("")) self._compare(x, [0], keepdims=True, zero=np.str("")) + @test_util.run_deprecated_v1 def testStringReduce2D(self): # Create a 2D array of strings x = np.asarray([["", "", "a", "", "", "b"], diff --git a/tensorflow/python/kernel_tests/regex_full_match_op_test.py b/tensorflow/python/kernel_tests/regex_full_match_op_test.py index 98746e7d9b..488ec85ab2 100644 --- a/tensorflow/python/kernel_tests/regex_full_match_op_test.py +++ b/tensorflow/python/kernel_tests/regex_full_match_op_test.py @@ -23,6 +23,7 @@ from absl.testing import parameterized from tensorflow.python.compat import compat from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import gen_string_ops from tensorflow.python.ops import string_ops from tensorflow.python.platform import test @@ -33,6 +34,7 @@ from tensorflow.python.platform import test (gen_string_ops.static_regex_full_match)) class RegexFullMatchOpVariantsTest(test.TestCase, parameterized.TestCase): + @test_util.run_deprecated_v1 def testRegexFullMatch(self, op): values = ["abaaba", "abcdabcde"] with self.cached_session(): @@ -40,6 +42,7 @@ class RegexFullMatchOpVariantsTest(test.TestCase, parameterized.TestCase): matched = op(input_tensor, "a.*a").eval() self.assertAllEqual([True, False], matched) + @test_util.run_deprecated_v1 def testRegexFullMatchTwoDims(self, op): values = [["abaaba", "abcdabcde"], ["acdcba", "ebcda"]] with self.cached_session(): @@ -47,6 +50,7 @@ class RegexFullMatchOpVariantsTest(test.TestCase, parameterized.TestCase): matched = op(input_tensor, "a.*a").eval() self.assertAllEqual([[True, False], [True, False]], matched) + @test_util.run_deprecated_v1 def testEmptyMatch(self, op): values = ["abc", "1"] with self.cached_session(): @@ -54,6 +58,7 @@ class RegexFullMatchOpVariantsTest(test.TestCase, parameterized.TestCase): matched = op(input_tensor, "").eval() self.assertAllEqual([False, False], matched) + @test_util.run_deprecated_v1 def testInvalidPattern(self, op): values = ["abc", "1"] with self.cached_session(): @@ -61,11 +66,12 @@ class RegexFullMatchOpVariantsTest(test.TestCase, parameterized.TestCase): invalid_pattern = "A[" matched = op(input_tensor, invalid_pattern) with self.assertRaisesOpError("Invalid pattern"): - matched.eval() + self.evaluate(matched) class RegexFullMatchOpTest(test.TestCase): + @test_util.run_deprecated_v1 def testRegexFullMatchDelegation(self): with compat.forward_compatibility_horizon(2018, 11, 1): with self.cached_session(): @@ -78,6 +84,7 @@ class RegexFullMatchOpTest(test.TestCase): op_tensor = string_ops.regex_full_match(input_tensor, pattern_tensor) self.assertTrue(op_tensor.name.startswith("RegexFullMatch"), op.name) + @test_util.run_deprecated_v1 def testStaticRegexFullMatchDelegation(self): with compat.forward_compatibility_horizon(2018, 11, 20): with self.cached_session(): diff --git a/tensorflow/python/kernel_tests/regex_replace_op_test.py b/tensorflow/python/kernel_tests/regex_replace_op_test.py index d9b7ed28d2..6c7dfee7b4 100644 --- a/tensorflow/python/kernel_tests/regex_replace_op_test.py +++ b/tensorflow/python/kernel_tests/regex_replace_op_test.py @@ -22,6 +22,7 @@ from absl.testing import parameterized from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import gen_string_ops from tensorflow.python.ops import string_ops from tensorflow.python.platform import test @@ -32,6 +33,7 @@ from tensorflow.python.platform import test (gen_string_ops.static_regex_replace)) class RegexReplaceOpVariantsTest(test.TestCase, parameterized.TestCase): + @test_util.run_deprecated_v1 def testForwarding(self, op): with self.cached_session(): # Generate an input that is uniquely consumed by the regex op. @@ -45,6 +47,7 @@ class RegexReplaceOpVariantsTest(test.TestCase, parameterized.TestCase): stripped = op(inp, "\\p{Ll}", ".").eval() self.assertAllEqual([b"A.C.E", b"H.J.L"], stripped) + @test_util.run_deprecated_v1 def testRemovePrefix(self, op): values = ["a:foo", "a:bar", "a:foo", "b:baz", "b:qux", "ca:b"] with self.cached_session(): @@ -53,6 +56,7 @@ class RegexReplaceOpVariantsTest(test.TestCase, parameterized.TestCase): self.assertAllEqual([b"foo", b"bar", b"foo", b"baz", b"qux", b"ca:b"], stripped) + @test_util.run_deprecated_v1 def testRegexReplace(self, op): values = ["aba\naba", "abcdabcde"] with self.cached_session(): @@ -60,6 +64,7 @@ class RegexReplaceOpVariantsTest(test.TestCase, parameterized.TestCase): stripped = op(input_vector, "a.*a", "(\\0)").eval() self.assertAllEqual([b"(aba)\n(aba)", b"(abcda)bcde"], stripped) + @test_util.run_deprecated_v1 def testEmptyMatch(self, op): values = ["abc", "1"] with self.cached_session(): @@ -67,6 +72,7 @@ class RegexReplaceOpVariantsTest(test.TestCase, parameterized.TestCase): stripped = op(input_vector, "", "x").eval() self.assertAllEqual([b"xaxbxcx", b"x1x"], stripped) + @test_util.run_deprecated_v1 def testInvalidPattern(self, op): values = ["abc", "1"] with self.cached_session(): @@ -74,8 +80,9 @@ class RegexReplaceOpVariantsTest(test.TestCase, parameterized.TestCase): invalid_pattern = "A[" replace = op(input_vector, invalid_pattern, "x") with self.assertRaisesOpError("Invalid pattern"): - replace.eval() + self.evaluate(replace) + @test_util.run_deprecated_v1 def testGlobal(self, op): values = ["ababababab", "abcabcabc", ""] with self.cached_session(): @@ -98,6 +105,7 @@ class RegexReplaceTest(test.TestCase, parameterized.TestCase): (as_string, as_tensor), (as_tensor, as_string), (as_tensor, as_tensor)) + @test_util.run_deprecated_v1 def testRegexReplaceDelegation(self, pattern_fn, rewrite_fn): with self.cached_session(): input_vector = constant_op.constant("foo", dtypes.string) @@ -106,6 +114,7 @@ class RegexReplaceTest(test.TestCase, parameterized.TestCase): op = string_ops.regex_replace(input_vector, pattern, replace) self.assertTrue(op.name.startswith("RegexReplace")) + @test_util.run_deprecated_v1 def testStaticRegexReplaceDelegation(self): with self.cached_session(): input_vector = constant_op.constant("foo", dtypes.string) diff --git a/tensorflow/python/kernel_tests/relu_op_test.py b/tensorflow/python/kernel_tests/relu_op_test.py index b0f2796ede..55e68f4884 100644 --- a/tensorflow/python/kernel_tests/relu_op_test.py +++ b/tensorflow/python/kernel_tests/relu_op_test.py @@ -25,6 +25,8 @@ from tensorflow.python.compat import compat from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors +from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import gradients_impl @@ -55,55 +57,56 @@ class ReluTest(test.TestCase): np.array([[-0.9, 0.7, -0.5, 0.3, -0.1], [0.1, -0.3, 0.5, -0.7, 0.9]]))) - def _testRelu(self, np_features, use_gpu=False): + def _testRelu(self, np_features): np_relu = self._npRelu(np_features) - with self.cached_session(use_gpu=use_gpu): - relu = nn_ops.relu(np_features) - tf_relu = relu.eval() + tf_relu = nn_ops.relu(np_features) self.assertAllClose(np_relu, tf_relu) - self.assertShapeEqual(np_relu, relu) + self.assertShapeEqual(np_relu, tf_relu) - def testNumbers(self): + def testNumbersCPU(self): for t in [np.int32, np.int64, np.float16, np.float32, np.float64]: - self._testRelu( - np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]).astype(t), - use_gpu=False) - if t in [np.float16, np.float32, np.float64]: + # Force execution on CPU even if a GPU kernel is available for the type. + with ops.device("/device:CPU:0"): self._testRelu( - np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]).astype(t), - use_gpu=True) + np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]).astype(t)) - def _testReluInt8x4(self, np_inputs): - if not test.is_gpu_available(cuda_only=True): - return - np_relu = self._npRelu(np_inputs) - with self.cached_session(use_gpu=True): - relu = nn_ops.relu(constant_op.constant(np_inputs, dtypes.qint8)) - if np_inputs.size % 4 == 0: - tf_relu = relu.eval() - self.assertAllClose(np_relu, tf_relu) - self.assertShapeEqual(np_relu, relu) - else: - with self.assertRaisesRegexp( - errors.InvalidArgumentError, - "Tensor size must be a multiple of 4 for Relu. Got %d" % - np_inputs.size): - tf_relu = relu.eval() + def testNumbersGPU(self): + if not test.is_gpu_available(): + self.skipTest("No GPU available") + for t in [np.float16, np.float32, np.float64]: + self._testRelu( + np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]).astype(t)) def testReluInt8x4GoodShape(self): - self._testReluInt8x4(np.array([[-50, 7, 23, 0], [-1, -5, 6, 11]])) + if not test.is_gpu_available(cuda_only=True): + self.skipTest("No GPU available") + inputs = np.array([[-50, 7, 23, 0], [-1, -5, 6, 11]]) + np_relu = self._npRelu(inputs) + tf_relu = nn_ops.relu(constant_op.constant(inputs, dtypes.qint8)) + self.assertAllClose(np_relu, tf_relu) + self.assertShapeEqual(np_relu, tf_relu) def testReluInt8x4BadShape(self): - np_inputs = np.array([[-50, 7, 23], [0, 1, -5], [6, -2, 11]]) - self.assertEqual(np_inputs.size, 9) - self._testReluInt8x4(np_inputs) - np_inputs = np.array( - [1, -2, 3, -4, 5, -6, 7, -8, 9, -8, 7, -6, 5, -4, 3, -2, 1]) - self.assertEqual(np_inputs.size, 17) - self._testReluInt8x4(np_inputs) + if not test.is_gpu_available(cuda_only=True): + self.skipTest("No GPU available") + inputs = constant_op.constant( + np.array([[-50, 7, 23], [0, 1, -5], [6, -2, 11]]), dtypes.qint8) + with self.assertRaisesRegexp( + errors.InvalidArgumentError, + "Tensor size must be a multiple of 4 for Relu. Got 9"): + self.evaluate(nn_ops.relu(inputs)) + + inputs = constant_op.constant( + np.array([1, -2, 3, -4, 5, -6, 7, -8, 9, -8, 7, -6, 5, -4, 3, -2, 1]), + dtypes.qint8) + with self.assertRaisesRegexp( + errors.InvalidArgumentError, + "Tensor size must be a multiple of 4 for Relu. Got 17"): + self.evaluate(nn_ops.relu(inputs)) # The gradient test for ReLU is a bit tricky as the derivative is not well # defined at around zero and we want to avoid that in terms of input values. + @test_util.run_deprecated_v1 def testGradientFloat32(self): with self.cached_session(): x = constant_op.constant( @@ -123,6 +126,7 @@ class ReluTest(test.TestCase): # The gradient for fp16 is inaccurate due to the low-precision. # Instead of relying on compute_gradient_error, we compare the fp16 analytical # gradient against their fp32 counterpart. + @test_util.run_deprecated_v1 def testGradientFloat16(self): with self.session(use_gpu=True) as sess: # Randomly construct a 1D shape from [1, 40) @@ -146,9 +150,10 @@ class ReluTest(test.TestCase): # Repeat the experiment for 100 times. All tensor shapes and its tensor # values are randomly generated for each run. for _ in xrange(100): - dx_f32_v, dx_f16_v = sess.run([dx_f32, dx_f16]) + dx_f32_v, dx_f16_v = self.evaluate([dx_f32, dx_f16]) self.assertAllClose(dx_f32_v, dx_f16_v, atol=3e-4) + @test_util.run_deprecated_v1 def testGradientFloat64(self): with self.cached_session(): x = constant_op.constant( @@ -166,6 +171,7 @@ class ReluTest(test.TestCase): print("relu (float64) gradient err = ", err) self.assertLess(err, 1e-10) + @test_util.run_deprecated_v1 def testGradGradFloat32(self): with self.cached_session(): x = constant_op.constant( @@ -183,6 +189,7 @@ class ReluTest(test.TestCase): print("relu (float32) gradient of gradient err = ", err) self.assertLess(err, 1e-4) + @test_util.run_deprecated_v1 def testGradGradFloat64(self): with self.cached_session(): x = constant_op.constant( @@ -202,15 +209,15 @@ class ReluTest(test.TestCase): self.assertLess(err, 1e-10) def testGradientScalar(self): - with self.cached_session() as sess: - x = variables.Variable(100.) - y = nn_ops.relu(x) - loss = y**2 - optimizer = gradient_descent.GradientDescentOptimizer(learning_rate=0.25) - train_op = optimizer.minimize(loss) - sess.run(variables.global_variables_initializer()) - sess.run(train_op) - self.assertAllClose(x.eval(), 50.0) + x = variables.Variable(100.) + + def loss(): + return nn_ops.relu(x)**2 + + optimizer = gradient_descent.GradientDescentOptimizer(learning_rate=0.25) + self.evaluate(variables.global_variables_initializer()) + self.evaluate(optimizer.minimize(loss)) + self.assertAllClose(x.read_value(), 50.0) class Relu6Test(test.TestCase): @@ -228,27 +235,30 @@ class Relu6Test(test.TestCase): np.array([[-0.9, 0.7, -0.5, 0.3, 6.0], [0.1, -0.3, 6.5, -0.7, 0.9]]))) - def _testRelu6(self, np_features, use_gpu=False): + def _testRelu6(self, np_features): np_relu6 = self._npRelu6(np_features) - with self.cached_session(use_gpu=use_gpu): - relu6 = nn_ops.relu6(np_features) - tf_relu6 = relu6.eval() + tf_relu6 = nn_ops.relu6(np_features) self.assertAllClose(np_relu6, tf_relu6) - self.assertShapeEqual(np_relu6, relu6) + self.assertShapeEqual(np_relu6, tf_relu6) - def testNumbers(self): + def testNumbersCPU(self): for t in [np.int32, np.int64, np.float16, np.float32, np.float64]: - self._testRelu6( - np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]).astype(t), - use_gpu=False) - if t in [np.float16, np.float, np.double]: + # Force execution on CPU even if a GPU kernel is available for the type. + with ops.device("/device:CPU:0"): self._testRelu6( - np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]).astype(t), - use_gpu=True) + np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]).astype(t)) + + def testNumbersGPU(self): + if not test.is_gpu_available(): + self.skipTest("No GPU available") + for t in [np.float16, np.float, np.double]: + self._testRelu6( + np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]).astype(t)) # The gradient test for ReLU6 is a bit tricky as the derivative is # not well defined at around zero and six and we want to avoid that # in terms of input values. + @test_util.run_deprecated_v1 def testGradientFloat32(self): with self.cached_session(): x = constant_op.constant( @@ -265,6 +275,7 @@ class Relu6Test(test.TestCase): print("relu6 (float32) gradient err = ", err) self.assertLess(err, 1e-4) + @test_util.run_deprecated_v1 def testGradientFloat64(self): with self.cached_session(): x = constant_op.constant( @@ -297,29 +308,32 @@ class LeakyReluTest(test.TestCase): 0.9]]), alpha=0.1)) - def _testLeakyRelu(self, np_features, alpha, use_gpu=False): + def _testLeakyRelu(self, np_features, alpha): np_leaky_relu = self._npLeakyRelu(np_features, alpha) - with self.test_session(use_gpu=use_gpu): - leaky_relu = nn_ops.leaky_relu(np_features, alpha) - tf_leaky_relu = leaky_relu.eval() + tf_leaky_relu = nn_ops.leaky_relu(np_features, alpha) self.assertAllClose(np_leaky_relu, tf_leaky_relu) - self.assertShapeEqual(np_leaky_relu, leaky_relu) + self.assertShapeEqual(np_leaky_relu, tf_leaky_relu) - def testNumbers(self): + def testNumbersCPU(self): for t in [np.int32, np.int64, np.float16, np.float32, np.float64]: - self._testLeakyRelu( - np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]).astype(t), - alpha=0.2, - use_gpu=False) - if t in [np.float16, np.float32, np.float64]: + # Force execution on CPU even if a GPU kernel is available for the type. + with ops.device("/device:CPU:0"): self._testLeakyRelu( np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]).astype(t), - alpha=0.1, - use_gpu=True) + alpha=0.2) + + def testNumbersGPU(self): + if not test.is_gpu_available(): + self.skipTest("No GPU available") + for t in [np.float16, np.float32, np.float64]: + self._testLeakyRelu( + np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]).astype(t), + alpha=0.1) # The gradient test for Leaky ReLU is a bit tricky as the derivative is not # well defined at around zero and we want to avoid that in terms of input # values. + @test_util.run_deprecated_v1 def testGradientFloat32(self): with self.test_session(): x = constant_op.constant( @@ -336,6 +350,7 @@ class LeakyReluTest(test.TestCase): print("leaky_relu (float32) gradient err = ", err) self.assertLess(err, 1e-4) + @test_util.run_deprecated_v1 def testGradientFloat64(self): with self.test_session(): x = constant_op.constant( @@ -353,6 +368,7 @@ class LeakyReluTest(test.TestCase): print("leaky_relu (float64) gradient err = ", err) self.assertLess(err, 1e-10) + @test_util.run_deprecated_v1 def testGradGradFloat32(self): with compat.forward_compatibility_horizon(2018, 11, 2): with self.test_session(): @@ -371,6 +387,7 @@ class LeakyReluTest(test.TestCase): print("leaky_relu (float32) gradient of gradient err = ", err) self.assertLess(err, 1e-4) + @test_util.run_deprecated_v1 def testGradGradFloat64(self): with compat.forward_compatibility_horizon(2018, 11, 2): with self.test_session(): @@ -391,15 +408,15 @@ class LeakyReluTest(test.TestCase): self.assertLess(err, 1e-10) def testGradientScalar(self): - with self.test_session() as sess: - x = variables.Variable(-100.) - y = nn_ops.leaky_relu(x, 0.05) - loss = y**2 - optimizer = gradient_descent.GradientDescentOptimizer(learning_rate=0.2) - train_op = optimizer.minimize(loss) - sess.run(variables.global_variables_initializer()) - sess.run(train_op) - self.assertAllClose(x.eval(), -99.9) + x = variables.Variable(-100.) + + def loss(): + return nn_ops.leaky_relu(x, 0.05)**2 + + optimizer = gradient_descent.GradientDescentOptimizer(learning_rate=0.2) + self.evaluate(variables.global_variables_initializer()) + self.evaluate(optimizer.minimize(loss)) + self.assertAllClose(x.read_value(), -99.9) class EluTest(test.TestCase): @@ -415,23 +432,26 @@ class EluTest(test.TestCase): np.array([[-0.9, 0.7, -0.5, 0.3, -0.1], [0.1, -0.3, 0.5, -0.7, 0.9]]))) - def _testElu(self, np_features, use_gpu=False): + def _testElu(self, np_features): np_elu = self._npElu(np_features) - with self.cached_session(use_gpu=use_gpu): - elu = nn_ops.elu(np_features) - tf_elu = elu.eval() + tf_elu = nn_ops.elu(np_features) self.assertAllClose(np_elu, tf_elu) - self.assertShapeEqual(np_elu, elu) + self.assertShapeEqual(np_elu, tf_elu) - def testNumbers(self): + def testNumbersCPU(self): for t in [np.float16, np.float32, np.float64]: - self._testElu( - np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]).astype(t), - use_gpu=False) - self._testElu( - np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]).astype(t), - use_gpu=True) + # Force execution on CPU even if a GPU kernel is available for the type. + with ops.device("/device:CPU:0"): + self._testElu( + np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]).astype(t)) + + def testNumbersGPU(self): + if not test.is_gpu_available(): + self.skipTest("No GPU available") + for t in [np.float16, np.float32, np.float64]: + self._testElu(np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]).astype(t)) + @test_util.run_deprecated_v1 def testGradientFloat32(self): with self.cached_session(): x_val = [[-0.9, -0.7, -0.5, -0.3, -0.1], [0.1, 0.3, 0.5, 0.7, 0.9]] @@ -443,6 +463,7 @@ class EluTest(test.TestCase): print("elu (float32) gradient err = ", err) self.assertLess(err, 1e-4) + @test_util.run_deprecated_v1 def testGradientFloat64(self): with self.cached_session(): x_val = [[-0.9, -0.7, -0.5, -0.3, -0.1], [0.1, 0.3, 0.5, 0.7, 0.9]] @@ -454,6 +475,7 @@ class EluTest(test.TestCase): print("elu (float64) gradient err = ", err) self.assertLess(err, 1e-6) + @test_util.run_deprecated_v1 def testGradGrad(self): with self.cached_session(): x = array_ops.placeholder(dtype=dtypes.float32) @@ -465,6 +487,7 @@ class EluTest(test.TestCase): err = np.abs(gg.eval(feed_dict={x: x_val}) - _elu_grad_grad(x_val)) self.assertLess(err, 1e-4) + @test_util.run_deprecated_v1 def testGradGradFloat32(self): with self.cached_session(): x = constant_op.constant( @@ -482,6 +505,7 @@ class EluTest(test.TestCase): print("elu (float32) gradient of gradient err = ", err) self.assertLess(err, 1e-4) + @test_util.run_deprecated_v1 def testGradGradFloat64(self): with self.cached_session(): x = constant_op.constant( @@ -517,23 +541,22 @@ class SeluTest(test.TestCase): np.array([[-0.9, 0.7, -0.5, 0.3, -0.1], [0.1, -0.3, 0.5, -0.7, 0.9]]))) - def _testSelu(self, np_features, use_gpu=False): + def _testSelu(self, np_features): np_selu = self._npSelu(np_features) - with self.cached_session(use_gpu=use_gpu): - selu = nn_ops.selu(np_features) - tf_selu = selu.eval() + tf_selu = nn_ops.selu(np_features) self.assertAllClose(np_selu, tf_selu) - self.assertShapeEqual(np_selu, selu) + self.assertShapeEqual(np_selu, tf_selu) def testNumbers(self): for t in [np.float16, np.float32, np.float64]: self._testSelu( - np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]).astype(t), - use_gpu=False) - self._testSelu( - np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]).astype(t), - use_gpu=True) + np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]).astype(t)) + # Force executed on CPU in case GPU kernels are avaiable. + with ops.device("/device:CPU:0"): + self._testSelu( + np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]).astype(t)) + @test_util.run_deprecated_v1 def testGradientFloat32(self): with self.cached_session(): x_val = [[-0.9, -0.7, -0.5, -0.3, -0.1], [0.1, 0.3, 0.5, 0.7, 0.9]] @@ -545,6 +568,7 @@ class SeluTest(test.TestCase): print("selu (float32) gradient err = ", err) self.assertLess(err, 1e-4) + @test_util.run_deprecated_v1 def testGradientFloat64(self): with self.cached_session(): x_val = [[-0.9, -0.7, -0.5, -0.3, -0.1], [0.1, 0.3, 0.5, 0.7, 0.9]] @@ -556,6 +580,7 @@ class SeluTest(test.TestCase): print("selu (float64) gradient err = ", err) self.assertLess(err, 1e-6) + @test_util.run_deprecated_v1 def testGradGradFloat32(self): with self.cached_session(): x = constant_op.constant( @@ -573,6 +598,7 @@ class SeluTest(test.TestCase): print("selu (float32) gradient of gradient err = ", err) self.assertLess(err, 1e-4) + @test_util.run_deprecated_v1 def testGradGradFloat64(self): with self.cached_session(): x = constant_op.constant( @@ -599,46 +625,44 @@ class CreluTest(test.TestCase): t = nn_ops.crelu(f) self.assertEqual([50, 5, 7, 20], t.get_shape()) - def _testCrelu(self, np_features, use_gpu=False): + def _testCrelu(self, np_features): np_relu = np.maximum(np_features, np.zeros_like(np_features)) np_neg_relu = np.maximum(-np_features, np.zeros_like(np_features)) np_crelu = np.concatenate((np_relu, np_neg_relu), len(np_features.shape) - 1) - with self.cached_session(use_gpu=use_gpu): - crelu = nn_ops.crelu(np_features) - tf_relu = crelu.eval() + tf_crelu = nn_ops.crelu(np_features) - self.assertAllClose(np_crelu, tf_relu) - self.assertShapeEqual(np_crelu, crelu) + self.assertAllClose(np_crelu, tf_crelu) + self.assertShapeEqual(np_crelu, tf_crelu) - def testNumbers(self): + def testNumbersCPU(self): for t in [np.int32, np.int64, np.float16, np.float32, np.float64]: - self._testCrelu( - np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]).astype(t), - use_gpu=False) - if t in [np.float16, np.float32, np.float64]: + # Force execution on CPU even if a GPU kernel is available for the type. + with ops.device("/device:CPU:0"): self._testCrelu( - np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]).astype(t), - use_gpu=True) + np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]).astype(t)) + + def testNumbersGPU(self): + if not test.is_gpu_available(): + self.skipTest("No GPU available") + for t in [np.float16, np.float32, np.float64]: + self._testCrelu( + np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]).astype(t)) def testNumbersWithAxis0(self): - with self.cached_session(): - crelu = nn_ops.crelu( - np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]), axis=0) - tf_relu = crelu.eval() - np_crelu = np.array([[0, 7, 0, 3, 0], [1, 0, 5, 0, 9], [9, 0, 5, 0, 1], - [0, 3, 0, 7, 0]]) - self.assertAllEqual(np_crelu, tf_relu) + tf_crelu = nn_ops.crelu( + np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]), axis=0) + np_crelu = np.array([[0, 7, 0, 3, 0], [1, 0, 5, 0, 9], [9, 0, 5, 0, 1], + [0, 3, 0, 7, 0]]) + self.assertAllEqual(np_crelu, tf_crelu) def testNumbersWithAxis1(self): - with self.cached_session(): - crelu = nn_ops.crelu( - np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]), axis=1) - tf_relu = crelu.eval() - np_crelu = np.array([[0, 7, 0, 3, 0, 9, 0, 5, 0, 1], - [1, 0, 5, 0, 9, 0, 3, 0, 7, 0]]) - self.assertAllEqual(np_crelu, tf_relu) + tf_crelu = nn_ops.crelu( + np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]), axis=1) + np_crelu = np.array([[0, 7, 0, 3, 0, 9, 0, 5, 0, 1], + [1, 0, 5, 0, 9, 0, 3, 0, 7, 0]]) + self.assertAllEqual(np_crelu, tf_crelu) if __name__ == "__main__": diff --git a/tensorflow/python/kernel_tests/reshape_op_test.py b/tensorflow/python/kernel_tests/reshape_op_test.py index 14cdae1837..db3e88a104 100644 --- a/tensorflow/python/kernel_tests/reshape_op_test.py +++ b/tensorflow/python/kernel_tests/reshape_op_test.py @@ -22,6 +22,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradient_checker from tensorflow.python.platform import test @@ -33,14 +34,14 @@ class ReshapeTest(test.TestCase): with self.cached_session(use_gpu=use_gpu): np_ans = x.reshape(y) tf_ans = array_ops.reshape(x, y) - out = tf_ans.eval() + out = self.evaluate(tf_ans) self.assertEqual(tf_ans.get_shape(), out.shape) self.assertShapeEqual(np_ans, tf_ans) # Repeat with an int64 shape tensor. y64 = constant_op.constant(y, dtype=dtypes.int64) tf_ans = array_ops.reshape(x, y64) - out = tf_ans.eval() + out = self.evaluate(tf_ans) self.assertEqual(tf_ans.get_shape(), out.shape) self.assertShapeEqual(np_ans, tf_ans) @@ -91,6 +92,7 @@ class ReshapeTest(test.TestCase): # TODO(vrv): Add tests for failure conditions once python test_util # reports errors. + @test_util.run_deprecated_v1 def testFloatReshapeGradThreeDimensions(self): x = np.arange(1., 25.).reshape([2, 3, 4]).astype(np.float32) s = list(np.shape(x)) @@ -111,6 +113,7 @@ class ReshapeTest(test.TestCase): self._testBothReshape(x, [0, 0, 0]) self._testBothReshape(x, [1, -1, 5]) + @test_util.run_deprecated_v1 def testErrors(self): y = constant_op.constant(0.0, shape=[23, 29, 31]) with self.assertRaisesRegexp(ValueError, "must be evenly divisible by 17"): @@ -121,6 +124,7 @@ class ReshapeTest(test.TestCase): "Cannot reshape a tensor with 4096 elements"): array_ops.reshape(z, [4095]) + @test_util.run_deprecated_v1 def testPartialShapes(self): x = array_ops.placeholder(dtypes.float32) diff --git a/tensorflow/python/kernel_tests/resource_variable_ops_test.py b/tensorflow/python/kernel_tests/resource_variable_ops_test.py index c8227dc117..b57d9d47aa 100644 --- a/tensorflow/python/kernel_tests/resource_variable_ops_test.py +++ b/tensorflow/python/kernel_tests/resource_variable_ops_test.py @@ -29,6 +29,7 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors from tensorflow.python.framework import ops +from tensorflow.python.framework import tensor_util from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import control_flow_ops @@ -53,6 +54,7 @@ class ResourceVariableOpsTest(test_util.TensorFlowTestCase): # involving objects with __del__ defined. self.assertEqual(0, len(gc.garbage)) + @test_util.run_deprecated_v1 def testHandleDtypeShapeMatch(self): with self.cached_session(): handle = resource_variable_ops.var_handle_op(dtype=dtypes.int32, shape=[]) @@ -122,6 +124,7 @@ class ResourceVariableOpsTest(test_util.TensorFlowTestCase): # values. self.assertFalse(np.allclose(variable.numpy(), copied_variable.numpy())) + @test_util.run_deprecated_v1 def testGraphDeepCopy(self): with self.cached_session(): init_value = np.ones((4, 4, 4)) @@ -137,6 +140,15 @@ class ResourceVariableOpsTest(test_util.TensorFlowTestCase): self.evaluate(v[0].assign(2.0)) self.assertAllEqual(self.evaluate(v), [2.0, 2.0]) + @test_util.run_in_graph_and_eager_modes + def testVariableShape(self): + v = resource_variable_ops.ResourceVariable([1., 1.]) + self.assertAllEqual( + tensor_util.constant_value( + resource_variable_ops.variable_shape(v.handle)), + [2]) + + @test_util.run_deprecated_v1 def testDifferentAssignGraph(self): with ops.Graph().as_default(): v = resource_variable_ops.ResourceVariable(1.0) @@ -144,16 +156,18 @@ class ResourceVariableOpsTest(test_util.TensorFlowTestCase): v.assign(2.0) # Note: this fails if we run convert_to_tensor on not the # variable graph. + @test_util.run_deprecated_v1 def testFetchHandle(self): with self.cached_session(): handle = resource_variable_ops.var_handle_op( dtype=dtypes.int32, shape=[1], name="foo") self.assertGreater(len(handle.eval()), 0) + @test_util.run_deprecated_v1 def testCachedValueReadBeforeWrite(self): with self.cached_session() as sess: v = resource_variable_ops.ResourceVariable(0.0, caching_device="cpu:0") - sess.run(v.initializer) + self.evaluate(v.initializer) value, _ = sess.run([v, v.assign_add(1.0)]) self.assertAllEqual(value, 0.0) @@ -426,6 +440,7 @@ class ResourceVariableOpsTest(test_util.TensorFlowTestCase): read = resource_variable_ops.read_variable_op(handle, dtype=dtypes.int32) self.assertEqual(self.evaluate(read), [[6]]) + @test_util.run_deprecated_v1 def testScatterUpdateString(self): handle = resource_variable_ops.var_handle_op( dtype=dtypes.string, shape=[1, 1]) @@ -437,6 +452,7 @@ class ResourceVariableOpsTest(test_util.TensorFlowTestCase): self.assertEqual(compat.as_bytes(self.evaluate(read)[0][0]), compat.as_bytes("b")) + @test_util.run_deprecated_v1 def testScatterUpdateStringScalar(self): handle = resource_variable_ops.var_handle_op( dtype=dtypes.string, shape=[1, 1]) @@ -456,7 +472,7 @@ class ResourceVariableOpsTest(test_util.TensorFlowTestCase): # TODO(alive): get this to work in Eager mode. def testGPU(self): - with self.test_session(use_gpu=True): + with test_util.use_gpu(): abc = variable_scope.get_variable( "abc", shape=[1], @@ -491,6 +507,7 @@ class ResourceVariableOpsTest(test_util.TensorFlowTestCase): initial_value=lambda: 1, constraint=constraint, name="var1") # TODO(alive): how should this work in Eager mode? + @test_util.run_deprecated_v1 def testInitFn(self): with self.cached_session(): v = resource_variable_ops.ResourceVariable( @@ -568,6 +585,21 @@ class ResourceVariableOpsTest(test_util.TensorFlowTestCase): v.load(2.0) self.assertEqual(2.0, self.evaluate(v.value())) + def testToFromProtoCachedValue(self): + with ops.Graph().as_default(): + v_def = resource_variable_ops.ResourceVariable( + initial_value=constant_op.constant(3.0)).to_proto() + v_prime = resource_variable_ops.ResourceVariable(variable_def=v_def) + self.assertTrue(getattr(v_prime, "_cached_value", None) is None) + + other_v_def = resource_variable_ops.ResourceVariable( + caching_device="cpu:0", + initial_value=constant_op.constant(3.0)).to_proto() + other_v_prime = resource_variable_ops.ResourceVariable( + variable_def=other_v_def) + self.assertTrue(other_v_prime._cached_value is not None) + + @test_util.run_deprecated_v1 def testVariableDefInitializedInstances(self): with ops.Graph().as_default(), self.cached_session() as sess: v_def = resource_variable_ops.ResourceVariable( @@ -576,11 +608,11 @@ class ResourceVariableOpsTest(test_util.TensorFlowTestCase): with ops.Graph().as_default(), self.cached_session() as sess: # v describes a VariableDef-based variable without an initial value. v = resource_variable_ops.ResourceVariable(variable_def=v_def) - self.assertEqual(3.0, sess.run(v.initialized_value())) + self.assertEqual(3.0, self.evaluate(v.initialized_value())) # initialized_value should not rerun the initializer_op if the variable # has already been initialized elsewhere. - sess.run(v.assign(1.0)) + self.evaluate(v.assign(1.0)) self.assertEqual(1.0, v.initialized_value().eval()) v_def.ClearField("initial_value_name") @@ -592,7 +624,7 @@ class ResourceVariableOpsTest(test_util.TensorFlowTestCase): self.assertProtoEquals(v_def, v.to_proto()) # But attempts to use initialized_value will result in errors. with self.assertRaises(ValueError): - sess.run(v.initialized_value()) + self.evaluate(v.initialized_value()) def testTrainableInProto(self): with ops.Graph().as_default(): @@ -623,6 +655,7 @@ class ResourceVariableOpsTest(test_util.TensorFlowTestCase): value = self.evaluate(v.sparse_read([0, 3, 1, 2])) self.assertAllEqual(init_value[[0, 3, 1, 2], ...], value) + @test_util.run_deprecated_v1 def testToFromProto(self): with self.cached_session(): v = resource_variable_ops.ResourceVariable(1.0) @@ -671,6 +704,7 @@ class ResourceVariableOpsTest(test_util.TensorFlowTestCase): self.assertEqual(0.0, self.evaluate(v.value())) @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def testDestroyResource(self): v = resource_variable_ops.ResourceVariable(3.0, name="var0") self.evaluate(variables.global_variables_initializer()) @@ -684,6 +718,7 @@ class ResourceVariableOpsTest(test_util.TensorFlowTestCase): self.evaluate(resource_variable_ops.destroy_resource_op( handle, ignore_lookup_error=True)) + @test_util.run_deprecated_v1 def testAssignDifferentShapes(self): with self.cached_session() as sess, variable_scope.variable_scope( "foo", use_resource=True): @@ -704,6 +739,7 @@ class ResourceVariableOpsTest(test_util.TensorFlowTestCase): assign = var.assign(np.zeros(shape=[2, 2])) self.evaluate(assign) + @test_util.run_deprecated_v1 def testDtypeAfterFromProto(self): v = resource_variable_ops.ResourceVariable(2.0) w = resource_variable_ops.ResourceVariable.from_proto(v.to_proto()) @@ -711,6 +747,7 @@ class ResourceVariableOpsTest(test_util.TensorFlowTestCase): self.assertEqual(v.dtype, w.dtype) # TODO(alive): get caching to work in eager mode. + @test_util.run_deprecated_v1 def testCachingDevice(self): with ops.device("/job:server/task:1"): v = resource_variable_ops.ResourceVariable( @@ -726,6 +763,7 @@ class ResourceVariableOpsTest(test_util.TensorFlowTestCase): with self.assertRaises(ValueError): _ = w.value().op.get_attr("_class") + @test_util.run_deprecated_v1 def testSharedName(self): with self.cached_session(): v = resource_variable_ops.ResourceVariable(300.0, name="var4") @@ -736,7 +774,7 @@ class ResourceVariableOpsTest(test_util.TensorFlowTestCase): # Needed in Eager since we get a unique container name by default. container=ops.get_default_graph()._container) w_read = resource_variable_ops.read_variable_op(w, v.dtype.base_dtype) - self.assertEqual(300.0, w_read.eval()) + self.assertEqual(300.0, self.evaluate(w_read)) x = resource_variable_ops.var_handle_op( dtype=v.dtype.base_dtype, shape=v.get_shape(), shared_name="var5", @@ -744,6 +782,7 @@ class ResourceVariableOpsTest(test_util.TensorFlowTestCase): with self.assertRaisesOpError("Resource .*/var5/.* does not exist"): resource_variable_ops.read_variable_op(x, v.dtype.base_dtype).eval() + @test_util.run_deprecated_v1 def testSharedNameWithNamescope(self): with self.cached_session(): with ops.name_scope("foo"): @@ -772,6 +811,7 @@ class ResourceVariableOpsTest(test_util.TensorFlowTestCase): "", str(v.sparse_read(array_ops.placeholder(dtypes.int32)).shape)) + @test_util.run_deprecated_v1 def testSetInitialValue(self): with self.cached_session(): # Initialize variable with a value different from the initial value passed @@ -780,6 +820,7 @@ class ResourceVariableOpsTest(test_util.TensorFlowTestCase): v.initializer.run(feed_dict={v.initial_value: 3.0}) self.assertEqual(3.0, v.value().eval()) + @test_util.run_deprecated_v1 def testControlFlowInitialization(self): """Expects an error if an initializer is in a control-flow scope.""" @@ -916,6 +957,7 @@ class ResourceVariableOpsTest(test_util.TensorFlowTestCase): self.assertAllEqual(self.evaluate(v.assign_add(1)), [1, 2, 3, 4]) @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def testCopyToGraphUninitialized(self): v = resource_variable_ops.ResourceVariable([0, 1, 2, 3]) copy_to_graph = ops.Graph() diff --git a/tensorflow/python/kernel_tests/reverse_sequence_op_test.py b/tensorflow/python/kernel_tests/reverse_sequence_op_test.py index 56609bd0a5..05307c9834 100644 --- a/tensorflow/python/kernel_tests/reverse_sequence_op_test.py +++ b/tensorflow/python/kernel_tests/reverse_sequence_op_test.py @@ -23,6 +23,7 @@ from six.moves import xrange # pylint: disable=redefined-builtin from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradient_checker from tensorflow.python.platform import test @@ -42,12 +43,12 @@ class ReverseSequenceTest(test.TestCase): ans = array_ops.reverse_sequence( x, batch_axis=batch_axis, seq_axis=seq_axis, seq_lengths=seq_lengths) if expected_err_re is None: - tf_ans = ans.eval() + tf_ans = self.evaluate(ans) self.assertAllClose(tf_ans, truth, atol=1e-10) self.assertShapeEqual(truth, ans) else: with self.assertRaisesOpError(expected_err_re): - ans.eval() + self.evaluate(ans) def _testBothReverseSequence(self, x, @@ -107,6 +108,7 @@ class ReverseSequenceTest(test.TestCase): def testComplex128Basic(self): self._testBasic(np.complex128) + @test_util.run_deprecated_v1 def testFloatReverseSequenceGrad(self): x = np.asarray( [[[1, 2, 3, 4], [5, 6, 7, 8]], [[9, 10, 11, 12], [13, 14, 15, 16]], @@ -133,6 +135,7 @@ class ReverseSequenceTest(test.TestCase): print("ReverseSequence gradient error = %g" % err) self.assertLess(err, 1e-8) + @test_util.run_deprecated_v1 def testShapeFunctionEdgeCases(self): t = array_ops.reverse_sequence( array_ops.placeholder( diff --git a/tensorflow/python/kernel_tests/rnn_test.py b/tensorflow/python/kernel_tests/rnn_test.py index 0090b7332f..3bc457f8fb 100644 --- a/tensorflow/python/kernel_tests/rnn_test.py +++ b/tensorflow/python/kernel_tests/rnn_test.py @@ -262,6 +262,7 @@ class RNNTest(test.TestCase): rnn.dynamic_rnn(cell, inputs, dtype=dtypes.float32, sequence_length=[4]) @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def testTensorArrayStateIsAccepted(self): cell = TensorArrayStateRNNCell() in_eager_mode = context.executing_eagerly() @@ -285,6 +286,7 @@ class RNNTest(test.TestCase): self.assertAllEqual(4, state[0]) self.assertAllEqual([[[1]], [[2]], [[3]], [[4]]], state[1]) + @test_util.run_deprecated_v1 def testCellGetInitialState(self): cell = rnn_cell_impl.BasicRNNCell(5) with self.assertRaisesRegexp( @@ -345,6 +347,7 @@ class RNNTest(test.TestCase): self._assert_cell_builds(contrib_rnn.IndyLSTMCell, f32, 5, 7, 3) self._assert_cell_builds(contrib_rnn.IndyLSTMCell, f64, 5, 7, 3) + @test_util.run_deprecated_v1 def testRNNWithKerasSimpleRNNCell(self): with self.cached_session() as sess: input_shape = 10 @@ -378,6 +381,7 @@ class RNNTest(test.TestCase): self.assertEqual(len(outputs), batch) self.assertEqual(len(state), batch) + @test_util.run_deprecated_v1 def testRNNWithKerasGRUCell(self): with self.cached_session() as sess: input_shape = 10 @@ -411,6 +415,7 @@ class RNNTest(test.TestCase): self.assertEqual(len(outputs), batch) self.assertEqual(len(state), batch) + @test_util.run_deprecated_v1 def testRNNWithKerasLSTMCell(self): with self.cached_session() as sess: input_shape = 10 @@ -448,6 +453,7 @@ class RNNTest(test.TestCase): self.assertEqual(len(state[0]), batch) self.assertEqual(len(state[1]), batch) + @test_util.run_deprecated_v1 def testRNNWithStackKerasCell(self): with self.cached_session() as sess: input_shape = 10 @@ -491,6 +497,7 @@ class RNNTest(test.TestCase): for s in state: self.assertEqual(len(s), batch) + @test_util.run_deprecated_v1 def testStaticRNNWithKerasSimpleRNNCell(self): with self.cached_session() as sess: input_shape = 10 @@ -529,6 +536,7 @@ class RNNTest(test.TestCase): self.assertEqual(len(outputs[0]), batch) self.assertEqual(len(state), batch) + @test_util.run_deprecated_v1 def testKerasAndTFRNNLayerOutputComparison(self): input_shape = 10 output_shape = 5 @@ -562,6 +570,7 @@ class RNNTest(test.TestCase): self.assertAllClose(tf_out, k_out) self.assertAllClose(tf_state, k_state) + @test_util.run_deprecated_v1 def testSimpleRNNCellAndBasicRNNCellComparison(self): input_shape = 10 output_shape = 5 @@ -601,6 +610,7 @@ class RNNTest(test.TestCase): self.assertAllClose(tf_out, k_out, atol=1e-5) self.assertAllClose(tf_state, k_state, atol=1e-5) + @test_util.run_deprecated_v1 def testBasicLSTMCellInterchangeWithLSTMCell(self): with self.session(graph=ops_lib.Graph()) as sess: basic_cell = rnn_cell_impl.BasicLSTMCell(1) diff --git a/tensorflow/python/kernel_tests/save_restore_ops_test.py b/tensorflow/python/kernel_tests/save_restore_ops_test.py index cb9aa1e34d..fecc9a3800 100644 --- a/tensorflow/python/kernel_tests/save_restore_ops_test.py +++ b/tensorflow/python/kernel_tests/save_restore_ops_test.py @@ -17,14 +17,30 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import os + from tensorflow.core.protobuf import config_pb2 from tensorflow.python.client import session +from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import gen_io_ops from tensorflow.python.ops import io_ops from tensorflow.python.platform import test +class SaveTest(test.TestCase): + + @test_util.run_in_graph_and_eager_modes + def testRelativePath(self): + os.chdir(self.get_temp_dir()) + self.evaluate(io_ops.save_v2( + "ckpt", ["x"], [""], [constant_op.constant(100.)])) + self.assertAllEqual([100.], + self.evaluate(io_ops.restore_v2( + "ckpt", ["x"], [""], [dtypes.float32]))) + + class ShardedFileOpsTest(test.TestCase): def testShardedFileName(self): @@ -39,6 +55,7 @@ class ShardedFileOpsTest(test.TestCase): class ShapeInferenceTest(test.TestCase): + @test_util.run_deprecated_v1 def testRestoreV2WithSliceInput(self): op = io_ops.restore_v2("model", ["var1", "var2"], ["", "3 4 0,1:-"], [dtypes.float32, dtypes.float32]) @@ -46,11 +63,13 @@ class ShapeInferenceTest(test.TestCase): self.assertFalse(op[0].get_shape().is_fully_defined()) self.assertEqual([1, 4], op[1].get_shape()) + @test_util.run_deprecated_v1 def testRestoreV2NumSlicesNotMatch(self): with self.assertRaises(ValueError): io_ops.restore_v2("model", ["var1", "var2", "var3"], ["", "3 4 0,1:-"], [dtypes.float32, dtypes.float32]) + @test_util.run_deprecated_v1 def testRestoreSlice(self): op = gen_io_ops.restore_slice("model", "var", "3 4 0,1:-", dtypes.float32) self.assertEqual([1, 4], op.get_shape()) diff --git a/tensorflow/python/kernel_tests/scan_ops_test.py b/tensorflow/python/kernel_tests/scan_ops_test.py index b369222565..33e491fee1 100644 --- a/tensorflow/python/kernel_tests/scan_ops_test.py +++ b/tensorflow/python/kernel_tests/scan_ops_test.py @@ -24,6 +24,7 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors_impl from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import math_ops from tensorflow.python.platform import test @@ -88,12 +89,14 @@ class CumsumTest(test.TestCase): for reverse in [True, False]: self._compare(x, axis, exclusive, reverse) + @test_util.run_deprecated_v1 def testEmpty(self): for dtype in self.valid_dtypes: x = np.zeros([0]).astype(dtype) for axis in (-1, 0): self._compareAll(x, axis) + @test_util.run_deprecated_v1 def testAxisType(self): for dtype in self.valid_dtypes: x = np.arange(1, 6).reshape([5]).astype(dtype) @@ -102,30 +105,40 @@ class CumsumTest(test.TestCase): axis = constant_op.constant(0, axis_dtype) tf_out = math_ops.cumsum(x, axis).eval() + @test_util.run_deprecated_v1 def test1D(self): for dtype in self.valid_dtypes: x = np.arange(1, 6).reshape([5]).astype(dtype) for axis in (-1, 0): self._compareAll(x, axis) + @test_util.run_deprecated_v1 def test2D(self): for dtype in self.valid_dtypes: x = np.arange(0, 10).reshape([2, 5]).astype(dtype) for axis in (-2, -1, 0, 1): self._compareAll(x, axis) + @test_util.run_deprecated_v1 def test3D(self): for dtype in self.valid_dtypes: x = np.arange(0, 20).reshape([2, 2, 5]).astype(dtype) for axis in (-3, -2, -1, 0, 1, 2): self._compareAll(x, axis) + @test_util.run_deprecated_v1 def test6D(self): for dtype in self.valid_dtypes: x = np.arange(1, 145).reshape([2, 2, 3, 3, 2, 2]).astype(dtype) for axis in range(-6, 6, 3): self._compareAll(x, axis) + @test_util.run_deprecated_v1 + def testLarge(self): + for dtype in self.valid_dtypes: + x = np.ones([1000000], dtype=dtype) / 1024 + self._compareAll(x, 0) + def testInvalidAxis(self): x = np.arange(0, 10).reshape([2, 5]).astype(np.float32) input_tensor = ops.convert_to_tensor(x) @@ -152,22 +165,27 @@ class CumsumTest(test.TestCase): t, shape, result, shape, x_init_value=x, delta=1) self.assertAllClose(jacob_t, jacob_n, rtol=1e-8, atol=1e-8) + @test_util.run_deprecated_v1 def testGradient(self): for axis in (-1, 0): self._compareGradient([50], axis, False, False) + @test_util.run_deprecated_v1 def testGradientReverse(self): for axis in (-1, 0): self._compareGradient([50], axis, False, True) + @test_util.run_deprecated_v1 def testGradientExclusive(self): for axis in (-1, 0): self._compareGradient([50], axis, True, False) + @test_util.run_deprecated_v1 def testGradientExclusiveReverse(self): for axis in (-1, 0): self._compareGradient([50], axis, True, True) + @test_util.run_deprecated_v1 def testGradient2D(self): for axis in (-1, 0, 1): for exclusive in [True, False]: @@ -194,12 +212,14 @@ class CumprodTest(test.TestCase): for reverse in [True, False]: self._compare(x, axis, exclusive, reverse) + @test_util.run_deprecated_v1 def testEmpty(self): for dtype in self.valid_dtypes: x = np.zeros([0]).astype(dtype) for axis in (-1, 0): self._compareAll(x, axis) + @test_util.run_deprecated_v1 def testAxisType(self): for dtype in self.valid_dtypes: x = np.arange(1, 6).reshape([5]).astype(dtype) @@ -208,24 +228,28 @@ class CumprodTest(test.TestCase): axis = constant_op.constant(0, axis_dtype) tf_out = math_ops.cumprod(x, axis).eval() + @test_util.run_deprecated_v1 def test1D(self): for dtype in self.valid_dtypes: x = np.arange(1, 6).reshape([5]).astype(dtype) for axis in (-1, 0): self._compareAll(x, axis) + @test_util.run_deprecated_v1 def test2D(self): for dtype in self.valid_dtypes: x = np.arange(1, 11).reshape([2, 5]).astype(dtype) for axis in (-2, -1, 0, 1): self._compareAll(x, axis) + @test_util.run_deprecated_v1 def test3D(self): for dtype in self.valid_dtypes: x = np.arange(1, 21).reshape([2, 2, 5]).astype(dtype) for axis in (-3, -2, -1, 0, 1, 2): self._compareAll(x, axis) + @test_util.run_deprecated_v1 def test6D(self): for dtype in self.valid_dtypes: x = np.arange(1, 145).reshape([2, 2, 3, 3, 2, 2]).astype(dtype) @@ -258,22 +282,27 @@ class CumprodTest(test.TestCase): t, shape, result, shape, x_init_value=x, delta=1) self.assertAllClose(jacob_t, jacob_n, rtol=1e-8, atol=1e-8) + @test_util.run_deprecated_v1 def testGradient(self): for axis in (-1, 0): self._compareGradient([8], axis, False, False) + @test_util.run_deprecated_v1 def testGradientReverse(self): for axis in (-1, 0): self._compareGradient([8], axis, False, True) + @test_util.run_deprecated_v1 def testGradientExclusive(self): for axis in (-1, 0): self._compareGradient([8], axis, True, False) + @test_util.run_deprecated_v1 def testGradientExclusiveReverse(self): for axis in (-1, 0): self._compareGradient([8], axis, True, True) + @test_util.run_deprecated_v1 def testGradient2D(self): for axis in (-2, -1, 0, 1): for exclusive in [True, False]: diff --git a/tensorflow/python/kernel_tests/scatter_nd_ops_test.py b/tensorflow/python/kernel_tests/scatter_nd_ops_test.py index 0ed508b9fe..c1241ba87e 100644 --- a/tensorflow/python/kernel_tests/scatter_nd_ops_test.py +++ b/tensorflow/python/kernel_tests/scatter_nd_ops_test.py @@ -29,6 +29,7 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops +from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import gradients_impl from tensorflow.python.ops import resource_variable_ops from tensorflow.python.ops import state_ops @@ -144,7 +145,7 @@ class StatefulScatterNdTest(test.TestCase): tf_scatter(ref_var, indices, updates).eval() # Compare - self.assertAllClose(new, ref_var.eval()) + self.assertAllClose(new, self.evaluate(ref_var)) def _VariableRankTests(self, np_scatter, tf_scatter): for vtype in (np.int32, np.float16, np.float32, np.float64, np.complex64, @@ -161,10 +162,11 @@ class StatefulScatterNdTest(test.TestCase): init = variables.global_variables_initializer() with self.session(use_gpu=True) as sess: - sess.run(init) - result = sess.run(scatter) + self.evaluate(init) + result = self.evaluate(scatter) self.assertAllClose(result, expected) + @test_util.run_deprecated_v1 def testSimpleResource(self): indices = constant_op.constant([[4], [3], [1], [7]], dtype=dtypes.int32) updates = constant_op.constant([9, 10, 11, 12], dtype=dtypes.float32) @@ -175,8 +177,8 @@ class StatefulScatterNdTest(test.TestCase): init = variables.global_variables_initializer() with self.session(use_gpu=True) as sess: - sess.run(init) - sess.run(scatter) + self.evaluate(init) + self.evaluate(scatter) self.assertAllClose(ref.eval(), expected) def testSimple2(self): @@ -189,8 +191,8 @@ class StatefulScatterNdTest(test.TestCase): init = variables.global_variables_initializer() with self.session(use_gpu=True) as sess: - sess.run(init) - result = sess.run(scatter) + self.evaluate(init) + result = self.evaluate(scatter) self.assertAllClose(result, expected) def testSimple3(self): @@ -203,16 +205,19 @@ class StatefulScatterNdTest(test.TestCase): init = variables.global_variables_initializer() with self.session(use_gpu=True) as sess: - sess.run(init) - result = sess.run(scatter) + self.evaluate(init) + result = self.evaluate(scatter) self.assertAllClose(result, expected) + @test_util.run_deprecated_v1 def testVariableRankUpdate(self): self._VariableRankTests(_NumpyUpdate, state_ops.scatter_nd_update) + @test_util.run_deprecated_v1 def testVariableRankAdd(self): self._VariableRankTests(_NumpyAdd, state_ops.scatter_nd_add) + @test_util.run_deprecated_v1 def testVariableRankSub(self): self._VariableRankTests(_NumpySub, state_ops.scatter_nd_sub) @@ -230,6 +235,7 @@ class StatefulScatterNdTest(test.TestCase): self._VariableRankTest( np_scatter, tf_scatter, vtype, itype, repeat_indices=True) + @test_util.run_deprecated_v1 def testScatterRepeatIndices(self): """This tests scatter_add using indices that repeat.""" self._ScatterRepeatIndicesTest(_NumpyAdd, state_ops.scatter_nd_add) @@ -249,8 +255,9 @@ class StatefulScatterNdTest(test.TestCase): # [[0]], dtype=tf.int64), [False]) # var.initializer.run() # session.run([update0, update1]) - # self.assertAllEqual([False, True], var.eval()) + # self.assertAllEqual([False, True], self.evaluate(var)) + @test_util.run_deprecated_v1 def testScatterOutOfRangeCpu(self): # TODO(simister): Re-enable once binary size increase due to # scatter_nd ops is under control. @@ -287,6 +294,7 @@ class StatefulScatterNdTest(test.TestCase): state_ops.scatter_nd_update(ref, indices, updates).get_shape().as_list(), shape) + @test_util.run_deprecated_v1 def testResVarInvalidOutputShape(self): res = variables.Variable( initial_value=lambda: array_ops.zeros(shape=[], dtype=dtypes.float32), @@ -296,6 +304,7 @@ class StatefulScatterNdTest(test.TestCase): with self.assertRaisesOpError("Output must be at least 1-D"): state_ops.scatter_nd_update(res, [[0]], [0.22]).eval() + @test_util.run_deprecated_v1 def testExtraIndicesDimensions(self): indices = array_ops.zeros([1, 1, 2], dtypes.int32) updates = array_ops.zeros([1, 1], dtypes.int32) @@ -307,8 +316,9 @@ class StatefulScatterNdTest(test.TestCase): expected_result = np.zeros([2, 2], dtype=np.int32) with self.cached_session(): ref.initializer.run() - self.assertAllEqual(expected_result, scatter_update.eval()) + self.assertAllEqual(expected_result, self.evaluate(scatter_update)) + @test_util.run_deprecated_v1 def testRank3InvalidShape1(self): indices = array_ops.zeros([3, 2, 2], dtypes.int32) updates = array_ops.zeros([2, 2, 2], dtypes.int32) @@ -318,6 +328,7 @@ class StatefulScatterNdTest(test.TestCase): ValueError, "The outer \\d+ dimensions of indices\\.shape="): state_ops.scatter_nd_update(ref, indices, updates) + @test_util.run_deprecated_v1 def testRank3InvalidShape2(self): indices = array_ops.zeros([2, 2, 1], dtypes.int32) updates = array_ops.zeros([2, 2], dtypes.int32) @@ -327,6 +338,7 @@ class StatefulScatterNdTest(test.TestCase): ValueError, "The inner \\d+ dimensions of input\\.shape="): state_ops.scatter_nd_update(ref, indices, updates) + @test_util.run_deprecated_v1 def testConcurrentUpdates(self): num_updates = 10000 update_values = np.random.rand(num_updates) @@ -341,8 +353,8 @@ class StatefulScatterNdTest(test.TestCase): init = variables.global_variables_initializer() with session.Session() as sess: - sess.run(init) - result = sess.run(scatter) + self.evaluate(init) + result = self.evaluate(scatter) assert np.allclose(result, expected_result) # TODO(fpmc): Re-enable this test when gpu_pip test actually runs on a GPU. @@ -421,7 +433,7 @@ class ScatterNdTest(test.TestCase): b"", b"", b"seven"]) scatter = self.scatter_nd(indices, updates, shape=(8,)) with self.cached_session() as sess: - result = sess.run(scatter) + result = self.evaluate(scatter) self.assertAllEqual(expected, result) # Same indice is updated twice by same value. @@ -432,7 +444,7 @@ class ScatterNdTest(test.TestCase): expected = np.array([b"", b"", b"", b"bb", b"a", b"", b"", b"c"]) scatter = self.scatter_nd(indices, updates, shape=(8,)) with self.cached_session() as sess: - result = sess.run(scatter) + result = self.evaluate(scatter) self.assertAllEqual(expected, result) # Same indice is updated twice by different value. @@ -444,7 +456,7 @@ class ScatterNdTest(test.TestCase): np.array([b"", b"", b"", b"cb", b"a", b"", b"", b"d"])] scatter = self.scatter_nd(indices, updates, shape=(8,)) with self.cached_session() as sess: - result = sess.run(scatter) + result = self.evaluate(scatter) self.assertTrue(np.array_equal(result, expected[0]) or np.array_equal(result, expected[1])) @@ -455,6 +467,7 @@ class ScatterNdTest(test.TestCase): self.assertAllEqual( self.scatter_nd(indices, updates, shape).get_shape().as_list(), shape) + @test_util.run_deprecated_v1 def testExtraIndicesDimensions(self): indices = array_ops.zeros([1, 1, 2], dtypes.int32) updates = array_ops.zeros([1, 1], dtypes.int32) @@ -463,26 +476,30 @@ class ScatterNdTest(test.TestCase): self.assertAllEqual(scatter.get_shape().as_list(), shape) expected_result = np.zeros([2, 2], dtype=np.int32) with self.cached_session(): - self.assertAllEqual(expected_result, scatter.eval()) + self.assertAllEqual(expected_result, self.evaluate(scatter)) + @test_util.run_deprecated_v1 def testUndefinedIndicesShape(self): indices = array_ops.placeholder(dtypes.int32, shape=None) updates = array_ops.placeholder(dtypes.int32, shape=[2, 2, 2]) shape = constant_op.constant([2, 2, 2], dtypes.int32) self.scatter_nd(indices, updates, shape) + @test_util.run_deprecated_v1 def testUndefinedUpdatesShape(self): indices = array_ops.placeholder(dtypes.int32, shape=[2, 2, 2]) updates = array_ops.placeholder(dtypes.int32, shape=None) shape = constant_op.constant([2, 2, 2], dtypes.int32) self.scatter_nd(indices, updates, shape) + @test_util.run_deprecated_v1 def testUndefinedOutputShape(self): indices = array_ops.placeholder(dtypes.int32, shape=[2, 2, 2]) updates = array_ops.placeholder(dtypes.int32, shape=[2, 2, 2]) shape = array_ops.placeholder(dtypes.int32, shape=[None]) self.scatter_nd(indices, updates, shape) + @test_util.run_deprecated_v1 def testEmptyOutputShape1(self): indices = array_ops.zeros([2, 2, 2], dtypes.int32) updates = array_ops.zeros([2, 2, 2], dtypes.int32) @@ -492,6 +509,7 @@ class ScatterNdTest(test.TestCase): ValueError, "Indices and updates specified for empty output shape"): self.scatter_nd(indices, updates, shape) + @test_util.run_deprecated_v1 def testEmptyOutputShape2(self): indices = array_ops.placeholder(dtypes.int32, shape=None) updates = array_ops.placeholder(dtypes.int32, shape=None) @@ -505,6 +523,7 @@ class ScatterNdTest(test.TestCase): updates: np.zeros([2, 2, 2], dtype=np.int32) }) + @test_util.run_deprecated_v1 def testEmptyOutputShape3(self): indices = array_ops.zeros([0], dtypes.int32) updates = array_ops.zeros([0], dtypes.int32) @@ -514,6 +533,7 @@ class ScatterNdTest(test.TestCase): with self.cached_session(): self.assertEqual(scatter.eval().size, 0) + @test_util.run_deprecated_v1 def testRank3InvalidShape1(self): indices = array_ops.zeros([3, 2, 2], dtypes.int32) updates = array_ops.zeros([2, 2, 2], dtypes.int32) @@ -522,6 +542,7 @@ class ScatterNdTest(test.TestCase): ValueError, "The outer \\d+ dimensions of indices\\.shape="): self.scatter_nd(indices, updates, shape) + @test_util.run_deprecated_v1 def testRank3InvalidShape2(self): indices = array_ops.zeros([2, 2, 1], dtypes.int32) updates = array_ops.zeros([2, 2], dtypes.int32) @@ -530,6 +551,7 @@ class ScatterNdTest(test.TestCase): ValueError, "The inner \\d+ dimensions of (input|output)\\.shape="): self.scatter_nd(indices, updates, shape) + @test_util.run_deprecated_v1 def testGradientsRank2ElementUpdate(self): for dtype in GRADIENT_TESTS_DTYPES: indices = constant_op.constant([[0, 0], [1, 1]], dtype=dtypes.int32) @@ -545,10 +567,11 @@ class ScatterNdTest(test.TestCase): expected_input_grad = np.array([[1, 2], [3, 4]], dtype=dtype.as_numpy_dtype()) with self.cached_session(): - self.assertAllEqual(expected_updates_grad, updates_grad.eval()) + self.assertAllEqual(expected_updates_grad, self.evaluate(updates_grad)) if self.non_aliasing_add_test: - self.assertAllEqual(expected_input_grad, input_grad.eval()) + self.assertAllEqual(expected_input_grad, self.evaluate(input_grad)) + @test_util.run_deprecated_v1 def testGradientsRank2SliceUpdate(self): for dtype in GRADIENT_TESTS_DTYPES: indices = constant_op.constant([[1], [0]], dtype=dtypes.int32) @@ -565,10 +588,11 @@ class ScatterNdTest(test.TestCase): expected_input_grad = np.array([[3, 4], [1, 2]], dtype=dtype.as_numpy_dtype()) with self.cached_session(): - self.assertAllEqual(expected_updates_grad, updates_grad.eval()) + self.assertAllEqual(expected_updates_grad, self.evaluate(updates_grad)) if self.non_aliasing_add_test: - self.assertAllEqual(expected_input_grad, input_grad.eval()) + self.assertAllEqual(expected_input_grad, self.evaluate(input_grad)) + @test_util.run_deprecated_v1 def testGradientsRank3SliceUpdate(self): for dtype in GRADIENT_TESTS_DTYPES: indices = constant_op.constant([[[0, 1], [1, 0]], [[0, 0], [1, 1]]], @@ -588,10 +612,11 @@ class ScatterNdTest(test.TestCase): expected_input_grad = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]], dtype=dtype.as_numpy_dtype()) with self.cached_session(): - self.assertAllEqual(expected_updates_grad, updates_grad.eval()) + self.assertAllEqual(expected_updates_grad, self.evaluate(updates_grad)) if self.non_aliasing_add_test: - self.assertAllEqual(expected_input_grad, input_grad.eval()) + self.assertAllEqual(expected_input_grad, self.evaluate(input_grad)) + @test_util.run_deprecated_v1 def testGradientsRank7SliceUpdate(self): for dtype in GRADIENT_TESTS_DTYPES: indices = constant_op.constant( @@ -615,10 +640,11 @@ class ScatterNdTest(test.TestCase): [[[[[[[1, 2], [3, 4]]]], [[[[5, 6], [7, 8]]]]]]], dtype=dtype.as_numpy_dtype()) with self.cached_session(): - self.assertAllEqual(expected_updates_grad, updates_grad.eval()) + self.assertAllEqual(expected_updates_grad, self.evaluate(updates_grad)) if self.non_aliasing_add_test: - self.assertAllEqual(expected_input_grad, input_grad.eval()) + self.assertAllEqual(expected_input_grad, self.evaluate(input_grad)) + @test_util.run_deprecated_v1 def testScatterNdRepatedIndicesAdd(self): indices = array_ops.zeros([100000, 1], dtypes.int32) values = np.random.randn(100000) @@ -627,6 +653,7 @@ class ScatterNdTest(test.TestCase): val = self.scatter_nd(indices, values, shape).eval() self.assertAllClose([np.sum(values)], val) + @test_util.run_deprecated_v1 def testSmokeScatterNdBatch2DSliceDim2(self): with self.cached_session(): indices = array_ops.zeros([3, 5, 2], dtype=dtypes.int32) @@ -634,6 +661,7 @@ class ScatterNdTest(test.TestCase): shape = [4, 6, 7] self.scatter_nd(indices, values, shape).eval() + @test_util.run_deprecated_v1 def testSmokeScatterNdBatch1DSliceDim2(self): with self.cached_session(): indices = array_ops.zeros([0, 2], dtype=dtypes.int32) @@ -641,6 +669,7 @@ class ScatterNdTest(test.TestCase): shape = [4, 6, 7] self.scatter_nd(indices, values, shape).eval() + @test_util.run_deprecated_v1 def testSmokeScatterNdBatch1DSliceDim3ShapeRank7(self): with self.cached_session(): indices = array_ops.zeros([1, 3], dtype=dtypes.int32) @@ -648,6 +677,7 @@ class ScatterNdTest(test.TestCase): shape = [3, 4, 5, 6, 7, 8, 9] self.scatter_nd(indices, values, shape).eval() + @test_util.run_deprecated_v1 def testSmokeScatterNdBatch2DSliceDim3ShapeRank7(self): with self.cached_session(): indices = array_ops.zeros([1, 2, 3], dtype=dtypes.int32) @@ -669,5 +699,55 @@ class ScatterNdNonAliasingAddTest(ScatterNdTest): pass +class ScatterNdTensorTest(test.TestCase): + + @test_util.run_in_graph_and_eager_modes + def testUpdateAddSub(self): + indices = constant_op.constant([[4], [3], [1], [7]]) + updates = constant_op.constant([9, 10, 11, 12], dtype=dtypes.float32) + t = array_ops.ones([8], dtype=dtypes.float32) + assigned = array_ops.tensor_scatter_update(t, indices, updates) + added = array_ops.tensor_scatter_add(t, indices, updates) + subbed = array_ops.tensor_scatter_sub(t, indices, updates) + + self.assertAllEqual(assigned, + constant_op.constant([1, 11, 1, 10, 9, 1, 1, 12])) + self.assertAllEqual(added, + constant_op.constant([1, 12, 1, 11, 10, 1, 1, 13])) + self.assertAllEqual(subbed, + constant_op.constant([1, -10, 1, -9, -8, 1, 1, -11])) + + def testUpdateAddSubGradients(self): + + with self.cached_session(): + indices = constant_op.constant([[3], [1]]) + updates = constant_op.constant([9, 10], dtype=dtypes.float32) + x = array_ops.ones([4], dtype=dtypes.float32) + + assigned = array_ops.tensor_scatter_update(x, indices, updates) + added = array_ops.tensor_scatter_add(x, indices, updates) + subbed = array_ops.tensor_scatter_sub(x, indices, updates) + + err_assigned = gradient_checker.compute_gradient_error( + x, [4], assigned, [4]) + err_added = gradient_checker.compute_gradient_error(x, [4], added, [4]) + err_subbed = gradient_checker.compute_gradient_error(x, [4], subbed, [4]) + + self.assertLess(err_assigned, 2e-4) + self.assertLess(err_added, 2e-4) + self.assertLess(err_subbed, 2e-4) + + err_assigned_wrt_updates = gradient_checker.compute_gradient_error( + updates, [2], assigned, [4]) + err_added_wrt_updates = gradient_checker.compute_gradient_error( + updates, [2], added, [4]) + err_subbed_wrt_updates = gradient_checker.compute_gradient_error( + updates, [2], subbed, [4]) + + self.assertLess(err_assigned_wrt_updates, 2e-4) + self.assertLess(err_added_wrt_updates, 2e-4) + self.assertLess(err_subbed_wrt_updates, 2e-4) + + if __name__ == "__main__": test.main() diff --git a/tensorflow/python/kernel_tests/scatter_ops_test.py b/tensorflow/python/kernel_tests/scatter_ops_test.py index 87c345245c..623c17d373 100644 --- a/tensorflow/python/kernel_tests/scatter_ops_test.py +++ b/tensorflow/python/kernel_tests/scatter_ops_test.py @@ -22,6 +22,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import state_ops from tensorflow.python.ops import variables from tensorflow.python.platform import test @@ -196,84 +197,111 @@ class ScatterTest(test.TestCase): self._VariableRankTest(tf_scatter, vtype, itype, repeat_indices, updates_are_scalar) + @test_util.run_deprecated_v1 def testVariableRankUpdate(self): self._VariableRankTests(state_ops.scatter_update, False) + @test_util.run_deprecated_v1 def testVariableRankAdd(self): self._VariableRankTests(state_ops.scatter_add, False) + @test_util.run_deprecated_v1 def testVariableRankSub(self): self._VariableRankTests(state_ops.scatter_sub, False) + @test_util.run_deprecated_v1 def testVariableRankMul(self): self._VariableRankTests(state_ops.scatter_mul, False) + @test_util.run_deprecated_v1 def testVariableRankDiv(self): self._VariableRankTests(state_ops.scatter_div, False) + @test_util.run_deprecated_v1 def testVariableRankMin(self): self._VariableRankTests(state_ops.scatter_min, False) + @test_util.run_deprecated_v1 def testVariableRankMax(self): self._VariableRankTests(state_ops.scatter_max, False) + @test_util.run_deprecated_v1 def testRepeatIndicesAdd(self): self._VariableRankTests(state_ops.scatter_add, True) + @test_util.run_deprecated_v1 def testRepeatIndicesSub(self): self._VariableRankTests(state_ops.scatter_sub, True) + @test_util.run_deprecated_v1 def testRepeatIndicesMul(self): self._VariableRankTests(state_ops.scatter_mul, True) + @test_util.run_deprecated_v1 def testRepeatIndicesDiv(self): self._VariableRankTests(state_ops.scatter_div, True) + @test_util.run_deprecated_v1 def testRepeatIndicesMin(self): self._VariableRankTests(state_ops.scatter_min, True) + @test_util.run_deprecated_v1 def testRepeatIndicesMax(self): self._VariableRankTests(state_ops.scatter_max, True) + @test_util.run_deprecated_v1 def testVariableRankUpdateScalar(self): self._VariableRankTests(state_ops.scatter_update, False, True) + @test_util.run_deprecated_v1 def testVariableRankAddScalar(self): self._VariableRankTests(state_ops.scatter_add, False, True) + @test_util.run_deprecated_v1 def testVariableRankSubScalar(self): self._VariableRankTests(state_ops.scatter_sub, False, True) + @test_util.run_deprecated_v1 def testVariableRankMulScalar(self): self._VariableRankTests(state_ops.scatter_mul, False, True) + @test_util.run_deprecated_v1 def testVariableRankDivScalar(self): self._VariableRankTests(state_ops.scatter_div, False, True) + @test_util.run_deprecated_v1 def testVariableRankMinScalar(self): self._VariableRankTests(state_ops.scatter_min, False, True) + @test_util.run_deprecated_v1 def testVariableRankMaxScalar(self): self._VariableRankTests(state_ops.scatter_max, False, True) + @test_util.run_deprecated_v1 def testRepeatIndicesAddScalar(self): self._VariableRankTests(state_ops.scatter_add, True, True) + @test_util.run_deprecated_v1 def testRepeatIndicesSubScalar(self): self._VariableRankTests(state_ops.scatter_sub, True, True) + @test_util.run_deprecated_v1 def testRepeatIndicesMulScalar(self): self._VariableRankTests(state_ops.scatter_mul, True, True) + @test_util.run_deprecated_v1 def testRepeatIndicesDivScalar(self): self._VariableRankTests(state_ops.scatter_div, True, True) + @test_util.run_deprecated_v1 def testRepeatIndicesMinScalar(self): self._VariableRankTests(state_ops.scatter_min, True, True) + @test_util.run_deprecated_v1 def testRepeatIndicesMaxScalar(self): self._VariableRankTests(state_ops.scatter_max, True, True) + @test_util.run_deprecated_v1 def testBooleanScatterUpdate(self): if not test.is_gpu_available(): with self.session(use_gpu=False) as session: @@ -286,8 +314,9 @@ class ScatterTest(test.TestCase): session.run([update0, update1]) - self.assertAllEqual([False, True], var.eval()) + self.assertAllEqual([False, True], self.evaluate(var)) + @test_util.run_deprecated_v1 def testScatterOutOfRangeCpu(self): for op, _ in _TF_OPS_TO_NUMPY.items(): params = np.array([1, 2, 3, 4, 5, 6]).astype(np.float32) @@ -320,19 +349,19 @@ class ScatterTest(test.TestCase): updates = np.array([-3, -4, -5]).astype(np.float32) # With GPU, the code ignores indices that are out of range. # We don't test the implementation; just test there's no failures. - with self.cached_session(force_gpu=True): + with test_util.force_gpu(): ref = variables.Variable(params) ref.initializer.run() # Indices all in range, no problem. indices = np.array([2, 0, 5]) - op(ref, indices, updates).eval() + self.evaluate(op(ref, indices, updates)) # Indicies out of range should not fail. indices = np.array([-1, 0, 5]) - op(ref, indices, updates).eval() + self.evaluate(op(ref, indices, updates)) indices = np.array([2, 0, 6]) - op(ref, indices, updates).eval() + self.evaluate(op(ref, indices, updates)) if __name__ == '__main__': diff --git a/tensorflow/python/kernel_tests/segment_reduction_ops_test.py b/tensorflow/python/kernel_tests/segment_reduction_ops_test.py index 3f7e43b533..8af1b47e83 100644 --- a/tensorflow/python/kernel_tests/segment_reduction_ops_test.py +++ b/tensorflow/python/kernel_tests/segment_reduction_ops_test.py @@ -26,6 +26,7 @@ from tensorflow.python.client import session from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes as dtypes_lib from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import math_ops from tensorflow.python.ops import variables @@ -118,7 +119,7 @@ class SegmentReductionOpTest(SegmentReductionHelper): for np_op1, np_op2, tf_op in curr_ops_list: np_ans = self._segmentReduce(indices, np_x, np_op1, np_op2) s = tf_op(data=tf_x, segment_ids=indices) - tf_ans = s.eval() + tf_ans = self.evaluate(s) self.assertAllClose(np_ans, tf_ans) # NOTE(mrry): The static shape inference that computes # `tf_ans.shape` can only infer that sizes from dimension 1 @@ -126,6 +127,7 @@ class SegmentReductionOpTest(SegmentReductionHelper): # and may therefore vary dynamically. self.assertAllEqual(np_ans.shape[1:], tf_ans.shape[1:]) + @test_util.run_deprecated_v1 def testSegmentIdsShape(self): shape = [4, 4] tf_x, _ = self._input(shape) @@ -133,6 +135,7 @@ class SegmentReductionOpTest(SegmentReductionHelper): with self.assertRaises(ValueError): math_ops.segment_sum(data=tf_x, segment_ids=indices) + @test_util.run_deprecated_v1 def testSegmentIdsSize(self): shape = [4, 4] for use_gpu in [True, False]: @@ -141,8 +144,9 @@ class SegmentReductionOpTest(SegmentReductionHelper): indices = [0, 1] s = math_ops.segment_sum(data=tf_x, segment_ids=indices) with self.assertRaisesOpError("segment_ids should be the same size"): - s.eval() + self.evaluate(s) + @test_util.run_deprecated_v1 def testSegmentIdsValid(self): # This is a baseline for the following SegmentIdsInvalid* tests. shape = [4, 4] @@ -161,7 +165,7 @@ class SegmentReductionOpTest(SegmentReductionHelper): indices = [1, 1, 2, 2] np_ans = self._segmentReduce(indices, np_x, np.add) s = math_ops.segment_sum(data=tf_x, segment_ids=indices) - tf_ans = s.eval() + tf_ans = self.evaluate(s) self.assertAllClose(np_ans, tf_ans) def testSegmentIdsHole(self): @@ -172,9 +176,10 @@ class SegmentReductionOpTest(SegmentReductionHelper): indices = [0, 0, 3, 3] np_ans = self._segmentReduce(indices, np_x, np.add) s = math_ops.segment_sum(data=tf_x, segment_ids=indices) - tf_ans = s.eval() + tf_ans = self.evaluate(s) self.assertAllClose(np_ans, tf_ans) + @test_util.run_deprecated_v1 def testSegmentIdsInvalid1(self): shape = [4, 4] with self.cached_session(): @@ -184,8 +189,9 @@ class SegmentReductionOpTest(SegmentReductionHelper): with self.assertRaisesOpError( r"Segment id -1 out of range \[0, 1\), possibly because " "'segment_ids' input is not sorted."): - s.eval() + self.evaluate(s) + @test_util.run_deprecated_v1 def testSegmentIdsInvalid2(self): shape = [4, 4] with self.cached_session(): @@ -193,8 +199,9 @@ class SegmentReductionOpTest(SegmentReductionHelper): indices = [0, 1, 0, 1] s = math_ops.segment_sum(data=tf_x, segment_ids=indices) with self.assertRaisesOpError("segment ids are not increasing"): - s.eval() + self.evaluate(s) + @test_util.run_deprecated_v1 def testSegmentIdsInvalid3(self): shape = [4, 4] with self.cached_session(): @@ -204,8 +211,9 @@ class SegmentReductionOpTest(SegmentReductionHelper): with self.assertRaisesOpError( r"Segment id 1 out of range \[0, 1\), possibly " "because 'segment_ids' input is not sorted."): - s.eval() + self.evaluate(s) + @test_util.run_deprecated_v1 def testSegmentIdsInvalid4(self): shape = [4, 4] for use_gpu in [True, False]: @@ -214,8 +222,9 @@ class SegmentReductionOpTest(SegmentReductionHelper): indices = [0, 0, 0, -1] s = math_ops.segment_sum(data=tf_x, segment_ids=indices) with self.assertRaisesOpError("segment ids must be >= 0"): - s.eval() + self.evaluate(s) + @test_util.run_deprecated_v1 def testSegmentIdsInvalid5(self): shape = [4, 4] for use_gpu in [True, False]: @@ -224,8 +233,9 @@ class SegmentReductionOpTest(SegmentReductionHelper): indices = [0, 0, 0, -2] s = math_ops.segment_sum(data=tf_x, segment_ids=indices) with self.assertRaisesOpError("segment ids must be >= 0"): - s.eval() + self.evaluate(s) + @test_util.run_deprecated_v1 def testGradient(self): shape = [4, 4] indices = [0, 1, 2, 2] @@ -297,7 +307,7 @@ class UnsortedSegmentTest(SegmentReductionHelper): indices, np_x, np_op1, np_op2, num_segments=num_segments, initial_value=init_op(dtype)) s = tf_op(tf_x, segment_ids=indices, num_segments=num_segments) - tf_ans = s.eval() + tf_ans = self.evaluate(s) if dtype is dtypes_lib.bfloat16: tf_ans = tf_ans.astype(np.float32) self.assertAllCloseAccordingToType(np_ans, tf_ans) @@ -320,10 +330,11 @@ class UnsortedSegmentTest(SegmentReductionHelper): data=tf_x, segment_ids=indices, num_segments=num_segments_constant) - tf_ans = s.eval() + tf_ans = self.evaluate(s) self.assertAllClose(np_ans, tf_ans) self.assertShapeEqual(np_ans, s) + @test_util.run_deprecated_v1 def testGradients(self): num_cols = 2 indices_flat = np.array([0, 4, 0, -1, 3, -1, 4, 7, 7, 3]) @@ -346,6 +357,7 @@ class UnsortedSegmentTest(SegmentReductionHelper): delta=1) self.assertAllClose(jacob_t, jacob_n) + @test_util.run_deprecated_v1 def testProdGrad(self): # additional test for the prod gradient to ensure correct handling of zeros values = np.array([0, 0, 1, 0, 2, 2, 3, 3, 3], dtype=np.float32) @@ -370,6 +382,7 @@ class UnsortedSegmentTest(SegmentReductionHelper): self.assertAllClose(jacob_t, jacob_n) self.assertAllClose(jacob_t, grad_gt) + @test_util.run_deprecated_v1 def testGradientMatchesSegmentSum(self): # Strategy: compute the gradient for UnsortedSegmentSum and SegmentSum # and compare the outputs, which should be identical. @@ -403,6 +416,7 @@ class UnsortedSegmentTest(SegmentReductionHelper): self.assertAllClose(unsorted_jacob_t, sorted_jacob_t) self.assertAllClose(unsorted_jacob_n, sorted_jacob_n) + @test_util.run_deprecated_v1 def testBadIndices(self): # Note: GPU kernel does not return the out-of-range error needed for this # test, so this test is marked as cpu-only. @@ -412,8 +426,9 @@ class UnsortedSegmentTest(SegmentReductionHelper): unsorted = math_ops.unsorted_segment_sum([[17]], bad, num_segments=2) with self.assertRaisesOpError( r"segment_ids\[0,0\] = %d is out of range \[0, 2\)" % bad[0][0]): - unsorted.eval() + self.evaluate(unsorted) + @test_util.run_deprecated_v1 def testEmptySecondDimension(self): dtypes = [np.float16, np.float32, np.float64, np.int64, np.int32, np.complex64, np.complex128] @@ -443,7 +458,7 @@ class UnsortedSegmentTest(SegmentReductionHelper): np.place(indices, indices == 8, [-1]) s = math_ops.unsorted_segment_sum( data=tf_x, segment_ids=indices, num_segments=num_segments) - tf_ans = s.eval() + tf_ans = self.evaluate(s) self.assertAllClose(np_ans, tf_ans) self.assertShapeEqual(np_ans, s) @@ -499,7 +514,7 @@ class SparseSegmentReductionOpTest(SparseSegmentReductionHelper): np_ans = self._sparseSegmentReduce(np_x, np_indices, segment_indices, np_op1, np_op2) s = tf_op(data=tf_x, indices=tf_indices, segment_ids=segment_indices) - tf_ans = s.eval() + tf_ans = self.evaluate(s) self.assertAllClose(np_ans, tf_ans) # NOTE(mrry): The static shape inference that computes # `tf_ans.shape` can only infer that sizes from dimension 1 @@ -518,7 +533,7 @@ class SparseSegmentReductionOpTest(SparseSegmentReductionHelper): np_ans = self._sparseSegmentReduce(np_x, tf_indices, segment_indices, np_op1, np_op2) s = tf_op(data=tf_x, indices=tf_indices, segment_ids=segment_indices) - tf_ans = s.eval() + tf_ans = self.evaluate(s) self.assertAllClose(np_ans, tf_ans) def testWithNumSegments(self): @@ -543,7 +558,7 @@ class SparseSegmentReductionOpTest(SparseSegmentReductionHelper): indices=tf_indices, segment_ids=segment_indices, num_segments=num_segments) - tf_ans = s.eval() + tf_ans = self.evaluate(s) self.assertAllClose(np_ans, tf_ans) def testWithEmptySegments(self): @@ -562,7 +577,7 @@ class SparseSegmentReductionOpTest(SparseSegmentReductionHelper): indices=tf_indices, segment_ids=segment_indices, num_segments=num_segments) - tf_ans = s.eval() + tf_ans = self.evaluate(s) self.assertAllClose(np.zeros([5, 4]), tf_ans) def testSegmentIdsGreaterThanZero(self): @@ -576,7 +591,7 @@ class SparseSegmentReductionOpTest(SparseSegmentReductionHelper): np_ans = self._sparseSegmentReduce(np_x, tf_indices, segment_indices, np_op1, np_op2) s = tf_op(data=tf_x, indices=tf_indices, segment_ids=segment_indices) - tf_ans = s.eval() + tf_ans = self.evaluate(s) self.assertAllClose(np_ans, tf_ans) def testValid(self): @@ -588,8 +603,9 @@ class SparseSegmentReductionOpTest(SparseSegmentReductionHelper): with self.session(use_gpu=False): for tf_op in ops_list: s = tf_op(data=tf_x, indices=tf_indices, segment_ids=segment_indices) - s.eval() + self.evaluate(s) + @test_util.run_deprecated_v1 def testIndicesInvalid1(self): tf_x, _ = self._input([10, 4], dtype=dtypes_lib.float32) ops_list = [math_ops.sparse_segment_sum, math_ops.sparse_segment_mean] @@ -600,8 +616,9 @@ class SparseSegmentReductionOpTest(SparseSegmentReductionHelper): s = tf_op(data=tf_x, indices=tf_indices, segment_ids=segment_indices) with self.assertRaisesOpError( r"indices\[1\] == -1 out of range \[0, 10\)"): - s.eval() + self.evaluate(s) + @test_util.run_deprecated_v1 def testIndicesInvalid2(self): tf_x, _ = self._input([10, 4], dtype=dtypes_lib.float32) ops_list = [math_ops.sparse_segment_sum, math_ops.sparse_segment_mean] @@ -612,8 +629,9 @@ class SparseSegmentReductionOpTest(SparseSegmentReductionHelper): s = tf_op(data=tf_x, indices=tf_indices, segment_ids=segment_indices) with self.assertRaisesOpError( r"indices\[3\] == 10 out of range \[0, 10\)"): - s.eval() + self.evaluate(s) + @test_util.run_deprecated_v1 def testSegmentsInvalid2(self): tf_x, _ = self._input([10, 4], dtype=dtypes_lib.float32) ops_list = [math_ops.sparse_segment_sum, math_ops.sparse_segment_mean] @@ -623,8 +641,9 @@ class SparseSegmentReductionOpTest(SparseSegmentReductionHelper): for tf_op in ops_list: s = tf_op(data=tf_x, indices=tf_indices, segment_ids=segment_indices) with self.assertRaisesOpError("segment ids are not increasing"): - s.eval() + self.evaluate(s) + @test_util.run_deprecated_v1 def testSegmentsInvalid3(self): tf_x, _ = self._input([10, 4], dtype=dtypes_lib.float32) ops_list = [math_ops.sparse_segment_sum, math_ops.sparse_segment_mean] @@ -636,8 +655,9 @@ class SparseSegmentReductionOpTest(SparseSegmentReductionHelper): with self.assertRaisesOpError( r"Segment id 1 out of range \[0, 1\), possibly because " "'segment_ids' input is not sorted"): - s.eval() + self.evaluate(s) + @test_util.run_deprecated_v1 def testSegmentsInvalid4(self): tf_x, _ = self._input([10, 4], dtype=dtypes_lib.float32) ops_list = [math_ops.sparse_segment_sum, math_ops.sparse_segment_mean] @@ -649,8 +669,9 @@ class SparseSegmentReductionOpTest(SparseSegmentReductionHelper): with self.assertRaisesOpError( r"Segment id -1 out of range \[0, 2\), possibly because " "'segment_ids' input is not sorted"): - s.eval() + self.evaluate(s) + @test_util.run_deprecated_v1 def testSegmentsInvalid6(self): tf_x, _ = self._input([10, 4], dtype=dtypes_lib.float32) ops_list = [math_ops.sparse_segment_sum, math_ops.sparse_segment_mean] @@ -660,8 +681,9 @@ class SparseSegmentReductionOpTest(SparseSegmentReductionHelper): for tf_op in ops_list: s = tf_op(data=tf_x, indices=tf_indices, segment_ids=segment_indices) with self.assertRaisesOpError("segment ids must be >= 0"): - s.eval() + self.evaluate(s) + @test_util.run_deprecated_v1 def testSegmentsInvalid7(self): tf_x, _ = self._input([10, 4], dtype=dtypes_lib.float32) ops_list = [math_ops.sparse_segment_sum, math_ops.sparse_segment_mean] @@ -671,7 +693,7 @@ class SparseSegmentReductionOpTest(SparseSegmentReductionHelper): for tf_op in ops_list: s = tf_op(data=tf_x, indices=tf_indices, segment_ids=segment_indices) with self.assertRaisesOpError("segment ids must be >= 0"): - s.eval() + self.evaluate(s) def testSegmentWithNumSegmentsValid(self): # Baseline for the test*WithNumSegmentsInvalid* methods below. @@ -690,8 +712,9 @@ class SparseSegmentReductionOpTest(SparseSegmentReductionHelper): indices=tf_indices, segment_ids=segment_indices, num_segments=num_segments) - s.eval() + self.evaluate(s) + @test_util.run_deprecated_v1 def testSegmentWithNumSegmentsInvalid1(self): tf_x, _ = self._input([10, 4], dtype=dtypes_lib.float32) ops_list = [ @@ -709,8 +732,9 @@ class SparseSegmentReductionOpTest(SparseSegmentReductionHelper): segment_ids=segment_indices, num_segments=num_segments) with self.assertRaisesOpError("segment ids must be < num_segments"): - s.eval() + self.evaluate(s) + @test_util.run_deprecated_v1 def testSegmentWithNumSegmentsInvalid2(self): tf_x, _ = self._input([10, 4], dtype=dtypes_lib.float32) ops_list = [ @@ -730,6 +754,7 @@ class SparseSegmentReductionOpTest(SparseSegmentReductionHelper): segment_ids=segment_indices, num_segments=num_segments) + @test_util.run_deprecated_v1 def testGradient(self): shape = [10, 4] @@ -748,6 +773,7 @@ class SparseSegmentReductionOpTest(SparseSegmentReductionHelper): delta=1) self.assertAllClose(jacob_t, jacob_n) + @test_util.run_deprecated_v1 def testGradientWithEmptySegmentsAtEnd(self): shape = [10, 4] @@ -785,8 +811,9 @@ class SparseSegmentReductionOpTest(SparseSegmentReductionHelper): with self.session(use_gpu=False): for tf_op in ops_list: s = tf_op(tf_x, tf_indices, segment_indices, 10) - s.eval() + self.evaluate(s) + @test_util.run_deprecated_v1 def testGradientIndicesInvalid1(self): tf_x, _ = self._input([3, 4], dtype=dtypes_lib.float32) ops_list = [ @@ -798,8 +825,9 @@ class SparseSegmentReductionOpTest(SparseSegmentReductionHelper): for tf_op in ops_list: s = tf_op(tf_x, tf_indices, segment_indices, 10) with self.assertRaisesOpError(r"Index 10 out of range \[0, 10\)"): - s.eval() + self.evaluate(s) + @test_util.run_deprecated_v1 def testGradientIndicesInvalid2(self): tf_x, _ = self._input([3, 4], dtype=dtypes_lib.float32) ops_list = [ @@ -811,8 +839,9 @@ class SparseSegmentReductionOpTest(SparseSegmentReductionHelper): for tf_op in ops_list: s = tf_op(tf_x, tf_indices, segment_indices, 10) with self.assertRaisesOpError(r"Index -1 out of range \[0, 10\)"): - s.eval() + self.evaluate(s) + @test_util.run_deprecated_v1 def testGradientSegmentsInvalid1(self): tf_x, _ = self._input( [3, 4], dtype=dtypes_lib.float32) # expecting 3 segments @@ -825,8 +854,9 @@ class SparseSegmentReductionOpTest(SparseSegmentReductionHelper): for tf_op in ops_list: s = tf_op(tf_x, tf_indices, segment_indices, 10) with self.assertRaisesOpError("Invalid number of segments"): - s.eval() + self.evaluate(s) + @test_util.run_deprecated_v1 def testGradientSegmentsInvalid2(self): tf_x, _ = self._input([1, 4], dtype=dtypes_lib.float32) ops_list = [ @@ -838,8 +868,9 @@ class SparseSegmentReductionOpTest(SparseSegmentReductionHelper): for tf_op in ops_list: s = tf_op(tf_x, tf_indices, segment_indices, 10) with self.assertRaisesOpError(r"Segment id 1 out of range \[0, 1\)"): - s.eval() + self.evaluate(s) + @test_util.run_deprecated_v1 def testGradientSegmentsInvalid3(self): tf_x, _ = self._input([2, 4], dtype=dtypes_lib.float32) ops_list = [ @@ -851,8 +882,9 @@ class SparseSegmentReductionOpTest(SparseSegmentReductionHelper): for tf_op in ops_list: s = tf_op(tf_x, tf_indices, segment_indices, 10) with self.assertRaisesOpError(r"Segment id -1 out of range \[0, 2\)"): - s.eval() + self.evaluate(s) + @test_util.run_deprecated_v1 def testGradientSegmentsInvalid4(self): tf_x, _ = self._input([0, 4], dtype=dtypes_lib.float32) ops_list = [ @@ -864,7 +896,8 @@ class SparseSegmentReductionOpTest(SparseSegmentReductionHelper): for tf_op in ops_list: s = tf_op(tf_x, tf_indices, segment_indices, 10) with self.assertRaisesOpError(r"Segment id 0 out of range \[0, 0\)"): - s.eval() + self.evaluate(s) + class SegmentReductionOpBenchmark(test.Benchmark): outer_dim_options = [2**x for x in range(9, 14, 2)] 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 1b4aff8c9c..42577f7e42 100644 --- a/tensorflow/python/kernel_tests/self_adjoint_eig_op_test.py +++ b/tensorflow/python/kernel_tests/self_adjoint_eig_op_test.py @@ -63,7 +63,7 @@ class SelfAdjointEigTest(test.TestCase): e1 = linalg_ops.self_adjoint_eigvals(matrix1) e2 = linalg_ops.self_adjoint_eigvals(matrix2) all_ops += [e1, e2] - val = sess.run(all_ops) + 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. @@ -81,7 +81,7 @@ class SelfAdjointEigTest(test.TestCase): self.assertEqual(matrix.shape, (32, 32)) matrix_tensor = constant_op.constant(matrix) with self.session(use_gpu=True) as sess: - (e, v) = sess.run(linalg_ops.self_adjoint_eig(matrix_tensor)) + (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) @@ -164,8 +164,8 @@ def _GetSelfAdjointEigTest(dtype_, shape_, compute_v_): self.assertAllClose(a_ev.eval(), a, atol=atol) # Compare to numpy.linalg.eigh. - CompareEigenDecompositions(self, np_e, np_v, - tf_e.eval(), tf_v.eval(), atol) + CompareEigenDecompositions(self, np_e, np_v, self.evaluate(tf_e), + self.evaluate(tf_v), atol) else: tf_e = linalg_ops.self_adjoint_eigvals(constant_op.constant(a)) self.assertAllClose( diff --git a/tensorflow/python/kernel_tests/session_ops_test.py b/tensorflow/python/kernel_tests/session_ops_test.py index 03e1ae852f..dc663cb091 100644 --- a/tensorflow/python/kernel_tests/session_ops_test.py +++ b/tensorflow/python/kernel_tests/session_ops_test.py @@ -37,7 +37,7 @@ class SessionOpsTest(test.TestCase): b = constant_op.constant(5) c = math_ops.multiply(a, b) h = session_ops.get_session_handle(c) - h = sess.run(h) + h = self.evaluate(h) # Feed a tensor handle. f, x = session_ops.get_session_tensor(h.handle, dtypes.int32) @@ -51,7 +51,7 @@ class SessionOpsTest(test.TestCase): b = constant_op.constant(5) c = math_ops.multiply(a, b) h = session_ops.get_session_handle(c) - h = sess.run(h) + h = self.evaluate(h) # Get the tensor from its handle. self.assertEqual(50, h.eval()) @@ -64,7 +64,7 @@ class SessionOpsTest(test.TestCase): c = math_ops.multiply(a, b) h = session_ops.get_session_handle(c) v = math_ops.multiply(a, c) - h, v = sess.run([h, v]) + h, v = self.evaluate([h, v]) self.assertEqual(50, h.eval()) self.assertEqual(500, v) @@ -77,7 +77,7 @@ class SessionOpsTest(test.TestCase): p = math_ops.less(a, b) c = math_ops.multiply(a, b) h = session_ops.get_session_handle(c) - p, h = sess.run([p, h]) + p, h = self.evaluate([p, h]) # Run by feeding a tensor handle. f, x = session_ops.get_session_tensor(h.handle, dtypes.int32) @@ -94,7 +94,7 @@ class SessionOpsTest(test.TestCase): # Initialize a handle. a = constant_op.constant(0) h = session_ops.get_session_handle(a) - h = sess.run(h) + h = self.evaluate(h) # Do some computation. f, x = session_ops.get_session_tensor(h.handle, dtypes.int32) @@ -111,7 +111,7 @@ class SessionOpsTest(test.TestCase): # Initialize a handle. a = constant_op.constant(0) h = session_ops.get_session_handle(a) - h = sess.run(h) + h = self.evaluate(h) # Do some computation. f, x = session_ops.get_session_tensor(h.handle, dtypes.int32) @@ -133,7 +133,7 @@ class SessionOpsTest(test.TestCase): b = constant_op.constant(5) c = math_ops.multiply(a, b) h = session_ops.get_session_handle(c) - h = sess.run(h) + h = self.evaluate(h) # Feed a tensor handle. f, x = session_ops.get_session_tensor(h.handle, dtypes.int32) @@ -144,7 +144,7 @@ class SessionOpsTest(test.TestCase): with ops.device(test.gpu_device_name()): a = constant_op.constant(10) h = session_ops.get_session_handle(a) - h = sess.run(h) + h = self.evaluate(h) self.assertEqual(100, sess.run(y, feed_dict={f: h.handle})) def testHandleDelete(self): @@ -154,7 +154,7 @@ class SessionOpsTest(test.TestCase): b = constant_op.constant(5) c = math_ops.multiply(a, b) h = session_ops.get_session_handle(c) - sess.run(h).delete() + self.evaluate(h).delete() def testHandleDeleteRaw(self): with self.cached_session() as sess: @@ -163,7 +163,7 @@ class SessionOpsTest(test.TestCase): b = constant_op.constant(5) c = math_ops.multiply(a, b) h = session_ops.get_session_handle(c) - h = sess.run(h) + h = self.evaluate(h) # Delete using a raw tensor handle. raw_h = h.get_raw_handle() @@ -174,10 +174,10 @@ class SessionOpsTest(test.TestCase): with self.cached_session() as sess: with ops.device(test.gpu_device_name()): a = constant_op.constant(1.0) - a_handle = sess.run(session_ops.get_session_handle(a)) + a_handle = self.evaluate(session_ops.get_session_handle(a)) with ops.device("/cpu:0"): b = constant_op.constant(2.0) - b_handle = sess.run(session_ops.get_session_handle(b)) + b_handle = self.evaluate(session_ops.get_session_handle(b)) a_p, a_t = session_ops.get_session_tensor(a_handle.handle, dtypes.float32) b_p, b_t = session_ops.get_session_tensor(b_handle.handle, dtypes.float32) @@ -193,8 +193,8 @@ class SessionOpsTest(test.TestCase): # initial values live on CPU with ops.device("/cpu:0"): one = constant_op.constant(1, dtype=dtypes.float32) - one_handle = sess.run(session_ops.get_session_handle(one)) - x_handle = sess.run(session_ops.get_session_handle(one)) + one_handle = self.evaluate(session_ops.get_session_handle(one)) + x_handle = self.evaluate(session_ops.get_session_handle(one)) # addition lives on GPU with ops.device(test.gpu_device_name()): @@ -219,8 +219,8 @@ class SessionOpsTest(test.TestCase): b = constant_op.constant(2.0) b_handle_op = session_ops.get_session_handle(b) - a_handle = sess.run(a_handle_op) - b_handle = sess.run(b_handle_op) + a_handle = self.evaluate(a_handle_op) + b_handle = self.evaluate(b_handle_op) a_p, a_t = session_ops.get_session_tensor(a_handle.handle, dtypes.float32) b_p, b_t = session_ops.get_session_tensor(b_handle.handle, dtypes.float32) @@ -239,7 +239,7 @@ class SessionOpsTest(test.TestCase): c = math_ops.multiply(a, b) d = math_ops.multiply(c, c) - h_c = sess.run(session_ops.get_session_handle(c)) + h_c = self.evaluate(session_ops.get_session_handle(c)) self.assertAllClose(2500.0, sess.run(d, feed_dict={c: h_c})) @@ -248,7 +248,7 @@ class SessionOpsTest(test.TestCase): a = constant_op.constant(10.0) b = constant_op.constant(5.0) c = math_ops.multiply(a, b) - h_c = sess.run(session_ops.get_session_handle(c)) + h_c = self.evaluate(session_ops.get_session_handle(c)) d = array_ops.identity(c) c_val = sess.run(c, feed_dict={c: h_c}) @@ -277,8 +277,8 @@ class SessionOpsTest(test.TestCase): d = math_ops.div(a, b) e = math_ops.subtract(c, d) - h_c = sess.run(session_ops.get_session_handle(c)) - h_d = sess.run(session_ops.get_session_handle(d)) + h_c = self.evaluate(session_ops.get_session_handle(c)) + h_d = self.evaluate(session_ops.get_session_handle(d)) self.assertAllClose(48.0, sess.run(e, feed_dict={c: h_c, d: h_d})) self.assertAllClose(-48.0, sess.run(e, feed_dict={c: h_d, d: h_c})) @@ -288,13 +288,13 @@ class SessionOpsTest(test.TestCase): a = variables.Variable(12.0) inc_a = state_ops.assign_add(a, 2.0) b = math_ops.add(a, 5.0) - sess.run(a.initializer) + self.evaluate(a.initializer) h_a_read = sess.run(session_ops.get_session_handle(a.read_value())) - self.assertAllClose(12.0, sess.run(a)) + self.assertAllClose(12.0, self.evaluate(a)) self.assertAllClose(17.0, sess.run(b, feed_dict={a: h_a_read})) - sess.run(inc_a) + self.evaluate(inc_a) self.assertAllClose(19.0, sess.run(b, feed_dict={a: h_a_read})) diff --git a/tensorflow/python/kernel_tests/sets_test.py b/tensorflow/python/kernel_tests/sets_test.py index 8335e9c139..b4f2322934 100644 --- a/tensorflow/python/kernel_tests/sets_test.py +++ b/tensorflow/python/kernel_tests/sets_test.py @@ -70,6 +70,7 @@ def _dense_to_sparse(dense, dtype): class SetOpsTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def test_set_size_2d(self): for dtype in _DTYPES: self._test_set_size_2d(dtype) @@ -83,6 +84,7 @@ class SetOpsTest(test_util.TensorFlowTestCase): self.assertAllEqual( [0, 3], self._set_size(_dense_to_sparse([[], [1, 9, 2]], dtype))) + @test_util.run_deprecated_v1 def test_set_size_duplicates_2d(self): for dtype in _DTYPES: self._test_set_size_duplicates_2d(dtype) @@ -96,6 +98,7 @@ class SetOpsTest(test_util.TensorFlowTestCase): 6, 7, 8, 8, 6, 7, 5, 3, 3, 0, 6, 6, 9, 0, 0, 0 ], [999, 1, -1000], [], [-1]], dtype))) + @test_util.run_deprecated_v1 def test_set_size_3d(self): for dtype in _DTYPES: self._test_set_size_3d(dtype) @@ -159,10 +162,11 @@ class SetOpsTest(test_util.TensorFlowTestCase): self.assertEqual(None, op.get_shape().dims) self.assertEqual(dtypes.int32, op.dtype) with self.cached_session() as sess: - results = sess.run(ops) + results = self.evaluate(ops) self.assertAllEqual(results[0], results[1]) return results[0] + @test_util.run_deprecated_v1 def test_set_intersection_multirow_2d(self): for dtype in _DTYPES: self._test_set_intersection_multirow_2d(dtype) @@ -199,6 +203,7 @@ class SetOpsTest(test_util.TensorFlowTestCase): self.assertAllEqual(expected_counts, self._set_intersection_count(sp_a, sp_b)) + @test_util.run_deprecated_v1 def test_dense_set_intersection_multirow_2d(self): for dtype in _DTYPES: self._test_dense_set_intersection_multirow_2d(dtype) @@ -223,6 +228,7 @@ class SetOpsTest(test_util.TensorFlowTestCase): dtype=dtype) self.assertAllEqual(expected_counts, self._set_intersection_count(a, b)) + @test_util.run_deprecated_v1 def test_set_intersection_duplicates_2d(self): for dtype in _DTYPES: self._test_set_intersection_duplicates_2d(dtype) @@ -270,6 +276,7 @@ class SetOpsTest(test_util.TensorFlowTestCase): self.assertAllEqual(expected_counts, self._set_intersection_count(sp_a, sp_b)) + @test_util.run_deprecated_v1 def test_set_intersection_3d(self): for dtype in _DTYPES: self._test_set_intersection_3d(dtype=dtype) @@ -534,8 +541,9 @@ class SetOpsTest(test_util.TensorFlowTestCase): def _set_intersection_count(self, a, b): op = sets.set_size(sets.set_intersection(a, b)) with self.cached_session() as sess: - return sess.run(op) + return self.evaluate(op) + @test_util.run_deprecated_v1 def test_set_difference_multirow_2d(self): for dtype in _DTYPES: self._test_set_difference_multirow_2d(dtype) @@ -604,6 +612,7 @@ class SetOpsTest(test_util.TensorFlowTestCase): self.assertAllEqual(expected_counts, self._set_difference_count(sp_a, sp_b, False)) + @test_util.run_deprecated_v1 def test_dense_set_difference_multirow_2d(self): for dtype in _DTYPES: self._test_dense_set_difference_multirow_2d(dtype) @@ -647,6 +656,7 @@ class SetOpsTest(test_util.TensorFlowTestCase): self.assertAllEqual(expected_counts, self._set_difference_count(a, b, False)) + @test_util.run_deprecated_v1 def test_sparse_set_difference_multirow_2d(self): for dtype in _DTYPES: self._test_sparse_set_difference_multirow_2d(dtype) @@ -688,6 +698,7 @@ class SetOpsTest(test_util.TensorFlowTestCase): self.assertAllEqual(expected_counts, self._set_difference_count(sp_a, sp_b, False)) + @test_util.run_deprecated_v1 def test_set_difference_duplicates_2d(self): for dtype in _DTYPES: self._test_set_difference_duplicates_2d(dtype) @@ -755,6 +766,7 @@ class SetOpsTest(test_util.TensorFlowTestCase): self.assertAllEqual(expected_counts, self._set_difference_count(a, sp_b, False)) + @test_util.run_deprecated_v1 def test_sparse_set_difference_3d(self): for dtype in _DTYPES: self._test_sparse_set_difference_3d(dtype) @@ -972,8 +984,9 @@ class SetOpsTest(test_util.TensorFlowTestCase): def _set_difference_count(self, a, b, aminusb=True): op = sets.set_size(sets.set_difference(a, b, aminusb)) with self.cached_session() as sess: - return sess.run(op) + return self.evaluate(op) + @test_util.run_deprecated_v1 def test_set_union_multirow_2d(self): for dtype in _DTYPES: self._test_set_union_multirow_2d(dtype) @@ -1001,6 +1014,7 @@ class SetOpsTest(test_util.TensorFlowTestCase): expected_indices, expected_values, expected_shape, union, dtype=dtype) self.assertAllEqual(expected_counts, self._set_union_count(sp_a, sp_b)) + @test_util.run_deprecated_v1 def test_dense_set_union_multirow_2d(self): for dtype in _DTYPES: self._test_dense_set_union_multirow_2d(dtype) @@ -1021,6 +1035,7 @@ class SetOpsTest(test_util.TensorFlowTestCase): expected_indices, expected_values, expected_shape, union, dtype=dtype) self.assertAllEqual(expected_counts, self._set_union_count(a, b)) + @test_util.run_deprecated_v1 def test_set_union_duplicates_2d(self): for dtype in _DTYPES: self._test_set_union_duplicates_2d(dtype) @@ -1047,6 +1062,7 @@ class SetOpsTest(test_util.TensorFlowTestCase): expected_indices, expected_values, expected_shape, union, dtype=dtype) self.assertAllEqual([2], self._set_union_count(sp_a, sp_b)) + @test_util.run_deprecated_v1 def test_sparse_set_union_3d(self): for dtype in _DTYPES: self._test_sparse_set_union_3d(dtype) @@ -1221,7 +1237,7 @@ class SetOpsTest(test_util.TensorFlowTestCase): def _set_union_count(self, a, b): op = sets.set_size(sets.set_union(a, b)) with self.cached_session() as sess: - return sess.run(op) + return self.evaluate(op) def _assert_set_operation(self, expected_indices, expected_values, expected_shape, sparse_tensor_value, dtype): diff --git a/tensorflow/python/kernel_tests/shape_ops_test.py b/tensorflow/python/kernel_tests/shape_ops_test.py index ee813e5ffd..c8e7c143ad 100644 --- a/tensorflow/python/kernel_tests/shape_ops_test.py +++ b/tensorflow/python/kernel_tests/shape_ops_test.py @@ -26,6 +26,7 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors_impl from tensorflow.python.framework import importer from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import gradients_impl @@ -53,8 +54,8 @@ class ShapeOpsTest(test.TestCase): with self.cached_session(use_gpu=use_gpu): tf_ans = array_ops.shape(x) tf_ans_64 = array_ops.shape(x, out_type=dtypes.int64) - result = tf_ans.eval() - result_64 = tf_ans_64.eval() + result = self.evaluate(tf_ans) + result_64 = self.evaluate(tf_ans_64) self.assertAllEqual(np_ans, result) self.assertAllEqual(np_ans, result_64) self.assertShapeEqual(np_ans, tf_ans) @@ -64,7 +65,7 @@ class ShapeOpsTest(test.TestCase): x_tf, unused_nnz = _sparsify(x_np) with self.cached_session(use_gpu=use_gpu): tf_ans = array_ops.shape(x_tf) - result = tf_ans.eval() + result = self.evaluate(tf_ans) self.assertAllEqual(np_ans, result) self.assertShapeEqual(np_ans, tf_ans) @@ -73,8 +74,8 @@ class ShapeOpsTest(test.TestCase): with self.cached_session(use_gpu=use_gpu) as sess: tf_ans = array_ops.shape_n([x, x, x]) tf_ans_64 = array_ops.shape_n([x, x, x], out_type=dtypes.int64) - result = sess.run(tf_ans) - result_64 = sess.run(tf_ans_64) + result = self.evaluate(tf_ans) + result_64 = self.evaluate(tf_ans_64) for i in range(3): self.assertAllEqual(np_ans, result[i]) self.assertAllEqual(np_ans, result_64[i]) @@ -84,7 +85,7 @@ class ShapeOpsTest(test.TestCase): np_ans = np.asarray(np.ndim(x)) with self.cached_session(use_gpu=use_gpu): tf_ans = array_ops.rank(x) - result = tf_ans.eval() + result = self.evaluate(tf_ans) self.assertAllEqual(np_ans, result) self.assertShapeEqual(np_ans, tf_ans) @@ -93,7 +94,7 @@ class ShapeOpsTest(test.TestCase): x_tf, unused_nnz = _sparsify(x_np) with self.cached_session(use_gpu=use_gpu): tf_ans = array_ops.rank(x_tf) - result = tf_ans.eval() + result = self.evaluate(tf_ans) self.assertAllEqual(np_ans, result) self.assertShapeEqual(np_ans, tf_ans) @@ -101,9 +102,9 @@ class ShapeOpsTest(test.TestCase): np_ans = np.asarray(np.size(x)) with self.cached_session(use_gpu=use_gpu): tf_ans = array_ops.size(x) - result = tf_ans.eval() + result = self.evaluate(tf_ans) tf_ans_64 = array_ops.size(x, out_type=dtypes.int64) - result_64 = tf_ans_64.eval() + result_64 = self.evaluate(tf_ans_64) self.assertAllEqual(np_ans, result) self.assertAllEqual(np_ans, result_64) self.assertShapeEqual(np_ans, tf_ans) @@ -113,7 +114,7 @@ class ShapeOpsTest(test.TestCase): x_tf, unused_nnz = _sparsify(x_np) with self.cached_session(use_gpu=use_gpu): tf_ans = array_ops.size(x_tf) - result = tf_ans.eval() + result = self.evaluate(tf_ans) self.assertAllEqual(np_ans, result) self.assertShapeEqual(np_ans, tf_ans) @@ -162,7 +163,7 @@ class ShapeOpsTest(test.TestCase): inp = array_ops.zeros([2**31]) num_elements = array_ops.size_internal( inp, optimize=False, out_type=dtypes.int64) - self.assertEqual(2**31, num_elements.eval()) + self.assertEqual(2**31, self.evaluate(num_elements)) # Too large for tf.int32 output. with self.assertRaises(errors_impl.InvalidArgumentError): @@ -170,13 +171,13 @@ class ShapeOpsTest(test.TestCase): inp = array_ops.zeros([2**31]) num_elements = array_ops.size_internal( inp, optimize=False, out_type=dtypes.int32) - self.assertEqual(2**31, num_elements.eval()) + self.assertEqual(2**31, self.evaluate(num_elements)) def _compareExpandDims(self, x, dim, use_gpu): np_ans = np.expand_dims(x, axis=dim) with self.cached_session(use_gpu=use_gpu): tensor = array_ops.expand_dims(x, dim) - tf_ans = tensor.eval() + tf_ans = self.evaluate(tensor) self.assertShapeEqual(np_ans, tensor) self.assertAllEqual(np_ans, tf_ans) @@ -227,6 +228,7 @@ class ShapeOpsTest(test.TestCase): self._compareExpandDimsAll(choice([2, 3, 5]), -3) self._compareExpandDimsAll(choice([2, 3, 5]), -4) + @test_util.run_deprecated_v1 def testExpandDimsErrors(self): with self.cached_session(): self.assertRaises(ValueError, array_ops.expand_dims, @@ -238,6 +240,7 @@ class ShapeOpsTest(test.TestCase): self.assertRaises(ValueError, array_ops.expand_dims, [False, True, True], 4) + @test_util.run_deprecated_v1 def testExpandDimsGradient(self): with self.cached_session(): inp = constant_op.constant( @@ -248,6 +251,7 @@ class ShapeOpsTest(test.TestCase): [4, 1, 2]) self.assertLess(err, 1e-3) + @test_util.run_deprecated_v1 def testExpandDimsScalar(self): with self.cached_session(): inp = constant_op.constant(7) @@ -264,7 +268,7 @@ class ShapeOpsTest(test.TestCase): np_ans = np.expand_dims(x, axis=0) with self.cached_session(use_gpu=True): tensor = array_ops.expand_dims(x, constant_op.constant(0, dtype)) - tf_ans = tensor.eval() + tf_ans = self.evaluate(tensor) self.assertShapeEqual(np_ans, tensor) self.assertAllEqual(np_ans, tf_ans) @@ -273,11 +277,11 @@ class ShapeOpsTest(test.TestCase): if squeeze_dims: np_ans = np.squeeze(x, axis=tuple(squeeze_dims)) tensor = array_ops.squeeze(x, squeeze_dims) - tf_ans = tensor.eval() + tf_ans = self.evaluate(tensor) else: np_ans = np.squeeze(x) tensor = array_ops.squeeze(x) - tf_ans = tensor.eval() + tf_ans = self.evaluate(tensor) self.assertShapeEqual(np_ans, tensor) self.assertAllEqual(np_ans, tf_ans) @@ -340,7 +344,7 @@ class ShapeOpsTest(test.TestCase): with self.cached_session(use_gpu=use_gpu): tensor = array_ops.squeeze(np.zeros([1, 1, 1]), []) self.assertEqual(np.shape(1), tensor.get_shape()) - tf_ans = tensor.eval() + tf_ans = self.evaluate(tensor) self.assertEqual(np.shape(1), tf_ans.shape) def testSqueezeAllOnesBool(self): @@ -350,9 +354,10 @@ class ShapeOpsTest(test.TestCase): with self.cached_session(use_gpu=use_gpu): tensor = array_ops.squeeze([[[False]]], []) self.assertEqual(np.shape(1), tensor.get_shape()) - tf_ans = tensor.eval() + tf_ans = self.evaluate(tensor) self.assertEqual(np.shape(1), tf_ans.shape) + @test_util.run_deprecated_v1 def testSqueezeOnlyOnes(self): for use_gpu in [False, True]: with self.cached_session(use_gpu=use_gpu): @@ -362,6 +367,7 @@ class ShapeOpsTest(test.TestCase): self._compareSqueezeAll(input_1x1x3, [1]) self.assertRaises(ValueError, array_ops.squeeze, input_1x1x3, [2]) + @test_util.run_deprecated_v1 def testSqueezeErrors(self): for use_gpu in [False, True]: with self.cached_session(use_gpu=use_gpu): @@ -374,6 +380,7 @@ class ShapeOpsTest(test.TestCase): self.assertRaises(ValueError, array_ops.squeeze, np.zeros([1, 2, 1]), [2, 3]) + @test_util.run_deprecated_v1 def testSqueezeGradient(self): with self.cached_session(): inp = np.random.rand(4, 2).astype("f") @@ -384,6 +391,7 @@ class ShapeOpsTest(test.TestCase): [4, 2]) self.assertLess(err, 1e-3) + @test_util.run_deprecated_v1 def testSqueezeGradientWithSqueezeDims(self): with self.cached_session(): inp = np.random.rand(4, 2).astype("f") @@ -394,6 +402,7 @@ class ShapeOpsTest(test.TestCase): [4, 2, 1]) self.assertLess(err, 1e-3) + @test_util.run_deprecated_v1 def testSqueezeWithUnknownShape(self): with self.cached_session(): a = array_ops.placeholder(dtypes.float32, shape=[2, None]) @@ -415,7 +424,7 @@ class TileTest(test.TestCase): with self.cached_session(use_gpu=use_gpu): a = constant_op.constant(7, shape=[], dtype=dtypes.float32) tiled = array_ops.tile(a, []) - result = tiled.eval() + result = self.evaluate(tiled) self.assertEqual(result.shape, ()) self.assertEqual([], tiled.get_shape()) self.assertEqual(7, result) @@ -427,7 +436,7 @@ class TileTest(test.TestCase): inp = np.random.rand(4, 1).astype(np.float32) a = constant_op.constant(inp) tiled = array_ops.tile(a, constant_op.constant([1, 4], dtype=dtype)) - result = tiled.eval() + result = self.evaluate(tiled) self.assertEqual(result.shape, (4, 4)) self.assertEqual([4, 4], tiled.get_shape()) self.assertTrue((result == np.tile(inp, (1, 4))).all()) @@ -437,7 +446,7 @@ class TileTest(test.TestCase): inp = np.random.rand(4, 1).astype(np.float32) a = constant_op.constant(inp) tiled = array_ops.tile(a, [1, 1]) - result = tiled.eval() + result = self.evaluate(tiled) self.assertEqual(result.shape, (4, 1)) self.assertEqual([4, 1], tiled.get_shape()) self.assertTrue((result == np.tile(inp, (1, 1))).all()) @@ -447,10 +456,11 @@ class TileTest(test.TestCase): inp = np.random.rand(2, 3).astype(np.float32) a = constant_op.constant(inp) tiled = array_ops.tile(a, [5, 0]) - result = tiled.eval() + result = self.evaluate(tiled) self.assertEqual(result.shape, (10, 0)) self.assertEqual([10, 0], tiled.get_shape()) + @test_util.run_deprecated_v1 def testUnknownInputShape(self): """Importing can call _TileShape without shape of known.""" with self.cached_session(): @@ -497,11 +507,12 @@ class TileTest(test.TestCase): shape=[4, 1], dtype=dtype_tf) tiled = array_ops.tile(a, [1, 4]) - result = tiled.eval() + result = self.evaluate(tiled) self.assertEqual(result.shape, (4, 4)) self.assertEqual([4, 4], tiled.get_shape()) self.assertAllEqual(result, np.tile(inp, (1, 4))) + @test_util.run_deprecated_v1 def testInvalidDim(self): with self.cached_session(): inp = np.random.rand(4, 1).astype("f") @@ -527,7 +538,7 @@ class TileTest(test.TestCase): dtype=dtypes.float32) multiples = np.random.randint(1, 4, size=rank).astype(np.int32) tiled = array_ops.tile(a, multiples) - result = tiled.eval() + result = self.evaluate(tiled) self.assertTrue((np.array(multiples) * np.array(inp.shape) == np.array( result.shape)).all()) self.assertAllEqual(result, np.tile(inp, tuple(multiples))) @@ -545,6 +556,7 @@ class TileTest(test.TestCase): for _ in range(5): self._RunAndVerifyResult(10, use_gpu=True) + @test_util.run_deprecated_v1 def testGradientSimpleReduction(self): with self.cached_session(): inp = np.random.rand(4, 1).astype("f") @@ -557,9 +569,10 @@ class TileTest(test.TestCase): [float(x) for x in grad_inp.flatten()], shape=grad_shape) grad = gradients_impl.gradients([tiled], [a], [grad_tensor])[0] self.assertShapeEqual(inp, grad) - result = grad.eval() + result = self.evaluate(grad) self.assertAllClose(np.sum(grad_inp, axis=1).reshape(4, 1), result, 1e-3) + @test_util.run_deprecated_v1 def testGradientStridedReduction(self): with self.cached_session(): inp = np.random.rand(4, 2).astype("f") @@ -572,13 +585,14 @@ class TileTest(test.TestCase): [float(x) for x in grad_inp.flatten()], shape=grad_shape) grad = gradients_impl.gradients([tiled], [a], [grad_tensor])[0] self.assertShapeEqual(inp, grad) - result = grad.eval() + result = self.evaluate(grad) expected_shape = [4, 2] expected = np.zeros(expected_shape) expected[:, 0] = grad_inp[:, 0] + grad_inp[:, 2] expected[:, 1] = grad_inp[:, 1] + grad_inp[:, 3] self.assertTrue((np.abs(expected - result) < 1e-3).all()) + @test_util.run_deprecated_v1 def testGradientSimpleReductionOnGPU(self): with self.session(use_gpu=True): inp = np.random.rand(4, 1).astype("f") @@ -590,9 +604,10 @@ class TileTest(test.TestCase): grad_tensor = constant_op.constant( [float(x) for x in grad_inp.flatten()], shape=grad_shape) grad = gradients_impl.gradients([tiled], [a], [grad_tensor])[0] - result = grad.eval() + result = self.evaluate(grad) self.assertAllClose(np.sum(grad_inp, axis=1).reshape(4, 1), result, 1e-3) + @test_util.run_deprecated_v1 def testGradientStridedReductionOnGPU(self): with self.session(use_gpu=True): inp = np.random.rand(4, 2).astype("f") @@ -604,7 +619,7 @@ class TileTest(test.TestCase): grad_tensor = constant_op.constant( [float(x) for x in grad_inp.flatten()], shape=grad_shape) grad = gradients_impl.gradients([tiled], [a], [grad_tensor])[0] - result = grad.eval() + result = self.evaluate(grad) expected_shape = [4, 2] expected = np.zeros(expected_shape) expected[:, 0] = grad_inp[:, 0] + grad_inp[:, 2] @@ -624,15 +639,18 @@ class TileTest(test.TestCase): print("tile(float) error = ", err) self.assertLess(err, 1e-3) + @test_util.run_deprecated_v1 def testGradientRandomScalar(self): self._RunAndVerifyGradientResult([], []) + @test_util.run_deprecated_v1 def testGradientRandom(self): self._RunAndVerifyGradientResult([2, 2, 1, 1, 3], [1, 1, 1, 1, 1]) self._RunAndVerifyGradientResult([2, 2, 1, 1, 3], [1, 2, 1, 3, 1]) self._RunAndVerifyGradientResult([2, 3, 1, 1, 3], [3, 1, 1, 2, 2]) self._RunAndVerifyGradientResult([2, 1, 3, 3, 2], [1, 3, 3, 1, 2]) + @test_util.run_deprecated_v1 def testGradientStridedReductionGC(self): with self.cached_session(): inp = np.random.rand(4, 2).astype("f") @@ -642,6 +660,7 @@ class TileTest(test.TestCase): err = gradient_checker.compute_gradient_error(a, [4, 2], tiled, [4, 4]) self.assertLess(err, 1e-3) + @test_util.run_deprecated_v1 def testGradientWithSparseGradWithRank1(self): inputs = constant_op.constant([1.0, 2.0, 3.0, 4.0], dtype=dtypes.float32) @@ -653,6 +672,7 @@ class TileTest(test.TestCase): outputs, outputs.get_shape().as_list()) self.assertLess(error, 1e-4) + @test_util.run_deprecated_v1 def testGradientWithSparseGradWithRank3(self): inputs = constant_op.constant([1.0, 2.0, 3.0, 4.0], dtype=dtypes.float32) @@ -665,6 +685,7 @@ class TileTest(test.TestCase): outputs, outputs.get_shape().as_list()) self.assertLess(error, 1e-4) + @test_util.run_deprecated_v1 def testShapeFunctionEdgeCases(self): # Unknown multiples shape. inp = constant_op.constant(0.0, shape=[4, 4, 4, 4]) diff --git a/tensorflow/python/kernel_tests/signal/BUILD b/tensorflow/python/kernel_tests/signal/BUILD index 56d4d46d46..8f4e31abe3 100644 --- a/tensorflow/python/kernel_tests/signal/BUILD +++ b/tensorflow/python/kernel_tests/signal/BUILD @@ -18,6 +18,35 @@ py_library( ], ) +cuda_py_tests( + name = "dct_ops_test", + srcs = ["dct_ops_test.py"], + additional_deps = [ + "@absl_py//absl/testing:parameterized", + "//third_party/py/numpy", + "//tensorflow/python:client_testlib", + "//tensorflow/python:framework_for_generated_wrappers", + "//tensorflow/python:spectral_ops_test_util", + "//tensorflow/python/ops/signal", + ], +) + +cuda_py_tests( + name = "fft_ops_test", + size = "medium", + srcs = ["fft_ops_test.py"], + additional_deps = [ + "//third_party/py/numpy", + "//tensorflow/python:client_testlib", + "//tensorflow/python:framework_for_generated_wrappers", + "//tensorflow/python:math_ops", + "//tensorflow/python:spectral_ops_test_util", + "//tensorflow/python/ops/signal", + ], + shard_count = 4, + tags = ["optonly"], +) + cuda_py_tests( name = "mel_ops_test", srcs = ["mel_ops_test.py"], @@ -91,9 +120,9 @@ cuda_py_tests( "//tensorflow/python:framework", "//tensorflow/python:framework_for_generated_wrappers", "//tensorflow/python:framework_test_lib", - "//tensorflow/python/ops/signal", "//tensorflow/python:platform_test", "//tensorflow/python:spectral_ops_test_util", + "//tensorflow/python/ops/signal", ], tags = ["nomac"], ) diff --git a/tensorflow/python/kernel_tests/dct_ops_test.py b/tensorflow/python/kernel_tests/signal/dct_ops_test.py similarity index 67% rename from tensorflow/python/kernel_tests/dct_ops_test.py rename to tensorflow/python/kernel_tests/signal/dct_ops_test.py index c9d0167608..a3ac15bab8 100644 --- a/tensorflow/python/kernel_tests/dct_ops_test.py +++ b/tensorflow/python/kernel_tests/signal/dct_ops_test.py @@ -20,10 +20,12 @@ from __future__ import print_function import importlib +from absl.testing import parameterized import numpy as np -from tensorflow.python.ops import spectral_ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import spectral_ops_test_util +from tensorflow.python.ops.signal import dct_ops from tensorflow.python.platform import test from tensorflow.python.platform import tf_logging @@ -40,6 +42,20 @@ def try_import(name): # pylint: disable=invalid-name fftpack = try_import("scipy.fftpack") +def _np_dct1(signals, norm=None): + """Computes the DCT-I manually with NumPy.""" + # X_k = (x_0 + (-1)**k * x_{N-1} + + # 2 * sum_{n=0}^{N-2} x_n * cos(\frac{pi}{N-1} * n * k) k=0,...,N-1 + del norm + dct_size = signals.shape[-1] + dct = np.zeros_like(signals) + for k in range(dct_size): + phi = np.cos(np.pi * np.arange(1, dct_size - 1) * k / (dct_size - 1)) + dct[..., k] = 2 * np.sum(signals[..., 1:-1] * phi, axis=-1) + ( + signals[..., 0] + (-1) ** k * signals[..., -1]) + return dct + + def _np_dct2(signals, norm=None): """Computes the DCT-II manually with NumPy.""" # X_k = sum_{n=0}^{N-1} x_n * cos(\frac{pi}{N} * (n + 0.5) * k) k=0,...,N-1 @@ -81,19 +97,19 @@ def _np_dct3(signals, norm=None): return dct -NP_DCT = {2: _np_dct2, 3: _np_dct3} -NP_IDCT = {2: _np_dct3, 3: _np_dct2} +NP_DCT = {1: _np_dct1, 2: _np_dct2, 3: _np_dct3} +NP_IDCT = {1: _np_dct1, 2: _np_dct3, 3: _np_dct2} -class DCTOpsTest(test.TestCase): +class DCTOpsTest(parameterized.TestCase, test.TestCase): def _compare(self, signals, norm, dct_type, atol=5e-4, rtol=5e-4): """Compares (I)DCT to SciPy (if available) and a NumPy implementation.""" np_dct = NP_DCT[dct_type](signals, norm) - tf_dct = spectral_ops.dct(signals, type=dct_type, norm=norm).eval() + tf_dct = dct_ops.dct(signals, type=dct_type, norm=norm).eval() self.assertAllClose(np_dct, tf_dct, atol=atol, rtol=rtol) np_idct = NP_IDCT[dct_type](signals, norm) - tf_idct = spectral_ops.idct(signals, type=dct_type, norm=norm).eval() + tf_idct = dct_ops.idct(signals, type=dct_type, norm=norm).eval() self.assertAllClose(np_idct, tf_idct, atol=atol, rtol=rtol) if fftpack: scipy_dct = fftpack.dct(signals, type=dct_type, norm=norm) @@ -101,38 +117,52 @@ class DCTOpsTest(test.TestCase): scipy_idct = fftpack.idct(signals, type=dct_type, norm=norm) self.assertAllClose(scipy_idct, tf_idct, atol=atol, rtol=rtol) # Verify inverse(forward(s)) == s, up to a normalization factor. - tf_idct_dct = spectral_ops.idct( + tf_idct_dct = dct_ops.idct( tf_dct, type=dct_type, norm=norm).eval() - tf_dct_idct = spectral_ops.dct( + tf_dct_idct = dct_ops.dct( tf_idct, type=dct_type, norm=norm).eval() if norm is None: - tf_idct_dct *= 0.5 / signals.shape[-1] - tf_dct_idct *= 0.5 / signals.shape[-1] + if dct_type == 1: + tf_idct_dct *= 0.5 / (signals.shape[-1] - 1) + tf_dct_idct *= 0.5 / (signals.shape[-1] - 1) + else: + tf_idct_dct *= 0.5 / signals.shape[-1] + tf_dct_idct *= 0.5 / signals.shape[-1] self.assertAllClose(signals, tf_idct_dct, atol=atol, rtol=rtol) self.assertAllClose(signals, tf_dct_idct, atol=atol, rtol=rtol) - def test_random(self): + @parameterized.parameters([ + [[2]], [[3]], [[10]], [[2, 20]], [[2, 3, 25]]]) + @test_util.run_deprecated_v1 + def test_random(self, shape): """Test randomly generated batches of data.""" with spectral_ops_test_util.fft_kernel_label_map(): with self.session(use_gpu=True): - for shape in ([1], [2], [3], [10], [2, 20], [2, 3, 25]): - signals = np.random.rand(*shape).astype(np.float32) - for norm in (None, "ortho"): - self._compare(signals, norm, 2) - self._compare(signals, norm, 3) + signals = np.random.rand(*shape).astype(np.float32) + # Normalization not implemented for orthonormal. + self._compare(signals, norm=None, dct_type=1) + for norm in (None, "ortho"): + self._compare(signals, norm, 2) + self._compare(signals, norm, 3) def test_error(self): signals = np.random.rand(10) # Unsupported type. with self.assertRaises(ValueError): - spectral_ops.dct(signals, type=1) + dct_ops.dct(signals, type=5) + # DCT-I normalization not implemented. + with self.assertRaises(ValueError): + dct_ops.dct(signals, type=1, norm="ortho") + # DCT-I requires at least two inputs. + with self.assertRaises(ValueError): + dct_ops.dct(np.random.rand(1), type=1) # Unknown normalization. with self.assertRaises(ValueError): - spectral_ops.dct(signals, norm="bad") + dct_ops.dct(signals, norm="bad") with self.assertRaises(NotImplementedError): - spectral_ops.dct(signals, n=10) + dct_ops.dct(signals, n=10) with self.assertRaises(NotImplementedError): - spectral_ops.dct(signals, axis=0) + dct_ops.dct(signals, axis=0) if __name__ == "__main__": diff --git a/tensorflow/python/kernel_tests/fft_ops_test.py b/tensorflow/python/kernel_tests/signal/fft_ops_test.py similarity index 96% rename from tensorflow/python/kernel_tests/fft_ops_test.py rename to tensorflow/python/kernel_tests/signal/fft_ops_test.py index 8592550f99..5b1053428c 100644 --- a/tensorflow/python/kernel_tests/fft_ops_test.py +++ b/tensorflow/python/kernel_tests/signal/fft_ops_test.py @@ -25,12 +25,13 @@ from tensorflow.core.protobuf import config_pb2 from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gen_spectral_ops from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import math_ops -from tensorflow.python.ops import spectral_ops from tensorflow.python.ops import spectral_ops_test_util +from tensorflow.python.ops.signal import fft_ops from tensorflow.python.platform import test VALID_FFT_RANKS = (1, 2, 3) @@ -139,24 +140,25 @@ class FFTOpsTest(BaseFFTOpsTest): def _tfFFTForRank(self, rank): if rank == 1: - return spectral_ops.fft + return fft_ops.fft elif rank == 2: - return spectral_ops.fft2d + return fft_ops.fft2d elif rank == 3: - return spectral_ops.fft3d + return fft_ops.fft3d else: raise ValueError("invalid rank") def _tfIFFTForRank(self, rank): if rank == 1: - return spectral_ops.ifft + return fft_ops.ifft elif rank == 2: - return spectral_ops.ifft2d + return fft_ops.ifft2d elif rank == 3: - return spectral_ops.ifft3d + return fft_ops.ifft3d else: raise ValueError("invalid rank") + @test_util.run_deprecated_v1 def testEmpty(self): with spectral_ops_test_util.fft_kernel_label_map(): for np_type in (np.complex64, np.complex128): @@ -166,6 +168,7 @@ class FFTOpsTest(BaseFFTOpsTest): self.assertEqual(x.shape, self._tfFFT(x, rank).shape) self.assertEqual(x.shape, self._tfIFFT(x, rank).shape) + @test_util.run_deprecated_v1 def testBasic(self): with spectral_ops_test_util.fft_kernel_label_map(): for np_type, tol in ((np.complex64, 1e-4), (np.complex128, 1e-8)): @@ -194,6 +197,7 @@ class FFTOpsTest(BaseFFTOpsTest): # np.mod(np.arange(np.power(128, dims)), 64).reshape( # (128,) * dims).astype(np.complex64), rank) + @test_util.run_deprecated_v1 def testBasicPlaceholder(self): with spectral_ops_test_util.fft_kernel_label_map(): for np_type, tol in ((np.complex64, 1e-4), (np.complex128, 1e-8)): @@ -204,6 +208,7 @@ class FFTOpsTest(BaseFFTOpsTest): (4,) * dims).astype(np_type), rank, use_placeholder=True, rtol=tol, atol=tol) + @test_util.run_deprecated_v1 def testRandom(self): with spectral_ops_test_util.fft_kernel_label_map(): for np_type, tol in ((np.complex64, 1e-4), (np.complex128, 5e-6)): @@ -218,6 +223,7 @@ class FFTOpsTest(BaseFFTOpsTest): self._compare(gen((4,) * dims).astype(np_type), rank, rtol=tol, atol=tol) + @test_util.run_deprecated_v1 def testRandom1D(self): with spectral_ops_test_util.fft_kernel_label_map(): for np_type in (np.complex64, np.complex128): @@ -240,6 +246,7 @@ class FFTOpsTest(BaseFFTOpsTest): for dim in (127, 255, 511, 1023): self._compare(gen((dim,)).astype(np_type), 1, rtol=tol, atol=tol) + @test_util.run_deprecated_v1 def testError(self): for rank in VALID_FFT_RANKS: for dims in xrange(0, rank): @@ -251,6 +258,7 @@ class FFTOpsTest(BaseFFTOpsTest): ValueError, "Shape must be .*rank {}.*".format(rank)): self._tfIFFT(x, rank) + @test_util.run_deprecated_v1 def testGrad_Simple(self): with spectral_ops_test_util.fft_kernel_label_map(): for np_type, tol in ((np.float32, 1e-4), (np.float64, 1e-10)): @@ -263,6 +271,7 @@ class FFTOpsTest(BaseFFTOpsTest): self._checkGradComplex(self._tfIFFTForRank(rank), re, im, rtol=tol, atol=tol) + @test_util.run_deprecated_v1 def testGrad_Random(self): with spectral_ops_test_util.fft_kernel_label_map(): for np_type, tol in ((np.float32, 1e-2), (np.float64, 1e-10)): @@ -312,24 +321,25 @@ class RFFTOpsTest(BaseFFTOpsTest): def _tfFFTForRank(self, rank): if rank == 1: - return spectral_ops.rfft + return fft_ops.rfft elif rank == 2: - return spectral_ops.rfft2d + return fft_ops.rfft2d elif rank == 3: - return spectral_ops.rfft3d + return fft_ops.rfft3d else: raise ValueError("invalid rank") def _tfIFFTForRank(self, rank): if rank == 1: - return spectral_ops.irfft + return fft_ops.irfft elif rank == 2: - return spectral_ops.irfft2d + return fft_ops.irfft2d elif rank == 3: - return spectral_ops.irfft3d + return fft_ops.irfft3d else: raise ValueError("invalid rank") + @test_util.run_deprecated_v1 def testEmpty(self): with spectral_ops_test_util.fft_kernel_label_map(): for rank in VALID_FFT_RANKS: @@ -339,6 +349,7 @@ class RFFTOpsTest(BaseFFTOpsTest): x = np.zeros((0,) * dims).astype(np.complex64) self.assertEqual(x.shape, self._tfIFFT(x, rank).shape) + @test_util.run_deprecated_v1 def testBasic(self): with spectral_ops_test_util.fft_kernel_label_map(): for rank in VALID_FFT_RANKS: @@ -366,6 +377,7 @@ class RFFTOpsTest(BaseFFTOpsTest): 10).reshape((size,) * (dims - 1) + (inner_dim,)) self._compareBackward(c2r.astype(np.complex64), rank, (size,) * rank) + @test_util.run_deprecated_v1 def testBasicPlaceholder(self): with spectral_ops_test_util.fft_kernel_label_map(): for rank in VALID_FFT_RANKS: @@ -427,6 +439,7 @@ class RFFTOpsTest(BaseFFTOpsTest): fft_length, use_placeholder=True) + @test_util.run_deprecated_v1 def testRandom(self): with spectral_ops_test_util.fft_kernel_label_map(): def gen_real(shape): @@ -451,6 +464,7 @@ class RFFTOpsTest(BaseFFTOpsTest): self._compareBackward( gen_complex(complex_dims), rank, (size,) * rank) + @test_util.run_deprecated_v1 def testError(self): with spectral_ops_test_util.fft_kernel_label_map(): for rank in VALID_FFT_RANKS: @@ -507,6 +521,7 @@ class RFFTOpsTest(BaseFFTOpsTest): with self.cached_session(): irfft_fn(x, fft_length).eval() + @test_util.run_deprecated_v1 def testGrad_Simple(self): with spectral_ops_test_util.fft_kernel_label_map(): for rank in VALID_FFT_RANKS: @@ -521,6 +536,7 @@ class RFFTOpsTest(BaseFFTOpsTest): self._checkGradComplex( self._tfIFFTForRank(rank), re, im, result_is_complex=False) + @test_util.run_deprecated_v1 def testGrad_Random(self): with spectral_ops_test_util.fft_kernel_label_map(): for rank in VALID_FFT_RANKS: diff --git a/tensorflow/python/kernel_tests/signal/mel_ops_test.py b/tensorflow/python/kernel_tests/signal/mel_ops_test.py index 1ed4429b42..3134503dae 100644 --- a/tensorflow/python/kernel_tests/signal/mel_ops_test.py +++ b/tensorflow/python/kernel_tests/signal/mel_ops_test.py @@ -22,6 +22,7 @@ import numpy as np from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util as tf_test_util from tensorflow.python.kernel_tests.signal import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops.signal import mel_ops @@ -141,14 +142,16 @@ class LinearToMelTest(test.TestCase): for config in configs: mel_matrix_np = spectrogram_to_mel_matrix(*config) mel_matrix = mel_ops.linear_to_mel_weight_matrix(*config) - self.assertAllClose(mel_matrix_np, mel_matrix.eval(), atol=3e-6) + self.assertAllClose(mel_matrix_np, self.evaluate(mel_matrix), atol=3e-6) + @tf_test_util.run_deprecated_v1 def test_dtypes(self): # LinSpace is not supported for tf.float16. for dtype in (dtypes.bfloat16, dtypes.float32, dtypes.float64): self.assertEqual(dtype, mel_ops.linear_to_mel_weight_matrix(dtype=dtype).dtype) + @tf_test_util.run_deprecated_v1 def test_error(self): with self.assertRaises(ValueError): mel_ops.linear_to_mel_weight_matrix(num_mel_bins=0) @@ -177,6 +180,7 @@ class LinearToMelTest(test.TestCase): rewritten_graph = test_util.grappler_optimize(g, [mel_matrix]) self.assertEqual(1, len(rewritten_graph.node)) + @tf_test_util.run_deprecated_v1 def test_num_spectrogram_bins_dynamic(self): with self.session(use_gpu=True): num_spectrogram_bins = array_ops.placeholder(shape=(), diff --git a/tensorflow/python/kernel_tests/signal/mfcc_ops_test.py b/tensorflow/python/kernel_tests/signal/mfcc_ops_test.py index 79d23d77d1..935922657c 100644 --- a/tensorflow/python/kernel_tests/signal/mfcc_ops_test.py +++ b/tensorflow/python/kernel_tests/signal/mfcc_ops_test.py @@ -20,6 +20,7 @@ from __future__ import print_function from tensorflow.python.framework import dtypes from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import random_ops from tensorflow.python.ops import spectral_ops_test_util @@ -32,6 +33,7 @@ from tensorflow.python.platform import test # HTK conventions. class MFCCTest(test.TestCase): + @test_util.run_deprecated_v1 def test_error(self): # num_mel_bins must be positive. with self.assertRaises(ValueError): @@ -43,6 +45,7 @@ class MFCCTest(test.TestCase): signal = array_ops.zeros((2, 3, 5), dtype=dtypes.float64) mfcc_ops.mfccs_from_log_mel_spectrograms(signal) + @test_util.run_deprecated_v1 def test_basic(self): """A basic test that the op runs on random input.""" with spectral_ops_test_util.fft_kernel_label_map(): @@ -50,6 +53,7 @@ class MFCCTest(test.TestCase): signal = random_ops.random_normal((2, 3, 5)) mfcc_ops.mfccs_from_log_mel_spectrograms(signal).eval() + @test_util.run_deprecated_v1 def test_unknown_shape(self): """A test that the op runs when shape and rank are unknown.""" with spectral_ops_test_util.fft_kernel_label_map(): diff --git a/tensorflow/python/kernel_tests/signal/reconstruction_ops_test.py b/tensorflow/python/kernel_tests/signal/reconstruction_ops_test.py index c4e5b6f674..4cb6cedee9 100644 --- a/tensorflow/python/kernel_tests/signal/reconstruction_ops_test.py +++ b/tensorflow/python/kernel_tests/signal/reconstruction_ops_test.py @@ -20,8 +20,10 @@ from __future__ import print_function import numpy as np +from tensorflow.python.eager import context from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradients_impl from tensorflow.python.ops import math_ops @@ -52,16 +54,75 @@ class ReconstructionOpsTest(test.TestCase): "100000000000000"] def test_all_ones(self): - signal = constant_op.constant(np.ones((3, 5)), dtype=dtypes.int64) + signal = array_ops.ones([3, 5]) reconstruction = reconstruction_ops.overlap_and_add(signal, 2) - with self.session(use_gpu=True) as sess: - output = sess.run(reconstruction) + self.assertEqual(reconstruction.shape.as_list(), [9]) + + with self.session(use_gpu=True): + output = self.evaluate(reconstruction) expected_output = np.array([1, 1, 2, 2, 3, 2, 2, 1, 1]) self.assertAllClose(output, expected_output) + def test_unknown_shapes(self): + # This test uses placeholders and does not work in eager mode. + if context.executing_eagerly(): + return + + signal = array_ops.placeholder(dtype=dtypes.int32, shape=[None, None, None]) + frame_step = array_ops.placeholder(dtype=dtypes.int32, shape=[]) + reconstruction = reconstruction_ops.overlap_and_add(signal, frame_step) + + self.assertEqual(reconstruction.shape.as_list(), [None, None]) + + with self.session(use_gpu=True) as sess: + output = sess.run(reconstruction, + feed_dict={signal: np.ones([4, 3, 5]), frame_step: 2}) + + expected_output = np.array([[1, 1, 2, 2, 3, 2, 2, 1, 1]] * 4) + + self.assertAllClose(output, expected_output) + + def test_unknown_rank(self): + # This test uses placeholders and does not work in eager mode. + if context.executing_eagerly(): + return + + signal = array_ops.placeholder(dtype=dtypes.int32, shape=None) + frame_step = array_ops.placeholder(dtype=dtypes.int32, shape=[]) + reconstruction = reconstruction_ops.overlap_and_add(signal, frame_step) + + self.assertEqual(reconstruction.shape, None) + + with self.session(use_gpu=True) as sess: + output = sess.run(reconstruction, + feed_dict={signal: np.ones([4, 3, 5]), frame_step: 2}) + + expected_output = np.array([[1, 1, 2, 2, 3, 2, 2, 1, 1]] * 4) + + self.assertAllClose(output, expected_output) + + def test_fast_path(self): + # This test uses tensor names and does not work in eager mode. + if context.executing_eagerly(): + return + + signal = array_ops.ones([3, 5]) + frame_step = 5 + reconstruction = reconstruction_ops.overlap_and_add(signal, frame_step) + + self.assertEqual(reconstruction.name, "overlap_and_add/fast_path:0") + + with self.session(use_gpu=True) as sess: + output = self.evaluate(reconstruction) + + expected_output = np.ones([15]) + + self.assertAllClose(output, expected_output) + + @test_util.run_deprecated_v1 def test_simple(self): def make_input(frame_length, num_frames=3): """Generate a tensor of num_frames frames of frame_length.""" @@ -98,8 +159,8 @@ class ReconstructionOpsTest(test.TestCase): dtype=dtypes.int64) reconstruction = reconstruction_ops.overlap_and_add(signal, self.frame_hop) - with self.session(use_gpu=True) as sess: - output = sess.run(reconstruction) + with self.session(use_gpu=True): + output = self.evaluate(reconstruction) string_output = [np.base_repr(x, self.bases[0]) for x in output] self.assertEqual(string_output, self.expected_string) @@ -108,8 +169,8 @@ class ReconstructionOpsTest(test.TestCase): signal = constant_op.constant(self.powers, dtype=dtypes.int64) reconstruction = reconstruction_ops.overlap_and_add(signal, self.frame_hop) - with self.session(use_gpu=True) as sess: - output = sess.run(reconstruction) + with self.session(use_gpu=True): + output = self.evaluate(reconstruction) accumulator = True for i in range(self.batch_size): @@ -124,8 +185,8 @@ class ReconstructionOpsTest(test.TestCase): signal = constant_op.constant(input_matrix, dtype=dtypes.float32) reconstruction = reconstruction_ops.overlap_and_add(signal, self.frame_hop) - with self.session(use_gpu=True) as sess: - output = sess.run(reconstruction) + with self.session(use_gpu=True): + output = self.evaluate(reconstruction) string_output = [np.base_repr(int(x), self.bases[0]) for x in np.squeeze(output)] @@ -133,6 +194,7 @@ class ReconstructionOpsTest(test.TestCase): self.assertEqual(output.shape, (1, 9)) self.assertEqual(string_output, self.expected_string) + @test_util.run_deprecated_v1 def test_gradient(self): configurations = [ ((1, 128), 1), @@ -154,6 +216,7 @@ class ReconstructionOpsTest(test.TestCase): gradient = sess.run(gradients_impl.gradients([loss], [signal])[0]) self.assertTrue((gradient == 1.0).all()) + @test_util.run_deprecated_v1 def test_gradient_batch(self): with self.session(use_gpu=True) as sess: signal = array_ops.zeros((2, 10, 10)) @@ -176,6 +239,7 @@ class ReconstructionOpsTest(test.TestCase): np.reshape(np.arange(100).astype(np.float32), (10, 10))]) self.assertAllEqual(expected_gradient, gradient) + @test_util.run_deprecated_v1 def test_gradient_numerical(self): with self.session(use_gpu=True): shape = (2, 10, 10) diff --git a/tensorflow/python/kernel_tests/signal/shape_ops_test.py b/tensorflow/python/kernel_tests/signal/shape_ops_test.py index 398fba8b6d..32ac76e80d 100644 --- a/tensorflow/python/kernel_tests/signal/shape_ops_test.py +++ b/tensorflow/python/kernel_tests/signal/shape_ops_test.py @@ -23,6 +23,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util as tf_test_util from tensorflow.python.kernel_tests.signal import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops @@ -32,6 +33,7 @@ from tensorflow.python.platform import test class FrameTest(test.TestCase): + @tf_test_util.run_deprecated_v1 def test_mapping_of_indices_without_padding(self): with self.session(use_gpu=True): tensor = constant_op.constant(np.arange(9152), dtypes.int32) @@ -47,6 +49,7 @@ class FrameTest(test.TestCase): self.assertAllEqual(expected, result) + @tf_test_util.run_deprecated_v1 def test_mapping_of_indices_with_padding(self): with self.session(use_gpu=True): tensor = constant_op.constant(np.arange(10000), dtypes.int32) @@ -64,6 +67,7 @@ class FrameTest(test.TestCase): self.assertAllEqual(expected, result) + @tf_test_util.run_deprecated_v1 def test_invalid_inputs(self): # Rank 0 input signal. with self.assertRaises(ValueError): @@ -84,6 +88,7 @@ class FrameTest(test.TestCase): with self.assertRaises(ValueError): shape_ops.frame([1], 1, 1, pad_end=True, pad_value=[1]) + @tf_test_util.run_deprecated_v1 def test_length_zero(self): signal = constant_op.constant([], dtype=dtypes.float32) frame_length = 2 @@ -98,6 +103,7 @@ class FrameTest(test.TestCase): pad_end=False).eval() self.assertEqual((0, 2), result.shape) + @tf_test_util.run_deprecated_v1 def test_shape_inference(self): signal = array_ops.placeholder(dtypes.int32, shape=[1, 1]) frame_length = 2 @@ -150,9 +156,10 @@ class FrameTest(test.TestCase): op = shape_ops.frame(signal, frame_length, frame_step, pad_end=pad_end, pad_value=99) with self.cached_session(use_gpu=True): - result = op.eval() + result = self.evaluate(op) self.assertEqual(op.shape.as_list(), list(result.shape)) + @tf_test_util.run_deprecated_v1 def test_basic_mono(self): signal = np.arange(6) frame_length = 3 @@ -178,6 +185,7 @@ class FrameTest(test.TestCase): pad_end=False).eval() self.assertAllEqual(expected, result) + @tf_test_util.run_deprecated_v1 def test_basic_stereo(self): signal = np.vstack([np.arange(6), np.arange(6) + 10]) @@ -207,6 +215,7 @@ class FrameTest(test.TestCase): pad_end=False).eval() self.assertAllEqual(expected, result) + @tf_test_util.run_deprecated_v1 def test_complex_shape(self): signal = np.vstack([np.arange(6), np.arange(6) + 10, @@ -248,7 +257,7 @@ class FrameTest(test.TestCase): result = shape_ops.frame(signal, frame_length=2, frame_step=2, pad_end=True, axis=1) expected = np.reshape(np.arange(16), (2, 2, 2, 2)) - self.assertAllEqual(expected, result.eval()) + self.assertAllEqual(expected, self.evaluate(result)) result = shape_ops.frame(signal, frame_length=2, frame_step=1, pad_end=True, axis=1) @@ -260,7 +269,7 @@ class FrameTest(test.TestCase): [[10, 11], [12, 13]], [[12, 13], [14, 15]], [[14, 15], [0, 0]]]] - self.assertAllEqual(expected, result.eval()) + self.assertAllEqual(expected, self.evaluate(result)) result = shape_ops.frame(signal, frame_length=3, frame_step=1, pad_end=True, axis=1) @@ -272,8 +281,9 @@ class FrameTest(test.TestCase): [[10, 11], [12, 13], [14, 15]], [[12, 13], [14, 15], [0, 0]], [[14, 15], [0, 0], [0, 0]]]] - self.assertAllEqual(expected, result.eval()) + self.assertAllEqual(expected, self.evaluate(result)) + @tf_test_util.run_deprecated_v1 def test_window_larger_than_signal(self): signal = constant_op.constant([[1, 2], [11, 12]], dtype=dtypes.float32) frame_length = 4 @@ -307,6 +317,7 @@ class FrameTest(test.TestCase): result = shape_ops.frame(signal, frame_length, frame_step) self.assertEqual(result.dtype, signal.dtype) + @tf_test_util.run_deprecated_v1 def test_dynamic_tensor(self): # Show that frame works even when the dimensions of its input are # not known at graph creation time. @@ -325,6 +336,7 @@ class FrameTest(test.TestCase): [[10, 11], [12, 13]], [[20, 21], [22, 23]]], result) + @tf_test_util.run_deprecated_v1 def test_gradient_numerical(self): with self.session(use_gpu=True): signal_shape = (2, 128) diff --git a/tensorflow/python/kernel_tests/signal/spectral_ops_test.py b/tensorflow/python/kernel_tests/signal/spectral_ops_test.py index 26cb127063..7b9748c7f2 100644 --- a/tensorflow/python/kernel_tests/signal/spectral_ops_test.py +++ b/tensorflow/python/kernel_tests/signal/spectral_ops_test.py @@ -125,22 +125,22 @@ class SpectralOpsTest(test.TestCase): stft = spectral_ops.stft(signal, frame_length=7, frame_step=8, pad_end=True) self.assertAllEqual([64, 5], stft.shape.as_list()) - self.assertAllEqual([64, 5], stft.eval().shape) + self.assertAllEqual([64, 5], self.evaluate(stft).shape) stft = spectral_ops.stft(signal, frame_length=8, frame_step=8, pad_end=True) self.assertAllEqual([64, 5], stft.shape.as_list()) - self.assertAllEqual([64, 5], stft.eval().shape) + self.assertAllEqual([64, 5], self.evaluate(stft).shape) stft = spectral_ops.stft(signal, frame_length=8, frame_step=8, fft_length=16, pad_end=True) self.assertAllEqual([64, 9], stft.shape.as_list()) - self.assertAllEqual([64, 9], stft.eval().shape) + self.assertAllEqual([64, 9], self.evaluate(stft).shape) stft = spectral_ops.stft(signal, frame_length=16, frame_step=8, fft_length=8, pad_end=True) self.assertAllEqual([64, 5], stft.shape.as_list()) - self.assertAllEqual([64, 5], stft.eval().shape) + self.assertAllEqual([64, 5], self.evaluate(stft).shape) stft = np.zeros((32, 9)).astype(np.complex64) @@ -148,7 +148,7 @@ class SpectralOpsTest(test.TestCase): fft_length=16, frame_step=8) expected_length = (stft.shape[0] - 1) * 8 + 8 self.assertAllEqual([256], inverse_stft.shape.as_list()) - self.assertAllEqual([expected_length], inverse_stft.eval().shape) + self.assertAllEqual([expected_length], self.evaluate(inverse_stft).shape) def test_stft_and_inverse_stft(self): """Test that spectral_ops.stft/inverse_stft match a NumPy implementation.""" @@ -235,7 +235,8 @@ class SpectralOpsTest(test.TestCase): inverse_window = inverse_window_fn(frame_length, dtype=dtypes.float32) with self.cached_session(use_gpu=True) as sess: - hann_window, inverse_window = sess.run([hann_window, inverse_window]) + hann_window, inverse_window = self.evaluate( + [hann_window, inverse_window]) # Expect unit gain at each phase of the window. product_window = hann_window * inverse_window @@ -263,7 +264,8 @@ class SpectralOpsTest(test.TestCase): inverse_window = inverse_window_fn(frame_length, dtype=dtypes.float32) with self.cached_session(use_gpu=True) as sess: - hann_window, inverse_window = sess.run([hann_window, inverse_window]) + hann_window, inverse_window = self.evaluate( + [hann_window, inverse_window]) self.assertAllClose(hann_window, inverse_window * 1.5) @@ -293,7 +295,7 @@ class SpectralOpsTest(test.TestCase): # the sum of the magnitude STFT. sinusoid = math_ops.sin( 2 * np.pi * math_ops.linspace(0.0, 1.0, signal_length)) - sinusoid_gradient = sess.run(self._compute_stft_gradient(sinusoid)) + sinusoid_gradient = self.evaluate(self._compute_stft_gradient(sinusoid)) self.assertFalse((sinusoid_gradient == 0.0).all()) def test_gradients_numerical(self): diff --git a/tensorflow/python/kernel_tests/signal/test_util.py b/tensorflow/python/kernel_tests/signal/test_util.py index f2c4d0dc8f..0a8a621c3e 100644 --- a/tensorflow/python/kernel_tests/signal/test_util.py +++ b/tensorflow/python/kernel_tests/signal/test_util.py @@ -18,12 +18,12 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -from tensorflow.core.protobuf import rewriter_config_pb2 +from tensorflow.core.protobuf import config_pb2 from tensorflow.python.grappler import tf_optimizer from tensorflow.python.training import saver -def grappler_optimize(graph, fetches=None, rewriter_config=None): +def grappler_optimize(graph, fetches=None, config_proto=None): """Tries to optimize the provided graph using grappler. Args: @@ -31,17 +31,17 @@ def grappler_optimize(graph, fetches=None, rewriter_config=None): fetches: An optional list of `Tensor`s to fetch (i.e. not optimize away). Grappler uses the 'train_op' collection to look for fetches, so if not provided this collection should be non-empty. - rewriter_config: An optional `tf.RewriterConfig` to use when rewriting the + config_proto: An optional `tf.ConfigProto` to use when rewriting the graph. Returns: A `tf.GraphDef` containing the rewritten graph. """ - if rewriter_config is None: - rewriter_config = rewriter_config_pb2.RewriterConfig() - rewriter_config.min_graph_nodes = -1 + if config_proto is None: + config_proto = config_pb2.ConfigProto() + config_proto.graph_options.rewrite_options.min_graph_nodes = -1 if fetches is not None: for fetch in fetches: graph.add_to_collection('train_op', fetch) metagraph = saver.export_meta_graph(graph_def=graph.as_graph_def()) - return tf_optimizer.OptimizeGraph(rewriter_config, metagraph) + return tf_optimizer.OptimizeGraph(config_proto, metagraph) diff --git a/tensorflow/python/kernel_tests/signal/window_ops_test.py b/tensorflow/python/kernel_tests/signal/window_ops_test.py index 2f19134f5a..a72cdb288b 100644 --- a/tensorflow/python/kernel_tests/signal/window_ops_test.py +++ b/tensorflow/python/kernel_tests/signal/window_ops_test.py @@ -24,6 +24,7 @@ import numpy as np from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util as tf_test_util from tensorflow.python.kernel_tests.signal import test_util from tensorflow.python.ops.signal import window_ops from tensorflow.python.platform import test @@ -75,6 +76,7 @@ class WindowOpsTest(test.TestCase): dtype=tf_dtype).eval() self.assertAllClose(expected, actual, tol, tol) + @tf_test_util.run_deprecated_v1 def test_hann_window(self): """Check that hann_window matches scipy.signal.hann behavior.""" # The Hann window is a raised cosine window with parameters alpha=0.5 and @@ -84,6 +86,7 @@ class WindowOpsTest(test.TestCase): functools.partial(_scipy_raised_cosine, a=0.5, b=0.5), window_ops.hann_window) + @tf_test_util.run_deprecated_v1 def test_hamming_window(self): """Check that hamming_window matches scipy.signal.hamming's behavior.""" # The Hamming window is a raised cosine window with parameters alpha=0.54 diff --git a/tensorflow/python/kernel_tests/slice_op_test.py b/tensorflow/python/kernel_tests/slice_op_test.py index 41f040ab73..8f7245214a 100644 --- a/tensorflow/python/kernel_tests/slice_op_test.py +++ b/tensorflow/python/kernel_tests/slice_op_test.py @@ -24,6 +24,7 @@ from six.moves import xrange # pylint: disable=redefined-builtin from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors_impl +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradients_impl from tensorflow.python.ops import math_ops @@ -38,7 +39,7 @@ class SliceTest(test.TestCase): with self.cached_session(use_gpu=True): a = constant_op.constant(inp, shape=[4, 4], dtype=dtypes.float32) slice_t = a[2, k:k] - slice_val = slice_t.eval() + slice_val = self.evaluate(slice_t) self.assertAllEqual(slice_val, inp[2, k:k]) def testInt32(self): @@ -47,7 +48,7 @@ class SliceTest(test.TestCase): with self.cached_session(use_gpu=True): a = constant_op.constant(inp, shape=[4, 4], dtype=dtypes.int32) slice_t = a[2, k:k] - slice_val = slice_t.eval() + slice_val = self.evaluate(slice_t) self.assertAllEqual(slice_val, inp[2, k:k]) def testSlicingWithInt64Index(self): @@ -57,33 +58,33 @@ class SliceTest(test.TestCase): # Slice using int64 Tensor. i = constant_op.constant(1, dtype=dtypes.int64) slice_t = a[i] - slice_val = slice_t.eval() + slice_val = self.evaluate(slice_t) self.assertAllEqual(1, slice_val) slice_t = a[i:i+1] - slice_val = slice_t.eval() + slice_val = self.evaluate(slice_t) self.assertAllEqual([1], slice_val) # Slice using int64 integer. i = np.asarray(1).astype(np.int64) slice_t = a[i] - slice_val = slice_t.eval() + slice_val = self.evaluate(slice_t) self.assertAllEqual(1, slice_val) slice_t = a[i:i+1] - slice_val = slice_t.eval() + slice_val = self.evaluate(slice_t) self.assertAllEqual([1], slice_val) a_int32 = constant_op.constant([0, 1, 2], dtype=dtypes.int32) slice_t = array_ops.slice(a_int32, np.asarray([1]).astype(np.int64), np.asarray([2]).astype(np.int64)) - slice_val = slice_t.eval() + slice_val = self.evaluate(slice_t) self.assertAllEqual([1, 2], slice_val) a_float32 = constant_op.constant([0, 1, 2], dtype=dtypes.float32) slice_t = array_ops.slice(a_float32, np.asarray([1]).astype(np.int64), np.asarray([2]).astype(np.int64)) - slice_val = slice_t.eval() + slice_val = self.evaluate(slice_t) self.assertAllEqual([1, 2], slice_val) def testSlicingInt64Tensor(self): @@ -93,23 +94,23 @@ class SliceTest(test.TestCase): # Slice using int32 Tensor. i = constant_op.constant(1, dtype=dtypes.int32) slice_t = a[i] - slice_val = slice_t.eval() + slice_val = self.evaluate(slice_t) self.assertAllEqual(1, slice_val) slice_t = a[i:i + 1] - slice_val = slice_t.eval() + slice_val = self.evaluate(slice_t) self.assertAllEqual([1], slice_val) # Slice using int32 integer. i = np.asarray(1).astype(np.int32) slice_t = a[i] - slice_val = slice_t.eval() + slice_val = self.evaluate(slice_t) self.assertAllEqual(1, slice_val) slice_t = a[i:i + 1] - slice_val = slice_t.eval() + slice_val = self.evaluate(slice_t) self.assertAllEqual([1], slice_val) slice_t = array_ops.slice(a, [1], [2]) - slice_val = slice_t.eval() + slice_val = self.evaluate(slice_t) self.assertAllEqual([1, 2], slice_val) def testSelectAll(self): @@ -121,8 +122,8 @@ class SliceTest(test.TestCase): slice_explicit_t = array_ops.slice(a, [0, 0, 0, 0], [-1, -1, -1, -1]) slice_implicit_t = a[:, :, :, :] - self.assertAllEqual(inp, slice_explicit_t.eval()) - self.assertAllEqual(inp, slice_implicit_t.eval()) + self.assertAllEqual(inp, self.evaluate(slice_explicit_t)) + self.assertAllEqual(inp, self.evaluate(slice_implicit_t)) self.assertEqual(inp.shape, slice_explicit_t.get_shape()) self.assertEqual(inp.shape, slice_implicit_t.get_shape()) @@ -134,7 +135,7 @@ class SliceTest(test.TestCase): hi = np.random.randint(0, 9) scalar_t = a[hi] - scalar_val = scalar_t.eval() + scalar_val = self.evaluate(scalar_t) self.assertAllEqual(scalar_val, inp[hi]) if hi > 0: @@ -142,9 +143,10 @@ class SliceTest(test.TestCase): else: lo = 0 slice_t = a[lo:hi] - slice_val = slice_t.eval() + slice_val = self.evaluate(slice_t) self.assertAllEqual(slice_val, inp[lo:hi]) + @test_util.run_deprecated_v1 def testScalarInput(self): input_val = 0 with self.cached_session() as sess: @@ -159,6 +161,7 @@ class SliceTest(test.TestCase): "out of range"): sess.run([slice_t], feed_dict={input_t: input_val}) + @test_util.run_deprecated_v1 def testInvalidIndex(self): input_val = [1, 2] with self.cached_session() as sess: @@ -179,6 +182,7 @@ class SliceTest(test.TestCase): np_ans = x[begin:begin + size, :] self.assertAllEqual(tf_ans, np_ans) + @test_util.run_deprecated_v1 def testSliceMatrixDim0(self): x = np.random.rand(8, 4).astype("f") self._testSliceMatrixDim0(x, 1, 2) @@ -195,7 +199,7 @@ class SliceTest(test.TestCase): x, y = np.random.randint(0, 3, size=2).tolist() slice_t = a[x, 0:y] - slice_val = slice_t.eval() + slice_val = self.evaluate(slice_t) self.assertAllEqual(slice_val, inp[x, 0:y]) def testSimple(self): @@ -207,12 +211,13 @@ class SliceTest(test.TestCase): dtype=dtypes.float32) slice_t = array_ops.slice(a, [0, 0], [2, 2]) slice2_t = a[:2, :2] - slice_val, slice2_val = sess.run([slice_t, slice2_t]) + slice_val, slice2_val = self.evaluate([slice_t, slice2_t]) self.assertAllEqual(slice_val, inp[:2, :2]) self.assertAllEqual(slice2_val, inp[:2, :2]) self.assertEqual(slice_val.shape, slice_t.get_shape()) self.assertEqual(slice2_val.shape, slice2_t.get_shape()) + @test_util.run_deprecated_v1 def testComplex(self): with self.session(use_gpu=True): inp = np.random.rand(4, 10, 10, 4).astype("f") @@ -247,7 +252,7 @@ class SliceTest(test.TestCase): + sizes[3], indices[4]:indices[4] + sizes[4], indices[5]: indices[5] + sizes[5]] - slice_val, slice2_val = sess.run([slice_t, slice2_t]) + slice_val, slice2_val = self.evaluate([slice_t, slice2_t]) expected_val = inp[indices[0]:indices[0] + sizes[0], indices[1]:indices[ 1] + sizes[1], indices[2]:indices[2] + sizes[2], indices[3]:indices[ @@ -282,7 +287,7 @@ class SliceTest(test.TestCase): grads = np.random.rand(num_grads).astype("f").reshape(slice_size) grad_tensor = constant_op.constant(grads) grad = gradients_impl.gradients(slice_t, [a], grad_tensor)[0] - result = grad.eval() + result = self.evaluate(grad) # Create a zero tensor of the input shape ane place # the grads into the right location to compare against TensorFlow. @@ -313,9 +318,10 @@ class SliceTest(test.TestCase): g1 = gradients_impl.gradients(loss1, x)[0] g2 = gradients_impl.gradients(loss2, x)[0] - g1_val, g2_val = sess.run([g1, g2]) + g1_val, g2_val = self.evaluate([g1, g2]) self.assertAllEqual(g1_val, g2_val) + @test_util.run_deprecated_v1 def testGradientsAll(self): # Slice the middle square out of a 4x4 input self._testGradientSlice([4, 4], [1, 1], [2, 2]) @@ -335,6 +341,7 @@ class SliceTest(test.TestCase): # Use -1 as a slice dimension on a 2D tensor. self._testGradientVariableSize2D() + @test_util.run_deprecated_v1 def testNotIterable(self): # NOTE(mrry): If we register __getitem__ as an overloaded # operator, Python will valiantly attempt to iterate over the @@ -346,6 +353,7 @@ class SliceTest(test.TestCase): for _ in c: pass + @test_util.run_deprecated_v1 def testComputedShape(self): # NOTE(mrry): We cannot currently handle partially-known values, # because `tf.slice()` uses -1 to specify a wildcard size, and @@ -368,7 +376,7 @@ class SliceTest(test.TestCase): c = b[:-1, :] d = c[1, :] res = 2 * d - c[1, :] + a[2, :] - 2 * b[-2, :] - self.assertAllEqual([0, 0, 0], res.eval()) + self.assertAllEqual([0, 0, 0], self.evaluate(res)) if __name__ == "__main__": diff --git a/tensorflow/python/kernel_tests/softmax_op_test.py b/tensorflow/python/kernel_tests/softmax_op_test.py index ef9301d4e3..707b8a429f 100644 --- a/tensorflow/python/kernel_tests/softmax_op_test.py +++ b/tensorflow/python/kernel_tests/softmax_op_test.py @@ -24,6 +24,7 @@ import numpy as np from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors_impl +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import nn_ops from tensorflow.python.platform import test @@ -64,7 +65,7 @@ class SoftmaxTest(test.TestCase): tf_softmax = nn_ops.log_softmax(np_features, axis=dim, name=name) else: tf_softmax = nn_ops.softmax(np_features, axis=dim, name=name) - out = tf_softmax.eval() + out = self.evaluate(tf_softmax) self.assertAllCloseAccordingToType(np_softmax, out) self.assertShapeEqual(np_softmax, tf_softmax) if not log: @@ -113,7 +114,7 @@ class SoftmaxTest(test.TestCase): features = np.array([[1., 1., 1., 1.], [max, 1., 2., 3.]]).astype(type) with self.cached_session(use_gpu=use_gpu): tf_log_softmax = nn_ops.log_softmax(features) - out = tf_log_softmax.eval() + out = self.evaluate(tf_log_softmax) self.assertAllClose( np.array([[-1.386294, -1.386294, -1.386294, -1.386294], [0, -max, -max, -max]]), @@ -206,6 +207,7 @@ class SoftmaxTest(test.TestCase): [[5., 4., 3., 2.], [1., 2., 3., 4.]]]) self.assertEqual([3, 2, 4], op.get_shape()) + @test_util.run_deprecated_v1 def testEmptyInput(self): with self.cached_session(): x = array_ops.placeholder(dtypes.float32, shape=[0, 3]) @@ -229,6 +231,7 @@ class SoftmaxTest(test.TestCase): with self.assertRaises(errors_impl.InvalidArgumentError): nn_ops.softmax(ones, axis=2).eval() + @test_util.run_deprecated_v1 def testLargeDims(self): # Make sure that we properly handle large inputs. See # https://github.com/tensorflow/tensorflow/issues/4425 for details diff --git a/tensorflow/python/kernel_tests/softplus_op_test.py b/tensorflow/python/kernel_tests/softplus_op_test.py index 50a8291ea8..5273dd7ffc 100644 --- a/tensorflow/python/kernel_tests/softplus_op_test.py +++ b/tensorflow/python/kernel_tests/softplus_op_test.py @@ -21,6 +21,7 @@ from __future__ import print_function import numpy as np from tensorflow.python.framework import constant_op +from tensorflow.python.framework import test_util from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import gradients_impl from tensorflow.python.ops import nn_ops @@ -39,7 +40,7 @@ class SoftplusTest(test.TestCase): np_softplus = self._npSoftplus(np_features) with self.cached_session(use_gpu=use_gpu): softplus = nn_ops.softplus(np_features) - tf_softplus = softplus.eval() + tf_softplus = self.evaluate(softplus) self.assertAllCloseAccordingToType(np_softplus, tf_softplus) self.assertTrue(np.all(tf_softplus > 0)) self.assertShapeEqual(np_softplus, softplus) @@ -70,6 +71,7 @@ class SoftplusTest(test.TestCase): ], use_gpu=True) + @test_util.run_deprecated_v1 def testGradient(self): with self.cached_session(): x = constant_op.constant( @@ -86,6 +88,7 @@ class SoftplusTest(test.TestCase): print("softplus (float) gradient err = ", err) self.assertLess(err, 1e-4) + @test_util.run_deprecated_v1 def testGradGrad(self): with self.cached_session(): x = constant_op.constant( @@ -103,6 +106,7 @@ class SoftplusTest(test.TestCase): print("softplus (float) gradient of gradient err = ", err) self.assertLess(err, 5e-5) + @test_util.run_deprecated_v1 def testGradGradGrad(self): with self.cached_session(): x = constant_op.constant( @@ -121,6 +125,7 @@ class SoftplusTest(test.TestCase): print("softplus (float) third-order gradient err = ", err) self.assertLess(err, 5e-5) + @test_util.run_deprecated_v1 def testNoInts(self): with self.cached_session(): with self.assertRaisesRegexp( diff --git a/tensorflow/python/kernel_tests/softsign_op_test.py b/tensorflow/python/kernel_tests/softsign_op_test.py index ee2e2e0303..5554240c82 100644 --- a/tensorflow/python/kernel_tests/softsign_op_test.py +++ b/tensorflow/python/kernel_tests/softsign_op_test.py @@ -21,6 +21,7 @@ from __future__ import print_function import numpy as np from tensorflow.python.framework import constant_op +from tensorflow.python.framework import test_util from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import nn_ops import tensorflow.python.ops.nn_grad # pylint: disable=unused-import @@ -36,7 +37,7 @@ class SoftsignTest(test.TestCase): np_softsign = self._npSoftsign(np_features) with self.cached_session(use_gpu=use_gpu): softsign = nn_ops.softsign(np_features) - tf_softsign = softsign.eval() + tf_softsign = self.evaluate(softsign) self.assertAllClose(np_softsign, tf_softsign) self.assertShapeEqual(np_softsign, softsign) @@ -49,6 +50,7 @@ class SoftsignTest(test.TestCase): np.array([[-9, 7, -5, 3, -1], [1, -3, 5, -7, 9]]).astype(t), use_gpu=True) + @test_util.run_deprecated_v1 def testGradient(self): with self.cached_session(): x = constant_op.constant( @@ -65,6 +67,7 @@ class SoftsignTest(test.TestCase): print("softsign (float) gradient err = ", err) self.assertLess(err, 1e-4) + @test_util.run_deprecated_v1 def testNoInts(self): with self.cached_session(): with self.assertRaisesRegexp( diff --git a/tensorflow/python/kernel_tests/spacetobatch_op_test.py b/tensorflow/python/kernel_tests/spacetobatch_op_test.py index 21134adf2c..8641156604 100644 --- a/tensorflow/python/kernel_tests/spacetobatch_op_test.py +++ b/tensorflow/python/kernel_tests/spacetobatch_op_test.py @@ -23,6 +23,7 @@ import numpy as np from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_util +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gen_array_ops from tensorflow.python.ops import gradient_checker @@ -115,6 +116,7 @@ class SpaceToBatchTest(test.TestCase, PythonOpImpl): self._testPad(inputs, paddings, block_size, outputs) # [1, 2, 2, 1] <-> [4, 1, 1, 1] + @test_util.run_deprecated_v1 def testSmallInput2x2(self): x_np = [[[[1], [2]], [[3], [4]]]] block_size = 2 @@ -122,6 +124,7 @@ class SpaceToBatchTest(test.TestCase, PythonOpImpl): self._testOne(x_np, block_size, x_out) # [1, 2, 2, 1] <-> [1, 3, 3, 1] (padding) <-> [9, 1, 1, 1] + @test_util.run_deprecated_v1 def testSmallInput2x2Pad1x0(self): x_np = [[[[1], [2]], [[3], [4]]]] paddings = np.array([[1, 0], [1, 0]], dtype=np.int32) @@ -132,6 +135,7 @@ class SpaceToBatchTest(test.TestCase, PythonOpImpl): # Test with depth larger than 1. # [1, 2, 2, 3] <-> [4, 1, 1, 3] + @test_util.run_deprecated_v1 def testDepthInput2x2(self): x_np = [[[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]] block_size = 2 @@ -140,6 +144,7 @@ class SpaceToBatchTest(test.TestCase, PythonOpImpl): # Test for larger input dimensions. # [1, 4, 4, 1] <-> [4, 2, 2, 1] + @test_util.run_deprecated_v1 def testLargerInput2x2(self): x_np = [[[[1], [2], [3], [4]], [[5], [6], [7], [8]], [[9], [10], [11], [12]], [[13], [14], [15], [16]]]] @@ -150,6 +155,7 @@ class SpaceToBatchTest(test.TestCase, PythonOpImpl): # Test with batch larger than 1. # [2, 2, 4, 1] <-> [8, 1, 2, 1] + @test_util.run_deprecated_v1 def testBatchInput2x2(self): x_np = [[[[1], [2], [3], [4]], [[5], [6], [7], [8]]], [[[9], [10], [11], [12]], [[13], [14], [15], [16]]]] @@ -162,6 +168,7 @@ class SpaceToBatchTest(test.TestCase, PythonOpImpl): # that elements are correctly laid out spatially and properly interleaved # along the batch dimension. # [2, 4, 4, 1] <-> [8, 2, 2, 1] + @test_util.run_deprecated_v1 def testLargerInputBatch2x2(self): x_np = [[[[1], [2], [3], [4]], [[5], [6], [7], [8]], [[9], [10], [11], [12]], [[13], [14], [15], [16]]], @@ -206,6 +213,7 @@ class SpaceToBatchNDTest(test.TestCase): self._testPad(inputs, block_shape, paddings, space_to_batch_direct(inputs, block_shape, paddings)) + @test_util.run_deprecated_v1 def testZeroBlockDimsZeroRemainingDims(self): self._testPad( inputs=[1, 2], @@ -213,6 +221,7 @@ class SpaceToBatchNDTest(test.TestCase): paddings=[], outputs=[1, 2],) + @test_util.run_deprecated_v1 def testZeroBlockDimsOneRemainingDim(self): self._testPad( inputs=[[1, 2], [3, 4]], @@ -227,6 +236,7 @@ class SpaceToBatchNDTest(test.TestCase): paddings=[[0, 0]], outputs=[[1, 2], [3, 4]]) + @test_util.run_deprecated_v1 def testZeroBlockDimsTwoRemainingDims(self): self._testPad( inputs=[[[1, 2], [3, 4]], [[5, 6], [7, 8]]], @@ -248,6 +258,7 @@ class SpaceToBatchNDTest(test.TestCase): paddings=[[0, 0], [0, 0]], outputs=[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]) + @test_util.run_deprecated_v1 def testOneBlockDimZeroRemainingDims(self): self._testPad( inputs=[[1, 2, 3], [4, 5, 6]], @@ -255,6 +266,7 @@ class SpaceToBatchNDTest(test.TestCase): paddings=[1, 0], outputs=[[0, 2], [0, 5], [1, 3], [4, 6]]) + @test_util.run_deprecated_v1 def testOneBlockDimOneRemainingDim(self): self._testPad( inputs=[[[1, 11], [2, 21], [3, 31]], [[4, 41], [5, 51], [6, 61]]], @@ -263,6 +275,7 @@ class SpaceToBatchNDTest(test.TestCase): outputs=[[[0, 0], [2, 21]], [[0, 0], [5, 51]], [[1, 11], [3, 31]], [[4, 41], [6, 61]]]) + @test_util.run_deprecated_v1 def testDirect(self): # Test with zero-size remaining dimension. self._testDirect( @@ -300,6 +313,7 @@ class SpaceToBatchNDTest(test.TestCase): class SpaceToBatchSpaceToDepth(test.TestCase, PythonOpImpl): # Verifies that: space_to_batch(x) = transpose(space_to_depth(transpose(x))) + @test_util.run_deprecated_v1 def testSpaceToDepthTranspose(self): x = np.arange(5 * 10 * 16 * 7, dtype=np.float32).reshape([5, 10, 16, 7]) block_size = 2 @@ -319,6 +333,7 @@ class SpaceToBatchSpaceToDepthCpp(SpaceToBatchSpaceToDepth, CppOpImpl): class SpaceToBatchErrorHandlingTest(test.TestCase, PythonOpImpl): + @test_util.run_deprecated_v1 def testInputWrongDimMissingBatch(self): # The input is missing the first dimension ("batch") x_np = [[[1], [2]], [[3], [4]]] @@ -327,6 +342,7 @@ class SpaceToBatchErrorHandlingTest(test.TestCase, PythonOpImpl): with self.assertRaises(ValueError): _ = self.space_to_batch(x_np, paddings, block_size) + @test_util.run_deprecated_v1 def testBlockSize0(self): # The block size is 0. x_np = [[[[1], [2]], [[3], [4]]]] @@ -336,6 +352,7 @@ class SpaceToBatchErrorHandlingTest(test.TestCase, PythonOpImpl): out_tf = self.space_to_batch(x_np, paddings, block_size) out_tf.eval() + @test_util.run_deprecated_v1 def testBlockSizeOne(self): # The block size is 1. The block size needs to be > 1. x_np = [[[[1], [2]], [[3], [4]]]] @@ -345,6 +362,7 @@ class SpaceToBatchErrorHandlingTest(test.TestCase, PythonOpImpl): out_tf = self.space_to_batch(x_np, paddings, block_size) out_tf.eval() + @test_util.run_deprecated_v1 def testBlockSizeLarger(self): # The block size is too large for this input. x_np = [[[[1], [2]], [[3], [4]]]] @@ -354,6 +372,7 @@ class SpaceToBatchErrorHandlingTest(test.TestCase, PythonOpImpl): out_tf = self.space_to_batch(x_np, paddings, block_size) out_tf.eval() + @test_util.run_deprecated_v1 def testBlockSizeNotDivisibleWidth(self): # The block size divides width but not height. x_np = [[[[1], [2], [3]], [[3], [4], [7]]]] @@ -362,6 +381,7 @@ class SpaceToBatchErrorHandlingTest(test.TestCase, PythonOpImpl): with self.assertRaises(ValueError): _ = self.space_to_batch(x_np, paddings, block_size) + @test_util.run_deprecated_v1 def testBlockSizeNotDivisibleHeight(self): # The block size divides height but not width. x_np = [[[[1], [2]], [[3], [4]], [[5], [6]]]] @@ -370,6 +390,7 @@ class SpaceToBatchErrorHandlingTest(test.TestCase, PythonOpImpl): with self.assertRaises(ValueError): _ = self.space_to_batch(x_np, paddings, block_size) + @test_util.run_deprecated_v1 def testBlockSizeNotDivisibleBoth(self): # The block size does not divide neither width or height. x_np = [[[[1], [2]], [[3], [4]]]] @@ -378,6 +399,7 @@ class SpaceToBatchErrorHandlingTest(test.TestCase, PythonOpImpl): with self.assertRaises(ValueError): _ = self.space_to_batch(x_np, paddings, block_size) + @test_util.run_deprecated_v1 def testUnknownShape(self): t = self.space_to_batch( array_ops.placeholder(dtypes.float32), @@ -424,25 +446,31 @@ class SpaceToBatchNDErrorHandlingTest(test.TestCase): self._testStaticShape(input_shape, block_shape, paddings, error) self._testDynamicShape(input_shape, block_shape, paddings) + @test_util.run_deprecated_v1 def testBlockSize0(self): # The block size is 0. self._testShape([1, 2, 2], [0, 2], [[0, 0], [0, 0]], ValueError) + @test_util.run_deprecated_v1 def testBlockSizeNegative(self): self._testShape([1, 2, 2], [-1, 2], [[0, 0], [0, 0]], ValueError) + @test_util.run_deprecated_v1 def testNegativePadding(self): # The padding is negative. self._testShape([1, 2, 2], [1, 1], [[0, -1], [0, 0]], ValueError) + @test_util.run_deprecated_v1 def testBlockSizeNotDivisible(self): # The padded size is not divisible by the block size. self._testShape([1, 2, 3, 1], [3, 3], [[0, 0], [0, 0]], ValueError) + @test_util.run_deprecated_v1 def testBlockDimsMismatch(self): # Shape of block_shape does not match shape of paddings. self._testStaticShape([1, 3, 3, 1], [3, 3], [[0, 0]], ValueError) + @test_util.run_deprecated_v1 def testUnknown(self): # Verify that input shape and paddings shape can be unknown. _ = array_ops.space_to_batch_nd( @@ -522,18 +550,21 @@ class SpaceToBatchGradientTest(test.TestCase, PythonOpImpl): # Don't use very large numbers as dimensions here as the result is tensor # with cartesian product of the dimensions. + @test_util.run_deprecated_v1 def testSmall(self): block_size = 2 pad_beg = 0 pad_end = 0 self._compare(1, 2, 3, 5, block_size, pad_beg, pad_end) + @test_util.run_deprecated_v1 def testSmall2(self): block_size = 2 pad_beg = 0 pad_end = 0 self._compare(2, 4, 3, 2, block_size, pad_beg, pad_end) + @test_util.run_deprecated_v1 def testSmallPad1x1(self): block_size = 2 pad_beg = 1 @@ -572,15 +603,19 @@ class SpaceToBatchNDGradientTest(test.TestCase): # Don't use very large numbers as dimensions here as the result is tensor # with cartesian product of the dimensions. + @test_util.run_deprecated_v1 def testSmall(self): self._compare([1, 4, 6, 5], [2, 2], [[0, 0], [0, 0]]) + @test_util.run_deprecated_v1 def testSmall2(self): self._compare([2, 8, 6, 2], [2, 2], [[0, 0], [0, 0]]) + @test_util.run_deprecated_v1 def testSmallPad1(self): self._compare([2, 4, 6, 2], [2, 2], [[1, 1], [1, 1]]) + @test_util.run_deprecated_v1 def testSmallPadThreeBlockDims(self): self._compare([2, 2, 4, 3, 2], [2, 2, 2], [[1, 1], [1, 1], [1, 0]]) @@ -644,6 +679,7 @@ class RequiredSpaceToBatchPaddingsTest(test.TestCase): self.assertAllEqual(paddings_result, paddings_const) self.assertAllEqual(crops_result, crops_const) + @test_util.run_deprecated_v1 def testSimple(self): self._test( input_shape=np.zeros((0,), np.int32), diff --git a/tensorflow/python/kernel_tests/spacetodepth_op_test.py b/tensorflow/python/kernel_tests/spacetodepth_op_test.py index b05f14f738..e96bc09f36 100644 --- a/tensorflow/python/kernel_tests/spacetodepth_op_test.py +++ b/tensorflow/python/kernel_tests/spacetodepth_op_test.py @@ -36,21 +36,22 @@ class SpaceToDepthTest(test.TestCase): def _testOne(self, inputs, block_size, outputs, dtype=dtypes.float32): input_nhwc = math_ops.cast(inputs, dtype) - with self.session(use_gpu=False): + with test_util.force_cpu(): # test NHWC (default) on CPU x_tf = array_ops.space_to_depth(input_nhwc, block_size) - self.assertAllEqual(x_tf.eval(), outputs) - if test.is_gpu_available(): - with self.session(force_gpu=True): + self.assertAllEqual(self.evaluate(x_tf), outputs) + + if test_util.is_gpu_available(): + with test_util.force_gpu(): # test NHWC (default) on GPU x_tf = array_ops.space_to_depth(input_nhwc, block_size) - self.assertAllEqual(x_tf.eval(), outputs) + self.assertAllEqual(self.evaluate(x_tf), outputs) # test NCHW on GPU input_nchw = test_util.NHWCToNCHW(input_nhwc) output_nchw = array_ops.space_to_depth( input_nchw, block_size, data_format="NCHW") output_nhwc = test_util.NCHWToNHWC(output_nchw) - self.assertAllEqual(output_nhwc.eval(), outputs) + self.assertAllEqual(self.evaluate(output_nhwc), outputs) def testBasic(self): x_np = [[[[1], [2]], [[3], [4]]]] @@ -134,17 +135,18 @@ class SpaceToDepthTest(test.TestCase): input_nhwc = array_ops.ones([batch_size, 4, 6, 3]) x_out = array_ops.ones([batch_size, 2, 3, 12]) - with self.session(use_gpu=False): + with test_util.force_cpu(): # test NHWC (default) on CPU x_tf = array_ops.space_to_depth(input_nhwc, block_size) self.assertAllEqual(x_tf.shape, x_out.shape) - x_tf.eval() + self.evaluate(x_tf) + if test.is_gpu_available(): - with self.session(use_gpu=True): + with test_util.use_gpu(): # test NHWC (default) on GPU x_tf = array_ops.space_to_depth(input_nhwc, block_size) self.assertAllEqual(x_tf.shape, x_out.shape) - x_tf.eval() + self.evaluate(x_tf) # Tests for different width and height. def testNonSquare(self): @@ -157,14 +159,16 @@ class SpaceToDepthTest(test.TestCase): # Error handling: + @test_util.run_deprecated_v1 def testInputWrongDimMissingDepth(self): # The input is missing the last dimension ("depth") x_np = [[[1, 2], [3, 4]]] block_size = 2 with self.assertRaises(ValueError): out_tf = array_ops.space_to_depth(x_np, block_size) - out_tf.eval() + self.evaluate(out_tf) + @test_util.run_deprecated_v1 def testInputWrongDimMissingBatch(self): # The input is missing the first dimension ("batch") x_np = [[[1], [2]], [[3], [4]]] @@ -172,30 +176,34 @@ class SpaceToDepthTest(test.TestCase): with self.assertRaises(ValueError): _ = array_ops.space_to_depth(x_np, block_size) + @test_util.run_deprecated_v1 def testBlockSize0(self): # The block size is 0. x_np = [[[[1], [2]], [[3], [4]]]] block_size = 0 with self.assertRaises(ValueError): out_tf = array_ops.space_to_depth(x_np, block_size) - out_tf.eval() + self.evaluate(out_tf) + @test_util.run_deprecated_v1 def testBlockSizeOne(self): # The block size is 1. The block size needs to be > 1. x_np = [[[[1], [2]], [[3], [4]]]] block_size = 1 with self.assertRaises(ValueError): out_tf = array_ops.space_to_depth(x_np, block_size) - out_tf.eval() + self.evaluate(out_tf) + @test_util.run_deprecated_v1 def testBlockSizeLarger(self): # The block size is too large for this input. x_np = [[[[1], [2]], [[3], [4]]]] block_size = 10 with self.assertRaises(ValueError): out_tf = array_ops.space_to_depth(x_np, block_size) - out_tf.eval() + self.evaluate(out_tf) + @test_util.run_deprecated_v1 def testBlockSizeNotDivisibleWidth(self): # The block size divides width but not height. x_np = [[[[1], [2], [3]], [[3], [4], [7]]]] @@ -203,6 +211,7 @@ class SpaceToDepthTest(test.TestCase): with self.assertRaises(ValueError): _ = array_ops.space_to_depth(x_np, block_size) + @test_util.run_deprecated_v1 def testBlockSizeNotDivisibleHeight(self): # The block size divides height but not width. x_np = [[[[1], [2]], [[3], [4]], [[5], [6]]]] @@ -210,6 +219,7 @@ class SpaceToDepthTest(test.TestCase): with self.assertRaises(ValueError): _ = array_ops.space_to_depth(x_np, block_size) + @test_util.run_deprecated_v1 def testBlockSizeNotDivisibleBoth(self): # The block size does not divide neither width or height. x_np = [[[[1], [2]], [[3], [4]]]] @@ -217,6 +227,7 @@ class SpaceToDepthTest(test.TestCase): with self.assertRaises(ValueError): _ = array_ops.space_to_depth(x_np, block_size) + @test_util.run_deprecated_v1 def testUnknownShape(self): t = array_ops.space_to_depth( array_ops.placeholder(dtypes.float32), block_size=4) @@ -271,7 +282,7 @@ class SpaceToDepthTest(test.TestCase): actual = array_ops.space_to_depth(t, block_size, data_format=data_format) with self.cached_session(use_gpu=use_gpu) as sess: - actual_vals, expected_vals = sess.run([actual, expected]) + actual_vals, expected_vals = self.evaluate([actual, expected]) self.assertTrue(np.array_equal(actual_vals, expected_vals)) def testAgainstTranspose(self): @@ -332,11 +343,13 @@ class SpaceToDepthGradientTest(test.TestCase): # Don't use very large numbers as dimensions here as the result is tensor # with cartesian product of the dimensions. + @test_util.run_deprecated_v1 def testSmall(self): block_size = 2 self._compare(1, 2, 3, 5, block_size, "NHWC") self._compare(1, 2, 3, 5, block_size, "NCHW") + @test_util.run_deprecated_v1 def testSmall2(self): block_size = 2 self._compare(2, 4, 3, 2, block_size, "NHWC") diff --git a/tensorflow/python/kernel_tests/sparse_add_op_test.py b/tensorflow/python/kernel_tests/sparse_add_op_test.py index a746830afb..00eff54077 100644 --- a/tensorflow/python/kernel_tests/sparse_add_op_test.py +++ b/tensorflow/python/kernel_tests/sparse_add_op_test.py @@ -28,6 +28,7 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors_impl from tensorflow.python.framework import ops from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import test_util from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import math_ops from tensorflow.python.ops import sparse_ops @@ -85,13 +86,13 @@ class SparseAddTest(test.TestCase): constant_op.constant(shape, dtypes.int64)) def testAddSelf(self): - with self.session(use_gpu=False) as sess: + with test_util.force_cpu(): for sp_a in (self._SparseTensorValue_3x3(), self._SparseTensor_3x3()): for sp_b in (self._SparseTensorValue_3x3(), self._SparseTensor_3x3()): sp_sum = sparse_ops.sparse_add(sp_a, sp_b) self.assertAllEqual((3, 3), sp_sum.get_shape()) - sum_out = sess.run(sp_sum) + sum_out = self.evaluate(sp_sum) self.assertEqual(sp_sum.dense_shape.get_shape(), [2]) self.assertAllEqual(sum_out.indices, [[0, 1], [1, 0], [2, 0], [2, 1]]) @@ -99,12 +100,12 @@ class SparseAddTest(test.TestCase): self.assertAllEqual(sum_out.dense_shape, [3, 3]) def testAddSelfAndNegation(self): - with self.session(use_gpu=False) as sess: + with test_util.force_cpu(): sp_a = self._SparseTensor_3x3() sp_b = self._SparseTensor_3x3(negate=True) sp_sum = sparse_ops.sparse_add(sp_a, sp_b, 0.1) - sum_out = sess.run(sp_sum) + sum_out = self.evaluate(sp_sum) self.assertEqual(sp_sum.dense_shape.get_shape(), [2]) self.assertAllEqual(sum_out.indices, np.empty([0, 2])) @@ -112,7 +113,7 @@ class SparseAddTest(test.TestCase): self.assertAllEqual(sum_out.dense_shape, [3, 3]) def testSmallValuesShouldVanish(self): - with self.session(use_gpu=False) as sess: + with test_util.force_cpu(): sp_a = self._SparseTensor_3x3() sp_b = self._SparseTensor_3x3_v2() @@ -123,7 +124,7 @@ class SparseAddTest(test.TestCase): # two values should vanish: |.1| < .21, and |-.2| < .21 sp_sum = sparse_ops.sparse_add(sp_a, sp_b, thresh=0.21) - sum_out = sess.run(sp_sum) + sum_out = self.evaluate(sp_sum) self.assertEqual(sp_sum.dense_shape.get_shape(), [2]) self.assertAllEqual(sum_out.indices, [[0, 1], [2, 0]]) @@ -132,13 +133,14 @@ class SparseAddTest(test.TestCase): # only .1 vanishes sp_sum = sparse_ops.sparse_add(sp_a, sp_b, thresh=0.11) - sum_out = sess.run(sp_sum) + sum_out = self.evaluate(sp_sum) self.assertEqual(sp_sum.dense_shape.get_shape(), [2]) self.assertAllEqual(sum_out.indices, [[0, 1], [2, 0], [2, 1]]) self.assertAllClose(sum_out.values, [2, 6, -.2]) self.assertAllEqual(sum_out.dense_shape, [3, 3]) + @test_util.run_deprecated_v1 def testGradients(self): np.random.seed(1618) # Make it reproducible. with self.session(use_gpu=False): @@ -147,7 +149,7 @@ class SparseAddTest(test.TestCase): sp_a, nnz_a = self._randomTensor([n, m], np.float32) sp_b, nnz_b = self._randomTensor([n, m], np.float32) sp_sum = sparse_ops.sparse_add(sp_a, sp_b) - nnz_sum = len(sp_sum.values.eval()) + nnz_sum = len(self.evaluate(sp_sum.values)) err = gradient_checker.compute_gradient_error( [sp_a.values, sp_b.values], [(nnz_a,), (nnz_b,)], sp_sum.values, @@ -162,19 +164,20 @@ class SparseAddTest(test.TestCase): rand_vals_np = np.random.randn(n, m).astype(dtype) dense_np = np.random.randn(n, m).astype(dtype) - with self.cached_session(use_gpu=False): + with test_util.force_cpu(): sparse, unused_nnz = _sparsify(rand_vals_np, index_dtype=index_dtype) - s = sparse_ops.sparse_add(sparse, - constant_op.constant(dense_np)).eval() + s = self.evaluate( + sparse_ops.sparse_add(sparse, constant_op.constant(dense_np))) self.assertAllEqual(dense_np + rand_vals_np, s) self.assertTrue(s.dtype == dtype) # check commutativity - s = sparse_ops.sparse_add(constant_op.constant(dense_np), - sparse).eval() + s = self.evaluate( + sparse_ops.sparse_add(constant_op.constant(dense_np), sparse)) self.assertAllEqual(dense_np + rand_vals_np, s) self.assertTrue(s.dtype == dtype) + @test_util.run_deprecated_v1 def testSparseTensorDenseAddGradients(self): np.random.seed(1618) # Make it reproducible. n, m = np.random.randint(30, size=2) @@ -190,8 +193,9 @@ class SparseAddTest(test.TestCase): [(nnz,), (n, m)], s, (n, m)) self.assertLess(err, 1e-3) + @test_util.run_deprecated_v1 def testInvalidSparseTensor(self): - with self.session(use_gpu=False) as sess: + with test_util.force_cpu(): shape = [2, 2] val = [0] dense = constant_op.constant(np.zeros(shape, dtype=np.int32)) @@ -205,7 +209,7 @@ class SparseAddTest(test.TestCase): with self.assertRaisesRegexp(errors_impl.InvalidArgumentError, "invalid index"): - sess.run(s) + self.evaluate(s) ######################## Benchmarking code diff --git a/tensorflow/python/kernel_tests/sparse_concat_op_test.py b/tensorflow/python/kernel_tests/sparse_concat_op_test.py index 402c5eb4ea..04b6b9b8d2 100644 --- a/tensorflow/python/kernel_tests/sparse_concat_op_test.py +++ b/tensorflow/python/kernel_tests/sparse_concat_op_test.py @@ -23,6 +23,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import sparse_ops from tensorflow.python.platform import test @@ -147,7 +148,7 @@ class SparseConcatTest(test.TestCase): self.assertEqual(sp_concat.values.get_shape(), [4]) self.assertEqual(sp_concat.dense_shape.get_shape(), [2]) - concat_out = sess.run(sp_concat) + concat_out = self.evaluate(sp_concat) self.assertAllEqual(concat_out.indices, [[0, 2], [1, 0], [2, 0], [2, 2]]) @@ -169,7 +170,7 @@ class SparseConcatTest(test.TestCase): self.assertEqual(sp_concat.values.get_shape(), [8]) self.assertEqual(sp_concat.dense_shape.get_shape(), [2]) - concat_out = sess.run(sp_concat) + concat_out = self.evaluate(sp_concat) self.assertAllEqual(concat_out.indices, [[0, 2], [1, 0], [1, 4], [2, 0], [2, 2], [2, 3], @@ -195,7 +196,7 @@ class SparseConcatTest(test.TestCase): self.assertEqual(sp_concat.values.get_shape(), [7]) self.assertEqual(sp_concat.dense_shape.get_shape(), [2]) - concat_out = sess.run(sp_concat) + concat_out = self.evaluate(sp_concat) self.assertAllEqual( concat_out.indices, @@ -220,7 +221,7 @@ class SparseConcatTest(test.TestCase): self.assertEqual(sp_concat.values.get_shape(), [10]) self.assertEqual(sp_concat.dense_shape.get_shape(), [2]) - concat_out = sess.run(sp_concat) + concat_out = self.evaluate(sp_concat) self.assertAllEqual(concat_out.indices, [[0, 2], [1, 0], [1, 4], [1, 8], [2, 0], [2, 2], [2, 3], [2, 6], @@ -244,7 +245,7 @@ class SparseConcatTest(test.TestCase): self.assertEqual(sp_concat.values.get_shape(), [8]) self.assertEqual(sp_concat.dense_shape.get_shape(), [2]) - concat_out = sess.run(sp_concat) + concat_out = self.evaluate(sp_concat) self.assertAllEqual( concat_out.indices, @@ -253,6 +254,7 @@ class SparseConcatTest(test.TestCase): [b"a", b"b", b"e", b"c", b"d", b"f", b"g", b"h"]) self.assertAllEqual(concat_out.dense_shape, [3, 8]) + @test_util.run_deprecated_v1 def testMismatchedRank(self): with self.session(use_gpu=False): sp_a = self._SparseTensor_3x3() @@ -263,6 +265,7 @@ class SparseConcatTest(test.TestCase): with self.assertRaises(ValueError): sparse_ops.sparse_concat(concat_dim, [sp_a, sp_e]) + @test_util.run_deprecated_v1 def testMismatchedRankExpandNonconcatDim(self): with self.session(use_gpu=False): sp_a = self._SparseTensor_3x3() @@ -275,6 +278,7 @@ class SparseConcatTest(test.TestCase): sparse_ops.sparse_concat( concat_dim, [sp_a, sp_e], expand_nonconcat_dim=True) + @test_util.run_deprecated_v1 def testMismatchedShapes(self): with self.session(use_gpu=False) as sess: sp_a = self._SparseTensor_3x3() @@ -287,7 +291,7 @@ class SparseConcatTest(test.TestCase): # Shape mismatches can only be caught when the op is run with self.assertRaisesOpError("Input shapes must match"): - sess.run(sp_concat) + self.evaluate(sp_concat) def testMismatchedShapesExpandNonconcatDim(self): with self.session(use_gpu=False) as sess: @@ -302,8 +306,8 @@ class SparseConcatTest(test.TestCase): sp_concat_dim1 = sparse_ops.sparse_concat( concat_dim1, [sp_a, sp_b, sp_c, sp_d], expand_nonconcat_dim=True) - sp_concat_dim0_out = sess.run(sp_concat_dim0) - sp_concat_dim1_out = sess.run(sp_concat_dim1) + sp_concat_dim0_out = self.evaluate(sp_concat_dim0) + sp_concat_dim1_out = self.evaluate(sp_concat_dim1) self.assertAllEqual(sp_concat_dim0_out.indices, [[0, 2], [1, 0], [2, 0], [2, 2], [4, 1], [5, 0], @@ -321,6 +325,7 @@ class SparseConcatTest(test.TestCase): [1, 1, 2, 1, 1, 1, 2, 3, 4, 2, 1, 0, 2]) self.assertAllEqual(sp_concat_dim1_out.dense_shape, [3, 13]) + @test_util.run_deprecated_v1 def testShapeInferenceUnknownShapes(self): with self.session(use_gpu=False): sp_inputs = [ diff --git a/tensorflow/python/kernel_tests/sparse_conditional_accumulator_test.py b/tensorflow/python/kernel_tests/sparse_conditional_accumulator_test.py index a824d5c826..275c86e534 100644 --- a/tensorflow/python/kernel_tests/sparse_conditional_accumulator_test.py +++ b/tensorflow/python/kernel_tests/sparse_conditional_accumulator_test.py @@ -26,6 +26,7 @@ from tensorflow.python.framework import dtypes as dtypes_lib from tensorflow.python.framework import errors_impl from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import data_flow_ops from tensorflow.python.platform import test @@ -98,12 +99,14 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): attr { key: 'reduction_type' value {s: 'MEAN'} } """, q.accumulator_ref.op.node_def) + @test_util.run_deprecated_v1 def testAccumulatorSizeEmpty(self): with self.cached_session(): q = data_flow_ops.SparseConditionalAccumulator( dtypes_lib.float32, name="Q") self.assertEqual(q.num_accumulated().eval(), 0) + @test_util.run_deprecated_v1 def testAccumulatorSetGlobalStep(self): with self.cached_session(): q = data_flow_ops.SparseConditionalAccumulator( @@ -111,6 +114,7 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): set_global_step_op = q.set_global_step(1) set_global_step_op.run() + @test_util.run_deprecated_v1 def testAccumulatorApplyGradFloat32(self): with self.cached_session(): q = data_flow_ops.SparseConditionalAccumulator( @@ -122,6 +126,7 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): accum_op.run() self.assertEqual(q.num_accumulated().eval(), 1) + @test_util.run_deprecated_v1 def testDtypes(self): with self.cached_session() as sess: dtypes = [dtypes_lib.float16, dtypes_lib.float32, dtypes_lib.float64] @@ -140,10 +145,11 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): t = _indexedslice(mat_to_add) q.apply_indexed_slices_grad(t).run() - result = sess.run(q.take_indexed_slices_grad(1)) + result = self.evaluate(q.take_indexed_slices_grad(1)) self._assertEqual_nparray(sum_elems / len(elems), result, sess) + @test_util.run_deprecated_v1 def testAccumulatorMultipleAccumulators(self): with self.cached_session() as sess: q_f32_0 = data_flow_ops.SparseConditionalAccumulator( @@ -174,6 +180,7 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): result = sess.run(accums[i].take_indexed_slices_grad(1)) self._assertEqual_indexedslices(expected_tensors[i], result) + @test_util.run_deprecated_v1 def testAccumulatorTakeGradMean(self): with self.cached_session() as sess: q = data_flow_ops.SparseConditionalAccumulator( @@ -189,11 +196,12 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): accum_op.run() takeg_t = q.take_indexed_slices_grad(1) - val = sess.run(takeg_t) + val = self.evaluate(takeg_t) self.assertAllEqual([0, 1, 2], val.indices) self.assertAllEqual([[0.5, 0.5], [0, 2], [3, 0]], val.values) self.assertAllEqual([-1, 2], val.dense_shape) + @test_util.run_deprecated_v1 def testAccumulatorTakeGradSum(self): with self.cached_session() as sess: q = data_flow_ops.SparseConditionalAccumulator( @@ -209,16 +217,18 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): accum_op.run() takeg_t = q.take_indexed_slices_grad(1) - val = sess.run(takeg_t) + val = self.evaluate(takeg_t) self.assertAllEqual([0, 1, 2], val.indices) self.assertAllEqual([[1, 1], [0, 2], [3, 0]], val.values) self.assertAllEqual([-1, 2], val.dense_shape) + @test_util.run_deprecated_v1 def testAccumulatorTakeGradInvalidReductionType(self): with self.assertRaises(ValueError): data_flow_ops.SparseConditionalAccumulator( dtypes_lib.float32, name="Q", shape=(), reduction_type="Invalid") + @test_util.run_deprecated_v1 def testAccumulatorRepeatedTakeGrad(self): with self.cached_session() as sess: q = data_flow_ops.SparseConditionalAccumulator( @@ -235,7 +245,7 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): accum_op.run() takeg_t = q.take_indexed_slices_grad(1) - val = sess.run(takeg_t) + val = self.evaluate(takeg_t) self.assertAllEqual(val.indices, [0, 1, 2]) self.assertAllEqual(val.values, [[0.5, 0.5], [0, 2], [3, 0]]) self.assertAllEqual(val.dense_shape, [-1, 2]) @@ -252,11 +262,12 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): accum_op.run() takeg_t = q.take_indexed_slices_grad(1) - val = sess.run(takeg_t) + val = self.evaluate(takeg_t) self.assertAllEqual(val.indices, [0, 1, 2]) self.assertAllEqual(val.values, [[5, 5], [0, 20], [30, 0]]) self.assertAllEqual(val.dense_shape, [-1, 2]) + @test_util.run_deprecated_v1 def testParallelApplyGradMean(self): with self.cached_session() as sess: q = data_flow_ops.SparseConditionalAccumulator( @@ -269,7 +280,7 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): takeg_t = q.take_indexed_slices_grad(1) def apply_indexed_slices_grad(accum_op): - sess.run(accum_op) + self.evaluate(accum_op) threads = [ self.checkedThread( @@ -281,13 +292,14 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): for thread in threads: thread.join() - val = sess.run(takeg_t) + val = self.evaluate(takeg_t) expected_val = sum(elems) / len(elems) self._assertEqual_nparray( np.array([[expected_val, 0], [0, expected_val]]).astype(np.float32), val, sess) + @test_util.run_deprecated_v1 def testParallelApplyGradSum(self): with self.cached_session() as sess: q = data_flow_ops.SparseConditionalAccumulator( @@ -303,7 +315,7 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): takeg_t = q.take_indexed_slices_grad(1) def apply_indexed_slices_grad(accum_op): - sess.run(accum_op) + self.evaluate(accum_op) threads = [ self.checkedThread(target=apply_indexed_slices_grad, args=(o,)) @@ -315,13 +327,14 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): for thread in threads: thread.join() - val = sess.run(takeg_t) + val = self.evaluate(takeg_t) expected_val = 550.0 self._assertEqual_nparray( np.array([[expected_val, 0], [0, expected_val]]).astype(np.float32), val, sess) + @test_util.run_deprecated_v1 def testParallelTakeGrad(self): with self.cached_session() as sess: q = data_flow_ops.SparseConditionalAccumulator( @@ -338,13 +351,13 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): def apply_indexed_slices_grad(): for accum_op in accum_ops: time.sleep(1.0) - sess.run(accum_op) + self.evaluate(accum_op) apply_indexed_slices_grad_thread = self.checkedThread( target=apply_indexed_slices_grad) def take_grad(): - t = sess.run(takeg_t) + t = self.evaluate(takeg_t) results.append(t) threads = [self.checkedThread(target=take_grad) for _ in range(10)] @@ -361,6 +374,7 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): self._assertEqual_nparray( np.array([[0, 0], [elems[i], 0]]), results[i], sess) + @test_util.run_deprecated_v1 def testAccumulatorApplyAndBlockingTake(self): with self.cached_session() as sess: q = data_flow_ops.SparseConditionalAccumulator( @@ -378,10 +392,10 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): def apply_indexed_slices_grad(): for accum_op in accum_ops: - sess.run(accum_op) + self.evaluate(accum_op) def take_grad(): - results.append(sess.run(takeg_t)) + results.append(self.evaluate(takeg_t)) accum_thread = self.checkedThread(target=apply_indexed_slices_grad) takeg_thread = self.checkedThread(target=take_grad) @@ -394,8 +408,9 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): def _blocking_takeg(self, sess, takeg_op): with self.assertRaisesOpError("was cancelled"): - sess.run(takeg_op) + self.evaluate(takeg_op) + @test_util.run_deprecated_v1 def testAccumulatorCancel(self): with self.cached_session() as sess: q = data_flow_ops.SparseConditionalAccumulator( @@ -415,6 +430,7 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): takeg_thread.join() + @test_util.run_deprecated_v1 def testNonVectorIndices(self): with self.cached_session(): q = data_flow_ops.SparseConditionalAccumulator( @@ -427,6 +443,7 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): grad_indices=[[0, 1], [1, 0]], grad_values=np.array([1, 2]).astype(np.float32)).run() + @test_util.run_deprecated_v1 def testZeroDimensionValues(self): with self.cached_session(): q = data_flow_ops.SparseConditionalAccumulator( @@ -437,6 +454,7 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): q.apply_grad( grad_indices=[0], grad_values=np.array(1).astype(np.float32)).run() + @test_util.run_deprecated_v1 def testWrongNonEmptyInputValues(self): with self.cached_session(): q = data_flow_ops.SparseConditionalAccumulator( @@ -448,6 +466,7 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): grad_indices=[0, 1], grad_values=np.array([[0, 1, 1]]).astype(np.float32)).run() + @test_util.run_deprecated_v1 def testDynamicNonVectorIndices(self): with self.cached_session() as sess: q = data_flow_ops.SparseConditionalAccumulator( @@ -467,6 +486,7 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): x_values: np.array([1, 2]).astype(np.float32) }) + @test_util.run_deprecated_v1 def testDynamicWrongNonEmptyInputValues(self): with self.cached_session() as sess: q = data_flow_ops.SparseConditionalAccumulator( @@ -485,6 +505,7 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): x_values: np.array([[0, 1, 1]]).astype(np.float32) }) + @test_util.run_deprecated_v1 def testEmptyShapeApply(self): with self.cached_session(): q = data_flow_ops.SparseConditionalAccumulator( @@ -510,6 +531,7 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): q.apply_grad(grad_indices=[0], grad_values=[1.0], grad_shape=[]).run() q.apply_grad(grad_indices=[0], grad_values=[1.0]).run() + @test_util.run_deprecated_v1 def testValidateShape(self): with self.cached_session() as sess: q = data_flow_ops.SparseConditionalAccumulator( @@ -585,7 +607,7 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): np.float32)).run() # After take grad, constraints on accumulated gradient are removed - sess.run(q.take_grad(1)) + self.evaluate(q.take_grad(1)) # First successful gradient imposes new constraints. # Hereafter, shape will additionally constrained to [None,2,2,3] @@ -605,6 +627,7 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): [[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]]).astype(np.float32), local_step=1).run() + @test_util.run_deprecated_v1 def testReturnShape(self): with self.cached_session() as sess: q = data_flow_ops.SparseConditionalAccumulator( @@ -615,7 +638,7 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): grad_values=np.array( [[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]]).astype(np.float32)).run() - val = sess.run(q.take_indexed_slices_grad(1)) + val = self.evaluate(q.take_indexed_slices_grad(1)) self.assertAllEqual(val.dense_shape, [2, 2, 2, 2]) q = data_flow_ops.SparseConditionalAccumulator( @@ -627,9 +650,10 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): [[[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]]).astype( np.float32)).run() - val = sess.run(q.take_indexed_slices_grad(1)) + val = self.evaluate(q.take_indexed_slices_grad(1)) self.assertAllEqual(val.dense_shape, [-1, 2, 2, 3]) + @test_util.run_deprecated_v1 def testApplyGradtInt32IndicesAndShape(self): with self.cached_session() as sess: q = data_flow_ops.SparseConditionalAccumulator( @@ -653,7 +677,7 @@ class IndexedSlicesConditionalAccumulatorTest(test.TestCase): accum_op.run() self.assertEqual(q.num_accumulated().eval(), 2) - val = sess.run(q.take_indexed_slices_grad(1)) + val = self.evaluate(q.take_indexed_slices_grad(1)) self.assertAllEqual(val.indices, [0, 2]) self.assertAllEqual(val.values, [[0, 0, 1], [3, 0, 4]]) self.assertAllEqual(val.dense_shape, [3, 3]) diff --git a/tensorflow/python/kernel_tests/sparse_cross_op_test.py b/tensorflow/python/kernel_tests/sparse_cross_op_test.py index 6e0714da70..566bbb56f0 100644 --- a/tensorflow/python/kernel_tests/sparse_cross_op_test.py +++ b/tensorflow/python/kernel_tests/sparse_cross_op_test.py @@ -24,12 +24,14 @@ from tensorflow.python.client import session from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import test_util from tensorflow.python.ops import sparse_ops from tensorflow.python.platform import test class SparseCrossOpTest(test.TestCase): + @test_util.run_deprecated_v1 def test_simple(self): """Tests a simple scenario.""" op = sparse_ops.sparse_cross([ @@ -43,8 +45,9 @@ class SparseCrossOpTest(test.TestCase): 'batch2-FC1-F2_X_batch2-FC2-F1', 'batch2-FC1-F2_X_batch2-FC2-F2' ]]) with self.cached_session() as sess: - self._assert_sparse_tensor_equals(expected_out, sess.run(op)) + self._assert_sparse_tensor_equals(expected_out, self.evaluate(op)) + @test_util.run_deprecated_v1 def test_dense(self): """Tests only dense inputs.""" op = sparse_ops.sparse_cross([ @@ -63,8 +66,9 @@ class SparseCrossOpTest(test.TestCase): 'batch2-FC1-F2_X_batch2-FC2-F1', 'batch2-FC1-F2_X_batch2-FC2-F2' ]]) with self.cached_session() as sess: - self._assert_sparse_tensor_equals(expected_out, sess.run(op)) + self._assert_sparse_tensor_equals(expected_out, self.evaluate(op)) + @test_util.run_deprecated_v1 def test_integer_mixed_string_sparse(self): """Tests mixed type.""" op = sparse_ops.sparse_cross([ @@ -77,8 +81,9 @@ class SparseCrossOpTest(test.TestCase): '55555_X_batch2-FC2-F2' ]]) with self.cached_session() as sess: - self._assert_sparse_tensor_equals(expected_out, sess.run(op)) + self._assert_sparse_tensor_equals(expected_out, self.evaluate(op)) + @test_util.run_deprecated_v1 def test_integer_mixed_string_dense(self): """Tests mixed dense inputs.""" op = sparse_ops.sparse_cross([ @@ -95,8 +100,9 @@ class SparseCrossOpTest(test.TestCase): '999999_X_batch2-FC2-F1', '999999_X_batch2-FC2-F2' ]]) with self.cached_session() as sess: - self._assert_sparse_tensor_equals(expected_out, sess.run(op)) + self._assert_sparse_tensor_equals(expected_out, self.evaluate(op)) + @test_util.run_deprecated_v1 def test_sparse_cross_dense(self): """Tests sparse and dense inputs.""" op = sparse_ops.sparse_cross([ @@ -112,8 +118,9 @@ class SparseCrossOpTest(test.TestCase): 'batch2-FC1-F2_X_batch2-FC2-F1', 'batch2-FC1-F2_X_batch2-FC2-F2' ]]) with self.cached_session() as sess: - self._assert_sparse_tensor_equals(expected_out, sess.run(op)) + self._assert_sparse_tensor_equals(expected_out, self.evaluate(op)) + @test_util.run_deprecated_v1 def test_integer_sparse_input(self): """Tests mixed type sparse and dense inputs.""" op = sparse_ops.sparse_cross([ @@ -128,8 +135,9 @@ class SparseCrossOpTest(test.TestCase): '5555_X_batch2-FC2-F1', '5555_X_batch2-FC2-F2' ]]) with self.cached_session() as sess: - self._assert_sparse_tensor_equals(expected_out, sess.run(op)) + self._assert_sparse_tensor_equals(expected_out, self.evaluate(op)) + @test_util.run_deprecated_v1 def test_permutation_3x3x3(self): """Tests 3x3x3 permutation.""" op = sparse_ops.sparse_cross([ @@ -170,8 +178,9 @@ class SparseCrossOpTest(test.TestCase): 'batch1-FC1-F3_X_batch1-FC2-F3_X_batch1-FC3-F3' ]]) with self.cached_session() as sess: - self._assert_sparse_tensor_equals(expected_out, sess.run(op)) + self._assert_sparse_tensor_equals(expected_out, self.evaluate(op)) + @test_util.run_deprecated_v1 def test_permutation_3x1x2(self): """Tests 3x1x2 permutation.""" op = sparse_ops.sparse_cross([ @@ -189,8 +198,9 @@ class SparseCrossOpTest(test.TestCase): 'batch1-FC1-F3_X_batch1-FC2-F1_X_batch1-FC3-F2' ]]) with self.cached_session() as sess: - self._assert_sparse_tensor_equals(expected_out, sess.run(op)) + self._assert_sparse_tensor_equals(expected_out, self.evaluate(op)) + @test_util.run_deprecated_v1 def test_large_batch(self): """Tests with large batch size to force multithreading.""" batch_size = 5000 @@ -222,8 +232,9 @@ class SparseCrossOpTest(test.TestCase): expected_out = self._sparse_tensor(col_out) with self.cached_session() as sess: - self._assert_sparse_tensor_equals(expected_out, sess.run(op)) + self._assert_sparse_tensor_equals(expected_out, self.evaluate(op)) + @test_util.run_deprecated_v1 def test_one_column_empty(self): """Tests when one column is empty. @@ -235,8 +246,9 @@ class SparseCrossOpTest(test.TestCase): self._sparse_tensor([['batch1-FC3-F1', 'batch1-FC3-F2']]) ]) with self.cached_session() as sess: - self._assert_sparse_tensor_empty(sess.run(op)) + self._assert_sparse_tensor_empty(self.evaluate(op)) + @test_util.run_deprecated_v1 def test_some_columns_empty(self): """Tests when more than one columns are empty. @@ -254,8 +266,9 @@ class SparseCrossOpTest(test.TestCase): 'batch1-FC1-F2_X_batch1-FC2-F1_X_batch1-FC3-F2' ]], 2) with self.cached_session() as sess: - self._assert_sparse_tensor_equals(expected_out, sess.run(op)) + self._assert_sparse_tensor_equals(expected_out, self.evaluate(op)) + @test_util.run_deprecated_v1 def test_all_columns_empty(self): """Tests when all columns are empty. @@ -267,8 +280,9 @@ class SparseCrossOpTest(test.TestCase): self._sparse_tensor([]) ]) with self.cached_session() as sess: - self._assert_sparse_tensor_empty(sess.run(op)) + self._assert_sparse_tensor_empty(self.evaluate(op)) + @test_util.run_deprecated_v1 def test_hashed_zero_bucket_no_hash_key(self): op = sparse_ops.sparse_cross_hashed([ self._sparse_tensor([['batch1-FC1-F1']]), @@ -278,8 +292,9 @@ class SparseCrossOpTest(test.TestCase): # Check actual hashed output to prevent unintentional hashing changes. expected_out = self._sparse_tensor([[1971693436396284976]]) with self.cached_session() as sess: - self._assert_sparse_tensor_equals(expected_out, sess.run(op)) + self._assert_sparse_tensor_equals(expected_out, self.evaluate(op)) + @test_util.run_deprecated_v1 def test_hashed_zero_bucket(self): op = sparse_ops.sparse_cross_hashed( [ @@ -291,9 +306,10 @@ class SparseCrossOpTest(test.TestCase): # Check actual hashed output to prevent unintentional hashing changes. expected_out = self._sparse_tensor([[4847552627144134031]]) with self.cached_session() as sess: - self._assert_sparse_tensor_equals(expected_out, sess.run(op)) + self._assert_sparse_tensor_equals(expected_out, self.evaluate(op)) # TODO(sibyl-Aix6ihai): Add benchmark to compare Hashed vs Non-hashed. + @test_util.run_deprecated_v1 def test_hashed_no_hash_key(self): op = sparse_ops.sparse_cross_hashed( [ @@ -305,8 +321,9 @@ class SparseCrossOpTest(test.TestCase): # Check actual hashed output to prevent unintentional hashing changes. expected_out = self._sparse_tensor([[83]]) with self.cached_session() as sess: - self._assert_sparse_tensor_equals(expected_out, sess.run(op)) + self._assert_sparse_tensor_equals(expected_out, self.evaluate(op)) + @test_util.run_deprecated_v1 def test_hashed_output(self): op = sparse_ops.sparse_cross_hashed( [ @@ -319,8 +336,9 @@ class SparseCrossOpTest(test.TestCase): # Check actual hashed output to prevent unintentional hashing changes. expected_out = self._sparse_tensor([[31]]) with self.cached_session() as sess: - self._assert_sparse_tensor_equals(expected_out, sess.run(op)) + self._assert_sparse_tensor_equals(expected_out, self.evaluate(op)) + @test_util.run_deprecated_v1 def test_hashed__has_no_collision(self): """Tests that fingerprint concatenation has no collisions.""" # Although the last 10 bits of 359 and 1024+359 are identical. @@ -331,7 +349,7 @@ class SparseCrossOpTest(test.TestCase): [t2, t1], num_buckets=1024, hash_key=sparse_ops._DEFAULT_HASH_KEY + 1) cross_dense = sparse_ops.sparse_tensor_to_dense(cross) with session.Session(): - values = cross_dense.eval() + values = self.evaluate(cross_dense) self.assertTrue(numpy.not_equal(values[0], values[1]).all()) def test_hashed_3x1x2(self): @@ -345,7 +363,7 @@ class SparseCrossOpTest(test.TestCase): ], num_buckets=1000) with self.cached_session() as sess: - out = sess.run(op) + out = self.evaluate(op) self.assertEqual(6, len(out.values)) self.assertAllEqual([[0, i] for i in range(6)], out.indices) self.assertTrue(all(x < 1000 and x >= 0 for x in out.values)) diff --git a/tensorflow/python/kernel_tests/sparse_matmul_op_test.py b/tensorflow/python/kernel_tests/sparse_matmul_op_test.py index 541463e76b..2e17a9c608 100644 --- a/tensorflow/python/kernel_tests/sparse_matmul_op_test.py +++ b/tensorflow/python/kernel_tests/sparse_matmul_op_test.py @@ -22,6 +22,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import math_ops from tensorflow.python.platform import test @@ -58,7 +59,7 @@ class SparseMatMulTest(test.TestCase): transpose_b=tr_b, a_is_sparse=sp_a, b_is_sparse=sp_b) - out = tf_ans.eval() + out = self.evaluate(tf_ans) np_x = math_ops.cast(tf_x, dtypes.float32).eval() np_y = math_ops.cast(tf_y, dtypes.float32).eval() @@ -71,6 +72,7 @@ class SparseMatMulTest(test.TestCase): self.assertShapeEqual(np_ans, tf_ans) self.assertAllCloseAccordingToType(np_ans, out, rtol=1e-4, atol=1e-4) + @test_util.run_deprecated_v1 def testBasic(self): x = np.arange(0., 4.).reshape([4, 1]).astype(np.float32) y = np.arange(-1., 1.).reshape([1, 2]).astype(np.float32) @@ -78,6 +80,7 @@ class SparseMatMulTest(test.TestCase): for y_dtype in (dtypes.float32, dtypes.bfloat16): self._testCpuMatmul(x, y, x_dtype=x_dtype, y_dtype=y_dtype) + @test_util.run_deprecated_v1 def testZeroDim(self): x = np.ones((4, 0)).astype(np.float32) y = np.ones((0, 3)).astype(np.float32) @@ -85,6 +88,7 @@ class SparseMatMulTest(test.TestCase): for y_dtype in (dtypes.float32, dtypes.bfloat16): self._testCpuMatmul(x, y, x_dtype=x_dtype, y_dtype=y_dtype) + @test_util.run_deprecated_v1 def testEmpty(self): x = np.ones((0, 0)).astype(np.float32) y = np.ones((0, 0)).astype(np.float32) @@ -93,6 +97,7 @@ class SparseMatMulTest(test.TestCase): self._testCpuMatmul(x, y, x_dtype=x_dtype, y_dtype=y_dtype) # Tests setting one dimension to be a high value. + @test_util.run_deprecated_v1 def testLarge(self): r1 = np.random.randint(6000, 20000) r2 = np.random.randint(1, 10) @@ -105,6 +110,7 @@ class SparseMatMulTest(test.TestCase): self._testCpuMatmul(x, y, x_dtype=x_dtype, y_dtype=y_dtype) # Tests random sized matrices. + @test_util.run_deprecated_v1 def testRandom(self): for tr_a in [True, False]: for tr_b in [True, False]: @@ -159,6 +165,7 @@ class MatMulGradientTest(test.TestCase): delta=delta)) self.assertLessEqual(err, delta / 2.) + @test_util.run_deprecated_v1 def testGradientInput(self): for tr_a in [True, False]: for tr_b in [True, False]: diff --git a/tensorflow/python/kernel_tests/sparse_ops_test.py b/tensorflow/python/kernel_tests/sparse_ops_test.py index a45ce2e13b..75f65e6251 100644 --- a/tensorflow/python/kernel_tests/sparse_ops_test.py +++ b/tensorflow/python/kernel_tests/sparse_ops_test.py @@ -71,6 +71,7 @@ class SparseToIndicatorTest(test_util.TensorFlowTestCase): constant_op.constant(val, dtype), constant_op.constant(shape, dtypes.int64)) + @test_util.run_deprecated_v1 def testInt32(self): with self.session(use_gpu=False): sp_input = self._SparseTensor_5x6(dtypes.int32) @@ -83,6 +84,7 @@ class SparseToIndicatorTest(test_util.TensorFlowTestCase): self.assertAllEqual(output, expected_output) + @test_util.run_deprecated_v1 def testInt64(self): with self.session(use_gpu=False): sp_input = self._SparseTensor_5x6(dtypes.int64) @@ -95,6 +97,7 @@ class SparseToIndicatorTest(test_util.TensorFlowTestCase): self.assertAllEqual(output, expected_output) + @test_util.run_deprecated_v1 def testHigherRank(self): with self.session(use_gpu=False): sp_input = self._SparseTensor_2x3x4(dtypes.int64) @@ -154,7 +157,7 @@ class SparseMergeTest(test_util.TensorFlowTestCase): sparse_tensor.SparseTensor.from_value(values_v)): sp_output = sparse_ops.sparse_merge(indices, values, vocab_size) - output = sess.run(sp_output) + output = self.evaluate(sp_output) self._AssertResultsSorted(output, vocab_size) def testInt64AndFloat32(self): @@ -163,7 +166,7 @@ class SparseMergeTest(test_util.TensorFlowTestCase): indices, values = self._SparseTensor_3x50(np.int64, np.float32) sp_output = sparse_ops.sparse_merge(indices, values, vocab_size) - output = sess.run(sp_output) + output = self.evaluate(sp_output) self._AssertResultsSorted(output, vocab_size) def testInt64AndFloat64(self): @@ -172,7 +175,7 @@ class SparseMergeTest(test_util.TensorFlowTestCase): indices, values = self._SparseTensor_3x50(np.int64, np.float64) sp_output = sparse_ops.sparse_merge(indices, values, vocab_size) - output = sess.run(sp_output) + output = self.evaluate(sp_output) self._AssertResultsSorted(output, vocab_size) def testInt32AndFloat32NonCanonicalOrder(self): @@ -182,7 +185,7 @@ class SparseMergeTest(test_util.TensorFlowTestCase): sp_output = sparse_ops.sparse_merge( indices, values, vocab_size, already_sorted=True) - output = sess.run(sp_output) + output = self.evaluate(sp_output) self._AssertResultsNotSorted(output, vocab_size) def testInt64AndFloat32NonCanonicalOrder(self): @@ -192,7 +195,7 @@ class SparseMergeTest(test_util.TensorFlowTestCase): sp_output = sparse_ops.sparse_merge( indices, values, vocab_size, already_sorted=True) - output = sess.run(sp_output) + output = self.evaluate(sp_output) self._AssertResultsNotSorted(output, vocab_size) def testInt64AndFloat64NonCanonicalOrder(self): @@ -203,7 +206,7 @@ class SparseMergeTest(test_util.TensorFlowTestCase): sp_output = sparse_ops.sparse_merge( indices, values, vocab_size_tensor, already_sorted=True) - output = sess.run(sp_output) + output = self.evaluate(sp_output) self._AssertResultsNotSorted(output, vocab_size) def testShouldSetLastDimensionInDynamicShape(self): @@ -261,7 +264,7 @@ class SparseMergeHighDimTest(test_util.TensorFlowTestCase): indices, values = self._SparseTensor_3x50(np.int64, np.float32) sp_output = sparse_ops.sparse_merge(indices, values, vocab_size) - output = sess.run(sp_output) + output = self.evaluate(sp_output) self._AssertResultsSorted(output, vocab_size) def testInt64AndFloat64(self): @@ -270,7 +273,7 @@ class SparseMergeHighDimTest(test_util.TensorFlowTestCase): indices, values = self._SparseTensor_3x50(np.int64, np.float64) sp_output = sparse_ops.sparse_merge(indices, values, vocab_size) - output = sess.run(sp_output) + output = self.evaluate(sp_output) self._AssertResultsSorted(output, vocab_size) def testInt64AndFloat64Shape(self): @@ -279,7 +282,7 @@ class SparseMergeHighDimTest(test_util.TensorFlowTestCase): indices, values = self._SparseTensor_3x50(np.int64, np.float64) sp_output = sparse_ops.sparse_merge(indices, values, vocab_size) - output = sess.run(sp_output) + output = self.evaluate(sp_output) self._AssertResultsSorted(output, vocab_size) @@ -296,13 +299,14 @@ class SparseRetainTest(test_util.TensorFlowTestCase): def _SparseTensor_5x6(self): return sparse_tensor.SparseTensor.from_value(self._SparseTensorValue_5x6()) + @test_util.run_deprecated_v1 def testBasic(self): with self.session(use_gpu=False) as sess: for sp_input in (self._SparseTensorValue_5x6(), self._SparseTensor_5x6()): to_retain = np.array([1, 0, 0, 1, 1, 0], dtype=np.bool) sp_output = sparse_ops.sparse_retain(sp_input, to_retain) - output = sess.run(sp_output) + output = self.evaluate(sp_output) self.assertAllEqual(output.indices, [[0, 0], [1, 4], [3, 2]]) self.assertAllEqual(output.values, [0, 14, 32]) @@ -314,7 +318,7 @@ class SparseRetainTest(test_util.TensorFlowTestCase): to_retain = np.zeros((6,), dtype=np.bool) sp_output = sparse_ops.sparse_retain(sp_input, to_retain) - output = sess.run(sp_output) + output = self.evaluate(sp_output) self.assertAllEqual(output.indices, np.array([]).reshape((0, 2))) self.assertAllEqual(output.values, []) @@ -353,38 +357,42 @@ class SparseResetShapeTest(test_util.TensorFlowTestCase): return sparse_tensor.SparseTensorValue(self._IND_2_5_6, self._VAL_2_5_6, self._SHP_2_5_6) + @test_util.run_deprecated_v1 def testStaticShapeInfoPreservedWhenNewShapeIsProvidedAndStatic(self): sp_input = self._SparseTensor_2x5x6() new_shape = np.array([3, 6, 7], dtype=np.int64) sp_output = sparse_ops.sparse_reset_shape(sp_input, new_shape) self.assertAllEqual([3, 6, 7], sp_output.get_shape()) + @test_util.run_deprecated_v1 def testBasic(self): with self.session(use_gpu=False) as sess: sp_input = self._SparseTensor_2x5x6() new_shape = np.array([3, 6, 7], dtype=np.int64) sp_output = sparse_ops.sparse_reset_shape(sp_input, new_shape) - output = sess.run(sp_output) + output = self.evaluate(sp_output) self.assertAllEqual(output.indices, [[0, 0, 0], [0, 1, 0], [0, 1, 3], [1, 1, 4], [1, 3, 2], [1, 3, 3]]) self.assertAllEqual(output.values, [0, 10, 13, 14, 32, 33]) self.assertAllEqual(output.dense_shape, [3, 6, 7]) + @test_util.run_deprecated_v1 def testInputUnavailableInGraphConstructionOk(self): with self.session(use_gpu=False) as sess: sp_input = self._SparseTensorValue_2x5x6() new_shape = np.array([3, 6, 7], dtype=np.int64) sp_output = sparse_ops.sparse_reset_shape(sp_input, new_shape) - output = sess.run(sp_output) + output = self.evaluate(sp_output) self.assertAllEqual(output.indices, [[0, 0, 0], [0, 1, 0], [0, 1, 3], [1, 1, 4], [1, 3, 2], [1, 3, 3]]) self.assertAllEqual(output.values, [0, 10, 13, 14, 32, 33]) self.assertAllEqual(output.dense_shape, [3, 6, 7]) + @test_util.run_deprecated_v1 def testFeedInputUnavailableInGraphConstructionOk(self): with self.session(use_gpu=False) as sess: sp_input = array_ops.sparse_placeholder(dtype=dtypes.int32) @@ -404,7 +412,7 @@ class SparseResetShapeTest(test_util.TensorFlowTestCase): sp_input = self._SparseTensor_2x5x6() sp_output = sparse_ops.sparse_reset_shape(sp_input) - output = sess.run(sp_output) + output = self.evaluate(sp_output) self.assertAllEqual(output.indices, [[0, 0, 0], [0, 1, 0], [0, 1, 3], [1, 1, 4], [1, 3, 2], [1, 3, 3]]) @@ -416,12 +424,13 @@ class SparseResetShapeTest(test_util.TensorFlowTestCase): sp_input = self._SparseTensor_2x5x6_Empty() sp_output = sparse_ops.sparse_reset_shape(sp_input) - output = sess.run(sp_output) + output = self.evaluate(sp_output) self.assertAllEqual(output.indices.shape, [0, 3]) self.assertAllEqual(output.values.shape, [0]) self.assertAllEqual(output.dense_shape, [0, 0, 0]) + @test_util.run_deprecated_v1 def testInvalidRank(self): with self.session(use_gpu=False): sp_input = self._SparseTensor_2x5x6() @@ -430,6 +439,7 @@ class SparseResetShapeTest(test_util.TensorFlowTestCase): with self.assertRaises(ValueError): sparse_ops.sparse_reset_shape(sp_input, new_shape) + @test_util.run_deprecated_v1 def testInvalidRankNewShapeUnavailableInGraphConstruction(self): with self.session(use_gpu=False) as sess: new_shape = array_ops.placeholder(dtype=dtypes.int64) @@ -439,6 +449,7 @@ class SparseResetShapeTest(test_util.TensorFlowTestCase): with self.assertRaisesOpError("x == y did not hold element-wise"): sess.run(out, feed_dict={new_shape: np.array([3, 7], dtype=np.int64)}) + @test_util.run_deprecated_v1 def testInvalidDimensionSizeStatic(self): sp_input = self._SparseTensor_2x5x6() new_shape = np.array([3, 7, 5], dtype=np.int64) @@ -446,6 +457,7 @@ class SparseResetShapeTest(test_util.TensorFlowTestCase): with self.assertRaisesRegexp(ValueError, "should have dimension sizes"): sparse_ops.sparse_reset_shape(sp_input, new_shape) + @test_util.run_deprecated_v1 def testInvalidDimensionSizeDynamic(self): with self.session(use_gpu=False) as sess: sp_input = self._SparseTensor_2x5x6() @@ -455,6 +467,7 @@ class SparseResetShapeTest(test_util.TensorFlowTestCase): with self.assertRaisesOpError("x <= y did not hold element-wise"): sess.run(out, feed_dict={new_shape: [3, 7, 5]}) + @test_util.run_deprecated_v1 def testInvalidDimensionSizeInputUnavailableInGraphConstruction(self): sp_input = array_ops.sparse_placeholder(dtype=dtypes.int32) with self.session(use_gpu=False) as sess: @@ -496,6 +509,7 @@ class SparseFillEmptyRowsTest(test_util.TensorFlowTestCase): constant_op.constant(val, dtypes.int32), constant_op.constant(shape, dtypes.int64)) + @test_util.run_deprecated_v1 def testFillNumber(self): with self.session(use_gpu=False) as sess: for sp_input in (self._SparseTensorValue_5x6(), self._SparseTensor_5x6()): @@ -513,6 +527,7 @@ class SparseFillEmptyRowsTest(test_util.TensorFlowTestCase): self.assertAllEqual(empty_row_indicator_out, np.array([0, 0, 1, 0, 1]).astype(np.bool)) + @test_util.run_deprecated_v1 def testFillFloat(self): with self.session(use_gpu=False) as sess: values = constant_op.constant( @@ -547,6 +562,7 @@ class SparseFillEmptyRowsTest(test_util.TensorFlowTestCase): self.assertGreater(default_value_grad_err, 0) self.assertLess(default_value_grad_err, 1e-8) + @test_util.run_deprecated_v1 def testFillString(self): with self.session(use_gpu=False) as sess: sp_input = self._SparseTensor_String5x6() @@ -565,6 +581,7 @@ class SparseFillEmptyRowsTest(test_util.TensorFlowTestCase): self.assertAllEqual(empty_row_indicator_out, np.array([0, 0, 1, 0, 1]).astype(np.bool)) + @test_util.run_deprecated_v1 def testNoEmptyRows(self): with self.session(use_gpu=False) as sess: sp_input = self._SparseTensor_2x6() @@ -582,6 +599,7 @@ class SparseFillEmptyRowsTest(test_util.TensorFlowTestCase): class SparseAddTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testValuesInVariable(self): indices = constant_op.constant([[1]], dtype=dtypes.int64) values = variables.Variable([1], trainable=False, dtype=dtypes.float32) @@ -591,8 +609,8 @@ class SparseAddTest(test_util.TensorFlowTestCase): sp_output = sparse_ops.sparse_add(sp_input, sp_input) with self.session(use_gpu=False) as sess: - sess.run(variables.global_variables_initializer()) - output = sess.run(sp_output) + self.evaluate(variables.global_variables_initializer()) + output = self.evaluate(sp_output) self.assertAllEqual(output.values, [2]) @@ -635,7 +653,7 @@ class SparseReduceTest(test_util.TensorFlowTestCase): else: tf_dense_ans = sparse_ops.sparse_reduce_max(sp_t, reduction_axes, keep_dims) - out_dense = tf_dense_ans.eval() + out_dense = self.evaluate(tf_dense_ans) if do_sum: tf_sparse_ans = sparse_ops.sparse_reduce_sum_sparse(sp_t, @@ -657,6 +675,7 @@ class SparseReduceTest(test_util.TensorFlowTestCase): self._compare(sp_t, reduction_axes, ndims, True, False) self._compare(sp_t, reduction_axes, ndims, True, True) + @test_util.run_deprecated_v1 def testSimpleAndRandomInputs(self): if np.__version__ == "1.13.0": self.skipTest("numpy 1.13.0 bug") @@ -696,6 +715,7 @@ class SparseReduceTest(test_util.TensorFlowTestCase): with self.assertRaisesOpError("Invalid reduction dimension 2"): sparse_ops.sparse_reduce_max(sp_t, 2).eval() + @test_util.run_deprecated_v1 def testGradient(self): if np.__version__ == "1.13.0": self.skipTest("numpy 1.13.0 bug") @@ -710,18 +730,59 @@ class SparseReduceTest(test_util.TensorFlowTestCase): axes = np.random.choice(len(dims), size=d, replace=False).tolist() reduced = sparse_ops.sparse_reduce_sum(sp_t, axes) - err = gradient_checker.compute_gradient_error(sp_t.values, (nnz,), - reduced, - reduced.eval().shape) + err = gradient_checker.compute_gradient_error( + sp_t.values, (nnz,), reduced, + self.evaluate(reduced).shape) self.assertLess(err, 1e-3) # Tests for negative axes. reduced = sparse_ops.sparse_reduce_sum(sp_t, -1) - err = gradient_checker.compute_gradient_error(sp_t.values, (nnz,), - reduced, - reduced.eval().shape) + err = gradient_checker.compute_gradient_error( + sp_t.values, (nnz,), reduced, + self.evaluate(reduced).shape) self.assertLess(err, 1e-3) + def _testSparseReduceShape(self, sp_t, reduction_axes, ndims, keep_dims, + do_sum): + densified = sparse_ops.sparse_tensor_to_dense(sp_t).eval() + + np_op = np.sum + tf_op = sparse_ops.sparse_reduce_sum + if not do_sum: + np_op = np.max + tf_op = sparse_ops.sparse_reduce_max + + np_ans = densified + if reduction_axes is None: + np_ans = np_op(np_ans, keepdims=keep_dims) + else: + if not isinstance(reduction_axes, list): # Single scalar. + reduction_axes = [reduction_axes] + reduction_axes = np.array(reduction_axes).astype(np.int32) + # Handles negative axes. + reduction_axes = (reduction_axes + ndims) % ndims + # Loop below depends on sorted. + reduction_axes.sort() + for ra in reduction_axes.ravel()[::-1]: + np_ans = np_op(np_ans, axis=ra, keepdims=keep_dims) + + tf_ans = tf_op(sp_t, reduction_axes, keep_dims) + self.assertAllEqual(np_ans.shape, tf_ans.get_shape().as_list()) + + def testSparseReduceSumOrMaxShape(self): + sp_t = sparse_tensor.SparseTensor(self.ind, self.vals, self.dense_shape) + + with self.session(use_gpu=False): + for do_sum in [True, False]: + for keep_dims in [True, False]: + self._testSparseReduceShape(sp_t, None, 2, keep_dims, do_sum) + self._testSparseReduceShape(sp_t, 0, 2, keep_dims, do_sum) + self._testSparseReduceShape(sp_t, [1], 2, keep_dims, do_sum) + self._testSparseReduceShape(sp_t, [0, 1], 2, keep_dims, do_sum) + self._testSparseReduceShape(sp_t, [1, 0], 2, keep_dims, do_sum) + self._testSparseReduceShape(sp_t, [-1], 2, keep_dims, do_sum) + self._testSparseReduceShape(sp_t, [1, -2], 2, keep_dims, do_sum) + class SparseMathOpsTest(test_util.TensorFlowTestCase): @@ -737,6 +798,7 @@ class SparseMathOpsTest(test_util.TensorFlowTestCase): result_tensor.values).eval() self.assertAllEqual(result_np, res_densified) + @test_util.run_deprecated_v1 def testCwiseDivAndMul(self): np.random.seed(1618) sp_shapes = [(10, 10, 10), (5, 5), (1618,), (3, 3, 7)] @@ -760,6 +822,7 @@ class SparseMathOpsTest(test_util.TensorFlowTestCase): res = sp_t / dense_t # should invoke "__truediv__" self.assertEqual(res.values.eval().dtype, np.float64) + @test_util.run_deprecated_v1 def testCwiseAdd(self): with self.session(use_gpu=False): # Identity(2) + AllOnes(2,2). Should be equal to 2 * Identity(2). @@ -779,6 +842,7 @@ class SparseMathOpsTest(test_util.TensorFlowTestCase): sparse_ops.sparse_dense_cwise_add(sp_t, dense_t), np.identity(2) * 2, sp_t) + @test_util.run_deprecated_v1 def testGradients(self): np.random.seed(1618) sp_shapes = [(10, 10, 10), (5, 5), (1618,), (3, 3, 7)] @@ -812,6 +876,7 @@ class SparseMathOpsTest(test_util.TensorFlowTestCase): class SparseSoftmaxTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testEquivalentToDensified(self): np.random.seed(1618) n, m = np.random.choice(20, size=2) @@ -831,6 +896,7 @@ class SparseSoftmaxTest(test_util.TensorFlowTestCase): self.assertAllClose(dense_result.eval(), sp_result) + @test_util.run_deprecated_v1 def testHigherRanks(self): # For the first shape: # First batch: @@ -860,6 +926,7 @@ class SparseSoftmaxTest(test_util.TensorFlowTestCase): self.assertAllEqual(sp_t.indices.eval(), result.indices) self.assertAllEqual(shape, result.dense_shape) + @test_util.run_deprecated_v1 def testGradient(self): x_shape = [2, 5, 10] with self.cached_session(use_gpu=False): @@ -879,6 +946,7 @@ class SparseMinimumMaximumTest(test_util.TensorFlowTestCase): self.assertAllEqual(a.values, b.values) self.assertAllEqual(a.dense_shape, b.dense_shape) + @test_util.run_deprecated_v1 def testBasic(self): with self.cached_session(use_gpu=False): # 1-D, values at index 0. @@ -898,6 +966,7 @@ class SparseMinimumMaximumTest(test_util.TensorFlowTestCase): self._assertSparseTensorValueEqual(expected.eval(), max_tf) self._assertSparseTensorValueEqual(expected.eval(), min_tf) + @test_util.run_deprecated_v1 def testRandom(self): np.random.seed(1618) shapes = [(13,), (6, 8), (1, 7, 1)] @@ -939,6 +1008,7 @@ class SparseMinimumMaximumTest(test_util.TensorFlowTestCase): class SparseTransposeTest(test.TestCase): + @test_util.run_deprecated_v1 def testTranspose(self): if np.__version__ == "1.13.0": self.skipTest("numpy 1.13.0 bug") @@ -961,16 +1031,19 @@ class SparseTransposeTest(test.TestCase): class SparsePlaceholderTest(test.TestCase): + @test_util.run_deprecated_v1 def testPlaceholder(self): foo = array_ops.sparse_placeholder(dtypes.float32, shape=(10, 47)) self.assertAllEqual([10, 47], foo.get_shape()) self.assertAllEqual([None, 2], foo.indices.get_shape().as_list()) + @test_util.run_deprecated_v1 def testPartialShapePlaceholder(self): foo = array_ops.sparse_placeholder(dtypes.float32, shape=(None, 47)) self.assertAllEqual([None, None], foo.get_shape().as_list()) self.assertAllEqual([None, 2], foo.indices.get_shape().as_list()) + @test_util.run_deprecated_v1 def testNoShapePlaceholder(self): foo = array_ops.sparse_placeholder(dtypes.float32, shape=None) self.assertAllEqual(None, foo.get_shape()) diff --git a/tensorflow/python/kernel_tests/sparse_reorder_op_test.py b/tensorflow/python/kernel_tests/sparse_reorder_op_test.py index 7b83ae5177..93fcc6a18e 100644 --- a/tensorflow/python/kernel_tests/sparse_reorder_op_test.py +++ b/tensorflow/python/kernel_tests/sparse_reorder_op_test.py @@ -22,6 +22,7 @@ import numpy as np from tensorflow.python.framework import dtypes from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import sparse_ops @@ -60,11 +61,12 @@ class SparseReorderTest(test.TestCase): input_val = self._SparseTensorValue_5x6(np.arange(6)) sp_output = sparse_ops.sparse_reorder(input_val) - output_val = sess.run(sp_output) + output_val = self.evaluate(sp_output) self.assertAllEqual(output_val.indices, input_val.indices) self.assertAllEqual(output_val.values, input_val.values) self.assertAllEqual(output_val.dense_shape, input_val.dense_shape) + @test_util.run_deprecated_v1 def testFeedAlreadyInOrder(self): with self.session(use_gpu=False) as sess: sp_input = self._SparseTensorPlaceholder() @@ -83,12 +85,13 @@ class SparseReorderTest(test.TestCase): input_val = self._SparseTensorValue_5x6(np.random.permutation(6)) sp_output = sparse_ops.sparse_reorder(input_val) - output_val = sess.run(sp_output) + output_val = self.evaluate(sp_output) self.assertAllEqual(output_val.indices, expected_output_val.indices) self.assertAllEqual(output_val.values, expected_output_val.values) self.assertAllEqual(output_val.dense_shape, expected_output_val.dense_shape) + @test_util.run_deprecated_v1 def testFeedOutOfOrder(self): expected_output_val = self._SparseTensorValue_5x6(np.arange(6)) with self.session(use_gpu=False) as sess: @@ -103,6 +106,7 @@ class SparseReorderTest(test.TestCase): self.assertAllEqual(output_val.dense_shape, expected_output_val.dense_shape) + @test_util.run_deprecated_v1 def testGradients(self): with self.session(use_gpu=False): for _ in range(5): # To test various random permutations diff --git a/tensorflow/python/kernel_tests/sparse_reshape_op_test.py b/tensorflow/python/kernel_tests/sparse_reshape_op_test.py index f7be397c33..9341228d57 100644 --- a/tensorflow/python/kernel_tests/sparse_reshape_op_test.py +++ b/tensorflow/python/kernel_tests/sparse_reshape_op_test.py @@ -22,6 +22,7 @@ import numpy as np from tensorflow.python.framework import dtypes from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import sparse_ops from tensorflow.python.platform import test @@ -64,12 +65,14 @@ class SparseReshapeTest(test.TestCase): sp_output = sparse_ops.sparse_reshape(sp_input, shape=(2, -1)) self.assertAllEqual((2, 3 * 4), sp_output.get_shape()) + @test_util.run_deprecated_v1 def testRaisesIfMoreThanOneInferredDim(self): sp_input = sparse_tensor.SparseTensor.from_value( self._SparseTensorValue_2x3x4()) with self.assertRaisesRegexp(ValueError, "At most one dimension can"): sparse_ops.sparse_reshape(sp_input, shape=(-1, 2, -1)) + @test_util.run_deprecated_v1 def testRaisesIfInferredShapeNotPossible(self): sp_input = sparse_tensor.SparseTensor.from_value( self._SparseTensorValue_2x3x4()) @@ -81,11 +84,12 @@ class SparseReshapeTest(test.TestCase): input_val = self._SparseTensorValue_5x6() sp_output = sparse_ops.sparse_reshape(input_val, [5, 6]) - output_val = sess.run(sp_output) + output_val = self.evaluate(sp_output) self.assertAllEqual(output_val.indices, input_val.indices) self.assertAllEqual(output_val.values, input_val.values) self.assertAllEqual(output_val.dense_shape, input_val.dense_shape) + @test_util.run_deprecated_v1 def testFeedSameShape(self): with self.session(use_gpu=False) as sess: sp_input = self._SparseTensorPlaceholder() @@ -97,6 +101,7 @@ class SparseReshapeTest(test.TestCase): self.assertAllEqual(output_val.values, input_val.values) self.assertAllEqual(output_val.dense_shape, input_val.dense_shape) + @test_util.run_deprecated_v1 def testWorksWellWithTfShape(self): with self.session(use_gpu=False) as sess: sp_input = self._SparseTensorPlaceholder() @@ -109,6 +114,7 @@ class SparseReshapeTest(test.TestCase): self.assertAllEqual(output_val.values, input_val.values) self.assertAllEqual(output_val.dense_shape, input_val.dense_shape) + @test_util.run_deprecated_v1 def testFeedSameShapeWithInferredDim(self): with self.session(use_gpu=False) as sess: sp_input = self._SparseTensorPlaceholder() @@ -120,6 +126,7 @@ class SparseReshapeTest(test.TestCase): self.assertAllEqual(output_val.values, input_val.values) self.assertAllEqual(output_val.dense_shape, input_val.dense_shape) + @test_util.run_deprecated_v1 def testFeedNewShapeSameRank(self): with self.session(use_gpu=False) as sess: sp_input = self._SparseTensorPlaceholder() @@ -133,6 +140,7 @@ class SparseReshapeTest(test.TestCase): self.assertAllEqual(output_val.values, input_val.values) self.assertAllEqual(output_val.dense_shape, [3, 10]) + @test_util.run_deprecated_v1 def testFeedNewShapeSameRankWithInferredDim(self): with self.session(use_gpu=False) as sess: sp_input = self._SparseTensorPlaceholder() @@ -151,13 +159,14 @@ class SparseReshapeTest(test.TestCase): input_val = self._SparseTensorValue_5x6() sp_output = sparse_ops.sparse_reshape(input_val, [2, 3, 5]) - output_val = sess.run(sp_output) + output_val = self.evaluate(sp_output) self.assertAllEqual(output_val.indices, np.array([[0, 0, 0], [0, 1, 1], [0, 1, 4], [0, 2, 0], [1, 1, 0], [1, 1, 1]])) self.assertAllEqual(output_val.values, input_val.values) self.assertAllEqual(output_val.dense_shape, [2, 3, 5]) + @test_util.run_deprecated_v1 def testFeedUpRank(self): with self.session(use_gpu=False) as sess: sp_input = self._SparseTensorPlaceholder() @@ -171,6 +180,7 @@ class SparseReshapeTest(test.TestCase): self.assertAllEqual(output_val.values, input_val.values) self.assertAllEqual(output_val.dense_shape, [2, 3, 5]) + @test_util.run_deprecated_v1 def testFeedUpRankWithInferredDim(self): with self.session(use_gpu=False) as sess: sp_input = self._SparseTensorPlaceholder() @@ -184,6 +194,7 @@ class SparseReshapeTest(test.TestCase): self.assertAllEqual(output_val.values, input_val.values) self.assertAllEqual(output_val.dense_shape, [2, 3, 5]) + @test_util.run_deprecated_v1 def testFeedDownRank(self): with self.session(use_gpu=False) as sess: sp_input = self._SparseTensorPlaceholder() @@ -197,6 +208,7 @@ class SparseReshapeTest(test.TestCase): self.assertAllEqual(output_val.values, input_val.values) self.assertAllEqual(output_val.dense_shape, [6, 4]) + @test_util.run_deprecated_v1 def testFeedDownRankWithInferredDim(self): with self.session(use_gpu=False) as sess: sp_input = self._SparseTensorPlaceholder() @@ -210,6 +222,7 @@ class SparseReshapeTest(test.TestCase): self.assertAllEqual(output_val.values, input_val.values) self.assertAllEqual(output_val.dense_shape, [6, 4]) + @test_util.run_deprecated_v1 def testFeedMultipleInferredDims(self): with self.session(use_gpu=False) as sess: sp_input = self._SparseTensorPlaceholder() @@ -218,12 +231,14 @@ class SparseReshapeTest(test.TestCase): with self.assertRaisesOpError("only one output dimension may be -1"): sess.run(sp_output, {sp_input: input_val}) + @test_util.run_deprecated_v1 def testProvideStaticallyMismatchedSizes(self): input_val = self._SparseTensorValue_5x6() sp_input = sparse_tensor.SparseTensor.from_value(input_val) with self.assertRaisesRegexp(ValueError, "Cannot reshape"): sparse_ops.sparse_reshape(sp_input, [4, 7]) + @test_util.run_deprecated_v1 def testFeedMismatchedSizes(self): with self.session(use_gpu=False) as sess: sp_input = self._SparseTensorPlaceholder() @@ -233,6 +248,7 @@ class SparseReshapeTest(test.TestCase): "Input to reshape is a tensor with 30 dense values"): sess.run(sp_output, {sp_input: input_val}) + @test_util.run_deprecated_v1 def testFeedMismatchedSizesWithInferredDim(self): with self.session(use_gpu=False) as sess: sp_input = self._SparseTensorPlaceholder() @@ -241,6 +257,7 @@ class SparseReshapeTest(test.TestCase): with self.assertRaisesOpError("requested shape requires a multiple"): sess.run(sp_output, {sp_input: input_val}) + @test_util.run_deprecated_v1 def testFeedPartialShapes(self): with self.session(use_gpu=False): # Incorporate new rank into shape information if known @@ -266,6 +283,7 @@ class SparseReshapeTest(test.TestCase): self.assertListEqual(sp_output.indices.get_shape().as_list(), [5, None]) self.assertListEqual(sp_output.dense_shape.get_shape().as_list(), [None]) + @test_util.run_deprecated_v1 def testFeedDenseReshapeSemantics(self): with self.session(use_gpu=False) as sess: # Compute a random rank-5 initial shape and new shape, randomly sparsify diff --git a/tensorflow/python/kernel_tests/sparse_serialization_ops_test.py b/tensorflow/python/kernel_tests/sparse_serialization_ops_test.py index b24a086969..5a48eb825d 100644 --- a/tensorflow/python/kernel_tests/sparse_serialization_ops_test.py +++ b/tensorflow/python/kernel_tests/sparse_serialization_ops_test.py @@ -22,6 +22,7 @@ import numpy as np from tensorflow.python.framework import dtypes from tensorflow.python.framework import sparse_tensor as sparse_tensor_lib +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import sparse_ops from tensorflow.python.platform import test @@ -73,7 +74,7 @@ class SerializeSparseTest(test.TestCase): serialized = serialize_fn(sp_input, out_type=out_type) sp_deserialized = deserialize_fn(serialized, dtype=dtypes.int32) - indices, values, shape = sess.run(sp_deserialized) + indices, values, shape = self.evaluate(sp_deserialized) self.assertAllEqual(indices, sp_input[0]) self.assertAllEqual(values, sp_input[1]) @@ -110,14 +111,17 @@ class SerializeSparseTest(test.TestCase): self.assertAllEqual(combined_values[6:], sp_input[1]) self.assertAllEqual(combined_shape, [2, 5, 6]) + @test_util.run_deprecated_v1 def testSerializeDeserializeBatch(self): self._testSerializeDeserializeBatchHelper(sparse_ops.serialize_sparse, sparse_ops.deserialize_sparse) + @test_util.run_deprecated_v1 def testSerializeDeserializeManyBatch(self): self._testSerializeDeserializeBatchHelper( sparse_ops.serialize_sparse, sparse_ops.deserialize_many_sparse) + @test_util.run_deprecated_v1 def testVariantSerializeDeserializeBatch(self): self._testSerializeDeserializeBatchHelper(sparse_ops.serialize_sparse, sparse_ops.deserialize_sparse, @@ -145,10 +149,12 @@ class SerializeSparseTest(test.TestCase): self.assertAllEqual(combined_values[6:], sp_input1[1]) self.assertAllEqual(combined_shape, [2, 5, 6]) + @test_util.run_deprecated_v1 def testSerializeDeserializeBatchInconsistentShape(self): self._testSerializeDeserializeBatchInconsistentShapeHelper( sparse_ops.serialize_sparse, sparse_ops.deserialize_sparse) + @test_util.run_deprecated_v1 def testVariantSerializeDeserializeBatchInconsistentShape(self): self._testSerializeDeserializeBatchInconsistentShapeHelper( sparse_ops.serialize_sparse, sparse_ops.deserialize_sparse, @@ -188,10 +194,12 @@ class SerializeSparseTest(test.TestCase): self.assertAllEqual(combined_shape, [2, 2, 5, 6]) + @test_util.run_deprecated_v1 def testSerializeDeserializeNestedBatch(self): self._testSerializeDeserializeNestedBatchHelper( sparse_ops.serialize_sparse, sparse_ops.deserialize_sparse) + @test_util.run_deprecated_v1 def testVariantSerializeDeserializeNestedBatch(self): self._testSerializeDeserializeNestedBatchHelper( sparse_ops.serialize_sparse, sparse_ops.deserialize_sparse, @@ -224,14 +232,17 @@ class SerializeSparseTest(test.TestCase): self.assertAllEqual(combined_values[6:], input1_val[1]) self.assertAllEqual(combined_shape, [2, 5, 6]) + @test_util.run_deprecated_v1 def testFeedSerializeDeserializeBatch(self): self._testFeedSerializeDeserializeBatchHelper(sparse_ops.serialize_sparse, sparse_ops.deserialize_sparse) + @test_util.run_deprecated_v1 def testFeedSerializeDeserializeManyBatch(self): self._testFeedSerializeDeserializeBatchHelper( sparse_ops.serialize_sparse, sparse_ops.deserialize_many_sparse) + @test_util.run_deprecated_v1 def testFeedVariantSerializeDeserializeBatch(self): self._testFeedSerializeDeserializeBatchHelper(sparse_ops.serialize_sparse, sparse_ops.deserialize_sparse, @@ -256,6 +267,7 @@ class SerializeSparseTest(test.TestCase): }) self.assertEqual(serialized_value.shape, (4, 3)) + @test_util.run_deprecated_v1 def testSerializeManyShape(self): self._testSerializeManyShapeHelper(sparse_ops.serialize_many_sparse) @@ -287,19 +299,23 @@ class SerializeSparseTest(test.TestCase): self.assertAllEqual(deserialized_value.values, values_value) self.assertAllEqual(deserialized_value.dense_shape, shape_value) + @test_util.run_deprecated_v1 def testSerializeManyDeserializeBatch(self): self._testSerializeManyDeserializeBatchHelper( sparse_ops.serialize_many_sparse, sparse_ops.deserialize_sparse) + @test_util.run_deprecated_v1 def testSerializeManyDeserializeManyBatch(self): self._testSerializeManyDeserializeBatchHelper( sparse_ops.serialize_many_sparse, sparse_ops.deserialize_many_sparse) + @test_util.run_deprecated_v1 def testVariantSerializeManyDeserializeBatch(self): self._testSerializeManyDeserializeBatchHelper( sparse_ops.serialize_many_sparse, sparse_ops.deserialize_sparse, dtypes.variant) + @test_util.run_deprecated_v1 def testVariantSerializeDeserializeScalar(self): with self.session(use_gpu=False) as sess: indices_value = np.array([[]], dtype=np.int64) @@ -321,6 +337,7 @@ class SerializeSparseTest(test.TestCase): self.assertAllEqual(deserialized_value.values, values_value) self.assertAllEqual(deserialized_value.dense_shape, shape_value) + @test_util.run_deprecated_v1 def testVariantSerializeDeserializeScalarBatch(self): with self.session(use_gpu=False) as sess: indices_value = np.array([[]], dtype=np.int64) @@ -367,14 +384,17 @@ class SerializeSparseTest(test.TestCase): {sp_input0: input0_val, sp_input1: input1_val}) + @test_util.run_deprecated_v1 def testDeserializeFailsWrongType(self): self._testDeserializeFailsWrongTypeHelper(sparse_ops.serialize_sparse, sparse_ops.deserialize_sparse) + @test_util.run_deprecated_v1 def testDeserializeManyFailsWrongType(self): self._testDeserializeFailsWrongTypeHelper( sparse_ops.serialize_sparse, sparse_ops.deserialize_many_sparse) + @test_util.run_deprecated_v1 def testVariantDeserializeFailsWrongType(self): self._testDeserializeFailsWrongTypeHelper(sparse_ops.serialize_sparse, sparse_ops.deserialize_sparse, @@ -402,14 +422,17 @@ class SerializeSparseTest(test.TestCase): {sp_input0: input0_val, sp_input1: input1_val}) + @test_util.run_deprecated_v1 def testDeserializeFailsInconsistentRank(self): self._testDeserializeFailsInconsistentRankHelper( sparse_ops.serialize_sparse, sparse_ops.deserialize_sparse) + @test_util.run_deprecated_v1 def testDeserializeManyFailsInconsistentRank(self): self._testDeserializeFailsInconsistentRankHelper( sparse_ops.serialize_sparse, sparse_ops.deserialize_many_sparse) + @test_util.run_deprecated_v1 def testVariantDeserializeFailsInconsistentRank(self): self._testDeserializeFailsInconsistentRankHelper( sparse_ops.serialize_sparse, sparse_ops.deserialize_sparse, @@ -431,10 +454,12 @@ class SerializeSparseTest(test.TestCase): with self.assertRaisesOpError(r"Could not parse serialized proto"): sess.run(sp_deserialized, {sp_input0: input0_val}) + @test_util.run_deprecated_v1 def testDeserializeFailsInvalidProto(self): self._testDeserializeFailsInvalidProtoHelper(sparse_ops.serialize_sparse, sparse_ops.deserialize_sparse) + @test_util.run_deprecated_v1 def testDeserializeManyFailsInvalidProto(self): self._testDeserializeFailsInvalidProtoHelper( sparse_ops.serialize_sparse, sparse_ops.deserialize_many_sparse) diff --git a/tensorflow/python/kernel_tests/sparse_slice_op_test.py b/tensorflow/python/kernel_tests/sparse_slice_op_test.py index 098353741f..7f8c91bde6 100644 --- a/tensorflow/python/kernel_tests/sparse_slice_op_test.py +++ b/tensorflow/python/kernel_tests/sparse_slice_op_test.py @@ -21,6 +21,7 @@ from __future__ import print_function import numpy as np from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import test_util from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import sparse_ops import tensorflow.python.ops.sparse_grad # pylint: disable=unused-import @@ -79,6 +80,7 @@ class SparseSliceOpTest(test.TestCase): return sparse_tensor.SparseTensor.from_value( self._SparseTensorValue_3x4x2()) + @test_util.run_deprecated_v1 def testSliceMatrixRows(self): with self.session(use_gpu=False): sp_input = self._SparseTensor_4x6() @@ -96,6 +98,7 @@ class SparseSliceOpTest(test.TestCase): [20, 23, 25, 30, 32, 33, 35]) self.assertAllEqual(sp_tensor1.dense_shape.eval(), [2, 6]) + @test_util.run_deprecated_v1 def testSliceMatrixUnevenCols(self): with self.session(use_gpu=False): sp_input = self._SparseTensor_5x7() @@ -137,6 +140,7 @@ class SparseSliceOpTest(test.TestCase): self.assertAllEqual(sp_tensor3.values.eval(), [16, 46]) self.assertAllEqual(sp_tensor3.dense_shape.eval(), [5, 1]) + @test_util.run_deprecated_v1 def testSliceMatrixUnevenRows(self): with self.session(use_gpu=False): sp_input = self._SparseTensor_5x7() @@ -173,6 +177,7 @@ class SparseSliceOpTest(test.TestCase): self.assertAllEqual(sp_tensor2.dense_shape.eval(), [1, 7]) return + @test_util.run_deprecated_v1 def testSliceAllRows(self): with self.session(use_gpu=False): sp_input = self._SparseTensor_4x6() @@ -195,6 +200,7 @@ class SparseSliceOpTest(test.TestCase): self.assertAllEqual(sp_tensor3.values.eval(), [30, 32, 33, 35]) self.assertAllEqual(sp_tensor3.dense_shape.eval(), [1, 6]) + @test_util.run_deprecated_v1 def testSliceColumns(self): with self.session(use_gpu=False): sp_input = self._SparseTensor_4x6() @@ -215,6 +221,7 @@ class SparseSliceOpTest(test.TestCase): self.assertAllEqual(sparse_tensor2.values.eval(), [4, 5, 14, 25, 35]) self.assertAllEqual(sparse_tensor2.dense_shape.eval(), [4, 2]) + @test_util.run_deprecated_v1 def testSliceAllColumns(self): with self.session(use_gpu=False): sp_input = self._SparseTensor_4x6() @@ -246,6 +253,7 @@ class SparseSliceOpTest(test.TestCase): self.assertAllEqual(sparse_tensor5.values.eval(), [5, 25, 35]) self.assertAllEqual(sparse_tensor5.dense_shape.eval(), [4, 1]) + @test_util.run_deprecated_v1 def testGradients(self): sp_input = self._SparseTensor_4x6(val_dtype=np.float32) start_and_size = [([0, 0], [4, 2]), diff --git a/tensorflow/python/kernel_tests/sparse_split_op_test.py b/tensorflow/python/kernel_tests/sparse_split_op_test.py index 95661ded4b..f4bb7498b0 100644 --- a/tensorflow/python/kernel_tests/sparse_split_op_test.py +++ b/tensorflow/python/kernel_tests/sparse_split_op_test.py @@ -21,6 +21,7 @@ from __future__ import print_function import numpy as np from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import test_util from tensorflow.python.ops import sparse_ops from tensorflow.python.platform import test @@ -75,6 +76,7 @@ class SparseSplitOpTest(test.TestCase): return sparse_tensor.SparseTensor.from_value(self._SparseTensorValue_3x4x2( )) + @test_util.run_deprecated_v1 def testSplitMatrixRows(self): with self.session(use_gpu=False): sp_tensors = sparse_ops.sparse_split( @@ -92,6 +94,7 @@ class SparseSplitOpTest(test.TestCase): [20, 23, 25, 30, 32, 33, 35]) self.assertAllEqual(sp_tensors[1].dense_shape.eval(), [2, 6]) + @test_util.run_deprecated_v1 def testSplitMatrixUnevenCols(self): with self.session(use_gpu=False): sp_tensors_3 = sparse_ops.sparse_split( @@ -131,6 +134,7 @@ class SparseSplitOpTest(test.TestCase): self.assertAllEqual(sp_tensors_4[3].values.eval(), [16, 46]) self.assertAllEqual(sp_tensors_4[3].dense_shape.eval(), [5, 1]) + @test_util.run_deprecated_v1 def testSplitMatrixUnevenRows(self): with self.session(use_gpu=False): sp_tensors_2 = sparse_ops.sparse_split( @@ -167,6 +171,7 @@ class SparseSplitOpTest(test.TestCase): self.assertAllEqual(sp_tensors_3[2].dense_shape.eval(), [1, 7]) return + @test_util.run_deprecated_v1 def testSplitAllRows(self): with self.session(use_gpu=False): sp_tensors = sparse_ops.sparse_split( @@ -189,6 +194,7 @@ class SparseSplitOpTest(test.TestCase): self.assertAllEqual(sp_tensors[3].values.eval(), [30, 32, 33, 35]) self.assertAllEqual(sp_tensors[3].dense_shape.eval(), [1, 6]) + @test_util.run_deprecated_v1 def testSplitColumns(self): with self.session(use_gpu=False): sparse_tensors = sparse_ops.sparse_split( @@ -207,6 +213,7 @@ class SparseSplitOpTest(test.TestCase): self.assertAllEqual(sparse_tensors[2].values.eval(), [4, 5, 14, 25, 35]) self.assertAllEqual(sparse_tensors[2].dense_shape.eval(), [4, 2]) + @test_util.run_deprecated_v1 def testSplitAllColumns(self): with self.session(use_gpu=False): sparse_tensors = sparse_ops.sparse_split( @@ -234,6 +241,7 @@ class SparseSplitOpTest(test.TestCase): self.assertAllEqual(sparse_tensors[5].values.eval(), [5, 25, 35]) self.assertAllEqual(sparse_tensors[5].dense_shape.eval(), [4, 1]) + @test_util.run_deprecated_v1 def testSliceConcat(self): for sp_input in (self._SparseTensorValue_3x4x2(), self._SparseTensor_3x4x2()): diff --git a/tensorflow/python/kernel_tests/sparse_tensor_dense_matmul_grad_test.py b/tensorflow/python/kernel_tests/sparse_tensor_dense_matmul_grad_test.py index b8f33d6a81..fa2bab1fca 100644 --- a/tensorflow/python/kernel_tests/sparse_tensor_dense_matmul_grad_test.py +++ b/tensorflow/python/kernel_tests/sparse_tensor_dense_matmul_grad_test.py @@ -22,6 +22,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import test_util from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import sparse_ops import tensorflow.python.ops.sparse_grad # pylint: disable=unused-import @@ -89,6 +90,7 @@ class SparseTensorDenseMatMulGradientTest(test.TestCase): self._testGradients(adjoint_a, adjoint_b, name, values_dtype, indices_dtype) + @test_util.run_deprecated_v1 def testGradients(self): np.random.seed(5) # Fix seed to avoid flakiness self._testGradientsType(np.float32, np.int64) diff --git a/tensorflow/python/kernel_tests/sparse_tensor_dense_matmul_op_test.py b/tensorflow/python/kernel_tests/sparse_tensor_dense_matmul_op_test.py index fe334045af..637cfaec99 100644 --- a/tensorflow/python/kernel_tests/sparse_tensor_dense_matmul_op_test.py +++ b/tensorflow/python/kernel_tests/sparse_tensor_dense_matmul_op_test.py @@ -30,6 +30,7 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import sparse_tensor from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import math_ops @@ -80,7 +81,7 @@ class SparseTensorDenseMatMulTest(test.TestCase): self.assertEqual(tf_value_ans.get_shape()[1], np_ans.shape[1]) self.assertEqual(tf_tensor_ans.get_shape()[1], np_ans.shape[1]) - for out in (tf_value_ans.eval(), tf_tensor_ans.eval()): + for out in (tf_value_ans.eval(), self.evaluate(tf_tensor_ans)): if x.dtype == np.float32: self.assertAllClose(np_ans, out, rtol=1e-4, atol=1e-4) elif x.dtype == np.float64: @@ -96,6 +97,7 @@ class SparseTensorDenseMatMulTest(test.TestCase): self._testMatmul(x, y, indices_dtype=indices_dtype) + @test_util.run_deprecated_v1 def testBasic(self): np.random.seed(127) # Repeatable results self._testBasic(np.int32) @@ -106,6 +108,7 @@ class SparseTensorDenseMatMulTest(test.TestCase): self._testBasic(np.int32, indices_dtype=np.int32) self._testBasic(np.float32, indices_dtype=np.int32) + @test_util.run_deprecated_v1 def testShapeInference(self): x = np.random.rand(10, 10) x[np.abs(x) < 0.5] = 0 # Make it sparse @@ -229,6 +232,7 @@ class SparseTensorDenseMatMulTest(test.TestCase): self._testLarge(np.complex128) # Tests random sized matrices. + @test_util.run_deprecated_v1 def testFloatRandom(self): np.random.seed(127) # Repeatable results for _ in range(8): diff --git a/tensorflow/python/kernel_tests/sparse_tensors_map_ops_test.py b/tensorflow/python/kernel_tests/sparse_tensors_map_ops_test.py index e08464a701..6039ff1afa 100644 --- a/tensorflow/python/kernel_tests/sparse_tensors_map_ops_test.py +++ b/tensorflow/python/kernel_tests/sparse_tensors_map_ops_test.py @@ -24,6 +24,7 @@ from tensorflow.python.client import session from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import sparse_tensor as sparse_tensor_lib +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import sparse_ops from tensorflow.python.ops import variables @@ -76,6 +77,7 @@ class SparseTensorsMapTest(test.TestCase): shape = np.array([3, 4, 5]).astype(np.int64) return sparse_tensor_lib.SparseTensorValue(ind, val, shape) + @test_util.run_deprecated_v1 def testAddTakeMany(self): with self.session(graph=ops.Graph(), use_gpu=False) as sess: sp_input0 = self._SparseTensorValue_5x6(np.arange(6)) @@ -88,7 +90,7 @@ class SparseTensorsMapTest(test.TestCase): sp_out = take_many_sparse_from_tensors_map( sparse_map_op=handle0.op, sparse_handles=handles_concat) - combined_indices, combined_values, combined_shape = sess.run(sp_out) + combined_indices, combined_values, combined_shape = self.evaluate(sp_out) self.assertAllEqual(combined_indices[:6, 0], [0] * 6) # minibatch 0 self.assertAllEqual(combined_indices[:6, 1:], sp_input0[0]) @@ -98,6 +100,7 @@ class SparseTensorsMapTest(test.TestCase): self.assertAllEqual(combined_values[6:], sp_input1[1]) self.assertAllEqual(combined_shape, [2, 5, 6]) + @test_util.run_deprecated_v1 def testFeedAddTakeMany(self): with self.session(use_gpu=False) as sess: sp_input = self._SparseTensorPlaceholder() @@ -114,7 +117,8 @@ class SparseTensorsMapTest(test.TestCase): sp_roundtrip = take_many_sparse_from_tensors_map( sparse_map_op=handle.op, sparse_handles=sparse_handles) - combined_indices, combined_values, combined_shape = sess.run(sp_roundtrip) + combined_indices, combined_values, combined_shape = self.evaluate( + sp_roundtrip) self.assertAllEqual(combined_indices[:6, 0], [0] * 6) # minibatch 0 self.assertAllEqual(combined_indices[:6, 1:], input0_val[0]) @@ -124,6 +128,7 @@ class SparseTensorsMapTest(test.TestCase): self.assertAllEqual(combined_values[6:], input1_val[1]) self.assertAllEqual(combined_shape, [2, 5, 6]) + @test_util.run_deprecated_v1 def testAddManyTakeManyRoundTrip(self): with self.session(use_gpu=False) as sess: # N == 4 because shape_value == [4, 5] @@ -146,6 +151,7 @@ class SparseTensorsMapTest(test.TestCase): self.assertAllEqual(roundtrip_value.values, values_value) self.assertAllEqual(roundtrip_value.dense_shape, shape_value) + @test_util.run_deprecated_v1 def testDeserializeFailsInconsistentRank(self): with self.session(use_gpu=False) as sess: sp_input = self._SparseTensorPlaceholder() @@ -165,19 +171,20 @@ class SparseTensorsMapTest(test.TestCase): with self.assertRaisesOpError( r"Inconsistent rank across SparseTensors: rank prior to " r"SparseTensor\[1\] was: 3 but rank of SparseTensor\[1\] is: 4"): - sess.run(sp_roundtrip) + self.evaluate(sp_roundtrip) + @test_util.run_deprecated_v1 def testTakeManyFailsWrongInputOp(self): with self.session(use_gpu=False) as sess: input_val = self._SparseTensorValue_5x6(np.arange(6)) handle = add_sparse_to_tensors_map(input_val) - handle_value = sess.run(handle) + handle_value = self.evaluate(handle) bad_handle = handle_value + 10 sp_roundtrip = take_many_sparse_from_tensors_map( sparse_map_op=handle.op, sparse_handles=[handle_value, bad_handle]) with self.assertRaisesOpError(r"Unable to find SparseTensor: 10"): - sess.run(sp_roundtrip) + self.evaluate(sp_roundtrip) class BenchmarkSparseTensorsMapVsSerialization(test.Benchmark): @@ -212,8 +219,8 @@ class BenchmarkSparseTensorsMapVsSerialization(test.Benchmark): variables.global_variables_initializer().run() - st_roundtrip_values = sess.run(st_roundtrip) - st_deserialized_values = sess.run(st_deserialized) + st_roundtrip_values = self.evaluate(st_roundtrip) + st_deserialized_values = self.evaluate(st_deserialized) np.testing.assert_equal(st_roundtrip_values.values, st_deserialized_values.values) np.testing.assert_equal(st_roundtrip_values.indices, diff --git a/tensorflow/python/kernel_tests/sparse_to_dense_op_py_test.py b/tensorflow/python/kernel_tests/sparse_to_dense_op_py_test.py index 7f63532e10..c6c45db4f9 100644 --- a/tensorflow/python/kernel_tests/sparse_to_dense_op_py_test.py +++ b/tensorflow/python/kernel_tests/sparse_to_dense_op_py_test.py @@ -21,6 +21,7 @@ from __future__ import print_function import numpy as np from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import sparse_ops from tensorflow.python.platform import test @@ -41,36 +42,42 @@ def _SparseToDense(sparse_indices, class SparseToDenseTest(test.TestCase): + @test_util.run_deprecated_v1 def testInt(self): with self.session(use_gpu=False): tf_ans = _SparseToDense([1, 3], [5], 1, 0).eval() np_ans = np.array([0, 1, 0, 1, 0]).astype(np.int32) self.assertAllClose(np_ans, tf_ans) + @test_util.run_deprecated_v1 def testFloat(self): with self.session(use_gpu=False): tf_ans = _SparseToDense([1, 3], [5], 1.0, 0.0).eval() np_ans = np.array([0, 1, 0, 1, 0]).astype(np.float32) self.assertAllClose(np_ans, tf_ans) + @test_util.run_deprecated_v1 def testString(self): with self.session(use_gpu=False): tf_ans = _SparseToDense([1, 3], [5], "a", "b").eval() np_ans = np.array(["b", "a", "b", "a", "b"]).astype(np.string_) self.assertAllEqual(np_ans, tf_ans) + @test_util.run_deprecated_v1 def testSetValue(self): with self.session(use_gpu=False): tf_ans = _SparseToDense([1, 3], [5], [1, 2], -1).eval() np_ans = np.array([-1, 1, -1, 2, -1]).astype(np.int32) self.assertAllClose(np_ans, tf_ans) + @test_util.run_deprecated_v1 def testSetSingleValue(self): with self.session(use_gpu=False): tf_ans = _SparseToDense([1, 3], [5], 1, -1).eval() np_ans = np.array([-1, 1, -1, 1, -1]).astype(np.int32) self.assertAllClose(np_ans, tf_ans) + @test_util.run_deprecated_v1 def test2d(self): # pylint: disable=bad-whitespace with self.session(use_gpu=False): @@ -80,11 +87,13 @@ class SparseToDenseTest(test.TestCase): [ 1, -1, -1, -1]]).astype(np.int32) self.assertAllClose(np_ans, tf_ans) + @test_util.run_deprecated_v1 def testZeroDefault(self): with self.cached_session(): x = sparse_ops.sparse_to_dense(2, [4], 7).eval() self.assertAllEqual(x, [0, 0, 7, 0]) + @test_util.run_deprecated_v1 def test3d(self): with self.session(use_gpu=False): tf_ans = _SparseToDense([[1, 3, 0], [2, 0, 1]], [3, 4, 2], 1, -1).eval() @@ -93,32 +102,37 @@ class SparseToDenseTest(test.TestCase): np_ans[2, 0, 1] = 1 self.assertAllClose(np_ans, tf_ans) + @test_util.run_deprecated_v1 def testBadShape(self): with self.cached_session(): with self.assertRaisesWithPredicateMatch(ValueError, "must be rank 1"): _SparseToDense([1, 3], [[5], [3]], 1, -1) + @test_util.run_deprecated_v1 def testBadValue(self): with self.cached_session(): dense = _SparseToDense([1, 3], [5], [[5], [3]], -1) with self.assertRaisesOpError( r"sparse_values has incorrect shape \[2,1\], " r"should be \[\] or \[2\]"): - dense.eval() + self.evaluate(dense) + @test_util.run_deprecated_v1 def testBadNumValues(self): with self.cached_session(): dense = _SparseToDense([1, 3], [5], [1, 2, 3], -1) with self.assertRaisesOpError( r"sparse_values has incorrect shape \[3\], should be \[\] or \[2\]"): - dense.eval() + self.evaluate(dense) + @test_util.run_deprecated_v1 def testBadDefault(self): with self.cached_session(): dense = _SparseToDense([1, 3], [5], [1, 2], [0]) with self.assertRaisesOpError("default_value should be a scalar"): - dense.eval() + self.evaluate(dense) + @test_util.run_deprecated_v1 def testOutOfBoundsIndicesWithWithoutValidation(self): with self.cached_session(): dense = _SparseToDense( @@ -128,7 +142,7 @@ class SparseToDenseTest(test.TestCase): default_value=0.0) with self.assertRaisesOpError( r"indices\[1\] = \[10\] is out of bounds: need 0 <= index < \[5\]"): - dense.eval() + self.evaluate(dense) # Disable checks, the allocation should still fail. with self.assertRaisesOpError("out of bounds"): dense_without_validation = _SparseToDense( @@ -137,8 +151,9 @@ class SparseToDenseTest(test.TestCase): sparse_values=[-1.0, 1.0], default_value=0.0, validate_indices=False) - dense_without_validation.eval() + self.evaluate(dense_without_validation) + @test_util.run_deprecated_v1 def testRepeatingIndicesWithWithoutValidation(self): with self.cached_session(): dense = _SparseToDense( @@ -147,7 +162,7 @@ class SparseToDenseTest(test.TestCase): sparse_values=[-1.0, 1.0], default_value=0.0) with self.assertRaisesOpError(r"indices\[1\] = \[1\] is repeated"): - dense.eval() + self.evaluate(dense) # Disable checks dense_without_validation = _SparseToDense( sparse_indices=[[1], [1]], @@ -155,8 +170,9 @@ class SparseToDenseTest(test.TestCase): sparse_values=[-1.0, 1.0], default_value=0.0, validate_indices=False) - dense_without_validation.eval() + self.evaluate(dense_without_validation) + @test_util.run_deprecated_v1 def testUnsortedIndicesWithWithoutValidation(self): with self.cached_session(): dense = _SparseToDense( @@ -165,7 +181,7 @@ class SparseToDenseTest(test.TestCase): sparse_values=[-1.0, 1.0], default_value=0.0) with self.assertRaisesOpError(r"indices\[1\] = \[1\] is out of order"): - dense.eval() + self.evaluate(dense) # Disable checks dense_without_validation = _SparseToDense( sparse_indices=[[2], [1]], @@ -173,8 +189,9 @@ class SparseToDenseTest(test.TestCase): sparse_values=[-1.0, 1.0], default_value=0.0, validate_indices=False) - dense_without_validation.eval() + self.evaluate(dense_without_validation) + @test_util.run_deprecated_v1 def testShapeInferenceKnownShape(self): with self.session(use_gpu=False): indices = array_ops.placeholder(dtypes.int64) @@ -187,6 +204,7 @@ class SparseToDenseTest(test.TestCase): output = sparse_ops.sparse_to_dense(indices, shape, 1, 0) self.assertEqual(output.get_shape().as_list(), [None, None, None]) + @test_util.run_deprecated_v1 def testShapeInferenceUnknownShape(self): with self.session(use_gpu=False): indices = array_ops.placeholder(dtypes.int64) diff --git a/tensorflow/python/kernel_tests/sparse_xent_op_test.py b/tensorflow/python/kernel_tests/sparse_xent_op_test.py index 0510bc5321..8f0842f7f5 100644 --- a/tensorflow/python/kernel_tests/sparse_xent_op_test.py +++ b/tensorflow/python/kernel_tests/sparse_xent_op_test.py @@ -29,6 +29,7 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors_impl from tensorflow.python.framework import ops as ops_lib +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gen_nn_ops from tensorflow.python.ops import gradient_checker @@ -66,7 +67,7 @@ class SparseXentTest(test.TestCase): with self.cached_session(use_gpu=True) as sess: loss, backprop = gen_nn_ops.sparse_softmax_cross_entropy_with_logits( np_features, np_labels) - tf_loss, tf_backprop = sess.run([loss, backprop]) + tf_loss, tf_backprop = self.evaluate([loss, backprop]) self.assertAllCloseAccordingToType(np_loss, tf_loss) self.assertAllCloseAccordingToType(np_backprop, tf_backprop) @@ -76,10 +77,11 @@ class SparseXentTest(test.TestCase): loss, backprop = gen_nn_ops.sparse_softmax_cross_entropy_with_logits( np.array([[1.], [-1.], [0.]]).astype(np.float32), np.array([0, 0, 0]).astype(label_dtype)) - tf_loss, tf_backprop = sess.run([loss, backprop]) + tf_loss, tf_backprop = self.evaluate([loss, backprop]) self.assertAllClose([0.0, 0.0, 0.0], tf_loss) self.assertAllClose([[0.0], [0.0], [0.0]], tf_backprop) + @test_util.run_deprecated_v1 def testInvalidLabel(self): features = [[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 2., 3., 4.], [1., 2., 3., 4.]] @@ -90,7 +92,7 @@ class SparseXentTest(test.TestCase): loss, backprop = ( gen_nn_ops.sparse_softmax_cross_entropy_with_logits( features, labels)) - tf_loss, tf_backprop = sess.run([loss, backprop]) + tf_loss, tf_backprop = self.evaluate([loss, backprop]) self.assertAllClose( [[np.nan] * 4, [0.25, 0.25, 0.25, -0.75], [-0.968, 0.087, 0.237, 0.6439], [np.nan] * 4], @@ -104,7 +106,7 @@ class SparseXentTest(test.TestCase): loss, backprop = ( gen_nn_ops.sparse_softmax_cross_entropy_with_logits(features, labels)) with self.assertRaisesOpError("Received a label value of"): - sess.run([loss, backprop]) + self.evaluate([loss, backprop]) def testNpXent(self): # We create 2 batches of logits for testing. @@ -152,6 +154,7 @@ class SparseXentTest(test.TestCase): nn_ops.sparse_softmax_cross_entropy_with_logits( labels=constant_op.constant(0), logits=constant_op.constant(1.0)) + @test_util.run_deprecated_v1 def testLabelsPlaceholderScalar(self): with self.session(use_gpu=True): labels = array_ops.placeholder(np.int32) @@ -164,7 +167,7 @@ class SparseXentTest(test.TestCase): with self.session(use_gpu=True): loss = nn_ops.sparse_softmax_cross_entropy_with_logits( labels=constant_op.constant(0), logits=constant_op.constant([1.0])) - self.assertAllClose(0.0, loss.eval()) + self.assertAllClose(0.0, self.evaluate(loss)) def testFloat(self): for label_dtype in np.int32, np.int64: @@ -187,6 +190,7 @@ class SparseXentTest(test.TestCase): def testEmpty(self): self._testXent(np.zeros((0, 3)), np.zeros((0,), dtype=np.int32)) + @test_util.run_deprecated_v1 def testGradient(self): with self.session(use_gpu=True): l = constant_op.constant([3, 0, 1], name="l") @@ -201,6 +205,7 @@ class SparseXentTest(test.TestCase): print("cross entropy gradient err = ", err) self.assertLess(err, 5e-8) + @test_util.run_deprecated_v1 def testSecondGradient(self): images_placeholder = array_ops.placeholder(dtypes.float32, shape=(3, 2)) labels_placeholder = array_ops.placeholder(dtypes.int32, shape=(3)) @@ -226,21 +231,24 @@ class SparseXentTest(test.TestCase): loss = nn_ops.sparse_softmax_cross_entropy_with_logits( labels=labels, logits=features) backprop = loss.op.inputs[0].op.outputs[1] - tf_loss, tf_backprop = sess.run([loss, backprop]) + tf_loss, tf_backprop = self.evaluate([loss, backprop]) self.assertAllCloseAccordingToType(np_loss, tf_loss) self.assertAllCloseAccordingToType(np_backprop, tf_backprop) + @test_util.run_deprecated_v1 def testHighDim(self): features = [[[1., 1., 1., 1.]], [[1., 2., 3., 4.]]] labels = [[3], [0]] self._testHighDim(features, labels) + @test_util.run_deprecated_v1 def testHighDim2(self): features = [[[1., 1., 1., 1.], [2., 2., 2., 2.]], [[1., 2., 3., 4.], [5., 6., 7., 8.]]] labels = [[3, 2], [0, 3]] self._testHighDim(features, labels) + @test_util.run_deprecated_v1 def testScalarHandling(self): with self.session(use_gpu=False) as sess: with self.assertRaisesRegexp(errors_impl.InvalidArgumentError, @@ -318,7 +326,7 @@ def sparse_vs_dense_xent_benchmark(batch_size, num_entries, use_gpu): # Using sparse_softmax_cross_entropy_with_logits with session.Session(config=config) as sess: if not use_gpu: - with ops_lib.device("/cpu:0"): + with test_util.device("/cpu:0"): ops = _sparse_vs_dense_xent_benchmark_sparse(labels, logits) else: ops = _sparse_vs_dense_xent_benchmark_sparse(labels, logits) diff --git a/tensorflow/python/kernel_tests/sparsemask_op_test.py b/tensorflow/python/kernel_tests/sparsemask_op_test.py index 6f5dd45b61..b1cd0227bc 100644 --- a/tensorflow/python/kernel_tests/sparsemask_op_test.py +++ b/tensorflow/python/kernel_tests/sparsemask_op_test.py @@ -20,12 +20,14 @@ from __future__ import print_function import numpy as np from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.platform import test class SparseMaskTest(test.TestCase): + @test_util.run_deprecated_v1 def testBasic(self): values = np.random.rand(4, 4).astype(np.single) indices = np.array([0, 2, 3, 4], dtype=np.int32) diff --git a/tensorflow/python/kernel_tests/split_op_test.py b/tensorflow/python/kernel_tests/split_op_test.py index 944b0e59b1..517db3450f 100644 --- a/tensorflow/python/kernel_tests/split_op_test.py +++ b/tensorflow/python/kernel_tests/split_op_test.py @@ -42,6 +42,7 @@ class SplitOpTest(test.TestCase): data -= 1j * data return data + @test_util.run_deprecated_v1 def testShapeInference(self): model_input = array_ops.placeholder(dtypes.float32, shape=(1, 10)) @@ -85,6 +86,7 @@ class SplitOpTest(test.TestCase): with self.cached_session(use_gpu=True) as sess: sess.run(result, feed_dict={model_input2: np.ones([4, 2])}) + @test_util.run_deprecated_v1 def testFailWithoutExplicitNum(self): size_splits = array_ops.placeholder(dtype=dtypes.int32, shape=[None]) @@ -209,6 +211,7 @@ class SplitOpTest(test.TestCase): self.assertAllEqual(result[:, 0:1], inp_grads[0]) self.assertAllEqual(result[:, 1:4], inp_grads[1]) + @test_util.run_deprecated_v1 def testOutputShape(self): for axis in [1, -1]: with self.cached_session(use_gpu=True): @@ -318,15 +321,17 @@ class SplitOpTest(test.TestCase): inp_grads = [self._makeData((4, 1), dtype)for _ in range(4)] grad_tensors = [constant_op.constant(x) for x in inp_grads] grad = gradients_impl.gradients(s, [inp_tensor], grad_tensors)[0] - result = grad.eval() + result = self.evaluate(grad) for i in range(4): self.assertAllEqual(result[:, i:i + 1], inp_grads[i]) + @test_util.run_deprecated_v1 def testGradientsAll(self): for dtype in _TEST_DTYPES: self._testGradientsSimple(dtype) self._testGradientsSimpleVariable(dtype) + @test_util.run_deprecated_v1 def testShapeFunctionEdgeCases(self): # split_dim greater than rank of input. with self.assertRaises(ValueError): @@ -356,6 +361,7 @@ class SplitOpTest(test.TestCase): for s in splits: self.assertEqual(None, s.get_shape().ndims) + @test_util.run_deprecated_v1 def testVariableShapeFunction(self): # size_splits too big with self.assertRaises(ValueError): @@ -366,6 +372,7 @@ class SplitOpTest(test.TestCase): assert s0.shape.as_list() == [2] assert s1.shape.as_list() == [1] + @test_util.run_deprecated_v1 def testNonexistentDimTensor(self): x = array_ops.placeholder(dtypes.int32) values = np.zeros([5, 30]) diff --git a/tensorflow/python/kernel_tests/stack_op_test.py b/tensorflow/python/kernel_tests/stack_op_test.py index 4b355620bf..ca3357a0ed 100644 --- a/tensorflow/python/kernel_tests/stack_op_test.py +++ b/tensorflow/python/kernel_tests/stack_op_test.py @@ -24,6 +24,7 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors_impl from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import variables @@ -41,6 +42,7 @@ def np_split_squeeze(array, axis): class StackOpTest(test.TestCase): + @test_util.run_deprecated_v1 def testSimple(self): np.random.seed(7) with self.session(use_gpu=True): @@ -54,6 +56,7 @@ class StackOpTest(test.TestCase): c = array_ops.stack(xs) self.assertAllEqual(c.eval(), data) + @test_util.run_deprecated_v1 def testSimpleParallelCPU(self): np.random.seed(7) with self.session(use_gpu=False): @@ -63,6 +66,7 @@ class StackOpTest(test.TestCase): c = array_ops.parallel_stack(xs) self.assertAllEqual(c.eval(), data) + @test_util.run_deprecated_v1 def testSimpleParallelGPU(self): np.random.seed(7) with self.session(use_gpu=True): @@ -72,6 +76,7 @@ class StackOpTest(test.TestCase): c = array_ops.parallel_stack(xs) self.assertAllEqual(c.eval(), data) + @test_util.run_deprecated_v1 def testConst(self): np.random.seed(7) with self.session(use_gpu=True): @@ -96,6 +101,7 @@ class StackOpTest(test.TestCase): b = array_ops.reshape(a, array_ops.stack([2, 3])) self.assertAllEqual(b.get_shape(), [2, 3]) + @test_util.run_deprecated_v1 def testConstParallelCPU(self): np.random.seed(7) with self.session(use_gpu=False): @@ -110,6 +116,7 @@ class StackOpTest(test.TestCase): c = array_ops.parallel_stack(data) self.assertAllEqual(c.eval(), data) + @test_util.run_deprecated_v1 def testConstParallelGPU(self): np.random.seed(7) with self.session(use_gpu=True): @@ -124,6 +131,7 @@ class StackOpTest(test.TestCase): c = array_ops.parallel_stack(data) self.assertAllEqual(c.eval(), data) + @test_util.run_deprecated_v1 def testGradientsAxis0(self): np.random.seed(7) for shape in (2,), (3,), (2, 3), (3, 2), (4, 3, 2): @@ -136,6 +144,7 @@ class StackOpTest(test.TestCase): err = gradient_checker.compute_gradient_error(xs, shapes, c, shape) self.assertLess(err, 1e-6) + @test_util.run_deprecated_v1 def testGradientsAxis1(self): np.random.seed(7) for shape in (2, 3), (3, 2), (4, 3, 2): @@ -150,6 +159,7 @@ class StackOpTest(test.TestCase): err = gradient_checker.compute_gradient_error(xs, shapes, c, out_shape) self.assertLess(err, 1e-6) + @test_util.run_deprecated_v1 def testZeroSizeCPU(self): # Verify that stack doesn't crash for zero size inputs with self.session(use_gpu=False): @@ -161,6 +171,7 @@ class StackOpTest(test.TestCase): p = array_ops.parallel_stack(list(x)).eval() self.assertAllEqual(p, x) + @test_util.run_deprecated_v1 def testZeroSizeGPU(self): # Verify that stack doesn't crash for zero size inputs with self.session(use_gpu=True): @@ -172,6 +183,7 @@ class StackOpTest(test.TestCase): p = array_ops.parallel_stack(list(x)).eval() self.assertAllEqual(p, x) + @test_util.run_deprecated_v1 def testAxis0DefaultCPU(self): with self.session(use_gpu=False): t = [constant_op.constant([1, 2, 3]), constant_op.constant([4, 5, 6])] @@ -182,6 +194,7 @@ class StackOpTest(test.TestCase): self.assertAllEqual(stacked, expected) self.assertAllEqual(parallel_stacked, expected) + @test_util.run_deprecated_v1 def testAxis0DefaultGPU(self): with self.session(use_gpu=True): t = [constant_op.constant([1, 2, 3]), constant_op.constant([4, 5, 6])] @@ -204,11 +217,11 @@ class StackOpTest(test.TestCase): with self.cached_session(use_gpu=True): actual_pack = array_ops.stack(test_arrays, axis=j) self.assertEqual(expected.shape, actual_pack.get_shape()) - actual_pack = actual_pack.eval() + actual_pack = self.evaluate(actual_pack) actual_stack = array_ops.stack(test_arrays, axis=j) self.assertEqual(expected.shape, actual_stack.get_shape()) - actual_stack = actual_stack.eval() + actual_stack = self.evaluate(actual_stack) self.assertNDArrayNear(expected, actual_stack, 1e-6) @@ -225,6 +238,7 @@ class StackOpTest(test.TestCase): class AutomaticStackingTest(test.TestCase): + @test_util.run_deprecated_v1 def testSimple(self): with self.session(use_gpu=True): self.assertAllEqual( @@ -253,17 +267,20 @@ class AutomaticStackingTest(test.TestCase): [[2., 2.], [3., 3.]], dtype=np.float32)]) self.assertAllEqual([[[0., 0.], [1., 1.]], [[2., 2.], [3., 3.]]], - result.eval()) + self.evaluate(result)) + @test_util.run_deprecated_v1 def testVariable(self): with self.session(use_gpu=True): v = variables.Variable(17) result = ops.convert_to_tensor([[0, 0, 0], [0, v, 0], [0, 0, 0]]) v.initializer.run() - self.assertAllEqual([[0, 0, 0], [0, 17, 0], [0, 0, 0]], result.eval()) + self.assertAllEqual([[0, 0, 0], [0, 17, 0], [0, 0, 0]], + self.evaluate(result)) v.assign(38).op.run() - self.assertAllEqual([[0, 0, 0], [0, 38, 0], [0, 0, 0]], result.eval()) + self.assertAllEqual([[0, 0, 0], [0, 38, 0], [0, 0, 0]], + self.evaluate(result)) def testDtype(self): t_0 = ops.convert_to_tensor([[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]]) @@ -306,6 +323,7 @@ class AutomaticStackingTest(test.TestCase): t_2 = ops.convert_to_tensor([t_0, t_0, t_1], dtype=dtypes.float64) self.assertEqual(dtypes.float64, t_2.dtype) + @test_util.run_deprecated_v1 def testPlaceholder(self): with self.session(use_gpu=True): # Test using placeholder with a defined shape. @@ -324,6 +342,7 @@ class AutomaticStackingTest(test.TestCase): self.assertAllEqual( [[0, 0, 0], [0, 2, 0], [0, 0, 0]], result_1.eval(feed_dict={ph_1: 2})) + @test_util.run_deprecated_v1 def testShapeErrors(self): # Static shape error. ph_0 = array_ops.placeholder(dtypes.int32, shape=[1]) diff --git a/tensorflow/python/kernel_tests/stack_ops_test.py b/tensorflow/python/kernel_tests/stack_ops_test.py index 1aa12009ea..d50f3f4680 100644 --- a/tensorflow/python/kernel_tests/stack_ops_test.py +++ b/tensorflow/python/kernel_tests/stack_ops_test.py @@ -24,6 +24,7 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import gen_data_flow_ops from tensorflow.python.ops import math_ops @@ -39,8 +40,9 @@ class StackOpTest(test.TestCase): c = gen_data_flow_ops.stack_push_v2(h, [[4.0, 5.0]]) with ops.control_dependencies([c]): c1 = gen_data_flow_ops.stack_pop_v2(h, dtypes.float32) - self.assertAllClose([[4.0, 5.0]], c1.eval()) + self.assertAllClose([[4.0, 5.0]], self.evaluate(c1)) + @test_util.run_deprecated_v1 def testStackPushPop(self): self._testStackPushPop(use_gpu=False) self._testStackPushPop(use_gpu=True) @@ -54,8 +56,9 @@ class StackOpTest(test.TestCase): c = gen_data_flow_ops.stack_push_v2(h, x, swap_memory=True) with ops.control_dependencies([c]): c1 = gen_data_flow_ops.stack_pop_v2(h, dtypes.float32) - self.assertAllClose(a, c1.eval()) + self.assertAllClose(a, self.evaluate(c1)) + @test_util.run_deprecated_v1 def testStackPushPopSwap(self): self._testStackPushPopSwap(use_gpu=False) self._testStackPushPopSwap(use_gpu=True) @@ -91,8 +94,9 @@ class StackOpTest(test.TestCase): _, ry = control_flow_ops.while_loop( c1, b1, [r, v], [r.get_shape(), tensor_shape.unknown_shape()]) - self.assertAllClose(np.ones(2000) * 10.0, ry.eval()) + self.assertAllClose(np.ones(2000) * 10.0, self.evaluate(ry)) + @test_util.run_deprecated_v1 def testStackWhileSwap(self): self._testStackWhileSwap(use_gpu=False) self._testStackWhileSwap(use_gpu=True) @@ -110,8 +114,9 @@ class StackOpTest(test.TestCase): with ops.control_dependencies([c2]): c2 = gen_data_flow_ops.stack_pop_v2(h2, dtypes.float32) r = c1 + c2 - self.assertAllClose(9.0, r.eval()) + self.assertAllClose(9.0, self.evaluate(r)) + @test_util.run_deprecated_v1 def testMultiStack(self): self._testMultiStack(use_gpu=False) self._testMultiStack(use_gpu=True) @@ -131,10 +136,11 @@ class StackOpTest(test.TestCase): pop1 = gen_data_flow_ops.stack_pop_v2(h1, dtypes.float32) pop2 = gen_data_flow_ops.stack_pop_v2(h2, dtypes.float32) - out1, out2 = sess.run([pop1, pop2]) + out1, out2 = self.evaluate([pop1, pop2]) self.assertAllClose(out1, 4.0) self.assertAllClose(out2, 5.0) + @test_util.run_deprecated_v1 def testSameNameStacks(self): self._testSameNameStacks(use_gpu=False) self._testSameNameStacks(use_gpu=True) @@ -144,8 +150,9 @@ class StackOpTest(test.TestCase): h = gen_data_flow_ops.stack_v2( -1, elem_type=dtypes.float32, stack_name="foo") c1 = gen_data_flow_ops.stack_close_v2(h) - sess.run(c1) + self.evaluate(c1) + @test_util.run_deprecated_v1 def testCloseStack(self): self._testCloseStack(use_gpu=False) self._testCloseStack(use_gpu=True) @@ -157,8 +164,9 @@ class StackOpTest(test.TestCase): c = gen_data_flow_ops.stack_push_v2(h, [[4.0, 5.0]]) with ops.control_dependencies([c]): c1 = gen_data_flow_ops.stack_close_v2(h) - sess.run(c1) + self.evaluate(c1) + @test_util.run_deprecated_v1 def testPushCloseStack(self): self._testPushCloseStack(use_gpu=False) self._testPushCloseStack(use_gpu=True) @@ -173,8 +181,9 @@ class StackOpRefTest(test.TestCase): c = gen_data_flow_ops.stack_push(h, [[4.0, 5.0]]) with ops.control_dependencies([c]): c1 = gen_data_flow_ops.stack_pop(h, dtypes.float32) - self.assertAllClose([[4.0, 5.0]], c1.eval()) + self.assertAllClose([[4.0, 5.0]], self.evaluate(c1)) + @test_util.run_deprecated_v1 def testStackPushPop(self): self._testStackPushPop(use_gpu=False) self._testStackPushPop(use_gpu=True) @@ -187,8 +196,9 @@ class StackOpRefTest(test.TestCase): c = gen_data_flow_ops.stack_push(h, x, swap_memory=True) with ops.control_dependencies([c]): c1 = gen_data_flow_ops.stack_pop(h, dtypes.float32) - self.assertAllClose(a, c1.eval()) + self.assertAllClose(a, self.evaluate(c1)) + @test_util.run_deprecated_v1 def testStackPushPopSwap(self): self._testStackPushPopSwap(use_gpu=False) self._testStackPushPopSwap(use_gpu=True) @@ -204,7 +214,7 @@ class StackOpRefTest(test.TestCase): with ops.control_dependencies([c2]): c2 = gen_data_flow_ops.stack_pop(h2, dtypes.float32) r = c1 + c2 - self.assertAllClose(9.0, r.eval()) + self.assertAllClose(9.0, self.evaluate(r)) def _testStackWhileSwap(self, use_gpu): with self.cached_session(use_gpu=use_gpu): @@ -236,12 +246,14 @@ class StackOpRefTest(test.TestCase): _, ry = control_flow_ops.while_loop( c1, b1, [r, v], [r.get_shape(), tensor_shape.unknown_shape()]) - self.assertAllClose(np.ones(2000) * 10.0, ry.eval()) + self.assertAllClose(np.ones(2000) * 10.0, self.evaluate(ry)) + @test_util.run_deprecated_v1 def testStackWhileSwap(self): self._testStackWhileSwap(use_gpu=False) self._testStackWhileSwap(use_gpu=True) + @test_util.run_deprecated_v1 def testMultiStack(self): self._testMultiStack(use_gpu=False) self._testMultiStack(use_gpu=True) @@ -253,8 +265,9 @@ class StackOpRefTest(test.TestCase): h2 = gen_data_flow_ops._stack(dtypes.float32, stack_name="foo") c2 = gen_data_flow_ops.stack_push(h2, 5.0) _ = c1 + c2 - self.assertNotEqual(h1.eval()[1], h2.eval()[1]) + self.assertNotEqual(h1.eval()[1], self.evaluate(h2)[1]) + @test_util.run_deprecated_v1 def testSameNameStacks(self): self._testSameNameStacks(use_gpu=False) self._testSameNameStacks(use_gpu=True) @@ -263,8 +276,9 @@ class StackOpRefTest(test.TestCase): with self.cached_session(use_gpu=use_gpu) as sess: h = gen_data_flow_ops._stack(dtypes.float32, stack_name="foo") c1 = gen_data_flow_ops.stack_close(h) - sess.run(c1) + self.evaluate(c1) + @test_util.run_deprecated_v1 def testCloseStack(self): self._testCloseStack(use_gpu=False) self._testCloseStack(use_gpu=True) @@ -275,8 +289,9 @@ class StackOpRefTest(test.TestCase): c = gen_data_flow_ops.stack_push(h, [[4.0, 5.0]]) with ops.control_dependencies([c]): c1 = gen_data_flow_ops.stack_close(h) - sess.run(c1) + self.evaluate(c1) + @test_util.run_deprecated_v1 def testPushCloseStack(self): self._testPushCloseStack(use_gpu=False) self._testPushCloseStack(use_gpu=True) diff --git a/tensorflow/python/kernel_tests/stage_op_test.py b/tensorflow/python/kernel_tests/stage_op_test.py index b814843b86..83e06ba48b 100644 --- a/tensorflow/python/kernel_tests/stage_op_test.py +++ b/tensorflow/python/kernel_tests/stage_op_test.py @@ -18,6 +18,7 @@ from __future__ import print_function from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import data_flow_ops from tensorflow.python.ops import math_ops @@ -28,6 +29,7 @@ TIMEOUT = 1 class StageTest(test.TestCase): + @test_util.run_deprecated_v1 def testSimple(self): with ops.Graph().as_default() as G: with ops.device('/cpu:0'): @@ -47,6 +49,7 @@ class StageTest(test.TestCase): _, yval = sess.run([stage, y], feed_dict={x: i}) self.assertAllClose(4 * (i - 1) * (i - 1) * 128, yval, rtol=1e-4) + @test_util.run_deprecated_v1 def testMultiple(self): with ops.Graph().as_default() as G: with ops.device('/cpu:0'): @@ -67,6 +70,7 @@ class StageTest(test.TestCase): self.assertAllClose( 4 * (i - 1) * (i - 1) * (i - 1) * 128, yval, rtol=1e-4) + @test_util.run_deprecated_v1 def testDictionary(self): with ops.Graph().as_default() as G: with ops.device('/cpu:0'): @@ -110,6 +114,7 @@ class StageTest(test.TestCase): G.finalize() + @test_util.run_deprecated_v1 def testPeek(self): with ops.Graph().as_default() as G: with ops.device('/cpu:0'): @@ -133,6 +138,7 @@ class StageTest(test.TestCase): for i in range(10): self.assertTrue(sess.run(peek, feed_dict={p: i}) == [i]) + @test_util.run_deprecated_v1 def testSizeAndClear(self): with ops.Graph().as_default() as G: with ops.device('/cpu:0'): @@ -158,6 +164,7 @@ class StageTest(test.TestCase): sess.run(clear) self.assertEqual(sess.run(size), 0) + @test_util.run_deprecated_v1 def testCapacity(self): capacity = 3 @@ -219,6 +226,7 @@ class StageTest(test.TestCase): # It should now be empty self.assertTrue(sess.run(size) == 0) + @test_util.run_deprecated_v1 def testMemoryLimit(self): memory_limit = 512 * 1024 # 512K chunk = 200 * 1024 # 256K diff --git a/tensorflow/python/kernel_tests/string_join_op_test.py b/tensorflow/python/kernel_tests/string_join_op_test.py index e4371ab5b9..2548e8695f 100644 --- a/tensorflow/python/kernel_tests/string_join_op_test.py +++ b/tensorflow/python/kernel_tests/string_join_op_test.py @@ -17,12 +17,14 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +from tensorflow.python.framework import test_util from tensorflow.python.ops import string_ops from tensorflow.python.platform import test class StringJoinOpTest(test.TestCase): + @test_util.run_deprecated_v1 def testStringJoin(self): input0 = ["a", "b"] input1 = "a" diff --git a/tensorflow/python/kernel_tests/string_length_op_test.py b/tensorflow/python/kernel_tests/string_length_op_test.py index 57db7302b1..bfa6ac2454 100644 --- a/tensorflow/python/kernel_tests/string_length_op_test.py +++ b/tensorflow/python/kernel_tests/string_length_op_test.py @@ -18,6 +18,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +from tensorflow.python.framework import test_util from tensorflow.python.ops import string_ops from tensorflow.python.platform import test @@ -29,9 +30,10 @@ class StringLengthOpTest(test.TestCase): with self.cached_session() as sess: lengths = string_ops.string_length(strings) - values = sess.run(lengths) + values = self.evaluate(lengths) self.assertAllEqual(values, [[[1, 2], [3, 4], [5, 6]]]) + @test_util.run_deprecated_v1 def testUnit(self): unicode_strings = [u"H\xc3llo", u"\U0001f604"] utf8_strings = [s.encode("utf-8") for s in unicode_strings] @@ -43,14 +45,15 @@ class StringLengthOpTest(test.TestCase): utf8_char_lengths = string_ops.string_length( utf8_strings, unit="UTF8_CHAR") self.assertAllEqual( - sess.run(utf8_byte_lengths), expected_utf8_byte_lengths) + self.evaluate(utf8_byte_lengths), expected_utf8_byte_lengths) self.assertAllEqual( - sess.run(utf8_char_lengths), expected_utf8_char_lengths) + self.evaluate(utf8_char_lengths), expected_utf8_char_lengths) with self.assertRaisesRegexp( ValueError, "Attr 'unit' of 'StringLength' Op passed string 'XYZ' " 'not in: "BYTE", "UTF8_CHAR"'): string_ops.string_length(utf8_strings, unit="XYZ") + @test_util.run_deprecated_v1 def testLegacyPositionalName(self): # Code that predates the 'unit' parameter may have used a positional # argument for the 'name' parameter. Check that we don't break such code. diff --git a/tensorflow/python/kernel_tests/string_split_op_test.py b/tensorflow/python/kernel_tests/string_split_op_test.py index b968e885ed..0c91deb522 100644 --- a/tensorflow/python/kernel_tests/string_split_op_test.py +++ b/tensorflow/python/kernel_tests/string_split_op_test.py @@ -22,6 +22,7 @@ import numpy as np from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors_impl +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import string_ops from tensorflow.python.platform import test @@ -34,17 +35,18 @@ class StringSplitOpTest(test.TestCase): with self.cached_session() as sess: tokens = string_ops.string_split(strings) - indices, values, shape = sess.run(tokens) + indices, values, shape = self.evaluate(tokens) self.assertAllEqual(indices, [[0, 0], [0, 1], [0, 2], [0, 3], [1, 0]]) self.assertAllEqual(values, [b"pigs", b"on", b"the", b"wing", b"animals"]) self.assertAllEqual(shape, [2, 4]) + @test_util.run_deprecated_v1 def testStringSplitEmptyDelimiter(self): strings = ["hello", "hola", b"\xF0\x9F\x98\x8E"] # Last string is U+1F60E with self.cached_session() as sess: tokens = string_ops.string_split(strings, delimiter="") - indices, values, shape = sess.run(tokens) + indices, values, shape = self.evaluate(tokens) self.assertAllEqual(indices, [[0, 0], [0, 1], [0, 2], [0, 3], [0, 4], [1, 0], [1, 1], [1, 2], [1, 3], [2, 0], [2, 1], [2, 2], [2, 3]]) @@ -62,7 +64,7 @@ class StringSplitOpTest(test.TestCase): with self.cached_session() as sess: tokens = string_ops.string_split(strings) - indices, values, shape = sess.run(tokens) + indices, values, shape = self.evaluate(tokens) self.assertAllEqual( indices, [[1, 0], [2, 0], [3, 0], [5, 0], [6, 0], [7, 0], [8, 0]]) @@ -74,13 +76,14 @@ class StringSplitOpTest(test.TestCase): with self.cached_session() as sess: tokens = string_ops.string_split(strings, delimiter=" .") - indices, values, shape = sess.run(tokens) + indices, values, shape = self.evaluate(tokens) self.assertAllEqual( indices, [[1, 0], [2, 0], [3, 0], [5, 0], [6, 0], [7, 0], [8, 0]]) self.assertAllEqual(values, [b"a", b"b", b"c", b"d", b"e", b"f", b"g"]) self.assertAllEqual(shape, [10, 1]) + @test_util.run_deprecated_v1 def testStringSplitWithDelimiter(self): strings = ["hello|world", "hello world"] @@ -92,17 +95,18 @@ class StringSplitOpTest(test.TestCase): ValueError, string_ops.string_split, strings, delimiter=["a"]) tokens = string_ops.string_split(strings, delimiter="|") - indices, values, shape = sess.run(tokens) + indices, values, shape = self.evaluate(tokens) self.assertAllEqual(indices, [[0, 0], [0, 1], [1, 0]]) self.assertAllEqual(values, [b"hello", b"world", b"hello world"]) self.assertAllEqual(shape, [2, 2]) tokens = string_ops.string_split(strings, delimiter="| ") - indices, values, shape = sess.run(tokens) + indices, values, shape = self.evaluate(tokens) self.assertAllEqual(indices, [[0, 0], [0, 1], [1, 0], [1, 1]]) self.assertAllEqual(values, [b"hello", b"world", b"hello", b"world"]) self.assertAllEqual(shape, [2, 2]) + @test_util.run_deprecated_v1 def testStringSplitWithDelimiterTensor(self): strings = ["hello|world", "hello world"] @@ -121,6 +125,7 @@ class StringSplitOpTest(test.TestCase): self.assertAllEqual(values, [b"hello", b"world", b"hello world"]) self.assertAllEqual(shape, [2, 2]) + @test_util.run_deprecated_v1 def testStringSplitWithDelimitersTensor(self): strings = ["hello.cruel,world", "hello cruel world"] @@ -145,7 +150,7 @@ class StringSplitOpTest(test.TestCase): with self.cached_session() as sess: tokens = string_ops.string_split(strings, "#", skip_empty=False) - indices, values, shape = sess.run(tokens) + indices, values, shape = self.evaluate(tokens) self.assertAllEqual(indices, [[0, 0], [0, 1], [1, 0], [1, 1], [2, 0], [2, 1], [2, 2]]) @@ -154,7 +159,7 @@ class StringSplitOpTest(test.TestCase): with self.cached_session() as sess: tokens = string_ops.string_split(strings, "#") - indices, values, shape = sess.run(tokens) + indices, values, shape = self.evaluate(tokens) self.assertAllEqual(values, [b"a", b"b", b"c"]) self.assertAllEqual(indices, [[0, 0], [1, 0], [2, 0]]) self.assertAllEqual(shape, [3, 1]) @@ -167,7 +172,7 @@ class StringSplitV2OpTest(test.TestCase): with self.cached_session() as sess: tokens = string_ops.string_split_v2(strings) - indices, values, shape = sess.run(tokens) + indices, values, shape = self.evaluate(tokens) self.assertAllEqual(indices, [[0, 0], [0, 1], [0, 2], [0, 3], [1, 0]]) self.assertAllEqual(values, [b"pigs", b"on", b"the", b"wing", b"animals"]) self.assertAllEqual(shape, [2, 4]) @@ -182,7 +187,7 @@ class StringSplitV2OpTest(test.TestCase): with self.cached_session() as sess: tokens = string_ops.string_split_v2(strings, sep="<>") - indices, values, shape = sess.run(tokens) + indices, values, shape = self.evaluate(tokens) self.assertAllEqual( indices, [[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], [1, 3], [1, 4], [1, 5], [1, 6]]) @@ -200,7 +205,7 @@ class StringSplitV2OpTest(test.TestCase): with self.cached_session() as sess: tokens = string_ops.string_split_v2(strings, sep=',') - indices, values, shape = sess.run(tokens) + indices, values, shape = self.evaluate(tokens) self.assertAllEqual(indices, [[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], [1, 3], [1, 4]]) self.assertAllEqual(values, [b"1", b"2", b"3", @@ -217,7 +222,7 @@ class StringSplitV2OpTest(test.TestCase): with self.cached_session() as sess: tokens = string_ops.string_split_v2(strings) - indices, values, shape = sess.run(tokens) + indices, values, shape = self.evaluate(tokens) self.assertAllEqual(indices, [[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2]]) self.assertAllEqual(values, [b"1", b"2", b"3", b"4", b"5", b"6"]) @@ -233,7 +238,7 @@ class StringSplitV2OpTest(test.TestCase): with self.cached_session() as sess: tokens = string_ops.string_split_v2(strings, sep=',', maxsplit=1) - indices, values, shape = sess.run(tokens) + indices, values, shape = self.evaluate(tokens) self.assertAllEqual(indices, [[0, 0], [0, 1], [1, 0], [1, 1]]) self.assertAllEqual(values, [b"1", b"2,3", b"4", b"5,,6,"]) @@ -249,7 +254,7 @@ class StringSplitV2OpTest(test.TestCase): with self.cached_session() as sess: tokens = string_ops.string_split_v2(strings, maxsplit=1) - indices, values, shape = sess.run(tokens) + indices, values, shape = self.evaluate(tokens) self.assertAllEqual(indices, [[0, 0], [0, 1], [1, 0], [1, 1]]) self.assertAllEqual(values, [b"1", b"2 3", b"4", b"5 6 "]) diff --git a/tensorflow/python/kernel_tests/string_strip_op_test.py b/tensorflow/python/kernel_tests/string_strip_op_test.py index 1e404b7146..edff3862ff 100644 --- a/tensorflow/python/kernel_tests/string_strip_op_test.py +++ b/tensorflow/python/kernel_tests/string_strip_op_test.py @@ -30,7 +30,7 @@ class StringStripOpTest(test.TestCase): with self.cached_session() as sess: output = string_ops.string_strip(strings) - output = sess.run(output) + output = self.evaluate(output) self.assertAllEqual(output, [b"pigs on the wing", b"animals"]) def test_string_strip_2d(self): @@ -39,7 +39,7 @@ class StringStripOpTest(test.TestCase): with self.cached_session() as sess: output = string_ops.string_strip(strings) - output = sess.run(output) + output = self.evaluate(output) self.assertAllEqual(output, [[b"pigs on the wing", b"animals"], [b"hello", b"world"]]) @@ -48,7 +48,7 @@ class StringStripOpTest(test.TestCase): with self.cached_session() as sess: output = string_ops.string_strip(strings) - output = sess.run(output) + output = self.evaluate(output) self.assertAllEqual(output, [b"hello", b"", b"world", b""]) diff --git a/tensorflow/python/kernel_tests/string_to_hash_bucket_op_test.py b/tensorflow/python/kernel_tests/string_to_hash_bucket_op_test.py index 9cb0c9d18f..25f573fc14 100644 --- a/tensorflow/python/kernel_tests/string_to_hash_bucket_op_test.py +++ b/tensorflow/python/kernel_tests/string_to_hash_bucket_op_test.py @@ -19,6 +19,7 @@ from __future__ import print_function from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import string_ops from tensorflow.python.platform import test @@ -26,6 +27,7 @@ from tensorflow.python.platform import test class StringToHashBucketOpTest(test.TestCase): + @test_util.run_deprecated_v1 def testStringToOneHashBucketFast(self): with self.cached_session(): input_string = array_ops.placeholder(dtypes.string) @@ -34,6 +36,7 @@ class StringToHashBucketOpTest(test.TestCase): self.assertAllEqual([0, 0, 0], result) + @test_util.run_deprecated_v1 def testStringToHashBucketsFast(self): with self.cached_session(): input_string = array_ops.placeholder(dtypes.string) @@ -46,6 +49,7 @@ class StringToHashBucketOpTest(test.TestCase): # Fingerprint64('d') -> 4470636696479570465 -> mod 10 -> 5 self.assertAllEqual([9, 2, 2, 5], result) + @test_util.run_deprecated_v1 def testStringToOneHashBucketLegacyHash(self): with self.cached_session(): input_string = array_ops.placeholder(dtypes.string) @@ -54,6 +58,7 @@ class StringToHashBucketOpTest(test.TestCase): self.assertAllEqual([0, 0, 0], result) + @test_util.run_deprecated_v1 def testStringToHashBucketsLegacyHash(self): with self.cached_session(): input_string = array_ops.placeholder(dtypes.string) @@ -70,7 +75,7 @@ class StringToHashBucketOpTest(test.TestCase): input_string = constant_op.constant(['a', 'b', 'c']) output = string_ops.string_to_hash_bucket_strong( input_string, 1, key=[123, 345]) - self.assertAllEqual([0, 0, 0], output.eval()) + self.assertAllEqual([0, 0, 0], self.evaluate(output)) def testStringToHashBucketsStrong(self): with self.cached_session(): @@ -81,7 +86,7 @@ class StringToHashBucketOpTest(test.TestCase): # StrongKeyedHash(key, 'a') -> 7157389809176466784 -> mod 10 -> 4 # StrongKeyedHash(key, 'b') -> 15805638358933211562 -> mod 10 -> 2 # StrongKeyedHash(key, 'c') -> 18100027895074076528 -> mod 10 -> 8 - self.assertAllEqual([4, 2, 8], output.eval()) + self.assertAllEqual([4, 2, 8], self.evaluate(output)) def testStringToHashBucketsStrongInvalidKey(self): with self.cached_session(): diff --git a/tensorflow/python/kernel_tests/string_to_number_op_test.py b/tensorflow/python/kernel_tests/string_to_number_op_test.py index 99ee25e125..49ccfd1028 100644 --- a/tensorflow/python/kernel_tests/string_to_number_op_test.py +++ b/tensorflow/python/kernel_tests/string_to_number_op_test.py @@ -19,6 +19,7 @@ from __future__ import division from __future__ import print_function from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import parsing_ops from tensorflow.python.platform import test @@ -45,6 +46,7 @@ class StringToNumberOpTest(test.TestCase): with self.assertRaisesOpError(outstr): output.eval(feed_dict={input_string: [instr]}) + @test_util.run_deprecated_v1 def testToFloat(self): self._test(dtypes.float32, [("0", 0), ("3", 3), ("-1", -1), @@ -58,6 +60,7 @@ class StringToNumberOpTest(test.TestCase): ("INF", float("INF"))], [("10foobar", _ERROR_MESSAGE + "10foobar")]) + @test_util.run_deprecated_v1 def testToDouble(self): self._test(dtypes.float64, [("0", 0), ("3", 3), ("-1", -1), @@ -71,6 +74,7 @@ class StringToNumberOpTest(test.TestCase): ("INF", float("INF"))], [("10foobar", _ERROR_MESSAGE + "10foobar")]) + @test_util.run_deprecated_v1 def testToInt32(self): self._test(dtypes.int32, [("0", 0), ("3", 3), ("-1", -1), @@ -84,6 +88,7 @@ class StringToNumberOpTest(test.TestCase): ("2.9", _ERROR_MESSAGE + "2.9"), ("10foobar", _ERROR_MESSAGE + "10foobar")]) + @test_util.run_deprecated_v1 def testToInt64(self): self._test(dtypes.int64, [("0", 0), ("3", 3), ("-1", -1), diff --git a/tensorflow/python/kernel_tests/substr_op_test.py b/tensorflow/python/kernel_tests/substr_op_test.py index 37aa624b07..9302152e82 100644 --- a/tensorflow/python/kernel_tests/substr_op_test.py +++ b/tensorflow/python/kernel_tests/substr_op_test.py @@ -22,6 +22,7 @@ from absl.testing import parameterized import numpy as np from tensorflow.python.framework import errors_impl +from tensorflow.python.framework import test_util from tensorflow.python.ops import string_ops from tensorflow.python.platform import test @@ -51,7 +52,7 @@ class SubstrOpTest(test.TestCase, parameterized.TestCase): length = np.array(3, dtype) substr_op = string_ops.substr(test_string, position, length, unit=unit) with self.cached_session(): - substr = substr_op.eval() + substr = self.evaluate(substr_op) self.assertAllEqual(substr, expected_value) @parameterized.parameters( @@ -71,7 +72,7 @@ class SubstrOpTest(test.TestCase, parameterized.TestCase): length = np.array(3, dtype) substr_op = string_ops.substr(test_string, position, length, unit=unit) with self.cached_session(): - substr = substr_op.eval() + substr = self.evaluate(substr_op) self.assertAllEqual(substr, expected_value) # Full string @@ -83,7 +84,7 @@ class SubstrOpTest(test.TestCase, parameterized.TestCase): length = np.array(5, dtype) substr_op = string_ops.substr(test_string, position, length, unit=unit) with self.cached_session(): - substr = substr_op.eval() + substr = self.evaluate(substr_op) self.assertAllEqual(substr, test_string) # Full string (Negative) @@ -95,7 +96,7 @@ class SubstrOpTest(test.TestCase, parameterized.TestCase): length = np.array(5, dtype) substr_op = string_ops.substr(test_string, position, length, unit=unit) with self.cached_session(): - substr = substr_op.eval() + substr = self.evaluate(substr_op) self.assertAllEqual(substr, test_string) # Length is larger in magnitude than a negative position @@ -111,7 +112,7 @@ class SubstrOpTest(test.TestCase, parameterized.TestCase): length = np.array(5, dtype) substr_op = string_ops.substr(test_string, position, length, unit=unit) with self.cached_session(): - substr = substr_op.eval() + substr = self.evaluate(substr_op) self.assertAllEqual(substr, expected_string) @parameterized.parameters( @@ -138,7 +139,7 @@ class SubstrOpTest(test.TestCase, parameterized.TestCase): length = np.array(3, dtype) substr_op = string_ops.substr(test_string, position, length, unit=unit) with self.cached_session(): - substr = substr_op.eval() + substr = self.evaluate(substr_op) self.assertAllEqual(substr, expected_value) @parameterized.parameters( @@ -173,7 +174,7 @@ class SubstrOpTest(test.TestCase, parameterized.TestCase): }[unit] substr_op = string_ops.substr(test_string, position, length, unit=unit) with self.cached_session(): - substr = substr_op.eval() + substr = self.evaluate(substr_op) self.assertAllEqual(substr, expected_value) position = np.array(-3, dtype) @@ -188,7 +189,7 @@ class SubstrOpTest(test.TestCase, parameterized.TestCase): }[unit] substr_op = string_ops.substr(test_string, position, length, unit=unit) with self.cached_session(): - substr = substr_op.eval() + substr = self.evaluate(substr_op) self.assertAllEqual(substr, expected_value) @parameterized.parameters( @@ -229,7 +230,7 @@ class SubstrOpTest(test.TestCase, parameterized.TestCase): }[unit] substr_op = string_ops.substr(test_string, position, length, unit=unit) with self.cached_session(): - substr = substr_op.eval() + substr = self.evaluate(substr_op) self.assertAllEqual(substr, expected_value) @parameterized.parameters( @@ -271,7 +272,7 @@ class SubstrOpTest(test.TestCase, parameterized.TestCase): }[unit] substr_op = string_ops.substr(test_string, position, length, unit=unit) with self.cached_session(): - substr = substr_op.eval() + substr = self.evaluate(substr_op) self.assertAllEqual(substr, expected_value) # Broadcast input string onto pos/len @@ -294,7 +295,7 @@ class SubstrOpTest(test.TestCase, parameterized.TestCase): }[unit] substr_op = string_ops.substr(test_string, position, length, unit=unit) with self.cached_session(): - substr = substr_op.eval() + substr = self.evaluate(substr_op) self.assertAllEqual(substr, expected_value) # Test 1D broadcast @@ -310,7 +311,7 @@ class SubstrOpTest(test.TestCase, parameterized.TestCase): }[unit] substr_op = string_ops.substr(test_string, position, length, unit=unit) with self.cached_session(): - substr = substr_op.eval() + substr = self.evaluate(substr_op) self.assertAllEqual(substr, expected_value) @parameterized.parameters( @@ -319,6 +320,7 @@ class SubstrOpTest(test.TestCase, parameterized.TestCase): (np.int32, "UTF8_CHAR"), (np.int64, "UTF8_CHAR"), ) + @test_util.run_deprecated_v1 def testBadBroadcast(self, dtype, unit): test_string = [[b"ten", b"eleven", b"twelve"], [b"thirteen", b"fourteen", b"fifteen"], @@ -338,6 +340,7 @@ class SubstrOpTest(test.TestCase, parameterized.TestCase): (np.int32, -6, "UTF8_CHAR"), (np.int64, -6, "UTF8_CHAR"), ) + @test_util.run_deprecated_v1 def testOutOfRangeError_Scalar(self, dtype, pos, unit): # Scalar/Scalar test_string = { @@ -349,7 +352,7 @@ class SubstrOpTest(test.TestCase, parameterized.TestCase): substr_op = string_ops.substr(test_string, position, length, unit=unit) with self.cached_session(): with self.assertRaises(errors_impl.InvalidArgumentError): - substr_op.eval() + self.evaluate(substr_op) @parameterized.parameters( (np.int32, 4, "BYTE"), @@ -361,6 +364,7 @@ class SubstrOpTest(test.TestCase, parameterized.TestCase): (np.int32, -4, "UTF8_CHAR"), (np.int64, -4, "UTF8_CHAR"), ) + @test_util.run_deprecated_v1 def testOutOfRangeError_VectorScalar(self, dtype, pos, unit): # Vector/Scalar test_string = { @@ -373,7 +377,7 @@ class SubstrOpTest(test.TestCase, parameterized.TestCase): substr_op = string_ops.substr(test_string, position, length, unit=unit) with self.cached_session(): with self.assertRaises(errors_impl.InvalidArgumentError): - substr_op.eval() + self.evaluate(substr_op) @parameterized.parameters( (np.int32, "BYTE"), @@ -381,6 +385,7 @@ class SubstrOpTest(test.TestCase, parameterized.TestCase): (np.int32, "UTF8_CHAR"), (np.int64, "UTF8_CHAR"), ) + @test_util.run_deprecated_v1 def testOutOfRangeError_MatrixMatrix(self, dtype, unit): # Matrix/Matrix test_string = { @@ -398,7 +403,7 @@ class SubstrOpTest(test.TestCase, parameterized.TestCase): substr_op = string_ops.substr(test_string, position, length, unit=unit) with self.cached_session(): with self.assertRaises(errors_impl.InvalidArgumentError): - substr_op.eval() + self.evaluate(substr_op) # Matrix/Matrix (with negative) position = np.array([[1, 2, -3], [1, 2, -4], [1, 2, -3]], dtype) @@ -406,7 +411,7 @@ class SubstrOpTest(test.TestCase, parameterized.TestCase): substr_op = string_ops.substr(test_string, position, length, unit=unit) with self.cached_session(): with self.assertRaises(errors_impl.InvalidArgumentError): - substr_op.eval() + self.evaluate(substr_op) @parameterized.parameters( (np.int32, "BYTE"), @@ -414,6 +419,7 @@ class SubstrOpTest(test.TestCase, parameterized.TestCase): (np.int32, "UTF8_CHAR"), (np.int64, "UTF8_CHAR"), ) + @test_util.run_deprecated_v1 def testOutOfRangeError_Broadcast(self, dtype, unit): # Broadcast test_string = { @@ -428,7 +434,7 @@ class SubstrOpTest(test.TestCase, parameterized.TestCase): substr_op = string_ops.substr(test_string, position, length, unit=unit) with self.cached_session(): with self.assertRaises(errors_impl.InvalidArgumentError): - substr_op.eval() + self.evaluate(substr_op) # Broadcast (with negative) position = np.array([-1, -2, -4], dtype) @@ -436,7 +442,7 @@ class SubstrOpTest(test.TestCase, parameterized.TestCase): substr_op = string_ops.substr(test_string, position, length, unit=unit) with self.cached_session(): with self.assertRaises(errors_impl.InvalidArgumentError): - substr_op.eval() + self.evaluate(substr_op) @parameterized.parameters( (np.int32, "BYTE"), @@ -444,6 +450,7 @@ class SubstrOpTest(test.TestCase, parameterized.TestCase): (np.int32, "UTF8_CHAR"), (np.int64, "UTF8_CHAR"), ) + @test_util.run_deprecated_v1 def testMismatchPosLenShapes(self, dtype, unit): test_string = { "BYTE": [[b"ten", b"eleven", b"twelve"], @@ -471,6 +478,7 @@ class SubstrOpTest(test.TestCase, parameterized.TestCase): with self.assertRaises(ValueError): string_ops.substr(test_string, position, length) + @test_util.run_deprecated_v1 def testWrongDtype(self): with self.cached_session(): with self.assertRaises(TypeError): @@ -478,6 +486,7 @@ class SubstrOpTest(test.TestCase, parameterized.TestCase): with self.assertRaises(TypeError): string_ops.substr(b"test", 3, 1.0) + @test_util.run_deprecated_v1 def testInvalidUnit(self): with self.cached_session(): with self.assertRaises(ValueError): diff --git a/tensorflow/python/kernel_tests/summary_v1_audio_op_test.py b/tensorflow/python/kernel_tests/summary_v1_audio_op_test.py index 63ce77b9d5..1547c55f8b 100644 --- a/tensorflow/python/kernel_tests/summary_v1_audio_op_test.py +++ b/tensorflow/python/kernel_tests/summary_v1_audio_op_test.py @@ -60,7 +60,7 @@ class SummaryV1AudioOpTest(test.TestCase): sample_rate = 8000 summ = summary.audio( "snd", const, max_outputs=3, sample_rate=sample_rate) - value = sess.run(summ) + value = self.evaluate(summ) self.assertEqual([], summ.get_shape()) audio_summ = self._AsSummary(value) diff --git a/tensorflow/python/kernel_tests/summary_v1_image_op_test.py b/tensorflow/python/kernel_tests/summary_v1_image_op_test.py index 094606944f..56de2e933d 100644 --- a/tensorflow/python/kernel_tests/summary_v1_image_op_test.py +++ b/tensorflow/python/kernel_tests/summary_v1_image_op_test.py @@ -24,6 +24,7 @@ from six.moves import xrange # pylint: disable=redefined-builtin from tensorflow.core.framework import summary_pb2 from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import image_ops import tensorflow.python.ops.nn_grad # pylint: disable=unused-import from tensorflow.python.platform import test @@ -49,6 +50,7 @@ class SummaryV1ImageOpTest(test.TestCase): }""" % ((i,) + shape[1:]) for i in xrange(3)) self.assertProtoEquals(expected, image_summ) + @test_util.run_deprecated_v1 def testImageSummary(self): for depth in (1, 3, 4): for positive in False, True: @@ -70,7 +72,7 @@ class SummaryV1ImageOpTest(test.TestCase): # Summarize summ = summary.image("img", const) - value = sess.run(summ) + value = self.evaluate(summ) self.assertEqual([], summ.get_shape()) image_summ = self._AsSummary(value) @@ -84,6 +86,7 @@ class SummaryV1ImageOpTest(test.TestCase): # Check the rest of the proto self._CheckProto(image_summ, shape) + @test_util.run_deprecated_v1 def testImageSummaryUint8(self): np.random.seed(7) for depth in (1, 3, 4): @@ -97,7 +100,7 @@ class SummaryV1ImageOpTest(test.TestCase): # Summarize summ = summary.image("img", tf_images) - value = sess.run(summ) + value = self.evaluate(summ) self.assertEqual([], summ.get_shape()) image_summ = self._AsSummary(value) diff --git a/tensorflow/python/kernel_tests/summary_v1_ops_test.py b/tensorflow/python/kernel_tests/summary_v1_ops_test.py index 6c4e106b11..e070f5bf6f 100644 --- a/tensorflow/python/kernel_tests/summary_v1_ops_test.py +++ b/tensorflow/python/kernel_tests/summary_v1_ops_test.py @@ -26,6 +26,7 @@ from __future__ import print_function from tensorflow.core.framework import summary_pb2 from tensorflow.python.framework import constant_op from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import logging_ops from tensorflow.python.platform import test from tensorflow.python.summary import summary @@ -42,7 +43,7 @@ class SummaryV1OpsTest(test.TestCase): with self.cached_session() as sess: const = constant_op.constant([10.0, 20.0]) summ = logging_ops.scalar_summary(["c1", "c2"], const, name="mysumm") - value = sess.run(summ) + value = self.evaluate(summ) self.assertEqual([], summ.get_shape()) self.assertProtoEquals(""" value { tag: "c1" simple_value: 10.0 } @@ -53,20 +54,21 @@ class SummaryV1OpsTest(test.TestCase): with self.cached_session() as sess: const = constant_op.constant([10.0, 20.0]) summ = logging_ops.scalar_summary(["c1", "c2"], const) - value = sess.run(summ) + value = self.evaluate(summ) self.assertEqual([], summ.get_shape()) self.assertProtoEquals(""" value { tag: "c1" simple_value: 10.0 } value { tag: "c2" simple_value: 20.0 } """, self._AsSummary(value)) + @test_util.run_deprecated_v1 def testMergeSummary(self): with self.cached_session() as sess: const = constant_op.constant(10.0) summ1 = summary.histogram("h", const) summ2 = logging_ops.scalar_summary("c", const) merge = summary.merge([summ1, summ2]) - value = sess.run(merge) + value = self.evaluate(merge) self.assertEqual([], merge.get_shape()) self.assertProtoEquals(""" value { diff --git a/tensorflow/python/kernel_tests/summary_v1_tensor_op_test.py b/tensorflow/python/kernel_tests/summary_v1_tensor_op_test.py index 34f771679a..b8e5b5b882 100644 --- a/tensorflow/python/kernel_tests/summary_v1_tensor_op_test.py +++ b/tensorflow/python/kernel_tests/summary_v1_tensor_op_test.py @@ -50,7 +50,7 @@ class SummaryV1TensorOpTest(test.TestCase): with ops.name_scope("zod"): s3 = summary_lib.tensor_summary("s3", c) s4 = summary_lib.tensor_summary("TensorSummary", c) - summ1, summ2, summ3, summ4 = sess.run([s1, s2, s3, s4]) + summ1, summ2, summ3, summ4 = self.evaluate([s1, s2, s3, s4]) v1 = self._SummarySingleValue(summ1) self.assertEqual(v1.tag, "s1") @@ -68,7 +68,7 @@ class SummaryV1TensorOpTest(test.TestCase): with self.cached_session() as sess: const = constant_op.constant(10.0) summ = summary_lib.tensor_summary("foo", const) - result = sess.run(summ) + result = self.evaluate(summ) value = self._SummarySingleValue(result) n = tensor_util.MakeNdarray(value.tensor) @@ -79,7 +79,7 @@ class SummaryV1TensorOpTest(test.TestCase): with self.cached_session() as sess: const = constant_op.constant(s) summ = summary_lib.tensor_summary("foo", const) - result = sess.run(summ) + result = self.evaluate(summ) value = self._SummarySingleValue(result) n = tensor_util.MakeNdarray(value.tensor) @@ -89,7 +89,7 @@ class SummaryV1TensorOpTest(test.TestCase): with self.cached_session() as sess: const = array_ops.ones([5, 5, 5]) summ = summary_lib.tensor_summary("foo", const) - result = sess.run(summ) + result = self.evaluate(summ) value = self._SummarySingleValue(result) n = tensor_util.MakeNdarray(value.tensor) self._AssertNumpyEq(n, np.ones([5, 5, 5])) @@ -99,7 +99,7 @@ class SummaryV1TensorOpTest(test.TestCase): with self.cached_session() as sess: const = constant_op.constant(strings) summ = summary_lib.tensor_summary("foo", const) - result = sess.run(summ) + result = self.evaluate(summ) value = self._SummarySingleValue(result) n = tensor_util.MakeNdarray(value.tensor) self._AssertNumpyEq(n, strings) @@ -109,7 +109,7 @@ class SummaryV1TensorOpTest(test.TestCase): with self.cached_session() as sess: const = constant_op.constant(bools) summ = summary_lib.tensor_summary("foo", const) - result = sess.run(summ) + result = self.evaluate(summ) value = self._SummarySingleValue(result) n = tensor_util.MakeNdarray(value.tensor) @@ -119,7 +119,7 @@ class SummaryV1TensorOpTest(test.TestCase): with self.cached_session() as sess: def get_description(summary_op): - summ_str = sess.run(summary_op) + summ_str = self.evaluate(summary_op) summ = summary_pb2.Summary() summ.ParseFromString(summ_str) return summ.value[0].metadata diff --git a/tensorflow/python/kernel_tests/svd_op_test.py b/tensorflow/python/kernel_tests/svd_op_test.py index 57298c0fec..97a280ef51 100644 --- a/tensorflow/python/kernel_tests/svd_op_test.py +++ b/tensorflow/python/kernel_tests/svd_op_test.py @@ -68,7 +68,7 @@ class SvdOpTest(test.TestCase): s2 = linalg_ops.svd( matrix2, compute_uv=compute_uv_, full_matrices=full_matrices_) all_ops += [s1, s2] - val = sess.run(all_ops) + val = self.evaluate(all_ops) for i in range(2): s = 6 * i self.assertAllEqual(val[s], val[s + 3]) # s1 == s2 @@ -123,7 +123,7 @@ def _GetSvdOpTest(dtype_, shape_, use_static_shape_, compute_uv_, # Tests that x[...,:,:]^H * x[...,:,:] is close to the identity. xx = math_ops.matmul(x, x, adjoint_a=True) identity = array_ops.matrix_band_part(array_ops.ones_like(xx), 0, 0) - self.assertAllClose(identity.eval(), xx.eval(), atol=tol) + self.assertAllClose(identity.eval(), self.evaluate(xx), atol=tol) def Test(self): is_complex = dtype_ in (np.complex64, np.complex128) @@ -150,7 +150,7 @@ def _GetSvdOpTest(dtype_, shape_, use_static_shape_, compute_uv_, s_tf, u_tf, v_tf = linalg_ops.svd( x_tf, compute_uv=compute_uv_, full_matrices=full_matrices_) if use_static_shape_: - s_tf_val, u_tf_val, v_tf_val = sess.run([s_tf, u_tf, v_tf]) + s_tf_val, u_tf_val, v_tf_val = self.evaluate([s_tf, u_tf, v_tf]) else: s_tf_val, u_tf_val, v_tf_val = sess.run( [s_tf, u_tf, v_tf], feed_dict={x_tf: x_np}) @@ -158,7 +158,7 @@ def _GetSvdOpTest(dtype_, shape_, use_static_shape_, compute_uv_, s_tf = linalg_ops.svd( x_tf, compute_uv=compute_uv_, full_matrices=full_matrices_) if use_static_shape_: - s_tf_val = sess.run(s_tf) + s_tf_val = self.evaluate(s_tf) else: s_tf_val = sess.run(s_tf, feed_dict={x_tf: x_np}) diff --git a/tensorflow/python/kernel_tests/template_test.py b/tensorflow/python/kernel_tests/template_test.py index 9dcdaa61ed..3b2a56bd1f 100644 --- a/tensorflow/python/kernel_tests/template_test.py +++ b/tensorflow/python/kernel_tests/template_test.py @@ -72,6 +72,7 @@ def variable_scoped_function_with_local_variable(): class TemplateTest(test.TestCase): + @test_util.run_deprecated_v1 def test_end_to_end(self): """This test shows a very simple line model with test_loss. @@ -104,10 +105,10 @@ class TemplateTest(test.TestCase): train_op = optimizer.minimize(train_loss) with session.Session() as sess: - sess.run(variables.global_variables_initializer()) - initial_test_loss = sess.run(test_loss) - sess.run(train_op) - final_test_loss = sess.run(test_loss) + self.evaluate(variables.global_variables_initializer()) + initial_test_loss = self.evaluate(test_loss) + self.evaluate(train_op) + final_test_loss = self.evaluate(test_loss) # Parameters are tied, so the loss should have gone down when we trained it. self.assertLess(final_test_loss, initial_test_loss) @@ -172,6 +173,7 @@ class TemplateTest(test.TestCase): self.assertEqual("s1/dummy:0", v1.name) self.assertEqual("s1_1/dummy:0", v3.name) + @test_util.run_deprecated_v1 def test_same_unique_name_raise_error(self): tmpl1 = template.make_template( "_", variable_scoped_function, unique_name_="s1") @@ -190,6 +192,7 @@ class TemplateTest(test.TestCase): template.make_template( "_", variable_scoped_function, unique_name_="s1") + @test_util.run_deprecated_v1 def test_unique_name_and_reuse(self): tmpl1 = template.make_template( "_", variable_scoped_function, unique_name_="s1") @@ -260,6 +263,7 @@ class TemplateTest(test.TestCase): self.assertEqual("s1/test/dummy:0", v1.name) self.assertEqual("s1_1/test/dummy:0", v3.name) + @test_util.run_deprecated_v1 def test_enforces_no_extra_trainable_variables(self): tmpl = template.make_template("s", function_with_create, trainable=True) @@ -675,6 +679,7 @@ class TemplateTest(test.TestCase): self.assertEqual(1, len(tb.variables)) # TODO(apassos) handle local variables in Eager + @test_util.run_deprecated_v1 def test_local_variables(self): # Make sure trainable_variables are created. with variable_scope.variable_scope("foo3"): diff --git a/tensorflow/python/kernel_tests/tensor_array_ops_test.py b/tensorflow/python/kernel_tests/tensor_array_ops_test.py index 0188eb246f..884c04eb7a 100644 --- a/tensorflow/python/kernel_tests/tensor_array_ops_test.py +++ b/tensorflow/python/kernel_tests/tensor_array_ops_test.py @@ -63,6 +63,8 @@ def _make_ta(size, name, dtype=dtypes.float32, infer_shape=False): dtype=dtype, tensor_array_name=name, size=size, infer_shape=infer_shape) +@test_util.run_all_in_graph_and_eager_modes +@test_util.with_control_flow_v2 class TensorArrayTest(test.TestCase): @classmethod @@ -123,11 +125,9 @@ class TensorArrayTest(test.TestCase): self._testTensorArrayWritePack(dtypes.complex128) self._testTensorArrayWritePack(dtypes.string) - @test_util.run_in_graph_and_eager_modes def testTensorArrayWritePack(self): self._testTensorArrayWritePackMaybeLegacy() - @test_util.run_in_graph_and_eager_modes def testEmptyTensorArrayPack(self): with self.session(use_gpu=True): ta = tensor_array_ops.TensorArray( @@ -161,7 +161,7 @@ class TensorArrayTest(test.TestCase): convert([[4.0, 5.0], [104.0, 105.0], [204.0, 205.0], [6.0, 7.0], [106.0, 107.0], [8.0, 9.0]]), c0) - @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def testTensorArrayWriteConcat(self): self._testTensorArrayWriteConcat(dtypes.float32) self._testTensorArrayWriteConcat(dtypes.float64) @@ -184,7 +184,8 @@ class TensorArrayTest(test.TestCase): self.assertAllEqual([[0.0, 0.0], [4.0, 5.0], [0.0, 0.0]], self.evaluate(ta.write(1, [[4.0, 5.0]]).concat())) - @test_util.run_in_graph_and_eager_modes + @test_util.disable_control_flow_v2("b/118890905") + @test_util.run_v1_only("b/118890905") def testTensorArrayReadOrPackNotAllValuesAvailableFillsZeros(self): self._testTensorArrayReadOrPackNotAllValuesAvailableFillsZeros() @@ -200,7 +201,8 @@ class TensorArrayTest(test.TestCase): self.assertAllEqual([[0.0, 0.0], [4.0, 5.0], [0.0, 0.0]], self.evaluate(ta.write(1, [[4.0, 5.0]]).concat())) - @test_util.run_in_graph_and_eager_modes + @test_util.disable_control_flow_v2("b/118890905") + @test_util.run_v1_only("b/118890905") def testTensorArrayReadOrPackNotAllValuesAvailableInferShapeFillsZeros(self): self._testTensorArrayReadOrPackNotAllValuesAvailableInferShapeFillsZeros() @@ -251,7 +253,6 @@ class TensorArrayTest(test.TestCase): self._testTensorArrayUnpackRead(dtypes.complex128) self._testTensorArrayUnpackRead(dtypes.string) - @test_util.run_in_graph_and_eager_modes def testTensorArrayUnpackRead(self): self._testTensorArrayUnpackReadMaybeLegacy() @@ -297,7 +298,7 @@ class TensorArrayTest(test.TestCase): self.assertAllEqual(convert([]).reshape(0, 2), d1) self.assertAllEqual(convert([[3.0, 301.0]]), d2) - @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def testTensorArraySplitRead(self): self._testTensorArraySplitRead(dtypes.float32) self._testTensorArraySplitRead(dtypes.float64) @@ -307,7 +308,9 @@ class TensorArrayTest(test.TestCase): self._testTensorArraySplitRead(dtypes.complex128) self._testTensorArraySplitRead(dtypes.string) - def testTensorGradArrayWriteRead(self): + @test_util.disable_control_flow_v2("v2 does not support TensorArray.grad.") + @test_util.run_deprecated_v1 + def testSkipEagerTensorGradArrayWriteRead(self): with self.session(use_gpu=True) as session: ta = tensor_array_ops.TensorArray( dtype=dtypes.float32, @@ -340,7 +343,29 @@ class TensorArrayTest(test.TestCase): self.assertAllEqual([[2.0]], g_d1) self.assertAllEqual(-2.0, g_d2) - def testTensorGradArrayDynamicWriteRead(self): + @test_util.run_deprecated_v1 + def testSkipEagerTensorArrayGradGrad(self): + if not tensor_array_ops.ENABLE_TENSOR_ARRAY_V2: + self.skipTest("Legacy TensorArray does not support double derivatives.") + with self.test_session(use_gpu=True) as session: + x = constant_op.constant(4.0) + + ta = tensor_array_ops.TensorArray( + dtype=dtypes.float32, + tensor_array_name="foo", + size=1, + infer_shape=False) + w0 = ta.write(0, x) + r0 = w0.read(0) + y = r0 * r0 + + g1 = gradients_impl.gradients(ys=[y], xs=[x]) + g2 = gradients_impl.gradients(ys=[g1], xs=[x]) + self.assertAllEqual([2.0], session.run(g2)) + + @test_util.disable_control_flow_v2("v2 does not support TensorArray.grad.") + @test_util.run_deprecated_v1 + def testSkipEagerTensorGradArrayDynamicWriteRead(self): with self.session(use_gpu=True) as session: ta = tensor_array_ops.TensorArray( dtype=dtypes.float32, @@ -381,7 +406,9 @@ class TensorArrayTest(test.TestCase): self.assertAllEqual(3, vs) self.assertAllEqual(3, g_vs) - def testTensorGradAccessTwiceReceiveSameObject(self): + @test_util.disable_control_flow_v2("v2 does not support TensorArray.grad.") + @test_util.run_deprecated_v1 + def testSkipEagerTensorGradAccessTwiceReceiveSameObject(self): with self.session(use_gpu=True) as session: ta = tensor_array_ops.TensorArray( dtype=dtypes.float32, tensor_array_name="foo", size=3) @@ -397,26 +424,41 @@ class TensorArrayTest(test.TestCase): self.assertAllEqual(t_g_ta_0, t_g_ta_1) self.assertAllEqual([[4.0, 5.0]], d_r1_0) - @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def testTensorArrayWriteWrongIndexOrDataTypeFails(self): with self.session(use_gpu=True): ta = _make_ta(3, "foo", dtype=dtypes.float32) # Test writing the wrong datatype - with self.assertRaisesOpError( - "TensorArray dtype is (float|float32) but Op is trying to write " - "dtype string"): + if (tensor_array_ops.ENABLE_TENSOR_ARRAY_V2 and + not context.executing_eagerly()): + error_msg = ("Invalid data types; op elements string but list elements " + "float") + else: + error_msg = ( + "TensorArray dtype is (float|float32) but Op is trying to write " + "dtype string") + with self.assertRaisesOpError(error_msg): self.evaluate(ta.write(0, "wrong_type_scalar").flow) - with self.assertRaisesOpError("index -1"): + if (tensor_array_ops.ENABLE_TENSOR_ARRAY_V2 and + not context.executing_eagerly()): + error_msg = "Trying to modify element -1 in a list with 3 elements." + else: + error_msg = "index -1" + with self.assertRaisesOpError(error_msg): self.evaluate(ta.write(-1, 3.0).flow) + if (tensor_array_ops.ENABLE_TENSOR_ARRAY_V2 and + not context.executing_eagerly()): + error_msg = "Trying to modify element 3 in a list with 3 elements" + else: + error_msg = ("Tried to write to index 3 but array is not " + "resizeable and size is: 3") # Test reading from too large an index - with self.assertRaisesOpError( - "Tried to write to index 3 but array is not " - "resizeable and size is: 3"): + with self.assertRaisesOpError(error_msg): self.evaluate(ta.write(3, 3.0).flow) - @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def testTensorArrayReadWrongIndexOrDataTypeFails(self): with self.session(use_gpu=True): ta = _make_ta(3, "foo", dtype=dtypes.float32) @@ -424,23 +466,35 @@ class TensorArrayTest(test.TestCase): w0 = ta.write(0, [[4.0, 5.0]]) # Test reading wrong datatype (only possible when constructing graphs). - if not context.executing_eagerly(): + if (not context.executing_eagerly() and + not tensor_array_ops.ENABLE_TENSOR_ARRAY_V2): r0_bad = gen_data_flow_ops.tensor_array_read_v3( handle=w0.handle, index=0, dtype=dtypes.float64, flow_in=w0.flow) with self.assertRaisesOpError( "TensorArray dtype is float but Op requested dtype double."): - r0_bad.eval() + self.evaluate(r0_bad) + if (tensor_array_ops.ENABLE_TENSOR_ARRAY_V2 and + not context.executing_eagerly()): + error_msg = "Trying to access element -1 in a list with 3 elements." + else: + error_msg = "index -1" # Test reading from a negative index, which is not allowed - with self.assertRaisesOpError("index -1"): + with self.assertRaisesOpError(error_msg): self.evaluate(ta.read(-1)) + if (tensor_array_ops.ENABLE_TENSOR_ARRAY_V2 and + not context.executing_eagerly()): + error_msg = "Trying to access element 3 in a list with 3 elements." + else: + error_msg = "Tried to read from index 3 but array size is: 3" # Test reading from too large an index - with self.assertRaisesOpError( - "Tried to read from index 3 but array size is: 3"): + with self.assertRaisesOpError(error_msg): self.evaluate(ta.read(3)) - def testTensorArrayWriteMultipleFails(self): + @test_util.disable_control_flow_v2("v2 allows multiple writes.") + @test_util.run_deprecated_v1 + def testSkipEagerTensorArrayWriteMultipleFails(self): with self.session(use_gpu=True): ta = tensor_array_ops.TensorArray( dtype=dtypes.float32, tensor_array_name="foo", size=3) @@ -450,7 +504,7 @@ class TensorArrayTest(test.TestCase): "it has already been written to."): self.evaluate(ta.write(2, 3.0).write(2, 3.0).flow) - @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def testTensorArrayConcatIncompatibleShapesFails(self): with self.session(use_gpu=True): ta = tensor_array_ops.TensorArray( @@ -482,7 +536,7 @@ class TensorArrayTest(test.TestCase): with self.assertRaisesOpError("shape"): self.evaluate(w3.concat()) - @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def testTensorArraySplitIncompatibleShapesFails(self): with self.session(use_gpu=True): in_eager_mode = context.executing_eagerly() @@ -495,22 +549,32 @@ class TensorArrayTest(test.TestCase): lengths = array_ops.placeholder(dtypes.int64) ta.split([1.0, 2.0, 3.0], lengths).flow.eval(feed_dict={lengths: 1}) - with self.assertRaisesOpError( - r"Expected sum of lengths to be equal to values.shape\[0\], " - r"but sum of lengths is 1 and value's shape is: \[3\]"): + error_msg = ("Unused values in tensor. Length of tensor: 3 Values used: 1" + if tensor_array_ops.ENABLE_TENSOR_ARRAY_V2 and + not in_eager_mode else + r"Expected sum of lengths to be equal to values.shape\[0\], " + r"but sum of lengths is 1 and value's shape is: \[3\]") + with self.assertRaisesOpError(error_msg): self.evaluate(ta.split([1.0, 2.0, 3.0], [1]).flow) ta = _make_ta(1, "baz") - with self.assertRaisesOpError( - r"Expected value to be at least a vector, but received shape: \[\]"): - self.evaluate(ta.split(1.0, [1]).flow) + if tensor_array_ops.ENABLE_TENSOR_ARRAY_V2 and not in_eager_mode: + with self.assertRaisesRegexp( + ValueError, "Shape must be at least rank 1 but is rank 0"): + self.evaluate(ta.split(1.0, [1]).flow) + else: + with self.assertRaisesOpError( + r"Expected value to be at least a vector, but received shape: \[\]" + ): + self.evaluate(ta.split(1.0, [1]).flow) - ta = _make_ta(2, "buz") - with self.assertRaisesOpError( - r"TensorArray's size is not equal to the size of lengths " - r"\(2 vs. 1\), and the TensorArray is not marked as " - r"dynamically resizeable"): - self.evaluate(ta.split([1.0], [1]).flow) + if not tensor_array_ops.ENABLE_TENSOR_ARRAY_V2 or in_eager_mode: + ta = _make_ta(2, "buz") + with self.assertRaisesOpError( + r"TensorArray's size is not equal to the size of lengths " + r"\(2 vs. 1\), and the TensorArray is not marked as " + r"dynamically resizeable"): + self.evaluate(ta.split([1.0], [1]).flow) def _testTensorArrayWriteGradientAddMultipleAdds(self, dtype): with self.cached_session(use_gpu=True): @@ -546,12 +610,16 @@ class TensorArrayTest(test.TestCase): r"existing shape is \[\] but the new input shape is \[1\]"): wb1_grad.flow.eval() - def testTensorArrayWriteGradientAddMultipleAdds(self): + @test_util.disable_control_flow_v2("v2 does not support TensorArray.grad.") + @test_util.run_deprecated_v1 + def testSkipEagerTensorArrayWriteGradientAddMultipleAdds(self): for dtype in (dtypes.int32, dtypes.int64, dtypes.float32, dtypes.float64, dtypes.complex64, dtypes.complex128): self._testTensorArrayWriteGradientAddMultipleAdds(dtype) - def testTensorArrayGradWithShapeKnownElementShape(self): + @test_util.disable_control_flow_v2("Low level legacy TA op test.") + @test_util.run_deprecated_v1 + def testSkipEagerTensorArrayGradWithShapeKnownElementShape(self): with self.session(use_gpu=True) as sess: ta = tensor_array_ops.TensorArray( size=3, @@ -580,7 +648,9 @@ class TensorArrayTest(test.TestCase): self.assertAllClose(fed_value, sess.run(read_value, feed_dict={value: fed_value})) - def testTensorArrayGradWithShapeUnknownElementShape(self): + @test_util.disable_control_flow_v2("Low level legacy TA op test.") + @test_util.run_deprecated_v1 + def testSkipEagerTensorArrayGradWithShapeUnknownElementShape(self): with self.session(use_gpu=True) as sess: ta = tensor_array_ops.TensorArray( size=3, dtype=dtypes.float32, @@ -603,7 +673,6 @@ class TensorArrayTest(test.TestCase): self.assertAllClose(fed_value, sess.run(read_value, feed_dict={value: fed_value})) - @test_util.run_in_graph_and_eager_modes def testMultiTensorArray(self): with self.session(use_gpu=True): h1 = tensor_array_ops.TensorArray( @@ -667,7 +736,8 @@ class TensorArrayTest(test.TestCase): self.assertAllEqual(c([[3.0, 2.0]]), grad_vals[0]) self.assertAllEqual(c(-2.0), grad_vals[1]) - def testTensorArrayGradientWriteRead(self): + @test_util.run_deprecated_v1 + def testSkipEagerTensorArrayGradientWriteRead(self): for dtype in (np.float32, np.float64, np.complex64, np.complex128): self._testTensorArrayGradientWriteReadType(dtype) @@ -698,15 +768,17 @@ class TensorArrayTest(test.TestCase): [-0.5, 1.5], # read(0) gradient [20.0, 30.0, 40.0, 50.0] ]) # concat gradient - grad_vals = sess.run(grad_r) # 2 + 2 entries + grad_vals = self.evaluate(grad_r) # 2 + 2 entries self.assertAllClose([2.0 - 0.5 + 20.0, 3.0 + 1.5 + 30.0], grad_vals[0]) self.assertAllEqual([4.0 + 40.0, 5.0 + 50.0], grad_vals[1]) - def testTensorArrayGradientWritePackConcatAndRead(self): + @test_util.run_deprecated_v1 + def testSkipEagerTensorArrayGradientWritePackConcatAndRead(self): self._testTensorArrayGradientWritePackConcatAndRead() - @test_util.run_in_graph_and_eager_modes + @test_util.disable_control_flow_v2("v2 does not support clear_after_read.") + @test_util.run_deprecated_v1 def testTensorArrayReadTwice(self): with self.session(use_gpu=True): value = constant_op.constant([[1.0, -1.0], [10.0, -10.0]]) @@ -760,10 +832,12 @@ class TensorArrayTest(test.TestCase): self.assertEqual(len(grad_vals), 1) self.assertAllEqual([[2.0 - 1.5, 3.0 + 1.5], [4.0, 5.0]], grad_vals[0]) - def testTensorArrayGradientUnpackRead(self): + @test_util.run_deprecated_v1 + def testSkipEagerTensorArrayGradientUnpackRead(self): self._testTensorArrayGradientUnpackRead() - def testTensorArrayGradientSplitConcat(self): + @test_util.run_deprecated_v1 + def testSkipEagerTensorArrayGradientSplitConcat(self): with self.session(use_gpu=True) as session: ta = tensor_array_ops.TensorArray( dtype=dtypes.float32, tensor_array_name="foo", size=2, @@ -808,17 +882,16 @@ class TensorArrayTest(test.TestCase): self.assertEqual(len(grad_vals), 1) self.assertAllEqual([[2.0, 3.0], [4.0, 5.0]], grad_vals[0]) - def testTensorArrayGradientDynamicUnpackRead(self): + @test_util.run_deprecated_v1 + def testSkipEagerTensorArrayGradientDynamicUnpackRead(self): self._testTensorArrayGradientDynamicUnpackRead() - @test_util.run_in_graph_and_eager_modes def testCloseTensorArray(self): with self.session(use_gpu=True): ta = tensor_array_ops.TensorArray( dtype=dtypes.float32, tensor_array_name="foo", size=3) self.evaluate(ta.close()) - @test_util.run_in_graph_and_eager_modes def testSizeTensorArray(self): with self.session(use_gpu=True): ta = tensor_array_ops.TensorArray( @@ -826,7 +899,6 @@ class TensorArrayTest(test.TestCase): s = ta.size() self.assertAllEqual(3, self.evaluate(s)) - @test_util.run_in_graph_and_eager_modes def testWriteCloseTensorArray(self): with self.session(use_gpu=True): ta = tensor_array_ops.TensorArray( @@ -924,7 +996,6 @@ class TensorArrayTest(test.TestCase): self.assertAllClose(grad_val.sum(axis=0), var_grad_t) self.assertAllClose(grad_val.sum(axis=0), state0_grad_t) - @test_util.run_in_graph_and_eager_modes def testWhileLoopWritePackGradients(self): self._testWhileLoopWritePackGradients( dynamic_size=False, dtype=dtypes.float32) @@ -932,11 +1003,28 @@ class TensorArrayTest(test.TestCase): # self._testWhileLoopWritePackGradients( # dynamic_size=False, dtype=tf.int64) - def testWhileLoopDynamicWritePackGradients(self): + @test_util.disable_control_flow_v2("Testing v1 while_loop with v2 TA") + @test_util.enable_tensor_array_v2 + def testWhileLoopV1WithTensorArrayV2(self): + size = 3 + ta = tensor_array_ops.TensorArray( + dtype=dtypes.int32, size=size, element_shape=tensor_shape.scalar()) + + def Body(counter, ta): + return counter + 1, ta.write(counter, counter) + + _, ta = control_flow_ops.while_loop(lambda i, _: i < size, Body, [0, ta]) + + for i in range(size): + self.assertEqual(self.evaluate(ta.read(i)), i) + + @test_util.disable_control_flow_v2("b/117943489 (dynamic_size)") + @test_util.run_v1_only("b/117943489") + def testSkipEagerWhileLoopDynamicWritePackGradients(self): self._testWhileLoopWritePackGradients( dynamic_size=True, dtype=dtypes.float32) - @test_util.run_in_graph_and_eager_modes + @test_util.disable_control_flow_v2("b/119323158") def testGradSerialTwoLoops(self): with self.session(use_gpu=True): def loop(x): @@ -976,7 +1064,8 @@ class TensorArrayTest(test.TestCase): grad = gradients_impl.gradients(loop(x), [x])[0] self.assertAllClose(31.0, self.evaluate(grad)) - def testSumOfTwoReadVariablesWithoutRepeatGrad(self): + @test_util.run_deprecated_v1 + def testSkipEagerSumOfTwoReadVariablesWithoutRepeatGrad(self): with self.session(use_gpu=True) as session: a = array_ops.identity( np.arange( @@ -1011,7 +1100,8 @@ class TensorArrayTest(test.TestCase): def _grad_source_for_name(self, name): return tensor_array_grad._GetGradSource(constant_op.constant(0, name=name)) - def testGetGradSource_Invalid(self): + @test_util.run_deprecated_v1 + def testSkipEagerGetGradSource_Invalid(self): with self.assertRaises(ValueError): self._grad_source_for_name("") with self.assertRaises(ValueError): @@ -1019,7 +1109,8 @@ class TensorArrayTest(test.TestCase): with self.assertRaises(ValueError): self._grad_source_for_name("foo/bar") - def testGetGradSource_NoEnclosingScope(self): + @test_util.run_deprecated_v1 + def testSkipEagerGetGradSource_NoEnclosingScope(self): self.assertEqual("gradients:0", self._grad_source_for_name("gradients")) self.assertEqual("gradients_0:0", self._grad_source_for_name("gradients_0")) self.assertEqual("gradients", self._grad_source_for_name("gradients/foo")) @@ -1030,7 +1121,8 @@ class TensorArrayTest(test.TestCase): self.assertEqual("gradients_0", self._grad_source_for_name("gradients_0/foo/bar")) - def testGetGradSource_EnclosingScope(self): + @test_util.run_deprecated_v1 + def testSkipEagerGetGradSource_EnclosingScope(self): self.assertEqual("foo/gradients:0", self._grad_source_for_name("foo/gradients")) self.assertEqual("foo/gradients_0:0", @@ -1044,12 +1136,14 @@ class TensorArrayTest(test.TestCase): self.assertEqual("foo/bar/gradients_0", self._grad_source_for_name("foo/bar/gradients_0/baz")) - def testGetGradSource_NestedUsesInnermost(self): + @test_util.run_deprecated_v1 + def testSkipEagerGetGradSource_NestedUsesInnermost(self): self.assertEqual( "foo/gradients/bar/gradients_0", self._grad_source_for_name("foo/gradients/bar/gradients_0/baz")) - def testWriteShape(self): + @test_util.run_deprecated_v1 + def testSkipEagerWriteShape(self): with self.session(use_gpu=True): ta = tensor_array_ops.TensorArray( dtype=dtypes.float32, tensor_array_name="foo", size=3) @@ -1073,7 +1167,8 @@ class TensorArrayTest(test.TestCase): with self.assertRaises(ValueError): w0.write(0, c2) - def testPartlyUnknownShape(self): + @test_util.run_deprecated_v1 + def testSkipEagerPartlyUnknownShape(self): with self.session(use_gpu=True): ta = tensor_array_ops.TensorArray( dtype=dtypes.float32, tensor_array_name="foo", size=6) @@ -1113,7 +1208,6 @@ class TensorArrayTest(test.TestCase): r5 = w5.read(0) self.assertAllEqual([5, 4, 2, 3], r5.get_shape().as_list()) - @test_util.run_in_graph_and_eager_modes def _testUnpackShape(self): with self.cached_session(use_gpu=True): ta = tensor_array_ops.TensorArray( @@ -1144,10 +1238,12 @@ class TensorArrayTest(test.TestCase): with self.assertRaises(ValueError): w1.write(4, c2) + @test_util.disable_control_flow_v2("b/117943489 (dynamic_size)") + @test_util.run_v1_only("b/117943489") def testUnpackShape(self): self._testUnpackShape() - @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def testSplitShape(self): with self.session(use_gpu=True): ta = tensor_array_ops.TensorArray( @@ -1174,11 +1270,13 @@ class TensorArrayTest(test.TestCase): self.assertEqual((2, 2), w0.read(1).get_shape()) else: self.assertEqual(r0.get_shape().ndims, None) - self.assertEqual( - tensor_shape.TensorShape( - ta1.handle.op.get_attr("element_shape")).ndims, None) + if not tensor_array_ops.ENABLE_TENSOR_ARRAY_V2: + self.assertEqual( + tensor_shape.TensorShape( + ta1.handle.op.get_attr("element_shape")).ndims, None) - def testWriteUnknownShape(self): + @test_util.run_deprecated_v1 + def testSkipEagerWriteUnknownShape(self): with self.session(use_gpu=True): ta = tensor_array_ops.TensorArray( dtype=dtypes.float32, @@ -1201,7 +1299,11 @@ class TensorArrayTest(test.TestCase): grad_r0_vals = session.run(grad_r0)[0] self.assertAllEqual(grad_r0_vals, [1.0, 0.0]) - def testGradientWhenNotAllComponentsRead(self): + # TODO(srbs): Figure out how to enable this. This is probably failing + # because we are trying to stack a TensorList with invalid tensors. + # That is because we do not receive gradients for all list indices. + # Figure out how TensorArray handles this. + def disabletestGradientWhenNotAllComponentsRead(self): self._testGradientWhenNotAllComponentsRead() def _testTensorArrayUnpackDynamic(self): @@ -1212,14 +1314,18 @@ class TensorArrayTest(test.TestCase): w0 = ta.unstack(x) w1 = w0.write(3, 4.0) r = w1.stack() - self.assertAllEqual(np.array([1.0, 2.0, 3.0, 4.0]), r.eval()) + self.assertAllEqual(np.array([1.0, 2.0, 3.0, 4.0]), self.evaluate(r)) grad = gradients_impl.gradients(ys=[r], xs=[x]) - self.assertAllEqual(np.array([1.0, 1.0, 1.0]), sess.run(grad)[0]) + self.assertAllEqual(np.array([1.0, 1.0, 1.0]), self.evaluate(grad)[0]) - def testTensorArrayUnpackDynamic(self): + @test_util.disable_control_flow_v2("b/117943489") + @test_util.run_v1_only("b/117943489") + def testSkipEagerTensorArrayUnpackDynamic(self): self._testTensorArrayUnpackDynamic() - def testTensorArraySplitDynamic(self): + @test_util.disable_control_flow_v2("b/117943489") + @test_util.run_v1_only("b/117943489") + def testSkipEagerTensorArraySplitDynamic(self): with self.session(use_gpu=True) as sess: ta = tensor_array_ops.TensorArray( dtype=dtypes.float32, size=3, dynamic_size=True) @@ -1227,21 +1333,26 @@ class TensorArrayTest(test.TestCase): w0 = ta.split(x, [1, 1, 1]) w1 = w0.write(3, [4.0]) r = w1.concat() - self.assertAllEqual(np.array([1.0, 2.0, 3.0, 4.0]), r.eval()) + self.assertAllEqual(np.array([1.0, 2.0, 3.0, 4.0]), self.evaluate(r)) grad = gradients_impl.gradients(ys=[r], xs=[x]) - self.assertAllEqual(np.array([1.0, 1.0, 1.0]), sess.run(grad)[0]) + self.assertAllEqual(np.array([1.0, 1.0, 1.0]), self.evaluate(grad)[0]) def _testTensorArrayEvalEmpty(self): with self.cached_session(use_gpu=True): ta = tensor_array_ops.TensorArray( dtype=dtypes.float32, size=0, dynamic_size=False, infer_shape=False) - with self.assertRaisesOpError( - "TensorArray has size zero, but element shape is not fully " - "defined. Currently only static shapes are supported when packing " - "zero-size TensorArrays."): + v2_msg = ("Tried to stack elements of a empty list with " + "non-fully-defined shape") + v1_msg = ( + "TensorArray has size zero, but element shape is not " + "fully defined. Currently only static shapes are supported when " + "packing zero-size TensorArrays.") + with self.assertRaisesOpError(v2_msg if tensor_array_ops + .ENABLE_TENSOR_ARRAY_V2 else v1_msg): ta.stack().eval() - def testTensorArrayEvalEmpty(self): + @test_util.run_deprecated_v1 + def testSkipEagerTensorArrayEvalEmpty(self): self._testTensorArrayEvalEmpty() # this test is ill-defined for Eager mode --- unpacking an empty tensor @@ -1255,15 +1366,19 @@ class TensorArrayTest(test.TestCase): ta.unstack(array_ops.zeros([0, 3, 5])).mark_used() packed = ta.stack() concatenated = ta.concat() - self.assertAllEqual([0, 3, 5], packed.eval().shape) + self.assertAllEqual([0, 3, 5], self.evaluate(packed).shape) # Concatenating zero tensors along their first dimension gives a # first dimension of zero - self.assertAllEqual([0, 5], concatenated.eval().shape) + self.assertAllEqual([0, 5], self.evaluate(concatenated).shape) - def testTensorArrayEvalEmptyWithDefault(self): + @test_util.disable_control_flow_v2("b/117943489") + @test_util.run_v1_only("b/117943489") + def testSkipEagerTensorArrayEvalEmptyWithDefault(self): self._testTensorArrayEvalEmptyWithDefault() - def testTensorArrayScatterReadAndGradients(self): + @test_util.disable_control_flow_v2("b/117943489") + @test_util.run_v1_only("b/117943489") + def testSkipEagerTensorArrayScatterReadAndGradients(self): with self.session(use_gpu=True) as session: ta = tensor_array_ops.TensorArray( dtype=dtypes.float32, @@ -1289,7 +1404,8 @@ class TensorArrayTest(test.TestCase): self.assertAllEqual([10.0, -10.0], read_vals[1]) self.assertAllEqual([[2.0, 3.0], [4.0, 5.0]], grad_vals[0]) - @test_util.run_in_graph_and_eager_modes + @test_util.disable_control_flow_v2("b/117943286") + @test_util.run_v1_only("b/117943286") def testTensorArrayWriteGatherAndGradients(self): with self.session(use_gpu=True) as session: ta = tensor_array_ops.TensorArray( @@ -1326,7 +1442,9 @@ class TensorArrayTest(test.TestCase): self.assertAllEqual([[1.0, -1.0], [8.0, -8.0]], g_vals[0]) self.assertAllEqual(expected_grad, grad_vals[0]) - def testTensorArrayGetsDeviceFromFirstWrite(self): + @test_util.disable_control_flow_v2("colocate_with not supported in v2.") + @test_util.run_deprecated_v1 + def testSkipEagerTensorArrayGetsDeviceFromFirstWrite(self): with ops.device("/job:worker/task:0/cpu:0"): # this initial device will be ignored. ta = tensor_array_ops.TensorArray(dtype=dtypes.float32, size=2) @@ -1374,7 +1492,9 @@ class TensorArrayTest(test.TestCase): self.assertFalse( [s for s in dev_stats[d] if "/TensorArray" in s.node_name]) - def testTensorArrayGetsDeviceFromFirstWriteInWhileLoop(self): + @test_util.disable_control_flow_v2("colocate_with not supported in v2.") + @test_util.run_deprecated_v1 + def testSkipEagerTensorArrayGetsDeviceFromFirstWriteInWhileLoop(self): with ops.device("/job:worker/task:0/cpu:0"): ta = tensor_array_ops.TensorArray(dtype=dtypes.float32, size=2) @@ -1403,7 +1523,9 @@ class TensorArrayTest(test.TestCase): self.assertFalse( [s for s in dev_stats[d] if "TensorArray" == s.node_name]) - def testTensorArrayDisabledColocateWithFirstWriteCall(self): + @test_util.disable_control_flow_v2("colocate_with not supported in v2.") + @test_util.run_deprecated_v1 + def testSkipEagerTensorArrayDisabledColocateWithFirstWriteCall(self): with ops.device("/job:worker/task:0/cpu:0"): ta = tensor_array_ops.TensorArray( dtype=dtypes.float32, size=2, colocate_with_first_write_call=False) @@ -1433,7 +1555,6 @@ class TensorArrayTest(test.TestCase): self.assertFalse( [s for s in dev_stats[d] if "TensorArray" == s.node_name]) - @test_util.run_in_graph_and_eager_modes def testTensorArrayIdentity(self): with self.session(use_gpu=True): ta0 = tensor_array_ops.TensorArray(dtype=dtypes.float32, size=2, @@ -1486,7 +1607,8 @@ class TensorArrayTest(test.TestCase): self.assertEqual(size0_v, 2) self.assertEqual(size1_v, 4) - def testTensorArrayGradYsInCorrectScope(self): + @test_util.run_deprecated_v1 + def testSkipEagerTensorArrayGradYsInCorrectScope(self): n_time = 1 n_dim = 1 x = constant_op.constant([[1.42]]) @@ -1501,10 +1623,10 @@ class TensorArrayTest(test.TestCase): # wrap it in the correct name scope. dx, = gradients_impl.gradients(ys=[y], xs=[x], grad_ys=[dy]) with self.cached_session(use_gpu=True) as sess: - vdx, vdy = sess.run([dx, dy]) + vdx, vdy = self.evaluate([dx, dy]) self.assertAllClose(vdx, vdy) - def testTensorArrayInt64GPU(self): + def testSkipEagerTensorArrayInt64GPU(self): if not test.is_gpu_available(): return with self.session(use_gpu=True, force_gpu=True) as sess: diff --git a/tensorflow/python/kernel_tests/topk_op_test.py b/tensorflow/python/kernel_tests/topk_op_test.py index d9f340de6b..5d46176bce 100644 --- a/tensorflow/python/kernel_tests/topk_op_test.py +++ b/tensorflow/python/kernel_tests/topk_op_test.py @@ -27,6 +27,7 @@ from tensorflow.python.client import session from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradients_impl from tensorflow.python.ops import nn_ops @@ -48,7 +49,7 @@ class TopKTest(test.TestCase): np_expected_indices = np.array(expected_indices) with self.cached_session(use_gpu=True) as sess: values_op, indices_op = nn_ops.top_k(inputs, k, sorted=sorted) - values, indices = sess.run([values_op, indices_op]) + values, indices = self.evaluate([values_op, indices_op]) self.assertShapeEqual(np_expected_values, values_op) self.assertShapeEqual(np_expected_indices, indices_op) @@ -181,6 +182,7 @@ class TopKTest(test.TestCase): k = constant_op.constant(3) self._validateTopK(inputs, k, [19, 18, 17], [11, 3, 7]) + @test_util.run_deprecated_v1 def testKNegative(self): inputs = [[0.1, 0.2], [0.3, 0.4]] with self.session(use_gpu=True): @@ -189,12 +191,14 @@ class TopKTest(test.TestCase): with self.assertRaisesOpError("Need k >= 0, got -7"): values.eval(feed_dict={k: -7}) + @test_util.run_deprecated_v1 def testKTooLarge(self): inputs = [[0.1, 0.2], [0.3, 0.4]] with self.assertRaisesRegexp(ValueError, r"must have last dimension >= k = 4"): nn_ops.top_k(inputs, 4) + @test_util.run_deprecated_v1 def testTopKGradients(self): with self.session(use_gpu=True) as sess: inputs = array_ops.placeholder(dtypes.float32, shape=[2, 5]) diff --git a/tensorflow/python/kernel_tests/trace_op_test.py b/tensorflow/python/kernel_tests/trace_op_test.py index f1abaefb66..52640c02c2 100644 --- a/tensorflow/python/kernel_tests/trace_op_test.py +++ b/tensorflow/python/kernel_tests/trace_op_test.py @@ -19,6 +19,7 @@ from __future__ import print_function import numpy as np +from tensorflow.python.framework import test_util from tensorflow.python.ops import math_ops from tensorflow.python.platform import test @@ -34,6 +35,7 @@ class TraceTest(test.TestCase): tf_ans = math_ops.trace(x).eval() self.assertAllClose(tf_ans, np_ans) + @test_util.run_deprecated_v1 def testTrace(self): for dtype in [np.int32, np.float32, np.float64]: for shape in [[2, 2], [2, 3], [3, 2], [2, 3, 2], [2, 2, 2, 3]]: diff --git a/tensorflow/python/kernel_tests/transpose_op_test.py b/tensorflow/python/kernel_tests/transpose_op_test.py index 8c11c20709..76e1002ee1 100644 --- a/tensorflow/python/kernel_tests/transpose_op_test.py +++ b/tensorflow/python/kernel_tests/transpose_op_test.py @@ -50,7 +50,7 @@ class TransposeTest(test.TestCase): with self.cached_session(use_gpu=False): inx = ops.convert_to_tensor(x) y = array_ops.transpose(inx, p, conjugate=conjugate) - tf_ans = y.eval() + tf_ans = self.evaluate(y) self.assertShapeEqual(np_ans, y) self.assertAllEqual(np_ans, tf_ans) @@ -81,7 +81,7 @@ class TransposeTest(test.TestCase): with self.cached_session(use_gpu=True): inx = ops.convert_to_tensor(x) y = array_ops.transpose(inx, p, conjugate=conjugate) - tf_ans = y.eval() + tf_ans = self.evaluate(y) self.assertAllEqual(np_ans, tf_ans) self.assertShapeEqual(np_ans, y) @@ -168,7 +168,7 @@ class TransposeTest(test.TestCase): with self.cached_session(use_gpu=True): inx = ops.convert_to_tensor(inp) y = array_ops.transpose(inx, perm) - tf_ans = y.eval() + tf_ans = self.evaluate(y) self.assertAllEqual(np_ans, tf_ans) self.assertShapeEqual(np_ans, y) @@ -189,7 +189,7 @@ class TransposeTest(test.TestCase): with self.cached_session(use_gpu=True): inx = ops.convert_to_tensor(inp) y = array_ops.transpose(inx, perm) - tf_ans = y.eval() + tf_ans = self.evaluate(y) self.assertAllEqual(np_ans, tf_ans) self.assertShapeEqual(np_ans, y) @@ -224,7 +224,7 @@ class TransposeTest(test.TestCase): with self.cached_session(use_gpu=True): inx = ops.convert_to_tensor(inp) y = array_ops.transpose(inx, perm) - tf_ans = y.eval() + tf_ans = self.evaluate(y) self.assertAllEqual(np_ans, tf_ans) self.assertShapeEqual(np_ans, y) @@ -246,7 +246,7 @@ class TransposeTest(test.TestCase): with self.cached_session(use_gpu=True): inx = ops.convert_to_tensor(inp) y = array_ops.transpose(inx, perm) - tf_ans = y.eval() + tf_ans = self.evaluate(y) self.assertAllEqual(np_ans, tf_ans) self.assertShapeEqual(np_ans, y) @@ -267,7 +267,7 @@ class TransposeTest(test.TestCase): with self.cached_session(use_gpu=True): inx = ops.convert_to_tensor(inp) y = array_ops.transpose(inx, perm) - tf_ans = y.eval() + tf_ans = self.evaluate(y) self.assertAllEqual(np_ans, tf_ans) self.assertShapeEqual(np_ans, y) @@ -319,7 +319,7 @@ class TransposeTest(test.TestCase): with self.cached_session(use_gpu=True): inx = ops.convert_to_tensor(inp) y = array_ops.transpose(inx, perm) - tf_ans = y.eval() + tf_ans = self.evaluate(y) self.assertAllEqual(np_ans, tf_ans) self.assertShapeEqual(np_ans, y) self._ClearCachedSession() @@ -341,7 +341,7 @@ class TransposeTest(test.TestCase): inx = ops.convert_to_tensor(x) inp = constant_op.constant(p) y = array_ops.transpose(inx, inp) - tf_ans = y.eval() + tf_ans = self.evaluate(y) self.assertShapeEqual(np_ans, y) self.assertAllEqual(np_ans, tf_ans) diff --git a/tensorflow/python/kernel_tests/unicode_decode_op_test.py b/tensorflow/python/kernel_tests/unicode_decode_op_test.py new file mode 100644 index 0000000000..c165021eea --- /dev/null +++ b/tensorflow/python/kernel_tests/unicode_decode_op_test.py @@ -0,0 +1,153 @@ +# -*- coding: utf-8 -*- +# Copyright 2018 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 unicode_decode and unicode_decode_with_splits.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import errors_impl as errors +from tensorflow.python.framework import test_util +from tensorflow.python.ops import gen_string_ops +from tensorflow.python.platform import test + + +# Account for python2 and python3 execution of the test. +def codepoint(s): + if isinstance(s, bytes): + return ord(s.decode("utf-8")) + elif isinstance(s, str): + return ord(s) + + +class UnicodeDecodeTest(test.TestCase): + + def testBatchDecode(self): + text = constant_op.constant( + ["仅今年前", "分享介面終於迎來更新"]) + row_splits, utf8_text, offsets = gen_string_ops.unicode_decode_with_offsets( + text, "utf-8") + + with self.test_session(): + self.assertAllEqual([ + codepoint("仅"), + codepoint("今"), + codepoint("年"), + codepoint("前"), + codepoint("分"), + codepoint("享"), + codepoint("介"), + codepoint("面"), + codepoint("終"), + codepoint("於"), + codepoint("迎"), + codepoint("來"), + codepoint("更"), + codepoint("新") + ], + self.evaluate(utf8_text).tolist()) + self.assertAllEqual([0, 4, 14], self.evaluate(row_splits).tolist()) + self.assertAllEqual([0, 3, 6, 9, 0, 3, 6, 9, 12, 15, 18, 21, 24, 27], + self.evaluate(offsets).tolist()) + + def testBasicDecodeWithOffset(self): + text = constant_op.constant(["仅今年前"]) + row_splits, utf8_text, starts = gen_string_ops.unicode_decode_with_offsets( + text, "utf-8") + + with self.test_session(): + self.assertAllEqual([ + codepoint("仅"), + codepoint("今"), + codepoint("年"), + codepoint("前"), + ], + self.evaluate(utf8_text).tolist()) + self.assertAllEqual(self.evaluate(row_splits).tolist(), [0, 4]) + self.assertAllEqual(self.evaluate(starts).tolist(), [0, 3, 6, 9]) + + @test_util.run_deprecated_v1 + def testStrictError(self): + text = constant_op.constant([b"\xFEED"]) + _, error, _ = gen_string_ops.unicode_decode_with_offsets( + text, "utf-8", errors="strict") + + with self.assertRaises(errors.InvalidArgumentError): + with self.test_session(): + self.evaluate(error) + + def testReplaceOnError(self): + text = constant_op.constant([b"\xFE"]) + + _, utf8_text, _ = gen_string_ops.unicode_decode_with_offsets( + text, "utf-8", errors="replace") + + with self.test_session(): + self.assertAllEqual(self.evaluate(utf8_text).tolist(), [65533]) + + @test_util.run_deprecated_v1 + def testBadReplacementChar(self): + text = constant_op.constant([b"\xFE"]) + _, error, _ = gen_string_ops.unicode_decode_with_offsets( + text, "utf-8", errors="replace", replacement_char=11141111) + + with self.assertRaises(errors.InvalidArgumentError): + with self.test_session(): + self.evaluate(error) + + def testIgnoreOnError(self): + text = constant_op.constant([b"\xFEhello"]) + + _, utf8_text, _ = gen_string_ops.unicode_decode_with_offsets( + text, "utf-8", errors="ignore") + + with self.test_session(): + self.assertAllEqual(self.evaluate(utf8_text).tolist(), [ + codepoint("h"), + codepoint("e"), + codepoint("l"), + codepoint("l"), + codepoint("o") + ]) + + @test_util.run_deprecated_v1 + def testBadErrorPolicy(self): + text = constant_op.constant(["hippopotamus"]) + + with self.assertRaises(ValueError): + _, _, _ = gen_string_ops.unicode_decode_with_offsets( + text, "utf-8", errors="oranguatan") + + def testReplaceControlChars(self): + text = constant_op.constant(["\x02仅今年前"]) + row_splits, utf8_text, _ = gen_string_ops.unicode_decode_with_offsets( + text, "utf-8", replace_control_characters=True) + + with self.test_session(): + self.assertAllEqual([ + 65533, + codepoint("仅"), + codepoint("今"), + codepoint("年"), + codepoint("前"), + ], + self.evaluate(utf8_text).tolist()) + self.assertAllEqual([0, 5], self.evaluate(row_splits).tolist()) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/kernel_tests/unicode_encode_op_test.py b/tensorflow/python/kernel_tests/unicode_encode_op_test.py new file mode 100644 index 0000000000..a5a5c2017c --- /dev/null +++ b/tensorflow/python/kernel_tests/unicode_encode_op_test.py @@ -0,0 +1,301 @@ +# 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 UnicodeEncode op from ragged_string_ops.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from absl.testing import parameterized +import numpy as np + +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import errors_impl as errors +from tensorflow.python.framework import ops +from tensorflow.python.ops.ragged import ragged_factory_ops +from tensorflow.python.ops.ragged import ragged_string_ops +from tensorflow.python.platform import test + + +class UnicodeEncodeOpTest(test.TestCase, parameterized.TestCase): + + def testScalar(self): + with self.cached_session(): + with self.assertRaises(ValueError): + ragged_string_ops.unicode_encode(72, "UTF-8") + with self.cached_session(): + with self.assertRaises(ValueError): + ragged_string_ops.unicode_encode(constant_op.constant(72), "UTF-8") + + def testRequireParams(self): + with self.cached_session(): + with self.assertRaises(TypeError): + ragged_string_ops.unicode_encode() + with self.cached_session(): + with self.assertRaises(TypeError): + ragged_string_ops.unicode_encode(72) + with self.cached_session(): + with self.assertRaises(TypeError): + ragged_string_ops.unicode_encode(encoding="UTF-8") + + @parameterized.parameters("UTF-8", "UTF-16-BE", "UTF-32-BE") + def testStrictErrors(self, encoding): + test_value = np.array([72, 101, 2147483647, -1, 111], np.int32) + with self.cached_session(): + with self.assertRaises(errors.InvalidArgumentError): + ragged_string_ops.unicode_encode(test_value, encoding, "strict").eval() + + @parameterized.parameters("UTF-8", "UTF-16-BE", "UTF-32-BE") + def testIgnoreErrors(self, encoding): + test_value = np.array([72, 101, 2147483647, -1, 111], np.int32) + expected_value = u"Heo".encode(encoding) + unicode_encode_op = ragged_string_ops.unicode_encode(test_value, encoding, + "ignore") + with self.cached_session(): + result = unicode_encode_op.eval() + self.assertIsInstance(result, bytes) + self.assertAllEqual(result, expected_value) + + @parameterized.parameters("UTF-8", "UTF-16-BE", "UTF-32-BE") + def testReplaceErrors(self, encoding): + test_value = np.array([72, 101, 2147483647, -1, 111], np.int32) + expected_value = u"He\U0000fffd\U0000fffdo".encode(encoding) + unicode_encode_op = ragged_string_ops.unicode_encode(test_value, encoding, + "replace") + with self.cached_session(): + result = unicode_encode_op.eval() + self.assertIsInstance(result, bytes) + self.assertAllEqual(result, expected_value) + + # Test custom replacement character + test_value = np.array([72, 101, 2147483647, -1, 111], np.int32) + expected_value = u"Heooo".encode(encoding) + unicode_encode_op = ragged_string_ops.unicode_encode(test_value, encoding, + "replace", 111) + with self.cached_session(): + result = unicode_encode_op.eval() + self.assertIsInstance(result, bytes) + self.assertAllEqual(result, expected_value) + + # Verify "replace" is default + test_value = np.array([72, 101, 2147483647, -1, 111], np.int32) + expected_value = u"He\U0000fffd\U0000fffdo".encode(encoding) + unicode_encode_op = ragged_string_ops.unicode_encode(test_value, encoding) + with self.cached_session(): + result = unicode_encode_op.eval() + self.assertIsInstance(result, bytes) + self.assertAllEqual(result, expected_value) + + # Replacement_char must be within range + test_value = np.array([72, 101, 2147483647, -1, 111], np.int32) + unicode_encode_op = ragged_string_ops.unicode_encode(test_value, encoding, + "replace", 1114112) + with self.cached_session(): + with self.assertRaises(errors.InvalidArgumentError): + unicode_encode_op.eval() + + # -- regular Tensor tests -- # + + @parameterized.parameters("UTF-8", "UTF-16-BE", "UTF-32-BE") + def testVector(self, encoding): + test_value = np.array([72, 101, 108, 108, 111], np.int32) + expected_value = u"Hello".encode(encoding) + unicode_encode_op = ragged_string_ops.unicode_encode(test_value, encoding) + with self.cached_session(): + result = unicode_encode_op.eval() + self.assertIsInstance(result, bytes) + self.assertAllEqual(result, expected_value) + + test_value = np.array([72, 101, 195, 195, 128516], np.int32) + expected_value = u"He\xc3\xc3\U0001f604".encode(encoding) + unicode_encode_op = ragged_string_ops.unicode_encode(test_value, encoding) + with self.cached_session(): + result = unicode_encode_op.eval() + self.assertIsInstance(result, bytes) + self.assertAllEqual(result, expected_value) + + # Single character string + test_value = np.array([72], np.int32) + expected_value = u"H".encode(encoding) + unicode_encode_op = ragged_string_ops.unicode_encode(test_value, encoding) + with self.cached_session(): + result = unicode_encode_op.eval() + self.assertIsInstance(result, bytes) + self.assertAllEqual(result, expected_value) + + test_value = np.array([128516], np.int32) + expected_value = u"\U0001f604".encode(encoding) + unicode_encode_op = ragged_string_ops.unicode_encode(test_value, encoding) + with self.cached_session(): + result = unicode_encode_op.eval() + self.assertIsInstance(result, bytes) + self.assertAllEqual(result, expected_value) + + @parameterized.parameters("UTF-8", "UTF-16-BE", "UTF-32-BE") + def testMatrix(self, encoding): + test_value = np.array( + [[72, 128516, 108, 108, 111], [87, 128516, 114, 108, 100]], np.int32) + expected_value = [ + u"H\U0001f604llo".encode(encoding), u"W\U0001f604rld".encode(encoding) + ] + unicode_encode_op = ragged_string_ops.unicode_encode(test_value, encoding) + with self.cached_session(): + result = unicode_encode_op.eval() + self.assertIsInstance(unicode_encode_op, ops.Tensor) + self.assertAllEqual(result, expected_value) + + @parameterized.parameters("UTF-8", "UTF-16-BE", "UTF-32-BE") + def test3DimMatrix(self, encoding): + test_value = constant_op.constant( + [[[72, 101, 108, 108, 111], [87, 111, 114, 108, 100]], + [[102, 105, 120, 101, 100], [119, 111, 114, 100, 115]], + [[72, 121, 112, 101, 114], [99, 117, 98, 101, 46]]], np.int32) + expected_value = [[u"Hello".encode(encoding), u"World".encode(encoding)], + [u"fixed".encode(encoding), u"words".encode(encoding)], + [u"Hyper".encode(encoding), u"cube.".encode(encoding)]] + unicode_encode_op = ragged_string_ops.unicode_encode(test_value, encoding) + with self.cached_session(): + result = unicode_encode_op.eval() + self.assertIsInstance(unicode_encode_op, ops.Tensor) + self.assertAllEqual(result, expected_value) + + @parameterized.parameters("UTF-8", "UTF-16-BE", "UTF-32-BE") + def test4DimMatrix(self, encoding): + test_value = constant_op.constant( + [[[[72, 101, 108, 108, 111]], [[87, 111, 114, 108, 100]]], + [[[102, 105, 120, 101, 100]], [[119, 111, 114, 100, 115]]], + [[[72, 121, 112, 101, 114]], [[99, 117, 98, 101, 46]]]], np.int32) + expected_value = [[[u"Hello".encode(encoding)], + [u"World".encode(encoding)]], + [[u"fixed".encode(encoding)], + [u"words".encode(encoding)]], + [[u"Hyper".encode(encoding)], + [u"cube.".encode(encoding)]]] + unicode_encode_op = ragged_string_ops.unicode_encode(test_value, encoding) + with self.cached_session(): + result = unicode_encode_op.eval() + self.assertIsInstance(unicode_encode_op, ops.Tensor) + self.assertAllEqual(result, expected_value) + + # -- Ragged Tensor tests -- # + + @parameterized.parameters("UTF-8", "UTF-16-BE", "UTF-32-BE") + def testRaggedMatrix(self, encoding): + test_value = ragged_factory_ops.constant( + [[72, 195, 108, 108, 111], [87, 128516, 114, 108, 100, 46]], np.int32) + expected_value = [ + u"H\xc3llo".encode(encoding), u"W\U0001f604rld.".encode(encoding) + ] + unicode_encode_op = ragged_string_ops.unicode_encode(test_value, encoding) + with self.cached_session(): + result = unicode_encode_op.eval() + self.assertIsInstance(unicode_encode_op, ops.Tensor) + self.assertAllEqual(result, expected_value) + + @parameterized.parameters("UTF-8", "UTF-16-BE", "UTF-32-BE") + def test3DimMatrixWithRagged2ndDim(self, encoding): + test_value = ragged_factory_ops.constant( + [[[72, 101, 108, 108, 111], [87, 111, 114, 108, 100]], + [[102, 105, 120, 101, 100]], + [[72, 121, 112, 101, 114], [119, 111, 114, 100, 115], + [99, 117, 98, 101, 46]]], np.int32) + expected_value = [[u"Hello".encode(encoding), u"World".encode(encoding)], + [u"fixed".encode(encoding)], + [ + u"Hyper".encode(encoding), u"words".encode(encoding), + u"cube.".encode(encoding) + ]] + unicode_encode_op = ragged_string_ops.unicode_encode(test_value, encoding) + with self.cached_session(): + result = unicode_encode_op.eval() + self.assertEqual(unicode_encode_op.ragged_rank, 1) + self.assertAllEqual(result.tolist(), expected_value) + + @parameterized.parameters("UTF-8", "UTF-16-BE", "UTF-32-BE") + def test3DimMatrixWithRagged3rdDim(self, encoding): + test_value = ragged_factory_ops.constant( + [[[72, 101, 108, 108, 111], [87, 111, 114, 108, 100, 46]], + [[68, 111, 110, 39, 116], [119, 195, 114, 114, 121, 44, 32, 98, 101]], + [[128516], []]], np.int32) + expected_value = [[u"Hello".encode(encoding), u"World.".encode(encoding)], + [ + u"Don't".encode(encoding), + u"w\xc3rry, be".encode(encoding) + ], [u"\U0001f604".encode(encoding), u"".encode(encoding)]] + unicode_encode_op = ragged_string_ops.unicode_encode(test_value, encoding) + with self.cached_session(): + result = unicode_encode_op.eval() + self.assertEqual(unicode_encode_op.ragged_rank, 1) + self.assertAllEqual(result.tolist(), expected_value) + + @parameterized.parameters("UTF-8", "UTF-16-BE", "UTF-32-BE") + def test3DimMatrixWithRagged2ndAnd3rdDim(self, encoding): + test_value = ragged_factory_ops.constant( + [[[72, 101, 108, 108, 111], [87, 111, 114, 108, 100, 46]], [], + [[128516]]], np.int32) + expected_value = [[u"Hello".encode(encoding), u"World.".encode(encoding)], + [], [u"\U0001f604".encode(encoding)]] + unicode_encode_op = ragged_string_ops.unicode_encode(test_value, encoding) + with self.cached_session(): + result = unicode_encode_op.eval() + self.assertEqual(unicode_encode_op.ragged_rank, 1) + self.assertAllEqual(result.tolist(), expected_value) + + @parameterized.parameters("UTF-8", "UTF-16-BE", "UTF-32-BE") + def test4DimRaggedMatrix(self, encoding): + test_value = ragged_factory_ops.constant( + [[[[72, 101, 108, 108, 111], [87, 111, 114, 108, 100]]], + [[[]], [[72, 121, 112, 101]]]], np.int32) + expected_value = [[[u"Hello".encode(encoding), u"World".encode(encoding)]], + [[u"".encode(encoding)], [u"Hype".encode(encoding)]]] + unicode_encode_op = ragged_string_ops.unicode_encode(test_value, encoding) + with self.cached_session(): + result = unicode_encode_op.eval() + self.assertEqual(unicode_encode_op.ragged_rank, 2) + self.assertAllEqual(result.tolist(), expected_value) + + @parameterized.parameters("UTF-8", "UTF-16-BE", "UTF-32-BE") + def testRaggedMatrixWithMultiDimensionInnerValues(self, encoding): + test_inner_values = constant_op.constant([[[72, 101, 108, 108, 111], + [87, 111, 114, 108, 100]], + [[102, 105, 120, 101, 100], + [119, 111, 114, 100, 115]], + [[72, 121, 112, 101, 114], + [99, 117, 98, 101, 46]]]) + test_row_splits = [ + constant_op.constant([0, 2, 3], dtype=np.int64), + constant_op.constant([0, 1, 1, 3], dtype=np.int64) + ] + test_value = ragged_factory_ops.from_nested_row_splits(test_inner_values, + test_row_splits) + expected_value = [[[[u"Hello".encode(encoding), u"World".encode(encoding)]], + []], + [[[u"fixed".encode(encoding), u"words".encode(encoding)], + [u"Hyper".encode(encoding), + u"cube.".encode(encoding)]]]] + unicode_encode_op = ragged_string_ops.unicode_encode(test_value, encoding) + with self.cached_session(): + result = unicode_encode_op.eval() + self.assertEqual(unicode_encode_op.ragged_rank, 2) + self.assertAllEqual(result.tolist(), expected_value) + # These next two assertions don't necessarily need to be here as they test + # internal representations and we already verified the value is correct. + self.assertAllEqual(len(result.nested_row_splits), len(test_row_splits)) + self.assertEqual(unicode_encode_op.inner_values.shape.ndims, + test_inner_values.shape.ndims - 1) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/kernel_tests/unicode_script_op_test.py b/tensorflow/python/kernel_tests/unicode_script_op_test.py index 927e5459ed..83cfeb2021 100644 --- a/tensorflow/python/kernel_tests/unicode_script_op_test.py +++ b/tensorflow/python/kernel_tests/unicode_script_op_test.py @@ -20,12 +20,14 @@ from __future__ import print_function from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import string_ops from tensorflow.python.platform import test class UnicodeScriptOpTest(test.TestCase): + @test_util.run_deprecated_v1 def testValidScripts(self): inputs = [ ord("a"), @@ -45,6 +47,7 @@ class UnicodeScriptOpTest(test.TestCase): 0 # USCRIPT_COMMON (ZYYY) ]) + @test_util.run_deprecated_v1 def testInvalidScript(self): inputs = [-100, 0xffffff] with self.cached_session(): diff --git a/tensorflow/python/kernel_tests/unicode_transcode_op_test.py b/tensorflow/python/kernel_tests/unicode_transcode_op_test.py index 2908e2bfc5..a3b4fd0347 100644 --- a/tensorflow/python/kernel_tests/unicode_transcode_op_test.py +++ b/tensorflow/python/kernel_tests/unicode_transcode_op_test.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Tests for string_length_op.""" +"""Tests for unicode_transcode op.""" from __future__ import absolute_import from __future__ import division @@ -22,6 +22,7 @@ from absl.testing import parameterized from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import string_ops from tensorflow.python.platform import test @@ -42,7 +43,7 @@ class UnicodeTranscodeOpTest(test.TestCase, parameterized.TestCase): errors="replace", replacement_char=ord(" "), replace_control_characters=False) - values = sess.run(outputs) + values = self.evaluate(outputs) self.assertAllEqual(values, strings) outputs = string_ops.unicode_transcode( @@ -52,7 +53,7 @@ class UnicodeTranscodeOpTest(test.TestCase, parameterized.TestCase): errors="replace", replacement_char=ord(" "), replace_control_characters=False) - values = sess.run(outputs) + values = self.evaluate(outputs) self.assertAllEqual(values, strings) outputs = string_ops.unicode_transcode( @@ -62,7 +63,7 @@ class UnicodeTranscodeOpTest(test.TestCase, parameterized.TestCase): errors="replace", replacement_char=ord(" "), replace_control_characters=False) - values = sess.run(outputs) + values = self.evaluate(outputs) self.assertAllEqual(values, strings) def test_transcode_utf16_to_utf8(self): @@ -77,7 +78,7 @@ class UnicodeTranscodeOpTest(test.TestCase, parameterized.TestCase): errors="replace", replacement_char=ord(" "), replace_control_characters=False) - values = sess.run(outputs) + values = self.evaluate(outputs) self.assertAllEqual(values, expected) def test_transcode_bad_utf8(self): @@ -90,7 +91,7 @@ class UnicodeTranscodeOpTest(test.TestCase, parameterized.TestCase): errors="replace", replacement_char=ord(" "), replace_control_characters=True) - values = sess.run(outputs) + values = self.evaluate(outputs) self.assertAllEqual(values, b" ") outputs = string_ops.unicode_transcode( @@ -100,7 +101,7 @@ class UnicodeTranscodeOpTest(test.TestCase, parameterized.TestCase): errors="replace", replacement_char=ord(" "), replace_control_characters=False) - values = sess.run(outputs) + values = self.evaluate(outputs) self.assertAllEqual(values, b"\x00 ") def test_transcode_bad_utf8_with_some_good(self): @@ -113,7 +114,7 @@ class UnicodeTranscodeOpTest(test.TestCase, parameterized.TestCase): errors="replace", replacement_char=ord(" "), replace_control_characters=False) - values = sess.run(outputs) + values = self.evaluate(outputs) self.assertAllEqual(values, b"abc abcdefg") def test_transcode_bad_utf8_with_defaults(self): @@ -121,7 +122,7 @@ class UnicodeTranscodeOpTest(test.TestCase, parameterized.TestCase): with self.cached_session() as sess: outputs = string_ops.unicode_transcode( bad_string, input_encoding="UTF-8", output_encoding="UTF-8") - values = sess.run(outputs) + values = self.evaluate(outputs) self.assertAllEqual(values, b"\x00\xef\xbf\xbd") def test_transcode_bad_utf8_with_space_replacement(self): @@ -130,9 +131,10 @@ class UnicodeTranscodeOpTest(test.TestCase, parameterized.TestCase): outputs = string_ops.unicode_transcode( bad_string, input_encoding="UTF-8", output_encoding="UTF-8", replacement_char=ord(" ")) - values = sess.run(outputs) + values = self.evaluate(outputs) self.assertAllEqual(values, b"\x00 ") + @test_util.run_deprecated_v1 def test_transcode_bad_utf8_with_strict_errors(self): bad_string = b"\x00\xff" with self.cached_session() as sess: @@ -143,8 +145,9 @@ class UnicodeTranscodeOpTest(test.TestCase, parameterized.TestCase): errors="strict") with self.assertRaisesOpError( "Invalid formatting on input string"): - sess.run(outputs) + self.evaluate(outputs) + @test_util.run_deprecated_v1 def test_transcode_bad_utf8_start_with_strict_errors(self): bad_string = b"\xffabcd" with self.cached_session() as sess: @@ -155,7 +158,7 @@ class UnicodeTranscodeOpTest(test.TestCase, parameterized.TestCase): errors="strict") with self.assertRaisesOpError( "Invalid formatting on input string"): - sess.run(outputs) + self.evaluate(outputs) def test_transcode_bad_utf8_with_elision_of_malformatting(self): bad_string = b"\x00\xff" @@ -165,7 +168,7 @@ class UnicodeTranscodeOpTest(test.TestCase, parameterized.TestCase): input_encoding="UTF-8", output_encoding="UTF-8", errors="ignore") - values = sess.run(outputs) + values = self.evaluate(outputs) self.assertAllEqual(values, b"\x00") def test_transcode_bad_utf8_with_elision_including_control_chars(self): @@ -177,7 +180,7 @@ class UnicodeTranscodeOpTest(test.TestCase, parameterized.TestCase): output_encoding="UTF-8", errors="ignore", replace_control_characters=True) - values = sess.run(outputs) + values = self.evaluate(outputs) self.assertAllEqual(values, b"") def test_transcode_bad_utf8_termination_with_defaults(self): @@ -185,7 +188,7 @@ class UnicodeTranscodeOpTest(test.TestCase, parameterized.TestCase): with self.cached_session() as sess: outputs = string_ops.unicode_transcode( bad_string, input_encoding="UTF-8", output_encoding="UTF-8") - values = sess.run(outputs) + values = self.evaluate(outputs) self.assertAllEqual(values, b"a\xef\xbf\xbd") # 0xFFFD def test_transcode_utf8_with_replacement_char(self): @@ -194,13 +197,13 @@ class UnicodeTranscodeOpTest(test.TestCase, parameterized.TestCase): outputs = string_ops.unicode_transcode( strings, input_encoding="UTF-8", output_encoding="UTF-8", errors="strict") - values = sess.run(outputs) + values = self.evaluate(outputs) self.assertAllEqual(values, [b"a\xef\xbf\xbd"]) outputs = string_ops.unicode_transcode( strings, input_encoding="UTF-8", output_encoding="UTF-8", errors="replace", replacement_char=ord("?")) - values = sess.run(outputs) + values = self.evaluate(outputs) self.assertAllEqual(values, [b"a\xef\xbf\xbd"]) def test_transcode_utf8_to_utf16(self): @@ -214,7 +217,7 @@ class UnicodeTranscodeOpTest(test.TestCase, parameterized.TestCase): output_encoding="UTF-16-BE", replacement_char=ord(" "), replace_control_characters=False) - values = sess.run(outputs) + values = self.evaluate(outputs) print("values=", values) self.assertAllEqual(values, expected) @@ -230,7 +233,7 @@ class UnicodeTranscodeOpTest(test.TestCase, parameterized.TestCase): output_encoding="UTF-8", replacement_char=ord(" "), replace_control_characters=False) - values = sess.run(outputs) + values = self.evaluate(outputs) self.assertAllEqual(values, expected) def test_transcode_utf8_to_utf32(self): @@ -243,7 +246,7 @@ class UnicodeTranscodeOpTest(test.TestCase, parameterized.TestCase): output_encoding="UTF-32-BE", replacement_char=ord(" "), replace_control_characters=False) - values = sess.run(outputs) + values = self.evaluate(outputs) self.assertAllEqual(values, expected) # Documentation in ICU suggests that getNextUChar may produce a different @@ -258,7 +261,7 @@ class UnicodeTranscodeOpTest(test.TestCase, parameterized.TestCase): output_encoding="UTF-8", replacement_char=ord(" "), replace_control_characters=False) - values = sess.run(outputs) + values = self.evaluate(outputs) self.assertAllEqual(values, strings) def test_transcode_utf8_with_bom(self): @@ -266,12 +269,12 @@ class UnicodeTranscodeOpTest(test.TestCase, parameterized.TestCase): with self.cached_session() as sess: outputs = string_ops.unicode_transcode( bom_string, input_encoding="UTF-8", output_encoding="UTF-8") - values = sess.run(outputs) + values = self.evaluate(outputs) self.assertAllEqual(values, b"\xef\xbb\xbfabcdefg") # BOM preserved outputs = string_ops.unicode_transcode( bom_string, input_encoding="UTF-8", output_encoding="UTF-16-BE") - values = sess.run(outputs) + values = self.evaluate(outputs) utf16expected = bom_string.decode("UTF-8").encode("UTF-16-BE") self.assertAllEqual(values, utf16expected) @@ -280,20 +283,20 @@ class UnicodeTranscodeOpTest(test.TestCase, parameterized.TestCase): with self.cached_session() as sess: outputs = string_ops.unicode_transcode( bom_string, input_encoding="UTF-16-BE", output_encoding="UTF-8") - values = sess.run(outputs) + values = self.evaluate(outputs) # BOM is preserved in output self.assertAllEqual(values, b"\xef\xbb\xbfa") outputs = string_ops.unicode_transcode( bom_string, input_encoding="UTF-16-LE", output_encoding="UTF-8") - values = sess.run(outputs) + values = self.evaluate(outputs) # mangled BOM and value from (incorrect) LE encoding self.assertAllEqual(values, b"\xef\xbf\xbe\xe6\x84\x80") bom_string = b"\xff\xfe\x61\x00" # Little-endian BOM with 'a' encoded outputs = string_ops.unicode_transcode( bom_string, input_encoding="UTF-16-LE", output_encoding="UTF-8") - values = sess.run(outputs) + values = self.evaluate(outputs) self.assertAllEqual(values, b"\xef\xbb\xbfa") @parameterized.parameters( @@ -317,12 +320,14 @@ class UnicodeTranscodeOpTest(test.TestCase, parameterized.TestCase): (b"\xfe\xff\x00<\xfe\xff\x00>", "UTF-16", b"<\xef\xbb\xbf>"), (b"\xff\xfe<\x00\xff\xfe>\x00", "UTF-16", b"<\xef\xbb\xbf>"), ) + @test_util.run_deprecated_v1 def test_bom_handling(self, string, input_encoding, expected): with self.test_session(): output = string_ops.unicode_transcode( string, input_encoding=input_encoding, output_encoding="UTF-8") self.assertAllEqual(output.eval(), expected) + @test_util.run_deprecated_v1 def test_invalid_encoding_causes_errors(self): strings = [[b"a", b"abc"], [b"ABC", b"DEF"]] @@ -336,7 +341,7 @@ class UnicodeTranscodeOpTest(test.TestCase, parameterized.TestCase): replace_control_characters=False) with self.assertRaisesOpError( "Could not create converter for input encoding: invalid"): - sess.run(outputs) + self.evaluate(outputs) with self.assertRaisesRegexp(ValueError, "Op passed string 'invalid'"): with self.cached_session() as sess: @@ -347,8 +352,9 @@ class UnicodeTranscodeOpTest(test.TestCase, parameterized.TestCase): errors="replace", replacement_char=ord(" "), replace_control_characters=False) - sess.run(outputs) + self.evaluate(outputs) + @test_util.run_deprecated_v1 def test_invalid_error_policy_causes_errors(self): strings = [[b"a", b"abc"], [b"ABC", b"DEF"]] @@ -362,7 +368,7 @@ class UnicodeTranscodeOpTest(test.TestCase, parameterized.TestCase): errors="invalid", replacement_char=ord(" "), replace_control_characters=False) - sess.run(outputs) + self.evaluate(outputs) def test_forwarding(self): with self.cached_session(): @@ -378,6 +384,61 @@ class UnicodeTranscodeOpTest(test.TestCase, parameterized.TestCase): self.assertAllEqual([b"AbCdE", b"HiJkL"], transcoded) + @test_util.run_deprecated_v1 + def test_cjk_encodings(self): + strings_ja = [ + b"\x5c\x5c", # Yen sign + b"\x8f\x70", # kanji character "waza" + b"\x83\x4f" + ] # katakana character "gu" + strings_zh_cn = [b"\xca\xf5"] # simplified "shu4" + strings_zh_tw = [b"\xb3\x4e"] # traditional "shu4" + strings_ko = [b"\xc7\xd1\xb9\xce"] # hangul "hanmin" + + expected_ja = [s.decode("shift_jis").encode("UTF-8") for s in strings_ja] + expected_zh_cn = [ + s.decode("gb18030").encode("UTF-8") for s in strings_zh_cn + ] + expected_zh_tw = [s.decode("big5").encode("UTF-8") for s in strings_zh_tw] + expected_ko = [s.decode("euc_kr").encode("UTF-8") for s in strings_ko] + + with self.cached_session() as sess: + outputs_ja = string_ops.unicode_transcode( + strings_ja, + input_encoding="shift_jis", + output_encoding="UTF-8", + replacement_char=ord(" "), + replace_control_characters=False) + + outputs_zh_cn = string_ops.unicode_transcode( + strings_zh_cn, + input_encoding="gb18030", + output_encoding="UTF-8", + replacement_char=ord(" "), + replace_control_characters=False) + + outputs_zh_tw = string_ops.unicode_transcode( + strings_zh_tw, + input_encoding="big5", + output_encoding="UTF-8", + replacement_char=ord(" "), + replace_control_characters=False) + + outputs_ko = string_ops.unicode_transcode( + strings_ko, + input_encoding="euc_kr", + output_encoding="UTF-8", + replacement_char=ord(" "), + replace_control_characters=False) + + result_ja, result_zh_cn, result_zh_tw, result_ko = sess.run( + [outputs_ja, outputs_zh_cn, outputs_zh_tw, outputs_ko]) + + self.assertAllEqual(result_ja, expected_ja) + self.assertAllEqual(result_zh_cn, expected_zh_cn) + self.assertAllEqual(result_zh_tw, expected_zh_tw) + self.assertAllEqual(result_ko, expected_ko) + if __name__ == "__main__": test.main() diff --git a/tensorflow/python/kernel_tests/unique_op_test.py b/tensorflow/python/kernel_tests/unique_op_test.py index 316570e13e..f203263e0c 100644 --- a/tensorflow/python/kernel_tests/unique_op_test.py +++ b/tensorflow/python/kernel_tests/unique_op_test.py @@ -32,7 +32,7 @@ class UniqueTest(test.TestCase): x = np.random.randint(2, high=10, size=7000) with self.cached_session() as sess: y, idx = array_ops.unique(x) - tf_y, tf_idx = sess.run([y, idx]) + tf_y, tf_idx = self.evaluate([y, idx]) self.assertEqual(len(x), len(tf_idx)) self.assertEqual(len(tf_y), len(np.unique(x))) @@ -43,7 +43,7 @@ class UniqueTest(test.TestCase): x = np.random.randint(2, high=10, size=7000) with self.cached_session() as sess: y, idx = array_ops.unique(x, out_idx=dtypes.int64) - tf_y, tf_idx = sess.run([y, idx]) + tf_y, tf_idx = self.evaluate([y, idx]) self.assertEqual(len(x), len(tf_idx)) self.assertEqual(len(tf_y), len(np.unique(x))) @@ -55,7 +55,7 @@ class UniqueTest(test.TestCase): x = [chr(i) for i in indx] with self.cached_session() as sess: y, idx = array_ops.unique(x) - tf_y, tf_idx = sess.run([y, idx]) + tf_y, tf_idx = self.evaluate([y, idx]) self.assertEqual(len(x), len(tf_idx)) self.assertEqual(len(tf_y), len(np.unique(x))) @@ -67,9 +67,9 @@ class UniqueTest(test.TestCase): x = np.array([[1, 0, 0], [1, 0, 0], [2, 0, 0]]) with self.cached_session() as sess: y0, idx0 = gen_array_ops.unique_v2(x, axis=np.array([0], dtype)) - tf_y0, tf_idx0 = sess.run([y0, idx0]) + tf_y0, tf_idx0 = self.evaluate([y0, idx0]) y1, idx1 = gen_array_ops.unique_v2(x, axis=np.array([1], dtype)) - tf_y1, tf_idx1 = sess.run([y1, idx1]) + tf_y1, tf_idx1 = self.evaluate([y1, idx1]) self.assertAllEqual(tf_y0, np.array([[1, 0, 0], [2, 0, 0]])) self.assertAllEqual(tf_idx0, np.array([0, 0, 1])) self.assertAllEqual(tf_y1, np.array([[1, 0], [1, 0], [2, 0]])) @@ -81,7 +81,7 @@ class UniqueTest(test.TestCase): x = np.random.randint(2, high=10, size=7000) with self.cached_session() as sess: y, idx = gen_array_ops.unique_v2(x, axis=np.array([], np.int32)) - tf_y, tf_idx = sess.run([y, idx]) + tf_y, tf_idx = self.evaluate([y, idx]) self.assertEqual(len(x), len(tf_idx)) self.assertEqual(len(tf_y), len(np.unique(x))) @@ -95,7 +95,7 @@ class UniqueWithCountsTest(test.TestCase): x = np.random.randint(2, high=10, size=7000) with self.cached_session() as sess: y, idx, count = array_ops.unique_with_counts(x) - tf_y, tf_idx, tf_count = sess.run([y, idx, count]) + tf_y, tf_idx, tf_count = self.evaluate([y, idx, count]) self.assertEqual(len(x), len(tf_idx)) self.assertEqual(len(tf_y), len(np.unique(x))) @@ -108,7 +108,7 @@ class UniqueWithCountsTest(test.TestCase): x = np.random.randint(2, high=10, size=7000) with self.cached_session() as sess: y, idx, count = array_ops.unique_with_counts(x, out_idx=dtypes.int64) - tf_y, tf_idx, tf_count = sess.run([y, idx, count]) + tf_y, tf_idx, tf_count = self.evaluate([y, idx, count]) self.assertEqual(len(x), len(tf_idx)) self.assertEqual(len(tf_y), len(np.unique(x))) @@ -123,7 +123,7 @@ class UniqueWithCountsTest(test.TestCase): with self.cached_session() as sess: y, idx, count = array_ops.unique_with_counts(x) - tf_y, tf_idx, tf_count = sess.run([y, idx, count]) + tf_y, tf_idx, tf_count = self.evaluate([y, idx, count]) self.assertEqual(len(x), len(tf_idx)) self.assertEqual(len(tf_y), len(np.unique(x))) @@ -139,10 +139,10 @@ class UniqueWithCountsTest(test.TestCase): with self.cached_session() as sess: y0, idx0, count0 = gen_array_ops.unique_with_counts_v2( x, axis=np.array([0], dtype)) - tf_y0, tf_idx0, tf_count0 = sess.run([y0, idx0, count0]) + tf_y0, tf_idx0, tf_count0 = self.evaluate([y0, idx0, count0]) y1, idx1, count1 = gen_array_ops.unique_with_counts_v2( x, axis=np.array([1], dtype)) - tf_y1, tf_idx1, tf_count1 = sess.run([y1, idx1, count1]) + tf_y1, tf_idx1, tf_count1 = self.evaluate([y1, idx1, count1]) self.assertAllEqual(tf_y0, np.array([[1, 0, 0], [2, 0, 0]])) self.assertAllEqual(tf_idx0, np.array([0, 0, 1])) self.assertAllEqual(tf_count0, np.array([2, 1])) @@ -157,7 +157,7 @@ class UniqueWithCountsTest(test.TestCase): with self.cached_session() as sess: y, idx, count = gen_array_ops.unique_with_counts_v2( x, axis=np.array([], np.int32)) - tf_y, tf_idx, tf_count = sess.run([y, idx, count]) + tf_y, tf_idx, tf_count = self.evaluate([y, idx, count]) self.assertEqual(len(x), len(tf_idx)) self.assertEqual(len(tf_y), len(np.unique(x))) diff --git a/tensorflow/python/kernel_tests/unstack_op_test.py b/tensorflow/python/kernel_tests/unstack_op_test.py index 6aea42990a..f5ba475e7a 100644 --- a/tensorflow/python/kernel_tests/unstack_op_test.py +++ b/tensorflow/python/kernel_tests/unstack_op_test.py @@ -41,7 +41,7 @@ class UnstackOpTest(test.TestCase): def testSimple(self): np.random.seed(7) - with self.session(use_gpu=True): + with test_util.use_gpu(): for shape in (2,), (3,), (2, 3), (3, 2), (4, 3, 2): for dtype in [ np.bool, np.float16, np.float32, np.float64, np.int32, np.int64 @@ -53,14 +53,15 @@ class UnstackOpTest(test.TestCase): cs = array_ops.unstack(x, num=shape[0]) self.assertEqual(type(cs), list) self.assertEqual(len(cs), shape[0]) - cs = [c.eval() for c in cs] + cs = [self.evaluate(c) for c in cs] self.assertAllEqual(cs, data) def testSimpleGpu(self): if not test_util.is_gpu_available(): self.skipTest('No GPU available') + np.random.seed(7) - with self.session(use_gpu=True, force_gpu=True): + with test_util.force_gpu(): for shape in (2,), (3,), (2, 3), (3, 2), (4, 3, 2): for dtype in [np.float16, np.float32, np.float64, np.int32, np.int64]: data = np.random.randn(*shape).astype(dtype) @@ -70,9 +71,10 @@ class UnstackOpTest(test.TestCase): cs = array_ops.unstack(x, num=shape[0]) self.assertEqual(type(cs), list) self.assertEqual(len(cs), shape[0]) - cs = [c.eval() for c in cs] + cs = [self.evaluate(c) for c in cs] self.assertAllEqual(cs, data) + @test_util.run_deprecated_v1 def testGradientsAxis0(self): for shape in (2,), (3,), (2, 3), (3, 2), (4, 3, 2): data = np.random.randn(*shape) @@ -85,6 +87,7 @@ class UnstackOpTest(test.TestCase): shapes[i]) self.assertLess(err, 1e-6) + @test_util.run_deprecated_v1 def testGradientsAxis1(self): for shape in (2, 3), (3, 2), (4, 3, 2): data = np.random.randn(*shape) @@ -98,6 +101,7 @@ class UnstackOpTest(test.TestCase): out_shape) self.assertLess(err, 1e-6) + @test_util.run_deprecated_v1 def testInferNum(self): with self.cached_session(): for shape in (2,), (3,), (2, 3), (3, 2), (4, 3, 2): @@ -106,16 +110,19 @@ class UnstackOpTest(test.TestCase): self.assertEqual(type(cs), list) self.assertEqual(len(cs), shape[0]) + @test_util.run_deprecated_v1 def testCannotInferNumFromUnknownShape(self): x = array_ops.placeholder(np.float32) with self.assertRaisesRegexp(ValueError, r'Cannot infer num from shape '): array_ops.unstack(x) + @test_util.run_deprecated_v1 def testUnknownShapeOkWithNum(self): x = array_ops.placeholder(np.float32) array_ops.unstack(x, num=2) + @test_util.run_deprecated_v1 def testCannotInferNumFromNoneShape(self): x = array_ops.placeholder(np.float32, shape=(None,)) with self.assertRaisesRegexp(ValueError, @@ -131,15 +138,13 @@ class UnstackOpTest(test.TestCase): for j in range(-i, i): expected = np_split_squeeze(a, j) - with self.cached_session() as sess: - actual_unstack = sess.run(array_ops.unstack(a, axis=j)) + actual_unstack = self.evaluate(array_ops.unstack(a, axis=j)) self.assertAllEqual(expected, actual_unstack) def testAxis0Default(self): - with self.cached_session() as sess: - a = constant_op.constant([[1, 2, 3], [4, 5, 6]], name='a') - unstacked = sess.run(array_ops.unstack(a)) + a = constant_op.constant([[1, 2, 3], [4, 5, 6]], name='a') + unstacked = self.evaluate(array_ops.unstack(a)) self.assertEqual(len(unstacked), 2) self.assertAllEqual(unstacked[0], [1, 2, 3]) @@ -156,10 +161,9 @@ class UnstackOpTest(test.TestCase): array_ops.unstack(a, axis=-3) def testZeroLengthDim(self): - with self.cached_session(): - x = array_ops.zeros(shape=(0, 1, 2)) - y = array_ops.unstack(x, axis=1)[0].eval() - self.assertEqual(y.shape, (0, 2)) + x = array_ops.zeros(shape=(0, 1, 2)) + y = self.evaluate(array_ops.unstack(x, axis=1)[0]) + self.assertEqual(y.shape, (0, 2)) if __name__ == '__main__': diff --git a/tensorflow/python/kernel_tests/variable_ops_test.py b/tensorflow/python/kernel_tests/variable_ops_test.py index 3d2f8b6155..0f3e261992 100644 --- a/tensorflow/python/kernel_tests/variable_ops_test.py +++ b/tensorflow/python/kernel_tests/variable_ops_test.py @@ -24,6 +24,7 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gen_state_ops from tensorflow.python.ops import math_ops @@ -46,7 +47,7 @@ class VariableOpTest(test.TestCase): p = state_ops.variable_op(x.shape, tftype) op = state_ops.assign(p, x) op.op.run() - return p.eval() + return self.evaluate(p) def _testTypes(self, vals): for dtype in [np.float32, np.float64, np.int32, np.int64]: @@ -59,15 +60,18 @@ class VariableOpTest(test.TestCase): # that Variable and Assign have GPU implementations for matching tf. self.assertAllEqual(x, self._initFetch(x, tftype, use_gpu=True)) + @test_util.run_deprecated_v1 def testBasic(self): self._testTypes(np.arange(0, 20).reshape([4, 5])) + @test_util.run_deprecated_v1 def testset_shape(self): p = state_ops.variable_op([1, 2], dtypes.float32) self.assertEqual([1, 2], p.get_shape()) p = state_ops.variable_op([1, 2], dtypes.float32, set_shape=False) self.assertEqual(tensor_shape.unknown_shape(), p.get_shape()) + @test_util.run_deprecated_v1 def testAssign(self): value = np.array([[42.0, 43.0]]) var = state_ops.variable_op(value.shape, dtypes.float32) @@ -75,6 +79,7 @@ class VariableOpTest(test.TestCase): assigned = state_ops.assign(var, value) self.assertShapeEqual(value, assigned) + @test_util.run_deprecated_v1 def testAssignNoValidateShape(self): value = np.array([[42.0, 43.0]]) var = state_ops.variable_op(value.shape, dtypes.float32) @@ -82,6 +87,7 @@ class VariableOpTest(test.TestCase): assigned = state_ops.assign(var, value, validate_shape=False) self.assertShapeEqual(value, assigned) + @test_util.run_deprecated_v1 def testAssignNoVarShape(self): value = np.array([[42.0, 43.0]]) var = state_ops.variable_op(value.shape, dtypes.float32, set_shape=False) @@ -89,6 +95,7 @@ class VariableOpTest(test.TestCase): assigned = state_ops.assign(var, value) self.assertShapeEqual(value, assigned) + @test_util.run_deprecated_v1 def testAssignNoVarShapeNoValidateShape(self): value = np.array([[42.0, 43.0]]) var = state_ops.variable_op(value.shape, dtypes.float32, set_shape=False) @@ -101,6 +108,7 @@ class VariableOpTest(test.TestCase): self.assertEqual(tensor_shape.unknown_shape(), tensor.get_shape()) return tensor + @test_util.run_deprecated_v1 def testAssignNoValueShape(self): value = self._NewShapelessTensor() shape = [1, 2] @@ -109,6 +117,7 @@ class VariableOpTest(test.TestCase): self.assertEqual(shape, var.get_shape()) self.assertEqual(shape, assigned.get_shape()) + @test_util.run_deprecated_v1 def testAssignNoValueShapeNoValidateShape(self): value = self._NewShapelessTensor() shape = [1, 2] @@ -117,6 +126,7 @@ class VariableOpTest(test.TestCase): assigned = state_ops.assign(var, value, validate_shape=False) self.assertEqual(tensor_shape.unknown_shape(), assigned.get_shape()) + @test_util.run_deprecated_v1 def testAssignNoShape(self): with self.cached_session(): value = self._NewShapelessTensor() @@ -125,6 +135,7 @@ class VariableOpTest(test.TestCase): self.assertEqual(tensor_shape.unknown_shape(), state_ops.assign(var, value).get_shape()) + @test_util.run_deprecated_v1 def testAssignNoShapeNoValidateShape(self): with self.cached_session(): value = self._NewShapelessTensor() @@ -135,6 +146,7 @@ class VariableOpTest(test.TestCase): state_ops.assign( var, value, validate_shape=False).get_shape()) + @test_util.run_deprecated_v1 def testAssignUpdate(self): var = state_ops.variable_op([1, 2], dtypes.float32) added = state_ops.assign_add(var, [[2.0, 3.0]]) @@ -142,6 +154,7 @@ class VariableOpTest(test.TestCase): subbed = state_ops.assign_sub(var, [[12.0, 13.0]]) self.assertEqual([1, 2], subbed.get_shape()) + @test_util.run_deprecated_v1 def testAssignUpdateNoVarShape(self): var = state_ops.variable_op([1, 2], dtypes.float32, set_shape=False) added = state_ops.assign_add(var, [[2.0, 3.0]]) @@ -149,6 +162,7 @@ class VariableOpTest(test.TestCase): subbed = state_ops.assign_sub(var, [[12.0, 13.0]]) self.assertEqual([1, 2], subbed.get_shape()) + @test_util.run_deprecated_v1 def testAssignUpdateNoValueShape(self): var = state_ops.variable_op([1, 2], dtypes.float32) added = state_ops.assign_add(var, self._NewShapelessTensor()) @@ -156,6 +170,7 @@ class VariableOpTest(test.TestCase): subbed = state_ops.assign_sub(var, self._NewShapelessTensor()) self.assertEqual([1, 2], subbed.get_shape()) + @test_util.run_deprecated_v1 def testAssignUpdateNoShape(self): var = state_ops.variable_op([1, 2], dtypes.float32, set_shape=False) added = state_ops.assign_add(var, self._NewShapelessTensor()) @@ -163,24 +178,27 @@ class VariableOpTest(test.TestCase): subbed = state_ops.assign_sub(var, self._NewShapelessTensor()) self.assertEqual(tensor_shape.unknown_shape(), subbed.get_shape()) + @test_util.run_deprecated_v1 def testTemporaryVariable(self): - with self.test_session(use_gpu=True): + with test_util.use_gpu(): var = gen_state_ops.temporary_variable( [1, 2], dtypes.float32, var_name="foo") var = state_ops.assign(var, [[4.0, 5.0]]) var = state_ops.assign_add(var, [[6.0, 7.0]]) final = gen_state_ops.destroy_temporary_variable(var, var_name="foo") - self.assertAllClose([[10.0, 12.0]], final.eval()) + self.assertAllClose([[10.0, 12.0]], self.evaluate(final)) + @test_util.run_deprecated_v1 def testDestroyNonexistentTemporaryVariable(self): - with self.test_session(use_gpu=True): + with test_util.use_gpu(): var = gen_state_ops.temporary_variable([1, 2], dtypes.float32) final = gen_state_ops.destroy_temporary_variable(var, var_name="bad") with self.assertRaises(errors.NotFoundError): - final.eval() + self.evaluate(final) + @test_util.run_deprecated_v1 def testDuplicateTemporaryVariable(self): - with self.test_session(use_gpu=True): + with test_util.use_gpu(): var1 = gen_state_ops.temporary_variable( [1, 2], dtypes.float32, var_name="dup") var1 = state_ops.assign(var1, [[1.0, 2.0]]) @@ -189,48 +207,53 @@ class VariableOpTest(test.TestCase): var2 = state_ops.assign(var2, [[3.0, 4.0]]) final = var1 + var2 with self.assertRaises(errors.AlreadyExistsError): - final.eval() + self.evaluate(final) + @test_util.run_deprecated_v1 def testDestroyTemporaryVariableTwice(self): - with self.test_session(use_gpu=True): + with test_util.use_gpu(): var = gen_state_ops.temporary_variable([1, 2], dtypes.float32) val1 = gen_state_ops.destroy_temporary_variable(var, var_name="dup") val2 = gen_state_ops.destroy_temporary_variable(var, var_name="dup") final = val1 + val2 with self.assertRaises(errors.NotFoundError): - final.eval() + self.evaluate(final) + @test_util.run_deprecated_v1 def testTemporaryVariableNoLeak(self): - with self.test_session(use_gpu=True): + with test_util.use_gpu(): var = gen_state_ops.temporary_variable( [1, 2], dtypes.float32, var_name="bar") final = array_ops.identity(var) - final.eval() + self.evaluate(final) + @test_util.run_deprecated_v1 def testTwoTemporaryVariablesNoLeaks(self): - with self.test_session(use_gpu=True): + with test_util.use_gpu(): var1 = gen_state_ops.temporary_variable( [1, 2], dtypes.float32, var_name="var1") var2 = gen_state_ops.temporary_variable( [1, 2], dtypes.float32, var_name="var2") final = var1 + var2 - final.eval() + self.evaluate(final) + @test_util.run_deprecated_v1 def testAssignDependencyAcrossDevices(self): - with self.test_session(use_gpu=True): + with test_util.use_gpu(): # The variable and an op to increment it are on the GPU. var = state_ops.variable_op([1], dtypes.float32) - state_ops.assign(var, [1.0]).eval() + self.evaluate(state_ops.assign(var, [1.0])) increment = state_ops.assign_add(var, [1.0]) with ops.control_dependencies([increment]): - with ops.device("/cpu:0"): + with test_util.force_cpu(): # This mul op is pinned to the CPU, but reads the variable from the # GPU. The test ensures that the dependency on 'increment' is still # honored, i.e., the Send and Recv from GPU to CPU should take place # only after the increment. result = math_ops.multiply(var, var) - self.assertAllClose([4.0], result.eval()) + self.assertAllClose([4.0], self.evaluate(result)) + @test_util.run_deprecated_v1 def testIsVariableInitialized(self): for use_gpu in [True, False]: with self.test_session(use_gpu=use_gpu): diff --git a/tensorflow/python/kernel_tests/variable_scope_test.py b/tensorflow/python/kernel_tests/variable_scope_test.py index 2ba064f8a5..44d4bd5e30 100644 --- a/tensorflow/python/kernel_tests/variable_scope_test.py +++ b/tensorflow/python/kernel_tests/variable_scope_test.py @@ -152,6 +152,7 @@ class VariableScopeTest(test.TestCase): # TypeError: Fetch argument # has invalid type , must be a string or Tensor. # (Can not convert a ResourceVariable into a Tensor or Operation.) + @test_util.run_deprecated_v1 def testStringDefaultInitializer(self): with self.cached_session(): v = variable_scope.get_variable("string", shape=[], dtype=dtypes.string) @@ -308,9 +309,9 @@ class VariableScopeTest(test.TestCase): self.evaluate(variables_lib.global_variables_initializer()) self.assertAllEqual(self.evaluate(x.value()), self.evaluate(y.value())) - # TODO(alive): support variable partitioning/caching in eager mode. # TODO(mihaimaruseac): Not converted to use wrap_function because of # InvalidArgumentError: /job:moo/replica:0/task:0/device:CPU:0 unknown device. + @test_util.run_deprecated_v1 def testVarScopeCachingDevice(self): with self.cached_session(): caching_device = "/job:moo" @@ -425,6 +426,7 @@ class VariableScopeTest(test.TestCase): # invalid type , must # be a string or Tensor. (Can not convert a ResourceVariable into a Tensor or # Operation.) + @test_util.run_deprecated_v1 def testControlDeps(self): with self.cached_session() as sess: v0 = variable_scope.get_variable( @@ -435,22 +437,23 @@ class VariableScopeTest(test.TestCase): add = v1 + v0 # v0 should be uninitialized. with self.assertRaisesRegexp(errors.OpError, "uninitialized"): - sess.run(v0) + self.evaluate(v0) # We should be able to initialize and run v1 without initializing # v0, even if the variable was created with a control dep on v0. - sess.run(v1.initializer) - self.assertEqual(1, sess.run(v1)) + self.evaluate(v1.initializer) + self.assertEqual(1, self.evaluate(v1)) # v0 should still be uninitialized. with self.assertRaisesRegexp(errors.OpError, "uninitialized"): - sess.run(v0) + self.evaluate(v0) with self.assertRaisesRegexp(errors.OpError, "uninitialized"): - sess.run(add) + self.evaluate(add) # If we initialize v0 we should be able to run 'add'. - sess.run(v0.initializer) - sess.run(add) + self.evaluate(v0.initializer) + self.evaluate(add) # TODO(mihaimaruseac): Not converted to use wrap_function because of # AssertionError: True is not false (last assertFalse) + @test_util.run_deprecated_v1 def testEnableResourceVariables(self): old = variable_scope._DEFAULT_USE_RESOURCE try: @@ -465,6 +468,7 @@ class VariableScopeTest(test.TestCase): # TODO(mihaimaruseac): Not converted to use wrap_function because of # TypeError: Fetch argument None has invalid type + @test_util.run_deprecated_v1 def testControlFlow(self): with self.cached_session() as sess: v0 = variable_scope.get_variable( @@ -490,19 +494,19 @@ class VariableScopeTest(test.TestCase): v2 = var_dict["v2"] # We should be able to initialize and run v1 and v2 without initializing # v0, even if the variable was created with a control dep on v0. - sess.run(v1.initializer) - self.assertEqual([1], sess.run(v1)) - sess.run(v2.initializer) - self.assertEqual([2], sess.run(v2)) + self.evaluate(v1.initializer) + self.assertEqual([1], self.evaluate(v1)) + self.evaluate(v2.initializer) + self.assertEqual([2], self.evaluate(v2)) # v0 should still be uninitialized. with self.assertRaisesRegexp(errors.OpError, "uninitialized"): - sess.run(v0) + self.evaluate(v0) # We should not be able to run 'add' yet. with self.assertRaisesRegexp(errors.OpError, "uninitialized"): - sess.run(add) + self.evaluate(add) # If we initialize v0 we should be able to run 'add'. - sess.run(v0.initializer) - sess.run(add) + self.evaluate(v0.initializer) + self.evaluate(add) # TODO(mihaimaruseac): Not converted to use wrap_function because of # TypeError: Expected tf.group() expected Tensor arguments not 'None' with @@ -649,7 +653,7 @@ class VariableScopeTest(test.TestCase): "testVarScopeGetOrCreateReuse_bar", reuse=variable_scope.AUTO_REUSE): _ = variable_scope.get_variable("var", []) - self.assertEqual(value, x.eval()) + self.assertEqual(value, self.evaluate(x)) test_value(42.) # Variable is created. test_value(13.) # Variable is reused hereafter. @@ -1149,6 +1153,7 @@ class VariableScopeTest(test.TestCase): # TODO(mihaimaruseac): Not converted to use wrap_function because of # obtaining different results in the eager case compared to the graph one + @test_util.run_deprecated_v1 def testGetCollection(self): with self.cached_session(): _ = variable_scope.get_variable("testGetCollection_a", []) @@ -1205,6 +1210,7 @@ class VariableScopeTest(test.TestCase): # TODO(mihaimaruseac): Not converted to use wrap_function because of # obtaining different results in the eager case compared to the graph one + @test_util.run_deprecated_v1 def testGetTrainableVariablesWithGetVariable(self): with self.cached_session(): _ = variable_scope.get_variable("testGetTrainableVariables_a", []) @@ -1243,6 +1249,7 @@ class VariableScopeTest(test.TestCase): # TODO(mihaimaruseac): Not converted to use wrap_function because of # obtaining different results in the eager case compared to the graph one + @test_util.run_deprecated_v1 def testGetTrainableVariablesWithVariable(self): with self.cached_session(): _ = variable_scope.variable(1.0, name="testGetTrainableVariables_a") @@ -1284,6 +1291,7 @@ class VariableScopeTest(test.TestCase): # TODO(mihaimaruseac): Not converted to use wrap_function because of # obtaining different results in the eager case compared to the graph one + @test_util.run_deprecated_v1 def testGetGlobalVariables(self): with self.cached_session(): _ = variable_scope.get_variable("testGetGlobalVariables_a", []) @@ -1296,6 +1304,7 @@ class VariableScopeTest(test.TestCase): # TODO(mihaimaruseac): Not converted to use wrap_function because of # obtaining different results in the eager case compared to the graph one + @test_util.run_deprecated_v1 def testGetLocalVariables(self): with self.cached_session(): _ = variable_scope.get_variable( @@ -1313,6 +1322,28 @@ class VariableScopeTest(test.TestCase): # Ensure it is possible to do get_variable with a _ref dtype passed in. _ = variable_scope.get_variable("w", shape=[5, 6], dtype=v.dtype) + @test_util.run_in_graph_and_eager_modes + @run_inside_wrap_function_in_eager_mode + def testGetVariableWithInitializerWhichTakesNoArgs(self): + v = variable_scope.get_variable("foo", initializer=lambda: [2]) + self.assertEqual(v.name, "foo:0") + + @test_util.run_in_graph_and_eager_modes + @run_inside_wrap_function_in_eager_mode + def testGetVariableWithInitializerWhichTakesOptionalArgs(self): + v = variable_scope.get_variable("foo", initializer=lambda x=True: [2]) + self.assertEqual(v.name, "foo:0") + + @test_util.run_in_graph_and_eager_modes + @run_inside_wrap_function_in_eager_mode + def testGetVariableWithInitializerWhichTakesUnprovidedArgsAndNoShape(self): + with self.assertRaisesRegexp( + ValueError, + "The initializer passed is not valid. It should be a callable with no " + "arguments and the shape should not be provided or an instance of " + "`tf.keras.initializers.*' and `shape` should be fully defined."): + variable_scope.get_variable("foo", initializer=lambda x: [2]) + @test_util.run_in_graph_and_eager_modes @run_inside_wrap_function_in_eager_mode def testTwoGraphs(self): @@ -1349,6 +1380,7 @@ class VariableScopeWithPartitioningTest(test.TestCase): # TODO(mihaimaruseac): Not converted to use wrap_function because of # obtaining different results in the eager case compared to the graph one + @test_util.run_deprecated_v1 def testResultNameMatchesRequested(self): with variable_scope.variable_scope( "scope0", partitioner=axis0_into2_partitioner): @@ -1404,6 +1436,14 @@ class VariableScopeWithPartitioningTest(test.TestCase): v_reused = variable_scope.get_variable("name0") self.assertEqual(v, v_reused) + def testNoReuseInEagerByDefault(self): + with context.eager_mode(): + with variable_scope.variable_scope( + "scope0", partitioner=axis0_into2_partitioner): + v1 = variable_scope.get_variable("name0", shape=(3, 1, 1)) + v2 = variable_scope.get_variable("name0", shape=(3, 1, 1)) + self.assertIsNot(v1, v2) + @test_util.run_in_graph_and_eager_modes @run_inside_wrap_function_in_eager_mode def testPropagatePartitionerOnReopening(self): @@ -1415,6 +1455,7 @@ class VariableScopeWithPartitioningTest(test.TestCase): # TODO(mihaimaruseac): Not converted to use wrap_function because of # obtaining different results in the eager case compared to the graph one + @test_util.run_deprecated_v1 def testScalarIgnoresPartitioner(self): with variable_scope.variable_scope( "scope0", partitioner=axis0_into2_partitioner): @@ -1459,6 +1500,10 @@ class VariableScopeWithPartitioningTest(test.TestCase): def testPartitionConcatenatesAlongCorrectAxisResource(self): self._testPartitionConcatenatesAlongCorrectAxis(use_resource=True) + def testPartitionConcatenatesAlongCorrectAxisResourceInEager(self): + with context.eager_mode(): + self._testPartitionConcatenatesAlongCorrectAxis(use_resource=True) + class VariableScopeWithCustomGetterTest(test.TestCase): @@ -1550,6 +1595,7 @@ class VariableScopeWithCustomGetterTest(test.TestCase): # dtype=float32> cannot be interpreted as a Tensor. (Tensor # Tensor("custom_getter/add:0", shape=(1, 2, 3), dtype=float32) is not an # element of this graph.) + @test_util.run_deprecated_v1 def testGetterThatCreatesTwoVariablesAndSumsThem(self): def custom_getter(getter, name, *args, **kwargs): @@ -1569,7 +1615,7 @@ class VariableScopeWithCustomGetterTest(test.TestCase): self.assertEqual("custom_getter/add:0", v.name) with self.cached_session() as sess: variables_lib.global_variables_initializer().run() - np_vars, np_v = sess.run([true_vars, v]) + np_vars, np_v = self.evaluate([true_vars, v]) self.assertAllClose(np_v, sum(np_vars)) # TODO(mihaimaruseac): Not converted to use wrap_function because of @@ -1577,6 +1623,7 @@ class VariableScopeWithCustomGetterTest(test.TestCase): # dtype=float32> cannot be interpreted as a Tensor. (Tensor # Tensor("sum_getter_2/add:0", shape=(1, 2, 3), dtype=float32) is not an # element of this graph.) + @test_util.run_deprecated_v1 def testNestedCustomGetters(self): def sum_getter(getter, name, *args, **kwargs): @@ -1614,7 +1661,7 @@ class VariableScopeWithCustomGetterTest(test.TestCase): with self.cached_session() as sess: variables_lib.global_variables_initializer().run() - np_vars, np_v = sess.run([true_vars, v]) + np_vars, np_v = self.evaluate([true_vars, v]) # take products of sums of products self.assertAllClose( np_v, (((np_vars[0] * np_vars[1]) + (np_vars[2] * np_vars[3])) + ( diff --git a/tensorflow/python/kernel_tests/variables_test.py b/tensorflow/python/kernel_tests/variables_test.py index b3eebf8316..08d885e8a8 100644 --- a/tensorflow/python/kernel_tests/variables_test.py +++ b/tensorflow/python/kernel_tests/variables_test.py @@ -18,6 +18,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import functools import operator import numpy as np @@ -27,6 +28,7 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors_impl from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import gen_state_ops @@ -41,6 +43,7 @@ from tensorflow.python.util import compat class VariablesTestCase(test.TestCase): + @test_util.run_deprecated_v1 def testInitialization(self): with self.cached_session(): var0 = variables.VariableV1(0.0) @@ -58,16 +61,17 @@ class VariablesTestCase(test.TestCase): self.assertEqual([], var1.shape) with self.assertRaisesOpError("Attempting to use uninitialized value"): - var0.eval() + self.evaluate(var0) with self.assertRaisesOpError("Attempting to use uninitialized value"): - var1.eval() + self.evaluate(var1) variables.global_variables_initializer().run() - self.assertAllClose(0.0, var0.eval()) - self.assertAllClose(1.1, var1.eval()) + self.assertAllClose(0.0, self.evaluate(var0)) + self.assertAllClose(1.1, self.evaluate(var1)) + @test_util.run_deprecated_v1 def testInitializationOrder(self): with self.cached_session(): rnd = variables.Variable(random_ops.random_uniform([3, 6]), name="rnd") @@ -94,8 +98,9 @@ class VariablesTestCase(test.TestCase): variables.global_variables_initializer().run() - self.assertAllClose(rnd.eval(), dep.eval()) - self.assertAllClose(rnd.eval() + dep.eval() + 2.0, depdep.eval()) + self.assertAllClose(rnd.eval(), self.evaluate(dep)) + self.assertAllClose(rnd.eval() + self.evaluate(dep) + 2.0, + self.evaluate(depdep)) def testIterable(self): with self.assertRaisesRegexp(TypeError, "not iterable"): @@ -105,6 +110,7 @@ class VariablesTestCase(test.TestCase): for _ in variables.Variable([0.0, 1.0]): pass + @test_util.run_deprecated_v1 def testAssignments(self): with self.cached_session(): var = variables.Variable(0.0) @@ -112,17 +118,18 @@ class VariablesTestCase(test.TestCase): minus_one = var.assign_sub(2.0) four = var.assign(4.0) variables.global_variables_initializer().run() - self.assertAllClose(0.0, var.eval()) + self.assertAllClose(0.0, self.evaluate(var)) - self.assertAllClose(1.0, plus_one.eval()) - self.assertAllClose(1.0, var.eval()) + self.assertAllClose(1.0, self.evaluate(plus_one)) + self.assertAllClose(1.0, self.evaluate(var)) - self.assertAllClose(-1.0, minus_one.eval()) - self.assertAllClose(-1.0, var.eval()) + self.assertAllClose(-1.0, self.evaluate(minus_one)) + self.assertAllClose(-1.0, self.evaluate(var)) - self.assertAllClose(4.0, four.eval()) - self.assertAllClose(4.0, var.eval()) + self.assertAllClose(4.0, self.evaluate(four)) + self.assertAllClose(4.0, self.evaluate(var)) + @test_util.run_deprecated_v1 def testResourceAssignments(self): with self.session(use_gpu=True): var = resource_variable_ops.ResourceVariable(0.0) @@ -130,16 +137,16 @@ class VariablesTestCase(test.TestCase): minus_one = var.assign_sub(2.0) four = var.assign(4.0) variables.global_variables_initializer().run() - self.assertAllClose(0.0, var.eval()) + self.assertAllClose(0.0, self.evaluate(var)) - plus_one.eval() - self.assertAllClose(1.0, var.eval()) + self.evaluate(plus_one) + self.assertAllClose(1.0, self.evaluate(var)) - minus_one.eval() - self.assertAllClose(-1.0, var.eval()) + self.evaluate(minus_one) + self.assertAllClose(-1.0, self.evaluate(var)) - four.eval() - self.assertAllClose(4.0, var.eval()) + self.evaluate(four) + self.assertAllClose(4.0, self.evaluate(var)) def testZeroSizeStringAssign(self): with self.cached_session() as sess: @@ -148,10 +155,10 @@ class VariablesTestCase(test.TestCase): name="foo", trainable=False, collections=[ops.GraphKeys.LOCAL_VARIABLES]) - sess.run(variables.local_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) old_value = array.value() copy_op = array.assign(old_value) - self.assertEqual([], list(sess.run(copy_op))) + self.assertEqual([], list(self.evaluate(copy_op))) def _countUpToTest(self, dtype): with self.cached_session(): @@ -160,31 +167,34 @@ class VariablesTestCase(test.TestCase): count_up_to = var.count_up_to(3) variables.global_variables_initializer().run() - self.assertEqual(0, var.eval()) + self.assertEqual(0, self.evaluate(var)) - self.assertEqual(0, count_up_to.eval()) - self.assertEqual(1, var.eval()) + self.assertEqual(0, self.evaluate(count_up_to)) + self.assertEqual(1, self.evaluate(var)) - self.assertEqual(1, count_up_to.eval()) - self.assertEqual(2, var.eval()) + self.assertEqual(1, self.evaluate(count_up_to)) + self.assertEqual(2, self.evaluate(var)) - self.assertEqual(2, count_up_to.eval()) - self.assertEqual(3, var.eval()) + self.assertEqual(2, self.evaluate(count_up_to)) + self.assertEqual(3, self.evaluate(var)) with self.assertRaisesOpError("Reached limit of 3"): - count_up_to.eval() - self.assertEqual(3, var.eval()) + self.evaluate(count_up_to) + self.assertEqual(3, self.evaluate(var)) with self.assertRaisesOpError("Reached limit of 3"): - count_up_to.eval() - self.assertEqual(3, var.eval()) + self.evaluate(count_up_to) + self.assertEqual(3, self.evaluate(var)) + @test_util.run_deprecated_v1 def testCountUpToInt32(self): self._countUpToTest(dtypes.int32) + @test_util.run_deprecated_v1 def testCountUpToInt64(self): self._countUpToTest(dtypes.int64) + @test_util.run_deprecated_v1 def testControlDepsNone(self): with self.cached_session(): c = constant_op.constant(1.0) @@ -198,6 +208,7 @@ class VariablesTestCase(test.TestCase): self.assertEqual([], var_x.value().op.control_inputs) self.assertEqual([], var_x._ref().op.control_inputs) # pylint: disable=protected-access + @test_util.run_deprecated_v1 def testControlFlow(self): with self.cached_session() as sess: v0 = variables.Variable(0, name="v0") @@ -220,20 +231,21 @@ class VariablesTestCase(test.TestCase): v2 = var_dict["v2"] # We should be able to initialize and run v1 and v2 without initializing # v0, even if the variable was created with a control dep on v0. - sess.run(v1.initializer) - self.assertEqual([1], sess.run(v1)) - sess.run(v2.initializer) - self.assertEqual([2], sess.run(v2)) + self.evaluate(v1.initializer) + self.assertEqual([1], self.evaluate(v1)) + self.evaluate(v2.initializer) + self.assertEqual([2], self.evaluate(v2)) # v0 should still be uninitialized. with self.assertRaisesRegexp(errors_impl.OpError, "uninitialized"): - sess.run(v0) + self.evaluate(v0) # We should not be able to run 'add' yet. with self.assertRaisesRegexp(errors_impl.OpError, "uninitialized"): - sess.run(add) + self.evaluate(add) # If we initialize v0 we should be able to run 'add'. - sess.run(v0.initializer) - sess.run(add) + self.evaluate(v0.initializer) + self.evaluate(add) + @test_util.run_deprecated_v1 def testControlFlowInitialization(self): """Expects an error if an initializer is in a control-flow scope.""" def cond(i, _): @@ -247,15 +259,17 @@ class VariablesTestCase(test.TestCase): with self.assertRaisesRegexp(ValueError, "inside a control-flow"): control_flow_ops.while_loop(cond, body, [0, 0]) + @test_util.run_deprecated_v1 def testUseVariableAsTensor(self): with self.cached_session(): var_x = variables.Variable(2.0) var_y = variables.Variable(3.0) variables.global_variables_initializer().run() - self.assertAllClose(2.0, var_x.eval()) - self.assertAllClose(3.0, var_y.eval()) + self.assertAllClose(2.0, self.evaluate(var_x)) + self.assertAllClose(3.0, self.evaluate(var_y)) self.assertAllClose(5.0, math_ops.add(var_x, var_y).eval()) + @test_util.run_deprecated_v1 def testZeroSizeVarSameAsConst(self): with self.cached_session(): zero_size_var = variables.Variable(array_ops.zeros([0, 2])) @@ -264,10 +278,11 @@ class VariablesTestCase(test.TestCase): const_mul = math_ops.matmul( zero_size_const, zero_size_const, transpose_b=True) variables.global_variables_initializer().run() - variable_output = variable_mul.eval() + variable_output = self.evaluate(variable_mul) self.assertAllClose(const_mul.eval(), variable_output) self.assertAllClose([[0., 0.], [0., 0.]], variable_output) + @test_util.run_deprecated_v1 def testCachingDevice(self): with self.cached_session(): var = variables.Variable(2.0) @@ -278,6 +293,7 @@ class VariablesTestCase(test.TestCase): self.assertFalse(var_cached.device.startswith("/job:foo")) self.assertTrue(var_cached.value().device.startswith("/job:foo")) + @test_util.run_deprecated_v1 def testCollections(self): with self.cached_session(): var_x = variables.VariableV1(2.0) @@ -293,6 +309,7 @@ class VariablesTestCase(test.TestCase): variables.global_variables()) self.assertEqual([var_x, var_z, var_t], variables.trainable_variables()) + @test_util.run_deprecated_v1 def testCollectionsWithScope(self): with self.cached_session(): with ops.name_scope("scope_1"): @@ -308,6 +325,13 @@ class VariablesTestCase(test.TestCase): self.assertEqual([var_x], variables.trainable_variables("scope_1")) self.assertEqual([var_y], variables.trainable_variables("scope_2")) + def testOperatorWrapping(self): + for attr in functools.WRAPPER_ASSIGNMENTS: + self.assertEqual( + getattr(variables.Variable.__add__, attr), + getattr(ops.Tensor.__add__, attr)) + + @test_util.run_deprecated_v1 def testOperators(self): with self.cached_session(): var_f = variables.Variable([2.0]) @@ -349,54 +373,46 @@ class VariablesTestCase(test.TestCase): rmatmul = var_m.__rmatmul__([[10.0], [20.0]]) variables.global_variables_initializer().run() - self.assertAllClose([2.0], add.eval()) - self.assertAllClose([3.0], radd.eval()) - self.assertAllClose([1.0], sub.eval()) - self.assertAllClose([-1.0], rsub.eval()) - self.assertAllClose([20.0], mul.eval()) - self.assertAllClose([20.0], rmul.eval()) - self.assertAllClose([0.2], div.eval()) - self.assertAllClose([5.0], rdiv.eval()) - self.assertAllClose([-2.0], neg.eval()) - self.assertAllClose([2.0], abs_v.eval()) - self.assertAllClose([True], lt.eval()) - self.assertAllClose([False], rlt.eval()) - self.assertAllClose([True], le.eval()) - self.assertAllClose([True], rle.eval()) - self.assertAllClose([False], gt.eval()) - self.assertAllClose([True], rgt.eval()) - self.assertAllClose([True], ge.eval()) - self.assertAllClose([True], rge.eval()) - - self.assertAllClose([6], mod.eval()) - self.assertAllClose([3], rmod.eval()) - - self.assertAllClose([True, False], and_v.eval()) - self.assertAllClose([True, True], or_v.eval()) - self.assertAllClose([True, False], xor_v.eval()) - self.assertAllClose([False, True], invert_v.eval()) - - self.assertAllClose(rnd[2, 0:0], slice_v.eval()) - - self.assertAllClose([[80.0]], matmul.eval()) - self.assertAllClose([[20.0, 30.0], [40.0, 60.0]], rmatmul.eval()) - + self.assertAllClose([2.0], self.evaluate(add)) + self.assertAllClose([3.0], self.evaluate(radd)) + self.assertAllClose([1.0], self.evaluate(sub)) + self.assertAllClose([-1.0], self.evaluate(rsub)) + self.assertAllClose([20.0], self.evaluate(mul)) + self.assertAllClose([20.0], self.evaluate(rmul)) + self.assertAllClose([0.2], self.evaluate(div)) + self.assertAllClose([5.0], self.evaluate(rdiv)) + self.assertAllClose([-2.0], self.evaluate(neg)) + self.assertAllClose([2.0], self.evaluate(abs_v)) + self.assertAllClose([True], self.evaluate(lt)) + self.assertAllClose([False], self.evaluate(rlt)) + self.assertAllClose([True], self.evaluate(le)) + self.assertAllClose([True], self.evaluate(rle)) + self.assertAllClose([False], self.evaluate(gt)) + self.assertAllClose([True], self.evaluate(rgt)) + self.assertAllClose([True], self.evaluate(ge)) + self.assertAllClose([True], self.evaluate(rge)) + + self.assertAllClose([6], self.evaluate(mod)) + self.assertAllClose([3], self.evaluate(rmod)) + + self.assertAllClose([True, False], self.evaluate(and_v)) + self.assertAllClose([True, True], self.evaluate(or_v)) + self.assertAllClose([True, False], self.evaluate(xor_v)) + self.assertAllClose([False, True], self.evaluate(invert_v)) + + self.assertAllClose(rnd[2, 0:0], self.evaluate(slice_v)) + + self.assertAllClose([[80.0]], self.evaluate(matmul)) + self.assertAllClose([[20.0, 30.0], [40.0, 60.0]], self.evaluate(rmatmul)) + + @test_util.run_deprecated_v1 def testSession(self): with self.cached_session() as sess: var = variables.Variable([1, 12]) variables.global_variables_initializer().run() - self.assertAllClose([1, 12], sess.run(var)) - - def testDevicePlacement(self): - with self.cached_session() as sess: - with ops.device("/cpu:0"): - var = variables.Variable([1, 12]) - init_value = var.initialized_value() - init_op = variables.global_variables_initializer() - self.assertEqual(var.op.device, init_value.device) - self.assertEqual(var.op.device, init_op.device) - sess.run(init_op) + self.assertAllClose([1, 12], self.evaluate(var)) + @test_util.run_deprecated_v1 def testColocation(self): with ops.device("/job:ps"): var = variables.VariableV1(0, name="v") @@ -405,6 +421,7 @@ class VariablesTestCase(test.TestCase): self.assertDeviceEqual("/job:ps", assign_op.device) self.assertEqual([b"loc:@v"], assign_op.op.colocation_groups()) + @test_util.run_deprecated_v1 def testInitializerFunction(self): value = [[-42], [133.7]] shape = [2, 1] @@ -416,7 +433,7 @@ class VariablesTestCase(test.TestCase): self.assertEqual(shape, v1.shape) self.assertAllClose(value, v1.initial_value.eval()) with self.assertRaises(errors_impl.FailedPreconditionError): - v1.eval() + self.evaluate(v1) v2 = variables.Variable( math_ops.negative(v1.initialized_value()), dtype=dtypes.float32) @@ -425,9 +442,9 @@ class VariablesTestCase(test.TestCase): self.assertAllClose(np.negative(value), v2.initial_value.eval()) with self.assertRaises(errors_impl.FailedPreconditionError): - v2.eval() + self.evaluate(v2) variables.global_variables_initializer().run() - self.assertAllClose(np.negative(value), v2.eval()) + self.assertAllClose(np.negative(value), self.evaluate(v2)) def testConstraintArg(self): constraint = lambda x: x @@ -442,6 +459,7 @@ class VariablesTestCase(test.TestCase): lambda: constant_op.constant(1.), constraint=constraint) + @test_util.run_deprecated_v1 def testNoRefDataRace(self): with self.cached_session(): a = variables.Variable([1, 2, 3], dtype=dtypes.float32) @@ -452,6 +470,7 @@ class VariablesTestCase(test.TestCase): self.assertAllEqual(b.eval(), [3, 4, 5]) self.assertAllEqual(c.eval(), [5, 6, 7]) + @test_util.run_deprecated_v1 def testInitializerFunctionDevicePlacement(self): with self.cached_session(): initializer = lambda: constant_op.constant(42.0) @@ -470,6 +489,7 @@ class VariablesTestCase(test.TestCase): for i in v2.initializer.inputs: self.assertEqual(expected_group_v2, i.op.colocation_groups()) + @test_util.run_deprecated_v1 def testVariableDefInitializedInstances(self): with ops.Graph().as_default(), self.cached_session() as sess: v_def = variables.Variable( @@ -478,11 +498,11 @@ class VariablesTestCase(test.TestCase): with ops.Graph().as_default(), self.cached_session() as sess: # v describes a VariableDef-based variable without an initial value. v = variables.Variable(variable_def=v_def) - self.assertEqual(3.0, sess.run(v.initialized_value())) + self.assertEqual(3.0, self.evaluate(v.initialized_value())) # initialized_value should not rerun the initializer_op if the variable # has already been initialized elsewhere. - sess.run(v.assign(1.0)) + self.evaluate(v.assign(1.0)) self.assertEqual(1.0, v.initialized_value().eval()) v_def.ClearField("initial_value_name") @@ -494,7 +514,7 @@ class VariablesTestCase(test.TestCase): self.assertProtoEquals(v_def, v.to_proto()) # But attempts to use initialized_value will result in errors. with self.assertRaises(ValueError): - sess.run(v.initialized_value()) + self.evaluate(v.initialized_value()) def testTrainableInProto(self): with ops.Graph().as_default(): @@ -513,14 +533,16 @@ class VariablesTestCase(test.TestCase): variables.Variable(variable_def=trainable_variable.to_proto()) .trainable) + @test_util.run_deprecated_v1 def testLoad(self): with self.cached_session(): var = variables.Variable(np.zeros((5, 5), np.float32)) variables.global_variables_initializer().run() var.load(np.ones((5, 5), np.float32)) - self.assertAllClose(np.ones((5, 5), np.float32), var.eval()) + self.assertAllClose(np.ones((5, 5), np.float32), self.evaluate(var)) + @test_util.run_deprecated_v1 def testRepr(self): var = variables.VariableV1(np.zeros((5, 5), np.float32), name="noop") self.assertEqual( @@ -542,7 +564,7 @@ class IsInitializedTest(test.TestCase): def testNoVars(self): with ops.Graph().as_default(), self.cached_session() as sess: uninited = variables.report_uninitialized_variables() - self.assertEqual(0, sess.run(uninited).size) + self.assertEqual(0, self.evaluate(uninited).size) def testAssertVariablesInitialized(self): with ops.Graph().as_default(), self.cached_session() as sess: @@ -550,27 +572,28 @@ class IsInitializedTest(test.TestCase): w = variables.Variable([3, 4], name="w") _ = v, w uninited = variables.report_uninitialized_variables() - self.assertAllEqual(np.array([b"v", b"w"]), sess.run(uninited)) + self.assertAllEqual(np.array([b"v", b"w"]), self.evaluate(uninited)) variables.global_variables_initializer().run() - self.assertEqual(0, sess.run(uninited).size) + self.assertEqual(0, self.evaluate(uninited).size) + @test_util.run_deprecated_v1 def testVariableList(self): with ops.Graph().as_default(), self.cached_session() as sess: v = variables.VariableV1([1, 2], name="v") w = variables.VariableV1([3, 4], name="w") uninited = variables.report_uninitialized_variables() - self.assertAllEqual(np.array([b"v", b"w"]), sess.run(uninited)) - sess.run(w.initializer) - self.assertAllEqual(np.array([b"v"]), sess.run(uninited)) + self.assertAllEqual(np.array([b"v", b"w"]), self.evaluate(uninited)) + self.evaluate(w.initializer) + self.assertAllEqual(np.array([b"v"]), self.evaluate(uninited)) v.initializer.run() - self.assertEqual(0, sess.run(uninited).size) + self.assertEqual(0, self.evaluate(uninited).size) def testZeroSizeVarInitialized(self): with ops.Graph().as_default(), self.cached_session() as sess: v = variables.Variable(array_ops.zeros([0, 2]), name="v") uninited = variables.report_uninitialized_variables() v.initializer.run() # not strictly necessary - self.assertEqual(0, sess.run(uninited).size) + self.assertEqual(0, self.evaluate(uninited).size) def testTrainingWithZeroSizeVar(self): with ops.Graph().as_default(), self.cached_session() as sess: @@ -581,8 +604,8 @@ class IsInitializedTest(test.TestCase): variables.global_variables_initializer().run() do_opt = gradient_descent.GradientDescentOptimizer(0.1).minimize( objective) - sess.run([do_opt]) - self.assertAllClose([[0.9, 0.9], [0.9, 0.9]], b.eval()) + self.evaluate([do_opt]) + self.assertAllClose([[0.9, 0.9], [0.9, 0.9]], self.evaluate(b)) class ObsoleteIsInitializedTest(test.TestCase): @@ -591,6 +614,7 @@ class ObsoleteIsInitializedTest(test.TestCase): with ops.Graph().as_default(): self.assertEqual(None, variables.assert_variables_initialized()) + @test_util.run_deprecated_v1 def testVariables(self): with ops.Graph().as_default(), self.cached_session() as sess: v = variables.VariableV1([1, 2]) @@ -598,10 +622,11 @@ class ObsoleteIsInitializedTest(test.TestCase): _ = v, w inited = variables.assert_variables_initialized() with self.assertRaisesOpError("Attempting to use uninitialized value"): - sess.run(inited) + self.evaluate(inited) variables.global_variables_initializer().run() - sess.run(inited) + self.evaluate(inited) + @test_util.run_deprecated_v1 def testVariableList(self): with ops.Graph().as_default(), self.cached_session() as sess: v = variables.VariableV1([1, 2]) @@ -609,7 +634,7 @@ class ObsoleteIsInitializedTest(test.TestCase): inited = variables.assert_variables_initialized([v]) with self.assertRaisesOpError("Attempting to use uninitialized value"): inited.op.run() - sess.run(w.initializer) + self.evaluate(w.initializer) with self.assertRaisesOpError("Attempting to use uninitialized value"): inited.op.run() v.initializer.run() @@ -744,34 +769,34 @@ class PartitionedVariableTest(test.TestCase): variables.global_variables_initializer().run() self.assertEqual([1.0], plus_delta[0].eval()) - self.assertEqual([1.0], v0.eval()) + self.assertEqual([1.0], self.evaluate(v0)) self.assertEqual([3.0], plus_delta[1].eval()) - self.assertEqual([3.0], v1.eval()) + self.assertEqual([3.0], self.evaluate(v1)) self.assertEqual([-2.0], minus_delta[0].eval()) - self.assertEqual([-2.0], v0.eval()) + self.assertEqual([-2.0], self.evaluate(v0)) self.assertEqual([-1.0], minus_delta[1].eval()) - self.assertEqual([-1.0], v1.eval()) + self.assertEqual([-1.0], self.evaluate(v1)) self.assertEqual([1.0], assign_ones[0].eval()) - self.assertEqual([1.0], v0.eval()) + self.assertEqual([1.0], self.evaluate(v0)) self.assertEqual([1.0], assign_ones[1].eval()) - self.assertEqual([1.0], v1.eval()) + self.assertEqual([1.0], self.evaluate(v1)) self.assertEqual([2.0], assign_list[0].eval()) - self.assertEqual([2.0], v2.eval()) + self.assertEqual([2.0], self.evaluate(v2)) self.assertEqual([3.0], assign_list[1].eval()) - self.assertEqual([3.0], v3.eval()) + self.assertEqual([3.0], self.evaluate(v3)) self.assertEqual([3.0], assign_part_value[0].eval()) - self.assertEqual([3.0], v2.eval()) + self.assertEqual([3.0], self.evaluate(v2)) self.assertEqual([4.0], assign_part_value[1].eval()) - self.assertEqual([4.0], v3.eval()) + self.assertEqual([4.0], self.evaluate(v3)) self.assertEqual([2.0], assign_part_var[0].eval()) - self.assertEqual([2.0], v2.eval()) + self.assertEqual([2.0], self.evaluate(v2)) self.assertEqual([3.0], assign_part_var[1].eval()) - self.assertEqual([3.0], v3.eval()) + self.assertEqual([3.0], self.evaluate(v3)) class VariableContainerTest(test.TestCase): diff --git a/tensorflow/python/kernel_tests/weights_broadcast_test.py b/tensorflow/python/kernel_tests/weights_broadcast_test.py index 85f9abc69f..677d8f2f22 100644 --- a/tensorflow/python/kernel_tests/weights_broadcast_test.py +++ b/tensorflow/python/kernel_tests/weights_broadcast_test.py @@ -23,6 +23,7 @@ import numpy as np from tensorflow.python.framework import dtypes as dtypes_lib from tensorflow.python.framework import errors_impl from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import weights_broadcast_ops from tensorflow.python.platform import test @@ -51,40 +52,48 @@ class AssertBroadcastableTest(test.TestCase): values_placeholder: values, }) + @test_util.run_deprecated_v1 def testScalar(self): self._test_valid(weights=5, values=_test_values((3, 2, 4))) + @test_util.run_deprecated_v1 def test1x1x1(self): self._test_valid( weights=np.asarray((5,)).reshape((1, 1, 1)), values=_test_values((3, 2, 4))) + @test_util.run_deprecated_v1 def test1x1xN(self): self._test_valid( weights=np.asarray((5, 7, 11, 3)).reshape((1, 1, 4)), values=_test_values((3, 2, 4))) + @test_util.run_deprecated_v1 def test1xNx1(self): self._test_valid( weights=np.asarray((5, 11)).reshape((1, 2, 1)), values=_test_values((3, 2, 4))) + @test_util.run_deprecated_v1 def test1xNxN(self): self._test_valid( weights=np.asarray((5, 7, 11, 3, 2, 13, 7, 5)).reshape((1, 2, 4)), values=_test_values((3, 2, 4))) + @test_util.run_deprecated_v1 def testNx1x1(self): self._test_valid( weights=np.asarray((5, 7, 11)).reshape((3, 1, 1)), values=_test_values((3, 2, 4))) + @test_util.run_deprecated_v1 def testNx1xN(self): self._test_valid( weights=np.asarray(( 5, 7, 11, 3, 2, 12, 7, 5, 2, 17, 11, 3)).reshape((3, 1, 4)), values=_test_values((3, 2, 4))) + @test_util.run_deprecated_v1 def testNxNxN(self): self._test_valid( weights=np.asarray(( @@ -107,29 +116,35 @@ class AssertBroadcastableTest(test.TestCase): values_placeholder: values, }) + @test_util.run_deprecated_v1 def testInvalid1(self): self._test_invalid(weights=np.asarray((5,)), values=_test_values((3, 2, 4))) + @test_util.run_deprecated_v1 def testInvalid1x1(self): self._test_invalid( weights=np.asarray((5,)).reshape((1, 1)), values=_test_values((3, 2, 4))) + @test_util.run_deprecated_v1 def testInvalidPrefixMatch(self): self._test_invalid( weights=np.asarray((5, 7, 11, 3, 2, 12)).reshape((3, 2)), values=_test_values((3, 2, 4))) + @test_util.run_deprecated_v1 def testInvalidSuffixMatch(self): self._test_invalid( weights=np.asarray((5, 7, 11, 3, 2, 12, 7, 5)).reshape((2, 4)), values=_test_values((3, 2, 4))) + @test_util.run_deprecated_v1 def testInvalidOnesExtraDim(self): self._test_invalid( weights=np.asarray((5,)).reshape((1, 1, 1, 1)), values=_test_values((3, 2, 4))) + @test_util.run_deprecated_v1 def testInvalidPrefixMatchExtraDim(self): self._test_invalid( weights=np.asarray(( @@ -137,6 +152,7 @@ class AssertBroadcastableTest(test.TestCase): 2, 17, 11, 3, 5, 7, 11, 3, 2, 12, 7, 5)).reshape((3, 2, 4, 1)), values=_test_values((3, 2, 4))) + @test_util.run_deprecated_v1 def testInvalidSuffixMatchExtraDim(self): self._test_invalid( weights=np.asarray(( @@ -158,24 +174,27 @@ class BroadcastWeightsTest(test.TestCase): dynamic_op = weights_broadcast_ops.broadcast_weights( weights=weights_placeholder, values=values_placeholder) with self.cached_session(): - self.assertAllEqual(expected, static_op.eval()) + self.assertAllEqual(expected, self.evaluate(static_op)) self.assertAllEqual(expected, dynamic_op.eval(feed_dict={ weights_placeholder: weights, values_placeholder: values, })) + @test_util.run_deprecated_v1 def testScalar(self): self._test_valid( weights=5, values=_test_values((3, 2, 4)), expected=5 * np.ones((3, 2, 4))) + @test_util.run_deprecated_v1 def test1x1x1(self): self._test_valid( weights=np.asarray((5,)).reshape((1, 1, 1)), values=_test_values((3, 2, 4)), expected=5 * np.ones((3, 2, 4))) + @test_util.run_deprecated_v1 def test1x1xN(self): weights = np.asarray((5, 7, 11, 3)).reshape((1, 1, 4)) self._test_valid( @@ -183,6 +202,7 @@ class BroadcastWeightsTest(test.TestCase): values=_test_values((3, 2, 4)), expected=np.tile(weights, reps=(3, 2, 1))) + @test_util.run_deprecated_v1 def test1xNx1(self): weights = np.asarray((5, 11)).reshape((1, 2, 1)) self._test_valid( @@ -190,6 +210,7 @@ class BroadcastWeightsTest(test.TestCase): values=_test_values((3, 2, 4)), expected=np.tile(weights, reps=(3, 1, 4))) + @test_util.run_deprecated_v1 def test1xNxN(self): weights = np.asarray((5, 7, 11, 3, 2, 13, 7, 5)).reshape((1, 2, 4)) self._test_valid( @@ -197,6 +218,7 @@ class BroadcastWeightsTest(test.TestCase): values=_test_values((3, 2, 4)), expected=np.tile(weights, reps=(3, 1, 1))) + @test_util.run_deprecated_v1 def testNx1x1(self): weights = np.asarray((5, 7, 11)).reshape((3, 1, 1)) self._test_valid( @@ -204,6 +226,7 @@ class BroadcastWeightsTest(test.TestCase): values=_test_values((3, 2, 4)), expected=np.tile(weights, reps=(1, 2, 4))) + @test_util.run_deprecated_v1 def testNx1xN(self): weights = np.asarray(( 5, 7, 11, 3, 2, 12, 7, 5, 2, 17, 11, 3)).reshape((3, 1, 4)) @@ -212,6 +235,7 @@ class BroadcastWeightsTest(test.TestCase): values=_test_values((3, 2, 4)), expected=np.tile(weights, reps=(1, 2, 1))) + @test_util.run_deprecated_v1 def testNxNxN(self): weights = np.asarray(( 5, 7, 11, 3, 2, 12, 7, 5, 2, 17, 11, 3, @@ -234,29 +258,35 @@ class BroadcastWeightsTest(test.TestCase): values_placeholder: values, }) + @test_util.run_deprecated_v1 def testInvalid1(self): self._test_invalid(weights=np.asarray((5,)), values=_test_values((3, 2, 4))) + @test_util.run_deprecated_v1 def testInvalid1x1(self): self._test_invalid( weights=np.asarray((5,)).reshape((1, 1)), values=_test_values((3, 2, 4))) + @test_util.run_deprecated_v1 def testInvalidPrefixMatch(self): self._test_invalid( weights=np.asarray((5, 7, 11, 3, 2, 12)).reshape((3, 2)), values=_test_values((3, 2, 4))) + @test_util.run_deprecated_v1 def testInvalidSuffixMatch(self): self._test_invalid( weights=np.asarray((5, 7, 11, 3, 2, 12, 7, 5)).reshape((2, 4)), values=_test_values((3, 2, 4))) + @test_util.run_deprecated_v1 def testInvalidOnesExtraDim(self): self._test_invalid( weights=np.asarray((5,)).reshape((1, 1, 1, 1)), values=_test_values((3, 2, 4))) + @test_util.run_deprecated_v1 def testInvalidPrefixMatchExtraDim(self): self._test_invalid( weights=np.asarray(( @@ -264,6 +294,7 @@ class BroadcastWeightsTest(test.TestCase): 2, 17, 11, 3, 5, 7, 11, 3, 2, 12, 7, 5)).reshape((3, 2, 4, 1)), values=_test_values((3, 2, 4))) + @test_util.run_deprecated_v1 def testInvalidSuffixMatchExtraDim(self): self._test_invalid( weights=np.asarray(( diff --git a/tensorflow/python/kernel_tests/where_op_test.py b/tensorflow/python/kernel_tests/where_op_test.py index fca45c3ece..56c1390411 100644 --- a/tensorflow/python/kernel_tests/where_op_test.py +++ b/tensorflow/python/kernel_tests/where_op_test.py @@ -27,6 +27,7 @@ from tensorflow.python.client import session from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import random_ops from tensorflow.python.ops import resource_variable_ops @@ -41,11 +42,11 @@ class WhereOpTest(test.TestCase): ans = array_ops.where(x) self.assertEqual([None, x.ndim], ans.get_shape().as_list()) if expected_err_re is None: - tf_ans = ans.eval() + tf_ans = self.evaluate(ans) self.assertAllClose(tf_ans, truth, atol=1e-10) else: with self.assertRaisesOpError(expected_err_re): - ans.eval() + self.evaluate(ans) def testWrongNumbers(self): with self.session(use_gpu=True): @@ -54,6 +55,7 @@ class WhereOpTest(test.TestCase): with self.assertRaises(ValueError): array_ops.where([False, True], None, [1, 2]) + @test_util.run_deprecated_v1 def testBasicVec(self): x = np.asarray([True, False]) truth = np.asarray([[0]], dtype=np.int64) @@ -67,11 +69,13 @@ class WhereOpTest(test.TestCase): truth = np.asarray([[2], [4]], dtype=np.int64) self._testWhere(x, truth) + @test_util.run_deprecated_v1 def testRandomVec(self): x = np.random.rand(1000000) > 0.5 truth = np.vstack([np.where(x)[0].astype(np.int64)]).T self._testWhere(x, truth) + @test_util.run_deprecated_v1 def testBasicMat(self): x = np.asarray([[True, False], [True, False]]) @@ -80,6 +84,7 @@ class WhereOpTest(test.TestCase): self._testWhere(x, truth) + @test_util.run_deprecated_v1 def testBasic3Tensor(self): x = np.asarray([[[True, False], [True, False]], [[False, True], [False, True]], @@ -99,36 +104,47 @@ class WhereOpTest(test.TestCase): truth = np.vstack(truth).T # Convert to [num_true, indices]. self._testWhere(x, truth, expected_err_re) + @test_util.run_deprecated_v1 def testRandomBool(self): self._testRandom(np.bool) + @test_util.run_deprecated_v1 def testRandomInt32(self): self._testRandom(np.int32) + @test_util.run_deprecated_v1 def testRandomInt64(self): self._testRandom(np.int64) + @test_util.run_deprecated_v1 def testRandomFloat(self): self._testRandom(np.float32) + @test_util.run_deprecated_v1 def testRandomDouble(self): self._testRandom(np.float64) + @test_util.run_deprecated_v1 def testRandomComplex64(self): self._testRandom(np.complex64) + @test_util.run_deprecated_v1 def testRandomComplex128(self): self._testRandom(np.complex128) + @test_util.run_deprecated_v1 def testRandomUint8(self): self._testRandom(np.uint8) + @test_util.run_deprecated_v1 def testRandomInt8(self): self._testRandom(np.int8) + @test_util.run_deprecated_v1 def testRandomInt16(self): self._testRandom(np.int16) + @test_util.run_deprecated_v1 def testThreeArgument(self): x = np.array([[-2, 3, -1], [1, -3, -3]]) np_val = np.where(x > 0, x * x, -x) @@ -136,6 +152,7 @@ class WhereOpTest(test.TestCase): tf_val = array_ops.where(constant_op.constant(x) > 0, x * x, -x).eval() self.assertAllEqual(tf_val, np_val) + @test_util.run_deprecated_v1 def testBatchSelect(self): x = np.array([[-2, 3, -1] * 64, [1, -3, -3] * 64] * 8192) # [16384, 192] c_mat = np.array([[False] * 192, [True] * 192] * 8192) # [16384, 192] diff --git a/tensorflow/python/kernel_tests/while_v2_test.py b/tensorflow/python/kernel_tests/while_v2_test.py index dc1bcb78b8..09cbeb1a0d 100644 --- a/tensorflow/python/kernel_tests/while_v2_test.py +++ b/tensorflow/python/kernel_tests/while_v2_test.py @@ -20,14 +20,15 @@ from __future__ import print_function from absl.testing import parameterized +from tensorflow.core.protobuf import config_pb2 from tensorflow.core.protobuf import rewriter_config_pb2 from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import meta_graph from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.grappler import tf_optimizer from tensorflow.python.ops import array_ops -from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import functional_ops from tensorflow.python.ops import gradients_impl from tensorflow.python.ops import list_ops @@ -42,14 +43,29 @@ from tensorflow.python.platform import test class WhileV2Test(test.TestCase, parameterized.TestCase): + @test_util.run_deprecated_v1 def testSingleLoopVar(self): x = constant_op.constant(2.) - ret = while_loop_v2(lambda v: v < 8., lambda v: v * v, [x]) + ret = while_loop_v2( + lambda v: v < 8., lambda v: v * v, [x], return_same_structure=False) grad = gradients_impl.gradients(ret, [x]) with self.cached_session() as sess: - self.assertEqual(sess.run(ret), 16.) + self.assertEqual(self.evaluate(ret), 16.) + self.assertSequenceEqual(self.evaluate(grad), [32.]) + + def testReturnSameStructureTrue(self): + x = constant_op.constant(2.) + ret = while_loop_v2( + lambda v: v < 8., lambda v: v * v, [x], return_same_structure=True) + grad = gradients_impl.gradients(ret, [x]) + with self.cached_session() as sess: + eval_result = sess.run(ret) + self.assertIsInstance(eval_result, list) + self.assertLen(eval_result, 1) + self.assertEqual(16., eval_result[0]) self.assertSequenceEqual(sess.run(grad), [32.]) + @test_util.run_deprecated_v1 def testMultipleLoopVarsBasic(self): x = constant_op.constant(5.) y = constant_op.constant(3.) @@ -58,15 +74,19 @@ class WhileV2Test(test.TestCase, parameterized.TestCase): # y = 3. # while x < 45.: # x = x * y - ret = while_loop_v2(lambda v, _: v < 45., lambda v, w: (v * w, w), [x, y]) + ret = while_loop_v2( + lambda v, _: v < 45., + lambda v, w: (v * w, w), [x, y], + return_same_structure=False) # ret = [x*y^2, y] # Note: This is simply d_ret[0]/d_x since d_ret[1]/d_x is 0. grad = gradients_impl.gradients(ret, [x]) # [2*x*y] with self.cached_session() as sess: - self.assertSequenceEqual(sess.run(ret), [45., 3.]) - self.assertSequenceEqual(sess.run(grad), [9.]) + self.assertSequenceEqual(self.evaluate(ret), [45., 3.]) + self.assertSequenceEqual(self.evaluate(grad), [9.]) + @test_util.run_deprecated_v1 def testMultipleLoopVars(self): x = constant_op.constant(5.) y = constant_op.constant(3.) @@ -76,8 +96,10 @@ class WhileV2Test(test.TestCase, parameterized.TestCase): # while x < 45.: # x = x * y # y = x + y - ret = while_loop_v2(lambda v, _: v < 45., lambda v, w: (v * w, v + w), - [x, y]) + ret = while_loop_v2( + lambda v, _: v < 45., + lambda v, w: (v * w, v + w), [x, y], + return_same_structure=False) # ret = [y*x**2 + x*y**2, x*y + x + y] gradx_0 = gradients_impl.gradients(ret[0], [x]) # [2*x*y + y**2] @@ -87,34 +109,43 @@ class WhileV2Test(test.TestCase, parameterized.TestCase): grady_1 = gradients_impl.gradients(ret[1], [y]) # [x + 1] grady_2 = gradients_impl.gradients(ret, [y]) # [2*x*y + x**2 + x + 1] with self.cached_session() as sess: - self.assertSequenceEqual(sess.run(ret), [120., 23.]) - self.assertSequenceEqual(sess.run(gradx_0), [39.]) - self.assertSequenceEqual(sess.run(gradx_1), [4.]) - self.assertSequenceEqual(sess.run(gradx_2), [43.]) - self.assertSequenceEqual(sess.run(grady_0), [55.]) - self.assertSequenceEqual(sess.run(grady_1), [6.]) - self.assertSequenceEqual(sess.run(grady_2), [61.]) - + self.assertSequenceEqual(self.evaluate(ret), [120., 23.]) + self.assertSequenceEqual(self.evaluate(gradx_0), [39.]) + self.assertSequenceEqual(self.evaluate(gradx_1), [4.]) + self.assertSequenceEqual(self.evaluate(gradx_2), [43.]) + self.assertSequenceEqual(self.evaluate(grady_0), [55.]) + self.assertSequenceEqual(self.evaluate(grady_1), [6.]) + self.assertSequenceEqual(self.evaluate(grady_2), [61.]) + + @test_util.run_deprecated_v1 def testMultipleWhileLoops(self): x = constant_op.constant(2.) - ret1 = while_loop_v2(lambda v: v < 4., lambda v: v * v, [x]) # x**2 - ret2 = while_loop_v2(lambda v: v < 16., lambda v: v * v, [ret1]) # x**4 + ret1 = while_loop_v2( + lambda v: v < 4., lambda v: v * v, [x], + return_same_structure=False) # x**2 + ret2 = while_loop_v2( + lambda v: v < 16., lambda v: v * v, [ret1], + return_same_structure=False) # x**4 grad = gradients_impl.gradients(ret2, [x]) # 4x**3 grad_grad = gradients_impl.gradients(grad, [x]) # 12x**2 with self.cached_session() as sess: - self.assertSequenceEqual(sess.run(grad), [32.]) - self.assertSequenceEqual(sess.run(grad_grad), [48.]) + self.assertSequenceEqual(self.evaluate(grad), [32.]) + self.assertSequenceEqual(self.evaluate(grad_grad), [48.]) + @test_util.run_deprecated_v1 def testDoubleDerivative(self): x = constant_op.constant(2.) - ret = while_loop_v2(lambda v: v < 8., lambda v: v**2, [x]) # x**4 + ret = while_loop_v2( + lambda v: v < 8., lambda v: v**2, [x], + return_same_structure=False) # x**4 grad = gradients_impl.gradients(ret, [x]) # 4x**3 grad_grad = gradients_impl.gradients(grad, [x]) # 12x**2 with self.cached_session() as sess: - self.assertEqual(sess.run(ret), 16.) - self.assertSequenceEqual(sess.run(grad), [32.]) - self.assertSequenceEqual(sess.run(grad_grad), [48.]) + self.assertEqual(self.evaluate(ret), 16.) + self.assertSequenceEqual(self.evaluate(grad), [32.]) + self.assertSequenceEqual(self.evaluate(grad_grad), [48.]) + @test_util.run_deprecated_v1 def testPruning(self): x = constant_op.constant(1) @@ -135,10 +166,12 @@ class WhileV2Test(test.TestCase, parameterized.TestCase): def GetOptimizedGraph(): mg = meta_graph.create_meta_graph_def(graph=ops.get_default_graph()) - rewriter_config = rewriter_config_pb2.RewriterConfig( - constant_folding=rewriter_config_pb2.RewriterConfig.OFF, - memory_optimization=rewriter_config_pb2.RewriterConfig.MANUAL) - return tf_optimizer.OptimizeGraph(rewriter_config, mg) + config = config_pb2.ConfigProto() + config.graph_options.rewrite_options.CopyFrom( + rewriter_config_pb2.RewriterConfig( + constant_folding=rewriter_config_pb2.RewriterConfig.OFF, + memory_optimization=rewriter_config_pb2.RewriterConfig.MANUAL)) + return tf_optimizer.OptimizeGraph(config, mg) g = GetOptimizedGraph() self.assertEqual(len([n for n in g.node if n.op == "Enter"]), 1) @@ -148,24 +181,31 @@ class WhileV2Test(test.TestCase, parameterized.TestCase): g = GetOptimizedGraph() self.assertEqual(len([n for n in g.node if n.op == "Enter"]), 2) + @test_util.run_deprecated_v1 def testCaptureExternalTensorInCond(self): x = constant_op.constant(2.) y = constant_op.constant(1.) - ret = while_loop_v2(lambda v: v + y < 9., lambda v: v * 3., [x]) + ret = while_loop_v2( + lambda v: v + y < 9., + lambda v: v * 3., [x], + return_same_structure=False) grad = gradients_impl.gradients(ret, [x]) with self.cached_session() as sess: - self.assertEqual(sess.run(ret), 18.) - self.assertSequenceEqual(sess.run(grad), [9.]) + self.assertEqual(self.evaluate(ret), 18.) + self.assertSequenceEqual(self.evaluate(grad), [9.]) + @test_util.run_deprecated_v1 def testCaptureExternalTensorInBody(self): x = constant_op.constant(2.) y = constant_op.constant(3.) - ret = while_loop_v2(lambda v: v < 8., lambda v: v * y, [x]) + ret = while_loop_v2( + lambda v: v < 8., lambda v: v * y, [x], return_same_structure=False) grad = gradients_impl.gradients(ret, [x]) with self.cached_session() as sess: - self.assertEqual(sess.run(ret), 18.) - self.assertSequenceEqual(sess.run(grad), [9.]) + self.assertEqual(self.evaluate(ret), 18.) + self.assertSequenceEqual(self.evaluate(grad), [9.]) + @test_util.run_deprecated_v1 def testLoopWithTensorListPushBack(self): x = constant_op.constant(2.) @@ -181,12 +221,14 @@ class WhileV2Test(test.TestCase, parameterized.TestCase): tl = list_ops.tensor_list_push_back(tl, constant_op.constant(100.)) return x**2., tl - ret = while_loop_v2(Cond, Body, [x, tensor_list]) + ret = while_loop_v2( + Cond, Body, [x, tensor_list], return_same_structure=False) grad = gradients_impl.gradients(ret[0], x) with self.cached_session() as sess: self.assertEqual(sess.run(ret[0]), 16.) - self.assertSequenceEqual(sess.run(grad), [32.]) + self.assertSequenceEqual(self.evaluate(grad), [32.]) + @test_util.run_deprecated_v1 def testDuplicateAccumulator(self): x = constant_op.constant(2.) @@ -203,7 +245,8 @@ class WhileV2Test(test.TestCase, parameterized.TestCase): tl = list_ops.tensor_list_push_back(tl, x) return x**2., tl - ret = while_loop_v2(Cond, Body, [x, tensor_list]) + ret = while_loop_v2( + Cond, Body, [x, tensor_list], return_same_structure=False) for op in ops.get_default_graph().get_operations(): if op.type == "While": @@ -219,13 +262,14 @@ class WhileV2Test(test.TestCase, parameterized.TestCase): grad = gradients_impl.gradients(ret[0], x) with self.cached_session() as sess: self.assertEqual(sess.run(ret[0]), 16.) - self.assertSequenceEqual(sess.run(grad), [32.]) + self.assertSequenceEqual(self.evaluate(grad), [32.]) @parameterized.named_parameters( ("UnknownShape", None), ("PartiallyDefinedShape", [None, 2]), ("FullyDefinedShape", [1, 2]), ) + @test_util.run_deprecated_v1 def testAccumulatorElementShape(self, shape): def MatchShape(actual_tensor_shape): @@ -250,7 +294,10 @@ class WhileV2Test(test.TestCase, parameterized.TestCase): y = array_ops.placeholder(dtype=dtypes.float32, shape=shape) # Forward pass. - ret = while_loop_v2(lambda v, u: v < 8., lambda v, u: (v * v, u), [x, y]) + ret = while_loop_v2( + lambda v, u: v < 8., + lambda v, u: (v * v, u), [x, y], + return_same_structure=False) while_op = ret[0].op.inputs[0].op # Get the TensorList output of While op containing the accumulated values # of y. @@ -262,7 +309,7 @@ class WhileV2Test(test.TestCase, parameterized.TestCase): # Gradient pass. grad = gradients_impl.gradients(ret[1], y) - grad_while_op = grad[0].op + grad_while_op = grad[0].op.inputs[0].op # Get the TensorList output of gradient While op containing the accumulated # values of grad_y. # grad_while_op.inputs: @@ -274,8 +321,10 @@ class WhileV2Test(test.TestCase, parameterized.TestCase): def _createWhile(self, name): """Helper function testDefaultName.""" - output = while_v2.while_loop(lambda i: i < 3, lambda i: i + 1, - [constant_op.constant(0)]) + output = while_v2.while_loop( + lambda i: i < 3, + lambda i: i + 1, [constant_op.constant(0)], + return_same_structure=False) while_op = output.op.inputs[0].op self.assertEqual(while_op.type, "While") return while_op @@ -305,19 +354,19 @@ class WhileV2Test(test.TestCase, parameterized.TestCase): self.assertRegexpMatches( while2_op.get_attr("body").name, r"foo_while_1_body_\d*") + @test_util.enable_control_flow_v2 + @test_util.run_deprecated_v1 def testWhileAndTensorArray(self): - old_enable_while_v2 = control_flow_ops.ENABLE_WHILE_V2 - control_flow_ops.ENABLE_WHILE_V2 = True with self.cached_session() as sess: param = constant_op.constant(2.0) y0 = constant_op.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], name="elems") # map_fn uses TensorArray internally. r = functional_ops.map_fn(lambda x: math_ops.multiply(x, param), y0) - self.assertAllClose([2.0, 4.0, 6.0, 8.0, 10.0, 12.0], sess.run(r)) + self.assertAllClose([2.0, 4.0, 6.0, 8.0, 10.0, 12.0], self.evaluate(r)) r = gradients_impl.gradients(r, param)[0] - self.assertAllClose(21.0, sess.run(r)) - control_flow_ops.ENABLE_WHILE_V2 = old_enable_while_v2 + self.assertAllClose(21.0, self.evaluate(r)) + @test_util.run_deprecated_v1 def testNestedWhile(self): # Compute sum of geometric progression: n^0 + n^1 + ... + n^m # We compute the pow using a while loop. @@ -328,14 +377,20 @@ class WhileV2Test(test.TestCase, parameterized.TestCase): def Body(i, previous_sum): prod = constant_op.constant(1.) return i - 1., previous_sum + while_loop_v2( - lambda c, _: c > 0, lambda c, v: (c - 1., v * n), [i, prod])[1] - - result = while_loop_v2(lambda i, _: i >= 0, Body, [m, sum_of_powers])[1] + lambda c, _: c > 0, + lambda c, v: (c - 1., v * n), [i, prod], + return_same_structure=False)[1] + + result = while_loop_v2( + lambda i, _: i >= 0, + Body, [m, sum_of_powers], + return_same_structure=False)[1] grad = gradients_impl.gradients(result, [n]) with self.cached_session() as sess: - self.assertEqual(sess.run(result), 364.) - self.assertSequenceEqual(sess.run(grad), [547.]) + self.assertEqual(self.evaluate(result), 364.) + self.assertSequenceEqual(self.evaluate(grad), [547.]) + @test_util.run_deprecated_v1 def testIdentityNodeInBody(self): def Body(v): @@ -344,12 +399,14 @@ class WhileV2Test(test.TestCase, parameterized.TestCase): return v * v x = constant_op.constant(2.) - ret = while_loop_v2(lambda v: v < 8., Body, [x]) + ret = while_loop_v2( + lambda v: v < 8., Body, [x], return_same_structure=False) grad = gradients_impl.gradients(ret, [x]) with self.cached_session() as sess: - self.assertEqual(sess.run(ret), 16.) - self.assertSequenceEqual(sess.run(grad), [32.]) + self.assertEqual(self.evaluate(ret), 16.) + self.assertSequenceEqual(self.evaluate(grad), [32.]) + @test_util.run_deprecated_v1 def testNestedWhileAndTensorArray(self): n = constant_op.constant(3.0) @@ -362,13 +419,17 @@ class WhileV2Test(test.TestCase, parameterized.TestCase): return row, col + 1., ta, n # TODO(b/118457764): Remove n from loop_vars from both loops once fixed. - ta = while_loop_v2(lambda _, col, _1, n: col <= n, InnerBody, - [row, constant_op.constant(1.), ta, n])[2] + ta = while_loop_v2( + lambda _, col, _1, n: col <= n, + InnerBody, [row, constant_op.constant(1.), ta, n], + return_same_structure=False)[2] return row + 1., ta, n ta = tensor_array_ops.TensorArray(dtype=dtypes.float32, size=9) - ta = while_loop_v2(lambda row, _, _1: row <= n, Body, - [constant_op.constant(1.), ta, n])[1] + ta = while_loop_v2( + lambda row, _, _1: row <= n, + Body, [constant_op.constant(1.), ta, n], + return_same_structure=False)[1] output = array_ops.reshape(ta.stack(), [3, 3]) self.assertAllEqual( diff --git a/tensorflow/python/kernel_tests/xent_op_test.py b/tensorflow/python/kernel_tests/xent_op_test.py index c3c7f867a1..f5d03c2370 100644 --- a/tensorflow/python/kernel_tests/xent_op_test.py +++ b/tensorflow/python/kernel_tests/xent_op_test.py @@ -27,6 +27,7 @@ from tensorflow.python.client import session from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gen_nn_ops from tensorflow.python.ops import gradient_checker @@ -56,7 +57,7 @@ class XentTest(test.TestCase): with self.cached_session(use_gpu=use_gpu) as sess: loss, backprop = gen_nn_ops.softmax_cross_entropy_with_logits( np_features, np_labels) - tf_loss, tf_backprop = sess.run([loss, backprop]) + tf_loss, tf_backprop = self.evaluate([loss, backprop]) self.assertAllCloseAccordingToType(np_loss, tf_loss) self.assertAllCloseAccordingToType(np_backprop, tf_backprop) @@ -65,7 +66,7 @@ class XentTest(test.TestCase): with self.cached_session(use_gpu=use_gpu) as sess: loss = nn_ops.softmax_cross_entropy_with_logits( labels=np_labels, logits=np_features, dim=dim) - tf_loss = sess.run(loss) + tf_loss = self.evaluate(loss) print("np_loss:", np_loss) print("tf_loss:", tf_loss) self.assertAllCloseAccordingToType(np_loss, tf_loss) @@ -80,7 +81,7 @@ class XentTest(test.TestCase): loss, backprop = gen_nn_ops.softmax_cross_entropy_with_logits( np.array([[1.], [-1.], [0.]]).astype(dtype), np.array([[-1.], [0.], [1.]]).astype(dtype)) - tf_loss, tf_backprop = sess.run([loss, backprop]) + tf_loss, tf_backprop = self.evaluate([loss, backprop]) self.assertAllClose([0.0, 0.0, 0.0], tf_loss) self.assertAllClose([[2.0], [1.0], [0.0]], tf_backprop) @@ -88,6 +89,7 @@ class XentTest(test.TestCase): self._testSingleClass(True) self._testSingleClass(False) + @test_util.run_deprecated_v1 def testRankTooLarge(self): for dtype in np.float16, np.float32: np_features = np.array([[[1., 1., 1., 1.]], [[1., 2., 3., @@ -148,16 +150,18 @@ class XentTest(test.TestCase): with self.cached_session(use_gpu=use_gpu) as sess: loss, backprop = gen_nn_ops.softmax_cross_entropy_with_logits( tf_f, tf_l) - tf_loss, tf_backprop = sess.run([loss, backprop]) + tf_loss, tf_backprop = self.evaluate([loss, backprop]) self.assertAllCloseAccordingToType(np_loss, tf_loss) self.assertAllCloseAccordingToType(np_backprop, tf_backprop) + @test_util.run_deprecated_v1 def testShapeMismatch(self): with self.cached_session(): with self.assertRaises(ValueError): gen_nn_ops.softmax_cross_entropy_with_logits( [[0., 1.], [2., 3.]], [[0., 1., 0.], [1., 0., 0.]]) + @test_util.run_deprecated_v1 def testNotMatrix(self): with self.cached_session(): with self.assertRaises(ValueError): @@ -179,6 +183,7 @@ class XentTest(test.TestCase): np.array([[1., 1., 1., 1.], [1., 2., 3., 4.]]).astype(np.float64), np.array([[0., 0., 0., 1.], [0., .5, .5, 0.]]).astype(np.float64)) + @test_util.run_deprecated_v1 def testGradient(self): with self.cached_session() as sess: l = constant_op.constant( @@ -206,6 +211,7 @@ class XentTest(test.TestCase): print("cross entropy gradient err = ", err) self.assertLess(err, 5e-8) + @test_util.run_deprecated_v1 def testGradientLabelWithV2(self): with self.cached_session(): l = constant_op.constant( @@ -224,6 +230,7 @@ class XentTest(test.TestCase): self.assertLess(err, 5e-8) + @test_util.run_deprecated_v1 def testSecondGradient(self): with self.cached_session() as sess: l = constant_op.constant( @@ -280,7 +287,7 @@ class XentTest(test.TestCase): with self.session(use_gpu=True) as sess: loss = nn_ops.softmax_cross_entropy_with_logits( labels=labels, logits=features) - tf_loss = sess.run(loss) + tf_loss = self.evaluate(loss) self.assertAllEqual(np_loss, tf_loss) diff --git a/tensorflow/python/kernel_tests/zero_division_test.py b/tensorflow/python/kernel_tests/zero_division_test.py index e68b96e670..3dd9ec4ba9 100644 --- a/tensorflow/python/kernel_tests/zero_division_test.py +++ b/tensorflow/python/kernel_tests/zero_division_test.py @@ -21,13 +21,15 @@ from __future__ import print_function from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors_impl +from tensorflow.python.framework import test_util from tensorflow.python.platform import test class ZeroDivisionTest(test.TestCase): + @test_util.run_deprecated_v1 def testZeros(self): - with self.session(use_gpu=True): + with test_util.use_gpu(): for dtype in dtypes.uint8, dtypes.int16, dtypes.int32, dtypes.int64: zero = constant_op.constant(0, dtype=dtype) one = constant_op.constant(1, dtype=dtype) @@ -36,7 +38,7 @@ class ZeroDivisionTest(test.TestCase): bads.append(one % zero) for bad in bads: try: - result = bad.eval() + result = self.evaluate(bad) except errors_impl.OpError as e: # Ideally, we'd get a nice exception. In theory, this should only # happen on CPU, but 32 bit integer GPU division is actually on diff --git a/tensorflow/python/layers/base.py b/tensorflow/python/layers/base.py index fccea484b0..bfe591f875 100644 --- a/tensorflow/python/layers/base.py +++ b/tensorflow/python/layers/base.py @@ -23,6 +23,7 @@ from tensorflow.python.eager import context from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.keras.engine import base_layer +from tensorflow.python.keras.engine import base_layer_utils from tensorflow.python.ops import variable_scope as vs from tensorflow.python.ops import variables as tf_variables from tensorflow.python.util import function_utils @@ -30,10 +31,10 @@ from tensorflow.python.util import nest from tensorflow.python.util import tf_contextlib from tensorflow.python.util.tf_export import tf_export - +# Avoid breaking users who directly import this symbol from this file. +# TODO(fchollet): remove this. InputSpec = base_layer.InputSpec # pylint: disable=invalid-name - _KERAS_STYLE_SCOPE = False @@ -242,11 +243,11 @@ class Layer(base_layer.Layer): def _make_unique_name(self, name_uid_map=None, avoid_names=None, namespace='', zero_based=False): base_name = base_layer.to_snake_case(self.__class__.__name__) - name = base_layer.unique_layer_name(base_name, - name_uid_map=name_uid_map, - avoid_names=avoid_names, - namespace=namespace, - zero_based=zero_based) + name = base_layer_utils.unique_layer_name(base_name, + name_uid_map=name_uid_map, + avoid_names=avoid_names, + namespace=namespace, + zero_based=zero_based) return (name, base_name) @property diff --git a/tensorflow/python/layers/base_test.py b/tensorflow/python/layers/base_test.py index 90abf35e87..d0ec4f4425 100644 --- a/tensorflow/python/layers/base_test.py +++ b/tensorflow/python/layers/base_test.py @@ -26,6 +26,7 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import test_util from tensorflow.python.keras.engine import base_layer as keras_base_layer +from tensorflow.python.keras.engine import input_spec from tensorflow.python.layers import base as base_layers from tensorflow.python.layers import core as core_layers from tensorflow.python.ops import array_ops @@ -142,6 +143,7 @@ class BaseLayerTest(test.TestCase): synchronization=variable_scope.VariableSynchronization.ON_READ, trainable=True) + @test_util.run_deprecated_v1 def testReusePartitionedVaraiblesAndRegularizers(self): regularizer = lambda x: math_ops.reduce_sum(x) * 1e-3 partitioner = partitioned_variables.fixed_size_partitioner(3) @@ -251,7 +253,7 @@ class BaseLayerTest(test.TestCase): def __init__(self): super(CustomerLayer, self).__init__() - self.input_spec = base_layers.InputSpec(ndim=2) + self.input_spec = input_spec.InputSpec(ndim=2) def call(self, inputs): return inputs @@ -278,7 +280,7 @@ class BaseLayerTest(test.TestCase): def __init__(self): super(CustomerLayer, self).__init__() - self.input_spec = base_layers.InputSpec(min_ndim=2) + self.input_spec = input_spec.InputSpec(min_ndim=2) def call(self, inputs): return inputs @@ -306,7 +308,7 @@ class BaseLayerTest(test.TestCase): def __init__(self): super(CustomerLayer, self).__init__() - self.input_spec = base_layers.InputSpec(max_ndim=2) + self.input_spec = input_spec.InputSpec(max_ndim=2) def call(self, inputs): return inputs @@ -334,7 +336,7 @@ class BaseLayerTest(test.TestCase): def __init__(self): super(CustomerLayer, self).__init__() - self.input_spec = base_layers.InputSpec(dtype='float32') + self.input_spec = input_spec.InputSpec(dtype='float32') def call(self, inputs): return inputs @@ -354,7 +356,7 @@ class BaseLayerTest(test.TestCase): def __init__(self): super(CustomerLayer, self).__init__() - self.input_spec = base_layers.InputSpec(axes={-1: 2}) + self.input_spec = input_spec.InputSpec(axes={-1: 2}) def call(self, inputs): return inputs @@ -376,7 +378,7 @@ class BaseLayerTest(test.TestCase): def __init__(self): super(CustomerLayer, self).__init__() - self.input_spec = base_layers.InputSpec(shape=(None, 3)) + self.input_spec = input_spec.InputSpec(shape=(None, 3)) def call(self, inputs): return inputs @@ -444,6 +446,7 @@ class BaseLayerTest(test.TestCase): self.assertTrue(isinstance(result, dict)) self.assertEqual(set(['label', 'logits']), set(result.keys())) + @test_util.run_deprecated_v1 def testActivityRegularizer(self): regularizer = math_ops.reduce_sum layer = base_layers.Layer(activity_regularizer=regularizer) @@ -532,6 +535,7 @@ class BaseLayerTest(test.TestCase): self.assertEqual(len(layer.trainable_variables), 1) self.assertEqual(layer.variables[0].graph, outer_graph) + @test_util.run_deprecated_v1 def testGetUpdateFor(self): class MyLayer(base_layers.Layer): @@ -576,6 +580,7 @@ class BaseLayerTest(test.TestCase): self.assertEqual(len(layer.get_updates_for([intermediate_inputs])), 1) self.assertEqual(len(layer.get_updates_for([outputs])), 0) + @test_util.run_deprecated_v1 def testGetLossesFor(self): class MyLayer(base_layers.Layer): diff --git a/tensorflow/python/layers/convolutional_test.py b/tensorflow/python/layers/convolutional_test.py index 257fa27156..a3e493edfe 100644 --- a/tensorflow/python/layers/convolutional_test.py +++ b/tensorflow/python/layers/convolutional_test.py @@ -22,6 +22,7 @@ import numpy as np from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.layers import convolutional as conv_layers from tensorflow.python.ops import array_ops from tensorflow.python.ops import init_ops @@ -59,6 +60,7 @@ class ConvTest(test.TestCase): with self.assertRaisesRegexp(ValueError, 'kernel_size'): conv_layers.conv2d(images, 32, None) + @test_util.run_deprecated_v1 def testCreateConv2D(self): height, width = 7, 9 images = random_ops.random_uniform((5, height, width, 4)) @@ -87,6 +89,7 @@ class ConvTest(test.TestCase): self.assertListEqual(layer.kernel.get_shape().as_list(), [3, 3, 4, 32]) self.assertListEqual(layer.bias.get_shape().as_list(), [32]) + @test_util.run_deprecated_v1 def testCreateConv2DChannelsFirst(self): height, width = 7, 9 images = random_ops.random_uniform((5, 4, height, width)) @@ -97,6 +100,7 @@ class ConvTest(test.TestCase): self.assertListEqual(layer.kernel.get_shape().as_list(), [3, 3, 4, 32]) self.assertListEqual(layer.bias.get_shape().as_list(), [32]) + @test_util.run_deprecated_v1 def testUnknownInputChannels(self): images = array_ops.placeholder(dtypes.float32, (5, 7, 9, None)) layer = conv_layers.Conv2D(32, [3, 3], activation=nn_ops.relu) @@ -140,6 +144,7 @@ class ConvTest(test.TestCase): self.assertListEqual(output.get_shape().as_list(), [5, height / 2, width, 32]) + @test_util.run_deprecated_v1 def testCreateConv1D(self): width = 7 data = random_ops.random_uniform((5, width, 4)) @@ -156,6 +161,7 @@ class ConvTest(test.TestCase): output = conv_layers.conv1d(data, 32, 3, activation=nn_ops.relu) self.assertListEqual(output.get_shape().as_list(), [5, width - 2, 32]) + @test_util.run_deprecated_v1 def testCreateConv1DChannelsFirst(self): width = 7 data = random_ops.random_uniform((5, 4, width)) @@ -165,6 +171,7 @@ class ConvTest(test.TestCase): self.assertListEqual(layer.kernel.get_shape().as_list(), [3, 4, 32]) self.assertListEqual(layer.bias.get_shape().as_list(), [32]) + @test_util.run_deprecated_v1 def testUnknownInputChannelsConv1D(self): data = array_ops.placeholder(dtypes.float32, (5, 4, None)) layer = conv_layers.Conv1D(32, 3, activation=nn_ops.relu) @@ -180,6 +187,7 @@ class ConvTest(test.TestCase): 'should be defined. Found `None`.'): _ = layer.apply(data) + @test_util.run_deprecated_v1 def testCreateConv3D(self): depth, height, width = 6, 7, 9 volumes = random_ops.random_uniform((5, depth, height, width, 4)) @@ -191,6 +199,7 @@ class ConvTest(test.TestCase): self.assertListEqual(layer.kernel.get_shape().as_list(), [3, 3, 3, 4, 32]) self.assertListEqual(layer.bias.get_shape().as_list(), [32]) + @test_util.run_deprecated_v1 def testUnknownInputChannelsConv3D(self): volumes = array_ops.placeholder(dtypes.float32, (5, 6, 7, 9, None)) layer = conv_layers.Conv3D(32, [3, 3, 3], activation=nn_ops.relu) @@ -199,6 +208,7 @@ class ConvTest(test.TestCase): 'should be defined. Found `None`.'): _ = layer.apply(volumes) + @test_util.run_deprecated_v1 def testConv2DKernelRegularizer(self): height, width = 7, 9 images = random_ops.random_uniform((5, height, width, 4)) @@ -210,6 +220,7 @@ class ConvTest(test.TestCase): self.evaluate([v.initializer for v in layer.variables]) self.assertListEqual(self.evaluate(layer.losses), self.evaluate(loss_keys)) + @test_util.run_deprecated_v1 def testConv2DBiasRegularizer(self): height, width = 7, 9 images = random_ops.random_uniform((5, height, width, 4)) @@ -221,6 +232,7 @@ class ConvTest(test.TestCase): self.evaluate([v.initializer for v in layer.variables]) self.assertListEqual(self.evaluate(layer.losses), self.evaluate(loss_keys)) + @test_util.run_deprecated_v1 def testConv2DNoBias(self): height, width = 7, 9 images = random_ops.random_uniform((5, height, width, 4)) @@ -247,6 +259,7 @@ class ConvTest(test.TestCase): output = layer.apply(images) self.assertListEqual(output.get_shape().as_list(), [5, height - 2, 3, 32]) + @test_util.run_deprecated_v1 def testFunctionalConv2DReuse(self): height, width = 7, 9 images = random_ops.random_uniform((5, height, width, 3), seed=1) @@ -255,6 +268,7 @@ class ConvTest(test.TestCase): conv_layers.conv2d(images, 32, [3, 3], name='conv1', reuse=True) self.assertEqual(len(variables.trainable_variables()), 2) + @test_util.run_deprecated_v1 def testFunctionalConv2DReuseFromScope(self): with variable_scope.variable_scope('scope'): height, width = 7, 9 @@ -265,6 +279,7 @@ class ConvTest(test.TestCase): conv_layers.conv2d(images, 32, [3, 3], name='conv1') self.assertEqual(len(variables.trainable_variables()), 2) + @test_util.run_deprecated_v1 def testFunctionalConv2DInitializerFromScope(self): with self.cached_session() as sess: with variable_scope.variable_scope( @@ -276,13 +291,14 @@ class ConvTest(test.TestCase): # Check the names of weights in order. self.assertTrue('kernel' in weights[0].name) self.assertTrue('bias' in weights[1].name) - sess.run(variables.global_variables_initializer()) - weights = sess.run(weights) + self.evaluate(variables.global_variables_initializer()) + weights = self.evaluate(weights) # Check that the kernel weights got initialized to ones (from scope) self.assertAllClose(weights[0], np.ones((3, 3, 3, 32))) # Check that the bias still got initialized to zeros. self.assertAllClose(weights[1], np.zeros((32))) + @test_util.run_deprecated_v1 def testFunctionalConv2DNoReuse(self): height, width = 7, 9 images = random_ops.random_uniform((5, height, width, 3), seed=1) @@ -325,6 +341,7 @@ class ConvTest(test.TestCase): self.assertEqual(conv3d.kernel_constraint, k_constraint) self.assertEqual(conv3d.bias_constraint, b_constraint) + @test_util.run_deprecated_v1 def testConv3DChannelsFirst(self): # Test case for GitHub issue 15655 images = array_ops.placeholder( @@ -358,6 +375,7 @@ class SeparableConv1DTest(test.TestCase): with self.assertRaisesRegexp(ValueError, 'kernel_size'): conv_layers.separable_conv1d(data, 32, None) + @test_util.run_deprecated_v1 def testCreateSeparableConv1D(self): length = 9 data = random_ops.random_uniform((5, length, 4)) @@ -379,6 +397,7 @@ class SeparableConv1DTest(test.TestCase): self.assertEqual(layer.pointwise_kernel.get_shape().as_list(), [1, 8, 32]) self.assertEqual(layer.bias.get_shape().as_list(), [32]) + @test_util.run_deprecated_v1 def testCreateSeparableConv1DChannelsFirst(self): length = 9 data = random_ops.random_uniform((5, 4, length)) @@ -404,6 +423,7 @@ class SeparableConv1DTest(test.TestCase): output = layer.apply(data) self.assertEqual(output.get_shape().as_list(), [5, length // 2, 32]) + @test_util.run_deprecated_v1 def testCreateSeparableConv1DWithStridesChannelsFirst(self): data_format = 'channels_first' length = 10 @@ -413,6 +433,7 @@ class SeparableConv1DTest(test.TestCase): output = layer.apply(data) self.assertEqual(output.get_shape().as_list(), [5, 32, length // 2]) + @test_util.run_deprecated_v1 def testFunctionalConv1DReuse(self): length = 10 data = random_ops.random_uniform((5, length, 3), seed=1) @@ -421,6 +442,7 @@ class SeparableConv1DTest(test.TestCase): conv_layers.separable_conv1d(data, 32, 3, name='sepconv1', reuse=True) self.assertEqual(len(variables.trainable_variables()), 3) + @test_util.run_deprecated_v1 def testFunctionalConv1DReuseFromScope(self): with variable_scope.variable_scope('scope'): length = 10 @@ -431,6 +453,7 @@ class SeparableConv1DTest(test.TestCase): conv_layers.separable_conv1d(data, 32, 3, name='sepconv1') self.assertEqual(len(variables.trainable_variables()), 3) + @test_util.run_deprecated_v1 def testFunctionalConv1DNoReuse(self): length = 10 data = random_ops.random_uniform((5, length, 3), seed=1) @@ -439,6 +462,7 @@ class SeparableConv1DTest(test.TestCase): conv_layers.separable_conv1d(data, 32, 3) self.assertEqual(len(variables.trainable_variables()), 6) + @test_util.run_deprecated_v1 def testSeparableConv1DDepthwiseRegularizer(self): length = 9 data = random_ops.random_uniform((5, length, 4)) @@ -450,6 +474,7 @@ class SeparableConv1DTest(test.TestCase): self.evaluate([v.initializer for v in layer.variables]) self.assertListEqual(self.evaluate(layer.losses), self.evaluate(loss_keys)) + @test_util.run_deprecated_v1 def testSeparableConv1DPointwiseRegularizer(self): length = 9 data = random_ops.random_uniform((5, length, 4)) @@ -461,6 +486,7 @@ class SeparableConv1DTest(test.TestCase): self.evaluate([v.initializer for v in layer.variables]) self.assertListEqual(self.evaluate(layer.losses), self.evaluate(loss_keys)) + @test_util.run_deprecated_v1 def testSeparableConv1DBiasRegularizer(self): length = 9 data = random_ops.random_uniform((5, length, 4)) @@ -472,6 +498,7 @@ class SeparableConv1DTest(test.TestCase): self.evaluate([v.initializer for v in layer.variables]) self.assertListEqual(self.evaluate(layer.losses), self.evaluate(loss_keys)) + @test_util.run_deprecated_v1 def testSeparableConv1DNoBias(self): length = 9 data = random_ops.random_uniform((5, length, 4)) @@ -522,6 +549,7 @@ class SeparableConv2DTest(test.TestCase): with self.assertRaisesRegexp(ValueError, 'kernel_size'): conv_layers.separable_conv2d(images, 32, None) + @test_util.run_deprecated_v1 def testCreateSeparableConv2D(self): height, width = 7, 9 images = random_ops.random_uniform((5, height, width, 4)) @@ -562,6 +590,7 @@ class SeparableConv2DTest(test.TestCase): [1, 1, 4, 32]) self.assertListEqual(layer.bias.get_shape().as_list(), [32]) + @test_util.run_deprecated_v1 def testCreateSeparableConv2DChannelsFirst(self): height, width = 7, 9 images = random_ops.random_uniform((5, 4, height, width)) @@ -584,6 +613,7 @@ class SeparableConv2DTest(test.TestCase): output = layer.apply(images) self.assertListEqual(output.get_shape().as_list(), [5, height, width, 64]) + @test_util.run_deprecated_v1 def testCreateSeparableConvWithStrides(self): height, width = 6, 8 # Test strides tuple @@ -607,6 +637,7 @@ class SeparableConv2DTest(test.TestCase): self.assertListEqual(output.get_shape().as_list(), [5, height / 2, width, 32]) + @test_util.run_deprecated_v1 def testCreateSeparableConvWithStridesChannelsFirst(self): data_format = 'channels_first' height, width = 6, 8 @@ -632,6 +663,7 @@ class SeparableConv2DTest(test.TestCase): self.assertListEqual(output.get_shape().as_list(), [5, 32, height / 2, width]) + @test_util.run_deprecated_v1 def testFunctionalConv2DReuse(self): height, width = 7, 9 images = random_ops.random_uniform((5, height, width, 3), seed=1) @@ -641,6 +673,7 @@ class SeparableConv2DTest(test.TestCase): images, 32, [3, 3], name='sepconv1', reuse=True) self.assertEqual(len(variables.trainable_variables()), 3) + @test_util.run_deprecated_v1 def testFunctionalConv2DReuseFromScope(self): with variable_scope.variable_scope('scope'): height, width = 7, 9 @@ -651,6 +684,7 @@ class SeparableConv2DTest(test.TestCase): conv_layers.separable_conv2d(images, 32, [3, 3], name='sepconv1') self.assertEqual(len(variables.trainable_variables()), 3) + @test_util.run_deprecated_v1 def testFunctionalConv2DInitializerFromScope(self): with self.cached_session() as sess: with variable_scope.variable_scope( @@ -663,14 +697,15 @@ class SeparableConv2DTest(test.TestCase): self.assertTrue('depthwise_kernel' in weights[0].name) self.assertTrue('pointwise_kernel' in weights[1].name) self.assertTrue('bias' in weights[2].name) - sess.run(variables.global_variables_initializer()) - weights = sess.run(weights) + self.evaluate(variables.global_variables_initializer()) + weights = self.evaluate(weights) # Check that the kernel weights got initialized to ones (from scope) self.assertAllClose(weights[0], np.ones((3, 3, 3, 1))) self.assertAllClose(weights[1], np.ones((1, 1, 3, 32))) # Check that the bias still got initialized to zeros. self.assertAllClose(weights[2], np.zeros((32))) + @test_util.run_deprecated_v1 def testFunctionalConv2DNoReuse(self): height, width = 7, 9 images = random_ops.random_uniform((5, height, width, 3), seed=1) @@ -679,6 +714,7 @@ class SeparableConv2DTest(test.TestCase): conv_layers.separable_conv2d(images, 32, [3, 3]) self.assertEqual(len(variables.trainable_variables()), 6) + @test_util.run_deprecated_v1 def testSeparableConv2DDepthwiseRegularizer(self): height, width = 7, 9 images = random_ops.random_uniform((5, height, width, 4)) @@ -690,6 +726,7 @@ class SeparableConv2DTest(test.TestCase): self.evaluate([v.initializer for v in layer.variables]) self.assertListEqual(self.evaluate(layer.losses), self.evaluate(loss_keys)) + @test_util.run_deprecated_v1 def testSeparableConv2DPointwiseRegularizer(self): height, width = 7, 9 images = random_ops.random_uniform((5, height, width, 4)) @@ -701,6 +738,7 @@ class SeparableConv2DTest(test.TestCase): self.evaluate([v.initializer for v in layer.variables]) self.assertListEqual(self.evaluate(layer.losses), self.evaluate(loss_keys)) + @test_util.run_deprecated_v1 def testSeparableConv2DBiasRegularizer(self): height, width = 7, 9 images = random_ops.random_uniform((5, height, width, 4)) @@ -712,6 +750,7 @@ class SeparableConv2DTest(test.TestCase): self.evaluate([v.initializer for v in layer.variables]) self.assertListEqual(self.evaluate(layer.losses), self.evaluate(loss_keys)) + @test_util.run_deprecated_v1 def testSeparableConv2DNoBias(self): height, width = 7, 9 images = random_ops.random_uniform((5, height, width, 4)) @@ -768,6 +807,7 @@ class Conv2DTransposeTest(test.TestCase): with self.assertRaisesRegexp(ValueError, 'kernel_size'): conv_layers.conv2d_transpose(images, 32, None) + @test_util.run_deprecated_v1 def testCreateConv2DTranspose(self): height, width = 7, 9 images = random_ops.random_uniform((5, height, width, 4)) @@ -839,6 +879,7 @@ class Conv2DTransposeTest(test.TestCase): self.assertListEqual(output.get_shape().as_list(), [5, height * 2, width, 32]) + @test_util.run_deprecated_v1 def testConv2DTransposeKernelRegularizer(self): height, width = 7, 9 images = random_ops.random_uniform((5, height, width, 4)) @@ -850,6 +891,7 @@ class Conv2DTransposeTest(test.TestCase): self.evaluate([v.initializer for v in layer.variables]) self.assertListEqual(self.evaluate(layer.losses), self.evaluate(loss_keys)) + @test_util.run_deprecated_v1 def testConv2DTransposeBiasRegularizer(self): height, width = 7, 9 images = random_ops.random_uniform((5, height, width, 4)) @@ -861,6 +903,7 @@ class Conv2DTransposeTest(test.TestCase): self.evaluate([v.initializer for v in layer.variables]) self.assertListEqual(self.evaluate(layer.losses), self.evaluate(loss_keys)) + @test_util.run_deprecated_v1 def testConv2DTransposeNoBias(self): height, width = 7, 9 images = random_ops.random_uniform((5, height, width, 4)) @@ -873,6 +916,7 @@ class Conv2DTransposeTest(test.TestCase): self.assertListEqual(layer.kernel.get_shape().as_list(), [3, 3, 32, 4]) self.assertEqual(layer.bias, None) + @test_util.run_deprecated_v1 def testFunctionalConv2DTransposeReuse(self): height, width = 7, 9 images = random_ops.random_uniform((5, height, width, 3), seed=1) @@ -881,6 +925,7 @@ class Conv2DTransposeTest(test.TestCase): conv_layers.conv2d_transpose(images, 32, [3, 3], name='deconv1', reuse=True) self.assertEqual(len(variables.trainable_variables()), 2) + @test_util.run_deprecated_v1 def testFunctionalConv2DTransposeReuseFromScope(self): with variable_scope.variable_scope('scope'): height, width = 7, 9 @@ -891,6 +936,7 @@ class Conv2DTransposeTest(test.TestCase): conv_layers.conv2d_transpose(images, 32, [3, 3], name='deconv1') self.assertEqual(len(variables.trainable_variables()), 2) + @test_util.run_deprecated_v1 def testFunctionalConv2DTransposeInitializerFromScope(self): with self.cached_session() as sess: with variable_scope.variable_scope( @@ -902,13 +948,14 @@ class Conv2DTransposeTest(test.TestCase): # Check the names of weights in order. self.assertTrue('kernel' in weights[0].name) self.assertTrue('bias' in weights[1].name) - sess.run(variables.global_variables_initializer()) - weights = sess.run(weights) + self.evaluate(variables.global_variables_initializer()) + weights = self.evaluate(weights) # Check that the kernel weights got initialized to ones (from scope) self.assertAllClose(weights[0], np.ones((3, 3, 32, 3))) # Check that the bias still got initialized to zeros. self.assertAllClose(weights[1], np.zeros((32))) + @test_util.run_deprecated_v1 def testFunctionalConv2DTransposeNoReuse(self): height, width = 7, 9 images = random_ops.random_uniform((5, height, width, 3), seed=1) @@ -955,6 +1002,7 @@ class Conv3DTransposeTest(test.TestCase): with self.assertRaisesRegexp(ValueError, 'kernel_size'): conv_layers.conv3d_transpose(volumes, 4, None) + @test_util.run_deprecated_v1 def testCreateConv3DTranspose(self): depth, height, width = 5, 7, 9 volumes = random_ops.random_uniform((5, depth, height, width, 32)) @@ -976,6 +1024,7 @@ class Conv3DTransposeTest(test.TestCase): self.assertListEqual(layer.kernel.get_shape().as_list(), [3, 3, 3, 4, 32]) self.assertListEqual(layer.bias.get_shape().as_list(), [4]) + @test_util.run_deprecated_v1 def testCreateConv3DTransposeChannelsFirst(self): depth, height, width = 5, 7, 9 volumes = random_ops.random_uniform((5, 32, depth, height, width)) @@ -1019,6 +1068,7 @@ class Conv3DTransposeTest(test.TestCase): self.assertListEqual(output.get_shape().as_list(), [5, depth * 2, height, width, 4]) + @test_util.run_deprecated_v1 def testConv3DTransposeKernelRegularizer(self): depth, height, width = 5, 7, 9 volumes = random_ops.random_uniform((5, depth, height, width, 32)) @@ -1030,6 +1080,7 @@ class Conv3DTransposeTest(test.TestCase): self.evaluate([v.initializer for v in layer.variables]) self.assertListEqual(self.evaluate(layer.losses), self.evaluate(loss_keys)) + @test_util.run_deprecated_v1 def testConv3DTransposeBiasRegularizer(self): depth, height, width = 5, 7, 9 volumes = random_ops.random_uniform((5, depth, height, width, 32)) @@ -1041,6 +1092,7 @@ class Conv3DTransposeTest(test.TestCase): self.evaluate([v.initializer for v in layer.variables]) self.assertListEqual(self.evaluate(layer.losses), self.evaluate(loss_keys)) + @test_util.run_deprecated_v1 def testConv3DTransposeNoBias(self): depth, height, width = 5, 7, 9 volumes = random_ops.random_uniform((5, depth, height, width, 32)) @@ -1053,6 +1105,7 @@ class Conv3DTransposeTest(test.TestCase): self.assertListEqual(layer.kernel.get_shape().as_list(), [3, 3, 3, 4, 32]) self.assertEqual(layer.bias, None) + @test_util.run_deprecated_v1 def testFunctionalConv3DTransposeReuse(self): depth, height, width = 5, 7, 9 volumes = random_ops.random_uniform((5, depth, height, width, 32), seed=1) @@ -1062,6 +1115,7 @@ class Conv3DTransposeTest(test.TestCase): volumes, 4, [3, 3, 3], name='deconv1', reuse=True) self.assertEqual(len(variables.trainable_variables()), 2) + @test_util.run_deprecated_v1 def testFunctionalConv3DTransposeReuseFromScope(self): with variable_scope.variable_scope('scope'): depth, height, width = 5, 7, 9 @@ -1072,6 +1126,7 @@ class Conv3DTransposeTest(test.TestCase): conv_layers.conv3d_transpose(volumes, 4, [3, 3, 3], name='deconv1') self.assertEqual(len(variables.trainable_variables()), 2) + @test_util.run_deprecated_v1 def testFunctionalConv3DTransposeInitializerFromScope(self): with self.cached_session() as sess: with variable_scope.variable_scope( @@ -1084,13 +1139,14 @@ class Conv3DTransposeTest(test.TestCase): # Check the names of weights in order. self.assertTrue('kernel' in weights[0].name) self.assertTrue('bias' in weights[1].name) - sess.run(variables.global_variables_initializer()) - weights = sess.run(weights) + self.evaluate(variables.global_variables_initializer()) + weights = self.evaluate(weights) # Check that the kernel weights got initialized to ones (from scope) self.assertAllClose(weights[0], np.ones((3, 3, 3, 4, 32))) # Check that the bias still got initialized to zeros. self.assertAllClose(weights[1], np.zeros((4))) + @test_util.run_deprecated_v1 def testFunctionalConv3DTransposeNoReuse(self): depth, height, width = 5, 7, 9 volumes = random_ops.random_uniform((5, depth, height, width, 32), seed=1) diff --git a/tensorflow/python/layers/core_test.py b/tensorflow/python/layers/core_test.py index 0343bfa8bd..cf6f0fbb70 100644 --- a/tensorflow/python/layers/core_test.py +++ b/tensorflow/python/layers/core_test.py @@ -59,6 +59,7 @@ class DenseTest(test.TestCase): dense.apply(random_ops.random_uniform((5, 2))) self.assertEqual(dense.name, 'dense_2') + @test_util.run_deprecated_v1 def testVariableInput(self): with self.cached_session(): v = variable_scope.get_variable( @@ -140,6 +141,7 @@ class DenseTest(test.TestCase): outputs = dense.apply(inputs) self.assertEqual(outputs.get_shape().as_list(), [1, 2, 4, 7]) + @test_util.run_deprecated_v1 def testCallOnPlaceHolder(self): inputs = array_ops.placeholder(dtype=dtypes.float32) dense = core_layers.Dense(4, name='my_dense') @@ -179,6 +181,7 @@ class DenseTest(test.TestCase): if not context.executing_eagerly(): self.assertEqual(outputs.op.name, 'dense2/BiasAdd') + @test_util.run_deprecated_v1 def testActivityRegularizer(self): regularizer = lambda x: math_ops.reduce_sum(x) * 1e-3 dense = core_layers.Dense( @@ -189,6 +192,7 @@ class DenseTest(test.TestCase): self.assertEqual(len(loss_keys), 1) self.assertListEqual(dense.losses, loss_keys) + @test_util.run_deprecated_v1 def testKernelRegularizer(self): regularizer = lambda x: math_ops.reduce_sum(x) * 1e-3 dense = core_layers.Dense( @@ -200,6 +204,7 @@ class DenseTest(test.TestCase): self.evaluate([v.initializer for v in dense.variables]) self.assertAllEqual(self.evaluate(dense.losses), self.evaluate(loss_keys)) + @test_util.run_deprecated_v1 def testKernelRegularizerWithReuse(self): regularizer = lambda x: math_ops.reduce_sum(x) * 1e-3 inputs = random_ops.random_uniform((5, 3), seed=1) @@ -212,6 +217,7 @@ class DenseTest(test.TestCase): self.assertEqual( len(ops.get_collection(ops.GraphKeys.REGULARIZATION_LOSSES)), 1) + @test_util.run_deprecated_v1 def testBiasRegularizer(self): regularizer = lambda x: math_ops.reduce_sum(x) * 1e-3 dense = core_layers.Dense(2, name='my_dense', bias_regularizer=regularizer) @@ -222,6 +228,7 @@ class DenseTest(test.TestCase): self.evaluate([v.initializer for v in dense.variables]) self.assertAllEqual(self.evaluate(dense.losses), self.evaluate(loss_keys)) + @test_util.run_deprecated_v1 def testFunctionalDense(self): with self.cached_session(): inputs = random_ops.random_uniform((5, 3), seed=1) @@ -231,6 +238,7 @@ class DenseTest(test.TestCase): len(ops.get_collection(ops.GraphKeys.TRAINABLE_VARIABLES)), 2) self.assertEqual(outputs.op.name, 'my_dense/Relu') + @test_util.run_deprecated_v1 def testFunctionalDenseTwice(self): inputs = random_ops.random_uniform((5, 3), seed=1) core_layers.dense(inputs, 2) @@ -262,6 +270,7 @@ class DenseTest(test.TestCase): vars2 = variables.trainable_variables() self.assertEqual(vars1, vars2) + @test_util.run_deprecated_v1 def testFunctionalDenseInitializerFromScope(self): with variable_scope.variable_scope( 'scope', @@ -307,6 +316,7 @@ class DenseTest(test.TestCase): core_layers.dense(inputs, 2) self.assertEqual(called[0], 2) + @test_util.run_deprecated_v1 def testFunctionalDenseInScope(self): with self.cached_session(): with variable_scope.variable_scope('test'): @@ -393,6 +403,7 @@ class DropoutTest(test.TestCase): np_output = self.evaluate(dropped) self.assertAllClose(np.ones((5, 3)), np_output) + @test_util.run_deprecated_v1 def testDynamicLearningPhase(self): with self.cached_session() as sess: dp = core_layers.Dropout(0.5, seed=1) @@ -426,6 +437,7 @@ class DropoutTest(test.TestCase): self.assertAlmostEqual(0., np_output.min()) self.assertAllClose(np_output[:, 0, :], np_output[:, 1, :]) + @test_util.run_deprecated_v1 def testFunctionalDropout(self): with self.cached_session(): inputs = array_ops.ones((5, 5)) @@ -437,13 +449,14 @@ class DropoutTest(test.TestCase): np_output = self.evaluate(dropped) self.assertAllClose(np.ones((5, 5)), np_output) + @test_util.run_deprecated_v1 def testDynamicRate(self): with self.cached_session() as sess: rate = array_ops.placeholder(dtype='float32', name='rate') dp = core_layers.Dropout(rate, name='dropout') inputs = array_ops.ones((5, 5)) dropped = dp.apply(inputs, training=True) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) np_output = sess.run(dropped, feed_dict={rate: 0.5}) self.assertAlmostEqual(0., np_output.min()) np_output = sess.run(dropped, feed_dict={rate: 0.0}) @@ -452,6 +465,7 @@ class DropoutTest(test.TestCase): class FlattenTest(test.TestCase): + @test_util.run_deprecated_v1 def testCreateFlatten(self): with self.cached_session() as sess: x = array_ops.placeholder(shape=(None, 2, 3), dtype='float32') @@ -476,6 +490,7 @@ class FlattenTest(test.TestCase): shape = core_layers.Flatten().compute_output_shape((None, 3, None)) self.assertEqual(shape.as_list(), [None, None]) + @test_util.run_deprecated_v1 def testDataFormat5d(self): np_input_channels_last = np.arange( 120, dtype='float32').reshape([1, 5, 4, 3, 2]) @@ -493,6 +508,7 @@ class FlattenTest(test.TestCase): self.assertAllEqual(np_output_cl, np_output_cf) + @test_util.run_deprecated_v1 def testDataFormat4d(self): np_input_channels_last = np.arange( 24, dtype='float32').reshape([1, 4, 3, 2]) @@ -510,16 +526,19 @@ class FlattenTest(test.TestCase): self.assertAllEqual(np_output_cl, np_output_cf) + @test_util.run_deprecated_v1 def testFunctionalFlatten(self): x = array_ops.placeholder(shape=(None, 2, 3), dtype='float32') y = core_layers.flatten(x, name='flatten') self.assertEqual(y.get_shape().as_list(), [None, 6]) + @test_util.run_deprecated_v1 def testFlattenValueError(self): x = array_ops.placeholder(shape=(None,), dtype='float32') with self.assertRaises(ValueError): core_layers.Flatten()(x) + @test_util.run_deprecated_v1 def testFlattenUnknownAxes(self): with self.cached_session() as sess: x = array_ops.placeholder(shape=(5, None, None), dtype='float32') diff --git a/tensorflow/python/layers/layers.py b/tensorflow/python/layers/layers.py index 11a2ebc040..93eec38a08 100644 --- a/tensorflow/python/layers/layers.py +++ b/tensorflow/python/layers/layers.py @@ -24,7 +24,7 @@ from __future__ import print_function # Base objects. from tensorflow.python.layers.base import Layer -from tensorflow.python.layers.base import InputSpec +from tensorflow.python.keras.engine.input_spec import InputSpec # Core layers. from tensorflow.python.layers.core import Dense diff --git a/tensorflow/python/layers/normalization_test.py b/tensorflow/python/layers/normalization_test.py index ba2bf10cf3..07d8e40b75 100644 --- a/tensorflow/python/layers/normalization_test.py +++ b/tensorflow/python/layers/normalization_test.py @@ -24,6 +24,7 @@ import numpy as np from tensorflow.core.protobuf import saver_pb2 from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.layers import convolutional as conv_layers from tensorflow.python.layers import normalization as normalization_layers from tensorflow.python.ops import array_ops @@ -78,7 +79,7 @@ class BNTest(test.TestCase): if restore: saver.restore(sess, checkpoint_path) else: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) np.random.seed(0) for _ in range(2): image_val = np.random.rand(*shape).astype(dtype.as_numpy_dtype) @@ -143,6 +144,7 @@ class BNTest(test.TestCase): return train_vars, loss_val + @test_util.run_deprecated_v1 def testHalfPrecision(self): ref_vars, ref_loss = self._trainEvalSequence( dtype=dtypes.float32, @@ -228,33 +230,43 @@ class BNTest(test.TestCase): ckpt_b_use_gpu, use_gpu_test_a, use_gpu_test_b, freeze_mode) + @test_util.run_deprecated_v1 def testCheckpointFusedCPUAndFusedGPU(self): self._testCheckpointCrossDevice(True, False, True, True) + @test_util.run_deprecated_v1 def testCheckpointFusedCPUAndFusedCPU(self): self._testCheckpointCrossDevice(True, False, True, False) + @test_util.run_deprecated_v1 def testCheckpointFusedGPUAndFusedGPU(self): self._testCheckpointCrossDevice(True, True, True, True) + @test_util.run_deprecated_v1 def testCheckpointNonFusedCPUAndNonFusedGPU(self): self._testCheckpointCrossDevice(False, False, False, True) + @test_util.run_deprecated_v1 def testCheckpointNonFusedCPUAndNonFusedCPU(self): self._testCheckpointCrossDevice(False, False, False, False) + @test_util.run_deprecated_v1 def testCheckpointNonFusedGPUAndNonFusedGPU(self): self._testCheckpointCrossDevice(False, True, False, True) + @test_util.run_deprecated_v1 def testCheckpointNonFusedGPUAndFusedGPU(self): self._testCheckpointCrossDevice(False, True, True, True) + @test_util.run_deprecated_v1 def testCheckpointNonFusedGPUAndFusedCPU(self): self._testCheckpointCrossDevice(False, True, True, False) + @test_util.run_deprecated_v1 def testCheckpointNonFusedCPUAndFusedCPU(self): self._testCheckpointCrossDevice(False, False, True, False) + @test_util.run_deprecated_v1 def testCreateBN(self): # Call layer. bn = normalization_layers.BatchNormalization(axis=1) @@ -281,6 +293,7 @@ class BNTest(test.TestCase): ops.get_collection(ops.GraphKeys.TRAINABLE_VARIABLES), bn.trainable_variables) + @test_util.run_deprecated_v1 def testCreateFusedBNFloat16(self): # Call layer. bn = normalization_layers.BatchNormalization(axis=1, fused=True) @@ -310,6 +323,7 @@ class BNTest(test.TestCase): ops.get_collection(ops.GraphKeys.TRAINABLE_VARIABLES), bn.trainable_variables) + @test_util.run_deprecated_v1 def test3DInputAxis1(self): epsilon = 1e-3 bn = normalization_layers.BatchNormalization( @@ -321,9 +335,9 @@ class BNTest(test.TestCase): with self.cached_session() as sess: # Test training with placeholder learning phase. - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) - np_gamma, np_beta = sess.run([bn.gamma, bn.beta]) + np_gamma, np_beta = self.evaluate([bn.gamma, bn.beta]) np_gamma = np.reshape(np_gamma, (1, 4, 1)) np_beta = np.reshape(np_beta, (1, 4, 1)) @@ -336,8 +350,9 @@ class BNTest(test.TestCase): self.assertAlmostEqual(np.std(normed_np_output), 1., places=1) # Verify that the statistics are updated during training. - moving_mean, moving_var = sess.run([bn.moving_mean, bn.moving_variance]) - np_inputs = sess.run(inputs) + moving_mean, moving_var = self.evaluate( + [bn.moving_mean, bn.moving_variance]) + np_inputs = self.evaluate(inputs) mean = np.mean(np_inputs, axis=(0, 2)) std = np.std(np_inputs, axis=(0, 2)) variance = np.square(std) @@ -352,6 +367,7 @@ class BNTest(test.TestCase): self.assertAlmostEqual(np.mean(normed_np_output), 0., places=1) self.assertAlmostEqual(np.std(normed_np_output), 1., places=1) + @test_util.run_deprecated_v1 def test3DInputAxis2(self): epsilon = 1e-3 bn = normalization_layers.BatchNormalization( @@ -363,8 +379,8 @@ class BNTest(test.TestCase): with self.cached_session() as sess: # Test training with placeholder learning phase. - sess.run(variables.global_variables_initializer()) - np_gamma, np_beta = sess.run([bn.gamma, bn.beta]) + self.evaluate(variables.global_variables_initializer()) + np_gamma, np_beta = self.evaluate([bn.gamma, bn.beta]) np_gamma = np.reshape(np_gamma, (1, 1, 3)) np_beta = np.reshape(np_beta, (1, 1, 3)) for _ in range(100): @@ -376,8 +392,9 @@ class BNTest(test.TestCase): self.assertAlmostEqual(np.std(normed_np_output), 1., places=1) # Verify that the statistics are updated during training. - moving_mean, moving_var = sess.run([bn.moving_mean, bn.moving_variance]) - np_inputs = sess.run(inputs) + moving_mean, moving_var = self.evaluate( + [bn.moving_mean, bn.moving_variance]) + np_inputs = self.evaluate(inputs) mean = np.mean(np_inputs, axis=(0, 1)) std = np.std(np_inputs, axis=(0, 1)) variance = np.square(std) @@ -404,8 +421,8 @@ class BNTest(test.TestCase): with self.session(use_gpu=True) as sess: # Test training with placeholder learning phase. - sess.run(variables.global_variables_initializer()) - np_gamma, np_beta = sess.run([bn.gamma, bn.beta]) + self.evaluate(variables.global_variables_initializer()) + np_gamma, np_beta = self.evaluate([bn.gamma, bn.beta]) np_gamma = np.reshape(np_gamma, (1, 4, 1, 1)) np_beta = np.reshape(np_beta, (1, 4, 1, 1)) for _ in range(100): @@ -417,8 +434,9 @@ class BNTest(test.TestCase): self.assertAlmostEqual(np.std(normed_np_output), 1., places=1) # Verify that the statistics are updated during training. - moving_mean, moving_var = sess.run([bn.moving_mean, bn.moving_variance]) - np_inputs = sess.run(inputs) + moving_mean, moving_var = self.evaluate( + [bn.moving_mean, bn.moving_variance]) + np_inputs = self.evaluate(inputs) mean = np.mean(np_inputs, axis=(0, 2, 3)) std = np.std(np_inputs, axis=(0, 2, 3)) variance = np.square(std) @@ -433,6 +451,7 @@ class BNTest(test.TestCase): self.assertAlmostEqual(np.mean(normed_np_output), 0., places=1) self.assertAlmostEqual(np.std(normed_np_output), 1., places=1) + @test_util.run_deprecated_v1 def test4DInputAxis2(self): epsilon = 1e-3 bn = normalization_layers.BatchNormalization( @@ -444,8 +463,8 @@ class BNTest(test.TestCase): with self.cached_session() as sess: # Test training with placeholder learning phase. - sess.run(variables.global_variables_initializer()) - np_gamma, np_beta = sess.run([bn.gamma, bn.beta]) + self.evaluate(variables.global_variables_initializer()) + np_gamma, np_beta = self.evaluate([bn.gamma, bn.beta]) np_gamma = np.reshape(np_gamma, (1, 1, 3, 1)) np_beta = np.reshape(np_beta, (1, 1, 3, 1)) for _ in range(100): @@ -457,8 +476,9 @@ class BNTest(test.TestCase): self.assertAlmostEqual(np.std(normed_np_output), 1., places=1) # Verify that the statistics are updated during training. - moving_mean, moving_var = sess.run([bn.moving_mean, bn.moving_variance]) - np_inputs = sess.run(inputs) + moving_mean, moving_var = self.evaluate( + [bn.moving_mean, bn.moving_variance]) + np_inputs = self.evaluate(inputs) mean = np.mean(np_inputs, axis=(0, 1, 3)) std = np.std(np_inputs, axis=(0, 1, 3)) variance = np.square(std) @@ -473,6 +493,7 @@ class BNTest(test.TestCase): self.assertAlmostEqual(np.mean(normed_np_output), 0., places=1) self.assertAlmostEqual(np.std(normed_np_output), 1., places=1) + @test_util.run_deprecated_v1 def test4DInputAxis3(self): epsilon = 1e-3 bn = normalization_layers.BatchNormalization( @@ -484,8 +505,8 @@ class BNTest(test.TestCase): with self.cached_session() as sess: # Test training with placeholder learning phase. - sess.run(variables.global_variables_initializer()) - np_gamma, np_beta = sess.run([bn.gamma, bn.beta]) + self.evaluate(variables.global_variables_initializer()) + np_gamma, np_beta = self.evaluate([bn.gamma, bn.beta]) np_gamma = np.reshape(np_gamma, (1, 1, 1, 6)) np_beta = np.reshape(np_beta, (1, 1, 1, 6)) for _ in range(100): @@ -497,8 +518,9 @@ class BNTest(test.TestCase): self.assertAlmostEqual(np.std(normed_np_output), 1., places=1) # Verify that the statistics are updated during training. - moving_mean, moving_var = sess.run([bn.moving_mean, bn.moving_variance]) - np_inputs = sess.run(inputs) + moving_mean, moving_var = self.evaluate( + [bn.moving_mean, bn.moving_variance]) + np_inputs = self.evaluate(inputs) mean = np.mean(np_inputs, axis=(0, 1, 2)) std = np.std(np_inputs, axis=(0, 1, 2)) variance = np.square(std) @@ -513,6 +535,7 @@ class BNTest(test.TestCase): self.assertAlmostEqual(np.mean(normed_np_output), 0., places=1) self.assertAlmostEqual(np.std(normed_np_output), 1., places=1) + @test_util.run_deprecated_v1 def test4DInputAxis3Fused(self): epsilon = 1e-3 bn = normalization_layers.BatchNormalization( @@ -524,8 +547,8 @@ class BNTest(test.TestCase): with self.cached_session() as sess: # Test training with placeholder learning phase. - sess.run(variables.global_variables_initializer()) - np_gamma, np_beta = sess.run([bn.gamma, bn.beta]) + self.evaluate(variables.global_variables_initializer()) + np_gamma, np_beta = self.evaluate([bn.gamma, bn.beta]) np_gamma = np.reshape(np_gamma, (1, 1, 1, 6)) np_beta = np.reshape(np_beta, (1, 1, 1, 6)) for _ in range(100): @@ -537,8 +560,9 @@ class BNTest(test.TestCase): self.assertAlmostEqual(np.std(normed_np_output), 1., places=1) # Verify that the statistics are updated during training. - moving_mean, moving_var = sess.run([bn.moving_mean, bn.moving_variance]) - np_inputs = sess.run(inputs) + moving_mean, moving_var = self.evaluate( + [bn.moving_mean, bn.moving_variance]) + np_inputs = self.evaluate(inputs) mean = np.mean(np_inputs, axis=(0, 1, 2)) std = np.std(np_inputs, axis=(0, 1, 2)) variance = np.square(std) @@ -565,8 +589,8 @@ class BNTest(test.TestCase): with self.cached_session() as sess: # Test training with placeholder learning phase. - sess.run(variables.global_variables_initializer()) - np_gamma, np_beta = sess.run([bn.gamma, bn.beta]) + self.evaluate(variables.global_variables_initializer()) + np_gamma, np_beta = self.evaluate([bn.gamma, bn.beta]) np_gamma = np.reshape(np_gamma, (1, 4, 1, 1)) np_beta = np.reshape(np_beta, (1, 4, 1, 1)) for _ in range(100): @@ -578,8 +602,9 @@ class BNTest(test.TestCase): self.assertAlmostEqual(np.std(normed_np_output), 1., places=1) # Verify that the statistics are updated during training. - moving_mean, moving_var = sess.run([bn.moving_mean, bn.moving_variance]) - np_inputs = sess.run(inputs) + moving_mean, moving_var = self.evaluate( + [bn.moving_mean, bn.moving_variance]) + np_inputs = self.evaluate(inputs) mean = np.mean(np_inputs, axis=(0, 2, 3)) std = np.std(np_inputs, axis=(0, 2, 3)) variance = np.square(std) @@ -594,6 +619,7 @@ class BNTest(test.TestCase): self.assertAlmostEqual(np.mean(normed_np_output), 0., places=1) self.assertAlmostEqual(np.std(normed_np_output), 1., places=1) + @test_util.run_deprecated_v1 def testNegativeAxis(self): epsilon = 1e-3 bn = normalization_layers.BatchNormalization( @@ -605,8 +631,8 @@ class BNTest(test.TestCase): with self.cached_session() as sess: # Test training with placeholder learning phase. - sess.run(variables.global_variables_initializer()) - np_gamma, np_beta = sess.run([bn.gamma, bn.beta]) + self.evaluate(variables.global_variables_initializer()) + np_gamma, np_beta = self.evaluate([bn.gamma, bn.beta]) np_gamma = np.reshape(np_gamma, (1, 1, 1, 6)) np_beta = np.reshape(np_beta, (1, 1, 1, 6)) for _ in range(100): @@ -619,8 +645,9 @@ class BNTest(test.TestCase): self.assertAlmostEqual(np.std(normed_np_output), 1., places=1) # Verify that the statistics are updated during training. - moving_mean, moving_var = sess.run([bn.moving_mean, bn.moving_variance]) - np_inputs = sess.run(inputs) + moving_mean, moving_var = self.evaluate( + [bn.moving_mean, bn.moving_variance]) + np_inputs = self.evaluate(inputs) mean = np.mean(np_inputs, axis=(0, 1, 2)) std = np.std(np_inputs, axis=(0, 1, 2)) variance = np.square(std) @@ -635,6 +662,7 @@ class BNTest(test.TestCase): self.assertAlmostEqual(np.mean(normed_np_output), 0., places=1) self.assertAlmostEqual(np.std(normed_np_output), 1., places=1) + @test_util.run_deprecated_v1 def testBooleanLearningPhase(self): epsilon = 1e-3 bn = normalization_layers.BatchNormalization( @@ -646,8 +674,8 @@ class BNTest(test.TestCase): with self.cached_session() as sess: # Test training with placeholder learning phase. - sess.run(variables.global_variables_initializer()) - np_gamma, np_beta = sess.run([bn.gamma, bn.beta]) + self.evaluate(variables.global_variables_initializer()) + np_gamma, np_beta = self.evaluate([bn.gamma, bn.beta]) np_gamma = np.reshape(np_gamma, (1, 1, 1, 6)) np_beta = np.reshape(np_beta, (1, 1, 1, 6)) for _ in range(100): @@ -658,8 +686,9 @@ class BNTest(test.TestCase): self.assertAlmostEqual(np.std(normed_np_output), 1., places=1) # Verify that the statistics are updated during training. - moving_mean, moving_var = sess.run([bn.moving_mean, bn.moving_variance]) - np_inputs = sess.run(inputs) + moving_mean, moving_var = self.evaluate( + [bn.moving_mean, bn.moving_variance]) + np_inputs = self.evaluate(inputs) mean = np.mean(np_inputs, axis=(0, 1, 2)) std = np.std(np_inputs, axis=(0, 1, 2)) variance = np.square(std) @@ -667,13 +696,14 @@ class BNTest(test.TestCase): self.assertAllClose(variance, moving_var, atol=1e-2) # Test inference with placeholder learning phase. - np_output = sess.run(outputs_infer) + np_output = self.evaluate(outputs_infer) # Verify that the axis is normalized during inference. normed_np_output = ((np_output - epsilon) * np_gamma) + np_beta self.assertAlmostEqual(np.mean(normed_np_output), 0., places=1) self.assertAlmostEqual(np.std(normed_np_output), 1., places=1) + @test_util.run_deprecated_v1 def testFunctionalNoReuse(self): inputs = variables.Variable( np.random.random((5, 4, 3, 6)), dtype=dtypes.float32) @@ -696,8 +726,8 @@ class BNTest(test.TestCase): with self.cached_session() as sess: # Test training with placeholder learning phase. - sess.run(variables.global_variables_initializer()) - np_gamma, np_beta = sess.run([gamma, beta]) + self.evaluate(variables.global_variables_initializer()) + np_gamma, np_beta = self.evaluate([gamma, beta]) np_gamma = np.reshape(np_gamma, (1, 1, 1, 6)) np_beta = np.reshape(np_beta, (1, 1, 1, 6)) for _ in range(100): @@ -709,8 +739,9 @@ class BNTest(test.TestCase): self.assertAlmostEqual(np.std(normed_np_output), 1., places=1) # Verify that the statistics are updated during training. - np_moving_mean, np_moving_var = sess.run([moving_mean, moving_variance]) - np_inputs = sess.run(inputs) + np_moving_mean, np_moving_var = self.evaluate( + [moving_mean, moving_variance]) + np_inputs = self.evaluate(inputs) np_mean = np.mean(np_inputs, axis=(0, 1, 2)) np_std = np.std(np_inputs, axis=(0, 1, 2)) np_variance = np.square(np_std) @@ -725,6 +756,7 @@ class BNTest(test.TestCase): self.assertAlmostEqual(np.mean(normed_np_output), 0., places=1) self.assertAlmostEqual(np.std(normed_np_output), 1., places=1) + @test_util.run_deprecated_v1 def testFunctionalReuse(self): inputs1 = variables.Variable( np.random.random((5, 4, 3, 6)), dtype=dtypes.float32) @@ -758,14 +790,15 @@ class BNTest(test.TestCase): with self.cached_session() as sess: # Test training with placeholder learning phase. - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) for _ in range(100): np_output, _, _ = sess.run([outputs2] + updates, feed_dict={training: True}) # Verify that the statistics are updated during training. - np_moving_mean, np_moving_var = sess.run([moving_mean, moving_variance]) - np_inputs = sess.run(inputs2) + np_moving_mean, np_moving_var = self.evaluate( + [moving_mean, moving_variance]) + np_inputs = self.evaluate(inputs2) np_mean = np.mean(np_inputs, axis=(0, 1, 2)) np_std = np.std(np_inputs, axis=(0, 1, 2)) np_variance = np.square(np_std) @@ -773,7 +806,7 @@ class BNTest(test.TestCase): self.assertAllClose(np_variance, np_moving_var, atol=1e-2) # Verify that the axis is normalized during training. - np_gamma, np_beta = sess.run([gamma, beta]) + np_gamma, np_beta = self.evaluate([gamma, beta]) np_gamma = np.reshape(np_gamma, (1, 1, 1, 6)) np_beta = np.reshape(np_beta, (1, 1, 1, 6)) normed_np_output = ((np_output - epsilon) * np_gamma) + np_beta @@ -788,6 +821,7 @@ class BNTest(test.TestCase): self.assertAlmostEqual(np.mean(normed_np_output), 0., places=2) self.assertAlmostEqual(np.std(normed_np_output), 1., places=1) + @test_util.run_deprecated_v1 def testFunctionalReuseFromScope(self): inputs = variables.Variable( np.random.random((5, 4, 3, 6)), dtype=dtypes.float32) @@ -802,6 +836,7 @@ class BNTest(test.TestCase): inputs, axis=-1, momentum=0.9, epsilon=epsilon, training=training) self.assertEqual(len(variables.global_variables()), 5) + @test_util.run_deprecated_v1 def testNoCenter(self): bn = normalization_layers.BatchNormalization(axis=1, center=False) inputs = random_ops.random_uniform((5, 4, 3), seed=1) @@ -817,6 +852,7 @@ class BNTest(test.TestCase): self.assertEqual(len(bn.trainable_variables), 1) self.assertEqual(len(bn.non_trainable_variables), 2) + @test_util.run_deprecated_v1 def testNoScale(self): bn = normalization_layers.BatchNormalization(axis=1, scale=False) inputs = random_ops.random_uniform((5, 4, 3), seed=1) @@ -832,6 +868,7 @@ class BNTest(test.TestCase): self.assertEqual(len(bn.trainable_variables), 1) self.assertEqual(len(bn.non_trainable_variables), 2) + @test_util.run_deprecated_v1 def testRegularizers(self): reg = lambda x: 0.1 * math_ops.reduce_sum(x) bn = normalization_layers.BatchNormalization(axis=1, beta_regularizer=reg) @@ -857,6 +894,7 @@ class BNTest(test.TestCase): self.assertEqual(bn.gamma_constraint, g_constraint) self.assertEqual(bn.beta_constraint, b_constraint) + @test_util.run_deprecated_v1 def testRenorm(self): shape = (4, 3) xt = array_ops.placeholder(dtypes.float32, shape) @@ -885,7 +923,7 @@ class BNTest(test.TestCase): renorm_mean = renorm_stddev = 0. renorm_weight = 0. with self.session(use_gpu=True) as sess: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) for _ in range(5): x = np.random.random(shape) @@ -915,6 +953,7 @@ class BNTest(test.TestCase): self.assertAllClose(y_train, yt_val_train, atol=1e-5) self.assertAllClose(y_test, yt_val_test, atol=1e-5) + @test_util.run_deprecated_v1 def testAdjustment(self): shape = (4, 3) xt = array_ops.placeholder(dtypes.float32, shape) @@ -937,7 +976,7 @@ class BNTest(test.TestCase): moving_mean = 0. moving_variance = 1. with self.session(use_gpu=True) as sess: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) for _ in range(5): x = np.random.random(shape) yt_val_train, adj_scale_val, adj_bias_val = sess.run( @@ -959,6 +998,7 @@ class BNTest(test.TestCase): self.assertAllClose(y_train, yt_val_train, atol=1e-5) self.assertAllClose(y_test, yt_val_test, atol=1e-5) + @test_util.run_deprecated_v1 def testRenormWithAdjustment(self): shape = (4, 3) xt = array_ops.placeholder(dtypes.float32, shape) @@ -990,7 +1030,7 @@ class BNTest(test.TestCase): renorm_mean = renorm_stddev = 0. renorm_weight = 0. with self.session(use_gpu=True) as sess: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) for _ in range(5): x = np.random.random(shape) yt_val_train, adj_scale_val, adj_bias_val = sess.run( @@ -1029,6 +1069,7 @@ class BNTest(test.TestCase): normalization_layers.batch_normalization( inp, virtual_batch_size=-1) + @test_util.run_deprecated_v1 def testGhostBNVirtualBatchFull(self): shape = [6, 5, 4, 3] inp = random_ops.random_uniform(shape, seed=1) @@ -1040,7 +1081,7 @@ class BNTest(test.TestCase): out1.shape.as_list(), out2.shape.as_list()) with self.session(use_gpu=True) as sess: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) x = np.random.random(shape) y1, y2 = sess.run([out1, out2], feed_dict={inp: x}) @@ -1054,6 +1095,7 @@ class BNTest(test.TestCase): inp, virtual_batch_size=3) self.assertListEqual(out.shape.as_list(), shape) + @test_util.run_deprecated_v1 def testGhostBNUnknownBatchSize(self): np_shape = [10, 5, 4] tf_shape = [None, 5, 4] @@ -1062,13 +1104,14 @@ class BNTest(test.TestCase): inp, virtual_batch_size=2) with self.session(use_gpu=True) as sess: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) x = np.random.random(np_shape) y = sess.run(out, feed_dict={inp: x}) self.assertListEqual(list(y.shape), np_shape) + @test_util.run_deprecated_v1 def testGhostBN2Dims(self): shape = [6, 2] virtual_batch_size = 3 @@ -1093,7 +1136,7 @@ class BNTest(test.TestCase): shape[1]]) with self.session(use_gpu=True) as sess: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) for _ in range(5): x = np.random.random(shape) @@ -1122,6 +1165,7 @@ class BNTest(test.TestCase): self.assertAllClose(y_train, y_val_train, atol=1e-5) self.assertAllClose(y_test, y_val_test, atol=1e-5) + @test_util.run_deprecated_v1 def testGhostBN4DimsAxis3(self): shape = [6, 10, 10, 3] virtual_batch_size = 2 @@ -1146,7 +1190,7 @@ class BNTest(test.TestCase): shape[1:]) with self.session(use_gpu=True) as sess: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) for _ in range(5): x = np.random.random(shape) @@ -1175,6 +1219,7 @@ class BNTest(test.TestCase): self.assertAllClose(y_train, y_val_train, atol=1e-2) self.assertAllClose(y_test, y_val_test, atol=1e-2) + @test_util.run_deprecated_v1 def testGhostBN4DimsAxis1(self): shape = [6, 3, 10, 10] virtual_batch_size = 2 @@ -1200,7 +1245,7 @@ class BNTest(test.TestCase): shape[1:]) with self.session(use_gpu=True) as sess: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) for _ in range(5): x = np.random.random(shape) @@ -1245,6 +1290,7 @@ class BNTest(test.TestCase): normalization_layers.batch_normalization( inp, axis=[1, 2, 1]) # duplicate + @test_util.run_deprecated_v1 def test3DInputMultiAxis12(self): epsilon = 1e-3 bn = normalization_layers.BatchNormalization( @@ -1256,9 +1302,9 @@ class BNTest(test.TestCase): with self.cached_session() as sess: # Test training with placeholder learning phase. - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) - np_gamma, np_beta = sess.run([bn.gamma, bn.beta]) + np_gamma, np_beta = self.evaluate([bn.gamma, bn.beta]) for _ in range(100): np_output, _, _ = sess.run([outputs] + bn.updates, @@ -1269,8 +1315,9 @@ class BNTest(test.TestCase): self.assertAlmostEqual(np.std(normed_np_output), 1., places=1) # Verify that the statistics are updated during training. - moving_mean, moving_var = sess.run([bn.moving_mean, bn.moving_variance]) - np_inputs = sess.run(inputs) + moving_mean, moving_var = self.evaluate( + [bn.moving_mean, bn.moving_variance]) + np_inputs = self.evaluate(inputs) mean = np.mean(np_inputs, axis=0, keepdims=True) std = np.std(np_inputs, axis=0, keepdims=True) variance = np.square(std) @@ -1285,6 +1332,7 @@ class BNTest(test.TestCase): self.assertAlmostEqual(np.mean(normed_np_output), 0., places=1) self.assertAlmostEqual(np.std(normed_np_output), 1., places=1) + @test_util.run_deprecated_v1 def test5DInputMultiAxis123(self): epsilon = 1e-3 bn = normalization_layers.BatchNormalization( @@ -1296,9 +1344,9 @@ class BNTest(test.TestCase): with self.cached_session() as sess: # Test training with placeholder learning phase. - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) - np_gamma, np_beta = sess.run([bn.gamma, bn.beta]) + np_gamma, np_beta = self.evaluate([bn.gamma, bn.beta]) for _ in range(100): np_output, _, _ = sess.run([outputs] + bn.updates, @@ -1309,8 +1357,9 @@ class BNTest(test.TestCase): self.assertAlmostEqual(np.std(normed_np_output), 1., places=1) # Verify that the statistics are updated during training. - moving_mean, moving_var = sess.run([bn.moving_mean, bn.moving_variance]) - np_inputs = sess.run(inputs) + moving_mean, moving_var = self.evaluate( + [bn.moving_mean, bn.moving_variance]) + np_inputs = self.evaluate(inputs) mean = np.mean(np_inputs, axis=(0, 4), keepdims=True) std = np.std(np_inputs, axis=(0, 4), keepdims=True) variance = np.square(std) @@ -1325,6 +1374,7 @@ class BNTest(test.TestCase): self.assertAlmostEqual(np.mean(normed_np_output), 0., places=1) self.assertAlmostEqual(np.std(normed_np_output), 1., places=1) + @test_util.run_deprecated_v1 def testGhostBN5DimsMultiAxis14(self): shape = [6, 3, 10, 10, 4] virtual_batch_size = 3 @@ -1350,7 +1400,7 @@ class BNTest(test.TestCase): shape[1:]) with self.session(use_gpu=True) as sess: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) for _ in range(5): x = np.random.random(shape) diff --git a/tensorflow/python/layers/pooling_test.py b/tensorflow/python/layers/pooling_test.py index 7533674e5a..cf1fa1e691 100644 --- a/tensorflow/python/layers/pooling_test.py +++ b/tensorflow/python/layers/pooling_test.py @@ -18,6 +18,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +from tensorflow.python.framework import test_util from tensorflow.python.layers import pooling as pooling_layers from tensorflow.python.ops import array_ops from tensorflow.python.ops import random_ops @@ -64,6 +65,7 @@ class PoolingTest(test.TestCase): output = layer.apply(images) self.assertListEqual(output.get_shape().as_list(), [5, 3, 4, 4]) + @test_util.run_deprecated_v1 def testCreateMaxPooling2DChannelsFirst(self): height, width = 7, 9 images = random_ops.random_uniform((5, 2, height, width)) @@ -73,6 +75,7 @@ class PoolingTest(test.TestCase): output = layer.apply(images) self.assertListEqual(output.get_shape().as_list(), [5, 2, 6, 8]) + @test_util.run_deprecated_v1 def testCreateAveragePooling2DChannelsFirst(self): height, width = 5, 6 images = random_ops.random_uniform((3, 4, height, width)) @@ -83,6 +86,7 @@ class PoolingTest(test.TestCase): output = layer.apply(images) self.assertListEqual(output.get_shape().as_list(), [3, 4, 4, 5]) + @test_util.run_deprecated_v1 def testCreateAveragePooling2DChannelsFirstWithNoneBatch(self): height, width = 5, 6 images = array_ops.placeholder(dtype='float32', diff --git a/tensorflow/python/lib/io/file_io.py b/tensorflow/python/lib/io/file_io.py index f22fb253e4..4caa5750bf 100644 --- a/tensorflow/python/lib/io/file_io.py +++ b/tensorflow/python/lib/io/file_io.py @@ -241,7 +241,7 @@ class FileIO(object): self._writable_file = None -@tf_export("gfile.Exists") +@tf_export(v1=["gfile.Exists"]) def file_exists(filename): """Determines whether a path exists or not. @@ -252,18 +252,35 @@ def file_exists(filename): True if the path exists, whether its a file or a directory. False if the path does not exist and there are no filesystem errors. + Raises: + errors.OpError: Propagates any errors reported by the FileSystem API. + """ + return file_exists_v2(filename) + + +@tf_export("io.gfile.exists") +def file_exists_v2(path): + """Determines whether a path exists or not. + + Args: + path: string, a path + + Returns: + True if the path exists, whether its a file or a directory. + False if the path does not exist and there are no filesystem errors. + Raises: errors.OpError: Propagates any errors reported by the FileSystem API. """ try: with errors.raise_exception_on_not_ok_status() as status: - pywrap_tensorflow.FileExists(compat.as_bytes(filename), status) + pywrap_tensorflow.FileExists(compat.as_bytes(path), status) except errors.NotFoundError: return False return True -@tf_export("gfile.Remove") +@tf_export(v1=["gfile.Remove"]) def delete_file(filename): """Deletes the file located at 'filename'. @@ -274,8 +291,22 @@ def delete_file(filename): errors.OpError: Propagates any errors reported by the FileSystem API. E.g., NotFoundError if the file does not exist. """ + delete_file_v2(filename) + + +@tf_export("io.gfile.remove") +def delete_file_v2(path): + """Deletes the path located at 'path'. + + Args: + path: string, a path + + Raises: + errors.OpError: Propagates any errors reported by the FileSystem API. E.g., + NotFoundError if the path does not exist. + """ with errors.raise_exception_on_not_ok_status() as status: - pywrap_tensorflow.DeleteFile(compat.as_bytes(filename), status) + pywrap_tensorflow.DeleteFile(compat.as_bytes(path), status) def read_file_to_string(filename, binary_mode=False): @@ -314,7 +345,7 @@ def write_string_to_file(filename, file_content): f.write(file_content) -@tf_export("gfile.Glob") +@tf_export(v1=["gfile.Glob"]) def get_matching_files(filename): """Returns a list of files that match the given pattern(s). @@ -324,28 +355,44 @@ def get_matching_files(filename): Returns: A list of strings containing filenames that match the given pattern(s). + Raises: + errors.OpError: If there are filesystem / directory listing errors. + """ + return get_matching_files_v2(filename) + + +@tf_export("io.gfile.glob") +def get_matching_files_v2(pattern): + """Returns a list of files that match the given pattern(s). + + Args: + pattern: string or iterable of strings. The glob pattern(s). + + Returns: + A list of strings containing filenames that match the given pattern(s). + Raises: errors.OpError: If there are filesystem / directory listing errors. """ with errors.raise_exception_on_not_ok_status() as status: - if isinstance(filename, six.string_types): + if isinstance(pattern, six.string_types): return [ # Convert the filenames to string from bytes. compat.as_str_any(matching_filename) for matching_filename in pywrap_tensorflow.GetMatchingFiles( - compat.as_bytes(filename), status) + compat.as_bytes(pattern), status) ] else: return [ # Convert the filenames to string from bytes. compat.as_str_any(matching_filename) - for single_filename in filename + for single_filename in pattern for matching_filename in pywrap_tensorflow.GetMatchingFiles( compat.as_bytes(single_filename), status) ] -@tf_export("gfile.MkDir") +@tf_export(v1=["gfile.MkDir"]) def create_dir(dirname): """Creates a directory with the name 'dirname'. @@ -356,14 +403,31 @@ def create_dir(dirname): The parent directories need to exist. Use recursive_create_dir instead if there is the possibility that the parent dirs don't exist. + Raises: + errors.OpError: If the operation fails. + """ + create_dir_v2(dirname) + + +@tf_export("io.gfile.mkdir") +def create_dir_v2(path): + """Creates a directory with the name given by 'path'. + + Args: + path: string, name of the directory to be created + + Notes: + The parent directories need to exist. Use recursive_create_dir instead if + there is the possibility that the parent dirs don't exist. + Raises: errors.OpError: If the operation fails. """ with errors.raise_exception_on_not_ok_status() as status: - pywrap_tensorflow.CreateDir(compat.as_bytes(dirname), status) + pywrap_tensorflow.CreateDir(compat.as_bytes(path), status) -@tf_export("gfile.MakeDirs") +@tf_export(v1=["gfile.MakeDirs"]) def recursive_create_dir(dirname): """Creates a directory and all parent/intermediate directories. @@ -372,14 +436,29 @@ def recursive_create_dir(dirname): Args: dirname: string, name of the directory to be created + Raises: + errors.OpError: If the operation fails. + """ + recursive_create_dir_v2(dirname) + + +@tf_export("io.gfile.makedirs") +def recursive_create_dir_v2(path): + """Creates a directory and all parent/intermediate directories. + + It succeeds if path already exists and is writable. + + Args: + path: string, name of the directory to be created + Raises: errors.OpError: If the operation fails. """ with errors.raise_exception_on_not_ok_status() as status: - pywrap_tensorflow.RecursivelyCreateDir(compat.as_bytes(dirname), status) + pywrap_tensorflow.RecursivelyCreateDir(compat.as_bytes(path), status) -@tf_export("gfile.Copy") +@tf_export(v1=["gfile.Copy"]) def copy(oldpath, newpath, overwrite=False): """Copies data from oldpath to newpath. @@ -389,15 +468,31 @@ def copy(oldpath, newpath, overwrite=False): overwrite: boolean, if false its an error for newpath to be occupied by an existing file. + Raises: + errors.OpError: If the operation fails. + """ + copy_v2(oldpath, newpath, overwrite) + + +@tf_export("io.gfile.copy") +def copy_v2(src, dst, overwrite=False): + """Copies data from src to dst. + + Args: + src: string, name of the file whose contents need to be copied + dst: string, name of the file to which to copy to + overwrite: boolean, if false its an error for newpath to be occupied by an + existing file. + Raises: errors.OpError: If the operation fails. """ with errors.raise_exception_on_not_ok_status() as status: pywrap_tensorflow.CopyFile( - compat.as_bytes(oldpath), compat.as_bytes(newpath), overwrite, status) + compat.as_bytes(src), compat.as_bytes(dst), overwrite, status) -@tf_export("gfile.Rename") +@tf_export(v1=["gfile.Rename"]) def rename(oldname, newname, overwrite=False): """Rename or move a file / directory. @@ -407,12 +502,28 @@ def rename(oldname, newname, overwrite=False): overwrite: boolean, if false it's an error for `newname` to be occupied by an existing file. + Raises: + errors.OpError: If the operation fails. + """ + rename_v2(oldname, newname, overwrite) + + +@tf_export("io.gfile.rename") +def rename_v2(src, dst, overwrite=False): + """Rename or move a file / directory. + + Args: + src: string, pathname for a file + dst: string, pathname to which the file needs to be moved + overwrite: boolean, if false it's an error for `dst` to be occupied by + an existing file. + Raises: errors.OpError: If the operation fails. """ with errors.raise_exception_on_not_ok_status() as status: pywrap_tensorflow.RenameFile( - compat.as_bytes(oldname), compat.as_bytes(newname), overwrite, status) + compat.as_bytes(src), compat.as_bytes(dst), overwrite, status) def atomic_write_string_to_file(filename, contents, overwrite=True): @@ -439,35 +550,61 @@ def atomic_write_string_to_file(filename, contents, overwrite=True): raise -@tf_export("gfile.DeleteRecursively") +@tf_export(v1=["gfile.DeleteRecursively"]) def delete_recursively(dirname): """Deletes everything under dirname recursively. Args: dirname: string, a path to a directory + Raises: + errors.OpError: If the operation fails. + """ + delete_recursively_v2(dirname) + + +@tf_export("io.gfile.rmtree") +def delete_recursively_v2(path): + """Deletes everything under path recursively. + + Args: + path: string, a path + Raises: errors.OpError: If the operation fails. """ with errors.raise_exception_on_not_ok_status() as status: - pywrap_tensorflow.DeleteRecursively(compat.as_bytes(dirname), status) + pywrap_tensorflow.DeleteRecursively(compat.as_bytes(path), status) -@tf_export("gfile.IsDirectory") +@tf_export(v1=["gfile.IsDirectory"]) def is_directory(dirname): """Returns whether the path is a directory or not. Args: dirname: string, path to a potential directory + Returns: + True, if the path is a directory; False otherwise + """ + return is_directory_v2(dirname) + + +@tf_export("io.gfile.isdir") +def is_directory_v2(path): + """Returns whether the path is a directory or not. + + Args: + path: string, path to a potential directory + Returns: True, if the path is a directory; False otherwise """ status = c_api_util.ScopedTFStatus() - return pywrap_tensorflow.IsDirectory(compat.as_bytes(dirname), status) + return pywrap_tensorflow.IsDirectory(compat.as_bytes(path), status) -@tf_export("gfile.ListDirectory") +@tf_export(v1=["gfile.ListDirectory"]) def list_directory(dirname): """Returns a list of entries contained within a directory. @@ -483,7 +620,26 @@ def list_directory(dirname): Raises: errors.NotFoundError if directory doesn't exist """ - if not is_directory(dirname): + return list_directory_v2(dirname) + + +@tf_export("io.gfile.listdir") +def list_directory_v2(path): + """Returns a list of entries contained within a directory. + + The list is in arbitrary order. It does not contain the special entries "." + and "..". + + Args: + path: string, path to a directory + + Returns: + [filename1, filename2, ... filenameN] as strings + + Raises: + errors.NotFoundError if directory doesn't exist + """ + if not is_directory(path): raise errors.NotFoundError(None, None, "Could not find directory") with errors.raise_exception_on_not_ok_status() as status: # Convert each element to string, since the return values of the @@ -491,11 +647,11 @@ def list_directory(dirname): return [ compat.as_str_any(filename) for filename in pywrap_tensorflow.GetChildren( - compat.as_bytes(dirname), status) + compat.as_bytes(path), status) ] -@tf_export("gfile.Walk") +@tf_export(v1=["gfile.Walk"]) def walk(top, in_order=True): """Recursive directory tree generator for directories. @@ -505,6 +661,27 @@ def walk(top, in_order=True): Errors that happen while listing directories are ignored. + Yields: + Each yield is a 3-tuple: the pathname of a directory, followed by lists of + all its subdirectories and leaf files. + (dirname, [subdirname, subdirname, ...], [filename, filename, ...]) + as strings + """ + return walk_v2(top, in_order) + + +@tf_export("io.gfile.walk") +def walk_v2(top, topdown, onerror=None): + """Recursive directory tree generator for directories. + + Args: + top: string, a Directory name + topdown: bool, Traverse pre order if True, post order if False. + onerror: optional handler for errors. Should be a function, it will be + called with the error as argument. Rethrowing the error aborts the walk. + + Errors that happen while listing directories are ignored. + Yields: Each yield is a 3-tuple: the pathname of a directory, followed by lists of all its subdirectories and leaf files. @@ -514,8 +691,11 @@ def walk(top, in_order=True): top = compat.as_str_any(top) try: listing = list_directory(top) - except errors.NotFoundError: - return + except errors.NotFoundError as err: + if onerror: + onerror(err) + else: + return files = [] subdirs = [] @@ -528,18 +708,18 @@ def walk(top, in_order=True): here = (top, subdirs, files) - if in_order: + if topdown: yield here for subdir in subdirs: - for subitem in walk(os.path.join(top, subdir), in_order): + for subitem in walk_v2(os.path.join(top, subdir), topdown, onerror=onerror): yield subitem - if not in_order: + if not topdown: yield here -@tf_export("gfile.Stat") +@tf_export(v1=["gfile.Stat"]) def stat(filename): """Returns file statistics for a given path. @@ -549,12 +729,28 @@ def stat(filename): Returns: FileStatistics struct that contains information about the path + Raises: + errors.OpError: If the operation fails. + """ + return stat_v2(filename) + + +@tf_export("io.gfile.stat") +def stat_v2(path): + """Returns file statistics for a given path. + + Args: + path: string, path to a file + + Returns: + FileStatistics struct that contains information about the path + Raises: errors.OpError: If the operation fails. """ file_statistics = pywrap_tensorflow.FileStatistics() with errors.raise_exception_on_not_ok_status() as status: - pywrap_tensorflow.Stat(compat.as_bytes(filename), file_statistics, status) + pywrap_tensorflow.Stat(compat.as_bytes(path), file_statistics, status) return file_statistics diff --git a/tensorflow/python/lib/io/tf_record.py b/tensorflow/python/lib/io/tf_record.py index b7fae85295..43086ab18d 100644 --- a/tensorflow/python/lib/io/tf_record.py +++ b/tensorflow/python/lib/io/tf_record.py @@ -150,10 +150,11 @@ class TFRecordOptions(object): return options -@tf_export( - "io.tf_record_iterator", - v1=["io.tf_record_iterator", "python_io.tf_record_iterator"]) -@deprecation.deprecated_endpoints("python_io.tf_record_iterator") +@tf_export(v1=["io.tf_record_iterator", "python_io.tf_record_iterator"]) +@deprecation.deprecated( + date=None, + instructions=("Use eager execution and: \n" + "`tf.data.TFRecordDataset(path)`")) def tf_record_iterator(path, options=None): """An iterator that read the records from a TFRecords file. diff --git a/tensorflow/python/ops/array_grad.py b/tensorflow/python/ops/array_grad.py index 68c392bf28..45e741ef22 100644 --- a/tensorflow/python/ops/array_grad.py +++ b/tensorflow/python/ops/array_grad.py @@ -489,10 +489,12 @@ def _GatherNdGrad(op, grad): @ops.RegisterGradient("CheckNumerics") -def _CheckNumericsGrad(_, grad): +def _CheckNumericsGrad(op, grad): """Gradient for check_numerics op.""" return array_ops.check_numerics( - grad, "Not a number (NaN) or infinity (Inf) values detected in gradient.") + grad, + "Not a number (NaN) or infinity (Inf) values detected in gradient. %s" % + op.get_attr("message")) @ops.RegisterGradient("PlaceholderWithDefault") @@ -800,6 +802,32 @@ def _ScatterNdGrad(op, grad): return [None, updates_grad, None] +@ops.RegisterGradient("TensorScatterUpdate") +def _TensorScatterUpdateGrad(op, grad): + indices = op.inputs[1] + updates_grad = array_ops.gather_nd(grad, indices) + tensor_grad = array_ops.tensor_scatter_update( + array_ops.identity(grad), indices, + array_ops.zeros_like(op.inputs[2], dtype=grad.dtype)) + return [tensor_grad, None, updates_grad] + + +@ops.RegisterGradient("TensorScatterAdd") +def _TensorScatterAddGrad(op, grad): + indices = op.inputs[1] + updates_grad = array_ops.gather_nd(grad, indices) + tensor_grad = array_ops.identity(grad) + return [tensor_grad, None, updates_grad] + + +@ops.RegisterGradient("TensorScatterSub") +def _TensorScatterSubGrad(op, grad): + indices = op.inputs[1] + updates_grad = array_ops.gather_nd(grad, indices) + tensor_grad = array_ops.identity(grad) + return [tensor_grad, None, -updates_grad] + + @ops.RegisterGradient("ScatterNdNonAliasingAdd") def _ScatterNdNonAliasingAddGrad(op, grad): indices = op.inputs[1] diff --git a/tensorflow/python/ops/array_ops.py b/tensorflow/python/ops/array_ops.py index bbf7d166bf..7b6242b1cd 100644 --- a/tensorflow/python/ops/array_ops.py +++ b/tensorflow/python/ops/array_ops.py @@ -22,6 +22,7 @@ from __future__ import print_function import sys import numpy as np +import six from tensorflow.python.eager import context from tensorflow.python.framework import common_shapes @@ -40,6 +41,7 @@ from tensorflow.python.ops import gen_math_ops from tensorflow.python.ops.gen_array_ops import * from tensorflow.python.ops.gen_array_ops import reverse_v2 as reverse # pylint: disable=unused-import from tensorflow.python.util import deprecation +from tensorflow.python.util import dispatch from tensorflow.python.util import nest from tensorflow.python.util.tf_export import tf_export # pylint: enable=wildcard-import @@ -79,7 +81,7 @@ def identity(input, name=None): # pylint: disable=redefined-builtin # pylint: disable=redefined-builtin,protected-access -@tf_export("expand_dims") +@tf_export(v1=["expand_dims"]) @deprecation.deprecated_args(None, "Use the `axis` argument instead", "dim") def expand_dims(input, axis=None, name=None, dim=None): """Inserts a dimension of 1 into a tensor's shape. @@ -133,6 +135,55 @@ def expand_dims(input, axis=None, name=None, dim=None): axis = deprecation.deprecated_argument_lookup("axis", axis, "dim", dim) if axis is None: raise ValueError("Must specify an axis argument to tf.expand_dims()") + return expand_dims_v2(input, axis, name) + + +@tf_export("expand_dims", v1=[]) +def expand_dims_v2(input, axis, name=None): + """Inserts a dimension of 1 into a tensor's shape. + + Given a tensor `input`, this operation inserts a dimension of 1 at the + dimension index `axis` of `input`'s shape. The dimension index `axis` starts + at zero; if you specify a negative number for `axis` it is counted backward + from the end. + + This operation is useful if you want to add a batch dimension to a single + element. For example, if you have a single image of shape `[height, width, + channels]`, you can make it a batch of 1 image with `expand_dims(image, 0)`, + which will make the shape `[1, height, width, channels]`. + + Other examples: + + ```python + # 't' is a tensor of shape [2] + tf.shape(tf.expand_dims(t, 0)) # [1, 2] + tf.shape(tf.expand_dims(t, 1)) # [2, 1] + tf.shape(tf.expand_dims(t, -1)) # [2, 1] + + # 't2' is a tensor of shape [2, 3, 5] + tf.shape(tf.expand_dims(t2, 0)) # [1, 2, 3, 5] + tf.shape(tf.expand_dims(t2, 2)) # [2, 3, 1, 5] + tf.shape(tf.expand_dims(t2, 3)) # [2, 3, 5, 1] + ``` + + This operation requires that: + + `-1-input.dims() <= dim <= input.dims()` + + This operation is related to `squeeze()`, which removes dimensions of + size 1. + + Args: + input: A `Tensor`. + axis: 0-D (scalar). Specifies the dimension index at which to + expand the shape of `input`. Must be in the range + `[-rank(input) - 1, rank(input)]`. + name: The name of the output `Tensor` (optional). + + Returns: + A `Tensor` with the same data as `input`, but its shape has an additional + dimension of size 1 added. + """ return gen_array_ops.expand_dims(input, axis, name) @@ -219,7 +270,13 @@ def broadcast_static_shape(shape_x, shape_y): return common_shapes.broadcast_shape(shape_x, shape_y) -@tf_export("shape") +@tf_export("shape", v1=[]) +def shape_v2(input, out_type=dtypes.int32, name=None): + # pylint: disable=redefined-builtin + return shape(input, name, out_type) + + +@tf_export(v1=["shape"]) def shape(input, name=None, out_type=dtypes.int32): # pylint: disable=redefined-builtin """Returns the shape of a tensor. @@ -292,7 +349,13 @@ def shape_n(input, out_type=dtypes.int32, name=None): return gen_array_ops.shape_n(input, out_type=out_type, name=name) -@tf_export("size") +@tf_export("size", v1=[]) +def size_v2(input, out_type=dtypes.int32, name=None): + # pylint: disable=redefined-builtin + return size(input, name, out_type) + + +@tf_export(v1=["size"]) def size(input, name=None, out_type=dtypes.int32): # pylint: disable=redefined-builtin """Returns the size of a tensor. @@ -341,7 +404,7 @@ def size_internal(input, name=None, optimize=True, out_type=dtypes.int32): input, (sparse_tensor.SparseTensor, sparse_tensor.SparseTensorValue)): input = ops.convert_to_tensor(input) np_out_type = out_type.as_numpy_dtype - num_elements = np.prod(input._shape_tuple(), dtype=np_out_type) # pylint: disable=protected-acces: + num_elements = np.prod(input._shape_tuple(), dtype=np_out_type) # pylint: disable=protected-access return ops.convert_to_tensor(num_elements, dtype=out_type) with ops.name_scope(name, "Size", [input]) as name: if isinstance(input, (sparse_tensor.SparseTensor, @@ -431,7 +494,7 @@ _SUPPORTED_SLICE_DTYPES = ( def _check_index(idx): """Check if a given value is a valid index into a tensor.""" - if isinstance(idx, (int, tensor_shape.Dimension)): + if isinstance(idx, (six.integer_types, tensor_shape.Dimension)): return # Optimistic check. Assumptions: @@ -1445,7 +1508,75 @@ def split(value, num_or_size_splits, axis=0, num=None, name="split"): value=value, size_splits=size_splits, axis=axis, num_split=num, name=name) -@tf_export("transpose") +@tf_export("transpose", v1=[]) +def transpose_v2(a, perm=None, conjugate=False, name="transpose"): + """Transposes `a`. Permutes the dimensions according to `perm`. + + The returned tensor's dimension i will correspond to the input dimension + `perm[i]`. If `perm` is not given, it is set to (n-1...0), where n is + the rank of the input tensor. Hence by default, this operation performs a + regular matrix transpose on 2-D input Tensors. If conjugate is True and + `a.dtype` is either `complex64` or `complex128` then the values of `a` + are conjugated and transposed. + + @compatibility(numpy) + In `numpy` transposes are memory-efficient constant time operations as they + simply return a new view of the same data with adjusted `strides`. + + TensorFlow does not support strides, so `transpose` returns a new tensor with + the items permuted. + @end_compatibility + + For example: + + ```python + x = tf.constant([[1, 2, 3], [4, 5, 6]]) + tf.transpose(x) # [[1, 4] + # [2, 5] + # [3, 6]] + + # Equivalently + tf.transpose(x, perm=[1, 0]) # [[1, 4] + # [2, 5] + # [3, 6]] + + # If x is complex, setting conjugate=True gives the conjugate transpose + x = tf.constant([[1 + 1j, 2 + 2j, 3 + 3j], + [4 + 4j, 5 + 5j, 6 + 6j]]) + tf.transpose(x, conjugate=True) # [[1 - 1j, 4 - 4j], + # [2 - 2j, 5 - 5j], + # [3 - 3j, 6 - 6j]] + + # 'perm' is more useful for n-dimensional tensors, for n > 2 + x = tf.constant([[[ 1, 2, 3], + [ 4, 5, 6]], + [[ 7, 8, 9], + [10, 11, 12]]]) + + # Take the transpose of the matrices in dimension-0 + # (this common operation has a shorthand `linalg.transpose`) + tf.transpose(x, perm=[0, 2, 1]) # [[[1, 4], + # [2, 5], + # [3, 6]], + # [[7, 10], + # [8, 11], + # [9, 12]]] + ``` + + Args: + a: A `Tensor`. + perm: A permutation of the dimensions of `a`. + conjugate: Optional bool. Setting it to `True` is mathematically equivalent + to tf.conj(tf.transpose(input)). + name: A name for the operation (optional). + + Returns: + A transposed `Tensor`. + """ + return transpose(a=a, perm=perm, name=name, conjugate=conjugate) + + +@tf_export(v1=["transpose"]) def transpose(a, perm=None, name="transpose", conjugate=False): """Transposes `a`. Permutes the dimensions according to `perm`. @@ -1678,7 +1809,7 @@ def zeros(shape, dtype=dtypes.float32, name=None): return output -@tf_export("zeros_like") +@tf_export(v1=["zeros_like"]) def zeros_like(tensor, dtype=None, name=None, optimize=True): """Creates a tensor with all elements set to zero. @@ -1705,6 +1836,42 @@ def zeros_like(tensor, dtype=None, name=None, optimize=True): Returns: A `Tensor` with all elements set to zero. """ + return zeros_like_impl(tensor, dtype, name, optimize) + + +@tf_export("zeros_like", v1=[]) +def zeros_like_v2( + input, # pylint: disable=redefined-builtin + dtype=None, + name=None): + """Creates a tensor with all elements set to zero. + + Given a single tensor (`tensor`), this operation returns a tensor of the + same type and shape as `tensor` with all elements set to zero. Optionally, + you can use `dtype` to specify a new type for the returned tensor. + + For example: + + ```python + tensor = tf.constant([[1, 2, 3], [4, 5, 6]]) + tf.zeros_like(tensor) # [[0, 0, 0], [0, 0, 0]] + ``` + + Args: + input: A `Tensor`. + dtype: A type for the returned `Tensor`. Must be `float16`, `float32`, + `float64`, `int8`, `uint8`, `int16`, `uint16`, `int32`, `int64`, + `complex64`, `complex128`, `bool` or `string`. + name: A name for the operation (optional). + + Returns: + A `Tensor` with all elements set to zero. + """ + return zeros_like_impl(input, dtype, name, optimize=True) + + +def zeros_like_impl(tensor, dtype, name, optimize=True): + """Internal implementation for the v1/v2 zeros_like API calls.""" with ops.name_scope(name, "zeros_like", [tensor]) as name: tensor = ops.convert_to_tensor(tensor, name="tensor") @@ -1731,7 +1898,7 @@ def zeros_like(tensor, dtype=None, name=None, optimize=True): return gen_array_ops.zeros_like(tensor, name=name) -@tf_export("ones_like") +@tf_export(v1=["ones_like"]) def ones_like(tensor, dtype=None, name=None, optimize=True): """Creates a tensor with all elements set to 1. @@ -1758,6 +1925,42 @@ def ones_like(tensor, dtype=None, name=None, optimize=True): Returns: A `Tensor` with all elements set to 1. """ + return ones_like_impl(tensor, dtype, name, optimize) + + +@tf_export("ones_like", v1=[]) +def ones_like_v2( + input, # pylint: disable=redefined-builtin + dtype=None, + name=None): + """Creates a tensor with all elements set to zero. + + Given a single tensor (`tensor`), this operation returns a tensor of the + same type and shape as `tensor` with all elements set to zero. Optionally, + you can use `dtype` to specify a new type for the returned tensor. + + For example: + + ```python + tensor = tf.constant([[1, 2, 3], [4, 5, 6]]) + tf.ones_like(tensor) # [[1, 1, 1], [1, 1, 1]] + ``` + + Args: + input: A `Tensor`. + dtype: A type for the returned `Tensor`. Must be `float16`, `float32`, + `float64`, `int8`, `uint8`, `int16`, `uint16`, `int32`, `int64`, + `complex64`, `complex128`, `bool` or `string`. + name: A name for the operation (optional). + + Returns: + A `Tensor` with all elements set to zero. + """ + return ones_like_impl(input, dtype, name, optimize=True) + + +def ones_like_impl(tensor, dtype, name, optimize=True): + """Internal implementation for the v1/v2 ones_like API calls.""" with ops.name_scope(name, "ones_like", [tensor]) as name: tensor = ops.convert_to_tensor(tensor, name="tensor") ones_shape = shape_internal(tensor, optimize=optimize) @@ -1955,7 +2158,65 @@ def sparse_placeholder(dtype, shape=None, name=None): # pylint: enable=redefined-outer-name -@tf_export("pad") +@tf_export("pad", v1=[]) +def pad_v2(tensor, paddings, mode="CONSTANT", constant_values=0, name=None): + """Pads a tensor. + + This operation pads a `tensor` according to the `paddings` you specify. + `paddings` is an integer tensor with shape `[n, 2]`, where n is the rank of + `tensor`. For each dimension D of `input`, `paddings[D, 0]` indicates how + many values to add before the contents of `tensor` in that dimension, and + `paddings[D, 1]` indicates how many values to add after the contents of + `tensor` in that dimension. If `mode` is "REFLECT" then both `paddings[D, 0]` + and `paddings[D, 1]` must be no greater than `tensor.dim_size(D) - 1`. If + `mode` is "SYMMETRIC" then both `paddings[D, 0]` and `paddings[D, 1]` must be + no greater than `tensor.dim_size(D)`. + + The padded size of each dimension D of the output is: + + `paddings[D, 0] + tensor.dim_size(D) + paddings[D, 1]` + + For example: + + ```python + t = tf.constant([[1, 2, 3], [4, 5, 6]]) + paddings = tf.constant([[1, 1,], [2, 2]]) + # 'constant_values' is 0. + # rank of 't' is 2. + tf.pad(t, paddings, "CONSTANT") # [[0, 0, 0, 0, 0, 0, 0], + # [0, 0, 1, 2, 3, 0, 0], + # [0, 0, 4, 5, 6, 0, 0], + # [0, 0, 0, 0, 0, 0, 0]] + + tf.pad(t, paddings, "REFLECT") # [[6, 5, 4, 5, 6, 5, 4], + # [3, 2, 1, 2, 3, 2, 1], + # [6, 5, 4, 5, 6, 5, 4], + # [3, 2, 1, 2, 3, 2, 1]] + + tf.pad(t, paddings, "SYMMETRIC") # [[2, 1, 1, 2, 3, 3, 2], + # [2, 1, 1, 2, 3, 3, 2], + # [5, 4, 4, 5, 6, 6, 5], + # [5, 4, 4, 5, 6, 6, 5]] + ``` + + Args: + tensor: A `Tensor`. + paddings: A `Tensor` of type `int32`. + mode: One of "CONSTANT", "REFLECT", or "SYMMETRIC" (case-insensitive) + constant_values: In "CONSTANT" mode, the scalar pad value to use. Must be + same type as `tensor`. + name: A name for the operation (optional). + + Returns: + A `Tensor`. Has the same type as `tensor`. + + Raises: + ValueError: When mode is not one of "CONSTANT", "REFLECT", or "SYMMETRIC". + """ + return pad(tensor, paddings, mode, name, constant_values) + + +@tf_export(v1=["pad"]) def pad(tensor, paddings, mode="CONSTANT", name=None, constant_values=0): # pylint: disable=invalid-name """Pads a tensor. @@ -2415,7 +2676,7 @@ def depth_to_space(input, block_size, name=None, data_format="NHWC"): # pylint: depth_to_space.__doc__ = gen_array_ops.depth_to_space.__doc__ -@tf_export("batch_to_space") +@tf_export(v1=["batch_to_space"]) def batch_to_space(input, crops, block_size, name=None): # pylint: disable=redefined-builtin result = batch_to_space_nd( input, @@ -2429,6 +2690,151 @@ def batch_to_space(input, crops, block_size, name=None): # pylint: disable=rede batch_to_space.__doc__ = gen_array_ops.batch_to_space.__doc__ +@tf_export("batch_to_space", v1=[]) +def batch_to_space_v2(input, block_shape, crops, name=None): # pylint: disable=redefined-builtin + """BatchToSpace for N-D tensors of type T. + + This operation reshapes the "batch" dimension 0 into `M + 1` dimensions of + shape `block_shape + [batch]`, interleaves these blocks back into the grid + defined by the spatial dimensions `[1, ..., M]`, to obtain a result with the + same rank as the input. The spatial dimensions of this intermediate result + are then optionally cropped according to `crops` to produce the output. This + is the reverse of SpaceToBatch. See below for a precise description. + + Args: + input: A `Tensor`. + N-D with shape `input_shape = [batch] + spatial_shape + remaining_shape`, + where spatial_shape has M dimensions. + block_shape: A `Tensor`. Must be one of the following types: + `int32`, `int64`. 1-D with shape `[M]`, all values must be >= 1. + For backwards compatibility with TF 1.0, this parameter may be an int, in + which case it is converted to + `numpy.array([block_shape, block_shape], dtype=numpy.int64)`. + crops: A `Tensor`. Must be one of the following types: `int32`, `int64`. + 2-D with shape `[M, 2]`, all values must be >= 0. + `crops[i] = [crop_start, crop_end]` specifies the amount to crop from + input dimension `i + 1`, which corresponds to spatial dimension `i`. It + is required that + `crop_start[i] + crop_end[i] <= block_shape[i] * input_shape[i + 1]`. + + This operation is equivalent to the following steps: + + 1. Reshape `input` to `reshaped` of shape: + [block_shape[0], ..., block_shape[M-1], + batch / prod(block_shape), + input_shape[1], ..., input_shape[N-1]] + + 2. Permute dimensions of `reshaped` to produce `permuted` of shape + [batch / prod(block_shape), + + input_shape[1], block_shape[0], + ..., + input_shape[M], block_shape[M-1], + + input_shape[M+1], ..., input_shape[N-1]] + + 3. Reshape `permuted` to produce `reshaped_permuted` of shape + [batch / prod(block_shape), + + input_shape[1] * block_shape[0], + ..., + input_shape[M] * block_shape[M-1], + + input_shape[M+1], + ..., + input_shape[N-1]] + + 4. Crop the start and end of dimensions `[1, ..., M]` of + `reshaped_permuted` according to `crops` to produce the + output of shape: + [batch / prod(block_shape), + + input_shape[1] * block_shape[0] - crops[0,0] - crops[0,1], + ..., + input_shape[M] * block_shape[M-1] - crops[M-1,0] - crops[M-1,1], + + input_shape[M+1], ..., input_shape[N-1]] + + Some examples: + + (1) For the following input of shape `[4, 1, 1, 1]`, + `block_shape = [2, 2]`, and `crops = [[0, 0], [0, 0]]`: + + ``` + [[[[1]]], [[[2]]], [[[3]]], [[[4]]]] + ``` + + The output tensor has shape `[1, 2, 2, 1]` and value: + + ``` + x = [[[[1], [2]], [[3], [4]]]] + ``` + + (2) For the following input of shape `[4, 1, 1, 3]`, + `block_shape = [2, 2]`, and `crops = [[0, 0], [0, 0]]`: + + ``` + [[[1, 2, 3]], [[4, 5, 6]], [[7, 8, 9]], [[10, 11, 12]]] + ``` + + The output tensor has shape `[1, 2, 2, 3]` and value: + + ``` + x = [[[[1, 2, 3], [4, 5, 6]], + [[7, 8, 9], [10, 11, 12]]]] + ``` + + (3) For the following input of shape `[4, 2, 2, 1]`, + `block_shape = [2, 2]`, and `crops = [[0, 0], [0, 0]]`: + + ``` + x = [[[[1], [3]], [[9], [11]]], + [[[2], [4]], [[10], [12]]], + [[[5], [7]], [[13], [15]]], + [[[6], [8]], [[14], [16]]]] + ``` + + The output tensor has shape `[1, 4, 4, 1]` and value: + + ``` + x = [[[1], [2], [3], [4]], + [[5], [6], [7], [8]], + [[9], [10], [11], [12]], + [[13], [14], [15], [16]]] + ``` + + (4) For the following input of shape `[8, 1, 3, 1]`, + `block_shape = [2, 2]`, and `crops = [[0, 0], [2, 0]]`: + + ``` + x = [[[[0], [1], [3]]], [[[0], [9], [11]]], + [[[0], [2], [4]]], [[[0], [10], [12]]], + [[[0], [5], [7]]], [[[0], [13], [15]]], + [[[0], [6], [8]]], [[[0], [14], [16]]]] + ``` + + The output tensor has shape `[2, 2, 4, 1]` and value: + + ``` + x = [[[[1], [2], [3], [4]], + [[5], [6], [7], [8]]], + [[[9], [10], [11], [12]], + [[13], [14], [15], [16]]]] + ``` + name: A name for the operation (optional). + + Returns: + A `Tensor`. Has the same type as `input`. + """ + if isinstance(block_shape, int): + block_shape = np.array([block_shape, block_shape], dtype=np.int64) + + return batch_to_space_nd(input=input, + block_shape=block_shape, + crops=crops, + name=name) + + @tf_export("one_hot") def one_hot(indices, depth, @@ -2652,7 +3058,7 @@ def sequence_mask(lengths, maxlen=None, dtype=dtypes.bool, name=None): return gen_math_ops.cast(result, dtype) -@tf_export("squeeze") +@tf_export(v1=["squeeze"]) @deprecation.deprecated_args(None, "Use the `axis` argument instead", "squeeze_dims") def squeeze(input, axis=None, name=None, squeeze_dims=None): @@ -2702,6 +3108,12 @@ def squeeze(input, axis=None, name=None, squeeze_dims=None): return gen_array_ops.squeeze(input, axis, name) +@tf_export("squeeze", v1=[]) +def squeeze_v2(input, axis=None, name=None): + # pylint: disable=redefined-builtin + return squeeze(input, axis, name) + + @tf_export("where") def where(condition, x=None, y=None, name=None): """Return the elements, either from `x` or `y`, depending on the `condition`. @@ -2756,7 +3168,7 @@ def where(condition, x=None, y=None, name=None): # pylint: disable=redefined-builtin -@tf_export("reverse_sequence") +@tf_export(v1=["reverse_sequence"]) @deprecation.deprecated_args( None, "seq_dim is deprecated, use seq_axis instead", "seq_dim") @deprecation.deprecated_args( @@ -2780,15 +3192,32 @@ def reverse_sequence(input, name=name) -# pylint: enable=redefined-builtin - reverse_sequence.__doc__ = deprecation.rewrite_argument_docstring( deprecation.rewrite_argument_docstring( gen_array_ops.reverse_sequence.__doc__, "batch_dim", "batch_axis"), "seq_dim", "seq_axis") -@tf_export("gather") +@tf_export("reverse_sequence", v1=[]) +def reverse_sequence_v2( + input, seq_lengths, seq_axis=None, batch_axis=None, name=None): + return gen_array_ops.reverse_sequence( + input=input, + seq_lengths=seq_lengths, + seq_dim=seq_axis, + batch_dim=batch_axis, + name=name) + + +reverse_sequence_v2.__doc__ = deprecation.rewrite_argument_docstring( + deprecation.rewrite_argument_docstring( + gen_array_ops.reverse_sequence.__doc__, "batch_dim", "batch_axis"), + "seq_dim", "seq_axis") + +# pylint: enable=redefined-builtin + + +@tf_export(v1=["gather"]) def gather(params, indices, validate_indices=None, name=None, axis=0): del validate_indices if axis != 0: @@ -2804,10 +3233,18 @@ def gather(params, indices, validate_indices=None, name=None, axis=0): return gen_array_ops.gather_v2(params, indices, axis, name=name) -gather.__doc__ = gen_array_ops.gather_v2.__doc__ +@tf_export("gather", v1=[]) +def gather_v2(params, indices, validate_indices=None, axis=0, name=None): + return gather(params, indices, validate_indices=validate_indices, name=name, + axis=axis) + + +gather.__doc__ = gather_v2.__doc__ = gen_array_ops.gather_v2.__doc__ + @tf_export("batch_gather") +@dispatch.add_dispatch_support def batch_gather(params, indices, name=None): """Gather slices from `params` according to `indices` with leading batch dims. @@ -2885,7 +3322,7 @@ def batch_gather(params, indices, name=None): # Define quantize_v2 here in order to make name the second-to-last attribute, # because round_mode was added later. -@tf_export("quantize_v2") +@tf_export(v1=["quantize_v2"]) @deprecation.deprecated( "2017-10-25", "`tf.quantize_v2` is deprecated, please use `tf.quantization.quantize` " @@ -2906,7 +3343,7 @@ def quantize_v2(input, # pylint: disable=redefined-builtin round_mode=round_mode) -quantize_v2.__doc__ = """Please use `tf.quantize` instead.""" +quantize_v2.__doc__ = """Please use `tf.quantization.quantize` instead.""" # We want to expose tf.quantize instead of tf.quantize_v2; we can deprecate @@ -2992,3 +3429,48 @@ def searchsorted(sorted_sequence, quantize.__doc__ = gen_array_ops.quantize_v2.__doc__ + + +@tf_export("image.extract_image_patches", v1=[]) +def extract_image_patches_v2( + images, + sizes, + strides, + rates, + padding, + name=None): + # pylint: disable=line-too-long + r"""Extract `patches` from `images` and put them in the \"depth\" output dimension. + + Args: + images: A 4-D Tensor with shape `[batch, in_rows, in_cols, depth] + sizes: The size of the sliding window for each dimension of `images`. + strides: A 1-D Tensor of length 4. How far the centers of two consecutive + patches are in the images. Must be: `[1, stride_rows, stride_cols, 1]`. + rates: A 1-D Tensor of length 4. Must be: `[1, rate_rows, rate_cols, 1]`. + This is the input stride, specifying how far two consecutive patch samples + are in the input. Equivalent to extracting patches with `patch_sizes_eff = + patch_sizes + (patch_sizes - 1) * (rates - 1)`, followed by subsampling + them spatially by a factor of `rates`. This is equivalent to `rate` in + dilated (a.k.a. Atrous) convolutions. + padding: The type of padding algorithm to use. + We specify the size-related attributes as: ```python ksizes = [1, + ksize_rows, ksize_cols, 1] strides = [1, strides_rows, strides_cols, 1] + rates = [1, rates_rows, rates_cols, 1] + name: A name for the operation (optional). + + Returns: + A 4-D Tensor. Has the same type as `images`, and with shape `[batch, + out_rows, out_cols, ksize_rows * ksize_cols * depth]` containing image + patches with size `ksize_rows x ksize_cols x depth` vectorized in the + \"depth\" dimension. Note `out_rows` and `out_cols` are the dimensions of + the output patches. + """ + # pylint: enable=line-too-long + return gen_array_ops.extract_image_patches( + images, sizes, strides, rates, padding, name) + +extract_image_patches_deprecation = deprecation.deprecated_args( + None, "ksizes is deprecated, use sizes instead", "ksizes") +tf_export(v1=["image.extract_image_patches", "extract_image_patches"])( + extract_image_patches_deprecation(gen_array_ops.extract_image_patches)) diff --git a/tensorflow/python/ops/bitwise_ops_test.py b/tensorflow/python/ops/bitwise_ops_test.py index dfb40db2d5..d154b6759b 100644 --- a/tensorflow/python/ops/bitwise_ops_test.py +++ b/tensorflow/python/ops/bitwise_ops_test.py @@ -34,6 +34,7 @@ class BitwiseOpTest(test_util.TensorFlowTestCase): def __init__(self, method_name="runTest"): super(BitwiseOpTest, self).__init__(method_name) + @test_util.run_deprecated_v1 def testBinaryOps(self): dtype_list = [dtypes.int8, dtypes.int16, dtypes.int32, dtypes.int64, dtypes.uint8, dtypes.uint16, dtypes.uint32, dtypes.uint64] @@ -59,16 +60,18 @@ class BitwiseOpTest(test_util.TensorFlowTestCase): 2**31 - 1, 2**31, 2**32 - 1, 2**32, -2**32 + 1, -2**32, -2**63 + 1, 2**63 - 1] def count_bits(x): - return sum([bin(z).count("1") for z in six.iterbytes(x.tobytes())]) + return sum(bin(z).count("1") for z in six.iterbytes(x.tobytes())) for dtype in dtype_list: with self.cached_session(use_gpu=True) as sess: print("PopulationCount test: ", dtype) inputs = np.array(raw_inputs, dtype=dtype.as_numpy_dtype) truth = [count_bits(x) for x in inputs] input_tensor = constant_op.constant(inputs, dtype=dtype) - popcnt_result = sess.run(gen_bitwise_ops.population_count(input_tensor)) + popcnt_result = self.evaluate( + gen_bitwise_ops.population_count(input_tensor)) self.assertAllEqual(truth, popcnt_result) + @test_util.run_deprecated_v1 def testInvertOp(self): dtype_list = [dtypes.int8, dtypes.int16, dtypes.int32, dtypes.int64, dtypes.uint8, dtypes.uint16, dtypes.uint32, dtypes.uint64] @@ -89,10 +92,11 @@ class BitwiseOpTest(test_util.TensorFlowTestCase): self.assertAllEqual(not_a_or_a, [not_0] * 4) # For unsigned dtypes let's also check the result directly. if dtype.is_unsigned: - inverted = sess.run(bitwise_ops.invert(input_tensor)) + inverted = self.evaluate(bitwise_ops.invert(input_tensor)) expected = [dtype.max - x for x in inputs] self.assertAllEqual(inverted, expected) + @test_util.run_deprecated_v1 def testShiftsWithPositiveLHS(self): dtype_list = [np.int8, np.int16, np.int32, np.int64, np.uint8, np.uint16, np.uint32, np.uint64] @@ -107,6 +111,7 @@ class BitwiseOpTest(test_util.TensorFlowTestCase): self.assertAllEqual(left_shift_result, np.left_shift(lhs, rhs)) self.assertAllEqual(right_shift_result, np.right_shift(lhs, rhs)) + @test_util.run_deprecated_v1 def testShiftsWithNegativeLHS(self): dtype_list = [np.int8, np.int16, np.int32, np.int64] @@ -120,6 +125,7 @@ class BitwiseOpTest(test_util.TensorFlowTestCase): self.assertAllEqual(left_shift_result, np.left_shift(lhs, rhs)) self.assertAllEqual(right_shift_result, np.right_shift(lhs, rhs)) + @test_util.run_deprecated_v1 def testImplementationDefinedShiftsDoNotCrash(self): dtype_list = [np.int8, np.int16, np.int32, np.int64] @@ -135,6 +141,7 @@ class BitwiseOpTest(test_util.TensorFlowTestCase): bitwise_ops.right_shift(lhs, rhs)]) + @test_util.run_deprecated_v1 def testShapeInference(self): dtype_list = [dtypes.int8, dtypes.int16, dtypes.int32, dtypes.int64, dtypes.uint8, dtypes.uint16] diff --git a/tensorflow/python/ops/candidate_sampling_ops.py b/tensorflow/python/ops/candidate_sampling_ops.py index f0bfdb2b7a..c64000b65d 100644 --- a/tensorflow/python/ops/candidate_sampling_ops.py +++ b/tensorflow/python/ops/candidate_sampling_ops.py @@ -208,7 +208,9 @@ def learned_unigram_candidate_sampler(true_classes, num_true, num_sampled, seed2=seed2, name=name) -@tf_export('nn.fixed_unigram_candidate_sampler') +@tf_export('random.fixed_unigram_candidate_sampler', + 'nn.fixed_unigram_candidate_sampler', + v1=['nn.fixed_unigram_candidate_sampler']) def fixed_unigram_candidate_sampler(true_classes, num_true, num_sampled, @@ -300,7 +302,8 @@ def fixed_unigram_candidate_sampler(true_classes, unigrams=unigrams, seed=seed1, seed2=seed2, name=name) -@tf_export('nn.all_candidate_sampler') +@tf_export('random.all_candidate_sampler', 'nn.all_candidate_sampler', + v1=['nn.all_candidate_sampler']) def all_candidate_sampler(true_classes, num_true, num_sampled, unique, seed=None, name=None): """Generate the set of all classes. diff --git a/tensorflow/python/ops/check_ops.py b/tensorflow/python/ops/check_ops.py index 5589bbc848..f1f36269cf 100644 --- a/tensorflow/python/ops/check_ops.py +++ b/tensorflow/python/ops/check_ops.py @@ -119,9 +119,31 @@ def assert_proper_iterable(values): 'Expected argument "values" to be iterable. Found: %s' % type(values)) -@tf_export( - 'debugging.assert_negative', - v1=['debugging.assert_negative', 'assert_negative']) +@tf_export('debugging.assert_negative', v1=[]) +def assert_negative_v2(x, message=None, summarize=None, name=None): + """Assert the condition `x < 0` holds element-wise. + + This Op checks that `x[i] < 0` holds for every element of `x`. If `x` is + empty, this is trivially satisfied. + + If `x` is not negative everywhere, `message`, as well as the first `summarize` + entries of `x` are printed, and `InvalidArgumentError` is raised. + + Args: + x: Numeric `Tensor`. + message: A string to prefix to the default message. + summarize: Print this many entries of each tensor. + name: A name for this operation (optional). Defaults to "assert_negative". + + Raises: + InvalidArgumentError: if the check can be performed immediately and + `x[i] < 0` is False. The check can be performed immediately during eager + execution or if `x` is statically known. + """ + assert_negative(x=x, message=message, summarize=summarize, name=name) + + +@tf_export(v1=['debugging.assert_negative', 'assert_negative']) @deprecation.deprecated_endpoints('assert_negative') def assert_negative(x, data=None, summarize=None, message=None, name=None): """Assert the condition `x < 0` holds element-wise. @@ -163,9 +185,31 @@ def assert_negative(x, data=None, summarize=None, message=None, name=None): return assert_less(x, zero, data=data, summarize=summarize) -@tf_export( - 'debugging.assert_positive', - v1=['debugging.assert_positive', 'assert_positive']) +@tf_export('debugging.assert_positive', v1=[]) +def assert_positive_v2(x, message=None, summarize=None, name=None): + """Assert the condition `x > 0` holds element-wise. + + This Op checks that `x[i] > 0` holds for every element of `x`. If `x` is + empty, this is trivially satisfied. + + If `x` is not positive everywhere, `message`, as well as the first `summarize` + entries of `x` are printed, and `InvalidArgumentError` is raised. + + Args: + x: Numeric `Tensor`. + message: A string to prefix to the default message. + summarize: Print this many entries of each tensor. + name: A name for this operation (optional). Defaults to "assert_positive". + + Raises: + InvalidArgumentError: if the check can be performed immediately and + `x[i] > 0` is False. The check can be performed immediately during eager + execution or if `x` is statically known. + """ + assert_positive(x=x, summarize=summarize, message=message, name=name) + + +@tf_export(v1=['debugging.assert_positive', 'assert_positive']) @deprecation.deprecated_endpoints('assert_positive') def assert_positive(x, data=None, summarize=None, message=None, name=None): """Assert the condition `x > 0` holds element-wise. @@ -206,9 +250,32 @@ def assert_positive(x, data=None, summarize=None, message=None, name=None): return assert_less(zero, x, data=data, summarize=summarize) -@tf_export( - 'debugging.assert_non_negative', - v1=['debugging.assert_non_negative', 'assert_non_negative']) +@tf_export('debugging.assert_non_negative', v1=[]) +def assert_non_negative_v2(x, message=None, summarize=None, name=None): + """Assert the condition `x >= 0` holds element-wise. + + This Op checks that `x[i] >= 0` holds for every element of `x`. If `x` is + empty, this is trivially satisfied. + + If `x` is not >= 0 everywhere, `message`, as well as the first `summarize` + entries of `x` are printed, and `InvalidArgumentError` is raised. + + Args: + x: Numeric `Tensor`. + message: A string to prefix to the default message. + summarize: Print this many entries of each tensor. + name: A name for this operation (optional). Defaults to + "assert_non_negative". + + Raises: + InvalidArgumentError: if the check can be performed immediately and + `x[i] >= 0` is False. The check can be performed immediately during eager + execution or if `x` is statically known. + """ + assert_non_negative(x=x, summarize=summarize, message=message, name=name) + + +@tf_export(v1=['debugging.assert_non_negative', 'assert_non_negative']) @deprecation.deprecated_endpoints('assert_non_negative') def assert_non_negative(x, data=None, summarize=None, message=None, name=None): """Assert the condition `x >= 0` holds element-wise. @@ -251,9 +318,32 @@ def assert_non_negative(x, data=None, summarize=None, message=None, name=None): return assert_less_equal(zero, x, data=data, summarize=summarize) -@tf_export( - 'debugging.assert_non_positive', - v1=['debugging.assert_non_positive', 'assert_non_positive']) +@tf_export('debugging.assert_non_positive', v1=[]) +def assert_non_positive_v2(x, message=None, summarize=None, name=None): + """Assert the condition `x <= 0` holds element-wise. + + This Op checks that `x[i] <= 0` holds for every element of `x`. If `x` is + empty, this is trivially satisfied. + + If `x` is not <= 0 everywhere, `message`, as well as the first `summarize` + entries of `x` are printed, and `InvalidArgumentError` is raised. + + Args: + x: Numeric `Tensor`. + message: A string to prefix to the default message. + summarize: Print this many entries of each tensor. + name: A name for this operation (optional). Defaults to + "assert_non_positive". + + Raises: + InvalidArgumentError: if the check can be performed immediately and + `x[i] <= 0` is False. The check can be performed immediately during eager + execution or if `x` is statically known. + """ + assert_non_positive(x=x, summarize=summarize, message=message, name=name) + + +@tf_export(v1=['debugging.assert_non_positive', 'assert_non_positive']) @deprecation.deprecated_endpoints('assert_non_positive') def assert_non_positive(x, data=None, summarize=None, message=None, name=None): """Assert the condition `x <= 0` holds element-wise. @@ -296,7 +386,33 @@ def assert_non_positive(x, data=None, summarize=None, message=None, name=None): return assert_less_equal(x, zero, data=data, summarize=summarize) -@tf_export('debugging.assert_equal', 'assert_equal') +@tf_export('debugging.assert_equal', 'assert_equal', v1=[]) +def assert_equal_v2(x, y, message=None, summarize=None, name=None): + """Assert the condition `x == y` holds element-wise. + + This Op checks that `x[i] == y[i]` holds for every pair of (possibly + broadcast) elements of `x` and `y`. If both `x` and `y` are empty, this is + trivially satisfied. + + If `x` and `y` are not equal, `message`, as well as the first `summarize` + entries of `x` and `y` are printed, and `InvalidArgumentError` is raised. + + Args: + x: Numeric `Tensor`. + y: Numeric `Tensor`, same dtype as and broadcastable to `x`. + message: A string to prefix to the default message. + summarize: Print this many entries of each tensor. + name: A name for this operation (optional). Defaults to "assert_equal". + + Raises: + InvalidArgumentError: if the check can be performed immediately and + `x == y` is False. The check can be performed immediately during eager + execution or if `x` and `y` are statically known. + """ + assert_equal(x=x, y=y, summarize=summarize, message=message, name=name) + + +@tf_export(v1=['debugging.assert_equal', 'assert_equal']) def assert_equal(x, y, data=None, summarize=None, message=None, name=None): """Assert the condition `x == y` holds element-wise. @@ -396,9 +512,36 @@ def assert_equal(x, y, data=None, summarize=None, message=None, name=None): return control_flow_ops.Assert(condition, data, summarize=summarize) -@tf_export( - 'debugging.assert_none_equal', - v1=['debugging.assert_none_equal', 'assert_none_equal']) +@tf_export('debugging.assert_none_equal', v1=[]) +def assert_none_equal_v2(x, y, summarize=None, message=None, name=None): + """Assert the condition `x != y` holds for all elements. + + This Op checks that `x[i] != y[i]` holds for every pair of (possibly + broadcast) elements of `x` and `y`. If both `x` and `y` are empty, this is + trivially satisfied. + + If any elements of `x` and `y` are equal, `message`, as well as the first + `summarize` entries of `x` and `y` are printed, and `InvalidArgumentError` + is raised. + + Args: + x: Numeric `Tensor`. + y: Numeric `Tensor`, same dtype as and broadcastable to `x`. + summarize: Print this many entries of each tensor. + message: A string to prefix to the default message. + name: A name for this operation (optional). Defaults to + "assert_none_equal". + + Raises: + InvalidArgumentError: if the check can be performed immediately and + `x != y` is False for any pair of elements in `x` and `y`. The check can + be performed immediately during eager execution or if `x` and `y` are + statically known. + """ + assert_none_equal(x=x, y=y, summarize=summarize, message=message, name=name) + + +@tf_export(v1=['debugging.assert_none_equal', 'assert_none_equal']) @deprecation.deprecated_endpoints('assert_none_equal') def assert_none_equal( x, y, data=None, summarize=None, message=None, name=None): @@ -450,7 +593,52 @@ def assert_none_equal( return control_flow_ops.Assert(condition, data, summarize=summarize) -@tf_export('debugging.assert_near', v1=['debugging.assert_near', 'assert_near']) +@tf_export('debugging.assert_near', v1=[]) +def assert_near_v2(x, y, rtol=None, atol=None, message=None, summarize=None, + name=None): + """Assert the condition `x` and `y` are close element-wise. + + This Op checks that `x[i] - y[i] < atol + rtol * tf.abs(y[i])` holds for every + pair of (possibly broadcast) elements of `x` and `y`. If both `x` and `y` are + empty, this is trivially satisfied. + + If any elements of `x` and `y` are not close, `message`, as well as the first + `summarize` entries of `x` and `y` are printed, and `InvalidArgumentError` + is raised. + + The default `atol` and `rtol` is `10 * eps`, where `eps` is the smallest + representable positive number such that `1 + eps != 1`. This is about + `1.2e-6` in `32bit`, `2.22e-15` in `64bit`, and `0.00977` in `16bit`. + See `numpy.finfo`. + + Args: + x: Float or complex `Tensor`. + y: Float or complex `Tensor`, same dtype as and broadcastable to `x`. + rtol: `Tensor`. Same `dtype` as, and broadcastable to, `x`. + The relative tolerance. Default is `10 * eps`. + atol: `Tensor`. Same `dtype` as, and broadcastable to, `x`. + The absolute tolerance. Default is `10 * eps`. + message: A string to prefix to the default message. + summarize: Print this many entries of each tensor. + name: A name for this operation (optional). Defaults to "assert_near". + + Raises: + InvalidArgumentError: if the check can be performed immediately and + `x != y` is False for any pair of elements in `x` and `y`. The check can + be performed immediately during eager execution or if `x` and `y` are + statically known. + + @compatibility(numpy) + Similar to `numpy.assert_allclose`, except tolerance depends on data type. + This is due to the fact that `TensorFlow` is often used with `32bit`, `64bit`, + and even `16bit` data. + @end_compatibility + """ + assert_near(x=x, y=y, rtol=rtol, atol=atol, summarize=summarize, + message=message, name=name) + + +@tf_export(v1=['debugging.assert_near', 'assert_near']) @deprecation.deprecated_endpoints('assert_near') def assert_near( x, y, rtol=None, atol=None, data=None, summarize=None, message=None, @@ -529,7 +717,34 @@ def assert_near( return control_flow_ops.Assert(condition, data, summarize=summarize) -@tf_export('debugging.assert_less', 'assert_less') +@tf_export('debugging.assert_less', 'assert_less', v1=[]) +def assert_less_v2(x, y, message=None, summarize=None, name=None): + """Assert the condition `x < y` holds element-wise. + + This Op checks that `x[i] < y[i]` holds for every pair of (possibly + broadcast) elements of `x` and `y`. If both `x` and `y` are empty, this is + trivially satisfied. + + If `x` is not less than `y` element-wise, `message`, as well as the first + `summarize` entries of `x` and `y` are printed, and `InvalidArgumentError` is + raised. + + Args: + x: Numeric `Tensor`. + y: Numeric `Tensor`, same dtype as and broadcastable to `x`. + message: A string to prefix to the default message. + summarize: Print this many entries of each tensor. + name: A name for this operation (optional). Defaults to "assert_less". + + Raises: + InvalidArgumentError: if the check can be performed immediately and + `x < y` is False. The check can be performed immediately during eager + execution or if `x` and `y` are statically known. + """ + assert_less(x=x, y=y, summarize=summarize, message=message, name=name) + + +@tf_export(v1=['debugging.assert_less', 'assert_less']) def assert_less(x, y, data=None, summarize=None, message=None, name=None): """Assert the condition `x < y` holds element-wise. @@ -577,9 +792,34 @@ def assert_less(x, y, data=None, summarize=None, message=None, name=None): return control_flow_ops.Assert(condition, data, summarize=summarize) -@tf_export( - 'debugging.assert_less_equal', - v1=['debugging.assert_less_equal', 'assert_less_equal']) +@tf_export('debugging.assert_less_equal', v1=[]) +def assert_less_equal_v2(x, y, message=None, summarize=None, name=None): + """Assert the condition `x <= y` holds element-wise. + + This Op checks that `x[i] <= y[i]` holds for every pair of (possibly + broadcast) elements of `x` and `y`. If both `x` and `y` are empty, this is + trivially satisfied. + + If `x` is not less or equal than `y` element-wise, `message`, as well as the + first `summarize` entries of `x` and `y` are printed, and + `InvalidArgumentError` is raised. + + Args: + x: Numeric `Tensor`. + y: Numeric `Tensor`, same dtype as and broadcastable to `x`. + message: A string to prefix to the default message. + summarize: Print this many entries of each tensor. + name: A name for this operation (optional). Defaults to "assert_less_equal". + + Raises: + InvalidArgumentError: if the check can be performed immediately and + `x <= y` is False. The check can be performed immediately during eager + execution or if `x` and `y` are statically known. + """ + assert_less_equal(x=x, y=y, summarize=summarize, message=message, name=name) + + +@tf_export(v1=['debugging.assert_less_equal', 'assert_less_equal']) @deprecation.deprecated_endpoints('assert_less_equal') def assert_less_equal(x, y, data=None, summarize=None, message=None, name=None): """Assert the condition `x <= y` holds element-wise. @@ -628,7 +868,34 @@ def assert_less_equal(x, y, data=None, summarize=None, message=None, name=None): return control_flow_ops.Assert(condition, data, summarize=summarize) -@tf_export('debugging.assert_greater', 'assert_greater') +@tf_export('debugging.assert_greater', 'assert_greater', v1=[]) +def assert_greater_v2(x, y, message=None, summarize=None, name=None): + """Assert the condition `x > y` holds element-wise. + + This Op checks that `x[i] > y[i]` holds for every pair of (possibly + broadcast) elements of `x` and `y`. If both `x` and `y` are empty, this is + trivially satisfied. + + If `x` is not greater than `y` element-wise, `message`, as well as the first + `summarize` entries of `x` and `y` are printed, and `InvalidArgumentError` is + raised. + + Args: + x: Numeric `Tensor`. + y: Numeric `Tensor`, same dtype as and broadcastable to `x`. + message: A string to prefix to the default message. + summarize: Print this many entries of each tensor. + name: A name for this operation (optional). Defaults to "assert_greater". + + Raises: + InvalidArgumentError: if the check can be performed immediately and + `x > y` is False. The check can be performed immediately during eager + execution or if `x` and `y` are statically known. + """ + assert_greater(x=x, y=y, summarize=summarize, message=message, name=name) + + +@tf_export(v1=['debugging.assert_greater', 'assert_greater']) def assert_greater(x, y, data=None, summarize=None, message=None, name=None): """Assert the condition `x > y` holds element-wise. @@ -676,9 +943,36 @@ def assert_greater(x, y, data=None, summarize=None, message=None, name=None): return control_flow_ops.Assert(condition, data, summarize=summarize) -@tf_export( - 'debugging.assert_greater_equal', - v1=['debugging.assert_greater_equal', 'assert_greater_equal']) +@tf_export('debugging.assert_greater_equal', v1=[]) +def assert_greater_equal_v2(x, y, message=None, summarize=None, name=None): + """Assert the condition `x >= y` holds element-wise. + + This Op checks that `x[i] >= y[i]` holds for every pair of (possibly + broadcast) elements of `x` and `y`. If both `x` and `y` are empty, this is + trivially satisfied. + + If `x` is not greater or equal to `y` element-wise, `message`, as well as the + first `summarize` entries of `x` and `y` are printed, and + `InvalidArgumentError` is raised. + + Args: + x: Numeric `Tensor`. + y: Numeric `Tensor`, same dtype as and broadcastable to `x`. + message: A string to prefix to the default message. + summarize: Print this many entries of each tensor. + name: A name for this operation (optional). Defaults to + "assert_greater_equal". + + Raises: + InvalidArgumentError: if the check can be performed immediately and + `x >= y` is False. The check can be performed immediately during eager + execution or if `x` and `y` are statically known. + """ + assert_greater_equal(x=x, y=y, summarize=summarize, message=message, + name=name) + + +@tf_export(v1=['debugging.assert_greater_equal', 'assert_greater_equal']) @deprecation.deprecated_endpoints('assert_greater_equal') def assert_greater_equal(x, y, data=None, summarize=None, message=None, name=None): @@ -777,7 +1071,31 @@ def _assert_rank_condition( return control_flow_ops.Assert(condition, data, summarize=summarize) -@tf_export('debugging.assert_rank', 'assert_rank') +@tf_export('debugging.assert_rank', 'assert_rank', v1=[]) +def assert_rank_v2(x, rank, message=None, name=None): + """Assert that `x` has rank equal to `rank`. + + This Op checks that the rank of `x` is equal to `rank`. + + If `x` has a different rank, `message`, as well as the shape of `x` are + printed, and `InvalidArgumentError` is raised. + + Args: + x: `Tensor`. + rank: Scalar integer `Tensor`. + message: A string to prefix to the default message. + name: A name for this operation (optional). Defaults to + "assert_rank". + + Raises: + InvalidArgumentError: if the check can be performed immediately and + `x` does not have rank `rank`. The check can be performed immediately + during eager execution or if the shape of `x` is statically known. + """ + assert_rank(x=x, rank=rank, message=message, name=name) + + +@tf_export(v1=['debugging.assert_rank', 'assert_rank']) def assert_rank(x, rank, data=None, summarize=None, message=None, name=None): """Assert `x` has rank equal to `rank`. @@ -792,7 +1110,7 @@ def assert_rank(x, rank, data=None, summarize=None, message=None, name=None): x: Numeric `Tensor`. rank: Scalar integer `Tensor`. data: The tensors to print out if the condition is False. Defaults to - error message and first few entries of `x`. + error message and the shape of `x`. summarize: Print this many entries of each tensor. message: A string to prefix to the default message. name: A name for this operation (optional). Defaults to "assert_rank". @@ -839,9 +1157,31 @@ def assert_rank(x, rank, data=None, summarize=None, message=None, name=None): return assert_op -@tf_export( - 'debugging.assert_rank_at_least', - v1=['debugging.assert_rank_at_least', 'assert_rank_at_least']) +@tf_export('debugging.assert_rank_at_least', v1=[]) +def assert_rank_at_least_v2(x, rank, message=None, name=None): + """Assert that `x` has rank of at least `rank`. + + This Op checks that the rank of `x` is greater or equal to `rank`. + + If `x` has a rank lower than `rank`, `message`, as well as the shape of `x` + are printed, and `InvalidArgumentError` is raised. + + Args: + x: `Tensor`. + rank: Scalar integer `Tensor`. + message: A string to prefix to the default message. + name: A name for this operation (optional). Defaults to + "assert_rank_at_least". + + Raises: + InvalidArgumentError: `x` does not have rank at least `rank`, but the rank + cannot be statically determined. + ValueError: If static checks determine `x` has mismatched rank. + """ + assert_rank_at_least(x=x, rank=rank, message=message, name=name) + + +@tf_export(v1=['debugging.assert_rank_at_least', 'assert_rank_at_least']) @deprecation.deprecated_endpoints('assert_rank_at_least') def assert_rank_at_least( x, rank, data=None, summarize=None, message=None, name=None): @@ -973,9 +1313,30 @@ def _assert_ranks_condition( return control_flow_ops.Assert(condition, data, summarize=summarize) -@tf_export( - 'debugging.assert_rank_in', - v1=['debugging.assert_rank_in', 'assert_rank_in']) +@tf_export('debugging.assert_rank_in', v1=[]) +def assert_rank_in_v2(x, ranks, message=None, name=None): + """Assert that `x` has a rank in `ranks`. + + This Op checks that the rank of `x` is in `ranks`. + + If `x` has a different rank, `message`, as well as the shape of `x` are + printed, and `InvalidArgumentError` is raised. + + Args: + x: `Tensor`. + ranks: `Iterable` of scalar `Tensor` objects. + message: A string to prefix to the default message. + name: A name for this operation (optional). Defaults to "assert_rank_in". + + Raises: + InvalidArgumentError: `x` does not have rank in `ranks`, but the rank cannot + be statically determined. + ValueError: If static checks determine `x` has mismatched rank. + """ + assert_rank_in(x=x, ranks=ranks, message=message, name=name) + + +@tf_export(v1=['debugging.assert_rank_in', 'assert_rank_in']) @deprecation.deprecated_endpoints('assert_rank_in') def assert_rank_in( x, ranks, data=None, summarize=None, message=None, name=None): @@ -1038,9 +1399,25 @@ def assert_rank_in( return assert_op -@tf_export( - 'debugging.assert_integer', - v1=['debugging.assert_integer', 'assert_integer']) +@tf_export('debugging.assert_integer', v1=[]) +def assert_integer_v2(x, message=None, name=None): + """Assert that `x` is of integer dtype. + + If `x` has a non-integer type, `message`, as well as the dtype of `x` are + printed, and `InvalidArgumentError` is raised. + + Args: + x: A `Tensor`. + message: A string to prefix to the default message. + name: A name for this operation (optional). Defaults to "assert_integer". + + Raises: + TypeError: If `x.dtype` is not a non-quantized integer type. + """ + assert_integer(x=x, message=message, name=name) + + +@tf_export(v1=['debugging.assert_integer', 'assert_integer']) @deprecation.deprecated_endpoints('assert_integer') def assert_integer(x, message=None, name=None): """Assert that `x` is of integer dtype. @@ -1079,13 +1456,30 @@ def assert_integer(x, message=None, name=None): return control_flow_ops.no_op('statically_determined_was_integer') -@tf_export('debugging.assert_type', v1=['debugging.assert_type', 'assert_type']) +@tf_export('debugging.assert_type', v1=[]) +def assert_type_v2(tensor, tf_type, message=None, name=None): + """Asserts that the given `Tensor` is of the specified type. + + Args: + tensor: A `Tensor`. + tf_type: A tensorflow type (`dtypes.float32`, `tf.int64`, `dtypes.bool`, + etc). + message: A string to prefix to the default message. + name: A name for this operation. Defaults to "assert_type" + + Raises: + TypeError: If the tensor's data type doesn't match `tf_type`. + """ + assert_type(tensor=tensor, tf_type=tf_type, message=message, name=name) + + +@tf_export(v1=['debugging.assert_type', 'assert_type']) @deprecation.deprecated_endpoints('assert_type') def assert_type(tensor, tf_type, message=None, name=None): """Statically asserts that the given `Tensor` is of the specified type. Args: - tensor: A tensorflow `Tensor`. + tensor: A `Tensor`. tf_type: A tensorflow type (`dtypes.float32`, `tf.int64`, `dtypes.bool`, etc). message: A string to prefix to the default message. @@ -1136,9 +1530,13 @@ def is_numeric_tensor(tensor): @tf_export( - 'debugging.is_non_decreasing', - v1=['debugging.is_non_decreasing', 'is_non_decreasing']) -@deprecation.deprecated_endpoints('is_non_decreasing') + 'math.is_non_decreasing', + v1=[ + 'math.is_non_decreasing', 'debugging.is_non_decreasing', + 'is_non_decreasing' + ]) +@deprecation.deprecated_endpoints('debugging.is_non_decreasing', + 'is_non_decreasing') def is_non_decreasing(x, name=None): """Returns `True` if `x` is non-decreasing. @@ -1166,9 +1564,13 @@ def is_non_decreasing(x, name=None): @tf_export( - 'debugging.is_strictly_increasing', - v1=['debugging.is_strictly_increasing', 'is_strictly_increasing']) -@deprecation.deprecated_endpoints('is_strictly_increasing') + 'math.is_strictly_increasing', + v1=[ + 'math.is_strictly_increasing', 'debugging.is_strictly_increasing', + 'is_strictly_increasing' + ]) +@deprecation.deprecated_endpoints('debugging.is_strictly_increasing', + 'is_strictly_increasing') def is_strictly_increasing(x, name=None): """Returns `True` if `x` is strictly increasing. @@ -1260,8 +1662,10 @@ def assert_same_float_dtype(tensors=None, dtype=None): tensors: Tensors of input values. Can include `None` elements, which will be ignored. dtype: Expected type. + Returns: Validated type. + Raises: ValueError: if neither `tensors` nor `dtype` is supplied, or result is not float, or the common type of the inputs is not a floating point type. @@ -1275,20 +1679,57 @@ def assert_same_float_dtype(tensors=None, dtype=None): return dtype -@tf_export( - 'debugging.assert_scalar', v1=['debugging.assert_scalar', 'assert_scalar']) +@tf_export('debugging.assert_scalar', v1=[]) +def assert_scalar_v2(tensor, message=None, name=None): + """Asserts that the given `tensor` is a scalar. + + This function raises `ValueError` unless it can be certain that the given + `tensor` is a scalar. `ValueError` is also raised if the shape of `tensor` is + unknown. + + Args: + tensor: A `Tensor`. + message: A string to prefix to the default message. + name: A name for this operation. Defaults to "assert_scalar" + + Raises: + ValueError: If the tensor is not scalar (rank 0), or if its shape is + unknown. + """ + assert_scalar(tensor=tensor, message=message, name=name) + + +@tf_export(v1=['debugging.assert_scalar', 'assert_scalar']) @deprecation.deprecated_endpoints('assert_scalar') -def assert_scalar(tensor, name=None): +def assert_scalar(tensor, name=None, message=None): + """Asserts that the given `tensor` is a scalar. + + This function raises `ValueError` unless it can be certain that the given + `tensor` is a scalar. `ValueError` is also raised if the shape of `tensor` is + unknown. + + Args: + tensor: A `Tensor`. + name: A name for this operation. Defaults to "assert_scalar" + message: A string to prefix to the default message. + + Returns: + The input tensor (potentially converted to a `Tensor`). + + Raises: + ValueError: If the tensor is not scalar (rank 0), or if its shape is + unknown. + """ with ops.name_scope(name, 'assert_scalar', [tensor]) as name_scope: tensor = ops.convert_to_tensor(tensor, name=name_scope) shape = tensor.get_shape() if shape.ndims != 0: if context.executing_eagerly(): - raise ValueError('Expected scalar shape, saw shape: %s.' - % (shape,)) + raise ValueError('%sExpected scalar shape, saw shape: %s.' + % (message or '', shape,)) else: - raise ValueError('Expected scalar shape for %s, saw shape: %s.' - % (tensor.name, shape)) + raise ValueError('%sExpected scalar shape for %s, saw shape: %s.' + % (message or '', tensor.name, shape)) return tensor diff --git a/tensorflow/python/ops/clip_ops.py b/tensorflow/python/ops/clip_ops.py index 5cd626b92d..82803ac351 100644 --- a/tensorflow/python/ops/clip_ops.py +++ b/tensorflow/python/ops/clip_ops.py @@ -300,7 +300,12 @@ def clip_by_global_norm(t_list, clip_norm, use_norm=None, name=None): return list_clipped, use_norm -@tf_export("clip_by_average_norm") +@deprecation.deprecated( + date=None, + instructions= + "clip_by_average_norm is deprecated in TensorFlow 2.0. Please use " + "clip_by_norm(t, clip_norm * tf.to_float(tf.size(t), name)) instead.") +@tf_export(v1=["clip_by_average_norm"]) def clip_by_average_norm(t, clip_norm, name=None): """Clips tensor values to a maximum average L2-norm. diff --git a/tensorflow/python/ops/clip_ops_test.py b/tensorflow/python/ops/clip_ops_test.py index 8aa9c4ffb3..a59a0c22d4 100644 --- a/tensorflow/python/ops/clip_ops_test.py +++ b/tensorflow/python/ops/clip_ops_test.py @@ -20,6 +20,7 @@ from __future__ import print_function from tensorflow.python.framework import constant_op from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import clip_ops from tensorflow.python.ops import numerics from tensorflow.python.platform import test @@ -35,7 +36,7 @@ class ClipOpsTest(test.TestCase): input_op = constant_op.constant(inputs) clipped = clip_ops.clip_by_norm(input_op, max_norm) check_op = numerics.add_check_numerics_ops() - result, _ = sess.run([clipped, check_op]) + result, _ = self.evaluate([clipped, check_op]) self.assertAllClose(result, expected) def _testClipIndexedSlicesByNorm(self, values, indices, shape, max_norm, @@ -54,9 +55,10 @@ class ClipOpsTest(test.TestCase): # Tensor mode dense_tensor = ops.convert_to_tensor(indixed_slices) dense_clipped = clip_ops.clip_by_norm(dense_tensor, max_norm, axes) - result, expected = sess.run([clipped, dense_clipped]) + result, expected = self.evaluate([clipped, dense_clipped]) self.assertAllClose(result, expected) + @test_util.run_deprecated_v1 def testClipTensorByNorm(self): # Simple example self._testClipTensorByNorm([[-3.0, 0.0, 0.0], [4.0, 0.0, 0.0]], 4.0, diff --git a/tensorflow/python/ops/collective_ops_test.py b/tensorflow/python/ops/collective_ops_test.py index 9c772a9354..0fd9368d21 100644 --- a/tensorflow/python/ops/collective_ops_test.py +++ b/tensorflow/python/ops/collective_ops_test.py @@ -21,6 +21,7 @@ from __future__ import print_function from tensorflow.core.protobuf import config_pb2 from tensorflow.python.framework import constant_op from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import collective_ops from tensorflow.python.platform import test @@ -49,16 +50,19 @@ class CollectiveOpTest(test.TestCase): self.assertAllClose(results[0], expected, rtol=1e-5, atol=1e-5) self.assertAllClose(results[1], expected, rtol=1e-5, atol=1e-5) + @test_util.run_deprecated_v1 def testCollectiveReduce(self): self._testCollectiveReduce([0.1, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1], [0.3, 1.3, 2.3, 3.3, 4.3, 5.3, 6.3, 7.3], [0.2, 1.2, 2.2, 3.2, 4.2, 5.2, 6.2, 7.2], True) + @test_util.run_deprecated_v1 def testCollectiveAutoGraphKey(self): self._testCollectiveReduce([0.1, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1], [0.3, 1.3, 2.3, 3.3, 4.3, 5.3, 6.3, 7.3], [0.2, 1.2, 2.2, 3.2, 4.2, 5.2, 6.2, 7.2], False) + @test_util.run_deprecated_v1 def testCollectiveReduceScalar(self): self._testCollectiveReduce(0.1, 0.3, 0.2, True) @@ -81,6 +85,7 @@ class CollectiveOpTest(test.TestCase): self.assertAllClose(results[0], t0, rtol=1e-5, atol=1e-5) self.assertAllClose(results[1], t0, rtol=1e-5, atol=1e-5) + @test_util.run_deprecated_v1 def testCollectiveBroadcast(self): self._testCollectiveBroadcast([0.1, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1]) diff --git a/tensorflow/python/ops/cond_v2.py b/tensorflow/python/ops/cond_v2.py index 998c3e08f6..e882a270c8 100644 --- a/tensorflow/python/ops/cond_v2.py +++ b/tensorflow/python/ops/cond_v2.py @@ -25,15 +25,19 @@ from __future__ import print_function import collections -from tensorflow.core.framework import attr_value_pb2 +from tensorflow.python.framework import dtypes from tensorflow.python.framework import func_graph as func_graph_module from tensorflow.python.framework import function_def_to_graph from tensorflow.python.framework import ops from tensorflow.python.ops import array_ops from tensorflow.python.ops import control_flow_util from tensorflow.python.ops import control_flow_util_v2 as util +from tensorflow.python.ops import gen_dataset_ops from tensorflow.python.ops import gen_functional_ops +from tensorflow.python.ops import gen_resource_variable_ops from tensorflow.python.ops import gradients_impl +from tensorflow.python.util import nest + # NOTE(skyewm): TensorFlow uses protected class methods and fields to signify # that they aren't part of the official public API. These protected members @@ -74,73 +78,17 @@ def cond_v2(pred, true_fn, false_fn, name="cond"): false_name, read_only_collections=False), add_control_dependencies=add_control_dependencies, op_return_value=pred) - _check_same_outputs(true_graph, false_graph) - - # Add inputs to true_graph and false_graph to make them match. Note that - # this modifies true_graph and false_graph. - cond_inputs = _make_inputs_match(true_graph, false_graph, - true_graph.external_captures, - false_graph.external_captures) - - # Add all intermediate tensors as function outputs so they're available for - # the gradient computation. - true_intermediates = _get_intermediates(true_graph) - false_intermediates = _get_intermediates(false_graph) - - # Save the original number of outputs to return to the caller. - num_cond_outputs = len(true_graph.outputs) - - # Make the number/type of new intermediate outputs match. - extra_true_outputs, extra_false_outputs = _pad_params( - true_graph, false_graph, true_intermediates, false_intermediates) + outputs = _build_cond(pred, true_graph, false_graph, + true_graph.external_captures, + false_graph.external_captures, + name=scope) - true_graph.outputs.extend(extra_true_outputs) - false_graph.outputs.extend(extra_false_outputs) - - # Create the If op. - tensors = gen_functional_ops._if( # pylint: disable=protected-access - pred, - cond_inputs, [t.dtype for t in true_graph.outputs], - util.create_new_tf_function(true_graph), - util.create_new_tf_function(false_graph), - output_shapes=_get_output_shapes(true_graph.outputs, - false_graph.outputs), - name=scope) - - # Set the flag to enable lowering on the `if` op if necessary - # Lowering allows cond_v2 to avoid some of the limitations of Functions, - # allowing users to specify devices & colocation inside of cond_v2 branches, - # and enabling non-strict evaluation & partial pruning of cond_v2 branches. - # This brings cond_v2 closer to feature parity with tf.cond. - # - # However, we do not lower `If` in the XLA context because it is easier for - # XLA to apply its own optimizations when dealing with un-lowered `If` - # operators than with lowered switch/merge control flow. - # - # TODO(b/110167197) this approach requires cond_v2 to have at least 1 output - if_op = tensors[0].op - if not control_flow_util.IsInXLAContext(if_op): - # pylint: disable=protected-access - if_op._set_attr("_lower_using_switch_merge", - attr_value_pb2.AttrValue(b=True)) - # pylint: enable=protected-access - - # Return identities for each output of the If op, rather than the output of - # the If op directly. This makes pruning work if the output of cond() is - # fetched: the lowering pass converts the If outputs into IdentityN outputs, - # which if fetched will cause all ops in the taken branch to be run (since - # it takes all merge ops as input). After lowering, each output identity op - # will end up with only the appropriate merge op as input. - # TODO(b/79984175): this doesn't have to be a tuple once we covert to the - # correct output structure - tensors = tuple(array_ops.identity(t) for t in tensors) - - result = tuple(tensors[:num_cond_outputs]) - if len(result) == 1: - return result[0] - else: - return result + # Packing output tensors in the same nested structure as the true and false + # functions return + result = nest.pack_sequence_as(structure=true_graph.structured_outputs, + flat_sequence=tensors[:num_cond_outputs]) + return result @ops.RegisterGradient("If") @@ -168,39 +116,105 @@ def _IfGrad(op, *grads): # pylint: disable=invalid-name true_grad_inputs = _resolve_grad_inputs(true_graph, true_grad_graph) false_grad_inputs = _resolve_grad_inputs(false_graph, false_grad_graph) - # Make the inputs to true_grad_graph and false_grad_graph match. Note that - # this modifies true_grad_graph and false_grad_graph. - grad_inputs = _make_inputs_match(true_grad_graph, false_grad_graph, - true_grad_inputs, false_grad_inputs) + outputs = _build_cond(op.inputs[0], true_grad_graph, false_grad_graph, + true_grad_inputs, false_grad_inputs) - # Add all intermediate tensors as function outputs so they're available for - # higher-order gradient computations. + # The predicate has no gradient. + return [None] + outputs - true_grad_intermediates = _get_intermediates(true_grad_graph) - false_grad_intermediates = _get_intermediates(false_grad_graph) - # Save the original number of gradient outputs to return. - num_grad_outputs = len(true_grad_graph.outputs) +def _build_cond(pred, true_graph, false_graph, true_inputs, false_inputs, + name=None): + """Creates an If op from the specified predicate, branch functions and inputs. - # Make the number/type of new intermediate outputs match. - extra_true_grad_outputs, extra_false_grad_outputs = _pad_params( - true_grad_graph, false_grad_graph, - true_grad_intermediates, false_grad_intermediates) + Note that this modifies true_graph and false_graph to make the inputs match, + and to output all intermediates values so they're available for the gradient + computation. - true_grad_graph.outputs.extend(extra_true_grad_outputs) - false_grad_graph.outputs.extend(extra_false_grad_outputs) + true_graph and false_graph need not have the same input types, but they must + have the same outpute types. - # Create the gradient If op. - tensors = gen_functional_ops._if( - op.inputs[0], - grad_inputs, [t.dtype for t in true_grad_graph.outputs], - util.create_new_tf_function(true_grad_graph), - util.create_new_tf_function(false_grad_graph), - output_shapes=_get_output_shapes(true_grad_graph.outputs, - false_grad_graph.outputs)) + Args: + pred: boolean Tensor + true_graph: FuncGraph + false_graph: FuncGraph + true_inputs: a list of Tensors to be passed to true_graph as input. + false_inputs: a list of Tensors to be passed to false_graph as input. + name: the name for the If op. - # The predicate has no gradient. - return [None] + tensors[:num_grad_outputs] + Returns: + A list of Tensors which are the outputs of the If op. Does not include added + intermediate outputs. + """ + _check_same_outputs(true_graph, false_graph) + + # Add inputs to true_graph and false_graph to make them match. Note that + # this modifies true_graph and false_graph. + cond_inputs = _make_inputs_match(true_graph, false_graph, + true_inputs, false_inputs) + + # Add all intermediate tensors as function outputs so they're available for + # the gradient computation. Since the outputs of the two functions must match, + # we wrap all the intermediates in optionals. Each intermediate output will + # have a value iff its corresponding branch is taken. + + true_intermediates = _get_intermediates(true_graph) + false_intermediates = _get_intermediates(false_graph) + + # Save the original number of outputs to return to the caller. + num_cond_outputs = len(true_graph.outputs) + + if control_flow_util.InXlaContext(ops.get_default_graph()): + # XLA does not yet support optionals, so output intermediates directly and + # make them match via FakeParams, which can be converted to zeros in XLA. + # TODO(skyewm,jpienaar): can XLA support optionals? + extra_true_outputs, extra_false_outputs = _make_intermediates_match_xla( + true_graph, false_graph, true_intermediates, false_intermediates) + else: + # Wrap intermediates in optionals. + wrapped_true_intermediates = _wrap_intermediates(true_graph, + true_intermediates) + wrapped_false_intermediates = _wrap_intermediates(false_graph, + false_intermediates) + + # Make outputs match by adding none optionals. + extra_true_outputs, extra_false_outputs = _make_intermediates_match( + true_graph, false_graph, + wrapped_true_intermediates, wrapped_false_intermediates) + + true_graph.outputs.extend(extra_true_outputs) + false_graph.outputs.extend(extra_false_outputs) + # TODO(skyewm): somehow indicate it's a bug if this fails. + _check_same_outputs(true_graph, false_graph) + + # Create the If op. + tensors = gen_functional_ops._if( # pylint: disable=protected-access + pred, + cond_inputs, [t.dtype for t in true_graph.outputs], + util.create_new_tf_function(true_graph), + util.create_new_tf_function(false_graph), + output_shapes=_get_output_shapes(true_graph.outputs, + false_graph.outputs), + name=name) + + # TODO(b/110167197) this approach requires cond_v2 to have at least 1 output + if_op = tensors[0].op + util.maybe_set_lowering_attr(if_op) + + # Return identities for each output of the If op, rather than the output of + # the If op directly. This makes pruning work if the output of cond() is + # fetched: the lowering pass converts the If outputs into IdentityN outputs, + # which if fetched will cause all ops in the taken branch to be run (since + # it takes all merge ops as input). After lowering, each output identity op + # will end up with only the appropriate merge op as input. + # TODO(b/79984175): this doesn't have to be a tuple once we covert to the + # correct output structure + tensors = [array_ops.identity(t) for t in tensors] + + # Prevent fetching since the variant outputs can't be fetched directly. + if_op.graph.prevent_fetching(if_op) + + return tensors[:num_cond_outputs] def _get_func_graphs(if_op): @@ -277,7 +291,11 @@ def _grad_fn(func_graph, grads): # both branches have zero gradient. for i in range(len(result)): if result[i] is None: - result[i] = array_ops.zeros_like(func_graph.inputs[i]) + if func_graph.inputs[i].dtype == dtypes.resource: + result[i] = array_ops.zeros( + gen_resource_variable_ops.variable_shape(func_graph.inputs[i])) + else: + result[i] = array_ops.zeros_like(func_graph.inputs[i]) return result @@ -287,7 +305,7 @@ def _create_grad_func(func_graph, grads, name): return func_graph_module.func_graph_from_py_func( name, lambda: _grad_fn(func_graph, grads), [], {}, - func_graph=util.CondBranchFuncGraph(name, read_only_collections=False)) + func_graph=_CondGradFuncGraph(name, func_graph)) def _resolve_grad_inputs(cond_graph, grad_graph): @@ -369,28 +387,39 @@ def _separate_unique_inputs(true_inputs, false_inputs): return list(shared_inputs), list(true_only_inputs), list(false_only_inputs) -def _pad_params(true_graph, false_graph, true_params, false_params): - """Returns new param lists that have matching signatures. +def _make_intermediates_match(true_graph, false_graph, + true_optionals, false_optionals): + """Returns new optionals lists that have matching signatures. - This is done by mirroring each param list in the other using dummy params. - There is no merging of params. + This is done by mirroring each list in the other using none optionals. + There is no merging of like optionals. Args: true_graph: FuncGraph false_graph: FuncGraph - true_params: a list of Tensors from true_graph - false_params: a list of Tensors from false_graph + true_optionals: a list of optional Tensors from true_graph + false_optionals: a list of optional Tensors from false_graph Returns: A new list of Tensors in true_graph and a new list of Tensors in - false_graph. The two lists have the same number of Tensors, with matching - types and shapes across the lists. + false_graph. The two lists have the same number of Tensors, all of which + will be optionals of the same shape/type. """ - new_true_params = (true_params + - _create_dummy_params(true_graph, false_params)) - new_false_inputs = (_create_dummy_params(false_graph, true_params) - + false_params) - return new_true_params, new_false_inputs + new_true_optionals = (true_optionals + + _create_none_optionals(true_graph, false_optionals)) + new_false_optionals = (_create_none_optionals(false_graph, true_optionals) + + false_optionals) + return new_true_optionals, new_false_optionals + + +def _make_intermediates_match_xla(true_graph, false_graph, true_intermediates, + false_intermediates): + """Like _make_intermediates_match but for the XLA case.""" + new_true_intermediates = (true_intermediates + + _create_fakeparams(true_graph, false_intermediates)) + new_false_intermediates = (_create_fakeparams(false_graph, true_intermediates) + + false_intermediates) + return new_true_intermediates, new_false_intermediates def _make_inputs_match(true_graph, false_graph, true_inputs, false_inputs): @@ -425,11 +454,11 @@ def _make_inputs_match(true_graph, false_graph, true_inputs, false_inputs): true_graph.inputs = ( [true_input_to_param[t] for t in shared_inputs] + [true_input_to_param[t] for t in true_only_inputs] + - _create_dummy_params(true_graph, false_only_inputs)) + _create_dummy_inputs(true_graph, false_only_inputs)) false_graph.inputs = ( [false_input_to_param[t] for t in shared_inputs] + - _create_dummy_params(false_graph, true_only_inputs) + + _create_dummy_inputs(false_graph, true_only_inputs) + [false_input_to_param[t] for t in false_only_inputs]) # Rewrite the FuncGraphs' state to reflect the new inputs. @@ -441,7 +470,12 @@ def _make_inputs_match(true_graph, false_graph, true_inputs, false_inputs): return new_inputs -def _create_dummy_params(func_graph, template_tensors): +def _wrap_intermediates(func_graph, intermediates): + with func_graph.as_default(): + return [gen_dataset_ops.optional_from_value([t]) for t in intermediates] + + +def _create_dummy_inputs(func_graph, template_tensors): """Creates tensors in func_graph to represent template_tensors. Args: @@ -451,6 +485,27 @@ def _create_dummy_params(func_graph, template_tensors): Returns: A list of tensors in func_graph. """ + with func_graph.as_default(): + return [array_ops.placeholder(t.dtype, shape=t.shape) + for t in template_tensors] + + +def _create_none_optionals(func_graph, template_tensors): + """Creates none optionals in func_graph to represent template_tensors. + + Args: + func_graph: FuncGraph. + template_tensors: a list of tensors in func_graph. + + Returns: + A list of tensors in func_graph. + """ + with func_graph.as_default(): + return [gen_dataset_ops.optional_none() for _ in template_tensors] + + +def _create_fakeparams(func_graph, template_tensors): + """Create FakeParams for the XLA case.""" with func_graph.as_default(): return [gen_functional_ops.fake_param(dtype=t.dtype, shape=t.shape) for t in template_tensors] @@ -462,12 +517,16 @@ def _check_same_outputs(true_graph, false_graph): false_output_types = [t.dtype for t in false_graph.outputs] if (len(true_graph.outputs) != len(false_graph.outputs) or true_output_types != false_output_types): - raise ValueError( + raise TypeError( "true_fn() and false_fn() must return the same number and type of " "arguments, got:\n" " true_fn: %s\n" " false_fn: %s" % (true_output_types, false_output_types)) + # Make sure both structured outputs for both graphs have the same structure + nest.assert_same_structure(true_graph.structured_outputs, + false_graph.structured_outputs) + def _get_output_shapes(true_graph_outputs, false_graph_outputs): output_shapes = [ @@ -475,3 +534,38 @@ def _get_output_shapes(true_graph_outputs, false_graph_outputs): for t_out, f_out in zip(true_graph_outputs, false_graph_outputs) ] return output_shapes + + +class _CondGradFuncGraph(util.CondBranchFuncGraph): + """FuncGraph for the gradient function of the branch of an If op. + + Handles unwrapping optional intermediate values that are captured by the + gradient computation. + """ + + def __init__(self, name, forward_graph): + super(_CondGradFuncGraph, self).__init__(name, read_only_collections=False) + self._forward_graph = forward_graph + + def _capture_helper(self, tensor, name): + if (tensor.graph is not self._forward_graph or + tensor in self._forward_graph.inputs or + tensor in self._forward_graph.outputs): + return super(_CondGradFuncGraph, self)._capture_helper(tensor, name) + + # 'tensor' is an intermediate in the forward graph. We find the corresonding + # optional tensor, which is output from the If op, and capture it as + # normal. We then unwrap the captured optional value to get the raw + # intermediate value. + for consumer in tensor.consumers(): + if (consumer.type == "OptionalFromValue" + and consumer.outputs[0] in self._forward_graph.outputs): + optional = consumer.outputs[0] + captured_optional = super(_CondGradFuncGraph, self)._capture_helper( + optional, name) + return gen_dataset_ops.optional_get_value( + captured_optional, [tensor.dtype], [tensor.shape])[0] + raise ValueError( + "Couldn't find OptionalFromValue consumer for tensor '%s'.\n" + "This is an internal bug, please report at " + "https://github.com/tensorflow/tensorflow/issues." % tensor.name) diff --git a/tensorflow/python/ops/confusion_matrix.py b/tensorflow/python/ops/confusion_matrix.py index b86b174afe..ccfe3b65c2 100644 --- a/tensorflow/python/ops/confusion_matrix.py +++ b/tensorflow/python/ops/confusion_matrix.py @@ -90,12 +90,13 @@ def remove_squeezable_dimensions( return labels, predictions -@tf_export( - 'math.confusion_matrix', - v1=['math.confusion_matrix', 'confusion_matrix']) -@deprecation.deprecated_endpoints('confusion_matrix', 'train.confusion_matrix') -def confusion_matrix(labels, predictions, num_classes=None, dtype=dtypes.int32, - name=None, weights=None): +@tf_export('math.confusion_matrix', v1=[]) +def confusion_matrix(labels, + predictions, + num_classes=None, + weights=None, + dtype=dtypes.int32, + name=None): """Computes the confusion matrix from predictions and labels. The matrix columns represent the prediction labels and the rows represent the @@ -132,9 +133,9 @@ def confusion_matrix(labels, predictions, num_classes=None, dtype=dtypes.int32, num_classes: The possible number of labels the classification task can have. If this value is not provided, it will be calculated using both predictions and labels array. + weights: An optional `Tensor` whose shape matches `predictions`. dtype: Data type of the confusion matrix. name: Scope name. - weights: An optional `Tensor` whose shape matches `predictions`. Returns: A `Tensor` of type `dtype` with shape `[n, n]` representing the confusion @@ -193,3 +194,65 @@ def confusion_matrix(labels, predictions, num_classes=None, dtype=dtypes.int32, zero_matrix = array_ops.zeros(math_ops.to_int32(shape), dtype) return sparse_ops.sparse_add(zero_matrix, cm_sparse) + + +@tf_export(v1=['math.confusion_matrix', 'confusion_matrix']) +@deprecation.deprecated_endpoints('confusion_matrix', 'train.confusion_matrix') +def confusion_matrix_v1(labels, + predictions, + num_classes=None, + dtype=dtypes.int32, + name=None, + weights=None): + """Computes the confusion matrix from predictions and labels. + + The matrix columns represent the prediction labels and the rows represent the + real labels. The confusion matrix is always a 2-D array of shape `[n, n]`, + where `n` is the number of valid labels for a given classification task. Both + prediction and labels must be 1-D arrays of the same shape in order for this + function to work. + + If `num_classes` is `None`, then `num_classes` will be set to one plus the + maximum value in either predictions or labels. Class labels are expected to + start at 0. For example, if `num_classes` is 3, then the possible labels + would be `[0, 1, 2]`. + + If `weights` is not `None`, then each prediction contributes its + corresponding weight to the total value of the confusion matrix cell. + + For example: + + ```python + tf.confusion_matrix([1, 2, 4], [2, 2, 4]) ==> + [[0 0 0 0 0] + [0 0 1 0 0] + [0 0 1 0 0] + [0 0 0 0 0] + [0 0 0 0 1]] + ``` + + Note that the possible labels are assumed to be `[0, 1, 2, 3, 4]`, + resulting in a 5x5 confusion matrix. + + Args: + labels: 1-D `Tensor` of real labels for the classification task. + predictions: 1-D `Tensor` of predictions for a given classification. + num_classes: The possible number of labels the classification task can have. + If this value is not provided, it will be calculated using both + predictions and labels array. + dtype: Data type of the confusion matrix. + name: Scope name. + weights: An optional `Tensor` whose shape matches `predictions`. + + Returns: + A `Tensor` of type `dtype` with shape `[n, n]` representing the confusion + matrix, where `n` is the number of possible labels in the classification + task. + + Raises: + ValueError: If both predictions and labels are not 1-D vectors and have + mismatched shapes, or if `weights` is not `None` and its shape doesn't + match `predictions`. + """ + return confusion_matrix(labels, predictions, num_classes, weights, dtype, + name) diff --git a/tensorflow/python/ops/control_flow_ops.py b/tensorflow/python/ops/control_flow_ops.py index 0d04f0697d..b7e50c1dae 100644 --- a/tensorflow/python/ops/control_flow_ops.py +++ b/tensorflow/python/ops/control_flow_ops.py @@ -158,7 +158,7 @@ def Assert(condition, data, summarize=None, name=None): with ops.name_scope(name, "Assert", [condition, data]) as name: xs = ops.convert_n_to_tensor(data) - if all([x.dtype in {dtypes.string, dtypes.int32} for x in xs]): + if all(x.dtype in {dtypes.string, dtypes.int32} for x in xs): # As a simple heuristic, we assume that string and int32 are # on host to avoid the need to use cond. If it is not case, # we will pay the price copying the tensor to host memory. @@ -457,19 +457,19 @@ def merge(inputs, name=None): ValueError: If any of the inputs is None, or inputs are IndexedSlices and some but not all have a dense_shape property. """ - if any([inp is None for inp in inputs]): + if any(inp is None for inp in inputs): raise ValueError("At least one of the merge inputs is None: %s" % inputs) with ops.name_scope(name, "Merge", inputs) as name: inputs = [ ops.internal_convert_to_tensor_or_indexed_slices(inp, as_ref=True) for inp in inputs ] - if all([isinstance(v, ops.Tensor) for v in inputs]): - if all([v.dtype._is_ref_dtype for v in inputs]): # pylint: disable=protected-access + if all(isinstance(v, ops.Tensor) for v in inputs): + if all(v.dtype._is_ref_dtype for v in inputs): # pylint: disable=protected-access return gen_control_flow_ops.ref_merge(inputs, name) else: return gen_control_flow_ops.merge(inputs, name) - elif all([isinstance(v, sparse_tensor.SparseTensor) for v in inputs]): + elif all(isinstance(v, sparse_tensor.SparseTensor) for v in inputs): # Only handle the case when all inputs are SparseTensor. values, _ = merge([inp.values for inp in inputs], name=name) indices, chosen_index = gen_control_flow_ops.merge( @@ -557,7 +557,7 @@ def _SetShapeInvariants(input_vars, enter_vars, shapes): if shapes is None: return flat_shapes = nest.flatten(shapes) - if not all([isinstance(s, tensor_shape.TensorShape) for s in flat_shapes]): + if not all(isinstance(s, tensor_shape.TensorShape) for s in flat_shapes): raise ValueError("`shapes` must be a (possibly nested) list of shapes.") # Check that the shapes of the inputs are less than the shape invariants, # and set the shapes of `enter_vars` to the shape invariants. @@ -1976,7 +1976,7 @@ def _UnpackIfSingleton(res): # pylint: disable=redefined-outer-name # pylint: disable=g-doc-args -@tf_export("cond") +@tf_export(v1=["cond"]) @deprecation.deprecated_args( None, "fn1/fn2 are deprecated in favor of the true_fn/false_fn arguments.", "fn1", "fn2") @@ -2173,6 +2173,77 @@ def cond(pred, # pylint: enable=redefined-outer-name +@tf_export("cond", v1=[]) +def cond_for_tf_v2(pred, + true_fn=None, + false_fn=None, + name=None): + """Return `true_fn()` if the predicate `pred` is true else `false_fn()`. + + `true_fn` and `false_fn` both return lists of output tensors. `true_fn` and + `false_fn` must have the same non-zero number and type of outputs. + + **WARNING**: Any Tensors or Operations created outside of `true_fn` and + `false_fn` will be executed regardless of which branch is selected at runtime. + + Although this behavior is consistent with the dataflow model of TensorFlow, + it has frequently surprised users who expected a lazier semantics. + Consider the following simple program: + + ```python + z = tf.multiply(a, b) + result = tf.cond(x < y, lambda: tf.add(x, z), lambda: tf.square(y)) + ``` + + If `x < y`, the `tf.add` operation will be executed and `tf.square` + operation will not be executed. Since `z` is needed for at least one + branch of the `cond`, the `tf.multiply` operation is always executed, + unconditionally. + + Note that `cond` calls `true_fn` and `false_fn` *exactly once* (inside the + call to `cond`, and not at all during `Session.run()`). `cond` + stitches together the graph fragments created during the `true_fn` and + `false_fn` calls with some additional graph nodes to ensure that the right + branch gets executed depending on the value of `pred`. + + `tf.cond` supports nested structures as implemented in + `tensorflow.python.util.nest`. Both `true_fn` and `false_fn` must return the + same (possibly nested) value structure of lists, tuples, and/or named tuples. + Singleton lists and tuples form the only exceptions to this: when returned by + `true_fn` and/or `false_fn`, they are implicitly unpacked to single values. + + Args: + pred: A scalar determining whether to return the result of `true_fn` or + `false_fn`. + true_fn: The callable to be performed if pred is true. + false_fn: The callable to be performed if pred is false. + name: Optional name prefix for the returned tensors. + + Returns: + Tensors returned by the call to either `true_fn` or `false_fn`. If the + callables return a singleton list, the element is extracted from the list. + + Raises: + TypeError: if `true_fn` or `false_fn` is not callable. + ValueError: if `true_fn` and `false_fn` do not return the same number of + tensors, or return tensors of different types. + + Example: + + ```python + x = tf.constant(2) + y = tf.constant(5) + def f1(): return tf.multiply(x, 17) + def f2(): return tf.add(y, 23) + r = tf.cond(tf.less(x, y), f1, f2) + # r is set to f1(). + # Operations in f2 (e.g., tf.add) are not executed. + ``` + + """ + return cond(pred, true_fn=true_fn, false_fn=false_fn, strict=True, name=name) + + def _resource_safe_shape(t): """Returns the shape of t or the variable it points to.""" if t.dtype == dtypes.resource: @@ -3065,7 +3136,186 @@ class WhileContext(ControlFlowContext): # pylint: disable=redefined-outer-name -@tf_export("while_loop") +@tf_export("while_loop", v1=[]) +def while_loop_v2(cond, + body, + loop_vars, + shape_invariants=None, + parallel_iterations=10, + back_prop=True, + swap_memory=False, + maximum_iterations=None, + name=None): + """Repeat `body` while the condition `cond` is true. + + `cond` is a callable returning a boolean scalar tensor. `body` is a callable + returning a (possibly nested) tuple, namedtuple or list of tensors of the same + arity (length and structure) and types as `loop_vars`. `loop_vars` is a + (possibly nested) tuple, namedtuple or list of tensors that is passed to both + `cond` and `body`. `cond` and `body` both take as many arguments as there are + `loop_vars`. + + In addition to regular Tensors or IndexedSlices, the body may accept and + return TensorArray objects. The flows of the TensorArray objects will + be appropriately forwarded between loops and during gradient calculations. + + Note that `while_loop` calls `cond` and `body` *exactly once* (inside the + call to `while_loop`, and not at all during `Session.run()`). `while_loop` + stitches together the graph fragments created during the `cond` and `body` + calls with some additional graph nodes to create the graph flow that + repeats `body` until `cond` returns false. + + For correctness, `tf.while_loop()` strictly enforces shape invariants for + the loop variables. A shape invariant is a (possibly partial) shape that + is unchanged across the iterations of the loop. An error will be raised + if the shape of a loop variable after an iteration is determined to be more + general than or incompatible with its shape invariant. For example, a shape + of [11, None] is more general than a shape of [11, 17], and [11, 21] is not + compatible with [11, 17]. By default (if the argument `shape_invariants` is + not specified), it is assumed that the initial shape of each tensor in + `loop_vars` is the same in every iteration. The `shape_invariants` argument + allows the caller to specify a less specific shape invariant for each loop + variable, which is needed if the shape varies between iterations. The + `tf.Tensor.set_shape` + function may also be used in the `body` function to indicate that + the output loop variable has a particular shape. The shape invariant for + SparseTensor and IndexedSlices are treated specially as follows: + + a) If a loop variable is a SparseTensor, the shape invariant must be + TensorShape([r]) where r is the rank of the dense tensor represented + by the sparse tensor. It means the shapes of the three tensors of the + SparseTensor are ([None], [None, r], [r]). NOTE: The shape invariant here + is the shape of the SparseTensor.dense_shape property. It must be the shape of + a vector. + + b) If a loop variable is an IndexedSlices, the shape invariant must be + a shape invariant of the values tensor of the IndexedSlices. It means + the shapes of the three tensors of the IndexedSlices are (shape, [shape[0]], + [shape.ndims]). + + `while_loop` implements non-strict semantics, enabling multiple iterations + to run in parallel. The maximum number of parallel iterations can be + controlled by `parallel_iterations`, which gives users some control over + memory consumption and execution order. For correct programs, `while_loop` + should return the same result for any parallel_iterations > 0. + + For training, TensorFlow stores the tensors that are produced in the + forward inference and are needed in back propagation. These tensors are a + main source of memory consumption and often cause OOM errors when training + on GPUs. When the flag swap_memory is true, we swap out these tensors from + GPU to CPU. This for example allows us to train RNN models with very long + sequences and large batches. + + Args: + cond: A callable that represents the termination condition of the loop. + body: A callable that represents the loop body. + loop_vars: A (possibly nested) tuple, namedtuple or list of numpy array, + `Tensor`, and `TensorArray` objects. + shape_invariants: The shape invariants for the loop variables. + parallel_iterations: The number of iterations allowed to run in parallel. It + must be a positive integer. + back_prop: Whether backprop is enabled for this while loop. + swap_memory: Whether GPU-CPU memory swap is enabled for this loop. + maximum_iterations: Optional maximum number of iterations of the while loop + to run. If provided, the `cond` output is AND-ed with an additional + condition ensuring the number of iterations executed is no greater than + `maximum_iterations`. + name: Optional name prefix for the returned tensors. + + Returns: + The output tensors for the loop variables after the loop. The return value + has the same structure as `loop_vars`. + + Raises: + TypeError: if `cond` or `body` is not callable. + ValueError: if `loop_vars` is empty. + + Example: + + ```python + i = tf.constant(0) + c = lambda i: tf.less(i, 10) + b = lambda i: tf.add(i, 1) + r = tf.while_loop(c, b, [i]) + ``` + + Example with nesting and a namedtuple: + + ```python + import collections + Pair = collections.namedtuple('Pair', 'j, k') + ijk_0 = (tf.constant(0), Pair(tf.constant(1), tf.constant(2))) + c = lambda i, p: i < 10 + b = lambda i, p: (i + 1, Pair((p.j + p.k), (p.j - p.k))) + ijk_final = tf.while_loop(c, b, ijk_0) + ``` + + Example using shape_invariants: + + ```python + i0 = tf.constant(0) + m0 = tf.ones([2, 2]) + c = lambda i, m: i < 10 + b = lambda i, m: [i+1, tf.concat([m, m], axis=0)] + tf.while_loop( + c, b, loop_vars=[i0, m0], + shape_invariants=[i0.get_shape(), tf.TensorShape([None, 2])]) + ``` + + Example which demonstrates non-strict semantics: In the following + example, the final value of the counter `i` does not depend on `x`. So + the `while_loop` can increment the counter parallel to updates of `x`. + However, because the loop counter at one loop iteration depends + on the value at the previous iteration, the loop counter itself cannot + be incremented in parallel. Hence if we just want the final value of the + counter (which we print on the line `print(sess.run(i))`), then + `x` will never be incremented, but the counter will be updated on a + single thread. Conversely, if we want the value of the output (which we + print on the line `print(sess.run(out).shape)`), then the counter may be + incremented on its own thread, while `x` can be incremented in + parallel on a separate thread. In the extreme case, it is conceivable + that the thread incrementing the counter runs until completion before + `x` is incremented even a single time. The only thing that can never + happen is that the thread updating `x` can never get ahead of the + counter thread because the thread incrementing `x` depends on the value + of the counter. + + ```python + import tensorflow as tf + + n = 10000 + x = tf.constant(list(range(n))) + c = lambda i, x: i < n + b = lambda i, x: (tf.Print(i + 1, [i]), tf.Print(x + 1, [i], "x:")) + i, out = tf.while_loop(c, b, (0, x)) + with tf.Session() as sess: + print(sess.run(i)) # prints [0] ... [9999] + + # The following line may increment the counter and x in parallel. + # The counter thread may get ahead of the other thread, but not the + # other way around. So you may see things like + # [9996] x:[9987] + # meaning that the counter thread is on iteration 9996, + # while the other thread is on iteration 9987 + print(sess.run(out).shape) + ``` + + """ + return while_loop( + cond=cond, + body=body, + loop_vars=loop_vars, + shape_invariants=shape_invariants, + parallel_iterations=parallel_iterations, + back_prop=back_prop, + swap_memory=swap_memory, + name=name, + maximum_iterations=maximum_iterations, + return_same_structure=True) + + +# pylint: disable=redefined-outer-name +@tf_export(v1=["while_loop"]) def while_loop(cond, body, loop_vars, @@ -3244,7 +3494,8 @@ def while_loop(cond, loop_vars, shape_invariants=shape_invariants, maximum_iterations=maximum_iterations, - name=name) + name=name, + return_same_structure=return_same_structure) with ops.name_scope(name, "while", loop_vars): if not loop_vars: @@ -3465,7 +3716,43 @@ def group(*inputs, **kwargs): return no_op(name=name) -@tf_export("tuple") +@tf_export("tuple", v1=[]) +def tuple_v2(tensors, control_inputs=None, name=None): + """Group tensors together. + + This creates a tuple of tensors with the same values as the `tensors` + argument, except that the value of each tensor is only returned after the + values of all tensors have been computed. + + `control_inputs` contains additional ops that have to finish before this op + finishes, but whose outputs are not returned. + + This can be used as a "join" mechanism for parallel computations: all the + argument tensors can be computed in parallel, but the values of any tensor + returned by `tuple` are only available after all the parallel computations + are done. + + See also `tf.group` and + `tf.control_dependencies`. + + Args: + tensors: A list of `Tensor`s or `IndexedSlices`, some entries can be `None`. + control_inputs: List of additional ops to finish before returning. + name: (optional) A name to use as a `name_scope` for the operation. + + Returns: + Same as `tensors`. + + Raises: + ValueError: If `tensors` does not contain any `Tensor` or `IndexedSlices`. + TypeError: If `control_inputs` is not a list of `Operation` or `Tensor` + objects. + + """ + return tuple(tensors=tensors, name=name, control_inputs=control_inputs) # pylint: disable=redefined-builtin + + +@tf_export(v1=["tuple"]) def tuple(tensors, name=None, control_inputs=None): # pylint: disable=redefined-builtin """Group tensors together. diff --git a/tensorflow/python/ops/control_flow_ops_test.py b/tensorflow/python/ops/control_flow_ops_test.py index c3514c183c..c020189ad6 100644 --- a/tensorflow/python/ops/control_flow_ops_test.py +++ b/tensorflow/python/ops/control_flow_ops_test.py @@ -155,9 +155,9 @@ class WithDependenciesTestCase(test_util.TensorFlowTestCase): constant_op.constant(7)) with self.cached_session(): variables.global_variables_initializer().run() - self.assertEquals(0, counter.eval()) - self.assertEquals(7, const_with_dep.eval()) - self.assertEquals(1, counter.eval()) + self.assertEquals(0, self.evaluate(counter)) + self.assertEquals(7, self.evaluate(const_with_dep)) + self.assertEquals(1, self.evaluate(counter)) def testListDependencies(self): with ops.Graph().as_default(): @@ -169,9 +169,9 @@ class WithDependenciesTestCase(test_util.TensorFlowTestCase): constant_op.constant(7)) with self.cached_session(): variables.global_variables_initializer().run() - self.assertEquals(0, counter.eval()) - self.assertEquals(7, const_with_dep.eval()) - self.assertEquals(1, counter.eval()) + self.assertEquals(0, self.evaluate(counter)) + self.assertEquals(7, self.evaluate(const_with_dep)) + self.assertEquals(1, self.evaluate(counter)) class SwitchTestCase(test_util.TensorFlowTestCase): @@ -209,9 +209,9 @@ class SwitchTestCase(test_util.TensorFlowTestCase): optimizer = momentum.MomentumOptimizer(0.1, 0.9) train_op = optimizer.minimize(cost) with self.cached_session() as sess: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) for _ in range(10): - sess.run([train_op]) + self.evaluate([train_op]) def testResourceReadInLoop(self): with ops.Graph().as_default(): @@ -232,8 +232,8 @@ class SwitchTestCase(test_util.TensorFlowTestCase): cond, body, [constant_op.constant(0), constant_op.constant(0.0)]) with self.cached_session() as sess: - sess.run(variables.global_variables_initializer()) - self.assertAllEqual(10.0, cost.eval()) + self.evaluate(variables.global_variables_initializer()) + self.assertAllEqual(10.0, self.evaluate(cost)) def doTestIndexedSlicesGradientInCondInWhileLoop(self, use_resource=False): with ops.Graph().as_default(): @@ -269,8 +269,8 @@ class SwitchTestCase(test_util.TensorFlowTestCase): static_grads.indices) with self.cached_session() as sess: - sess.run(variables.global_variables_initializer()) - self.assertAllEqual(*sess.run([static_grads, dynamic_grads])) + self.evaluate(variables.global_variables_initializer()) + self.assertAllEqual(*self.evaluate([static_grads, dynamic_grads])) def testIndexedSlicesGradientInCondInWhileLoop(self): self.doTestIndexedSlicesGradientInCondInWhileLoop(use_resource=False) @@ -398,9 +398,9 @@ class CondTest(test_util.TensorFlowTestCase): pred=bool_var, true_fn=lambda: state_ops.assign(bool_var, False), false_fn=lambda: True) - sess.run(bool_var.initializer) - self.assertEquals(sess.run(cond_on_bool_var), False) - self.assertEquals(sess.run(cond_on_bool_var), True) + self.evaluate(bool_var.initializer) + self.assertEquals(self.evaluate(cond_on_bool_var), False) + self.assertEquals(self.evaluate(cond_on_bool_var), True) def testCondMissingArg1(self): with ops.Graph().as_default(): diff --git a/tensorflow/python/ops/control_flow_util.py b/tensorflow/python/ops/control_flow_util.py index 72c074ed1a..cb628f4aa6 100644 --- a/tensorflow/python/ops/control_flow_util.py +++ b/tensorflow/python/ops/control_flow_util.py @@ -38,6 +38,11 @@ def IsInXLAContext(op): return GetContainingXLAContext(ctxt) is not None +def InXlaContext(graph): + ctxt = graph._get_control_flow_context() # pylint: disable=protected-access + return GetContainingXLAContext(ctxt) is not None + + def IsInWhileLoop(op): ctxt = op._get_control_flow_context() # pylint: disable=protected-access return GetContainingWhileContext(ctxt) is not None diff --git a/tensorflow/python/ops/control_flow_util_v2.py b/tensorflow/python/ops/control_flow_util_v2.py index cab1d7b02e..5f56850884 100644 --- a/tensorflow/python/ops/control_flow_util_v2.py +++ b/tensorflow/python/ops/control_flow_util_v2.py @@ -19,10 +19,12 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +from tensorflow.core.framework import attr_value_pb2 from tensorflow.python.eager import context from tensorflow.python.eager import function from tensorflow.python.framework import ops from tensorflow.python.framework.func_graph import FuncGraph +from tensorflow.python.ops import control_flow_util class CondBranchFuncGraph(FuncGraph): @@ -90,3 +92,31 @@ def unique_fn_name(scope, name): def unique_grad_fn_name(forward_name): return "%s_grad_%s" % (forward_name, ops.uid()) + + +def maybe_set_lowering_attr(op): + """Sets the flag to enable lowering on `op` if necessary. + + Lowering allows cond_v2 and while_v2 to avoid some of the limitations of + Functions, allowing users to specify devices & colocation inside of cond_v2 + and while_v2 input functions, and enabling non-strict evaluation & partial + pruning. This brings v2 control flow closer to feature parity with v1 control + flow. + + However, we do not lower in the following cases: + - When the `If` or `While` ops are in the XLA context. Because it is easier + for XLA to apply its own optimizations when dealing with un-lowered + control flow operators than with low-level control flow primitives. + - When the eager execution context specifies the executor of functions to + be the single threaded executor (see context.function_executor_type()). + Because the single threaded executor does not support v1 control flow ops. + + Args: + op: An `If` or `While` Operation. + """ + if (not control_flow_util.IsInXLAContext(op) and + context.context().get_function_call_options().executor_type + != "SINGLE_THREADED_EXECUTOR"): + # pylint: disable=protected-access + op._set_attr("_lower_using_switch_merge", attr_value_pb2.AttrValue(b=True)) + # pylint: enable=protected-access diff --git a/tensorflow/python/ops/ctc_ops.py b/tensorflow/python/ops/ctc_ops.py index e1071afd8e..3a7eb9355a 100644 --- a/tensorflow/python/ops/ctc_ops.py +++ b/tensorflow/python/ops/ctc_ops.py @@ -19,17 +19,27 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import function from tensorflow.python.framework import ops from tensorflow.python.framework import sparse_tensor from tensorflow.python.ops import array_ops +from tensorflow.python.ops import functional_ops from tensorflow.python.ops import gen_ctc_ops +from tensorflow.python.ops import inplace_ops +from tensorflow.python.ops import linalg_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import nn_ops +from tensorflow.python.ops import sparse_ops from tensorflow.python.ops.nn_grad import _BroadcastMul +from tensorflow.python.util import nest from tensorflow.python.util.tf_export import tf_export # pylint: disable=protected-access, invalid-name -@tf_export("nn.ctc_loss") +@tf_export(v1=["nn.ctc_loss"]) def ctc_loss(labels, inputs, sequence_length, preprocess_collapse_repeated=False, ctc_merge_repeated=True, @@ -336,6 +346,785 @@ def ctc_beam_search_decoder_v2(inputs, sequence_length, beam_width=100, ops.NotDifferentiable("CTCGreedyDecoder") +ops.NotDifferentiable("CTCBeamSearchDecoder") -ops.NotDifferentiable("CTCBeamSearchDecoder") +def _ctc_state_trans(label_seq): + """Compute CTC alignment model transition matrix. + + Args: + label_seq: tensor of shape [batch_size, max_seq_length] + + Returns: + tensor of shape [batch_size, states, states] with a state transition matrix + computed for each sequence of the batch. + """ + + with ops.name_scope("ctc_state_trans"): + label_seq = ops.convert_to_tensor(label_seq, name="label_seq") + batch_size = _get_dim(label_seq, 0) + num_labels = _get_dim(label_seq, 1) + + num_label_states = num_labels + 1 + num_states = 2 * num_label_states + + label_states = math_ops.range(num_label_states) + blank_states = label_states + num_label_states + + # Start state to first label. + start_to_label = [[1, 0]] + + # Blank to label transitions. + blank_to_label = array_ops.stack([label_states[1:], blank_states[:-1]], 1) + + # Label to blank transitions. + label_to_blank = array_ops.stack([blank_states, label_states], 1) + + # Scatter transitions that don't depend on sequence. + indices = array_ops.concat( + [start_to_label, blank_to_label, label_to_blank], 0) + values = array_ops.ones([_get_dim(indices, 0)]) + trans = array_ops.scatter_nd( + indices, values, shape=[num_states, num_states]) + trans += linalg_ops.eye(num_states) # Self-loops. + + # Label to label transitions. Disallow transitions between repeated labels + # with no blank state in between. + batch_idx = array_ops.zeros_like(label_states[2:]) + indices = array_ops.stack( + [batch_idx, label_states[2:], label_states[1:-1]], 1) + indices = array_ops.tile( + array_ops.expand_dims(indices, 0), [batch_size, 1, 1]) + batch_idx = array_ops.expand_dims(math_ops.range(batch_size), 1) * [1, 0, 0] + indices += array_ops.expand_dims(batch_idx, 1) + repeats = math_ops.equal(label_seq[:, :-1], label_seq[:, 1:]) + values = 1.0 - math_ops.cast(repeats, dtypes.float32) + batched_shape = [batch_size, num_states, num_states] + label_to_label = array_ops.scatter_nd(indices, values, batched_shape) + + return array_ops.expand_dims(trans, 0) + label_to_label + + +def ctc_state_log_probs(seq_lengths, max_seq_length): + """Computes CTC alignment initial and final state log probabilities. + + Create the initial/final state values directly as log values to avoid + having to take a float64 log on tpu (which does not exist). + + Args: + seq_lengths: int tensor of shape [batch_size], seq lengths in the batch. + max_seq_length: int, max sequence length possible. + + Returns: + initial_state_log_probs, final_state_log_probs + """ + + batch_size = _get_dim(seq_lengths, 0) + num_label_states = max_seq_length + 1 + num_duration_states = 2 + num_states = num_duration_states * num_label_states + log_0 = math_ops.cast( + math_ops.log(math_ops.cast(0, dtypes.float64) + 1e-307), + dtypes.float32) + + initial_state_log_probs = array_ops.one_hot( + indices=array_ops.zeros([batch_size], dtype=dtypes.int32), + depth=num_states, + on_value=0.0, + off_value=log_0, axis=1) + + label_final_state_mask = array_ops.one_hot( + seq_lengths, depth=num_label_states, axis=0) + duration_final_state_mask = array_ops.ones( + [num_duration_states, 1, batch_size]) + final_state_mask = duration_final_state_mask * label_final_state_mask + final_state_log_probs = (1.0 - final_state_mask) * log_0 + final_state_log_probs = array_ops.reshape( + final_state_log_probs, [num_states, batch_size]) + + return initial_state_log_probs, array_ops.transpose(final_state_log_probs) + + +def _ilabel_to_state(labels, num_labels, ilabel_log_probs): + """Project ilabel log probs to state log probs.""" + + num_label_states = _get_dim(labels, 1) + blank = ilabel_log_probs[:, :, :1] + blank = array_ops.tile(blank, [1, 1, num_label_states + 1]) + one_hot = array_ops.one_hot(labels, depth=num_labels) + one_hot = array_ops.expand_dims(one_hot, axis=0) + ilabel_log_probs = array_ops.expand_dims(ilabel_log_probs, axis=2) + state_log_probs = math_ops.reduce_sum(ilabel_log_probs * one_hot, axis=3) + state_log_probs = array_ops.concat([state_log_probs, blank], axis=2) + return array_ops.pad( + state_log_probs, [[0, 0], [0, 0], [1, 0]], + constant_values=math_ops.log(0.0)) + + +def _state_to_olabel(labels, num_labels, states): + """Sum state log probs to ilabel log probs.""" + + num_label_states = _get_dim(labels, 1) + 1 + label_states = states[:, :, 1:num_label_states] + blank_states = states[:, :, num_label_states:] + one_hot = array_ops.one_hot( + labels - 1, depth=(num_labels - 1), + on_value=0.0, off_value=math_ops.log(0.0)) + one_hot = array_ops.expand_dims(one_hot, axis=0) + label_states = array_ops.expand_dims(label_states, axis=3) + label_olabels = math_ops.reduce_logsumexp(label_states + one_hot, axis=2) + blank_olabels = math_ops.reduce_logsumexp( + blank_states, axis=2, keepdims=True) + return array_ops.concat([blank_olabels, label_olabels], axis=-1) + + +# pylint: disable=redefined-outer-name +def _state_to_olabel_unique(labels, num_labels, states, unique): + """Sum state log probs to ilabel log probs using unique label indices.""" + + num_label_states = _get_dim(labels, 1) + 1 + label_states = states[:, :, 1:num_label_states] + blank_states = states[:, :, num_label_states:] + + unique_y, unique_idx = unique + mul_reduce = _sum_states(unique_idx, label_states) + + num_frames = states.shape[0] + batch_size = states.shape[1] + num_states = num_label_states - 1 + batch_state_major = array_ops.transpose(mul_reduce, perm=[1, 2, 0]) + batch_state_major = array_ops.reshape( + batch_state_major, [batch_size * num_states, num_frames]) + batch_offset = math_ops.range(batch_size, dtype=unique_y.dtype) * num_labels + indices = unique_y + array_ops.expand_dims(batch_offset, axis=-1) + indices = array_ops.reshape(indices, [-1, 1]) + scatter = array_ops.scatter_nd( + indices=indices, + updates=batch_state_major, + shape=[batch_size * num_labels, num_frames]) + scatter = array_ops.reshape(scatter, [batch_size, num_labels, num_frames]) + scatter = array_ops.where( + math_ops.equal(scatter, 0.0), + array_ops.fill(array_ops.shape(scatter), math_ops.log(0.0)), + scatter) + label_olabels = array_ops.transpose(scatter, [2, 0, 1]) + label_olabels = label_olabels[:, :, 1:] + + blank_olabels = math_ops.reduce_logsumexp( + blank_states, axis=2, keepdims=True) + + return array_ops.concat([blank_olabels, label_olabels], axis=-1) + + +def ctc_loss_and_grad(logits, labels, label_length, logit_length, unique=None): + """Computes the CTC loss and gradients. + + Most users will want fwd_bwd.ctc_loss + + This function returns the computed gradient, it does not have a gradient + of its own defined. + + Args: + logits: tensor of shape [frames, batch_size, num_labels] + labels: tensor of shape [batch_size, max_label_seq_length] + label_length: tensor of shape [batch_size] + Length of reference label sequence in labels. + logit_length: tensor of shape [batch_size] + Length of input sequence in logits. + unique: (optional) unique label indices as computed by unique(labels) + If supplied, enables an implementation that is faster and more memory + efficient on TPU. + + Returns: + loss: tensor of shape [batch_size] + gradient: tensor of shape [frames, batch_size, num_labels] + """ + + num_labels = _get_dim(logits, 2) + max_label_seq_length = _get_dim(labels, 1) + + ilabel_log_probs = nn_ops.log_softmax(logits) + state_log_probs = _ilabel_to_state(labels, num_labels, ilabel_log_probs) + state_trans_probs = _ctc_state_trans(labels) + initial_state_log_probs, final_state_log_probs = ctc_state_log_probs( + label_length, max_label_seq_length) + fwd_bwd_log_probs, log_likelihood = _forward_backward_log( + state_trans_log_probs=math_ops.log(state_trans_probs), + initial_state_log_probs=initial_state_log_probs, + final_state_log_probs=final_state_log_probs, + observed_log_probs=state_log_probs, + sequence_length=logit_length) + + if unique: + olabel_log_probs = _state_to_olabel_unique( + labels, num_labels, fwd_bwd_log_probs, unique) + else: + olabel_log_probs = _state_to_olabel(labels, num_labels, fwd_bwd_log_probs) + + grad = math_ops.exp(ilabel_log_probs) - math_ops.exp(olabel_log_probs) + loss = -log_likelihood + return loss, grad + + +def _ctc_loss_grad(op, grad_loss, _): + grad = op.outputs[1] + grad = [array_ops.reshape(grad_loss, [1, -1, 1]) * grad] + grad += [None] * (len(op.inputs) - len(grad)) + return grad + + +def _ctc_loss_shape(op): + return [op.inputs[2].get_shape(), op.inputs[0].get_shape()] + + +@tf_export("nn.ctc_loss", v1=["nn.ctc_loss_v2"]) +def ctc_loss_v2(labels, logits, label_length, logit_length, + logits_time_major=True, unique=None, + blank_index=None, name=None): + """Computes CTC (Connectionist Temporal Classification) loss. + + This op implements the CTC loss as presented in the article: + + [A. Graves, S. Fernandez, F. Gomez, J. Schmidhuber. + Connectionist Temporal Classification: Labeling Unsegmented Sequence Data + with Recurrent Neural Networks. ICML 2006, Pittsburgh, USA, + pp. 369-376.](http://www.cs.toronto.edu/~graves/icml_2006.pdf) + + Notes: + - Same as the "Classic CTC" in TensorFlow 1.x's tf.nn.ctc_loss setting of + preprocess_collapse_repeated=False, ctc_merge_repeated=True + - Labels may be supplied as either a dense, zero-padded tensor with a + vector of label sequence lengths OR as a SparseTensor. + - On TPU and GPU: + - Only dense padded labels are supported. + - On CPU: + - Caller may use SparseTensor or dense padded labels but calling with + a SparseTensor will be significantly faster. + - Default blank label is 0 rather num_classes - 1, unless overridden by + blank_index. + + Args: + labels: tensor of shape [batch_size, max_label_seq_length] or SparseTensor + logits: tensor of shape [frames, batch_size, num_labels], + if logits_time_major == False, shape is [batch_size, frames, num_labels]. + label_length: tensor of shape [batch_size], None if labels is SparseTensor + Length of reference label sequence in labels. + logit_length: tensor of shape [batch_size] + Length of input sequence in logits. + logits_time_major: (optional) If True (default), logits is shaped + [time, batch, logits]. If False, shape is [batch, time, logits] + unique: (optional) Unique label indices as computed by + ctc_unique_labels(labels). If supplied, enable a faster, memory + efficient implementation on TPU. + blank_index: (optional) Set the class index to use for the blank label. + Negative values will start from num_classes, ie, -1 will reproduce the + ctc_loss behavior of using num_classes - 1 for the blank symbol. + There is some memory/performance overhead to switching from the default + of 0 as an additional shifted copy of the logits may be created. + name: A name for this `Op`. Defaults to "ctc_loss_dense". + + Returns: + loss: tensor of shape [batch_size], negative log probabilities. + """ + if isinstance(labels, sparse_tensor.SparseTensor): + if blank_index is None: + raise ValueError( + "blank_index must be given when using SparseTensor labels.") + + if blank_index < 0: + blank_index += _get_dim(logits, 2) + + if blank_index != _get_dim(logits, 2) - 1: + logits = array_ops.concat([ + logits[:, :, :blank_index], + logits[:, :, blank_index+1:], + logits[:, :, blank_index:blank_index+1], + ], axis=2) + labels = sparse_tensor.SparseTensor( + labels.indices, + array_ops.where(labels.values < blank_index, + labels.values, + labels.values - 1), + labels.dense_shape) + + return ctc_loss(labels=labels, + inputs=logits, + sequence_length=logit_length, + time_major=logits_time_major) + + if blank_index is None: + blank_index = 0 + + return ctc_loss_dense(labels=labels, + logits=logits, + label_length=label_length, + logit_length=logit_length, + logits_time_major=logits_time_major, + unique=unique, + blank_index=blank_index, + name=name) + + +def ctc_loss_dense(labels, logits, label_length, logit_length, + logits_time_major=True, unique=None, + blank_index=0, name=None): + """Computes CTC (Connectionist Temporal Classification) loss. + + This op implements the CTC loss as presented in the article: + + [A. Graves, S. Fernandez, F. Gomez, J. Schmidhuber. + Connectionist Temporal Classification: Labeling Unsegmented Sequence Data + with Recurrent Neural Networks. ICML 2006, Pittsburgh, USA, + pp. 369-376.](http://www.cs.toronto.edu/~graves/icml_2006.pdf) + + Using the batched forward backward algorithm described in: + + [Sim, K. C., Narayanan, A., Bagby, T., Sainath, T. N., & Bacchiani, M. + Improving the efficiency of forward-backward algorithm using batched + computation in TensorFlow. + Automatic Speech Recognition and Understanding Workshop (ASRU), + 2017 IEEE (pp. 258-264). + ](https://ieeexplore.ieee.org/iel7/8260578/8268903/08268944.pdf) + + Notes: + Significant differences from tf.nn.ctc_loss: + Supports GPU and TPU (tf.nn.ctc_loss supports CPU only): + For batched operations, GPU and TPU are significantly faster than using + ctc_loss on CPU. + This implementation runs on CPU, but significantly slower than ctc_loss. + Blank label is 0 rather num_classes - 1, unless overridden by blank_index. + Logits and labels are dense arrays with padding rather than SparseTensor. + The only mode supported is the same as: + preprocess_collapse_repeated=False, ctc_merge_repeated=True + To collapse labels, the caller can preprocess label sequence first. + + The dense implementation supports both CPU, GPU and TPU. A fast path is + provided that significantly improves memory use for large vocabulary if the + caller preprocesses label sequences to get unique label indices on the CPU + (eg. in the data input pipeline) using ctc_ops.unique and simplies this in + the optional "unique" kwarg. This is especially useful for TPU and GPU but + also works with if used on CPU. + + Args: + labels: tensor of shape [batch_size, max_label_seq_length] + logits: tensor of shape [frames, batch_size, num_labels], + if logits_time_major == False, shape is [batch_size, frames, num_labels]. + label_length: tensor of shape [batch_size] + Length of reference label sequence in labels. + logit_length: tensor of shape [batch_size] + Length of input sequence in logits. + logits_time_major: (optional) If True (default), logits is shaped + [time, batch, logits]. If False, shape is [batch, time, logits] + unique: (optional) Unique label indices as computed by unique(labels). + If supplied, enable a faster, memory efficient implementation on TPU. + blank_index: (optional) Set the class index to use for the blank label. + Negative values will start from num_classes, ie, -1 will reproduce the + ctc_loss behavior of using num_classes - 1 for the blank symbol. + There is some memory/performance overhead to switching from the default + of 0 as an additional shifted copy of the logits may be created. + name: A name for this `Op`. Defaults to "ctc_loss_dense". + + Returns: + loss: tensor of shape [batch_size], negative log probabilities. + """ + + with ops.name_scope(name, "ctc_loss_dense", + [logits, labels, label_length, logit_length]): + logits = ops.convert_to_tensor(logits, name="logits") + labels = ops.convert_to_tensor(labels, name="labels") + label_length = ops.convert_to_tensor(label_length, name="label_length") + logit_length = ops.convert_to_tensor(logit_length, name="logit_length") + + if not logits_time_major: + logits = array_ops.transpose(logits, perm=[1, 0, 2]) + + if blank_index != 0: + if blank_index < 0: + blank_index += _get_dim(logits, 2) + logits = array_ops.concat([ + logits[:, :, blank_index:blank_index+1], + logits[:, :, :blank_index], + logits[:, :, blank_index+1:], + ], axis=2) + labels = array_ops.where(labels < blank_index, labels + 1, labels) + + args = [logits, labels, label_length, logit_length] + + if unique: + unique_y, unique_idx = unique + args.extend([unique_y, unique_idx]) + + # TODO(tombagby): Update to tfe.defun + @function.Defun(*[x.dtype for x in args], + python_grad_func=_ctc_loss_grad, + shape_func=_ctc_loss_shape) + def compute_ctc_loss(logits_t, labels_t, label_length_t, logit_length_t, + *unique_t): + """Compute CTC loss.""" + logits_t.set_shape(logits.shape) + labels_t.set_shape(labels.shape) + label_length_t.set_shape(label_length.shape) + logit_length_t.set_shape(logit_length.shape) + kwargs = dict( + logits=logits_t, + labels=labels_t, + label_length=label_length_t, + logit_length=logit_length_t) + if unique_t: + kwargs["unique"] = unique_t + return ctc_loss_and_grad(**kwargs) + + return compute_ctc_loss(*args)[0] + + +@tf_export("nn.collapse_repeated") +def collapse_repeated(labels, seq_length, name=None): + """Merge repeated labels into single labels. + + Args: + labels: Tensor of shape (batch, max value in seq_length) + seq_length: Tensor of shape (batch), sequence length of each batch element. + name: A name for this `Op`. Defaults to "collapse_repeated_labels". + + Returns: + tuple of Tensor of shape (batch, max_seq_length) with repeated labels + collapsed and padded to max_seq_length, eg: + [[A, A, B, B, A], + [A, B, C, D, E]] => [[A, B, A, 0, 0], + [A, B, C, D, E]] + and int tensor of shape [batch] with new sequence lengths. + """ + + with ops.name_scope(name, "collapse_repeated_labels", + [labels, seq_length]): + labels = ops.convert_to_tensor(labels, name="labels") + seq_length = ops.convert_to_tensor(seq_length, name="seq_length") + + # Mask labels that don't equal previous label. + label_mask = array_ops.concat( + [array_ops.ones_like(labels[:, :1], dtypes.bool), + math_ops.not_equal(labels[:, 1:], labels[:, :-1])], + axis=1) + + # Filter labels that aren't in the original sequence. + maxlen = _get_dim(labels, 1) + seq_mask = array_ops.sequence_mask(seq_length, maxlen=maxlen) + label_mask = math_ops.logical_and(label_mask, seq_mask) + + # Count masks for new sequence lengths. + new_seq_len = math_ops.reduce_sum( + math_ops.cast(label_mask, dtypes.int32), axis=1) + + # Mask indexes based on sequence length mask. + new_maxlen = math_ops.reduce_max(new_seq_len) + idx_mask = array_ops.sequence_mask(new_seq_len, maxlen=new_maxlen) + + # Flatten everything and mask out labels to keep and sparse indices. + flat_labels = array_ops.reshape(labels, [-1]) + flat_label_mask = array_ops.reshape(label_mask, [-1]) + flat_idx_mask = array_ops.reshape(idx_mask, [-1]) + idx = math_ops.range(_get_dim(flat_idx_mask, 0)) + + # Scatter to flat shape. + flat = array_ops.scatter_nd( + indices=array_ops.expand_dims( + array_ops.boolean_mask(idx, flat_idx_mask), axis=1), + updates=array_ops.boolean_mask(flat_labels, flat_label_mask), + shape=array_ops.shape(flat_idx_mask)) + + # Reshape back to square batch. + batch_size = _get_dim(labels, 0) + new_shape = [batch_size, new_maxlen] + return (array_ops.reshape(flat, new_shape), + math_ops.cast(new_seq_len, seq_length.dtype)) + + +def dense_labels_to_sparse(dense, length): + """Convert dense labels with sequence lengths to sparse tensor. + + Args: + dense: tensor of shape [batch, max_length] + length: int tensor of shape [batch] + The length of each sequence in dense. + + Returns: + tf.SparseTensor with values only for the valid elements of sequences. + """ + + flat_values = array_ops.reshape(dense, [-1]) + flat_indices = math_ops.range( + array_ops.shape(flat_values, out_type=dtypes.int64)[0]) + mask = array_ops.sequence_mask(length, maxlen=array_ops.shape(dense)[1]) + flat_mask = array_ops.reshape(mask, [-1]) + indices = array_ops.expand_dims( + array_ops.boolean_mask(flat_indices, flat_mask), 1) + values = array_ops.boolean_mask(flat_values, flat_mask) + sparse = sparse_tensor.SparseTensor( + indices=indices, values=math_ops.cast(values, dtypes.int32), + dense_shape=array_ops.shape(flat_values, out_type=dtypes.int64)) + reshaped = sparse_ops.sparse_reshape(sparse, array_ops.shape(dense)) + max_length = math_ops.reduce_max(length) + return sparse_tensor.SparseTensor( + indices=reshaped.indices, + values=reshaped.values, + dense_shape=[ + math_ops.cast(reshaped.dense_shape[0], dtypes.int64), + math_ops.cast(max_length, dtypes.int64)]) + + +@tf_export("nn.ctc_unique_labels") +def ctc_unique_labels(labels, name=None): + """Get unique labels and indices for batched labels for tf.nn.ctc_loss. + + For use with tf.nn.ctc_loss_v2 optional argument `unique`: This op can be + used to preprocess labels in input pipeline to for better speed/memory use + computing the ctc loss on TPU. + + Example: + ctc_unique_labels([[3, 4, 4, 3]]) -> + unique labels padded with 0: [[3, 4, 0, 0]] + indices of original labels in unique: [0, 1, 1, 0] + + Args: + labels: tensor of shape [batch_size, max_label_length] padded with 0. + name: A name for this `Op`. Defaults to "ctc_unique_labels". + + Returns: + tuple of + - unique labels, tensor of shape `[batch_size, max_label_length]` + - indices into unique labels, shape `[batch_size, max_label_length]` + """ + + with ops.name_scope(name, "ctc_unique_labels", [labels]): + labels = ops.convert_to_tensor(labels, name="labels") + def _unique(x): + u = array_ops.unique(x) + y = array_ops.pad( + u.y, [[0, _get_dim(u.idx, 0) - _get_dim(u.y, 0)]]) + y = math_ops.cast(y, dtypes.int64) + return [y, u.idx] + return functional_ops.map_fn( + _unique, labels, dtype=[dtypes.int64, dtypes.int32]) + + +def _sum_states(idx, states): + """Take logsumexp for each unique state out of all label states. + + Args: + idx: tensor of shape [batch, label_length] + For each sequence, indices into a set of unique labels as computed by + calling unique. + states: tensor of shape [frames, batch, label_length] + Log probabilities for each label state. + + Returns: + tensor of shape [frames, batch_size, label_length], log probabilites summed + for each unique label of the sequence. + """ + + with ops.name_scope("sum_states"): + idx = ops.convert_to_tensor(idx, name="idx") + num_states = _get_dim(states, 2) + states = array_ops.expand_dims(states, axis=2) + one_hot = array_ops.one_hot( + idx, depth=num_states, on_value=0.0, off_value=math_ops.log(0.0), + axis=1) + return math_ops.reduce_logsumexp(states + one_hot, axis=-1) + + +def _forward_backward_log(state_trans_log_probs, initial_state_log_probs, + final_state_log_probs, observed_log_probs, + sequence_length): + """Forward-backward algorithm computed in log domain. + + Args: + state_trans_log_probs: tensor of shape [states, states] or + if different transition matrix per batch [batch_size, states, states] + initial_state_log_probs: tensor of shape [batch_size, states] + final_state_log_probs: tensor of shape [batch_size, states] + observed_log_probs: tensor of shape [frames, batch_size, states] + sequence_length: tensor of shape [batch_size] + + Returns: + forward backward log probabilites: tensor of shape [frames, batch, states] + log_likelihood: tensor of shape [batch_size] + + Raises: + ValueError: If state_trans_log_probs has unknown or incorrect rank. + """ + + if state_trans_log_probs.shape.ndims == 2: + perm = [1, 0] + elif state_trans_log_probs.shape.ndims == 3: + perm = [0, 2, 1] + else: + raise ValueError( + "state_trans_log_probs rank must be known and == 2 or 3, is: %s" % + state_trans_log_probs.shape.ndims) + + bwd_state_trans_log_probs = array_ops.transpose(state_trans_log_probs, perm) + batch_size = _get_dim(observed_log_probs, 1) + + def _forward(state_log_prob, obs_log_prob): + state_log_prob = array_ops.expand_dims(state_log_prob, axis=1) # Broadcast. + state_log_prob += state_trans_log_probs + state_log_prob = math_ops.reduce_logsumexp(state_log_prob, axis=-1) + state_log_prob += obs_log_prob + log_prob_sum = math_ops.reduce_logsumexp( + state_log_prob, axis=-1, keepdims=True) + state_log_prob -= log_prob_sum + return state_log_prob + + fwd = _scan(_forward, observed_log_probs, initial_state_log_probs, + inclusive=True) + + def _backward(accs, elems): + """Calculate log probs and cumulative sum masked for sequence length.""" + state_log_prob, cum_log_sum = accs + obs_log_prob, mask = elems + state_log_prob += obs_log_prob + state_log_prob = array_ops.expand_dims(state_log_prob, axis=1) # Broadcast. + state_log_prob += bwd_state_trans_log_probs + state_log_prob = math_ops.reduce_logsumexp(state_log_prob, axis=-1) + + log_prob_sum = math_ops.reduce_logsumexp( + state_log_prob, axis=-1, keepdims=True) + state_log_prob -= log_prob_sum + + cum_log_sum += array_ops.squeeze(log_prob_sum) * mask + batched_mask = array_ops.expand_dims(mask, axis=1) + out = state_log_prob * batched_mask + out += final_state_log_probs * (1.0 - batched_mask) + return out, cum_log_sum + + zero_log_sum = array_ops.zeros([batch_size]) + maxlen = _get_dim(observed_log_probs, 0) + mask = array_ops.sequence_mask(sequence_length, maxlen, dtypes.float32) + mask = array_ops.transpose(mask, perm=[1, 0]) + + bwd, cum_log_sum = _scan(_backward, (observed_log_probs, mask), + (final_state_log_probs, zero_log_sum), + reverse=True, inclusive=True) + + fwd_bwd_log_probs = fwd[1:] + bwd[1:] + fwd_bwd_log_probs_sum = math_ops.reduce_logsumexp( + fwd_bwd_log_probs, axis=2, keepdims=True) + fwd_bwd_log_probs -= fwd_bwd_log_probs_sum + fwd_bwd_log_probs += math_ops.log(array_ops.expand_dims(mask, axis=2)) + + log_likelihood = bwd[0, :, 0] + cum_log_sum[0] + + return fwd_bwd_log_probs, log_likelihood + + +# TODO(tombagby): This is currently faster for the ctc implementation than using +# functional_ops.scan, but could be replaced by that or something similar if +# things change. +def _scan(fn, elems, initial, reverse=False, inclusive=False, final_only=False): + """Repeatedly applies callable `fn` to a sequence of elements. + + Implemented by functional_ops.While, tpu friendly, no gradient. + + This is similar to functional_ops.scan but significantly faster on tpu/gpu + for the forward backward use case. + + Examples: + scan(lambda a, e: a + e, [1.0, 2.0, 3.0], 1.0) => [2.0, 3.0, 4.0] + + Multiple accumulators: + scan(lambda a, e: (a[0] + e, a[1] * e), [1.0, 2.0, 3.0], (0.0, 1.0)) + + Multiple inputs: + scan(lambda a, e: a + (e[0] * e[1]), (elems1, elems2), 0.0) + + Args: + fn: callable, fn(accumulators, element) return new accumulator values. + The (possibly nested) sequence of accumulators is the same as `initial` + and the return value must have the same structure. + elems: A (possibly nested) tensor which will be unpacked along the first + dimension. The resulting slices will be the second argument to fn. The + first dimension of all nested input tensors must be the same. + initial: A tensor or (possibly nested) sequence of tensors with initial + values for the accumulators. + reverse: (optional) True enables scan and output elems in reverse order. + inclusive: (optional) True includes the initial accumulator values in the + output. Length of output will be len(elem sequence) + 1. Not meaningful + if final_only is True. + final_only: (optional) When True, return only the final accumulated values, + not the concatenation of accumulated values for each input. + + Returns: + A (possibly nested) sequence of tensors with the results of applying fn + to tensors unpacked from elems and previous accumulator values. + """ + + flat_elems = [ops.convert_to_tensor(x) for x in nest.flatten(elems)] + num_elems = array_ops.shape(flat_elems[0])[0] + pack_elems = lambda x: nest.pack_sequence_as(structure=elems, flat_sequence=x) + flat_initial = [ops.convert_to_tensor(x) for x in nest.flatten(initial)] + pack = lambda x: nest.pack_sequence_as(structure=initial, flat_sequence=x) + accum_dtypes = [x.dtype for x in flat_initial] + num_accums = len(flat_initial) + + # Types for counter, [outputs], [accumulators] loop arguments. + if final_only: + loop_dtypes = [dtypes.int32, dtypes.int32] + accum_dtypes + else: + loop_dtypes = [dtypes.int32, dtypes.int32] + accum_dtypes + accum_dtypes + + # TODO(tombagby): Update to tfe.defun + @function.Defun(*loop_dtypes) + def cond(i, num_elems, *args): + del args + return i >= 0 if reverse else i < num_elems + + # The loop *args are [output tensors] + [accumulator tensors] which must + # be paired. Each output corresponds to one accumulator. + @function.Defun(*loop_dtypes) + def body(i, num_elems, *args): + """Loop body.""" + i.set_shape([]) + if final_only: + accum = args + else: + out, accum = args[:num_accums], args[num_accums:] + slices = [array_ops.gather(e, i) for e in flat_elems] + accum = fn(pack(accum), pack_elems(slices)) + flat_accum = nest.flatten(accum) + if final_only: + new_out = [] + else: + update_i = i + 1 if inclusive and not reverse else i + new_out = [inplace_ops.alias_inplace_update(x, update_i, y) + for x, y in zip(out, flat_accum)] + i = i - 1 if reverse else i + 1 + return [i, num_elems] + new_out + flat_accum + + init_i = (array_ops.shape(flat_elems[0])[0] - 1 if reverse + else constant_op.constant(0, dtype=dtypes.int32)) + outputs = [] + if not final_only: + num_outputs = array_ops.shape(flat_elems[0])[0] + (1 if inclusive else 0) + for initial_accum in flat_initial: + out_shape = array_ops.concat( + [[num_outputs], array_ops.shape(initial_accum)], 0) + out = inplace_ops.empty(out_shape, dtype=initial_accum.dtype, init=True) + if inclusive: + out = inplace_ops.alias_inplace_add( + out, init_i + (1 if reverse else 0), initial_accum) + outputs.append(out) + loop_in = [init_i, num_elems] + outputs + flat_initial + hostmem = [ + i for i, x in enumerate(loop_in) + if x.dtype.base_dtype in (dtypes.int32, dtypes.int64) + ] + + # TODO(tombagby): Update to while_v2. + loop_results = functional_ops.While(loop_in, cond, body, hostmem=hostmem) + out = loop_results[2:num_accums + 2] + return pack(out) + + +def _get_dim(tensor, i): + """Get value of tensor shape[i] preferring static value if available.""" + return tensor.shape[i].value or array_ops.shape(tensor)[i] diff --git a/tensorflow/python/ops/data_flow_ops.py b/tensorflow/python/ops/data_flow_ops.py index cca8e12b43..2030332e4e 100644 --- a/tensorflow/python/ops/data_flow_ops.py +++ b/tensorflow/python/ops/data_flow_ops.py @@ -79,7 +79,7 @@ def _as_shape_list(shapes, shapes = [shapes] shapes = [tensor_shape.as_shape(shape) for shape in shapes] if not unknown_dim_allowed: - if any([not shape.is_fully_defined() for shape in shapes]): + if any(not shape.is_fully_defined() for shape in shapes): raise ValueError("All shapes must be fully defined: %s" % shapes) if not unknown_rank_allowed: if any([shape.dims is None for shape in shapes]): @@ -171,7 +171,10 @@ class QueueBase(object): self._names = None self._queue_ref = queue_ref if context.executing_eagerly(): - self._name = context.context().scope_name + if context.context().scope_name: + self._name = context.context().scope_name + else: + self._name = "Empty" self._resource_deleter = resource_variable_ops.EagerResourceDeleter( queue_ref, None) else: @@ -198,11 +201,11 @@ class QueueBase(object): raise TypeError("A list of queues expected") dtypes = queues[0].dtypes - if not all([dtypes == q.dtypes for q in queues[1:]]): + if not all(dtypes == q.dtypes for q in queues[1:]): raise TypeError("Queues do not have matching component dtypes.") names = queues[0].names - if not all([names == q.names for q in queues[1:]]): + if not all(names == q.names for q in queues[1:]): raise TypeError("Queues do not have matching component names.") queue_shapes = [q.shapes for q in queues] @@ -1148,7 +1151,7 @@ class Barrier(object): self._barrier_ref, name=name) -@tf_export("ConditionalAccumulatorBase") +@tf_export(v1=["ConditionalAccumulatorBase"]) class ConditionalAccumulatorBase(object): """A conditional accumulator for aggregating gradients. @@ -1227,7 +1230,7 @@ class ConditionalAccumulatorBase(object): name=name) -@tf_export("ConditionalAccumulator") +@tf_export(v1=["ConditionalAccumulator"]) class ConditionalAccumulator(ConditionalAccumulatorBase): """A conditional accumulator for aggregating gradients. diff --git a/tensorflow/python/ops/dequantize_op_test.py b/tensorflow/python/ops/dequantize_op_test.py index 13e50273d8..794985b2db 100644 --- a/tensorflow/python/ops/dequantize_op_test.py +++ b/tensorflow/python/ops/dequantize_op_test.py @@ -35,7 +35,7 @@ class DequantizeOpTest(test.TestCase): with self.cached_session(): input_op = constant_op.constant(inputs, shape=[len(inputs)], dtype=dtype) dequantized = array_ops.dequantize(input_op, min_range, max_range) - tf_ans = dequantized.eval() + tf_ans = self.evaluate(dequantized) # TODO(vrv): Add support for DT_QINT32 quantization if needed. type_dict = { diff --git a/tensorflow/python/ops/distributions/util.py b/tensorflow/python/ops/distributions/util.py index 760e7a8a84..24314e8fc9 100644 --- a/tensorflow/python/ops/distributions/util.py +++ b/tensorflow/python/ops/distributions/util.py @@ -343,7 +343,7 @@ def embed_check_categorical_event_shape( x_dtype = x.dtype.base_dtype max_event_size = (_largest_integer_by_dtype(x_dtype) if x_dtype.is_floating else 0) - if max_event_size is 0: + if max_event_size == 0: raise TypeError("Unable to validate size of unrecognized dtype " "({}).".format(x_dtype.name)) try: diff --git a/tensorflow/python/ops/embedding_ops.py b/tensorflow/python/ops/embedding_ops.py index 9ce024ad96..b398601e6f 100644 --- a/tensorflow/python/ops/embedding_ops.py +++ b/tensorflow/python/ops/embedding_ops.py @@ -554,7 +554,10 @@ def safe_embedding_lookup_sparse(embedding_weights, dtype = sparse_weights.dtype if sparse_weights is not None else None embedding_weights = [ - ops.convert_to_tensor(w, dtype=dtype) for w in embedding_weights + w if (isinstance(w, resource_variable_ops.ResourceVariable) + and dtype in (None, w.dtype)) + else ops.convert_to_tensor(w, dtype=dtype) + for w in embedding_weights ] with ops.name_scope(name, 'embedding_lookup', diff --git a/tensorflow/python/ops/functional_ops.py b/tensorflow/python/ops/functional_ops.py index fecd7ddbf9..57542e3c7b 100644 --- a/tensorflow/python/ops/functional_ops.py +++ b/tensorflow/python/ops/functional_ops.py @@ -19,7 +19,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -from tensorflow.core.protobuf import rewriter_config_pb2 +from tensorflow.core.protobuf import config_pb2 from tensorflow.core.framework import attr_value_pb2 from tensorflow.python.eager import context from tensorflow.python.framework import constant_op @@ -1027,9 +1027,10 @@ _rewriter_config_optimizer_disabled = None def _get_disabled_rewriter_config(): global _rewriter_config_optimizer_disabled if _rewriter_config_optimizer_disabled is None: - rewriter_config = rewriter_config_pb2.RewriterConfig() + config = config_pb2.ConfigProto() + rewriter_config = config.graph_options.rewrite_options rewriter_config.disable_meta_optimizer = True - _rewriter_config_optimizer_disabled = rewriter_config.SerializeToString() + _rewriter_config_optimizer_disabled = config.SerializeToString() return _rewriter_config_optimizer_disabled @@ -1048,7 +1049,7 @@ def partitioned_call(args, f, tout=None, executing_eagerly=None, config=None, the signature of `f`. executing_eagerly: (Optional) A boolean indicating whether the context is executing eagerly. If `None`, fetched from the global context. - config: (Optional) A tensorflow::RewriterConfig proto, serialized. If + config: (Optional) A `tensorflow::ConfigProto` proto, serialized. If `None`, all optimizations are disabled. Currently only handled for eager defined functions. executor_type: (Optional) A string for the name of the executor to be used @@ -1076,10 +1077,12 @@ def partitioned_call(args, f, tout=None, executing_eagerly=None, config=None, if executing_eagerly or len(tout): if f.stateful_ops: outputs = gen_functional_ops.stateful_partitioned_call( - args=args, Tout=tout, f=f, config=config, executor_type=executor_type) + args=args, Tout=tout, f=f, config_proto=config, + executor_type=executor_type) else: outputs = gen_functional_ops.partitioned_call( - args=args, Tout=tout, f=f, config=config, executor_type=executor_type) + args=args, Tout=tout, f=f, config_proto=config, + executor_type=executor_type) return outputs if outputs else None # The generated binding returns an empty list for functions that don't @@ -1098,7 +1101,7 @@ def partitioned_call(args, f, tout=None, executing_eagerly=None, config=None, # When running in graph mode, the graph and function graphs are optimized # (i.e. run through grappler) per the session options, so we can disable any # eager-specific rewriting. - rewriter_config = attr_value_pb2.AttrValue(s=_get_disabled_rewriter_config()) + config_proto = attr_value_pb2.AttrValue(s=_get_disabled_rewriter_config()) graph = ops.get_default_graph() f.add_to_graph(graph) @@ -1113,7 +1116,7 @@ def partitioned_call(args, f, tout=None, executing_eagerly=None, config=None, "Tin": tin_attr, "Tout": tout_attr, "f": func_attr, - "config": rewriter_config, + "config_proto": config_proto, "executor_type": executor_type_attr, }) outputs = op.outputs diff --git a/tensorflow/python/ops/gradient_checker.py b/tensorflow/python/ops/gradient_checker.py index 1665219c80..683f78ce9b 100644 --- a/tensorflow/python/ops/gradient_checker.py +++ b/tensorflow/python/ops/gradient_checker.py @@ -31,7 +31,6 @@ from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradients from tensorflow.python.ops import math_ops from tensorflow.python.platform import tf_logging as logging -from tensorflow.python.util import deprecation from tensorflow.python.util.tf_export import tf_export @@ -158,7 +157,8 @@ def _compute_numeric_jacobian(x, x_shape, x_data, y, y_shape, delta, # as delta. Convert to float32 here. Since numeric_jacobian is expected to # be the groundtruth to compare against, it shouldn't lose any information. if x.dtype == dtypes.bfloat16: - x = math_ops.cast(x, dtypes.float32) + x = math_ops.cast(x, dtypes.float32) # TODO(wangpeng): Now that the new x + # is an output of the old x, isn't feeding to the new x a mistake? if y.dtype == dtypes.bfloat16: y = math_ops.cast(y, dtypes.float32) if x_data.dtype == dtypes.bfloat16.as_numpy_dtype: @@ -266,7 +266,7 @@ def _compute_gradient_list(x, return ret -@tf_export("test.compute_gradient") +@tf_export(v1=["test.compute_gradient"]) def compute_gradient(x, x_shape, y, @@ -301,7 +301,6 @@ def compute_gradient(x, as the initial value. delta: (optional) the amount of perturbation. init_targets: list of targets to run to initialize model params. - TODO(mrry): remove this argument. extra_feed_dict: dict that allows fixing specified tensor values during the Jacobian calculation. @@ -311,6 +310,7 @@ def compute_gradient(x, where "x_size" is the number of elements in x and "y_size" is the number of elements in y. If x is a list, returns a list of two numpy arrays. """ + # TODO(mrry): remove argument `init_targets` if extra_feed_dict is None: extra_feed_dict = {} @@ -328,10 +328,17 @@ def compute_gradient(x, return ret +def _compute_error(grad): + if isinstance(grad, tuple): + grad = [grad] + error = 0 + for j_t, j_n in grad: + if j_t.size or j_n.size: # Handle zero size tensors correctly + error = np.maximum(error, np.fabs(j_t - j_n).max()) + return error + + @tf_export(v1=["test.compute_gradient_error"]) -@deprecation.deprecated_args( - None, "init_targets will be deprecated in TensorFlow 2.0", - ("init_targets", None)) # Do not trigger warning in V2 def compute_gradient_error(x, x_shape, y, @@ -373,59 +380,4 @@ def compute_gradient_error(x, """ grad = compute_gradient(x, x_shape, y, y_shape, x_init_value, delta, init_targets, extra_feed_dict=extra_feed_dict) - if isinstance(grad, tuple): - grad = [grad] - error = 0 - for j_t, j_n in grad: - if j_t.size or j_n.size: # Handle zero size tensors correctly - error = np.maximum(error, np.fabs(j_t - j_n).max()) - return error - - -@tf_export("test.compute_gradient_error", v1=[]) -def compute_gradient_error_v2(x, - x_shape, - y, - y_shape, - x_init_value=None, - delta=1e-3, - extra_feed_dict=None): - """Computes the gradient error. - - Computes the maximum error for dy/dx between the computed Jacobian and the - numerically estimated Jacobian. - - This function will modify the tensors passed in as it adds more operations - and hence changing the consumers of the operations of the input tensors. - - This function adds operations to the current session. To compute the error - using a particular device, such as a GPU, use the standard methods for - setting a device (e.g. using with sess.graph.device() or setting a device - function in the session constructor). - - Args: - x: a tensor or list of tensors - x_shape: the dimensions of x as a tuple or an array of ints. If x is a list, - then this is the list of shapes. - y: a tensor - y_shape: the dimensions of y as a tuple or an array of ints. - x_init_value: (optional) a numpy array of the same shape as "x" representing - the initial value of x. If x is a list, this should be a list of numpy - arrays. If this is none, the function will pick a random tensor as the - initial value. - delta: (optional) the amount of perturbation. - extra_feed_dict: dict that allows fixing specified tensor values during the - Jacobian calculation. - - Returns: - The maximum error in between the two Jacobians. - """ - return compute_gradient_error( - x, - x_shape, - y, - y_shape, - x_init_value=x_init_value, - delta=delta, - init_targets=None, - extra_feed_dict=extra_feed_dict) + return _compute_error(grad) diff --git a/tensorflow/python/ops/gradient_checker_test.py b/tensorflow/python/ops/gradient_checker_test.py index 66c7b9a71b..4d2b5efac7 100644 --- a/tensorflow/python/ops/gradient_checker_test.py +++ b/tensorflow/python/ops/gradient_checker_test.py @@ -23,6 +23,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import math_ops @@ -46,6 +47,7 @@ def _nan_grad(unused_op, grad): class GradientCheckerTest(test.TestCase): + @test_util.run_deprecated_v1 def testAddSimple(self): np.random.seed(1) # Fix seed to avoid flakiness with self.session(use_gpu=False): @@ -60,6 +62,7 @@ class GradientCheckerTest(test.TestCase): tf_logging.info("x1 error = %f", error) assert error < 1e-4 + @test_util.run_deprecated_v1 def testAddSimpleGPU(self): np.random.seed(2) # Fix seed to avoid flakiness with self.session(use_gpu=True): @@ -74,6 +77,7 @@ class GradientCheckerTest(test.TestCase): tf_logging.info("x1 error = %f", error) assert error < 1e-4 + @test_util.run_deprecated_v1 def testAddCustomized(self): np.random.seed(3) # Fix seed to avoid flakiness with self.cached_session(): @@ -92,6 +96,7 @@ class GradientCheckerTest(test.TestCase): tf_logging.info("x2 error = %f", error) assert error < 1e-10 + @test_util.run_deprecated_v1 def testGather(self): np.random.seed(4) # Fix seed to avoid flakiness with self.cached_session(): @@ -109,6 +114,7 @@ class GradientCheckerTest(test.TestCase): tf_logging.info("gather error = %f", error) assert error < 1e-4 + @test_util.run_deprecated_v1 def testNestedGather(self): np.random.seed(5) # Fix seed to avoid flakiness with self.cached_session(): @@ -130,6 +136,7 @@ class GradientCheckerTest(test.TestCase): tf_logging.info("nested gather error = %f", error) assert error < 1e-4 + @test_util.run_deprecated_v1 def testComplexMul(self): with self.cached_session(): size = () @@ -144,6 +151,7 @@ class GradientCheckerTest(test.TestCase): self.assertLess( gradient_checker.compute_gradient_error(x, size, y, size), 2e-4) + @test_util.run_deprecated_v1 def testComplexConj(self): with self.cached_session(): size = () @@ -157,6 +165,7 @@ class GradientCheckerTest(test.TestCase): self.assertLess( gradient_checker.compute_gradient_error(x, size, y, size), 2e-5) + @test_util.run_deprecated_v1 def testEmptySucceeds(self): with self.cached_session(): x = array_ops.placeholder(dtypes.float32) @@ -279,18 +288,23 @@ class MiniMNISTTest(test.TestCase): tf_logging.info("Mini MNIST: %s gradient error = %g", tag, err) return err + @test_util.run_deprecated_v1 def testInputGradient(self): self.assertLess(self._BuildAndTestMiniMNIST(0, "input"), 1e-8) + @test_util.run_deprecated_v1 def testHiddenWeightGradient(self): self.assertLess(self._BuildAndTestMiniMNIST(1, "hidden_weight"), 1e-8) + @test_util.run_deprecated_v1 def testHiddenBiasGradient(self): self.assertLess(self._BuildAndTestMiniMNIST(2, "hidden_bias"), 1e-8) + @test_util.run_deprecated_v1 def testSoftmaxWeightGradient(self): self.assertLess(self._BuildAndTestMiniMNIST(3, "softmax_weight"), 1e-8) + @test_util.run_deprecated_v1 def testSoftmaxBiasGradient(self): self.assertLess(self._BuildAndTestMiniMNIST(4, "softmax_bias"), 1e-8) diff --git a/tensorflow/python/ops/gradient_checker_v2.py b/tensorflow/python/ops/gradient_checker_v2.py new file mode 100644 index 0000000000..cf84484112 --- /dev/null +++ b/tensorflow/python/ops/gradient_checker_v2.py @@ -0,0 +1,318 @@ +# Copyright 2015 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. +# ============================================================================== + +"""Gradient checker for functions. + +The gradient checker verifies numerically that an function properly +computes the gradients +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +from tensorflow.python.eager import backprop +from tensorflow.python.eager import context +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.platform import tf_logging as logging +from tensorflow.python.util.tf_export import tf_export + + +def _product(t): + if isinstance(t, int): + return t + else: + y = 1 + for x in t: + y *= x + return y + + +def _to_numpy(a): + """Converts tensors to numpy arrays. + + Converts Tensors and EagerTensors to numpy arrays. + When eager execution is enabled, converts IndexedSlices + to IndexedSlicesValue with numpy indices/values + + Args: + a: any value. + + Returns: + If a is EagerTensor or Tensor, returns the evaluation of a by calling + numpy() or run(). + If a is IndexedSlices and eager execution is enabled, calls numpy() on a's + fields. Otherwise returns a unchanged. + """ + if isinstance(a, ops.EagerTensor): + return a.numpy() + if isinstance(a, ops.Tensor): + sess = ops.get_default_session() + return sess.run(a) + if isinstance(a, ops.IndexedSlices) and context.executing_eagerly(): + return ops.IndexedSlicesValue( + indices=[x.numpy() for x in a.indices], + values=[x.numpy() for x in a.values], + dense_shape=a.dense_shape) + return a + + +def _prepare(f, xs_dtypes): + """Return a function that executes 'f'. + + In TF 2.x, this is the same as `f`. + In TF 1.x, returns a Python function that executes the graph defined by `f` + in a Session. + + Args: + f: the function. + xs_dtypes: dtypes of f's arguments. + + Returns: + a function that will be evaluated in both graph and eager mode + """ + if context.executing_eagerly(): + + def decorated_eager(*xs_data): + return f(*map(ops.convert_to_tensor, xs_data)) + + return decorated_eager + xs = [array_ops.placeholder(x_dtype) for x_dtype in xs_dtypes] + y = f(*xs) + sess = ops.get_default_session() + def decorated_graph(*xs_data): + xs_data = [_to_numpy(a) for a in xs_data] + return sess.run(y, feed_dict=dict(zip(xs, xs_data))) + return decorated_graph + + +def _compute_theoretical_jacobian(f, y_shape, y_dtype, xs, param): + """Computes the theoretical Jacobian for f regarding xs[param]. + + One can think of the relation among f, xs and y as y = f(xs). + + Args: + f: the function. + y_shape: the shape of the result. + y_dtype: the dtype of the result. + xs: a list of tensors. + param: the index of the target parameter. + + Returns: + A 2-d numpy array representing the Jacobian. It has "x_size" rows + and "y_size" columns where "x_size" is the number of elements in xs[param] + and "y_size" is the number of elements in the result. + + Raises: + ValueError: If result is empty but the gradient is nonzero. + """ + x = xs[param] + # Complex vectors are treated as vectors of twice as many reals. + x_shape = tuple(x.shape) + (2,) if x.dtype.is_complex else x.shape + y_factor = 2 if y_dtype.is_complex else 1 + + # To compute the jacobian, we treat x and y as one-dimensional vectors. + x_size = _product(x_shape) + x_val_size = _product(x_shape[1:]) # This is used for sparse gradients + y_size = _product(y_shape) * y_factor + + # Allocate 2-D Jacobian, with x dimensions smashed into the first + # dimension and y dimensions smashed into the second. + jacobian = np.zeros((x_size, y_size), dtype=x.dtype.real_dtype.as_numpy_dtype) + + # For each of the entry of dy, we set this to be 1 and + # everything else to be 0 and compute the gradients -- this will give us one + # one column of the Jacobian matrix. + dy_data = np.zeros(y_shape, dtype=y_dtype.as_numpy_dtype) + dy_data_flat = dy_data.ravel().view(y_dtype.real_dtype.as_numpy_dtype) + grad_fn_unprep = backprop.gradients_function(f, [param]) + grad_fn = _prepare(lambda dy, *xs: grad_fn_unprep(*xs, dy=dy), + [y_dtype] + [x.dtype for x in xs]) + for col in range(y_size): + dy_data_flat[col] = 1 + grad = _to_numpy(grad_fn(dy_data, *xs)[0]) + dy_data_flat[col] = 0 + if isinstance(grad, ops.IndexedSlicesValue): + for i, v in zip(grad.indices, grad.values): + r_begin = i * x_val_size + r_end = r_begin + x_val_size + jacobian[r_begin:r_end, col] += v.flat + else: + jacobian[:, col] = grad.ravel().view(jacobian.dtype) + + # If the output is empty, run the gradients at least once and make sure + # they produce zeros. + if y_size == 0: # don't use 'not y_size', because y_size may not be an int + grad = _to_numpy(grad_fn(dy_data, *xs)[0]) + if grad.shape != x.shape: + raise ValueError("Empty gradient has wrong shape: expected %s, got %s" % + (x.shape, grad.shape)) + if np.any(grad): + raise ValueError("Empty tensor with nonzero gradients") + + logging.vlog(1, "Theoretical Jacobian =\n%s", jacobian) + return jacobian + + +def _compute_numeric_jacobian(f, y_size, y_dtype, xs, param, + delta): + """Computes the numeric Jacobian for f regarding xs[param]. + + One can think of the relation among f, xs and y as y = f(xs). + + Args: + f: the function. + y_size: the number of elements of the result. + y_dtype: the dtype of the result. + xs: a list of tensors. + param: the index of the target parameter. + delta: the amount of perturbation we give to the input. + + Returns: + A 2-d numpy array representing the Jacobian. It has "x_size" rows + and "y_size" columns where "x_size" is the number of elements in xs[param] + and "y_size" is the number of elements in the result. + """ + # bfloat16 doesn't have enough bits to represent high precision numbers such + # as delta. Convert to float32 here. Since numeric_jacobian is expected to + # be the groundtruth to compare against, it shouldn't lose any information. + x_shape = xs[param].shape + x_dtype = xs[param].dtype + if y_dtype == dtypes.bfloat16: + f = lambda *xs: math_ops.cast(f(*xs), dtypes.float32) + y_dtype = dtypes.float32 + + # To compute the jacobian, we treat x and y as one-dimensional vectors + x_size = _product(x_shape) * (2 if x_dtype.is_complex else 1) + y_size = y_size * (2 if y_dtype.is_complex else 1) + x_dtype = x_dtype.real_dtype.as_numpy_dtype + y_dtype = y_dtype.real_dtype.as_numpy_dtype + + xs_dtypes = [x.dtype for x in xs] + # Converts xs to numpy arrays to do in-place perturbation. + # Calls asarray() to avoid copying in ravel() later. + xs = [np.asarray(_to_numpy(x)) for x in xs] + x = xs[param] + + # Make sure we have the right types + scale = np.asarray(2 * delta, dtype=y_dtype)[()] + + jacobian = np.zeros((x_size, y_size), dtype=x_dtype) + # For each of the entry of x, we slightly perturbs this by adding and + # subtracting a delta and then compute difference between the outputs. This + # will give us one row of the Jacobian matrix. + + f = _prepare(f, xs_dtypes) + for row in range(x_size): + original = x.ravel().view(x_dtype)[row] + x.ravel().view(x_dtype)[row] += delta + y_pos = _to_numpy(f(*xs)) + x.ravel().view(x_dtype)[row] = original + x.ravel().view(x_dtype)[row] -= delta + y_neg = _to_numpy(f(*xs)) + x.ravel().view(x_dtype)[row] = original + diff = (y_pos - y_neg) / scale + jacobian[row, :] = diff.ravel().view(y_dtype) + + logging.vlog(1, "Numeric Jacobian =\n%s", jacobian) + return jacobian + + +def _compute_gradient(f, + y_shape, + y_dtype, + xs, + param, + delta): + """Computes the theoretical and numerical jacobian.""" + x = xs[param] + t = x.dtype + allowed_types = [dtypes.float16, dtypes.bfloat16, dtypes.float32, + dtypes.float64, dtypes.complex64, dtypes.complex128] + assert t.base_dtype in allowed_types, ("Cannot compute gradient for" + "unsupported type %s of argument %s" % + (t.name, param)) + t2 = y_dtype + assert t2.base_dtype in allowed_types, ("Cannot compute gradient for" + "unsupported type %s of y" % t2.name) + y_size = _product(y_shape) + jacob_t = _compute_theoretical_jacobian(f, y_shape, y_dtype, + xs, param) + jacob_n = _compute_numeric_jacobian(f, y_size, y_dtype, xs, + param, delta) + return jacob_t, jacob_n + + +def _compute_gradient_list(f, xs, delta): + """Compute gradients for a list of x values.""" + # convert xs to tensors so that dtype and shape have uniform types + xs = list(map(ops.convert_to_tensor, xs)) + # run the function to get info of the result + xs_dtypes = [x.dtype for x in xs] + f_temp = _prepare(f, xs_dtypes) + y = f_temp(*xs) + return zip(*[_compute_gradient(f, y.shape, dtypes.as_dtype(y.dtype), + xs, i, delta) for i in range(len(xs))]) + + +@tf_export("test.compute_gradient", v1=[]) +def compute_gradient(f, x, delta=1e-3): + """Computes the theoretical and numeric Jacobian of f. + + With y = f(x), computes the theoretical and numeric Jacobian dy/dx. + + Args: + f: the function. + x: a list of tensors. + delta: (optional) perturbation used to compute numeric Jacobian. + + Returns: + A pair of lists, where the first is a list of 2-d numpy arrays representing + the theoretical Jacobians for each argument, and the second list is the + numerical ones. Each 2-d array has "x_size" rows + and "y_size" columns where "x_size" is the number of elements in the + corresponding argument and "y_size" is the number of elements in f(x). + + Raises: + ValueError: If result is empty but the gradient is nonzero. + """ + if not isinstance(x, list): + raise ValueError( + "`x` must be a list of Tensors (arguments to `f`), not a %s" % type(x)) + return _compute_gradient_list(f, x, delta) + + +def max_error(grad1, grad2): + """Computes maximum elementwise gap. + + Computes the maximum elementwise gap between two lists of tensors of the same + shape. + + Args: + grad1: a lists of tensors. + grad2: a lists of tensors with the same shape as grad1. + + Returns: + The maximum elementwise gap between the two. + """ + error = 0 + for j_t, j_n in zip(grad1, grad2): + if j_t.size or j_n.size: # Handle zero size tensors correctly + error = np.maximum(error, np.fabs(j_t - j_n).max()) + return error diff --git a/tensorflow/python/ops/gradient_checker_v2_test.py b/tensorflow/python/ops/gradient_checker_v2_test.py new file mode 100644 index 0000000000..ce9ff47d61 --- /dev/null +++ b/tensorflow/python/ops/gradient_checker_v2_test.py @@ -0,0 +1,300 @@ +# Copyright 2015 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 compute_gradient. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +from tensorflow.python.eager import backprop +from tensorflow.python.eager import context +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import custom_gradient +from tensorflow.python.ops import \ +gradient_checker_v2 as gradient_checker +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import nn_ops +# needs this to register gradient for SoftmaxCrossEntropyWithLogits: +import tensorflow.python.ops.nn_grad # pylint: disable=unused-import +from tensorflow.python.platform import test +from tensorflow.python.platform import tf_logging + + +@test_util.run_all_in_graph_and_eager_modes +class GradientCheckerTest(test.TestCase): + + def testAddSimple(self): + # if context.executing_eagerly(): + # return + np.random.seed(1) # Fix seed to avoid flakiness + size = (2, 3) + x1 = constant_op.constant(2.0, shape=size, name="x1") + x2 = constant_op.constant(3.0, shape=size, name="x2") + error = gradient_checker.max_error(*gradient_checker.compute_gradient( + lambda x1: math_ops.add(x1, x2), [x1])) + tf_logging.info("x1 error = %f", error) + assert error < 1e-4 + + def testAddCustomized(self): + np.random.seed(3) # Fix seed to avoid flakiness + size = (2, 3) + x1 = constant_op.constant( + 2.0, shape=size, dtype=dtypes.float64, name="x1") + x2 = np.asarray(np.arange(6, dtype=np.float64).reshape(2, 3)) + # checkint gradients for x2 using a special delta + error = gradient_checker.max_error(*gradient_checker.compute_gradient( + lambda x2: math_ops.add(x1, x2), + [x2], delta=1e-2)) + tf_logging.info("x2 error = %f", error) + assert error < 1e-10 + + def testGather(self): + np.random.seed(4) # Fix seed to avoid flakiness + def f(params): + index_values = [1, 3] + indices = constant_op.constant(index_values, name="i") + return array_ops.gather(params, indices, name="y") + p_shape = (4, 2) + p_size = 8 + params = constant_op.constant( + np.arange(p_size).astype(np.float), shape=p_shape, name="p") + error = gradient_checker.max_error(*gradient_checker.compute_gradient( + f, [params])) + tf_logging.info("gather error = %f", error) + assert error < 1e-4 + + def testNestedGather(self): + np.random.seed(5) # Fix seed to avoid flakiness + def f(params): + index_values = [1, 3, 5, 6] + indices = constant_op.constant(index_values, name="i") + y = array_ops.gather(params, indices, name="y") + index_values2 = [0, 2] + indices2 = constant_op.constant(index_values2, name="i2") + return array_ops.gather(y, indices2, name="y2") + p_shape = (8, 2) + p_size = 16 + params = constant_op.constant( + np.arange(p_size).astype(np.float), shape=p_shape, name="p") + error = gradient_checker.max_error(*gradient_checker.compute_gradient( + f, [params])) + tf_logging.info("nested gather error = %f", error) + assert error < 1e-4 + + def testComplexMul(self): + if not context.executing_eagerly(): + return + c = constant_op.constant(5 + 7j, dtype=dtypes.complex64) + def f(x): + return c * x + x = constant_op.constant(11 - 13j, dtype=dtypes.complex64) + analytical, numerical = gradient_checker.compute_gradient( + f, [x], delta=0.1) + correct = np.array([[5, 7], [-7, 5]]) + self.assertAllEqual(correct, analytical[0]) + self.assertAllClose(correct, numerical[0], rtol=1e-4) + self.assertLess( + gradient_checker.max_error(*gradient_checker.compute_gradient( + f, [x], delta=0.1)), 2e-4) + + def testComplexConj(self): + def f(x): + return math_ops.conj(x) + x = constant_op.constant(11 - 13j, dtype=dtypes.complex64) + analytical, numerical = gradient_checker.compute_gradient( + f, [x], delta=0.1) + correct = np.array([[1, 0], [0, -1]]) + self.assertAllEqual(correct, analytical[0]) + self.assertAllClose(correct, numerical[0], rtol=2e-5) + self.assertLess( + gradient_checker.max_error(*gradient_checker.compute_gradient( + f, [x], delta=0.1)), 2e-5) + + def testEmptySucceeds(self): + def f(x): + return array_ops.identity(x) + x = constant_op.constant(np.random.random_sample((0, 3)), + dtype=dtypes.float32) + for grad in gradient_checker.compute_gradient(f, [x]): + self.assertEqual(grad[0].shape, (0, 0)) + error = gradient_checker.max_error(*gradient_checker.compute_gradient( + f, [x])) + self.assertEqual(error, 0) + + def testEmptyFails(self): + # if not context.executing_eagerly(): + # return + @custom_gradient.custom_gradient + def id_bad_grad(x): + y = array_ops.identity(x) + def grad_fn(dy): + # dx = constant_op.constant(np.zeros((1, 4)), dtype=dtypes.float32) + dx = array_ops.transpose(dy) + return dx + return y, grad_fn + def f(x): + return id_bad_grad(x) + x = constant_op.constant(np.random.random_sample((0, 3)), + dtype=dtypes.float32) + bad = r"Empty gradient has wrong shape: expected \(0, 3\), got \(3, 0\)" + with self.assertRaisesRegexp(ValueError, bad): + gradient_checker.compute_gradient(f, [x]) + + def testNaNGradFails(self): + @custom_gradient.custom_gradient + def id_nan_grad(x): + y = array_ops.identity(x) + def grad_fn(dy): + dx = np.nan * dy + # dx = dy + return dx + return y, grad_fn + def f(x): + return id_nan_grad(x) + x = constant_op.constant(np.random.random_sample((1, 1)), + dtype=dtypes.float32) + error = gradient_checker.max_error(*gradient_checker.compute_gradient( + f, [x])) + # Typical test would assert error < max_err, so assert this test would + # raise AssertionError, since NaN is not < 1.0. + with self.assertRaisesRegexp(AssertionError, "False is not true"): + self.assertTrue(error < 1.0) + + def testGradGrad(self): + + def f(x): + with backprop.GradientTape() as tape: + tape.watch(x) + y = math_ops.square(x) + z = math_ops.square(y) + return tape.gradient(z, x) + + analytical, numerical = gradient_checker.compute_gradient(f, [2.0]) + self.assertAllEqual([[[48.]]], analytical) + self.assertAllClose([[[48.]]], numerical, rtol=1e-4) + + +@test_util.run_all_in_graph_and_eager_modes +class MiniMNISTTest(test.TestCase): + + # Gradient checker for MNIST. + def _BuildAndTestMiniMNIST(self, param_index, tag): + # Fix seed to avoid occasional flakiness + np.random.seed(6) + + # Hyperparameters + batch = 3 + inputs = 16 + features = 32 + classes = 10 + + # Define the parameters + inp_data = np.random.random_sample(inputs * batch) + hidden_weight_data = np.random.randn(inputs * features) / np.sqrt(inputs) + hidden_bias_data = np.random.random_sample(features) + sm_weight_data = np.random.randn(features * classes) / np.sqrt(features) + sm_bias_data = np.random.random_sample(classes) + + # special care for labels since they need to be normalized per batch + label_data = np.random.random(batch * classes).reshape((batch, classes)) + s = label_data.sum(axis=1) + label_data /= s[:, None] + + # We treat the inputs as "parameters" here + inp = constant_op.constant( + inp_data.tolist(), + shape=[batch, inputs], + dtype=dtypes.float64, + name="inp") + hidden_weight = constant_op.constant( + hidden_weight_data.tolist(), + shape=[inputs, features], + dtype=dtypes.float64, + name="hidden_weight") + hidden_bias = constant_op.constant( + hidden_bias_data.tolist(), + shape=[features], + dtype=dtypes.float64, + name="hidden_bias") + softmax_weight = constant_op.constant( + sm_weight_data.tolist(), + shape=[features, classes], + dtype=dtypes.float64, + name="softmax_weight") + softmax_bias = constant_op.constant( + sm_bias_data.tolist(), + shape=[classes], + dtype=dtypes.float64, + name="softmax_bias") + + # List all the parameter so that we can test them one at a time + all_params = [ + inp, hidden_weight, hidden_bias, softmax_weight, softmax_bias + ] + + # Now, Building MNIST + def f(inp, hidden_weight, hidden_bias, softmax_weight, softmax_bias): + features = nn_ops.relu( + nn_ops.xw_plus_b(inp, hidden_weight, hidden_bias), name="features") + logits = nn_ops.xw_plus_b( + features, softmax_weight, softmax_bias, name="logits") + labels = constant_op.constant( + label_data.tolist(), + shape=[batch, classes], + dtype=dtypes.float64, + name="labels") + cost = nn_ops.softmax_cross_entropy_with_logits( + labels=labels, logits=logits, name="cost") + return cost + + def f_restricted(x): + xs = all_params + i = param_index + # use x for the i-th parameter + xs = xs[0:i]+[x]+xs[i+1:] + return f(*xs) + # Test the gradients. + err = gradient_checker.max_error(*gradient_checker.compute_gradient( + f_restricted, [all_params[param_index]], delta=1e-5)) + + tf_logging.info("Mini MNIST: %s gradient error = %g", tag, err) + return err + + def testInputGradient(self): + # if context.executing_eagerly(): + # return + self.assertLess(self._BuildAndTestMiniMNIST(0, "input"), 1e-8) + + def testHiddenWeightGradient(self): + self.assertLess(self._BuildAndTestMiniMNIST(1, "hidden_weight"), 1e-8) + + def testHiddenBiasGradient(self): + self.assertLess(self._BuildAndTestMiniMNIST(2, "hidden_bias"), 1e-8) + + def testSoftmaxWeightGradient(self): + self.assertLess(self._BuildAndTestMiniMNIST(3, "softmax_weight"), 1e-8) + + def testSoftmaxBiasGradient(self): + self.assertLess(self._BuildAndTestMiniMNIST(4, "softmax_bias"), 1e-8) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/ops/gradients_impl.py b/tensorflow/python/ops/gradients_impl.py index 4f0fb54dca..8cc4d926c7 100644 --- a/tensorflow/python/ops/gradients_impl.py +++ b/tensorflow/python/ops/gradients_impl.py @@ -49,9 +49,9 @@ from tensorflow.python.ops import logging_ops # pylint: disable=unused-import from tensorflow.python.ops import manip_grad # pylint: disable=unused-import from tensorflow.python.ops import math_grad # pylint: disable=unused-import from tensorflow.python.ops import math_ops +from tensorflow.python.ops import optional_grad # pylint: disable=unused-import from tensorflow.python.ops import random_grad # pylint: disable=unused-import from tensorflow.python.ops import resource_variable_ops -from tensorflow.python.ops import spectral_grad # pylint: disable=unused-import from tensorflow.python.ops import tensor_array_ops from tensorflow.python.ops.unconnected_gradients import UnconnectedGradients from tensorflow.python.platform import tf_logging as logging @@ -540,7 +540,7 @@ def _Consumers(t, func_graphs): return consumers -@tf_export("gradients") +@tf_export(v1=["gradients"]) def gradients(ys, xs, grad_ys=None, @@ -656,6 +656,119 @@ def gradients(ys, unconnected_gradients) +@tf_export("gradients", v1=[]) +def gradients_v2(ys, # pylint: disable=invalid-name + xs, + grad_ys=None, + name="gradients", + gate_gradients=False, + aggregation_method=None, + stop_gradients=None, + unconnected_gradients=UnconnectedGradients.NONE): + """Constructs symbolic derivatives of sum of `ys` w.r.t. x in `xs`. + + `ys` and `xs` are each a `Tensor` or a list of tensors. `grad_ys` + is a list of `Tensor`, holding the gradients received by the + `ys`. The list must be the same length as `ys`. + + `gradients()` adds ops to the graph to output the derivatives of `ys` with + respect to `xs`. It returns a list of `Tensor` of length `len(xs)` where + each tensor is the `sum(dy/dx)` for y in `ys`. + + `grad_ys` is a list of tensors of the same length as `ys` that holds + the initial gradients for each y in `ys`. When `grad_ys` is None, + we fill in a tensor of '1's of the shape of y for each y in `ys`. A + user can provide their own initial `grad_ys` to compute the + derivatives using a different initial gradient for each y (e.g., if + one wanted to weight the gradient differently for each value in + each y). + + `stop_gradients` is a `Tensor` or a list of tensors to be considered constant + with respect to all `xs`. These tensors will not be backpropagated through, + as though they had been explicitly disconnected using `stop_gradient`. Among + other things, this allows computation of partial derivatives as opposed to + total derivatives. For example: + + ```python + a = tf.constant(0.) + b = 2 * a + g = tf.gradients(a + b, [a, b], stop_gradients=[a, b]) + ``` + + Here the partial derivatives `g` evaluate to `[1.0, 1.0]`, compared to the + total derivatives `tf.gradients(a + b, [a, b])`, which take into account the + influence of `a` on `b` and evaluate to `[3.0, 1.0]`. Note that the above is + equivalent to: + + ```python + a = tf.stop_gradient(tf.constant(0.)) + b = tf.stop_gradient(2 * a) + g = tf.gradients(a + b, [a, b]) + ``` + + `stop_gradients` provides a way of stopping gradient after the graph has + already been constructed, as compared to `tf.stop_gradient` which is used + during graph construction. When the two approaches are combined, + backpropagation stops at both `tf.stop_gradient` nodes and nodes in + `stop_gradients`, whichever is encountered first. + + All integer tensors are considered constant with respect to all `xs`, as if + they were included in `stop_gradients`. + + `unconnected_gradients` determines the value returned for each x in xs if it + is unconnected in the graph to ys. By default this is None to safeguard + against errors. MAthematically these gradients are zero which can be requested + using the `'zero'` option. `tf.UnconnectedGradients` provides the + following options and behaviors: + + ```python + a = tf.ones([1, 2]) + b = tf.ones([3, 1]) + g1 = tf.gradients([b], [a], unnconnected_gradients='none') + sess.run(g1) # [None] + + g2 = tf.gradients([b], [a], unconnected_gradients='zero') + sess.run(g2) # [array([[0., 0.]], dtype=float32)] + ``` + + + Args: + ys: A `Tensor` or list of tensors to be differentiated. + xs: A `Tensor` or list of tensors to be used for differentiation. + grad_ys: Optional. A `Tensor` or list of tensors the same size as + `ys` and holding the gradients computed for each y in `ys`. + name: Optional name to use for grouping all the gradient ops together. + defaults to 'gradients'. + gate_gradients: If True, add a tuple around the gradients returned + for an operations. This avoids some race conditions. + aggregation_method: Specifies the method used to combine gradient terms. + Accepted values are constants defined in the class `AggregationMethod`. + stop_gradients: Optional. A `Tensor` or list of tensors not to differentiate + through. + unconnected_gradients: Optional. Specifies the gradient value returned when + the given input tensors are unconnected. Accepted values are constants + defined in the class `tf.UnconnectedGradients` and the default value is + `none`. + + Returns: + A list of `sum(dy/dx)` for each x in `xs`. + + Raises: + LookupError: if one of the operations between `x` and `y` does not + have a registered gradient function. + ValueError: if the arguments are invalid. + RuntimeError: if called in Eager mode. + + """ + # Creating the gradient graph for control flow mutates Operations. + # _mutation_lock ensures a Session.run call cannot occur between creating and + # mutating new ops. + with ops.get_default_graph()._mutation_lock(): # pylint: disable=protected-access + return _GradientsHelper(ys, xs, grad_ys, name, True, gate_gradients, + aggregation_method, stop_gradients, + unconnected_gradients) + + def _GradientsHelper(ys, xs, grad_ys=None, @@ -896,7 +1009,7 @@ def _HasAnyNotNoneGrads(grads, op): if isinstance(out_grad, (ops.Tensor, ops.IndexedSlices)): return True if out_grad and isinstance(out_grad, collections.Sequence): - if any([g is not None for g in out_grad]): + if any(g is not None for g in out_grad): return True return False @@ -1111,11 +1224,11 @@ def _AggregatedGrads(grads, assert control_flow_util.IsLoopSwitch(op) continue # Grads have to be Tensors or IndexedSlices - if (isinstance(out_grad, collections.Sequence) and not all([ + if (isinstance(out_grad, collections.Sequence) and not all( isinstance(g, (ops.Tensor, ops.IndexedSlices)) for g in out_grad if g is not None - ])): + )): raise TypeError("gradients have to be either all Tensors " "or all IndexedSlices") # Aggregate multiple gradients, and convert [] to None. @@ -1123,7 +1236,7 @@ def _AggregatedGrads(grads, if len(out_grad) < 2: used = "nop" out_grads[i] = out_grad[0] - elif all([isinstance(g, ops.Tensor) for g in out_grad if g is not None]): + elif all(isinstance(g, ops.Tensor) for g in out_grad if g is not None): tensor_shape = _AccumulatorShape(out_grad) if (aggregation_method == AggregationMethod.EXPERIMENTAL_ACCUMULATE_N and len(out_grad) > 2 and tensor_shape.is_fully_defined()): @@ -1240,7 +1353,7 @@ def _hessian_vector_product(ys, xs, v): return gradients(elemwise_products, xs) -@tf_export("hessians") +@tf_export(v1=["hessians"]) def hessians(ys, xs, name="hessians", @@ -1305,3 +1418,16 @@ def hessians(ys, array_ops.concat((_shape, _shape), 0)) hessians.append(_reshaped_hessian) return hessians + + +@tf_export("hessians", v1=[]) +def HessiansV2(ys, + xs, + gate_gradients=False, + aggregation_method=None, + name="hessians"): + return hessians(ys, xs, name=name, gate_gradients=gate_gradients, + aggregation_method=aggregation_method) + + +HessiansV2.__doc__ = hessians.__doc__ diff --git a/tensorflow/python/ops/gradients_test.py b/tensorflow/python/ops/gradients_test.py index 103e3902b6..a9058c4a34 100644 --- a/tensorflow/python/ops/gradients_test.py +++ b/tensorflow/python/ops/gradients_test.py @@ -144,7 +144,7 @@ class GradientsTest(test_util.TensorFlowTestCase): gate_gradients=True)[0] with session.Session(): # Make sure the placer doesn't complain. - gz_x.eval() + self.evaluate(gz_x) def testBoundaryStop(self): # Test that we don't differentiate 'x'. The gradient function for 'x' is @@ -365,7 +365,7 @@ class GradientsTest(test_util.TensorFlowTestCase): grads = gradients.gradients( [y], [x], unconnected_gradients="zero") with self.cached_session() as sess: - self.assertAllEqual([[0.0, 0.0], [0.0, 0.0]], sess.run(grads)[0]) + self.assertAllEqual([[0.0, 0.0], [0.0, 0.0]], self.evaluate(grads)[0]) def testUnconnectedGradientsZeroConnectedGradients(self): with ops.Graph().as_default(): @@ -374,7 +374,7 @@ class GradientsTest(test_util.TensorFlowTestCase): grad = gradients.gradients( [y], [x], unconnected_gradients="zero") with self.cached_session() as sess: - self.assertEquals(3.0, sess.run(grad)[0]) + self.assertEquals(3.0, self.evaluate(grad)[0]) def testUnknownUnconnectedGradientsValueGiven(self): with ops.Graph().as_default(): @@ -438,8 +438,8 @@ class FunctionGradientsTest(test_util.TensorFlowTestCase): grads = gradients.gradients(y, [x, b1]) with self.cached_session() as sess: - self.assertAllEqual([40.0], sess.run(grads)[0]) - self.assertAllEqual([10.0], sess.run(grads)[1]) + self.assertAllEqual([40.0], self.evaluate(grads)[0]) + self.assertAllEqual([10.0], self.evaluate(grads)[1]) def testFunctionGradientsWithGradFunc(self): g = ops.Graph() @@ -487,7 +487,7 @@ class FunctionGradientsTest(test_util.TensorFlowTestCase): f = Foo() with self.cached_session() as sess: - self.assertEqual(sess.run(f), 2.0) + self.assertEqual(self.evaluate(f), 2.0) def testGradientOfCaptured(self): with ops.Graph().as_default(): @@ -501,7 +501,7 @@ class FunctionGradientsTest(test_util.TensorFlowTestCase): f = Foo() with self.cached_session() as sess: - self.assertEqual(sess.run(f), 2.0) + self.assertEqual(self.evaluate(f), 2.0) def testCapturedResourceVariable(self): with ops.Graph().as_default(): @@ -515,8 +515,8 @@ class FunctionGradientsTest(test_util.TensorFlowTestCase): f = Foo() with self.cached_session() as sess: - sess.run(variables.global_variables_initializer()) - self.assertEqual(sess.run(f), 2.0) + self.evaluate(variables.global_variables_initializer()) + self.assertEqual(self.evaluate(f), 2.0) def testCapturedNested(self): with ops.Graph().as_default(): @@ -541,9 +541,9 @@ class FunctionGradientsTest(test_util.TensorFlowTestCase): x1_grad, x2_grad = Outer() with self.cached_session() as sess: # 1.0 + None + 2.0 + 1.0 = 4.0 - self.assertEqual(sess.run(x1_grad), 4.0) + self.assertEqual(self.evaluate(x1_grad), 4.0) # None + 1.0 + 1.0 + None = 2.0 - self.assertEqual(sess.run(x2_grad), 2.0) + self.assertEqual(self.evaluate(x2_grad), 2.0) def testCapturedFromFunction(self): with ops.Graph().as_default(): @@ -563,7 +563,7 @@ class FunctionGradientsTest(test_util.TensorFlowTestCase): z_grad = Outer() with self.cached_session() as sess: - self.assertEqual(sess.run(z_grad), 3.0) + self.assertEqual(self.evaluate(z_grad), 3.0) def testCapturedEagerTensors(self): # Test that we can handle captured eager tensors unrelated to the gradient @@ -628,7 +628,7 @@ class HessianVectorProductTest(test_util.TensorFlowTestCase): mat_x = math_ops.matmul(mat, x, name="Ax") x_mat_x = math_ops.matmul(array_ops.transpose(x), mat_x, name="xAx") hess_v = gradients_impl._hessian_vector_product(x_mat_x, [x], [v])[0] - hess_v_actual = hess_v.eval() + hess_v_actual = self.evaluate(hess_v) self.assertAllClose(hess_v_value, hess_v_actual) @@ -648,7 +648,7 @@ class HessianTest(test_util.TensorFlowTestCase): x = constant_op.constant(x_value) x_mat_x = math_ops.reduce_sum(x[:, None] * mat * x[None, :]) hess = gradients.hessians(x_mat_x, x)[0] - hess_actual = hess.eval() + hess_actual = self.evaluate(hess) self.assertAllClose(hess_value, hess_actual) def testHessian1D_multi(self): @@ -692,7 +692,7 @@ class HessianTest(test_util.TensorFlowTestCase): math_ops.matmul(array_ops.transpose(x), x) * 0.5 ) hess = gradients.hessians(x_square, x)[0] - hess_actual = hess.eval() + hess_actual = self.evaluate(hess) hess_value = np.bmat([ [elem*np.ones((m, m)) for elem in vec] for vec in np.eye(m) @@ -711,7 +711,7 @@ class HessianTest(test_util.TensorFlowTestCase): math_ops.matmul(array_ops.transpose(x), x) * 0.5 ) hess = gradients.hessians(x_square, x)[0] - hess_actual = hess.eval() + hess_actual = self.evaluate(hess) hess_value = np.bmat([ [elem*np.ones((n, n)) for elem in vec] for vec in np.eye(m) @@ -729,7 +729,7 @@ class IndexedSlicesToTensorTest(test_util.TensorFlowTestCase): c_sparse = math_ops._as_indexed_slices(c) self.assertAllEqual(np_val.shape, c_sparse.dense_shape.eval()) c_dense = math_ops.multiply(c_sparse, 1.0) - self.assertAllClose(np_val, c_dense.eval()) + self.assertAllClose(np_val, self.evaluate(c_dense)) def testIndexedSlicesToTensorList(self): with self.cached_session(): @@ -745,7 +745,7 @@ class IndexedSlicesToTensorTest(test_util.TensorFlowTestCase): sparse_list.append(c_sparse) packed_dense = array_ops.stack(dense_list) packed_sparse = array_ops.stack(sparse_list) - self.assertAllClose(packed_dense.eval(), packed_sparse.eval()) + self.assertAllClose(packed_dense.eval(), self.evaluate(packed_sparse)) def testInt64Indices(self): with self.cached_session(): @@ -757,7 +757,7 @@ class IndexedSlicesToTensorTest(test_util.TensorFlowTestCase): math_ops.cast(c_sparse.indices, dtypes.int64), c_sparse.dense_shape) self.assertAllEqual(np_val.shape, c_sparse.dense_shape.eval()) c_dense = math_ops.multiply(c_sparse, 1.0) - self.assertAllClose(np_val, c_dense.eval()) + self.assertAllClose(np_val, self.evaluate(c_dense)) def testWarnings(self): # TODO(gunan) Reenable after this issue is fixed: @@ -853,7 +853,7 @@ class CustomGradientTest(test_util.TensorFlowTestCase): y = MyIdentity(MyIdentity(x)) dy = gradients.gradients(y, x)[0] with session.Session(): - self.assertEqual(9., dy.eval()) + self.assertEqual(9., self.evaluate(dy)) def testCustomGradient(self): @@ -873,7 +873,7 @@ class CustomGradientTest(test_util.TensorFlowTestCase): y = MyMultiply(x1, x2) dy = gradients.gradients(y, [x1, x2]) with session.Session() as sess: - self.assertAllEqual([3., 5.], sess.run(dy)) + self.assertAllEqual([3., 5.], self.evaluate(dy)) def testCustomGradientErrors(self): @@ -914,7 +914,7 @@ class CustomGradientTest(test_util.TensorFlowTestCase): for g in grads: self.assertTrue(g is not None) with session.Session() as sess: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) dw = sess.run(math_ops.reduce_sum(grads[1])) self.assertEqual(12., dw) @@ -1074,7 +1074,7 @@ class TensorListGradientsTest(test_util.TensorFlowTestCase): grad = gradients.gradients(tl, a, grad_ys=grad_tl)[0] with self.cached_session() as sess: - self.assertEquals(sess.run(grad), 5.) + self.assertEquals(self.evaluate(grad), 5.) if __name__ == "__main__": diff --git a/tensorflow/python/ops/histogram_ops_test.py b/tensorflow/python/ops/histogram_ops_test.py index e7fe0efba4..b48ef67196 100644 --- a/tensorflow/python/ops/histogram_ops_test.py +++ b/tensorflow/python/ops/histogram_ops_test.py @@ -21,6 +21,7 @@ from __future__ import print_function import numpy as np from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.framework import constant_op from tensorflow.python.ops import array_ops from tensorflow.python.ops import histogram_ops @@ -39,7 +40,7 @@ class BinValuesFixedWidth(test.TestCase): bins = histogram_ops.histogram_fixed_width_bins( values, value_range, nbins=5) self.assertEqual(dtypes.int32, bins.dtype) - self.assertAllClose(expected_bins, bins.eval()) + self.assertAllClose(expected_bins, self.evaluate(bins)) def test_1d_values_int32_output(self): # Bins will be: @@ -51,7 +52,7 @@ class BinValuesFixedWidth(test.TestCase): bins = histogram_ops.histogram_fixed_width_bins( values, value_range, nbins=5, dtype=dtypes.int64) self.assertEqual(dtypes.int32, bins.dtype) - self.assertAllClose(expected_bins, bins.eval()) + self.assertAllClose(expected_bins, self.evaluate(bins)) def test_1d_float64_values_int32_output(self): # Bins will be: @@ -63,7 +64,7 @@ class BinValuesFixedWidth(test.TestCase): bins = histogram_ops.histogram_fixed_width_bins( values, value_range, nbins=5) self.assertEqual(dtypes.int32, bins.dtype) - self.assertAllClose(expected_bins, bins.eval()) + self.assertAllClose(expected_bins, self.evaluate(bins)) def test_2d_values(self): # Bins will be: @@ -76,7 +77,7 @@ class BinValuesFixedWidth(test.TestCase): bins = histogram_ops.histogram_fixed_width_bins( values, value_range, nbins=5) self.assertEqual(dtypes.int32, bins.dtype) - self.assertAllClose(expected_bins, bins.eval()) + self.assertAllClose(expected_bins, self.evaluate(bins)) class HistogramFixedWidthTest(test.TestCase): @@ -84,6 +85,7 @@ class HistogramFixedWidthTest(test.TestCase): def setUp(self): self.rng = np.random.RandomState(0) + @test_util.run_deprecated_v1 def test_with_invalid_value_range(self): values = [-1.0, 0.0, 1.5, 2.0, 5.0, 15] with self.assertRaisesRegexp( @@ -92,6 +94,7 @@ class HistogramFixedWidthTest(test.TestCase): with self.assertRaisesRegexp(ValueError, "Dimension must be 2 but is 3"): histogram_ops.histogram_fixed_width(values, [1.0, 2.0, 3.0]) + @test_util.run_deprecated_v1 def test_with_invalid_nbins(self): values = [-1.0, 0.0, 1.5, 2.0, 5.0, 15] with self.assertRaisesRegexp( @@ -110,7 +113,7 @@ class HistogramFixedWidthTest(test.TestCase): with self.session(use_gpu=True): hist = histogram_ops.histogram_fixed_width(values, value_range, nbins=5) self.assertEqual(dtypes.int32, hist.dtype) - self.assertAllClose(expected_bin_counts, hist.eval()) + self.assertAllClose(expected_bin_counts, self.evaluate(hist)) def test_1d_values_int64_output(self): # Bins will be: @@ -122,7 +125,7 @@ class HistogramFixedWidthTest(test.TestCase): hist = histogram_ops.histogram_fixed_width( values, value_range, nbins=5, dtype=dtypes.int64) self.assertEqual(dtypes.int64, hist.dtype) - self.assertAllClose(expected_bin_counts, hist.eval()) + self.assertAllClose(expected_bin_counts, self.evaluate(hist)) def test_1d_float64_values(self): # Bins will be: @@ -133,7 +136,7 @@ class HistogramFixedWidthTest(test.TestCase): with self.session(use_gpu=True): hist = histogram_ops.histogram_fixed_width(values, value_range, nbins=5) self.assertEqual(dtypes.int32, hist.dtype) - self.assertAllClose(expected_bin_counts, hist.eval()) + self.assertAllClose(expected_bin_counts, self.evaluate(hist)) def test_2d_values(self): # Bins will be: @@ -144,8 +147,9 @@ class HistogramFixedWidthTest(test.TestCase): with self.session(use_gpu=True): hist = histogram_ops.histogram_fixed_width(values, value_range, nbins=5) self.assertEqual(dtypes.int32, hist.dtype) - self.assertAllClose(expected_bin_counts, hist.eval()) + self.assertAllClose(expected_bin_counts, self.evaluate(hist)) + @test_util.run_deprecated_v1 def test_shape_inference(self): value_range = [0.0, 5.0] values = [[-1.0, 0.0, 1.5], [2.0, 5.0, 15]] @@ -155,7 +159,7 @@ class HistogramFixedWidthTest(test.TestCase): hist = histogram_ops.histogram_fixed_width(values, value_range, nbins=5) self.assertAllEqual(hist.shape.as_list(), (5,)) self.assertEqual(dtypes.int32, hist.dtype) - self.assertAllClose(expected_bin_counts, hist.eval()) + self.assertAllClose(expected_bin_counts, self.evaluate(hist)) hist = histogram_ops.histogram_fixed_width( values, value_range, nbins=placeholder) diff --git a/tensorflow/python/ops/image_grad_test.py b/tensorflow/python/ops/image_grad_test.py index 32c2f37c0b..c481266dd7 100644 --- a/tensorflow/python/ops/image_grad_test.py +++ b/tensorflow/python/ops/image_grad_test.py @@ -21,6 +21,7 @@ from __future__ import print_function import numpy as np from tensorflow.python.framework import constant_op +from tensorflow.python.framework import test_util from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import gradients_impl from tensorflow.python.ops import image_ops @@ -44,9 +45,10 @@ class ResizeNearestNeighborOpTest(test.TestCase): out_shape[1:3]) self.assertEqual(out_shape, list(resize_out.get_shape())) - resize_out = sess.run(resize_out) + resize_out = self.evaluate(resize_out) self.assertEqual(out_shape, list(resize_out.shape)) + @test_util.run_deprecated_v1 def testGradFromResizeToLargerInBothDims(self): in_shape = [1, 2, 3, 1] out_shape = [1, 4, 6, 1] @@ -62,6 +64,7 @@ class ResizeNearestNeighborOpTest(test.TestCase): input_tensor, in_shape, resize_out, out_shape, x_init_value=x) self.assertLess(err, 1e-3) + @test_util.run_deprecated_v1 def testGradFromResizeToSmallerInBothDims(self): in_shape = [1, 4, 6, 1] out_shape = [1, 2, 3, 1] @@ -77,6 +80,7 @@ class ResizeNearestNeighborOpTest(test.TestCase): input_tensor, in_shape, resize_out, out_shape, x_init_value=x) self.assertLess(err, 1e-3) + @test_util.run_deprecated_v1 def testCompareGpuVsCpu(self): in_shape = [1, 4, 6, 3] out_shape = [1, 8, 16, 3] @@ -113,9 +117,10 @@ class ResizeBilinearOpTest(test.TestCase): resize_out = image_ops.resize_bilinear(input_tensor, out_shape[1:3]) self.assertEqual(out_shape, list(resize_out.get_shape())) - resize_out = sess.run(resize_out) + resize_out = self.evaluate(resize_out) self.assertEqual(out_shape, list(resize_out.shape)) + @test_util.run_deprecated_v1 def testGradFromResizeToLargerInBothDims(self): in_shape = [1, 2, 3, 1] out_shape = [1, 4, 6, 1] @@ -129,6 +134,7 @@ class ResizeBilinearOpTest(test.TestCase): input_tensor, in_shape, resize_out, out_shape, x_init_value=x) self.assertLess(err, 1e-3) + @test_util.run_deprecated_v1 def testGradFromResizeToSmallerInBothDims(self): in_shape = [1, 4, 6, 1] out_shape = [1, 2, 3, 1] @@ -142,6 +148,7 @@ class ResizeBilinearOpTest(test.TestCase): input_tensor, in_shape, resize_out, out_shape, x_init_value=x) self.assertLess(err, 1e-3) + @test_util.run_deprecated_v1 def testCompareGpuVsCpu(self): in_shape = [2, 4, 6, 3] out_shape = [2, 8, 16, 3] @@ -160,6 +167,7 @@ class ResizeBilinearOpTest(test.TestCase): self.assertAllClose(grad[False], grad[True], rtol=1e-4, atol=1e-4) + @test_util.run_deprecated_v1 def testTypes(self): in_shape = [1, 4, 6, 1] out_shape = [1, 2, 3, 1] @@ -196,9 +204,10 @@ class ResizeBicubicOpTest(test.TestCase): align_corners=align_corners) self.assertEqual(out_shape, list(resize_out.get_shape())) - resize_out = sess.run(resize_out) + resize_out = self.evaluate(resize_out) self.assertEqual(out_shape, list(resize_out.shape)) + @test_util.run_deprecated_v1 def testGradFromResizeToLargerInBothDims(self): in_shape = [1, 2, 3, 1] out_shape = [1, 4, 6, 1] @@ -214,6 +223,7 @@ class ResizeBicubicOpTest(test.TestCase): input_tensor, in_shape, resize_out, out_shape, x_init_value=x) self.assertLess(err, 1e-3) + @test_util.run_deprecated_v1 def testGradFromResizeToSmallerInBothDims(self): in_shape = [1, 4, 6, 1] out_shape = [1, 2, 3, 1] @@ -229,6 +239,7 @@ class ResizeBicubicOpTest(test.TestCase): input_tensor, in_shape, resize_out, out_shape, x_init_value=x) self.assertLess(err, 1e-3) + @test_util.run_deprecated_v1 def testGradOnUnsupportedType(self): in_shape = [1, 4, 6, 1] out_shape = [1, 2, 3, 1] @@ -273,7 +284,7 @@ class CropAndResizeOpTest(test.TestCase): constant_op.constant( crop_size, shape=[2])) self.assertEqual(crops_shape, list(crops.get_shape())) - crops = sess.run(crops) + crops = self.evaluate(crops) self.assertEqual(crops_shape, list(crops.shape)) def _randomUniformAvoidAnchors(self, low, high, anchors, radius, num_samples): @@ -306,6 +317,7 @@ class CropAndResizeOpTest(test.TestCase): samples.append(sample) return samples + @test_util.run_deprecated_v1 def testGradRandomBoxes(self): """Test that the gradient is correct for randomly generated boxes. diff --git a/tensorflow/python/ops/image_ops_impl.py b/tensorflow/python/ops/image_ops_impl.py index 3ab3695a03..229393c970 100644 --- a/tensorflow/python/ops/image_ops_impl.py +++ b/tensorflow/python/ops/image_ops_impl.py @@ -24,6 +24,7 @@ from tensorflow.python.compat import compat from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import random_seed from tensorflow.python.framework import tensor_shape from tensorflow.python.framework import tensor_util from tensorflow.python.ops import array_ops @@ -37,6 +38,7 @@ from tensorflow.python.ops import nn_ops from tensorflow.python.ops import random_ops from tensorflow.python.ops import string_ops from tensorflow.python.ops import variables +from tensorflow.python.util import deprecation from tensorflow.python.util.tf_export import tf_export ops.NotDifferentiable('RandomCrop') @@ -511,15 +513,20 @@ def _rot90_4D(images, k, name_scope): result.set_shape([shape[0], None, None, shape[3]]) return result -@tf_export('image.transpose_image') + +@tf_export(v1=['image.transpose', 'image.transpose_image']) def transpose_image(image): - """Transpose image(s) by swapping the height and width dimension. + return transpose(image=image, name=None) - See also `transpose()`. + +@tf_export('image.transpose', v1=[]) +def transpose(image, name=None): + """Transpose image(s) by swapping the height and width dimension. Args: image: 4-D Tensor of shape `[batch, height, width, channels]` or 3-D Tensor of shape `[height, width, channels]`. + name: A name for this operation (optional). Returns: If `image` was 4-D, a 4-D float Tensor of shape @@ -530,14 +537,14 @@ def transpose_image(image): Raises: ValueError: if the shape of `image` not supported. """ - with ops.name_scope(None, 'transpose_image', [image]): + with ops.name_scope(name, 'transpose', [image]): image = ops.convert_to_tensor(image, name='image') image = _AssertAtLeast3DImage(image) shape = image.get_shape() if shape.ndims == 3 or shape.ndims is None: - return array_ops.transpose(image, [1, 0, 2], name='transpose_image') + return array_ops.transpose(image, [1, 0, 2], name=name) elif shape.ndims == 4: - return array_ops.transpose(image, [0, 2, 1, 3], name='transpose_image') + return array_ops.transpose(image, [0, 2, 1, 3], name=name) else: raise ValueError('\'image\' must have either 3 or 4 dimensions.') @@ -938,12 +945,28 @@ class ResizeMethod(object): AREA = 3 -@tf_export('image.resize_images') +@tf_export(v1=['image.resize_images', 'image.resize']) def resize_images(images, size, method=ResizeMethod.BILINEAR, align_corners=False, preserve_aspect_ratio=False): + return resize_images_v2( + images=images, + size=size, + method=method, + align_corners=align_corners, + preserve_aspect_ratio=preserve_aspect_ratio, + name=None) + + +@tf_export('image.resize', v1=[]) +def resize_images_v2(images, + size, + method=ResizeMethod.BILINEAR, + align_corners=False, + preserve_aspect_ratio=False, + name=None): """Resize `images` to `size` using the specified `method`. Resized images will be distorted if their original aspect ratio is not @@ -979,6 +1002,7 @@ def resize_images(images, then `images` will be resized to a size that fits in `size` while preserving the aspect ratio of the original image. Scales up the image if `size` is bigger than the current size of the `image`. Defaults to False. + name: A name for this operation (optional). Raises: ValueError: if the shape of `images` is incompatible with the @@ -992,7 +1016,7 @@ def resize_images(images, If `images` was 3-D, a 3-D float Tensor of shape `[new_height, new_width, channels]`. """ - with ops.name_scope(None, 'resize_images', [images, size]): + with ops.name_scope(name, 'resize', [images, size]): images = ops.convert_to_tensor(images, name='images') if images.get_shape().ndims is None: raise ValueError('\'images\' contains no shape.') @@ -1736,7 +1760,7 @@ def adjust_saturation(image, saturation_factor, name=None): orig_dtype) -@tf_export('image.is_jpeg') +@tf_export('io.is_jpeg', 'image.is_jpeg', v1=['io.is_jpeg', 'image.is_jpeg']) def is_jpeg(contents, name=None): r"""Convenience function to check if the 'contents' encodes a JPEG image. @@ -1771,8 +1795,28 @@ def _is_png(contents, name=None): substr = string_ops.substr(contents, 0, 3) return math_ops.equal(substr, b'\211PN', name=name) +tf_export('io.decode_and_crop_jpeg', 'image.decode_and_crop_jpeg', + v1=['io.decode_and_crop_jpeg', 'image.decode_and_crop_jpeg'])( + gen_image_ops.decode_and_crop_jpeg) + +tf_export('io.decode_bmp', 'image.decode_bmp', + v1=['io.decode_bmp', 'image.decode_bmp'])(gen_image_ops.decode_bmp) +tf_export('io.decode_gif', 'image.decode_gif', + v1=['io.decode_gif', 'image.decode_gif'])(gen_image_ops.decode_gif) +tf_export('io.decode_jpeg', 'image.decode_jpeg', + v1=['io.decode_jpeg', 'image.decode_jpeg'])(gen_image_ops.decode_jpeg) +tf_export('io.decode_png', 'image.decode_png', + v1=['io.decode_png', 'image.decode_png'])(gen_image_ops.decode_png) -@tf_export('image.decode_image') +tf_export('io.encode_jpeg', 'image.encode_jpeg', + v1=['io.encode_jpeg', 'image.encode_jpeg'])(gen_image_ops.encode_jpeg) +tf_export('io.extract_jpeg_shape', 'image.extract_jpeg_shape', + v1=['io.extract_jpeg_shape', 'image.extract_jpeg_shape'])( + gen_image_ops.extract_jpeg_shape) + + +@tf_export('io.decode_image', 'image.decode_image', + v1=['io.decode_image', 'image.decode_image']) def decode_image(contents, channels=None, dtype=dtypes.uint8, name=None): """Convenience function for `decode_bmp`, `decode_gif`, `decode_jpeg`, and `decode_png`. @@ -1942,7 +1986,114 @@ def total_variation(images, name=None): return tot_var -@tf_export('image.sample_distorted_bounding_box') +@tf_export('image.sample_distorted_bounding_box', v1=[]) +def sample_distorted_bounding_box_v2(image_size, + bounding_boxes, + seed=0, + min_object_covered=0.1, + aspect_ratio_range=None, + area_range=None, + max_attempts=None, + use_image_if_no_bounding_boxes=None, + name=None): + """Generate a single randomly distorted bounding box for an image. + + Bounding box annotations are often supplied in addition to ground-truth labels + in image recognition or object localization tasks. A common technique for + training such a system is to randomly distort an image while preserving + its content, i.e. *data augmentation*. This Op outputs a randomly distorted + localization of an object, i.e. bounding box, given an `image_size`, + `bounding_boxes` and a series of constraints. + + The output of this Op is a single bounding box that may be used to crop the + original image. The output is returned as 3 tensors: `begin`, `size` and + `bboxes`. The first 2 tensors can be fed directly into `tf.slice` to crop the + image. The latter may be supplied to `tf.image.draw_bounding_boxes` to + visualize what the bounding box looks like. + + Bounding boxes are supplied and returned as `[y_min, x_min, y_max, x_max]`. + The bounding box coordinates are floats in `[0.0, 1.0]` relative to the width + and height of the underlying image. + + For example, + + ```python + # Generate a single distorted bounding box. + begin, size, bbox_for_draw = tf.image.sample_distorted_bounding_box( + tf.shape(image), + bounding_boxes=bounding_boxes, + min_object_covered=0.1) + + # Draw the bounding box in an image summary. + image_with_box = tf.image.draw_bounding_boxes(tf.expand_dims(image, 0), + bbox_for_draw) + tf.summary.image('images_with_box', image_with_box) + + # Employ the bounding box to distort the image. + distorted_image = tf.slice(image, begin, size) + ``` + + Note that if no bounding box information is available, setting + `use_image_if_no_bounding_boxes = true` will assume there is a single implicit + bounding box covering the whole image. If `use_image_if_no_bounding_boxes` is + false and no bounding boxes are supplied, an error is raised. + + Args: + image_size: A `Tensor`. Must be one of the following types: `uint8`, `int8`, + `int16`, `int32`, `int64`. + 1-D, containing `[height, width, channels]`. + bounding_boxes: A `Tensor` of type `float32`. + 3-D with shape `[batch, N, 4]` describing the N bounding boxes + associated with the image. + seed: An optional `int`. Defaults to `0`. + If either `seed` or `seed2` are set to non-zero, the random number + generator is seeded by the given `seed`. Otherwise, it is seeded by a + random seed. + min_object_covered: A Tensor of type `float32`. Defaults to `0.1`. + The cropped area of the image must contain at least this + fraction of any bounding box supplied. The value of this parameter should + be non-negative. In the case of 0, the cropped area does not need to + overlap any of the bounding boxes supplied. + aspect_ratio_range: An optional list of `floats`. Defaults to `[0.75, + 1.33]`. + The cropped area of the image must have an aspect `ratio = + width / height` within this range. + area_range: An optional list of `floats`. Defaults to `[0.05, 1]`. + The cropped area of the image must contain a fraction of the + supplied image within this range. + max_attempts: An optional `int`. Defaults to `100`. + Number of attempts at generating a cropped region of the image + of the specified constraints. After `max_attempts` failures, return the + entire image. + use_image_if_no_bounding_boxes: An optional `bool`. Defaults to `False`. + Controls behavior if no bounding boxes supplied. + If true, assume an implicit bounding box covering the whole input. If + false, raise an error. + name: A name for the operation (optional). + + Returns: + A tuple of `Tensor` objects (begin, size, bboxes). + + begin: A `Tensor`. Has the same type as `image_size`. 1-D, containing + `[offset_height, offset_width, 0]`. Provide as input to + `tf.slice`. + size: A `Tensor`. Has the same type as `image_size`. 1-D, containing + `[target_height, target_width, -1]`. Provide as input to + `tf.slice`. + bboxes: A `Tensor` of type `float32`. 3-D with shape `[1, 1, 4]` containing + the distorted bounding box. + Provide as input to `tf.image.draw_bounding_boxes`. + """ + seed1, seed2 = random_seed.get_seed(seed) if seed else (0, 0) + return sample_distorted_bounding_box( + image_size, bounding_boxes, seed1, seed2, min_object_covered, + aspect_ratio_range, area_range, max_attempts, + use_image_if_no_bounding_boxes, name) + + +@tf_export(v1=['image.sample_distorted_bounding_box']) +@deprecation.deprecated(date=None, instructions='`seed2` arg is deprecated.' + 'Use sample_distorted_bounding_box_v2 instead.') def sample_distorted_bounding_box(image_size, bounding_boxes, seed=None, @@ -2808,3 +2959,102 @@ def sobel_edges(image): output = array_ops.reshape(output, shape=shape) output.set_shape(static_image_shape.concatenate([num_kernels])) return output + + +resize_area_deprecation = deprecation.deprecated( + date=None, + instructions=( + 'Use `tf.image.resize(...method=ResizeMethod.AREA...)` instead.')) +tf_export(v1=['image.resize_area'])( + resize_area_deprecation(gen_image_ops.resize_area)) + +resize_bicubic_deprecation = deprecation.deprecated( + date=None, + instructions=( + 'Use `tf.image.resize(...method=ResizeMethod.BICUBIC...)` instead.')) +tf_export(v1=['image.resize_bicubic'])( + resize_bicubic_deprecation(gen_image_ops.resize_bicubic)) + +resize_bilinear_deprecation = deprecation.deprecated( + date=None, + instructions=( + 'Use `tf.image.resize(...method=ResizeMethod.BILINEAR...)` instead.')) +tf_export(v1=['image.resize_bilinear'])( + resize_bilinear_deprecation(gen_image_ops.resize_bilinear)) + +resize_nearest_neighbor_deprecation = deprecation.deprecated( + date=None, + instructions=( + 'Use `tf.image.resize(...method=ResizeMethod.NEAREST_NEIGHBOR...)` ' + 'instead.')) +tf_export(v1=['image.resize_nearest_neighbor'])( + resize_nearest_neighbor_deprecation(gen_image_ops.resize_nearest_neighbor)) + + +@tf_export('image.crop_and_resize', v1=[]) +def crop_and_resize_v2( + image, + boxes, + box_indices, + crop_size, + method='bilinear', + extrapolation_value=0, + name=None): + """Extracts crops from the input image tensor and resizes them. + + Extracts crops from the input image tensor and resizes them using bilinear + sampling or nearest neighbor sampling (possibly with aspect ratio change) to a + common output size specified by `crop_size`. This is more general than the + `crop_to_bounding_box` op which extracts a fixed size slice from the input + image and does not allow resizing or aspect ratio change. + + Returns a tensor with `crops` from the input `image` at positions defined at + the bounding box locations in `boxes`. The cropped boxes are all resized (with + bilinear or nearest neighbor interpolation) to a fixed + `size = [crop_height, crop_width]`. The result is a 4-D tensor + `[num_boxes, crop_height, crop_width, depth]`. The resizing is corner aligned. + In particular, if `boxes = [[0, 0, 1, 1]]`, the method will give identical + results to using `tf.image.resize_bilinear()` or + `tf.image.resize_nearest_neighbor()`(depends on the `method` argument) with + `align_corners=True`. + + Args: + image: A 4-D tensor of shape `[batch, image_height, image_width, depth]`. + Both `image_height` and `image_width` need to be positive. + boxes: A 2-D tensor of shape `[num_boxes, 4]`. The `i`-th row of the tensor + specifies the coordinates of a box in the `box_ind[i]` image and is + specified in normalized coordinates `[y1, x1, y2, x2]`. A normalized + coordinate value of `y` is mapped to the image coordinate at `y * + (image_height - 1)`, so as the `[0, 1]` interval of normalized image + height is mapped to `[0, image_height - 1]` in image height coordinates. + We do allow `y1` > `y2`, in which case the sampled crop is an up-down + flipped version of the original image. The width dimension is treated + similarly. Normalized coordinates outside the `[0, 1]` range are allowed, + in which case we use `extrapolation_value` to extrapolate the input image + values. + box_indices: A 1-D tensor of shape `[num_boxes]` with int32 values in `[0, + batch)`. The value of `box_ind[i]` specifies the image that the `i`-th box + refers to. + crop_size: A 1-D tensor of 2 elements, `size = [crop_height, crop_width]`. + All cropped image patches are resized to this size. The aspect ratio of + the image content is not preserved. Both `crop_height` and `crop_width` + need to be positive. + method: An optional string specifying the sampling method for resizing. It + can be either `"bilinear"` or `"nearest"` and default to `"bilinear"`. + Currently two sampling methods are supported: Bilinear and Nearest + Neighbor. + extrapolation_value: An optional `float`. Defaults to `0`. Value used for + extrapolation, when applicable. + name: A name for the operation (optional). + + Returns: + A 4-D tensor of shape `[num_boxes, crop_height, crop_width, depth]`. + """ + return gen_image_ops.crop_and_resize( + image, boxes, box_indices, crop_size, method, extrapolation_value, name) + + +crop_and_resize_deprecation = deprecation.deprecated_args( + None, 'box_ind is deprecated, use box_indices instead', 'box_ind') +tf_export(v1=['image.crop_and_resize'])( + crop_and_resize_deprecation(gen_image_ops.crop_and_resize)) diff --git a/tensorflow/python/ops/image_ops_test.py b/tensorflow/python/ops/image_ops_test.py index a3aeb79586..e7249333bd 100644 --- a/tensorflow/python/ops/image_ops_test.py +++ b/tensorflow/python/ops/image_ops_test.py @@ -70,7 +70,8 @@ class RGBToHSVTest(test_util.TensorFlowTestCase): split2 = list(map(image_ops.hsv_to_rgb, split1)) join1 = array_ops.stack(split1) join2 = array_ops.stack(split2) - batch1, batch2, join1, join2 = sess.run([batch1, batch2, join1, join2]) + batch1, batch2, join1, join2 = self.evaluate( + [batch1, batch2, join1, join2]) # Verify that processing batch elements together is the same as separate self.assertAllClose(batch1, join1) @@ -84,7 +85,7 @@ class RGBToHSVTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): hsv = image_ops.rgb_to_hsv(rgb_np) rgb = image_ops.hsv_to_rgb(hsv) - rgb_tf = rgb.eval() + rgb_tf = self.evaluate(rgb) self.assertAllClose(rgb_tf, rgb_np) @@ -109,7 +110,8 @@ class RGBToYIQTest(test_util.TensorFlowTestCase): split2 = list(map(image_ops.yiq_to_rgb, split1)) join1 = array_ops.stack(split1) join2 = array_ops.stack(split2) - batch1, batch2, join1, join2 = sess.run([batch1, batch2, join1, join2]) + batch1, batch2, join1, join2 = self.evaluate( + [batch1, batch2, join1, join2]) # Verify that processing batch elements together is the same as separate self.assertAllClose(batch1, join1, rtol=1e-4, atol=1e-4) @@ -138,7 +140,8 @@ class RGBToYUVTest(test_util.TensorFlowTestCase): split2 = list(map(image_ops.yuv_to_rgb, split1)) join1 = array_ops.stack(split1) join2 = array_ops.stack(split2) - batch1, batch2, join1, join2 = sess.run([batch1, batch2, join1, join2]) + batch1, batch2, join1, join2 = self.evaluate( + [batch1, batch2, join1, join2]) # Verify that processing batch elements together is the same as separate self.assertAllClose(batch1, join1, rtol=1e-4, atol=1e-4) @@ -173,7 +176,7 @@ class GrayscaleToRGBTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): x_tf = constant_op.constant(x_np, shape=x_np.shape) y = image_ops.rgb_to_grayscale(x_tf) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllEqual(y_tf, y_np) def testBasicRGBToGrayscale(self): @@ -195,7 +198,7 @@ class GrayscaleToRGBTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): x_tf = constant_op.constant(x_np, shape=x_np.shape) y = image_ops.grayscale_to_rgb(x_tf) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllEqual(y_tf, y_np) # 3-D input with no batch dimension. @@ -205,9 +208,10 @@ class GrayscaleToRGBTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): x_tf = constant_op.constant(x_np, shape=x_np.shape) y = image_ops.grayscale_to_rgb(x_tf) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllEqual(y_tf, y_np) + @test_util.run_deprecated_v1 def testShapeInference(self): # Shape inference works and produces expected output where possible rgb_shape = [7, None, 19, 3] @@ -245,7 +249,7 @@ class AdjustGamma(test_util.TensorFlowTestCase): x = constant_op.constant(x_np, shape=x_np.shape) y = image_ops.adjust_gamma(x, gamma=1) - y_tf = y.eval() + y_tf = self.evaluate(y) y_np = x_np self.assertAllClose(y_tf, y_np, 1e-6) @@ -268,6 +272,7 @@ class AdjustGamma(test_util.TensorFlowTestCase): else: raise AssertionError("Exception not raised: %s" % err_msg) + @test_util.run_deprecated_v1 def test_adjust_gamma_less_zero_tensor(self): """White image should be returned for gamma equal to zero""" with self.cached_session(): @@ -281,7 +286,7 @@ class AdjustGamma(test_util.TensorFlowTestCase): err_msg = "Gamma should be a non-negative real number." try: - image.eval() + self.evaluate(image) except Exception as e: if err_msg not in str(e): raise @@ -297,7 +302,7 @@ class AdjustGamma(test_util.TensorFlowTestCase): x = constant_op.constant(x_np, shape=x_np.shape) y = image_ops.adjust_gamma(x, gamma=0) - y_tf = y.eval() + y_tf = self.evaluate(y) dtype = x.dtype.as_numpy_dtype y_np = np.array([dtypes.dtype_range[dtype][1]] * x_np.size) @@ -305,6 +310,7 @@ class AdjustGamma(test_util.TensorFlowTestCase): self.assertAllClose(y_tf, y_np, 1e-6) + @test_util.run_deprecated_v1 def test_adjust_gamma_less_one(self): """Verifying the output with expected results for gamma correction with gamma equal to half""" @@ -326,6 +332,7 @@ class AdjustGamma(test_util.TensorFlowTestCase): self.assertAllClose(y_tf, y_np, 1e-6) + @test_util.run_deprecated_v1 def test_adjust_gamma_greater_one(self): """Verifying the output with expected results for gamma correction with gamma equal to two""" @@ -360,7 +367,7 @@ class AdjustHueTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): x = constant_op.constant(x_np, shape=x_shape) y = image_ops.adjust_hue(x, delta) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllEqual(y_tf, y_np) def testAdjustPositiveHue(self): @@ -375,7 +382,7 @@ class AdjustHueTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): x = constant_op.constant(x_np, shape=x_shape) y = image_ops.adjust_hue(x, delta) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllEqual(y_tf, y_np) def testBatchAdjustHue(self): @@ -390,7 +397,7 @@ class AdjustHueTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): x = constant_op.constant(x_np, shape=x_shape) y = image_ops.adjust_hue(x, delta) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllEqual(y_tf, y_np) def _adjustHueNp(self, x_np, delta_h): @@ -415,7 +422,7 @@ class AdjustHueTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): x = constant_op.constant(x_np) y = image_ops.adjust_hue(x, delta_h) - y_tf = y.eval() + y_tf = self.evaluate(y) return y_tf def testAdjustRandomHue(self): @@ -488,11 +495,11 @@ class FlipImageBenchmark(test.Benchmark): trainable=False, dtype=dtypes.float32) run_op = image_ops.flip_left_right(inputs) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) for i in xrange(warmup_rounds + benchmark_rounds): if i == warmup_rounds: start = time.time() - sess.run(run_op) + self.evaluate(run_op) end = time.time() step_time = (end - start) / benchmark_rounds tag = device + "_%s" % (cpu_count if cpu_count is not None else "_all") @@ -518,11 +525,11 @@ class FlipImageBenchmark(test.Benchmark): trainable=False, dtype=dtypes.float32) run_op = image_ops.random_flip_left_right(inputs) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) for i in xrange(warmup_rounds + benchmark_rounds): if i == warmup_rounds: start = time.time() - sess.run(run_op) + self.evaluate(run_op) end = time.time() step_time = (end - start) / benchmark_rounds tag = device + "_%s" % (cpu_count if cpu_count is not None else "_all") @@ -548,11 +555,11 @@ class FlipImageBenchmark(test.Benchmark): trainable=False, dtype=dtypes.float32) run_op = image_ops.random_flip_left_right(inputs) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) for i in xrange(warmup_rounds + benchmark_rounds): if i == warmup_rounds: start = time.time() - sess.run(run_op) + self.evaluate(run_op) end = time.time() step_time = (end - start) / benchmark_rounds tag = device + "_%s" % (cpu_count if cpu_count is not None else "_all") @@ -610,11 +617,11 @@ class AdjustHueBenchmark(test.Benchmark): delta = constant_op.constant(0.1, dtype=dtypes.float32) outputs = image_ops.adjust_hue(inputs, delta) run_op = control_flow_ops.group(outputs) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) for i in xrange(warmup_rounds + benchmark_rounds): if i == warmup_rounds: start = time.time() - sess.run(run_op) + self.evaluate(run_op) end = time.time() step_time = (end - start) / benchmark_rounds tag = device + "_%s" % (cpu_count if cpu_count is not None else "_all") @@ -653,12 +660,12 @@ class AdjustSaturationBenchmark(test.Benchmark): delta = constant_op.constant(0.1, dtype=dtypes.float32) outputs = image_ops.adjust_saturation(inputs, delta) run_op = control_flow_ops.group(outputs) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) for _ in xrange(warmup_rounds): - sess.run(run_op) + self.evaluate(run_op) start = time.time() for _ in xrange(benchmark_rounds): - sess.run(run_op) + self.evaluate(run_op) end = time.time() step_time = (end - start) / benchmark_rounds tag = device + "_%s" % (cpu_count if cpu_count is not None else "_all") @@ -698,7 +705,7 @@ class ResizeBilinearBenchmark(test.Benchmark): benchmark_op = control_flow_ops.group(*deps) with self.benchmark_session() as sess: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) results = self.run_op_benchmark( sess, benchmark_op, @@ -746,7 +753,7 @@ class ResizeBicubicBenchmark(test.Benchmark): benchmark_op = control_flow_ops.group(*deps) with self.benchmark_session() as sess: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) results = self.run_op_benchmark( sess, benchmark_op, @@ -803,7 +810,7 @@ class ResizeAreaBenchmark(test.Benchmark): benchmark_op = control_flow_ops.group(*deps) with self.benchmark_session() as sess: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) results = self.run_op_benchmark( sess, benchmark_op, @@ -846,7 +853,7 @@ class AdjustSaturationTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): x = constant_op.constant(x_np, shape=x_shape) y = image_ops.adjust_saturation(x, saturation_factor) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllEqual(y_tf, y_np) def testTwiceSaturation(self): @@ -861,7 +868,7 @@ class AdjustSaturationTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): x = constant_op.constant(x_np, shape=x_shape) y = image_ops.adjust_saturation(x, saturation_factor) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllEqual(y_tf, y_np) def testBatchSaturation(self): @@ -876,7 +883,7 @@ class AdjustSaturationTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): x = constant_op.constant(x_np, shape=x_shape) y = image_ops.adjust_saturation(x, saturation_factor) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllEqual(y_tf, y_np) def _adjust_saturation(self, image, saturation_factor): @@ -899,7 +906,7 @@ class AdjustSaturationTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): x = constant_op.constant(x_np, shape=x_shape) y = self._adjust_saturation(x, saturation_factor) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllEqual(y_tf, y_np) def testTwiceSaturationFused(self): @@ -914,7 +921,7 @@ class AdjustSaturationTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): x = constant_op.constant(x_np, shape=x_shape) y = self._adjust_saturation(x, saturation_factor) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllEqual(y_tf, y_np) def _adjustSaturationNp(self, x_np, scale): @@ -935,6 +942,7 @@ class AdjustSaturationTest(test_util.TensorFlowTestCase): y_v[i][2] = b return y_v.reshape(x_np.shape) + @test_util.run_deprecated_v1 def testAdjustRandomSaturation(self): x_shapes = [ [2, 2, 3], @@ -980,7 +988,7 @@ class FlipTransposeRotateTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): x_tf = constant_op.constant(x_np, shape=x_np.shape) y = image_ops.flip_left_right(image_ops.flip_left_right(x_tf)) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllEqual(y_tf, x_np) def testInvolutionLeftRightWithBatch(self): @@ -990,9 +998,10 @@ class FlipTransposeRotateTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): x_tf = constant_op.constant(x_np, shape=x_np.shape) y = image_ops.flip_left_right(image_ops.flip_left_right(x_tf)) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllEqual(y_tf, x_np) + @test_util.run_deprecated_v1 def testLeftRight(self): x_np = np.array([[1, 2, 3], [1, 2, 3]], dtype=np.uint8).reshape([2, 3, 1]) y_np = np.array([[3, 2, 1], [3, 2, 1]], dtype=np.uint8).reshape([2, 3, 1]) @@ -1001,7 +1010,7 @@ class FlipTransposeRotateTest(test_util.TensorFlowTestCase): x_tf = constant_op.constant(x_np, shape=x_np.shape) y = image_ops.flip_left_right(x_tf) self.assertTrue(y.op.name.startswith("flip_left_right")) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllEqual(y_tf, y_np) def testLeftRightWithBatch(self): @@ -1015,9 +1024,10 @@ class FlipTransposeRotateTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): x_tf = constant_op.constant(x_np, shape=x_np.shape) y = image_ops.flip_left_right(x_tf) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllEqual(y_tf, y_np) + @test_util.run_deprecated_v1 def testRandomFlipLeftRight(self): x_np = np.array([[1, 2, 3], [1, 2, 3]], dtype=np.uint8).reshape([2, 3, 1]) y_np = np.array([[3, 2, 1], [3, 2, 1]], dtype=np.uint8).reshape([2, 3, 1]) @@ -1031,7 +1041,7 @@ class FlipTransposeRotateTest(test_util.TensorFlowTestCase): count_flipped = 0 count_unflipped = 0 for _ in range(100): - y_tf = y.eval() + y_tf = self.evaluate(y) if y_tf[0][0] == 1: self.assertAllEqual(y_tf, x_np) count_unflipped += 1 @@ -1046,6 +1056,7 @@ class FlipTransposeRotateTest(test_util.TensorFlowTestCase): self.assertGreaterEqual(count_flipped, 20) self.assertGreaterEqual(count_unflipped, 20) + @test_util.run_deprecated_v1 def testRandomFlipLeftRightWithBatch(self): batch_size = 16 seed = 42 @@ -1070,7 +1081,7 @@ class FlipTransposeRotateTest(test_util.TensorFlowTestCase): count_flipped = 0 count_unflipped = 0 for _ in range(100): - y_tf = y.eval() + y_tf = self.evaluate(y) # check every element of the batch for i in range(batch_size): @@ -1096,7 +1107,7 @@ class FlipTransposeRotateTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): x_tf = constant_op.constant(x_np, shape=x_np.shape) y = image_ops.flip_up_down(image_ops.flip_up_down(x_tf)) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllEqual(y_tf, x_np) def testInvolutionUpDownWithBatch(self): @@ -1107,9 +1118,10 @@ class FlipTransposeRotateTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): x_tf = constant_op.constant(x_np, shape=x_np.shape) y = image_ops.flip_up_down(image_ops.flip_up_down(x_tf)) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllEqual(y_tf, x_np) + @test_util.run_deprecated_v1 def testUpDown(self): x_np = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.uint8).reshape([2, 3, 1]) y_np = np.array([[4, 5, 6], [1, 2, 3]], dtype=np.uint8).reshape([2, 3, 1]) @@ -1118,7 +1130,7 @@ class FlipTransposeRotateTest(test_util.TensorFlowTestCase): x_tf = constant_op.constant(x_np, shape=x_np.shape) y = image_ops.flip_up_down(x_tf) self.assertTrue(y.op.name.startswith("flip_up_down")) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllEqual(y_tf, y_np) def testUpDownWithBatch(self): @@ -1132,9 +1144,10 @@ class FlipTransposeRotateTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): x_tf = constant_op.constant(x_np, shape=x_np.shape) y = image_ops.flip_up_down(x_tf) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllEqual(y_tf, y_np) + @test_util.run_deprecated_v1 def testRandomFlipUpDown(self): x_np = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.uint8).reshape([2, 3, 1]) y_np = np.array([[4, 5, 6], [1, 2, 3]], dtype=np.uint8).reshape([2, 3, 1]) @@ -1148,7 +1161,7 @@ class FlipTransposeRotateTest(test_util.TensorFlowTestCase): count_flipped = 0 count_unflipped = 0 for _ in range(100): - y_tf = y.eval() + y_tf = self.evaluate(y) if y_tf[0][0] == 1: self.assertAllEqual(y_tf, x_np) count_unflipped += 1 @@ -1163,6 +1176,7 @@ class FlipTransposeRotateTest(test_util.TensorFlowTestCase): self.assertGreaterEqual(count_flipped, 20) self.assertGreaterEqual(count_unflipped, 20) + @test_util.run_deprecated_v1 def testRandomFlipUpDownWithBatch(self): batch_size = 16 seed = 42 @@ -1187,7 +1201,7 @@ class FlipTransposeRotateTest(test_util.TensorFlowTestCase): count_flipped = 0 count_unflipped = 0 for _ in range(100): - y_tf = y.eval() + y_tf = self.evaluate(y) # check every element of the batch for i in range(batch_size): @@ -1213,7 +1227,7 @@ class FlipTransposeRotateTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): x_tf = constant_op.constant(x_np, shape=x_np.shape) y = image_ops.transpose_image(image_ops.transpose_image(x_tf)) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllEqual(y_tf, x_np) def testInvolutionTransposeWithBatch(self): @@ -1224,9 +1238,10 @@ class FlipTransposeRotateTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): x_tf = constant_op.constant(x_np, shape=x_np.shape) y = image_ops.transpose_image(image_ops.transpose_image(x_tf)) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllEqual(y_tf, x_np) + @test_util.run_deprecated_v1 def testTranspose(self): x_np = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.uint8).reshape([2, 3, 1]) y_np = np.array([[1, 4], [2, 5], [3, 6]], dtype=np.uint8).reshape([3, 2, 1]) @@ -1234,8 +1249,8 @@ class FlipTransposeRotateTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): x_tf = constant_op.constant(x_np, shape=x_np.shape) y = image_ops.transpose_image(x_tf) - self.assertTrue(y.op.name.startswith("transpose_image")) - y_tf = y.eval() + self.assertTrue(y.op.name.startswith("transpose")) + y_tf = self.evaluate(y) self.assertAllEqual(y_tf, y_np) def testTransposeWithBatch(self): @@ -1250,9 +1265,10 @@ class FlipTransposeRotateTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): x_tf = constant_op.constant(x_np, shape=x_np.shape) y = image_ops.transpose_image(x_tf) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllEqual(y_tf, y_np) + @test_util.run_deprecated_v1 def testPartialShapes(self): p_unknown_rank = array_ops.placeholder(dtypes.uint8) p_unknown_dims_3 = array_ops.placeholder( @@ -1301,7 +1317,7 @@ class FlipTransposeRotateTest(test_util.TensorFlowTestCase): rotated = image for _ in xrange(4): rotated = image_ops.rot90(rotated) - self.assertAllEqual(image, rotated.eval()) + self.assertAllEqual(image, self.evaluate(rotated)) def testRot90GroupOrderWithBatch(self): image = np.arange(48, dtype=np.uint8).reshape([2, 2, 4, 3]) @@ -1309,8 +1325,9 @@ class FlipTransposeRotateTest(test_util.TensorFlowTestCase): rotated = image for _ in xrange(4): rotated = image_ops.rot90(rotated) - self.assertAllEqual(image, rotated.eval()) + self.assertAllEqual(image, self.evaluate(rotated)) + @test_util.run_deprecated_v1 def testRot90NumpyEquivalence(self): image = np.arange(24, dtype=np.uint8).reshape([2, 4, 3]) with self.test_session(use_gpu=True): @@ -1320,6 +1337,7 @@ class FlipTransposeRotateTest(test_util.TensorFlowTestCase): y_np = np.rot90(image, k=k) self.assertAllEqual(y_np, y_tf.eval({k_placeholder: k})) + @test_util.run_deprecated_v1 def testRot90NumpyEquivalenceWithBatch(self): image = np.arange(48, dtype=np.uint8).reshape([2, 2, 4, 3]) with self.test_session(use_gpu=True): @@ -1335,7 +1353,7 @@ class AdjustContrastTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): x = constant_op.constant(x_np, shape=x_np.shape) y = image_ops.adjust_contrast(x, contrast_factor) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllClose(y_tf, y_np, 1e-6) def testDoubleContrastUint8(self): @@ -1390,7 +1408,7 @@ class AdjustContrastTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): x = constant_op.constant(x_np) y = image_ops.adjust_contrast(x, contrast_factor) - y_tf = y.eval() + y_tf = self.evaluate(y) return y_tf def testRandomContrast(self): @@ -1408,6 +1426,7 @@ class AdjustContrastTest(test_util.TensorFlowTestCase): y_tf = self._adjustContrastTf(x_np, contrast_factor) self.assertAllClose(y_tf, y_np, rtol=1e-5, atol=1e-5) + @test_util.run_deprecated_v1 def testContrastFactorShape(self): x_shape = [1, 2, 2, 3] x_data = [0, 5, 13, 54, 135, 226, 37, 8, 234, 90, 255, 1] @@ -1423,7 +1442,7 @@ class AdjustBrightnessTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): x = constant_op.constant(x_np, shape=x_np.shape) y = image_ops.adjust_brightness(x, delta) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllClose(y_tf, y_np, 1e-6) def testPositiveDeltaUint8(self): @@ -1471,6 +1490,7 @@ class PerImageWhiteningTest(test_util.TensorFlowTestCase): y /= stddev return y + @test_util.run_deprecated_v1 def testBasic(self): x_shape = [13, 9, 3] x_np = np.arange(0, np.prod(x_shape), dtype=np.int32).reshape(x_shape) @@ -1480,7 +1500,7 @@ class PerImageWhiteningTest(test_util.TensorFlowTestCase): x = constant_op.constant(x_np, shape=x_shape) y = image_ops.per_image_standardization(x) self.assertTrue(y.op.name.startswith("per_image_standardization")) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllClose(y_tf, y_np, atol=1e-4) def testUniformImage(self): @@ -1488,7 +1508,7 @@ class PerImageWhiteningTest(test_util.TensorFlowTestCase): im = constant_op.constant(im_np) whiten = image_ops.per_image_standardization(im) with self.test_session(use_gpu=True): - whiten_np = whiten.eval() + whiten_np = self.evaluate(whiten) self.assertFalse(np.any(np.isnan(whiten_np))) def testBatchWhitening(self): @@ -1497,7 +1517,7 @@ class PerImageWhiteningTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): imgs = constant_op.constant(imgs_np) whiten = image_ops.per_image_standardization(imgs) - whiten_tf = whiten.eval() + whiten_tf = self.evaluate(whiten) for w_tf, w_np in zip(whiten_tf, whiten_np): self.assertAllClose(w_tf, w_np, atol=1e-4) @@ -1571,11 +1591,13 @@ class CropToBoundingBoxTest(test_util.TensorFlowTestCase): y = image_ops.crop_to_bounding_box(image, 0, 0, height, width) self.assertEqual(y.get_shape().as_list(), post_shape) + @test_util.run_deprecated_v1 def testNoOp(self): x_shape = [10, 10, 10] x = np.random.uniform(size=x_shape) self._assertReturns(x, x_shape, 0, 0, x, x_shape) + @test_util.run_deprecated_v1 def testCrop(self): x = [1, 2, 3, 4, 5, 6, 7, 8, 9] x_shape = [3, 3, 1] @@ -1600,6 +1622,7 @@ class CropToBoundingBoxTest(test_util.TensorFlowTestCase): y = [1, 2, 4, 5, 7, 8] self._assertReturns(x, x_shape, offset_height, offset_width, y, y_shape) + @test_util.run_deprecated_v1 def testShapeInference(self): self._assertShapeInference([55, 66, 3], 55, 66, [55, 66, 3]) self._assertShapeInference([59, 69, 3], 55, 66, [55, 66, 3]) @@ -1613,6 +1636,7 @@ class CropToBoundingBoxTest(test_util.TensorFlowTestCase): self._assertShapeInference([None, None, None], 55, 66, [55, 66, None]) self._assertShapeInference(None, 55, 66, [55, 66, None]) + @test_util.run_deprecated_v1 def testNon3DInput(self): # Input image is not 3D x = [0] * 15 @@ -1624,6 +1648,7 @@ class CropToBoundingBoxTest(test_util.TensorFlowTestCase): target_width, "'image' must have either 3 or 4 dimensions.") + @test_util.run_deprecated_v1 def testZeroLengthInput(self): # Input image has 0-length dimension(s). # Each line is a test configuration: @@ -1655,6 +1680,7 @@ class CropToBoundingBoxTest(test_util.TensorFlowTestCase): "assertion failed:", use_tensor_inputs_options=[True]) + @test_util.run_deprecated_v1 def testBadParams(self): x_shape = [4, 4, 1] x = np.zeros(x_shape) @@ -1672,6 +1698,7 @@ class CropToBoundingBoxTest(test_util.TensorFlowTestCase): for params, err_msg in test_config: self._assertRaises(x, x_shape, *params, err_msg=err_msg) + @test_util.run_deprecated_v1 def testNameScope(self): image = array_ops.placeholder(dtypes.float32, shape=[55, 66, 3]) y = image_ops.crop_to_bounding_box(image, 0, 0, 55, 66) @@ -1688,6 +1715,7 @@ class CentralCropTest(test_util.TensorFlowTestCase): else: self.assertEqual(y.get_shape().as_list(), post_shape) + @test_util.run_deprecated_v1 def testNoOp(self): x_shapes = [[13, 9, 3], [5, 13, 9, 3]] for x_shape in x_shapes: @@ -1696,7 +1724,7 @@ class CentralCropTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=use_gpu): x = constant_op.constant(x_np, shape=x_shape) y = image_ops.central_crop(x, 1.0) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllEqual(y_tf, x_np) self.assertEqual(y.op.name, x.op.name) @@ -1711,7 +1739,7 @@ class CentralCropTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=use_gpu): x = constant_op.constant(x_np, shape=x_shape) y = image_ops.central_crop(x, 0.5) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllEqual(y_tf, y_np) self.assertAllEqual(y_tf.shape, y_np.shape) @@ -1727,10 +1755,11 @@ class CentralCropTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): x = constant_op.constant(x_np, shape=x_shape) y = image_ops.central_crop(x, 0.5) - y_tf = y.eval() + y_tf = self.evaluate(y) self.assertAllEqual(y_tf, y_np) self.assertAllEqual(y_tf.shape, y_np.shape) + @test_util.run_deprecated_v1 def testCropping2(self): # Test case for 10315 x_shapes = [[240, 320, 3], [5, 240, 320, 3]] @@ -1747,6 +1776,7 @@ class CentralCropTest(test_util.TensorFlowTestCase): self.assertAllEqual(y_tf, y_np) self.assertAllEqual(y_tf.shape, y_np.shape) + @test_util.run_deprecated_v1 def testShapeInference(self): # Test no-op fraction=1.0, with 3-D tensors. self._assertShapeInference([50, 60, 3], 1.0, [50, 60, 3]) @@ -1807,6 +1837,7 @@ class CentralCropTest(test_util.TensorFlowTestCase): with self.assertRaises(ValueError): _ = image_ops.central_crop(x, 0.5) + @test_util.run_deprecated_v1 def testNameScope(self): x_shape = [13, 9, 3] x_np = np.ones(x_shape, dtype=np.float32) @@ -1897,14 +1928,16 @@ class PadToBoundingBoxTest(test_util.TensorFlowTestCase): i = constant_op.constant([1, 0, 4, 3], dtype=dtypes.int64) y_tf = image_ops.pad_to_bounding_box(x, i[0], i[1], i[2], i[3]) with self.test_session(use_gpu=True): - self.assertAllClose(y, y_tf.eval()) + self.assertAllClose(y, self.evaluate(y_tf)) + @test_util.run_deprecated_v1 def testNoOp(self): x_shape = [10, 10, 10] x = np.random.uniform(size=x_shape) offset_height, offset_width = [0, 0] self._assertReturns(x, x_shape, offset_height, offset_width, x, x_shape) + @test_util.run_deprecated_v1 def testPadding(self): x = [1, 2, 3, 4, 5, 6, 7, 8, 9] x_shape = [3, 3, 1] @@ -1929,6 +1962,7 @@ class PadToBoundingBoxTest(test_util.TensorFlowTestCase): y_shape = [3, 4, 1] self._assertReturns(x, x_shape, offset_height, offset_width, y, y_shape) + @test_util.run_deprecated_v1 def testShapeInference(self): self._assertShapeInference([55, 66, 3], 55, 66, [55, 66, 3]) self._assertShapeInference([50, 60, 3], 55, 66, [55, 66, 3]) @@ -1942,6 +1976,7 @@ class PadToBoundingBoxTest(test_util.TensorFlowTestCase): self._assertShapeInference([None, None, None], 55, 66, [55, 66, None]) self._assertShapeInference(None, 55, 66, [55, 66, None]) + @test_util.run_deprecated_v1 def testNon3DInput(self): # Input image is not 3D x = [0] * 15 @@ -1953,6 +1988,7 @@ class PadToBoundingBoxTest(test_util.TensorFlowTestCase): target_width, "'image' must have either 3 or 4 dimensions.") + @test_util.run_deprecated_v1 def testZeroLengthInput(self): # Input image has 0-length dimension(s). # Each line is a test configuration: @@ -1985,6 +2021,7 @@ class PadToBoundingBoxTest(test_util.TensorFlowTestCase): "all dims of \\'image.shape\\' must be > 0", use_tensor_inputs_options=[True]) + @test_util.run_deprecated_v1 def testBadParams(self): x_shape = [3, 3, 1] x = np.zeros(x_shape) @@ -1999,6 +2036,7 @@ class PadToBoundingBoxTest(test_util.TensorFlowTestCase): for config_item in test_config: self._assertRaises(x, x_shape, *config_item) + @test_util.run_deprecated_v1 def testNameScope(self): image = array_ops.placeholder(dtypes.float32, shape=[55, 66, 3]) y = image_ops.pad_to_bounding_box(image, 0, 0, 55, 66) @@ -2040,7 +2078,7 @@ class SelectDistortedCropBoxTest(test_util.TensorFlowTestCase): y = array_ops.strided_slice(image_tf, begin, begin + size) for _ in xrange(num_iter): - y_tf = y.eval() + y_tf = self.evaluate(y) crop_height = y_tf.shape[0] crop_width = y_tf.shape[1] aspect_ratio = float(crop_width) / float(crop_height) @@ -2106,6 +2144,7 @@ class SelectDistortedCropBoxTest(test_util.TensorFlowTestCase): # TODO(wicke, shlens, dga): Restore this test so that it is no longer flaky. # self.assertGreaterEqual(min(fraction_object_covered), min_object_covered) + @test_util.run_deprecated_v1 def testWholeImageBoundingBox(self): height = 40 width = 50 @@ -2120,6 +2159,7 @@ class SelectDistortedCropBoxTest(test_util.TensorFlowTestCase): aspect_ratio_range=(0.75, 1.33), area_range=(0.05, 1.0)) + @test_util.run_deprecated_v1 def testWithBoundingBox(self): height = 40 width = 50 @@ -2150,6 +2190,7 @@ class SelectDistortedCropBoxTest(test_util.TensorFlowTestCase): aspect_ratio_range=(0.75, 1.33), area_range=(0.05, 1.0)) + @test_util.run_deprecated_v1 def testSampleDistortedBoundingBoxShape(self): with self.test_session(use_gpu=True): image_size = constant_op.constant( @@ -2171,9 +2212,9 @@ class SelectDistortedCropBoxTest(test_util.TensorFlowTestCase): self.assertAllEqual([3], end.get_shape().as_list()) self.assertAllEqual([1, 1, 4], bbox_for_drawing.get_shape().as_list()) # Actual run to make sure shape is correct inside Compute(). - begin = begin.eval() - end = end.eval() - bbox_for_drawing = bbox_for_drawing.eval() + begin = self.evaluate(begin) + end = self.evaluate(end) + bbox_for_drawing = self.evaluate(bbox_for_drawing) begin, end, bbox_for_drawing = image_ops.sample_distorted_bounding_box( image_size=image_size, @@ -2207,9 +2248,9 @@ class SelectDistortedCropBoxTest(test_util.TensorFlowTestCase): self.assertAllEqual([3], end.get_shape().as_list()) self.assertAllEqual([1, 1, 4], bbox_for_drawing.get_shape().as_list()) # Actual run to make sure shape is correct inside Compute(). - begin = begin.eval() - end = end.eval() - bbox_for_drawing = bbox_for_drawing.eval() + begin = self.evaluate(begin) + end = self.evaluate(end) + bbox_for_drawing = self.evaluate(bbox_for_drawing) class ResizeImagesTest(test_util.TensorFlowTestCase): @@ -2245,6 +2286,7 @@ class ResizeImagesTest(test_util.TensorFlowTestCase): else: return False + @test_util.run_deprecated_v1 def testNoOp(self): img_shape = [1, 6, 4, 1] single_shape = [6, 4, 1] @@ -2265,7 +2307,7 @@ class ResizeImagesTest(test_util.TensorFlowTestCase): image = constant_op.constant(img_np, shape=img_shape) y = image_ops.resize_images(image, [target_height, target_width], opt) yshape = array_ops.shape(y) - resized, newshape = sess.run([y, yshape]) + resized, newshape = self.evaluate([y, yshape]) self.assertAllEqual(img_shape, newshape) self.assertAllClose(resized, img_np, atol=1e-5) @@ -2276,9 +2318,10 @@ class ResizeImagesTest(test_util.TensorFlowTestCase): y = image_ops.resize_images(image, [target_height, target_width], self.OPTIONS[0]) yshape = array_ops.shape(y) - newshape = yshape.eval() + newshape = self.evaluate(yshape) self.assertAllEqual(single_shape, newshape) + @test_util.run_deprecated_v1 def testTensorArguments(self): img_shape = [1, 6, 4, 1] single_shape = [6, 4, 1] @@ -2340,6 +2383,7 @@ class ResizeImagesTest(test_util.TensorFlowTestCase): _ = image_ops.resize_images(image, [6, None], image_ops.ResizeMethod.BILINEAR) + @test_util.run_deprecated_v1 def testReturnDtype(self): target_shapes = [[6, 4], [3, 2], [ array_ops.placeholder(dtypes.int32), @@ -2379,7 +2423,7 @@ class ResizeImagesTest(test_util.TensorFlowTestCase): image = constant_op.constant(img_np, shape=img_shape) y = image_ops.resize_images(image, [height, width], opt) yshape = array_ops.shape(y) - resized, newshape = sess.run([y, yshape]) + resized, newshape = self.evaluate([y, yshape]) self.assertAllEqual(img_shape, newshape) self.assertAllClose(resized, img_np, atol=1e-5) @@ -2411,7 +2455,7 @@ class ResizeImagesTest(test_util.TensorFlowTestCase): y = image_ops.resize_images(image, [target_height, target_width], opt) expected = np.array(expected_data).reshape(target_shape) - resized = y.eval() + resized = self.evaluate(y) self.assertAllClose(resized, expected, atol=1e-5) def testResizeUpAlignCornersFalse(self): @@ -2446,7 +2490,7 @@ class ResizeImagesTest(test_util.TensorFlowTestCase): image = constant_op.constant(img_np, shape=img_shape) y = image_ops.resize_images( image, [target_height, target_width], opt, align_corners=False) - resized = y.eval() + resized = self.evaluate(y) expected = np.array(expected_data[opt]).reshape( [1, target_height, target_width, 1]) self.assertAllClose(resized, expected, atol=1e-05) @@ -2482,7 +2526,7 @@ class ResizeImagesTest(test_util.TensorFlowTestCase): image = constant_op.constant(img_np, shape=img_shape) y = image_ops.resize_images( image, [target_height, target_width], opt, align_corners=True) - resized = y.eval() + resized = self.evaluate(y) expected = np.array(expected_data[opt]).reshape( [1, target_height, target_width, 1]) self.assertAllClose(resized, expected, atol=1e-05) @@ -2509,7 +2553,7 @@ class ResizeImagesTest(test_util.TensorFlowTestCase): image = constant_op.constant(img_np, shape=img_shape) y = image_ops.resize_images(image, [target_height, target_width], image_ops.ResizeMethod.BICUBIC) - resized = y.eval() + resized = self.evaluate(y) expected = np.array(expected_data).reshape( [1, target_height, target_width, 1]) self.assertAllClose(resized, expected, atol=1) @@ -2534,7 +2578,7 @@ class ResizeImagesTest(test_util.TensorFlowTestCase): image_ops.ResizeMethod.AREA) expected = np.array(expected_data).reshape( [1, target_height, target_width, 1]) - resized = y.eval() + resized = self.evaluate(y) self.assertAllClose(resized, expected, atol=1) def testCompareNearestNeighbor(self): @@ -2554,7 +2598,7 @@ class ResizeImagesTest(test_util.TensorFlowTestCase): new_size, image_ops.ResizeMethod.NEAREST_NEIGHBOR, align_corners=align_corners) - gpu_val = out_op.eval() + gpu_val = self.evaluate(out_op) with self.test_session(use_gpu=False): image = constant_op.constant(img_np, shape=input_shape) new_size = constant_op.constant([target_height, target_width]) @@ -2563,7 +2607,7 @@ class ResizeImagesTest(test_util.TensorFlowTestCase): new_size, image_ops.ResizeMethod.NEAREST_NEIGHBOR, align_corners=align_corners) - cpu_val = out_op.eval() + cpu_val = self.evaluate(out_op) self.assertAllClose(cpu_val, gpu_val, rtol=1e-5, atol=1e-5) def testCompareBilinear(self): @@ -2585,9 +2629,10 @@ class ResizeImagesTest(test_util.TensorFlowTestCase): new_size, image_ops.ResizeMethod.BILINEAR, align_corners=align_corners) - value[use_gpu] = out_op.eval() + value[use_gpu] = self.evaluate(out_op) self.assertAllClose(value[True], value[False], rtol=1e-5, atol=1e-5) + @test_util.run_deprecated_v1 def testShapeInference(self): self._assertShapeInference([50, 60, 3], [55, 66], [55, 66, 3]) self._assertShapeInference([55, 66, 3], [55, 66], [55, 66, 3]) @@ -2608,12 +2653,13 @@ class ResizeImagesTest(test_util.TensorFlowTestCase): self._assertShapeInference([59, 60, None], [55, 66], [55, 66, None]) self._assertShapeInference([None, None, None], [55, 66], [55, 66, None]) + @test_util.run_deprecated_v1 def testNameScope(self): img_shape = [1, 3, 2, 1] with self.test_session(use_gpu=True): single_image = array_ops.placeholder(dtypes.float32, shape=[50, 60, 3]) y = image_ops.resize_images(single_image, [55, 66]) - self.assertTrue(y.op.name.startswith("resize_images")) + self.assertTrue(y.op.name.startswith("resize")) def _ResizeImageCall(self, x, max_h, max_w, preserve_aspect_ratio, use_tensor_inputs): @@ -2658,6 +2704,7 @@ class ResizeImagesTest(test_util.TensorFlowTestCase): preserve_aspect_ratio, use_tensor_inputs) self.assertShapeEqual(y, ops.convert_to_tensor(y_tf)) + @test_util.run_deprecated_v1 def testPreserveAspectRatioMultipleImages(self): x_shape = [10, 100, 100, 10] x = np.random.uniform(size=x_shape) @@ -2665,36 +2712,42 @@ class ResizeImagesTest(test_util.TensorFlowTestCase): self._assertResizeCheckShape(x, x_shape, [250, 250], [10, 250, 250, 10], preserve_aspect_ratio=False) + @test_util.run_deprecated_v1 def testPreserveAspectRatioNoOp(self): x_shape = [10, 10, 10] x = np.random.uniform(size=x_shape) self._assertResizeEqual(x, x_shape, x, x_shape) + @test_util.run_deprecated_v1 def testPreserveAspectRatioSmaller(self): x_shape = [100, 100, 10] x = np.random.uniform(size=x_shape) self._assertResizeCheckShape(x, x_shape, [75, 50], [50, 50, 10]) + @test_util.run_deprecated_v1 def testPreserveAspectRatioSmallerMultipleImages(self): x_shape = [10, 100, 100, 10] x = np.random.uniform(size=x_shape) self._assertResizeCheckShape(x, x_shape, [75, 50], [10, 50, 50, 10]) + @test_util.run_deprecated_v1 def testPreserveAspectRatioLarger(self): x_shape = [100, 100, 10] x = np.random.uniform(size=x_shape) self._assertResizeCheckShape(x, x_shape, [150, 200], [150, 150, 10]) + @test_util.run_deprecated_v1 def testPreserveAspectRatioSameRatio(self): x_shape = [1920, 1080, 3] x = np.random.uniform(size=x_shape) self._assertResizeCheckShape(x, x_shape, [3840, 2160], [3840, 2160, 3]) + @test_util.run_deprecated_v1 def testPreserveAspectRatioSquare(self): x_shape = [299, 299, 3] x = np.random.uniform(size=x_shape) @@ -2764,12 +2817,14 @@ class ResizeImageWithPadTest(test_util.TensorFlowTestCase): y = image_ops.resize_image_with_pad(image, height, width) self.assertEqual(y.get_shape().as_list(), post_shape) + @test_util.run_deprecated_v1 def testNoOp(self): x_shape = [10, 10, 10] x = np.random.uniform(size=x_shape) self._assertReturns(x, x_shape, x, x_shape) + @test_util.run_deprecated_v1 def testPad(self): # Reduce vertical dimension x = [1, 2, 3, 4, 5, 6, 7, 8] @@ -2860,12 +2915,14 @@ class ResizeImageWithCropOrPadTest(test_util.TensorFlowTestCase): y = image_ops.resize_image_with_crop_or_pad(image, height, width) self.assertEqual(y.get_shape().as_list(), post_shape) + @test_util.run_deprecated_v1 def testNoOp(self): x_shape = [10, 10, 10] x = np.random.uniform(size=x_shape) self._assertReturns(x, x_shape, x, x_shape) + @test_util.run_deprecated_v1 def testPad(self): # Pad even along col. x = [1, 2, 3, 4, 5, 6, 7, 8] @@ -2903,6 +2960,7 @@ class ResizeImageWithCropOrPadTest(test_util.TensorFlowTestCase): self._assertReturns(x, x_shape, y, y_shape) + @test_util.run_deprecated_v1 def testCrop(self): # Crop even along col. x = [1, 2, 3, 4, 5, 6, 7, 8] @@ -2940,6 +2998,7 @@ class ResizeImageWithCropOrPadTest(test_util.TensorFlowTestCase): self._assertReturns(x, x_shape, y, y_shape) + @test_util.run_deprecated_v1 def testCropAndPad(self): # Pad along row but crop along col. x = [1, 2, 3, 4, 5, 6, 7, 8] @@ -2959,6 +3018,7 @@ class ResizeImageWithCropOrPadTest(test_util.TensorFlowTestCase): self._assertReturns(x, x_shape, y, y_shape) + @test_util.run_deprecated_v1 def testShapeInference(self): self._assertShapeInference([50, 60, 3], 55, 66, [55, 66, 3]) self._assertShapeInference([55, 66, 3], 55, 66, [55, 66, 3]) @@ -2980,6 +3040,7 @@ class ResizeImageWithCropOrPadTest(test_util.TensorFlowTestCase): self._assertShapeInference([None, None, None], 55, 66, [55, 66, None]) self._assertShapeInference(None, 55, 66, [55, 66, None]) + @test_util.run_deprecated_v1 def testNon3DInput(self): # Input image is not 3D x = [0] * 15 @@ -2993,6 +3054,7 @@ class ResizeImageWithCropOrPadTest(test_util.TensorFlowTestCase): self._assertRaises(x, x_shape, target_height, target_width, "'image' must have either 3 or 4 dimensions.") + @test_util.run_deprecated_v1 def testZeroLengthInput(self): # Input image has 0-length dimension(s). target_height, target_width = [1, 1] @@ -3018,6 +3080,7 @@ class ResizeImageWithCropOrPadTest(test_util.TensorFlowTestCase): "all dims of \\'image.shape\\' must be > 0", use_tensor_inputs_options=[True]) + @test_util.run_deprecated_v1 def testBadParams(self): x_shape = [4, 4, 1] x = np.zeros(x_shape) @@ -3032,6 +3095,7 @@ class ResizeImageWithCropOrPadTest(test_util.TensorFlowTestCase): self._assertRaises(x, x_shape, target_height, target_width, "target_width must be > 0") + @test_util.run_deprecated_v1 def testNameScope(self): image = array_ops.placeholder(dtypes.float32, shape=[50, 60, 3]) y = image_ops.resize_image_with_crop_or_pad(image, 55, 66) @@ -3066,7 +3130,7 @@ class JpegTest(test_util.TensorFlowTestCase): jpeg0 = io_ops.read_file(path) image0 = image_ops.decode_jpeg(jpeg0) image1 = image_ops.decode_jpeg(image_ops.encode_jpeg(image0)) - jpeg0, image0, image1 = sess.run([jpeg0, image0, image1]) + jpeg0, image0, image1 = self.evaluate([jpeg0, image0, image1]) self.assertEqual(len(jpeg0), 3771) self.assertEqual(image0.shape, (256, 128, 3)) self.assertLess(self.averageError(image0, image1), 1.4) @@ -3083,7 +3147,7 @@ class JpegTest(test_util.TensorFlowTestCase): io_ops.read_file(rgb_path), channels=channels) cmyk = image_ops.decode_jpeg( io_ops.read_file(cmyk_path), channels=channels) - rgb, cmyk = sess.run([rgb, cmyk]) + rgb, cmyk = self.evaluate([rgb, cmyk]) self.assertEqual(rgb.shape, shape) self.assertEqual(cmyk.shape, shape) error = self.averageError(rgb, cmyk) @@ -3112,9 +3176,10 @@ class JpegTest(test_util.TensorFlowTestCase): image2.get_shape().as_list()) # CropAndDecode should be equal to DecodeJpeg+Crop. - image1_crop, image2 = sess.run([image1_crop, image2]) + image1_crop, image2 = self.evaluate([image1_crop, image2]) self.assertAllEqual(image1_crop, image2) + @test_util.run_deprecated_v1 def testCropAndDecodeJpegWithInvalidCropWindow(self): with self.cached_session() as sess: # Encode it, then decode it, then encode it @@ -3131,7 +3196,7 @@ class JpegTest(test_util.TensorFlowTestCase): with self.assertRaisesWithPredicateMatch( errors.InvalidArgumentError, lambda e: "Invalid JPEG data or crop window" in str(e)): - sess.run(result) + self.evaluate(result) def testSynthetic(self): with self.test_session(use_gpu=True) as sess: @@ -3141,7 +3206,8 @@ class JpegTest(test_util.TensorFlowTestCase): image1 = image_ops.decode_jpeg(jpeg0, dct_method="INTEGER_ACCURATE") image2 = image_ops.decode_jpeg( image_ops.encode_jpeg(image1), dct_method="INTEGER_ACCURATE") - jpeg0, image0, image1, image2 = sess.run([jpeg0, image0, image1, image2]) + jpeg0, image0, image1, image2 = self.evaluate( + [jpeg0, image0, image1, image2]) # The decoded-encoded image should be similar to the input self.assertLess(self.averageError(image0, image1), 0.6) @@ -3161,7 +3227,8 @@ class JpegTest(test_util.TensorFlowTestCase): image1 = image_ops.decode_jpeg(jpeg0, dct_method="INTEGER_FAST") image2 = image_ops.decode_jpeg( image_ops.encode_jpeg(image1), dct_method="INTEGER_FAST") - jpeg0, image0, image1, image2 = sess.run([jpeg0, image0, image1, image2]) + jpeg0, image0, image1, image2 = self.evaluate( + [jpeg0, image0, image1, image2]) # The decoded-encoded image should be similar to the input, but # note this is worse than the slower algorithm because it is @@ -3184,11 +3251,12 @@ class JpegTest(test_util.TensorFlowTestCase): jpeg0 = image_ops.encode_jpeg(image0) image1 = image_ops.decode_jpeg(jpeg0, dct_method="INTEGER_FAST") image2 = image_ops.decode_jpeg(jpeg0) - image1, image2 = sess.run([image1, image2]) + image1, image2 = self.evaluate([image1, image2]) # The images should be the same. self.assertAllClose(image1, image2) + @test_util.run_deprecated_v1 def testShape(self): with self.test_session(use_gpu=True) as sess: jpeg = constant_op.constant("nonsense") @@ -3197,6 +3265,7 @@ class JpegTest(test_util.TensorFlowTestCase): self.assertEqual(image.get_shape().as_list(), [None, None, channels or None]) + @test_util.run_deprecated_v1 def testExtractJpegShape(self): # Read a real jpeg and verify shape. path = ("tensorflow/core/lib/jpeg/testdata/" @@ -3207,6 +3276,7 @@ class JpegTest(test_util.TensorFlowTestCase): [image_shape] = sess.run([image_ops.extract_jpeg_shape(jpeg)]) self.assertEqual(image_shape.tolist(), [256, 128, 3]) + @test_util.run_deprecated_v1 def testExtractJpegShapeforCmyk(self): # Read a cmyk jpeg image, and verify its shape. path = ("tensorflow/core/lib/jpeg/testdata/" @@ -3230,11 +3300,11 @@ class PngTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True) as sess: png0 = io_ops.read_file(prefix + filename) image0 = image_ops.decode_png(png0, channels=channels) - png0, image0 = sess.run([png0, image0]) + png0, image0 = self.evaluate([png0, image0]) self.assertEqual(image0.shape, (26, 51, channels or channels_in)) if channels == channels_in: image1 = image_ops.decode_png(image_ops.encode_png(image0)) - self.assertAllEqual(image0, image1.eval()) + self.assertAllEqual(image0, self.evaluate(image1)) def testSynthetic(self): with self.test_session(use_gpu=True) as sess: @@ -3242,7 +3312,7 @@ class PngTest(test_util.TensorFlowTestCase): image0 = constant_op.constant(_SimpleColorRamp()) png0 = image_ops.encode_png(image0, compression=7) image1 = image_ops.decode_png(png0) - png0, image0, image1 = sess.run([png0, image0, image1]) + png0, image0, image1 = self.evaluate([png0, image0, image1]) # PNG is lossless self.assertAllEqual(image0, image1) @@ -3257,7 +3327,7 @@ class PngTest(test_util.TensorFlowTestCase): image0 = constant_op.constant(_SimpleColorRamp(), dtype=dtypes.uint16) png0 = image_ops.encode_png(image0, compression=7) image1 = image_ops.decode_png(png0, dtype=dtypes.uint16) - png0, image0, image1 = sess.run([png0, image0, image1]) + png0, image0, image1 = self.evaluate([png0, image0, image1]) # PNG is lossless self.assertAllEqual(image0, image1) @@ -3273,7 +3343,7 @@ class PngTest(test_util.TensorFlowTestCase): image0 = constant_op.constant(gray_alpha) png0 = image_ops.encode_png(image0, compression=7) image1 = image_ops.decode_png(png0) - png0, image0, image1 = sess.run([png0, image0, image1]) + png0, image0, image1 = self.evaluate([png0, image0, image1]) self.assertEqual(2, image0.shape[-1]) self.assertAllEqual(image0, image1) @@ -3284,10 +3354,11 @@ class PngTest(test_util.TensorFlowTestCase): image0 = constant_op.constant(gray_alpha, dtype=dtypes.uint16) png0 = image_ops.encode_png(image0, compression=7) image1 = image_ops.decode_png(png0, dtype=dtypes.uint16) - png0, image0, image1 = sess.run([png0, image0, image1]) + png0, image0, image1 = self.evaluate([png0, image0, image1]) self.assertEqual(2, image0.shape[-1]) self.assertAllEqual(image0, image1) + @test_util.run_deprecated_v1 def testShape(self): with self.test_session(use_gpu=True): png = constant_op.constant("nonsense") @@ -3310,7 +3381,7 @@ class GifTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True) as sess: gif0 = io_ops.read_file(prefix + filename) image0 = image_ops.decode_gif(gif0) - gif0, image0 = sess.run([gif0, image0]) + gif0, image0 = self.evaluate([gif0, image0]) self.assertEqual(image0.shape, shape) @@ -3332,6 +3403,7 @@ class GifTest(test_util.TensorFlowTestCase): self._testValid("scan.gif") self._testValid("optimized.gif") + @test_util.run_deprecated_v1 def testShape(self): with self.test_session(use_gpu=True) as sess: gif = constant_op.constant("nonsense") @@ -3358,6 +3430,7 @@ class ConvertImageTest(test_util.TensorFlowTestCase): self.assertTrue(y_saturate.dtype == output_dtype) self.assertAllClose(y_saturate.eval(), y_np, atol=1e-5) + @test_util.run_deprecated_v1 def testNoConvert(self): # Make sure converting to the same data type creates only an identity op with self.test_session(use_gpu=True): @@ -3367,6 +3440,7 @@ class ConvertImageTest(test_util.TensorFlowTestCase): self.assertEquals(y.op.type, "Identity") self.assertEquals(y.op.inputs[0], image) + @test_util.run_deprecated_v1 def testConvertBetweenInteger(self): # Make sure converting to between integer types scales appropriately with self.test_session(use_gpu=True): @@ -3375,6 +3449,7 @@ class ConvertImageTest(test_util.TensorFlowTestCase): self._convert([0, 2**32], dtypes.int64, dtypes.int32, [0, 1]) self._convert([0, 1], dtypes.int32, dtypes.int64, [0, 2**32]) + @test_util.run_deprecated_v1 def testConvertBetweenFloat(self): # Make sure converting to between float types does nothing interesting with self.test_session(use_gpu=True): @@ -3383,6 +3458,7 @@ class ConvertImageTest(test_util.TensorFlowTestCase): self._convert([-1.0, 0, 1.0, 200000], dtypes.float64, dtypes.float32, [-1.0, 0, 1.0, 200000]) + @test_util.run_deprecated_v1 def testConvertBetweenIntegerAndFloat(self): # Make sure converting from and to a float type scales appropriately with self.test_session(use_gpu=True): @@ -3391,6 +3467,7 @@ class ConvertImageTest(test_util.TensorFlowTestCase): self._convert([0, 1.1 / 255.0, 1], dtypes.float32, dtypes.uint8, [0, 1, 255]) + @test_util.run_deprecated_v1 def testConvertBetweenInt16AndInt8(self): with self.test_session(use_gpu=True): # uint8, uint16 @@ -3431,7 +3508,7 @@ class TotalVariationTest(test_util.TensorFlowTestCase): y = image_ops.total_variation(images=x_tf) # Run the TensorFlow session to calculate the result. - y_tf = y.eval() + y_tf = self.evaluate(y) # Assert that the results are as expected within # some small error-bound in case they are float-values. @@ -3582,6 +3659,7 @@ class TotalVariationTest(test_util.TensorFlowTestCase): class FormatTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testFormats(self): prefix = "tensorflow/core/lib" paths = ("png/testdata/lena_gray.png", "jpeg/testdata/jpeg_merge_test1.jpg", @@ -3614,6 +3692,7 @@ class FormatTest(test_util.TensorFlowTestCase): class NonMaxSuppressionTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testSelectFromThreeClusters(self): boxes_np = [[0, 0, 1, 1], [0, 0.1, 1, 1.1], [0, -0.1, 1, 0.9], [0, 10, 1, 11], [0, 10.1, 1, 11.1], [0, 100, 1, 101]] @@ -3629,6 +3708,7 @@ class NonMaxSuppressionTest(test_util.TensorFlowTestCase): boxes, scores, max_output_size, iou_threshold).eval() self.assertAllClose(selected_indices, [3, 0, 5]) + @test_util.run_deprecated_v1 def testInvalidShape(self): # The boxes should be 2D of shape [num_boxes, 4]. with self.assertRaisesRegexp(ValueError, @@ -3671,6 +3751,7 @@ class NonMaxSuppressionTest(test_util.TensorFlowTestCase): scores = constant_op.constant([0.9]) image_ops.non_max_suppression(boxes, scores, 3, [[0.5]]) + @test_util.run_deprecated_v1 def testDataTypes(self): # Test case for GitHub issue 20199. boxes_np = [[0, 0, 1, 1], [0, 0.1, 1, 1.1], [0, -0.1, 1, 0.9], @@ -3709,12 +3790,13 @@ class NonMaxSuppressionTest(test_util.TensorFlowTestCase): iou_threshold = constant_op.constant(iou_threshold_np) selected_indices, _ = gen_image_ops.non_max_suppression_v4( boxes, scores, max_output_size, iou_threshold, score_threshold) - selected_indices = selected_indices.eval() + selected_indices = self.evaluate(selected_indices) self.assertAllClose(selected_indices, [3, 0, 5]) class NonMaxSuppressionPaddedTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testSelectFromThreeClusters(self): boxes_np = [[0, 0, 1, 1], [0, 0.1, 1, 1.1], [0, -0.1, 1, 0.9], [0, 10, 1, 11], [0, 10.1, 1, 11.1], [0, 100, 1, 101]] @@ -3747,6 +3829,7 @@ class NonMaxSuppressionPaddedTest(test_util.TensorFlowTestCase): self.assertAllClose(selected_indices.eval(), [3, 0, 5]) self.assertEqual(num_valid.eval(), 3) + @test_util.run_deprecated_v1 def testSelectFromContinuousOverLap(self): boxes_np = [[0, 0, 1, 1], [0, 0.2, 1, 1.2], [0, 0.4, 1, 1.4], [0, 0.6, 1, 1.6], [0, 0.8, 1, 1.8], [0, 2, 1, 2]] @@ -3774,6 +3857,7 @@ class NonMaxSuppressionPaddedTest(test_util.TensorFlowTestCase): class NonMaxSuppressionWithOverlapsTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testSelectOneFromThree(self): overlaps_np = [ [1.0, 0.7, 0.2], @@ -3799,6 +3883,7 @@ class NonMaxSuppressionWithOverlapsTest(test_util.TensorFlowTestCase): class VerifyCompatibleImageShapesTest(test_util.TensorFlowTestCase): """Tests utility function used by ssim() and psnr().""" + @test_util.run_deprecated_v1 def testWrongDims(self): img = array_ops.placeholder(dtype=dtypes.float32) img_np = np.array((2, 2)) @@ -3808,6 +3893,7 @@ class VerifyCompatibleImageShapesTest(test_util.TensorFlowTestCase): with self.assertRaises(errors.InvalidArgumentError): sess.run(checks, {img: img_np}) + @test_util.run_deprecated_v1 def testShapeMismatch(self): img1 = array_ops.placeholder(dtype=dtypes.float32) img2 = array_ops.placeholder(dtype=dtypes.float32) @@ -3829,7 +3915,7 @@ class PSNRTest(test_util.TensorFlowTestCase): "tensorflow/core/lib/psnr/testdata", filename)) im = image_ops.decode_jpeg(content, dct_method="INTEGER_ACCURATE") im = image_ops.convert_image_dtype(im, dtypes.float32) - im, = sess.run([im]) + im, = self.evaluate([im]) return np.expand_dims(im, axis=0) def _LoadTestImages(self): @@ -3848,6 +3934,7 @@ class PSNRTest(test_util.TensorFlowTestCase): """Returns an image or image batch with given shape.""" return np.random.rand(*shape).astype(np.float32) * max_val + @test_util.run_deprecated_v1 def testPSNRSingleImage(self): image1 = self._RandomImage((8, 8, 1), 1) image2 = self._RandomImage((8, 8, 1), 1) @@ -3861,6 +3948,7 @@ class PSNRTest(test_util.TensorFlowTestCase): tf_psnr = image_ops.psnr(tf_image1, tf_image2, 1.0, "psnr").eval() self.assertAllClose(psnr, tf_psnr, atol=0.001) + @test_util.run_deprecated_v1 def testPSNRMultiImage(self): image1 = self._RandomImage((10, 8, 8, 1), 1) image2 = self._RandomImage((10, 8, 8, 1), 1) @@ -3874,6 +3962,7 @@ class PSNRTest(test_util.TensorFlowTestCase): tf_psnr = image_ops.psnr(tf_image1, tf_image2, 1, "psnr").eval() self.assertAllClose(psnr, tf_psnr, atol=0.001) + @test_util.run_deprecated_v1 def testGoldenPSNR(self): q20, q72, q95 = self._LoadTestImages() @@ -3898,6 +3987,7 @@ class PSNRTest(test_util.TensorFlowTestCase): self.assertAllClose(psnr2, tf_psnr2, atol=0.001) self.assertAllClose(psnr3, tf_psnr3, atol=0.001) + @test_util.run_deprecated_v1 def testInfinity(self): q20, _, _ = self._LoadTestImages() psnr = self._PSNR_NumPy(q20, q20, 1) @@ -3906,6 +3996,7 @@ class PSNRTest(test_util.TensorFlowTestCase): tf_psnr = image_ops.psnr(tf_q20, tf_q20, 1, "psnr").eval() self.assertAllClose(psnr, tf_psnr, atol=0.001) + @test_util.run_deprecated_v1 def testInt(self): img1 = self._RandomImage((10, 8, 8, 1), 255) img2 = self._RandomImage((10, 8, 8, 1), 255) @@ -3916,7 +4007,8 @@ class PSNRTest(test_util.TensorFlowTestCase): img2 = image_ops.convert_image_dtype(img2, dtypes.float32) psnr_float32 = image_ops.psnr(img1, img2, 1.0) with self.test_session(use_gpu=True): - self.assertAllClose(psnr_uint8.eval(), psnr_float32.eval(), atol=0.001) + self.assertAllClose( + psnr_uint8.eval(), self.evaluate(psnr_float32), atol=0.001) class SSIMTest(test_util.TensorFlowTestCase): @@ -3935,7 +4027,7 @@ class SSIMTest(test_util.TensorFlowTestCase): "tensorflow/core/lib/ssim/testdata", filename)) im = image_ops.decode_png(content) im = image_ops.convert_image_dtype(im, dtypes.float32) - im, = sess.run([im]) + im, = self.evaluate([im]) return np.expand_dims(im, axis=0) def _LoadTestImages(self): @@ -3946,6 +4038,7 @@ class SSIMTest(test_util.TensorFlowTestCase): """Returns an image or image batch with given shape.""" return np.random.rand(*shape).astype(np.float32) * max_val + @test_util.run_deprecated_v1 def testAgainstMatlab(self): """Tests against values produced by Matlab.""" img = self._LoadTestImages() @@ -3969,7 +4062,7 @@ class SSIMTest(test_util.TensorFlowTestCase): ssim = image_ops.ssim(constant_op.constant(img1), constant_op.constant(img2), 1.0) with self.test_session(use_gpu=True): - self.assertAllClose(expected, ssim.eval(), atol=1e-4) + self.assertAllClose(expected, self.evaluate(ssim), atol=1e-4) def testBroadcast(self): img = self._LoadTestImages()[:2] @@ -3981,8 +4074,9 @@ class SSIMTest(test_util.TensorFlowTestCase): ssim = image_ops.ssim(img1, img2, 1.0) with self.test_session(use_gpu=True): - self.assertAllClose(expected, ssim.eval(), atol=1e-4) + self.assertAllClose(expected, self.evaluate(ssim), atol=1e-4) + @test_util.run_deprecated_v1 def testNegative(self): """Tests against negative SSIM index.""" step = np.expand_dims(np.arange(0, 256, 16, dtype=np.uint8), axis=0) @@ -3997,6 +4091,7 @@ class SSIMTest(test_util.TensorFlowTestCase): with self.test_session(use_gpu=True): self.assertLess(ssim.eval(), 0) + @test_util.run_deprecated_v1 def testInt(self): img1 = self._RandomImage((1, 16, 16, 3), 255) img2 = self._RandomImage((1, 16, 16, 3), 255) @@ -4007,7 +4102,8 @@ class SSIMTest(test_util.TensorFlowTestCase): img2 = image_ops.convert_image_dtype(img2, dtypes.float32) ssim_float32 = image_ops.ssim(img1, img2, 1.0) with self.test_session(use_gpu=True): - self.assertAllClose(ssim_uint8.eval(), ssim_float32.eval(), atol=0.001) + self.assertAllClose( + ssim_uint8.eval(), self.evaluate(ssim_float32), atol=0.001) class MultiscaleSSIMTest(test_util.TensorFlowTestCase): @@ -4026,7 +4122,7 @@ class MultiscaleSSIMTest(test_util.TensorFlowTestCase): "tensorflow/core/lib/ssim/testdata", filename)) im = image_ops.decode_png(content) im = image_ops.convert_image_dtype(im, dtypes.float32) - im, = sess.run([im]) + im, = self.evaluate([im]) return np.expand_dims(im, axis=0) def _LoadTestImages(self): @@ -4037,6 +4133,7 @@ class MultiscaleSSIMTest(test_util.TensorFlowTestCase): """Returns an image or image batch with given shape.""" return np.random.rand(*shape).astype(np.float32) * max_val + @test_util.run_deprecated_v1 def testAgainstMatlab(self): """Tests against MS-SSIM computed with Matlab implementation. @@ -4053,6 +4150,7 @@ class MultiscaleSSIMTest(test_util.TensorFlowTestCase): self.assertAllClose(expected, np.squeeze(scores), atol=1e-4) + @test_util.run_deprecated_v1 def testUnweightedIsDifferentiable(self): img = self._LoadTestImages() ph = [array_ops.placeholder(dtype=dtypes.float32) for _ in range(2)] @@ -4077,7 +4175,7 @@ class MultiscaleSSIMTest(test_util.TensorFlowTestCase): msssim = image_ops.ssim_multiscale(constant_op.constant(img1), constant_op.constant(img2), 1.0) with self.test_session(use_gpu=True): - self.assertAllClose(expected, msssim.eval(), 1e-4) + self.assertAllClose(expected, self.evaluate(msssim), 1e-4) def testBroadcast(self): """Tests MS-SSIM broadcasting.""" @@ -4090,7 +4188,7 @@ class MultiscaleSSIMTest(test_util.TensorFlowTestCase): score_tensor = image_ops.ssim_multiscale(img1, img2, 1.0) with self.test_session(use_gpu=True): - self.assertAllClose(expected, score_tensor.eval(), 1e-4) + self.assertAllClose(expected, self.evaluate(score_tensor), 1e-4) def testRange(self): """Tests against low MS-SSIM score. @@ -4108,12 +4206,13 @@ class MultiscaleSSIMTest(test_util.TensorFlowTestCase): images = [ops.convert_to_tensor(x, dtype=dtypes.float32) for x in images] msssim_ops = [image_ops.ssim_multiscale(x, y, 1.0) for x, y in itertools.combinations(images, 2)] - msssim = sess.run(msssim_ops) + msssim = self.evaluate(msssim_ops) msssim = np.squeeze(msssim) self.assertTrue(np.all(msssim >= 0.0)) self.assertTrue(np.all(msssim <= 1.0)) + @test_util.run_deprecated_v1 def testInt(self): img1 = self._RandomImage((1, 180, 240, 3), 255) img2 = self._RandomImage((1, 180, 240, 3), 255) @@ -4124,7 +4223,8 @@ class MultiscaleSSIMTest(test_util.TensorFlowTestCase): img2 = image_ops.convert_image_dtype(img2, dtypes.float32) ssim_float32 = image_ops.ssim_multiscale(img1, img2, 1.0) with self.test_session(use_gpu=True): - self.assertAllClose(ssim_uint8.eval(), ssim_float32.eval(), atol=0.001) + self.assertAllClose( + ssim_uint8.eval(), self.evaluate(ssim_float32), atol=0.001) class ImageGradientsTest(test_util.TensorFlowTestCase): @@ -4139,8 +4239,8 @@ class ImageGradientsTest(test_util.TensorFlowTestCase): dy, dx = image_ops.image_gradients(img) with self.cached_session(): - actual_dy = dy.eval() - actual_dx = dx.eval() + actual_dy = self.evaluate(dy) + actual_dx = self.evaluate(dx) self.assertAllClose(expected_dy, actual_dy) self.assertAllClose(expected_dx, actual_dx) @@ -4164,8 +4264,8 @@ class ImageGradientsTest(test_util.TensorFlowTestCase): assert batch.get_shape().as_list() == [2, 2, 3, 2] dy, dx = image_ops.image_gradients(batch) with self.test_session(use_gpu=True): - actual_dy = dy.eval() - actual_dx = dx.eval() + actual_dy = self.evaluate(dy) + actual_dx = self.evaluate(dx) self.assertAllClose(expected_dy, actual_dy) self.assertAllClose(expected_dx, actual_dx) @@ -4185,7 +4285,7 @@ class SobelEdgesTest(test_util.TensorFlowTestCase): [[0, 0], [0, 12], [0, 0]]], [1, 2, 3, 1, 2]) sobel = image_ops.sobel_edges(img) with self.test_session(use_gpu=True): - actual_sobel = sobel.eval() + actual_sobel = self.evaluate(sobel) self.assertAllClose(expected, actual_sobel) def testSobelEdges5x3x4x2(self): @@ -4207,7 +4307,7 @@ class SobelEdgesTest(test_util.TensorFlowTestCase): sobel = image_ops.sobel_edges(img) with self.test_session(use_gpu=True): - actual_sobel = sobel.eval() + actual_sobel = self.evaluate(sobel) self.assertAllClose(expected_batch, actual_sobel) @@ -4220,7 +4320,7 @@ class DecodeImageTest(test_util.TensorFlowTestCase): image0 = image_ops.decode_image(jpeg0, dtype=dtypes.uint16) image1 = image_ops.convert_image_dtype(image_ops.decode_jpeg(jpeg0), dtypes.uint16) - image0, image1 = sess.run([image0, image1]) + image0, image1 = self.evaluate([image0, image1]) self.assertAllEqual(image0, image1) def testPngUint16(self): @@ -4230,7 +4330,7 @@ class DecodeImageTest(test_util.TensorFlowTestCase): image0 = image_ops.decode_image(png0, dtype=dtypes.uint16) image1 = image_ops.convert_image_dtype( image_ops.decode_png(png0, dtype=dtypes.uint16), dtypes.uint16) - image0, image1 = sess.run([image0, image1]) + image0, image1 = self.evaluate([image0, image1]) self.assertAllEqual(image0, image1) def testGifUint16(self): @@ -4240,7 +4340,7 @@ class DecodeImageTest(test_util.TensorFlowTestCase): image0 = image_ops.decode_image(gif0, dtype=dtypes.uint16) image1 = image_ops.convert_image_dtype(image_ops.decode_gif(gif0), dtypes.uint16) - image0, image1 = sess.run([image0, image1]) + image0, image1 = self.evaluate([image0, image1]) self.assertAllEqual(image0, image1) def testBmpUint16(self): @@ -4250,7 +4350,7 @@ class DecodeImageTest(test_util.TensorFlowTestCase): image0 = image_ops.decode_image(bmp0, dtype=dtypes.uint16) image1 = image_ops.convert_image_dtype(image_ops.decode_bmp(bmp0), dtypes.uint16) - image0, image1 = sess.run([image0, image1]) + image0, image1 = self.evaluate([image0, image1]) self.assertAllEqual(image0, image1) def testJpegFloat32(self): @@ -4260,7 +4360,7 @@ class DecodeImageTest(test_util.TensorFlowTestCase): image0 = image_ops.decode_image(jpeg0, dtype=dtypes.float32) image1 = image_ops.convert_image_dtype(image_ops.decode_jpeg(jpeg0), dtypes.float32) - image0, image1 = sess.run([image0, image1]) + image0, image1 = self.evaluate([image0, image1]) self.assertAllEqual(image0, image1) def testPngFloat32(self): @@ -4270,7 +4370,7 @@ class DecodeImageTest(test_util.TensorFlowTestCase): image0 = image_ops.decode_image(png0, dtype=dtypes.float32) image1 = image_ops.convert_image_dtype( image_ops.decode_png(png0, dtype=dtypes.uint16), dtypes.float32) - image0, image1 = sess.run([image0, image1]) + image0, image1 = self.evaluate([image0, image1]) self.assertAllEqual(image0, image1) def testGifFloat32(self): @@ -4280,7 +4380,7 @@ class DecodeImageTest(test_util.TensorFlowTestCase): image0 = image_ops.decode_image(gif0, dtype=dtypes.float32) image1 = image_ops.convert_image_dtype(image_ops.decode_gif(gif0), dtypes.float32) - image0, image1 = sess.run([image0, image1]) + image0, image1 = self.evaluate([image0, image1]) self.assertAllEqual(image0, image1) def testBmpFloat32(self): @@ -4290,7 +4390,7 @@ class DecodeImageTest(test_util.TensorFlowTestCase): image0 = image_ops.decode_image(bmp0, dtype=dtypes.float32) image1 = image_ops.convert_image_dtype(image_ops.decode_bmp(bmp0), dtypes.float32) - image0, image1 = sess.run([image0, image1]) + image0, image1 = self.evaluate([image0, image1]) self.assertAllEqual(image0, image1) diff --git a/tensorflow/python/ops/init_ops.py b/tensorflow/python/ops/init_ops.py index 4fe6d05620..c0a4bcd51d 100644 --- a/tensorflow/python/ops/init_ops.py +++ b/tensorflow/python/ops/init_ops.py @@ -55,6 +55,15 @@ class Initializer(object): """ def __call__(self, shape, dtype=None, partition_info=None): + """Returns a tensor object initialized as specified by the initializer. + + Args: + shape: Shape of the tensor. + dtype: Optional dtype of the tensor. If not provided use the initializer + dtype. + partition_info: Optional information about the possible partitioning of a + tensor. + """ raise NotImplementedError def get_config(self): @@ -143,7 +152,8 @@ class Constant(Initializer): value: A Python scalar, list or tuple of values, or a N-dimensional numpy array. All elements of the initialized variable will be set to the corresponding value in the `value` argument. - dtype: The data type. + dtype: Default data type, used if no `dtype` argument is provided when + calling the initializer. verify_shape: Boolean that enables verification of the shape of `value`. If `True`, the initializer will throw an error if the shape of `value` is not compatible with the shape of the initialized tensor. @@ -216,7 +226,7 @@ class Constant(Initializer): dtype = self.dtype if verify_shape is None: verify_shape = self._verify_shape - return constant_op.constant( + return constant_op.constant_v1( self.value, dtype=dtype, shape=shape, verify_shape=verify_shape) def get_config(self): @@ -239,7 +249,8 @@ class RandomUniform(Initializer): seed: A Python integer. Used to create random seeds. See `tf.set_random_seed` for behavior. - dtype: The data type. + dtype: Default data type, used if no `dtype` argument is provided when + calling the initializer. """ def __init__(self, minval=0, maxval=None, seed=None, dtype=dtypes.float32): @@ -275,7 +286,8 @@ class RandomNormal(Initializer): seed: A Python integer. Used to create random seeds. See `tf.set_random_seed` for behavior. - dtype: The data type. Only floating point types are supported. + dtype: Default data type, used if no `dtype` argument is provided when + calling the initializer. Only floating point types are supported. """ def __init__(self, mean=0.0, stddev=1.0, seed=None, dtype=dtypes.float32): @@ -316,7 +328,8 @@ class TruncatedNormal(Initializer): seed: A Python integer. Used to create random seeds. See `tf.set_random_seed` for behavior. - dtype: The data type. Only floating point types are supported. + dtype: Default data type, used if no `dtype` argument is provided when + calling the initializer. Only floating point types are supported. """ def __init__(self, mean=0.0, stddev=1.0, seed=None, dtype=dtypes.float32): @@ -360,8 +373,7 @@ class UniformUnitScaling(Initializer): A similar calculation for convolutional networks gives an analogous result with `dim` equal to the product of the first 3 dimensions. When nonlinearities are present, we need to multiply this by a constant `factor`. - See [Sussillo et al., 2014](https://arxiv.org/abs/1412.6558) - ([pdf](http://arxiv.org/pdf/1412.6558.pdf)) for deeper motivation, experiments + See (Sussillo et al., 2014) for deeper motivation, experiments and the calculation of constants. In section 2.3 there, the constants were numerically computed: for a linear layer it's 1.0, relu: ~1.43, tanh: ~1.15. @@ -370,7 +382,12 @@ class UniformUnitScaling(Initializer): seed: A Python integer. Used to create random seeds. See `tf.set_random_seed` for behavior. - dtype: The data type. Only floating point types are supported. + dtype: Default data type, used if no `dtype` argument is provided when + calling the initializer. Only floating point types are supported. + + References: + [Sussillo et al., 2014](https://arxiv.org/abs/1412.6558) + ([pdf](http://arxiv.org/pdf/1412.6558.pdf)) """ @deprecated(None, @@ -434,7 +451,8 @@ class VarianceScaling(Initializer): seed: A Python integer. Used to create random seeds. See `tf.set_random_seed` for behavior. - dtype: The data type. Only floating point types are supported. + dtype: Default data type, used if no `dtype` argument is provided when + calling the initializer. Only floating point types are supported. Raises: ValueError: In case of an invalid value for the "scale", mode" or @@ -480,7 +498,7 @@ class VarianceScaling(Initializer): else: scale /= max(1., (fan_in + fan_out) / 2.) if self.distribution == "normal" or self.distribution == "truncated_normal": - # constant taken from scipy.stats.truncnorm.std(a=-2, b=2, loc=0., scale=1.) + # constant taken from scipy.stats.truncnorm.std(a=-2, b=2, loc=0., scale=1.) stddev = math.sqrt(scale) / .87962566103423978 return random_ops.truncated_normal( shape, 0.0, stddev, dtype, seed=self.seed) @@ -531,7 +549,12 @@ class Orthogonal(Initializer): seed: A Python integer. Used to create random seeds. See `tf.set_random_seed` for behavior. - dtype: The data type. + dtype: Default data type, used if no `dtype` argument is provided when + calling the initializer. Only floating point types are supported. + + References: + [Saxe et al., 2014](https://openreview.net/forum?id=_wzZwKpTDF_9C) + ([pdf](https://arxiv.org/pdf/1312.6120.pdf)) """ def __init__(self, gain=1.0, seed=None, dtype=dtypes.float32): @@ -576,16 +599,21 @@ class ConvolutionDeltaOrthogonal(Initializer): The shape of the tensor must have length 3, 4 or 5. The number of input filters must not exceed the number of output filters. The center pixels of the tensor form an orthogonal matrix. Other pixels are set to be zero. See - algorithm 2 in [Xiao et al., 2018]: https://arxiv.org/abs/1806.05393 + algorithm 2 in (Xiao et al., 2018). Args: - gain: Multiplicative factor to apply to the orthogonal matrix. Default is 1. - The 2-norm of an input is multiplied by a factor of 'sqrt(gain)' after - applying this convolution. + gain: Multiplicative factor to apply to the orthogonal + matrix. Default is 1. The 2-norm of an input is multiplied by a factor of + `gain` after applying this convolution. seed: A Python integer. Used to create random seeds. See `tf.set_random_seed` for behavior. - dtype: The data type. + dtype: Default data type, used if no `dtype` argument is provided when + calling the initializer. Only floating point types are supported. + + References: + [Xiao et al., 2018](http://proceedings.mlr.press/v80/xiao18a.html) + ([pdf](http://proceedings.mlr.press/v80/xiao18a/xiao18a.pdf)) """ def __init__(self, gain=1.0, seed=None, dtype=dtypes.float32): @@ -613,7 +641,7 @@ class ConvolutionDeltaOrthogonal(Initializer): d = array_ops.diag_part(r) q *= math_ops.sign(d) q = q[:shape[-2], :] - q *= math_ops.sqrt(math_ops.cast(self.gain, dtype=dtype)) + q *= math_ops.cast(self.gain, dtype=dtype) if len(shape) == 3: weight = array_ops.scatter_nd([[(shape[0]-1)//2]], array_ops.expand_dims(q, 0), shape) @@ -636,12 +664,17 @@ class ConvolutionOrthogonal(Initializer): Base class used to construct 1D, 2D and 3D orthogonal kernels for convolution. Args: - gain: multiplicative factor to apply to the orthogonal matrix. Default is 1. - The 2-norm of an input is multiplied by a factor of 'sqrt(gain)' after - applying this convolution. + gain: multiplicative factor to apply to the orthogonal + matrix. Default is 1. The 2-norm of an input is multiplied by a factor of + `gain` after applying this convolution. seed: A Python integer. Used to create random seeds. See `tf.set_random_seed` for behavior. - dtype: The data type. + dtype: Default data type, used if no `dtype` argument is provided when + calling the initializer. Only floating point types are supported. + + References: + [Xiao et al., 2018](http://proceedings.mlr.press/v80/xiao18a.html) + ([pdf](http://proceedings.mlr.press/v80/xiao18a/xiao18a.pdf)) """ def __init__(self, gain=1.0, seed=None, dtype=dtypes.float32): @@ -698,15 +731,20 @@ class ConvolutionOrthogonal2D(ConvolutionOrthogonal): filters must not exceed the number of output filters. The orthogonality(==isometry) is exact when the inputs are circular padded. There are finite-width effects with non-circular padding (e.g. zero padding). - See algorithm 1 in [Xiao et al., 2018]: https://arxiv.org/abs/1806.05393 + See algorithm 1 in (Xiao et al., 2018). Args: - gain: Multiplicative factor to apply to the orthogonal matrix. Default is 1. - This has the effect of scaling the output 2-norm by a factor of - `sqrt(gain)`. + gain: Multiplicative factor to apply to the orthogonal + matrix. Default is 1. This has the effect of scaling the output 2-norm by + a factor of `gain`. seed: A Python integer. Used to create random seeds. See `tf.set_random_seed` for behavior. - dtype: The data type. + dtype: Default data type, used if no `dtype` argument is provided when + calling the initializer. Only floating point types are supported. + + References: + [Xiao et al., 2018](http://proceedings.mlr.press/v80/xiao18a.html) + ([pdf](http://proceedings.mlr.press/v80/xiao18a/xiao18a.pdf)) """ def __call__(self, shape, dtype=None, partition_info=None): @@ -722,7 +760,7 @@ class ConvolutionOrthogonal2D(ConvolutionOrthogonal): raise ValueError("Kernel sizes must be equal.") kernel = self._orthogonal_kernel(shape[0], shape[2], shape[3]) - kernel *= math_ops.sqrt(math_ops.cast(self.gain, dtype=dtype)) + kernel *= math_ops.cast(self.gain, dtype=dtype) return kernel def _dict_to_tensor(self, x, k1, k2): @@ -834,16 +872,21 @@ class ConvolutionOrthogonal1D(ConvolutionOrthogonal): filters must not exceed the number of output filters. The orthogonality(==isometry) is exact when the inputs are circular padded. There are finite-width effects with non-circular padding (e.g. zero padding). - See algorithm 1 in [Xiao et al., 2018]: https://arxiv.org/abs/1806.05393 + See algorithm 1 in (Xiao et al., 2018). Args: - gain: Multiplicative factor to apply to the orthogonal matrix. Default is 1. - The 2-norm of an input is multiplied by a factor of 'sqrt(gain)' after - applying this convolution. + gain: Multiplicative factor to apply to the orthogonal + matrix. Default is 1. The 2-norm of an input is multiplied by a factor of + `gain` after applying this convolution. seed: A Python integer. Used to create random seeds. See `tf.set_random_seed` for behavior. - dtype: The data type. + dtype: Default data type, used if no `dtype` argument is provided when + calling the initializer. Only floating point types are supported. + + References: + [Xiao et al., 2018](http://proceedings.mlr.press/v80/xiao18a.html) + ([pdf](http://proceedings.mlr.press/v80/xiao18a/xiao18a.pdf)) """ def __call__(self, shape, dtype=None, partition_info=None): @@ -856,7 +899,7 @@ class ConvolutionOrthogonal1D(ConvolutionOrthogonal): raise ValueError("In_filters cannot be greater than out_filters.") kernel = self._orthogonal_kernel(shape[0], shape[-2], shape[-1]) - kernel *= math_ops.sqrt(math_ops.cast(self.gain, dtype=dtype)) + kernel *= math_ops.cast(self.gain, dtype=dtype) return kernel def _dict_to_tensor(self, x, k): @@ -951,15 +994,20 @@ class ConvolutionOrthogonal3D(ConvolutionOrthogonal): filters must not exceed the number of output filters. The orthogonality(==isometry) is exact when the inputs are circular padded. There are finite-width effects with non-circular padding (e.g. zero padding). - See algorithm 1 [Xiao et al., 2018] in: https://arxiv.org/abs/1806.05393 + See algorithm 1 (Xiao et al., 2018). Args: - gain: Multiplicative factor to apply to the orthogonal matrix. Default is 1. - The 2-norm of an input is multiplied by a factor of 'sqrt(gain)' after - applying this convolution. + gain: Multiplicative factor to apply to the orthogonal + matrix. Default is 1. The 2-norm of an input is multiplied by a factor of + `gain` after applying this convolution. seed: A Python integer. Used to create random seeds. See `tf.set_random_seed` for behavior. - dtype: The data type. + dtype: Default data type, used if no `dtype` argument is provided when + calling the initializer. Only floating point types are supported. + + References: + [Xiao et al., 2018](http://proceedings.mlr.press/v80/xiao18a.html) + ([pdf](http://proceedings.mlr.press/v80/xiao18a/xiao18a.pdf)) """ def __call__(self, shape, dtype=None, partition_info=None): @@ -975,7 +1023,7 @@ class ConvolutionOrthogonal3D(ConvolutionOrthogonal): raise ValueError("Kernel sizes must be equal.") kernel = self._orthogonal_kernel(shape[0], shape[-2], shape[-1]) - kernel *= math_ops.sqrt(math_ops.cast(self.gain, dtype=dtype)) + kernel *= math_ops.cast(self.gain, dtype=dtype) return kernel def _dict_to_tensor(self, x, k1, k2, k3): @@ -1105,7 +1153,8 @@ class Identity(Initializer): Args: gain: Multiplicative factor to apply to the identity matrix. - dtype: The type of the output. + dtype: Default data type, used if no `dtype` argument is provided when + calling the initializer. Only floating point types are supported. """ def __init__(self, gain=1.0, dtype=dtypes.float32): @@ -1139,18 +1188,19 @@ class GlorotUniform(VarianceScaling): where `fan_in` is the number of input units in the weight tensor and `fan_out` is the number of output units in the weight tensor. - Reference: http://jmlr.org/proceedings/papers/v9/glorot10a/glorot10a.pdf - Args: seed: A Python integer. Used to create random seeds. See `tf.set_random_seed` for behavior. - dtype: The data type. Only floating point types are supported. + dtype: Default data type, used if no `dtype` argument is provided when + calling the initializer. Only floating point types are supported. + + References: + [Glorot et al., 2010](http://proceedings.mlr.press/v9/glorot10a.html) + ([pdf](http://jmlr.org/proceedings/papers/v9/glorot10a/glorot10a.pdf)) """ - def __init__(self, - seed=None, - dtype=dtypes.float32): + def __init__(self, seed=None, dtype=dtypes.float32): super(GlorotUniform, self).__init__( scale=1.0, mode="fan_avg", @@ -1159,10 +1209,7 @@ class GlorotUniform(VarianceScaling): dtype=dtype) def get_config(self): - return { - "seed": self.seed, - "dtype": self.dtype.name - } + return {"seed": self.seed, "dtype": self.dtype.name} @tf_export( @@ -1181,18 +1228,18 @@ class GlorotNormal(VarianceScaling): where `fan_in` is the number of input units in the weight tensor and `fan_out` is the number of output units in the weight tensor. - Reference: http://jmlr.org/proceedings/papers/v9/glorot10a/glorot10a.pdf - Args: seed: A Python integer. Used to create random seeds. See - `tf.set_random_seed` - for behavior. - dtype: The data type. Only floating point types are supported. + `tf.set_random_seed` for behavior. + dtype: Default data type, used if no `dtype` argument is provided when + calling the initializer. Only floating point types are supported. + + References: + [Glorot et al., 2010](http://proceedings.mlr.press/v9/glorot10a.html) + ([pdf](http://jmlr.org/proceedings/papers/v9/glorot10a/glorot10a.pdf)) """ - def __init__(self, - seed=None, - dtype=dtypes.float32): + def __init__(self, seed=None, dtype=dtypes.float32): super(GlorotNormal, self).__init__( scale=1.0, mode="fan_avg", @@ -1201,10 +1248,7 @@ class GlorotNormal(VarianceScaling): dtype=dtype) def get_config(self): - return { - "seed": self.seed, - "dtype": self.dtype.name - } + return {"seed": self.seed, "dtype": self.dtype.name} # Aliases. @@ -1244,9 +1288,11 @@ def lecun_normal(seed=None): An initializer. References: - - [Self-Normalizing Neural Networks](https://arxiv.org/abs/1706.02515) - - [Efficient - Backprop](http://yann.lecun.com/exdb/publis/pdf/lecun-98b.pdf) + - Self-Normalizing Neural Networks, + [Klambauer et al., 2017](https://papers.nips.cc/paper/6698-self-normalizing-neural-networks) + ([pdf](https://papers.nips.cc/paper/6698-self-normalizing-neural-networks.pdf)) + - Efficient Backprop, + [Lecun et al., 1998](http://yann.lecun.com/exdb/publis/pdf/lecun-98b.pdf) """ return VarianceScaling( scale=1., mode="fan_in", distribution="truncated_normal", seed=seed) @@ -1267,8 +1313,11 @@ def lecun_uniform(seed=None): An initializer. References: - LeCun 98, Efficient Backprop, - http://yann.lecun.com/exdb/publis/pdf/lecun-98b.pdf + - Self-Normalizing Neural Networks, + [Klambauer et al., 2017](https://papers.nips.cc/paper/6698-self-normalizing-neural-networks) + ([pdf](https://papers.nips.cc/paper/6698-self-normalizing-neural-networks.pdf)) + - Efficient Backprop, + [Lecun et al., 1998](http://yann.lecun.com/exdb/publis/pdf/lecun-98b.pdf) """ return VarianceScaling( scale=1., mode="fan_in", distribution="uniform", seed=seed) @@ -1289,7 +1338,8 @@ def he_normal(seed=None): An initializer. References: - He et al., http://arxiv.org/abs/1502.01852 + [He et al., 2015](https://www.cv-foundation.org/openaccess/content_iccv_2015/html/He_Delving_Deep_into_ICCV_2015_paper.html) + ([pdf](https://www.cv-foundation.org/openaccess/content_iccv_2015/papers/He_Delving_Deep_into_ICCV_2015_paper.pdf)) """ return VarianceScaling( scale=2., mode="fan_in", distribution="truncated_normal", seed=seed) @@ -1310,7 +1360,8 @@ def he_uniform(seed=None): An initializer. References: - He et al., http://arxiv.org/abs/1502.01852 + [He et al., 2015](https://www.cv-foundation.org/openaccess/content_iccv_2015/html/He_Delving_Deep_into_ICCV_2015_paper.html) + ([pdf](https://www.cv-foundation.org/openaccess/content_iccv_2015/papers/He_Delving_Deep_into_ICCV_2015_paper.pdf)) """ return VarianceScaling( scale=2., mode="fan_in", distribution="uniform", seed=seed) diff --git a/tensorflow/python/ops/init_ops_test.py b/tensorflow/python/ops/init_ops_test.py index 5693c3caaf..1f22248004 100644 --- a/tensorflow/python/ops/init_ops_test.py +++ b/tensorflow/python/ops/init_ops_test.py @@ -45,8 +45,8 @@ class InitializersTest(test.TestCase): output = variable.numpy() else: sess = ops.get_default_session() - sess.run(variable.initializer) - output = sess.run(variable) + self.evaluate(variable.initializer) + output = self.evaluate(variable) lim = 3e-2 if target_std is not None: self.assertGreater(lim, abs(output.std() - target_std)) diff --git a/tensorflow/python/ops/linalg/BUILD b/tensorflow/python/ops/linalg/BUILD index c7314d7774..5df2d6b838 100644 --- a/tensorflow/python/ops/linalg/BUILD +++ b/tensorflow/python/ops/linalg/BUILD @@ -18,6 +18,7 @@ py_library( "//tensorflow/python:random_ops", "//tensorflow/python:tensor_util", "//tensorflow/python:util", + "//tensorflow/python/ops/signal", "//third_party/py/numpy", "@six_archive//:six", ], diff --git a/tensorflow/python/ops/linalg/cholesky_registrations.py b/tensorflow/python/ops/linalg/cholesky_registrations.py new file mode 100644 index 0000000000..e5284cf22a --- /dev/null +++ b/tensorflow/python/ops/linalg/cholesky_registrations.py @@ -0,0 +1,101 @@ +# Copyright 2018 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. +# ============================================================================== +"""Registrations for LinearOperator.cholesky.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.ops import linalg_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops.linalg import linear_operator +from tensorflow.python.ops.linalg import linear_operator_algebra +from tensorflow.python.ops.linalg import linear_operator_block_diag +from tensorflow.python.ops.linalg import linear_operator_diag +from tensorflow.python.ops.linalg import linear_operator_identity +from tensorflow.python.ops.linalg import linear_operator_kronecker +from tensorflow.python.ops.linalg import linear_operator_lower_triangular + + +# By default, compute the Cholesky of the dense matrix, and return a +# LowerTriangular operator. Methods below specialize this registration. +@linear_operator_algebra.RegisterCholesky(linear_operator.LinearOperator) +def _cholesky_linear_operator(linop): + return linear_operator_lower_triangular.LinearOperatorLowerTriangular( + linalg_ops.cholesky(linop.to_dense()), + is_non_singular=True, + is_self_adjoint=False, + is_square=True) + + +@linear_operator_algebra.RegisterCholesky( + linear_operator_diag.LinearOperatorDiag) +def _cholesky_diag(diag_operator): + return linear_operator_diag.LinearOperatorDiag( + math_ops.sqrt(diag_operator.diag), + is_non_singular=True, + is_self_adjoint=True, + is_positive_definite=True, + is_square=True) + + +@linear_operator_algebra.RegisterCholesky( + linear_operator_identity.LinearOperatorIdentity) +def _cholesky_identity(identity_operator): + return linear_operator_identity.LinearOperatorIdentity( + num_rows=identity_operator._num_rows, # pylint: disable=protected-access + batch_shape=identity_operator.batch_shape, + dtype=identity_operator.dtype, + is_non_singular=True, + is_self_adjoint=True, + is_positive_definite=True, + is_square=True) + + +@linear_operator_algebra.RegisterCholesky( + linear_operator_identity.LinearOperatorScaledIdentity) +def _cholesky_scaled_identity(identity_operator): + return linear_operator_identity.LinearOperatorScaledIdentity( + num_rows=identity_operator._num_rows, # pylint: disable=protected-access + multiplier=math_ops.sqrt(identity_operator.multiplier), + is_non_singular=True, + is_self_adjoint=True, + is_positive_definite=True, + is_square=True) + + +@linear_operator_algebra.RegisterCholesky( + linear_operator_block_diag.LinearOperatorBlockDiag) +def _cholesky_block_diag(block_diag_operator): + # We take the cholesky of each block on the diagonal. + return linear_operator_block_diag.LinearOperatorBlockDiag( + operators=[ + operator.cholesky() for operator in block_diag_operator.operators], + is_non_singular=True, + is_self_adjoint=False, + is_square=True) + + +@linear_operator_algebra.RegisterCholesky( + linear_operator_kronecker.LinearOperatorKronecker) +def _cholesky_kronecker(kronecker_operator): + # Cholesky decomposition of a Kronecker product is the Kronecker product + # of cholesky decompositions. + return linear_operator_kronecker.LinearOperatorKronecker( + operators=[ + operator.cholesky() for operator in kronecker_operator.operators], + is_non_singular=True, + is_self_adjoint=False, + is_square=True) diff --git a/tensorflow/python/ops/linalg/linalg.py b/tensorflow/python/ops/linalg/linalg.py index c29b5033bb..ac4fd4ebc6 100644 --- a/tensorflow/python/ops/linalg/linalg.py +++ b/tensorflow/python/ops/linalg/linalg.py @@ -20,6 +20,9 @@ from __future__ import print_function # go/tf-wildcard-import # pylint: disable=wildcard-import,unused-import +from tensorflow.python.ops.linalg import cholesky_registrations as _cholesky_registrations +from tensorflow.python.ops.linalg import linear_operator_algebra as _linear_operator_algebra +from tensorflow.python.ops.linalg import matmul_registrations as _matmul_registrations from tensorflow.python.ops.linalg.linalg_impl import * from tensorflow.python.ops.linalg.linear_operator import * from tensorflow.python.ops.linalg.linear_operator_block_diag import * diff --git a/tensorflow/python/ops/linalg/linalg_impl.py b/tensorflow/python/ops/linalg/linalg_impl.py index 08d50ce622..2c9476a9bd 100644 --- a/tensorflow/python/ops/linalg/linalg_impl.py +++ b/tensorflow/python/ops/linalg/linalg_impl.py @@ -88,7 +88,7 @@ def logdet(matrix, name=None): chol = gen_linalg_ops.cholesky(matrix) return 2.0 * math_ops.reduce_sum( math_ops.log(math_ops.real(array_ops.matrix_diag_part(chol))), - reduction_indices=[-1]) + axis=[-1]) @tf_export('linalg.adjoint') diff --git a/tensorflow/python/ops/linalg/linear_operator.py b/tensorflow/python/ops/linalg/linear_operator.py index 9ef6c42b04..8efafda3a1 100644 --- a/tensorflow/python/ops/linalg/linear_operator.py +++ b/tensorflow/python/ops/linalg/linear_operator.py @@ -32,6 +32,7 @@ from tensorflow.python.ops import check_ops from tensorflow.python.ops import linalg_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops.linalg import linalg_impl as linalg +from tensorflow.python.ops.linalg import linear_operator_algebra from tensorflow.python.ops.linalg import linear_operator_util from tensorflow.python.platform import tf_logging as logging from tensorflow.python.util.tf_export import tf_export @@ -284,7 +285,7 @@ class LinearOperator(object): `[B1,...,Bb, M, N]`, equivalent to `tf.shape(A)`. Args: - name: A name for this `Op. + name: A name for this `Op`. Returns: `int32` `Tensor` @@ -318,7 +319,7 @@ class LinearOperator(object): `[B1,...,Bb]`. Args: - name: A name for this `Op. + name: A name for this `Op`. Returns: `int32` `Tensor` @@ -340,7 +341,7 @@ class LinearOperator(object): `A.shape = [B1,...,Bb, M, N]`, then this returns `b + 2`. Args: - name: A name for this `Op. + name: A name for this `Op`. Returns: Python integer, or None if the tensor rank is undefined. @@ -356,7 +357,7 @@ class LinearOperator(object): `A.shape = [B1,...,Bb, M, N]`, then this returns `b + 2`. Args: - name: A name for this `Op. + name: A name for this `Op`. Returns: `int32` `Tensor`, determined at runtime. @@ -581,16 +582,29 @@ class LinearOperator(object): ``` Args: - x: `Tensor` with compatible shape and same `dtype` as `self`. - See class docstring for definition of compatibility. + x: `LinearOperator` or `Tensor` with compatible shape and same `dtype` as + `self`. See class docstring for definition of compatibility. adjoint: Python `bool`. If `True`, left multiply by the adjoint: `A^H x`. adjoint_arg: Python `bool`. If `True`, compute `A x^H` where `x^H` is the hermitian transpose (transposition and complex conjugation). - name: A name for this `Op. + name: A name for this `Op`. Returns: - A `Tensor` with shape `[..., M, R]` and same `dtype` as `self`. + A `LinearOperator` or `Tensor` with shape `[..., M, R]` and same `dtype` + as `self`. """ + if isinstance(x, LinearOperator): + if adjoint or adjoint_arg: + raise ValueError(".matmul not supported with adjoints.") + if (x.range_dimension is not None and + self.domain_dimension is not None and + x.range_dimension != self.domain_dimension): + raise ValueError( + "Operators are incompatible. Expected `x` to have dimension" + " {} but got {}.".format(self.domain_dimension, x.range_dimension)) + with self._name_scope(name): + return linear_operator_algebra.matmul(self, x) + with self._name_scope(name, values=[x]): x = ops.convert_to_tensor(x, name="x") self._check_input_dtype(x) @@ -630,7 +644,7 @@ class LinearOperator(object): dimensions, the last dimension defines a vector. See class docstring for definition of compatibility. adjoint: Python `bool`. If `True`, left multiply by the adjoint: `A^H x`. - name: A name for this `Op. + name: A name for this `Op`. Returns: A `Tensor` with shape `[..., M]` and same `dtype` as `self`. @@ -655,7 +669,7 @@ class LinearOperator(object): """Determinant for every batch member. Args: - name: A name for this `Op. + name: A name for this `Op`. Returns: `Tensor` with shape `self.batch_shape` and same `dtype` as `self`. @@ -676,7 +690,7 @@ class LinearOperator(object): " Requires conversion to a dense matrix and O(N^3) operations.") if self._can_use_cholesky(): diag = array_ops.matrix_diag_part(linalg_ops.cholesky(self.to_dense())) - return 2 * math_ops.reduce_sum(math_ops.log(diag), reduction_indices=[-1]) + return 2 * math_ops.reduce_sum(math_ops.log(diag), axis=[-1]) _, log_abs_det = linalg.slogdet(self.to_dense()) return log_abs_det @@ -684,7 +698,7 @@ class LinearOperator(object): """Log absolute value of determinant for every batch member. Args: - name: A name for this `Op. + name: A name for this `Op`. Returns: `Tensor` with shape `self.batch_shape` and same `dtype` as `self`. @@ -830,6 +844,31 @@ class LinearOperator(object): return self._solvevec(rhs, adjoint=adjoint) + def cholesky(self, name="cholesky"): + """Returns a Cholesky factor as a `LinearOperator`. + + Given `A` representing this `LinearOperator`, if `A` is positive definite + self-adjoint, return `L`, where `A = L L^T`, i.e. the cholesky + decomposition. + + Args: + name: A name for this `Op`. + + Returns: + `LinearOperator` which represents the lower triangular matrix + in the Cholesky decomposition. + + Raises: + ValueError: When the `LinearOperator` is not hinted to be positive + definite and self adjoint. + """ + + if not self._can_use_cholesky(): + raise ValueError("Cannot take the Cholesky decomposition: " + "Not a positive definite self adjoint matrix.") + with self._name_scope(name): + return linear_operator_algebra.cholesky(self) + def _to_dense(self): """Generic and often inefficient implementation. Override often.""" logging.warn("Using (possibly slow) default implementation of to_dense." @@ -922,6 +961,4 @@ class LinearOperator(object): return self._add_to_tensor(x) def _can_use_cholesky(self): - # TODO(langmore) Add complex types when tf.cholesky can use them. - return (not self.dtype.is_complex and self.is_self_adjoint and - self.is_positive_definite) + return self.is_self_adjoint and self.is_positive_definite diff --git a/tensorflow/python/ops/linalg/linear_operator_adjoint.py b/tensorflow/python/ops/linalg/linear_operator_adjoint.py new file mode 100644 index 0000000000..858e224b9a --- /dev/null +++ b/tensorflow/python/ops/linalg/linear_operator_adjoint.py @@ -0,0 +1,207 @@ +# Copyright 2018 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. +# ============================================================================== +"""Takes the adjoint of a `LinearOperator`.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.framework import ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops.linalg import linalg_impl as linalg +from tensorflow.python.ops.linalg import linear_operator +from tensorflow.python.util.tf_export import tf_export + +__all__ = [] + + +@tf_export("linalg.LinearOperatorAdjoint") +class LinearOperatorAdjoint(linear_operator.LinearOperator): + """`LinearOperator` representing the adjoint of another operator. + + This operator represents the adjoint of another operator. + + ```python + # Create a 2 x 2 linear operator. + operator = LinearOperatorFullMatrix([[1 - i., 3.], [0., 1. + i]]) + operator_adjoint = LinearOperatorAdjoint(operator) + + operator_adjoint.to_dense() + ==> [[1. + i, 0.] + [3., 1 - i]] + + operator_adjoint.shape + ==> [2, 2] + + operator_adjoint.log_abs_determinant() + ==> - log(2) + + x = ... Shape [2, 4] Tensor + operator_adjoint.matmul(x) + ==> Shape [2, 4] Tensor, equal to operator.matmul(x, adjoint=True) + ``` + + #### Performance + + The performance of `LinearOperatorAdjoint` depends on the underlying + operators performance. + + #### Matrix property hints + + This `LinearOperator` is initialized with boolean flags of the form `is_X`, + for `X = non_singular, self_adjoint, positive_definite, square`. + These have the following meaning: + + * If `is_X == True`, callers should expect the operator to have the + property `X`. This is a promise that should be fulfilled, but is *not* a + runtime assert. For example, finite floating point precision may result + in these promises being violated. + * If `is_X == False`, callers should expect the operator to not have `X`. + * If `is_X == None` (the default), callers should have no expectation either + way. + """ + + def __init__(self, + operator, + is_non_singular=None, + is_self_adjoint=None, + is_positive_definite=None, + is_square=None, + name=None): + r"""Initialize a `LinearOperatorAdjoint`. + + `LinearOperatorAdjoint` is initialized with an operator `A`. The `solve` + and `matmul` methods effectively flip the `adjoint` argument. E.g. + + ``` + A = MyLinearOperator(...) + B = LinearOperatorAdjoint(A) + x = [....] # a vector + + assert A.matvec(x, adjoint=True) == B.matvec(x, adjoint=False) + ``` + + Args: + operator: `LinearOperator` object. + is_non_singular: Expect that this operator is non-singular. + is_self_adjoint: Expect that this operator is equal to its hermitian + transpose. + 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`. Default is `operator.name + + "_adjoint"`. + + Raises: + ValueError: If `operator.is_non_singular` is False. + """ + + self._operator = operator + + # The congruency of is_non_singular and is_self_adjoint was checked in the + # base operator. + def _combined_hint(hint_str, provided_hint_value, message): + """Get combined hint in the case where operator.hint should equal hint.""" + op_hint = getattr(operator, hint_str) + if op_hint is False and provided_hint_value: + raise ValueError(message) + if op_hint and provided_hint_value is False: + raise ValueError(message) + return (op_hint or provided_hint_value) or None + + is_square = _combined_hint( + "is_square", is_square, + "An operator is square if and only if its adjoint is square.") + + is_non_singular = _combined_hint( + "is_non_singular", is_non_singular, + "An operator is non-singular if and only if its adjoint is " + "non-singular.") + + is_self_adjoint = _combined_hint( + "is_self_adjoint", is_self_adjoint, + "An operator is self-adjoint if and only if its adjoint is " + "self-adjoint.") + + is_positive_definite = _combined_hint( + "is_positive_definite", is_positive_definite, + "An operator is positive-definite if and only if its adjoint is " + "positive-definite.") + + is_square = _combined_hint( + "is_square", is_square, + "An operator is square if and only if its adjoint is square.") + + # Initialization. + if name is None: + name = operator.name + "_adjoint" + with ops.name_scope(name, values=operator.graph_parents): + super(LinearOperatorAdjoint, self).__init__( + dtype=operator.dtype, + graph_parents=operator.graph_parents, + is_non_singular=is_non_singular, + is_self_adjoint=is_self_adjoint, + is_positive_definite=is_positive_definite, + is_square=is_square, + name=name) + + @property + def operator(self): + """The operator before taking the adjoint.""" + return self._operator + + def _assert_non_singular(self): + return self.operator.assert_non_singular() + + def _assert_positive_definite(self): + return self.operator.assert_positive_definite() + + def _assert_self_adjoint(self): + return self.operator.assert_self_adjoint() + + def _shape(self): + return self.operator.shape + + def _shape_tensor(self): + return self.operator.shape_tensor() + + def _matmul(self, x, adjoint=False, adjoint_arg=False): + return self.operator.matmul( + x, adjoint=(not adjoint), adjoint_arg=adjoint_arg) + + def _determinant(self): + if self.is_self_adjoint: + return self.operator.determinant() + return math_ops.conj(self.operator.determinant()) + + def _log_abs_determinant(self): + return self.operator.log_abs_determinant() + + def _trace(self): + if self.is_self_adjoint: + return self.operator.trace() + return math_ops.conj(self.operator.trace()) + + def _solve(self, rhs, adjoint=False, adjoint_arg=False): + return self.operator.solve( + rhs, adjoint=(not adjoint), adjoint_arg=adjoint_arg) + + def _to_dense(self): + if self.is_self_adjoint: + return self.operator.to_dense() + return linalg.adjoint(self.operator.to_dense()) diff --git a/tensorflow/python/ops/linalg/linear_operator_algebra.py b/tensorflow/python/ops/linalg/linear_operator_algebra.py new file mode 100644 index 0000000000..7b99066e4c --- /dev/null +++ b/tensorflow/python/ops/linalg/linear_operator_algebra.py @@ -0,0 +1,191 @@ +# Copyright 2018 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. +# ============================================================================== + +"""Registration mechanisms for various n-ary operations on LinearOperators.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import itertools + +from tensorflow.python.framework import ops +from tensorflow.python.util import tf_inspect + + +_CHOLESKY_DECOMPS = {} +_MATMUL = {} + + +def _registered_function(type_list, registry): + """Given a list of classes, finds the most specific function registered.""" + enumerated_hierarchies = [enumerate(tf_inspect.getmro(t)) for t in type_list] + # Get all possible combinations of hierarchies. + cls_combinations = list(itertools.product(*enumerated_hierarchies)) + + def hierarchy_distance(cls_combination): + candidate_distance = sum(c[0] for c in cls_combination) + if tuple(c[1] for c in cls_combination) in registry: + return candidate_distance + return 10000 + + registered_combination = min(cls_combinations, key=hierarchy_distance) + return registry.get(tuple(r[1] for r in registered_combination), None) + + +def _registered_cholesky(type_a): + """Get the Cholesky function registered for class a.""" + return _registered_function([type_a], _CHOLESKY_DECOMPS) + + +def _registered_matmul(type_a, type_b): + """Get the Matmul function registered for classes a and b.""" + return _registered_function([type_a, type_b], _MATMUL) + + +def cholesky(lin_op_a, name=None): + """Get the Cholesky factor associated to lin_op_a. + + Args: + lin_op_a: The LinearOperator to decompose. + name: Name to use for this operation. + + Returns: + A LinearOperator that represents the lower Cholesky factor of `lin_op_a`. + + Raises: + NotImplementedError: If no Cholesky method is defined for the LinearOperator + type of `lin_op_a`. + """ + cholesky_fn = _registered_cholesky(type(lin_op_a)) + if cholesky_fn is None: + raise ValueError("No cholesky decomposition registered for {}".format( + type(lin_op_a))) + + with ops.name_scope(name, "Cholesky"): + return cholesky_fn(lin_op_a) + + +def matmul(lin_op_a, lin_op_b, name=None): + """Compute lin_op_a.matmul(lin_op_b). + + Args: + lin_op_a: The LinearOperator on the left. + lin_op_b: The LinearOperator on the right. + name: Name to use for this operation. + + Returns: + A LinearOperator that represents the matmul between `lin_op_a` and + `lin_op_b`. + + Raises: + NotImplementedError: If no matmul method is defined between types of + `lin_op_a` and `lin_op_b`. + """ + matmul_fn = _registered_matmul(type(lin_op_a), type(lin_op_b)) + if matmul_fn is None: + raise ValueError("No matmul registered for {}.matmul({})".format( + type(lin_op_a), type(lin_op_b))) + + with ops.name_scope(name, "Matmul"): + return matmul_fn(lin_op_a, lin_op_b) + + +class RegisterCholesky(object): + """Decorator to register a Cholesky implementation function. + + Usage: + + @linear_operator_algebra.RegisterCholesky(lin_op.LinearOperatorIdentity) + def _cholesky_identity(lin_op_a): + # Return the identity matrix. + """ + + def __init__(self, lin_op_cls_a): + """Initialize the LinearOperator registrar. + + Args: + lin_op_cls_a: the class of the LinearOperator to decompose. + """ + self._key = (lin_op_cls_a,) + + def __call__(self, cholesky_fn): + """Perform the Cholesky registration. + + Args: + cholesky_fn: The function to use for the Cholesky. + + Returns: + cholesky_fn + + Raises: + TypeError: if cholesky_fn is not a callable. + ValueError: if a Cholesky function has already been registered for + the given argument classes. + """ + if not callable(cholesky_fn): + raise TypeError( + "cholesky_fn must be callable, received: {}".format(cholesky_fn)) + if self._key in _CHOLESKY_DECOMPS: + raise ValueError("Cholesky({}) has already been registered to: {}".format( + self._key[0].__name__, _CHOLESKY_DECOMPS[self._key])) + _CHOLESKY_DECOMPS[self._key] = cholesky_fn + return cholesky_fn + + +class RegisterMatmul(object): + """Decorator to register a Matmul implementation function. + + Usage: + + @linear_operator_algebra.RegisterMatmul( + lin_op.LinearOperatorIdentity, + lin_op.LinearOperatorIdentity) + def _matmul_identity(a, b): + # Return the identity matrix. + """ + + def __init__(self, lin_op_cls_a, lin_op_cls_b): + """Initialize the LinearOperator registrar. + + Args: + lin_op_cls_a: the class of the LinearOperator to multiply. + lin_op_cls_b: the class of the second LinearOperator to multiply. + """ + self._key = (lin_op_cls_a, lin_op_cls_b) + + def __call__(self, matmul_fn): + """Perform the Matmul registration. + + Args: + matmul_fn: The function to use for the Matmul. + + Returns: + matmul_fn + + Raises: + TypeError: if matmul_fn is not a callable. + ValueError: if a Matmul function has already been registered for + the given argument classes. + """ + if not callable(matmul_fn): + raise TypeError( + "matmul_fn must be callable, received: {}".format(matmul_fn)) + if self._key in _MATMUL: + raise ValueError("Matmul({}, {}) has already been registered.".format( + self._key[0].__name__, + self._key[1].__name__)) + _MATMUL[self._key] = matmul_fn + return matmul_fn diff --git a/tensorflow/python/ops/linalg/linear_operator_block_diag.py b/tensorflow/python/ops/linalg/linear_operator_block_diag.py index 438c3496bd..b0b418c997 100644 --- a/tensorflow/python/ops/linalg/linear_operator_block_diag.py +++ b/tensorflow/python/ops/linalg/linear_operator_block_diag.py @@ -29,9 +29,7 @@ from tensorflow.python.ops.linalg import linear_operator from tensorflow.python.ops.linalg import linear_operator_util from tensorflow.python.util.tf_export import tf_export -__all__ = [ - "LinearOperatorBlockDiag", -] +__all__ = ["LinearOperatorBlockDiag"] @tf_export("linalg.LinearOperatorBlockDiag") diff --git a/tensorflow/python/ops/linalg/linear_operator_circulant.py b/tensorflow/python/ops/linalg/linear_operator_circulant.py index 021ef47383..b74baa5dfd 100644 --- a/tensorflow/python/ops/linalg/linear_operator_circulant.py +++ b/tensorflow/python/ops/linalg/linear_operator_circulant.py @@ -30,6 +30,7 @@ from tensorflow.python.ops.distributions import util as distribution_util from tensorflow.python.ops.linalg import linalg_impl as linalg from tensorflow.python.ops.linalg import linear_operator from tensorflow.python.ops.linalg import linear_operator_util +from tensorflow.python.ops.signal import fft_ops from tensorflow.python.util.tf_export import tf_export __all__ = [ @@ -39,8 +40,8 @@ __all__ = [ ] # Different FFT Ops will be used for different block depths. -_FFT_OP = {1: math_ops.fft, 2: math_ops.fft2d, 3: math_ops.fft3d} -_IFFT_OP = {1: math_ops.ifft, 2: math_ops.ifft2d, 3: math_ops.ifft3d} +_FFT_OP = {1: fft_ops.fft, 2: fft_ops.fft2d, 3: fft_ops.fft3d} +_IFFT_OP = {1: fft_ops.ifft, 2: fft_ops.ifft2d, 3: fft_ops.ifft3d} # This is the only dtype allowed with fft ops. # TODO(langmore) Add other types once available. @@ -417,15 +418,13 @@ class _BaseLinearOperatorCirculant(linear_operator.LinearOperator): return math_ops.cast(y, self.dtype) def _determinant(self): - reduction_indices = [-(i + 1) for i in range(self.block_depth)] - det = math_ops.reduce_prod( - self.spectrum, reduction_indices=reduction_indices) + axis = [-(i + 1) for i in range(self.block_depth)] + det = math_ops.reduce_prod(self.spectrum, axis=axis) return math_ops.cast(det, self.dtype) def _log_abs_determinant(self): - reduction_indices = [-(i + 1) for i in range(self.block_depth)] - lad = math_ops.reduce_sum( - math_ops.log(self._abs_spectrum), reduction_indices=reduction_indices) + axis = [-(i + 1) for i in range(self.block_depth)] + lad = math_ops.reduce_sum(math_ops.log(self._abs_spectrum), axis=axis) return math_ops.cast(lad, self.dtype) def _solve(self, rhs, adjoint=False, adjoint_arg=False): diff --git a/tensorflow/python/ops/linalg/linear_operator_composition.py b/tensorflow/python/ops/linalg/linear_operator_composition.py index 0292bc51dc..f499b30661 100644 --- a/tensorflow/python/ops/linalg/linear_operator_composition.py +++ b/tensorflow/python/ops/linalg/linear_operator_composition.py @@ -275,6 +275,3 @@ class LinearOperatorComposition(linear_operator.LinearOperator): for operator in solve_order_list[1:]: solution = operator.solve(solution, adjoint=adjoint) return solution - - def _add_to_tensor(self, x): - return self.to_dense() + x diff --git a/tensorflow/python/ops/linalg/linear_operator_diag.py b/tensorflow/python/ops/linalg/linear_operator_diag.py index ed53decc00..be893c705c 100644 --- a/tensorflow/python/ops/linalg/linear_operator_diag.py +++ b/tensorflow/python/ops/linalg/linear_operator_diag.py @@ -228,11 +228,11 @@ class LinearOperatorDiag(linear_operator.LinearOperator): return diag_mat * x def _determinant(self): - return math_ops.reduce_prod(self._diag, reduction_indices=[-1]) + return math_ops.reduce_prod(self._diag, axis=[-1]) def _log_abs_determinant(self): log_det = math_ops.reduce_sum( - math_ops.log(math_ops.abs(self._diag)), reduction_indices=[-1]) + math_ops.log(math_ops.abs(self._diag)), axis=[-1]) if self.dtype.is_complex: log_det = math_ops.cast(log_det, dtype=self.dtype) return log_det diff --git a/tensorflow/python/ops/linalg/linear_operator_inversion.py b/tensorflow/python/ops/linalg/linear_operator_inversion.py new file mode 100644 index 0000000000..7aa4b40e16 --- /dev/null +++ b/tensorflow/python/ops/linalg/linear_operator_inversion.py @@ -0,0 +1,207 @@ +# Copyright 2018 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. +# ============================================================================== +"""Inverts a non-singular `LinearOperator`.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.framework import ops +from tensorflow.python.ops.linalg import linear_operator +from tensorflow.python.util.tf_export import tf_export + +__all__ = [] + + +@tf_export("linalg.LinearOperatorInversion") +class LinearOperatorInversion(linear_operator.LinearOperator): + """`LinearOperator` representing the inverse of another operator. + + This operator represents the inverse of another operator. + + ```python + # Create a 2 x 2 linear operator. + operator = LinearOperatorFullMatrix([[1., 0.], [0., 2.]]) + operator_inv = LinearOperatorInversion(operator) + + operator_inv.to_dense() + ==> [[1., 0.] + [0., 0.5]] + + operator_inv.shape + ==> [2, 2] + + operator_inv.log_abs_determinant() + ==> - log(2) + + x = ... Shape [2, 4] Tensor + operator_inv.matmul(x) + ==> Shape [2, 4] Tensor, equal to operator.solve(x) + ``` + + #### Performance + + The performance of `LinearOperatorInversion` depends on the underlying + operators performance: `solve` and `matmul` are swapped, and determinant is + inverted. + + #### Matrix property hints + + This `LinearOperator` is initialized with boolean flags of the form `is_X`, + for `X = non_singular, self_adjoint, positive_definite, square`. + These have the following meaning: + + * If `is_X == True`, callers should expect the operator to have the + property `X`. This is a promise that should be fulfilled, but is *not* a + runtime assert. For example, finite floating point precision may result + in these promises being violated. + * If `is_X == False`, callers should expect the operator to not have `X`. + * If `is_X == None` (the default), callers should have no expectation either + way. + """ + + def __init__(self, + operator, + is_non_singular=None, + is_self_adjoint=None, + is_positive_definite=None, + is_square=None, + name=None): + r"""Initialize a `LinearOperatorInversion`. + + `LinearOperatorInversion` is initialized with an operator `A`. The `solve` + and `matmul` methods are effectively swapped. E.g. + + ``` + A = MyLinearOperator(...) + B = LinearOperatorInversion(A) + x = [....] # a vector + + assert A.matvec(x) == B.solvevec(x) + ``` + + Args: + operator: `LinearOperator` object. If `operator.is_non_singular == False`, + an exception is raised. We do allow `operator.is_non_singular == None`, + in which case this operator will have `is_non_singular == None`. + Similarly for `is_self_adjoint` and `is_positive_definite`. + is_non_singular: Expect that this operator is non-singular. + is_self_adjoint: Expect that this operator is equal to its hermitian + transpose. + 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`. Default is `operator.name + + "_inv"`. + + Raises: + ValueError: If `operator.is_non_singular` is False. + """ + + self._operator = operator + + # Auto-set and check hints. + if operator.is_non_singular is False or is_non_singular is False: + raise ValueError( + "operator and supplied hints must have `is_non_singular` equal to " + "`True` or `None`. Found %s, %s" % (operator.is_non_singular, + is_non_singular)) + if operator.is_square is False or is_square is False: + raise ValueError( + "operator and supplied hints must have `is_square` equal to " + "`True` or `None`. Found %s, %s" % (operator.is_square, is_square)) + + # The congruency of is_non_singular and is_self_adjoint was checked in the + # base operator. Other hints are, in this special case of inversion, ones + # that must be the same for base/derived operator. + def _combined_hint(hint_str, provided_hint_value, message): + """Get combined hint in the case where operator.hint should equal hint.""" + op_hint = getattr(operator, hint_str) + if op_hint is False and provided_hint_value: + raise ValueError(message) + if op_hint and provided_hint_value is False: + raise ValueError(message) + return (op_hint or provided_hint_value) or None + + is_square = _combined_hint( + "is_square", is_square, + "An operator is square if and only if its inverse is square.") + + is_non_singular = _combined_hint( + "is_non_singular", is_non_singular, + "An operator is non-singular if and only if its inverse is " + "non-singular.") + + is_self_adjoint = _combined_hint( + "is_self_adjoint", is_self_adjoint, + "An operator is self-adjoint if and only if its inverse is " + "self-adjoint.") + + is_positive_definite = _combined_hint( + "is_positive_definite", is_positive_definite, + "An operator is positive-definite if and only if its inverse is " + "positive-definite.") + + is_square = _combined_hint( + "is_square", is_square, + "An operator is square if and only if its inverse is square.") + + # Initialization. + if name is None: + name = operator.name + "_inv" + with ops.name_scope(name, values=operator.graph_parents): + super(LinearOperatorInversion, self).__init__( + dtype=operator.dtype, + graph_parents=operator.graph_parents, + is_non_singular=is_non_singular, + is_self_adjoint=is_self_adjoint, + is_positive_definite=is_positive_definite, + is_square=is_square, + name=name) + + @property + def operator(self): + """The operator before inversion.""" + return self._operator + + def _assert_non_singular(self): + return self.operator.assert_non_singular() + + def _assert_positive_definite(self): + return self.operator.assert_positive_definite() + + def _assert_self_adjoint(self): + return self.operator.assert_self_adjoint() + + def _shape(self): + return self.operator.shape + + def _shape_tensor(self): + return self.operator.shape_tensor() + + def _matmul(self, x, adjoint=False, adjoint_arg=False): + return self.operator.solve(x, adjoint=adjoint, adjoint_arg=adjoint_arg) + + def _determinant(self): + return 1. / self.operator.determinant() + + def _log_abs_determinant(self): + return -1. * self.operator.log_abs_determinant() + + def _solve(self, rhs, adjoint=False, adjoint_arg=False): + return self.operator.matmul(rhs, adjoint=adjoint, adjoint_arg=adjoint_arg) diff --git a/tensorflow/python/ops/linalg/linear_operator_kronecker.py b/tensorflow/python/ops/linalg/linear_operator_kronecker.py index 1fd5073c17..f7e785caa5 100644 --- a/tensorflow/python/ops/linalg/linear_operator_kronecker.py +++ b/tensorflow/python/ops/linalg/linear_operator_kronecker.py @@ -30,9 +30,7 @@ from tensorflow.python.ops.linalg import linalg_impl as linalg from tensorflow.python.ops.linalg import linear_operator from tensorflow.python.util.tf_export import tf_export -__all__ = [ - "LinearOperatorKronecker", -] +__all__ = ["LinearOperatorKronecker"] def _vec(x): diff --git a/tensorflow/python/ops/linalg/linear_operator_low_rank_update.py b/tensorflow/python/ops/linalg/linear_operator_low_rank_update.py index c4288ff8f8..aa0500aff0 100644 --- a/tensorflow/python/ops/linalg/linear_operator_low_rank_update.py +++ b/tensorflow/python/ops/linalg/linear_operator_low_rank_update.py @@ -391,7 +391,7 @@ class LinearOperatorLowRankUpdate(linear_operator.LinearOperator): if self._use_cholesky: chol_cap_diag = array_ops.matrix_diag_part(self._chol_capacitance) log_abs_det_c = 2 * math_ops.reduce_sum( - math_ops.log(chol_cap_diag), reduction_indices=[-1]) + math_ops.log(chol_cap_diag), axis=[-1]) else: det_c = linalg_ops.matrix_determinant(self._capacitance) log_abs_det_c = math_ops.log(math_ops.abs(det_c)) diff --git a/tensorflow/python/ops/linalg/linear_operator_lower_triangular.py b/tensorflow/python/ops/linalg/linear_operator_lower_triangular.py index ca6d3f5405..d33fe17e04 100644 --- a/tensorflow/python/ops/linalg/linear_operator_lower_triangular.py +++ b/tensorflow/python/ops/linalg/linear_operator_lower_triangular.py @@ -195,11 +195,11 @@ class LinearOperatorLowerTriangular(linear_operator.LinearOperator): self._tril, x, adjoint_a=adjoint, adjoint_b=adjoint_arg) def _determinant(self): - return math_ops.reduce_prod(self._diag, reduction_indices=[-1]) + return math_ops.reduce_prod(self._diag, axis=[-1]) def _log_abs_determinant(self): return math_ops.reduce_sum( - math_ops.log(math_ops.abs(self._diag)), reduction_indices=[-1]) + math_ops.log(math_ops.abs(self._diag)), axis=[-1]) def _solve(self, rhs, adjoint=False, adjoint_arg=False): rhs = linalg.adjoint(rhs) if adjoint_arg else rhs diff --git a/tensorflow/python/ops/linalg/linear_operator_test_util.py b/tensorflow/python/ops/linalg/linear_operator_test_util.py index 76d659f109..e50f572b5f 100644 --- a/tensorflow/python/ops/linalg/linear_operator_test_util.py +++ b/tensorflow/python/ops/linalg/linear_operator_test_util.py @@ -102,7 +102,9 @@ class LinearOperatorDerivedClassTest(test.TestCase): raise NotImplementedError("operator_build_infos has not been implemented.") @abc.abstractmethod - def _operator_and_matrix(self, build_info, dtype, use_placeholder): + def _operator_and_matrix( + self, build_info, dtype, use_placeholder, + ensure_self_adjoint_and_pd=False): """Build a batch matrix and an Operator that should have similar behavior. Every operator acts like a (batch) matrix. This method returns both @@ -114,6 +116,11 @@ class LinearOperatorDerivedClassTest(test.TestCase): dtype: Numpy dtype. Data type of returned array/operator. use_placeholder: Python bool. If True, initialize the operator with a placeholder of undefined shape and correct dtype. + ensure_self_adjoint_and_pd: If `True`, + construct this operator to be Hermitian Positive Definite, as well + as ensuring the hints `is_positive_definite` and `is_self_adjoint` + are set. + This is useful for testing methods such as `cholesky`. Returns: operator: `LinearOperator` subclass instance. @@ -271,6 +278,21 @@ class LinearOperatorDerivedClassTest(test.TestCase): self._skip_if_tests_to_skip_contains("matmul_with_broadcast") self._test_matmul(with_batch=False) + def test_cholesky(self): + self._skip_if_tests_to_skip_contains("cholesky") + for use_placeholder in self._use_placeholder_options: + for build_info in self._operator_build_infos: + for dtype in self._dtypes_to_test: + with self.test_session(graph=ops.Graph()) as sess: + sess.graph.seed = random_seed.DEFAULT_GRAPH_SEED + operator, mat = self._operator_and_matrix( + build_info, dtype, use_placeholder=use_placeholder, + ensure_self_adjoint_and_pd=True) + op_chol = operator.cholesky().to_dense() + mat_chol = linalg_ops.cholesky(mat) + op_chol_v, mat_chol_v = sess.run([op_chol, mat_chol]) + self.assertAC(mat_chol_v, op_chol_v) + def _test_solve(self, with_batch): for use_placeholder in self._use_placeholder_options: for build_info in self._operator_build_infos: @@ -441,7 +463,7 @@ class NonSquareLinearOperatorDerivedClassTest(LinearOperatorDerivedClassTest): @property def _tests_to_skip(self): """List of test names to skip.""" - return ["solve", "solve_with_broadcast", "det", "log_abs_det"] + return ["cholesky", "solve", "solve_with_broadcast", "det", "log_abs_det"] @property def _operator_build_infos(self): diff --git a/tensorflow/python/ops/linalg/matmul_registrations.py b/tensorflow/python/ops/linalg/matmul_registrations.py new file mode 100644 index 0000000000..e0ac988ba2 --- /dev/null +++ b/tensorflow/python/ops/linalg/matmul_registrations.py @@ -0,0 +1,252 @@ +# Copyright 2018 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. +# ============================================================================== +"""Registrations for LinearOperator.matmul.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.ops.linalg import linear_operator +from tensorflow.python.ops.linalg import linear_operator_algebra +from tensorflow.python.ops.linalg import linear_operator_circulant +from tensorflow.python.ops.linalg import linear_operator_composition +from tensorflow.python.ops.linalg import linear_operator_diag +from tensorflow.python.ops.linalg import linear_operator_identity +from tensorflow.python.ops.linalg import linear_operator_lower_triangular +from tensorflow.python.ops.linalg import linear_operator_zeros + + +def _combined_self_adjoint_hint(operator_a, operator_b): + """Get combined hint for self-adjoint-ness.""" + # Note: only use this method in the commuting case. + # The property is preserved under composition when the operators commute. + if operator_a.is_self_adjoint and operator_b.is_self_adjoint: + return True + + # The property is not preserved when an operator with the property is composed + # with an operator without the property. + if ((operator_a.is_self_adjoint is True and + operator_b.is_self_adjoint is False) or + (operator_a.is_self_adjoint is False and + operator_b.is_self_adjoint is True)): + return False + + # The property is not known when operators are not known to have the property + # or both operators don't have the property (the property for the complement + # class is not closed under composition). + return None + + +def _is_square(operator_a, operator_b): + """Return a hint to whether the composition is square.""" + if operator_a.is_square and operator_b.is_square: + return True + if operator_a.is_square is False and operator_b.is_square is False: + # Let A have shape [B, M, N], B have shape [B, N, L]. + m = operator_a.range_dimension + l = operator_b.domain_dimension + if m is not None and l is not None: + return m == l + + return None + + +def _combined_positive_definite_hint(operator_a, operator_b): + """Get combined PD hint for compositions.""" + # Note: Positive definiteness is only guaranteed to be preserved + # when the operators commute and are symmetric. Only use this method in + # commuting cases. + + if (operator_a.is_positive_definite is True and + operator_a.is_self_adjoint is True and + operator_b.is_positive_definite is True and + operator_b.is_self_adjoint is True): + return True + + return None + + +def _combined_non_singular_hint(operator_a, operator_b): + """Get combined hint for when .""" + # If either operator is not-invertible the composition isn't. + if (operator_a.is_non_singular is False or + operator_b.is_non_singular is False): + return False + + return operator_a.is_non_singular and operator_b.is_non_singular + + +# By default, use a LinearOperatorComposition to delay the computation. +@linear_operator_algebra.RegisterMatmul( + linear_operator.LinearOperator, linear_operator.LinearOperator) +def _matmul_linear_operator(linop_a, linop_b): + """Generic matmul of two `LinearOperator`s.""" + is_square = _is_square(linop_a, linop_b) + is_non_singular = None + is_self_adjoint = None + is_positive_definite = None + + if is_square: + is_non_singular = _combined_non_singular_hint(linop_a, linop_b) + is_self_adjoint = _combined_self_adjoint_hint(linop_a, linop_b) + elif is_square is False: + is_non_singular = False + is_self_adjoint = False + is_positive_definite = False + + return linear_operator_composition.LinearOperatorComposition( + operators=[linop_a, linop_b], + is_non_singular=is_non_singular, + is_self_adjoint=is_self_adjoint, + is_positive_definite=is_positive_definite, + is_square=is_square, + ) + +# Identity + + +@linear_operator_algebra.RegisterMatmul( + linear_operator_identity.LinearOperatorIdentity, + linear_operator.LinearOperator) +def _matmul_linear_operator_identity_left(identity, linop): + del identity + return linop + + +@linear_operator_algebra.RegisterMatmul( + linear_operator.LinearOperator, + linear_operator_identity.LinearOperatorIdentity) +def _matmul_linear_operator_identity_right(linop, identity): + del identity + return linop + + +# Zeros + + +@linear_operator_algebra.RegisterMatmul( + linear_operator.LinearOperator, + linear_operator_zeros.LinearOperatorZeros) +def _matmul_linear_operator_zeros_right(linop, zeros): + if not zeros.is_square or not linop.is_square: + raise ValueError("Matmul with non-square `LinearOperator`s or non-square " + "`LinearOperatorZeros` not supported at this time.") + return zeros + + +@linear_operator_algebra.RegisterMatmul( + linear_operator_zeros.LinearOperatorZeros, + linear_operator.LinearOperator) +def _matmul_linear_operator_zeros_left(zeros, linop): + if not zeros.is_square or not linop.is_square: + raise ValueError("Matmul with non-square `LinearOperator`s or non-square " + "`LinearOperatorZeros` not supported at this time.") + return zeros + + +# Diag. + + +@linear_operator_algebra.RegisterMatmul( + linear_operator_diag.LinearOperatorDiag, + linear_operator_diag.LinearOperatorDiag) +def _matmul_linear_operator_diag(linop_a, linop_b): + return linear_operator_diag.LinearOperatorDiag( + diag=linop_a.diag * linop_b.diag, + 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) + + +@linear_operator_algebra.RegisterMatmul( + linear_operator_diag.LinearOperatorDiag, + linear_operator_identity.LinearOperatorScaledIdentity) +def _matmul_linear_operator_diag_scaled_identity_right( + linop_diag, linop_scaled_identity): + return linear_operator_diag.LinearOperatorDiag( + diag=linop_diag.diag * linop_scaled_identity.multiplier, + is_non_singular=_combined_non_singular_hint( + linop_diag, linop_scaled_identity), + is_self_adjoint=_combined_self_adjoint_hint( + linop_diag, linop_scaled_identity), + is_positive_definite=_combined_positive_definite_hint( + linop_diag, linop_scaled_identity), + is_square=True) + + +@linear_operator_algebra.RegisterMatmul( + linear_operator_identity.LinearOperatorScaledIdentity, + linear_operator_diag.LinearOperatorDiag) +def _matmul_linear_operator_diag_scaled_identity_left( + linop_scaled_identity, linop_diag): + return linear_operator_diag.LinearOperatorDiag( + diag=linop_diag.diag * linop_scaled_identity.multiplier, + is_non_singular=_combined_non_singular_hint( + linop_diag, linop_scaled_identity), + is_self_adjoint=_combined_self_adjoint_hint( + linop_diag, linop_scaled_identity), + is_positive_definite=_combined_positive_definite_hint( + linop_diag, linop_scaled_identity), + is_square=True) + + +@linear_operator_algebra.RegisterMatmul( + linear_operator_diag.LinearOperatorDiag, + linear_operator_lower_triangular.LinearOperatorLowerTriangular) +def _matmul_linear_operator_diag_tril(linop_diag, linop_triangular): + return linear_operator_lower_triangular.LinearOperatorLowerTriangular( + tril=linop_diag.diag[..., None] * linop_triangular.to_dense(), + is_non_singular=_combined_non_singular_hint( + linop_diag, linop_triangular), + # This is safe to do since the Triangular matrix is only self-adjoint + # when it is a diagonal matrix, and hence commutes. + is_self_adjoint=_combined_self_adjoint_hint( + linop_diag, linop_triangular), + is_positive_definite=None, + is_square=True) + + +@linear_operator_algebra.RegisterMatmul( + linear_operator_lower_triangular.LinearOperatorLowerTriangular, + linear_operator_diag.LinearOperatorDiag) +def _matmul_linear_operator_tril_diag(linop_triangular, linop_diag): + return linear_operator_lower_triangular.LinearOperatorLowerTriangular( + tril=linop_triangular.to_dense() * linop_diag.diag, + is_non_singular=_combined_non_singular_hint( + linop_diag, linop_triangular), + # This is safe to do since the Triangular matrix is only self-adjoint + # when it is a diagonal matrix, and hence commutes. + is_self_adjoint=_combined_self_adjoint_hint( + linop_diag, linop_triangular), + is_positive_definite=None, + is_square=True) + +# Circulant. + + +@linear_operator_algebra.RegisterMatmul( + linear_operator_circulant.LinearOperatorCirculant, + linear_operator_circulant.LinearOperatorCirculant) +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) diff --git a/tensorflow/python/ops/linalg_ops.py b/tensorflow/python/ops/linalg_ops.py index bbccc7e036..1a9e7112b4 100644 --- a/tensorflow/python/ops/linalg_ops.py +++ b/tensorflow/python/ops/linalg_ops.py @@ -423,7 +423,78 @@ def svd(tensor, full_matrices=False, compute_uv=True, name=None): # pylint: disable=redefined-builtin -@tf_export('norm', 'linalg.norm') +@tf_export('norm', 'linalg.norm', v1=[]) +def norm_v2(tensor, + ord='euclidean', + axis=None, + keepdims=None, + name=None): + r"""Computes the norm of vectors, matrices, and tensors. + + This function can compute several different vector norms (the 1-norm, the + Euclidean or 2-norm, the inf-norm, and in general the p-norm for p > 0) and + matrix norms (Frobenius, 1-norm, 2-norm and inf-norm). + + Args: + tensor: `Tensor` of types `float32`, `float64`, `complex64`, `complex128` + ord: Order of the norm. Supported values are 'fro', 'euclidean', + `1`, `2`, `np.inf` and any positive real number yielding the corresponding + p-norm. Default is 'euclidean' which is equivalent to Frobenius norm if + `tensor` is a matrix and equivalent to 2-norm for vectors. + Some restrictions apply: + a) The Frobenius norm `fro` is not defined for vectors, + b) If axis is a 2-tuple (matrix norm), only 'euclidean', 'fro', `1`, + `2`, `np.inf` are supported. + See the description of `axis` on how to compute norms for a batch of + vectors or matrices stored in a tensor. + axis: If `axis` is `None` (the default), the input is considered a vector + and a single vector norm is computed over the entire set of values in the + tensor, i.e. `norm(tensor, ord=ord)` is equivalent to + `norm(reshape(tensor, [-1]), ord=ord)`. + If `axis` is a Python integer, the input is considered a batch of vectors, + and `axis` determines the axis in `tensor` over which to compute vector + norms. + If `axis` is a 2-tuple of Python integers it is considered a batch of + matrices and `axis` determines the axes in `tensor` over which to compute + a matrix norm. + Negative indices are supported. Example: If you are passing a tensor that + can be either a matrix or a batch of matrices at runtime, pass + `axis=[-2,-1]` instead of `axis=None` to make sure that matrix norms are + computed. + keepdims: If True, the axis indicated in `axis` are kept with size 1. + Otherwise, the dimensions in `axis` are removed from the output shape. + name: The name of the op. + + Returns: + output: A `Tensor` of the same type as tensor, containing the vector or + matrix norms. If `keepdims` is True then the rank of output is equal to + the rank of `tensor`. Otherwise, if `axis` is none the output is a scalar, + if `axis` is an integer, the rank of `output` is one less than the rank + of `tensor`, if `axis` is a 2-tuple the rank of `output` is two less + than the rank of `tensor`. + + Raises: + ValueError: If `ord` or `axis` is invalid. + + @compatibility(numpy) + Mostly equivalent to numpy.linalg.norm. + Not supported: ord <= 0, 2-norm for matrices, nuclear norm. + Other differences: + a) If axis is `None`, treats the flattened `tensor` as a vector + regardless of rank. + b) Explicitly supports 'euclidean' norm as the default, including for + higher order tensors. + @end_compatibility + """ + return norm(tensor=tensor, + ord=ord, + axis=axis, + keepdims=keepdims, + name=name) + + +# pylint: disable=redefined-builtin +@tf_export(v1=['norm', 'linalg.norm']) @deprecation.deprecated_args( None, 'keep_dims is deprecated, use keepdims instead', 'keep_dims') def norm(tensor, diff --git a/tensorflow/python/ops/list_ops.py b/tensorflow/python/ops/list_ops.py index b4a1fc6af6..dbaae886d4 100644 --- a/tensorflow/python/ops/list_ops.py +++ b/tensorflow/python/ops/list_ops.py @@ -21,6 +21,7 @@ from __future__ import print_function from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import tensor_shape from tensorflow.python.ops import array_ops from tensorflow.python.ops import gen_list_ops # go/tf-wildcard-import @@ -29,7 +30,9 @@ from tensorflow.python.ops.gen_list_ops import * # pylint: enable=wildcard-import -ops.NotDifferentiable("TensorListConcat") +ops.NotDifferentiable("TensorListConcatLists") +ops.NotDifferentiable("TensorListElementShape") +ops.NotDifferentiable("TensorListLength") ops.NotDifferentiable("TensorListPushBackBatch") @@ -41,12 +44,42 @@ def empty_tensor_list(element_shape, max_num_elements = -1 return gen_list_ops.empty_tensor_list( - element_shape=element_shape, + element_shape=_build_element_shape(element_shape), element_dtype=element_dtype, max_num_elements=max_num_elements, name=name) +def tensor_list_reserve(element_shape, num_elements, element_dtype, name=None): + return gen_list_ops.tensor_list_reserve( + element_shape=_build_element_shape(element_shape), + num_elements=num_elements, + element_dtype=element_dtype, + name=name) + + +def tensor_list_from_tensor(tensor, element_shape, name=None): + return gen_list_ops.tensor_list_from_tensor( + tensor=tensor, + element_shape=_build_element_shape(element_shape), + name=name) + + +def tensor_list_concat(input_handle, element_dtype, name=None): + # Ignore the lengths output of TensorListConcat. It is only used during + # gradient computation. + return gen_list_ops.tensor_list_concat( + input_handle=input_handle, element_dtype=element_dtype, name=name)[0] + + +def tensor_list_split(tensor, element_shape, lengths, name=None): + return gen_list_ops.tensor_list_split( + tensor=tensor, + element_shape=_build_element_shape(element_shape), + lengths=lengths, + name=name) + + @ops.RegisterGradient("TensorListPushBack") def _PushBackGrad(op, dresult): return gen_list_ops.tensor_list_pop_back( @@ -65,14 +98,32 @@ def _PopBackGrad(op, dlist, delement): @ops.RegisterGradient("TensorListStack") def _TensorListStackGrad(unused_op, dtensor): - return gen_list_ops.tensor_list_from_tensor(dtensor, - element_shape=dtensor.shape[1:]) + return tensor_list_from_tensor(dtensor, element_shape=dtensor.shape[1:]) + + +@ops.RegisterGradient("TensorListConcat") +def _TensorListConcatGrad(op, dtensor, unused_dlengths): + # TODO(srbs): We lose the element_shape information in tensor_list_concat. + # Consider providing that as an output of TensorListConcat? + if dtensor.shape.rank is None: + element_shape = None + else: + element_shape = [None] + dtensor.shape.as_list()[1:] + return tensor_list_split( + dtensor, + element_shape=_build_element_shape(element_shape), + lengths=op.outputs[1]) + + +@ops.RegisterGradient("TensorListSplit") +def _TensorListSplitGrad(op, dlist): + return tensor_list_concat(dlist, element_dtype=op.inputs[0].dtype), None, None @ops.RegisterGradient("TensorListFromTensor") def _TensorListFromTensorGrad(op, dlist): """Gradient for TensorListFromTensor.""" - if op.inputs[0].shape.dims[0].value is not None: + if op.inputs[0].shape.dims and op.inputs[0].shape.dims[0].value is not None: num_elements = op.inputs[0].shape.dims[0].value else: num_elements = None @@ -126,3 +177,40 @@ def _TensorListScatterGrad(op, dlist): t, indices, _ = op.inputs return gen_list_ops.tensor_list_gather( dlist, indices, element_dtype=t.dtype), None + + +def _build_element_shape(shape): + """Converts shape to a format understood by list_ops for element_shape. + + If `shape` is already a `Tensor` it is returned as-is. We do not perform a + type check here. + + If shape is None or a TensorShape with unknown rank, -1 is returned. + + If shape is a scalar, an int32 tensor with empty list is returned. Note we + do directly return an empty list since ops.convert_to_tensor would conver it + to a float32 which is not a valid type for element_shape. + + If shape is a sequence of dims, None's in the list are replaced with -1. We + do not check the dtype of the other dims. + + Args: + shape: Could be None, Tensor, TensorShape or a list of dims (each dim could + be a None, scalar or Tensor). + + Returns: + A None-free shape that can be converted to a tensor. + """ + if isinstance(shape, ops.Tensor): + return shape + if isinstance(shape, tensor_shape.TensorShape): + # `TensorShape.as_list` requires rank to be known. + shape = shape.as_list() if shape else None + # Shape is unknown. + if shape is None: + return -1 + # Shape is a scalar. + if not shape: + return ops.convert_to_tensor(shape, dtype=dtypes.int32) + # Shape is a sequence of dimensions. Convert None dims to -1. + return [d if d is not None else -1 for d in shape] diff --git a/tensorflow/python/ops/lookup_ops.py b/tensorflow/python/ops/lookup_ops.py index 397d56ef40..758cb8041d 100644 --- a/tensorflow/python/ops/lookup_ops.py +++ b/tensorflow/python/ops/lookup_ops.py @@ -39,6 +39,7 @@ from tensorflow.python.ops import string_ops # pylint: disable=wildcard-import from tensorflow.python.ops.gen_lookup_ops import * # pylint: enable=wildcard-import +from tensorflow.python.training.checkpointable import base as checkpointable_base from tensorflow.python.training.checkpointable import tracking as checkpointable from tensorflow.python.util import compat from tensorflow.python.util.deprecation import deprecated @@ -160,7 +161,9 @@ class InitializableLookupTableBase(LookupInterface): self._default_value = ops.convert_to_tensor( default_value, dtype=self._value_dtype) self._default_value.get_shape().merge_with(tensor_shape.scalar()) - self._initializer = initializer + if isinstance(initializer, checkpointable_base.CheckpointableBase): + self._initializer = self._track_checkpointable( + initializer, "_initializer") self._resource_handle = self.create_resource() self._init_op = self.initialize() @@ -309,7 +312,7 @@ class HashTable(InitializableLookupTableBase): return exported_keys, exported_values -class TableInitializerBase(object): +class TableInitializerBase(checkpointable_base.CheckpointableBase): """Base class for lookup table initializers.""" def __init__(self, key_dtype, value_dtype): @@ -522,12 +525,14 @@ class TextFileInitializer(TableInitializerBase): if (vocab_size is not None) and (vocab_size <= 0): raise ValueError("Invalid vocab_size %s." % vocab_size) - self._filename = filename self._key_index = key_index self._value_index = value_index self._vocab_size = vocab_size self._delimiter = delimiter self._name = name + self._filename = self._track_checkpointable( + checkpointable.TrackableAsset(filename), + "_filename") super(TextFileInitializer, self).__init__(key_dtype, value_dtype) diff --git a/tensorflow/python/ops/losses/losses_impl.py b/tensorflow/python/ops/losses/losses_impl.py index 53c09ee8dd..9e9de62e6c 100644 --- a/tensorflow/python/ops/losses/losses_impl.py +++ b/tensorflow/python/ops/losses/losses_impl.py @@ -18,7 +18,6 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -from tensorflow.python.compat import compat from tensorflow.python.eager import context from tensorflow.python.framework import ops from tensorflow.python.ops import array_ops @@ -34,28 +33,48 @@ from tensorflow.python.util.deprecation import deprecated_argument_lookup from tensorflow.python.util.tf_export import tf_export -@tf_export("losses.Reduction") +@tf_export("losses.Reduction", "keras.losses.Reduction", v1=[]) +class ReductionV2(object): + """Types of loss reduction. + + Contains the following values: + `NONE`: Un-reduced weighted losses with the same shape as input. + `SUM`: Scalar sum of weighted losses. + `SUM_OVER_BATCH_SIZE`: Scalar `SUM` divided by number of elements in losses. + """ + + NONE = "none" + SUM = "sum" + SUM_OVER_BATCH_SIZE = "sum_over_batch_size" + + @classmethod + def all(cls): + return (cls.NONE, cls.SUM, cls.SUM_OVER_BATCH_SIZE) + + @classmethod + def validate(cls, key): + if key not in cls.all(): + raise ValueError("Invalid Reduction Key %s." % key) + + +@tf_export(v1=["losses.Reduction"]) class Reduction(object): """Types of loss reduction. Contains the following values: `NONE`: Un-reduced weighted losses with the same shape as input. `SUM`: Scalar sum of weighted losses. - `MEAN`: Scalar `SUM` divided by sum of weights. + `MEAN`: Scalar `SUM` divided by sum of weights. DEPRECATED. `SUM_OVER_BATCH_SIZE`: Scalar `SUM` divided by number of elements in losses. `SUM_OVER_NONZERO_WEIGHTS`: Scalar `SUM` divided by number of non-zero - weights. + weights. DEPRECATED. `SUM_BY_NONZERO_WEIGHTS`: Same as `SUM_OVER_NONZERO_WEIGHTS`. """ NONE = "none" - SUM = "weighted_sum" - - MEAN = "weighted_mean" - SUM_OVER_BATCH_SIZE = "weighted_sum_over_batch_size" - + MEAN = "weighted_mean" SUM_BY_NONZERO_WEIGHTS = "weighted_sum_by_nonzero_weights" SUM_OVER_NONZERO_WEIGHTS = SUM_BY_NONZERO_WEIGHTS @@ -72,35 +91,7 @@ class Reduction(object): @classmethod def validate(cls, key): if key not in cls.all(): - raise ValueError("Invalid ReductionKey %s." % key) - - -def _safe_div(numerator, denominator, name="value"): - """Computes a safe divide which returns 0 if the denominator is zero. - - Note that the function contains an additional conditional check that is - necessary for avoiding situations where the loss is zero causing NaNs to - creep into the gradient computation. - - Args: - numerator: An arbitrary `Tensor`. - denominator: A `Tensor` whose shape matches `numerator` and whose values are - assumed to be non-negative. - name: An optional name for the returned op. - - Returns: - The element-wise value of the numerator divided by the denominator. - """ - if compat.forward_compatible(2018, 11, 1): - return math_ops.div_no_nan(numerator, denominator, name=name) - return array_ops.where( - math_ops.greater(denominator, 0), - math_ops.div(numerator, - array_ops.where( - math_ops.equal(denominator, 0), - array_ops.ones_like(denominator), denominator)), - array_ops.zeros_like(numerator), - name=name) + raise ValueError("Invalid Reduction Key %s." % key) def _safe_mean(losses, num_present): @@ -115,7 +106,7 @@ def _safe_mean(losses, num_present): then zero is returned. """ total_loss = math_ops.reduce_sum(losses) - return _safe_div(total_loss, num_present) + return math_ops.div_no_nan(total_loss, num_present, name="value") def _num_present(losses, weights, per_batch=False): @@ -166,7 +157,7 @@ def _num_elements(losses): return math_ops.cast(array_ops.size(losses, name=scope), dtype=losses.dtype) -@tf_export("losses.compute_weighted_loss") +@tf_export(v1=["losses.compute_weighted_loss"]) def compute_weighted_loss( losses, weights=1.0, scope=None, loss_collection=ops.GraphKeys.LOSSES, reduction=Reduction.SUM_BY_NONZERO_WEIGHTS): @@ -236,7 +227,7 @@ def compute_weighted_loss( return loss -@tf_export("losses.absolute_difference") +@tf_export(v1=["losses.absolute_difference"]) def absolute_difference( labels, predictions, weights=1.0, scope=None, loss_collection=ops.GraphKeys.LOSSES, @@ -289,7 +280,7 @@ def absolute_difference( losses, weights, scope, loss_collection, reduction=reduction) -@tf_export("losses.cosine_distance") +@tf_export(v1=["losses.cosine_distance"]) @deprecated_args(None, "dim is deprecated, use axis instead", "dim") def cosine_distance( labels, predictions, axis=None, weights=1.0, scope=None, @@ -345,7 +336,7 @@ def cosine_distance( losses, weights, scope, loss_collection, reduction=reduction) -@tf_export("losses.hinge_loss") +@tf_export(v1=["losses.hinge_loss"]) def hinge_loss(labels, logits, weights=1.0, scope=None, loss_collection=ops.GraphKeys.LOSSES, reduction=Reduction.SUM_BY_NONZERO_WEIGHTS): @@ -395,7 +386,7 @@ def hinge_loss(labels, logits, weights=1.0, scope=None, losses, weights, scope, loss_collection, reduction=reduction) -@tf_export("losses.huber_loss") +@tf_export(v1=["losses.huber_loss"]) def huber_loss(labels, predictions, weights=1.0, delta=1.0, scope=None, loss_collection=ops.GraphKeys.LOSSES, reduction=Reduction.SUM_BY_NONZERO_WEIGHTS): @@ -473,7 +464,7 @@ def huber_loss(labels, predictions, weights=1.0, delta=1.0, scope=None, losses, weights, scope, loss_collection, reduction=reduction) -@tf_export("losses.log_loss") +@tf_export(v1=["losses.log_loss"]) def log_loss(labels, predictions, weights=1.0, epsilon=1e-7, scope=None, loss_collection=ops.GraphKeys.LOSSES, reduction=Reduction.SUM_BY_NONZERO_WEIGHTS): @@ -530,7 +521,7 @@ def log_loss(labels, predictions, weights=1.0, epsilon=1e-7, scope=None, # TODO(b/37208492): Add reduction arg. -@tf_export("losses.mean_pairwise_squared_error") +@tf_export(v1=["losses.mean_pairwise_squared_error"]) def mean_pairwise_squared_error( labels, predictions, weights=1.0, scope=None, loss_collection=ops.GraphKeys.LOSSES): @@ -595,26 +586,24 @@ def mean_pairwise_squared_error( diffs = math_ops.subtract(predictions, labels) - reduction_indices = math_ops.range(1, array_ops.rank(diffs)) + axis = math_ops.range(1, array_ops.rank(diffs)) sum_squares_diff_per_batch = math_ops.reduce_sum( - math_ops.square(diffs), - reduction_indices=reduction_indices, - keepdims=True) + math_ops.square(diffs), axis=axis, keepdims=True) num_present_per_batch = _num_present(diffs, weights, per_batch=True) - term1 = 2.0 * _safe_div( + term1 = 2.0 * math_ops.div_no_nan( sum_squares_diff_per_batch, - math_ops.maximum(num_present_per_batch - 1, 0)) + math_ops.maximum(num_present_per_batch - 1, 0), + name="value") - sum_diff = math_ops.reduce_sum( - diffs, reduction_indices=reduction_indices, keepdims=True) - term2 = 2.0 * _safe_div( + sum_diff = math_ops.reduce_sum(diffs, axis=axis, keepdims=True) + term2 = 2.0 * math_ops.div_no_nan( math_ops.square(sum_diff), math_ops.maximum( math_ops.multiply(num_present_per_batch, - num_present_per_batch - 1), - 0)) + num_present_per_batch - 1), 0), + name="value") weighted_losses = math_ops.multiply(term1 - term2, weights) loss = math_ops.reduce_sum(weighted_losses) @@ -628,7 +617,7 @@ def mean_pairwise_squared_error( return mean_loss -@tf_export("losses.mean_squared_error") +@tf_export(v1=["losses.mean_squared_error"]) def mean_squared_error( labels, predictions, weights=1.0, scope=None, loss_collection=ops.GraphKeys.LOSSES, @@ -681,7 +670,7 @@ def mean_squared_error( losses, weights, scope, loss_collection, reduction=reduction) -@tf_export("losses.sigmoid_cross_entropy") +@tf_export(v1=["losses.sigmoid_cross_entropy"]) def sigmoid_cross_entropy( multi_class_labels, logits, weights=1.0, label_smoothing=0, scope=None, loss_collection=ops.GraphKeys.LOSSES, @@ -745,7 +734,7 @@ def sigmoid_cross_entropy( losses, weights, scope, loss_collection, reduction=reduction) -@tf_export("losses.softmax_cross_entropy") +@tf_export(v1=["losses.softmax_cross_entropy"]) def softmax_cross_entropy( onehot_labels, logits, weights=1.0, label_smoothing=0, scope=None, loss_collection=ops.GraphKeys.LOSSES, @@ -867,7 +856,7 @@ def _remove_squeezable_dimensions( return labels, predictions, weights -@tf_export("losses.sparse_softmax_cross_entropy") +@tf_export(v1=["losses.sparse_softmax_cross_entropy"]) def sparse_softmax_cross_entropy( labels, logits, weights=1.0, scope=None, loss_collection=ops.GraphKeys.LOSSES, diff --git a/tensorflow/python/ops/losses/util_test.py b/tensorflow/python/ops/losses/util_test.py index df2e60e2e4..22a8eaae26 100644 --- a/tensorflow/python/ops/losses/util_test.py +++ b/tensorflow/python/ops/losses/util_test.py @@ -20,12 +20,14 @@ from __future__ import print_function from tensorflow.python.framework import constant_op from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops.losses import util from tensorflow.python.platform import test class LossesUtilTest(test.TestCase): + @test_util.run_deprecated_v1 def testGetRegularizationLoss(self): # Empty regularization collection should evaluate to 0.0. with self.cached_session(): diff --git a/tensorflow/python/ops/math_grad.py b/tensorflow/python/ops/math_grad.py index 35278d9680..c7ec1c57d1 100644 --- a/tensorflow/python/ops/math_grad.py +++ b/tensorflow/python/ops/math_grad.py @@ -1041,11 +1041,12 @@ def _PowGrad(op, grad): # Avoid false singularity at x = 0 if x.dtype.is_complex: # real(x) < 0 is fine for the complex case - log_x = array_ops.where( - math_ops.not_equal(x, 0), math_ops.log(x), array_ops.zeros_like(x)) + mask = math_ops.not_equal(x, 0) else: # There's no sensible real value to return if x < 0, so return 0 - log_x = array_ops.where(x > 0, math_ops.log(x), array_ops.zeros_like(x)) + mask = x > 0 + safe_x = array_ops.where(mask, x, array_ops.ones_like(x)) + log_x = array_ops.where(mask, math_ops.log(safe_x), array_ops.zeros_like(x)) gy = array_ops.reshape(math_ops.reduce_sum(grad * z * log_x, ry), sy) return gx, gy diff --git a/tensorflow/python/ops/math_grad_test.py b/tensorflow/python/ops/math_grad_test.py index d1fe834fc7..822f89768c 100644 --- a/tensorflow/python/ops/math_grad_test.py +++ b/tensorflow/python/ops/math_grad_test.py @@ -20,9 +20,13 @@ from __future__ import print_function import numpy as np +from tensorflow.python.eager import backprop +from tensorflow.python.eager import context +from tensorflow.python.eager import execution_callbacks from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import gradients @@ -52,6 +56,7 @@ class SquaredDifferenceOpTest(test.TestCase): self.assertLess(left_err, 1e-10) self.assertLess(right_err, 1e-10) + @test_util.run_deprecated_v1 def testGrad(self): self._testGrad([1, 2, 3, 2], [3, 2]) self._testGrad([2, 4], [3, 2, 4]) @@ -83,6 +88,7 @@ class AbsOpTest(test.TestCase): value, shape, output, output.get_shape().as_list()) self.assertLess(error, max_error) + @test_util.run_deprecated_v1 def testComplexAbs(self): # Bias random test values away from zero to avoid numeric instabilities. self._testGrad( @@ -99,6 +105,7 @@ class AbsOpTest(test.TestCase): class MinOrMaxGradientTest(test.TestCase): + @test_util.run_deprecated_v1 def testMinGradient(self): inputs = constant_op.constant([1.0], dtype=dtypes.float32) outputs = math_ops.reduce_min(array_ops.concat([inputs, inputs], 0)) @@ -106,6 +113,7 @@ class MinOrMaxGradientTest(test.TestCase): error = gradient_checker.compute_gradient_error(inputs, [1], outputs, []) self.assertLess(error, 1e-4) + @test_util.run_deprecated_v1 def testMaxGradient(self): inputs = constant_op.constant([1.0], dtype=dtypes.float32) outputs = math_ops.reduce_max(array_ops.concat([inputs, inputs], 0)) @@ -116,6 +124,7 @@ class MinOrMaxGradientTest(test.TestCase): class MaximumOrMinimumGradientTest(test.TestCase): + @test_util.run_deprecated_v1 def testMaximumGradient(self): inputs = constant_op.constant([1.0, 2.0, 3.0, 4.0], dtype=dtypes.float32) outputs = math_ops.maximum(inputs, 3.0) @@ -123,6 +132,7 @@ class MaximumOrMinimumGradientTest(test.TestCase): error = gradient_checker.compute_gradient_error(inputs, [4], outputs, [4]) self.assertLess(error, 1e-4) + @test_util.run_deprecated_v1 def testMinimumGradient(self): inputs = constant_op.constant([1.0, 2.0, 3.0, 4.0], dtype=dtypes.float32) outputs = math_ops.minimum(inputs, 2.0) @@ -133,6 +143,7 @@ class MaximumOrMinimumGradientTest(test.TestCase): class ProdGradientTest(test.TestCase): + @test_util.run_deprecated_v1 def testProdGradient(self): inputs = constant_op.constant([[1., 2.], [3., 4.]], dtype=dtypes.float32) @@ -143,6 +154,7 @@ class ProdGradientTest(test.TestCase): outputs, outputs.get_shape().as_list()) self.assertLess(error, 1e-4) + @test_util.run_deprecated_v1 def testProdGradientForNegativeAxis(self): inputs = constant_op.constant([[1., 2.], [3., 4.]], dtype=dtypes.float32) @@ -153,6 +165,7 @@ class ProdGradientTest(test.TestCase): outputs, outputs.get_shape().as_list()) self.assertLess(error, 1e-4) + @test_util.run_deprecated_v1 def testProdGradientComplex(self): for dtype in dtypes.complex64, dtypes.complex128: inputs = constant_op.constant([[1 + 3j, 2 - 1j], [3j, 4]], @@ -164,6 +177,7 @@ class ProdGradientTest(test.TestCase): outputs, outputs.get_shape().as_list()) self.assertLess(error, 1e-4) + @test_util.run_deprecated_v1 def testProdGradientForNegativeAxisComplex(self): for dtype in dtypes.complex64, dtypes.complex128: inputs = constant_op.constant([[1 + 3j, 2 - 1j], [3j, 4]], @@ -178,6 +192,7 @@ class ProdGradientTest(test.TestCase): class SegmentMinOrMaxGradientTest(test.TestCase): + @test_util.run_deprecated_v1 def testSegmentMinGradient(self): data = constant_op.constant([1.0, 2.0, 3.0], dtype=dtypes.float32) segment_ids = constant_op.constant([0, 0, 1], dtype=dtypes.int64) @@ -187,6 +202,7 @@ class SegmentMinOrMaxGradientTest(test.TestCase): [2]) self.assertLess(error, 1e-4) + @test_util.run_deprecated_v1 def testSegmentMaxGradient(self): data = constant_op.constant([1.0, 2.0, 3.0], dtype=dtypes.float32) segment_ids = constant_op.constant([0, 0, 1], dtype=dtypes.int64) @@ -196,6 +212,7 @@ class SegmentMinOrMaxGradientTest(test.TestCase): [2]) self.assertLess(error, 1e-4) + @test_util.run_deprecated_v1 def testSegmentMinGradientWithTies(self): inputs = constant_op.constant([1.0], dtype=dtypes.float32) data = array_ops.concat([inputs, inputs], 0) @@ -206,6 +223,7 @@ class SegmentMinOrMaxGradientTest(test.TestCase): [1]) self.assertLess(error, 1e-4) + @test_util.run_deprecated_v1 def testSegmentMaxGradientWithTies(self): inputs = constant_op.constant([1.0], dtype=dtypes.float32) data = array_ops.concat([inputs, inputs], 0) @@ -219,6 +237,7 @@ class SegmentMinOrMaxGradientTest(test.TestCase): class FloorModGradientTest(test.TestCase): + @test_util.run_deprecated_v1 def testFloorModGradient(self): # Making sure the input is not near the discontinuity point where # x/y == floor(x/y) @@ -233,6 +252,7 @@ class FloorModGradientTest(test.TestCase): class DivNoNanGradientTest(test.TestCase): + @test_util.run_deprecated_v1 def testBasicGradient(self): inputs = constant_op.constant(np.arange(-3, 3), dtype=dtypes.float32) @@ -244,6 +264,7 @@ class DivNoNanGradientTest(test.TestCase): outputs.get_shape().as_list()) self.assertLess(error, 1e-4) + @test_util.run_deprecated_v1 def testGradientWithDenominatorIsZero(self): x = constant_op.constant(np.arange(-3, 3), dtype=dtypes.float32) @@ -263,6 +284,7 @@ class XlogyTest(test.TestCase): xlogy_ygrad = self.evaluate(gradients.gradients(math_ops.xlogy(x, y), y)[0]) return xlogy_xgrad, xlogy_ygrad + @test_util.run_deprecated_v1 def testNonZeroValuesGrad(self): for dtype in [dtypes.float16, dtypes.float32, dtypes.float64]: x = constant_op.constant(0.1, dtype=dtype) @@ -273,6 +295,7 @@ class XlogyTest(test.TestCase): self.assertAllClose(xlogy_expected_xgrad, xlogy_xgrad) self.assertAllClose(xlogy_expected_ygrad, xlogy_ygrad) + @test_util.run_deprecated_v1 def testZeroXGrad(self): for dtype in [dtypes.float16, dtypes.float32, dtypes.float64]: x = constant_op.constant(0., dtype=dtype) @@ -282,6 +305,7 @@ class XlogyTest(test.TestCase): self.assertAllClose(zero, xlogy_xgrad) self.assertAllClose(zero, xlogy_ygrad) + @test_util.run_deprecated_v1 def testZeroYGrad(self): for dtype in [dtypes.float16, dtypes.float32, dtypes.float64]: x = constant_op.constant(0.1, dtype=dtype) @@ -290,6 +314,7 @@ class XlogyTest(test.TestCase): self.assertAllClose(-np.inf, xlogy_xgrad) self.assertAllClose(np.inf, xlogy_ygrad) + @test_util.run_deprecated_v1 def testZeroXYGrad(self): for dtype in [dtypes.float16, dtypes.float32, dtypes.float64]: x = constant_op.constant(0., dtype=dtype) @@ -307,6 +332,7 @@ class XdivyTest(test.TestCase): xdivy_ygrad = self.evaluate(gradients.gradients(math_ops.xdivy(x, y), y)[0]) return xdivy_xgrad, xdivy_ygrad + @test_util.run_deprecated_v1 def testNonZeroValuesGrad(self): for dtype in [dtypes.float16, dtypes.float32, dtypes.float64]: x = constant_op.constant(0.1, dtype=dtype) @@ -317,6 +343,7 @@ class XdivyTest(test.TestCase): self.assertAllClose(xdivy_expected_xgrad, xdivy_xgrad) self.assertAllClose(xdivy_expected_ygrad, xdivy_ygrad) + @test_util.run_deprecated_v1 def testZeroXGrad(self): for dtype in [dtypes.float16, dtypes.float32, dtypes.float64]: x = constant_op.constant(0., dtype=dtype) @@ -326,6 +353,7 @@ class XdivyTest(test.TestCase): self.assertAllClose(zero, xdivy_xgrad) self.assertAllClose(zero, xdivy_ygrad) + @test_util.run_deprecated_v1 def testZeroYGrad(self): for dtype in [dtypes.float16, dtypes.float32, dtypes.float64]: x = constant_op.constant(0.1, dtype=dtype) @@ -334,6 +362,7 @@ class XdivyTest(test.TestCase): self.assertAllClose(np.inf, xdivy_xgrad) self.assertAllClose(-np.inf, xdivy_ygrad) + @test_util.run_deprecated_v1 def testZeroXYGrad(self): for dtype in [dtypes.float16, dtypes.float32, dtypes.float64]: x = constant_op.constant(0., dtype=dtype) @@ -344,5 +373,25 @@ class XdivyTest(test.TestCase): self.assertAllClose(zero, xdivy_ygrad) +@test_util.run_all_in_graph_and_eager_modes +class PowGradTest(test.TestCase): + + def test_zero_grad_tf_gradients(self): + if context.executing_eagerly(): + self.skipTest("tf.gradients not supported in eager.") + + x = constant_op.constant([-1., 0., 1.]) + g = self.evaluate(gradients.gradients(math_ops.pow(x, 2), x)[0]) + self.assertAllClose([-2., 0., 2.], g) + + def test_zero_grad_tape(self): + with execution_callbacks.errstate(inf_or_nan=execution_callbacks.RAISE): + x = constant_op.constant([-1, 0., 1.]) + with backprop.GradientTape() as tape: + tape.watch(x) + g = tape.gradient(math_ops.pow(x, 2), x) + g = self.evaluate(g) + self.assertAllClose([-2., 0., 2.], g) + if __name__ == "__main__": test.main() diff --git a/tensorflow/python/ops/math_ops.py b/tensorflow/python/ops/math_ops.py index 39b1ca8993..f0d8bed508 100644 --- a/tensorflow/python/ops/math_ops.py +++ b/tensorflow/python/ops/math_ops.py @@ -36,7 +36,6 @@ from tensorflow.python.ops import gen_data_flow_ops from tensorflow.python.ops import gen_math_ops from tensorflow.python.ops import gen_nn_ops from tensorflow.python.ops import gen_sparse_ops -from tensorflow.python.ops import gen_spectral_ops # go/tf-wildcard-import # pylint: disable=wildcard-import from tensorflow.python.ops.gen_math_ops import * @@ -44,6 +43,7 @@ from tensorflow.python.ops.gen_math_ops import * from tensorflow.python.platform import tf_logging as logging from tensorflow.python.util import compat from tensorflow.python.util import deprecation +from tensorflow.python.util import dispatch from tensorflow.python.util import nest from tensorflow.python.util.tf_export import tf_export @@ -52,8 +52,8 @@ linspace = gen_math_ops.lin_space arg_max = deprecation.deprecated(None, "Use `tf.math.argmax` instead")(arg_max) # pylint: disable=used-before-assignment arg_min = deprecation.deprecated(None, "Use `tf.math.argmin` instead")(arg_min) # pylint: disable=used-before-assignment -tf_export("arg_max")(arg_max) -tf_export("arg_min")(arg_min) +tf_export(v1=["arg_max"])(arg_max) +tf_export(v1=["arg_min"])(arg_min) # This is set by resource_variable_ops.py. It is included in this way since # there is a circular dependency between math_ops and resource_variable_ops @@ -83,8 +83,6 @@ def argmax(input, output_type=dtypes.int64): axis = deprecation.deprecated_argument_lookup( "axis", axis, "dimension", dimension) - if axis is None: - axis = 0 return argmax_v2(input, axis, output_type, name) @@ -112,6 +110,8 @@ def argmax_v2(input, Returns: A `Tensor` of type `output_type`. """ + if axis is None: + axis = 0 return gen_math_ops.arg_max(input, axis, name=name, output_type=output_type) @@ -128,8 +128,6 @@ def argmin(input, output_type=dtypes.int64): axis = deprecation.deprecated_argument_lookup( "axis", axis, "dimension", dimension) - if axis is None: - axis = 0 return argmin_v2(input, axis, output_type, name) @@ -157,6 +155,8 @@ def argmin_v2(input, Returns: A `Tensor` of type `output_type`. """ + if axis is None: + axis = 0 return gen_math_ops.arg_min(input, axis, name=name, output_type=output_type) @@ -166,6 +166,7 @@ def argmin_v2(input, # pylint: disable=anomalous-backslash-in-string,protected-access # pylint: disable=g-docstring-has-escape @tf_export("math.abs", "abs") +@dispatch.add_dispatch_support def abs(x, name=None): # pylint: disable=redefined-builtin r"""Computes the absolute value of a tensor. @@ -190,22 +191,10 @@ def abs(x, name=None): # pylint: disable=redefined-builtin of type `float32` or `float64`, respectively. """ with ops.name_scope(name, "Abs", [x]) as name: - if isinstance(x, sparse_tensor.SparseTensor): - if x.values.dtype.is_complex: - x_abs = gen_math_ops.complex_abs( - x.values, Tout=x.values.dtype.real_dtype, name=name) - return sparse_tensor.SparseTensor( - indices=x.indices, values=x_abs, dense_shape=x.dense_shape) - x_abs = gen_math_ops._abs(x.values, name=name) - return sparse_tensor.SparseTensor( - indices=x.indices, values=x_abs, dense_shape=x.dense_shape) - else: - x = ops.convert_to_tensor(x, name="x") - if x.dtype.is_complex: - return gen_math_ops.complex_abs(x, Tout=x.dtype.real_dtype, name=name) - return gen_math_ops._abs(x, name=name) - - + x = ops.convert_to_tensor(x, name="x") + if x.dtype.is_complex: + return gen_math_ops.complex_abs(x, Tout=x.dtype.real_dtype, name=name) + return gen_math_ops._abs(x, name=name) # pylint: enable=g-docstring-has-escape @@ -292,31 +281,7 @@ _sub.__doc__ = ( gen_math_ops.sub.__doc__ + ("" if _sub.__doc__ is None else _sub.__doc__)) -# pylint: disable=g-docstring-has-escape -@tf_export("math.negative", "negative") -def negative(x, name=None): - """Computes numerical negative value element-wise. - - I.e., \\(y = -x\\). - - Args: - x: A `Tensor` or `SparseTensor`. Must be one of the following types: `half`, - `float32`, `float64`, `int32`, `int64`, `complex64`, `complex128`. - name: A name for the operation (optional). - - Returns: - A `Tensor` or `SparseTensor`, respectively. Has the same type as `x`. - """ - with ops.name_scope(name, "Neg", [x]) as name: - if isinstance(x, sparse_tensor.SparseTensor): - x_neg = gen_math_ops.neg(x.values, name=name) - return sparse_tensor.SparseTensor( - indices=x.indices, values=x_neg, dense_shape=x.dense_shape) - else: - return gen_math_ops.neg(x, name=name) - - -# pylint: enable=g-docstring-has-escape +negative = gen_math_ops.neg # pylint: disable=g-docstring-has-escape @@ -342,107 +307,8 @@ def _neg(x, name=None): # pylint: enable=g-docstring-has-escape -@tf_export("math.sign", "sign") -def sign(x, name=None): - """Returns an element-wise indication of the sign of a number. - - `y = sign(x) = -1` if `x < 0`; 0 if `x == 0` or `tf.is_nan(x)`; 1 if `x > 0`. - - Zero is returned for NaN inputs. - - For complex numbers, `y = sign(x) = x / |x|` if `x != 0`, otherwise `y = 0`. - - Args: - x: A `Tensor` or `SparseTensor`. Must be one of the following types: `half`, - `float32`, `float64`, `int32`, `int64`, `complex64`, `complex128`. - name: A name for the operation (optional). - - Returns: - A `Tensor` or `SparseTensor`, respectively. Has the same type as `x`. - - @compatibility(numpy) - Equivalent to numpy.sign except for the behavior for input values of NaN. - @end_compatibility - """ - with ops.name_scope(name, "Sign", [x]) as name: - if isinstance(x, sparse_tensor.SparseTensor): - x_sign = gen_math_ops.sign(x.values, name=name) - return sparse_tensor.SparseTensor( - indices=x.indices, values=x_sign, dense_shape=x.dense_shape) - else: - return gen_math_ops.sign(x, name=name) - - -@tf_export("math.square", "square") -def square(x, name=None): - r"""Computes square of x element-wise. - - I.e., \\(y = x * x = x^2\\). - - Args: - x: A `Tensor` or `SparseTensor`. Must be one of the following types: `half`, - `float32`, `float64`, `int32`, `int64`, `complex64`, `complex128`. - name: A name for the operation (optional). - - Returns: - A `Tensor` or `SparseTensor`. Has the same type as `x`. - """ - with ops.name_scope(name, "Square", [x]) as name: - if isinstance(x, sparse_tensor.SparseTensor): - x_square = gen_math_ops.square(x.values, name=name) - return sparse_tensor.SparseTensor( - indices=x.indices, values=x_square, dense_shape=x.dense_shape) - else: - return gen_math_ops.square(x, name=name) - - -@tf_export("math.sqrt", "sqrt") -def sqrt(x, name=None): - r"""Computes square root of x element-wise. - - I.e., \\(y = \sqrt{x} = x^{1/2}\\). - - Args: - x: A `Tensor` or `SparseTensor`. Must be one of the following types: `half`, - `float32`, `float64`, `complex64`, `complex128`. - name: A name for the operation (optional). - - Returns: - A `Tensor` or `SparseTensor`, respectively. Has the same type as `x`. - """ - with ops.name_scope(name, "Sqrt", [x]) as name: - if isinstance(x, sparse_tensor.SparseTensor): - x_sqrt = gen_math_ops.sqrt(x.values, name=name) - return sparse_tensor.SparseTensor( - indices=x.indices, values=x_sqrt, dense_shape=x.dense_shape) - else: - return gen_math_ops.sqrt(x, name=name) - - -@tf_export("math.erf", v1=["math.erf", "erf"]) -@deprecation.deprecated_endpoints("erf") -def erf(x, name=None): - """Computes the Gauss error function of `x` element-wise. - - Args: - x: A `Tensor` or `SparseTensor`. Must be one of the following types: `half`, - `float32`, `float64`. - name: A name for the operation (optional). - - Returns: - A `Tensor` or `SparseTensor`, respectively. Has the same type as `x`. - """ - with ops.name_scope(name, "Erf", [x]) as name: - if isinstance(x, sparse_tensor.SparseTensor): - x_erf = gen_math_ops.erf(x.values, name=name) - return sparse_tensor.SparseTensor( - indices=x.indices, values=x_erf, dense_shape=x.dense_shape) - else: - return gen_math_ops.erf(x, name=name) - - -@tf_export("math.scalar_mul", "scalar_mul") -def scalar_mul(scalar, x): +@tf_export(v1=["math.scalar_mul", "scalar_mul"]) +def scalar_mul(scalar, x, name=None): """Multiplies a scalar times a `Tensor` or `IndexedSlices` object. Intended for use in gradient code which might deal with `IndexedSlices` @@ -452,6 +318,7 @@ def scalar_mul(scalar, x): Args: scalar: A 0-D scalar `Tensor`. Must have known shape. x: A `Tensor` or `IndexedSlices` to be scaled. + name: A name for the operation (optional). Returns: `scalar * x` of the same type (`Tensor` or `IndexedSlices`) as `x`. @@ -464,13 +331,21 @@ def scalar_mul(scalar, x): shape = scalar.get_shape() if shape.ndims == 0: if isinstance(x, ops.IndexedSlices): - return ops.IndexedSlices(scalar * x.values, x.indices, x.dense_shape) + return ops.IndexedSlices(gen_math_ops.mul(scalar, x.values, name), + x.indices, x.dense_shape) else: - return scalar * x + return gen_math_ops.mul(scalar, x, name) else: raise ValueError("Only scalar multiply works, got shape %s" % shape) +@tf_export("math.scalar_mul", "scalar_mul", v1=[]) +@_set_doc(scalar_mul.__doc__) +def scalar_mul_v2(scalar, x, name=None): + with ops.name_scope(name, "scalar_mul", [x]) as name: + return scalar_mul(scalar, x, name) + + @tf_export("math.pow", "pow") def pow(x, y, name=None): # pylint: disable=redefined-builtin r"""Computes the power of one value to another. @@ -1091,7 +966,10 @@ def truediv(x, y, name=None): return _truediv_python3(x, y, name) -@tf_export("div") +@deprecation.deprecated( + date=None, + instructions="Deprecated in favor of operator or tf.math.divide.") +@tf_export(v1=["div"]) def div(x, y, name=None): """Divides x / y elementwise (using Python 2 division operator semantics). @@ -1312,7 +1190,7 @@ def range(start, limit=None, delta=1, dtype=None, name="range"): # pylint: disa # Reduction operations -def _ReductionDims(x, axis, reduction_indices): +def _ReductionDims(x, axis, reduction_indices=None): # pylint: disable=invalid-name """Returns range(0, rank(x)) if reduction_indices is None.""" # TODO(aselle): Remove this after deprecation if reduction_indices is not None: @@ -1335,23 +1213,23 @@ def _ReductionDims(x, axis, reduction_indices): return range(0, array_ops.rank(x)) -def _may_reduce_to_scalar(keepdims, axis, reduction_indices, output): +def _may_reduce_to_scalar(keepdims, axis, output): """Set a reduction's output shape to be a scalar if we are certain.""" if not common_shapes.has_fully_defined_shape(output) and (not keepdims) and ( - axis is None) and (reduction_indices is None): + axis is None): output.set_shape(()) return output -@tf_export("math.reduce_sum", "reduce_sum") +@tf_export(v1=["math.reduce_sum", "reduce_sum"]) @deprecation.deprecated_args( None, "keep_dims is deprecated, use keepdims instead", "keep_dims") -def reduce_sum(input_tensor, - axis=None, - keepdims=None, - name=None, - reduction_indices=None, - keep_dims=None): +def reduce_sum_v1(input_tensor, + axis=None, + keepdims=None, + name=None, + reduction_indices=None, + keep_dims=None): """Computes the sum of elements across dimensions of a tensor. Reduces `input_tensor` along the dimensions given in `axis`. @@ -1391,21 +1269,61 @@ def reduce_sum(input_tensor, int64 while tensorflow returns the same dtype as the input. @end_compatibility """ + axis = deprecation.deprecated_argument_lookup( + "axis", axis, "reduction_indices", reduction_indices) keepdims = deprecation.deprecated_argument_lookup("keepdims", keepdims, "keep_dims", keep_dims) - if keepdims is None: - keepdims = False + return reduce_sum(input_tensor, axis, keepdims, name) + + +@tf_export("math.reduce_sum", "reduce_sum", v1=[]) +def reduce_sum(input_tensor, axis=None, keepdims=False, name=None): + """Computes the sum of elements across dimensions of a tensor. + + Reduces `input_tensor` along the dimensions given in `axis`. + Unless `keepdims` is true, the rank of the tensor is reduced by 1 for each + entry in `axis`. If `keepdims` is true, the reduced dimensions + are retained with length 1. - return _may_reduce_to_scalar(keepdims, axis, reduction_indices, - gen_math_ops._sum( - input_tensor, - _ReductionDims(input_tensor, axis, - reduction_indices), - keepdims, - name=name)) + If `axis` is None, all dimensions are reduced, and a + tensor with a single element is returned. + For example: -@tf_export("math.count_nonzero", "count_nonzero") + ```python + x = tf.constant([[1, 1, 1], [1, 1, 1]]) + tf.reduce_sum(x) # 6 + tf.reduce_sum(x, 0) # [2, 2, 2] + tf.reduce_sum(x, 1) # [3, 3] + tf.reduce_sum(x, 1, keepdims=True) # [[3], [3]] + tf.reduce_sum(x, [0, 1]) # 6 + ``` + + Args: + input_tensor: The tensor to reduce. Should have numeric type. + axis: The dimensions to reduce. If `None` (the default), reduces all + dimensions. Must be in the range `[-rank(input_tensor), + rank(input_tensor))`. + keepdims: If true, retains reduced dimensions with length 1. + name: A name for the operation (optional). + + Returns: + The reduced tensor, of the same dtype as the input_tensor. + + @compatibility(numpy) + Equivalent to np.sum apart the fact that numpy upcast uint8 and int32 to + int64 while tensorflow returns the same dtype as the input. + @end_compatibility + """ + keepdims = False if keepdims is None else keepdims + return _may_reduce_to_scalar( + keepdims, axis, + gen_math_ops._sum( + input_tensor, _ReductionDims(input_tensor, axis), keepdims, + name=name)) + + +@tf_export(v1=["math.count_nonzero", "count_nonzero"]) @deprecation.deprecated_args( None, "keep_dims is deprecated, use keepdims instead", "keep_dims") def count_nonzero(input_tensor, @@ -1466,32 +1384,89 @@ def count_nonzero(input_tensor, """ keepdims = deprecation.deprecated_argument_lookup("keepdims", keepdims, "keep_dims", keep_dims) + axis = deprecation.deprecated_argument_lookup( + "axis", axis, + "reduction_indices", reduction_indices + ) + + return count_nonzero_v2(input_tensor, axis, keepdims, dtype, name) + + +@tf_export("math.count_nonzero", v1=[]) +def count_nonzero_v2(input, # pylint: disable=redefined-builtin + axis=None, + keepdims=None, + dtype=dtypes.int64, + name=None): + """Computes number of nonzero elements across dimensions of a tensor. + + Reduces `input` along the dimensions given in `axis`. + Unless `keepdims` is true, the rank of the tensor is reduced by 1 for each + entry in `axis`. If `keepdims` is true, the reduced dimensions + are retained with length 1. + + If `axis` has no entries, all dimensions are reduced, and a + tensor with a single element is returned. + + **NOTE** Floating point comparison to zero is done by exact floating point + equality check. Small values are **not** rounded to zero for purposes of + the nonzero check. + + For example: + + ```python + x = tf.constant([[0, 1, 0], [1, 1, 0]]) + tf.count_nonzero(x) # 3 + tf.count_nonzero(x, 0) # [1, 2, 0] + tf.count_nonzero(x, 1) # [1, 2] + tf.count_nonzero(x, 1, keepdims=True) # [[1], [2]] + tf.count_nonzero(x, [0, 1]) # 3 + ``` + + **NOTE** Strings are compared against zero-length empty string `""`. Any + string with a size greater than zero is already considered as nonzero. + + For example: + ```python + x = tf.constant(["", "a", " ", "b", ""]) + tf.count_nonzero(x) # 3, with "a", " ", and "b" as nonzero strings. + ``` + + Args: + input: The tensor to reduce. Should be of numeric type, `bool`, + or `string`. + axis: The dimensions to reduce. If `None` (the default), + reduces all dimensions. Must be in the range + `[-rank(input), rank(input))`. + keepdims: If true, retains reduced dimensions with length 1. + dtype: The output dtype; defaults to `tf.int64`. + name: A name for the operation (optional). + + Returns: + The reduced tensor (number of nonzero values). + """ if keepdims is None: keepdims = False - - with ops.name_scope(name, "count_nonzero", [input_tensor]): - input_tensor = ops.convert_to_tensor(input_tensor, name="input_tensor") + with ops.name_scope(name, "count_nonzero", [input]): + input = ops.convert_to_tensor(input, name="input") # A scalar of 'zero' is enough as `not_equal` will broadcast. - zero = array_ops.zeros([], dtype=input_tensor.dtype) + zero = array_ops.zeros([], dtype=input.dtype) return cast( reduce_sum( # int64 reduction happens on GPU - to_int64(gen_math_ops.not_equal(input_tensor, zero)), + to_int64(gen_math_ops.not_equal(input, zero)), axis=axis, - keepdims=keepdims, - reduction_indices=reduction_indices), + keepdims=keepdims), dtype=dtype) -@tf_export("math.reduce_mean", "reduce_mean") -@deprecation.deprecated_args( - None, "keep_dims is deprecated, use keepdims instead", "keep_dims") -def reduce_mean(input_tensor, - axis=None, - keepdims=None, - name=None, - reduction_indices=None, - keep_dims=None): +@tf_export(v1=["math.reduce_mean", "reduce_mean"]) +def reduce_mean_v1(input_tensor, + axis=None, + keepdims=None, + name=None, + reduction_indices=None, + keep_dims=None): """Computes the mean of elements across dimensions of a tensor. Reduces `input_tensor` along the dimensions given in `axis`. @@ -1541,22 +1516,72 @@ def reduce_mean(input_tensor, @end_compatibility """ + axis = deprecation.deprecated_argument_lookup( + "axis", axis, "reduction_indices", reduction_indices) keepdims = deprecation.deprecated_argument_lookup("keepdims", keepdims, "keep_dims", keep_dims) + return reduce_mean(input_tensor, axis, keepdims, name) - if keepdims is None: - keepdims = False - return _may_reduce_to_scalar(keepdims, axis, reduction_indices, - gen_math_ops.mean( - input_tensor, - _ReductionDims(input_tensor, axis, - reduction_indices), - keepdims, - name=name)) + +@tf_export("math.reduce_mean", "reduce_mean", v1=[]) +def reduce_mean(input_tensor, axis=None, keepdims=False, name=None): + """Computes the mean of elements across dimensions of a tensor. + + Reduces `input_tensor` along the dimensions given in `axis`. + Unless `keepdims` is true, the rank of the tensor is reduced by 1 for each + entry in `axis`. If `keepdims` is true, the reduced dimensions + are retained with length 1. + + If `axis` is None, all dimensions are reduced, and a + tensor with a single element is returned. + + For example: + + ```python + x = tf.constant([[1., 1.], [2., 2.]]) + tf.reduce_mean(x) # 1.5 + tf.reduce_mean(x, 0) # [1.5, 1.5] + tf.reduce_mean(x, 1) # [1., 2.] + ``` + + Args: + input_tensor: The tensor to reduce. Should have numeric type. + axis: The dimensions to reduce. If `None` (the default), reduces all + dimensions. Must be in the range `[-rank(input_tensor), + rank(input_tensor))`. + keepdims: If true, retains reduced dimensions with length 1. + name: A name for the operation (optional). + + Returns: + The reduced tensor. + + @compatibility(numpy) + Equivalent to np.mean + + Please note that `np.mean` has a `dtype` parameter that could be used to + specify the output type. By default this is `dtype=float64`. On the other + hand, `tf.reduce_mean` has an aggressive type inference from `input_tensor`, + for example: + + ```python + x = tf.constant([1, 0, 1, 0]) + tf.reduce_mean(x) # 0 + y = tf.constant([1., 0., 1., 0.]) + tf.reduce_mean(y) # 0.5 + ``` + + @end_compatibility + """ + keepdims = False if keepdims is None else keepdims + return _may_reduce_to_scalar( + keepdims, axis, + gen_math_ops.mean( + input_tensor, _ReductionDims(input_tensor, axis), keepdims, + name=name)) @tf_export("math.reduce_variance") -def reduce_variance(input_tensor, axis=None, keepdims=None, name=None): +def reduce_variance(input_tensor, axis=None, keepdims=False, name=None): """Computes the variance of elements across dimensions of a tensor. Reduces `input_tensor` along the dimensions given in `axis`. @@ -1599,12 +1624,12 @@ def reduce_variance(input_tensor, axis=None, keepdims=None, name=None): name = name if name else "reduce_variance" with ops.name_scope(name): means = reduce_mean(input_tensor, axis=axis, keepdims=True) - squared_deviations = square(input_tensor - means) + squared_deviations = gen_math_ops.square(input_tensor - means) return reduce_mean(squared_deviations, axis=axis, keepdims=keepdims) @tf_export("math.reduce_std") -def reduce_std(input_tensor, axis=None, keepdims=None, name=None): +def reduce_std(input_tensor, axis=None, keepdims=False, name=None): """Computes the standard deviation of elements across dimensions of a tensor. Reduces `input_tensor` along the dimensions given in `axis`. @@ -1646,18 +1671,53 @@ def reduce_std(input_tensor, axis=None, keepdims=None, name=None): name = name if name else "reduce_std" with ops.name_scope(name): variance = reduce_variance(input_tensor, axis=axis, keepdims=keepdims) - return sqrt(variance) + return gen_math_ops.sqrt(variance) + + +@tf_export("math.reduce_prod", "reduce_prod", v1=[]) +def reduce_prod(input_tensor, axis=None, keepdims=False, name=None): + """Computes the product of elements across dimensions of a tensor. + + Reduces `input_tensor` along the dimensions given in `axis`. + Unless `keepdims` is true, the rank of the tensor is reduced by 1 for each + entry in `axis`. If `keepdims` is true, the reduced dimensions + are retained with length 1. + If `axis` is None, all dimensions are reduced, and a + tensor with a single element is returned. -@tf_export("math.reduce_prod", "reduce_prod") + Args: + input_tensor: The tensor to reduce. Should have numeric type. + axis: The dimensions to reduce. If `None` (the default), + reduces all dimensions. Must be in the range + `[-rank(input_tensor), rank(input_tensor))`. + keepdims: If true, retains reduced dimensions with length 1. + name: A name for the operation (optional). + + Returns: + The reduced tensor. + + @compatibility(numpy) + Equivalent to np.prod + @end_compatibility + """ + keepdims = False if keepdims is None else keepdims + return _may_reduce_to_scalar( + keepdims, axis, + gen_math_ops.prod( + input_tensor, _ReductionDims(input_tensor, axis), keepdims, + name=name)) + + +@tf_export(v1=["math.reduce_prod", "reduce_prod"]) @deprecation.deprecated_args( None, "keep_dims is deprecated, use keepdims instead", "keep_dims") -def reduce_prod(input_tensor, - axis=None, - keepdims=None, - name=None, - reduction_indices=None, - keep_dims=None): +def reduce_prod_v1(input_tensor, + axis=None, + keepdims=None, + name=None, + reduction_indices=None, + keep_dims=None): """Computes the product of elements across dimensions of a tensor. Reduces `input_tensor` along the dimensions given in `axis`. @@ -1670,6 +1730,127 @@ def reduce_prod(input_tensor, Args: input_tensor: The tensor to reduce. Should have numeric type. + axis: The dimensions to reduce. If `None` (the default), reduces all + dimensions. Must be in the range `[-rank(input_tensor), + rank(input_tensor))`. + keepdims: If true, retains reduced dimensions with length 1. + name: A name for the operation (optional). + reduction_indices: The old (deprecated) name for axis. + keep_dims: Deprecated alias for `keepdims`. + + Returns: + The reduced tensor. + + @compatibility(numpy) + Equivalent to np.prod + @end_compatibility + """ + axis = deprecation.deprecated_argument_lookup( + "axis", axis, "reduction_indices", reduction_indices) + keepdims = deprecation.deprecated_argument_lookup("keepdims", keepdims, + "keep_dims", keep_dims) + return reduce_prod(input_tensor, axis, keepdims, name) + + +@tf_export(v1=["math.reduce_min", "reduce_min"]) +@deprecation.deprecated_args( + None, "keep_dims is deprecated, use keepdims instead", "keep_dims") +def reduce_min_v1(input_tensor, + axis=None, + keepdims=None, + name=None, + reduction_indices=None, + keep_dims=None): + """Computes the minimum of elements across dimensions of a tensor. + + Reduces `input_tensor` along the dimensions given in `axis`. + Unless `keepdims` is true, the rank of the tensor is reduced by 1 for each + entry in `axis`. If `keepdims` is true, the reduced dimensions + are retained with length 1. + + If `axis` is None, all dimensions are reduced, and a + tensor with a single element is returned. + + Args: + input_tensor: The tensor to reduce. Should have real numeric type. + axis: The dimensions to reduce. If `None` (the default), reduces all + dimensions. Must be in the range `[-rank(input_tensor), + rank(input_tensor))`. + keepdims: If true, retains reduced dimensions with length 1. + name: A name for the operation (optional). + reduction_indices: The old (deprecated) name for axis. + keep_dims: Deprecated alias for `keepdims`. + + Returns: + The reduced tensor. + + @compatibility(numpy) + Equivalent to np.min + @end_compatibility + """ + axis = deprecation.deprecated_argument_lookup( + "axis", axis, "reduction_indices", reduction_indices) + keepdims = deprecation.deprecated_argument_lookup("keepdims", keepdims, + "keep_dims", keep_dims) + return reduce_min(input_tensor, axis, keepdims, name) + + +@tf_export("math.reduce_min", "reduce_min", v1=[]) +def reduce_min(input_tensor, axis=None, keepdims=False, name=None): + """Computes the minimum of elements across dimensions of a tensor. + + Reduces `input_tensor` along the dimensions given in `axis`. + Unless `keepdims` is true, the rank of the tensor is reduced by 1 for each + entry in `axis`. If `keepdims` is true, the reduced dimensions + are retained with length 1. + + If `axis` is None, all dimensions are reduced, and a + tensor with a single element is returned. + + Args: + input_tensor: The tensor to reduce. Should have real numeric type. + axis: The dimensions to reduce. If `None` (the default), reduces all + dimensions. Must be in the range `[-rank(input_tensor), + rank(input_tensor))`. + keepdims: If true, retains reduced dimensions with length 1. + name: A name for the operation (optional). + + Returns: + The reduced tensor. + + @compatibility(numpy) + Equivalent to np.min + @end_compatibility + """ + keepdims = False if keepdims is None else keepdims + return _may_reduce_to_scalar( + keepdims, axis, + gen_math_ops._min( + input_tensor, _ReductionDims(input_tensor, axis), keepdims, + name=name)) + + +@tf_export(v1=["math.reduce_max", "reduce_max"]) +@deprecation.deprecated_args( + None, "keep_dims is deprecated, use keepdims instead", "keep_dims") +def reduce_max_v1(input_tensor, + axis=None, + keepdims=None, + name=None, + reduction_indices=None, + keep_dims=None): + """Computes the maximum of elements across dimensions of a tensor. + + Reduces `input_tensor` along the dimensions given in `axis`. + Unless `keepdims` is true, the rank of the tensor is reduced by 1 for each + entry in `axis`. If `keepdims` is true, the reduced dimensions + are retained with length 1. + + If `axis` is None, all dimensions are reduced, and a + tensor with a single element is returned. + + Args: + input_tensor: The tensor to reduce. Should have real numeric type. axis: The dimensions to reduce. If `None` (the default), reduces all dimensions. Must be in the range `[-rank(input_tensor), rank(input_tensor))`. @@ -1682,33 +1863,106 @@ def reduce_prod(input_tensor, The reduced tensor. @compatibility(numpy) - Equivalent to np.prod + Equivalent to np.max + @end_compatibility + """ + axis = deprecation.deprecated_argument_lookup( + "axis", axis, "reduction_indices", reduction_indices) + keepdims = deprecation.deprecated_argument_lookup("keepdims", keepdims, + "keep_dims", keep_dims) + return reduce_max(input_tensor, axis, keepdims, name) + + +@tf_export("math.reduce_max", "reduce_max", v1=[]) +def reduce_max(input_tensor, axis=None, keepdims=False, name=None): + """Computes the maximum of elements across dimensions of a tensor. + + Reduces `input_tensor` along the dimensions given in `axis`. + Unless `keepdims` is true, the rank of the tensor is reduced by 1 for each + entry in `axis`. If `keepdims` is true, the reduced dimensions + are retained with length 1. + + If `axis` is None, all dimensions are reduced, and a + tensor with a single element is returned. + + Args: + input_tensor: The tensor to reduce. Should have real numeric type. + axis: The dimensions to reduce. If `None` (the default), reduces all + dimensions. Must be in the range `[-rank(input_tensor), + rank(input_tensor))`. + keepdims: If true, retains reduced dimensions with length 1. + name: A name for the operation (optional). + + Returns: + The reduced tensor. + + @compatibility(numpy) + Equivalent to np.max + @end_compatibility + """ + keepdims = False if keepdims is None else keepdims + return _may_reduce_to_scalar( + keepdims, axis, + gen_math_ops._max( + input_tensor, _ReductionDims(input_tensor, axis), keepdims, + name=name)) + + +@tf_export(v1=["math.reduce_all", "reduce_all"]) +@deprecation.deprecated_args( + None, "keep_dims is deprecated, use keepdims instead", "keep_dims") +def reduce_all_v1(input_tensor, + axis=None, + keepdims=None, + name=None, + reduction_indices=None, + keep_dims=None): + """Computes the "logical and" of elements across dimensions of a tensor. + + Reduces `input_tensor` along the dimensions given in `axis`. + Unless `keepdims` is true, the rank of the tensor is reduced by 1 for each + entry in `axis`. If `keepdims` is true, the reduced dimensions + are retained with length 1. + + If `axis` is None, all dimensions are reduced, and a + tensor with a single element is returned. + + For example: + + ```python + x = tf.constant([[True, True], [False, False]]) + tf.reduce_all(x) # False + tf.reduce_all(x, 0) # [False, False] + tf.reduce_all(x, 1) # [True, False] + ``` + + Args: + input_tensor: The boolean tensor to reduce. + axis: The dimensions to reduce. If `None` (the default), reduces all + dimensions. Must be in the range `[-rank(input_tensor), + rank(input_tensor))`. + keepdims: If true, retains reduced dimensions with length 1. + name: A name for the operation (optional). + reduction_indices: The old (deprecated) name for axis. + keep_dims: Deprecated alias for `keepdims`. + + Returns: + The reduced tensor. + + @compatibility(numpy) + Equivalent to np.all @end_compatibility """ + axis = deprecation.deprecated_argument_lookup( + "axis", axis, "reduction_indices", reduction_indices) keepdims = deprecation.deprecated_argument_lookup("keepdims", keepdims, "keep_dims", keep_dims) - - if keepdims is None: - keepdims = False - return _may_reduce_to_scalar(keepdims, axis, reduction_indices, - gen_math_ops.prod( - input_tensor, - _ReductionDims(input_tensor, axis, - reduction_indices), - keepdims, - name=name)) + return reduce_all(input_tensor, axis, keepdims, name) -@tf_export("math.reduce_min", "reduce_min") -@deprecation.deprecated_args( - None, "keep_dims is deprecated, use keepdims instead", "keep_dims") -def reduce_min(input_tensor, - axis=None, - keepdims=None, - name=None, - reduction_indices=None, - keep_dims=None): - """Computes the minimum of elements across dimensions of a tensor. +@tf_export("reduce_all", "math.reduce_all", v1=[]) +def reduce_all(input_tensor, axis=None, keepdims=False, name=None): + """Computes the "logical and" of elements across dimensions of a tensor. Reduces `input_tensor` along the dimensions given in `axis`. Unless `keepdims` is true, the rank of the tensor is reduced by 1 for each @@ -1718,46 +1972,48 @@ def reduce_min(input_tensor, If `axis` is None, all dimensions are reduced, and a tensor with a single element is returned. + For example: + + ```python + x = tf.constant([[True, True], [False, False]]) + tf.reduce_all(x) # False + tf.reduce_all(x, 0) # [False, False] + tf.reduce_all(x, 1) # [True, False] + ``` + Args: - input_tensor: The tensor to reduce. Should have real numeric type. - axis: The dimensions to reduce. If `None` (the default), - reduces all dimensions. Must be in the range - `[-rank(input_tensor), rank(input_tensor))`. + input_tensor: The boolean tensor to reduce. + axis: The dimensions to reduce. If `None` (the default), reduces all + dimensions. Must be in the range `[-rank(input_tensor), + rank(input_tensor))`. keepdims: If true, retains reduced dimensions with length 1. name: A name for the operation (optional). - reduction_indices: The old (deprecated) name for axis. - keep_dims: Deprecated alias for `keepdims`. Returns: The reduced tensor. @compatibility(numpy) - Equivalent to np.min + Equivalent to np.all @end_compatibility """ - keepdims = deprecation.deprecated_argument_lookup("keepdims", keepdims, - "keep_dims", keep_dims) - if keepdims is None: - keepdims = False - return _may_reduce_to_scalar(keepdims, axis, reduction_indices, - gen_math_ops._min( - input_tensor, - _ReductionDims(input_tensor, axis, - reduction_indices), - keepdims, - name=name)) + keepdims = False if keepdims is None else keepdims + return _may_reduce_to_scalar( + keepdims, axis, + gen_math_ops._all( + input_tensor, _ReductionDims(input_tensor, axis), keepdims, + name=name)) -@tf_export("math.reduce_max", "reduce_max") +@tf_export(v1=["math.reduce_any", "reduce_any"]) @deprecation.deprecated_args( None, "keep_dims is deprecated, use keepdims instead", "keep_dims") -def reduce_max(input_tensor, - axis=None, - keepdims=None, - name=None, - reduction_indices=None, - keep_dims=None): - """Computes the maximum of elements across dimensions of a tensor. +def reduce_any_v1(input_tensor, + axis=None, + keepdims=None, + name=None, + reduction_indices=None, + keep_dims=None): + """Computes the "logical or" of elements across dimensions of a tensor. Reduces `input_tensor` along the dimensions given in `axis`. Unless `keepdims` is true, the rank of the tensor is reduced by 1 for each @@ -1767,11 +2023,20 @@ def reduce_max(input_tensor, If `axis` is None, all dimensions are reduced, and a tensor with a single element is returned. + For example: + + ```python + x = tf.constant([[True, True], [False, False]]) + tf.reduce_any(x) # True + tf.reduce_any(x, 0) # [True, True] + tf.reduce_any(x, 1) # [True, False] + ``` + Args: - input_tensor: The tensor to reduce. Should have real numeric type. - axis: The dimensions to reduce. If `None` (the default), - reduces all dimensions. Must be in the range - `[-rank(input_tensor), rank(input_tensor))`. + input_tensor: The boolean tensor to reduce. + axis: The dimensions to reduce. If `None` (the default), reduces all + dimensions. Must be in the range `[-rank(input_tensor), + rank(input_tensor))`. keepdims: If true, retains reduced dimensions with length 1. name: A name for the operation (optional). reduction_indices: The old (deprecated) name for axis. @@ -1781,32 +2046,19 @@ def reduce_max(input_tensor, The reduced tensor. @compatibility(numpy) - Equivalent to np.max + Equivalent to np.any @end_compatibility """ + axis = deprecation.deprecated_argument_lookup( + "axis", axis, "reduction_indices", reduction_indices) keepdims = deprecation.deprecated_argument_lookup("keepdims", keepdims, "keep_dims", keep_dims) - if keepdims is None: - keepdims = False - return _may_reduce_to_scalar(keepdims, axis, reduction_indices, - gen_math_ops._max( - input_tensor, - _ReductionDims(input_tensor, axis, - reduction_indices), - keepdims, - name=name)) + return reduce_any(input_tensor, axis, keepdims, name) -@tf_export("math.reduce_all", "reduce_all") -@deprecation.deprecated_args( - None, "keep_dims is deprecated, use keepdims instead", "keep_dims") -def reduce_all(input_tensor, - axis=None, - keepdims=None, - name=None, - reduction_indices=None, - keep_dims=None): - """Computes the "logical and" of elements across dimensions of a tensor. +@tf_export("math.reduce_any", "reduce_any", v1=[]) +def reduce_any(input_tensor, axis=None, keepdims=False, name=None): + """Computes the "logical or" of elements across dimensions of a tensor. Reduces `input_tensor` along the dimensions given in `axis`. Unless `keepdims` is true, the rank of the tensor is reduced by 1 for each @@ -1820,74 +2072,73 @@ def reduce_all(input_tensor, ```python x = tf.constant([[True, True], [False, False]]) - tf.reduce_all(x) # False - tf.reduce_all(x, 0) # [False, False] - tf.reduce_all(x, 1) # [True, False] + tf.reduce_any(x) # True + tf.reduce_any(x, 0) # [True, True] + tf.reduce_any(x, 1) # [True, False] ``` Args: input_tensor: The boolean tensor to reduce. - axis: The dimensions to reduce. If `None` (the default), - reduces all dimensions. Must be in the range - `[-rank(input_tensor), rank(input_tensor))`. + axis: The dimensions to reduce. If `None` (the default), reduces all + dimensions. Must be in the range `[-rank(input_tensor), + rank(input_tensor))`. keepdims: If true, retains reduced dimensions with length 1. name: A name for the operation (optional). - reduction_indices: The old (deprecated) name for axis. - keep_dims: Deprecated alias for `keepdims`. Returns: The reduced tensor. @compatibility(numpy) - Equivalent to np.all + Equivalent to np.any @end_compatibility """ - keepdims = deprecation.deprecated_argument_lookup("keepdims", keepdims, - "keep_dims", keep_dims) - if keepdims is None: - keepdims = False - return _may_reduce_to_scalar(keepdims, axis, reduction_indices, - gen_math_ops._all( - input_tensor, - _ReductionDims(input_tensor, axis, - reduction_indices), - keepdims, - name=name)) + keepdims = False if keepdims is None else keepdims + return _may_reduce_to_scalar( + keepdims, axis, + gen_math_ops._any( + input_tensor, _ReductionDims(input_tensor, axis), keepdims, + name=name)) -@tf_export("math.reduce_any", "reduce_any") +@tf_export(v1=["math.reduce_logsumexp", "reduce_logsumexp"]) @deprecation.deprecated_args( None, "keep_dims is deprecated, use keepdims instead", "keep_dims") -def reduce_any(input_tensor, - axis=None, - keepdims=None, - name=None, - reduction_indices=None, - keep_dims=None): - """Computes the "logical or" of elements across dimensions of a tensor. +def reduce_logsumexp_v1(input_tensor, + axis=None, + keepdims=None, + name=None, + reduction_indices=None, + keep_dims=None): + """Computes log(sum(exp(elements across dimensions of a tensor))). Reduces `input_tensor` along the dimensions given in `axis`. Unless `keepdims` is true, the rank of the tensor is reduced by 1 for each entry in `axis`. If `keepdims` is true, the reduced dimensions are retained with length 1. - If `axis` is None, all dimensions are reduced, and a + If `axis` has no entries, all dimensions are reduced, and a tensor with a single element is returned. + This function is more numerically stable than log(sum(exp(input))). It avoids + overflows caused by taking the exp of large inputs and underflows caused by + taking the log of small inputs. + For example: ```python - x = tf.constant([[True, True], [False, False]]) - tf.reduce_any(x) # True - tf.reduce_any(x, 0) # [True, True] - tf.reduce_any(x, 1) # [True, False] + x = tf.constant([[0., 0., 0.], [0., 0., 0.]]) + tf.reduce_logsumexp(x) # log(6) + tf.reduce_logsumexp(x, 0) # [log(2), log(2), log(2)] + tf.reduce_logsumexp(x, 1) # [log(3), log(3)] + tf.reduce_logsumexp(x, 1, keepdims=True) # [[log(3)], [log(3)]] + tf.reduce_logsumexp(x, [0, 1]) # log(6) ``` Args: - input_tensor: The boolean tensor to reduce. - axis: The dimensions to reduce. If `None` (the default), - reduces all dimensions. Must be in the range - `[-rank(input_tensor), rank(input_tensor))`. + input_tensor: The tensor to reduce. Should have numeric type. + axis: The dimensions to reduce. If `None` (the default), reduces all + dimensions. Must be in the range `[-rank(input_tensor), + rank(input_tensor))`. keepdims: If true, retains reduced dimensions with length 1. name: A name for the operation (optional). reduction_indices: The old (deprecated) name for axis. @@ -1895,33 +2146,16 @@ def reduce_any(input_tensor, Returns: The reduced tensor. - - @compatibility(numpy) - Equivalent to np.any - @end_compatibility """ + axis = deprecation.deprecated_argument_lookup( + "axis", axis, "reduction_indices", reduction_indices) keepdims = deprecation.deprecated_argument_lookup("keepdims", keepdims, "keep_dims", keep_dims) - if keepdims is None: - keepdims = False - return _may_reduce_to_scalar(keepdims, axis, reduction_indices, - gen_math_ops._any( - input_tensor, - _ReductionDims(input_tensor, axis, - reduction_indices), - keepdims, - name=name)) + return reduce_logsumexp(input_tensor, axis, keepdims, name) -@tf_export("math.reduce_logsumexp", "reduce_logsumexp") -@deprecation.deprecated_args( - None, "keep_dims is deprecated, use keepdims instead", "keep_dims") -def reduce_logsumexp(input_tensor, - axis=None, - keepdims=None, - name=None, - reduction_indices=None, - keep_dims=None): +@tf_export("math.reduce_logsumexp", "reduce_logsumexp", v1=[]) +def reduce_logsumexp(input_tensor, axis=None, keepdims=False, name=None): """Computes log(sum(exp(elements across dimensions of a tensor))). Reduces `input_tensor` along the dimensions given in `axis`. @@ -1949,27 +2183,21 @@ def reduce_logsumexp(input_tensor, Args: input_tensor: The tensor to reduce. Should have numeric type. - axis: The dimensions to reduce. If `None` (the default), - reduces all dimensions. Must be in the range - `[-rank(input_tensor), rank(input_tensor))`. + axis: The dimensions to reduce. If `None` (the default), reduces all + dimensions. Must be in the range `[-rank(input_tensor), + rank(input_tensor))`. keepdims: If true, retains reduced dimensions with length 1. name: A name for the operation (optional). - reduction_indices: The old (deprecated) name for axis. - keep_dims: Deprecated alias for `keepdims`. Returns: The reduced tensor. """ - keepdims = deprecation.deprecated_argument_lookup("keepdims", keepdims, - "keep_dims", keep_dims) - if keepdims is None: - keepdims = False + keepdims = False if keepdims is None else keepdims input_tensor = ops.convert_to_tensor(input_tensor) with ops.name_scope(name, "ReduceLogSumExp", [input_tensor]) as name: raw_max = reduce_max( input_tensor, axis=axis, - reduction_indices=reduction_indices, keepdims=True) my_max = array_ops.stop_gradient( array_ops.where( @@ -1979,12 +2207,11 @@ def reduce_logsumexp(input_tensor, reduce_sum( gen_math_ops.exp(gen_math_ops.sub(input_tensor, my_max)), axis, - keepdims=keepdims, - reduction_indices=reduction_indices)) + keepdims=keepdims)) if not keepdims: my_max = array_ops.reshape(my_max, array_ops.shape(result)) result = gen_math_ops.add(result, my_max) - return _may_reduce_to_scalar(keepdims, axis, reduction_indices, result) + return _may_reduce_to_scalar(keepdims, axis, result) @tf_export("linalg.trace", v1=["linalg.trace", "trace"]) @@ -2311,7 +2538,8 @@ def matvec(a, _OverrideBinaryOperatorHelper(matmul, "matmul") -sparse_matmul = gen_math_ops.sparse_mat_mul +sparse_matmul = deprecation.deprecated(None, "Use `tf.linalg.matmul` instead")( + gen_math_ops.sparse_mat_mul) tf_export(v1=["sparse_matmul"])(sparse_matmul) @@ -2555,34 +2783,64 @@ def log_sigmoid(x, name=None): return gen_math_ops.neg(gen_nn_ops.softplus(-x), name=name) -@tf_export("math.tanh", "nn.tanh", "tanh") -def tanh(x, name=None): - """Computes hyperbolic tangent of `x` element-wise. +@tf_export("math.bincount", v1=[]) +def bincount(arr, + weights=None, + minlength=None, + maxlength=None, + dtype=dtypes.int32, + name=None): + """Counts the number of occurrences of each value in an integer array. + + If `minlength` and `maxlength` are not given, returns a vector with length + `tf.reduce_max(arr) + 1` if `arr` is non-empty, and length 0 otherwise. + If `weights` are non-None, then index `i` of the output stores the sum of the + value in `weights` at each index where the corresponding value in `arr` is + `i`. Args: - x: A Tensor or SparseTensor with type `float16`, `float32`, `double`, - `complex64`, or `complex128`. - name: A name for the operation (optional). + arr: An int32 tensor of non-negative values. + weights: If non-None, must be the same shape as arr. For each value in + `arr`, the bin will be incremented by the corresponding weight instead of + 1. + minlength: If given, ensures the output has length at least `minlength`, + padding with zeros at the end if necessary. + maxlength: If given, skips values in `arr` that are equal or greater than + `maxlength`, ensuring that the output has length at most `maxlength`. + dtype: If `weights` is None, determines the type of the output bins. + name: A name scope for the associated operations (optional). Returns: - A Tensor or SparseTensor respectively with the same type as `x`. + A vector with the same dtype as `weights` or the given `dtype`. The bin + values. """ - with ops.name_scope(name, "Tanh", [x]) as name: - if isinstance(x, sparse_tensor.SparseTensor): - x_tanh = gen_math_ops.tanh(x.values, name=name) - return sparse_tensor.SparseTensor( - indices=x.indices, values=x_tanh, dense_shape=x.dense_shape) - else: - return gen_math_ops.tanh(x, name=name) - - -@tf_export("math.bincount", v1=["math.bincount", "bincount"]) + name = "bincount" if name is None else name + with ops.name_scope(name): + arr = ops.convert_to_tensor(arr, name="arr", dtype=dtypes.int32) + array_is_nonempty = reduce_prod(array_ops.shape(arr)) > 0 + output_size = cast(array_is_nonempty, dtypes.int32) * (reduce_max(arr) + 1) + if minlength is not None: + minlength = ops.convert_to_tensor( + minlength, name="minlength", dtype=dtypes.int32) + output_size = gen_math_ops.maximum(minlength, output_size) + if maxlength is not None: + maxlength = ops.convert_to_tensor( + maxlength, name="maxlength", dtype=dtypes.int32) + output_size = gen_math_ops.minimum(maxlength, output_size) + if weights is not None: + weights = ops.convert_to_tensor(weights, name="weights") + return gen_math_ops.unsorted_segment_sum(weights, arr, output_size) + weights = constant_op.constant([], dtype) + return gen_math_ops.bincount(arr, output_size, weights) + + +@tf_export(v1=["math.bincount", "bincount"]) @deprecation.deprecated_endpoints("bincount") -def bincount(arr, - weights=None, - minlength=None, - maxlength=None, - dtype=dtypes.int32): +def bincount_v1(arr, + weights=None, + minlength=None, + maxlength=None, + dtype=dtypes.int32): """Counts the number of occurrences of each value in an integer array. If `minlength` and `maxlength` are not given, returns a vector with length @@ -2594,34 +2852,19 @@ def bincount(arr, Args: arr: An int32 tensor of non-negative values. weights: If non-None, must be the same shape as arr. For each value in - `arr`, the bin will be incremented by the corresponding weight instead - of 1. + `arr`, the bin will be incremented by the corresponding weight instead of + 1. minlength: If given, ensures the output has length at least `minlength`, - padding with zeros at the end if necessary. + padding with zeros at the end if necessary. maxlength: If given, skips values in `arr` that are equal or greater than - `maxlength`, ensuring that the output has length at most `maxlength`. + `maxlength`, ensuring that the output has length at most `maxlength`. dtype: If `weights` is None, determines the type of the output bins. Returns: A vector with the same dtype as `weights` or the given `dtype`. The bin values. """ - arr = ops.convert_to_tensor(arr, name="arr", dtype=dtypes.int32) - array_is_nonempty = reduce_prod(array_ops.shape(arr)) > 0 - output_size = cast(array_is_nonempty, dtypes.int32) * (reduce_max(arr) + 1) - if minlength is not None: - minlength = ops.convert_to_tensor( - minlength, name="minlength", dtype=dtypes.int32) - output_size = gen_math_ops.maximum(minlength, output_size) - if maxlength is not None: - maxlength = ops.convert_to_tensor( - maxlength, name="maxlength", dtype=dtypes.int32) - output_size = gen_math_ops.minimum(maxlength, output_size) - if weights is not None: - weights = ops.convert_to_tensor(weights, name="weights") - return gen_math_ops.unsorted_segment_sum(weights, arr, output_size) - weights = constant_op.constant([], dtype) - return gen_math_ops.bincount(arr, output_size, weights) + return bincount(arr, weights, minlength, maxlength, dtype) @tf_export("math.cumsum", "cumsum") @@ -2923,8 +3166,7 @@ def unsorted_segment_sqrt_n(data, segment_ids, num_segments, name=None): return summed / gen_math_ops.sqrt(N) -@tf_export( - "sparse.segment_sum", v1=["sparse.segment_sum", "sparse_segment_sum"]) +@tf_export(v1=["sparse.segment_sum", "sparse_segment_sum"]) @deprecation.deprecated_endpoints("sparse_segment_sum") def sparse_segment_sum(data, indices, segment_ids, name=None, num_segments=None): @@ -2998,8 +3240,17 @@ def sparse_segment_sum(data, indices, segment_ids, name=None, data=data, indices=indices, segment_ids=segment_ids, name=name) -@tf_export( - "sparse.segment_mean", v1=["sparse.segment_mean", "sparse_segment_mean"]) +@tf_export("sparse.segment_sum", v1=[]) +def sparse_segment_sum_v2(data, + indices, + segment_ids, + num_segments=None, + name=None): + return sparse_segment_mean( + data, indices, segment_ids, name=name, num_segments=num_segments) + + +@tf_export(v1=["sparse.segment_mean", "sparse_segment_mean"]) @deprecation.deprecated_endpoints("sparse_segment_mean") def sparse_segment_mean(data, indices, @@ -3045,9 +3296,44 @@ def sparse_segment_mean(data, data=data, indices=indices, segment_ids=segment_ids, name=name) -@tf_export( - "sparse.segment_sqrt_n", - v1=["sparse.segment_sqrt_n", "sparse_segment_sqrt_n"]) +@tf_export("sparse.segment_mean", v1=[]) +def sparse_segment_mean_v2(data, + indices, + segment_ids, + num_segments=None, + name=None): + r"""Computes the mean along sparse segments of a tensor. + + Read [the section on + segmentation](https://tensorflow.org/api_guides/python/math_ops#Segmentation) + for an explanation of segments. + + Like `SegmentMean`, but `segment_ids` can have rank less than `data`'s first + dimension, selecting a subset of dimension 0, specified by `indices`. + `segment_ids` is allowed to have missing ids, in which case the output will + be zeros at those indices. In those cases `num_segments` is used to determine + the size of the output. + + Args: + data: A `Tensor` with data that will be assembled in the output. + indices: A 1-D `Tensor` with indices into `data`. Has same rank as + `segment_ids`. + segment_ids: A 1-D `Tensor` with indices into the output `Tensor`. Values + should be sorted and can be repeated. + num_segments: An optional int32 scalar. Indicates the size of the output + `Tensor`. + name: A name for the operation (optional). + + Returns: + A `tensor` of the shape as data, except for dimension 0 which + has size `k`, the number of segments specified via `num_segments` or + inferred for the last element in `segments_ids`. + """ + return sparse_segment_mean( + data, indices, segment_ids, name=name, num_segments=num_segments) + + +@tf_export(v1=["sparse.segment_sqrt_n", "sparse_segment_sqrt_n"]) @deprecation.deprecated_endpoints("sparse_segment_sqrt_n") def sparse_segment_sqrt_n(data, indices, @@ -3085,6 +3371,35 @@ def sparse_segment_sqrt_n(data, data=data, indices=indices, segment_ids=segment_ids, name=name) +@tf_export("sparse.segment_sqrt_n", v1=[]) +def sparse_segment_sqrt_n_v2(data, + indices, + segment_ids, + num_segments=None, + name=None): + r"""Computes the sum along sparse segments of a tensor divided by the sqrt(N). + + `N` is the size of the segment being reduced. + + Args: + data: A `Tensor` with data that will be assembled in the output. + indices: A 1-D `Tensor` with indices into `data`. Has same rank as + `segment_ids`. + segment_ids: A 1-D `Tensor` with indices into the output `Tensor`. Values + should be sorted and can be repeated. + num_segments: An optional int32 scalar. Indicates the size of the output + `Tensor`. + name: A name for the operation (optional). + + Returns: + A `tensor` of the shape as data, except for dimension 0 which + has size `k`, the number of segments specified via `num_segments` or + inferred for the last element in `segments_ids`. + """ + return sparse_segment_sqrt_n( + data, indices, segment_ids, name=name, num_segments=num_segments) + + @tf_export("tensordot", "linalg.tensordot") def tensordot(a, b, axes, name=None): r"""Tensor contraction of a and b along specified axes. @@ -3118,12 +3433,11 @@ def tensordot(a, b, axes, name=None): a: `Tensor` of type `float32` or `float64`. b: `Tensor` with the same type as `a`. axes: Either a scalar `N`, or a list or an `int32` `Tensor` of shape [2, k]. - If axes is a scalar, sum over the last N axes of a and the first N axes - of b in order. - If axes is a list or `Tensor` the first and second row contain the set of - unique integers specifying axes along which the contraction is computed, - for `a` and `b`, respectively. The number of axes for `a` and `b` must - be equal. + If axes is a scalar, sum over the last N axes of a and the first N axes of + b in order. If axes is a list or `Tensor` the first and second row contain + the set of unique integers specifying axes along which the contraction is + computed, for `a` and `b`, respectively. The number of axes for `a` and + `b` must be equal. name: A name for the operation (optional). Returns: @@ -3295,73 +3609,3 @@ def polyval(coeffs, x, name=None): for c in coeffs[1:]: p = c + p * x return p - - -@tf_export("math.bessel_i0e") -def bessel_i0e(x, name=None): - """Computes the Bessel i0e function of `x` element-wise. - - Exponentially scaled modified Bessel function of order 0 defined as - `bessel_i0e(x) = exp(-abs(x)) bessel_i0(x)`. - - This function is faster and numerically stabler than `bessel_i0(x)`. - - Args: - x: A `Tensor` or `SparseTensor`. Must be one of the following types: `half`, - `float32`, `float64`. - name: A name for the operation (optional). - - Returns: - A `Tensor` or `SparseTensor`, respectively. Has the same type as `x`. - - @compatibility(scipy) - Equivalent to scipy.special.i0e - @end_compatibility - """ - with ops.name_scope(name, "bessel_i0e", [x]) as name: - if isinstance(x, sparse_tensor.SparseTensor): - x_i0e = gen_math_ops.bessel_i0e(x.values, name=name) - return sparse_tensor.SparseTensor( - indices=x.indices, values=x_i0e, dense_shape=x.dense_shape) - else: - return gen_math_ops.bessel_i0e(x, name=name) - - -@tf_export("math.bessel_i1e") -def bessel_i1e(x, name=None): - """Computes the Bessel i1e function of `x` element-wise. - - Exponentially scaled modified Bessel function of order 1 defined as - `bessel_i1e(x) = exp(-abs(x)) bessel_i1(x)`. - - This function is faster and numerically stabler than `bessel_i1(x)`. - - Args: - x: A `Tensor` or `SparseTensor`. Must be one of the following types: `half`, - `float32`, `float64`. - name: A name for the operation (optional). - - Returns: - A `Tensor` or `SparseTensor`, respectively. Has the same type as `x`. - - @compatibility(scipy) - Equivalent to scipy.special.i1e - @end_compatibility - """ - with ops.name_scope(name, "bessel_i1e", [x]) as name: - if isinstance(x, sparse_tensor.SparseTensor): - x_i1e = gen_math_ops.bessel_i1e(x.values, name=name) - return sparse_tensor.SparseTensor( - indices=x.indices, values=x_i1e, dense_shape=x.dense_shape) - else: - return gen_math_ops.bessel_i1e(x, name=name) - - -# FFT ops were moved to tf.spectral. tf.fft symbols were part of the TensorFlow -# 1.0 API so we leave these here for backwards compatibility. -fft = gen_spectral_ops.fft -ifft = gen_spectral_ops.ifft -fft2d = gen_spectral_ops.fft2d -ifft2d = gen_spectral_ops.ifft2d -fft3d = gen_spectral_ops.fft3d -ifft3d = gen_spectral_ops.ifft3d diff --git a/tensorflow/python/ops/math_ops_test.py b/tensorflow/python/ops/math_ops_test.py index a4da0c6c33..add1621a56 100644 --- a/tensorflow/python/ops/math_ops_test.py +++ b/tensorflow/python/ops/math_ops_test.py @@ -92,6 +92,7 @@ class ReduceTest(test_util.TensorFlowTestCase): class LogSumExpTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testReduceLogSumExp(self): for dtype in [np.float16, np.float32, np.double]: x_np = np.random.rand(5, 5).astype(dtype) @@ -104,22 +105,23 @@ class LogSumExpTest(test_util.TensorFlowTestCase): for dtype in [np.float16, np.float32, np.double]: x_np = np.random.rand(5, 5).astype(dtype) with self.cached_session(use_gpu=True): - y_tf = math_ops.reduce_logsumexp(x_np, reduction_indices=[0]) + y_tf = math_ops.reduce_logsumexp(x_np, axis=[0]) y_np = log(np.sum(exp(x_np), axis=0)) self.assertShapeEqual(y_np, y_tf) - y_tf_np = y_tf.eval() + y_tf_np = self.evaluate(y_tf) self.assertAllClose(y_tf_np, y_np) def testReductionIndices2(self): for dtype in [np.float16, np.float32, np.double]: x_np = np.random.rand(5, 5).astype(dtype) with self.cached_session(use_gpu=True): - y_tf = math_ops.reduce_logsumexp(x_np, reduction_indices=0) + y_tf = math_ops.reduce_logsumexp(x_np, axis=0) y_np = log(np.sum(exp(x_np), axis=0)) self.assertShapeEqual(y_np, y_tf) - y_tf_np = y_tf.eval() + y_tf_np = self.evaluate(y_tf) self.assertAllClose(y_tf_np, y_np) + @test_util.run_deprecated_v1 def testKeepDims(self): for dtype in [np.float16, np.float32, np.double]: x_np = np.random.rand(5, 5).astype(dtype) @@ -129,6 +131,7 @@ class LogSumExpTest(test_util.TensorFlowTestCase): y_np = log(np.sum(exp(x_np), keepdims=True)) self.assertAllClose(y_tf_np, y_np) + @test_util.run_deprecated_v1 def testOverflow(self): x = [1000, 1001, 1002, 1003] for dtype in [np.float16, np.float32, np.double]: @@ -146,6 +149,7 @@ class LogSumExpTest(test_util.TensorFlowTestCase): y_np = log(np.sum(exp(x_np - max_np))) + max_np self.assertAllClose(y_tf_np, y_np) + @test_util.run_deprecated_v1 def testUnderflow(self): x = [-1000, -1001, -1002, -1003] for dtype in [np.float16, np.float32, np.double]: @@ -163,6 +167,7 @@ class LogSumExpTest(test_util.TensorFlowTestCase): y_np = log(np.sum(exp(x_np - max_np))) + max_np self.assertAllClose(y_tf_np, y_np) + @test_util.run_deprecated_v1 def testInfinity(self): with self.session(use_gpu=True): res = math_ops.reduce_logsumexp(-np.inf).eval() @@ -186,6 +191,7 @@ class RoundTest(test_util.TensorFlowTestCase): class ModTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testFloat(self): x = [0.5, 0.7, 0.3] for dtype in [np.float32, np.double]: @@ -195,7 +201,7 @@ class ModTest(test_util.TensorFlowTestCase): with self.cached_session(use_gpu=True): x_tf = constant_op.constant(x_np, shape=x_np.shape) y_tf = math_ops.mod(x_tf, denom) - y_tf_np = y_tf.eval() + y_tf_np = self.evaluate(y_tf) y_np = np.fmod(x_np, denom) self.assertAllClose(y_tf_np, y_np, atol=1e-2) @@ -208,7 +214,7 @@ class ModTest(test_util.TensorFlowTestCase): with self.cached_session(use_gpu=True): x_tf = constant_op.constant(x_np, shape=x_np.shape) y_tf = math_ops.mod(x_tf, denom) - y_tf_np = y_tf.eval() + y_tf_np = self.evaluate(y_tf) y_np = np.mod(x_np, denom) self.assertAllClose(y_tf_np, y_np) @@ -256,6 +262,7 @@ class ApproximateEqualTest(test_util.TensorFlowTestCase): z_tf = self.evaluate(math_ops.approximate_equal(x, y, tolerance=0.0001)) self.assertAllEqual(z, z_tf) + @test_util.run_deprecated_v1 def testApproximateEqualShape(self): for dtype in [np.float32, np.double]: x = np.array([1, 2], dtype=dtype) @@ -309,6 +316,7 @@ class ScalarMulTest(test_util.TensorFlowTestCase): class AccumulateNTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testFloat(self): np.random.seed(12345) x = [np.random.random((1, 2, 3, 4, 5)) - 0.5 for _ in range(5)] @@ -317,6 +325,7 @@ class AccumulateNTest(test_util.TensorFlowTestCase): self.assertAllClose(sum(x), math_ops.accumulate_n(tf_x).eval()) self.assertAllClose(x[0] * 5, math_ops.accumulate_n([tf_x[0]] * 5).eval()) + @test_util.run_deprecated_v1 def testInt(self): np.random.seed(54321) x = [np.random.randint(-128, 128, (5, 4, 3, 2, 1)) for _ in range(6)] @@ -328,6 +337,7 @@ class AccumulateNTest(test_util.TensorFlowTestCase): class AddNTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testPartials(self): """Test that previously revealed a bug in buffer forwarding for AddN.""" partials = [] @@ -341,6 +351,7 @@ class AddNTest(test_util.TensorFlowTestCase): with self.session(use_gpu=True): self.assertAllEqual(res.eval(), 100) + @test_util.run_deprecated_v1 def testFloat(self): np.random.seed(12345) for num_inputs in range(1, 10): @@ -351,6 +362,7 @@ class AddNTest(test_util.TensorFlowTestCase): self.assertAllClose(x[0] * num_inputs, math_ops.add_n([tf_x[0]] * num_inputs).eval()) + @test_util.run_deprecated_v1 def testInt(self): np.random.seed(54321) for num_inputs in range(1, 10): @@ -364,6 +376,7 @@ class AddNTest(test_util.TensorFlowTestCase): self.assertAllEqual(x[0] * num_inputs, math_ops.add_n([tf_x[0]] * num_inputs).eval()) + @test_util.run_deprecated_v1 def testGrad(self): np.random.seed(42) for num_inputs in range(1, 10): @@ -373,7 +386,7 @@ class AddNTest(test_util.TensorFlowTestCase): for i in range(0, num_inputs) ] addn = math_ops.add_n(input_vars) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) add_n_grad = gradients.gradients(addn, input_vars) self.assertAllEqual(np.repeat(1.0, num_inputs), # d/dx (x + y + ...) = 1 [g.eval() for g in add_n_grad]) @@ -392,6 +405,7 @@ class DivAndModTest(test_util.TensorFlowTestCase): divs = np.arange(-3, 0, .25).reshape(1, 12) return nums, divs + @test_util.run_deprecated_v1 def testFloorModInt(self): nums, divs = self.intTestData() with self.cached_session(): @@ -401,6 +415,7 @@ class DivAndModTest(test_util.TensorFlowTestCase): np_result = nums % divs self.assertAllEqual(tf_result, np_result) + @test_util.run_deprecated_v1 def testFloorModFloat(self): nums, divs = self.floatTestData() with self.cached_session(): @@ -412,6 +427,7 @@ class DivAndModTest(test_util.TensorFlowTestCase): # % array_ops.constant(divs)).eval() # self.assertAllEqual(tf2_result, tf_result) + @test_util.run_deprecated_v1 def testTruncateModInt(self): nums, divs = self.intTestData() with self.cached_session(): @@ -419,6 +435,7 @@ class DivAndModTest(test_util.TensorFlowTestCase): np_result = np.fmod(nums, divs) self.assertAllEqual(tf_result, np_result) + @test_util.run_deprecated_v1 def testTruncateModFloat(self): nums, divs = self.floatTestData() with self.cached_session(): @@ -426,6 +443,7 @@ class DivAndModTest(test_util.TensorFlowTestCase): np_result = np.fmod(nums, divs) self.assertAllEqual(tf_result, np_result) + @test_util.run_deprecated_v1 def testDivideInt(self): nums, divs = self.intTestData() with self.cached_session(): @@ -437,12 +455,14 @@ class DivAndModTest(test_util.TensorFlowTestCase): # // array_ops.constant(divs)).eval() # self.assertAllEqual(tf2_result, tf_result) + @test_util.run_deprecated_v1 def testDivideName(self): with self.cached_session(): op = math_ops.divide( array_ops.constant(3), array_ops.constant(4), name="my_cool_divide") self.assertEqual(op.name, "my_cool_divide:0") + @test_util.run_deprecated_v1 def testRealDiv(self): nums, divs = self.floatTestData() with self.cached_session(): @@ -450,26 +470,30 @@ class DivAndModTest(test_util.TensorFlowTestCase): np_result = np.divide(nums, divs) self.assertAllEqual(tf_result, np_result) + @test_util.run_deprecated_v1 def testComplexDiv(self): foo = array_ops.constant([1. + 3.j]) with self.cached_session(): _ = math_ops.divide(foo, 1.).eval() _ = math_ops.div(foo, 2.).eval() + @test_util.run_deprecated_v1 def testFloorDivGrad(self): with self.cached_session(): a = variables.Variable(2.) b = variables.Variable(4.) with self.cached_session() as sess: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) c_grad = gradients.gradients(math_ops.divide(a, b), [a, b]) self.assertAllEqual([x.eval() for x in c_grad], [.25, -.125]) c_grad = gradients.gradients(math_ops.div(a, b), [a, b]) self.assertAllEqual([x.eval() for x in c_grad], [.25, -.125]) c_grad = gradients.gradients(math_ops.floordiv(a, b), [a, b]) - self.assertAllEqual([None if x is None else x.eval() - for x in c_grad], [None, None]) + self.assertAllEqual( + [None if x is None else self.evaluate(x) for x in c_grad], + [None, None]) + @test_util.run_deprecated_v1 def testConsistent(self): nums, divs = self.intTestData() with self.cached_session(): @@ -496,6 +520,7 @@ class DivAndModTest(test_util.TensorFlowTestCase): class DivNoNanTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testBasic(self): for dtype in [np.float32, np.float64]: nums = np.arange(-10, 10, .25, dtype=dtype).reshape(80, 1) diff --git a/tensorflow/python/ops/metrics_impl.py b/tensorflow/python/ops/metrics_impl.py index e86a3b8536..cb42199011 100644 --- a/tensorflow/python/ops/metrics_impl.py +++ b/tensorflow/python/ops/metrics_impl.py @@ -18,7 +18,6 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -from tensorflow.python.compat import compat from tensorflow.python.eager import context from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops @@ -213,26 +212,6 @@ def _maybe_expand_labels(labels, predictions): lambda: array_ops.expand_dims(labels, -1, name=scope), lambda: labels) -def _safe_div(numerator, denominator, name): - """Divides two tensors element-wise, returning 0 if the denominator is <= 0. - - Args: - numerator: A real `Tensor`. - denominator: A real `Tensor`, with dtype matching `numerator`. - name: Name for the returned op. - - Returns: - 0 if `denominator` <= 0, else `numerator` / `denominator` - """ - if compat.forward_compatible(2018, 11, 1): - return math_ops.div_no_nan(numerator, denominator, name=name) - t = math_ops.truediv(numerator, denominator) - zero = array_ops.zeros_like(t, dtype=denominator.dtype) - condition = math_ops.greater(denominator, zero) - zero = math_ops.cast(zero, t.dtype) - return array_ops.where(condition, t, zero, name=name) - - def _safe_scalar_div(numerator, denominator, name): """Divides two values, returning 0 if the denominator is 0. @@ -246,7 +225,7 @@ def _safe_scalar_div(numerator, denominator, name): """ numerator.get_shape().with_rank_at_most(1) denominator.get_shape().with_rank_at_most(1) - return _safe_div(numerator, denominator, name=name) + return math_ops.div_no_nan(numerator, denominator, name=name) def _streaming_confusion_matrix(labels, predictions, num_classes, weights=None): @@ -302,7 +281,7 @@ def _aggregate_across_replicas(metrics_collections, metric_value_fn, *args): """Aggregate metric value across replicas.""" def fn(distribution, *a): """Call `metric_value_fn` in the correct control flow context.""" - if hasattr(distribution, '_outer_control_flow_context'): + if hasattr(distribution.extended, '_outer_control_flow_context'): # If there was an outer context captured before this method was called, # then we enter that context to create the metric value op. If the # caputred context is `None`, ops.control_dependencies(None) gives the @@ -315,13 +294,13 @@ def _aggregate_across_replicas(metrics_collections, metric_value_fn, *args): # once the update ops have been evaluted. # pylint: disable=protected-access - if distribution._outer_control_flow_context is None: + if distribution.extended._outer_control_flow_context is None: with ops.control_dependencies(None): metric_value = metric_value_fn(distribution, *a) else: - distribution._outer_control_flow_context.Enter() + distribution.extended._outer_control_flow_context.Enter() metric_value = metric_value_fn(distribution, *a) - distribution._outer_control_flow_context.Exit() + distribution.extended._outer_control_flow_context.Exit() # pylint: enable=protected-access else: metric_value = metric_value_fn(distribution, *a) @@ -330,10 +309,10 @@ def _aggregate_across_replicas(metrics_collections, metric_value_fn, *args): return metric_value return distribution_strategy_context.get_replica_context().merge_call( - fn, *args) + fn, args=args) -@tf_export('metrics.mean') +@tf_export(v1=['metrics.mean']) def mean(values, weights=None, metrics_collections=None, @@ -401,13 +380,12 @@ def mean(values, update_count_op = state_ops.assign_add(count, num_values) def compute_mean(_, t, c): - return _safe_div(t, math_ops.maximum(c, 0), name='value') + return math_ops.div_no_nan(t, math_ops.maximum(c, 0), name='value') mean_t = _aggregate_across_replicas( metrics_collections, compute_mean, total, count) - update_op = _safe_div(update_total_op, - math_ops.maximum(update_count_op, 0), - name='update_op') + update_op = math_ops.div_no_nan( + update_total_op, math_ops.maximum(update_count_op, 0), name='update_op') if updates_collections: ops.add_to_collections(updates_collections, update_op) @@ -415,7 +393,7 @@ def mean(values, return mean_t, update_op -@tf_export('metrics.accuracy') +@tf_export(v1=['metrics.accuracy']) def accuracy(labels, predictions, weights=None, @@ -647,7 +625,7 @@ def _aggregate_variable(v, collections): return _aggregate_across_replicas(collections, f, v) -@tf_export('metrics.auc') +@tf_export(v1=['metrics.auc']) def auc(labels, predictions, weights=None, @@ -779,19 +757,19 @@ def auc(labels, """ dtp = tp[:num_thresholds - 1] - tp[1:] p = tp + fp - prec_slope = _safe_div( + prec_slope = math_ops.div_no_nan( dtp, math_ops.maximum(p[:num_thresholds - 1] - p[1:], 0), name='prec_slope') intercept = tp[1:] - math_ops.multiply(prec_slope, p[1:]) safe_p_ratio = array_ops.where( math_ops.logical_and(p[:num_thresholds - 1] > 0, p[1:] > 0), - _safe_div(p[:num_thresholds - 1], - math_ops.maximum(p[1:], 0), - name='recall_relative_ratio'), - array_ops.ones_like(p[1:])) + math_ops.div_no_nan( + p[:num_thresholds - 1], + math_ops.maximum(p[1:], 0), + name='recall_relative_ratio'), array_ops.ones_like(p[1:])) return math_ops.reduce_sum( - _safe_div( + math_ops.div_no_nan( prec_slope * (dtp + intercept * math_ops.log(safe_p_ratio)), math_ops.maximum(tp[1:] + fn[1:], 0), name='pr_auc_increment'), @@ -852,7 +830,7 @@ def auc(labels, return auc_value, update_op -@tf_export('metrics.mean_absolute_error') +@tf_export(v1=['metrics.mean_absolute_error']) def mean_absolute_error(labels, predictions, weights=None, @@ -913,7 +891,7 @@ def mean_absolute_error(labels, updates_collections, name or 'mean_absolute_error') -@tf_export('metrics.mean_cosine_distance') +@tf_export(v1=['metrics.mean_cosine_distance']) def mean_cosine_distance(labels, predictions, dim, @@ -970,7 +948,7 @@ def mean_cosine_distance(labels, predictions=predictions, labels=labels, weights=weights) radial_diffs = math_ops.multiply(predictions, labels) radial_diffs = math_ops.reduce_sum( - radial_diffs, reduction_indices=[ + radial_diffs, axis=[ dim, ], keepdims=True) mean_distance, update_op = mean(radial_diffs, weights, None, None, name or @@ -987,7 +965,7 @@ def mean_cosine_distance(labels, return mean_distance, update_op -@tf_export('metrics.mean_per_class_accuracy') +@tf_export(v1=['metrics.mean_per_class_accuracy']) def mean_per_class_accuracy(labels, predictions, num_classes, @@ -1074,7 +1052,7 @@ def mean_per_class_accuracy(labels, update_count_op = state_ops.scatter_add(count, labels, is_correct) def compute_mean_accuracy(_, count, total): - per_class_accuracy = _safe_div( + per_class_accuracy = math_ops.div_no_nan( count, math_ops.maximum(total, 0), name=None) mean_accuracy_v = math_ops.reduce_mean( per_class_accuracy, name='mean_accuracy') @@ -1083,16 +1061,15 @@ def mean_per_class_accuracy(labels, mean_accuracy_v = _aggregate_across_replicas( metrics_collections, compute_mean_accuracy, count, total) - update_op = _safe_div(update_count_op, - math_ops.maximum(update_total_op, 0), - name='update_op') + update_op = math_ops.div_no_nan( + update_count_op, math_ops.maximum(update_total_op, 0), name='update_op') if updates_collections: ops.add_to_collections(updates_collections, update_op) return mean_accuracy_v, update_op -@tf_export('metrics.mean_iou') +@tf_export(v1=['metrics.mean_iou']) def mean_iou(labels, predictions, num_classes, @@ -1193,7 +1170,7 @@ def mean_iou(labels, return mean_iou_v, update_op -@tf_export('metrics.mean_relative_error') +@tf_export(v1=['metrics.mean_relative_error']) def mean_relative_error(labels, predictions, normalizer, @@ -1262,7 +1239,7 @@ def mean_relative_error(labels, updates_collections, name or 'mean_relative_error') -@tf_export('metrics.mean_squared_error') +@tf_export(v1=['metrics.mean_squared_error']) def mean_squared_error(labels, predictions, weights=None, @@ -1323,7 +1300,7 @@ def mean_squared_error(labels, name or 'mean_squared_error') -@tf_export('metrics.mean_tensor') +@tf_export(v1=['metrics.mean_tensor']) def mean_tensor(values, weights=None, metrics_collections=None, @@ -1394,22 +1371,21 @@ def mean_tensor(values, with ops.control_dependencies([values]): update_count_op = state_ops.assign_add(count, num_values) - compute_mean = lambda _, t, c: _safe_div( + compute_mean = lambda _, t, c: math_ops.div_no_nan( # pylint: disable=g-long-lambda t, math_ops.maximum(c, 0), name='value') mean_t = _aggregate_across_replicas( metrics_collections, compute_mean, total, count) - update_op = _safe_div(update_total_op, - math_ops.maximum(update_count_op, 0), - name='update_op') + update_op = math_ops.div_no_nan( + update_total_op, math_ops.maximum(update_count_op, 0), name='update_op') if updates_collections: ops.add_to_collections(updates_collections, update_op) return mean_t, update_op -@tf_export('metrics.percentage_below') +@tf_export(v1=['metrics.percentage_below']) def percentage_below(values, threshold, weights=None, @@ -1509,7 +1485,7 @@ def _count_condition(values, return value_tensor, update_op -@tf_export('metrics.false_negatives') +@tf_export(v1=['metrics.false_negatives']) def false_negatives(labels, predictions, weights=None, @@ -1561,7 +1537,7 @@ def false_negatives(labels, updates_collections) -@tf_export('metrics.false_negatives_at_thresholds') +@tf_export(v1=['metrics.false_negatives_at_thresholds']) def false_negatives_at_thresholds(labels, predictions, thresholds, @@ -1617,7 +1593,7 @@ def false_negatives_at_thresholds(labels, return fn_value, update_ops['fn'] -@tf_export('metrics.false_positives') +@tf_export(v1=['metrics.false_positives']) def false_positives(labels, predictions, weights=None, @@ -1670,7 +1646,7 @@ def false_positives(labels, updates_collections) -@tf_export('metrics.false_positives_at_thresholds') +@tf_export(v1=['metrics.false_positives_at_thresholds']) def false_positives_at_thresholds(labels, predictions, thresholds, @@ -1726,7 +1702,7 @@ def false_positives_at_thresholds(labels, return fp_value, update_ops['fp'] -@tf_export('metrics.true_negatives') +@tf_export(v1=['metrics.true_negatives']) def true_negatives(labels, predictions, weights=None, @@ -1779,7 +1755,7 @@ def true_negatives(labels, updates_collections) -@tf_export('metrics.true_negatives_at_thresholds') +@tf_export(v1=['metrics.true_negatives_at_thresholds']) def true_negatives_at_thresholds(labels, predictions, thresholds, @@ -1835,7 +1811,7 @@ def true_negatives_at_thresholds(labels, return tn_value, update_ops['tn'] -@tf_export('metrics.true_positives') +@tf_export(v1=['metrics.true_positives']) def true_positives(labels, predictions, weights=None, @@ -1888,7 +1864,7 @@ def true_positives(labels, updates_collections) -@tf_export('metrics.true_positives_at_thresholds') +@tf_export(v1=['metrics.true_positives_at_thresholds']) def true_positives_at_thresholds(labels, predictions, thresholds, @@ -1944,7 +1920,7 @@ def true_positives_at_thresholds(labels, return tp_value, update_ops['tp'] -@tf_export('metrics.precision') +@tf_export(v1=['metrics.precision']) def precision(labels, predictions, weights=None, @@ -2039,7 +2015,7 @@ def precision(labels, return p, update_op -@tf_export('metrics.precision_at_thresholds') +@tf_export(v1=['metrics.precision_at_thresholds']) def precision_at_thresholds(labels, predictions, thresholds, @@ -2120,7 +2096,7 @@ def precision_at_thresholds(labels, return prec, update_op -@tf_export('metrics.recall') +@tf_export(v1=['metrics.recall']) def recall(labels, predictions, weights=None, @@ -2471,7 +2447,7 @@ def _streaming_sparse_false_negative_at_k(labels, return var, state_ops.assign_add(var, batch_total_fn, name='update') -@tf_export('metrics.recall_at_k') +@tf_export(v1=['metrics.recall_at_k']) def recall_at_k(labels, predictions, k, @@ -2564,7 +2540,7 @@ def recall_at_k(labels, name=scope) -@tf_export('metrics.recall_at_top_k') +@tf_export(v1=['metrics.recall_at_top_k']) def recall_at_top_k(labels, predictions_idx, k=None, @@ -2648,7 +2624,7 @@ def recall_at_top_k(labels, return metric, update -@tf_export('metrics.recall_at_thresholds') +@tf_export(v1=['metrics.recall_at_thresholds']) def recall_at_thresholds(labels, predictions, thresholds, @@ -2726,7 +2702,7 @@ def recall_at_thresholds(labels, return rec, update_op -@tf_export('metrics.root_mean_squared_error') +@tf_export(v1=['metrics.root_mean_squared_error']) def root_mean_squared_error(labels, predictions, weights=None, @@ -2797,7 +2773,7 @@ def root_mean_squared_error(labels, return rmse, update_rmse_op -@tf_export('metrics.sensitivity_at_specificity') +@tf_export(v1=['metrics.sensitivity_at_specificity']) def sensitivity_at_specificity(labels, predictions, specificity, @@ -3069,7 +3045,7 @@ def _sparse_average_precision_at_top_k(labels, predictions_idx): # Reduce along k dimension to get the sum, yielding a [D1, ... DN] tensor. precision_sum = math_ops.reduce_sum( - relevant_precision_per_k, reduction_indices=(-1,), name='precision_sum') + relevant_precision_per_k, axis=(-1,), name='precision_sum') # Divide by number of relevant items to get average precision. These are # the "num_relevant_items" and "AveP" terms from the formula above. @@ -3170,7 +3146,7 @@ def _streaming_sparse_average_precision_at_top_k(labels, return mean_average_precision, update -@tf_export('metrics.sparse_average_precision_at_k') +@tf_export(v1=['metrics.sparse_average_precision_at_k']) @deprecated(None, 'Use average_precision_at_k instead') def sparse_average_precision_at_k(labels, predictions, @@ -3190,7 +3166,7 @@ def sparse_average_precision_at_k(labels, name=name) -@tf_export('metrics.average_precision_at_k') +@tf_export(v1=['metrics.average_precision_at_k']) def average_precision_at_k(labels, predictions, k, @@ -3364,7 +3340,7 @@ def _streaming_sparse_false_positive_at_k(labels, return var, state_ops.assign_add(var, batch_total_fp, name='update') -@tf_export('metrics.precision_at_top_k') +@tf_export(v1=['metrics.precision_at_top_k']) def precision_at_top_k(labels, predictions_idx, k=None, @@ -3453,7 +3429,7 @@ def precision_at_top_k(labels, return metric, update -@tf_export('metrics.sparse_precision_at_k') +@tf_export(v1=['metrics.sparse_precision_at_k']) @deprecated(None, 'Use precision_at_k instead') def sparse_precision_at_k(labels, predictions, @@ -3475,7 +3451,7 @@ def sparse_precision_at_k(labels, name=name) -@tf_export('metrics.precision_at_k') +@tf_export(v1=['metrics.precision_at_k']) def precision_at_k(labels, predictions, k, @@ -3569,7 +3545,7 @@ def precision_at_k(labels, name=scope) -@tf_export('metrics.specificity_at_sensitivity') +@tf_export(v1=['metrics.specificity_at_sensitivity']) def specificity_at_sensitivity(labels, predictions, sensitivity, diff --git a/tensorflow/python/ops/nccl_ops_test.py b/tensorflow/python/ops/nccl_ops_test.py index 1b496fec47..3b2e2b0175 100644 --- a/tensorflow/python/ops/nccl_ops_test.py +++ b/tensorflow/python/ops/nccl_ops_test.py @@ -102,7 +102,7 @@ class NcclTestCase(test.TestCase): continue # Test execution and results. - for t in sess.run(result_tensors): + for t in self.evaluate(result_tensors): self.assertAllClose(t, np_ans) def _TestGradient(self, nccl_reduce, numpy_fn): diff --git a/tensorflow/python/ops/nn_batchnorm_test.py b/tensorflow/python/ops/nn_batchnorm_test.py index c8a5b58e45..e978f1d326 100644 --- a/tensorflow/python/ops/nn_batchnorm_test.py +++ b/tensorflow/python/ops/nn_batchnorm_test.py @@ -71,6 +71,7 @@ class BatchNormalizationTest(test.TestCase): gamma if scale_after_normalization else None, epsilon) + @test_util.run_deprecated_v1 def testBatchNorm(self): x_shape = [3, 5, 4, 2] param_shape = [2] @@ -169,16 +170,20 @@ class BatchNormalizationTest(test.TestCase): shift_after_normalization, v, err_tolerance) + @test_util.run_deprecated_v1 def testBatchNormInputGradient(self): self._testBatchNormGradientInAllNeedConfigs(0, "x") + @test_util.run_deprecated_v1 def testBatchNormMeanGradient(self): self._testBatchNormGradientInAllNeedConfigs(1, "mean") + @test_util.run_deprecated_v1 def testBatchNormVarianceGradient(self): self._testBatchNormGradientInAllNeedConfigs( 2, "variance", err_tolerance=1e-03) + @test_util.run_deprecated_v1 def testBatchNormBetaGradient(self): # Since beta does not exist when scale_after_normalization=False, we only # test for scale_after_normalization=True. @@ -187,6 +192,7 @@ class BatchNormalizationTest(test.TestCase): self._testBatchNormGradient(3, "beta", scale_after_normalization, True, v) + @test_util.run_deprecated_v1 def testBatchNormGammaGradient(self): # If scale_after_normalization is False, backprop for gamma in v1 # will be 0. In version 2 of the API, if scale_after_normalization is False, @@ -199,6 +205,7 @@ class BatchNormalizationTest(test.TestCase): self._testBatchNormGradient(4, "gamma", True, shift_after_normalization, 2) + @test_util.run_deprecated_v1 def testBatchNormGradImpl(self): x_shape = [7, 5, 4, 6] param_shape = [6] @@ -235,15 +242,17 @@ class BatchNormalizationTest(test.TestCase): odx, odm, odv, odb, odg = gradients_impl.gradients( [on], [x, m, v, beta, gamma], [backprop]) if scale_after_normalization: - all_grads = sess.run([dx, dm, dv, db, dg, odx, odm, odv, odb, odg]) + all_grads = self.evaluate( + [dx, dm, dv, db, dg, odx, odm, odv, odb, odg]) to_check = ["dx", "dm", "dv", "db", "dg"] else: - all_grads = sess.run([dx, dm, dv, db, odx, odm, odv, odb]) + all_grads = self.evaluate([dx, dm, dv, db, odx, odm, odv, odb]) to_check = ["dx", "dm", "dv", "db"] for i, _ in enumerate(to_check): self.assertAllClose( all_grads[i + len(to_check)], all_grads[i], atol=0.000001) + @test_util.run_deprecated_v1 def testBatchNormKeepDims(self): """Test for tf.nn.moments(..., keep_dims=True / False). @@ -318,7 +327,7 @@ class BatchNormalizationTest(test.TestCase): gamma_val, epsilon, scale_after_normalization, shift_after_normalization) - [tf_batch_norm] = sess.run([bn]) + [tf_batch_norm] = self.evaluate([bn]) self.assertEquals(x_shape, np_batch_norm.shape) self.assertEquals(x_shape, tf_batch_norm.shape) self.assertAllClose(np_batch_norm, tf_batch_norm, atol=atol) @@ -371,9 +380,9 @@ class SufficientStatisticsTest(test.TestCase): x.set_shape(x_shape) op_c, op_m, op_v, op_s = self._opSuffStats(x, axes, shift, keep_dims) if shift: - tf_c, tf_m, tf_v, tf_s = sess.run([op_c, op_m, op_v, op_s]) + tf_c, tf_m, tf_v, tf_s = self.evaluate([op_c, op_m, op_v, op_s]) else: - tf_c, tf_m, tf_v = sess.run([op_c, op_m, op_v]) + tf_c, tf_m, tf_v = self.evaluate([op_c, op_m, op_v]) else: x = array_ops.placeholder( dtype=dtypes.float32, shape=[None] * len(x_shape), name="x") @@ -390,6 +399,7 @@ class SufficientStatisticsTest(test.TestCase): if shift: self.assertAllClose(np_s, tf_s, atol=0.000001) + @test_util.run_deprecated_v1 def testSuffStats(self): for has_shape in [True, False]: for keep_dims in [True, False]: @@ -432,7 +442,7 @@ class NormalizeMomentsTest(test.TestCase): tf_shift_v = None opm, opv = self._opNormalizeMoments(tf_counts, tf_mean_ss, tf_variance_ss, tf_shift_v) - tfm, tfv = sess.run([opm, opv]) + tfm, tfv = self.evaluate([opm, opv]) self.assertAllClose(npm, tfm, atol=0.000001) self.assertAllClose(npv, tfv, atol=0.000001) @@ -507,9 +517,10 @@ class MomentsTest(test.TestCase): expected_variance = expected_x_squared - expected_mean_squared # Check that the moments are correct. - self.assertAllCloseAccordingToType(expected_mean, mean.eval()) - self.assertAllCloseAccordingToType(expected_variance, var.eval()) + self.assertAllCloseAccordingToType(expected_mean, self.evaluate(mean)) + self.assertAllCloseAccordingToType(expected_variance, self.evaluate(var)) + @test_util.run_deprecated_v1 def testBasic(self): for keep_dims in [False, True]: for dtype in [dtypes.float32, dtypes.float16]: @@ -518,6 +529,7 @@ class MomentsTest(test.TestCase): self.RunMomentTestWithDynamicShape( shape=[2, 3, 5, 4], axes=[0], keep_dims=keep_dims, dtype=dtype) + @test_util.run_deprecated_v1 def testGlobalNormalization(self): for keep_dims in [False, True]: for dtype in [dtypes.float32, dtypes.float16]: @@ -532,6 +544,7 @@ class MomentsTest(test.TestCase): keep_dims=keep_dims, dtype=dtype) + @test_util.run_deprecated_v1 def testAxes(self): for keep_dims in [False, True]: for dtype in [dtypes.float32, dtypes.float16]: @@ -572,9 +585,11 @@ class MomentsTest(test.TestCase): print("Moments %s gradient err vs input %d = %g" % (from_y, i, err)) self.assertLess(err, 1e-11) + @test_util.run_deprecated_v1 def testMeanGlobalGradient(self): self._testGlobalGradient(from_y="mean") + @test_util.run_deprecated_v1 def testVarGlobalGradient(self): self._testGlobalGradient(from_y="var") diff --git a/tensorflow/python/ops/nn_fused_batchnorm_test.py b/tensorflow/python/ops/nn_fused_batchnorm_test.py index 5ac8eba6f7..4bc33ff8bd 100644 --- a/tensorflow/python/ops/nn_fused_batchnorm_test.py +++ b/tensorflow/python/ops/nn_fused_batchnorm_test.py @@ -50,7 +50,7 @@ class BatchNormalizationTest(test.TestCase): y = self._batch_norm(x, mean, var, offset, scale, epsilon) if data_format == 'NCHW': y = array_ops.transpose(y, [0, 3, 1, 2]) - return y.eval() + return self.evaluate(y) def _test_inference(self, x_shape, @@ -82,7 +82,7 @@ class BatchNormalizationTest(test.TestCase): epsilon=epsilon, data_format=data_format, is_training=False) - y_val = sess.run(y) + y_val = self.evaluate(y) y_ref = self._inference_ref(x, scale, offset, mean, var, epsilon, data_format) # An atol value of 1e-3 is too small for float16's, because some adjacent @@ -102,7 +102,7 @@ class BatchNormalizationTest(test.TestCase): y = self._batch_norm(x, mean, var, offset, scale, epsilon) if data_format == 'NCHW': y = array_ops.transpose(y, [0, 3, 1, 2]) - return y.eval(), mean.eval(), var.eval() + return self.evaluate(y), self.evaluate(mean), self.evaluate(var) def _test_training(self, x_shape, @@ -127,7 +127,7 @@ class BatchNormalizationTest(test.TestCase): epsilon=epsilon, data_format=data_format, is_training=True) - y_val, mean_val, var_val = sess.run([y, mean, var]) + y_val, mean_val, var_val = self.evaluate([y, mean, var]) y_ref, mean_ref, var_ref = self._training_ref(x, scale, offset, epsilon, data_format) y_atol = 2e-3 if x_dtype == np.float16 else 1e-3 @@ -277,10 +277,10 @@ class BatchNormalizationTest(test.TestCase): if is_training: epsilon = y.op.get_attr('epsilon') data_format = y.op.get_attr('data_format') - grad_vals = sess.run([grad_x, grad_scale, grad_offset]) + grad_vals = self.evaluate([grad_x, grad_scale, grad_offset]) grad_internal = nn_grad._BatchNormGrad(grad_y, x, scale, pop_mean, pop_var, epsilon, data_format) - grad_internal_vals = sess.run(list(grad_internal)) + grad_internal_vals = self.evaluate(list(grad_internal)) for grad_val, grad_internal_val in zip(grad_vals, grad_internal_vals): self.assertAllClose(grad_val, grad_internal_val, atol=err_tolerance) diff --git a/tensorflow/python/ops/nn_grad.py b/tensorflow/python/ops/nn_grad.py index 902653befc..34404edc9a 100644 --- a/tensorflow/python/ops/nn_grad.py +++ b/tensorflow/python/ops/nn_grad.py @@ -18,13 +18,13 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +from tensorflow.python.eager import backprop from tensorflow.python.eager import context from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gen_nn_ops -from tensorflow.python.ops import gradients_impl from tensorflow.python.ops import math_ops from tensorflow.python.ops import nn_ops @@ -948,10 +948,14 @@ def _FusedBatchNormGradGrad(op, *grad): grad_grad_x = grad[0] grad_grad_scale = grad[1] grad_grad_offset = grad[2] - grad_x, grad_scale, grad_offset = _BatchNormGrad( - grad_y, x, scale, pop_mean, pop_var, epsilon, data_format, is_training) - grad_initial = [grad_grad_x, grad_grad_scale, grad_grad_offset] - grad_grad_y, grad_x, grad_scale = gradients_impl.gradients( + with backprop.GradientTape() as tape: + tape.watch(grad_y) + tape.watch(x) + tape.watch(scale) + grad_x, grad_scale, grad_offset = _BatchNormGrad( + grad_y, x, scale, pop_mean, pop_var, epsilon, data_format, is_training) + grad_initial = [grad_grad_x, grad_grad_scale, grad_grad_offset] + grad_grad_y, grad_x, grad_scale = tape.gradient( [grad_x, grad_scale, grad_offset], [grad_y, x, scale], grad_initial) return grad_grad_y, grad_x, grad_scale, None, None diff --git a/tensorflow/python/ops/nn_grad_test.py b/tensorflow/python/ops/nn_grad_test.py index 8065df4b16..95e05a977b 100644 --- a/tensorflow/python/ops/nn_grad_test.py +++ b/tensorflow/python/ops/nn_grad_test.py @@ -22,6 +22,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import gradients_impl from tensorflow.python.ops import nn_grad # pylint: disable=unused-import @@ -31,6 +32,7 @@ from tensorflow.python.platform import test class Relu6OpTest(test.TestCase): + @test_util.run_deprecated_v1 def testRelu6GradGrad(self): inputs = constant_op.constant( [[-2, -1, 1, 3], [5, 7, 8, 9]], dtype=dtypes.float32) diff --git a/tensorflow/python/ops/nn_impl.py b/tensorflow/python/ops/nn_impl.py index ef763a4b61..8f74f831c1 100644 --- a/tensorflow/python/ops/nn_impl.py +++ b/tensorflow/python/ops/nn_impl.py @@ -329,7 +329,7 @@ def swish(features): return features * math_ops.sigmoid(features) -@tf_export("math.l2_normalize", "linalg.l2_normalize", "nn.l2_normalize") +@tf_export(v1=["math.l2_normalize", "linalg.l2_normalize", "nn.l2_normalize"]) @deprecated_args(None, "dim is deprecated, use axis instead", "dim") def l2_normalize(x, axis=None, epsilon=1e-12, name=None, dim=None): """Normalizes along dimension `axis` using an L2 norm. @@ -350,11 +350,36 @@ def l2_normalize(x, axis=None, epsilon=1e-12, name=None, dim=None): name: A name for this operation (optional). dim: Deprecated alias for axis. + Returns: + A `Tensor` with the same shape as `x`. + """ + axis = deprecated_argument_lookup("axis", axis, "dim", dim) + return l2_normalize_v2(x, axis, epsilon, name) + + +@tf_export("math.l2_normalize", "linalg.l2_normalize", "nn.l2_normalize", v1=[]) +def l2_normalize_v2(x, axis=None, epsilon=1e-12, name=None): + """Normalizes along dimension `axis` using an L2 norm. + + For a 1-D tensor with `axis = 0`, computes + + output = x / sqrt(max(sum(x**2), epsilon)) + + For `x` with more dimensions, independently normalizes each 1-D slice along + dimension `axis`. + + Args: + x: A `Tensor`. + axis: Dimension along which to normalize. A scalar or a vector of + integers. + epsilon: A lower bound value for the norm. Will use `sqrt(epsilon)` as the + divisor if `norm < sqrt(epsilon)`. + name: A name for this operation (optional). + Returns: A `Tensor` with the same shape as `x`. """ with ops.name_scope(name, "l2_normalize", [x]) as name: - axis = deprecated_argument_lookup("axis", axis, "dim", dim) x = ops.convert_to_tensor(x, name="x") square_sum = math_ops.reduce_sum(math_ops.square(x), axis, keepdims=True) x_inv_norm = math_ops.rsqrt(math_ops.maximum(square_sum, epsilon)) @@ -424,7 +449,7 @@ def zero_fraction(value, name=None): # pylint: disable=redefined-builtin -@tf_export("nn.depthwise_conv2d") +@tf_export(v1=["nn.depthwise_conv2d"]) def depthwise_conv2d(input, filter, strides, @@ -497,11 +522,68 @@ def depthwise_conv2d(input, op=op) +@tf_export("nn.depthwise_conv2d", v1=[]) +def depthwise_conv2d_v2(input, + filter, + strides, + padding, + data_format=None, + dilations=None, + name=None): + """Depthwise 2-D convolution. + + Given a 4D input tensor ('NHWC' or 'NCHW' data formats) + and a filter tensor of shape + `[filter_height, filter_width, in_channels, channel_multiplier]` + containing `in_channels` convolutional filters of depth 1, `depthwise_conv2d` + applies a different filter to each input channel (expanding from 1 channel + to `channel_multiplier` channels for each), then concatenates the results + together. The output has `in_channels * channel_multiplier` channels. + + In detail, + + output[b, i, j, k * channel_multiplier + q] = sum_{di, dj} + filter[di, dj, k, q] * input[b, strides[1] * i + rate[0] * di, + strides[2] * j + rate[1] * dj, k] + + Must have `strides[0] = strides[3] = 1`. For the most common case of the + same horizontal and vertical strides, `strides = [1, stride, stride, 1]`. + If any value in `rate` is greater than 1, we perform atrous depthwise + convolution, in which case all values in the `strides` tensor must be equal + to 1. + + Args: + input: 4-D with shape according to `data_format`. + filter: 4-D with shape + `[filter_height, filter_width, in_channels, channel_multiplier]`. + strides: 1-D of size 4. The stride of the sliding window for each + dimension of `input`. + padding: A string, either `'VALID'` or `'SAME'`. The padding algorithm. + See the "returns" section of `tf.nn.convolution` for details. + data_format: The data format for input. Either "NHWC" (default) or "NCHW". + dilations: 1-D of size 2. The dilation rate in which we sample input values + across the `height` and `width` dimensions in atrous convolution. If it is + greater than 1, then all values of strides must be 1. + name: A name for this operation (optional). + + Returns: + A 4-D `Tensor` with shape according to `data_format`. E.g., for + "NHWC" format, shape is + `[batch, out_height, out_width, in_channels * channel_multiplier].` + """ + return depthwise_conv2d(input=input, + filter=filter, + strides=strides, + padding=padding, + rate=dilations, + name=name, + data_format=data_format) + # pylint: enable=redefined-builtin # pylint: disable=redefined-builtin,line-too-long -@tf_export("nn.separable_conv2d") +@tf_export(v1=["nn.separable_conv2d"]) def separable_conv2d(input, depthwise_filter, pointwise_filter, @@ -599,10 +681,76 @@ def separable_conv2d(input, name=name) +@tf_export("nn.separable_conv2d", v1=[]) +def separable_conv2d_v2( + input, + depthwise_filter, + pointwise_filter, + strides, + padding, + data_format=None, + dilations=None, + name=None, +): + """2-D convolution with separable filters. + + Performs a depthwise convolution that acts separately on channels followed by + a pointwise convolution that mixes channels. Note that this is separability + between dimensions `[1, 2]` and `3`, not spatial separability between + dimensions `1` and `2`. + + In detail, + + output[b, i, j, k] = sum_{di, dj, q, r} + input[b, strides[1] * i + di, strides[2] * j + dj, q] * + depthwise_filter[di, dj, q, r] * + pointwise_filter[0, 0, q * channel_multiplier + r, k] + + `strides` controls the strides for the depthwise convolution only, since + the pointwise convolution has implicit strides of `[1, 1, 1, 1]`. Must have + `strides[0] = strides[3] = 1`. For the most common case of the same + horizontal and vertical strides, `strides = [1, stride, stride, 1]`. + If any value in `rate` is greater than 1, we perform atrous depthwise + convolution, in which case all values in the `strides` tensor must be equal + to 1. + + Args: + input: 4-D `Tensor` with shape according to `data_format`. + depthwise_filter: 4-D `Tensor` with shape `[filter_height, filter_width, + in_channels, channel_multiplier]`. Contains `in_channels` convolutional + filters of depth 1. + pointwise_filter: 4-D `Tensor` with shape `[1, 1, channel_multiplier * + in_channels, out_channels]`. Pointwise filter to mix channels after + `depthwise_filter` has convolved spatially. + strides: 1-D of size 4. The strides for the depthwise convolution for each + dimension of `input`. + padding: A string, either `'VALID'` or `'SAME'`. The padding algorithm. See + the "returns" section of `tf.nn.convolution` for details. + data_format: The data format for input. Either "NHWC" (default) or "NCHW". + dilations: 1-D of size 2. The dilation rate in which we sample input values + across the `height` and `width` dimensions in atrous convolution. If it is + greater than 1, then all values of strides must be 1. + name: A name for this operation (optional). + + Returns: + A 4-D `Tensor` with shape according to 'data_format'. For + example, with data_format="NHWC", shape is [batch, out_height, + out_width, out_channels]. + """ + return separable_conv2d( + input, + depthwise_filter, + pointwise_filter, + strides, + padding, + rate=dilations, + name=name, + data_format=data_format) + # pylint: enable=redefined-builtin,line-too-long -@tf_export("nn.sufficient_statistics") +@tf_export(v1=["nn.sufficient_statistics"]) def sufficient_statistics(x, axes, shift=None, keep_dims=False, name=None): """Calculate the sufficient statistics for the mean and variance of `x`. @@ -652,6 +800,35 @@ def sufficient_statistics(x, axes, shift=None, keep_dims=False, name=None): return counts, m_ss, v_ss, shift +@tf_export("nn.sufficient_statistics", v1=[]) +def sufficient_statistics_v2(x, axes, shift=None, keepdims=False, name=None): + """Calculate the sufficient statistics for the mean and variance of `x`. + + These sufficient statistics are computed using the one pass algorithm on + an input that's optionally shifted. See: + https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Computing_shifted_data + + Args: + x: A `Tensor`. + axes: Array of ints. Axes along which to compute mean and variance. + shift: A `Tensor` containing the value by which to shift the data for + numerical stability, or `None` if no shift is to be performed. A shift + close to the true mean provides the most numerically stable results. + keepdims: produce statistics with the same dimensionality as the input. + name: Name used to scope the operations that compute the sufficient stats. + + Returns: + Four `Tensor` objects of the same type as `x`: + + * the count (number of elements to average over). + * the (possibly shifted) sum of the elements in the array. + * the (possibly shifted) sum of squares of the elements in the array. + * the shift by which the mean must be corrected or None if `shift` is None. + """ + return sufficient_statistics( + x=x, axes=axes, shift=shift, keep_dims=keepdims, name=name) + + @tf_export("nn.normalize_moments") def normalize_moments(counts, mean_ss, variance_ss, shift, name=None): """Calculate the mean and variance of based on the sufficient statistics. @@ -684,7 +861,7 @@ def normalize_moments(counts, mean_ss, variance_ss, shift, name=None): return (mean, variance) -@tf_export("nn.moments") +@tf_export(v1=["nn.moments"]) def moments( x, axes, @@ -743,7 +920,43 @@ def moments( return (mean, variance) -@tf_export("nn.weighted_moments") +@tf_export("nn.moments", v1=[]) +def moments_v2( + x, + axes, + shift=None, + keepdims=False, + name=None): + """Calculates the mean and variance of `x`. + + The mean and variance are calculated by aggregating the contents of `x` + across `axes`. If `x` is 1-D and `axes = [0]` this is just the mean + and variance of a vector. + + Note: shift is currently not used; the true mean is computed and used. + + When using these moments for batch normalization (see + `tf.nn.batch_normalization`): + + * for so-called "global normalization", used with convolutional filters with + shape `[batch, height, width, depth]`, pass `axes=[0, 1, 2]`. + * for simple batch normalization pass `axes=[0]` (batch only). + + Args: + x: A `Tensor`. + axes: Array of ints. Axes along which to compute mean and + variance. + shift: Not used in the current implementation. + keepdims: produce moments with the same dimensionality as the input. + name: Name used to scope the operations that compute the moments. + + Returns: + Two `Tensor` objects: `mean` and `variance`. + """ + return moments(x=x, axes=axes, shift=shift, name=name, keep_dims=keepdims) + + +@tf_export(v1=["nn.weighted_moments"]) def weighted_moments(x, axes, frequency_weights, name=None, keep_dims=False): """Returns the frequency-weighted mean and variance of `x`. @@ -815,6 +1028,30 @@ def weighted_moments(x, axes, frequency_weights, name=None, keep_dims=False): return weighted_mean, weighted_variance +@tf_export("nn.weighted_moments", v1=[]) +def weighted_moments_v2(x, axes, frequency_weights, keepdims=False, name=None): + """Returns the frequency-weighted mean and variance of `x`. + + Args: + x: A tensor. + axes: 1-d tensor of int32 values; these are the axes along which + to compute mean and variance. + frequency_weights: A tensor of positive weights which can be + broadcast with x. + keepdims: Produce moments with the same dimensionality as the input. + name: Name used to scope the operation. + + Returns: + Two tensors: `weighted_mean` and `weighted_variance`. + """ + return weighted_moments( + x=x, + axes=axes, + frequency_weights=frequency_weights, + name=name, + keep_dims=keepdims) + + @tf_export("nn.batch_normalization") def batch_normalization(x, mean, @@ -875,7 +1112,7 @@ def batch_normalization(x, offset - mean * inv if offset is not None else -mean * inv, x.dtype) -@tf_export("nn.fused_batch_norm") +@tf_export(v1=["nn.fused_batch_norm"]) def fused_batch_norm( x, scale, @@ -946,7 +1183,7 @@ def fused_batch_norm( return y, batch_mean, batch_var -@tf_export("nn.batch_norm_with_global_normalization") +@tf_export(v1=["nn.batch_norm_with_global_normalization"]) def batch_norm_with_global_normalization(t, m, v, @@ -984,6 +1221,53 @@ def batch_norm_with_global_normalization(t, else None, variance_epsilon, name) +# pylint: disable=redefined-builtin,line-too-long +@tf_export("nn.batch_norm_with_global_normalization", v1=[]) +def batch_norm_with_global_normalization_v2(input, + mean, + variance, + beta, + gamma, + variance_epsilon, + scale_after_normalization, + name=None): + """Batch normalization. + + This op is deprecated. See `tf.nn.batch_normalization`. + + Args: + input: A 4D input Tensor. + mean: A 1D mean Tensor with size matching the last dimension of t. + This is the first output from tf.nn.moments, + or a saved moving average thereof. + variance: A 1D variance Tensor with size matching the last dimension of t. + This is the second output from tf.nn.moments, + or a saved moving average thereof. + beta: A 1D beta Tensor with size matching the last dimension of t. + An offset to be added to the normalized tensor. + gamma: A 1D gamma Tensor with size matching the last dimension of t. + If "scale_after_normalization" is true, this tensor will be multiplied + with the normalized tensor. + variance_epsilon: A small float number to avoid dividing by 0. + scale_after_normalization: A bool indicating whether the resulted tensor + needs to be multiplied with gamma. + name: A name for this operation (optional). + + Returns: + A batch-normalized `t`. + """ + return batch_norm_with_global_normalization(t=input, + m=mean, + v=variance, + beta=beta, + gamma=gamma, + variance_epsilon=variance_epsilon, + scale_after_normalization=scale_after_normalization, + name=name) + +# pylint: enable=redefined-builtin,line-too-long + + def _sum_rows(x): """Returns a vector summing up each row of the matrix x.""" # _sum_rows(x) is equivalent to math_ops.reduce_sum(x, 1) when x is @@ -1178,7 +1462,111 @@ def _compute_sampled_logits(weights, return out_logits, out_labels -@tf_export("nn.nce_loss") +@tf_export("nn.nce_loss", v1=[]) +def nce_loss_v2(weights, + biases, + labels, + inputs, + num_sampled, + num_classes, + num_true=1, + sampled_values=None, + remove_accidental_hits=False, + name="nce_loss"): + """Computes and returns the noise-contrastive estimation training loss. + + See [Noise-contrastive estimation: A new estimation principle for + unnormalized statistical + models](http://www.jmlr.org/proceedings/papers/v9/gutmann10a/gutmann10a.pdf). + Also see our [Candidate Sampling Algorithms + Reference](https://www.tensorflow.org/extras/candidate_sampling.pdf) + + A common use case is to use this method for training, and calculate the full + sigmoid loss for evaluation or inference as in the following example: + + ```python + if mode == "train": + loss = tf.nn.nce_loss( + weights=weights, + biases=biases, + labels=labels, + inputs=inputs, + ...) + elif mode == "eval": + logits = tf.matmul(inputs, tf.transpose(weights)) + logits = tf.nn.bias_add(logits, biases) + labels_one_hot = tf.one_hot(labels, n_classes) + loss = tf.nn.sigmoid_cross_entropy_with_logits( + labels=labels_one_hot, + logits=logits) + loss = tf.reduce_sum(loss, axis=1) + ``` + + Note: when doing embedding lookup on `weights` and `bias`, "div" partition + strategy will be used. Support for other partition strategy will be added + later. + + Note: By default this uses a log-uniform (Zipfian) distribution for sampling, + so your labels must be sorted in order of decreasing frequency to achieve + good results. For more details, see + `tf.nn.log_uniform_candidate_sampler`. + + Note: In the case where `num_true` > 1, we assign to each target class + the target probability 1 / `num_true` so that the target probabilities + sum to 1 per-example. + + Note: It would be useful to allow a variable number of target classes per + example. We hope to provide this functionality in a future release. + For now, if you have a variable number of target classes, you can pad them + out to a constant number by either repeating them or by padding + with an otherwise unused class. + + Args: + weights: A `Tensor` of shape `[num_classes, dim]`, or a list of `Tensor` + objects whose concatenation along dimension 0 has shape [num_classes, + dim]. The (possibly-partitioned) class embeddings. + biases: A `Tensor` of shape `[num_classes]`. The class biases. + labels: A `Tensor` of type `int64` and shape `[batch_size, num_true]`. The + target classes. + inputs: A `Tensor` of shape `[batch_size, dim]`. The forward activations of + the input network. + num_sampled: An `int`. The number of negative classes to randomly sample + per batch. This single sample of negative classes is evaluated for each + element in the batch. + num_classes: An `int`. The number of possible classes. + num_true: An `int`. The number of target classes per training example. + sampled_values: a tuple of (`sampled_candidates`, `true_expected_count`, + `sampled_expected_count`) returned by a `*_candidate_sampler` function. + (if None, we default to `log_uniform_candidate_sampler`) + remove_accidental_hits: A `bool`. Whether to remove "accidental hits" + where a sampled class equals one of the target classes. If set to `True`, + this is a "Sampled Logistic" loss instead of NCE, and we are learning to + generate log-odds instead of log probabilities. See our [Candidate + Sampling Algorithms Reference] + (https://www.tensorflow.org/extras/candidate_sampling.pdf). Default is + False. + name: A name for the operation (optional). + + Returns: + A `batch_size` 1-D tensor of per-example NCE losses. + """ + # TODO(yuefengz): get partition_strategy from either variables or distribution + # strategies. + return nce_loss( + weights, + biases, + labels, + inputs, + num_sampled, + num_classes, + num_true=num_true, + sampled_values=sampled_values, + remove_accidental_hits=remove_accidental_hits, + partition_strategy="div", + name=name) + + +@tf_export(v1=["nn.nce_loss"]) def nce_loss(weights, biases, labels, diff --git a/tensorflow/python/ops/nn_ops.py b/tensorflow/python/ops/nn_ops.py index bc195993c2..225904854f 100644 --- a/tensorflow/python/ops/nn_ops.py +++ b/tensorflow/python/ops/nn_ops.py @@ -28,6 +28,7 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors_impl from tensorflow.python.framework import graph_util from tensorflow.python.framework import ops +from tensorflow.python.framework import random_seed from tensorflow.python.framework import tensor_shape from tensorflow.python.framework import tensor_util from tensorflow.python.ops import array_ops @@ -35,13 +36,14 @@ from tensorflow.python.ops import check_ops from tensorflow.python.ops import gen_nn_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import random_ops - # go/tf-wildcard-import # pylint: disable=wildcard-import from tensorflow.python.ops.gen_nn_ops import * # pylint: enable=wildcard-import - from tensorflow.python.util import deprecation +from tensorflow.python.util.deprecation import deprecated_args +from tensorflow.python.util.deprecation import deprecated_argument_lookup + from tensorflow.python.util.tf_export import tf_export # Aliases for some automatically-generated names. @@ -206,6 +208,73 @@ class _NonAtrousConvolution(object): name=self.name) +@tf_export("nn.dilation2d", v1=[]) +def dilation2d_v2( + input, # pylint: disable=redefined-builtin + filters, # pylint: disable=redefined-builtin + strides, + padding, + data_format, + dilations, + name=None): + """Computes the grayscale dilation of 4-D `input` and 3-D `filters` tensors. + + The `input` tensor has shape `[batch, in_height, in_width, depth]` and the + `filters` tensor has shape `[filter_height, filter_width, depth]`, i.e., each + input channel is processed independently of the others with its own + structuring function. The `output` tensor has shape + `[batch, out_height, out_width, depth]`. The spatial dimensions of the output + tensor depend on the `padding` algorithm. We currently only support the + default "NHWC" `data_format`. + + In detail, the grayscale morphological 2-D dilation is the max-sum correlation + (for consistency with `conv2d`, we use unmirrored filters): + + output[b, y, x, c] = + max_{dy, dx} input[b, + strides[1] * y + rates[1] * dy, + strides[2] * x + rates[2] * dx, + c] + + filters[dy, dx, c] + + Max-pooling is a special case when the filter has size equal to the pooling + kernel size and contains all zeros. + + Note on duality: The dilation of `input` by the `filters` is equal to the + negation of the erosion of `-input` by the reflected `filters`. + + Args: + input: A `Tensor`. Must be one of the following types: `float32`, `float64`, + `int32`, `uint8`, `int16`, `int8`, `int64`, `bfloat16`, `uint16`, `half`, + `uint32`, `uint64`. + 4-D with shape `[batch, in_height, in_width, depth]`. + filters: A `Tensor`. Must have the same type as `input`. + 3-D with shape `[filter_height, filter_width, depth]`. + strides: A list of `ints` that has length `>= 4`. + The stride of the sliding window for each dimension of the input + tensor. Must be: `[1, stride_height, stride_width, 1]`. + padding: A `string` from: `"SAME", "VALID"`. + The type of padding algorithm to use. + data_format: A `string`, only `"NCHW"` is currently supported. + dilations: A list of `ints` that has length `>= 4`. + The input stride for atrous morphological dilation. Must be: + `[1, rate_height, rate_width, 1]`. + name: A name for the operation (optional). + + Returns: + A `Tensor`. Has the same type as `input`. + """ + if data_format != "NCHW": + raise ValueError("Data formats other than NCHW are not yet supported") + + return gen_nn_ops.dilation2d(input=input, + filter=filters, + strides=strides, + rates=dilations, + padding=padding, + name=name) + + @tf_export("nn.with_space_to_batch") def with_space_to_batch( input, # pylint: disable=redefined-builtin @@ -644,7 +713,7 @@ def _get_strides_and_dilation_rate(num_spatial_dims, strides, dilation_rate): return strides, dilation_rate -@tf_export("nn.convolution") +@tf_export(v1=["nn.convolution"]) def convolution( input, # pylint: disable=redefined-builtin filter, # pylint: disable=redefined-builtin @@ -782,6 +851,30 @@ def convolution( return op(input, filter) +@tf_export("nn.convolution", v1=[]) +def convolution_v2( + input, # pylint: disable=redefined-builtin + filters, + strides=None, + padding="VALID", + data_format=None, + dilations=None, + name=None): + return convolution( + input, # pylint: disable=redefined-builtin + filters, + padding=padding, + strides=strides, + dilation_rate=dilations, + name=name, + data_format=data_format) + +convolution_v2.__doc__ = deprecation.rewrite_argument_docstring( + deprecation.rewrite_argument_docstring( + convolution.__doc__, "dilation_rate", "dilations"), + "filter", "filters") + + class Convolution(object): """Helper class for convolution. @@ -873,7 +966,7 @@ class Convolution(object): return self.conv_op(inp, filter) -@tf_export("nn.pool") +@tf_export(v1=["nn.pool"]) def pool( input, # pylint: disable=redefined-builtin window_shape, @@ -1044,6 +1137,105 @@ def pool( filter_shape=window_shape) +@tf_export("nn.pool", v1=[]) +def pool_v2( + input, # pylint: disable=redefined-builtin + window_shape, + pooling_type, + strides=None, + padding="VALID", + data_format=None, + dilations=None, + name=None): + # pylint: disable=line-too-long + """Performs an N-D pooling operation. + + In the case that `data_format` does not start with "NC", computes for + 0 <= b < batch_size, + 0 <= x[i] < output_spatial_shape[i], + 0 <= c < num_channels: + + ``` + output[b, x[0], ..., x[N-1], c] = + REDUCE_{z[0], ..., z[N-1]} + input[b, + x[0] * strides[0] - pad_before[0] + dilation_rate[0]*z[0], + ... + x[N-1]*strides[N-1] - pad_before[N-1] + dilation_rate[N-1]*z[N-1], + c], + ``` + + where the reduction function REDUCE depends on the value of `pooling_type`, + and pad_before is defined based on the value of `padding` as described in + the "returns" section of `tf.nn.convolution` for details. + The reduction never includes out-of-bounds positions. + + In the case that `data_format` starts with `"NC"`, the `input` and output are + simply transposed as follows: + + ``` + pool(input, data_format, **kwargs) = + tf.transpose(pool(tf.transpose(input, [0] + range(2,N+2) + [1]), + **kwargs), + [0, N+1] + range(1, N+1)) + ``` + + Args: + input: Tensor of rank N+2, of shape `[batch_size] + input_spatial_shape + + [num_channels]` if data_format does not start with "NC" (default), or + `[batch_size, num_channels] + input_spatial_shape` if data_format starts + with "NC". Pooling happens over the spatial dimensions only. + window_shape: Sequence of N ints >= 1. + pooling_type: Specifies pooling operation, must be "AVG" or "MAX". + strides: Optional. Sequence of N ints >= 1. Defaults to [1]*N. If any value of + strides is > 1, then all values of dilation_rate must be 1. + padding: The padding algorithm, must be "SAME" or "VALID". Defaults to "SAME". + See the "returns" section of `tf.nn.convolution` for details. + data_format: A string or None. Specifies whether the channel dimension of + the `input` and output is the last dimension (default, or if `data_format` + does not start with "NC"), or the second dimension (if `data_format` + starts with "NC"). For N=1, the valid values are "NWC" (default) and + "NCW". For N=2, the valid values are "NHWC" (default) and "NCHW". For + N=3, the valid values are "NDHWC" (default) and "NCDHW". + dilations: Optional. Dilation rate. List of N ints >= 1. Defaults to + [1]*N. If any value of dilation_rate is > 1, then all values of strides + must be 1. + name: Optional. Name of the op. + + Returns: + Tensor of rank N+2, of shape + [batch_size] + output_spatial_shape + [num_channels] + + if data_format is None or does not start with "NC", or + + [batch_size, num_channels] + output_spatial_shape + + if data_format starts with "NC", + where `output_spatial_shape` depends on the value of padding: + + If padding = "SAME": + output_spatial_shape[i] = ceil(input_spatial_shape[i] / strides[i]) + + If padding = "VALID": + output_spatial_shape[i] = + ceil((input_spatial_shape[i] - (window_shape[i] - 1) * dilation_rate[i]) + / strides[i]). + + Raises: + ValueError: if arguments are invalid. + + """ + return pool( + input=input, + window_shape=window_shape, + pooling_type=pooling_type, + padding=padding, + dilation_rate=dilations, + strides=strides, + name=name, + data_format=data_format) + + @tf_export("nn.atrous_conv2d") def atrous_conv2d(value, filters, rate, padding, name=None): """Atrous convolution (a.k.a. convolution with holes or dilated convolution). @@ -1181,7 +1373,208 @@ def atrous_conv2d(value, filters, rate, padding, name=None): name=name) -@tf_export("nn.conv2d_transpose") +@tf_export("nn.conv2d", v1=[]) +def conv2d_v2(input, # pylint: disable=redefined-builtin + filters, + strides, + padding, + data_format="NHWC", + dilations=None, + name=None): + # pylint: disable=line-too-long + r"""Computes a 2-D convolution given 4-D `input` and `filters` tensors. + + Given an input tensor of shape `[batch, in_height, in_width, in_channels]` + and a filter / kernel tensor of shape + `[filter_height, filter_width, in_channels, out_channels]`, this op + performs the following: + + 1. Flattens the filter to a 2-D matrix with shape + `[filter_height * filter_width * in_channels, output_channels]`. + 2. Extracts image patches from the input tensor to form a *virtual* + tensor of shape `[batch, out_height, out_width, + filter_height * filter_width * in_channels]`. + 3. For each patch, right-multiplies the filter matrix and the image patch + vector. + + In detail, with the default NHWC format, + + output[b, i, j, k] = + sum_{di, dj, q} input[b, strides[1] * i + di, strides[2] * j + dj, q] * + filter[di, dj, q, k] + + Must have `strides[0] = strides[3] = 1`. For the most common case of the same + horizontal and vertices strides, `strides = [1, stride, stride, 1]`. + + Args: + input: A `Tensor`. Must be one of the following types: + `half`, `bfloat16`, `float32`, `float64`. + A 4-D tensor. The dimension order is interpreted according to the value + of `data_format`, see below for details. + filters: A `Tensor`. Must have the same type as `input`. + A 4-D tensor of shape + `[filter_height, filter_width, in_channels, out_channels]` + strides: A list of `ints`. + 1-D tensor of length 4. The stride of the sliding window for each + dimension of `input`. The dimension order is determined by the value of + `data_format`, see below for details. + padding: A `string` from: `"SAME", "VALID"`. + The type of padding algorithm to use. + data_format: An optional `string` from: `"NHWC", "NCHW"`. + Defaults to `"NHWC"`. + Specify the data format of the input and output data. With the + default format "NHWC", the data is stored in the order of: + [batch, height, width, channels]. + Alternatively, the format could be "NCHW", the data storage order of: + [batch, channels, height, width]. + dilations: An optional list of `ints`. Defaults to `[1, 1, 1, 1]`. + 1-D tensor of length 4. The dilation factor for each dimension of + `input`. If set to k > 1, there will be k-1 skipped cells between each + filter element on that dimension. The dimension order is determined by the + value of `data_format`, see above for details. Dilations in the batch and + depth dimensions must be 1. + name: A name for the operation (optional). + + Returns: + A `Tensor`. Has the same type as `input`. + """ + # pylint: enable=line-too-long + if dilations is None: + dilations = [1, 1, 1, 1] + return gen_nn_ops.conv2d(input, # pylint: disable=redefined-builtin + filters, + strides, + padding, + use_cudnn_on_gpu=True, + data_format=data_format, + dilations=dilations, + name=name) +tf_export(v1=["nn.conv2d"])(gen_nn_ops.conv2d) + + +@tf_export("nn.conv2d_backprop_filter", v1=[]) +def conv2d_backprop_filter_v2(input, # pylint: disable=redefined-builtin + filter_sizes, + out_backprop, + strides, + padding, + data_format="NHWC", + dilations=None, + name=None): + r"""Computes the gradients of convolution with respect to the filter. + + Args: + input: A `Tensor`. Must be one of the following types: + `half`, `bfloat16`, `float32`, `float64`. + 4-D with shape `[batch, in_height, in_width, in_channels]`. + filter_sizes: A `Tensor` of type `int32`. + An integer vector representing the tensor shape of `filter`, + where `filter` is a 4-D + `[filter_height, filter_width, in_channels, out_channels]` tensor. + out_backprop: A `Tensor`. Must have the same type as `input`. + 4-D with shape `[batch, out_height, out_width, out_channels]`. + Gradients w.r.t. the output of the convolution. + strides: A list of `ints`. + The stride of the sliding window for each dimension of the input + of the convolution. Must be in the same order as the dimension specified + with format. + padding: A `string` from: `"SAME", "VALID"`. + The type of padding algorithm to use. + data_format: An optional `string` from: `"NHWC", "NCHW"`. + Defaults to `"NHWC"`. + Specify the data format of the input and output data. With the + default format "NHWC", the data is stored in the order of: + [batch, in_height, in_width, in_channels]. + Alternatively, the format could be "NCHW", the data storage order of: + [batch, in_channels, in_height, in_width]. + dilations: An optional list of `ints`. Defaults to `[1, 1, 1, 1]`. + 1-D tensor of length 4. The dilation factor for each dimension of + `input`. If set to k > 1, there will be k-1 skipped cells between each + filter element on that dimension. The dimension order is determined by + the value of `data_format`, see above for details. Dilations in the batch + and depth dimensions must be 1. + name: A name for the operation (optional). + + Returns: + A `Tensor`. Has the same type as `input`. + """ + if dilations is None: + dilations = [1, 1, 1, 1] + return gen_nn_ops.conv2d_backprop_filter(input, # pylint: disable=redefined-builtin + filter_sizes, + out_backprop, + strides, + padding, + use_cudnn_on_gpu=True, + data_format=data_format, + dilations=dilations, + name=name) +tf_export(v1=["nn.conv2d_backprop_filter"])( + gen_nn_ops.conv2d_backprop_filter) + + +@tf_export("nn.conv2d_backprop_input", v1=[]) +def conv2d_backprop_input_v2(input_sizes, + filters, + out_backprop, + strides, + padding, + data_format="NHWC", + dilations=None, + name=None): + r"""Computes the gradients of convolution with respect to the input. + + Args: + input_sizes: A `Tensor` of type `int32`. + An integer vector representing the shape of `input`, + where `input` is a 4-D `[batch, height, width, channels]` tensor. + filters: A `Tensor`. Must be one of the following types: + `half`, `bfloat16`, `float32`, `float64`. + 4-D with shape + `[filter_height, filter_width, in_channels, out_channels]`. + out_backprop: A `Tensor`. Must have the same type as `filters`. + 4-D with shape `[batch, out_height, out_width, out_channels]`. + Gradients w.r.t. the output of the convolution. + strides: A list of `ints`. + The stride of the sliding window for each dimension of the input + of the convolution. Must be in the same order as the dimension specified + with format. + padding: A `string` from: `"SAME", "VALID"`. + The type of padding algorithm to use. + data_format: An optional `string` from: `"NHWC", "NCHW"`. + Defaults to `"NHWC"`. + Specify the data format of the input and output data. With the + default format "NHWC", the data is stored in the order of: + [batch, in_height, in_width, in_channels]. + Alternatively, the format could be "NCHW", the data storage order of: + [batch, in_channels, in_height, in_width]. + dilations: An optional list of `ints`. Defaults to `[1, 1, 1, 1]`. + 1-D tensor of length 4. The dilation factor for each dimension of + `input`. If set to k > 1, there will be k-1 skipped cells between each + filter element on that dimension. The dimension order is determined by + the value of `data_format`, see above for details. Dilations in the batch + and depth dimensions must be 1. + name: A name for the operation (optional). + + Returns: + A `Tensor`. Has the same type as `filters`. + """ + if dilations is None: + dilations = [1, 1, 1, 1] + return gen_nn_ops.conv2d_backprop_input(input_sizes, + filters, + out_backprop, + strides, + padding, + use_cudnn_on_gpu=True, + data_format=data_format, + dilations=dilations, + name=name) +tf_export(v1=["nn.conv2d_backprop_input"])( + gen_nn_ops.conv2d_backprop_input) + + +@tf_export(v1=["nn.conv2d_transpose"]) def conv2d_transpose( value, filter, # pylint: disable=redefined-builtin @@ -1261,6 +1654,31 @@ def conv2d_transpose( name=name) +# pylint: disable=redefined-builtin +@tf_export("nn.conv2d_transpose", v1=[]) +def conv2d_transpose_v2( + input, + filters, # pylint: disable=redefined-builtin + output_shape, + strides, + padding="SAME", + data_format="NHWC", + name=None): + return conv2d_transpose( + input, + filters, + output_shape, + strides, + padding=padding, + data_format=data_format, + name=name) +# pylint: enable=redefined-builtin +conv2d_transpose_v2.__doc__ = deprecation.rewrite_argument_docstring( + deprecation.rewrite_argument_docstring( + conv2d_transpose.__doc__, "filter", "filters"), + "value", "input") + + @tf_export("nn.atrous_conv2d_transpose") def atrous_conv2d_transpose(value, filters, @@ -1409,7 +1827,29 @@ def atrous_conv2d_transpose(value, input=value, crops=batch_to_space_crop, block_size=rate) -@tf_export("nn.conv3d_transpose") +@tf_export("nn.conv3d", v1=[]) +def conv3d_v2(input, # pylint: disable=redefined-builtin,missing-docstring + filters, + strides, + padding, + data_format="NDHWC", + dilations=None, + name=None): + if dilations is None: + dilations = [1, 1, 1, 1, 1] + return gen_nn_ops.conv3d(input, # pylint: disable=redefined-builtin + filters, + strides, + padding, + data_format=data_format, + dilations=dilations, + name=name) +tf_export(v1=["nn.conv3d"])(gen_nn_ops.conv3d) +conv3d_v2.__doc__ = deprecation.rewrite_argument_docstring( + gen_nn_ops.conv3d.__doc__, "filter", "filters") + + +@tf_export(v1=["nn.conv3d_transpose"]) def conv3d_transpose( value, filter, # pylint: disable=redefined-builtin @@ -1487,6 +1927,31 @@ def conv3d_transpose( name=name) +# pylint: disable=redefined-builtin +@tf_export("nn.conv3d_transpose", v1=[]) +def conv3d_transpose_v2( + input, + filters, + output_shape, + strides, + padding="SAME", + data_format="NDHWC", + name=None): + return conv3d_transpose( + input, + filters, + output_shape, + strides, + padding=padding, + data_format=data_format, + name=name) +# pylint: enable=redefined-builtin +conv3d_transpose_v2.__doc__ = deprecation.rewrite_argument_docstring( + deprecation.rewrite_argument_docstring( + conv3d_transpose.__doc__, "filter", "filters"), + "value", "input") + + @tf_export("nn.bias_add") def bias_add(value, bias, data_format=None, name=None): """Adds `bias` to `value`. @@ -1542,7 +2007,7 @@ def bias_add_v1(value, bias, name=None): return gen_nn_ops.bias_add_v1(value, bias, name=name) -@tf_export("nn.crelu") +@tf_export(v1=["nn.crelu"]) def crelu(features, name=None, axis=-1): """Computes Concatenated ReLU. @@ -1568,6 +2033,12 @@ def crelu(features, name=None, axis=-1): return gen_nn_ops.relu(c) +@tf_export("nn.crelu", v1=[]) +def crelu_v2(features, axis=-1, name=None): + return crelu(features, name=name, axis=axis) +crelu_v2.__doc__ = crelu.__doc__ + + @tf_export("nn.relu6") def relu6(features, name=None): """Computes Rectified Linear 6: `min(max(features, 0), 6)`. @@ -1715,7 +2186,7 @@ def _softmax(logits, compute_op, dim=-1, name=None): return output -@tf_export("nn.softmax", "math.softmax") +@tf_export(v1=["nn.softmax", "math.softmax"]) @deprecation.deprecated_args(None, "dim is deprecated, use axis instead", "dim") def softmax(logits, axis=None, name=None, dim=None): """Computes softmax activations. @@ -1745,7 +2216,34 @@ def softmax(logits, axis=None, name=None, dim=None): return _softmax(logits, gen_nn_ops.softmax, axis, name) -@tf_export("nn.log_softmax", "math.log_softmax") +@tf_export("nn.softmax", "math.softmax", v1=[]) +def softmax_v2(logits, axis=None, name=None): + """Computes softmax activations. + + This function performs the equivalent of + + softmax = tf.exp(logits) / tf.reduce_sum(tf.exp(logits), axis) + + Args: + logits: A non-empty `Tensor`. Must be one of the following types: `half`, + `float32`, `float64`. + axis: The dimension softmax would be performed on. The default is -1 which + indicates the last dimension. + name: A name for the operation (optional). + + Returns: + A `Tensor`. Has the same type and shape as `logits`. + + Raises: + InvalidArgumentError: if `logits` is empty or `axis` is beyond the last + dimension of `logits`. + """ + if axis is None: + axis = -1 + return _softmax(logits, gen_nn_ops.softmax, axis, name) + + +@tf_export(v1=["nn.log_softmax", "math.log_softmax"]) @deprecation.deprecated_args(None, "dim is deprecated, use axis instead", "dim") def log_softmax(logits, axis=None, name=None, dim=None): """Computes log softmax activations. @@ -1775,6 +2273,33 @@ def log_softmax(logits, axis=None, name=None, dim=None): return _softmax(logits, gen_nn_ops.log_softmax, axis, name) +@tf_export("nn.log_softmax", "math.log_softmax", v1=[]) +def log_softmax_v2(logits, axis=None, name=None): + """Computes log softmax activations. + + For each batch `i` and class `j` we have + + logsoftmax = logits - log(reduce_sum(exp(logits), axis)) + + Args: + logits: A non-empty `Tensor`. Must be one of the following types: `half`, + `float32`, `float64`. + axis: The dimension softmax would be performed on. The default is -1 which + indicates the last dimension. + name: A name for the operation (optional). + + Returns: + A `Tensor`. Has the same type as `logits`. Same shape as `logits`. + + Raises: + InvalidArgumentError: if `logits` is empty or `axis` is beyond the last + dimension of `logits`. + """ + if axis is None: + axis = -1 + return _softmax(logits, gen_nn_ops.log_softmax, axis, name) + + def _ensure_xent_args(name, sentinel, labels, logits): # Make sure that all arguments were passed as named arguments. if sentinel is not None: @@ -1784,9 +2309,8 @@ def _ensure_xent_args(name, sentinel, labels, logits): raise ValueError("Both labels and logits must be provided.") -@tf_export("nn.softmax_cross_entropy_with_logits", - v1=["nn.softmax_cross_entropy_with_logits_v2"]) -def softmax_cross_entropy_with_logits_v2(labels, logits, dim=-1, name=None): +@tf_export("nn.softmax_cross_entropy_with_logits", v1=[]) +def softmax_cross_entropy_with_logits_v2(labels, logits, axis=-1, name=None): """Computes softmax cross entropy between `logits` and `labels`. Measures the probability error in discrete classification tasks in which the @@ -1808,7 +2332,7 @@ def softmax_cross_entropy_with_logits_v2(labels, logits, dim=-1, name=None): A common use case is to have logits and labels of shape `[batch_size, num_classes]`, but higher dimensions are supported, with - the `dim` argument specifying the class dimension. + the `axis` argument specifying the class dimension. `logits` and `labels` must have the same dtype (either `float16`, `float32`, or `float64`). @@ -1826,8 +2350,64 @@ def softmax_cross_entropy_with_logits_v2(labels, logits, dim=-1, name=None): `[batch_size, num_classes]`, each row of `labels[i]` must be a valid probability distribution. logits: Unscaled log probabilities. - dim: The class dimension. Defaulted to -1 which is the last dimension. + axis: The class dimension. Defaulted to -1 which is the last dimension. + name: A name for the operation (optional). + + Returns: + A `Tensor` that contains the softmax cross entropy loss. Its type is the + same as `logits` and its shape is the same as `labels` except that it does + not have the last dimension of `labels`. + """ + return softmax_cross_entropy_with_logits_v2_helper( + labels=labels, logits=logits, axis=axis, name=name) + + +@tf_export(v1=["nn.softmax_cross_entropy_with_logits_v2"]) +@deprecated_args(None, "dim is deprecated, use axis instead", "dim") +def softmax_cross_entropy_with_logits_v2_helper( + labels, logits, axis=None, name=None, dim=None): + """Computes softmax cross entropy between `logits` and `labels`. + + Measures the probability error in discrete classification tasks in which the + classes are mutually exclusive (each entry is in exactly one class). For + example, each CIFAR-10 image is labeled with one and only one label: an image + can be a dog or a truck, but not both. + + **NOTE:** While the classes are mutually exclusive, their probabilities + need not be. All that is required is that each row of `labels` is + a valid probability distribution. If they are not, the computation of the + gradient will be incorrect. + + If using exclusive `labels` (wherein one and only + one class is true at a time), see `sparse_softmax_cross_entropy_with_logits`. + + **WARNING:** This op expects unscaled logits, since it performs a `softmax` + on `logits` internally for efficiency. Do not call this op with the + output of `softmax`, as it will produce incorrect results. + + A common use case is to have logits and labels of shape + `[batch_size, num_classes]`, but higher dimensions are supported, with + the `axis` argument specifying the class dimension. + + `logits` and `labels` must have the same dtype (either `float16`, `float32`, + or `float64`). + + Backpropagation will happen into both `logits` and `labels`. To disallow + backpropagation into `labels`, pass label tensors through `tf.stop_gradient` + before feeding it to this function. + + **Note that to avoid confusion, it is required to pass only named arguments to + this function.** + + Args: + labels: Each vector along the class dimension should hold a valid + probability distribution e.g. for the case in which labels are of shape + `[batch_size, num_classes]`, each row of `labels[i]` must be a valid + probability distribution. + logits: Unscaled log probabilities. + axis: The class dimension. Defaulted to -1 which is the last dimension. name: A name for the operation (optional). + dim: Deprecated alias for axis. Returns: A `Tensor` that contains the softmax cross entropy loss. Its type is the @@ -1837,6 +2417,10 @@ def softmax_cross_entropy_with_logits_v2(labels, logits, dim=-1, name=None): # TODO(pcmurray) Raise an error when the labels do not sum to 1. Note: This # could break users who call this with bad labels, but disregard the bad # results. + axis = deprecated_argument_lookup("axis", axis, "dim", dim) + del dim + if axis is None: + axis = -1 with ops.name_scope(name, "softmax_cross_entropy_with_logits", [logits, labels]) as name: @@ -1853,7 +2437,7 @@ def softmax_cross_entropy_with_logits_v2(labels, logits, dim=-1, name=None): shape = logits.get_shape() # Move the dim to the end if dim is not the last dimension. - if dim is not -1: + if axis != -1: def _move_dim_to_end(tensor, dim_index, rank): return array_ops.transpose( @@ -1863,8 +2447,8 @@ def softmax_cross_entropy_with_logits_v2(labels, logits, dim=-1, name=None): math_ops.range(dim_index + 1, rank), [dim_index] ], 0)) - precise_logits = _move_dim_to_end(precise_logits, dim, input_rank) - labels = _move_dim_to_end(labels, dim, input_rank) + precise_logits = _move_dim_to_end(precise_logits, axis, input_rank) + labels = _move_dim_to_end(labels, axis, input_rank) input_shape = array_ops.shape(precise_logits) @@ -1878,7 +2462,7 @@ def softmax_cross_entropy_with_logits_v2(labels, logits, dim=-1, name=None): cost, unused_backprop = gen_nn_ops.softmax_cross_entropy_with_logits( precise_logits, labels, name=name) - # The output cost shape should be the input minus dim. + # The output cost shape should be the input minus axis. output_shape = array_ops.slice(input_shape, [0], [math_ops.subtract(input_rank, 1)]) cost = array_ops.reshape(cost, output_shape) @@ -1888,7 +2472,7 @@ def softmax_cross_entropy_with_logits_v2(labels, logits, dim=-1, name=None): if not context.executing_eagerly( ) and shape is not None and shape.dims is not None: shape = shape.as_list() - del shape[dim] + del shape[axis] cost.set_shape(shape) if convert_to_float32: @@ -1966,7 +2550,7 @@ def softmax_cross_entropy_with_logits( labels = array_ops.stop_gradient(labels, name="labels_stop_gradient") return softmax_cross_entropy_with_logits_v2( - labels=labels, logits=logits, dim=dim, name=name) + labels=labels, logits=logits, axis=dim, name=name) @tf_export("nn.sparse_softmax_cross_entropy_with_logits") @@ -2155,6 +2739,67 @@ def max_pool(value, ksize, strides, padding, data_format="NHWC", name=None): name=name) +# pylint: disable=redefined-builtin +@tf_export("nn.max_pool_with_argmax", v1=[]) +def max_pool_with_argmax_v2(input, + ksize, + strides, + padding, + data_format="NHWC", + output_dtype=dtypes.int64, + name=None): + """Performs max pooling on the input and outputs both max values and indices. + + The indices in `argmax` are flattened, so that a maximum value at position + `[b, y, x, c]` becomes flattened index + `((b * height + y) * width + x) * channels + c`. + + The indices returned are always in `[0, height) x [0, width)` before + flattening, even if padding is involved and the mathematically correct answer + is outside (either negative or too large). This is a bug, but fixing it is + difficult to do in a safe backwards compatible way, especially due to + flattening. + + Args: + input: A `Tensor`. Must be one of the following types: `float32`, `float64`, + `int32`, `uint8`, `int16`, `int8`, `int64`, `bfloat16`, `uint16`, `half`, + `uint32`, `uint64`. + 4-D with shape `[batch, height, width, channels]`. Input to pool over. + ksize: A list of `ints` that has length `>= 4`. + The size of the window for each dimension of the input tensor. + strides: A list of `ints` that has length `>= 4`. + The stride of the sliding window for each dimension of the + input tensor. + padding: A `string` from: `"SAME", "VALID"`. + The type of padding algorithm to use. + data_format: An optional `string`, must be set to `"NHWC"`. Defaults to + `"NHWC"`. + Specify the data format of the input and output data. + output_dtype: An optional `tf.DType` from: `tf.int32, tf.int64`. + Defaults to `tf.int64`. + The dtype of the returned argmax tensor. + name: A name for the operation (optional). + + Returns: + A tuple of `Tensor` objects (output, argmax). + + output: A `Tensor`. Has the same type as `input`. + argmax: A `Tensor` of type `output_dtype`. + """ + + if data_format != "NHWC": + raise ValueError("Data formats other than 'NHWC' are not yet supported") + + return gen_nn_ops.max_pool_with_argmax(input=input, + ksize=ksize, + strides=strides, + padding=padding, + Targmax=output_dtype, + name=name) + +# pylint: enable=redefined-builtin + + @ops.RegisterStatistics("Conv2D", "flops") def _calc_conv_flops(graph, node): """Calculates the compute resources needed for Conv2D.""" @@ -2199,7 +2844,7 @@ def _calc_bias_add_flops(graph, node): return ops.OpStats("flops", input_count) -@tf_export("nn.xw_plus_b") +@tf_export(v1=["nn.xw_plus_b"]) def xw_plus_b(x, weights, biases, name=None): # pylint: disable=invalid-name """Computes matmul(x, weights) + biases. @@ -2271,12 +2916,16 @@ def _get_noise_shape(x, noise_shape): return noise_shape -@tf_export("nn.dropout") -def dropout(x, keep_prob, noise_shape=None, seed=None, name=None): # pylint: disable=invalid-name +@tf_export(v1=["nn.dropout"]) +@deprecation.deprecated_args(None, "Please use `rate` instead of `keep_prob`. " + "Rate should be set to `rate = 1 - keep_prob`.", + "keep_prob") +def dropout(x, keep_prob=None, noise_shape=None, seed=None, name=None, + rate=None): # pylint: disable=invalid-name """Computes dropout. - With probability `keep_prob`, outputs the input element scaled up by - `1 / keep_prob`, otherwise outputs `0`. The scaling is so that the expected + For each element of `x`, with probability `rate`, outputs `0`, and otherwise + scales up the input by `1 / (1-rate)`. The scaling is such that the expected sum is unchanged. By default, each element is kept or dropped independently. If `noise_shape` @@ -2289,8 +2938,59 @@ def dropout(x, keep_prob, noise_shape=None, seed=None, name=None): # pylint: di Args: x: A floating point tensor. - keep_prob: A scalar `Tensor` with the same type as x. The probability - that each element is kept. + keep_prob: (deprecated) A deprecated alias for `(1-rate)`. + noise_shape: A 1-D `Tensor` of type `int32`, representing the + shape for randomly generated keep/drop flags. + seed: A Python integer. Used to create random seeds. See + `tf.set_random_seed` for behavior. + name: A name for this operation (optional). + rate: A scalar `Tensor` with the same type as `x`. The probability that each + element of `x` is discarded. + + Returns: + A Tensor of the same shape of `x`. + + Raises: + ValueError: If `rate` is not in `[0, 1)` or if `x` is not a floating + point tensor. + """ + try: + keep = 1. - keep_prob if keep_prob is not None else None + except TypeError: + raise ValueError("keep_prob must be a floating point number or Tensor " + "(got %r)" % keep_prob) + + rate = deprecation.deprecated_argument_lookup( + "rate", rate, + "keep_prob", keep) + + if rate is None: + raise ValueError("You must provide a rate to dropout.") + + return dropout_v2(x, rate, noise_shape=noise_shape, seed=seed, name=name) + + +@tf_export("nn.dropout", v1=[]) +def dropout_v2(x, rate, noise_shape=None, seed=None, name=None): # pylint: disable=invalid-name + """Computes dropout. + + With probability `rate`, drops elements of `x`. Input that are kept are + scaled up by `1 / (1 - rate)`, otherwise outputs `0`. The scaling is so that + the expected sum is unchanged. + + By default, each element is kept or dropped independently. If `noise_shape` + is specified, it must be + [broadcastable](http://docs.scipy.org/doc/numpy/user/basics.broadcasting.html) + to the shape of `x`, and only dimensions with `noise_shape[i] == shape(x)[i]` + will make independent decisions. For example, if `shape(x) = [k, l, m, n]` + and `noise_shape = [k, 1, 1, n]`, each batch and channel component will be + kept independently and each row and column will be kept or not kept together. + + Args: + x: A floating point tensor. + rate: A scalar `Tensor` with the same type as x. The probability + that each element is dropped. For example, setting rate=0.1 would drop + 10% of input elements. noise_shape: A 1-D `Tensor` of type `int32`, representing the shape for randomly generated keep/drop flags. seed: A Python integer. Used to create random seeds. See @@ -2310,28 +3010,29 @@ def dropout(x, keep_prob, noise_shape=None, seed=None, name=None): # pylint: di if not x.dtype.is_floating: raise ValueError("x has to be a floating point tensor since it's going to" " be scaled. Got a %s tensor instead." % x.dtype) - if isinstance(keep_prob, numbers.Real) and not 0 < keep_prob <= 1: - raise ValueError("keep_prob must be a scalar tensor or a float in the " - "range (0, 1], got %g" % keep_prob) + if isinstance(rate, numbers.Real) and not (rate >= 0 and rate < 1): + raise ValueError("rate must be a scalar tensor or a float in the " + "range [0, 1), got %g" % rate) # Early return if nothing needs to be dropped. - if isinstance(keep_prob, float) and keep_prob == 1: + if isinstance(rate, numbers.Real) and rate == 0: return x if context.executing_eagerly(): - if isinstance(keep_prob, ops.EagerTensor): - if keep_prob.numpy() == 1: + if isinstance(rate, ops.EagerTensor): + if rate.numpy() == 0: return x else: - keep_prob = ops.convert_to_tensor( - keep_prob, dtype=x.dtype, name="keep_prob") - keep_prob.get_shape().assert_is_compatible_with(tensor_shape.scalar()) + rate = ops.convert_to_tensor( + rate, dtype=x.dtype, name="rate") + rate.get_shape().assert_is_compatible_with(tensor_shape.scalar()) - # Do nothing if we know keep_prob == 1 - if tensor_util.constant_value(keep_prob) == 1: + # Do nothing if we know rate == 0 + if tensor_util.constant_value(rate) == 0: return x noise_shape = _get_noise_shape(x, noise_shape) + keep_prob = 1 - rate # uniform [keep_prob, 1.0 + keep_prob) random_tensor = keep_prob random_tensor += random_ops.random_uniform( @@ -2402,7 +3103,293 @@ def nth_element(input, n, reverse=False, name=None): # pylint: disable=redefine return gen_nn_ops.nth_element(input, n, reverse=reverse, name=name) -@tf_export("nn.conv1d") +@tf_export(v1=["nn.fractional_max_pool"]) +@deprecation.deprecated(date=None, instructions="`seed2` and `deterministic` " + "args are deprecated. Use fractional_max_pool_v2.") +def fractional_max_pool(value, + pooling_ratio, + pseudo_random=False, + overlapping=False, + deterministic=False, + seed=0, + seed2=0, + name=None): # pylint: disable=redefined-builtin + r"""Performs fractional max pooling on the input. + + This is a deprecated version of `fractional_max_pool`. + + Fractional max pooling is slightly different than regular max pooling. In + regular max pooling, you downsize an input set by taking the maximum value of + smaller N x N subsections of the set (often 2x2), and try to reduce the set by + a factor of N, where N is an integer. Fractional max pooling, as you might + expect from the word "fractional", means that the overall reduction ratio N + does not have to be an integer. + + The sizes of the pooling regions are generated randomly but are fairly + uniform. For example, let's look at the height dimension, and the constraints + on the list of rows that will be pool boundaries. + + First we define the following: + + 1. input_row_length : the number of rows from the input set + 2. output_row_length : which will be smaller than the input + 3. alpha = input_row_length / output_row_length : our reduction ratio + 4. K = floor(alpha) + 5. row_pooling_sequence : this is the result list of pool boundary rows + + Then, row_pooling_sequence should satisfy: + + 1. a[0] = 0 : the first value of the sequence is 0 + 2. a[end] = input_row_length : the last value of the sequence is the size + 3. K <= (a[i+1] - a[i]) <= K+1 : all intervals are K or K+1 size + 4. length(row_pooling_sequence) = output_row_length+1 + + For more details on fractional max pooling, see this paper: [Benjamin Graham, + Fractional Max-Pooling](http://arxiv.org/abs/1412.6071) + + Args: + value: A `Tensor`. 4-D with shape `[batch, height, width, channels]`. + pooling_ratio: A list of `floats` that has length >= 4. Pooling ratio for + each dimension of `value`, currently only supports row and col dimension + and should be >= 1.0. For example, a valid pooling ratio looks like [1.0, + 1.44, 1.73, 1.0]. The first and last elements must be 1.0 because we don't + allow pooling on batch and channels dimensions. 1.44 and 1.73 are pooling + ratio on height and width dimensions respectively. + pseudo_random: An optional `bool`. Defaults to `False`. When set to `True`, + generates the pooling sequence in a pseudorandom fashion, otherwise, in a + random fashion. Check paper [Benjamin Graham, Fractional + Max-Pooling](http://arxiv.org/abs/1412.6071) for difference between + pseudorandom and random. + overlapping: An optional `bool`. Defaults to `False`. When set to `True`, + it means when pooling, the values at the boundary of adjacent pooling + cells are used by both cells. For example: + `index 0 1 2 3 4` + `value 20 5 16 3 7` + If the pooling sequence is [0, 2, 4], then 16, at index 2 will be used + twice. The result would be [20, 16] for fractional max pooling. + deterministic: An optional `bool`. Deprecated; use `fractional_max_pool_v2` + instead. + seed: An optional `int`. Defaults to `0`. If set to be non-zero, the + random number generator is seeded by the given seed. Otherwise it is + seeded by a random seed. + seed2: An optional `int`. Deprecated; use `fractional_max_pool_v2` instead. + name: A name for the operation (optional). + + Returns: + A tuple of `Tensor` objects (`output`, `row_pooling_sequence`, + `col_pooling_sequence`). + output: Output `Tensor` after fractional max pooling. Has the same type as + `value`. + row_pooling_sequence: A `Tensor` of type `int64`. + col_pooling_sequence: A `Tensor` of type `int64`. + """ + return gen_nn_ops.fractional_max_pool(value, pooling_ratio, pseudo_random, + overlapping, deterministic, seed, seed2, + name) + + +@tf_export("nn.fractional_max_pool", v1=[]) +def fractional_max_pool_v2(value, + pooling_ratio, + pseudo_random=False, + overlapping=False, + seed=0, + name=None): # pylint: disable=redefined-builtin + r"""Performs fractional max pooling on the input. + + Fractional max pooling is slightly different than regular max pooling. In + regular max pooling, you downsize an input set by taking the maximum value of + smaller N x N subsections of the set (often 2x2), and try to reduce the set by + a factor of N, where N is an integer. Fractional max pooling, as you might + expect from the word "fractional", means that the overall reduction ratio N + does not have to be an integer. + + The sizes of the pooling regions are generated randomly but are fairly + uniform. For example, let's look at the height dimension, and the constraints + on the list of rows that will be pool boundaries. + + First we define the following: + + 1. input_row_length : the number of rows from the input set + 2. output_row_length : which will be smaller than the input + 3. alpha = input_row_length / output_row_length : our reduction ratio + 4. K = floor(alpha) + 5. row_pooling_sequence : this is the result list of pool boundary rows + + Then, row_pooling_sequence should satisfy: + + 1. a[0] = 0 : the first value of the sequence is 0 + 2. a[end] = input_row_length : the last value of the sequence is the size + 3. K <= (a[i+1] - a[i]) <= K+1 : all intervals are K or K+1 size + 4. length(row_pooling_sequence) = output_row_length+1 + + For more details on fractional max pooling, see this paper: [Benjamin Graham, + Fractional Max-Pooling](http://arxiv.org/abs/1412.6071) + + Args: + value: A `Tensor`. 4-D with shape `[batch, height, width, channels]`. + pooling_ratio: A list of `floats` that has length >= 4. Pooling ratio for + each dimension of `value`, currently only supports row and col dimension + and should be >= 1.0. For example, a valid pooling ratio looks like [1.0, + 1.44, 1.73, 1.0]. The first and last elements must be 1.0 because we don't + allow pooling on batch and channels dimensions. 1.44 and 1.73 are pooling + ratio on height and width dimensions respectively. + pseudo_random: An optional `bool`. Defaults to `False`. When set to `True`, + generates the pooling sequence in a pseudorandom fashion, otherwise, in a + random fashion. Check paper [Benjamin Graham, Fractional + Max-Pooling](http://arxiv.org/abs/1412.6071) for difference between + pseudorandom and random. + overlapping: An optional `bool`. Defaults to `False`. When set to `True`, + it means when pooling, the values at the boundary of adjacent pooling + cells are used by both cells. For example: + `index 0 1 2 3 4` + `value 20 5 16 3 7` + If the pooling sequence is [0, 2, 4], then 16, at index 2 will be used + twice. The result would be [20, 16] for fractional max pooling. + seed: An optional `int`. Defaults to `0`. If set to be non-zero, the + random number generator is seeded by the given seed. Otherwise it is + seeded by a random seed. + name: A name for the operation (optional). + + Returns: + A tuple of `Tensor` objects (`output`, `row_pooling_sequence`, + `col_pooling_sequence`). + output: Output `Tensor` after fractional max pooling. Has the same type as + `value`. + row_pooling_sequence: A `Tensor` of type `int64`. + col_pooling_sequence: A `Tensor` of type `int64`. + """ + if seed == 0: + return gen_nn_ops.fractional_max_pool(value, pooling_ratio, pseudo_random, + overlapping, deterministic=False, + seed=0, seed2=0, name=name) + else: + seed1, seed2 = random_seed.get_seed(seed) + return gen_nn_ops.fractional_max_pool(value, pooling_ratio, pseudo_random, + overlapping, deterministic=True, + seed=seed1, seed2=seed2, name=name) + + +@tf_export(v1=["nn.fractional_avg_pool"]) +@deprecation.deprecated(date=None, instructions="`seed2` and `deterministic` " + "args are deprecated. Use fractional_avg_pool_v2.") +def fractional_avg_pool(value, + pooling_ratio, + pseudo_random=False, + overlapping=False, + deterministic=False, + seed=0, + seed2=0, + name=None): # pylint: disable=redefined-builtin + r"""Performs fractional average pooling on the input. + + This is a deprecated version of `fractional_avg_pool`. + + Fractional average pooling is similar to Fractional max pooling in the pooling + region generation step. The only difference is that after pooling regions are + generated, a mean operation is performed instead of a max operation in each + pooling region. + + Args: + value: A `Tensor`. 4-D with shape `[batch, height, width, channels]`. + pooling_ratio: A list of `floats` that has length >= 4. Pooling ratio for + each dimension of `value`, currently only supports row and col dimension + and should be >= 1.0. For example, a valid pooling ratio looks like [1.0, + 1.44, 1.73, 1.0]. The first and last elements must be 1.0 because we don't + allow pooling on batch and channels dimensions. 1.44 and 1.73 are pooling + ratio on height and width dimensions respectively. + pseudo_random: An optional `bool`. Defaults to `False`. When set to `True`, + generates the pooling sequence in a pseudorandom fashion, otherwise, in a + random fashion. Check paper [Benjamin Graham, Fractional + Max-Pooling](http://arxiv.org/abs/1412.6071) for difference between + pseudorandom and random. + overlapping: An optional `bool`. Defaults to `False`. When set to `True`, + it means when pooling, the values at the boundary of adjacent pooling + cells are used by both cells. For example: + `index 0 1 2 3 4` + `value 20 5 16 3 7` + If the pooling sequence is [0, 2, 4], then 16, at index 2 will be used + twice. The result would be [20, 16] for fractional avg pooling. + deterministic: An optional `bool`. Deprecated; use `fractional_avg_pool_v2` + instead. + seed: An optional `int`. Defaults to `0`. If set to be non-zero, the + random number generator is seeded by the given seed. Otherwise it is + seeded by a random seed. + seed2: An optional `int`. Deprecated; use `fractional_avg_pool_v2` instead. + name: A name for the operation (optional). + + Returns: + A tuple of `Tensor` objects (`output`, `row_pooling_sequence`, + `col_pooling_sequence`). + output: Output `Tensor` after fractional avg pooling. Has the same type as + `value`. + row_pooling_sequence: A `Tensor` of type `int64`. + col_pooling_sequence: A `Tensor` of type `int64`. + """ + return gen_nn_ops.fractional_avg_pool(value, pooling_ratio, pseudo_random, + overlapping, deterministic, seed, seed2, + name=name) + + +@tf_export("nn.fractional_avg_pool", v1=[]) +def fractional_avg_pool_v2(value, + pooling_ratio, + pseudo_random=False, + overlapping=False, + seed=0, + name=None): # pylint: disable=redefined-builtin + r"""Performs fractional average pooling on the input. + + Fractional average pooling is similar to Fractional max pooling in the pooling + region generation step. The only difference is that after pooling regions are + generated, a mean operation is performed instead of a max operation in each + pooling region. + + Args: + value: A `Tensor`. 4-D with shape `[batch, height, width, channels]`. + pooling_ratio: A list of `floats` that has length >= 4. Pooling ratio for + each dimension of `value`, currently only supports row and col dimension + and should be >= 1.0. For example, a valid pooling ratio looks like [1.0, + 1.44, 1.73, 1.0]. The first and last elements must be 1.0 because we don't + allow pooling on batch and channels dimensions. 1.44 and 1.73 are pooling + ratio on height and width dimensions respectively. + pseudo_random: An optional `bool`. Defaults to `False`. When set to `True`, + generates the pooling sequence in a pseudorandom fashion, otherwise, in a + random fashion. Check paper [Benjamin Graham, Fractional + Max-Pooling](http://arxiv.org/abs/1412.6071) for difference between + pseudorandom and random. + overlapping: An optional `bool`. Defaults to `False`. When set to `True`, + it means when pooling, the values at the boundary of adjacent pooling + cells are used by both cells. For example: + `index 0 1 2 3 4` + `value 20 5 16 3 7` + If the pooling sequence is [0, 2, 4], then 16, at index 2 will be used + twice. The result would be [20, 16] for fractional avg pooling. + seed: An optional `int`. Defaults to `0`. If set to be non-zero, the + random number generator is seeded by the given seed. Otherwise it is + seeded by a random seed. + name: A name for the operation (optional). + + Returns: + A tuple of `Tensor` objects (`output`, `row_pooling_sequence`, + `col_pooling_sequence`). + output: Output `Tensor` after fractional avg pooling. Has the same type as + `value`. + row_pooling_sequence: A `Tensor` of type `int64`. + col_pooling_sequence: A `Tensor` of type `int64`. + """ + if seed == 0: + return gen_nn_ops.fractional_avg_pool(value, pooling_ratio, pseudo_random, + overlapping, deterministic=False, + seed=0, seed2=0, name=name) + else: + seed1, seed2 = random_seed.get_seed(seed) + return gen_nn_ops.fractional_avg_pool(value, pooling_ratio, pseudo_random, + overlapping, deterministic=True, + seed=seed1, seed2=seed2, name=name) + + +@tf_export(v1=["nn.conv1d"]) @deprecation.deprecated_arg_values( None, "`NCHW` for data_format is deprecated, use `NCW` instead", @@ -2487,6 +3474,64 @@ def conv1d(value, return array_ops.squeeze(result, [spatial_start_dim]) +@tf_export("nn.conv1d", v1=[]) +def conv1d_v2(input, # pylint: disable=redefined-builtin + filters, + stride, + padding, + data_format=None, + name=None): + r"""Computes a 1-D convolution given 3-D input and filter tensors. + + Given an input tensor of shape + [batch, in_width, in_channels] + if data_format is "NWC", or + [batch, in_channels, in_width] + if data_format is "NCW", + and a filter / kernel tensor of shape + [filter_width, in_channels, out_channels], this op reshapes + the arguments to pass them to conv2d to perform the equivalent + convolution operation. + + Internally, this op reshapes the input tensors and invokes `tf.nn.conv2d`. + For example, if `data_format` does not start with "NC", a tensor of shape + [batch, in_width, in_channels] + is reshaped to + [batch, 1, in_width, in_channels], + and the filter is reshaped to + [1, filter_width, in_channels, out_channels]. + The result is then reshaped back to + [batch, out_width, out_channels] + \(where out_width is a function of the stride and padding as in conv2d\) and + returned to the caller. + + Args: + input: A 3D `Tensor`. Must be of type `float16`, `float32`, or `float64`. + filters: A 3D `Tensor`. Must have the same type as `input`. + stride: An `integer`. The number of entries by which + the filter is moved right at each step. + padding: 'SAME' or 'VALID' + data_format: An optional `string` from `"NWC", "NCW"`. Defaults + to `"NWC"`, the data is stored in the order of + [batch, in_width, in_channels]. The `"NCW"` format stores + data as [batch, in_channels, in_width]. + name: A name for the operation (optional). + + Returns: + A `Tensor`. Has the same type as input. + + Raises: + ValueError: if `data_format` is invalid. + """ + return conv1d(input, # pylint: disable=redefined-builtin + filters, + stride, + padding, + use_cudnn_on_gpu=True, + data_format=data_format, + name=name) + + def conv1d_transpose( value, filter, # pylint: disable=redefined-builtin @@ -2602,7 +3647,7 @@ def _calc_dilation2d_flops(graph, node): return ops.OpStats("flops", (output_count * filter_height * filter_width * 2)) -@tf_export("nn.erosion2d") +@tf_export(v1=["nn.erosion2d"]) def erosion2d(value, kernel, strides, rates, padding, name=None): """Computes the grayscale erosion of 4-D `value` and 3-D `kernel` tensors. @@ -2661,6 +3706,75 @@ def erosion2d(value, kernel, strides, rates, padding, name=None): name=name)) +@tf_export("nn.erosion2d", v1=[]) +def erosion2d_v2(value, + filters, + strides, + padding, + data_format, + dilations, + name=None): + """Computes the grayscale erosion of 4-D `value` and 3-D `filters` tensors. + + The `value` tensor has shape `[batch, in_height, in_width, depth]` and the + `filters` tensor has shape `[filters_height, filters_width, depth]`, i.e., + each input channel is processed independently of the others with its own + structuring function. The `output` tensor has shape + `[batch, out_height, out_width, depth]`. The spatial dimensions of the + output tensor depend on the `padding` algorithm. We currently only support the + default "NHWC" `data_format`. + + In detail, the grayscale morphological 2-D erosion is given by: + + output[b, y, x, c] = + min_{dy, dx} value[b, + strides[1] * y - dilations[1] * dy, + strides[2] * x - dilations[2] * dx, + c] - + filters[dy, dx, c] + + Duality: The erosion of `value` by the `filters` is equal to the negation of + the dilation of `-value` by the reflected `filters`. + + Args: + value: A `Tensor`. 4-D with shape `[batch, in_height, in_width, depth]`. + filters: A `Tensor`. Must have the same type as `value`. + 3-D with shape `[filters_height, filters_width, depth]`. + strides: A list of `ints` that has length `>= 4`. + 1-D of length 4. The stride of the sliding window for each dimension of + the input tensor. Must be: `[1, stride_height, stride_width, 1]`. + padding: A `string` from: `"SAME", "VALID"`. + The type of padding algorithm to use. + data_format: A `string`, only `"NHWC"` is currently supported. + dilations: A list of `ints` that has length `>= 4`. + 1-D of length 4. The input stride for atrous morphological dilation. + Must be: `[1, rate_height, rate_width, 1]`. + name: A name for the operation (optional). If not specified "erosion2d" + is used. + + Returns: + A `Tensor`. Has the same type as `value`. + 4-D with shape `[batch, out_height, out_width, depth]`. + + Raises: + ValueError: If the `value` depth does not match `filters`' shape, or if + padding is other than `'VALID'` or `'SAME'`. + """ + if data_format != "NHWC": + raise ValueError("Data formats other than NHWC are not yet supported") + + with ops.name_scope(name, "erosion2d", [value, filters]) as name: + # Reduce erosion to dilation by duality. + return math_ops.negative( + gen_nn_ops.dilation2d( + input=math_ops.negative(value), + filter=array_ops.reverse_v2(filters, [0, 1]), + strides=strides, + rates=dilations, + padding=padding, + name=name)) + + @tf_export("math.in_top_k", "nn.in_top_k") def in_top_k(predictions, targets, k, name=None): r"""Says whether the targets are in the top `K` predictions. @@ -2693,3 +3807,10 @@ def in_top_k(predictions, targets, k, name=None): """ with ops.name_scope(name, "in_top_k"): return gen_nn_ops.in_top_kv2(predictions, targets, k, name=name) + + +tf_export(v1=["nn.quantized_avg_pool"])(gen_nn_ops.quantized_avg_pool) +tf_export(v1=["nn.quantized_conv2d"])(gen_nn_ops.quantized_conv2d) +tf_export(v1=["nn.quantized_relu_x"])(gen_nn_ops.quantized_relu_x) +tf_export(v1=["nn.quantized_max_pool"])(gen_nn_ops.quantized_max_pool) + diff --git a/tensorflow/python/ops/nn_test.py b/tensorflow/python/ops/nn_test.py index 152b2020eb..0336d0d27c 100644 --- a/tensorflow/python/ops/nn_test.py +++ b/tensorflow/python/ops/nn_test.py @@ -49,36 +49,39 @@ class ZeroFractionTest(test_lib.TestCase): nonzeros = np.count_nonzero(x.flatten()) return 1.0 - nonzeros / total_elements + @test_util.run_deprecated_v1 def testZeroFraction(self): x_shape = [5, 17] x_np = np.random.randint(0, 2, size=x_shape).astype(np.float32) y_np = self._ZeroFraction(x_np) - with self.cached_session(): - x_tf = constant_op.constant(x_np) - x_tf.set_shape(x_shape) - y_tf = nn_impl.zero_fraction(x_tf) - y_tf_np = y_tf.eval() + + x_tf = constant_op.constant(x_np) + x_tf.set_shape(x_shape) + y_tf = nn_impl.zero_fraction(x_tf) + y_tf_np = self.evaluate(y_tf) + eps = 1e-8 self.assertAllClose(y_tf_np, y_np, eps) + @test_util.run_deprecated_v1 def testZeroFractionEmpty(self): - with self.cached_session(): - x = np.zeros(0) - y = nn_impl.zero_fraction(x).eval() - self.assertTrue(np.isnan(y)) + x = np.zeros(0) + y = self.evaluate(nn_impl.zero_fraction(x)) + self.assertTrue(np.isnan(y)) + @test_util.run_deprecated_v1 def testZeroFraction2_27Zeros(self): sparsity = nn_impl.zero_fraction( array_ops.zeros([int(2**27 * 1.01)], dtype=dtypes.int8)) - with self.cached_session(): - self.assertAllClose(1.0, sparsity.eval()) + self.assertAllClose(1.0, self.evaluate(sparsity)) + @test_util.run_deprecated_v1 def testZeroFraction2_27Ones(self): sparsity = nn_impl.zero_fraction( array_ops.ones([int(2**27 * 1.01)], dtype=dtypes.int8)) - with self.cached_session(): - self.assertAllClose(0.0, sparsity.eval()) + self.assertAllClose(0.0, self.evaluate(sparsity)) + @test_util.run_deprecated_v1 def testUnknownSize(self): value = array_ops.placeholder(dtype=dtypes.float32) sparsity = nn_impl.zero_fraction(value) @@ -103,8 +106,8 @@ class SoftmaxTest(test_lib.TestCase, parameterized.TestCase): x_np = np.random.randn(*x_shape).astype(np.float32) y_np = self._softmax(x_np) x_tf = constant_op.constant(x_np) - y_tf = nn_ops.softmax(x_tf) - y_tf_last_dim = nn_ops.softmax(x_tf, 1) + y_tf = nn_ops.softmax_v2(x_tf) + y_tf_last_dim = nn_ops.softmax_v2(x_tf, 1) y_tf_np = self.evaluate(y_tf) y_tf_last_dim_np = self.evaluate(y_tf_last_dim) eps = 1e-3 @@ -113,9 +116,9 @@ class SoftmaxTest(test_lib.TestCase, parameterized.TestCase): def testSoftmaxAxes(self): arr = np.linspace(0., 1, 12).reshape(3, 4) - x_neg_axis = nn_ops.softmax(arr, axis=-2) - y_pos_axis = nn_ops.softmax(arr, axis=0) - z_gt_axis = nn_ops.softmax(arr, axis=0) + x_neg_axis = nn_ops.softmax_v2(arr, axis=-2) + y_pos_axis = nn_ops.softmax_v2(arr, axis=0) + z_gt_axis = nn_ops.softmax_v2(arr, axis=0) x_neg_axis_tf = self.evaluate(x_neg_axis) y_pos_axis_tf = self.evaluate(y_pos_axis) z_gt_axis_tf = self.evaluate(z_gt_axis) @@ -124,11 +127,12 @@ class SoftmaxTest(test_lib.TestCase, parameterized.TestCase): self.assertAllClose(y_pos_axis_tf, z_gt_axis_tf, eps) @parameterized.parameters(((5, 10),), ((2, 3, 4),)) + @test_util.run_deprecated_v1 def testGradient(self, x_shape): x_np = np.random.randn(*x_shape).astype(np.float64) with self.cached_session(): x_tf = constant_op.constant(x_np) - y_tf = nn_ops.softmax(x_tf) + y_tf = nn_ops.softmax_v2(x_tf) err = gradient_checker.compute_gradient_error(x_tf, x_shape, y_tf, x_shape) eps = 2e-8 @@ -159,6 +163,7 @@ class LogPoissonLossTest(test_lib.TestCase): self.assertAllClose(y_tf_np, y_np, eps) self.assertAllClose(y_tf_np_stirling, y_np_stirling, eps) + @test_util.run_deprecated_v1 def testGradient(self): x_shape = [5, 10] x_np = np.random.randn(*x_shape).astype(np.float64) @@ -191,16 +196,16 @@ class LogSoftmaxTest(test_lib.TestCase, parameterized.TestCase): x_np = np.random.randn(*x_shape).astype(np.float32) y_np = self._log_softmax(x_np) x_tf = constant_op.constant(x_np) - y_tf = nn_ops.log_softmax(x_tf) + y_tf = nn_ops.log_softmax_v2(x_tf) y_tf_np = self.evaluate(y_tf) eps = 1e-3 self.assertAllClose(y_tf_np, y_np, eps) def testLogSoftmaxAxes(self): arr = np.linspace(0., 1, 12).reshape(3, 4) - x_neg_axis = nn_ops.log_softmax(arr, axis=-2) - y_pos_axis = nn_ops.log_softmax(arr, axis=0) - z_gt_axis = nn_ops.log_softmax(arr, axis=0) + x_neg_axis = nn_ops.log_softmax_v2(arr, axis=-2) + y_pos_axis = nn_ops.log_softmax_v2(arr, axis=0) + z_gt_axis = nn_ops.log_softmax_v2(arr, axis=0) x_neg_axis_tf = self.evaluate(x_neg_axis) y_pos_axis_tf = self.evaluate(y_pos_axis) z_gt_axis_tf = self.evaluate(z_gt_axis) @@ -209,11 +214,12 @@ class LogSoftmaxTest(test_lib.TestCase, parameterized.TestCase): self.assertAllClose(y_pos_axis_tf, z_gt_axis_tf, eps) @parameterized.parameters(((5, 10),), ((2, 3, 4),)) + @test_util.run_deprecated_v1 def testGradient(self, x_shape): x_np = np.random.randn(*x_shape).astype(np.float64) with self.cached_session(): x_tf = constant_op.constant(x_np) - y_tf = nn_ops.log_softmax(x_tf) + y_tf = nn_ops.log_softmax_v2(x_tf) err = gradient_checker.compute_gradient_error(x_tf, x_shape, y_tf, x_shape) eps = 1e-7 @@ -231,6 +237,7 @@ class L2LossTest(test_lib.TestCase): value = self.evaluate(l2loss) self.assertAllClose(7.0, value) + @test_util.run_deprecated_v1 def testGradient(self): x_shape = [20, 7, 3] np.random.seed(1) # Make it reproducible. @@ -264,7 +271,7 @@ class L2NormalizeTest(test_lib.TestCase): for dim in range(len(x_shape)): y_np = self._l2Normalize(x_np, dim) x_tf = constant_op.constant(x_np, name="x") - y_tf = nn_impl.l2_normalize(x_tf, dim) + y_tf = nn_impl.l2_normalize_v2(x_tf, dim) self.assertAllClose(y_np, self.evaluate(y_tf)) @test_util.run_in_graph_and_eager_modes @@ -275,9 +282,10 @@ class L2NormalizeTest(test_lib.TestCase): dim = [1, 2] y_np = self._l2Normalize(x_np, dim) x_tf = constant_op.constant(x_np, name="x") - y_tf = nn_impl.l2_normalize(x_tf, dim) + y_tf = nn_impl.l2_normalize_v2(x_tf, dim) self.assertAllClose(y_np, self.evaluate(y_tf)) + @test_util.run_deprecated_v1 def testL2NormalizeGradient(self): x_shape = [20, 7, 3] np.random.seed(1) @@ -285,7 +293,7 @@ class L2NormalizeTest(test_lib.TestCase): for dim in range(len(x_shape)): with self.cached_session(): x_tf = constant_op.constant(x_np, name="x") - y_tf = nn_impl.l2_normalize(x_tf, dim) + y_tf = nn_impl.l2_normalize_v2(x_tf, dim) err = gradient_checker.compute_gradient_error(x_tf, x_shape, y_tf, x_shape) print("L2Normalize gradient err = %g " % err) @@ -302,19 +310,18 @@ class DropoutTest(test_lib.TestCase): y_dim = 30 num_iter = 10 for keep_prob in [0.1, 0.5, 0.8]: - with self.cached_session(): - t = constant_op.constant( - 1.0, shape=[x_dim, y_dim], dtype=dtypes.float32) - dropout = nn_ops.dropout(t, keep_prob) - final_count = 0 - self.assertEqual([x_dim, y_dim], dropout.get_shape()) - for _ in xrange(0, num_iter): - value = dropout.eval() - final_count += np.count_nonzero(value) - # Verifies that there are only two values: 0 and 1/keep_prob. - sorted_value = np.unique(np.sort(value)) - self.assertEqual(0, sorted_value[0]) - self.assertAllClose(1 / keep_prob, sorted_value[1]) + t = constant_op.constant(1.0, shape=[x_dim, y_dim], dtype=dtypes.float32) + dropout = nn_ops.dropout(t, keep_prob) + final_count = 0 + self.assertEqual([x_dim, y_dim], dropout.get_shape()) + for _ in xrange(0, num_iter): + value = self.evaluate(dropout) + final_count += np.count_nonzero(value) + # Verifies that there are only two values: 0 and 1/keep_prob. + sorted_value = np.unique(np.sort(value)) + self.assertEqual(0, sorted_value[0]) + self.assertAllClose(1 / keep_prob, sorted_value[1]) + # Check that we are in the 15% error range expected_count = x_dim * y_dim * keep_prob * num_iter rel_error = math.fabs(final_count - expected_count) / expected_count @@ -330,19 +337,18 @@ class DropoutTest(test_lib.TestCase): y_dim = 3 num_iter = 10 for keep_prob in [0.1, 0.5, 0.8]: - with self.cached_session(): - t = constant_op.constant( - 1.0, shape=[x_dim, y_dim], dtype=dtypes.float32) - dropout = nn_ops.dropout(t, keep_prob, noise_shape=[x_dim, 1]) - self.assertEqual([x_dim, y_dim], dropout.get_shape()) - final_count = 0 - for _ in xrange(0, num_iter): - value = dropout.eval() - final_count += np.count_nonzero(value) - # Verifies that there are only two values: 0 and 1/keep_prob. - sorted_value = np.unique(np.sort(value)) - self.assertEqual(0, sorted_value[0]) - self.assertAllClose(1 / keep_prob, sorted_value[1]) + t = constant_op.constant(1.0, shape=[x_dim, y_dim], dtype=dtypes.float32) + dropout = nn_ops.dropout(t, keep_prob, noise_shape=[x_dim, 1]) + self.assertEqual([x_dim, y_dim], dropout.get_shape()) + final_count = 0 + for _ in xrange(0, num_iter): + value = self.evaluate(dropout) + final_count += np.count_nonzero(value) + # Verifies that there are only two values: 0 and 1/keep_prob. + sorted_value = np.unique(np.sort(value)) + self.assertEqual(0, sorted_value[0]) + self.assertAllClose(1 / keep_prob, sorted_value[1]) + # Check that we are in the 15% error range expected_count = x_dim * y_dim * keep_prob * num_iter rel_error = math.fabs(final_count - expected_count) / expected_count @@ -355,18 +361,17 @@ class DropoutTest(test_lib.TestCase): y_dim = 30 num_iter = 10 for keep_prob in [0.1, 0.5, 0.8]: - with self.cached_session(): - t = constant_op.constant( - 1.0, shape=[x_dim, y_dim], dtype=dtypes.float32) - dropout = nn_ops.dropout(t, keep_prob, noise_shape=[x_dim, 1]) - self.assertEqual([x_dim, y_dim], dropout.get_shape()) - for _ in xrange(0, num_iter): - value = dropout.eval() - # Verifies that each y column as only one type of activation. - for i in xrange(x_dim): - sorted_value = np.unique(np.sort(value[i, :])) - self.assertEqual(sorted_value.size, 1) - + t = constant_op.constant(1.0, shape=[x_dim, y_dim], dtype=dtypes.float32) + dropout = nn_ops.dropout(t, keep_prob, noise_shape=[x_dim, 1]) + self.assertEqual([x_dim, y_dim], dropout.get_shape()) + for _ in xrange(0, num_iter): + value = self.evaluate(dropout) + # Verifies that each y column as only one type of activation. + for i in xrange(x_dim): + sorted_value = np.unique(np.sort(value[i, :])) + self.assertEqual(sorted_value.size, 1) + + @test_util.run_deprecated_v1 def testDropoutPlaceholderKeepProb(self): # Runs dropout with 0-1 tensor 10 times, sum the number of ones and validate # that it is producing approximately the right number of ones over a large @@ -395,6 +400,7 @@ class DropoutTest(test_lib.TestCase): print(rel_error) self.assertTrue(rel_error < 0.15) + @test_util.run_deprecated_v1 def testShapedDropoutUnknownShape(self): x_dim = 40 y_dim = 30 @@ -409,26 +415,26 @@ class DropoutTest(test_lib.TestCase): y_dim = 3 num_iter = 10 for keep_prob in [0.1, 0.5, 0.8]: - with self.cached_session(): - t = constant_op.constant( - 1.0, shape=[x_dim, y_dim], dtype=dtypes.float32) - # Set noise_shape=[None, 1] which means [x_dim, 1]. - dropout = nn_ops.dropout(t, keep_prob, noise_shape=[None, 1]) - self.assertEqual([x_dim, y_dim], dropout.get_shape()) - final_count = 0 - for _ in xrange(0, num_iter): - value = dropout.eval() - final_count += np.count_nonzero(value) - # Verifies that there are only two values: 0 and 1/keep_prob. - sorted_value = np.unique(np.sort(value)) - self.assertEqual(0, sorted_value[0]) - self.assertAllClose(1 / keep_prob, sorted_value[1]) + t = constant_op.constant(1.0, shape=[x_dim, y_dim], dtype=dtypes.float32) + # Set noise_shape=[None, 1] which means [x_dim, 1]. + dropout = nn_ops.dropout(t, keep_prob, noise_shape=[None, 1]) + self.assertEqual([x_dim, y_dim], dropout.get_shape()) + final_count = 0 + for _ in xrange(0, num_iter): + value = self.evaluate(dropout) + final_count += np.count_nonzero(value) + # Verifies that there are only two values: 0 and 1/keep_prob. + sorted_value = np.unique(np.sort(value)) + self.assertEqual(0, sorted_value[0]) + self.assertAllClose(1 / keep_prob, sorted_value[1]) + # Check that we are in the 15% error range expected_count = x_dim * y_dim * keep_prob * num_iter rel_error = math.fabs(final_count - expected_count) / expected_count print(rel_error) self.assertTrue(rel_error < 0.15) + @test_util.run_deprecated_v1 def testInvalidKeepProb(self): x_dim = 40 y_dim = 30 @@ -444,6 +450,18 @@ class DropoutTest(test_lib.TestCase): with self.assertRaises(ValueError): nn_ops.dropout(t, array_ops.placeholder(dtypes.float32, shape=[2])) + def testInvalidRate(self): + x_dim = 40 + y_dim = 30 + t = constant_op.constant(1.0, shape=[x_dim, y_dim], dtype=dtypes.float32) + with self.assertRaises(ValueError): + nn_ops.dropout_v2(t, -1.0) + with self.assertRaises(ValueError): + nn_ops.dropout_v2(t, 1.1) + with self.assertRaises(ValueError): + nn_ops.dropout_v2(t, [0.0, 1.0]) + + @test_util.run_deprecated_v1 def testShapedDropoutShapeError(self): # Runs shaped dropout and verifies an error is thrown on misshapen noise. x_dim = 40 @@ -466,9 +484,11 @@ class DropoutTest(test_lib.TestCase): def testNoDropoutFast(self): x = array_ops.zeros((5,)) - for p in 1, constant_op.constant(1.0): - y = nn_ops.dropout(x, keep_prob=p) - self.assertTrue(x is y) + y = nn_ops.dropout(x, keep_prob=1) + self.assertTrue(x is y) + + y = nn_ops.dropout_v2(x, rate=0) + self.assertTrue(x is y) def testDropoutWithIntegerInputs(self): x = constant_op.constant([1, 1, 1, 1, 1]) @@ -563,78 +583,78 @@ class ComputeSampledLogitsTest(test_lib.TestCase): initializer=constant_op.constant(biases)) with self.session(graph=g) as sess: variables.global_variables_initializer().run() - return sess.run([list(sharded_weights), list(sharded_biases)]) + return self.evaluate([list(sharded_weights), list(sharded_biases)]) def testShapes(self): np.random.seed(0) num_classes = 5 batch_size = 3 - with self.cached_session() as sess: - for num_true in range(1, 5): - labels = np.random.randint( - low=0, high=num_classes, size=batch_size * num_true) - (weights, biases, hidden_acts, sampled_vals, exp_logits, - exp_labels) = self._GenerateTestData( - num_classes=num_classes, - dim=10, - batch_size=batch_size, - num_true=num_true, - labels=labels, - sampled=[1, 0, 2, 3], - subtract_log_q=False) - logits_tensor, labels_tensor = _compute_sampled_logits( - weights=constant_op.constant(weights), - biases=constant_op.constant(biases), - labels=constant_op.constant( - labels, dtype=dtypes.int64, shape=(batch_size, num_true)), - inputs=constant_op.constant(hidden_acts), - num_sampled=4, - num_classes=num_classes, - num_true=num_true, - sampled_values=sampled_vals, - subtract_log_q=False, - remove_accidental_hits=False, - partition_strategy="div", - name="sampled_logits_basic_num_true_%d" % num_true) - got_logits, got_labels = sess.run([logits_tensor, labels_tensor]) - self.assertEqual(exp_logits.shape, got_logits.shape, self._eps) - self.assertEqual(exp_labels.shape, got_labels.shape, self._eps) + + for num_true in range(1, 5): + labels = np.random.randint( + low=0, high=num_classes, size=batch_size * num_true) + (weights, biases, hidden_acts, sampled_vals, exp_logits, + exp_labels) = self._GenerateTestData( + num_classes=num_classes, + dim=10, + batch_size=batch_size, + num_true=num_true, + labels=labels, + sampled=[1, 0, 2, 3], + subtract_log_q=False) + logits_tensor, labels_tensor = _compute_sampled_logits( + weights=constant_op.constant(weights), + biases=constant_op.constant(biases), + labels=constant_op.constant( + labels, dtype=dtypes.int64, shape=(batch_size, num_true)), + inputs=constant_op.constant(hidden_acts), + num_sampled=4, + num_classes=num_classes, + num_true=num_true, + sampled_values=sampled_vals, + subtract_log_q=False, + remove_accidental_hits=False, + partition_strategy="div", + name="sampled_logits_basic_num_true_%d" % num_true) + got_logits, got_labels = self.evaluate([logits_tensor, labels_tensor]) + self.assertEqual(exp_logits.shape, got_logits.shape, self._eps) + self.assertEqual(exp_labels.shape, got_labels.shape, self._eps) def testBasic(self): """Without accidental hit removal or subtract_log_q.""" np.random.seed(0) num_classes = 5 batch_size = 3 - with self.cached_session() as sess: - for num_true in range(1, 5): - labels = np.random.randint( - low=0, high=num_classes, size=batch_size * num_true) - (weights, biases, hidden_acts, sampled_vals, exp_logits, - exp_labels) = self._GenerateTestData( - num_classes=num_classes, - dim=10, - batch_size=batch_size, - num_true=num_true, - labels=labels, - sampled=[1, 0, 2, 3], - subtract_log_q=False) - logits_tensor, labels_tensor = _compute_sampled_logits( - weights=constant_op.constant(weights), - biases=constant_op.constant(biases), - labels=constant_op.constant( - labels, dtype=dtypes.int64, shape=(batch_size, num_true)), - inputs=constant_op.constant(hidden_acts), - num_sampled=4, - num_classes=num_classes, - num_true=num_true, - sampled_values=sampled_vals, - subtract_log_q=False, - remove_accidental_hits=False, - partition_strategy="div", - name="sampled_logits_basic_num_true_%d" % num_true) - got_logits, got_labels = sess.run([logits_tensor, labels_tensor]) - self.assertAllClose(exp_logits, got_logits, self._eps) - self.assertAllClose(exp_labels, got_labels, self._eps) + + for num_true in range(1, 5): + labels = np.random.randint( + low=0, high=num_classes, size=batch_size * num_true) + (weights, biases, hidden_acts, sampled_vals, exp_logits, + exp_labels) = self._GenerateTestData( + num_classes=num_classes, + dim=10, + batch_size=batch_size, + num_true=num_true, + labels=labels, + sampled=[1, 0, 2, 3], + subtract_log_q=False) + logits_tensor, labels_tensor = _compute_sampled_logits( + weights=constant_op.constant(weights), + biases=constant_op.constant(biases), + labels=constant_op.constant( + labels, dtype=dtypes.int64, shape=(batch_size, num_true)), + inputs=constant_op.constant(hidden_acts), + num_sampled=4, + num_classes=num_classes, + num_true=num_true, + sampled_values=sampled_vals, + subtract_log_q=False, + remove_accidental_hits=False, + partition_strategy="div", + name="sampled_logits_basic_num_true_%d" % num_true) + got_logits, got_labels = self.evaluate([logits_tensor, labels_tensor]) + self.assertAllClose(exp_logits, got_logits, self._eps) + self.assertAllClose(exp_labels, got_labels, self._eps) def testAccidentalHitRemoval(self): """With accidental hit removal, no subtract_log_q.""" @@ -642,118 +662,118 @@ class ComputeSampledLogitsTest(test_lib.TestCase): num_classes = 5 batch_size = 3 sampled = [1, 0, 2, 3] - with self.cached_session(): - for num_true in range(1, 5): - labels = np.random.randint( - low=0, high=num_classes, size=batch_size * num_true) - (weights, biases, hidden_acts, sampled_vals, _, - _) = self._GenerateTestData( - num_classes=num_classes, - dim=10, - batch_size=batch_size, - num_true=num_true, - labels=labels, - sampled=sampled, - subtract_log_q=False) - logits_tensor, _ = _compute_sampled_logits( - weights=constant_op.constant(weights), - biases=constant_op.constant(biases), - labels=constant_op.constant( - labels, dtype=dtypes.int64, shape=(batch_size, num_true)), - inputs=constant_op.constant(hidden_acts), - num_sampled=len(sampled), - num_classes=num_classes, - num_true=num_true, - sampled_values=sampled_vals, - subtract_log_q=False, - remove_accidental_hits=True, - partition_strategy="div", - name="sampled_logits_accidental_hit_removal_num_true_%d" % num_true) - # Test that the exponentiated logits of accidental hits are near 0. - # First we need to find the hits in this random test run: - labels_reshape = labels.reshape((batch_size, num_true)) - got_logits = logits_tensor.eval() - for row in xrange(batch_size): - row_labels = labels_reshape[row, :] - for col in xrange(len(sampled)): - if sampled[col] in row_labels: - # We need to add the num_true_test offset into logits_* - self.assertNear( - np.exp(got_logits[row, col + num_true]), 0., self._eps) + + for num_true in range(1, 5): + labels = np.random.randint( + low=0, high=num_classes, size=batch_size * num_true) + (weights, biases, hidden_acts, sampled_vals, _, + _) = self._GenerateTestData( + num_classes=num_classes, + dim=10, + batch_size=batch_size, + num_true=num_true, + labels=labels, + sampled=sampled, + subtract_log_q=False) + logits_tensor, _ = _compute_sampled_logits( + weights=constant_op.constant(weights), + biases=constant_op.constant(biases), + labels=constant_op.constant( + labels, dtype=dtypes.int64, shape=(batch_size, num_true)), + inputs=constant_op.constant(hidden_acts), + num_sampled=len(sampled), + num_classes=num_classes, + num_true=num_true, + sampled_values=sampled_vals, + subtract_log_q=False, + remove_accidental_hits=True, + partition_strategy="div", + name="sampled_logits_accidental_hit_removal_num_true_%d" % num_true) + # Test that the exponentiated logits of accidental hits are near 0. + # First we need to find the hits in this random test run: + labels_reshape = labels.reshape((batch_size, num_true)) + got_logits = self.evaluate(logits_tensor) + for row in xrange(batch_size): + row_labels = labels_reshape[row, :] + for col in xrange(len(sampled)): + if sampled[col] in row_labels: + # We need to add the num_true_test offset into logits_* + self.assertNear( + np.exp(got_logits[row, col + num_true]), 0., self._eps) def testSubtractLogQ(self): """With subtract_log_q, no accidental hit removal.""" np.random.seed(0) num_classes = 5 batch_size = 3 - with self.cached_session() as sess: - for num_true in range(1, 5): - labels = np.random.randint( - low=0, high=num_classes, size=batch_size * num_true) - (weights, biases, hidden_acts, sampled_vals, exp_logits, - exp_labels) = self._GenerateTestData( - num_classes=num_classes, - dim=10, - batch_size=batch_size, - num_true=num_true, - labels=labels, - sampled=[1, 0, 2, 3], - subtract_log_q=True) - logits_tensor, labels_tensor = _compute_sampled_logits( - weights=constant_op.constant(weights), - biases=constant_op.constant(biases), - labels=constant_op.constant( - labels, dtype=dtypes.int64, shape=(batch_size, num_true)), - inputs=constant_op.constant(hidden_acts), - num_sampled=4, - num_classes=num_classes, - num_true=num_true, - sampled_values=sampled_vals, - subtract_log_q=True, - remove_accidental_hits=False, - partition_strategy="div", - name="sampled_logits_subtract_log_q_num_true_%d" % num_true) - got_logits, got_labels = sess.run([logits_tensor, labels_tensor]) - self.assertAllClose(exp_logits, got_logits, self._eps) - self.assertAllClose(exp_labels, got_labels, self._eps) + + for num_true in range(1, 5): + labels = np.random.randint( + low=0, high=num_classes, size=batch_size * num_true) + (weights, biases, hidden_acts, sampled_vals, exp_logits, + exp_labels) = self._GenerateTestData( + num_classes=num_classes, + dim=10, + batch_size=batch_size, + num_true=num_true, + labels=labels, + sampled=[1, 0, 2, 3], + subtract_log_q=True) + logits_tensor, labels_tensor = _compute_sampled_logits( + weights=constant_op.constant(weights), + biases=constant_op.constant(biases), + labels=constant_op.constant( + labels, dtype=dtypes.int64, shape=(batch_size, num_true)), + inputs=constant_op.constant(hidden_acts), + num_sampled=4, + num_classes=num_classes, + num_true=num_true, + sampled_values=sampled_vals, + subtract_log_q=True, + remove_accidental_hits=False, + partition_strategy="div", + name="sampled_logits_subtract_log_q_num_true_%d" % num_true) + got_logits, got_labels = self.evaluate([logits_tensor, labels_tensor]) + self.assertAllClose(exp_logits, got_logits, self._eps) + self.assertAllClose(exp_labels, got_labels, self._eps) def testSharded(self): """With sharded weights and sharded biases.""" np.random.seed(0) num_classes = 5 batch_size = 3 - with self.cached_session() as sess: - for num_true in range(1, 5): - labels = np.random.randint( - low=0, high=num_classes, size=batch_size * num_true) - (weights, biases, hidden_acts, sampled_vals, exp_logits, - exp_labels) = self._GenerateTestData( - num_classes=num_classes, - dim=10, - batch_size=batch_size, - num_true=num_true, - labels=labels, - sampled=[1, 0, 2, 3], - subtract_log_q=False) - weight_shards, bias_shards = self._ShardTestEmbeddings( - weights, biases, num_shards=3) - logits_tensor, labels_tensor = _compute_sampled_logits( - weights=[constant_op.constant(shard) for shard in weight_shards], - biases=[constant_op.constant(shard) for shard in bias_shards], - labels=constant_op.constant( - labels, dtype=dtypes.int64, shape=(batch_size, num_true)), - inputs=constant_op.constant(hidden_acts), - num_sampled=4, - num_classes=num_classes, - num_true=num_true, - sampled_values=sampled_vals, - subtract_log_q=False, - remove_accidental_hits=False, - partition_strategy="div", - name="sampled_logits_sharded_num_true_%d" % num_true) - got_logits, got_labels = sess.run([logits_tensor, labels_tensor]) - self.assertAllClose(exp_logits, got_logits, self._eps) - self.assertAllClose(exp_labels, got_labels, self._eps) + + for num_true in range(1, 5): + labels = np.random.randint( + low=0, high=num_classes, size=batch_size * num_true) + (weights, biases, hidden_acts, sampled_vals, exp_logits, + exp_labels) = self._GenerateTestData( + num_classes=num_classes, + dim=10, + batch_size=batch_size, + num_true=num_true, + labels=labels, + sampled=[1, 0, 2, 3], + subtract_log_q=False) + weight_shards, bias_shards = self._ShardTestEmbeddings( + weights, biases, num_shards=3) + logits_tensor, labels_tensor = _compute_sampled_logits( + weights=[constant_op.constant(shard) for shard in weight_shards], + biases=[constant_op.constant(shard) for shard in bias_shards], + labels=constant_op.constant( + labels, dtype=dtypes.int64, shape=(batch_size, num_true)), + inputs=constant_op.constant(hidden_acts), + num_sampled=4, + num_classes=num_classes, + num_true=num_true, + sampled_values=sampled_vals, + subtract_log_q=False, + remove_accidental_hits=False, + partition_strategy="div", + name="sampled_logits_sharded_num_true_%d" % num_true) + got_logits, got_labels = self.evaluate([logits_tensor, labels_tensor]) + self.assertAllClose(exp_logits, got_logits, self._eps) + self.assertAllClose(exp_labels, got_labels, self._eps) def testNCELoss(self): # A simple test to verify the numerics. @@ -782,35 +802,32 @@ class ComputeSampledLogitsTest(test_lib.TestCase): exp_nce_loss = np.sum( _SigmoidCrossEntropyWithLogits(exp_logits, exp_labels), 1) - with self.cached_session(): - got_nce_loss = nn_impl.nce_loss( - weights=constant_op.constant(weights), - biases=constant_op.constant(biases), - labels=constant_op.constant(labels, shape=(batch_size, 1)), - inputs=constant_op.constant(hidden_acts), - num_sampled=4, - num_classes=num_classes, - num_true=1, - sampled_values=sampled_vals, - partition_strategy="div") - - self.assertAllClose(exp_nce_loss, got_nce_loss.eval(), 1e-4) - - # Test with sharded weights and sharded biases. - weight_shards, bias_shards = self._ShardTestEmbeddings( - weights, biases, num_shards=3) - got_nce_loss = nn_impl.nce_loss( - weights=[constant_op.constant(shard) for shard in weight_shards], - biases=[constant_op.constant(shard) for shard in bias_shards], - labels=constant_op.constant(labels, shape=(batch_size, 1)), - inputs=constant_op.constant(hidden_acts), - num_sampled=4, - num_classes=num_classes, - num_true=1, - sampled_values=sampled_vals, - partition_strategy="div") - - self.assertAllClose(exp_nce_loss, got_nce_loss.eval(), 1e-4) + got_nce_loss = nn_impl.nce_loss_v2( + weights=constant_op.constant(weights), + biases=constant_op.constant(biases), + labels=constant_op.constant(labels, shape=(batch_size, 1)), + inputs=constant_op.constant(hidden_acts), + num_sampled=4, + num_classes=num_classes, + num_true=1, + sampled_values=sampled_vals) + + self.assertAllClose(exp_nce_loss, self.evaluate(got_nce_loss), 1e-4) + + # Test with sharded weights and sharded biases. + weight_shards, bias_shards = self._ShardTestEmbeddings( + weights, biases, num_shards=3) + got_nce_loss = nn_impl.nce_loss_v2( + weights=[constant_op.constant(shard) for shard in weight_shards], + biases=[constant_op.constant(shard) for shard in bias_shards], + labels=constant_op.constant(labels, shape=(batch_size, 1)), + inputs=constant_op.constant(hidden_acts), + num_sampled=4, + num_classes=num_classes, + num_true=1, + sampled_values=sampled_vals) + + self.assertAllClose(exp_nce_loss, self.evaluate(got_nce_loss), 1e-4) def testSampledSoftmaxLoss(self): # A simple test to verify the numerics. @@ -839,39 +856,38 @@ class ComputeSampledLogitsTest(test_lib.TestCase): exp_sampled_softmax_loss = _SoftmaxCrossEntropyWithLogits( exp_logits, exp_labels) - with self.cached_session(): - got_sampled_softmax_loss = nn_impl.sampled_softmax_loss( - weights=constant_op.constant(weights), - biases=constant_op.constant(biases), - labels=constant_op.constant(labels, shape=(batch_size, 1)), - inputs=constant_op.constant(hidden_acts), - num_sampled=4, - num_classes=num_classes, - num_true=1, - sampled_values=sampled_vals, - remove_accidental_hits=False, - partition_strategy="div") - - self.assertAllClose(exp_sampled_softmax_loss, - got_sampled_softmax_loss.eval(), 1e-4) - - # Test with sharded weights and sharded biases. - weight_shards, bias_shards = self._ShardTestEmbeddings( - weights, biases, num_shards=3) - got_sampled_softmax_loss = nn_impl.sampled_softmax_loss( - weights=[constant_op.constant(shard) for shard in weight_shards], - biases=[constant_op.constant(shard) for shard in bias_shards], - labels=constant_op.constant(labels, shape=(batch_size, 1)), - inputs=constant_op.constant(hidden_acts), - num_sampled=4, - num_classes=num_classes, - num_true=1, - sampled_values=sampled_vals, - remove_accidental_hits=False, - partition_strategy="div") - - self.assertAllClose(exp_sampled_softmax_loss, - got_sampled_softmax_loss.eval(), 1e-4) + got_sampled_softmax_loss = nn_impl.sampled_softmax_loss( + weights=constant_op.constant(weights), + biases=constant_op.constant(biases), + labels=constant_op.constant(labels, shape=(batch_size, 1)), + inputs=constant_op.constant(hidden_acts), + num_sampled=4, + num_classes=num_classes, + num_true=1, + sampled_values=sampled_vals, + remove_accidental_hits=False, + partition_strategy="div") + + self.assertAllClose(exp_sampled_softmax_loss, + self.evaluate(got_sampled_softmax_loss), 1e-4) + + # Test with sharded weights and sharded biases. + weight_shards, bias_shards = self._ShardTestEmbeddings( + weights, biases, num_shards=3) + got_sampled_softmax_loss = nn_impl.sampled_softmax_loss( + weights=[constant_op.constant(shard) for shard in weight_shards], + biases=[constant_op.constant(shard) for shard in bias_shards], + labels=constant_op.constant(labels, shape=(batch_size, 1)), + inputs=constant_op.constant(hidden_acts), + num_sampled=4, + num_classes=num_classes, + num_true=1, + sampled_values=sampled_vals, + remove_accidental_hits=False, + partition_strategy="div") + + self.assertAllClose(exp_sampled_softmax_loss, + self.evaluate(got_sampled_softmax_loss), 1e-4) def testSampledSoftmaxLossBf16(self): # A simple test to verify the numerics for bfloat16. @@ -900,29 +916,30 @@ class ComputeSampledLogitsTest(test_lib.TestCase): exp_sampled_softmax_loss = _SoftmaxCrossEntropyWithLogits( exp_logits, exp_labels) - with self.cached_session(): - true_exp_bf16 = np.full( - [batch_size, 1], fill_value=0.5, dtype=dtypes.bfloat16.as_numpy_dtype) - sampled_exp_bf16 = np.full( - [len(sampled)], fill_value=0.5, dtype=dtypes.bfloat16.as_numpy_dtype) - sampled_vals_bf16 = (sampled, true_exp_bf16, sampled_exp_bf16) - - got_sampled_softmax_loss = math_ops.cast( - nn_impl.sampled_softmax_loss( - weights=constant_op.constant(weights, dtype=dtypes.bfloat16), - biases=constant_op.constant(biases, dtype=dtypes.bfloat16), - labels=constant_op.constant( - labels, shape=(batch_size, 1), dtype=dtypes.bfloat16), - inputs=constant_op.constant(hidden_acts, dtype=dtypes.bfloat16), - num_sampled=4, - num_classes=num_classes, - num_true=1, - sampled_values=sampled_vals_bf16, - remove_accidental_hits=False, - partition_strategy="div"), dtypes.float32) - - self.assertAllClose(exp_sampled_softmax_loss, - got_sampled_softmax_loss.eval(), 1e-1) + true_exp_bf16 = np.full([batch_size, 1], + fill_value=0.5, + dtype=dtypes.bfloat16.as_numpy_dtype) + sampled_exp_bf16 = np.full([len(sampled)], + fill_value=0.5, + dtype=dtypes.bfloat16.as_numpy_dtype) + sampled_vals_bf16 = (sampled, true_exp_bf16, sampled_exp_bf16) + + got_sampled_softmax_loss = math_ops.cast( + nn_impl.sampled_softmax_loss( + weights=constant_op.constant(weights, dtype=dtypes.bfloat16), + biases=constant_op.constant(biases, dtype=dtypes.bfloat16), + labels=constant_op.constant( + labels, shape=(batch_size, 1), dtype=dtypes.bfloat16), + inputs=constant_op.constant(hidden_acts, dtype=dtypes.bfloat16), + num_sampled=4, + num_classes=num_classes, + num_true=1, + sampled_values=sampled_vals_bf16, + remove_accidental_hits=False, + partition_strategy="div"), dtypes.float32) + + self.assertAllClose(exp_sampled_softmax_loss, + self.evaluate(got_sampled_softmax_loss), 1e-1) class CReluTest(test_lib.TestCase): @@ -931,9 +948,9 @@ class CReluTest(test_lib.TestCase): np.random.seed(1) # Make it reproducible. x = np.random.randn(3, 4).astype(np.float32) y = np.concatenate([x * (x > 0), -x * (x < 0)], axis=1) - with self.cached_session(): - z = nn_ops.crelu(constant_op.constant(x)).eval() - self.assertAllClose(y, z, 1e-4) + + z = self.evaluate(nn_ops.crelu(constant_op.constant(x))) + self.assertAllClose(y, z, 1e-4) class ReluTest(test_lib.TestCase): @@ -942,10 +959,11 @@ class ReluTest(test_lib.TestCase): np.random.seed(1) # Make it reproducible. x = np.random.randn(3, 4).astype(np.float32) y = np.maximum(x, 0.0) - with self.cached_session(): - z = nn_ops.relu(constant_op.constant(x)).eval() - self.assertAllEqual(y, z) + z = self.evaluate(nn_ops.relu(constant_op.constant(x))) + self.assertAllEqual(y, z) + + @test_util.run_deprecated_v1 def testNaNs(self): # Test that relu(nan) = nan for various sizes. for i in range(18): @@ -967,22 +985,26 @@ class LeakyReluTest(test_lib.TestCase): outputs = nn_ops.leaky_relu(inputs) self.assertEquals(inputs.shape, outputs.shape) - with self.cached_session() as sess: - inputs, outputs = sess.run([inputs, outputs]) + + inputs, outputs = self.evaluate([inputs, outputs]) + self.assertGreaterEqual(outputs.min(), 0.0) self.assertLessEqual(outputs.max(), 1.0) self.assertAllClose(inputs, outputs) + @test_util.run_deprecated_v1 def testValues(self): for dtype in [np.int32, np.int64, np.float16, np.float32, np.float64]: np_values = np.array([-2, -1, 0, 1, 2], dtype=dtype) outputs = nn_ops.leaky_relu(constant_op.constant(np_values)) - with self.cached_session() as sess: - outputs = sess.run(outputs) + + outputs = self.evaluate(outputs) + tol = 2e-3 if dtype == np.float16 else 1e-6 self.assertAllClose( outputs, [-0.4, -0.2, 0.0, 1.0, 2.0], rtol=tol, atol=tol) + @test_util.run_deprecated_v1 def testName(self): np_values = np.array([-2, -1, 0, 1, 2], dtype=np.float64) outputs_with_name_set = nn_ops.leaky_relu( @@ -996,6 +1018,7 @@ class LeakyReluTest(test_lib.TestCase): class SwishTest(test_lib.TestCase): + @test_util.run_deprecated_v1 def testValues(self): np_values = np.array( [np.linspace(-10.0, 0.0, 100), @@ -1004,11 +1027,13 @@ class SwishTest(test_lib.TestCase): tf_values = constant_op.constant(np_values) actual_tf_outputs = nn_impl.swish(tf_values) expected_tf_outputs = tf_values * math_ops.sigmoid(tf_values) - with self.cached_session() as sess: - actual_outputs, expected_outputs = sess.run( - [actual_tf_outputs, expected_tf_outputs]) + + actual_outputs, expected_outputs = self.evaluate( + [actual_tf_outputs, expected_tf_outputs]) + self.assertAllClose(actual_outputs, expected_outputs) + @test_util.run_deprecated_v1 def testGradients(self): shape = [5, 3, 4] sigma = 5 @@ -1039,8 +1064,8 @@ class MomentsTest(test_lib.TestCase): with self.session(graph=g) as sess: inputs = constant_op.constant( input_values, shape=input_shape, dtype=dtypes.float32) - mean, variance = nn_impl.moments( - inputs, moments_axes, keep_dims=keep_dims) + mean, variance = nn_impl.moments_v2( + inputs, moments_axes, keepdims=keep_dims) if check_gradients: err = gradient_checker.compute_gradient_error( @@ -1051,7 +1076,7 @@ class MomentsTest(test_lib.TestCase): self.assertLess(err, 1e-3) # Evaluate. - [mean, variance] = sess.run([mean, variance]) + [mean, variance] = self.evaluate([mean, variance]) # Make sure that there are no NaNs self.assertFalse(np.isnan(mean).any()) self.assertFalse(np.isnan(variance).any()) @@ -1094,9 +1119,9 @@ class DataFormatDimMapTest(test_lib.TestCase): def _test(self, x_val, y_val_expected): x = constant_op.constant(x_val) y = nn_ops.data_format_dim_map(x) - with self.cached_session(use_gpu=test_lib.is_gpu_available()) as sess: - y_val = sess.run(y) - self.assertAllEqual(y_val, y_val_expected) + + y_val = self.evaluate(y) + self.assertAllEqual(y_val, y_val_expected) def test(self): self._test(0, 0) @@ -1117,8 +1142,8 @@ class DataFormatDimMapTest(test_lib.TestCase): y_val_expected = [2, 2, 3] x = constant_op.constant(x_val) y = nn_ops.data_format_dim_map(x, src_format="NHWC", dst_format="NCHW") - with self.session(use_gpu=test_lib.is_gpu_available()) as sess: - y_val = sess.run(y) + with test_util.use_gpu(): + y_val = self.evaluate(y) self.assertAllEqual(y_val, y_val_expected) def testNHWCtoHWNC(self): @@ -1126,8 +1151,8 @@ class DataFormatDimMapTest(test_lib.TestCase): y_val_expected = [2, 0, 1, 3, 2, 0, 1, 3] x = constant_op.constant(x_val) y = nn_ops.data_format_dim_map(x, src_format="NHWC", dst_format="HWNC") - with self.session(use_gpu=test_lib.is_gpu_available()) as sess: - y_val = sess.run(y) + with test_util.use_gpu(): + y_val = self.evaluate(y) self.assertAllEqual(y_val, y_val_expected) def testNHWCtoWHCN(self): @@ -1135,8 +1160,8 @@ class DataFormatDimMapTest(test_lib.TestCase): y_val_expected = [3, 1, 0, 2, 3, 1, 0, 2] x = constant_op.constant(x_val) y = nn_ops.data_format_dim_map(x, src_format="NHWC", dst_format="WHCN") - with self.session(use_gpu=test_lib.is_gpu_available()) as sess: - y_val = sess.run(y) + with test_util.use_gpu(): + y_val = self.evaluate(y) self.assertAllEqual(y_val, y_val_expected) def testArbitraryASCII(self): @@ -1144,8 +1169,8 @@ class DataFormatDimMapTest(test_lib.TestCase): y_val_expected = [3, 2, 1, 0, 3, 2, 1, 0] x = constant_op.constant(x_val) y = nn_ops.data_format_dim_map(x, src_format="qwer", dst_format="rewq") - with self.session(use_gpu=test_lib.is_gpu_available()) as sess: - y_val = sess.run(y) + with test_util.use_gpu(): + y_val = self.evaluate(y) self.assertAllEqual(y_val, y_val_expected) @@ -1155,64 +1180,64 @@ class DataFormatVectorPermuteTest(test_lib.TestCase): x_val = [7, 4, 9, 3] x = constant_op.constant(x_val) y = nn_ops.data_format_vec_permute(x) - with self.session(use_gpu=test_lib.is_gpu_available()) as sess: - y_val = sess.run(y) + with test_util.use_gpu(): + y_val = self.evaluate(y) self.assertAllEqual(y_val, [7, 3, 4, 9]) def testNCHWToNHWC(self): x_val = [7, 4, 9, 3] x = constant_op.constant(x_val) y = nn_ops.data_format_vec_permute(x, src_format="NCHW", dst_format="NHWC") - with self.session(use_gpu=test_lib.is_gpu_available()) as sess: - y_val = sess.run(y) + with test_util.use_gpu(): + y_val = self.evaluate(y) self.assertAllEqual(y_val, [7, 9, 3, 4]) def testNHWCToHWNC(self): x_val = [7, 4, 9, 3] x = constant_op.constant(x_val) y = nn_ops.data_format_vec_permute(x, src_format="NHWC", dst_format="HWNC") - with self.session(use_gpu=test_lib.is_gpu_available()) as sess: - y_val = sess.run(y) + with test_util.use_gpu(): + y_val = self.evaluate(y) self.assertAllEqual(y_val, [4, 9, 7, 3]) def testHWNCToNHWC(self): x_val = [7, 4, 9, 3] x = constant_op.constant(x_val) y = nn_ops.data_format_vec_permute(x, src_format="HWNC", dst_format="NHWC") - with self.session(use_gpu=test_lib.is_gpu_available()) as sess: - y_val = sess.run(y) + with test_util.use_gpu(): + y_val = self.evaluate(y) self.assertAllEqual(y_val, [9, 7, 4, 3]) def testNHWCToNCHW2D(self): x_val = [[7, 4], [9, 3], [4, 5], [5, 1]] x = constant_op.constant(x_val) y = nn_ops.data_format_vec_permute(x) - with self.session(use_gpu=test_lib.is_gpu_available()) as sess: - y_val = sess.run(y) + with test_util.use_gpu(): + y_val = self.evaluate(y) self.assertAllEqual(y_val, [[7, 4], [5, 1], [9, 3], [4, 5]]) def testNHWCToHWNC2D(self): x_val = [[7, 4], [9, 3], [4, 5], [5, 1]] x = constant_op.constant(x_val) y = nn_ops.data_format_vec_permute(x, src_format="NHWC", dst_format="HWNC") - with self.session(use_gpu=test_lib.is_gpu_available()) as sess: - y_val = sess.run(y) + with test_util.use_gpu(): + y_val = self.evaluate(y) self.assertAllEqual(y_val, [[9, 3], [4, 5], [7, 4], [5, 1]]) def testHWNCToNHWC2D(self): x_val = [[7, 4], [9, 3], [4, 5], [5, 1]] x = constant_op.constant(x_val) y = nn_ops.data_format_vec_permute(x, src_format="HWNC", dst_format="NHWC") - with self.session(use_gpu=test_lib.is_gpu_available()) as sess: - y_val = sess.run(y) + with test_util.use_gpu(): + y_val = self.evaluate(y) self.assertAllEqual(y_val, [[4, 5], [7, 4], [9, 3], [5, 1]]) def testNCHWToNHWC2D(self): x_val = [[7, 4], [9, 3], [4, 5], [5, 1]] x = constant_op.constant(x_val) y = nn_ops.data_format_vec_permute(x, src_format="NCHW", dst_format="NHWC") - with self.session(use_gpu=test_lib.is_gpu_available()) as sess: - y_val = sess.run(y) + with test_util.use_gpu(): + y_val = self.evaluate(y) self.assertAllEqual(y_val, [[7, 4], [4, 5], [5, 1], [9, 3]]) diff --git a/tensorflow/python/ops/nn_xent_test.py b/tensorflow/python/ops/nn_xent_test.py index 57ce4fd0a9..3e5c198fc6 100644 --- a/tensorflow/python/ops/nn_xent_test.py +++ b/tensorflow/python/ops/nn_xent_test.py @@ -24,6 +24,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import gradients_impl from tensorflow.python.ops import nn_impl @@ -53,6 +54,7 @@ class SigmoidCrossEntropyWithLogitsTest(test.TestCase): losses = np.array(self._SigmoidCrossEntropyWithLogits(x, y)).reshape(*sizes) return logits, targets, losses + @test_util.run_deprecated_v1 def testConstructionNamed(self): with self.cached_session(): logits, targets, _ = self._Inputs() @@ -68,7 +70,7 @@ class SigmoidCrossEntropyWithLogitsTest(test.TestCase): loss = nn_impl.sigmoid_cross_entropy_with_logits( labels=targets, logits=logits) np_loss = np.array(losses).astype(np.float32) - tf_loss = loss.eval() + tf_loss = self.evaluate(loss) self.assertAllClose(np_loss, tf_loss, atol=0.001) def testLogisticOutputMultiDim(self): @@ -79,9 +81,10 @@ class SigmoidCrossEntropyWithLogitsTest(test.TestCase): loss = nn_impl.sigmoid_cross_entropy_with_logits( labels=targets, logits=logits) np_loss = np.array(losses).astype(np.float32) - tf_loss = loss.eval() + tf_loss = self.evaluate(loss) self.assertAllClose(np_loss, tf_loss, atol=0.001) + @test_util.run_deprecated_v1 def testGradient(self): sizes = [4, 2] with self.cached_session(): @@ -92,6 +95,7 @@ class SigmoidCrossEntropyWithLogitsTest(test.TestCase): print("logistic loss gradient err = ", err) self.assertLess(err, 1e-7) + @test_util.run_deprecated_v1 def testGradientAtZero(self): with self.cached_session(): logits = constant_op.constant([0.0, 0.0], dtype=dtypes.float64) @@ -129,6 +133,7 @@ class WeightedCrossEntropyTest(test.TestCase): losses = np.array(self._WeightedCrossEntropy(x, y, q)).reshape(*sizes) return logits, targets, q, losses + @test_util.run_deprecated_v1 def testConstructionNamed(self): with self.cached_session(): logits, targets, pos_weight, _ = self._Inputs() @@ -143,7 +148,7 @@ class WeightedCrossEntropyTest(test.TestCase): loss = nn_impl.weighted_cross_entropy_with_logits( targets=targets, logits=logits, pos_weight=pos_weight) np_loss = np.array(losses).astype(np.float32) - tf_loss = loss.eval() + tf_loss = self.evaluate(loss) self.assertAllClose(np_loss, tf_loss, atol=0.001) def testOutputMultiDim(self): @@ -154,9 +159,10 @@ class WeightedCrossEntropyTest(test.TestCase): loss = nn_impl.weighted_cross_entropy_with_logits( targets=targets, logits=logits, pos_weight=pos_weight) np_loss = np.array(losses).astype(np.float32) - tf_loss = loss.eval() + tf_loss = self.evaluate(loss) self.assertAllClose(np_loss, tf_loss, atol=0.001) + @test_util.run_deprecated_v1 def testGradient(self): sizes = [4, 2] with self.cached_session(): diff --git a/tensorflow/python/ops/numerics.py b/tensorflow/python/ops/numerics.py index 1a235de90c..0ab39ad0a8 100644 --- a/tensorflow/python/ops/numerics.py +++ b/tensorflow/python/ops/numerics.py @@ -28,9 +28,7 @@ from tensorflow.python.util import deprecation from tensorflow.python.util.tf_export import tf_export -@tf_export( - "debugging.assert_all_finite", - v1=["debugging.assert_all_finite", "verify_tensor_all_finite"]) +@tf_export(v1=["debugging.assert_all_finite", "verify_tensor_all_finite"]) @deprecation.deprecated_endpoints("verify_tensor_all_finite") def verify_tensor_all_finite(t, msg, name=None): """Assert that the tensor does not contain any NaN's or Inf's. @@ -43,11 +41,26 @@ def verify_tensor_all_finite(t, msg, name=None): Returns: Same tensor as `t`. """ - with ops.name_scope(name, "VerifyFinite", [t]) as name: - t = ops.convert_to_tensor(t, name="t") - with ops.colocate_with(t): - verify_input = array_ops.check_numerics(t, message=msg) - out = control_flow_ops.with_dependencies([verify_input], t) + return verify_tensor_all_finite_v2(t, msg, name) + + +@tf_export("debugging.assert_all_finite", v1=[]) +def verify_tensor_all_finite_v2(x, message, name=None): + """Assert that the tensor does not contain any NaN's or Inf's. + + Args: + x: Tensor to check. + message: Message to log on failure. + name: A name for this operation (optional). + + Returns: + Same tensor as `x`. + """ + with ops.name_scope(name, "VerifyFinite", [x]) as name: + x = ops.convert_to_tensor(x, name="x") + with ops.colocate_with(x): + verify_input = array_ops.check_numerics(x, message=message) + out = control_flow_ops.with_dependencies([verify_input], x) return out diff --git a/tensorflow/tools/compatibility/testdata/test_file_v1_10.py b/tensorflow/python/ops/optional_grad.py similarity index 62% rename from tensorflow/tools/compatibility/testdata/test_file_v1_10.py rename to tensorflow/python/ops/optional_grad.py index e5ca8d3e2e..0d1eae3cda 100644 --- a/tensorflow/tools/compatibility/testdata/test_file_v1_10.py +++ b/tensorflow/python/ops/optional_grad.py @@ -12,23 +12,22 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Tests for tf upgrader.""" +"""Gradient functions for optional ops.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function -import tensorflow as tf -from tensorflow.python.framework import test_util -from tensorflow.python.platform import test as test_lib +from tensorflow.python.framework import ops +from tensorflow.python.ops import gen_dataset_ops -class TestUpgrade(test_util.TensorFlowTestCase): - """Test various APIs that have been changed in 2.0.""" - def testRenames(self): - with self.cached_session(): - self.assertAllClose(1.04719755, tf.acos(0.5).eval()) - self.assertAllClose(0.5, tf.rsqrt(4.0).eval()) +@ops.RegisterGradient("OptionalFromValue") +def _OptionalFromValueGrad(op, grad): + return gen_dataset_ops.optional_get_value( + grad, [t.dtype for t in op.inputs], [t.shape for t in op.inputs]) -if __name__ == "__main__": - test_lib.main() + +@ops.RegisterGradient("OptionalGetValue") +def _OptionalGetValueGrad(unused_op, *grads): + return gen_dataset_ops.optional_from_value(grads) diff --git a/tensorflow/python/ops/parallel_for/control_flow_ops.py b/tensorflow/python/ops/parallel_for/control_flow_ops.py index ead7ae5478..8f652e9c50 100644 --- a/tensorflow/python/ops/parallel_for/control_flow_ops.py +++ b/tensorflow/python/ops/parallel_for/control_flow_ops.py @@ -17,16 +17,20 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +from tensorflow.python.eager import context +from tensorflow.python.eager import function from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import tensor_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import control_flow_ops +from tensorflow.python.ops import math_ops from tensorflow.python.ops import tensor_array_ops from tensorflow.python.ops.parallel_for.pfor import PFor from tensorflow.python.util import nest -def for_loop(loop_fn, loop_fn_dtypes, iters): +def for_loop(loop_fn, loop_fn_dtypes, iters, parallel_iterations=None): """Runs `loop_fn` `iters` times and stacks the outputs. @@ -39,6 +43,8 @@ def for_loop(loop_fn, loop_fn_dtypes, iters): objects. The shape of these outputs should not depend on the input. loop_fn_dtypes: dtypes for the outputs of loop_fn. iters: Number of iterations for which to run loop_fn. + parallel_iterations: The number of iterations that can be dispatched in + parallel. This knob can be used to control the total memory usage. Returns: Returns a nested structure of stacked output tensor objects with the same @@ -66,11 +72,16 @@ def for_loop(loop_fn, loop_fn_dtypes, iters): outputs.append(ta) return tuple([i + 1] + outputs) + if parallel_iterations is not None: + extra_args = {"parallel_iterations": parallel_iterations} + else: + extra_args = {} ta_list = control_flow_ops.while_loop( - lambda i, *ta: i < iters, while_body, [0] + [ - tensor_array_ops.TensorArray(dtype, iters) - for dtype in flat_loop_fn_dtypes - ])[1:] + lambda i, *ta: i < iters, + while_body, + [0] + [tensor_array_ops.TensorArray(dtype, iters) + for dtype in flat_loop_fn_dtypes], + **extra_args)[1:] # TODO(rachelim): enable this for sparse tensors @@ -79,7 +90,15 @@ def for_loop(loop_fn, loop_fn_dtypes, iters): return nest.pack_sequence_as(loop_fn_dtypes, output) -def pfor(loop_fn, iters): +def _flatten_first_two_dims(x): + """Flattens the first two dimensions of x into a single dimension.""" + old_shape = array_ops.shape(x) + new_shape = array_ops.concat([[old_shape[0] * old_shape[1]], old_shape[2:]], + axis=0) + return array_ops.reshape(x, new_shape) + + +def pfor(loop_fn, iters, parallel_iterations=None): """Equivalent to running `loop_fn` `iters` times and stacking the outputs. `pfor` has functionality similar to `for_loop`, i.e. running `loop_fn` `iters` @@ -99,8 +118,8 @@ def pfor(loop_fn, iters): reads, etc). - Conversion works only on a limited set of kernels for which a converter has been registered. - - loop_fn cannot currently contain control flow operations like - tf.while_loop or tf.cond. + - loop_fn has limited support for control flow operations. tf.cond in + particular is not supported. - `loop_fn` should return nested structure of Tensors or Operations. However if an Operation is returned, it should have zero outputs. - The shape and dtype of `loop_fn` outputs should not depend on the input @@ -109,22 +128,92 @@ def pfor(loop_fn, iters): Args: loop_fn: A function that takes an int32 scalar tf.Tensor object representing the iteration number, and returns a possibly nested structure of Tensor or - Operation objects. + Operation objects. Note that if setting `parallel_iterations` argument to + something other than None, `loop_fn` may be called more than once during + graph construction. So it may need to avoid mutating global state. iters: Number of iterations for which to run loop_fn. + parallel_iterations: A knob to control how many iterations are vectorized + and dispatched in parallel. The default value of None corresponds to + vectorizing all the iterations. If `parallel_iterations` is smaller than + `iters`, then chunks of at most that many iterations are dispatched in + sequence. This knob can be used to control the total memory usage. Returns: Returns a nested structure of stacked tensor objects with the same nested structure as the output of `loop_fn`. + Raises: + ValueError: If parallel_iterations is not None and not an integer > 1. """ + def f(): + return _pfor_impl(loop_fn, iters, parallel_iterations=parallel_iterations) + if context.executing_eagerly(): + f = function.defun(f) + return f() + + +def _pfor_impl(loop_fn, iters, parallel_iterations=None): + """Implementation of pfor.""" existing_ops = set(ops.get_default_graph().get_operations()) with ops.name_scope("loop_body"): loop_var = array_ops.placeholder(dtypes.int32, shape=[]) loop_fn_outputs = loop_fn(loop_var) new_ops = set(ops.get_default_graph().get_operations()) - existing_ops iters = ops.convert_to_tensor(iters) - with ops.name_scope("pfor"): - converter = PFor(loop_var, iters, new_ops) - outputs = [] - for loop_fn_output in nest.flatten(loop_fn_outputs): - outputs.append(converter.convert(loop_fn_output)) - return nest.pack_sequence_as(loop_fn_outputs, outputs) + if parallel_iterations is not None: + if parallel_iterations < 1: + raise ValueError("parallel_iterations must be None or a positive integer") + if parallel_iterations == 1: + raise ValueError("Found parallel_iterations == 1. Use for_loop instead.") + iters_value = tensor_util.constant_value(iters) + if iters_value is not None and iters_value < parallel_iterations: + parallel_iterations = None + if parallel_iterations is None: + with ops.name_scope("pfor"): + converter = PFor(loop_var, iters, new_ops) + outputs = [] + for loop_fn_output in nest.flatten(loop_fn_outputs): + outputs.append(converter.convert(loop_fn_output)) + return nest.pack_sequence_as(loop_fn_outputs, outputs) + else: + num_tiled_iterations = iters // parallel_iterations + num_remaining_iterations = iters % parallel_iterations + # TODO(agarwal): Avoid calling loop_fn twice. Generate the loop body inside + # a tf.function and extract the graph from there to vectorize it. + with ops.name_scope("pfor_untiled"): + converter = PFor(loop_var, num_remaining_iterations, new_ops) + remaining_outputs = [] + flattened_loop_fn_outputs = nest.flatten(loop_fn_outputs) + for loop_fn_output in flattened_loop_fn_outputs: + remaining_outputs.append(converter.convert(loop_fn_output)) + + with ops.name_scope("pfor_tiled"): + loop_fn_dtypes = [ops.convert_to_tensor(x).dtype + for x in flattened_loop_fn_outputs] + + def tiled_loop_body(j): + offset = j * parallel_iterations + num_remaining_iterations + + def tiled_loop_fn(i): + return nest.flatten(loop_fn(i + offset)) + + return pfor(tiled_loop_fn, parallel_iterations) + + tiled_outputs = for_loop(tiled_loop_body, loop_fn_dtypes, + num_tiled_iterations, parallel_iterations=1) + tiled_outputs = [_flatten_first_two_dims(y) for y in tiled_outputs] + + with ops.name_scope("pfor"): + iters_value = tensor_util.constant_value(iters) + if iters_value is None or iters_value % parallel_iterations: + outputs = control_flow_ops.cond( + math_ops.equal(num_remaining_iterations, 0), + lambda: tiled_outputs, + lambda: [array_ops.concat([x, y], axis=0) + for x, y in zip(remaining_outputs, tiled_outputs)]) + else: + outputs = tiled_outputs + return nest.pack_sequence_as(loop_fn_outputs, nest.flatten(outputs)) + + + + diff --git a/tensorflow/python/ops/parallel_for/control_flow_ops_test.py b/tensorflow/python/ops/parallel_for/control_flow_ops_test.py index 171369b724..cc20d7ca6a 100644 --- a/tensorflow/python/ops/parallel_for/control_flow_ops_test.py +++ b/tensorflow/python/ops/parallel_for/control_flow_ops_test.py @@ -26,10 +26,12 @@ import numpy as np from tensorflow.core.example import example_pb2 from tensorflow.core.example import feature_pb2 from tensorflow.python.client import session +from tensorflow.python.eager import backprop from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import bitwise_ops from tensorflow.python.ops import clip_ops @@ -52,6 +54,7 @@ from tensorflow.python.platform import test from tensorflow.python.util import nest +@test_util.run_all_in_graph_and_eager_modes class PForTest(test.TestCase): def _run_targets(self, targets1, targets2=None, run_init=True): @@ -73,9 +76,13 @@ class PForTest(test.TestCase): else: self.assertAllEqual(outputs[i + n], outputs[i]) - def _test_loop_fn(self, loop_fn, iters, loop_fn_dtypes=dtypes.float32): - t1 = pfor_control_flow_ops.pfor(loop_fn, iters=iters) - t2 = pfor_control_flow_ops.for_loop(loop_fn, loop_fn_dtypes, iters=iters) + def _test_loop_fn(self, loop_fn, iters, + loop_fn_dtypes=dtypes.float32, + parallel_iterations=None): + t1 = pfor_control_flow_ops.pfor(loop_fn, iters=iters, + parallel_iterations=parallel_iterations) + t2 = pfor_control_flow_ops.for_loop(loop_fn, loop_fn_dtypes, iters=iters, + parallel_iterations=parallel_iterations) self.run_and_assert_equal(t1, t2) def test_op_conversion_fallback_to_while_loop(self): @@ -96,7 +103,32 @@ class PForTest(test.TestCase): loop_fn, 3, loop_fn_dtypes=[dtypes.float32, dtypes.int32]) flags.FLAGS.op_conversion_fallback_to_while_loop = False + def test_parallel_iterations(self): + for parallel_iterations in [2, 3, 8, 10]: + x = random_ops.random_uniform([8, 3]) + # pylint: disable=cell-var-from-loop + def loop_fn(i): + return array_ops.gather(x, i) + # pylint: enable=cell-var-from-loop + + self._test_loop_fn(loop_fn, 8, parallel_iterations=parallel_iterations) + self._test_loop_fn(loop_fn, 4 * constant_op.constant(2), + parallel_iterations=parallel_iterations) + + def test_parallel_iterations_zero(self): + with self.assertRaisesRegexp(ValueError, "positive integer"): + pfor_control_flow_ops.pfor(lambda i: 1, 8, parallel_iterations=0) + with self.assertRaisesRegexp(TypeError, "positive integer"): + pfor_control_flow_ops.for_loop(lambda i: 1, dtypes.int32, 8, + parallel_iterations=0) + + def test_parallel_iterations_one(self): + with self.assertRaisesRegexp(ValueError, "Use for_loop instead"): + pfor_control_flow_ops.pfor(lambda i: 1, 8, parallel_iterations=1) + + +@test_util.run_all_in_graph_and_eager_modes class ArrayTest(PForTest): def test_gather(self): @@ -288,14 +320,17 @@ class ArrayTest(PForTest): def test_unary_cwise_ops(self): for op in [array_ops.identity, array_ops.stop_gradient]: - x = random_ops.random_uniform([3, 5]) + with backprop.GradientTape(persistent=True) as g: + x = random_ops.random_uniform([3, 5]) + g.watch(x) # pylint: disable=cell-var-from-loop def loop_fn(i): - x1 = array_ops.gather(x, i) - y = op(x1) + x1 - loss = nn.l2_loss(y) - return op(x), y, gradient_ops.gradients(loss, x1) + with g: + x1 = array_ops.gather(x, i) + y = op(x1) + x1 + loss = nn.l2_loss(y) + return op(x), y, g.gradient(loss, x1) # pylint: enable=cell-var-from-loop @@ -318,17 +353,21 @@ class ArrayTest(PForTest): self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32]) def test_strided_slice(self): - x = random_ops.random_uniform([3, 3, 4, 4, 2, 2, 2]) + with backprop.GradientTape(persistent=True) as g: + x = random_ops.random_uniform([3, 3, 4, 4, 2, 2, 2]) + g.watch(x) def loop_fn(i): - x_i = array_ops.gather(x, i) - y = x_i[:2, ::2, 1::3, ..., array_ops.newaxis, 1] - loss = nn.l2_loss(y) - return y, gradient_ops.gradients(loss, x_i) + with g: + x_i = array_ops.gather(x, i) + y = x_i[:2, ::2, 1::3, ..., array_ops.newaxis, 1] + loss = nn.l2_loss(y) + return y, g.gradient(loss, x_i) self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 2) +@test_util.run_all_in_graph_and_eager_modes class BitwiseTest(PForTest): def test_unary_cwise(self): @@ -368,6 +407,7 @@ class BitwiseTest(PForTest): self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=output_dtypes) +@test_util.run_all_in_graph_and_eager_modes class MathTest(PForTest): def test_unary_cwise_ops(self): @@ -424,22 +464,29 @@ class MathTest(PForTest): nn.softsign, ] for op in complex_ops + real_ops: - x = random_ops.random_uniform([3, 5]) - if op in complex_ops: - y = random_ops.random_uniform([3, 5]) - x = math_ops.complex(x, y) + with backprop.GradientTape(persistent=True) as g: + x = random_ops.random_uniform([3, 5]) + g.watch(x) + if op in complex_ops: + y = random_ops.random_uniform([3, 5]) + g.watch(y) + x = math_ops.complex(x, y) # pylint: disable=cell-var-from-loop output_dtypes = [] def loop_fn(i): - x1 = array_ops.gather(x, i) - y1 = op(x1) - outputs = [op(x), y1] - if y1.dtype == dtypes.float32: - loss = math_ops.reduce_sum(y1 * y1) - grad = gradient_ops.gradients(loss, x1) - if grad and grad[0] is not None: - outputs.extend(grad) + with g: + x1 = array_ops.gather(x, i) + y1 = op(x1) + outputs = [op(x), y1] + if y1.dtype == dtypes.float32: + loss = math_ops.reduce_sum(y1 * y1) + else: + loss = None + if loss is not None: + grad = g.gradient(loss, x1) + if grad is not None: + outputs.append(grad) del output_dtypes[:] output_dtypes.extend([t.dtype for t in outputs]) return outputs @@ -656,17 +703,19 @@ class MathTest(PForTest): x_shape = [2, 3, 4, 5, 6] x = random_ops.random_uniform(x_shape) for data_format in ("NCHW", "NHWC"): - bias_dim = 2 if data_format == "NCHW" else -1 - bias_shape = x_shape[bias_dim] - bias = random_ops.random_uniform([bias_shape]) + with backprop.GradientTape(persistent=True) as g: + bias_dim = 2 if data_format == "NCHW" else -1 + bias_shape = x_shape[bias_dim] + bias = random_ops.random_uniform([bias_shape]) + g.watch(bias) # pylint: disable=cell-var-from-loop def loop_fn(i): - a = array_ops.gather(x, i) - y = nn.bias_add(a, bias, data_format=data_format) - loss = math_ops.reduce_sum(y * y) - return y, gradient_ops.gradients(loss, bias) - + with g: + a = array_ops.gather(x, i) + y = nn.bias_add(a, bias, data_format=data_format) + loss = math_ops.reduce_sum(y * y) + return y, g.gradient(loss, bias) # pylint: enable=cell-var-from-loop self._test_loop_fn( @@ -727,6 +776,7 @@ class MathTest(PForTest): self._test_loop_fn(loop_fn, 2) +@test_util.run_all_in_graph_and_eager_modes class NNTest(PForTest): def test_conv2d(self): @@ -779,30 +829,60 @@ class NNTest(PForTest): self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 2) def test_avg_pool(self): - x = random_ops.random_uniform([3, 2, 12, 12, 3]) - ksize = [1, 3, 3, 1] + with backprop.GradientTape(persistent=True) as g: + x = random_ops.random_uniform([3, 2, 12, 12, 3]) + g.watch(x) + ksize = [1, 3, 3, 1] def loop_fn(i): - x1 = array_ops.gather(x, i) - output = nn.avg_pool( - x1, ksize, strides=[1, 2, 2, 1], padding="VALID", data_format="NHWC") - loss = nn.l2_loss(output) - return output, gradient_ops.gradients(loss, x1) + with g: + x1 = array_ops.gather(x, i) + output = nn.avg_pool( + x1, ksize, strides=[1, 2, 2, 1], padding="VALID", + data_format="NHWC") + loss = nn.l2_loss(output) + return output, g.gradient(loss, x1) self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 2) def test_max_pool(self): - x = random_ops.random_uniform([3, 2, 12, 12, 3]) - ksize = [1, 3, 3, 1] + with backprop.GradientTape(persistent=True) as g: + x = random_ops.random_uniform([3, 2, 12, 12, 3]) + g.watch(x) + ksize = [1, 3, 3, 1] + strides = [1, 2, 2, 1] def loop_fn(i): - x1 = array_ops.gather(x, i) - output = nn.max_pool( - x1, ksize, strides=[1, 2, 2, 1], padding="VALID", data_format="NHWC") - loss = nn.l2_loss(output) - ones = array_ops.ones_like(output) - grad = gradient_ops.gradients(loss, x1, grad_ys=ones) - grad_grad = gradient_ops.gradients(grad, ones) + with g: + x1 = array_ops.gather(x, i) + output = nn.max_pool( + x1, ksize, strides=strides, padding="VALID", data_format="NHWC") + loss = nn.l2_loss(output) + ones = array_ops.ones_like(output) + g.watch(ones) + grad = g.gradient(loss, x1, output_gradients=ones) + grad_grad = g.gradient(grad, ones) + return output, grad, grad_grad + + self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 3) + + def test_max_pool3d(self): + with backprop.GradientTape(persistent=True) as g: + x = random_ops.random_uniform([3, 3, 2, 12, 12, 3]) + g.watch(x) + ksize = [1, 1, 3, 3, 1] + strides = [1, 1, 2, 2, 1] + + def loop_fn(i): + with g: + x1 = array_ops.gather(x, i) + output = nn.max_pool3d( + x1, ksize, strides=strides, padding="VALID", data_format="NDHWC") + loss = nn.l2_loss(output) + ones = array_ops.ones_like(output) + g.watch(ones) + grad = g.gradient(loss, x1, output_gradients=ones) + grad_grad = g.gradient(grad, ones) return output, grad, grad_grad self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 3) @@ -813,36 +893,41 @@ class NNTest(PForTest): data_formats.append("NCHW") for is_training in (True, False): for data_format in data_formats: - if data_format == "NCHW": - x = random_ops.random_uniform([3, 1, 2, 5, 5]) - else: - x = random_ops.random_uniform([3, 1, 5, 5, 2]) - scale = random_ops.random_uniform([2]) - offset = random_ops.random_uniform([2]) - mean = None if is_training else random_ops.random_uniform([2]) - variance = None if is_training else random_ops.random_uniform([2]) + with backprop.GradientTape(persistent=True) as g: + if data_format == "NCHW": + x = random_ops.random_uniform([3, 1, 2, 5, 5]) + else: + x = random_ops.random_uniform([3, 1, 5, 5, 2]) + g.watch(x) + scale = random_ops.random_uniform([2]) + g.watch(scale) + offset = random_ops.random_uniform([2]) + g.watch(offset) + mean = None if is_training else random_ops.random_uniform([2]) + variance = None if is_training else random_ops.random_uniform([2]) # pylint: disable=cell-var-from-loop def loop_fn(i): - x1 = array_ops.gather(x, i) - outputs = nn.fused_batch_norm( - x1, - scale, - offset, - mean=mean, - variance=variance, - epsilon=0.01, - data_format=data_format, - is_training=is_training) - outputs = list(outputs) - # We only test the first value of outputs when is_training is False. - # It looks like CPU and GPU have different outputs for batch_mean and - # batch_variance for this case. - if not is_training: - outputs[1] = constant_op.constant(0.) - outputs[2] = constant_op.constant(0.) - loss = nn.l2_loss(outputs[0]) - gradients = gradient_ops.gradients(loss, [x1, scale, offset]) + with g: + x1 = array_ops.gather(x, i) + outputs = nn.fused_batch_norm( + x1, + scale, + offset, + mean=mean, + variance=variance, + epsilon=0.01, + data_format=data_format, + is_training=is_training) + outputs = list(outputs) + # We only test the first value of outputs when is_training is False. + # It looks like CPU and GPU have different outputs for batch_mean + # and batch_variance for this case. + if not is_training: + outputs[1] = constant_op.constant(0.) + outputs[2] = constant_op.constant(0.) + loss = nn.l2_loss(outputs[0]) + gradients = g.gradient(loss, [x1, scale, offset]) return outputs + gradients # pylint: enable=cell-var-from-loop @@ -850,16 +935,20 @@ class NNTest(PForTest): self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 6) def test_softmax_cross_entropy_with_logits(self): - logits = random_ops.random_uniform([3, 2, 4]) - labels = random_ops.random_uniform([3, 2, 4]) - labels /= math_ops.reduce_sum(labels, axis=[2], keepdims=True) + with backprop.GradientTape(persistent=True) as g: + logits = random_ops.random_uniform([3, 2, 4]) + g.watch(logits) + labels = random_ops.random_uniform([3, 2, 4]) + labels /= math_ops.reduce_sum(labels, axis=[2], keepdims=True) def loop_fn(i): - logits_i = array_ops.gather(logits, i) - labels_i = array_ops.gather(labels, i) - loss = nn.softmax_cross_entropy_with_logits( - labels=labels_i, logits=logits_i) - return loss, gradient_ops.gradients(math_ops.reduce_sum(loss), logits_i) + with g: + logits_i = array_ops.gather(logits, i) + labels_i = array_ops.gather(labels, i) + loss = nn.softmax_cross_entropy_with_logits( + labels=labels_i, logits=logits_i) + total_loss = math_ops.reduce_sum(loss) + return loss, g.gradient(total_loss, logits_i) self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 2) @@ -1278,13 +1367,12 @@ class ControlFlowTest(PForTest): pfor_out, pfor_out_grad = pfor_control_flow_ops.pfor(loop_fn, 4) # Note that tf.while_loop does not work in the setup above. So we manually # construct the equivalent computation of the above loops here. - real_out = math_ops.reduce_sum(inp, reduction_indices=[0]) - real_out = math_ops.reduce_prod(real_out, reduction_indices=[1]) + real_out = math_ops.reduce_sum(inp, axis=[0]) + real_out = math_ops.reduce_prod(real_out, axis=[1]) # Note that gradients of real_out will accumulate the gradients across the # output value. Hence we do the same aggregation on pfor_out_grad. real_out_grad = gradient_ops.gradients(real_out, inp)[0] - sum_pfor_out_grad = math_ops.reduce_sum( - pfor_out_grad, reduction_indices=[0]) + sum_pfor_out_grad = math_ops.reduce_sum(pfor_out_grad, axis=[0]) with session.Session() as sess: v1, v2, v1_grad, v2_grad = sess.run( diff --git a/tensorflow/python/ops/parallel_for/gradients.py b/tensorflow/python/ops/parallel_for/gradients.py index 1f026b3660..3ba1bde347 100644 --- a/tensorflow/python/ops/parallel_for/gradients.py +++ b/tensorflow/python/ops/parallel_for/gradients.py @@ -25,7 +25,7 @@ from tensorflow.python.ops.parallel_for import control_flow_ops from tensorflow.python.util import nest -def jacobian(output, inputs, use_pfor=True): +def jacobian(output, inputs, use_pfor=True, parallel_iterations=None): """Computes jacobian of `output` w.r.t. `inputs`. Args: @@ -33,6 +33,8 @@ def jacobian(output, inputs, use_pfor=True): inputs: A tensor or a nested structure of tensor objects. use_pfor: If true, uses pfor for computing the jacobian. Else uses tf.while_loop. + parallel_iterations: A knob to control how many iterations and dispatched in + parallel. This knob can be used to control the total memory usage. Returns: A tensor or a nested strucutre of tensors with the same structure as @@ -56,10 +58,14 @@ def jacobian(output, inputs, use_pfor=True): output_size = array_ops.shape(output)[0] if use_pfor: - pfor_outputs = control_flow_ops.pfor(loop_fn, output_size) + pfor_outputs = control_flow_ops.pfor( + loop_fn, output_size, parallel_iterations=parallel_iterations) else: pfor_outputs = control_flow_ops.for_loop( - loop_fn, [output.dtype] * len(flat_inputs), output_size) + loop_fn, + [output.dtype] * len(flat_inputs), + output_size, + parallel_iterations=parallel_iterations) for i, out in enumerate(pfor_outputs): if out is not None: @@ -72,7 +78,7 @@ def jacobian(output, inputs, use_pfor=True): return nest.pack_sequence_as(inputs, pfor_outputs) -def batch_jacobian(output, inp, use_pfor=True): +def batch_jacobian(output, inp, use_pfor=True, parallel_iterations=None): """Computes and stacks jacobians of `output[i,...]` w.r.t. `input[i,...]`. e.g. @@ -87,6 +93,8 @@ def batch_jacobian(output, inp, use_pfor=True): inp: A tensor with shape [b, x1, ..., x_m] use_pfor: If true, uses pfor for computing the Jacobian. Else uses a tf.while_loop. + parallel_iterations: A knob to control how many iterations and dispatched in + parallel. This knob can be used to control the total memory usage. Returns: A tensor `t` with shape [b, y_1, ..., y_n, x1, ..., x_m] where `t[i, ...]` @@ -118,10 +126,13 @@ def batch_jacobian(output, inp, use_pfor=True): return gradient_ops.gradients(y, inp)[0] if use_pfor: - pfor_output = control_flow_ops.pfor(loop_fn, output_row_size) + pfor_output = control_flow_ops.pfor(loop_fn, output_row_size, + parallel_iterations=parallel_iterations) else: - pfor_output = control_flow_ops.for_loop(loop_fn, output.dtype, - output_row_size) + pfor_output = control_flow_ops.for_loop( + loop_fn, output.dtype, + output_row_size, + parallel_iterations=parallel_iterations) if pfor_output is None: return None pfor_output = array_ops.reshape(pfor_output, diff --git a/tensorflow/python/ops/parallel_for/gradients_test.py b/tensorflow/python/ops/parallel_for/gradients_test.py index 5a058bae82..4342833e3e 100644 --- a/tensorflow/python/ops/parallel_for/gradients_test.py +++ b/tensorflow/python/ops/parallel_for/gradients_test.py @@ -416,6 +416,12 @@ class GradientsTest(test.TestCase): self.assertAllClose(ans, pfor_value) self.assertAllClose(ans, while_value) + def test_jacobian_parallel_iterations(self): + x = constant_op.constant([[1., 2], [3, 4]]) + y = math_ops.matmul(x, x) + self.assertAllClose(gradients.jacobian(y, x, parallel_iterations=2), + gradients.jacobian(y, x, parallel_iterations=3)) + def test_batch_jacobian_bad_shapes(self): x = random_ops.random_uniform([2, 2]) y = random_ops.random_uniform([3, 2]) @@ -459,6 +465,13 @@ class GradientsTest(test.TestCase): self.assertAllClose(ans, pfor_value) self.assertAllClose(ans, while_value) + def test_batch_jacobian_parallel_iterations(self): + x = constant_op.constant([[1., 2], [3, 4]]) + w = constant_op.constant([[1., 2, 3, 4], [5, 6, 7, 8]]) + y = math_ops.matmul(x, w) + self.assertAllClose(gradients.batch_jacobian(y, x, parallel_iterations=2), + gradients.batch_jacobian(y, x, parallel_iterations=3)) + def test_fc_batch_jacobian(self): pfor_jacobian, while_jacobian = create_fc_batch_jacobian(8, 4, 2) self.run_and_assert_equal(pfor_jacobian, while_jacobian) @@ -471,8 +484,8 @@ class GradientsTest(test.TestCase): pfor_jacobian, while_gradients = create_dynamic_lstm_batch_jacobian(8, 4, 3) with session.Session() as sess: init = variables.global_variables_initializer() - sess.run(init) - pfor = sess.run(pfor_jacobian) + self.evaluate(init) + pfor = self.evaluate(pfor_jacobian) for i in range(4): while_i = sess.run(while_gradients[i]) self.assertAllClose(while_i, pfor[:, i, ...]) @@ -547,11 +560,11 @@ class GradientsBenchmarks(test.Benchmark): sess = session.Session() with sess: init = variables.global_variables_initializer() - sess.run(init) - sess.run(targets) + self.evaluate(init) + self.evaluate(targets) begin = time.time() for _ in range(iters): - sess.run(targets) + self.evaluate(targets) end = time.time() avg_time_ms = 1000 * (end - begin) / iters self.report_benchmark(iters=iters, wall_time=avg_time_ms, name=name) diff --git a/tensorflow/python/ops/parallel_for/pfor.py b/tensorflow/python/ops/parallel_for/pfor.py index e6f140a941..a22c1126c9 100644 --- a/tensorflow/python/ops/parallel_for/pfor.py +++ b/tensorflow/python/ops/parallel_for/pfor.py @@ -1152,9 +1152,8 @@ class PFor(object): continue converted_inputs = [self._conversion_map[inp] for inp in y_op.inputs] - some_input_converted = any( - [self._was_converted(x) for x in y_op.inputs]) - some_input_stacked = any([x.is_stacked for x in converted_inputs]) + some_input_converted = any(self._was_converted(x) for x in y_op.inputs) + some_input_stacked = any(x.is_stacked for x in converted_inputs) converted_control_ops = set() some_control_input_converted = False @@ -1198,7 +1197,7 @@ class PFor(object): # All inputs are unstacked or uncoverted but some control inputs are # converted. # TODO(rachelim): Handle the case where some inputs are sparsely - # stacked (i.e. any([x.is_sparse_stacked for x in converted_inputs])) + # stacked (i.e. any(x.is_sparse_stacked for x in converted_inputs)) new_op = _create_op(y_op.type, [x.t for x in converted_inputs], [x.dtype for x in y_op.outputs], y_op.node_def.attr) @@ -1303,7 +1302,10 @@ def _inputs_with_flattening(pfor_input, input_indices): @RegisterPForWithArgs("Conv2D", dims=[0]) @RegisterPForWithArgs("AvgPool", dims=[0]) @RegisterPForWithArgs("MaxPool", dims=[0]) +@RegisterPForWithArgs("MaxPool3D", dims=[0]) +@RegisterPForWithArgs("MaxPool3DGrad", dims=[0, 1, 2]) @RegisterPForWithArgs("MaxPoolGrad", dims=[0, 1, 2]) +@RegisterPForWithArgs("MaxPool3DGradGrad", dims=[0, 1, 2]) @RegisterPForWithArgs("MaxPoolGradGrad", dims=[0, 1, 2]) @RegisterPForWithArgs("SoftmaxCrossEntropyWithLogits", dims=[0, 1]) def _convert_flatten_batch(pfor_input, op_type, dims): diff --git a/tensorflow/python/ops/parsing_ops.py b/tensorflow/python/ops/parsing_ops.py index 484caf0179..a84af6c5cf 100644 --- a/tensorflow/python/ops/parsing_ops.py +++ b/tensorflow/python/ops/parsing_ops.py @@ -363,7 +363,7 @@ def _prepend_none_dimension(features): return features -@tf_export("io.parse_example", v1=["io.parse_example", "parse_example"]) +@tf_export(v1=["io.parse_example", "parse_example"]) def parse_example(serialized, features, name=None, example_names=None): # pylint: disable=line-too-long """Parses `Example` protos into a `dict` of tensors. @@ -574,6 +574,223 @@ def parse_example(serialized, features, name=None, example_names=None): Returns: A `dict` mapping feature keys to `Tensor` and `SparseTensor` values. + Raises: + ValueError: if any feature is invalid. + """ + return parse_example_v2(serialized, features, example_names, name) + + +@tf_export("io.parse_example", v1=[]) +def parse_example_v2(serialized, features, example_names=None, name=None): + # pylint: disable=line-too-long + """Parses `Example` protos into a `dict` of tensors. + + Parses a number of serialized [`Example`](https://www.tensorflow.org/code/tensorflow/core/example/example.proto) + protos given in `serialized`. We refer to `serialized` as a batch with + `batch_size` many entries of individual `Example` protos. + + `example_names` may contain descriptive names for the corresponding serialized + protos. These may be useful for debugging purposes, but they have no effect on + the output. If not `None`, `example_names` must be the same length as + `serialized`. + + This op parses serialized examples into a dictionary mapping keys to `Tensor` + and `SparseTensor` objects. `features` is a dict from keys to `VarLenFeature`, + `SparseFeature`, and `FixedLenFeature` objects. Each `VarLenFeature` + and `SparseFeature` is mapped to a `SparseTensor`, and each + `FixedLenFeature` is mapped to a `Tensor`. + + Each `VarLenFeature` maps to a `SparseTensor` of the specified type + representing a ragged matrix. Its indices are `[batch, index]` where `batch` + identifies the example in `serialized`, and `index` is the value's index in + the list of values associated with that feature and example. + + Each `SparseFeature` maps to a `SparseTensor` of the specified type + representing a Tensor of `dense_shape` `[batch_size] + SparseFeature.size`. + Its `values` come from the feature in the examples with key `value_key`. + A `values[i]` comes from a position `k` in the feature of an example at batch + entry `batch`. This positional information is recorded in `indices[i]` as + `[batch, index_0, index_1, ...]` where `index_j` is the `k-th` value of + the feature in the example at with key `SparseFeature.index_key[j]`. + In other words, we split the indices (except the first index indicating the + batch entry) of a `SparseTensor` by dimension into different features of the + `Example`. Due to its complexity a `VarLenFeature` should be preferred over a + `SparseFeature` whenever possible. + + Each `FixedLenFeature` `df` maps to a `Tensor` of the specified type (or + `tf.float32` if not specified) and shape `(serialized.size(),) + df.shape`. + + `FixedLenFeature` entries with a `default_value` are optional. With no default + value, we will fail if that `Feature` is missing from any example in + `serialized`. + + Each `FixedLenSequenceFeature` `df` maps to a `Tensor` of the specified type + (or `tf.float32` if not specified) and shape + `(serialized.size(), None) + df.shape`. + All examples in `serialized` will be padded with `default_value` along the + second dimension. + + Examples: + + For example, if one expects a `tf.float32` `VarLenFeature` `ft` and three + serialized `Example`s are provided: + + ``` + serialized = [ + features + { feature { key: "ft" value { float_list { value: [1.0, 2.0] } } } }, + features + { feature []}, + features + { feature { key: "ft" value { float_list { value: [3.0] } } } + ] + ``` + + then the output will look like: + + ```python + {"ft": SparseTensor(indices=[[0, 0], [0, 1], [2, 0]], + values=[1.0, 2.0, 3.0], + dense_shape=(3, 2)) } + ``` + + If instead a `FixedLenSequenceFeature` with `default_value = -1.0` and + `shape=[]` is used then the output will look like: + + ```python + {"ft": [[1.0, 2.0], [3.0, -1.0]]} + ``` + + Given two `Example` input protos in `serialized`: + + ``` + [ + features { + feature { key: "kw" value { bytes_list { value: [ "knit", "big" ] } } } + feature { key: "gps" value { float_list { value: [] } } } + }, + features { + feature { key: "kw" value { bytes_list { value: [ "emmy" ] } } } + feature { key: "dank" value { int64_list { value: [ 42 ] } } } + feature { key: "gps" value { } } + } + ] + ``` + + And arguments + + ``` + example_names: ["input0", "input1"], + features: { + "kw": VarLenFeature(tf.string), + "dank": VarLenFeature(tf.int64), + "gps": VarLenFeature(tf.float32), + } + ``` + + Then the output is a dictionary: + + ```python + { + "kw": SparseTensor( + indices=[[0, 0], [0, 1], [1, 0]], + values=["knit", "big", "emmy"] + dense_shape=[2, 2]), + "dank": SparseTensor( + indices=[[1, 0]], + values=[42], + dense_shape=[2, 1]), + "gps": SparseTensor( + indices=[], + values=[], + dense_shape=[2, 0]), + } + ``` + + For dense results in two serialized `Example`s: + + ``` + [ + features { + feature { key: "age" value { int64_list { value: [ 0 ] } } } + feature { key: "gender" value { bytes_list { value: [ "f" ] } } } + }, + features { + feature { key: "age" value { int64_list { value: [] } } } + feature { key: "gender" value { bytes_list { value: [ "f" ] } } } + } + ] + ``` + + We can use arguments: + + ``` + example_names: ["input0", "input1"], + features: { + "age": FixedLenFeature([], dtype=tf.int64, default_value=-1), + "gender": FixedLenFeature([], dtype=tf.string), + } + ``` + + And the expected output is: + + ```python + { + "age": [[0], [-1]], + "gender": [["f"], ["f"]], + } + ``` + + An alternative to `VarLenFeature` to obtain a `SparseTensor` is + `SparseFeature`. For example, given two `Example` input protos in + `serialized`: + + ``` + [ + features { + feature { key: "val" value { float_list { value: [ 0.5, -1.0 ] } } } + feature { key: "ix" value { int64_list { value: [ 3, 20 ] } } } + }, + features { + feature { key: "val" value { float_list { value: [ 0.0 ] } } } + feature { key: "ix" value { int64_list { value: [ 42 ] } } } + } + ] + ``` + + And arguments + + ``` + example_names: ["input0", "input1"], + features: { + "sparse": SparseFeature( + index_key="ix", value_key="val", dtype=tf.float32, size=100), + } + ``` + + Then the output is a dictionary: + + ```python + { + "sparse": SparseTensor( + indices=[[0, 3], [0, 20], [1, 42]], + values=[0.5, -1.0, 0.0] + dense_shape=[2, 100]), + } + ``` + + Args: + serialized: A vector (1-D Tensor) of strings, a batch of binary + serialized `Example` protos. + features: A `dict` mapping feature keys to `FixedLenFeature`, + `VarLenFeature`, and `SparseFeature` values. + example_names: A vector (1-D Tensor) of strings (optional), the names of + the serialized protos in the batch. + name: A name for this operation (optional). + + Returns: + A `dict` mapping feature keys to `Tensor` and `SparseTensor` values. + Raises: ValueError: if any feature is invalid. """ @@ -764,8 +981,7 @@ def _process_raw_parameters(names, dense_defaults, sparse_keys, sparse_types, dense_shapes_as_proto, dense_shapes) -@tf_export("io.parse_single_example", - v1=["io.parse_single_example", "parse_single_example"]) +@tf_export(v1=["io.parse_single_example", "parse_single_example"]) def parse_single_example(serialized, features, name=None, example_names=None): """Parses a single `Example` proto. @@ -795,6 +1011,48 @@ def parse_single_example(serialized, features, name=None, example_names=None): Returns: A `dict` mapping feature keys to `Tensor` and `SparseTensor` values. + Raises: + ValueError: if any feature is invalid. + """ + return parse_single_example_v2_unoptimized( + serialized, features, example_names, name + ) + + +# TODO(b/70890287): Combine the implementation of this op and +# `parse_single_example_v2()` after 1/10/2018. +@tf_export("io.parse_single_example", v1=[]) +def parse_single_example_v2_unoptimized( + serialized, features, example_names=None, name=None + ): + """Parses a single `Example` proto. + + Similar to `parse_example`, except: + + For dense tensors, the returned `Tensor` is identical to the output of + `parse_example`, except there is no batch dimension, the output shape is the + same as the shape given in `dense_shape`. + + For `SparseTensor`s, the first (batch) column of the indices matrix is removed + (the indices matrix is a column vector), the values vector is unchanged, and + the first (`batch_size`) entry of the shape vector is removed (it is now a + single element vector). + + One might see performance advantages by batching `Example` protos with + `parse_example` instead of using this function directly. + + Args: + serialized: A scalar string Tensor, a single serialized Example. + See `_parse_single_example_raw` documentation for more details. + features: A `dict` mapping feature keys to `FixedLenFeature` or + `VarLenFeature` values. + example_names: (Optional) A scalar string Tensor, the associated name. + See `_parse_single_example_raw` documentation for more details. + name: A name for this operation (optional). + + Returns: + A `dict` mapping feature keys to `Tensor` and `SparseTensor` values. + Raises: ValueError: if any feature is invalid. """ @@ -1570,7 +1828,7 @@ def _parse_single_sequence_example_raw(serialized, # Swap `name` and `na_value` for backward compatibility. -@tf_export("io.decode_csv", v1=["io.decode_csv", "decode_csv"]) +@tf_export(v1=["io.decode_csv", "decode_csv"]) @deprecation.deprecated_endpoints("decode_csv") def decode_csv(records, record_defaults, @@ -1609,6 +1867,54 @@ def decode_csv(records, A list of `Tensor` objects. Has the same type as `record_defaults`. Each tensor will have the same shape as records. + Raises: + ValueError: If any of the arguments is malformed. + """ + return decode_csv_v2( + records, record_defaults, + field_delim, use_quote_delim, + na_value, select_cols, name + ) + + +@tf_export("io.decode_csv", v1=[]) +def decode_csv_v2(records, + record_defaults, + field_delim=",", + use_quote_delim=True, + na_value="", + select_cols=None, + name=None): + """Convert CSV records to tensors. Each column maps to one tensor. + + RFC 4180 format is expected for the CSV records. + (https://tools.ietf.org/html/rfc4180) + Note that we allow leading and trailing spaces with int or float field. + + Args: + records: A `Tensor` of type `string`. + Each string is a record/row in the csv and all records should have + the same format. + record_defaults: A list of `Tensor` objects with specific types. + Acceptable types are `float32`, `float64`, `int32`, `int64`, `string`. + One tensor per column of the input record, with either a + scalar default value for that column or an empty vector if the column is + required. + field_delim: An optional `string`. Defaults to `","`. + char delimiter to separate fields in a record. + use_quote_delim: An optional `bool`. Defaults to `True`. + If false, treats double quotation marks as regular + characters inside of the string fields (ignoring RFC 4180, Section 2, + Bullet 5). + na_value: Additional string to recognize as NA/NaN. + select_cols: Optional sorted list of column indices to select. If specified, + only this subset of columns will be parsed and returned. + name: A name for the operation (optional). + + Returns: + A list of `Tensor` objects. Has the same type as `record_defaults`. + Each tensor will have the same shape as records. + Raises: ValueError: If any of the arguments is malformed. """ diff --git a/tensorflow/python/ops/partitioned_variables.py b/tensorflow/python/ops/partitioned_variables.py index 7743b634e8..c1084c2559 100644 --- a/tensorflow/python/ops/partitioned_variables.py +++ b/tensorflow/python/ops/partitioned_variables.py @@ -57,7 +57,7 @@ import math from tensorflow.python.framework import dtypes from tensorflow.python.framework import tensor_shape from tensorflow.python.ops import variable_scope -from tensorflow.python.platform import tf_logging as logging +from tensorflow.python.util import deprecation from tensorflow.python.util.tf_export import tf_export __all__ = [ @@ -68,7 +68,7 @@ __all__ = [ ] -@tf_export("variable_axis_size_partitioner") +@tf_export(v1=["variable_axis_size_partitioner"]) def variable_axis_size_partitioner( max_shard_bytes, axis=0, bytes_per_string_element=16, max_shards=None): """Get a partitioner for VariableScope to keep shards below `max_shard_bytes`. @@ -96,7 +96,7 @@ def variable_axis_size_partitioner( Returns: A partition function usable as the `partitioner` argument to - `variable_scope`, `get_variable`, and `get_partitioned_variable_list`. + `variable_scope` and `get_variable`. Raises: ValueError: If any of the byte counts are non-positive. @@ -154,7 +154,7 @@ def variable_axis_size_partitioner( return _partitioner -@tf_export("min_max_variable_partitioner") +@tf_export(v1=["min_max_variable_partitioner"]) def min_max_variable_partitioner(max_partitions=1, axis=0, min_slice_size=256 << 10, bytes_per_string_element=16): @@ -175,7 +175,7 @@ def min_max_variable_partitioner(max_partitions=1, axis=0, Returns: A partition function usable as the `partitioner` argument to - `variable_scope`, `get_variable`, and `get_partitioned_variable_list`. + `variable_scope` and `get_variable`. """ def _partitioner(shape, dtype): @@ -218,7 +218,7 @@ def min_max_variable_partitioner(max_partitions=1, axis=0, return _partitioner -@tf_export("fixed_size_partitioner") +@tf_export(v1=["fixed_size_partitioner"]) def fixed_size_partitioner(num_shards, axis=0): """Partitioner to specify a fixed number of shards along given axis. @@ -228,7 +228,7 @@ def fixed_size_partitioner(num_shards, axis=0): Returns: A partition function usable as the `partitioner` argument to - `variable_scope`, `get_variable`, and `get_partitioned_variable_list`. + `variable_scope` and `get_variable`. """ def _partitioner(shape, **unused_args): partitions_list = [1] * len(shape) @@ -237,7 +237,10 @@ def fixed_size_partitioner(num_shards, axis=0): return _partitioner -@tf_export("create_partitioned_variables") +@tf_export(v1=["create_partitioned_variables"]) +@deprecation.deprecated( + date=None, + instructions="Use tf.get_variable with a partitioner set.") def create_partitioned_variables( shape, slicing, initializer, dtype=dtypes.float32, trainable=True, collections=None, name=None, reuse=None): @@ -282,11 +285,6 @@ def create_partitioned_variables( Raises: ValueError: If any of the arguments is malformed. """ - logging.warn( - "create_partitioned_variables is deprecated. Use " - "tf.get_variable with a partitioner set, or " - "tf.get_partitioned_variable_list, instead.") - if len(shape) != len(slicing): raise ValueError("The 'shape' and 'slicing' of a partitioned Variable " "must have the length: shape: %s, slicing: %s" % diff --git a/tensorflow/python/ops/quantized_conv_ops_test.py b/tensorflow/python/ops/quantized_conv_ops_test.py index f7fa264461..6b469a954f 100644 --- a/tensorflow/python/ops/quantized_conv_ops_test.py +++ b/tensorflow/python/ops/quantized_conv_ops_test.py @@ -73,7 +73,7 @@ class Conv2DTest(test.TestCase): max_input=x1_max, min_filter=x2_min, max_filter=x2_max) - value = sess.run(conv) + value = self.evaluate(conv) quantized_output = value[0] output_min = value[1] output_max = value[2] diff --git a/tensorflow/python/ops/quantized_ops_test.py b/tensorflow/python/ops/quantized_ops_test.py index 0f3b04e4ad..b81843d174 100644 --- a/tensorflow/python/ops/quantized_ops_test.py +++ b/tensorflow/python/ops/quantized_ops_test.py @@ -41,7 +41,7 @@ class QuantizedOpsTest(test.TestCase): x_min = 0.0 x_max = 255.0 op = array_ops.quantize(x, x_min, x_max, dtypes.quint8, mode="MIN_FIRST") - value = sess.run(op) + value = self.evaluate(op) self.assertArrayNear(expected_output, value.output, 0.1) def testDequantizeOp(self): @@ -52,7 +52,7 @@ class QuantizedOpsTest(test.TestCase): x_min = 0.0 x_max = 255.0 op = array_ops.dequantize(x, x_min, x_max, mode="MIN_FIRST") - value = sess.run(op) + value = self.evaluate(op) self.assertArrayNear(expected_output, value, 0.1) diff --git a/tensorflow/python/ops/ragged/BUILD b/tensorflow/python/ops/ragged/BUILD index 152c6dc841..e335c5cb6f 100644 --- a/tensorflow/python/ops/ragged/BUILD +++ b/tensorflow/python/ops/ragged/BUILD @@ -32,7 +32,9 @@ py_library( ":ragged_map_ops", ":ragged_math_ops", ":ragged_operators", + ":ragged_string_ops", ":ragged_tensor", + ":ragged_tensor_shape", ":ragged_tensor_value", ":ragged_util", ":segment_id_ops", @@ -155,6 +157,7 @@ py_library( deps = [ ":ragged_factory_ops", ":ragged_tensor", + ":ragged_tensor_shape", ":ragged_util", "//tensorflow/python:array_ops", "//tensorflow/python:clip_ops", @@ -178,6 +181,21 @@ py_library( ], ) +py_library( + name = "ragged_string_ops", + srcs = ["ragged_string_ops.py"], + srcs_version = "PY2AND3", + deps = [ + ":ragged_array_ops", + ":ragged_conversion_ops", + ":ragged_factory_ops", + ":ragged_tensor", + "//tensorflow/python:array_ops", + "//tensorflow/python:math_ops", + "//tensorflow/python:util", + ], +) + py_library( name = "ragged_tensor", srcs = ["ragged_tensor.py"], @@ -190,6 +208,25 @@ py_library( ], ) +py_library( + name = "ragged_tensor_shape", + srcs = ["ragged_tensor_shape.py"], + srcs_version = "PY2AND3", + deps = [ + ":ragged_array_ops", + ":ragged_conversion_ops", + ":ragged_factory_ops", + ":ragged_tensor", + ":ragged_util", + "//tensorflow/python:array_ops", + "//tensorflow/python:dtypes", + "//tensorflow/python:framework_ops", + "//tensorflow/python:math_ops", + "//tensorflow/python:tensor_shape", + "//tensorflow/python:tensor_util", + ], +) + py_library( name = "ragged_tensor_value", srcs = ["ragged_tensor_value.py"], @@ -207,6 +244,7 @@ py_library( "//tensorflow/python:dtypes", "//tensorflow/python:framework_ops", "//tensorflow/python:math_ops", + "//tensorflow/python:ragged_math_ops_gen", ], ) @@ -256,6 +294,9 @@ py_test( size = "medium", srcs = ["ragged_tensor_test.py"], srcs_version = "PY2AND3", + tags = [ + "no_windows", + ], deps = [ ":ragged", "//tensorflow/python:array_ops", @@ -407,6 +448,9 @@ py_test( name = "ragged_to_sparse_op_test", srcs = ["ragged_to_sparse_op_test.py"], srcs_version = "PY2AND3", + tags = [ + "no_windows", + ], deps = [ ":ragged", "//tensorflow/python:array_ops", @@ -513,6 +557,9 @@ py_test( name = "ragged_constant_value_op_test", srcs = ["ragged_constant_value_op_test.py"], srcs_version = "PY2AND3", + tags = [ + "no_windows", + ], deps = [ ":ragged", "//tensorflow/python:framework_test_lib", @@ -681,3 +728,15 @@ py_test( "@absl_py//absl/testing:parameterized", ], ) + +py_test( + name = "ragged_tensor_shape_test", + srcs = ["ragged_tensor_shape_test.py"], + srcs_version = "PY2AND3", + deps = [ + ":ragged", + "//tensorflow/python:framework_test_lib", + "//tensorflow/python:platform_test", + "@absl_py//absl/testing:parameterized", + ], +) diff --git a/tensorflow/python/ops/ragged/__init__.py b/tensorflow/python/ops/ragged/__init__.py index 3a28848545..1b2a7be95f 100644 --- a/tensorflow/python/ops/ragged/__init__.py +++ b/tensorflow/python/ops/ragged/__init__.py @@ -143,6 +143,11 @@ The following operations are specific to ragged tensors: @@make_elementwise_op + +@@RaggedTensorDynamicShape +@@broadcast_to +@@broadcast_dynamic_shape + """ @@ -151,6 +156,7 @@ from __future__ import division from __future__ import print_function from tensorflow.python.ops.ragged import ragged_operators +from tensorflow.python.ops.ragged import ragged_string_ops from tensorflow.python.ops.ragged.ragged_array_ops import batch_gather from tensorflow.python.ops.ragged.ragged_array_ops import boolean_mask @@ -214,6 +220,10 @@ from tensorflow.python.ops.ragged.ragged_tensor import is_ragged from tensorflow.python.ops.ragged.ragged_tensor import RaggedTensor from tensorflow.python.ops.ragged.ragged_tensor import RaggedTensorType +from tensorflow.python.ops.ragged.ragged_tensor_shape import broadcast_dynamic_shape +from tensorflow.python.ops.ragged.ragged_tensor_shape import broadcast_to +from tensorflow.python.ops.ragged.ragged_tensor_shape import RaggedTensorDynamicShape + from tensorflow.python.ops.ragged.ragged_tensor_value import RaggedTensorValue from tensorflow.python.ops.ragged.segment_id_ops import row_splits_to_segment_ids diff --git a/tensorflow/python/ops/ragged/convert_to_tensor_or_ragged_tensor_op_test.py b/tensorflow/python/ops/ragged/convert_to_tensor_or_ragged_tensor_op_test.py index b43470dfa1..ef3464f243 100644 --- a/tensorflow/python/ops/ragged/convert_to_tensor_or_ragged_tensor_op_test.py +++ b/tensorflow/python/ops/ragged/convert_to_tensor_or_ragged_tensor_op_test.py @@ -90,6 +90,7 @@ class RaggedConvertToTensorOrRaggedTensorTest(test_util.TensorFlowTestCase, preferred_dtype=dtypes.string, expected_dtype=dtypes.int32), ]) + @test_util.run_deprecated_v1 def testConvertRaggedTensorValue(self, value, dtype=None, @@ -102,7 +103,7 @@ class RaggedConvertToTensorOrRaggedTensorTest(test_util.TensorFlowTestCase, self.assertEqual(value.ragged_rank, converted.ragged_rank) self.assertEqual(dtypes.as_dtype(expected_dtype), converted.dtype) with self.test_session(): - self.assertEqual(value.tolist(), converted.eval().tolist()) + self.assertEqual(value.tolist(), self.evaluate(converted).tolist()) @parameterized.parameters([ dict( @@ -145,6 +146,7 @@ class RaggedConvertToTensorOrRaggedTensorTest(test_util.TensorFlowTestCase, message=('Tensor conversion requested dtype string for ' 'Tensor with dtype int32')), ]) + @test_util.run_deprecated_v1 def testConvertTensorError(self, pylist, message, diff --git a/tensorflow/python/ops/ragged/ragged_array_ops.py b/tensorflow/python/ops/ragged/ragged_array_ops.py index 425f3957c3..603e39d1dc 100644 --- a/tensorflow/python/ops/ragged/ragged_array_ops.py +++ b/tensorflow/python/ops/ragged/ragged_array_ops.py @@ -225,6 +225,28 @@ def row_lengths(rt_input, axis=1, name=None): return array_ops.ones(shape[:axis], dtypes.int64) * shape[axis] +def nested_row_lengths(rt_input, name=None): + """Returns a tuple containing the row_lengths for all ragged dimensions. + + `nested_row_lengths(rt)` is a tuple containing the `row_lengths` tensors for + all ragged dimensions in `rt`, ordered from outermost to innermost. + + Args: + rt_input: A potentially ragged tensor. + name: A name prefix for the returned tensors (optional). + + Returns: + A `tuple` of 1-D `int64` `Tensors`. The length of the tuple is equal to + `rt_input.ragged_rank`. + """ + with ops.name_scope(name, 'RaggedNestedRowLengths', [rt_input]): + rt_nested_row_lengths = [] + while isinstance(rt_input, ragged_tensor.RaggedTensor): + rt_nested_row_lengths.append(row_lengths(rt_input)) + rt_input = rt_input.values + return tuple(rt_nested_row_lengths) + + #=============================================================================== # Bounding Shape #=============================================================================== @@ -451,8 +473,7 @@ def batch_gather(params, indices, name=None): adjusted_indices = math_ops.to_int64(indices) + adjustments return gather(params.values, adjusted_indices) else: - raise ValueError( - 'batch shape from indices does not match params shape') + raise ValueError('batch shape from indices does not match params shape') #=============================================================================== @@ -719,7 +740,7 @@ def boolean_mask(data, mask, keepdims=False, name=None): int_mask = ragged_functional_ops.map_inner_values( math_ops.cast, mask, dtype=dtypes.int64) masked_row_lengths = ragged_math_ops.reduce_sum(int_mask, axis=1) - splits.append(_lengths_to_splits(masked_row_lengths)) + splits.append(ragged_util.lengths_to_splits(masked_row_lengths)) mask = mask.values data = data.values @@ -741,7 +762,7 @@ def boolean_mask(data, mask, keepdims=False, name=None): # masks back to a splits tensor. lengths = row_lengths(data) masked_lengths = array_ops.boolean_mask(lengths, mask) - masked_splits = _lengths_to_splits(masked_lengths) + masked_splits = ragged_util.lengths_to_splits(masked_lengths) # Get the masked values: first get row ids corresponding to each # value, then use tf.gather to build a boolean mask that's false for @@ -977,7 +998,7 @@ def _ragged_stack_concat_axis_0(rt_inputs, stack_values): # If we are performing a stack operation, then add another splits. if stack_values: stack_lengths = array_ops.stack([nrows(rt) for rt in rt_inputs]) - stack_splits = _lengths_to_splits(stack_lengths) + stack_splits = ragged_util.lengths_to_splits(stack_lengths) concatenated_nested_splits.insert(0, stack_splits) return ragged_factory_ops.from_nested_row_splits(concatenated_inner_values, @@ -1131,7 +1152,8 @@ def _tile_ragged_values(rt_input, multiples, const_multiples=None): # Repeat each element in this ragged dimension `multiples[axis]` times. if const_multiples is None or const_multiples[axis] != 1: - inner_value_ids = _repeat_ranges(inner_value_ids, splits, multiples[axis]) + inner_value_ids = ragged_util.repeat_ranges(inner_value_ids, splits, + multiples[axis]) prev_splits = splits @@ -1172,6 +1194,17 @@ def _tile_ragged_splits(rt_input, multiples, const_multiples=None): ragged_rank = rt_input.ragged_rank nested_splits = rt_input.nested_row_splits + # projected_splits[src_axis, dst_axis] contains the split points that divide + # the rows from src_axis in the list of dst_axis values. E.g., + # projected_splits[i, i] = nested_splits[i], and + # projected_splits[i, i+1] = gather(nested_splits[i+1], nested_splits[i]). + projected_splits = [{i: nested_splits[i]} for i in range(ragged_rank)] + for src_axis in range(ragged_rank): + for dst_axis in range(src_axis + 1, ragged_rank - 1): + projected_splits[src_axis][dst_axis] = array_ops.gather( + nested_splits[dst_axis], + projected_splits[src_axis][dst_axis - 1]) + # For each ragged dimension: nested_splits[axis] -> result_splits[axis]. result_splits = [] for axis in range(ragged_rank): @@ -1188,16 +1221,16 @@ def _tile_ragged_splits(rt_input, multiples, const_multiples=None): repeats = 1 for d in range(axis - 1, -1, -1): if const_multiples is None or const_multiples[d + 1] != 1: - splits = nested_splits[d] * repeats - output_lengths = _repeat_ranges(output_lengths, splits, - multiples[d + 1]) + splits = projected_splits[d][axis - 1] * repeats + output_lengths = ragged_util.repeat_ranges(output_lengths, splits, + multiples[d + 1]) repeats *= multiples[d + 1] # Tile splits for the outermost (uniform) dimension. output_lengths = array_ops.tile(output_lengths, multiples[:1]) # Convert to splits. - result_splits.append(_lengths_to_splits(output_lengths)) + result_splits.append(ragged_util.lengths_to_splits(output_lengths)) return result_splits @@ -1425,11 +1458,6 @@ def _coordinate_where(condition): #=============================================================================== -def _lengths_to_splits(lengths): - """Returns splits corresponding to the given lengths.""" - return array_ops.concat([[0], math_ops.cumsum(lengths)], axis=0) - - def _increase_ragged_rank_to(rt_input, ragged_rank): """Adds ragged dimensions to `rt_input` so it has the desired ragged rank.""" if ragged_rank > 0: @@ -1449,45 +1477,3 @@ def _concat_ragged_splits(splits_list): pieces.append(splits[1:] + splits_offset) splits_offset += splits[-1] return array_ops.concat(pieces, axis=0) - - -def _repeat_ranges(params, splits, multiple): - """Repeats each range of `params` (as specified by `splits`) `multiple` times. - - Let the `i`th range of `params` be defined as - `params[splits[i]:splits[i + 1]]`. Then this function returns a tensor - containing range 0 repeated `multiple` times, followed by range 1 repeated - `multiple`, ..., followed by the last range repeated `multiple` times. - - Args: - params: The `Tensor` whose values should be repeated. - splits: A splits tensor indicating the ranges of `params` that should be - repeated. - multiple: The number of times each range should be repeated. - - Returns: - A `Tensor` with the same rank and type as `params`. - - #### Example: - ```python - >>> _repeat_ranges(['a', 'b', 'c'], [0, 2, 3], 3) - ['a', 'b', 'a', 'b', 'a', 'b', 'c', 'c', 'c'] - ``` - """ - # Repeat each split value `multiple` times. E.g., if `splits=[0 3 4]` and - # `multiples=3`, then `repeated_splits=[0 0 0 3 3 3 4 4 4]`. - repeated_splits = array_ops.tile( - array_ops.expand_dims(splits, axis=1), array_ops.stack([1, multiple])) - repeated_splits = array_ops.reshape(repeated_splits, [-1]) - - # Divide the splits into repeated starts & repeated limits. E.g., if - # `repeated_splits=[0 0 0 3 3 3 4 4 4]` then `repeated_starts=[0 0 0 3 3 3]` - # and `repeated_limits=[3 3 3 4 4 4]`. - n_splits = array_ops.shape(repeated_splits, out_type=dtypes.int64)[0] - repeated_starts = repeated_splits[:n_splits - multiple] - repeated_limits = repeated_splits[multiple:] - - # Get indices for each range from starts to limits, and use those to gather - # the values in the desired repetition pattern. - offsets = ragged_math_ops.range(repeated_starts, repeated_limits).values - return array_ops.gather(params, offsets) diff --git a/tensorflow/python/ops/ragged/ragged_batch_gather_op_test.py b/tensorflow/python/ops/ragged/ragged_batch_gather_op_test.py index 79a2ecd87a..d9d840500c 100644 --- a/tensorflow/python/ops/ragged/ragged_batch_gather_op_test.py +++ b/tensorflow/python/ops/ragged/ragged_batch_gather_op_test.py @@ -135,6 +135,7 @@ class RaggedBatchGatherOpTest(test_util.TensorFlowTestCase, expected=ragged.constant_value( [[[[b'c', b'a'], [b'd', b'd']], [[b'f', b'e']]]], ragged_rank=2)), ]) + @test_util.run_deprecated_v1 def testRaggedBatchGather(self, descr, params, indices, expected): result = ragged.batch_gather(params, indices) self.assertEqual( @@ -144,6 +145,7 @@ class RaggedBatchGatherOpTest(test_util.TensorFlowTestCase, expected = expected.tolist() self.assertEqual(result.eval().tolist(), expected) + @test_util.run_deprecated_v1 def testRaggedBatchGatherUnknownRankError(self): params = [['a', 'b'], ['c', 'd']] indices = array_ops.placeholder(dtypes.int32, shape=None) @@ -186,6 +188,7 @@ class RaggedBatchGatherOpTest(test_util.TensorFlowTestCase, indices=[[[0]]], message='batch shape from indices does not match params shape'), ]) + @test_util.run_deprecated_v1 def testRaggedBatchGatherStaticError(self, params, indices, diff --git a/tensorflow/python/ops/ragged/ragged_boolean_mask_op_test.py b/tensorflow/python/ops/ragged/ragged_boolean_mask_op_test.py index b3279c1e84..d939d9d634 100644 --- a/tensorflow/python/ops/ragged/ragged_boolean_mask_op_test.py +++ b/tensorflow/python/ops/ragged/ragged_boolean_mask_op_test.py @@ -298,6 +298,7 @@ class RaggedBooleanMaskOpTest(test_util.TensorFlowTestCase, keepdims=True, expected=ragged.constant_value([[[1], [4, 6]], [[7, 9], []]])), ]) # pyformat: disable + @test_util.run_deprecated_v1 def testBooleanMask(self, descr, data, mask, keepdims, expected): actual = ragged.boolean_mask(data, mask, keepdims=keepdims) self.assertEqual( @@ -307,6 +308,7 @@ class RaggedBooleanMaskOpTest(test_util.TensorFlowTestCase, expected = expected.tolist() self.assertEqual(actual.eval().tolist(), expected) + @test_util.run_deprecated_v1 def testErrors(self): self.assertRaisesRegexp(ValueError, r'mask\.shape\.ndims must be kown statically', diff --git a/tensorflow/python/ops/ragged/ragged_concat_op_test.py b/tensorflow/python/ops/ragged/ragged_concat_op_test.py index 6b1a602d04..3699f90f46 100644 --- a/tensorflow/python/ops/ragged/ragged_concat_op_test.py +++ b/tensorflow/python/ops/ragged/ragged_concat_op_test.py @@ -41,6 +41,11 @@ class RaggedConcatOpTest(test_util.TensorFlowTestCase, parameterized.TestCase): ] @parameterized.parameters( + dict( + descr='Two rank-2 inputs with empty value axis=1', + rt_inputs=([[]], [[]]), + axis=1, + expected=[[]]), dict( descr='Two rank-2 inputs (ragged_rank=1), axis=0', rt_inputs=( @@ -216,6 +221,7 @@ class RaggedConcatOpTest(test_util.TensorFlowTestCase, parameterized.TestCase): axis=0, expected=[[b'a00', b'a01'], [], [b'a20', b'a21']]), ) # pyformat: disable + @test_util.run_deprecated_v1 def testRaggedConcat(self, descr, rt_inputs, @@ -261,6 +267,7 @@ class RaggedConcatOpTest(test_util.TensorFlowTestCase, parameterized.TestCase): error=ValueError, message='Dimension 0 in both shapes must be equal'), ) + @test_util.run_deprecated_v1 def testStaticError(self, rt_inputs, axis, error, message, ragged_ranks=None): rt_inputs = self._rt_inputs_to_tensors(rt_inputs, ragged_ranks) self.assertRaisesRegexp(error, message, ragged.concat, rt_inputs, axis) @@ -273,6 +280,7 @@ class RaggedConcatOpTest(test_util.TensorFlowTestCase, parameterized.TestCase): error=errors.InvalidArgumentError, message='Input tensors have incompatible shapes'), ]) + @test_util.run_deprecated_v1 def testRuntimeError(self, rt_inputs, axis, error, message, ragged_ranks=None): rt_inputs = [ @@ -282,6 +290,7 @@ class RaggedConcatOpTest(test_util.TensorFlowTestCase, parameterized.TestCase): with self.test_session(): self.assertRaisesRegexp(error, message, concatenated.eval) + @test_util.run_deprecated_v1 def testNegativeAxisWithUnknownRankError(self): rt_inputs = [ array_ops.placeholder(dtypes.int64), @@ -291,6 +300,7 @@ class RaggedConcatOpTest(test_util.TensorFlowTestCase, parameterized.TestCase): ValueError, r'axis may only be negative if ndims is statically known.', ragged.concat, rt_inputs, -1) + @test_util.run_deprecated_v1 def testSingleTensorInput(self): """Tests ragged_concat with a single tensor input. diff --git a/tensorflow/python/ops/ragged/ragged_const_op_test.py b/tensorflow/python/ops/ragged/ragged_const_op_test.py index 13f79c5729..2505b23912 100644 --- a/tensorflow/python/ops/ragged/ragged_const_op_test.py +++ b/tensorflow/python/ops/ragged/ragged_const_op_test.py @@ -133,6 +133,7 @@ class RaggedConstOpTest(test_util.TensorFlowTestCase, parameterized.TestCase): dict(pylist=[[b'a', b'b'], [b'c'], [b'd', b'e', b'f']], dtype=dtypes.string), ) + @test_util.run_deprecated_v1 def testRaggedConst(self, pylist, dtype=None, @@ -183,7 +184,7 @@ class RaggedConstOpTest(test_util.TensorFlowTestCase, parameterized.TestCase): self.assertEqual(tuple(rt.shape.as_list()), expected_shape) with self.test_session(): - result = rt.eval() + result = self.evaluate(rt) if rt.shape.ndims > 0: self.assertEqual(result.tolist(), pylist) if expected_shape is not None: @@ -238,8 +239,8 @@ class RaggedConstOpTest(test_util.TensorFlowTestCase, parameterized.TestCase): dict( pylist=[1, 2, 3], inner_shape=(1, 1), - exception=ValueError, - message='Too many elements provided.'), + exception=TypeError, + message='Expected Tensor\'s shape'), dict( pylist=[[[1, 2], [3, 4]], [[5, 6], [7, 8]]], inner_shape=(2, 2), @@ -258,6 +259,7 @@ class RaggedConstOpTest(test_util.TensorFlowTestCase, parameterized.TestCase): exception=ValueError, message='inner values have inconsistent shape'), ) + @test_util.run_deprecated_v1 def testRaggedConstError(self, pylist, dtype=None, diff --git a/tensorflow/python/ops/ragged/ragged_conversion_ops.py b/tensorflow/python/ops/ragged/ragged_conversion_ops.py index 3ec246ccaf..83212e49cf 100644 --- a/tensorflow/python/ops/ragged/ragged_conversion_ops.py +++ b/tensorflow/python/ops/ragged/ragged_conversion_ops.py @@ -196,7 +196,7 @@ def to_tensor(rt_input, default_value=None, name=None): Args: rt_input: The input `RaggedTensor`. default_value: Value to set for indices not specified in `rt_input`. - Defaults to zero. `default_value.shape` must be equal to + Defaults to zero. `default_value` must be broadcastable to `rt_input.shape[rt_input.ragged_rank + 1:]`. name: A name prefix for the returned tensors (optional). @@ -210,6 +210,9 @@ def to_tensor(rt_input, default_value=None, name=None): rt_input, name='rt_input') if not ragged_tensor.is_ragged(rt_input): return rt_input # already dense + if default_value is not None: + default_value = ops.convert_to_tensor( + default_value, name='default_value', dtype=rt_input.dtype) # If ragged_rank > 1, then recursively convert the ragged values into a # `Tensor` before we proceed. @@ -217,6 +220,16 @@ def to_tensor(rt_input, default_value=None, name=None): if ragged_tensor.is_ragged(values): values = to_tensor(values, default_value) + # Tile the default value, if necessary. + if default_value is not None: + if values.shape.ndims is not None: + default_value.shape.with_rank_at_most(values.shape.ndims - 1) + if (values.shape.ndims is None or default_value.shape.ndims is None or + values.shape.ndims != default_value.shape.ndims + 1): + value_shape = array_ops.shape(values)[1:] + default_value = array_ops.broadcast_to(default_value, value_shape) + default_value.shape.assert_is_compatible_with(values.shape[1:]) + # Get the expected dense shape ([nrows, ncols] + value_shape). rt_row_lengths = [rt_input.row_splits[1:] - rt_input.row_splits[:-1]] nrows = array_ops.shape(rt_input.row_splits, out_type=dtypes.int64)[0] - 1 @@ -228,9 +241,6 @@ def to_tensor(rt_input, default_value=None, name=None): # Build a default value if none was supplied. if default_value is None: default_value = array_ops.zeros(value_shape, dtype=values.dtype) - else: - default_value = ops.convert_to_tensor( - default_value, name='default_value', dtype=values.dtype) default_value.shape.assert_is_compatible_with(values.shape[1:]) default_value.set_shape(values.shape[1:]) @@ -351,9 +361,14 @@ def from_sparse(st_input, name=None): st_input = sparse_tensor.convert_to_tensor_or_sparse_tensor( st_input, name='rt_input') - if (st_input.dense_shape.shape.ndims != 2 and - st_input.indices.shape.ndims is None or - st_input.indices.shape.dims[1].value != 2): + static_rank_from_dense_shape = ( + None if st_input.dense_shape.shape.ndims is None + else st_input.dense_shape.shape.dims[0].value) + static_rank_from_indices = ( + None if st_input.indices.shape.ndims is None + else st_input.indices.shape.dims[1].value) + + if static_rank_from_dense_shape != 2 and static_rank_from_indices != 2: raise ValueError('rank(st_input) must be 2') with ops.control_dependencies( diff --git a/tensorflow/python/ops/ragged/ragged_elementwise_ops.py b/tensorflow/python/ops/ragged/ragged_elementwise_ops.py index 23d0e8b5fc..59b7dd1661 100644 --- a/tensorflow/python/ops/ragged/ragged_elementwise_ops.py +++ b/tensorflow/python/ops/ragged/ragged_elementwise_ops.py @@ -28,7 +28,7 @@ from tensorflow.python.ops import parsing_ops from tensorflow.python.ops import string_ops from tensorflow.python.ops.ragged import ragged_factory_ops from tensorflow.python.ops.ragged import ragged_tensor -from tensorflow.python.ops.ragged import ragged_util +from tensorflow.python.ops.ragged import ragged_tensor_shape from tensorflow.python.util import tf_decorator from tensorflow.python.util import tf_export from tensorflow.python.util import tf_inspect @@ -209,28 +209,45 @@ def _broadcast_elementwise_args(elementwise_args): if not any(is_ragged): return elementwise_args, (), () - # Support limited broadcasting (namely, scalar + ragged). Full - # broadcasting support will be added later. - if all((ragged_tensor.is_ragged(t) or t.shape.ndims == 0) - for t in elementwise_args.values()): + # If we have a single ragged tensor plus a set of scalars, then we can + # rely on the underlying elementwise op to do broadcasting. + if (sum(is_ragged) == 1 and + all((ragged_tensor.is_ragged(t) or t.shape.ndims == 0) + for t in elementwise_args.values())): nested_splits_lists = [ t.nested_row_splits for t in elementwise_args.values() - if ragged_tensor.is_ragged(t) - ] - if len(nested_splits_lists) == 1: - checks = () - else: - if any(t.shape.ndims is None for t in elementwise_args.values()): - raise ValueError('Ragged elementwise ops require that rank (number ' - 'of dimensions) be statically known.') - if len(set(t.shape.ndims for t in elementwise_args.values())) != 1: - raise ValueError('Ragged elementwise ops do not support ' - 'broadcasting yet') - checks = ragged_util.assert_splits_match(nested_splits_lists) - return (elementwise_args, nested_splits_lists[0], checks) + if ragged_tensor.is_ragged(t)][0] + return elementwise_args, nested_splits_lists, () + else: - raise ValueError('Ragged elementwise ops do not support broadcasting yet') + # Get the shapes of all the elementwise arguments. + shapes = [ragged_tensor_shape.RaggedTensorDynamicShape.from_tensor(t) + for t in elementwise_args.values()] + + # Broadcast the shapes to all have the same rank (the max rank). + ranks = [t.shape.ndims for t in elementwise_args.values()] + if any(rank is None for rank in ranks): + raise ValueError('Unable to broadcast: unknown rank') + broadcast_rank = max(ranks) + shapes = [shape.broadcast_to_rank(broadcast_rank) for shape in shapes] + + # For each dimension, broadcast the shapes to be compatible. + for axis in range(broadcast_rank): + # For each i, broadcast shape[i+1] to be compatible with shape[i]; and + # then finally broadcast shape[0] to be compatible with shape[-1]. + for i in range(len(shapes)): + j = (i + 1) % len(shapes) + dim_size = shapes[i].dimension_size(axis) + shapes[j] = shapes[j].broadcast_dimension(axis, dim_size) + broadcast_shape = shapes[0] + + # Broadcast every elementwise arg to the shape that we calculated. + elementwise_args = dict([ + (key, ragged_tensor_shape.broadcast_to(t, broadcast_shape, False)) + for (key, t) in elementwise_args.items()]) + nested_splits_lists = list(elementwise_args.values())[0].nested_row_splits + return elementwise_args, nested_splits_lists, () # A list of symbols that should be exported in the "ragged" package. @@ -252,6 +269,10 @@ def _add_elementwise_ops_to_this_module(specs, verbose=False): op_name = canonical_name else: op_name = original_op.__name__ + + # Temporary hack (will be removed once dispatch is added for RaggedTensors): + if op_name == 'neg': op_name = 'negative' + if verbose: print('Adding ragged_elementwise_op: tf.ragged.%s (based on tf.%s)' % (op_name, canonical_name)) @@ -348,7 +369,7 @@ _TF_ELEMENTWISE_OPS = [ (string_ops.regex_replace, 'input'), (string_ops.string_join, '[inputs]'), (string_ops.string_strip, 'input'), - (string_ops.string_to_hash_bucket, 'string_tensor'), + (string_ops.string_to_hash_bucket, 'input'), (string_ops.string_to_hash_bucket_fast, 'input'), (string_ops.string_to_hash_bucket_strong, 'input'), (string_ops.substr, 'input'), @@ -365,3 +386,4 @@ _TF_ELEMENTWISE_OPS = [ (parsing_ops.string_to_number, 'string_tensor'), ] _add_elementwise_ops_to_this_module(_TF_ELEMENTWISE_OPS) + diff --git a/tensorflow/python/ops/ragged/ragged_elementwise_ops_test.py b/tensorflow/python/ops/ragged/ragged_elementwise_ops_test.py index 5dfa5cff45..305a96df9c 100644 --- a/tensorflow/python/ops/ragged/ragged_elementwise_ops_test.py +++ b/tensorflow/python/ops/ragged/ragged_elementwise_ops_test.py @@ -394,49 +394,43 @@ class RaggedElementwiseOpsTest(test_util.TensorFlowTestCase, result_flat_values = array_ops.reshape(result, [-1]) self.assertAllEqual(expected_flat_values, result_flat_values) + @test_util.run_deprecated_v1 def testUnknownRankError(self): x = ragged.constant([[1, 2], [3]]) y = ragged.from_row_splits( array_ops.placeholder_with_default([1, 2, 3], shape=None), x.row_splits) with self.assertRaisesRegexp( - ValueError, r'Ragged elementwise ops require that rank \(number ' - r'of dimensions\) be statically known.'): + ValueError, r'Unable to broadcast: unknown rank'): ragged.add(x, y) - def testBroadcastError1(self): - x = ragged.constant([[1, 2], [3]]) - y = [[12]] - with self.assertRaisesRegexp( - ValueError, 'Ragged elementwise ops do not support broadcasting yet'): - ragged.add(x, y) - - def testBroadcastError2(self): - x = ragged.constant([[[1, 2], [3, 4]], [[5]]], ragged_rank=2) - y = ragged.constant([[[8], [3]], [[2]]], ragged_rank=1) - with self.assertRaisesRegexp(ValueError, - 'Inputs must have identical ragged splits'): - ragged.add(x, y) - - def testBroadcastError3(self): - x = ragged.constant([[[1, 2], [3]], [[4, 5], [6]]], ragged_rank=2) - y = ragged.constant([[7, 8], [9]], ragged_rank=1) - with self.assertRaisesRegexp( - ValueError, 'Ragged elementwise ops do not support broadcasting yet'): - ragged.add(x, y) - - def testBroadcastError4(self): - x = ragged.constant([[[1]]]) - y = ragged.constant([[1]]) - with self.assertRaisesRegexp( - ValueError, 'Ragged elementwise ops do not support broadcasting yet'): - ragged.add(x, y) + @parameterized.parameters([ + dict( + x=ragged.constant_value([[1, 2], [3]]), + y=[[10]], + expected=[[11, 12], [13]]), + dict( + x=ragged.constant_value([[[1, 2], [3, 4]], [[5]]], ragged_rank=2), + y=ragged.constant_value([[[10], [20]], [[30]]], ragged_rank=1), + expected=[[[11, 12], [23, 24]], [[35]]]), + dict( + x=ragged.constant_value([[[1]]]), + y=ragged.constant_value([[1]]), + expected=[[[2]]]), + ]) + def testBroadcastAdd(self, x, y, expected): + x = ragged.convert_to_tensor_or_ragged_tensor(x, dtype=dtypes.int32) + y = ragged.convert_to_tensor_or_ragged_tensor(y, dtype=dtypes.int32) + result = x + y + with self.cached_session(): + self.assertEqual(result.eval().tolist(), expected) def testShapeMismatch(self): x = ragged.constant([[1, 2, 3], [4, 5]]) y = ragged.constant([[1, 2, 3], [4, 5, 6]]) with self.assertRaisesRegexp(errors.InvalidArgumentError, - 'Inputs must have identical ragged splits'): - ragged.add(x, y) + 'Incompatible shapes'): + with self.cached_session(): + ragged.add(x, y).eval() def testDocstring(self): self.assertRegexpMatches( diff --git a/tensorflow/python/ops/ragged/ragged_expand_dims_op_test.py b/tensorflow/python/ops/ragged/ragged_expand_dims_op_test.py index 0c4fd458c2..3ff66973b6 100644 --- a/tensorflow/python/ops/ragged/ragged_expand_dims_op_test.py +++ b/tensorflow/python/ops/ragged/ragged_expand_dims_op_test.py @@ -105,6 +105,7 @@ class RaggedExpandDimsOpTest(test_util.TensorFlowTestCase, expected=EXAMPLE4D_EXPAND_AXIS[4], expected_shape=[3, None, None, 2, 1]), ]) # pyformat: disable + @test_util.run_deprecated_v1 def testRaggedExpandDims(self, rt_input, axis, diff --git a/tensorflow/python/ops/ragged/ragged_factory_ops.py b/tensorflow/python/ops/ragged/ragged_factory_ops.py index de3a2d5b10..d1f301bc58 100644 --- a/tensorflow/python/ops/ragged/ragged_factory_ops.py +++ b/tensorflow/python/ops/ragged/ragged_factory_ops.py @@ -676,3 +676,33 @@ def from_nested_row_splits(inner_values, nested_row_splits, name=None): for splits in reversed(nested_row_splits): result = from_row_splits(result, splits) return result + + +def from_nested_row_lengths(inner_values, nested_row_lengths, name=None): + """Creates a `RaggedTensor` from a nested list of `row_lengths` tensors. + + Equivalent to: + + ```python + result = inner_values + for row_lengths in reversed(nested_row_lengths): + result = from_row_lengths(result, row_lengths) + ``` + + Args: + inner_values: A potentially ragged tensor. + nested_row_lengths: A list of 1-D int64 tensors. The `i`th tensor is used + as the `row_lengths` for the `i`th ragged dimension. + name: A name prefix for the RaggedTensor (optional). + + Returns: + A `RaggedTensor` (or `inner_values` if `nested_row_lengths` is empty). + """ + if isinstance(nested_row_lengths, ops.Tensor): + raise TypeError('nested_row_lengths must be a list of Tensors') + with ops.name_scope(name, 'RaggedFromNestedRowlengths', + [inner_values] + list(nested_row_lengths)): + result = inner_values + for lengths in reversed(nested_row_lengths): + result = from_row_lengths(result, lengths) + return result diff --git a/tensorflow/python/ops/ragged/ragged_from_sparse_op_test.py b/tensorflow/python/ops/ragged/ragged_from_sparse_op_test.py index ff19ddedeb..3c0db9e8fb 100644 --- a/tensorflow/python/ops/ragged/ragged_from_sparse_op_test.py +++ b/tensorflow/python/ops/ragged/ragged_from_sparse_op_test.py @@ -29,6 +29,7 @@ from tensorflow.python.platform import googletest class RaggedTensorToSparseOpTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testDocStringExample(self): st = sparse_tensor.SparseTensor( indices=[[0, 0], [0, 1], [0, 2], [1, 0], [3, 0]], @@ -39,6 +40,7 @@ class RaggedTensorToSparseOpTest(test_util.TensorFlowTestCase): with self.test_session(): self.assertEqual(rt.eval().tolist(), [[1, 2, 3], [4], [], [5]]) + @test_util.run_deprecated_v1 def testEmpty(self): st = sparse_tensor.SparseTensor( indices=array_ops.zeros([0, 2], dtype=dtypes.int64), @@ -49,6 +51,7 @@ class RaggedTensorToSparseOpTest(test_util.TensorFlowTestCase): with self.test_session(): self.assertEqual(rt.eval().tolist(), [[], [], [], []]) + @test_util.run_deprecated_v1 def testBadSparseTensorRank(self): st1 = sparse_tensor.SparseTensor(indices=[[0]], values=[0], dense_shape=[3]) st2 = sparse_tensor.SparseTensor( @@ -64,6 +67,22 @@ class RaggedTensorToSparseOpTest(test_util.TensorFlowTestCase): self.assertRaisesRegexp(ValueError, r'rank\(st_input\) must be 2', ragged.from_sparse, st3) + @test_util.run_deprecated_v1 + def testGoodPartialSparseTensorRank(self): + st1 = sparse_tensor.SparseTensor( + indices=[[0, 0]], + values=[0], + dense_shape=array_ops.placeholder(dtypes.int64)) + st2 = sparse_tensor.SparseTensor( + indices=array_ops.placeholder(dtypes.int64), + values=[0], + dense_shape=[4, 3]) + + # Shouldn't throw ValueError + ragged.from_sparse(st1) + ragged.from_sparse(st2) + + @test_util.run_deprecated_v1 def testNonRaggedSparseTensor(self): # "index_suffix" means the value of the innermost dimension of the index # (i.e., indices[i][-1]). diff --git a/tensorflow/python/ops/ragged/ragged_from_tensor_op_test.py b/tensorflow/python/ops/ragged/ragged_from_tensor_op_test.py index eb237f4c95..1d8a00cc18 100644 --- a/tensorflow/python/ops/ragged/ragged_from_tensor_op_test.py +++ b/tensorflow/python/ops/ragged/ragged_from_tensor_op_test.py @@ -31,6 +31,7 @@ from tensorflow.python.platform import googletest class RaggedFromTensorOpTest(test_util.TensorFlowTestCase, parameterized.TestCase): + @test_util.run_deprecated_v1 def testDocStringExamples(self): # The examples from ragged.from_tensor.__doc__. dt = constant_op.constant([[5, 7, 0], [0, 3, 0], [6, 0, 0]]) @@ -262,6 +263,7 @@ class RaggedFromTensorOpTest(test_util.TensorFlowTestCase, [[[5, 6], [7]], [[0, 8], []]]] }, ) # pyformat: disable + @test_util.run_deprecated_v1 def testRaggedFromTensor(self, tensor, expected, @@ -278,6 +280,7 @@ class RaggedFromTensorOpTest(test_util.TensorFlowTestCase, with self.test_session(): self.assertEqual(rt.eval().tolist(), expected) + @test_util.run_deprecated_v1 def testHighDimensions(self): # Use distinct prime numbers for all dimension shapes in this test, so # we can see any errors that are caused by mixing up dimension sizes. @@ -291,7 +294,7 @@ class RaggedFromTensorOpTest(test_util.TensorFlowTestCase, dt.shape.is_compatible_with(rt.shape), '%s is incompatible with %s' % (dt.shape, rt.shape)) with self.test_session(): - self.assertEqual(rt.eval().tolist(), dt.eval().tolist()) + self.assertEqual(rt.eval().tolist(), self.evaluate(dt).tolist()) @parameterized.parameters( # With no padding or lengths @@ -395,6 +398,7 @@ class RaggedFromTensorOpTest(test_util.TensorFlowTestCase, 'expected': [[], []] }, ) + @test_util.run_deprecated_v1 def testEmpty(self, dt_shape, expected, lengths=None, padding=None): dt = array_ops.zeros(dt_shape) rt = ragged.from_tensor(dt, lengths, padding) @@ -447,6 +451,7 @@ class RaggedFromTensorOpTest(test_util.TensorFlowTestCase, 'error': (ValueError, r'ragged_rank must be greater than 0; got -1') }, ) + @test_util.run_deprecated_v1 def testErrors(self, tensor, lengths=None, diff --git a/tensorflow/python/ops/ragged/ragged_gather_nd_op_test.py b/tensorflow/python/ops/ragged/ragged_gather_nd_op_test.py index dcf1feaa69..62c6819374 100644 --- a/tensorflow/python/ops/ragged/ragged_gather_nd_op_test.py +++ b/tensorflow/python/ops/ragged/ragged_gather_nd_op_test.py @@ -183,6 +183,7 @@ class RaggedGatherNdOpTest(test_util.TensorFlowTestCase, indices=[[0, 0, 1], [0, 0, 0], [0, 1, 0]], expected=[[b'c', b'd'], [b'a', b'b'], [b'e', b'f']]), ]) # pyformat: disable + @test_util.run_deprecated_v1 def testRaggedGatherNd(self, descr, params, indices, expected): result = ragged.gather_nd(params, indices) self.assertEqual( @@ -190,8 +191,9 @@ class RaggedGatherNdOpTest(test_util.TensorFlowTestCase, with self.test_session() as sess: if hasattr(expected, 'tolist'): expected = expected.tolist() - self.assertEqual(sess.run(result).tolist(), expected) + self.assertEqual(self.evaluate(result).tolist(), expected) + @test_util.run_deprecated_v1 def testRaggedGatherNdUnknownRankError(self): params = ragged.constant([['a', 'b'], ['c', 'd']]) indices1 = array_ops.placeholder(dtypes.int32, shape=None) @@ -219,6 +221,7 @@ class RaggedGatherNdOpTest(test_util.TensorFlowTestCase, indices=ragged.constant([[0]]), message='The innermost dimension of indices may not be ragged'), ]) + @test_util.run_deprecated_v1 def testRaggedGatherNdStaticError(self, params, indices, diff --git a/tensorflow/python/ops/ragged/ragged_gather_op_test.py b/tensorflow/python/ops/ragged/ragged_gather_op_test.py index bb52d05c32..76c90cdfee 100644 --- a/tensorflow/python/ops/ragged/ragged_gather_op_test.py +++ b/tensorflow/python/ops/ragged/ragged_gather_op_test.py @@ -30,6 +30,7 @@ from tensorflow.python.platform import googletest class RaggedTensorOpsTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testDocStringExamples(self): params = constant_op.constant(['a', 'b', 'c', 'd', 'e']) indices = constant_op.constant([3, 1, 2, 1, 0]) @@ -46,6 +47,7 @@ class RaggedTensorOpsTest(test_util.TensorFlowTestCase): ragged.gather(ragged_params, ragged_indices).eval().tolist(), [[[b'e'], [b'd'], []], [[b'd']], [], [[b'a', b'b', b'c']]]) + @test_util.run_deprecated_v1 def testTensorParamsAndTensorIndices(self): params = ['a', 'b', 'c', 'd', 'e'] indices = [2, 0, 2, 1] @@ -55,6 +57,7 @@ class RaggedTensorOpsTest(test_util.TensorFlowTestCase): [b'c', b'a', b'c', b'b']) self.assertEqual(type(ragged.gather(params, indices)), ops.Tensor) + @test_util.run_deprecated_v1 def testRaggedParamsAndTensorIndices(self): params = ragged.constant([['a', 'b'], ['c', 'd', 'e'], ['f'], [], ['g']]) indices = [2, 0, 2, 1] @@ -63,6 +66,7 @@ class RaggedTensorOpsTest(test_util.TensorFlowTestCase): ragged.gather(params, indices).eval().tolist(), [[b'f'], [b'a', b'b'], [b'f'], [b'c', b'd', b'e']]) + @test_util.run_deprecated_v1 def testTensorParamsAndRaggedIndices(self): params = ['a', 'b', 'c', 'd', 'e'] indices = ragged.constant([[2, 1], [1, 2, 0], [3]]) @@ -71,6 +75,7 @@ class RaggedTensorOpsTest(test_util.TensorFlowTestCase): ragged.gather(params, indices).eval().tolist(), [[b'c', b'b'], [b'b', b'c', b'a'], [b'd']]) + @test_util.run_deprecated_v1 def testRaggedParamsAndRaggedIndices(self): params = ragged.constant([['a', 'b'], ['c', 'd', 'e'], ['f'], [], ['g']]) indices = ragged.constant([[2, 1], [1, 2, 0], [3]]) @@ -82,6 +87,7 @@ class RaggedTensorOpsTest(test_util.TensorFlowTestCase): [[]]] # [p[3] ]] ) # pyformat: disable + @test_util.run_deprecated_v1 def testRaggedParamsAndScalarIndices(self): params = ragged.constant([['a', 'b'], ['c', 'd', 'e'], ['f'], [], ['g']]) indices = 1 @@ -89,6 +95,7 @@ class RaggedTensorOpsTest(test_util.TensorFlowTestCase): self.assertEqual( ragged.gather(params, indices).eval().tolist(), [b'c', b'd', b'e']) + @test_util.run_deprecated_v1 def test3DRaggedParamsAnd2DTensorIndices(self): params = ragged.constant([[['a', 'b'], []], [['c', 'd'], ['e'], ['f']], [['g']]]) @@ -101,6 +108,7 @@ class RaggedTensorOpsTest(test_util.TensorFlowTestCase): [[[b'g']], [[b'g']]]] # [p2, p2]] ) # pyformat: disable + @test_util.run_deprecated_v1 def testTensorParamsAnd4DRaggedIndices(self): indices = ragged.constant( [[[[3, 4], [0, 6]], []], [[[2, 1], [1, 0]], [[2, 5]], [[2, 3]]], @@ -115,6 +123,7 @@ class RaggedTensorOpsTest(test_util.TensorFlowTestCase): [[[b'c', b'b'], [b'b', b'a']], [[b'c', b'f']], [[b'c', b'd']]], [[[b'b', b'a']]]]) # pyformat: disable + @test_util.run_deprecated_v1 def testOutOfBoundsError(self): tensor_params = ['a', 'b', 'c'] tensor_indices = [0, 1, 2] @@ -131,6 +140,7 @@ class RaggedTensorOpsTest(test_util.TensorFlowTestCase): r'indices\[1\] = 3 is not in \[0, 2\)', ragged.gather(ragged_params, ragged_indices).eval) + @test_util.run_deprecated_v1 def testUnknownIndicesRankError(self): params = ragged.constant([], ragged_rank=1) indices = constant_op.constant([0], dtype=dtypes.int64) diff --git a/tensorflow/python/ops/ragged/ragged_map_fn_op_test.py b/tensorflow/python/ops/ragged/ragged_map_fn_op_test.py index 6f3f33b444..7a8603c949 100644 --- a/tensorflow/python/ops/ragged/ragged_map_fn_op_test.py +++ b/tensorflow/python/ops/ragged/ragged_map_fn_op_test.py @@ -140,6 +140,7 @@ class RaggedMapOpTest(test_util.TensorFlowTestCase, parameterized.TestCase): ), ]) + @test_util.run_deprecated_v1 def testRaggedMap( self, fn, @@ -161,9 +162,10 @@ class RaggedMapOpTest(test_util.TensorFlowTestCase, parameterized.TestCase): with self.test_session(): if ragged.is_ragged(expected_output): self.assertEqual(output.ragged_rank, expected_rt.ragged_rank) - output_values = output.eval() + output_values = self.evaluate(output) self.assertAllEqual(expected_output, output_values.tolist()) + @test_util.run_deprecated_v1 def testRaggedMapOnStructure(self): batman = ragged.constant([[1, 2, 3], [4], [5, 6, 7]]) # [[10, 20, 30], [40], [50, 60, 70]] @@ -184,6 +186,7 @@ class RaggedMapOpTest(test_util.TensorFlowTestCase, parameterized.TestCase): self.assertAllEqual(output.eval().tolist(), [66, 44, 198]) # Test mapping over a dict of RTs can produce a dict of RTs. + @test_util.run_deprecated_v1 def testRaggedMapOnStructure_RaggedOutputs(self): batman = ragged.constant([[1, 2, 3], [4], [5, 6, 7]]) # [[10, 20, 30], [40], [50, 60, 70]] @@ -215,6 +218,7 @@ class RaggedMapOpTest(test_util.TensorFlowTestCase, parameterized.TestCase): self.assertAllEqual(output['robin'].eval().tolist(), [[11, 21, 31], [41], [51, 61, 71]]) + @test_util.run_deprecated_v1 def testZip(self): x = ragged.constant([[10, 20], [30, 40], [50, 60], [70], [80, 90, 100]], dtypes.int64) @@ -232,11 +236,12 @@ class RaggedMapOpTest(test_util.TensorFlowTestCase, parameterized.TestCase): infer_shape=False) with self.test_session(): - result = output.eval().tolist() + result = self.evaluate(output).tolist() self.assertAllEqual( result, [[[0, 10], [0, 20]], [[1, 30], [1, 40]], [[2, 50], [2, 60]], [[3, 70]], [[4, 80], [4, 90], [4, 100]]]) + @test_util.run_deprecated_v1 def testBatchGather(self): tokens = ragged.constant([['hello', '.', 'there'], ['merhaba'], ['bonjour', '.', 'ca va', '?']]) @@ -255,7 +260,7 @@ class RaggedMapOpTest(test_util.TensorFlowTestCase, parameterized.TestCase): with self.test_session(): self.assertAllEqual( - out.eval().tolist(), + self.evaluate(out).tolist(), [[b'hello', b'there'], [b'merhaba'], [b'bonjour', b'ca va']]) def testMismatchRaggedRank(self): diff --git a/tensorflow/python/ops/ragged/ragged_map_inner_values_op_test.py b/tensorflow/python/ops/ragged/ragged_map_inner_values_op_test.py index 798d7c3ce8..b5802cb82d 100644 --- a/tensorflow/python/ops/ragged/ragged_map_inner_values_op_test.py +++ b/tensorflow/python/ops/ragged/ragged_map_inner_values_op_test.py @@ -43,6 +43,7 @@ class RaggedMapInnerValuesOpTest(test_util.TensorFlowTestCase, with self.test_session(): self.assertEqual(result.eval().tolist(), expected) + @test_util.run_deprecated_v1 def testDocStringExamples(self): """Test the examples in apply_op_to_ragged_values.__doc__.""" rt = ragged.constant([[1, 2, 3], [], [4, 5], [6]]) @@ -54,6 +55,7 @@ class RaggedMapInnerValuesOpTest(test_util.TensorFlowTestCase, self.assertEqual(v2.eval().tolist(), [[1, 4, 9], [], [16, 25], [36]]) self.assertEqual(v3.eval().tolist(), [[6, 7, 8], [], [9, 10], [11]]) + @test_util.run_deprecated_v1 def testOpWithSingleRaggedTensorArg(self): tensor = ragged.constant([[1, 2, 3], [], [4, 5]]) self.assertRaggedMapInnerValuesReturns( @@ -61,17 +63,20 @@ class RaggedMapInnerValuesOpTest(test_util.TensorFlowTestCase, args=(tensor,), expected=[[0, 0, 0], [], [0, 0]]) + @test_util.run_deprecated_v1 def testOpWithTwoRaggedTensorArgs(self): x = ragged.constant([[3, 1, 4], [], [1, 5]]) y = ragged.constant([[1, 2, 3], [], [4, 5]]) self.assertRaggedMapInnerValuesReturns( op=math_ops.multiply, args=(x, y), expected=[[3, 2, 12], [], [4, 25]]) + @test_util.run_deprecated_v1 def testOpWithRaggedTensorAndScalarArgs(self): y = ragged.constant([[1, 2, 3], [], [4, 5]]) self.assertRaggedMapInnerValuesReturns( op=math_ops.multiply, args=(5, y), expected=[[5, 10, 15], [], [20, 25]]) + @test_util.run_deprecated_v1 def testOpWithThreeRaggedTensorArgs(self): condition = ragged.constant( [[True, True, False], [], [True, False]]) # pyformat: disable @@ -82,6 +87,7 @@ class RaggedMapInnerValuesOpTest(test_util.TensorFlowTestCase, args=(condition, x, y), expected=[[b'a', b'b', b'C'], [], [b'd', b'E']]) + @test_util.run_deprecated_v1 def testOpWithRaggedTensorListArg(self): x = ragged.constant([[1, 2, 3], [], [4, 5]]) y = ragged.constant([[10, 20, 30], [], [40, 50]]) @@ -90,6 +96,7 @@ class RaggedMapInnerValuesOpTest(test_util.TensorFlowTestCase, args=([x, y, x],), expected=[[12, 24, 36], [], [48, 60]]) + @test_util.run_deprecated_v1 def testOpWithKeywordArgs(self): x = ragged.constant([[3, 1, 4], [], [1, 5]]) y = ragged.constant([[1, 2, 3], [], [4, 5]]) @@ -98,6 +105,7 @@ class RaggedMapInnerValuesOpTest(test_util.TensorFlowTestCase, kwargs=dict(x=x, y=y), expected=[[3, 2, 12], [], [4, 25]]) + @test_util.run_deprecated_v1 def testOpWithMixedPositionalAndKeywordArgs(self): x = ragged.constant([[3, 1, 4], [], [1, 5]]) y = ragged.constant([[1, 2, 3], [], [4, 5]]) @@ -107,6 +115,7 @@ class RaggedMapInnerValuesOpTest(test_util.TensorFlowTestCase, kwargs=dict(y=y), expected=[[3, 2, 12], [], [4, 25]]) + @test_util.run_deprecated_v1 def testNonElementWiseOp(self): x = ragged.constant( [[[3, 1, 4], [1, 5, 9], [2, 6, 5]], [], [[3, 5, 8], [9, 7, 9]]], @@ -119,6 +128,7 @@ class RaggedMapInnerValuesOpTest(test_util.TensorFlowTestCase, }, expected=[[8, 15, 13], [], [16, 25]]) + @test_util.run_deprecated_v1 def testOpWithRaggedRankGreaterThanOne(self): # ragged_rank=0 x0 = [3, 1, 4, 1, 5, 9, 2, 6, 5] @@ -163,6 +173,7 @@ class RaggedMapInnerValuesOpTest(test_util.TensorFlowTestCase, [[[54, 14], [48, 45]]] # row 3 ]) # pyformat: disable + @test_util.run_deprecated_v1 def testOpWithRaggedRankThree(self): x = ragged.constant([[[3, 1, 4]], [], [[], [1, 5]]]) y = ragged.constant([[[1, 2, 3]], [], [[], [4, 5]]]) @@ -171,6 +182,7 @@ class RaggedMapInnerValuesOpTest(test_util.TensorFlowTestCase, args=(x, y), expected=[[[3, 2, 12]], [], [[], [4, 25]]]) + @test_util.run_deprecated_v1 def testOpWithInnerValuesOnly(self): x = constant_op.constant([[1, 2], [3, 4], [5, 6]]) y = constant_op.constant(2) @@ -191,6 +203,7 @@ class RaggedMapInnerValuesOpTest(test_util.TensorFlowTestCase, r'Inputs must have identical ragged splits.*', ragged.map_inner_values, math_ops.add, x, y) + @test_util.run_deprecated_v1 def testRaggedTensorSplitsMismatchErrorAtRuntime(self): splits1 = array_ops.placeholder_with_default( constant_op.constant([0, 3, 3, 5], dtypes.int64), None) diff --git a/tensorflow/python/ops/ragged/ragged_operators_test.py b/tensorflow/python/ops/ragged/ragged_operators_test.py index a99d788ef7..7fe8159d82 100644 --- a/tensorflow/python/ops/ragged/ragged_operators_test.py +++ b/tensorflow/python/ops/ragged/ragged_operators_test.py @@ -27,6 +27,7 @@ class RaggedElementwiseOpsTest(test_util.TensorFlowTestCase): # @TODO(edloper): Test right-handed versions of operators once we add # broadcasting support for elementwise ops. + @test_util.run_deprecated_v1 def testOrderingOperators(self): x = ragged.constant([[1, 5], [3]]) y = ragged.constant([[4, 5], [1]]) @@ -40,6 +41,7 @@ class RaggedElementwiseOpsTest(test_util.TensorFlowTestCase): if a != b: print('%30s %s' % (b, a)) + @test_util.run_deprecated_v1 def testArithmeticOperators(self): x = ragged.constant([[1.0, -2.0], [8.0]]) y = ragged.constant([[4.0, 4.0], [2.0]]) @@ -75,6 +77,7 @@ class RaggedElementwiseOpsTest(test_util.TensorFlowTestCase): self.assertEqual((2.0 % y).eval().tolist(), [[2.0, 2.0], [0.0]]) self.assertEqual((x % 2.0).eval().tolist(), [[1.0, 0.0], [0.0]]) + @test_util.run_deprecated_v1 def testLogicalOperators(self): a = ragged.constant([[True, True], [False]]) b = ragged.constant([[True, False], [False]]) diff --git a/tensorflow/python/ops/ragged/ragged_range_op_test.py b/tensorflow/python/ops/ragged/ragged_range_op_test.py index 3c6a6fb75c..644423ecb7 100644 --- a/tensorflow/python/ops/ragged/ragged_range_op_test.py +++ b/tensorflow/python/ops/ragged/ragged_range_op_test.py @@ -26,6 +26,7 @@ from tensorflow.python.platform import googletest class RaggedRangeOpTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testDocStringExamples(self): """Examples from ragged_range.__doc__.""" with self.test_session(): @@ -38,6 +39,7 @@ class RaggedRangeOpTest(test_util.TensorFlowTestCase): rt3 = ragged.range([0, 5, 8], [3, 3, 12], 2).eval().tolist() self.assertEqual(rt3, [[0, 2], [], [8, 10]]) + @test_util.run_deprecated_v1 def testBasicRanges(self): with self.test_session(): # Specify limits only. @@ -56,6 +58,7 @@ class RaggedRangeOpTest(test_util.TensorFlowTestCase): [list(range(0, 4, 2)), list(range(3, 4, 3)), list(range(5, 15, 4))]) + @test_util.run_deprecated_v1 def testFloatRanges(self): with self.test_session(): expected = [[0.0, 0.4, 0.8, 1.2, 1.6, 2.0, 2.4, 2.8, 3.2, 3.6], [3.0], @@ -64,6 +67,7 @@ class RaggedRangeOpTest(test_util.TensorFlowTestCase): [0.4, 1.5, 2.2]).eval().tolist() self.assertEqual(expected, [[round(v, 5) for v in row] for row in actual]) + @test_util.run_deprecated_v1 def testNegativeDeltas(self): with self.test_session(): self.assertEqual( @@ -77,6 +81,7 @@ class RaggedRangeOpTest(test_util.TensorFlowTestCase): [list(range(0, 0, -1)), list(range(-3, 0, 1)), list(range(5, 0, -2))]) + @test_util.run_deprecated_v1 def testBroadcast(self): with self.test_session(): # Specify starts and limits, broadcast deltas. @@ -89,6 +94,7 @@ class RaggedRangeOpTest(test_util.TensorFlowTestCase): self.assertEqual( ragged.range(0, 5, 1).eval().tolist(), [list(range(0, 5, 1))]) + @test_util.run_deprecated_v1 def testEmptyRanges(self): rt1 = ragged.range([0, 5, 3], [0, 3, 5]) rt2 = ragged.range([0, 5, 5], [0, 3, 5], -1) @@ -96,6 +102,7 @@ class RaggedRangeOpTest(test_util.TensorFlowTestCase): self.assertEqual(rt1.eval().tolist(), [[], [], [3, 4]]) self.assertEqual(rt2.eval().tolist(), [[], [5, 4], []]) + @test_util.run_deprecated_v1 def testShapeFnErrors(self): with self.test_session(): self.assertRaisesRegexp(ValueError, r'Shape must be at most rank 1.*', @@ -107,12 +114,14 @@ class RaggedRangeOpTest(test_util.TensorFlowTestCase): self.assertRaisesRegexp(ValueError, r'Dimensions must be equal.*', ragged.range, [0], [1, 2]) + @test_util.run_deprecated_v1 def testKernelErrors(self): with self.test_session(): self.assertRaisesRegexp(errors.InvalidArgumentError, r'Requires delta != 0', ragged.range(0, 0, 0).eval) + @test_util.run_deprecated_v1 def testShape(self): self.assertEqual(ragged.range(0, 0, 0).shape.as_list(), [1, None]) self.assertEqual(ragged.range([1, 2, 3]).shape.as_list(), [3, None]) diff --git a/tensorflow/python/ops/ragged/ragged_reduce_op_test.py b/tensorflow/python/ops/ragged/ragged_reduce_op_test.py index 93176c738d..9f51d59ba3 100644 --- a/tensorflow/python/ops/ragged/ragged_reduce_op_test.py +++ b/tensorflow/python/ops/ragged/ragged_reduce_op_test.py @@ -300,6 +300,7 @@ class RaggedReduceOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): axis=2, expected=[[mean(1, 2), mean(3, 4, 5)], [mean(6, 7), 8], [9]]), ) + @test_util.run_deprecated_v1 def testReduce(self, ragged_reduce_op, rt_input, axis, expected): rt_input = ragged.constant(rt_input) reduced = ragged_reduce_op(rt_input, axis) @@ -311,6 +312,7 @@ class RaggedReduceOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): self.assertTrue( ((actual == expected) | (np.isnan(actual) & np.isnan(expected))).all()) + @test_util.run_deprecated_v1 def testMeanNan(self): rt_as_list = [[0, 1, 2, 3], [4], [], [5, 6], [7], [8, 9]] expected = ( @@ -321,6 +323,7 @@ class RaggedReduceOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): with self.test_session(): self.assertEqualWithNan(reduced.eval(), expected) + @test_util.run_deprecated_v1 def testMeanWithTensorInputs(self): tensor = [[1.0, 2.0, 3.0], [10.0, 20.0, 30.0]] expected = [2.0, 20.0] @@ -328,6 +331,7 @@ class RaggedReduceOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): with self.test_session(): self.assertAllEqual(reduced.eval(), expected) + @test_util.run_deprecated_v1 def testErrors(self): rt_input = ragged.constant([[1, 2, 3], [4, 5]]) axis = array_ops.placeholder_with_default(constant_op.constant([0]), None) diff --git a/tensorflow/python/ops/ragged/ragged_row_lengths_op_test.py b/tensorflow/python/ops/ragged/ragged_row_lengths_op_test.py index 4d5a0a5d11..4a705be484 100644 --- a/tensorflow/python/ops/ragged/ragged_row_lengths_op_test.py +++ b/tensorflow/python/ops/ragged/ragged_row_lengths_op_test.py @@ -143,6 +143,7 @@ class RaggedRowLengthsOp(test_util.TensorFlowTestCase, parameterized.TestCase): expected=[[2, 3, 0], [4, 1]], expected_ragged_rank=1), ]) # pyformat: disable + @test_util.run_deprecated_v1 def testRowLengths(self, rt_input, expected, diff --git a/tensorflow/python/ops/ragged/ragged_row_splits_to_segment_ids_op_test.py b/tensorflow/python/ops/ragged/ragged_row_splits_to_segment_ids_op_test.py index f246bf3552..7f5f4e91bd 100644 --- a/tensorflow/python/ops/ragged/ragged_row_splits_to_segment_ids_op_test.py +++ b/tensorflow/python/ops/ragged/ragged_row_splits_to_segment_ids_op_test.py @@ -26,6 +26,7 @@ from tensorflow.python.platform import googletest class RaggedSplitsToSegmentIdsOpTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testDocStringExample(self): splits = [0, 3, 3, 5, 6, 9] expected = [0, 0, 0, 2, 2, 3, 4, 4, 4] @@ -33,12 +34,14 @@ class RaggedSplitsToSegmentIdsOpTest(test_util.TensorFlowTestCase): with self.test_session(): self.assertEqual(segment_ids.eval().tolist(), expected) + @test_util.run_deprecated_v1 def testEmptySplits(self): # Note: the splits for an empty ragged tensor contains a single zero. segment_ids = ragged.row_splits_to_segment_ids([0]) with self.test_session(): self.assertEqual(segment_ids.eval().tolist(), []) + @test_util.run_deprecated_v1 def testErrors(self): self.assertRaisesRegexp(ValueError, r'Invalid row_splits: \[\]', ragged.row_splits_to_segment_ids, []) diff --git a/tensorflow/python/ops/ragged/ragged_segment_ids_to_row_splits_op_test.py b/tensorflow/python/ops/ragged/ragged_segment_ids_to_row_splits_op_test.py index fa7adf66b0..7e52f2d844 100644 --- a/tensorflow/python/ops/ragged/ragged_segment_ids_to_row_splits_op_test.py +++ b/tensorflow/python/ops/ragged/ragged_segment_ids_to_row_splits_op_test.py @@ -26,6 +26,7 @@ from tensorflow.python.platform import googletest class RaggedSplitsToSegmentIdsOpTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testDocStringExample(self): segment_ids = [0, 0, 0, 2, 2, 3, 4, 4, 4] expected = [0, 3, 3, 5, 6, 9] @@ -33,6 +34,7 @@ class RaggedSplitsToSegmentIdsOpTest(test_util.TensorFlowTestCase): with self.test_session(): self.assertEqual(splits.eval().tolist(), expected) + @test_util.run_deprecated_v1 def testEmptySegmentIds(self): # Note: the splits for an empty ragged tensor contains a single zero. segment_ids = ragged.segment_ids_to_row_splits([]) @@ -49,6 +51,7 @@ class RaggedSplitsToSegmentIdsOpTest(test_util.TensorFlowTestCase): self.assertRaisesRegexp(ValueError, r'Shape \(1, 1\) must have rank 1', ragged.segment_ids_to_row_splits, [[0]]) + @test_util.run_deprecated_v1 def testNumSegments(self): segment_ids = [0, 0, 0, 2, 2, 3, 4, 4, 4] num_segments = 7 @@ -57,6 +60,7 @@ class RaggedSplitsToSegmentIdsOpTest(test_util.TensorFlowTestCase): with self.test_session(): self.assertEqual(splits.eval().tolist(), expected) + @test_util.run_deprecated_v1 def testUnsortedSegmentIds(self): # Segment ids are not required to be sorted. segment_ids = [0, 4, 3, 2, 4, 4, 2, 0, 0] diff --git a/tensorflow/python/ops/ragged/ragged_segment_op_test.py b/tensorflow/python/ops/ragged/ragged_segment_op_test.py index 7d41eb7f75..9e4877ae3e 100644 --- a/tensorflow/python/ops/ragged/ragged_segment_op_test.py +++ b/tensorflow/python/ops/ragged/ragged_segment_op_test.py @@ -110,6 +110,7 @@ class RaggedSegmentOpsTest(test_util.TensorFlowTestCase, (ragged.segment_mean, mean, [5, 4, 3, 2, 1, 0]), (ragged.segment_mean, mean, [0, 0, 0, 10, 10, 10]), ) + @test_util.run_deprecated_v1 def testRaggedSegment_Int(self, segment_op, combiner, segment_ids): rt_as_list = [[0, 1, 2, 3], [4], [], [5, 6], [7], [8, 9]] rt = ragged.constant(rt_as_list) @@ -118,8 +119,7 @@ class RaggedSegmentOpsTest(test_util.TensorFlowTestCase, combiner) segmented = segment_op(rt, segment_ids, num_segments) - with self.test_session(): - self.assertListEqual(segmented.eval().tolist(), expected) + self.assertListEqual(self.evaluate(segmented).tolist(), expected) @parameterized.parameters( (ragged.segment_sum, sum, [0, 0, 1, 1, 2, 2]), @@ -147,6 +147,7 @@ class RaggedSegmentOpsTest(test_util.TensorFlowTestCase, (ragged.segment_sqrt_n, sqrt_n, [5, 4, 3, 2, 1, 0]), (ragged.segment_sqrt_n, sqrt_n, [0, 0, 0, 10, 10, 10]), ) + @test_util.run_deprecated_v1 def testRaggedSegment_Float(self, segment_op, combiner, segment_ids): rt_as_list = [[0., 1., 2., 3.], [4.], [], [5., 6.], [7.], [8., 9.]] rt = ragged.constant(rt_as_list) @@ -155,10 +156,10 @@ class RaggedSegmentOpsTest(test_util.TensorFlowTestCase, combiner) segmented = segment_op(rt, segment_ids, num_segments) - with self.test_session(): - self.assertNestedListAmostEqual( - segmented.eval().tolist(), expected, places=5) + self.assertNestedListAmostEqual( + self.evaluate(segmented).tolist(), expected, places=5) + @test_util.run_deprecated_v1 def testRaggedRankTwo(self): rt = ragged.constant([ [[111, 112, 113, 114], [121],], # row 0 @@ -172,17 +173,16 @@ class RaggedSegmentOpsTest(test_util.TensorFlowTestCase, [], # row 1 [[411, 412], [321, 322], [331]] # row 2 ] # pyformat: disable - with self.test_session(): - self.assertEqual(segmented1.eval().tolist(), expected1) + self.assertEqual(self.evaluate(segmented1).tolist(), expected1) segment_ids2 = [1, 2, 1, 1] segmented2 = ragged.segment_sum(rt, segment_ids2, 3) expected2 = [[], [[111+411, 112+412, 113, 114], [121+321, 322], [331]], []] # pyformat: disable - with self.test_session(): - self.assertEqual(segmented2.eval().tolist(), expected2) + self.assertEqual(self.evaluate(segmented2).tolist(), expected2) + @test_util.run_deprecated_v1 def testRaggedSegmentIds(self): rt = ragged.constant([ [[111, 112, 113, 114], [121],], # row 0 @@ -195,8 +195,7 @@ class RaggedSegmentOpsTest(test_util.TensorFlowTestCase, expected = [[], [111+321, 112+322, 113, 114], [121+331+411, 412]] # pyformat: disable - with self.test_session(): - self.assertEqual(segmented.eval().tolist(), expected) + self.assertEqual(self.evaluate(segmented).tolist(), expected) def testShapeMismatchError1(self): dt = constant_op.constant([1, 2, 3, 4, 5, 6]) @@ -206,6 +205,7 @@ class RaggedSegmentOpsTest(test_util.TensorFlowTestCase, 'but segment_ids is ragged and data is not.', ragged.segment_sum, dt, segment_ids, 3) + @test_util.run_deprecated_v1 def testShapeMismatchError2(self): rt = ragged.constant([ [[111, 112, 113, 114], [121]], # row 0 @@ -226,7 +226,7 @@ class RaggedSegmentOpsTest(test_util.TensorFlowTestCase, array_ops.placeholder_with_default(segment_ids.values, None), array_ops.placeholder_with_default(segment_ids.row_splits, None)) segmented2 = ragged.segment_sum(rt, segment_ids2, 3) - with self.test_session(): + with self.cached_session(): self.assertRaisesRegexp( errors.InvalidArgumentError, 'segment_ids.shape must be a prefix of data.shape.*', segmented2.eval) diff --git a/tensorflow/python/ops/ragged/ragged_stack_op_test.py b/tensorflow/python/ops/ragged/ragged_stack_op_test.py index d474a749f0..4343471694 100644 --- a/tensorflow/python/ops/ragged/ragged_stack_op_test.py +++ b/tensorflow/python/ops/ragged/ragged_stack_op_test.py @@ -265,6 +265,7 @@ class RaggedStackOpTest(test_util.TensorFlowTestCase, parameterized.TestCase): axis=0, expected=[[[b'a00', b'a01'], [], [b'a20', b'a21']]]), ) # pyformat: disable + @test_util.run_deprecated_v1 def testRaggedStack(self, descr, rt_inputs, @@ -313,6 +314,7 @@ class RaggedStackOpTest(test_util.TensorFlowTestCase, parameterized.TestCase): def testError(self, rt_inputs, axis, error, message): self.assertRaisesRegexp(error, message, ragged.stack, rt_inputs, axis) + @test_util.run_deprecated_v1 def testSingleTensorInput(self): """Tests ragged_stack with a single tensor input. diff --git a/tensorflow/python/ops/ragged/ragged_string_ops.py b/tensorflow/python/ops/ragged/ragged_string_ops.py new file mode 100644 index 0000000000..cdcdbdff07 --- /dev/null +++ b/tensorflow/python/ops/ragged/ragged_string_ops.py @@ -0,0 +1,119 @@ +# Copyright 2015 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. +# ============================================================================== +"""Ragged operations for working with string Tensors.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import gen_string_ops +from tensorflow.python.ops.ragged import ragged_conversion_ops +from tensorflow.python.ops.ragged import ragged_factory_ops +from tensorflow.python.ops.ragged import ragged_tensor +from tensorflow.python.util.tf_export import tf_export + + +# pylint: disable=redefined-builtin +@tf_export("strings.unicode_encode") +def unicode_encode(input, output_encoding, errors="replace", + replacement_char=65533, name=None): + r"""Encodes each sequence of Unicode code points in `input` into a string. + + `result[i1...iN]` is the string formed by concatenating the Unicode + codepoints `input[1...iN, :]`, encoded using `output_encoding`. + + Args: + input: An `N+1` dimensional potentially ragged integer tensor with + shape `[D1...DN, num_chars]`. + output_encoding: Unicode encoding that should be used to encode each + codepoint sequence. Can be `"UTF-8"`, `"UTF-16-BE"`, or `"UTF-32-BE"`. + errors: Specifies the response when an invalid codepoint is encountered + (optional). One of: + * `'replace'`: Replace invalid codepoint with the + `replacement_char`. (default) + * `'ignore'`: Skip invalid codepoints. + * `'strict'`: Raise an exception for any invalid codepoint. + replacement_char: The replacement character codepoint to be used in place of + any invalid input when `errors='replace'`. Any valid unicode codepoint may + be used. The default value is the default unicode replacement character + which is 0xFFFD (U+65533). + name: A name for the operation (optional). + + Returns: + A `N` dimensional `string` tensor with shape `[D1...DN]`. + + #### Example: + ```python + >>> input = [[71, 246, 246, 100, 110, 105, 103, 104, 116], [128522]] + >>> unicode_encode(input, 'UTF8') + ['G\xc3\xb6\xc3\xb6dnight', '\xf0\x9f\x98\x8a'] + ``` + """ + with ops.name_scope(name, "UnicodeEncode", [input]): + input_tensor = ragged_factory_ops.convert_to_tensor_or_ragged_tensor(input) + if input_tensor.shape.ndims is None: + raise ValueError("Rank of input_tensor must be statically known.") + if ragged_tensor.is_ragged(input_tensor): + if input_tensor.inner_values.shape.ndims > 1: + # If the inner_values of our ragged tensor is multi-dimensional, we can + # process it separately and our output will have the same nested splits + # as our input. + return input_tensor.with_inner_values( + unicode_encode(input_tensor.inner_values, output_encoding, errors, + replacement_char)) + elif input_tensor.ragged_rank > 1: + # Recursively process the values of the ragged tensor. + return input_tensor.with_values( + unicode_encode(input_tensor.values, output_encoding, errors, + replacement_char)) + else: + # Our ragged tensor is of the correct shape (rank 1 inner_values tensor + # with ragged_rank of 1) so we can process it as normal. + return gen_string_ops.unicode_encode( + input_values=input_tensor.values, + input_splits=input_tensor.row_splits, + output_encoding=output_encoding, + errors=errors, + replacement_char=replacement_char) + else: + if input_tensor.shape.ndims == 2: + # The input tensor is of the correct 2-D shape, it's just not ragged. + return unicode_encode(ragged_conversion_ops.from_tensor(input_tensor), + output_encoding, errors, replacement_char) + elif input_tensor.shape.ndims > 2: + # We need to initially flatten the input tensor to 2-D, and then can + # reshape the output of our processed flattened tensor. + flat_input_tensor = array_ops.reshape( + input_tensor, + array_ops.stack([-1, array_ops.shape(input_tensor)[-1]])) + flat_output_tensor = unicode_encode(flat_input_tensor, output_encoding, + errors, replacement_char) + return array_ops.reshape(flat_output_tensor, input_tensor.shape[:-1]) + elif input_tensor.shape.ndims == 0: + raise ValueError("input_tensor's rank must be at least 1.") + else: + # Our input tensor is rank 1, so we create a ragged tensor with an added + # dimension to create the correct input shape & type, and then remove + # the additional dimension from the output and return the string scalar. + ragged_input_tensor = ragged_factory_ops.from_row_splits( + input_tensor, + array_ops.stack([0, array_ops.shape(input_tensor, + out_type=dtypes.int64)[0]])) + output_tensor = unicode_encode(ragged_input_tensor, output_encoding, + errors, replacement_char) + return array_ops.reshape(output_tensor, []) diff --git a/tensorflow/python/ops/ragged/ragged_tensor.py b/tensorflow/python/ops/ragged/ragged_tensor.py index abb27fc3c0..ddeabfb464 100644 --- a/tensorflow/python/ops/ragged/ragged_tensor.py +++ b/tensorflow/python/ops/ragged/ragged_tensor.py @@ -64,7 +64,7 @@ class RaggedTensor(object): a 3-D `RaggedTensor` that stores the fixed-size word embedding for each word in a sentence, for each sentence in a batch, could be written as `[num_sentences, (num_words), embedding_size]`. The parentheses around - `(num_words)` indicate that that dimension is ragged, and that the length + `(num_words)` indicate that dimension is ragged, and that the length of each element list in that dimension may vary for each item. ### Component Tensors @@ -257,6 +257,7 @@ class RaggedTensor(object): raise TypeError("Row-partitioning argument must be a Tensor.") values.shape.with_rank_at_least(1) row_splits.shape.assert_has_rank(1) + row_splits.set_shape([None]) self._values = values self._row_splits = row_splits diff --git a/tensorflow/python/ops/ragged/ragged_tensor_bounding_shape_op_test.py b/tensorflow/python/ops/ragged/ragged_tensor_bounding_shape_op_test.py index a1c10aff9d..befe30f0e1 100644 --- a/tensorflow/python/ops/ragged/ragged_tensor_bounding_shape_op_test.py +++ b/tensorflow/python/ops/ragged/ragged_tensor_bounding_shape_op_test.py @@ -28,41 +28,39 @@ class RaggedTensorBoundingShapeOp(test_util.TensorFlowTestCase): def testDocStringExample(self): # This is the example from ragged.bounding_shape.__doc__. rt = ragged.constant([[1, 2, 3, 4], [5], [], [6, 7, 8, 9], [10]]) - with self.test_session(): - self.assertEqual(ragged.bounding_shape(rt).eval().tolist(), [5, 4]) + self.assertEqual(self.evaluate(ragged.bounding_shape(rt)).tolist(), [5, 4]) def test2DRaggedTensorWithOneRaggedDimension(self): values = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] rt1 = ragged.from_row_splits(values, [0, 2, 5, 6, 6, 7]) rt2 = ragged.from_row_splits(values, [0, 7]) rt3 = ragged.from_row_splits(values, [0, 0, 7, 7]) - with self.test_session(): - self.assertEqual(ragged.bounding_shape(rt1).eval().tolist(), [5, 3]) - self.assertEqual(ragged.bounding_shape(rt2).eval().tolist(), [1, 7]) - self.assertEqual(ragged.bounding_shape(rt3).eval().tolist(), [3, 7]) + self.assertEqual(self.evaluate(ragged.bounding_shape(rt1)).tolist(), [5, 3]) + self.assertEqual(self.evaluate(ragged.bounding_shape(rt2)).tolist(), [1, 7]) + self.assertEqual(self.evaluate(ragged.bounding_shape(rt3)).tolist(), [3, 7]) def test3DRaggedTensorWithOneRaggedDimension(self): values = [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [10, 11], [12, 13]] rt1 = ragged.from_row_splits(values, [0, 2, 5, 6, 6, 7]) rt2 = ragged.from_row_splits(values, [0, 7]) rt3 = ragged.from_row_splits(values, [0, 0, 7, 7]) - with self.test_session(): - self.assertEqual(ragged.bounding_shape(rt1).eval().tolist(), [5, 3, 2]) - self.assertEqual(ragged.bounding_shape(rt2).eval().tolist(), [1, 7, 2]) - self.assertEqual(ragged.bounding_shape(rt3).eval().tolist(), [3, 7, 2]) + self.assertEqual( + self.evaluate(ragged.bounding_shape(rt1)).tolist(), [5, 3, 2]) + self.assertEqual( + self.evaluate(ragged.bounding_shape(rt2)).tolist(), [1, 7, 2]) + self.assertEqual( + self.evaluate(ragged.bounding_shape(rt3)).tolist(), [3, 7, 2]) def testNonRaggedTensor(self): dt = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]] - with self.test_session(): - self.assertEqual(ragged.bounding_shape(dt).eval().tolist(), [4, 3]) + self.assertEqual(self.evaluate(ragged.bounding_shape(dt)).tolist(), [4, 3]) def testExplicitAxisOptimizations(self): rt = ragged.from_row_splits(b'a b c d e f g'.split(), [0, 2, 5, 6, 6, 7]) - with self.test_session(): - self.assertEqual(ragged.bounding_shape(rt, 0).eval().tolist(), 5) - self.assertEqual(ragged.bounding_shape(rt, 1).eval().tolist(), 3) - self.assertEqual( - ragged.bounding_shape(rt, [1, 0]).eval().tolist(), [3, 5]) + self.assertEqual(self.evaluate(ragged.bounding_shape(rt, 0)).tolist(), 5) + self.assertEqual(self.evaluate(ragged.bounding_shape(rt, 1)).tolist(), 3) + self.assertEqual( + self.evaluate(ragged.bounding_shape(rt, [1, 0])).tolist(), [3, 5]) if __name__ == '__main__': diff --git a/tensorflow/python/ops/ragged/ragged_tensor_shape.py b/tensorflow/python/ops/ragged/ragged_tensor_shape.py new file mode 100644 index 0000000000..9129b4b10b --- /dev/null +++ b/tensorflow/python/ops/ragged/ragged_tensor_shape.py @@ -0,0 +1,570 @@ +# Copyright 2018 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. +# ============================================================================== +"""Shapes & broadcasting for RaggedTensors.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops +from tensorflow.python.framework import tensor_util +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import control_flow_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops.ragged import ragged_array_ops +from tensorflow.python.ops.ragged import ragged_conversion_ops +from tensorflow.python.ops.ragged import ragged_factory_ops +from tensorflow.python.ops.ragged import ragged_tensor +from tensorflow.python.ops.ragged import ragged_util + + +class RaggedTensorDynamicShape(object): + """A collection of tensors encoding the shape of a potentially ragged tensor. + + Each `RaggedTensorDynamicShape` consists of an ordered list of dimension + sizes. There are two dimension types: + + * "Uniform dimensions" are dimenisons where all slices have the same + length. `RaggedTensorDynamicShape` records the size of each uniform + dimension using a single scalar integer. + + * "Ragged dimensions" are dimensions whose slices may have different + lengths. `RaggedTensorDynamicShape` records the size of each ragged + dimension using an integer vector containing the slice lengths for all + the slices across that dimension. + + Furthermore, there are two ways a dimension might be encoded: + + * "Partitioned dimensions" are dimensions that are encoded using a + `RaggedTensor`'s `nested_row_splits`. The outermostmost partitioned + dimension must be uniform, and the innermost partitioned dimension must + be ragged. + + * "Inner dimensions" are dimensions that are encoded using a + `RaggedTensor`'s `inner_values`. Inner dimensions are always uniform. + + The sizes of partitioned dimensions are recorded using `partitioned_dim_sizes` + and `inner_dim_sizes`: + + * `paritioned_dim_sizes` is a list of tensors (one for each partitioned + dimension). + + * For uniform dimensions, the tensor is an integer scalar specifying the + size of all slices across that dimension. + * For ragged dimensions, the tensor is an integer vector specifying the + size of each slice across that dimension. + + * `inner_dim_sizes` is a single integer vector, where each element + specifies the size of a single inner dimension. + + Examples: + + Tensor | Ragged | Partitioned Dim Sizes | Inner Dim + : Rank : : Sizes + ------------------------------ | ------ | ---------------------- | ---------- + `[[1, 2, 3], [4, 5, 6]]` | 0 | | `2, 3` + `[[1, 2], [], [3, 4, 5]]` | 1 | `3, (2, 0, 3)` | + `[[[1, 2], [3, 4]], [[5, 6]]]` | 1 | `2, (2, 1)` | 2 + `[[[1, 2], [3]], [[4, 5]]]` | 2 | `2, (2, 1), (2, 1, 2)` | + """ + + def __init__(self, partitioned_dim_sizes, inner_dim_sizes): + """Creates a RaggedTensorDynamicShape. + + Args: + partitioned_dim_sizes: A `list` of 0-D or 1-D integer `Tensor`, one for + each partitioned dimension. If dimension `d` is uniform, then + `partitioned_dim_sizes[d]` must be an integer scalar, specifying the + size of all slices across dimension `d`. If dimension `d` is ragged, + then `partitioned_dim_sizes[d]` must be an integer vector, specifying + the size of each slice across dimension `d`. + inner_dim_sizes: A 1-D integer `Tensor`, whose length is equal to the + number of inner dimensions. `inner_dim_sizes[n]` is the size of all + slices across the `n`th inner dimension (which is the + `(len(partitioned_dim_sizes)+n)`th dimension in the overall tensor. + """ + assert isinstance(partitioned_dim_sizes, (list, tuple)) + with ops.name_scope(None, 'RaggedTensorDynamicShape', + (partitioned_dim_sizes, inner_dim_sizes)): + partitioned_dim_sizes = tuple( + ragged_util.convert_to_int_tensor( + size, dtype=dtypes.int64, name='partitioned_dimension_size') + for size in partitioned_dim_sizes) + inner_dim_sizes = ragged_util.convert_to_int_tensor( + inner_dim_sizes, dtype=dtypes.int64, name='inner_dim_sizes') + + # Validate shapes. + if partitioned_dim_sizes: + for axis, dimension_size in enumerate(partitioned_dim_sizes): + if dimension_size.shape.ndims is None: + raise ValueError( + 'rank of partitioned_dim_sizes[%d] is unknown' % axis) + dimension_size.shape.with_rank_at_most(1) + if partitioned_dim_sizes[0].shape.ndims == 1: + raise ValueError('outermost partitioned dimension must be uniform') + if partitioned_dim_sizes[-1].shape.ndims == 0: + raise ValueError('innermost partitioned dimension must be ragged') + inner_dim_sizes.shape.assert_has_rank(1) + + self._partitioned_dim_sizes = partitioned_dim_sizes + self._inner_dim_sizes = inner_dim_sizes + + def __repr__(self): + return ('RaggedTensorDynamicShape' + '(partitioned_dim_sizes=%r, inner_dim_sizes=%r)' % + (self._partitioned_dim_sizes, self._inner_dim_sizes)) + + @staticmethod + def from_dim_sizes(dim_sizes): + """Constructs a ragged shape from a list of dimension sizes. + + This list contains a single tensor for each dimension, where the tensor + is a scalar if the dimension is uniform, or a vector if the dimension is + ragged. + + Args: + dim_sizes: List of int64 scalars or vectors. + + Returns: + A RaggedTensorDynamicShape. + """ + with ops.name_scope(None, 'RaggedTensorDynamicShapeFromDimensionSizes', + [dim_sizes]): + dim_sizes = tuple( + ragged_util.convert_to_int_tensor( + size, dtype=dtypes.int64, name='dim_sizes') for size in dim_sizes) + # Split the dimensions into partitioned & inner dimensions. + inner_split = 0 + for dim, dim_size in enumerate(dim_sizes): + if dim_size.shape.ndims == 1: + inner_split = dim + 1 + elif dim_size.shape.ndims != 0: + raise ValueError('Each dim_size must be a scalar or a vector') + return RaggedTensorDynamicShape(dim_sizes[:inner_split], + dim_sizes[inner_split:]) + + @classmethod + def from_tensor(cls, rt_input): + """Constructs a ragged shape for a potentially ragged tensor.""" + with ops.name_scope(None, 'RaggedTensorDynamicShapeFromTensor', [rt_input]): + rt_input = ragged_factory_ops.convert_to_tensor_or_ragged_tensor(rt_input) + if not ragged_tensor.is_ragged(rt_input): + return cls([], array_ops.shape(rt_input)) + else: + partitioned_dim_sizes = ((ragged_array_ops.nrows(rt_input),) + + ragged_array_ops.nested_row_lengths(rt_input)) + return RaggedTensorDynamicShape( + partitioned_dim_sizes, + array_ops.shape(rt_input.inner_values)[1:]) + + def dimension_size(self, axis): + """Returns the size of slices across the specified dimension.""" + if not isinstance(axis, int): + raise TypeError('axis must be an integer') + partitioned_ndims = len(self._partitioned_dim_sizes) + if axis < partitioned_ndims: + return self._partitioned_dim_sizes[axis] + else: + return self._inner_dim_sizes[axis - partitioned_ndims] + + def is_ragged(self, axis): + """Returns true if the indicated dimension is ragged.""" + if not isinstance(axis, int): + raise TypeError('axis must be an integer') + rank = self.rank + if axis < 0: + raise ValueError('Negative axis values are not supported') + elif rank is not None and axis >= rank: + raise ValueError('Expected axis=%s < rank=%s' % (axis, rank)) + else: + return (axis > 0 and axis < len(self._partitioned_dim_sizes) and + self._partitioned_dim_sizes[axis].shape.ndims == 1) + + @property + def rank(self): + """The number of dimensions in this shape, or None if unknown.""" + inner_ndims = self._inner_dim_sizes.shape[0].value + if inner_ndims is None: + return None + else: + return len(self._partitioned_dim_sizes) + inner_ndims + + @property + def partitioned_dim_sizes(self): + """The partitioned dimension sizes for this shape. + + Returns: + A `list` of 0-D or 1-D integer `Tensor`. + """ + return self._partitioned_dim_sizes + + @property + def inner_dim_sizes(self): + """The inner dimension sizes for this shape. + + Returns: + A 1-D integer `Tensor`. + """ + return self._inner_dim_sizes + + @property + def num_partitioned_dimensions(self): + """The number of partitioned dimensions in this shape.""" + return len(self._partitioned_dim_sizes) + + @property + def num_inner_dimensions(self): + """The number of inner dimensions, or `None` if not statically known.""" + return self._inner_dim_sizes.shape[0].value + + def broadcast_to_rank(self, rank): + """Adds leading size-1 dimensions to broadcast `self` to the given rank. + + E.g., if `shape1` is `[3, (D2), 4]`, then `shape1.broadcast_to_rank(5)` + is `[1, 1, 3, (D2), 4]`. + + Args: + rank: The rank for the returned shape. + + Returns: + A RaggedTensorDynamicShape with `rank` dimensions, whose inner dimensions + have the same size as `self` and whose outer dimensions have size `1`. + + Raises: + ValueError: If `self.rank` is unknown or greater than `rank`. + """ + if self.rank is None: + raise ValueError('Unable to broadcast: self.rank is unknown') + dims_to_add = rank - self.rank + if dims_to_add < 0: + raise ValueError('Unable to broadcast: rank=%d must be greater than ' + 'self.rank=%d.' % (rank, self.rank)) + elif dims_to_add == 0: + return self + elif self._partitioned_dim_sizes: + partitioned_dims = (1,) * dims_to_add + self._partitioned_dim_sizes + return RaggedTensorDynamicShape(partitioned_dims, self._inner_dim_sizes) + else: + inner_dims = array_ops.concat( + [array_ops.ones([dims_to_add], dtypes.int64), self.inner_dim_sizes], + axis=0) + return RaggedTensorDynamicShape([], inner_dims) + + def broadcast_dimension(self, axis, lengths): + """Returns a shape that is broadcast-compatible with self & lengths. + + * If dimension[axis] is uniform and lengths is a scalar, the check + that either lengths==1 or axis==1 or lengths==axis, and tile + dimension[axis] with tf.where(lengths==axis, 1, axis) repeats. + + * If dimension[axis] is uniform and lengths is a vector, then check + that dimension[axis]==1, and raggedly tile dimension[axis] with + lengths repeats. (we can skip tiling if we statically know that + slice_lengths == 1??) + + * If dimension[axis] is ragged and lengths is a scalar, then check + that lengths==1. + + * If dimension[axis] is ragged and lengths is a vector, then check + that self.dimension_size(axis) == lengths. + + Args: + axis: `int`. The dimension to broadcast. + lengths: 0-D or 1-D integer `Tensor`. + + Returns: + A `RaggedTensorDynamicShape`. + """ + lengths = ragged_util.convert_to_int_tensor( + lengths, name='lengths', dtype=dtypes.int64) + # Check whether lengths is a scalar (for uniform dimensions) or + # vector (for ragged dimensions). + if lengths.shape.ndims is None: + raise ValueError('lengths must have a known rank.') + elif lengths.shape.ndims > 1: + raise ValueError('lengths must be a scalar or vector') + else: + lengths_is_scalar = (lengths.shape.ndims == 0) + + # Verify that the shapes are compatible. + if self.is_ragged(axis): + if lengths_is_scalar: + condition = math_ops.equal(lengths, 1) + else: + condition = math_ops.reduce_all( + math_ops.equal(lengths, self.dimension_size(axis))) + else: + axis_dim_size = self.dimension_size(axis) + if lengths_is_scalar: + condition = ( + math_ops.equal(lengths, 1) | math_ops.equal(axis_dim_size, 1) + | math_ops.equal(axis_dim_size, lengths)) + else: + condition = math_ops.equal(axis_dim_size, 1) + broadcast_err = [ + 'Unable to broadcast: dimension size mismatch in dimension', axis, + 'lengths=', lengths, 'dim_size=', + self.dimension_size(axis) + ] + broadcast_check = control_flow_ops.Assert( + condition, data=broadcast_err, summarize=10) + + with ops.control_dependencies([broadcast_check]): + # Partitioned dimensions: + if axis < self.num_partitioned_dimensions: + if self.is_ragged(axis): + # Use an identity op to make sure the check actually gets run. + return RaggedTensorDynamicShape( + self._partitioned_dim_sizes, + array_ops.identity(self.inner_dim_sizes)) + else: + return self._broadcast_uniform_partitioned_dimension(axis, lengths) + + # Inner dimensions: + else: + if lengths_is_scalar: + return self._broadcast_inner_dimension_to_uniform(axis, lengths) + else: + if axis == 0: + raise ValueError('Unable to broadcast: ' + 'outermost dimension must be uniform.') + return self._broadcast_inner_dimension_to_ragged(axis, lengths) + + def num_slices_in_dimension(self, axis): + """Returns the total number of slices across the indicated dimension.""" + if axis < 0: + return constant_op.constant(1, dtype=dtypes.int64) + elif self.is_ragged(axis): + return math_ops.reduce_sum(self._partitioned_dim_sizes[axis]) + else: + return self.dimension_size(axis) * self.num_slices_in_dimension(axis - 1) + + def _broadcast_uniform_partitioned_dimension(self, axis, lengths): + """Broadcasts the partitioned dimension `axis` to match `lengths`.""" + axis_dim_size = self.dimension_size(axis) + partitioned_sizes = list(self._partitioned_dim_sizes[:axis]) + + if lengths.shape.ndims == 0: + lengths = array_ops.where( + math_ops.equal(axis_dim_size, 1), lengths, axis_dim_size) + repeats = array_ops.where(math_ops.equal(axis_dim_size, 1), lengths, 1) + splits = array_ops.stack([0, self.num_slices_in_dimension(axis)]) + else: + splits = math_ops.range( + array_ops.size(lengths, out_type=dtypes.int64) + 1) + repeats = lengths + + partitioned_sizes.append(lengths) + + for dim_size in self._partitioned_dim_sizes[axis + 1:]: + if dim_size.shape.ndims == 0: + partitioned_sizes.append(dim_size) + splits *= dim_size + else: + partitioned_sizes.append( + ragged_util.repeat_ranges(dim_size, splits, repeats)) + splits = array_ops.gather( + ragged_util.lengths_to_splits(dim_size), splits) + inner_sizes = self._inner_dim_sizes + return RaggedTensorDynamicShape(partitioned_sizes, inner_sizes) + + def _broadcast_inner_dimension_to_uniform(self, axis, length): + """Broadcasts the inner dimension `axis` to match `lengths`.""" + dim_size = self.dimension_size(axis) + axis_in_inner_dims = axis - self.num_partitioned_dimensions + partitioned_sizes = self._partitioned_dim_sizes + inner_sizes = array_ops.concat([ + self._inner_dim_sizes[:axis_in_inner_dims], + [array_ops.where(math_ops.equal(dim_size, 1), length, dim_size)], + self._inner_dim_sizes[axis_in_inner_dims + 1:] + ], + axis=0) + return RaggedTensorDynamicShape(partitioned_sizes, inner_sizes) + + def _broadcast_inner_dimension_to_ragged(self, axis, lengths): + axis_in_inner_dims = axis - self.num_partitioned_dimensions + partitioned_sizes = ( + self._partitioned_dim_sizes + tuple([ + self._inner_dim_sizes[i] for i in range(axis_in_inner_dims) + ]) + (lengths,)) + inner_sizes = self._inner_dim_sizes[axis_in_inner_dims + 1:] + return RaggedTensorDynamicShape(partitioned_sizes, inner_sizes) + + +def broadcast_dynamic_shape(shape_x, shape_y): + """Returns the shape formed by broadcasting two shapes to be compatible. + + Args: + shape_x: A `RaggedTensorDynamicShape` + shape_y: A `RaggedTensorDynamicShape` + + Returns: + A `RaggedTensorDynamicShape`. + Raises: + ValueError: If `shape_x` and `shape_y` are not broadcast-compatible. + """ + if not isinstance(shape_x, RaggedTensorDynamicShape): + raise TypeError('shape_x must be a RaggedTensorDynamicShape') + if not isinstance(shape_y, RaggedTensorDynamicShape): + raise TypeError('shape_y must be a RaggedTensorDynamicShape') + + # Broadcast both shapes to have the same rank. + if shape_x.rank is None or shape_y.rank is None: + raise ValueError('Unable to broadcast: unknown rank') + broadcast_rank = max(shape_x.rank, shape_y.rank) + shape_x = shape_x.broadcast_to_rank(broadcast_rank) + shape_y = shape_y.broadcast_to_rank(broadcast_rank) + + # Broadcast dimensions one at a time, starting from the outermost dimension. + for axis in range(broadcast_rank): + shape_x = shape_x.broadcast_dimension(axis, shape_y.dimension_size(axis)) + shape_y = shape_y.broadcast_dimension(axis, shape_x.dimension_size(axis)) + + return shape_x + + +def broadcast_to(rt_input, shape, broadcast_inner_dimensions=True): + """Broadcasts a potentially ragged tensor to a ragged shape. + + Tiles `rt_input` as necessary to match the given shape. + + Behavior is undefined if `rt_input` is not broadcast-compatible with `shape`. + + Args: + rt_input: The potentially ragged tensor to broadcast. + shape: A `RaggedTensorDynamicShape` + broadcast_inner_dimensions: If false, then inner dimensions will not be + tiled. + + Returns: + A potentially ragged tensor whose values are taken from + `rt_input`, and whose shape matches `shape`. + """ + if not isinstance(shape, RaggedTensorDynamicShape): + raise TypeError('shape must be a RaggedTensorDynamicShape') + rt_input = ragged_factory_ops.convert_to_tensor_or_ragged_tensor(rt_input) + + # Broadcasting to a uniform shape. + if shape.num_partitioned_dimensions == 0: + return _broadcast_to_uniform_shape(rt_input, shape, + broadcast_inner_dimensions) + else: + return _broadcast_to_ragged_shape(rt_input, shape, + broadcast_inner_dimensions) + + +def _broadcast_to_uniform_shape(rt_input, shape, broadcast_inner_dimensions): + """Broadcasts rt_input to the uniform shape `shape`.""" + if isinstance(rt_input, ragged_tensor.RaggedTensor): + raise ValueError('Incompatible with shape: ragged rank mismatch') + if broadcast_inner_dimensions: + return array_ops.broadcast_to(rt_input, shape.inner_dim_sizes) + else: + return rt_input + + +def _broadcast_to_ragged_shape(rt_input, dst_shape, broadcast_inner_dimensions): + """Broadcasts rt_input to the ragged shape `dst_shape`.""" + # dst_shape's rank and ragged_rank must be greater than or equal to rt_input's + if rt_input.shape.ndims is None or dst_shape.rank is None: + raise ValueError('Unable to broadcast: unknown rank') + if rt_input.shape.ndims > dst_shape.rank: + raise ValueError('Incompatible with shape: rank mismatch') + if (isinstance(rt_input, ragged_tensor.RaggedTensor) and + rt_input.ragged_rank >= dst_shape.num_partitioned_dimensions): + raise ValueError('Incompatible with shape: ragged rank mismatch') + + src_shape = RaggedTensorDynamicShape.from_tensor(rt_input) + src_shape = src_shape.broadcast_to_rank(dst_shape.rank) + + # Add dimensions to rt_input so its rank and ragged_rank matches dst_shape. + if dst_shape.rank > rt_input.shape.ndims: + if rt_input.shape.ndims < dst_shape.num_inner_dimensions + 1: + rt_input = array_ops.reshape( + rt_input, array_ops.concat([[-1], dst_shape.inner_dim_sizes], axis=0)) + for _ in range(dst_shape.rank - rt_input.shape.ndims): + rt_input = ragged_factory_ops.from_row_lengths( + rt_input, [ragged_array_ops.nrows(rt_input)]) + + # Add ragged dimensions to match dst_shape. + if ragged_tensor.is_ragged(rt_input): + inner_rank_diff = ( + rt_input.inner_values.shape.ndims - 1 - dst_shape.num_inner_dimensions) + if inner_rank_diff > 0: + rt_input = rt_input.with_inner_values( + ragged_conversion_ops.from_tensor( + rt_input.inner_values, ragged_rank=inner_rank_diff)) + else: + rt_input = ragged_conversion_ops.from_tensor( + rt_input, ragged_rank=dst_shape.num_partitioned_dimensions - 1) + + # Do broadcasting for any dimensions that will remain uniform. We can do + # these all at once, since they're independent of one another. + multiples = [1] * dst_shape.rank + for axis in range(dst_shape.num_partitioned_dimensions): + if not src_shape.is_ragged(axis) and not dst_shape.is_ragged(axis): + src_size = src_shape.dimension_size(axis) + dst_size = dst_shape.dimension_size(axis) + if ((tensor_util.constant_value(src_size) in (1, None)) and + (tensor_util.constant_value(dst_size) != 1)): + multiples[axis] = array_ops.where( + math_ops.equal(src_size, 1), dst_size, 1) + if not all(isinstance(v, int) and v == 1 for v in multiples): + multiples = array_ops.stack(multiples, axis=0) + rt_input = ragged_array_ops.tile(rt_input, multiples) + + if broadcast_inner_dimensions: + rt_input = rt_input.with_inner_values( + array_ops.reshape( + rt_input.inner_values, + array_ops.concat([[-1], dst_shape.inner_dim_sizes], axis=0))) + + # Do broadcasting for dimensions that become ragged. We must do these from + # outermost to innermost. + for axis in range(dst_shape.num_partitioned_dimensions): + if not src_shape.is_ragged(axis) and dst_shape.is_ragged(axis): + dst_size = dst_shape.dimension_size(axis) + rt_input = _ragged_tile_axis(rt_input, axis, dst_size) + + return rt_input + + +def _ragged_tile_axis(rt_input, axis, repeats): + """Tile a dimension of a RaggedTensor to match a ragged shape.""" + assert axis > 0 # Outermost dimension may not be ragged. + + if not ragged_tensor.is_ragged(rt_input): + rt_input = ragged_conversion_ops.from_tensor(rt_input, ragged_rank=1) + + if axis > 1: + return rt_input.with_values( + _ragged_tile_axis(rt_input.values, axis - 1, repeats)) + else: + src_row_splits = rt_input.nested_row_splits + src_row_lengths = ragged_array_ops.nested_row_lengths(rt_input) + splits = src_row_splits[0] + + dst_row_lengths = [repeats] + for i in range(1, len(src_row_lengths)): + dst_row_lengths.append( + ragged_util.repeat_ranges(src_row_lengths[i], splits, repeats)) + splits = array_ops.gather(src_row_splits[i], splits) + dst_values = ragged_util.repeat_ranges(rt_input.inner_values, splits, + repeats) + return ragged_factory_ops.from_nested_row_lengths(dst_values, + dst_row_lengths) + diff --git a/tensorflow/python/ops/ragged/ragged_tensor_shape_test.py b/tensorflow/python/ops/ragged/ragged_tensor_shape_test.py new file mode 100644 index 0000000000..9c2dd26050 --- /dev/null +++ b/tensorflow/python/ops/ragged/ragged_tensor_shape_test.py @@ -0,0 +1,487 @@ +# Copyright 2018 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 tf.ragged.ragged_tensor_shape.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from absl.testing import parameterized +import numpy as np + +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util +from tensorflow.python.ops import ragged +from tensorflow.python.platform import googletest + + +class RaggedTensorShapeTest(test_util.TensorFlowTestCase, + parameterized.TestCase): + + def assertShapeEq(self, x, y): + assert isinstance(x, ragged.RaggedTensorDynamicShape) + assert isinstance(y, ragged.RaggedTensorDynamicShape) + x_partitioned_dim_sizes = [ + splits.eval().tolist() # + for splits in x.partitioned_dim_sizes + ] + y_partitioned_dim_sizes = [ + splits.eval().tolist() # + for splits in y.partitioned_dim_sizes + ] + self.assertEqual(x_partitioned_dim_sizes, y_partitioned_dim_sizes) + self.assertEqual(x.inner_dim_sizes.eval().tolist(), + y.inner_dim_sizes.eval().tolist()) + + @parameterized.parameters([ + dict(value='x', expected_dim_sizes=[]), + dict(value=['a', 'b', 'c'], expected_dim_sizes=[3]), + dict(value=[['a', 'b', 'c'], ['d', 'e', 'f']], expected_dim_sizes=[2, 3]), + dict( + value=[[['a', 'b', 'c'], ['d', 'e', 'f']]], + expected_dim_sizes=[1, 2, 3]), + dict( + value=ragged.constant_value([['a', 'b', 'c'], ['d', 'e']]), + expected_dim_sizes=[2, [3, 2]]), + dict( + value=ragged.constant_value([[['a', 'b', 'c'], ['d', 'e']]]), + expected_dim_sizes=[1, [2], [3, 2]]), + dict( + value=ragged.constant_value([[['a', 'b', 'c'], ['d', 'e', 'f']]], + ragged_rank=1), + expected_dim_sizes=[1, [2], 3]), + dict( + value=ragged.constant_value([[[[1], [2]], [[3], [4]]], + [[[5], [6]]]], ragged_rank=1), + expected_dim_sizes=[2, [2, 1], 2, 1]), + dict( + value=ragged.constant_value([[10, 20], [30]]), + expected_dim_sizes=[2, [2, 1]]), + # Docstring examples: + dict(value=[[1, 2, 3], [4, 5, 6]], expected_dim_sizes=[2, 3]), + dict( + value=ragged.constant_value([[1, 2], [], [3, 4, 5]]), + expected_dim_sizes=[3, [2, 0, 3]]), + dict( + value=ragged.constant_value([[[1, 2], [3, 4]], [[5, 6]]], + ragged_rank=1), + expected_dim_sizes=[2, [2, 1], 2]), + dict( + value=ragged.constant_value([[[1, 2], [3]], [[4, 5]]]), + expected_dim_sizes=[2, [2, 1], [2, 1, 2]]), + ]) + def testFromTensor(self, value, expected_dim_sizes): + shape = ragged.RaggedTensorDynamicShape.from_tensor(value) + expected = ragged.RaggedTensorDynamicShape.from_dim_sizes( + expected_dim_sizes) + with self.cached_session(): + self.assertShapeEq(shape, expected) + + @parameterized.parameters([ + dict(dim_sizes=[], rank=0, expected_dim_sizes=[]), + dict(dim_sizes=[], rank=3, expected_dim_sizes=[1, 1, 1]), + dict(dim_sizes=[3], rank=1, expected_dim_sizes=[3]), + dict(dim_sizes=[3], rank=3, expected_dim_sizes=[1, 1, 3]), + dict(dim_sizes=[2, 3], rank=3, expected_dim_sizes=[1, 2, 3]), + dict(dim_sizes=[3, [3, 2, 4]], rank=2, expected_dim_sizes=[3, [3, 2, 4]]), + dict( + dim_sizes=[3, [3, 2, 4]], + rank=4, + expected_dim_sizes=[1, 1, 3, [3, 2, 4]]), + dict( + dim_sizes=[3, [3, 2, 4], 2, 3], + rank=5, + expected_dim_sizes=[1, 3, [3, 2, 4], 2, 3]), + ]) + def testBroadcastToRank(self, dim_sizes, rank, expected_dim_sizes): + shape = ragged.RaggedTensorDynamicShape.from_dim_sizes(dim_sizes) + expected = ragged.RaggedTensorDynamicShape.from_dim_sizes( + expected_dim_sizes) + broadcasted_shape = shape.broadcast_to_rank(rank) + with self.cached_session(): + self.assertShapeEq(broadcasted_shape, expected) + self.assertEqual(broadcasted_shape.rank, rank) + + @parameterized.parameters([ + #========================================================================= + # dimension[axis] is uniform inner; and row_lengths is a scalar + #========================================================================= + # shape: [BROADCAST(UNIFORM), UNIFORM, UNIFORM] + dict(axis=0, + row_length=3, + original_dim_sizes=[1, 4, 5], + broadcast_dim_sizes=[3, 4, 5]), + + # shape: [UNIFORM, UNIFORM, BROADCAST(UNIFORM)] + dict(axis=2, + row_length=5, + original_dim_sizes=[3, 4, 1], + broadcast_dim_sizes=[3, 4, 5]), + + # shape: [UNIFORM, RAGGED, BROADCAST(UNIFORM)] + dict(axis=2, + row_length=5, + original_dim_sizes=[3, [3, 2, 8], 1], + broadcast_dim_sizes=[3, [3, 2, 8], 5]), + + # shape: [UNIFORM, RAGGED, RAGGED, UNIFORM, UNIFORM, BROADCAST(UNIFORM)] + dict(axis=5, + row_length=5, + original_dim_sizes=[2, [2, 1], [3, 2, 8], 3, 4, 1], + broadcast_dim_sizes=[2, [2, 1], [3, 2, 8], 3, 4, 5]), + + #========================================================================= + # dimension[axis] is uniform inner; and row_lengths is a vector + #========================================================================= + # shape: [UNIFORM, BROADCAST(UNIFORM)] + dict(axis=1, + row_length=[2, 0, 1], + original_dim_sizes=[3, 1], + broadcast_dim_sizes=[3, [2, 0, 1]]), + # shape: [UNIFORM, BROADCAST(UNIFORM), UNIFORM] + dict(axis=1, + row_length=[2, 0, 1], + original_dim_sizes=[3, 1, 5], + broadcast_dim_sizes=[3, [2, 0, 1], 5]), + + # shape: [UNIFORM, UNIFORM, BROADCAST(UNIFORM)] + dict(axis=2, + row_length=[2, 0, 1, 3, 8, 2, 3, 4, 1, 8, 7, 0], + original_dim_sizes=[4, 3, 1], + broadcast_dim_sizes=[4, 3, [2, 0, 1, 3, 8, 2, 3, 4, 1, 8, 7, 0]]), + + # shape: [UNIFORM, RAGGED, BROADCAST(UNIFORM)] + dict(axis=2, + row_length=[2, 5, 3], + original_dim_sizes=[2, [2, 1], 1], + broadcast_dim_sizes=[2, [2, 1], [2, 5, 3]]), + + # shape: [UNIFORM, RAGGED, UNIFORM, UNIFORM, BROADCAST(UNIFORM), UNIFORM] + dict(axis=4, + row_length=list(range(18)), + original_dim_sizes=[2, [2, 1], 3, 2, 1, 8], + broadcast_dim_sizes=[2, [2, 1], 3, 2, list(range(18)), 8]), + + #========================================================================= + # dimension[axis] is uniform partitioned; and row_lengths is a scalar + #========================================================================= + # shape: [BROADCAST(UNIFORM), RAGGED] + dict(axis=0, + row_length=3, + original_dim_sizes=[1, [5]], + broadcast_dim_sizes=[3, [5, 5, 5]]), + + # shape: [BROADCAST(UNIFORM), UNIFORM, RAGGED] + dict(axis=0, + row_length=2, + original_dim_sizes=[1, 3, [3, 0, 2]], + broadcast_dim_sizes=[2, 3, [3, 0, 2, 3, 0, 2]]), + + # shape: [BROADCAST(UNIFORM), RAGGED, RAGGED, UNIFORM, UNIFORM] + dict(axis=0, + row_length=3, + original_dim_sizes=[1, [3], [3, 5, 2], 9, 4, 5], + broadcast_dim_sizes=[3, [3, 3, 3], [3, 5, 2, 3, 5, 2, 3, 5, 2], + 9, 4, 5]), + + # shape: [BROADCAST(UNIFORM), UNIFORM, RAGGED, UNIFORM] + dict(axis=0, + row_length=2, + original_dim_sizes=[1, 2, [2, 1], [3, 5, 2], 2], + broadcast_dim_sizes=[2, 2, [2, 1, 2, 1], [3, 5, 2, 3, 5, 2], 2]), + + # shape: [UNIFORM, BROADCAST(UNIFORM), RAGGED, UNIFORM] + dict(axis=1, + row_length=2, + original_dim_sizes=[3, 1, [4, 0, 2], 5], + broadcast_dim_sizes=[3, 2, [4, 0, 2, 4, 0, 2], 5]), + + # shape: [UNIFORM, BROADCAST(UNIFORM), RAGGED] + dict(axis=1, + row_length=1, + original_dim_sizes=[2, 3, (1, 2, 3, 4, 5, 6)], + broadcast_dim_sizes=[2, 3, (1, 2, 3, 4, 5, 6)]), + + #========================================================================= + # dimension[axis] is uniform partitioned; and row_lengths is a vector + #========================================================================= + # shape: [UNIFORM, BROADCAST(UNIFORM), RAGGED, UNIFORM] + dict(axis=1, + row_length=[4, 1, 2], + original_dim_sizes=[ + 3, # axis=0 + 1, # axis=1 (broadcast) + [3, 1, 2], # axis=2 + 5], # axis=3 + broadcast_dim_sizes=[ + 3, # axis=0 + [4, 1, 2], # axis=1 (broadcast) + [3, 3, 3, 3, 1, 2, 2], # axis=2 + 5]), # axis=3 + + # shape: [UNIFORM, BROADCAST(UNIFORM), RAGGED, RAGGED] + dict(axis=1, + row_length=[2, 0, 3], + original_dim_sizes=[ + 3, # axis=0 + 1, # axis=1 (broadcast) + [3, 1, 2], # axis=2 + [3, 1, 4, 1, 5, 9]], # axis=3 + broadcast_dim_sizes=[ + 3, # axis=0 + [2, 0, 3], # axis=1 (broadcast) + [3, 3, 2, 2, 2], # axis=2 + [3, 1, 4, 3, 1, 4, 5, 9, 5, 9, 5, 9]]), # axis=3 + + # shape: [UNIFORM, RAGGED, BROADCAST(UNIFORM), RAGGED, RAGGED, UNIFORM] + dict(axis=2, + row_length=[4, 1, 2], + original_dim_sizes=[ + 3, # axis=0 + [2, 0, 1], # axis=1 + 1, # axis=2 (broadcast) + [3, 2, 1], # axis=3 + [1, 0, 1, 0, 2, 3], # axis=4 + 5], # axis=5 + broadcast_dim_sizes=[ + 3, # axis=0 + [2, 0, 1], # axis=2 + [4, 1, 2], # axis=2 (broadcast) + [3, 3, 3, 3, 2, 1, 1], # axis=3 + [1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, # axis=4 + 2, 3, 3], + 5]), # axis=5 + + dict(axis=0, + row_length=2, + original_dim_sizes=[1, 1, 2, (2, 1)], + broadcast_dim_sizes=[2, 1, 2, (2, 1, 2, 1)]), + dict(axis=1, + row_length=(2, 1), + original_dim_sizes=[2, 1, 2, (2, 1, 2, 1)], + broadcast_dim_sizes=[2, (2, 1), 2, (2, 1, 2, 1, 2, 1)]), + dict(axis=2, + row_length=2, + original_dim_sizes=[2, (2, 1), 2, (2, 1, 2, 1, 2, 1)], + broadcast_dim_sizes=[2, (2, 1), 2, (2, 1, 2, 1, 2, 1)]), + dict(axis=3, + row_length=(2, 1, 2, 1, 2, 1), + original_dim_sizes=[2, (2, 1), 2, 1], + broadcast_dim_sizes=[2, (2, 1), 2, (2, 1, 2, 1, 2, 1)]), + ]) # pyformat: disable + def testBroadcastDimension(self, axis, row_length, original_dim_sizes, + broadcast_dim_sizes): + """Tests for the broadcast_dimension method. + + Verifies that: + + * `original.broadcast_dimension(axis, row_length) == broadcast` + * `broadcast.broadcast_dimension(axis, row_length) == broadcast` + * `broadcast.broadcast_dimension(axis, 1) == broadcast` + + Args: + axis: The axis to broadcast + row_length: The slice lengths to broadcast to. + original_dim_sizes: The dimension sizes before broadcasting. + original_dim_sizes[axis] should be equal to `1` or `row_length`. + broadcast_dim_sizes: THe dimension sizes after broadcasting. + """ + original_shape = ragged.RaggedTensorDynamicShape.from_dim_sizes( + original_dim_sizes) + broadcast_shape = ragged.RaggedTensorDynamicShape.from_dim_sizes( + broadcast_dim_sizes) + self.assertEqual(original_shape.rank, broadcast_shape.rank) + with self.cached_session(): + # shape[axis].value == 1 and row_length > 1: + bcast1 = original_shape.broadcast_dimension(axis, row_length) + # shape[axis].value > 1 and row_length == shape[axis].value: + bcast2 = broadcast_shape.broadcast_dimension(axis, row_length) + # shape[axis].value > 1 and row_length == 1: + bcast3 = broadcast_shape.broadcast_dimension(axis, 1) + + self.assertShapeEq(bcast1, broadcast_shape) + self.assertShapeEq(bcast2, broadcast_shape) + self.assertShapeEq(bcast3, broadcast_shape) + + @parameterized.parameters( + [ + # Broadcast scalar + dict(x_dims=[], y_dims=[], expected_dims=[]), + dict(x_dims=[], y_dims=[2], expected_dims=[2]), + dict(x_dims=[], y_dims=[2, 3], expected_dims=[2, 3]), + dict( + x_dims=[], + y_dims=[2, (2, 3), (5, 7, 2, 0, 9)], + expected_dims=[2, (2, 3), (5, 7, 2, 0, 9)]), + # Broadcast vector + dict(x_dims=[3], y_dims=[4, 2, 3], expected_dims=[4, 2, 3]), + dict(x_dims=[1], y_dims=[4, 2, 3], expected_dims=[4, 2, 3]), + dict(x_dims=[3], y_dims=[4, 2, 1], expected_dims=[4, 2, 3]), + dict( + x_dims=[3], + y_dims=[3, (2, 3, 1), 1], + expected_dims=[3, (2, 3, 1), 3]), + dict(x_dims=[1], y_dims=[3, (2, 1, 3)], expected_dims=[3, (2, 1, 3)]), + dict( + x_dims=[1], + y_dims=[3, (2, 1, 3), 8], + expected_dims=[3, (2, 1, 3), 8]), + dict( + x_dims=[1], + y_dims=[2, (2, 3), (5, 7, 2, 0, 9)], + expected_dims=[2, (2, 3), (5, 7, 2, 0, 9)]), + # Mixed broadcasting + dict( + x_dims=[ + 1, # axis=0 + 3, # axis=1 + (3, 0, 2), # axis=2 + 1, # axis=3 + 2, # axis=4 + ], + y_dims=[ + 2, # axis=0 + 1, # axis=1 + 1, # axis=2 + (7, 2), # axis=3 + 1, # axis=4 + ], + expected_dims=[ + 2, # axis=0 + 3, # axis=1 + (3, 0, 2, 3, 0, 2), # axis=2 + (7, 7, 7, 7, 7, 2, 2, 2, 2, 2), # axis=3 + 2, # axis=4 + ]), + dict( + x_dims=[2, (2, 1), 2, 1], + y_dims=[1, 1, 2, (2, 1)], + expected_dims=[2, (2, 1), 2, (2, 1, 2, 1, 2, 1)]), + ]) + def testBroadcastDynamicShape(self, x_dims, y_dims, expected_dims): + x_shape = ragged.RaggedTensorDynamicShape.from_dim_sizes(x_dims) + y_shape = ragged.RaggedTensorDynamicShape.from_dim_sizes(y_dims) + expected = ragged.RaggedTensorDynamicShape.from_dim_sizes(expected_dims) + result1 = ragged.broadcast_dynamic_shape(x_shape, y_shape) + result2 = ragged.broadcast_dynamic_shape(y_shape, x_shape) + with self.cached_session(): + self.assertShapeEq(expected, result1) + self.assertShapeEq(expected, result2) + + def testRepr(self): + shape = ragged.RaggedTensorDynamicShape.from_dim_sizes([2, (2, 1), 2, 1]) + self.assertRegexpMatches( + repr(shape), + r'RaggedTensorDynamicShape\(' + r'partitioned_dim_sizes=\(<[^>]+>, <[^>]+>\), ' + r'inner_dim_sizes=<[^>]+>\)') + + @parameterized.parameters([ + dict( + x=[[10], [20], [30]], # shape=[3, 1] + dim_sizes=[3, 2], + expected=[[10, 10], [20, 20], [30, 30]]), + dict( + x=[[10], [20], [30]], # shape=[3, 1] + dim_sizes=[3, [3, 0, 2]], + expected=ragged.constant_value([[10, 10, 10], [], [30, 30]], + dtype=np.int32)), + dict( + x=[[[1, 2, 3]], [[4, 5, 6]]], # shape = [2, 1, 3] + dim_sizes=[2, [2, 3], 3], + expected=ragged.constant_value( + [[[1, 2, 3], [1, 2, 3]], [[4, 5, 6], [4, 5, 6], [4, 5, 6]]], + dtype=np.int32, + ragged_rank=1)), + dict( + x=[[[1]], [[2]]], # shape = [2, 1, 1] + dim_sizes=[2, [2, 3], [0, 2, 1, 2, 0]], + expected=ragged.constant_value([[[], [1, 1]], [[2], [2, 2], []]], + dtype=np.int32, + ragged_rank=2)), + dict( + x=10, + dim_sizes=[3, [3, 0, 2]], + expected=ragged.constant_value([[10, 10, 10], [], [10, 10]])), + ]) + def testRaggedBroadcastTo(self, x, dim_sizes, expected): + shape = ragged.RaggedTensorDynamicShape.from_dim_sizes(dim_sizes) + result = ragged.broadcast_to(x, shape) + with self.cached_session(): + self.assertEqual( + getattr(result, 'ragged_rank', 0), getattr(expected, 'ragged_rank', + 0)) + if hasattr(expected, 'tolist'): + expected = expected.tolist() + self.assertEqual(result.eval().tolist(), expected) + + @parameterized.parameters([ + dict( + doc='x.shape=[3, (D1)]; y.shape=[3, 1]; bcast.shape=[3, (D1)]', + x=ragged.constant_value([[1, 2, 3], [], [4, 5]], dtype=np.int32), + y=[[10], [20], [30]], + expected=ragged.constant_value([[11, 12, 13], [], [34, 35]])), + dict( + doc='x.shape=[3, (D1)]; y.shape=[]; bcast.shape=[3, (D1)]', + x=ragged.constant_value([[1, 2, 3], [], [4, 5]], dtype=np.int32), + y=10, + expected=ragged.constant_value([[11, 12, 13], [], [14, 15]])), + dict( + doc='x.shape=[1, (D1)]; y.shape=[3, 1]; bcast.shape=[3, (D1)]', + x=ragged.constant_value([[1, 2, 3]], dtype=np.int32), + y=[[10], [20], [30]], + expected=ragged.constant_value( + [[11, 12, 13], [21, 22, 23], [31, 32, 33]], dtype=np.int32)), + dict( + doc=('x.shape=[2, (D1), 1]; y.shape=[1, (D2)]; ' + 'bcast.shape=[2, (D1), (D2)]'), + x=ragged.constant_value([[[1], [2], [3]], [[4]]], ragged_rank=1), + y=ragged.constant_value([[10, 20, 30]]), + expected=ragged.constant_value([[[11, 21, 31], [12, 22, 32], + [13, 23, 33]], [[14, 24, 34]]])), + dict( + doc=('x.shape=[2, (D1), 1]; y.shape=[1, 1, 4]; ' + 'bcast.shape=[2, (D1), 4]'), + x=ragged.constant_value([[[10], [20]], [[30]]], ragged_rank=1), + y=[[[1, 2, 3, 4]]], + expected=ragged.constant_value( + [[[11, 12, 13, 14], [21, 22, 23, 24]], [[31, 32, 33, 34]]], + ragged_rank=1)), + dict( + doc=('x.shape=[2, (D1), 2, 1]; y.shape=[2, (D2)]; ' + 'bcast.shape=[2, (D1), (2), (D2)'), + x=ragged.constant_value([[[[1], [2]], [[3], [4]]], + [[[5], [6]]]], + ragged_rank=1), + y=ragged.constant_value([[10, 20], [30]]), + expected=ragged.constant_value( + [[[[11, 21], [32]], [[13, 23], [34]]], + [[[15, 25], [36]]]])), + ]) + def testRaggedAddWithBroadcasting(self, x, y, expected, doc): + expected_rrank = getattr(expected, 'ragged_rank', 0) + x = ragged.convert_to_tensor_or_ragged_tensor(x, dtype=dtypes.int32) + y = ragged.convert_to_tensor_or_ragged_tensor(y, dtype=dtypes.int32) + result = x + y + result_rrank = getattr(result, 'ragged_rank', 0) + self.assertEqual(expected_rrank, result_rrank) + if hasattr(expected, 'tolist'): + expected = expected.tolist() + with self.cached_session(): + self.assertEqual(result.eval().tolist(), expected) + + +if __name__ == '__main__': + googletest.main() diff --git a/tensorflow/python/ops/ragged/ragged_tensor_test.py b/tensorflow/python/ops/ragged/ragged_tensor_test.py index 61bfcb6809..608fbd6e5b 100644 --- a/tensorflow/python/ops/ragged/ragged_tensor_test.py +++ b/tensorflow/python/ops/ragged/ragged_tensor_test.py @@ -114,13 +114,13 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): # RaggedTensor class docstring examples #============================================================================= + @test_util.run_deprecated_v1 def testClassDocStringExamples(self): # From section: "Component Tensors" rt = ragged.from_row_splits( values=[3, 1, 4, 1, 5, 9, 2, 6], row_splits=[0, 4, 4, 7, 8, 8]) - with self.test_session(): - self.assertEqual(rt.tolist(), - [[3, 1, 4, 1], [], [5, 9, 2], [6], []]) + self.assertEqual( + self.evaluate(rt).tolist(), [[3, 1, 4, 1], [], [5, 9, 2], [6], []]) del rt # From section: "Alternative Row-Partitioning Schemes" @@ -132,9 +132,8 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): rt4 = ragged.from_row_starts(values, row_starts=[0, 4, 4, 7, 8]) rt5 = ragged.from_row_limits(values, row_limits=[4, 4, 7, 8, 8]) for rt in (rt1, rt2, rt3, rt4, rt5): - with self.test_session(): - self.assertEqual(rt.tolist(), - [[3, 1, 4, 1], [], [5, 9, 2], [6], []]) + self.assertEqual( + self.evaluate(rt).tolist(), [[3, 1, 4, 1], [], [5, 9, 2], [6], []]) del rt1, rt2, rt3, rt4, rt5 # From section: "Multiple Ragged Dimensions" @@ -142,28 +141,27 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): values=[3, 1, 4, 1, 5, 9, 2, 6], row_splits=[0, 4, 4, 7, 8, 8]) outer_rt = ragged.from_row_splits(values=inner_rt, row_splits=[0, 3, 3, 5]) self.assertEqual(outer_rt.ragged_rank, 2) - with self.test_session(): - self.assertEqual(outer_rt.tolist(), - [[[3, 1, 4, 1], [], [5, 9, 2]], [], [[6], []]]) + self.assertEqual( + self.evaluate(outer_rt).tolist(), + [[[3, 1, 4, 1], [], [5, 9, 2]], [], [[6], []]]) del inner_rt, outer_rt # From section: "Multiple Ragged Dimensions" rt = ragged.from_nested_row_splits( inner_values=[3, 1, 4, 1, 5, 9, 2, 6], nested_row_splits=([0, 3, 3, 5], [0, 4, 4, 7, 8, 8])) - with self.test_session(): - self.assertEqual(rt.tolist(), - [[[3, 1, 4, 1], [], [5, 9, 2]], [], [[6], []]]) + self.assertEqual( + self.evaluate(rt).tolist(), + [[[3, 1, 4, 1], [], [5, 9, 2]], [], [[6], []]]) del rt # From section: "Uniform Inner Dimensions" rt = ragged.from_row_splits( values=array_ops.ones([5, 3]), row_splits=[0, 2, 5]) - with self.test_session(): - self.assertEqual( - rt.tolist(), - [[[1, 1, 1], [1, 1, 1]], [[1, 1, 1], [1, 1, 1], [1, 1, 1]]]) - self.assertEqual(rt.shape.as_list(), [2, None, 3]) + self.assertEqual( + self.evaluate(rt).tolist(), + [[[1, 1, 1], [1, 1, 1]], [[1, 1, 1], [1, 1, 1], [1, 1, 1]]]) + self.assertEqual(rt.shape.as_list(), [2, None, 3]) del rt #============================================================================= @@ -202,15 +200,16 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): # RaggedTensor Constructor (private) #============================================================================= + @test_util.run_deprecated_v1 def testRaggedTensorConstruction(self): values = constant_op.constant(['a', 'b', 'c', 'd', 'e', 'f', 'g']) row_splits = constant_op.constant([0, 2, 2, 5, 6, 7], dtypes.int64) rt = ragged.RaggedTensor( values=values, row_splits=row_splits, internal=True) - with self.test_session(): - self.assertEqual(rt.tolist(), - [[b'a', b'b'], [], [b'c', b'd', b'e'], [b'f'], [b'g']]) + self.assertEqual( + self.evaluate(rt).tolist(), + [[b'a', b'b'], [], [b'c', b'd', b'e'], [b'f'], [b'g']]) def testRaggedTensorConstructionErrors(self): values = constant_op.constant(['a', 'b', 'c', 'd', 'e', 'f', 'g']) @@ -246,6 +245,7 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): # RaggedTensor Factory Ops #============================================================================= + @test_util.run_deprecated_v1 def testFromValueRowIdsWithDerivedNRows(self): # nrows is known at graph creation time. values = constant_op.constant(['a', 'b', 'c', 'd', 'e', 'f', 'g']) @@ -262,12 +262,13 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): self.assertIs(rt_values, values) self.assertIs(rt_value_rowids, value_rowids) # cached_value_rowids - with self.test_session(): - self.assertAllEqual(rt_value_rowids, value_rowids) - self.assertEqual(rt_nrows.eval(), 5) - self.assertEqual(rt.tolist(), - [[b'a', b'b'], [], [b'c', b'd', b'e'], [b'f'], [b'g']]) + self.assertAllEqual(rt_value_rowids, value_rowids) + self.assertEqual(self.evaluate(rt_nrows), 5) + self.assertEqual( + self.evaluate(rt).tolist(), + [[b'a', b'b'], [], [b'c', b'd', b'e'], [b'f'], [b'g']]) + @test_util.run_deprecated_v1 def testFromValueRowIdsWithDerivedNRowsDynamic(self): # nrows is not known at graph creation time. values = constant_op.constant(['a', 'b', 'c', 'd', 'e', 'f', 'g']) @@ -285,12 +286,13 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): self.assertIs(rt_values, values) self.assertIs(rt_value_rowids, value_rowids) # cached_value_rowids - with self.test_session(): - self.assertAllEqual(rt_value_rowids, value_rowids) - self.assertEqual(rt_nrows.eval(), 5) - self.assertEqual(rt.tolist(), - [[b'a', b'b'], [], [b'c', b'd', b'e'], [b'f'], [b'g']]) + self.assertAllEqual(rt_value_rowids, value_rowids) + self.assertEqual(self.evaluate(rt_nrows), 5) + self.assertEqual( + self.evaluate(rt).tolist(), + [[b'a', b'b'], [], [b'c', b'd', b'e'], [b'f'], [b'g']]) + @test_util.run_deprecated_v1 def testFromValueRowIdsWithExplicitNRows(self): values = constant_op.constant(['a', 'b', 'c', 'd', 'e', 'f', 'g']) value_rowids = constant_op.constant([0, 0, 2, 2, 2, 3, 4], dtypes.int64) @@ -308,11 +310,11 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): self.assertIs(rt_values, values) self.assertIs(rt_value_rowids, value_rowids) # cached_value_rowids self.assertIs(rt_nrows, nrows) # cached_nrows - with self.test_session(): - self.assertEqual( - rt.tolist(), - [[b'a', b'b'], [], [b'c', b'd', b'e'], [b'f'], [b'g'], [], []]) + self.assertEqual( + self.evaluate(rt).tolist(), + [[b'a', b'b'], [], [b'c', b'd', b'e'], [b'f'], [b'g'], [], []]) + @test_util.run_deprecated_v1 def testFromValueRowIdsWithExplicitNRowsEqualToDefault(self): values = constant_op.constant(['a', 'b', 'c', 'd', 'e', 'f', 'g']) value_rowids = constant_op.constant([0, 0, 2, 2, 2, 3, 4], dtypes.int64) @@ -330,12 +332,13 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): self.assertIs(rt_values, values) self.assertIs(rt_value_rowids, value_rowids) # cached_value_rowids self.assertIs(rt_nrows, nrows) # cached_nrows - with self.test_session(): - self.assertAllEqual(rt_value_rowids, value_rowids) - self.assertAllEqual(rt_nrows, nrows) - self.assertEqual(rt.tolist(), - [[b'a', b'b'], [], [b'c', b'd', b'e'], [b'f'], [b'g']]) + self.assertAllEqual(rt_value_rowids, value_rowids) + self.assertAllEqual(rt_nrows, nrows) + self.assertEqual( + self.evaluate(rt).tolist(), + [[b'a', b'b'], [], [b'c', b'd', b'e'], [b'f'], [b'g']]) + @test_util.run_deprecated_v1 def testFromValueRowIdsWithEmptyValues(self): rt = ragged.from_value_rowids([], []) rt_nrows = ragged.nrows(rt) @@ -344,10 +347,10 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): self.assertEqual(rt.ragged_rank, 1) self.assertEqual(rt.values.shape.as_list(), [0]) self.assertEqual(ragged.value_rowids(rt).shape.as_list(), [0]) - with self.test_session(): - self.assertEqual(rt_nrows.eval().tolist(), 0) - self.assertEqual(rt.tolist(), []) + self.assertEqual(self.evaluate(rt_nrows).tolist(), 0) + self.assertEqual(self.evaluate(rt).tolist(), []) + @test_util.run_deprecated_v1 def testFromRowSplits(self): values = constant_op.constant(['a', 'b', 'c', 'd', 'e', 'f', 'g']) row_splits = constant_op.constant([0, 2, 2, 5, 6, 7], dtypes.int64) @@ -363,16 +366,17 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): self.assertIs(rt_values, values) self.assertIs(rt_row_splits, row_splits) - with self.test_session(): - self.assertEqual(rt_nrows.eval(), 5) - self.assertEqual(rt.tolist(), - [[b'a', b'b'], [], [b'c', b'd', b'e'], [b'f'], [b'g']]) + self.assertEqual(self.evaluate(rt_nrows), 5) + self.assertEqual( + self.evaluate(rt).tolist(), + [[b'a', b'b'], [], [b'c', b'd', b'e'], [b'f'], [b'g']]) def testFromRowSplitsWithEmptySplits(self): err_msg = 'row_splits tensor may not be empty' with self.assertRaisesRegexp(ValueError, err_msg): ragged.from_row_splits([], []) + @test_util.run_deprecated_v1 def testFromRowStarts(self): values = constant_op.constant(['a', 'b', 'c', 'd', 'e', 'f', 'g']) row_starts = constant_op.constant([0, 2, 2, 5, 6], dtypes.int64) @@ -387,12 +391,13 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): rt_nrows = ragged.nrows(rt) self.assertIs(rt_values, values) - with self.test_session(): - self.assertEqual(rt_nrows.eval(), 5) - self.assertAllEqual(rt_row_starts, row_starts) - self.assertEqual(rt.tolist(), - [[b'a', b'b'], [], [b'c', b'd', b'e'], [b'f'], [b'g']]) + self.assertEqual(self.evaluate(rt_nrows), 5) + self.assertAllEqual(rt_row_starts, row_starts) + self.assertEqual( + self.evaluate(rt).tolist(), + [[b'a', b'b'], [], [b'c', b'd', b'e'], [b'f'], [b'g']]) + @test_util.run_deprecated_v1 def testFromRowLimits(self): values = constant_op.constant(['a', 'b', 'c', 'd', 'e', 'f', 'g']) row_limits = constant_op.constant([2, 2, 5, 6, 7], dtypes.int64) @@ -407,12 +412,13 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): rt_nrows = ragged.nrows(rt) self.assertIs(rt_values, values) - with self.test_session(): - self.assertEqual(rt_nrows.eval(), 5) - self.assertAllEqual(rt_row_limits, row_limits) - self.assertEqual(rt.tolist(), - [[b'a', b'b'], [], [b'c', b'd', b'e'], [b'f'], [b'g']]) + self.assertEqual(self.evaluate(rt_nrows), 5) + self.assertAllEqual(rt_row_limits, row_limits) + self.assertEqual( + self.evaluate(rt).tolist(), + [[b'a', b'b'], [], [b'c', b'd', b'e'], [b'f'], [b'g']]) + @test_util.run_deprecated_v1 def testFromRowLengths(self): values = constant_op.constant(['a', 'b', 'c', 'd', 'e', 'f', 'g']) row_lengths = constant_op.constant([2, 0, 3, 1, 1], dtypes.int64) @@ -428,12 +434,13 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): self.assertIs(rt_values, values) self.assertIs(rt_row_lengths, row_lengths) # cached_nrows - with self.test_session(): - self.assertEqual(rt_nrows.eval(), 5) - self.assertAllEqual(rt_row_lengths, row_lengths) - self.assertEqual(rt.tolist(), - [[b'a', b'b'], [], [b'c', b'd', b'e'], [b'f'], [b'g']]) + self.assertEqual(self.evaluate(rt_nrows), 5) + self.assertAllEqual(rt_row_lengths, row_lengths) + self.assertEqual( + self.evaluate(rt).tolist(), + [[b'a', b'b'], [], [b'c', b'd', b'e'], [b'f'], [b'g']]) + @test_util.run_deprecated_v1 def testFromNestedValueRowIdsWithDerivedNRows(self): values = constant_op.constant(['a', 'b', 'c', 'd', 'e', 'f', 'g']) nested_value_rowids = [ @@ -452,13 +459,13 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): rt_values_value_rowids = ragged.value_rowids(rt_values) self.assertIs(rt_values_values, values) - with self.test_session(): - self.assertAllEqual(rt_value_rowids, nested_value_rowids[0]) - self.assertAllEqual(rt_values_value_rowids, nested_value_rowids[1]) - self.assertEqual( - rt.tolist(), - [[[b'a', b'b'], []], [[b'c', b'd', b'e']], [], [[b'f'], [b'g']]]) + self.assertAllEqual(rt_value_rowids, nested_value_rowids[0]) + self.assertAllEqual(rt_values_value_rowids, nested_value_rowids[1]) + self.assertEqual( + self.evaluate(rt).tolist(), + [[[b'a', b'b'], []], [[b'c', b'd', b'e']], [], [[b'f'], [b'g']]]) + @test_util.run_deprecated_v1 def testFromNestedValueRowIdsWithExplicitNRows(self): values = constant_op.constant(['a', 'b', 'c', 'd', 'e', 'f', 'g']) nested_value_rowids = [ @@ -483,14 +490,14 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): rt_values_nrows = ragged.nrows(rt_values) self.assertIs(rt_values_values, values) - with self.test_session(): - self.assertAllEqual(rt_value_rowids, nested_value_rowids[0]) - self.assertAllEqual(rt_values_value_rowids, nested_value_rowids[1]) - self.assertAllEqual(rt_nrows, nrows[0]) - self.assertAllEqual(rt_values_nrows, nrows[1]) - self.assertEqual(rt.tolist(), - [[[b'a', b'b'], []], [[b'c', b'd', b'e']], [], - [[b'f'], [b'g'], []], [], []]) + self.assertAllEqual(rt_value_rowids, nested_value_rowids[0]) + self.assertAllEqual(rt_values_value_rowids, nested_value_rowids[1]) + self.assertAllEqual(rt_nrows, nrows[0]) + self.assertAllEqual(rt_values_nrows, nrows[1]) + self.assertEqual( + self.evaluate(rt).tolist(), + [[[b'a', b'b'], []], [[b'c', b'd', b'e']], [], [[b'f'], [b'g'], []], [], + []]) def testFromNestedValueRowIdsWithExplicitNRowsMismatch(self): values = constant_op.constant(['a', 'b', 'c', 'd', 'e', 'f', 'g']) @@ -515,6 +522,7 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): ragged.from_nested_value_rowids([1, 2, 3], [[0, 1, 2], [0, 1, 2]], constant_op.constant([3, 3])) + @test_util.run_deprecated_v1 def testFromNestedRowSplits(self): inner_values = constant_op.constant(['a', 'b', 'c', 'd', 'e', 'f', 'g']) nested_row_splits = [ @@ -535,10 +543,9 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): self.assertIs(rt_values_values, inner_values) self.assertIs(rt_row_splits, nested_row_splits[0]) self.assertIs(rt_values_row_splits, nested_row_splits[1]) - with self.test_session(): - self.assertEqual( - rt.tolist(), - [[[b'a', b'b'], []], [[b'c', b'd', b'e']], [], [[b'f'], [b'g']]]) + self.assertEqual( + self.evaluate(rt).tolist(), + [[[b'a', b'b'], []], [[b'c', b'd', b'e']], [], [[b'f'], [b'g']]]) def testFromNestedRowSplitsWithNonListInput(self): with self.assertRaisesRegexp(TypeError, @@ -583,6 +590,7 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): value_rowids=value_rowids, nrows=array_ops.expand_dims(nrows, 0)) + @test_util.run_deprecated_v1 def testGraphMismatch(self): with ops.Graph().as_default(): values = constant_op.constant([1, 2, 3]) @@ -595,6 +603,7 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): # Ragged Value & Row-Partitioning Tensor Accessors #============================================================================= + @test_util.run_deprecated_v1 def testRaggedTensorAccessors_2d(self): values = constant_op.constant(['a', 'b', 'c', 'd', 'e', 'f', 'g']) row_splits = constant_op.constant([0, 2, 2, 5, 6, 7], dtypes.int64) @@ -603,25 +612,33 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): rt2 = ragged.from_value_rowids(values, value_rowids) for rt in [rt1, rt2]: - with self.test_session(): - self.assertEqual(rt.tolist(), - [[b'a', b'b'], [], [b'c', b'd', b'e'], [b'f'], [b'g']]) - self.assertEqual(rt.values.eval().tolist(), - [b'a', b'b', b'c', b'd', b'e', b'f', b'g']) - self.assertEqual(rt.values.shape.dims[0].value, 7) - self.assertEqual( - ragged.value_rowids(rt).eval().tolist(), [0, 0, 2, 2, 2, 3, 4]) - self.assertEqual(ragged.nrows(rt).eval().tolist(), 5) - self.assertEqual(rt.row_splits.eval().tolist(), [0, 2, 2, 5, 6, 7]) - self.assertEqual(ragged.row_starts(rt).eval().tolist(), [0, 2, 2, 5, 6]) - self.assertEqual(ragged.row_limits(rt).eval().tolist(), [2, 2, 5, 6, 7]) - self.assertEqual( - ragged.row_lengths(rt).eval().tolist(), [2, 0, 3, 1, 1]) - self.assertEqual(rt.inner_values.eval().tolist(), - [b'a', b'b', b'c', b'd', b'e', b'f', b'g']) - self.assertEqual([s.eval().tolist() for s in rt.nested_row_splits], - [[0, 2, 2, 5, 6, 7]]) + self.assertEqual( + self.evaluate(rt).tolist(), + [[b'a', b'b'], [], [b'c', b'd', b'e'], [b'f'], [b'g']]) + self.assertEqual( + self.evaluate(rt.values).tolist(), + [b'a', b'b', b'c', b'd', b'e', b'f', b'g']) + self.assertEqual(rt.values.shape.dims[0].value, 7) + self.assertEqual( + self.evaluate(ragged.value_rowids(rt)).tolist(), + [0, 0, 2, 2, 2, 3, 4]) + self.assertEqual(self.evaluate(ragged.nrows(rt)).tolist(), 5) + self.assertEqual( + self.evaluate(rt.row_splits).tolist(), [0, 2, 2, 5, 6, 7]) + self.assertEqual( + self.evaluate(ragged.row_starts(rt)).tolist(), [0, 2, 2, 5, 6]) + self.assertEqual( + self.evaluate(ragged.row_limits(rt)).tolist(), [2, 2, 5, 6, 7]) + self.assertEqual( + self.evaluate(ragged.row_lengths(rt)).tolist(), [2, 0, 3, 1, 1]) + self.assertEqual( + self.evaluate(rt.inner_values).tolist(), + [b'a', b'b', b'c', b'd', b'e', b'f', b'g']) + self.assertEqual( + [self.evaluate(s).tolist() for s in rt.nested_row_splits], + [[0, 2, 2, 5, 6, 7]]) + @test_util.run_deprecated_v1 def testRaggedTensorAccessors_3d_with_ragged_rank_1(self): values = [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [10, 11], [12, 13]] row_splits = constant_op.constant([0, 2, 2, 5, 6, 7], dtypes.int64) @@ -630,28 +647,34 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): rt2 = ragged.from_value_rowids(values, value_rowids) for rt in [rt1, rt2]: - with self.test_session(): - self.assertEqual(rt.tolist(), - [[[0, 1], [2, 3]], [], [[4, 5], [6, 7], [8, 9]], - [[10, 11]], [[12, 13]]]) - self.assertEqual( - rt.values.eval().tolist(), - [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [10, 11], [12, 13]]) - self.assertEqual(rt.values.shape.dims[0].value, 7) - self.assertEqual( - ragged.value_rowids(rt).eval().tolist(), [0, 0, 2, 2, 2, 3, 4]) - self.assertEqual(ragged.nrows(rt).eval().tolist(), 5) - self.assertEqual(rt.row_splits.eval().tolist(), [0, 2, 2, 5, 6, 7]) - self.assertEqual(ragged.row_starts(rt).eval().tolist(), [0, 2, 2, 5, 6]) - self.assertEqual(ragged.row_limits(rt).eval().tolist(), [2, 2, 5, 6, 7]) - self.assertEqual( - ragged.row_lengths(rt).eval().tolist(), [2, 0, 3, 1, 1]) - self.assertEqual( - rt.inner_values.eval().tolist(), - [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [10, 11], [12, 13]]) - self.assertEqual([s.eval().tolist() for s in rt.nested_row_splits], - [[0, 2, 2, 5, 6, 7]]) + self.assertEqual( + self.evaluate(rt).tolist(), + [[[0, 1], [2, 3]], [], [[4, 5], [6, 7], [8, 9]], [[10, 11]], + [[12, 13]]]) + self.assertEqual( + self.evaluate(rt.values).tolist(), + [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [10, 11], [12, 13]]) + self.assertEqual(rt.values.shape.dims[0].value, 7) + self.assertEqual( + self.evaluate(ragged.value_rowids(rt)).tolist(), + [0, 0, 2, 2, 2, 3, 4]) + self.assertEqual(self.evaluate(ragged.nrows(rt)).tolist(), 5) + self.assertEqual( + self.evaluate(rt.row_splits).tolist(), [0, 2, 2, 5, 6, 7]) + self.assertEqual( + self.evaluate(ragged.row_starts(rt)).tolist(), [0, 2, 2, 5, 6]) + self.assertEqual( + self.evaluate(ragged.row_limits(rt)).tolist(), [2, 2, 5, 6, 7]) + self.assertEqual( + self.evaluate(ragged.row_lengths(rt)).tolist(), [2, 0, 3, 1, 1]) + self.assertEqual( + self.evaluate(rt.inner_values).tolist(), + [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [10, 11], [12, 13]]) + self.assertEqual( + [self.evaluate(s).tolist() for s in rt.nested_row_splits], + [[0, 2, 2, 5, 6, 7]]) + @test_util.run_deprecated_v1 def testRaggedTensorAccessors_3d_with_ragged_rank_2(self): values = constant_op.constant(['a', 'b', 'c', 'd', 'e', 'f', 'g']) nested_row_splits = [ @@ -666,41 +689,45 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): rt2 = ragged.from_nested_value_rowids(values, nested_value_rowids) for rt in [rt1, rt2]: - with self.test_session(): - self.assertEqual( - rt.tolist(), - [[[b'a', b'b'], []], [[b'c', b'd', b'e']], [], [[b'f'], [b'g']]]) - self.assertEqual(rt.values.eval().tolist(), - [[b'a', b'b'], [], [b'c', b'd', b'e'], [b'f'], [b'g']]) - self.assertEqual(rt.values.shape.dims[0].value, 5) - self.assertEqual( - ragged.value_rowids(rt).eval().tolist(), [0, 0, 1, 3, 3]) - self.assertEqual(ragged.nrows(rt).eval().tolist(), 4) - self.assertEqual(rt.row_splits.eval().tolist(), [0, 2, 3, 3, 5]) - self.assertEqual(ragged.row_starts(rt).eval().tolist(), [0, 2, 3, 3]) - self.assertEqual(ragged.row_limits(rt).eval().tolist(), [2, 3, 3, 5]) - self.assertEqual(ragged.row_lengths(rt).eval().tolist(), [2, 1, 0, 2]) - self.assertEqual(rt.inner_values.eval().tolist(), - [b'a', b'b', b'c', b'd', b'e', b'f', b'g']) - self.assertEqual([s.eval().tolist() for s in rt.nested_row_splits], - [[0, 2, 3, 3, 5], [0, 2, 2, 5, 6, 7]]) + self.assertEqual( + self.evaluate(rt).tolist(), + [[[b'a', b'b'], []], [[b'c', b'd', b'e']], [], [[b'f'], [b'g']]]) + self.assertEqual( + self.evaluate(rt.values).tolist(), + [[b'a', b'b'], [], [b'c', b'd', b'e'], [b'f'], [b'g']]) + self.assertEqual(rt.values.shape.dims[0].value, 5) + self.assertEqual( + self.evaluate(ragged.value_rowids(rt)).tolist(), [0, 0, 1, 3, 3]) + self.assertEqual(self.evaluate(ragged.nrows(rt)).tolist(), 4) + self.assertEqual(self.evaluate(rt.row_splits).tolist(), [0, 2, 3, 3, 5]) + self.assertEqual( + self.evaluate(ragged.row_starts(rt)).tolist(), [0, 2, 3, 3]) + self.assertEqual( + self.evaluate(ragged.row_limits(rt)).tolist(), [2, 3, 3, 5]) + self.assertEqual( + self.evaluate(ragged.row_lengths(rt)).tolist(), [2, 1, 0, 2]) + self.assertEqual( + self.evaluate(rt.inner_values).tolist(), + [b'a', b'b', b'c', b'd', b'e', b'f', b'g']) + self.assertEqual( + [self.evaluate(s).tolist() for s in rt.nested_row_splits], + [[0, 2, 3, 3, 5], [0, 2, 2, 5, 6, 7]]) def testNRowsWithTensorInput(self): dt = constant_op.constant([[1, 2, 3], [4, 5, 6]]) nrows = ragged.nrows(dt) - with self.test_session(): - self.assertEqual(nrows.eval(), 2) + self.assertEqual(self.evaluate(nrows), 2) def testRowLengthsWithTensorInput(self): dt = constant_op.constant([[1, 2, 3], [4, 5, 6]]) row_lengths = ragged.row_lengths(dt) - with self.test_session(): - self.assertEqual(row_lengths.eval().tolist(), [3, 3]) + self.assertEqual(self.evaluate(row_lengths).tolist(), [3, 3]) #============================================================================= # RaggedTensor.shape #============================================================================= + @test_util.run_deprecated_v1 def testShape(self): """Tests for RaggedTensor.shape.""" rt1 = ragged.from_row_splits(b'a b c d e f g'.split(), [0, 2, 5, 6, 6, 7]) @@ -748,29 +775,27 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): expected: The expected value of rt.__getitem__(slice_spec), as a python list; or an exception class. """ - with self.test_session(): - tensor_slice_spec1 = _make_tensor_slice_spec(slice_spec, True) - tensor_slice_spec2 = _make_tensor_slice_spec(slice_spec, False) - value1 = rt.__getitem__(slice_spec).eval() - value2 = rt.__getitem__(tensor_slice_spec1).eval() - value3 = rt.__getitem__(tensor_slice_spec2).eval() - if hasattr(value1, 'tolist'): - value1 = value1.tolist() - if hasattr(value2, 'tolist'): - value2 = value2.tolist() - if hasattr(value3, 'tolist'): - value3 = value3.tolist() - self.assertEqual(value1, expected, 'slice_spec=%s' % (slice_spec,)) - self.assertEqual(value2, expected, 'slice_spec=%s' % (slice_spec,)) - self.assertEqual(value3, expected, 'slice_spec=%s' % (slice_spec,)) + tensor_slice_spec1 = _make_tensor_slice_spec(slice_spec, True) + tensor_slice_spec2 = _make_tensor_slice_spec(slice_spec, False) + value1 = self.evaluate(rt.__getitem__(slice_spec)) + value2 = self.evaluate(rt.__getitem__(tensor_slice_spec1)) + value3 = self.evaluate(rt.__getitem__(tensor_slice_spec2)) + if hasattr(value1, 'tolist'): + value1 = value1.tolist() + if hasattr(value2, 'tolist'): + value2 = value2.tolist() + if hasattr(value3, 'tolist'): + value3 = value3.tolist() + self.assertEqual(value1, expected, 'slice_spec=%s' % (slice_spec,)) + self.assertEqual(value2, expected, 'slice_spec=%s' % (slice_spec,)) + self.assertEqual(value3, expected, 'slice_spec=%s' % (slice_spec,)) def _TestGetItemException(self, rt, slice_spec, expected, message): """Helper function for testing RaggedTensor.__getitem__ exceptions.""" - with self.test_session(): - tensor_slice_spec1 = _make_tensor_slice_spec(slice_spec, True) - self.assertRaisesRegexp(expected, message, rt.__getitem__, slice_spec) - self.assertRaisesRegexp(expected, message, rt.__getitem__, - tensor_slice_spec1) + tensor_slice_spec1 = _make_tensor_slice_spec(slice_spec, True) + self.assertRaisesRegexp(expected, message, rt.__getitem__, slice_spec) + self.assertRaisesRegexp(expected, message, rt.__getitem__, + tensor_slice_spec1) @parameterized.parameters( # Tests for rt[i] @@ -836,14 +861,14 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): (SLICE_BUILDER[:, -2:], [row[-2:] for row in EXAMPLE_RAGGED_TENSOR_2D]), # TODO(edloper): Add tests for strided slices, once support is added. ) + @test_util.run_deprecated_v1 def testRaggedTensorGetItemWithRaggedRank1(self, slice_spec, expected): """Test that rt.__getitem__(slice_spec) == expected.""" # Ragged tensor rt = ragged.from_row_splits(EXAMPLE_RAGGED_TENSOR_2D_VALUES, EXAMPLE_RAGGED_TENSOR_2D_SPLITS) - with self.test_session(): - self.assertEqual(rt.tolist(), EXAMPLE_RAGGED_TENSOR_2D) + self.assertEqual(self.evaluate(rt).tolist(), EXAMPLE_RAGGED_TENSOR_2D) self._TestGetItem(rt, slice_spec, expected) # pylint: disable=invalid-slice-index @@ -878,6 +903,7 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): (SLICE_BUILDER[..., 0, 0, 0], IndexError, 'Too many indices for RaggedTensor'), ) + @test_util.run_deprecated_v1 def testRaggedTensorGetItemErrorsWithRaggedRank1(self, slice_spec, expected, message): """Test that rt.__getitem__(slice_spec) == expected.""" @@ -887,8 +913,7 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): # if sys.version_info[0] == 3: # message = 'must be str, not int' - with self.test_session(): - self.assertEqual(rt.tolist(), EXAMPLE_RAGGED_TENSOR_2D) + self.assertEqual(self.evaluate(rt).tolist(), EXAMPLE_RAGGED_TENSOR_2D) self._TestGetItemException(rt, slice_spec, expected, message) @parameterized.parameters( @@ -957,13 +982,13 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): # TODO(edloper): Add tests slicing inner ragged dimensions, one support # is added. ) + @test_util.run_deprecated_v1 def testRaggedTensorGetItemWithRaggedRank2(self, slice_spec, expected): """Test that rt.__getitem__(slice_spec) == expected.""" rt = ragged.from_nested_row_splits( EXAMPLE_RAGGED_TENSOR_4D_VALUES, [EXAMPLE_RAGGED_TENSOR_4D_SPLITS1, EXAMPLE_RAGGED_TENSOR_4D_SPLITS2]) - with self.test_session(): - self.assertEqual(rt.tolist(), EXAMPLE_RAGGED_TENSOR_4D) + self.assertEqual(self.evaluate(rt).tolist(), EXAMPLE_RAGGED_TENSOR_4D) self._TestGetItem(rt, slice_spec, expected) @parameterized.parameters( @@ -979,14 +1004,14 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): (SLICE_BUILDER[5], ValueError, '.*out of bounds.*'), (SLICE_BUILDER[0, 5], ValueError, '.*out of bounds.*'), ) + @test_util.run_deprecated_v1 def testRaggedTensorGetItemErrorsWithRaggedRank2(self, slice_spec, expected, message): """Test that rt.__getitem__(slice_spec) == expected.""" rt = ragged.from_nested_row_splits( EXAMPLE_RAGGED_TENSOR_4D_VALUES, [EXAMPLE_RAGGED_TENSOR_4D_SPLITS1, EXAMPLE_RAGGED_TENSOR_4D_SPLITS2]) - with self.test_session(): - self.assertEqual(rt.tolist(), EXAMPLE_RAGGED_TENSOR_4D) + self.assertEqual(self.evaluate(rt).tolist(), EXAMPLE_RAGGED_TENSOR_4D) self._TestGetItemException(rt, slice_spec, expected, message) @parameterized.parameters( @@ -994,6 +1019,7 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): (SLICE_BUILDER[2:], []), (SLICE_BUILDER[:-3], []), ) + @test_util.run_deprecated_v1 def testRaggedTensorGetItemWithEmptyTensor(self, slice_spec, expected): """Test that rt.__getitem__(slice_spec) == expected.""" rt = ragged.from_row_splits([], [0]) @@ -1003,6 +1029,7 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): (SLICE_BUILDER[0], ValueError, '.*out of bounds.*'), (SLICE_BUILDER[-1], ValueError, '.*out of bounds.*'), ) + @test_util.run_deprecated_v1 def testRaggedTensorGetItemErrorsWithEmptyTensor(self, slice_spec, expected, message): """Test that rt.__getitem__(slice_spec) == expected.""" @@ -1018,6 +1045,7 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): (SLICE_BUILDER[0, 1], EXAMPLE_RAGGED_TENSOR_2D[0][1]), (SLICE_BUILDER[-3, 0], EXAMPLE_RAGGED_TENSOR_2D[-3][0]), ) + @test_util.run_deprecated_v1 def testRaggedTensorGetItemWithPlaceholderShapes(self, slice_spec, expected): """Test that rt.__getitem__(slice_spec) == expected.""" # Intentionally use an unknown shape for `splits`, to force the code path @@ -1026,13 +1054,13 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): EXAMPLE_RAGGED_TENSOR_2D_SPLITS, dtype=dtypes.int64) splits = array_ops.placeholder_with_default(splits, None) rt = ragged.from_row_splits(EXAMPLE_RAGGED_TENSOR_2D_VALUES, splits) - with self.test_session(): - self.assertEqual(rt.tolist(), EXAMPLE_RAGGED_TENSOR_2D) + self.assertEqual(self.evaluate(rt).tolist(), EXAMPLE_RAGGED_TENSOR_2D) self._TestGetItem(rt, slice_spec, expected) @parameterized.parameters( (SLICE_BUILDER[..., 2], ValueError, 'Ellipsis not supported for unknown shape RaggedTensors'),) + @test_util.run_deprecated_v1 def testRaggedTensorGetItemErrorsWithPlaceholderShapes( self, slice_spec, expected, message): """Test that rt.__getitem__(slice_spec) == expected.""" @@ -1041,55 +1069,55 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): rt = ragged.from_row_splits(values, [0, 1]) self._TestGetItemException(rt, slice_spec, expected, message) - # TODO(edloper): Remove this decorator once c shapes become the default. - @test_util.enable_c_shapes + @test_util.run_deprecated_v1 def testGetItemNewAxis(self): # rt: [[[['a', 'b'], ['c', 'd']], [], [['e', 'f']]], []] splits1 = [0, 3, 3] splits2 = [0, 2, 2, 3] values = constant_op.constant([['a', 'b'], ['c', 'd'], ['e', 'f']]) rt = ragged.from_nested_row_splits(values, [splits1, splits2]) - with self.test_session(): - rt_newaxis0 = rt[array_ops.newaxis] - rt_newaxis1 = rt[:, array_ops.newaxis] - rt_newaxis2 = rt[:, :, array_ops.newaxis] - rt_newaxis3 = rt[:, :, :, array_ops.newaxis] - rt_newaxis4 = rt[:, :, :, :, array_ops.newaxis] - - self.assertEqual(rt.tolist(), - [[[[b'a', b'b'], [b'c', b'd']], [], [[b'e', b'f']]], []]) - self.assertEqual( - rt_newaxis0.tolist(), - [[[[[b'a', b'b'], [b'c', b'd']], [], [[b'e', b'f']]], []]]) - self.assertEqual( - rt_newaxis1.tolist(), - [[[[[b'a', b'b'], [b'c', b'd']], [], [[b'e', b'f']]]], [[]]]) - self.assertEqual( - rt_newaxis2.tolist(), - [[[[[b'a', b'b'], [b'c', b'd']]], [[]], [[[b'e', b'f']]]], []]) - self.assertEqual( - rt_newaxis3.tolist(), - [[[[[b'a', b'b']], [[b'c', b'd']]], [], [[[b'e', b'f']]]], []]) - self.assertEqual( - rt_newaxis4.tolist(), - [[[[[b'a'], [b'b']], [[b'c'], [b'd']]], [], [[[b'e'], [b'f']]]], []]) - - self.assertEqual(rt.ragged_rank, 2) - self.assertEqual(rt_newaxis0.ragged_rank, 3) - self.assertEqual(rt_newaxis1.ragged_rank, 3) - self.assertEqual(rt_newaxis2.ragged_rank, 3) - self.assertEqual(rt_newaxis3.ragged_rank, 2) - self.assertEqual(rt_newaxis4.ragged_rank, 2) - - self.assertEqual(rt_newaxis0.shape.as_list(), [1, None, None, None, 2]) - self.assertEqual(rt_newaxis1.shape.as_list(), [2, None, None, None, 2]) - self.assertEqual(rt_newaxis2.shape.as_list(), [2, None, None, None, 2]) - self.assertEqual(rt_newaxis3.shape.as_list(), [2, None, None, 1, 2]) - self.assertEqual(rt_newaxis4.shape.as_list(), [2, None, None, 2, 1]) + rt_newaxis0 = rt[array_ops.newaxis] + rt_newaxis1 = rt[:, array_ops.newaxis] + rt_newaxis2 = rt[:, :, array_ops.newaxis] + rt_newaxis3 = rt[:, :, :, array_ops.newaxis] + rt_newaxis4 = rt[:, :, :, :, array_ops.newaxis] + + self.assertEqual( + self.evaluate(rt).tolist(), + [[[[b'a', b'b'], [b'c', b'd']], [], [[b'e', b'f']]], []]) + self.assertEqual( + self.evaluate(rt_newaxis0).tolist(), + [[[[[b'a', b'b'], [b'c', b'd']], [], [[b'e', b'f']]], []]]) + self.assertEqual( + self.evaluate(rt_newaxis1).tolist(), + [[[[[b'a', b'b'], [b'c', b'd']], [], [[b'e', b'f']]]], [[]]]) + self.assertEqual( + self.evaluate(rt_newaxis2).tolist(), + [[[[[b'a', b'b'], [b'c', b'd']]], [[]], [[[b'e', b'f']]]], []]) + self.assertEqual( + self.evaluate(rt_newaxis3).tolist(), + [[[[[b'a', b'b']], [[b'c', b'd']]], [], [[[b'e', b'f']]]], []]) + self.assertEqual( + self.evaluate(rt_newaxis4).tolist(), + [[[[[b'a'], [b'b']], [[b'c'], [b'd']]], [], [[[b'e'], [b'f']]]], []]) + + self.assertEqual(rt.ragged_rank, 2) + self.assertEqual(rt_newaxis0.ragged_rank, 3) + self.assertEqual(rt_newaxis1.ragged_rank, 3) + self.assertEqual(rt_newaxis2.ragged_rank, 3) + self.assertEqual(rt_newaxis3.ragged_rank, 2) + self.assertEqual(rt_newaxis4.ragged_rank, 2) + + self.assertEqual(rt_newaxis0.shape.as_list(), [1, None, None, None, 2]) + self.assertEqual(rt_newaxis1.shape.as_list(), [2, None, None, None, 2]) + self.assertEqual(rt_newaxis2.shape.as_list(), [2, None, None, None, 2]) + self.assertEqual(rt_newaxis3.shape.as_list(), [2, None, None, 1, 2]) + self.assertEqual(rt_newaxis4.shape.as_list(), [2, None, None, 2, 1]) #============================================================================= # RaggedTensor.__str__ #============================================================================= + @test_util.run_deprecated_v1 def testRaggedTensorStr(self): rt1 = ragged.from_row_splits(b'a b c d e f g'.split(), [0, 2, 5, 6, 6, 7]) expected1 = ('RaggedTensor(values=Tensor("RaggedFromRowSplits/values:0", ' @@ -1127,6 +1155,7 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): # RaggedTensor.with_values() and RaggedTensor.with_inner_values(). #============================================================================= + @test_util.run_deprecated_v1 def testWithValues(self): rt1 = ragged.constant([[1, 2], [3, 4, 5], [6], [], [7]]) rt2 = ragged.constant([[[1, 2], [3, 4, 5]], [[6]], [], [[], [7]]]) @@ -1135,17 +1164,20 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): rt2_times_10 = rt2.with_inner_values(rt2.inner_values * 10) rt1_expanded = rt1.with_values(array_ops.expand_dims(rt1.values, axis=1)) - with self.test_session(): - self.assertEqual(rt1_plus_10.tolist(), - [[11, 12], [13, 14, 15], [16], [], [17]]) - self.assertEqual(rt2_times_10.tolist(), - [[[10, 20], [30, 40, 50]], [[60]], [], [[], [70]]]) - self.assertEqual(rt1_expanded.tolist(), - [[[1], [2]], [[3], [4], [5]], [[6]], [], [[7]]]) + self.assertEqual( + self.evaluate(rt1_plus_10).tolist(), + [[11, 12], [13, 14, 15], [16], [], [17]]) + self.assertEqual( + self.evaluate(rt2_times_10).tolist(), + [[[10, 20], [30, 40, 50]], [[60]], [], [[], [70]]]) + self.assertEqual( + self.evaluate(rt1_expanded).tolist(), + [[[1], [2]], [[3], [4], [5]], [[6]], [], [[7]]]) #============================================================================= # Session.run #============================================================================= + @test_util.run_deprecated_v1 def testSessionRun(self): rt1 = ragged.constant([[1, 2, 3], [4]]) rt2 = ragged.constant([[[], [1, 2]], [[3]]]) @@ -1155,6 +1187,7 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): self.assertEqual(result['rt1'].tolist(), [[1, 2, 3], [4]]) self.assertEqual(result['rt2'].tolist(), [[[], [1, 2]], [[3]]]) + @test_util.run_deprecated_v1 def testSessionRunFeed(self): rt1 = ragged.from_row_splits( array_ops.placeholder(dtypes.int32), @@ -1175,6 +1208,7 @@ class RaggedTensorTest(test_util.TensorFlowTestCase, parameterized.TestCase): self.assertEqual(result['rt1'].tolist(), [[1, 2, 3], [4]]) self.assertEqual(result['rt2'].tolist(), [[[], [1, 2]], [[3]]]) + @test_util.run_deprecated_v1 def testSessionPartialRunFeed(self): # Placeholder inputs. a = ragged.from_row_splits( diff --git a/tensorflow/python/ops/ragged/ragged_tile_op_test.py b/tensorflow/python/ops/ragged/ragged_tile_op_test.py index bf62d96e7a..f335b15dd1 100644 --- a/tensorflow/python/ops/ragged/ragged_tile_op_test.py +++ b/tensorflow/python/ops/ragged/ragged_tile_op_test.py @@ -170,8 +170,18 @@ class RaggedTileOpTest(test_util.TensorFlowTestCase, parameterized.TestCase): rt_input=[[[[1], [2]], [[3]]], [[]], [[[4, 5]]]], multiples=[1, 1, 1, 0], expected=[[[[], []], [[]]], [[]], [[[]]]]), + #========================================================================= + # multiple=1 + #========================================================================= + dict( + descr='rank=4, multiples=1 (no repeats)', + rt_input=[[[[1], [2]], [[3], [4]]], [[[5], [6]]]], + multiples=[1, 1, 1, 1], + expected=[[[[1], [2]], [[3], [4]]], + [[[5], [6]]]]), ]) # pyformat: disable + @test_util.run_deprecated_v1 def testRaggedTile(self, descr, rt_input, @@ -200,6 +210,7 @@ class RaggedTileOpTest(test_util.TensorFlowTestCase, parameterized.TestCase): with self.test_session(): self.assertEqual(tiled.eval().tolist(), expected) + @test_util.run_deprecated_v1 def testRaggedTileWithTensorInput(self): # When the input is a `Tensor`, ragged_tile just delegates to tf.tile. dt = constant_op.constant([[1, 2], [3, 4]]) diff --git a/tensorflow/python/ops/ragged/ragged_to_sparse_op_test.py b/tensorflow/python/ops/ragged/ragged_to_sparse_op_test.py index 2fd31837c6..69b31ad0e9 100644 --- a/tensorflow/python/ops/ragged/ragged_to_sparse_op_test.py +++ b/tensorflow/python/ops/ragged/ragged_to_sparse_op_test.py @@ -23,12 +23,14 @@ from tensorflow.python.framework import errors from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import gradients_impl +from tensorflow.python.ops import math_ops from tensorflow.python.ops import ragged from tensorflow.python.platform import googletest class RaggedTensorToSparseOpTest(test_util.TensorFlowTestCase): + @test_util.run_deprecated_v1 def testDocStringExample(self): rt = ragged.constant([[1, 2, 3], [4], [], [5, 6]]) st = ragged.to_sparse(rt) @@ -39,6 +41,7 @@ class RaggedTensorToSparseOpTest(test_util.TensorFlowTestCase): with self.test_session(): self.assertEqual(' '.join(repr(st.eval()).split()), expected) + @test_util.run_deprecated_v1 def test2DRaggedTensorWithOneRaggedDimension(self): rt = ragged.constant([['a', 'b'], ['c', 'd', 'e'], ['f'], [], ['g']]) with self.test_session(): @@ -48,6 +51,7 @@ class RaggedTensorToSparseOpTest(test_util.TensorFlowTestCase): self.assertAllEqual(st.values, b'a b c d e f g'.split()) self.assertAllEqual(st.dense_shape, [5, 3]) + @test_util.run_deprecated_v1 def test3DRaggedTensorWithOneRaggedDimension(self): rt = ragged.constant([[[1, 2], [3, 4]], [[5, 6], [7, 8], [9, 10]], [[11, 12]], [], [[13, 14]]], @@ -62,6 +66,7 @@ class RaggedTensorToSparseOpTest(test_util.TensorFlowTestCase): [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]) self.assertAllEqual(st.dense_shape, [5, 3, 2]) + @test_util.run_deprecated_v1 def test4DRaggedTensorWithOneRaggedDimension(self): rt = ragged.constant( [[[[1, 2], [3, 4]], [[5, 6], [7, 8]]], [], [[[9, 10], [11, 12]]]], @@ -87,6 +92,7 @@ class RaggedTensorToSparseOpTest(test_util.TensorFlowTestCase): ]) self.assertAllEqual(st.dense_shape, [3, 2, 2, 2]) + @test_util.run_deprecated_v1 def test4DRaggedTensorWithTwoRaggedDimensions(self): rt = ragged.constant([[[[1, 2], [3, 4]], [[5, 6], [7, 8], [9, 10]]], [[[11, 12]], [], [[13, 14]]], []], @@ -134,6 +140,7 @@ class RaggedTensorToSparseOpTest(test_util.TensorFlowTestCase): self.assertEqual(st.values.shape.as_list(), [7]) self.assertEqual(st.dense_shape.shape.as_list(), [3]) + @test_util.run_deprecated_v1 def testKernelErrors(self): # An empty vector, defined using a placeholder to ensure that we can't # determine that it's invalid at graph-construction time. @@ -172,13 +179,14 @@ class RaggedTensorToSparseOpTest(test_util.TensorFlowTestCase): self.assertRaisesRegexp(errors.InvalidArgumentError, empty_splits_error, ragged.to_sparse(bad_rt5).eval) + @test_util.run_deprecated_v1 def testGradient(self): # rt1.shape == rt2.shape == [2, (D2), (D3), 2]. rt1 = ragged.constant([[[[1.0, 2.0], [3.0, 4.0]], [[5.0, 6.0]]]], ragged_rank=2) rt2 = ragged.constant([[[[9.0, 8.0], [7.0, 6.0]], [[5.0, 4.0]]]], ragged_rank=2) - rt = rt1 + rt2 * 2.0 + rt = ragged.map_inner_values(math_ops.add, rt1, rt2 * 2.0) st = ragged.to_sparse(rt) g1, g2 = gradients_impl.gradients(st.values, [rt1.inner_values, diff --git a/tensorflow/python/ops/ragged/ragged_to_tensor_op_test.py b/tensorflow/python/ops/ragged/ragged_to_tensor_op_test.py index 0ccc214a9c..77499b9cb3 100644 --- a/tensorflow/python/ops/ragged/ragged_to_tensor_op_test.py +++ b/tensorflow/python/ops/ragged/ragged_to_tensor_op_test.py @@ -30,6 +30,7 @@ from tensorflow.python.platform import googletest class RaggedTensorToTensorOpTest(test_util.TensorFlowTestCase, parameterized.TestCase): + @test_util.run_deprecated_v1 def testDocStringExamples(self): """Example from ragged_to_tensor.__doc__.""" rt = ragged.constant([[9, 8, 7], [], [6, 5], [4]]) @@ -71,10 +72,33 @@ class RaggedTensorToTensorOpTest(test_util.TensorFlowTestCase, [[1, 2], [0, 0], [3, 4]], # [[0, 0], [0, 0], [0, 0]], # [[5, 0], [0, 0], [0, 0]], # - [[6, 7], [8, 0], [0, 0]] - ] # + [[6, 7], [8, 0], [0, 0]], # + ] + }, + { + 'rt_input': [[[1, 2], [], [3, 4]], [], [[5]], [[6, 7], [8]]], + 'default': + 9, + 'expected': [ + [[1, 2], [9, 9], [3, 4]], # + [[9, 9], [9, 9], [9, 9]], # + [[5, 9], [9, 9], [9, 9]], # + [[6, 7], [8, 9], [9, 9]], # + ] + }, + { + 'rt_input': [[[1], [2], [3]]], + 'ragged_rank': 1, + 'default': 0, + 'expected': [[[1], [2], [3]]], + }, + { + 'rt_input': [[[[1], [2]], [], [[3]]]], + 'default': 9, + 'expected': [[[[1], [2]], [[9], [9]], [[3], [9]]]], }, ) + @test_util.run_deprecated_v1 def testRaggedTensorToTensor(self, rt_input, expected, @@ -96,17 +120,13 @@ class RaggedTensorToTensorOpTest(test_util.TensorFlowTestCase, { 'rt_input': [[1, 2, 3]], 'default': [0], - 'error': (ValueError, r'Shapes \(1,\) and \(\) are incompatible'), - }, - { - 'rt_input': [[[1], [2], [3]]], - 'default': 0, - 'error': (ValueError, r'Shapes \(\) and \(1,\) are incompatible'), + 'error': (ValueError, r'Shape \(1,\) must have rank at most 0'), }, { - 'rt_input': [[[[1], [2]], [], [[3]]]], - 'default': 0, - 'error': (ValueError, r'Shapes \(\) and \(1,\) are incompatible'), + 'rt_input': [[[1, 2], [3, 4]], [[5, 6]]], + 'ragged_rank': 1, + 'default': [7, 8, 9], + 'error': (ValueError, r'Shapes \(3,\) and \(2,\) are incompatible'), }, { 'rt_input': [[1, 2, 3]], @@ -114,9 +134,11 @@ class RaggedTensorToTensorOpTest(test_util.TensorFlowTestCase, 'error': (TypeError, "Expected int32, got 'a' of type 'str' instead"), }, ) - def testError(self, rt_input, default, error): - rt = ragged.constant(rt_input) - self.assertRaisesRegexp(error[0], error[1], ragged.to_tensor, rt, default) + @test_util.run_deprecated_v1 + def testError(self, rt_input, default, error, ragged_rank=None): + rt = ragged.constant(rt_input, ragged_rank=ragged_rank) + with self.assertRaisesRegexp(error[0], error[1]): + ragged.to_tensor(rt, default) if __name__ == '__main__': diff --git a/tensorflow/python/ops/ragged/ragged_util.py b/tensorflow/python/ops/ragged/ragged_util.py index 03f050de51..a832f937d1 100644 --- a/tensorflow/python/ops/ragged/ragged_util.py +++ b/tensorflow/python/ops/ragged/ragged_util.py @@ -25,6 +25,7 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.ops import array_ops from tensorflow.python.ops import check_ops +from tensorflow.python.ops import gen_ragged_math_ops from tensorflow.python.ops import math_ops @@ -229,3 +230,51 @@ def _with_nonzero_rank(data): return array_ops.reshape( data, array_ops.concat([[1], data_shape], axis=0)[-data_ndims:]) + + +def lengths_to_splits(lengths): + """Returns splits corresponding to the given lengths.""" + return array_ops.concat([[0], math_ops.cumsum(lengths)], axis=-1) + + +def repeat_ranges(params, splits, repeats): + """Repeats each range of `params` (as specified by `splits`) `repeats` times. + + Let the `i`th range of `params` be defined as + `params[splits[i]:splits[i + 1]]`. Then this function returns a tensor + containing range 0 repeated `repeats[0]` times, followed by range 1 repeated + `repeats[1]`, ..., followed by the last range repeated `repeats[-1]` times. + + Args: + params: The `Tensor` whose values should be repeated. + splits: A splits tensor indicating the ranges of `params` that should be + repeated. + repeats: The number of times each range should be repeated. Supports + broadcasting from a scalar value. + + Returns: + A `Tensor` with the same rank and type as `params`. + + #### Example: + ```python + >>> repeat_ranges(['a', 'b', 'c'], [0, 2, 3], 3) + ['a', 'b', 'a', 'b', 'a', 'b', 'c', 'c', 'c'] + ``` + """ + # Divide `splits` into starts and limits, and repeat them `repeats` times. + if repeats.shape.ndims != 0: + repeated_starts = repeat(splits[:-1], repeats, axis=0) + repeated_limits = repeat(splits[1:], repeats, axis=0) + else: + # Optimization: we can just call repeat once, and then slice the result. + repeated_splits = repeat(splits, repeats, axis=0) + n_splits = array_ops.shape(repeated_splits, out_type=dtypes.int64)[0] + repeated_starts = repeated_splits[:n_splits - repeats] + repeated_limits = repeated_splits[repeats:] + + # Get indices for each range from starts to limits, and use those to gather + # the values in the desired repetition pattern. + one = array_ops.ones((), repeated_starts.dtype) + offsets = gen_ragged_math_ops.ragged_range( + repeated_starts, repeated_limits, one) + return array_ops.gather(params, offsets.rt_dense_values) diff --git a/tensorflow/python/ops/ragged/ragged_where_op_test.py b/tensorflow/python/ops/ragged/ragged_where_op_test.py index 755333de39..de83a54977 100644 --- a/tensorflow/python/ops/ragged/ragged_where_op_test.py +++ b/tensorflow/python/ops/ragged/ragged_where_op_test.py @@ -165,12 +165,13 @@ class RaggedWhereOpTest(test_util.TensorFlowTestCase, parameterized.TestCase): y=ragged.constant_value([[[['a']]], [[['b']]]]), expected=ragged.constant_value([[[[], [b'A']]], [[[b'b']]]])), ]) # pyformat: disable + @test_util.run_deprecated_v1 def testRaggedWhere(self, condition, expected, x=None, y=None): result = ragged.where(condition, x, y) self.assertEqual( getattr(result, 'ragged_rank', 0), getattr(expected, 'ragged_rank', 0)) with self.test_session(): - result_value = result.eval() + result_value = self.evaluate(result) if hasattr(result_value, 'tolist'): result_value = result_value.tolist() if hasattr(expected, 'tolist'): diff --git a/tensorflow/python/ops/random_ops.py b/tensorflow/python/ops/random_ops.py index 1f7db0af61..62e2f6d102 100644 --- a/tensorflow/python/ops/random_ops.py +++ b/tensorflow/python/ops/random_ops.py @@ -138,7 +138,9 @@ def parameterized_truncated_normal(shape, return rnd -@tf_export("random.truncated_normal", "truncated_normal") +@tf_export("random.truncated_normal", + v1=["random.truncated_normal", "truncated_normal"]) +@deprecation.deprecated_endpoints("truncated_normal") def truncated_normal(shape, mean=0.0, stddev=1.0, @@ -325,7 +327,9 @@ def random_crop(value, size, seed=None, name=None): return array_ops.slice(value, offset, size, name=name) -@tf_export("random.multinomial", "multinomial") +@tf_export(v1=["random.multinomial", "multinomial"]) +@deprecation.deprecated( + date=None, instructions="Use tf.random.categorical instead.") def multinomial(logits, num_samples, seed=None, name=None, output_dtype=None): """Draws samples from a multinomial distribution. @@ -342,9 +346,7 @@ def multinomial(logits, num_samples, seed=None, name=None, output_dtype=None): `[i, :]` represents the unnormalized log-probabilities for all classes. num_samples: 0-D. Number of independent samples to draw for each row slice. seed: A Python integer. Used to create a random seed for the distribution. - See - `tf.set_random_seed` - for behavior. + See `tf.set_random_seed` for behavior. name: Optional name for the operation. output_dtype: integer type to use for the output. Defaults to int64. @@ -352,10 +354,43 @@ def multinomial(logits, num_samples, seed=None, name=None, output_dtype=None): The drawn samples of shape `[batch_size, num_samples]`. """ with ops.name_scope(name, "multinomial", [logits]): - logits = ops.convert_to_tensor(logits, name="logits") - seed1, seed2 = random_seed.get_seed(seed) - return gen_random_ops.multinomial( - logits, num_samples, seed=seed1, seed2=seed2, output_dtype=output_dtype) + return multinomial_categorical_impl(logits, num_samples, output_dtype, seed) + + +@tf_export("random.categorical") +def categorical(logits, num_samples, dtype=None, seed=None, name=None): + """Draws samples from a categorical distribution. + + Example: + + ```python + # samples has shape [1, 5], where each value is either 0 or 1 with equal + # probability. + samples = tf.random.categorical(tf.log([[10., 10.]]), 5) + ``` + + Args: + logits: 2-D Tensor with shape `[batch_size, num_classes]`. Each slice + `[i, :]` represents the unnormalized log-probabilities for all classes. + num_samples: 0-D. Number of independent samples to draw for each row slice. + dtype: integer type to use for the output. Defaults to int64. + seed: A Python integer. Used to create a random seed for the distribution. + See `tf.set_random_seed` for behavior. + name: Optional name for the operation. + + Returns: + The drawn samples of shape `[batch_size, num_samples]`. + """ + with ops.name_scope(name, "categorical", [logits]): + return multinomial_categorical_impl(logits, num_samples, dtype, seed) + + +def multinomial_categorical_impl(logits, num_samples, dtype, seed): + """Implementation for random.multinomial (v1) and random.categorical (v2).""" + logits = ops.convert_to_tensor(logits, name="logits") + seed1, seed2 = random_seed.get_seed(seed) + return gen_random_ops.multinomial( + logits, num_samples, seed=seed1, seed2=seed2, output_dtype=dtype) ops.NotDifferentiable("Multinomial") @@ -445,7 +480,7 @@ def random_gamma(shape, shape, alpha_broadcast, seed=seed1, seed2=seed2) / beta) -@tf_export("random.poisson", v1=["random.poisson", "random_poisson"]) +@tf_export(v1=["random.poisson", "random_poisson"]) @deprecation.deprecated_endpoints("random_poisson") def random_poisson(lam, shape, dtype=dtypes.float32, seed=None, name=None): """Draws `shape` samples from each of the given Poisson distribution(s). @@ -478,6 +513,45 @@ def random_poisson(lam, shape, dtype=dtypes.float32, seed=None, name=None): for behavior. name: Optional name for the operation. + Returns: + samples: a `Tensor` of shape `tf.concat([shape, tf.shape(lam)], axis=0)` + with values of type `dtype`. + """ + return random_poisson_v2(shape, lam, dtype, seed, name) + + +@tf_export("random.poisson", v1=[]) +def random_poisson_v2(shape, lam, dtype=dtypes.float32, seed=None, name=None): + """Draws `shape` samples from each of the given Poisson distribution(s). + + `lam` is the rate parameter describing the distribution(s). + + Example: + + ```python + samples = tf.random_poisson([10], [0.5, 1.5]) + # samples has shape [10, 2], where each slice [:, 0] and [:, 1] represents + # the samples drawn from each distribution + + samples = tf.random_poisson([7, 5], [12.2, 3.3]) + # samples has shape [7, 5, 2], where each slice [:, :, 0] and [:, :, 1] + # represents the 7x5 samples drawn from each of the two distributions + ``` + + Args: + shape: A 1-D integer Tensor or Python array. The shape of the output samples + to be drawn per "rate"-parameterized distribution. + lam: A Tensor or Python value or N-D array of type `dtype`. + `lam` provides the rate parameter(s) describing the poisson + distribution(s) to sample. + dtype: The type of the output: `float16`, `float32`, `float64`, `int32` or + `int64`. + seed: A Python integer. Used to create a random seed for the distributions. + See + `tf.set_random_seed` + for behavior. + name: Optional name for the operation. + Returns: samples: a `Tensor` of shape `tf.concat([shape, tf.shape(lam)], axis=0)` with values of type `dtype`. diff --git a/tensorflow/python/ops/resource_variable_ops.py b/tensorflow/python/ops/resource_variable_ops.py index 488b6fcbcd..1066b357b4 100644 --- a/tensorflow/python/ops/resource_variable_ops.py +++ b/tensorflow/python/ops/resource_variable_ops.py @@ -26,6 +26,7 @@ from tensorflow.core.framework import variable_pb2 from tensorflow.python import pywrap_tensorflow from tensorflow.python.eager import context from tensorflow.python.eager import tape +from tensorflow.python.framework import constant_op from tensorflow.python.framework import cpp_shape_inference_pb2 from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops @@ -64,6 +65,7 @@ def eager_safe_variable_handle(shape, dtype, shared_name, name, graph_mode): name=name, container=container) if graph_mode: + handle._handle_data = get_resource_handle_data(handle) # pylint: disable=protected-access return handle # We do not want two distinct ResourceVariable objects for the same @@ -519,7 +521,10 @@ class ResourceVariable(variables.RefVariable): snapshot = g.as_graph_element( ops.prepend_name_scope( variable_def.snapshot_name, import_scope=import_scope)) - self._cached_value = snapshot + if snapshot.op.type != "ReadVariableOp": + self._cached_value = snapshot + else: + self._cached_value = None while snapshot.op.type != "ReadVariableOp": snapshot = snapshot.op.inputs[0] self._graph_element = snapshot @@ -802,16 +807,6 @@ class ResourceVariable(variables.RefVariable): return ResourceVariable( variable_def=variable_def, import_scope=import_scope) - @staticmethod - def _OverloadAllOperators(): # pylint: disable=invalid-name - """Register overloads for all operators.""" - for operator in ops.Tensor.OVERLOADABLE_OPERATORS: - ResourceVariable._OverloadOperator(operator) - # For slicing, bind getitem differently than a tensor (use SliceHelperVar - # instead) - # pylint: disable=protected-access - setattr(ResourceVariable, "__getitem__", array_ops._SliceHelperVar) - def _AsTensor(self): return self.value() @@ -823,30 +818,6 @@ class ResourceVariable(variables.RefVariable): """Unsupported.""" raise NotImplementedError("ResourceVariable does not implement set_shape()") - @staticmethod - def _OverloadOperator(operator): # pylint: disable=invalid-name - """Defer an operator overload to `ops.Tensor`. - - We pull the operator out of ops.Tensor dynamically to avoid ordering issues. - - Args: - operator: string. The operator name. - """ - - tensor_oper = getattr(ops.Tensor, operator) - def _run_op(a, *args): - # pylint: disable=protected-access - value = a._AsTensor() - return tensor_oper(value, *args) - - # Propagate __doc__ to wrapper - try: - _run_op.__doc__ = tensor_oper.__doc__ - except AttributeError: - pass - - setattr(ResourceVariable, operator, _run_op) - __array_priority__ = 100 def is_initialized(self, name=None): @@ -1432,7 +1403,6 @@ ops.register_tensor_conversion_function( variables.Variable, variables.Variable._TensorConversionFunction) # pylint: disable=protected-access # pylint: disable=protected-access -ResourceVariable._OverloadAllOperators() ops.register_dense_tensor_like_type(ResourceVariable) @@ -1442,13 +1412,23 @@ def _ReadGrad(_, grad): return grad +def variable_shape(handle, out_type=dtypes.int32): + if getattr( + handle, "_handle_data", None) is None or not handle._handle_data.is_set: + return gen_resource_variable_ops.variable_shape(handle, out_type=out_type) + shape_proto = handle._handle_data.shape_and_type[0].shape + if shape_proto.unknown_rank or any(x.size == -1 for x in shape_proto.dim): + return gen_resource_variable_ops.variable_shape(handle, out_type=out_type) + return constant_op.constant([x.size for x in shape_proto.dim], dtype=out_type) + + @ops.RegisterGradient("ResourceGather") def _GatherGrad(op, grad): """Gradient for gather op.""" # Build appropriately shaped IndexedSlices handle = op.inputs[0] indices = op.inputs[1] - params_shape = gen_resource_variable_ops.variable_shape(handle) + params_shape = variable_shape(handle) size = array_ops.expand_dims(array_ops.size(indices), 0) values_shape = array_ops.concat([size, params_shape[1:]], 0) values = array_ops.reshape(grad, values_shape) @@ -1522,3 +1502,6 @@ def copy_to_graph_uninitialized(var): new_variable._maybe_initialize_checkpointable() # pylint: enable=protected-access return new_variable + +ops.NotDifferentiable("VarIsInitializedOp") +ops.NotDifferentiable("VariableShape") diff --git a/tensorflow/python/ops/resources.py b/tensorflow/python/ops/resources.py index db6740643c..6cd12b7bbb 100644 --- a/tensorflow/python/ops/resources.py +++ b/tensorflow/python/ops/resources.py @@ -21,6 +21,7 @@ from __future__ import division from __future__ import print_function import collections +import os from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops @@ -86,7 +87,8 @@ def report_uninitialized_resources(resource_list=None, resource_list = shared_resources() + local_resources() with ops.name_scope(name): # Run all operations on CPU - with ops.device("/cpu:0"): + local_device = os.environ.get("TF_DEVICE_FOR_UNINITIALIZED_VARIABLE_REPORTING", "/cpu:0") + with ops.device(local_device): if not resource_list: # Return an empty tensor so we only need to check for returned tensor # size being 0 as an indication of model ready. diff --git a/tensorflow/python/ops/rnn.py b/tensorflow/python/ops/rnn.py index 57ecb50557..ec48cab91d 100644 --- a/tensorflow/python/ops/rnn.py +++ b/tensorflow/python/ops/rnn.py @@ -117,7 +117,7 @@ def _infer_state_dtype(explicit_dtype, state): inferred_dtypes = [element.dtype for element in nest.flatten(state)] if not inferred_dtypes: raise ValueError("Unable to infer dtype from empty state.") - all_same = all([x == inferred_dtypes[0] for x in inferred_dtypes]) + all_same = all(x == inferred_dtypes[0] for x in inferred_dtypes) if not all_same: raise ValueError( "State has tensors of different inferred_dtypes. Unable to infer a " @@ -348,7 +348,10 @@ def _reverse_seq(input_seq, lengths): return results -@tf_export("nn.bidirectional_dynamic_rnn") +@deprecation.deprecated(None, "Please use `keras.layers.Bidirectional(" + "keras.layers.RNN(cell))`, which is equivalent to " + "this API") +@tf_export(v1=["nn.bidirectional_dynamic_rnn"]) def bidirectional_dynamic_rnn(cell_fw, cell_bw, inputs, sequence_length=None, initial_state_fw=None, initial_state_bw=None, dtype=None, parallel_iterations=None, @@ -1490,7 +1493,10 @@ def static_state_saving_rnn(cell, return (outputs, state) -@tf_export("nn.static_bidirectional_rnn") +@deprecation.deprecated(None, "Please use `keras.layers.Bidirectional(" + "keras.layers.RNN(cell, unroll=True))`, which is " + "equivalent to this API") +@tf_export(v1=["nn.static_bidirectional_rnn"]) def static_bidirectional_rnn(cell_fw, cell_bw, inputs, diff --git a/tensorflow/python/ops/rnn_cell_impl.py b/tensorflow/python/ops/rnn_cell_impl.py index 050b486893..ffc45619a7 100644 --- a/tensorflow/python/ops/rnn_cell_impl.py +++ b/tensorflow/python/ops/rnn_cell_impl.py @@ -36,6 +36,7 @@ from tensorflow.python.framework import tensor_shape from tensorflow.python.framework import tensor_util from tensorflow.python.keras import activations from tensorflow.python.keras import initializers +from tensorflow.python.keras.engine import input_spec from tensorflow.python.keras.utils import tf_utils from tensorflow.python.layers import base as base_layer from tensorflow.python.ops import array_ops @@ -410,7 +411,7 @@ class BasicRNNCell(LayerRNNCell): "performance on GPU.", self) # Inputs must be 2-dimensional. - self.input_spec = base_layer.InputSpec(ndim=2) + self.input_spec = input_spec.InputSpec(ndim=2) self._num_units = num_units if activation: @@ -507,7 +508,7 @@ class GRUCell(LayerRNNCell): "Please use tf.contrib.cudnn_rnn.CudnnGRU for better " "performance on GPU.", self) # Inputs must be 2-dimensional. - self.input_spec = base_layer.InputSpec(ndim=2) + self.input_spec = input_spec.InputSpec(ndim=2) self._num_units = num_units if activation: @@ -683,7 +684,7 @@ class BasicLSTMCell(LayerRNNCell): "performance on GPU.", self) # Inputs must be 2-dimensional. - self.input_spec = base_layer.InputSpec(ndim=2) + self.input_spec = input_spec.InputSpec(ndim=2) self._num_units = num_units self._forget_bias = forget_bias @@ -871,7 +872,7 @@ class LSTMCell(LayerRNNCell): "performance on GPU.", self) # Inputs must be 2-dimensional. - self.input_spec = base_layer.InputSpec(ndim=2) + self.input_spec = input_spec.InputSpec(ndim=2) self._num_units = num_units self._use_peepholes = use_peepholes @@ -1394,7 +1395,7 @@ class DeviceWrapper(RNNCell): return self._cell(inputs, state, scope=scope) -@tf_export("nn.rnn_cell.MultiRNNCell") +@tf_export(v1=["nn.rnn_cell.MultiRNNCell"]) class MultiRNNCell(RNNCell): """RNN cell composed sequentially of multiple simple cells. @@ -1407,6 +1408,9 @@ class MultiRNNCell(RNNCell): ``` """ + @deprecated(None, "This class is equivalent as " + "tf.keras.layers.StackedRNNCells, and will be replaced by " + "that in Tensorflow 2.0.") def __init__(self, cells, state_is_tuple=True): """Create a RNN cell composed sequentially of a number of RNNCells. @@ -1452,7 +1456,7 @@ class MultiRNNCell(RNNCell): if self._state_is_tuple: return tuple(cell.state_size for cell in self._cells) else: - return sum([cell.state_size for cell in self._cells]) + return sum(cell.state_size for cell in self._cells) @property def output_size(self): diff --git a/tensorflow/python/ops/sets_impl.py b/tensorflow/python/ops/sets_impl.py index 21e08d03d2..ee9c9b6bc0 100644 --- a/tensorflow/python/ops/sets_impl.py +++ b/tensorflow/python/ops/sets_impl.py @@ -31,7 +31,7 @@ _VALID_DTYPES = set([ dtypes.uint8, dtypes.uint16, dtypes.string]) -@tf_export("sets.set_size") +@tf_export("sets.size", v1=["sets.size", "sets.set_size"]) def set_size(a, validate_indices=True): """Compute number of unique elements along last dimension of `a`. @@ -133,7 +133,8 @@ def _set_operation(a, b, set_operation, validate_indices=True): return sparse_tensor.SparseTensor(indices, values, shape) -@tf_export("sets.set_intersection") +@tf_export( + "sets.intersection", v1=["sets.intersection", "sets.set_intersection"]) def set_intersection(a, b, validate_indices=True): """Compute set intersection of elements in last dimension of `a` and `b`. @@ -200,7 +201,8 @@ def set_intersection(a, b, validate_indices=True): return _set_operation(a, b, "intersection", validate_indices) -@tf_export("sets.set_difference") +@tf_export( + "sets.difference", v1=["sets.difference", "sets.set_difference"]) def set_difference(a, b, aminusb=True, validate_indices=True): """Compute set difference of elements in last dimension of `a` and `b`. @@ -271,7 +273,8 @@ def set_difference(a, b, aminusb=True, validate_indices=True): return _set_operation(a, b, "a-b" if aminusb else "b-a", validate_indices) -@tf_export("sets.set_union") +@tf_export( + "sets.union", v1=["sets.union", "sets.set_union"]) def set_union(a, b, validate_indices=True): """Compute set union of elements in last dimension of `a` and `b`. diff --git a/tensorflow/python/ops/signal/BUILD b/tensorflow/python/ops/signal/BUILD index 0d04dc0c1b..da2bf9c1d2 100644 --- a/tensorflow/python/ops/signal/BUILD +++ b/tensorflow/python/ops/signal/BUILD @@ -6,16 +6,29 @@ exports_files(["LICENSE"]) py_library( name = "signal", - srcs = glob(["*.py"]), + srcs = [ + "dct_ops.py", + "fft_ops.py", + "mel_ops.py", + "mfcc_ops.py", + "reconstruction_ops.py", + "shape_ops.py", + "signal.py", + "spectral_ops.py", + "util_ops.py", + "window_ops.py", + ], srcs_version = "PY2AND3", deps = [ "//tensorflow/python:array_ops", "//tensorflow/python:constant_op", "//tensorflow/python:control_flow_ops", "//tensorflow/python:dtypes", + "//tensorflow/python:framework", + "//tensorflow/python:framework_for_generated_wrappers", "//tensorflow/python:framework_ops", "//tensorflow/python:math_ops", - "//tensorflow/python:spectral_ops", + "//tensorflow/python:spectral_ops_gen", "//tensorflow/python:tensor_util", "//tensorflow/python:util", "//third_party/py/numpy", diff --git a/tensorflow/python/ops/signal/__init__.py b/tensorflow/python/ops/signal/__init__.py deleted file mode 100644 index 3fa4e94e58..0000000000 --- a/tensorflow/python/ops/signal/__init__.py +++ /dev/null @@ -1,37 +0,0 @@ -"""Signal processing operations. - -See the [tf.signal](https://tensorflow.org/api_guides/python/contrib.signal) -guide. - -@@frame -@@hamming_window -@@hann_window -@@inverse_stft -@@inverse_stft_window_fn -@@mfccs_from_log_mel_spectrograms -@@linear_to_mel_weight_matrix -@@overlap_and_add -@@stft - -[hamming]: https://en.wikipedia.org/wiki/Window_function#Hamming_window -[hann]: https://en.wikipedia.org/wiki/Window_function#Hann_window -[mel]: https://en.wikipedia.org/wiki/Mel_scale -[mfcc]: https://en.wikipedia.org/wiki/Mel-frequency_cepstrum -[stft]: https://en.wikipedia.org/wiki/Short-time_Fourier_transform -""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -# pylint: disable=unused-import -from tensorflow.python.ops.signal.mel_ops import linear_to_mel_weight_matrix -from tensorflow.python.ops.signal.mfcc_ops import mfccs_from_log_mel_spectrograms -from tensorflow.python.ops.signal.reconstruction_ops import overlap_and_add -from tensorflow.python.ops.signal.shape_ops import frame -from tensorflow.python.ops.signal.spectral_ops import inverse_stft -from tensorflow.python.ops.signal.spectral_ops import inverse_stft_window_fn -from tensorflow.python.ops.signal.spectral_ops import stft -from tensorflow.python.ops.signal.window_ops import hamming_window -from tensorflow.python.ops.signal.window_ops import hann_window -# pylint: enable=unused-import diff --git a/tensorflow/python/ops/signal/dct_ops.py b/tensorflow/python/ops/signal/dct_ops.py new file mode 100644 index 0000000000..d042c95c04 --- /dev/null +++ b/tensorflow/python/ops/signal/dct_ops.py @@ -0,0 +1,192 @@ +# 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. +# ============================================================================== +"""Discrete Cosine Transform ops.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math as _math + +from tensorflow.python.framework import dtypes as _dtypes +from tensorflow.python.framework import ops as _ops +from tensorflow.python.framework import tensor_shape +from tensorflow.python.ops import array_ops as _array_ops +from tensorflow.python.ops import math_ops as _math_ops +from tensorflow.python.ops.signal import fft_ops +from tensorflow.python.util.tf_export import tf_export + + +def _validate_dct_arguments(input_tensor, dct_type, n, axis, norm): + """Checks that DCT/IDCT arguments are compatible and well formed.""" + if n is not None: + raise NotImplementedError("The DCT length argument is not implemented.") + if axis != -1: + raise NotImplementedError("axis must be -1. Got: %s" % axis) + if dct_type not in (1, 2, 3): + raise ValueError("Only Types I, II and III (I)DCT are supported.") + if dct_type == 1: + if norm == "ortho": + raise ValueError("Normalization is not supported for the Type-I DCT.") + if input_tensor.shape[-1] is not None and input_tensor.shape[-1] < 2: + raise ValueError( + "Type-I DCT requires the dimension to be greater than one.") + + if norm not in (None, "ortho"): + raise ValueError( + "Unknown normalization. Expected None or 'ortho', got: %s" % norm) + + +# TODO(rjryan): Implement `n` and `axis` parameters. +@tf_export("signal.dct", v1=["signal.dct", "spectral.dct"]) +def dct(input, type=2, n=None, axis=-1, norm=None, name=None): # pylint: disable=redefined-builtin + """Computes the 1D [Discrete Cosine Transform (DCT)][dct] of `input`. + + Currently only Types I, II and III are supported. + Type I is implemented using a length `2N` padded `tf.spectral.rfft`. + Type II is implemented using a length `2N` padded `tf.spectral.rfft`, as + described here: + https://dsp.stackexchange.com/a/10606. + Type III is a fairly straightforward inverse of Type II + (i.e. using a length `2N` padded `tf.spectral.irfft`). + + @compatibility(scipy) + Equivalent to scipy.fftpack.dct for Type-I, Type-II and Type-III DCT. + https://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.fftpack.dct.html + @end_compatibility + + Args: + input: A `[..., samples]` `float32` `Tensor` containing the signals to + take the DCT of. + type: The DCT type to perform. Must be 1, 2 or 3. + n: For future expansion. The length of the transform. Must be `None`. + axis: For future expansion. The axis to compute the DCT along. Must be `-1`. + norm: The normalization to apply. `None` for no normalization or `'ortho'` + for orthonormal normalization. + name: An optional name for the operation. + + Returns: + A `[..., samples]` `float32` `Tensor` containing the DCT of `input`. + + Raises: + ValueError: If `type` is not `1`, `2` or `3`, `n` is not `None, `axis` is + not `-1`, or `norm` is not `None` or `'ortho'`. + ValueError: If `type` is `1` and `norm` is `ortho`. + + [dct]: https://en.wikipedia.org/wiki/Discrete_cosine_transform + """ + _validate_dct_arguments(input, type, n, axis, norm) + with _ops.name_scope(name, "dct", [input]): + # We use the RFFT to compute the DCT and TensorFlow only supports float32 + # for FFTs at the moment. + input = _ops.convert_to_tensor(input, dtype=_dtypes.float32) + + axis_dim = (tensor_shape.dimension_value(input.shape[-1]) + or _array_ops.shape(input)[-1]) + axis_dim_float = _math_ops.to_float(axis_dim) + + if type == 1: + dct1_input = _array_ops.concat([input, input[..., -2:0:-1]], axis=-1) + dct1 = _math_ops.real(fft_ops.rfft(dct1_input)) + return dct1 + + if type == 2: + scale = 2.0 * _math_ops.exp( + _math_ops.complex( + 0.0, -_math_ops.range(axis_dim_float) * _math.pi * 0.5 / + axis_dim_float)) + + # TODO(rjryan): Benchmark performance and memory usage of the various + # approaches to computing a DCT via the RFFT. + dct2 = _math_ops.real( + fft_ops.rfft( + input, fft_length=[2 * axis_dim])[..., :axis_dim] * scale) + + if norm == "ortho": + n1 = 0.5 * _math_ops.rsqrt(axis_dim_float) + n2 = n1 * _math_ops.sqrt(2.0) + # Use tf.pad to make a vector of [n1, n2, n2, n2, ...]. + weights = _array_ops.pad( + _array_ops.expand_dims(n1, 0), [[0, axis_dim - 1]], + constant_values=n2) + dct2 *= weights + + return dct2 + + elif type == 3: + if norm == "ortho": + n1 = _math_ops.sqrt(axis_dim_float) + n2 = n1 * _math_ops.sqrt(0.5) + # Use tf.pad to make a vector of [n1, n2, n2, n2, ...]. + weights = _array_ops.pad( + _array_ops.expand_dims(n1, 0), [[0, axis_dim - 1]], + constant_values=n2) + input *= weights + else: + input *= axis_dim_float + scale = 2.0 * _math_ops.exp( + _math_ops.complex( + 0.0, + _math_ops.range(axis_dim_float) * _math.pi * 0.5 / + axis_dim_float)) + dct3 = _math_ops.real( + fft_ops.irfft( + scale * _math_ops.complex(input, 0.0), + fft_length=[2 * axis_dim]))[..., :axis_dim] + + return dct3 + + +# TODO(rjryan): Implement `n` and `axis` parameters. +@tf_export("signal.idct", v1=["signal.idct", "spectral.idct"]) +def idct(input, type=2, n=None, axis=-1, norm=None, name=None): # pylint: disable=redefined-builtin + """Computes the 1D [Inverse Discrete Cosine Transform (DCT)][idct] of `input`. + + Currently only Types I, II and III are supported. Type III is the inverse of + Type II, and vice versa. + + Note that you must re-normalize by 1/(2n) to obtain an inverse if `norm` is + not `'ortho'`. That is: + `signal == idct(dct(signal)) * 0.5 / signal.shape[-1]`. + When `norm='ortho'`, we have: + `signal == idct(dct(signal, norm='ortho'), norm='ortho')`. + + @compatibility(scipy) + Equivalent to scipy.fftpack.idct for Type-I, Type-II and Type-III DCT. + https://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.fftpack.idct.html + @end_compatibility + + Args: + input: A `[..., samples]` `float32` `Tensor` containing the signals to take + the DCT of. + type: The IDCT type to perform. Must be 1, 2 or 3. + n: For future expansion. The length of the transform. Must be `None`. + axis: For future expansion. The axis to compute the DCT along. Must be `-1`. + norm: The normalization to apply. `None` for no normalization or `'ortho'` + for orthonormal normalization. + name: An optional name for the operation. + + Returns: + A `[..., samples]` `float32` `Tensor` containing the IDCT of `input`. + + Raises: + ValueError: If `type` is not `1`, `2` or `3`, `n` is not `None, `axis` is + not `-1`, or `norm` is not `None` or `'ortho'`. + + [idct]: + https://en.wikipedia.org/wiki/Discrete_cosine_transform#Inverse_transforms + """ + _validate_dct_arguments(input, type, n, axis, norm) + inverse_type = {1: 1, 2: 3, 3: 2}[type] + return dct(input, type=inverse_type, n=n, axis=axis, norm=norm, name=name) diff --git a/tensorflow/python/ops/spectral_ops.py b/tensorflow/python/ops/signal/fft_ops.py similarity index 51% rename from tensorflow/python/ops/spectral_ops.py rename to tensorflow/python/ops/signal/fft_ops.py index 4dcc90aefa..2d14b2bbd7 100644 --- a/tensorflow/python/ops/spectral_ops.py +++ b/tensorflow/python/ops/signal/fft_ops.py @@ -12,16 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Spectral operators (e.g. DCT, FFT, RFFT).""" +"""Fast-Fourier Transform ops.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function -import math as _math +import numpy as np from tensorflow.python.framework import dtypes as _dtypes from tensorflow.python.framework import ops as _ops -from tensorflow.python.framework import tensor_shape from tensorflow.python.framework import tensor_util as _tensor_util from tensorflow.python.ops import array_ops as _array_ops from tensorflow.python.ops import gen_spectral_ops @@ -112,6 +111,7 @@ def _rfft_wrapper(fft_fn, fft_rank, default_name): """Wrapper around gen_spectral_ops.rfft* that infers fft_length argument.""" def _rfft(input_tensor, fft_length=None, name=None): + """Wrapper around gen_spectral_ops.rfft* that infers fft_length argument.""" with _ops.name_scope(name, default_name, [input_tensor, fft_length]) as name: input_tensor = _ops.convert_to_tensor(input_tensor, _dtypes.float32) @@ -130,6 +130,7 @@ def _irfft_wrapper(ifft_fn, fft_rank, default_name): """Wrapper around gen_spectral_ops.irfft* that infers fft_length argument.""" def _irfft(input_tensor, fft_length=None, name=None): + """Wrapper irfft* that infers fft_length argument.""" with _ops.name_scope(name, default_name, [input_tensor, fft_length]) as name: input_tensor = _ops.convert_to_tensor(input_tensor, _dtypes.complex64) @@ -145,6 +146,8 @@ def _irfft_wrapper(ifft_fn, fft_rank, default_name): return _irfft +# FFT/IFFT 1/2/3D are exported via +# third_party/tensorflow/core/api_def/python_api/ fft = gen_spectral_ops.fft ifft = gen_spectral_ops.ifft fft2d = gen_spectral_ops.fft2d @@ -152,159 +155,176 @@ ifft2d = gen_spectral_ops.ifft2d fft3d = gen_spectral_ops.fft3d ifft3d = gen_spectral_ops.ifft3d rfft = _rfft_wrapper(gen_spectral_ops.rfft, 1, "rfft") -tf_export("spectral.rfft")(rfft) +tf_export("signal.rfft", v1=["signal.rfft", "spectral.rfft"])(rfft) irfft = _irfft_wrapper(gen_spectral_ops.irfft, 1, "irfft") -tf_export("spectral.irfft")(irfft) +tf_export("signal.irfft", v1=["signal.irfft", "spectral.irfft"])(irfft) rfft2d = _rfft_wrapper(gen_spectral_ops.rfft2d, 2, "rfft2d") -tf_export("spectral.rfft2d")(rfft2d) +tf_export("signal.rfft2d", v1=["signal.rfft2d", "spectral.rfft2d"])(rfft2d) irfft2d = _irfft_wrapper(gen_spectral_ops.irfft2d, 2, "irfft2d") -tf_export("spectral.irfft2d")(irfft2d) +tf_export("signal.irfft2d", v1=["signal.irfft2d", "spectral.irfft2d"])(irfft2d) rfft3d = _rfft_wrapper(gen_spectral_ops.rfft3d, 3, "rfft3d") -tf_export("spectral.rfft3d")(rfft3d) +tf_export("signal.rfft3d", v1=["signal.rfft3d", "spectral.rfft3d"])(rfft3d) irfft3d = _irfft_wrapper(gen_spectral_ops.irfft3d, 3, "irfft3d") -tf_export("spectral.irfft3d")(irfft3d) - - -def _validate_dct_arguments(dct_type, n, axis, norm): - if n is not None: - raise NotImplementedError("The DCT length argument is not implemented.") - if axis != -1: - raise NotImplementedError("axis must be -1. Got: %s" % axis) - if dct_type not in (2, 3): - raise ValueError("Only Types II and III (I)DCT are supported.") - if norm not in (None, "ortho"): - raise ValueError( - "Unknown normalization. Expected None or 'ortho', got: %s" % norm) - - -# TODO(rjryan): Implement `type`, `n` and `axis` parameters. -@tf_export("spectral.dct") -def dct(input, type=2, n=None, axis=-1, norm=None, name=None): # pylint: disable=redefined-builtin - """Computes the 1D [Discrete Cosine Transform (DCT)][dct] of `input`. - - Currently only Types II and III are supported. Type II is implemented using a - length `2N` padded `tf.spectral.rfft`, as described here: - https://dsp.stackexchange.com/a/10606. Type III is a fairly straightforward - inverse of Type II (i.e. using a length `2N` padded `tf.spectral.irfft`). - - @compatibility(scipy) - Equivalent to scipy.fftpack.dct for Type-II and Type-III DCT. - https://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.fftpack.dct.html - @end_compatibility - - Args: - input: A `[..., samples]` `float32` `Tensor` containing the signals to - take the DCT of. - type: The DCT type to perform. Must be 2 or 3. - n: For future expansion. The length of the transform. Must be `None`. - axis: For future expansion. The axis to compute the DCT along. Must be `-1`. - norm: The normalization to apply. `None` for no normalization or `'ortho'` - for orthonormal normalization. - name: An optional name for the operation. - - Returns: - A `[..., samples]` `float32` `Tensor` containing the DCT of `input`. - - Raises: - ValueError: If `type` is not `2` or `3`, `n` is not `None, `axis` is not - `-1`, or `norm` is not `None` or `'ortho'`. - - [dct]: https://en.wikipedia.org/wiki/Discrete_cosine_transform - """ - _validate_dct_arguments(type, n, axis, norm) - with _ops.name_scope(name, "dct", [input]): - # We use the RFFT to compute the DCT and TensorFlow only supports float32 - # for FFTs at the moment. - input = _ops.convert_to_tensor(input, dtype=_dtypes.float32) - - axis_dim = (tensor_shape.dimension_value(input.shape[-1]) - or _array_ops.shape(input)[-1]) - axis_dim_float = _math_ops.to_float(axis_dim) - if type == 2: - scale = 2.0 * _math_ops.exp( - _math_ops.complex( - 0.0, -_math_ops.range(axis_dim_float) * _math.pi * 0.5 / - axis_dim_float)) - - # TODO(rjryan): Benchmark performance and memory usage of the various - # approaches to computing a DCT via the RFFT. - dct2 = _math_ops.real( - rfft(input, fft_length=[2 * axis_dim])[..., :axis_dim] * scale) - - if norm == "ortho": - n1 = 0.5 * _math_ops.rsqrt(axis_dim_float) - n2 = n1 * _math_ops.sqrt(2.0) - # Use tf.pad to make a vector of [n1, n2, n2, n2, ...]. - weights = _array_ops.pad( - _array_ops.expand_dims(n1, 0), [[0, axis_dim - 1]], - constant_values=n2) - dct2 *= weights - - return dct2 - - elif type == 3: - if norm == "ortho": - n1 = _math_ops.sqrt(axis_dim_float) - n2 = n1 * _math_ops.sqrt(0.5) - # Use tf.pad to make a vector of [n1, n2, n2, n2, ...]. - weights = _array_ops.pad( - _array_ops.expand_dims(n1, 0), [[0, axis_dim - 1]], - constant_values=n2) - input *= weights - else: - input *= axis_dim_float - scale = 2.0 * _math_ops.exp( - _math_ops.complex( - 0.0, - _math_ops.range(axis_dim_float) * _math.pi * 0.5 / - axis_dim_float)) - dct3 = _math_ops.real( - irfft( - scale * _math_ops.complex(input, 0.0), - fft_length=[2 * axis_dim]))[..., :axis_dim] - - return dct3 - - -# TODO(rjryan): Implement `type`, `n` and `axis` parameters. -@tf_export("spectral.idct") -def idct(input, type=2, n=None, axis=-1, norm=None, name=None): # pylint: disable=redefined-builtin - """Computes the 1D [Inverse Discrete Cosine Transform (DCT)][idct] of `input`. - - Currently only Types II and III are supported. Type III is the inverse of - Type II, and vice versa. - - Note that you must re-normalize by 1/(2n) to obtain an inverse if `norm` is - not `'ortho'`. That is: - `signal == idct(dct(signal)) * 0.5 / signal.shape[-1]`. - When `norm='ortho'`, we have: - `signal == idct(dct(signal, norm='ortho'), norm='ortho')`. - - @compatibility(scipy) - Equivalent to scipy.fftpack.idct for Type-II and Type-III DCT. - https://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.fftpack.idct.html - @end_compatibility - - Args: - input: A `[..., samples]` `float32` `Tensor` containing the signals to take - the DCT of. - type: The IDCT type to perform. Must be 2 or 3. - n: For future expansion. The length of the transform. Must be `None`. - axis: For future expansion. The axis to compute the DCT along. Must be `-1`. - norm: The normalization to apply. `None` for no normalization or `'ortho'` - for orthonormal normalization. - name: An optional name for the operation. - - Returns: - A `[..., samples]` `float32` `Tensor` containing the IDCT of `input`. - - Raises: - ValueError: If `type` is not `2` or `3`, `n` is not `None, `axis` is not - `-1`, or `norm` is not `None` or `'ortho'`. - - [idct]: - https://en.wikipedia.org/wiki/Discrete_cosine_transform#Inverse_transforms - """ - _validate_dct_arguments(type, n, axis, norm) - inverse_type = {2: 3, 3: 2}[type] - return dct(input, type=inverse_type, n=n, axis=axis, norm=norm, name=name) +tf_export("signal.irfft3d", v1=["signal.irfft3d", "spectral.irfft3d"])(irfft3d) + + +def _fft_size_for_grad(grad, rank): + return _math_ops.reduce_prod(_array_ops.shape(grad)[-rank:]) + + +@_ops.RegisterGradient("FFT") +def _fft_grad(_, grad): + size = _math_ops.cast(_fft_size_for_grad(grad, 1), grad.dtype) + return ifft(grad) * size + + +@_ops.RegisterGradient("IFFT") +def _ifft_grad(_, grad): + rsize = _math_ops.cast( + 1. / _math_ops.cast(_fft_size_for_grad(grad, 1), grad.dtype.real_dtype), + grad.dtype) + return fft(grad) * rsize + + +@_ops.RegisterGradient("FFT2D") +def _fft2d_grad(_, grad): + size = _math_ops.cast(_fft_size_for_grad(grad, 2), grad.dtype) + return ifft2d(grad) * size + + +@_ops.RegisterGradient("IFFT2D") +def _ifft2d_grad(_, grad): + rsize = _math_ops.cast( + 1. / _math_ops.cast(_fft_size_for_grad(grad, 2), grad.dtype.real_dtype), + grad.dtype) + return fft2d(grad) * rsize + + +@_ops.RegisterGradient("FFT3D") +def _fft3d_grad(_, grad): + size = _math_ops.cast(_fft_size_for_grad(grad, 3), grad.dtype) + return ifft3d(grad) * size + + +@_ops.RegisterGradient("IFFT3D") +def _ifft3d_grad(_, grad): + rsize = _math_ops.cast( + 1. / _math_ops.cast(_fft_size_for_grad(grad, 3), grad.dtype.real_dtype), + grad.dtype) + return fft3d(grad) * rsize + + +def _rfft_grad_helper(rank, irfft_fn): + """Returns a gradient function for an RFFT of the provided rank.""" + # Can't happen because we don't register a gradient for RFFT3D. + assert rank in (1, 2), "Gradient for RFFT3D is not implemented." + + def _grad(op, grad): + """A gradient function for RFFT with the provided `rank` and `irfft_fn`.""" + fft_length = op.inputs[1] + input_shape = _array_ops.shape(op.inputs[0]) + is_even = _math_ops.cast(1 - (fft_length[-1] % 2), _dtypes.complex64) + + def _tile_for_broadcasting(matrix, t): + expanded = _array_ops.reshape( + matrix, + _array_ops.concat([ + _array_ops.ones([_array_ops.rank(t) - 2], _dtypes.int32), + _array_ops.shape(matrix) + ], 0)) + return _array_ops.tile( + expanded, _array_ops.concat([_array_ops.shape(t)[:-2], [1, 1]], 0)) + + def _mask_matrix(length): + """Computes t_n = exp(sqrt(-1) * pi * n^2 / line_len).""" + # TODO(rjryan): Speed up computation of twiddle factors using the + # following recurrence relation and cache them across invocations of RFFT. + # + # t_n = exp(sqrt(-1) * pi * n^2 / line_len) + # for n = 0, 1,..., line_len-1. + # For n > 2, use t_n = t_{n-1}^2 / t_{n-2} * t_1^2 + a = _array_ops.tile( + _array_ops.expand_dims(_math_ops.range(length), 0), (length, 1)) + b = _array_ops.transpose(a, [1, 0]) + return _math_ops.exp( + -2j * np.pi * _math_ops.cast(a * b, _dtypes.complex64) / + _math_ops.cast(length, _dtypes.complex64)) + + def _ymask(length): + """A sequence of [1+0j, -1+0j, 1+0j, -1+0j, ...] with length `length`.""" + return _math_ops.cast(1 - 2 * (_math_ops.range(length) % 2), + _dtypes.complex64) + + y0 = grad[..., 0:1] + if rank == 1: + ym = grad[..., -1:] + extra_terms = y0 + is_even * ym * _ymask(input_shape[-1]) + elif rank == 2: + # Create a mask matrix for y0 and ym. + base_mask = _mask_matrix(input_shape[-2]) + + # Tile base_mask to match y0 in shape so that we can batch-matmul the + # inner 2 dimensions. + tiled_mask = _tile_for_broadcasting(base_mask, y0) + + y0_term = _math_ops.matmul(tiled_mask, _math_ops.conj(y0)) + extra_terms = y0_term + + ym = grad[..., -1:] + ym_term = _math_ops.matmul(tiled_mask, _math_ops.conj(ym)) + + inner_dim = input_shape[-1] + ym_term = _array_ops.tile( + ym_term, + _array_ops.concat([ + _array_ops.ones([_array_ops.rank(grad) - 1], _dtypes.int32), + [inner_dim] + ], 0)) * _ymask(inner_dim) + + extra_terms += is_even * ym_term + + # The gradient of RFFT is the IRFFT of the incoming gradient times a scaling + # factor, plus some additional terms to make up for the components dropped + # due to Hermitian symmetry. + input_size = _math_ops.to_float(_fft_size_for_grad(op.inputs[0], rank)) + the_irfft = irfft_fn(grad, fft_length) + return 0.5 * (the_irfft * input_size + _math_ops.real(extra_terms)), None + + return _grad + + +def _irfft_grad_helper(rank, rfft_fn): + """Returns a gradient function for an IRFFT of the provided rank.""" + # Can't happen because we don't register a gradient for IRFFT3D. + assert rank in (1, 2), "Gradient for IRFFT3D is not implemented." + + def _grad(op, grad): + """A gradient function for IRFFT with the provided `rank` and `rfft_fn`.""" + # Generate a simple mask like [1.0, 2.0, ..., 2.0, 1.0] for even-length FFTs + # and [1.0, 2.0, ..., 2.0] for odd-length FFTs. To reduce extra ops in the + # graph we special-case the situation where the FFT length and last + # dimension of the input are known at graph construction time. + fft_length = op.inputs[1] + is_odd = _math_ops.mod(fft_length[-1], 2) + input_last_dimension = _array_ops.shape(op.inputs[0])[-1] + mask = _array_ops.concat( + [[1.0], 2.0 * _array_ops.ones([input_last_dimension - 2 + is_odd]), + _array_ops.ones([1 - is_odd])], 0) + + rsize = _math_ops.reciprocal(_math_ops.to_float( + _fft_size_for_grad(grad, rank))) + + # The gradient of IRFFT is the RFFT of the incoming gradient times a scaling + # factor and a mask. The mask scales the gradient for the Hermitian + # symmetric components of the RFFT by a factor of two, since these + # components are de-duplicated in the RFFT. + the_rfft = rfft_fn(grad, fft_length) + return the_rfft * _math_ops.cast(rsize * mask, _dtypes.complex64), None + + return _grad + + +_ops.RegisterGradient("RFFT")(_rfft_grad_helper(1, irfft)) +_ops.RegisterGradient("IRFFT")(_irfft_grad_helper(1, rfft)) +_ops.RegisterGradient("RFFT2D")(_rfft_grad_helper(2, irfft2d)) +_ops.RegisterGradient("IRFFT2D")(_irfft_grad_helper(2, rfft2d)) diff --git a/tensorflow/python/ops/signal/mfcc_ops.py b/tensorflow/python/ops/signal/mfcc_ops.py index 6ae3b222ba..601409dea9 100644 --- a/tensorflow/python/ops/signal/mfcc_ops.py +++ b/tensorflow/python/ops/signal/mfcc_ops.py @@ -22,7 +22,7 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops -from tensorflow.python.ops import spectral_ops +from tensorflow.python.ops.signal import dct_ops from tensorflow.python.util.tf_export import tf_export @@ -106,5 +106,5 @@ def mfccs_from_log_mel_spectrograms(log_mel_spectrograms, name=None): else: num_mel_bins = array_ops.shape(log_mel_spectrograms)[-1] - dct2 = spectral_ops.dct(log_mel_spectrograms) + dct2 = dct_ops.dct(log_mel_spectrograms, type=2) return dct2 * math_ops.rsqrt(math_ops.to_float(num_mel_bins) * 2.0) diff --git a/tensorflow/python/ops/signal/reconstruction_ops.py b/tensorflow/python/ops/signal/reconstruction_ops.py index 0fc7fec239..4eaab4e0a0 100644 --- a/tensorflow/python/ops/signal/reconstruction_ops.py +++ b/tensorflow/python/ops/signal/reconstruction_ops.py @@ -18,46 +18,14 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops -from tensorflow.python.ops.signal import shape_ops -from tensorflow.python.ops.signal import util_ops from tensorflow.python.util.tf_export import tf_export -def _shuffle_to_front(input_tensor, k): - """Shuffles the last `k` indices of `input_tensor` to the front. - - Transposes `input_tensor` to have the last `k` indices at the front. The input - may have arbitrary rank and unknown shape. - - Args: - input_tensor: A `Tensor` of arbitrary rank and unknown shape. - k: A scalar `Tensor` specifying how many indices to shuffle. - - Returns: - A transposed version of `input_tensor` with `k` indices shuffled to the - front. - - Raises: - ValueError: If `input_tensor` is not at least rank `k` or `k` is not scalar. - """ - k = ops.convert_to_tensor(k, name="k") - k.shape.with_rank(0) - k_static = tensor_util.constant_value(k) - if k_static is not None: - input_tensor.shape.with_rank_at_least(k_static) - - rank = array_ops.rank(input_tensor) - outer_indices, inner_indices = array_ops.split(math_ops.range(rank), - [rank - k, k]) - permutation = array_ops.concat([inner_indices, outer_indices], 0) - - return array_ops.transpose(input_tensor, perm=permutation) - - @tf_export("signal.overlap_and_add") def overlap_and_add(signal, frame_step, name=None): """Reconstructs a signal from a framed representation. @@ -80,8 +48,8 @@ def overlap_and_add(signal, frame_step, name=None): frames of `signal`'s inner-most two dimensions. Raises: - ValueError: If `signal`'s rank is less than 2, `frame_step` is not a scalar - integer or `frame_step` is greater than `frame_length`. + ValueError: If `signal`'s rank is less than 2, or `frame_step` is not a + scalar integer. """ with ops.name_scope(name, "overlap_and_add", [signal, frame_step]): signal = ops.convert_to_tensor(signal, name="signal") @@ -97,56 +65,91 @@ def overlap_and_add(signal, frame_step, name=None): # All dimensions that are not part of the overlap-and-add. Can be empty for # rank 2 inputs. outer_dimensions = signal_shape[:-2] + outer_rank = array_ops.size(outer_dimensions) + + def full_shape(inner_shape): + return array_ops.concat([outer_dimensions, inner_shape], 0) - # If frame_length and frame_step are known at graph construction time, check - # frame_step is less than or equal to frame_length. - frame_step_static = tensor_util.constant_value(frame_step) - if (frame_step_static is not None and signal.shape.ndims is not None and - signal.shape.dims[-1].value is not None): - if frame_step_static > signal.shape.dims[-1].value: - raise ValueError( - "frame_step (%d) must be less than or equal to " - "frame_length (%d)" % ( - frame_step_static, signal.shape.dims[-1].value)) - # If frame_length is equal to frame_step, there's no overlap so just - # reshape the tensor. - if frame_step_static == signal.shape.dims[-1].value: - return array_ops.reshape(signal, array_ops.concat( - [outer_dimensions, [-1]], 0)) - - signal_rank = array_ops.rank(signal) - frames = signal_shape[-2] frame_length = signal_shape[-1] + frames = signal_shape[-2] - subframe_length = util_ops.gcd(frame_length, frame_step) - subframe_step = frame_step // subframe_length - subframes_per_frame = frame_length // subframe_length - output_size = frame_step * (frames - 1) + frame_length - output_subframes = output_size // subframe_length - - # To avoid overlap-adding sample-by-sample, we overlap-add at the "subframe" - # level, where a subframe is gcd(frame_length, frame_step). Reshape signal - # from [..., frames, frame_length] into [..., subframes, subframe_length]. - subframe_shape = array_ops.concat( - [outer_dimensions, [-1, subframe_length]], 0) - subframe_signal = array_ops.reshape(signal, subframe_shape) - - # Now we shuffle the last [subframes, subframe_length] dimensions to the - # front. - # TODO(rjryan): Add an axis argument to unsorted_segment_sum so we can - # avoid this pair of transposes. - subframe_signal = _shuffle_to_front(subframe_signal, 2) - - # Use unsorted_segment_sum to add overlapping subframes together. - segment_ids = array_ops.reshape(shape_ops.frame( - math_ops.range(output_subframes), subframes_per_frame, subframe_step, - pad_end=False), [-1]) - result = math_ops.unsorted_segment_sum(subframe_signal, segment_ids, - num_segments=output_subframes) - - # result is a [subframes, subframe_length, ...outer_dimensions] tensor. We - # return a [...outer_dimensions, output_size] tensor with a transpose and - # reshape. - result_shape = array_ops.concat([outer_dimensions, [output_size]], 0) - return array_ops.reshape(_shuffle_to_front(result, signal_rank - 2), - result_shape) + # Compute output length. + output_length = frame_length + frame_step * (frames - 1) + + # If frame_length is equal to frame_step, there's no overlap so just + # reshape the tensor. + frame_step_static = tensor_util.constant_value(frame_step) + if (frame_step_static is not None and signal.shape.dims is not None and + frame_step_static == signal.shape.dims[-1].value): + output_shape = full_shape([output_length]) + return array_ops.reshape(signal, output_shape, name="fast_path") + + # The following code is documented using this example: + # + # frame_step = 2 + # signal.shape = (3, 5) + # a b c d e + # f g h i j + # k l m n o + + # Compute the number of segments, per frame. + segments = -(-frame_length // frame_step) # Divide and round up. + + # Pad the frame_length dimension to a multiple of the frame step. + # Pad the frames dimension by `segments` so that signal.shape = (6, 6) + # a b c d e 0 + # f g h i j 0 + # k l m n o 0 + # 0 0 0 0 0 0 + # 0 0 0 0 0 0 + # 0 0 0 0 0 0 + paddings = [[0, segments], [0, segments * frame_step - frame_length]] + outer_paddings = array_ops.zeros([outer_rank, 2], dtypes.int32) + paddings = array_ops.concat([outer_paddings, paddings], 0) + signal = array_ops.pad(signal, paddings) + + # Reshape so that signal.shape = (3, 6, 2) + # ab cd e0 + # fg hi j0 + # kl mn o0 + # 00 00 00 + # 00 00 00 + # 00 00 00 + shape = full_shape([frames + segments, segments, frame_step]) + signal = array_ops.reshape(signal, shape) + + # Transpose dimensions so that signal.shape = (3, 6, 2) + # ab fg kl 00 00 00 + # cd hi mn 00 00 00 + # e0 j0 o0 00 00 00 + perm = array_ops.concat( + [math_ops.range(outer_rank), outer_rank + [1, 0, 2]], 0) + signal = array_ops.transpose(signal, perm) + + # Reshape so that signal.shape = (18, 2) + # ab fg kl 00 00 00 cd hi mn 00 00 00 e0 j0 o0 00 00 00 + shape = full_shape([(frames + segments) * segments, frame_step]) + signal = array_ops.reshape(signal, shape) + + # Truncate so that signal.shape = (15, 2) + # ab fg kl 00 00 00 cd hi mn 00 00 00 e0 j0 o0 + signal = signal[..., :(frames + segments - 1) * segments, :] + + # Reshape so that signal.shape = (3, 5, 2) + # ab fg kl 00 00 + # 00 cd hi mn 00 + # 00 00 e0 j0 o0 + shape = full_shape([segments, (frames + segments - 1), frame_step]) + signal = array_ops.reshape(signal, shape) + + # Now, reduce over the columns, to achieve the desired sum. + signal = math_ops.reduce_sum(signal, -3) + + # Flatten the array. + shape = full_shape([(frames + segments - 1) * frame_step]) + signal = array_ops.reshape(signal, shape) + + # Truncate to final length. + signal = signal[..., :output_length] + + return signal diff --git a/tensorflow/python/ops/signal/shape_ops.py b/tensorflow/python/ops/signal/shape_ops.py index 02dd7c97e8..ae9c2ef28e 100644 --- a/tensorflow/python/ops/signal/shape_ops.py +++ b/tensorflow/python/ops/signal/shape_ops.py @@ -71,7 +71,7 @@ def frame(signal, frame_length, frame_step, pad_end=False, pad_value=0, axis=-1, ```python pcm = tf.placeholder(tf.float32, [None, 9152]) frames = tf.signal.frame(pcm, 512, 180) - magspec = tf.abs(tf.spectral.rfft(frames, [512])) + magspec = tf.abs(tf.signal.rfft(frames, [512])) image = tf.expand_dims(magspec, 3) ``` diff --git a/tensorflow/python/ops/signal/signal.py b/tensorflow/python/ops/signal/signal.py new file mode 100644 index 0000000000..cdc4d1c191 --- /dev/null +++ b/tensorflow/python/ops/signal/signal.py @@ -0,0 +1,65 @@ +# 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. +# ============================================================================== +"""Signal processing operations. + +See the [tf.signal](https://tensorflow.org/api_guides/python/contrib.signal) +guide. + +@@frame +@@hamming_window +@@hann_window +@@inverse_stft +@@inverse_stft_window_fn +@@mfccs_from_log_mel_spectrograms +@@linear_to_mel_weight_matrix +@@overlap_and_add +@@stft + +[hamming]: https://en.wikipedia.org/wiki/Window_function#Hamming_window +[hann]: https://en.wikipedia.org/wiki/Window_function#Hann_window +[mel]: https://en.wikipedia.org/wiki/Mel_scale +[mfcc]: https://en.wikipedia.org/wiki/Mel-frequency_cepstrum +[stft]: https://en.wikipedia.org/wiki/Short-time_Fourier_transform +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +# pylint: disable=unused-import +from tensorflow.python.ops.signal.dct_ops import dct +from tensorflow.python.ops.signal.dct_ops import idct +from tensorflow.python.ops.signal.fft_ops import fft +from tensorflow.python.ops.signal.fft_ops import fft2d +from tensorflow.python.ops.signal.fft_ops import fft3d +from tensorflow.python.ops.signal.fft_ops import ifft +from tensorflow.python.ops.signal.fft_ops import ifft2d +from tensorflow.python.ops.signal.fft_ops import ifft3d +from tensorflow.python.ops.signal.fft_ops import irfft +from tensorflow.python.ops.signal.fft_ops import irfft2d +from tensorflow.python.ops.signal.fft_ops import irfft3d +from tensorflow.python.ops.signal.fft_ops import rfft +from tensorflow.python.ops.signal.fft_ops import rfft2d +from tensorflow.python.ops.signal.fft_ops import rfft3d +from tensorflow.python.ops.signal.mel_ops import linear_to_mel_weight_matrix +from tensorflow.python.ops.signal.mfcc_ops import mfccs_from_log_mel_spectrograms +from tensorflow.python.ops.signal.reconstruction_ops import overlap_and_add +from tensorflow.python.ops.signal.shape_ops import frame +from tensorflow.python.ops.signal.spectral_ops import inverse_stft +from tensorflow.python.ops.signal.spectral_ops import inverse_stft_window_fn +from tensorflow.python.ops.signal.spectral_ops import stft +from tensorflow.python.ops.signal.window_ops import hamming_window +from tensorflow.python.ops.signal.window_ops import hann_window +# pylint: enable=unused-import diff --git a/tensorflow/python/ops/signal/spectral_ops.py b/tensorflow/python/ops/signal/spectral_ops.py index b0b7d964b9..f029e0a8b5 100644 --- a/tensorflow/python/ops/signal/spectral_ops.py +++ b/tensorflow/python/ops/signal/spectral_ops.py @@ -25,7 +25,7 @@ from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops -from tensorflow.python.ops import spectral_ops +from tensorflow.python.ops.signal import fft_ops from tensorflow.python.ops.signal import reconstruction_ops from tensorflow.python.ops.signal import shape_ops from tensorflow.python.ops.signal import window_ops @@ -86,9 +86,9 @@ def stft(signals, frame_length, frame_step, fft_length=None, window = window_fn(frame_length, dtype=framed_signals.dtype) framed_signals *= window - # spectral_ops.rfft produces the (fft_length/2 + 1) unique components of the + # fft_ops.rfft produces the (fft_length/2 + 1) unique components of the # FFT of the real windowed signals in framed_signals. - return spectral_ops.rfft(framed_signals, [fft_length]) + return fft_ops.rfft(framed_signals, [fft_length]) @tf_export('signal.inverse_stft_window_fn') @@ -232,7 +232,7 @@ def inverse_stft(stfts, fft_length = ops.convert_to_tensor(fft_length, name='fft_length') fft_length.shape.assert_has_rank(0) - real_frames = spectral_ops.irfft(stfts, [fft_length]) + real_frames = fft_ops.irfft(stfts, [fft_length]) # frame_length may be larger or smaller than fft_length, so we pad or # truncate real_frames to frame_length. diff --git a/tensorflow/python/ops/sort_ops.py b/tensorflow/python/ops/sort_ops.py new file mode 100644 index 0000000000..c3e23d701e --- /dev/null +++ b/tensorflow/python/ops/sort_ops.py @@ -0,0 +1,197 @@ +# 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. +# ============================================================================== +"""Support for sorting tensors. + +@@argsort +@@sort +""" + +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 ops as framework_ops +from tensorflow.python.framework import tensor_util +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import nn_ops +from tensorflow.python.util.tf_export import tf_export + + +@tf_export('sort') +def sort(values, axis=-1, direction='ASCENDING', name=None): + """Sorts a tensor. + + Args: + values: 1-D or higher numeric `Tensor`. + axis: The axis along which to sort. The default is -1, which sorts the last + axis. + direction: The direction in which to sort the values (`'ASCENDING'` or + `'DESCENDING'`). + name: Optional name for the operation. + + Returns: + A `Tensor` with the same dtype and shape as `values`, with the elements + sorted along the given `axis`. + + Raises: + ValueError: If axis is not a constant scalar, or the direction is invalid. + """ + with framework_ops.name_scope(name, 'sort'): + return _sort_or_argsort(values, axis, direction, return_argsort=False) + + +@tf_export('argsort') +def argsort(values, axis=-1, direction='ASCENDING', stable=False, name=None): + """Returns the indices of a tensor that give its sorted order along an axis. + + For a 1D tensor, `tf.gather(values, tf.argsort(values))` is equivalent to + `tf.sort(values)`. For higher dimensions, the output has the same shape as + `values`, but along the given axis, values represent the index of the sorted + element in that slice of the tensor at the given position. + + Args: + values: 1-D or higher numeric `Tensor`. + axis: The axis along which to sort. The default is -1, which sorts the last + axis. + direction: The direction in which to sort the values (`'ASCENDING'` or + `'DESCENDING'`). + stable: If True, equal elements in the original tensor will not be + re-ordered in the returned order. Unstable sort is not yet implemented, + but will eventually be the default for performance reasons. If you require + a stable order, pass `stable=True` for forwards compatibility. + name: Optional name for the operation. + + Returns: + An int32 `Tensor` with the same shape as `values`. The indices that would + sort each slice of the given `values` along the given `axis`. + + Raises: + ValueError: If axis is not a constant scalar, or the direction is invalid. + """ + del stable # Unused. + with framework_ops.name_scope(name, 'argsort'): + return _sort_or_argsort(values, axis, direction, return_argsort=True) + + +def _sort_or_argsort(values, axis, direction, return_argsort): + """Internal sort/argsort implementation. + + Args: + values: The input values. + axis: The axis along which to sort. + direction: 'ASCENDING' or 'DESCENDING'. + return_argsort: Whether to return the argsort result. + + Returns: + Either the sorted values, or the indices of the sorted values in the + original tensor. See the `sort` and `argsort` docstrings. + + Raises: + ValueError: If axis is not a constant scalar, or the direction is invalid. + """ + if direction not in _SORT_IMPL: + raise ValueError('%s should be one of %s' % (direction, ', '.join( + sorted(_SORT_IMPL.keys())))) + # Axis must be an integer, not a Tensor. + axis = framework_ops.convert_to_tensor(axis, name='axis') + axis_static = tensor_util.constant_value(axis) + if axis.shape.ndims != 0 or axis_static is None: + raise ValueError('axis must be a constant scalar') + axis_static = int(axis_static) # Avoids NumPy casting error + + values = framework_ops.convert_to_tensor(values, name='values') + + return _SORT_IMPL[direction](values, axis_static, return_argsort) + + +def _descending_sort(values, axis, return_argsort=False): + """Sorts values in reverse using `top_k`. + + Args: + values: Tensor of numeric values. + axis: Index of the axis which values should be sorted along. + return_argsort: If False, return the sorted values. If True, return the + indices that would sort the values. + + Returns: + The sorted values. + """ + k = array_ops.shape(values)[axis] + rank = array_ops.rank(values) + static_rank = values.shape.ndims + # Fast path: sorting the last axis. + if axis == -1 or axis + 1 == values.get_shape().ndims: + top_k_input = values + transposition = None + else: + # Otherwise, transpose the array. Swap axes `axis` and `rank - 1`. + if axis < 0: + # Calculate the actual axis index if counting from the end. Use the static + # rank if available, or else make the axis back into a tensor. + axis += static_rank or rank + if static_rank is not None: + # Prefer to calculate the transposition array in NumPy and make it a + # constant. + transposition = constant_op.constant( + np.r_[ + # Axes up to axis are unchanged. + np.arange(axis), + # Swap axis and rank - 1. + [static_rank - 1], + # Axes in [axis + 1, rank - 1) are unchanged. + np.arange(axis + 1, static_rank - 1), + # Swap axis and rank - 1. + [axis]], + name='transposition') + else: + # Generate the transposition array from the tensors. + transposition = array_ops.concat( + [ + # Axes up to axis are unchanged. + math_ops.range(axis), + # Swap axis and rank - 1. + [rank - 1], + # Axes in [axis + 1, rank - 1) are unchanged. + math_ops.range(axis + 1, rank - 1), + # Swap axis and rank - 1. + [axis] + ], + axis=0) + top_k_input = array_ops.transpose(values, transposition) + + values, indices = nn_ops.top_k(top_k_input, k) + return_value = indices if return_argsort else values + if transposition is not None: + # transposition contains a single cycle of length 2 (swapping 2 elements), + # so it is an involution (it is its own inverse). + return_value = array_ops.transpose(return_value, transposition) + return return_value + + +def _ascending_sort(values, axis, return_argsort=False): + # Negate the values to get the ascending order from descending sort. + values_or_indices = _descending_sort(-values, axis, return_argsort) + # If not argsort, negate the values again. + return values_or_indices if return_argsort else -values_or_indices + + +_SORT_IMPL = { + 'ASCENDING': _ascending_sort, + 'DESCENDING': _descending_sort, +} diff --git a/tensorflow/contrib/framework/python/ops/sort_ops_test.py b/tensorflow/python/ops/sort_ops_test.py similarity index 90% rename from tensorflow/contrib/framework/python/ops/sort_ops_test.py rename to tensorflow/python/ops/sort_ops_test.py index 791b32cd1e..17ce604cbf 100644 --- a/tensorflow/contrib/framework/python/ops/sort_ops_test.py +++ b/tensorflow/python/ops/sort_ops_test.py @@ -20,22 +20,25 @@ from __future__ import print_function import numpy as np -from tensorflow.contrib.framework.python.ops import sort_ops from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_util +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import random_ops +from tensorflow.python.ops import sort_ops from tensorflow.python.platform import test class SortTest(test.TestCase): + @test_util.run_deprecated_v1 def testRandom_lowDimensionality(self): self._testRandom_lowDimensionality(negative_axis=False) + @test_util.run_deprecated_v1 def testRandom_lowDimensionality_negative(self): self._testRandom_lowDimensionality(negative_axis=True) @@ -53,6 +56,7 @@ class SortTest(test.TestCase): np.sort(arr, axis=sort_axis), sort_ops.sort(constant_op.constant(arr), axis=sort_axis).eval()) + @test_util.run_deprecated_v1 def testRandom_highDimensionality(self): np.random.seed(100) for _ in range(20): @@ -65,6 +69,7 @@ class SortTest(test.TestCase): np.sort(arr, axis=sort_axis), sort_ops.sort(constant_op.constant(arr), axis=sort_axis).eval()) + @test_util.run_deprecated_v1 def testScalar(self): # Create an empty scalar where the static shape is unknown. zeros_length_1 = array_ops.zeros( @@ -77,21 +82,22 @@ class SortTest(test.TestCase): with self.assertRaises(errors.InvalidArgumentError): sort.eval() + @test_util.run_deprecated_v1 def testNegativeOutOfBounds_staticShape(self): arr = constant_op.constant([3, 4, 5]) with self.assertRaises(ValueError): sort_ops.sort(arr, axis=-4) + @test_util.run_deprecated_v1 def testDescending(self): arr = np.random.random((10, 5, 5)) with self.cached_session(): self.assertAllEqual( np.sort(arr, axis=0)[::-1], sort_ops.sort( - constant_op.constant(arr), - axis=0, - direction='DESCENDING').eval()) + constant_op.constant(arr), axis=0, direction='DESCENDING').eval()) + @test_util.run_deprecated_v1 def testSort_staticallyKnownRank_constantTransposition(self): # The transposition array should be a constant if the rank of "values" is # statically known. @@ -109,6 +115,7 @@ class SortTest(test.TestCase): tensor_util.constant_value(transposition), [0, 4, 2, 3, 1]) + @test_util.run_deprecated_v1 def testArgsort_1d(self): arr = np.random.random(42) with self.cached_session(): @@ -116,6 +123,7 @@ class SortTest(test.TestCase): np.sort(arr), array_ops.gather(arr, sort_ops.argsort(arr)).eval()) + @test_util.run_deprecated_v1 def testArgsort(self): arr = np.random.random((5, 6, 7, 8)) for axis in range(4): diff --git a/tensorflow/python/ops/sparse_grad.py b/tensorflow/python/ops/sparse_grad.py index 1223b290ff..2ca9c0c647 100644 --- a/tensorflow/python/ops/sparse_grad.py +++ b/tensorflow/python/ops/sparse_grad.py @@ -195,7 +195,7 @@ def _SparseTensorDenseMatMulGrad(op, grad): parts_a = array_ops.gather(grad, rows if not adj_a else cols) parts_b = array_ops.gather(b if not adj_b else array_ops.transpose(b), cols if not adj_a else rows) - a_values_grad = math_ops.reduce_sum(parts_a * parts_b, reduction_indices=1) + a_values_grad = math_ops.reduce_sum(parts_a * parts_b, axis=1) # gradients w.r.t. (a_indices, a_values, a_shape, b) return (None, a_values_grad, None, b_grad) diff --git a/tensorflow/python/ops/sparse_ops.py b/tensorflow/python/ops/sparse_ops.py index b98c7f5f65..346ab9c0cb 100644 --- a/tensorflow/python/ops/sparse_ops.py +++ b/tensorflow/python/ops/sparse_ops.py @@ -44,6 +44,9 @@ from tensorflow.python.ops.gen_sparse_ops import * # pylint: enable=wildcard-import from tensorflow.python.util import compat from tensorflow.python.util import deprecation +from tensorflow.python.util import dispatch +from tensorflow.python.util import tf_inspect +from tensorflow.python.util.tf_export import get_canonical_name_for_symbol from tensorflow.python.util.tf_export import tf_export @@ -186,7 +189,7 @@ def sparse_eye(num_rows, # pylint: disable=protected-access -@tf_export("sparse.concat", "sparse_concat") +@tf_export(v1=["sparse.concat", "sparse_concat"]) @deprecation.deprecated_endpoints("sparse_concat") @deprecation.deprecated_args( None, "concat_dim is deprecated, use axis instead", "concat_dim") @@ -292,6 +295,11 @@ def sparse_concat(axis, """ axis = deprecation.deprecated_argument_lookup("axis", axis, "concat_dim", concat_dim) + return sparse_concat_v2(axis, sp_inputs, expand_nonconcat_dim, name) + + +@tf_export("sparse.concat", v1=[]) +def sparse_concat_v2(axis, sp_inputs, expand_nonconcat_dim=False, name=None): # pylint: disable=missing-docstring sp_inputs = _convert_to_sparse_tensors(sp_inputs) if len(sp_inputs) == 1: # Degenerate case of one tensor. @@ -319,9 +327,15 @@ def sparse_concat(axis, return sparse_tensor.SparseTensor(output_ind, output_val, output_shape) -@tf_export("sparse.add", v1=["sparse.add", "sparse_add"]) +sparse_concat_v2.__doc__ = sparse_concat.__doc__.replace( + " concat_dim: The old (deprecated) name for axis.\n", "") + + +@tf_export(v1=["sparse.add", "sparse_add"]) @deprecation.deprecated_endpoints("sparse_add") -def sparse_add(a, b, thresh=0): +@deprecation.deprecated_args( + None, "thresh is deprecated, use threshold instead", "thresh") +def sparse_add(a, b, threshold=None, thresh=None): """Adds two tensors, at least one of each is a `SparseTensor`. If one `SparseTensor` and one `Tensor` are passed in, returns a `Tensor`. If @@ -359,12 +373,74 @@ def sparse_add(a, b, thresh=0): Args: a: The first operand; `SparseTensor` or `Tensor`. - b: The second operand; `SparseTensor` or `Tensor`. At least one operand + b: The second operand; `SparseTensor` or `Tensor`. At least one operand must be sparse. - thresh: A 0-D `Tensor`. The magnitude threshold that determines if an - output value/index pair takes space. Its dtype should match that of the - values if they are real; if the latter are complex64/complex128, then the - dtype should be float32/float64, correspondingly. + threshold: An optional 0-D `Tensor` (defaults to `0`). The magnitude + threshold that determines if an output value/index pair takes space. Its + dtype should match that of the values if they are real; if the latter are + complex64/complex128, then the dtype should be float32/float64, + correspondingly. + thresh: Deprecated alias for `threshold`. + + Returns: + A `SparseTensor` or a `Tensor`, representing the sum. + + Raises: + TypeError: If both `a` and `b` are `Tensor`s. Use `tf.add()` instead. + """ + threshold = deprecation.deprecated_argument_lookup("threshold", threshold, + "thresh", thresh) + if threshold is None: + threshold = 0 + return sparse_add_v2(a, b, threshold) + + +@tf_export("sparse.add", v1=[]) +def sparse_add_v2(a, b, threshold=0): + """Adds two tensors, at least one of each is a `SparseTensor`. + + If one `SparseTensor` and one `Tensor` are passed in, returns a `Tensor`. If + both arguments are `SparseTensor`s, this returns a `SparseTensor`. The order + of arguments does not matter. Use vanilla `tf.add()` for adding two dense + `Tensor`s. + + The shapes of the two operands must match: broadcasting is not supported. + + The indices of any input `SparseTensor` are assumed ordered in standard + lexicographic order. If this is not the case, before this step run + `SparseReorder` to restore index ordering. + + If both arguments are sparse, we perform "clipping" as follows. By default, + if two values sum to zero at some index, the output `SparseTensor` would still + include that particular location in its index, storing a zero in the + corresponding value slot. To override this, callers can specify `threshold`, + indicating that if the sum has a magnitude strictly smaller than `threshold`, + its corresponding value and index would then not be included. In particular, + `threshold == 0.0` (default) means everything is kept and actual thresholding + happens only for a positive value. + + For example, suppose the logical sum of two sparse operands is (densified): + + [ 2] + [.1 0] + [ 6 -.2] + + Then, + + * `threshold == 0` (the default): all 5 index/value pairs will be + returned. + * `threshold == 0.11`: only .1 and 0 will vanish, and the remaining three + index/value pairs will be returned. + * `threshold == 0.21`: .1, 0, and -.2 will vanish. + + Args: + a: The first operand; `SparseTensor` or `Tensor`. + b: The second operand; `SparseTensor` or `Tensor`. At least one operand + must be sparse. + threshold: A 0-D `Tensor`. The magnitude threshold that determines if an + output value/index pair takes space. Its dtype should match that of the + values if they are real; if the latter are complex64/complex128, then the + dtype should be float32/float64, correspondingly. Returns: A `SparseTensor` or a `Tensor`, representing the sum. @@ -380,11 +456,12 @@ def sparse_add(a, b, thresh=0): if all(isinstance(inp, sparse_classes) for inp in [a, b]): a = _convert_to_sparse_tensor(a) b = _convert_to_sparse_tensor(b) - thresh = ops.convert_to_tensor( - thresh, dtype=a.values.dtype.real_dtype.base_dtype, name="thresh") + threshold = ops.convert_to_tensor( + threshold, dtype=a.values.dtype.real_dtype.base_dtype, name="threshold") output_ind, output_val, output_shape = ( gen_sparse_ops.sparse_add(a.indices, a.values, a.dense_shape, - b.indices, b.values, b.dense_shape, thresh)) + b.indices, b.values, b.dense_shape, + threshold)) # Attempt to get output_shape statically. a.get_shape().assert_is_compatible_with(b.get_shape()) @@ -705,7 +782,7 @@ class KeywordRequired(object): return "KeywordRequired()" -@tf_export("sparse.split", "sparse_split") +@tf_export(v1=["sparse.split", "sparse_split"]) @deprecation.deprecated_endpoints("sparse_split") @deprecation.deprecated_args( None, "split_dim is deprecated, use axis instead", "split_dim") @@ -779,6 +856,51 @@ def sparse_split(keyword_required=KeywordRequired(), return sparse_tensors +@tf_export("sparse.split", v1=[]) +def sparse_split_v2(sp_input=None, + num_split=None, + axis=None, + name=None): + """Split a `SparseTensor` into `num_split` tensors along `axis`. + + If the `sp_input.dense_shape[axis]` is not an integer multiple of `num_split` + each slice starting from 0:`shape[axis] % num_split` gets extra one + dimension. For example, if `axis = 1` and `num_split = 2` and the + input is: + + input_tensor = shape = [2, 7] + [ a d e ] + [b c ] + + Graphically the output tensors are: + + output_tensor[0] = + [ a ] + [b c ] + + output_tensor[1] = + [ d e ] + [ ] + + Args: + sp_input: The `SparseTensor` to split. + num_split: A Python integer. The number of ways to split. + axis: A 0-D `int32` `Tensor`. The dimension along which to split. + name: A name for the operation (optional). + + Returns: + `num_split` `SparseTensor` objects resulting from splitting `value`. + + Raises: + TypeError: If `sp_input` is not a `SparseTensor`. + """ + return sparse_split(sp_input=sp_input, + num_split=num_split, + axis=axis, + name=name, + split_dim=None) + + @tf_export("sparse.slice", v1=["sparse.slice", "sparse_slice"]) @deprecation.deprecated_endpoints("sparse_slice") def sparse_slice(sp_input, start, size, name=None): @@ -829,7 +951,7 @@ def sparse_slice(sp_input, start, size, name=None): output_shape) -@tf_export("sparse_to_dense") +@tf_export(v1=["sparse_to_dense"]) @deprecation.deprecated( None, "Create a `tf.sparse.SparseTensor` and use `tf.sparse.to_dense` instead.") @@ -888,7 +1010,86 @@ def sparse_to_dense(sparse_indices, name=name) -@tf_export("sparse.reduce_max", "sparse_reduce_max") +@tf_export("sparse.reduce_max", v1=[]) +def sparse_reduce_max_v2( + sp_input, axis=None, keepdims=None, output_is_sparse=False, name=None): + """Computes the max of elements across dimensions of a SparseTensor. + + This Op takes a SparseTensor and is the sparse counterpart to + `tf.reduce_max()`. In particular, this Op also returns a dense `Tensor` + if `output_is_sparse` is `False`, or a `SparseTensor` if `output_is_sparse` + is `True`. + + Note: A gradient is not defined for this function, so it can't be used + in training models that need gradient descent. + + Reduces `sp_input` along the dimensions given in `axis`. Unless + `keepdims` is true, the rank of the tensor is reduced by 1 for each entry in + `axis`. If `keepdims` is true, the reduced dimensions are retained + with length 1. + + If `axis` has no entries, all dimensions are reduced, and a tensor + with a single element is returned. Additionally, the axes can be negative, + similar to the indexing rules in Python. + + The values not defined in `sp_input` don't participate in the reduce max, + as opposed to be implicitly assumed 0 -- hence it can return negative values + for sparse `axis`. But, in case there are no values in + `axis`, it will reduce to 0. See second example below. + + For example: + + ```python + # 'x' represents [[1, ?, 2] + # [?, 3, ?]] + # where ? is implicitly-zero. + tf.sparse.reduce_max(x) ==> 3 + tf.sparse.reduce_max(x, 0) ==> [1, 3, 2] + tf.sparse.reduce_max(x, 1) ==> [2, 3] # Can also use -1 as the axis. + tf.sparse.reduce_max(x, 1, keepdims=True) ==> [[2], [3]] + tf.sparse.reduce_max(x, [0, 1]) ==> 3 + + # 'y' represents [[-7, ?] + # [ 4, 3] + # [ ?, ?] + tf.sparse.reduce_max(x, 1) ==> [-7, 4, 0] + ``` + + Args: + sp_input: The SparseTensor to reduce. Should have numeric type. + axis: The dimensions to reduce; list or scalar. If `None` (the + default), reduces all dimensions. + keepdims: If true, retain reduced dimensions with length 1. + output_is_sparse: If true, returns a `SparseTensor` instead of a dense + `Tensor` (the default). + name: A name for the operation (optional). + + Returns: + The reduced Tensor or the reduced SparseTensor if `output_is_sparse` is + True. + """ + if keepdims is None: + keepdims = False + + # reduction_axes is the deprecated name for axis. + reduction_axes = None + + if output_is_sparse: + output_ind, output_val, output_shape = ( + gen_sparse_ops.sparse_reduce_max_sparse( + sp_input.indices, sp_input.values, sp_input.dense_shape, + math_ops._ReductionDims(sp_input, axis, reduction_axes), keepdims, + name=name)) + + return sparse_tensor.SparseTensor(output_ind, output_val, output_shape) + + return gen_sparse_ops.sparse_reduce_max( + sp_input.indices, sp_input.values, sp_input.dense_shape, + math_ops._ReductionDims(sp_input, axis, reduction_axes), keepdims, + name=name) + + +@tf_export(v1=["sparse.reduce_max", "sparse_reduce_max"]) @deprecation.deprecated_endpoints("sparse_reduce_max") @deprecation.deprecated_args( None, "keep_dims is deprecated, use keepdims instead", "keep_dims") @@ -956,7 +1157,7 @@ def sparse_reduce_max(sp_input, axis=None, keepdims=None, math_ops._ReductionDims(sp_input, axis, reduction_axes), keepdims) -@tf_export("sparse.reduce_max_sparse", "sparse_reduce_max_sparse") +@tf_export(v1=["sparse.reduce_max_sparse", "sparse_reduce_max_sparse"]) @deprecation.deprecated_endpoints("sparse_reduce_max_sparse") @deprecation.deprecated_args( None, "keep_dims is deprecated, use keepdims instead", "keep_dims") @@ -1007,7 +1208,74 @@ def sparse_reduce_max_sparse(sp_input, return sparse_tensor.SparseTensor(output_ind, output_val, output_shape) -@tf_export("sparse.reduce_sum", "sparse_reduce_sum") +@tf_export("sparse.reduce_sum", v1=[]) +def sparse_reduce_sum_v2( + sp_input, axis=None, keepdims=None, output_is_sparse=False, name=None): + """Computes the sum of elements across dimensions of a SparseTensor. + + This Op takes a SparseTensor and is the sparse counterpart to + `tf.reduce_sum()`. In particular, this Op also returns a dense `Tensor` + if `output_is_sparse` is `False`, or a `SparseTensor` if `output_is_sparse` + is `True`. + + Note: if `output_is_sparse` is True, a gradient is not defined for this + function, so it can't be used in training models that need gradient descent. + + Reduces `sp_input` along the dimensions given in `axis`. Unless `keepdims` is + true, the rank of the tensor is reduced by 1 for each entry in `axis`. If + `keepdims` is true, the reduced dimensions are retained with length 1. + + If `axis` has no entries, all dimensions are reduced, and a tensor + with a single element is returned. Additionally, the axes can be negative, + similar to the indexing rules in Python. + + For example: + + ```python + # 'x' represents [[1, ?, 1] + # [?, 1, ?]] + # where ? is implicitly-zero. + tf.sparse.reduce_sum(x) ==> 3 + tf.sparse.reduce_sum(x, 0) ==> [1, 1, 1] + tf.sparse.reduce_sum(x, 1) ==> [2, 1] # Can also use -1 as the axis. + tf.sparse.reduce_sum(x, 1, keepdims=True) ==> [[2], [1]] + tf.sparse.reduce_sum(x, [0, 1]) ==> 3 + ``` + + Args: + sp_input: The SparseTensor to reduce. Should have numeric type. + axis: The dimensions to reduce; list or scalar. If `None` (the + default), reduces all dimensions. + keepdims: If true, retain reduced dimensions with length 1. + output_is_sparse: If true, returns a `SparseTensor` instead of a dense + `Tensor` (the default). + name: A name for the operation (optional). + + Returns: + The reduced Tensor or the reduced SparseTensor if `output_is_sparse` is + True. + """ + if keepdims is None: + keepdims = False + + # reduction_axes is the deprecated name for axis. + reduction_axes = None + + if output_is_sparse: + output_ind, output_val, output_shape = ( + gen_sparse_ops.sparse_reduce_sum_sparse( + sp_input.indices, sp_input.values, sp_input.dense_shape, + math_ops._ReductionDims(sp_input, axis, reduction_axes), keepdims, + name=name)) + return sparse_tensor.SparseTensor(output_ind, output_val, output_shape) + + return gen_sparse_ops.sparse_reduce_sum( + sp_input.indices, sp_input.values, sp_input.dense_shape, + math_ops._ReductionDims(sp_input, axis, reduction_axes), keepdims, + name=name) + + +@tf_export(v1=["sparse.reduce_sum", "sparse_reduce_sum"]) @deprecation.deprecated_endpoints("sparse_reduce_sum") @deprecation.deprecated_args( None, "keep_dims is deprecated, use keepdims instead", "keep_dims") @@ -1062,7 +1330,7 @@ def sparse_reduce_sum(sp_input, axis=None, keepdims=None, math_ops._ReductionDims(sp_input, axis, reduction_axes), keepdims) -@tf_export("sparse.reduce_sum_sparse", "sparse_reduce_sum_sparse") +@tf_export(v1=["sparse.reduce_sum_sparse", "sparse_reduce_sum_sparse"]) @deprecation.deprecated_endpoints("sparse_reduce_sum_sparse") @deprecation.deprecated_args( None, "keep_dims is deprecated, use keepdims instead", "keep_dims") @@ -1157,7 +1425,7 @@ def sparse_tensor_to_dense(sp_input, """ sp_input = _convert_to_sparse_tensor(sp_input) - return sparse_to_dense( + return gen_sparse_ops.sparse_to_dense( sp_input.indices, sp_input.dense_shape, sp_input.values, @@ -1231,8 +1499,8 @@ def sparse_to_indicator(sp_input, vocab_size, name=None): sp_new, default_value=False, validate_indices=False, name=name) -@tf_export("sparse.merge", v1=["sparse.merge", "sparse_merge"]) -@deprecation.deprecated_endpoints("sparse_merge") +@tf_export(v1=["sparse.merge", "sparse_merge"]) +@deprecation.deprecated(None, "No similar op available at this time.") def sparse_merge(sp_ids, sp_values, vocab_size, name=None, already_sorted=False): """Combines a batch of feature ids and values into a single `SparseTensor`. @@ -1593,8 +1861,7 @@ def sparse_fill_empty_rows(sp_input, default_value, name=None): dense_shape=sp_input.dense_shape), empty_row_indicator) -@tf_export( - "io.serialize_sparse", v1=["io.serialize_sparse", "serialize_sparse"]) +@tf_export(v1=["io.serialize_sparse", "serialize_sparse"]) @deprecation.deprecated_endpoints("serialize_sparse") def serialize_sparse(sp_input, name=None, out_type=dtypes.string): """Serialize a `SparseTensor` into a 3-vector (1-D `Tensor`) object. @@ -1608,6 +1875,25 @@ def serialize_sparse(sp_input, name=None, out_type=dtypes.string): A 3-vector (1-D `Tensor`), with each column representing the serialized `SparseTensor`'s indices, values, and shape (respectively). + Raises: + TypeError: If `sp_input` is not a `SparseTensor`. + """ + return serialize_sparse_v2(sp_input, out_type, name) + + +@tf_export("io.serialize_sparse", v1=[]) +def serialize_sparse_v2(sp_input, out_type=dtypes.string, name=None): + """Serialize a `SparseTensor` into a 3-vector (1-D `Tensor`) object. + + Args: + sp_input: The input `SparseTensor`. + out_type: The `dtype` to use for serialization. + name: A name prefix for the returned tensors (optional). + + Returns: + A 3-vector (1-D `Tensor`), with each column representing the serialized + `SparseTensor`'s indices, values, and shape (respectively). + Raises: TypeError: If `sp_input` is not a `SparseTensor`. """ @@ -1621,9 +1907,7 @@ def serialize_sparse(sp_input, name=None, out_type=dtypes.string): out_type=out_type) -@tf_export( - "io.serialize_many_sparse", - v1=["io.serialize_many_sparse", "serialize_many_sparse"]) +@tf_export(v1=["io.serialize_many_sparse", "serialize_many_sparse"]) @deprecation.deprecated_endpoints("serialize_many_sparse") def serialize_many_sparse(sp_input, name=None, out_type=dtypes.string): """Serialize `N`-minibatch `SparseTensor` into an `[N, 3]` `Tensor`. @@ -1646,6 +1930,34 @@ def serialize_many_sparse(sp_input, name=None, out_type=dtypes.string): represents serialized `SparseTensor`'s indices, values, and shape (respectively). + Raises: + TypeError: If `sp_input` is not a `SparseTensor`. + """ + return serialize_many_sparse_v2(sp_input, out_type, name) + + +@tf_export("io.serialize_many_sparse", v1=[]) +def serialize_many_sparse_v2(sp_input, out_type=dtypes.string, name=None): + """Serialize `N`-minibatch `SparseTensor` into an `[N, 3]` `Tensor`. + + The `SparseTensor` must have rank `R` greater than 1, and the first dimension + is treated as the minibatch dimension. Elements of the `SparseTensor` + must be sorted in increasing order of this first dimension. The serialized + `SparseTensor` objects going into each row of the output `Tensor` will have + rank `R-1`. + + The minibatch size `N` is extracted from `sparse_shape[0]`. + + Args: + sp_input: The input rank `R` `SparseTensor`. + out_type: The `dtype` to use for serialization. + name: A name prefix for the returned tensors (optional). + + Returns: + A matrix (2-D `Tensor`) with `N` rows and `3` columns. Each column + represents serialized `SparseTensor`'s indices, values, and shape + (respectively). + Raises: TypeError: If `sp_input` is not a `SparseTensor`. """ @@ -1798,7 +2110,9 @@ def deserialize_many_sparse(serialized_sparse, dtype, rank=None, name=None): return sparse_tensor.SparseTensor(output_indices, output_values, output_shape) -@tf_export("sparse.matmul", v1=["sparse.matmul", "sparse_tensor_dense_matmul"]) +@tf_export("sparse.sparse_dense_matmul", + v1=["sparse.sparse_dense_matmul", "sparse.matmul", + "sparse_tensor_dense_matmul"]) @deprecation.deprecated_endpoints("sparse_tensor_dense_matmul") def sparse_tensor_dense_matmul(sp_a, b, @@ -2362,3 +2676,47 @@ def _take_many_sparse_from_tensors_map(sparse_map_op, output_shape.set_shape([rank]) return sparse_tensor.SparseTensor(output_indices, output_values, output_shape) + + +class _UnaryMapValueDispatcher(dispatch.OpDispatcher): + """OpDispatcher for unary ops that maps base function across sparse values.""" + + def __init__(self, original_func): + self._original_func = original_func + func_name = get_canonical_name_for_symbol(original_func) + arg_names = tf_inspect.getfullargspec(original_func)[0] + self._x = arg_names[0] + original_func.__doc__ = ( + original_func.__doc__.rstrip() + "\n\n" + + (" If `{x}` is a `SparseTensor`, returns\n" + " `SparseTensor({x}.indices, tf.{func}({x}.values, ...), " + "{x}.dense_shape)`").format(x=self._x, func=func_name)) + + def handle(self, args, kwargs): + if args: + x, args = args[0], args[1:] + else: + x = kwargs.pop(self._x, None) + if isinstance(x, sparse_tensor.SparseTensor): + return sparse_tensor.SparseTensor( + indices=x.indices, + values=self._original_func(x.values, *args, **kwargs), + dense_shape=x.dense_shape) + else: + return self.NOT_SUPPORTED + + +_UNARY_OPS = [ + # TODO(b/120307967) Add dispatchers for additional TensorFlow ops. + math_ops.abs, + math_ops.negative, + math_ops.sign, + math_ops.square, + math_ops.sqrt, + math_ops.erf, + math_ops.tanh, + math_ops.bessel_i0e, + math_ops.bessel_i1e, +] +for unary_op in _UNARY_OPS: + _UnaryMapValueDispatcher(unary_op).register(unary_op) diff --git a/tensorflow/python/ops/sparse_ops_test.py b/tensorflow/python/ops/sparse_ops_test.py index 4ee1569249..031069a0f0 100644 --- a/tensorflow/python/ops/sparse_ops_test.py +++ b/tensorflow/python/ops/sparse_ops_test.py @@ -18,18 +18,20 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +from absl.testing import parameterized import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import sparse_tensor from tensorflow.python.framework import test_util +from tensorflow.python.ops import math_ops from tensorflow.python.ops import sparse_ops from tensorflow.python.platform import googletest @test_util.run_all_in_graph_and_eager_modes -class SparseOpsTest(test_util.TensorFlowTestCase): +class SparseOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): def testSparseEye(self): def test_one(n, m, as_tensors): @@ -77,5 +79,23 @@ class SparseOpsTest(test_util.TensorFlowTestCase): d = sparse_ops.sparse_to_dense(s.indices, s.dense_shape, s.values) self.assertAllEqual(self.evaluate(d), expected_after) + @parameterized.parameters([ + (math_ops.abs, [1.0, -1.0, 3.0, -4.0], [1.0, 1.0, 3.0, 4.0]), + (math_ops.negative, [1.0, -1.0, 3.0, -4.0], [-1.0, 1.0, -3.0, 4.0]), + (math_ops.sign, [3.0, -2.0, 0.0, -4.0], [1.0, -1.0, 0.0, -1.0]), + (math_ops.square, [1.0, -1.0, 3.0, -4.0], [1.0, 1.0, 9.0, 16.0]), + ]) + def testUnarySparseDispatch(self, op, values, expected): + st = sparse_tensor.SparseTensor( + indices=[[0, 0], [0, 1], [2, 0], [2, 4]], + values=values, + dense_shape=[3, 6]) + result = op(st) + result_value = self.evaluate(result) + self.assertAllEqual(result_value.indices, st.indices) + self.assertAllEqual(result_value.values, expected) + self.assertAllEqual(result_value.dense_shape, st.dense_shape) + + if __name__ == '__main__': googletest.main() diff --git a/tensorflow/python/ops/special_math_ops.py b/tensorflow/python/ops/special_math_ops.py index f44f694109..21f4996798 100644 --- a/tensorflow/python/ops/special_math_ops.py +++ b/tensorflow/python/ops/special_math_ops.py @@ -70,8 +70,7 @@ def lbeta(x, name=None): x = ops.convert_to_tensor(x, name='x') # Note reduce_sum([]) = 0. - log_prod_gamma_x = math_ops.reduce_sum( - math_ops.lgamma(x), reduction_indices=[-1]) + log_prod_gamma_x = math_ops.reduce_sum(math_ops.lgamma(x), axis=[-1]) # Note lgamma(0) = infinity, so if x = [] # log_gamma_sum_x = lgamma(0) = infinity, and @@ -264,11 +263,11 @@ def einsum(equation, *inputs, **kwargs): missing_indices = set(temp_axis_labels) - set(output_axis_labels) if missing_indices: - reduction_indices = [ + axis = [ i for i, a in enumerate(temp_axis_labels) if a not in output_axis_labels ] - temp = math_ops.reduce_sum(temp, reduction_indices=reduction_indices) + temp = math_ops.reduce_sum(temp, axis=axis) temp_axis_labels = ''.join( a for a in temp_axis_labels if a in output_axis_labels) diff --git a/tensorflow/python/ops/special_math_ops_test.py b/tensorflow/python/ops/special_math_ops_test.py index 7438cdb3f1..94aaebed95 100644 --- a/tensorflow/python/ops/special_math_ops_test.py +++ b/tensorflow/python/ops/special_math_ops_test.py @@ -46,6 +46,7 @@ class LBetaTest(test.TestCase): 0.5, self.evaluate(math_ops.exp(special_math_ops.lbeta(x_one_half)))) self.assertEqual([], special_math_ops.lbeta(x_one).get_shape()) + @test_util.run_deprecated_v1 def test_one_dimensional_arg_dynamic(self): # Should evaluate to 1 and 1/2. x_one = [1, 1.] @@ -57,6 +58,7 @@ class LBetaTest(test.TestCase): self.assertAllClose(0.5, beta_ph.eval(feed_dict={ph: x_one_half})) + @test_util.run_deprecated_v1 def test_four_dimensional_arg_with_partial_shape_dynamic(self): x_ = np.ones((3, 2, 3, 4)) # Gamma(1) = 0! = 1 @@ -81,6 +83,7 @@ class LBetaTest(test.TestCase): self.evaluate(math_ops.exp(special_math_ops.lbeta(x_one_half)))) self.assertEqual((2,), special_math_ops.lbeta(x_one_half).get_shape()) + @test_util.run_deprecated_v1 def test_two_dimensional_arg_dynamic(self): # Should evaluate to 1/2. x_one_half = [[2, 1.], [2, 1.]] @@ -288,6 +291,7 @@ class EinsumTest(test.TestCase): for case in self.long_cases: self.run_test(case) + @test_util.run_deprecated_v1 def test_invalid(self): for axes in self.invalid_cases: inputs = [ @@ -297,6 +301,7 @@ class EinsumTest(test.TestCase): with self.assertRaises(ValueError): _ = special_math_ops.einsum(axes, *inputs) + @test_util.run_deprecated_v1 def test_invalid_keyword_arguments(self): m0 = array_ops.placeholder(dtypes.int32, shape=(1, None)) m1 = array_ops.placeholder(dtypes.int32, shape=(None, 1)) @@ -311,11 +316,13 @@ class EinsumTest(test.TestCase): invalid1='value1', invalid2='value2') + @test_util.run_deprecated_v1 def test_repeated_axis_single_input(self): x = array_ops.placeholder(dtypes.float32, shape=[2, 2]) with self.assertRaises(ValueError): _ = special_math_ops.einsum('ii->', x) + @test_util.run_deprecated_v1 def test_dim_mismatch(self): for axes, input_shapes in self.dim_mismatch_cases: inputs = [ diff --git a/tensorflow/python/ops/spectral_grad.py b/tensorflow/python/ops/spectral_grad.py deleted file mode 100644 index 0af24114ac..0000000000 --- a/tensorflow/python/ops/spectral_grad.py +++ /dev/null @@ -1,185 +0,0 @@ -# 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. -# ============================================================================== -"""Gradients for operators defined in spectral_ops.py.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import numpy as np - -from tensorflow.python.framework import dtypes -from tensorflow.python.framework import ops -from tensorflow.python.ops import array_ops -from tensorflow.python.ops import math_ops -from tensorflow.python.ops import spectral_ops - - -def _FFTSizeForGrad(grad, rank): - return math_ops.reduce_prod(array_ops.shape(grad)[-rank:]) - - -@ops.RegisterGradient("FFT") -def _FFTGrad(_, grad): - size = math_ops.cast(_FFTSizeForGrad(grad, 1), grad.dtype) - return spectral_ops.ifft(grad) * size - - -@ops.RegisterGradient("IFFT") -def _IFFTGrad(_, grad): - rsize = math_ops.cast( - 1. / math_ops.cast(_FFTSizeForGrad(grad, 1), grad.dtype.real_dtype), - grad.dtype) - return spectral_ops.fft(grad) * rsize - - -@ops.RegisterGradient("FFT2D") -def _FFT2DGrad(_, grad): - size = math_ops.cast(_FFTSizeForGrad(grad, 2), grad.dtype) - return spectral_ops.ifft2d(grad) * size - - -@ops.RegisterGradient("IFFT2D") -def _IFFT2DGrad(_, grad): - rsize = math_ops.cast( - 1. / math_ops.cast(_FFTSizeForGrad(grad, 2), grad.dtype.real_dtype), - grad.dtype) - return spectral_ops.fft2d(grad) * rsize - - -@ops.RegisterGradient("FFT3D") -def _FFT3DGrad(_, grad): - size = math_ops.cast(_FFTSizeForGrad(grad, 3), grad.dtype) - return spectral_ops.ifft3d(grad) * size - - -@ops.RegisterGradient("IFFT3D") -def _IFFT3DGrad(_, grad): - rsize = math_ops.cast( - 1. / math_ops.cast(_FFTSizeForGrad(grad, 3), grad.dtype.real_dtype), - grad.dtype) - return spectral_ops.fft3d(grad) * rsize - - -def _RFFTGradHelper(rank, irfft_fn): - """Returns a gradient function for an RFFT of the provided rank.""" - # Can't happen because we don't register a gradient for RFFT3D. - assert rank in (1, 2), "Gradient for RFFT3D is not implemented." - - def _Grad(op, grad): - """A gradient function for RFFT with the provided `rank` and `irfft_fn`.""" - fft_length = op.inputs[1] - input_shape = array_ops.shape(op.inputs[0]) - is_even = math_ops.cast(1 - (fft_length[-1] % 2), dtypes.complex64) - - def _TileForBroadcasting(matrix, t): - expanded = array_ops.reshape( - matrix, - array_ops.concat([ - array_ops.ones([array_ops.rank(t) - 2], dtypes.int32), - array_ops.shape(matrix) - ], 0)) - return array_ops.tile( - expanded, array_ops.concat([array_ops.shape(t)[:-2], [1, 1]], 0)) - - def _MaskMatrix(length): - # TODO(rjryan): Speed up computation of twiddle factors using the - # following recurrence relation and cache them across invocations of RFFT. - # - # t_n = exp(sqrt(-1) * pi * n^2 / line_len) - # for n = 0, 1,..., line_len-1. - # For n > 2, use t_n = t_{n-1}^2 / t_{n-2} * t_1^2 - a = array_ops.tile( - array_ops.expand_dims(math_ops.range(length), 0), (length, 1)) - b = array_ops.transpose(a, [1, 0]) - return math_ops.exp(-2j * np.pi * math_ops.cast(a * b, dtypes.complex64) / - math_ops.cast(length, dtypes.complex64)) - - def _YMMask(length): - """A sequence of [1+0j, -1+0j, 1+0j, -1+0j, ...] with length `length`.""" - return math_ops.cast(1 - 2 * (math_ops.range(length) % 2), - dtypes.complex64) - - y0 = grad[..., 0:1] - if rank == 1: - ym = grad[..., -1:] - extra_terms = y0 + is_even * ym * _YMMask(input_shape[-1]) - elif rank == 2: - # Create a mask matrix for y0 and ym. - base_mask = _MaskMatrix(input_shape[-2]) - - # Tile base_mask to match y0 in shape so that we can batch-matmul the - # inner 2 dimensions. - tiled_mask = _TileForBroadcasting(base_mask, y0) - - y0_term = math_ops.matmul(tiled_mask, math_ops.conj(y0)) - extra_terms = y0_term - - ym = grad[..., -1:] - ym_term = math_ops.matmul(tiled_mask, math_ops.conj(ym)) - - inner_dim = input_shape[-1] - ym_term = array_ops.tile( - ym_term, - array_ops.concat([ - array_ops.ones([array_ops.rank(grad) - 1], dtypes.int32), - [inner_dim] - ], 0)) * _YMMask(inner_dim) - - extra_terms += is_even * ym_term - - # The gradient of RFFT is the IRFFT of the incoming gradient times a scaling - # factor, plus some additional terms to make up for the components dropped - # due to Hermitian symmetry. - input_size = math_ops.to_float(_FFTSizeForGrad(op.inputs[0], rank)) - irfft = irfft_fn(grad, fft_length) - return 0.5 * (irfft * input_size + math_ops.real(extra_terms)), None - - return _Grad - - -def _IRFFTGradHelper(rank, rfft_fn): - """Returns a gradient function for an IRFFT of the provided rank.""" - # Can't happen because we don't register a gradient for IRFFT3D. - assert rank in (1, 2), "Gradient for IRFFT3D is not implemented." - - def _Grad(op, grad): - """A gradient function for IRFFT with the provided `rank` and `rfft_fn`.""" - # Generate a simple mask like [1.0, 2.0, ..., 2.0, 1.0] for even-length FFTs - # and [1.0, 2.0, ..., 2.0] for odd-length FFTs. To reduce extra ops in the - # graph we special-case the situation where the FFT length and last - # dimension of the input are known at graph construction time. - fft_length = op.inputs[1] - is_odd = math_ops.mod(fft_length[-1], 2) - input_last_dimension = array_ops.shape(op.inputs[0])[-1] - mask = array_ops.concat( - [[1.0], 2.0 * array_ops.ones([input_last_dimension - 2 + is_odd]), - array_ops.ones([1 - is_odd])], 0) - - rsize = math_ops.reciprocal(math_ops.to_float(_FFTSizeForGrad(grad, rank))) - - # The gradient of IRFFT is the RFFT of the incoming gradient times a scaling - # factor and a mask. The mask scales the gradient for the Hermitian - # symmetric components of the RFFT by a factor of two, since these - # components are de-duplicated in the RFFT. - rfft = rfft_fn(grad, fft_length) - return rfft * math_ops.cast(rsize * mask, dtypes.complex64), None - - return _Grad - - -ops.RegisterGradient("RFFT")(_RFFTGradHelper(1, spectral_ops.irfft)) -ops.RegisterGradient("IRFFT")(_IRFFTGradHelper(1, spectral_ops.rfft)) -ops.RegisterGradient("RFFT2D")(_RFFTGradHelper(2, spectral_ops.irfft2d)) -ops.RegisterGradient("IRFFT2D")(_IRFFTGradHelper(2, spectral_ops.rfft2d)) diff --git a/tensorflow/python/ops/standard_ops.py b/tensorflow/python/ops/standard_ops.py index 4f1662ab08..c614d072ba 100644 --- a/tensorflow/python/ops/standard_ops.py +++ b/tensorflow/python/ops/standard_ops.py @@ -31,7 +31,6 @@ from tensorflow.python.ops import manip_grad from tensorflow.python.ops import math_grad from tensorflow.python.ops import random_grad from tensorflow.python.ops import sparse_grad -from tensorflow.python.ops import spectral_grad from tensorflow.python.ops import state_grad from tensorflow.python.ops import tensor_array_grad @@ -51,6 +50,7 @@ from tensorflow.python.ops.control_flow_ops import group from tensorflow.python.ops.control_flow_ops import no_op from tensorflow.python.ops.control_flow_ops import tuple # pylint: disable=redefined-builtin # pylint: enable=redefined-builtin +from tensorflow.python.eager import wrap_function from tensorflow.python.ops.control_flow_ops import while_loop from tensorflow.python.ops.data_flow_ops import * from tensorflow.python.ops.functional_ops import * @@ -72,6 +72,7 @@ from tensorflow.python.ops.partitioned_variables import * from tensorflow.python.ops.random_ops import * from tensorflow.python.ops.script_ops import py_func from tensorflow.python.ops.session_ops import * +from tensorflow.python.ops.sort_ops import * from tensorflow.python.ops.sparse_ops import * from tensorflow.python.ops.state_ops import assign from tensorflow.python.ops.state_ops import assign_add diff --git a/tensorflow/python/ops/stateless_random_ops.py b/tensorflow/python/ops/stateless_random_ops.py index c6defabacd..b119049b16 100644 --- a/tensorflow/python/ops/stateless_random_ops.py +++ b/tensorflow/python/ops/stateless_random_ops.py @@ -24,6 +24,7 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.ops import random_ops from tensorflow.python.ops import math_ops +from tensorflow.python.util import deprecation from tensorflow.python.util.tf_export import tf_export ops.NotDifferentiable("StatelessMultinomial") @@ -179,7 +180,9 @@ def stateless_truncated_normal(shape, return math_ops.add(rnd * stddev, mean, name=name) -@tf_export("random.stateless_multinomial") +@tf_export(v1=["random.stateless_multinomial"]) +@deprecation.deprecated( + date=None, instructions="Use tf.random.stateless_categorical instead.") def stateless_multinomial(logits, num_samples, seed, @@ -207,13 +210,58 @@ def stateless_multinomial(logits, `[i, :]` represents the unnormalized log-probabilities for all classes. num_samples: 0-D. Number of independent samples to draw for each row slice. seed: A shape [2] integer Tensor of seeds to the random number generator. - name: Optional name for the operation. output_dtype: integer type to use for the output. Defaults to int64. + name: Optional name for the operation. Returns: The drawn samples of shape `[batch_size, num_samples]`. """ with ops.name_scope(name, "stateless_multinomial", [logits, seed]): - logits = ops.convert_to_tensor(logits, name="logits") - return gen_stateless_random_ops.stateless_multinomial( - logits, num_samples, seed, output_dtype=output_dtype) + return stateless_multinomial_categorical_impl(logits, num_samples, + output_dtype, seed) + + +@tf_export("random.stateless_categorical") +def stateless_categorical(logits, + num_samples, + seed, + dtype=dtypes.int64, + name=None): + """Draws deterministic pseudorandom samples from a categorical distribution. + + This is a stateless version of `tf.categorical`: if run twice with the + same seeds, it will produce the same pseudorandom numbers. The output is + consistent across multiple runs on the same hardware (and between CPU + and GPU), but may change between versions of TensorFlow or on non-CPU/GPU + hardware. + + Example: + + ```python + # samples has shape [1, 5], where each value is either 0 or 1 with equal + # probability. + samples = tf.random.stateless_categorical( + tf.log([[10., 10.]]), 5, seed=[7, 17]) + ``` + + Args: + logits: 2-D Tensor with shape `[batch_size, num_classes]`. Each slice + `[i, :]` represents the unnormalized log-probabilities for all classes. + num_samples: 0-D. Number of independent samples to draw for each row slice. + seed: A shape [2] integer Tensor of seeds to the random number generator. + dtype: integer type to use for the output. Defaults to int64. + name: Optional name for the operation. + + Returns: + The drawn samples of shape `[batch_size, num_samples]`. + """ + with ops.name_scope(name, "stateless_categorical", [logits, seed]): + return stateless_multinomial_categorical_impl(logits, num_samples, dtype, + seed) + + +def stateless_multinomial_categorical_impl(logits, num_samples, dtype, seed): + """Implementation for stateless multinomial/categorical ops (v1/v2).""" + logits = ops.convert_to_tensor(logits, name="logits") + return gen_stateless_random_ops.stateless_multinomial( + logits, num_samples, seed, output_dtype=dtype) diff --git a/tensorflow/python/ops/string_ops.py b/tensorflow/python/ops/string_ops.py index 25e86cadeb..b6b329c486 100644 --- a/tensorflow/python/ops/string_ops.py +++ b/tensorflow/python/ops/string_ops.py @@ -28,6 +28,7 @@ from tensorflow.python.framework import ops from tensorflow.python.framework import sparse_tensor from tensorflow.python.framework import tensor_util from tensorflow.python.ops import array_ops +from tensorflow.python.ops import gen_parsing_ops from tensorflow.python.ops import gen_string_ops from tensorflow.python.ops import math_ops @@ -311,7 +312,7 @@ def _reduce_join_reduction_dims(x, axis, reduction_indices): return math_ops.range(array_ops.rank(x) - 1, -1, -1) -@tf_export("strings.reduce_join", v1=["strings.reduce_join", "reduce_join"]) +@tf_export(v1=["strings.reduce_join", "reduce_join"]) @deprecation.deprecated_endpoints("reduce_join") def reduce_join(inputs, axis=None, # pylint: disable=missing-docstring keep_dims=False, @@ -329,6 +330,17 @@ def reduce_join(inputs, axis=None, # pylint: disable=missing-docstring name=name) +@tf_export("strings.reduce_join", v1=[]) +def reduce_join_v2( # pylint: disable=missing-docstring + inputs, + axis=None, + keepdims=False, + separator="", + name=None): + return reduce_join( + inputs, axis, keep_dims=keepdims, separator=separator, name=name) + + reduce_join.__doc__ = deprecation.rewrite_argument_docstring( gen_string_ops.reduce_join.__doc__, "reduction_indices", "axis") reduce_join.__doc__ = reduce_join.__doc__.replace("tf.reduce_join(", @@ -337,10 +349,14 @@ reduce_join.__doc__ = reduce_join.__doc__.replace("tf.reduce_join(", # This wrapper provides backwards compatibility for code that predates the # unit argument and that passed 'name' as a positional argument. -@tf_export("strings.length") +@tf_export(v1=["strings.length"]) def string_length(input, name=None, unit="BYTE"): return gen_string_ops.string_length(input, unit=unit, name=name) +@tf_export("strings.length", v1=[]) +def string_length_v2(input, unit="BYTE", name=None): + return string_length(input, name, unit) + string_length.__doc__ = gen_string_ops.string_length.__doc__ @@ -353,11 +369,16 @@ def substr_deprecated(input, pos, len, name=None, unit="BYTE"): substr_deprecated.__doc__ = gen_string_ops.substr.__doc__ -@tf_export("strings.substr") +@tf_export(v1=["strings.substr"]) def substr(input, pos, len, name=None, unit="BYTE"): return gen_string_ops.substr(input, pos, len, unit=unit, name=name) +@tf_export("strings.substr", v1=[]) +def substr_v2(input, pos, len, unit="BYTE", name=None): + return substr(input, pos, len, name=name, unit=unit) + + substr.__doc__ = gen_string_ops.substr.__doc__ @@ -371,3 +392,53 @@ ops.NotDifferentiable("StringSplit") ops.NotDifferentiable("AsString") ops.NotDifferentiable("EncodeBase64") ops.NotDifferentiable("DecodeBase64") + + +@tf_export("strings.to_number", v1=[]) +def string_to_number(input, out_type=dtypes.float32, name=None): + r"""Converts each string in the input Tensor to the specified numeric type. + + (Note that int32 overflow results in an error while float overflow + results in a rounded value.) + + Args: + input: A `Tensor` of type `string`. + out_type: An optional `tf.DType` from: `tf.float32, tf.float64, tf.int32, + tf.int64`. Defaults to `tf.float32`. + The numeric type to interpret each string in `string_tensor` as. + name: A name for the operation (optional). + + Returns: + A `Tensor` of type `out_type`. + """ + return gen_parsing_ops.string_to_number(input, out_type, name) +tf_export(v1=["strings.to_number", "string_to_number"])( + gen_parsing_ops.string_to_number + ) + + +@tf_export("strings.to_hash_bucket", v1=[]) +def string_to_hash_bucket(input, num_buckets, name=None): + # pylint: disable=line-too-long + r"""Converts each string in the input Tensor to its hash mod by a number of buckets. + + The hash function is deterministic on the content of the string within the + process. + + Note that the hash function may change from time to time. + This functionality will be deprecated and it's recommended to use + `tf.string_to_hash_bucket_fast()` or `tf.string_to_hash_bucket_strong()`. + + Args: + input: A `Tensor` of type `string`. + num_buckets: An `int` that is `>= 1`. The number of buckets. + name: A name for the operation (optional). + + Returns: + A `Tensor` of type `int64`. + """ + # pylint: enable=line-too-long + return gen_string_ops.string_to_hash_bucket(input, num_buckets, name) +tf_export(v1=["strings.to_hash_bucket", "string_to_hash_bucket"])( + gen_string_ops.string_to_hash_bucket + ) diff --git a/tensorflow/python/ops/summary_op_util.py b/tensorflow/python/ops/summary_op_util.py index 14aa44a920..c72a9aefc3 100644 --- a/tensorflow/python/ops/summary_op_util.py +++ b/tensorflow/python/ops/summary_op_util.py @@ -22,6 +22,7 @@ import contextlib import re from tensorflow.python.framework import ops +from tensorflow.python.framework import tensor_util from tensorflow.python.platform import tf_logging from tensorflow.python.training import distribution_strategy_context @@ -44,13 +45,27 @@ _INVALID_TAG_CHARACTERS = re.compile(r'[^-/\w\.]') def skip_summary(): - # If using multiple replicas in distributed strategy, skip summaries on all - # replicas except the first one (replica_id=0). + """Determines if summary should be skipped. + + If using multiple replicas in distributed strategy, skip summaries on all + replicas except the first one (replica_id=0). + + Returns: + True if the summary is skipped; False otherwise. + """ + # TODO(priyag): Add a new optional argument that will provide multiple # alternatives to override default behavior. (e.g. run on last replica, # compute sum or mean across replicas). replica_context = distribution_strategy_context.get_replica_context() - return replica_context and replica_context.replica_id > 0 + if not replica_context: + return False + # TODO(b/118385803): when replica_id of _TPUReplicaContext is properly + # initialized, remember to change here as well. + replica_id = replica_context.replica_id_in_sync_group + if isinstance(replica_id, ops.Tensor): + replica_id = tensor_util.constant_value(replica_id) + return replica_id and replica_id > 0 def clean_tag(name): diff --git a/tensorflow/python/ops/summary_ops_v2.py b/tensorflow/python/ops/summary_ops_v2.py index 18cefb8e1c..3f99b9f877 100644 --- a/tensorflow/python/ops/summary_ops_v2.py +++ b/tensorflow/python/ops/summary_ops_v2.py @@ -40,11 +40,14 @@ from tensorflow.python.ops import resource_variable_ops from tensorflow.python.ops import summary_op_util from tensorflow.python.platform import tf_logging as logging from tensorflow.python.training import training_util +from tensorflow.python.util import deprecation from tensorflow.python.util import tf_contextlib +from tensorflow.python.util.tf_export import tf_export -# A global dictionary mapping graph keys to boolean values indicating whether -# we should record summaries for this particular graph or not. +# Dictionary mapping graph keys to a boolean Tensor (or callable returning +# a boolean Tensor) indicating whether we should record summaries for the +# graph identified by the key of the dictionary. _SHOULD_RECORD_SUMMARIES = {} # A global dictionary mapping graph keys to a list of summary writer init ops. @@ -59,58 +62,67 @@ def should_record_summaries(): """Returns boolean Tensor which is true if summaries should be recorded.""" global _SHOULD_RECORD_SUMMARIES key = ops.get_default_graph()._graph_key # pylint: disable=protected-access - return _SHOULD_RECORD_SUMMARIES.setdefault(key, False) + should = _SHOULD_RECORD_SUMMARIES.setdefault(key, False) + return should() if callable(should) else should -# TODO(apassos) consider how to handle local step here. @tf_contextlib.contextmanager -def record_summaries_every_n_global_steps(n, global_step=None): - """Sets the should_record_summaries Tensor to true if global_step % n == 0.""" - if global_step is None: - global_step = training_util.get_or_create_global_step() +def _record_summaries(boolean=True): + """Sets summary recording on or off per the provided boolean value. + + The provided value can be a python boolean, a scalar boolean Tensor, or + or a callable providing such a value; if a callable is passed it will be + invoked each time should_record_summaries() is called to determine whether + summary writing should be enabled. + + Args: + boolean: can be True, False, a bool Tensor, or a callable providing such. + Defaults to True. + + Yields: + Returns a context manager that sets this value on enter and restores the + previous value on exit. + """ + # TODO(nickfelt): make this threadlocal global _SHOULD_RECORD_SUMMARIES key = ops.get_default_graph()._graph_key # pylint: disable=protected-access old = _SHOULD_RECORD_SUMMARIES.setdefault(key, False) try: - with ops.device("cpu:0"): - _SHOULD_RECORD_SUMMARIES[key] = math_ops.equal(global_step % n, 0) + _SHOULD_RECORD_SUMMARIES[key] = boolean yield finally: _SHOULD_RECORD_SUMMARIES[key] = old -@tf_contextlib.contextmanager +# TODO(apassos) consider how to handle local step here. +def record_summaries_every_n_global_steps(n, global_step=None): + """Sets the should_record_summaries Tensor to true if global_step % n == 0.""" + if global_step is None: + global_step = training_util.get_or_create_global_step() + with ops.device("cpu:0"): + should = lambda: math_ops.equal(global_step % n, 0) + if not context.executing_eagerly(): + should = should() + return _record_summaries(should) + + def always_record_summaries(): """Sets the should_record_summaries Tensor to always true.""" - global _SHOULD_RECORD_SUMMARIES - key = ops.get_default_graph()._graph_key # pylint: disable=protected-access - old = _SHOULD_RECORD_SUMMARIES.setdefault(key, False) - try: - _SHOULD_RECORD_SUMMARIES[key] = True - yield - finally: - _SHOULD_RECORD_SUMMARIES[key] = old + return _record_summaries(True) -@tf_contextlib.contextmanager def never_record_summaries(): """Sets the should_record_summaries Tensor to always false.""" - global _SHOULD_RECORD_SUMMARIES - key = ops.get_default_graph()._graph_key # pylint: disable=protected-access - old = _SHOULD_RECORD_SUMMARIES.setdefault(key, False) - try: - _SHOULD_RECORD_SUMMARIES[key] = False - yield - finally: - _SHOULD_RECORD_SUMMARIES[key] = old + return _record_summaries(False) +@tf_export("summary.SummaryWriter", v1=[]) class SummaryWriter(object): """Encapsulates a stateful summary writer resource. See also: - - `tf.contrib.summary.create_file_writer` - - `tf.contrib.summary.create_db_writer` + - `tf.summary.create_file_writer` + - `tf.summary.create_db_writer` """ def __init__(self, resource, init_op_fn): @@ -205,6 +217,7 @@ def initialize( session.run(_graph(x, 0), feed_dict={x: data}) +@tf_export("summary.create_file_writer", v1=[]) def create_file_writer(logdir, max_queue=None, flush_millis=None, @@ -280,7 +293,7 @@ def create_db_writer(db_uri, `tf.Graph`. Returns: - A `tf.contrib.summary.SummaryWriter` instance. + A `tf.summary.SummaryWriter` instance. """ with ops.device("cpu:0"): if experiment_name is None: @@ -329,7 +342,7 @@ def _nothing(): def all_summary_ops(): """Graph-mode only. Returns all summary ops. - Please note this excludes `tf.contrib.summary.graph` ops. + Please note this excludes `tf.summary.graph` ops. Returns: The summary ops. @@ -497,7 +510,7 @@ def graph(param, step=None, name=None): """Writes a TensorFlow graph to the summary interface. The graph summary is, strictly speaking, not a summary. Conditions - like `tf.contrib.summary.never_record_summaries` do not apply. Only + like `tf.summary.should_record_summaries` do not apply. Only a single graph can be associated with a particular run. If multiple graphs are written, then only the last one will be considered by TensorBoard. @@ -541,14 +554,13 @@ def graph(param, step=None, name=None): _graph = graph # for functions with a graph parameter +@tf_export("summary.import_event", v1=[]) def import_event(tensor, name=None): """Writes a `tf.Event` binary proto. - When using create_db_writer(), this can be used alongside - `tf.TFRecordReader` to load event logs into the database. Please - note that this is lower level than the other summary functions and - will ignore any conditions set by methods like - `tf.contrib.summary.should_record_summaries`. + This can be used to import existing event logs into a new summary writer sink. + Please note that this is lower level than the other summary functions and + will ignore the `tf.summary.should_record_summaries` setting. Args: tensor: A `tf.Tensor` of type `string` containing a serialized @@ -562,13 +574,14 @@ def import_event(tensor, name=None): context.context().summary_writer_resource, tensor, name=name) +@tf_export("summary.flush", v1=[]) def flush(writer=None, name=None): """Forces summary writer to send any buffered data to storage. This operation blocks until that finishes. Args: - writer: The `tf.contrib.summary.SummaryWriter` resource to flush. + writer: The `tf.summary.SummaryWriter` resource to flush. The thread default will be used if this parameter is None. Otherwise a `tf.no_op` is returned. name: A name for the operation (optional). @@ -595,6 +608,8 @@ def eval_dir(model_dir, name=None): return os.path.join(model_dir, "eval" if not name else "eval_" + name) +@deprecation.deprecated(date=None, + instructions="Renamed to create_file_writer().") def create_summary_file_writer(*args, **kwargs): """Please use `tf.contrib.summary.create_file_writer`.""" logging.warning("Deprecation Warning: create_summary_file_writer was renamed " diff --git a/tensorflow/python/ops/tensor_array_ops.py b/tensorflow/python/ops/tensor_array_ops.py index f86dfb3527..d151694951 100644 --- a/tensorflow/python/ops/tensor_array_ops.py +++ b/tensorflow/python/ops/tensor_array_ops.py @@ -20,8 +20,10 @@ from __future__ import division from __future__ import print_function import contextlib +import os import weakref +from tensorflow.python import tf2 from tensorflow.python.eager import context from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes @@ -30,12 +32,18 @@ from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape from tensorflow.python.framework import tensor_util from tensorflow.python.ops import array_ops +from tensorflow.python.ops import gen_control_flow_ops from tensorflow.python.ops import gen_data_flow_ops +from tensorflow.python.ops import list_ops from tensorflow.python.ops import math_ops from tensorflow.python.util import tf_should_use from tensorflow.python.util.tf_export import tf_export +ENABLE_TENSOR_ARRAY_V2 = ( + tf2.enabled() or os.getenv("TF_ENABLE_TENSOR_ARRAY_V2") is not None) + + # _GraphTensorArray accesses many of the hidden generated ops, but is in # fact built to wrap these methods. # pylint: disable=protected-access @@ -393,6 +401,273 @@ class _GraphTensorArray(object): return gen_data_flow_ops.tensor_array_close_v3( handle=self._handle, name=name) + +class _GraphTensorArrayV2(object): + """Graph-mode implementation of TensorArray backed by TensorLists. + + The backing tensor of this TensorArray is a TensorList variant tensor which is + stored in the `flow`. The `handle` is always none here. The reason we use the + `flow` field and not the `handle` field is to ensure backwards compatibility + with legacy control flow. + """ + + def __init__(self, + dtype, + size=None, + dynamic_size=None, + clear_after_read=None, + tensor_array_name=None, + handle=None, + flow=None, + infer_shape=True, + element_shape=None, + colocate_with_first_write_call=True, + name=None): + """Constructs a graph mode TensorArray. + + Args: + dtype: (required) data type of the TensorArray. + size: (optional) int32 scalar `Tensor`: the size of the TensorArray. + Required if flow is not provided. + dynamic_size: (optional) Python bool: If true, writes to the TensorArray + can grow the TensorArray past its initial size. Default: False. + clear_after_read: (optional) unused. Not supported in TensorLists. + tensor_array_name: (optional) unused. + handle: (optional) Must always be None. + flow: (optional) A variant `Tensor` scalar for a TensorList. + infer_shape: (optional, default: True) If True, shape inference is + enabled. In this case, all elements must have the same shape. + element_shape: (optional, default: None) A `TensorShape` object specifying + the shape constraints of each of the elements of the TensorArray. Need + not be fully defined. + colocate_with_first_write_call: (optional). unused. + name: (optional) A name for the operation. + + Raises: + ValueError: if both handle and tensor_array_name are provided. + TypeError: if handle is provided but is not a Tensor. + """ + assert handle is None + del handle + del clear_after_read + del tensor_array_name + del colocate_with_first_write_call + + del dynamic_size # TODO(b/117943489): Unused for now. + + if (flow is not None and + (not isinstance(flow, ops.Tensor) or flow.dtype != dtypes.variant)): + raise TypeError("flow must be a variant tensor") + if flow is None and size is None: + raise ValueError("Size must be provided if flow is not provided") + if flow is not None and size is not None: + raise ValueError("Cannot provide both a flow and size " + "at the same time") + if flow is not None and element_shape is not None: + raise ValueError("Cannot provide both a flow and element_shape " + "at the same time") + + self._dtype = dtype + + # Record the current static shape for the array elements. The element + # shape is defined either by `element_shape` or the shape of the tensor + # of the first write. If `infer_shape` is true, all writes checks for + # shape equality. + if element_shape is None: + self._infer_shape = infer_shape + self._element_shape = [] + else: + self._infer_shape = True + self._element_shape = [tensor_shape.TensorShape(element_shape)] + with ops.name_scope(name, "TensorArrayV2", [size, flow]) as scope: + if flow is None: + self._flow = list_ops.tensor_list_reserve( + element_shape=element_shape, + num_elements=size, + element_dtype=dtype, + name=scope) + else: + self._flow = flow + + # For backwards compatibility. + self._colocate_with_first_write_call = None + self._colocate_with = None + + @property + def flow(self): + return self._flow + + @property + def dtype(self): + return self._dtype + + @property + def handle(self): + # We intentionally do not raise an error so that legacy while_loop does not + # complain. + return None + + def _merge_element_shape(self, shape): + """Changes the element shape of the array given a shape to merge with. + + Args: + shape: A `TensorShape` object to merge with. + + Raises: + ValueError: if the provided shape is incompatible with the current + element shape of the `TensorArray`. + """ + + if self._element_shape: + if not shape.is_compatible_with(self._element_shape[0]): + raise ValueError( + "Inconsistent shapes: saw %s but expected %s " + "(and infer_shape=True)" % (shape, self._element_shape[0])) + self._element_shape[0] = self._element_shape[0].merge_with(shape) + else: + self._element_shape.append(shape) + + def identity(self): + """See TensorArray.""" + flow = array_ops.identity(self._flow) + ta = TensorArray( + dtype=self._dtype, flow=flow, infer_shape=self._infer_shape) + ta._element_shape = self._element_shape + return ta + + def grad(self, source, flow=None, name=None): + """Not supported.""" + raise NotImplementedError() + + def read(self, index, name=None): + """See TensorArray.""" + value = list_ops.tensor_list_get_item( + input_handle=self._flow, + index=index, + element_dtype=self._dtype, + name=name) + if self._element_shape: + value.set_shape(self._element_shape[0].dims) + return value + + @tf_should_use.should_use_result + def write(self, index, value, name=None): + """See TensorArray.""" + with ops.name_scope(name, "TensorArrayV2Write", [self._flow, index, value]): + value = ops.convert_to_tensor(value, name="value") + if self._infer_shape: + self._merge_element_shape(value.shape) + flow_out = list_ops.tensor_list_set_item( + input_handle=self._flow, index=index, item=value, name=name) + ta = TensorArray(dtype=self._dtype, handle=None, flow=flow_out) + ta._infer_shape = self._infer_shape + ta._element_shape = self._element_shape + return ta + + def stack(self, name=None): + """See TensorArray.""" + with ops.name_scope(name, "TensorArrayV2Stack", [self._flow]): + value = list_ops.tensor_list_stack( + input_handle=self._flow, element_dtype=self._dtype) + if self._element_shape and self._element_shape[0].dims is not None: + value.set_shape([None] + self._element_shape[0].dims) + return value + + def gather(self, indices, name=None): + """See TensorArray.""" + value = list_ops.tensor_list_gather( + input_handle=self._flow, + indices=indices, + element_dtype=self._dtype, + name=name) + if self._element_shape and self._element_shape[0].dims is not None: + value.set_shape([None] + self._element_shape[0].dims) + return value + + def concat(self, name=None): + """See TensorArray.""" + value = list_ops.tensor_list_concat( + input_handle=self._flow, element_dtype=self._dtype, name=name) + if self._element_shape and self._element_shape[0].dims is not None: + value.set_shape([None] + self._element_shape[0].dims[1:]) + return value + + @tf_should_use.should_use_result + def unstack(self, value, name=None): + """See TensorArray.""" + with ops.name_scope(name, "TensorArrayUnstack", [self._flow, value]): + value = ops.convert_to_tensor(value, name="value") + if self._infer_shape and not context.executing_eagerly(): + self._merge_element_shape(value.shape[1:]) + flow_out = list_ops.tensor_list_from_tensor( + tensor=value, element_shape=value.shape[1:]) + ta = TensorArray( + dtype=self._dtype, + handle=self.handle, + flow=flow_out, + colocate_with_first_write_call=self._colocate_with_first_write_call) + ta._infer_shape = self._infer_shape + ta._element_shape = self._element_shape + ta._colocate_with = self._colocate_with + return ta + + @tf_should_use.should_use_result + def scatter(self, indices, value, name=None): + """See TensorArray.""" + with ops.name_scope(name, "TensorArrayScatter", + [self._flow, value, indices]): + value = ops.convert_to_tensor(value, name="value") + if self._infer_shape and not context.executing_eagerly(): + self._merge_element_shape(value.shape[1:]) + flow_out = list_ops.tensor_list_scatter( + tensor=value, indices=indices, element_shape=-1) + ta = TensorArray( + dtype=self._dtype, + handle=self.handle, + flow=flow_out, + colocate_with_first_write_call=self._colocate_with_first_write_call) + ta._infer_shape = self._infer_shape + ta._element_shape = self._element_shape + ta._colocate_with = self._colocate_with + return ta + + @tf_should_use.should_use_result + def split(self, value, lengths, name=None): + """See TensorArray.""" + with ops.name_scope(name, "TensorArraySplit", [self._flow, value, lengths]): + value = ops.convert_to_tensor(value, name="value") + lengths_64 = math_ops.to_int64(lengths) + if self._infer_shape and not context.executing_eagerly(): + clengths = tensor_util.constant_value(lengths_64) + if value.shape.dims is not None: + if clengths is not None and clengths.max() == clengths.min(): + self._merge_element_shape( + tensor_shape.TensorShape([clengths[0]]).concatenate( + value.shape[1:])) + flow_out = list_ops.tensor_list_split( + tensor=value, + lengths=lengths_64, + element_shape=self._element_shape[0] if self._element_shape else None, + name=name) + ta = TensorArray( + dtype=self._dtype, + handle=self.handle, + flow=flow_out, + colocate_with_first_write_call=self._colocate_with_first_write_call) + ta._infer_shape = self._infer_shape + ta._element_shape = self._element_shape + ta._colocate_with = self._colocate_with + return ta + + def size(self, name=None): + """See TensorArray.""" + return list_ops.tensor_list_length(input_handle=self._flow, name=name) + + @tf_should_use.should_use_result + def close(self, name=None): + """See TensorArray.""" + return gen_control_flow_ops.no_op(name=name) + # pylint: enable=protected-access @@ -738,8 +1013,10 @@ class TensorArray(object): if context.executing_eagerly(): implementation = _EagerTensorArray else: - implementation = _GraphTensorArray - + if ENABLE_TENSOR_ARRAY_V2: + implementation = _GraphTensorArrayV2 + else: + implementation = _GraphTensorArray self._implementation = implementation( dtype, size=size, @@ -768,7 +1045,7 @@ class TensorArray(object): @property def handle(self): """The reference to the TensorArray.""" - return self._implementation._handle + return self._implementation.handle @property def _infer_shape(self): @@ -953,4 +1230,16 @@ class TensorArray(object): """Close the current TensorArray.""" return self._implementation.close(name=name) + +def build_ta_with_new_flow(old_ta, flow): + ta = TensorArray( + dtype=old_ta.dtype, + handle=old_ta.handle, + flow=flow, + infer_shape=old_ta._infer_shape, + colocate_with_first_write_call=old_ta._colocate_with_first_write_call) + ta._colocate_with = old_ta._colocate_with + ta._element_shape = old_ta._element_shape + return ta + # pylint: enable=protected-access diff --git a/tensorflow/python/ops/tensor_forest_ops.py b/tensorflow/python/ops/tensor_forest_ops.py new file mode 100644 index 0000000000..42f3cdf324 --- /dev/null +++ b/tensorflow/python/ops/tensor_forest_ops.py @@ -0,0 +1,110 @@ +# Copyright 2018 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. +# ============================================================================== +"""Ops for tensor_forest.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.ops import resources + +from tensorflow.python import ops +from tensorflow.python.ops import gen_tensor_forest_ops +from tensorflow.python.training import saver + + +class TreeVariableSaveable(saver.BaseSaverBuilder.SaveableObject): + + def __init__(self, type_name, name, container, config, resource_handle_func, + create_op_func, is_initialized_op_func, serialize_op_func, + deserialize_op_func): + + with ops.name_scope(name, type_name) as name: + self._resource_handle = resource_handle_func( + container, shared_name=name, name=name) + + self._is_initialized_op = is_initialized_op_func( + self._resource_handle) + tensor = serialize_op_func(self._resource_handle) + self._create_op = create_op_func( + self._resource_handle, + config) + # slice_spec is useful for saving a slice from a variable. + # It's not meaningful the tree variable. So we just pass an empty + # value. + slice_spec = "" + specs = [saver.BaseSaverBuilder.SaveSpec(tensor, slice_spec, name)] + super(TreeVariableSaveable, + self).__init__(self._resource_handle, specs, name) + + ops.add_to_collection( + ops.GraphKeys.SAVEABLE_OBJECTS, self) + + resources.register_resource( + self._resource_handle, self._create_op, self._is_initialized_op) + self._deserialize_op_func = deserialize_op_func + + def restore(self, restored_tensors, unused_restored_shapes): + """Restores the associated tree from 'restored_tensors'. + + Args: + restored_tensors: the tensors that were loaded from a checkpoint. + unused_restored_shapes: the shapes this object should conform to after + restore. Not meaningful for trees. + + Returns: + The operation that restores the state of the tree variable. + """ + with ops.control_dependencies([self._create_op]): + return self._deserialize_op_func( + self._resource_handle, + restored_tensors[0], + ) + + @property + def resource(self): + return self._resource_handle + + +def tree_variable(tree_config, name, container=None): + return TreeVariableSaveable( + "TreeVariable", + name, + container, + tree_config, + gen_tensor_forest_ops.tensor_forest_tree_resource_handle_op, + gen_tensor_forest_ops.tensor_forest_create_tree_variable, + gen_tensor_forest_ops.tensor_forest_tree_is_initialized_op, + gen_tensor_forest_ops.tensor_forest_tree_serialize, + gen_tensor_forest_ops.tensor_forest_tree_deserialize).resource + + +class ForestVariables(object): + + def __init__(self, params, + tree_configs=None): + + self._variables = [] + + for i in range(params.n_trees): + tree_config = '' + if tree_configs is not None: + tree_config = tree_configs[i] + self._variables.append(tree_variable( + tree_config, + 'tree-%s' % i, + )) + + def __getitem__(self, t): + return self._variables[t] diff --git a/tensorflow/python/ops/variable_scope.py b/tensorflow/python/ops/variable_scope.py index fe93bfb61f..ccce9e2f93 100644 --- a/tensorflow/python/ops/variable_scope.py +++ b/tensorflow/python/ops/variable_scope.py @@ -646,14 +646,8 @@ class _VariableStore(object): when violating reuse during variable creation, or if an existing sharded variable exists for the given name but with different sharding. """ - if context.executing_eagerly(): - raise NotImplementedError("Partitioned variables are not yet supported " - "when eager execution is enabled.") - initializing_from_value = initializer is not None and isinstance( initializer, ops.Tensor) - reuse_without_partition = reuse and not partitioner - if name in self._vars: raise ValueError( "A partitioner was provided, but an unpartitioned version of the " @@ -664,30 +658,9 @@ class _VariableStore(object): if initializing_from_value: shape = shape.merge_with(initializer.get_shape()) - if not reuse_without_partition: - if not shape.is_fully_defined(): - raise ValueError("Shape of a new partitioned variable (%s) must be " - "fully defined, but instead was %s." % (name, shape)) - - if shape.ndims < 1: - raise ValueError("A partitioned Variable must have rank at least 1, " - "shape: %s" % shape) - - partitions = partitioner(shape=shape, dtype=dtype) - - if not isinstance(partitions, collections_lib.Sequence): - raise ValueError("Partitioner must return a sequence, but saw: %s" - % partitions) - - if len(partitions) != shape.ndims: - raise ValueError( - "Partitioner returned a partition list that does not match the " - "Variable's rank: %s vs. %s" % (partitions, shape)) - - if any([p < 1 for p in partitions]): - raise ValueError( - "Partitioner returned zero partitions for some axes: %s" % - partitions) + partitions = None + if not reuse or partitioner: + partitions = _call_partitioner(partitioner, shape, dtype) if name in self._partitioned_vars: if reuse is False: @@ -709,7 +682,7 @@ class _VariableStore(object): % (name, dtype.name, existing_var.dtype.name)) # pylint: disable=protected-access - if (not reuse_without_partition and + if (partitions is not None and existing_var._get_partitions() != partitions): raise ValueError( "Trying to reuse partitioned variable %s, but specified partitions " @@ -724,14 +697,7 @@ class _VariableStore(object): "created with tf.get_variable(). Did you mean to set " "reuse=False or reuse=tf.AUTO_REUSE in VarScope?" % name) - slice_dim, slice_shape = _compute_slice_dim_and_shape( - shape.as_list(), partitions) - - vs = [] - num_slices = partitions[slice_dim] - num_slices_with_excess = shape.dims[slice_dim].value % num_slices - - slice_offset = [0] * shape.ndims + slice_dim, num_slices = _get_slice_dim_and_num_slices(partitions) if "%s/part_0" % name in self._vars: if "%s/part_%d" % (name, num_slices - 1) not in self._vars: @@ -747,15 +713,14 @@ class _VariableStore(object): "%s/part_0 was found, but so was the extra shard %s/part_%d." % (num_slices, name, name, num_slices)) - for i in xrange(num_slices): - var_shape = slice_shape[:] - var_offset = slice_offset[:] + vs = [] + for i, (var_offset, var_shape) in enumerate(_iter_slices( + shape.as_list(), + num_slices, + slice_dim + )): partition_info = _PartitionInfo( full_shape=shape.as_list(), var_offset=var_offset) - if i < num_slices_with_excess: - var_shape[slice_dim] += 1 - slice_offset[slice_dim] += var_shape[slice_dim] - var_full_name = "%s/part_%d" % (name, i) with ops.name_scope(var_full_name + "/PartitionedInitializer"): # Create the tensor to initialize the variable with default value. @@ -803,15 +768,13 @@ class _VariableStore(object): vs.append(var) # pylint: enable=protected-access - # pylint: disable=protected-access partitioned_var = variables.PartitionedVariable(name=name, shape=shape, dtype=dtype, variable_list=vs, partitions=partitions) - # pylint: enable=protected-access - - self._partitioned_vars[name] = partitioned_var + if not context.executing_eagerly() or self._store_eager_variables: + self._partitioned_vars[name] = partitioned_var return partitioned_var def _get_single_variable(self, @@ -913,20 +876,22 @@ class _VariableStore(object): variable_dtype = None else: # Instantiate initializer if provided initializer is a type object. - if isinstance(initializer, type(init_ops.Initializer)): + if tf_inspect.isclass(initializer): initializer = initializer(dtype=dtype) - if shape and shape.is_fully_defined(): + if shape is not None and shape.is_fully_defined(): init_val = lambda: initializer( # pylint: disable=g-long-lambda shape.as_list(), dtype=dtype, partition_info=partition_info) - elif not tf_inspect.getargspec(initializer).args: + variable_dtype = dtype.base_dtype + elif len(tf_inspect.getargspec(initializer).args) == len( + tf_inspect.getargspec(initializer).defaults or []): init_val = initializer + variable_dtype = None else: - raise ValueError("You can only pass an initializer function that " - "expects no arguments to its callable when the " - "shape is not fully defined. The given initializer " - "function expects the following args %s" % - tf_inspect.getargspec(initializer).args) - variable_dtype = dtype.base_dtype + raise ValueError("The initializer passed is not valid. It should " + "be a callable with no arguments and the " + "shape should not be provided or an instance of " + "`tf.keras.initializers.*' and `shape` should be " + "fully defined.") # Create the variable. if use_resource is None: @@ -1080,9 +1045,6 @@ class VariableScope(object): if self._caching_device is not None: raise NotImplementedError("Caching devices is not yet supported " "when eager execution is enabled.") - if self._partitioner is not None: - raise NotImplementedError("Partitioned variables are not yet supported " - "when eager execution is enabled.") self._reuse = AUTO_REUSE self._use_resource = True @@ -1162,9 +1124,6 @@ class VariableScope(object): def set_partitioner(self, partitioner): """Set partitioner for this scope.""" - if partitioner and context.executing_eagerly(): - raise NotImplementedError("Partitioned variables are not yet supported " - "when eager execution is enabled.") self._partitioner = partitioner def set_custom_getter(self, custom_getter): @@ -1277,9 +1236,6 @@ class VariableScope(object): synchronization=VariableSynchronization.AUTO, aggregation=VariableAggregation.NONE): """Gets an existing variable with this name or create a new one.""" - if context.executing_eagerly(): - raise NotImplementedError("Partitioned variables are not yet supported " - "when eager execution is enabled.") if initializer is None: initializer = self._initializer if regularizer is None: @@ -2246,8 +2202,8 @@ class variable_scope(object): try: return self._enter_scope_uncached() - except: - if not self._building_function: + except Exception: + if self._in_graph_mode and not self._building_function: if self._graph_context_manager is not None: self._graph_context_manager.__exit__(*sys.exc_info()) raise @@ -2413,34 +2369,71 @@ def variable_op_scope(values, yield scope -def _compute_slice_dim_and_shape(full_shape, slicing): - """Computes which dimension is being sliced and the typical slice shape.""" - - slice_shape = [0] * len(full_shape) - slice_dim = None - for dim, num_slices in enumerate(slicing): - dim_size = full_shape[dim] - if num_slices <= 0 or dim_size < num_slices: - raise ValueError("Cannot create %d slices for size %d. shape: %s, " - "slicing: %s" % - (num_slices, full_shape[dim], full_shape, slicing)) - if num_slices == 1: - # Not slicing in this dimension. - slice_shape[dim] = dim_size - elif slice_dim is not None: - # We only support slicing along one of the dimensions. - raise ValueError("Can only slice a variable along one dimension: " - "shape: %s, slicing: %s" % (full_shape, slicing)) - else: - # Note: We will add any extras onto the last slice, later. - slice_dim = dim - slice_shape[dim] = dim_size // num_slices +def _call_partitioner(partitioner, shape, dtype): + """Call partitioner validating its inputs/output. - # Degenerate case: If "slicing" was all ones, pretend we are slicing along - # the first dimension. - if slice_dim is None: + Args: + partitioner: a function mapping `Tensor` shape and dtype to a + list of partitions. + shape: shape of the `Tensor` to partition, must have at least two + dimensions. + dtype: dtype of the elements in the `Tensor`. + + Returns: + A list with elements >=1 and exactly one >1. The index of that + element corresponds to the partitioning axis. + """ + if not shape.is_fully_defined(): + raise ValueError("Shape of a new partitioned variable must be " + "fully defined, but instead was %s." % (shape,)) + if shape.ndims < 1: + raise ValueError("A partitioned Variable must have rank at least 1, " + "shape: %s" % shape) + + slicing = partitioner(shape=shape, dtype=dtype) + if not isinstance(slicing, collections_lib.Sequence): + raise ValueError("Partitioner must return a sequence, but saw: %s" + % slicing) + if len(slicing) != shape.ndims: + raise ValueError( + "Partitioner returned a partition list that does not match the " + "Variable's rank: %s vs. %s" % (slicing, shape)) + if any(p < 1 for p in slicing): + raise ValueError( + "Partitioner returned zero partitions for some axes: %s" % + slicing) + if sum(p > 1 for p in slicing) > 1: + raise ValueError( + "Can only slice a variable along one dimension: " + "shape: %s, partitioning: %s" % (shape, slicing)) + return slicing + + +# TODO(slebedev): could be inlined, but +# `_VariableStore._get_partitioned_variable` is too complex even +# without this logic. +def _get_slice_dim_and_num_slices(slicing): + """Get slicing dimension and number of slices from the partitioner output.""" + for slice_dim, num_slices in enumerate(slicing): + if num_slices > 1: + break + else: + # Degenerate case: no partitioning applied. slice_dim = 0 - return slice_dim, slice_shape + num_slices = 1 + return slice_dim, num_slices + + +def _iter_slices(full_shape, num_slices, slice_dim): + """Slices a given a shape along the specified dimension.""" + num_slices_with_excess = full_shape[slice_dim] % num_slices + offset = [0] * len(full_shape) + min_slice_len = full_shape[slice_dim] // num_slices + for i in xrange(num_slices): + shape = full_shape[:] + shape[slice_dim] = min_slice_len + bool(i < num_slices_with_excess) + yield offset[:], shape + offset[slice_dim] += shape[slice_dim] def _get_trainable_value(synchronization, trainable): diff --git a/tensorflow/python/ops/variables.py b/tensorflow/python/ops/variables.py index e43736069e..d809c81b98 100644 --- a/tensorflow/python/ops/variables.py +++ b/tensorflow/python/ops/variables.py @@ -18,7 +18,8 @@ from __future__ import division from __future__ import print_function import enum # pylint: disable=g-bad-import-order - +import functools +import os import six from tensorflow.core.framework import attr_value_pb2 @@ -860,18 +861,18 @@ class Variable(six.with_metaclass(VariableMetaclass, else: return v.value() - @staticmethod - def _OverloadAllOperators(): # pylint: disable=invalid-name + @classmethod + def _OverloadAllOperators(cls): # pylint: disable=invalid-name """Register overloads for all operators.""" for operator in ops.Tensor.OVERLOADABLE_OPERATORS: - Variable._OverloadOperator(operator) + cls._OverloadOperator(operator) # For slicing, bind getitem differently than a tensor (use SliceHelperVar # instead) # pylint: disable=protected-access - setattr(Variable, "__getitem__", array_ops._SliceHelperVar) + setattr(cls, "__getitem__", array_ops._SliceHelperVar) - @staticmethod - def _OverloadOperator(operator): # pylint: disable=invalid-name + @classmethod + def _OverloadOperator(cls, operator): # pylint: disable=invalid-name """Defer an operator overload to `ops.Tensor`. We pull the operator out of ops.Tensor dynamically to avoid ordering issues. @@ -879,17 +880,26 @@ class Variable(six.with_metaclass(VariableMetaclass, Args: operator: string. The operator name. """ + tensor_oper = getattr(ops.Tensor, operator) - def _run_op(a, *args): + def _run_op(a, *args, **kwargs): # pylint: disable=protected-access - return getattr(ops.Tensor, operator)(a._AsTensor(), *args) - # Propagate __doc__ to wrapper - try: - _run_op.__doc__ = getattr(ops.Tensor, operator).__doc__ - except AttributeError: - pass + return tensor_oper(a._AsTensor(), *args, **kwargs) - setattr(Variable, operator, _run_op) + functools.update_wrapper(_run_op, tensor_oper) + setattr(cls, operator, _run_op) + + def __iter__(self): + """Dummy method to prevent iteration. Do not call. + + NOTE(mrry): If we register __getitem__ as an overloaded operator, + Python will valiantly attempt to iterate over the variable's Tensor from 0 + to infinity. Declaring this method prevents this unintended behavior. + + Raises: + TypeError: when invoked. + """ + raise TypeError("'Variable' object is not iterable.") # NOTE(mrry): This enables the Variable's overloaded "right" binary # operators to run when the left operand is an ndarray, because it @@ -1045,27 +1055,6 @@ class Variable(six.with_metaclass(VariableMetaclass, else: return None - def __iadd__(self, other): - raise NotImplementedError - - def __isub__(self, other): - raise NotImplementedError - - def __imul__(self, other): - raise NotImplementedError - - def __idiv__(self, other): - raise NotImplementedError - - def __itruediv__(self, other): - raise NotImplementedError - - def __irealdiv__(self, other): - raise NotImplementedError - - def __ipow__(self, other): - raise NotImplementedError - @tf_export(v1=["Variable"]) class VariableV1(Variable): @@ -1576,18 +1565,6 @@ class RefVariable(VariableV1): """ return self._snapshot - def __iter__(self): - """Dummy method to prevent iteration. Do not call. - - NOTE(mrry): If we register __getitem__ as an overloaded operator, - Python will valiantly attempt to iterate over the variable's Tensor from 0 - to infinity. Declaring this method prevents this unintended behavior. - - Raises: - TypeError: when invoked. - """ - raise TypeError("'Variable' object is not iterable.") - def value(self): """Returns the last snapshot of this variable. @@ -2123,37 +2100,6 @@ class RefVariable(VariableV1): else: return v.value() - @staticmethod - def _OverloadAllOperators(): # pylint: disable=invalid-name - """Register overloads for all operators.""" - for operator in ops.Tensor.OVERLOADABLE_OPERATORS: - Variable._OverloadOperator(operator) # pylint: disable=protected-access - # For slicing, bind getitem differently than a tensor (use SliceHelperVar - # instead) - # pylint: disable=protected-access - setattr(Variable, "__getitem__", array_ops._SliceHelperVar) - - @staticmethod - def _OverloadOperator(operator): # pylint: disable=invalid-name - """Defer an operator overload to `ops.Tensor`. - - We pull the operator out of ops.Tensor dynamically to avoid ordering issues. - - Args: - operator: string. The operator name. - """ - - def _run_op(a, *args): - # pylint: disable=protected-access - return getattr(ops.Tensor, operator)(a._AsTensor(), *args) - # Propagate __doc__ to wrapper - try: - _run_op.__doc__ = getattr(ops.Tensor, operator).__doc__ - except AttributeError: - pass - - setattr(Variable, operator, _run_op) - def _gather_saveables_for_checkpoint(self): """For implementing `Checkpointable`. This object is saveable on its own.""" return {checkpointable.VARIABLE_VALUE_KEY: self} @@ -2457,34 +2403,6 @@ class PartitionedVariable(object): @end_compatibility """ - class PartitionedVariableIterator(object): - """An iterator that allows accessing the underlying `Variable` objects. - - This iterator is necessary to control order of access when Variables - are not partitioned in a standard way along a single axis. - - Allows e.g. `list(partitioned_variable)` to return a proper list. - """ - - def __init__(self, partitioned_variable): - self._ix = 0 - self._partitioned_variable = partitioned_variable - - def __iter__(self): - return self - - def __next__(self): # For python3 compatibility. - return self.next() - - def next(self): - # pylint: disable=protected-access - if self._ix >= len(self._partitioned_variable._variable_list): - raise StopIteration() - variable = self._partitioned_variable._variable_list[self._ix] - # pylint: enable=protected-access - self._ix += 1 - return variable - def __init__(self, name, shape, dtype, variable_list, partitions): """Creates a new partitioned variable wrapper. @@ -2504,31 +2422,27 @@ class PartitionedVariable(object): `partitions` is not a list. ValueError: If `variable_list` is empty, or the `Variable` shape information does not match `shape`, or `partitions` has invalid values. - RuntimeError: If eager execution is enabled """ - if context.executing_eagerly(): - raise RuntimeError( - "tf.PartitionedVariable not supported with eager execution enabled.") if not isinstance(variable_list, (list, tuple)): raise TypeError( "variable_list is not a list or tuple: %s" % variable_list) if not isinstance(partitions, (list, tuple)): raise TypeError("partitions is not a list or tuple: %s" % partitions) - if not all([p >= 1 for p in partitions]): + if not all(p >= 1 for p in partitions): raise ValueError("partition values must be positive: %s" % partitions) if not variable_list: raise ValueError("variable_list may not be empty") # pylint: disable=protected-access for v in variable_list: # Sort the variable_list lexicographically according to var offset value. - if not all([v._get_save_slice_info() is not None for v in variable_list]): + if not all(v._get_save_slice_info() is not None for v in variable_list): raise ValueError( "All variables must have a save_slice_info available: %s" % [v.name for v in variable_list]) if len(shape) != len(partitions): raise ValueError("len(shape) != len(partitions): %s vs. %s" % (shape, partitions)) - if not all([v._get_save_slice_info().full_shape == shape]): + if v._get_save_slice_info().full_shape != shape: raise ValueError( "All variables' full shapes must match shape: %s; " "but full shapes were: %s" @@ -2545,7 +2459,7 @@ class PartitionedVariable(object): def __iter__(self): """Return an iterable for accessing the underlying partition Variables.""" - return self.PartitionedVariableIterator(self) + return iter(self._variable_list) def __len__(self): num_partition_axes = len(self._partition_axes()) @@ -2555,7 +2469,7 @@ class PartitionedVariable(object): return len(self._variable_list) def _partition_axes(self): - if all([p == 1 for p in self._partitions]): + if all(p == 1 for p in self._partitions): return [0] else: return [i for i, p in enumerate(self._partitions) if p > 1] @@ -2995,7 +2909,8 @@ def report_uninitialized_variables(var_list=None, # Run all operations on CPU if var_list: init_vars = [state_ops.is_variable_initialized(v) for v in var_list] - with ops.device("/cpu:0"): + local_device = os.environ.get("TF_DEVICE_FOR_UNINITIALIZED_VARIABLE_REPORTING", "/cpu:0") + with ops.device(local_device): if not var_list: # Return an empty tensor so we only need to check for returned tensor # size being 0 as an indication of model ready. diff --git a/tensorflow/python/ops/while_v2.py b/tensorflow/python/ops/while_v2.py index 7b0f0ed4fc..59ca29e3ba 100644 --- a/tensorflow/python/ops/while_v2.py +++ b/tensorflow/python/ops/while_v2.py @@ -23,7 +23,6 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -from tensorflow.core.framework import attr_value_pb2 from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import func_graph as func_graph_module @@ -65,7 +64,8 @@ def while_loop(cond, loop_vars, shape_invariants=None, maximum_iterations=None, - name=None): + name=None, + return_same_structure=True): """Like tf.while_loop, except emits a single While op.""" maximum_iterations = _validate_and_convert_to_tensor(maximum_iterations) # Keep the original loop_vars around to know which args were TensorArrays. @@ -100,7 +100,8 @@ def while_loop(cond, # Add loop counter needed for computing gradients. loop_vars = [loop_counter] + loop_vars - shape_invariants = [tensor_shape.scalar()] + shape_invariants + shape_invariants = type(shape_invariants)([tensor_shape.scalar() + ]) + shape_invariants # Automatic control dependencies are added in defuns, but not in v1 # graphs. Propagate that behavior here. @@ -133,9 +134,8 @@ def while_loop(cond, # the value of that tensor in each iteration is the same as it was at the # beginning of the loop execution. loop_vars = loop_vars + cond_graph.external_captures - shape_invariants = shape_invariants + [ - t.shape for t in cond_graph.external_captures - ] + shape_invariants = shape_invariants + type(shape_invariants)( + [t.shape for t in cond_graph.external_captures]) def wrapped_body(loop_counter, *args): """Loop body augmented with counter update. @@ -208,8 +208,7 @@ def while_loop(cond, for intermediate_tensor in intermediate_tensors: tensor_list = list_ops.empty_tensor_list( element_dtype=intermediate_tensor.dtype, - element_shape=_get_tensor_convertible_shape( - intermediate_tensor.shape), + element_shape=intermediate_tensor.shape, max_num_elements=maximum_iterations) loop_vars.append(tensor_list) with cond_graph.as_default(): @@ -245,7 +244,7 @@ def while_loop(cond, name=scope) _copy_handle_data(body_graph.outputs, outputs) - _maybe_set_lowering_attr(outputs[0].op) + util.maybe_set_lowering_attr(outputs[0].op) _maybe_set_maximum_iterations_attr(outputs[0].op, maximum_iterations) # Return identities for each output of the While op, rather than the output @@ -257,11 +256,17 @@ def while_loop(cond, outputs = tuple(array_ops.identity(t) for t in outputs) # First var is loop counter. - if num_flattened_outputs == 1: - return outputs[1] + outputs = _pack_sequence_as(orig_loop_vars, + outputs[1:1 + num_flattened_outputs]) + + if return_same_structure: + return outputs + + flattened_outputs = nest.flatten(outputs) + if len(flattened_outputs) == 1: + return flattened_outputs[0] else: - return _pack_sequence_as(orig_loop_vars, - outputs[1:1 + num_flattened_outputs]) + return outputs @ops.RegisterGradient("While") @@ -313,7 +318,7 @@ def _WhileGrad(op, *grads): # pylint: disable=invalid-name for intermediate_tensor in intermediate_tensors: tensor_list = list_ops.empty_tensor_list( element_dtype=intermediate_tensor.dtype, - element_shape=_get_tensor_convertible_shape(intermediate_tensor.shape), + element_shape=intermediate_tensor.shape, max_num_elements=maximum_iterations) with body_grad_graph.as_default(): @@ -343,9 +348,12 @@ def _WhileGrad(op, *grads): # pylint: disable=invalid-name name="%s_grad" % op.name) _copy_handle_data(body_grad_graph.outputs, outputs) - _maybe_set_lowering_attr(outputs[0].op) + util.maybe_set_lowering_attr(outputs[0].op) _maybe_set_maximum_iterations_attr(outputs[0].op, maximum_iterations) + # See comment in while_loop. + outputs = [array_ops.identity(t) for t in outputs] + # Set None as the output gradient for tensors with None input gradient # e.g. TensorArray handles. # outputs[0] is the loop counter. @@ -505,7 +513,7 @@ def _grad_fn(ys, xs, args, func_graph): # TODO(b/118712257): Handle the case when grad_outs has None's e.g. when there # is a tf.StopGradient in the loop body. - assert all([g is not None for g in grad_outs]) + assert all(g is not None for g in grad_outs) counter = args[0] total_iters = args[1] return [counter + 1, total_iters] + grad_outs @@ -792,29 +800,6 @@ def _copy_handle_data(src_tensors, tgt_tensors): custom_gradient.copy_handle_data(src_t, tgt_t) -# TODO(srbs): Move to common utils for cond_v2 and while_v2. -def _maybe_set_lowering_attr(op): - """Sets the flag to enable lowering on the `While` op if necessary. - - Lowering allows while_v2 to avoid some of the limitations of Functions, - allowing users to specify devices & colocation inside of while_v2 - branches, and enabling non-strict evaluation & partial pruning of while_v2 - branches. This brings while_v2 closer to feature parity with - tf.while_loop. - - However, we do not lower `While` in the XLA context because it is easier - for XLA to apply its own optimizations when dealing with un-lowered - `While` operators than with low-level control flow primitives. - - Args: - op: The While op. - """ - if not control_flow_util.IsInXLAContext(op): - # pylint: disable=protected-access - op._set_attr("_lower_using_switch_merge", attr_value_pb2.AttrValue(b=True)) - # pylint: enable=protected-access - - def _maybe_set_maximum_iterations_attr(op, maximum_iterations): if control_flow_util.IsInXLAContext(op): # Store the maximum_iterations to use in the gradient pass. @@ -837,18 +822,6 @@ def _is_in_xla_context(): return control_flow_util.GetContainingXLAContext(cur_ctxt) is not None -def _get_tensor_convertible_shape(shape): - assert isinstance(shape, tensor_shape.TensorShape) - if shape.is_fully_defined(): - return shape - if not shape: # Unknown shape. - return -1 - # Partially defined shape. - shape_list = shape.as_list() - shape_list = [s if s is not None else -1 for s in shape_list] - return ops.convert_to_tensor(shape_list) - - def _graph_name(graph): if isinstance(graph, func_graph_module.FuncGraph): return graph.name @@ -870,6 +843,10 @@ def _is_tensor_array_handle(tensor): # TODO(b/118452219): add test coverage for this. tensor = func_graph_module.maybe_captured(tensor) + if isinstance(tensor, ops.EagerTensor): + # Eager execution doesn't quite support legacy tensorarray + return False + return tensor.op.type in TENSOR_ARRAY_HANDLE_OPS diff --git a/tensorflow/python/platform/__init__.py b/tensorflow/python/platform/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tensorflow/python/platform/app.py b/tensorflow/python/platform/app.py index 4c91bc3652..7b917235c0 100644 --- a/tensorflow/python/platform/app.py +++ b/tensorflow/python/platform/app.py @@ -108,7 +108,7 @@ def _define_help_flags(): _define_help_flags_called = True -@tf_export('app.run') +@tf_export(v1=['app.run']) def run(main=None, argv=None): """Runs the program with an optional 'main' function and 'argv' list.""" diff --git a/tensorflow/python/platform/benchmark.py b/tensorflow/python/platform/benchmark.py index 4f7abb311a..d6773d7b81 100644 --- a/tensorflow/python/platform/benchmark.py +++ b/tensorflow/python/platform/benchmark.py @@ -30,6 +30,7 @@ from tensorflow.core.protobuf import config_pb2 from tensorflow.core.protobuf import rewriter_config_pb2 from tensorflow.core.util import test_log_pb2 from tensorflow.python.client import timeline +from tensorflow.python.framework import ops from tensorflow.python.platform import app from tensorflow.python.platform import gfile from tensorflow.python.platform import tf_logging as logging @@ -299,6 +300,18 @@ class TensorFlowBenchmark(Benchmark): benchmark_values["extras"].update(unreported_extras) return benchmark_values + def evaluate(self, tensors): + """Evaluates tensors and returns numpy values. + + Args: + tensors: A Tensor or a nested list/tuple of Tensors. + + Returns: + tensors numpy values. + """ + sess = ops.get_default_session() or self.cached_session() + return sess.run(tensors) + def _run_benchmarks(regex): """Run benchmarks that match regex `regex`. diff --git a/tensorflow/python/platform/gfile.py b/tensorflow/python/platform/gfile.py index 5927bc2409..d0159e9e98 100644 --- a/tensorflow/python/platform/gfile.py +++ b/tensorflow/python/platform/gfile.py @@ -37,7 +37,7 @@ from tensorflow.python.util.deprecation import deprecated from tensorflow.python.util.tf_export import tf_export -@tf_export('gfile.GFile', 'gfile.Open') +@tf_export(v1=['gfile.GFile', 'gfile.Open'], v2=['io.gfile.GFile']) class GFile(_FileIO): """File I/O wrappers without thread locking. @@ -52,7 +52,7 @@ class GFile(_FileIO): super(GFile, self).__init__(name=name, mode=mode) -@tf_export('gfile.FastGFile') +@tf_export(v1=['gfile.FastGFile']) class FastGFile(_FileIO): """File I/O wrappers without thread locking. diff --git a/tensorflow/python/platform/googletest.py b/tensorflow/python/platform/googletest.py index 8141cf92c5..4d34c508da 100644 --- a/tensorflow/python/platform/googletest.py +++ b/tensorflow/python/platform/googletest.py @@ -104,10 +104,13 @@ def GetTempDir(): """Return a temporary directory for tests to use.""" global _googletest_temp_dir if not _googletest_temp_dir: - first_frame = tf_inspect.stack()[-1][0] - temp_dir = os.path.join(tempfile.gettempdir(), - os.path.basename(tf_inspect.getfile(first_frame))) - temp_dir = tempfile.mkdtemp(prefix=temp_dir.rstrip('.py')) + if os.environ.get('TEST_TMPDIR'): + temp_dir = tempfile.mkdtemp(prefix=os.environ['TEST_TMPDIR']) + else: + first_frame = tf_inspect.stack()[-1][0] + temp_dir = os.path.join(tempfile.gettempdir(), + os.path.basename(tf_inspect.getfile(first_frame))) + temp_dir = tempfile.mkdtemp(prefix=temp_dir.rstrip('.py')) def delete_temp_dir(dirname=temp_dir): try: diff --git a/tensorflow/python/platform/test.py b/tensorflow/python/platform/test.py index d084870b25..943832af7a 100644 --- a/tensorflow/python/platform/test.py +++ b/tensorflow/python/platform/test.py @@ -46,9 +46,9 @@ from tensorflow.python.util.tf_export import tf_export if sys.version_info.major == 2: import mock # pylint: disable=g-import-not-at-top,unused-import else: - from unittest import mock # pylint: disable=g-import-not-at-top + from unittest import mock # pylint: disable=g-import-not-at-top,g-importing-member -tf_export('test.mock')(mock) +tf_export(v1=['test.mock'])(mock) # Import Benchmark class Benchmark = _googletest.Benchmark # pylint: disable=invalid-name diff --git a/tensorflow/python/platform/tf_logging.py b/tensorflow/python/platform/tf_logging.py index 59e60856ae..813bcb89be 100644 --- a/tensorflow/python/platform/tf_logging.py +++ b/tensorflow/python/platform/tf_logging.py @@ -37,7 +37,7 @@ import six from tensorflow.python.util.tf_export import tf_export -# Don't use this directly. Use _get_logger() instead. +# Don't use this directly. Use get_logger() instead. _logger = None _logger_lock = threading.Lock() @@ -78,7 +78,8 @@ else: return '(unknown file)', 0, '(unknown function)' -def _get_logger(): +@tf_export('get_logger') +def get_logger(): """Return TF logger instance.""" global _logger @@ -130,39 +131,39 @@ def _get_logger(): _logger_lock.release() -@tf_export('logging.log') +@tf_export(v1=['logging.log']) def log(level, msg, *args, **kwargs): - _get_logger().log(level, msg, *args, **kwargs) + get_logger().log(level, msg, *args, **kwargs) -@tf_export('logging.debug') +@tf_export(v1=['logging.debug']) def debug(msg, *args, **kwargs): - _get_logger().debug(msg, *args, **kwargs) + get_logger().debug(msg, *args, **kwargs) -@tf_export('logging.error') +@tf_export(v1=['logging.error']) def error(msg, *args, **kwargs): - _get_logger().error(msg, *args, **kwargs) + get_logger().error(msg, *args, **kwargs) -@tf_export('logging.fatal') +@tf_export(v1=['logging.fatal']) def fatal(msg, *args, **kwargs): - _get_logger().fatal(msg, *args, **kwargs) + get_logger().fatal(msg, *args, **kwargs) -@tf_export('logging.info') +@tf_export(v1=['logging.info']) def info(msg, *args, **kwargs): - _get_logger().info(msg, *args, **kwargs) + get_logger().info(msg, *args, **kwargs) -@tf_export('logging.warn') +@tf_export(v1=['logging.warn']) def warn(msg, *args, **kwargs): - _get_logger().warn(msg, *args, **kwargs) + get_logger().warn(msg, *args, **kwargs) -@tf_export('logging.warning') +@tf_export(v1=['logging.warning']) def warning(msg, *args, **kwargs): - _get_logger().warning(msg, *args, **kwargs) + get_logger().warning(msg, *args, **kwargs) _level_names = { @@ -183,20 +184,20 @@ _log_prefix = None # later set to google2_log_prefix _log_counter_per_token = {} -@tf_export('logging.TaskLevelStatusMessage') +@tf_export(v1=['logging.TaskLevelStatusMessage']) def TaskLevelStatusMessage(msg): error(msg) -@tf_export('logging.flush') +@tf_export(v1=['logging.flush']) def flush(): raise NotImplementedError() # Code below is taken from pyglib/logging -@tf_export('logging.vlog') +@tf_export(v1=['logging.vlog']) def vlog(level, msg, *args, **kwargs): - _get_logger().log(level, msg, *args, **kwargs) + get_logger().log(level, msg, *args, **kwargs) def _GetNextLogCountPerToken(token): @@ -214,7 +215,7 @@ def _GetNextLogCountPerToken(token): return _log_counter_per_token[token] -@tf_export('logging.log_every_n') +@tf_export(v1=['logging.log_every_n']) def log_every_n(level, msg, n, *args): """Log 'msg % args' at level 'level' once per 'n' times. @@ -231,7 +232,7 @@ def log_every_n(level, msg, n, *args): log_if(level, msg, not (count % n), *args) -@tf_export('logging.log_first_n') +@tf_export(v1=['logging.log_first_n']) def log_first_n(level, msg, n, *args): # pylint: disable=g-bad-name """Log 'msg % args' at level 'level' only first 'n' times. @@ -247,7 +248,7 @@ def log_first_n(level, msg, n, *args): # pylint: disable=g-bad-name log_if(level, msg, count < n, *args) -@tf_export('logging.log_if') +@tf_export(v1=['logging.log_if']) def log_if(level, msg, condition, *args): """Log 'msg % args' at level 'level' only if condition is fulfilled.""" if condition: @@ -296,16 +297,16 @@ def google2_log_prefix(level, timestamp=None, file_and_line=None): return s -@tf_export('logging.get_verbosity') +@tf_export(v1=['logging.get_verbosity']) def get_verbosity(): """Return how much logging output will be produced.""" - return _get_logger().getEffectiveLevel() + return get_logger().getEffectiveLevel() -@tf_export('logging.set_verbosity') +@tf_export(v1=['logging.set_verbosity']) def set_verbosity(v): """Sets the threshold for what messages will be logged.""" - _get_logger().setLevel(v) + get_logger().setLevel(v) def _get_thread_id(): @@ -318,8 +319,8 @@ def _get_thread_id(): _log_prefix = google2_log_prefix -tf_export('logging.DEBUG').export_constant(__name__, 'DEBUG') -tf_export('logging.ERROR').export_constant(__name__, 'ERROR') -tf_export('logging.FATAL').export_constant(__name__, 'FATAL') -tf_export('logging.INFO').export_constant(__name__, 'INFO') -tf_export('logging.WARN').export_constant(__name__, 'WARN') +tf_export(v1=['logging.DEBUG']).export_constant(__name__, 'DEBUG') +tf_export(v1=['logging.ERROR']).export_constant(__name__, 'ERROR') +tf_export(v1=['logging.FATAL']).export_constant(__name__, 'FATAL') +tf_export(v1=['logging.INFO']).export_constant(__name__, 'INFO') +tf_export(v1=['logging.WARN']).export_constant(__name__, 'WARN') diff --git a/tensorflow/python/profiler/internal/run_metadata_test.py b/tensorflow/python/profiler/internal/run_metadata_test.py index 216cc3dd54..a8859f845b 100644 --- a/tensorflow/python/profiler/internal/run_metadata_test.py +++ b/tensorflow/python/profiler/internal/run_metadata_test.py @@ -26,6 +26,7 @@ from tensorflow.core.protobuf import config_pb2 from tensorflow.core.protobuf import rewriter_config_pb2 from tensorflow.python.client import session from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import math_ops from tensorflow.python.ops import random_ops from tensorflow.python.ops import variables @@ -154,6 +155,7 @@ class RunMetadataTest(test.TestCase): # deallocates the memory after matmul started. self.assertGreater(random_allocs[1].alloc_micros, mm.all_start_micros) + @test_util.run_deprecated_v1 def testCPU(self): ops.reset_default_graph() with ops.device('/cpu:0'): @@ -167,6 +169,7 @@ class RunMetadataTest(test.TestCase): ret = _extract_node(run_meta, 'MatMul:MatMul') self.assertEqual(len(ret), 0) + @test_util.run_deprecated_v1 def testLoopCPU(self): ops.reset_default_graph() with ops.device('/cpu:0'): diff --git a/tensorflow/python/profiler/model_analyzer.py b/tensorflow/python/profiler/model_analyzer.py index 5f19eac043..4b2d9052b7 100644 --- a/tensorflow/python/profiler/model_analyzer.py +++ b/tensorflow/python/profiler/model_analyzer.py @@ -122,7 +122,7 @@ def _build_advisor_options(options): return opts -@tf_export('profiler.Profiler') +@tf_export(v1=['profiler.Profiler']) class Profiler(object): """TensorFlow multi-step profiler. @@ -306,7 +306,7 @@ class Profiler(object): print_mdl.WriteProfile(filename) -@tf_export('profiler.profile') +@tf_export(v1=['profiler.profile']) def profile(graph=None, run_meta=None, op_log=None, @@ -381,7 +381,7 @@ def profile(graph=None, return tfprof_node -@tf_export('profiler.advise') +@tf_export(v1=['profiler.advise']) def advise(graph=None, run_meta=None, options=_DEFAULT_ADVISE_OPTIONS): """Auto profile and advise. diff --git a/tensorflow/python/profiler/model_analyzer_test.py b/tensorflow/python/profiler/model_analyzer_test.py index 94c685274a..8648f0b514 100644 --- a/tensorflow/python/profiler/model_analyzer_test.py +++ b/tensorflow/python/profiler/model_analyzer_test.py @@ -93,10 +93,10 @@ class PrintModelAnalysisTest(test.TestCase): config=self._no_rewrite_session_config()) as sess, ops.device(dev): x = lib.BuildSmallModel() - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) pctx.trace_next_step() pctx.dump_next_step() - _ = sess.run(x) + _ = self.evaluate(x) pctx.profiler.profile_name_scope(options=opts) @@ -160,7 +160,7 @@ class PrintModelAnalysisTest(test.TestCase): ) as sess, ops.device('/device:CPU:0'): x = lib.BuildSmallModel() - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) run_meta = config_pb2.RunMetadata() _ = sess.run(x, options=config_pb2.RunOptions( @@ -186,7 +186,7 @@ class PrintModelAnalysisTest(test.TestCase): with session.Session(config=self._no_rewrite_session_config()) as sess: x = lib.BuildSmallModel() - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) run_meta = config_pb2.RunMetadata() _ = sess.run(x, options=config_pb2.RunOptions( @@ -220,9 +220,9 @@ class PrintModelAnalysisTest(test.TestCase): with session.Session(config=self._no_rewrite_session_config()) as sess: x = lib.BuildFullModel() - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) pctx.trace_next_step() - _ = sess.run(x) + _ = self.evaluate(x) tfprof_node = pctx.profiler.profile_python(options=opts) # pylint: disable=line-too-long @@ -281,7 +281,7 @@ class PrintModelAnalysisTest(test.TestCase): with session.Session(config=self._no_rewrite_session_config()) as sess: x = lib.BuildSmallModel() - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) run_meta = config_pb2.RunMetadata() _ = sess.run(x, options=config_pb2.RunOptions( @@ -309,7 +309,7 @@ class PrintModelAnalysisTest(test.TestCase): with session.Session(config=self._no_rewrite_session_config()) as sess: x = lib.BuildFullModel() - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) run_meta = config_pb2.RunMetadata() _ = sess.run( x, @@ -345,7 +345,7 @@ class PrintModelAnalysisTest(test.TestCase): with session.Session(config=self._no_rewrite_session_config()) as sess: x = lib.BuildFullModel() - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) run_meta = config_pb2.RunMetadata() _ = sess.run(x, options=config_pb2.RunOptions( @@ -391,7 +391,7 @@ class PrintModelAnalysisTest(test.TestCase): with session.Session(config=self._no_rewrite_session_config()) as sess: x = lib.BuildFullModel() - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) run_meta = config_pb2.RunMetadata() _ = sess.run( x, @@ -424,7 +424,7 @@ class PrintModelAnalysisTest(test.TestCase): with session.Session(config=self._no_rewrite_session_config()) as sess: x = lib.BuildFullModel() - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) run_meta = config_pb2.RunMetadata() _ = sess.run( x, @@ -490,7 +490,7 @@ class PrintModelAnalysisTest(test.TestCase): with session.Session(config=self._no_rewrite_session_config()) as sess: x = lib.BuildSmallModel() - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) run_meta = config_pb2.RunMetadata() _ = sess.run(x, options=config_pb2.RunOptions( @@ -555,7 +555,7 @@ class PrintModelAnalysisTest(test.TestCase): with session.Session(config=self._no_rewrite_session_config()) as sess: x = lib.BuildSmallModel() - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) run_meta = config_pb2.RunMetadata() _ = sess.run(x, options=config_pb2.RunOptions( @@ -587,10 +587,10 @@ class PrintModelAnalysisTest(test.TestCase): def _trainLoop(self, train_op, train_steps, time_dir, time_step, memory_dir, memory_step, profile_dir, dump_step): with session.Session(config=self._no_rewrite_session_config()) as sess: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) # start from 1 because variable_initializer took one step. for i in range(1, train_steps + 1): - _ = sess.run(train_op) + _ = self.evaluate(train_op) if i in time_step: ret = gfile.ListDirectory(time_dir) self.assertEqual(len(ret), 1) diff --git a/tensorflow/python/profiler/option_builder.py b/tensorflow/python/profiler/option_builder.py index 2ad7adf769..9d8f7683a6 100644 --- a/tensorflow/python/profiler/option_builder.py +++ b/tensorflow/python/profiler/option_builder.py @@ -23,7 +23,7 @@ from tensorflow.python.profiler import tfprof_logger from tensorflow.python.util.tf_export import tf_export -@tf_export('profiler.ProfileOptionBuilder') +@tf_export(v1=['profiler.ProfileOptionBuilder']) class ProfileOptionBuilder(object): # pylint: disable=line-too-long """Option Builder for Profiling API. diff --git a/tensorflow/python/profiler/pprof_profiler_test.py b/tensorflow/python/profiler/pprof_profiler_test.py index 11a3487360..120a0d0eaa 100644 --- a/tensorflow/python/profiler/pprof_profiler_test.py +++ b/tensorflow/python/profiler/pprof_profiler_test.py @@ -24,6 +24,7 @@ from proto import profile_pb2 from tensorflow.core.framework import step_stats_pb2 from tensorflow.core.protobuf import config_pb2 from tensorflow.python.framework import constant_op +from tensorflow.python.framework import test_util from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import math_ops from tensorflow.python.platform import test @@ -135,6 +136,7 @@ comment: 9 profile.ParseFromString(profile_contents) self.assertEquals(expected_proto, str(profile)) + @test_util.run_deprecated_v1 def testProfileWithWhileLoop(self): options = config_pb2.RunOptions() options.trace_level = config_pb2.RunOptions.FULL_TRACE diff --git a/tensorflow/python/profiler/profile_context_test.py b/tensorflow/python/profiler/profile_context_test.py index 107ad443c3..885f08ca4b 100644 --- a/tensorflow/python/profiler/profile_context_test.py +++ b/tensorflow/python/profiler/profile_context_test.py @@ -21,6 +21,7 @@ import os from tensorflow.python.client import session from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import variables from tensorflow.python.platform import gfile from tensorflow.python.platform import test @@ -35,6 +36,7 @@ builder = option_builder.ProfileOptionBuilder class ProfilerContextTest(test.TestCase): + @test_util.run_deprecated_v1 def testBasics(self): ops.reset_default_graph() outfile = os.path.join(test.get_temp_dir(), "dump") @@ -48,10 +50,10 @@ class ProfilerContextTest(test.TestCase): with profile_context.ProfileContext(test.get_temp_dir()) as pctx: pctx.add_auto_profiling("op", options=opts, profile_steps=[15, 50, 100]) with session.Session() as sess: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) total_steps = 101 for i in range(total_steps): - sess.run(x) + self.evaluate(x) if i == 14 or i == 49: self.assertTrue(gfile.Exists(outfile)) gfile.Remove(outfile) @@ -69,45 +71,47 @@ class ProfilerContextTest(test.TestCase): with gfile.Open(outfile, "r") as f: self.assertEqual(profile_str, f.read()) + @test_util.run_deprecated_v1 def testAutoTracingInDeubMode(self): ops.reset_default_graph() x = lib.BuildFullModel() with profile_context.ProfileContext(test.get_temp_dir(), debug=True): with session.Session() as sess: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) for _ in range(10): - sess.run(x) + self.evaluate(x) for f in gfile.ListDirectory(test.get_temp_dir()): # Warm up, no tracing. self.assertFalse("run_meta" in f) - sess.run(x) + self.evaluate(x) self.assertTrue( gfile.Exists(os.path.join(test.get_temp_dir(), "run_meta_11"))) gfile.Remove(os.path.join(test.get_temp_dir(), "run_meta_11")) # fetched already. - sess.run(x) + self.evaluate(x) for f in gfile.ListDirectory(test.get_temp_dir()): self.assertFalse("run_meta" in f) + @test_util.run_deprecated_v1 def testDisabled(self): ops.reset_default_graph() x = lib.BuildFullModel() with profile_context.ProfileContext(test.get_temp_dir(), enabled=False) as pctx: with session.Session() as sess: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) for _ in range(10): - sess.run(x) + self.evaluate(x) self.assertTrue(pctx.profiler is None) self.assertTrue( getattr(session.BaseSession, "profile_context", None) is None) with profile_context.ProfileContext(test.get_temp_dir()) as pctx: with session.Session() as sess: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) for _ in range(10): - sess.run(x) + self.evaluate(x) self.assertFalse(pctx.profiler is None) self.assertFalse( getattr(session.BaseSession, "profile_context", None) is None) diff --git a/tensorflow/python/profiler/profiler.py b/tensorflow/python/profiler/profiler.py index efbdd1ba68..5f62690b54 100644 --- a/tensorflow/python/profiler/profiler.py +++ b/tensorflow/python/profiler/profiler.py @@ -49,7 +49,7 @@ _allowed_symbols.extend([ ]) # Export protos -tf_export('profiler.GraphNodeProto')(GraphNodeProto) -tf_export('profiler.MultiGraphNodeProto')(MultiGraphNodeProto) -tf_export('profiler.AdviceProto')(AdviceProto) -tf_export('profiler.OpLogProto')(OpLogProto) +tf_export(v1=['profiler.GraphNodeProto'])(GraphNodeProto) +tf_export(v1=['profiler.MultiGraphNodeProto'])(MultiGraphNodeProto) +tf_export(v1=['profiler.AdviceProto'])(AdviceProto) +tf_export(v1=['profiler.OpLogProto'])(OpLogProto) diff --git a/tensorflow/python/profiler/profiler_test.py b/tensorflow/python/profiler/profiler_test.py index eacb7d21e6..e4f7361e5d 100644 --- a/tensorflow/python/profiler/profiler_test.py +++ b/tensorflow/python/profiler/profiler_test.py @@ -21,6 +21,7 @@ import os from tensorflow.core.protobuf import config_pb2 from tensorflow.python.client import session from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import variables from tensorflow.python.platform import gfile from tensorflow.python.platform import test @@ -35,6 +36,7 @@ builder = option_builder.ProfileOptionBuilder class ProfilerTest(test.TestCase): + @test_util.run_deprecated_v1 def testProfileBasic(self): ops.reset_default_graph() outfile = os.path.join(test.get_temp_dir(), 'dump') @@ -171,6 +173,7 @@ class ProfilerTest(test.TestCase): checker = advice_pb.checkers['ExpensiveOperationChecker'] self.assertGreater(len(checker.reports), 0) + @test_util.run_deprecated_v1 def testMultipleProfilePerStep(self): ops.reset_default_graph() opts = (builder(builder.trainable_variables_parameter()) diff --git a/tensorflow/python/profiler/tfprof_logger.py b/tensorflow/python/profiler/tfprof_logger.py index e651de32ea..6ccd0e0ff3 100644 --- a/tensorflow/python/profiler/tfprof_logger.py +++ b/tensorflow/python/profiler/tfprof_logger.py @@ -188,7 +188,7 @@ def merge_default_with_oplog(graph, op_log=None, run_meta=None, return tmp_op_log -@tf_export('profiler.write_op_log') +@tf_export(v1=['profiler.write_op_log']) def write_op_log(graph, log_dir, op_log=None, run_meta=None, add_trace=True): """Log provided 'op_log', and add additional model information below. diff --git a/tensorflow/python/saved_model/BUILD b/tensorflow/python/saved_model/BUILD index e7a3b8afd5..53d0640542 100644 --- a/tensorflow/python/saved_model/BUILD +++ b/tensorflow/python/saved_model/BUILD @@ -12,6 +12,8 @@ licenses(["notice"]) # Apache 2.0 exports_files(["LICENSE"]) load("//tensorflow:tensorflow.bzl", "py_test") +load("//tensorflow/core:platform/default/build_config.bzl", "tf_proto_library") +load("//tensorflow/core:platform/default/build_config.bzl", "tf_additional_all_protos") py_library( name = "saved_model", @@ -21,6 +23,7 @@ py_library( deps = [ ":builder", ":constants", + ":load", ":loader", ":main_op", ":save", @@ -83,12 +86,13 @@ py_library( srcs_version = "PY2AND3", deps = [ ":constants", + ":signature_def_utils", ":utils", "//tensorflow/core:protos_all_py", "//tensorflow/python:framework_for_generated_wrappers", "//tensorflow/python:lib", "//tensorflow/python:platform", - "//tensorflow/python:training", + "//tensorflow/python:saver", "//tensorflow/python:util", "//tensorflow/python:variables", ], @@ -114,6 +118,7 @@ py_test( "//tensorflow/python:state_ops", "//tensorflow/python:training", "//tensorflow/python:variables", + "@absl_py//absl/testing:parameterized", ], ) @@ -166,14 +171,15 @@ py_test( ":signature_def_utils", ":tag_constants", "//tensorflow/core:protos_all_py", - "//tensorflow/python:client", "//tensorflow/python:client_testlib", "//tensorflow/python:control_flow_ops", "//tensorflow/python:errors", "//tensorflow/python:framework_for_generated_wrappers", + "//tensorflow/python:framework_test_lib", "//tensorflow/python:lib", "//tensorflow/python:math_ops", "//tensorflow/python:saver_test_utils", + "//tensorflow/python:session", "//tensorflow/python:state_ops", "//tensorflow/python:test_ops", "//tensorflow/python:training", @@ -264,6 +270,14 @@ py_test( ], ) +tf_proto_library( + name = "saved_object_graph", + srcs = ["saved_object_graph.proto"], + cc_api_version = 2, + protodeps = tf_additional_all_protos(), + visibility = ["//tensorflow:internal"], +) + py_library( name = "save", srcs = [ @@ -271,16 +285,26 @@ py_library( ], srcs_version = "PY2AND3", deps = [ + ":builder", + ":constants", ":loader", + ":saved_object_graph_py", ":signature_constants", ":signature_def_utils", + ":tag_constants", ":utils", "//tensorflow/core:protos_all_py", "//tensorflow/python:array_ops", + "//tensorflow/python:framework", "//tensorflow/python:framework_ops", + "//tensorflow/python:lib", + "//tensorflow/python:resource_variable_ops", "//tensorflow/python:util", + "//tensorflow/python/eager:context", "//tensorflow/python/eager:def_function", - "//tensorflow/python/eager:test", + "//tensorflow/python/eager:function", + "//tensorflow/python/training/checkpointable:base", + "//tensorflow/python/training/checkpointable:util", ], ) @@ -289,13 +313,42 @@ py_test( srcs = ["save_test.py"], srcs_version = "PY2AND3", deps = [ + ":loader", ":save", ":signature_constants", ":tag_constants", "//tensorflow/python/eager:def_function", + "//tensorflow/python/eager:test", "@absl_py//absl/testing:parameterized", ], ) -# ----------------------------------------------------------------------------- -# Google-internal targets. These must be at the end for syncrepo. +py_library( + name = "load", + srcs = [ + "load.py", + ], + srcs_version = "PY2AND3", + deps = [ + ":loader", + ":saved_object_graph_py", + "//tensorflow/python:lib", + "//tensorflow/python:util", + "//tensorflow/python/training/checkpointable:tracking", + ], +) + +py_test( + name = "load_test", + srcs = ["load_test.py"], + srcs_version = "PY2AND3", + deps = [ + ":load", + ":save", + "//tensorflow/python:dtypes", + "//tensorflow/python:tensor_spec", + "//tensorflow/python/eager:def_function", + "//tensorflow/python/eager:test", + "//tensorflow/python/training/checkpointable:tracking", + ], +) diff --git a/tensorflow/python/saved_model/builder.py b/tensorflow/python/saved_model/builder.py index be49c70c60..b929934eeb 100644 --- a/tensorflow/python/saved_model/builder.py +++ b/tensorflow/python/saved_model/builder.py @@ -24,5 +24,6 @@ from __future__ import division from __future__ import print_function # pylint: disable=unused-import +from tensorflow.python.saved_model.builder_impl import _SavedModelBuilder from tensorflow.python.saved_model.builder_impl import SavedModelBuilder # pylint: enable=unused-import diff --git a/tensorflow/python/saved_model/builder_impl.py b/tensorflow/python/saved_model/builder_impl.py index 4f68f7c5ae..f37d283a2a 100644 --- a/tensorflow/python/saved_model/builder_impl.py +++ b/tensorflow/python/saved_model/builder_impl.py @@ -18,6 +18,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import functools import os from google.protobuf.any_pb2 import Any @@ -32,6 +33,7 @@ from tensorflow.python.lib.io import file_io from tensorflow.python.ops import variables from tensorflow.python.platform import tf_logging from tensorflow.python.saved_model import constants +from tensorflow.python.saved_model import signature_def_utils from tensorflow.python.saved_model import utils_impl as saved_model_utils from tensorflow.python.training import saver as tf_saver from tensorflow.python.util import compat @@ -39,8 +41,9 @@ from tensorflow.python.util.deprecation import deprecated_args from tensorflow.python.util.tf_export import tf_export -@tf_export(v1=["saved_model.Builder", "saved_model.builder.SavedModelBuilder"]) -class SavedModelBuilder(object): +# Base class for the SavedModelBuilder that is only used by Tensorflow +# internally. Please use tf.compat.v1.saved_model.SavedModelBuilder instead. +class _SavedModelBuilder(object): """Builds the `SavedModel` protocol buffer and saves variables and assets. The `SavedModelBuilder` class provides functionality to build a `SavedModel` @@ -68,7 +71,7 @@ class SavedModelBuilder(object): builder.add_meta_graph_and_variables(sess, ["foo-tag"], signature_def_map=foo_signatures, - assets_collection=foo_assets) + assets_list=foo_assets) ... with tf.Session(graph=tf.Graph()) as sess: @@ -105,82 +108,24 @@ class SavedModelBuilder(object): # weights. self._has_saved_variables = False - def _save_and_write_assets(self, assets_collection_to_add=None): + def _save_and_write_assets(self, meta_graph_def, assets_list=None): """Saves asset to the meta graph and writes asset files to disk. Args: - assets_collection_to_add: The collection where the asset paths are setup. + meta_graph_def: The meta graph def to which the assets will be added. + assets_list: The list where the asset paths are setup. """ - asset_filename_map = _maybe_save_assets(assets_collection_to_add) + # Creates a function that adds assets into the meta graph def. + write_fn = functools.partial(_add_asset_to_metagraph, meta_graph_def) + asset_filename_map = _maybe_save_assets(write_fn, assets_list) # Return if there are no assets to write. if not asset_filename_map: tf_logging.info("No assets to write.") return - assets_destination_dir = saved_model_utils.get_or_create_assets_dir( - self._export_dir) - - # Copy each asset from source path to destination path. - for asset_basename, asset_source_filepath in asset_filename_map.items(): - asset_destination_filepath = os.path.join( - compat.as_bytes(assets_destination_dir), - compat.as_bytes(asset_basename)) - - # Only copy the asset file to the destination if it does not already - # exist. This is to ensure that an asset with the same name defined as - # part of multiple graphs is only copied the first time. - if not file_io.file_exists(asset_destination_filepath): - file_io.copy(asset_source_filepath, asset_destination_filepath) - - tf_logging.info("Assets written to: %s", - compat.as_text(assets_destination_dir)) - - def _maybe_add_main_op(self, main_op): - """Adds main op to the SavedModel. - - Args: - main_op: Main op to run as part of graph initialization. If None, no - main op will be added to the graph. - - Raises: - TypeError: if main op is provided but is not of type `Operation`. - ValueError: if the Graph already contains an init op. - """ - if main_op is None: - return - - if not isinstance(main_op, ops.Operation): - raise TypeError("main_op needs to be an Operation: %r" % main_op) - - # Validate that no other init ops have been added to this graph already. - # We check main_op and legacy_init_op for thoroughness and explicitness. - for init_op_key in (constants.MAIN_OP_KEY, constants.LEGACY_INIT_OP_KEY): - if ops.get_collection(init_op_key): - raise ValueError( - "Graph already contains one or more main ops under the " - "collection {}.".format(init_op_key)) - - ops.add_to_collection(constants.MAIN_OP_KEY, main_op) - - def _add_train_op(self, train_op): - """Add train op to the SavedModel. - - Note that this functionality is in development, and liable to be - moved elsewhere. - - Args: - train_op: Op or group of ops that are used for training. These are - stored as a collection with key TRAIN_OP_KEY, but not executed. - - Raises: - TypeError if Train op is not of type `Operation`. - """ - if train_op is not None: - if (not isinstance(train_op, ops.Tensor) and - not isinstance(train_op, ops.Operation)): - raise TypeError("train_op needs to be a Tensor or Op: %r" % train_op) - ops.add_to_collection(constants.TRAIN_OP_KEY, train_op) + # Copy assets from source path to destination path. + copy_assets_to_destination_dir(asset_filename_map, self._export_dir) def _tag_and_add_meta_graph(self, meta_graph_def, tags, signature_def_map): """Tags the meta graph def and adds it to the SavedModel. @@ -237,30 +182,32 @@ class SavedModelBuilder(object): Validation of entries in the signature def map includes ensuring that the `name` and `dtype` fields of the TensorInfo protos of the `inputs` and - `outputs` of each `SignatureDef` are populated. + `outputs` of each `SignatureDef` are populated. Also ensures that reserved + SigantureDef keys for the initialization and train ops are not used. Args: signature_def_map: The map of signature defs to be validated. - """ - if signature_def_map is not None: - for signature_def_key in signature_def_map: - signature_def = signature_def_map[signature_def_key] - inputs = signature_def.inputs - outputs = signature_def.outputs - for inputs_key in inputs: - self._validate_tensor_info(inputs[inputs_key]) - for outputs_key in outputs: - self._validate_tensor_info(outputs[outputs_key]) - - def _add_collections( - self, assets_collection, main_op, train_op): - """Add asset and op collections to be saved.""" - # Save asset files and write them to disk, if any. - self._save_and_write_assets(assets_collection) - self._maybe_add_main_op(main_op) - - self._add_train_op(train_op) + Raises: + AssertionError: If a TensorInfo is not valid. + KeyError: If a reserved signature key is used in the map. + """ + for signature_def_key in signature_def_map: + signature_def = signature_def_map[signature_def_key] + inputs = signature_def.inputs + outputs = signature_def.outputs + for inputs_key in inputs: + self._validate_tensor_info(inputs[inputs_key]) + for outputs_key in outputs: + self._validate_tensor_info(outputs[outputs_key]) + if constants.INIT_OP_SIGNATURE_KEY in signature_def_map: + raise KeyError( + "SignatureDef map key \"{}\" is reserved for initialization. Please " + "use a different key.".format(constants.INIT_OP_SIGNATURE_KEY)) + if constants.TRAIN_OP_SIGNATURE_KEY in signature_def_map: + raise KeyError( + "SignatureDef map key \"{}\" is reserved for the train op. Please " + "use a different key.".format(constants.TRAIN_OP_SIGNATURE_KEY)) def _maybe_create_saver(self, saver=None): """Creates a sharded saver if one does not already exist.""" @@ -274,19 +221,14 @@ class SavedModelBuilder(object): allow_empty=True) return saver - @deprecated_args(None, - "Pass your op to the equivalent parameter main_op instead.", - "legacy_init_op") def add_meta_graph(self, tags, signature_def_map=None, - assets_collection=None, - legacy_init_op=None, + assets_list=None, clear_devices=False, - main_op=None, - strip_default_attrs=False, + init_op=None, + train_op=None, saver=None): - # pylint: disable=line-too-long """Adds the current meta graph to the SavedModel. Creates a Saver in the current scope and uses the Saver to export the meta @@ -297,19 +239,17 @@ class SavedModelBuilder(object): tags: The set of tags to annotate the meta graph def with. signature_def_map: The map of signature defs to be added to the meta graph def. - assets_collection: Assets collection to be saved with SavedModel. Note - that this collection should be a subset of the assets saved as part of + assets_list: Assets to be saved with SavedModel. Note + that this list should be a subset of the assets saved as part of the first meta graph in the SavedModel. - legacy_init_op: Legacy support for op or group of ops to execute after the - restore op upon a load. Deprecated; please use main_op instead. clear_devices: Set to true if the device info on the default graph should be cleared. - main_op: Op or group of ops to execute when the graph is loaded. Note - that when the main_op is specified it is run after the restore op at + init_op: Op or group of ops to execute when the graph is loaded. Note + that when the init_op is specified it is run after the restore op at load-time. - strip_default_attrs: Boolean. If `True`, default-valued attributes will be - removed from the NodeDefs. For a detailed guide, see - [Stripping Default-Valued Attributes](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/saved_model/README.md#stripping-default-valued-attributes). + train_op: Op or group of opts that trains the model when run. This will + not be run automatically when the graph is loaded, instead saved in + a SignatureDef accessible through the exported MetaGraph. saver: An instance of tf.train.Saver that will be used to export the metagraph. If None, a sharded Saver that restores all variables will be used. @@ -318,7 +258,6 @@ class SavedModelBuilder(object): AssertionError: If the variables for the SavedModel have not been saved yet, or if the graph already contains one or more legacy init ops. """ - # pylint: enable=line-too-long if not self._has_saved_variables: raise AssertionError( "Graph state including variables and assets has not been saved yet. " @@ -326,14 +265,15 @@ class SavedModelBuilder(object): # Validate the signature def map to ensure all included TensorInfos are # properly populated. + signature_def_map = signature_def_map or {} self._validate_signature_def_map(signature_def_map) - # legacy_init_op is deprecated, and going away in TF 2.0. - # Re-mapping to main_op, as treatment is identical regardless. - main_op = main_op or legacy_init_op - - # Add assets and ops - self._add_collections(assets_collection, main_op, None) + # Create a SignatureDef pointing to the graph initialization op, which will + # be added to the MetaGraphDef. + _add_op_to_signature_def_map(signature_def_map, init_op, + constants.INIT_OP_SIGNATURE_KEY) + _add_op_to_signature_def_map(signature_def_map, train_op, + constants.TRAIN_OP_SIGNATURE_KEY) saver = self._maybe_create_saver(saver) @@ -345,22 +285,22 @@ class SavedModelBuilder(object): # resolved, we just leave the option set to False for now. # TODO(soergel): Reinstate clear_extraneous_savers=True when possible. meta_graph_def = saver.export_meta_graph( - clear_devices=clear_devices, strip_default_attrs=strip_default_attrs) + clear_devices=clear_devices, strip_default_attrs=True) + + # Save asset files and write them to disk, if any. + self._save_and_write_assets(meta_graph_def, assets_list) # Tag the meta graph def and add it to the SavedModel. self._tag_and_add_meta_graph(meta_graph_def, tags, signature_def_map) - @deprecated_args(None, - "Pass your op to the equivalent parameter main_op instead.", - "legacy_init_op") def add_meta_graph_and_variables(self, sess, tags, signature_def_map=None, - assets_collection=None, - legacy_init_op=None, + assets_list=None, clear_devices=False, - main_op=None, + init_op=None, + train_op=None, strip_default_attrs=False, saver=None): # pylint: disable=line-too-long @@ -378,14 +318,15 @@ class SavedModelBuilder(object): tags: The set of tags with which to save the meta graph. signature_def_map: The map of signature def map to add to the meta graph def. - assets_collection: Assets collection to be saved with SavedModel. - legacy_init_op: Legacy support for op or group of ops to execute after the - restore op upon a load. Deprecated; please use main_op instead. + assets_list: Assets to be saved with SavedModel. clear_devices: Set to true if the device info on the default graph should be cleared. - main_op: Op or group of ops to execute when the graph is loaded. Note - that when the main_op is specified it is run after the restore op at + init_op: Op or group of ops to execute when the graph is loaded. Note + that when the init_op is specified it is run after the restore op at load-time. + train_op: Op or group of ops that trains the model when run. This will + not be run automatically when the graph is loaded, instead saved in + a SignatureDef accessible through the exported MetaGraph. strip_default_attrs: Boolean. If `True`, default-valued attributes will be removed from the NodeDefs. For a detailed guide, see [Stripping Default-Valued Attributes](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/saved_model/README.md#stripping-default-valued-attributes). @@ -402,14 +343,15 @@ class SavedModelBuilder(object): # Validate the signature def map to ensure all included TensorInfos are # properly populated. + signature_def_map = signature_def_map or {} self._validate_signature_def_map(signature_def_map) - # legacy_init_op is deprecated, and going away in TF 2.0. - # Re-mapping to main_op, as treatment is identical regardless. - main_op = main_op or legacy_init_op - - # Add assets and ops - self._add_collections(assets_collection, main_op, None) + # Create a SignatureDef pointing to the graph initialization op, which will + # be added to the MetaGraphDef. + _add_op_to_signature_def_map(signature_def_map, init_op, + constants.INIT_OP_SIGNATURE_KEY) + _add_op_to_signature_def_map(signature_def_map, train_op, + constants.TRAIN_OP_SIGNATURE_KEY) saved_model_utils.get_or_create_variables_dir(self._export_dir) variables_path = saved_model_utils.get_variables_path(self._export_dir) @@ -434,6 +376,9 @@ class SavedModelBuilder(object): meta_graph_def = saver.export_meta_graph( clear_devices=clear_devices, strip_default_attrs=strip_default_attrs) + # Save asset files and write them to disk, if any. + self._save_and_write_assets(meta_graph_def, assets_list) + # Tag the meta graph def and add it to the SavedModel. self._tag_and_add_meta_graph(meta_graph_def, tags, signature_def_map) @@ -471,11 +416,205 @@ class SavedModelBuilder(object): return path -def _maybe_save_assets(assets_collection_to_add=None): +@tf_export(v1=["saved_model.Builder", "saved_model.builder.SavedModelBuilder"]) # pylint: disable=missing-docstring +class SavedModelBuilder(_SavedModelBuilder): + __doc__ = _SavedModelBuilder.__doc__.replace("assets_list", + "assets_collection") + + def __init__(self, export_dir): + super(SavedModelBuilder, self).__init__(export_dir=export_dir) + + def _add_collections(self, assets_collection, main_op, train_op): + """Add asset and op collections to be saved.""" + # Save asset files and write them to disk, if any. + self._save_and_write_assets(assets_collection) + + self._maybe_add_main_op(main_op) + + self._add_train_op(train_op) + + def _save_and_write_assets(self, assets_collection_to_add=None): + """Saves asset to the meta graph and writes asset files to disk. + + Args: + assets_collection_to_add: The collection where the asset paths are setup. + """ + # Add assets to the collection with key `constants.ASSETS_KEY`, in the + # graph. + asset_filename_map = _maybe_save_assets(_add_asset_to_collection, + assets_collection_to_add) + + # Return if there are no assets to write. + if not asset_filename_map: + tf_logging.info("No assets to write.") + return + + # Copy assets from source path to destination path. + copy_assets_to_destination_dir(asset_filename_map, self._export_dir) + + def _maybe_add_main_op(self, main_op): + """Adds main op to the SavedModel. + + Args: + main_op: Main op to run as part of graph initialization. If None, no main + op will be added to the graph. + + Raises: + TypeError: if main op is provided but is not of type `Operation`. + ValueError: if the Graph already contains an init op. + """ + if main_op is None: + return + + if not isinstance(main_op, ops.Operation): + raise TypeError("main_op needs to be an Operation: %r" % main_op) + + # Validate that no other init ops have been added to this graph already. + # We check main_op and legacy_init_op for thoroughness and explicitness. + for init_op_key in (constants.MAIN_OP_KEY, constants.LEGACY_INIT_OP_KEY): + if ops.get_collection(init_op_key): + raise ValueError( + "Graph already contains one or more main ops under the " + "collection {}.".format(init_op_key)) + + ops.add_to_collection(constants.MAIN_OP_KEY, main_op) + + def _add_train_op(self, train_op): + """Add train op to the SavedModel. + + Note that this functionality is in development, and liable to be + moved elsewhere. + + Args: + train_op: Op or group of ops that are used for training. These are stored + as a collection with key TRAIN_OP_KEY, but not executed. + + Raises: + TypeError if Train op is not of type `Operation`. + """ + if train_op is not None: + if (not isinstance(train_op, ops.Tensor) and + not isinstance(train_op, ops.Operation)): + raise TypeError("train_op needs to be a Tensor or Op: %r" % train_op) + ops.add_to_collection(constants.TRAIN_OP_KEY, train_op) + + @deprecated_args(None, + "Pass your op to the equivalent parameter main_op instead.", + "legacy_init_op") + def add_meta_graph(self, + tags, + signature_def_map=None, + assets_collection=None, + legacy_init_op=None, + clear_devices=False, + main_op=None, + strip_default_attrs=False, + saver=None): + if not self._has_saved_variables: + raise AssertionError( + "Graph state including variables and assets has not been saved yet. " + "Please invoke `add_meta_graph_and_variables()` first.") + + # Validate the signature def map to ensure all included TensorInfos are + # properly populated. + signature_def_map = signature_def_map or {} + self._validate_signature_def_map(signature_def_map) + + # legacy_init_op is deprecated, and going away in TF 2.0. + # Re-mapping to main_op, as treatment is identical regardless. + main_op = main_op or legacy_init_op + + # Add assets and ops + self._add_collections(assets_collection, main_op, None) + + saver = self._maybe_create_saver(saver) + + # The graph almost certainly previously contained at least one Saver, and + # possibly several (e.g. one for loading a pretrained embedding, and another + # for the model weights). Removing the preexisting ones was the + # motivation for the clear_extraneous_savers option, but it turns out that + # there are edge cases where that option breaks the graph. Until that is + # resolved, we just leave the option set to False for now. + # TODO(soergel): Reinstate clear_extraneous_savers=True when possible. + meta_graph_def = saver.export_meta_graph( + clear_devices=clear_devices, strip_default_attrs=strip_default_attrs) + + # Tag the meta graph def and add it to the SavedModel. + self._tag_and_add_meta_graph(meta_graph_def, tags, signature_def_map) + + @deprecated_args(None, + "Pass your op to the equivalent parameter main_op instead.", + "legacy_init_op") + def add_meta_graph_and_variables(self, + sess, + tags, + signature_def_map=None, + assets_collection=None, + legacy_init_op=None, + clear_devices=False, + main_op=None, + strip_default_attrs=False, + saver=None): + if self._has_saved_variables: + raise AssertionError("Graph state including variables and assets has " + "already been saved. Please invoke " + "`add_meta_graph()` instead.") + + # Validate the signature def map to ensure all included TensorInfos are + # properly populated. + signature_def_map = signature_def_map or {} + self._validate_signature_def_map(signature_def_map) + + # legacy_init_op is deprecated, and going away in TF 2.0. + # Re-mapping to main_op, as treatment is identical regardless. + main_op = main_op or legacy_init_op + + # Add assets and ops + self._add_collections(assets_collection, main_op, None) + + saved_model_utils.get_or_create_variables_dir(self._export_dir) + variables_path = saved_model_utils.get_variables_path(self._export_dir) + + saver = self._maybe_create_saver(saver) + + # Save the variables. Also, disable writing the checkpoint state proto. The + # file is not used during SavedModel loading. In addition, since a + # SavedModel can be copied or moved, this avoids the checkpoint state to + # become outdated. + saver.save(sess, variables_path, write_meta_graph=False, write_state=False) + + # Export the meta graph def. + + # The graph almost certainly previously contained at least one Saver, and + # possibly several (e.g. one for loading a pretrained embedding, and another + # for the model weights). Removing the preexisting ones was the + # motivation for the clear_extraneous_savers option, but it turns out that + # there are edge cases where that option breaks the graph. Until that is + # resolved, we just leave the option set to False for now. + # TODO(soergel): Reinstate clear_extraneous_savers=True when possible. + meta_graph_def = saver.export_meta_graph( + clear_devices=clear_devices, strip_default_attrs=strip_default_attrs) + + # Tag the meta graph def and add it to the SavedModel. + self._tag_and_add_meta_graph(meta_graph_def, tags, signature_def_map) + + # Mark this instance of SavedModel as having saved variables, such that + # subsequent attempts to save variables will fail. + self._has_saved_variables = True + + add_meta_graph.__doc__ = _SavedModelBuilder.add_meta_graph.__doc__.replace( + "assets_list", "assets_collection") + add_meta_graph_and_variables.__doc__ = \ + _SavedModelBuilder.add_meta_graph_and_variables.__doc__.replace( + "assets_list", "assets_collection") + + +def _maybe_save_assets(write_fn, assets_to_add=None): """Saves assets to the meta graph. Args: - assets_collection_to_add: The collection where the asset paths are setup. + write_fn: A function callback that writes asset into meta graph. + assets_to_add: The list where the asset paths are setup. Returns: A dict of asset basenames for saving to the original full path to the asset. @@ -486,25 +625,25 @@ def _maybe_save_assets(assets_collection_to_add=None): # Map of target file names to original filenames asset_filename_map = {} - if assets_collection_to_add is None: + if assets_to_add is None: tf_logging.info("No assets to save.") return asset_filename_map - # Iterate over the supplied asset collection, build the `AssetFile` proto - # and add them to the collection with key `constants.ASSETS_KEY`, in the - # graph. - for asset_tensor in assets_collection_to_add: + # Iterate over the supplied assets, build the `AssetFile` proto and add them + # to the meta graph. + for asset_tensor in assets_to_add: asset_source_filepath = _asset_path_from_tensor(asset_tensor) if not asset_source_filepath: raise ValueError("Invalid asset filepath tensor %s" % asset_tensor) - asset_filename = _get_asset_filename_to_add( + asset_filename = get_asset_filename_to_add( asset_source_filepath, asset_filename_map) - # Build `AssetFile` proto and add it to the asset collection in the graph. + # Call the passed-in function that builds AssetFileDef proto and adds it + # to either the collection or asset_file_def field of the meta graph. # Note that this should be done even when the file is a duplicate of an # already-added file, as the tensor reference should still exist. - _add_asset_to_collection(asset_filename, asset_tensor) + write_fn(asset_filename, asset_tensor) # In the cases where we are adding a duplicate, this will result in the # last of the filepaths being the one used for copying the file to the @@ -516,7 +655,7 @@ def _maybe_save_assets(assets_collection_to_add=None): return asset_filename_map -def _get_asset_filename_to_add(asset_filepath, asset_filename_map): +def get_asset_filename_to_add(asset_filepath, asset_filename_map): """Get a unique basename to add to the SavedModel if this file is unseen. Assets come from users as full paths, and we save them out to the @@ -542,7 +681,7 @@ def _get_asset_filename_to_add(asset_filepath, asset_filename_map): other_asset_filepath = asset_filename_map[asset_filename] if other_asset_filepath == asset_filepath: - # This is the same file, stored twice in the collection list. No need + # This is the same file, stored twice in the list. No need # to make unique. return asset_filename @@ -589,6 +728,41 @@ def _asset_path_from_tensor(path_tensor): return str_values[0] +def _add_asset_to_metagraph(meta_graph_def, asset_filename, asset_tensor): + """Builds an asset proto and adds it to the meta graph def. + + Args: + meta_graph_def: The meta graph def to which the asset will be added. + asset_filename: The filename of the asset to be added. + asset_tensor: The asset tensor used to populate the tensor info of the asset + proto. + """ + asset_proto = meta_graph_def.asset_file_def.add() + asset_proto.filename = asset_filename + asset_proto.tensor_info.name = asset_tensor.name + + +def copy_assets_to_destination_dir(asset_filename_map, destination_dir): + """Copy all assets from source path to destination path.""" + assets_destination_dir = saved_model_utils.get_or_create_assets_dir( + destination_dir) + + # Copy each asset from source path to destination path. + for asset_basename, asset_source_filepath in asset_filename_map.items(): + asset_destination_filepath = os.path.join( + compat.as_bytes(assets_destination_dir), + compat.as_bytes(asset_basename)) + + # Only copy the asset file to the destination if it does not already + # exist. This is to ensure that an asset with the same name defined as + # part of multiple graphs is only copied the first time. + if not file_io.file_exists(asset_destination_filepath): + file_io.copy(asset_source_filepath, asset_destination_filepath) + + tf_logging.info("Assets written to: %s", + compat.as_text(assets_destination_dir)) + + def _add_asset_to_collection(asset_filename, asset_tensor): """Builds an asset proto and adds it to the asset collection of the graph. @@ -604,3 +778,8 @@ def _add_asset_to_collection(asset_filename, asset_tensor): asset_any_proto = Any() asset_any_proto.Pack(asset_proto) ops.add_to_collection(constants.ASSETS_KEY, asset_any_proto) + + +def _add_op_to_signature_def_map(signature_def_map, op, key): + if op is not None: + signature_def_map[key] = signature_def_utils.op_signature_def(op, key) diff --git a/tensorflow/python/saved_model/constants.py b/tensorflow/python/saved_model/constants.py index 0addbdc968..90511a409e 100644 --- a/tensorflow/python/saved_model/constants.py +++ b/tensorflow/python/saved_model/constants.py @@ -29,6 +29,9 @@ tf_export( "saved_model.ASSETS_DIRECTORY", "saved_model.constants.ASSETS_DIRECTORY" ]).export_constant(__name__, "ASSETS_DIRECTORY") +# Subdirectory name containing unmanaged files from higher-level APIs. +EXTRA_ASSETS_DIRECTORY = "assets.extra" + # CollectionDef key containing SavedModel assets. ASSETS_KEY = "saved_model_assets" tf_export( @@ -40,7 +43,6 @@ tf_export( # CollectionDef key for the legacy init op. LEGACY_INIT_OP_KEY = "legacy_init_op" tf_export( - "saved_model.LEGACY_INIT_OP_KEY", v1=[ "saved_model.LEGACY_INIT_OP_KEY", "saved_model.constants.LEGACY_INIT_OP_KEY" @@ -49,13 +51,12 @@ tf_export( # CollectionDef key for the SavedModel main op. MAIN_OP_KEY = "saved_model_main_op" tf_export( - "saved_model.MAIN_OP_KEY", v1=["saved_model.MAIN_OP_KEY", "saved_model.constants.MAIN_OP_KEY"]).export_constant( __name__, "MAIN_OP_KEY") # CollectionDef key for the SavedModel train op. -# Not exported while export_all_saved_models is in contrib. +# Not exported while export_all_saved_models is experimental. TRAIN_OP_KEY = "saved_model_train_op" # Schema version for SavedModel. @@ -106,3 +107,8 @@ tf_export( "saved_model.VARIABLES_FILENAME", "saved_model.constants.VARIABLES_FILENAME" ]).export_constant(__name__, "VARIABLES_FILENAME") + +# The initialization and train ops for a MetaGraph are stored in the +# signature def map. The ops are added to the map with the following keys. +INIT_OP_SIGNATURE_KEY = "__saved_model_init_op" +TRAIN_OP_SIGNATURE_KEY = "__saved_model_train_op" diff --git a/tensorflow/python/saved_model/load.py b/tensorflow/python/saved_model/load.py new file mode 100644 index 0000000000..397086e106 --- /dev/null +++ b/tensorflow/python/saved_model/load.py @@ -0,0 +1,61 @@ +# Copyright 2018 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. +# ============================================================================== +"""Import a checkpointable object from a SavedModel.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os + +from tensorflow.python.lib.io import file_io +from tensorflow.python.saved_model import constants +from tensorflow.python.saved_model import saved_object_graph_pb2 +from tensorflow.python.training.checkpointable import tracking +from tensorflow.python.util import compat + + +def _recreate_object_graph(object_graph_proto): + """Recreates Python objects from an ObjectGraph proto.""" + objects = [] + for _ in object_graph_proto.nodes: + # TODO(allenl): re-create variables and other types + objects.append(tracking.Checkpointable()) + for obj, object_proto in zip(objects, object_graph_proto.nodes): + for reference in object_proto.children: + setattr(obj, reference.local_name, objects[reference.node_id]) + return objects[0] + + +def load(export_dir): + """Load a SavedModel from `export_dir`.""" + object_graph_filename = os.path.join( + compat.as_bytes(export_dir), + compat.as_bytes(constants.EXTRA_ASSETS_DIRECTORY), + compat.as_bytes("object_graph.pb")) + if file_io.file_exists(object_graph_filename): + # If there is an object graph associated with the SavedModel, we'll create a + # root object from that. + object_graph_string = file_io.FileIO(object_graph_filename, "rb").read() + object_graph_proto = ( + saved_object_graph_pb2.SavedObjectGraph()) + object_graph_proto.ParseFromString(object_graph_string) + root = _recreate_object_graph(object_graph_proto) + else: + raise NotImplementedError( + "Currently only SavedModels exported with `tf.saved_model.save` may be " + "imported. Other SavedModels may eventually be supported via load().") + # TODO(allenl): load functions from the SavedModel into the eager context + return root diff --git a/tensorflow/python/saved_model/load_test.py b/tensorflow/python/saved_model/load_test.py new file mode 100644 index 0000000000..bfa201ad63 --- /dev/null +++ b/tensorflow/python/saved_model/load_test.py @@ -0,0 +1,51 @@ +# Copyright 2018 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 checkpointable object SavedModel loading.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os + +from tensorflow.python.eager import def_function +from tensorflow.python.eager import test +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import tensor_spec +from tensorflow.python.saved_model import load +from tensorflow.python.saved_model import save +from tensorflow.python.training.checkpointable import tracking + + +class LoadTest(test.TestCase): + + def test_structure_import(self): + root = tracking.Checkpointable() + root.f = def_function.function( + lambda x: 2. * x, + input_signature=[tensor_spec.TensorSpec(None, dtypes.float32)]) + root.dep_one = tracking.Checkpointable() + root.dep_two = tracking.Checkpointable() + root.dep_two.dep = tracking.Checkpointable() + root.dep_three = root.dep_two.dep + save_dir = os.path.join(self.get_temp_dir(), "saved_model") + save.save(root, save_dir) + imported = load.load(save_dir) + self.assertIs(imported.dep_three, imported.dep_two.dep) + self.assertIsNot(imported.dep_one, imported.dep_two) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/saved_model/loader_impl.py b/tensorflow/python/saved_model/loader_impl.py index 8c8eaf038a..6bf39a2c67 100644 --- a/tensorflow/python/saved_model/loader_impl.py +++ b/tensorflow/python/saved_model/loader_impl.py @@ -31,6 +31,7 @@ from tensorflow.python.lib.io import file_io from tensorflow.python.ops import variables from tensorflow.python.platform import tf_logging from tensorflow.python.saved_model import constants +from tensorflow.python.saved_model import signature_def_utils from tensorflow.python.saved_model import utils_impl as saved_model_utils from tensorflow.python.training import saver as tf_saver from tensorflow.python.util import compat @@ -99,22 +100,29 @@ def _get_asset_tensors(export_dir, meta_graph_def_to_load, import_scope=None): collection_def = meta_graph_def_to_load.collection_def asset_tensor_dict = {} - if constants.ASSETS_KEY in collection_def: - # Location of the assets for SavedModel. - assets_directory = os.path.join( - compat.as_bytes(export_dir), - compat.as_bytes(constants.ASSETS_DIRECTORY)) + asset_protos = [] + + if meta_graph_def_to_load.asset_file_def: + asset_protos = meta_graph_def_to_load.asset_file_def + elif constants.ASSETS_KEY in collection_def: assets_any_proto = collection_def[constants.ASSETS_KEY].any_list.value - # Process each asset and add it to the asset tensor dictionary. for asset_any_proto in assets_any_proto: asset_proto = meta_graph_pb2.AssetFileDef() asset_any_proto.Unpack(asset_proto) - tensor_name = asset_proto.tensor_info.name - if import_scope: - tensor_name = "%s/%s" % (import_scope, tensor_name) - asset_tensor_dict[tensor_name] = os.path.join( - compat.as_bytes(assets_directory), - compat.as_bytes(asset_proto.filename)) + asset_protos.append(asset_proto) + + # Location of the assets for SavedModel. + assets_directory = os.path.join( + compat.as_bytes(export_dir), compat.as_bytes(constants.ASSETS_DIRECTORY)) + # Process each asset and add it to the asset tensor dictionary. + for asset_proto in asset_protos: + tensor_name = asset_proto.tensor_info.name + if import_scope: + tensor_name = "%s/%s" % (import_scope, tensor_name) + asset_tensor_dict[tensor_name] = os.path.join( + compat.as_bytes(assets_directory), + compat.as_bytes(asset_proto.filename)) + return asset_tensor_dict @@ -134,23 +142,53 @@ def _get_main_op_tensor( RuntimeError: If the collection def corresponding to the main op key has other than exactly one tensor. """ + # TODO(kathywu): Rename this method to _get_op_from_collection when + # dependency from SavedModelEstimator is removed. collection_def = meta_graph_def_to_load.collection_def - main_op_tensor = None + init_op = None if init_op_key in collection_def: - main_ops = collection_def[init_op_key].node_list.value - if len(main_ops) != 1: - raise RuntimeError("Expected exactly one SavedModel main op. " - "Found: {}".format(main_ops)) - main_op_tensor = ops.get_collection(init_op_key)[0] - return main_op_tensor + init_op_list = collection_def[init_op_key].node_list.value + if len(init_op_list) != 1: + raise RuntimeError("Expected exactly one SavedModel init op. " + "Found: {}".format(init_op_list)) + init_op = ops.get_collection(init_op_key)[0] + return init_op + + +def _get_op_from_collection(meta_graph_def, op_key): + return _get_main_op_tensor(meta_graph_def, op_key) + + +def _get_op_from_signature_def(meta_graph_def, op_signature_key, import_scope): + """Retrieve op stored in the imported meta graph's signature def.""" + if op_signature_key in meta_graph_def.signature_def: + return signature_def_utils.load_op_from_signature_def( + meta_graph_def.signature_def[op_signature_key], op_signature_key, + import_scope) + else: + return None -@tf_export( +def get_init_op(meta_graph_def, import_scope=None): + return (_get_op_from_signature_def( + meta_graph_def, constants.INIT_OP_SIGNATURE_KEY, import_scope) or + _get_op_from_collection(meta_graph_def, constants.MAIN_OP_KEY) or + _get_op_from_collection(meta_graph_def, constants.LEGACY_INIT_OP_KEY)) + + +def get_train_op(meta_graph_def, import_scope=None): + train_op = _get_op_from_signature_def( + meta_graph_def, constants.TRAIN_OP_SIGNATURE_KEY, import_scope) + if train_op is None: + train_op = _get_op_from_collection(meta_graph_def, constants.TRAIN_OP_KEY) + return train_op + + +@tf_export(v1=[ + "saved_model.contains_saved_model", "saved_model.maybe_saved_model_directory", - v1=[ - "saved_model.maybe_saved_model_directory", - "saved_model.loader.maybe_saved_model_directory" - ]) + "saved_model.loader.maybe_saved_model_directory" +]) @deprecation.deprecated_endpoints( "saved_model.loader.maybe_saved_model_directory") def maybe_saved_model_directory(export_dir): @@ -173,6 +211,25 @@ def maybe_saved_model_directory(export_dir): return file_io.file_exists(txt_path) or file_io.file_exists(pb_path) +@tf_export("saved_model.contains_saved_model", v1=[]) +def contains_saved_model(export_dir): + """Checks whether the provided export directory could contain a SavedModel. + + Note that the method does not load any data by itself. If the method returns + `false`, the export directory definitely does not contain a SavedModel. If the + method returns `true`, the export directory may contain a SavedModel but + provides no guarantee that it can be loaded. + + Args: + export_dir: Absolute string path to possible export location. For example, + '/my/foo/model'. + + Returns: + True if the export directory contains SavedModel files, False otherwise. + """ + return maybe_saved_model_directory(export_dir) + + @tf_export(v1=["saved_model.load", "saved_model.loader.load"]) @deprecation.deprecated( None, @@ -334,11 +391,9 @@ class SavedModelLoader(object): asset_tensors_dictionary = _get_asset_tensors( self._export_dir, meta_graph_def, import_scope=import_scope) - main_op_tensor = ( - _get_main_op_tensor(meta_graph_def, constants.MAIN_OP_KEY) or - _get_main_op_tensor(meta_graph_def, constants.LEGACY_INIT_OP_KEY)) - if main_op_tensor is not None: - sess.run(fetches=[main_op_tensor], feed_dict=asset_tensors_dictionary) + init_op = get_init_op(meta_graph_def, import_scope) + if init_op is not None: + sess.run(fetches=[init_op], feed_dict=asset_tensors_dictionary) def load(self, sess, tags, import_scope=None, **saver_kwargs): """Load the MetaGraphDef graph and restore variable values into the session. diff --git a/tensorflow/python/saved_model/loader_test.py b/tensorflow/python/saved_model/loader_test.py index 924b2e7c06..3b7f0b250e 100644 --- a/tensorflow/python/saved_model/loader_test.py +++ b/tensorflow/python/saved_model/loader_test.py @@ -19,11 +19,14 @@ from __future__ import division from __future__ import print_function import os +import shutil + +from absl.testing import parameterized from tensorflow.python.client import session from tensorflow.python.framework import errors from tensorflow.python.framework import ops -from tensorflow.python.lib.io import file_io +from tensorflow.python.framework import test_util from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import state_ops from tensorflow.python.ops import variables @@ -42,55 +45,74 @@ SIMPLE_ADD_SAVED_MODEL = _get_export_dir("simple_add_saved_model") SAVED_MODEL_WITH_MAIN_OP = _get_export_dir("saved_model_with_main_op") -class SavedModelLoaderTest(test.TestCase): - - def setUp(self): - """Write test SavedModels to a temp directory.""" - with session.Session(graph=ops.Graph()) as sess: - x = variables.VariableV1(5, name="x") - y = variables.VariableV1(11, name="y") - z = x + y - sess.run(variables.global_variables_initializer()) - - foo_sig_def = signature_def_utils.build_signature_def( - {"foo_input": utils.build_tensor_info(x)}, - {"foo_output": utils.build_tensor_info(z)}) - bar_sig_def = signature_def_utils.build_signature_def( - {"bar_x": utils.build_tensor_info(x), - "bar_y": utils.build_tensor_info(y)}, - {"bar_z": utils.build_tensor_info(z)}) - - builder = saved_model_builder.SavedModelBuilder(SIMPLE_ADD_SAVED_MODEL) - builder.add_meta_graph_and_variables( - sess, ["foo_graph"], {"foo": foo_sig_def, "bar": bar_sig_def}) +def build_graph_helper(): + g = ops.Graph() + with g.as_default(): + x = variables.VariableV1(5, name="x") + y = variables.VariableV1(11, name="y") + z = x + y + + foo_sig_def = signature_def_utils.build_signature_def({ + "foo_input": utils.build_tensor_info(x) + }, {"foo_output": utils.build_tensor_info(z)}) + bar_sig_def = signature_def_utils.build_signature_def({ + "bar_x": utils.build_tensor_info(x), + "bar_y": utils.build_tensor_info(y) + }, {"bar_z": utils.build_tensor_info(z)}) + return g, {"foo": foo_sig_def, "bar": bar_sig_def}, y + + +@parameterized.parameters((saved_model_builder.SavedModelBuilder,), + (saved_model_builder._SavedModelBuilder,)) +class SavedModelLoaderTest(test.TestCase, parameterized.TestCase): + + def export_simple_graph(self, builder_cls): + g, sig_def_map, _ = build_graph_helper() + with session.Session(graph=g) as sess: + self.evaluate(variables.global_variables_initializer()) + builder = builder_cls(SIMPLE_ADD_SAVED_MODEL) + builder.add_meta_graph_and_variables(sess, ["foo_graph"], sig_def_map) builder.save() - # Write SavedModel with a main_op + def export_graph_with_main_op(self, builder_cls): + g, sig_def_map, y = build_graph_helper() + with session.Session(graph=g) as sess: + self.evaluate(variables.global_variables_initializer()) assign_op = control_flow_ops.group(state_ops.assign(y, 7)) - builder = saved_model_builder.SavedModelBuilder(SAVED_MODEL_WITH_MAIN_OP) - builder.add_meta_graph_and_variables( - sess, ["foo_graph"], {"foo": foo_sig_def, "bar": bar_sig_def}, - main_op=assign_op) + builder = builder_cls(SAVED_MODEL_WITH_MAIN_OP) + + if builder_cls == saved_model_builder._SavedModelBuilder: + builder.add_meta_graph_and_variables( + sess, ["foo_graph"], sig_def_map, init_op=assign_op) + else: + builder.add_meta_graph_and_variables( + sess, ["foo_graph"], sig_def_map, main_op=assign_op) builder.save() def tearDown(self): - file_io.delete_recursively(test.get_temp_dir()) + super(SavedModelLoaderTest, self).tearDown() + shutil.rmtree(test.get_temp_dir(), ignore_errors=True) - def test_load_function(self): + @test_util.run_deprecated_v1 + def test_load_function(self, builder_cls): + self.export_simple_graph(builder_cls) loader = loader_impl.SavedModelLoader(SIMPLE_ADD_SAVED_MODEL) with self.session(graph=ops.Graph()) as sess: loader.load(sess, ["foo_graph"]) self.assertEqual(5, sess.graph.get_tensor_by_name("x:0").eval()) self.assertEqual(11, sess.graph.get_tensor_by_name("y:0").eval()) + self.export_graph_with_main_op(builder_cls) loader2 = loader_impl.SavedModelLoader(SAVED_MODEL_WITH_MAIN_OP) with self.session(graph=ops.Graph()) as sess: loader2.load(sess, ["foo_graph"]) self.assertEqual(5, sess.graph.get_tensor_by_name("x:0").eval()) self.assertEqual(7, sess.graph.get_tensor_by_name("y:0").eval()) - def test_load_graph(self): + @test_util.run_deprecated_v1 + def test_load_graph(self, builder_cls): + self.export_simple_graph(builder_cls) loader = loader_impl.SavedModelLoader(SIMPLE_ADD_SAVED_MODEL) graph = ops.Graph() loader.load_graph(graph, ["foo_graph"]) @@ -101,14 +123,16 @@ class SavedModelLoaderTest(test.TestCase): with self.assertRaises(KeyError): graph.get_tensor_by_name("z:0") - with self.session(graph=graph) as sess: + with self.session(graph=graph): # Check that x and y are not initialized with self.assertRaises(errors.FailedPreconditionError): - sess.run(x) + self.evaluate(x) with self.assertRaises(errors.FailedPreconditionError): - sess.run(y) + self.evaluate(y) - def test_load_with_import_scope(self): + @test_util.run_deprecated_v1 + def test_load_with_import_scope(self, builder_cls): + self.export_graph_with_main_op(builder_cls) loader = loader_impl.SavedModelLoader(SAVED_MODEL_WITH_MAIN_OP) with self.session(graph=ops.Graph()) as sess: saver, _ = loader.load_graph( @@ -119,7 +143,13 @@ class SavedModelLoaderTest(test.TestCase): loader.restore_variables(sess, tf_saver.Saver()) loader.restore_variables(sess, saver) - loader.run_init_ops(sess, ["foo_graph"]) + + if builder_cls == saved_model_builder._SavedModelBuilder: + with self.assertRaises(errors.NotFoundError): + loader.run_init_ops(sess, ["foo_graph"]) + loader.run_init_ops(sess, ["foo_graph"], import_scope="baz") + else: + loader.run_init_ops(sess, ["foo_graph"]) self.assertEqual(5, sess.graph.get_tensor_by_name("baz/x:0").eval()) self.assertEqual(7, sess.graph.get_tensor_by_name("baz/y:0").eval()) @@ -131,23 +161,27 @@ class SavedModelLoaderTest(test.TestCase): self.assertEqual(5, sess.graph.get_tensor_by_name("baa/x:0").eval()) self.assertEqual(7, sess.graph.get_tensor_by_name("baa/y:0").eval()) - def test_restore_variables(self): + @test_util.run_deprecated_v1 + def test_restore_variables(self, builder_cls): + self.export_graph_with_main_op(builder_cls) loader = loader_impl.SavedModelLoader(SAVED_MODEL_WITH_MAIN_OP) with self.session(graph=ops.Graph()) as sess: x = variables.VariableV1(0, name="x") y = variables.VariableV1(0, name="y") z = x * y - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) # There are variables to restore, so a saver must be created. with self.assertRaises(ValueError): loader.restore_variables(sess, None) loader.restore_variables(sess, tf_saver.Saver()) - self.assertEqual(55, z.eval()) + self.assertEqual(55, self.evaluate(z)) - def test_run_init_op(self): + @test_util.run_deprecated_v1 + def test_run_init_op(self, builder_cls): + self.export_graph_with_main_op(builder_cls) loader = loader_impl.SavedModelLoader(SAVED_MODEL_WITH_MAIN_OP) graph = ops.Graph() saver, _ = loader.load_graph(graph, ["foo_graph"]) @@ -160,14 +194,16 @@ class SavedModelLoaderTest(test.TestCase): self.assertEqual(5, sess.graph.get_tensor_by_name("x:0").eval()) self.assertEqual(7, sess.graph.get_tensor_by_name("y:0").eval()) - def test_parse_saved_model(self): + def test_parse_saved_model(self, builder_cls): + self.export_simple_graph(builder_cls) loader = loader_impl.SavedModelLoader(SIMPLE_ADD_SAVED_MODEL) meta_graph = loader.get_meta_graph_def_from_tags(["foo_graph"]) self.assertIsNotNone(meta_graph) self.assertIn("foo", meta_graph.signature_def) self.assertIn("bar", meta_graph.signature_def) - def test_load_invalid_meta_graph(self): + def test_load_invalid_meta_graph(self, builder_cls): + self.export_simple_graph(builder_cls) loader = loader_impl.SavedModelLoader(SIMPLE_ADD_SAVED_MODEL) with self.assertRaises(RuntimeError): loader.get_meta_graph_def_from_tags([]) @@ -176,13 +212,17 @@ class SavedModelLoaderTest(test.TestCase): with self.assertRaises(RuntimeError): loader.get_meta_graph_def_from_tags(["not_a_graph"]) - def test_load_saved_model_with_no_variables(self): + @test_util.run_deprecated_v1 + def test_load_saved_model_with_no_variables(self, builder_cls): """Test that SavedModel runs saver when there appear to be no variables. When no variables are detected, this may mean that the variables were saved to different collections, or the collections weren't saved to the SavedModel. If the SavedModel MetaGraphDef contains a saver, it should still run in either of these cases. + + Args: + builder_cls: SavedModelBuilder or _SavedModelBuilder class """ path = _get_export_dir("no_variable_saved_model") with session.Session(graph=ops.Graph()) as sess: @@ -192,7 +232,7 @@ class SavedModelLoaderTest(test.TestCase): 11, name="y", collections=["not_global_variable"]) self.assertFalse(variables._all_saveable_objects()) z = x + y - sess.run(variables.variables_initializer([x, y])) + self.evaluate(variables.variables_initializer([x, y])) foo_sig_def = signature_def_utils.build_signature_def( {"foo_input": utils.build_tensor_info(x)}, @@ -215,8 +255,9 @@ class SavedModelLoaderTest(test.TestCase): self.assertEqual(5, sess.graph.get_tensor_by_name("x:0").eval()) self.assertEqual(11, sess.graph.get_tensor_by_name("y:0").eval()) - def test_load_saved_model_graph_with_return_elements(self): + def test_load_saved_model_graph_with_return_elements(self, builder_cls): """Ensure that the correct elements are returned.""" + self.export_simple_graph(builder_cls) loader = loader_impl.SavedModelLoader(SIMPLE_ADD_SAVED_MODEL) graph = ops.Graph() _, ret = loader.load_graph(graph, ["foo_graph"], @@ -228,5 +269,6 @@ class SavedModelLoaderTest(test.TestCase): with self.assertRaisesRegexp(ValueError, "not found in graph"): loader.load_graph(graph, ["foo_graph"], return_elements=["z:0"]) + if __name__ == "__main__": test.main() diff --git a/tensorflow/python/saved_model/save.py b/tensorflow/python/saved_model/save.py index 63575f631e..a66e19b199 100644 --- a/tensorflow/python/saved_model/save.py +++ b/tensorflow/python/saved_model/save.py @@ -19,39 +19,82 @@ from __future__ import division from __future__ import print_function import collections +import functools import os +from tensorflow.core.protobuf import meta_graph_pb2 from tensorflow.core.protobuf import saved_model_pb2 from tensorflow.python.eager import context from tensorflow.python.eager import def_function -from tensorflow.python.eager import function +from tensorflow.python.eager import function as defun +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes from tensorflow.python.framework import meta_graph from tensorflow.python.framework import ops +from tensorflow.python.framework import tensor_spec from tensorflow.python.lib.io import file_io from tensorflow.python.ops import array_ops +from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import resource_variable_ops +from tensorflow.python.saved_model import builder_impl from tensorflow.python.saved_model import constants +from tensorflow.python.saved_model import saved_object_graph_pb2 from tensorflow.python.saved_model import signature_constants from tensorflow.python.saved_model import signature_def_utils +from tensorflow.python.saved_model import tag_constants from tensorflow.python.saved_model import utils_impl from tensorflow.python.training.checkpointable import base +from tensorflow.python.training.checkpointable import tracking from tensorflow.python.training.checkpointable import util from tensorflow.python.util import compat from tensorflow.python.util import nest from tensorflow.python.util.tf_export import tf_export +def _check_for_functional_keras_model(root): + """Makes an export signature for `root` if it's a functional Keras Model.""" + # If nothing is decorated yet but this is a functional Keras Model (duck + # typed), we'll try to make a signature ourselves. + try: + inputs = root.inputs + input_names = root.input_names + except AttributeError: + return None + input_signature = [] + for input_tensor, input_name in zip(inputs, input_names): + input_signature.append(tensor_spec.TensorSpec( + shape=input_tensor.shape, dtype=input_tensor.dtype, + name=input_name)) + + @def_function.function(input_signature=input_signature) + def _wrapped_model(*args): + outputs_list = nest.flatten(root(inputs=list(args))) + return {name: output for name, output + in zip(root.output_names, outputs_list)} + return _wrapped_model + + def _find_function_to_export(root): """Iterate over `root`'s attributes, finding traced functions.""" - functions = [] - function_attribute_names = [] + exported_function = None + previous_attribute_name = None for attribute_name in dir(root): attribute_value = getattr(root, attribute_name, None) if isinstance(attribute_value, def_function.PolymorphicFunction): - functions.append(attribute_value) - function_attribute_names.append(attribute_name) - # TODO(allenl): Automatically infer signatures for Keras functional models? - if not functions: + if exported_function is not None: + raise ValueError( + ("Exporting an object with no " + "tf.saved_model.save(..., signatures=...) " + "argument specified, and with more than one " + "@tf.function-decorated method attached to it: {}. The signature " + "keys for these functions are ambiguous. Specify signature " + "functions explicitly.").format( + [previous_attribute_name, attribute_name])) + exported_function = attribute_value + previous_attribute_name = attribute_name + if exported_function is None: + exported_function = _check_for_functional_keras_model(root) + if exported_function is None: raise ValueError( ("Exporting an object with no tf.saved_model.save(..., signatures=...) " "argument specified, and with no @tf.function-decorated methods " @@ -60,14 +103,7 @@ def _find_function_to_export(root): "signatures does not make sense, as the only consumers will expect " "signatures. Either decorate a method or specify a signature function " "explicitly.")) - elif len(functions) > 1: - raise ValueError( - ("Exporting an object with no tf.saved_model.save(..., signatures=...) " - "argument specified, and with more than one @tf.function-decorated " - "method attached to it: {}. The signature keys for these functions " - "are ambiguous. Specify signature functions explicitly.").format( - function_attribute_names)) - return functions[0] + return exported_function def _canonicalize_signatures(signatures): @@ -77,7 +113,7 @@ def _canonicalize_signatures(signatures): signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY: signatures} concrete_signatures = {} for serving_key, signature_function in signatures.items(): - if isinstance(signature_function, (function.PolymorphicFunction, + if isinstance(signature_function, (defun.PolymorphicFunction, def_function.PolymorphicFunction)): input_signature = signature_function._input_signature # pylint: disable=protected-access if input_signature is None: @@ -88,7 +124,7 @@ def _canonicalize_signatures(signatures): "converted to concrete functions using " "`f.get_concrete_function(...)`.").format(signature_function)) signature_function = signature_function.get_concrete_function() - elif not isinstance(signature_function, function.Function): + elif not isinstance(signature_function, defun.Function): raise ValueError( ("Expected a TensorFlow function to generate a signature for, but " "got {}. Python functions may be decorated with " @@ -145,64 +181,65 @@ def _tensor_dict_to_tensorinfo(tensor_dict): for key, value in tensor_dict.items()} -def _map_captured_resources_to_created_resources( +def _map_captures_to_created_tensors( original_captures, resource_map): - """Maps eager resources captured by a function to Graph resources for export. + """Maps eager tensors captured by a function to Graph resources for export. Args: - original_captures: A dictionary mapping from resource tensors captured by - the function to interior placeholders for those resources (inside the - function body). + original_captures: A dictionary mapping from tensors captured by the + function to interior placeholders for those tensors (inside the function + body). resource_map: A dictionary mapping from resource tensors owned by the eager context to resource tensors in the exported graph. Returns: - A dictionary mapping from interior placeholders in the function body to - exterior stand-in resource tensors which belong to the exported graph. + A list of stand-in tensors which belong to the exported graph, corresponding + to the function's captures. Raises: AssertionError: If the function references a resource which is not part of `resource_map`. """ - export_captures = {} + export_captures = [] for exterior, interior in original_captures.items(): mapped_resource = resource_map.get(exterior, None) if mapped_resource is None: - raise AssertionError( - ("Tried to export a function which references untracked stateful " - "object {}. Stateful TensorFlow objects (e.g. tf.Variable) must " - "be tracked by the main object. Objects may be tracked by " - "assigning them to an attribute of another tracked object, or to " - "an attribute of the main object directly.") - .format(interior)) - export_captures[interior] = mapped_resource + if exterior.dtype == dtypes.resource: + raise AssertionError( + ("Tried to export a function which references untracked stateful " + "object {}. Stateful TensorFlow objects (e.g. tf.Variable) must " + "be tracked by the main object. Objects may be tracked by " + "assigning them to an attribute of another tracked object, or to " + "an attribute of the main object directly.") + .format(interior)) + else: + # This is a captured Tensor, but it's not a resource. We'll just add it + # to the graph as a constant. + mapped_resource = constant_op.constant(exterior.numpy()) + export_captures.append(mapped_resource) return export_captures -def _map_function_inputs_to_created_inputs( - function_inputs, export_captures, signature_key, function_name): - """Creates exterior placeholders in the exported graph for function inputs. +def _map_function_arguments_to_created_inputs( + function_arguments, signature_key, function_name): + """Creates exterior placeholders in the exported graph for function arguments. Functions have two types of inputs: tensors captured from the outside (eager) context, and arguments to the function which we expect to receive from the - user at each call. `_map_captured_resources_to_created_resources` replaces + user at each call. `_map_captures_to_created_tensors` replaces captured tensors with stand-ins (typically these are resource dtype tensors associated with variables). `_map_function_inputs_to_created_inputs` runs over - every input, either captured or argument. For captures, it uses the mapped - resource from `export_captures`. For arguments, it creates a new placeholder - which will belong to the exported graph rather than the function body. + every argument, creating a new placeholder for each which will belong to the + exported graph rather than the function body. Args: - function_inputs: A list of all placeholders in the function body. - export_captures: A dictionary mapping from interior placeholders in the - function body to exterior stand-in resource tensors which belong to the - exported graph (see `_map_captured_resources_to_created_resources`). + function_arguments: A list of argument placeholders in the function body. signature_key: The name of the signature being exported, for error messages. function_name: The name of the function, for error messages. Returns: A tuple of (mapped_inputs, exterior_placeholders) - mapped_inputs: A list with entries corresponding to `function_inputs` + mapped_inputs: A list with entries corresponding to `function_arguments` containing all of the inputs of the function gathered from the exported graph (both captured resources and arguments). exterior_argument_placeholders: A dictionary mapping from argument names @@ -220,12 +257,7 @@ def _map_function_inputs_to_created_inputs( # MetaGraph. exterior_argument_placeholders = {} mapped_inputs = [] - for placeholder in function_inputs: - mapped_resource_tensor = export_captures.get(placeholder, None) - if mapped_resource_tensor is not None: - # This is a captured resource. - mapped_inputs.append(mapped_resource_tensor) - continue + for placeholder in function_arguments: # `export_captures` contains an exhaustive set of captures, so if we don't # find the input there then we now know we have an argument. user_input_name = compat.as_str_any( @@ -258,6 +290,20 @@ def _map_function_inputs_to_created_inputs( return mapped_inputs, exterior_argument_placeholders +def _call_function_with_mapped_captures(function, args, resource_map): + """Calls `function` in the exported graph, using mapped resource captures.""" + export_captures = _map_captures_to_created_tensors( + function.graph.captures, resource_map) + mapped_inputs = args + export_captures + # Calls the function quite directly, since we have new captured resource + # tensors we need to feed in which weren't part of the original function + # definition. + # pylint: disable=protected-access + outputs = function._build_call_outputs( + function._inference_function.call(context.context(), mapped_inputs)) + return outputs + + def _generate_signatures(signature_functions, resource_map): """Validates and calls `signature_functions` in the default graph. @@ -287,35 +333,77 @@ def _generate_signatures(signature_functions, resource_map): SignatureDefs as part of that MetaGraph. """ signatures = {} - for signature_key, func in sorted(signature_functions.items()): - # Register the inference function for this signature in the exported - # graph. There is no direct use for the gradient of this function, so we - # don't generate/register a gradient function here (but may end up with one - # if another function relies on it). Users can still take symbolic gradients - # of the function on import, the gradient just won't be in the saved - # graph. When exporting a signature which already computes gradients, this - # stops us from taking needless second-order gradients. - func.add_to_graph(register_gradient_functions=False) - export_captures = _map_captured_resources_to_created_resources( - func.graph.captures, resource_map) + for signature_key, function in sorted(signature_functions.items()): + if function.graph.captures: + argument_inputs = function.graph.inputs[:-len(function.graph.captures)] + else: + argument_inputs = function.graph.inputs mapped_inputs, exterior_argument_placeholders = ( - _map_function_inputs_to_created_inputs( - func.inputs, export_captures, signature_key, func.name)) - # Calls the function quite directly, since we have new captured resource - # tensors we need to feed in which weren't part of the original function - # definition. - # pylint: disable=protected-access + _map_function_arguments_to_created_inputs( + argument_inputs, signature_key, function.name)) outputs = _normalize_outputs( - func._build_call_outputs( - func._inference_function.call(context.context(), mapped_inputs)), - func.name, signature_key) - # pylint: enable=protected-access + _call_function_with_mapped_captures( + function, mapped_inputs, resource_map), + function.name, signature_key) signatures[signature_key] = signature_def_utils.build_signature_def( _tensor_dict_to_tensorinfo(exterior_argument_placeholders), _tensor_dict_to_tensorinfo(outputs)) return signatures +def _trace_resource_initializers(accessible_objects): + """Create concrete functions from `TrackableResource` objects.""" + resource_initializers = [] + + def _wrap_initializer(obj): + obj.initialize() + return constant_op.constant(1.) # Dummy control output + + for obj in accessible_objects: + if isinstance(obj, tracking.TrackableResource): + resource_initializers.append(def_function.function( + functools.partial(_wrap_initializer, obj), + # All inputs are captures. + input_signature=[]).get_concrete_function()) + return resource_initializers + + +_AssetInfo = collections.namedtuple( + "_AssetInfo", [ + # List of AssetFileDef protocol buffers + "asset_defs", + # Map from asset variable resource Tensors to their init ops + "asset_initializers_by_resource", + # Map from base asset filenames to full paths + "asset_filename_map"]) + + +def _process_asset(trackable_asset, asset_info, resource_map): + """Add `trackable_asset` to `asset_info` and `resource_map`.""" + original_variable = trackable_asset.asset_path + with context.eager_mode(): + original_path = original_variable.numpy() + path = builder_impl.get_asset_filename_to_add( + asset_filepath=original_path, + asset_filename_map=asset_info.asset_filename_map) + asset_variable = asset_info.asset_filename_map.get(path, None) + if asset_variable is None: + asset_path_initializer = array_ops.placeholder( + shape=original_variable.shape, + dtype=dtypes.string, + name="asset_path_initializer") + asset_variable = resource_variable_ops.ResourceVariable( + asset_path_initializer) + asset_info.asset_filename_map[path] = original_path + asset_def = meta_graph_pb2.AssetFileDef() + asset_def.filename = path + asset_def.tensor_info.name = asset_path_initializer.name + asset_info.asset_defs.append(asset_def) + asset_info.asset_initializers_by_resource[original_variable.handle] = ( + asset_variable.initializer) + resource_map[original_variable.handle] = asset_variable.handle + + def _map_resources(accessible_objects): """Makes new resource handle ops corresponding to existing resource tensors. @@ -329,34 +417,83 @@ def _map_resources(accessible_objects): to create replacements for. Returns: - A tuple of (object_map, resource_map): + A tuple of (object_map, resource_map, asset_info): object_map: A dictionary mapping from object in `accessible_objects` to replacement objects created to hold the new resource tensors. resource_map: A dictionary mapping from resource tensors extracted from `accessible_objects` to newly created resource tensors. + asset_info: An _AssetInfo tuple describing external assets referenced from + accessible_objects. """ - # TODO(allenl, rohanj): Map generic resources rather than just variables. # TODO(allenl): Handle MirroredVariables and other types of variables which # may need special casing. object_map = {} resource_map = {} + asset_info = _AssetInfo( + asset_defs=[], + asset_initializers_by_resource={}, + asset_filename_map={}) for obj in accessible_objects: - if resource_variable_ops.is_resource_variable(obj): + if isinstance(obj, tracking.TrackableResource): + new_resource = obj.create_resource() + resource_map[obj.resource_handle] = new_resource + elif resource_variable_ops.is_resource_variable(obj): new_variable = resource_variable_ops.copy_to_graph_uninitialized(obj) object_map[obj] = new_variable resource_map[obj.handle] = new_variable.handle - return object_map, resource_map + if isinstance(obj, tracking.TrackableAsset): + _process_asset(obj, asset_info, resource_map) + return object_map, resource_map, asset_info + + +def _fill_meta_graph_def(meta_graph_def, obj, signature_functions, + object_saver): + """Generates a MetaGraph which calls `signature_functions`. + Args: + meta_graph_def: The MetaGraphDef proto to fill. + obj: The checkpointable object being exported. + signature_functions: A dictionary mapping signature keys to concrete + functions containing signatures to add to the MetaGraph. + object_saver: A CheckpointableSaver to add to the MetaGraph. -def _make_graph_def(root, signature_functions, object_saver): - """Generates and exports call ops for `signature_functions`.""" + Returns: + asset_filename_map, a dictionary mapping from asset base names to + user-specified full asset paths, which should be copied to the SavedModel's + assets/ directory. + """ signatures = {} # List objects from the eager context to make sure Optimizers give us the # right Graph-dependent variables. - accessible_objects = util.list_objects(root) + accessible_objects = util.list_objects(obj) + resource_initializer_functions = _trace_resource_initializers( + accessible_objects) exported_graph = ops.Graph() + resource_initializer_ops = [] with exported_graph.as_default(): - object_map, resource_map = _map_resources(accessible_objects) + object_map, resource_map, asset_info = _map_resources(accessible_objects) + for resource_initializer_function in resource_initializer_functions: + asset_dependencies = [] + for capture in resource_initializer_function.graph.external_captures: + asset_initializer = asset_info.asset_initializers_by_resource.get( + capture, None) + if asset_initializer is not None: + asset_dependencies.append(asset_initializer) + with ops.control_dependencies(asset_dependencies): + resource_initializer_ops.append( + _call_function_with_mapped_captures( + resource_initializer_function, [], resource_map)) + with ops.control_dependencies(resource_initializer_ops): + init_op = control_flow_ops.no_op() + # Add the same op to the main_op collection and to the init_op + # signature. The collection is for compatibility with older loader APIs; + # only one will be executed. + meta_graph_def.collection_def[constants.MAIN_OP_KEY].node_list.value.append( + init_op.name) + meta_graph_def.signature_def[constants.INIT_OP_SIGNATURE_KEY].CopyFrom( + signature_def_utils.op_signature_def( + init_op, constants.INIT_OP_SIGNATURE_KEY)) + # Saving an object-based checkpoint again gathers variables. We need to do the # gathering from the eager context so Optimizers save the right set of # variables, but want any operations associated with the save/restore to be in @@ -365,11 +502,35 @@ def _make_graph_def(root, signature_functions, object_saver): with exported_graph.as_default(): signatures = _generate_signatures(signature_functions, resource_map) saver_def = saver.to_proto() + meta_graph_def.saver_def.CopyFrom(saver_def) graph_def = exported_graph.as_graph_def(add_shapes=True) # Clean reference cycles so repeated export()s don't make work for the garbage # collector. ops.dismantle_graph(exported_graph) - return graph_def, signatures, saver_def + + meta_graph_def.graph_def.CopyFrom(graph_def) + meta_graph_def.meta_info_def.tags.append(tag_constants.SERVING) + meta_graph_def.asset_file_def.extend(asset_info.asset_defs) + for signature_key, signature in signatures.items(): + meta_graph_def.signature_def[signature_key].CopyFrom(signature) + meta_graph.strip_graph_default_valued_attrs(meta_graph_def) + return asset_info.asset_filename_map + + +def _write_object_graph(obj, export_dir): + """Save a SavedObjectGraph proto for `obj`.""" + # SavedObjectGraph is similar to the CheckpointableObjectGraph proto in the + # checkpoint. It will eventually go into the SavedModel. + object_proto = util.make_object_graph_without_attributes( + obj, proto=saved_object_graph_pb2.SavedObjectGraph()) + extra_asset_dir = os.path.join( + compat.as_bytes(export_dir), + compat.as_bytes(constants.EXTRA_ASSETS_DIRECTORY)) + file_io.recursive_create_dir(extra_asset_dir) + object_graph_filename = os.path.join( + extra_asset_dir, compat.as_bytes("object_graph.pb")) + file_io.write_string_to_file(object_graph_filename, + object_proto.SerializeToString()) @tf_export("saved_model.save", v1=["saved_model.experimental.save"]) @@ -450,6 +611,19 @@ def save(obj, export_dir, signatures=None): tf.TensorSpec(shape=[None, 3], dtype=tf.float32, name="inp"))) ``` + `tf.keras.Model` instances constructed from inputs and outputs already have a + signature and so do not require a `@tf.function` decorator or a `signatures` + argument. If neither are specified, the model's forward pass is exported. + + ```python + x = input_layer.Input((4,), name="x") + y = core.Dense(5, name="out")(x) + model = training.Model(x, y) + tf.saved_model.save(model, '/tmp/saved_model/') + # The exported SavedModel takes "x" with shape [None, 4] and returns "out" + # with shape [None, 5] + ``` + Variables must be tracked by assigning them to an attribute of a tracked object or to an attribute of `obj` directly. TensorFlow objects (e.g. layers from `tf.keras.layers`, optimizers from `tf.train`) track their variables @@ -515,26 +689,26 @@ def save(obj, export_dir, signatures=None): # Note that we run this before saving the checkpoint, since looping over # attributes may have the side effect of creating variables in some cases. signatures = _find_function_to_export(obj) - object_saver = util.CheckpointableSaver(obj) - utils_impl.get_or_create_variables_dir(export_dir) - object_saver.save(utils_impl.get_variables_path(export_dir)) signatures = _canonicalize_signatures(signatures) - graph_def, signatures, saver_def = _make_graph_def( - obj, signatures, object_saver) - saved_model = saved_model_pb2.SavedModel() - saved_model.saved_model_schema_version = ( - constants.SAVED_MODEL_SCHEMA_VERSION) - meta_graph_def = saved_model.meta_graphs.add() - meta_graph_def.saver_def.CopyFrom(saver_def) # TODO(allenl): Factor out some subset of SavedModelBuilder which is 2.x # compatible (no sessions) and share it with this export API rather than # making a SavedModel proto and writing it directly. - meta_graph_def.graph_def.MergeFrom(graph_def) - for signature_key, signature in signatures.items(): - meta_graph_def.signature_def[signature_key].MergeFrom(signature) - meta_graph.strip_graph_default_valued_attrs(meta_graph_def) + saved_model = saved_model_pb2.SavedModel() + meta_graph_def = saved_model.meta_graphs.add() + object_saver = util.CheckpointableSaver(obj) + asset_filename_map = _fill_meta_graph_def( + meta_graph_def, obj, signatures, object_saver) + saved_model.saved_model_schema_version = ( + constants.SAVED_MODEL_SCHEMA_VERSION) + # So far we've just been generating protocol buffers with no I/O. Now we write + # the checkpoint, copy assets into the assets directory, and write out the + # SavedModel proto itself. + utils_impl.get_or_create_variables_dir(export_dir) + object_saver.save(utils_impl.get_variables_path(export_dir)) + builder_impl.copy_assets_to_destination_dir(asset_filename_map, export_dir) path = os.path.join( compat.as_bytes(export_dir), compat.as_bytes(constants.SAVED_MODEL_FILENAME_PB)) file_io.write_string_to_file(path, saved_model.SerializeToString()) + _write_object_graph(obj, export_dir) diff --git a/tensorflow/python/saved_model/save_test.py b/tensorflow/python/saved_model/save_test.py index 42ff508b38..9e5b9b9717 100644 --- a/tensorflow/python/saved_model/save_test.py +++ b/tensorflow/python/saved_model/save_test.py @@ -21,6 +21,9 @@ from __future__ import print_function import os import sys +import numpy + +from tensorflow.python.client import session as session_lib from tensorflow.python.eager import backprop from tensorflow.python.eager import def_function from tensorflow.python.eager import test @@ -29,13 +32,19 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_spec from tensorflow.python.framework import test_util +from tensorflow.python.keras.engine import input_layer from tensorflow.python.keras.engine import training from tensorflow.python.keras.layers import core +from tensorflow.python.keras.layers import merge +from tensorflow.python.lib.io import file_io +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import lookup_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import variables from tensorflow.python.saved_model import loader from tensorflow.python.saved_model import save from tensorflow.python.saved_model import signature_constants +from tensorflow.python.saved_model import tag_constants from tensorflow.python.training import adam from tensorflow.python.training.checkpointable import tracking from tensorflow.python.training.checkpointable import util @@ -60,26 +69,27 @@ class _ModelWithOptimizer(training.Model): return {"loss": loss} -class SaveTest(test.TestCase): +def _import_and_infer( + save_dir, inputs, + signature_key=signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY): + """Import a SavedModel into a TF 1.x-style graph and run `signature_key`.""" + graph = ops.Graph() + with graph.as_default(), session_lib.Session() as session: + model = loader.load(session, [tag_constants.SERVING], save_dir) + signature = model.signature_def[signature_key] + assert set(inputs.keys()) == set(signature.inputs.keys()) + feed_dict = {} + for arg_name in inputs.keys(): + feed_dict[graph.get_tensor_by_name(signature.inputs[arg_name].name)] = ( + inputs[arg_name]) + output_dict = {} + for output_name, output_tensor_info in signature.outputs.items(): + output_dict[output_name] = graph.get_tensor_by_name( + output_tensor_info.name) + return session.run(output_dict, feed_dict=feed_dict) - def _import_and_infer( - self, save_dir, inputs, - signature_key=signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY): - """Import a SavedModel into a TF 1.x-style graph and run `signature_key`.""" - graph = ops.Graph() - with graph.as_default(), self.session(graph) as session: - model = loader.load(session, [], save_dir) - signature = model.signature_def[signature_key] - self.assertEqual(set(inputs.keys()), set(signature.inputs.keys())) - feed_dict = {} - for arg_name in inputs.keys(): - feed_dict[graph.get_tensor_by_name(signature.inputs[arg_name].name)] = ( - inputs[arg_name]) - output_dict = {} - for output_name, output_tensor_info in signature.outputs.items(): - output_dict[output_name] = graph.get_tensor_by_name( - output_tensor_info.name) - return session.run(output_dict, feed_dict=feed_dict) + +class SaveTest(test.TestCase): def test_method_save_signature(self): root = tracking.Checkpointable() @@ -91,7 +101,7 @@ class SaveTest(test.TestCase): save.save(root, save_dir, root.f) self.assertEqual( {"output_0": 2.}, - self._import_and_infer(save_dir, {"x": 1.})) + _import_and_infer(save_dir, {"x": 1.})) def test_method_save_concrete(self): root = tracking.Checkpointable() @@ -106,7 +116,7 @@ class SaveTest(test.TestCase): tensor_spec.TensorSpec(None, dtypes.float32))}) self.assertEqual( {"out": 2.}, - self._import_and_infer( + _import_and_infer( save_dir, {"z": 1.}, signature_key="non_default_key")) def test_non_concrete_error(self): @@ -163,7 +173,7 @@ class SaveTest(test.TestCase): save_dir = os.path.join(self.get_temp_dir(), "saved_model") save.save(root, save_dir, to_save) self.assertAllEqual({"output_0": 12.}, - self._import_and_infer(save_dir, {"x": 2.})) + _import_and_infer(save_dir, {"x": 2.})) def test_optimizer(self): x = constant_op.constant([[3., 4.]]) @@ -176,7 +186,7 @@ class SaveTest(test.TestCase): self.assertNotEqual(first_loss, second_loss) self.assertAllClose( second_loss, - self._import_and_infer(save_dir, {"x": [[3., 4.]], "y": [2.]})) + _import_and_infer(save_dir, {"x": [[3., 4.]], "y": [2.]})) def test_trivial_save_exception(self): save_dir = os.path.join(self.get_temp_dir(), "saved_model") @@ -191,8 +201,8 @@ class SaveTest(test.TestCase): save_dir = os.path.join(self.get_temp_dir(), "saved_model") save.save(model, save_dir) self.assertIn("loss", - self._import_and_infer(save_dir, - {"x": [[3., 4.]], "y": [2.]})) + _import_and_infer(save_dir, + {"x": [[3., 4.]], "y": [2.]})) def test_single_function_default_signature(self): model = tracking.Checkpointable() @@ -201,7 +211,7 @@ class SaveTest(test.TestCase): save_dir = os.path.join(self.get_temp_dir(), "saved_model") save.save(model, save_dir) self.assertAllClose({"output_0": 3.}, - self._import_and_infer(save_dir, {})) + _import_and_infer(save_dir, {})) def test_ambiguous_signatures(self): model = _ModelWithOptimizer() @@ -213,6 +223,19 @@ class SaveTest(test.TestCase): with self.assertRaisesRegexp(ValueError, "call.*second_function"): save.save(model, save_dir) + def test_subclassed_no_signature(self): + + class Subclassed(training.Model): + + def call(self, inputs): + return inputs * 2. + + save_dir = os.path.join(self.get_temp_dir(), "saved_model") + model = Subclassed() + with self.assertRaisesRegexp( + ValueError, "no @tf.function-decorated methods"): + save.save(model, save_dir) + def test_docstring(self): class Adder(util.Checkpoint): @@ -227,7 +250,7 @@ class SaveTest(test.TestCase): save_dir = os.path.join(self.get_temp_dir(), "saved_model") save.save(to_save, save_dir) self.assertAllClose({"output_0": 7.}, - self._import_and_infer(save_dir, {"x": 3.})) + _import_and_infer(save_dir, {"x": 3.})) def test_default_attr_stripping(self): @@ -246,13 +269,90 @@ class SaveTest(test.TestCase): save.save(to_save, save_dir) graph = ops.Graph() with graph.as_default(), self.session(graph) as session: - loader.load(session, [], save_dir) + loader.load(session, [tag_constants.SERVING], save_dir) func, = graph._functions.values() complex_node, = [ node for node in func.definition.node_def if node.op == "Complex"] self.assertNotIn("T", complex_node.attr) self.assertNotIn("Tout", complex_node.attr) + def test_export_functional_keras_model(self): + x = input_layer.Input((4,), name="x") + y = core.Dense(4, name="out")(x) + model = training.Model(x, y) + save_dir = os.path.join(self.get_temp_dir(), "saved_model") + save.save(model, save_dir) + self.assertAllClose( + {"out": model(array_ops.ones([1, 4]))}, + _import_and_infer(save_dir, {"x": [[1., 1., 1., 1.]]})) + + @test_util.run_deprecated_v1 + def test_export_functional_keras_model_after_fit(self): + x = input_layer.Input((1,)) + y = core.Dense(1, name="y")(x) + model = training.Model(x, y) + model.compile(optimizer="sgd", loss="mse") + model.fit(x=numpy.array([[1.]]), + y=numpy.array([2.]), epochs=2) + save_dir = os.path.join(self.get_temp_dir(), "saved_model") + save.save(model, save_dir) + self.assertAllClose( + {"y": model(constant_op.constant([[1.], [2.]]))}, + _import_and_infer(save_dir, {"input_1": [[1.], [2.]]})) + + def test_export_multi_input_functional_keras_model(self): + x1 = input_layer.Input((2,), name="x1") + x2 = input_layer.Input((2,), name="x2") + y1 = core.Dense(4)(merge.Add()([x1, x2])) + y2 = core.Dense(4)(merge.Multiply()([x1, x2])) + model = training.Model([x1, x2], [y1, y2]) + save_dir = os.path.join(self.get_temp_dir(), "saved_model") + save.save(model, save_dir) + outputs = model([array_ops.ones([1, 2]), 2. * array_ops.ones([1, 2])]) + self.assertAllClose( + {"dense": outputs[0], "dense_1": outputs[1]}, + _import_and_infer( + save_dir, + {"x1": [[1., 1.]], + "x2": [[2., 2.]]})) + + +class AssetTests(test.TestCase): + + def setUp(self): + super(AssetTests, self).setUp() + self._vocab_path = os.path.join(self.get_temp_dir(), "vocab.txt") + with open(self._vocab_path, "w") as f: + f.write("alpha\nbeta\ngamma\n") + + def test_table(self): + initializer = lookup_ops.TextFileInitializer( + self._vocab_path, + key_dtype=dtypes.string, + key_index=lookup_ops.TextFileIndex.WHOLE_LINE, + value_dtype=dtypes.int64, + value_index=lookup_ops.TextFileIndex.LINE_NUMBER) + root = util.Checkpoint(table=lookup_ops.HashTable( + initializer, default_value=-1)) + root.table_user = def_function.function( + root.table.lookup, + input_signature=[tensor_spec.TensorSpec(None, dtypes.string)]) + self.assertEqual( + 2, + self.evaluate(root.table_user(constant_op.constant("gamma")))) + save_dir = os.path.join(self.get_temp_dir(), "saved_model") + save.save(root, save_dir) + file_io.delete_file(self._vocab_path) + self.assertAllClose( + {"output_0": [2, 0]}, + _import_and_infer(save_dir, {"keys": ["gamma", "alpha"]})) + second_dir = os.path.join(self.get_temp_dir(), "second_dir") + # Asset paths should track the location the SavedModel is loaded from. + file_io.rename(save_dir, second_dir) + self.assertAllClose( + {"output_0": [2, 1]}, + _import_and_infer(second_dir, {"keys": ["gamma", "beta"]})) + class MemoryTests(test.TestCase): diff --git a/tensorflow/python/saved_model/saved_model_test.py b/tensorflow/python/saved_model/saved_model_test.py index 5d6167ab38..0f18fb1a01 100644 --- a/tensorflow/python/saved_model/saved_model_test.py +++ b/tensorflow/python/saved_model/saved_model_test.py @@ -54,15 +54,15 @@ def tearDownModule(): file_io.delete_recursively(test.get_temp_dir()) -class SavedModelTest(test.TestCase): +class SavedModelTestBase(test.TestCase): def _get_export_dir(self, label): return os.path.join(test.get_temp_dir(), label) def _init_and_validate_variable(self, sess, variable_name, variable_value): v = variables.VariableV1(variable_value, name=variable_name) - sess.run(variables.global_variables_initializer()) - self.assertEqual(variable_value, v.eval()) + self.evaluate(variables.global_variables_initializer()) + self.assertEqual(variable_value, self.evaluate(v)) def _build_asset_collection(self, asset_file_name, asset_file_contents, asset_file_tensor_name, asset_subdir=""): @@ -78,14 +78,16 @@ class SavedModelTest(test.TestCase): asset_collection = ops.get_collection(ops.GraphKeys.ASSET_FILEPATHS) return asset_collection - def _validate_asset_collection(self, export_dir, graph_collection_def, - expected_asset_file_name, - expected_asset_file_contents, - expected_asset_tensor_name, - asset_id=0): - assets_any = graph_collection_def[constants.ASSETS_KEY].any_list.value - asset = meta_graph_pb2.AssetFileDef() - assets_any[asset_id].Unpack(asset) + +class SavedModelTest(SavedModelTestBase): + + def _validate_assets(self, + export_dir, + asset_file_def, + expected_asset_file_name, + expected_asset_file_contents, + expected_asset_tensor_name, + asset_id=0): assets_path = os.path.join( compat.as_bytes(export_dir), compat.as_bytes(constants.ASSETS_DIRECTORY), @@ -93,8 +95,10 @@ class SavedModelTest(test.TestCase): actual_asset_contents = file_io.read_file_to_string(assets_path) self.assertEqual(expected_asset_file_contents, compat.as_text(actual_asset_contents)) - self.assertEqual(expected_asset_file_name, asset.filename) - self.assertEqual(expected_asset_tensor_name, asset.tensor_info.name) + self.assertEqual(expected_asset_file_name, + asset_file_def[asset_id].filename) + self.assertEqual(expected_asset_tensor_name, + asset_file_def[asset_id].tensor_info.name) def _validate_inputs_tensor_info_fail(self, builder, tensor_info): with self.session(graph=ops.Graph()) as sess: @@ -142,6 +146,18 @@ class SavedModelTest(test.TestCase): sess, ["foo"], signature_def_map={"foo_key": foo_signature}) + def _validate_sig_def_keys(self, builder, valid_tensor_info, invalid_key): + with self.session(graph=ops.Graph()) as sess: + self._init_and_validate_variable(sess, "v", 42) + + foo_signature = signature_def_utils.build_signature_def( + dict(), {"foo_key": valid_tensor_info}, "foo") + self.assertRaises( + KeyError, + builder.add_meta_graph_and_variables, + sess, ["foo"], + signature_def_map={invalid_key: foo_signature}) + def testMaybeSavedModelDir(self): base_path = test.test_src_dir_path("/python/saved_model") self.assertFalse(loader.maybe_saved_model_directory(base_path)) @@ -183,9 +199,10 @@ class SavedModelTest(test.TestCase): constants.SAVED_MODEL_FILENAME_PBTXT): loader.load(sess, ["foo"], export_dir) + @test_util.run_deprecated_v1 def testVerifySessionGraphUsage(self): export_dir = self._get_export_dir("test_verify_session_graph_usage") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) with self.session(graph=ops.Graph()) as sess: self._init_and_validate_variable(sess, "v", 42) @@ -203,9 +220,10 @@ class SavedModelTest(test.TestCase): self.assertEqual( 42, ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES)[0].eval()) + @test_util.run_deprecated_v1 def testSequence(self): export_dir = self._get_export_dir("test_sequence") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) # Expect an assertion error since add_meta_graph_and_variables() should be # invoked before any add_meta_graph() calls. @@ -220,9 +238,10 @@ class SavedModelTest(test.TestCase): self.assertRaises(AssertionError, builder.add_meta_graph_and_variables, sess, ["baz"]) + @test_util.run_deprecated_v1 def testTags(self): export_dir = self._get_export_dir("test_tags") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) # Graph with a single variable. SavedModel invoked to: # - add with weights. @@ -309,9 +328,10 @@ class SavedModelTest(test.TestCase): self.assertRaises(RuntimeError, loader.load, sess, ["foo", "baz"], export_dir) + @test_util.run_deprecated_v1 def testVariables(self): export_dir = self._get_export_dir("test_variables") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) # Graph with two variables. SavedModel invoked to: # - add with weights. @@ -361,9 +381,10 @@ class SavedModelTest(test.TestCase): self.assertRaises(errors.NotFoundError, loader.load, sess, ["baz"], export_dir) + @test_util.run_deprecated_v1 def testGraphWithoutVariables(self): export_dir = self._get_export_dir("test_graph_has_variables") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) # Graph with no variables. with self.session(graph=ops.Graph()) as sess: @@ -385,7 +406,7 @@ class SavedModelTest(test.TestCase): a = ops.get_default_graph().get_tensor_by_name(constant_5_name) b = constant_op.constant(6.0) c = a * b - self.assertEqual(30.0, sess.run(c)) + self.assertEqual(30.0, self.evaluate(c)) # Restore the graph with tag "bar". with self.session(graph=ops.Graph()) as sess: @@ -394,11 +415,12 @@ class SavedModelTest(test.TestCase): a = ops.get_default_graph().get_tensor_by_name(constant_6_name) b = constant_op.constant(5.0) c = a * b - self.assertEqual(30.0, sess.run(c)) + self.assertEqual(30.0, self.evaluate(c)) + @test_util.run_deprecated_v1 def testNoOverwrite(self): export_dir = self._get_export_dir("test_no_overwrite") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) # Graph with a single variable. SavedModel invoked to: # - add with weights. @@ -417,12 +439,13 @@ class SavedModelTest(test.TestCase): # An attempt to create another builder with the same export directory should # result in an assertion error. - self.assertRaises(AssertionError, saved_model_builder.SavedModelBuilder, + self.assertRaises(AssertionError, saved_model_builder._SavedModelBuilder, export_dir) + @test_util.run_deprecated_v1 def testSaveAsText(self): export_dir = self._get_export_dir("test_astext") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) # Graph with a single variable. SavedModel invoked to: # - add with weights. @@ -451,17 +474,18 @@ class SavedModelTest(test.TestCase): self.assertEqual( 42, ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES)[0].eval()) + @test_util.run_deprecated_v1 def testCollections(self): export_dir = self._get_export_dir("test_collections") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) # Graph with a single variable added to a collection. SavedModel invoked to: # - add with weights. with self.session(graph=ops.Graph()) as sess: v = variables.VariableV1(42, name="v") ops.add_to_collection("foo_vars", v) - sess.run(variables.global_variables_initializer()) - self.assertEqual(42, v.eval()) + self.evaluate(variables.global_variables_initializer()) + self.assertEqual(42, self.evaluate(v)) builder.add_meta_graph_and_variables(sess, ["foo"]) # Graph with the same single variable added to a different collection. @@ -470,8 +494,8 @@ class SavedModelTest(test.TestCase): with self.session(graph=ops.Graph()) as sess: v = variables.VariableV1(43, name="v") ops.add_to_collection("bar_vars", v) - sess.run(variables.global_variables_initializer()) - self.assertEqual(43, v.eval()) + self.evaluate(variables.global_variables_initializer()) + self.assertEqual(43, self.evaluate(v)) builder.add_meta_graph(["bar"]) # Save the SavedModel to disk. @@ -501,9 +525,10 @@ class SavedModelTest(test.TestCase): self.assertEqual(len(ops.get_collection("foo_vars")), 0) + @test_util.run_deprecated_v1 def testSignatureDefs(self): export_dir = self._get_export_dir("test_signature_defs") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) # Graph with a single variable and a single entry in the signature def map. # SavedModel is invoked to add with weights. @@ -563,7 +588,7 @@ class SavedModelTest(test.TestCase): def testSignatureDefValidationFails(self): export_dir = self._get_export_dir("test_signature_def_validation_fail") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) tensor_without_encoding = meta_graph_pb2.TensorInfo() tensor_without_encoding.dtype = types_pb2.DT_FLOAT @@ -579,19 +604,30 @@ class SavedModelTest(test.TestCase): self._validate_inputs_tensor_info_fail(builder, tensor_empty) self._validate_outputs_tensor_info_fail(builder, tensor_empty) + valid_tensor_info = meta_graph_pb2.TensorInfo() + valid_tensor_info.name = "foo" + valid_tensor_info.dtype = types_pb2.DT_FLOAT + + self._validate_sig_def_keys(builder, valid_tensor_info, + constants.INIT_OP_SIGNATURE_KEY) + self._validate_sig_def_keys(builder, valid_tensor_info, + constants.TRAIN_OP_SIGNATURE_KEY) + + @test_util.run_deprecated_v1 def testSignatureDefValidationSucceedsWithName(self): tensor_with_name = meta_graph_pb2.TensorInfo() tensor_with_name.name = "foo" tensor_with_name.dtype = types_pb2.DT_FLOAT export_dir = self._get_export_dir("test_signature_def_validation_name_1") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) self._validate_inputs_tensor_info_accept(builder, tensor_with_name) export_dir = self._get_export_dir("test_signature_def_validation_name_2") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) self._validate_outputs_tensor_info_accept(builder, tensor_with_name) + @test_util.run_deprecated_v1 def testSignatureDefValidationSucceedsWithCoo(self): tensor_with_coo = meta_graph_pb2.TensorInfo() # TODO(soergel) test validation of each of the fields of coo_sparse @@ -599,16 +635,17 @@ class SavedModelTest(test.TestCase): tensor_with_coo.dtype = types_pb2.DT_FLOAT export_dir = self._get_export_dir("test_signature_def_validation_coo_1") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) self._validate_inputs_tensor_info_accept(builder, tensor_with_coo) export_dir = self._get_export_dir("test_signature_def_validation_coo_2") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) self._validate_outputs_tensor_info_accept(builder, tensor_with_coo) + @test_util.run_deprecated_v1 def testAssets(self): export_dir = self._get_export_dir("test_assets") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) with self.session(graph=ops.Graph()) as sess: self._init_and_validate_variable(sess, "v", 42) @@ -618,145 +655,151 @@ class SavedModelTest(test.TestCase): compat.as_bytes(test.get_temp_dir()), compat.as_bytes("ignored.txt")) file_io.write_string_to_file(ignored_filepath, "will be ignored") - asset_collection = self._build_asset_collection("hello42.txt", - "foo bar baz", - "asset_file_tensor") + asset_list = self._build_asset_collection("hello42.txt", "foo bar baz", + "asset_file_tensor") builder.add_meta_graph_and_variables( - sess, ["foo"], assets_collection=asset_collection) + sess, ["foo"], assets_list=asset_list) # Save the SavedModel to disk. builder.save() with self.session(graph=ops.Graph()) as sess: foo_graph = loader.load(sess, ["foo"], export_dir) - self._validate_asset_collection(export_dir, foo_graph.collection_def, - "hello42.txt", "foo bar baz", - "asset_file_tensor:0") + self._validate_assets(export_dir, foo_graph.asset_file_def, "hello42.txt", + "foo bar baz", "asset_file_tensor:0") ignored_asset_path = os.path.join( compat.as_bytes(export_dir), compat.as_bytes(constants.ASSETS_DIRECTORY), compat.as_bytes("ignored.txt")) self.assertFalse(file_io.file_exists(ignored_asset_path)) + @test_util.run_deprecated_v1 def testAssetsNameCollisionDiffFile(self): export_dir = self._get_export_dir("test_assets_name_collision_diff_file") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) with self.session(graph=ops.Graph()) as sess: self._init_and_validate_variable(sess, "v", 42) - asset_collection = self._build_asset_collection( - "hello42.txt", "foo bar bak", "asset_file_tensor", - asset_subdir="1") + asset_list = self._build_asset_collection( + "hello42.txt", "foo bar bak", "asset_file_tensor", asset_subdir="1") - asset_collection = self._build_asset_collection( - "hello42.txt", "foo bar baz", "asset_file_tensor_1", - asset_subdir="2") + asset_list = self._build_asset_collection( + "hello42.txt", "foo bar baz", "asset_file_tensor_1", asset_subdir="2") builder.add_meta_graph_and_variables( - sess, ["foo"], assets_collection=asset_collection) + sess, ["foo"], assets_list=asset_list) # Save the SavedModel to disk. builder.save() with self.session(graph=ops.Graph()) as sess: foo_graph = loader.load(sess, ["foo"], export_dir) - self._validate_asset_collection(export_dir, foo_graph.collection_def, - "hello42.txt", "foo bar bak", - "asset_file_tensor:0") - self._validate_asset_collection(export_dir, foo_graph.collection_def, - "hello42.txt_1", "foo bar baz", - "asset_file_tensor_1:0", - asset_id=1) - + self._validate_assets(export_dir, foo_graph.asset_file_def, "hello42.txt", + "foo bar bak", "asset_file_tensor:0") + self._validate_assets( + export_dir, + foo_graph.asset_file_def, + "hello42.txt_1", + "foo bar baz", + "asset_file_tensor_1:0", + asset_id=1) + + @test_util.run_deprecated_v1 def testAssetsNameCollisionSameFilepath(self): export_dir = self._get_export_dir("test_assets_name_collision_same_path") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) with self.session(graph=ops.Graph()) as sess: self._init_and_validate_variable(sess, "v", 42) - asset_collection = self._build_asset_collection( - "hello42.txt", "foo bar baz", "asset_file_tensor") + asset_list = self._build_asset_collection("hello42.txt", "foo bar baz", + "asset_file_tensor") - asset_collection = self._build_asset_collection( - "hello42.txt", "foo bar baz", "asset_file_tensor_1") + asset_list = self._build_asset_collection("hello42.txt", "foo bar baz", + "asset_file_tensor_1") builder.add_meta_graph_and_variables( - sess, ["foo"], assets_collection=asset_collection) + sess, ["foo"], assets_list=asset_list) # Save the SavedModel to disk. builder.save() with self.session(graph=ops.Graph()) as sess: foo_graph = loader.load(sess, ["foo"], export_dir) - self._validate_asset_collection(export_dir, foo_graph.collection_def, - "hello42.txt", "foo bar baz", - "asset_file_tensor:0") + self._validate_assets(export_dir, foo_graph.asset_file_def, "hello42.txt", + "foo bar baz", "asset_file_tensor:0") # The second tensor should be recorded, but the same. - self._validate_asset_collection(export_dir, foo_graph.collection_def, - "hello42.txt", "foo bar baz", - "asset_file_tensor_1:0", - asset_id=1) + self._validate_assets( + export_dir, + foo_graph.asset_file_def, + "hello42.txt", + "foo bar baz", + "asset_file_tensor_1:0", + asset_id=1) ignored_asset_path = os.path.join( compat.as_bytes(export_dir), compat.as_bytes(constants.ASSETS_DIRECTORY), compat.as_bytes("hello42.txt_1")) self.assertFalse(file_io.file_exists(ignored_asset_path)) + @test_util.run_deprecated_v1 def testAssetsNameCollisionSameFile(self): export_dir = self._get_export_dir("test_assets_name_collision_same_file") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) with self.session(graph=ops.Graph()) as sess: self._init_and_validate_variable(sess, "v", 42) - asset_collection = self._build_asset_collection( - "hello42.txt", "foo bar baz", "asset_file_tensor", - asset_subdir="1") + asset_list = self._build_asset_collection( + "hello42.txt", "foo bar baz", "asset_file_tensor", asset_subdir="1") - asset_collection = self._build_asset_collection( - "hello42.txt", "foo bar baz", "asset_file_tensor_1", - asset_subdir="2") + asset_list = self._build_asset_collection( + "hello42.txt", "foo bar baz", "asset_file_tensor_1", asset_subdir="2") builder.add_meta_graph_and_variables( - sess, ["foo"], assets_collection=asset_collection) + sess, ["foo"], assets_list=asset_list) # Save the SavedModel to disk. builder.save() with self.session(graph=ops.Graph()) as sess: foo_graph = loader.load(sess, ["foo"], export_dir) - self._validate_asset_collection(export_dir, foo_graph.collection_def, - "hello42.txt", "foo bar baz", - "asset_file_tensor:0") + self._validate_assets(export_dir, foo_graph.asset_file_def, "hello42.txt", + "foo bar baz", "asset_file_tensor:0") # The second tensor should be recorded, but the same. - self._validate_asset_collection(export_dir, foo_graph.collection_def, - "hello42.txt", "foo bar baz", - "asset_file_tensor_1:0", - asset_id=1) + self._validate_assets( + export_dir, + foo_graph.asset_file_def, + "hello42.txt", + "foo bar baz", + "asset_file_tensor_1:0", + asset_id=1) ignored_asset_path = os.path.join( compat.as_bytes(export_dir), compat.as_bytes(constants.ASSETS_DIRECTORY), compat.as_bytes("hello42.txt_1")) self.assertFalse(file_io.file_exists(ignored_asset_path)) + @test_util.run_deprecated_v1 def testAssetsNameCollisionManyFiles(self): export_dir = self._get_export_dir("test_assets_name_collision_many_files") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) with self.session(graph=ops.Graph()) as sess: self._init_and_validate_variable(sess, "v", 42) for i in range(5): idx = str(i) - asset_collection = self._build_asset_collection( - "hello42.txt", "foo bar baz " + idx, "asset_file_tensor_" + idx, + asset_list = self._build_asset_collection( + "hello42.txt", + "foo bar baz " + idx, + "asset_file_tensor_" + idx, asset_subdir=idx) builder.add_meta_graph_and_variables( - sess, ["foo"], assets_collection=asset_collection) + sess, ["foo"], assets_list=asset_list) # Save the SavedModel to disk. builder.save() @@ -765,18 +808,20 @@ class SavedModelTest(test.TestCase): foo_graph = loader.load(sess, ["foo"], export_dir) for i in range(1, 5): idx = str(i) - self._validate_asset_collection( - export_dir, foo_graph.collection_def, "hello42.txt_" + idx, - "foo bar baz " + idx, "asset_file_tensor_{}:0".format(idx), + self._validate_assets( + export_dir, + foo_graph.asset_file_def, + "hello42.txt_" + idx, + "foo bar baz " + idx, + "asset_file_tensor_{}:0".format(idx), asset_id=i) - self._validate_asset_collection(export_dir, foo_graph.collection_def, - "hello42.txt", "foo bar baz 0", - "asset_file_tensor_0:0") + self._validate_assets(export_dir, foo_graph.asset_file_def, "hello42.txt", + "foo bar baz 0", "asset_file_tensor_0:0") - def testCustomMainOp(self): + def testCustomInitOp(self): export_dir = self._get_export_dir("test_main_op") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) with self.session(graph=ops.Graph()) as sess: # Add `v1` and `v2` variables to the graph. @@ -792,11 +837,11 @@ class SavedModelTest(test.TestCase): # Set up an assignment op to be run as part of the main_op. with ops.control_dependencies([main_op.main_op()]): add_v1_v2 = math_ops.add(v1._ref(), v2._ref()) - custom_main_op = control_flow_ops.group(state_ops.assign(v3, add_v1_v2)) + custom_init_op = control_flow_ops.group(state_ops.assign(v3, add_v1_v2)) - sess.run(custom_main_op) + self.evaluate(custom_init_op) builder.add_meta_graph_and_variables( - sess, ["foo"], main_op=custom_main_op) + sess, ["foo"], init_op=custom_init_op) # Save the SavedModel to disk. builder.save() @@ -809,83 +854,10 @@ class SavedModelTest(test.TestCase): # the main_op, following a restore. self.assertEqual(3, ops.get_collection("v")[2].eval()) - def testLegacyInitOp(self): - export_dir = self._get_export_dir("test_legacy_init_op") - builder = saved_model_builder.SavedModelBuilder(export_dir) - - with self.session(graph=ops.Graph()) as sess: - # Add `v1` and `v2` variables to the graph. - v1 = variables.VariableV1(1, name="v1") - ops.add_to_collection("v", v1) - v2 = variables.VariableV1(2, name="v2") - ops.add_to_collection("v", v2) - - # Initialize another variable `v3` to 42. - v3 = variables.VariableV1(42, name="v3", trainable=False, collections=[]) - ops.add_to_collection("v", v3) - - # Set up an assignment op to be run as part of the legacy_init_op. - assign_v3 = state_ops.assign(v3, math_ops.add(v1, v2)) - legacy_init_op = control_flow_ops.group(assign_v3, name="legacy_init_op") - - sess.run(variables.global_variables_initializer()) - builder.add_meta_graph_and_variables( - sess, ["foo"], legacy_init_op=legacy_init_op) - - # Save the SavedModel to disk. - builder.save() - - with self.session(graph=ops.Graph()) as sess: - loader.load(sess, ["foo"], export_dir) - self.assertEqual(1, ops.get_collection("v")[0].eval()) - self.assertEqual(2, ops.get_collection("v")[1].eval()) - # Evaluates to the sum of the first two variables and assigned as part of - # the legacy_init_op, following a restore. - self.assertEqual(3, ops.get_collection("v")[2].eval()) - - def testLegacyInitOpWithNonEmptyCollection(self): - export_dir = self._get_export_dir( - "test_legacy_init_op_with_non_empty_collection") - self._testInitOpsWithNonEmptyCollection( - export_dir, constants.LEGACY_INIT_OP_KEY) - - def testMainOpWithNonEmptyCollection(self): - export_dir = self._get_export_dir( - "test_main_op_with_non_empty_collection") - self._testInitOpsWithNonEmptyCollection(export_dir, constants.MAIN_OP_KEY) - - def _testInitOpsWithNonEmptyCollection(self, export_dir, key): - builder = saved_model_builder.SavedModelBuilder(export_dir) - - g = ops.Graph() - with self.session(graph=g) as sess: - # Initialize variable `v1` to 1. - v1 = variables.VariableV1(1, name="v1") - ops.add_to_collection("v", v1) - - # Initialize another variable `v2` to 42. - v2 = variables.VariableV1(42, name="v2", trainable=False, collections=[]) - ops.add_to_collection("v", v2) - - # Set up an assignment op to be run as part of the init op. - assign_v2 = state_ops.assign(v2, v1) - init_op = control_flow_ops.group(assign_v2, name="init_op") - - sess.run(variables.global_variables_initializer()) - - ops.add_to_collection(key, control_flow_ops.no_op()) - # ValueError should be raised since the LEGACY_INIT_OP_KEY collection - # is not empty and we don't support multiple init ops. - with self.assertRaisesRegexp(ValueError, "Graph already contains"): - builder.add_meta_graph_and_variables( - sess, ["foo"], legacy_init_op=init_op) - # We shouldn't be able to add as MAIN_OP, either. - with self.assertRaisesRegexp(ValueError, "Graph already contains"): - builder.add_meta_graph_and_variables(sess, ["foo"], main_op=init_op) - + @test_util.run_deprecated_v1 def testTrainOp(self): export_dir = self._get_export_dir("test_train_op") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) with self.session(graph=ops.Graph()) as sess: # Add `v1` and `v2` variables to the graph. @@ -894,27 +866,26 @@ class SavedModelTest(test.TestCase): v2 = variables.VariableV1(2, name="v2") ops.add_to_collection("v", v2) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) train_op = state_ops.assign_add(v1, v2) - sess.run(train_op) - # TODO(karmel): remove explicit call when in the public method. - builder._add_train_op(train_op) - builder.add_meta_graph_and_variables(sess, ["foo"]) + self.evaluate(train_op) + builder.add_meta_graph_and_variables(sess, ["foo"], train_op=train_op) # Save the SavedModel to disk. builder.save() with self.session(graph=ops.Graph()) as sess: - loader.load(sess, ["foo"], export_dir) + meta_graph_def = loader.load(sess, ["foo"], export_dir) self.assertEqual(3, ops.get_collection("v")[0].eval()) self.assertEqual(2, ops.get_collection("v")[1].eval()) self.assertIsInstance( - ops.get_collection(constants.TRAIN_OP_KEY)[0], ops.Tensor) + loader_impl.get_train_op(meta_graph_def), ops.Tensor) + @test_util.run_deprecated_v1 def testTrainOpGroup(self): export_dir = self._get_export_dir("test_train_op_group") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) with self.session(graph=ops.Graph()) as sess: # Add `v1` and `v2` variables to the graph. @@ -923,27 +894,26 @@ class SavedModelTest(test.TestCase): v2 = variables.VariableV1(2, name="v2") ops.add_to_collection("v", v2) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) train_op = control_flow_ops.group() - sess.run(train_op) - # TODO(karmel): remove explicit call when in the public method. - builder._add_train_op(train_op) - builder.add_meta_graph_and_variables(sess, ["foo"]) + self.evaluate(train_op) + builder.add_meta_graph_and_variables(sess, ["foo"], train_op=train_op) # Save the SavedModel to disk. builder.save() with self.session(graph=ops.Graph()) as sess: - loader.load(sess, ["foo"], export_dir) + meta_graph_def = loader.load(sess, ["foo"], export_dir) self.assertEqual(1, ops.get_collection("v")[0].eval()) self.assertEqual(2, ops.get_collection("v")[1].eval()) self.assertIsInstance( - ops.get_collection(constants.TRAIN_OP_KEY)[0], ops.Operation) + loader_impl.get_train_op(meta_graph_def), ops.Operation) + @test_util.run_deprecated_v1 def testTrainOpAfterVariables(self): export_dir = self._get_export_dir("test_train_op_after_variables") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) with self.session(graph=ops.Graph()) as sess: # Add `v1` and `v2` variables to the graph. @@ -952,51 +922,50 @@ class SavedModelTest(test.TestCase): v2 = variables.VariableV1(2, name="v2") ops.add_to_collection("v", v2) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) builder.add_meta_graph_and_variables(sess, ["pre_foo"]) train_op = state_ops.assign_add(v1, v2) - sess.run(train_op) - # TODO(karmel): remove explicit call when in the public method. - builder._add_train_op(train_op) - builder.add_meta_graph(["foo"]) + self.evaluate(train_op) + builder.add_meta_graph(["foo"], train_op=train_op) # Save the SavedModel to disk. builder.save() with self.session(graph=ops.Graph()) as sess: - loader.load(sess, ["foo"], export_dir) + meta_graph_def = loader.load(sess, ["foo"], export_dir) self.assertIsInstance( - ops.get_collection(constants.TRAIN_OP_KEY)[0], ops.Tensor) + loader_impl.get_train_op(meta_graph_def), ops.Tensor) with self.session(graph=ops.Graph()) as sess: loader.load(sess, ["pre_foo"], export_dir) self.assertFalse(ops.get_collection(constants.TRAIN_OP_KEY)) + @test_util.run_deprecated_v1 def testMultipleAssets(self): export_dir = self._get_export_dir("test_multiple_assets") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) with self.session(graph=ops.Graph()) as sess: self._init_and_validate_variable(sess, "v", 42) # Build an asset collection specific to `foo` graph. - asset_collection = self._build_asset_collection("foo.txt", "content_foo", - "asset_file_tensor") + asset_list = self._build_asset_collection("foo.txt", "content_foo", + "asset_file_tensor") # Add the asset collection as part of the graph with tag "foo". builder.add_meta_graph_and_variables( - sess, ["foo"], assets_collection=asset_collection) + sess, ["foo"], assets_list=asset_list) with self.session(graph=ops.Graph()) as sess: self._init_and_validate_variable(sess, "v", 42) # Build an asset collection specific to `bar` graph. - asset_collection = self._build_asset_collection("bar.txt", "content_bar", - "asset_file_tensor") + asset_list = self._build_asset_collection("bar.txt", "content_bar", + "asset_file_tensor") # Add the asset collection as part of the graph with tag "bar". - builder.add_meta_graph(["bar"], assets_collection=asset_collection) + builder.add_meta_graph(["bar"], assets_list=asset_list) # Save the SavedModel to disk. builder.save() @@ -1004,43 +973,42 @@ class SavedModelTest(test.TestCase): # Check assets restored for graph with tag "foo". with self.session(graph=ops.Graph()) as sess: foo_graph = loader.load(sess, ["foo"], export_dir) - self._validate_asset_collection(export_dir, foo_graph.collection_def, - "foo.txt", "content_foo", - "asset_file_tensor:0") + self._validate_assets(export_dir, foo_graph.asset_file_def, "foo.txt", + "content_foo", "asset_file_tensor:0") # Check assets restored for graph with tag "bar". with self.session(graph=ops.Graph()) as sess: bar_graph = loader.load(sess, ["bar"], export_dir) - self._validate_asset_collection(export_dir, bar_graph.collection_def, - "bar.txt", "content_bar", - "asset_file_tensor:0") + self._validate_assets(export_dir, bar_graph.asset_file_def, "bar.txt", + "content_bar", "asset_file_tensor:0") + @test_util.run_deprecated_v1 def testDuplicateAssets(self): export_dir = self._get_export_dir("test_duplicate_assets") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) with self.session(graph=ops.Graph()) as sess: self._init_and_validate_variable(sess, "v", 42) # Build an asset collection with `foo.txt` that has `foo` specific # content. - asset_collection = self._build_asset_collection("foo.txt", "content_foo", - "asset_file_tensor") + asset_list = self._build_asset_collection("foo.txt", "content_foo", + "asset_file_tensor") # Add the asset collection as part of the graph with tag "foo". builder.add_meta_graph_and_variables( - sess, ["foo"], assets_collection=asset_collection) + sess, ["foo"], assets_list=asset_list) with self.session(graph=ops.Graph()) as sess: self._init_and_validate_variable(sess, "v", 42) # Build an asset collection with `foo.txt` that has `bar` specific # content. - asset_collection = self._build_asset_collection("foo.txt", "content_bar", - "asset_file_tensor") + asset_list = self._build_asset_collection("foo.txt", "content_bar", + "asset_file_tensor") # Add the asset collection as part of the graph with tag "bar". - builder.add_meta_graph(["bar"], assets_collection=asset_collection) + builder.add_meta_graph(["bar"], assets_list=asset_list) # Save the SavedModel to disk. builder.save() @@ -1048,9 +1016,8 @@ class SavedModelTest(test.TestCase): # Check assets restored for graph with tag "foo". with self.session(graph=ops.Graph()) as sess: foo_graph = loader.load(sess, ["foo"], export_dir) - self._validate_asset_collection(export_dir, foo_graph.collection_def, - "foo.txt", "content_foo", - "asset_file_tensor:0") + self._validate_assets(export_dir, foo_graph.asset_file_def, "foo.txt", + "content_foo", "asset_file_tensor:0") # Check assets restored for graph with tag "bar". with self.session(graph=ops.Graph()) as sess: @@ -1059,13 +1026,13 @@ class SavedModelTest(test.TestCase): # Validate the assets for `bar` graph. `foo.txt` should contain the # original contents corresponding to `foo` graph since an asset with the # same name across multiple graphs is only stored the first time - self._validate_asset_collection(export_dir, bar_graph.collection_def, - "foo.txt", "content_foo", - "asset_file_tensor:0") + self._validate_assets(export_dir, bar_graph.asset_file_def, "foo.txt", + "content_foo", "asset_file_tensor:0") + @test_util.run_deprecated_v1 def testOp(self): export_dir = self._get_export_dir("test_op") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) with session.Session( graph=ops.Graph(), @@ -1086,7 +1053,7 @@ class SavedModelTest(test.TestCase): ops.add_to_collection("v", v3) ops.add_to_collection("init_op", init_op) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) self.assertEqual(1, ops.get_collection("v")[0].eval()) self.assertEqual(2, ops.get_collection("v")[1].eval()) @@ -1108,7 +1075,7 @@ class SavedModelTest(test.TestCase): def testCustomSaveable(self): export_dir = self._get_export_dir("custom_saveable") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) with session.Session( graph=ops.Graph(), @@ -1135,13 +1102,14 @@ class SavedModelTest(test.TestCase): self.assertEqual(b"k1", v1.keys().eval()) self.assertEqual(3.0, v1.values().eval()) + @test_util.run_deprecated_v1 def testCustomSaver(self): export_dir = self._get_export_dir("test_custom_saver") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) with self.session(graph=ops.Graph()) as sess: variables.VariableV1(1, name="v1") - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) custom_saver = training.Saver(name="my_saver") builder.add_meta_graph_and_variables(sess, ["tag"], saver=custom_saver) @@ -1157,13 +1125,14 @@ class SavedModelTest(test.TestCase): self.assertEqual( saved_graph.saver_def.restore_op_name, "my_saver/restore_all") + @test_util.run_deprecated_v1 def testNoCustomSaver(self): export_dir = self._get_export_dir("test_no_custom_saver") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) with self.session(graph=ops.Graph()) as sess: variables.VariableV1(1, name="v1") - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) training.Saver(name="my_saver") builder.add_meta_graph_and_variables(sess, ["tag"]) @@ -1179,13 +1148,14 @@ class SavedModelTest(test.TestCase): self.assertEqual( saved_graph.saver_def.restore_op_name, "save/restore_all") + @test_util.run_deprecated_v1 def testMultipleCustomSavers(self): export_dir = self._get_export_dir("test_multiple_custom_savers") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) with self.session(graph=ops.Graph()) as sess: variables.VariableV1(1, name="v1") - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) builder.add_meta_graph_and_variables(sess, ["tag_0"]) saver_1 = training.Saver() @@ -1209,21 +1179,22 @@ class SavedModelTest(test.TestCase): _validate_custom_saver("tag_1", "save_1/restore_all") _validate_custom_saver("tag_2", "save_2/restore_all") + @test_util.run_deprecated_v1 def testImportScope(self): export_dir = self._get_export_dir("test_scoped_assets") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) # Build a SavedModel with a variable, an asset, and a constant tensor. with self.session(graph=ops.Graph()) as sess: self._init_and_validate_variable(sess, "v", 42) - asset_collection = self._build_asset_collection("foo.txt", "content_foo", - "asset_file_tensor") + asset_list = self._build_asset_collection("foo.txt", "content_foo", + "asset_file_tensor") constant_op.constant("constant value", name="constant_tensor_name") builder.add_meta_graph_and_variables( - sess, ["tag_name"], assets_collection=asset_collection) + sess, ["tag_name"], assets_list=asset_list) # Save the asset file path for later comparison. - asset_file_path = asset_collection[0].eval() + asset_file_path = asset_list[0].eval() # Save the SavedModel to disk. builder.save() @@ -1244,16 +1215,14 @@ class SavedModelTest(test.TestCase): # The loaded asset tensor should be scoped, but the asset file path and # contents should be unchanged. - asset_collection = ops.get_collection(ops.GraphKeys.ASSET_FILEPATHS) - self.assertEqual(1, len(asset_collection)) - self.assertEqual(asset_file_path, asset_collection[0].eval()) - self.assertEqual("scope_name/asset_file_tensor:0", - asset_collection[0].name) + asset_list = ops.get_collection(ops.GraphKeys.ASSET_FILEPATHS) + self.assertEqual(1, len(asset_list)) + self.assertEqual(asset_file_path, asset_list[0].eval()) + self.assertEqual("scope_name/asset_file_tensor:0", asset_list[0].name) # The static asset data inside graph_proto.collection_def should not be # scoped. - self._validate_asset_collection(export_dir, graph_proto.collection_def, - "foo.txt", "content_foo", - "asset_file_tensor:0") + self._validate_assets(export_dir, graph_proto.asset_file_def, "foo.txt", + "content_foo", "asset_file_tensor:0") # The constant tensor should be scoped, but its contents should be # unchanged. @@ -1262,9 +1231,10 @@ class SavedModelTest(test.TestCase): ops.get_default_graph().get_tensor_by_name( "scope_name/constant_tensor_name:0").eval()) + @test_util.run_deprecated_v1 def testClearDevices(self): export_dir = self._get_export_dir("test_clear_devices") - builder = saved_model_builder.SavedModelBuilder(export_dir) + builder = saved_model_builder._SavedModelBuilder(export_dir) # Specify a device and save a variable. ops.reset_default_graph() @@ -1286,6 +1256,174 @@ class SavedModelTest(test.TestCase): self.assertEqual( 42, ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES)[0].eval()) + # Tests the behavior of loading SavedModels that having missing attrs or attrs + # with incorrect types. + def testInconsistentConsumerDefaultAttrs(self): + export_dir = self._get_export_dir( + "test_strip_default_attrs_no_consumer_defaults") + builder = saved_model_builder._SavedModelBuilder(export_dir) + + # Add a graph with a single variable and a test op with a defaultless + # float32 attr, "test_attr". + with session.Session(graph=ops.Graph()) as sess: + variables.VariableV1(1.0, dtype=dtypes.float64, name="var") + test_ops.test_attr(T=dtypes.float32, name="test_attr") + self.evaluate(variables.global_variables_initializer()) + builder.add_meta_graph_and_variables(sess, ["foo"]) + + # Save the SavedModel to disk in text format. + builder.save(as_text=True) + + # Rewrite the SavedModel to remove the T attr from "test_attr". + saved_model_file = os.path.join( + export_dir, constants.SAVED_MODEL_FILENAME_PBTXT) + with open(saved_model_file) as f: + original_saved_model = f.read() + + no_attr_saved_model = original_saved_model.replace(""" + attr { + key: "T" + value { + type: DT_FLOAT + } + }""", "") + with open(saved_model_file, "w") as f: + f.write(no_attr_saved_model) + + # Loading the SavedModel via the loader must fail because the SavedModel + # does not have any attr values for the "TestAttr" node, and there is no + # default specified in the TestAttr OpDef. + sess = session.Session(graph=ops.Graph()) + with self.assertRaisesRegexp( + ValueError, "NodeDef missing attr 'T' from Op= mtimes[0]) + @test_util.run_deprecated_v1 def testRemoveCheckpoint(self): for sharded in (False, True): for version in (saver_pb2.SaverDef.V2, saver_pb2.SaverDef.V1): diff --git a/tensorflow/python/training/checkpoint_ops_test.py b/tensorflow/python/training/checkpoint_ops_test.py index dde8431497..21ad3df1c8 100644 --- a/tensorflow/python/training/checkpoint_ops_test.py +++ b/tensorflow/python/training/checkpoint_ops_test.py @@ -47,7 +47,7 @@ class LoadAndRemapWrappersTest(test.TestCase): with variable_scope.variable_scope('some_scope'): variable_scope.get_variable(name='embeddings', shape=[5, 16], initializer=initializer) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) saver = saver_lib.Saver() saver.save(sess, checkpoint_prefix, global_step=5) self.checkpoint_file = '{}-5'.format(checkpoint_prefix) @@ -115,7 +115,8 @@ class LoadAndRemapWrappersTest(test.TestCase): axis=1) with self.cached_session(): - self.assertAllClose(expected_remapped_matrix, remapped_matrix.eval()) + self.assertAllClose(expected_remapped_matrix, + self.evaluate(remapped_matrix)) def test_load_and_remap_output_layer_weight_initializer_linear(self): """Tests for the output layer initializer in the linear multi-class case.""" diff --git a/tensorflow/python/training/checkpoint_utils.py b/tensorflow/python/training/checkpoint_utils.py index 857da431db..58166dbb68 100644 --- a/tensorflow/python/training/checkpoint_utils.py +++ b/tensorflow/python/training/checkpoint_utils.py @@ -101,7 +101,7 @@ def list_variables(ckpt_dir_or_file): return result -@tf_export("train.init_from_checkpoint") +@tf_export(v1=["train.init_from_checkpoint"]) def init_from_checkpoint(ckpt_dir_or_file, assignment_map): """Replaces `tf.Variable` initializers so they load from a checkpoint file. @@ -187,7 +187,7 @@ def init_from_checkpoint(ckpt_dir_or_file, assignment_map): _init_from_checkpoint(None, ckpt_dir_or_file, assignment_map) else: distribution_strategy_context.get_replica_context().merge_call( - _init_from_checkpoint, ckpt_dir_or_file, assignment_map) + _init_from_checkpoint, args=(ckpt_dir_or_file, assignment_map)) def _init_from_checkpoint(_, ckpt_dir_or_file, assignment_map): diff --git a/tensorflow/python/training/checkpointable/BUILD b/tensorflow/python/training/checkpointable/BUILD index d26932c1aa..f97f42a659 100644 --- a/tensorflow/python/training/checkpointable/BUILD +++ b/tensorflow/python/training/checkpointable/BUILD @@ -152,7 +152,7 @@ py_test( "//tensorflow/python:variable_scope", "//tensorflow/python/eager:backprop", "//tensorflow/python/eager:context", - "//tensorflow/python/eager:function", + "//tensorflow/python/eager:def_function", "//tensorflow/python/eager:test", "//tensorflow/python/keras:engine", "//tensorflow/python/keras:layers", diff --git a/tensorflow/python/training/checkpointable/data_structures.py b/tensorflow/python/training/checkpointable/data_structures.py index c29e5db075..817552f326 100644 --- a/tensorflow/python/training/checkpointable/data_structures.py +++ b/tensorflow/python/training/checkpointable/data_structures.py @@ -111,9 +111,6 @@ class CheckpointableDataStructure(base.CheckpointableBase): """Base class for data structures which contain checkpointable objects.""" def __init__(self): - # An append-only ordered set - self._layers = [] - self.trainable = True self._extra_variables = [] @@ -128,21 +125,30 @@ class CheckpointableDataStructure(base.CheckpointableBase): ("Only checkpointable objects (such as Layers or Optimizers) may be " "stored in a List object. Got %s, which does not inherit from " "CheckpointableBase.") % (value,)) - if (isinstance(value, CheckpointableDataStructure) - or layer_utils.is_layer(value) - or layer_utils.has_weights(value)): - # Check for object-identity rather than with __eq__ to avoid - # de-duplicating empty container types. Automatically generated list - # wrappers keep things like "[] == []" true, which means "[] in [[]]" is - # also true. This becomes not true once one of the lists is mutated. - if not any((layer is value for layer in self._layers)): - self._layers.append(value) - if hasattr(value, "_use_resource_variables"): - # In subclassed models, legacy layers (tf.layers) must always use - # resource variables. - value._use_resource_variables = True # pylint: disable=protected-access + if hasattr(value, "_use_resource_variables"): + # In subclassed models, legacy layers (tf.layers) must always use + # resource variables. + value._use_resource_variables = True # pylint: disable=protected-access return value + @property + def _values(self): + """An iterable/sequence which may contain checkpointable objects.""" + raise NotImplementedError("Abstract method") + + @property + def _layers(self): + """All Layers and Layer containers, including empty containers.""" + # Filter objects on demand so that wrapper objects use values from the thing + # they're wrapping if out of sync. + collected = [] + for obj in self._values: + if (isinstance(obj, CheckpointableDataStructure) + or layer_utils.is_layer(obj) + or layer_utils.has_weights(obj)): + collected.append(obj) + return collected + @property def layers(self): return layer_utils.filter_empty_layer_containers(self._layers) @@ -265,6 +271,10 @@ class List(CheckpointableDataStructure, collections.Sequence): def _name_element(self, index): return "%d" % (index,) + @property + def _values(self): + return self + def append(self, value): """Add a new checkpointable value.""" value = self._track_value(value, self._name_element(len(self._storage))) @@ -479,6 +489,14 @@ class Mapping(CheckpointableDataStructure, collections.Mapping): def _make_storage(self, *args, **kwargs): return dict(*args, **kwargs) + @property + def _values(self): + # Sort items deterministically by key + ordered = list(zip(*sorted(self.items(), key=lambda it: it[0]))) + if ordered: + return ordered[1] + return [] + def _name_element(self, key): if not isinstance(key, six.string_types): raise TypeError( diff --git a/tensorflow/python/training/checkpointable/data_structures_test.py b/tensorflow/python/training/checkpointable/data_structures_test.py index ff7d1f1d2d..9cefd942ac 100644 --- a/tensorflow/python/training/checkpointable/data_structures_test.py +++ b/tensorflow/python/training/checkpointable/data_structures_test.py @@ -253,6 +253,13 @@ class ListTests(test.TestCase): l.append(1) self.assertEqual([1], l_wrapper) + def testLayerCollectionWithExternalMutation(self): + l = [] + l_wrapper = data_structures._ListWrapper(l) + layer = core.Dense(1) + l.append(layer) + self.assertEqual([layer], l_wrapper.layers) + def testHashing(self): has_sequences = set([data_structures.List(), data_structures.List()]) @@ -324,6 +331,20 @@ class MappingTests(test.TestCase): with self.assertRaises(TypeError): mapping[1] = data_structures.List() + def testLayerCollectionWithExternalMutation(self): + d = {} + root = tracking.Checkpointable() + root.wrapper = d + self.assertEqual([], root.wrapper.layers) + self.assertEqual([], root.wrapper.trainable_weights) + layer1 = core.Dense(1) + layer2 = core.Dense(1) + d["a"] = layer1 + d["b"] = layer2 + self.assertEqual([layer1, layer2], root.wrapper.layers) + # The layers have still not created variables + self.assertEqual([], root.wrapper.trainable_weights) + def testHashing(self): has_mappings = set([data_structures.Mapping(), data_structures.Mapping()]) diff --git a/tensorflow/python/training/checkpointable/tracking.py b/tensorflow/python/training/checkpointable/tracking.py index c85b208d47..4e96aee0c5 100644 --- a/tensorflow/python/training/checkpointable/tracking.py +++ b/tensorflow/python/training/checkpointable/tracking.py @@ -17,6 +17,10 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +from tensorflow.python.eager import context +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops +from tensorflow.python.ops import resource_variable_ops from tensorflow.python.training.checkpointable import base from tensorflow.python.training.checkpointable import data_structures from tensorflow.python.util import tf_contextlib @@ -145,3 +149,36 @@ class TrackableResource(base.CheckpointableBase): if self._resource_handle is None: self._resource_handle = self.create_resource() return self._resource_handle + + +class TrackableAsset(base.CheckpointableBase): + """Base class for asset files which need to be tracked.""" + + def __init__(self, path): + """Record the full path to the asset.""" + # We use a variable here so that @tf.functions do not capture a literal + # value. The init_scope prevents functions from capturing `path` in an + # initialization graph, since it is transient and should not end up in a + # serialized function body. When serialized in a SavedModel, the variable + # will be set during the loading process to its location in the assets/ + # directory. + with ops.init_scope(): + if context.executing_eagerly(): + self._path = self._no_dependency( + resource_variable_ops.ResourceVariable( + path, dtype=dtypes.string, + name="asset_path")) + else: + # Adding a variable is too disruptive when v1-style graph building, + # since things may get fed and local variable initializers would then + # need to be run. + self._path = path + + @property + def asset_path(self): + """Fetch the current asset path.""" + return self._path + +ops.register_tensor_conversion_function( + TrackableAsset, + lambda asset, **kw: ops.internal_convert_to_tensor(asset.asset_path, **kw)) diff --git a/tensorflow/python/training/checkpointable/util.py b/tensorflow/python/training/checkpointable/util.py index f45f7445f1..36d3a7bebd 100644 --- a/tensorflow/python/training/checkpointable/util.py +++ b/tensorflow/python/training/checkpointable/util.py @@ -31,6 +31,7 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors_impl from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape +from tensorflow.python.lib.io import file_io from tensorflow.python.ops import array_ops from tensorflow.python.ops import gen_io_ops as io_ops from tensorflow.python.ops import init_ops @@ -549,13 +550,11 @@ def _serialize_slot_variables(checkpointable_objects, node_ids, object_names): return slot_variables -def _serialize_checkpointables( - checkpointable_objects, node_ids, object_names, slot_variables, +def _add_attributes_to_object_graph( + checkpointable_objects, object_graph_proto, node_ids, object_names, saveables_cache, object_map): - """Name non-slot `Checkpointable`s and add them to `object_graph_proto`.""" - object_graph_proto = ( - checkpointable_object_graph_pb2.CheckpointableObjectGraph()) - named_saveables = [] + """Create SaveableObjects and corresponding SerializedTensor protos.""" + named_saveable_objects = [] if saveables_cache is None: # No SaveableObject caching. Either we're executing eagerly, or building a # static save which is specialized to the current Python state. @@ -564,10 +563,9 @@ def _serialize_checkpointables( # If we are caching SaveableObjects, we need to build up a feed_dict with # functions computing volatile Python state to be saved with the checkpoint. feed_additions = {} - for checkpoint_id, checkpointable in enumerate(checkpointable_objects): + for checkpoint_id, (checkpointable, object_proto) in enumerate( + zip(checkpointable_objects, object_graph_proto.nodes)): assert node_ids[checkpointable] == checkpoint_id - object_proto = object_graph_proto.nodes.add() - object_proto.slot_variables.extend(slot_variables.get(checkpointable, ())) object_name = object_names[checkpointable] if object_map: object_to_save = object_map.get(checkpointable, checkpointable) @@ -645,14 +643,26 @@ def _serialize_checkpointables( "value.") % (checkpointable, new_feed_key)) feed_additions.update(saveable_feed_dict) - named_saveables.append(saveable) + named_saveable_objects.append(saveable) + return named_saveable_objects, feed_additions + + +def _fill_object_graph_proto(checkpointable_objects, node_ids, slot_variables, + object_graph_proto=None): + """Name non-slot `Checkpointable`s and add them to `object_graph_proto`.""" + if object_graph_proto is None: + object_graph_proto = ( + checkpointable_object_graph_pb2.CheckpointableObjectGraph()) + for checkpoint_id, checkpointable in enumerate(checkpointable_objects): + assert node_ids[checkpointable] == checkpoint_id + object_proto = object_graph_proto.nodes.add() + object_proto.slot_variables.extend(slot_variables.get(checkpointable, ())) for child in checkpointable._checkpoint_dependencies: # pylint: disable=protected-access child_proto = object_proto.children.add() child_proto.node_id = node_ids[child.ref] child_proto.local_name = child.name - - return named_saveables, object_graph_proto, feed_additions + return object_graph_proto def _serialize_gathered_objects( @@ -668,13 +678,18 @@ def _serialize_gathered_objects( checkpointable_objects=checkpointable_objects, node_ids=node_ids, object_names=object_names) - return _serialize_checkpointables( + object_graph_proto = _fill_object_graph_proto( + checkpointable_objects=checkpointable_objects, + node_ids=node_ids, + slot_variables=slot_variables) + named_saveable_objects, feed_additions = _add_attributes_to_object_graph( checkpointable_objects=checkpointable_objects, + object_graph_proto=object_graph_proto, node_ids=node_ids, object_names=object_names, - slot_variables=slot_variables, saveables_cache=saveables_cache, object_map=object_map) + return named_saveable_objects, object_graph_proto, feed_additions def _serialize_object_graph(root_checkpointable, saveables_cache): @@ -716,6 +731,23 @@ def named_saveables(root_checkpointable): return _serialize_object_graph(root_checkpointable, None)[0] +def _find_objects(root_checkpointable): + """Find and number objects which are dependencies of `root_checkpointable`.""" + checkpointable_objects, path_to_root = ( + _breadth_first_checkpointable_traversal(root_checkpointable)) + object_names = _ObjectIdentityDictionary() + for obj, path in path_to_root.items(): + object_names[obj] = _object_prefix_from_path(path) + node_ids = _ObjectIdentityDictionary() + for node_id, node in enumerate(checkpointable_objects): + node_ids[node] = node_id + slot_variables = _serialize_slot_variables( + checkpointable_objects=checkpointable_objects, + node_ids=node_ids, + object_names=object_names) + return checkpointable_objects, node_ids, slot_variables + + def list_objects(root_checkpointable): """Traverse the object graph and list all accessible objects. @@ -730,23 +762,18 @@ def list_objects(root_checkpointable): Returns: A flat list of objects. """ - # TODO(allenl): Extract out gathering logic so the naming logic doesn't have - # to run. - checkpointable_objects, path_to_root = ( - _breadth_first_checkpointable_traversal(root_checkpointable)) - object_names = _ObjectIdentityDictionary() - for obj, path in path_to_root.items(): - object_names[obj] = _object_prefix_from_path(path) - node_ids = _ObjectIdentityDictionary() - for node_id, node in enumerate(checkpointable_objects): - node_ids[node] = node_id - _serialize_slot_variables( - checkpointable_objects=checkpointable_objects, - node_ids=node_ids, - object_names=object_names) + checkpointable_objects, _, _ = _find_objects(root_checkpointable) return checkpointable_objects +def make_object_graph_without_attributes(root_checkpointable, proto=None): + """Fill an object graph proto, ignoring variable values.""" + checkpointable_objects, node_ids, slot_variables = _find_objects( + root_checkpointable) + return _fill_object_graph_proto( + checkpointable_objects, node_ids, slot_variables, proto) + + def gather_initializers(root_checkpointable): """Traverse the object graph and find initialization ops. @@ -1434,6 +1461,7 @@ class CheckpointableSaver(object): elif session is None: session = ops.get_default_session() + file_io.recursive_create_dir(os.path.dirname(file_prefix)) with ops.device("/cpu:0"): save_path = saver.save( sess=_SessionWithFeedDictAdditions( @@ -1898,3 +1926,4 @@ class Checkpoint(tracking.Checkpointable): # initialization when executing eagerly. self._maybe_create_save_counter() return status + diff --git a/tensorflow/python/training/checkpointable/util_test.py b/tensorflow/python/training/checkpointable/util_test.py index 1995514012..de9cac0863 100644 --- a/tensorflow/python/training/checkpointable/util_test.py +++ b/tensorflow/python/training/checkpointable/util_test.py @@ -26,7 +26,7 @@ from tensorflow.python import pywrap_tensorflow from tensorflow.python.client import session as session_lib from tensorflow.python.eager import backprop from tensorflow.python.eager import context -from tensorflow.python.eager import function +from tensorflow.python.eager import def_function from tensorflow.python.eager import test from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes @@ -44,6 +44,7 @@ from tensorflow.python.ops import variable_scope from tensorflow.python.ops import variables from tensorflow.python.training import adam from tensorflow.python.training import checkpoint_management +from tensorflow.python.training import momentum from tensorflow.python.training import saver as saver_lib from tensorflow.python.training import training_util from tensorflow.python.training.checkpointable import base @@ -198,6 +199,17 @@ class InterfaceTests(test.TestCase): with self.assertRaises(NotImplementedError): checkpoint_reversed.save(prefix) + @test_util.run_in_graph_and_eager_modes(assert_no_eager_garbage=True) + def test_object_graph_no_attributes(self): + root = tracking.Checkpointable() + root.v = resource_variable_ops.ResourceVariable(1.) + root.opt = momentum.MomentumOptimizer(0.01, 0.5) + root.opt.minimize(root.v.read_value) + object_graph = checkpointable_utils.make_object_graph_without_attributes( + root) + # Four objects: Root, v, opt, and a slot variable for v + self.assertEqual(4, len(object_graph.nodes)) + class _MirroringSaveable(saver_lib.BaseSaverBuilder.SaveableObject): @@ -632,7 +644,7 @@ class CheckpointingTests(test.TestCase): checkpoint_directory) status = root.restore(save_path=checkpoint_path) def train_fn(): - @function.defun + @def_function.function def _call_model(x): return model(x) with backprop.GradientTape() as tape: diff --git a/tensorflow/python/training/coordinator.py b/tensorflow/python/training/coordinator.py index 0ff97d85e3..b7e5c98c78 100644 --- a/tensorflow/python/training/coordinator.py +++ b/tensorflow/python/training/coordinator.py @@ -408,7 +408,7 @@ class Coordinator(object): # Threads for the standard services. -@tf_export("train.LooperThread") +@tf_export(v1=["train.LooperThread"]) class LooperThread(threading.Thread): """A thread that runs code repeatedly, optionally on a timer. diff --git a/tensorflow/python/training/device_setter.py b/tensorflow/python/training/device_setter.py index be80c36571..5874a1ff41 100644 --- a/tensorflow/python/training/device_setter.py +++ b/tensorflow/python/training/device_setter.py @@ -130,7 +130,7 @@ class _ReplicaDeviceChooser(object): return worker_device.to_string() -@tf_export("train.replica_device_setter") +@tf_export(v1=["train.replica_device_setter"]) def replica_device_setter(ps_tasks=0, ps_device="/job:ps", worker_device="/job:worker", merge_devices=True, cluster=None, ps_ops=None, ps_strategy=None): diff --git a/tensorflow/python/training/device_setter_test.py b/tensorflow/python/training/device_setter_test.py index 85b75502ab..3cff87b326 100644 --- a/tensorflow/python/training/device_setter_test.py +++ b/tensorflow/python/training/device_setter_test.py @@ -19,6 +19,7 @@ from __future__ import division from __future__ import print_function from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import resource_variable_ops from tensorflow.python.ops import variables from tensorflow.python.platform import test @@ -33,6 +34,7 @@ class DeviceSetterTest(test.TestCase): "worker": ["worker0:2222", "worker1:2222", "worker2:2222"] }) + @test_util.run_deprecated_v1 def testCPUOverride(self): with ops.device( device_setter.replica_device_setter(cluster=self._cluster_spec)): @@ -47,12 +49,14 @@ class DeviceSetterTest(test.TestCase): self.assertDeviceEqual("/job:ps/task:1", w.initializer.device) self.assertDeviceEqual("/job:worker/cpu:0", a.device) + @test_util.run_deprecated_v1 def testResource(self): with ops.device( device_setter.replica_device_setter(cluster=self._cluster_spec)): v = resource_variable_ops.ResourceVariable([1, 2]) self.assertDeviceEqual("/job:ps/task:0", v.device) + @test_util.run_deprecated_v1 def testPS2TasksWithClusterSpecClass(self): with ops.device( device_setter.replica_device_setter(cluster=self._cluster_spec)): @@ -65,6 +69,7 @@ class DeviceSetterTest(test.TestCase): self.assertDeviceEqual("/job:ps/task:1", w.initializer.device) self.assertDeviceEqual("/job:worker", a.device) + @test_util.run_deprecated_v1 def testPS2TasksPinVariableToJob(self): with ops.device( device_setter.replica_device_setter(cluster=self._cluster_spec)): @@ -82,6 +87,7 @@ class DeviceSetterTest(test.TestCase): self.assertDeviceEqual("/job:ps/task:1", x.initializer.device) self.assertDeviceEqual("/job:worker", a.device) + @test_util.run_deprecated_v1 def testPS2TasksUseCpuForPS(self): with ops.device( device_setter.replica_device_setter(ps_tasks=1, ps_device="/cpu:0")): @@ -95,6 +101,7 @@ class DeviceSetterTest(test.TestCase): self.assertDeviceEqual("/job:moon/cpu:0", w.initializer.device) self.assertDeviceEqual("/job:worker", a.device) + @test_util.run_deprecated_v1 def testPS2TasksNoMerging(self): with ops.device( device_setter.replica_device_setter( @@ -109,6 +116,7 @@ class DeviceSetterTest(test.TestCase): self.assertDeviceEqual("/job:ps", w.initializer.device) self.assertDeviceEqual("/job:worker", a.device) + @test_util.run_deprecated_v1 def testPS2TasksWithClusterSpecDict(self): with ops.device( device_setter.replica_device_setter(cluster=self._cluster_spec.as_dict( @@ -122,6 +130,7 @@ class DeviceSetterTest(test.TestCase): self.assertDeviceEqual("/job:ps/task:1", w.initializer.device) self.assertDeviceEqual("/job:worker", a.device) + @test_util.run_deprecated_v1 def testPS2TasksWithClusterDef(self): with ops.device( device_setter.replica_device_setter( @@ -135,6 +144,7 @@ class DeviceSetterTest(test.TestCase): self.assertDeviceEqual("/job:ps/task:1", w.initializer.device) self.assertDeviceEqual("/job:worker", a.device) + @test_util.run_deprecated_v1 def testPS2TasksWithDevice(self): cluster_spec = server_lib.ClusterSpec({ "sun": ["sun0:2222", "sun1:2222", "sun2:2222"], @@ -155,6 +165,7 @@ class DeviceSetterTest(test.TestCase): self.assertDeviceEqual("/job:moon/task:1", w.initializer.device) self.assertDeviceEqual("/job:sun", a.device) + @test_util.run_deprecated_v1 def testPS2TasksWithCPUConstraint(self): cluster_spec = server_lib.ClusterSpec({ "sun": ["sun0:2222", "sun1:2222", "sun2:2222"], diff --git a/tensorflow/python/training/distribute.py b/tensorflow/python/training/distribute.py index 95104ad577..ad27bc8a70 100644 --- a/tensorflow/python/training/distribute.py +++ b/tensorflow/python/training/distribute.py @@ -12,1234 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Class DistributionStrategy, ReplicaContext, and supporting APIs.""" +"""Deprecated, please use ../distribute/distribute_lib.py.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function -import threading - -from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.framework import ops -from tensorflow.python.ops import array_ops -from tensorflow.python.ops import control_flow_ops -from tensorflow.python.ops import resource_variable_ops -from tensorflow.python.ops import variable_scope -from tensorflow.python.ops.losses import losses_impl -from tensorflow.python.platform import tf_logging -from tensorflow.python.training import device_util -from tensorflow.python.training import distribution_strategy_context -from tensorflow.python.util import nest - - -# ------------------------------------------------------------------------------ -# Context tracking whether in a distribution.update() or .update_non_slot() -# call. - - -_update_device = threading.local() - - -def get_update_device(): - """Get the current device if in a `DistributionStrategy.update()` call.""" - try: - return _update_device.current - except AttributeError: - return None - - -class UpdateContext(object): - """Context manager when you are in `update()` or `update_non_slot()`.""" - - def __init__(self, device): - self._device = device - self._old_device = None - - def __enter__(self): - self._old_device = get_update_device() - _update_device.current = self._device - - def __exit__(self, exception_type, exception_value, traceback): - del exception_type, exception_value, traceback - _update_device.current = self._old_device - - -# ------------------------------------------------------------------------------ -# Public utility functions. - - -def get_loss_reduction(): - """Reduce `aggregation` corresponding to the last loss reduction.""" - loss_reduction = ops.get_default_graph()._last_loss_reduction # pylint: disable=protected-access - if loss_reduction == losses_impl.Reduction.SUM: - return variable_scope.VariableAggregation.SUM - return variable_scope.VariableAggregation.MEAN - - -# ------------------------------------------------------------------------------ -# Internal API for validating the current thread mode - - -def _require_cross_replica_context(distribution_strategy): - """Verify in cross-replica context for `distribution_strategy`.""" - context = _get_per_thread_mode() - if context.cross_replica_context is distribution_strategy: return - # We have an error to report, figure out the right message. - if context.distribution_strategy is not distribution_strategy: - if not distribution_strategy_context.has_distribution_strategy(): - raise RuntimeError( - 'Need to be inside "with distribution_strategy.scope()" for %s' % - (distribution_strategy,)) - else: - raise RuntimeError( - "Mixing different DistributionStrategy objects: %s is not %s" % - (context.distribution_strategy, distribution_strategy)) - assert context.cross_replica_context is None - raise RuntimeError("Method requires being in cross-replica context, use " - "get_replica_context().merge_call()") - - -def require_replica_context(replica_ctx): - """Verify in `replica_ctx` replica context.""" - context = _get_per_thread_mode() - if context.replica_context is replica_ctx: return - # We have an error to report, figure out the right message. - if context.replica_context is None: - raise RuntimeError("Need to be inside `call_for_each_replica()`") - if context.distribution_strategy is replica_ctx.distribution_strategy: - # Two different ReplicaContexts with the same DistributionStrategy. - raise RuntimeError("Mismatching replica context.") - raise RuntimeError( - "Mismatching DistributionStrategy objects: %s is not %s." % - (context.distribution_strategy, replica_ctx.distribution_strategy)) - - -def _require_distribution_strategy_scope(distribution_strategy): - """Verify in a `distribution_strategy.scope()` in this thread.""" - context = _get_per_thread_mode() - if context.distribution_strategy is distribution_strategy: return - # We have an error to report, figure out the right message. - if not distribution_strategy_context.has_distribution_strategy(): - raise RuntimeError( - 'Need to be inside "with distribution_strategy.scope()" for %s' % - (distribution_strategy,)) - else: - raise RuntimeError( - "Mixing different DistributionStrategy objects: %s is not %s" % - (context.distribution_strategy, distribution_strategy)) - - -# ------------------------------------------------------------------------------ -# Internal context managers used to implement the DistributionStrategy -# base class - - -class _CurrentDistributionContext(object): - """Context manager for setting the `DistributionStrategy` and var creator.""" - - def __init__(self, - distribution_strategy, - var_creator_scope, - var_scope=None, - default_device=None): - self._context = distribution_strategy_context._CrossReplicaThreadMode( # pylint: disable=protected-access - distribution_strategy) - self._var_creator_scope = var_creator_scope - self._var_scope = var_scope - if default_device: - self._device_scope = ops.device(default_device) - else: - self._device_scope = None - - def __enter__(self): - _push_per_thread_mode(self._context) - if self._var_scope: - self._var_scope.__enter__() - self._var_creator_scope.__enter__() - if self._device_scope: - self._device_scope.__enter__() - return self._context.distribution_strategy - - def __exit__(self, exception_type, exception_value, traceback): - if self._device_scope: - self._device_scope.__exit__(exception_type, exception_value, traceback) - self._var_creator_scope.__exit__(exception_type, exception_value, traceback) - if self._var_scope: - self._var_scope.__exit__(exception_type, exception_value, traceback) - _pop_per_thread_mode() - - -class _SameScopeAgainContext(object): - """Trivial context manager when you are already in `scope()`.""" - - def __init__(self, distribution_strategy): - self._distribution_strategy = distribution_strategy - - def __enter__(self): - return self._distribution_strategy - - def __exit__(self, exception_type, exception_value, traceback): - del exception_type, exception_value, traceback - - -# ------------------------------------------------------------------------------ -# Base classes for all distribution strategies. - - -class DistributionStrategy(object): - """A list of devices with a state & compute distribution policy. - - See [tensorflow/contrib/distribute/README.md]( - https://www.tensorflow.org/code/tensorflow/contrib/distribute/README.md) - for overview and examples. - - The intent is that you can write an algorithm in a stylized way and - it will be usable with a variety of different `DistributionStrategy` - implementations. Each descendant will implement a different strategy - for distributing the algorithm across multiple devices/machines. - Furthermore, these changes can be hidden inside the specific layers - and other library classes that need special treatment to run in a - distributed setting, so that most users' model definition code can - run unchanged. The `DistributionStrategy` API works the same way - with eager and graph execution. - - First let's introduce a few high-level concepts: - - * _Data parallelism_ is where we run multiple copies of the model - on different slices of the input data. This is in contrast to - _model parallelism_ where we divide up a single copy of a model - across multiple devices. - Note: we only support data parallelism for now, but - hope to add support for model parallelism in the future. - * A _replica_ is one copy of the model, running on one slice of the - input data. - * _Synchronous_, or more commonly _sync_, training is where the - updates from each replica are aggregated together before updating - the model variables. This is in contrast to _asynchronous_, or - _async_ training, where each replica updates the model variables - independently. - * Furthermore you might run your computation on multiple devices - on one machine (or "host"), or on multiple machines/hosts. - If you are running on multiple machines, you might have a - single master host that drives computation across all of them, - or you might have multiple clients driving the computation - asynchronously. - - To distribute an algorithm, we might use some of these ingredients: - - * Parameter servers: These are hosts that hold a single copy of - parameters/variables. All replicas that want to operate on a variable - retrieve it at the beginning of a step and send an update to be - applied at the end of the step. Can support either sync or async - training. - * Mirrored variables: These are variables that are copied to multiple - devices, where we keep the copies in sync by applying the same - updates to every copy. Normally would only be used with sync training. - * Reductions and Allreduce: A _reduction_ is some method of - aggregating multiple values into one value, like "sum" or - "mean". If doing sync training, we will perform a reduction on the - gradients to a parameter from all replicas before applying the - update. Allreduce is an algorithm for performing a reduction on - values from multiple devices and making the result available on - all of those devices. - * In the future we will have support for TensorFlow's partitioned - variables, where a single variable is split across multiple - devices. - - We have then a few approaches we want to support: - - * Code written (as if) with no knowledge of class `DistributionStrategy`. - This code should work as before, even if some of the layers, etc. - used by that code are written to be distribution-aware. This is done - by having a default `DistributionStrategy` that gives ordinary behavior, - and by default being in a single replica context. - * Ordinary model code that you want to run using a specific - `DistributionStrategy`. This can be as simple as: - - ``` - with my_distribution.scope(): - iterator = my_distribution.distribute_dataset( - dataset).make_one_shot_iterator() - replica_train_ops = my_distribution.call_for_each_replica( - replica_fn, args=(iterator.get_next(),)) - train_op = tf.group(my_distribution.unwrap(replica_train_ops)) - ``` - - This takes an ordinary `dataset` and `replica_fn` and runs it - distributed using a particular `DistributionStrategy` in - `my_distribution`. Any variables created in `replica_fn` are created - using `my_distribution`'s policy, and library functions called by - `replica_fn` can use the `get_replica_context()` API to get enhanced - behavior in this case. - - You can also create an initializable iterator instead of a one-shot - iterator. In that case, you will need to ensure that you initialize the - iterator before calling get_next. - ``` - iterator = my_distribution.distribute_dataset( - dataset).make_initializable_iterator()) - session.run(iterator.initializer) - ``` - - * If you want to write a distributed algorithm, you may use any of - the `DistributionStrategy` APIs inside a - `with my_distribution.scope():` block of code. - - Lower-level concepts: - - * Wrapped values: In order to represent values parallel across devices - (either replicas or the devices associated with a particular value), we - wrap them in a "PerReplica" or "Mirrored" object that contains a map - from device to values. "PerReplica" is used when the value may be - different across replicas, and "Mirrored" when the value are the same. - * Unwrapping and merging: Consider calling a function `fn` on - multiple replicas, like `call_for_each_replica(fn, args=[w])` with an - argument `w` that is a wrapped value. This means `w` will have a - map taking replica device `d0` to `w0`, replica device `d1` to `w1`, - etc. `call_for_each_replica()` unwraps `w` before calling `fn`, so - it calls `fn(w0)` on `d0`, `fn(w1)` on `d1`, etc. It then merges - the return values from `fn()`, which can possibly result in - wrapped values. For example, let's say `fn()` returns a tuple with - three components: `(x, a, v0)` from replica 0, `(x, b, v1)` on replica 1, - etc. If the first component is the same object `x` from every - replica, then the first component of the merged result will also be - `x`. If the second component is different (`a`, `b`, ...) from - each replica, then the merged value will have a wrapped map from - replica device to the different values. If the third component is - the members of a mirrored variable (`v` maps `d0` to `v0`, `d1` to - `v1`, etc.), then the merged result will be that mirrored variable - (`v`). - * Replica context vs. Cross-replica context: _replica context_ is when we - are in some function that is being called once for each replica. - Otherwise we are in cross-replica context, which is useful for - calling `DistributionStrategy` methods which operate across the - replicas (like `reduce()`). By default you start in a replica context - (the default "single replica context") and then some methods can - switch you back and forth, as described below. - * Worker devices vs. parameter devices: Most replica computations will - happen on worker devices. Since we don't yet support model - parallelism, there will be one worker device per replica. When using - parameter servers (see above), the set of devices holding - variables may be different, otherwise the parameter devices might - match the worker devices. - * Non-slot devices are some subset of the parameter devices where we - put all the non-slot variables. We need to ensure that all - non-slot variables are allocated on the same device, or mirrored - across the same set of devices. If you have some variable you want - to colocate all the non-slot variables with, you can use - `colocate_vars_with()` to get the remaining non-slot variables on - the same device. Otherwise you can use `non_slot_devices()` to - pick a consistent set of devices to pass to both - `colocate_vars_with()` and `update_non_slot()`. - - When using a `DistributionStrategy`, we have a new type dimension - called _locality_ that says what values are compatible with which - APIs: - - * T: different value for each replica (e.g. a PerReplica-wrapped value). - * M: value is "mirrored" across replicas, i.e. there are copies with the - same value on each replica (e.g. a Mirrored-wrapped value). - * V(`v`): value is "mirrored" across all the devices which have a - copy of variable `v` (also a Mirrored-wrapped value, but over - parameter devices instead of worker devices). - * N: value is "mirrored" across all the "non-slot" devices - - Rules for methods with respect to locality and single-replica vs. - cross-replica context: - - * `with d.scope()`: default single-replica context -> cross-replica context - for `d` - * `with d.colocate_vars_with(v)`: in replica/cross-replica context, variables - will be created with locality V(`v`). That is, if we write - `with d.colocate_vars_with(v1): v2 = tf.get_variable(...)`, then - `v2` will have locality V(`v1`), i.e. locality V(`v2`) will equal - V(`v1`). - * `with d.colocate_vars_with(d.non_slot_devices(...))`: in - replica/cross-replica context, variables will be created with locality N - * `v = tf.get_variable(...)`: in replica/cross-replica context, creates - a variable (which by definition will have locality V(`v`), though - will match another locality if inside a `colocate_vars_with` - scope). - * `d.distribute_dataset(dataset).make_one_shot_iterator()`: in cross-replica - context, produces an iterator with locality T - * `d.broadcast(t)`: in cross-replica context, produces a value with locality M - * `d.broadcast(t, v)`: in cross-replica context, produces a value with - locality V(`v`) - * `d.call_for_each_replica(fn, ...)`: in cross-replica context, runs - `fn()` in a replica context (and so may call `get_replica_context()` and - use its API, including `merge_call()` to get back to cross-replica - context), once for each replica. May use values with locality T or - M, and any variable. - * `d.reduce(m, t, t)`: in cross-replica context, accepts t with locality T - and produces a value with locality M. - * `d.reduce(m, t, v)`: in cross-replica context, accepts t with - locality T and produces a value with locality V(`v`). - * `d.batch_reduce(m, [(t, v)]): see `d.reduce()` - * `d.update(v, fn, ...)`: in cross-replica context, runs `fn()` once - for each device `v` is copied to, all inputs should have locality - V(`v`), output will have locality V(`v`) as well. - * `d.update_non_slot(d.non_slot_devices(), fn)`: in cross-replica - context, like `d.update()` except with locality N. - * `d.read_var(v)`: Gets the (read-only) value of the variable `v` (on - the device determined by the current device scope), aggregating - across replicas for replica-local variables. Frequently, this will be - done automatically when using `v` in an expression or fetching it in - a cross-replica context, but this function can be used to force that - conversion happens at a particular point in time (for example, to - add the result of the conversion to a graph collection). - - The standard pattern for updating variables is to: - - 1. Wrap your input dataset in `d.distribute_dataset()` and create an iterator. - 2. Define each replica `d.call_for_each_replica()` up to the point of - getting a list of gradient, variable pairs. - 3. Call `d.reduce(VariableAggregation.SUM, t, v)` or `d.batch_reduce()` to sum - the gradients (with locality T) into values with locality V(`v`). - 4. Call `d.update(v)` for each variable to update its value. - - Steps 3 and 4 are done automatically by class `Optimizer` if you call - its `apply_gradients` method in a replica context. Otherwise you can - manually call its `_distributed_apply` method in a cross-replica context. - - Another thing you might want to do in the middle of your replica function - is an all-reduce of some intermediate value, using `d.reduce()` or - `d.batch_reduce()`. You simply provide the same tensor as the input and - destination. - - Layers should expect to be called in a replica context, and can use - the `get_replica_context()` function to get a `ReplicaContext` object. The - `ReplicaContext` object has a `merge_call()` method for entering - cross-replica context where you can use `reduce()` (or - `batch_reduce()`) and then optionally `update()` to update state. - - You may use this API whether or not a `DistributionStrategy` is - being used, since there is a default implementation of - `ReplicaContext` and `DistributionStrategy`. - """ - - # TODO(josh11b): Raise an exception if variable partitioning requested before - # we add support. - # TODO(josh11b): Also `parameter_device_index` property? - # TODO(josh11b): `map()` - # TODO(josh11b): ClusterSpec/ClusterResolver - # TODO(josh11b): Partitioned computations, state; sharding - # TODO(josh11b): Model parallelism: "replicas" with multiple devices; shuffling - # TODO(josh11b): List of replicas with their worker and parameter devices - # (where the parameter devices may overlap in the ps case). - - def __init__(self): - self._default_device = None - # This property is used to determine if we should set drop_remainder=True - # when creating Datasets from numpy array inputs. - self._require_static_shapes = False - - def scope(self): - """Returns a context manager selecting this DistributionStrategy as current. - - Inside a `with distribution_strategy.scope():` code block, this thread - will use a variable creator set by `distribution_strategy`, and will - enter its "cross-replica context". - - Returns: - A context manager. - """ - if distribution_strategy_context.has_distribution_strategy(): - _require_cross_replica_context(self) - return _SameScopeAgainContext(self) - - def creator_with_resource_vars(*args, **kwargs): - _require_distribution_strategy_scope(self) - kwargs["use_resource"] = True - return self._create_variable(*args, **kwargs) - - def distributed_getter(getter, *args, **kwargs): - if not self._allow_variable_partition(): - if kwargs.pop("partitioner", None) is not None: - tf_logging.log_first_n( - tf_logging.WARN, "Partitioned variables are disabled when using " - "current DistributionStrategy.", 1) - return getter(*args, **kwargs) - - return _CurrentDistributionContext( - self, variable_scope.variable_creator_scope(creator_with_resource_vars), - variable_scope.variable_scope( - variable_scope.get_variable_scope(), - custom_getter=distributed_getter), self._default_device) - - def _allow_variable_partition(self): - return False - - def _create_variable(self, next_creator, *args, **kwargs): - # Note: should support "colocate_with" argument. - raise NotImplementedError("must be implemented in descendants") - - def read_var(self, v): - """Reads the value of a variable. - - Returns the aggregate value of a replica-local variable, or the - (read-only) value of any other variable. - - Args: - v: A variable allocated within the scope of this `DistributionStrategy`. - - Returns: - A tensor representing the value of `v`, aggregated across replicas if - necessary. - """ - raise NotImplementedError("must be implemented in descendants") - - def colocate_vars_with(self, colocate_with_variable): - """Scope that controls which devices variables will be created on. - - No operations should be added to the graph inside this scope, it - should only be used when creating variables (some implementations - work by changing variable creation, others work by using a - tf.colocate_with() scope). - - This may only be used inside `self.scope()`. - - Example usage: - - ``` - with distribution_strategy.scope(): - var1 = tf.get_variable(...) - with distribution_strategy.colocate_vars_with(v1): - # var2 and var3 will be created on the same device(s) as var1 - var2 = tf.get_variable(...) - var3 = tf.get_variable(...) - - def fn(v1, v2, v3): - # operates on v1 from var1, v2 from var2, and v3 from var3 - - # `fn` runs on every device `v1` is on, `v2` and `v3` will be there too. - distribution_strategy.update(v1, fn, v2, v3) - ``` - - Args: - colocate_with_variable: A created in `self.scope()`. Variables created - while in the returned context manager will be on the same set of - devices as `colocate_with_variable`. - - Returns: - A context manager. - """ - def create_colocated_variable(next_creator, *args, **kwargs): - _require_distribution_strategy_scope(self) - kwargs["use_resource"] = True - kwargs["colocate_with"] = colocate_with_variable - return next_creator(*args, **kwargs) - - _require_distribution_strategy_scope(self) - return variable_scope.variable_creator_scope(create_colocated_variable) - - def _call_dataset_fn(self, dataset_fn): - result = dataset_fn() - if not isinstance(result, dataset_ops.Dataset): - raise ValueError( - "dataset_fn() must return a tf.data.Dataset when using a " - "DistributionStrategy.") - return result - - # TODO(josh11b): `PerReplicaDataset` currently only implements a few methods of - # Dataset API such as make_one_shot_iterator and make_initializable_iterator. - # Extend to implement more functionality of datasets. - def distribute_dataset(self, dataset_fn): - """Return a `dataset` split across all replicas. - - Suitable for providing input to for `call_for_each_replica()` by creating an - iterator: - - ``` - def dataset_fn(): - return tf.data.Dataset.from_tensors([[1.]]).repeat() - with distribution_strategy.scope(): - distributed_dataset = distribution_strategy.distribute_dataset(dataset_fn) - iterator = distributed_dataset.make_one_shot_iterator() - replica_results = distribution_strategy.call_for_each_replica( - replica_fn, args=(iterator.get_next(),)) - ``` - - Args: - dataset_fn: A function that returns a `tf.data.Dataset`. - - Returns: - A `PerReplicaDataset` that will produce data for each replica. - """ - raise NotImplementedError("must be implemented in descendants") - - def broadcast(self, tensor, destinations=None): - """Mirror a tensor on one device to all worker devices. - - Args: - tensor: A Tensor value to broadcast. - destinations: An optional mirrored variable, device string, or - list of device strings, specifying the destination devices - to copy `tensor` to. Defaults to `self.worker_devices`. - - Returns: - A value mirrored to `destinations` devices. - """ - # TODO(josh11b): More docstring - _require_cross_replica_context(self) - return self._broadcast(tensor, destinations) - - def _broadcast(self, tensor, destinations): - raise NotImplementedError("must be implemented in descendants") - - def initialize(self): - """Any initialization to be done before running any computations. - - In eager mode, it executes any initialization as a side effect. - In graph mode, it creates the initialization ops and returns them. - - For example, TPU initialize_system ops. - - Returns: - A list of ops to execute. - """ - return [] - - def finalize(self): - """Any final actions to be done at the end of all computations. - - In eager mode, it executes any finalize actions as a side effect. - In graph mode, it creates the finalize ops and returns them. - - For example, TPU shutdown ops. - - Returns: - A list of ops to execute. - """ - return [] - - def run_steps_on_dataset(self, fn, iterator, iterations=1, - initial_loop_values=None): - """Run `fn` with input from `iterator` for `iterations` times. - - This method can be used to run a step function for training a number of - times using input from a dataset. - - Args: - fn: function to run using this distribution strategy. The function must - have the following signature: `def fn(context, *inputs)`. - `context` is an instance of `MultiStepContext` that will be passed when - `fn` is run. `context` can be used to specify the outputs to be returned - from `fn` by calling `context.set_last_step_output`. It can also be used - to capture non tensor outputs by `context.set_non_tensor_output`. - See `MultiStepContext` documentation for more information. - `inputs` will have same type/structure as `iterator.get_next()`. If the - `iterator.get_next()` returns a tuple say `return x, y` then whose will - be unpacked and passed to the `step_fn`; and step_fn signature would - look like `def step_fn(context, x, y)`. If the iterator returns a single - value say `return x` then the value is passed as is; the step_fn - signature would look like `def step_fn(context, x)`. - Typically, `fn` will use `call_for_each_replica` method of the strategy - to distribute the computation over multiple replicas. - iterator: Iterator of a dataset that represents the input for `fn`. The - caller is responsible for initializing the iterator as needed. - iterations: (Optional) Number of iterations that `fn` should be run. - Defaults to 1. - initial_loop_values: (Optional) Initial values to be passed into the - loop that runs `fn`. Defaults to `None`. # TODO(priyag): Remove - initial_loop_values argument when we have a mechanism to infer the - outputs of `fn`. - - Returns: - Returns the `MultiStepContext` object which has the following properties, - among other things: - - run_op: An op that runs `fn` `iterations` times. - - last_step_outputs: A dictionary containing tensors set using - `context.set_last_step_output`. Evaluating this returns the value of - the tensors after the last iteration. - - non_tensor_outputs: A dictionatry containing anything that was set by - `fn` by calling `context.set_non_tensor_output`. - """ - _require_cross_replica_context(self) - return self._run_steps_on_dataset(fn, iterator, iterations, - initial_loop_values) - - def _run_steps_on_dataset(self, fn, iterator, iterations, - initial_loop_values): - raise NotImplementedError("must be implemented in descendants") - - def call_for_each_replica(self, fn, *args, **kwargs): - """Run `fn` once per replica. - - `fn` may call `tf.get_replica_context()` to access methods such as - `replica_id()` and `merge_call()`. - - `merge_call()` is used to communicate between the replicas and - re-enter the cross-replica context. All replicas pause their execution - having encountered a `merge_call()` call. After that the - `merge_fn`-function is executed. Its results are then unwrapped and - given back to each replica call. After that execution resumes until - `fn` is complete or encounters another `merge_call()`. Example: - - ```python - # Called once in "cross-replica" context. - def merge_fn(distribution, three_plus_replica_id): - # sum the values across replicas - return sum(distribution.unwrap(three_plus_replica_id)) - - # Called once per replica in `distribution`, in a "replica" context. - def fn(three): - replica_ctx = tf.get_replica_context() - v = three + replica_ctx.replica_id - # Computes the sum of the `v` values across all replicas. - s = replica_ctx.merge_call(merge_fn, args=(v,)) - return s + v - - with distribution.scope(): - # in "cross-replica" context - ... - merged_results = distribution.call_for_each_replica(fn, args=[3]) - # merged_results has the values from every replica execution of `fn`. - print(distribution.unwrap(merged_results)) # Prints a list - ``` - - Args: - fn: function to run (will be run once per replica). - args: Tuple or list with positional arguments for `fn`. - kwargs: Dict with keyword arguments for `fn`. - - Returns: - Merged return value of `fn` across all replicas. - """ - _require_cross_replica_context(self) - # Handle old *args, **kwargs, and new args=(...), kwargs={...}, to - # allow transition. - a = kwargs.pop("args", None) - if a is not None: - if args: - raise ValueError( - "Can't pass *args and args=... to call_for_each_replica") - args = a - k = kwargs.pop("kwargs", None) - if k is not None: - if kwargs: - raise ValueError( - "Can't pass **kwargs and kwargs=... to call_for_each_replica") - kwargs = k - kwargs.pop("run_concurrently", None) # Ignore old option. - return self._call_for_each_replica(fn, args, kwargs) - - def _call_for_each_replica(self, fn, args, kwargs): - raise NotImplementedError("must be implemented in descendants") - - def reduce(self, aggregation, value, destinations): - """Combine (via e.g. sum or mean) values across replicas. - - Args: - aggregation: Indicates how a variable will be aggregated. Accepted values - are `tf.VariableAggregation.SUM`, `tf.VariableAggregation.MEAN`, - `tf.VariableAggregation.ONLY_FIRST_REPLICA`. - value: A per-replica value with one value per replica. - destinations: A mirrored variable, a per-replica tensor, a device string, - or list of device strings. The return value will be copied to all - destination devices (or all the devices where the `destinations` value - resides). To perform an all-reduction, pass `value` to `destinations`. - - Returns: - A value mirrored to `destinations`. - """ - # TODO(josh11b): More docstring - # TODO(josh11b): Return an unwrapped value if colocate_with is a - # single device. - _require_cross_replica_context(self) - assert aggregation in [ - variable_scope.VariableAggregation.SUM, - variable_scope.VariableAggregation.MEAN, - variable_scope.VariableAggregation.ONLY_FIRST_REPLICA - ] - return self._reduce(aggregation, value, destinations) - - def _reduce(self, aggregation, value, destinations): - raise NotImplementedError("must be implemented in descendants") - - def batch_reduce(self, aggregation, value_destination_pairs): - """Combine multiple `reduce` calls into one for faster execution. - - Args: - aggregation: Indicates how a variable will be aggregated. Accepted values - are `tf.VariableAggregation.SUM`, `tf.VariableAggregation.MEAN`, - `tf.VariableAggregation.ONLY_FIRST_REPLICA`. - value_destination_pairs: A sequence of (value, destinations) - pairs. See `reduce()` for a description. - - Returns: - A list of mirrored values, one per pair in `value_destination_pairs`. - """ - # TODO(josh11b): More docstring - _require_cross_replica_context(self) - assert aggregation in [ - variable_scope.VariableAggregation.SUM, - variable_scope.VariableAggregation.MEAN, - variable_scope.VariableAggregation.ONLY_FIRST_REPLICA - ] - return self._batch_reduce(aggregation, value_destination_pairs) - - def _batch_reduce(self, aggregation, value_destination_pairs): - return [ - self.reduce(aggregation, t, destinations=v) - for t, v in value_destination_pairs - ] - - def update(self, var, fn, *args, **kwargs): - """Run `fn` to update `var` using inputs mirrored to the same devices. - - If `var` is mirrored across multiple devices, then this implements - logic like: - - ``` - results = {} - for device, v in var: - with tf.device(device): - # *args and **kwargs will be unwrapped if they are mirrored. - results[device] = fn(v, *args, **kwargs) - return merged(results) - ``` - - Otherwise this returns `fn(var, *args, **kwargs)` colocated with `var`. - - Neither `*args` nor `**kwargs` may contain per-replica values. - If they contain mirrored values, they will be unwrapped before - calling `fn`. - - Args: - var: Variable, possibly mirrored to multiple devices, to operate on. - fn: Function to call. Should take the variable as the first argument. - *args: Additional positional arguments to pass to `fn()`. - **kwargs: Keyword arguments to pass to `fn()`. If "grouped=False" is - specified, the return value will be unwrapped. - - Returns: - By default, the merged return value of `fn` across all replicas. The - merged result has dependencies to make sure that if it is evaluated at - all, the side effects (updates) will happen on every replica. If instead - "grouped=False" is specified, this function will return a nest of lists - where each list has an element per replica, and the caller is responsible - for ensuring all elements are executed. - """ - _require_cross_replica_context(self) - options = {"grouped": kwargs.pop("grouped", True)} - return self._update(var, options, fn, *args, **kwargs) - - def _update(self, var, options, fn, *args, **kwargs): - raise NotImplementedError("must be implemented in descendants") - - def update_non_slot(self, colocate_with, fn, *args, **kwargs): - """Runs `fn(*args, **kwargs)` on `colocate_with` devices. - - Args: - colocate_with: The return value of `non_slot_devices()`. - fn: Function to execute. - *args: Positional arguments to pass to `fn()`. - **kwargs: Keyword arguments to pass to `fn()`. If "grouped=False" is - specified, the return value will be unwrapped and the caller is - responsible for ensuring all elements are executed. - - Returns: - Return value of `fn`, possibly merged across devices. - """ - _require_cross_replica_context(self) - options = {"grouped": kwargs.pop("grouped", True)} - return self._update_non_slot(colocate_with, options, fn, *args, **kwargs) - - def _update_non_slot(self, colocate_with, options, fn, *args, **kwargs): - raise NotImplementedError("must be implemented in descendants") - - def unwrap(self, value): - """Returns the list of all per-replica values contained in `value`. - - Args: - value: A value returned by `call_for_each_replica()` or a variable - created in `scope()`. - - Returns: - A list of values contained in `value`. If `value` represents a single - value, this returns `[value].` - """ - return self._unwrap(value) - - def value_container(self, value): - """Returns the container that this per-replica `value` belongs to. - - Args: - value: A value returned by `call_for_each_replica()` or a variable - created in `scope()`. - - Returns: - A container that `value` belongs to. - If value does not belong to any container (including the case of - container having been destroyed), returns the value itself. - `value in unwrap(value_container(value))` will always be true. - """ - raise NotImplementedError("must be implemented in descendants") - - def _unwrap(self, distributed_value): - raise NotImplementedError("must be implemented in descendants") - - def group(self, value, name=None): - """Shortcut for `tf.group(distribution.unwrap(value))`.""" - value = nest.flatten(self.unwrap(value)) - - if len(value) != 1 or name is not None: - return control_flow_ops.group(value, name=name) - # Special handling for the common case of one op. - v, = value - if hasattr(v, "op"): - v = v.op - return v - - @property - def require_static_shapes(self): - return self._require_static_shapes - - @property - def num_replicas(self): - """Returns number of replicas, for purposes of averaging across replicas. - - DEPRECATED: use `num_replicas_in_sync` instead. - """ - raise NotImplementedError("must be implemented in descendants") - - @property - def num_replicas_in_sync(self): - """Returns number of replicas over which gradients are aggregated.""" - raise NotImplementedError("must be implemented in descendants") - - @property - def worker_devices(self): - """Returns the list of devices used to run `call_for_each_replica()` calls. - """ - # TODO(josh11b): More docstring - raise NotImplementedError("must be implemented in descendants") - - @property - def parameter_devices(self): - """Returns the list of devices used for variable and `update` placement.""" - # TODO(josh11b): More docstring - raise NotImplementedError("must be implemented in descendants") - - def non_slot_devices(self, var_list): - """Device(s) for non-slot variables. - - Create variables on these devices in a - `with colocate_vars_with(non_slot_devices(...)):` block. - Update those using `update_non_slot()`. - - Args: - var_list: The list of variables being optimized, needed with the - default `DistributionStrategy`. - """ - raise NotImplementedError("must be implemented in descendants") - - @property - def worker_device_index(self): - """An object mapping worker device to an id. - - This might be passed as an argument to `call_for_each_replica()`, as in: - - ``` - with distribution_strategy.scope(): - - def fn(device_id): - # device_id is an integer. `fn` is being executed on device: - # distribution_strategy.worker_devices[device_id]. - - distribution_strategy.call_for_each_replica( - fn, distribution_strategy.worker_device_index) - ``` - - Returns: - An index object, or the integer 0 if there is only a single replica. - """ - _require_cross_replica_context(self) - return self._worker_device_index() - - def _worker_device_index(self): - raise NotImplementedError("must be implemented in descendants") - - @property - def between_graph(self): - """Whether the strategy uses between-graph replication or not. - - This is expected to return a constant value that will not be changed - throughout its life cycle. - """ - raise NotImplementedError("must be implemented in descendants") - - def configure(self, - session_config=None, - cluster_spec=None, - task_type=None, - task_id=None): - """Configures the strategy class.""" - del session_config, cluster_spec, task_type, task_id - - @property - def should_init(self): - """Whether initialization is needed.""" - raise NotImplementedError("must be implemented in descendants") - - @property - def should_checkpoint(self): - """Whether checkpointing is needed.""" - raise NotImplementedError("must be implemented in descendants") - - @property - def should_save_summary(self): - """Whether saving summaries is needed.""" - raise NotImplementedError("must be implemented in descendants") - - -# A note about the difference between the context managers -# `ReplicaContext` (defined here) and `_CurrentDistributionContext` -# (defined above) used by `DistributionStrategy.scope()`: -# -# * a ReplicaContext is only present during a `call_for_each_replica()` -# call (except during a `merge_run` call) and in such a scope it -# will be returned by calls to `get_replica_context()`. Implementers of new -# DistributionStrategy descendants will frequently also need to -# define a descendant of ReplicaContext, and are responsible for -# entering and exiting this context. -# -# * DistributionStrategy.scope() sets up a variable_creator scope that -# changes variable creation calls (e.g. to make mirrored -# variables). This is intended as an outer scope that users enter once -# around their model creation and graph definition. There is no -# anticipated need to define descendants of _CurrentDistributionContext. -# It sets the current DistributionStrategy for purposes of -# `get_distribution_strategy()` and `has_distribution_strategy()` -# and switches the thread mode to a "cross-replica context". -class ReplicaContext(object): - """DistributionStrategy API inside a `call_for_each_replica()` call.""" - - def __init__(self, distribution_strategy, replica_id): - self._distribution_strategy = distribution_strategy - self._thread_context = distribution_strategy_context._InReplicaThreadMode( # pylint: disable=protected-access - self) - self._replica_id = replica_id - - def __enter__(self): - _push_per_thread_mode(self._thread_context) - - def __exit__(self, exception_type, exception_value, traceback): - _pop_per_thread_mode() - - def merge_call(self, merge_fn, *args, **kwargs): - """Merge args across replicas and run `merge_fn` in a cross-replica context. - - This allows communication and coordination when there are multiple calls - to a model function triggered by a call to - `distribution.call_for_each_replica(model_fn, ...)`. - - See `MirroredDistribution.call_for_each_replica()` for an explanation. - - Otherwise, this is equivalent to: - - ``` - distribution = get_distribution_strategy() - with cross-replica-context(distribution): - return merge_fn(distribution, *args, **kwargs) - ``` - - Args: - merge_fn: function that joins arguments from threads that are given as - PerReplica. It accepts `DistributionStrategy` object as the first - argument. - args: List or tuple with positional per-thread arguments for `merge_fn` - kwargs: Dict with keyword per-thread arguments for `merge_fn`. - - Returns: - The return value of `merge_fn`, except for `PerReplica` values which are - unpacked. - """ - require_replica_context(self) - # Handle old *args, **kwargs, and new args=(...), kwargs={...}, to - # allow transition. - a = kwargs.pop("args", None) - if a is not None: - if args: - raise ValueError( - "Can't pass *args and args=... to merge_call") - args = a - k = kwargs.pop("kwargs", None) - if k is not None: - if kwargs: - raise ValueError( - "Can't pass **kwargs and kwargs=... to merge_call") - kwargs = k - return self._merge_call(merge_fn, args, kwargs) - - def _merge_call(self, merge_fn, args, kwargs): - """Default implementation for single replica.""" - _push_per_thread_mode( # thread-local, so not needed with multiple threads - distribution_strategy_context._CrossReplicaThreadMode( # pylint: disable=protected-access - self._distribution_strategy)) - try: - return merge_fn(self._distribution_strategy, *args, **kwargs) - finally: - _pop_per_thread_mode() - - @property - def num_replicas(self): - """Returns number of replicas, for purposes of averaging across replicas.""" - return self._distribution_strategy.num_replicas - - @property - def num_replicas_in_sync(self): - """Returns number of replicas over which gradients are aggregated.""" - return self._distribution_strategy.num_replicas_in_sync - - @property - def replica_id(self): - """Which replica is being defined, a number from 0 to `num_replicas - 1`.""" - require_replica_context(self) - return self._replica_id - - @property - def distribution_strategy(self): - """The current `DistributionStrategy` object.""" - return self._distribution_strategy - - @property - def device(self): - """BEING DELETED: use .devices instead.""" - raise RuntimeError("Use .devices instead") - - @property - def devices(self): - """The devices this replica is to be executed on, as a list of strings.""" - require_replica_context(self) - return [device_util.current()] - - # TODO(josh11b): Implement `start_all_reduce(method, t)` for efficient - # all-reduce. It would return a function returning the result of reducing `t` - # across all replicas. The caller would wait to call this function until they - # needed the reduce result, allowing an efficient implementation: - # * With eager execution, the reduction could be performed asynchronously - # in the background, not blocking until the result was needed. - # * When constructing a graph, it could batch up all reduction requests up - # to that point that the first result is needed. Most likely this can be - # implemented in terms of `merge_call()` and `batch_reduce()`. - -# ------------------------------------------------------------------------------ - - -class _DefaultDistributionStrategy(DistributionStrategy): - """Default `DistributionStrategy` if none is explicitly selected.""" - - def scope(self): - """Context manager setting a variable creator and `self` as current.""" - if distribution_strategy_context.has_distribution_strategy(): - raise RuntimeError("Must not nest DistributionStrategy scopes.") - - def creator(next_creator, *args, **kwargs): - _require_distribution_strategy_scope(self) - return next_creator(*args, **kwargs) - - return _CurrentDistributionContext( - self, variable_scope.variable_creator_scope(creator)) - - def colocate_vars_with(self, colocate_with_variable): - """Does not require `self.scope`.""" - _require_distribution_strategy_scope(self) - return ops.colocate_with(colocate_with_variable) - - def distribute_dataset(self, dataset_fn): - return self._call_dataset_fn(dataset_fn) - - def _broadcast(self, tensor, destinations): - if destinations is None: - return tensor - else: - raise NotImplementedError("TODO") - - def _call_for_each_replica(self, fn, args, kwargs): - with ReplicaContext(self, replica_id=0): - return fn(*args, **kwargs) - - def _reduce(self, aggregation, value, destinations): - # TODO(josh11b): Use destinations? - del aggregation, destinations - return value - - def _update(self, var, options, fn, *args, **kwargs): - # The implementations of _update() and _update_non_slot() are identical - # except _update() passes `var` as the first argument to `fn()`. - return self._update_non_slot(var, options, fn, var, *args, **kwargs) - - def _update_non_slot(self, colocate_with, options, fn, *args, **kwargs): - should_group = options.pop("grouped") - assert not options # Validate that we are processing all of the options. - # TODO(josh11b): Figure out what we should be passing to UpdateContext() - # once that value is used for something. - with ops.colocate_with(colocate_with), UpdateContext(colocate_with): - result = fn(*args, **kwargs) - if should_group: - return result - else: - return nest.map_structure(self._unwrap, result) - - def read_var(self, replica_local_var): - return array_ops.identity(replica_local_var) - - def _unwrap(self, distributed_value): - return [distributed_value] - - def value_container(self, value): - return value - - @property - def num_replicas(self): - return 1 - - @property - def num_replicas_in_sync(self): - return 1 - - @property - def worker_devices(self): - raise RuntimeError( - "worker_devices() method unsupported by _DefaultDistributionStrategy.") - - @property - def parameter_devices(self): - raise RuntimeError("parameter_devices() method unsupported by " - "_DefaultDistributionStrategy.") - - def non_slot_devices(self, var_list): - return min(var_list, key=lambda x: x.name) - - def _worker_device_index(self): - raise RuntimeError("worker_device_index() method unsupported by " - "_DefaultDistributionStrategy.") - - -# ------------------------------------------------------------------------------ -# We haven't yet implemented deserialization for DistributedVariables. -# So here we catch any attempts to deserialize variables -# when using distribution strategies. -# pylint: disable=protected-access -_original_from_proto = resource_variable_ops._from_proto_fn - - -def _from_proto_fn(v, import_scope=None): - if distribution_strategy_context.has_distribution_strategy(): - raise NotImplementedError( - "Deserialization of variables is not yet supported when using" - "distributed strategies.") - else: - return _original_from_proto(v, import_scope=import_scope) - -resource_variable_ops._from_proto_fn = _from_proto_fn -# pylint: enable=protected-access - - -#------------------------------------------------------------------------------- -# Shorthand for some methods from distribution_strategy_context. -_push_per_thread_mode = distribution_strategy_context._push_per_thread_mode # pylint: disable=protected-access -_get_per_thread_mode = distribution_strategy_context._get_per_thread_mode # pylint: disable=protected-access -_pop_per_thread_mode = distribution_strategy_context._pop_per_thread_mode # pylint: disable=protected-access +# pylint: disable=wildcard-import +from tensorflow.python.distribute.distribute_lib import * diff --git a/tensorflow/python/training/distribution_strategy_context.py b/tensorflow/python/training/distribution_strategy_context.py index 278f35b97e..7391bf3b22 100644 --- a/tensorflow/python/training/distribution_strategy_context.py +++ b/tensorflow/python/training/distribution_strategy_context.py @@ -12,195 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Utility to get distribution strategy related contexts.""" +"""Deprecated, please use ../distribute/distribution_strategy_context.py.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function -from tensorflow.python.framework import ops -from tensorflow.python.util.lazy_loader import LazyLoader - - -# There is a circular dependency between this and `distribute` module. So we -# load it lazily to workaround this. -distribute_lib = LazyLoader( - "distribute_lib", globals(), - "tensorflow.python.training.distribute") - -# ------------------------------------------------------------------------------ -# Internal API for setting the current thread mode as being either in a -# replica or cross-replica context for a particular distribution strategy. - - -class _ThreadMode(object): - - def __init__(self, dist, cross, replica): - self.distribution_strategy = dist - self.cross_replica_context = cross - self.replica_context = replica - - -class _CrossReplicaThreadMode(_ThreadMode): - - def __init__(self, distribution_strategy): - _ThreadMode.__init__( - self, distribution_strategy, distribution_strategy, None) - - -class _InReplicaThreadMode(_ThreadMode): - - def __init__(self, replica_ctx): - _ThreadMode.__init__( - self, replica_ctx.distribution_strategy, None, replica_ctx) - - -def _push_per_thread_mode(context): - ops.get_default_graph()._distribution_strategy_stack.append(context) # pylint: disable=protected-access - - -def _pop_per_thread_mode(): - ops.get_default_graph()._distribution_strategy_stack.pop(-1) # pylint: disable=protected-access - - -class _DefaultReplicaThreadMode(_ThreadMode): - """Type of default value returned by `_get_per_thread_mode()`. - - Used when the thread-local stack is empty. - """ - - def __init__(self): - _ThreadMode.__init__(self, _get_default_distribution_strategy(), None, - _get_default_replica_context()) - - -def _get_per_thread_mode(): - try: - return ops.get_default_graph()._distribution_strategy_stack[-1] # pylint: disable=protected-access - except (AttributeError, IndexError): - return _get_default_replica_mode() - - -# ------------------------------------------------------------------------------ -# Public API for accessing the current thread mode - - -def get_replica_context(): - """Returns the current ReplicaContext or None if in a cross-replica context. - - Note that execution: - - 1. starts in the default (single-replica) replica context (this function - will return the default ReplicaContext object); - 2. switches to cross-replica context (in which case this will return - None) when entering a `with DistributionStrategy.scope():` block; - 3. switches to a (non-default) replica context inside - `call_for_each_replica(fn, ...)`; - 4. if `fn` calls `get_replica_context()->merge_call(merge_fn, ...)`, then - inside `merge_fn` you are back in the cross-replica context (and again - this function will return None). - - Note that you can also go directly from step 1 to 4 to switch to a - cross-replica context for the default `DistributionStrategy`. You may - also switch from the cross-replica context of 4 to a replica context by - calling `call_for_each_replica()`, jumping back to step 3. - - Most `DistributionStrategy` methods may only be executed in - a cross-replica context, in a replica context you should use the - `ReplicaContext` API instead. - - Returns: - The current `ReplicaContext` object when in a replica context scope, - else None. - - Exactly one of `get_replica_context()` and `get_cross_replica_context()` - will return None in a particular block. - """ - return _get_per_thread_mode().replica_context - - -def get_cross_replica_context(): - """Returns the current DistributionStrategy if in a cross-replica context. - - Note that execution: - - 1. starts in the default (single-replica) replica context; - 2. switches to cross-replica context when entering a - `with DistributionStrategy.scope():` block; - 3. switches to a (non-default) replica context inside - `call_for_each_replica(fn, ...)`; - 4. if `fn` calls `get_replica_context()->merge_call(merge_fn, ...)`, then - inside `merge_fn` you are back in the cross-replica context. - - Note that you can also go directly from step 1 to 4 to switch to a - cross-replica context for the default `DistributionStrategy`. You may - also switch from the cross-replica context of 4 to a replica context by - calling `call_for_each_replica()`, jumping back to step 3. - - Most `DistributionStrategy` methods may only be executed in - a cross-replica context. - - Returns: - Returns the current `DistributionStrategy` object in a cross-replica - context, or None. - - Exactly one of `get_replica_context()` and `get_cross_replica_context()` - will return None in a particular block. - """ - return _get_per_thread_mode().cross_replica_context - - -def get_distribution_strategy(): - """Returns the current `DistributionStrategy` object. - - Prefer to use `get_replica_context()` or `get_cross_replica_context()` - instead when possible. - - Returns: - A `DistributionStrategy` object. Inside a - `with distribution_strategy.scope()` block, it returns - `distribution_strategy`, otherwise it returns the default - (single-replica) `DistributionStrategy` object. - """ - return _get_per_thread_mode().distribution_strategy - - -def has_distribution_strategy(): - """Return if there is a current non-default `DistributionStrategy`. - - Returns: - True if inside a `with distribution_strategy.scope():`. - """ - return get_distribution_strategy() is not _get_default_distribution_strategy() - - -# ------------------------------------------------------------------------------ -# Defaults that are used when no distribution strategy is explicitly created. -# We create them lazily in a function so that we can workaround the circular -# dependency on distribute_lib. See lazy loader at the top of this file. - -_defaults = { - "distribution_strategy": None, - "replica_context": None, - "replica_mode": None -} - - -def _get_default_distribution_strategy(): - if _defaults["distribution_strategy"] is None: - _defaults["distribution_strategy"] = ( - distribute_lib._DefaultDistributionStrategy()) # pylint: disable=protected-access - return _defaults["distribution_strategy"] - - -def _get_default_replica_context(): - if _defaults["replica_context"] is None: - _defaults["replica_context"] = distribute_lib.ReplicaContext( - _get_default_distribution_strategy(), replica_id=0) - return _defaults["replica_context"] - - -def _get_default_replica_mode(): - if _defaults["replica_mode"] is None: - _defaults["replica_mode"] = _DefaultReplicaThreadMode() - return _defaults["replica_mode"] +# pylint: disable=wildcard-import +from tensorflow.python.distribute.distribution_strategy_context import * diff --git a/tensorflow/python/training/evaluation.py b/tensorflow/python/training/evaluation.py index 2c4eb02d53..a10178f8cf 100644 --- a/tensorflow/python/training/evaluation.py +++ b/tensorflow/python/training/evaluation.py @@ -230,7 +230,7 @@ def _evaluate_once(checkpoint_path, hooks = list(hooks or []) if eval_ops is not None: - if any([isinstance(h, _MultiStepStopAfterNEvalsHook) for h in hooks]): + if any(isinstance(h, _MultiStepStopAfterNEvalsHook) for h in hooks): steps_per_run_variable = \ basic_session_run_hooks.get_or_create_steps_per_run_variable() update_eval_step = state_ops.assign_add( diff --git a/tensorflow/python/training/ftrl.py b/tensorflow/python/training/ftrl.py index 2fafc9a2d8..a2ef3c76b4 100644 --- a/tensorflow/python/training/ftrl.py +++ b/tensorflow/python/training/ftrl.py @@ -25,7 +25,7 @@ from tensorflow.python.training import training_ops from tensorflow.python.util.tf_export import tf_export -@tf_export("train.FtrlOptimizer") +@tf_export(v1=["train.FtrlOptimizer"]) class FtrlOptimizer(optimizer.Optimizer): """Optimizer that implements the FTRL algorithm. diff --git a/tensorflow/python/training/ftrl_test.py b/tensorflow/python/training/ftrl_test.py index 15c50bc878..39b299c64a 100644 --- a/tensorflow/python/training/ftrl_test.py +++ b/tensorflow/python/training/ftrl_test.py @@ -23,6 +23,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import embedding_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import resource_variable_ops @@ -54,7 +55,7 @@ class FtrlOptimizerTest(test.TestCase): update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) self.assertAllClose([0.0, 0.0], v0_val) self.assertAllClose([0.0, 0.0], v1_val) @@ -62,18 +63,21 @@ class FtrlOptimizerTest(test.TestCase): for _ in range(3): update.run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) self.assertAllCloseAccordingToType( np.array([-2.60260963, -4.29698515]), v0_val) self.assertAllCloseAccordingToType( np.array([-0.28432083, -0.56694895]), v1_val) + @test_util.run_deprecated_v1 def testFtrlWithoutRegularization(self): self.doTestFtrlwithoutRegularization(use_resource=False) + @test_util.run_deprecated_v1 def testResourceFtrlWithoutRegularization(self): self.doTestFtrlwithoutRegularization(use_resource=True) + @test_util.run_deprecated_v1 def testFtrlwithoutRegularization2(self): for dtype in [dtypes.half, dtypes.float32]: with self.cached_session() as sess: @@ -90,19 +94,20 @@ class FtrlOptimizerTest(test.TestCase): update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) self.assertAllCloseAccordingToType([1.0, 2.0], v0_val) self.assertAllCloseAccordingToType([4.0, 3.0], v1_val) # Run 3 steps FTRL for _ in range(3): update.run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) self.assertAllCloseAccordingToType( np.array([-2.55607247, -3.98729396]), v0_val) self.assertAllCloseAccordingToType( np.array([-0.28232238, -0.56096673]), v1_val) + @test_util.run_deprecated_v1 def testMinimizeSparseResourceVariable(self): for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -113,12 +118,15 @@ class FtrlOptimizerTest(test.TestCase): sgd_op = ftrl.FtrlOptimizer(1.0).minimize(loss) variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllCloseAccordingToType([[1.0, 2.0]], var0.eval()) + self.assertAllCloseAccordingToType([[1.0, 2.0]], self.evaluate(var0)) # Run 1 step of sgd sgd_op.run() # Validate updated params - self.assertAllCloseAccordingToType([[0, 1]], var0.eval(), atol=0.01) + self.assertAllCloseAccordingToType([[0, 1]], + self.evaluate(var0), + atol=0.01) + @test_util.run_deprecated_v1 def testFtrlWithL1(self): for dtype in [dtypes.half, dtypes.float32]: with self.cached_session() as sess: @@ -135,19 +143,20 @@ class FtrlOptimizerTest(test.TestCase): update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) self.assertAllCloseAccordingToType([1.0, 2.0], v0_val) self.assertAllCloseAccordingToType([4.0, 3.0], v1_val) # Run 10 steps FTRL for _ in range(10): update.run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) self.assertAllCloseAccordingToType( np.array([-7.66718769, -10.91273689]), v0_val) self.assertAllCloseAccordingToType( np.array([-0.93460727, -1.86147261]), v1_val) + @test_util.run_deprecated_v1 def testFtrlWithL1_L2(self): for dtype in [dtypes.half, dtypes.float32]: with self.cached_session() as sess: @@ -164,7 +173,7 @@ class FtrlOptimizerTest(test.TestCase): update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) self.assertAllCloseAccordingToType([1.0, 2.0], v0_val) self.assertAllCloseAccordingToType([4.0, 3.0], v1_val) @@ -172,12 +181,13 @@ class FtrlOptimizerTest(test.TestCase): for _ in range(10): update.run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) self.assertAllCloseAccordingToType( np.array([-0.24059935, -0.46829352]), v0_val) self.assertAllCloseAccordingToType( np.array([-0.02406147, -0.04830509]), v1_val) + @test_util.run_deprecated_v1 def testFtrlWithL1_L2_L2Shrinkage(self): """Test the new FTRL op with support for l2 shrinkage. @@ -201,7 +211,7 @@ class FtrlOptimizerTest(test.TestCase): update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) self.assertAllCloseAccordingToType([1.0, 2.0], v0_val) self.assertAllCloseAccordingToType([4.0, 3.0], v1_val) @@ -209,12 +219,13 @@ class FtrlOptimizerTest(test.TestCase): for _ in range(10): update.run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) self.assertAllCloseAccordingToType( np.array([-0.22578995, -0.44345796]), v0_val) self.assertAllCloseAccordingToType( np.array([-0.14378493, -0.13229476]), v1_val) + @test_util.run_deprecated_v1 def testFtrlWithL1_L2_L2ShrinkageSparse(self): """Tests the new FTRL op with support for l2 shrinkage on sparse grads.""" for dtype in [dtypes.half, dtypes.float32]: @@ -237,7 +248,7 @@ class FtrlOptimizerTest(test.TestCase): update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) self.assertAllCloseAccordingToType([[1.0], [2.0]], v0_val) self.assertAllCloseAccordingToType([[4.0], [3.0]], v1_val) @@ -245,10 +256,11 @@ class FtrlOptimizerTest(test.TestCase): for _ in range(10): update.run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) self.assertAllCloseAccordingToType([[-0.22578995], [2.]], v0_val) self.assertAllCloseAccordingToType([[4.], [-0.13229476]], v1_val) + @test_util.run_deprecated_v1 def testFtrlWithL2ShrinkageDoesNotChangeLrSchedule(self): """Verifies that l2 shrinkage in FTRL does not change lr schedule.""" for dtype in [dtypes.half, dtypes.float32]: @@ -273,7 +285,7 @@ class FtrlOptimizerTest(test.TestCase): update1 = opt1.apply_gradients([(grads1, var1)]) variables.global_variables_initializer().run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) self.assertAllCloseAccordingToType([1.0, 2.0], v0_val) self.assertAllCloseAccordingToType([1.0, 2.0], v1_val) @@ -282,12 +294,12 @@ class FtrlOptimizerTest(test.TestCase): update0.run() update1.run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) # var0 is experiencing L2 shrinkage so it should be smaller than var1 # in magnitude. self.assertTrue((v0_val**2 < v1_val**2).all()) - accum0 = list(sess.run(opt0._slots)["accum"].values())[0] - accum1 = list(sess.run(opt1._slots)["accum"].values())[0] + accum0 = list(self.evaluate(opt0._slots)["accum"].values())[0] + accum1 = list(self.evaluate(opt1._slots)["accum"].values())[0] # L2 shrinkage should not change how we update grad accumulator. self.assertAllCloseAccordingToType(accum0, accum1) @@ -311,7 +323,7 @@ class FtrlOptimizerTest(test.TestCase): variables.global_variables_initializer().run() sess = ops.get_default_session() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) if is_sparse: self.assertAllCloseAccordingToType([[0.0], [0.0]], v0_val) self.assertAllCloseAccordingToType([[0.0], [0.0]], v1_val) @@ -323,7 +335,7 @@ class FtrlOptimizerTest(test.TestCase): for _ in range(steps): update.run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) return v0_val, v1_val # When variables are initialized with Zero, FTRL-Proximal has two properties: @@ -333,6 +345,7 @@ class FtrlOptimizerTest(test.TestCase): # with Adagrad. # So, basing on these two properties, we test if our implementation of # FTRL-Proximal performs same updates as Adagrad or GradientDescent. + @test_util.run_deprecated_v1 def testEquivAdagradwithoutRegularization(self): for dtype in [dtypes.half, dtypes.float32]: with self.cached_session(): @@ -353,6 +366,7 @@ class FtrlOptimizerTest(test.TestCase): self.assertAllCloseAccordingToType(val0, val2) self.assertAllCloseAccordingToType(val1, val3) + @test_util.run_deprecated_v1 def testEquivSparseAdagradwithoutRegularization(self): for dtype in [dtypes.half, dtypes.float32]: with self.cached_session(): @@ -376,6 +390,7 @@ class FtrlOptimizerTest(test.TestCase): self.assertAllCloseAccordingToType(val0, val2) self.assertAllCloseAccordingToType(val1, val3) + @test_util.run_deprecated_v1 def testEquivSparseGradientDescentwithoutRegularization(self): for dtype in [dtypes.half, dtypes.float32]: with self.cached_session(): @@ -399,6 +414,7 @@ class FtrlOptimizerTest(test.TestCase): self.assertAllCloseAccordingToType(val0, val2) self.assertAllCloseAccordingToType(val1, val3) + @test_util.run_deprecated_v1 def testEquivGradientDescentwithoutRegularization(self): for dtype in [dtypes.half, dtypes.float32]: with self.cached_session(): diff --git a/tensorflow/python/training/gradient_descent.py b/tensorflow/python/training/gradient_descent.py index ef50f6315d..1a527345ef 100644 --- a/tensorflow/python/training/gradient_descent.py +++ b/tensorflow/python/training/gradient_descent.py @@ -26,7 +26,7 @@ from tensorflow.python.training import training_ops from tensorflow.python.util.tf_export import tf_export -@tf_export("train.GradientDescentOptimizer") +@tf_export(v1=["train.GradientDescentOptimizer"]) class GradientDescentOptimizer(optimizer.Optimizer): """Optimizer that implements the gradient descent algorithm. """ diff --git a/tensorflow/python/training/gradient_descent_test.py b/tensorflow/python/training/gradient_descent_test.py index 1ddea598e5..5a6c5cfa74 100644 --- a/tensorflow/python/training/gradient_descent_test.py +++ b/tensorflow/python/training/gradient_descent_test.py @@ -24,6 +24,7 @@ from tensorflow.python.eager import function from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import embedding_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import resource_variable_ops @@ -35,6 +36,7 @@ from tensorflow.python.training import gradient_descent class GradientDescentOptimizerTest(test.TestCase): + @test_util.run_deprecated_v1 def testBasic(self): for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -47,17 +49,18 @@ class GradientDescentOptimizerTest(test.TestCase): zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllCloseAccordingToType([1.0, 2.0], var0.eval()) - self.assertAllCloseAccordingToType([3.0, 4.0], var1.eval()) + self.assertAllCloseAccordingToType([1.0, 2.0], self.evaluate(var0)) + self.assertAllCloseAccordingToType([3.0, 4.0], self.evaluate(var1)) # Run 1 step of sgd sgd_op.run() # Validate updated params self.assertAllCloseAccordingToType([1.0 - 3.0 * 0.1, 2.0 - 3.0 * 0.1], - var0.eval()) + self.evaluate(var0)) self.assertAllCloseAccordingToType([3.0 - 3.0 * 0.01, 4.0 - 3.0 * 0.01], - var1.eval()) + self.evaluate(var1)) self.assertEqual(0, len(optimizer.variables())) + @test_util.run_deprecated_v1 def testBasicResourceVariable(self): for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -73,16 +76,17 @@ class GradientDescentOptimizerTest(test.TestCase): # a long-term solution for this. resources.initialize_resources([var0, var1]).run() # Fetch params to validate initial values - self.assertAllCloseAccordingToType([1.0, 2.0], var0.eval()) - self.assertAllCloseAccordingToType([3.0, 4.0], var1.eval()) + self.assertAllCloseAccordingToType([1.0, 2.0], self.evaluate(var0)) + self.assertAllCloseAccordingToType([3.0, 4.0], self.evaluate(var1)) # Run 1 step of sgd sgd_op.run() # Validate updated params self.assertAllCloseAccordingToType([1.0 - 3.0 * 0.1, 2.0 - 3.0 * 0.1], - var0.eval()) + self.evaluate(var0)) self.assertAllCloseAccordingToType([3.0 - 3.0 * 0.01, 4.0 - 3.0 * 0.01], - var1.eval()) + self.evaluate(var1)) + @test_util.run_deprecated_v1 def testBasicCallableParams(self): for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -99,16 +103,17 @@ class GradientDescentOptimizerTest(test.TestCase): # a long-term solution for this. resources.initialize_resources([var0, var1]).run() # Fetch params to validate initial values - self.assertAllCloseAccordingToType([1.0, 2.0], var0.eval()) - self.assertAllCloseAccordingToType([3.0, 4.0], var1.eval()) + self.assertAllCloseAccordingToType([1.0, 2.0], self.evaluate(var0)) + self.assertAllCloseAccordingToType([3.0, 4.0], self.evaluate(var1)) # Run 1 step of sgd sgd_op.run() # Validate updated params self.assertAllCloseAccordingToType([1.0 - 3.0 * 0.1, 2.0 - 3.0 * 0.1], - var0.eval()) + self.evaluate(var0)) self.assertAllCloseAccordingToType([3.0 - 3.0 * 0.01, 4.0 - 3.0 * 0.01], - var1.eval()) + self.evaluate(var1)) + @test_util.run_deprecated_v1 def testMinimizeResourceVariable(self): for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -124,17 +129,18 @@ class GradientDescentOptimizerTest(test.TestCase): # a long-term solution for this. resources.initialize_resources([var0, var1]).run() # Fetch params to validate initial values - self.assertAllCloseAccordingToType([[1.0, 2.0]], var0.eval()) - self.assertAllCloseAccordingToType([3.0], var1.eval()) + self.assertAllCloseAccordingToType([[1.0, 2.0]], self.evaluate(var0)) + self.assertAllCloseAccordingToType([3.0], self.evaluate(var1)) # Run 1 step of sgd sgd_op.run() # Validate updated params np_pred = 1.0 * 4.0 + 2.0 * 5.0 + 3.0 np_grad = 2 * np_pred self.assertAllCloseAccordingToType( - [[1.0 - np_grad * 4.0, 2.0 - np_grad * 5.0]], var0.eval()) - self.assertAllCloseAccordingToType([3.0 - np_grad], var1.eval()) + [[1.0 - np_grad * 4.0, 2.0 - np_grad * 5.0]], self.evaluate(var0)) + self.assertAllCloseAccordingToType([3.0 - np_grad], self.evaluate(var1)) + @test_util.run_deprecated_v1 def testMinimizeSparseResourceVariable(self): for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -151,17 +157,18 @@ class GradientDescentOptimizerTest(test.TestCase): # a long-term solution for this. variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllCloseAccordingToType([[1.0, 2.0]], var0.eval()) - self.assertAllCloseAccordingToType([3.0], var1.eval()) + self.assertAllCloseAccordingToType([[1.0, 2.0]], self.evaluate(var0)) + self.assertAllCloseAccordingToType([3.0], self.evaluate(var1)) # Run 1 step of sgd sgd_op.run() # Validate updated params np_pred = 1.0 * 4.0 + 2.0 * 5.0 + 3.0 np_grad = 2 * np_pred self.assertAllCloseAccordingToType( - [[1.0 - np_grad * 4.0, 2.0 - np_grad * 5.0]], var0.eval()) - self.assertAllCloseAccordingToType([3.0 - np_grad], var1.eval()) + [[1.0 - np_grad * 4.0, 2.0 - np_grad * 5.0]], self.evaluate(var0)) + self.assertAllCloseAccordingToType([3.0 - np_grad], self.evaluate(var1)) + @test_util.run_deprecated_v1 def testTensorLearningRate(self): for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -174,16 +181,17 @@ class GradientDescentOptimizerTest(test.TestCase): lrate).apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllCloseAccordingToType([1.0, 2.0], var0.eval()) - self.assertAllCloseAccordingToType([3.0, 4.0], var1.eval()) + self.assertAllCloseAccordingToType([1.0, 2.0], self.evaluate(var0)) + self.assertAllCloseAccordingToType([3.0, 4.0], self.evaluate(var1)) # Run 1 step of sgd sgd_op.run() # Validate updated params self.assertAllCloseAccordingToType([1.0 - 3.0 * 0.1, 2.0 - 3.0 * 0.1], - var0.eval()) + self.evaluate(var0)) self.assertAllCloseAccordingToType([3.0 - 3.0 * 0.01, 4.0 - 3.0 * 0.01], - var1.eval()) + self.evaluate(var1)) + @test_util.run_deprecated_v1 def testGradWrtRef(self): for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -193,8 +201,9 @@ class GradientDescentOptimizerTest(test.TestCase): grads_and_vars = opt.compute_gradients(vars_[0] + vars_[1], vars_) variables.global_variables_initializer().run() for grad, _ in grads_and_vars: - self.assertAllCloseAccordingToType([1.0], grad.eval()) + self.assertAllCloseAccordingToType([1.0], self.evaluate(grad)) + @test_util.run_deprecated_v1 def testWithGlobalStep(self): for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -207,17 +216,18 @@ class GradientDescentOptimizerTest(test.TestCase): zip([grads0, grads1], [var0, var1]), global_step=global_step) variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllCloseAccordingToType([1.0, 2.0], var0.eval()) - self.assertAllCloseAccordingToType([3.0, 4.0], var1.eval()) + self.assertAllCloseAccordingToType([1.0, 2.0], self.evaluate(var0)) + self.assertAllCloseAccordingToType([3.0, 4.0], self.evaluate(var1)) # Run 1 step of sgd sgd_op.run() # Validate updated params and global_step self.assertAllCloseAccordingToType([1.0 - 3.0 * 0.1, 2.0 - 3.0 * 0.1], - var0.eval()) + self.evaluate(var0)) self.assertAllCloseAccordingToType([3.0 - 3.0 * 0.01, 4.0 - 3.0 * 0.01], - var1.eval()) - self.assertAllCloseAccordingToType(1, global_step.eval()) + self.evaluate(var1)) + self.assertAllCloseAccordingToType(1, self.evaluate(global_step)) + @test_util.run_deprecated_v1 def testSparseBasic(self): for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -237,15 +247,15 @@ class GradientDescentOptimizerTest(test.TestCase): zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllCloseAccordingToType([[1.0], [2.0]], var0.eval()) - self.assertAllCloseAccordingToType([[3.0], [4.0]], var1.eval()) + self.assertAllCloseAccordingToType([[1.0], [2.0]], self.evaluate(var0)) + self.assertAllCloseAccordingToType([[3.0], [4.0]], self.evaluate(var1)) # Run 1 step of sgd sgd_op.run() # Validate updated params self.assertAllCloseAccordingToType([[1.0 - 3.0 * 0.1], [2.0]], - var0.eval()) + self.evaluate(var0)) self.assertAllCloseAccordingToType([[3.0], [4.0 - 3.0 * 0.01]], - var1.eval()) + self.evaluate(var1)) def testCapturingInDefunWhileExecutingEagerly(self): with context.eager_mode(): diff --git a/tensorflow/python/training/input_test.py b/tensorflow/python/training/input_test.py index 085b77d1d6..a3d268a017 100644 --- a/tensorflow/python/training/input_test.py +++ b/tensorflow/python/training/input_test.py @@ -28,6 +28,7 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors_impl from tensorflow.python.framework import sparse_tensor +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import variables @@ -41,6 +42,7 @@ from tensorflow.python.util import compat class MatchFilenamesOnceTest(test_lib.TestCase): + @test_util.run_deprecated_v1 def test(self): temp_dir = self.get_temp_dir() filenames = [os.path.join(temp_dir, n) for n in os.listdir(temp_dir)] @@ -58,35 +60,41 @@ class MatchFilenamesOnceTest(test_lib.TestCase): one = inp.match_filenames_once(additional[1]) variables.global_variables_initializer().run() variables.local_variables_initializer().run() - self.assertItemsEqual(map(compat.as_bytes, filenames), star.eval()) - self.assertItemsEqual(map(compat.as_bytes, additional), question.eval()) - self.assertItemsEqual([compat.as_bytes(additional[1])], one.eval()) + self.assertItemsEqual( + map(compat.as_bytes, filenames), self.evaluate(star)) + self.assertItemsEqual( + map(compat.as_bytes, additional), self.evaluate(question)) + self.assertItemsEqual([compat.as_bytes(additional[1])], + self.evaluate(one)) class LimitEpochsTest(test_lib.TestCase): + @test_util.run_deprecated_v1 def testNoLimit(self): with self.cached_session(): seven = constant_op.constant(7) seven_forever = inp.limit_epochs(seven) variables.local_variables_initializer().run() for _ in range(100): - self.assertEqual(7, seven_forever.eval()) + self.assertEqual(7, self.evaluate(seven_forever)) + @test_util.run_deprecated_v1 def testLimit(self): with self.cached_session(): love_me = constant_op.constant("Love Me") love_me_two_times = inp.limit_epochs(love_me, num_epochs=2) variables.global_variables_initializer().run() variables.local_variables_initializer().run() - self.assertEqual(b"Love Me", love_me_two_times.eval()) - self.assertEqual(b"Love Me", love_me_two_times.eval()) + self.assertEqual(b"Love Me", self.evaluate(love_me_two_times)) + self.assertEqual(b"Love Me", self.evaluate(love_me_two_times)) with self.assertRaises(errors_impl.OutOfRangeError): - love_me_two_times.eval() + self.evaluate(love_me_two_times) class InputProducerTest(test_lib.TestCase): + @test_util.run_deprecated_v1 def testNoShuffle(self): with self.cached_session(): input_tensor = [[1, 2, 3, 4], @@ -102,14 +110,16 @@ class InputProducerTest(test_lib.TestCase): threads = queue_runner_impl.start_queue_runners() # No randomness, so just see repeated copies of the input. - self.assertAllEqual(input_tensor * num_epochs, dequeue_many.eval()) + self.assertAllEqual(input_tensor * num_epochs, + self.evaluate(dequeue_many)) # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - dequeue.eval() + self.evaluate(dequeue) for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testNoShapeInference(self): with self.cached_session(): # Disable shape inference for the input. @@ -127,14 +137,15 @@ class InputProducerTest(test_lib.TestCase): threads = queue_runner_impl.start_queue_runners() # No randomness, so just see repeated copies of the input. - self.assertAllEqual(input_value * num_epochs, dequeue_many.eval()) + self.assertAllEqual(input_value * num_epochs, self.evaluate(dequeue_many)) # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - dequeue.eval() + self.evaluate(dequeue) for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testShapeError(self): input_tensor = array_ops.placeholder(dtypes.float32, None) with self.assertRaisesRegexp(ValueError, "fully defined shape"): @@ -143,6 +154,7 @@ class InputProducerTest(test_lib.TestCase): class StringInputProducerTest(test_lib.TestCase): + @test_util.run_deprecated_v1 def testNoShuffle(self): with self.cached_session(): strings = [b"to", b"be", b"or", b"not", b"to", b"be"] @@ -156,15 +168,16 @@ class StringInputProducerTest(test_lib.TestCase): threads = queue_runner_impl.start_queue_runners() # No randomness, so just see repeated copies of the input. - output = dequeue_many.eval() + output = self.evaluate(dequeue_many) self.assertAllEqual(strings * num_epochs, output) # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - dequeue.eval() + self.evaluate(dequeue) for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testShuffle(self): with self.cached_session(): strings = [b"a", b"b", b"c"] @@ -184,7 +197,7 @@ class StringInputProducerTest(test_lib.TestCase): for e in expected: frequency[e] = 0 for _ in range(num_epochs): - output = dequeue_many.eval() + output = self.evaluate(dequeue_many) key = b"".join(output) self.assertIn(key, expected) frequency[key] += 1 @@ -200,7 +213,7 @@ class StringInputProducerTest(test_lib.TestCase): # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - dequeue.eval() + self.evaluate(dequeue) for thread in threads: thread.join() @@ -210,6 +223,7 @@ class StringInputProducerTest(test_lib.TestCase): with self.assertRaises(ValueError): _ = inp.string_input_producer([]) + @test_util.run_deprecated_v1 def testNullString(self): # Runtime check for empty string list. This is slightly oblique: # The queue runner should die with an assertion error on the null @@ -224,11 +238,12 @@ class StringInputProducerTest(test_lib.TestCase): variables.local_variables_initializer().run() threads = queue_runner_impl.start_queue_runners(coord=coord) with self.assertRaises(errors_impl.OutOfRangeError): - dequeue.eval() + self.evaluate(dequeue) coord.request_stop() for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testSharedName(self): with self.cached_session(): strings = [b"to", b"be", b"or", b"not", b"to", b"be"] @@ -237,6 +252,7 @@ class StringInputProducerTest(test_lib.TestCase): self.assertProtoEquals("s: 'SHARED_NAME_XYZ'", queue.queue_ref.op.node_def.attr["shared_name"]) + @test_util.run_deprecated_v1 def testConstructionRace(self): with self.cached_session() as sess: strings = [b"to", b"be", b"or", b"not", b"to", b"be"] @@ -252,13 +268,14 @@ class StringInputProducerTest(test_lib.TestCase): # writing of the `tf.Graph` object. However, many users # write code this way, so we include this test to ensure # that we can support it. - self.assertEquals(string, sess.run(queue.dequeue())) + self.assertEquals(string, self.evaluate(queue.dequeue())) coord.request_stop() coord.join(threads) class RangeInputProducerTest(test_lib.TestCase): + @test_util.run_deprecated_v1 def testNoShuffle(self): with self.cached_session(): num_epochs = 3 @@ -272,15 +289,16 @@ class RangeInputProducerTest(test_lib.TestCase): threads = queue_runner_impl.start_queue_runners() # No randomness, so just see repeated copies of the input. - output = dequeue_many.eval() + output = self.evaluate(dequeue_many) self.assertAllEqual(list(xrange(range_size)) * num_epochs, output) # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - dequeue.eval() + self.evaluate(dequeue) for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testShuffle(self): with self.cached_session(): num_epochs = 200 @@ -300,7 +318,7 @@ class RangeInputProducerTest(test_lib.TestCase): for e in expected: frequency[e] = 0 for _ in range(num_epochs): - output = dequeue_many.eval() + output = self.evaluate(dequeue_many) key = 10 * (output[0] + 1) + (output[1] + 1) self.assertIn(key, expected) frequency[key] += 1 @@ -316,10 +334,11 @@ class RangeInputProducerTest(test_lib.TestCase): # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - dequeue.eval() + self.evaluate(dequeue) for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testSharedName(self): with self.cached_session(): range_size = 5 @@ -331,6 +350,7 @@ class RangeInputProducerTest(test_lib.TestCase): class SliceInputProducerTest(test_lib.TestCase): + @test_util.run_deprecated_v1 def testNoShuffle(self): with self.cached_session() as sess: num_epochs = 3 @@ -344,17 +364,18 @@ class SliceInputProducerTest(test_lib.TestCase): # No randomness, so just see repeated copies of the input. num_items = len(source_strings) * num_epochs - output = [sess.run(slices) for _ in range(num_items)] + output = [self.evaluate(slices) for _ in range(num_items)] out_strings, out_ints = zip(*output) self.assertAllEqual(source_strings * num_epochs, out_strings) self.assertAllEqual(source_ints * num_epochs, out_ints) # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - sess.run(slices) + self.evaluate(slices) for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testShuffle(self): with self.cached_session() as sess: num_epochs = 1200 @@ -379,7 +400,7 @@ class SliceInputProducerTest(test_lib.TestCase): for e in expected: frequency[e] = 0 for _ in range(num_epochs): - output = [sess.run(slices) for _ in range(len(source_strings))] + output = [self.evaluate(slices) for _ in range(len(source_strings))] key = b",".join([s + compat.as_bytes(str(i)) for s, i in output]) self.assertIn(key, expected) frequency[key] += 1 @@ -395,10 +416,11 @@ class SliceInputProducerTest(test_lib.TestCase): # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - sess.run(slices) + self.evaluate(slices) for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testSharedName(self): with self.cached_session(): source_strings = ["A", "B", "D", "G"] @@ -470,7 +492,7 @@ class BatchTest(test_lib.TestCase): threads = queue_runner_impl.start_queue_runners() for i in range(num_batches): - results = sess.run(batched_fetch) + results = self.evaluate(batched_fetch) self.assertAllEqual(results[0], np.arange(i * batch_size, (i + 1) * batch_size)) self.assertAllEqual( @@ -487,38 +509,43 @@ class BatchTest(test_lib.TestCase): # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - sess.run(batched_fetch) + self.evaluate(batched_fetch) for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testOneThread(self): self._testOneThreadHelper(use_dict=False) + @test_util.run_deprecated_v1 def testOneThreadDict(self): self._testOneThreadHelper(use_dict=True) + @test_util.run_deprecated_v1 def testUint32DataTypes(self): values = constant_op.constant([0, 1, 2, 3, 4, 5], dtype=dtypes.uint32) batched = inp.batch([values], batch_size=2) with self.cached_session() as sess: coord = coordinator.Coordinator() threads = queue_runner_impl.start_queue_runners(sess=sess, coord=coord) - sess.run(batched) + self.evaluate(batched) coord.request_stop() for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testUint64DataTypes(self): values = constant_op.constant([0, 1, 2, 3, 4, 5], dtype=dtypes.uint64) batched = inp.batch([values], batch_size=2) with self.cached_session() as sess: coord = coordinator.Coordinator() threads = queue_runner_impl.start_queue_runners(sess=sess, coord=coord) - sess.run(batched) + self.evaluate(batched) coord.request_stop() for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testOneThreadDynamicPad(self): with self.cached_session() as sess: batch_size = 10 @@ -535,7 +562,7 @@ class BatchTest(test_lib.TestCase): threads = queue_runner_impl.start_queue_runners() for i in range(num_batches): - results = sess.run(batched) + results = self.evaluate(batched) expected_results = np.arange(i * batch_size, (i + 1) * batch_size) max_len = expected_results[-1] self.assertAllEqual(results[0], expected_results) @@ -545,10 +572,11 @@ class BatchTest(test_lib.TestCase): # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - sess.run(batched) + self.evaluate(batched) for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testOneThreadEnqueueMany(self): with self.cached_session() as sess: batch_size = 10 @@ -567,7 +595,7 @@ class BatchTest(test_lib.TestCase): threads = queue_runner_impl.start_queue_runners() for i in range(num_batches): - results = sess.run(batched) + results = self.evaluate(batched) self.assertAllEqual(results[0], np.arange(i * batch_size, (i + 1) * batch_size)) self.assertAllEqual( @@ -580,10 +608,11 @@ class BatchTest(test_lib.TestCase): # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - sess.run(batched) + self.evaluate(batched) for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testManyThreads(self): with self.cached_session() as sess: batch_size = 10 @@ -606,7 +635,7 @@ class BatchTest(test_lib.TestCase): all_counts = [] for i in range(num_batches): - results = sess.run(batched) + results = self.evaluate(batched) tf_logging.info("Batch %d: %s", i, results[0]) self.assertEqual(len(results[0]), batch_size) self.assertAllEqual(results[0], results[1].values) @@ -620,10 +649,11 @@ class BatchTest(test_lib.TestCase): # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - sess.run(batched) + self.evaluate(batched) for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testOneThreadSmallerBatch(self): with self.cached_session() as sess: batch_size = 10 @@ -647,7 +677,7 @@ class BatchTest(test_lib.TestCase): threads = queue_runner_impl.start_queue_runners() for i in range(num_batches): - results = sess.run(batched) + results = self.evaluate(batched) self.assertAllEqual(results[0], np.arange(i * batch_size, (i + 1) * batch_size)) self.assertAllEqual( @@ -663,7 +693,7 @@ class BatchTest(test_lib.TestCase): self.assertAllEqual(results[2], [b"string"] * batch_size) # Reached the final batch with extra_elements. - results = sess.run(batched) + results = self.evaluate(batched) self.assertAllEqual(results[0], np.arange(num_batches * batch_size, num_batches * batch_size + extra_elements)) @@ -677,10 +707,11 @@ class BatchTest(test_lib.TestCase): # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - sess.run(batched) + self.evaluate(batched) for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testManyThreadsSmallerBatch(self): with self.cached_session() as sess: batch_size = 10 @@ -705,7 +736,7 @@ class BatchTest(test_lib.TestCase): all_counts = [] for i in range(num_batches): - results = sess.run(batched) + results = self.evaluate(batched) tf_logging.info("Batch %d: %s", i, results[0]) self.assertEqual(len(results[0]), batch_size) self.assertAllEqual(results[0], results[1].values) @@ -717,7 +748,7 @@ class BatchTest(test_lib.TestCase): self.assertAllEqual(results[2], [b"string"] * batch_size) # Reached the final batch with extra_elements. - results = sess.run(batched) + results = self.evaluate(batched) tf_logging.info("Last Batch: %s", results[0]) self.assertEqual(len(results[0]), extra_elements) self.assertAllEqual(results[0], results[1].values) @@ -732,10 +763,11 @@ class BatchTest(test_lib.TestCase): # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - sess.run(batched) + self.evaluate(batched) for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testSharedName(self): with self.cached_session(): batch_size = 10 @@ -753,12 +785,14 @@ class BatchTest(test_lib.TestCase): "s: 'SHARED_NAME_XYZ'", batched[0].op.inputs[0].op.node_def.attr["shared_name"]) + @test_util.run_deprecated_v1 def testCannotInferRankError(self): with self.cached_session(): x = array_ops.placeholder(dtype=dtypes.int64) with self.assertRaisesRegexp(ValueError, "Cannot infer Tensor's rank"): inp.batch([x], batch_size=2) + @test_util.run_deprecated_v1 def testBatchedSparseTensorInferredShape(self): sparse = sparse_tensor.SparseTensor( indices=[[0]], values=[1.0], dense_shape=[1]) @@ -766,6 +800,7 @@ class BatchTest(test_lib.TestCase): batched = inp.batch([sparse], batch_size=2) self.assertAllEqual((2,), batched.dense_shape.get_shape().as_list()) + @test_util.run_deprecated_v1 def testBatchedSparseTensorInferredShapeEnqueueMany(self): sparse = sparse_tensor.SparseTensor( indices=[[0]], values=[1.0], dense_shape=[1]) @@ -773,6 +808,7 @@ class BatchTest(test_lib.TestCase): batched = inp.batch([sparse], batch_size=2, enqueue_many=True) self.assertAllEqual((1,), batched.dense_shape.get_shape().as_list()) + @test_util.run_deprecated_v1 def testBatchedSparseTensorInferredShapeUnknownRank(self): sparse = sparse_tensor.SparseTensor( indices=array_ops.placeholder(dtypes.int64), @@ -782,6 +818,7 @@ class BatchTest(test_lib.TestCase): batched = inp.batch([sparse], batch_size=2) self.assertIs(None, batched.dense_shape.get_shape().num_elements()) + @test_util.run_deprecated_v1 def testBatchedSparseTensorInferredShapeUnknownRankEnqueueMany(self): sparse = sparse_tensor.SparseTensor( indices=array_ops.placeholder(dtypes.int64), @@ -791,6 +828,7 @@ class BatchTest(test_lib.TestCase): batched = inp.batch([sparse], batch_size=2, enqueue_many=True) self.assertIs(None, batched.dense_shape.get_shape().num_elements()) + @test_util.run_deprecated_v1 def testSingleElementDict(self): x = inp.batch({"c": [12, 12]}, batch_size=8) self.assertAllEqual((8, 2), x["c"].get_shape().as_list()) @@ -823,35 +861,42 @@ class BatchTest(test_lib.TestCase): threads = queue_runner_impl.start_queue_runners() for _ in range(num_batches): - results = sess.run(batched) + results = self.evaluate(batched) self.assertAllEqual([0] * batch_size, np.mod(results[0], 2)) self.assertAllEqual([0] * batch_size, np.mod(results[1].values, 2)) self.assertAllEqual([b"string"] * batch_size, results[2]) # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - sess.run(batched) + self.evaluate(batched) for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testSingleThreadKeepInput(self): self._testKeepInputHelper(1, False) + @test_util.run_deprecated_v1 def testSingleThreadKeepInputEnqueueMany(self): self._testKeepInputHelper(1, True) + @test_util.run_deprecated_v1 def testMultipleThreadKeepInput(self): self._testKeepInputHelper(5, False) + @test_util.run_deprecated_v1 def testMultipleThreadKeepInputEnqueueMany(self): self._testKeepInputHelper(5, True) + @test_util.run_deprecated_v1 def testMaybeEnqueuePerExample(self): self._testKeepInputHelper(1, True, keep_input_vector=True) + @test_util.run_deprecated_v1 def testMultipleThreadMaybeEnqueuePerExample(self): self._testKeepInputHelper(5, True, keep_input_vector=True) + @test_util.run_deprecated_v1 def testInvalidKeepInputVector(self): # Can't have vector `keep_input` with `enqueue_many=False`. with self.assertRaisesRegexp(ValueError, "`keep_input` cannot be a vector"): @@ -873,6 +918,7 @@ class BatchTest(test_lib.TestCase): batch_size=1, enqueue_many=True) + @test_util.run_deprecated_v1 def testMaybeBatchedSparseTensorInferredShape(self): sparse = sparse_tensor.SparseTensor( indices=[[0]], values=[1.0], dense_shape=[1]) @@ -880,6 +926,7 @@ class BatchTest(test_lib.TestCase): batched = inp.maybe_batch([sparse], keep_input=True, batch_size=2) self.assertAllEqual((2,), batched.dense_shape.get_shape().as_list()) + @test_util.run_deprecated_v1 def testMaybeBatchedSparseTensorInferredShapeEnqueueMany(self): sparse = sparse_tensor.SparseTensor( indices=[[0]], values=[1.0], dense_shape=[1]) @@ -888,6 +935,7 @@ class BatchTest(test_lib.TestCase): [sparse], keep_input=True, batch_size=2, enqueue_many=True) self.assertAllEqual((1,), batched.dense_shape.get_shape().as_list()) + @test_util.run_deprecated_v1 def testMaybeBatchedSparseTensorInferredShapeEnqueueManyPerExample(self): sparse = sparse_tensor.SparseTensor( indices=[[0], [0]], values=[1.0, 2.0], dense_shape=[2]) @@ -896,6 +944,7 @@ class BatchTest(test_lib.TestCase): [sparse], keep_input=[True, False], batch_size=2, enqueue_many=True) self.assertAllEqual((1,), batched.dense_shape.get_shape().as_list()) + @test_util.run_deprecated_v1 def testMaybeBatchedSparseTensorInferredShapeUnknownRank(self): sparse = sparse_tensor.SparseTensor( indices=array_ops.placeholder(dtypes.int64), @@ -905,6 +954,7 @@ class BatchTest(test_lib.TestCase): batched = inp.maybe_batch([sparse], keep_input=True, batch_size=2) self.assertIs(None, batched.dense_shape.get_shape().num_elements()) + @test_util.run_deprecated_v1 def testMaybeBatchedSparseTensorInferredShapeUnknownRankEnqueueMany(self): sparse = sparse_tensor.SparseTensor( indices=array_ops.placeholder(dtypes.int64), @@ -915,6 +965,7 @@ class BatchTest(test_lib.TestCase): [sparse], keep_input=True, batch_size=2, enqueue_many=True) self.assertIs(None, batched.dense_shape.get_shape().num_elements()) + @test_util.run_deprecated_v1 def testMaybeBatchedSparseTensorInferredShapeUnknownRankPerExample(self): sparse = sparse_tensor.SparseTensor( indices=array_ops.placeholder(dtypes.int64), @@ -925,6 +976,7 @@ class BatchTest(test_lib.TestCase): [sparse], keep_input=[True, False], batch_size=2, enqueue_many=True) self.assertIs(None, batched.dense_shape.get_shape().num_elements()) + @test_util.run_deprecated_v1 def testMaybeBatchCorrectValues(self): sparse_t = sparse_tensor.SparseTensor( indices=[[0, 1], [0, 2], [1, 0], [1, 3]], @@ -938,7 +990,7 @@ class BatchTest(test_lib.TestCase): coord = coordinator.Coordinator() threads = queue_runner_impl.start_queue_runners(coord=coord) - batched_np = batched.eval() + batched_np = self.evaluate(batched) coord.request_stop() for thread in threads: @@ -1016,7 +1068,7 @@ class BatchJoinTest(test_lib.TestCase): saw_both = 0 num_batches = (num_a + num_b) // batch_size for i in range(num_batches): - results = sess.run(batched_fetch) + results = self.evaluate(batched_fetch) self.assertEqual(3, len(results)) self.assertEqual(batch_size, len(results[0])) self.assertEqual(batch_size, len(results[2])) @@ -1047,16 +1099,19 @@ class BatchJoinTest(test_lib.TestCase): # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - sess.run(batched_fetch) + self.evaluate(batched_fetch) for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testTwoThreads(self): self._testTwoThreadsHelper(use_dict=False) + @test_util.run_deprecated_v1 def testTwoThreadsDict(self): self._testTwoThreadsHelper(use_dict=True) + @test_util.run_deprecated_v1 def testMismatchedDictKeys(self): with self.assertRaisesRegexp(ValueError, "must have the same keys"): inp.batch_join( @@ -1071,6 +1126,7 @@ class BatchJoinTest(test_lib.TestCase): }], batch_size=8) + @test_util.run_deprecated_v1 def testTwoThreadsDynamicPad(self): with self.cached_session() as sess: # Two threads, the first generates (0..69, ["a"] * 1..70). @@ -1112,7 +1168,7 @@ class BatchJoinTest(test_lib.TestCase): saw_both = 0 num_batches = (num_a + num_b) // batch_size for i in range(num_batches): - results = sess.run(batched) + results = self.evaluate(batched) self.assertEqual(2, len(results)) self.assertEqual(len(results[0]), batch_size) self.assertEqual(len(results[1]), batch_size) @@ -1144,10 +1200,11 @@ class BatchJoinTest(test_lib.TestCase): # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - sess.run(batched) + self.evaluate(batched) for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testTwoThreadsSmallerBatch(self): with self.cached_session() as sess: extra_elements = 2 @@ -1197,7 +1254,7 @@ class BatchJoinTest(test_lib.TestCase): saw_both = 0 num_batches = (num_a + num_b) // batch_size for i in range(num_batches): - results = sess.run(batched) + results = self.evaluate(batched) tf_logging.info("Batch %d: %s", i, results[0]) self.assertEqual(len(results[0]), batch_size) self.assertEqual(len(results[2]), batch_size) @@ -1217,7 +1274,7 @@ class BatchJoinTest(test_lib.TestCase): [results[0][i] for i in which_b]) # Reached the final batch with 2 * extra_elements. - results = sess.run(batched) + results = self.evaluate(batched) tf_logging.info("Last Batch: %s", results[0]) self.assertEqual(len(results[0]), 2 * extra_elements) self.assertEqual(len(results[2]), 2 * extra_elements) @@ -1245,10 +1302,11 @@ class BatchJoinTest(test_lib.TestCase): # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - sess.run(batched) + self.evaluate(batched) for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testTwoThreadsDynamicPadSmallerBatch(self): with self.cached_session() as sess: extra_elements = 2 @@ -1292,7 +1350,7 @@ class BatchJoinTest(test_lib.TestCase): saw_both = 0 num_batches = (num_a + num_b) // batch_size for i in range(num_batches): - results = sess.run(batched) + results = self.evaluate(batched) tf_logging.info("Batch %d: %s", i, results[0]) self.assertEqual(len(results[0]), batch_size) self.assertEqual(len(results[1]), batch_size) @@ -1312,7 +1370,7 @@ class BatchJoinTest(test_lib.TestCase): [results[0][i] for i in which_b]) # Reached the final batch with 2 * extra_elements. - results = sess.run(batched) + results = self.evaluate(batched) tf_logging.info("Last Batch: %s", results[0]) self.assertEqual(len(results[0]), 2 * extra_elements) self.assertEqual(len(results[1]), 2 * extra_elements) @@ -1343,10 +1401,11 @@ class BatchJoinTest(test_lib.TestCase): # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - sess.run(batched) + self.evaluate(batched) for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testSharedName(self): with self.cached_session(): batch_size = 10 @@ -1369,12 +1428,14 @@ class BatchJoinTest(test_lib.TestCase): "s: 'SHARED_NAME_XYZ'", batched[0].op.inputs[0].op.node_def.attr["shared_name"]) + @test_util.run_deprecated_v1 def testCannotInferRankError(self): with self.cached_session(): x = array_ops.placeholder(dtype=dtypes.int64) with self.assertRaisesRegexp(ValueError, "Cannot infer Tensor's rank"): inp.batch_join([[x]], batch_size=2) + @test_util.run_deprecated_v1 def testSingleElementDict(self): x = inp.batch_join([{"c": [12, 12]}], batch_size=8) self.assertAllEqual((8, 2), x["c"].get_shape().as_list()) @@ -1406,7 +1467,7 @@ class BatchJoinTest(test_lib.TestCase): threads = queue_runner_impl.start_queue_runners() for _ in range(num_batches): - results = sess.run(batched) + results = self.evaluate(batched) self.assertAllEqual( [0] * batch_size, np.mod(results[0], 2),) @@ -1417,28 +1478,35 @@ class BatchJoinTest(test_lib.TestCase): # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - sess.run(batched) + self.evaluate(batched) for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testSingleThreadKeepInput(self): self._testKeepInputHelper(1, False) + @test_util.run_deprecated_v1 def testSingleThreadKeepInputEnqueueMany(self): self._testKeepInputHelper(1, True) + @test_util.run_deprecated_v1 def testMultipleThreadKeepInput(self): self._testKeepInputHelper(5, False) + @test_util.run_deprecated_v1 def testMultipleThreadKeepInputEnqueueMany(self): self._testKeepInputHelper(5, True) + @test_util.run_deprecated_v1 def testSingleThreadKeepInputPerExample(self): self._testKeepInputHelper(1, True, keep_input_vector=True) + @test_util.run_deprecated_v1 def testMultipleThreadKeepInputPerExample(self): self._testKeepInputHelper(5, True, keep_input_vector=True) + @test_util.run_deprecated_v1 def testInvalidKeepInputVector(self): # Can't have vector `keep_input` with `enqueue_many=False`. with self.assertRaisesRegexp(ValueError, "`keep_input` cannot be a vector"): @@ -1460,6 +1528,7 @@ class BatchJoinTest(test_lib.TestCase): batch_size=1, enqueue_many=True) + @test_util.run_deprecated_v1 def testMaybeBatchedSparseTensorInferredShape(self): sparse = sparse_tensor.SparseTensor( indices=[[0]], values=[1.0], dense_shape=[1]) @@ -1467,6 +1536,7 @@ class BatchJoinTest(test_lib.TestCase): batched = inp.maybe_batch_join([[sparse]], keep_input=True, batch_size=2) self.assertAllEqual((2,), batched.dense_shape.get_shape().as_list()) + @test_util.run_deprecated_v1 def testMaybeBatchedSparseTensorInferredShapeEnqueueMany(self): sparse = sparse_tensor.SparseTensor( indices=[[0]], values=[1.0], dense_shape=[1]) @@ -1475,6 +1545,7 @@ class BatchJoinTest(test_lib.TestCase): [[sparse]], keep_input=True, batch_size=2, enqueue_many=True) self.assertAllEqual((1,), batched.dense_shape.get_shape().as_list()) + @test_util.run_deprecated_v1 def testMaybeBatchedSparseTensorInferredShapeEnqueueManyPerExample(self): sparse = sparse_tensor.SparseTensor( indices=[[0], [0]], values=[1.0, 2.0], dense_shape=[2]) @@ -1483,6 +1554,7 @@ class BatchJoinTest(test_lib.TestCase): [[sparse]], keep_input=[True, False], batch_size=2, enqueue_many=True) self.assertAllEqual((1,), batched.dense_shape.get_shape().as_list()) + @test_util.run_deprecated_v1 def testMaybeBatchedSparseTensorInferredShapeUnknownRank(self): sparse = sparse_tensor.SparseTensor( indices=array_ops.placeholder(dtypes.int64), @@ -1492,6 +1564,7 @@ class BatchJoinTest(test_lib.TestCase): batched = inp.maybe_batch_join([[sparse]], keep_input=True, batch_size=2) self.assertIs(None, batched.dense_shape.get_shape().num_elements()) + @test_util.run_deprecated_v1 def testMaybeBatchedSparseTensorInferredShapeUnknownRankEnqueueMany(self): sparse = sparse_tensor.SparseTensor( indices=array_ops.placeholder(dtypes.int64), @@ -1502,6 +1575,7 @@ class BatchJoinTest(test_lib.TestCase): [[sparse]], keep_input=True, batch_size=2, enqueue_many=True) self.assertIs(None, batched.dense_shape.get_shape().num_elements()) + @test_util.run_deprecated_v1 def testMaybeBatchedSparseTensorInferredShapeUnknownRankPerExample(self): sparse = sparse_tensor.SparseTensor( indices=array_ops.placeholder(dtypes.int64), @@ -1512,6 +1586,7 @@ class BatchJoinTest(test_lib.TestCase): [[sparse]], keep_input=[True, False], batch_size=2, enqueue_many=True) self.assertIs(None, batched.dense_shape.get_shape().num_elements()) + @test_util.run_deprecated_v1 def testMaybeBatchCorrectValues(self): sparse = sparse_tensor.SparseTensor( indices=[[0, 1], [0, 2], [1, 0], [1, 3]], @@ -1525,7 +1600,7 @@ class BatchJoinTest(test_lib.TestCase): coord = coordinator.Coordinator() threads = queue_runner_impl.start_queue_runners(coord=coord) - batched_np = batched.eval() + batched_np = self.evaluate(batched) coord.request_stop() for thread in threads: @@ -1575,7 +1650,7 @@ class ShuffleBatchTest(test_lib.TestCase): all_counts = [] for i in range(num_batches): - results = sess.run(batched_fetch) + results = self.evaluate(batched_fetch) self.assertEqual(len(results[0]), batch_size) all_counts.extend(results[0]) self.assertAllEqual( @@ -1593,16 +1668,19 @@ class ShuffleBatchTest(test_lib.TestCase): # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - sess.run(batched_fetch) + self.evaluate(batched_fetch) for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testOneThread(self): self._testOneThreadHelper(use_dict=False) + @test_util.run_deprecated_v1 def testOneThreadDict(self): self._testOneThreadHelper(use_dict=True) + @test_util.run_deprecated_v1 def testOneThreadSmallerBatch(self): with self.cached_session() as sess: batch_size = 10 @@ -1630,7 +1708,7 @@ class ShuffleBatchTest(test_lib.TestCase): all_counts = [] for _ in range(num_batches): - results = sess.run(batched_fetch) + results = self.evaluate(batched_fetch) self.assertEqual(len(results[0]), batch_size) all_counts.extend(results[0]) self.assertAllEqual( @@ -1641,7 +1719,7 @@ class ShuffleBatchTest(test_lib.TestCase): self.assertAllEqual(results[2], [b"string"] * batch_size) # Reached the final batch with extra elements. - results = sess.run(batched) + results = self.evaluate(batched) self.assertAllEqual(results[1].dense_shape, [extra_elements, 1]) self.assertAllEqual(results[2], [b"string"] * extra_elements) all_counts.extend(results[0]) @@ -1655,10 +1733,11 @@ class ShuffleBatchTest(test_lib.TestCase): # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - sess.run(batched_fetch) + self.evaluate(batched_fetch) for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testManyThreads(self): with self.cached_session() as sess: batch_size = 10 @@ -1683,7 +1762,7 @@ class ShuffleBatchTest(test_lib.TestCase): all_counts = [] for i in range(num_batches): - results = sess.run(batched) + results = self.evaluate(batched) tf_logging.info("Batch %d: %s", i, results[0]) self.assertEqual(len(results[0]), batch_size) all_counts.extend(results[0]) @@ -1702,10 +1781,11 @@ class ShuffleBatchTest(test_lib.TestCase): # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - sess.run(batched) + self.evaluate(batched) for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testManyThreadsSmallerBatch(self): with self.cached_session() as sess: batch_size = 10 @@ -1733,7 +1813,7 @@ class ShuffleBatchTest(test_lib.TestCase): all_counts = [] for i in range(num_batches): - results = sess.run(batched) + results = self.evaluate(batched) tf_logging.info("Batch %d: %s", i, results[0]) self.assertEqual(len(results[0]), batch_size) all_counts.extend(results[0]) @@ -1745,7 +1825,7 @@ class ShuffleBatchTest(test_lib.TestCase): self.assertAllEqual(results[2], [b"string"] * batch_size) # Reached the final batch with extra elements. - results = sess.run(batched) + results = self.evaluate(batched) self.assertAllEqual(results[0].shape, [extra_elements]) self.assertAllEqual(results[1].dense_shape, [extra_elements, 1]) self.assertAllEqual(results[2], [b"string"] * extra_elements) @@ -1760,10 +1840,11 @@ class ShuffleBatchTest(test_lib.TestCase): # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - sess.run(batched) + self.evaluate(batched) for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testSharedName(self): with self.cached_session(): batch_size = 10 @@ -1813,35 +1894,42 @@ class ShuffleBatchTest(test_lib.TestCase): threads = queue_runner_impl.start_queue_runners() for _ in range(num_batches): - results = sess.run(batched) + results = self.evaluate(batched) self.assertAllEqual([0] * batch_size, np.mod(results[0], 2)) self.assertAllEqual([0] * batch_size, np.mod(results[1].values, 2)) self.assertAllEqual([b"string"] * batch_size, results[2]) # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - sess.run(batched) + self.evaluate(batched) for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testSingleThreadKeepInput(self): self._testKeepInputHelper(1, False) + @test_util.run_deprecated_v1 def testSingleThreadKeepInputEnqueueMany(self): self._testKeepInputHelper(1, True) + @test_util.run_deprecated_v1 def testMultipleThreadKeepInput(self): self._testKeepInputHelper(5, False) + @test_util.run_deprecated_v1 def testMultipleThreadKeepInputEnqueueMany(self): self._testKeepInputHelper(5, True) + @test_util.run_deprecated_v1 def testSingleThreadKeepInputPerExample(self): self._testKeepInputHelper(1, True, keep_input_vector=True) + @test_util.run_deprecated_v1 def testMultipleThreadKeepInputPerExample(self): self._testKeepInputHelper(5, True, keep_input_vector=True) + @test_util.run_deprecated_v1 def testInvalidKeepInputVector(self): # Can't have vector `keep_input` with `enqueue_many=False`. with self.assertRaisesRegexp(ValueError, "`keep_input` cannot be a vector"): @@ -1860,6 +1948,7 @@ class ShuffleBatchTest(test_lib.TestCase): keep_input=array_ops.placeholder(dtypes.bool), enqueue_many=True) + @test_util.run_deprecated_v1 def testMaybeBatchedSparseTensorInferredShape(self): sparse = sparse_tensor.SparseTensor( indices=[[0]], values=[1.0], dense_shape=[1]) @@ -1867,6 +1956,7 @@ class ShuffleBatchTest(test_lib.TestCase): batched = inp.maybe_shuffle_batch([sparse], 2, 10, 1, True) self.assertAllEqual((2,), batched.dense_shape.get_shape().as_list()) + @test_util.run_deprecated_v1 def testMaybeBatchedSparseTensorInferredShapeEnqueueMany(self): sparse = sparse_tensor.SparseTensor( indices=[[0]], values=[1.0], dense_shape=[1]) @@ -1875,6 +1965,7 @@ class ShuffleBatchTest(test_lib.TestCase): [sparse], 2, 10, 1, True, enqueue_many=True) self.assertAllEqual((1,), batched.dense_shape.get_shape().as_list()) + @test_util.run_deprecated_v1 def testMaybeBatchedSparseTensorInferredShapeEnqueueManyPerExample(self): sparse = sparse_tensor.SparseTensor( indices=[[0], [0]], values=[1.0, 2.0], dense_shape=[2]) @@ -1883,6 +1974,7 @@ class ShuffleBatchTest(test_lib.TestCase): [sparse], 2, 10, 1, [True, False], enqueue_many=True) self.assertAllEqual((1,), batched.dense_shape.get_shape().as_list()) + @test_util.run_deprecated_v1 def testMaybeBatchedSparseTensorInferredShapeUnknownRank(self): sparse = sparse_tensor.SparseTensor( indices=array_ops.placeholder(dtypes.int64), @@ -1892,6 +1984,7 @@ class ShuffleBatchTest(test_lib.TestCase): batched = inp.maybe_shuffle_batch([sparse], 2, 10, 1, True) self.assertIs(None, batched.dense_shape.get_shape().num_elements()) + @test_util.run_deprecated_v1 def testMaybeBatchedSparseTensorInferredShapeUnknownRankEnqueueMany(self): sparse = sparse_tensor.SparseTensor( indices=array_ops.placeholder(dtypes.int64), @@ -1902,6 +1995,7 @@ class ShuffleBatchTest(test_lib.TestCase): [sparse], 2, 10, 1, True, enqueue_many=True) self.assertIs(None, batched.dense_shape.get_shape().num_elements()) + @test_util.run_deprecated_v1 def testMaybeBatchedSparseTensorInferredShapeUnknownRankPerExample(self): sparse = sparse_tensor.SparseTensor( indices=array_ops.placeholder(dtypes.int64), @@ -1986,7 +2080,7 @@ class ShuffleBatchJoinTest(test_lib.TestCase): saw_both = 0 num_batches = (num_a + num_b) // batch_size for i in range(num_batches): - results = sess.run(batched_fetch) + results = self.evaluate(batched_fetch) self.assertEqual(3, len(results)) self.assertEqual(len(results[0]), batch_size) self.assertEqual(len(results[2]), batch_size) @@ -2016,16 +2110,19 @@ class ShuffleBatchJoinTest(test_lib.TestCase): # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - sess.run(batched_fetch) + self.evaluate(batched_fetch) for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testTwoThreads(self): self._testTwoThreadsHelper(use_dict=False) + @test_util.run_deprecated_v1 def testTwoThreadsDict(self): self._testTwoThreadsHelper(use_dict=True) + @test_util.run_deprecated_v1 def testTwoThreadsSmallerBatch(self): with self.cached_session() as sess: # Two threads, the first generates (0..26, "a"). @@ -2078,7 +2175,7 @@ class ShuffleBatchJoinTest(test_lib.TestCase): saw_both = 0 num_batches = (num_a + num_b) // batch_size for i in range(num_batches): - results = sess.run(batched) + results = self.evaluate(batched) tf_logging.info("Batch %d: %s", i, results[0]) self.assertEqual(len(results[0]), batch_size) self.assertEqual(len(results[2]), batch_size) @@ -2098,7 +2195,7 @@ class ShuffleBatchJoinTest(test_lib.TestCase): [results[0][i] for i in which_b]) # Reached end with 2 * extra_elements left - results = sess.run(batched) + results = self.evaluate(batched) self.assertEqual(len(results[0]), 2 * extra_elements) self.assertAllEqual(results[1].dense_shape, [2 * extra_elements, 1]) self.assertEqual(len(results[2]), 2 * extra_elements) @@ -2125,10 +2222,11 @@ class ShuffleBatchJoinTest(test_lib.TestCase): # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - sess.run(batched) + self.evaluate(batched) for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testMismatchedDictKeys(self): with self.assertRaisesRegexp(ValueError, "must have the same keys"): inp.shuffle_batch_join( @@ -2146,6 +2244,7 @@ class ShuffleBatchJoinTest(test_lib.TestCase): min_after_dequeue=16, seed=223607) + @test_util.run_deprecated_v1 def testSharedName(self): with self.cached_session(): batch_size = 10 @@ -2199,35 +2298,42 @@ class ShuffleBatchJoinTest(test_lib.TestCase): threads = queue_runner_impl.start_queue_runners() for _ in range(num_batches): - results = sess.run(batched) + results = self.evaluate(batched) self.assertAllEqual([0] * batch_size, np.mod(results[0], 2)) self.assertAllEqual([0] * batch_size, np.mod(results[1].values, 2)) self.assertAllEqual([b"string"] * batch_size, results[2]) # Reached the limit. with self.assertRaises(errors_impl.OutOfRangeError): - sess.run(batched) + self.evaluate(batched) for thread in threads: thread.join() + @test_util.run_deprecated_v1 def testSingleThreadKeepInput(self): self._testKeepInputHelper(1, False) + @test_util.run_deprecated_v1 def testSingleThreadKeepInputEnqueueMany(self): self._testKeepInputHelper(1, True) + @test_util.run_deprecated_v1 def testMultipleThreadKeepInput(self): self._testKeepInputHelper(5, False) + @test_util.run_deprecated_v1 def testMultipleThreadKeepInputEnqueueMany(self): self._testKeepInputHelper(5, True) + @test_util.run_deprecated_v1 def testSingleThreadKeepInputPerExample(self): self._testKeepInputHelper(1, True, keep_input_vector=True) + @test_util.run_deprecated_v1 def testMultipleThreadKeepInputPerExample(self): self._testKeepInputHelper(5, True, keep_input_vector=True) + @test_util.run_deprecated_v1 def testInvalidKeepInputVector(self): # Can't have vector `keep_input` with `enqueue_many=False`. with self.assertRaisesRegexp(ValueError, "`keep_input` cannot be a vector"): @@ -2249,6 +2355,7 @@ class ShuffleBatchJoinTest(test_lib.TestCase): keep_input=array_ops.placeholder(dtypes.bool), enqueue_many=True) + @test_util.run_deprecated_v1 def testMaybeBatchedSparseTensorInferredShape(self): sparse = sparse_tensor.SparseTensor( indices=[[0]], values=[1.0], dense_shape=[1]) @@ -2256,6 +2363,7 @@ class ShuffleBatchJoinTest(test_lib.TestCase): batched = inp.maybe_shuffle_batch_join([[sparse]], 2, 10, 1, True) self.assertAllEqual((2,), batched.dense_shape.get_shape().as_list()) + @test_util.run_deprecated_v1 def testMaybeBatchedSparseTensorInferredShapeEnqueueMany(self): sparse = sparse_tensor.SparseTensor( indices=[[0]], values=[1.0], dense_shape=[1]) @@ -2264,6 +2372,7 @@ class ShuffleBatchJoinTest(test_lib.TestCase): [[sparse]], 2, 10, 1, True, enqueue_many=True) self.assertAllEqual((1,), batched.dense_shape.get_shape().as_list()) + @test_util.run_deprecated_v1 def testMaybeBatchedSparseTensorInferredShapeEnqueueManyPerExample(self): sparse = sparse_tensor.SparseTensor( indices=[[0], [0]], values=[1.0, 2.0], dense_shape=[2]) @@ -2272,6 +2381,7 @@ class ShuffleBatchJoinTest(test_lib.TestCase): [[sparse]], 2, 10, 1, [True, False], enqueue_many=True) self.assertAllEqual((1,), batched.dense_shape.get_shape().as_list()) + @test_util.run_deprecated_v1 def testMaybeBatchedSparseTensorInferredShapeUnknownRank(self): sparse = sparse_tensor.SparseTensor( indices=array_ops.placeholder(dtypes.int64), @@ -2281,6 +2391,7 @@ class ShuffleBatchJoinTest(test_lib.TestCase): batched = inp.maybe_shuffle_batch_join([[sparse]], 2, 10, 1, True) self.assertIs(None, batched.dense_shape.get_shape().num_elements()) + @test_util.run_deprecated_v1 def testMaybeBatchedSparseTensorInferredShapeUnknownRankEnqueueMany(self): sparse = sparse_tensor.SparseTensor( indices=array_ops.placeholder(dtypes.int64), @@ -2291,6 +2402,7 @@ class ShuffleBatchJoinTest(test_lib.TestCase): [[sparse]], 2, 10, 1, True, enqueue_many=True) self.assertIs(None, batched.dense_shape.get_shape().num_elements()) + @test_util.run_deprecated_v1 def testMaybeBatchedSparseTensorInferredShapeUnknownRankPerExample(self): sparse = sparse_tensor.SparseTensor( indices=array_ops.placeholder(dtypes.int64), diff --git a/tensorflow/python/training/learning_rate_decay.py b/tensorflow/python/training/learning_rate_decay.py index 29b5465321..c52e89db1f 100644 --- a/tensorflow/python/training/learning_rate_decay.py +++ b/tensorflow/python/training/learning_rate_decay.py @@ -100,7 +100,7 @@ def exponential_decay(learning_rate, return decayed_lr -@tf_export(v1=["train.piecewise_constant"]) +@tf_export(v1=["train.piecewise_constant_decay", "train.piecewise_constant"]) def piecewise_constant(x, boundaries, values, name=None): """Piecewise constant from boundaries and interval values. diff --git a/tensorflow/python/training/learning_rate_decay_test.py b/tensorflow/python/training/learning_rate_decay_test.py index 03a32f6ca0..9de5bc8168 100644 --- a/tensorflow/python/training/learning_rate_decay_test.py +++ b/tensorflow/python/training/learning_rate_decay_test.py @@ -61,24 +61,24 @@ class LRDecayTest(test_util.TensorFlowTestCase): self.evaluate(step.assign(100)) self.assertAllClose(self.evaluate(decayed_lr), expected, 1e-6) + @test_util.run_deprecated_v1 def testVariables(self): - with self.cached_session(): - step = variables.VariableV1(1) - assign_1 = step.assign(1) - assign_2 = step.assign(2) - assign_100 = step.assign(100) - decayed_lr = learning_rate_decay.exponential_decay(.1, step, 3, 0.96, - staircase=True) - variables.global_variables_initializer().run() - # No change to learning rate - assign_1.op.run() - self.assertAllClose(decayed_lr.eval(), .1, 1e-6) - assign_2.op.run() - self.assertAllClose(decayed_lr.eval(), .1, 1e-6) - # Decayed learning rate - assign_100.op.run() - expected = .1 * 0.96 ** (100 // 3) - self.assertAllClose(decayed_lr.eval(), expected, 1e-6) + step = variables.VariableV1(1) + assign_1 = step.assign(1) + assign_2 = step.assign(2) + assign_100 = step.assign(100) + decayed_lr = learning_rate_decay.exponential_decay( + .1, step, 3, 0.96, staircase=True) + self.evaluate(variables.global_variables_initializer()) + # No change to learning rate + self.evaluate(assign_1.op) + self.assertAllClose(self.evaluate(decayed_lr), .1, 1e-6) + self.evaluate(assign_2.op) + self.assertAllClose(self.evaluate(decayed_lr), .1, 1e-6) + # Decayed learning rate + self.evaluate(assign_100.op) + expected = .1 * 0.96**(100 // 3) + self.assertAllClose(self.evaluate(decayed_lr), expected, 1e-6) @test_util.run_in_graph_and_eager_modes def testPiecewiseConstant(self): @@ -101,6 +101,7 @@ class LRDecayTest(test_util.TensorFlowTestCase): self.assertAllClose(self.evaluate(decayed_lr), 0.001, 1e-6) @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def testPiecewiseConstantEdgeCases(self): x_int = resource_variable_ops.ResourceVariable( 0, dtype=variables.dtypes.int32) diff --git a/tensorflow/python/training/learning_rate_decay_v2.py b/tensorflow/python/training/learning_rate_decay_v2.py index 9c5e144be6..eb69feb17d 100644 --- a/tensorflow/python/training/learning_rate_decay_v2.py +++ b/tensorflow/python/training/learning_rate_decay_v2.py @@ -117,7 +117,7 @@ def exponential_decay(learning_rate, decay_rate, staircase, name) -@tf_export("train.piecewise_constant", v1=[]) +@tf_export("train.piecewise_constant_decay", v1=[]) def piecewise_constant(x, boundaries, values, name=None): """Piecewise constant from boundaries and interval values. diff --git a/tensorflow/python/training/learning_rate_decay_v2_test.py b/tensorflow/python/training/learning_rate_decay_v2_test.py index b2ac93f06f..cb96773e29 100644 --- a/tensorflow/python/training/learning_rate_decay_v2_test.py +++ b/tensorflow/python/training/learning_rate_decay_v2_test.py @@ -61,24 +61,24 @@ class LRDecayTestV2(test_util.TensorFlowTestCase): self.evaluate(step.assign(100)) self.assertAllClose(self.evaluate(decayed_lr()), expected, 1e-6) + @test_util.run_deprecated_v1 def testVariables(self): - with self.cached_session(): - step = variables.Variable(1) - assign_1 = step.assign(1) - assign_2 = step.assign(2) - assign_100 = step.assign(100) - decayed_lr = learning_rate_decay_v2.exponential_decay(.1, step, 3, 0.96, - staircase=True) - variables.global_variables_initializer().run() - # No change to learning rate - assign_1.op.run() - self.assertAllClose(decayed_lr().eval(), .1, 1e-6) - assign_2.op.run() - self.assertAllClose(decayed_lr().eval(), .1, 1e-6) - # Decayed learning rate - assign_100.op.run() - expected = .1 * 0.96 ** (100 // 3) - self.assertAllClose(decayed_lr().eval(), expected, 1e-6) + step = variables.Variable(1) + assign_1 = step.assign(1) + assign_2 = step.assign(2) + assign_100 = step.assign(100) + decayed_lr = learning_rate_decay_v2.exponential_decay( + .1, step, 3, 0.96, staircase=True) + self.evaluate(variables.global_variables_initializer()) + # No change to learning rate + self.evaluate(assign_1.op) + self.assertAllClose(self.evaluate(decayed_lr()), .1, 1e-6) + self.evaluate(assign_2.op) + self.assertAllClose(self.evaluate(decayed_lr()), .1, 1e-6) + # Decayed learning rate + self.evaluate(assign_100.op) + expected = .1 * 0.96**(100 // 3) + self.assertAllClose(self.evaluate(decayed_lr()), expected, 1e-6) @test_util.run_in_graph_and_eager_modes def testPiecewiseConstant(self): diff --git a/tensorflow/python/training/momentum.py b/tensorflow/python/training/momentum.py index 4a280e7c51..f3bc83bbfa 100644 --- a/tensorflow/python/training/momentum.py +++ b/tensorflow/python/training/momentum.py @@ -25,7 +25,7 @@ from tensorflow.python.training import training_ops from tensorflow.python.util.tf_export import tf_export -@tf_export("train.MomentumOptimizer") +@tf_export(v1=["train.MomentumOptimizer"]) class MomentumOptimizer(optimizer.Optimizer): """Optimizer that implements the Momentum algorithm. diff --git a/tensorflow/python/training/momentum_test.py b/tensorflow/python/training/momentum_test.py index 8a21c39d32..ba155fa6c6 100644 --- a/tensorflow/python/training/momentum_test.py +++ b/tensorflow/python/training/momentum_test.py @@ -160,6 +160,7 @@ class MomentumOptimizerTest(test.TestCase): self.assertStartsWith(optimizer_variables[1].name, "var3") self.assertEquals(2, len(optimizer_variables)) + @test_util.run_deprecated_v1 def testNesterovMomentum(self): for dtype in [dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -183,9 +184,10 @@ class MomentumOptimizerTest(test.TestCase): var1_np, accum1_np = self._update_nesterov_momentum_numpy(var1_np, accum1_np, 3, 2.0, 0.9) - self.assertAllClose(var0_np, var0.eval()) - self.assertAllClose(var1_np, var1.eval()) + self.assertAllClose(var0_np, self.evaluate(var0)) + self.assertAllClose(var1_np, self.evaluate(var1)) + @test_util.run_deprecated_v1 def testSparseNesterovMomentum(self): for dtype in [dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -224,8 +226,8 @@ class MomentumOptimizerTest(test.TestCase): var1_np, accum1_np = self._update_nesterov_momentum_numpy(var1_np, accum1_np, 3, 2.0, 0.9) - self.assertAllClose(var0_np, var0.eval()) - self.assertAllClose(var1_np, var1.eval()) + self.assertAllClose(var0_np, self.evaluate(var0)) + self.assertAllClose(var1_np, self.evaluate(var1)) @test_util.run_in_graph_and_eager_modes(reset_test=True) def testMinimizeSparseResourceVariable(self): @@ -280,6 +282,7 @@ class MomentumOptimizerTest(test.TestCase): self.evaluate(sgd_op) self.assertAllCloseAccordingToType([[1, 1], [0, 0]], self.evaluate(var0)) + @test_util.run_deprecated_v1 def testTensorLearningRateAndMomentum(self): for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -303,37 +306,43 @@ class MomentumOptimizerTest(test.TestCase): self.assertFalse(slot1 in variables.trainable_variables()) # Fetch params to validate initial values - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([3.0, 4.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) # Step 1: the momentum accumulators where 0. So we should see a normal # update: v -= grad * learning_rate mom_update.run() # Check that the momentum accumulators have been updated. - self.assertAllCloseAccordingToType(np.array([0.1, 0.1]), slot0.eval()) - self.assertAllCloseAccordingToType(np.array([0.01, 0.01]), slot1.eval()) + self.assertAllCloseAccordingToType( + np.array([0.1, 0.1]), self.evaluate(slot0)) + self.assertAllCloseAccordingToType( + np.array([0.01, 0.01]), self.evaluate(slot1)) # Check that the parameters have been updated. self.assertAllCloseAccordingToType( - np.array([1.0 - (0.1 * 2.0), 2.0 - (0.1 * 2.0)]), var0.eval()) + np.array([1.0 - (0.1 * 2.0), 2.0 - (0.1 * 2.0)]), + self.evaluate(var0)) self.assertAllCloseAccordingToType( - np.array([3.0 - (0.01 * 2.0), 4.0 - (0.01 * 2.0)]), var1.eval()) + np.array([3.0 - (0.01 * 2.0), 4.0 - (0.01 * 2.0)]), + self.evaluate(var1)) # Step 2: the momentum accumulators contain the previous update. mom_update.run() # Check that the momentum accumulators have been updated. self.assertAllCloseAccordingToType( - np.array([(0.9 * 0.1 + 0.1), (0.9 * 0.1 + 0.1)]), slot0.eval()) + np.array([(0.9 * 0.1 + 0.1), (0.9 * 0.1 + 0.1)]), + self.evaluate(slot0)) self.assertAllCloseAccordingToType( - np.array([(0.9 * 0.01 + 0.01), (0.9 * 0.01 + 0.01)]), slot1.eval()) + np.array([(0.9 * 0.01 + 0.01), (0.9 * 0.01 + 0.01)]), + self.evaluate(slot1)) # Check that the parameters have been updated. self.assertAllCloseAccordingToType( np.array([ 1.0 - (0.1 * 2.0) - ((0.9 * 0.1 + 0.1) * 2.0), 2.0 - (0.1 * 2.0) - ((0.9 * 0.1 + 0.1) * 2.0) - ]), var0.eval()) + ]), self.evaluate(var0)) self.assertAllCloseAccordingToType( np.array([ - 2.98 - ((0.9 * 0.01 + 0.01) * 2.0), 3.98 - ( - (0.9 * 0.01 + 0.01) * 2.0) - ]), var1.eval()) + 2.98 - ((0.9 * 0.01 + 0.01) * 2.0), + 3.98 - ((0.9 * 0.01 + 0.01) * 2.0) + ]), self.evaluate(var1)) def _dbParamsMom01(self): """Return dist-belief momentum values. @@ -434,6 +443,7 @@ class MomentumOptimizerTest(test.TestCase): # pylint: enable=line-too-long return db_grad, db_out + @test_util.run_deprecated_v1 def testLikeDistBeliefMom01(self): with self.cached_session(): db_grad, db_out = self._dbParamsMom01() @@ -445,8 +455,9 @@ class MomentumOptimizerTest(test.TestCase): variables.global_variables_initializer().run() for i in xrange(num_samples): mom_update.run(feed_dict={grads0: db_grad[i]}) - self.assertAllClose(np.array(db_out[i]), var0.eval()) + self.assertAllClose(np.array(db_out[i]), self.evaluate(var0)) + @test_util.run_deprecated_v1 def testSparse(self): for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -476,46 +487,59 @@ class MomentumOptimizerTest(test.TestCase): self.assertEquals(slot1.get_shape(), var1.get_shape()) # Fetch params to validate initial values - self.assertAllClose([0, 0], var0.eval()[0]) - self.assertAllClose([0, 0], var0.eval()[1]) - self.assertAllClose([1, 1], var1.eval()[2]) + self.assertAllClose([0, 0], self.evaluate(var0)[0]) + self.assertAllClose([0, 0], self.evaluate(var0)[1]) + self.assertAllClose([1, 1], self.evaluate(var1)[2]) # Step 1: the momentum accumulators are 0. So we should see a normal # update: v -= grad * learning_rate mom_update.run() # Check that the momentum accumulators have been updated. - self.assertAllCloseAccordingToType(np.array([0, 0]), slot0.eval()[0]) - self.assertAllCloseAccordingToType(np.array([.1, .1]), slot0.eval()[1]) self.assertAllCloseAccordingToType( - np.array([.01, .01]), slot1.eval()[2]) + np.array([0, 0]), + self.evaluate(slot0)[0]) + self.assertAllCloseAccordingToType( + np.array([.1, .1]), + self.evaluate(slot0)[1]) + self.assertAllCloseAccordingToType( + np.array([.01, .01]), + self.evaluate(slot1)[2]) # Check that the parameters have been updated. - self.assertAllCloseAccordingToType(np.array([0, 0]), var0.eval()[0]) self.assertAllCloseAccordingToType( - np.array([-(0.1 * 2.0), -(0.1 * 2.0)]), var0.eval()[1]) + np.array([0, 0]), + self.evaluate(var0)[0]) self.assertAllCloseAccordingToType( - np.array([1.0 - (0.01 * 2.0), 1.0 - (0.01 * 2.0)]), var1.eval()[2]) + np.array([-(0.1 * 2.0), -(0.1 * 2.0)]), + self.evaluate(var0)[1]) + self.assertAllCloseAccordingToType( + np.array([1.0 - (0.01 * 2.0), 1.0 - (0.01 * 2.0)]), + self.evaluate(var1)[2]) # Step 2: the momentum accumulators contain the previous update. mom_update.run() # Check that the momentum accumulators have been updated. - self.assertAllClose(np.array([0, 0]), slot0.eval()[0]) + self.assertAllClose(np.array([0, 0]), self.evaluate(slot0)[0]) self.assertAllCloseAccordingToType( - np.array([(0.9 * 0.1 + 0.1), (0.9 * 0.1 + 0.1)]), slot0.eval()[1]) + np.array([(0.9 * 0.1 + 0.1), (0.9 * 0.1 + 0.1)]), + self.evaluate(slot0)[1]) self.assertAllCloseAccordingToType( np.array([(0.9 * 0.01 + 0.01), (0.9 * 0.01 + 0.01)]), - slot1.eval()[2]) + self.evaluate(slot1)[2]) # Check that the parameters have been updated. - self.assertAllClose(np.array([0, 0]), var0.eval()[0]) + self.assertAllClose(np.array([0, 0]), self.evaluate(var0)[0]) self.assertAllCloseAccordingToType( np.array([ - -(0.1 * 2.0) - ((0.9 * 0.1 + 0.1) * 2.0), -(0.1 * 2.0) - ( - (0.9 * 0.1 + 0.1) * 2.0) - ]), var0.eval()[1]) + -(0.1 * 2.0) - ((0.9 * 0.1 + 0.1) * 2.0), + -(0.1 * 2.0) - ((0.9 * 0.1 + 0.1) * 2.0) + ]), + self.evaluate(var0)[1]) self.assertAllCloseAccordingToType( np.array([ - 0.98 - ((0.9 * 0.01 + 0.01) * 2.0), 0.98 - ( - (0.9 * 0.01 + 0.01) * 2.0) - ]), var1.eval()[2]) + 0.98 - ((0.9 * 0.01 + 0.01) * 2.0), + 0.98 - ((0.9 * 0.01 + 0.01) * 2.0) + ]), + self.evaluate(var1)[2]) + @test_util.run_deprecated_v1 def testSharing(self): for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -538,37 +562,43 @@ class MomentumOptimizerTest(test.TestCase): self.assertEquals(slot1.get_shape(), var1.get_shape()) # Fetch params to validate initial values - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([3.0, 4.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) # Step 1: the momentum accumulators where 0. So we should see a normal # update: v -= grad * learning_rate mom_update1.run() # Check that the momentum accumulators have been updated. - self.assertAllCloseAccordingToType(np.array([0.1, 0.1]), slot0.eval()) - self.assertAllCloseAccordingToType(np.array([0.01, 0.01]), slot1.eval()) + self.assertAllCloseAccordingToType( + np.array([0.1, 0.1]), self.evaluate(slot0)) + self.assertAllCloseAccordingToType( + np.array([0.01, 0.01]), self.evaluate(slot1)) # Check that the parameters have been updated. self.assertAllCloseAccordingToType( - np.array([1.0 - (0.1 * 2.0), 2.0 - (0.1 * 2.0)]), var0.eval()) + np.array([1.0 - (0.1 * 2.0), 2.0 - (0.1 * 2.0)]), + self.evaluate(var0)) self.assertAllCloseAccordingToType( - np.array([3.0 - (0.01 * 2.0), 4.0 - (0.01 * 2.0)]), var1.eval()) + np.array([3.0 - (0.01 * 2.0), 4.0 - (0.01 * 2.0)]), + self.evaluate(var1)) # Step 2: the second momentum accumulators contain the previous update. mom_update2.run() # Check that the momentum accumulators have been updated. self.assertAllCloseAccordingToType( - np.array([(0.9 * 0.1 + 0.1), (0.9 * 0.1 + 0.1)]), slot0.eval()) + np.array([(0.9 * 0.1 + 0.1), (0.9 * 0.1 + 0.1)]), + self.evaluate(slot0)) self.assertAllCloseAccordingToType( - np.array([(0.9 * 0.01 + 0.01), (0.9 * 0.01 + 0.01)]), slot1.eval()) + np.array([(0.9 * 0.01 + 0.01), (0.9 * 0.01 + 0.01)]), + self.evaluate(slot1)) # Check that the parameters have been updated. self.assertAllCloseAccordingToType( np.array([ 1.0 - (0.1 * 2.0) - ((0.9 * 0.1 + 0.1) * 2.0), 2.0 - (0.1 * 2.0) - ((0.9 * 0.1 + 0.1) * 2.0) - ]), var0.eval()) + ]), self.evaluate(var0)) self.assertAllCloseAccordingToType( np.array([ - 2.98 - ((0.9 * 0.01 + 0.01) * 2.0), 3.98 - ( - (0.9 * 0.01 + 0.01) * 2.0) - ]), var1.eval()) + 2.98 - ((0.9 * 0.01 + 0.01) * 2.0), + 3.98 - ((0.9 * 0.01 + 0.01) * 2.0) + ]), self.evaluate(var1)) if __name__ == "__main__": diff --git a/tensorflow/python/training/monitored_session.py b/tensorflow/python/training/monitored_session.py index 162fef971d..6a7d27df5c 100644 --- a/tensorflow/python/training/monitored_session.py +++ b/tensorflow/python/training/monitored_session.py @@ -54,7 +54,7 @@ _PREEMPTION_ERRORS = (errors.AbortedError, errors.UnavailableError) USE_DEFAULT = object() -@tf_export('train.Scaffold') +@tf_export(v1=['train.Scaffold']) class Scaffold(object): """Structure to create or gather pieces commonly needed to train a model. @@ -508,7 +508,7 @@ def MonitoredTrainingSession(master='', # pylint: disable=invalid-name stop_grace_period_secs=stop_grace_period_secs) -@tf_export('train.SessionCreator') +@tf_export(v1=['train.SessionCreator']) @six.add_metaclass(abc.ABCMeta) class SessionCreator(object): """A factory for tf.Session.""" @@ -519,7 +519,7 @@ class SessionCreator(object): 'create_session is not implemented for {}.'.format(self)) -@tf_export('train.ChiefSessionCreator') +@tf_export(v1=['train.ChiefSessionCreator']) class ChiefSessionCreator(SessionCreator): """Creates a tf.Session for a chief.""" @@ -571,7 +571,7 @@ class ChiefSessionCreator(SessionCreator): init_fn=self._scaffold.init_fn) -@tf_export('train.WorkerSessionCreator') +@tf_export(v1=['train.WorkerSessionCreator']) class WorkerSessionCreator(SessionCreator): """Creates a tf.Session for a worker.""" @@ -840,10 +840,18 @@ class _MonitoredSession(object): return self._coordinated_creator.tf_sess is None def _tf_sess(self): + """Return underlying tf.Session object. + + Warning: accessing the returned object in user code is likely to cause races + or "flaky tests". + + Returns: + A tf.Session object. + """ return self._coordinated_creator.tf_sess -@tf_export('train.MonitoredSession') +@tf_export(v1=['train.MonitoredSession']) class MonitoredSession(_MonitoredSession): """Session-like object that handles initialization, recovery and hooks. @@ -926,7 +934,7 @@ class MonitoredSession(_MonitoredSession): stop_grace_period_secs=stop_grace_period_secs) -@tf_export('train.SingularMonitoredSession') +@tf_export(v1=['train.SingularMonitoredSession']) class SingularMonitoredSession(_MonitoredSession): """Session-like object that handles initialization, restoring, and hooks. diff --git a/tensorflow/python/training/monitored_session_test.py b/tensorflow/python/training/monitored_session_test.py index c870d99de9..9dbcfa52b7 100644 --- a/tensorflow/python/training/monitored_session_test.py +++ b/tensorflow/python/training/monitored_session_test.py @@ -37,6 +37,7 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors_impl from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import resource_variable_ops @@ -382,6 +383,16 @@ class MonitoredTrainingSessionTest(test.TestCase): self.assertEqual(0, session.run(gstep)) +class MockExtended(object): + + def __init__(self, between_graph, should_init, should_checkpoint, + should_save_summary): + self.experimental_between_graph = between_graph + self.experimental_should_init = should_init + self.should_checkpoint = should_checkpoint + self.should_save_summary = should_save_summary + + class MockStrategy(object): def __init__(self, @@ -389,26 +400,8 @@ class MockStrategy(object): should_init=True, should_checkpoint=None, should_save_summary=None): - self._between_graph = between_graph - self._should_init = should_init - self._should_checkpoint = should_checkpoint - self._should_save_summary = should_save_summary - - @property - def between_graph(self): - return self._between_graph - - @property - def should_init(self): - return self._should_init - - @property - def should_checkpoint(self): - return self._should_checkpoint - - @property - def should_save_summary(self): - return self._should_save_summary + self.extended = MockExtended(between_graph, should_init, should_checkpoint, + should_save_summary) class MonitoredTrainingSessionWithDistributeCoordinatorTest(test.TestCase): @@ -512,6 +505,7 @@ class StopAtNSession(monitored_session._WrappedSession): class WrappedSessionTest(test.TestCase): """_WrappedSession tests.""" + @test_util.run_deprecated_v1 def test_properties(self): with self.cached_session() as sess: constant_op.constant(0.0) @@ -519,6 +513,7 @@ class WrappedSessionTest(test.TestCase): self.assertEquals(sess.graph, wrapped_sess.graph) self.assertEquals(sess.sess_str, wrapped_sess.sess_str) + @test_util.run_deprecated_v1 def test_should_stop_on_close(self): with self.cached_session() as sess: wrapped_sess = monitored_session._WrappedSession(sess) @@ -526,6 +521,7 @@ class WrappedSessionTest(test.TestCase): wrapped_sess.close() self.assertTrue(wrapped_sess.should_stop()) + @test_util.run_deprecated_v1 def test_should_stop_uses_check_stop(self): with self.cached_session() as sess: wrapped_sess = StopAtNSession(sess, 3) @@ -534,6 +530,7 @@ class WrappedSessionTest(test.TestCase): self.assertFalse(wrapped_sess.should_stop()) self.assertTrue(wrapped_sess.should_stop()) + @test_util.run_deprecated_v1 def test_should_stop_delegates_to_wrapped_session(self): with self.cached_session() as sess: wrapped_sess0 = StopAtNSession(sess, 4) @@ -552,6 +549,7 @@ class WrappedSessionTest(test.TestCase): wrapped_sess.close() self.assertTrue(wrapped_sess.should_stop()) + @test_util.run_deprecated_v1 def test_run(self): with self.cached_session() as sess: c = constant_op.constant(0) @@ -569,6 +567,7 @@ def busy_wait_for_coord_stop(coord): class CoordinatedSessionTest(test.TestCase): """_CoordinatedSession tests.""" + @test_util.run_deprecated_v1 def test_properties(self): with self.cached_session() as sess: constant_op.constant(0.0) @@ -577,6 +576,7 @@ class CoordinatedSessionTest(test.TestCase): self.assertEquals(sess.graph, coord_sess.graph) self.assertEquals(sess.sess_str, coord_sess.sess_str) + @test_util.run_deprecated_v1 def test_run(self): with self.cached_session() as sess: c = constant_op.constant(0) @@ -585,6 +585,7 @@ class CoordinatedSessionTest(test.TestCase): coord_sess = monitored_session._CoordinatedSession(sess, coord) self.assertEqual(42, coord_sess.run(v, feed_dict={c: 42})) + @test_util.run_deprecated_v1 def test_should_stop_on_close(self): with self.cached_session() as sess: coord = coordinator.Coordinator() @@ -593,6 +594,7 @@ class CoordinatedSessionTest(test.TestCase): coord_sess.close() self.assertTrue(coord_sess.should_stop()) + @test_util.run_deprecated_v1 def test_should_stop_on_coord_stop(self): with self.cached_session() as sess: coord = coordinator.Coordinator() @@ -601,6 +603,7 @@ class CoordinatedSessionTest(test.TestCase): coord.request_stop() self.assertTrue(coord_sess.should_stop()) + @test_util.run_deprecated_v1 def test_dont_request_stop_on_exception_in_main_thread(self): with self.cached_session() as sess: c = constant_op.constant(0) @@ -615,6 +618,7 @@ class CoordinatedSessionTest(test.TestCase): self.assertFalse(coord.should_stop()) self.assertFalse(coord_sess.should_stop()) + @test_util.run_deprecated_v1 def test_stop_threads_on_close_after_exception(self): with self.cached_session() as sess: c = constant_op.constant(0) @@ -662,6 +666,7 @@ class CoordinatedSessionTest(test.TestCase): self.assertTrue(coord.should_stop()) self.assertTrue(coord_sess.should_stop()) + @test_util.run_deprecated_v1 def test_propagates_exception_trace(self): assertion = control_flow_ops.Assert(False, ['This should fail.']) with self.cached_session() as sess: @@ -809,6 +814,7 @@ class RecoverableSessionTest(test.TestCase): def create_session(self): return self._sess + @test_util.run_deprecated_v1 def test_properties(self): with self.cached_session() as sess: constant_op.constant(0.0) @@ -817,6 +823,7 @@ class RecoverableSessionTest(test.TestCase): self.assertEquals(sess.graph, recoverable_sess.graph) self.assertEquals(sess.sess_str, recoverable_sess.sess_str) + @test_util.run_deprecated_v1 def test_run(self): with self.cached_session() as sess: c = constant_op.constant(0) @@ -825,6 +832,7 @@ class RecoverableSessionTest(test.TestCase): self._SessionReturner(sess)) self.assertEqual(51, recoverable_sess.run(v, feed_dict={c: 51})) + @test_util.run_deprecated_v1 def test_recovery(self): with self.cached_session() as sess: @@ -871,6 +879,7 @@ class RecoverableSessionTest(test.TestCase): with self.assertRaisesRegexp(IndexError, 'pop from empty list'): recoverable_sess.run(v, feed_dict={c: -12}) + @test_util.run_deprecated_v1 def test_recovery_from_coordinator_exception(self): with self.cached_session() as test_session: session_creator = CountingSessionCreator(test_session) @@ -896,6 +905,7 @@ class RecoverableSessionTest(test.TestCase): self.assertFalse(session.should_stop()) self.assertEqual(2, session_creator.number_of_sessions_created) + @test_util.run_deprecated_v1 def test_recovery_from_non_preemption_in_coordinator(self): with self.cached_session() as test_session: session_creator = CountingSessionCreator(test_session) @@ -925,6 +935,7 @@ class RecoverableSessionTest(test.TestCase): with self.assertRaises(errors_impl.UnknownError): session.close() + @test_util.run_deprecated_v1 def test_recovery_from_session_getting_stuck(self): with self.cached_session() as test_session: session_creator = CountingSessionCreator(test_session) @@ -949,6 +960,7 @@ class RecoverableSessionTest(test.TestCase): self.assertFalse(session.should_stop()) self.assertEqual(2, session_creator.number_of_sessions_created) + @test_util.run_deprecated_v1 def test_step_fn_recovery_from_coordinator_exception_when_run_hooks(self): with self.cached_session() as test_session: session_creator = CountingSessionCreator(test_session) @@ -979,6 +991,7 @@ class RecoverableSessionTest(test.TestCase): self.assertFalse(session.should_stop()) self.assertEqual(2, session_creator.number_of_sessions_created) + @test_util.run_deprecated_v1 def test_recovery_from_non_preemption_in_coordinator_when_run_hooks(self): with self.cached_session() as test_session: session_creator = CountingSessionCreator(test_session) @@ -1013,6 +1026,7 @@ class RecoverableSessionTest(test.TestCase): with self.assertRaises(errors_impl.UnknownError): session.close() + @test_util.run_deprecated_v1 def test_recovery_from_session_getting_stuck_when_run_hooks(self): with self.cached_session() as test_session: session_creator = CountingSessionCreator(test_session) @@ -1057,6 +1071,7 @@ class RecoverableSessionTest(test.TestCase): # exception. return session + @test_util.run_deprecated_v1 def test_step_fn_recovery_from_coordinator_exception_with_raw_session(self): with self.cached_session() as test_session: session_creator = CountingSessionCreator(test_session) @@ -1089,6 +1104,7 @@ class RecoverableSessionTest(test.TestCase): self.assertFalse(session.should_stop()) self.assertEqual(2, session_creator.number_of_sessions_created) + @test_util.run_deprecated_v1 def test_recovery_from_non_preemption_in_coordinator_with_raw_session(self): with self.cached_session() as test_session: session_creator = CountingSessionCreator(test_session) @@ -1126,6 +1142,7 @@ class RecoverableSessionTest(test.TestCase): with self.assertRaises(errors_impl.UnknownError): session.close() + @test_util.run_deprecated_v1 def test_recovery_from_session_getting_stuck_with_raw_session(self): with self.cached_session() as test_session: session_creator = CountingSessionCreator(test_session) @@ -1178,7 +1195,7 @@ class HookedSessionTest(test.TestCase): mock_run = FakeSession(sess) mon_sess = monitored_session._HookedSession(sess=mock_run, hooks=[]) a_tensor = constant_op.constant([0], name='a_tensor') - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) output = mon_sess.run(fetches=a_tensor, feed_dict='a_feed', options='an_option', @@ -1197,7 +1214,7 @@ class HookedSessionTest(test.TestCase): mon_sess = monitored_session._HookedSession( sess=sess, hooks=[mock_hook, mock_hook2]) a_tensor = constant_op.constant([0], name='a_tensor') - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) mon_sess.run(a_tensor) for hook in [mock_hook, mock_hook2]: @@ -1222,7 +1239,7 @@ class HookedSessionTest(test.TestCase): mon_sess = monitored_session._HookedSession( sess=sess, hooks=[mock_hook, mock_hook2]) constant_op.constant([0], name='a_tensor') - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) mon_sess.run(fetches='a_tensor') self.assertFalse(mon_sess.should_stop()) @@ -1242,7 +1259,7 @@ class HookedSessionTest(test.TestCase): third_tensor = constant_op.constant([10], name='third_tensor') mock_hook.request = session_run_hook.SessionRunArgs([another_tensor]) mock_hook2.request = session_run_hook.SessionRunArgs([third_tensor]) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) output = mon_sess.run(fetches=a_tensor) self.assertEqual(output, [0]) @@ -1262,7 +1279,7 @@ class HookedSessionTest(test.TestCase): None, feed_dict={a_tensor: [5]}) mock_hook2.request = session_run_hook.SessionRunArgs( None, feed_dict={b_tensor: [10]}) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) self.assertEqual(mon_sess.run(fetches=add_tensor), [15]) @@ -1280,7 +1297,7 @@ class HookedSessionTest(test.TestCase): None, feed_dict={a_tensor: [5]}) mock_hook2.request = session_run_hook.SessionRunArgs( None, feed_dict={b_tensor: [10]}) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) feed_dict = {c_tensor: [20]} self.assertEqual( @@ -1301,7 +1318,7 @@ class HookedSessionTest(test.TestCase): None, feed_dict={a_tensor: [5]}) mock_hook2.request = session_run_hook.SessionRunArgs( None, feed_dict={a_tensor: [10]}) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) with self.assertRaisesRegexp(RuntimeError, 'Same tensor is fed'): mon_sess.run(fetches=add_tensor) @@ -1319,7 +1336,7 @@ class HookedSessionTest(test.TestCase): None, feed_dict={a_tensor: [5]}) mock_hook2.request = session_run_hook.SessionRunArgs( None, feed_dict={b_tensor: [10]}) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) with self.assertRaisesRegexp(RuntimeError, 'Same tensor is fed'): mon_sess.run(fetches=add_tensor, feed_dict={b_tensor: [10]}) @@ -1451,6 +1468,7 @@ class MonitoredSessionTest(test.TestCase): # This set of tests, verifies the supervised session behavior when exceptions # are raised next to the innermost session run() call. + @test_util.run_deprecated_v1 def test_recovery(self): logdir = _test_dir(self.get_temp_dir(), 'test_recovery') with ops.Graph().as_default(): @@ -1803,6 +1821,7 @@ class MonitoredSessionTest(test.TestCase): isinstance(hook.run_metadata_list[0], config_pb2.RunMetadata)) self.assertGreater(len(hook.run_metadata_list[0].partition_graphs), 0) + @test_util.run_deprecated_v1 def test_with_statement_and_close(self): # Test case for https://github.com/tensorflow/tensorflow/issues/12224 # where close() inside the with should have a better error message. diff --git a/tensorflow/python/training/moving_averages.py b/tensorflow/python/training/moving_averages.py index fc9eb479cc..8785f9a8e7 100644 --- a/tensorflow/python/training/moving_averages.py +++ b/tensorflow/python/training/moving_averages.py @@ -17,6 +17,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +from tensorflow.python.distribute import reduce_util as ds_reduce_util from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.ops import control_flow_ops @@ -95,11 +96,11 @@ def assign_moving_average(variable, value, decay, zero_debias=True, name=None): # In a replica context, we update variable using the mean of value across # replicas. def merge_fn(strategy, v, value): - value = strategy.reduce( - variable_scope.VariableAggregation.MEAN, value, v) + value = strategy.extended.reduce_to( + ds_reduce_util.ReduceOp.MEAN, value, v) return strategy.update(v, update_fn, value) - return replica_context.merge_call(merge_fn, variable, value) + return replica_context.merge_call(merge_fn, args=(variable, value)) else: strategy = distribution_strategy_context.get_cross_replica_context() return strategy.update(variable, update_fn, value) diff --git a/tensorflow/python/training/moving_averages_test.py b/tensorflow/python/training/moving_averages_test.py index bb2fca66e3..b15f7377f0 100644 --- a/tensorflow/python/training/moving_averages_test.py +++ b/tensorflow/python/training/moving_averages_test.py @@ -35,6 +35,7 @@ from tensorflow.python.training import saver as saver_lib class MovingAveragesTest(test.TestCase): + @test_util.run_deprecated_v1 def testAssignMovingAverageWithoutZeroDebias(self): with self.cached_session(): var = variables.Variable([10.0, 11.0]) @@ -43,12 +44,13 @@ class MovingAveragesTest(test.TestCase): assign = moving_averages.assign_moving_average( var, val, decay, zero_debias=False) variables.global_variables_initializer().run() - self.assertAllClose([10.0, 11.0], var.eval()) + self.assertAllClose([10.0, 11.0], self.evaluate(var)) assign.op.run() self.assertAllClose( [10.0 * 0.25 + 1.0 * (1.0 - 0.25), 11.0 * 0.25 + 2.0 * (1.0 - 0.25)], - var.eval()) + self.evaluate(var)) + @test_util.run_deprecated_v1 def testAssignMovingAverage(self): with self.cached_session(): var = variables.Variable([0.0, 0.0]) @@ -56,12 +58,13 @@ class MovingAveragesTest(test.TestCase): decay = 0.25 assign = moving_averages.assign_moving_average(var, val, decay) variables.global_variables_initializer().run() - self.assertAllClose([0.0, 0.0], var.eval()) + self.assertAllClose([0.0, 0.0], self.evaluate(var)) assign.op.run() - self.assertAllClose([ - 1.0 * (1.0 - 0.25) / (1 - 0.25), 2.0 * (1.0 - 0.25) / (1 - 0.25) - ], var.eval()) + self.assertAllClose( + [1.0 * (1.0 - 0.25) / (1 - 0.25), 2.0 * (1.0 - 0.25) / (1 - 0.25)], + self.evaluate(var)) + @test_util.run_deprecated_v1 def testAssignMovingAverageNewNamingMultipleCalls(self): with variable_scope.variable_scope("scope1") as vs1: with variable_scope.variable_scope("scope2"): @@ -76,6 +79,7 @@ class MovingAveragesTest(test.TestCase): actual_names = [v.name for v in vs1.global_variables()] self.assertSetEqual(set(expected_names), set(actual_names)) + @test_util.run_deprecated_v1 def testAssignMovingAverageNewNamingMultipleCallsWithReuse(self): with variable_scope.variable_scope("scope1") as vs1: var = variable_scope.get_variable("Var", shape=[]) @@ -86,6 +90,7 @@ class MovingAveragesTest(test.TestCase): moving_averages.assign_moving_average(var, 0.0, 0.99) moving_averages.assign_moving_average(var, 0.0, 0.99) + @test_util.run_deprecated_v1 def testWeightedMovingAverage(self): with self.cached_session() as sess: decay = 0.5 @@ -111,6 +116,7 @@ class MovingAveragesTest(test.TestCase): denominator_2 = denominator_1 * decay + weight_2 * (1.0 - decay) self.assertAllClose(numerator_2 / denominator_2, wma_array) + @test_util.run_deprecated_v1 def testWeightedMovingAverageBfloat16(self): bfloat16 = pywrap_tensorflow.TF_bfloat16_type() with self.cached_session() as sess: @@ -179,66 +185,72 @@ class ExponentialMovingAverageTest(test.TestCase): self.assertEqual("add/ExponentialMovingAverage:0", avg2.name) # Check initial values. - self.assertAllClose(tens, var0.eval()) - self.assertAllClose(thirties, var1.eval()) - self.assertAllClose(_Repeat(10.0 + 30.0, dim), tensor2.eval()) + self.assertAllClose(tens, self.evaluate(var0)) + self.assertAllClose(thirties, self.evaluate(var1)) + self.assertAllClose(_Repeat(10.0 + 30.0, dim), self.evaluate(tensor2)) # Check that averages are initialized correctly. - self.assertAllClose(tens, avg0.eval()) - self.assertAllClose(thirties, avg1.eval()) + self.assertAllClose(tens, self.evaluate(avg0)) + self.assertAllClose(thirties, self.evaluate(avg1)) # Note that averages of Tensor's initialize to zeros_like since no value # of the Tensor is known because the Op has not been run (yet). - self.assertAllClose(_Repeat(0.0, dim), avg2.eval()) + self.assertAllClose(_Repeat(0.0, dim), self.evaluate(avg2)) # Update the averages and check. update.run() dk = actual_decay expected = _Repeat(10.0 * dk + 10.0 * (1 - dk), dim) - self.assertAllClose(expected, avg0.eval()) + self.assertAllClose(expected, self.evaluate(avg0)) expected = _Repeat(30.0 * dk + 30.0 * (1 - dk), dim) - self.assertAllClose(expected, avg1.eval()) + self.assertAllClose(expected, self.evaluate(avg1)) expected = _Repeat(0.0 * dk + (10.0 + 30.0) * (1 - dk) / _Scale(dk, 1), dim) - self.assertAllClose(expected, avg2.eval()) + self.assertAllClose(expected, self.evaluate(avg2)) # Again, update the averages and check. update.run() expected = _Repeat((10.0 * dk + 10.0 * (1 - dk)) * dk + 10.0 * (1 - dk), dim) - self.assertAllClose(expected, avg0.eval()) + self.assertAllClose(expected, self.evaluate(avg0)) expected = _Repeat((30.0 * dk + 30.0 * (1 - dk)) * dk + 30.0 * (1 - dk), dim) - self.assertAllClose(expected, avg1.eval()) + self.assertAllClose(expected, self.evaluate(avg1)) expected = _Repeat(((0.0 * dk + (10.0 + 30.0) * (1 - dk)) * dk + (10.0 + 30.0) * (1 - dk)) / _Scale(dk, 2), dim) - self.assertAllClose(expected, avg2.eval()) + self.assertAllClose(expected, self.evaluate(avg2)) + @test_util.run_deprecated_v1 def testAverageVariablesNoNumUpdates_Scalar(self): with self.cached_session(): ema = moving_averages.ExponentialMovingAverage(0.25) self._CheckDecay(ema, actual_decay=0.25, dim=1) + @test_util.run_deprecated_v1 def testAverageVariablesNoNumUpdates_Scalar_Debias(self): with self.cached_session(): ema = moving_averages.ExponentialMovingAverage(0.25, zero_debias=True) self._CheckDecay(ema, actual_decay=0.25, dim=1) + @test_util.run_deprecated_v1 def testAverageVariablesNoNumUpdates_Vector(self): with self.cached_session(): ema = moving_averages.ExponentialMovingAverage(0.25) self._CheckDecay(ema, actual_decay=0.25, dim=5) + @test_util.run_deprecated_v1 def testAverageVariablesNoNumUpdates_Vector_Debias(self): with self.cached_session(): ema = moving_averages.ExponentialMovingAverage(0.25, zero_debias=True) self._CheckDecay(ema, actual_decay=0.25, dim=5) + @test_util.run_deprecated_v1 def testAverageVariablesNumUpdates_Scalar(self): with self.cached_session(): # With num_updates 1, the decay applied is 0.1818 ema = moving_averages.ExponentialMovingAverage(0.25, num_updates=1) self._CheckDecay(ema, actual_decay=0.181818, dim=1) + @test_util.run_deprecated_v1 def testAverageVariablesNumUpdates_Scalar_Debias(self): with self.cached_session(): # With num_updates 1, the decay applied is 0.1818 @@ -246,12 +258,14 @@ class ExponentialMovingAverageTest(test.TestCase): 0.25, num_updates=1, zero_debias=True) self._CheckDecay(ema, actual_decay=0.181818, dim=1) + @test_util.run_deprecated_v1 def testAverageVariablesNumUpdates_Vector(self): with self.cached_session(): # With num_updates 1, the decay applied is 0.1818 ema = moving_averages.ExponentialMovingAverage(0.25, num_updates=1) self._CheckDecay(ema, actual_decay=0.181818, dim=5) + @test_util.run_deprecated_v1 def testAverageVariablesNumUpdates_Vector_Debias(self): with self.cached_session(): # With num_updates 1, the decay applied is 0.1818 @@ -259,6 +273,7 @@ class ExponentialMovingAverageTest(test.TestCase): 0.25, num_updates=1, zero_debias=True) self._CheckDecay(ema, actual_decay=0.181818, dim=5) + @test_util.run_deprecated_v1 def testAverageVariablesWithControlDeps(self): with self.cached_session() as sess: v0 = variables.Variable(0, name="v0") @@ -274,16 +289,17 @@ class ExponentialMovingAverageTest(test.TestCase): self.assertEqual([], v1_avg.value().op.control_inputs) self.assertEqual([], v1_avg.value().op.control_inputs) # We should be able to initialize v1_avg before v0. - sess.run(v1_avg.initializer) - sess.run(v0.initializer) - self.assertEqual([10.0], sess.run(v1_avg)) + self.evaluate(v1_avg.initializer) + self.evaluate(v0.initializer) + self.assertEqual([10.0], self.evaluate(v1_avg)) # running ema_op should add to v0 (in addition to updating v1_avg) - sess.run(assign_to_v1) - sess.run(ema_op) - self.assertEqual(1, sess.run(v0)) - self.assertEqual([17.5], sess.run(v1_avg)) + self.evaluate(assign_to_v1) + self.evaluate(ema_op) + self.assertEqual(1, self.evaluate(v0)) + self.assertEqual([17.5], self.evaluate(v1_avg)) @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def testBasicEager(self): v0 = variables.Variable(1.0) v1 = variables.Variable(2.0) @@ -339,9 +355,11 @@ class ExponentialMovingAverageTest(test.TestCase): self.assertEqual(ema.average(v1).op.name, ema.average_name(v1)) self.assertEqual(ema.average(tensor2).op.name, ema.average_name(tensor2)) + @test_util.run_deprecated_v1 def testAverageVariablesNames(self): self.averageVariablesNamesHelper(zero_debias=True) + @test_util.run_deprecated_v1 def testAverageVariablesNamesNoDebias(self): self.averageVariablesNamesHelper(zero_debias=False) @@ -387,12 +405,15 @@ class ExponentialMovingAverageTest(test.TestCase): self.assertEqual( ema.average(tensor2).op.name, ema.average_name(tensor2)) + @test_util.run_deprecated_v1 def testAverageVariablesNamesRespectScope(self): self.averageVariablesNamesRespectScopeHelper(zero_debias=True) + @test_util.run_deprecated_v1 def testAverageVariablesNamesRespectScopeNoDebias(self): self.averageVariablesNamesRespectScopeHelper(zero_debias=False) + @test_util.run_deprecated_v1 def testSubsetAverageVariablesNames(self): with self.cached_session(): v0 = variables.Variable(10.0, name="v0") @@ -421,6 +442,7 @@ class ExponentialMovingAverageTest(test.TestCase): self.assertEqual(ema.average(v1).op.name, ema.average_name(v1)) self.assertEqual(ema.average(tensor2).op.name, ema.average_name(tensor2)) + @test_util.run_deprecated_v1 def testAverageVariablesDeviceAssignment(self): with ops.device("/job:dev_v0"): v0 = variables.Variable(10.0, name="v0") @@ -451,6 +473,7 @@ class ExponentialMovingAverageTest(test.TestCase): _ = saver_lib.import_meta_graph(meta_graph) return graph_copy + @test_util.run_deprecated_v1 def testImportedGraphVariablesToRestore(self): g = ops.Graph() with g.as_default(): diff --git a/tensorflow/python/training/optimizer.py b/tensorflow/python/training/optimizer.py index 9dfa9d2afb..a9508b862a 100644 --- a/tensorflow/python/training/optimizer.py +++ b/tensorflow/python/training/optimizer.py @@ -24,6 +24,8 @@ import abc import six +from tensorflow.python.distribute import distribute_lib +from tensorflow.python.distribute import reduce_util as ds_reduce_util from tensorflow.python.eager import backprop from tensorflow.python.eager import context from tensorflow.python.framework import dtypes @@ -36,7 +38,6 @@ from tensorflow.python.ops import resource_variable_ops from tensorflow.python.ops import state_ops from tensorflow.python.ops import variable_scope from tensorflow.python.ops import variables -from tensorflow.python.training import distribute as distribute_lib from tensorflow.python.training import distribution_strategy_context as distribute_ctx from tensorflow.python.training import slot_creator from tensorflow.python.training.checkpointable import base as checkpointable @@ -200,8 +201,7 @@ def _get_processor(v): return _TensorProcessor(v) else: return _DenseResourceVariableProcessor(v) - if isinstance( - v, resource_variable_ops.ResourceVariable) and not v._in_graph_mode: # pylint: disable=protected-access + if resource_variable_ops.is_resource_variable(v) and not v._in_graph_mode: # pylint: disable=protected-access # True if and only if `v` was initialized eagerly. return _DenseResourceVariableProcessor(v) if v.op.type == "VarHandleOp": @@ -213,7 +213,7 @@ def _get_processor(v): raise NotImplementedError("Trying to optimize unsupported type ", v) -@tf_export("train.Optimizer") +@tf_export(v1=["train.Optimizer"]) class Optimizer( # Optimizers inherit from CheckpointableBase rather than Checkpointable # since they do most of their dependency management themselves (slot @@ -520,8 +520,7 @@ class Optimizer( @staticmethod def _scale_loss(loss_value): - if (distribute_lib.get_loss_reduction() == - variable_scope.VariableAggregation.MEAN): + if distribute_lib.get_loss_reduction() == ds_reduce_util.ReduceOp.MEAN: num_replicas = \ distribute_ctx.get_distribution_strategy().num_replicas_in_sync if num_replicas > 1: @@ -565,7 +564,7 @@ class Optimizer( if distribute_ctx.has_distribution_strategy(): grads_and_vars = get_filtered_grad_fn(lambda: grads_and_vars)() return distribute_ctx.get_replica_context().merge_call( - self._distributed_apply, grads_and_vars, global_step, name) + self._distributed_apply, args=(grads_and_vars, global_step, name)) # No DistributionStrategy case. grads_and_vars = tuple(grads_and_vars) # Make sure repeat iteration works. @@ -658,14 +657,16 @@ class Optimizer( Returns: An `Operation` that applies the specified gradients across all replicas. If `global_step` was not None, that operation also - increments `global_step`. + increments `global_step` """ - reduced_grads = distribution.batch_reduce( - variable_scope.VariableAggregation.SUM, grads_and_vars) + reduced_grads = distribution.extended.batch_reduce_to( + ds_reduce_util.ReduceOp.SUM, grads_and_vars) var_list = [v for _, v in grads_and_vars] grads_and_vars = zip(reduced_grads, var_list) + # Note that this is called in a cross-replica context. - self._create_slots(var_list) + with ops.init_scope(): + self._create_slots(var_list) def update(v, g): """Apply gradients to a replica variable.""" @@ -682,7 +683,13 @@ class Optimizer( "Gradient must be a Tensor, IndexedSlices, or None: %s" % g) p = _get_processor(v) - scope_name = "" if context.executing_eagerly() else v.op.name + if context.executing_eagerly() or ( + resource_variable_ops.is_resource_variable(v) and + not v._in_graph_mode): # pylint: disable=protected-access + scope_name = v.name.split(":")[0] + else: + scope_name = v.op.name + # device_policy is set because non-mirrored tensors will be read in # `update_op`. `_resource_apply_dense`, `lr_t`, `beta1_t` and `beta2_t` # is an example. @@ -695,21 +702,23 @@ class Optimizer( update_ops = [ op for grad, var in grads_and_vars - for op in distribution.update(var, update, grad, grouped=False) + for op in distribution.extended.update( + var, update, args=(grad,), group=False) ] def finish(self, update_ops): return self._finish(update_ops, "update") - non_slot_devices = distribution.non_slot_devices(var_list) - finish_updates = distribution.update_non_slot( - non_slot_devices, finish, self, update_ops, grouped=False) + non_slot_devices = distribution.extended.non_slot_devices(var_list) + finish_updates = distribution.extended.update_non_slot( + non_slot_devices, finish, args=(self, update_ops), group=False) if global_step is None: apply_updates = distribution.group(finish_updates, name=name) else: with ops.control_dependencies(finish_updates): - apply_updates = distribution.update( - global_step, state_ops.assign_add, 1, name=name) + apply_updates = distribution.extended.update( + global_step, state_ops.assign_add, args=(1,), + kwargs={"name": name}) if not context.executing_eagerly(): if isinstance(apply_updates, ops.Tensor): @@ -747,7 +756,7 @@ class Optimizer( # `_resource_apply_dense`. distributed_container = var._distributed_container() assert distributed_container is not None - if context.executing_eagerly(): + if ops.executing_eagerly_outside_functions(): key = distributed_container._unique_id else: key = (distributed_container.graph, distributed_container._shared_name) diff --git a/tensorflow/python/training/optimizer_test.py b/tensorflow/python/training/optimizer_test.py index 7a7d01d50e..e175b5a799 100644 --- a/tensorflow/python/training/optimizer_test.py +++ b/tensorflow/python/training/optimizer_test.py @@ -62,6 +62,7 @@ class OptimizerTest(test.TestCase): self.assertAllClose([-14., -13.], self.evaluate(var0)) self.assertAllClose([-6., -5.], self.evaluate(var1)) + @test_util.run_deprecated_v1 def testAggregationMethod(self): for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -79,14 +80,15 @@ class OptimizerTest(test.TestCase): variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([3.0, 4.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) # Run 1 step of sgd through optimizer opt_op.run() # Validate updated params - self.assertAllClose([-14., -13.], var0.eval()) - self.assertAllClose([-6., -5.], var1.eval()) + self.assertAllClose([-14., -13.], self.evaluate(var0)) + self.assertAllClose([-6., -5.], self.evaluate(var1)) + @test_util.run_deprecated_v1 def testPrecomputedGradient(self): for dtype in [dtypes.half, dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -102,15 +104,15 @@ class OptimizerTest(test.TestCase): variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([3.0, 4.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) # Run 1 step of sgd through optimizer opt_op.run() # Validate updated params self.assertAllClose([1.0 - 3 * 5 * 42.0, 2.0 - 3 * 5 * (-42.0)], - var0.eval()) + self.evaluate(var0)) self.assertAllClose([3.0 - 3 * 3 * 42.0, 4.0 - 3 * 3 * (-42.0)], - var1.eval()) + self.evaluate(var1)) @test_util.run_in_graph_and_eager_modes def testNoVariables(self): @@ -230,6 +232,7 @@ class OptimizerTest(test.TestCase): with self.assertRaises(NotImplementedError): sgd_op.apply_gradients(grads_and_vars) + @test_util.run_deprecated_v1 def testTrainOp(self): with self.cached_session(): var0 = variables.Variable([1.0, 2.0]) @@ -241,6 +244,7 @@ class OptimizerTest(test.TestCase): opt_op = sgd_op.minimize(cost, global_step, [var0, var1]) self.assertTrue(opt_op in ops.get_collection(ops.GraphKeys.TRAIN_OP)) + @test_util.run_deprecated_v1 def testConstraint(self): constraint_01 = lambda x: clip_ops.clip_by_value(x, -0.1, 0.) constraint_0 = lambda x: clip_ops.clip_by_value(x, 0., 1.) @@ -257,13 +261,13 @@ class OptimizerTest(test.TestCase): variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([3.0, 4.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) # Run 1 step of sgd through optimizer opt_op.run() # Validate updated params - self.assertAllClose([-0.1, -0.1], var0.eval()) - self.assertAllClose([0., 0.], var1.eval()) + self.assertAllClose([-0.1, -0.1], self.evaluate(var0)) + self.assertAllClose([0., 0.], self.evaluate(var1)) if __name__ == '__main__': diff --git a/tensorflow/python/training/proximal_adagrad.py b/tensorflow/python/training/proximal_adagrad.py index 9bd677b8ef..2ea628a56b 100644 --- a/tensorflow/python/training/proximal_adagrad.py +++ b/tensorflow/python/training/proximal_adagrad.py @@ -26,7 +26,7 @@ from tensorflow.python.training import training_ops from tensorflow.python.util.tf_export import tf_export -@tf_export("train.ProximalAdagradOptimizer") +@tf_export(v1=["train.ProximalAdagradOptimizer"]) class ProximalAdagradOptimizer(optimizer.Optimizer): # pylint: disable=line-too-long """Optimizer that implements the Proximal Adagrad algorithm. diff --git a/tensorflow/python/training/proximal_adagrad_test.py b/tensorflow/python/training/proximal_adagrad_test.py index 74e06a5e2e..ce214ac418 100644 --- a/tensorflow/python/training/proximal_adagrad_test.py +++ b/tensorflow/python/training/proximal_adagrad_test.py @@ -23,6 +23,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import embedding_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import resource_variable_ops @@ -48,7 +49,7 @@ class ProximalAdagradOptimizerTest(test.TestCase): update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) self.assertAllClose([0.0, 0.0], v0_val) self.assertAllClose([0.0, 0.0], v1_val) @@ -56,7 +57,7 @@ class ProximalAdagradOptimizerTest(test.TestCase): for _ in range(3): update.run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) self.assertAllClose(np.array([-2.60260963, -4.29698515]), v0_val) self.assertAllClose(np.array([-0.28432083, -0.56694895]), v1_val) opt_vars = opt.variables() @@ -64,12 +65,15 @@ class ProximalAdagradOptimizerTest(test.TestCase): self.assertStartsWith(opt_vars[1].name, var1._shared_name) self.assertEqual(2, len(opt_vars)) + @test_util.run_deprecated_v1 def testProximalAdagradwithoutRegularization(self): self.doTestProximalAdagradwithoutRegularization(use_resource=False) + @test_util.run_deprecated_v1 def testResourceProximalAdagradwithoutRegularization(self): self.doTestProximalAdagradwithoutRegularization(use_resource=True) + @test_util.run_deprecated_v1 def testProximalAdagradwithoutRegularization2(self): with self.cached_session() as sess: var0 = variables.Variable([1.0, 2.0]) @@ -85,17 +89,18 @@ class ProximalAdagradOptimizerTest(test.TestCase): update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) self.assertAllClose([1.0, 2.0], v0_val) self.assertAllClose([4.0, 3.0], v1_val) # Run 3 steps Proximal Adagrad. for _ in range(3): update.run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) self.assertAllClose(np.array([-1.60261, -2.296985]), v0_val) self.assertAllClose(np.array([3.715679, 2.433051]), v1_val) + @test_util.run_deprecated_v1 def testMinimizeSparseResourceVariable(self): for dtype in [dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -106,13 +111,15 @@ class ProximalAdagradOptimizerTest(test.TestCase): sgd_op = proximal_adagrad.ProximalAdagradOptimizer(1.0).minimize(loss) variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllCloseAccordingToType([[1.0, 2.0]], var0.eval()) + self.assertAllCloseAccordingToType([[1.0, 2.0]], self.evaluate(var0)) # Run 1 step of sgd sgd_op.run() # Validate updated params - self.assertAllCloseAccordingToType( - [[0, 1]], var0.eval(), atol=0.01) + self.assertAllCloseAccordingToType([[0, 1]], + self.evaluate(var0), + atol=0.01) + @test_util.run_deprecated_v1 def testProximalAdagradWithL1(self): with self.cached_session() as sess: var0 = variables.Variable([1.0, 2.0]) @@ -128,17 +135,18 @@ class ProximalAdagradOptimizerTest(test.TestCase): update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) self.assertAllClose([1.0, 2.0], v0_val) self.assertAllClose([4.0, 3.0], v1_val) # Run 10 steps Proximal Adagrad for _ in range(10): update.run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) self.assertAllClose(np.array([-6.663634, -9.190331]), v0_val) self.assertAllClose(np.array([2.959304, 1.029232]), v1_val) + @test_util.run_deprecated_v1 def testProximalAdagradWithL1_L2(self): with self.cached_session() as sess: var0 = variables.Variable([1.0, 2.0]) @@ -154,7 +162,7 @@ class ProximalAdagradOptimizerTest(test.TestCase): update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) self.assertAllClose([1.0, 2.0], v0_val) self.assertAllClose([4.0, 3.0], v1_val) @@ -162,7 +170,7 @@ class ProximalAdagradOptimizerTest(test.TestCase): for _ in range(10): update.run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) self.assertAllClose(np.array([-0.0495, -0.0995]), v0_val) self.assertAllClose(np.array([-0.0045, -0.0095]), v1_val) @@ -190,7 +198,7 @@ class ProximalAdagradOptimizerTest(test.TestCase): variables.global_variables_initializer().run() sess = ops.get_default_session() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) if is_sparse: self.assertAllClose([[1.0], [2.0]], v0_val) self.assertAllClose([[3.0], [4.0]], v1_val) @@ -202,9 +210,10 @@ class ProximalAdagradOptimizerTest(test.TestCase): for _ in range(steps): update.run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) return v0_val, v1_val + @test_util.run_deprecated_v1 def testEquivAdagradwithoutRegularization(self): with self.cached_session(): val0, val1 = self.applyOptimizer( @@ -222,6 +231,7 @@ class ProximalAdagradOptimizerTest(test.TestCase): self.assertAllClose(val0, val2) self.assertAllClose(val1, val3) + @test_util.run_deprecated_v1 def testEquivSparseAdagradwithoutRegularization(self): with self.cached_session(): val0, val1 = self.applyOptimizer( diff --git a/tensorflow/python/training/proximal_gradient_descent_test.py b/tensorflow/python/training/proximal_gradient_descent_test.py index f77f68b234..25b206605d 100644 --- a/tensorflow/python/training/proximal_gradient_descent_test.py +++ b/tensorflow/python/training/proximal_gradient_descent_test.py @@ -23,6 +23,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import embedding_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import resource_variable_ops @@ -50,7 +51,7 @@ class ProximalGradientDescentOptimizerTest(test.TestCase): update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) self.assertAllClose([0.0, 0.0], v0_val) self.assertAllClose([0.0, 0.0], v1_val) @@ -58,16 +59,19 @@ class ProximalGradientDescentOptimizerTest(test.TestCase): for _ in range(3): update.run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) self.assertAllClose(np.array([-0.9, -1.8]), v0_val) self.assertAllClose(np.array([-0.09, -0.18]), v1_val) + @test_util.run_deprecated_v1 def testProximalGradientDescentwithoutRegularization(self): self.doTestProximalGradientDescentwithoutRegularization(use_resource=False) + @test_util.run_deprecated_v1 def testResourceProximalGradientDescentwithoutRegularization(self): self.doTestProximalGradientDescentwithoutRegularization(use_resource=True) + @test_util.run_deprecated_v1 def testProximalGradientDescentwithoutRegularization2(self): with self.cached_session() as sess: var0 = variables.Variable([1.0, 2.0]) @@ -80,7 +84,7 @@ class ProximalGradientDescentOptimizerTest(test.TestCase): update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) self.assertAllClose([1.0, 2.0], v0_val) self.assertAllClose([4.0, 3.0], v1_val) @@ -88,10 +92,11 @@ class ProximalGradientDescentOptimizerTest(test.TestCase): for _ in range(3): update.run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) self.assertAllClose(np.array([0.1, 0.2]), v0_val) self.assertAllClose(np.array([3.91, 2.82]), v1_val) + @test_util.run_deprecated_v1 def testMinimizeSparseResourceVariable(self): for dtype in [dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -103,13 +108,15 @@ class ProximalGradientDescentOptimizerTest(test.TestCase): 1.0).minimize(loss) variables.global_variables_initializer().run() # Fetch params to validate initial values - self.assertAllCloseAccordingToType([[1.0, 2.0]], var0.eval()) + self.assertAllCloseAccordingToType([[1.0, 2.0]], self.evaluate(var0)) # Run 1 step of sgd sgd_op.run() # Validate updated params - self.assertAllCloseAccordingToType( - [[-111, -138]], var0.eval(), atol=0.01) + self.assertAllCloseAccordingToType([[-111, -138]], + self.evaluate(var0), + atol=0.01) + @test_util.run_deprecated_v1 def testProximalGradientDescentWithL1_L2(self): with self.cached_session() as sess: var0 = variables.Variable([1.0, 2.0]) @@ -122,7 +129,7 @@ class ProximalGradientDescentOptimizerTest(test.TestCase): update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) variables.global_variables_initializer().run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) self.assertAllClose([1.0, 2.0], v0_val) self.assertAllClose([4.0, 3.0], v1_val) @@ -130,7 +137,7 @@ class ProximalGradientDescentOptimizerTest(test.TestCase): for _ in range(10): update.run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) self.assertAllClose(np.array([-0.0495, -0.0995]), v0_val) self.assertAllClose(np.array([-0.0045, -0.0095]), v1_val) @@ -158,7 +165,7 @@ class ProximalGradientDescentOptimizerTest(test.TestCase): variables.global_variables_initializer().run() sess = ops.get_default_session() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) if is_sparse: self.assertAllClose([[1.0], [2.0]], v0_val) self.assertAllClose([[3.0], [4.0]], v1_val) @@ -170,9 +177,10 @@ class ProximalGradientDescentOptimizerTest(test.TestCase): for _ in range(steps): update.run() - v0_val, v1_val = sess.run([var0, var1]) + v0_val, v1_val = self.evaluate([var0, var1]) return v0_val, v1_val + @test_util.run_deprecated_v1 def testEquivSparseGradientDescentwithoutRegularization(self): with self.cached_session(): val0, val1 = self.applyOptimizer( @@ -189,6 +197,7 @@ class ProximalGradientDescentOptimizerTest(test.TestCase): self.assertAllClose(val0, val2) self.assertAllClose(val1, val3) + @test_util.run_deprecated_v1 def testEquivGradientDescentwithoutRegularization(self): with self.cached_session(): val0, val1 = self.applyOptimizer( diff --git a/tensorflow/python/training/quantize_training_test.py b/tensorflow/python/training/quantize_training_test.py index 6edbf7665f..62e783f200 100644 --- a/tensorflow/python/training/quantize_training_test.py +++ b/tensorflow/python/training/quantize_training_test.py @@ -25,6 +25,7 @@ from tensorflow.python.client import session from tensorflow.python.framework import constant_op from tensorflow.python.framework import importer from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import math_ops from tensorflow.python.ops import variables from tensorflow.python.platform import test @@ -52,6 +53,7 @@ class PywrapQuantizeTrainingTest(test.TestCase): # Test that save/restoring works for EMA variables generated in the # quantized training rewrite. + @test_util.run_deprecated_v1 def testQuantizedSaveRestore(self): save_path = os.path.join(self.get_temp_dir(), 'quantized_save_restore') @@ -73,11 +75,11 @@ class PywrapQuantizeTrainingTest(test.TestCase): _ = importer.import_graph_def(result, name='') # Initialize the variable. - sess.run(g.get_operation_by_name(init_op.name)) + self.evaluate(g.get_operation_by_name(init_op.name)) # Run the graph for one step to assign values to the quantization min/max # variables. - sess.run(g.get_tensor_by_name(c.name)) + self.evaluate(g.get_tensor_by_name(c.name)) saver.save(sess, save_path) diff --git a/tensorflow/python/training/queue_runner_test.py b/tensorflow/python/training/queue_runner_test.py index 15fe42bbd8..4113cecf55 100644 --- a/tensorflow/python/training/queue_runner_test.py +++ b/tensorflow/python/training/queue_runner_test.py @@ -26,6 +26,7 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors_impl from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import data_flow_ops from tensorflow.python.ops import variables @@ -40,6 +41,7 @@ _MockOp = collections.namedtuple("MockOp", ["name"]) class QueueRunnerTest(test.TestCase): + @test_util.run_deprecated_v1 def testBasic(self): with self.cached_session() as sess: # CountUpTo will raise OUT_OF_RANGE when it reaches the count. @@ -58,8 +60,9 @@ class QueueRunnerTest(test.TestCase): t.join() self.assertEqual(0, len(qr.exceptions_raised)) # The variable should be 3. - self.assertEqual(3, var.eval()) + self.assertEqual(3, self.evaluate(var)) + @test_util.run_deprecated_v1 def testTwoOps(self): with self.cached_session() as sess: # CountUpTo will raise OUT_OF_RANGE when it reaches the count. @@ -80,9 +83,10 @@ class QueueRunnerTest(test.TestCase): for t in threads: t.join() self.assertEqual(0, len(qr.exceptions_raised)) - self.assertEqual(3, var0.eval()) - self.assertEqual(30, var1.eval()) + self.assertEqual(3, self.evaluate(var0)) + self.assertEqual(30, self.evaluate(var1)) + @test_util.run_deprecated_v1 def testExceptionsCaptured(self): with self.cached_session() as sess: queue = data_flow_ops.FIFOQueue(10, dtypes.float32) @@ -99,6 +103,7 @@ class QueueRunnerTest(test.TestCase): self.assertTrue("Operation not in the graph" in str(exceptions[0])) self.assertTrue("Operation not in the graph" in str(exceptions[1])) + @test_util.run_deprecated_v1 def testRealDequeueEnqueue(self): with self.cached_session() as sess: q0 = data_flow_ops.FIFOQueue(3, dtypes.float32) @@ -121,12 +126,13 @@ class QueueRunnerTest(test.TestCase): # It should have terminated cleanly. self.assertEqual(0, len(qr.exceptions_raised)) # The 2 values should be in queue1. - self.assertEqual(10.0, dequeue1.eval()) - self.assertEqual(10.0, dequeue1.eval()) + self.assertEqual(10.0, self.evaluate(dequeue1)) + self.assertEqual(10.0, self.evaluate(dequeue1)) # And queue1 should now be closed. with self.assertRaisesRegexp(errors_impl.OutOfRangeError, "is closed"): - dequeue1.eval() + self.evaluate(dequeue1) + @test_util.run_deprecated_v1 def testRespectCoordShouldStop(self): with self.cached_session() as sess: # CountUpTo will raise OUT_OF_RANGE when it reaches the count. @@ -149,8 +155,9 @@ class QueueRunnerTest(test.TestCase): coord.join() self.assertEqual(0, len(qr.exceptions_raised)) # The variable should be 0. - self.assertEqual(0, var.eval()) + self.assertEqual(0, self.evaluate(var)) + @test_util.run_deprecated_v1 def testRequestStopOnException(self): with self.cached_session() as sess: queue = data_flow_ops.FIFOQueue(10, dtypes.float32) @@ -163,6 +170,7 @@ class QueueRunnerTest(test.TestCase): with self.assertRaisesRegexp(ValueError, "Operation not in the graph"): coord.join() + @test_util.run_deprecated_v1 def testGracePeriod(self): with self.cached_session() as sess: # The enqueue will quickly block. @@ -180,6 +188,7 @@ class QueueRunnerTest(test.TestCase): # the queue to be closed and the enqueue to terminate. coord.join(stop_grace_period_secs=1.0) + @test_util.run_deprecated_v1 def testMultipleSessions(self): with self.cached_session() as sess: with session.Session() as other_sess: @@ -195,6 +204,7 @@ class QueueRunnerTest(test.TestCase): other_threads = qr.create_threads(other_sess, coord=coord) self.assertEqual(len(threads), len(other_threads)) + @test_util.run_deprecated_v1 def testIgnoreMultiStarts(self): with self.cached_session() as sess: # CountUpTo will raise OUT_OF_RANGE when it reaches the count. @@ -211,6 +221,7 @@ class QueueRunnerTest(test.TestCase): new_threads = qr.create_threads(sess, coord=coord) self.assertEqual([], new_threads) + @test_util.run_deprecated_v1 def testThreads(self): with self.cached_session() as sess: # CountUpTo will raise OUT_OF_RANGE when it reaches the count. @@ -238,6 +249,7 @@ class QueueRunnerTest(test.TestCase): self.assertEqual(1, len(exceptions)) self.assertTrue("Operation not in the graph" in str(exceptions[0])) + @test_util.run_deprecated_v1 def testName(self): with ops.name_scope("scope"): queue = data_flow_ops.FIFOQueue(10, dtypes.float32, name="queue") @@ -247,6 +259,7 @@ class QueueRunnerTest(test.TestCase): self.assertEqual( 1, len(ops.get_collection(ops.GraphKeys.QUEUE_RUNNERS, "scope"))) + @test_util.run_deprecated_v1 def testStartQueueRunners(self): # CountUpTo will raise OUT_OF_RANGE when it reaches the count. zero64 = constant_op.constant(0, dtype=dtypes.int64) @@ -263,8 +276,9 @@ class QueueRunnerTest(test.TestCase): t.join() self.assertEqual(0, len(qr.exceptions_raised)) # The variable should be 3. - self.assertEqual(3, var.eval()) + self.assertEqual(3, self.evaluate(var)) + @test_util.run_deprecated_v1 def testStartQueueRunnersRaisesIfNotASession(self): zero64 = constant_op.constant(0, dtype=dtypes.int64) var = variables.VariableV1(zero64) @@ -278,6 +292,7 @@ class QueueRunnerTest(test.TestCase): with self.assertRaisesRegexp(TypeError, "tf.Session"): queue_runner_impl.start_queue_runners("NotASession") + @test_util.run_deprecated_v1 def testStartQueueRunnersIgnoresMonitoredSession(self): zero64 = constant_op.constant(0, dtype=dtypes.int64) var = variables.VariableV1(zero64) @@ -292,6 +307,7 @@ class QueueRunnerTest(test.TestCase): monitored_session.MonitoredSession()) self.assertFalse(threads) + @test_util.run_deprecated_v1 def testStartQueueRunnersNonDefaultGraph(self): # CountUpTo will raise OUT_OF_RANGE when it reaches the count. graph = ops.Graph() @@ -310,7 +326,7 @@ class QueueRunnerTest(test.TestCase): t.join() self.assertEqual(0, len(qr.exceptions_raised)) # The variable should be 3. - self.assertEqual(3, var.eval()) + self.assertEqual(3, self.evaluate(var)) def testQueueRunnerSerializationRoundTrip(self): graph = ops.Graph() diff --git a/tensorflow/python/training/rmsprop.py b/tensorflow/python/training/rmsprop.py index f38c9861d6..fb53b5883f 100644 --- a/tensorflow/python/training/rmsprop.py +++ b/tensorflow/python/training/rmsprop.py @@ -50,7 +50,7 @@ from tensorflow.python.training import training_ops from tensorflow.python.util.tf_export import tf_export -@tf_export("train.RMSPropOptimizer") +@tf_export(v1=["train.RMSPropOptimizer"]) class RMSPropOptimizer(optimizer.Optimizer): """Optimizer that implements the RMSProp algorithm. diff --git a/tensorflow/python/training/rmsprop_test.py b/tensorflow/python/training/rmsprop_test.py index b63abe0529..8f029d5310 100644 --- a/tensorflow/python/training/rmsprop_test.py +++ b/tensorflow/python/training/rmsprop_test.py @@ -28,6 +28,7 @@ from tensorflow.python.eager import context from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import embedding_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import resource_variable_ops @@ -88,11 +89,12 @@ class RMSPropOptimizerTest(test.TestCase): var_t[gindex] = var[gindex] - mom_t[gindex] return var_t, mg_t, rms_t, mom_t + @test_util.run_deprecated_v1 def testDense(self): # TODO(yori): Use ParameterizedTest when available for (dtype, learning_rate, decay, momentum, epsilon, centered, use_resource) in _TESTPARAMS: - with self.cached_session(use_gpu=True): + with test_util.use_gpu(): # Initialize variables for numpy implementation. var0_np = np.array([1.0, 2.0], dtype=dtype.as_numpy_dtype) grads0_np = np.array([0.1, 0.2], dtype=dtype.as_numpy_dtype) @@ -115,7 +117,7 @@ class RMSPropOptimizerTest(test.TestCase): centered=centered) update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) - variables.global_variables_initializer().run() + self.evaluate(variables.global_variables_initializer()) mg0 = opt.get_slot(var0, "mg") self.assertEqual(mg0 is not None, centered) @@ -138,12 +140,12 @@ class RMSPropOptimizerTest(test.TestCase): mom1_np = np.array([0.0, 0.0], dtype=dtype.as_numpy_dtype) # Fetch params to validate initial values - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([3.0, 4.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) # Run 4 steps of RMSProp for _ in range(1, 5): - update.run() + self.evaluate(update) var0_np, mg0_np, rms0_np, mom0_np = self._rmsprop_update_numpy( var0_np, grads0_np, mg0_np, rms0_np, mom0_np, learning_rate, @@ -154,15 +156,16 @@ class RMSPropOptimizerTest(test.TestCase): # Validate updated params if centered: - self.assertAllCloseAccordingToType(mg0_np, mg0.eval()) - self.assertAllCloseAccordingToType(mg1_np, mg1.eval()) - self.assertAllCloseAccordingToType(rms0_np, rms0.eval()) - self.assertAllCloseAccordingToType(rms1_np, rms1.eval()) - self.assertAllCloseAccordingToType(mom0_np, mom0.eval()) - self.assertAllCloseAccordingToType(mom1_np, mom1.eval()) - self.assertAllCloseAccordingToType(var0_np, var0.eval()) - self.assertAllCloseAccordingToType(var1_np, var1.eval()) - + self.assertAllCloseAccordingToType(mg0_np, self.evaluate(mg0)) + self.assertAllCloseAccordingToType(mg1_np, self.evaluate(mg1)) + self.assertAllCloseAccordingToType(rms0_np, self.evaluate(rms0)) + self.assertAllCloseAccordingToType(rms1_np, self.evaluate(rms1)) + self.assertAllCloseAccordingToType(mom0_np, self.evaluate(mom0)) + self.assertAllCloseAccordingToType(mom1_np, self.evaluate(mom1)) + self.assertAllCloseAccordingToType(var0_np, self.evaluate(var0)) + self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1)) + + @test_util.run_deprecated_v1 def testMinimizeSparseResourceVariable(self): for dtype in [dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -176,15 +179,17 @@ class RMSPropOptimizerTest(test.TestCase): momentum=0.0, epsilon=0.0, centered=False).minimize(loss) - variables.global_variables_initializer().run() + self.evaluate(variables.global_variables_initializer()) # Fetch params to validate initial values - self.assertAllCloseAccordingToType([[1.0, 2.0]], var0.eval()) + self.assertAllCloseAccordingToType([[1.0, 2.0]], self.evaluate(var0)) # Run 1 step of sgd - sgd_op.run() + self.evaluate(sgd_op) # Validate updated params - self.assertAllCloseAccordingToType( - [[0., 1.]], var0.eval(), atol=0.01) + self.assertAllCloseAccordingToType([[0., 1.]], + self.evaluate(var0), + atol=0.01) + @test_util.run_deprecated_v1 def testMinimizeSparseResourceVariableCentered(self): for dtype in [dtypes.float32, dtypes.float64]: with self.cached_session(): @@ -198,20 +203,22 @@ class RMSPropOptimizerTest(test.TestCase): momentum=0.0, epsilon=1.0, centered=True).minimize(loss) - variables.global_variables_initializer().run() + self.evaluate(variables.global_variables_initializer()) # Fetch params to validate initial values - self.assertAllCloseAccordingToType([[1.0, 2.0]], var0.eval()) + self.assertAllCloseAccordingToType([[1.0, 2.0]], self.evaluate(var0)) # Run 1 step of sgd - sgd_op.run() + self.evaluate(sgd_op) # Validate updated params - self.assertAllCloseAccordingToType( - [[-111, -138]], var0.eval(), atol=0.01) + self.assertAllCloseAccordingToType([[-111, -138]], + self.evaluate(var0), + atol=0.01) + @test_util.run_deprecated_v1 def testSparse(self): # TODO(yori): Use ParameterizedTest when available for (dtype, learning_rate, decay, momentum, epsilon, centered, _) in _TESTPARAMS: - with self.cached_session(use_gpu=True): + with test_util.use_gpu(): # Initialize variables for numpy implementation. var0_np = np.array([1.0, 2.0], dtype=dtype.as_numpy_dtype) grads0_np = np.array([0.1], dtype=dtype.as_numpy_dtype) @@ -235,7 +242,7 @@ class RMSPropOptimizerTest(test.TestCase): epsilon=epsilon, centered=centered) update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) - variables.global_variables_initializer().run() + self.evaluate(variables.global_variables_initializer()) mg0 = opt.get_slot(var0, "mg") self.assertEqual(mg0 is not None, centered) @@ -258,12 +265,12 @@ class RMSPropOptimizerTest(test.TestCase): mom1_np = np.array([0.0, 0.0], dtype=dtype.as_numpy_dtype) # Fetch params to validate initial values - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([3.0, 4.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) # Run 4 steps of RMSProp for _ in range(1, 5): - update.run() + self.evaluate(update) var0_np, mg0_np, rms0_np, mom0_np = self._sparse_rmsprop_update_numpy( var0_np, grads0_np_indices, grads0_np, mg0_np, rms0_np, mom0_np, @@ -274,18 +281,19 @@ class RMSPropOptimizerTest(test.TestCase): # Validate updated params if centered: - self.assertAllCloseAccordingToType(mg0_np, mg0.eval()) - self.assertAllCloseAccordingToType(mg1_np, mg1.eval()) - self.assertAllCloseAccordingToType(rms0_np, rms0.eval()) - self.assertAllCloseAccordingToType(rms1_np, rms1.eval()) - self.assertAllCloseAccordingToType(mom0_np, mom0.eval()) - self.assertAllCloseAccordingToType(mom1_np, mom1.eval()) - self.assertAllCloseAccordingToType(var0_np, var0.eval()) - self.assertAllCloseAccordingToType(var1_np, var1.eval()) - + self.assertAllCloseAccordingToType(mg0_np, self.evaluate(mg0)) + self.assertAllCloseAccordingToType(mg1_np, self.evaluate(mg1)) + self.assertAllCloseAccordingToType(rms0_np, self.evaluate(rms0)) + self.assertAllCloseAccordingToType(rms1_np, self.evaluate(rms1)) + self.assertAllCloseAccordingToType(mom0_np, self.evaluate(mom0)) + self.assertAllCloseAccordingToType(mom1_np, self.evaluate(mom1)) + self.assertAllCloseAccordingToType(var0_np, self.evaluate(var0)) + self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1)) + + @test_util.run_deprecated_v1 def testWithoutMomentum(self): for dtype in [dtypes.half, dtypes.float32]: - with self.cached_session(use_gpu=True): + with test_util.use_gpu(): var0 = variables.Variable([1.0, 2.0], dtype=dtype) var1 = variables.Variable([3.0, 4.0], dtype=dtype) grads0 = constant_op.constant([0.1, 0.1], dtype=dtype) @@ -293,7 +301,7 @@ class RMSPropOptimizerTest(test.TestCase): opt = rmsprop.RMSPropOptimizer( learning_rate=2.0, decay=0.9, momentum=0.0, epsilon=1.0) update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) - variables.global_variables_initializer().run() + self.evaluate(variables.global_variables_initializer()) rms0 = opt.get_slot(var0, "rms") self.assertTrue(rms0 is not None) @@ -305,34 +313,36 @@ class RMSPropOptimizerTest(test.TestCase): self.assertTrue(mom1 is not None) # Fetch params to validate initial values - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([3.0, 4.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) # Step 1: the rms accumulators where 1. So we should see a normal # update: v -= grad * learning_rate - update.run() + self.evaluate(update) # Check the root mean square accumulators. self.assertAllCloseAccordingToType( - np.array([0.901, 0.901]), rms0.eval()) + np.array([0.901, 0.901]), self.evaluate(rms0)) self.assertAllCloseAccordingToType( - np.array([0.90001, 0.90001]), rms1.eval()) + np.array([0.90001, 0.90001]), self.evaluate(rms1)) # Check the parameters. self.assertAllCloseAccordingToType( np.array([ 1.0 - (0.1 * 2.0 / math.sqrt(0.901 + 1.0)), 2.0 - (0.1 * 2.0 / math.sqrt(0.901 + 1.0)) - ]), var0.eval()) + ]), self.evaluate(var0)) self.assertAllCloseAccordingToType( np.array([ 3.0 - (0.01 * 2.0 / math.sqrt(0.90001 + 1.0)), 4.0 - (0.01 * 2.0 / math.sqrt(0.90001 + 1.0)) - ]), var1.eval()) + ]), self.evaluate(var1)) # Step 2: the root mean square accumulators contain the previous update. - update.run() + self.evaluate(update) # Check the rms accumulators. self.assertAllCloseAccordingToType( - np.array([0.901 * 0.9 + 0.001, 0.901 * 0.9 + 0.001]), rms0.eval()) + np.array([0.901 * 0.9 + 0.001, 0.901 * 0.9 + 0.001]), + self.evaluate(rms0)) self.assertAllCloseAccordingToType( - np.array([0.90001 * 0.9 + 1e-5, 0.90001 * 0.9 + 1e-5]), rms1.eval()) + np.array([0.90001 * 0.9 + 1e-5, 0.90001 * 0.9 + 1e-5]), + self.evaluate(rms1)) # Check the parameters. self.assertAllCloseAccordingToType( np.array([ @@ -340,18 +350,19 @@ class RMSPropOptimizerTest(test.TestCase): (0.1 * 2.0 / math.sqrt(0.901 * 0.9 + 0.001 + 1.0)), 2.0 - (0.1 * 2.0 / math.sqrt(0.901 + 1.0)) - (0.1 * 2.0 / math.sqrt(0.901 * 0.9 + 0.001 + 1.0)) - ]), var0.eval()) + ]), self.evaluate(var0)) self.assertAllCloseAccordingToType( np.array([ 3.0 - (0.01 * 2.0 / math.sqrt(0.90001 + 1.0)) - (0.01 * 2.0 / math.sqrt(0.90001 * 0.9 + 1e-5 + 1.0)), 4.0 - (0.01 * 2.0 / math.sqrt(0.90001 + 1.0)) - (0.01 * 2.0 / math.sqrt(0.90001 * 0.9 + 1e-5 + 1.0)) - ]), var1.eval()) + ]), self.evaluate(var1)) + @test_util.run_deprecated_v1 def testWithMomentum(self): for dtype in [dtypes.half, dtypes.float32]: - with self.cached_session(use_gpu=True): + with test_util.use_gpu(): var0 = variables.Variable([1.0, 2.0], dtype=dtype) var1 = variables.Variable([3.0, 4.0], dtype=dtype) grads0 = constant_op.constant([0.1, 0.1], dtype=dtype) @@ -360,7 +371,7 @@ class RMSPropOptimizerTest(test.TestCase): opt = rmsprop.RMSPropOptimizer( learning_rate=2.0, decay=0.9, momentum=0.5, epsilon=1e-5) update = opt.apply_gradients(zip([grads0, grads1], [var0, var1])) - variables.global_variables_initializer().run() + self.evaluate(variables.global_variables_initializer()) rms0 = opt.get_slot(var0, "rms") self.assertTrue(rms0 is not None) @@ -372,57 +383,61 @@ class RMSPropOptimizerTest(test.TestCase): self.assertTrue(mom1 is not None) # Fetch params to validate initial values - self.assertAllClose([1.0, 2.0], var0.eval()) - self.assertAllClose([3.0, 4.0], var1.eval()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) # Step 1: rms = 1, mom = 0. So we should see a normal # update: v -= grad * learning_rate - update.run() + self.evaluate(update) # Check the root mean square accumulators. self.assertAllCloseAccordingToType( - np.array([0.901, 0.901]), rms0.eval()) + np.array([0.901, 0.901]), self.evaluate(rms0)) self.assertAllCloseAccordingToType( - np.array([0.90001, 0.90001]), rms1.eval()) + np.array([0.90001, 0.90001]), self.evaluate(rms1)) # Check the momentum accumulators self.assertAllCloseAccordingToType( np.array([(0.1 * 2.0 / math.sqrt(0.901 + 1e-5)), - (0.1 * 2.0 / math.sqrt(0.901 + 1e-5))]), mom0.eval()) + (0.1 * 2.0 / math.sqrt(0.901 + 1e-5))]), + self.evaluate(mom0)) self.assertAllCloseAccordingToType( np.array([(0.01 * 2.0 / math.sqrt(0.90001 + 1e-5)), - (0.01 * 2.0 / math.sqrt(0.90001 + 1e-5))]), mom1.eval()) + (0.01 * 2.0 / math.sqrt(0.90001 + 1e-5))]), + self.evaluate(mom1)) # Check that the parameters. self.assertAllCloseAccordingToType( np.array([ 1.0 - (0.1 * 2.0 / math.sqrt(0.901 + 1e-5)), 2.0 - (0.1 * 2.0 / math.sqrt(0.901 + 1e-5)) - ]), var0.eval()) + ]), self.evaluate(var0)) self.assertAllCloseAccordingToType( np.array([ 3.0 - (0.01 * 2.0 / math.sqrt(0.90001 + 1e-5)), 4.0 - (0.01 * 2.0 / math.sqrt(0.90001 + 1e-5)) - ]), var1.eval()) + ]), self.evaluate(var1)) # Step 2: the root mean square accumulators contain the previous update. - update.run() + self.evaluate(update) # Check the rms accumulators. self.assertAllCloseAccordingToType( - np.array([0.901 * 0.9 + 0.001, 0.901 * 0.9 + 0.001]), rms0.eval()) + np.array([0.901 * 0.9 + 0.001, 0.901 * 0.9 + 0.001]), + self.evaluate(rms0)) self.assertAllCloseAccordingToType( - np.array([0.90001 * 0.9 + 1e-5, 0.90001 * 0.9 + 1e-5]), rms1.eval()) + np.array([0.90001 * 0.9 + 1e-5, 0.90001 * 0.9 + 1e-5]), + self.evaluate(rms1)) self.assertAllCloseAccordingToType( np.array([ 0.5 * (0.1 * 2.0 / math.sqrt(0.901 + 1e-5)) + (0.1 * 2.0 / math.sqrt(0.901 * 0.9 + 0.001 + 1e-5)), 0.5 * (0.1 * 2.0 / math.sqrt(0.901 + 1e-5)) + (0.1 * 2.0 / math.sqrt(0.901 * 0.9 + 0.001 + 1e-5)) - ]), mom0.eval()) + ]), self.evaluate(mom0)) self.assertAllCloseAccordingToType( np.array([ 0.5 * (0.01 * 2.0 / math.sqrt(0.90001 + 1e-5)) + (0.01 * 2.0 / math.sqrt(0.90001 * 0.9 + 2e-5)), 0.5 * (0.01 * 2.0 / math.sqrt(0.90001 + 1e-5)) + (0.01 * 2.0 / math.sqrt(0.90001 * 0.9 + 2e-5)) - ]), mom1.eval()) + ]), self.evaluate(mom1)) # Check the parameters. self.assertAllCloseAccordingToType( @@ -433,7 +448,7 @@ class RMSPropOptimizerTest(test.TestCase): 2.0 - (0.1 * 2.0 / math.sqrt(0.901 + 1e-5)) - (0.5 * (0.1 * 2.0 / math.sqrt(0.901 + 1e-5)) + (0.1 * 2.0 / math.sqrt(0.901 * 0.9 + 0.001 + 1e-5))) - ]), var0.eval()) + ]), self.evaluate(var0)) self.assertAllCloseAccordingToType( np.array([ @@ -443,7 +458,7 @@ class RMSPropOptimizerTest(test.TestCase): 4.0 - (0.01 * 2.0 / math.sqrt(0.90001 + 1e-5)) - (0.5 * (0.01 * 2.0 / math.sqrt(0.90001 + 1e-5)) + (0.01 * 2.0 / math.sqrt(0.90001 * 0.9 + 2e-5))) - ]), var1.eval()) + ]), self.evaluate(var1)) def testCallableParams(self): with context.eager_mode(): diff --git a/tensorflow/python/training/saver.py b/tensorflow/python/training/saver.py index a29926a57d..4cd09f8a1d 100644 --- a/tensorflow/python/training/saver.py +++ b/tensorflow/python/training/saver.py @@ -1077,16 +1077,28 @@ class Saver(object): @compatibility(eager) When eager execution is enabled, `var_list` must specify a `list` or `dict` of variables to save. Otherwise, a `RuntimeError` will be raised. + + Although Saver works in some cases when executing eagerly, it is + fragile. Please switch to `tf.train.Checkpoint` or + `tf.keras.Model.save_weights`, which perform a more robust object-based + saving. These APIs will load checkpoints written by `Saver`. @end_compatibility """ if defer_build and var_list: raise ValueError( "If `var_list` is provided then build cannot be deferred. " "Either set defer_build=False or var_list=None.") - if context.executing_eagerly() and var_list is None: - raise RuntimeError( - "When eager execution is enabled, `var_list` must specify a list or " - "dict of variables to save") + if context.executing_eagerly(): + logging.warning( + "Saver is deprecated, please switch to tf.train.Checkpoint or " + "tf.keras.Model.save_weights for training checkpoints. When " + "executing eagerly variables do not necessarily have unique names, " + "and so the variable.name-based lookups Saver performs are " + "error-prone.") + if var_list is None: + raise RuntimeError( + "When eager execution is enabled, `var_list` must specify a list " + "or dict of variables to save") self._var_list = var_list self._reshape = reshape self._sharded = sharded @@ -1899,16 +1911,40 @@ def saver_from_object_based_checkpoint( builder = BulkSaverBuilder() saveables = builder._ValidateAndSliceInputs(var_list) # pylint: disable=protected-access + current_names = set() + for saveable in saveables: + for spec in saveable.specs: + current_names.add(spec.name) + previous_names = set(names_to_keys.keys()) + missing_names = current_names - previous_names + if missing_names: + extra_names = previous_names - current_names + intersecting_names = previous_names.intersection(current_names) + raise errors.NotFoundError( + None, None, + message=( + "\n\nExisting variables not in the checkpoint: %s\n\n" + "Variables names when this checkpoint was written which don't " + "exist now: %s\n\n" + "(%d variable name(s) did match)\n\n" + "Could not find some variables in the checkpoint (see names " + "above). Saver was attempting to load an object-based checkpoint " + "(saved using tf.train.Checkpoint or tf.keras.Model.save_weights) " + "using variable names. If the checkpoint was written with eager " + "execution enabled, it's possible that variable names have " + "changed (for example missing a '_1' suffix). It's also " + "possible that there are new variables which did not exist " + "when the checkpoint was written. You can construct a " + "Saver(var_list=...) with only the variables which previously " + "existed, and if variable names have changed you may need to " + "make this a dictionary with the old names as keys. If you're " + "using an Estimator, you'll need to return a tf.train.Saver " + "inside a tf.train.Scaffold from your model_fn.") + % (", ".join(sorted(missing_names)), ", ".join(sorted(extra_names)), + len(intersecting_names))) for saveable in saveables: for spec in saveable.specs: - if spec.name not in names_to_keys: - raise errors.NotFoundError( - None, None, - message=("Attempting to load an object-based checkpoint using " - "variable names, but could not find %s in the " - "checkpoint.") % spec.name) spec.name = names_to_keys[spec.name] - if cached_saver is None: return Saver(saveables) return cached_saver diff --git a/tensorflow/python/training/saver_large_partitioned_variable_test.py b/tensorflow/python/training/saver_large_partitioned_variable_test.py index 1a44511cfe..84458836d0 100644 --- a/tensorflow/python/training/saver_large_partitioned_variable_test.py +++ b/tensorflow/python/training/saver_large_partitioned_variable_test.py @@ -25,6 +25,7 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.ops import partitioned_variables +from tensorflow.python.ops import variable_scope from tensorflow.python.ops import variables from tensorflow.python.platform import test from tensorflow.python.training import saver @@ -44,8 +45,12 @@ class SaverLargePartitionedVariableTest(test.TestCase): # split into smaller sized variables. init = lambda shape, dtype, partition_info: constant_op.constant( True, dtype, shape) - partitioned_var = partitioned_variables.create_partitioned_variables( - [1 << 31], [4], init, dtype=dtypes.bool, name=var_name) + partitioned_var = list(variable_scope.get_variable( + var_name, + shape=[1 << 31], + partitioner=partitioned_variables.fixed_size_partitioner(4), + initializer=init, + dtype=dtypes.bool)) variables.global_variables_initializer().run() save = saver.Saver(partitioned_var) val = save.save(sess, save_path) diff --git a/tensorflow/python/training/saver_test.py b/tensorflow/python/training/saver_test.py index eb2690985d..5d621ba4ff 100644 --- a/tensorflow/python/training/saver_test.py +++ b/tensorflow/python/training/saver_test.py @@ -170,6 +170,7 @@ class SaverTest(test.TestCase): def testResourceBasic(self): self.basicSaveRestore(resource_variable_ops.ResourceVariable) + @test_util.run_deprecated_v1 def testResourceColocation(self): partitioner = partitioned_variables.fixed_size_partitioner(num_shards=2) with ops_lib.device("/job:ps/device:GPU:0"): @@ -227,7 +228,7 @@ class SaverTest(test.TestCase): w1 = resource_variable_ops.ResourceVariable(1.0, name="w1") w2 = resource_variable_ops.ResourceVariable(2.0, name="w2") graph_saver = saver_module.Saver([w1, w2]) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) graph_saver.save(sess, graph_ckpt_prefix) with context.eager_mode(): @@ -260,7 +261,7 @@ class SaverTest(test.TestCase): w3 = resource_variable_ops.ResourceVariable(0.0, name="w3") w4 = resource_variable_ops.ResourceVariable(0.0, name="w4") graph_saver = saver_module.Saver([w3, w4]) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) graph_saver.restore(sess, eager_ckpt_prefix) self.assertAllEqual(w3.eval(), 3.0) self.assertAllEqual(w4.eval(), 4.0) @@ -300,6 +301,7 @@ class SaverTest(test.TestCase): not op.name.startswith("saver2/save/"))] self.assertEqual(ops_in_saver2_scope_but_not_save_scope, []) + @test_util.run_deprecated_v1 def testSaveCopyRestoreWithSaveRelativePaths(self): """Save, copy checkpoint dir and restore from copied dir. @@ -326,7 +328,7 @@ class SaverTest(test.TestCase): with self.cached_session() as sess: # Initialize all variables - sess.run(init_all_op) + self.evaluate(init_all_op) # Check that the parameter nodes have been initialized. self.assertEqual(10.0, v0.eval()) @@ -369,6 +371,7 @@ class SaverTest(test.TestCase): self.assertEqual(b"k1", v2.keys().eval()) self.assertEqual(30.0, v2.values().eval()) + @test_util.run_deprecated_v1 def testFilenameTensor(self): v0 = variables.VariableV1(0, name="v0") filename = b"somerandomfilename" @@ -376,7 +379,7 @@ class SaverTest(test.TestCase): with self.cached_session() as sess: tensor = sess.graph.get_tensor_by_name( save.saver_def.filename_tensor_name) - self.assertEqual(sess.run(tensor), filename) + self.assertEqual(self.evaluate(tensor), filename) def testInvalidPath(self): v0 = variables.VariableV1(0, name="v0") @@ -387,6 +390,7 @@ class SaverTest(test.TestCase): ValueError, "The passed save_path is not a valid checkpoint:"): save.restore(sess, "invalid path") + @test_util.run_deprecated_v1 def testInt64(self): save_path = os.path.join(self.get_temp_dir(), "int64") @@ -407,7 +411,7 @@ class SaverTest(test.TestCase): with self.assertRaisesWithPredicateMatch( errors_impl.OpError, lambda e: "uninitialized value v" in e.message): - sess.run(v) + self.evaluate(v) # Restore the saved values in the parameter nodes. save.restore(sess, save_path) @@ -462,6 +466,7 @@ class SaverTest(test.TestCase): # Verify non-duplicate names work. saver_module.Saver({"v0": v0, "v2": v2.saveable}) + @test_util.run_deprecated_v1 def testBasicsWithListOfVariables(self): save_path = os.path.join(self.get_temp_dir(), "basics_with_list") @@ -497,10 +502,10 @@ class SaverTest(test.TestCase): with self.assertRaisesWithPredicateMatch( errors_impl.OpError, lambda e: "uninitialized value v0" in e.message): - sess.run(v0) + self.evaluate(v0) with self.assertRaisesWithPredicateMatch( errors_impl.OpError, lambda e: "uninitialized value v1" in e.message): - sess.run(v1) + self.evaluate(v1) self.assertEqual(0, len(v2.keys().eval())) self.assertEqual(0, len(v2.values().eval())) @@ -557,6 +562,7 @@ class SaverTest(test.TestCase): # The cached readers should know to re-read the file. self._SaveAndLoad("var1", 1.1, 2.2, save_path) + @test_util.run_deprecated_v1 def testAllowEmpty(self): save_path = os.path.join(self.get_temp_dir(), "allow_empty") with self.cached_session() as sess: @@ -661,6 +667,7 @@ class SaverTest(test.TestCase): self.assertAllClose(1.0, one.eval()) self.assertAllClose([2.0, 2.0, 2.0], twos.eval()) + @test_util.run_deprecated_v1 def testReshape(self): save_path = os.path.join(self.get_temp_dir(), "variables_reshape") with session.Session("", graph=ops_lib.Graph()) as sess: @@ -719,6 +726,7 @@ class SaverTest(test.TestCase): def testSaveWithGlobalStepWithPadding(self): self.testSaveWithGlobalStep(pad_step_number=True) + @test_util.run_deprecated_v1 def testSaveToNonexistingPath(self): file_io.write_string_to_file( os.path.join(self.get_temp_dir(), "actually_a_file"), "") @@ -742,7 +750,7 @@ class SaverTest(test.TestCase): try: with self.cached_session() as sess: # Initialize all variables - sess.run(init_all_op) + self.evaluate(init_all_op) # Check that the parameter nodes have been initialized. self.assertEqual(10.0, v0.eval()) @@ -761,6 +769,7 @@ class SaverTest(test.TestCase): error_msg_template = "Parent directory of {} doesn't exist, can't save." self.assertEqual(error_msg_template.format(save_path), str(exc)) + @test_util.run_deprecated_v1 def testSaveToURI(self): # ParseURI functions don't work on Windows yet. # TODO(jhseu): Remove this check when it works. @@ -777,7 +786,7 @@ class SaverTest(test.TestCase): with self.cached_session() as sess: # Initialize all variables - sess.run(init_all_op) + self.evaluate(init_all_op) # Check that the parameter nodes have been initialized. self.assertEqual(10.0, v0.eval()) @@ -824,11 +833,11 @@ class SaverTest(test.TestCase): save_graph = ops_lib.Graph() with save_graph.as_default(), self.session(graph=save_graph) as sess: orig_vars = _model() - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) save = saver_module.Saver(max_to_keep=1) variables.global_variables_initializer().run() save.save(sess, save_dir) - orig_vals = sess.run(orig_vars) + orig_vals = self.evaluate(orig_vars) restore_graph = ops_lib.Graph() with restore_graph.as_default(), self.session( @@ -836,7 +845,7 @@ class SaverTest(test.TestCase): restored_vars = _model() save = saver_module.Saver(max_to_keep=1) save.restore(sess, save_dir) - restored_vals = sess.run(restored_vars) + restored_vals = self.evaluate(restored_vars) for orig, restored in zip(orig_vals, restored_vals): self.assertAllEqual(orig, restored) @@ -982,6 +991,7 @@ class SaveRestoreShardedTest(test.TestCase): checkpoint_management.latest_checkpoint(self.get_temp_dir()), os.path.join(self.get_temp_dir(), "sharded_basics")) + @test_util.run_deprecated_v1 def testSaverDef(self): with self.cached_session(): v0 = variables.VariableV1(123, name="v0") @@ -998,19 +1008,12 @@ class SaveRestoreShardedTest(test.TestCase): call_saver_with_dict = False # updated by test loop below - def _save(slices=None, partitioner=None): + def _save(partitioner=None): with self.session(graph=ops_lib.Graph()) as sess: # Calls .eval() to return the ndarray that makes up the full variable. rnd = random_ops.random_uniform(var_full_shape).eval() - if slices: - assert not partitioner - # TODO(apassos): make create_partitioned_variables take use_resource - # option to make this test passable without creating a named - # variable_scope. - vs = partitioned_variables.create_partitioned_variables( - var_full_shape, slices, rnd, name=var_name) - elif partitioner: + if partitioner: vs = [ variable_scope.get_variable( var_name, @@ -1027,7 +1030,7 @@ class SaveRestoreShardedTest(test.TestCase): variables.global_variables_initializer().run() if call_saver_with_dict: - saver = saver_module.Saver({var_name: (vs if slices else vs[0])}) + saver = saver_module.Saver({var_name: vs[0]}) else: saver = saver_module.Saver(vs) actual_path = saver.save(sess, saved_path) @@ -1035,16 +1038,9 @@ class SaveRestoreShardedTest(test.TestCase): return rnd - def _restore(slices=None, partitioner=None): + def _restore(partitioner=None): with self.session(graph=ops_lib.Graph()) as sess: - if slices: - assert not partitioner - new_vs = partitioned_variables.create_partitioned_variables( - var_full_shape, - slices, - array_ops.zeros(var_full_shape), # != original contents. - name=var_name) - elif partitioner: + if partitioner: new_vs = [ variable_scope.get_variable( var_name, @@ -1063,7 +1059,7 @@ class SaveRestoreShardedTest(test.TestCase): variables.global_variables_initializer().run() if call_saver_with_dict: saver = saver_module.Saver({ - var_name: (new_vs if slices else new_vs[0]) + var_name: new_vs[0] }) else: saver = saver_module.Saver(new_vs) @@ -1071,11 +1067,7 @@ class SaveRestoreShardedTest(test.TestCase): if partitioner: return new_vs[0].as_tensor().eval() - elif slices and slices[0] != 1: - return array_ops.concat(new_vs, 0).eval() - elif slices and slices[1] != 1: - return array_ops.concat(new_vs, 1).eval() - else: # Non-sliced. + else: return new_vs[0].eval() for call_saver_with_dict in {False, True}: @@ -1086,32 +1078,30 @@ class SaveRestoreShardedTest(test.TestCase): restored_full = _restore() self.assertAllEqual(saved_full, restored_full) - # Saves 10 horizontal parts of a partitioned variable. - # Restores into a full variable, non-sliced. - saved_full = _save(slices=[10, 1]) - restored_full = _restore() - self.assertAllEqual(saved_full, restored_full) - - # Restores into a different number/orientation of slices. - restored_full = _restore(slices=[2, 1]) # 2 horizon parts. - self.assertAllEqual(saved_full, restored_full) - restored_full = _restore(slices=[1, 3]) # 3 vertical parts. + # Restores into the same number of partitions. + restored_full = _restore( + partitioner=partitioned_variables.fixed_size_partitioner( + num_shards=2)) self.assertAllEqual(saved_full, restored_full) - # Restores into a PartitionedVariable + # Restores into a different number of partitions. restored_full = _restore( partitioner=partitioned_variables.fixed_size_partitioner( - num_shards=2)) + num_shards=3)) self.assertAllEqual(saved_full, restored_full) - # Now, saves a full variable and restores in slices. + # Now, saves a full variable and restores PartitionedVariable. saved_full = _save() - restored_full = _restore(slices=[1, 3]) + restored_full = _restore( + partitioner=partitioned_variables.fixed_size_partitioner( + num_shards=3)) self.assertAllEqual(saved_full, restored_full) + @test_util.run_deprecated_v1 def testPartitionedVariable(self): self._testPartitionedVariables(use_resource=False) + @test_util.run_deprecated_v1 def testPartitionedResourceVariable(self): self._testPartitionedVariables(use_resource=True) @@ -1206,6 +1196,7 @@ class MaxToKeepTest(test.TestCase): # Deleted by the first helper. self.assertFalse(checkpoint_management.checkpoint_exists(s3)) + @test_util.run_deprecated_v1 def testNonSharded(self): save_dir = self._get_test_dir("max_to_keep_non_sharded") @@ -1443,6 +1434,7 @@ class MaxToKeepTest(test.TestCase): self.assertTrue( gfile.Exists(checkpoint_management.meta_graph_filename(s3))) + @test_util.run_deprecated_v1 def testNoMaxToKeep(self): save_dir = self._get_test_dir("no_max_to_keep") save_dir2 = self._get_test_dir("max_to_keep_0") @@ -1471,6 +1463,7 @@ class MaxToKeepTest(test.TestCase): self.assertEqual([], save2.last_checkpoints) self.assertTrue(checkpoint_management.checkpoint_exists(s2)) + @test_util.run_deprecated_v1 def testNoMetaGraph(self): save_dir = self._get_test_dir("no_meta_graph") @@ -1494,6 +1487,7 @@ class KeepCheckpointEveryNHoursTest(test.TestCase): @test_util.run_in_graph_and_eager_modes @test.mock.patch.object(saver_module, "time") + @test_util.run_deprecated_v1 def testNonSharded(self, mock_time): save_dir = self._get_test_dir("keep_checkpoint_every_n_hours") @@ -1613,6 +1607,7 @@ class SaveRestoreWithVariableNameMap(test.TestCase): self.assertEqual(20.0, self.evaluate(v1)) @test_util.run_in_graph_and_eager_modes + @test_util.run_deprecated_v1 def testNonReshapeResourceVariable(self): self._testNonReshape(resource_variable_ops.ResourceVariable) @@ -1627,6 +1622,7 @@ class MetaGraphTest(test.TestCase): gfile.MakeDirs(test_dir) return test_dir + @test_util.run_deprecated_v1 def testAddCollectionDef(self): test_dir = self._get_test_dir("good_collection") filename = os.path.join(test_dir, "metafile") @@ -1769,18 +1765,20 @@ class MetaGraphTest(test.TestCase): self.assertEqual([], v1.get_shape()) with self.assertRaisesWithPredicateMatch( errors_impl.OpError, lambda e: "uninitialized value v1" in e.message): - sess.run(v1) + self.evaluate(v1) # Retrieves saver1. Verifies that new_saver1 can restore v1. new_saver1 = savers[1] new_saver1.restore(sess, saver1_ckpt) v1 = sess.graph.get_tensor_by_name("v1:0") self.assertEqual(11.0, v1.eval()) + @test_util.run_deprecated_v1 def testMultiSaverCollection(self): test_dir = self._get_test_dir("saver_collection") self._testMultiSaverCollectionSave(test_dir) self._testMultiSaverCollectionRestore(test_dir) + @test_util.run_deprecated_v1 def testClearExtraneousSavers(self): test_dir = self._get_test_dir("clear_extraneous_savers") filename = os.path.join(test_dir, "metafile") @@ -1835,6 +1833,7 @@ class MetaGraphTest(test.TestCase): self.assertEqual(33, len(meta_graph_def0.graph_def.node)) self.assertEqual(21, len(meta_graph_def1.graph_def.node)) + @test_util.run_deprecated_v1 def testBinaryAndTextFormat(self): test_dir = self._get_test_dir("binary_and_text") filename = os.path.join(test_dir, "metafile") @@ -1867,6 +1866,7 @@ class MetaGraphTest(test.TestCase): lambda e: "does not exist"): saver_module.import_meta_graph(filename) + @test_util.run_deprecated_v1 def testSliceVariable(self): test_dir = self._get_test_dir("slice_saver") filename = os.path.join(test_dir, "metafile") @@ -1949,9 +1949,9 @@ class MetaGraphTest(test.TestCase): with self.cached_session() as sess: # Initializes all the variables. - sess.run(init_all_op) + self.evaluate(init_all_op) # Runs to logit. - sess.run(logits) + self.evaluate(logits) # Creates a saver. saver0 = saver_module.Saver() saver0.save(sess, saver0_ckpt) @@ -1991,7 +1991,7 @@ class MetaGraphTest(test.TestCase): ops_lib.add_to_collection("train_op", train_op) # Runs train_op. - sess.run(train_op) + self.evaluate(train_op) # Generates MetaGraphDef. saver_module.export_meta_graph(train_filename) @@ -2005,8 +2005,9 @@ class MetaGraphTest(test.TestCase): # Restores from checkpoint. new_saver.restore(sess, saver0_ckpt) train_op = ops_lib.get_collection("train_op")[0] - sess.run(train_op) + self.evaluate(train_op) + @test_util.run_deprecated_v1 def testGraphExtension(self): test_dir = self._get_test_dir("graph_extension") self._testGraphExtensionSave(test_dir) @@ -2037,8 +2038,8 @@ class MetaGraphTest(test.TestCase): # Generate a MetaGraphDef containing the while loop. with session.Session() as sess: - sess.run(init_op) - sess.run(output) + self.evaluate(init_op) + self.evaluate(output) saver = saver_module.Saver() saver.save(sess, saver_ckpt) saver.export_meta_graph(filename) @@ -2053,8 +2054,8 @@ class MetaGraphTest(test.TestCase): no_constfold_config.graph_options.rewrite_options.constant_folding = ( rewriter_config_pb2.RewriterConfig.OFF) with session.Session(config=no_constfold_config) as sess: - sess.run(init_op) - expected_grad_value = sess.run(grad) + self.evaluate(init_op) + expected_grad_value = self.evaluate(grad) # Restore the MetaGraphDef into a new Graph. with ops_lib.Graph().as_default(): @@ -2070,8 +2071,8 @@ class MetaGraphTest(test.TestCase): init_op = variables.global_variables_initializer() with session.Session(config=no_constfold_config) as sess: - sess.run(init_op) - actual_grad_value = sess.run(grad) + self.evaluate(init_op) + actual_grad_value = self.evaluate(grad) self.assertEqual(expected_grad_value, actual_grad_value) def _testWhileLoopAndGradientSerDes(self, outer_body_fn): @@ -2092,6 +2093,7 @@ class MetaGraphTest(test.TestCase): return i + 1, x + r self._testWhileLoopAndGradientSerDes(body) + @test_util.run_deprecated_v1 def testNestedControlFlowSerDes(self): # Test while loop in a cond in a while loop. # pylint: disable=g-long-lambda @@ -2120,6 +2122,7 @@ class MetaGraphTest(test.TestCase): lambda: math_ops.multiply(x, -1.0)))) # pylint: enable=g-long-lambda + @test_util.run_deprecated_v1 def testStrippedOpListDef(self): with self.cached_session(): # Creates a graph. @@ -2157,6 +2160,7 @@ class MetaGraphTest(test.TestCase): self.assertEqual(o.summary, "") self.assertEqual(o.description, "") + @test_util.run_deprecated_v1 def testStripDefaultValuedAttrs(self): """Verifies that default valued attrs are stripped, unless disabled.""" @@ -2193,6 +2197,7 @@ class MetaGraphTest(test.TestCase): self.assertIn("T", node_def.attr) self.assertIn("Tout", node_def.attr) + @test_util.run_deprecated_v1 def testImportIntoNamescope(self): # Test that we can import a meta graph into a namescope. test_dir = self._get_test_dir("import_into_namescope") @@ -2209,7 +2214,7 @@ class MetaGraphTest(test.TestCase): logits=logit, name="cost") adam.AdamOptimizer().minimize(cost, name="optimize") saver = saver_module.Saver() - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) saver.save(sess, filename) graph = ops_lib.Graph() @@ -2246,7 +2251,7 @@ class MetaGraphTest(test.TestCase): # Create a variable in graph_2 under scope "my_scope". variables.VariableV1(array_ops.zeros([10]), name="my_scope/my_var") - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) # Restore the checkpoint into a different scope "subgraph_2". new_saver_2 = saver_module.import_meta_graph( filename + ".meta", graph=graph_2, import_scope="subgraph_2") @@ -2263,6 +2268,7 @@ class MetaGraphTest(test.TestCase): filename + ".meta", graph=graph_2, import_scope="my_scope") self.assertIsInstance(new_saver_3, saver_module.Saver) + @test_util.run_deprecated_v1 def testImportIntoImplicitNamescope(self): # Test that we can import a meta graph into an implicit namescope. test_dir = self._get_test_dir("import_into_namescope") @@ -2279,7 +2285,7 @@ class MetaGraphTest(test.TestCase): logits=logit, name="cost") adam.AdamOptimizer().minimize(cost, name="optimize") saver = saver_module.Saver() - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) saver.save(sess, filename) graph = ops_lib.Graph() @@ -2316,12 +2322,12 @@ class MetaGraphTest(test.TestCase): meta_graph_def, clear_devices=False, import_scope="new_model") # Device refers to GPU, which is not available here. with self.assertRaises(errors_impl.InvalidArgumentError): - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) with session.Session(graph=ops_lib.Graph()) as sess: saver_module.import_meta_graph( meta_graph_def, clear_devices=True, import_scope="new_model") - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) sess.run(["new_model/optimize"], { "new_model/image:0": np.random.random([1, 784]), "new_model/label:0": np.random.randint( @@ -2348,7 +2354,7 @@ class MetaGraphTest(test.TestCase): with session.Session(graph=ops_lib.Graph()) as sess: saver_module.import_meta_graph(meta_graph_def, import_scope="new_model") - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) sess.run(["new_model/optimize"], { "new_model/image:0": np.random.random([1, 784]), "new_model/label:0": np.random.randint( @@ -2358,7 +2364,7 @@ class MetaGraphTest(test.TestCase): def testPreserveDatasetAndFunctions(self): with ops_lib.Graph().as_default() as g: dataset = dataset_ops.Dataset.range(10).map(lambda x: x * x) - iterator = dataset.make_one_shot_iterator() + iterator = dataset_ops.make_one_shot_iterator(dataset) next_element = iterator.get_next() _ = array_ops.identity(next_element, name="output") @@ -2374,7 +2380,7 @@ class MetaGraphTest(test.TestCase): meta_graph_def_from_graph_def]: with session.Session(graph=ops_lib.Graph()) as sess: saver_module.import_meta_graph(meta_graph_def, import_scope="new_model") - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) for i in range(10): self.assertEqual(i * i, sess.run("new_model/output:0")) with self.assertRaises(errors.OutOfRangeError): @@ -2385,6 +2391,7 @@ class CheckpointReaderTest(test.TestCase): _WRITE_VERSION = saver_pb2.SaverDef.V1 + @test_util.run_deprecated_v1 def testDebugString(self): # Builds a graph. v0 = variables.VariableV1( @@ -2400,7 +2407,7 @@ class CheckpointReaderTest(test.TestCase): save_path = os.path.join(self.get_temp_dir(), "ckpt_for_debug_string" + str(self._WRITE_VERSION)) with self.cached_session() as sess: - sess.run(init_all_op) + self.evaluate(init_all_op) # Saves a checkpoint. save.save(sess, save_path) @@ -2546,7 +2553,7 @@ class ScopedGraphTest(test.TestCase): self.assertEqual(["biases:0", "weights:0"], sorted(var_list.keys())) with self.session(graph=graph) as sess: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) saver = saver_module.Saver(var_list=var_list, max_to_keep=1) saver.save(sess, os.path.join(test_dir, ckpt_filename), write_state=False) @@ -2609,13 +2616,14 @@ class ScopedGraphTest(test.TestCase): saver = saver_module.Saver(var_list=var_list, max_to_keep=1) saver.restore(sess, os.path.join(test_dir, ckpt_filename)) # Verify that we have restored weights1 and biases1. - sess.run([weights1, biases1]) + self.evaluate([weights1, biases1]) # Initialize the rest of the variables and run logits. - sess.run(init_rest_op) - sess.run(logits) + self.evaluate(init_rest_op) + self.evaluate(logits) # Verifies that we can save the subgraph under "hidden1" and restore it # into "new_hidden1" in the new graph. + @test_util.run_deprecated_v1 def testScopedSaveAndRestore(self): test_dir = self._get_test_dir("scoped_export_import") ckpt_filename = "ckpt" @@ -2625,6 +2633,7 @@ class ScopedGraphTest(test.TestCase): # Verifies that we can copy the subgraph under "hidden1" and copy it # to different name scope in the same graph or different graph. + @test_util.run_deprecated_v1 def testCopyScopedGraph(self): test_dir = self._get_test_dir("scoped_copy") saver0_ckpt = os.path.join(test_dir, "saver0.ckpt") @@ -2640,7 +2649,7 @@ class ScopedGraphTest(test.TestCase): # Run the graph and save scoped checkpoint. with self.session(graph=graph1) as sess: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) _, var_list_1 = meta_graph.export_scoped_meta_graph( export_scope="hidden1") saver = saver_module.Saver(var_list=var_list_1, max_to_keep=1) @@ -2681,6 +2690,7 @@ class ScopedGraphTest(test.TestCase): saver3.restore(sess, saver0_ckpt) self.assertAllClose(expected, sess.run("new_hidden1/relu:0")) + @test_util.run_deprecated_v1 def testExportGraphDefWithScope(self): test_dir = self._get_test_dir("export_graph_def") saver0_ckpt = os.path.join(test_dir, "saver0.ckpt") @@ -2696,7 +2706,7 @@ class ScopedGraphTest(test.TestCase): # Run the graph and save scoped checkpoint. with self.session(graph=graph1) as sess: - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) _, var_list_1 = meta_graph.export_scoped_meta_graph( graph_def=graph1.as_graph_def(), export_scope="hidden1") saver = saver_module.Saver(var_list=var_list_1, max_to_keep=1) @@ -2717,6 +2727,7 @@ class ScopedGraphTest(test.TestCase): saver3.restore(sess, saver0_ckpt) self.assertAllClose(expected, sess.run("new_hidden1/relu:0")) + @test_util.run_deprecated_v1 def testSerializeSaverWithScope(self): test_dir = self._get_test_dir("export_graph_def") saver1_ckpt = os.path.join(test_dir, "saver1.ckpt") @@ -2964,7 +2975,7 @@ class CheckpointableCompatibilityTests(test.TestCase): a_saver = saver_module.Saver([a]) b_saver = saver_module.Saver([b]) with self.cached_session() as sess: - sess.run(a.initializer) + self.evaluate(a.initializer) save_path = a_saver.save(sess=sess, save_path=checkpoint_prefix) with self.assertRaisesRegexp( errors.NotFoundError, "Key b not found in checkpoint"): @@ -2977,6 +2988,7 @@ class CheckpointableCompatibilityTests(test.TestCase): # exception" block in Python 3. self.assertNotIn("NewCheckpointReader", cs.exception.message) + @test_util.run_deprecated_v1 def testGraphChangedForRestoreErrorRaised(self): checkpoint_directory = self.get_temp_dir() checkpoint_prefix = os.path.join(checkpoint_directory, "ckpt") @@ -2986,7 +2998,7 @@ class CheckpointableCompatibilityTests(test.TestCase): a_saver = saver_module.Saver([a]) with self.session(graph=g) as sess: - sess.run(a.initializer) + self.evaluate(a.initializer) save_path = a_saver.save(sess=sess, save_path=checkpoint_prefix) with ops_lib.Graph().as_default() as g: @@ -2998,6 +3010,7 @@ class CheckpointableCompatibilityTests(test.TestCase): "a mismatch between the current graph and the graph"): a_saver.restore(sess=sess, save_path=save_path) + @test_util.run_deprecated_v1 def testLoadFromObjectBasedGraph(self): checkpoint_directory = self.get_temp_dir() checkpoint_prefix = os.path.join(checkpoint_directory, "ckpt") @@ -3029,7 +3042,7 @@ class CheckpointableCompatibilityTests(test.TestCase): self.assertEqual(before_second_restore_ops, restore_graph.get_operations()) with self.assertRaisesRegexp(errors.NotFoundError, - "could not find a_variable"): + "Could not find some variables"): saver.restore(sess=sess, save_path=second_path) def testLoadFromObjectBasedEager(self): diff --git a/tensorflow/python/training/server_lib_multiple_containers_test.py b/tensorflow/python/training/server_lib_multiple_containers_test.py index f599e9b55b..fb6118942b 100644 --- a/tensorflow/python/training/server_lib_multiple_containers_test.py +++ b/tensorflow/python/training/server_lib_multiple_containers_test.py @@ -21,6 +21,7 @@ from __future__ import print_function from tensorflow.python.client import session from tensorflow.python.framework import errors_impl from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import variables from tensorflow.python.platform import test from tensorflow.python.training import server_lib @@ -33,6 +34,7 @@ class MultipleContainersTest(test.TestCase): # TODO(b/34465411): Starting multiple servers with different configurations # in the same test is flaky. Move this test case back into # "server_lib_test.py" when this is no longer the case. + @test_util.run_deprecated_v1 def testMultipleContainers(self): with ops.container("test0"): v0 = variables.Variable(1.0, name="v0") diff --git a/tensorflow/python/training/server_lib_same_variables_clear_container_test.py b/tensorflow/python/training/server_lib_same_variables_clear_container_test.py index 11e6f28ab0..e0ab21bbd9 100644 --- a/tensorflow/python/training/server_lib_same_variables_clear_container_test.py +++ b/tensorflow/python/training/server_lib_same_variables_clear_container_test.py @@ -20,6 +20,7 @@ from __future__ import print_function from tensorflow.python.client import session from tensorflow.python.framework import errors_impl +from tensorflow.python.framework import test_util from tensorflow.python.ops import variables from tensorflow.python.platform import test from tensorflow.python.training import server_lib @@ -32,6 +33,7 @@ class SameVariablesClearContainerTest(test.TestCase): # TODO(b/34465411): Starting multiple servers with different configurations # in the same test is flaky. Move this test case back into # "server_lib_test.py" when this is no longer the case. + @test_util.run_deprecated_v1 def testSameVariablesClearContainer(self): # Starts two servers with different names so they map to different # resource "containers". @@ -60,9 +62,9 @@ class SameVariablesClearContainerTest(test.TestCase): session.Session.reset(server0.target, ["local0"]) sess = session.Session(server0.target) with self.assertRaises(errors_impl.FailedPreconditionError): - sess.run(v0) + self.evaluate(v0) # Reinitializes v0 for the following test. - sess.run(v0.initializer) + self.evaluate(v0.initializer) # Verifies that v1 is still valid. self.assertAllEqual(2.0, sess_1.run(v1)) @@ -71,10 +73,10 @@ class SameVariablesClearContainerTest(test.TestCase): session.Session.reset(server1.target, ["local1"]) sess = session.Session(server1.target) with self.assertRaises(errors_impl.FailedPreconditionError): - sess.run(v1) + self.evaluate(v1) # Verifies that v0 is still valid. sess = session.Session(server0.target) - self.assertAllEqual(1.0, sess.run(v0)) + self.assertAllEqual(1.0, self.evaluate(v0)) if __name__ == "__main__": diff --git a/tensorflow/python/training/server_lib_same_variables_clear_test.py b/tensorflow/python/training/server_lib_same_variables_clear_test.py index 4682f1ab84..7b147af6c5 100644 --- a/tensorflow/python/training/server_lib_same_variables_clear_test.py +++ b/tensorflow/python/training/server_lib_same_variables_clear_test.py @@ -20,6 +20,7 @@ from __future__ import print_function from tensorflow.python.client import session from tensorflow.python.framework import errors_impl +from tensorflow.python.framework import test_util from tensorflow.python.ops import math_ops from tensorflow.python.ops import variables from tensorflow.python.platform import test @@ -32,6 +33,7 @@ class SameVariablesClearTest(test.TestCase): # TODO(b/34465411): Starting multiple servers with different configurations # in the same test is flaky. Move this test case back into # "server_lib_test.py" when this is no longer the case. + @test_util.run_deprecated_v1 def testSameVariablesClear(self): server = server_lib.Server.create_local_server() diff --git a/tensorflow/python/training/server_lib_same_variables_no_clear_test.py b/tensorflow/python/training/server_lib_same_variables_no_clear_test.py index 5aa7f45c2b..1b2d588f44 100644 --- a/tensorflow/python/training/server_lib_same_variables_no_clear_test.py +++ b/tensorflow/python/training/server_lib_same_variables_no_clear_test.py @@ -20,6 +20,7 @@ from __future__ import print_function from tensorflow.python.client import session from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import math_ops from tensorflow.python.ops import variables from tensorflow.python.platform import test @@ -33,6 +34,7 @@ class SameVariablesNoClearTest(test.TestCase): # TODO(b/34465411): Starting multiple servers with different configurations # in the same test is flaky. Move this test case back into # "server_lib_test.py" when this is no longer the case. + @test_util.run_deprecated_v1 def testSameVariablesNoClear(self): server = server_lib.Server.create_local_server() diff --git a/tensorflow/python/training/server_lib_sparse_job_test.py b/tensorflow/python/training/server_lib_sparse_job_test.py index 1a6b44b90e..93b06e6216 100644 --- a/tensorflow/python/training/server_lib_sparse_job_test.py +++ b/tensorflow/python/training/server_lib_sparse_job_test.py @@ -21,6 +21,7 @@ from __future__ import print_function from tensorflow.python.client import session from tensorflow.python.framework import constant_op from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.platform import test from tensorflow.python.training import server_lib @@ -30,13 +31,14 @@ class SparseJobTest(test.TestCase): # TODO(b/34465411): Starting multiple servers with different configurations # in the same test is flaky. Move this test case back into # "server_lib_test.py" when this is no longer the case. + @test_util.run_deprecated_v1 def testSparseJob(self): server = server_lib.Server({"local": {37: "localhost:0"}}) with ops.device("/job:local/task:37"): a = constant_op.constant(1.0) with session.Session(server.target) as sess: - self.assertEqual(1.0, sess.run(a)) + self.assertEqual(1.0, self.evaluate(a)) if __name__ == "__main__": diff --git a/tensorflow/python/training/server_lib_test.py b/tensorflow/python/training/server_lib_test.py index cf995707fc..323e94c257 100644 --- a/tensorflow/python/training/server_lib_test.py +++ b/tensorflow/python/training/server_lib_test.py @@ -174,7 +174,7 @@ class GrpcServerTest(test.TestCase): # is not supported, but it should successfully ignore it. sess = session.InteractiveSession(server.target) c = constant_op.constant(42.0) - self.assertEqual(42.0, c.eval()) + self.assertEqual(42.0, self.evaluate(c)) sess.close() def testSetConfiguration(self): diff --git a/tensorflow/python/training/session_manager.py b/tensorflow/python/training/session_manager.py index cd313c2ce0..14658630c5 100644 --- a/tensorflow/python/training/session_manager.py +++ b/tensorflow/python/training/session_manager.py @@ -46,7 +46,7 @@ def _maybe_name(obj): return "" % type(obj) -@tf_export("train.SessionManager") +@tf_export(v1=["train.SessionManager"]) class SessionManager(object): """Training helper that restores from checkpoint and creates session. diff --git a/tensorflow/python/training/session_manager_test.py b/tensorflow/python/training/session_manager_test.py index 2b5c3b01de..4294ffa851 100644 --- a/tensorflow/python/training/session_manager_test.py +++ b/tensorflow/python/training/session_manager_test.py @@ -25,6 +25,7 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors from tensorflow.python.framework import errors_impl from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import variables @@ -68,6 +69,7 @@ class SessionManagerTest(test.TestCase): "", init_fn=lambda sess: sess.run(v.initializer)) self.assertAllClose([125], sess.run(v)) + @test_util.run_deprecated_v1 def testPrepareSessionFails(self): checkpoint_dir = os.path.join(self.get_temp_dir(), "prepare_session") checkpoint_dir2 = os.path.join(self.get_temp_dir(), "prepare_session2") @@ -152,6 +154,7 @@ class SessionManagerTest(test.TestCase): sess.graph.get_tensor_by_name("v:0")).eval(session=sess)) self.assertEquals(1, sess.run(v)) + @test_util.run_deprecated_v1 def testRecoverSession(self): # Create a checkpoint. checkpoint_dir = os.path.join(self.get_temp_dir(), "recover_session") @@ -206,6 +209,7 @@ class SessionManagerTest(test.TestCase): variables.global_variables()), local_init_op=None) + @test_util.run_deprecated_v1 def testRecoverSessionWithReadyForLocalInitOp(self): # Create a checkpoint. checkpoint_dir = os.path.join(self.get_temp_dir(), @@ -259,6 +263,7 @@ class SessionManagerTest(test.TestCase): self.assertEquals(1, sess.run(v)) self.assertEquals(1, sess.run(w)) + @test_util.run_deprecated_v1 def testRecoverSessionWithReadyForLocalInitOpFailsToReadyLocal(self): # We use ready_for_local_init_op=tf.report_uninitialized_variables(), # which causes recover_session to not run local_init_op, and to return @@ -315,6 +320,7 @@ class SessionManagerTest(test.TestCase): sess.graph.get_tensor_by_name("w:0")).eval(session=sess)) self.assertEquals(1, sess.run(v)) + @test_util.run_deprecated_v1 def testRecoverSessionNoChkptStillRunsLocalInitOp(self): # This test checks for backwards compatibility. # In particular, we continue to ensure that recover_session will execute @@ -343,6 +349,7 @@ class SessionManagerTest(test.TestCase): sess.graph.get_tensor_by_name("w:0")).eval(session=sess)) self.assertEquals(1, sess.run(w)) + @test_util.run_deprecated_v1 def testRecoverSessionFailsStillRunsLocalInitOp(self): # Create a checkpoint. checkpoint_dir = os.path.join( @@ -386,6 +393,7 @@ class SessionManagerTest(test.TestCase): sess.graph.get_tensor_by_name("w:0")).eval(session=sess)) self.assertEquals(1, sess.run(w)) + @test_util.run_deprecated_v1 def testWaitForSessionLocalInit(self): server = server_lib.Server.create_local_server() with ops.Graph().as_default() as graph: @@ -437,6 +445,7 @@ class SessionManagerTest(test.TestCase): # because of overly restrictive ready_for_local_init_op sm.wait_for_session("", max_wait_secs=3) + @test_util.run_deprecated_v1 def testWaitForSessionInsufficientReadyForLocalInitCheck(self): with ops.Graph().as_default() as graph: v = variables.VariableV1(1, name="v") @@ -454,6 +463,7 @@ class SessionManagerTest(test.TestCase): "Session was not ready after waiting.*"): sm.wait_for_session("", max_wait_secs=3) + @test_util.run_deprecated_v1 def testPrepareSessionWithReadyForLocalInitOp(self): with ops.Graph().as_default(): v = variables.VariableV1(1, name="v") @@ -493,6 +503,7 @@ class SessionManagerTest(test.TestCase): self.assertEquals(1, sess.run(w)) self.assertEquals(3, sess.run(x)) + @test_util.run_deprecated_v1 def testPrepareSessionWithPartialInitOp(self): with ops.Graph().as_default(): v = variables.VariableV1(1, name="v") @@ -559,6 +570,7 @@ class SessionManagerTest(test.TestCase): self.assertEquals(1, sess.run(w_res)) self.assertEquals(3, sess.run(x_res)) + @test_util.run_deprecated_v1 def testPrepareSessionWithCyclicInitializer(self): # Regression test. Previously Variable._build_initializer_expr would enter # into an infinite recursion when the variable's initial_value involved @@ -632,6 +644,7 @@ class SessionManagerTest(test.TestCase): "Init operations did not make model ready for local_init"): sm2.prepare_session("", init_op=None) + @test_util.run_deprecated_v1 def testPrepareSessionWithInsufficientReadyForLocalInitCheck(self): with ops.Graph().as_default(): v = variables.VariableV1(1, name="v") @@ -684,6 +697,7 @@ class ObsoleteSessionManagerTest(test.TestCase): "", init_fn=lambda sess: sess.run(v.initializer)) self.assertAllClose([125], sess.run(v)) + @test_util.run_deprecated_v1 def testPrepareSessionFails(self): checkpoint_dir = os.path.join(self.get_temp_dir(), "prepare_session") checkpoint_dir2 = os.path.join(self.get_temp_dir(), "prepare_session2") @@ -745,6 +759,7 @@ class ObsoleteSessionManagerTest(test.TestCase): variables.is_variable_initialized( sess.graph.get_tensor_by_name("v:0")).eval(session=sess)) + @test_util.run_deprecated_v1 def testRecoverSession(self): # Create a checkpoint. checkpoint_dir = os.path.join(self.get_temp_dir(), "recover_session") diff --git a/tensorflow/python/training/session_run_hook.py b/tensorflow/python/training/session_run_hook.py index 5daea93128..e9a61def74 100644 --- a/tensorflow/python/training/session_run_hook.py +++ b/tensorflow/python/training/session_run_hook.py @@ -186,7 +186,7 @@ class SessionRunHook(object): pass -@tf_export("train.SessionRunArgs") +@tf_export(v1=["train.SessionRunArgs"]) class SessionRunArgs( collections.namedtuple("SessionRunArgs", ["fetches", "feed_dict", "options"])): @@ -211,7 +211,7 @@ class SessionRunArgs( return super(SessionRunArgs, cls).__new__(cls, fetches, feed_dict, options) -@tf_export("train.SessionRunContext") +@tf_export(v1=["train.SessionRunContext"]) class SessionRunContext(object): """Provides information about the `session.run()` call being made. @@ -263,7 +263,7 @@ class SessionRunContext(object): self._stop_requested = True -@tf_export("train.SessionRunValues") +@tf_export(v1=["train.SessionRunValues"]) class SessionRunValues( collections.namedtuple("SessionRunValues", ["results", "options", "run_metadata"])): diff --git a/tensorflow/python/training/slot_creator_test.py b/tensorflow/python/training/slot_creator_test.py index 6d6364169f..1f26aaa434 100644 --- a/tensorflow/python/training/slot_creator_test.py +++ b/tensorflow/python/training/slot_creator_test.py @@ -21,6 +21,7 @@ from __future__ import print_function from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import random_ops from tensorflow.python.ops import variable_scope @@ -31,6 +32,7 @@ from tensorflow.python.training import slot_creator class SlotCreatorTest(test.TestCase): + @test_util.run_deprecated_v1 def testCreateSlotFromVariable(self): with self.cached_session(): v = variables.Variable([1.0, 2.5], name="var") @@ -41,8 +43,9 @@ class SlotCreatorTest(test.TestCase): self.assertEqual("var/slot", slot.op.name) self.assertEqual([2], slot.get_shape().as_list()) self.assertEqual(dtypes.float32, slot.dtype.base_dtype) - self.assertAllEqual([1.0, 2.5], slot.eval()) + self.assertAllEqual([1.0, 2.5], self.evaluate(slot)) + @test_util.run_deprecated_v1 def testCreateSlotFromTensor(self): with self.cached_session(): v = constant_op.constant([1.0, 2.5], name="const") @@ -53,8 +56,9 @@ class SlotCreatorTest(test.TestCase): self.assertEqual("const/slot", slot.op.name) self.assertEqual([2], slot.get_shape().as_list()) self.assertEqual(dtypes.float32, slot.dtype.base_dtype) - self.assertAllEqual([2.0, 5.0], slot.eval()) + self.assertAllEqual([2.0, 5.0], self.evaluate(slot)) + @test_util.run_deprecated_v1 def testCreateZerosSlotFromVariable(self): with self.cached_session(): v = variables.Variable([1.0, 2.5], name="var") @@ -67,8 +71,9 @@ class SlotCreatorTest(test.TestCase): self.assertEqual("var/slot", slot.op.name) self.assertEqual([2], slot.get_shape().as_list()) self.assertEqual(dtypes.float64, slot.dtype.base_dtype) - self.assertAllEqual([0.0, 0.0], slot.eval()) + self.assertAllEqual([0.0, 0.0], self.evaluate(slot)) + @test_util.run_deprecated_v1 def testCreateZerosSlotFromDynamicShapedVariable(self): with self.cached_session(): dyn_shape = constant_op.constant([2], dtype=dtypes.int32) @@ -88,8 +93,9 @@ class SlotCreatorTest(test.TestCase): self.assertEqual("var/slot", slot.op.name) self.assertEqual([2], array_ops.shape(slot).eval()) self.assertEqual(dtypes.float64, slot.dtype.base_dtype) - self.assertAllEqual([0.0, 0.0], slot.eval()) + self.assertAllEqual([0.0, 0.0], self.evaluate(slot)) + @test_util.run_deprecated_v1 def testCreateZerosSlotFromTensor(self): with self.cached_session(): v = constant_op.constant([1.0, 2.5], name="const") @@ -101,8 +107,9 @@ class SlotCreatorTest(test.TestCase): self.assertEqual("const/slot", slot.op.name) self.assertEqual([2], slot.get_shape().as_list()) self.assertEqual(dtypes.float32, slot.dtype.base_dtype) - self.assertAllEqual([0.0, 0.0], slot.eval()) + self.assertAllEqual([0.0, 0.0], self.evaluate(slot)) + @test_util.run_deprecated_v1 def testCreateZerosSlotFromDynamicShapedTensor(self): with self.cached_session(): v = random_ops.random_uniform([2], dtype=dtypes.float64) @@ -116,8 +123,9 @@ class SlotCreatorTest(test.TestCase): self.assertEqual("const/slot", slot.op.name) self.assertEqual([2], array_ops.shape(slot).eval()) self.assertEqual(dtypes.float64, slot.dtype.base_dtype) - self.assertAllEqual([0.0, 0.0], slot.eval()) + self.assertAllEqual([0.0, 0.0], self.evaluate(slot)) + @test_util.run_deprecated_v1 def testCreateSlotFromVariableRespectsScope(self): # See discussion on #2740. with self.cached_session(): diff --git a/tensorflow/python/training/supervisor.py b/tensorflow/python/training/supervisor.py index a5e626d320..de60dd456f 100644 --- a/tensorflow/python/training/supervisor.py +++ b/tensorflow/python/training/supervisor.py @@ -40,7 +40,7 @@ from tensorflow.python.util import deprecation from tensorflow.python.util.tf_export import tf_export -@tf_export("train.Supervisor") +@tf_export(v1=["train.Supervisor"]) class Supervisor(object): """A training helper that checkpoints models and computes summaries. diff --git a/tensorflow/python/training/supervisor_test.py b/tensorflow/python/training/supervisor_test.py index 7cd99d8680..f6505acc9a 100644 --- a/tensorflow/python/training/supervisor_test.py +++ b/tensorflow/python/training/supervisor_test.py @@ -35,6 +35,7 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors_impl from tensorflow.python.framework import meta_graph from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import io_ops from tensorflow.python.ops import parsing_ops @@ -100,7 +101,7 @@ class SupervisorTest(test.TestCase): sv = supervisor.Supervisor(logdir=logdir) sess = sv.prepare_or_wait_for_session("") for _ in xrange(10): - sess.run(my_op) + self.evaluate(my_op) sess.close() sv.stop() @@ -111,7 +112,7 @@ class SupervisorTest(test.TestCase): sv = supervisor.Supervisor(logdir=logdir) with sv.managed_session("") as sess: for _ in xrange(10): - sess.run(my_op) + self.evaluate(my_op) # Supervisor has been stopped. self.assertTrue(sv.should_stop()) @@ -128,7 +129,7 @@ class SupervisorTest(test.TestCase): if step == 1: raise RuntimeError("failing here") else: - sess.run(my_op) + self.evaluate(my_op) # Supervisor has been stopped. self.assertTrue(sv.should_stop()) self.assertEqual(1, last_step) @@ -146,7 +147,7 @@ class SupervisorTest(test.TestCase): raise errors_impl.OutOfRangeError(my_op.op.node_def, my_op.op, "all done") else: - sess.run(my_op) + self.evaluate(my_op) # Supervisor has been stopped. OutOfRangeError was not thrown. self.assertTrue(sv.should_stop()) self.assertEqual(3, last_step) @@ -335,7 +336,7 @@ class SupervisorTest(test.TestCase): sess = sv.prepare_or_wait_for_session( "", config=config_pb2.ConfigProto(device_count={"CPU": 2})) for _ in xrange(10): - sess.run(my_op) + self.evaluate(my_op) sess.close() sv.stop() @@ -420,6 +421,7 @@ class SupervisorTest(test.TestCase): with self.assertRaisesRegexp(RuntimeError, "requires a summary writer"): sv.summary_computed(sess, sess.run(summ)) + @test_util.run_deprecated_v1 def testLogdirButExplicitlyNoSummaryWriter(self): logdir = self._test_dir("explicit_no_summary_writer") with ops.Graph().as_default(): @@ -505,6 +507,7 @@ class SupervisorTest(test.TestCase): sv = supervisor.Supervisor(logdir="", session_manager=sm) sv.prepare_or_wait_for_session("") + @test_util.run_deprecated_v1 def testInitOp(self): logdir = self._test_dir("default_init_op") with ops.Graph().as_default(): @@ -514,6 +517,7 @@ class SupervisorTest(test.TestCase): self.assertAllClose([1.0, 2.0, 3.0], sess.run(v)) sv.stop() + @test_util.run_deprecated_v1 def testInitFn(self): logdir = self._test_dir("default_init_op") with ops.Graph().as_default(): @@ -527,6 +531,7 @@ class SupervisorTest(test.TestCase): self.assertAllClose([1.0, 2.0, 3.0], sess.run(v)) sv.stop() + @test_util.run_deprecated_v1 def testInitOpWithFeedDict(self): logdir = self._test_dir("feed_dict_init_op") with ops.Graph().as_default(): @@ -540,6 +545,7 @@ class SupervisorTest(test.TestCase): self.assertAllClose([1.0, 2.0, 3.0], sess.run(v)) sv.stop() + @test_util.run_deprecated_v1 def testReadyForLocalInitOp(self): server = server_lib.Server.create_local_server() logdir = self._test_dir("default_ready_for_local_init_op") @@ -582,6 +588,7 @@ class SupervisorTest(test.TestCase): sv0.stop() sv1.stop() + @test_util.run_deprecated_v1 def testReadyForLocalInitOpRestoreFromCheckpoint(self): server = server_lib.Server.create_local_server() logdir = self._test_dir("ready_for_local_init_op_restore") @@ -713,6 +720,7 @@ class SupervisorTest(test.TestCase): "Variables not initialized: w"): sv.prepare_or_wait_for_session(server.target) + @test_util.run_deprecated_v1 def testSetupFail(self): logdir = self._test_dir("setup_fail") with ops.Graph().as_default(): @@ -723,6 +731,7 @@ class SupervisorTest(test.TestCase): variables.VariableV1([1.0, 2.0, 3.0], name="v") supervisor.Supervisor(logdir=logdir, is_chief=False) + @test_util.run_deprecated_v1 def testDefaultGlobalStep(self): logdir = self._test_dir("default_global_step") with ops.Graph().as_default(): @@ -732,6 +741,7 @@ class SupervisorTest(test.TestCase): self.assertEquals(287, sess.run(sv.global_step)) sv.stop() + @test_util.run_deprecated_v1 def testRestoreFromMetaGraph(self): logdir = self._test_dir("restore_from_meta_graph") with ops.Graph().as_default(): @@ -753,6 +763,7 @@ class SupervisorTest(test.TestCase): # This test is based on the fact that the standard services start # right away and get to run once before sv.stop() returns. # We still sleep a bit to make the test robust. + @test_util.run_deprecated_v1 def testStandardServicesWithoutGlobalStep(self): logdir = self._test_dir("standard_services_without_global_step") # Create a checkpoint. @@ -799,10 +810,11 @@ class SupervisorTest(test.TestCase): v = variables.VariableV1([10.10], name="foo") sav = saver_lib.Saver([v]) sav.restore(sess, save_path) - self.assertEqual(1.0, v.eval()[0]) + self.assertEqual(1.0, self.evaluate(v)[0]) # Same as testStandardServicesNoGlobalStep but with a global step. # We should get a summary about the step time. + @test_util.run_deprecated_v1 def testStandardServicesWithGlobalStep(self): logdir = self._test_dir("standard_services_with_global_step") # Create a checkpoint. @@ -863,7 +875,7 @@ class SupervisorTest(test.TestCase): v = variables.VariableV1([-12], name="global_step") sav = saver_lib.Saver([v]) sav.restore(sess, save_path) - self.assertEqual(123, v.eval()[0]) + self.assertEqual(123, self.evaluate(v)[0]) def testNoQueueRunners(self): with ops.Graph().as_default(), self.cached_session() as sess: diff --git a/tensorflow/python/training/sync_replicas_optimizer.py b/tensorflow/python/training/sync_replicas_optimizer.py index fbde8fe3c2..172c141150 100644 --- a/tensorflow/python/training/sync_replicas_optimizer.py +++ b/tensorflow/python/training/sync_replicas_optimizer.py @@ -44,6 +44,9 @@ from tensorflow.python.util.tf_export import tf_export class SyncReplicasOptimizer(optimizer.Optimizer): """Class to synchronize, aggregate gradients and pass them to the optimizer. + This class is deprecated. For synchrononous training, please use [Distribution + Strategies](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/distribute). + In a typical asynchronous training environment, it's common to have some stale gradients. For example, with a N-replica asynchronous training, gradients will be applied to the variables N times independently. Depending @@ -142,9 +145,9 @@ class SyncReplicasOptimizer(optimizer.Optimizer): @deprecation.deprecated( None, - "The `SyncReplicaOptimizer` is deprecated. For synchrononous training, " - "please use [Distribution Strategies](https://github.com/tensorflow/" - "tensorflow/tree/master/tensorflow/contrib/distribute).", + "The `SyncReplicaOptimizer` class is deprecated. For synchrononous " + "training, please use [Distribution Strategies](https://github.com/" + "tensorflow/tensorflow/tree/master/tensorflow/contrib/distribute).", warn_once=True) def __init__(self, opt, diff --git a/tensorflow/python/training/training_ops_test.py b/tensorflow/python/training/training_ops_test.py index 0216482825..51f49ca081 100644 --- a/tensorflow/python/training/training_ops_test.py +++ b/tensorflow/python/training/training_ops_test.py @@ -24,6 +24,7 @@ import numpy as np from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.framework.test_util import TensorFlowTestCase # Import resource_variable_ops for the variables-to-tensor implicit conversion. from tensorflow.python.ops import resource_variable_ops # pylint: disable=unused-import @@ -53,12 +54,13 @@ class TrainingOpsTest(TensorFlowTestCase): with self.session(use_gpu=use_gpu): var = variables.VariableV1(x) variables.global_variables_initializer().run() - self.assertAllCloseAccordingToType(x, var.eval()) + self.assertAllCloseAccordingToType(x, self.evaluate(var)) apply_sgd = training_ops.apply_gradient_descent(var, alpha, delta) - out = apply_sgd.eval() + out = self.evaluate(apply_sgd) self.assertShapeEqual(out, apply_sgd) self.assertAllCloseAccordingToType(x - alpha * delta, out) + @test_util.run_deprecated_v1 def testApplyGradientDescent(self): for (dtype, use_gpu) in itertools.product( [np.float16, np.float32, np.float64], [False, True]): @@ -74,13 +76,13 @@ class TrainingOpsTest(TensorFlowTestCase): accum = variables.VariableV1(y) variables.global_variables_initializer().run() - self.assertAllCloseAccordingToType(x, var.eval()) + self.assertAllCloseAccordingToType(x, self.evaluate(var)) apply_adagrad = training_ops.apply_adagrad(var, accum, lr, grad) - out = apply_adagrad.eval() + out = self.evaluate(apply_adagrad) self.assertShapeEqual(out, apply_adagrad) self.assertAllCloseAccordingToType(x - lr * grad * (y + grad * grad)** (-0.5), out) - self.assertAllCloseAccordingToType(y + grad * grad, accum.eval()) + self.assertAllCloseAccordingToType(y + grad * grad, self.evaluate(accum)) def _testTypesForFtrl(self, x, @@ -99,10 +101,10 @@ class TrainingOpsTest(TensorFlowTestCase): linear = variables.VariableV1(z) variables.global_variables_initializer().run() - self.assertAllCloseAccordingToType(x, var.eval()) + self.assertAllCloseAccordingToType(x, self.evaluate(var)) apply_ftrl = training_ops.apply_ftrl(var, accum, linear, grad, lr, l1, l2, lr_power) - out = apply_ftrl.eval() + out = self.evaluate(apply_ftrl) self.assertShapeEqual(out, apply_ftrl) accum_update = y + grad * grad linear_update = z + grad - (accum_update**(-lr_power) - y** @@ -112,19 +114,22 @@ class TrainingOpsTest(TensorFlowTestCase): np.sign(linear_update[i]) * l1 - linear_update[i]) / (quadratic[i]) if np.abs(linear_update[i]) > l1 else 0.0 for i in range(linear_update.size)]) - self.assertAllCloseAccordingToType(accum_update, accum.eval()) + self.assertAllCloseAccordingToType(accum_update, self.evaluate(accum)) if x.dtype == np.float16: # The calculations here really are not very precise in float16. - self.assertAllClose(linear_update, linear.eval(), rtol=2e-2, atol=2e-2) + self.assertAllClose( + linear_update, self.evaluate(linear), rtol=2e-2, atol=2e-2) self.assertAllClose(expected_out, out, rtol=2e-2, atol=2e-2) elif x.dtype == np.float32: # The calculations here not sufficiently precise in float32. - self.assertAllClose(linear_update, linear.eval(), rtol=1e-5, atol=1e-5) + self.assertAllClose( + linear_update, self.evaluate(linear), rtol=1e-5, atol=1e-5) self.assertAllClose(expected_out, out, rtol=1e-5, atol=1e-5) else: - self.assertAllClose(linear_update, linear.eval()) + self.assertAllClose(linear_update, self.evaluate(linear)) self.assertAllClose(expected_out, out) + @test_util.run_deprecated_v1 def testApplyAdagrad(self): for (dtype, use_gpu) in itertools.product( [np.float16, np.float32, np.float64], [False, True]): @@ -134,6 +139,7 @@ class TrainingOpsTest(TensorFlowTestCase): grad = np.arange(100).astype(dtype) self._testTypesForAdagrad(x, y, lr, grad, use_gpu) + @test_util.run_deprecated_v1 def testApplyFtrl(self): for dtype in [np.float16, np.float32, np.float64]: x = np.arange(100).astype(dtype) @@ -152,19 +158,19 @@ class TrainingOpsTest(TensorFlowTestCase): accum = variables.VariableV1(y) variables.global_variables_initializer().run() - self.assertAllCloseAccordingToType(x, var.eval()) + self.assertAllCloseAccordingToType(x, self.evaluate(var)) sparse_apply_adagrad = training_ops.sparse_apply_adagrad( var, accum, lr, grad, constant_op.constant(indices, self._toType(indices.dtype))) - out = sparse_apply_adagrad.eval() + out = self.evaluate(sparse_apply_adagrad) self.assertShapeEqual(out, sparse_apply_adagrad) for (i, index) in enumerate(indices): self.assertAllCloseAccordingToType( x[index] - lr * grad[i] * (y[index] + grad[i] * grad[i])**(-0.5), - var.eval()[index]) + self.evaluate(var)[index]) self.assertAllCloseAccordingToType(y[index] + grad[i] * grad[i], - accum.eval()[index]) + self.evaluate(accum)[index]) def _testTypesForSparseFtrl(self, x, @@ -183,7 +189,7 @@ class TrainingOpsTest(TensorFlowTestCase): linear = variables.VariableV1(z) variables.global_variables_initializer().run() - self.assertAllCloseAccordingToType(x, var.eval()) + self.assertAllCloseAccordingToType(x, self.evaluate(var)) sparse_apply_ftrl = training_ops.sparse_apply_ftrl( var, accum, @@ -194,16 +200,18 @@ class TrainingOpsTest(TensorFlowTestCase): l1, l2, lr_power=lr_power) - out = sparse_apply_ftrl.eval() + out = self.evaluate(sparse_apply_ftrl) self.assertShapeEqual(out, sparse_apply_ftrl) for (i, index) in enumerate(indices): - self.assertAllCloseAccordingToType(x[index] - lr * grad[i] * - (y[index] + grad[i] * grad[i])** - (lr_power), var.eval()[index]) + self.assertAllCloseAccordingToType( + x[index] - lr * grad[i] * (y[index] + grad[i] * grad[i])** + (lr_power), + self.evaluate(var)[index]) self.assertAllCloseAccordingToType(y[index] + grad[i] * grad[i], - accum.eval()[index]) + self.evaluate(accum)[index]) + @test_util.run_deprecated_v1 def testSparseApplyAdagrad(self): for (dtype, index_type) in itertools.product( [np.float16, np.float32, np.float64], [np.int32, np.int64]): @@ -217,6 +225,7 @@ class TrainingOpsTest(TensorFlowTestCase): indices = np.array([0, 2]).astype(index_type) self._testTypesForSparseAdagrad(x, y, lr, grad, indices) + @test_util.run_deprecated_v1 def testSparseApplyAdagradDim1(self): for (dtype, index_type) in itertools.product( [np.float16, np.float32, np.float64], [np.int32, np.int64]): @@ -230,6 +239,7 @@ class TrainingOpsTest(TensorFlowTestCase): indices = np.array([0, 2]).astype(index_type) self._testTypesForSparseAdagrad(x, y, lr, grad, indices) + @test_util.run_deprecated_v1 def testSparseApplyFtrlDim1(self): for (dtype, index_type) in itertools.product( [np.float16, np.float32, np.float64], [np.int32, np.int64]): @@ -245,6 +255,7 @@ class TrainingOpsTest(TensorFlowTestCase): indices = np.array([0, 2]).astype(index_type) self._testTypesForSparseFtrl(x, y, z, lr, grad, indices) + @test_util.run_deprecated_v1 def testApplyAdam(self): for dtype, use_gpu in itertools.product( [np.float16, np.float32, np.float64], [False, True]): @@ -276,13 +287,13 @@ class TrainingOpsTest(TensorFlowTestCase): epsilon_t = constant_op.constant(epsilon, self._toType(var.dtype), []) variables.global_variables_initializer().run() - self.assertAllCloseAccordingToType(var, var_t.eval()) + self.assertAllCloseAccordingToType(var, self.evaluate(var_t)) new_var, _, _ = self._adamUpdateNumpy(var, grad, t, m, v, lr, beta1, beta2, epsilon) apply_adam = training_ops.apply_adam(var_t, m_t, v_t, beta1_power_t, beta2_power_t, lr_t, beta1_t, beta2_t, epsilon_t, grad) - out = apply_adam.eval() + out = self.evaluate(apply_adam) self.assertShapeEqual(out, apply_adam) self.assertAllCloseAccordingToType(new_var, out) diff --git a/tensorflow/python/training/training_util_test.py b/tensorflow/python/training/training_util_test.py index ba64e785ac..3317008fce 100644 --- a/tensorflow/python/training/training_util_test.py +++ b/tensorflow/python/training/training_util_test.py @@ -20,6 +20,7 @@ from __future__ import print_function from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.ops import variables from tensorflow.python.platform import test from tensorflow.python.training import monitored_session @@ -46,6 +47,7 @@ class GlobalStepTest(test.TestCase): self.assertRaisesRegexp(TypeError, 'does not have integer type', training_util.get_global_step, g) + @test_util.run_deprecated_v1 def test_invalid_shape(self): with ops.Graph().as_default() as g: self.assertIsNone(training_util.get_global_step()) @@ -70,6 +72,7 @@ class GlobalStepTest(test.TestCase): training_util.create_global_step, g) self._assert_global_step(training_util.create_global_step(ops.Graph())) + @test_util.run_deprecated_v1 def test_get_global_step(self): with ops.Graph().as_default() as g: self.assertIsNone(training_util.get_global_step()) diff --git a/tensorflow/python/training/warm_starting_util.py b/tensorflow/python/training/warm_starting_util.py index 78dbb465b5..8c97f101da 100644 --- a/tensorflow/python/training/warm_starting_util.py +++ b/tensorflow/python/training/warm_starting_util.py @@ -32,7 +32,7 @@ from tensorflow.python.training import saver from tensorflow.python.util.tf_export import tf_export -@tf_export("train.VocabInfo") +@tf_export(v1=["train.VocabInfo"]) class VocabInfo( collections.namedtuple("VocabInfo", [ "new_vocab", @@ -248,7 +248,7 @@ def _warm_start_var_with_vocab(var, prev_tensor_name = _infer_var_name(var) # TODO(eddz): Fix functionality for rank-1 Variables (like FC biases). - total_v_first_axis = sum([v.get_shape().as_list()[0] for v in var]) + total_v_first_axis = sum(v.get_shape().as_list()[0] for v in var) for v in var: v_shape = v.get_shape().as_list() slice_info = v._get_save_slice_info() @@ -333,12 +333,12 @@ def _get_grouped_variables(vars_to_warm_start): ops.GraphKeys.TRAINABLE_VARIABLES, scope=vars_to_warm_start) elif isinstance(vars_to_warm_start, list): - if all([isinstance(v, str) for v in vars_to_warm_start]): + if all(isinstance(v, str) for v in vars_to_warm_start): list_of_vars = [] for v in vars_to_warm_start: list_of_vars += ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES, scope=v) - elif all([checkpoint_utils._is_variable(v) for v in vars_to_warm_start]): # pylint: disable=protected-access + elif all(checkpoint_utils._is_variable(v) for v in vars_to_warm_start): # pylint: disable=protected-access list_of_vars = vars_to_warm_start else: raise ValueError("If `vars_to_warm_start` is a list, it must be all " @@ -360,7 +360,7 @@ def _get_grouped_variables(vars_to_warm_start): return grouped_variables -@tf_export("train.warm_start") +@tf_export(v1=["train.warm_start"]) def warm_start(ckpt_to_initialize_from, vars_to_warm_start=".*", var_name_to_vocab_info=None, diff --git a/tensorflow/python/training/warm_starting_util_test.py b/tensorflow/python/training/warm_starting_util_test.py index 91a0b53b3a..fa1f370f41 100644 --- a/tensorflow/python/training/warm_starting_util_test.py +++ b/tensorflow/python/training/warm_starting_util_test.py @@ -22,7 +22,7 @@ import os import numpy as np import six -from tensorflow.python.feature_column import feature_column as fc +from tensorflow.python.feature_column import feature_column_lib as fc from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.ops import array_ops @@ -49,7 +49,7 @@ class WarmStartingUtilTest(test.TestCase): return vocab_file def _write_checkpoint(self, sess): - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) saver = saver_lib.Saver() ckpt_prefix = os.path.join(self.get_temp_dir(), "model") saver.save(sess, ckpt_prefix, global_step=0) @@ -70,7 +70,7 @@ class WarmStartingUtilTest(test.TestCase): if partitioner: self.assertTrue(isinstance(var, variables.PartitionedVariable)) var = var._get_variable_list() - return var, sess.run(var) + return var, self.evaluate(var) def _create_prev_run_vars(self, var_names, @@ -86,7 +86,7 @@ class WarmStartingUtilTest(test.TestCase): shape=shape, initializer=initializer)) self._write_checkpoint(sess) - return [sess.run(var) for var in all_vars] + return [self.evaluate(var) for var in all_vars] def _create_dummy_inputs(self): return { @@ -125,7 +125,7 @@ class WarmStartingUtilTest(test.TestCase): prev_tensor_name, var = ws_util._get_var_info(fruit_weights) checkpoint_utils.init_from_checkpoint(self.get_temp_dir(), {prev_tensor_name: var}) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) self.assertAllClose(prev_val, fruit_weights.eval(sess)) def testWarmStartVarPrevVarPartitioned(self): @@ -143,7 +143,7 @@ class WarmStartingUtilTest(test.TestCase): prev_tensor_name, var = ws_util._get_var_info(fruit_weights) checkpoint_utils.init_from_checkpoint(self.get_temp_dir(), {prev_tensor_name: var}) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) self.assertAllClose(prev_val, fruit_weights.eval(sess)) def testWarmStartVarCurrentVarPartitioned(self): @@ -162,7 +162,7 @@ class WarmStartingUtilTest(test.TestCase): prev_tensor_name, var = ws_util._get_var_info(fruit_weights) checkpoint_utils.init_from_checkpoint(self.get_temp_dir(), {prev_tensor_name: var}) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) fruit_weights = fruit_weights._get_variable_list() new_val = np.concatenate( [fruit_weights[0].eval(sess), fruit_weights[1].eval(sess)], axis=0) @@ -189,7 +189,7 @@ class WarmStartingUtilTest(test.TestCase): fruit_weights, prev_tensor_name="old_scope/fruit_weights") checkpoint_utils.init_from_checkpoint(self.get_temp_dir(), {prev_tensor_name: var}) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) fruit_weights = fruit_weights._get_variable_list() new_val = np.concatenate( [fruit_weights[0].eval(sess), fruit_weights[1].eval(sess)], axis=0) @@ -211,7 +211,7 @@ class WarmStartingUtilTest(test.TestCase): "fruit_weights", initializer=[[0.], [0.], [0.], [0.], [0.]]) ws_util._warm_start_var_with_vocab(fruit_weights, new_vocab_path, 5, self.get_temp_dir(), prev_vocab_path) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) self.assertAllClose([[2.], [1.5], [1.], [0.5], [0.]], fruit_weights.eval(sess)) @@ -236,7 +236,7 @@ class WarmStartingUtilTest(test.TestCase): prev_ckpt=self.get_temp_dir(), prev_vocab_path=prev_vocab_path, axis=1) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) self.assertAllClose([[0.3, 0.5, 0.], [0.8, 1.0, 0.], [1.2, 1.5, 0.], [2.3, 2., 0.]], fruit_output_layer.eval(sess)) @@ -261,7 +261,7 @@ class WarmStartingUtilTest(test.TestCase): self.get_temp_dir(), prev_vocab_path, previous_vocab_size=2) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) # Old vocabulary limited to ['apple', 'banana']. self.assertAllClose([[0.], [0.], [1.], [0.5], [0.]], fruit_weights.eval(sess)) @@ -285,7 +285,7 @@ class WarmStartingUtilTest(test.TestCase): "fruit_weights", initializer=[[0.], [0.], [0.], [0.], [0.]]) ws_util._warm_start_var_with_vocab(fruit_weights, new_vocab_path, 5, self.get_temp_dir(), prev_vocab_path) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) self.assertAllClose([[2.], [1.5], [1.], [0.5], [0.]], fruit_weights.eval(sess)) @@ -312,7 +312,7 @@ class WarmStartingUtilTest(test.TestCase): prev_ckpt=self.get_temp_dir(), prev_vocab_path=prev_vocab_path, axis=1) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) self.assertAllClose([[0.3, 0.5, 0.], [0.8, 1.0, 0.], [1.2, 1.5, 0.], [2.3, 2., 0.]], fruit_output_layer.eval(sess)) @@ -340,7 +340,7 @@ class WarmStartingUtilTest(test.TestCase): self.get_temp_dir(), prev_vocab_path, current_oov_buckets=1) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) self.assertTrue( isinstance(fruit_weights, variables.PartitionedVariable)) fruit_weights_vars = fruit_weights._get_variable_list() @@ -372,7 +372,7 @@ class WarmStartingUtilTest(test.TestCase): prev_ckpt=self.get_temp_dir(), prev_vocab_path=prev_vocab_path, axis=1) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) self.assertTrue( isinstance(fruit_output_layer, variables.PartitionedVariable)) fruit_output_layer_vars = fruit_output_layer._get_variable_list() @@ -404,7 +404,7 @@ class WarmStartingUtilTest(test.TestCase): partitioner=lambda shape, dtype: [2, 1]) ws_util._warm_start_var_with_vocab(fruit_weights, new_vocab_path, 6, self.get_temp_dir(), prev_vocab_path) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) self.assertTrue( isinstance(fruit_weights, variables.PartitionedVariable)) fruit_weights_vars = fruit_weights._get_variable_list() @@ -438,7 +438,7 @@ class WarmStartingUtilTest(test.TestCase): prev_ckpt=self.get_temp_dir(), prev_vocab_path=prev_vocab_path, axis=1) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) self.assertTrue( isinstance(fruit_output_layer, variables.PartitionedVariable)) fruit_output_layer_vars = fruit_output_layer._get_variable_list() @@ -463,7 +463,7 @@ class WarmStartingUtilTest(test.TestCase): shape=[10, 1], initializer=zeros()) ws_util.warm_start(self.get_temp_dir(), vars_to_warm_start=[var]) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) # Verify weights were correctly warm-started (init overridden to ones). self.assertAllEqual(var.eval(), prev_int_val) @@ -483,7 +483,7 @@ class WarmStartingUtilTest(test.TestCase): shape=[10, 1], initializer=zeros()) ws_util.warm_start(self.get_temp_dir(), vars_to_warm_start=["v1"]) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) # Verify weights were correctly warm-started (init overridden to ones). self.assertAllEqual(var.eval(), prev_int_val) @@ -519,7 +519,7 @@ class WarmStartingUtilTest(test.TestCase): # This warm-starts both v1 and v1/Momentum, but only # v2 (and not v2/Momentum). vars_to_warm_start=["v1", "v2[^/]"]) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) # Verify the selection of weights were correctly warm-started (init # overridden to ones). self.assertAllEqual(v1.eval(), prev_v1_val) @@ -542,7 +542,7 @@ class WarmStartingUtilTest(test.TestCase): with ops.Graph().as_default() as g: with self.session(graph=g) as sess: cols_to_vars = self._create_linear_model([sc_int], partitioner) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) # Without warm-starting, the weights should be initialized using default # initializer (which is init_ops.zeros_initializer). self._assert_cols_to_vars(cols_to_vars, {sc_int: [np.zeros([10, 1])]}, @@ -553,7 +553,7 @@ class WarmStartingUtilTest(test.TestCase): with self.session(graph=g) as sess: cols_to_vars = self._create_linear_model([sc_int], partitioner) ws_util.warm_start(self.get_temp_dir(), vars_to_warm_start=".*sc_int.*") - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) # Verify weights were correctly warm-started. self._assert_cols_to_vars(cols_to_vars, {sc_int: [prev_int_val]}, sess) @@ -571,7 +571,7 @@ class WarmStartingUtilTest(test.TestCase): with ops.Graph().as_default() as g: with self.session(graph=g) as sess: cols_to_vars = self._create_linear_model([sc_hash], partitioner) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) # Without warm-starting, the weights should be initialized using default # initializer (which is init_ops.zeros_initializer). self._assert_cols_to_vars(cols_to_vars, {sc_hash: [np.zeros([15, 1])]}, @@ -583,7 +583,7 @@ class WarmStartingUtilTest(test.TestCase): cols_to_vars = self._create_linear_model([sc_hash], partitioner) ws_util.warm_start( self.get_temp_dir(), vars_to_warm_start=".*sc_hash.*") - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) # Verify weights were correctly warm-started. self._assert_cols_to_vars(cols_to_vars, {sc_hash: [prev_hash_val]}, sess) @@ -605,7 +605,7 @@ class WarmStartingUtilTest(test.TestCase): with ops.Graph().as_default() as g: with self.session(graph=g) as sess: cols_to_vars = self._create_linear_model([sc_vocab], partitioner) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) # Without warm-starting, the weights should be initialized using default # initializer (which is init_ops.zeros_initializer). self._assert_cols_to_vars(cols_to_vars, {sc_vocab: [np.zeros([4, 1])]}, @@ -619,7 +619,7 @@ class WarmStartingUtilTest(test.TestCase): # vocab is assumed to be same as new vocab. ws_util.warm_start( self.get_temp_dir(), vars_to_warm_start=".*sc_vocab.*") - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) # Verify weights were correctly warm-started. self._assert_cols_to_vars(cols_to_vars, {sc_vocab: [prev_vocab_val]}, sess) @@ -641,7 +641,7 @@ class WarmStartingUtilTest(test.TestCase): with ops.Graph().as_default() as g: with self.session(graph=g) as sess: cols_to_vars = self._create_linear_model([sc_vocab], partitioner) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) # Without warm-starting, the weights should be initialized using default # initializer (which is init_ops.zeros_initializer). self._assert_cols_to_vars(cols_to_vars, {sc_vocab: [np.zeros([4, 1])]}, @@ -657,7 +657,7 @@ class WarmStartingUtilTest(test.TestCase): # Explicitly provide the file prefix instead of just the dir. os.path.join(self.get_temp_dir(), "model-0"), vars_to_warm_start=".*sc_vocab.*") - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) # Verify weights were correctly warm-started. self._assert_cols_to_vars(cols_to_vars, {sc_vocab: [prev_vocab_val]}, sess) @@ -686,7 +686,7 @@ class WarmStartingUtilTest(test.TestCase): with ops.Graph().as_default() as g: with self.session(graph=g) as sess: cols_to_vars = self._create_linear_model([sc_vocab], partitioner) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) # Without warm-starting, the weights should be initialized using default # initializer (which is init_ops.zeros_initializer). self._assert_cols_to_vars(cols_to_vars, {sc_vocab: [np.zeros([2, 1])]}, @@ -708,7 +708,7 @@ class WarmStartingUtilTest(test.TestCase): var_name_to_vocab_info={ "linear_model/sc_vocab/weights": vocab_info }) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) # Verify weights were correctly warm-started. 'banana' isn't in the # first two entries of the old vocabulary, so it's newly initialized. self._assert_cols_to_vars(cols_to_vars, {sc_vocab: [[[1], [0]]]}, sess) @@ -729,7 +729,7 @@ class WarmStartingUtilTest(test.TestCase): with ops.Graph().as_default() as g: with self.session(graph=g) as sess: cols_to_vars = self._create_linear_model([real_bucket], partitioner) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) # Without warm-starting, the weights should be initialized using default # initializer (which is init_ops.zeros_initializer). self._assert_cols_to_vars(cols_to_vars, @@ -741,7 +741,7 @@ class WarmStartingUtilTest(test.TestCase): cols_to_vars = self._create_linear_model([real_bucket], partitioner) ws_util.warm_start( self.get_temp_dir(), vars_to_warm_start=".*real_bucketized.*") - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) # Verify weights were correctly warm-started. self._assert_cols_to_vars(cols_to_vars, {real_bucket: [prev_bucket_val]}, sess) @@ -800,7 +800,7 @@ class WarmStartingUtilTest(test.TestCase): with ops.Graph().as_default() as g: with self.session(graph=g) as sess: cols_to_vars = self._create_linear_model(all_linear_cols, partitioner) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) # Without warm-starting, all weights should be initialized using default # initializer (which is init_ops.zeros_initializer). self._assert_cols_to_vars(cols_to_vars, { @@ -826,7 +826,7 @@ class WarmStartingUtilTest(test.TestCase): var_name_to_vocab_info={ "linear_model/sc_vocab/weights": vocab_info }) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) # Verify weights were correctly warm-started. self._assert_cols_to_vars(cols_to_vars, { sc_int: [prev_int_val], @@ -865,7 +865,7 @@ class WarmStartingUtilTest(test.TestCase): "linear_model/sc_vocab/weights", initializer=[[0.5], [1.], [2.], [3.]]) self._write_checkpoint(sess) - prev_keys_val = sess.run(sc_keys_weights) + prev_keys_val = self.evaluate(sc_keys_weights) def _partitioner(shape, dtype): # pylint:disable=unused-argument # Partition each var into 2 equal slices. @@ -892,7 +892,7 @@ class WarmStartingUtilTest(test.TestCase): ws_util._infer_var_name(cols_to_vars[sc_keys]): "some_other_name" }) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) # Verify weights were correctly warm-started. Var corresponding to # sc_hash should not be warm-started. Var corresponding to sc_vocab # should be correctly warm-started after vocab remapping. @@ -933,7 +933,7 @@ class WarmStartingUtilTest(test.TestCase): "linear_model/sc_vocab/weights", initializer=[[0.5], [1.], [2.], [3.]]) self._write_checkpoint(sess) - prev_keys_val = sess.run(sc_keys_weights) + prev_keys_val = self.evaluate(sc_keys_weights) # New graph, new session with warm-starting. with ops.Graph().as_default() as g: @@ -955,7 +955,7 @@ class WarmStartingUtilTest(test.TestCase): ws_util._infer_var_name(cols_to_vars[sc_keys]): "some_other_name" }) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) # Verify weights were correctly warm-started. Var corresponding to # sc_hash should not be warm-started. Var corresponding to sc_vocab # should be correctly warm-started after vocab remapping. @@ -1024,7 +1024,7 @@ class WarmStartingUtilTest(test.TestCase): ws_util._infer_var_name(cols_to_vars[sc_keys]): "some_other_name" }) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) # Verify weights were correctly warm-started. Var corresponding to # sc_vocab should be correctly warm-started after vocab remapping, # and neither of the other two should be warm-started.. @@ -1091,7 +1091,7 @@ class WarmStartingUtilTest(test.TestCase): ws_util._infer_var_name(cols_to_vars[emb_vocab_column]): vocab_info }) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) # Verify weights were correctly warm-started. Var corresponding to # emb_vocab_column should be correctly warm-started after vocab # remapping. Missing values are filled in with the EmbeddingColumn's @@ -1163,7 +1163,7 @@ class WarmStartingUtilTest(test.TestCase): var_name_to_vocab_info={ "linear_model/sc_vocab_embedding/embedding_weights": vocab_info }) - sess.run(variables.global_variables_initializer()) + self.evaluate(variables.global_variables_initializer()) # Verify weights were correctly warm-started. Var corresponding to # emb_vocab should be correctly warm-started after vocab remapping. # Missing values are filled in with the EmbeddingColumn's initializer. diff --git a/tensorflow/python/util/deprecation.py b/tensorflow/python/util/deprecation.py index 4c68d1aaae..9aaf0c2de9 100644 --- a/tensorflow/python/util/deprecation.py +++ b/tensorflow/python/util/deprecation.py @@ -28,6 +28,7 @@ from tensorflow.python.util import is_in_graph_mode from tensorflow.python.util import tf_contextlib from tensorflow.python.util import tf_decorator from tensorflow.python.util import tf_inspect +from tensorflow.python.util import tf_stack # Allow deprecation warnings to be silenced temporarily with a context manager. @@ -98,21 +99,9 @@ def _validate_deprecation_args(date, instructions): def _call_location(outer=False): """Returns call location given level up from current call.""" - frame = tf_inspect.currentframe() - if frame: - # CPython internals are available, use them for performance. - # walk back two frames to get to deprecated function caller. - frame = frame.f_back - if frame.f_back: - frame = frame.f_back - if outer and frame.f_back: - frame = frame.f_back - return '%s:%d' % (frame.f_code.co_filename, frame.f_lineno) - else: - # Slow fallback path - stack = tf_inspect.stack(0) # 0 avoids generating unused context - entry = stack[3 if outer else 2] - return '%s:%d' % (entry[1], entry[2]) + stack = tf_stack.extract_stack() + frame = stack[-4 if outer else -3] + return '{filename}:{lineno}'.format(filename=frame[0], lineno=frame[1]) def _wrap_decorator(wrapped_function): diff --git a/tensorflow/python/util/deprecation_test.py b/tensorflow/python/util/deprecation_test.py index 34cbca52a1..035c416d79 100644 --- a/tensorflow/python/util/deprecation_test.py +++ b/tensorflow/python/util/deprecation_test.py @@ -19,6 +19,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +from tensorflow.python.framework import test_util from tensorflow.python.platform import test from tensorflow.python.platform import tf_logging as logging from tensorflow.python.util import deprecation @@ -174,6 +175,7 @@ class DeprecationTest(test.TestCase): set(args[1:])) @test.mock.patch.object(logging, "warning", autospec=True) + @test_util.run_deprecated_v1 def test_static_fn_with_doc(self, mock_warning): date = "2016-07-04" instructions = "This is how you update..." @@ -214,6 +216,7 @@ class DeprecationTest(test.TestCase): self._assert_subset(set(["after " + date, instructions]), set(args[1:])) @test.mock.patch.object(logging, "warning", autospec=True) + @test_util.run_deprecated_v1 def test_static_fn_with_one_line_doc(self, mock_warning): date = "2016-07-04" instructions = "This is how you update..." @@ -239,6 +242,7 @@ class DeprecationTest(test.TestCase): self._assert_subset(set(["after " + date, instructions]), set(args[1:])) @test.mock.patch.object(logging, "warning", autospec=True) + @test_util.run_deprecated_v1 def test_static_fn_no_doc(self, mock_warning): date = "2016-07-04" instructions = "This is how you update..." @@ -488,6 +492,7 @@ class DeprecatedArgsTest(test.TestCase): deprecation.deprecated_args(date, instructions, "missing")(_fn) @test.mock.patch.object(logging, "warning", autospec=True) + @test_util.run_deprecated_v1 def test_static_fn_with_doc(self, mock_warning): date = "2016-07-04" instructions = "This is how you update..." @@ -535,6 +540,7 @@ class DeprecatedArgsTest(test.TestCase): self._assert_subset(set(["after " + date, instructions]), set(args[1:])) @test.mock.patch.object(logging, "warning", autospec=True) + @test_util.run_deprecated_v1 def test_static_fn_with_one_line_doc(self, mock_warning): date = "2016-07-04" instructions = "This is how you update..." @@ -565,6 +571,7 @@ class DeprecatedArgsTest(test.TestCase): self._assert_subset(set(["after " + date, instructions]), set(args[1:])) @test.mock.patch.object(logging, "warning", autospec=True) + @test_util.run_deprecated_v1 def test_static_fn_no_doc(self, mock_warning): date = "2016-07-04" instructions = "This is how you update..." @@ -595,6 +602,7 @@ class DeprecatedArgsTest(test.TestCase): self._assert_subset(set(["after " + date, instructions]), set(args[1:])) @test.mock.patch.object(logging, "warning", autospec=True) + @test_util.run_deprecated_v1 def test_varargs(self, mock_warning): date = "2016-07-04" instructions = "This is how you update..." @@ -615,6 +623,7 @@ class DeprecatedArgsTest(test.TestCase): self._assert_subset(set(["after " + date, instructions]), set(args[1:])) @test.mock.patch.object(logging, "warning", autospec=True) + @test_util.run_deprecated_v1 def test_kwargs(self, mock_warning): date = "2016-07-04" instructions = "This is how you update..." @@ -635,6 +644,7 @@ class DeprecatedArgsTest(test.TestCase): self._assert_subset(set(["after " + date, instructions]), set(args[1:])) @test.mock.patch.object(logging, "warning", autospec=True) + @test_util.run_deprecated_v1 def test_positional_and_named(self, mock_warning): date = "2016-07-04" instructions = "This is how you update..." @@ -660,6 +670,7 @@ class DeprecatedArgsTest(test.TestCase): set(args2[1:])) @test.mock.patch.object(logging, "warning", autospec=True) + @test_util.run_deprecated_v1 def test_positional_and_named_with_ok_vals(self, mock_warning): date = "2016-07-04" instructions = "This is how you update..." @@ -692,6 +703,7 @@ class DeprecatedArgsTest(test.TestCase): self.assertEqual(0, mock_warning.call_count) @test.mock.patch.object(logging, "warning", autospec=True) + @test_util.run_deprecated_v1 def test_deprecated_args_once(self, mock_warning): date = "2016-07-04" instructions = "This is how you update..." @@ -708,6 +720,7 @@ class DeprecatedArgsTest(test.TestCase): self.assertEqual(1, mock_warning.call_count) @test.mock.patch.object(logging, "warning", autospec=True) + @test_util.run_deprecated_v1 def test_deprecated_multiple_args_once_each(self, mock_warning): date = "2016-07-04" instructions = "This is how you update..." @@ -752,6 +765,7 @@ class DeprecatedArgValuesTest(test.TestCase): deprecation.deprecated_arg_values(date, instructions) @test.mock.patch.object(logging, "warning", autospec=True) + @test_util.run_deprecated_v1 def test_static_fn_with_doc(self, mock_warning): date = "2016-07-04" instructions = "This is how you update..." @@ -804,6 +818,7 @@ class DeprecatedArgValuesTest(test.TestCase): self.assertEqual(2, mock_warning.call_count) @test.mock.patch.object(logging, "warning", autospec=True) + @test_util.run_deprecated_v1 def test_static_fn_with_one_line_doc(self, mock_warning): date = "2016-07-04" instructions = "This is how you update..." @@ -839,6 +854,7 @@ class DeprecatedArgValuesTest(test.TestCase): self.assertEqual(2, mock_warning.call_count) @test.mock.patch.object(logging, "warning", autospec=True) + @test_util.run_deprecated_v1 def test_static_fn_no_doc(self, mock_warning): date = "2016-07-04" instructions = "This is how you update..." diff --git a/tensorflow/python/util/dispatch.py b/tensorflow/python/util/dispatch.py new file mode 100644 index 0000000000..e7a56b5922 --- /dev/null +++ b/tensorflow/python/util/dispatch.py @@ -0,0 +1,192 @@ +# Copyright 2018 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. +# ============================================================================== +"""Type-based dispatch for TensorFlow ops. + +"Operation dispatchers" can be used to override the behavior for TensorFlow ops +when they are called with otherwise unsupported argument types. In particular, +when an operation is called with arguments that would cause it to raise a +TypeError, it falls back on its registered operation dispatchers. If any +registered dispatchers can handle the arguments, then its result is returned. +Otherwise, the original TypeError is raised. + +By default, dispatch support is added to the generated op wrappers for any +visible ops by default. Ops that are implemented in Python can opt in to +dispatch support using the `add_dispatch_support` decorator. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import itertools + +from tensorflow.python.util import tf_decorator +from tensorflow.python.util import tf_inspect + +# Private function attribute used to store a list of dispatchers. +DISPATCH_ATTR = "_tf_dispatchers" + + +class OpDispatcher(object): + """Abstract base class for TensorFlow operator dispatchers. + + Each operation dispatcher acts as an override handler for a single + TensorFlow operation, and its results are used when the handler indicates + that it can handle the operation's arguments (by returning any value other + than `OpDispatcher.NOT_SUPPORTED`). + """ + + # Sentinel value that can be returned to indicate that an operation + # dispatcher does not support a given set of arguments. + NOT_SUPPORTED = object() + + def handle(self, args, kwargs): # pylint: disable=unused-argument + """Handle this dispatcher's operation with the specified arguments. + + If this operation dispatcher can handle the given arguments, then + return an appropriate value (or raise an appropriate exception). + + Args: + args: The arguments to the operation. + kwargs: They keyword arguments to the operation. + + Returns: + The result of the operation, or `OpDispatcher.NOT_SUPPORTED` if this + dispatcher can not handle the given arguments. + """ + return self.NOT_SUPPORTED + + def register(self, op): + """Register this dispatcher as a handler for `op`. + + Args: + op: Python function: the TensorFlow operation that should be handled. Must + have a dispatch list (which is added automatically for generated ops, + and can be added to Python ops using the `add_dispatch_support` + decorator). + """ + if not hasattr(op, DISPATCH_ATTR): + raise AssertionError("Dispatching not enabled for %s" % op) + getattr(op, DISPATCH_ATTR).append(self) + + +def dispatch(op, *args, **kwargs): + """Returns the result from the first successful dispatcher for a given op. + + Calls the `handle` method of each `OpDispatcher` that has been registered + to handle `op`, and returns the value from the first successful handler. + + Args: + op: Python function: the operation to dispatch for. + *args: The arguments to the operation. + **kwargs: They keyword arguments to the operation. + + Returns: + The result of the operation, or `NOT_SUPPORTED` if no registered + dispatcher can handle the given arguments. + """ + for dispatcher in getattr(op, DISPATCH_ATTR): + result = dispatcher.handle(args, kwargs) + if result is not OpDispatcher.NOT_SUPPORTED: + return result + return OpDispatcher.NOT_SUPPORTED + + +class _TypeBasedDispatcher(OpDispatcher): + """Dispatcher that handles op if any arguments have a specified type. + + Checks the types of the arguments and keyword arguments (including elements + of lists or tuples), and if any argument values have the indicated type(s), + then delegates to an override function. + """ + + def __init__(self, override_func, types): + self._types = types + self._override_func = override_func + + def _handles(self, args, kwargs): + for arg in itertools.chain(args, kwargs.values()): + if (isinstance(arg, self._types) or + (isinstance(arg, (list, tuple)) and + any(isinstance(elt, self._types) for elt in arg))): + return True + return False + + def handle(self, args, kwargs): + if self._handles(args, kwargs): + return self._override_func(*args, **kwargs) + else: + return self.NOT_SUPPORTED + + +# pylint: disable=g-doc-return-or-yield +def dispatch_for_types(op, *types): + """Decorator to declare that a Python function overrides an op for a type. + + The decorated function is used to override `op` if any of the arguments or + keyword arguments (including elements of lists or tuples) have one of the + specified types. + + Example: + + ```python + @dispatch_for_types(math_ops.add, RaggedTensor, RaggedTensorValue) + def ragged_add(x, y, name=None): ... + ``` + + Args: + op: Python function: the operation that should be overridden. + *types: The argument types for which this function should be used. + """ + + def decorator(func): + if tf_inspect.getargspec(func) != tf_inspect.getargspec(op): + raise AssertionError("The decorated function's signature must exactly " + "match the signature of the overridden op.") + _TypeBasedDispatcher(func, types).register(op) + return func + + return decorator + + +# pylint: enable=g-doc-return-or-yield + + +def add_dispatch_list(target): + """Decorator that adds a dispatch_list attribute to an op.""" + assert not hasattr(target, DISPATCH_ATTR) + setattr(target, DISPATCH_ATTR, []) + return target + + +def add_dispatch_support(target): + """Decorator that adds a dispatch handling wrapper to an op.""" + add_dispatch_list(target) + + def wrapper(*args, **kwargs): + """Call target, and fall back on dispatchers if there is a TypeError.""" + try: + return target(*args, **kwargs) + except (TypeError, ValueError): + # Note: convert_to_eager_tensor currently raises a ValueError, not a + # TypeError, when given unexpected types. So we need to catch both. + result = dispatch(wrapper, *args, **kwargs) + if result is not OpDispatcher.NOT_SUPPORTED: + return result + else: + raise + + setattr(wrapper, DISPATCH_ATTR, []) + return tf_decorator.make_decorator(target, wrapper) diff --git a/tensorflow/python/util/dispatch_test.py b/tensorflow/python/util/dispatch_test.py new file mode 100644 index 0000000000..b7c5c8eca8 --- /dev/null +++ b/tensorflow/python/util/dispatch_test.py @@ -0,0 +1,120 @@ +# Copyright 2018 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 operator dispatch.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util +from tensorflow.python.ops import gen_math_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.platform import googletest +from tensorflow.python.util import dispatch +from tensorflow.python.util.tf_export import tf_export + + +class CustomTensor(object): + """A fake composite tensor class, for testing type-based dispatching.""" + + def __init__(self, tensor, score): + self.tensor = ops.convert_to_tensor(tensor) + self.score = score + + +@tf_export("test_op") +@dispatch.add_dispatch_support +def test_op(x, y, z): + """A fake op for testing dispatch of Python ops.""" + return x + (2 * y) + (3 * z) + + +@test_util.run_all_in_graph_and_eager_modes +class DispatchTest(test_util.TensorFlowTestCase): + + def testAddDispatchForTypes_With_CppOp(self): + original_handlers = gen_math_ops.add._tf_dispatchers[:] + + # Override the behavior of gen_math_ops.add. + @dispatch.dispatch_for_types(gen_math_ops.add, CustomTensor) + def custom_add(x, y, name=None): # pylint: disable=unused-variable + return CustomTensor(gen_math_ops.add(x.tensor, y.tensor, name), + (x.score+y.score) / 2.0) + self.assertEqual(len(math_ops.add._tf_dispatchers), + len(original_handlers) + 1) + + # Test that we see the overridden behavior when using CustomTensors. + x = CustomTensor([1, 2, 3], 2.0) + y = CustomTensor([7, 8, 2], 0.0) + x_plus_y = gen_math_ops.add(x, y) + self.assertAllEqual(self.evaluate(x_plus_y.tensor), [8, 10, 5]) + self.assertNear(x_plus_y.score, 1.0, 0.001) + + # Test that we still get the right behavior when using normal Tensors. + a = [1, 2, 3] + b = [4, 5, 6] + a_plus_b = gen_math_ops.add(a, b) + self.assertAllEqual(a_plus_b, [5, 7, 9]) + + # Test that we still get a TypeError or ValueError if we pass some + # type that's not supported by any dispatcher. + with self.assertRaises((TypeError, ValueError)): + gen_math_ops.add(a, None) + + # Clean up + gen_math_ops.add._tf_dispatchers = original_handlers + + def testAddDispatchForTypes_With_PythonOp(self): + original_handlers = test_op._tf_dispatchers[:] + + @dispatch.dispatch_for_types(test_op, CustomTensor) + def override_for_test_op(x, y, z): # pylint: disable=unused-variable + return CustomTensor(test_op(x.tensor, y.tensor, z.tensor), + (x.score + y.score + z.score) / 3.0) + + x = CustomTensor([1, 2, 3], 0.2) + y = CustomTensor([7, 8, 2], 0.4) + z = CustomTensor([0, 1, 2], 0.6) + + result = test_op(x, y, z) + self.assertAllEqual(self.evaluate(result.tensor), [15, 21, 13]) + self.assertNear(result.score, 0.4, 0.001) + + # Clean up + test_op._tf_dispatchers = original_handlers + + def testDispatchForTypes_SignatureMismatch(self): + with self.assertRaisesRegexp(AssertionError, "The decorated function's " + "signature must exactly match.*"): + @dispatch.dispatch_for_types(test_op, CustomTensor) + def override_for_test_op(a, b, c): # pylint: disable=unused-variable + return CustomTensor(test_op(a.tensor, b.tensor, c.tensor), + (a.score + b.score + c.score) / 3.0) + + def testDispatchForTypes_OpDoesNotSupportDispatch(self): + def some_op(x, y): + return x + y + + with self.assertRaisesRegexp(AssertionError, "Dispatching not enabled for"): + @dispatch.dispatch_for_types(some_op, CustomTensor) + def override_for_some_op(x, y): # pylint: disable=unused-variable + return x if x.score > 0 else y + + +if __name__ == "__main__": + googletest.main() + + diff --git a/tensorflow/python/util/nest_test.py b/tensorflow/python/util/nest_test.py index 997a3c5c36..d0d0c5f793 100644 --- a/tensorflow/python/util/nest_test.py +++ b/tensorflow/python/util/nest_test.py @@ -482,6 +482,7 @@ class NestTest(parameterized.TestCase, test.TestCase): self.assertEqual(nt.a[1][::-1], rev_nt.a[1]) self.assertEqual(nt.b[::-1], rev_nt.b) + @test_util.run_deprecated_v1 def testMapStructureOverPlaceholders(self): inp_a = (array_ops.placeholder(dtypes.float32, shape=[3, 4]), array_ops.placeholder(dtypes.float32, shape=[3, 7])) diff --git a/tensorflow/python/util/py_checkpoint_reader.i b/tensorflow/python/util/py_checkpoint_reader.i index 1c73f7f06f..a1b98a2a75 100644 --- a/tensorflow/python/util/py_checkpoint_reader.i +++ b/tensorflow/python/util/py_checkpoint_reader.i @@ -165,7 +165,6 @@ def NewCheckpointReader(filepattern): from tensorflow.python.util import compat return CheckpointReader(compat.as_bytes(filepattern), status) -NewCheckpointReader._tf_api_names = ['train.NewCheckpointReader'] NewCheckpointReader._tf_api_names_v1 = ['train.NewCheckpointReader'] %} diff --git a/tensorflow/python/util/tf_export.py b/tensorflow/python/util/tf_export.py index 0924b36ade..ec70cae7d2 100644 --- a/tensorflow/python/util/tf_export.py +++ b/tensorflow/python/util/tf_export.py @@ -50,6 +50,10 @@ from tensorflow.python.util import tf_decorator ESTIMATOR_API_NAME = 'estimator' TENSORFLOW_API_NAME = 'tensorflow' +# List of subpackage names used by TensorFlow components. Have to check that +# TensorFlow core repo does not export any symbols under these names. +SUBPACKAGE_NAMESPACES = [ESTIMATOR_API_NAME] + _Attributes = collections.namedtuple( 'ExportedApiAttributes', ['names', 'constants']) @@ -78,6 +82,11 @@ class SymbolAlreadyExposedError(Exception): pass +class InvalidSymbolNameError(Exception): + """Raised when trying to export symbol as an invalid or unallowed name.""" + pass + + def get_canonical_name_for_symbol( symbol, api_name=TENSORFLOW_API_NAME, add_prefix_to_v1_names=False): @@ -163,6 +172,37 @@ class api_export(object): # pylint: disable=invalid-name self._overrides = kwargs.get('overrides', []) self._allow_multiple_exports = kwargs.get('allow_multiple_exports', False) + self._validate_symbol_names() + + def _validate_symbol_names(self): + """Validate you are exporting symbols under an allowed package. + + We need to ensure things exported by tf_export, estimator_export, etc. + export symbols under disjoint top-level package names. + + For TensorFlow, we check that it does not export anything under subpackage + names used by components (estimator, keras, etc.). + + For each component, we check that it exports everything under its own + subpackage. + + Raises: + InvalidSymbolNameError: If you try to export symbol under disallowed name. + """ + all_symbol_names = set(self._names) | set(self._names_v1) + if self._api_name == TENSORFLOW_API_NAME: + for subpackage in SUBPACKAGE_NAMESPACES: + if any(n.startswith(subpackage) for n in all_symbol_names): + raise InvalidSymbolNameError( + '@tf_export is not allowed to export symbols under %s.*' % ( + subpackage)) + else: + if not all(n.startswith(self._api_name) for n in all_symbol_names): + raise InvalidSymbolNameError( + 'Can only export symbols under package name of component. ' + 'e.g. tensorflow_estimator must export all symbols under ' + 'tf.estimator') + def __call__(self, func): """Calls this decorator. diff --git a/tensorflow/python/util/tf_export_test.py b/tensorflow/python/util/tf_export_test.py index 4ae1dc55e0..a0fac8bf36 100644 --- a/tensorflow/python/util/tf_export_test.py +++ b/tensorflow/python/util/tf_export_test.py @@ -130,6 +130,26 @@ class ValidateExportTest(test.TestCase): with self.assertRaises(tf_export.SymbolAlreadyExposedError): export_decorator(_test_function) + def testRaisesExceptionIfInvalidSymbolName(self): + # TensorFlow code is not allowed to export symbols under package + # tf.estimator + with self.assertRaises(tf_export.InvalidSymbolNameError): + tf_export.tf_export('estimator.invalid') + + # All symbols exported by Estimator must be under tf.estimator package. + with self.assertRaises(tf_export.InvalidSymbolNameError): + tf_export.estimator_export('invalid') + with self.assertRaises(tf_export.InvalidSymbolNameError): + tf_export.estimator_export('Estimator.invalid') + with self.assertRaises(tf_export.InvalidSymbolNameError): + tf_export.estimator_export('invalid.estimator') + + def testRaisesExceptionIfInvalidV1SymbolName(self): + with self.assertRaises(tf_export.InvalidSymbolNameError): + tf_export.tf_export('valid', v1=['estimator.invalid']) + with self.assertRaises(tf_export.InvalidSymbolNameError): + tf_export.estimator_export('estimator.valid', v1=['invalid']) + def testOverridesFunction(self): _test_function2._tf_api_names = ['abc'] diff --git a/tensorflow/python/util/tf_should_use_test.py b/tensorflow/python/util/tf_should_use_test.py index fedbe1dff6..65d848cf2a 100644 --- a/tensorflow/python/util/tf_should_use_test.py +++ b/tensorflow/python/util/tf_should_use_test.py @@ -24,6 +24,7 @@ import gc import sys from tensorflow.python.framework import constant_op +from tensorflow.python.framework import test_util from tensorflow.python.platform import test from tensorflow.python.platform import tf_logging from tensorflow.python.util import tf_should_use @@ -39,6 +40,7 @@ def reroute_error(): class TfShouldUseTest(test.TestCase): + @test_util.run_deprecated_v1 def testAddShouldUseWarningWhenNotUsed(self): c = constant_op.constant(0, name='blah0') def in_this_function(): @@ -52,6 +54,7 @@ class TfShouldUseTest(test.TestCase): self.assertIn('in_this_function', msg) self.assertFalse(gc.garbage) + @test_util.run_deprecated_v1 def testAddShouldUseFatalWhenNotUsed(self): c = constant_op.constant(0, name='blah0') def in_this_function(): @@ -74,6 +77,7 @@ class TfShouldUseTest(test.TestCase): error.assert_not_called() fatal.assert_not_called() + @test_util.run_deprecated_v1 def testAddShouldUseWarningWhenUsedWithAdd(self): def add(h): _ = h + 1 @@ -81,6 +85,7 @@ class TfShouldUseTest(test.TestCase): gc.collect() self.assertFalse(gc.garbage) + @test_util.run_deprecated_v1 def testAddShouldUseWarningWhenUsedWithGetName(self): def get_name(h): _ = h.name @@ -88,6 +93,7 @@ class TfShouldUseTest(test.TestCase): gc.collect() self.assertFalse(gc.garbage) + @test_util.run_deprecated_v1 def testShouldUseResult(self): @tf_should_use.should_use_result def return_const(value): @@ -101,6 +107,7 @@ class TfShouldUseTest(test.TestCase): gc.collect() self.assertFalse(gc.garbage) + @test_util.run_deprecated_v1 def testShouldUseResultWhenNotReallyUsed(self): @tf_should_use.should_use_result def return_const(value): @@ -111,7 +118,7 @@ class TfShouldUseTest(test.TestCase): # Creating another op and executing it does not mark the # unused op as being "used". v = constant_op.constant(1.0, name='meh') - v.eval() + self.evaluate(v) msg = '\n'.join(error.call_args[0]) self.assertIn('Object was never used', msg) self.assertIn('blah3:0', msg) diff --git a/tensorflow/stream_executor/BUILD b/tensorflow/stream_executor/BUILD index 5c9d85acf4..4c764a7b09 100644 --- a/tensorflow/stream_executor/BUILD +++ b/tensorflow/stream_executor/BUILD @@ -1,6 +1,8 @@ licenses(["restricted"]) load("@local_config_cuda//cuda:build_defs.bzl", "if_cuda_is_configured") +load("//tensorflow/core:platform/default/build_config.bzl", "tf_proto_library") +load("//tensorflow/core:platform/default/build_config.bzl", "tf_additional_all_protos") load("//tensorflow/core:platform/default/build_config_root.bzl", "if_static") load("//tensorflow:tensorflow.bzl", "cc_header_only_library") @@ -13,6 +15,14 @@ STREAM_EXECUTOR_HEADERS = glob([ "platform/**/*.h", ]) +tf_proto_library( + name = "dnn_proto", + srcs = ["dnn.proto"], + cc_api_version = 2, + default_header = True, + protodeps = tf_additional_all_protos(), +) + cc_library( name = "stream_executor_impl", srcs = glob( @@ -35,6 +45,7 @@ cc_library( }), visibility = ["//visibility:public"], deps = [ + ":dnn_proto_cc_impl", "//tensorflow/core:lib", "//tensorflow/core:ptr_util", "@com_google_absl//absl/container:flat_hash_map", @@ -51,6 +62,7 @@ cc_library( hdrs = STREAM_EXECUTOR_HEADERS, visibility = ["//visibility:public"], deps = [ + ":dnn_proto_cc", "//tensorflow/core:lib", "//tensorflow/core:ptr_util", "@com_google_absl//absl/strings", @@ -96,11 +108,8 @@ cc_library( "@local_config_cuda//cuda:cuda_headers", ] + if_cuda_is_configured([ "//tensorflow/core:cuda", - "@local_config_cuda//cuda:cublas", "@local_config_cuda//cuda:cuda_driver", "@local_config_cuda//cuda:cudnn", - "@local_config_cuda//cuda:cufft", - "@local_config_cuda//cuda:curand", ]), alwayslink = 1, ) diff --git a/tensorflow/stream_executor/cuda/cuda_blas.cc b/tensorflow/stream_executor/cuda/cuda_blas.cc index 7fabb35e28..957f6c98da 100644 --- a/tensorflow/stream_executor/cuda/cuda_blas.cc +++ b/tensorflow/stream_executor/cuda/cuda_blas.cc @@ -58,6 +58,11 @@ limitations under the License. #include "tensorflow/stream_executor/cuda/cuda_stream.h" #include "tensorflow/stream_executor/cuda/cuda_timer.h" #include "tensorflow/stream_executor/device_memory.h" + +#ifndef PLATFORM_GOOGLE +#include "tensorflow/stream_executor/dso_loader.h" +#endif + #include "tensorflow/stream_executor/lib/env.h" #include "tensorflow/stream_executor/lib/initialize.h" #include "tensorflow/stream_executor/lib/status.h" @@ -76,21 +81,8 @@ PLUGIN_REGISTRY_DEFINE_PLUGIN_ID(kCuBlasPlugin); namespace wrap { -#define STREAM_EXECUTOR_CUBLAS_WRAP(__name) \ - struct WrapperShim__##__name { \ - static const char *kName; \ - template \ - cublasStatus_t operator()(CUDAExecutor *parent, Args... args) { \ - cuda::ScopedActivateExecutorContext sac{parent}; \ - return ::__name(args...); \ - } \ - } __name; \ - const char *WrapperShim__##__name::kName = #__name; - -#define STREAM_EXECUTOR_CUBLAS_V2_WRAP(__name) \ - STREAM_EXECUTOR_CUBLAS_WRAP(__name) - -#define CUBLAS_BLAS_ROUTINE_EACH(__macro) \ +// clang-format off +#define CUBLAS_ROUTINE_EACH(__macro) \ __macro(cublasSnrm2) \ __macro(cublasDnrm2) \ __macro(cublasScnrm2) \ @@ -262,6 +254,58 @@ namespace wrap { __macro(cublasCdgmm) \ __macro(cublasZdgmm) +// clang-format off + +#ifdef PLATFORM_GOOGLE +#define STREAM_EXECUTOR_CUBLAS_WRAP(__name) \ + struct WrapperShim__##__name { \ + static const char *kName; \ + template \ + cublasStatus_t operator()(CUDAExecutor *parent, Args... args) { \ + cuda::ScopedActivateExecutorContext sac{parent}; \ + return ::__name(args...); \ + } \ + } __name; \ + const char *WrapperShim__##__name::kName = #__name; + +#define STREAM_EXECUTOR_CUBLAS_V2_WRAP(__name) \ + STREAM_EXECUTOR_CUBLAS_WRAP(__name) + +#else + +#define STREAM_EXECUTOR_CUBLAS_WRAP(__name) \ + struct DynLoadShim__##__name { \ + static const char* kName; \ + using FuncPtrT = std::add_pointer::type; \ + static void* GetDsoHandle() { \ + auto s = internal::CachedDsoLoader::GetCublasDsoHandle(); \ + return s.ValueOrDie(); \ + } \ + static FuncPtrT LoadOrDie() { \ + void* f; \ + auto s = port::Env::Default()->GetSymbolFromLibrary(GetDsoHandle(), \ + kName, &f); \ + CHECK(s.ok()) << "could not find " << kName \ + << " in cublas DSO; dlerror: " << s.error_message(); \ + return reinterpret_cast(f); \ + } \ + static FuncPtrT DynLoad() { \ + static FuncPtrT f = LoadOrDie(); \ + return f; \ + } \ + template \ + cublasStatus_t operator()(CUDAExecutor* parent, Args... args) { \ + cuda::ScopedActivateExecutorContext sac{parent}; \ + return DynLoad()(args...); \ + } \ + } __name; \ + const char* DynLoadShim__##__name::kName = #__name; + +#define STREAM_EXECUTOR_CUBLAS_V2_WRAP(__name) \ + STREAM_EXECUTOR_CUBLAS_WRAP(__name) + +#endif + STREAM_EXECUTOR_CUBLAS_V2_WRAP(cublasCreate) STREAM_EXECUTOR_CUBLAS_V2_WRAP(cublasDestroy) STREAM_EXECUTOR_CUBLAS_V2_WRAP(cublasSetStream) @@ -271,7 +315,7 @@ STREAM_EXECUTOR_CUBLAS_WRAP(cublasSgemmBatched) STREAM_EXECUTOR_CUBLAS_WRAP(cublasDgemmBatched) STREAM_EXECUTOR_CUBLAS_WRAP(cublasCgemmBatched) STREAM_EXECUTOR_CUBLAS_WRAP(cublasZgemmBatched) -CUBLAS_BLAS_ROUTINE_EACH(STREAM_EXECUTOR_CUBLAS_V2_WRAP) +CUBLAS_ROUTINE_EACH(STREAM_EXECUTOR_CUBLAS_V2_WRAP) #if CUDA_VERSION >= 7050 STREAM_EXECUTOR_CUBLAS_WRAP(cublasSgemmEx) @@ -424,7 +468,8 @@ class ScopedCublasMathMode { // Note that when false is returned, an appropriate error has already been // logged. bool Init(cublasMath_t new_mode) { - cublasStatus_t ret = wrap::cublasGetMathMode(parent_, handle_, &old_mode_); + cublasStatus_t ret = + wrap::cublasGetMathMode(parent_, handle_, &old_mode_); if (ret != CUBLAS_STATUS_SUCCESS) { LOG(ERROR) << "failed to get old cublas math mode: " << ToString(ret); return ok_ = false; @@ -442,7 +487,8 @@ class ScopedCublasMathMode { // successful in the first place. ~ScopedCublasMathMode() { if (ok_) { - cublasStatus_t ret = wrap::cublasSetMathMode(parent_, handle_, old_mode_); + cublasStatus_t ret = + wrap::cublasSetMathMode(parent_, handle_, old_mode_); if (ret != CUBLAS_STATUS_SUCCESS) { LOG(ERROR) << "failed to set former cublas math mode: " << ToString(ret); @@ -675,16 +721,16 @@ bool CUDABlas::DoBlasAsum(Stream *stream, uint64 elem_count, const DeviceMemory> &x, int incx, DeviceMemory *result) { return DoBlasInternal( - wrap::cublasScasum, stream, false /* = pointer_mode_host */, elem_count, - CUDAComplex(CUDAMemory(x)), incx, CUDAMemoryMutable(result)); + wrap::cublasScasum, stream, false /* = pointer_mode_host */, + elem_count, CUDAComplex(CUDAMemory(x)), incx, CUDAMemoryMutable(result)); } bool CUDABlas::DoBlasAsum(Stream *stream, uint64 elem_count, const DeviceMemory> &x, int incx, DeviceMemory *result) { return DoBlasInternal( - wrap::cublasDzasum, stream, false /* = pointer_mode_host */, elem_count, - CUDAComplex(CUDAMemory(x)), incx, CUDAMemoryMutable(result)); + wrap::cublasDzasum, stream, false /* = pointer_mode_host */, + elem_count, CUDAComplex(CUDAMemory(x)), incx, CUDAMemoryMutable(result)); } bool CUDABlas::DoBlasAxpy(Stream *stream, uint64 elem_count, float alpha, @@ -835,16 +881,16 @@ bool CUDABlas::DoBlasNrm2(Stream *stream, uint64 elem_count, const DeviceMemory> &x, int incx, DeviceMemory *result) { return DoBlasInternal( - wrap::cublasScnrm2, stream, false /* = pointer_mode_host */, elem_count, - CUDAComplex(CUDAMemory(x)), incx, CUDAMemoryMutable(result)); + wrap::cublasScnrm2, stream, false /* = pointer_mode_host */, + elem_count, CUDAComplex(CUDAMemory(x)), incx, CUDAMemoryMutable(result)); } bool CUDABlas::DoBlasNrm2(Stream *stream, uint64 elem_count, const DeviceMemory> &x, int incx, DeviceMemory *result) { return DoBlasInternal( - wrap::cublasDznrm2, stream, false /* = pointer_mode_host */, elem_count, - CUDAComplex(CUDAMemory(x)), incx, CUDAMemoryMutable(result)); + wrap::cublasDznrm2, stream, false /* = pointer_mode_host */, + elem_count, CUDAComplex(CUDAMemory(x)), incx, CUDAMemoryMutable(result)); } bool CUDABlas::DoBlasRot(Stream *stream, uint64 elem_count, @@ -1060,48 +1106,48 @@ bool CUDABlas::DoBlasIamax(Stream *stream, uint64 elem_count, const DeviceMemory> &x, int incx, DeviceMemory *result) { return DoBlasInternal( - wrap::cublasIcamax, stream, false /* = pointer_mode_host */, elem_count, - CUDAComplex(CUDAMemory(x)), incx, CUDAMemoryMutable(result)); + wrap::cublasIcamax, stream, false /* = pointer_mode_host */, + elem_count, CUDAComplex(CUDAMemory(x)), incx, CUDAMemoryMutable(result)); } bool CUDABlas::DoBlasIamax(Stream *stream, uint64 elem_count, const DeviceMemory> &x, int incx, DeviceMemory *result) { return DoBlasInternal( - wrap::cublasIzamax, stream, false /* = pointer_mode_host */, elem_count, - CUDAComplex(CUDAMemory(x)), incx, CUDAMemoryMutable(result)); + wrap::cublasIzamax, stream, false /* = pointer_mode_host */, + elem_count, CUDAComplex(CUDAMemory(x)), incx, CUDAMemoryMutable(result)); } bool CUDABlas::DoBlasIamin(Stream *stream, uint64 elem_count, const DeviceMemory &x, int incx, DeviceMemory *result) { return DoBlasInternal( - wrap::cublasIsamin, stream, false /* = pointer_mode_host */, elem_count, - CUDAComplex(CUDAMemory(x)), incx, CUDAMemoryMutable(result)); + wrap::cublasIsamin, stream, false /* = pointer_mode_host */, + elem_count, CUDAComplex(CUDAMemory(x)), incx, CUDAMemoryMutable(result)); } bool CUDABlas::DoBlasIamin(Stream *stream, uint64 elem_count, const DeviceMemory &x, int incx, DeviceMemory *result) { return DoBlasInternal( - wrap::cublasIdamin, stream, false /* = pointer_mode_host */, elem_count, - CUDAComplex(CUDAMemory(x)), incx, CUDAMemoryMutable(result)); + wrap::cublasIdamin, stream, false /* = pointer_mode_host */, + elem_count, CUDAComplex(CUDAMemory(x)), incx, CUDAMemoryMutable(result)); } bool CUDABlas::DoBlasIamin(Stream *stream, uint64 elem_count, const DeviceMemory> &x, int incx, DeviceMemory *result) { return DoBlasInternal( - wrap::cublasIcamin, stream, false /* = pointer_mode_host */, elem_count, - CUDAComplex(CUDAMemory(x)), incx, CUDAMemoryMutable(result)); + wrap::cublasIcamin, stream, false /* = pointer_mode_host */, + elem_count, CUDAComplex(CUDAMemory(x)), incx, CUDAMemoryMutable(result)); } bool CUDABlas::DoBlasIamin(Stream *stream, uint64 elem_count, const DeviceMemory> &x, int incx, DeviceMemory *result) { return DoBlasInternal( - wrap::cublasIzamin, stream, false /* = pointer_mode_host */, elem_count, - CUDAComplex(CUDAMemory(x)), incx, CUDAMemoryMutable(result)); + wrap::cublasIzamin, stream, false /* = pointer_mode_host */, + elem_count, CUDAComplex(CUDAMemory(x)), incx, CUDAMemoryMutable(result)); } bool CUDABlas::DoBlasGbmv(Stream *stream, blas::Transpose trans, uint64 m, diff --git a/tensorflow/stream_executor/cuda/cuda_dnn.cc b/tensorflow/stream_executor/cuda/cuda_dnn.cc index 19397c7dbf..1f2e2f48bb 100644 --- a/tensorflow/stream_executor/cuda/cuda_dnn.cc +++ b/tensorflow/stream_executor/cuda/cuda_dnn.cc @@ -132,43 +132,6 @@ string ToString(cudnnStatus_t status) { } } -template -cudnnDataType_t GetCudnnDataType( - dnn::DataLayout = dnn::DataLayout::kBatchDepthYX); - -template <> -cudnnDataType_t GetCudnnDataType(dnn::DataLayout) { - return CUDNN_DATA_DOUBLE; -} - -template <> -cudnnDataType_t GetCudnnDataType(dnn::DataLayout) { - return CUDNN_DATA_FLOAT; -} - -template <> -cudnnDataType_t GetCudnnDataType(dnn::DataLayout) { - return CUDNN_DATA_HALF; -} - -template <> -cudnnDataType_t GetCudnnDataType(dnn::DataLayout layout) { - switch (layout) { - case dnn::DataLayout::kYXDepthBatch: - case dnn::DataLayout::kYXBatchDepth: - case dnn::DataLayout::kBatchYXDepth: - case dnn::DataLayout::kBatchDepthYX: - return CUDNN_DATA_INT8; - case dnn::DataLayout::kBatchDepthYX4: - return CUDNN_DATA_INT8x4; - } -} - -template <> -cudnnDataType_t GetCudnnDataType(dnn::DataLayout) { - return CUDNN_DATA_INT32; -} - // RAII wrapper for all calls to cuDNN with a cuDNN handle argument. // // See CudnnAccess::GetHandle() for details. @@ -685,10 +648,10 @@ class CudnnConvolutionDescriptor { CHECK_CUDNN_OK(cudnnSetConvolutionNdDescriptor( handle_.get(), convolution_descriptor.ndims(), padding.data(), strides.data(), dilations.data(), - // NOTE(keveman): cuDNN supports convolution and cross correlation. - // However, almost all the use cases do cross correlation, so just - // hard coding it here. - CUDNN_CROSS_CORRELATION, data_type)); + convolution_descriptor.convolution_not_crosscorr() + ? CUDNN_CONVOLUTION + : CUDNN_CROSS_CORRELATION, + data_type)); // NOTE(benbarsdell): This only applies if tensor op math is enabled // and algo selection is set to Default. @@ -861,11 +824,19 @@ cudnnDataType_t ToCudnnDataType( case dnn::DataType::kInt8: return data_layout == dnn::DataLayout::kBatchDepthYX4 ? CUDNN_DATA_INT8x4 : CUDNN_DATA_INT8; + case dnn::DataType::kInt32: + return CUDNN_DATA_INT32; default: LOG(FATAL) << "Invalid DNN data type: " << static_cast(data_type); } } +template +cudnnDataType_t GetCudnnDataType( + dnn::DataLayout data_layout = dnn::DataLayout::kBatchDepthYX) { + return ToCudnnDataType(dnn::ToDataType::value, data_layout); +} + cudnnRNNInputMode_t ToCudnnRnnInputMode(dnn::RnnInputMode input_mode) { switch (input_mode) { case dnn::RnnInputMode::kRnnLinearSkip: @@ -2345,27 +2316,6 @@ struct ConvDoFP32ComputationFP16Input { static constexpr bool kDefaultFlag = true; }; -// A group of helper functions to return the internal compute type for -// convolutions in cudnn. -template -cudnnDataType_t GetConvComputeType() { - return CUDNN_DATA_FLOAT; -} - -template <> -cudnnDataType_t GetConvComputeType() { - if (CudnnEnvVar::IsEnabled()) { - return CUDNN_DATA_FLOAT; - } else { - return CUDNN_DATA_HALF; - } -} - -template <> -cudnnDataType_t GetConvComputeType() { - return CUDNN_DATA_DOUBLE; -} - // A helper struct to decide whether to use FP32 as the internal compute type // for rnn when the input data type is FP16. At present it is turned off, // users can explicitly control them through an env-var @@ -2437,7 +2387,7 @@ port::Status CudnnSupport::DoConvolveImpl( const DeviceMemory& filter_data, const dnn::ConvolutionDescriptor& convolution_descriptor, const dnn::BatchDescriptor& output_descriptor, DeviceMemory* output_data, - ScratchAllocator* scratch_allocator, + dnn::DataType accumulator_type, ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, dnn::ProfileResult* output_profile_result) { cudnnDataType_t cudnn_type = GetCudnnDataType(); @@ -2445,7 +2395,7 @@ port::Status CudnnSupport::DoConvolveImpl( CudnnTensorDescriptor output_nd(output_descriptor, cudnn_type); CudnnFilterDescriptor filter(filter_descriptor, cudnn_type); CudnnConvolutionDescriptor conv(convolution_descriptor, - GetConvComputeType()); + ToCudnnDataType(accumulator_type)); auto cudnn = cudnn_->GetHandle(parent_, stream); // Alpha is the scaling factor for input. @@ -2536,8 +2486,7 @@ port::Status CudnnSupport::DoConvolveImpl( return port::Status::OK(); } -template +template port::Status CudnnSupport::DoFusedConvolveImpl( Stream* stream, const dnn::BatchDescriptor& conv_input_descriptor, const DeviceMemory& conv_input_data, @@ -2548,7 +2497,8 @@ port::Status CudnnSupport::DoFusedConvolveImpl( ScaleType side_input_scale, const dnn::BatchDescriptor& bias_descriptor, const DeviceMemory& biases, dnn::ActivationMode activation_mode, const dnn::BatchDescriptor& output_descriptor, - DeviceMemory* output_data, ScratchAllocator* scratch_allocator, + DeviceMemory* output_data, dnn::DataType accumulator_type, + ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, dnn::ProfileResult* output_profile_result) { if (activation_mode != dnn::ActivationMode::kRelu && @@ -2569,7 +2519,7 @@ port::Status CudnnSupport::DoFusedConvolveImpl( GetCudnnDataType(conv_input_descriptor.layout())); CudnnTensorDescriptor bias_nd(bias_descriptor, GetCudnnDataType()); CudnnConvolutionDescriptor conv(convolution_descriptor, - GetCudnnDataType()); + ToCudnnDataType(accumulator_type)); auto cudnn = cudnn_->GetHandle(parent_, stream); @@ -2938,10 +2888,10 @@ bool CudnnSupport::DoConvolve( const dnn::AlgorithmConfig& algorithm_config, dnn::ProfileResult* output_profile_result) { return IsStatusOk( - DoConvolveImpl( - stream, batch_descriptor, input_data, filter_descriptor, filter_data, - convolution_descriptor, output_descriptor, output_data, - scratch_allocator, algorithm_config, output_profile_result), + DoConvolveImpl(stream, batch_descriptor, input_data, filter_descriptor, + filter_data, convolution_descriptor, output_descriptor, + output_data, dnn::DataType::kFloat, scratch_allocator, + algorithm_config, output_profile_result), /*report_error=*/!output_profile_result); } @@ -2956,10 +2906,10 @@ bool CudnnSupport::DoConvolve( const dnn::AlgorithmConfig& algorithm_config, dnn::ProfileResult* output_profile_result) { return IsStatusOk( - DoConvolveImpl( - stream, batch_descriptor, input_data, filter_descriptor, filter_data, - convolution_descriptor, output_descriptor, output_data, - scratch_allocator, algorithm_config, output_profile_result), + DoConvolveImpl(stream, batch_descriptor, input_data, filter_descriptor, + filter_data, convolution_descriptor, output_descriptor, + output_data, dnn::DataType::kDouble, scratch_allocator, + algorithm_config, output_profile_result), /*report_error=*/!output_profile_result); } @@ -2973,11 +2923,15 @@ bool CudnnSupport::DoConvolve( DeviceMemory* output_data, ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, dnn::ProfileResult* output_profile_result) { + dnn::DataType acc_type = + CudnnEnvVar::IsEnabled() + ? dnn::DataType::kFloat + : dnn::DataType::kHalf; return IsStatusOk( - DoConvolveImpl( - stream, batch_descriptor, input_data, filter_descriptor, filter_data, - convolution_descriptor, output_descriptor, output_data, - scratch_allocator, algorithm_config, output_profile_result), + DoConvolveImpl(stream, batch_descriptor, input_data, filter_descriptor, + filter_data, convolution_descriptor, output_descriptor, + output_data, acc_type, scratch_allocator, algorithm_config, + output_profile_result), /*report_error=*/!output_profile_result); } @@ -2995,12 +2949,13 @@ bool CudnnSupport::DoFusedConvolve( const dnn::AlgorithmConfig& algorithm_config, dnn::ProfileResult* output_profile_result) { return IsStatusOk( - DoFusedConvolveImpl( - stream, conv_input_descriptor, conv_input_data, conv_input_scale, - filter_descriptor, filter_data, convolution_descriptor, - side_input_data, side_input_scale, bias_descriptor, biases, - activation_mode, output_descriptor, output_data, scratch_allocator, - algorithm_config, output_profile_result), + DoFusedConvolveImpl(stream, conv_input_descriptor, conv_input_data, + conv_input_scale, filter_descriptor, filter_data, + convolution_descriptor, side_input_data, + side_input_scale, bias_descriptor, biases, + activation_mode, output_descriptor, output_data, + dnn::DataType::kDouble, scratch_allocator, + algorithm_config, output_profile_result), /*report_error=*/!output_profile_result); } @@ -3018,12 +2973,13 @@ bool CudnnSupport::DoFusedConvolve( const dnn::AlgorithmConfig& algorithm_config, dnn::ProfileResult* output_profile_result) { return IsStatusOk( - DoFusedConvolveImpl( - stream, conv_input_descriptor, conv_input_data, conv_input_scale, - filter_descriptor, filter_data, convolution_descriptor, - side_input_data, side_input_scale, bias_descriptor, biases, - activation_mode, output_descriptor, output_data, scratch_allocator, - algorithm_config, output_profile_result), + DoFusedConvolveImpl(stream, conv_input_descriptor, conv_input_data, + conv_input_scale, filter_descriptor, filter_data, + convolution_descriptor, side_input_data, + side_input_scale, bias_descriptor, biases, + activation_mode, output_descriptor, output_data, + dnn::DataType::kFloat, scratch_allocator, + algorithm_config, output_profile_result), /*report_error=*/!output_profile_result); } @@ -3041,13 +2997,17 @@ bool CudnnSupport::DoFusedConvolve( DeviceMemory* output_data, ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, dnn::ProfileResult* output_profile_result) { + dnn::DataType acc_type = + CudnnEnvVar::IsEnabled() + ? dnn::DataType::kFloat + : dnn::DataType::kHalf; return IsStatusOk( - DoFusedConvolveImpl( + DoFusedConvolveImpl( stream, conv_input_descriptor, conv_input_data, conv_input_scale, filter_descriptor, filter_data, convolution_descriptor, side_input_data, side_input_scale, bias_descriptor, biases, - activation_mode, output_descriptor, output_data, scratch_allocator, - algorithm_config, output_profile_result), + activation_mode, output_descriptor, output_data, acc_type, + scratch_allocator, algorithm_config, output_profile_result), /*report_error=*/!output_profile_result); } @@ -3073,12 +3033,13 @@ bool CudnnSupport::DoFusedConvolve( return false; } return IsStatusOk( - DoFusedConvolveImpl( - stream, conv_input_descriptor, conv_input_data, conv_input_scale, - filter_descriptor, filter_data, convolution_descriptor, - side_input_data, side_input_scale, bias_descriptor, biases, - activation_mode, output_descriptor, output_data, scratch_allocator, - algorithm_config, output_profile_result), + DoFusedConvolveImpl(stream, conv_input_descriptor, conv_input_data, + conv_input_scale, filter_descriptor, filter_data, + convolution_descriptor, side_input_data, + side_input_scale, bias_descriptor, biases, + activation_mode, output_descriptor, output_data, + dnn::DataType::kInt32, scratch_allocator, + algorithm_config, output_profile_result), /*report_error=*/!output_profile_result); } @@ -3112,7 +3073,8 @@ port::Status CudnnSupport::DoConvolveBackwardDataImpl( DeviceMemory backward_output_data, const dnn::ConvolutionDescriptor& convolution_descriptor, const dnn::BatchDescriptor& input_descriptor, - DeviceMemory* backward_input_data, ScratchAllocator* scratch_allocator, + DeviceMemory* backward_input_data, dnn::DataType accumulator_type, + ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, dnn::ProfileResult* output_profile_result) { cudnnDataType_t cudnn_type = GetCudnnDataType(); @@ -3133,7 +3095,7 @@ port::Status CudnnSupport::DoConvolveBackwardDataImpl( CudnnTensorDescriptor in_back_nd(input_descriptor, cudnn_type); CudnnFilterDescriptor filter(filter_descriptor, cudnn_type); CudnnConvolutionDescriptor conv(convolution_descriptor, - GetConvComputeType()); + ToCudnnDataType(accumulator_type)); const bool is_profiling = output_profile_result != nullptr; @@ -3213,11 +3175,11 @@ bool CudnnSupport::DoConvolveBackwardData( const dnn::AlgorithmConfig& algorithm_config, dnn::ProfileResult* output_profile_result) { return IsStatusOk( - DoConvolveBackwardDataImpl(stream, filter_descriptor, filter_data, - output_descriptor, backward_output_data, - convolution_descriptor, input_descriptor, - backward_input_data, scratch_allocator, - algorithm_config, output_profile_result), + DoConvolveBackwardDataImpl( + stream, filter_descriptor, filter_data, output_descriptor, + backward_output_data, convolution_descriptor, input_descriptor, + backward_input_data, dnn::DataType::kDouble, scratch_allocator, + algorithm_config, output_profile_result), /*report_error=*/!output_profile_result); } @@ -3233,11 +3195,11 @@ bool CudnnSupport::DoConvolveBackwardData( const dnn::AlgorithmConfig& algorithm_config, dnn::ProfileResult* output_profile_result) { return IsStatusOk( - DoConvolveBackwardDataImpl(stream, filter_descriptor, filter_data, - output_descriptor, backward_output_data, - convolution_descriptor, input_descriptor, - backward_input_data, scratch_allocator, - algorithm_config, output_profile_result), + DoConvolveBackwardDataImpl( + stream, filter_descriptor, filter_data, output_descriptor, + backward_output_data, convolution_descriptor, input_descriptor, + backward_input_data, dnn::DataType::kFloat, scratch_allocator, + algorithm_config, output_profile_result), /*report_error=*/!output_profile_result); } @@ -3252,12 +3214,16 @@ bool CudnnSupport::DoConvolveBackwardData( ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, dnn::ProfileResult* output_profile_result) { + dnn::DataType acc_type = + CudnnEnvVar::IsEnabled() + ? dnn::DataType::kFloat + : dnn::DataType::kHalf; return IsStatusOk( - DoConvolveBackwardDataImpl(stream, filter_descriptor, filter_data, - output_descriptor, backward_output_data, - convolution_descriptor, input_descriptor, - backward_input_data, scratch_allocator, - algorithm_config, output_profile_result), + DoConvolveBackwardDataImpl( + stream, filter_descriptor, filter_data, output_descriptor, + backward_output_data, convolution_descriptor, input_descriptor, + backward_input_data, acc_type, scratch_allocator, algorithm_config, + output_profile_result), /*report_error=*/!output_profile_result); } @@ -3269,7 +3235,8 @@ port::Status CudnnSupport::DoConvolveBackwardFilterImpl( DeviceMemory backward_output_data, const dnn::ConvolutionDescriptor& convolution_descriptor, const dnn::FilterDescriptor& filter_descriptor, - DeviceMemory* backward_filter_data, ScratchAllocator* scratch_allocator, + DeviceMemory* backward_filter_data, dnn::DataType accumulator_type, + ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, dnn::ProfileResult* output_profile_result) { cudnnDataType_t cudnn_type = GetCudnnDataType(); @@ -3290,7 +3257,7 @@ port::Status CudnnSupport::DoConvolveBackwardFilterImpl( CudnnTensorDescriptor input_nd(input_descriptor, cudnn_type); CudnnFilterDescriptor filter(filter_descriptor, cudnn_type); CudnnConvolutionDescriptor conv(convolution_descriptor, - GetConvComputeType()); + ToCudnnDataType(accumulator_type)); const bool is_profiling = output_profile_result != nullptr; @@ -3406,11 +3373,12 @@ bool CudnnSupport::DoConvolveBackwardFilter( const dnn::AlgorithmConfig& algorithm_config, dnn::ProfileResult* output_profile_result) { return IsStatusOk( - DoConvolveBackwardFilterImpl(stream, input_descriptor, input_data, - output_descriptor, backward_output_data, - convolution_descriptor, filter_descriptor, - backward_filter_data, scratch_allocator, - algorithm_config, output_profile_result), + DoConvolveBackwardFilterImpl( + stream, input_descriptor, input_data, output_descriptor, + backward_output_data, convolution_descriptor, filter_descriptor, + backward_filter_data, dnn::DataType::kDouble, + + scratch_allocator, algorithm_config, output_profile_result), /*report_error=*/!output_profile_result); } @@ -3425,13 +3393,14 @@ bool CudnnSupport::DoConvolveBackwardFilter( ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, dnn::ProfileResult* output_profile_result) { - return IsStatusOk( - DoConvolveBackwardFilterImpl(stream, input_descriptor, input_data, - output_descriptor, backward_output_data, - convolution_descriptor, filter_descriptor, - backward_filter_data, scratch_allocator, - algorithm_config, output_profile_result), - /*report_error=*/!output_profile_result); + return IsStatusOk(DoConvolveBackwardFilterImpl( + stream, input_descriptor, input_data, output_descriptor, + backward_output_data, convolution_descriptor, + filter_descriptor, backward_filter_data, + + dnn::DataType::kFloat, scratch_allocator, + algorithm_config, output_profile_result), + /*report_error=*/!output_profile_result); } bool CudnnSupport::DoConvolveBackwardFilter( @@ -3445,12 +3414,16 @@ bool CudnnSupport::DoConvolveBackwardFilter( ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, dnn::ProfileResult* output_profile_result) { + dnn::DataType acc_type = + CudnnEnvVar::IsEnabled() + ? dnn::DataType::kFloat + : dnn::DataType::kHalf; return IsStatusOk( - DoConvolveBackwardFilterImpl(stream, input_descriptor, input_data, - output_descriptor, backward_output_data, - convolution_descriptor, filter_descriptor, - backward_filter_data, scratch_allocator, - algorithm_config, output_profile_result), + DoConvolveBackwardFilterImpl( + stream, input_descriptor, input_data, output_descriptor, + backward_output_data, convolution_descriptor, filter_descriptor, + backward_filter_data, acc_type, scratch_allocator, algorithm_config, + output_profile_result), /*report_error=*/!output_profile_result); } diff --git a/tensorflow/stream_executor/cuda/cuda_dnn.h b/tensorflow/stream_executor/cuda/cuda_dnn.h index 74f6f935b8..0641be140d 100644 --- a/tensorflow/stream_executor/cuda/cuda_dnn.h +++ b/tensorflow/stream_executor/cuda/cuda_dnn.h @@ -670,12 +670,12 @@ class CudnnSupport : public dnn::DnnSupport { const DeviceMemory& filter_data, const dnn::ConvolutionDescriptor& convolution_descriptor, const dnn::BatchDescriptor& output_descriptor, - DeviceMemory* output_data, ScratchAllocator* scratch_allocator, + DeviceMemory* output_data, dnn::DataType accumulator_type, + ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, dnn::ProfileResult* output_profile_result); - template + template port::Status DoFusedConvolveImpl( Stream* stream, const dnn::BatchDescriptor& conv_input_descriptor, const DeviceMemory& conv_input_data, @@ -687,7 +687,7 @@ class CudnnSupport : public dnn::DnnSupport { ScaleType side_input_scale, const dnn::BatchDescriptor& bias_descriptor, const DeviceMemory& biases, dnn::ActivationMode activation_mode, const dnn::BatchDescriptor& output_descriptor, - DeviceMemory* output_data, + DeviceMemory* output_data, dnn::DataType accumulator_type, ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, dnn::ProfileResult* output_profile_result); @@ -700,7 +700,8 @@ class CudnnSupport : public dnn::DnnSupport { DeviceMemory backward_output_data, const dnn::ConvolutionDescriptor& convolution_descriptor, const dnn::BatchDescriptor& input_descriptor, - DeviceMemory* backward_input_data, ScratchAllocator* scratch_allocator, + DeviceMemory* backward_input_data, dnn::DataType accumulator_type, + ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, dnn::ProfileResult* output_profile_result); @@ -712,7 +713,7 @@ class CudnnSupport : public dnn::DnnSupport { DeviceMemory backward_output_data, const dnn::ConvolutionDescriptor& convolution_descriptor, const dnn::FilterDescriptor& filter_descriptor, - DeviceMemory* backward_filter_data, + DeviceMemory* backward_filter_data, dnn::DataType accumulator_type, ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, dnn::ProfileResult* output_profile_result); diff --git a/tensorflow/stream_executor/cuda/cuda_fft.cc b/tensorflow/stream_executor/cuda/cuda_fft.cc index cbf388a0f8..acac7d6368 100644 --- a/tensorflow/stream_executor/cuda/cuda_fft.cc +++ b/tensorflow/stream_executor/cuda/cuda_fft.cc @@ -23,6 +23,11 @@ limitations under the License. #include "tensorflow/stream_executor/cuda/cuda_platform_id.h" #include "tensorflow/stream_executor/cuda/cuda_stream.h" #include "tensorflow/stream_executor/device_memory.h" + +#ifndef PLATFORM_GOOGLE +#include "tensorflow/stream_executor/dso_loader.h" +#endif + #include "tensorflow/stream_executor/lib/env.h" #include "tensorflow/stream_executor/lib/initialize.h" #include "tensorflow/stream_executor/lib/status.h" @@ -38,6 +43,7 @@ PLUGIN_REGISTRY_DEFINE_PLUGIN_ID(kCuFftPlugin); namespace wrap { +#ifdef PLATFORM_GOOGLE // This macro wraps a global identifier, given by __name, in a callable // structure that loads the DLL symbol out of the DSO handle in a thread-safe // manner on first use. This dynamic loading technique is used to avoid DSO @@ -52,22 +58,69 @@ namespace wrap { } \ } __name; -#define CUFFT_ROUTINE_EACH(__macro) \ - __macro(cufftDestroy) __macro(cufftSetStream) __macro(cufftPlan1d) \ - __macro(cufftPlan2d) __macro(cufftPlan3d) __macro(cufftPlanMany) \ - __macro(cufftExecD2Z) __macro(cufftExecZ2D) __macro(cufftExecC2C) \ - __macro(cufftExecC2R) __macro(cufftExecZ2Z) \ - __macro(cufftExecR2C) __macro(cufftCreate) \ - __macro(cufftSetAutoAllocation) \ - __macro(cufftSetWorkArea) __macro(cufftGetSize1d) \ - __macro(cufftMakePlan1d) __macro(cufftGetSize2d) \ - __macro(cufftMakePlan2d) \ - __macro(cufftGetSize3d) \ - __macro(cufftMakePlan3d) \ - __macro(cufftGetSizeMany) \ - __macro(cufftMakePlanMany) +#else + +#define STREAM_EXECUTOR_CUFFT_WRAP(__name) \ + struct DynLoadShim__##__name { \ + static const char *kName; \ + using FuncPtrT = std::add_pointer::type; \ + static void *GetDsoHandle() { \ + auto s = internal::CachedDsoLoader::GetCufftDsoHandle(); \ + return s.ValueOrDie(); \ + } \ + static FuncPtrT LoadOrDie() { \ + void *f; \ + auto s = port::Env::Default()->GetSymbolFromLibrary(GetDsoHandle(), \ + kName, &f); \ + CHECK(s.ok()) << "could not find " << kName \ + << " in cufft DSO; dlerror: " << s.error_message(); \ + return reinterpret_cast(f); \ + } \ + static FuncPtrT DynLoad() { \ + static FuncPtrT f = LoadOrDie(); \ + return f; \ + } \ + template \ + cufftResult operator()(CUDAExecutor *parent, Args... args) { \ + cuda::ScopedActivateExecutorContext sac{parent}; \ + return DynLoad()(args...); \ + } \ + } __name; \ + const char *DynLoadShim__##__name::kName = #__name; + +#endif + +// clang-format off + +#define CUFFT_ROUTINE_EACH(__macro) \ + __macro(cufftDestroy) \ + __macro(cufftSetStream) \ + __macro(cufftPlan1d) \ + __macro(cufftPlan2d) \ + __macro(cufftPlan3d) \ + __macro(cufftPlanMany) \ + __macro(cufftExecD2Z) \ + __macro(cufftExecZ2D) \ + __macro(cufftExecC2C) \ + __macro(cufftExecC2R) \ + __macro(cufftExecZ2Z) \ + __macro(cufftExecR2C) \ + __macro(cufftCreate) \ + __macro(cufftSetAutoAllocation) \ + __macro(cufftSetWorkArea) \ + __macro(cufftGetSize1d) \ + __macro(cufftMakePlan1d) \ + __macro(cufftGetSize2d) \ + __macro(cufftMakePlan2d) \ + __macro(cufftGetSize3d) \ + __macro(cufftMakePlan3d) \ + __macro(cufftGetSizeMany) \ + __macro(cufftMakePlanMany) + +// clang-format on CUFFT_ROUTINE_EACH(STREAM_EXECUTOR_CUFFT_WRAP) +#undef CUFFT_ROUTINE_EACH } // namespace wrap diff --git a/tensorflow/stream_executor/cuda/cuda_gpu_executor.cc b/tensorflow/stream_executor/cuda/cuda_gpu_executor.cc index ad9154226c..4874d096ad 100644 --- a/tensorflow/stream_executor/cuda/cuda_gpu_executor.cc +++ b/tensorflow/stream_executor/cuda/cuda_gpu_executor.cc @@ -662,8 +662,13 @@ bool CUDAExecutor::MemcpyDeviceToDevice(Stream *stream, } bool CUDAExecutor::HostCallback(Stream *stream, - std::function callback) { - auto callback_ptr = new std::function(callback); + std::function callback) { + auto callback_ptr = new std::function([callback]() { + port::Status s = callback(); + if (!s.ok()) { + LOG(WARNING) << "Host callback failed: " << s; + } + }); return CUDADriver::AddStreamCallback(context_, AsCUDAStreamValue(stream), InternalHostCallback, callback_ptr); } diff --git a/tensorflow/stream_executor/cuda/cuda_gpu_executor.h b/tensorflow/stream_executor/cuda/cuda_gpu_executor.h index 90bf1c0242..ae8e4abf92 100644 --- a/tensorflow/stream_executor/cuda/cuda_gpu_executor.h +++ b/tensorflow/stream_executor/cuda/cuda_gpu_executor.h @@ -148,7 +148,8 @@ class CUDAExecutor : public internal::StreamExecutorInterface { const DeviceMemoryBase &gpu_src, uint64 size) override; - bool HostCallback(Stream *stream, std::function callback) override; + bool HostCallback(Stream *stream, + std::function callback) override; bool AllocateStream(Stream *stream) override; diff --git a/tensorflow/stream_executor/cuda/cuda_rng.cc b/tensorflow/stream_executor/cuda/cuda_rng.cc index 88c4f15792..7f92071932 100644 --- a/tensorflow/stream_executor/cuda/cuda_rng.cc +++ b/tensorflow/stream_executor/cuda/cuda_rng.cc @@ -21,6 +21,11 @@ limitations under the License. #include "tensorflow/stream_executor/cuda/cuda_platform_id.h" #include "tensorflow/stream_executor/cuda/cuda_stream.h" #include "tensorflow/stream_executor/device_memory.h" + +#ifndef PLATFORM_GOOGLE +#include "tensorflow/stream_executor/dso_loader.h" +#endif + #include "tensorflow/stream_executor/lib/env.h" #include "tensorflow/stream_executor/lib/initialize.h" #include "tensorflow/stream_executor/lib/status.h" @@ -61,6 +66,7 @@ PLUGIN_REGISTRY_DEFINE_PLUGIN_ID(kCuRandPlugin); namespace wrap { +#ifdef PLATFORM_GOOGLE #define STREAM_EXECUTOR_CURAND_WRAP(__name) \ struct WrapperShim__##__name { \ template \ @@ -70,6 +76,36 @@ namespace wrap { } \ } __name; +#else +#define STREAM_EXECUTOR_CURAND_WRAP(__name) \ + struct DynLoadShim__##__name { \ + static const char *kName; \ + using FuncPtrT = std::add_pointer::type; \ + static void *GetDsoHandle() { \ + auto s = internal::CachedDsoLoader::GetCurandDsoHandle(); \ + return s.ValueOrDie(); \ + } \ + static FuncPtrT LoadOrDie() { \ + void *f; \ + auto s = port::Env::Default()->GetSymbolFromLibrary(GetDsoHandle(), \ + kName, &f); \ + CHECK(s.ok()) << "could not find " << kName \ + << " in curand DSO; dlerror: " << s.error_message(); \ + return reinterpret_cast(f); \ + } \ + static FuncPtrT DynLoad() { \ + static FuncPtrT f = LoadOrDie(); \ + return f; \ + } \ + template \ + curandStatus_t operator()(CUDAExecutor *parent, Args... args) { \ + cuda::ScopedActivateExecutorContext sac{parent}; \ + return DynLoad()(args...); \ + } \ + } __name; \ + const char *DynLoadShim__##__name::kName = #__name; +#endif + STREAM_EXECUTOR_CURAND_WRAP(curandCreateGenerator); STREAM_EXECUTOR_CURAND_WRAP(curandDestroyGenerator); STREAM_EXECUTOR_CURAND_WRAP(curandSetStream); diff --git a/tensorflow/stream_executor/device_description.cc b/tensorflow/stream_executor/device_description.cc index 4120e230db..0b991b7ba8 100644 --- a/tensorflow/stream_executor/device_description.cc +++ b/tensorflow/stream_executor/device_description.cc @@ -140,21 +140,11 @@ void CalculateDimensionality(const DeviceDescription &device_description, uint64 element_count, uint64 *threads_per_block, uint64 *block_count) { *threads_per_block = device_description.threads_per_block_limit(); - *block_count = DivideCeil(element_count, *threads_per_block); + *block_count = port::MathUtil::CeilOfRatio(element_count, *threads_per_block); if (*block_count == 1) { CHECK_LE(element_count, *threads_per_block); *threads_per_block = element_count; } } -// Round value up to a multiple of n. -static uint64 RoundUp(uint64 value, uint64 n) { - return port::MathUtil::CeilOfRatio(value, n) * n; -} - -// Round value down to a multiple of n. -static uint64 RoundDown(uint64 value, uint64 n) { - return port::MathUtil::FloorOfRatio(value, n) * n; -} - } // namespace stream_executor diff --git a/tensorflow/stream_executor/dnn.cc b/tensorflow/stream_executor/dnn.cc index 3d8e691ab2..faa662211e 100644 --- a/tensorflow/stream_executor/dnn.cc +++ b/tensorflow/stream_executor/dnn.cc @@ -23,7 +23,7 @@ namespace stream_executor { namespace dnn { uint64 AlgorithmDesc::hash() const { - return ::tensorflow::Hash64Combine(algo_, tensor_ops_enabled_); + return ::tensorflow::Hash64Combine(algo_id(), tensor_ops_enabled()); } bool DnnSupport::GetConvolveAlgorithms( @@ -187,6 +187,9 @@ std::tuple GetDimIndices(const DataLayout& layout, batch_idx = 0; spatial_idx = 2; break; + + default: + LOG(FATAL) << "Unknown layout " << layout; } return std::make_tuple(depth_idx, batch_idx, spatial_idx); @@ -233,28 +236,27 @@ string AlgorithmConfig::ToString() const { // -- BatchDescriptor BatchDescriptor::BatchDescriptor(int ndims) - : count_(0), - feature_map_count_(0), - spatial_size_(ndims, 0), - value_max_(0.0), + : value_max_(0.0), value_min_(0.0), - layout_(DataLayout::kYXDepthBatch), - ndims_(ndims), - quantized_activation_mode_(QuantizedActivationMode::k8Bit) {} + quantized_activation_mode_(QuantizedActivationMode::k8Bit) { + tensor_.mutable_dimensions()->Resize(ndims + 2, 0); + set_layout(DataLayout::kYXDepthBatch); +} BatchDescriptor::BatchDescriptor() : BatchDescriptor(/*ndims=*/2) {} std::vector BatchDescriptor::full_dims(const DataLayout& layout) const { - std::vector bdyx_dims(ndims_ + 2); + std::vector bdyx_dims(ndims() + 2); bdyx_dims[0] = count(); bdyx_dims[1] = feature_map_count(); - std::copy(spatial_size_.begin(), spatial_size_.end(), bdyx_dims.begin() + 2); + std::copy(spatial_size().begin(), spatial_size().end(), + bdyx_dims.begin() + 2); return ReorderDims(bdyx_dims, DataLayout::kBatchDepthYX, layout); } std::vector BatchDescriptor::full_strides( const DataLayout& layout) const { - if (layout_ == DataLayout::kBatchDepthYX4) { + if (this->layout() == DataLayout::kBatchDepthYX4) { LOG(FATAL) << "Cannot compute full strides for batch descriptor " << ToString() << ", because its layout is kBatchDepthYX4. In fact, " @@ -262,36 +264,32 @@ std::vector BatchDescriptor::full_strides( "Use cudnnSetTensor4DDescriptor to set cudnnTensorDescriptor_t " "instead."; } - std::vector phys_dims = full_dims(layout_); + std::vector phys_dims = full_dims(this->layout()); std::vector phys_strides(phys_dims.size()); - phys_strides[ndims_ + 1] = 1; - for (int i = ndims_; i >= 0; i--) { + phys_strides[ndims() + 1] = 1; + for (int i = ndims(); i >= 0; i--) { phys_strides[i] = phys_strides[i + 1] * phys_dims[i + 1]; } - return ReorderDims(phys_strides, layout_, layout); + return ReorderDims(phys_strides, this->layout(), layout); } void BatchDescriptor::CloneFrom(const BatchDescriptor& other) { - count_ = other.count_; - feature_map_count_ = other.feature_map_count_; - spatial_size_ = other.spatial_size_; + tensor_ = other.tensor_; value_max_ = other.value_max_; value_min_ = other.value_min_; - layout_ = other.layout_; - ndims_ = other.ndims_; quantized_activation_mode_ = other.quantized_activation_mode_; } string BatchDescriptor::ToString() const { string spatial; - for (int i = 0; i < ndims_; i++) { - port::Appendf(&spatial, "%lld ", spatial_size_[i]); + for (int i = 0; i < ndims(); i++) { + port::Appendf(&spatial, "%lld ", spatial_size()[i]); } return port::Printf( "{count: %lld feature_map_count: %lld spatial: %s " "value_min: %f value_max: %f layout: %s}", - count_, feature_map_count_, spatial.c_str(), value_min_, value_max_, - DataLayoutString(layout_).c_str()); + count(), feature_map_count(), spatial.c_str(), value_min_, value_max_, + DataLayoutString(layout()).c_str()); } string BatchDescriptor::ToShortString() const { @@ -302,8 +300,8 @@ string BatchDescriptor::ToShortString() const { string batch = absl::StrCat("b", count()); string spatial = "s"; - for (int i = 0; i < ndims_; i++) { - port::Appendf(&spatial, "%lld ", spatial_size_[i]); + for (int i = 0; i < ndims(); i++) { + port::Appendf(&spatial, "%lld ", spatial_size()[i]); } string suffix; @@ -333,18 +331,18 @@ string BatchDescriptor::ToShortString() const { int64 BatchDescriptor::NodesPerFeatureMap() const { int64 ret = 1; - for (int i = 0; i < ndims_; i++) { - ret *= spatial_size_[i]; + for (int i = 0; i < ndims(); i++) { + ret *= spatial_size()[i]; } return ret; } int64 BatchDescriptor::NodesAcrossFeatureMaps() const { - return NodesPerFeatureMap() * feature_map_count_; + return NodesPerFeatureMap() * feature_map_count(); } int64 BatchDescriptor::ElementCount() const { - return count_ * feature_map_count_ * NodesPerFeatureMap(); + return count() * feature_map_count() * NodesPerFeatureMap(); } int64 BatchDescriptor::FullyConnectedWeightCount( @@ -372,33 +370,27 @@ BatchDescriptor BatchDescriptor::DepthConcatenateOutputDescriptor( // -- FilterDescriptor -FilterDescriptor::FilterDescriptor(int ndims) - : output_feature_map_count_(0), - input_feature_map_count_(0), - input_filter_dims_(ndims, 0), - ndims_(ndims), - layout_(FilterLayout::kOutputInputYX) {} +FilterDescriptor::FilterDescriptor(int ndims) { + tensor_.mutable_dimensions()->Resize(ndims + 2, 0); + set_layout(FilterLayout::kOutputInputYX); +} FilterDescriptor::FilterDescriptor() : FilterDescriptor(/*ndims=*/2) {} FilterDescriptor::~FilterDescriptor() {} void FilterDescriptor::CloneFrom(const FilterDescriptor& other) { - set_output_feature_map_count(other.output_feature_map_count()) - .set_input_feature_map_count(other.input_feature_map_count()) - .set_layout(other.layout()); - input_filter_dims_ = other.input_filter_dims_; - ndims_ = other.ndims_; + tensor_ = other.tensor_; } string FilterDescriptor::ToString() const { string desc = port::Printf( "{output_feature_map_count: %lld input_feature_map_count: %lld " "layout: %s shape: ", - output_feature_map_count_, input_feature_map_count_, - FilterLayoutString(layout_).c_str()); - for (int i = 0; i < ndims_; i++) { - port::Appendf(&desc, "%lld ", input_filter_dims_[i]); + output_feature_map_count(), input_feature_map_count(), + FilterLayoutString(layout()).c_str()); + for (int i = 0; i < ndims(); i++) { + port::Appendf(&desc, "%lld ", input_filter_dims()[i]); } absl::StrAppend(&desc, "}"); @@ -409,15 +401,15 @@ string FilterDescriptor::ToShortString() const { // All the constituent strings are less than 15 characters, so the // small string optimization ensures that there will be at most one // heap memory allocation. - string od = absl::StrCat("od", output_feature_map_count_); - string id = absl::StrCat("id", input_feature_map_count_); + string od = absl::StrCat("od", output_feature_map_count()); + string id = absl::StrCat("id", input_feature_map_count()); string spatial = "s"; - for (int i = 0; i < ndims_; i++) { - port::Appendf(&spatial, "%lld ", input_filter_dims_[i]); + for (int i = 0; i < ndims(); i++) { + port::Appendf(&spatial, "%lld ", input_filter_dims()[i]); } - switch (layout_) { + switch (layout()) { case FilterLayout::kOutputInputYX: return absl::StrCat(od, id, spatial); case FilterLayout::kOutputYXInput: @@ -429,27 +421,28 @@ string FilterDescriptor::ToShortString() const { case FilterLayout::kYXInputOutput: return absl::StrCat(spatial, id, od); default: - LOG(FATAL) << "Unknown layout " << static_cast(layout_); + LOG(FATAL) << "Unknown layout " << static_cast(layout()); return ""; // Avoid return warning (unreachable) } } int64 FilterDescriptor::ComputeWeightCount() const { - int64 ret = output_feature_map_count_ * input_feature_map_count_; - for (int i = 0; i < ndims_; i++) { - ret *= input_filter_dims_[i]; + int64 ret = output_feature_map_count() * input_feature_map_count(); + for (int i = 0; i < ndims(); i++) { + ret *= input_filter_dims()[i]; } return ret; } // -- ConvolutionDescriptor -ConvolutionDescriptor::ConvolutionDescriptor(int ndims) - : zero_padding_(ndims, 0), - filter_strides_(ndims, 1), - dilation_rates_(ndims, 1), - group_count_(1), - ndims_(ndims) {} +ConvolutionDescriptor::ConvolutionDescriptor(int ndims) { + proto_.mutable_paddings()->Resize(ndims, 0); + proto_.mutable_strides()->Resize(ndims, 1); + proto_.mutable_dilations()->Resize(ndims, 1); + proto_.set_group_count(1); + proto_.set_convolution_mode(ConvolutionMode::CROSS_CORRELATION); +} ConvolutionDescriptor::ConvolutionDescriptor() : ConvolutionDescriptor(/*ndims=*/2) {} @@ -460,10 +453,10 @@ string ConvolutionDescriptor::ToString() const { string padding; string strides; string dilations; - for (int i = 0; i < ndims_; i++) { - port::Appendf(&padding, "%lld ", zero_padding_[i]); - port::Appendf(&strides, "%lld ", filter_strides_[i]); - port::Appendf(&dilations, "%lld ", dilation_rates_[i]); + for (int i = 0; i < ndims(); i++) { + port::Appendf(&padding, "%lld ", this->padding()[i]); + port::Appendf(&strides, "%lld ", this->strides()[i]); + port::Appendf(&dilations, "%lld ", this->dilations()[i]); } return port::Printf( @@ -475,15 +468,15 @@ string ConvolutionDescriptor::ToString() const { string ConvolutionDescriptor::ToShortString() const { string desc; - for (int i = 0; i < ndims_; i++) { + for (int i = 0; i < ndims(); i++) { if (i > 0) port::Appendf(&desc, "_"); - port::Appendf(&desc, "p%d:%lld", i, zero_padding_[i]); + port::Appendf(&desc, "p%d:%lld", i, padding()[i]); } - for (int i = 0; i < ndims_; i++) { - port::Appendf(&desc, "_s%d:%lld", i, filter_strides_[i]); + for (int i = 0; i < ndims(); i++) { + port::Appendf(&desc, "_s%d:%lld", i, strides()[i]); } - for (int i = 0; i < ndims_; i++) { - port::Appendf(&desc, "_d%d:%lld", i, dilation_rates_[i]); + for (int i = 0; i < ndims(); i++) { + port::Appendf(&desc, "_d%d:%lld", i, dilations()[i]); } return desc; } diff --git a/tensorflow/stream_executor/dnn.h b/tensorflow/stream_executor/dnn.h index c934301829..c044a356ef 100644 --- a/tensorflow/stream_executor/dnn.h +++ b/tensorflow/stream_executor/dnn.h @@ -29,7 +29,9 @@ limitations under the License. #include "absl/types/optional.h" #include "absl/types/span.h" +#include "tensorflow/core/platform/protobuf.h" #include "tensorflow/stream_executor/device_memory.h" +#include "tensorflow/stream_executor/dnn.pb.h" #include "tensorflow/stream_executor/lib/array_slice.h" #include "tensorflow/stream_executor/lib/status.h" #include "tensorflow/stream_executor/lib/statusor.h" @@ -48,19 +50,6 @@ class ScratchAllocator; namespace dnn { -// Describes how an input or output layer's data is formatted. -// Specify int64 so there's no padding in BatchDescriptor. -enum class DataLayout : int64 { - kYXDepthBatch = 0, // Same as dist_belief::DF_DEPTH_MAJOR. - kYXBatchDepth, // Same as dist_belief::DF_BATCH_MAJOR. - kBatchYXDepth, // Same as run_brain output, and tensorflow's layout. - kBatchDepthYX, // cuDNN's NCHW layout, data laid out as image, feature - // maps, rows, columns. - kBatchDepthYX4, // cuDNN's NCHW_VECT_C layout, data laid out the same as - // kBatchDepthYX but each element is a vector of 4 feature - // maps. -}; - // Specifies an index to use when accessing specific spatial dimensions. enum class DimIndex : int { X = 0, @@ -73,8 +62,27 @@ inline int64 GetDim(absl::Span data, DimIndex dim) { return data.rbegin()[static_cast(dim)]; } +inline void SetDim(absl::Span data, DimIndex dim, int64 value) { + data.rbegin()[static_cast(dim)] = value; +} + inline void SetDim(std::vector* data, DimIndex dim, int64 value) { - data->rbegin()[static_cast(dim)] = value; + return SetDim(absl::MakeSpan(*data), dim, value); +} + +// tensorflow::int64 is not the same type as tensorflow::protobuf_int64 in +// open-source. Wrapper function that gives an int64 array slice view of a +// repeated int64 protobuf field. +inline absl::Span AsInt64Slice( + const tensorflow::protobuf::RepeatedField& v) { + return absl::Span(reinterpret_cast(v.data()), + v.size()); +} + +inline absl::Span AsInt64Slice( + tensorflow::protobuf::RepeatedField* v) { + return absl::Span(reinterpret_cast(v->mutable_data()), + v->size()); } // Returns a string representation of the given data layout. @@ -87,14 +95,6 @@ enum class QuantizedActivationMode { k32Bit = 4, }; -// Specifies the data type used by an operation. -enum class DataType { - kFloat = 0, - kDouble = 1, - kHalf = 2, - kInt8 = 3, -}; - // A helper class to convert C/C++ types to the proper enums. template struct ToDataType; @@ -114,6 +114,10 @@ template <> struct ToDataType { static constexpr DataType value = DataType::kInt8; }; +template <> +struct ToDataType { + static constexpr DataType value = DataType::kInt32; +}; // Specifies the types of a RNN model. enum class RnnMode { @@ -245,15 +249,15 @@ class BatchDescriptor { string ToShortString() const; // Accessors. - int64 count() const { return count_; } - int64 feature_map_count() const { return feature_map_count_; } - int64 height() const { return GetDim(spatial_size_, DimIndex::Y); } - int64 width() const { return GetDim(spatial_size_, DimIndex::X); } - int64 spatial_dim(DimIndex dim) const { return GetDim(spatial_size_, dim); } - int ndims() const { return ndims_; } + int64 count() const { return tensor_.dimensions(0); } + int64 feature_map_count() const { return tensor_.dimensions(1); } + int64 height() const { return GetDim(spatial_size(), DimIndex::Y); } + int64 width() const { return GetDim(spatial_size(), DimIndex::X); } + int64 spatial_dim(DimIndex dim) const { return GetDim(spatial_size(), dim); } + int ndims() const { return spatial_size().size(); } float value_max() const { return value_max_; } float value_min() const { return value_min_; } - DataLayout layout() const { return layout_; } + DataLayout layout() const { return tensor_.data_layout(); } QuantizedActivationMode quantized_activation_mode() const { return quantized_activation_mode_; } @@ -267,23 +271,23 @@ class BatchDescriptor { // Named-argument helpers for avoiding user error during construction. BatchDescriptor& set_count(int64 value) { - count_ = value; + tensor_.set_dimensions(0, value); return *this; } BatchDescriptor& set_feature_map_count(int64 value) { - feature_map_count_ = value; + tensor_.set_dimensions(1, value); return *this; } BatchDescriptor& set_height(int64 value) { - SetDim(&spatial_size_, DimIndex::Y, value); + SetDim(spatial_size(), DimIndex::Y, value); return *this; } BatchDescriptor& set_width(int64 value) { - SetDim(&spatial_size_, DimIndex::X, value); + SetDim(spatial_size(), DimIndex::X, value); return *this; } BatchDescriptor& set_spatial_dim(DimIndex dim, int64 value) { - SetDim(&spatial_size_, dim, value); + SetDim(spatial_size(), dim, value); return *this; } BatchDescriptor& set_value_max(float value) { @@ -295,7 +299,7 @@ class BatchDescriptor { return *this; } BatchDescriptor& set_layout(DataLayout layout) { - layout_ = layout; + tensor_.set_data_layout(layout); return *this; } BatchDescriptor& set_quantized_activation_mode( @@ -334,31 +338,20 @@ class BatchDescriptor { port::ArraySlice inputs); private: - int64 count_; - int64 feature_map_count_; - // Stored as: ..., y, x. - std::vector spatial_size_; + absl::Span spatial_size() const { + return AsInt64Slice(tensor_.dimensions()).subspan(2); + } + + absl::Span spatial_size() { + return AsInt64Slice(tensor_.mutable_dimensions()).subspan(2); + } + + TensorDescriptorProto tensor_; float value_max_; float value_min_; - DataLayout layout_; - int ndims_; QuantizedActivationMode quantized_activation_mode_; }; -// Describes how a filter is laid out in the memory. -// Specify int64 so there's no padding in FilterDescriptor. -enum class FilterLayout : int64 { - kOutputInputYX = 0, // cuDNN's default filter layout, laid out as: - // (major) output feature maps >> input feature maps >> - // rows >> columns (minor). - kOutputYXInput, // major to minor: - // (output features, row, columns, input features) - kOutputInputYX4, // laid out the same as kOutputInputYX but each element is a - // vector of 4 feature maps. - kInputYXOutput, // Same as dist_belief's default filter layout. - kYXInputOutput, // Same as tensorflow's default filter layout. -}; - // Returns a string representation of the given filter layout. string FilterLayoutString(FilterLayout layout); @@ -398,30 +391,30 @@ class FilterDescriptor { // Named-argument helpers for avoiding user error during construction. FilterDescriptor& set_output_feature_map_count(int64 value) { - output_feature_map_count_ = value; + tensor_.set_dimensions(0, value); return *this; } FilterDescriptor& set_input_feature_map_count(int64 value) { - input_feature_map_count_ = value; + tensor_.set_dimensions(1, value); return *this; } FilterDescriptor& set_input_filter_height(int64 value) { - SetDim(&input_filter_dims_, DimIndex::Y, value); + SetDim(input_filter_dims(), DimIndex::Y, value); return *this; } FilterDescriptor& set_input_filter_width(int64 value) { - SetDim(&input_filter_dims_, DimIndex::X, value); + SetDim(input_filter_dims(), DimIndex::X, value); return *this; } FilterDescriptor& set_layout(FilterLayout layout) { - layout_ = layout; + tensor_.set_filter_layout(layout); return *this; } FilterDescriptor& set_spatial_dim(DimIndex dim, int64 value) { - SetDim(&input_filter_dims_, dim, value); + SetDim(input_filter_dims(), dim, value); return *this; } - int ndims() const { return ndims_; } + int ndims() const { return input_filter_dims().size(); } void CloneFrom(const FilterDescriptor& other); @@ -434,32 +427,32 @@ class FilterDescriptor { // Returns the number of biases required as parameters for a convolution // using this filter descriptor. - int64 bias_count() const { return output_feature_map_count_; } + int64 bias_count() const { return output_feature_map_count(); } - int64 output_feature_map_count() const { return output_feature_map_count_; } - int64 input_feature_map_count() const { return input_feature_map_count_; } + int64 output_feature_map_count() const { return tensor_.dimensions(0); } + int64 input_feature_map_count() const { return tensor_.dimensions(1); } int64 input_filter_height() const { - return GetDim(input_filter_dims_, DimIndex::Y); + return GetDim(input_filter_dims(), DimIndex::Y); } int64 input_filter_width() const { - return GetDim(input_filter_dims_, DimIndex::X); + return GetDim(input_filter_dims(), DimIndex::X); } int64 input_filter_dim(DimIndex dim) const { - return GetDim(input_filter_dims_, dim); + return GetDim(input_filter_dims(), dim); } - FilterLayout layout() const { return layout_; } + FilterLayout layout() const { return tensor_.filter_layout(); } + absl::Span input_filter_dims() const { - return input_filter_dims_; + return AsInt64Slice(tensor_.dimensions()).subspan(2); } private: - int64 output_feature_map_count_; - int64 input_feature_map_count_; - // Stored as: ..., y, x. - std::vector input_filter_dims_; - int ndims_; - FilterLayout layout_; + absl::Span input_filter_dims() { + return AsInt64Slice(tensor_.mutable_dimensions()).subspan(2); + } + + TensorDescriptorProto tensor_; }; // Describes how padding should be aligned when the total number of pad @@ -500,6 +493,11 @@ std::ostream& operator<<(std::ostream& str, dnn::PadAlignment alignment); // cells between each filter element in the "y dimension". // - horizontal_dilation_rate: there will be (horizontal_dilation_rate - 1) // skipped cells between each filter element in the "x dimension". +// - convolution_not_crosscor: By default (convolution_not_crosscor == false), +// we perform cross correlation rather than convolution. With the flag set, +// we perform convolution. Convolution and cross correlation are related by +// rotating the filter by 180 degrees (or equivalently flipping all spatial +// dimensions). class ConvolutionDescriptor { public: // By default construction, there is no zero-padding and the filter stride is @@ -513,84 +511,102 @@ class ConvolutionDescriptor { string ToShortString() const; ConvolutionDescriptor& set_zero_padding_height(int64 value) { - SetDim(&zero_padding_, DimIndex::Y, value); + SetDim(padding(), DimIndex::Y, value); return *this; } ConvolutionDescriptor& set_zero_padding_width(int64 value) { - SetDim(&zero_padding_, DimIndex::X, value); + SetDim(padding(), DimIndex::X, value); return *this; } ConvolutionDescriptor& set_zero_padding(DimIndex dim, int64 value) { - SetDim(&zero_padding_, dim, value); + SetDim(padding(), dim, value); return *this; } ConvolutionDescriptor& set_vertical_filter_stride(int64 value) { - SetDim(&filter_strides_, DimIndex::Y, value); + SetDim(strides(), DimIndex::Y, value); return *this; } ConvolutionDescriptor& set_horizontal_filter_stride(int64 value) { - SetDim(&filter_strides_, DimIndex::X, value); + SetDim(strides(), DimIndex::X, value); return *this; } ConvolutionDescriptor& set_filter_stride(DimIndex dim, int64 value) { - SetDim(&filter_strides_, dim, value); + SetDim(strides(), dim, value); return *this; } ConvolutionDescriptor& set_vertical_dilation_rate(int64 value) { - SetDim(&dilation_rates_, DimIndex::Y, value); + SetDim(dilations(), DimIndex::Y, value); return *this; } ConvolutionDescriptor& set_horizontal_dilation_rate(int64 value) { - SetDim(&dilation_rates_, DimIndex::X, value); + SetDim(dilations(), DimIndex::X, value); return *this; } ConvolutionDescriptor& set_dilation_rate(DimIndex dim, int64 value) { - SetDim(&dilation_rates_, dim, value); + SetDim(dilations(), dim, value); return *this; } ConvolutionDescriptor& set_group_count(int group_count) { - group_count_ = group_count; + proto_.set_group_count(group_count); return *this; } - int64 zero_padding_height() const { - return GetDim(zero_padding_, DimIndex::Y); - } - int64 zero_padding_width() const { - return GetDim(zero_padding_, DimIndex::X); + ConvolutionDescriptor& set_convolution_not_crosscorr(bool conv) { + proto_.set_convolution_mode(conv ? ConvolutionMode::CONVOLUTION + : ConvolutionMode::CROSS_CORRELATION); + return *this; } + int64 zero_padding_height() const { return GetDim(padding(), DimIndex::Y); } + int64 zero_padding_width() const { return GetDim(padding(), DimIndex::X); } int64 vertical_filter_stride() const { - return GetDim(filter_strides_, DimIndex::Y); + return GetDim(strides(), DimIndex::Y); } int64 horizontal_filter_stride() const { - return GetDim(filter_strides_, DimIndex::X); + return GetDim(strides(), DimIndex::X); } int64 vertical_dilation_rate() const { - return GetDim(dilation_rates_, DimIndex::Y); + return GetDim(dilations(), DimIndex::Y); } int64 horizontal_dilation_rate() const { - return GetDim(dilation_rates_, DimIndex::X); + return GetDim(dilations(), DimIndex::X); } - int zero_padding(DimIndex dim) const { return GetDim(zero_padding_, dim); } - int filter_stride(DimIndex dim) const { return GetDim(filter_strides_, dim); } - int dilation_rate(DimIndex dim) const { return GetDim(dilation_rates_, dim); } + int zero_padding(DimIndex dim) const { return GetDim(padding(), dim); } + int filter_stride(DimIndex dim) const { return GetDim(strides(), dim); } + int dilation_rate(DimIndex dim) const { return GetDim(dilations(), dim); } // TODO(timshen): remove this function. No users of this class is setting a // non-default pad alignment. PadAlignment pad_alignment() const { return PadAlignment::kDefault; } - int group_count() const { return group_count_; } - int ndims() const { return ndims_; } + int group_count() const { return proto_.group_count(); } + int ndims() const { return padding().size(); } + bool convolution_not_crosscorr() const { + return proto_.convolution_mode() == ConvolutionMode::CONVOLUTION; + } + + absl::Span strides() const { + return AsInt64Slice(proto_.strides()); + } - absl::Span strides() const { return filter_strides_; } - absl::Span dilations() const { return dilation_rates_; } - absl::Span padding() const { return zero_padding_; } + absl::Span dilations() const { + return AsInt64Slice(proto_.dilations()); + } + + absl::Span padding() const { + return AsInt64Slice(proto_.paddings()); + } private: - // Stored as: .. y, x. - std::vector zero_padding_; - std::vector filter_strides_; - std::vector dilation_rates_; - int group_count_; - int ndims_; + absl::Span strides() { return AsInt64Slice(proto_.mutable_strides()); } + + absl::Span dilations() { + return AsInt64Slice(proto_.mutable_dilations()); + } + + absl::Span padding() { + return AsInt64Slice(proto_.mutable_paddings()); + } + + ConvolutionDescriptorProto proto_; + // TODO(leary) cudnn provides these fields, but need to characterize what // their effect is -- they may be boolean rather than integral. // int64 upscale_input_x; @@ -714,21 +730,23 @@ class PoolingDescriptor { class AlgorithmDesc { public: typedef int64 Index; - AlgorithmDesc(Index a, bool use_tensor_ops) - : algo_(a), tensor_ops_enabled_(use_tensor_ops) { - DCHECK_NE(a, -1); + AlgorithmDesc(Index a, bool use_tensor_ops) { + proto_.set_algo_id(a); + proto_.set_math_type(use_tensor_ops ? AlgorithmProto::TENSOR_OP_MATH + : AlgorithmProto::DEFAULT_MATH); + } + bool tensor_ops_enabled() const { + return proto_.math_type() == AlgorithmProto::TENSOR_OP_MATH; } - bool tensor_ops_enabled() const { return tensor_ops_enabled_; } - Index algo_id() const { return algo_; } + Index algo_id() const { return proto_.algo_id(); } bool operator==(const AlgorithmDesc& other) const { - return this->algo_ == other.algo_ && - this->tensor_ops_enabled_ == other.tensor_ops_enabled_; + return algo_id() == other.algo_id() && + tensor_ops_enabled() == other.tensor_ops_enabled(); } uint64 hash() const; private: - Index algo_; - bool tensor_ops_enabled_; + AlgorithmProto proto_; }; // Describes the result from a perf experiment. @@ -872,24 +890,6 @@ class NormalizeDescriptor { int32 segment_size_; }; -// Describes a kind of non-linearity (threshold-like mathematical function). -enum class ActivationMode { - kNone = 0, - kSigmoid, - // Rectified linear activation: f(x) = x < 0 ? 0 : x - kRelu, - // Rectified linear activation, where upper maximum is 6.0. - kRelu6, - // Rectified linear activation, where upper maximum specified by - // BatchDescriptor::value_max(). - kReluX, - kTanh, - // Like ReluX, but passes all values in the range [-X,X]. - kBandPass, - - kNumActivationModes, // Always in the end. -}; - // Returns a string representation of the given activation mode. string ActivationModeString(ActivationMode mode); diff --git a/tensorflow/stream_executor/dnn.proto b/tensorflow/stream_executor/dnn.proto new file mode 100644 index 0000000000..56b079c3f5 --- /dev/null +++ b/tensorflow/stream_executor/dnn.proto @@ -0,0 +1,103 @@ +// LINT: LEGACY_NAMES +syntax = "proto3"; + +package stream_executor.dnn; + +// Specifies the data type used by an operation. +enum DataType { + kFloat = 0; + kDouble = 1; + kHalf = 2; + kInt8 = 3; + kInt32 = 4; +} + +// Describes how a convolution input or output layer's data is formatted. +enum DataLayout { + // Naming convention: + // Y <-> row or height + // X <-> column or width + // Batch <-> batch, or N + // Depth <-> feature, or channel + // TODO(timshen): turn them into cuDNN names, e.g. kNCHW. + kYXDepthBatch = 0; + kYXBatchDepth = 1; + kBatchYXDepth = 2; // cuDNN's NHWC layout + kBatchDepthYX = 3; // cuDNN's NCHW layout + kBatchDepthYX4 = 4; // cuDNN's NCHW_VECT_C layout +} + +// Describes how a convolution filter is laid out in the memory. +enum FilterLayout { + // Naming convention: + // Y <-> row or height + // X <-> column or width + // Output <-> output feature, or N + // Input <-> input feature, or N + // TODO(timshen): turn them into cuDNN names, e.g. kNCHW. + kOutputInputYX = 0; // cuDNN's NCHW layout + kOutputYXInput = 1; // cuDNN's NHWC layout + kOutputInputYX4 = 2; // cuDNN's NCHW_VECT_C layout + kInputYXOutput = 3; + kYXInputOutput = 4; +} + +// Describes a kind of non-linearity (threshold-like mathematical function). +enum ActivationMode { + kNone = 0; + kSigmoid = 1; + // Rectified linear activation: f(x) = x < 0 ? 0 : x + kRelu = 2; + // Rectified linear activation; where upper maximum is 6.0. + kRelu6 = 3; + // Rectified linear activation; where upper maximum specified by + // BatchDescriptor::value_max(). + kReluX = 4; + kTanh = 5; + // Like ReluX; but passes all values in the range [-X,X]. + kBandPass = 6; +} + +// Describe the math definition for the conv op. The popular behavior is +// actually called cross-correlation in math, despite the operation is often +// referred as convolution. See cuDNN cudnnConvolutionMode_t. +enum ConvolutionMode { + CROSS_CORRELATION = 0; + CONVOLUTION = 1; +} + +// Generic tensor representation. +message TensorDescriptorProto { + repeated int64 dimensions = 1; + DataType data_type = 2; + oneof layout_oneof { + DataLayout data_layout = 3; + FilterLayout filter_layout = 4; + } +} + +// Generic algorithm representation. +message AlgorithmProto { + enum MathType { + DEFAULT_MATH = 0; + // The GPU may operate 4x4 matrix FMA. + // See cuDNN's documentation for CUDNN_TENSOR_OP_MATH. + TENSOR_OP_MATH = 1; + } + int64 algo_id = 1; + MathType math_type = 2; +} + +// Convolution-specific parameters. +message ConvolutionDescriptorProto { + repeated int64 paddings = 1; + repeated int64 strides = 2; + repeated int64 dilations = 3; + // The "accumulator" type. For example, use F32 as an accumulator for F16 + // convolutions. + // See cuDNN's cudnnConvolutionMode_t. + DataType compute_mode = 4; + // See cuDNN's group count. + int32 group_count = 5; + ConvolutionMode convolution_mode = 6; +} diff --git a/tensorflow/stream_executor/host/host_gpu_executor.cc b/tensorflow/stream_executor/host/host_gpu_executor.cc index 8adf739b17..1396a83dfb 100644 --- a/tensorflow/stream_executor/host/host_gpu_executor.cc +++ b/tensorflow/stream_executor/host/host_gpu_executor.cc @@ -148,8 +148,13 @@ port::Status HostExecutor::SynchronousMemcpyDeviceToDevice( } bool HostExecutor::HostCallback(Stream *stream, - std::function callback) { - AsHostStream(stream)->EnqueueTask(callback); + std::function callback) { + AsHostStream(stream)->EnqueueTask([callback]() { + port::Status s = callback(); + if (!s.ok()) { + LOG(WARNING) << "Host callback failed: " << s; + } + }); return true; } diff --git a/tensorflow/stream_executor/host/host_gpu_executor.h b/tensorflow/stream_executor/host/host_gpu_executor.h index 7ba1f18101..56e3c2aa6a 100644 --- a/tensorflow/stream_executor/host/host_gpu_executor.h +++ b/tensorflow/stream_executor/host/host_gpu_executor.h @@ -103,7 +103,8 @@ class HostExecutor : public internal::StreamExecutorInterface { const DeviceMemoryBase &gpu_src, uint64 size) override; - bool HostCallback(Stream *stream, std::function callback) override; + bool HostCallback(Stream *stream, + std::function callback) override; port::Status AllocateEvent(Event *event) override { return port::Status(port::error::UNIMPLEMENTED, ""); diff --git a/tensorflow/stream_executor/stream.cc b/tensorflow/stream_executor/stream.cc index 5421e4f4a5..3edc66cde8 100644 --- a/tensorflow/stream_executor/stream.cc +++ b/tensorflow/stream_executor/stream.cc @@ -191,8 +191,11 @@ string ToVlogString(dnn::DataType data_type) { return "dnn::DataType::kHalf"; case dnn::DataType::kInt8: return "dnn::DataType::kInt8"; + case dnn::DataType::kInt32: + return "dnn::DataType::kInt32"; + default: + return "unknown DataType"; } - return "unknown DataType"; } // Used together with PARAM to VLOG calls made to the stream. Intended diff --git a/tensorflow/stream_executor/stream.h b/tensorflow/stream_executor/stream.h index e1629b5b30..0fc90cf83d 100644 --- a/tensorflow/stream_executor/stream.h +++ b/tensorflow/stream_executor/stream.h @@ -2033,9 +2033,20 @@ class Stream { // transferred to the caller. internal::StreamInterface *implementation() { return implementation_.get(); } + // Entrains onto the stream a callback to the host (from the device). + // Behaves as ThenDoHostCallbackWithStatus below, but the callback should + // never fail or its failure is inconsequential. + // + // This is kept for backward compatibility. Future code should use + // ThenDoHostCallbackWithStatus and explicitly return a success status. + // TODO(b/112125301): Eventually remove this method. + Stream &ThenDoHostCallback(std::function callback); + // Entrains onto the stream a callback to the host (from the device). // Host callbacks block/occupy the stream just as device functions // (execute one at a time, block later stream operations). + // Whether the callback return status affects the result of BlockHostUntilDone + // is platform-dependent. // // Behavior is undefined when synchronizing using OpenCL user events. // Behavior is undefined if host callbacks call device routines or insert @@ -2043,11 +2054,6 @@ class Stream { // // On certain platforms, ThenDoHostCallback is expected to have significant // negative effects on performance. - Stream &ThenDoHostCallback(std::function callback); - - // Entrains onto the stream a callback to the host (from the device). - // Behaves as ThenDoHostCallback above, but returns a Status instead of void. - // This overload should be preferred if the callback could fail. Stream &ThenDoHostCallbackWithStatus(std::function callback); // Returns the StreamExecutor (parent object) associated with this stream. diff --git a/tensorflow/stream_executor/stream_executor_internal.cc b/tensorflow/stream_executor/stream_executor_internal.cc index 7df6a361c6..341c6edccd 100644 --- a/tensorflow/stream_executor/stream_executor_internal.cc +++ b/tensorflow/stream_executor/stream_executor_internal.cc @@ -36,16 +36,15 @@ StreamExecutorFactory* MakeOpenCLExecutorImplementation() { StreamExecutorFactory MakeHostExecutorImplementation; -// TODO(b/112125301): Consolodate this down to one implementation of -// HostCallback, taking a callback that returns a Status. -bool StreamExecutorInterface::HostCallback( - Stream* stream, std::function callback) { - return HostCallback(stream, [callback]() { - port::Status s = callback(); - if (!s.ok()) { - LOG(WARNING) << "HostCallback failed: " << s; - } - }); +// The default implementation just calls the other HostCallback method. +// It should make all existing code that uses a void() callback still work. +bool StreamExecutorInterface::HostCallback(Stream* stream, + std::function callback) { + return HostCallback( + stream, std::function([callback]() -> port::Status { + callback(); + return port::Status::OK(); + })); } } // namespace internal diff --git a/tensorflow/stream_executor/stream_executor_internal.h b/tensorflow/stream_executor/stream_executor_internal.h index 32f75fd1bc..0c2c33cfca 100644 --- a/tensorflow/stream_executor/stream_executor_internal.h +++ b/tensorflow/stream_executor/stream_executor_internal.h @@ -237,9 +237,9 @@ class StreamExecutorInterface { virtual bool MemcpyDeviceToDevice(Stream *stream, DeviceMemoryBase *gpu_dst, const DeviceMemoryBase &gpu_src, uint64 size) = 0; - virtual bool HostCallback(Stream *stream, std::function callback) = 0; + virtual bool HostCallback(Stream *stream, std::function callback); virtual bool HostCallback(Stream *stream, - std::function callback); + std::function callback) = 0; virtual port::Status AllocateEvent(Event *event) = 0; virtual port::Status DeallocateEvent(Event *event) = 0; virtual port::Status RecordEvent(Stream *stream, Event *event) = 0; diff --git a/tensorflow/tensorflow.bzl b/tensorflow/tensorflow.bzl index 8e5ab94b53..ed1de5a31c 100644 --- a/tensorflow/tensorflow.bzl +++ b/tensorflow/tensorflow.bzl @@ -203,8 +203,12 @@ def if_override_eigen_strong_inline(a): "//conditions:default": [], }) -def if_not_tx2_llvm_or_windows_cuda(a): - return if_not_windows_cuda(a) +def if_nccl(a): + return select({ + "//tensorflow:no_nccl_support": [], + "//tensorflow:windows": [], + "//conditions:default": a, + }) def get_win_copts(is_external = False): WINDOWS_COPTS = [ @@ -1307,13 +1311,13 @@ def _py_wrap_cc_impl(ctx): ctx.outputs.py_out.dirname, ] args += ["-l" + f.path for f in ctx.files.swig_includes] - args += ["-I" + i for i in swig_include_dirs] + args += ["-I" + i for i in swig_include_dirs.to_list()] args += [src.path] outputs = [ctx.outputs.cc_out, ctx.outputs.py_out] ctx.action( executable = ctx.executable._swig, arguments = args, - inputs = list(inputs), + inputs = inputs.to_list(), outputs = outputs, mnemonic = "PythonSwig", progress_message = "SWIGing " + src.path, @@ -1493,7 +1497,7 @@ check_deps = rule( }, ) -def tf_custom_op_library(name, srcs = [], gpu_srcs = [], deps = [], linkopts = [], **kwargs): +def tf_custom_op_library(name, srcs = [], gpu_srcs = [], deps = [], linkopts = [], copts = [], **kwargs): """Helper to build a dynamic library (.so) from the sources containing implementations of custom ops and kernels. """ cuda_deps = [ @@ -1505,12 +1509,18 @@ def tf_custom_op_library(name, srcs = [], gpu_srcs = [], deps = [], linkopts = [ clean_dep("//tensorflow/core:stream_executor_headers_lib"), ] deps = deps + tf_custom_op_library_additional_deps() + + # Override EIGEN_STRONG_INLINE to inline when + # --define=override_eigen_strong_inline=true to avoid long compiling time. + # See https://github.com/tensorflow/tensorflow/issues/10521 + copts = copts + if_override_eigen_strong_inline(["/DEIGEN_STRONG_INLINE=inline"]) + if gpu_srcs: basename = name.split(".")[0] native.cc_library( name = basename + "_gpu", srcs = gpu_srcs, - copts = _cuda_copts() + if_tensorrt(["-DGOOGLE_TENSORRT=1"]), + copts = copts + _cuda_copts() + if_tensorrt(["-DGOOGLE_TENSORRT=1"]), features = if_cuda(["-use_header_modules"]), deps = deps + if_cuda_is_configured_compat(cuda_deps) + if_rocm_is_configured(rocm_deps), **kwargs @@ -1531,7 +1541,7 @@ def tf_custom_op_library(name, srcs = [], gpu_srcs = [], deps = [], linkopts = [ srcs = srcs, deps = deps + if_cuda_is_configured_compat(cuda_deps) + if_rocm_is_configured(rocm_deps), data = if_static([name + "_check_deps"]), - copts = tf_copts(is_external = True), + copts = copts + tf_copts(is_external = True), features = ["windows_export_all_symbols"], linkopts = linkopts + select({ "//conditions:default": [ @@ -2022,3 +2032,6 @@ register_extension_info( extension_name = "cc_library_with_android_deps", label_regex_for_dep = "{extension_name}", ) + +def tensorflow_opensource_extra_deps(): + return [] diff --git a/tensorflow/tools/api/golden/v1/tensorflow.-config-proto.-experimental.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.-config-proto.-experimental.pbtxt index f7491649c2..a1083d732a 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.-config-proto.-experimental.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.-config-proto.-experimental.pbtxt @@ -20,7 +20,13 @@ tf_proto { label: LABEL_OPTIONAL type: TYPE_INT32 } - reserved_range { + field { + name: "use_numa_affinity" + number: 5 + label: LABEL_OPTIONAL + type: TYPE_BOOL + } + reserved_range { start: 2 end: 3 } diff --git a/tensorflow/tools/api/golden/v1/tensorflow.-config-proto.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.-config-proto.pbtxt index 53b532beab..b505d81350 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.-config-proto.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.-config-proto.pbtxt @@ -143,6 +143,12 @@ tf_proto { label: LABEL_OPTIONAL type: TYPE_INT32 } + field { + name: "use_numa_affinity" + number: 5 + label: LABEL_OPTIONAL + type: TYPE_BOOL + } reserved_range { start: 2 end: 3 diff --git a/tensorflow/tools/api/golden/v1/tensorflow.-gradient-tape.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.-gradient-tape.pbtxt index 0a16d6ab92..2299a009d3 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.-gradient-tape.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.-gradient-tape.pbtxt @@ -6,10 +6,18 @@ tf_class { name: "__init__" argspec: "args=[\'self\', \'persistent\', \'watch_accessed_variables\'], varargs=None, keywords=None, defaults=[\'False\', \'True\'], " } + member_method { + name: "batch_jacobian" + argspec: "args=[\'self\', \'target\', \'source\', \'unconnected_gradients\', \'parallel_iterations\', \'experimental_use_pfor\'], varargs=None, keywords=None, defaults=[\'UnconnectedGradients.NONE\', \'None\', \'True\'], " + } member_method { name: "gradient" argspec: "args=[\'self\', \'target\', \'sources\', \'output_gradients\', \'unconnected_gradients\'], varargs=None, keywords=None, defaults=[\'None\', \'UnconnectedGradients.NONE\'], " } + member_method { + name: "jacobian" + argspec: "args=[\'self\', \'target\', \'sources\', \'unconnected_gradients\', \'parallel_iterations\', \'experimental_use_pfor\'], varargs=None, keywords=None, defaults=[\'UnconnectedGradients.NONE\', \'None\', \'True\'], " + } member_method { name: "reset" argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.-tensor-spec.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.-tensor-spec.pbtxt new file mode 100644 index 0000000000..493dcba892 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.-tensor-spec.pbtxt @@ -0,0 +1,33 @@ +path: "tensorflow.TensorSpec" +tf_class { + is_instance: "" + is_instance: "" + member { + name: "dtype" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "shape" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'shape\', \'dtype\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "from_spec" + argspec: "args=[\'cls\', \'spec\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "from_tensor" + argspec: "args=[\'cls\', \'tensor\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "is_compatible_with" + argspec: "args=[\'self\', \'spec_or_tensor\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.data.-dataset.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.data.-dataset.pbtxt index 8b7f63e43e..f59082baeb 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.data.-dataset.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.data.-dataset.pbtxt @@ -1,6 +1,7 @@ path: "tensorflow.data.Dataset" tf_class { - is_instance: "" + is_instance: "" + is_instance: "" is_instance: "" member { name: "output_classes" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.data.-fixed-length-record-dataset.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.data.-fixed-length-record-dataset.pbtxt index 81358cecbc..d73168b070 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.data.-fixed-length-record-dataset.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.data.-fixed-length-record-dataset.pbtxt @@ -1,8 +1,9 @@ path: "tensorflow.data.FixedLengthRecordDataset" tf_class { - is_instance: "" - is_instance: "" - is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" is_instance: "" member { name: "output_classes" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.data.-options.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.data.-options.pbtxt index 9d032d43de..72fc2c3a9e 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.data.-options.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.data.-options.pbtxt @@ -1,6 +1,7 @@ path: "tensorflow.data.Options" tf_class { is_instance: "" + is_instance: "" is_instance: "" member { name: "experimental_autotune" @@ -11,47 +12,19 @@ tf_class { mtype: "" } member { - name: "experimental_filter_fusion" - mtype: "" - } - member { - name: "experimental_hoist_random_uniform" - mtype: "" - } - member { - name: "experimental_map_and_batch_fusion" - mtype: "" - } - member { - name: "experimental_map_and_filter_fusion" - mtype: "" - } - member { - name: "experimental_map_fusion" - mtype: "" - } - member { - name: "experimental_map_parallelization" - mtype: "" - } - member { - name: "experimental_map_vectorization" - mtype: "" - } - member { - name: "experimental_noop_elimination" + name: "experimental_numa_aware" mtype: "" } member { - name: "experimental_numa_aware" + name: "experimental_optimization" mtype: "" } member { - name: "experimental_shuffle_and_repeat_fusion" + name: "experimental_stats" mtype: "" } member { - name: "experimental_stats" + name: "experimental_threading" mtype: "" } member_method { diff --git a/tensorflow/tools/api/golden/v1/tensorflow.data.-t-f-record-dataset.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.data.-t-f-record-dataset.pbtxt index 7b7a9ebaf0..51224cd6b4 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.data.-t-f-record-dataset.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.data.-t-f-record-dataset.pbtxt @@ -1,7 +1,9 @@ path: "tensorflow.data.TFRecordDataset" tf_class { - is_instance: "" - is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" is_instance: "" member { name: "output_classes" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.data.-text-line-dataset.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.data.-text-line-dataset.pbtxt index 1c305abf68..a10add1b7e 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.data.-text-line-dataset.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.data.-text-line-dataset.pbtxt @@ -1,8 +1,9 @@ path: "tensorflow.data.TextLineDataset" tf_class { - is_instance: "" - is_instance: "" - is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" is_instance: "" member { name: "output_classes" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.-csv-dataset.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.-csv-dataset.pbtxt index 2520e28a3c..71b597c19c 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.-csv-dataset.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.-csv-dataset.pbtxt @@ -1,8 +1,9 @@ path: "tensorflow.data.experimental.CsvDataset" tf_class { - is_instance: "" - is_instance: "" - is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" is_instance: "" member { name: "output_classes" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.-optimization-options.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.-optimization-options.pbtxt new file mode 100644 index 0000000000..9ca75828e5 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.-optimization-options.pbtxt @@ -0,0 +1,46 @@ +path: "tensorflow.data.experimental.OptimizationOptions" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "filter_fusion" + mtype: "" + } + member { + name: "hoist_random_uniform" + mtype: "" + } + member { + name: "map_and_batch_fusion" + mtype: "" + } + member { + name: "map_and_filter_fusion" + mtype: "" + } + member { + name: "map_fusion" + mtype: "" + } + member { + name: "map_parallelization" + mtype: "" + } + member { + name: "map_vectorization" + mtype: "" + } + member { + name: "noop_elimination" + mtype: "" + } + member { + name: "shuffle_and_repeat_fusion" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.-random-dataset.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.-random-dataset.pbtxt index 1dd53b1eab..20646e87b5 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.-random-dataset.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.-random-dataset.pbtxt @@ -1,8 +1,9 @@ path: "tensorflow.data.experimental.RandomDataset" tf_class { - is_instance: "" - is_instance: "" - is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" is_instance: "" member { name: "output_classes" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.-sql-dataset.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.-sql-dataset.pbtxt index 8fdd9dc52e..86c5ff5b0b 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.-sql-dataset.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.-sql-dataset.pbtxt @@ -1,8 +1,9 @@ path: "tensorflow.data.experimental.SqlDataset" tf_class { - is_instance: "" - is_instance: "" - is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" is_instance: "" member { name: "output_classes" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.-stats-options.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.-stats-options.pbtxt index f423eed42c..892f8c1fb8 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.-stats-options.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.-stats-options.pbtxt @@ -1,6 +1,7 @@ path: "tensorflow.data.experimental.StatsOptions" tf_class { is_instance: "" + is_instance: "" is_instance: "" member { name: "aggregator" @@ -20,6 +21,6 @@ tf_class { } member_method { name: "__init__" - argspec: "args=[\'self\', \'aggregator\'], varargs=None, keywords=None, defaults=[\'None\'], " + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" } } diff --git a/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.-threading-options.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.-threading-options.pbtxt new file mode 100644 index 0000000000..5b5ebf1080 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.-threading-options.pbtxt @@ -0,0 +1,18 @@ +path: "tensorflow.data.experimental.ThreadingOptions" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "max_intra_op_parallelism" + mtype: "" + } + member { + name: "private_threadpool_size" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.pbtxt index 4c253bb8ad..f981b1af17 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.pbtxt @@ -12,6 +12,14 @@ tf_module { name: "CsvDataset" mtype: "" } + member { + name: "INFINITE_CARDINALITY" + mtype: "" + } + member { + name: "OptimizationOptions" + mtype: "" + } member { name: "Optional" mtype: "" @@ -40,6 +48,14 @@ tf_module { name: "TFRecordWriter" mtype: "" } + member { + name: "ThreadingOptions" + mtype: "" + } + member { + name: "UNKNOWN_CARDINALITY" + mtype: "" + } member_method { name: "Counter" argspec: "args=[\'start\', \'step\', \'dtype\'], varargs=None, keywords=None, defaults=[\'0\', \'1\', \"\"], " @@ -48,6 +64,10 @@ tf_module { name: "bucket_by_sequence_length" argspec: "args=[\'element_length_func\', \'bucket_boundaries\', \'bucket_batch_sizes\', \'padded_shapes\', \'padding_values\', \'pad_to_bucket_boundary\', \'no_padding\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'False\', \'False\'], " } + member_method { + name: "cardinality" + argspec: "args=[\'dataset\'], varargs=None, keywords=None, defaults=None" + } member_method { name: "choose_from_datasets" argspec: "args=[\'datasets\', \'choice_dataset\'], varargs=None, keywords=None, defaults=None" @@ -64,6 +84,10 @@ tf_module { name: "enumerate_dataset" argspec: "args=[\'start\'], varargs=None, keywords=None, defaults=[\'0\'], " } + member_method { + name: "filter_for_shard" + argspec: "args=[\'num_shards\', \'shard_index\'], varargs=None, keywords=None, defaults=None" + } member_method { name: "get_next_as_optional" argspec: "args=[\'iterator\'], varargs=None, keywords=None, defaults=None" @@ -90,7 +114,7 @@ tf_module { } member_method { name: "make_batched_features_dataset" - argspec: "args=[\'file_pattern\', \'batch_size\', \'features\', \'reader\', \'label_key\', \'reader_args\', \'num_epochs\', \'shuffle\', \'shuffle_buffer_size\', \'shuffle_seed\', \'prefetch_buffer_size\', \'reader_num_threads\', \'parser_num_threads\', \'sloppy_ordering\', \'drop_final_batch\'], varargs=None, keywords=None, defaults=[\"\", \'None\', \'None\', \'None\', \'True\', \'10000\', \'None\', \'-1\', \'1\', \'2\', \'False\', \'False\'], " + argspec: "args=[\'file_pattern\', \'batch_size\', \'features\', \'reader\', \'label_key\', \'reader_args\', \'num_epochs\', \'shuffle\', \'shuffle_buffer_size\', \'shuffle_seed\', \'prefetch_buffer_size\', \'reader_num_threads\', \'parser_num_threads\', \'sloppy_ordering\', \'drop_final_batch\'], varargs=None, keywords=None, defaults=[\"\", \'None\', \'None\', \'None\', \'True\', \'10000\', \'None\', \'-1\', \'1\', \'2\', \'False\', \'False\'], " } member_method { name: "make_csv_dataset" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.data.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.data.pbtxt index 509bbae833..aa47468059 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.data.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.data.pbtxt @@ -28,4 +28,12 @@ tf_module { name: "experimental" mtype: "" } + member_method { + name: "make_initializable_iterator" + argspec: "args=[\'dataset\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "make_one_shot_iterator" + argspec: "args=[\'dataset\'], varargs=None, keywords=None, defaults=None" + } } diff --git a/tensorflow/tools/api/golden/v1/tensorflow.debugging.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.debugging.pbtxt index ab6287f8cd..8a7f1e9363 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.debugging.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.debugging.pbtxt @@ -78,7 +78,7 @@ tf_module { } member_method { name: "assert_scalar" - argspec: "args=[\'tensor\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + argspec: "args=[\'tensor\', \'name\', \'message\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { name: "assert_type" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.distribute.-input-context.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.distribute.-input-context.pbtxt new file mode 100644 index 0000000000..583cbc6654 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.distribute.-input-context.pbtxt @@ -0,0 +1,25 @@ +path: "tensorflow.distribute.InputContext" +tf_class { + is_instance: "" + is_instance: "" + member { + name: "input_pipeline_id" + mtype: "" + } + member { + name: "num_input_pipelines" + mtype: "" + } + member { + name: "num_replicas_in_sync" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'num_input_pipelines\', \'input_pipeline_id\', \'num_replicas_in_sync\'], varargs=None, keywords=None, defaults=[\'1\', \'0\', \'1\'], " + } + member_method { + name: "get_per_replica_batch_size" + argspec: "args=[\'self\', \'global_batch_size\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.distribute.-input-replication-mode.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.distribute.-input-replication-mode.pbtxt new file mode 100644 index 0000000000..6a7a3a97aa --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.distribute.-input-replication-mode.pbtxt @@ -0,0 +1,8 @@ +path: "tensorflow.distribute.InputReplicationMode" +tf_class { + is_instance: "" + member { + name: "PER_WORKER" + mtype: "" + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.distribute.-reduce-op.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.distribute.-reduce-op.pbtxt new file mode 100644 index 0000000000..4899f38cad --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.distribute.-reduce-op.pbtxt @@ -0,0 +1,12 @@ +path: "tensorflow.distribute.ReduceOp" +tf_class { + is_instance: "" + member { + name: "MEAN" + mtype: "" + } + member { + name: "SUM" + mtype: "" + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.distribute.-replica-context.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.distribute.-replica-context.pbtxt new file mode 100644 index 0000000000..df707e8920 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.distribute.-replica-context.pbtxt @@ -0,0 +1,33 @@ +path: "tensorflow.distribute.ReplicaContext" +tf_class { + is_instance: "" + is_instance: "" + member { + name: "devices" + mtype: "" + } + member { + name: "distribution_strategy" + mtype: "" + } + member { + name: "num_replicas_in_sync" + mtype: "" + } + member { + name: "replica_id_in_sync_group" + mtype: "" + } + member { + name: "strategy" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'strategy\', \'replica_id_in_sync_group\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "merge_call" + argspec: "args=[\'self\', \'merge_fn\', \'args\', \'kwargs\'], varargs=None, keywords=None, defaults=[\'()\', \'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.distribute.-strategy-extended.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.distribute.-strategy-extended.pbtxt new file mode 100644 index 0000000000..77706e5713 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.distribute.-strategy-extended.pbtxt @@ -0,0 +1,81 @@ +path: "tensorflow.distribute.StrategyExtended" +tf_class { + is_instance: "" + is_instance: "" + member { + name: "experimental_between_graph" + mtype: "" + } + member { + name: "experimental_require_static_shapes" + mtype: "" + } + member { + name: "experimental_should_init" + mtype: "" + } + member { + name: "parameter_devices" + mtype: "" + } + member { + name: "should_checkpoint" + mtype: "" + } + member { + name: "should_save_summary" + mtype: "" + } + member { + name: "worker_devices" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'container_strategy\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "batch_reduce_to" + argspec: "args=[\'self\', \'reduce_op\', \'value_destination_pairs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "broadcast_to" + argspec: "args=[\'self\', \'tensor\', \'destinations\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call_for_each_replica" + argspec: "args=[\'self\', \'fn\', \'args\', \'kwargs\'], varargs=None, keywords=None, defaults=[\'()\', \'None\'], " + } + member_method { + name: "colocate_vars_with" + argspec: "args=[\'self\', \'colocate_with_variable\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "experimental_run_steps_on_iterator" + argspec: "args=[\'self\', \'fn\', \'iterator\', \'iterations\', \'initial_loop_values\'], varargs=None, keywords=None, defaults=[\'1\', \'None\'], " + } + member_method { + name: "non_slot_devices" + argspec: "args=[\'self\', \'var_list\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "read_var" + argspec: "args=[\'self\', \'v\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reduce_to" + argspec: "args=[\'self\', \'reduce_op\', \'value\', \'destinations\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update" + argspec: "args=[\'self\', \'var\', \'fn\', \'args\', \'kwargs\', \'group\'], varargs=None, keywords=None, defaults=[\'()\', \'None\', \'True\'], " + } + member_method { + name: "update_non_slot" + argspec: "args=[\'self\', \'colocate_with\', \'fn\', \'args\', \'kwargs\', \'group\'], varargs=None, keywords=None, defaults=[\'()\', \'None\', \'True\'], " + } + member_method { + name: "value_container" + argspec: "args=[\'self\', \'value\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.distribute.-strategy.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.distribute.-strategy.pbtxt new file mode 100644 index 0000000000..9eb73d2c0d --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.distribute.-strategy.pbtxt @@ -0,0 +1,137 @@ +path: "tensorflow.distribute.Strategy" +tf_class { + is_instance: "" + is_instance: "" + member { + name: "between_graph" + mtype: "" + } + member { + name: "extended" + mtype: "" + } + member { + name: "num_replicas_in_sync" + mtype: "" + } + member { + name: "parameter_devices" + mtype: "" + } + member { + name: "require_static_shapes" + mtype: "" + } + member { + name: "should_checkpoint" + mtype: "" + } + member { + name: "should_init" + mtype: "" + } + member { + name: "should_save_summary" + mtype: "" + } + member { + name: "worker_devices" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'extended\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "batch_reduce" + argspec: "args=[\'self\', \'aggregation\', \'value_destination_pairs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "broadcast" + argspec: "args=[\'self\', \'tensor\', \'destinations\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "call_for_each_replica" + argspec: "args=[\'self\', \'fn\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "colocate_vars_with" + argspec: "args=[\'self\', \'colocate_with_variable\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "configure" + argspec: "args=[\'self\', \'session_config\', \'cluster_spec\', \'task_type\', \'task_id\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + } + member_method { + name: "distribute_dataset" + argspec: "args=[\'self\', \'dataset_fn\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "experimental_finalize" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "experimental_initialize" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "finalize" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "group" + argspec: "args=[\'self\', \'value\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "initialize" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "make_dataset_iterator" + argspec: "args=[\'self\', \'dataset\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "make_input_fn_iterator" + argspec: "args=[\'self\', \'input_fn\', \'replication_mode\'], varargs=None, keywords=None, defaults=[\'InputReplicationMode.PER_WORKER\'], " + } + member_method { + name: "non_slot_devices" + argspec: "args=[\'self\', \'var_list\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "read_var" + argspec: "args=[\'self\', \'v\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reduce" + argspec: "args=[\'self\', \'reduce_op\', \'value\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "run_steps_on_dataset" + argspec: "args=[\'self\', \'fn\', \'iterator\', \'iterations\', \'initial_loop_values\'], varargs=None, keywords=None, defaults=[\'1\', \'None\'], " + } + member_method { + name: "scope" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "unwrap" + argspec: "args=[\'self\', \'value\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update" + argspec: "args=[\'self\', \'var\', \'fn\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "update_config_proto" + argspec: "args=[\'self\', \'config_proto\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_non_slot" + argspec: "args=[\'self\', \'colocate_with\', \'fn\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "value_container" + argspec: "args=[\'self\', \'value\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.distribute.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.distribute.pbtxt new file mode 100644 index 0000000000..4d833b54ba --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.distribute.pbtxt @@ -0,0 +1,47 @@ +path: "tensorflow.distribute" +tf_module { + member { + name: "InputContext" + mtype: "" + } + member { + name: "InputReplicationMode" + mtype: "" + } + member { + name: "ReduceOp" + mtype: "" + } + member { + name: "ReplicaContext" + mtype: "" + } + member { + name: "Strategy" + mtype: "" + } + member { + name: "StrategyExtended" + mtype: "" + } + member_method { + name: "get_loss_reduction" + argspec: "args=[], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_replica_context" + argspec: "args=[], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_strategy" + argspec: "args=[], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "has_strategy" + argspec: "args=[], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "in_cross_replica_context" + argspec: "args=[], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-baseline-classifier.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-baseline-classifier.pbtxt index 32b84e90ce..ee3a72bfce 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-baseline-classifier.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-baseline-classifier.pbtxt @@ -2,6 +2,7 @@ path: "tensorflow.estimator.BaselineClassifier" tf_class { is_instance: "" is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -31,9 +32,13 @@ tf_class { name: "evaluate" argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } + member_method { + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + } member_method { name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "export_savedmodel" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-baseline-estimator.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-baseline-estimator.pbtxt index 94933e7ffd..38b27f735f 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-baseline-estimator.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-baseline-estimator.pbtxt @@ -2,6 +2,7 @@ path: "tensorflow.estimator.BaselineEstimator" tf_class { is_instance: "" is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -31,9 +32,13 @@ tf_class { name: "evaluate" argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } + member_method { + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + } member_method { name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "export_savedmodel" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-baseline-regressor.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-baseline-regressor.pbtxt index db7776b5bf..3874b84d5a 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-baseline-regressor.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-baseline-regressor.pbtxt @@ -2,6 +2,7 @@ path: "tensorflow.estimator.BaselineRegressor" tf_class { is_instance: "" is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -31,9 +32,13 @@ tf_class { name: "evaluate" argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } + member_method { + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + } member_method { name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "export_savedmodel" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-boosted-trees-classifier.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-boosted-trees-classifier.pbtxt index d530c71482..e138ce936e 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-boosted-trees-classifier.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-boosted-trees-classifier.pbtxt @@ -3,6 +3,7 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -32,6 +33,10 @@ tf_class { name: "evaluate" argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } + member_method { + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + } member_method { name: "experimental_feature_importances" argspec: "args=[\'self\', \'normalize\'], varargs=None, keywords=None, defaults=[\'False\'], " @@ -42,7 +47,7 @@ tf_class { } member_method { name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "export_savedmodel" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-boosted-trees-regressor.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-boosted-trees-regressor.pbtxt index 4703c0f561..eae0a292a9 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-boosted-trees-regressor.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-boosted-trees-regressor.pbtxt @@ -3,6 +3,7 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -32,6 +33,10 @@ tf_class { name: "evaluate" argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } + member_method { + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + } member_method { name: "experimental_feature_importances" argspec: "args=[\'self\', \'normalize\'], varargs=None, keywords=None, defaults=[\'False\'], " @@ -42,7 +47,7 @@ tf_class { } member_method { name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "export_savedmodel" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-d-n-n-classifier.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-d-n-n-classifier.pbtxt index ce6040d0f2..b54133b294 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-d-n-n-classifier.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-d-n-n-classifier.pbtxt @@ -2,6 +2,7 @@ path: "tensorflow.estimator.DNNClassifier" tf_class { is_instance: "" is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -31,9 +32,13 @@ tf_class { name: "evaluate" argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } + member_method { + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + } member_method { name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "export_savedmodel" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-d-n-n-estimator.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-d-n-n-estimator.pbtxt index 4635a1544c..09e0d38192 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-d-n-n-estimator.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-d-n-n-estimator.pbtxt @@ -2,6 +2,7 @@ path: "tensorflow.estimator.DNNEstimator" tf_class { is_instance: "" is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -31,9 +32,13 @@ tf_class { name: "evaluate" argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } + member_method { + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + } member_method { name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "export_savedmodel" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-d-n-n-linear-combined-classifier.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-d-n-n-linear-combined-classifier.pbtxt index e85007e16e..5a1d85a9b1 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-d-n-n-linear-combined-classifier.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-d-n-n-linear-combined-classifier.pbtxt @@ -2,6 +2,7 @@ path: "tensorflow.estimator.DNNLinearCombinedClassifier" tf_class { is_instance: "" is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -31,9 +32,13 @@ tf_class { name: "evaluate" argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } + member_method { + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + } member_method { name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "export_savedmodel" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-d-n-n-linear-combined-estimator.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-d-n-n-linear-combined-estimator.pbtxt index a23f5daeac..e311f96d3d 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-d-n-n-linear-combined-estimator.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-d-n-n-linear-combined-estimator.pbtxt @@ -2,6 +2,7 @@ path: "tensorflow.estimator.DNNLinearCombinedEstimator" tf_class { is_instance: "" is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -31,9 +32,13 @@ tf_class { name: "evaluate" argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } + member_method { + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + } member_method { name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "export_savedmodel" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-d-n-n-linear-combined-regressor.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-d-n-n-linear-combined-regressor.pbtxt index 8a55bb835f..db4780e4c0 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-d-n-n-linear-combined-regressor.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-d-n-n-linear-combined-regressor.pbtxt @@ -2,6 +2,7 @@ path: "tensorflow.estimator.DNNLinearCombinedRegressor" tf_class { is_instance: "" is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -31,9 +32,13 @@ tf_class { name: "evaluate" argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } + member_method { + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + } member_method { name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "export_savedmodel" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-d-n-n-regressor.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-d-n-n-regressor.pbtxt index 2c4128ec48..a44e719099 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-d-n-n-regressor.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-d-n-n-regressor.pbtxt @@ -2,6 +2,7 @@ path: "tensorflow.estimator.DNNRegressor" tf_class { is_instance: "" is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -31,9 +32,13 @@ tf_class { name: "evaluate" argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } + member_method { + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + } member_method { name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "export_savedmodel" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-estimator.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-estimator.pbtxt index 9d270a87ab..bff6c86cd7 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-estimator.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-estimator.pbtxt @@ -1,6 +1,7 @@ path: "tensorflow.estimator.Estimator" tf_class { is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -30,9 +31,13 @@ tf_class { name: "evaluate" argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } + member_method { + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + } member_method { name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "export_savedmodel" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-linear-classifier.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-linear-classifier.pbtxt index 4b5de2e245..2c8e82517b 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-linear-classifier.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-linear-classifier.pbtxt @@ -2,6 +2,7 @@ path: "tensorflow.estimator.LinearClassifier" tf_class { is_instance: "" is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -31,9 +32,13 @@ tf_class { name: "evaluate" argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } + member_method { + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + } member_method { name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "export_savedmodel" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-linear-estimator.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-linear-estimator.pbtxt index 3d6b03098a..2148374fde 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-linear-estimator.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-linear-estimator.pbtxt @@ -2,6 +2,7 @@ path: "tensorflow.estimator.LinearEstimator" tf_class { is_instance: "" is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -31,9 +32,13 @@ tf_class { name: "evaluate" argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } + member_method { + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + } member_method { name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "export_savedmodel" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-linear-regressor.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-linear-regressor.pbtxt index 0d1510e9ab..1bdc6124fe 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.estimator.-linear-regressor.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.estimator.-linear-regressor.pbtxt @@ -2,6 +2,7 @@ path: "tensorflow.estimator.LinearRegressor" tf_class { is_instance: "" is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -31,9 +32,13 @@ tf_class { name: "evaluate" argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } + member_method { + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + } member_method { name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "export_savedmodel" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-profiler-hook.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.estimator.experimental.-in-memory-evaluator-hook.pbtxt similarity index 71% rename from tensorflow/tools/api/golden/v2/tensorflow.train.-profiler-hook.pbtxt rename to tensorflow/tools/api/golden/v1/tensorflow.estimator.experimental.-in-memory-evaluator-hook.pbtxt index 4df6c4156a..aba120218c 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-profiler-hook.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.estimator.experimental.-in-memory-evaluator-hook.pbtxt @@ -1,11 +1,11 @@ -path: "tensorflow.train.ProfilerHook" +path: "tensorflow.estimator.experimental.InMemoryEvaluatorHook" tf_class { - is_instance: "" + is_instance: "" is_instance: "" is_instance: "" member_method { name: "__init__" - argspec: "args=[\'self\', \'save_steps\', \'save_secs\', \'output_dir\', \'show_dataflow\', \'show_memory\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'\', \'True\', \'False\'], " + argspec: "args=[\'self\', \'estimator\', \'input_fn\', \'steps\', \'hooks\', \'name\', \'every_n_iter\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'100\'], " } member_method { name: "after_create_session" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.estimator.experimental.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.estimator.experimental.pbtxt index cabca3e883..f0fd7ce782 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.estimator.experimental.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.estimator.experimental.pbtxt @@ -1,9 +1,17 @@ path: "tensorflow.estimator.experimental" tf_module { + member { + name: "InMemoryEvaluatorHook" + mtype: "" + } member { name: "LinearSDCA" mtype: "" } + member_method { + name: "build_raw_supervised_input_receiver_fn" + argspec: "args=[\'features\', \'labels\', \'default_batch_size\'], varargs=None, keywords=None, defaults=[\'None\'], " + } member_method { name: "call_logit_fn" argspec: "args=[\'logit_fn\', \'features\', \'mode\', \'params\', \'config\'], varargs=None, keywords=None, defaults=None" @@ -20,6 +28,10 @@ tf_module { name: "make_early_stopping_hook" argspec: "args=[\'estimator\', \'should_stop_fn\', \'run_every_secs\', \'run_every_steps\'], varargs=None, keywords=None, defaults=[\'60\', \'None\'], " } + member_method { + name: "make_stop_at_checkpoint_step_hook" + argspec: "args=[\'estimator\', \'last_step\', \'wait_after_file_check_secs\'], varargs=None, keywords=None, defaults=[\'30\'], " + } member_method { name: "stop_if_higher_hook" argspec: "args=[\'estimator\', \'metric_name\', \'threshold\', \'eval_dir\', \'min_steps\', \'run_every_secs\', \'run_every_steps\'], varargs=None, keywords=None, defaults=[\'None\', \'0\', \'60\', \'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.image.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.image.pbtxt index 0a231f1b65..15d0e099ba 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.image.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.image.pbtxt @@ -172,6 +172,10 @@ tf_module { name: "random_saturation" argspec: "args=[\'image\', \'lower\', \'upper\', \'seed\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "resize" + argspec: "args=[\'images\', \'size\', \'method\', \'align_corners\', \'preserve_aspect_ratio\'], varargs=None, keywords=None, defaults=[\'0\', \'False\', \'False\'], " + } member_method { name: "resize_area" argspec: "args=[\'images\', \'size\', \'align_corners\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'None\'], " @@ -240,6 +244,10 @@ tf_module { name: "total_variation" argspec: "args=[\'images\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "transpose" + argspec: "args=[\'image\'], varargs=None, keywords=None, defaults=None" + } member_method { name: "transpose_image" argspec: "args=[\'image\'], varargs=None, keywords=None, defaults=None" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.io.gfile.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.io.gfile.pbtxt new file mode 100644 index 0000000000..93d9b0fd75 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.io.gfile.pbtxt @@ -0,0 +1,51 @@ +path: "tensorflow.io.gfile" +tf_module { + member_method { + name: "copy" + argspec: "args=[\'src\', \'dst\', \'overwrite\'], varargs=None, keywords=None, defaults=[\'False\'], " + } + member_method { + name: "exists" + argspec: "args=[\'path\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "glob" + argspec: "args=[\'pattern\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "isdir" + argspec: "args=[\'path\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "listdir" + argspec: "args=[\'path\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "makedirs" + argspec: "args=[\'path\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "mkdir" + argspec: "args=[\'path\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "remove" + argspec: "args=[\'path\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "rename" + argspec: "args=[\'src\', \'dst\', \'overwrite\'], varargs=None, keywords=None, defaults=[\'False\'], " + } + member_method { + name: "rmtree" + argspec: "args=[\'path\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "stat" + argspec: "args=[\'path\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "walk" + argspec: "args=[\'top\', \'topdown\', \'onerror\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.io.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.io.pbtxt index 64b63ed1a4..b760ec3890 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.io.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.io.pbtxt @@ -44,10 +44,22 @@ tf_module { name: "VarLenFeature" mtype: "" } + member { + name: "gfile" + mtype: "" + } + member_method { + name: "decode_and_crop_jpeg" + argspec: "args=[\'contents\', \'crop_window\', \'channels\', \'ratio\', \'fancy_upscaling\', \'try_recover_truncated\', \'acceptable_fraction\', \'dct_method\', \'name\'], varargs=None, keywords=None, defaults=[\'0\', \'1\', \'True\', \'False\', \'1\', \'\', \'None\'], " + } member_method { name: "decode_base64" argspec: "args=[\'input\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "decode_bmp" + argspec: "args=[\'contents\', \'channels\', \'name\'], varargs=None, keywords=None, defaults=[\'0\', \'None\'], " + } member_method { name: "decode_compressed" argspec: "args=[\'bytes\', \'compression_type\', \'name\'], varargs=None, keywords=None, defaults=[\'\', \'None\'], " @@ -56,10 +68,26 @@ tf_module { name: "decode_csv" argspec: "args=[\'records\', \'record_defaults\', \'field_delim\', \'use_quote_delim\', \'name\', \'na_value\', \'select_cols\'], varargs=None, keywords=None, defaults=[\',\', \'True\', \'None\', \'\', \'None\'], " } + member_method { + name: "decode_gif" + argspec: "args=[\'contents\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "decode_image" + argspec: "args=[\'contents\', \'channels\', \'dtype\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \"\", \'None\'], " + } + member_method { + name: "decode_jpeg" + argspec: "args=[\'contents\', \'channels\', \'ratio\', \'fancy_upscaling\', \'try_recover_truncated\', \'acceptable_fraction\', \'dct_method\', \'name\'], varargs=None, keywords=None, defaults=[\'0\', \'1\', \'True\', \'False\', \'1\', \'\', \'None\'], " + } member_method { name: "decode_json_example" argspec: "args=[\'json_examples\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "decode_png" + argspec: "args=[\'contents\', \'channels\', \'dtype\', \'name\'], varargs=None, keywords=None, defaults=[\'0\', \"\", \'None\'], " + } member_method { name: "decode_raw" argspec: "args=[\'bytes\', \'out_type\', \'little_endian\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'None\'], " @@ -72,6 +100,18 @@ tf_module { name: "encode_base64" argspec: "args=[\'input\', \'pad\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'None\'], " } + member_method { + name: "encode_jpeg" + argspec: "args=[\'image\', \'format\', \'quality\', \'progressive\', \'optimize_size\', \'chroma_downsampling\', \'density_unit\', \'x_density\', \'y_density\', \'xmp_metadata\', \'name\'], varargs=None, keywords=None, defaults=[\'\', \'95\', \'False\', \'False\', \'True\', \'in\', \'300\', \'300\', \'\', \'None\'], " + } + member_method { + name: "extract_jpeg_shape" + argspec: "args=[\'contents\', \'output_type\', \'name\'], varargs=None, keywords=None, defaults=[\"\", \'None\'], " + } + member_method { + name: "is_jpeg" + argspec: "args=[\'contents\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } member_method { name: "match_filenames_once" argspec: "args=[\'pattern\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.-model.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.-model.pbtxt index 8ccba990bd..a3254cbd94 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.-model.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.-model.pbtxt @@ -41,6 +41,14 @@ tf_class { name: "losses" mtype: "" } + member { + name: "metrics" + mtype: "" + } + member { + name: "metrics_names" + mtype: "" + } member { name: "name" mtype: "" @@ -69,6 +77,10 @@ tf_class { name: "output_shape" mtype: "" } + member { + name: "run_eagerly" + mtype: "" + } member { name: "state_updates" mtype: "" @@ -105,13 +117,17 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { name: "add_variable" - argspec: "args=[\'self\', \'name\', \'shape\', \'dtype\', \'initializer\', \'regularizer\', \'trainable\', \'constraint\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'True\', \'None\'], " + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" } member_method { name: "add_weight" @@ -225,6 +241,10 @@ tf_class { name: "predict_on_batch" argspec: "args=[\'self\', \'x\'], varargs=None, keywords=None, defaults=None" } + member_method { + name: "reset_metrics" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } member_method { name: "reset_states" argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" @@ -247,7 +267,7 @@ tf_class { } member_method { name: "test_on_batch" - argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\', \'reset_metrics\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'True\'], " } member_method { name: "to_json" @@ -259,6 +279,6 @@ tf_class { } member_method { name: "train_on_batch" - argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\', \'class_weight\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\', \'class_weight\', \'reset_metrics\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'True\'], " } } diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.-sequential.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.-sequential.pbtxt index 27aa91a645..b70e9ee98d 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.-sequential.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.-sequential.pbtxt @@ -42,6 +42,14 @@ tf_class { name: "losses" mtype: "" } + member { + name: "metrics" + mtype: "" + } + member { + name: "metrics_names" + mtype: "" + } member { name: "name" mtype: "" @@ -70,6 +78,10 @@ tf_class { name: "output_shape" mtype: "" } + member { + name: "run_eagerly" + mtype: "" + } member { name: "state_updates" mtype: "" @@ -110,13 +122,17 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { name: "add_variable" - argspec: "args=[\'self\', \'name\', \'shape\', \'dtype\', \'initializer\', \'regularizer\', \'trainable\', \'constraint\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'True\', \'None\'], " + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" } member_method { name: "add_weight" @@ -242,6 +258,10 @@ tf_class { name: "predict_proba" argspec: "args=[\'self\', \'x\', \'batch_size\', \'verbose\'], varargs=None, keywords=None, defaults=[\'32\', \'0\'], " } + member_method { + name: "reset_metrics" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } member_method { name: "reset_states" argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" @@ -264,7 +284,7 @@ tf_class { } member_method { name: "test_on_batch" - argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\', \'reset_metrics\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'True\'], " } member_method { name: "to_json" @@ -276,6 +296,6 @@ tf_class { } member_method { name: "train_on_batch" - argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\', \'class_weight\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\', \'class_weight\', \'reset_metrics\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'True\'], " } } diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.backend.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.backend.pbtxt index b0e5d2bde7..8cd0c6ea5f 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.backend.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.backend.pbtxt @@ -398,7 +398,7 @@ tf_module { } member_method { name: "rnn" - argspec: "args=[\'step_function\', \'inputs\', \'initial_states\', \'go_backwards\', \'mask\', \'constants\', \'unroll\', \'input_length\', \'time_major\'], varargs=None, keywords=None, defaults=[\'False\', \'None\', \'None\', \'False\', \'None\', \'False\'], " + argspec: "args=[\'step_function\', \'inputs\', \'initial_states\', \'go_backwards\', \'mask\', \'constants\', \'unroll\', \'input_length\', \'time_major\', \'zero_output_for_mask\'], varargs=None, keywords=None, defaults=[\'False\', \'None\', \'None\', \'False\', \'None\', \'False\', \'False\'], " } member_method { name: "round" @@ -512,6 +512,10 @@ tf_module { name: "temporal_padding" argspec: "args=[\'x\', \'padding\'], varargs=None, keywords=None, defaults=[\'(1, 1)\'], " } + member_method { + name: "tile" + argspec: "args=[\'x\', \'n\'], varargs=None, keywords=None, defaults=None" + } member_method { name: "to_dense" argspec: "args=[\'tensor\'], varargs=None, keywords=None, defaults=None" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.experimental.-peephole-l-s-t-m-cell.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.experimental.-peephole-l-s-t-m-cell.pbtxt index db69e25c5b..1d814b2c8b 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.experimental.-peephole-l-s-t-m-cell.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.experimental.-peephole-l-s-t-m-cell.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-activation.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-activation.pbtxt index 5510465d7b..b84629540e 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-activation.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-activation.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-activity-regularization.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-activity-regularization.pbtxt index 38ec8a0aff..5918a13ad8 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-activity-regularization.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-activity-regularization.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-add.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-add.pbtxt index 41cb8e30bf..599da06427 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-add.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-add.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-alpha-dropout.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-alpha-dropout.pbtxt index 9a7aaa8e96..f9ff1538c8 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-alpha-dropout.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-alpha-dropout.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-average-pooling1-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-average-pooling1-d.pbtxt index 014f5828fa..723fc9cdb0 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-average-pooling1-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-average-pooling1-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-average-pooling2-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-average-pooling2-d.pbtxt index cc303bf7b9..957ce2f0ce 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-average-pooling2-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-average-pooling2-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-average-pooling3-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-average-pooling3-d.pbtxt index 628447ce35..a52c0af681 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-average-pooling3-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-average-pooling3-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-average.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-average.pbtxt index f03c986c22..a004db62dd 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-average.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-average.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-avg-pool1-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-avg-pool1-d.pbtxt index a6e4856de9..44f83d1387 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-avg-pool1-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-avg-pool1-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-avg-pool2-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-avg-pool2-d.pbtxt index a01eaf8a12..8378faf718 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-avg-pool2-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-avg-pool2-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-avg-pool3-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-avg-pool3-d.pbtxt index 0d6698f2ef..9d5655c964 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-avg-pool3-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-avg-pool3-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-batch-normalization.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-batch-normalization.pbtxt index f1b23be48f..b3d3c84f92 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-batch-normalization.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-batch-normalization.pbtxt @@ -1,6 +1,7 @@ path: "tensorflow.keras.layers.BatchNormalization" tf_class { - is_instance: "" + is_instance: "" + is_instance: "" is_instance: "" is_instance: "" is_instance: "" @@ -88,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-bidirectional.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-bidirectional.pbtxt index 0672cd5b7b..d37a6b4710 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-bidirectional.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-bidirectional.pbtxt @@ -97,6 +97,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-concatenate.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-concatenate.pbtxt index b25ae1e82e..1ad7a91be0 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-concatenate.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-concatenate.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-conv-l-s-t-m2-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-conv-l-s-t-m2-d.pbtxt index bb1918eba6..cb9abc2539 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-conv-l-s-t-m2-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-conv-l-s-t-m2-d.pbtxt @@ -178,6 +178,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-conv1-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-conv1-d.pbtxt index 16e0fd5a31..47dba1d81f 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-conv1-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-conv1-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-conv2-d-transpose.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-conv2-d-transpose.pbtxt index 381839d6de..fd64941896 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-conv2-d-transpose.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-conv2-d-transpose.pbtxt @@ -90,6 +90,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-conv2-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-conv2-d.pbtxt index 543bae6fa9..1b1425d531 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-conv2-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-conv2-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-conv3-d-transpose.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-conv3-d-transpose.pbtxt index 2933f9f4b3..1741063fe8 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-conv3-d-transpose.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-conv3-d-transpose.pbtxt @@ -90,6 +90,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-conv3-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-conv3-d.pbtxt index 072943dc2c..50feb4f458 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-conv3-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-conv3-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-convolution1-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-convolution1-d.pbtxt index 222a1ef4fc..faaa535df9 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-convolution1-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-convolution1-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-convolution2-d-transpose.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-convolution2-d-transpose.pbtxt index 9c9c7461c8..4079329d1e 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-convolution2-d-transpose.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-convolution2-d-transpose.pbtxt @@ -90,6 +90,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-convolution2-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-convolution2-d.pbtxt index f939067178..32e56696e1 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-convolution2-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-convolution2-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-convolution3-d-transpose.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-convolution3-d-transpose.pbtxt index 44ca598724..381abe7340 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-convolution3-d-transpose.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-convolution3-d-transpose.pbtxt @@ -90,6 +90,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-convolution3-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-convolution3-d.pbtxt index 471b18ef85..b3e4bf9689 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-convolution3-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-convolution3-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-cropping1-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-cropping1-d.pbtxt index 0f250a09b7..7aeff8003c 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-cropping1-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-cropping1-d.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-cropping2-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-cropping2-d.pbtxt index f52128483c..a1728d9d4f 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-cropping2-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-cropping2-d.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-cropping3-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-cropping3-d.pbtxt index 98daf3bab1..8d8fd142cc 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-cropping3-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-cropping3-d.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-cu-d-n-n-g-r-u.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-cu-d-n-n-g-r-u.pbtxt index b207c68000..7758209adf 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-cu-d-n-n-g-r-u.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-cu-d-n-n-g-r-u.pbtxt @@ -98,6 +98,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-cu-d-n-n-l-s-t-m.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-cu-d-n-n-l-s-t-m.pbtxt index 2d7a09ceda..7c463ff125 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-cu-d-n-n-l-s-t-m.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-cu-d-n-n-l-s-t-m.pbtxt @@ -98,6 +98,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-dense.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-dense.pbtxt index 3ac3825759..4960d0264e 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-dense.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-dense.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-depthwise-conv2-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-depthwise-conv2-d.pbtxt index 280ec8c25f..8fad7535f8 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-depthwise-conv2-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-depthwise-conv2-d.pbtxt @@ -90,6 +90,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-dot.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-dot.pbtxt index 560f66f9c7..5b425f2d4d 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-dot.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-dot.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-dropout.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-dropout.pbtxt index c0543529c3..f6c4d0a438 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-dropout.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-dropout.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-e-l-u.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-e-l-u.pbtxt index 04eb2824b9..82b761fc17 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-e-l-u.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-e-l-u.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-embedding.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-embedding.pbtxt index f400432915..c9ff323877 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-embedding.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-embedding.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-flatten.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-flatten.pbtxt index ab176b441a..9b4165d4cb 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-flatten.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-flatten.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-g-r-u-cell.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-g-r-u-cell.pbtxt index c3895a0ac1..f225f7c430 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-g-r-u-cell.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-g-r-u-cell.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-g-r-u.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-g-r-u.pbtxt index 9e24bb8ae6..855d001700 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-g-r-u.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-g-r-u.pbtxt @@ -161,6 +161,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-gaussian-dropout.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-gaussian-dropout.pbtxt index 55e0d7ef02..2c404c99cd 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-gaussian-dropout.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-gaussian-dropout.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-gaussian-noise.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-gaussian-noise.pbtxt index 38fbff5e4a..6f109d59d0 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-gaussian-noise.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-gaussian-noise.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-average-pooling1-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-average-pooling1-d.pbtxt index a8094c0bde..69f8a9031d 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-average-pooling1-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-average-pooling1-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-average-pooling2-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-average-pooling2-d.pbtxt index 929f48df23..4299f765e5 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-average-pooling2-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-average-pooling2-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-average-pooling3-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-average-pooling3-d.pbtxt index 2e6d59337f..9153a1a240 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-average-pooling3-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-average-pooling3-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-avg-pool1-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-avg-pool1-d.pbtxt index 3ebe162f57..625e81fd23 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-avg-pool1-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-avg-pool1-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-avg-pool2-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-avg-pool2-d.pbtxt index 4e3e258430..2fc769742c 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-avg-pool2-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-avg-pool2-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-avg-pool3-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-avg-pool3-d.pbtxt index fb9166316f..e307a65c7c 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-avg-pool3-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-avg-pool3-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-max-pool1-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-max-pool1-d.pbtxt index c0a53b847b..4394ad0364 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-max-pool1-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-max-pool1-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-max-pool2-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-max-pool2-d.pbtxt index 87b7f6797a..050ed39fe9 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-max-pool2-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-max-pool2-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-max-pool3-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-max-pool3-d.pbtxt index 98bf96fa0c..436191821e 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-max-pool3-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-max-pool3-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-max-pooling1-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-max-pooling1-d.pbtxt index ff6c6f3ec4..4ba540aa6a 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-max-pooling1-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-max-pooling1-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-max-pooling2-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-max-pooling2-d.pbtxt index c9d4158d1c..a2e9322cb3 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-max-pooling2-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-max-pooling2-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-max-pooling3-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-max-pooling3-d.pbtxt index 9953102ff9..5d16a57fc1 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-max-pooling3-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-global-max-pooling3-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-input-layer.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-input-layer.pbtxt index 2617f5a95f..9dd29c1251 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-input-layer.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-input-layer.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-input-spec.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-input-spec.pbtxt index 5fd0a47a68..bc3ceb67a4 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-input-spec.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-input-spec.pbtxt @@ -1,6 +1,6 @@ path: "tensorflow.keras.layers.InputSpec" tf_class { - is_instance: "" + is_instance: "" is_instance: "" member_method { name: "__init__" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-l-s-t-m-cell.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-l-s-t-m-cell.pbtxt index e9f6ef45aa..0045d5775e 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-l-s-t-m-cell.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-l-s-t-m-cell.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-l-s-t-m.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-l-s-t-m.pbtxt index 1b1ccbe118..529c750f98 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-l-s-t-m.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-l-s-t-m.pbtxt @@ -161,6 +161,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-lambda.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-lambda.pbtxt index 2e0b6bac24..d4d1bc6b6b 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-lambda.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-lambda.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-layer.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-layer.pbtxt index 1e93d1118a..e1f5491180 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-layer.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-layer.pbtxt @@ -87,6 +87,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-leaky-re-l-u.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-leaky-re-l-u.pbtxt index bfd36012a7..9b69d9a944 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-leaky-re-l-u.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-leaky-re-l-u.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-locally-connected1-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-locally-connected1-d.pbtxt index 5ad5990d7e..fd52259432 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-locally-connected1-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-locally-connected1-d.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-locally-connected2-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-locally-connected2-d.pbtxt index 40d03369a5..5fc8af0d03 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-locally-connected2-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-locally-connected2-d.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-masking.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-masking.pbtxt index 86666b51bb..7f8932270e 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-masking.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-masking.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-max-pool1-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-max-pool1-d.pbtxt index d26da270e7..4723b99cb0 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-max-pool1-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-max-pool1-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-max-pool2-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-max-pool2-d.pbtxt index 85f23df671..173c5d4a8b 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-max-pool2-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-max-pool2-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-max-pool3-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-max-pool3-d.pbtxt index 235806b965..14e1899e14 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-max-pool3-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-max-pool3-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-max-pooling1-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-max-pooling1-d.pbtxt index 524c5fd69e..a708e652bf 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-max-pooling1-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-max-pooling1-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-max-pooling2-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-max-pooling2-d.pbtxt index fda2562fc8..e6706b5cf9 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-max-pooling2-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-max-pooling2-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-max-pooling3-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-max-pooling3-d.pbtxt index 71d2d09a8d..a73c082d1b 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-max-pooling3-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-max-pooling3-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-maximum.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-maximum.pbtxt index 12949b39a6..f3f195554b 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-maximum.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-maximum.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-minimum.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-minimum.pbtxt index ab16d0021e..f345d1d67b 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-minimum.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-minimum.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-multiply.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-multiply.pbtxt index 61ccbf5962..31cb8bc177 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-multiply.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-multiply.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-p-re-l-u.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-p-re-l-u.pbtxt index ce2320d703..44cccc92bd 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-p-re-l-u.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-p-re-l-u.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-permute.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-permute.pbtxt index 69848af8cf..b55e191ff1 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-permute.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-permute.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-r-n-n.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-r-n-n.pbtxt index 3358f26aeb..e9575436e5 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-r-n-n.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-r-n-n.pbtxt @@ -92,6 +92,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-re-l-u.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-re-l-u.pbtxt index 413f45f018..98223b207f 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-re-l-u.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-re-l-u.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-repeat-vector.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-repeat-vector.pbtxt index 9c61ff6027..2df918b16b 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-repeat-vector.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-repeat-vector.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-reshape.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-reshape.pbtxt index baa91804c4..ce5f9e2129 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-reshape.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-reshape.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-separable-conv1-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-separable-conv1-d.pbtxt index 15a5d6ac9e..a0bb917775 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-separable-conv1-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-separable-conv1-d.pbtxt @@ -90,6 +90,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-separable-conv2-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-separable-conv2-d.pbtxt index be43bd5b3c..d7942f201b 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-separable-conv2-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-separable-conv2-d.pbtxt @@ -90,6 +90,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-separable-convolution1-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-separable-convolution1-d.pbtxt index 6105992c7a..f7ac9042d4 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-separable-convolution1-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-separable-convolution1-d.pbtxt @@ -90,6 +90,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-separable-convolution2-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-separable-convolution2-d.pbtxt index 1b6cf1e9ec..e5a9268822 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-separable-convolution2-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-separable-convolution2-d.pbtxt @@ -90,6 +90,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-simple-r-n-n-cell.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-simple-r-n-n-cell.pbtxt index 29488a37f8..0fe2c974a7 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-simple-r-n-n-cell.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-simple-r-n-n-cell.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-simple-r-n-n.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-simple-r-n-n.pbtxt index 3d70cf8b65..2ee5873f0f 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-simple-r-n-n.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-simple-r-n-n.pbtxt @@ -149,6 +149,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-softmax.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-softmax.pbtxt index d29731ecf9..5b8f64aa35 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-softmax.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-softmax.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-spatial-dropout1-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-spatial-dropout1-d.pbtxt index a6d7494ca7..240cb6e562 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-spatial-dropout1-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-spatial-dropout1-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-spatial-dropout2-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-spatial-dropout2-d.pbtxt index c36e802693..6226c469f8 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-spatial-dropout2-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-spatial-dropout2-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-spatial-dropout3-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-spatial-dropout3-d.pbtxt index 9c46cfe40f..34dabce6d8 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-spatial-dropout3-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-spatial-dropout3-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-stacked-r-n-n-cells.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-stacked-r-n-n-cells.pbtxt index 8982f78794..0ddf628ace 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-stacked-r-n-n-cells.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-stacked-r-n-n-cells.pbtxt @@ -96,6 +96,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-subtract.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-subtract.pbtxt index ec2cc50298..12eb35ad15 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-subtract.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-subtract.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-thresholded-re-l-u.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-thresholded-re-l-u.pbtxt index d7bc1980f3..c41020c2b4 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-thresholded-re-l-u.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-thresholded-re-l-u.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-time-distributed.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-time-distributed.pbtxt index fec2de6b49..479f89cf6a 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-time-distributed.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-time-distributed.pbtxt @@ -93,6 +93,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-up-sampling1-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-up-sampling1-d.pbtxt index 3d285e7f17..233363ce02 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-up-sampling1-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-up-sampling1-d.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-up-sampling2-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-up-sampling2-d.pbtxt index b05e5ec84d..cb6228ac44 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-up-sampling2-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-up-sampling2-d.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-up-sampling3-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-up-sampling3-d.pbtxt index 728eca415a..03bad3ccb6 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-up-sampling3-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-up-sampling3-d.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-wrapper.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-wrapper.pbtxt index da64e77c39..158996792a 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-wrapper.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-wrapper.pbtxt @@ -92,6 +92,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-zero-padding1-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-zero-padding1-d.pbtxt index 2f505f9293..63a56cd3ee 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-zero-padding1-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-zero-padding1-d.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-zero-padding2-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-zero-padding2-d.pbtxt index f82c77072e..965a4cca04 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-zero-padding2-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-zero-padding2-d.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-zero-padding3-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-zero-padding3-d.pbtxt index 54e01a9917..1a62430887 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-zero-padding3-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.layers.-zero-padding3-d.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-binary-crossentropy.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-binary-crossentropy.pbtxt new file mode 100644 index 0000000000..2f7da93f6f --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-binary-crossentropy.pbtxt @@ -0,0 +1,22 @@ +path: "tensorflow.keras.losses.BinaryCrossentropy" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member_method { + name: "__init__" + argspec: "args=[\'self\', \'from_logits\', \'label_smoothing\', \'reduction\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'0\', \'sum_over_batch_size\', \'None\'], " + } + member_method { + name: "call" + argspec: "args=[\'self\', \'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-categorical-crossentropy.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-categorical-crossentropy.pbtxt new file mode 100644 index 0000000000..b3a7cd8097 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-categorical-crossentropy.pbtxt @@ -0,0 +1,22 @@ +path: "tensorflow.keras.losses.CategoricalCrossentropy" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member_method { + name: "__init__" + argspec: "args=[\'self\', \'from_logits\', \'label_smoothing\', \'reduction\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'0\', \'sum_over_batch_size\', \'None\'], " + } + member_method { + name: "call" + argspec: "args=[\'self\', \'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-mean-absolute-error.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-mean-absolute-error.pbtxt new file mode 100644 index 0000000000..712bb2ecd3 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-mean-absolute-error.pbtxt @@ -0,0 +1,22 @@ +path: "tensorflow.keras.losses.MeanAbsoluteError" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member_method { + name: "__init__" + argspec: "args=[\'self\', \'reduction\', \'name\'], varargs=None, keywords=None, defaults=[\'sum_over_batch_size\', \'None\'], " + } + member_method { + name: "call" + argspec: "args=[\'self\', \'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-mean-absolute-percentage-error.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-mean-absolute-percentage-error.pbtxt new file mode 100644 index 0000000000..7fe362da89 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-mean-absolute-percentage-error.pbtxt @@ -0,0 +1,22 @@ +path: "tensorflow.keras.losses.MeanAbsolutePercentageError" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member_method { + name: "__init__" + argspec: "args=[\'self\', \'reduction\', \'name\'], varargs=None, keywords=None, defaults=[\'sum_over_batch_size\', \'None\'], " + } + member_method { + name: "call" + argspec: "args=[\'self\', \'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-mean-squared-error.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-mean-squared-error.pbtxt new file mode 100644 index 0000000000..a571853350 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-mean-squared-error.pbtxt @@ -0,0 +1,22 @@ +path: "tensorflow.keras.losses.MeanSquaredError" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member_method { + name: "__init__" + argspec: "args=[\'self\', \'reduction\', \'name\'], varargs=None, keywords=None, defaults=[\'sum_over_batch_size\', \'None\'], " + } + member_method { + name: "call" + argspec: "args=[\'self\', \'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-mean-squared-logarithmic-error.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-mean-squared-logarithmic-error.pbtxt new file mode 100644 index 0000000000..200006db35 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-mean-squared-logarithmic-error.pbtxt @@ -0,0 +1,22 @@ +path: "tensorflow.keras.losses.MeanSquaredLogarithmicError" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member_method { + name: "__init__" + argspec: "args=[\'self\', \'reduction\', \'name\'], varargs=None, keywords=None, defaults=[\'sum_over_batch_size\', \'None\'], " + } + member_method { + name: "call" + argspec: "args=[\'self\', \'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.pbtxt index eca6b91538..9e26ddbdca 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.pbtxt @@ -1,5 +1,29 @@ path: "tensorflow.keras.losses" tf_module { + member { + name: "BinaryCrossentropy" + mtype: "" + } + member { + name: "CategoricalCrossentropy" + mtype: "" + } + member { + name: "MeanAbsoluteError" + mtype: "" + } + member { + name: "MeanAbsolutePercentageError" + mtype: "" + } + member { + name: "MeanSquaredError" + mtype: "" + } + member { + name: "MeanSquaredLogarithmicError" + mtype: "" + } member_method { name: "KLD" argspec: "args=[\'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" @@ -22,11 +46,11 @@ tf_module { } member_method { name: "binary_crossentropy" - argspec: "args=[\'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + argspec: "args=[\'y_true\', \'y_pred\', \'from_logits\'], varargs=None, keywords=None, defaults=[\'False\'], " } member_method { name: "categorical_crossentropy" - argspec: "args=[\'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + argspec: "args=[\'y_true\', \'y_pred\', \'from_logits\'], varargs=None, keywords=None, defaults=[\'False\'], " } member_method { name: "categorical_hinge" @@ -106,7 +130,7 @@ tf_module { } member_method { name: "sparse_categorical_crossentropy" - argspec: "args=[\'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + argspec: "args=[\'y_true\', \'y_pred\', \'from_logits\'], varargs=None, keywords=None, defaults=[\'False\'], " } member_method { name: "squared_hinge" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-accuracy.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-accuracy.pbtxt new file mode 100644 index 0000000000..2db07df523 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-accuracy.pbtxt @@ -0,0 +1,194 @@ +path: "tensorflow.keras.metrics.Accuracy" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'accuracy\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-binary-accuracy.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-binary-accuracy.pbtxt new file mode 100644 index 0000000000..904ad3a21a --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-binary-accuracy.pbtxt @@ -0,0 +1,194 @@ +path: "tensorflow.keras.metrics.BinaryAccuracy" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\', \'threshold\'], varargs=None, keywords=None, defaults=[\'binary_accuracy\', \'None\', \'0.5\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-categorical-accuracy.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-categorical-accuracy.pbtxt new file mode 100644 index 0000000000..17b74924fa --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-categorical-accuracy.pbtxt @@ -0,0 +1,194 @@ +path: "tensorflow.keras.metrics.CategoricalAccuracy" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'categorical_accuracy\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-false-negatives.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-false-negatives.pbtxt new file mode 100644 index 0000000000..49f577e136 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-false-negatives.pbtxt @@ -0,0 +1,193 @@ +path: "tensorflow.keras.metrics.FalseNegatives" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'thresholds\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-false-positives.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-false-positives.pbtxt new file mode 100644 index 0000000000..e8baf85866 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-false-positives.pbtxt @@ -0,0 +1,193 @@ +path: "tensorflow.keras.metrics.FalsePositives" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'thresholds\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-mean.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-mean.pbtxt new file mode 100644 index 0000000000..40fe64bbd2 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-mean.pbtxt @@ -0,0 +1,192 @@ +path: "tensorflow.keras.metrics.Mean" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'mean\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'values\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-precision.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-precision.pbtxt new file mode 100644 index 0000000000..ae6a85026d --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-precision.pbtxt @@ -0,0 +1,192 @@ +path: "tensorflow.keras.metrics.Precision" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'thresholds\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-recall.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-recall.pbtxt new file mode 100644 index 0000000000..31068a51d5 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-recall.pbtxt @@ -0,0 +1,192 @@ +path: "tensorflow.keras.metrics.Recall" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'thresholds\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-sparse-categorical-accuracy.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-sparse-categorical-accuracy.pbtxt new file mode 100644 index 0000000000..0c17452292 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-sparse-categorical-accuracy.pbtxt @@ -0,0 +1,194 @@ +path: "tensorflow.keras.metrics.SparseCategoricalAccuracy" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'sparse_categorical_accuracy\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-true-negatives.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-true-negatives.pbtxt new file mode 100644 index 0000000000..1b5eb8d0de --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-true-negatives.pbtxt @@ -0,0 +1,193 @@ +path: "tensorflow.keras.metrics.TrueNegatives" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'thresholds\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-true-positives.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-true-positives.pbtxt new file mode 100644 index 0000000000..5b9c470e32 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-true-positives.pbtxt @@ -0,0 +1,193 @@ +path: "tensorflow.keras.metrics.TruePositives" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'thresholds\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.pbtxt index a296e13158..8cab17edc5 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.pbtxt @@ -1,5 +1,49 @@ path: "tensorflow.keras.metrics" tf_module { + member { + name: "Accuracy" + mtype: "" + } + member { + name: "BinaryAccuracy" + mtype: "" + } + member { + name: "CategoricalAccuracy" + mtype: "" + } + member { + name: "FalseNegatives" + mtype: "" + } + member { + name: "FalsePositives" + mtype: "" + } + member { + name: "Mean" + mtype: "" + } + member { + name: "Precision" + mtype: "" + } + member { + name: "Recall" + mtype: "" + } + member { + name: "SparseCategoricalAccuracy" + mtype: "" + } + member { + name: "TrueNegatives" + mtype: "" + } + member { + name: "TruePositives" + mtype: "" + } member_method { name: "KLD" argspec: "args=[\'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" @@ -26,7 +70,7 @@ tf_module { } member_method { name: "binary_crossentropy" - argspec: "args=[\'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + argspec: "args=[\'y_true\', \'y_pred\', \'from_logits\'], varargs=None, keywords=None, defaults=[\'False\'], " } member_method { name: "categorical_accuracy" @@ -34,7 +78,7 @@ tf_module { } member_method { name: "categorical_crossentropy" - argspec: "args=[\'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + argspec: "args=[\'y_true\', \'y_pred\', \'from_logits\'], varargs=None, keywords=None, defaults=[\'False\'], " } member_method { name: "cosine" @@ -110,7 +154,7 @@ tf_module { } member_method { name: "sparse_categorical_crossentropy" - argspec: "args=[\'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + argspec: "args=[\'y_true\', \'y_pred\', \'from_logits\'], varargs=None, keywords=None, defaults=[\'False\'], " } member_method { name: "sparse_top_k_categorical_accuracy" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.models.-model.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.models.-model.pbtxt index ccff809f2b..c58c7bef22 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.models.-model.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.models.-model.pbtxt @@ -41,6 +41,14 @@ tf_class { name: "losses" mtype: "" } + member { + name: "metrics" + mtype: "" + } + member { + name: "metrics_names" + mtype: "" + } member { name: "name" mtype: "" @@ -69,6 +77,10 @@ tf_class { name: "output_shape" mtype: "" } + member { + name: "run_eagerly" + mtype: "" + } member { name: "state_updates" mtype: "" @@ -105,13 +117,17 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { name: "add_variable" - argspec: "args=[\'self\', \'name\', \'shape\', \'dtype\', \'initializer\', \'regularizer\', \'trainable\', \'constraint\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'True\', \'None\'], " + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" } member_method { name: "add_weight" @@ -225,6 +241,10 @@ tf_class { name: "predict_on_batch" argspec: "args=[\'self\', \'x\'], varargs=None, keywords=None, defaults=None" } + member_method { + name: "reset_metrics" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } member_method { name: "reset_states" argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" @@ -247,7 +267,7 @@ tf_class { } member_method { name: "test_on_batch" - argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\', \'reset_metrics\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'True\'], " } member_method { name: "to_json" @@ -259,6 +279,6 @@ tf_class { } member_method { name: "train_on_batch" - argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\', \'class_weight\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\', \'class_weight\', \'reset_metrics\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'True\'], " } } diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.models.-sequential.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.models.-sequential.pbtxt index b0fc7f97f1..473a1c16fb 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.models.-sequential.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.models.-sequential.pbtxt @@ -42,6 +42,14 @@ tf_class { name: "losses" mtype: "" } + member { + name: "metrics" + mtype: "" + } + member { + name: "metrics_names" + mtype: "" + } member { name: "name" mtype: "" @@ -70,6 +78,10 @@ tf_class { name: "output_shape" mtype: "" } + member { + name: "run_eagerly" + mtype: "" + } member { name: "state_updates" mtype: "" @@ -110,13 +122,17 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { name: "add_variable" - argspec: "args=[\'self\', \'name\', \'shape\', \'dtype\', \'initializer\', \'regularizer\', \'trainable\', \'constraint\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'True\', \'None\'], " + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" } member_method { name: "add_weight" @@ -242,6 +258,10 @@ tf_class { name: "predict_proba" argspec: "args=[\'self\', \'x\', \'batch_size\', \'verbose\'], varargs=None, keywords=None, defaults=[\'32\', \'0\'], " } + member_method { + name: "reset_metrics" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } member_method { name: "reset_states" argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" @@ -264,7 +284,7 @@ tf_class { } member_method { name: "test_on_batch" - argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\', \'reset_metrics\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'True\'], " } member_method { name: "to_json" @@ -276,6 +296,6 @@ tf_class { } member_method { name: "train_on_batch" - argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\', \'class_weight\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\', \'class_weight\', \'reset_metrics\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'True\'], " } } diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.utils.-progbar.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.utils.-progbar.pbtxt index be4496e753..8177cc71ed 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.utils.-progbar.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.utils.-progbar.pbtxt @@ -4,7 +4,7 @@ tf_class { is_instance: "" member_method { name: "__init__" - argspec: "args=[\'self\', \'target\', \'width\', \'verbose\', \'interval\', \'stateful_metrics\'], varargs=None, keywords=None, defaults=[\'30\', \'1\', \'0.05\', \'None\'], " + argspec: "args=[\'self\', \'target\', \'width\', \'verbose\', \'interval\', \'stateful_metrics\', \'unit_name\'], varargs=None, keywords=None, defaults=[\'30\', \'1\', \'0.05\', \'None\', \'step\'], " } member_method { name: "add" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.layers.-average-pooling1-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.layers.-average-pooling1-d.pbtxt index c82e67526b..059c91f724 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.layers.-average-pooling1-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.layers.-average-pooling1-d.pbtxt @@ -99,6 +99,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.layers.-average-pooling2-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.layers.-average-pooling2-d.pbtxt index 1d031cb5f8..d06c8e81ee 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.layers.-average-pooling2-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.layers.-average-pooling2-d.pbtxt @@ -99,6 +99,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.layers.-average-pooling3-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.layers.-average-pooling3-d.pbtxt index a8dda6655d..6be8e7c210 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.layers.-average-pooling3-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.layers.-average-pooling3-d.pbtxt @@ -99,6 +99,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.layers.-batch-normalization.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.layers.-batch-normalization.pbtxt index 97f65ed894..16d9ecce10 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.layers.-batch-normalization.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.layers.-batch-normalization.pbtxt @@ -1,7 +1,8 @@ path: "tensorflow.layers.BatchNormalization" tf_class { is_instance: "" - is_instance: "" + is_instance: "" + is_instance: "" is_instance: "" is_instance: "" is_instance: "" @@ -98,6 +99,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.layers.-conv1-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.layers.-conv1-d.pbtxt index ccd9578f0d..21c695935c 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.layers.-conv1-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.layers.-conv1-d.pbtxt @@ -99,6 +99,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.layers.-conv2-d-transpose.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.layers.-conv2-d-transpose.pbtxt index 9cbb58d721..f24d030720 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.layers.-conv2-d-transpose.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.layers.-conv2-d-transpose.pbtxt @@ -100,6 +100,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.layers.-conv2-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.layers.-conv2-d.pbtxt index c75ea3911e..0a510ece35 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.layers.-conv2-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.layers.-conv2-d.pbtxt @@ -99,6 +99,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.layers.-conv3-d-transpose.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.layers.-conv3-d-transpose.pbtxt index 5dc834e514..d0ee44bed3 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.layers.-conv3-d-transpose.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.layers.-conv3-d-transpose.pbtxt @@ -100,6 +100,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.layers.-conv3-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.layers.-conv3-d.pbtxt index 96ab209874..546de3cdab 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.layers.-conv3-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.layers.-conv3-d.pbtxt @@ -99,6 +99,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.layers.-dense.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.layers.-dense.pbtxt index 7e9656b352..3ad311581e 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.layers.-dense.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.layers.-dense.pbtxt @@ -98,6 +98,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.layers.-dropout.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.layers.-dropout.pbtxt index e9a2269a6e..9b83271350 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.layers.-dropout.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.layers.-dropout.pbtxt @@ -98,6 +98,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.layers.-flatten.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.layers.-flatten.pbtxt index 7d2eaaab2a..87a7fb3d84 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.layers.-flatten.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.layers.-flatten.pbtxt @@ -98,6 +98,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.layers.-input-spec.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.layers.-input-spec.pbtxt index fd02c919ae..80834e08f7 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.layers.-input-spec.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.layers.-input-spec.pbtxt @@ -1,6 +1,6 @@ path: "tensorflow.layers.InputSpec" tf_class { - is_instance: "" + is_instance: "" is_instance: "" member_method { name: "__init__" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.layers.-layer.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.layers.-layer.pbtxt index 8bc3eb26e9..32b17e90ad 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.layers.-layer.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.layers.-layer.pbtxt @@ -96,6 +96,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.layers.-max-pooling1-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.layers.-max-pooling1-d.pbtxt index 6a0dcce56a..643c469717 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.layers.-max-pooling1-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.layers.-max-pooling1-d.pbtxt @@ -99,6 +99,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.layers.-max-pooling2-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.layers.-max-pooling2-d.pbtxt index b6c84edf2a..434e25adc1 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.layers.-max-pooling2-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.layers.-max-pooling2-d.pbtxt @@ -99,6 +99,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.layers.-max-pooling3-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.layers.-max-pooling3-d.pbtxt index 062a02fa59..089fc6f924 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.layers.-max-pooling3-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.layers.-max-pooling3-d.pbtxt @@ -99,6 +99,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.layers.-separable-conv1-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.layers.-separable-conv1-d.pbtxt index eaad0fb23e..bc3d58b9ca 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.layers.-separable-conv1-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.layers.-separable-conv1-d.pbtxt @@ -100,6 +100,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.layers.-separable-conv2-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.layers.-separable-conv2-d.pbtxt index ece28a8ce9..fe7d71af3a 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.layers.-separable-conv2-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.layers.-separable-conv2-d.pbtxt @@ -100,6 +100,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-block-diag.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-block-diag.pbtxt index 973705dae2..773c74e64d 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-block-diag.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-block-diag.pbtxt @@ -79,6 +79,10 @@ tf_class { name: "batch_shape_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'batch_shape_tensor\'], " } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'det\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-circulant.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-circulant.pbtxt index de917706d5..533544d21f 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-circulant.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-circulant.pbtxt @@ -96,6 +96,10 @@ tf_class { name: "block_shape_tensor" argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "convolution_kernel" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'convolution_kernel\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-circulant2-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-circulant2-d.pbtxt index c4e6a21c3a..e3926eb6d4 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-circulant2-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-circulant2-d.pbtxt @@ -96,6 +96,10 @@ tf_class { name: "block_shape_tensor" argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "convolution_kernel" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'convolution_kernel\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-circulant3-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-circulant3-d.pbtxt index 2e085a8e28..ba209df782 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-circulant3-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-circulant3-d.pbtxt @@ -96,6 +96,10 @@ tf_class { name: "block_shape_tensor" argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "convolution_kernel" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'convolution_kernel\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-composition.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-composition.pbtxt index 42d22bce42..081fb0e08b 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-composition.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-composition.pbtxt @@ -79,6 +79,10 @@ tf_class { name: "batch_shape_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'batch_shape_tensor\'], " } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'det\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-diag.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-diag.pbtxt index d6749fdcec..2014a04301 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-diag.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-diag.pbtxt @@ -79,6 +79,10 @@ tf_class { name: "batch_shape_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'batch_shape_tensor\'], " } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'det\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-full-matrix.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-full-matrix.pbtxt index d9f363d133..9a87ae9687 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-full-matrix.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-full-matrix.pbtxt @@ -75,6 +75,10 @@ tf_class { name: "batch_shape_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'batch_shape_tensor\'], " } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'det\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-identity.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-identity.pbtxt index aac7ee31ed..33afb835ce 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-identity.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-identity.pbtxt @@ -76,6 +76,10 @@ tf_class { name: "batch_shape_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'batch_shape_tensor\'], " } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'det\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-kronecker.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-kronecker.pbtxt index c11d390829..a9078c8ab5 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-kronecker.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-kronecker.pbtxt @@ -79,6 +79,10 @@ tf_class { name: "batch_shape_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'batch_shape_tensor\'], " } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'det\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-low-rank-update.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-low-rank-update.pbtxt index 3ee800269e..4cfa3bb30d 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-low-rank-update.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-low-rank-update.pbtxt @@ -99,6 +99,10 @@ tf_class { name: "batch_shape_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'batch_shape_tensor\'], " } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'det\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-lower-triangular.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-lower-triangular.pbtxt index 63a1bc2321..a87649133f 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-lower-triangular.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-lower-triangular.pbtxt @@ -75,6 +75,10 @@ tf_class { name: "batch_shape_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'batch_shape_tensor\'], " } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'det\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-scaled-identity.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-scaled-identity.pbtxt index e2c5a505a7..3265646784 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-scaled-identity.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-scaled-identity.pbtxt @@ -80,6 +80,10 @@ tf_class { name: "batch_shape_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'batch_shape_tensor\'], " } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'det\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-zeros.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-zeros.pbtxt index a1b0e06b47..49d8890c89 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-zeros.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-zeros.pbtxt @@ -75,6 +75,10 @@ tf_class { name: "batch_shape_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'batch_shape_tensor\'], " } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'det\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator.pbtxt index 6d849dc040..c89dc067b3 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator.pbtxt @@ -74,6 +74,10 @@ tf_class { name: "batch_shape_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'batch_shape_tensor\'], " } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'det\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.lite.constants.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.lite.constants.pbtxt index 08845553e5..ef6c777665 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.lite.constants.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.lite.constants.pbtxt @@ -2,7 +2,7 @@ path: "tensorflow.lite.constants" tf_module { member { name: "FLOAT" - mtype: "" + mtype: "" } member { name: "GRAPHVIZ_DOT" @@ -10,19 +10,19 @@ tf_module { } member { name: "INT32" - mtype: "" + mtype: "" } member { name: "INT64" - mtype: "" + mtype: "" } member { name: "QUANTIZED_UINT8" - mtype: "" + mtype: "" } member { name: "STRING" - mtype: "" + mtype: "" } member { name: "TFLITE" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.math.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.math.pbtxt index a8334fdd1d..f34e2c2aa5 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.math.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.math.pbtxt @@ -176,6 +176,26 @@ tf_module { name: "invert_permutation" argspec: "args=[\'x\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "is_finite" + argspec: "args=[\'x\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "is_inf" + argspec: "args=[\'x\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "is_nan" + argspec: "args=[\'x\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "is_non_decreasing" + argspec: "args=[\'x\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "is_strictly_increasing" + argspec: "args=[\'x\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } member_method { name: "l2_normalize" argspec: "args=[\'x\', \'axis\', \'epsilon\', \'name\', \'dim\'], varargs=None, keywords=None, defaults=[\'None\', \'1e-12\', \'None\', \'None\'], " @@ -298,7 +318,7 @@ tf_module { } member_method { name: "reduce_std" - argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { name: "reduce_sum" @@ -306,7 +326,7 @@ tf_module { } member_method { name: "reduce_variance" - argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { name: "rint" @@ -322,7 +342,7 @@ tf_module { } member_method { name: "scalar_mul" - argspec: "args=[\'scalar\', \'x\'], varargs=None, keywords=None, defaults=None" + argspec: "args=[\'scalar\', \'x\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { name: "segment_max" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.metrics.-accuracy.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.metrics.-accuracy.pbtxt new file mode 100644 index 0000000000..f8e12f8817 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.metrics.-accuracy.pbtxt @@ -0,0 +1,194 @@ +path: "tensorflow.metrics.Accuracy" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'accuracy\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.metrics.-binary-accuracy.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.metrics.-binary-accuracy.pbtxt new file mode 100644 index 0000000000..b9bc6a716a --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.metrics.-binary-accuracy.pbtxt @@ -0,0 +1,194 @@ +path: "tensorflow.metrics.BinaryAccuracy" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\', \'threshold\'], varargs=None, keywords=None, defaults=[\'binary_accuracy\', \'None\', \'0.5\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.metrics.-categorical-accuracy.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.metrics.-categorical-accuracy.pbtxt new file mode 100644 index 0000000000..0ef75d8756 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.metrics.-categorical-accuracy.pbtxt @@ -0,0 +1,194 @@ +path: "tensorflow.metrics.CategoricalAccuracy" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'categorical_accuracy\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.metrics.-false-negatives.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.metrics.-false-negatives.pbtxt new file mode 100644 index 0000000000..33226a2df6 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.metrics.-false-negatives.pbtxt @@ -0,0 +1,193 @@ +path: "tensorflow.metrics.FalseNegatives" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'thresholds\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.metrics.-false-positives.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.metrics.-false-positives.pbtxt new file mode 100644 index 0000000000..9953162ea3 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.metrics.-false-positives.pbtxt @@ -0,0 +1,193 @@ +path: "tensorflow.metrics.FalsePositives" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'thresholds\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.metrics.-mean.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.metrics.-mean.pbtxt new file mode 100644 index 0000000000..7fe6d6fda9 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.metrics.-mean.pbtxt @@ -0,0 +1,192 @@ +path: "tensorflow.metrics.Mean" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'mean\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'values\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.metrics.-precision.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.metrics.-precision.pbtxt new file mode 100644 index 0000000000..8c3271a109 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.metrics.-precision.pbtxt @@ -0,0 +1,192 @@ +path: "tensorflow.metrics.Precision" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'thresholds\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.metrics.-recall.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.metrics.-recall.pbtxt new file mode 100644 index 0000000000..840a68bbc7 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.metrics.-recall.pbtxt @@ -0,0 +1,192 @@ +path: "tensorflow.metrics.Recall" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'thresholds\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.metrics.-sparse-categorical-accuracy.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.metrics.-sparse-categorical-accuracy.pbtxt new file mode 100644 index 0000000000..7bce43fbde --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.metrics.-sparse-categorical-accuracy.pbtxt @@ -0,0 +1,194 @@ +path: "tensorflow.metrics.SparseCategoricalAccuracy" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'sparse_categorical_accuracy\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.metrics.-true-negatives.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.metrics.-true-negatives.pbtxt new file mode 100644 index 0000000000..83cd5b736b --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.metrics.-true-negatives.pbtxt @@ -0,0 +1,193 @@ +path: "tensorflow.metrics.TrueNegatives" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'thresholds\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.metrics.-true-positives.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.metrics.-true-positives.pbtxt new file mode 100644 index 0000000000..5b2502eafe --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.metrics.-true-positives.pbtxt @@ -0,0 +1,193 @@ +path: "tensorflow.metrics.TruePositives" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'thresholds\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.metrics.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.metrics.pbtxt index e9b996c9f5..f5c267a166 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.metrics.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.metrics.pbtxt @@ -1,5 +1,49 @@ path: "tensorflow.metrics" tf_module { + member { + name: "Accuracy" + mtype: "" + } + member { + name: "BinaryAccuracy" + mtype: "" + } + member { + name: "CategoricalAccuracy" + mtype: "" + } + member { + name: "FalseNegatives" + mtype: "" + } + member { + name: "FalsePositives" + mtype: "" + } + member { + name: "Mean" + mtype: "" + } + member { + name: "Precision" + mtype: "" + } + member { + name: "Recall" + mtype: "" + } + member { + name: "SparseCategoricalAccuracy" + mtype: "" + } + member { + name: "TrueNegatives" + mtype: "" + } + member { + name: "TruePositives" + mtype: "" + } member_method { name: "accuracy" argspec: "args=[\'labels\', \'predictions\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.nn.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.nn.pbtxt index 93f2fda2ac..40e20f8c91 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.nn.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.nn.pbtxt @@ -44,6 +44,10 @@ tf_module { name: "bidirectional_dynamic_rnn" argspec: "args=[\'cell_fw\', \'cell_bw\', \'inputs\', \'sequence_length\', \'initial_state_fw\', \'initial_state_bw\', \'dtype\', \'parallel_iterations\', \'swap_memory\', \'time_major\', \'scope\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\', \'False\', \'False\', \'None\'], " } + member_method { + name: "collapse_repeated" + argspec: "args=[\'labels\', \'seq_length\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } member_method { name: "compute_accidental_hits" argspec: "args=[\'true_classes\', \'sampled_candidates\', \'num_true\', \'seed\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " @@ -72,6 +76,10 @@ tf_module { name: "conv3d" argspec: "args=[\'input\', \'filter\', \'strides\', \'padding\', \'data_format\', \'dilations\', \'name\'], varargs=None, keywords=None, defaults=[\'NDHWC\', \'[1, 1, 1, 1, 1]\', \'None\'], " } + member_method { + name: "conv3d_backprop_filter" + argspec: "args=[\'input\', \'filter_sizes\', \'out_backprop\', \'strides\', \'padding\', \'data_format\', \'dilations\', \'name\'], varargs=None, keywords=None, defaults=[\'NDHWC\', \'[1, 1, 1, 1, 1]\', \'None\'], " + } member_method { name: "conv3d_backprop_filter_v2" argspec: "args=[\'input\', \'filter_sizes\', \'out_backprop\', \'strides\', \'padding\', \'data_format\', \'dilations\', \'name\'], varargs=None, keywords=None, defaults=[\'NDHWC\', \'[1, 1, 1, 1, 1]\', \'None\'], " @@ -104,6 +112,14 @@ tf_module { name: "ctc_loss" argspec: "args=[\'labels\', \'inputs\', \'sequence_length\', \'preprocess_collapse_repeated\', \'ctc_merge_repeated\', \'ignore_longer_outputs_than_inputs\', \'time_major\'], varargs=None, keywords=None, defaults=[\'False\', \'True\', \'False\', \'True\'], " } + member_method { + name: "ctc_loss_v2" + argspec: "args=[\'labels\', \'logits\', \'label_length\', \'logit_length\', \'logits_time_major\', \'unique\', \'blank_index\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'None\', \'None\', \'None\'], " + } + member_method { + name: "ctc_unique_labels" + argspec: "args=[\'labels\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } member_method { name: "depth_to_space" argspec: "args=[\'input\', \'block_size\', \'name\', \'data_format\'], varargs=None, keywords=None, defaults=[\'None\', \'NHWC\'], " @@ -112,6 +128,14 @@ tf_module { name: "depthwise_conv2d" argspec: "args=[\'input\', \'filter\', \'strides\', \'padding\', \'rate\', \'name\', \'data_format\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " } + member_method { + name: "depthwise_conv2d_backprop_filter" + argspec: "args=[\'input\', \'filter_sizes\', \'out_backprop\', \'strides\', \'padding\', \'data_format\', \'dilations\', \'name\'], varargs=None, keywords=None, defaults=[\'NHWC\', \'[1, 1, 1, 1]\', \'None\'], " + } + member_method { + name: "depthwise_conv2d_backprop_input" + argspec: "args=[\'input_sizes\', \'filter\', \'out_backprop\', \'strides\', \'padding\', \'data_format\', \'dilations\', \'name\'], varargs=None, keywords=None, defaults=[\'NHWC\', \'[1, 1, 1, 1]\', \'None\'], " + } member_method { name: "depthwise_conv2d_native" argspec: "args=[\'input\', \'filter\', \'strides\', \'padding\', \'data_format\', \'dilations\', \'name\'], varargs=None, keywords=None, defaults=[\'NHWC\', \'[1, 1, 1, 1]\', \'None\'], " @@ -130,7 +154,7 @@ tf_module { } member_method { name: "dropout" - argspec: "args=[\'x\', \'keep_prob\', \'noise_shape\', \'seed\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + argspec: "args=[\'x\', \'keep_prob\', \'noise_shape\', \'seed\', \'name\', \'rate\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " } member_method { name: "dynamic_rnn" @@ -302,7 +326,7 @@ tf_module { } member_method { name: "softmax_cross_entropy_with_logits_v2" - argspec: "args=[\'labels\', \'logits\', \'dim\', \'name\'], varargs=None, keywords=None, defaults=[\'-1\', \'None\'], " + argspec: "args=[\'labels\', \'logits\', \'axis\', \'name\', \'dim\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " } member_method { name: "softplus" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-basic-l-s-t-m-cell.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-basic-l-s-t-m-cell.pbtxt index 88b8f37c4f..f7f9978c06 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-basic-l-s-t-m-cell.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-basic-l-s-t-m-cell.pbtxt @@ -107,6 +107,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-basic-r-n-n-cell.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-basic-r-n-n-cell.pbtxt index a4483fefa2..f9e898484b 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-basic-r-n-n-cell.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-basic-r-n-n-cell.pbtxt @@ -107,6 +107,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-device-wrapper.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-device-wrapper.pbtxt index 381c4975d7..9e52a42526 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-device-wrapper.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-device-wrapper.pbtxt @@ -106,6 +106,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-dropout-wrapper.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-dropout-wrapper.pbtxt index 912365a28b..9836433d08 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-dropout-wrapper.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-dropout-wrapper.pbtxt @@ -110,6 +110,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-g-r-u-cell.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-g-r-u-cell.pbtxt index a4bb3219c7..5fd9b329bd 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-g-r-u-cell.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-g-r-u-cell.pbtxt @@ -107,6 +107,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-l-s-t-m-cell.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-l-s-t-m-cell.pbtxt index 715bfd5fc7..76c8cff22b 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-l-s-t-m-cell.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-l-s-t-m-cell.pbtxt @@ -107,6 +107,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-multi-r-n-n-cell.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-multi-r-n-n-cell.pbtxt index b66c0f89cc..f53567af52 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-multi-r-n-n-cell.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-multi-r-n-n-cell.pbtxt @@ -106,6 +106,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-r-n-n-cell.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-r-n-n-cell.pbtxt index faeb4f3513..d3b68e4f29 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-r-n-n-cell.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-r-n-n-cell.pbtxt @@ -105,6 +105,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-residual-wrapper.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-residual-wrapper.pbtxt index caa2e60080..1f7840ab91 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-residual-wrapper.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.nn.rnn_cell.-residual-wrapper.pbtxt @@ -106,6 +106,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.pbtxt index f4dce81659..584c74f99d 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.pbtxt @@ -244,6 +244,10 @@ tf_module { name: "TensorShape" mtype: "" } + member { + name: "TensorSpec" + mtype: "" + } member { name: "TextLineReader" mtype: "" @@ -320,6 +324,10 @@ tf_module { name: "debugging" mtype: "" } + member { + name: "distribute" + mtype: "" + } member { name: "distributions" mtype: "" @@ -692,6 +700,10 @@ tf_module { name: "argmin" argspec: "args=[\'input\', \'axis\', \'name\', \'dimension\', \'output_type\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \"\"], " } + member_method { + name: "argsort" + argspec: "args=[\'values\', \'axis\', \'direction\', \'stable\', \'name\'], varargs=None, keywords=None, defaults=[\'-1\', \'ASCENDING\', \'False\', \'None\'], " + } member_method { name: "as_dtype" argspec: "args=[\'type_value\'], varargs=None, keywords=None, defaults=None" @@ -778,7 +790,7 @@ tf_module { } member_method { name: "assert_scalar" - argspec: "args=[\'tensor\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + argspec: "args=[\'tensor\', \'name\', \'message\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { name: "assert_type" @@ -1040,10 +1052,18 @@ tf_module { name: "dimension_value" argspec: "args=[\'dimension\'], varargs=None, keywords=None, defaults=None" } + member_method { + name: "disable_eager_execution" + argspec: "args=[], varargs=None, keywords=None, defaults=None" + } member_method { name: "disable_resource_variables" argspec: "args=[], varargs=None, keywords=None, defaults=None" } + member_method { + name: "disable_v2_behavior" + argspec: "args=[], varargs=None, keywords=None, defaults=None" + } member_method { name: "disable_v2_tensorshape" argspec: "args=[], varargs=None, keywords=None, defaults=None" @@ -1084,6 +1104,10 @@ tf_module { name: "enable_resource_variables" argspec: "args=[], varargs=None, keywords=None, defaults=None" } + member_method { + name: "enable_v2_behavior" + argspec: "args=[], varargs=None, keywords=None, defaults=None" + } member_method { name: "enable_v2_tensorshape" argspec: "args=[], varargs=None, keywords=None, defaults=None" @@ -1232,6 +1256,10 @@ tf_module { name: "get_local_variable" argspec: "args=[\'name\', \'shape\', \'dtype\', \'initializer\', \'regularizer\', \'trainable\', \'collections\', \'caching_device\', \'partitioner\', \'validate_shape\', \'use_resource\', \'custom_getter\', \'constraint\', \'synchronization\', \'aggregation\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'False\', \'None\', \'None\', \'None\', \'True\', \'None\', \'None\', \'None\', \'VariableSynchronization.AUTO\', \'VariableAggregation.NONE\'], " } + member_method { + name: "get_logger" + argspec: "args=[], varargs=None, keywords=None, defaults=None" + } member_method { name: "get_seed" argspec: "args=[\'op_seed\'], varargs=None, keywords=None, defaults=None" @@ -1466,7 +1494,7 @@ tf_module { } member_method { name: "make_tensor_proto" - argspec: "args=[\'values\', \'dtype\', \'shape\', \'verify_shape\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'False\'], " + argspec: "args=[\'values\', \'dtype\', \'shape\', \'verify_shape\', \'allow_broadcast\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'False\', \'False\'], " } member_method { name: "map_fn" @@ -1810,7 +1838,7 @@ tf_module { } member_method { name: "scalar_mul" - argspec: "args=[\'scalar\', \'x\'], varargs=None, keywords=None, defaults=None" + argspec: "args=[\'scalar\', \'x\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { name: "scan" @@ -1948,6 +1976,10 @@ tf_module { name: "slice" argspec: "args=[\'input_\', \'begin\', \'size\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "sort" + argspec: "args=[\'values\', \'axis\', \'direction\', \'name\'], varargs=None, keywords=None, defaults=[\'-1\', \'ASCENDING\', \'None\'], " + } member_method { name: "space_to_batch" argspec: "args=[\'input\', \'paddings\', \'block_size\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -1962,7 +1994,7 @@ tf_module { } member_method { name: "sparse_add" - argspec: "args=[\'a\', \'b\', \'thresh\'], varargs=None, keywords=None, defaults=[\'0\'], " + argspec: "args=[\'a\', \'b\', \'threshold\', \'thresh\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { name: "sparse_concat" @@ -2156,6 +2188,18 @@ tf_module { name: "tanh" argspec: "args=[\'x\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "tensor_scatter_add" + argspec: "args=[\'tensor\', \'indices\', \'updates\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "tensor_scatter_sub" + argspec: "args=[\'tensor\', \'indices\', \'updates\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "tensor_scatter_update" + argspec: "args=[\'tensor\', \'indices\', \'updates\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } member_method { name: "tensordot" argspec: "args=[\'a\', \'b\', \'axes\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -2296,6 +2340,10 @@ tf_module { name: "while_loop" argspec: "args=[\'cond\', \'body\', \'loop_vars\', \'shape_invariants\', \'parallel_iterations\', \'back_prop\', \'swap_memory\', \'name\', \'maximum_iterations\', \'return_same_structure\'], varargs=None, keywords=None, defaults=[\'None\', \'10\', \'True\', \'False\', \'None\', \'None\', \'False\'], " } + member_method { + name: "wrap_function" + argspec: "args=[\'fn\', \'signature\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } member_method { name: "write_file" argspec: "args=[\'filename\', \'contents\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.quantization.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.quantization.pbtxt index 2948b7318e..632c2f8f83 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.quantization.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.quantization.pbtxt @@ -34,7 +34,7 @@ tf_module { } member_method { name: "quantize_and_dequantize" - argspec: "args=[\'input\', \'input_min\', \'input_max\', \'signed_input\', \'num_bits\', \'range_given\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'8\', \'False\', \'None\'], " + argspec: "args=[\'input\', \'input_min\', \'input_max\', \'signed_input\', \'num_bits\', \'range_given\', \'round_mode\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'8\', \'False\', \'HALF_TO_EVEN\', \'None\'], " } member_method { name: "quantized_concat" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.random.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.random.pbtxt index 160c09798d..107534e086 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.random.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.random.pbtxt @@ -1,5 +1,9 @@ path: "tensorflow.random" tf_module { + member_method { + name: "categorical" + argspec: "args=[\'logits\', \'num_samples\', \'dtype\', \'seed\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + } member_method { name: "gamma" argspec: "args=[\'shape\', \'alpha\', \'beta\', \'dtype\', \'seed\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \"\", \'None\', \'None\'], " @@ -32,6 +36,10 @@ tf_module { name: "shuffle" argspec: "args=[\'value\', \'seed\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } + member_method { + name: "stateless_categorical" + argspec: "args=[\'logits\', \'num_samples\', \'seed\', \'dtype\', \'name\'], varargs=None, keywords=None, defaults=[\"\", \'None\'], " + } member_method { name: "stateless_multinomial" argspec: "args=[\'logits\', \'num_samples\', \'seed\', \'output_dtype\', \'name\'], varargs=None, keywords=None, defaults=[\"\", \'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.saved_model.-builder.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.saved_model.-builder.pbtxt index 67457de070..e4cc0061a9 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.saved_model.-builder.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.saved_model.-builder.pbtxt @@ -1,6 +1,7 @@ path: "tensorflow.saved_model.Builder" tf_class { is_instance: "" + is_instance: "" is_instance: "" member_method { name: "__init__" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.saved_model.builder.-saved-model-builder.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.saved_model.builder.-saved-model-builder.pbtxt index 83bd703540..44860b1172 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.saved_model.builder.-saved-model-builder.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.saved_model.builder.-saved-model-builder.pbtxt @@ -1,6 +1,7 @@ path: "tensorflow.saved_model.builder.SavedModelBuilder" tf_class { is_instance: "" + is_instance: "" is_instance: "" member_method { name: "__init__" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.saved_model.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.saved_model.pbtxt index 2055bfbf06..3929003fa1 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.saved_model.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.saved_model.pbtxt @@ -148,6 +148,10 @@ tf_module { name: "classification_signature_def" argspec: "args=[\'examples\', \'classes\', \'scores\'], varargs=None, keywords=None, defaults=None" } + member_method { + name: "contains_saved_model" + argspec: "args=[\'export_dir\'], varargs=None, keywords=None, defaults=None" + } member_method { name: "get_tensor_from_tensor_info" argspec: "args=[\'tensor_info\', \'graph\', \'import_scope\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.sets.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.sets.pbtxt index 8a196b1a55..09d6f1424b 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.sets.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.sets.pbtxt @@ -1,5 +1,13 @@ path: "tensorflow.sets" tf_module { + member_method { + name: "difference" + argspec: "args=[\'a\', \'b\', \'aminusb\', \'validate_indices\'], varargs=None, keywords=None, defaults=[\'True\', \'True\'], " + } + member_method { + name: "intersection" + argspec: "args=[\'a\', \'b\', \'validate_indices\'], varargs=None, keywords=None, defaults=[\'True\'], " + } member_method { name: "set_difference" argspec: "args=[\'a\', \'b\', \'aminusb\', \'validate_indices\'], varargs=None, keywords=None, defaults=[\'True\', \'True\'], " @@ -16,4 +24,12 @@ tf_module { name: "set_union" argspec: "args=[\'a\', \'b\', \'validate_indices\'], varargs=None, keywords=None, defaults=[\'True\'], " } + member_method { + name: "size" + argspec: "args=[\'a\', \'validate_indices\'], varargs=None, keywords=None, defaults=[\'True\'], " + } + member_method { + name: "union" + argspec: "args=[\'a\', \'b\', \'validate_indices\'], varargs=None, keywords=None, defaults=[\'True\'], " + } } diff --git a/tensorflow/tools/api/golden/v1/tensorflow.signal.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.signal.pbtxt index 2c50c41f18..ea717b4d71 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.signal.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.signal.pbtxt @@ -1,5 +1,9 @@ path: "tensorflow.signal" tf_module { + member_method { + name: "dct" + argspec: "args=[\'input\', \'type\', \'n\', \'axis\', \'norm\', \'name\'], varargs=None, keywords=None, defaults=[\'2\', \'None\', \'-1\', \'None\', \'None\'], " + } member_method { name: "fft" argspec: "args=[\'input\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -24,6 +28,10 @@ tf_module { name: "hann_window" argspec: "args=[\'window_length\', \'periodic\', \'dtype\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \"\", \'None\'], " } + member_method { + name: "idct" + argspec: "args=[\'input\', \'type\', \'n\', \'axis\', \'norm\', \'name\'], varargs=None, keywords=None, defaults=[\'2\', \'None\', \'-1\', \'None\', \'None\'], " + } member_method { name: "ifft" argspec: "args=[\'input\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -44,6 +52,18 @@ tf_module { name: "inverse_stft_window_fn" argspec: "args=[\'frame_step\', \'forward_window_fn\', \'name\'], varargs=None, keywords=None, defaults=[\'\', \'None\'], " } + member_method { + name: "irfft" + argspec: "args=[\'input_tensor\', \'fft_length\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "irfft2d" + argspec: "args=[\'input_tensor\', \'fft_length\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "irfft3d" + argspec: "args=[\'input_tensor\', \'fft_length\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "linear_to_mel_weight_matrix" argspec: "args=[\'num_mel_bins\', \'num_spectrogram_bins\', \'sample_rate\', \'lower_edge_hertz\', \'upper_edge_hertz\', \'dtype\', \'name\'], varargs=None, keywords=None, defaults=[\'20\', \'129\', \'8000\', \'125.0\', \'3800.0\', \"\", \'None\'], " @@ -56,6 +76,18 @@ tf_module { name: "overlap_and_add" argspec: "args=[\'signal\', \'frame_step\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "rfft" + argspec: "args=[\'input_tensor\', \'fft_length\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "rfft2d" + argspec: "args=[\'input_tensor\', \'fft_length\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "rfft3d" + argspec: "args=[\'input_tensor\', \'fft_length\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "stft" argspec: "args=[\'signals\', \'frame_length\', \'frame_step\', \'fft_length\', \'window_fn\', \'pad_end\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'\', \'False\', \'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.sparse.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.sparse.pbtxt index 32bd8d5f8e..33e342bc75 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.sparse.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.sparse.pbtxt @@ -10,7 +10,7 @@ tf_module { } member_method { name: "add" - argspec: "args=[\'a\', \'b\', \'thresh\'], varargs=None, keywords=None, defaults=[\'0\'], " + argspec: "args=[\'a\', \'b\', \'threshold\', \'thresh\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { name: "concat" @@ -112,6 +112,10 @@ tf_module { name: "softmax" argspec: "args=[\'sp_input\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "sparse_dense_matmul" + argspec: "args=[\'sp_a\', \'b\', \'adjoint_a\', \'adjoint_b\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'False\', \'None\'], " + } member_method { name: "split" argspec: "args=[\'keyword_required\', \'sp_input\', \'num_split\', \'axis\', \'name\', \'split_dim\'], varargs=None, keywords=None, defaults=[\'KeywordRequired()\', \'None\', \'None\', \'None\', \'None\', \'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.strings.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.strings.pbtxt index 03144cbe70..a1cd581a86 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.strings.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.strings.pbtxt @@ -52,6 +52,10 @@ tf_module { name: "to_number" argspec: "args=[\'string_tensor\', \'out_type\', \'name\'], varargs=None, keywords=None, defaults=[\"\", \'None\'], " } + member_method { + name: "unicode_encode" + argspec: "args=[\'input\', \'output_encoding\', \'errors\', \'replacement_char\', \'name\'], varargs=None, keywords=None, defaults=[\'replace\', \'65533\', \'None\'], " + } member_method { name: "unicode_script" argspec: "args=[\'input\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.test.-benchmark.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.test.-benchmark.pbtxt index df528e26b6..6fc489c860 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.test.-benchmark.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.test.-benchmark.pbtxt @@ -6,6 +6,10 @@ tf_class { member_method { name: "__init__" } + member_method { + name: "evaluate" + argspec: "args=[\'self\', \'tensors\'], varargs=None, keywords=None, defaults=None" + } member_method { name: "is_abstract" argspec: "args=[\'cls\'], varargs=None, keywords=None, defaults=None" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.train.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.train.pbtxt index 877c55c6b3..bdb3ea2197 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.train.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.train.pbtxt @@ -396,6 +396,10 @@ tf_module { name: "piecewise_constant" argspec: "args=[\'x\', \'boundaries\', \'values\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "piecewise_constant_decay" + argspec: "args=[\'x\', \'boundaries\', \'values\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } member_method { name: "polynomial_decay" argspec: "args=[\'learning_rate\', \'global_step\', \'decay_steps\', \'end_learning_rate\', \'power\', \'cycle\', \'name\'], varargs=None, keywords=None, defaults=[\'0.0001\', \'1.0\', \'False\', \'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.-conditional-accumulator-base.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.-conditional-accumulator-base.pbtxt deleted file mode 100644 index c9a32c16b3..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.-conditional-accumulator-base.pbtxt +++ /dev/null @@ -1,29 +0,0 @@ -path: "tensorflow.ConditionalAccumulatorBase" -tf_class { - is_instance: "" - is_instance: "" - member { - name: "accumulator_ref" - mtype: "" - } - member { - name: "dtype" - mtype: "" - } - member { - name: "name" - mtype: "" - } - member_method { - name: "__init__" - argspec: "args=[\'self\', \'dtype\', \'shape\', \'accumulator_ref\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "num_accumulated" - argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "set_global_step" - argspec: "args=[\'self\', \'new_global_step\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.-conditional-accumulator.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.-conditional-accumulator.pbtxt deleted file mode 100644 index 15e0ab76b6..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.-conditional-accumulator.pbtxt +++ /dev/null @@ -1,38 +0,0 @@ -path: "tensorflow.ConditionalAccumulator" -tf_class { - is_instance: "" - is_instance: "" - is_instance: "" - member { - name: "accumulator_ref" - mtype: "" - } - member { - name: "dtype" - mtype: "" - } - member { - name: "name" - mtype: "" - } - member_method { - name: "__init__" - argspec: "args=[\'self\', \'dtype\', \'shape\', \'shared_name\', \'name\', \'reduction_type\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'conditional_accumulator\', \'MEAN\'], " - } - member_method { - name: "apply_grad" - argspec: "args=[\'self\', \'grad\', \'local_step\', \'name\'], varargs=None, keywords=None, defaults=[\'0\', \'None\'], " - } - member_method { - name: "num_accumulated" - argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "set_global_step" - argspec: "args=[\'self\', \'new_global_step\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "take_grad" - argspec: "args=[\'self\', \'num_required\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.-config-proto.-experimental.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.-config-proto.-experimental.pbtxt index f7491649c2..caa72fe5a6 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.-config-proto.-experimental.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.-config-proto.-experimental.pbtxt @@ -20,6 +20,12 @@ tf_proto { label: LABEL_OPTIONAL type: TYPE_INT32 } + field { + name: "use_numa_affinity" + number: 5 + label: LABEL_OPTIONAL + type: TYPE_BOOL + } reserved_range { start: 2 end: 3 diff --git a/tensorflow/tools/api/golden/v2/tensorflow.-config-proto.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.-config-proto.pbtxt index 53b532beab..b505d81350 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.-config-proto.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.-config-proto.pbtxt @@ -143,6 +143,12 @@ tf_proto { label: LABEL_OPTIONAL type: TYPE_INT32 } + field { + name: "use_numa_affinity" + number: 5 + label: LABEL_OPTIONAL + type: TYPE_BOOL + } reserved_range { start: 2 end: 3 diff --git a/tensorflow/tools/api/golden/v2/tensorflow.-device-spec.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.-device-spec.pbtxt deleted file mode 100644 index 92e535c341..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.-device-spec.pbtxt +++ /dev/null @@ -1,37 +0,0 @@ -path: "tensorflow.DeviceSpec" -tf_class { - is_instance: "" - is_instance: "" - member { - name: "job" - mtype: "" - } - member { - name: "replica" - mtype: "" - } - member { - name: "task" - mtype: "" - } - member_method { - name: "__init__" - argspec: "args=[\'self\', \'job\', \'replica\', \'task\', \'device_type\', \'device_index\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "from_string" - argspec: "args=[\'spec\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "merge_from" - argspec: "args=[\'self\', \'dev\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "parse_from_string" - argspec: "args=[\'self\', \'spec\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "to_string" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.-dimension.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.-dimension.pbtxt deleted file mode 100644 index a9ab27719b..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.-dimension.pbtxt +++ /dev/null @@ -1,25 +0,0 @@ -path: "tensorflow.Dimension" -tf_class { - is_instance: "" - is_instance: "" - member { - name: "value" - mtype: "" - } - member_method { - name: "__init__" - argspec: "args=[\'self\', \'value\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "assert_is_compatible_with" - argspec: "args=[\'self\', \'other\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "is_compatible_with" - argspec: "args=[\'self\', \'other\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "merge_with" - argspec: "args=[\'self\', \'other\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.-gradient-tape.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.-gradient-tape.pbtxt index 0a16d6ab92..2299a009d3 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.-gradient-tape.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.-gradient-tape.pbtxt @@ -6,10 +6,18 @@ tf_class { name: "__init__" argspec: "args=[\'self\', \'persistent\', \'watch_accessed_variables\'], varargs=None, keywords=None, defaults=[\'False\', \'True\'], " } + member_method { + name: "batch_jacobian" + argspec: "args=[\'self\', \'target\', \'source\', \'unconnected_gradients\', \'parallel_iterations\', \'experimental_use_pfor\'], varargs=None, keywords=None, defaults=[\'UnconnectedGradients.NONE\', \'None\', \'True\'], " + } member_method { name: "gradient" argspec: "args=[\'self\', \'target\', \'sources\', \'output_gradients\', \'unconnected_gradients\'], varargs=None, keywords=None, defaults=[\'None\', \'UnconnectedGradients.NONE\'], " } + member_method { + name: "jacobian" + argspec: "args=[\'self\', \'target\', \'sources\', \'unconnected_gradients\', \'parallel_iterations\', \'experimental_use_pfor\'], varargs=None, keywords=None, defaults=[\'UnconnectedGradients.NONE\', \'None\', \'True\'], " + } member_method { name: "reset" argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.-graph-keys.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.-graph-keys.pbtxt deleted file mode 100644 index ffe4790933..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.-graph-keys.pbtxt +++ /dev/null @@ -1,140 +0,0 @@ -path: "tensorflow.GraphKeys" -tf_class { - is_instance: "" - is_instance: "" - member { - name: "ACTIVATIONS" - mtype: "" - } - member { - name: "ASSET_FILEPATHS" - mtype: "" - } - member { - name: "BIASES" - mtype: "" - } - member { - name: "CONCATENATED_VARIABLES" - mtype: "" - } - member { - name: "COND_CONTEXT" - mtype: "" - } - member { - name: "EVAL_STEP" - mtype: "" - } - member { - name: "GLOBAL_STEP" - mtype: "" - } - member { - name: "GLOBAL_VARIABLES" - mtype: "" - } - member { - name: "INIT_OP" - mtype: "" - } - member { - name: "LOCAL_INIT_OP" - mtype: "" - } - member { - name: "LOCAL_RESOURCES" - mtype: "" - } - member { - name: "LOCAL_VARIABLES" - mtype: "" - } - member { - name: "LOSSES" - mtype: "" - } - member { - name: "METRIC_VARIABLES" - mtype: "" - } - member { - name: "MODEL_VARIABLES" - mtype: "" - } - member { - name: "MOVING_AVERAGE_VARIABLES" - mtype: "" - } - member { - name: "QUEUE_RUNNERS" - mtype: "" - } - member { - name: "READY_FOR_LOCAL_INIT_OP" - mtype: "" - } - member { - name: "READY_OP" - mtype: "" - } - member { - name: "REGULARIZATION_LOSSES" - mtype: "" - } - member { - name: "RESOURCES" - mtype: "" - } - member { - name: "SAVEABLE_OBJECTS" - mtype: "" - } - member { - name: "SAVERS" - mtype: "" - } - member { - name: "SUMMARIES" - mtype: "" - } - member { - name: "SUMMARY_OP" - mtype: "" - } - member { - name: "TABLE_INITIALIZERS" - mtype: "" - } - member { - name: "TRAINABLE_RESOURCE_VARIABLES" - mtype: "" - } - member { - name: "TRAINABLE_VARIABLES" - mtype: "" - } - member { - name: "TRAIN_OP" - mtype: "" - } - member { - name: "UPDATE_OPS" - mtype: "" - } - member { - name: "VARIABLES" - mtype: "" - } - member { - name: "WEIGHTS" - mtype: "" - } - member { - name: "WHILE_CONTEXT" - mtype: "" - } - member_method { - name: "__init__" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.-tensor-info.-coo-sparse.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.-tensor-info.-coo-sparse.pbtxt deleted file mode 100644 index 0064c8460c..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.-tensor-info.-coo-sparse.pbtxt +++ /dev/null @@ -1,24 +0,0 @@ -path: "tensorflow.TensorInfo.CooSparse" -tf_proto { - descriptor { - name: "CooSparse" - field { - name: "values_tensor_name" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - field { - name: "indices_tensor_name" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - field { - name: "dense_shape_tensor_name" - number: 3 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.-tensor-info.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.-tensor-info.pbtxt deleted file mode 100644 index 63566c808e..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.-tensor-info.pbtxt +++ /dev/null @@ -1,59 +0,0 @@ -path: "tensorflow.TensorInfo" -tf_proto { - descriptor { - name: "TensorInfo" - field { - name: "name" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_STRING - oneof_index: 0 - } - field { - name: "coo_sparse" - number: 4 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.TensorInfo.CooSparse" - oneof_index: 0 - } - field { - name: "dtype" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_ENUM - type_name: ".tensorflow.DataType" - } - field { - name: "tensor_shape" - number: 3 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.TensorShapeProto" - } - nested_type { - name: "CooSparse" - field { - name: "values_tensor_name" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - field { - name: "indices_tensor_name" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - field { - name: "dense_shape_tensor_name" - number: 3 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - } - oneof_decl { - name: "encoding" - } - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.-tensor-spec.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.-tensor-spec.pbtxt new file mode 100644 index 0000000000..493dcba892 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.-tensor-spec.pbtxt @@ -0,0 +1,33 @@ +path: "tensorflow.TensorSpec" +tf_class { + is_instance: "" + is_instance: "" + member { + name: "dtype" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "shape" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'shape\', \'dtype\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "from_spec" + argspec: "args=[\'cls\', \'spec\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "from_tensor" + argspec: "args=[\'cls\', \'tensor\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "is_compatible_with" + argspec: "args=[\'self\', \'spec_or_tensor\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.app.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.app.pbtxt deleted file mode 100644 index 67e1b76cab..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.app.pbtxt +++ /dev/null @@ -1,7 +0,0 @@ -path: "tensorflow.app" -tf_module { - member_method { - name: "run" - argspec: "args=[\'main\', \'argv\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.data.-dataset.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.data.-dataset.pbtxt index 8b7f63e43e..ac8dd2de7f 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.data.-dataset.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.data.-dataset.pbtxt @@ -1,6 +1,6 @@ path: "tensorflow.data.Dataset" tf_class { - is_instance: "" + is_instance: "" is_instance: "" member { name: "output_classes" @@ -16,7 +16,6 @@ tf_class { } member_method { name: "__init__" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" } member_method { name: "apply" @@ -46,10 +45,6 @@ tf_class { name: "from_generator" argspec: "args=[\'generator\', \'output_types\', \'output_shapes\', \'args\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } - member_method { - name: "from_sparse_tensor_slices" - argspec: "args=[\'sparse_tensor\'], varargs=None, keywords=None, defaults=None" - } member_method { name: "from_tensor_slices" argspec: "args=[\'tensors\'], varargs=None, keywords=None, defaults=None" @@ -66,14 +61,6 @@ tf_class { name: "list_files" argspec: "args=[\'file_pattern\', \'shuffle\', \'seed\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } - member_method { - name: "make_initializable_iterator" - argspec: "args=[\'self\', \'shared_name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "make_one_shot_iterator" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } member_method { name: "map" argspec: "args=[\'self\', \'map_func\', \'num_parallel_calls\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -102,10 +89,6 @@ tf_class { name: "repeat" argspec: "args=[\'self\', \'count\'], varargs=None, keywords=None, defaults=[\'None\'], " } - member_method { - name: "shard" - argspec: "args=[\'self\', \'num_shards\', \'index\'], varargs=None, keywords=None, defaults=None" - } member_method { name: "shuffle" argspec: "args=[\'self\', \'buffer_size\', \'seed\', \'reshuffle_each_iteration\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.data.-fixed-length-record-dataset.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.data.-fixed-length-record-dataset.pbtxt index 81358cecbc..f157351243 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.data.-fixed-length-record-dataset.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.data.-fixed-length-record-dataset.pbtxt @@ -1,8 +1,8 @@ path: "tensorflow.data.FixedLengthRecordDataset" tf_class { - is_instance: "" + is_instance: "" is_instance: "" - is_instance: "" + is_instance: "" is_instance: "" member { name: "output_classes" @@ -48,10 +48,6 @@ tf_class { name: "from_generator" argspec: "args=[\'generator\', \'output_types\', \'output_shapes\', \'args\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } - member_method { - name: "from_sparse_tensor_slices" - argspec: "args=[\'sparse_tensor\'], varargs=None, keywords=None, defaults=None" - } member_method { name: "from_tensor_slices" argspec: "args=[\'tensors\'], varargs=None, keywords=None, defaults=None" @@ -68,14 +64,6 @@ tf_class { name: "list_files" argspec: "args=[\'file_pattern\', \'shuffle\', \'seed\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } - member_method { - name: "make_initializable_iterator" - argspec: "args=[\'self\', \'shared_name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "make_one_shot_iterator" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } member_method { name: "map" argspec: "args=[\'self\', \'map_func\', \'num_parallel_calls\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -104,10 +92,6 @@ tf_class { name: "repeat" argspec: "args=[\'self\', \'count\'], varargs=None, keywords=None, defaults=[\'None\'], " } - member_method { - name: "shard" - argspec: "args=[\'self\', \'num_shards\', \'index\'], varargs=None, keywords=None, defaults=None" - } member_method { name: "shuffle" argspec: "args=[\'self\', \'buffer_size\', \'seed\', \'reshuffle_each_iteration\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.data.-iterator.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.data.-iterator.pbtxt deleted file mode 100644 index 4f0147a523..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.data.-iterator.pbtxt +++ /dev/null @@ -1,46 +0,0 @@ -path: "tensorflow.data.Iterator" -tf_class { - is_instance: "" - is_instance: "" - is_instance: "" - member { - name: "initializer" - mtype: "" - } - member { - name: "output_classes" - mtype: "" - } - member { - name: "output_shapes" - mtype: "" - } - member { - name: "output_types" - mtype: "" - } - member_method { - name: "__init__" - argspec: "args=[\'self\', \'iterator_resource\', \'initializer\', \'output_types\', \'output_shapes\', \'output_classes\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "from_string_handle" - argspec: "args=[\'string_handle\', \'output_types\', \'output_shapes\', \'output_classes\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " - } - member_method { - name: "from_structure" - argspec: "args=[\'output_types\', \'output_shapes\', \'shared_name\', \'output_classes\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " - } - member_method { - name: "get_next" - argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "make_initializer" - argspec: "args=[\'self\', \'dataset\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "string_handle" - argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.data.-options.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.data.-options.pbtxt index 9d032d43de..72fc2c3a9e 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.data.-options.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.data.-options.pbtxt @@ -1,6 +1,7 @@ path: "tensorflow.data.Options" tf_class { is_instance: "" + is_instance: "" is_instance: "" member { name: "experimental_autotune" @@ -11,47 +12,19 @@ tf_class { mtype: "" } member { - name: "experimental_filter_fusion" - mtype: "" - } - member { - name: "experimental_hoist_random_uniform" - mtype: "" - } - member { - name: "experimental_map_and_batch_fusion" - mtype: "" - } - member { - name: "experimental_map_and_filter_fusion" - mtype: "" - } - member { - name: "experimental_map_fusion" - mtype: "" - } - member { - name: "experimental_map_parallelization" - mtype: "" - } - member { - name: "experimental_map_vectorization" - mtype: "" - } - member { - name: "experimental_noop_elimination" + name: "experimental_numa_aware" mtype: "" } member { - name: "experimental_numa_aware" + name: "experimental_optimization" mtype: "" } member { - name: "experimental_shuffle_and_repeat_fusion" + name: "experimental_stats" mtype: "" } member { - name: "experimental_stats" + name: "experimental_threading" mtype: "" } member_method { diff --git a/tensorflow/tools/api/golden/v2/tensorflow.data.-t-f-record-dataset.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.data.-t-f-record-dataset.pbtxt index 7b7a9ebaf0..690da98b1a 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.data.-t-f-record-dataset.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.data.-t-f-record-dataset.pbtxt @@ -1,7 +1,7 @@ path: "tensorflow.data.TFRecordDataset" tf_class { - is_instance: "" - is_instance: "" + is_instance: "" + is_instance: "" is_instance: "" member { name: "output_classes" @@ -47,10 +47,6 @@ tf_class { name: "from_generator" argspec: "args=[\'generator\', \'output_types\', \'output_shapes\', \'args\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } - member_method { - name: "from_sparse_tensor_slices" - argspec: "args=[\'sparse_tensor\'], varargs=None, keywords=None, defaults=None" - } member_method { name: "from_tensor_slices" argspec: "args=[\'tensors\'], varargs=None, keywords=None, defaults=None" @@ -67,14 +63,6 @@ tf_class { name: "list_files" argspec: "args=[\'file_pattern\', \'shuffle\', \'seed\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } - member_method { - name: "make_initializable_iterator" - argspec: "args=[\'self\', \'shared_name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "make_one_shot_iterator" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } member_method { name: "map" argspec: "args=[\'self\', \'map_func\', \'num_parallel_calls\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -103,10 +91,6 @@ tf_class { name: "repeat" argspec: "args=[\'self\', \'count\'], varargs=None, keywords=None, defaults=[\'None\'], " } - member_method { - name: "shard" - argspec: "args=[\'self\', \'num_shards\', \'index\'], varargs=None, keywords=None, defaults=None" - } member_method { name: "shuffle" argspec: "args=[\'self\', \'buffer_size\', \'seed\', \'reshuffle_each_iteration\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.data.-text-line-dataset.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.data.-text-line-dataset.pbtxt index 1c305abf68..fe0bc1a4db 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.data.-text-line-dataset.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.data.-text-line-dataset.pbtxt @@ -1,8 +1,8 @@ path: "tensorflow.data.TextLineDataset" tf_class { - is_instance: "" + is_instance: "" is_instance: "" - is_instance: "" + is_instance: "" is_instance: "" member { name: "output_classes" @@ -48,10 +48,6 @@ tf_class { name: "from_generator" argspec: "args=[\'generator\', \'output_types\', \'output_shapes\', \'args\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } - member_method { - name: "from_sparse_tensor_slices" - argspec: "args=[\'sparse_tensor\'], varargs=None, keywords=None, defaults=None" - } member_method { name: "from_tensor_slices" argspec: "args=[\'tensors\'], varargs=None, keywords=None, defaults=None" @@ -68,14 +64,6 @@ tf_class { name: "list_files" argspec: "args=[\'file_pattern\', \'shuffle\', \'seed\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } - member_method { - name: "make_initializable_iterator" - argspec: "args=[\'self\', \'shared_name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "make_one_shot_iterator" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } member_method { name: "map" argspec: "args=[\'self\', \'map_func\', \'num_parallel_calls\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -104,10 +92,6 @@ tf_class { name: "repeat" argspec: "args=[\'self\', \'count\'], varargs=None, keywords=None, defaults=[\'None\'], " } - member_method { - name: "shard" - argspec: "args=[\'self\', \'num_shards\', \'index\'], varargs=None, keywords=None, defaults=None" - } member_method { name: "shuffle" argspec: "args=[\'self\', \'buffer_size\', \'seed\', \'reshuffle_each_iteration\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.-csv-dataset.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.-csv-dataset.pbtxt index 2520e28a3c..261129b132 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.-csv-dataset.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.-csv-dataset.pbtxt @@ -1,8 +1,8 @@ path: "tensorflow.data.experimental.CsvDataset" tf_class { - is_instance: "" + is_instance: "" is_instance: "" - is_instance: "" + is_instance: "" is_instance: "" member { name: "output_classes" @@ -48,10 +48,6 @@ tf_class { name: "from_generator" argspec: "args=[\'generator\', \'output_types\', \'output_shapes\', \'args\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } - member_method { - name: "from_sparse_tensor_slices" - argspec: "args=[\'sparse_tensor\'], varargs=None, keywords=None, defaults=None" - } member_method { name: "from_tensor_slices" argspec: "args=[\'tensors\'], varargs=None, keywords=None, defaults=None" @@ -68,14 +64,6 @@ tf_class { name: "list_files" argspec: "args=[\'file_pattern\', \'shuffle\', \'seed\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } - member_method { - name: "make_initializable_iterator" - argspec: "args=[\'self\', \'shared_name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "make_one_shot_iterator" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } member_method { name: "map" argspec: "args=[\'self\', \'map_func\', \'num_parallel_calls\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -104,10 +92,6 @@ tf_class { name: "repeat" argspec: "args=[\'self\', \'count\'], varargs=None, keywords=None, defaults=[\'None\'], " } - member_method { - name: "shard" - argspec: "args=[\'self\', \'num_shards\', \'index\'], varargs=None, keywords=None, defaults=None" - } member_method { name: "shuffle" argspec: "args=[\'self\', \'buffer_size\', \'seed\', \'reshuffle_each_iteration\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.-optimization-options.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.-optimization-options.pbtxt new file mode 100644 index 0000000000..9ca75828e5 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.-optimization-options.pbtxt @@ -0,0 +1,46 @@ +path: "tensorflow.data.experimental.OptimizationOptions" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "filter_fusion" + mtype: "" + } + member { + name: "hoist_random_uniform" + mtype: "" + } + member { + name: "map_and_batch_fusion" + mtype: "" + } + member { + name: "map_and_filter_fusion" + mtype: "" + } + member { + name: "map_fusion" + mtype: "" + } + member { + name: "map_parallelization" + mtype: "" + } + member { + name: "map_vectorization" + mtype: "" + } + member { + name: "noop_elimination" + mtype: "" + } + member { + name: "shuffle_and_repeat_fusion" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.-random-dataset.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.-random-dataset.pbtxt index 1dd53b1eab..0b34bbc942 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.-random-dataset.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.-random-dataset.pbtxt @@ -1,8 +1,8 @@ path: "tensorflow.data.experimental.RandomDataset" tf_class { - is_instance: "" + is_instance: "" is_instance: "" - is_instance: "" + is_instance: "" is_instance: "" member { name: "output_classes" @@ -48,10 +48,6 @@ tf_class { name: "from_generator" argspec: "args=[\'generator\', \'output_types\', \'output_shapes\', \'args\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } - member_method { - name: "from_sparse_tensor_slices" - argspec: "args=[\'sparse_tensor\'], varargs=None, keywords=None, defaults=None" - } member_method { name: "from_tensor_slices" argspec: "args=[\'tensors\'], varargs=None, keywords=None, defaults=None" @@ -68,14 +64,6 @@ tf_class { name: "list_files" argspec: "args=[\'file_pattern\', \'shuffle\', \'seed\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } - member_method { - name: "make_initializable_iterator" - argspec: "args=[\'self\', \'shared_name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "make_one_shot_iterator" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } member_method { name: "map" argspec: "args=[\'self\', \'map_func\', \'num_parallel_calls\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -104,10 +92,6 @@ tf_class { name: "repeat" argspec: "args=[\'self\', \'count\'], varargs=None, keywords=None, defaults=[\'None\'], " } - member_method { - name: "shard" - argspec: "args=[\'self\', \'num_shards\', \'index\'], varargs=None, keywords=None, defaults=None" - } member_method { name: "shuffle" argspec: "args=[\'self\', \'buffer_size\', \'seed\', \'reshuffle_each_iteration\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.-sql-dataset.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.-sql-dataset.pbtxt index 8fdd9dc52e..0e61890eee 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.-sql-dataset.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.-sql-dataset.pbtxt @@ -1,8 +1,8 @@ path: "tensorflow.data.experimental.SqlDataset" tf_class { - is_instance: "" + is_instance: "" is_instance: "" - is_instance: "" + is_instance: "" is_instance: "" member { name: "output_classes" @@ -48,10 +48,6 @@ tf_class { name: "from_generator" argspec: "args=[\'generator\', \'output_types\', \'output_shapes\', \'args\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } - member_method { - name: "from_sparse_tensor_slices" - argspec: "args=[\'sparse_tensor\'], varargs=None, keywords=None, defaults=None" - } member_method { name: "from_tensor_slices" argspec: "args=[\'tensors\'], varargs=None, keywords=None, defaults=None" @@ -68,14 +64,6 @@ tf_class { name: "list_files" argspec: "args=[\'file_pattern\', \'shuffle\', \'seed\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } - member_method { - name: "make_initializable_iterator" - argspec: "args=[\'self\', \'shared_name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "make_one_shot_iterator" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } member_method { name: "map" argspec: "args=[\'self\', \'map_func\', \'num_parallel_calls\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -104,10 +92,6 @@ tf_class { name: "repeat" argspec: "args=[\'self\', \'count\'], varargs=None, keywords=None, defaults=[\'None\'], " } - member_method { - name: "shard" - argspec: "args=[\'self\', \'num_shards\', \'index\'], varargs=None, keywords=None, defaults=None" - } member_method { name: "shuffle" argspec: "args=[\'self\', \'buffer_size\', \'seed\', \'reshuffle_each_iteration\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.-stats-options.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.-stats-options.pbtxt index f423eed42c..892f8c1fb8 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.-stats-options.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.-stats-options.pbtxt @@ -1,6 +1,7 @@ path: "tensorflow.data.experimental.StatsOptions" tf_class { is_instance: "" + is_instance: "" is_instance: "" member { name: "aggregator" @@ -20,6 +21,6 @@ tf_class { } member_method { name: "__init__" - argspec: "args=[\'self\', \'aggregator\'], varargs=None, keywords=None, defaults=[\'None\'], " + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" } } diff --git a/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.-threading-options.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.-threading-options.pbtxt new file mode 100644 index 0000000000..5b5ebf1080 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.-threading-options.pbtxt @@ -0,0 +1,18 @@ +path: "tensorflow.data.experimental.ThreadingOptions" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "max_intra_op_parallelism" + mtype: "" + } + member { + name: "private_threadpool_size" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.pbtxt index 4c253bb8ad..f981b1af17 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.pbtxt @@ -12,6 +12,14 @@ tf_module { name: "CsvDataset" mtype: "" } + member { + name: "INFINITE_CARDINALITY" + mtype: "" + } + member { + name: "OptimizationOptions" + mtype: "" + } member { name: "Optional" mtype: "" @@ -40,6 +48,14 @@ tf_module { name: "TFRecordWriter" mtype: "" } + member { + name: "ThreadingOptions" + mtype: "" + } + member { + name: "UNKNOWN_CARDINALITY" + mtype: "" + } member_method { name: "Counter" argspec: "args=[\'start\', \'step\', \'dtype\'], varargs=None, keywords=None, defaults=[\'0\', \'1\', \"\"], " @@ -48,6 +64,10 @@ tf_module { name: "bucket_by_sequence_length" argspec: "args=[\'element_length_func\', \'bucket_boundaries\', \'bucket_batch_sizes\', \'padded_shapes\', \'padding_values\', \'pad_to_bucket_boundary\', \'no_padding\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'False\', \'False\'], " } + member_method { + name: "cardinality" + argspec: "args=[\'dataset\'], varargs=None, keywords=None, defaults=None" + } member_method { name: "choose_from_datasets" argspec: "args=[\'datasets\', \'choice_dataset\'], varargs=None, keywords=None, defaults=None" @@ -64,6 +84,10 @@ tf_module { name: "enumerate_dataset" argspec: "args=[\'start\'], varargs=None, keywords=None, defaults=[\'0\'], " } + member_method { + name: "filter_for_shard" + argspec: "args=[\'num_shards\', \'shard_index\'], varargs=None, keywords=None, defaults=None" + } member_method { name: "get_next_as_optional" argspec: "args=[\'iterator\'], varargs=None, keywords=None, defaults=None" @@ -90,7 +114,7 @@ tf_module { } member_method { name: "make_batched_features_dataset" - argspec: "args=[\'file_pattern\', \'batch_size\', \'features\', \'reader\', \'label_key\', \'reader_args\', \'num_epochs\', \'shuffle\', \'shuffle_buffer_size\', \'shuffle_seed\', \'prefetch_buffer_size\', \'reader_num_threads\', \'parser_num_threads\', \'sloppy_ordering\', \'drop_final_batch\'], varargs=None, keywords=None, defaults=[\"\", \'None\', \'None\', \'None\', \'True\', \'10000\', \'None\', \'-1\', \'1\', \'2\', \'False\', \'False\'], " + argspec: "args=[\'file_pattern\', \'batch_size\', \'features\', \'reader\', \'label_key\', \'reader_args\', \'num_epochs\', \'shuffle\', \'shuffle_buffer_size\', \'shuffle_seed\', \'prefetch_buffer_size\', \'reader_num_threads\', \'parser_num_threads\', \'sloppy_ordering\', \'drop_final_batch\'], varargs=None, keywords=None, defaults=[\"\", \'None\', \'None\', \'None\', \'True\', \'10000\', \'None\', \'-1\', \'1\', \'2\', \'False\', \'False\'], " } member_method { name: "make_csv_dataset" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.data.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.data.pbtxt index 509bbae833..4c3d6ddd85 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.data.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.data.pbtxt @@ -8,10 +8,6 @@ tf_module { name: "FixedLengthRecordDataset" mtype: "" } - member { - name: "Iterator" - mtype: "" - } member { name: "Options" mtype: "" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.debugging.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.debugging.pbtxt index ab6287f8cd..314aedda90 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.debugging.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.debugging.pbtxt @@ -6,19 +6,19 @@ tf_module { } member_method { name: "assert_all_finite" - argspec: "args=[\'t\', \'msg\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + argspec: "args=[\'x\', \'message\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { name: "assert_equal" - argspec: "args=[\'x\', \'y\', \'data\', \'summarize\', \'message\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'x\', \'y\', \'message\', \'summarize\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " } member_method { name: "assert_greater" - argspec: "args=[\'x\', \'y\', \'data\', \'summarize\', \'message\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'x\', \'y\', \'message\', \'summarize\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " } member_method { name: "assert_greater_equal" - argspec: "args=[\'x\', \'y\', \'data\', \'summarize\', \'message\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'x\', \'y\', \'message\', \'summarize\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " } member_method { name: "assert_integer" @@ -26,35 +26,35 @@ tf_module { } member_method { name: "assert_less" - argspec: "args=[\'x\', \'y\', \'data\', \'summarize\', \'message\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'x\', \'y\', \'message\', \'summarize\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " } member_method { name: "assert_less_equal" - argspec: "args=[\'x\', \'y\', \'data\', \'summarize\', \'message\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'x\', \'y\', \'message\', \'summarize\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " } member_method { name: "assert_near" - argspec: "args=[\'x\', \'y\', \'rtol\', \'atol\', \'data\', \'summarize\', \'message\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'x\', \'y\', \'rtol\', \'atol\', \'message\', \'summarize\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " } member_method { name: "assert_negative" - argspec: "args=[\'x\', \'data\', \'summarize\', \'message\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'x\', \'message\', \'summarize\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " } member_method { name: "assert_non_negative" - argspec: "args=[\'x\', \'data\', \'summarize\', \'message\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'x\', \'message\', \'summarize\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " } member_method { name: "assert_non_positive" - argspec: "args=[\'x\', \'data\', \'summarize\', \'message\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'x\', \'message\', \'summarize\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " } member_method { name: "assert_none_equal" - argspec: "args=[\'x\', \'y\', \'data\', \'summarize\', \'message\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'x\', \'y\', \'summarize\', \'message\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " } member_method { name: "assert_positive" - argspec: "args=[\'x\', \'data\', \'summarize\', \'message\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'x\', \'message\', \'summarize\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " } member_method { name: "assert_proper_iterable" @@ -62,15 +62,15 @@ tf_module { } member_method { name: "assert_rank" - argspec: "args=[\'x\', \'rank\', \'data\', \'summarize\', \'message\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'x\', \'rank\', \'message\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { name: "assert_rank_at_least" - argspec: "args=[\'x\', \'rank\', \'data\', \'summarize\', \'message\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'x\', \'rank\', \'message\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { name: "assert_rank_in" - argspec: "args=[\'x\', \'ranks\', \'data\', \'summarize\', \'message\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'x\', \'ranks\', \'message\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { name: "assert_same_float_dtype" @@ -78,7 +78,7 @@ tf_module { } member_method { name: "assert_scalar" - argspec: "args=[\'tensor\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + argspec: "args=[\'tensor\', \'message\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { name: "assert_type" @@ -88,28 +88,8 @@ tf_module { name: "check_numerics" argspec: "args=[\'tensor\', \'message\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } - member_method { - name: "is_finite" - argspec: "args=[\'x\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "is_inf" - argspec: "args=[\'x\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "is_nan" - argspec: "args=[\'x\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "is_non_decreasing" - argspec: "args=[\'x\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } member_method { name: "is_numeric_tensor" argspec: "args=[\'tensor\'], varargs=None, keywords=None, defaults=None" } - member_method { - name: "is_strictly_increasing" - argspec: "args=[\'x\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } } diff --git a/tensorflow/tools/api/golden/v2/tensorflow.distribute.-input-context.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.distribute.-input-context.pbtxt new file mode 100644 index 0000000000..583cbc6654 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.distribute.-input-context.pbtxt @@ -0,0 +1,25 @@ +path: "tensorflow.distribute.InputContext" +tf_class { + is_instance: "" + is_instance: "" + member { + name: "input_pipeline_id" + mtype: "" + } + member { + name: "num_input_pipelines" + mtype: "" + } + member { + name: "num_replicas_in_sync" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'num_input_pipelines\', \'input_pipeline_id\', \'num_replicas_in_sync\'], varargs=None, keywords=None, defaults=[\'1\', \'0\', \'1\'], " + } + member_method { + name: "get_per_replica_batch_size" + argspec: "args=[\'self\', \'global_batch_size\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.distribute.-input-replication-mode.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.distribute.-input-replication-mode.pbtxt new file mode 100644 index 0000000000..6a7a3a97aa --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.distribute.-input-replication-mode.pbtxt @@ -0,0 +1,8 @@ +path: "tensorflow.distribute.InputReplicationMode" +tf_class { + is_instance: "" + member { + name: "PER_WORKER" + mtype: "" + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.distribute.-reduce-op.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.distribute.-reduce-op.pbtxt new file mode 100644 index 0000000000..4899f38cad --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.distribute.-reduce-op.pbtxt @@ -0,0 +1,12 @@ +path: "tensorflow.distribute.ReduceOp" +tf_class { + is_instance: "" + member { + name: "MEAN" + mtype: "" + } + member { + name: "SUM" + mtype: "" + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.distribute.-replica-context.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.distribute.-replica-context.pbtxt new file mode 100644 index 0000000000..df707e8920 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.distribute.-replica-context.pbtxt @@ -0,0 +1,33 @@ +path: "tensorflow.distribute.ReplicaContext" +tf_class { + is_instance: "" + is_instance: "" + member { + name: "devices" + mtype: "" + } + member { + name: "distribution_strategy" + mtype: "" + } + member { + name: "num_replicas_in_sync" + mtype: "" + } + member { + name: "replica_id_in_sync_group" + mtype: "" + } + member { + name: "strategy" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'strategy\', \'replica_id_in_sync_group\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "merge_call" + argspec: "args=[\'self\', \'merge_fn\', \'args\', \'kwargs\'], varargs=None, keywords=None, defaults=[\'()\', \'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.distribute.-strategy-extended.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.distribute.-strategy-extended.pbtxt new file mode 100644 index 0000000000..77706e5713 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.distribute.-strategy-extended.pbtxt @@ -0,0 +1,81 @@ +path: "tensorflow.distribute.StrategyExtended" +tf_class { + is_instance: "" + is_instance: "" + member { + name: "experimental_between_graph" + mtype: "" + } + member { + name: "experimental_require_static_shapes" + mtype: "" + } + member { + name: "experimental_should_init" + mtype: "" + } + member { + name: "parameter_devices" + mtype: "" + } + member { + name: "should_checkpoint" + mtype: "" + } + member { + name: "should_save_summary" + mtype: "" + } + member { + name: "worker_devices" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'container_strategy\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "batch_reduce_to" + argspec: "args=[\'self\', \'reduce_op\', \'value_destination_pairs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "broadcast_to" + argspec: "args=[\'self\', \'tensor\', \'destinations\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call_for_each_replica" + argspec: "args=[\'self\', \'fn\', \'args\', \'kwargs\'], varargs=None, keywords=None, defaults=[\'()\', \'None\'], " + } + member_method { + name: "colocate_vars_with" + argspec: "args=[\'self\', \'colocate_with_variable\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "experimental_run_steps_on_iterator" + argspec: "args=[\'self\', \'fn\', \'iterator\', \'iterations\', \'initial_loop_values\'], varargs=None, keywords=None, defaults=[\'1\', \'None\'], " + } + member_method { + name: "non_slot_devices" + argspec: "args=[\'self\', \'var_list\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "read_var" + argspec: "args=[\'self\', \'v\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reduce_to" + argspec: "args=[\'self\', \'reduce_op\', \'value\', \'destinations\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update" + argspec: "args=[\'self\', \'var\', \'fn\', \'args\', \'kwargs\', \'group\'], varargs=None, keywords=None, defaults=[\'()\', \'None\', \'True\'], " + } + member_method { + name: "update_non_slot" + argspec: "args=[\'self\', \'colocate_with\', \'fn\', \'args\', \'kwargs\', \'group\'], varargs=None, keywords=None, defaults=[\'()\', \'None\', \'True\'], " + } + member_method { + name: "value_container" + argspec: "args=[\'self\', \'value\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.distribute.-strategy.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.distribute.-strategy.pbtxt new file mode 100644 index 0000000000..9eb73d2c0d --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.distribute.-strategy.pbtxt @@ -0,0 +1,137 @@ +path: "tensorflow.distribute.Strategy" +tf_class { + is_instance: "" + is_instance: "" + member { + name: "between_graph" + mtype: "" + } + member { + name: "extended" + mtype: "" + } + member { + name: "num_replicas_in_sync" + mtype: "" + } + member { + name: "parameter_devices" + mtype: "" + } + member { + name: "require_static_shapes" + mtype: "" + } + member { + name: "should_checkpoint" + mtype: "" + } + member { + name: "should_init" + mtype: "" + } + member { + name: "should_save_summary" + mtype: "" + } + member { + name: "worker_devices" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'extended\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "batch_reduce" + argspec: "args=[\'self\', \'aggregation\', \'value_destination_pairs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "broadcast" + argspec: "args=[\'self\', \'tensor\', \'destinations\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "call_for_each_replica" + argspec: "args=[\'self\', \'fn\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "colocate_vars_with" + argspec: "args=[\'self\', \'colocate_with_variable\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "configure" + argspec: "args=[\'self\', \'session_config\', \'cluster_spec\', \'task_type\', \'task_id\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + } + member_method { + name: "distribute_dataset" + argspec: "args=[\'self\', \'dataset_fn\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "experimental_finalize" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "experimental_initialize" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "finalize" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "group" + argspec: "args=[\'self\', \'value\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "initialize" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "make_dataset_iterator" + argspec: "args=[\'self\', \'dataset\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "make_input_fn_iterator" + argspec: "args=[\'self\', \'input_fn\', \'replication_mode\'], varargs=None, keywords=None, defaults=[\'InputReplicationMode.PER_WORKER\'], " + } + member_method { + name: "non_slot_devices" + argspec: "args=[\'self\', \'var_list\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "read_var" + argspec: "args=[\'self\', \'v\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reduce" + argspec: "args=[\'self\', \'reduce_op\', \'value\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "run_steps_on_dataset" + argspec: "args=[\'self\', \'fn\', \'iterator\', \'iterations\', \'initial_loop_values\'], varargs=None, keywords=None, defaults=[\'1\', \'None\'], " + } + member_method { + name: "scope" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "unwrap" + argspec: "args=[\'self\', \'value\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update" + argspec: "args=[\'self\', \'var\', \'fn\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "update_config_proto" + argspec: "args=[\'self\', \'config_proto\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_non_slot" + argspec: "args=[\'self\', \'colocate_with\', \'fn\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "value_container" + argspec: "args=[\'self\', \'value\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.distribute.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.distribute.pbtxt new file mode 100644 index 0000000000..4d833b54ba --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.distribute.pbtxt @@ -0,0 +1,47 @@ +path: "tensorflow.distribute" +tf_module { + member { + name: "InputContext" + mtype: "" + } + member { + name: "InputReplicationMode" + mtype: "" + } + member { + name: "ReduceOp" + mtype: "" + } + member { + name: "ReplicaContext" + mtype: "" + } + member { + name: "Strategy" + mtype: "" + } + member { + name: "StrategyExtended" + mtype: "" + } + member_method { + name: "get_loss_reduction" + argspec: "args=[], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_replica_context" + argspec: "args=[], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_strategy" + argspec: "args=[], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "has_strategy" + argspec: "args=[], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "in_cross_replica_context" + argspec: "args=[], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-baseline-classifier.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-baseline-classifier.pbtxt index 32b84e90ce..efe9e74697 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-baseline-classifier.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-baseline-classifier.pbtxt @@ -1,7 +1,7 @@ path: "tensorflow.estimator.BaselineClassifier" tf_class { - is_instance: "" - is_instance: "" + is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -21,7 +21,7 @@ tf_class { } member_method { name: "__init__" - argspec: "args=[\'self\', \'model_dir\', \'n_classes\', \'weight_column\', \'label_vocabulary\', \'optimizer\', \'config\', \'loss_reduction\'], varargs=None, keywords=None, defaults=[\'None\', \'2\', \'None\', \'None\', \'Ftrl\', \'None\', \'weighted_sum\'], " + argspec: "args=[\'self\', \'model_dir\', \'n_classes\', \'weight_column\', \'label_vocabulary\', \'optimizer\', \'config\', \'loss_reduction\'], varargs=None, keywords=None, defaults=[\'None\', \'2\', \'None\', \'None\', \'Ftrl\', \'None\', \'weighted_sum_over_batch_size\'], " } member_method { name: "eval_dir" @@ -32,12 +32,12 @@ tf_class { argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } member_method { - name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { - name: "export_savedmodel" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'strip_default_attrs\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'False\'], " + name: "export_saved_model" + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "get_variable_names" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-baseline-estimator.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-baseline-estimator.pbtxt index 94933e7ffd..382d392f39 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-baseline-estimator.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-baseline-estimator.pbtxt @@ -1,7 +1,7 @@ path: "tensorflow.estimator.BaselineEstimator" tf_class { - is_instance: "" - is_instance: "" + is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -32,12 +32,12 @@ tf_class { argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } member_method { - name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { - name: "export_savedmodel" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'strip_default_attrs\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'False\'], " + name: "export_saved_model" + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "get_variable_names" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-baseline-regressor.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-baseline-regressor.pbtxt index db7776b5bf..a7300bf06b 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-baseline-regressor.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-baseline-regressor.pbtxt @@ -1,7 +1,7 @@ path: "tensorflow.estimator.BaselineRegressor" tf_class { - is_instance: "" - is_instance: "" + is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -21,7 +21,7 @@ tf_class { } member_method { name: "__init__" - argspec: "args=[\'self\', \'model_dir\', \'label_dimension\', \'weight_column\', \'optimizer\', \'config\', \'loss_reduction\'], varargs=None, keywords=None, defaults=[\'None\', \'1\', \'None\', \'Ftrl\', \'None\', \'weighted_sum\'], " + argspec: "args=[\'self\', \'model_dir\', \'label_dimension\', \'weight_column\', \'optimizer\', \'config\', \'loss_reduction\'], varargs=None, keywords=None, defaults=[\'None\', \'1\', \'None\', \'Ftrl\', \'None\', \'weighted_sum_over_batch_size\'], " } member_method { name: "eval_dir" @@ -32,12 +32,12 @@ tf_class { argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } member_method { - name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { - name: "export_savedmodel" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'strip_default_attrs\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'False\'], " + name: "export_saved_model" + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "get_variable_names" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-boosted-trees-classifier.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-boosted-trees-classifier.pbtxt index d530c71482..e138ce936e 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-boosted-trees-classifier.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-boosted-trees-classifier.pbtxt @@ -3,6 +3,7 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -32,6 +33,10 @@ tf_class { name: "evaluate" argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } + member_method { + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + } member_method { name: "experimental_feature_importances" argspec: "args=[\'self\', \'normalize\'], varargs=None, keywords=None, defaults=[\'False\'], " @@ -42,7 +47,7 @@ tf_class { } member_method { name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "export_savedmodel" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-boosted-trees-regressor.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-boosted-trees-regressor.pbtxt index 4703c0f561..eae0a292a9 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-boosted-trees-regressor.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-boosted-trees-regressor.pbtxt @@ -3,6 +3,7 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -32,6 +33,10 @@ tf_class { name: "evaluate" argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } + member_method { + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + } member_method { name: "experimental_feature_importances" argspec: "args=[\'self\', \'normalize\'], varargs=None, keywords=None, defaults=[\'False\'], " @@ -42,7 +47,7 @@ tf_class { } member_method { name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "export_savedmodel" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-d-n-n-classifier.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-d-n-n-classifier.pbtxt index ce6040d0f2..a540085aba 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-d-n-n-classifier.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-d-n-n-classifier.pbtxt @@ -1,7 +1,7 @@ path: "tensorflow.estimator.DNNClassifier" tf_class { - is_instance: "" - is_instance: "" + is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -21,7 +21,7 @@ tf_class { } member_method { name: "__init__" - argspec: "args=[\'self\', \'hidden_units\', \'feature_columns\', \'model_dir\', \'n_classes\', \'weight_column\', \'label_vocabulary\', \'optimizer\', \'activation_fn\', \'dropout\', \'input_layer_partitioner\', \'config\', \'warm_start_from\', \'loss_reduction\', \'batch_norm\'], varargs=None, keywords=None, defaults=[\'None\', \'2\', \'None\', \'None\', \'Adagrad\', \'\', \'None\', \'None\', \'None\', \'None\', \'weighted_sum\', \'False\'], " + argspec: "args=[\'self\', \'hidden_units\', \'feature_columns\', \'model_dir\', \'n_classes\', \'weight_column\', \'label_vocabulary\', \'optimizer\', \'activation_fn\', \'dropout\', \'input_layer_partitioner\', \'config\', \'warm_start_from\', \'loss_reduction\', \'batch_norm\'], varargs=None, keywords=None, defaults=[\'None\', \'2\', \'None\', \'None\', \'Adagrad\', \'\', \'None\', \'None\', \'None\', \'None\', \'weighted_sum_over_batch_size\', \'False\'], " } member_method { name: "eval_dir" @@ -32,12 +32,12 @@ tf_class { argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } member_method { - name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { - name: "export_savedmodel" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'strip_default_attrs\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'False\'], " + name: "export_saved_model" + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "get_variable_names" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-d-n-n-estimator.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-d-n-n-estimator.pbtxt index 4635a1544c..d1b29d670a 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-d-n-n-estimator.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-d-n-n-estimator.pbtxt @@ -1,7 +1,7 @@ path: "tensorflow.estimator.DNNEstimator" tf_class { - is_instance: "" - is_instance: "" + is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -32,12 +32,12 @@ tf_class { argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } member_method { - name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { - name: "export_savedmodel" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'strip_default_attrs\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'False\'], " + name: "export_saved_model" + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "get_variable_names" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-d-n-n-linear-combined-classifier.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-d-n-n-linear-combined-classifier.pbtxt index e85007e16e..f6c3910a9f 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-d-n-n-linear-combined-classifier.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-d-n-n-linear-combined-classifier.pbtxt @@ -1,7 +1,7 @@ path: "tensorflow.estimator.DNNLinearCombinedClassifier" tf_class { - is_instance: "" - is_instance: "" + is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -21,7 +21,7 @@ tf_class { } member_method { name: "__init__" - argspec: "args=[\'self\', \'model_dir\', \'linear_feature_columns\', \'linear_optimizer\', \'dnn_feature_columns\', \'dnn_optimizer\', \'dnn_hidden_units\', \'dnn_activation_fn\', \'dnn_dropout\', \'n_classes\', \'weight_column\', \'label_vocabulary\', \'input_layer_partitioner\', \'config\', \'warm_start_from\', \'loss_reduction\', \'batch_norm\', \'linear_sparse_combiner\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'Ftrl\', \'None\', \'Adagrad\', \'None\', \'\', \'None\', \'2\', \'None\', \'None\', \'None\', \'None\', \'None\', \'weighted_sum\', \'False\', \'sum\'], " + argspec: "args=[\'self\', \'model_dir\', \'linear_feature_columns\', \'linear_optimizer\', \'dnn_feature_columns\', \'dnn_optimizer\', \'dnn_hidden_units\', \'dnn_activation_fn\', \'dnn_dropout\', \'n_classes\', \'weight_column\', \'label_vocabulary\', \'input_layer_partitioner\', \'config\', \'warm_start_from\', \'loss_reduction\', \'batch_norm\', \'linear_sparse_combiner\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'Ftrl\', \'None\', \'Adagrad\', \'None\', \'\', \'None\', \'2\', \'None\', \'None\', \'None\', \'None\', \'None\', \'weighted_sum_over_batch_size\', \'False\', \'sum\'], " } member_method { name: "eval_dir" @@ -32,12 +32,12 @@ tf_class { argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } member_method { - name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { - name: "export_savedmodel" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'strip_default_attrs\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'False\'], " + name: "export_saved_model" + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "get_variable_names" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-d-n-n-linear-combined-estimator.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-d-n-n-linear-combined-estimator.pbtxt index a23f5daeac..b78527279c 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-d-n-n-linear-combined-estimator.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-d-n-n-linear-combined-estimator.pbtxt @@ -1,7 +1,7 @@ path: "tensorflow.estimator.DNNLinearCombinedEstimator" tf_class { - is_instance: "" - is_instance: "" + is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -32,12 +32,12 @@ tf_class { argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } member_method { - name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { - name: "export_savedmodel" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'strip_default_attrs\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'False\'], " + name: "export_saved_model" + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "get_variable_names" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-d-n-n-linear-combined-regressor.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-d-n-n-linear-combined-regressor.pbtxt index 8a55bb835f..9133f0d3b2 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-d-n-n-linear-combined-regressor.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-d-n-n-linear-combined-regressor.pbtxt @@ -1,7 +1,7 @@ path: "tensorflow.estimator.DNNLinearCombinedRegressor" tf_class { - is_instance: "" - is_instance: "" + is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -21,7 +21,7 @@ tf_class { } member_method { name: "__init__" - argspec: "args=[\'self\', \'model_dir\', \'linear_feature_columns\', \'linear_optimizer\', \'dnn_feature_columns\', \'dnn_optimizer\', \'dnn_hidden_units\', \'dnn_activation_fn\', \'dnn_dropout\', \'label_dimension\', \'weight_column\', \'input_layer_partitioner\', \'config\', \'warm_start_from\', \'loss_reduction\', \'batch_norm\', \'linear_sparse_combiner\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'Ftrl\', \'None\', \'Adagrad\', \'None\', \'\', \'None\', \'1\', \'None\', \'None\', \'None\', \'None\', \'weighted_sum\', \'False\', \'sum\'], " + argspec: "args=[\'self\', \'model_dir\', \'linear_feature_columns\', \'linear_optimizer\', \'dnn_feature_columns\', \'dnn_optimizer\', \'dnn_hidden_units\', \'dnn_activation_fn\', \'dnn_dropout\', \'label_dimension\', \'weight_column\', \'input_layer_partitioner\', \'config\', \'warm_start_from\', \'loss_reduction\', \'batch_norm\', \'linear_sparse_combiner\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'Ftrl\', \'None\', \'Adagrad\', \'None\', \'\', \'None\', \'1\', \'None\', \'None\', \'None\', \'None\', \'weighted_sum_over_batch_size\', \'False\', \'sum\'], " } member_method { name: "eval_dir" @@ -32,12 +32,12 @@ tf_class { argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } member_method { - name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { - name: "export_savedmodel" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'strip_default_attrs\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'False\'], " + name: "export_saved_model" + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "get_variable_names" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-d-n-n-regressor.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-d-n-n-regressor.pbtxt index 2c4128ec48..a58d733302 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-d-n-n-regressor.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-d-n-n-regressor.pbtxt @@ -1,7 +1,7 @@ path: "tensorflow.estimator.DNNRegressor" tf_class { - is_instance: "" - is_instance: "" + is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -21,7 +21,7 @@ tf_class { } member_method { name: "__init__" - argspec: "args=[\'self\', \'hidden_units\', \'feature_columns\', \'model_dir\', \'label_dimension\', \'weight_column\', \'optimizer\', \'activation_fn\', \'dropout\', \'input_layer_partitioner\', \'config\', \'warm_start_from\', \'loss_reduction\', \'batch_norm\'], varargs=None, keywords=None, defaults=[\'None\', \'1\', \'None\', \'Adagrad\', \'\', \'None\', \'None\', \'None\', \'None\', \'weighted_sum\', \'False\'], " + argspec: "args=[\'self\', \'hidden_units\', \'feature_columns\', \'model_dir\', \'label_dimension\', \'weight_column\', \'optimizer\', \'activation_fn\', \'dropout\', \'input_layer_partitioner\', \'config\', \'warm_start_from\', \'loss_reduction\', \'batch_norm\'], varargs=None, keywords=None, defaults=[\'None\', \'1\', \'None\', \'Adagrad\', \'\', \'None\', \'None\', \'None\', \'None\', \'weighted_sum_over_batch_size\', \'False\'], " } member_method { name: "eval_dir" @@ -32,12 +32,12 @@ tf_class { argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } member_method { - name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { - name: "export_savedmodel" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'strip_default_attrs\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'False\'], " + name: "export_saved_model" + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "get_variable_names" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-estimator.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-estimator.pbtxt index 9d270a87ab..a1f0e76c8b 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-estimator.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-estimator.pbtxt @@ -1,6 +1,6 @@ path: "tensorflow.estimator.Estimator" tf_class { - is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -31,12 +31,12 @@ tf_class { argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } member_method { - name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { - name: "export_savedmodel" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'strip_default_attrs\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'False\'], " + name: "export_saved_model" + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "get_variable_names" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-linear-classifier.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-linear-classifier.pbtxt index 4acbff2cff..47de660a38 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-linear-classifier.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-linear-classifier.pbtxt @@ -1,7 +1,7 @@ path: "tensorflow.estimator.LinearClassifier" tf_class { is_instance: "" - is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -32,12 +32,12 @@ tf_class { argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } member_method { - name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { - name: "export_savedmodel" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'strip_default_attrs\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'False\'], " + name: "export_saved_model" + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "get_variable_names" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-linear-estimator.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-linear-estimator.pbtxt index 3d6b03098a..66a127606a 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-linear-estimator.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-linear-estimator.pbtxt @@ -1,7 +1,7 @@ path: "tensorflow.estimator.LinearEstimator" tf_class { - is_instance: "" - is_instance: "" + is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -32,12 +32,12 @@ tf_class { argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } member_method { - name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { - name: "export_savedmodel" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'strip_default_attrs\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'False\'], " + name: "export_saved_model" + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "get_variable_names" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-linear-regressor.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-linear-regressor.pbtxt index 0d1510e9ab..5c094fe131 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.estimator.-linear-regressor.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.estimator.-linear-regressor.pbtxt @@ -1,7 +1,7 @@ path: "tensorflow.estimator.LinearRegressor" tf_class { - is_instance: "" - is_instance: "" + is_instance: "" + is_instance: "" is_instance: "" member { name: "config" @@ -21,7 +21,7 @@ tf_class { } member_method { name: "__init__" - argspec: "args=[\'self\', \'feature_columns\', \'model_dir\', \'label_dimension\', \'weight_column\', \'optimizer\', \'config\', \'partitioner\', \'warm_start_from\', \'loss_reduction\', \'sparse_combiner\'], varargs=None, keywords=None, defaults=[\'None\', \'1\', \'None\', \'Ftrl\', \'None\', \'None\', \'None\', \'weighted_sum\', \'sum\'], " + argspec: "args=[\'self\', \'feature_columns\', \'model_dir\', \'label_dimension\', \'weight_column\', \'optimizer\', \'config\', \'partitioner\', \'warm_start_from\', \'loss_reduction\', \'sparse_combiner\'], varargs=None, keywords=None, defaults=[\'None\', \'1\', \'None\', \'Ftrl\', \'None\', \'None\', \'None\', \'weighted_sum_over_batch_size\', \'sum\'], " } member_method { name: "eval_dir" @@ -32,12 +32,12 @@ tf_class { argspec: "args=[\'self\', \'input_fn\', \'steps\', \'hooks\', \'checkpoint_path\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } member_method { - name: "export_saved_model" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + name: "experimental_export_all_saved_models" + argspec: "args=[\'self\', \'export_dir_base\', \'input_receiver_fn_map\', \'assets_extra\', \'as_text\', \'checkpoint_path\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { - name: "export_savedmodel" - argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'strip_default_attrs\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'False\'], " + name: "export_saved_model" + argspec: "args=[\'self\', \'export_dir_base\', \'serving_input_receiver_fn\', \'assets_extra\', \'as_text\', \'checkpoint_path\', \'experimental_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\', \'infer\'], " } member_method { name: "get_variable_names" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.estimator.experimental.-in-memory-evaluator-hook.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.estimator.experimental.-in-memory-evaluator-hook.pbtxt new file mode 100644 index 0000000000..aba120218c --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.estimator.experimental.-in-memory-evaluator-hook.pbtxt @@ -0,0 +1,30 @@ +path: "tensorflow.estimator.experimental.InMemoryEvaluatorHook" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member_method { + name: "__init__" + argspec: "args=[\'self\', \'estimator\', \'input_fn\', \'steps\', \'hooks\', \'name\', \'every_n_iter\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'100\'], " + } + member_method { + name: "after_create_session" + argspec: "args=[\'self\', \'session\', \'coord\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "after_run" + argspec: "args=[\'self\', \'run_context\', \'run_values\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "before_run" + argspec: "args=[\'self\', \'run_context\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "begin" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "end" + argspec: "args=[\'self\', \'session\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.estimator.experimental.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.estimator.experimental.pbtxt index cabca3e883..f0fd7ce782 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.estimator.experimental.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.estimator.experimental.pbtxt @@ -1,9 +1,17 @@ path: "tensorflow.estimator.experimental" tf_module { + member { + name: "InMemoryEvaluatorHook" + mtype: "" + } member { name: "LinearSDCA" mtype: "" } + member_method { + name: "build_raw_supervised_input_receiver_fn" + argspec: "args=[\'features\', \'labels\', \'default_batch_size\'], varargs=None, keywords=None, defaults=[\'None\'], " + } member_method { name: "call_logit_fn" argspec: "args=[\'logit_fn\', \'features\', \'mode\', \'params\', \'config\'], varargs=None, keywords=None, defaults=None" @@ -20,6 +28,10 @@ tf_module { name: "make_early_stopping_hook" argspec: "args=[\'estimator\', \'should_stop_fn\', \'run_every_secs\', \'run_every_steps\'], varargs=None, keywords=None, defaults=[\'60\', \'None\'], " } + member_method { + name: "make_stop_at_checkpoint_step_hook" + argspec: "args=[\'estimator\', \'last_step\', \'wait_after_file_check_secs\'], varargs=None, keywords=None, defaults=[\'30\'], " + } member_method { name: "stop_if_higher_hook" argspec: "args=[\'estimator\', \'metric_name\', \'threshold\', \'eval_dir\', \'min_steps\', \'run_every_secs\', \'run_every_steps\'], varargs=None, keywords=None, defaults=[\'None\', \'0\', \'60\', \'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.feature_column.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.feature_column.pbtxt index f06e798953..3aadd7dc34 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.feature_column.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.feature_column.pbtxt @@ -14,7 +14,7 @@ tf_module { } member_method { name: "categorical_column_with_vocabulary_file" - argspec: "args=[\'key\', \'vocabulary_file\', \'vocabulary_size\', \'num_oov_buckets\', \'default_value\', \'dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'0\', \'None\', \"\"], " + argspec: "args=[\'key\', \'vocabulary_file\', \'vocabulary_size\', \'dtype\', \'default_value\', \'num_oov_buckets\'], varargs=None, keywords=None, defaults=[\'None\', \"\", \'None\', \'0\'], " } member_method { name: "categorical_column_with_vocabulary_list" @@ -32,14 +32,6 @@ tf_module { name: "indicator_column" argspec: "args=[\'categorical_column\'], varargs=None, keywords=None, defaults=None" } - member_method { - name: "input_layer" - argspec: "args=[\'features\', \'feature_columns\', \'weight_collections\', \'trainable\', \'cols_to_vars\', \'cols_to_output_tensors\'], varargs=None, keywords=None, defaults=[\'None\', \'True\', \'None\', \'None\'], " - } - member_method { - name: "linear_model" - argspec: "args=[\'features\', \'feature_columns\', \'units\', \'sparse_combiner\', \'weight_collections\', \'trainable\', \'cols_to_vars\'], varargs=None, keywords=None, defaults=[\'1\', \'sum\', \'None\', \'True\', \'None\'], " - } member_method { name: "make_parse_example_spec" argspec: "args=[\'feature_columns\'], varargs=None, keywords=None, defaults=None" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.gfile.-fast-g-file.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.gfile.-fast-g-file.pbtxt deleted file mode 100644 index eecfaffd0a..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.gfile.-fast-g-file.pbtxt +++ /dev/null @@ -1,58 +0,0 @@ -path: "tensorflow.gfile.FastGFile" -tf_class { - is_instance: "" - is_instance: "" - is_instance: "" - member { - name: "mode" - mtype: "" - } - member { - name: "name" - mtype: "" - } - member_method { - name: "__init__" - argspec: "args=[\'self\', \'name\', \'mode\'], varargs=None, keywords=None, defaults=[\'r\'], " - } - member_method { - name: "close" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "flush" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "next" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "read" - argspec: "args=[\'self\', \'n\'], varargs=None, keywords=None, defaults=[\'-1\'], " - } - member_method { - name: "readline" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "readlines" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "seek" - argspec: "args=[\'self\', \'offset\', \'whence\', \'position\'], varargs=None, keywords=None, defaults=[\'None\', \'0\', \'None\'], " - } - member_method { - name: "size" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "tell" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "write" - argspec: "args=[\'self\', \'file_content\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.gfile.-g-file.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.gfile.-g-file.pbtxt deleted file mode 100644 index 305251059d..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.gfile.-g-file.pbtxt +++ /dev/null @@ -1,58 +0,0 @@ -path: "tensorflow.gfile.GFile" -tf_class { - is_instance: "" - is_instance: "" - is_instance: "" - member { - name: "mode" - mtype: "" - } - member { - name: "name" - mtype: "" - } - member_method { - name: "__init__" - argspec: "args=[\'self\', \'name\', \'mode\'], varargs=None, keywords=None, defaults=[\'r\'], " - } - member_method { - name: "close" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "flush" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "next" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "read" - argspec: "args=[\'self\', \'n\'], varargs=None, keywords=None, defaults=[\'-1\'], " - } - member_method { - name: "readline" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "readlines" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "seek" - argspec: "args=[\'self\', \'offset\', \'whence\', \'position\'], varargs=None, keywords=None, defaults=[\'None\', \'0\', \'None\'], " - } - member_method { - name: "size" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "tell" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "write" - argspec: "args=[\'self\', \'file_content\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.gfile.-open.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.gfile.-open.pbtxt deleted file mode 100644 index 6e8894180a..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.gfile.-open.pbtxt +++ /dev/null @@ -1,58 +0,0 @@ -path: "tensorflow.gfile.Open" -tf_class { - is_instance: "" - is_instance: "" - is_instance: "" - member { - name: "mode" - mtype: "" - } - member { - name: "name" - mtype: "" - } - member_method { - name: "__init__" - argspec: "args=[\'self\', \'name\', \'mode\'], varargs=None, keywords=None, defaults=[\'r\'], " - } - member_method { - name: "close" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "flush" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "next" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "read" - argspec: "args=[\'self\', \'n\'], varargs=None, keywords=None, defaults=[\'-1\'], " - } - member_method { - name: "readline" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "readlines" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "seek" - argspec: "args=[\'self\', \'offset\', \'whence\', \'position\'], varargs=None, keywords=None, defaults=[\'None\', \'0\', \'None\'], " - } - member_method { - name: "size" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "tell" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "write" - argspec: "args=[\'self\', \'file_content\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.gfile.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.gfile.pbtxt deleted file mode 100644 index 65b55a8b7c..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.gfile.pbtxt +++ /dev/null @@ -1,63 +0,0 @@ -path: "tensorflow.gfile" -tf_module { - member { - name: "FastGFile" - mtype: "" - } - member { - name: "GFile" - mtype: "" - } - member { - name: "Open" - mtype: "" - } - member_method { - name: "Copy" - argspec: "args=[\'oldpath\', \'newpath\', \'overwrite\'], varargs=None, keywords=None, defaults=[\'False\'], " - } - member_method { - name: "DeleteRecursively" - argspec: "args=[\'dirname\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "Exists" - argspec: "args=[\'filename\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "Glob" - argspec: "args=[\'filename\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "IsDirectory" - argspec: "args=[\'dirname\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "ListDirectory" - argspec: "args=[\'dirname\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "MakeDirs" - argspec: "args=[\'dirname\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "MkDir" - argspec: "args=[\'dirname\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "Remove" - argspec: "args=[\'filename\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "Rename" - argspec: "args=[\'oldname\', \'newname\', \'overwrite\'], varargs=None, keywords=None, defaults=[\'False\'], " - } - member_method { - name: "Stat" - argspec: "args=[\'filename\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "Walk" - argspec: "args=[\'top\', \'in_order\'], varargs=None, keywords=None, defaults=[\'True\'], " - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.image.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.image.pbtxt index 0a231f1b65..3c6ed1cfb8 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.image.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.image.pbtxt @@ -38,7 +38,7 @@ tf_module { } member_method { name: "crop_and_resize" - argspec: "args=[\'image\', \'boxes\', \'box_ind\', \'crop_size\', \'method\', \'extrapolation_value\', \'name\'], varargs=None, keywords=None, defaults=[\'bilinear\', \'0\', \'None\'], " + argspec: "args=[\'image\', \'boxes\', \'box_indices\', \'crop_size\', \'method\', \'extrapolation_value\', \'name\'], varargs=None, keywords=None, defaults=[\'bilinear\', \'0\', \'None\'], " } member_method { name: "crop_to_bounding_box" @@ -86,7 +86,7 @@ tf_module { } member_method { name: "extract_image_patches" - argspec: "args=[\'images\', \'ksizes\', \'strides\', \'rates\', \'padding\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + argspec: "args=[\'images\', \'sizes\', \'strides\', \'rates\', \'padding\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { name: "extract_jpeg_shape" @@ -173,16 +173,8 @@ tf_module { argspec: "args=[\'image\', \'lower\', \'upper\', \'seed\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { - name: "resize_area" - argspec: "args=[\'images\', \'size\', \'align_corners\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'None\'], " - } - member_method { - name: "resize_bicubic" - argspec: "args=[\'images\', \'size\', \'align_corners\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'None\'], " - } - member_method { - name: "resize_bilinear" - argspec: "args=[\'images\', \'size\', \'align_corners\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'None\'], " + name: "resize" + argspec: "args=[\'images\', \'size\', \'method\', \'align_corners\', \'preserve_aspect_ratio\', \'name\'], varargs=None, keywords=None, defaults=[\'0\', \'False\', \'False\', \'None\'], " } member_method { name: "resize_image_with_crop_or_pad" @@ -192,14 +184,6 @@ tf_module { name: "resize_image_with_pad" argspec: "args=[\'image\', \'target_height\', \'target_width\', \'method\'], varargs=None, keywords=None, defaults=[\'0\'], " } - member_method { - name: "resize_images" - argspec: "args=[\'images\', \'size\', \'method\', \'align_corners\', \'preserve_aspect_ratio\'], varargs=None, keywords=None, defaults=[\'0\', \'False\', \'False\'], " - } - member_method { - name: "resize_nearest_neighbor" - argspec: "args=[\'images\', \'size\', \'align_corners\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'None\'], " - } member_method { name: "rgb_to_grayscale" argspec: "args=[\'images\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -222,7 +206,7 @@ tf_module { } member_method { name: "sample_distorted_bounding_box" - argspec: "args=[\'image_size\', \'bounding_boxes\', \'seed\', \'seed2\', \'min_object_covered\', \'aspect_ratio_range\', \'area_range\', \'max_attempts\', \'use_image_if_no_bounding_boxes\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'0.1\', \'None\', \'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'image_size\', \'bounding_boxes\', \'seed\', \'min_object_covered\', \'aspect_ratio_range\', \'area_range\', \'max_attempts\', \'use_image_if_no_bounding_boxes\', \'name\'], varargs=None, keywords=None, defaults=[\'0\', \'0.1\', \'None\', \'None\', \'None\', \'None\', \'None\'], " } member_method { name: "sobel_edges" @@ -241,8 +225,8 @@ tf_module { argspec: "args=[\'images\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { - name: "transpose_image" - argspec: "args=[\'image\'], varargs=None, keywords=None, defaults=None" + name: "transpose" + argspec: "args=[\'image\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { name: "yiq_to_rgb" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.io.gfile.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.io.gfile.pbtxt new file mode 100644 index 0000000000..93d9b0fd75 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.io.gfile.pbtxt @@ -0,0 +1,51 @@ +path: "tensorflow.io.gfile" +tf_module { + member_method { + name: "copy" + argspec: "args=[\'src\', \'dst\', \'overwrite\'], varargs=None, keywords=None, defaults=[\'False\'], " + } + member_method { + name: "exists" + argspec: "args=[\'path\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "glob" + argspec: "args=[\'pattern\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "isdir" + argspec: "args=[\'path\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "listdir" + argspec: "args=[\'path\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "makedirs" + argspec: "args=[\'path\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "mkdir" + argspec: "args=[\'path\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "remove" + argspec: "args=[\'path\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "rename" + argspec: "args=[\'src\', \'dst\', \'overwrite\'], varargs=None, keywords=None, defaults=[\'False\'], " + } + member_method { + name: "rmtree" + argspec: "args=[\'path\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "stat" + argspec: "args=[\'path\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "walk" + argspec: "args=[\'top\', \'topdown\', \'onerror\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.io.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.io.pbtxt index 64b63ed1a4..8906329742 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.io.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.io.pbtxt @@ -44,22 +44,50 @@ tf_module { name: "VarLenFeature" mtype: "" } + member { + name: "gfile" + mtype: "" + } + member_method { + name: "decode_and_crop_jpeg" + argspec: "args=[\'contents\', \'crop_window\', \'channels\', \'ratio\', \'fancy_upscaling\', \'try_recover_truncated\', \'acceptable_fraction\', \'dct_method\', \'name\'], varargs=None, keywords=None, defaults=[\'0\', \'1\', \'True\', \'False\', \'1\', \'\', \'None\'], " + } member_method { name: "decode_base64" argspec: "args=[\'input\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "decode_bmp" + argspec: "args=[\'contents\', \'channels\', \'name\'], varargs=None, keywords=None, defaults=[\'0\', \'None\'], " + } member_method { name: "decode_compressed" argspec: "args=[\'bytes\', \'compression_type\', \'name\'], varargs=None, keywords=None, defaults=[\'\', \'None\'], " } member_method { name: "decode_csv" - argspec: "args=[\'records\', \'record_defaults\', \'field_delim\', \'use_quote_delim\', \'name\', \'na_value\', \'select_cols\'], varargs=None, keywords=None, defaults=[\',\', \'True\', \'None\', \'\', \'None\'], " + argspec: "args=[\'records\', \'record_defaults\', \'field_delim\', \'use_quote_delim\', \'na_value\', \'select_cols\', \'name\'], varargs=None, keywords=None, defaults=[\',\', \'True\', \'\', \'None\', \'None\'], " + } + member_method { + name: "decode_gif" + argspec: "args=[\'contents\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "decode_image" + argspec: "args=[\'contents\', \'channels\', \'dtype\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \"\", \'None\'], " + } + member_method { + name: "decode_jpeg" + argspec: "args=[\'contents\', \'channels\', \'ratio\', \'fancy_upscaling\', \'try_recover_truncated\', \'acceptable_fraction\', \'dct_method\', \'name\'], varargs=None, keywords=None, defaults=[\'0\', \'1\', \'True\', \'False\', \'1\', \'\', \'None\'], " } member_method { name: "decode_json_example" argspec: "args=[\'json_examples\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "decode_png" + argspec: "args=[\'contents\', \'channels\', \'dtype\', \'name\'], varargs=None, keywords=None, defaults=[\'0\', \"\", \'None\'], " + } member_method { name: "decode_raw" argspec: "args=[\'bytes\', \'out_type\', \'little_endian\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'None\'], " @@ -72,6 +100,18 @@ tf_module { name: "encode_base64" argspec: "args=[\'input\', \'pad\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'None\'], " } + member_method { + name: "encode_jpeg" + argspec: "args=[\'image\', \'format\', \'quality\', \'progressive\', \'optimize_size\', \'chroma_downsampling\', \'density_unit\', \'x_density\', \'y_density\', \'xmp_metadata\', \'name\'], varargs=None, keywords=None, defaults=[\'\', \'95\', \'False\', \'False\', \'True\', \'in\', \'300\', \'300\', \'\', \'None\'], " + } + member_method { + name: "extract_jpeg_shape" + argspec: "args=[\'contents\', \'output_type\', \'name\'], varargs=None, keywords=None, defaults=[\"\", \'None\'], " + } + member_method { + name: "is_jpeg" + argspec: "args=[\'contents\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } member_method { name: "match_filenames_once" argspec: "args=[\'pattern\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -82,7 +122,7 @@ tf_module { } member_method { name: "parse_example" - argspec: "args=[\'serialized\', \'features\', \'name\', \'example_names\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + argspec: "args=[\'serialized\', \'features\', \'example_names\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { name: "parse_sequence_example" @@ -90,7 +130,7 @@ tf_module { } member_method { name: "parse_single_example" - argspec: "args=[\'serialized\', \'features\', \'name\', \'example_names\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + argspec: "args=[\'serialized\', \'features\', \'example_names\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { name: "parse_single_sequence_example" @@ -106,20 +146,16 @@ tf_module { } member_method { name: "serialize_many_sparse" - argspec: "args=[\'sp_input\', \'name\', \'out_type\'], varargs=None, keywords=None, defaults=[\'None\', \"\"], " + argspec: "args=[\'sp_input\', \'out_type\', \'name\'], varargs=None, keywords=None, defaults=[\"\", \'None\'], " } member_method { name: "serialize_sparse" - argspec: "args=[\'sp_input\', \'name\', \'out_type\'], varargs=None, keywords=None, defaults=[\'None\', \"\"], " + argspec: "args=[\'sp_input\', \'out_type\', \'name\'], varargs=None, keywords=None, defaults=[\"\", \'None\'], " } member_method { name: "serialize_tensor" argspec: "args=[\'tensor\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } - member_method { - name: "tf_record_iterator" - argspec: "args=[\'path\', \'options\'], varargs=None, keywords=None, defaults=[\'None\'], " - } member_method { name: "write_file" argspec: "args=[\'filename\', \'contents\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.-model.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.-model.pbtxt index 8ccba990bd..a3254cbd94 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.-model.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.-model.pbtxt @@ -41,6 +41,14 @@ tf_class { name: "losses" mtype: "" } + member { + name: "metrics" + mtype: "" + } + member { + name: "metrics_names" + mtype: "" + } member { name: "name" mtype: "" @@ -69,6 +77,10 @@ tf_class { name: "output_shape" mtype: "" } + member { + name: "run_eagerly" + mtype: "" + } member { name: "state_updates" mtype: "" @@ -105,13 +117,17 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { name: "add_variable" - argspec: "args=[\'self\', \'name\', \'shape\', \'dtype\', \'initializer\', \'regularizer\', \'trainable\', \'constraint\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'True\', \'None\'], " + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" } member_method { name: "add_weight" @@ -225,6 +241,10 @@ tf_class { name: "predict_on_batch" argspec: "args=[\'self\', \'x\'], varargs=None, keywords=None, defaults=None" } + member_method { + name: "reset_metrics" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } member_method { name: "reset_states" argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" @@ -247,7 +267,7 @@ tf_class { } member_method { name: "test_on_batch" - argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\', \'reset_metrics\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'True\'], " } member_method { name: "to_json" @@ -259,6 +279,6 @@ tf_class { } member_method { name: "train_on_batch" - argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\', \'class_weight\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\', \'class_weight\', \'reset_metrics\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'True\'], " } } diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.-sequential.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.-sequential.pbtxt index 27aa91a645..b70e9ee98d 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.-sequential.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.-sequential.pbtxt @@ -42,6 +42,14 @@ tf_class { name: "losses" mtype: "" } + member { + name: "metrics" + mtype: "" + } + member { + name: "metrics_names" + mtype: "" + } member { name: "name" mtype: "" @@ -70,6 +78,10 @@ tf_class { name: "output_shape" mtype: "" } + member { + name: "run_eagerly" + mtype: "" + } member { name: "state_updates" mtype: "" @@ -110,13 +122,17 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { name: "add_variable" - argspec: "args=[\'self\', \'name\', \'shape\', \'dtype\', \'initializer\', \'regularizer\', \'trainable\', \'constraint\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'True\', \'None\'], " + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" } member_method { name: "add_weight" @@ -242,6 +258,10 @@ tf_class { name: "predict_proba" argspec: "args=[\'self\', \'x\', \'batch_size\', \'verbose\'], varargs=None, keywords=None, defaults=[\'32\', \'0\'], " } + member_method { + name: "reset_metrics" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } member_method { name: "reset_states" argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" @@ -264,7 +284,7 @@ tf_class { } member_method { name: "test_on_batch" - argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\', \'reset_metrics\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'True\'], " } member_method { name: "to_json" @@ -276,6 +296,6 @@ tf_class { } member_method { name: "train_on_batch" - argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\', \'class_weight\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\', \'class_weight\', \'reset_metrics\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'True\'], " } } diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.backend.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.backend.pbtxt index b30778b2a0..d200d3d26d 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.backend.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.backend.pbtxt @@ -394,7 +394,7 @@ tf_module { } member_method { name: "rnn" - argspec: "args=[\'step_function\', \'inputs\', \'initial_states\', \'go_backwards\', \'mask\', \'constants\', \'unroll\', \'input_length\', \'time_major\'], varargs=None, keywords=None, defaults=[\'False\', \'None\', \'None\', \'False\', \'None\', \'False\'], " + argspec: "args=[\'step_function\', \'inputs\', \'initial_states\', \'go_backwards\', \'mask\', \'constants\', \'unroll\', \'input_length\', \'time_major\', \'zero_output_for_mask\'], varargs=None, keywords=None, defaults=[\'False\', \'None\', \'None\', \'False\', \'None\', \'False\', \'False\'], " } member_method { name: "round" @@ -508,6 +508,10 @@ tf_module { name: "temporal_padding" argspec: "args=[\'x\', \'padding\'], varargs=None, keywords=None, defaults=[\'(1, 1)\'], " } + member_method { + name: "tile" + argspec: "args=[\'x\', \'n\'], varargs=None, keywords=None, defaults=None" + } member_method { name: "to_dense" argspec: "args=[\'tensor\'], varargs=None, keywords=None, defaults=None" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.experimental.-peephole-l-s-t-m-cell.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.experimental.-peephole-l-s-t-m-cell.pbtxt index db69e25c5b..1d814b2c8b 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.experimental.-peephole-l-s-t-m-cell.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.experimental.-peephole-l-s-t-m-cell.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-activation.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-activation.pbtxt index 5510465d7b..b84629540e 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-activation.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-activation.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-activity-regularization.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-activity-regularization.pbtxt index 38ec8a0aff..5918a13ad8 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-activity-regularization.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-activity-regularization.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-add.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-add.pbtxt index 41cb8e30bf..599da06427 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-add.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-add.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-alpha-dropout.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-alpha-dropout.pbtxt index 9a7aaa8e96..f9ff1538c8 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-alpha-dropout.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-alpha-dropout.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-average-pooling1-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-average-pooling1-d.pbtxt index 014f5828fa..723fc9cdb0 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-average-pooling1-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-average-pooling1-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-average-pooling2-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-average-pooling2-d.pbtxt index cc303bf7b9..957ce2f0ce 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-average-pooling2-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-average-pooling2-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-average-pooling3-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-average-pooling3-d.pbtxt index 628447ce35..a52c0af681 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-average-pooling3-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-average-pooling3-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-average.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-average.pbtxt index f03c986c22..a004db62dd 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-average.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-average.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-avg-pool1-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-avg-pool1-d.pbtxt index a6e4856de9..44f83d1387 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-avg-pool1-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-avg-pool1-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-avg-pool2-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-avg-pool2-d.pbtxt index a01eaf8a12..8378faf718 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-avg-pool2-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-avg-pool2-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-avg-pool3-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-avg-pool3-d.pbtxt index 0d6698f2ef..9d5655c964 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-avg-pool3-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-avg-pool3-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-batch-normalization.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-batch-normalization.pbtxt index f1b23be48f..5da7926812 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-batch-normalization.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-batch-normalization.pbtxt @@ -1,6 +1,6 @@ path: "tensorflow.keras.layers.BatchNormalization" tf_class { - is_instance: "" + is_instance: "" is_instance: "" is_instance: "" is_instance: "" @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-bidirectional.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-bidirectional.pbtxt index 0672cd5b7b..d37a6b4710 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-bidirectional.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-bidirectional.pbtxt @@ -97,6 +97,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-concatenate.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-concatenate.pbtxt index b25ae1e82e..1ad7a91be0 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-concatenate.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-concatenate.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-conv-l-s-t-m2-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-conv-l-s-t-m2-d.pbtxt index bb1918eba6..cb9abc2539 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-conv-l-s-t-m2-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-conv-l-s-t-m2-d.pbtxt @@ -178,6 +178,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-conv1-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-conv1-d.pbtxt index 16e0fd5a31..47dba1d81f 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-conv1-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-conv1-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-conv2-d-transpose.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-conv2-d-transpose.pbtxt index 381839d6de..fd64941896 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-conv2-d-transpose.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-conv2-d-transpose.pbtxt @@ -90,6 +90,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-conv2-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-conv2-d.pbtxt index 543bae6fa9..1b1425d531 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-conv2-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-conv2-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-conv3-d-transpose.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-conv3-d-transpose.pbtxt index 2933f9f4b3..1741063fe8 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-conv3-d-transpose.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-conv3-d-transpose.pbtxt @@ -90,6 +90,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-conv3-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-conv3-d.pbtxt index 072943dc2c..50feb4f458 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-conv3-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-conv3-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-convolution1-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-convolution1-d.pbtxt index 222a1ef4fc..faaa535df9 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-convolution1-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-convolution1-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-convolution2-d-transpose.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-convolution2-d-transpose.pbtxt index 9c9c7461c8..4079329d1e 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-convolution2-d-transpose.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-convolution2-d-transpose.pbtxt @@ -90,6 +90,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-convolution2-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-convolution2-d.pbtxt index f939067178..32e56696e1 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-convolution2-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-convolution2-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-convolution3-d-transpose.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-convolution3-d-transpose.pbtxt index 44ca598724..381abe7340 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-convolution3-d-transpose.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-convolution3-d-transpose.pbtxt @@ -90,6 +90,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-convolution3-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-convolution3-d.pbtxt index 471b18ef85..b3e4bf9689 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-convolution3-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-convolution3-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-cropping1-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-cropping1-d.pbtxt index 0f250a09b7..7aeff8003c 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-cropping1-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-cropping1-d.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-cropping2-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-cropping2-d.pbtxt index f52128483c..a1728d9d4f 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-cropping2-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-cropping2-d.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-cropping3-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-cropping3-d.pbtxt index 98daf3bab1..8d8fd142cc 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-cropping3-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-cropping3-d.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-cu-d-n-n-g-r-u.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-cu-d-n-n-g-r-u.pbtxt index b207c68000..7758209adf 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-cu-d-n-n-g-r-u.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-cu-d-n-n-g-r-u.pbtxt @@ -98,6 +98,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-cu-d-n-n-l-s-t-m.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-cu-d-n-n-l-s-t-m.pbtxt index 2d7a09ceda..7c463ff125 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-cu-d-n-n-l-s-t-m.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-cu-d-n-n-l-s-t-m.pbtxt @@ -98,6 +98,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.nn.rnn_cell.-multi-r-n-n-cell.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-dense-features.pbtxt similarity index 77% rename from tensorflow/tools/api/golden/v2/tensorflow.nn.rnn_cell.-multi-r-n-n-cell.pbtxt rename to tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-dense-features.pbtxt index b66c0f89cc..0781a93bd5 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.nn.rnn_cell.-multi-r-n-n-cell.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-dense-features.pbtxt @@ -1,8 +1,6 @@ -path: "tensorflow.nn.rnn_cell.MultiRNNCell" +path: "tensorflow.keras.layers.DenseFeatures" tf_class { - is_instance: "" - is_instance: "" - is_instance: "" + is_instance: "" is_instance: "" is_instance: "" is_instance: "" @@ -14,10 +12,6 @@ tf_class { name: "dtype" mtype: "" } - member { - name: "graph" - mtype: "" - } member { name: "inbound_nodes" mtype: "" @@ -66,18 +60,6 @@ tf_class { name: "output_shape" mtype: "" } - member { - name: "output_size" - mtype: "" - } - member { - name: "scope_name" - mtype: "" - } - member { - name: "state_size" - mtype: "" - } member { name: "trainable_variables" mtype: "" @@ -100,12 +82,16 @@ tf_class { } member_method { name: "__init__" - argspec: "args=[\'self\', \'cells\', \'state_is_tuple\'], varargs=None, keywords=None, defaults=[\'True\'], " + argspec: "args=[\'self\', \'feature_columns\', \'trainable\', \'name\'], varargs=None, keywords=kwargs, defaults=[\'True\', \'None\'], " } member_method { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -116,7 +102,7 @@ tf_class { } member_method { name: "add_weight" - argspec: "args=[\'self\', \'name\', \'shape\', \'dtype\', \'initializer\', \'regularizer\', \'trainable\', \'constraint\', \'use_resource\', \'synchronization\', \'aggregation\', \'partitioner\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\', \'None\', \'VariableSynchronization.AUTO\', \'VariableAggregation.NONE\', \'None\'], " + argspec: "args=[\'self\', \'name\', \'shape\', \'dtype\', \'initializer\', \'regularizer\', \'trainable\', \'constraint\', \'partitioner\', \'use_resource\', \'synchronization\', \'aggregation\'], varargs=None, keywords=kwargs, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\', \'None\', \'None\', \'VariableSynchronization.AUTO\', \'VariableAggregation.NONE\'], " } member_method { name: "apply" @@ -128,7 +114,7 @@ tf_class { } member_method { name: "call" - argspec: "args=[\'self\', \'inputs\', \'state\'], varargs=None, keywords=None, defaults=None" + argspec: "args=[\'self\', \'features\', \'cols_to_output_tensors\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { name: "compute_mask" @@ -150,10 +136,6 @@ tf_class { name: "get_config" argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" } - member_method { - name: "get_initial_state" - argspec: "args=[\'self\', \'inputs\', \'batch_size\', \'dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " - } member_method { name: "get_input_at" argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" @@ -194,8 +176,4 @@ tf_class { name: "set_weights" argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" } - member_method { - name: "zero_state" - argspec: "args=[\'self\', \'batch_size\', \'dtype\'], varargs=None, keywords=None, defaults=None" - } } diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-dense.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-dense.pbtxt index 3ac3825759..4960d0264e 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-dense.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-dense.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-depthwise-conv2-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-depthwise-conv2-d.pbtxt index 280ec8c25f..8fad7535f8 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-depthwise-conv2-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-depthwise-conv2-d.pbtxt @@ -90,6 +90,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-dot.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-dot.pbtxt index 560f66f9c7..5b425f2d4d 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-dot.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-dot.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-dropout.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-dropout.pbtxt index c0543529c3..f6c4d0a438 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-dropout.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-dropout.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-e-l-u.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-e-l-u.pbtxt index 04eb2824b9..82b761fc17 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-e-l-u.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-e-l-u.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-embedding.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-embedding.pbtxt index f400432915..c9ff323877 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-embedding.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-embedding.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-flatten.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-flatten.pbtxt index ab176b441a..9b4165d4cb 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-flatten.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-flatten.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-g-r-u-cell.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-g-r-u-cell.pbtxt index c3895a0ac1..f225f7c430 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-g-r-u-cell.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-g-r-u-cell.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-g-r-u.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-g-r-u.pbtxt index 9e24bb8ae6..855d001700 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-g-r-u.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-g-r-u.pbtxt @@ -161,6 +161,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-gaussian-dropout.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-gaussian-dropout.pbtxt index 55e0d7ef02..2c404c99cd 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-gaussian-dropout.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-gaussian-dropout.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-gaussian-noise.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-gaussian-noise.pbtxt index 38fbff5e4a..6f109d59d0 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-gaussian-noise.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-gaussian-noise.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-average-pooling1-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-average-pooling1-d.pbtxt index a8094c0bde..69f8a9031d 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-average-pooling1-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-average-pooling1-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-average-pooling2-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-average-pooling2-d.pbtxt index 929f48df23..4299f765e5 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-average-pooling2-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-average-pooling2-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-average-pooling3-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-average-pooling3-d.pbtxt index 2e6d59337f..9153a1a240 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-average-pooling3-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-average-pooling3-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-avg-pool1-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-avg-pool1-d.pbtxt index 3ebe162f57..625e81fd23 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-avg-pool1-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-avg-pool1-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-avg-pool2-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-avg-pool2-d.pbtxt index 4e3e258430..2fc769742c 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-avg-pool2-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-avg-pool2-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-avg-pool3-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-avg-pool3-d.pbtxt index fb9166316f..e307a65c7c 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-avg-pool3-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-avg-pool3-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-max-pool1-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-max-pool1-d.pbtxt index c0a53b847b..4394ad0364 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-max-pool1-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-max-pool1-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-max-pool2-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-max-pool2-d.pbtxt index 87b7f6797a..050ed39fe9 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-max-pool2-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-max-pool2-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-max-pool3-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-max-pool3-d.pbtxt index 98bf96fa0c..436191821e 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-max-pool3-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-max-pool3-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-max-pooling1-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-max-pooling1-d.pbtxt index ff6c6f3ec4..4ba540aa6a 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-max-pooling1-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-max-pooling1-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-max-pooling2-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-max-pooling2-d.pbtxt index c9d4158d1c..a2e9322cb3 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-max-pooling2-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-max-pooling2-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-max-pooling3-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-max-pooling3-d.pbtxt index 9953102ff9..5d16a57fc1 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-max-pooling3-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-global-max-pooling3-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-input-layer.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-input-layer.pbtxt index 2617f5a95f..9dd29c1251 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-input-layer.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-input-layer.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-input-spec.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-input-spec.pbtxt index 5fd0a47a68..bc3ceb67a4 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-input-spec.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-input-spec.pbtxt @@ -1,6 +1,6 @@ path: "tensorflow.keras.layers.InputSpec" tf_class { - is_instance: "" + is_instance: "" is_instance: "" member_method { name: "__init__" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-l-s-t-m-cell.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-l-s-t-m-cell.pbtxt index e9f6ef45aa..0045d5775e 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-l-s-t-m-cell.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-l-s-t-m-cell.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-l-s-t-m.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-l-s-t-m.pbtxt index 1b1ccbe118..529c750f98 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-l-s-t-m.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-l-s-t-m.pbtxt @@ -161,6 +161,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-lambda.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-lambda.pbtxt index 2e0b6bac24..d4d1bc6b6b 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-lambda.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-lambda.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-layer.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-layer.pbtxt index 1e93d1118a..e1f5491180 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-layer.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-layer.pbtxt @@ -87,6 +87,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-leaky-re-l-u.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-leaky-re-l-u.pbtxt index bfd36012a7..9b69d9a944 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-leaky-re-l-u.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-leaky-re-l-u.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-linear-model.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-linear-model.pbtxt new file mode 100644 index 0000000000..2b66576c96 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-linear-model.pbtxt @@ -0,0 +1,289 @@ +path: "tensorflow.keras.layers.LinearModel" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "bias" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "input_spec" + mtype: "" + } + member { + name: "layers" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "metrics" + mtype: "" + } + member { + name: "metrics_names" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "run_eagerly" + mtype: "" + } + member { + name: "state_updates" + mtype: "" + } + member { + name: "stateful" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'feature_columns\', \'units\', \'sparse_combiner\', \'trainable\', \'name\'], varargs=None, keywords=kwargs, defaults=[\'1\', \'sum\', \'True\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'dtype\', \'initializer\', \'regularizer\', \'trainable\', \'constraint\', \'partitioner\', \'use_resource\', \'synchronization\', \'aggregation\'], varargs=None, keywords=kwargs, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\', \'None\', \'None\', \'VariableSynchronization.AUTO\', \'VariableAggregation.NONE\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'features\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "compile" + argspec: "args=[\'self\', \'optimizer\', \'loss\', \'metrics\', \'loss_weights\', \'sample_weight_mode\', \'weighted_metrics\', \'target_tensors\', \'distribute\'], varargs=None, keywords=kwargs, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\', \'None\', \'None\'], " + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "evaluate" + argspec: "args=[\'self\', \'x\', \'y\', \'batch_size\', \'verbose\', \'sample_weight\', \'steps\', \'max_queue_size\', \'workers\', \'use_multiprocessing\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'1\', \'None\', \'None\', \'10\', \'1\', \'False\'], " + } + member_method { + name: "evaluate_generator" + argspec: "args=[\'self\', \'generator\', \'steps\', \'max_queue_size\', \'workers\', \'use_multiprocessing\', \'verbose\'], varargs=None, keywords=None, defaults=[\'None\', \'10\', \'1\', \'False\', \'0\'], " + } + member_method { + name: "fit" + argspec: "args=[\'self\', \'x\', \'y\', \'batch_size\', \'epochs\', \'verbose\', \'callbacks\', \'validation_split\', \'validation_data\', \'shuffle\', \'class_weight\', \'sample_weight\', \'initial_epoch\', \'steps_per_epoch\', \'validation_steps\', \'max_queue_size\', \'workers\', \'use_multiprocessing\'], varargs=None, keywords=kwargs, defaults=[\'None\', \'None\', \'None\', \'1\', \'1\', \'None\', \'0.0\', \'None\', \'True\', \'None\', \'None\', \'0\', \'None\', \'None\', \'10\', \'1\', \'False\'], " + } + member_method { + name: "fit_generator" + argspec: "args=[\'self\', \'generator\', \'steps_per_epoch\', \'epochs\', \'verbose\', \'callbacks\', \'validation_data\', \'validation_steps\', \'class_weight\', \'max_queue_size\', \'workers\', \'use_multiprocessing\', \'shuffle\', \'initial_epoch\'], varargs=None, keywords=None, defaults=[\'None\', \'1\', \'1\', \'None\', \'None\', \'None\', \'None\', \'10\', \'1\', \'False\', \'True\', \'0\'], " + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\', \'custom_objects\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_layer" + argspec: "args=[\'self\', \'name\', \'index\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "load_weights" + argspec: "args=[\'self\', \'filepath\', \'by_name\'], varargs=None, keywords=None, defaults=[\'False\'], " + } + member_method { + name: "predict" + argspec: "args=[\'self\', \'x\', \'batch_size\', \'verbose\', \'steps\', \'max_queue_size\', \'workers\', \'use_multiprocessing\'], varargs=None, keywords=None, defaults=[\'None\', \'0\', \'None\', \'10\', \'1\', \'False\'], " + } + member_method { + name: "predict_generator" + argspec: "args=[\'self\', \'generator\', \'steps\', \'max_queue_size\', \'workers\', \'use_multiprocessing\', \'verbose\'], varargs=None, keywords=None, defaults=[\'None\', \'10\', \'1\', \'False\', \'0\'], " + } + member_method { + name: "predict_on_batch" + argspec: "args=[\'self\', \'x\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_metrics" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "save" + argspec: "args=[\'self\', \'filepath\', \'overwrite\', \'include_optimizer\'], varargs=None, keywords=None, defaults=[\'True\', \'True\'], " + } + member_method { + name: "save_weights" + argspec: "args=[\'self\', \'filepath\', \'overwrite\', \'save_format\'], varargs=None, keywords=None, defaults=[\'True\', \'None\'], " + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "summary" + argspec: "args=[\'self\', \'line_length\', \'positions\', \'print_fn\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + } + member_method { + name: "test_on_batch" + argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\', \'reset_metrics\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'True\'], " + } + member_method { + name: "to_json" + argspec: "args=[\'self\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "to_yaml" + argspec: "args=[\'self\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "train_on_batch" + argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\', \'class_weight\', \'reset_metrics\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'True\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-locally-connected1-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-locally-connected1-d.pbtxt index 5ad5990d7e..fd52259432 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-locally-connected1-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-locally-connected1-d.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-locally-connected2-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-locally-connected2-d.pbtxt index 40d03369a5..5fc8af0d03 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-locally-connected2-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-locally-connected2-d.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-masking.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-masking.pbtxt index 86666b51bb..7f8932270e 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-masking.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-masking.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-max-pool1-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-max-pool1-d.pbtxt index d26da270e7..4723b99cb0 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-max-pool1-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-max-pool1-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-max-pool2-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-max-pool2-d.pbtxt index 85f23df671..173c5d4a8b 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-max-pool2-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-max-pool2-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-max-pool3-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-max-pool3-d.pbtxt index 235806b965..14e1899e14 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-max-pool3-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-max-pool3-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-max-pooling1-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-max-pooling1-d.pbtxt index 524c5fd69e..a708e652bf 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-max-pooling1-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-max-pooling1-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-max-pooling2-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-max-pooling2-d.pbtxt index fda2562fc8..e6706b5cf9 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-max-pooling2-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-max-pooling2-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-max-pooling3-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-max-pooling3-d.pbtxt index 71d2d09a8d..a73c082d1b 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-max-pooling3-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-max-pooling3-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-maximum.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-maximum.pbtxt index 12949b39a6..f3f195554b 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-maximum.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-maximum.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-minimum.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-minimum.pbtxt index ab16d0021e..f345d1d67b 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-minimum.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-minimum.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-multiply.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-multiply.pbtxt index 61ccbf5962..31cb8bc177 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-multiply.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-multiply.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-p-re-l-u.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-p-re-l-u.pbtxt index ce2320d703..44cccc92bd 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-p-re-l-u.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-p-re-l-u.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-permute.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-permute.pbtxt index 69848af8cf..b55e191ff1 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-permute.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-permute.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-r-n-n.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-r-n-n.pbtxt index 3358f26aeb..e9575436e5 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-r-n-n.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-r-n-n.pbtxt @@ -92,6 +92,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-re-l-u.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-re-l-u.pbtxt index 413f45f018..98223b207f 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-re-l-u.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-re-l-u.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-repeat-vector.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-repeat-vector.pbtxt index 9c61ff6027..2df918b16b 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-repeat-vector.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-repeat-vector.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-reshape.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-reshape.pbtxt index baa91804c4..ce5f9e2129 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-reshape.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-reshape.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-separable-conv1-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-separable-conv1-d.pbtxt index 15a5d6ac9e..a0bb917775 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-separable-conv1-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-separable-conv1-d.pbtxt @@ -90,6 +90,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-separable-conv2-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-separable-conv2-d.pbtxt index be43bd5b3c..d7942f201b 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-separable-conv2-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-separable-conv2-d.pbtxt @@ -90,6 +90,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-separable-convolution1-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-separable-convolution1-d.pbtxt index 6105992c7a..f7ac9042d4 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-separable-convolution1-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-separable-convolution1-d.pbtxt @@ -90,6 +90,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-separable-convolution2-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-separable-convolution2-d.pbtxt index 1b6cf1e9ec..e5a9268822 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-separable-convolution2-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-separable-convolution2-d.pbtxt @@ -90,6 +90,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-simple-r-n-n-cell.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-simple-r-n-n-cell.pbtxt index 29488a37f8..0fe2c974a7 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-simple-r-n-n-cell.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-simple-r-n-n-cell.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-simple-r-n-n.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-simple-r-n-n.pbtxt index 3d70cf8b65..2ee5873f0f 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-simple-r-n-n.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-simple-r-n-n.pbtxt @@ -149,6 +149,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-softmax.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-softmax.pbtxt index d29731ecf9..5b8f64aa35 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-softmax.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-softmax.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-spatial-dropout1-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-spatial-dropout1-d.pbtxt index a6d7494ca7..240cb6e562 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-spatial-dropout1-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-spatial-dropout1-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-spatial-dropout2-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-spatial-dropout2-d.pbtxt index c36e802693..6226c469f8 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-spatial-dropout2-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-spatial-dropout2-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-spatial-dropout3-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-spatial-dropout3-d.pbtxt index 9c46cfe40f..34dabce6d8 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-spatial-dropout3-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-spatial-dropout3-d.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-stacked-r-n-n-cells.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-stacked-r-n-n-cells.pbtxt index 8982f78794..0ddf628ace 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-stacked-r-n-n-cells.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-stacked-r-n-n-cells.pbtxt @@ -96,6 +96,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-subtract.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-subtract.pbtxt index ec2cc50298..12eb35ad15 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-subtract.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-subtract.pbtxt @@ -89,6 +89,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-thresholded-re-l-u.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-thresholded-re-l-u.pbtxt index d7bc1980f3..c41020c2b4 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-thresholded-re-l-u.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-thresholded-re-l-u.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-time-distributed.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-time-distributed.pbtxt index fec2de6b49..479f89cf6a 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-time-distributed.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-time-distributed.pbtxt @@ -93,6 +93,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-up-sampling1-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-up-sampling1-d.pbtxt index 3d285e7f17..233363ce02 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-up-sampling1-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-up-sampling1-d.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-up-sampling2-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-up-sampling2-d.pbtxt index b05e5ec84d..cb6228ac44 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-up-sampling2-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-up-sampling2-d.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-up-sampling3-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-up-sampling3-d.pbtxt index 728eca415a..03bad3ccb6 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-up-sampling3-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-up-sampling3-d.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-wrapper.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-wrapper.pbtxt index da64e77c39..158996792a 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-wrapper.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-wrapper.pbtxt @@ -92,6 +92,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-zero-padding1-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-zero-padding1-d.pbtxt index 2f505f9293..63a56cd3ee 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-zero-padding1-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-zero-padding1-d.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-zero-padding2-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-zero-padding2-d.pbtxt index f82c77072e..965a4cca04 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-zero-padding2-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-zero-padding2-d.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-zero-padding3-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-zero-padding3-d.pbtxt index 54e01a9917..1a62430887 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-zero-padding3-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.-zero-padding3-d.pbtxt @@ -88,6 +88,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.pbtxt index 9d7e5bb8c7..3b4724ef10 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.layers.pbtxt @@ -124,6 +124,10 @@ tf_module { name: "Dense" mtype: "" } + member { + name: "DenseFeatures" + mtype: "" + } member { name: "DepthwiseConv2D" mtype: "" @@ -240,6 +244,10 @@ tf_module { name: "LeakyReLU" mtype: "" } + member { + name: "LinearModel" + mtype: "" + } member { name: "LocallyConnected1D" mtype: "" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-binary-crossentropy.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-binary-crossentropy.pbtxt new file mode 100644 index 0000000000..2f7da93f6f --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-binary-crossentropy.pbtxt @@ -0,0 +1,22 @@ +path: "tensorflow.keras.losses.BinaryCrossentropy" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member_method { + name: "__init__" + argspec: "args=[\'self\', \'from_logits\', \'label_smoothing\', \'reduction\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'0\', \'sum_over_batch_size\', \'None\'], " + } + member_method { + name: "call" + argspec: "args=[\'self\', \'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-categorical-crossentropy.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-categorical-crossentropy.pbtxt new file mode 100644 index 0000000000..b3a7cd8097 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-categorical-crossentropy.pbtxt @@ -0,0 +1,22 @@ +path: "tensorflow.keras.losses.CategoricalCrossentropy" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member_method { + name: "__init__" + argspec: "args=[\'self\', \'from_logits\', \'label_smoothing\', \'reduction\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'0\', \'sum_over_batch_size\', \'None\'], " + } + member_method { + name: "call" + argspec: "args=[\'self\', \'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-mean-absolute-error.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-mean-absolute-error.pbtxt new file mode 100644 index 0000000000..712bb2ecd3 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-mean-absolute-error.pbtxt @@ -0,0 +1,22 @@ +path: "tensorflow.keras.losses.MeanAbsoluteError" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member_method { + name: "__init__" + argspec: "args=[\'self\', \'reduction\', \'name\'], varargs=None, keywords=None, defaults=[\'sum_over_batch_size\', \'None\'], " + } + member_method { + name: "call" + argspec: "args=[\'self\', \'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-mean-absolute-percentage-error.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-mean-absolute-percentage-error.pbtxt new file mode 100644 index 0000000000..7fe362da89 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-mean-absolute-percentage-error.pbtxt @@ -0,0 +1,22 @@ +path: "tensorflow.keras.losses.MeanAbsolutePercentageError" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member_method { + name: "__init__" + argspec: "args=[\'self\', \'reduction\', \'name\'], varargs=None, keywords=None, defaults=[\'sum_over_batch_size\', \'None\'], " + } + member_method { + name: "call" + argspec: "args=[\'self\', \'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-mean-squared-error.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-mean-squared-error.pbtxt new file mode 100644 index 0000000000..a571853350 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-mean-squared-error.pbtxt @@ -0,0 +1,22 @@ +path: "tensorflow.keras.losses.MeanSquaredError" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member_method { + name: "__init__" + argspec: "args=[\'self\', \'reduction\', \'name\'], varargs=None, keywords=None, defaults=[\'sum_over_batch_size\', \'None\'], " + } + member_method { + name: "call" + argspec: "args=[\'self\', \'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-mean-squared-logarithmic-error.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-mean-squared-logarithmic-error.pbtxt new file mode 100644 index 0000000000..200006db35 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-mean-squared-logarithmic-error.pbtxt @@ -0,0 +1,22 @@ +path: "tensorflow.keras.losses.MeanSquaredLogarithmicError" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member_method { + name: "__init__" + argspec: "args=[\'self\', \'reduction\', \'name\'], varargs=None, keywords=None, defaults=[\'sum_over_batch_size\', \'None\'], " + } + member_method { + name: "call" + argspec: "args=[\'self\', \'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-reduction.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-reduction.pbtxt new file mode 100644 index 0000000000..f20ed26e2e --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-reduction.pbtxt @@ -0,0 +1,28 @@ +path: "tensorflow.keras.losses.Reduction" +tf_class { + is_instance: "" + is_instance: "" + member { + name: "NONE" + mtype: "" + } + member { + name: "SUM" + mtype: "" + } + member { + name: "SUM_OVER_BATCH_SIZE" + mtype: "" + } + member_method { + name: "__init__" + } + member_method { + name: "all" + argspec: "args=[\'cls\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "validate" + argspec: "args=[\'cls\', \'key\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.pbtxt index eca6b91538..c198096d25 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.pbtxt @@ -1,5 +1,33 @@ path: "tensorflow.keras.losses" tf_module { + member { + name: "BinaryCrossentropy" + mtype: "" + } + member { + name: "CategoricalCrossentropy" + mtype: "" + } + member { + name: "MeanAbsoluteError" + mtype: "" + } + member { + name: "MeanAbsolutePercentageError" + mtype: "" + } + member { + name: "MeanSquaredError" + mtype: "" + } + member { + name: "MeanSquaredLogarithmicError" + mtype: "" + } + member { + name: "Reduction" + mtype: "" + } member_method { name: "KLD" argspec: "args=[\'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" @@ -22,11 +50,11 @@ tf_module { } member_method { name: "binary_crossentropy" - argspec: "args=[\'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + argspec: "args=[\'y_true\', \'y_pred\', \'from_logits\'], varargs=None, keywords=None, defaults=[\'False\'], " } member_method { name: "categorical_crossentropy" - argspec: "args=[\'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + argspec: "args=[\'y_true\', \'y_pred\', \'from_logits\'], varargs=None, keywords=None, defaults=[\'False\'], " } member_method { name: "categorical_hinge" @@ -106,7 +134,7 @@ tf_module { } member_method { name: "sparse_categorical_crossentropy" - argspec: "args=[\'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + argspec: "args=[\'y_true\', \'y_pred\', \'from_logits\'], varargs=None, keywords=None, defaults=[\'False\'], " } member_method { name: "squared_hinge" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-accuracy.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-accuracy.pbtxt new file mode 100644 index 0000000000..2db07df523 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-accuracy.pbtxt @@ -0,0 +1,194 @@ +path: "tensorflow.keras.metrics.Accuracy" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'accuracy\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-binary-accuracy.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-binary-accuracy.pbtxt new file mode 100644 index 0000000000..904ad3a21a --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-binary-accuracy.pbtxt @@ -0,0 +1,194 @@ +path: "tensorflow.keras.metrics.BinaryAccuracy" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\', \'threshold\'], varargs=None, keywords=None, defaults=[\'binary_accuracy\', \'None\', \'0.5\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-categorical-accuracy.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-categorical-accuracy.pbtxt new file mode 100644 index 0000000000..17b74924fa --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-categorical-accuracy.pbtxt @@ -0,0 +1,194 @@ +path: "tensorflow.keras.metrics.CategoricalAccuracy" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'categorical_accuracy\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-false-negatives.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-false-negatives.pbtxt new file mode 100644 index 0000000000..49f577e136 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-false-negatives.pbtxt @@ -0,0 +1,193 @@ +path: "tensorflow.keras.metrics.FalseNegatives" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'thresholds\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-false-positives.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-false-positives.pbtxt new file mode 100644 index 0000000000..e8baf85866 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-false-positives.pbtxt @@ -0,0 +1,193 @@ +path: "tensorflow.keras.metrics.FalsePositives" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'thresholds\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-mean.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-mean.pbtxt new file mode 100644 index 0000000000..40fe64bbd2 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-mean.pbtxt @@ -0,0 +1,192 @@ +path: "tensorflow.keras.metrics.Mean" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'mean\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'values\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-precision.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-precision.pbtxt new file mode 100644 index 0000000000..ae6a85026d --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-precision.pbtxt @@ -0,0 +1,192 @@ +path: "tensorflow.keras.metrics.Precision" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'thresholds\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-recall.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-recall.pbtxt new file mode 100644 index 0000000000..31068a51d5 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-recall.pbtxt @@ -0,0 +1,192 @@ +path: "tensorflow.keras.metrics.Recall" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'thresholds\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-sparse-categorical-accuracy.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-sparse-categorical-accuracy.pbtxt new file mode 100644 index 0000000000..0c17452292 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-sparse-categorical-accuracy.pbtxt @@ -0,0 +1,194 @@ +path: "tensorflow.keras.metrics.SparseCategoricalAccuracy" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'sparse_categorical_accuracy\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-true-negatives.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-true-negatives.pbtxt new file mode 100644 index 0000000000..1b5eb8d0de --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-true-negatives.pbtxt @@ -0,0 +1,193 @@ +path: "tensorflow.keras.metrics.TrueNegatives" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'thresholds\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-true-positives.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-true-positives.pbtxt new file mode 100644 index 0000000000..5b9c470e32 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-true-positives.pbtxt @@ -0,0 +1,193 @@ +path: "tensorflow.keras.metrics.TruePositives" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'thresholds\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.pbtxt index a296e13158..8cab17edc5 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.pbtxt @@ -1,5 +1,49 @@ path: "tensorflow.keras.metrics" tf_module { + member { + name: "Accuracy" + mtype: "" + } + member { + name: "BinaryAccuracy" + mtype: "" + } + member { + name: "CategoricalAccuracy" + mtype: "" + } + member { + name: "FalseNegatives" + mtype: "" + } + member { + name: "FalsePositives" + mtype: "" + } + member { + name: "Mean" + mtype: "" + } + member { + name: "Precision" + mtype: "" + } + member { + name: "Recall" + mtype: "" + } + member { + name: "SparseCategoricalAccuracy" + mtype: "" + } + member { + name: "TrueNegatives" + mtype: "" + } + member { + name: "TruePositives" + mtype: "" + } member_method { name: "KLD" argspec: "args=[\'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" @@ -26,7 +70,7 @@ tf_module { } member_method { name: "binary_crossentropy" - argspec: "args=[\'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + argspec: "args=[\'y_true\', \'y_pred\', \'from_logits\'], varargs=None, keywords=None, defaults=[\'False\'], " } member_method { name: "categorical_accuracy" @@ -34,7 +78,7 @@ tf_module { } member_method { name: "categorical_crossentropy" - argspec: "args=[\'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + argspec: "args=[\'y_true\', \'y_pred\', \'from_logits\'], varargs=None, keywords=None, defaults=[\'False\'], " } member_method { name: "cosine" @@ -110,7 +154,7 @@ tf_module { } member_method { name: "sparse_categorical_crossentropy" - argspec: "args=[\'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + argspec: "args=[\'y_true\', \'y_pred\', \'from_logits\'], varargs=None, keywords=None, defaults=[\'False\'], " } member_method { name: "sparse_top_k_categorical_accuracy" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.models.-model.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.models.-model.pbtxt index ccff809f2b..c58c7bef22 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.models.-model.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.models.-model.pbtxt @@ -41,6 +41,14 @@ tf_class { name: "losses" mtype: "" } + member { + name: "metrics" + mtype: "" + } + member { + name: "metrics_names" + mtype: "" + } member { name: "name" mtype: "" @@ -69,6 +77,10 @@ tf_class { name: "output_shape" mtype: "" } + member { + name: "run_eagerly" + mtype: "" + } member { name: "state_updates" mtype: "" @@ -105,13 +117,17 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { name: "add_variable" - argspec: "args=[\'self\', \'name\', \'shape\', \'dtype\', \'initializer\', \'regularizer\', \'trainable\', \'constraint\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'True\', \'None\'], " + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" } member_method { name: "add_weight" @@ -225,6 +241,10 @@ tf_class { name: "predict_on_batch" argspec: "args=[\'self\', \'x\'], varargs=None, keywords=None, defaults=None" } + member_method { + name: "reset_metrics" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } member_method { name: "reset_states" argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" @@ -247,7 +267,7 @@ tf_class { } member_method { name: "test_on_batch" - argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\', \'reset_metrics\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'True\'], " } member_method { name: "to_json" @@ -259,6 +279,6 @@ tf_class { } member_method { name: "train_on_batch" - argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\', \'class_weight\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\', \'class_weight\', \'reset_metrics\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'True\'], " } } diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.models.-sequential.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.models.-sequential.pbtxt index b0fc7f97f1..473a1c16fb 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.models.-sequential.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.models.-sequential.pbtxt @@ -42,6 +42,14 @@ tf_class { name: "losses" mtype: "" } + member { + name: "metrics" + mtype: "" + } + member { + name: "metrics_names" + mtype: "" + } member { name: "name" mtype: "" @@ -70,6 +78,10 @@ tf_class { name: "output_shape" mtype: "" } + member { + name: "run_eagerly" + mtype: "" + } member { name: "state_updates" mtype: "" @@ -110,13 +122,17 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { name: "add_variable" - argspec: "args=[\'self\', \'name\', \'shape\', \'dtype\', \'initializer\', \'regularizer\', \'trainable\', \'constraint\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'True\', \'None\'], " + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" } member_method { name: "add_weight" @@ -242,6 +258,10 @@ tf_class { name: "predict_proba" argspec: "args=[\'self\', \'x\', \'batch_size\', \'verbose\'], varargs=None, keywords=None, defaults=[\'32\', \'0\'], " } + member_method { + name: "reset_metrics" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } member_method { name: "reset_states" argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" @@ -264,7 +284,7 @@ tf_class { } member_method { name: "test_on_batch" - argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\', \'reset_metrics\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'True\'], " } member_method { name: "to_json" @@ -276,6 +296,6 @@ tf_class { } member_method { name: "train_on_batch" - argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\', \'class_weight\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + argspec: "args=[\'self\', \'x\', \'y\', \'sample_weight\', \'class_weight\', \'reset_metrics\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'True\'], " } } diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.utils.-progbar.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.utils.-progbar.pbtxt index be4496e753..8177cc71ed 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.utils.-progbar.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.utils.-progbar.pbtxt @@ -4,7 +4,7 @@ tf_class { is_instance: "" member_method { name: "__init__" - argspec: "args=[\'self\', \'target\', \'width\', \'verbose\', \'interval\', \'stateful_metrics\'], varargs=None, keywords=None, defaults=[\'30\', \'1\', \'0.05\', \'None\'], " + argspec: "args=[\'self\', \'target\', \'width\', \'verbose\', \'interval\', \'stateful_metrics\', \'unit_name\'], varargs=None, keywords=None, defaults=[\'30\', \'1\', \'0.05\', \'None\', \'step\'], " } member_method { name: "add" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-block-diag.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-block-diag.pbtxt index 973705dae2..773c74e64d 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-block-diag.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-block-diag.pbtxt @@ -79,6 +79,10 @@ tf_class { name: "batch_shape_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'batch_shape_tensor\'], " } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'det\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-circulant.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-circulant.pbtxt index de917706d5..533544d21f 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-circulant.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-circulant.pbtxt @@ -96,6 +96,10 @@ tf_class { name: "block_shape_tensor" argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "convolution_kernel" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'convolution_kernel\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-circulant2-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-circulant2-d.pbtxt index c4e6a21c3a..e3926eb6d4 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-circulant2-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-circulant2-d.pbtxt @@ -96,6 +96,10 @@ tf_class { name: "block_shape_tensor" argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "convolution_kernel" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'convolution_kernel\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-circulant3-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-circulant3-d.pbtxt index 2e085a8e28..ba209df782 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-circulant3-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-circulant3-d.pbtxt @@ -96,6 +96,10 @@ tf_class { name: "block_shape_tensor" argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "convolution_kernel" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'convolution_kernel\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-composition.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-composition.pbtxt index 42d22bce42..081fb0e08b 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-composition.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-composition.pbtxt @@ -79,6 +79,10 @@ tf_class { name: "batch_shape_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'batch_shape_tensor\'], " } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'det\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-diag.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-diag.pbtxt index d6749fdcec..2014a04301 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-diag.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-diag.pbtxt @@ -79,6 +79,10 @@ tf_class { name: "batch_shape_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'batch_shape_tensor\'], " } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'det\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-full-matrix.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-full-matrix.pbtxt index d9f363d133..9a87ae9687 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-full-matrix.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-full-matrix.pbtxt @@ -75,6 +75,10 @@ tf_class { name: "batch_shape_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'batch_shape_tensor\'], " } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'det\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-identity.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-identity.pbtxt index aac7ee31ed..33afb835ce 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-identity.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-identity.pbtxt @@ -76,6 +76,10 @@ tf_class { name: "batch_shape_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'batch_shape_tensor\'], " } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'det\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-kronecker.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-kronecker.pbtxt index c11d390829..a9078c8ab5 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-kronecker.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-kronecker.pbtxt @@ -79,6 +79,10 @@ tf_class { name: "batch_shape_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'batch_shape_tensor\'], " } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'det\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-low-rank-update.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-low-rank-update.pbtxt index 3ee800269e..4cfa3bb30d 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-low-rank-update.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-low-rank-update.pbtxt @@ -99,6 +99,10 @@ tf_class { name: "batch_shape_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'batch_shape_tensor\'], " } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'det\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-lower-triangular.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-lower-triangular.pbtxt index 63a1bc2321..a87649133f 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-lower-triangular.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-lower-triangular.pbtxt @@ -75,6 +75,10 @@ tf_class { name: "batch_shape_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'batch_shape_tensor\'], " } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'det\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-scaled-identity.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-scaled-identity.pbtxt index e2c5a505a7..3265646784 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-scaled-identity.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-scaled-identity.pbtxt @@ -80,6 +80,10 @@ tf_class { name: "batch_shape_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'batch_shape_tensor\'], " } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'det\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-zeros.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-zeros.pbtxt index a1b0e06b47..49d8890c89 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-zeros.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-zeros.pbtxt @@ -75,6 +75,10 @@ tf_class { name: "batch_shape_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'batch_shape_tensor\'], " } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'det\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator.pbtxt index 6d849dc040..c89dc067b3 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator.pbtxt @@ -74,6 +74,10 @@ tf_class { name: "batch_shape_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'batch_shape_tensor\'], " } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } member_method { name: "determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'det\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.pbtxt index 1a4098d121..a3599bfa80 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.pbtxt @@ -118,7 +118,7 @@ tf_module { } member_method { name: "l2_normalize" - argspec: "args=[\'x\', \'axis\', \'epsilon\', \'name\', \'dim\'], varargs=None, keywords=None, defaults=[\'None\', \'1e-12\', \'None\', \'None\'], " + argspec: "args=[\'x\', \'axis\', \'epsilon\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'1e-12\', \'None\'], " } member_method { name: "logdet" @@ -142,7 +142,7 @@ tf_module { } member_method { name: "norm" - argspec: "args=[\'tensor\', \'ord\', \'axis\', \'keepdims\', \'name\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'euclidean\', \'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'tensor\', \'ord\', \'axis\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'euclidean\', \'None\', \'None\', \'None\'], " } member_method { name: "qr" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.lite.constants.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.lite.constants.pbtxt index 08845553e5..4d5c4893b4 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.lite.constants.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.lite.constants.pbtxt @@ -1,29 +1,9 @@ path: "tensorflow.lite.constants" tf_module { - member { - name: "FLOAT" - mtype: "" - } member { name: "GRAPHVIZ_DOT" mtype: "" } - member { - name: "INT32" - mtype: "" - } - member { - name: "INT64" - mtype: "" - } - member { - name: "QUANTIZED_UINT8" - mtype: "" - } - member { - name: "STRING" - mtype: "" - } member { name: "TFLITE" mtype: "" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.logging.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.logging.pbtxt deleted file mode 100644 index 85bb15455d..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.logging.pbtxt +++ /dev/null @@ -1,83 +0,0 @@ -path: "tensorflow.logging" -tf_module { - member { - name: "DEBUG" - mtype: "" - } - member { - name: "ERROR" - mtype: "" - } - member { - name: "FATAL" - mtype: "" - } - member { - name: "INFO" - mtype: "" - } - member { - name: "WARN" - mtype: "" - } - member_method { - name: "TaskLevelStatusMessage" - argspec: "args=[\'msg\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "debug" - argspec: "args=[\'msg\'], varargs=args, keywords=kwargs, defaults=None" - } - member_method { - name: "error" - argspec: "args=[\'msg\'], varargs=args, keywords=kwargs, defaults=None" - } - member_method { - name: "fatal" - argspec: "args=[\'msg\'], varargs=args, keywords=kwargs, defaults=None" - } - member_method { - name: "flush" - argspec: "args=[], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "get_verbosity" - argspec: "args=[], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "info" - argspec: "args=[\'msg\'], varargs=args, keywords=kwargs, defaults=None" - } - member_method { - name: "log" - argspec: "args=[\'level\', \'msg\'], varargs=args, keywords=kwargs, defaults=None" - } - member_method { - name: "log_every_n" - argspec: "args=[\'level\', \'msg\', \'n\'], varargs=args, keywords=None, defaults=None" - } - member_method { - name: "log_first_n" - argspec: "args=[\'level\', \'msg\', \'n\'], varargs=args, keywords=None, defaults=None" - } - member_method { - name: "log_if" - argspec: "args=[\'level\', \'msg\', \'condition\'], varargs=args, keywords=None, defaults=None" - } - member_method { - name: "set_verbosity" - argspec: "args=[\'v\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "vlog" - argspec: "args=[\'level\', \'msg\'], varargs=args, keywords=kwargs, defaults=None" - } - member_method { - name: "warn" - argspec: "args=[\'msg\'], varargs=args, keywords=kwargs, defaults=None" - } - member_method { - name: "warning" - argspec: "args=[\'msg\'], varargs=args, keywords=kwargs, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.losses.-reduction.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.losses.-reduction.pbtxt index 258ad5047e..6a44e4ce66 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.losses.-reduction.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.losses.-reduction.pbtxt @@ -1,11 +1,7 @@ path: "tensorflow.losses.Reduction" tf_class { - is_instance: "" + is_instance: "" is_instance: "" - member { - name: "MEAN" - mtype: "" - } member { name: "NONE" mtype: "" @@ -14,18 +10,10 @@ tf_class { name: "SUM" mtype: "" } - member { - name: "SUM_BY_NONZERO_WEIGHTS" - mtype: "" - } member { name: "SUM_OVER_BATCH_SIZE" mtype: "" } - member { - name: "SUM_OVER_NONZERO_WEIGHTS" - mtype: "" - } member_method { name: "__init__" } diff --git a/tensorflow/tools/api/golden/v2/tensorflow.losses.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.losses.pbtxt index c1d190ae11..233b1a0131 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.losses.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.losses.pbtxt @@ -4,22 +4,10 @@ tf_module { name: "Reduction" mtype: "" } - member_method { - name: "absolute_difference" - argspec: "args=[\'labels\', \'predictions\', \'weights\', \'scope\', \'loss_collection\', \'reduction\'], varargs=None, keywords=None, defaults=[\'1.0\', \'None\', \'losses\', \'weighted_sum_by_nonzero_weights\'], " - } member_method { name: "add_loss" argspec: "args=[\'loss\', \'loss_collection\'], varargs=None, keywords=None, defaults=[\'losses\'], " } - member_method { - name: "compute_weighted_loss" - argspec: "args=[\'losses\', \'weights\', \'scope\', \'loss_collection\', \'reduction\'], varargs=None, keywords=None, defaults=[\'1.0\', \'None\', \'losses\', \'weighted_sum_by_nonzero_weights\'], " - } - member_method { - name: "cosine_distance" - argspec: "args=[\'labels\', \'predictions\', \'axis\', \'weights\', \'scope\', \'loss_collection\', \'reduction\', \'dim\'], varargs=None, keywords=None, defaults=[\'None\', \'1.0\', \'None\', \'losses\', \'weighted_sum_by_nonzero_weights\', \'None\'], " - } member_method { name: "get_losses" argspec: "args=[\'scope\', \'loss_collection\'], varargs=None, keywords=None, defaults=[\'None\', \'losses\'], " @@ -36,36 +24,4 @@ tf_module { name: "get_total_loss" argspec: "args=[\'add_regularization_losses\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'total_loss\'], " } - member_method { - name: "hinge_loss" - argspec: "args=[\'labels\', \'logits\', \'weights\', \'scope\', \'loss_collection\', \'reduction\'], varargs=None, keywords=None, defaults=[\'1.0\', \'None\', \'losses\', \'weighted_sum_by_nonzero_weights\'], " - } - member_method { - name: "huber_loss" - argspec: "args=[\'labels\', \'predictions\', \'weights\', \'delta\', \'scope\', \'loss_collection\', \'reduction\'], varargs=None, keywords=None, defaults=[\'1.0\', \'1.0\', \'None\', \'losses\', \'weighted_sum_by_nonzero_weights\'], " - } - member_method { - name: "log_loss" - argspec: "args=[\'labels\', \'predictions\', \'weights\', \'epsilon\', \'scope\', \'loss_collection\', \'reduction\'], varargs=None, keywords=None, defaults=[\'1.0\', \'1e-07\', \'None\', \'losses\', \'weighted_sum_by_nonzero_weights\'], " - } - member_method { - name: "mean_pairwise_squared_error" - argspec: "args=[\'labels\', \'predictions\', \'weights\', \'scope\', \'loss_collection\'], varargs=None, keywords=None, defaults=[\'1.0\', \'None\', \'losses\'], " - } - member_method { - name: "mean_squared_error" - argspec: "args=[\'labels\', \'predictions\', \'weights\', \'scope\', \'loss_collection\', \'reduction\'], varargs=None, keywords=None, defaults=[\'1.0\', \'None\', \'losses\', \'weighted_sum_by_nonzero_weights\'], " - } - member_method { - name: "sigmoid_cross_entropy" - argspec: "args=[\'multi_class_labels\', \'logits\', \'weights\', \'label_smoothing\', \'scope\', \'loss_collection\', \'reduction\'], varargs=None, keywords=None, defaults=[\'1.0\', \'0\', \'None\', \'losses\', \'weighted_sum_by_nonzero_weights\'], " - } - member_method { - name: "softmax_cross_entropy" - argspec: "args=[\'onehot_labels\', \'logits\', \'weights\', \'label_smoothing\', \'scope\', \'loss_collection\', \'reduction\'], varargs=None, keywords=None, defaults=[\'1.0\', \'0\', \'None\', \'losses\', \'weighted_sum_by_nonzero_weights\'], " - } - member_method { - name: "sparse_softmax_cross_entropy" - argspec: "args=[\'labels\', \'logits\', \'weights\', \'scope\', \'loss_collection\', \'reduction\'], varargs=None, keywords=None, defaults=[\'1.0\', \'None\', \'losses\', \'weighted_sum_by_nonzero_weights\'], " - } } diff --git a/tensorflow/tools/api/golden/v2/tensorflow.math.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.math.pbtxt index a441e42b0a..979d77ea6b 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.math.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.math.pbtxt @@ -78,7 +78,7 @@ tf_module { } member_method { name: "bincount" - argspec: "args=[\'arr\', \'weights\', \'minlength\', \'maxlength\', \'dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \"\"], " + argspec: "args=[\'arr\', \'weights\', \'minlength\', \'maxlength\', \'dtype\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \"\", \'None\'], " } member_method { name: "ceil" @@ -86,7 +86,7 @@ tf_module { } member_method { name: "confusion_matrix" - argspec: "args=[\'labels\', \'predictions\', \'num_classes\', \'dtype\', \'name\', \'weights\'], varargs=None, keywords=None, defaults=[\'None\', \"\", \'None\', \'None\'], " + argspec: "args=[\'labels\', \'predictions\', \'num_classes\', \'weights\', \'dtype\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \"\", \'None\'], " } member_method { name: "conj" @@ -102,7 +102,7 @@ tf_module { } member_method { name: "count_nonzero" - argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'dtype\', \'name\', \'reduction_indices\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \"\", \'None\', \'None\', \'None\'], " + argspec: "args=[\'input\', \'axis\', \'keepdims\', \'dtype\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \"\", \'None\'], " } member_method { name: "cumprod" @@ -176,9 +176,29 @@ tf_module { name: "invert_permutation" argspec: "args=[\'x\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "is_finite" + argspec: "args=[\'x\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "is_inf" + argspec: "args=[\'x\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "is_nan" + argspec: "args=[\'x\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "is_non_decreasing" + argspec: "args=[\'x\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "is_strictly_increasing" + argspec: "args=[\'x\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } member_method { name: "l2_normalize" - argspec: "args=[\'x\', \'axis\', \'epsilon\', \'name\', \'dim\'], varargs=None, keywords=None, defaults=[\'None\', \'1e-12\', \'None\', \'None\'], " + argspec: "args=[\'x\', \'axis\', \'epsilon\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'1e-12\', \'None\'], " } member_method { name: "lbeta" @@ -210,7 +230,7 @@ tf_module { } member_method { name: "log_softmax" - argspec: "args=[\'logits\', \'axis\', \'name\', \'dim\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + argspec: "args=[\'logits\', \'axis\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { name: "logical_and" @@ -270,43 +290,43 @@ tf_module { } member_method { name: "reduce_all" - argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\', \'reduction_indices\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { name: "reduce_any" - argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\', \'reduction_indices\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { name: "reduce_logsumexp" - argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\', \'reduction_indices\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { name: "reduce_max" - argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\', \'reduction_indices\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { name: "reduce_mean" - argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\', \'reduction_indices\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { name: "reduce_min" - argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\', \'reduction_indices\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { name: "reduce_prod" - argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\', \'reduction_indices\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { name: "reduce_std" - argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { name: "reduce_sum" - argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\', \'reduction_indices\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { name: "reduce_variance" - argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { name: "rint" @@ -322,7 +342,7 @@ tf_module { } member_method { name: "scalar_mul" - argspec: "args=[\'scalar\', \'x\'], varargs=None, keywords=None, defaults=None" + argspec: "args=[\'scalar\', \'x\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { name: "segment_max" @@ -362,7 +382,7 @@ tf_module { } member_method { name: "softmax" - argspec: "args=[\'logits\', \'axis\', \'name\', \'dim\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + argspec: "args=[\'logits\', \'axis\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { name: "softplus" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.metrics.-accuracy.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.metrics.-accuracy.pbtxt new file mode 100644 index 0000000000..f8e12f8817 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.metrics.-accuracy.pbtxt @@ -0,0 +1,194 @@ +path: "tensorflow.metrics.Accuracy" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'accuracy\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.metrics.-binary-accuracy.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.metrics.-binary-accuracy.pbtxt new file mode 100644 index 0000000000..b9bc6a716a --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.metrics.-binary-accuracy.pbtxt @@ -0,0 +1,194 @@ +path: "tensorflow.metrics.BinaryAccuracy" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\', \'threshold\'], varargs=None, keywords=None, defaults=[\'binary_accuracy\', \'None\', \'0.5\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.metrics.-categorical-accuracy.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.metrics.-categorical-accuracy.pbtxt new file mode 100644 index 0000000000..0ef75d8756 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.metrics.-categorical-accuracy.pbtxt @@ -0,0 +1,194 @@ +path: "tensorflow.metrics.CategoricalAccuracy" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'categorical_accuracy\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.metrics.-false-negatives.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.metrics.-false-negatives.pbtxt new file mode 100644 index 0000000000..33226a2df6 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.metrics.-false-negatives.pbtxt @@ -0,0 +1,193 @@ +path: "tensorflow.metrics.FalseNegatives" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'thresholds\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.metrics.-false-positives.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.metrics.-false-positives.pbtxt new file mode 100644 index 0000000000..9953162ea3 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.metrics.-false-positives.pbtxt @@ -0,0 +1,193 @@ +path: "tensorflow.metrics.FalsePositives" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'thresholds\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.metrics.-mean.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.metrics.-mean.pbtxt new file mode 100644 index 0000000000..7fe6d6fda9 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.metrics.-mean.pbtxt @@ -0,0 +1,192 @@ +path: "tensorflow.metrics.Mean" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'mean\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'values\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.metrics.-precision.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.metrics.-precision.pbtxt new file mode 100644 index 0000000000..8c3271a109 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.metrics.-precision.pbtxt @@ -0,0 +1,192 @@ +path: "tensorflow.metrics.Precision" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'thresholds\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.metrics.-recall.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.metrics.-recall.pbtxt new file mode 100644 index 0000000000..840a68bbc7 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.metrics.-recall.pbtxt @@ -0,0 +1,192 @@ +path: "tensorflow.metrics.Recall" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'thresholds\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.metrics.-sparse-categorical-accuracy.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.metrics.-sparse-categorical-accuracy.pbtxt new file mode 100644 index 0000000000..7bce43fbde --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.metrics.-sparse-categorical-accuracy.pbtxt @@ -0,0 +1,194 @@ +path: "tensorflow.metrics.SparseCategoricalAccuracy" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'sparse_categorical_accuracy\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.metrics.-true-negatives.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.metrics.-true-negatives.pbtxt new file mode 100644 index 0000000000..83cd5b736b --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.metrics.-true-negatives.pbtxt @@ -0,0 +1,193 @@ +path: "tensorflow.metrics.TrueNegatives" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'thresholds\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.metrics.-true-positives.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.metrics.-true-positives.pbtxt new file mode 100644 index 0000000000..5b2502eafe --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.metrics.-true-positives.pbtxt @@ -0,0 +1,193 @@ +path: "tensorflow.metrics.TruePositives" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'thresholds\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.metrics.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.metrics.pbtxt index e9b996c9f5..773efd03fc 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.metrics.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.metrics.pbtxt @@ -1,135 +1,47 @@ path: "tensorflow.metrics" tf_module { - member_method { - name: "accuracy" - argspec: "args=[\'labels\', \'predictions\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + member { + name: "Accuracy" + mtype: "" } - member_method { - name: "auc" - argspec: "args=[\'labels\', \'predictions\', \'weights\', \'num_thresholds\', \'metrics_collections\', \'updates_collections\', \'curve\', \'name\', \'summation_method\'], varargs=None, keywords=None, defaults=[\'None\', \'200\', \'None\', \'None\', \'ROC\', \'None\', \'trapezoidal\'], " + member { + name: "BinaryAccuracy" + mtype: "" } - member_method { - name: "average_precision_at_k" - argspec: "args=[\'labels\', \'predictions\', \'k\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + member { + name: "CategoricalAccuracy" + mtype: "" } - member_method { - name: "false_negatives" - argspec: "args=[\'labels\', \'predictions\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + member { + name: "FalseNegatives" + mtype: "" } - member_method { - name: "false_negatives_at_thresholds" - argspec: "args=[\'labels\', \'predictions\', \'thresholds\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + member { + name: "FalsePositives" + mtype: "" } - member_method { - name: "false_positives" - argspec: "args=[\'labels\', \'predictions\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + member { + name: "Mean" + mtype: "" } - member_method { - name: "false_positives_at_thresholds" - argspec: "args=[\'labels\', \'predictions\', \'thresholds\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + member { + name: "Precision" + mtype: "" } - member_method { - name: "mean" - argspec: "args=[\'values\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + member { + name: "Recall" + mtype: "" } - member_method { - name: "mean_absolute_error" - argspec: "args=[\'labels\', \'predictions\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + member { + name: "SparseCategoricalAccuracy" + mtype: "" } - member_method { - name: "mean_cosine_distance" - argspec: "args=[\'labels\', \'predictions\', \'dim\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + member { + name: "TrueNegatives" + mtype: "" } - member_method { - name: "mean_iou" - argspec: "args=[\'labels\', \'predictions\', \'num_classes\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "mean_per_class_accuracy" - argspec: "args=[\'labels\', \'predictions\', \'num_classes\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "mean_relative_error" - argspec: "args=[\'labels\', \'predictions\', \'normalizer\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "mean_squared_error" - argspec: "args=[\'labels\', \'predictions\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "mean_tensor" - argspec: "args=[\'values\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "percentage_below" - argspec: "args=[\'values\', \'threshold\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "precision" - argspec: "args=[\'labels\', \'predictions\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "precision_at_k" - argspec: "args=[\'labels\', \'predictions\', \'k\', \'class_id\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "precision_at_thresholds" - argspec: "args=[\'labels\', \'predictions\', \'thresholds\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "precision_at_top_k" - argspec: "args=[\'labels\', \'predictions_idx\', \'k\', \'class_id\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "recall" - argspec: "args=[\'labels\', \'predictions\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "recall_at_k" - argspec: "args=[\'labels\', \'predictions\', \'k\', \'class_id\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "recall_at_thresholds" - argspec: "args=[\'labels\', \'predictions\', \'thresholds\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "recall_at_top_k" - argspec: "args=[\'labels\', \'predictions_idx\', \'k\', \'class_id\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "root_mean_squared_error" - argspec: "args=[\'labels\', \'predictions\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "sensitivity_at_specificity" - argspec: "args=[\'labels\', \'predictions\', \'specificity\', \'weights\', \'num_thresholds\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'200\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "sparse_average_precision_at_k" - argspec: "args=[\'labels\', \'predictions\', \'k\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "sparse_precision_at_k" - argspec: "args=[\'labels\', \'predictions\', \'k\', \'class_id\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "specificity_at_sensitivity" - argspec: "args=[\'labels\', \'predictions\', \'sensitivity\', \'weights\', \'num_thresholds\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'200\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "true_negatives" - argspec: "args=[\'labels\', \'predictions\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "true_negatives_at_thresholds" - argspec: "args=[\'labels\', \'predictions\', \'thresholds\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "true_positives" - argspec: "args=[\'labels\', \'predictions\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "true_positives_at_thresholds" - argspec: "args=[\'labels\', \'predictions\', \'thresholds\', \'weights\', \'metrics_collections\', \'updates_collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + member { + name: "TruePositives" + mtype: "" } } diff --git a/tensorflow/tools/api/golden/v2/tensorflow.nn.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.nn.pbtxt index 2dc5c48aa6..63bf24b5d5 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.nn.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.nn.pbtxt @@ -30,7 +30,7 @@ tf_module { } member_method { name: "batch_norm_with_global_normalization" - argspec: "args=[\'t\', \'m\', \'v\', \'beta\', \'gamma\', \'variance_epsilon\', \'scale_after_normalization\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + argspec: "args=[\'input\', \'mean\', \'variance\', \'beta\', \'gamma\', \'variance_epsilon\', \'scale_after_normalization\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { name: "batch_normalization" @@ -41,8 +41,8 @@ tf_module { argspec: "args=[\'value\', \'bias\', \'data_format\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { - name: "bidirectional_dynamic_rnn" - argspec: "args=[\'cell_fw\', \'cell_bw\', \'inputs\', \'sequence_length\', \'initial_state_fw\', \'initial_state_bw\', \'dtype\', \'parallel_iterations\', \'swap_memory\', \'time_major\', \'scope\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\', \'False\', \'False\', \'None\'], " + name: "collapse_repeated" + argspec: "args=[\'labels\', \'seq_length\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { name: "compute_accidental_hits" @@ -50,43 +50,43 @@ tf_module { } member_method { name: "conv1d" - argspec: "args=[\'value\', \'filters\', \'stride\', \'padding\', \'use_cudnn_on_gpu\', \'data_format\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + argspec: "args=[\'input\', \'filters\', \'stride\', \'padding\', \'data_format\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { name: "conv2d" - argspec: "args=[\'input\', \'filter\', \'strides\', \'padding\', \'use_cudnn_on_gpu\', \'data_format\', \'dilations\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'NHWC\', \'[1, 1, 1, 1]\', \'None\'], " + argspec: "args=[\'input\', \'filters\', \'strides\', \'padding\', \'data_format\', \'dilations\', \'name\'], varargs=None, keywords=None, defaults=[\'NHWC\', \'None\', \'None\'], " } member_method { name: "conv2d_backprop_filter" - argspec: "args=[\'input\', \'filter_sizes\', \'out_backprop\', \'strides\', \'padding\', \'use_cudnn_on_gpu\', \'data_format\', \'dilations\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'NHWC\', \'[1, 1, 1, 1]\', \'None\'], " + argspec: "args=[\'input\', \'filter_sizes\', \'out_backprop\', \'strides\', \'padding\', \'data_format\', \'dilations\', \'name\'], varargs=None, keywords=None, defaults=[\'NHWC\', \'None\', \'None\'], " } member_method { name: "conv2d_backprop_input" - argspec: "args=[\'input_sizes\', \'filter\', \'out_backprop\', \'strides\', \'padding\', \'use_cudnn_on_gpu\', \'data_format\', \'dilations\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'NHWC\', \'[1, 1, 1, 1]\', \'None\'], " + argspec: "args=[\'input_sizes\', \'filters\', \'out_backprop\', \'strides\', \'padding\', \'data_format\', \'dilations\', \'name\'], varargs=None, keywords=None, defaults=[\'NHWC\', \'None\', \'None\'], " } member_method { name: "conv2d_transpose" - argspec: "args=[\'value\', \'filter\', \'output_shape\', \'strides\', \'padding\', \'data_format\', \'name\'], varargs=None, keywords=None, defaults=[\'SAME\', \'NHWC\', \'None\'], " + argspec: "args=[\'input\', \'filters\', \'output_shape\', \'strides\', \'padding\', \'data_format\', \'name\'], varargs=None, keywords=None, defaults=[\'SAME\', \'NHWC\', \'None\'], " } member_method { name: "conv3d" - argspec: "args=[\'input\', \'filter\', \'strides\', \'padding\', \'data_format\', \'dilations\', \'name\'], varargs=None, keywords=None, defaults=[\'NDHWC\', \'[1, 1, 1, 1, 1]\', \'None\'], " + argspec: "args=[\'input\', \'filters\', \'strides\', \'padding\', \'data_format\', \'dilations\', \'name\'], varargs=None, keywords=None, defaults=[\'NDHWC\', \'None\', \'None\'], " } member_method { - name: "conv3d_backprop_filter_v2" + name: "conv3d_backprop_filter" argspec: "args=[\'input\', \'filter_sizes\', \'out_backprop\', \'strides\', \'padding\', \'data_format\', \'dilations\', \'name\'], varargs=None, keywords=None, defaults=[\'NDHWC\', \'[1, 1, 1, 1, 1]\', \'None\'], " } member_method { name: "conv3d_transpose" - argspec: "args=[\'value\', \'filter\', \'output_shape\', \'strides\', \'padding\', \'data_format\', \'name\'], varargs=None, keywords=None, defaults=[\'SAME\', \'NDHWC\', \'None\'], " + argspec: "args=[\'input\', \'filters\', \'output_shape\', \'strides\', \'padding\', \'data_format\', \'name\'], varargs=None, keywords=None, defaults=[\'SAME\', \'NDHWC\', \'None\'], " } member_method { name: "convolution" - argspec: "args=[\'input\', \'filter\', \'padding\', \'strides\', \'dilation_rate\', \'name\', \'data_format\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'input\', \'filters\', \'strides\', \'padding\', \'data_format\', \'dilations\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'VALID\', \'None\', \'None\', \'None\'], " } member_method { name: "crelu" - argspec: "args=[\'features\', \'name\', \'axis\'], varargs=None, keywords=None, defaults=[\'None\', \'-1\'], " + argspec: "args=[\'features\', \'axis\', \'name\'], varargs=None, keywords=None, defaults=[\'-1\', \'None\'], " } member_method { name: "ctc_beam_search_decoder" @@ -98,7 +98,11 @@ tf_module { } member_method { name: "ctc_loss" - argspec: "args=[\'labels\', \'inputs\', \'sequence_length\', \'preprocess_collapse_repeated\', \'ctc_merge_repeated\', \'ignore_longer_outputs_than_inputs\', \'time_major\'], varargs=None, keywords=None, defaults=[\'False\', \'True\', \'False\', \'True\'], " + argspec: "args=[\'labels\', \'logits\', \'label_length\', \'logit_length\', \'logits_time_major\', \'unique\', \'blank_index\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'None\', \'None\', \'None\'], " + } + member_method { + name: "ctc_unique_labels" + argspec: "args=[\'labels\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { name: "depth_to_space" @@ -106,27 +110,23 @@ tf_module { } member_method { name: "depthwise_conv2d" - argspec: "args=[\'input\', \'filter\', \'strides\', \'padding\', \'rate\', \'name\', \'data_format\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + argspec: "args=[\'input\', \'filter\', \'strides\', \'padding\', \'data_format\', \'dilations\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " } member_method { - name: "depthwise_conv2d_native" - argspec: "args=[\'input\', \'filter\', \'strides\', \'padding\', \'data_format\', \'dilations\', \'name\'], varargs=None, keywords=None, defaults=[\'NHWC\', \'[1, 1, 1, 1]\', \'None\'], " - } - member_method { - name: "depthwise_conv2d_native_backprop_filter" + name: "depthwise_conv2d_backprop_filter" argspec: "args=[\'input\', \'filter_sizes\', \'out_backprop\', \'strides\', \'padding\', \'data_format\', \'dilations\', \'name\'], varargs=None, keywords=None, defaults=[\'NHWC\', \'[1, 1, 1, 1]\', \'None\'], " } member_method { - name: "depthwise_conv2d_native_backprop_input" + name: "depthwise_conv2d_backprop_input" argspec: "args=[\'input_sizes\', \'filter\', \'out_backprop\', \'strides\', \'padding\', \'data_format\', \'dilations\', \'name\'], varargs=None, keywords=None, defaults=[\'NHWC\', \'[1, 1, 1, 1]\', \'None\'], " } member_method { name: "dilation2d" - argspec: "args=[\'input\', \'filter\', \'strides\', \'rates\', \'padding\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + argspec: "args=[\'input\', \'filters\', \'strides\', \'padding\', \'data_format\', \'dilations\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { name: "dropout" - argspec: "args=[\'x\', \'keep_prob\', \'noise_shape\', \'seed\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + argspec: "args=[\'x\', \'rate\', \'noise_shape\', \'seed\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " } member_method { name: "elu" @@ -142,7 +142,7 @@ tf_module { } member_method { name: "erosion2d" - argspec: "args=[\'value\', \'kernel\', \'strides\', \'rates\', \'padding\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + argspec: "args=[\'value\', \'filters\', \'strides\', \'padding\', \'data_format\', \'dilations\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { name: "fixed_unigram_candidate_sampler" @@ -150,15 +150,11 @@ tf_module { } member_method { name: "fractional_avg_pool" - argspec: "args=[\'value\', \'pooling_ratio\', \'pseudo_random\', \'overlapping\', \'deterministic\', \'seed\', \'seed2\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'False\', \'False\', \'0\', \'0\', \'None\'], " + argspec: "args=[\'value\', \'pooling_ratio\', \'pseudo_random\', \'overlapping\', \'seed\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'False\', \'0\', \'None\'], " } member_method { name: "fractional_max_pool" - argspec: "args=[\'value\', \'pooling_ratio\', \'pseudo_random\', \'overlapping\', \'deterministic\', \'seed\', \'seed2\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'False\', \'False\', \'0\', \'0\', \'None\'], " - } - member_method { - name: "fused_batch_norm" - argspec: "args=[\'x\', \'scale\', \'offset\', \'mean\', \'variance\', \'epsilon\', \'data_format\', \'is_training\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'0.001\', \'NHWC\', \'True\', \'None\'], " + argspec: "args=[\'value\', \'pooling_ratio\', \'pseudo_random\', \'overlapping\', \'seed\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'False\', \'0\', \'None\'], " } member_method { name: "in_top_k" @@ -170,7 +166,7 @@ tf_module { } member_method { name: "l2_normalize" - argspec: "args=[\'x\', \'axis\', \'epsilon\', \'name\', \'dim\'], varargs=None, keywords=None, defaults=[\'None\', \'1e-12\', \'None\', \'None\'], " + argspec: "args=[\'x\', \'axis\', \'epsilon\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'1e-12\', \'None\'], " } member_method { name: "leaky_relu" @@ -190,7 +186,7 @@ tf_module { } member_method { name: "log_softmax" - argspec: "args=[\'logits\', \'axis\', \'name\', \'dim\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + argspec: "args=[\'logits\', \'axis\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { name: "lrn" @@ -206,15 +202,15 @@ tf_module { } member_method { name: "max_pool_with_argmax" - argspec: "args=[\'input\', \'ksize\', \'strides\', \'padding\', \'Targmax\', \'name\'], varargs=None, keywords=None, defaults=[\"\", \'None\'], " + argspec: "args=[\'input\', \'ksize\', \'strides\', \'padding\', \'data_format\', \'output_dtype\', \'name\'], varargs=None, keywords=None, defaults=[\'NHWC\', \"\", \'None\'], " } member_method { name: "moments" - argspec: "args=[\'x\', \'axes\', \'shift\', \'name\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'False\'], " + argspec: "args=[\'x\', \'axes\', \'shift\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { name: "nce_loss" - argspec: "args=[\'weights\', \'biases\', \'labels\', \'inputs\', \'num_sampled\', \'num_classes\', \'num_true\', \'sampled_values\', \'remove_accidental_hits\', \'partition_strategy\', \'name\'], varargs=None, keywords=None, defaults=[\'1\', \'None\', \'False\', \'mod\', \'nce_loss\'], " + argspec: "args=[\'weights\', \'biases\', \'labels\', \'inputs\', \'num_sampled\', \'num_classes\', \'num_true\', \'sampled_values\', \'remove_accidental_hits\', \'name\'], varargs=None, keywords=None, defaults=[\'1\', \'None\', \'False\', \'nce_loss\'], " } member_method { name: "normalize_moments" @@ -222,23 +218,7 @@ tf_module { } member_method { name: "pool" - argspec: "args=[\'input\', \'window_shape\', \'pooling_type\', \'padding\', \'dilation_rate\', \'strides\', \'name\', \'data_format\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "quantized_avg_pool" - argspec: "args=[\'input\', \'min_input\', \'max_input\', \'ksize\', \'strides\', \'padding\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "quantized_conv2d" - argspec: "args=[\'input\', \'filter\', \'min_input\', \'max_input\', \'min_filter\', \'max_filter\', \'strides\', \'padding\', \'out_type\', \'dilations\', \'name\'], varargs=None, keywords=None, defaults=[\"\", \'[1, 1, 1, 1]\', \'None\'], " - } - member_method { - name: "quantized_max_pool" - argspec: "args=[\'input\', \'min_input\', \'max_input\', \'ksize\', \'strides\', \'padding\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "quantized_relu_x" - argspec: "args=[\'features\', \'max_value\', \'min_features\', \'max_features\', \'out_type\', \'name\'], varargs=None, keywords=None, defaults=[\"\", \'None\'], " + argspec: "args=[\'input\', \'window_shape\', \'pooling_type\', \'strides\', \'padding\', \'data_format\', \'dilations\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'VALID\', \'None\', \'None\', \'None\'], " } member_method { name: "relu" @@ -266,7 +246,7 @@ tf_module { } member_method { name: "separable_conv2d" - argspec: "args=[\'input\', \'depthwise_filter\', \'pointwise_filter\', \'strides\', \'padding\', \'rate\', \'name\', \'data_format\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + argspec: "args=[\'input\', \'depthwise_filter\', \'pointwise_filter\', \'strides\', \'padding\', \'data_format\', \'dilations\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " } member_method { name: "sigmoid" @@ -278,11 +258,11 @@ tf_module { } member_method { name: "softmax" - argspec: "args=[\'logits\', \'axis\', \'name\', \'dim\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + argspec: "args=[\'logits\', \'axis\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { name: "softmax_cross_entropy_with_logits" - argspec: "args=[\'labels\', \'logits\', \'dim\', \'name\'], varargs=None, keywords=None, defaults=[\'-1\', \'None\'], " + argspec: "args=[\'labels\', \'logits\', \'axis\', \'name\'], varargs=None, keywords=None, defaults=[\'-1\', \'None\'], " } member_method { name: "softplus" @@ -304,17 +284,13 @@ tf_module { name: "sparse_softmax_cross_entropy_with_logits" argspec: "args=[\'_sentinel\', \'labels\', \'logits\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } - member_method { - name: "static_bidirectional_rnn" - argspec: "args=[\'cell_fw\', \'cell_bw\', \'inputs\', \'initial_state_fw\', \'initial_state_bw\', \'dtype\', \'sequence_length\', \'scope\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " - } member_method { name: "static_state_saving_rnn" argspec: "args=[\'cell\', \'inputs\', \'state_saver\', \'state_name\', \'sequence_length\', \'scope\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { name: "sufficient_statistics" - argspec: "args=[\'x\', \'axes\', \'shift\', \'keep_dims\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + argspec: "args=[\'x\', \'axes\', \'shift\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { name: "tanh" @@ -330,16 +306,12 @@ tf_module { } member_method { name: "weighted_moments" - argspec: "args=[\'x\', \'axes\', \'frequency_weights\', \'name\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'False\'], " + argspec: "args=[\'x\', \'axes\', \'frequency_weights\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'None\'], " } member_method { name: "with_space_to_batch" argspec: "args=[\'input\', \'dilation_rate\', \'padding\', \'op\', \'filter_shape\', \'spatial_dims\', \'data_format\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " } - member_method { - name: "xw_plus_b" - argspec: "args=[\'x\', \'weights\', \'biases\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } member_method { name: "zero_fraction" argspec: "args=[\'value\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.nn.rnn_cell.-device-wrapper.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.nn.rnn_cell.-device-wrapper.pbtxt index 381c4975d7..9e52a42526 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.nn.rnn_cell.-device-wrapper.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.nn.rnn_cell.-device-wrapper.pbtxt @@ -106,6 +106,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.nn.rnn_cell.-dropout-wrapper.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.nn.rnn_cell.-dropout-wrapper.pbtxt index 912365a28b..9836433d08 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.nn.rnn_cell.-dropout-wrapper.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.nn.rnn_cell.-dropout-wrapper.pbtxt @@ -110,6 +110,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.nn.rnn_cell.-r-n-n-cell.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.nn.rnn_cell.-r-n-n-cell.pbtxt index faeb4f3513..d3b68e4f29 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.nn.rnn_cell.-r-n-n-cell.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.nn.rnn_cell.-r-n-n-cell.pbtxt @@ -105,6 +105,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.nn.rnn_cell.-residual-wrapper.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.nn.rnn_cell.-residual-wrapper.pbtxt index caa2e60080..1f7840ab91 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.nn.rnn_cell.-residual-wrapper.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.nn.rnn_cell.-residual-wrapper.pbtxt @@ -106,6 +106,10 @@ tf_class { name: "add_loss" argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "add_update" argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.nn.rnn_cell.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.nn.rnn_cell.pbtxt index 3c78b07b39..b1f687f529 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.nn.rnn_cell.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.nn.rnn_cell.pbtxt @@ -12,10 +12,6 @@ tf_module { name: "LSTMStateTuple" mtype: "" } - member { - name: "MultiRNNCell" - mtype: "" - } member { name: "RNNCell" mtype: "" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.pbtxt index 078b471a4c..cb38ae0b49 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.pbtxt @@ -8,14 +8,6 @@ tf_module { name: "AttrValue" mtype: "" } - member { - name: "ConditionalAccumulator" - mtype: "" - } - member { - name: "ConditionalAccumulatorBase" - mtype: "" - } member { name: "ConfigProto" mtype: "" @@ -24,14 +16,6 @@ tf_module { name: "DType" mtype: "" } - member { - name: "DeviceSpec" - mtype: "" - } - member { - name: "Dimension" - mtype: "" - } member { name: "Event" mtype: "" @@ -56,10 +40,6 @@ tf_module { name: "GraphDef" mtype: "" } - member { - name: "GraphKeys" - mtype: "" - } member { name: "GraphOptions" mtype: "" @@ -137,11 +117,11 @@ tf_module { mtype: "" } member { - name: "TensorInfo" - mtype: "" + name: "TensorShape" + mtype: "" } member { - name: "TensorShape" + name: "TensorSpec" mtype: "" } member { @@ -160,10 +140,6 @@ tf_module { name: "VariableSynchronization" mtype: "" } - member { - name: "app" - mtype: "" - } member { name: "bfloat16" mtype: "" @@ -200,6 +176,10 @@ tf_module { name: "debugging" mtype: "" } + member { + name: "distribute" + mtype: "" + } member { name: "double" mtype: "" @@ -224,10 +204,6 @@ tf_module { name: "feature_column" mtype: "" } - member { - name: "flags" - mtype: "" - } member { name: "float16" mtype: "" @@ -240,10 +216,6 @@ tf_module { name: "float64" mtype: "" } - member { - name: "gfile" - mtype: "" - } member { name: "glorot_uniform_initializer" mtype: "" @@ -296,10 +268,6 @@ tf_module { name: "lite" mtype: "" } - member { - name: "logging" - mtype: "" - } member { name: "losses" mtype: "" @@ -328,14 +296,6 @@ tf_module { name: "ones_initializer" mtype: "" } - member { - name: "profiler" - mtype: "" - } - member { - name: "pywrap_tensorflow" - mtype: "" - } member { name: "qint16" mtype: "" @@ -392,10 +352,6 @@ tf_module { name: "sparse" mtype: "" } - member { - name: "spectral" - mtype: "" - } member { name: "string" mtype: "" @@ -476,14 +432,6 @@ tf_module { name: "add_n" argspec: "args=[\'inputs\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } - member_method { - name: "arg_max" - argspec: "args=[\'input\', \'dimension\', \'output_type\', \'name\'], varargs=None, keywords=None, defaults=[\"\", \'None\'], " - } - member_method { - name: "arg_min" - argspec: "args=[\'input\', \'dimension\', \'output_type\', \'name\'], varargs=None, keywords=None, defaults=[\"\", \'None\'], " - } member_method { name: "argmax" argspec: "args=[\'input\', \'axis\', \'output_type\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \"\", \'None\'], " @@ -492,6 +440,10 @@ tf_module { name: "argmin" argspec: "args=[\'input\', \'axis\', \'output_type\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \"\", \'None\'], " } + member_method { + name: "argsort" + argspec: "args=[\'values\', \'axis\', \'direction\', \'stable\', \'name\'], varargs=None, keywords=None, defaults=[\'-1\', \'ASCENDING\', \'False\', \'None\'], " + } member_method { name: "as_dtype" argspec: "args=[\'type_value\'], varargs=None, keywords=None, defaults=None" @@ -510,19 +462,19 @@ tf_module { } member_method { name: "assert_equal" - argspec: "args=[\'x\', \'y\', \'data\', \'summarize\', \'message\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'x\', \'y\', \'message\', \'summarize\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " } member_method { name: "assert_greater" - argspec: "args=[\'x\', \'y\', \'data\', \'summarize\', \'message\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'x\', \'y\', \'message\', \'summarize\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " } member_method { name: "assert_less" - argspec: "args=[\'x\', \'y\', \'data\', \'summarize\', \'message\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'x\', \'y\', \'message\', \'summarize\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " } member_method { name: "assert_rank" - argspec: "args=[\'x\', \'rank\', \'data\', \'summarize\', \'message\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'x\', \'rank\', \'message\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { name: "atan" @@ -546,10 +498,6 @@ tf_module { } member_method { name: "batch_to_space" - argspec: "args=[\'input\', \'crops\', \'block_size\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "batch_to_space_nd" argspec: "args=[\'input\', \'block_shape\', \'crops\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { @@ -580,10 +528,6 @@ tf_module { name: "cast" argspec: "args=[\'x\', \'dtype\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } - member_method { - name: "clip_by_average_norm" - argspec: "args=[\'t\', \'clip_norm\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } member_method { name: "clip_by_global_norm" argspec: "args=[\'t_list\', \'clip_norm\', \'use_norm\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " @@ -606,11 +550,11 @@ tf_module { } member_method { name: "cond" - argspec: "args=[\'pred\', \'true_fn\', \'false_fn\', \'strict\', \'name\', \'fn1\', \'fn2\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'False\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'pred\', \'true_fn\', \'false_fn\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " } member_method { name: "constant" - argspec: "args=[\'value\', \'dtype\', \'shape\', \'name\', \'verify_shape\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'Const\', \'False\'], " + argspec: "args=[\'value\', \'dtype\', \'shape\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'Const\'], " } member_method { name: "control_dependencies" @@ -628,14 +572,6 @@ tf_module { name: "cosh" argspec: "args=[\'x\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } - member_method { - name: "count_nonzero" - argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'dtype\', \'name\', \'reduction_indices\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \"\", \'None\', \'None\', \'None\'], " - } - member_method { - name: "create_partitioned_variables" - argspec: "args=[\'shape\', \'slicing\', \'initializer\', \'dtype\', \'trainable\', \'collections\', \'name\', \'reuse\'], varargs=None, keywords=None, defaults=[\"\", \'True\', \'None\', \'None\', \'None\'], " - } member_method { name: "cumsum" argspec: "args=[\'x\', \'axis\', \'exclusive\', \'reverse\', \'name\'], varargs=None, keywords=None, defaults=[\'0\', \'False\', \'False\', \'None\'], " @@ -648,10 +584,6 @@ tf_module { name: "device" argspec: "args=[\'device_name\'], varargs=None, keywords=None, defaults=None" } - member_method { - name: "div" - argspec: "args=[\'x\', \'y\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } member_method { name: "div_no_nan" argspec: "args=[\'x\', \'y\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -676,10 +608,6 @@ tf_module { name: "einsum" argspec: "args=[\'equation\'], varargs=inputs, keywords=kwargs, defaults=None" } - member_method { - name: "enable_eager_execution" - argspec: "args=[\'config\', \'device_policy\', \'execution_mode\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " - } member_method { name: "ensure_shape" argspec: "args=[\'x\', \'shape\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -698,7 +626,7 @@ tf_module { } member_method { name: "expand_dims" - argspec: "args=[\'input\', \'axis\', \'name\', \'dim\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + argspec: "args=[\'input\', \'axis\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { name: "extract_volume_patches" @@ -712,10 +640,6 @@ tf_module { name: "fill" argspec: "args=[\'dims\', \'value\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } - member_method { - name: "fixed_size_partitioner" - argspec: "args=[\'num_shards\', \'axis\'], varargs=None, keywords=None, defaults=[\'0\'], " - } member_method { name: "floor" argspec: "args=[\'x\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -738,31 +662,23 @@ tf_module { } member_method { name: "function" - argspec: "args=[\'func\', \'input_signature\', \'autograph\', \'experimental_autograph_options\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'False\', \'None\'], " + argspec: "args=[\'func\', \'input_signature\', \'autograph\', \'experimental_autograph_options\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'True\', \'None\'], " } member_method { name: "gather" - argspec: "args=[\'params\', \'indices\', \'validate_indices\', \'name\', \'axis\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'0\'], " + argspec: "args=[\'params\', \'indices\', \'validate_indices\', \'axis\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'0\', \'None\'], " } member_method { name: "gather_nd" argspec: "args=[\'params\', \'indices\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { - name: "get_collection" - argspec: "args=[\'key\', \'scope\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "get_collection_ref" - argspec: "args=[\'key\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "get_default_graph" + name: "get_logger" argspec: "args=[], varargs=None, keywords=None, defaults=None" } member_method { name: "gradients" - argspec: "args=[\'ys\', \'xs\', \'grad_ys\', \'name\', \'colocate_gradients_with_ops\', \'gate_gradients\', \'aggregation_method\', \'stop_gradients\', \'unconnected_gradients\'], varargs=None, keywords=None, defaults=[\'None\', \'gradients\', \'False\', \'False\', \'None\', \'None\', \'UnconnectedGradients.NONE\'], " + argspec: "args=[\'ys\', \'xs\', \'grad_ys\', \'name\', \'gate_gradients\', \'aggregation_method\', \'stop_gradients\', \'unconnected_gradients\'], varargs=None, keywords=None, defaults=[\'None\', \'gradients\', \'False\', \'None\', \'None\', \'UnconnectedGradients.NONE\'], " } member_method { name: "greater" @@ -782,7 +698,7 @@ tf_module { } member_method { name: "hessians" - argspec: "args=[\'ys\', \'xs\', \'name\', \'colocate_gradients_with_ops\', \'gate_gradients\', \'aggregation_method\'], varargs=None, keywords=None, defaults=[\'hessians\', \'False\', \'False\', \'None\'], " + argspec: "args=[\'ys\', \'xs\', \'gate_gradients\', \'aggregation_method\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'None\', \'hessians\'], " } member_method { name: "histogram_fixed_width" @@ -816,18 +732,10 @@ tf_module { name: "less_equal" argspec: "args=[\'x\', \'y\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } - member_method { - name: "lin_space" - argspec: "args=[\'start\', \'stop\', \'num\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } member_method { name: "linspace" argspec: "args=[\'start\', \'stop\', \'num\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } - member_method { - name: "load_file_system_library" - argspec: "args=[\'library_filename\'], varargs=None, keywords=None, defaults=None" - } member_method { name: "load_library" argspec: "args=[\'library_location\'], varargs=None, keywords=None, defaults=None" @@ -836,14 +744,6 @@ tf_module { name: "load_op_library" argspec: "args=[\'library_filename\'], varargs=None, keywords=None, defaults=None" } - member_method { - name: "log" - argspec: "args=[\'x\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "log1p" - argspec: "args=[\'x\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } member_method { name: "logical_and" argspec: "args=[\'x\', \'y\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -880,10 +780,6 @@ tf_module { name: "meshgrid" argspec: "args=[], varargs=args, keywords=kwargs, defaults=None" } - member_method { - name: "min_max_variable_partitioner" - argspec: "args=[\'max_partitions\', \'axis\', \'min_slice_size\', \'bytes_per_string_element\'], varargs=None, keywords=None, defaults=[\'1\', \'0\', \'262144\', \'16\'], " - } member_method { name: "minimum" argspec: "args=[\'x\', \'y\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -892,10 +788,6 @@ tf_module { name: "mod" argspec: "args=[\'x\', \'y\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } - member_method { - name: "multinomial" - argspec: "args=[\'logits\', \'num_samples\', \'seed\', \'name\', \'output_dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " - } member_method { name: "multiply" argspec: "args=[\'x\', \'y\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -918,7 +810,7 @@ tf_module { } member_method { name: "norm" - argspec: "args=[\'tensor\', \'ord\', \'axis\', \'keepdims\', \'name\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'euclidean\', \'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'tensor\', \'ord\', \'axis\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'euclidean\', \'None\', \'None\', \'None\'], " } member_method { name: "not_equal" @@ -934,11 +826,11 @@ tf_module { } member_method { name: "ones_like" - argspec: "args=[\'tensor\', \'dtype\', \'name\', \'optimize\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'True\'], " + argspec: "args=[\'input\', \'dtype\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { name: "pad" - argspec: "args=[\'tensor\', \'paddings\', \'mode\', \'name\', \'constant_values\'], varargs=None, keywords=None, defaults=[\'CONSTANT\', \'None\', \'0\'], " + argspec: "args=[\'tensor\', \'paddings\', \'mode\', \'constant_values\', \'name\'], varargs=None, keywords=None, defaults=[\'CONSTANT\', \'0\', \'None\'], " } member_method { name: "parallel_stack" @@ -956,10 +848,6 @@ tf_module { name: "py_function" argspec: "args=[\'func\', \'inp\', \'Tout\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } - member_method { - name: "quantize_v2" - argspec: "args=[\'input\', \'min_range\', \'max_range\', \'T\', \'mode\', \'name\', \'round_mode\'], varargs=None, keywords=None, defaults=[\'MIN_COMBINED\', \'None\', \'HALF_AWAY_FROM_ZERO\'], " - } member_method { name: "range" argspec: "args=[\'start\', \'limit\', \'delta\', \'dtype\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'1\', \'None\', \'range\'], " @@ -974,35 +862,35 @@ tf_module { } member_method { name: "reduce_all" - argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\', \'reduction_indices\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { name: "reduce_any" - argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\', \'reduction_indices\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { name: "reduce_logsumexp" - argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\', \'reduction_indices\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { name: "reduce_max" - argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\', \'reduction_indices\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { name: "reduce_mean" - argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\', \'reduction_indices\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { name: "reduce_min" - argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\', \'reduction_indices\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { name: "reduce_prod" - argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\', \'reduction_indices\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { name: "reduce_sum" - argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\', \'reduction_indices\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'input_tensor\', \'axis\', \'keepdims\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " } member_method { name: "register_tensor_conversion_function" @@ -1012,10 +900,6 @@ tf_module { name: "required_space_to_batch_paddings" argspec: "args=[\'input_shape\', \'block_shape\', \'base_paddings\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } - member_method { - name: "reset_default_graph" - argspec: "args=[], varargs=None, keywords=None, defaults=None" - } member_method { name: "reshape" argspec: "args=[\'tensor\', \'shape\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -1026,7 +910,7 @@ tf_module { } member_method { name: "reverse_sequence" - argspec: "args=[\'input\', \'seq_lengths\', \'seq_axis\', \'batch_axis\', \'name\', \'seq_dim\', \'batch_dim\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'input\', \'seq_lengths\', \'seq_axis\', \'batch_axis\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " } member_method { name: "roll" @@ -1042,7 +926,7 @@ tf_module { } member_method { name: "scalar_mul" - argspec: "args=[\'scalar\', \'x\'], varargs=None, keywords=None, defaults=None" + argspec: "args=[\'scalar\', \'x\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { name: "scan" @@ -1076,13 +960,9 @@ tf_module { name: "sequence_mask" argspec: "args=[\'lengths\', \'maxlen\', \'dtype\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \"\", \'None\'], " } - member_method { - name: "set_random_seed" - argspec: "args=[\'seed\'], varargs=None, keywords=None, defaults=None" - } member_method { name: "shape" - argspec: "args=[\'input\', \'name\', \'out_type\'], varargs=None, keywords=None, defaults=[\'None\', \"\"], " + argspec: "args=[\'input\', \'out_type\', \'name\'], varargs=None, keywords=None, defaults=[\"\", \'None\'], " } member_method { name: "shape_n" @@ -1106,43 +986,19 @@ tf_module { } member_method { name: "size" - argspec: "args=[\'input\', \'name\', \'out_type\'], varargs=None, keywords=None, defaults=[\'None\', \"\"], " + argspec: "args=[\'input\', \'out_type\', \'name\'], varargs=None, keywords=None, defaults=[\"\", \'None\'], " } member_method { name: "slice" argspec: "args=[\'input_\', \'begin\', \'size\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { - name: "space_to_batch_nd" - argspec: "args=[\'input\', \'block_shape\', \'paddings\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "sparse_concat" - argspec: "args=[\'axis\', \'sp_inputs\', \'name\', \'expand_nonconcat_dim\', \'concat_dim\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " - } - member_method { - name: "sparse_reduce_max" - argspec: "args=[\'sp_input\', \'axis\', \'keepdims\', \'reduction_axes\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "sparse_reduce_max_sparse" - argspec: "args=[\'sp_input\', \'axis\', \'keepdims\', \'reduction_axes\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "sparse_reduce_sum" - argspec: "args=[\'sp_input\', \'axis\', \'keepdims\', \'reduction_axes\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "sparse_reduce_sum_sparse" - argspec: "args=[\'sp_input\', \'axis\', \'keepdims\', \'reduction_axes\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + name: "sort" + argspec: "args=[\'values\', \'axis\', \'direction\', \'name\'], varargs=None, keywords=None, defaults=[\'-1\', \'ASCENDING\', \'None\'], " } member_method { - name: "sparse_split" - argspec: "args=[\'keyword_required\', \'sp_input\', \'num_split\', \'axis\', \'name\', \'split_dim\'], varargs=None, keywords=None, defaults=[\'KeywordRequired()\', \'None\', \'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "sparse_to_dense" - argspec: "args=[\'sparse_indices\', \'output_shape\', \'sparse_values\', \'default_value\', \'validate_indices\', \'name\'], varargs=None, keywords=None, defaults=[\'0\', \'True\', \'None\'], " + name: "space_to_batch_nd" + argspec: "args=[\'input\', \'block_shape\', \'paddings\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { name: "split" @@ -1158,7 +1014,7 @@ tf_module { } member_method { name: "squeeze" - argspec: "args=[\'input\', \'axis\', \'name\', \'squeeze_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + argspec: "args=[\'input\', \'axis\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { name: "stack" @@ -1192,6 +1048,18 @@ tf_module { name: "tanh" argspec: "args=[\'x\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "tensor_scatter_add" + argspec: "args=[\'tensor\', \'indices\', \'updates\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "tensor_scatter_sub" + argspec: "args=[\'tensor\', \'indices\', \'updates\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "tensor_scatter_update" + argspec: "args=[\'tensor\', \'indices\', \'updates\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } member_method { name: "tensordot" argspec: "args=[\'a\', \'b\', \'axes\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -1206,16 +1074,12 @@ tf_module { } member_method { name: "transpose" - argspec: "args=[\'a\', \'perm\', \'name\', \'conjugate\'], varargs=None, keywords=None, defaults=[\'None\', \'transpose\', \'False\'], " + argspec: "args=[\'a\', \'perm\', \'conjugate\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'transpose\'], " } member_method { name: "truediv" argspec: "args=[\'x\', \'y\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } - member_method { - name: "truncated_normal" - argspec: "args=[\'shape\', \'mean\', \'stddev\', \'dtype\', \'seed\', \'name\'], varargs=None, keywords=None, defaults=[\'0.0\', \'1.0\', \"\", \'None\', \'None\'], " - } member_method { name: "truncatediv" argspec: "args=[\'x\', \'y\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -1226,7 +1090,7 @@ tf_module { } member_method { name: "tuple" - argspec: "args=[\'tensors\', \'name\', \'control_inputs\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + argspec: "args=[\'tensors\', \'control_inputs\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { name: "unique" @@ -1244,10 +1108,6 @@ tf_module { name: "unstack" argspec: "args=[\'value\', \'num\', \'axis\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'0\', \'unstack\'], " } - member_method { - name: "variable_axis_size_partitioner" - argspec: "args=[\'max_shard_bytes\', \'axis\', \'bytes_per_string_element\', \'max_shards\'], varargs=None, keywords=None, defaults=[\'0\', \'16\', \'None\'], " - } member_method { name: "variable_creator_scope" argspec: "args=[\'variable_creator\'], varargs=None, keywords=None, defaults=None" @@ -1258,7 +1118,7 @@ tf_module { } member_method { name: "while_loop" - argspec: "args=[\'cond\', \'body\', \'loop_vars\', \'shape_invariants\', \'parallel_iterations\', \'back_prop\', \'swap_memory\', \'name\', \'maximum_iterations\', \'return_same_structure\'], varargs=None, keywords=None, defaults=[\'None\', \'10\', \'True\', \'False\', \'None\', \'None\', \'False\'], " + argspec: "args=[\'cond\', \'body\', \'loop_vars\', \'shape_invariants\', \'parallel_iterations\', \'back_prop\', \'swap_memory\', \'maximum_iterations\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'10\', \'True\', \'False\', \'None\', \'None\'], " } member_method { name: "zeros" @@ -1266,6 +1126,6 @@ tf_module { } member_method { name: "zeros_like" - argspec: "args=[\'tensor\', \'dtype\', \'name\', \'optimize\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'True\'], " + argspec: "args=[\'input\', \'dtype\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } } diff --git a/tensorflow/tools/api/golden/v2/tensorflow.profiler.-advice-proto.-checker.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.profiler.-advice-proto.-checker.pbtxt deleted file mode 100644 index e09c44cc9c..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.profiler.-advice-proto.-checker.pbtxt +++ /dev/null @@ -1,12 +0,0 @@ -path: "tensorflow.profiler.AdviceProto.Checker" -tf_proto { - descriptor { - name: "Checker" - field { - name: "reports" - number: 2 - label: LABEL_REPEATED - type: TYPE_STRING - } - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.profiler.-advice-proto.-checkers-entry.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.profiler.-advice-proto.-checkers-entry.pbtxt deleted file mode 100644 index 8746243549..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.profiler.-advice-proto.-checkers-entry.pbtxt +++ /dev/null @@ -1,22 +0,0 @@ -path: "tensorflow.profiler.AdviceProto.CheckersEntry" -tf_proto { - descriptor { - name: "CheckersEntry" - field { - name: "key" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - field { - name: "value" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.tfprof.AdviceProto.Checker" - } - options { - map_entry: true - } - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.profiler.-advice-proto.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.profiler.-advice-proto.pbtxt deleted file mode 100644 index a8a8858ccd..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.profiler.-advice-proto.pbtxt +++ /dev/null @@ -1,41 +0,0 @@ -path: "tensorflow.profiler.AdviceProto" -tf_proto { - descriptor { - name: "AdviceProto" - field { - name: "checkers" - number: 1 - label: LABEL_REPEATED - type: TYPE_MESSAGE - type_name: ".tensorflow.tfprof.AdviceProto.CheckersEntry" - } - nested_type { - name: "CheckersEntry" - field { - name: "key" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - field { - name: "value" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.tfprof.AdviceProto.Checker" - } - options { - map_entry: true - } - } - nested_type { - name: "Checker" - field { - name: "reports" - number: 2 - label: LABEL_REPEATED - type: TYPE_STRING - } - } - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.profiler.-graph-node-proto.-input-shapes-entry.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.profiler.-graph-node-proto.-input-shapes-entry.pbtxt deleted file mode 100644 index afec73f537..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.profiler.-graph-node-proto.-input-shapes-entry.pbtxt +++ /dev/null @@ -1,22 +0,0 @@ -path: "tensorflow.profiler.GraphNodeProto.InputShapesEntry" -tf_proto { - descriptor { - name: "InputShapesEntry" - field { - name: "key" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_INT32 - } - field { - name: "value" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.TensorShapeProto" - } - options { - map_entry: true - } - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.profiler.-graph-node-proto.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.profiler.-graph-node-proto.pbtxt deleted file mode 100644 index 3c83177005..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.profiler.-graph-node-proto.pbtxt +++ /dev/null @@ -1,191 +0,0 @@ -path: "tensorflow.profiler.GraphNodeProto" -tf_proto { - descriptor { - name: "GraphNodeProto" - field { - name: "name" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - field { - name: "tensor_value" - number: 15 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.tfprof.TFProfTensorProto" - } - field { - name: "run_count" - number: 21 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "exec_micros" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "accelerator_exec_micros" - number: 17 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "cpu_exec_micros" - number: 18 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "requested_bytes" - number: 3 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "peak_bytes" - number: 24 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "residual_bytes" - number: 25 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "output_bytes" - number: 26 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "parameters" - number: 4 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "float_ops" - number: 13 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "devices" - number: 10 - label: LABEL_REPEATED - type: TYPE_STRING - } - field { - name: "total_definition_count" - number: 23 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "total_run_count" - number: 22 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "total_exec_micros" - number: 6 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "total_accelerator_exec_micros" - number: 19 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "total_cpu_exec_micros" - number: 20 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "total_requested_bytes" - number: 7 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "total_peak_bytes" - number: 27 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "total_residual_bytes" - number: 28 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "total_output_bytes" - number: 29 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "total_parameters" - number: 8 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "total_float_ops" - number: 14 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "shapes" - number: 11 - label: LABEL_REPEATED - type: TYPE_MESSAGE - type_name: ".tensorflow.TensorShapeProto" - } - field { - name: "input_shapes" - number: 16 - label: LABEL_REPEATED - type: TYPE_MESSAGE - type_name: ".tensorflow.tfprof.GraphNodeProto.InputShapesEntry" - } - field { - name: "children" - number: 12 - label: LABEL_REPEATED - type: TYPE_MESSAGE - type_name: ".tensorflow.tfprof.GraphNodeProto" - } - nested_type { - name: "InputShapesEntry" - field { - name: "key" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_INT32 - } - field { - name: "value" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.TensorShapeProto" - } - options { - map_entry: true - } - } - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.profiler.-multi-graph-node-proto.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.profiler.-multi-graph-node-proto.pbtxt deleted file mode 100644 index 2b08a05437..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.profiler.-multi-graph-node-proto.pbtxt +++ /dev/null @@ -1,134 +0,0 @@ -path: "tensorflow.profiler.MultiGraphNodeProto" -tf_proto { - descriptor { - name: "MultiGraphNodeProto" - field { - name: "name" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - field { - name: "exec_micros" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "accelerator_exec_micros" - number: 12 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "cpu_exec_micros" - number: 13 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "requested_bytes" - number: 3 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "peak_bytes" - number: 16 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "residual_bytes" - number: 17 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "output_bytes" - number: 18 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "parameters" - number: 4 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "float_ops" - number: 5 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "total_exec_micros" - number: 6 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "total_accelerator_exec_micros" - number: 14 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "total_cpu_exec_micros" - number: 15 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "total_requested_bytes" - number: 7 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "total_peak_bytes" - number: 19 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "total_residual_bytes" - number: 20 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "total_output_bytes" - number: 21 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "total_parameters" - number: 8 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "total_float_ops" - number: 9 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "graph_nodes" - number: 10 - label: LABEL_REPEATED - type: TYPE_MESSAGE - type_name: ".tensorflow.tfprof.GraphNodeProto" - } - field { - name: "children" - number: 11 - label: LABEL_REPEATED - type: TYPE_MESSAGE - type_name: ".tensorflow.tfprof.MultiGraphNodeProto" - } - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.profiler.-op-log-proto.-id-to-string-entry.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.profiler.-op-log-proto.-id-to-string-entry.pbtxt deleted file mode 100644 index b3adc50c7e..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.profiler.-op-log-proto.-id-to-string-entry.pbtxt +++ /dev/null @@ -1,21 +0,0 @@ -path: "tensorflow.profiler.OpLogProto.IdToStringEntry" -tf_proto { - descriptor { - name: "IdToStringEntry" - field { - name: "key" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "value" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - options { - map_entry: true - } - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.profiler.-op-log-proto.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.profiler.-op-log-proto.pbtxt deleted file mode 100644 index 7510c566ba..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.profiler.-op-log-proto.pbtxt +++ /dev/null @@ -1,38 +0,0 @@ -path: "tensorflow.profiler.OpLogProto" -tf_proto { - descriptor { - name: "OpLogProto" - field { - name: "log_entries" - number: 1 - label: LABEL_REPEATED - type: TYPE_MESSAGE - type_name: ".tensorflow.tfprof.OpLogEntry" - } - field { - name: "id_to_string" - number: 2 - label: LABEL_REPEATED - type: TYPE_MESSAGE - type_name: ".tensorflow.tfprof.OpLogProto.IdToStringEntry" - } - nested_type { - name: "IdToStringEntry" - field { - name: "key" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "value" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - options { - map_entry: true - } - } - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.profiler.-profile-option-builder.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.profiler.-profile-option-builder.pbtxt deleted file mode 100644 index 19ff38a390..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.profiler.-profile-option-builder.pbtxt +++ /dev/null @@ -1,93 +0,0 @@ -path: "tensorflow.profiler.ProfileOptionBuilder" -tf_class { - is_instance: "" - is_instance: "" - member_method { - name: "__init__" - argspec: "args=[\'self\', \'options\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "account_displayed_op_only" - argspec: "args=[\'self\', \'is_true\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "build" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "float_operation" - argspec: "args=[], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "order_by" - argspec: "args=[\'self\', \'attribute\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "select" - argspec: "args=[\'self\', \'attributes\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "time_and_memory" - argspec: "args=[\'min_micros\', \'min_bytes\', \'min_accelerator_micros\', \'min_cpu_micros\', \'min_peak_bytes\', \'min_residual_bytes\', \'min_output_bytes\'], varargs=None, keywords=None, defaults=[\'1\', \'1\', \'0\', \'0\', \'0\', \'0\', \'0\'], " - } - member_method { - name: "trainable_variables_parameter" - argspec: "args=[], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "with_accounted_types" - argspec: "args=[\'self\', \'account_type_regexes\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "with_empty_output" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "with_file_output" - argspec: "args=[\'self\', \'outfile\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "with_max_depth" - argspec: "args=[\'self\', \'max_depth\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "with_min_execution_time" - argspec: "args=[\'self\', \'min_micros\', \'min_accelerator_micros\', \'min_cpu_micros\'], varargs=None, keywords=None, defaults=[\'0\', \'0\', \'0\'], " - } - member_method { - name: "with_min_float_operations" - argspec: "args=[\'self\', \'min_float_ops\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "with_min_memory" - argspec: "args=[\'self\', \'min_bytes\', \'min_peak_bytes\', \'min_residual_bytes\', \'min_output_bytes\'], varargs=None, keywords=None, defaults=[\'0\', \'0\', \'0\', \'0\'], " - } - member_method { - name: "with_min_occurrence" - argspec: "args=[\'self\', \'min_occurrence\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "with_min_parameters" - argspec: "args=[\'self\', \'min_params\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "with_node_names" - argspec: "args=[\'self\', \'start_name_regexes\', \'show_name_regexes\', \'hide_name_regexes\', \'trim_name_regexes\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "with_pprof_output" - argspec: "args=[\'self\', \'pprof_file\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "with_stdout_output" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "with_step" - argspec: "args=[\'self\', \'step\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "with_timeline_output" - argspec: "args=[\'self\', \'timeline_file\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.profiler.-profiler.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.profiler.-profiler.pbtxt deleted file mode 100644 index acb61dae9f..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.profiler.-profiler.pbtxt +++ /dev/null @@ -1,37 +0,0 @@ -path: "tensorflow.profiler.Profiler" -tf_class { - is_instance: "" - is_instance: "" - member_method { - name: "__init__" - argspec: "args=[\'self\', \'graph\', \'op_log\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " - } - member_method { - name: "add_step" - argspec: "args=[\'self\', \'step\', \'run_meta\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "advise" - argspec: "args=[\'self\', \'options\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "profile_graph" - argspec: "args=[\'self\', \'options\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "profile_name_scope" - argspec: "args=[\'self\', \'options\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "profile_operations" - argspec: "args=[\'self\', \'options\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "profile_python" - argspec: "args=[\'self\', \'options\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "serialize_to_string" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.profiler.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.profiler.pbtxt deleted file mode 100644 index 7b4d3ac522..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.profiler.pbtxt +++ /dev/null @@ -1,39 +0,0 @@ -path: "tensorflow.profiler" -tf_module { - member { - name: "AdviceProto" - mtype: "" - } - member { - name: "GraphNodeProto" - mtype: "" - } - member { - name: "MultiGraphNodeProto" - mtype: "" - } - member { - name: "OpLogProto" - mtype: "" - } - member { - name: "ProfileOptionBuilder" - mtype: "" - } - member { - name: "Profiler" - mtype: "" - } - member_method { - name: "advise" - argspec: "args=[\'graph\', \'run_meta\', \'options\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'0\'], " - } - member_method { - name: "profile" - argspec: "args=[\'graph\', \'run_meta\', \'op_log\', \'cmd\', \'options\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'scope\', \'0\'], " - } - member_method { - name: "write_op_log" - argspec: "args=[\'graph\', \'log_dir\', \'op_log\', \'run_meta\', \'add_trace\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'True\'], " - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.quantization.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.quantization.pbtxt index 2948b7318e..632c2f8f83 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.quantization.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.quantization.pbtxt @@ -34,7 +34,7 @@ tf_module { } member_method { name: "quantize_and_dequantize" - argspec: "args=[\'input\', \'input_min\', \'input_max\', \'signed_input\', \'num_bits\', \'range_given\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'8\', \'False\', \'None\'], " + argspec: "args=[\'input\', \'input_min\', \'input_max\', \'signed_input\', \'num_bits\', \'range_given\', \'round_mode\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'8\', \'False\', \'HALF_TO_EVEN\', \'None\'], " } member_method { name: "quantized_concat" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.random.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.random.pbtxt index 160c09798d..de5cb6b717 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.random.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.random.pbtxt @@ -1,20 +1,24 @@ path: "tensorflow.random" tf_module { member_method { - name: "gamma" - argspec: "args=[\'shape\', \'alpha\', \'beta\', \'dtype\', \'seed\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \"\", \'None\', \'None\'], " + name: "all_candidate_sampler" + argspec: "args=[\'true_classes\', \'num_true\', \'num_sampled\', \'unique\', \'seed\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { - name: "get_seed" - argspec: "args=[\'op_seed\'], varargs=None, keywords=None, defaults=None" + name: "categorical" + argspec: "args=[\'logits\', \'num_samples\', \'dtype\', \'seed\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " } member_method { - name: "log_uniform_candidate_sampler" - argspec: "args=[\'true_classes\', \'num_true\', \'num_sampled\', \'unique\', \'range_max\', \'seed\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + name: "fixed_unigram_candidate_sampler" + argspec: "args=[\'true_classes\', \'num_true\', \'num_sampled\', \'unique\', \'range_max\', \'vocab_file\', \'distortion\', \'num_reserved_ids\', \'num_shards\', \'shard\', \'unigrams\', \'seed\', \'name\'], varargs=None, keywords=None, defaults=[\'\', \'1.0\', \'0\', \'1\', \'0\', \'()\', \'None\', \'None\'], " + } + member_method { + name: "gamma" + argspec: "args=[\'shape\', \'alpha\', \'beta\', \'dtype\', \'seed\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \"\", \'None\', \'None\'], " } member_method { - name: "multinomial" - argspec: "args=[\'logits\', \'num_samples\', \'seed\', \'name\', \'output_dtype\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " + name: "log_uniform_candidate_sampler" + argspec: "args=[\'true_classes\', \'num_true\', \'num_sampled\', \'unique\', \'range_max\', \'seed\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { name: "normal" @@ -22,10 +26,10 @@ tf_module { } member_method { name: "poisson" - argspec: "args=[\'lam\', \'shape\', \'dtype\', \'seed\', \'name\'], varargs=None, keywords=None, defaults=[\"\", \'None\', \'None\'], " + argspec: "args=[\'shape\', \'lam\', \'dtype\', \'seed\', \'name\'], varargs=None, keywords=None, defaults=[\"\", \'None\', \'None\'], " } member_method { - name: "set_random_seed" + name: "set_seed" argspec: "args=[\'seed\'], varargs=None, keywords=None, defaults=None" } member_method { @@ -33,8 +37,8 @@ tf_module { argspec: "args=[\'value\', \'seed\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { - name: "stateless_multinomial" - argspec: "args=[\'logits\', \'num_samples\', \'seed\', \'output_dtype\', \'name\'], varargs=None, keywords=None, defaults=[\"\", \'None\'], " + name: "stateless_categorical" + argspec: "args=[\'logits\', \'num_samples\', \'seed\', \'dtype\', \'name\'], varargs=None, keywords=None, defaults=[\"\", \'None\'], " } member_method { name: "stateless_normal" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.saved_model.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.saved_model.pbtxt index d57936a2f1..63bebb20bc 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.saved_model.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.saved_model.pbtxt @@ -32,14 +32,6 @@ tf_module { name: "GPU" mtype: "" } - member { - name: "LEGACY_INIT_OP_KEY" - mtype: "" - } - member { - name: "MAIN_OP_KEY" - mtype: "" - } member { name: "PREDICT_INPUTS" mtype: "" @@ -105,12 +97,12 @@ tf_module { argspec: "args=[\'examples\', \'classes\', \'scores\'], varargs=None, keywords=None, defaults=None" } member_method { - name: "is_valid_signature" - argspec: "args=[\'signature_def\'], varargs=None, keywords=None, defaults=None" + name: "contains_saved_model" + argspec: "args=[\'export_dir\'], varargs=None, keywords=None, defaults=None" } member_method { - name: "maybe_saved_model_directory" - argspec: "args=[\'export_dir\'], varargs=None, keywords=None, defaults=None" + name: "is_valid_signature" + argspec: "args=[\'signature_def\'], varargs=None, keywords=None, defaults=None" } member_method { name: "predict_signature_def" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.sets.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.sets.pbtxt index 8a196b1a55..900d08ff47 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.sets.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.sets.pbtxt @@ -1,19 +1,19 @@ path: "tensorflow.sets" tf_module { member_method { - name: "set_difference" + name: "difference" argspec: "args=[\'a\', \'b\', \'aminusb\', \'validate_indices\'], varargs=None, keywords=None, defaults=[\'True\', \'True\'], " } member_method { - name: "set_intersection" + name: "intersection" argspec: "args=[\'a\', \'b\', \'validate_indices\'], varargs=None, keywords=None, defaults=[\'True\'], " } member_method { - name: "set_size" + name: "size" argspec: "args=[\'a\', \'validate_indices\'], varargs=None, keywords=None, defaults=[\'True\'], " } member_method { - name: "set_union" + name: "union" argspec: "args=[\'a\', \'b\', \'validate_indices\'], varargs=None, keywords=None, defaults=[\'True\'], " } } diff --git a/tensorflow/tools/api/golden/v2/tensorflow.signal.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.signal.pbtxt index 2c50c41f18..ea717b4d71 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.signal.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.signal.pbtxt @@ -1,5 +1,9 @@ path: "tensorflow.signal" tf_module { + member_method { + name: "dct" + argspec: "args=[\'input\', \'type\', \'n\', \'axis\', \'norm\', \'name\'], varargs=None, keywords=None, defaults=[\'2\', \'None\', \'-1\', \'None\', \'None\'], " + } member_method { name: "fft" argspec: "args=[\'input\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -24,6 +28,10 @@ tf_module { name: "hann_window" argspec: "args=[\'window_length\', \'periodic\', \'dtype\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \"\", \'None\'], " } + member_method { + name: "idct" + argspec: "args=[\'input\', \'type\', \'n\', \'axis\', \'norm\', \'name\'], varargs=None, keywords=None, defaults=[\'2\', \'None\', \'-1\', \'None\', \'None\'], " + } member_method { name: "ifft" argspec: "args=[\'input\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -44,6 +52,18 @@ tf_module { name: "inverse_stft_window_fn" argspec: "args=[\'frame_step\', \'forward_window_fn\', \'name\'], varargs=None, keywords=None, defaults=[\'\', \'None\'], " } + member_method { + name: "irfft" + argspec: "args=[\'input_tensor\', \'fft_length\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "irfft2d" + argspec: "args=[\'input_tensor\', \'fft_length\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "irfft3d" + argspec: "args=[\'input_tensor\', \'fft_length\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "linear_to_mel_weight_matrix" argspec: "args=[\'num_mel_bins\', \'num_spectrogram_bins\', \'sample_rate\', \'lower_edge_hertz\', \'upper_edge_hertz\', \'dtype\', \'name\'], varargs=None, keywords=None, defaults=[\'20\', \'129\', \'8000\', \'125.0\', \'3800.0\', \"\", \'None\'], " @@ -56,6 +76,18 @@ tf_module { name: "overlap_and_add" argspec: "args=[\'signal\', \'frame_step\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "rfft" + argspec: "args=[\'input_tensor\', \'fft_length\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "rfft2d" + argspec: "args=[\'input_tensor\', \'fft_length\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "rfft3d" + argspec: "args=[\'input_tensor\', \'fft_length\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } member_method { name: "stft" argspec: "args=[\'signals\', \'frame_length\', \'frame_step\', \'fft_length\', \'window_fn\', \'pad_end\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'\', \'False\', \'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.sparse.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.sparse.pbtxt index 9c9c4d838e..9808200d72 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.sparse.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.sparse.pbtxt @@ -10,11 +10,11 @@ tf_module { } member_method { name: "add" - argspec: "args=[\'a\', \'b\', \'thresh\'], varargs=None, keywords=None, defaults=[\'0\'], " + argspec: "args=[\'a\', \'b\', \'threshold\'], varargs=None, keywords=None, defaults=[\'0\'], " } member_method { name: "concat" - argspec: "args=[\'axis\', \'sp_inputs\', \'name\', \'expand_nonconcat_dim\', \'concat_dim\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'None\'], " + argspec: "args=[\'axis\', \'sp_inputs\', \'expand_nonconcat_dim\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'None\'], " } member_method { name: "cross" @@ -40,37 +40,21 @@ tf_module { name: "mask" argspec: "args=[\'a\', \'mask_indices\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } - member_method { - name: "matmul" - argspec: "args=[\'sp_a\', \'b\', \'adjoint_a\', \'adjoint_b\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'False\', \'None\'], " - } member_method { name: "maximum" argspec: "args=[\'sp_a\', \'sp_b\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } - member_method { - name: "merge" - argspec: "args=[\'sp_ids\', \'sp_values\', \'vocab_size\', \'name\', \'already_sorted\'], varargs=None, keywords=None, defaults=[\'None\', \'False\'], " - } member_method { name: "minimum" argspec: "args=[\'sp_a\', \'sp_b\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { name: "reduce_max" - argspec: "args=[\'sp_input\', \'axis\', \'keepdims\', \'reduction_axes\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "reduce_max_sparse" - argspec: "args=[\'sp_input\', \'axis\', \'keepdims\', \'reduction_axes\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'sp_input\', \'axis\', \'keepdims\', \'output_is_sparse\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'False\', \'None\'], " } member_method { name: "reduce_sum" - argspec: "args=[\'sp_input\', \'axis\', \'keepdims\', \'reduction_axes\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "reduce_sum_sparse" - argspec: "args=[\'sp_input\', \'axis\', \'keepdims\', \'reduction_axes\', \'keep_dims\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'sp_input\', \'axis\', \'keepdims\', \'output_is_sparse\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'False\', \'None\'], " } member_method { name: "reorder" @@ -90,15 +74,15 @@ tf_module { } member_method { name: "segment_mean" - argspec: "args=[\'data\', \'indices\', \'segment_ids\', \'name\', \'num_segments\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + argspec: "args=[\'data\', \'indices\', \'segment_ids\', \'num_segments\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { name: "segment_sqrt_n" - argspec: "args=[\'data\', \'indices\', \'segment_ids\', \'name\', \'num_segments\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + argspec: "args=[\'data\', \'indices\', \'segment_ids\', \'num_segments\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { name: "segment_sum" - argspec: "args=[\'data\', \'indices\', \'segment_ids\', \'name\', \'num_segments\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + argspec: "args=[\'data\', \'indices\', \'segment_ids\', \'num_segments\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { name: "slice" @@ -108,9 +92,13 @@ tf_module { name: "softmax" argspec: "args=[\'sp_input\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "sparse_dense_matmul" + argspec: "args=[\'sp_a\', \'b\', \'adjoint_a\', \'adjoint_b\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'False\', \'None\'], " + } member_method { name: "split" - argspec: "args=[\'keyword_required\', \'sp_input\', \'num_split\', \'axis\', \'name\', \'split_dim\'], varargs=None, keywords=None, defaults=[\'KeywordRequired()\', \'None\', \'None\', \'None\', \'None\', \'None\'], " + argspec: "args=[\'sp_input\', \'num_split\', \'axis\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } member_method { name: "to_dense" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.spectral.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.spectral.pbtxt deleted file mode 100644 index b0f0783e30..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.spectral.pbtxt +++ /dev/null @@ -1,35 +0,0 @@ -path: "tensorflow.spectral" -tf_module { - member_method { - name: "dct" - argspec: "args=[\'input\', \'type\', \'n\', \'axis\', \'norm\', \'name\'], varargs=None, keywords=None, defaults=[\'2\', \'None\', \'-1\', \'None\', \'None\'], " - } - member_method { - name: "idct" - argspec: "args=[\'input\', \'type\', \'n\', \'axis\', \'norm\', \'name\'], varargs=None, keywords=None, defaults=[\'2\', \'None\', \'-1\', \'None\', \'None\'], " - } - member_method { - name: "irfft" - argspec: "args=[\'input_tensor\', \'fft_length\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " - } - member_method { - name: "irfft2d" - argspec: "args=[\'input_tensor\', \'fft_length\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " - } - member_method { - name: "irfft3d" - argspec: "args=[\'input_tensor\', \'fft_length\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " - } - member_method { - name: "rfft" - argspec: "args=[\'input_tensor\', \'fft_length\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " - } - member_method { - name: "rfft2d" - argspec: "args=[\'input_tensor\', \'fft_length\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " - } - member_method { - name: "rfft3d" - argspec: "args=[\'input_tensor\', \'fft_length\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.strings.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.strings.pbtxt index 03144cbe70..f6e32ed08c 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.strings.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.strings.pbtxt @@ -10,11 +10,11 @@ tf_module { } member_method { name: "length" - argspec: "args=[\'input\', \'name\', \'unit\'], varargs=None, keywords=None, defaults=[\'None\', \'BYTE\'], " + argspec: "args=[\'input\', \'unit\', \'name\'], varargs=None, keywords=None, defaults=[\'BYTE\', \'None\'], " } member_method { name: "reduce_join" - argspec: "args=[\'inputs\', \'axis\', \'keep_dims\', \'separator\', \'name\', \'reduction_indices\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'\', \'None\', \'None\'], " + argspec: "args=[\'inputs\', \'axis\', \'keepdims\', \'separator\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'False\', \'\', \'None\'], " } member_method { name: "regex_full_match" @@ -34,11 +34,11 @@ tf_module { } member_method { name: "substr" - argspec: "args=[\'input\', \'pos\', \'len\', \'name\', \'unit\'], varargs=None, keywords=None, defaults=[\'None\', \'BYTE\'], " + argspec: "args=[\'input\', \'pos\', \'len\', \'unit\', \'name\'], varargs=None, keywords=None, defaults=[\'BYTE\', \'None\'], " } member_method { name: "to_hash_bucket" - argspec: "args=[\'string_tensor\', \'num_buckets\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + argspec: "args=[\'input\', \'num_buckets\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { name: "to_hash_bucket_fast" @@ -50,7 +50,11 @@ tf_module { } member_method { name: "to_number" - argspec: "args=[\'string_tensor\', \'out_type\', \'name\'], varargs=None, keywords=None, defaults=[\"\", \'None\'], " + argspec: "args=[\'input\', \'out_type\', \'name\'], varargs=None, keywords=None, defaults=[\"\", \'None\'], " + } + member_method { + name: "unicode_encode" + argspec: "args=[\'input\', \'output_encoding\', \'errors\', \'replacement_char\', \'name\'], varargs=None, keywords=None, defaults=[\'replace\', \'65533\', \'None\'], " } member_method { name: "unicode_script" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.summary.-summary-writer.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.summary.-summary-writer.pbtxt new file mode 100644 index 0000000000..6715c14e16 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.summary.-summary-writer.pbtxt @@ -0,0 +1,29 @@ +path: "tensorflow.summary.SummaryWriter" +tf_class { + is_instance: "" + is_instance: "" + member_method { + name: "__init__" + argspec: "args=[\'self\', \'resource\', \'init_op_fn\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "as_default" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "close" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "flush" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "init" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_as_default" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.summary.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.summary.pbtxt index 7ed9cd77a0..42a74a65fb 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.summary.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.summary.pbtxt @@ -24,44 +24,24 @@ tf_module { name: "SummaryDescription" mtype: "" } + member { + name: "SummaryWriter" + mtype: "" + } member { name: "TaggedRunMetadata" mtype: "" } member_method { - name: "audio" - argspec: "args=[\'name\', \'tensor\', \'sample_rate\', \'max_outputs\', \'collections\', \'family\'], varargs=None, keywords=None, defaults=[\'3\', \'None\', \'None\'], " - } - member_method { - name: "get_summary_description" - argspec: "args=[\'node_def\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "histogram" - argspec: "args=[\'name\', \'values\', \'collections\', \'family\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " - } - member_method { - name: "image" - argspec: "args=[\'name\', \'tensor\', \'max_outputs\', \'collections\', \'family\'], varargs=None, keywords=None, defaults=[\'3\', \'None\', \'None\'], " - } - member_method { - name: "merge" - argspec: "args=[\'inputs\', \'collections\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " - } - member_method { - name: "merge_all" - argspec: "args=[\'key\', \'scope\', \'name\'], varargs=None, keywords=None, defaults=[\'summaries\', \'None\', \'None\'], " - } - member_method { - name: "scalar" - argspec: "args=[\'name\', \'tensor\', \'collections\', \'family\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + name: "create_file_writer" + argspec: "args=[\'logdir\', \'max_queue\', \'flush_millis\', \'filename_suffix\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " } member_method { - name: "tensor_summary" - argspec: "args=[\'name\', \'tensor\', \'summary_description\', \'collections\', \'summary_metadata\', \'family\', \'display_name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " + name: "flush" + argspec: "args=[\'writer\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " } member_method { - name: "text" - argspec: "args=[\'name\', \'tensor\', \'collections\'], varargs=None, keywords=None, defaults=[\'None\'], " + name: "import_event" + argspec: "args=[\'tensor\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } } diff --git a/tensorflow/tools/api/golden/v2/tensorflow.test.-benchmark.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.test.-benchmark.pbtxt index df528e26b6..6fc489c860 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.test.-benchmark.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.test.-benchmark.pbtxt @@ -6,6 +6,10 @@ tf_class { member_method { name: "__init__" } + member_method { + name: "evaluate" + argspec: "args=[\'self\', \'tensors\'], varargs=None, keywords=None, defaults=None" + } member_method { name: "is_abstract" argspec: "args=[\'cls\'], varargs=None, keywords=None, defaults=None" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.test.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.test.pbtxt index af3f06d8de..72ce733044 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.test.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.test.pbtxt @@ -12,26 +12,14 @@ tf_module { name: "TestCase" mtype: "" } - member { - name: "mock" - mtype: "" - } member_method { name: "assert_equal_graph_def" - argspec: "args=[\'actual\', \'expected\', \'checkpoint_v2\'], varargs=None, keywords=None, defaults=[\'False\'], " + argspec: "args=[\'actual\', \'expected\'], varargs=None, keywords=None, defaults=None" } member_method { name: "benchmark_config" argspec: "args=[], varargs=None, keywords=None, defaults=None" } - member_method { - name: "compute_gradient" - argspec: "args=[\'x\', \'x_shape\', \'y\', \'y_shape\', \'x_init_value\', \'delta\', \'init_targets\', \'extra_feed_dict\'], varargs=None, keywords=None, defaults=[\'None\', \'0.001\', \'None\', \'None\'], " - } - member_method { - name: "compute_gradient_error" - argspec: "args=[\'x\', \'x_shape\', \'y\', \'y_shape\', \'x_init_value\', \'delta\', \'extra_feed_dict\'], varargs=None, keywords=None, defaults=[\'None\', \'0.001\', \'None\'], " - } member_method { name: "create_local_cluster" argspec: "args=[\'num_workers\', \'num_ps\', \'protocol\', \'worker_config\', \'ps_config\'], varargs=None, keywords=None, defaults=[\'grpc\', \'None\', \'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-adadelta-optimizer.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-adadelta-optimizer.pbtxt deleted file mode 100644 index 1f1d8b6f9e..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-adadelta-optimizer.pbtxt +++ /dev/null @@ -1,51 +0,0 @@ -path: "tensorflow.train.AdadeltaOptimizer" -tf_class { - is_instance: "" - is_instance: "" - is_instance: "" - is_instance: "" - member { - name: "GATE_GRAPH" - mtype: "" - } - member { - name: "GATE_NONE" - mtype: "" - } - member { - name: "GATE_OP" - mtype: "" - } - member_method { - name: "__init__" - argspec: "args=[\'self\', \'learning_rate\', \'rho\', \'epsilon\', \'use_locking\', \'name\'], varargs=None, keywords=None, defaults=[\'0.001\', \'0.95\', \'1e-08\', \'False\', \'Adadelta\'], " - } - member_method { - name: "apply_gradients" - argspec: "args=[\'self\', \'grads_and_vars\', \'global_step\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " - } - member_method { - name: "compute_gradients" - argspec: "args=[\'self\', \'loss\', \'var_list\', \'gate_gradients\', \'aggregation_method\', \'colocate_gradients_with_ops\', \'grad_loss\'], varargs=None, keywords=None, defaults=[\'None\', \'1\', \'None\', \'False\', \'None\'], " - } - member_method { - name: "get_name" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "get_slot" - argspec: "args=[\'self\', \'var\', \'name\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "get_slot_names" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "minimize" - argspec: "args=[\'self\', \'loss\', \'global_step\', \'var_list\', \'gate_gradients\', \'aggregation_method\', \'colocate_gradients_with_ops\', \'name\', \'grad_loss\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'1\', \'None\', \'False\', \'None\', \'None\'], " - } - member_method { - name: "variables" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-adagrad-d-a-optimizer.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-adagrad-d-a-optimizer.pbtxt deleted file mode 100644 index a7c05d4849..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-adagrad-d-a-optimizer.pbtxt +++ /dev/null @@ -1,51 +0,0 @@ -path: "tensorflow.train.AdagradDAOptimizer" -tf_class { - is_instance: "" - is_instance: "" - is_instance: "" - is_instance: "" - member { - name: "GATE_GRAPH" - mtype: "" - } - member { - name: "GATE_NONE" - mtype: "" - } - member { - name: "GATE_OP" - mtype: "" - } - member_method { - name: "__init__" - argspec: "args=[\'self\', \'learning_rate\', \'global_step\', \'initial_gradient_squared_accumulator_value\', \'l1_regularization_strength\', \'l2_regularization_strength\', \'use_locking\', \'name\'], varargs=None, keywords=None, defaults=[\'0.1\', \'0.0\', \'0.0\', \'False\', \'AdagradDA\'], " - } - member_method { - name: "apply_gradients" - argspec: "args=[\'self\', \'grads_and_vars\', \'global_step\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " - } - member_method { - name: "compute_gradients" - argspec: "args=[\'self\', \'loss\', \'var_list\', \'gate_gradients\', \'aggregation_method\', \'colocate_gradients_with_ops\', \'grad_loss\'], varargs=None, keywords=None, defaults=[\'None\', \'1\', \'None\', \'False\', \'None\'], " - } - member_method { - name: "get_name" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "get_slot" - argspec: "args=[\'self\', \'var\', \'name\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "get_slot_names" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "minimize" - argspec: "args=[\'self\', \'loss\', \'global_step\', \'var_list\', \'gate_gradients\', \'aggregation_method\', \'colocate_gradients_with_ops\', \'name\', \'grad_loss\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'1\', \'None\', \'False\', \'None\', \'None\'], " - } - member_method { - name: "variables" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-adagrad-optimizer.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-adagrad-optimizer.pbtxt deleted file mode 100644 index bc8b92389c..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-adagrad-optimizer.pbtxt +++ /dev/null @@ -1,51 +0,0 @@ -path: "tensorflow.train.AdagradOptimizer" -tf_class { - is_instance: "" - is_instance: "" - is_instance: "" - is_instance: "" - member { - name: "GATE_GRAPH" - mtype: "" - } - member { - name: "GATE_NONE" - mtype: "" - } - member { - name: "GATE_OP" - mtype: "" - } - member_method { - name: "__init__" - argspec: "args=[\'self\', \'learning_rate\', \'initial_accumulator_value\', \'use_locking\', \'name\'], varargs=None, keywords=None, defaults=[\'0.1\', \'False\', \'Adagrad\'], " - } - member_method { - name: "apply_gradients" - argspec: "args=[\'self\', \'grads_and_vars\', \'global_step\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " - } - member_method { - name: "compute_gradients" - argspec: "args=[\'self\', \'loss\', \'var_list\', \'gate_gradients\', \'aggregation_method\', \'colocate_gradients_with_ops\', \'grad_loss\'], varargs=None, keywords=None, defaults=[\'None\', \'1\', \'None\', \'False\', \'None\'], " - } - member_method { - name: "get_name" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "get_slot" - argspec: "args=[\'self\', \'var\', \'name\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "get_slot_names" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "minimize" - argspec: "args=[\'self\', \'loss\', \'global_step\', \'var_list\', \'gate_gradients\', \'aggregation_method\', \'colocate_gradients_with_ops\', \'name\', \'grad_loss\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'1\', \'None\', \'False\', \'None\', \'None\'], " - } - member_method { - name: "variables" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-adam-optimizer.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-adam-optimizer.pbtxt deleted file mode 100644 index 5d17be9378..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-adam-optimizer.pbtxt +++ /dev/null @@ -1,51 +0,0 @@ -path: "tensorflow.train.AdamOptimizer" -tf_class { - is_instance: "" - is_instance: "" - is_instance: "" - is_instance: "" - member { - name: "GATE_GRAPH" - mtype: "" - } - member { - name: "GATE_NONE" - mtype: "" - } - member { - name: "GATE_OP" - mtype: "" - } - member_method { - name: "__init__" - argspec: "args=[\'self\', \'learning_rate\', \'beta1\', \'beta2\', \'epsilon\', \'use_locking\', \'name\'], varargs=None, keywords=None, defaults=[\'0.001\', \'0.9\', \'0.999\', \'1e-08\', \'False\', \'Adam\'], " - } - member_method { - name: "apply_gradients" - argspec: "args=[\'self\', \'grads_and_vars\', \'global_step\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " - } - member_method { - name: "compute_gradients" - argspec: "args=[\'self\', \'loss\', \'var_list\', \'gate_gradients\', \'aggregation_method\', \'colocate_gradients_with_ops\', \'grad_loss\'], varargs=None, keywords=None, defaults=[\'None\', \'1\', \'None\', \'False\', \'None\'], " - } - member_method { - name: "get_name" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "get_slot" - argspec: "args=[\'self\', \'var\', \'name\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "get_slot_names" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "minimize" - argspec: "args=[\'self\', \'loss\', \'global_step\', \'var_list\', \'gate_gradients\', \'aggregation_method\', \'colocate_gradients_with_ops\', \'name\', \'grad_loss\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'1\', \'None\', \'False\', \'None\', \'None\'], " - } - member_method { - name: "variables" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-checkpoint-saver-listener.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-checkpoint-saver-listener.pbtxt deleted file mode 100644 index 9d3688e565..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-checkpoint-saver-listener.pbtxt +++ /dev/null @@ -1,24 +0,0 @@ -path: "tensorflow.train.CheckpointSaverListener" -tf_class { - is_instance: "" - is_instance: "" - member_method { - name: "__init__" - } - member_method { - name: "after_save" - argspec: "args=[\'self\', \'session\', \'global_step_value\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "before_save" - argspec: "args=[\'self\', \'session\', \'global_step_value\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "begin" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "end" - argspec: "args=[\'self\', \'session\', \'global_step_value\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-chief-session-creator.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-chief-session-creator.pbtxt deleted file mode 100644 index abbe273be3..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-chief-session-creator.pbtxt +++ /dev/null @@ -1,14 +0,0 @@ -path: "tensorflow.train.ChiefSessionCreator" -tf_class { - is_instance: "" - is_instance: "" - is_instance: "" - member_method { - name: "__init__" - argspec: "args=[\'self\', \'scaffold\', \'master\', \'config\', \'checkpoint_dir\', \'checkpoint_filename_with_path\'], varargs=None, keywords=None, defaults=[\'None\', \'\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "create_session" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-ftrl-optimizer.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-ftrl-optimizer.pbtxt deleted file mode 100644 index d265fdeb01..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-ftrl-optimizer.pbtxt +++ /dev/null @@ -1,51 +0,0 @@ -path: "tensorflow.train.FtrlOptimizer" -tf_class { - is_instance: "" - is_instance: "" - is_instance: "" - is_instance: "" - member { - name: "GATE_GRAPH" - mtype: "" - } - member { - name: "GATE_NONE" - mtype: "" - } - member { - name: "GATE_OP" - mtype: "" - } - member_method { - name: "__init__" - argspec: "args=[\'self\', \'learning_rate\', \'learning_rate_power\', \'initial_accumulator_value\', \'l1_regularization_strength\', \'l2_regularization_strength\', \'use_locking\', \'name\', \'accum_name\', \'linear_name\', \'l2_shrinkage_regularization_strength\'], varargs=None, keywords=None, defaults=[\'-0.5\', \'0.1\', \'0.0\', \'0.0\', \'False\', \'Ftrl\', \'None\', \'None\', \'0.0\'], " - } - member_method { - name: "apply_gradients" - argspec: "args=[\'self\', \'grads_and_vars\', \'global_step\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " - } - member_method { - name: "compute_gradients" - argspec: "args=[\'self\', \'loss\', \'var_list\', \'gate_gradients\', \'aggregation_method\', \'colocate_gradients_with_ops\', \'grad_loss\'], varargs=None, keywords=None, defaults=[\'None\', \'1\', \'None\', \'False\', \'None\'], " - } - member_method { - name: "get_name" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "get_slot" - argspec: "args=[\'self\', \'var\', \'name\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "get_slot_names" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "minimize" - argspec: "args=[\'self\', \'loss\', \'global_step\', \'var_list\', \'gate_gradients\', \'aggregation_method\', \'colocate_gradients_with_ops\', \'name\', \'grad_loss\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'1\', \'None\', \'False\', \'None\', \'None\'], " - } - member_method { - name: "variables" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-gradient-descent-optimizer.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-gradient-descent-optimizer.pbtxt deleted file mode 100644 index c673e29cd4..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-gradient-descent-optimizer.pbtxt +++ /dev/null @@ -1,51 +0,0 @@ -path: "tensorflow.train.GradientDescentOptimizer" -tf_class { - is_instance: "" - is_instance: "" - is_instance: "" - is_instance: "" - member { - name: "GATE_GRAPH" - mtype: "" - } - member { - name: "GATE_NONE" - mtype: "" - } - member { - name: "GATE_OP" - mtype: "" - } - member_method { - name: "__init__" - argspec: "args=[\'self\', \'learning_rate\', \'use_locking\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'GradientDescent\'], " - } - member_method { - name: "apply_gradients" - argspec: "args=[\'self\', \'grads_and_vars\', \'global_step\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " - } - member_method { - name: "compute_gradients" - argspec: "args=[\'self\', \'loss\', \'var_list\', \'gate_gradients\', \'aggregation_method\', \'colocate_gradients_with_ops\', \'grad_loss\'], varargs=None, keywords=None, defaults=[\'None\', \'1\', \'None\', \'False\', \'None\'], " - } - member_method { - name: "get_name" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "get_slot" - argspec: "args=[\'self\', \'var\', \'name\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "get_slot_names" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "minimize" - argspec: "args=[\'self\', \'loss\', \'global_step\', \'var_list\', \'gate_gradients\', \'aggregation_method\', \'colocate_gradients_with_ops\', \'name\', \'grad_loss\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'1\', \'None\', \'False\', \'None\', \'None\'], " - } - member_method { - name: "variables" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-looper-thread.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-looper-thread.pbtxt deleted file mode 100644 index c61859004e..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-looper-thread.pbtxt +++ /dev/null @@ -1,73 +0,0 @@ -path: "tensorflow.train.LooperThread" -tf_class { - is_instance: "" - is_instance: "" - member { - name: "daemon" - mtype: "" - } - member { - name: "ident" - mtype: "" - } - member { - name: "name" - mtype: "" - } - member_method { - name: "__init__" - argspec: "args=[\'self\', \'coord\', \'timer_interval_secs\', \'target\', \'args\', \'kwargs\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " - } - member_method { - name: "getName" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "isAlive" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "isDaemon" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "is_alive" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "join" - argspec: "args=[\'self\', \'timeout\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "loop" - argspec: "args=[\'coord\', \'timer_interval_secs\', \'target\', \'args\', \'kwargs\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " - } - member_method { - name: "run" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "run_loop" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "setDaemon" - argspec: "args=[\'self\', \'daemonic\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "setName" - argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "start" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "start_loop" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "stop_loop" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-momentum-optimizer.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-momentum-optimizer.pbtxt deleted file mode 100644 index 8199f63b9b..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-momentum-optimizer.pbtxt +++ /dev/null @@ -1,51 +0,0 @@ -path: "tensorflow.train.MomentumOptimizer" -tf_class { - is_instance: "" - is_instance: "" - is_instance: "" - is_instance: "" - member { - name: "GATE_GRAPH" - mtype: "" - } - member { - name: "GATE_NONE" - mtype: "" - } - member { - name: "GATE_OP" - mtype: "" - } - member_method { - name: "__init__" - argspec: "args=[\'self\', \'learning_rate\', \'momentum\', \'use_locking\', \'name\', \'use_nesterov\'], varargs=None, keywords=None, defaults=[\'False\', \'Momentum\', \'False\'], " - } - member_method { - name: "apply_gradients" - argspec: "args=[\'self\', \'grads_and_vars\', \'global_step\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " - } - member_method { - name: "compute_gradients" - argspec: "args=[\'self\', \'loss\', \'var_list\', \'gate_gradients\', \'aggregation_method\', \'colocate_gradients_with_ops\', \'grad_loss\'], varargs=None, keywords=None, defaults=[\'None\', \'1\', \'None\', \'False\', \'None\'], " - } - member_method { - name: "get_name" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "get_slot" - argspec: "args=[\'self\', \'var\', \'name\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "get_slot_names" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "minimize" - argspec: "args=[\'self\', \'loss\', \'global_step\', \'var_list\', \'gate_gradients\', \'aggregation_method\', \'colocate_gradients_with_ops\', \'name\', \'grad_loss\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'1\', \'None\', \'False\', \'None\', \'None\'], " - } - member_method { - name: "variables" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-monitored-session.-step-context.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-monitored-session.-step-context.pbtxt deleted file mode 100644 index 03efe6639e..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-monitored-session.-step-context.pbtxt +++ /dev/null @@ -1,21 +0,0 @@ -path: "tensorflow.train.MonitoredSession.StepContext" -tf_class { - is_instance: "" - is_instance: "" - member { - name: "session" - mtype: "" - } - member_method { - name: "__init__" - argspec: "args=[\'self\', \'session\', \'run_with_hooks_fn\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "request_stop" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "run_with_hooks" - argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-monitored-session.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-monitored-session.pbtxt deleted file mode 100644 index 09b7b3fb53..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-monitored-session.pbtxt +++ /dev/null @@ -1,34 +0,0 @@ -path: "tensorflow.train.MonitoredSession" -tf_class { - is_instance: "" - is_instance: "" - is_instance: "" - member { - name: "StepContext" - mtype: "" - } - member { - name: "graph" - mtype: "" - } - member_method { - name: "__init__" - argspec: "args=[\'self\', \'session_creator\', \'hooks\', \'stop_grace_period_secs\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'120\'], " - } - member_method { - name: "close" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "run" - argspec: "args=[\'self\', \'fetches\', \'feed_dict\', \'options\', \'run_metadata\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " - } - member_method { - name: "run_step_fn" - argspec: "args=[\'self\', \'step_fn\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "should_stop" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-nan-loss-during-training-error.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-nan-loss-during-training-error.pbtxt deleted file mode 100644 index e415819b3d..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-nan-loss-during-training-error.pbtxt +++ /dev/null @@ -1,12 +0,0 @@ -path: "tensorflow.train.NanLossDuringTrainingError" -tf_class { - is_instance: "" - is_instance: "" - member { - name: "args" - mtype: "" - } - member_method { - name: "__init__" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-optimizer.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-optimizer.pbtxt deleted file mode 100644 index 876bb35e39..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-optimizer.pbtxt +++ /dev/null @@ -1,50 +0,0 @@ -path: "tensorflow.train.Optimizer" -tf_class { - is_instance: "" - is_instance: "" - is_instance: "" - member { - name: "GATE_GRAPH" - mtype: "" - } - member { - name: "GATE_NONE" - mtype: "" - } - member { - name: "GATE_OP" - mtype: "" - } - member_method { - name: "__init__" - argspec: "args=[\'self\', \'use_locking\', \'name\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "apply_gradients" - argspec: "args=[\'self\', \'grads_and_vars\', \'global_step\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " - } - member_method { - name: "compute_gradients" - argspec: "args=[\'self\', \'loss\', \'var_list\', \'gate_gradients\', \'aggregation_method\', \'colocate_gradients_with_ops\', \'grad_loss\'], varargs=None, keywords=None, defaults=[\'None\', \'1\', \'None\', \'False\', \'None\'], " - } - member_method { - name: "get_name" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "get_slot" - argspec: "args=[\'self\', \'var\', \'name\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "get_slot_names" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "minimize" - argspec: "args=[\'self\', \'loss\', \'global_step\', \'var_list\', \'gate_gradients\', \'aggregation_method\', \'colocate_gradients_with_ops\', \'name\', \'grad_loss\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'1\', \'None\', \'False\', \'None\', \'None\'], " - } - member_method { - name: "variables" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-proximal-adagrad-optimizer.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-proximal-adagrad-optimizer.pbtxt deleted file mode 100644 index 14349a74ef..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-proximal-adagrad-optimizer.pbtxt +++ /dev/null @@ -1,51 +0,0 @@ -path: "tensorflow.train.ProximalAdagradOptimizer" -tf_class { - is_instance: "" - is_instance: "" - is_instance: "" - is_instance: "" - member { - name: "GATE_GRAPH" - mtype: "" - } - member { - name: "GATE_NONE" - mtype: "" - } - member { - name: "GATE_OP" - mtype: "" - } - member_method { - name: "__init__" - argspec: "args=[\'self\', \'learning_rate\', \'initial_accumulator_value\', \'l1_regularization_strength\', \'l2_regularization_strength\', \'use_locking\', \'name\'], varargs=None, keywords=None, defaults=[\'0.1\', \'0.0\', \'0.0\', \'False\', \'ProximalAdagrad\'], " - } - member_method { - name: "apply_gradients" - argspec: "args=[\'self\', \'grads_and_vars\', \'global_step\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " - } - member_method { - name: "compute_gradients" - argspec: "args=[\'self\', \'loss\', \'var_list\', \'gate_gradients\', \'aggregation_method\', \'colocate_gradients_with_ops\', \'grad_loss\'], varargs=None, keywords=None, defaults=[\'None\', \'1\', \'None\', \'False\', \'None\'], " - } - member_method { - name: "get_name" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "get_slot" - argspec: "args=[\'self\', \'var\', \'name\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "get_slot_names" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "minimize" - argspec: "args=[\'self\', \'loss\', \'global_step\', \'var_list\', \'gate_gradients\', \'aggregation_method\', \'colocate_gradients_with_ops\', \'name\', \'grad_loss\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'1\', \'None\', \'False\', \'None\', \'None\'], " - } - member_method { - name: "variables" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-r-m-s-prop-optimizer.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-r-m-s-prop-optimizer.pbtxt deleted file mode 100644 index 906384a287..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-r-m-s-prop-optimizer.pbtxt +++ /dev/null @@ -1,51 +0,0 @@ -path: "tensorflow.train.RMSPropOptimizer" -tf_class { - is_instance: "" - is_instance: "" - is_instance: "" - is_instance: "" - member { - name: "GATE_GRAPH" - mtype: "" - } - member { - name: "GATE_NONE" - mtype: "" - } - member { - name: "GATE_OP" - mtype: "" - } - member_method { - name: "__init__" - argspec: "args=[\'self\', \'learning_rate\', \'decay\', \'momentum\', \'epsilon\', \'use_locking\', \'centered\', \'name\'], varargs=None, keywords=None, defaults=[\'0.9\', \'0.0\', \'1e-10\', \'False\', \'False\', \'RMSProp\'], " - } - member_method { - name: "apply_gradients" - argspec: "args=[\'self\', \'grads_and_vars\', \'global_step\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " - } - member_method { - name: "compute_gradients" - argspec: "args=[\'self\', \'loss\', \'var_list\', \'gate_gradients\', \'aggregation_method\', \'colocate_gradients_with_ops\', \'grad_loss\'], varargs=None, keywords=None, defaults=[\'None\', \'1\', \'None\', \'False\', \'None\'], " - } - member_method { - name: "get_name" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "get_slot" - argspec: "args=[\'self\', \'var\', \'name\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "get_slot_names" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "minimize" - argspec: "args=[\'self\', \'loss\', \'global_step\', \'var_list\', \'gate_gradients\', \'aggregation_method\', \'colocate_gradients_with_ops\', \'name\', \'grad_loss\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'1\', \'None\', \'False\', \'None\', \'None\'], " - } - member_method { - name: "variables" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-scaffold.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-scaffold.pbtxt deleted file mode 100644 index 38cc98b48e..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-scaffold.pbtxt +++ /dev/null @@ -1,53 +0,0 @@ -path: "tensorflow.train.Scaffold" -tf_class { - is_instance: "" - is_instance: "" - member { - name: "init_feed_dict" - mtype: "" - } - member { - name: "init_fn" - mtype: "" - } - member { - name: "init_op" - mtype: "" - } - member { - name: "local_init_op" - mtype: "" - } - member { - name: "ready_for_local_init_op" - mtype: "" - } - member { - name: "ready_op" - mtype: "" - } - member { - name: "saver" - mtype: "" - } - member { - name: "summary_op" - mtype: "" - } - member_method { - name: "__init__" - argspec: "args=[\'self\', \'init_op\', \'init_feed_dict\', \'init_fn\', \'ready_op\', \'ready_for_local_init_op\', \'local_init_op\', \'summary_op\', \'saver\', \'copy_from_scaffold\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\', \'None\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "default_local_init_op" - argspec: "args=[], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "finalize" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "get_or_default" - argspec: "args=[\'arg_name\', \'collection_key\', \'default_constructor\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-second-or-step-timer.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-second-or-step-timer.pbtxt deleted file mode 100644 index 3c5a6ac13c..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-second-or-step-timer.pbtxt +++ /dev/null @@ -1,26 +0,0 @@ -path: "tensorflow.train.SecondOrStepTimer" -tf_class { - is_instance: "" - is_instance: "" - is_instance: "" - member_method { - name: "__init__" - argspec: "args=[\'self\', \'every_secs\', \'every_steps\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " - } - member_method { - name: "last_triggered_step" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "reset" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "should_trigger_for_step" - argspec: "args=[\'self\', \'step\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "update_last_triggered_step" - argspec: "args=[\'self\', \'step\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-session-creator.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-session-creator.pbtxt deleted file mode 100644 index beb232715f..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-session-creator.pbtxt +++ /dev/null @@ -1,12 +0,0 @@ -path: "tensorflow.train.SessionCreator" -tf_class { - is_instance: "" - is_instance: "" - member_method { - name: "__init__" - } - member_method { - name: "create_session" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-session-manager.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-session-manager.pbtxt deleted file mode 100644 index 448764fe08..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-session-manager.pbtxt +++ /dev/null @@ -1,21 +0,0 @@ -path: "tensorflow.train.SessionManager" -tf_class { - is_instance: "" - is_instance: "" - member_method { - name: "__init__" - argspec: "args=[\'self\', \'local_init_op\', \'ready_op\', \'ready_for_local_init_op\', \'graph\', \'recovery_wait_secs\', \'local_init_run_options\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'30\', \'None\'], " - } - member_method { - name: "prepare_session" - argspec: "args=[\'self\', \'master\', \'init_op\', \'saver\', \'checkpoint_dir\', \'checkpoint_filename_with_path\', \'wait_for_checkpoint\', \'max_wait_secs\', \'config\', \'init_feed_dict\', \'init_fn\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'False\', \'7200\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "recover_session" - argspec: "args=[\'self\', \'master\', \'saver\', \'checkpoint_dir\', \'checkpoint_filename_with_path\', \'wait_for_checkpoint\', \'max_wait_secs\', \'config\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'False\', \'7200\', \'None\'], " - } - member_method { - name: "wait_for_session" - argspec: "args=[\'self\', \'master\', \'config\', \'max_wait_secs\'], varargs=None, keywords=None, defaults=[\'None\', \'inf\'], " - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-session-run-args.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-session-run-args.pbtxt deleted file mode 100644 index 442990893e..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-session-run-args.pbtxt +++ /dev/null @@ -1,27 +0,0 @@ -path: "tensorflow.train.SessionRunArgs" -tf_class { - is_instance: "" - is_instance: "" - is_instance: "" - member { - name: "feed_dict" - mtype: "" - } - member { - name: "fetches" - mtype: "" - } - member { - name: "options" - mtype: "" - } - member_method { - name: "__init__" - } - member_method { - name: "count" - } - member_method { - name: "index" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-session-run-context.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-session-run-context.pbtxt deleted file mode 100644 index d5adb15c95..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-session-run-context.pbtxt +++ /dev/null @@ -1,25 +0,0 @@ -path: "tensorflow.train.SessionRunContext" -tf_class { - is_instance: "" - is_instance: "" - member { - name: "original_args" - mtype: "" - } - member { - name: "session" - mtype: "" - } - member { - name: "stop_requested" - mtype: "" - } - member_method { - name: "__init__" - argspec: "args=[\'self\', \'original_args\', \'session\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "request_stop" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-session-run-values.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-session-run-values.pbtxt deleted file mode 100644 index 0b401d59c4..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-session-run-values.pbtxt +++ /dev/null @@ -1,27 +0,0 @@ -path: "tensorflow.train.SessionRunValues" -tf_class { - is_instance: "" - is_instance: "" - is_instance: "" - member { - name: "options" - mtype: "" - } - member { - name: "results" - mtype: "" - } - member { - name: "run_metadata" - mtype: "" - } - member_method { - name: "__init__" - } - member_method { - name: "count" - } - member_method { - name: "index" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-singular-monitored-session.-step-context.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-singular-monitored-session.-step-context.pbtxt deleted file mode 100644 index 36d8ce7ff8..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-singular-monitored-session.-step-context.pbtxt +++ /dev/null @@ -1,21 +0,0 @@ -path: "tensorflow.train.SingularMonitoredSession.StepContext" -tf_class { - is_instance: "" - is_instance: "" - member { - name: "session" - mtype: "" - } - member_method { - name: "__init__" - argspec: "args=[\'self\', \'session\', \'run_with_hooks_fn\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "request_stop" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "run_with_hooks" - argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-singular-monitored-session.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-singular-monitored-session.pbtxt deleted file mode 100644 index de0f2c1c1a..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-singular-monitored-session.pbtxt +++ /dev/null @@ -1,38 +0,0 @@ -path: "tensorflow.train.SingularMonitoredSession" -tf_class { - is_instance: "" - is_instance: "" - is_instance: "" - member { - name: "StepContext" - mtype: "" - } - member { - name: "graph" - mtype: "" - } - member_method { - name: "__init__" - argspec: "args=[\'self\', \'hooks\', \'scaffold\', \'master\', \'config\', \'checkpoint_dir\', \'stop_grace_period_secs\', \'checkpoint_filename_with_path\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'\', \'None\', \'None\', \'120\', \'None\'], " - } - member_method { - name: "close" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "raw_session" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "run" - argspec: "args=[\'self\', \'fetches\', \'feed_dict\', \'options\', \'run_metadata\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\'], " - } - member_method { - name: "run_step_fn" - argspec: "args=[\'self\', \'step_fn\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "should_stop" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-supervisor.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-supervisor.pbtxt deleted file mode 100644 index 9677e5a98e..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-supervisor.pbtxt +++ /dev/null @@ -1,153 +0,0 @@ -path: "tensorflow.train.Supervisor" -tf_class { - is_instance: "" - is_instance: "" - member { - name: "USE_DEFAULT" - mtype: "" - } - member { - name: "coord" - mtype: "" - } - member { - name: "global_step" - mtype: "" - } - member { - name: "init_feed_dict" - mtype: "" - } - member { - name: "init_op" - mtype: "" - } - member { - name: "is_chief" - mtype: "" - } - member { - name: "ready_for_local_init_op" - mtype: "" - } - member { - name: "ready_op" - mtype: "" - } - member { - name: "save_model_secs" - mtype: "" - } - member { - name: "save_path" - mtype: "" - } - member { - name: "save_summaries_secs" - mtype: "" - } - member { - name: "saver" - mtype: "" - } - member { - name: "session_manager" - mtype: "" - } - member { - name: "summary_op" - mtype: "" - } - member { - name: "summary_writer" - mtype: "" - } - member_method { - name: "Loop" - argspec: "args=[\'self\', \'timer_interval_secs\', \'target\', \'args\', \'kwargs\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " - } - member_method { - name: "PrepareSession" - argspec: "args=[\'self\', \'master\', \'config\', \'wait_for_checkpoint\', \'max_wait_secs\', \'start_standard_services\'], varargs=None, keywords=None, defaults=[\'\', \'None\', \'False\', \'7200\', \'True\'], " - } - member_method { - name: "RequestStop" - argspec: "args=[\'self\', \'ex\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "ShouldStop" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "StartQueueRunners" - argspec: "args=[\'self\', \'sess\', \'queue_runners\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "StartStandardServices" - argspec: "args=[\'self\', \'sess\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "Stop" - argspec: "args=[\'self\', \'threads\', \'close_summary_writer\', \'ignore_live_threads\'], varargs=None, keywords=None, defaults=[\'None\', \'True\', \'False\'], " - } - member_method { - name: "StopOnException" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "SummaryComputed" - argspec: "args=[\'self\', \'sess\', \'summary\', \'global_step\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "WaitForStop" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "__init__" - argspec: "args=[\'self\', \'graph\', \'ready_op\', \'ready_for_local_init_op\', \'is_chief\', \'init_op\', \'init_feed_dict\', \'local_init_op\', \'logdir\', \'summary_op\', \'saver\', \'global_step\', \'save_summaries_secs\', \'save_model_secs\', \'recovery_wait_secs\', \'stop_grace_secs\', \'checkpoint_basename\', \'session_manager\', \'summary_writer\', \'init_fn\', \'local_init_run_options\'], varargs=None, keywords=None, defaults=[\'None\', \'0\', \'0\', \'True\', \'0\', \'None\', \'0\', \'None\', \'0\', \'0\', \'0\', \'120\', \'600\', \'30\', \'120\', \'model.ckpt\', \'None\', \'0\', \'None\', \'None\'], " - } - member_method { - name: "loop" - argspec: "args=[\'self\', \'timer_interval_secs\', \'target\', \'args\', \'kwargs\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " - } - member_method { - name: "managed_session" - argspec: "args=[], varargs=args, keywords=kwds, defaults=None" - } - member_method { - name: "prepare_or_wait_for_session" - argspec: "args=[\'self\', \'master\', \'config\', \'wait_for_checkpoint\', \'max_wait_secs\', \'start_standard_services\'], varargs=None, keywords=None, defaults=[\'\', \'None\', \'False\', \'7200\', \'True\'], " - } - member_method { - name: "request_stop" - argspec: "args=[\'self\', \'ex\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "should_stop" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "start_queue_runners" - argspec: "args=[\'self\', \'sess\', \'queue_runners\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "start_standard_services" - argspec: "args=[\'self\', \'sess\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "stop" - argspec: "args=[\'self\', \'threads\', \'close_summary_writer\', \'ignore_live_threads\'], varargs=None, keywords=None, defaults=[\'None\', \'True\', \'False\'], " - } - member_method { - name: "stop_on_exception" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "summary_computed" - argspec: "args=[\'self\', \'sess\', \'summary\', \'global_step\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "wait_for_stop" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-vocab-info.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-vocab-info.pbtxt deleted file mode 100644 index 39b946b82f..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-vocab-info.pbtxt +++ /dev/null @@ -1,43 +0,0 @@ -path: "tensorflow.train.VocabInfo" -tf_class { - is_instance: "" - is_instance: "" - is_instance: "" - member { - name: "axis" - mtype: "" - } - member { - name: "backup_initializer" - mtype: "" - } - member { - name: "new_vocab" - mtype: "" - } - member { - name: "new_vocab_size" - mtype: "" - } - member { - name: "num_oov_buckets" - mtype: "" - } - member { - name: "old_vocab" - mtype: "" - } - member { - name: "old_vocab_size" - mtype: "" - } - member_method { - name: "__init__" - } - member_method { - name: "count" - } - member_method { - name: "index" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.-worker-session-creator.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.-worker-session-creator.pbtxt deleted file mode 100644 index ac26358068..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.-worker-session-creator.pbtxt +++ /dev/null @@ -1,14 +0,0 @@ -path: "tensorflow.train.WorkerSessionCreator" -tf_class { - is_instance: "" - is_instance: "" - is_instance: "" - member_method { - name: "__init__" - argspec: "args=[\'self\', \'scaffold\', \'master\', \'config\', \'max_wait_secs\'], varargs=None, keywords=None, defaults=[\'None\', \'\', \'None\', \'1800\'], " - } - member_method { - name: "create_session" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.pbtxt index a091daa298..3ff4b69d39 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.train.pbtxt @@ -1,21 +1,5 @@ path: "tensorflow.train" tf_module { - member { - name: "AdadeltaOptimizer" - mtype: "" - } - member { - name: "AdagradDAOptimizer" - mtype: "" - } - member { - name: "AdagradOptimizer" - mtype: "" - } - member { - name: "AdamOptimizer" - mtype: "" - } member { name: "BytesList" mtype: "" @@ -32,14 +16,6 @@ tf_module { name: "CheckpointSaverHook" mtype: "" } - member { - name: "CheckpointSaverListener" - mtype: "" - } - member { - name: "ChiefSessionCreator" - mtype: "" - } member { name: "ClusterDef" mtype: "" @@ -88,18 +64,10 @@ tf_module { name: "FloatList" mtype: "" } - member { - name: "FtrlOptimizer" - mtype: "" - } member { name: "GlobalStepWaiterHook" mtype: "" } - member { - name: "GradientDescentOptimizer" - mtype: "" - } member { name: "Int64List" mtype: "" @@ -112,54 +80,14 @@ tf_module { name: "LoggingTensorHook" mtype: "" } - member { - name: "LooperThread" - mtype: "" - } - member { - name: "MomentumOptimizer" - mtype: "" - } - member { - name: "MonitoredSession" - mtype: "" - } - member { - name: "NanLossDuringTrainingError" - mtype: "" - } member { name: "NanTensorHook" mtype: "" } - member { - name: "Optimizer" - mtype: "" - } - member { - name: "ProfilerHook" - mtype: "" - } - member { - name: "ProximalAdagradOptimizer" - mtype: "" - } member { name: "ProximalGradientDescentOptimizer" mtype: "" } - member { - name: "RMSPropOptimizer" - mtype: "" - } - member { - name: "Scaffold" - mtype: "" - } - member { - name: "SecondOrStepTimer" - mtype: "" - } member { name: "SequenceExample" mtype: "" @@ -172,34 +100,10 @@ tf_module { name: "ServerDef" mtype: "" } - member { - name: "SessionCreator" - mtype: "" - } - member { - name: "SessionManager" - mtype: "" - } - member { - name: "SessionRunArgs" - mtype: "" - } - member { - name: "SessionRunContext" - mtype: "" - } member { name: "SessionRunHook" mtype: "" } - member { - name: "SessionRunValues" - mtype: "" - } - member { - name: "SingularMonitoredSession" - mtype: "" - } member { name: "StepCounterHook" mtype: "" @@ -212,22 +116,6 @@ tf_module { name: "SummarySaverHook" mtype: "" } - member { - name: "Supervisor" - mtype: "" - } - member { - name: "VocabInfo" - mtype: "" - } - member { - name: "WorkerSessionCreator" - mtype: "" - } - member_method { - name: "NewCheckpointReader" - argspec: "args=[\'filepattern\'], varargs=None, keywords=None, defaults=None" - } member_method { name: "cosine_decay" argspec: "args=[\'learning_rate\', \'global_step\', \'decay_steps\', \'alpha\', \'name\'], varargs=None, keywords=None, defaults=[\'0.0\', \'None\'], " @@ -244,10 +132,6 @@ tf_module { name: "get_checkpoint_state" argspec: "args=[\'checkpoint_dir\', \'latest_filename\'], varargs=None, keywords=None, defaults=[\'None\'], " } - member_method { - name: "init_from_checkpoint" - argspec: "args=[\'ckpt_dir_or_file\', \'assignment_map\'], varargs=None, keywords=None, defaults=None" - } member_method { name: "inverse_time_decay" argspec: "args=[\'learning_rate\', \'global_step\', \'decay_steps\', \'decay_rate\', \'staircase\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'None\'], " @@ -281,17 +165,13 @@ tf_module { argspec: "args=[\'learning_rate\', \'global_step\', \'decay_steps\', \'initial_variance\', \'variance_decay\', \'num_periods\', \'alpha\', \'beta\', \'name\'], varargs=None, keywords=None, defaults=[\'1.0\', \'0.55\', \'0.5\', \'0.0\', \'0.001\', \'None\'], " } member_method { - name: "piecewise_constant" + name: "piecewise_constant_decay" argspec: "args=[\'x\', \'boundaries\', \'values\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } member_method { name: "polynomial_decay" argspec: "args=[\'learning_rate\', \'global_step\', \'decay_steps\', \'end_learning_rate\', \'power\', \'cycle\', \'name\'], varargs=None, keywords=None, defaults=[\'0.0001\', \'1.0\', \'False\', \'None\'], " } - member_method { - name: "replica_device_setter" - argspec: "args=[\'ps_tasks\', \'ps_device\', \'worker_device\', \'merge_devices\', \'cluster\', \'ps_ops\', \'ps_strategy\'], varargs=None, keywords=None, defaults=[\'0\', \'/job:ps\', \'/job:worker\', \'True\', \'None\', \'None\', \'None\'], " - } member_method { name: "sdca_fprint" argspec: "args=[\'input\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -308,8 +188,4 @@ tf_module { name: "summary_iterator" argspec: "args=[\'path\'], varargs=None, keywords=None, defaults=None" } - member_method { - name: "warm_start" - argspec: "args=[\'ckpt_to_initialize_from\', \'vars_to_warm_start\', \'var_name_to_vocab_info\', \'var_name_to_prev_var_name\'], varargs=None, keywords=None, defaults=[\'.*\', \'None\', \'None\'], " - } } diff --git a/tensorflow/tools/api/tests/api_compatibility_test.py b/tensorflow/tools/api/tests/api_compatibility_test.py index fb489ea80f..e7f23a1174 100644 --- a/tensorflow/tools/api/tests/api_compatibility_test.py +++ b/tensorflow/tools/api/tests/api_compatibility_test.py @@ -33,12 +33,13 @@ import re import sys import tensorflow as tf -from tensorflow._api import v2 as tf_v2 +from tensorflow._api.v2 import v2 as tf_v2 from google.protobuf import message from google.protobuf import text_format from tensorflow.python.lib.io import file_io +from tensorflow.python.framework import test_util from tensorflow.python.platform import resource_loader from tensorflow.python.platform import test from tensorflow.python.platform import tf_logging as logging @@ -126,9 +127,9 @@ def _FilterNonCoreGoldenFiles(golden_file_list): filtered_file_list = [] filtered_package_prefixes = ['tensorflow.%s.' % p for p in _NON_CORE_PACKAGES] for f in golden_file_list: - if any([ + if any( f.rsplit('/')[-1].startswith(pre) for pre in filtered_package_prefixes - ]): + ): continue filtered_file_list.append(f) return filtered_file_list @@ -310,6 +311,7 @@ class ApiCompatibilityTest(test.TestCase): update_goldens=FLAGS.update_goldens, api_version=api_version) + @test_util.run_deprecated_v1 def testAPIBackwardsCompatibility(self): api_version = 1 golden_file_pattern = os.path.join( @@ -328,6 +330,7 @@ class ApiCompatibilityTest(test.TestCase): 'tensorflow.python.util.lazy_loader.LazyLoader' in str(type(tf.contrib))) + @test_util.run_deprecated_v1 def testAPIBackwardsCompatibilityV1(self): api_version = 1 golden_file_pattern = os.path.join( diff --git a/tensorflow/tools/ci_build/Dockerfile.rbe.cuda10.0-cudnn7-ubuntu14.04 b/tensorflow/tools/ci_build/Dockerfile.rbe.cuda10.0-cudnn7-ubuntu14.04 new file mode 100644 index 0000000000..03de89b717 --- /dev/null +++ b/tensorflow/tools/ci_build/Dockerfile.rbe.cuda10.0-cudnn7-ubuntu14.04 @@ -0,0 +1,75 @@ +# To push a new version, run: +# $ docker build -f Dockerfile.rbe.cuda10.0-cudnn7-ubuntu14.04 \ +# --tag "gcr.io/asci-toolchain/nosla-cuda10.0-cudnn7-ubuntu14.04" . +# $ docker push gcr.io/asci-toolchain/nosla-cuda10.0-cudnn7-ubuntu14.04 + +FROM ubuntu:14.04 +LABEL maintainer="Manuel Klimek " + +RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates apt-transport-https gnupg-curl && \ + rm -rf /var/lib/apt/lists/* && \ + NVIDIA_GPGKEY_SUM=d1be581509378368edeec8c1eb2958702feedf3bc3d17011adbf24efacce4ab5 && \ + NVIDIA_GPGKEY_FPR=ae09fe4bbd223a84b2ccfce3f60f4b3d7fa2af80 && \ + apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64/7fa2af80.pub && \ + apt-key adv --export --no-emit-version -a $NVIDIA_GPGKEY_FPR | tail -n +2 > cudasign.pub && \ + echo "$NVIDIA_GPGKEY_SUM cudasign.pub" | sha256sum -c --strict - && rm cudasign.pub && \ + echo "deb https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64 /" > /etc/apt/sources.list.d/cuda.list && \ + echo "deb https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1604/x86_64 /" > /etc/apt/sources.list.d/nvidia-ml.list + +ENV CUDA_VERSION 10.0.130 +ENV CUDA_PKG_VERSION 10-0=$CUDA_VERSION-1 +ENV CUDNN_VERSION 7.3.1.20 +ENV NCCL_VERSION 2.3.5 +ENV NVIDIA_DRIVER_CAPABILITIES compute,utility +ENV NVIDIA_REQUIRE_CUDA "cuda>=10.0,driver>=410" +ENV NVIDIA_VISIBLE_DEVICES all +ENV PATH /usr/local/cuda/bin:${PATH} + +# TODO(b/110903506): /usr/loca/cuda/lib64/stubs should not be needed in +# LD_LIBRARY_PATH. The stubs/libcuda.so is not meant to used at runtime. The +# correct way to pass the path to bfd-ld is to pass +# -Wl,-rpath-link=/usr/local/cuda/lib64/stubs to all binaries transitively +# depending on libcuda. Optimally, builds targeting cuda would do that +# internally. +ENV LD_LIBRARY_PATH /usr/local/cuda/lib64/stubs + +LABEL com.nvidia.cudnn.version="${CUDNN_VERSION}" + +RUN apt-get update && apt-get install -y --no-install-recommends \ + cuda-command-line-tools-$CUDA_PKG_VERSION \ + cuda-compat-10-0=410.48-1 \ + cuda-cudart-$CUDA_PKG_VERSION \ + cuda-libraries-$CUDA_PKG_VERSION \ + cuda-libraries-dev-$CUDA_PKG_VERSION \ + cuda-minimal-build-$CUDA_PKG_VERSION \ + cuda-nvml-dev-$CUDA_PKG_VERSION \ + cuda-nvtx-$CUDA_PKG_VERSION \ + libcudnn7=$CUDNN_VERSION-1+cuda10.0 \ + libcudnn7=$CUDNN_VERSION-1+cuda10.0 \ + libcudnn7-dev=$CUDNN_VERSION-1+cuda10.0 \ + libnccl2=$NCCL_VERSION-2+cuda10.0 \ + libnccl-dev=$NCCL_VERSION-2+cuda10.0 && \ + ln -s cuda-10.0 /usr/local/cuda && \ + apt-mark hold libcudnn7 && \ + apt-mark hold libnccl2 && \ + rm -rf /var/lib/apt/lists/* + +# TODO(b/110903506): Provide a link to the SONAME of libcuda.so. +# https://github.com/NVIDIA/nvidia-docker/issues/775 +RUN ln -s libcuda.so /usr/local/cuda/lib64/stubs/libcuda.so.1 + +# TODO(klimek): Once the TODO in tensorflow's configure.py to correctly find +# libnccl is resolved, delete this block. +RUN ln -s /usr/lib/x86_64-linux-gnu/libnccl.so /usr/lib/libnccl.so \ + && ln -s /usr/lib/x86_64-linux-gnu/libnccl.so /usr/lib/libnccl.so.2 + +# Copy and run the install scripts. +COPY install/*.sh /install/ +ARG DEBIAN_FRONTEND=noninteractive +RUN /install/install_bootstrap_deb_packages.sh +RUN add-apt-repository -y ppa:openjdk-r/ppa && \ + add-apt-repository -y ppa:george-edison55/cmake-3.x +RUN /install/install_deb_packages.sh +RUN /install/install_pip_packages.sh +RUN /install/install_golang.sh + diff --git a/tensorflow/tools/ci_build/builds/libtensorflow.sh b/tensorflow/tools/ci_build/builds/libtensorflow.sh index 9b3ff0cba7..44abcc309b 100755 --- a/tensorflow/tools/ci_build/builds/libtensorflow.sh +++ b/tensorflow/tools/ci_build/builds/libtensorflow.sh @@ -55,6 +55,7 @@ function build_libtensorflow_tarball() { export CC_OPT_FLAGS='-mavx' if [ "${TF_NEED_CUDA}" == "1" ]; then BAZEL_OPTS="${BAZEL_OPTS} --config=cuda" + export TF_NEED_ROCM=0 fi bazel clean --expunge yes "" | ./configure diff --git a/tensorflow/tools/ci_build/windows/cpu/pip/build_tf_windows.sh b/tensorflow/tools/ci_build/windows/cpu/pip/build_tf_windows.sh index 177ef390db..46f5bdef09 100644 --- a/tensorflow/tools/ci_build/windows/cpu/pip/build_tf_windows.sh +++ b/tensorflow/tools/ci_build/windows/cpu/pip/build_tf_windows.sh @@ -58,6 +58,8 @@ PY_TEST_DIR="py_test_dir" SKIP_TEST=0 RELEASE_BUILD=0 TEST_TARGET="//${PY_TEST_DIR}/tensorflow/python/..." +PROJECT_NAME="" +EXTRA_BUILD_FLAGS="" # --skip_test Skip running tests # --enable_remote_cache Add options to enable remote cache for build and test @@ -73,6 +75,20 @@ for ARG in "$@"; do --release_build) RELEASE_BUILD=1 ;; --test_core_only) TEST_TARGET="//${PY_TEST_DIR}/tensorflow/python/..." ;; --test_contrib_only) TEST_TARGET="//${PY_TEST_DIR}/tensorflow/contrib/..." ;; + --extra_build_flags) + shift + if [[ -z "$1" ]]; then + break + fi + EXTRA_BUILD_FLAGS="$1" + ;; + --project_name) + shift + if [[ -z "$1" ]]; then + break + fi + PROJECT_NAME="$1" + ;; *) esac done @@ -88,7 +104,11 @@ fi if [[ "$TF_NIGHTLY" == 1 ]]; then python tensorflow/tools/ci_build/update_version.py --nightly - EXTRA_PIP_FLAG="--nightly_flag" + if [ -z ${PROJECT_NAME} ]; then + EXTRA_PIP_FLAGS="--nightly_flag" + else + EXTRA_PIP_FLAGS="--project_name=${PROJECT_NAME} --nightly_flag" + fi fi # Enable short object file path to avoid long path issue on Windows. @@ -100,7 +120,8 @@ fi run_configure_for_cpu_build -bazel build --announce_rc --config=opt tensorflow/tools/pip_package:build_pip_package || exit $? +bazel build --announce_rc --config=opt ${EXTRA_BUILD_FLAGS} \ + tensorflow/tools/pip_package:build_pip_package || exit $? if [[ "$SKIP_TEST" == 1 ]]; then exit 0 @@ -109,7 +130,7 @@ fi # Create a python test directory to avoid package name conflict create_python_test_dir "${PY_TEST_DIR}" -./bazel-bin/tensorflow/tools/pip_package/build_pip_package "$PWD/${PY_TEST_DIR}" "${EXTRA_PIP_FLAG}" +./bazel-bin/tensorflow/tools/pip_package/build_pip_package "$PWD/${PY_TEST_DIR}" "${EXTRA_PIP_FLAGS}" if [[ "$TF_NIGHTLY" == 1 ]]; then exit 0 @@ -126,8 +147,8 @@ N_JOBS="${NUMBER_OF_PROCESSORS}" # which will result testing system installed tensorflow bazel test --announce_rc --config=opt -k --test_output=errors \ --define=no_tensorflow_py_deps=true --test_lang_filters=py \ - --test_tag_filters=-no_pip,-no_windows,-no_oss \ - --build_tag_filters=-no_pip,-no_windows,-no_oss --build_tests_only \ + --test_tag_filters=-no_pip,-no_windows,-no_oss,-gpu \ + --build_tag_filters=-no_pip,-no_windows,-no_oss,-gpu --build_tests_only \ --test_size_filters=small,medium \ --jobs="${N_JOBS}" --test_timeout="300,450,1200,3600" \ --flaky_test_attempts=3 \ diff --git a/tensorflow/tools/ci_build/windows/gpu/pip/build_tf_windows.sh b/tensorflow/tools/ci_build/windows/gpu/pip/build_tf_windows.sh index 6178d7794d..3aec8d6584 100644 --- a/tensorflow/tools/ci_build/windows/gpu/pip/build_tf_windows.sh +++ b/tensorflow/tools/ci_build/windows/gpu/pip/build_tf_windows.sh @@ -58,6 +58,8 @@ PY_TEST_DIR="py_test_dir" SKIP_TEST=0 RELEASE_BUILD=0 TEST_TARGET="//${PY_TEST_DIR}/tensorflow/python/..." +PROJECT_NAME="" +EXTRA_BUILD_FLAGS="" # --skip_test Skip running tests # --enable_remote_cache Add options to enable remote cache for build and test @@ -73,6 +75,20 @@ for ARG in "$@"; do --release_build) RELEASE_BUILD=1 ;; --test_core_only) TEST_TARGET="//${PY_TEST_DIR}/tensorflow/python/..." ;; --test_contrib_only) TEST_TARGET="//${PY_TEST_DIR}/tensorflow/contrib/..." ;; + --extra_build_flags) + shift + if [[ -z "$1" ]]; then + break + fi + EXTRA_BUILD_FLAGS="$1" + ;; + --project_name) + shift + if [[ -z "$1" ]]; then + break + fi + PROJECT_NAME="$1" + ;; *) esac done @@ -88,7 +104,11 @@ fi if [[ "$TF_NIGHTLY" == 1 ]]; then python tensorflow/tools/ci_build/update_version.py --nightly - EXTRA_PIP_FLAG="--nightly_flag" + if [ -z ${PROJECT_NAME} ]; then + EXTRA_PIP_FLAGS="--nightly_flag" + else + EXTRA_PIP_FLAGS="--project_name=${PROJECT_NAME} --nightly_flag" + fi fi # Enable short object file path to avoid long path issue on Windows. @@ -104,6 +124,7 @@ fi run_configure_for_gpu_build bazel build --announce_rc --config=opt --define=no_tensorflow_py_deps=true \ + ${EXTRA_BUILD_FLAGS} \ tensorflow/tools/pip_package:build_pip_package || exit $? if [[ "$SKIP_TEST" == 1 ]]; then @@ -113,7 +134,8 @@ fi # Create a python test directory to avoid package name conflict create_python_test_dir "${PY_TEST_DIR}" -./bazel-bin/tensorflow/tools/pip_package/build_pip_package "$PWD/${PY_TEST_DIR}" --gpu "${EXTRA_PIP_FLAG}" +./bazel-bin/tensorflow/tools/pip_package/build_pip_package "$PWD/${PY_TEST_DIR}" \ + --gpu "${EXTRA_PIP_FLAGS}" if [[ "$TF_NIGHTLY" == 1 ]]; then exit 0 diff --git a/tensorflow/tools/compatibility/BUILD b/tensorflow/tools/compatibility/BUILD index 5f619c4e62..05d924c092 100644 --- a/tensorflow/tools/compatibility/BUILD +++ b/tensorflow/tools/compatibility/BUILD @@ -14,6 +14,18 @@ py_library( srcs_version = "PY2AND3", ) +py_test( + name = "ast_edits_test", + srcs = ["ast_edits_test.py"], + srcs_version = "PY2AND3", + deps = [ + ":ast_edits", + "//tensorflow/python:client_testlib", + "//tensorflow/python:framework_test_lib", + "@six_archive//:six", + ], +) + py_binary( name = "tf_upgrade", srcs = ["tf_upgrade.py"], @@ -39,8 +51,8 @@ py_library( srcs_version = "PY2AND3", ) -py_binary( - name = "tf_upgrade_v2", +py_library( + name = "tf_upgrade_v2_lib", srcs = [ "renames_v2.py", "tf_upgrade_v2.py", @@ -49,14 +61,28 @@ py_binary( deps = [":ast_edits"], ) +py_binary( + name = "tf_upgrade_v2", + srcs = ["tf_upgrade_v2_main.py"], + main = "tf_upgrade_v2_main.py", + srcs_version = "PY2AND3", + deps = [ + ":ast_edits", + ":tf_upgrade_v2_lib", + ], +) + py_test( name = "tf_upgrade_v2_test", srcs = ["tf_upgrade_v2_test.py"], srcs_version = "PY2AND3", deps = [ ":tf_upgrade_v2", + "//tensorflow:tensorflow_py", "//tensorflow/python:client_testlib", "//tensorflow/python:framework_test_lib", + "//tensorflow/tools/common:public_api", + "//tensorflow/tools/common:traverse", "@six_archive//:six", ], ) @@ -100,18 +126,28 @@ py_test( genrule( name = "generate_upgraded_file_v2", testonly = 1, - srcs = ["testdata/test_file_v1_10.py"], + srcs = ["testdata/test_file_v1_12.py"], outs = [ "test_file_v2_0.py", "report_v2.txt", ], cmd = ("$(location :tf_upgrade_v2)" + - " --infile $(location testdata/test_file_v1_10.py)" + + " --infile $(location testdata/test_file_v1_12.py)" + " --outfile $(location test_file_v2_0.py)" + " --reportfile $(location report_v2.txt)"), tools = [":tf_upgrade_v2"], ) +py_test( + name = "test_file_v1_12", + size = "small", + srcs = ["testdata/test_file_v1_12.py"], + srcs_version = "PY2AND3", + deps = [ + "//tensorflow:tensorflow_py", + ], +) + py_test( name = "test_file_v2_0", size = "small", @@ -128,6 +164,6 @@ exports_files( "tf_upgrade.py", "renames_v2.py", "testdata/test_file_v0_11.py", - "testdata/test_file_v1_10.py", + "testdata/test_file_v1_12.py", ], ) diff --git a/tensorflow/tools/compatibility/README.md b/tensorflow/tools/compatibility/README.md index aabc7b253d..6ff42b1fef 100644 --- a/tensorflow/tools/compatibility/README.md +++ b/tensorflow/tools/compatibility/README.md @@ -1,60 +1,77 @@ # TensorFlow Python API Upgrade Utility This tool allows you to upgrade your existing TensorFlow Python scripts. -This script can be run on a single Python file: +Specifically: \ +`tf_upgrade_v2.py`: upgrades code from TensorFlow 1.12 to TensorFlow 2.0 preview. \ +`tf_upgrade.py`: upgrades code to TensorFlow 1.0 from TensorFlow 0.11. + +## Running the script from pip package + +First, install TensorFlow pip package. See +https://www.tensorflow.org/install/pip. + +Upgrade script can be run on a single Python file: ``` -tf_upgrade.py --infile foo.py --outfile foo-upgraded.py +tf_upgrade_v2 --infile foo.py --outfile foo-upgraded.py ``` It will print a list of errors it finds that it can't fix. You can also run it on a directory tree: ``` +# upgrade the .py files and copy all the other files to the outtree +tf_upgrade_v2 --intree coolcode --outtree coolcode-upgraded + # just upgrade the .py files -tf_upgrade.py --intree coolcode --outtree coolcode-upgraded -# after upgrade the .py files, then copy all the other files to the outtree -tf_upgrade.py --intree coolcode --outtree coolcode-upgraded --copyotherfiles True +tf_upgrade_v2 --intree coolcode --outtree coolcode-upgraded --copyotherfiles False ``` -In either case, it will also dump out a report e.g. which will detail changes + +## Report + +The script will also dump out a report e.g. which will detail changes e.g.: ``` -third_party/tensorflow/tools/compatibility/test_file_v0.11.py Line 125 +'tensorflow/tools/compatibility/testdata/test_file_v1_12.py' Line 65 +-------------------------------------------------------------------------------- + +Added keyword 'input' to reordered function 'tf.argmax' +Renamed keyword argument from 'dimension' to 'axis' -Renamed keyword argument from `dim` to `axis` -Renamed keyword argument from `squeeze_dims` to `axis` + Old: tf.argmax([[1, 3, 2]], dimension=0)) + ~~~~~~~~~~ + New: tf.argmax(input=[[1, 3, 2]], axis=0)) - Old: [[1, 2, 3]], dim=1), squeeze_dims=[1]).eval(), - ~~~~ ~~~~~~~~~~~~~ - New: [[1, 2, 3]], axis=1), axis=[1]).eval(), - ~~~~~ ~~~~~ ``` ## Caveats - Don't update parts of your code manually before running this script. In -particular, functions that have had reordered arguments like `tf.concat` -or `tf.split` will cause the script to incorrectly add keyword arguments that -mismap arguments. +particular, functions that have had reordered arguments like `tf.argmax` +or `tf.batch_to_space` will cause the script to incorrectly add keyword +arguments that mismap arguments. - This script wouldn't actually reorder arguments. Instead, the script will add keyword arguments to functions that had their arguments reordered. - This script is not able to upgrade all functions. One notable example is -`tf.reverse()` which has been changed to take a list of indices rather than -a tensor of bools. If the script detects this, it will report this to stdout +`tf.nn.conv2d` that no longer takes `use_cudnn_on_gpu` argument. +If the script detects this, it will report this to stdout (and in the report), and you can fix it manually. For example if you have -`tf.reverse(a, [False, True, True])` you will need to manually change it to -`tf.reverse(a, [1, 2])`. +`tf.nn.conv2d(inputs, filters, strides, padding, use_cudnn_on_gpu=True)` +you will need to manually change it to +`tf.nn.conv2d(input, filters, strides, padding)`. - There are some syntaxes that are not handleable with this script as this -script was designed to use only standard python packages. If the script fails -with "A necessary keyword argument failed to be inserted." or +script was designed to use only standard python packages. +There is an alternative available for TensorFlow 0.* to 1.0 upgrade script. +If the script fails with "A necessary keyword argument failed to be inserted." or "Failed to find keyword lexicographically. Fix manually.", you can try [@machrisaa's fork of this script](https://github.com/machrisaa/tf0to1). [@machrisaa](https://github.com/machrisaa) has used the [RedBaron Python refactoring engine](https://redbaron.readthedocs.io/en/latest/) which is able to localize syntactic elements more reliably than the built-in -`ast` module this script is based upon. +`ast` module this script is based upon. Note that the alternative script is not +available for TensorFlow 2.0 upgrade. diff --git a/tensorflow/tools/compatibility/ast_edits.py b/tensorflow/tools/compatibility/ast_edits.py index 56c67b8356..eac2150502 100644 --- a/tensorflow/tools/compatibility/ast_edits.py +++ b/tensorflow/tools/compatibility/ast_edits.py @@ -21,11 +21,16 @@ from __future__ import print_function import ast import collections import os +import re import shutil import sys import tempfile import traceback +# Some regular expressions we will need for parsing +FIND_OPEN = re.compile(r"^\s*(\[).*$") +FIND_STRING_CHARS = re.compile(r"['\"]") + class APIChangeSpec(object): """This class defines the transformations that need to happen. @@ -40,6 +45,10 @@ class APIChangeSpec(object): * `function_reorders`: maps functions whose argument order has changed to the list of arguments in the new order * `function_handle`: maps function names to custom handlers for the function + * `function_warnings`: maps full names of functions to warnings that will be + printed out if the function is used. (e.g. tf.nn.convolution()) + * `unrestricted_function_warnings`: maps names of functions to warnings that + will be printed out when the function is used (e.g. foo.convolution()). For an example, see `TFAPIChangeSpec`. """ @@ -53,7 +62,7 @@ class _FileEditTuple( Fields: comment: A description of the edit and why it was made. line: The line number in the file where the edit occurs (1-indexed). - start: The line number in the file where the edit occurs (0-indexed). + start: The column number in the file where the edit occurs (0-indexed). old: text string to remove (this must match what was in file). new: text string to add in place of `old`. """ @@ -195,6 +204,29 @@ class _ASTCallVisitor(ast.NodeVisitor): except KeyError: pass + def _print_warning_for_function_unrestricted(self, node): + """Print a warning when specific functions are called. + + The function _print_warning_for_function matches the full name of the called + function, e.g., tf.foo.bar(). This function matches the function name that + is called, as long as the function is an attribute. For example, + `tf.foo.bar()` and `foo.bar()` are matched, but not `bar()`. + + Args: + node: ast.Call object + """ + function_warnings = getattr( + self._api_change_spec, "unrestricted_function_warnings", {}) + if isinstance(node.func, ast.Attribute): + function_name = node.func.attr + try: + warning_message = function_warnings[function_name] + self._file_edit.add(warning_message, + node.lineno, node.col_offset, "", "", + error="%s requires manual check." % function_name) + except KeyError: + pass + def _get_attribute_full_path(self, node): """Traverse an attribute to generate a full name e.g. tf.foo.bar. @@ -209,11 +241,11 @@ class _ASTCallVisitor(ast.NodeVisitor): items = [] while not isinstance(curr, ast.Name): if not isinstance(curr, ast.Attribute): - return None + return None, None items.append(curr.attr) curr = curr.value items.append(curr.id) - return ".".join(reversed(items)) + return ".".join(reversed(items)), items[0] def _find_true_position(self, node): """Return correct line number and column offset for a given node. @@ -221,13 +253,12 @@ class _ASTCallVisitor(ast.NodeVisitor): This is necessary mainly because ListComp's location reporting reports the next token after the list comprehension list opening. + Returns: + lineno, offset for the given node + Args: node: Node for which we wish to know the lineno and col_offset """ - import re - find_open = re.compile("^\s*(\\[).*$") - find_string_chars = re.compile("['\"]") - if isinstance(node, ast.ListComp): # Strangely, ast.ListComp returns the col_offset of the first token # after the '[' token which appears to be a bug. Workaround by @@ -241,7 +272,7 @@ class _ASTCallVisitor(ast.NodeVisitor): reversed_preceding_text = text[:col][::-1] # First find if a [ can be found with only whitespace between it and # col. - m = find_open.match(reversed_preceding_text) + m = FIND_OPEN.match(reversed_preceding_text) if m: new_col_offset = col - m.start(1) - 1 return line, new_col_offset @@ -260,7 +291,7 @@ class _ASTCallVisitor(ast.NodeVisitor): comment_start = prev_line.find("#") if comment_start == -1: col = len(prev_line) - 1 - elif find_string_chars.search(prev_line[comment_start:]) is None: + elif FIND_STRING_CHARS.search(prev_line[comment_start:]) is None: col = comment_start else: return None, None @@ -276,9 +307,10 @@ class _ASTCallVisitor(ast.NodeVisitor): Args: node: Current Node """ + self._print_warning_for_function_unrestricted(node) # Find a simple attribute name path e.g. "tf.foo.bar" - full_name = self._get_attribute_full_path(node.func) + full_name, name = self._get_attribute_full_path(node.func) # Make sure the func is marked as being part of a call node.func.is_function_for_call = True @@ -286,6 +318,9 @@ class _ASTCallVisitor(ast.NodeVisitor): if full_name: # Call special handlers function_handles = self._api_change_spec.function_handle + glob_name = "*.{}".format(name) + if glob_name in function_handles: + function_handles[glob_name](self._file_edit, node) if full_name in function_handles: function_handles[full_name](self._file_edit, node) @@ -358,10 +393,11 @@ class _ASTCallVisitor(ast.NodeVisitor): Args: node: Node that is of type ast.Attribute """ - full_name = self._get_attribute_full_path(node) + full_name, _ = self._get_attribute_full_path(node) if full_name: - self._rename_functions(node, full_name) + # Make sure the warning comes first, otherwise the name may have changed self._print_warning_for_function(node, full_name) + self._rename_functions(node, full_name) if full_name in self._api_change_spec.change_to_function: if not hasattr(node, "is_function_for_call"): new_text = full_name + "()" diff --git a/tensorflow/tools/compatibility/ast_edits_test.py b/tensorflow/tools/compatibility/ast_edits_test.py new file mode 100644 index 0000000000..99f20a026f --- /dev/null +++ b/tensorflow/tools/compatibility/ast_edits_test.py @@ -0,0 +1,420 @@ +# Copyright 2018 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 ast_edits which is used in tf upgraders. + +All of the tests assume that we want to change from an API containing + + def f(a, b, kw1, kw2): ... + def g(a, b, kw1, c, kw1_alias): ... + def g2(a, b, kw1, c, d, kw1_alias): ... + def h(a, kw1, kw2, kw1_alias, kw2_alias): ... + +and the changes to the API consist of renaming, reordering, and/or removing +arguments. Thus, we want to be able to generate changes to produce each of the +following new APIs: + + def f(a, b, kw1, kw3): ... + def f(a, b, kw2, kw1): ... + def f(a, b, kw3, kw1): ... + def g(a, b, kw1, c): ... + def g(a, b, c, kw1): ... + def g2(a, b, kw1, c, d): ... + def g2(a, b, c, d, kw1): ... + def h(a, kw1, kw2): ... + +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +import six +from tensorflow.python.framework import test_util +from tensorflow.python.platform import test as test_lib +from tensorflow.tools.compatibility import ast_edits + + +class NoUpdateSpec(ast_edits.APIChangeSpec): + """A specification of an API change which doesn't change anything.""" + + def __init__(self): + self.function_handle = {} + self.function_reorders = {} + self.function_keyword_renames = {} + self.symbol_renames = {} + self.function_warnings = {} + self.unrestricted_function_warnings = {} + self.change_to_function = {} + + +class RenameKeywordSpec(NoUpdateSpec): + """A specification where kw2 gets renamed to kw3. + + The new API is + + def f(a, b, kw1, kw3): ... + + """ + + def __init__(self): + NoUpdateSpec.__init__(self) + self.update_renames() + + def update_renames(self): + self.function_keyword_renames["f"] = {"kw2": "kw3"} + + +class ReorderKeywordSpec(NoUpdateSpec): + """A specification where kw2 gets moved in front of kw1. + + The new API is + + def f(a, b, kw2, kw1): ... + + """ + + def __init__(self): + NoUpdateSpec.__init__(self) + self.update_reorders() + + def update_reorders(self): + # Note that these should be in the old order. + self.function_reorders["f"] = ["a", "b", "kw1", "kw2"] + + +class ReorderAndRenameKeywordSpec(ReorderKeywordSpec, RenameKeywordSpec): + """A specification where kw2 gets moved in front of kw1 and is changed to kw3. + + The new API is + + def f(a, b, kw3, kw1): ... + + """ + + def __init__(self): + ReorderKeywordSpec.__init__(self) + RenameKeywordSpec.__init__(self) + self.update_renames() + self.update_reorders() + + +class RemoveDeprecatedAliasKeyword(NoUpdateSpec): + """A specification where kw1_alias is removed in g. + + The new API is + + def g(a, b, kw1, c): ... + def g2(a, b, kw1, c, d): ... + + """ + + def __init__(self): + NoUpdateSpec.__init__(self) + self.function_keyword_renames["g"] = {"kw1_alias": "kw1"} + self.function_keyword_renames["g2"] = {"kw1_alias": "kw1"} + + +class RemoveDeprecatedAliasAndReorderRest(RemoveDeprecatedAliasKeyword): + """A specification where kw1_alias is removed in g. + + The new API is + + def g(a, b, c, kw1): ... + def g2(a, b, c, d, kw1): ... + + """ + + def __init__(self): + RemoveDeprecatedAliasKeyword.__init__(self) + # Note that these should be in the old order. + self.function_reorders["g"] = ["a", "b", "kw1", "c"] + self.function_reorders["g2"] = ["a", "b", "kw1", "c", "d"] + + +class RemoveMultipleKeywordArguments(NoUpdateSpec): + """A specification where both keyword aliases are removed from h. + + The new API is + + def h(a, kw1, kw2): ... + + """ + + def __init__(self): + NoUpdateSpec.__init__(self) + self.function_keyword_renames["h"] = { + "kw1_alias": "kw1", + "kw2_alias": "kw2", + } + + +class TestAstEdits(test_util.TensorFlowTestCase): + + def _upgrade(self, spec, old_file_text): + in_file = six.StringIO(old_file_text) + out_file = six.StringIO() + upgrader = ast_edits.ASTCodeUpgrader(spec) + count, report, errors = ( + upgrader.process_opened_file("test.py", in_file, + "test_out.py", out_file)) + return (count, report, errors), out_file.getvalue() + + def testNoTransformIfNothingIsSupplied(self): + text = "f(a, b, kw1=c, kw2=d)\n" + _, new_text = self._upgrade(NoUpdateSpec(), text) + self.assertEqual(new_text, text) + + text = "f(a, b, c, d)\n" + _, new_text = self._upgrade(NoUpdateSpec(), text) + self.assertEqual(new_text, text) + + def testKeywordRename(self): + """Test that we get the expected result if renaming kw2 to kw3.""" + text = "f(a, b, kw1=c, kw2=d)\n" + expected = "f(a, b, kw1=c, kw3=d)\n" + _, new_text = self._upgrade(RenameKeywordSpec(), text) + self.assertEqual(new_text, expected) + + # No keywords specified, no reordering, so we should get input as output + text = "f(a, b, c, d)\n" + _, new_text = self._upgrade(RenameKeywordSpec(), text) + self.assertEqual(new_text, text) + + def testKeywordReorder(self): + """Test that we get the expected result if kw2 is now before kw1.""" + text = "f(a, b, kw1=c, kw2=d)\n" + acceptable_outputs = [ + # No change is a valid output + text, + # Just reordering the kw.. args is also ok + "f(a, b, kw2=d, kw1=c)\n", + # Also cases where all arguments are fully specified are allowed + "f(a=a, b=b, kw1=c, kw2=d)\n", + "f(a=a, b=b, kw2=d, kw1=c)\n", + ] + _, new_text = self._upgrade(ReorderKeywordSpec(), text) + self.assertIn(new_text, acceptable_outputs) + + # Keywords are reordered, so we should reorder arguments too + text = "f(a, b, c, d)\n" + acceptable_outputs = [ + "f(a, b, d, c)\n", + "f(a=a, b=b, kw1=c, kw2=d)\n", + "f(a=a, b=b, kw2=d, kw1=c)\n", + ] + _, new_text = self._upgrade(ReorderKeywordSpec(), text) + self.assertIn(new_text, acceptable_outputs) + + def testKeywordReorderAndRename(self): + """Test that we get the expected result if kw2 is renamed and moved.""" + text = "f(a, b, kw1=c, kw2=d)\n" + acceptable_outputs = [ + "f(a, b, kw3=d, kw1=c)\n", + "f(a=a, b=b, kw1=c, kw3=d)\n", + "f(a=a, b=b, kw3=d, kw1=c)\n", + ] + _, new_text = self._upgrade(ReorderAndRenameKeywordSpec(), text) + self.assertIn(new_text, acceptable_outputs) + + # Keywords are reordered, so we should reorder arguments too + text = "f(a, b, c, d)\n" + acceptable_outputs = [ + "f(a, b, d, c)\n", + "f(a=a, b=b, kw1=c, kw3=d)\n", + "f(a=a, b=b, kw3=d, kw1=c)\n", + ] + _, new_text = self._upgrade(ReorderAndRenameKeywordSpec(), text) + self.assertIn(new_text, acceptable_outputs) + + def testRemoveDeprecatedKeywordAlias(self): + """Test that we get the expected result if a keyword alias is removed.""" + text = "g(a, b, kw1=x, c=c)\n" + acceptable_outputs = [ + # Not using deprecated alias, so original is ok + text, + "g(a=a, b=b, kw1=x, c=c)\n", + ] + _, new_text = self._upgrade(RemoveDeprecatedAliasKeyword(), text) + self.assertIn(new_text, acceptable_outputs) + + # No keyword used, should be no change + text = "g(a, b, x, c)\n" + _, new_text = self._upgrade(RemoveDeprecatedAliasKeyword(), text) + self.assertEqual(new_text, text) + + # If we used the alias, it should get renamed + text = "g(a, b, kw1_alias=x, c=c)\n" + acceptable_outputs = [ + "g(a, b, kw1=x, c=c)\n", + "g(a, b, c=c, kw1=x)\n", + "g(a=a, b=b, kw1=x, c=c)\n", + "g(a=a, b=b, c=c, kw1=x)\n", + ] + _, new_text = self._upgrade(RemoveDeprecatedAliasKeyword(), text) + self.assertIn(new_text, acceptable_outputs) + + # It should get renamed even if it's last + text = "g(a, b, c=c, kw1_alias=x)\n" + acceptable_outputs = [ + "g(a, b, kw1=x, c=c)\n", + "g(a, b, c=c, kw1=x)\n", + "g(a=a, b=b, kw1=x, c=c)\n", + "g(a=a, b=b, c=c, kw1=x)\n", + ] + _, new_text = self._upgrade(RemoveDeprecatedAliasKeyword(), text) + self.assertIn(new_text, acceptable_outputs) + + def testRemoveDeprecatedKeywordAndReorder(self): + """Test for when a keyword alias is removed and args are reordered.""" + text = "g(a, b, kw1=x, c=c)\n" + acceptable_outputs = [ + "g(a, b, c=c, kw1=x)\n", + "g(a=a, b=b, kw1=x, c=c)\n", + ] + _, new_text = self._upgrade(RemoveDeprecatedAliasAndReorderRest(), text) + self.assertIn(new_text, acceptable_outputs) + + # Keywords are reordered, so we should reorder arguments too + text = "g(a, b, x, c)\n" + # Don't accept an output which doesn't reorder c and d + acceptable_outputs = [ + "g(a, b, c, x)\n", + "g(a=a, b=b, kw1=x, c=c)\n", + ] + _, new_text = self._upgrade(RemoveDeprecatedAliasAndReorderRest(), text) + self.assertIn(new_text, acceptable_outputs) + + # If we used the alias, it should get renamed + text = "g(a, b, kw1_alias=x, c=c)\n" + acceptable_outputs = [ + "g(a, b, kw1=x, c=c)\n", + "g(a, b, c=c, kw1=x)\n", + "g(a=a, b=b, kw1=x, c=c)\n", + "g(a=a, b=b, c=c, kw1=x)\n", + ] + _, new_text = self._upgrade(RemoveDeprecatedAliasKeyword(), text) + self.assertIn(new_text, acceptable_outputs) + + # It should get renamed and reordered even if it's last + text = "g(a, b, c=c, kw1_alias=x)\n" + acceptable_outputs = [ + "g(a, b, kw1=x, c=c)\n", + "g(a, b, c=c, kw1=x)\n", + "g(a=a, b=b, kw1=x, c=c)\n", + "g(a=a, b=b, c=c, kw1=x)\n", + ] + _, new_text = self._upgrade(RemoveDeprecatedAliasKeyword(), text) + self.assertIn(new_text, acceptable_outputs) + + def testRemoveDeprecatedKeywordAndReorder2(self): + """Same as testRemoveDeprecatedKeywordAndReorder but on g2 (more args).""" + text = "g2(a, b, kw1=x, c=c, d=d)\n" + acceptable_outputs = [ + "g2(a, b, c=c, d=d, kw1=x)\n", + "g2(a=a, b=b, kw1=x, c=c, d=d)\n", + ] + _, new_text = self._upgrade(RemoveDeprecatedAliasAndReorderRest(), text) + self.assertIn(new_text, acceptable_outputs) + + # Keywords are reordered, so we should reorder arguments too + text = "g2(a, b, x, c, d)\n" + # Don't accept an output which doesn't reorder c and d + acceptable_outputs = [ + "g2(a, b, c, d, x)\n", + "g2(a=a, b=b, kw1=x, c=c, d=d)\n", + ] + _, new_text = self._upgrade(RemoveDeprecatedAliasAndReorderRest(), text) + self.assertIn(new_text, acceptable_outputs) + + # If we used the alias, it should get renamed + text = "g2(a, b, kw1_alias=x, c=c, d=d)\n" + acceptable_outputs = [ + "g2(a, b, kw1=x, c=c, d=d)\n", + "g2(a, b, c=c, d=d, kw1=x)\n", + "g2(a=a, b=b, kw1=x, c=c, d=d)\n", + "g2(a=a, b=b, c=c, d=d, kw1=x)\n", + ] + _, new_text = self._upgrade(RemoveDeprecatedAliasKeyword(), text) + self.assertIn(new_text, acceptable_outputs) + + # It should get renamed and reordered even if it's not in order + text = "g2(a, b, d=d, c=c, kw1_alias=x)\n" + acceptable_outputs = [ + "g2(a, b, kw1=x, c=c, d=d)\n", + "g2(a, b, c=c, d=d, kw1=x)\n", + "g2(a, b, d=d, c=c, kw1=x)\n", + "g2(a=a, b=b, kw1=x, c=c, d=d)\n", + "g2(a=a, b=b, c=c, d=d, kw1=x)\n", + "g2(a=a, b=b, d=d, c=c, kw1=x)\n", + ] + _, new_text = self._upgrade(RemoveDeprecatedAliasKeyword(), text) + self.assertIn(new_text, acceptable_outputs) + + def testRemoveMultipleKeywords(self): + """Remove multiple keywords at once.""" + # Not using deprecated keywords -> no rename + text = "h(a, kw1=x, kw2=y)\n" + _, new_text = self._upgrade(RemoveMultipleKeywordArguments(), text) + self.assertEqual(new_text, text) + + # Using positional arguments (in proper order) -> no change + text = "h(a, x, y)\n" + _, new_text = self._upgrade(RemoveMultipleKeywordArguments(), text) + self.assertEqual(new_text, text) + + # Use only the old names, in order + text = "h(a, kw1_alias=x, kw2_alias=y)\n" + acceptable_outputs = [ + "h(a, x, y)\n", + "h(a, kw1=x, kw2=y)\n", + "h(a=a, kw1=x, kw2=y)\n", + "h(a, kw2=y, kw1=x)\n", + "h(a=a, kw2=y, kw1=x)\n", + ] + _, new_text = self._upgrade(RemoveMultipleKeywordArguments(), text) + self.assertIn(new_text, acceptable_outputs) + + # Use only the old names, in reverse order, should give one of same outputs + text = "h(a, kw2_alias=y, kw1_alias=x)\n" + _, new_text = self._upgrade(RemoveMultipleKeywordArguments(), text) + self.assertIn(new_text, acceptable_outputs) + + # Mix old and new names + text = "h(a, kw1=x, kw2_alias=y)\n" + _, new_text = self._upgrade(RemoveMultipleKeywordArguments(), text) + self.assertIn(new_text, acceptable_outputs) + + def testUnrestrictedFunctionWarnings(self): + class FooWarningSpec(NoUpdateSpec): + """Usages of function attribute foo() prints out a warning.""" + + def __init__(self): + NoUpdateSpec.__init__(self) + self.unrestricted_function_warnings = {"foo": "not good"} + texts = ["object.foo()", "get_object().foo()", + "get_object().foo()", "object.foo().bar()"] + for text in texts: + (_, report, _), _ = self._upgrade(FooWarningSpec(), text) + self.assertIn("not good", report) + + # Note that foo() won't result in a warning, because in this case foo is + # not an attribute, but a name. + false_alarms = ["foo", "foo()", "foo.bar()", "obj.run_foo()", "obj.foo"] + for text in false_alarms: + (_, report, _), _ = self._upgrade(FooWarningSpec(), text) + self.assertNotIn("not good", report) + + +if __name__ == "__main__": + test_lib.main() diff --git a/tensorflow/tools/compatibility/renames_v2.py b/tensorflow/tools/compatibility/renames_v2.py index 5ea2fbcc4c..78839e36ba 100644 --- a/tensorflow/tools/compatibility/renames_v2.py +++ b/tensorflow/tools/compatibility/renames_v2.py @@ -28,6 +28,10 @@ renames = { 'tf.AUTO_REUSE': 'tf.compat.v1.AUTO_REUSE', 'tf.COMPILER_VERSION': 'tf.version.COMPILER_VERSION', 'tf.CXX11_ABI_FLAG': 'tf.sysconfig.CXX11_ABI_FLAG', + 'tf.ConditionalAccumulator': 'tf.compat.v1.ConditionalAccumulator', + 'tf.ConditionalAccumulatorBase': 'tf.compat.v1.ConditionalAccumulatorBase', + 'tf.DeviceSpec': 'tf.compat.v1.DeviceSpec', + 'tf.Dimension': 'tf.compat.v1.Dimension', 'tf.FixedLenFeature': 'tf.io.FixedLenFeature', 'tf.FixedLenSequenceFeature': 'tf.io.FixedLenSequenceFeature', 'tf.FixedLengthRecordReader': 'tf.compat.v1.FixedLengthRecordReader', @@ -35,6 +39,7 @@ renames = { 'tf.GRAPH_DEF_VERSION': 'tf.version.GRAPH_DEF_VERSION', 'tf.GRAPH_DEF_VERSION_MIN_CONSUMER': 'tf.version.GRAPH_DEF_VERSION_MIN_CONSUMER', 'tf.GRAPH_DEF_VERSION_MIN_PRODUCER': 'tf.version.GRAPH_DEF_VERSION_MIN_PRODUCER', + 'tf.GraphKeys': 'tf.compat.v1.GraphKeys', 'tf.IdentityReader': 'tf.compat.v1.IdentityReader', 'tf.InteractiveSession': 'tf.compat.v1.InteractiveSession', 'tf.LMDBReader': 'tf.compat.v1.LMDBReader', @@ -53,12 +58,10 @@ renames = { 'tf.SparseConditionalAccumulator': 'tf.sparse.SparseConditionalAccumulator', 'tf.SparseFeature': 'tf.io.SparseFeature', 'tf.TFRecordReader': 'tf.compat.v1.TFRecordReader', - 'tf.TensorShape': 'tf.compat.v1.TensorShape', + 'tf.TensorInfo': 'tf.compat.v1.TensorInfo', 'tf.TextLineReader': 'tf.compat.v1.TextLineReader', 'tf.VERSION': 'tf.version.VERSION', 'tf.VarLenFeature': 'tf.io.VarLenFeature', - 'tf.Variable': 'tf.compat.v1.Variable', - 'tf.VariableAggregation': 'tf.compat.v1.VariableAggregation', 'tf.VariableScope': 'tf.compat.v1.VariableScope', 'tf.WholeFileReader': 'tf.compat.v1.WholeFileReader', 'tf.accumulate_n': 'tf.math.accumulate_n', @@ -67,59 +70,67 @@ renames = { 'tf.add_to_collections': 'tf.compat.v1.add_to_collections', 'tf.all_variables': 'tf.compat.v1.all_variables', 'tf.angle': 'tf.math.angle', - 'tf.argmax': 'tf.compat.v1.argmax', - 'tf.argmin': 'tf.compat.v1.argmin', - 'tf.assert_greater_equal': 'tf.debugging.assert_greater_equal', - 'tf.assert_integer': 'tf.debugging.assert_integer', - 'tf.assert_less_equal': 'tf.debugging.assert_less_equal', - 'tf.assert_near': 'tf.debugging.assert_near', - 'tf.assert_negative': 'tf.debugging.assert_negative', - 'tf.assert_non_negative': 'tf.debugging.assert_non_negative', - 'tf.assert_non_positive': 'tf.debugging.assert_non_positive', - 'tf.assert_none_equal': 'tf.debugging.assert_none_equal', - 'tf.assert_positive': 'tf.debugging.assert_positive', + 'tf.app.run': 'tf.compat.v1.app.run', + 'tf.arg_max': 'tf.compat.v1.arg_max', + 'tf.arg_min': 'tf.compat.v1.arg_min', + 'tf.assert_greater_equal': 'tf.compat.v1.assert_greater_equal', + 'tf.assert_integer': 'tf.compat.v1.assert_integer', + 'tf.assert_less_equal': 'tf.compat.v1.assert_less_equal', + 'tf.assert_near': 'tf.compat.v1.assert_near', + 'tf.assert_negative': 'tf.compat.v1.assert_negative', + 'tf.assert_non_negative': 'tf.compat.v1.assert_non_negative', + 'tf.assert_non_positive': 'tf.compat.v1.assert_non_positive', + 'tf.assert_none_equal': 'tf.compat.v1.assert_none_equal', + 'tf.assert_positive': 'tf.compat.v1.assert_positive', 'tf.assert_proper_iterable': 'tf.debugging.assert_proper_iterable', - 'tf.assert_rank_at_least': 'tf.debugging.assert_rank_at_least', - 'tf.assert_rank_in': 'tf.debugging.assert_rank_in', + 'tf.assert_rank_at_least': 'tf.compat.v1.assert_rank_at_least', + 'tf.assert_rank_in': 'tf.compat.v1.assert_rank_in', 'tf.assert_same_float_dtype': 'tf.debugging.assert_same_float_dtype', - 'tf.assert_scalar': 'tf.debugging.assert_scalar', - 'tf.assert_type': 'tf.debugging.assert_type', + 'tf.assert_scalar': 'tf.compat.v1.assert_scalar', + 'tf.assert_type': 'tf.compat.v1.assert_type', 'tf.assert_variables_initialized': 'tf.compat.v1.assert_variables_initialized', 'tf.assign': 'tf.compat.v1.assign', 'tf.assign_add': 'tf.compat.v1.assign_add', 'tf.assign_sub': 'tf.compat.v1.assign_sub', 'tf.betainc': 'tf.math.betainc', - 'tf.bincount': 'tf.math.bincount', 'tf.ceil': 'tf.math.ceil', 'tf.check_numerics': 'tf.debugging.check_numerics', 'tf.cholesky': 'tf.linalg.cholesky', 'tf.cholesky_solve': 'tf.linalg.cholesky_solve', + 'tf.clip_by_average_norm': 'tf.compat.v1.clip_by_average_norm', 'tf.colocate_with': 'tf.compat.v1.colocate_with', - 'tf.confusion_matrix': 'tf.math.confusion_matrix', 'tf.conj': 'tf.math.conj', 'tf.container': 'tf.compat.v1.container', - 'tf.convert_to_tensor': 'tf.compat.v1.convert_to_tensor', 'tf.convert_to_tensor_or_indexed_slices': 'tf.compat.v1.convert_to_tensor_or_indexed_slices', 'tf.convert_to_tensor_or_sparse_tensor': 'tf.compat.v1.convert_to_tensor_or_sparse_tensor', + 'tf.count_nonzero': 'tf.compat.v1.count_nonzero', 'tf.count_up_to': 'tf.compat.v1.count_up_to', + 'tf.create_partitioned_variables': 'tf.compat.v1.create_partitioned_variables', 'tf.cross': 'tf.linalg.cross', 'tf.cumprod': 'tf.math.cumprod', + 'tf.data.make_initializable_iterator': 'tf.compat.v1.data.make_initializable_iterator', + 'tf.data.make_one_shot_iterator': 'tf.compat.v1.data.make_one_shot_iterator', + 'tf.debugging.is_finite': 'tf.math.is_finite', + 'tf.debugging.is_inf': 'tf.math.is_inf', + 'tf.debugging.is_nan': 'tf.math.is_nan', + 'tf.debugging.is_non_decreasing': 'tf.math.is_non_decreasing', + 'tf.debugging.is_strictly_increasing': 'tf.math.is_strictly_increasing', 'tf.decode_base64': 'tf.io.decode_base64', 'tf.decode_compressed': 'tf.io.decode_compressed', - 'tf.decode_csv': 'tf.io.decode_csv', 'tf.decode_json_example': 'tf.io.decode_json_example', 'tf.decode_raw': 'tf.io.decode_raw', 'tf.delete_session_tensor': 'tf.compat.v1.delete_session_tensor', 'tf.depth_to_space': 'tf.nn.depth_to_space', 'tf.dequantize': 'tf.quantization.dequantize', 'tf.deserialize_many_sparse': 'tf.io.deserialize_many_sparse', - 'tf.device': 'tf.compat.v1.device', 'tf.diag': 'tf.linalg.tensor_diag', 'tf.diag_part': 'tf.linalg.tensor_diag_part', 'tf.digamma': 'tf.math.digamma', 'tf.dimension_at_index': 'tf.compat.v1.dimension_at_index', 'tf.dimension_value': 'tf.compat.v1.dimension_value', + 'tf.disable_eager_execution': 'tf.compat.v1.disable_eager_execution', 'tf.disable_resource_variables': 'tf.compat.v1.disable_resource_variables', + 'tf.disable_v2_behavior': 'tf.compat.v1.disable_v2_behavior', 'tf.disable_v2_tensorshape': 'tf.compat.v1.disable_v2_tensorshape', 'tf.distributions.Bernoulli': 'tf.compat.v1.distributions.Bernoulli', 'tf.distributions.Beta': 'tf.compat.v1.distributions.Beta', @@ -139,30 +150,41 @@ renames = { 'tf.distributions.StudentT': 'tf.compat.v1.distributions.StudentT', 'tf.distributions.Uniform': 'tf.compat.v1.distributions.Uniform', 'tf.distributions.kl_divergence': 'tf.compat.v1.distributions.kl_divergence', + 'tf.div': 'tf.compat.v1.div', + 'tf.enable_eager_execution': 'tf.compat.v1.enable_eager_execution', 'tf.enable_resource_variables': 'tf.compat.v1.enable_resource_variables', + 'tf.enable_v2_behavior': 'tf.compat.v1.enable_v2_behavior', 'tf.enable_v2_tensorshape': 'tf.compat.v1.enable_v2_tensorshape', 'tf.encode_base64': 'tf.io.encode_base64', 'tf.erf': 'tf.math.erf', 'tf.erfc': 'tf.math.erfc', 'tf.expm1': 'tf.math.expm1', - 'tf.extract_image_patches': 'tf.image.extract_image_patches', 'tf.fake_quant_with_min_max_args': 'tf.quantization.fake_quant_with_min_max_args', 'tf.fake_quant_with_min_max_args_gradient': 'tf.quantization.fake_quant_with_min_max_args_gradient', 'tf.fake_quant_with_min_max_vars': 'tf.quantization.fake_quant_with_min_max_vars', 'tf.fake_quant_with_min_max_vars_gradient': 'tf.quantization.fake_quant_with_min_max_vars_gradient', 'tf.fake_quant_with_min_max_vars_per_channel': 'tf.quantization.fake_quant_with_min_max_vars_per_channel', 'tf.fake_quant_with_min_max_vars_per_channel_gradient': 'tf.quantization.fake_quant_with_min_max_vars_per_channel_gradient', + 'tf.feature_column.input_layer': 'tf.compat.v1.feature_column.input_layer', + 'tf.feature_column.linear_model': 'tf.compat.v1.feature_column.linear_model', 'tf.fft': 'tf.signal.fft', 'tf.fft2d': 'tf.signal.fft2d', 'tf.fft3d': 'tf.signal.fft3d', + 'tf.fixed_size_partitioner': 'tf.compat.v1.fixed_size_partitioner', 'tf.floordiv': 'tf.math.floordiv', + 'tf.get_collection': 'tf.compat.v1.get_collection', + 'tf.get_collection_ref': 'tf.compat.v1.get_collection_ref', + 'tf.get_default_graph': 'tf.compat.v1.get_default_graph', 'tf.get_default_session': 'tf.compat.v1.get_default_session', 'tf.get_local_variable': 'tf.compat.v1.get_local_variable', - 'tf.get_seed': 'tf.random.get_seed', + 'tf.get_seed': 'tf.compat.v1.get_seed', 'tf.get_session_handle': 'tf.compat.v1.get_session_handle', 'tf.get_session_tensor': 'tf.compat.v1.get_session_tensor', 'tf.get_variable': 'tf.compat.v1.get_variable', 'tf.get_variable_scope': 'tf.compat.v1.get_variable_scope', + 'tf.gfile.FastGFile': 'tf.compat.v1.gfile.FastGFile', + 'tf.gfile.GFile': 'tf.compat.v1.gfile.GFile', + 'tf.gfile.Open': 'tf.compat.v1.gfile.Open', 'tf.global_norm': 'tf.linalg.global_norm', 'tf.global_variables': 'tf.compat.v1.global_variables', 'tf.global_variables_initializer': 'tf.compat.v1.global_variables_initializer', @@ -178,6 +200,12 @@ renames = { 'tf.igamma': 'tf.math.igamma', 'tf.igammac': 'tf.math.igammac', 'tf.imag': 'tf.math.imag', + 'tf.image.resize_area': 'tf.compat.v1.image.resize_area', + 'tf.image.resize_bicubic': 'tf.compat.v1.image.resize_bicubic', + 'tf.image.resize_bilinear': 'tf.compat.v1.image.resize_bilinear', + 'tf.image.resize_images': 'tf.compat.v1.image.resize_images', + 'tf.image.resize_nearest_neighbor': 'tf.compat.v1.image.resize_nearest_neighbor', + 'tf.image.transpose_image': 'tf.compat.v1.image.transpose_image', 'tf.initialize_all_tables': 'tf.compat.v1.initialize_all_tables', 'tf.initialize_all_variables': 'tf.compat.v1.initialize_all_variables', 'tf.initialize_local_variables': 'tf.compat.v1.initialize_local_variables', @@ -187,12 +215,13 @@ renames = { 'tf.initializers.tables_initializer': 'tf.compat.v1.initializers.tables_initializer', 'tf.initializers.variables': 'tf.compat.v1.initializers.variables', 'tf.invert_permutation': 'tf.math.invert_permutation', - 'tf.is_finite': 'tf.debugging.is_finite', - 'tf.is_inf': 'tf.debugging.is_inf', - 'tf.is_nan': 'tf.debugging.is_nan', - 'tf.is_non_decreasing': 'tf.debugging.is_non_decreasing', + 'tf.io.tf_record_iterator': 'tf.compat.v1.io.tf_record_iterator', + 'tf.is_finite': 'tf.math.is_finite', + 'tf.is_inf': 'tf.math.is_inf', + 'tf.is_nan': 'tf.math.is_nan', + 'tf.is_non_decreasing': 'tf.math.is_non_decreasing', 'tf.is_numeric_tensor': 'tf.debugging.is_numeric_tensor', - 'tf.is_strictly_increasing': 'tf.debugging.is_strictly_increasing', + 'tf.is_strictly_increasing': 'tf.math.is_strictly_increasing', 'tf.is_variable_initialized': 'tf.compat.v1.is_variable_initialized', 'tf.keras.backend.get_session': 'tf.compat.v1.keras.backend.get_session', 'tf.layers.AveragePooling1D': 'tf.compat.v1.layers.AveragePooling1D', @@ -235,13 +264,46 @@ renames = { 'tf.layers.separable_conv2d': 'tf.compat.v1.layers.separable_conv2d', 'tf.lbeta': 'tf.math.lbeta', 'tf.lgamma': 'tf.math.lgamma', + 'tf.lin_space': 'tf.linspace', 'tf.local_variables': 'tf.compat.v1.local_variables', 'tf.local_variables_initializer': 'tf.compat.v1.local_variables_initializer', + 'tf.log': 'tf.math.log', + 'tf.log1p': 'tf.math.log1p', 'tf.log_sigmoid': 'tf.math.log_sigmoid', + 'tf.logging.DEBUG': 'tf.compat.v1.logging.DEBUG', + 'tf.logging.ERROR': 'tf.compat.v1.logging.ERROR', + 'tf.logging.FATAL': 'tf.compat.v1.logging.FATAL', + 'tf.logging.INFO': 'tf.compat.v1.logging.INFO', + 'tf.logging.TaskLevelStatusMessage': 'tf.compat.v1.logging.TaskLevelStatusMessage', + 'tf.logging.WARN': 'tf.compat.v1.logging.WARN', + 'tf.logging.debug': 'tf.compat.v1.logging.debug', + 'tf.logging.error': 'tf.compat.v1.logging.error', + 'tf.logging.fatal': 'tf.compat.v1.logging.fatal', + 'tf.logging.flush': 'tf.compat.v1.logging.flush', + 'tf.logging.get_verbosity': 'tf.compat.v1.logging.get_verbosity', + 'tf.logging.info': 'tf.compat.v1.logging.info', + 'tf.logging.log': 'tf.compat.v1.logging.log', + 'tf.logging.log_every_n': 'tf.compat.v1.logging.log_every_n', + 'tf.logging.log_first_n': 'tf.compat.v1.logging.log_first_n', + 'tf.logging.log_if': 'tf.compat.v1.logging.log_if', + 'tf.logging.set_verbosity': 'tf.compat.v1.logging.set_verbosity', + 'tf.logging.vlog': 'tf.compat.v1.logging.vlog', + 'tf.logging.warn': 'tf.compat.v1.logging.warn', + 'tf.logging.warning': 'tf.compat.v1.logging.warning', 'tf.logical_xor': 'tf.math.logical_xor', + 'tf.losses.absolute_difference': 'tf.compat.v1.losses.absolute_difference', + 'tf.losses.compute_weighted_loss': 'tf.compat.v1.losses.compute_weighted_loss', + 'tf.losses.cosine_distance': 'tf.compat.v1.losses.cosine_distance', + 'tf.losses.hinge_loss': 'tf.compat.v1.losses.hinge_loss', + 'tf.losses.huber_loss': 'tf.compat.v1.losses.huber_loss', + 'tf.losses.log_loss': 'tf.compat.v1.losses.log_loss', + 'tf.losses.mean_pairwise_squared_error': 'tf.compat.v1.losses.mean_pairwise_squared_error', + 'tf.losses.mean_squared_error': 'tf.compat.v1.losses.mean_squared_error', + 'tf.losses.sigmoid_cross_entropy': 'tf.compat.v1.losses.sigmoid_cross_entropy', + 'tf.losses.softmax_cross_entropy': 'tf.compat.v1.losses.softmax_cross_entropy', + 'tf.losses.sparse_softmax_cross_entropy': 'tf.compat.v1.losses.sparse_softmax_cross_entropy', 'tf.make_template': 'tf.compat.v1.make_template', 'tf.make_tensor_proto': 'tf.compat.v1.make_tensor_proto', - 'tf.manip.batch_to_space_nd': 'tf.batch_to_space_nd', 'tf.manip.gather_nd': 'tf.gather_nd', 'tf.manip.reshape': 'tf.reshape', 'tf.manip.reverse': 'tf.reverse', @@ -250,8 +312,6 @@ renames = { 'tf.manip.space_to_batch_nd': 'tf.space_to_batch_nd', 'tf.manip.tile': 'tf.tile', 'tf.matching_files': 'tf.io.matching_files', - 'tf.math.argmax': 'tf.compat.v1.math.argmax', - 'tf.math.argmin': 'tf.compat.v1.math.argmin', 'tf.matrix_band_part': 'tf.linalg.band_part', 'tf.matrix_determinant': 'tf.linalg.det', 'tf.matrix_diag': 'tf.linalg.diag', @@ -262,49 +322,105 @@ renames = { 'tf.matrix_solve_ls': 'tf.linalg.lstsq', 'tf.matrix_transpose': 'tf.linalg.transpose', 'tf.matrix_triangular_solve': 'tf.linalg.triangular_solve', + 'tf.metrics.accuracy': 'tf.compat.v1.metrics.accuracy', + 'tf.metrics.auc': 'tf.compat.v1.metrics.auc', + 'tf.metrics.average_precision_at_k': 'tf.compat.v1.metrics.average_precision_at_k', + 'tf.metrics.false_negatives': 'tf.compat.v1.metrics.false_negatives', + 'tf.metrics.false_negatives_at_thresholds': 'tf.compat.v1.metrics.false_negatives_at_thresholds', + 'tf.metrics.false_positives': 'tf.compat.v1.metrics.false_positives', + 'tf.metrics.false_positives_at_thresholds': 'tf.compat.v1.metrics.false_positives_at_thresholds', + 'tf.metrics.mean': 'tf.compat.v1.metrics.mean', + 'tf.metrics.mean_absolute_error': 'tf.compat.v1.metrics.mean_absolute_error', + 'tf.metrics.mean_cosine_distance': 'tf.compat.v1.metrics.mean_cosine_distance', + 'tf.metrics.mean_iou': 'tf.compat.v1.metrics.mean_iou', + 'tf.metrics.mean_per_class_accuracy': 'tf.compat.v1.metrics.mean_per_class_accuracy', + 'tf.metrics.mean_relative_error': 'tf.compat.v1.metrics.mean_relative_error', + 'tf.metrics.mean_squared_error': 'tf.compat.v1.metrics.mean_squared_error', + 'tf.metrics.mean_tensor': 'tf.compat.v1.metrics.mean_tensor', + 'tf.metrics.percentage_below': 'tf.compat.v1.metrics.percentage_below', + 'tf.metrics.precision': 'tf.compat.v1.metrics.precision', + 'tf.metrics.precision_at_k': 'tf.compat.v1.metrics.precision_at_k', + 'tf.metrics.precision_at_thresholds': 'tf.compat.v1.metrics.precision_at_thresholds', + 'tf.metrics.precision_at_top_k': 'tf.compat.v1.metrics.precision_at_top_k', + 'tf.metrics.recall': 'tf.compat.v1.metrics.recall', + 'tf.metrics.recall_at_k': 'tf.compat.v1.metrics.recall_at_k', + 'tf.metrics.recall_at_thresholds': 'tf.compat.v1.metrics.recall_at_thresholds', + 'tf.metrics.recall_at_top_k': 'tf.compat.v1.metrics.recall_at_top_k', + 'tf.metrics.root_mean_squared_error': 'tf.compat.v1.metrics.root_mean_squared_error', + 'tf.metrics.sensitivity_at_specificity': 'tf.compat.v1.metrics.sensitivity_at_specificity', + 'tf.metrics.sparse_average_precision_at_k': 'tf.compat.v1.metrics.sparse_average_precision_at_k', + 'tf.metrics.sparse_precision_at_k': 'tf.compat.v1.metrics.sparse_precision_at_k', + 'tf.metrics.specificity_at_sensitivity': 'tf.compat.v1.metrics.specificity_at_sensitivity', + 'tf.metrics.true_negatives': 'tf.compat.v1.metrics.true_negatives', + 'tf.metrics.true_negatives_at_thresholds': 'tf.compat.v1.metrics.true_negatives_at_thresholds', + 'tf.metrics.true_positives': 'tf.compat.v1.metrics.true_positives', + 'tf.metrics.true_positives_at_thresholds': 'tf.compat.v1.metrics.true_positives_at_thresholds', + 'tf.min_max_variable_partitioner': 'tf.compat.v1.min_max_variable_partitioner', 'tf.model_variables': 'tf.compat.v1.model_variables', 'tf.moving_average_variables': 'tf.compat.v1.moving_average_variables', - 'tf.nn.ctc_beam_search_decoder': 'tf.compat.v1.nn.ctc_beam_search_decoder', + 'tf.nn.bidirectional_dynamic_rnn': 'tf.compat.v1.nn.bidirectional_dynamic_rnn', + 'tf.nn.conv3d_backprop_filter_v2': 'tf.nn.conv3d_backprop_filter', 'tf.nn.ctc_beam_search_decoder_v2': 'tf.nn.ctc_beam_search_decoder', + 'tf.nn.ctc_loss_v2': 'tf.nn.ctc_loss', + 'tf.nn.depthwise_conv2d_native': 'tf.compat.v1.nn.depthwise_conv2d_native', + 'tf.nn.depthwise_conv2d_native_backprop_filter': 'tf.nn.depthwise_conv2d_backprop_filter', + 'tf.nn.depthwise_conv2d_native_backprop_input': 'tf.nn.depthwise_conv2d_backprop_input', 'tf.nn.dynamic_rnn': 'tf.compat.v1.nn.dynamic_rnn', 'tf.nn.log_uniform_candidate_sampler': 'tf.random.log_uniform_candidate_sampler', + 'tf.nn.quantized_avg_pool': 'tf.compat.v1.nn.quantized_avg_pool', + 'tf.nn.quantized_conv2d': 'tf.compat.v1.nn.quantized_conv2d', + 'tf.nn.quantized_max_pool': 'tf.compat.v1.nn.quantized_max_pool', + 'tf.nn.quantized_relu_x': 'tf.compat.v1.nn.quantized_relu_x', 'tf.nn.raw_rnn': 'tf.compat.v1.nn.raw_rnn', 'tf.nn.rnn_cell.BasicLSTMCell': 'tf.compat.v1.nn.rnn_cell.BasicLSTMCell', 'tf.nn.rnn_cell.BasicRNNCell': 'tf.compat.v1.nn.rnn_cell.BasicRNNCell', 'tf.nn.rnn_cell.GRUCell': 'tf.compat.v1.nn.rnn_cell.GRUCell', 'tf.nn.rnn_cell.LSTMCell': 'tf.compat.v1.nn.rnn_cell.LSTMCell', - 'tf.nn.softmax_cross_entropy_with_logits': 'tf.compat.v1.nn.softmax_cross_entropy_with_logits', - 'tf.nn.softmax_cross_entropy_with_logits_v2': 'tf.nn.softmax_cross_entropy_with_logits', + 'tf.nn.rnn_cell.MultiRNNCell': 'tf.compat.v1.nn.rnn_cell.MultiRNNCell', + 'tf.nn.static_bidirectional_rnn': 'tf.compat.v1.nn.static_bidirectional_rnn', 'tf.nn.static_rnn': 'tf.compat.v1.nn.static_rnn', 'tf.nn.uniform_candidate_sampler': 'tf.random.uniform_candidate_sampler', + 'tf.nn.xw_plus_b': 'tf.compat.v1.nn.xw_plus_b', 'tf.op_scope': 'tf.compat.v1.op_scope', 'tf.orthogonal_initializer': 'tf.keras.initializers.Orthogonal', - 'tf.parse_example': 'tf.io.parse_example', - 'tf.parse_single_example': 'tf.io.parse_single_example', + 'tf.parse_example': 'tf.compat.v1.parse_example', + 'tf.parse_single_example': 'tf.compat.v1.parse_single_example', 'tf.parse_single_sequence_example': 'tf.io.parse_single_sequence_example', 'tf.parse_tensor': 'tf.io.parse_tensor', 'tf.placeholder': 'tf.compat.v1.placeholder', 'tf.placeholder_with_default': 'tf.compat.v1.placeholder_with_default', 'tf.polygamma': 'tf.math.polygamma', + 'tf.profiler.AdviceProto': 'tf.compat.v1.profiler.AdviceProto', + 'tf.profiler.GraphNodeProto': 'tf.compat.v1.profiler.GraphNodeProto', + 'tf.profiler.MultiGraphNodeProto': 'tf.compat.v1.profiler.MultiGraphNodeProto', + 'tf.profiler.OpLogProto': 'tf.compat.v1.profiler.OpLogProto', + 'tf.profiler.ProfileOptionBuilder': 'tf.compat.v1.profiler.ProfileOptionBuilder', + 'tf.profiler.Profiler': 'tf.compat.v1.profiler.Profiler', + 'tf.profiler.advise': 'tf.compat.v1.profiler.advise', + 'tf.profiler.profile': 'tf.compat.v1.profiler.profile', + 'tf.profiler.write_op_log': 'tf.compat.v1.profiler.write_op_log', + 'tf.py_func': 'tf.compat.v1.py_func', 'tf.python_io.TFRecordCompressionType': 'tf.io.TFRecordCompressionType', 'tf.python_io.TFRecordOptions': 'tf.io.TFRecordOptions', 'tf.python_io.TFRecordWriter': 'tf.io.TFRecordWriter', - 'tf.python_io.tf_record_iterator': 'tf.io.tf_record_iterator', + 'tf.python_io.tf_record_iterator': 'tf.compat.v1.python_io.tf_record_iterator', 'tf.qr': 'tf.linalg.qr', 'tf.quantize': 'tf.quantization.quantize', 'tf.quantized_concat': 'tf.quantization.quantized_concat', + 'tf.random.get_seed': 'tf.compat.v1.random.get_seed', + 'tf.random.set_random_seed': 'tf.compat.v1.random.set_random_seed', 'tf.random_crop': 'tf.image.random_crop', 'tf.random_gamma': 'tf.random.gamma', 'tf.random_normal': 'tf.random.normal', - 'tf.random_poisson': 'tf.random.poisson', + 'tf.random_poisson': 'tf.compat.v1.random_poisson', 'tf.random_shuffle': 'tf.random.shuffle', 'tf.random_uniform': 'tf.random.uniform', 'tf.read_file': 'tf.io.read_file', 'tf.real': 'tf.math.real', 'tf.reciprocal': 'tf.math.reciprocal', - 'tf.reduce_join': 'tf.strings.reduce_join', 'tf.regex_replace': 'tf.strings.regex_replace', 'tf.report_uninitialized_variables': 'tf.compat.v1.report_uninitialized_variables', + 'tf.reset_default_graph': 'tf.compat.v1.reset_default_graph', 'tf.resource_loader.get_data_files_path': 'tf.compat.v1.resource_loader.get_data_files_path', 'tf.resource_loader.get_path_to_datafile': 'tf.compat.v1.resource_loader.get_path_to_datafile', 'tf.resource_loader.get_root_dir_with_all_resources': 'tf.compat.v1.resource_loader.get_root_dir_with_all_resources', @@ -314,13 +430,15 @@ renames = { 'tf.rint': 'tf.math.rint', 'tf.rsqrt': 'tf.math.rsqrt', 'tf.saved_model.Builder': 'tf.compat.v1.saved_model.Builder', + 'tf.saved_model.LEGACY_INIT_OP_KEY': 'tf.compat.v1.saved_model.LEGACY_INIT_OP_KEY', + 'tf.saved_model.MAIN_OP_KEY': 'tf.compat.v1.saved_model.MAIN_OP_KEY', 'tf.saved_model.TRAINING': 'tf.saved_model.TRANING', 'tf.saved_model.build_tensor_info': 'tf.compat.v1.saved_model.build_tensor_info', 'tf.saved_model.builder.SavedModelBuilder': 'tf.compat.v1.saved_model.builder.SavedModelBuilder', 'tf.saved_model.constants.ASSETS_DIRECTORY': 'tf.saved_model.ASSETS_DIRECTORY', 'tf.saved_model.constants.ASSETS_KEY': 'tf.saved_model.ASSETS_KEY', - 'tf.saved_model.constants.LEGACY_INIT_OP_KEY': 'tf.saved_model.LEGACY_INIT_OP_KEY', - 'tf.saved_model.constants.MAIN_OP_KEY': 'tf.saved_model.MAIN_OP_KEY', + 'tf.saved_model.constants.LEGACY_INIT_OP_KEY': 'tf.compat.v1.saved_model.constants.LEGACY_INIT_OP_KEY', + 'tf.saved_model.constants.MAIN_OP_KEY': 'tf.compat.v1.saved_model.constants.MAIN_OP_KEY', 'tf.saved_model.constants.SAVED_MODEL_FILENAME_PB': 'tf.saved_model.SAVED_MODEL_FILENAME_PB', 'tf.saved_model.constants.SAVED_MODEL_FILENAME_PBTXT': 'tf.saved_model.SAVED_MODEL_FILENAME_PBTXT', 'tf.saved_model.constants.SAVED_MODEL_SCHEMA_VERSION': 'tf.saved_model.SAVED_MODEL_SCHEMA_VERSION', @@ -330,10 +448,11 @@ renames = { 'tf.saved_model.get_tensor_from_tensor_info': 'tf.compat.v1.saved_model.get_tensor_from_tensor_info', 'tf.saved_model.load': 'tf.compat.v1.saved_model.load', 'tf.saved_model.loader.load': 'tf.compat.v1.saved_model.loader.load', - 'tf.saved_model.loader.maybe_saved_model_directory': 'tf.saved_model.maybe_saved_model_directory', + 'tf.saved_model.loader.maybe_saved_model_directory': 'tf.compat.v1.saved_model.loader.maybe_saved_model_directory', 'tf.saved_model.main_op.main_op': 'tf.compat.v1.saved_model.main_op.main_op', 'tf.saved_model.main_op.main_op_with_restore': 'tf.compat.v1.saved_model.main_op.main_op_with_restore', 'tf.saved_model.main_op_with_restore': 'tf.compat.v1.saved_model.main_op_with_restore', + 'tf.saved_model.maybe_saved_model_directory': 'tf.compat.v1.saved_model.maybe_saved_model_directory', 'tf.saved_model.signature_constants.CLASSIFY_INPUTS': 'tf.saved_model.CLASSIFY_INPUTS', 'tf.saved_model.signature_constants.CLASSIFY_METHOD_NAME': 'tf.saved_model.CLASSIFY_METHOD_NAME', 'tf.saved_model.signature_constants.CLASSIFY_OUTPUT_CLASSES': 'tf.saved_model.CLASSIFY_OUTPUT_CLASSES', @@ -370,51 +489,80 @@ renames = { 'tf.segment_sum': 'tf.math.segment_sum', 'tf.self_adjoint_eig': 'tf.linalg.eigh', 'tf.self_adjoint_eigvals': 'tf.linalg.eigvalsh', - 'tf.serialize_many_sparse': 'tf.io.serialize_many_sparse', - 'tf.serialize_sparse': 'tf.io.serialize_sparse', + 'tf.serialize_many_sparse': 'tf.compat.v1.serialize_many_sparse', + 'tf.serialize_sparse': 'tf.compat.v1.serialize_sparse', 'tf.serialize_tensor': 'tf.io.serialize_tensor', + 'tf.set_random_seed': 'tf.compat.v1.set_random_seed', 'tf.setdiff1d': 'tf.compat.v1.setdiff1d', + 'tf.sets.set_difference': 'tf.sets.difference', + 'tf.sets.set_intersection': 'tf.sets.intersection', + 'tf.sets.set_size': 'tf.sets.size', + 'tf.sets.set_union': 'tf.sets.union', 'tf.space_to_batch': 'tf.nn.space_to_batch', 'tf.space_to_depth': 'tf.nn.space_to_depth', + 'tf.sparse.matmul': 'tf.sparse.sparse_dense_matmul', + 'tf.sparse.merge': 'tf.compat.v1.sparse.merge', 'tf.sparse.placeholder': 'tf.compat.v1.sparse.placeholder', - 'tf.sparse_add': 'tf.sparse.add', + 'tf.sparse.reduce_max_sparse': 'tf.compat.v1.sparse.reduce_max_sparse', + 'tf.sparse.reduce_sum_sparse': 'tf.compat.v1.sparse.reduce_sum_sparse', 'tf.sparse_fill_empty_rows': 'tf.sparse.fill_empty_rows', 'tf.sparse_mask': 'tf.sparse.mask', - 'tf.sparse_matmul': 'tf.compat.v1.sparse_matmul', 'tf.sparse_maximum': 'tf.sparse.maximum', - 'tf.sparse_merge': 'tf.sparse.merge', + 'tf.sparse_merge': 'tf.compat.v1.sparse_merge', 'tf.sparse_minimum': 'tf.sparse.minimum', 'tf.sparse_placeholder': 'tf.compat.v1.sparse_placeholder', + 'tf.sparse_reduce_max': 'tf.compat.v1.sparse_reduce_max', + 'tf.sparse_reduce_max_sparse': 'tf.compat.v1.sparse_reduce_max_sparse', + 'tf.sparse_reduce_sum': 'tf.compat.v1.sparse_reduce_sum', + 'tf.sparse_reduce_sum_sparse': 'tf.compat.v1.sparse_reduce_sum_sparse', 'tf.sparse_reorder': 'tf.sparse.reorder', 'tf.sparse_reset_shape': 'tf.sparse.reset_shape', 'tf.sparse_reshape': 'tf.sparse.reshape', 'tf.sparse_retain': 'tf.sparse.retain', - 'tf.sparse_segment_mean': 'tf.sparse.segment_mean', - 'tf.sparse_segment_sqrt_n': 'tf.sparse.segment_sqrt_n', - 'tf.sparse_segment_sum': 'tf.sparse.segment_sum', + 'tf.sparse_segment_mean': 'tf.compat.v1.sparse_segment_mean', + 'tf.sparse_segment_sqrt_n': 'tf.compat.v1.sparse_segment_sqrt_n', + 'tf.sparse_segment_sum': 'tf.compat.v1.sparse_segment_sum', 'tf.sparse_slice': 'tf.sparse.slice', 'tf.sparse_softmax': 'tf.sparse.softmax', - 'tf.sparse_tensor_dense_matmul': 'tf.sparse.matmul', + 'tf.sparse_tensor_dense_matmul': 'tf.sparse.sparse_dense_matmul', 'tf.sparse_tensor_to_dense': 'tf.sparse.to_dense', + 'tf.sparse_to_dense': 'tf.compat.v1.sparse_to_dense', 'tf.sparse_to_indicator': 'tf.sparse.to_indicator', 'tf.sparse_transpose': 'tf.sparse.transpose', + 'tf.spectral.dct': 'tf.signal.dct', 'tf.spectral.fft': 'tf.signal.fft', 'tf.spectral.fft2d': 'tf.signal.fft2d', 'tf.spectral.fft3d': 'tf.signal.fft3d', + 'tf.spectral.idct': 'tf.signal.idct', 'tf.spectral.ifft': 'tf.signal.ifft', 'tf.spectral.ifft2d': 'tf.signal.ifft2d', 'tf.spectral.ifft3d': 'tf.signal.ifft3d', + 'tf.spectral.irfft': 'tf.signal.irfft', + 'tf.spectral.irfft2d': 'tf.signal.irfft2d', + 'tf.spectral.irfft3d': 'tf.signal.irfft3d', + 'tf.spectral.rfft': 'tf.signal.rfft', + 'tf.spectral.rfft2d': 'tf.signal.rfft2d', + 'tf.spectral.rfft3d': 'tf.signal.rfft3d', 'tf.squared_difference': 'tf.math.squared_difference', 'tf.string_join': 'tf.strings.join', 'tf.string_strip': 'tf.strings.strip', - 'tf.string_to_hash_bucket': 'tf.strings.to_hash_bucket', 'tf.string_to_hash_bucket_fast': 'tf.strings.to_hash_bucket_fast', 'tf.string_to_hash_bucket_strong': 'tf.strings.to_hash_bucket_strong', - 'tf.string_to_number': 'tf.strings.to_number', + 'tf.summary.audio': 'tf.compat.v1.summary.audio', + 'tf.summary.get_summary_description': 'tf.compat.v1.summary.get_summary_description', + 'tf.summary.histogram': 'tf.compat.v1.summary.histogram', + 'tf.summary.image': 'tf.compat.v1.summary.image', + 'tf.summary.merge': 'tf.compat.v1.summary.merge', + 'tf.summary.merge_all': 'tf.compat.v1.summary.merge_all', + 'tf.summary.scalar': 'tf.compat.v1.summary.scalar', + 'tf.summary.tensor_summary': 'tf.compat.v1.summary.tensor_summary', + 'tf.summary.text': 'tf.compat.v1.summary.text', 'tf.svd': 'tf.linalg.svd', 'tf.tables_initializer': 'tf.compat.v1.tables_initializer', + 'tf.test.compute_gradient': 'tf.compat.v1.test.compute_gradient', 'tf.test.compute_gradient_error': 'tf.compat.v1.test.compute_gradient_error', 'tf.test.get_temp_dir': 'tf.compat.v1.test.get_temp_dir', + 'tf.test.mock': 'tf.compat.v1.test.mock', 'tf.test.test_src_dir_path': 'tf.compat.v1.test.test_src_dir_path', 'tf.to_bfloat16': 'tf.compat.v1.to_bfloat16', 'tf.to_complex128': 'tf.compat.v1.to_complex128', @@ -424,22 +572,47 @@ renames = { 'tf.to_int32': 'tf.compat.v1.to_int32', 'tf.to_int64': 'tf.compat.v1.to_int64', 'tf.trace': 'tf.linalg.trace', + 'tf.train.AdadeltaOptimizer': 'tf.compat.v1.train.AdadeltaOptimizer', + 'tf.train.AdagradDAOptimizer': 'tf.compat.v1.train.AdagradDAOptimizer', + 'tf.train.AdagradOptimizer': 'tf.compat.v1.train.AdagradOptimizer', + 'tf.train.AdamOptimizer': 'tf.compat.v1.train.AdamOptimizer', + 'tf.train.CheckpointSaverListener': 'tf.compat.v1.train.CheckpointSaverListener', + 'tf.train.ChiefSessionCreator': 'tf.compat.v1.train.ChiefSessionCreator', + 'tf.train.FtrlOptimizer': 'tf.compat.v1.train.FtrlOptimizer', + 'tf.train.GradientDescentOptimizer': 'tf.compat.v1.train.GradientDescentOptimizer', + 'tf.train.LooperThread': 'tf.compat.v1.train.LooperThread', + 'tf.train.MomentumOptimizer': 'tf.compat.v1.train.MomentumOptimizer', + 'tf.train.MonitoredSession': 'tf.compat.v1.train.MonitoredSession', 'tf.train.MonitoredTrainingSession': 'tf.compat.v1.train.MonitoredTrainingSession', + 'tf.train.NanLossDuringTrainingError': 'tf.compat.v1.train.NanLossDuringTrainingError', + 'tf.train.NewCheckpointReader': 'tf.compat.v1.train.NewCheckpointReader', + 'tf.train.Optimizer': 'tf.compat.v1.train.Optimizer', + 'tf.train.ProfilerHook': 'tf.compat.v1.train.ProfilerHook', + 'tf.train.ProximalAdagradOptimizer': 'tf.compat.v1.train.ProximalAdagradOptimizer', 'tf.train.QueueRunner': 'tf.compat.v1.train.QueueRunner', + 'tf.train.RMSPropOptimizer': 'tf.compat.v1.train.RMSPropOptimizer', 'tf.train.Saver': 'tf.compat.v1.train.Saver', 'tf.train.SaverDef': 'tf.compat.v1.train.SaverDef', + 'tf.train.Scaffold': 'tf.compat.v1.train.Scaffold', + 'tf.train.SecondOrStepTimer': 'tf.compat.v1.train.SecondOrStepTimer', + 'tf.train.SessionCreator': 'tf.compat.v1.train.SessionCreator', + 'tf.train.SessionManager': 'tf.compat.v1.train.SessionManager', + 'tf.train.SessionRunArgs': 'tf.compat.v1.train.SessionRunArgs', + 'tf.train.SessionRunContext': 'tf.compat.v1.train.SessionRunContext', + 'tf.train.SessionRunValues': 'tf.compat.v1.train.SessionRunValues', + 'tf.train.SingularMonitoredSession': 'tf.compat.v1.train.SingularMonitoredSession', + 'tf.train.Supervisor': 'tf.compat.v1.train.Supervisor', 'tf.train.SyncReplicasOptimizer': 'tf.compat.v1.train.SyncReplicasOptimizer', + 'tf.train.VocabInfo': 'tf.estimator.VocabInfo', + 'tf.train.WorkerSessionCreator': 'tf.compat.v1.train.WorkerSessionCreator', 'tf.train.add_queue_runner': 'tf.compat.v1.train.add_queue_runner', 'tf.train.assert_global_step': 'tf.compat.v1.train.assert_global_step', 'tf.train.basic_train_loop': 'tf.compat.v1.train.basic_train_loop', 'tf.train.batch': 'tf.compat.v1.train.batch', 'tf.train.batch_join': 'tf.compat.v1.train.batch_join', 'tf.train.checkpoint_exists': 'tf.compat.v1.train.checkpoint_exists', - 'tf.train.cosine_decay': 'tf.compat.v1.train.cosine_decay', - 'tf.train.cosine_decay_restarts': 'tf.compat.v1.train.cosine_decay_restarts', 'tf.train.create_global_step': 'tf.compat.v1.train.create_global_step', 'tf.train.do_quantize_training_on_graphdef': 'tf.compat.v1.train.do_quantize_training_on_graphdef', - 'tf.train.exponential_decay': 'tf.compat.v1.train.exponential_decay', 'tf.train.export_meta_graph': 'tf.compat.v1.train.export_meta_graph', 'tf.train.generate_checkpoint_state_proto': 'tf.compat.v1.train.generate_checkpoint_state_proto', 'tf.train.get_checkpoint_mtimes': 'tf.compat.v1.train.get_checkpoint_mtimes', @@ -447,32 +620,31 @@ renames = { 'tf.train.get_or_create_global_step': 'tf.compat.v1.train.get_or_create_global_step', 'tf.train.global_step': 'tf.compat.v1.train.global_step', 'tf.train.import_meta_graph': 'tf.compat.v1.train.import_meta_graph', + 'tf.train.init_from_checkpoint': 'tf.compat.v1.train.init_from_checkpoint', 'tf.train.input_producer': 'tf.compat.v1.train.input_producer', - 'tf.train.inverse_time_decay': 'tf.compat.v1.train.inverse_time_decay', 'tf.train.limit_epochs': 'tf.compat.v1.train.limit_epochs', - 'tf.train.linear_cosine_decay': 'tf.compat.v1.train.linear_cosine_decay', 'tf.train.match_filenames_once': 'tf.io.match_filenames_once', 'tf.train.maybe_batch': 'tf.compat.v1.train.maybe_batch', 'tf.train.maybe_batch_join': 'tf.compat.v1.train.maybe_batch_join', 'tf.train.maybe_shuffle_batch': 'tf.compat.v1.train.maybe_shuffle_batch', 'tf.train.maybe_shuffle_batch_join': 'tf.compat.v1.train.maybe_shuffle_batch_join', - 'tf.train.natural_exp_decay': 'tf.compat.v1.train.natural_exp_decay', - 'tf.train.noisy_linear_cosine_decay': 'tf.compat.v1.train.noisy_linear_cosine_decay', 'tf.train.piecewise_constant': 'tf.compat.v1.train.piecewise_constant', - 'tf.train.polynomial_decay': 'tf.compat.v1.train.polynomial_decay', 'tf.train.queue_runner.QueueRunner': 'tf.compat.v1.train.queue_runner.QueueRunner', 'tf.train.queue_runner.add_queue_runner': 'tf.compat.v1.train.queue_runner.add_queue_runner', 'tf.train.queue_runner.start_queue_runners': 'tf.compat.v1.train.queue_runner.start_queue_runners', 'tf.train.range_input_producer': 'tf.compat.v1.train.range_input_producer', 'tf.train.remove_checkpoint': 'tf.compat.v1.train.remove_checkpoint', + 'tf.train.replica_device_setter': 'tf.compat.v1.train.replica_device_setter', 'tf.train.shuffle_batch': 'tf.compat.v1.train.shuffle_batch', 'tf.train.shuffle_batch_join': 'tf.compat.v1.train.shuffle_batch_join', 'tf.train.slice_input_producer': 'tf.compat.v1.train.slice_input_producer', 'tf.train.start_queue_runners': 'tf.compat.v1.train.start_queue_runners', 'tf.train.string_input_producer': 'tf.compat.v1.train.string_input_producer', 'tf.train.update_checkpoint_state': 'tf.compat.v1.train.update_checkpoint_state', + 'tf.train.warm_start': 'tf.compat.v1.train.warm_start', 'tf.train.write_graph': 'tf.io.write_graph', 'tf.trainable_variables': 'tf.compat.v1.trainable_variables', + 'tf.truncated_normal': 'tf.random.truncated_normal', 'tf.uniform_unit_scaling_initializer': 'tf.initializers.uniform_unit_scaling', 'tf.unsorted_segment_max': 'tf.math.unsorted_segment_max', 'tf.unsorted_segment_mean': 'tf.math.unsorted_segment_mean', @@ -480,12 +652,13 @@ renames = { 'tf.unsorted_segment_prod': 'tf.math.unsorted_segment_prod', 'tf.unsorted_segment_sqrt_n': 'tf.math.unsorted_segment_sqrt_n', 'tf.unsorted_segment_sum': 'tf.math.unsorted_segment_sum', - 'tf.variable_creator_scope': 'tf.compat.v1.variable_creator_scope', + 'tf.variable_axis_size_partitioner': 'tf.compat.v1.variable_axis_size_partitioner', 'tf.variable_op_scope': 'tf.compat.v1.variable_op_scope', 'tf.variable_scope': 'tf.compat.v1.variable_scope', 'tf.variables_initializer': 'tf.compat.v1.variables_initializer', 'tf.variance_scaling_initializer': 'tf.keras.initializers.VarianceScaling', - 'tf.verify_tensor_all_finite': 'tf.debugging.assert_all_finite', + 'tf.verify_tensor_all_finite': 'tf.compat.v1.verify_tensor_all_finite', + 'tf.wrap_function': 'tf.compat.v1.wrap_function', 'tf.write_file': 'tf.io.write_file', 'tf.zeta': 'tf.math.zeta' } diff --git a/tensorflow/tools/compatibility/testdata/test_file_v1_12.py b/tensorflow/tools/compatibility/testdata/test_file_v1_12.py new file mode 100644 index 0000000000..fd688781b0 --- /dev/null +++ b/tensorflow/tools/compatibility/testdata/test_file_v1_12.py @@ -0,0 +1,71 @@ +# Copyright 2018 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 tf upgrader.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +import tensorflow as tf +from tensorflow.python.framework import test_util +from tensorflow.python.platform import test as test_lib + + +class TestUpgrade(test_util.TensorFlowTestCase): + """Test various APIs that have been changed in 2.0.""" + + def setUp(self): + tf.enable_eager_execution() + + def testRenames(self): + with self.cached_session(): + self.assertAllClose(1.04719755, tf.acos(0.5)) + self.assertAllClose(0.5, tf.rsqrt(4.0)) + + def testSerializeSparseTensor(self): + sp_input = tf.SparseTensor( + indices=tf.constant([[1]], dtype=tf.int64), + values=tf.constant([2], dtype=tf.int64), + dense_shape=[2]) + + with self.cached_session(): + serialized_sp = tf.serialize_sparse(sp_input, 'serialize_name', tf.string) + self.assertEqual((3,), serialized_sp.shape) + self.assertTrue(serialized_sp[0].numpy()) # check non-empty + + def testSerializeManySparse(self): + sp_input = tf.SparseTensor( + indices=tf.constant([[0, 1]], dtype=tf.int64), + values=tf.constant([2], dtype=tf.int64), + dense_shape=[1, 2]) + + with self.cached_session(): + serialized_sp = tf.serialize_many_sparse( + sp_input, 'serialize_name', tf.string) + self.assertEqual((1, 3), serialized_sp.shape) + + def testArgMaxMin(self): + self.assertAllClose( + [1], + tf.argmax([[1, 3, 2]], name='abc', dimension=1)) + self.assertAllClose( + [0, 0, 0], + tf.argmax([[1, 3, 2]], dimension=0)) + self.assertAllClose( + [0], + tf.argmin([[1, 3, 2]], name='abc', dimension=1)) + + +if __name__ == "__main__": + test_lib.main() diff --git a/tensorflow/tools/compatibility/tf_upgrade_v2.py b/tensorflow/tools/compatibility/tf_upgrade_v2.py index 0df8b0f376..655e680d5b 100644 --- a/tensorflow/tools/compatibility/tf_upgrade_v2.py +++ b/tensorflow/tools/compatibility/tf_upgrade_v2.py @@ -18,8 +18,6 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -import argparse - from tensorflow.tools.compatibility import ast_edits from tensorflow.tools.compatibility import renames_v2 @@ -31,23 +29,452 @@ class TFAPIChangeSpec(ast_edits.APIChangeSpec): # Maps from a function name to a dictionary that describes how to # map from an old argument keyword to the new argument keyword. self.function_keyword_renames = { + "tf.argmin": { + "dimension": "axis", + }, + "tf.argmax": { + "dimension": "axis", + }, + "tf.image.crop_and_resize": { + "box_ind": "box_indices", + }, + "tf.image.extract_image_patches": { + "ksizes": "sizes", + }, + "tf.extract_image_patches": { + "ksizes": "sizes", + }, + "tf.expand_dims": { + "dim": "axis", + }, + "tf.batch_to_space": { + "block_size": "block_shape", + }, + "tf.constant": { + "verify_shape": "verify_shape_is_now_always_true", + }, "tf.convert_to_tensor": { "preferred_dtype": "dtype_hint" }, + "tf.nn.softmax_cross_entropy_with_logits_v2": { + "dim": "axis" + }, + "tf.linalg.l2_normalize": { + "dim": "axis", + }, + "tf.math.count_nonzero": { + "input_tensor": "input", + "keep_dims": "keepdims", + "reduction_indices": "axis", + }, + "tf.nn.erosion2d": { + "kernel": "filters", + "rates": "dilations", + }, + "tf.math.l2_normalize": { + "dim": "axis", + }, + "tf.math.log_softmax": { + "dim": "axis", + }, + "tf.math.softmax": { + "dim": "axis" + }, + "tf.nn.l2_normalize": { + "dim": "axis", + }, + "tf.nn.log_softmax": { + "dim": "axis", + }, + "tf.nn.moments": { + "keep_dims": "keepdims", + }, + "tf.nn.pool": { + "dilation_rate": "dilations" + }, + "tf.nn.separable_conv2d": { + "rate": "dilations" + }, + "tf.nn.softmax": { + "dim": "axis" + }, + "tf.nn.sufficient_statistics": { + "keep_dims": "keepdims" + }, + "tf.debugging.assert_all_finite": { + "t": "x", + "msg": "message", + }, + "tf.sparse.add": { + "thresh": "threshold", + }, + "tf.sparse_add": { + "thresh": "threshold", + }, + "tf.sparse.concat": { + "concat_dim": "axis", + }, + "tf.sparse_concat": { + "concat_dim": "axis", + }, + "tf.sparse.split": { + "split_dim": "axis", + }, + "tf.max_pool_with_argmax": { + "Targmax": "output_dtype", + }, + "tf.multinomial": { + "output_dtype": "dtype", + }, + "tf.random.multinomial": { + "output_dtype": "dtype", + }, + "tf.nn.batch_norm_with_global_normalization": { + "t": "input", + "m": "mean", + "v": "variance", + }, + "tf.nn.dilation2d": { + "filter": "filters", + "rates": "dilations", + }, + "tf.nn.conv3d": { + "filter": "filters" + }, + "tf.zeros_like": { + "tensor": "input", + }, + "tf.ones_like": { + "tensor": "input", + }, + "tf.nn.conv3d_transpose": { + "value": "input", + "filter": "filters", + }, + "tf.nn.convolution": { + "filter": "filters", + "dilation_rate": "dilations", + }, + "tf.gfile.Exists": { + "filename": "path", + }, + "tf.gfile.Remove": { + "filename": "path", + }, + "tf.gfile.Stat": { + "filename": "path", + }, + "tf.gfile.Glob": { + "filename": "pattern", + }, + "tf.gfile.MkDir": { + "dirname": "path", + }, + "tf.gfile.MakeDirs": { + "dirname": "path", + }, + "tf.gfile.DeleteRecursively": { + "dirname": "path", + }, + "tf.gfile.IsDirectory": { + "dirname": "path", + }, + "tf.gfile.ListDirectory": { + "dirname": "path", + }, + "tf.gfile.Copy": { + "oldpath": "src", + "newpath": "dst", + }, + "tf.gfile.Rename": { + "oldname": "src", + "newname": "dst", + }, + "tf.gfile.Walk": { + "in_order": "topdown", + }, + "tf.random.stateless_multinomial": { + "output_dtype": "dtype", + }, + "tf.string_to_number": { + "string_tensor": "input", + }, + "tf.strings.to_number": { + "string_tensor": "input", + }, + "tf.string_to_hash_bucket": { + "string_tensor": "input", + }, + "tf.strings.to_hash_bucket": { + "string_tensor": "input", + }, + "tf.reduce_all": { + "reduction_indices": "axis", + "keep_dims": "keepdims", + }, + "tf.math.reduce_all": { + "reduction_indices": "axis", + "keep_dims": "keepdims", + }, + "tf.reduce_any": { + "reduction_indices": "axis", + "keep_dims": "keepdims", + }, + "tf.math.reduce_any": { + "reduction_indices": "axis", + "keep_dims": "keepdims", + }, + "tf.reduce_min": { + "reduction_indices": "axis", + "keep_dims": "keepdims", + }, + "tf.math.reduce_min": { + "reduction_indices": "axis", + "keep_dims": "keepdims", + }, + "tf.reduce_max": { + "reduction_indices": "axis", + "keep_dims": "keepdims", + }, + "tf.math.reduce_max": { + "reduction_indices": "axis", + "keep_dims": "keepdims", + }, + "tf.reduce_sum": { + "reduction_indices": "axis", + "keep_dims": "keepdims", + }, + "tf.math.reduce_sum": { + "reduction_indices": "axis", + "keep_dims": "keepdims", + }, + "tf.reduce_mean": { + "reduction_indices": "axis", + "keep_dims": "keepdims", + }, + "tf.math.reduce_mean": { + "reduction_indices": "axis", + "keep_dims": "keepdims", + }, + "tf.reduce_prod": { + "reduction_indices": "axis", + "keep_dims": "keepdims", + }, + "tf.math.reduce_prod": { + "reduction_indices": "axis", + "keep_dims": "keepdims", + }, + "tf.reduce_logsumexp": { + "reduction_indices": "axis", + "keep_dims": "keepdims", + }, + "tf.math.reduce_logsumexp": { + "reduction_indices": "axis", + "keep_dims": "keepdims", + }, + "tf.reduce_join": { + "keep_dims": "keepdims", + "reduction_indices": "axis" + }, + "tf.strings.reduce_join": { + "keep_dims": "keepdims", + "reduction_indices": "axis" + }, + "tf.squeeze": { + "squeeze_dims": "axis", + }, } - # Mapping from function to the new name of the function - self.symbol_renames = renames_v2.renames # pylint: disable=line-too-long # Add additional renames not in renames_v2.py here. - self.symbol_renames.update({ - }) + # IMPORTANT: For the renames in here, if you also need to add to + # function_reorders or function_keyword_renames, use the OLD function name. + # These renames happen after the arguments have been processed. + self.manual_symbol_renames = { + "tf.batch_to_space_nd": + "tf.batch_to_space", + "tf.extract_image_patches": + "tf.image.extract_image_patches", + "tf.gfile.Copy": + "tf.io.gfile.copy", + "tf.gfile.DeleteRecursively": + "tf.io.gfile.rmtree", + "tf.gfile.Exists": + "tf.io.gfile.exists", + "tf.gfile.Glob": + "tf.io.gfile.glob", + "tf.gfile.IsDirectory": + "tf.io.gfile.isdir", + "tf.gfile.ListDirectory": + "tf.io.gfile.listdir", + "tf.gfile.MakeDirs": + "tf.io.gfile.makedirs", + "tf.gfile.MkDir": + "tf.io.gfile.mkdir", + "tf.gfile.Remove": + "tf.io.gfile.remove", + "tf.gfile.Rename": + "tf.io.gfile.rename", + "tf.gfile.Stat": + "tf.io.gfile.stat", + "tf.gfile.Walk": + "tf.io.gfile.walk", + "tf.contrib.data.AUTOTUNE": + "tf.data.experimental.AUTOTUNE", + "tf.contrib.data.Counter": + "tf.data.experimental.Counter", + "tf.contrib.data.CheckpointInputPipelineHook": + "tf.data.experimental.CheckpointInputPipelineHook", + "tf.contrib.data.CsvDataset": + "tf.data.experimental.CsvDataset", + "tf.contrib.data.Optional": + "tf.data.experimental.Optional", + "tf.contrib.data.RandomDataset": + "tf.data.experimental.RandomDataset", + "tf.contrib.data.Reducer": + "tf.data.experimental.Reducer", + "tf.contrib.data.SqlDataset": + "tf.data.experimental.SqlDataset", + "tf.contrib.data.StatsAggregator": + "tf.data.experimental.StatsAggregator", + "tf.contrib.data.TFRecordWriter": + "tf.data.experimental.TFRecordWriter", + "tf.contrib.data.assert_element_shape": + "tf.data.experimental.assert_element_shape", + "tf.contrib.data.batch_and_drop_remainder": + "tf.compat.v1.contrib.data.batch_and_drop_remainder", + "tf.contrib.data.bucket_by_sequence_length": + "tf.data.experimental.bucket_by_sequence_length", + "tf.contrib.data.choose_from_datasets": + "tf.data.experimental.choose_from_datasets", + "tf.contrib.data.copy_to_device": + "tf.data.experimental.copy_to_device", + "tf.contrib.data.dense_to_sparse_batch": + "tf.data.experimental.dense_to_sparse_batch", + "tf.contrib.data.enumerate_dataset": + "tf.data.experimental.enumerate_dataset", + "tf.contrib.data.get_next_as_optional": + "tf.data.experimental.get_next_as_optional", + "tf.contrib.data.get_single_element": + "tf.data.experimental.get_single_element", + "tf.contrib.data.group_by_reducer": + "tf.data.experimental.group_by_reducer", + "tf.contrib.data.group_by_window": + "tf.data.experimental.group_by_window", + "tf.contrib.data.ignore_errors": + "tf.data.experimental.ignore_errors", + "tf.contrib.data.latency_stats": + "tf.data.experimental.latency_stats", + "tf.contrib.data.make_batched_features_dataset": + "tf.data.experimental.make_batched_features_dataset", + "tf.contrib.data.make_csv_dataset": + "tf.data.experimental.make_csv_dataset", + "tf.contrib.data.make_saveable_from_iterator": + "tf.data.experimental.make_saveable_from_iterator", + "tf.contrib.data.map_and_batch": + "tf.data.experimental.map_and_batch", + "tf.contrib.data.padded_batch_and_drop_remainder": + "tf.compat.v1.contrib.data.padded_batch_and_drop_remainder", + "tf.contrib.data.parallel_interleave": + "tf.data.experimental.parallel_interleave", + "tf.contrib.data.parse_example_dataset": + "tf.data.experimental.parse_example_dataset", + "tf.contrib.data.prefetch_to_device": + "tf.data.experimental.prefetch_to_device", + "tf.contrib.data.read_batch_features": + "tf.compat.v1.contrib.data.read_batch_features", + "tf.contrib.data.reduce_dataset": + "tf.compat.v1.contrib.data.reduce_dataset", + "tf.contrib.data.rejection_resample": + "tf.data.experimental.rejection_resample", + "tf.contrib.data.sample_from_datasets": + "tf.data.experimental.sample_from_datasets", + "tf.contrib.data.scan": + "tf.data.experimental.scan", + "tf.contrib.data.set_stats_aggregator": + "tf.data.experimental.set_stats_aggregator", + "tf.contrib.data.shuffle_and_repeat": + "tf.data.experimental.shuffle_and_repeat", + "tf.contrib.data.sliding_window_batch": + "tf.compat.v1.contrib.data.sliding_window_batch", + "tf.contrib.data.sloppy_interleave": + "tf.compat.v1.contrib.data.sloppy_interleave", + "tf.contrib.data.unbatch": + "tf.data.experimental.unbatch", + "tf.contrib.data.unique": + "tf.data.experimental.unique", + "tf.contrib.framework.sort": + "tf.sort", + "tf.contrib.framework.argsort": + "tf.argsort", + "tf.manip.batch_to_space_nd": + "tf.batch_to_space", + "tf.quantize_v2": + "tf.quantization.quantize", + "tf.sparse_add": + "tf.sparse.add", + "tf.sparse_concat": + "tf.sparse.concat", + "tf.sparse_split": + "tf.sparse.split", + "tf.sparse_matmul": + "tf.linalg.matmul", + "tf.random.stateless_multinomial": + "tf.random.stateless_categorical", + "tf.string_to_hash_bucket": + "tf.strings.to_hash_bucket", + "tf.string_to_number": + "tf.strings.to_number", + "tf.multinomial": + "tf.random.categorical", + "tf.random.multinomial": + "tf.random.categorical", + "tf.reduce_join": + "tf.strings.reduce_join", + "tf.load_file_system_library": + "tf.load_library", + "tf.pywrap_tensorflow": + "tf.compat.v1.pywrap_tensorflow", + "tf.bincount": + "tf.math.bincount", + "tf.confusion_matrix": + "tf.math.confusion_matrix", + "tf.train.confusion_matrix": + "tf.math.confusion_matrix", + "tf.decode_csv": + "tf.io.decode_csv", + "tf.data.Iterator": + "tf.compat.v1.data.Iterator", + "tf.parse_example": + "tf.io.parse_example", + "tf.parse_single_example": + "tf.io.parse_single_example", + "tf.nn.fused_batch_norm": + "tf.compat.v1.nn.fused_batch_norm", + "tf.nn.softmax_cross_entropy_with_logits_v2": + "tf.nn.softmax_cross_entropy_with_logits", + "tf.losses.Reduction.MEAN": + "tf.compat.v1.losses.Reduction.MEAN", + "tf.losses.Reduction.SUM_BY_NONZERO_WEIGHTS": + "tf.compat.v1.losses.Reduction.SUM_BY_NONZERO_WEIGHTS", + "tf.losses.Reduction.SUM_OVER_NONZERO_WEIGHTS": + "tf.compat.v1.losses.Reduction.SUM_OVER_NONZERO_WEIGHTS", + "tf.lite.constants.FLOAT": + "tf.float32", + "tf.lite.constants.INT32": + "tf.int32", + "tf.lite.constants.INT64": + "tf.int64", + "tf.lite.constants.STRING": + "tf.string", + "tf.lite.constants.QUANTIZED_UINT8": + "tf.uint8", + } # pylint: enable=line-too-long - # For custom behavior and if auto-generate rename in renames_v2.py - # is incorrect, add the op name here to exclude it from renames_v2.py. - excluded_renames = [ - ] + # Mapping from function to the new name of the function + self.symbol_renames = renames_v2.renames + self.symbol_renames.update(self.manual_symbol_renames) # Variables that should be changed to functions. self.change_to_function = {} @@ -55,22 +482,194 @@ class TFAPIChangeSpec(ast_edits.APIChangeSpec): # Functions that were reordered should be changed to the new keyword args # for safety, if positional arguments are used. If you have reversed the # positional arguments yourself, this could do the wrong thing. + # IMPORTANT: order here should correspond to OLD argument order. + # We just prepend "arg_name=" to all arguments in function calls. self.function_reorders = { - "tf.convert_to_tensor": ["value", "dtype", "preferred_dtype", "name"], - "tf.argmin": ["input", "axis", "output_type", "name"], - "tf.argmax": ["input", "axis", "output_type", "name"], + "tf.io.serialize_sparse": ["sp_input", "name", "out_type"], + "tf.io.serialize_many_sparse": ["sp_input", "name", "out_type"], + "tf.argmax": ["input", "axis", "name", "axis", "output_type"], + "tf.argmin": ["input", "axis", "name", "axis", "output_type"], + "tf.batch_to_space": ["input", "crops", "block_size", "name"], "tf.boolean_mask": ["tensor", "mask", "name", "axis"], + "tf.convert_to_tensor": ["value", "dtype", "name", "preferred_dtype"], + "tf.nn.moments": ["x", "axes", "shift", "keepdims", "name"], + "tf.nn.convolution": [ + "input", "filter", "padding", "strides", "dilation_rate", "name", + "data_format" + ], + "tf.nn.crelu": ["features", "name", "axis"], + "tf.nn.pool": [ + "input", "window_shape", "pooling_type", "padding", "dilation_rate", + "strides", "name", "data_format" + ], + "tf.nn.depthwise_conv2d": [ + "input", "filter", "strides", "padding", "rate", "name", + "data_format" + ], + "tf.multinomial": [ + "logits", "num_samples", "seed", "name", "output_dtype" + ], + "tf.random.multinomial": [ + "logits", "num_samples", "seed", "name", "output_dtype" + ], + "tf.pad": ["tensor", "paddings", "mode", "name", "constant_values"], + "tf.quantize_v2": [ + "input", "min_range", "max_range", "T", "mode", "name", "round_mode" + ], + "tf.feature_column.categorical_column_with_vocabulary_file": [ + "key", "vocabulary_file", "vocabulary_size", "num_oov_buckets", + "default_value", "dtype" + ], + "tf.shape": ["input", "name", "out_type"], + "tf.size": ["input", "name", "out_type"], + "tf.random.poisson": ["lam", "shape", "dtype", "seed", "name"], + "tf.sparse.add": ["a", "b", "thresh"], + "tf.sparse_add": ["a", "b", "thresh"], + "tf.sparse.concat": [ + "axis", "sp_inputs", "name", "expand_nonconcat_dim", "concat_dim" + ], + "tf.sparse_concat": [ + "axis", "sp_inputs", "name", "expand_nonconcat_dim", "concat_dim" + ], + "tf.sparse.segment_mean": [ + "data", "indices", "segment_ids", "name", "num_segments" + ], + "tf.sparse.segment_sqrt_n": [ + "data", "indices", "segment_ids", "name", "num_segments" + ], + "tf.sparse.segment_sum": [ + "data", "indices", "segment_ids", "name", "num_segments" + ], + "tf.sparse_matmul": [ + "a", "b", "transpose_a", "transpose_b", "a_is_sparse", + "b_is_sparse", "name" + ], + "tf.io.decode_csv": [ + "records", + "record_defaults", + "field_delim", + "use_quote_delim", + "name", + "na_value", + "select_cols", + ], + "tf.strings.substr": ["input", "pos", "len", "name", "unit"], + "tf.strings.reduce_join": [ + "input", "axis", "keep_dims", "separator", "name", + "reduction_indices" + ], + "tf.strings.length": ["input", "name", "unit"], + "tf.transpose": ["a", "perm", "name", "conjugate"], + "tf.tuple": ["tensors", "name", "control_inputs"], + "tf.parse_example": [ + "serialized", "features", "name", "example_names" + ], + "tf.parse_single_example": [ + "serialized", "features", "name", "example_names" + ], + "tf.io.parse_example": [ + "serialized", "features", "name", "example_names" + ], + "tf.io.parse_single_example": [ + "serialized", "features", "name", "example_names" + ], + "tf.while_loop": [ + "cond", "body", "loop_vars", "shape_invariants", + "parallel_iterations", "back_prop", "swap_memory", "name", + "maximum_iterations", "return_same_structure" + ], + "tf.reduce_all": [ + "input_tensor", "axis", "keepdims", "name", "reduction_indices", + "keep_dims" + ], + "tf.math.reduce_all": [ + "input_tensor", "axis", "keepdims", "name", "reduction_indices", + "keep_dims" + ], + "tf.reduce_any": [ + "input_tensor", "axis", "keepdims", "name", "reduction_indices", + "keep_dims" + ], + "tf.math.reduce_any": [ + "input_tensor", "axis", "keepdims", "name", "reduction_indices", + "keep_dims" + ], + "tf.reduce_min": [ + "input_tensor", "axis", "keepdims", "name", "reduction_indices", + "keep_dims" + ], + "tf.math.reduce_min": [ + "input_tensor", "axis", "keepdims", "name", "reduction_indices", + "keep_dims" + ], + "tf.reduce_max": [ + "input_tensor", "axis", "keepdims", "name", "reduction_indices", + "keep_dims" + ], + "tf.math.reduce_max": [ + "input_tensor", "axis", "keepdims", "name", "reduction_indices", + "keep_dims" + ], + "tf.reduce_sum": [ + "input_tensor", "axis", "keepdims", "name", "reduction_indices", + "keep_dims" + ], + "tf.math.reduce_sum": [ + "input_tensor", "axis", "keepdims", "name", "reduction_indices", + "keep_dims" + ], + "tf.reduce_mean": [ + "input_tensor", "axis", "keepdims", "name", "reduction_indices", + "keep_dims" + ], + "tf.math.reduce_mean": [ + "input_tensor", "axis", "keepdims", "name", "reduction_indices", + "keep_dims" + ], + "tf.reduce_prod": [ + "input_tensor", "axis", "keepdims", "name", "reduction_indices", + "keep_dims" + ], + "tf.math.reduce_prod": [ + "input_tensor", "axis", "keepdims", "name", "reduction_indices", + "keep_dims" + ], + "tf.reduce_logsumexp": [ + "input_tensor", "axis", "keepdims", "name", "reduction_indices", + "keep_dims" + ], + "tf.math.reduce_logsumexp": [ + "input_tensor", "axis", "keepdims", "name", "reduction_indices", + "keep_dims" + ], + "tf.reduce_join": [ + "input", "axis", "keep_dims", "separator", "name", + "reduction_indices" + ], + "tf.confusion_matrix": [ + "labels", "predictions", "num_classes", "dtype", "name", "weights" + ], + "tf.math.confusion_matrix": [ + "labels", "predictions", "num_classes", "dtype", "name", "weights" + ] } # Specially handled functions. - self.function_handle = {} + self.function_handle = { + "tf.nn.dropout": self._dropout_handler, + "tf.gradients": self._colocate_handler("tf.gradients"), + "*.minimize": self._colocate_handler("Optimizer.minimize"), + "*.compute_gradients": + self._colocate_handler("Optimizer.compute_gradients"), + } decay_function_comment = ( - "ERROR: has been changed to return a callable instead " - "of a tensor when graph building, but its functionality remains " + "WARNING: has been changed to return a callable instead" + " of a tensor when graph building, but its functionality remains " "unchanged during eager execution (returns a callable like " "before). The converter cannot detect and fix this reliably, so " - "you need to inspect this usage manually.\n" + "this usage has been converted to compat.v1 (even though it may already" + " be correct).\n" ) # TODO(b/118888586): add default value change to update script. @@ -79,99 +678,187 @@ class TFAPIChangeSpec(ast_edits.APIChangeSpec): "SUM_OVER_BATCH_SIZE.\n" ) + assert_return_type_comment = ( + "WARNING: assert_* functions have been changed to return None, the " + "data argument has been removed, and arguments have been reordered." + "\nThe calls have been converted to compat.v1 for safety (even though " + " they may already have been correct)." + ) + + assert_rank_comment = ( + "WARNING: assert_rank_* functions have been changed to return None, and" + " the data and summarize arguments have been removed." + "\nThe calls have been converted to compat.v1 for safety (even though " + " they may already have been correct)." + ) + + tf_01s_like_no_optimize_comment = ( + "WARNING: tf.zeros_like and tf.ones_like no longer have the optimize " + "argument in TF 2.0 or after (also, `tensor' argument is renamed to " + "`input')." + "\nThe calls have been converted to compat.v1 for safety (even though " + " they may already have been correct)." + ) + # Function warnings. placeholder inside warnings will be # replaced by function name. self.function_warnings = { - "tf.train.exponential_decay": decay_function_comment, - "tf.train.piecewise_constant": decay_function_comment, - "tf.train.polynomial_decay": decay_function_comment, - "tf.train.natural_exp_decay": decay_function_comment, - "tf.train.inverse_time_decay": decay_function_comment, - "tf.train.cosine_decay": decay_function_comment, - "tf.train.cosine_decay_restarts": decay_function_comment, - "tf.train.linear_cosine_decay": decay_function_comment, - "tf.train.noisy_linear_cosine_decay": decay_function_comment, - "tf.estimator.LinearClassifier": default_loss_reduction_changed, + "tf.assert_greater": assert_return_type_comment, + "tf.assert_equal": assert_return_type_comment, + "tf.assert_less": assert_return_type_comment, + "tf.assert_rank": assert_rank_comment, + "tf.debugging.assert_equal": assert_return_type_comment, + "tf.debugging.assert_greater": assert_return_type_comment, + "tf.debugging.assert_greater_equal": assert_return_type_comment, + "tf.debugging.assert_integer": assert_return_type_comment, + "tf.debugging.assert_less": assert_return_type_comment, + "tf.debugging.assert_less_equal": assert_return_type_comment, + "tf.debugging.assert_near": assert_return_type_comment, + "tf.debugging.assert_negative": assert_return_type_comment, + "tf.debugging.assert_non_negative": assert_return_type_comment, + "tf.debugging.assert_non_positive": assert_return_type_comment, + "tf.debugging.assert_none_equal": assert_return_type_comment, + "tf.debugging.assert_positive": assert_return_type_comment, + "tf.debugging.assert_rank": assert_rank_comment, + "tf.debugging.assert_rank_at_least": assert_rank_comment, + "tf.debugging.assert_rank_in": assert_rank_comment, + "tf.flags": "tf.flags has been removed, please use the argparse or absl" + " module if you need command line parsing.", + "tf.train.exponential_decay": + decay_function_comment, + "tf.train.piecewise_constant_decay": + decay_function_comment, + "tf.train.polynomial_decay": + decay_function_comment, + "tf.train.natural_exp_decay": + decay_function_comment, + "tf.train.inverse_time_decay": + decay_function_comment, + "tf.train.cosine_decay": + decay_function_comment, + "tf.train.cosine_decay_restarts": + decay_function_comment, + "tf.train.linear_cosine_decay": + decay_function_comment, + "tf.train.noisy_linear_cosine_decay": + decay_function_comment, + "tf.estimator.LinearClassifier": + default_loss_reduction_changed, + "tf.estimator.LinearRegressor": + default_loss_reduction_changed, + "tf.estimator.DNNLinearCombinedClassifier": + default_loss_reduction_changed, + "tf.estimator.DNNLinearCombinedRegressor": + default_loss_reduction_changed, + "tf.estimator.DNNRegressor": + default_loss_reduction_changed, + "tf.estimator.DNNClassifier": + default_loss_reduction_changed, + "tf.estimator.BaselineClassifier": + default_loss_reduction_changed, + "tf.estimator.BaselineRegressor": + default_loss_reduction_changed, + "tf.nn.conv1d": + "WARNING: use_cudnn_on_gpu argument has been removed and \"value\" was " + "renamed to \"input\"", + "tf.nn.conv2d": + "WARNING: use_cudnn_on_gpu argument has been removed and \"filter\" " + "was renamed to \"filters\"", + "tf.nn.conv2d_backprop_filter": + "WARNING: use_cudnn_on_gpu argument has been removed", + "tf.nn.conv2d_backprop_input": + "WARNING: use_cudnn_on_gpu argument has been removed and \"filter\" " + "was renamed to \"filters\"", + "tf.nn.erosion2d": + "WARNING: now requires a data_format argument", + "tf.nn.nce_loss": + "WARNING: `partition_strategy` has been removed from `tf.nn.nce_loss` " + " The 'div' strategy is used by default.", + "tf.zeros_like": tf_01s_like_no_optimize_comment, + "tf.ones_like": tf_01s_like_no_optimize_comment, } - # Right now we can't have both a rename and a warning. + self.symbol_renames = { name: new_name for name, new_name in self.symbol_renames.items() - if name not in self.function_warnings and name not in excluded_renames } + export_saved_model_renamed = ( + "(Manual edit required) Please rename the method export_savedmodel() " + "to export_saved_model(). Two things to note:\n\t(1) The argument " + "strip_default_attributes has been removed. The function will always " + "strip the default attributes from ops. If this breaks your code, " + "please switch to tf.compat.v1.estimator.Estimator.\n\t(2) This change " + "only effects core estimator. If you are using " + "tf.contrib.learn.Estimator, please switch to using core estimator.") + + make_initializable_iterator_deprecation = ( + "(Manual edit required) The " + "`tf.data.Dataset.make_initializable_iterator()` method has been " + "removed. If you are using the Estimator API, you can return a dataset " + "directly from your input functions without creating an iterator. " + "As a last resort, please replace calls to that method on `dataset` " + "with a call to " + "`tf.compat.v1.data.make_initializable_iterator(dataset)`.") + + make_one_shot_iterator_deprecation = ( + "(Manual edit required) The " + "`tf.data.Dataset.make_one_shot_iterator()` method has been " + "removed. If you are using eager execution, you can iterate over " + "`dataset` using a Python `for` loop. If you are using the Estimator " + "API, you can return a dataset directly from your input functions " + "without creating an iterator. As a last resort, please replace calls " + "to that method on `dataset` with a call to " + "`tf.compat.v1.data.make_one_shot_iterator(dataset)`.") + + # Specify warnings for functions that aren't restricted to the tf.x.y.z + # format. This should only be used for methods with unique names, e.g. + # export_savedmodel, which is only defined in Estimator objects. + self.unrestricted_function_warnings = { + "export_savedmodel": export_saved_model_renamed, + "make_initializable_iterator": make_initializable_iterator_deprecation, + "make_one_shot_iterator": make_one_shot_iterator_deprecation, + } + + @staticmethod + def _dropout_handler(file_edit_recorder, node): + if len(node.args) < 2: + comment = ("ERROR: tf.nn.dropout did not take arguments, so automatic " + "transformation was disabled. tf.nn.dropout has changed " + "the semantics of the second argument.") + file_edit_recorder.add( + comment, + node.lineno, + node.col_offset, + "tf.nn.dropout", + "tf.nn.dropout", + error="tf.nn.dropout requires manual check.") + else: + comment = ("WARNING: tf.nn.dropout has changed the semantics of the " + "second argument. Please check the transformation.\n") + file_edit_recorder.add( + comment, + node.args[1].lineno, + node.args[1].col_offset, + "", + "1 - ") -if __name__ == "__main__": - parser = argparse.ArgumentParser( - formatter_class=argparse.RawDescriptionHelpFormatter, - description="""Convert a TensorFlow Python file to 2.0 - -Simple usage: - tf_convert_v2.py --infile foo.py --outfile bar.py - tf_convert_v2.py --intree ~/code/old --outtree ~/code/new -""") - parser.add_argument( - "--infile", - dest="input_file", - help="If converting a single file, the name of the file " - "to convert") - parser.add_argument( - "--outfile", - dest="output_file", - help="If converting a single file, the output filename.") - parser.add_argument( - "--intree", - dest="input_tree", - help="If converting a whole tree of files, the directory " - "to read from (relative or absolute).") - parser.add_argument( - "--outtree", - dest="output_tree", - help="If converting a whole tree of files, the output " - "directory (relative or absolute).") - parser.add_argument( - "--copyotherfiles", - dest="copy_other_files", - help=("If converting a whole tree of files, whether to " - "copy the other files."), - type=bool, - default=False) - parser.add_argument( - "--reportfile", - dest="report_filename", - help=("The name of the file where the report log is " - "stored." - "(default: %(default)s)"), - default="report.txt") - args = parser.parse_args() - - upgrade = ast_edits.ASTCodeUpgrader(TFAPIChangeSpec()) - report_text = None - report_filename = args.report_filename - files_processed = 0 - if args.input_file: - if not args.output_file: - raise ValueError( - "--outfile= argument is required when converting a " - "single file.") - files_processed, report_text, errors = upgrade.process_file( - args.input_file, args.output_file) - files_processed = 1 - elif args.input_tree: - if not args.output_tree: - raise ValueError( - "--outtree= argument is required when converting a " - "file tree.") - files_processed, report_text, errors = upgrade.process_tree( - args.input_tree, args.output_tree, args.copy_other_files) - else: - parser.print_help() - if report_text: - open(report_filename, "w").write(report_text) - print("TensorFlow 2.0 Upgrade Script") - print("-----------------------------") - print("Converted %d files\n" % files_processed) - print("Detected %d errors that require attention" % len(errors)) - print("-" * 80) - print("\n".join(errors)) - print("\nMake sure to read the detailed log %r\n" % report_filename) + @staticmethod + def _colocate_handler(name): + def _helper(file_edit_recorder, node): + for keyword in node.keywords: + if keyword.arg == "colocate_gradients_with_ops": + # TODO(jhseu): Since ast_edit.py does string replacement, there's no + # straightforward way to remove the argument. Try to fix before 2.0 is + # final. + comment = ("For tf.gradients and tf.Optimizer.minimize, " + "colocate_gradients_with_op has been removed and now " + "defaults to True.") + file_edit_recorder.add( + comment, + node.lineno, + node.col_offset, + "", + "", + error="{} requires manual check.".format(name)) + return _helper diff --git a/tensorflow/tools/compatibility/tf_upgrade_v2_main.py b/tensorflow/tools/compatibility/tf_upgrade_v2_main.py new file mode 100644 index 0000000000..543d078642 --- /dev/null +++ b/tensorflow/tools/compatibility/tf_upgrade_v2_main.py @@ -0,0 +1,104 @@ +# Copyright 2018 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. +# ============================================================================== +"""Upgrader for Python scripts from 1.* TensorFlow to 2.0 TensorFlow.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import argparse + +from tensorflow.tools.compatibility import ast_edits +from tensorflow.tools.compatibility import tf_upgrade_v2 + + +def main(): + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description="""Convert a TensorFlow Python file to 2.0 + +Simple usage: + tf_upgrade_v2.py --infile foo.py --outfile bar.py + tf_upgrade_v2.py --intree ~/code/old --outtree ~/code/new +""") + parser.add_argument( + "--infile", + dest="input_file", + help="If converting a single file, the name of the file " + "to convert") + parser.add_argument( + "--outfile", + dest="output_file", + help="If converting a single file, the output filename.") + parser.add_argument( + "--intree", + dest="input_tree", + help="If converting a whole tree of files, the directory " + "to read from (relative or absolute).") + parser.add_argument( + "--outtree", + dest="output_tree", + help="If converting a whole tree of files, the output " + "directory (relative or absolute).") + parser.add_argument( + "--copyotherfiles", + dest="copy_other_files", + help=("If converting a whole tree of files, whether to " + "copy the other files."), + type=bool, + default=True) + parser.add_argument( + "--reportfile", + dest="report_filename", + help=("The name of the file where the report log is " + "stored." + "(default: %(default)s)"), + default="report.txt") + args = parser.parse_args() + + upgrade = ast_edits.ASTCodeUpgrader(tf_upgrade_v2.TFAPIChangeSpec()) + report_text = None + report_filename = args.report_filename + files_processed = 0 + if args.input_file: + if not args.output_file: + raise ValueError( + "--outfile= argument is required when converting a " + "single file.") + files_processed, report_text, errors = upgrade.process_file( + args.input_file, args.output_file) + files_processed = 1 + elif args.input_tree: + if not args.output_tree: + raise ValueError( + "--outtree= argument is required when converting a " + "file tree.") + files_processed, report_text, errors = upgrade.process_tree( + args.input_tree, args.output_tree, args.copy_other_files) + else: + parser.print_help() + if report_text: + open(report_filename, "w").write(report_text) + print("TensorFlow 2.0 Upgrade Script") + print("-----------------------------") + print("Converted %d files\n" % files_processed) + print("Detected %d errors that require attention" % len(errors)) + print("-" * 80) + print("\n".join(errors)) + print("\nMake sure to read the detailed log %r\n" % report_filename) + + +if __name__ == "__main__": + main() diff --git a/tensorflow/tools/compatibility/tf_upgrade_v2_test.py b/tensorflow/tools/compatibility/tf_upgrade_v2_test.py index 9060b1c71f..b8b02c9c7f 100644 --- a/tensorflow/tools/compatibility/tf_upgrade_v2_test.py +++ b/tensorflow/tools/compatibility/tf_upgrade_v2_test.py @@ -17,15 +17,71 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function + import os import tempfile + import six +import tensorflow as tf +# OSS TF V2 import placeholder. + + from tensorflow.python.framework import test_util from tensorflow.python.platform import test as test_lib +from tensorflow.python.util import tf_decorator +from tensorflow.python.util import tf_export +from tensorflow.python.util import tf_inspect +from tensorflow.tools.common import public_api +from tensorflow.tools.common import traverse from tensorflow.tools.compatibility import ast_edits from tensorflow.tools.compatibility import tf_upgrade_v2 +_TENSORFLOW_API_ATTR_V1 = ( + tf_export.API_ATTRS_V1[tf_export.TENSORFLOW_API_NAME].names) +_TENSORFLOW_API_ATTR = tf_export.API_ATTRS[tf_export.TENSORFLOW_API_NAME].names +_ESTIMATOR_API_ATTR_V1 = ( + tf_export.API_ATTRS_V1[tf_export.ESTIMATOR_API_NAME].names) +_ESTIMATOR_API_ATTR = tf_export.API_ATTRS[tf_export.ESTIMATOR_API_NAME].names + + +def get_v1_names(symbol): + names_v1 = [] + if hasattr(symbol, _TENSORFLOW_API_ATTR_V1): + names_v1.extend(getattr(symbol, _TENSORFLOW_API_ATTR_V1)) + if hasattr(symbol, _ESTIMATOR_API_ATTR_V1): + names_v1.extend(getattr(symbol, _ESTIMATOR_API_ATTR_V1)) + return names_v1 + + +def get_v2_names(symbol): + names_v2 = set() + if hasattr(symbol, _TENSORFLOW_API_ATTR): + names_v2.update(getattr(symbol, _TENSORFLOW_API_ATTR)) + if hasattr(symbol, _ESTIMATOR_API_ATTR): + names_v2.update(getattr(symbol, _ESTIMATOR_API_ATTR)) + return list(names_v2) + + +def get_func_and_args_from_str(call_str): + """Parse call string to get function and argument names. + + Args: + call_str: Call string must be in the form: + `tf.foo(arg1=val1, arg2=val2, ...)`. + + Returns: + (function_name, list of arg names) tuple. + """ + open_paren_index = call_str.find("(") + close_paren_index = call_str.rfind(")") + + function_name = call_str[:call_str.find("(")] + args = call_str[open_paren_index+1:close_paren_index].split(",") + args = [arg.split("=")[0].strip() for arg in args] + return function_name, args + + class TestUpgrade(test_util.TensorFlowTestCase): """Test various APIs that have been changed in 2.0. @@ -34,6 +90,22 @@ class TestUpgrade(test_util.TensorFlowTestCase): work when run with current TensorFlow. """ + @classmethod + def setUpClass(cls): + cls.v2_symbols = {} + if not hasattr(tf.compat, "v2"): + return + + def symbol_collector(unused_path, unused_parent, children): + for child in children: + _, attr = tf_decorator.unwrap(child[1]) + api_names_v2 = get_v2_names(attr) + for name in api_names_v2: + cls.v2_symbols["tf." + name] = attr + + visitor = public_api.PublicAPIVisitor(symbol_collector) + traverse.traverse(tf.compat.v2, visitor) + def _upgrade(self, old_file_text): in_file = six.StringIO(old_file_text) out_file = six.StringIO() @@ -64,6 +136,85 @@ class TestUpgrade(test_util.TensorFlowTestCase): _, unused_report, unused_errors, new_text = self._upgrade(text) self.assertEqual(new_text, "tf.math.rsqrt(tf.math.log_sigmoid(3.8))\n") + def testAllAPI(self): + if not hasattr(tf.compat, "v2"): + return + + # Converts all symbols in the v1 namespace to the v2 namespace, raising + # an error if the target of the conversion is not in the v2 namespace. + def conversion_visitor(unused_path, unused_parent, children): + for child in children: + _, attr = tf_decorator.unwrap(child[1]) + api_names = get_v1_names(attr) + for name in api_names: + _, _, _, text = self._upgrade("tf." + name) + if (text and + not text.startswith("tf.compat.v1") and + text not in self.v2_symbols): + self.assertFalse( + True, "Symbol %s generated from %s not in v2 API" % ( + text, name)) + + visitor = public_api.PublicAPIVisitor(conversion_visitor) + visitor.do_not_descend_map["tf"].append("contrib") + visitor.private_map["tf.compat"] = ["v1", "v2"] + traverse.traverse(tf.compat.v1, visitor) + + def testKeywordArgNames(self): + if not hasattr(tf.compat, "v2"): + return + + all_keyword_renames = ( + tf_upgrade_v2.TFAPIChangeSpec().function_keyword_renames) + v2_name_exceptions = {"verify_shape_is_now_always_true"} + + # Visitor that verifies V1 argument names, converts to V2 and checks + # V2 argument names. + def conversion_visitor(unused_path, unused_parent, children): + for child in children: + _, attr = tf_decorator.unwrap(child[1]) + names_v1 = get_v1_names(attr) + + for name in names_v1: + name = "tf.%s" % name + if name not in all_keyword_renames: + continue + arg_names_v1 = tf_inspect.getargspec(attr)[0] + keyword_renames = all_keyword_renames[name] + self.assertEqual(type(keyword_renames), dict) + + # Assert that v1 function has valid v1 argument names. + for from_name, _ in keyword_renames.items(): + self.assertIn( + from_name, arg_names_v1, + "%s not found in %s arguments: %s" % + (from_name, name, str(arg_names_v1))) + + # Assert that arg names after converting to v2 are present in + # v2 function. + # 1. First, create an input of the form: + # tf.foo(arg1=val1, arg2=val2, ...) + args = ",".join( + ["%s=%d" % (from_name, from_index) + for from_index, from_name in enumerate(keyword_renames.keys())]) + text_input = "%s(%s)" % (name, args) + # 2. Convert the input to V2. + _, _, _, text = self._upgrade(text_input) + new_function_name, new_args = get_func_and_args_from_str(text) + # 3. Verify V2 function and arguments. + # Note: If we rename arguments, new function must be available in 2.0. + # We should not be using compat.v1 in this case. + self.assertIn(new_function_name, self.v2_symbols) + args_v2 = tf_inspect.getargspec(self.v2_symbols[new_function_name])[0] + args_v2.extend(v2_name_exceptions) + for new_arg in new_args: + self.assertIn(new_arg, args_v2) + + visitor = public_api.PublicAPIVisitor(conversion_visitor) + visitor.do_not_descend_map["tf"].append("contrib") + visitor.private_map["tf.compat"] = ["v1", "v2"] + traverse.traverse(tf.compat.v1, visitor) + def testRenameConstant(self): text = "tf.MONOLITHIC_BUILD\n" _, unused_report, unused_errors, new_text = self._upgrade(text) @@ -72,6 +223,16 @@ class TestUpgrade(test_util.TensorFlowTestCase): _, unused_report, unused_errors, new_text = self._upgrade(text) self.assertEqual(new_text, "some_call(tf.sysconfig.MONOLITHIC_BUILD)\n") + def testRenameArgs(self): + text = ("tf.nn.pool(input_a, window_shape_a, pooling_type_a, padding_a, " + "dilation_rate_a, strides_a, name_a, data_format_a)\n") + _, unused_report, unused_errors, new_text = self._upgrade(text) + self.assertEqual(new_text, + ("tf.nn.pool(input=input_a, window_shape=window_shape_a," + " pooling_type=pooling_type_a, padding=padding_a, " + "dilations=dilation_rate_a, strides=strides_a, " + "name=name_a, data_format=data_format_a)\n")) + def testReorder(self): text = "tf.boolean_mask(a, b, c, d)\n" _, unused_report, unused_errors, new_text = self._upgrade(text) @@ -79,7 +240,7 @@ class TestUpgrade(test_util.TensorFlowTestCase): "tf.boolean_mask(tensor=a, mask=b, name=c, axis=d)\n") def testLearningRateDecay(self): - for decay in ["tf.train.exponential_decay", "tf.train.piecewise_constant", + for decay in ["tf.train.exponential_decay", "tf.train.polynomial_decay", "tf.train.natural_exp_decay", "tf.train.inverse_time_decay", "tf.train.cosine_decay", "tf.train.cosine_decay_restarts", @@ -87,18 +248,208 @@ class TestUpgrade(test_util.TensorFlowTestCase): "tf.train.noisy_linear_cosine_decay"]: text = "%s(a, b)\n" % decay - _, report, errors, new_text = self._upgrade(text) - self.assertEqual(text, new_text) + _, report, errors, _ = self._upgrade(text) self.assertEqual(errors, ["test.py:1: %s requires manual check." % decay]) self.assertIn("%s has been changed" % decay, report) - def testEstimatorLossReductionChangege(self): - text = "tf.estimator.LinearClassifier(a, b)\n" - _, report, errors, new_text = self._upgrade(text) + def testPiecewiseDecay(self): + text = "tf.train.piecewise_constant_decay(a, b)\n" + _, report, errors, _ = self._upgrade(text) + self.assertEqual( + errors, + ["test.py:1: tf.train.piecewise_constant_decay requires manual check."]) + self.assertIn("tf.train.piecewise_constant_decay has been changed", report) + + def testEstimatorLossReductionChange(self): + classes = [ + "LinearClassifier", "LinearRegressor", "DNNLinearCombinedClassifier", + "DNNLinearCombinedRegressor", "DNNRegressor", "DNNClassifier", + "BaselineClassifier", "BaselineRegressor" + ] + for c in classes: + ns = "tf.estimator." + c + text = ns + "(a, b)" + _, report, errors, new_text = self._upgrade(text) + self.assertEqual(text, new_text) + self.assertEqual(errors, ["test.py:1: %s requires manual check." % ns]) + self.assertIn("loss_reduction has been changed", report) + + def testDropout(self): + text = "tf.nn.dropout(x, keep_prob, name=\"foo\")\n" + _, unused_report, unused_errors, new_text = self._upgrade(text) + self.assertEqual( + new_text, + "tf.nn.dropout(x, 1 - keep_prob, name=\"foo\")\n", + ) + + text = "tf.nn.dropout(x)\n" + _, unused_report, errors, new_text = self._upgrade(text) + self.assertEqual(new_text, text) + self.assertEqual( + errors, + ["test.py:1: tf.nn.dropout requires manual check."] + ) + + def testCountNonZeroChanges(self): + text = ( + "tf.math.count_nonzero(input_tensor=input, dtype=dtype, name=name, " + "reduction_indices=axis, keep_dims=keepdims)\n" + ) + _, unused_report, unused_errors, new_text = self._upgrade(text) + expected_text = ( + "tf.math.count_nonzero(input=input, dtype=dtype, name=name, " + "axis=axis, keepdims=keepdims)\n" + ) + self.assertEqual(new_text, expected_text) + + def testRandomMultinomialToRandomCategorical(self): + text = ( + "tf.random.multinomial(logits, samples, seed, name, output_dtype)\n" + ) + _, unused_report, unused_errors, new_text = self._upgrade(text) + expected_text = ( + "tf.random.categorical(logits=logits, num_samples=samples, seed=seed, " + "name=name, dtype=output_dtype)\n" + ) + self.assertEqual(new_text, expected_text) + + text = ( + "tf.multinomial(logits, samples, seed, name, output_dtype)\n" + ) + _, unused_report, unused_errors, new_text = self._upgrade(text) + expected_text = ( + "tf.random.categorical(logits=logits, num_samples=samples, seed=seed, " + "name=name, dtype=output_dtype)\n" + ) + self.assertEqual(new_text, expected_text) + + def testConvolutionOpUpdate(self): + text = ( + "tf.nn.convolution(input, filter, padding, strides, dilation_rate, " + "name, data_format)" + ) + _, unused_report, unused_errors, new_text = self._upgrade(text) + expected_text = ( + "tf.nn.convolution(input=input, filters=filter, padding=padding, " + "strides=strides, dilations=dilation_rate, name=name, " + "data_format=data_format)" + ) + self.assertEqual(new_text, expected_text) + + def testColocateGradientsWithOps(self): + text = "tf.gradients(a, foo=False)\n" + _, unused_report, errors, new_text = self._upgrade(text) + self.assertEqual(text, new_text) + self.assertEqual(errors, []) + + text = "tf.gradients(a, colocate_gradients_with_ops=False)\n" + _, unused_report, errors, new_text = self._upgrade(text) + self.assertEqual(text, new_text) + self.assertEqual(errors, ["test.py:1: tf.gradients requires manual check."]) + + text = "optimizer.minimize(a, foo=False)\n" + _, unused_report, errors, new_text = self._upgrade(text) self.assertEqual(text, new_text) - self.assertEqual(errors, ["test.py:1: %s requires manual check." - % "tf.estimator.LinearClassifier"]) - self.assertIn("loss_reduction has been changed", report) + self.assertEqual(errors, []) + + text = "optimizer.minimize(a, colocate_gradients_with_ops=False)\n" + _, unused_report, errors, new_text = self._upgrade(text) + self.assertEqual(text, new_text) + self.assertEqual(errors, + ["test.py:1: Optimizer.minimize requires manual check."]) + + text = "optimizer.compute_gradients(a, foo=False)\n" + _, unused_report, errors, new_text = self._upgrade(text) + self.assertEqual(text, new_text) + self.assertEqual(errors, []) + + text = "optimizer.compute_gradients(a, colocate_gradients_with_ops=False)\n" + _, unused_report, errors, new_text = self._upgrade(text) + self.assertEqual(text, new_text) + self.assertEqual(errors, + ["test.py:1: Optimizer.compute_gradients " + "requires manual check."]) + + def testExportSavedModelRename(self): + text = "self.est.export_savedmodel(path)" + _, report, unused_errors, unused_new_text = self._upgrade(text) + self.assertIn( + "rename the method export_savedmodel() to export_saved_model()", + report) + + def testArgmin(self): + text = "tf.argmin(input, name=n, dimension=1, output_type=type)" + expected_text = "tf.argmin(input=input, name=n, axis=1, output_type=type)" + _, unused_report, unused_errors, new_text = self._upgrade(text) + self.assertEqual(new_text, expected_text) + + text = "tf.argmin(input, 0)" + expected_text = "tf.argmin(input=input, axis=0)" + _, unused_report, unused_errors, new_text = self._upgrade(text) + self.assertEqual(new_text, expected_text) + + def testArgmax(self): + text = "tf.argmax(input, name=n, dimension=1, output_type=type)" + expected_text = "tf.argmax(input=input, name=n, axis=1, output_type=type)" + _, unused_report, unused_errors, new_text = self._upgrade(text) + self.assertEqual(new_text, expected_text) + + text = "tf.argmax(input, 0)" + expected_text = "tf.argmax(input=input, axis=0)" + _, unused_report, unused_errors, new_text = self._upgrade(text) + self.assertEqual(new_text, expected_text) + + def testBatchToSpace(self): + text = "tf.batch_to_space_nd(input, block_shape, crops, name)" + expected_text = "tf.batch_to_space(input, block_shape, crops, name)" + _, unused_report, unused_errors, new_text = self._upgrade(text) + self.assertEqual(new_text, expected_text) + + text = "tf.batch_to_space(input, crops, block_size, name)" + expected_text = ( + "tf.batch_to_space(input=input, crops=crops, block_shape=block_size, " + "name=name)") + _, unused_report, unused_errors, new_text = self._upgrade(text) + self.assertEqual(new_text, expected_text) + + text = "tf.manip.batch_to_space_nd(input, block_shape, crops, name)" + expected_text = "tf.batch_to_space(input, block_shape, crops, name)" + _, unused_report, unused_errors, new_text = self._upgrade(text) + self.assertEqual(new_text, expected_text) + + def testExtractImagePatches(self): + text = ( + "tf.extract_image_patches(images, ksizes=ksizes, strides=strides," + "rates=rates, padding=padding, name=name)") + expected_text = ( + "tf.image.extract_image_patches(images, sizes=ksizes, strides=strides," + "rates=rates, padding=padding, name=name)") + _, unused_report, unused_errors, new_text = self._upgrade(text) + self.assertEqual(new_text, expected_text) + + def testStatelessMultinomial(self): + text = ( + "tf.random.stateless_multinomial(logits, num_samples, seed, " + "output_dtype=dtype, name=name)") + expected_text = ( + "tf.random.stateless_categorical(logits, num_samples, seed, " + "dtype=dtype, name=name)") + _, unused_report, unused_errors, new_text = self._upgrade(text) + self.assertEqual(new_text, expected_text) + + def testSoftMaxCrossEntropyWithLogitsV2(self): + text = "tf.nn.softmax_cross_entropy_with_logits_v2(labels, logits, dim=2)" + expected_text = ( + "tf.nn.softmax_cross_entropy_with_logits(labels, logits, axis=2)") + _, unused_report, unused_errors, new_text = self._upgrade(text) + self.assertEqual(new_text, expected_text) + + def testSparseMatmul(self): + text = ("tf.sparse_matmul(a, b, c, d, e, f, g)\n") + expected_text = ("tf.linalg.matmul(a=a, b=b, transpose_a=c, transpose_b=d, " + "a_is_sparse=e, b_is_sparse=f, name=g)\n") + _, unused_report, unused_errors, new_text = self._upgrade(text) + self.assertEqual(new_text, expected_text) class TestUpgradeFiles(test_util.TensorFlowTestCase): diff --git a/tensorflow/tools/compatibility/update/BUILD b/tensorflow/tools/compatibility/update/BUILD index 0ee4550815..b9725a74ee 100644 --- a/tensorflow/tools/compatibility/update/BUILD +++ b/tensorflow/tools/compatibility/update/BUILD @@ -12,5 +12,6 @@ py_binary( "//tensorflow/python:no_contrib", "//tensorflow/tools/common:public_api", "//tensorflow/tools/common:traverse", + "//tensorflow/tools/compatibility:tf_upgrade_v2_lib", ], ) diff --git a/tensorflow/tools/compatibility/update/generate_v2_renames_map.py b/tensorflow/tools/compatibility/update/generate_v2_renames_map.py index 43aa8e057e..19ad6c3a2a 100644 --- a/tensorflow/tools/compatibility/update/generate_v2_renames_map.py +++ b/tensorflow/tools/compatibility/update/generate_v2_renames_map.py @@ -32,6 +32,7 @@ from tensorflow.python.util import tf_decorator from tensorflow.python.util import tf_export from tensorflow.tools.common import public_api from tensorflow.tools.common import traverse +from tensorflow.tools.compatibility import tf_upgrade_v2 _OUTPUT_FILE_PATH = 'third_party/tensorflow/tools/compatibility/renames_v2.py' @@ -71,6 +72,50 @@ _TENSORFLOW_CONSTANTS_ATTR_V1 = ( _TENSORFLOW_CONSTANTS_ATTR = ( tf_export.API_ATTRS[tf_export.TENSORFLOW_API_NAME].constants) +_ESTIMATOR_API_ATTR_V1 = ( + tf_export.API_ATTRS_V1[tf_export.ESTIMATOR_API_NAME].names) +_ESTIMATOR_API_ATTR = tf_export.API_ATTRS[tf_export.ESTIMATOR_API_NAME].names +_ESTIMATOR_CONSTANTS_ATTR_V1 = ( + tf_export.API_ATTRS_V1[tf_export.ESTIMATOR_API_NAME].constants) +_ESTIMATOR_CONSTANTS_ATTR = ( + tf_export.API_ATTRS[tf_export.ESTIMATOR_API_NAME].constants) + + +def get_v1_names(symbol): + names_v1 = [] + if hasattr(symbol, _TENSORFLOW_API_ATTR_V1): + names_v1.extend(getattr(symbol, _TENSORFLOW_API_ATTR_V1)) + if hasattr(symbol, _ESTIMATOR_API_ATTR_V1): + names_v1.extend(getattr(symbol, _ESTIMATOR_API_ATTR_V1)) + return names_v1 + + +def get_v2_names(symbol): + names_v2 = [] + if hasattr(symbol, _TENSORFLOW_API_ATTR): + names_v2.extend(getattr(symbol, _TENSORFLOW_API_ATTR)) + if hasattr(symbol, _ESTIMATOR_API_ATTR): + names_v2.extend(getattr(symbol, _ESTIMATOR_API_ATTR)) + return list(names_v2) + + +def get_v1_constants(module): + constants_v1 = [] + if hasattr(module, _TENSORFLOW_CONSTANTS_ATTR_V1): + constants_v1.extend(getattr(module, _TENSORFLOW_CONSTANTS_ATTR_V1)) + if hasattr(module, _ESTIMATOR_CONSTANTS_ATTR_V1): + constants_v1.extend(getattr(module, _ESTIMATOR_CONSTANTS_ATTR_V1)) + return constants_v1 + + +def get_v2_constants(module): + constants_v2 = [] + if hasattr(module, _TENSORFLOW_CONSTANTS_ATTR): + constants_v2.extend(getattr(module, _TENSORFLOW_CONSTANTS_ATTR)) + if hasattr(module, _ESTIMATOR_CONSTANTS_ATTR): + constants_v2.extend(getattr(module, _ESTIMATOR_CONSTANTS_ATTR)) + return constants_v2 + def get_canonical_name(v2_names, v1_name): if v2_names: @@ -78,18 +123,34 @@ def get_canonical_name(v2_names, v1_name): return 'compat.v1.%s' % v1_name +def get_all_v2_names(): + """Get a set of function/class names available in TensorFlow 2.0.""" + v2_names = set() # All op names in TensorFlow 2.0 + + def visit(unused_path, unused_parent, children): + """Visitor that collects TF 2.0 names.""" + for child in children: + _, attr = tf_decorator.unwrap(child[1]) + api_names_v2 = get_v2_names(attr) + for name in api_names_v2: + v2_names.add(name) + + visitor = public_api.PublicAPIVisitor(visit) + visitor.do_not_descend_map['tf'].append('contrib') + traverse.traverse(tf.compat.v2, visitor) + return v2_names + + def collect_constant_renames(): """Looks for constants that need to be renamed in TF 2.0. Returns: - List of tuples of the form (current name, new name). + Set of tuples of the form (current name, new name). """ renames = set() for module in sys.modules.values(): - if not hasattr(module, _TENSORFLOW_CONSTANTS_ATTR_V1): - continue - constants_v1_list = getattr(module, _TENSORFLOW_CONSTANTS_ATTR_V1) - constants_v2_list = getattr(module, _TENSORFLOW_CONSTANTS_ATTR) + constants_v1_list = get_v1_constants(module) + constants_v2_list = get_v2_constants(module) # _tf_api_constants attribute contains a list of tuples: # (api_names_list, constant_name) @@ -115,26 +176,21 @@ def collect_function_renames(): """Looks for functions/classes that need to be renamed in TF 2.0. Returns: - List of tuples of the form (current name, new name). + Set of tuples of the form (current name, new name). """ # Set of rename lines to write to output file in the form: # 'tf.deprecated_name': 'tf.canonical_name' renames = set() - v2_names = set() # All op names in TensorFlow 2.0 def visit(unused_path, unused_parent, children): """Visitor that collects rename strings to add to rename_line_set.""" for child in children: _, attr = tf_decorator.unwrap(child[1]) - if not hasattr(attr, '__dict__'): - continue - api_names_v1 = attr.__dict__.get(_TENSORFLOW_API_ATTR_V1, []) - api_names_v2 = attr.__dict__.get(_TENSORFLOW_API_ATTR, []) + api_names_v1 = get_v1_names(attr) + api_names_v2 = get_v2_names(attr) deprecated_api_names = set(api_names_v1) - set(api_names_v2) for name in deprecated_api_names: renames.add((name, get_canonical_name(api_names_v2, name))) - for name in api_names_v2: - v2_names.add(name) visitor = public_api.PublicAPIVisitor(visit) visitor.do_not_descend_map['tf'].append('contrib') @@ -144,8 +200,9 @@ def collect_function_renames(): # It is possible that a different function is exported with the # same name. For e.g. when creating a different function to # rename arguments. Exclude it from renames in this case. - renames = {name: new_name for name, new_name in renames.items() - if name not in v2_names} + v2_names = get_all_v2_names() + renames = set((name, new_name) for name, new_name in renames + if name not in v2_names) return renames @@ -163,12 +220,15 @@ def update_renames_v2(output_file_path): function_renames = collect_function_renames() constant_renames = collect_constant_renames() all_renames = function_renames.union(constant_renames) + manual_renames = set( + tf_upgrade_v2.TFAPIChangeSpec().manual_symbol_renames.keys()) # List of rename lines to write to output file in the form: # 'tf.deprecated_name': 'tf.canonical_name' rename_lines = [ get_rename_line(name, canonical_name) - for name, canonical_name in all_renames] + for name, canonical_name in all_renames + if 'tf.' + name not in manual_renames] renames_file_text = '%srenames = {\n%s\n}\n' % ( _FILE_HEADER, ',\n'.join(sorted(rename_lines))) file_io.write_string_to_file(output_file_path, renames_file_text) diff --git a/tensorflow/tools/docker/Dockerfile b/tensorflow/tools/docker/Dockerfile index 205128ad58..6676de02a4 100644 --- a/tensorflow/tools/docker/Dockerfile +++ b/tensorflow/tools/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:16.04 +FROM ubuntu:18.04 LABEL maintainer="Craig Citro " @@ -8,7 +8,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ curl \ libfreetype6-dev \ libhdf5-serial-dev \ - libpng12-dev \ + libpng-dev \ libzmq3-dev \ pkg-config \ python \ diff --git a/tensorflow/tools/docker/Dockerfile.devel b/tensorflow/tools/docker/Dockerfile.devel index a3893a2713..c256dd364e 100644 --- a/tensorflow/tools/docker/Dockerfile.devel +++ b/tensorflow/tools/docker/Dockerfile.devel @@ -1,4 +1,4 @@ -FROM ubuntu:16.04 +FROM ubuntu:18.04 LABEL maintainer="Craig Citro " @@ -9,7 +9,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libcurl3-dev \ libfreetype6-dev \ libhdf5-serial-dev \ - libpng12-dev \ + libpng-dev \ libzmq3-dev \ pkg-config \ python-dev \ diff --git a/tensorflow/tools/docker/Dockerfile.devel-mkl b/tensorflow/tools/docker/Dockerfile.devel-mkl index bd2883ddba..2341c0e8cc 100755 --- a/tensorflow/tools/docker/Dockerfile.devel-mkl +++ b/tensorflow/tools/docker/Dockerfile.devel-mkl @@ -1,4 +1,4 @@ -FROM ubuntu:16.04 +FROM ubuntu:18.04 LABEL maintainer="Clayne Robison " @@ -16,7 +16,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libcurl3-dev \ libfreetype6-dev \ libhdf5-serial-dev \ - libpng12-dev \ + libpng-dev \ libzmq3-dev \ libssl-dev \ pkg-config \ diff --git a/tensorflow/tools/docker/Dockerfile.devel-mkl-horovod b/tensorflow/tools/docker/Dockerfile.devel-mkl-horovod index df084e029c..5e24617b21 100755 --- a/tensorflow/tools/docker/Dockerfile.devel-mkl-horovod +++ b/tensorflow/tools/docker/Dockerfile.devel-mkl-horovod @@ -1,4 +1,4 @@ -FROM ubuntu:16.04 +FROM ubuntu:18.04 LABEL maintainer="Cong Xu " @@ -16,7 +16,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libcurl3-dev \ libfreetype6-dev \ libhdf5-serial-dev \ - libpng12-dev \ + libpng-dev \ libzmq3-dev \ pkg-config \ python-dev \ diff --git a/tensorflow/tools/docker/Dockerfile.mkl b/tensorflow/tools/docker/Dockerfile.mkl index ac41cffe4b..dad27697fa 100755 --- a/tensorflow/tools/docker/Dockerfile.mkl +++ b/tensorflow/tools/docker/Dockerfile.mkl @@ -1,4 +1,4 @@ -FROM ubuntu:16.04 +FROM ubuntu:18.04 LABEL maintainer="Clayne Robison " @@ -17,7 +17,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ curl \ libfreetype6-dev \ libhdf5-serial-dev \ - libpng12-dev \ + libpng-dev \ libzmq3-dev \ pkg-config \ ${PYTHON} \ diff --git a/tensorflow/tools/docker/Dockerfile.mkl-horovod b/tensorflow/tools/docker/Dockerfile.mkl-horovod index 0432cd5e80..19dc45c62c 100755 --- a/tensorflow/tools/docker/Dockerfile.mkl-horovod +++ b/tensorflow/tools/docker/Dockerfile.mkl-horovod @@ -1,4 +1,4 @@ -FROM ubuntu:16.04 +FROM ubuntu:18.04 LABEL maintainer="Cong Xu " @@ -17,7 +17,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ curl \ libfreetype6-dev \ libhdf5-serial-dev \ - libpng12-dev \ + libpng-dev \ libzmq3-dev \ pkg-config \ python \ diff --git a/tensorflow/tools/dockerfiles/.gitignore b/tensorflow/tools/dockerfiles/.gitignore new file mode 100644 index 0000000000..d7efa472a9 --- /dev/null +++ b/tensorflow/tools/dockerfiles/.gitignore @@ -0,0 +1 @@ +dockerfiles/*.temp.Dockerfile diff --git a/tensorflow/tools/dockerfiles/README.md b/tensorflow/tools/dockerfiles/README.md index 7c8ca1d1c7..2ac68666d0 100644 --- a/tensorflow/tools/dockerfiles/README.md +++ b/tensorflow/tools/dockerfiles/README.md @@ -1,8 +1,12 @@ # TensorFlow Dockerfiles -This directory houses TensorFlow's Dockerfiles. **DO NOT EDIT THE DOCKERFILES -MANUALLY!** They are maintained by `assembler.py`, which builds Dockerfiles from -the files in `partials/` and the rules in `spec.yml`. See [the Contributing +This directory houses TensorFlow's Dockerfiles and the infrastructure used to +create and deploy them to [Docker +Hub](https://hub.docker.com/r/tensorflow/tensorflow). + +**DO NOT EDIT THE DOCKERFILES/ DIRECTORY MANUALLY!** The files within are +maintained by `assembler.py`, which builds Dockerfiles from the files in +`partials/` and the rules in `spec.yml`. See [the Contributing section](#contributing) for more information. These Dockerfiles are planned to replace the Dockerfiles used to generate @@ -20,10 +24,10 @@ $ docker build -f ./dockerfiles/cpu.Dockerfile -t tf . Each Dockerfile has its own set of available `--build-arg`s which are documented in the Dockerfile itself. -## Running +## Running Locally Built Images After building the image with the tag `tf` (for example), use `docker run` to -run the images. Examples are below. +run the images. Note for new Docker users: the `-v` and `-u` flags share directories between the Docker container and your machine, and very important. Without @@ -42,8 +46,10 @@ $ docker run -u $(id -u):$(id -g) -v $(pwd):/my-devel -it tf # GPU-based images (set up nvidia-docker2 first) $ docker run --runtime=nvidia -u $(id -u):$(id -g) -v $(pwd):/my-devel -it tf -# Images with Jupyter run on port 8888, and needs a volume for notebooks -$ docker run --user $(id -u):$(id -g) -p 8888:8888 -v $(pwd):/notebooks -it tf +# Images with Jupyter run on port 8888 and need a volume for your notebooks +# You can change $(PWD) to the full path to a directory if your notebooks +# live outside the current directory. +$ docker run --user $(id -u):$(id -g) -p 8888:8888 -v $(PWD):/tf/notebooks -it tf ``` These images do not come with the TensorFlow source code -- but the development @@ -60,11 +66,32 @@ You can use the `Dockerfile` in this directory to build an editing environment that has all of the Python dependencies you'll need: ```bash -$ docker build -t tf-assembler -f assembler.Dockerfile . +# Build the tools-helper image so you can run the assembler +$ docker build -t tf-tools -f tools.Dockerfile . # Set --user to set correct permissions on generated files -$ docker run --user $(id -u):$(id -g) -it -v $(pwd):/tf tf-assembler bash +$ docker run --user $(id -u):$(id -g) -it -v $(pwd):/tf tf-tools bash + +# Next you can make a handy alias depending on what you're doing. When building +# Docker images, you need to run as root with docker.sock mounted so that the +# container can run Docker commands. When assembling Dockerfiles, though, you'll +# want to run as your user so that new files have the right permissions. + +# If you're BUILDING OR DEPLOYING DOCKER IMAGES, run as root with docker.sock: +$ alias asm_images="docker run --rm -v $(pwd):/tf -v /var/run/docker.sock:/var/run/docker.sock tf-tools python3 assembler.py " + +# If you're REBUILDING OR ADDING DOCKERFILES, remove docker.sock and add -u: +$ alias asm_dockerfiles="docker run --rm -u $(id -u):$(id -g) -v $(pwd):/tf tf-tools python3 assembler.py " + +# Check flags +$ asm_dockerfiles --help + +# Assemble all of the Dockerfiles +$ asm_dockerfiles --release ubuntu-dockerfiles --construct_dockerfiles + +# Build all of the "nightly" images on your local machine: +$ asm_images --release nightly --build_images -# In the container... -/tf $ python3 ./assembler.py -o dockerfiles -s spec.yml +# Build version release for version 99.0, except "gpu" tags: +$ asm_images --release versioned --arg _TAG_PREFIX=99.0 --build_images --exclude_tags_matching '*.gpu.*' ``` diff --git a/tensorflow/tools/dockerfiles/assembler.py b/tensorflow/tools/dockerfiles/assembler.py index 9cdd9bb0cb..9d8a59aebc 100644 --- a/tensorflow/tools/dockerfiles/assembler.py +++ b/tensorflow/tools/dockerfiles/assembler.py @@ -11,63 +11,144 @@ # 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. -# ============================================================================== -"""Assemble common TF Dockerfiles from many parts. +# ============================================================================ +"""Multipurpose TensorFlow Docker Helper. -This script constructs TF's Dockerfiles by aggregating partial -Dockerfiles. See README.md for usage examples. +- Assembles Dockerfiles +- Builds images (and optionally runs image tests) +- Pushes images to Docker Hub (provided with credentials) + +Read README.md (in this directory) for instructions! """ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import collections import copy import errno +import itertools +import multiprocessing import os -import os.path import re import shutil -import textwrap +import sys from absl import app from absl import flags import cerberus +import docker import yaml FLAGS = flags.FLAGS +flags.DEFINE_string('hub_username', None, + 'Dockerhub username, only used with --upload_to_hub') + +flags.DEFINE_string( + 'hub_password', None, + ('Dockerhub password, only used with --upload_to_hub. Use from an env param' + 'so your password isn\'t in your history.')) + +flags.DEFINE_integer('hub_timeout', 3600, + 'Abort Hub upload if it takes longer than this.') + +flags.DEFINE_string( + 'repository', 'tensorflow', + 'Tag local images as {repository}:tag (in addition to the ' + 'hub_repository, if uploading to hub)') + +flags.DEFINE_string( + 'hub_repository', None, + 'Push tags to this Docker Hub repository, e.g. tensorflow/tensorflow') + +flags.DEFINE_boolean( + 'upload_to_hub', + False, + ('Push built images to Docker Hub (you must also provide --hub_username, ' + '--hub_password, and --hub_repository)'), + short_name='u', +) + flags.DEFINE_boolean( - 'dry_run', False, 'Do not actually generate Dockerfiles', short_name='n') + 'construct_dockerfiles', False, 'Do not build images', short_name='d') + +flags.DEFINE_boolean( + 'keep_temp_dockerfiles', + False, + 'Retain .temp.Dockerfiles created while building images.', + short_name='k') + +flags.DEFINE_boolean( + 'build_images', False, 'Do not build images', short_name='b') flags.DEFINE_string( - 'spec_file', - './spec.yml', - 'Path to a YAML specification file', - short_name='s') + 'run_tests_path', None, + ('Execute test scripts on generated Dockerfiles before pushing them. ' + 'Flag value must be a full path to the "tests" directory, which is usually' + ' $(realpath ./tests). A failed tests counts the same as a failed build.')) + +flags.DEFINE_boolean( + 'stop_on_failure', False, + ('Stop processing tags if any one build fails. If False or not specified, ' + 'failures are reported but do not affect the other images.')) + +flags.DEFINE_boolean( + 'dry_run', + False, + 'Do not build or deploy anything at all.', + short_name='n', +) + +flags.DEFINE_string( + 'exclude_tags_matching', + None, + ('Regular expression that skips processing on any tag it matches. Must ' + 'match entire string, e.g. ".*gpu.*" ignores all GPU tags.'), + short_name='x') flags.DEFINE_string( - 'output_dir', - './dockerfiles', ('Path to an output directory for Dockerfiles. ' - 'Will be created if it doesn\'t exist.'), + 'only_tags_matching', + None, + ('Regular expression that skips processing on any tag it does not match. ' + 'Must match entire string, e.g. ".*gpu.*" includes only GPU tags.'), + short_name='i') + +flags.DEFINE_string( + 'dockerfile_dir', + './dockerfiles', 'Path to an output directory for Dockerfiles.' + ' Will be created if it doesn\'t exist.' + ' Existing files in this directory will be deleted when new Dockerfiles' + ' are made.', short_name='o') flags.DEFINE_string( 'partial_dir', './partials', - 'Path to a directory containing foo.partial.Dockerfile partial files.', + 'Path to a directory containing foo.partial.Dockerfile partial files.' + ' can have subdirectories, e.g. "bar/baz.partial.Dockerfile".', short_name='p') -flags.DEFINE_boolean( - 'quiet_dry_run', - True, - 'Do not print contents of dry run Dockerfiles.', - short_name='q') +flags.DEFINE_multi_string( + 'release', [], + 'Set of releases to build and tag. Defaults to every release type.', + short_name='r') -flags.DEFINE_boolean( - 'validate', True, 'Validate generated Dockerfiles', short_name='c') +flags.DEFINE_multi_string( + 'arg', [], + ('Extra build arguments. These are used for expanding tag names if needed ' + '(e.g. --arg _TAG_PREFIX=foo) and for using as build arguments (unused ' + 'args will print a warning).'), + short_name='a') -# Schema to verify the contents of spec.yml with Cerberus. +flags.DEFINE_string( + 'spec_file', + './spec.yml', + 'Path to the YAML specification file', + short_name='s') + +# Schema to verify the contents of tag-spec.yml with Cerberus. # Must be converted to a dict from yaml to work. # Note: can add python references with e.g. # !!python/name:builtins.str @@ -76,79 +157,76 @@ SCHEMA_TEXT = """ header: type: string -partials: +slice_sets: type: dict keyschema: type: string valueschema: - type: dict - schema: - desc: - type: string - args: + type: list + schema: type: dict - keyschema: - type: string - valueschema: - anyof: - - type: [ boolean, number, string ] - - type: dict - schema: - default: - type: [ boolean, number, string ] - desc: - type: string - options: - type: list - schema: - type: string - -images: + schema: + add_to_name: + type: string + dockerfile_exclusive_name: + type: string + partials: + type: list + schema: + type: string + ispartial: true + test_runtime: + type: string + required: false + tests: + type: list + default: [] + schema: + type: string + args: + type: list + default: [] + schema: + type: string + isfullarg: true + +releases: + type: dict keyschema: type: string valueschema: type: dict schema: - desc: - type: string - arg-defaults: - type: list - schema: - anyof: - - type: dict - keyschema: - type: string - arg_in_use: true - valueschema: - type: string - - type: string - isimage: true - create-dockerfile: + is_dockerfiles: + type: boolean + required: false + default: false + upload_images: type: boolean - partials: + required: false + default: true + tag_specs: type: list + required: true schema: - anyof: - - type: dict - keyschema: - type: string - regex: image - valueschema: - type: string - isimage: true - - type: string - ispartial: true + type: string """ -class TfDockerValidator(cerberus.Validator): - """Custom Cerberus validator for TF dockerfile spec. +class TfDockerTagValidator(cerberus.Validator): + """Custom Cerberus validator for TF tag spec. Note: Each _validate_foo function's docstring must end with a segment describing its own validation schema, e.g. "The rule's arguments are...". If you add a new validator, you can copy/paste that section. """ + def __init__(self, *args, **kwargs): + # See http://docs.python-cerberus.org/en/stable/customize.html + if 'partials' in kwargs: + self.partials = kwargs['partials'] + super(cerberus.Validator, self).__init__(*args, **kwargs) + def _validate_ispartial(self, ispartial, field, value): """Validate that a partial references an existing partial spec. @@ -156,398 +234,423 @@ class TfDockerValidator(cerberus.Validator): ispartial: Value of the rule, a bool field: The field being validated value: The field's value - The rule's arguments are validated against this schema: {'type': 'boolean'} """ - if ispartial and value not in self.root_document.get('partials', dict()): - self._error(field, '{} is not an existing partial.'.format(value)) + if ispartial and value not in self.partials: + self._error(field, + '{} is not present in the partials directory.'.format(value)) - def _validate_isimage(self, isimage, field, value): - """Validate that an image references an existing partial spec. + def _validate_isfullarg(self, isfullarg, field, value): + """Validate that a string is either a FULL=arg or NOT. Args: - isimage: Value of the rule, a bool + isfullarg: Value of the rule, a bool field: The field being validated value: The field's value - - The rule's arguments are validated against this schema: - {'type': 'boolean'} - """ - if isimage and value not in self.root_document.get('images', dict()): - self._error(field, '{} is not an existing image.'.format(value)) - - def _validate_arg_in_use(self, arg_in_use, field, value): - """Validate that an arg references an existing partial spec's args. - - Args: - arg_in_use: Value of the rule, a bool - field: The field being validated - value: The field's value - The rule's arguments are validated against this schema: {'type': 'boolean'} """ - if arg_in_use: - for partial in self.root_document.get('partials', dict()).values(): - if value in partial.get('args', tuple()): - return + if isfullarg and '=' not in value: + self._error(field, '{} should be of the form ARG=VALUE.'.format(value)) + if not isfullarg and '=' in value: + self._error(field, '{} should be of the form ARG (no =).'.format(value)) - self._error(field, '{} is not an arg used in any partial.'.format(value)) +def eprint(*args, **kwargs): + print(*args, file=sys.stderr, flush=True, **kwargs) -def build_partial_description(partial_spec): - """Create the documentation lines for a specific partial. - Generates something like this: +def aggregate_all_slice_combinations(spec, slice_set_names): + """Figure out all of the possible slice groupings for a tag spec.""" + slice_sets = copy.deepcopy(spec['slice_sets']) - # This is the partial's description, from spec.yml. - # --build-arg ARG_NAME=argdefault - # this is one of the args. - # --build-arg ANOTHER_ARG=(some|choices) - # another arg. + for name in slice_set_names: + for slice_set in slice_sets[name]: + slice_set['set_name'] = name - Args: - partial_spec: A dict representing one of the partials from spec.yml. Doesn't - include the name of the partial; is a dict like { desc: ..., args: ... }. + slices_grouped_but_not_keyed = [slice_sets[name] for name in slice_set_names] + all_slice_combos = list(itertools.product(*slices_grouped_but_not_keyed)) + return all_slice_combos - Returns: - A commented string describing this partial. - """ - # Start from linewrapped desc field - lines = [] - wrapper = textwrap.TextWrapper( - initial_indent='# ', subsequent_indent='# ', width=80) - description = wrapper.fill(partial_spec.get('desc', '( no comments )')) - lines.extend(['#', description]) - - # Document each arg - for arg, arg_data in partial_spec.get('args', dict()).items(): - # Wrap arg description with comment lines - desc = arg_data.get('desc', '( no description )') - desc = textwrap.fill( - desc, - initial_indent='# ', - subsequent_indent='# ', - width=80, - drop_whitespace=False) - - # Document (each|option|like|this) - if 'options' in arg_data: - arg_options = ' ({})'.format('|'.join(arg_data['options'])) - else: - arg_options = '' +def build_name_from_slices(format_string, slices, args, is_dockerfile=False): + """Build the tag name (cpu-devel...) from a list of slices.""" + name_formatter = copy.deepcopy(args) + name_formatter.update({s['set_name']: s['add_to_name'] for s in slices}) + name_formatter.update({ + s['set_name']: s['dockerfile_exclusive_name'] + for s in slices + if is_dockerfile and 'dockerfile_exclusive_name' in s + }) + name = format_string.format(**name_formatter) + return name - # Add usage sample - arg_use = '# --build-arg {}={}{}'.format(arg, - arg_data.get('default', '(unset)'), - arg_options) - lines.extend([arg_use, desc]) - return '\n'.join(lines) +def update_args_dict(args_dict, updater): + """Update a dict of arg values with more values from a list or dict.""" + if isinstance(updater, list): + for arg in updater: + key, sep, value = arg.partition('=') + if sep == '=': + args_dict[key] = value + if isinstance(updater, dict): + for key, value in updater.items(): + args_dict[key] = value + return args_dict -def construct_contents(partial_specs, image_spec): - """Assemble the dockerfile contents for an image spec. +def get_slice_sets_and_required_args(slice_sets, tag_spec): + """Extract used-slice-sets and required CLI arguments from a spec string. - It assembles a concrete list of partial references into a single, large - string. - Also expands argument defaults, so that the resulting Dockerfile doesn't have - to be configured with --build-arg=... every time. That is, any ARG directive - will be updated with a new default value. + For example, {FOO}{bar}{bat} finds FOO, bar, and bat. Assuming bar and bat + are both named slice sets, FOO must be specified on the command line. Args: - partial_specs: The dict from spec.yml["partials"]. - image_spec: One of the dict values from spec.yml["images"]. + slice_sets: Dict of named slice sets + tag_spec: The tag spec string, e.g. {_FOO}{blep} Returns: - A string containing a valid Dockerfile based on the partials listed in - image_spec. + (used_slice_sets, required_args), a tuple of lists """ - processed_partial_strings = [] - for partial_name in image_spec['partials']: - # Apply image arg-defaults to existing arg defaults - partial_spec = copy.deepcopy(partial_specs[partial_name]) - args = partial_spec.get('args', dict()) - for k_v in image_spec.get('arg-defaults', []): - arg, value = list(k_v.items())[0] - if arg in args: - args[arg]['default'] = value - - # Read partial file contents - filename = partial_spec.get('file', partial_name) - partial_path = os.path.join(FLAGS.partial_dir, - '{}.partial.Dockerfile'.format(filename)) - with open(partial_path, 'r') as f_partial: - partial_contents = f_partial.read() - - # Replace ARG FOO=BAR with ARG FOO=[new-default] - for arg, arg_data in args.items(): - if 'default' in arg_data and arg_data['default']: - default = '={}'.format(arg_data['default']) - else: - default = '' - partial_contents = re.sub(r'ARG {}.*'.format(arg), 'ARG {}{}'.format( - arg, default), partial_contents) - - # Store updated partial contents - processed_partial_strings.append(partial_contents) - - # Join everything together - return '\n'.join(processed_partial_strings) - - -def mkdir_p(path): - """Create a directory and its parents, even if it already exists.""" - try: - os.makedirs(path) - except OSError as e: - if e.errno != errno.EEXIST: - raise - - -def construct_documentation(header, partial_specs, image_spec): - """Assemble all of the documentation for a single dockerfile. - - Builds explanations of included partials and available build args. - - Args: - header: The string from spec.yml["header"]; will be commented and wrapped. - partial_specs: The dict from spec.yml["partials"]. - image_spec: The spec for the dockerfile being built. - - Returns: - A string containing a commented header that documents the contents of the - dockerfile. - - """ - # Comment and wrap header and image description - commented_header = '\n'.join( - [('# ' + l).rstrip() for l in header.splitlines()]) - commented_desc = '\n'.join( - ['# ' + l for l in image_spec.get('desc', '').splitlines()]) - partial_descriptions = [] - - # Build documentation for each partial in the image - for partial in image_spec['partials']: - # Copy partial data for default args unique to this image - partial_spec = copy.deepcopy(partial_specs[partial]) - args = partial_spec.get('args', dict()) - - # Overwrite any existing arg defaults - for k_v in image_spec.get('arg-defaults', []): - arg, value = list(k_v.items())[0] - if arg in args: - args[arg]['default'] = value - - # Build the description from new args - partial_description = build_partial_description(partial_spec) - partial_descriptions.append(partial_description) - - contents = [commented_header, '#', commented_desc] + partial_descriptions - return '\n'.join(contents) + '\n' - - -def normalize_partial_args(partial_specs): - """Normalize the shorthand form of a partial's args specification. - - Turns this: - - partial: - args: - SOME_ARG: arg_value - - Into this: - - partial: - args: - SOME_ARG: - default: arg_value + required_args = [] + used_slice_sets = [] + + extract_bracketed_words = re.compile(r'\{([^}]+)\}') + possible_args_or_slice_set_names = extract_bracketed_words.findall(tag_spec) + for name in possible_args_or_slice_set_names: + if name in slice_sets: + used_slice_sets.append(name) + else: + required_args.append(name) - Args: - partial_specs: The dict from spec.yml["partials"]. This dict is modified in - place. + return (used_slice_sets, required_args) - Returns: - The modified contents of partial_specs. - """ - for _, partial in partial_specs.items(): - args = partial.get('args', dict()) - for arg, value in args.items(): - if not isinstance(value, dict): - new_value = {'default': value} - args[arg] = new_value +def gather_tag_args(slices, cli_input_args, required_args): + """Build a dictionary of all the CLI and slice-specified args for a tag.""" + args = dict() - return partial_specs + for s in slices: + args = update_args_dict(args, s['args']) + args = update_args_dict(args, cli_input_args) + for arg in required_args: + if arg not in args: + eprint(('> Error: {} is not a valid slice_set, and also isn\'t an arg ' + 'provided on the command line. If it is an arg, please specify ' + 'it with --arg. If not, check the slice_sets list.'.format(arg))) + exit(1) -def flatten_args_references(image_specs): - """Resolve all default-args in each image spec to a concrete dict. + return args - Turns this: - example-image: - arg-defaults: - - MY_ARG: ARG_VALUE +def gather_slice_list_items(slices, key): + """For a list of slices, get the flattened list of all of a certain key.""" + return list(itertools.chain(*[s[key] for s in slices if key in s])) - another-example: - arg-defaults: - - ANOTHER_ARG: ANOTHER_VALUE - - example_image - Into this: +def find_first_slice_value(slices, key): + """For a list of slices, get the first value for a certain key.""" + for s in slices: + if key in s: + return s[key] - example-image: - arg-defaults: - - MY_ARG: ARG_VALUE - another-example: - arg-defaults: - - ANOTHER_ARG: ANOTHER_VALUE - - MY_ARG: ARG_VALUE +def assemble_tags(spec, cli_args, enabled_releases, all_partials): + """Gather all the tags based on our spec. Args: - image_specs: A dict of image_spec dicts; should be the contents of the - "images" key in the global spec.yaml. This dict is modified in place and - then returned. + spec: Nested dict containing full Tag spec + cli_args: List of ARG=foo arguments to pass along to Docker build + enabled_releases: List of releases to parse. Empty list = all + all_partials: Dict of every partial, for reference Returns: - The modified contents of image_specs. + Dict of tags and how to build them """ - for _, image_spec in image_specs.items(): - too_deep = 0 - while str in map(type, image_spec.get('arg-defaults', [])) and too_deep < 5: - new_args = [] - for arg in image_spec['arg-defaults']: - if isinstance(arg, str): - new_args.extend(image_specs[arg]['arg-defaults']) - else: - new_args.append(arg) + tag_data = collections.defaultdict(list) - image_spec['arg-defaults'] = new_args - too_deep += 1 + for name, release in spec['releases'].items(): + for tag_spec in release['tag_specs']: + if enabled_releases and name not in enabled_releases: + eprint('> Skipping release {}'.format(name)) + continue - return image_specs + used_slice_sets, required_cli_args = get_slice_sets_and_required_args( + spec['slice_sets'], tag_spec) + slice_combos = aggregate_all_slice_combinations(spec, used_slice_sets) + for slices in slice_combos: -def flatten_partial_references(image_specs): - """Resolve all partial references in each image spec to a concrete list. + tag_args = gather_tag_args(slices, cli_args, required_cli_args) + tag_name = build_name_from_slices(tag_spec, slices, tag_args, + release['is_dockerfiles']) + used_partials = gather_slice_list_items(slices, 'partials') + used_tests = gather_slice_list_items(slices, 'tests') + test_runtime = find_first_slice_value(slices, 'test_runtime') + dockerfile_contents = merge_partials(spec['header'], used_partials, + all_partials) - Turns this: + tag_data[tag_name].append({ + 'release': name, + 'tag_spec': tag_spec, + 'is_dockerfiles': release['is_dockerfiles'], + 'upload_images': release['upload_images'], + 'cli_args': tag_args, + 'partials': used_partials, + 'tests': used_tests, + 'test_runtime': test_runtime, + 'dockerfile_contents': dockerfile_contents, + }) - example-image: - partials: - - foo + return tag_data - another-example: - partials: - - bar - - image: example-image - - bat - Into this: +def merge_partials(header, used_partials, all_partials): + """Merge all partial contents with their header.""" + used_partials = list(used_partials) + return '\n'.join([header] + [all_partials[u] for u in used_partials]) - example-image: - partials: - - foo - another-example: - partials: - - bar - - foo - - bat - Args: - image_specs: A dict of image_spec dicts; should be the contents of the - "images" key in the global spec.yaml. This dict is modified in place and - then returned. - - Returns: - The modified contents of image_specs. - """ - for _, image_spec in image_specs.items(): - too_deep = 0 - while dict in map(type, image_spec['partials']) and too_deep < 5: - new_partials = [] - for partial in image_spec['partials']: - if isinstance(partial, str): - new_partials.append(partial) - else: - new_partials.extend(image_specs[partial['image']]['partials']) +def upload_in_background(hub_repository, dock, image, tag): + """Upload a docker image (to be used by multiprocessing).""" + image.tag(hub_repository, tag=tag) + for line in list(dock.images.push(hub_repository, tag=tag, stream=True)): + print(line) - image_spec['partials'] = new_partials - too_deep += 1 - return image_specs +def mkdir_p(path): + """Create a directory and its parents, even if it already exists.""" + try: + os.makedirs(path) + except OSError as e: + if e.errno != errno.EEXIST: + raise -def construct_dockerfiles(tf_spec): - """Generate a mapping of {"cpu": , ...}. +def gather_existing_partials(partial_path): + """Find and read all available partials. Args: - tf_spec: The full spec.yml loaded as a python object. + partial_path (string): read partials from this directory. Returns: - A string:string dict of short names ("cpu-devel") to Dockerfile contents. + Dict[string, string] of partial short names (like "ubuntu/python" or + "bazel") to the full contents of that partial. """ - names_to_contents = dict() - image_specs = tf_spec['images'] - image_specs = flatten_partial_references(image_specs) - image_specs = flatten_args_references(image_specs) - partial_specs = tf_spec['partials'] - partial_specs = normalize_partial_args(partial_specs) - - for name, image_spec in image_specs.items(): - if not image_spec.get('create-dockerfile', True): - continue - documentation = construct_documentation(tf_spec['header'], partial_specs, - image_spec) - contents = construct_contents(partial_specs, image_spec) - names_to_contents[name] = '\n'.join([documentation, contents]) - - return names_to_contents + partials = dict() + for path, _, files in os.walk(partial_path): + for name in files: + fullpath = os.path.join(path, name) + if '.partial.Dockerfile' not in fullpath: + eprint(('> Probably not a problem: skipping {}, which is not a ' + 'partial.').format(fullpath)) + continue + # partial_dir/foo/bar.partial.Dockerfile -> foo/bar + simple_name = fullpath[len(partial_path) + 1:-len('.partial.dockerfile')] + with open(fullpath, 'r') as f: + partial_contents = f.read() + partials[simple_name] = partial_contents + return partials def main(argv): if len(argv) > 1: - raise app.UsageError('Unexpected command line args found: {}'.format(argv)) + raise app.UsageError('Too many command-line arguments.') + # Read the full spec file, used for everything with open(FLAGS.spec_file, 'r') as spec_file: - tf_spec = yaml.load(spec_file) + tag_spec = yaml.load(spec_file) + + # Get existing partial contents + partials = gather_existing_partials(FLAGS.partial_dir) # Abort if spec.yaml is invalid - if FLAGS.validate: - schema = yaml.load(SCHEMA_TEXT) - v = TfDockerValidator(schema) - if not v.validate(tf_spec): - print('>> ERROR: {} is an invalid spec! The errors are:'.format( - FLAGS.spec_file)) - print(yaml.dump(v.errors, indent=2)) + schema = yaml.load(SCHEMA_TEXT) + v = TfDockerTagValidator(schema, partials=partials) + if not v.validate(tag_spec): + eprint('> Error: {} is an invalid spec! The errors are:'.format( + FLAGS.spec_file)) + eprint(yaml.dump(v.errors, indent=2)) + exit(1) + tag_spec = v.normalized(tag_spec) + + # Assemble tags and images used to build them + all_tags = assemble_tags(tag_spec, FLAGS.arg, FLAGS.release, partials) + + # Empty Dockerfile directory if building new Dockerfiles + if FLAGS.construct_dockerfiles: + eprint('> Emptying Dockerfile dir "{}"'.format(FLAGS.dockerfile_dir)) + shutil.rmtree(FLAGS.dockerfile_dir, ignore_errors=True) + mkdir_p(FLAGS.dockerfile_dir) + + # Set up Docker helper + dock = docker.from_env() + + # Login to Docker if uploading images + if FLAGS.upload_to_hub: + if not FLAGS.hub_username: + eprint('> Error: please set --hub_username when uploading to Dockerhub.') exit(1) - else: - print('>> WARNING: Not validating {}'.format(FLAGS.spec_file)) - - # Generate mapping of { "cpu-devel": "", ... } - names_to_contents = construct_dockerfiles(tf_spec) - - # Write each completed Dockerfile - if not FLAGS.dry_run: - print('>> Emptying destination dir "{}"'.format(FLAGS.output_dir)) - shutil.rmtree(FLAGS.output_dir, ignore_errors=True) - mkdir_p(FLAGS.output_dir) - else: - print('>> Skipping creation of {} (dry run)'.format(FLAGS.output_dir)) - for name, contents in names_to_contents.items(): - path = os.path.join(FLAGS.output_dir, name + '.Dockerfile') - if FLAGS.dry_run: - print('>> Skipping writing contents of {} (dry run)'.format(path)) - print(contents) - else: - mkdir_p(FLAGS.output_dir) - print('>> Writing {}'.format(path)) - with open(path, 'w') as f: - f.write(contents) + if not FLAGS.hub_repository: + eprint( + '> Error: please set --hub_repository when uploading to Dockerhub.') + exit(1) + if not FLAGS.hub_password: + eprint('> Error: please set --hub_password when uploading to Dockerhub.') + exit(1) + dock.login( + username=FLAGS.hub_username, + password=FLAGS.hub_password, + ) + + # Each tag has a name ('tag') and a definition consisting of the contents + # of its Dockerfile, its build arg list, etc. + failed_tags = [] + for tag, tag_defs in all_tags.items(): + for tag_def in tag_defs: + eprint('> Working on {}'.format(tag)) + + if FLAGS.exclude_tags_matching and re.match(FLAGS.exclude_tags_matching, + tag): + eprint('>> Excluded due to match against "{}".'.format( + FLAGS.exclude_tags_matching)) + continue + + if FLAGS.only_tags_matching and not re.match(FLAGS.only_tags_matching, + tag): + eprint('>> Excluded due to failure to match against "{}".'.format( + FLAGS.only_tags_matching)) + continue + + # Write releases marked "is_dockerfiles" into the Dockerfile directory + if FLAGS.construct_dockerfiles: + path = os.path.join(FLAGS.dockerfile_dir, tag + '.Dockerfile') + if tag_def['is_dockerfiles']: + eprint('>> Writing {}...'.format(path)) + if not FLAGS.dry_run: + with open(path, 'w') as f: + f.write(tag_def['dockerfile_contents']) + + # Don't build any images for dockerfile-only releases + if not FLAGS.build_images: + continue + + # Generate a temporary Dockerfile to use to build, since docker-py + # needs a filepath relative to the build context (i.e. the current + # directory) + dockerfile = os.path.join(FLAGS.dockerfile_dir, tag + '.temp.Dockerfile') + if not FLAGS.dry_run: + with open(dockerfile, 'w') as f: + f.write(tag_def['dockerfile_contents']) + eprint('>> (Temporary) writing {}...'.format(dockerfile)) + + repo_tag = '{}:{}'.format(FLAGS.repository, tag) + eprint('>> Building {} using build args:'.format(repo_tag)) + for arg, value in tag_def['cli_args'].items(): + eprint('>>> {}={}'.format(arg, value)) + + # Note that we are NOT using cache_from, which appears to limit + # available cache layers to those from explicitly specified layers. Many + # of our layers are similar between local builds, so we want to use the + # implied local build cache. + tag_failed = False + image, logs = None, [] + if not FLAGS.dry_run: + try: + image, logs = dock.images.build( + timeout=FLAGS.hub_timeout, + path='.', + dockerfile=dockerfile, + buildargs=tag_def['cli_args'], + tag=repo_tag) + + # Print logs after finishing + log_lines = [l.get('stream', '') for l in logs] + eprint(''.join(log_lines)) + + # Run tests if requested, and dump output + # Could be improved by backgrounding, but would need better + # multiprocessing support to track failures properly. + if FLAGS.run_tests_path: + if not tag_def['tests']: + eprint('>>> No tests to run.') + for test in tag_def['tests']: + eprint('>> Testing {}...'.format(test)) + container, = dock.containers.run( + image, + '/tests/' + test, + working_dir='/', + log_config={'type': 'journald'}, + detach=True, + stderr=True, + stdout=True, + volumes={FLAGS.run_tests_path: + {'bind': '/tests', 'mode': 'ro'}}, + runtime=tag_def['test_runtime']), + ret = container.wait() + code = ret['StatusCode'] + out = container.logs(stdout=True, stderr=False) + err = container.logs(stdout=False, stderr=True) + container.remove() + if out: + eprint('>>> Output stdout:') + eprint(out.decode('utf-8')) + else: + eprint('>>> No test standard out.') + if err: + eprint('>>> Output stderr:') + eprint(out.decode('utf-8')) + else: + eprint('>>> No test standard err.') + if code != 0: + eprint('>> {} failed tests with status: "{}"'.format( + repo_tag, code)) + failed_tags.append(tag) + tag_failed = True + if FLAGS.stop_on_failure: + eprint('>> ABORTING due to --stop_on_failure!') + exit(1) + else: + eprint('>> Tests look good!') + + except docker.errors.BuildError as e: + eprint('>> {} failed to build with message: "{}"'.format( + repo_tag, e.msg)) + eprint('>> Build logs follow:') + log_lines = [l.get('stream', '') for l in e.build_log] + eprint(''.join(log_lines)) + failed_tags.append(tag) + tag_failed = True + if FLAGS.stop_on_failure: + eprint('>> ABORTING due to --stop_on_failure!') + exit(1) + + # Clean temporary dockerfiles if they were created earlier + if not FLAGS.keep_temp_dockerfiles: + os.remove(dockerfile) + + # Upload new images to DockerHub as long as they built + passed tests + if FLAGS.upload_to_hub: + if not tag_def['upload_images']: + continue + if tag_failed: + continue + + eprint('>> Uploading to {}:{}'.format(FLAGS.hub_repository, tag)) + if not FLAGS.dry_run: + p = multiprocessing.Process( + target=upload_in_background, + args=(FLAGS.hub_repository, dock, image, tag)) + p.start() + + if failed_tags: + eprint( + '> Some tags failed to build or failed testing, check scrollback for ' + 'errors: {}'.format( + ','.join(failed_tags))) + exit(1) if __name__ == '__main__': diff --git a/tensorflow/tools/dockerfiles/dockerfiles/cpu-devel-jupyter.Dockerfile b/tensorflow/tools/dockerfiles/dockerfiles/cpu-devel-jupyter.Dockerfile index dab7178db3..14ddf08199 100644 --- a/tensorflow/tools/dockerfiles/dockerfiles/cpu-devel-jupyter.Dockerfile +++ b/tensorflow/tools/dockerfiles/dockerfiles/cpu-devel-jupyter.Dockerfile @@ -16,27 +16,12 @@ # THIS IS A GENERATED DOCKERFILE. # # This file was assembled from multiple pieces, whose use is documented -# below. Please refer to the the TensorFlow dockerfiles documentation for -# more information. Build args are documented as their default value. -# -# Ubuntu-based, CPU-only environment for developing changes for TensorFlow, with Jupyter included. -# -# Start from Ubuntu, with TF development packages (no GPU support) -# --build-arg UBUNTU_VERSION=16.04 -# ( no description ) -# -# Python is required for TensorFlow and other libraries. -# --build-arg USE_PYTHON_3_NOT_2=True -# Install python 3 over Python 2 -# -# Install the latest version of Bazel and Python development tools. -# -# Configure TensorFlow's shell prompt and login tools. -# -# Launch Jupyter on execution instead of a bash prompt. +# throughout. Please refer to the TensorFlow dockerfiles documentation +# for more information. ARG UBUNTU_VERSION=16.04 -FROM ubuntu:${UBUNTU_VERSION} + +FROM ubuntu:${UBUNTU_VERSION} AS base RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ @@ -48,7 +33,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libpng12-dev \ libzmq3-dev \ pkg-config \ - python-dev \ rsync \ software-properties-common \ unzip \ @@ -59,8 +43,11 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* + +ENV CI_BUILD_PYTHON python + -ARG USE_PYTHON_3_NOT_2=True +ARG USE_PYTHON_3_NOT_2 ARG _PY_SUFFIX=${USE_PYTHON_3_NOT_2:+3} ARG PYTHON=python${_PY_SUFFIX} ARG PIP=pip${_PY_SUFFIX} @@ -72,10 +59,13 @@ RUN apt-get update && apt-get install -y \ ${PYTHON} \ ${PYTHON}-pip -RUN ${PIP} install --upgrade \ +RUN ${PIP} --no-cache-dir install --upgrade \ pip \ setuptools +# Some TF tools expect a "python" binary +RUN ln -s $(which ${PYTHON}) /usr/local/bin/python + RUN apt-get update && apt-get install -y \ build-essential \ curl \ @@ -84,6 +74,20 @@ RUN apt-get update && apt-get install -y \ ${PYTHON}-dev \ swig +RUN ${PIP} --no-cache-dir install \ + Pillow \ + h5py \ + keras_applications \ + keras_preprocessing \ + matplotlib \ + mock \ + numpy \ + scipy \ + sklearn \ + pandas \ + && test "${USE_PYTHON_3_NOT_2}" -eq 1 && true || ${PIP} --no-cache-dir install \ + enum34 + # Install bazel RUN echo "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8" | tee /etc/apt/sources.list.d/bazel.list && \ curl https://bazel.build/bazel-release.pub.gpg | apt-key add - && \ @@ -93,11 +97,19 @@ RUN echo "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8 COPY bashrc /etc/bash.bashrc RUN chmod a+rwx /etc/bash.bashrc -RUN ${PIP} install jupyter +RUN ${PIP} install jupyter matplotlib -RUN mkdir /notebooks && chmod a+rwx /notebooks +RUN mkdir -p /tf/tensorflow-tutorials && chmod -R a+rwx /tf/ RUN mkdir /.local && chmod a+rwx /.local -WORKDIR /notebooks +RUN apt-get install -y --no-install-recommends wget +WORKDIR /tf/tensorflow-tutorials +RUN wget https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/keras/basic_classification.ipynb +RUN wget https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/keras/basic_text_classification.ipynb +COPY readme-for-jupyter.md README.md +RUN apt-get autoremove -y && apt-get remove -y wget +WORKDIR /tf EXPOSE 8888 -CMD ["bash", "-c", "source /etc/bash.bashrc && jupyter notebook --notebook-dir=/notebooks --ip 0.0.0.0 --no-browser --allow-root"] +RUN ${PYTHON} -m ipykernel.kernelspec + +CMD ["bash", "-c", "source /etc/bash.bashrc && jupyter notebook --notebook-dir=/tf --ip 0.0.0.0 --no-browser --allow-root"] diff --git a/tensorflow/tools/dockerfiles/dockerfiles/cpu-devel.Dockerfile b/tensorflow/tools/dockerfiles/dockerfiles/cpu-devel.Dockerfile index 68566ccc8a..16973b47af 100644 --- a/tensorflow/tools/dockerfiles/dockerfiles/cpu-devel.Dockerfile +++ b/tensorflow/tools/dockerfiles/dockerfiles/cpu-devel.Dockerfile @@ -16,25 +16,12 @@ # THIS IS A GENERATED DOCKERFILE. # # This file was assembled from multiple pieces, whose use is documented -# below. Please refer to the the TensorFlow dockerfiles documentation for -# more information. Build args are documented as their default value. -# -# Ubuntu-based, CPU-only environment for developing changes for TensorFlow. -# -# Start from Ubuntu, with TF development packages (no GPU support) -# --build-arg UBUNTU_VERSION=16.04 -# ( no description ) -# -# Python is required for TensorFlow and other libraries. -# --build-arg USE_PYTHON_3_NOT_2=True -# Install python 3 over Python 2 -# -# Install the latest version of Bazel and Python development tools. -# -# Configure TensorFlow's shell prompt and login tools. +# throughout. Please refer to the TensorFlow dockerfiles documentation +# for more information. ARG UBUNTU_VERSION=16.04 -FROM ubuntu:${UBUNTU_VERSION} + +FROM ubuntu:${UBUNTU_VERSION} AS base RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ @@ -46,7 +33,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libpng12-dev \ libzmq3-dev \ pkg-config \ - python-dev \ rsync \ software-properties-common \ unzip \ @@ -57,8 +43,11 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* + +ENV CI_BUILD_PYTHON python + -ARG USE_PYTHON_3_NOT_2=True +ARG USE_PYTHON_3_NOT_2 ARG _PY_SUFFIX=${USE_PYTHON_3_NOT_2:+3} ARG PYTHON=python${_PY_SUFFIX} ARG PIP=pip${_PY_SUFFIX} @@ -70,10 +59,13 @@ RUN apt-get update && apt-get install -y \ ${PYTHON} \ ${PYTHON}-pip -RUN ${PIP} install --upgrade \ +RUN ${PIP} --no-cache-dir install --upgrade \ pip \ setuptools +# Some TF tools expect a "python" binary +RUN ln -s $(which ${PYTHON}) /usr/local/bin/python + RUN apt-get update && apt-get install -y \ build-essential \ curl \ @@ -82,6 +74,20 @@ RUN apt-get update && apt-get install -y \ ${PYTHON}-dev \ swig +RUN ${PIP} --no-cache-dir install \ + Pillow \ + h5py \ + keras_applications \ + keras_preprocessing \ + matplotlib \ + mock \ + numpy \ + scipy \ + sklearn \ + pandas \ + && test "${USE_PYTHON_3_NOT_2}" -eq 1 && true || ${PIP} --no-cache-dir install \ + enum34 + # Install bazel RUN echo "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8" | tee /etc/apt/sources.list.d/bazel.list && \ curl https://bazel.build/bazel-release.pub.gpg | apt-key add - && \ diff --git a/tensorflow/tools/dockerfiles/dockerfiles/cpu-jupyter.Dockerfile b/tensorflow/tools/dockerfiles/dockerfiles/cpu-jupyter.Dockerfile index f889ed6f91..d8fabadec2 100644 --- a/tensorflow/tools/dockerfiles/dockerfiles/cpu-jupyter.Dockerfile +++ b/tensorflow/tools/dockerfiles/dockerfiles/cpu-jupyter.Dockerfile @@ -16,31 +16,14 @@ # THIS IS A GENERATED DOCKERFILE. # # This file was assembled from multiple pieces, whose use is documented -# below. Please refer to the the TensorFlow dockerfiles documentation for -# more information. Build args are documented as their default value. -# -# Ubuntu-based, CPU-only environment for using TensorFlow, with Jupyter included. -# -# Start from Ubuntu (no GPU support) -# --build-arg UBUNTU_VERSION=16.04 -# ( no description ) -# -# Python is required for TensorFlow and other libraries. -# --build-arg USE_PYTHON_3_NOT_2=True -# Install python 3 over Python 2 -# -# Install the TensorFlow Python package. -# --build-arg TF_PACKAGE=tensorflow (tensorflow|tensorflow-gpu|tf-nightly|tf-nightly-gpu) -# The specific TensorFlow Python package to install -# -# Configure TensorFlow's shell prompt and login tools. -# -# Launch Jupyter on execution instead of a bash prompt. +# throughout. Please refer to the TensorFlow dockerfiles documentation +# for more information. ARG UBUNTU_VERSION=16.04 -FROM ubuntu:${UBUNTU_VERSION} -ARG USE_PYTHON_3_NOT_2=True +FROM ubuntu:${UBUNTU_VERSION} as base + +ARG USE_PYTHON_3_NOT_2 ARG _PY_SUFFIX=${USE_PYTHON_3_NOT_2:+3} ARG PYTHON=python${_PY_SUFFIX} ARG PIP=pip${_PY_SUFFIX} @@ -52,21 +35,37 @@ RUN apt-get update && apt-get install -y \ ${PYTHON} \ ${PYTHON}-pip -RUN ${PIP} install --upgrade \ +RUN ${PIP} --no-cache-dir install --upgrade \ pip \ setuptools +# Some TF tools expect a "python" binary +RUN ln -s $(which ${PYTHON}) /usr/local/bin/python + +# Options: +# tensorflow +# tensorflow-gpu +# tf-nightly +# tf-nightly-gpu ARG TF_PACKAGE=tensorflow RUN ${PIP} install ${TF_PACKAGE} COPY bashrc /etc/bash.bashrc RUN chmod a+rwx /etc/bash.bashrc -RUN ${PIP} install jupyter +RUN ${PIP} install jupyter matplotlib -RUN mkdir /notebooks && chmod a+rwx /notebooks +RUN mkdir -p /tf/tensorflow-tutorials && chmod -R a+rwx /tf/ RUN mkdir /.local && chmod a+rwx /.local -WORKDIR /notebooks +RUN apt-get install -y --no-install-recommends wget +WORKDIR /tf/tensorflow-tutorials +RUN wget https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/keras/basic_classification.ipynb +RUN wget https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/keras/basic_text_classification.ipynb +COPY readme-for-jupyter.md README.md +RUN apt-get autoremove -y && apt-get remove -y wget +WORKDIR /tf EXPOSE 8888 -CMD ["bash", "-c", "source /etc/bash.bashrc && jupyter notebook --notebook-dir=/notebooks --ip 0.0.0.0 --no-browser --allow-root"] +RUN ${PYTHON} -m ipykernel.kernelspec + +CMD ["bash", "-c", "source /etc/bash.bashrc && jupyter notebook --notebook-dir=/tf --ip 0.0.0.0 --no-browser --allow-root"] diff --git a/tensorflow/tools/dockerfiles/dockerfiles/cpu.Dockerfile b/tensorflow/tools/dockerfiles/dockerfiles/cpu.Dockerfile index 182a534bed..857b5e2047 100644 --- a/tensorflow/tools/dockerfiles/dockerfiles/cpu.Dockerfile +++ b/tensorflow/tools/dockerfiles/dockerfiles/cpu.Dockerfile @@ -16,29 +16,14 @@ # THIS IS A GENERATED DOCKERFILE. # # This file was assembled from multiple pieces, whose use is documented -# below. Please refer to the the TensorFlow dockerfiles documentation for -# more information. Build args are documented as their default value. -# -# Ubuntu-based, CPU-only environment for using TensorFlow -# -# Start from Ubuntu (no GPU support) -# --build-arg UBUNTU_VERSION=16.04 -# ( no description ) -# -# Python is required for TensorFlow and other libraries. -# --build-arg USE_PYTHON_3_NOT_2=True -# Install python 3 over Python 2 -# -# Install the TensorFlow Python package. -# --build-arg TF_PACKAGE=tensorflow (tensorflow|tensorflow-gpu|tf-nightly|tf-nightly-gpu) -# The specific TensorFlow Python package to install -# -# Configure TensorFlow's shell prompt and login tools. +# throughout. Please refer to the TensorFlow dockerfiles documentation +# for more information. ARG UBUNTU_VERSION=16.04 -FROM ubuntu:${UBUNTU_VERSION} -ARG USE_PYTHON_3_NOT_2=True +FROM ubuntu:${UBUNTU_VERSION} as base + +ARG USE_PYTHON_3_NOT_2 ARG _PY_SUFFIX=${USE_PYTHON_3_NOT_2:+3} ARG PYTHON=python${_PY_SUFFIX} ARG PIP=pip${_PY_SUFFIX} @@ -50,10 +35,18 @@ RUN apt-get update && apt-get install -y \ ${PYTHON} \ ${PYTHON}-pip -RUN ${PIP} install --upgrade \ +RUN ${PIP} --no-cache-dir install --upgrade \ pip \ setuptools +# Some TF tools expect a "python" binary +RUN ln -s $(which ${PYTHON}) /usr/local/bin/python + +# Options: +# tensorflow +# tensorflow-gpu +# tf-nightly +# tf-nightly-gpu ARG TF_PACKAGE=tensorflow RUN ${PIP} install ${TF_PACKAGE} diff --git a/tensorflow/tools/dockerfiles/dockerfiles/nvidia-devel-jupyter.Dockerfile b/tensorflow/tools/dockerfiles/dockerfiles/gpu-devel-jupyter.Dockerfile similarity index 66% rename from tensorflow/tools/dockerfiles/dockerfiles/nvidia-devel-jupyter.Dockerfile rename to tensorflow/tools/dockerfiles/dockerfiles/gpu-devel-jupyter.Dockerfile index 17faa84a68..9ecaec38c2 100644 --- a/tensorflow/tools/dockerfiles/dockerfiles/nvidia-devel-jupyter.Dockerfile +++ b/tensorflow/tools/dockerfiles/dockerfiles/gpu-devel-jupyter.Dockerfile @@ -16,28 +16,12 @@ # THIS IS A GENERATED DOCKERFILE. # # This file was assembled from multiple pieces, whose use is documented -# below. Please refer to the the TensorFlow dockerfiles documentation for -# more information. Build args are documented as their default value. -# -# Ubuntu-based, Nvidia-GPU-enabled environment for developing changes for TensorFlow, with Jupyter included. -# -# Start from Nvidia's Ubuntu base image with CUDA and CuDNN, with TF development -# packages. -# --build-arg UBUNTU_VERSION=16.04 -# ( no description ) -# -# Python is required for TensorFlow and other libraries. -# --build-arg USE_PYTHON_3_NOT_2=True -# Install python 3 over Python 2 -# -# Install the latest version of Bazel and Python development tools. -# -# Configure TensorFlow's shell prompt and login tools. -# -# Launch Jupyter on execution instead of a bash prompt. +# throughout. Please refer to the TensorFlow dockerfiles documentation +# for more information. ARG UBUNTU_VERSION=16.04 -FROM nvidia/cuda:9.0-base-ubuntu${UBUNTU_VERSION} + +FROM nvidia/cuda:9.0-base-ubuntu${UBUNTU_VERSION} as base RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ @@ -60,6 +44,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libpng12-dev \ libzmq3-dev \ pkg-config \ + python-dev \ rsync \ software-properties-common \ unzip \ @@ -82,11 +67,19 @@ RUN mkdir /usr/local/cuda-9.0/lib && \ ln -s /usr/lib/x86_64-linux-gnu/libnccl.so.2 /usr/local/cuda/lib/libnccl.so.2 && \ ln -s /usr/include/nccl.h /usr/local/cuda/include/nccl.h -# TODO(tobyboyd): Remove after license is excluded from BUILD file. -RUN gunzip /usr/share/doc/libnccl2/NCCL-SLA.txt.gz && \ - cp /usr/share/doc/libnccl2/NCCL-SLA.txt /usr/local/cuda/ +# Configure the build for our CUDA configuration. +ENV CI_BUILD_PYTHON python +ENV LD_LIBRARY_PATH /usr/local/cuda/extras/CUPTI/lib64:$LD_LIBRARY_PATH +ENV TF_NEED_CUDA 1 +ENV TF_NEED_TENSORRT 1 +ENV TF_CUDA_COMPUTE_CAPABILITIES=3.5,5.2,6.0,6.1,7.0 +ENV TF_CUDA_VERSION=9.0 +ENV TF_CUDNN_VERSION=7 + +# NCCL 2.x +ENV TF_NCCL_VERSION=2 -ARG USE_PYTHON_3_NOT_2=True +ARG USE_PYTHON_3_NOT_2 ARG _PY_SUFFIX=${USE_PYTHON_3_NOT_2:+3} ARG PYTHON=python${_PY_SUFFIX} ARG PIP=pip${_PY_SUFFIX} @@ -98,10 +91,13 @@ RUN apt-get update && apt-get install -y \ ${PYTHON} \ ${PYTHON}-pip -RUN ${PIP} install --upgrade \ +RUN ${PIP} --no-cache-dir install --upgrade \ pip \ setuptools +# Some TF tools expect a "python" binary +RUN ln -s $(which ${PYTHON}) /usr/local/bin/python + RUN apt-get update && apt-get install -y \ build-essential \ curl \ @@ -110,6 +106,20 @@ RUN apt-get update && apt-get install -y \ ${PYTHON}-dev \ swig +RUN ${PIP} --no-cache-dir install \ + Pillow \ + h5py \ + keras_applications \ + keras_preprocessing \ + matplotlib \ + mock \ + numpy \ + scipy \ + sklearn \ + pandas \ + && test "${USE_PYTHON_3_NOT_2}" -eq 1 && true || ${PIP} --no-cache-dir install \ + enum34 + # Install bazel RUN echo "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8" | tee /etc/apt/sources.list.d/bazel.list && \ curl https://bazel.build/bazel-release.pub.gpg | apt-key add - && \ @@ -119,11 +129,19 @@ RUN echo "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8 COPY bashrc /etc/bash.bashrc RUN chmod a+rwx /etc/bash.bashrc -RUN ${PIP} install jupyter +RUN ${PIP} install jupyter matplotlib -RUN mkdir /notebooks && chmod a+rwx /notebooks +RUN mkdir -p /tf/tensorflow-tutorials && chmod -R a+rwx /tf/ RUN mkdir /.local && chmod a+rwx /.local -WORKDIR /notebooks +RUN apt-get install -y --no-install-recommends wget +WORKDIR /tf/tensorflow-tutorials +RUN wget https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/keras/basic_classification.ipynb +RUN wget https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/keras/basic_text_classification.ipynb +COPY readme-for-jupyter.md README.md +RUN apt-get autoremove -y && apt-get remove -y wget +WORKDIR /tf EXPOSE 8888 -CMD ["bash", "-c", "source /etc/bash.bashrc && jupyter notebook --notebook-dir=/notebooks --ip 0.0.0.0 --no-browser --allow-root"] +RUN ${PYTHON} -m ipykernel.kernelspec + +CMD ["bash", "-c", "source /etc/bash.bashrc && jupyter notebook --notebook-dir=/tf --ip 0.0.0.0 --no-browser --allow-root"] diff --git a/tensorflow/tools/dockerfiles/dockerfiles/nvidia-devel.Dockerfile b/tensorflow/tools/dockerfiles/dockerfiles/gpu-devel.Dockerfile similarity index 76% rename from tensorflow/tools/dockerfiles/dockerfiles/nvidia-devel.Dockerfile rename to tensorflow/tools/dockerfiles/dockerfiles/gpu-devel.Dockerfile index a3ba02a684..c79bc3cf4c 100644 --- a/tensorflow/tools/dockerfiles/dockerfiles/nvidia-devel.Dockerfile +++ b/tensorflow/tools/dockerfiles/dockerfiles/gpu-devel.Dockerfile @@ -16,26 +16,12 @@ # THIS IS A GENERATED DOCKERFILE. # # This file was assembled from multiple pieces, whose use is documented -# below. Please refer to the the TensorFlow dockerfiles documentation for -# more information. Build args are documented as their default value. -# -# Ubuntu-based, Nvidia-GPU-enabled environment for developing changes for TensorFlow. -# -# Start from Nvidia's Ubuntu base image with CUDA and CuDNN, with TF development -# packages. -# --build-arg UBUNTU_VERSION=16.04 -# ( no description ) -# -# Python is required for TensorFlow and other libraries. -# --build-arg USE_PYTHON_3_NOT_2=True -# Install python 3 over Python 2 -# -# Install the latest version of Bazel and Python development tools. -# -# Configure TensorFlow's shell prompt and login tools. +# throughout. Please refer to the TensorFlow dockerfiles documentation +# for more information. ARG UBUNTU_VERSION=16.04 -FROM nvidia/cuda:9.0-base-ubuntu${UBUNTU_VERSION} + +FROM nvidia/cuda:9.0-base-ubuntu${UBUNTU_VERSION} as base RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ @@ -58,6 +44,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libpng12-dev \ libzmq3-dev \ pkg-config \ + python-dev \ rsync \ software-properties-common \ unzip \ @@ -80,11 +67,19 @@ RUN mkdir /usr/local/cuda-9.0/lib && \ ln -s /usr/lib/x86_64-linux-gnu/libnccl.so.2 /usr/local/cuda/lib/libnccl.so.2 && \ ln -s /usr/include/nccl.h /usr/local/cuda/include/nccl.h -# TODO(tobyboyd): Remove after license is excluded from BUILD file. -RUN gunzip /usr/share/doc/libnccl2/NCCL-SLA.txt.gz && \ - cp /usr/share/doc/libnccl2/NCCL-SLA.txt /usr/local/cuda/ +# Configure the build for our CUDA configuration. +ENV CI_BUILD_PYTHON python +ENV LD_LIBRARY_PATH /usr/local/cuda/extras/CUPTI/lib64:$LD_LIBRARY_PATH +ENV TF_NEED_CUDA 1 +ENV TF_NEED_TENSORRT 1 +ENV TF_CUDA_COMPUTE_CAPABILITIES=3.5,5.2,6.0,6.1,7.0 +ENV TF_CUDA_VERSION=9.0 +ENV TF_CUDNN_VERSION=7 + +# NCCL 2.x +ENV TF_NCCL_VERSION=2 -ARG USE_PYTHON_3_NOT_2=True +ARG USE_PYTHON_3_NOT_2 ARG _PY_SUFFIX=${USE_PYTHON_3_NOT_2:+3} ARG PYTHON=python${_PY_SUFFIX} ARG PIP=pip${_PY_SUFFIX} @@ -96,10 +91,13 @@ RUN apt-get update && apt-get install -y \ ${PYTHON} \ ${PYTHON}-pip -RUN ${PIP} install --upgrade \ +RUN ${PIP} --no-cache-dir install --upgrade \ pip \ setuptools +# Some TF tools expect a "python" binary +RUN ln -s $(which ${PYTHON}) /usr/local/bin/python + RUN apt-get update && apt-get install -y \ build-essential \ curl \ @@ -108,6 +106,20 @@ RUN apt-get update && apt-get install -y \ ${PYTHON}-dev \ swig +RUN ${PIP} --no-cache-dir install \ + Pillow \ + h5py \ + keras_applications \ + keras_preprocessing \ + matplotlib \ + mock \ + numpy \ + scipy \ + sklearn \ + pandas \ + && test "${USE_PYTHON_3_NOT_2}" -eq 1 && true || ${PIP} --no-cache-dir install \ + enum34 + # Install bazel RUN echo "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8" | tee /etc/apt/sources.list.d/bazel.list && \ curl https://bazel.build/bazel-release.pub.gpg | apt-key add - && \ diff --git a/tensorflow/tools/dockerfiles/dockerfiles/nvidia-jupyter.Dockerfile b/tensorflow/tools/dockerfiles/dockerfiles/gpu-jupyter.Dockerfile similarity index 62% rename from tensorflow/tools/dockerfiles/dockerfiles/nvidia-jupyter.Dockerfile rename to tensorflow/tools/dockerfiles/dockerfiles/gpu-jupyter.Dockerfile index fbdea4628a..acfe4d8607 100644 --- a/tensorflow/tools/dockerfiles/dockerfiles/nvidia-jupyter.Dockerfile +++ b/tensorflow/tools/dockerfiles/dockerfiles/gpu-jupyter.Dockerfile @@ -16,30 +16,13 @@ # THIS IS A GENERATED DOCKERFILE. # # This file was assembled from multiple pieces, whose use is documented -# below. Please refer to the the TensorFlow dockerfiles documentation for -# more information. Build args are documented as their default value. -# -# Ubuntu-based, Nvidia-GPU-enabled environment for using TensorFlow, with Jupyter included. -# -# NVIDIA with CUDA and CuDNN, no dev stuff -# --build-arg UBUNTU_VERSION=16.04 -# ( no description ) -# -# Python is required for TensorFlow and other libraries. -# --build-arg USE_PYTHON_3_NOT_2=True -# Install python 3 over Python 2 -# -# Install the TensorFlow Python package. -# --build-arg TF_PACKAGE=tensorflow-gpu (tensorflow|tensorflow-gpu|tf-nightly|tf-nightly-gpu) -# The specific TensorFlow Python package to install -# -# Configure TensorFlow's shell prompt and login tools. -# -# Launch Jupyter on execution instead of a bash prompt. +# throughout. Please refer to the TensorFlow dockerfiles documentation +# for more information. + +ARG UBUNTU_VERSION=16.04 -FROM nvidia/cuda:9.0-base-ubuntu16.04 +FROM nvidia/cuda:9.0-base-ubuntu${UBUNTU_VERSION} as base -# Pick up some TF dependencies RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ cuda-command-line-tools-9-0 \ @@ -48,6 +31,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ cuda-curand-9-0 \ cuda-cusolver-9-0 \ cuda-cusparse-9-0 \ + curl \ libcudnn7=7.2.1.38-1+cuda9.0 \ libnccl2=2.2.13-1+cuda9.0 \ libfreetype6-dev \ @@ -55,6 +39,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libpng12-dev \ libzmq3-dev \ pkg-config \ + rsync \ software-properties-common \ unzip \ && \ @@ -66,7 +51,10 @@ RUN apt-get update && \ apt-get update && \ apt-get install libnvinfer4=4.1.2-1+cuda9.0 -ARG USE_PYTHON_3_NOT_2=True +# For CUDA profiling, TensorFlow requires CUPTI. +ENV LD_LIBRARY_PATH /usr/local/cuda/extras/CUPTI/lib64:$LD_LIBRARY_PATH + +ARG USE_PYTHON_3_NOT_2 ARG _PY_SUFFIX=${USE_PYTHON_3_NOT_2:+3} ARG PYTHON=python${_PY_SUFFIX} ARG PIP=pip${_PY_SUFFIX} @@ -78,21 +66,37 @@ RUN apt-get update && apt-get install -y \ ${PYTHON} \ ${PYTHON}-pip -RUN ${PIP} install --upgrade \ +RUN ${PIP} --no-cache-dir install --upgrade \ pip \ setuptools -ARG TF_PACKAGE=tensorflow-gpu +# Some TF tools expect a "python" binary +RUN ln -s $(which ${PYTHON}) /usr/local/bin/python + +# Options: +# tensorflow +# tensorflow-gpu +# tf-nightly +# tf-nightly-gpu +ARG TF_PACKAGE=tensorflow RUN ${PIP} install ${TF_PACKAGE} COPY bashrc /etc/bash.bashrc RUN chmod a+rwx /etc/bash.bashrc -RUN ${PIP} install jupyter +RUN ${PIP} install jupyter matplotlib -RUN mkdir /notebooks && chmod a+rwx /notebooks +RUN mkdir -p /tf/tensorflow-tutorials && chmod -R a+rwx /tf/ RUN mkdir /.local && chmod a+rwx /.local -WORKDIR /notebooks +RUN apt-get install -y --no-install-recommends wget +WORKDIR /tf/tensorflow-tutorials +RUN wget https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/keras/basic_classification.ipynb +RUN wget https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/keras/basic_text_classification.ipynb +COPY readme-for-jupyter.md README.md +RUN apt-get autoremove -y && apt-get remove -y wget +WORKDIR /tf EXPOSE 8888 -CMD ["bash", "-c", "source /etc/bash.bashrc && jupyter notebook --notebook-dir=/notebooks --ip 0.0.0.0 --no-browser --allow-root"] +RUN ${PYTHON} -m ipykernel.kernelspec + +CMD ["bash", "-c", "source /etc/bash.bashrc && jupyter notebook --notebook-dir=/tf --ip 0.0.0.0 --no-browser --allow-root"] diff --git a/tensorflow/tools/dockerfiles/dockerfiles/nvidia.Dockerfile b/tensorflow/tools/dockerfiles/dockerfiles/gpu.Dockerfile similarity index 69% rename from tensorflow/tools/dockerfiles/dockerfiles/nvidia.Dockerfile rename to tensorflow/tools/dockerfiles/dockerfiles/gpu.Dockerfile index e0312dbc29..f36a21eaf0 100644 --- a/tensorflow/tools/dockerfiles/dockerfiles/nvidia.Dockerfile +++ b/tensorflow/tools/dockerfiles/dockerfiles/gpu.Dockerfile @@ -16,28 +16,13 @@ # THIS IS A GENERATED DOCKERFILE. # # This file was assembled from multiple pieces, whose use is documented -# below. Please refer to the the TensorFlow dockerfiles documentation for -# more information. Build args are documented as their default value. -# -# Ubuntu-based, Nvidia-GPU-enabled environment for using TensorFlow. -# -# NVIDIA with CUDA and CuDNN, no dev stuff -# --build-arg UBUNTU_VERSION=16.04 -# ( no description ) -# -# Python is required for TensorFlow and other libraries. -# --build-arg USE_PYTHON_3_NOT_2=True -# Install python 3 over Python 2 -# -# Install the TensorFlow Python package. -# --build-arg TF_PACKAGE=tensorflow-gpu (tensorflow|tensorflow-gpu|tf-nightly|tf-nightly-gpu) -# The specific TensorFlow Python package to install -# -# Configure TensorFlow's shell prompt and login tools. +# throughout. Please refer to the TensorFlow dockerfiles documentation +# for more information. + +ARG UBUNTU_VERSION=16.04 -FROM nvidia/cuda:9.0-base-ubuntu16.04 +FROM nvidia/cuda:9.0-base-ubuntu${UBUNTU_VERSION} as base -# Pick up some TF dependencies RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ cuda-command-line-tools-9-0 \ @@ -46,6 +31,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ cuda-curand-9-0 \ cuda-cusolver-9-0 \ cuda-cusparse-9-0 \ + curl \ libcudnn7=7.2.1.38-1+cuda9.0 \ libnccl2=2.2.13-1+cuda9.0 \ libfreetype6-dev \ @@ -53,6 +39,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libpng12-dev \ libzmq3-dev \ pkg-config \ + rsync \ software-properties-common \ unzip \ && \ @@ -64,7 +51,10 @@ RUN apt-get update && \ apt-get update && \ apt-get install libnvinfer4=4.1.2-1+cuda9.0 -ARG USE_PYTHON_3_NOT_2=True +# For CUDA profiling, TensorFlow requires CUPTI. +ENV LD_LIBRARY_PATH /usr/local/cuda/extras/CUPTI/lib64:$LD_LIBRARY_PATH + +ARG USE_PYTHON_3_NOT_2 ARG _PY_SUFFIX=${USE_PYTHON_3_NOT_2:+3} ARG PYTHON=python${_PY_SUFFIX} ARG PIP=pip${_PY_SUFFIX} @@ -76,11 +66,19 @@ RUN apt-get update && apt-get install -y \ ${PYTHON} \ ${PYTHON}-pip -RUN ${PIP} install --upgrade \ +RUN ${PIP} --no-cache-dir install --upgrade \ pip \ setuptools -ARG TF_PACKAGE=tensorflow-gpu +# Some TF tools expect a "python" binary +RUN ln -s $(which ${PYTHON}) /usr/local/bin/python + +# Options: +# tensorflow +# tensorflow-gpu +# tf-nightly +# tf-nightly-gpu +ARG TF_PACKAGE=tensorflow RUN ${PIP} install ${TF_PACKAGE} COPY bashrc /etc/bash.bashrc diff --git a/tensorflow/tools/dockerfiles/partials/jupyter.partial.Dockerfile b/tensorflow/tools/dockerfiles/partials/jupyter.partial.Dockerfile index 2c9b9f3f9a..c4ec6095c0 100644 --- a/tensorflow/tools/dockerfiles/partials/jupyter.partial.Dockerfile +++ b/tensorflow/tools/dockerfiles/partials/jupyter.partial.Dockerfile @@ -1,8 +1,16 @@ -RUN ${PIP} install jupyter +RUN ${PIP} install jupyter matplotlib -RUN mkdir /notebooks && chmod a+rwx /notebooks +RUN mkdir -p /tf/tensorflow-tutorials && chmod -R a+rwx /tf/ RUN mkdir /.local && chmod a+rwx /.local -WORKDIR /notebooks +RUN apt-get install -y --no-install-recommends wget +WORKDIR /tf/tensorflow-tutorials +RUN wget https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/keras/basic_classification.ipynb +RUN wget https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/keras/basic_text_classification.ipynb +COPY readme-for-jupyter.md README.md +RUN apt-get autoremove -y && apt-get remove -y wget +WORKDIR /tf EXPOSE 8888 -CMD ["bash", "-c", "source /etc/bash.bashrc && jupyter notebook --notebook-dir=/notebooks --ip 0.0.0.0 --no-browser --allow-root"] +RUN ${PYTHON} -m ipykernel.kernelspec + +CMD ["bash", "-c", "source /etc/bash.bashrc && jupyter notebook --notebook-dir=/tf --ip 0.0.0.0 --no-browser --allow-root"] diff --git a/tensorflow/tools/dockerfiles/partials/tensorflow.partial.Dockerfile b/tensorflow/tools/dockerfiles/partials/tensorflow.partial.Dockerfile index 96e79547f0..76758bd147 100644 --- a/tensorflow/tools/dockerfiles/partials/tensorflow.partial.Dockerfile +++ b/tensorflow/tools/dockerfiles/partials/tensorflow.partial.Dockerfile @@ -1,2 +1,7 @@ -ARG TF_PACKAGE +# Options: +# tensorflow +# tensorflow-gpu +# tf-nightly +# tf-nightly-gpu +ARG TF_PACKAGE=tensorflow RUN ${PIP} install ${TF_PACKAGE} diff --git a/tensorflow/tools/dockerfiles/partials/test-import.partial.Dockerfile b/tensorflow/tools/dockerfiles/partials/test-import.partial.Dockerfile new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tensorflow/tools/dockerfiles/partials/ubuntu.partial.Dockerfile b/tensorflow/tools/dockerfiles/partials/ubuntu.partial.Dockerfile deleted file mode 100644 index 0a50735bf8..0000000000 --- a/tensorflow/tools/dockerfiles/partials/ubuntu.partial.Dockerfile +++ /dev/null @@ -1,2 +0,0 @@ -ARG UBUNTU_VERSION=16.04 -FROM ubuntu:${UBUNTU_VERSION} diff --git a/tensorflow/tools/dockerfiles/partials/bazel.partial.Dockerfile b/tensorflow/tools/dockerfiles/partials/ubuntu/bazel.partial.Dockerfile similarity index 58% rename from tensorflow/tools/dockerfiles/partials/bazel.partial.Dockerfile rename to tensorflow/tools/dockerfiles/partials/ubuntu/bazel.partial.Dockerfile index b08d8bdd14..156bb01991 100644 --- a/tensorflow/tools/dockerfiles/partials/bazel.partial.Dockerfile +++ b/tensorflow/tools/dockerfiles/partials/ubuntu/bazel.partial.Dockerfile @@ -6,6 +6,20 @@ RUN apt-get update && apt-get install -y \ ${PYTHON}-dev \ swig +RUN ${PIP} --no-cache-dir install \ + Pillow \ + h5py \ + keras_applications \ + keras_preprocessing \ + matplotlib \ + mock \ + numpy \ + scipy \ + sklearn \ + pandas \ + && test "${USE_PYTHON_3_NOT_2}" -eq 1 && true || ${PIP} --no-cache-dir install \ + enum34 + # Install bazel RUN echo "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8" | tee /etc/apt/sources.list.d/bazel.list && \ curl https://bazel.build/bazel-release.pub.gpg | apt-key add - && \ diff --git a/tensorflow/tools/dockerfiles/partials/ubuntu-devel.partial.Dockerfile b/tensorflow/tools/dockerfiles/partials/ubuntu/cpu-devel.partial.Dockerfile similarity index 86% rename from tensorflow/tools/dockerfiles/partials/ubuntu-devel.partial.Dockerfile rename to tensorflow/tools/dockerfiles/partials/ubuntu/cpu-devel.partial.Dockerfile index bc79272276..901652cc28 100644 --- a/tensorflow/tools/dockerfiles/partials/ubuntu-devel.partial.Dockerfile +++ b/tensorflow/tools/dockerfiles/partials/ubuntu/cpu-devel.partial.Dockerfile @@ -1,5 +1,4 @@ -ARG UBUNTU_VERSION=16.04 -FROM ubuntu:${UBUNTU_VERSION} +FROM ubuntu:${UBUNTU_VERSION} AS base RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ @@ -11,7 +10,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libpng12-dev \ libzmq3-dev \ pkg-config \ - python-dev \ rsync \ software-properties-common \ unzip \ @@ -22,3 +20,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* + +ENV CI_BUILD_PYTHON python + diff --git a/tensorflow/tools/dockerfiles/partials/ubuntu/cpu.partial.Dockerfile b/tensorflow/tools/dockerfiles/partials/ubuntu/cpu.partial.Dockerfile new file mode 100644 index 0000000000..d01b26e27f --- /dev/null +++ b/tensorflow/tools/dockerfiles/partials/ubuntu/cpu.partial.Dockerfile @@ -0,0 +1 @@ +FROM ubuntu:${UBUNTU_VERSION} as base diff --git a/tensorflow/tools/dockerfiles/partials/nvidia-devel.partial.Dockerfile b/tensorflow/tools/dockerfiles/partials/ubuntu/nvidia-devel.partial.Dockerfile similarity index 78% rename from tensorflow/tools/dockerfiles/partials/nvidia-devel.partial.Dockerfile rename to tensorflow/tools/dockerfiles/partials/ubuntu/nvidia-devel.partial.Dockerfile index 45159f711f..48d457e40c 100644 --- a/tensorflow/tools/dockerfiles/partials/nvidia-devel.partial.Dockerfile +++ b/tensorflow/tools/dockerfiles/partials/ubuntu/nvidia-devel.partial.Dockerfile @@ -1,5 +1,4 @@ -ARG UBUNTU_VERSION=16.04 -FROM nvidia/cuda:9.0-base-ubuntu${UBUNTU_VERSION} +FROM nvidia/cuda:9.0-base-ubuntu${UBUNTU_VERSION} as base RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ @@ -22,6 +21,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libpng12-dev \ libzmq3-dev \ pkg-config \ + python-dev \ rsync \ software-properties-common \ unzip \ @@ -44,6 +44,14 @@ RUN mkdir /usr/local/cuda-9.0/lib && \ ln -s /usr/lib/x86_64-linux-gnu/libnccl.so.2 /usr/local/cuda/lib/libnccl.so.2 && \ ln -s /usr/include/nccl.h /usr/local/cuda/include/nccl.h -# TODO(tobyboyd): Remove after license is excluded from BUILD file. -RUN gunzip /usr/share/doc/libnccl2/NCCL-SLA.txt.gz && \ - cp /usr/share/doc/libnccl2/NCCL-SLA.txt /usr/local/cuda/ +# Configure the build for our CUDA configuration. +ENV CI_BUILD_PYTHON python +ENV LD_LIBRARY_PATH /usr/local/cuda/extras/CUPTI/lib64:$LD_LIBRARY_PATH +ENV TF_NEED_CUDA 1 +ENV TF_NEED_TENSORRT 1 +ENV TF_CUDA_COMPUTE_CAPABILITIES=3.5,5.2,6.0,6.1,7.0 +ENV TF_CUDA_VERSION=9.0 +ENV TF_CUDNN_VERSION=7 + +# NCCL 2.x +ENV TF_NCCL_VERSION=2 diff --git a/tensorflow/tools/dockerfiles/partials/nvidia.partial.Dockerfile b/tensorflow/tools/dockerfiles/partials/ubuntu/nvidia.partial.Dockerfile similarity index 78% rename from tensorflow/tools/dockerfiles/partials/nvidia.partial.Dockerfile rename to tensorflow/tools/dockerfiles/partials/ubuntu/nvidia.partial.Dockerfile index 1064390af3..1dc8e43aad 100644 --- a/tensorflow/tools/dockerfiles/partials/nvidia.partial.Dockerfile +++ b/tensorflow/tools/dockerfiles/partials/ubuntu/nvidia.partial.Dockerfile @@ -1,6 +1,5 @@ -FROM nvidia/cuda:9.0-base-ubuntu16.04 +FROM nvidia/cuda:9.0-base-ubuntu${UBUNTU_VERSION} as base -# Pick up some TF dependencies RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ cuda-command-line-tools-9-0 \ @@ -9,6 +8,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ cuda-curand-9-0 \ cuda-cusolver-9-0 \ cuda-cusparse-9-0 \ + curl \ libcudnn7=7.2.1.38-1+cuda9.0 \ libnccl2=2.2.13-1+cuda9.0 \ libfreetype6-dev \ @@ -16,6 +16,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libpng12-dev \ libzmq3-dev \ pkg-config \ + rsync \ software-properties-common \ unzip \ && \ @@ -26,3 +27,6 @@ RUN apt-get update && \ apt-get install nvinfer-runtime-trt-repo-ubuntu1604-4.0.1-ga-cuda9.0 && \ apt-get update && \ apt-get install libnvinfer4=4.1.2-1+cuda9.0 + +# For CUDA profiling, TensorFlow requires CUPTI. +ENV LD_LIBRARY_PATH /usr/local/cuda/extras/CUPTI/lib64:$LD_LIBRARY_PATH diff --git a/tensorflow/tools/dockerfiles/partials/python.partial.Dockerfile b/tensorflow/tools/dockerfiles/partials/ubuntu/python.partial.Dockerfile similarity index 66% rename from tensorflow/tools/dockerfiles/partials/python.partial.Dockerfile rename to tensorflow/tools/dockerfiles/partials/ubuntu/python.partial.Dockerfile index ee08af73a8..6af4731953 100644 --- a/tensorflow/tools/dockerfiles/partials/python.partial.Dockerfile +++ b/tensorflow/tools/dockerfiles/partials/ubuntu/python.partial.Dockerfile @@ -10,6 +10,9 @@ RUN apt-get update && apt-get install -y \ ${PYTHON} \ ${PYTHON}-pip -RUN ${PIP} install --upgrade \ +RUN ${PIP} --no-cache-dir install --upgrade \ pip \ setuptools + +# Some TF tools expect a "python" binary +RUN ln -s $(which ${PYTHON}) /usr/local/bin/python diff --git a/tensorflow/tools/dockerfiles/partials/ubuntu/test-devel.partial.Dockerfile b/tensorflow/tools/dockerfiles/partials/ubuntu/test-devel.partial.Dockerfile new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tensorflow/tools/dockerfiles/partials/ubuntu/version.partial.Dockerfile b/tensorflow/tools/dockerfiles/partials/ubuntu/version.partial.Dockerfile new file mode 100644 index 0000000000..6ecd2b8b1a --- /dev/null +++ b/tensorflow/tools/dockerfiles/partials/ubuntu/version.partial.Dockerfile @@ -0,0 +1 @@ +ARG UBUNTU_VERSION=16.04 diff --git a/tensorflow/tools/dockerfiles/readme-for-jupyter.md b/tensorflow/tools/dockerfiles/readme-for-jupyter.md new file mode 100644 index 0000000000..f104a7533b --- /dev/null +++ b/tensorflow/tools/dockerfiles/readme-for-jupyter.md @@ -0,0 +1,3 @@ +Want more tutorials like these? + +Check out tensorflow.org/tutorials! diff --git a/tensorflow/tools/dockerfiles/spec.yml b/tensorflow/tools/dockerfiles/spec.yml index 28bf9a55da..4826ddd8e2 100644 --- a/tensorflow/tools/dockerfiles/spec.yml +++ b/tensorflow/tools/dockerfiles/spec.yml @@ -1,195 +1,135 @@ -# ====== -# HEADER -# ====== -# -# This is commented-out and prepended to each generated Dockerfile. header: | - Copyright 2018 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. - ============================================================================ - - THIS IS A GENERATED DOCKERFILE. - - This file was assembled from multiple pieces, whose use is documented - below. Please refer to the the TensorFlow dockerfiles documentation for - more information. Build args are documented as their default value. - -# ======== -# PARTIALS -# ======== + # Copyright 2018 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. + # ============================================================================ + # + # THIS IS A GENERATED DOCKERFILE. + # + # This file was assembled from multiple pieces, whose use is documented + # throughout. Please refer to the TensorFlow dockerfiles documentation + # for more information. + +# A combinatorial explosion of Docker images and Dockerfiles. +# Each "release" defines all of the ways to combine related but separate chunks +# of functionality ("slices") by listing all of the "slice sets" to use when +# building. # -# Represent and document pieces of a Dockerfile. Spec: -# -# name: the name of the partial, is referenced from the images section -# desc: A description, inserted later into the Dockerfile -# file: Alternative file prefix, e.g. file.partial.Dockerfile. The default is -# the name of the partial. -# args: A dict of ARGs in the Dockerfile; each entry has the format -# ARG_NAME: VALUE where VALUE is one of: -# - a dict: -# desc: Documentation for the arg -# default: Default value for the arg; is written to the Dockerfile -# options: List of strings, part of documentation -# - a concrete value: the same as a dictionary with default: [value]. - -partials: - ubuntu: - desc: Start from Ubuntu (no GPU support) - args: - UBUNTU_VERSION: 16.04 - - ubuntu-devel: - desc: Start from Ubuntu, with TF development packages (no GPU support) - args: - UBUNTU_VERSION: 16.04 - - bazel: - desc: Install the latest version of Bazel and Python development tools. - - nvidia: - desc: NVIDIA with CUDA and CuDNN, no dev stuff - args: - UBUNTU_VERSION: 16.04 - - nvidia-devel: - desc: > - Start from Nvidia's Ubuntu base image with CUDA and CuDNN, with TF - development packages. - args: - UBUNTU_VERSION: 16.04 +# For example, a release that uses {nightly}{py} would create 4 Dockerfiles +# (which could become images or concrete Dockerfiles), because the "nightly" +# and "py" slice sets both have two entries: +# +# - nightly (no -py2 because the Python 2 slice set has add_to_name: "" +# - nightly-py3 +# - nightly-gpu (similar) +# - nightly-gpu-py3 + +releases: + nightly: + tag_specs: + - "{nightly}{py}{jupyter}" + + versioned: + tag_specs: + - "{_TAG_PREFIX}{ubuntu}{py}{jupyter}" + + ubuntu-dockerfiles: + is_dockerfiles: true + upload_images: false + tag_specs: + - "{ubuntu}{jupyter}" + +slice_sets: + + py: + - add_to_name: "" + args: + - USE_PYTHON_3_NOT_2= + - add_to_name: "-py3" + args: + - USE_PYTHON_3_NOT_2=1 - python: - desc: Python is required for TensorFlow and other libraries. - args: - USE_PYTHON_3_NOT_2: - default: true - desc: Install python 3 over Python 2 - - tensorflow: - desc: Install the TensorFlow Python package. - args: - TF_PACKAGE: - default: tensorflow - options: - - tensorflow - - tensorflow-gpu - - tf-nightly - - tf-nightly-gpu - desc: The specific TensorFlow Python package to install - shell: - desc: Configure TensorFlow's shell prompt and login tools. jupyter: - desc: Launch Jupyter on execution instead of a bash prompt. - -# ====== -# IMAGES -# ====== -# -# Represent Dockerfiles. Spec: -# -# name: the name of the image, possibly referenced by other images -# desc: A description, inserted later into the Dockerfile -# create-dockerfile: Create a dockerfile based on this. Useful for creating -# extensible base images that don't need a file. Default is true. -# partials: List of VALUEs, where a VALUE is either: -# - the name of a partial, which inserts that partial into this image -# - image: [name of another image], which inserts the partials from that -# image into this image -# arg-defaults: List of VALUEs, where a VALUE is either: -# - ARG_NAME: VALUE, which sets the ARG_NAME to VALUE wherever it appears -# in this image's partials -# - [name of another image], which loads the default args from that image -images: - - nodev: - create-dockerfile: false - partials: - - python - - tensorflow - - shell - - dev: - create-dockerfile: false - partials: - - python - - bazel - - shell - - cpu: - desc: Ubuntu-based, CPU-only environment for using TensorFlow - partials: - - ubuntu - - image: nodev - - cpu-devel: - desc: > - Ubuntu-based, CPU-only environment for developing changes for - TensorFlow. - partials: - - ubuntu-devel - - image: dev + - add_to_name: "" + - add_to_name: "-jupyter" + partials: + - jupyter - nvidia: - desc: Ubuntu-based, Nvidia-GPU-enabled environment for using TensorFlow. - arg-defaults: - - TF_PACKAGE: tensorflow-gpu - partials: - - nvidia - - image: nodev - - nvidia-devel: - desc: > - Ubuntu-based, Nvidia-GPU-enabled environment for developing changes - for TensorFlow. - arg-defaults: - - TF_PACKAGE: tensorflow-gpu - partials: - - nvidia-devel - - image: dev - - cpu-jupyter: - desc: > - Ubuntu-based, CPU-only environment for using TensorFlow, with Jupyter - included. - partials: - - image: cpu - - jupyter - - cpu-devel-jupyter: - desc: > - Ubuntu-based, CPU-only environment for developing changes for - TensorFlow, with Jupyter included. - partials: - - image: cpu-devel - - jupyter - - nvidia-jupyter: - desc: > - Ubuntu-based, Nvidia-GPU-enabled environment for using TensorFlow, with - Jupyter included. - arg-defaults: - - nvidia - partials: - - image: nvidia - - jupyter - - nvidia-devel-jupyter: - desc: > - Ubuntu-based, Nvidia-GPU-enabled environment for developing changes for - TensorFlow, with Jupyter included. - arg-defaults: - - nvidia-devel - partials: - - image: nvidia-devel - - jupyter + ubuntu: + - add_to_name: "" + dockerfile_exclusive_name: "cpu" + partials: + - ubuntu/version + - ubuntu/cpu + - ubuntu/python + - tensorflow + - shell + - add_to_name: "-gpu" + dockerfile_exclusive_name: "gpu" + args: + - TF_PACKAGE=tensorflow-gpu + partials: + - ubuntu/version + - ubuntu/nvidia + - ubuntu/python + - tensorflow + - shell + tests: + - import-gpu.sh + test_runtime: nvidia + - add_to_name: "-devel" + dockerfile_exclusive_name: "cpu-devel" + partials: + - ubuntu/version + - ubuntu/cpu-devel + - ubuntu/python + - ubuntu/bazel + - shell + tests: + - build-cpu.sh + - add_to_name: "-gpu-devel" + dockerfile_exclusive_name: "gpu-devel" + partials: + - ubuntu/version + - ubuntu/nvidia-devel + - ubuntu/python + - ubuntu/bazel + - shell + tests: + - build-gpu.sh + test_runtime: nvidia + + nightly: + - add_to_name: "nightly" + partials: + - ubuntu/version + - ubuntu/cpu + - ubuntu/python + - tensorflow + - shell + args: + - TF_PACKAGE=tf-nightly + tests: + - import.sh + - add_to_name: "nightly-gpu" + partials: + - ubuntu/version + - ubuntu/nvidia + - ubuntu/python + - tensorflow + - shell + test_runtime: nvidia + tests: + - import-gpu.sh + args: + - TF_PACKAGE=tf-nightly-gpu diff --git a/tensorflow/tools/dockerfiles/tests/build-cpu.sh b/tensorflow/tools/dockerfiles/tests/build-cpu.sh new file mode 100755 index 0000000000..bcdc4c2139 --- /dev/null +++ b/tensorflow/tools/dockerfiles/tests/build-cpu.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +# Copyright 2018 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. +# ============================================================================ + +# Download and build TensorFlow. +set -euxo pipefail +git clone --branch=master --depth=1 https://github.com/tensorflow/tensorflow.git /tensorflow +cd /tensorflow + +ln -s $(which ${PYTHON}) /usr/local/bin/python + +# For optimized builds appropriate for the hardware platform of your choosing, uncomment below... +# For ivy-bridge or sandy-bridge +# --copt=-march="ivybridge" \ +# for haswell, broadwell, or skylake +# --copt=-march="haswell" \ +tensorflow/tools/ci_build/builds/configured CPU \ + bazel build -c opt --copt=-mavx --cxxopt="-D_GLIBCXX_USE_CXX11_ABI=0" \ + tensorflow/tools/pip_package:build_pip_package && \ + bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/pip && \ + pip --no-cache-dir install --upgrade /tmp/pip/tensorflow-*.whl && \ + rm -rf /tmp/pip && \ + rm -rf /root/.cache + diff --git a/tensorflow/tools/dockerfiles/tests/build-gpu.sh b/tensorflow/tools/dockerfiles/tests/build-gpu.sh new file mode 100755 index 0000000000..76b25d5a74 --- /dev/null +++ b/tensorflow/tools/dockerfiles/tests/build-gpu.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +# Copyright 2018 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. +# ============================================================================ + +# Download and build TensorFlow. +set -euxo pipefail +git clone --branch=master --depth=1 https://github.com/tensorflow/tensorflow.git /tensorflow +cd /tensorflow + +ln -s $(which ${PYTHON}) /usr/local/bin/python + +ln -s /usr/local/cuda/lib64/stubs/libcuda.so /usr/local/cuda/lib64/stubs/libcuda.so.1 + +LD_LIBRARY_PATH=/usr/local/cuda/lib64/stubs:${LD_LIBRARY_PATH} \ +tensorflow/tools/ci_build/builds/configured GPU \ +bazel build -c opt --copt=-mavx --config=cuda \ + --cxxopt="-D_GLIBCXX_USE_CXX11_ABI=0" \ + tensorflow/tools/pip_package:build_pip_package && \ +rm /usr/local/cuda/lib64/stubs/libcuda.so.1 && \ +bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/pip && \ +pip --no-cache-dir install --upgrade /tmp/pip/tensorflow-*.whl && \ +rm -rf /tmp/pip && \ +rm -rf /root/.cache diff --git a/tensorflow/tools/dockerfiles/tests/import-gpu.sh b/tensorflow/tools/dockerfiles/tests/import-gpu.sh new file mode 100755 index 0000000000..6559210dcb --- /dev/null +++ b/tensorflow/tools/dockerfiles/tests/import-gpu.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +# Copyright 2018 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. +# ============================================================================ + +python -c 'import tensorflow as tf; tf.test.is_gpu_available() or exit(1)' diff --git a/tensorflow/tools/dockerfiles/tests/import.sh b/tensorflow/tools/dockerfiles/tests/import.sh new file mode 100755 index 0000000000..b73bd86a85 --- /dev/null +++ b/tensorflow/tools/dockerfiles/tests/import.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +# Copyright 2018 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. +# ============================================================================ + +set -euxo pipefail +python -c 'import tensorflow as tf' diff --git a/tensorflow/tools/dockerfiles/assembler.Dockerfile b/tensorflow/tools/dockerfiles/tools.Dockerfile similarity index 95% rename from tensorflow/tools/dockerfiles/assembler.Dockerfile rename to tensorflow/tools/dockerfiles/tools.Dockerfile index 7a8e07fced..e8929295a5 100644 --- a/tensorflow/tools/dockerfiles/assembler.Dockerfile +++ b/tensorflow/tools/dockerfiles/tools.Dockerfile @@ -20,8 +20,9 @@ FROM debian:stretch LABEL maintainer="Austin Anderson " -RUN apt-get update && apt-get install -y python3 python3-pip bash -RUN pip3 install --upgrade pip setuptools pyyaml absl-py cerberus +RUN apt-get update && apt-get install -y python3 python3-pip bash curl +RUN curl -sSL https://get.docker.com/ | sh +RUN pip3 install --upgrade pip setuptools pyyaml absl-py cerberus docker WORKDIR /tf VOLUME ["/tf"] diff --git a/tensorflow/tools/docs/BUILD b/tensorflow/tools/docs/BUILD index 1a53f24177..b072853a4e 100644 --- a/tensorflow/tools/docs/BUILD +++ b/tensorflow/tools/docs/BUILD @@ -142,17 +142,30 @@ py_test( ], ) -py_binary( - name = "generate_1_0", - srcs = ["generate_1_0.py"], +py_test( + name = "generate2_test", + srcs = ["generate2_test.py"], srcs_version = "PY2AND3", + tags = [ + "manual", + # No reason to run sanitizers or fastbuild for this test. + "noasan", + "nomsan", + "notsan", + "optonly", + ], deps = [ - ":generate_lib", - "//tensorflow:tensorflow_py", - "//tensorflow/python/debug:debug_py", + ":generate2", ], ) +py_binary( + name = "generate2", + srcs = ["generate2.py"], + srcs_version = "PY2AND3", + deps = ["//tensorflow:tensorflow_py"], +) + py_library( name = "py_guide_parser", srcs = ["py_guide_parser.py"], diff --git a/tensorflow/tools/docs/generate2.py b/tensorflow/tools/docs/generate2.py new file mode 100644 index 0000000000..fba909d26d --- /dev/null +++ b/tensorflow/tools/docs/generate2.py @@ -0,0 +1,82 @@ +# Copyright 2018 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. +# ============================================================================== +r"""A tool to generate api_docs for TensorFlow2. + +``` +python generate2.py --output_dir=/tmp/out +``` + +Requires a local installation of: + https://github.com/tensorflow/docs/tree/master/tools + tf-nightly-2.0-preview +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from os import path + +from absl import app +from absl import flags + +import tensorflow as tf + +from tensorflow_docs.api_generator import generate_lib + +FLAGS = flags.FLAGS + +flags.DEFINE_string( + "code_url_prefix", + "/code/stable/tensorflow/", + "A url to prepend to code paths when creating links to defining code") + +flags.DEFINE_string( + "output_dir", "/tmp/out", + "A directory, where the docs will be output to.") + +flags.DEFINE_bool("search_hints", True, + "Include meta-data search hints at the top of each file.") + + +def build_docs(output_dir, code_url_prefix, search_hints=True): + """Build api docs for tensorflow v2. + + Args: + output_dir: A string path, where to put the files. + code_url_prefix: prefix for "Defined in" links. + search_hints: Bool. Include meta-data search hints at the top of each file. + """ + base_dir = path.dirname(tf.__file__) + doc_generator = generate_lib.DocGenerator( + root_title="TensorFlow 2.0 Preview", + py_modules=[("tf", tf)], + base_dir=base_dir, + search_hints=search_hints, + code_url_prefix=code_url_prefix, + site_path="api_docs/") + + doc_generator.build(output_dir) + + +def main(argv): + del argv + build_docs(output_dir=FLAGS.output_dir, + code_url_prefix=FLAGS.code_url_prefix, + search_hints=FLAGS.search_hints) + + +if __name__ == "__main__": + app.run(main) diff --git a/tensorflow/contrib/estimator/python/estimator/linear.py b/tensorflow/tools/docs/generate2_test.py similarity index 60% rename from tensorflow/contrib/estimator/python/estimator/linear.py rename to tensorflow/tools/docs/generate2_test.py index b6a4444f66..774d45c536 100644 --- a/tensorflow/contrib/estimator/python/estimator/linear.py +++ b/tensorflow/tools/docs/generate2_test.py @@ -12,21 +12,28 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""linear python module. - -Importing from tensorflow.python.estimator is unsupported -and will soon break! -""" -# pylint: disable=unused-import,g-bad-import-order,g-import-not-at-top,wildcard-import +"""Tests for tensorflow.tools.docs.generate2.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function -from tensorflow_estimator.contrib.estimator.python.estimator import linear +import os +import shutil + +from tensorflow.python.platform import googletest +from tensorflow.tools.docs import generate2 + + +class Generate2Test(googletest.TestCase): + + def test_end_to_end(self): + output_dir = os.path.join(googletest.GetTempDir(), 'output') + if os.path.exists(output_dir): + shutil.rmtree(output_dir) + os.makedirs(output_dir) + generate2.build_docs(output_dir=output_dir, code_url_prefix='') -# Include attrs that start with single underscore. -_HAS_DYNAMIC_ATTRIBUTES = True -linear.__all__ = [s for s in dir(linear) if not s.startswith('__')] -from tensorflow_estimator.contrib.estimator.python.estimator.linear import * +if __name__ == '__main__': + googletest.main() diff --git a/tensorflow/tools/docs/generate_1_0.py b/tensorflow/tools/docs/generate_1_0.py deleted file mode 100644 index f4384e0ced..0000000000 --- a/tensorflow/tools/docs/generate_1_0.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright 2015 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. -# ============================================================================== -"""Generate docs for the TensorFlow Python API.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import os -import sys - -import tensorflow as tf - -from tensorflow.python import debug as tf_debug -from tensorflow.python.util import tf_inspect -from tensorflow.tools.docs import generate_lib - -if __name__ == '__main__': - doc_generator = generate_lib.DocGenerator() - doc_generator.add_output_dir_argument() - doc_generator.add_src_dir_argument() - - # This doc generator works on the TensorFlow codebase. Since this script lives - # at tensorflow/tools/docs, and all code is defined somewhere inside - # tensorflow/, we can compute the base directory (two levels up), which is - # valid unless we're trying to apply this to a different code base, or are - # moving the script around. - script_dir = os.path.dirname(tf_inspect.getfile(tf_inspect.currentframe())) - default_base_dir = os.path.join(script_dir, '..', '..') - doc_generator.add_base_dir_argument(default_base_dir) - - flags = doc_generator.parse_known_args() - - # tf_debug is not imported with tf, it's a separate module altogether - doc_generator.set_py_modules([('tf', tf), ('tfdbg', tf_debug)]) - - doc_generator.set_do_not_descend_map({ - 'tf': ['cli', 'lib', 'wrappers'], - 'tf.contrib': [ - 'compiler', - 'factorization', - 'grid_rnn', - 'labeled_tensor', - 'quantization', - 'session_bundle', - 'slim', - 'solvers', - 'specs', - 'tensor_forest', - 'tensorboard', - 'testing', - 'training', - 'tfprof', - ], - 'tf.contrib.bayesflow': [ - 'entropy', 'monte_carlo', 'special_math', - 'stochastic_gradient_estimators', 'stochastic_graph', - 'stochastic_tensor', 'stochastic_variables', 'variational_inference' - ], - 'tf.contrib.distributions': ['bijector'], - 'tf.contrib.ffmpeg': ['ffmpeg_ops'], - 'tf.contrib.graph_editor': [ - 'edit', 'match', 'reroute', 'subgraph', 'transform', 'select', 'util' - ], - 'tf.contrib.layers': ['feature_column', 'summaries'], - 'tf.contrib.learn': [ - 'datasets', - 'head', - 'graph_actions', - 'io', - 'models', - 'monitors', - 'ops', - 'preprocessing', - 'utils', - ], - 'tf.contrib.util': ['loader'], - }) - - sys.exit(doc_generator.build(flags)) diff --git a/tensorflow/tools/docs/parser.py b/tensorflow/tools/docs/parser.py index 83b4bf8128..6dc18ee8dc 100644 --- a/tensorflow/tools/docs/parser.py +++ b/tensorflow/tools/docs/parser.py @@ -39,7 +39,7 @@ def is_free_function(py_object, full_name, index): """Check if input is a free function (and not a class- or static method). Args: - py_object: The the object in question. + py_object: The object in question. full_name: The full name of the object, like `tf.module.symbol`. index: The {full_name:py_object} dictionary for the public API. diff --git a/tensorflow/tools/lib_package/BUILD b/tensorflow/tools/lib_package/BUILD index 7aaa845ae9..1cac5ee138 100644 --- a/tensorflow/tools/lib_package/BUILD +++ b/tensorflow/tools/lib_package/BUILD @@ -112,6 +112,7 @@ pkg_tar( genrule( name = "clicenses_generate", srcs = [ + "//third_party/icu/data:LICENSE", "//third_party/hadoop:LICENSE.txt", "//third_party/eigen3:LICENSE", "//third_party/fft2d:LICENSE", @@ -180,6 +181,7 @@ genrule( genrule( name = "jnilicenses_generate", srcs = [ + "//third_party/icu/data:LICENSE", "//third_party/hadoop:LICENSE.txt", "//third_party/eigen3:LICENSE", "//third_party/fft2d:LICENSE", diff --git a/tensorflow/tools/pip_package/BUILD b/tensorflow/tools/pip_package/BUILD index 3a863d3c52..82c6bf383f 100644 --- a/tensorflow/tools/pip_package/BUILD +++ b/tensorflow/tools/pip_package/BUILD @@ -88,6 +88,9 @@ COMMON_PIP_DEPS = [ "//tensorflow/contrib/timeseries:timeseries_pip", "//tensorflow/contrib/tpu", "//tensorflow/examples/tutorials/mnist:package", + "//tensorflow/lite/python:interpreter_test_data", + "//tensorflow/lite/python:tflite_convert", + "//tensorflow/lite/toco/python:toco_from_protos", # "//tensorflow/python/autograph/converters:converters", # "//tensorflow/python/autograph/core:core", "//tensorflow/python/autograph/core:test_lib", @@ -124,7 +127,7 @@ COMMON_PIP_DEPS = [ py_binary( name = "simple_console_for_windows", srcs = ["simple_console_for_windows.py"], - data = COMMON_PIP_DEPS, + data = COMMON_PIP_DEPS + ["//tensorflow/python:pywrap_tensorflow_import_lib_file"], srcs_version = "PY2AND3", deps = ["//tensorflow:tensorflow_py"], ) @@ -132,6 +135,7 @@ py_binary( filegroup( name = "licenses", data = [ + "//third_party/icu/data:LICENSE", "//third_party/eigen3:LICENSE", "//third_party/fft2d:LICENSE", "//third_party/hadoop:LICENSE.txt", @@ -227,15 +231,9 @@ sh_binary( data = select({ "//tensorflow:windows": [ ":simple_console_for_windows", - "//tensorflow/lite/python:interpreter_test_data", - "//tensorflow/lite/python:tflite_convert", - "//tensorflow/lite/toco/python:toco_from_protos", ], "//conditions:default": COMMON_PIP_DEPS + [ ":simple_console", - "//tensorflow/lite/python:interpreter_test_data", - "//tensorflow/lite/python:tflite_convert", - "//tensorflow/lite/toco/python:toco_from_protos", ], }) + if_mkl_ml(["//third_party/mkl:intel_binary_blob"]), ) diff --git a/tensorflow/tools/pip_package/setup.py b/tensorflow/tools/pip_package/setup.py index 07475cc0c4..85c913f158 100644 --- a/tensorflow/tools/pip_package/setup.py +++ b/tensorflow/tools/pip_package/setup.py @@ -87,7 +87,8 @@ if 'tf_nightly' in project_name: for i, pkg in enumerate(REQUIRED_PACKAGES): if 'tensorboard' in pkg: REQUIRED_PACKAGES[i] = 'tb-nightly >= 1.13.0a0, < 1.14.0a0' - break + if 'tensorflow_estimator' in pkg: + REQUIRED_PACKAGES[i] = 'tf-estimator-nightly' # weakref.finalize and enum were introduced in Python 3.4 if sys.version_info < (3, 4): @@ -106,6 +107,7 @@ CONSOLE_SCRIPTS = [ # TensorBoard command, pip will inappropriately remove it during install, # even though the command is not removed, just moved to a different wheel. 'tensorboard = tensorboard.main:run_main', + 'tf_upgrade_v2 = tensorflow.tools.compatibility.tf_upgrade_v2_main:main', ] # pylint: enable=line-too-long diff --git a/tensorflow/workspace.bzl b/tensorflow/workspace.bzl index 6d3562caef..7e5f84be16 100755 --- a/tensorflow/workspace.bzl +++ b/tensorflow/workspace.bzl @@ -77,31 +77,31 @@ def tf_workspace(path_prefix = "", tf_repo_name = ""): mkl_repository( name = "mkl_linux", build_file = clean_dep("//third_party/mkl:mkl.BUILD"), - sha256 = "e2233534a9d15c387e22260997af4312a39e9f86f791768409be273b5453c4e6", - strip_prefix = "mklml_lnx_2019.0.20180710", + sha256 = "f00dc3b142a5be399bdeebd7e7ea369545a35d4fb84c86f98b6b048d72685295", + strip_prefix = "mklml_lnx_2019.0.1.20180928", urls = [ - "https://mirror.bazel.build/github.com/intel/mkl-dnn/releases/download/v0.16/mklml_lnx_2019.0.20180710.tgz", - "https://github.com/intel/mkl-dnn/releases/download/v0.16/mklml_lnx_2019.0.20180710.tgz", + "https://mirror.bazel.build/github.com/intel/mkl-dnn/releases/download/v0.17-rc/mklml_lnx_2019.0.1.20180928.tgz", + "https://github.com/intel/mkl-dnn/releases/download/v0.17-rc/mklml_lnx_2019.0.1.20180928.tgz", ], ) mkl_repository( name = "mkl_windows", build_file = clean_dep("//third_party/mkl:mkl.BUILD"), - sha256 = "3fdcff17b018a0082491adf3ba143358265336a801646e46e0191ec8d58d24a2", - strip_prefix = "mklml_win_2019.0.20180710", + sha256 = "efef90b7b9613fab10f44c8ac4ff28db613a112c64ed94826d7e44df09c44b0b", + strip_prefix = "mklml_win_2019.0.1.20180928", urls = [ - "https://mirror.bazel.build/github.com/intel/mkl-dnn/releases/download/v0.16/mklml_win_2019.0.20180710.zip", - "https://github.com/intel/mkl-dnn/releases/download/v0.16/mklml_win_2019.0.20180710.zip", + "https://mirror.bazel.build/github.com/intel/mkl-dnn/releases/download/v0.17-rc/mklml_win_2019.0.1.20180928.zip", + "https://github.com/intel/mkl-dnn/releases/download/v0.17-rc/mklml_win_2019.0.1.20180928.zip", ], ) mkl_repository( name = "mkl_darwin", build_file = clean_dep("//third_party/mkl:mkl.BUILD"), - sha256 = "411a30014a938eb83fb9f37b3dbe8e371b106fc1dd621fc23123cadc72737ce6", - strip_prefix = "mklml_mac_2019.0.20180710", + sha256 = "83f02938a0c095274db7b8b7b694157abafa3837c5cbaef740440d466c86a477", + strip_prefix = "mklml_mac_2019.0.1.20180928", urls = [ - "https://mirror.bazel.build/github.com/intel/mkl-dnn/releases/download/v0.16/mklml_mac_2019.0.20180710.tgz", - "https://github.com/intel/mkl-dnn/releases/download/v0.16/mklml_mac_2019.0.20180710.tgz", + "https://mirror.bazel.build/github.com/intel/mkl-dnn/releases/download/v0.17-rc/mklml_mac_2019.0.1.20180928.tgz", + "https://github.com/intel/mkl-dnn/releases/download/v0.17-rc/mklml_mac_2019.0.1.20180928.tgz", ], ) @@ -112,11 +112,11 @@ def tf_workspace(path_prefix = "", tf_repo_name = ""): tf_http_archive( name = "mkl_dnn", build_file = clean_dep("//third_party/mkl_dnn:mkldnn.BUILD"), - sha256 = "363cc9239eacf8e7917753c6d8c94f767e4cd049160d0654a61ef32d5e1b3049", - strip_prefix = "mkl-dnn-4e333787e0d66a1dca1218e99a891d493dbc8ef1", + sha256 = "b100f57af4a2b59a3a37a1ba38f77b644d2107d758a1a7f4e51310063cd21e73", + strip_prefix = "mkl-dnn-733fc908874c71a5285043931a1cf80aa923165c", urls = [ - "https://mirror.bazel.build/github.com/intel/mkl-dnn/archive/4e333787e0d66a1dca1218e99a891d493dbc8ef1.tar.gz", - "https://github.com/intel/mkl-dnn/archive/4e333787e0d66a1dca1218e99a891d493dbc8ef1.tar.gz", + "https://mirror.bazel.build/github.com/intel/mkl-dnn/archive/733fc908874c71a5285043931a1cf80aa923165c.tar.gz", + "https://github.com/intel/mkl-dnn/archive/733fc908874c71a5285043931a1cf80aa923165c.tar.gz", ], ) @@ -134,12 +134,11 @@ def tf_workspace(path_prefix = "", tf_repo_name = ""): tf_http_archive( name = "eigen_archive", build_file = clean_dep("//third_party:eigen.BUILD"), - patch_file = clean_dep("//third_party:eigen_reshaped.patch"), - sha256 = "d66cec3b54b3dfaa4666c1d49481a7197f93fc078cd53c54e2b4a8893a529c9f", - strip_prefix = "eigen-eigen-b4890dc6bc34", + sha256 = "37a483ec219c43219b6e0fc07e799277a4a36abb2b9f4162cfcd256aa211eae8", + strip_prefix = "eigen-eigen-2e50f4a5542a", urls = [ - "https://mirror.bazel.build/bitbucket.org/eigen/eigen/get/b4890dc6bc34.tar.gz", - "https://bitbucket.org/eigen/eigen/get/b4890dc6bc34.tar.gz", + "https://mirror.bazel.build/bitbucket.org/eigen/eigen/get/2e50f4a5542a.tar.gz", + "https://bitbucket.org/eigen/eigen/get/2e50f4a5542a.tar.gz", ], ) @@ -180,15 +179,15 @@ def tf_workspace(path_prefix = "", tf_repo_name = ""): tf_http_archive( name = "com_github_googlecloudplatform_google_cloud_cpp", - sha256 = "fdd3b3aecce60987e5525e55bf3a21d68a8695320bd5b980775af6507eec3944", - strip_prefix = "google-cloud-cpp-14760a86c4ffab9943b476305c4fe927ad95db1c", + sha256 = "3ade2072e6588ff56c0434abe6c63aa5f3f2d56be15a299bafc7e9cdf0a12c17", + strip_prefix = "google-cloud-cpp-0.3.0", system_build_file = clean_dep("//third_party/systemlibs:google_cloud_cpp.BUILD"), system_link_files = { "//third_party/systemlibs:google_cloud_cpp.google.cloud.bigtable.BUILD": "google/cloud/bigtable/BUILD", }, urls = [ - "https://mirror.bazel.build/github.com/GoogleCloudPlatform/google-cloud-cpp/archive/14760a86c4ffab9943b476305c4fe927ad95db1c.tar.gz", - "https://github.com/GoogleCloudPlatform/google-cloud-cpp/archive/14760a86c4ffab9943b476305c4fe927ad95db1c.tar.gz", + "https://mirror.bazel.build/github.com/GoogleCloudPlatform/google-cloud-cpp/archive/v0.3.0.tar.gz", + "https://github.com/GoogleCloudPlatform/google-cloud-cpp/archive/v0.3.0.tar.gz", ], ) @@ -348,11 +347,11 @@ def tf_workspace(path_prefix = "", tf_repo_name = ""): ) PROTOBUF_URLS = [ - "https://mirror.bazel.build/github.com/google/protobuf/archive/v3.6.1.tar.gz", - "https://github.com/google/protobuf/archive/v3.6.1.tar.gz", + "https://mirror.bazel.build/github.com/google/protobuf/archive/v3.6.1.1.tar.gz", + "https://github.com/google/protobuf/archive/v3.6.1.1.tar.gz", ] - PROTOBUF_SHA256 = "3d4e589d81b2006ca603c1ab712c9715a76227293032d05b26fca603f90b3f5b" - PROTOBUF_STRIP_PREFIX = "protobuf-3.6.1" + PROTOBUF_SHA256 = "1ade182f91f0fa6c6116195def5d22270e01b9d03fe91319e4c6215022d0d24b" + PROTOBUF_STRIP_PREFIX = "protobuf-3.6.1.1" tf_http_archive( name = "protobuf_archive", @@ -473,11 +472,11 @@ def tf_workspace(path_prefix = "", tf_repo_name = ""): tf_http_archive( name = "llvm", build_file = clean_dep("//third_party/llvm:llvm.autogenerated.BUILD"), - sha256 = "a22a9b4c3af50a52ba0015b6987bba7202c3ec8e1d40ae76ee7d7643638936ae", - strip_prefix = "llvm-b4ace5f3454131a3070ef7c11e19e42fc9a80b4e", + sha256 = "34170a4aa07e434dd537d98a705dcf1b3901f73820fe1d6b9370e8c1c94e9157", + strip_prefix = "llvm-0487bd8f42c8b38166ff825d56014d0ff49db604", urls = [ - "https://mirror.bazel.build/github.com/llvm-mirror/llvm/archive/b4ace5f3454131a3070ef7c11e19e42fc9a80b4e.tar.gz", - "https://github.com/llvm-mirror/llvm/archive/b4ace5f3454131a3070ef7c11e19e42fc9a80b4e.tar.gz", + "https://mirror.bazel.build/github.com/llvm-mirror/llvm/archive/0487bd8f42c8b38166ff825d56014d0ff49db604.tar.gz", + "https://github.com/llvm-mirror/llvm/archive/0487bd8f42c8b38166ff825d56014d0ff49db604.tar.gz", ], ) @@ -690,11 +689,11 @@ def tf_workspace(path_prefix = "", tf_repo_name = ""): tf_http_archive( name = "arm_neon_2_x86_sse", build_file = clean_dep("//third_party:arm_neon_2_x86_sse.BUILD"), - sha256 = "c8d90aa4357f8079d427e87a6f4c493da1fa4140aee926c05902d7ec1533d9a5", - strip_prefix = "ARM_NEON_2_x86_SSE-0f77d9d182265259b135dad949230ecbf1a2633d", + sha256 = "213733991310b904b11b053ac224fee2d4e0179e46b52fe7f8735b8831e04dcc", + strip_prefix = "ARM_NEON_2_x86_SSE-1200fe90bb174a6224a525ee60148671a786a71f", urls = [ - "https://mirror.bazel.build/github.com/intel/ARM_NEON_2_x86_SSE/archive/0f77d9d182265259b135dad949230ecbf1a2633d.tar.gz", - "https://github.com/intel/ARM_NEON_2_x86_SSE/archive/0f77d9d182265259b135dad949230ecbf1a2633d.tar.gz", + "https://mirror.bazel.build/github.com/intel/ARM_NEON_2_x86_SSE/archive/1200fe90bb174a6224a525ee60148671a786a71f.tar.gz", + "https://github.com/intel/ARM_NEON_2_x86_SSE/archive/1200fe90bb174a6224a525ee60148671a786a71f.tar.gz", ], ) diff --git a/third_party/clang_toolchain/download_clang.bzl b/third_party/clang_toolchain/download_clang.bzl index 9023e250b2..7ced902747 100644 --- a/third_party/clang_toolchain/download_clang.bzl +++ b/third_party/clang_toolchain/download_clang.bzl @@ -39,15 +39,15 @@ def download_clang(repo_ctx, out_folder): # Latest CLANG_REVISION and CLANG_SUB_REVISION of the Chromiums's release # can be found in https://chromium.googlesource.com/chromium/src/tools/clang/+/master/scripts/update.py - CLANG_REVISION = "346388" + CLANG_REVISION = "347933" CLANG_SUB_REVISION = 1 package_version = "%s-%s" % (CLANG_REVISION, CLANG_SUB_REVISION) checksums = { - "Linux_x64": "5e5564e4e743414c7eaec9fd9e739732ddd2a343e49bde4c88fc2530b1c598b9", - "Mac": "19271a7cc5c2bcaf9643d3dd622b5458569dc662bbc58f63b129cf6e3a4e3243", - "Win": "60b0bd1f11e53892109f4159e2aba0f803604823e07875ca98b82bd5628d7f4d", + "Linux_x64": "cae3643fdf5d46fc9bc8731212bb37573547148d90b64b083165e090133d11b0", + "Mac": "083a0e91a38c06e568652313ac7372b17a101268f7d65533d721ca30413442b4", + "Win": "43160487cfc7e88076a369a2b6e8e4a0f42e104c28d8903f3aaa62d630aba949", } platform_folder = _get_platform_folder(repo_ctx.os.name) diff --git a/third_party/eigen_reshaped.patch b/third_party/eigen_reshaped.patch deleted file mode 100644 index 7acfdcf9fe..0000000000 --- a/third_party/eigen_reshaped.patch +++ /dev/null @@ -1,48 +0,0 @@ ---- a/Eigen/src/Core/util/ReshapedHelper.h (date 1541195478000) -+++ b/Eigen/src/Core/util/ReshapedHelper.h (date 1541195478000) -@@ -39,6 +39,11 @@ - return total/other; - } - -+template -+struct get_compiletime_reshape_order { -+ enum { value = Order == AutoOrder ? Flags & RowMajorBit : Order }; -+}; -+ - } - - } // end namespace Eigen ---- a/Eigen/src/plugins/ReshapedMethods.h (date 1541195254000) -+++ b/Eigen/src/plugins/ReshapedMethods.h (date 1541195254000) -@@ -105,13 +105,13 @@ - inline Reshaped::value, - internal::get_compiletime_reshape_size::value, -- (Order==AutoOrder?Flags&RowMajorBit:Order)> -+ internal::get_compiletime_reshape_order::value> - reshaped(NRowsType nRows, NColsType nCols) EIGEN_RESHAPED_METHOD_CONST - { - return Reshaped::value, - internal::get_compiletime_reshape_size::value, -- (Order==AutoOrder?Flags&RowMajorBit:Order)> -+ internal::get_compiletime_reshape_order::value> - (derived(), - internal::get_runtime_reshape_size(nRows,internal::get_runtime_value(nCols),size()), - internal::get_runtime_reshape_size(nCols,internal::get_runtime_value(nRows),size())); -@@ -128,11 +128,13 @@ - - template - EIGEN_DEVICE_FUNC --inline Reshaped -+inline Reshaped::value> - reshaped() EIGEN_RESHAPED_METHOD_CONST - { - EIGEN_STATIC_ASSERT(Order==RowMajor || Order==ColMajor || Order==AutoOrder, INVALID_TEMPLATE_PARAMETER); -- return Reshaped -+ return Reshaped::value> - (derived(), size(), 1); - } - \ No newline at end of file diff --git a/third_party/googleapis.BUILD b/third_party/googleapis.BUILD index 95e999af18..b8871eda72 100644 --- a/third_party/googleapis.BUILD +++ b/third_party/googleapis.BUILD @@ -13,7 +13,9 @@ # limitations under the License. package(default_visibility = ["//visibility:public"]) + licenses(["notice"]) # Apache 2.0 + exports_files(["LICENSE"]) load("@protobuf_archive//:protobuf.bzl", "cc_proto_library") @@ -21,6 +23,9 @@ load("@protobuf_archive//:protobuf.bzl", "cc_proto_library") cc_proto_library( name = "bigtable_protos", srcs = [ + "google/api/annotations.proto", + "google/api/auth.proto", + "google/api/http.proto", "google/bigtable/admin/v2/bigtable_instance_admin.proto", "google/bigtable/admin/v2/bigtable_table_admin.proto", "google/bigtable/admin/v2/common.proto", @@ -31,15 +36,12 @@ cc_proto_library( "google/iam/v1/iam_policy.proto", "google/iam/v1/policy.proto", "google/longrunning/operations.proto", - "google/rpc/status.proto", "google/rpc/error_details.proto", - "google/api/annotations.proto", - "google/api/auth.proto", - "google/api/http.proto", + "google/rpc/status.proto", ], include = ".", - protoc = "@protobuf_archive//:protoc", default_runtime = "@protobuf_archive//:protobuf", - deps = ["@protobuf_archive//:cc_wkt_protos"], + protoc = "@protobuf_archive//:protoc", use_grpc_plugin = True, + deps = ["@protobuf_archive//:cc_wkt_protos"], ) diff --git a/third_party/gpus/crosstool/CROSSTOOL.tpl b/third_party/gpus/crosstool/CROSSTOOL.tpl index 3189cf8e31..921188cbb4 100644 --- a/third_party/gpus/crosstool/CROSSTOOL.tpl +++ b/third_party/gpus/crosstool/CROSSTOOL.tpl @@ -184,7 +184,8 @@ toolchain { action: "c++-link-dynamic-library" action: "c++-link-nodeps-dynamic-library" flag_group { - flag:"-no-canonical-prefixes" + flag: "-no-canonical-prefixes" + %{extra_no_canonical_prefixes_flags} } } } diff --git a/third_party/gpus/cuda_configure.bzl b/third_party/gpus/cuda_configure.bzl index 831a3067b2..03c67bcb3d 100644 --- a/third_party/gpus/cuda_configure.bzl +++ b/third_party/gpus/cuda_configure.bzl @@ -1418,6 +1418,7 @@ def _create_local_cuda_repository(repository_ctx): flag: "-Wno-invalid-partial-specialization" """ cuda_defines["%{host_compiler_includes}"] = host_compiler_includes + cuda_defines["%{extra_no_canonical_prefixes_flags}"] = "" _tpl(repository_ctx, "crosstool:BUILD", { "%{linker_files}": ":empty", "%{win_linker_files}": ":empty" @@ -1439,6 +1440,14 @@ def _create_local_cuda_repository(repository_ctx): repository_ctx, cuda_config) + "\n cxx_builtin_include_directory: \"%s\"" % cupti_header_dir + "\n cxx_builtin_include_directory: \"%s\"" % cudnn_header_dir) + + # For gcc, do not canonicalize system header paths; some versions of gcc + # pick the shortest possible path for system includes when creating the + # .d file - given that includes that are prefixed with "../" multiple + # time quickly grow longer than the root of the tree, this can lead to + # bazel's header check failing. + cuda_defines["%{extra_no_canonical_prefixes_flags}"] = ( + "flag: \"-fno-canonical-system-headers\"") nvcc_path = str( repository_ctx.path("%s/bin/nvcc%s" % ( cuda_config.cuda_toolkit_path, diff --git a/third_party/gpus/rocm_configure.bzl b/third_party/gpus/rocm_configure.bzl index 9108639b0b..6df6799bd7 100644 --- a/third_party/gpus/rocm_configure.bzl +++ b/third_party/gpus/rocm_configure.bzl @@ -105,7 +105,7 @@ def get_cxx_inc_directories(repository_ctx, cc): return includes_cpp + [ inc for inc in includes_c - if inc not in includes_cpp_set + if inc not in includes_cpp_set.to_list() ] def auto_configure_fail(msg): diff --git a/third_party/icu/data/BUILD.bazel b/third_party/icu/data/BUILD.bazel new file mode 100644 index 0000000000..7db21566e4 --- /dev/null +++ b/third_party/icu/data/BUILD.bazel @@ -0,0 +1,46 @@ +package( + default_visibility = ["//visibility:public"], +) + +licenses(["notice"]) # Apache 2.0 + +exports_files(["LICENSE"]) + +# Data for core MIME/Unix/Windows encodings: +# ISO 8859-2..9, 15; Windows-125x; EUC-CN; GBK (Windows cp936); GB 18030; +# Big5 (Windows cp950); SJIS (Windows cp932); EUC-JP; EUC-KR, KS C 5601; +# Windows cp949. Data is pre-processed for little-endian platforms. To replicate +# this pre-processing (if you want additional encodings, for example), do the +# following: +# +# First, download, build, and install ICU. This installs tools such as makeconv. +# Then, run the following from your icu4c/source directory: +# $ cd data/mappings +# $ rm *.cnv # there shouldn't be any .cnv files here to begin with +# $ grep \.ucm ucmcore.mk | \ +# sed 's/\(UCM_SOURCE_CORE=\)\?\([^ ]\+\.ucm\)\\\?/\2/g' | \ +# tr '\n' ' ' | xargs makeconv +# $ ls *.cnv > filelist.lst +# $ pkgdata -m common -p ucmcore filelist.lst +# $ genccode -f custom_conversion_data ucmcore.dat +# This creates custom_conversion_data.c. You will need to change the target +# :conversion_data to depend on your custom source instead of :conversion_data.c +filegroup( + name = "conversion_files", + srcs = glob(["icu_conversion_data.c.gz.*"]), +) + +# Data files are compressed and split to work around git performance degradation +# around large files. +genrule( + name = "merge_conversion_data", + srcs = [":conversion_files"], + outs = ["conversion_data.c"], + cmd = "cat $(locations :conversion_files) | gunzip > $@", +) + +cc_library( + name = "conversion_data", + srcs = [":conversion_data.c"], + deps = ["@icu//:headers"], +) diff --git a/third_party/icu/data/LICENSE b/third_party/icu/data/LICENSE new file mode 100644 index 0000000000..25b6eb9d34 --- /dev/null +++ b/third_party/icu/data/LICENSE @@ -0,0 +1,414 @@ +COPYRIGHT AND PERMISSION NOTICE (ICU 58 and later) + +Copyright © 1991-2018 Unicode, Inc. All rights reserved. +Distributed under the Terms of Use in http://www.unicode.org/copyright.html. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Unicode data files and any associated documentation +(the "Data Files") or Unicode software and any associated documentation +(the "Software") to deal in the Data Files or Software +without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, and/or sell copies of +the Data Files or Software, and to permit persons to whom the Data Files +or Software are furnished to do so, provided that either +(a) this copyright and permission notice appear with all copies +of the Data Files or Software, or +(b) this copyright and permission notice appear in associated +Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT OF THIRD PARTY RIGHTS. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS +NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL +DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THE DATA FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, +use or other dealings in these Data Files or Software without prior +written authorization of the copyright holder. + +--------------------- + +Third-Party Software Licenses + +This section contains third-party software notices and/or additional +terms for licensed third-party software components included within ICU +libraries. + +1. ICU License - ICU 1.8.1 to ICU 57.1 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright (c) 1995-2016 International Business Machines Corporation and others +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, and/or sell copies of the Software, and to permit persons +to whom the Software is furnished to do so, provided that the above +copyright notice(s) and this permission notice appear in all copies of +the Software and that both the above copyright notice(s) and this +permission notice appear in supporting documentation. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY +SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER +RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF +CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, use +or other dealings in this Software without prior written authorization +of the copyright holder. + +All trademarks and registered trademarks mentioned herein are the +property of their respective owners. + +2. Chinese/Japanese Word Break Dictionary Data (cjdict.txt) + + # The Google Chrome software developed by Google is licensed under + # the BSD license. Other software included in this distribution is + # provided under other licenses, as set forth below. + # + # The BSD License + # http://opensource.org/licenses/bsd-license.php + # Copyright (C) 2006-2008, Google Inc. + # + # All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions are met: + # + # Redistributions of source code must retain the above copyright notice, + # this list of conditions and the following disclaimer. + # Redistributions in binary form must reproduce the above + # copyright notice, this list of conditions and the following + # disclaimer in the documentation and/or other materials provided with + # the distribution. + # Neither the name of Google Inc. nor the names of its + # contributors may be used to endorse or promote products derived from + # this software without specific prior written permission. + # + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + # CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + # + # + # The word list in cjdict.txt are generated by combining three word lists + # listed below with further processing for compound word breaking. The + # frequency is generated with an iterative training against Google web + # corpora. + # + # * Libtabe (Chinese) + # - https://sourceforge.net/project/?group_id=1519 + # - Its license terms and conditions are shown below. + # + # * IPADIC (Japanese) + # - http://chasen.aist-nara.ac.jp/chasen/distribution.html + # - Its license terms and conditions are shown below. + # + # ---------COPYING.libtabe ---- BEGIN-------------------- + # + # /* + # * Copyright (c) 1999 TaBE Project. + # * Copyright (c) 1999 Pai-Hsiang Hsiao. + # * All rights reserved. + # * + # * Redistribution and use in source and binary forms, with or without + # * modification, are permitted provided that the following conditions + # * are met: + # * + # * . Redistributions of source code must retain the above copyright + # * notice, this list of conditions and the following disclaimer. + # * . Redistributions in binary form must reproduce the above copyright + # * notice, this list of conditions and the following disclaimer in + # * the documentation and/or other materials provided with the + # * distribution. + # * . Neither the name of the TaBE Project nor the names of its + # * contributors may be used to endorse or promote products derived + # * from this software without specific prior written permission. + # * + # * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + # * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + # * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + # * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + # * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + # * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + # * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + # * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + # * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + # * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + # * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + # * OF THE POSSIBILITY OF SUCH DAMAGE. + # */ + # + # /* + # * Copyright (c) 1999 Computer Systems and Communication Lab, + # * Institute of Information Science, Academia + # * Sinica. All rights reserved. + # * + # * Redistribution and use in source and binary forms, with or without + # * modification, are permitted provided that the following conditions + # * are met: + # * + # * . Redistributions of source code must retain the above copyright + # * notice, this list of conditions and the following disclaimer. + # * . Redistributions in binary form must reproduce the above copyright + # * notice, this list of conditions and the following disclaimer in + # * the documentation and/or other materials provided with the + # * distribution. + # * . Neither the name of the Computer Systems and Communication Lab + # * nor the names of its contributors may be used to endorse or + # * promote products derived from this software without specific + # * prior written permission. + # * + # * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + # * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + # * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + # * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + # * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + # * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + # * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + # * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + # * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + # * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + # * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + # * OF THE POSSIBILITY OF SUCH DAMAGE. + # */ + # + # Copyright 1996 Chih-Hao Tsai @ Beckman Institute, + # University of Illinois + # c-tsai4@uiuc.edu http://casper.beckman.uiuc.edu/~c-tsai4 + # + # ---------------COPYING.libtabe-----END-------------------------------- + # + # + # ---------------COPYING.ipadic-----BEGIN------------------------------- + # + # Copyright 2000, 2001, 2002, 2003 Nara Institute of Science + # and Technology. All Rights Reserved. + # + # Use, reproduction, and distribution of this software is permitted. + # Any copy of this software, whether in its original form or modified, + # must include both the above copyright notice and the following + # paragraphs. + # + # Nara Institute of Science and Technology (NAIST), + # the copyright holders, disclaims all warranties with regard to this + # software, including all implied warranties of merchantability and + # fitness, in no event shall NAIST be liable for + # any special, indirect or consequential damages or any damages + # whatsoever resulting from loss of use, data or profits, whether in an + # action of contract, negligence or other tortuous action, arising out + # of or in connection with the use or performance of this software. + # + # A large portion of the dictionary entries + # originate from ICOT Free Software. The following conditions for ICOT + # Free Software applies to the current dictionary as well. + # + # Each User may also freely distribute the Program, whether in its + # original form or modified, to any third party or parties, PROVIDED + # that the provisions of Section 3 ("NO WARRANTY") will ALWAYS appear + # on, or be attached to, the Program, which is distributed substantially + # in the same form as set out herein and that such intended + # distribution, if actually made, will neither violate or otherwise + # contravene any of the laws and regulations of the countries having + # jurisdiction over the User or the intended distribution itself. + # + # NO WARRANTY + # + # The program was produced on an experimental basis in the course of the + # research and development conducted during the project and is provided + # to users as so produced on an experimental basis. Accordingly, the + # program is provided without any warranty whatsoever, whether express, + # implied, statutory or otherwise. The term "warranty" used herein + # includes, but is not limited to, any warranty of the quality, + # performance, merchantability and fitness for a particular purpose of + # the program and the nonexistence of any infringement or violation of + # any right of any third party. + # + # Each user of the program will agree and understand, and be deemed to + # have agreed and understood, that there is no warranty whatsoever for + # the program and, accordingly, the entire risk arising from or + # otherwise connected with the program is assumed by the user. + # + # Therefore, neither ICOT, the copyright holder, or any other + # organization that participated in or was otherwise related to the + # development of the program and their respective officials, directors, + # officers and other employees shall be held liable for any and all + # damages, including, without limitation, general, special, incidental + # and consequential damages, arising out of or otherwise in connection + # with the use or inability to use the program or any product, material + # or result produced or otherwise obtained by using the program, + # regardless of whether they have been advised of, or otherwise had + # knowledge of, the possibility of such damages at any time during the + # project or thereafter. Each user will be deemed to have agreed to the + # foregoing by his or her commencement of use of the program. The term + # "use" as used herein includes, but is not limited to, the use, + # modification, copying and distribution of the program and the + # production of secondary products from the program. + # + # In the case where the program, whether in its original form or + # modified, was distributed or delivered to or received by a user from + # any person, organization or entity other than ICOT, unless it makes or + # grants independently of ICOT any specific warranty to the user in + # writing, such person, organization or entity, will also be exempted + # from and not be held liable to the user for any such damages as noted + # above as far as the program is concerned. + # + # ---------------COPYING.ipadic-----END---------------------------------- + +3. Lao Word Break Dictionary Data (laodict.txt) + + # Copyright (c) 2013 International Business Machines Corporation + # and others. All Rights Reserved. + # + # Project: http://code.google.com/p/lao-dictionary/ + # Dictionary: http://lao-dictionary.googlecode.com/git/Lao-Dictionary.txt + # License: http://lao-dictionary.googlecode.com/git/Lao-Dictionary-LICENSE.txt + # (copied below) + # + # This file is derived from the above dictionary, with slight + # modifications. + # ---------------------------------------------------------------------- + # Copyright (C) 2013 Brian Eugene Wilson, Robert Martin Campbell. + # All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, + # are permitted provided that the following conditions are met: + # + # + # Redistributions of source code must retain the above copyright notice, this + # list of conditions and the following disclaimer. Redistributions in + # binary form must reproduce the above copyright notice, this list of + # conditions and the following disclaimer in the documentation and/or + # other materials provided with the distribution. + # + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + # OF THE POSSIBILITY OF SUCH DAMAGE. + # -------------------------------------------------------------------------- + +4. Burmese Word Break Dictionary Data (burmesedict.txt) + + # Copyright (c) 2014 International Business Machines Corporation + # and others. All Rights Reserved. + # + # This list is part of a project hosted at: + # github.com/kanyawtech/myanmar-karen-word-lists + # + # -------------------------------------------------------------------------- + # Copyright (c) 2013, LeRoy Benjamin Sharon + # All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: Redistributions of source code must retain the above + # copyright notice, this list of conditions and the following + # disclaimer. Redistributions in binary form must reproduce the + # above copyright notice, this list of conditions and the following + # disclaimer in the documentation and/or other materials provided + # with the distribution. + # + # Neither the name Myanmar Karen Word Lists, nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + # CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + # SUCH DAMAGE. + # -------------------------------------------------------------------------- + +5. Time Zone Database + + ICU uses the public domain data and code derived from Time Zone +Database for its time zone support. The ownership of the TZ database +is explained in BCP 175: Procedure for Maintaining the Time Zone +Database section 7. + + # 7. Database Ownership + # + # The TZ database itself is not an IETF Contribution or an IETF + # document. Rather it is a pre-existing and regularly updated work + # that is in the public domain, and is intended to remain in the + # public domain. Therefore, BCPs 78 [RFC5378] and 79 [RFC3979] do + # not apply to the TZ Database or contributions that individuals make + # to it. Should any claims be made and substantiated against the TZ + # Database, the organization that is providing the IANA + # Considerations defined in this RFC, under the memorandum of + # understanding with the IETF, currently ICANN, may act in accordance + # with all competent court orders. No ownership claims will be made + # by ICANN or the IETF Trust on the database or the code. Any person + # making a contribution to the database or code waives all rights to + # future claims in that contribution or in the TZ Database. + +6. Google double-conversion + +Copyright 2006-2011, the V8 project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/third_party/icu/data/icu_conversion_data.c.gz.aa b/third_party/icu/data/icu_conversion_data.c.gz.aa new file mode 100644 index 0000000000000000000000000000000000000000..b68a2c6516f8183e805c509a9139cf63d1ee3fa5 GIT binary patch literal 177696 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 !EW9Gr>iU*_O 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Ǹ$xps+yyuyR;QvPb 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ξ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 literal 0 HcmV?d00001 diff --git a/third_party/icu/data/icu_conversion_data.c.gz.ad b/third_party/icu/data/icu_conversion_data.c.gz.ad new file mode 100644 index 0000000000000000000000000000000000000000..d5abb06b8ca21e1e6116ef1732c661c815b1489a GIT binary patch literal 177696 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 MR>NZ6c^el30N55?$N&HU literal 0 HcmV?d00001 diff --git a/third_party/icu/data/icu_conversion_data.c.gz.ae b/third_party/icu/data/icu_conversion_data.c.gz.ae new file mode 100644 index 0000000000000000000000000000000000000000..0e54fdb9eaffd814477460f71bc194104c1b247d GIT binary patch literal 177696 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

6i{vi;aMkMJksbp+~stC>bEu89JS#Bzt$;y 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%0D_ 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

6B9D03Qn7{P=EE3 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 Lw%fap+;#*2xAJ6r literal 0 HcmV?d00001 diff --git a/third_party/icu/data/icu_conversion_data.c.gz.ag b/third_party/icu/data/icu_conversion_data.c.gz.ag new file mode 100644 index 0000000000000000000000000000000000000000..bde20b6da6253d866f87fcadc7e6c3571bd64d44 GIT binary patch literal 177696 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%0AWYT@Dfe z6(N`EMBj#A+&8EGqX@`%47H{VO%f(YU_Z%2#^_;auGJM*^G>Rk<7lk4%Eb9oRc@u&9D%vu(bp$#6Eqwk!sY ziCgqb!INQfVAArxF7D!Xkdgyb;e1@OWB!ZYLr5k5Nu~gnjUl$6c!BsL zTckm&Bh(pEPlFTTS#Aem!~PI~OFfA8|k?@?XR zqFVJ^Ljlm@%omJ?%7Sj>mn^JN{sEwDP;tFK&LN+2H4)@^*DlZqU+OPh5U$dCZ=ZG2boL zZZ)HT%X&p{bq390DwDp=&4@RRn=Jg+`rTnuP{7Cu4i))Xl?_ysz9;k$k05&<5qa(sU@GL{FQ_?uu$R> z+`t0+Zmn^KeaE+EFmaBYOC`mg^I)5kKaiN@RNDrCu2$5 z(<#$ZxV$|BKX{fcQZWvg6sUGmVsc7}rR(kHb%+jhHKOtmkhEI@&+Dq2SS86G4H6VX%igbJj&Ry~N>aa-=*k~M24FFVzL;@{_@mBluVFU+n#QN6C=@UV4y z_vY#E=U(UEQ}S4RV0 z7+v*XJ~K<3Z}QTBb3yA;HKn@rVqJwY5`VMToR!hcHuF1BaR-|oQH#YZ)e!S89EPDCUOT8_@2I~8cO=QW+ z`>~{vX_7^ph~ZhK)v*&<7kexXy}FLeVJ*{zFU@LO^Ed0hRbM3N$`+Wk;1eT5M_^!r zz*AOV^__DvTVF`d^wgd|;j8KMIAFC?s^IXqc&1K9?GJ2(OImc)FuL-72I+M;tt<#zGY+_&fRn
  • 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 z

    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

    1kZqjdwA|M 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 PJ$Z&eM`#1LuTCrgk|N3@ literal 0 HcmV?d00001 diff --git a/third_party/icu/data/icu_conversion_data.c.gz.aj b/third_party/icu/data/icu_conversion_data.c.gz.aj new file mode 100644 index 0000000000000000000000000000000000000000..1ae6bce382a05570b46217e1a031414515439a42 GIT binary patch literal 177689 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

    eij=vVwDz!Yt+zkV_t10OG1c$| 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%0A@(&Bj4j;M|&m0$I5q{xrCzJV9bCHP; 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 literal 0 HcmV?d00001 diff --git a/third_party/icu/udata.patch b/third_party/icu/udata.patch new file mode 100644 index 0000000000..d6d59100e4 --- /dev/null +++ b/third_party/icu/udata.patch @@ -0,0 +1,53 @@ +--- /icu4c/source/common/udata.cpp.old 2018-06-19 22:34:56.000000000 -0700 ++++ /icu4c/source/common/udata.cpp 2018-10-19 14:26:09.778950855 -0700 +@@ -18,15 +18,15 @@ + + #include "unicode/utypes.h" /* U_PLATFORM etc. */ + +-#ifdef __GNUC__ +-/* if gcc +-#define ATTRIBUTE_WEAK __attribute__ ((weak)) +-might have to #include some other header +-*/ ++#if defined(__GNUC__) || defined(__SUNPRO_CC) ++# define ATTRIBUTE_WEAK __attribute__ ((weak)) ++#else ++# define ATTRIBUTE_WEAK + #endif + + #include "unicode/putil.h" + #include "unicode/udata.h" ++#include "unicode/umachine.h" + #include "unicode/uversion.h" + #include "charstr.h" + #include "cmemory.h" +@@ -641,10 +641,11 @@ + * partial-data-library access functions where each returns a pointer + * to its data package, if it is linked in. + */ +-/* +-extern const void *uprv_getICUData_collation(void) ATTRIBUTE_WEAK; +-extern const void *uprv_getICUData_conversion(void) ATTRIBUTE_WEAK; +-*/ ++ ++//extern "C" const void *uprv_getICUData_collation(void); ++U_CDECL_BEGIN ++const void *uprv_getICUData_conversion(void) ATTRIBUTE_WEAK; ++U_CDECL_END + + /*----------------------------------------------------------------------* + * * +@@ -702,10 +703,11 @@ + if (uprv_getICUData_collation) { + setCommonICUDataPointer(uprv_getICUData_collation(), FALSE, pErrorCode); + } ++ */ + if (uprv_getICUData_conversion) { +- setCommonICUDataPointer(uprv_getICUData_conversion(), FALSE, pErrorCode); ++ setCommonICUDataPointer(uprv_getICUData_conversion(), FALSE, pErrorCode); + } +- */ ++ + #if U_PLATFORM_HAS_WINUWP_API == 0 // Windows UWP Platform does not support dll icu data at this time + setCommonICUDataPointer(&U_ICUDATA_ENTRY_POINT, FALSE, pErrorCode); + { diff --git a/third_party/icu/workspace.bzl b/third_party/icu/workspace.bzl index a4f653e026..f100836b41 100644 --- a/third_party/icu/workspace.bzl +++ b/third_party/icu/workspace.bzl @@ -2,6 +2,11 @@ load("//third_party:repo.bzl", "third_party_http_archive") +# Sanitize a dependency so that it works correctly from code that includes +# TensorFlow as a submodule. +def clean_dep(dep): + return str(Label(dep)) + def repo(): third_party_http_archive( name = "icu", @@ -13,4 +18,5 @@ def repo(): ], build_file = "//third_party/icu:BUILD.bazel", system_build_file = "//third_party/icu:BUILD.system", + patch_file = clean_dep("//third_party/icu:udata.patch"), ) diff --git a/third_party/libxsmm.BUILD b/third_party/libxsmm.BUILD index ee49d281ab..dc7dcc9517 100644 --- a/third_party/libxsmm.BUILD +++ b/third_party/libxsmm.BUILD @@ -38,8 +38,8 @@ genrule( ":libxsmm_interface", ], visibility = [ - "//third_party/eigen3:__pkg__", "//tensorflow/core/kernels:__pkg__", + "//third_party/eigen3:__pkg__", ], ) diff --git a/third_party/llvm/llvm.autogenerated.BUILD b/third_party/llvm/llvm.autogenerated.BUILD index 776935739a..eb468aa65f 100644 --- a/third_party/llvm/llvm.autogenerated.BUILD +++ b/third_party/llvm/llvm.autogenerated.BUILD @@ -823,6 +823,7 @@ cc_library( ]), copts = llvm_copts + ["-Iexternal/llvm/lib/Target/ARM"], deps = [ + ":arm_asm_printer", ":arm_desc", ":arm_info", ":arm_utils", @@ -2141,6 +2142,7 @@ cc_library( ":core", ":global_i_sel", ":mc", + ":profile_data", ":selection_dag", ":support", ":target", diff --git a/third_party/llvm/llvm.bzl b/third_party/llvm/llvm.bzl index 54ca86f327..5a977f82c4 100644 --- a/third_party/llvm/llvm.bzl +++ b/third_party/llvm/llvm.bzl @@ -250,6 +250,7 @@ linux_cmake_vars = { # CMake variables specific to the Darwin (Mac OS X) platform. darwin_cmake_vars = { "HAVE_MALLOC_MALLOC_H": 1, + "HAVE_MALLOC_ZONE_STATISTICS": 1, } # CMake variables specific to the Windows platform. diff --git a/third_party/mkl_dnn/mkldnn.BUILD b/third_party/mkl_dnn/mkldnn.BUILD index 597ac69e2f..7a8ed3bf43 100644 --- a/third_party/mkl_dnn/mkldnn.BUILD +++ b/third_party/mkl_dnn/mkldnn.BUILD @@ -42,8 +42,8 @@ cc_library( "src", "src/common", "src/cpu", - "src/cpu/xbyak", "src/cpu/gemm", + "src/cpu/xbyak", ], nocopts = "-fno-exceptions", visibility = ["//visibility:public"], diff --git a/third_party/nccl/archive.BUILD b/third_party/nccl/archive.BUILD index c0833828a7..7a08f97ef3 100644 --- a/third_party/nccl/archive.BUILD +++ b/third_party/nccl/archive.BUILD @@ -64,13 +64,13 @@ nccl_library( ":device_srcs", ], copts = ["-DNCCL_OP=0"] + rdc_copts(), + linkstatic = True, prefix = "sum_", deps = [ - ":src_hdrs", ":include_hdrs", + ":src_hdrs", "@local_config_cuda//cuda:cuda_headers", ], - linkstatic = True, ) nccl_library( @@ -80,13 +80,13 @@ nccl_library( ":device_srcs", ], copts = ["-DNCCL_OP=1"] + rdc_copts(), + linkstatic = True, prefix = "_prod", deps = [ - ":src_hdrs", ":include_hdrs", + ":src_hdrs", "@local_config_cuda//cuda:cuda_headers", ], - linkstatic = True, ) nccl_library( @@ -96,13 +96,13 @@ nccl_library( ":device_srcs", ], copts = ["-DNCCL_OP=2"] + rdc_copts(), + linkstatic = True, prefix = "min_", deps = [ - ":src_hdrs", ":include_hdrs", + ":src_hdrs", "@local_config_cuda//cuda:cuda_headers", ], - linkstatic = True, ) nccl_library( @@ -112,28 +112,28 @@ nccl_library( ":device_srcs", ], copts = ["-DNCCL_OP=3"] + rdc_copts(), + linkstatic = True, prefix = "max_", deps = [ - ":src_hdrs", ":include_hdrs", + ":src_hdrs", "@local_config_cuda//cuda:cuda_headers", ], - linkstatic = True, ) nccl_library( name = "functions", srcs = [ - ":device_hdrs", "src/collectives/device/functions.cu", + ":device_hdrs", ], copts = rdc_copts(), + linkstatic = True, deps = [ - ":src_hdrs", ":include_hdrs", + ":src_hdrs", "@local_config_cuda//cuda:cuda_headers", ], - linkstatic = True, ) rdc_library( @@ -162,13 +162,13 @@ nccl_library( "src/nccl.h", ], hdrs = ["src/nccl.h"], + copts = cuda_default_copts(), include_prefix = "third_party/nccl", strip_include_prefix = "src", - copts = cuda_default_copts(), + visibility = ["//visibility:public"], deps = [ ":device_code", ":include_hdrs", ":src_hdrs", ], - visibility = ["//visibility:public"], ) diff --git a/third_party/ngraph/ngraph.BUILD b/third_party/ngraph/ngraph.BUILD index f556c5279d..63e9548c53 100644 --- a/third_party/ngraph/ngraph.BUILD +++ b/third_party/ngraph/ngraph.BUILD @@ -97,13 +97,6 @@ cc_library( "src/ngraph/runtime/cpu/pass/cpu_workspace_insertion.cpp", ], hdrs = glob(["src/ngraph/runtime/cpu/**/*.hpp"]) + glob([]), - deps = [ - ":ngraph_headers", - "@eigen_archive//:eigen", - "@nlohmann_json_lib", - "@tbb", - "@mkl_dnn//:mkl_dnn", - ], copts = [ "-I external/ngraph/src", "-I external/nlohmann_json_lib/include/", @@ -113,6 +106,13 @@ cc_library( '-D PROJECT_ROOT_DIR=\\"\\"', ], visibility = ["//visibility:public"], + deps = [ + ":ngraph_headers", + "@eigen_archive//:eigen", + "@mkl_dnn", + "@nlohmann_json_lib", + "@tbb", + ], alwayslink = 1, ) @@ -138,12 +138,6 @@ cc_library( "src/ngraph/runtime/*.cpp", "src/ngraph/type/*.cpp", ]), - deps = [ - ":ngraph_headers", - ":ngraph_cpu_backend", - "@eigen_archive//:eigen", - "@nlohmann_json_lib", - ], copts = [ "-I external/ngraph/src", "-I external/nlohmann_json_lib/include/", @@ -152,5 +146,11 @@ cc_library( '-D PROJECT_ROOT_DIR=\\"\\"', ], visibility = ["//visibility:public"], + deps = [ + ":ngraph_cpu_backend", + ":ngraph_headers", + "@eigen_archive//:eigen", + "@nlohmann_json_lib", + ], alwayslink = 1, ) diff --git a/third_party/ngraph/ngraph_tf.BUILD b/third_party/ngraph/ngraph_tf.BUILD index 068e411e81..db9a66f9b5 100644 --- a/third_party/ngraph/ngraph_tf.BUILD +++ b/third_party/ngraph/ngraph_tf.BUILD @@ -10,6 +10,10 @@ load( cc_library( name = "ngraph_tf", srcs = [ + "logging/ngraph_log.cc", + "logging/ngraph_log.h", + "logging/tf_graph_writer.cc", + "logging/tf_graph_writer.h", "src/ngraph_api.cc", "src/ngraph_api.h", "src/ngraph_assign_clusters.cc", @@ -41,27 +45,23 @@ cc_library( "src/tf_deadness_analysis.h", "src/tf_graphcycles.cc", "src/tf_graphcycles.h", - "logging/ngraph_log.h", - "logging/ngraph_log.cc", - "logging/tf_graph_writer.h", - "logging/tf_graph_writer.cc", - ], - deps = [ - "@org_tensorflow//tensorflow/core:protos_all_proto_text", - "@org_tensorflow//tensorflow/core:framework_headers_lib", - "@org_tensorflow//tensorflow/core:core_cpu_headers_lib", - "@ngraph//:ngraph_core", - "@com_google_absl//absl/container:container_memory", - "@com_google_absl//absl/container:flat_hash_set", - "@com_google_absl//absl/types:variant", ], copts = [ "-I external/ngraph_tf/src", "-I external/ngraph_tf/logging", "-I external/ngraph/src", ], - alwayslink = 1, visibility = ["//visibility:public"], + deps = [ + "@com_google_absl//absl/container:container_memory", + "@com_google_absl//absl/container:flat_hash_set", + "@com_google_absl//absl/types:variant", + "@ngraph//:ngraph_core", + "@org_tensorflow//tensorflow/core:core_cpu_headers_lib", + "@org_tensorflow//tensorflow/core:framework_headers_lib", + "@org_tensorflow//tensorflow/core:protos_all_proto_text", + ], + alwayslink = 1, ) tf_cc_test( @@ -82,6 +82,12 @@ tf_cc_test( "test/test_utilities.h", "test/tf_exec.cpp", ], + extra_copts = [ + "-fexceptions ", + "-I external/ngraph_tf/src", + "-I external/ngraph_tf/logging", + "-I external/ngraph/src", + ], deps = [ ":ngraph_tf", "@com_google_googletest//:gtest", @@ -89,10 +95,4 @@ tf_cc_test( "@org_tensorflow//tensorflow/cc:client_session", "@org_tensorflow//tensorflow/core:tensorflow", ], - extra_copts = [ - "-fexceptions ", - "-I external/ngraph_tf/src", - "-I external/ngraph_tf/logging", - "-I external/ngraph/src", - ], ) diff --git a/third_party/ngraph/tbb.BUILD b/third_party/ngraph/tbb.BUILD index 04e6544ffb..c78a2d79dd 100644 --- a/third_party/ngraph/tbb.BUILD +++ b/third_party/ngraph/tbb.BUILD @@ -14,6 +14,10 @@ genrule( srcs = glob(["**"]) + [ "@local_config_cc//:toolchain", ], + outs = [ + "libtbb.a", + "libtbbmalloc.a", + ], cmd = """ set -e WORK_DIR=$$PWD @@ -45,19 +49,15 @@ genrule( cp build/build_{release,debug}/*.a $$DEST_DIR cd $$WORK_DIR """, - outs = [ - "libtbb.a", - "libtbbmalloc.a", - ], ) cc_library( name = "tbb", + srcs = ["libtbb.a"], hdrs = glob([ "include/serial/**", "include/tbb/**/**", ]), - srcs = ["libtbb.a"], includes = ["include"], visibility = ["//visibility:public"], ) diff --git a/third_party/png.BUILD b/third_party/png.BUILD index c26a289717..e82948648e 100644 --- a/third_party/png.BUILD +++ b/third_party/png.BUILD @@ -44,11 +44,11 @@ cc_library( "png.h", "pngconf.h", ], - includes = ["."], copts = select({ ":windows": ["-DPNG_INTEL_SSE_OPT=1"], "//conditions:default": [], }), + includes = ["."], linkopts = select({ ":windows": [], "//conditions:default": ["-lm"], diff --git a/third_party/repo.bzl b/third_party/repo.bzl index 07b853ff11..bad6d20a08 100644 --- a/third_party/repo.bzl +++ b/third_party/repo.bzl @@ -84,7 +84,7 @@ def _apply_delete(ctx, paths): def _tf_http_archive(ctx): if ("mirror.bazel.build" not in ctx.attr.urls[0] and (len(ctx.attr.urls) < 2 and - ctx.attr.name not in _SINGLE_URL_WHITELIST)): + ctx.attr.name not in _SINGLE_URL_WHITELIST.to_list())): fail("tf_http_archive(urls) must have redundant URLs. The " + "mirror.bazel.build URL must be present and it must come first. " + "Even if you don't have permission to mirror the file, please " + @@ -150,7 +150,7 @@ ensure best practices are followed. def _third_party_http_archive(ctx): if ("mirror.bazel.build" not in ctx.attr.urls[0] and (len(ctx.attr.urls) < 2 and - ctx.attr.name not in _SINGLE_URL_WHITELIST)): + ctx.attr.name not in _SINGLE_URL_WHITELIST.to_list())): fail("tf_http_archive(urls) must have redundant URLs. The " + "mirror.bazel.build URL must be present and it must come first. " + "Even if you don't have permission to mirror the file, please " + diff --git a/third_party/toolchains/BUILD b/third_party/toolchains/BUILD index a7b4687c02..9da417fd5f 100644 --- a/third_party/toolchains/BUILD +++ b/third_party/toolchains/BUILD @@ -35,3 +35,16 @@ platform( value:"docker://gcr.io/asci-toolchain/nosla-cuda9.0-cudnn7-ubuntu14.04@%s" }""" % container_digests["cuda9.0-cudnn7-ubuntu14.04"], ) + +platform( + name = "rbe_cuda10.0-cudnn7-ubuntu14.04", + constraint_values = [ + "@bazel_tools//platforms:x86_64", + "@bazel_tools//platforms:linux", + ], + remote_execution_properties = """ + properties: { + name: "container-image" + value:"docker://gcr.io/asci-toolchain/nosla-cuda10.0-cudnn7-ubuntu14.04@%s" + }""" % container_digests["cuda10.0-cudnn7-ubuntu14.04"], +) diff --git a/third_party/toolchains/preconfig/generate/containers.bzl b/third_party/toolchains/preconfig/generate/containers.bzl index 1f9e29d440..7099b9bf3e 100644 --- a/third_party/toolchains/preconfig/generate/containers.bzl +++ b/third_party/toolchains/preconfig/generate/containers.bzl @@ -1,4 +1,4 @@ container_digests = { "cuda9.0-cudnn7-ubuntu14.04": "sha256:c26138f4c38c754da2bad44a8a068523abf7fbd71d58a57ce92e5342c5431bf5", - "cuda10.0-cudnn7-ubuntu14.04": "sha256:34c4a55e2376b300cdc2b903775fc32e62352f6e33f927df5653743324378bfc", + "cuda10.0-cudnn7-ubuntu14.04": "sha256:66e7d592c8149291d5562a0f3093655a15b09c22e0eb30a87b3b6469b7a30ffc", } diff --git a/third_party/toolchains/preconfig/ubuntu14.04/cuda10.0-cudnn7/WORKSPACE b/third_party/toolchains/preconfig/ubuntu14.04/cuda10.0-cudnn7/WORKSPACE new file mode 100644 index 0000000000..b61f572d6d --- /dev/null +++ b/third_party/toolchains/preconfig/ubuntu14.04/cuda10.0-cudnn7/WORKSPACE @@ -0,0 +1,2 @@ +# DO NOT EDIT: automatically generated WORKSPACE file for cuda_configure rule +workspace(name = "local_config_cuda") diff --git a/third_party/toolchains/preconfig/ubuntu14.04/cuda10.0-cudnn7/cuda/BUILD b/third_party/toolchains/preconfig/ubuntu14.04/cuda10.0-cudnn7/cuda/BUILD new file mode 100755 index 0000000000..c813efccf9 --- /dev/null +++ b/third_party/toolchains/preconfig/ubuntu14.04/cuda10.0-cudnn7/cuda/BUILD @@ -0,0 +1,1275 @@ +licenses(["restricted"]) # MPL2, portions GPL v3, LGPL v3, BSD-like + +package(default_visibility = ["//visibility:public"]) + +config_setting( + name = "using_nvcc", + values = { + "define": "using_cuda_nvcc=true", + }, +) + +config_setting( + name = "using_clang", + values = { + "define": "using_cuda_clang=true", + }, +) + +# Equivalent to using_clang && -c opt. +config_setting( + name = "using_clang_opt", + values = { + "define": "using_cuda_clang=true", + "compilation_mode": "opt", + }, +) + +config_setting( + name = "darwin", + values = {"cpu": "darwin"}, + visibility = ["//visibility:public"], +) + +config_setting( + name = "freebsd", + values = {"cpu": "freebsd"}, + visibility = ["//visibility:public"], +) + +cc_library( + name = "cuda_headers", + hdrs = [ + "cuda/cuda_config.h", + ":cuda-include", + ":cudnn-include", + ], + includes = [ + ".", + "cuda/include", + "cuda/include/crt", + ], + visibility = ["//visibility:public"], +) + +cc_library( + name = "cudart_static", + srcs = ["cuda/lib/libcudart_static.a"], + includes = [ + ".", + "cuda/include", + ], + linkopts = select({ + ":freebsd": [], + "//conditions:default": ["-ldl"], + }) + [ + "-lpthread", + "-lrt", + ], + visibility = ["//visibility:public"], +) + +cc_library( + name = "cuda_driver", + srcs = ["cuda/lib/libcuda.so"], + includes = [ + ".", + "cuda/include", + ], + visibility = ["//visibility:public"], +) + +cc_library( + name = "cudart", + srcs = ["cuda/lib/libcudart.so.10.0"], + data = ["cuda/lib/libcudart.so.10.0"], + includes = [ + ".", + "cuda/include", + ], + linkstatic = 1, + visibility = ["//visibility:public"], +) + +cc_library( + name = "cublas", + srcs = ["cuda/lib/libcublas.so.10.0"], + data = ["cuda/lib/libcublas.so.10.0"], + includes = [ + ".", + "cuda/include", + ], + linkstatic = 1, + visibility = ["//visibility:public"], +) + +cc_library( + name = "cusolver", + srcs = ["cuda/lib/libcusolver.so.10.0"], + data = ["cuda/lib/libcusolver.so.10.0"], + includes = [ + ".", + "cuda/include", + ], + linkopts = ["-lgomp"], + linkstatic = 1, + visibility = ["//visibility:public"], +) + +cc_library( + name = "cudnn", + srcs = ["cuda/lib/libcudnn.so.7"], + data = ["cuda/lib/libcudnn.so.7"], + includes = [ + ".", + "cuda/include", + ], + linkstatic = 1, + visibility = ["//visibility:public"], +) + +cc_library( + name = "cudnn_header", + includes = [ + ".", + "cuda/include", + ], + visibility = ["//visibility:public"], +) + +cc_library( + name = "cufft", + srcs = ["cuda/lib/libcufft.so.10.0"], + data = ["cuda/lib/libcufft.so.10.0"], + includes = [ + ".", + "cuda/include", + ], + linkstatic = 1, + visibility = ["//visibility:public"], +) + +cc_library( + name = "curand", + srcs = ["cuda/lib/libcurand.so.10.0"], + data = ["cuda/lib/libcurand.so.10.0"], + includes = [ + ".", + "cuda/include", + ], + linkstatic = 1, + visibility = ["//visibility:public"], +) + +cc_library( + name = "cuda", + visibility = ["//visibility:public"], + deps = [ + ":cublas", + ":cuda_headers", + ":cudart", + ":cudnn", + ":cufft", + ":curand", + ], +) + +cc_library( + name = "cupti_headers", + hdrs = [ + "cuda/cuda_config.h", + ":cuda-extras", + ], + includes = [ + ".", + "cuda/extras/CUPTI/include/", + ], + visibility = ["//visibility:public"], +) + +cc_library( + name = "cupti_dsos", + data = ["cuda/lib/libcupti.so.10.0"], + includes = [ + ".", + "cuda/include", + ], + visibility = ["//visibility:public"], +) + +cc_library( + name = "libdevice_root", + data = [":cuda-nvvm"], + visibility = ["//visibility:public"], +) + +genrule( + name = "cuda-include", + outs = [ + "cuda/include/CL/cl.h", + "cuda/include/CL/cl.hpp", + "cuda/include/CL/cl_egl.h", + "cuda/include/CL/cl_ext.h", + "cuda/include/CL/cl_gl.h", + "cuda/include/CL/cl_gl_ext.h", + "cuda/include/CL/cl_platform.h", + "cuda/include/CL/opencl.h", + "cuda/include/builtin_types.h", + "cuda/include/channel_descriptor.h", + "cuda/include/common_functions.h", + "cuda/include/cooperative_groups.h", + "cuda/include/cooperative_groups_helpers.h", + "cuda/include/crt/common_functions.h", + "cuda/include/crt/device_double_functions.h", + "cuda/include/crt/device_double_functions.hpp", + "cuda/include/crt/device_functions.h", + "cuda/include/crt/device_functions.hpp", + "cuda/include/crt/func_macro.h", + "cuda/include/crt/host_config.h", + "cuda/include/crt/host_defines.h", + "cuda/include/crt/host_runtime.h", + "cuda/include/crt/math_functions.h", + "cuda/include/crt/math_functions.hpp", + "cuda/include/crt/mma.h", + "cuda/include/crt/mma.hpp", + "cuda/include/crt/nvfunctional", + "cuda/include/crt/sm_70_rt.h", + "cuda/include/crt/sm_70_rt.hpp", + "cuda/include/crt/storage_class.h", + "cuda/include/cuComplex.h", + "cuda/include/cublas.h", + "cuda/include/cublasXt.h", + "cuda/include/cublas_api.h", + "cuda/include/cublas_v2.h", + "cuda/include/cuda.h", + "cuda/include/cudaEGL.h", + "cuda/include/cudaGL.h", + "cuda/include/cudaProfiler.h", + "cuda/include/cudaVDPAU.h", + "cuda/include/cuda_device_runtime_api.h", + "cuda/include/cuda_egl_interop.h", + "cuda/include/cuda_fp16.h", + "cuda/include/cuda_fp16.hpp", + "cuda/include/cuda_gl_interop.h", + "cuda/include/cuda_occupancy.h", + "cuda/include/cuda_profiler_api.h", + "cuda/include/cuda_runtime.h", + "cuda/include/cuda_runtime_api.h", + "cuda/include/cuda_surface_types.h", + "cuda/include/cuda_texture_types.h", + "cuda/include/cuda_vdpau_interop.h", + "cuda/include/cudalibxt.h", + "cuda/include/cudart_platform.h", + "cuda/include/cufft.h", + "cuda/include/cufftXt.h", + "cuda/include/cufftw.h", + "cuda/include/curand.h", + "cuda/include/curand_discrete.h", + "cuda/include/curand_discrete2.h", + "cuda/include/curand_globals.h", + "cuda/include/curand_kernel.h", + "cuda/include/curand_lognormal.h", + "cuda/include/curand_mrg32k3a.h", + "cuda/include/curand_mtgp32.h", + "cuda/include/curand_mtgp32_host.h", + "cuda/include/curand_mtgp32_kernel.h", + "cuda/include/curand_mtgp32dc_p_11213.h", + "cuda/include/curand_normal.h", + "cuda/include/curand_normal_static.h", + "cuda/include/curand_philox4x32_x.h", + "cuda/include/curand_poisson.h", + "cuda/include/curand_precalc.h", + "cuda/include/curand_uniform.h", + "cuda/include/cusolverDn.h", + "cuda/include/cusolverRf.h", + "cuda/include/cusolverSp.h", + "cuda/include/cusolverSp_LOWLEVEL_PREVIEW.h", + "cuda/include/cusolver_common.h", + "cuda/include/cusparse.h", + "cuda/include/cusparse_v2.h", + "cuda/include/device_atomic_functions.h", + "cuda/include/device_atomic_functions.hpp", + "cuda/include/device_double_functions.h", + "cuda/include/device_functions.h", + "cuda/include/device_launch_parameters.h", + "cuda/include/device_types.h", + "cuda/include/driver_functions.h", + "cuda/include/driver_types.h", + "cuda/include/fatBinaryCtl.h", + "cuda/include/fatbinary.h", + "cuda/include/host_config.h", + "cuda/include/host_defines.h", + "cuda/include/library_types.h", + "cuda/include/math_constants.h", + "cuda/include/math_functions.h", + "cuda/include/mma.h", + "cuda/include/npp.h", + "cuda/include/nppcore.h", + "cuda/include/nppdefs.h", + "cuda/include/nppi.h", + "cuda/include/nppi_arithmetic_and_logical_operations.h", + "cuda/include/nppi_color_conversion.h", + "cuda/include/nppi_compression_functions.h", + "cuda/include/nppi_computer_vision.h", + "cuda/include/nppi_data_exchange_and_initialization.h", + "cuda/include/nppi_filtering_functions.h", + "cuda/include/nppi_geometry_transforms.h", + "cuda/include/nppi_linear_transforms.h", + "cuda/include/nppi_morphological_operations.h", + "cuda/include/nppi_statistics_functions.h", + "cuda/include/nppi_support_functions.h", + "cuda/include/nppi_threshold_and_compare_operations.h", + "cuda/include/npps.h", + "cuda/include/npps_arithmetic_and_logical_operations.h", + "cuda/include/npps_conversion_functions.h", + "cuda/include/npps_filtering_functions.h", + "cuda/include/npps_initialization.h", + "cuda/include/npps_statistics_functions.h", + "cuda/include/npps_support_functions.h", + "cuda/include/nppversion.h", + "cuda/include/nvToolsExt.h", + "cuda/include/nvToolsExtCuda.h", + "cuda/include/nvToolsExtCudaRt.h", + "cuda/include/nvToolsExtMeta.h", + "cuda/include/nvToolsExtSync.h", + "cuda/include/nvblas.h", + "cuda/include/nvfunctional", + "cuda/include/nvgraph.h", + "cuda/include/nvjpeg.h", + "cuda/include/nvml.h", + "cuda/include/nvrtc.h", + "cuda/include/nvtx3/nvToolsExt.h", + "cuda/include/nvtx3/nvToolsExtCuda.h", + "cuda/include/nvtx3/nvToolsExtCudaRt.h", + "cuda/include/nvtx3/nvToolsExtOpenCL.h", + "cuda/include/nvtx3/nvToolsExtSync.h", + "cuda/include/nvtx3/nvtxDetail/nvtxImpl.h", + "cuda/include/nvtx3/nvtxDetail/nvtxImplCore.h", + "cuda/include/nvtx3/nvtxDetail/nvtxImplCudaRt_v3.h", + "cuda/include/nvtx3/nvtxDetail/nvtxImplCuda_v3.h", + "cuda/include/nvtx3/nvtxDetail/nvtxImplOpenCL_v3.h", + "cuda/include/nvtx3/nvtxDetail/nvtxImplSync_v3.h", + "cuda/include/nvtx3/nvtxDetail/nvtxInit.h", + "cuda/include/nvtx3/nvtxDetail/nvtxInitDecls.h", + "cuda/include/nvtx3/nvtxDetail/nvtxInitDefs.h", + "cuda/include/nvtx3/nvtxDetail/nvtxLinkOnce.h", + "cuda/include/nvtx3/nvtxDetail/nvtxTypes.h", + "cuda/include/sm_20_atomic_functions.h", + "cuda/include/sm_20_atomic_functions.hpp", + "cuda/include/sm_20_intrinsics.h", + "cuda/include/sm_20_intrinsics.hpp", + "cuda/include/sm_30_intrinsics.h", + "cuda/include/sm_30_intrinsics.hpp", + "cuda/include/sm_32_atomic_functions.h", + "cuda/include/sm_32_atomic_functions.hpp", + "cuda/include/sm_32_intrinsics.h", + "cuda/include/sm_32_intrinsics.hpp", + "cuda/include/sm_35_atomic_functions.h", + "cuda/include/sm_35_intrinsics.h", + "cuda/include/sm_60_atomic_functions.h", + "cuda/include/sm_60_atomic_functions.hpp", + "cuda/include/sm_61_intrinsics.h", + "cuda/include/sm_61_intrinsics.hpp", + "cuda/include/sobol_direction_vectors.h", + "cuda/include/surface_functions.h", + "cuda/include/surface_functions.hpp", + "cuda/include/surface_indirect_functions.h", + "cuda/include/surface_indirect_functions.hpp", + "cuda/include/surface_types.h", + "cuda/include/texture_fetch_functions.h", + "cuda/include/texture_fetch_functions.hpp", + "cuda/include/texture_indirect_functions.h", + "cuda/include/texture_indirect_functions.hpp", + "cuda/include/texture_types.h", + "cuda/include/thrust/adjacent_difference.h", + "cuda/include/thrust/advance.h", + "cuda/include/thrust/binary_search.h", + "cuda/include/thrust/complex.h", + "cuda/include/thrust/copy.h", + "cuda/include/thrust/count.h", + "cuda/include/thrust/detail/adjacent_difference.inl", + "cuda/include/thrust/detail/advance.inl", + "cuda/include/thrust/detail/alignment.h", + "cuda/include/thrust/detail/allocator/allocator_traits.h", + "cuda/include/thrust/detail/allocator/allocator_traits.inl", + "cuda/include/thrust/detail/allocator/copy_construct_range.h", + "cuda/include/thrust/detail/allocator/copy_construct_range.inl", + "cuda/include/thrust/detail/allocator/default_construct_range.h", + "cuda/include/thrust/detail/allocator/default_construct_range.inl", + "cuda/include/thrust/detail/allocator/destroy_range.h", + "cuda/include/thrust/detail/allocator/destroy_range.inl", + "cuda/include/thrust/detail/allocator/fill_construct_range.h", + "cuda/include/thrust/detail/allocator/fill_construct_range.inl", + "cuda/include/thrust/detail/allocator/malloc_allocator.h", + "cuda/include/thrust/detail/allocator/malloc_allocator.inl", + "cuda/include/thrust/detail/allocator/no_throw_allocator.h", + "cuda/include/thrust/detail/allocator/tagged_allocator.h", + "cuda/include/thrust/detail/allocator/tagged_allocator.inl", + "cuda/include/thrust/detail/allocator/temporary_allocator.h", + "cuda/include/thrust/detail/allocator/temporary_allocator.inl", + "cuda/include/thrust/detail/binary_search.inl", + "cuda/include/thrust/detail/complex/arithmetic.h", + "cuda/include/thrust/detail/complex/c99math.h", + "cuda/include/thrust/detail/complex/catrig.h", + "cuda/include/thrust/detail/complex/catrigf.h", + "cuda/include/thrust/detail/complex/ccosh.h", + "cuda/include/thrust/detail/complex/ccoshf.h", + "cuda/include/thrust/detail/complex/cexp.h", + "cuda/include/thrust/detail/complex/cexpf.h", + "cuda/include/thrust/detail/complex/clog.h", + "cuda/include/thrust/detail/complex/clogf.h", + "cuda/include/thrust/detail/complex/complex.inl", + "cuda/include/thrust/detail/complex/cpow.h", + "cuda/include/thrust/detail/complex/cproj.h", + "cuda/include/thrust/detail/complex/csinh.h", + "cuda/include/thrust/detail/complex/csinhf.h", + "cuda/include/thrust/detail/complex/csqrt.h", + "cuda/include/thrust/detail/complex/csqrtf.h", + "cuda/include/thrust/detail/complex/ctanh.h", + "cuda/include/thrust/detail/complex/ctanhf.h", + "cuda/include/thrust/detail/complex/math_private.h", + "cuda/include/thrust/detail/complex/stream.h", + "cuda/include/thrust/detail/config.h", + "cuda/include/thrust/detail/config/compiler.h", + "cuda/include/thrust/detail/config/compiler_fence.h", + "cuda/include/thrust/detail/config/config.h", + "cuda/include/thrust/detail/config/debug.h", + "cuda/include/thrust/detail/config/device_system.h", + "cuda/include/thrust/detail/config/exec_check_disable.h", + "cuda/include/thrust/detail/config/forceinline.h", + "cuda/include/thrust/detail/config/global_workarounds.h", + "cuda/include/thrust/detail/config/host_device.h", + "cuda/include/thrust/detail/config/host_system.h", + "cuda/include/thrust/detail/config/simple_defines.h", + "cuda/include/thrust/detail/contiguous_storage.h", + "cuda/include/thrust/detail/contiguous_storage.inl", + "cuda/include/thrust/detail/copy.h", + "cuda/include/thrust/detail/copy.inl", + "cuda/include/thrust/detail/copy_if.h", + "cuda/include/thrust/detail/copy_if.inl", + "cuda/include/thrust/detail/count.inl", + "cuda/include/thrust/detail/cstdint.h", + "cuda/include/thrust/detail/device_delete.inl", + "cuda/include/thrust/detail/device_free.inl", + "cuda/include/thrust/detail/device_malloc.inl", + "cuda/include/thrust/detail/device_new.inl", + "cuda/include/thrust/detail/device_ptr.inl", + "cuda/include/thrust/detail/device_reference.inl", + "cuda/include/thrust/detail/device_vector.inl", + "cuda/include/thrust/detail/dispatch/is_trivial_copy.h", + "cuda/include/thrust/detail/distance.inl", + "cuda/include/thrust/detail/equal.inl", + "cuda/include/thrust/detail/execute_with_allocator.h", + "cuda/include/thrust/detail/execution_policy.h", + "cuda/include/thrust/detail/extrema.inl", + "cuda/include/thrust/detail/fill.inl", + "cuda/include/thrust/detail/find.inl", + "cuda/include/thrust/detail/for_each.inl", + "cuda/include/thrust/detail/function.h", + "cuda/include/thrust/detail/functional.inl", + "cuda/include/thrust/detail/functional/actor.h", + "cuda/include/thrust/detail/functional/actor.inl", + "cuda/include/thrust/detail/functional/argument.h", + "cuda/include/thrust/detail/functional/composite.h", + "cuda/include/thrust/detail/functional/operators.h", + "cuda/include/thrust/detail/functional/operators/arithmetic_operators.h", + "cuda/include/thrust/detail/functional/operators/assignment_operator.h", + "cuda/include/thrust/detail/functional/operators/bitwise_operators.h", + "cuda/include/thrust/detail/functional/operators/compound_assignment_operators.h", + "cuda/include/thrust/detail/functional/operators/logical_operators.h", + "cuda/include/thrust/detail/functional/operators/operator_adaptors.h", + "cuda/include/thrust/detail/functional/operators/relational_operators.h", + "cuda/include/thrust/detail/functional/placeholder.h", + "cuda/include/thrust/detail/functional/value.h", + "cuda/include/thrust/detail/gather.inl", + "cuda/include/thrust/detail/generate.inl", + "cuda/include/thrust/detail/get_iterator_value.h", + "cuda/include/thrust/detail/host_vector.inl", + "cuda/include/thrust/detail/inner_product.inl", + "cuda/include/thrust/detail/integer_math.h", + "cuda/include/thrust/detail/integer_traits.h", + "cuda/include/thrust/detail/internal_functional.h", + "cuda/include/thrust/detail/logical.inl", + "cuda/include/thrust/detail/malloc_and_free.h", + "cuda/include/thrust/detail/merge.inl", + "cuda/include/thrust/detail/minmax.h", + "cuda/include/thrust/detail/mismatch.inl", + "cuda/include/thrust/detail/mpl/math.h", + "cuda/include/thrust/detail/numeric_traits.h", + "cuda/include/thrust/detail/overlapped_copy.h", + "cuda/include/thrust/detail/pair.inl", + "cuda/include/thrust/detail/partition.inl", + "cuda/include/thrust/detail/pointer.h", + "cuda/include/thrust/detail/pointer.inl", + "cuda/include/thrust/detail/preprocessor.h", + "cuda/include/thrust/detail/range/head_flags.h", + "cuda/include/thrust/detail/range/tail_flags.h", + "cuda/include/thrust/detail/raw_pointer_cast.h", + "cuda/include/thrust/detail/raw_reference_cast.h", + "cuda/include/thrust/detail/reduce.inl", + "cuda/include/thrust/detail/reference.h", + "cuda/include/thrust/detail/reference.inl", + "cuda/include/thrust/detail/reference_forward_declaration.h", + "cuda/include/thrust/detail/remove.inl", + "cuda/include/thrust/detail/replace.inl", + "cuda/include/thrust/detail/reverse.inl", + "cuda/include/thrust/detail/scan.inl", + "cuda/include/thrust/detail/scatter.inl", + "cuda/include/thrust/detail/seq.h", + "cuda/include/thrust/detail/sequence.inl", + "cuda/include/thrust/detail/set_operations.inl", + "cuda/include/thrust/detail/sort.inl", + "cuda/include/thrust/detail/static_assert.h", + "cuda/include/thrust/detail/static_map.h", + "cuda/include/thrust/detail/swap.h", + "cuda/include/thrust/detail/swap.inl", + "cuda/include/thrust/detail/swap_ranges.inl", + "cuda/include/thrust/detail/tabulate.inl", + "cuda/include/thrust/detail/temporary_array.h", + "cuda/include/thrust/detail/temporary_array.inl", + "cuda/include/thrust/detail/temporary_buffer.h", + "cuda/include/thrust/detail/transform.inl", + "cuda/include/thrust/detail/transform_reduce.inl", + "cuda/include/thrust/detail/transform_scan.inl", + "cuda/include/thrust/detail/trivial_sequence.h", + "cuda/include/thrust/detail/tuple.inl", + "cuda/include/thrust/detail/tuple_meta_transform.h", + "cuda/include/thrust/detail/tuple_transform.h", + "cuda/include/thrust/detail/type_traits.h", + "cuda/include/thrust/detail/type_traits/algorithm/intermediate_type_from_function_and_iterators.h", + "cuda/include/thrust/detail/type_traits/function_traits.h", + "cuda/include/thrust/detail/type_traits/has_member_function.h", + "cuda/include/thrust/detail/type_traits/has_nested_type.h", + "cuda/include/thrust/detail/type_traits/has_trivial_assign.h", + "cuda/include/thrust/detail/type_traits/is_call_possible.h", + "cuda/include/thrust/detail/type_traits/is_metafunction_defined.h", + "cuda/include/thrust/detail/type_traits/iterator/is_discard_iterator.h", + "cuda/include/thrust/detail/type_traits/iterator/is_output_iterator.h", + "cuda/include/thrust/detail/type_traits/minimum_type.h", + "cuda/include/thrust/detail/type_traits/pointer_traits.h", + "cuda/include/thrust/detail/type_traits/result_of_adaptable_function.h", + "cuda/include/thrust/detail/uninitialized_copy.inl", + "cuda/include/thrust/detail/uninitialized_fill.inl", + "cuda/include/thrust/detail/unique.inl", + "cuda/include/thrust/detail/use_default.h", + "cuda/include/thrust/detail/util/align.h", + "cuda/include/thrust/detail/util/blocking.h", + "cuda/include/thrust/detail/vector_base.h", + "cuda/include/thrust/detail/vector_base.inl", + "cuda/include/thrust/device_allocator.h", + "cuda/include/thrust/device_delete.h", + "cuda/include/thrust/device_free.h", + "cuda/include/thrust/device_malloc.h", + "cuda/include/thrust/device_malloc_allocator.h", + "cuda/include/thrust/device_new.h", + "cuda/include/thrust/device_new_allocator.h", + "cuda/include/thrust/device_ptr.h", + "cuda/include/thrust/device_reference.h", + "cuda/include/thrust/device_vector.h", + "cuda/include/thrust/distance.h", + "cuda/include/thrust/equal.h", + "cuda/include/thrust/execution_policy.h", + "cuda/include/thrust/extrema.h", + "cuda/include/thrust/fill.h", + "cuda/include/thrust/find.h", + "cuda/include/thrust/for_each.h", + "cuda/include/thrust/functional.h", + "cuda/include/thrust/gather.h", + "cuda/include/thrust/generate.h", + "cuda/include/thrust/host_vector.h", + "cuda/include/thrust/inner_product.h", + "cuda/include/thrust/iterator/constant_iterator.h", + "cuda/include/thrust/iterator/counting_iterator.h", + "cuda/include/thrust/iterator/detail/any_assign.h", + "cuda/include/thrust/iterator/detail/any_system_tag.h", + "cuda/include/thrust/iterator/detail/constant_iterator_base.h", + "cuda/include/thrust/iterator/detail/counting_iterator.inl", + "cuda/include/thrust/iterator/detail/device_system_tag.h", + "cuda/include/thrust/iterator/detail/discard_iterator_base.h", + "cuda/include/thrust/iterator/detail/distance_from_result.h", + "cuda/include/thrust/iterator/detail/host_system_tag.h", + "cuda/include/thrust/iterator/detail/is_iterator_category.h", + "cuda/include/thrust/iterator/detail/is_trivial_iterator.h", + "cuda/include/thrust/iterator/detail/iterator_adaptor_base.h", + "cuda/include/thrust/iterator/detail/iterator_category_to_system.h", + "cuda/include/thrust/iterator/detail/iterator_category_to_traversal.h", + "cuda/include/thrust/iterator/detail/iterator_category_with_system_and_traversal.h", + "cuda/include/thrust/iterator/detail/iterator_facade_category.h", + "cuda/include/thrust/iterator/detail/iterator_traits.inl", + "cuda/include/thrust/iterator/detail/iterator_traversal_tags.h", + "cuda/include/thrust/iterator/detail/join_iterator.h", + "cuda/include/thrust/iterator/detail/minimum_category.h", + "cuda/include/thrust/iterator/detail/minimum_system.h", + "cuda/include/thrust/iterator/detail/normal_iterator.h", + "cuda/include/thrust/iterator/detail/permutation_iterator_base.h", + "cuda/include/thrust/iterator/detail/retag.h", + "cuda/include/thrust/iterator/detail/reverse_iterator.inl", + "cuda/include/thrust/iterator/detail/reverse_iterator_base.h", + "cuda/include/thrust/iterator/detail/tagged_iterator.h", + "cuda/include/thrust/iterator/detail/transform_iterator.inl", + "cuda/include/thrust/iterator/detail/transform_output_iterator.inl", + "cuda/include/thrust/iterator/detail/tuple_of_iterator_references.h", + "cuda/include/thrust/iterator/detail/universal_categories.h", + "cuda/include/thrust/iterator/detail/zip_iterator.inl", + "cuda/include/thrust/iterator/detail/zip_iterator_base.h", + "cuda/include/thrust/iterator/discard_iterator.h", + "cuda/include/thrust/iterator/iterator_adaptor.h", + "cuda/include/thrust/iterator/iterator_categories.h", + "cuda/include/thrust/iterator/iterator_facade.h", + "cuda/include/thrust/iterator/iterator_traits.h", + "cuda/include/thrust/iterator/permutation_iterator.h", + "cuda/include/thrust/iterator/retag.h", + "cuda/include/thrust/iterator/reverse_iterator.h", + "cuda/include/thrust/iterator/transform_iterator.h", + "cuda/include/thrust/iterator/transform_output_iterator.h", + "cuda/include/thrust/iterator/zip_iterator.h", + "cuda/include/thrust/logical.h", + "cuda/include/thrust/memory.h", + "cuda/include/thrust/merge.h", + "cuda/include/thrust/mismatch.h", + "cuda/include/thrust/pair.h", + "cuda/include/thrust/partition.h", + "cuda/include/thrust/random.h", + "cuda/include/thrust/random/detail/discard_block_engine.inl", + "cuda/include/thrust/random/detail/linear_congruential_engine.inl", + "cuda/include/thrust/random/detail/linear_congruential_engine_discard.h", + "cuda/include/thrust/random/detail/linear_feedback_shift_engine.inl", + "cuda/include/thrust/random/detail/linear_feedback_shift_engine_wordmask.h", + "cuda/include/thrust/random/detail/mod.h", + "cuda/include/thrust/random/detail/normal_distribution.inl", + "cuda/include/thrust/random/detail/normal_distribution_base.h", + "cuda/include/thrust/random/detail/random_core_access.h", + "cuda/include/thrust/random/detail/subtract_with_carry_engine.inl", + "cuda/include/thrust/random/detail/uniform_int_distribution.inl", + "cuda/include/thrust/random/detail/uniform_real_distribution.inl", + "cuda/include/thrust/random/detail/xor_combine_engine.inl", + "cuda/include/thrust/random/detail/xor_combine_engine_max.h", + "cuda/include/thrust/random/discard_block_engine.h", + "cuda/include/thrust/random/linear_congruential_engine.h", + "cuda/include/thrust/random/linear_feedback_shift_engine.h", + "cuda/include/thrust/random/normal_distribution.h", + "cuda/include/thrust/random/subtract_with_carry_engine.h", + "cuda/include/thrust/random/uniform_int_distribution.h", + "cuda/include/thrust/random/uniform_real_distribution.h", + "cuda/include/thrust/random/xor_combine_engine.h", + "cuda/include/thrust/reduce.h", + "cuda/include/thrust/remove.h", + "cuda/include/thrust/replace.h", + "cuda/include/thrust/reverse.h", + "cuda/include/thrust/scan.h", + "cuda/include/thrust/scatter.h", + "cuda/include/thrust/sequence.h", + "cuda/include/thrust/set_operations.h", + "cuda/include/thrust/sort.h", + "cuda/include/thrust/swap.h", + "cuda/include/thrust/system/cpp/detail/adjacent_difference.h", + "cuda/include/thrust/system/cpp/detail/assign_value.h", + "cuda/include/thrust/system/cpp/detail/binary_search.h", + "cuda/include/thrust/system/cpp/detail/copy.h", + "cuda/include/thrust/system/cpp/detail/copy_if.h", + "cuda/include/thrust/system/cpp/detail/count.h", + "cuda/include/thrust/system/cpp/detail/equal.h", + "cuda/include/thrust/system/cpp/detail/execution_policy.h", + "cuda/include/thrust/system/cpp/detail/extrema.h", + "cuda/include/thrust/system/cpp/detail/fill.h", + "cuda/include/thrust/system/cpp/detail/find.h", + "cuda/include/thrust/system/cpp/detail/for_each.h", + "cuda/include/thrust/system/cpp/detail/gather.h", + "cuda/include/thrust/system/cpp/detail/generate.h", + "cuda/include/thrust/system/cpp/detail/get_value.h", + "cuda/include/thrust/system/cpp/detail/inner_product.h", + "cuda/include/thrust/system/cpp/detail/iter_swap.h", + "cuda/include/thrust/system/cpp/detail/logical.h", + "cuda/include/thrust/system/cpp/detail/malloc_and_free.h", + "cuda/include/thrust/system/cpp/detail/memory.inl", + "cuda/include/thrust/system/cpp/detail/merge.h", + "cuda/include/thrust/system/cpp/detail/mismatch.h", + "cuda/include/thrust/system/cpp/detail/par.h", + "cuda/include/thrust/system/cpp/detail/partition.h", + "cuda/include/thrust/system/cpp/detail/reduce.h", + "cuda/include/thrust/system/cpp/detail/reduce_by_key.h", + "cuda/include/thrust/system/cpp/detail/remove.h", + "cuda/include/thrust/system/cpp/detail/replace.h", + "cuda/include/thrust/system/cpp/detail/reverse.h", + "cuda/include/thrust/system/cpp/detail/scan.h", + "cuda/include/thrust/system/cpp/detail/scan_by_key.h", + "cuda/include/thrust/system/cpp/detail/scatter.h", + "cuda/include/thrust/system/cpp/detail/sequence.h", + "cuda/include/thrust/system/cpp/detail/set_operations.h", + "cuda/include/thrust/system/cpp/detail/sort.h", + "cuda/include/thrust/system/cpp/detail/swap_ranges.h", + "cuda/include/thrust/system/cpp/detail/tabulate.h", + "cuda/include/thrust/system/cpp/detail/temporary_buffer.h", + "cuda/include/thrust/system/cpp/detail/transform.h", + "cuda/include/thrust/system/cpp/detail/transform_reduce.h", + "cuda/include/thrust/system/cpp/detail/transform_scan.h", + "cuda/include/thrust/system/cpp/detail/uninitialized_copy.h", + "cuda/include/thrust/system/cpp/detail/uninitialized_fill.h", + "cuda/include/thrust/system/cpp/detail/unique.h", + "cuda/include/thrust/system/cpp/detail/unique_by_key.h", + "cuda/include/thrust/system/cpp/detail/vector.inl", + "cuda/include/thrust/system/cpp/execution_policy.h", + "cuda/include/thrust/system/cpp/memory.h", + "cuda/include/thrust/system/cpp/vector.h", + "cuda/include/thrust/system/cuda/config.h", + "cuda/include/thrust/system/cuda/detail/adjacent_difference.h", + "cuda/include/thrust/system/cuda/detail/assign_value.h", + "cuda/include/thrust/system/cuda/detail/binary_search.h", + "cuda/include/thrust/system/cuda/detail/copy.h", + "cuda/include/thrust/system/cuda/detail/copy_if.h", + "cuda/include/thrust/system/cuda/detail/core/agent_launcher.h", + "cuda/include/thrust/system/cuda/detail/core/alignment.h", + "cuda/include/thrust/system/cuda/detail/core/triple_chevron_launch.h", + "cuda/include/thrust/system/cuda/detail/core/util.h", + "cuda/include/thrust/system/cuda/detail/count.h", + "cuda/include/thrust/system/cuda/detail/cross_system.h", + "cuda/include/thrust/system/cuda/detail/cub/agent/agent_histogram.cuh", + "cuda/include/thrust/system/cuda/detail/cub/agent/agent_radix_sort_downsweep.cuh", + "cuda/include/thrust/system/cuda/detail/cub/agent/agent_radix_sort_upsweep.cuh", + "cuda/include/thrust/system/cuda/detail/cub/agent/agent_reduce.cuh", + "cuda/include/thrust/system/cuda/detail/cub/agent/agent_reduce_by_key.cuh", + "cuda/include/thrust/system/cuda/detail/cub/agent/agent_rle.cuh", + "cuda/include/thrust/system/cuda/detail/cub/agent/agent_scan.cuh", + "cuda/include/thrust/system/cuda/detail/cub/agent/agent_segment_fixup.cuh", + "cuda/include/thrust/system/cuda/detail/cub/agent/agent_select_if.cuh", + "cuda/include/thrust/system/cuda/detail/cub/agent/agent_spmv_orig.cuh", + "cuda/include/thrust/system/cuda/detail/cub/agent/single_pass_scan_operators.cuh", + "cuda/include/thrust/system/cuda/detail/cub/block/block_adjacent_difference.cuh", + "cuda/include/thrust/system/cuda/detail/cub/block/block_discontinuity.cuh", + "cuda/include/thrust/system/cuda/detail/cub/block/block_exchange.cuh", + "cuda/include/thrust/system/cuda/detail/cub/block/block_histogram.cuh", + "cuda/include/thrust/system/cuda/detail/cub/block/block_load.cuh", + "cuda/include/thrust/system/cuda/detail/cub/block/block_radix_rank.cuh", + "cuda/include/thrust/system/cuda/detail/cub/block/block_radix_sort.cuh", + "cuda/include/thrust/system/cuda/detail/cub/block/block_raking_layout.cuh", + "cuda/include/thrust/system/cuda/detail/cub/block/block_reduce.cuh", + "cuda/include/thrust/system/cuda/detail/cub/block/block_scan.cuh", + "cuda/include/thrust/system/cuda/detail/cub/block/block_shuffle.cuh", + "cuda/include/thrust/system/cuda/detail/cub/block/block_store.cuh", + "cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_histogram_atomic.cuh", + "cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_histogram_sort.cuh", + "cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_reduce_raking.cuh", + "cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_reduce_raking_commutative_only.cuh", + "cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_reduce_warp_reductions.cuh", + "cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_raking.cuh", + "cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_warp_scans.cuh", + "cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_warp_scans2.cuh", + "cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_warp_scans3.cuh", + "cuda/include/thrust/system/cuda/detail/cub/cub.cuh", + "cuda/include/thrust/system/cuda/detail/cub/device/device_histogram.cuh", + "cuda/include/thrust/system/cuda/detail/cub/device/device_partition.cuh", + "cuda/include/thrust/system/cuda/detail/cub/device/device_radix_sort.cuh", + "cuda/include/thrust/system/cuda/detail/cub/device/device_reduce.cuh", + "cuda/include/thrust/system/cuda/detail/cub/device/device_run_length_encode.cuh", + "cuda/include/thrust/system/cuda/detail/cub/device/device_scan.cuh", + "cuda/include/thrust/system/cuda/detail/cub/device/device_segmented_radix_sort.cuh", + "cuda/include/thrust/system/cuda/detail/cub/device/device_segmented_reduce.cuh", + "cuda/include/thrust/system/cuda/detail/cub/device/device_select.cuh", + "cuda/include/thrust/system/cuda/detail/cub/device/device_spmv.cuh", + "cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_histogram.cuh", + "cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_radix_sort.cuh", + "cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_reduce.cuh", + "cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_reduce_by_key.cuh", + "cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_rle.cuh", + "cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_scan.cuh", + "cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_select_if.cuh", + "cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_spmv_orig.cuh", + "cuda/include/thrust/system/cuda/detail/cub/grid/grid_barrier.cuh", + "cuda/include/thrust/system/cuda/detail/cub/grid/grid_even_share.cuh", + "cuda/include/thrust/system/cuda/detail/cub/grid/grid_mapping.cuh", + "cuda/include/thrust/system/cuda/detail/cub/grid/grid_queue.cuh", + "cuda/include/thrust/system/cuda/detail/cub/host/mutex.cuh", + "cuda/include/thrust/system/cuda/detail/cub/iterator/arg_index_input_iterator.cuh", + "cuda/include/thrust/system/cuda/detail/cub/iterator/cache_modified_input_iterator.cuh", + "cuda/include/thrust/system/cuda/detail/cub/iterator/cache_modified_output_iterator.cuh", + "cuda/include/thrust/system/cuda/detail/cub/iterator/constant_input_iterator.cuh", + "cuda/include/thrust/system/cuda/detail/cub/iterator/counting_input_iterator.cuh", + "cuda/include/thrust/system/cuda/detail/cub/iterator/discard_output_iterator.cuh", + "cuda/include/thrust/system/cuda/detail/cub/iterator/tex_obj_input_iterator.cuh", + "cuda/include/thrust/system/cuda/detail/cub/iterator/tex_ref_input_iterator.cuh", + "cuda/include/thrust/system/cuda/detail/cub/iterator/transform_input_iterator.cuh", + "cuda/include/thrust/system/cuda/detail/cub/thread/thread_load.cuh", + "cuda/include/thrust/system/cuda/detail/cub/thread/thread_operators.cuh", + "cuda/include/thrust/system/cuda/detail/cub/thread/thread_reduce.cuh", + "cuda/include/thrust/system/cuda/detail/cub/thread/thread_scan.cuh", + "cuda/include/thrust/system/cuda/detail/cub/thread/thread_search.cuh", + "cuda/include/thrust/system/cuda/detail/cub/thread/thread_store.cuh", + "cuda/include/thrust/system/cuda/detail/cub/util_allocator.cuh", + "cuda/include/thrust/system/cuda/detail/cub/util_arch.cuh", + "cuda/include/thrust/system/cuda/detail/cub/util_debug.cuh", + "cuda/include/thrust/system/cuda/detail/cub/util_device.cuh", + "cuda/include/thrust/system/cuda/detail/cub/util_macro.cuh", + "cuda/include/thrust/system/cuda/detail/cub/util_namespace.cuh", + "cuda/include/thrust/system/cuda/detail/cub/util_ptx.cuh", + "cuda/include/thrust/system/cuda/detail/cub/util_type.cuh", + "cuda/include/thrust/system/cuda/detail/cub/warp/specializations/warp_reduce_shfl.cuh", + "cuda/include/thrust/system/cuda/detail/cub/warp/specializations/warp_reduce_smem.cuh", + "cuda/include/thrust/system/cuda/detail/cub/warp/specializations/warp_scan_shfl.cuh", + "cuda/include/thrust/system/cuda/detail/cub/warp/specializations/warp_scan_smem.cuh", + "cuda/include/thrust/system/cuda/detail/cub/warp/warp_reduce.cuh", + "cuda/include/thrust/system/cuda/detail/cub/warp/warp_scan.cuh", + "cuda/include/thrust/system/cuda/detail/equal.h", + "cuda/include/thrust/system/cuda/detail/error.inl", + "cuda/include/thrust/system/cuda/detail/execution_policy.h", + "cuda/include/thrust/system/cuda/detail/extrema.h", + "cuda/include/thrust/system/cuda/detail/fill.h", + "cuda/include/thrust/system/cuda/detail/find.h", + "cuda/include/thrust/system/cuda/detail/for_each.h", + "cuda/include/thrust/system/cuda/detail/gather.h", + "cuda/include/thrust/system/cuda/detail/generate.h", + "cuda/include/thrust/system/cuda/detail/get_value.h", + "cuda/include/thrust/system/cuda/detail/guarded_cuda_runtime_api.h", + "cuda/include/thrust/system/cuda/detail/guarded_driver_types.h", + "cuda/include/thrust/system/cuda/detail/inner_product.h", + "cuda/include/thrust/system/cuda/detail/internal/copy_cross_system.h", + "cuda/include/thrust/system/cuda/detail/internal/copy_device_to_device.h", + "cuda/include/thrust/system/cuda/detail/iter_swap.h", + "cuda/include/thrust/system/cuda/detail/logical.h", + "cuda/include/thrust/system/cuda/detail/malloc_and_free.h", + "cuda/include/thrust/system/cuda/detail/memory.inl", + "cuda/include/thrust/system/cuda/detail/merge.h", + "cuda/include/thrust/system/cuda/detail/mismatch.h", + "cuda/include/thrust/system/cuda/detail/par.h", + "cuda/include/thrust/system/cuda/detail/par_to_seq.h", + "cuda/include/thrust/system/cuda/detail/parallel_for.h", + "cuda/include/thrust/system/cuda/detail/partition.h", + "cuda/include/thrust/system/cuda/detail/reduce.h", + "cuda/include/thrust/system/cuda/detail/reduce_by_key.h", + "cuda/include/thrust/system/cuda/detail/remove.h", + "cuda/include/thrust/system/cuda/detail/replace.h", + "cuda/include/thrust/system/cuda/detail/reverse.h", + "cuda/include/thrust/system/cuda/detail/scan.h", + "cuda/include/thrust/system/cuda/detail/scan_by_key.h", + "cuda/include/thrust/system/cuda/detail/scatter.h", + "cuda/include/thrust/system/cuda/detail/sequence.h", + "cuda/include/thrust/system/cuda/detail/set_operations.h", + "cuda/include/thrust/system/cuda/detail/sort.h", + "cuda/include/thrust/system/cuda/detail/swap_ranges.h", + "cuda/include/thrust/system/cuda/detail/tabulate.h", + "cuda/include/thrust/system/cuda/detail/temporary_buffer.h", + "cuda/include/thrust/system/cuda/detail/terminate.h", + "cuda/include/thrust/system/cuda/detail/transform.h", + "cuda/include/thrust/system/cuda/detail/transform_reduce.h", + "cuda/include/thrust/system/cuda/detail/transform_scan.h", + "cuda/include/thrust/system/cuda/detail/uninitialized_copy.h", + "cuda/include/thrust/system/cuda/detail/uninitialized_fill.h", + "cuda/include/thrust/system/cuda/detail/unique.h", + "cuda/include/thrust/system/cuda/detail/unique_by_key.h", + "cuda/include/thrust/system/cuda/detail/util.h", + "cuda/include/thrust/system/cuda/detail/vector.inl", + "cuda/include/thrust/system/cuda/error.h", + "cuda/include/thrust/system/cuda/execution_policy.h", + "cuda/include/thrust/system/cuda/experimental/pinned_allocator.h", + "cuda/include/thrust/system/cuda/memory.h", + "cuda/include/thrust/system/cuda/vector.h", + "cuda/include/thrust/system/detail/adl/adjacent_difference.h", + "cuda/include/thrust/system/detail/adl/assign_value.h", + "cuda/include/thrust/system/detail/adl/binary_search.h", + "cuda/include/thrust/system/detail/adl/copy.h", + "cuda/include/thrust/system/detail/adl/copy_if.h", + "cuda/include/thrust/system/detail/adl/count.h", + "cuda/include/thrust/system/detail/adl/equal.h", + "cuda/include/thrust/system/detail/adl/extrema.h", + "cuda/include/thrust/system/detail/adl/fill.h", + "cuda/include/thrust/system/detail/adl/find.h", + "cuda/include/thrust/system/detail/adl/for_each.h", + "cuda/include/thrust/system/detail/adl/gather.h", + "cuda/include/thrust/system/detail/adl/generate.h", + "cuda/include/thrust/system/detail/adl/get_value.h", + "cuda/include/thrust/system/detail/adl/inner_product.h", + "cuda/include/thrust/system/detail/adl/iter_swap.h", + "cuda/include/thrust/system/detail/adl/logical.h", + "cuda/include/thrust/system/detail/adl/malloc_and_free.h", + "cuda/include/thrust/system/detail/adl/merge.h", + "cuda/include/thrust/system/detail/adl/mismatch.h", + "cuda/include/thrust/system/detail/adl/partition.h", + "cuda/include/thrust/system/detail/adl/reduce.h", + "cuda/include/thrust/system/detail/adl/reduce_by_key.h", + "cuda/include/thrust/system/detail/adl/remove.h", + "cuda/include/thrust/system/detail/adl/replace.h", + "cuda/include/thrust/system/detail/adl/reverse.h", + "cuda/include/thrust/system/detail/adl/scan.h", + "cuda/include/thrust/system/detail/adl/scan_by_key.h", + "cuda/include/thrust/system/detail/adl/scatter.h", + "cuda/include/thrust/system/detail/adl/sequence.h", + "cuda/include/thrust/system/detail/adl/set_operations.h", + "cuda/include/thrust/system/detail/adl/sort.h", + "cuda/include/thrust/system/detail/adl/swap_ranges.h", + "cuda/include/thrust/system/detail/adl/tabulate.h", + "cuda/include/thrust/system/detail/adl/temporary_buffer.h", + "cuda/include/thrust/system/detail/adl/transform.h", + "cuda/include/thrust/system/detail/adl/transform_reduce.h", + "cuda/include/thrust/system/detail/adl/transform_scan.h", + "cuda/include/thrust/system/detail/adl/uninitialized_copy.h", + "cuda/include/thrust/system/detail/adl/uninitialized_fill.h", + "cuda/include/thrust/system/detail/adl/unique.h", + "cuda/include/thrust/system/detail/adl/unique_by_key.h", + "cuda/include/thrust/system/detail/bad_alloc.h", + "cuda/include/thrust/system/detail/errno.h", + "cuda/include/thrust/system/detail/error_category.inl", + "cuda/include/thrust/system/detail/error_code.inl", + "cuda/include/thrust/system/detail/error_condition.inl", + "cuda/include/thrust/system/detail/generic/adjacent_difference.h", + "cuda/include/thrust/system/detail/generic/adjacent_difference.inl", + "cuda/include/thrust/system/detail/generic/advance.h", + "cuda/include/thrust/system/detail/generic/advance.inl", + "cuda/include/thrust/system/detail/generic/binary_search.h", + "cuda/include/thrust/system/detail/generic/binary_search.inl", + "cuda/include/thrust/system/detail/generic/copy.h", + "cuda/include/thrust/system/detail/generic/copy.inl", + "cuda/include/thrust/system/detail/generic/copy_if.h", + "cuda/include/thrust/system/detail/generic/copy_if.inl", + "cuda/include/thrust/system/detail/generic/count.h", + "cuda/include/thrust/system/detail/generic/count.inl", + "cuda/include/thrust/system/detail/generic/distance.h", + "cuda/include/thrust/system/detail/generic/distance.inl", + "cuda/include/thrust/system/detail/generic/equal.h", + "cuda/include/thrust/system/detail/generic/equal.inl", + "cuda/include/thrust/system/detail/generic/extrema.h", + "cuda/include/thrust/system/detail/generic/extrema.inl", + "cuda/include/thrust/system/detail/generic/fill.h", + "cuda/include/thrust/system/detail/generic/find.h", + "cuda/include/thrust/system/detail/generic/find.inl", + "cuda/include/thrust/system/detail/generic/for_each.h", + "cuda/include/thrust/system/detail/generic/gather.h", + "cuda/include/thrust/system/detail/generic/gather.inl", + "cuda/include/thrust/system/detail/generic/generate.h", + "cuda/include/thrust/system/detail/generic/generate.inl", + "cuda/include/thrust/system/detail/generic/inner_product.h", + "cuda/include/thrust/system/detail/generic/inner_product.inl", + "cuda/include/thrust/system/detail/generic/logical.h", + "cuda/include/thrust/system/detail/generic/memory.h", + "cuda/include/thrust/system/detail/generic/memory.inl", + "cuda/include/thrust/system/detail/generic/merge.h", + "cuda/include/thrust/system/detail/generic/merge.inl", + "cuda/include/thrust/system/detail/generic/mismatch.h", + "cuda/include/thrust/system/detail/generic/mismatch.inl", + "cuda/include/thrust/system/detail/generic/partition.h", + "cuda/include/thrust/system/detail/generic/partition.inl", + "cuda/include/thrust/system/detail/generic/reduce.h", + "cuda/include/thrust/system/detail/generic/reduce.inl", + "cuda/include/thrust/system/detail/generic/reduce_by_key.h", + "cuda/include/thrust/system/detail/generic/reduce_by_key.inl", + "cuda/include/thrust/system/detail/generic/remove.h", + "cuda/include/thrust/system/detail/generic/remove.inl", + "cuda/include/thrust/system/detail/generic/replace.h", + "cuda/include/thrust/system/detail/generic/replace.inl", + "cuda/include/thrust/system/detail/generic/reverse.h", + "cuda/include/thrust/system/detail/generic/reverse.inl", + "cuda/include/thrust/system/detail/generic/scalar/binary_search.h", + "cuda/include/thrust/system/detail/generic/scalar/binary_search.inl", + "cuda/include/thrust/system/detail/generic/scan.h", + "cuda/include/thrust/system/detail/generic/scan.inl", + "cuda/include/thrust/system/detail/generic/scan_by_key.h", + "cuda/include/thrust/system/detail/generic/scan_by_key.inl", + "cuda/include/thrust/system/detail/generic/scatter.h", + "cuda/include/thrust/system/detail/generic/scatter.inl", + "cuda/include/thrust/system/detail/generic/select_system.h", + "cuda/include/thrust/system/detail/generic/sequence.h", + "cuda/include/thrust/system/detail/generic/sequence.inl", + "cuda/include/thrust/system/detail/generic/set_operations.h", + "cuda/include/thrust/system/detail/generic/set_operations.inl", + "cuda/include/thrust/system/detail/generic/sort.h", + "cuda/include/thrust/system/detail/generic/sort.inl", + "cuda/include/thrust/system/detail/generic/swap_ranges.h", + "cuda/include/thrust/system/detail/generic/swap_ranges.inl", + "cuda/include/thrust/system/detail/generic/tabulate.h", + "cuda/include/thrust/system/detail/generic/tabulate.inl", + "cuda/include/thrust/system/detail/generic/tag.h", + "cuda/include/thrust/system/detail/generic/temporary_buffer.h", + "cuda/include/thrust/system/detail/generic/temporary_buffer.inl", + "cuda/include/thrust/system/detail/generic/transform.h", + "cuda/include/thrust/system/detail/generic/transform.inl", + "cuda/include/thrust/system/detail/generic/transform_reduce.h", + "cuda/include/thrust/system/detail/generic/transform_reduce.inl", + "cuda/include/thrust/system/detail/generic/transform_scan.h", + "cuda/include/thrust/system/detail/generic/transform_scan.inl", + "cuda/include/thrust/system/detail/generic/type_traits.h", + "cuda/include/thrust/system/detail/generic/uninitialized_copy.h", + "cuda/include/thrust/system/detail/generic/uninitialized_copy.inl", + "cuda/include/thrust/system/detail/generic/uninitialized_fill.h", + "cuda/include/thrust/system/detail/generic/uninitialized_fill.inl", + "cuda/include/thrust/system/detail/generic/unique.h", + "cuda/include/thrust/system/detail/generic/unique.inl", + "cuda/include/thrust/system/detail/generic/unique_by_key.h", + "cuda/include/thrust/system/detail/generic/unique_by_key.inl", + "cuda/include/thrust/system/detail/internal/decompose.h", + "cuda/include/thrust/system/detail/sequential/adjacent_difference.h", + "cuda/include/thrust/system/detail/sequential/assign_value.h", + "cuda/include/thrust/system/detail/sequential/binary_search.h", + "cuda/include/thrust/system/detail/sequential/copy.h", + "cuda/include/thrust/system/detail/sequential/copy.inl", + "cuda/include/thrust/system/detail/sequential/copy_backward.h", + "cuda/include/thrust/system/detail/sequential/copy_if.h", + "cuda/include/thrust/system/detail/sequential/count.h", + "cuda/include/thrust/system/detail/sequential/equal.h", + "cuda/include/thrust/system/detail/sequential/execution_policy.h", + "cuda/include/thrust/system/detail/sequential/extrema.h", + "cuda/include/thrust/system/detail/sequential/fill.h", + "cuda/include/thrust/system/detail/sequential/find.h", + "cuda/include/thrust/system/detail/sequential/for_each.h", + "cuda/include/thrust/system/detail/sequential/gather.h", + "cuda/include/thrust/system/detail/sequential/general_copy.h", + "cuda/include/thrust/system/detail/sequential/generate.h", + "cuda/include/thrust/system/detail/sequential/get_value.h", + "cuda/include/thrust/system/detail/sequential/inner_product.h", + "cuda/include/thrust/system/detail/sequential/insertion_sort.h", + "cuda/include/thrust/system/detail/sequential/iter_swap.h", + "cuda/include/thrust/system/detail/sequential/logical.h", + "cuda/include/thrust/system/detail/sequential/malloc_and_free.h", + "cuda/include/thrust/system/detail/sequential/merge.h", + "cuda/include/thrust/system/detail/sequential/merge.inl", + "cuda/include/thrust/system/detail/sequential/mismatch.h", + "cuda/include/thrust/system/detail/sequential/partition.h", + "cuda/include/thrust/system/detail/sequential/reduce.h", + "cuda/include/thrust/system/detail/sequential/reduce_by_key.h", + "cuda/include/thrust/system/detail/sequential/remove.h", + "cuda/include/thrust/system/detail/sequential/replace.h", + "cuda/include/thrust/system/detail/sequential/reverse.h", + "cuda/include/thrust/system/detail/sequential/scan.h", + "cuda/include/thrust/system/detail/sequential/scan_by_key.h", + "cuda/include/thrust/system/detail/sequential/scatter.h", + "cuda/include/thrust/system/detail/sequential/sequence.h", + "cuda/include/thrust/system/detail/sequential/set_operations.h", + "cuda/include/thrust/system/detail/sequential/sort.h", + "cuda/include/thrust/system/detail/sequential/sort.inl", + "cuda/include/thrust/system/detail/sequential/stable_merge_sort.h", + "cuda/include/thrust/system/detail/sequential/stable_merge_sort.inl", + "cuda/include/thrust/system/detail/sequential/stable_primitive_sort.h", + "cuda/include/thrust/system/detail/sequential/stable_primitive_sort.inl", + "cuda/include/thrust/system/detail/sequential/stable_radix_sort.h", + "cuda/include/thrust/system/detail/sequential/stable_radix_sort.inl", + "cuda/include/thrust/system/detail/sequential/swap_ranges.h", + "cuda/include/thrust/system/detail/sequential/tabulate.h", + "cuda/include/thrust/system/detail/sequential/temporary_buffer.h", + "cuda/include/thrust/system/detail/sequential/transform.h", + "cuda/include/thrust/system/detail/sequential/transform_reduce.h", + "cuda/include/thrust/system/detail/sequential/transform_scan.h", + "cuda/include/thrust/system/detail/sequential/trivial_copy.h", + "cuda/include/thrust/system/detail/sequential/uninitialized_copy.h", + "cuda/include/thrust/system/detail/sequential/uninitialized_fill.h", + "cuda/include/thrust/system/detail/sequential/unique.h", + "cuda/include/thrust/system/detail/sequential/unique_by_key.h", + "cuda/include/thrust/system/detail/system_error.inl", + "cuda/include/thrust/system/error_code.h", + "cuda/include/thrust/system/omp/detail/adjacent_difference.h", + "cuda/include/thrust/system/omp/detail/assign_value.h", + "cuda/include/thrust/system/omp/detail/binary_search.h", + "cuda/include/thrust/system/omp/detail/copy.h", + "cuda/include/thrust/system/omp/detail/copy.inl", + "cuda/include/thrust/system/omp/detail/copy_if.h", + "cuda/include/thrust/system/omp/detail/copy_if.inl", + "cuda/include/thrust/system/omp/detail/count.h", + "cuda/include/thrust/system/omp/detail/default_decomposition.h", + "cuda/include/thrust/system/omp/detail/default_decomposition.inl", + "cuda/include/thrust/system/omp/detail/equal.h", + "cuda/include/thrust/system/omp/detail/execution_policy.h", + "cuda/include/thrust/system/omp/detail/extrema.h", + "cuda/include/thrust/system/omp/detail/fill.h", + "cuda/include/thrust/system/omp/detail/find.h", + "cuda/include/thrust/system/omp/detail/for_each.h", + "cuda/include/thrust/system/omp/detail/for_each.inl", + "cuda/include/thrust/system/omp/detail/gather.h", + "cuda/include/thrust/system/omp/detail/generate.h", + "cuda/include/thrust/system/omp/detail/get_value.h", + "cuda/include/thrust/system/omp/detail/inner_product.h", + "cuda/include/thrust/system/omp/detail/iter_swap.h", + "cuda/include/thrust/system/omp/detail/logical.h", + "cuda/include/thrust/system/omp/detail/malloc_and_free.h", + "cuda/include/thrust/system/omp/detail/memory.inl", + "cuda/include/thrust/system/omp/detail/merge.h", + "cuda/include/thrust/system/omp/detail/mismatch.h", + "cuda/include/thrust/system/omp/detail/par.h", + "cuda/include/thrust/system/omp/detail/partition.h", + "cuda/include/thrust/system/omp/detail/partition.inl", + "cuda/include/thrust/system/omp/detail/reduce.h", + "cuda/include/thrust/system/omp/detail/reduce.inl", + "cuda/include/thrust/system/omp/detail/reduce_by_key.h", + "cuda/include/thrust/system/omp/detail/reduce_by_key.inl", + "cuda/include/thrust/system/omp/detail/reduce_intervals.h", + "cuda/include/thrust/system/omp/detail/reduce_intervals.inl", + "cuda/include/thrust/system/omp/detail/remove.h", + "cuda/include/thrust/system/omp/detail/remove.inl", + "cuda/include/thrust/system/omp/detail/replace.h", + "cuda/include/thrust/system/omp/detail/reverse.h", + "cuda/include/thrust/system/omp/detail/scan.h", + "cuda/include/thrust/system/omp/detail/scan_by_key.h", + "cuda/include/thrust/system/omp/detail/scatter.h", + "cuda/include/thrust/system/omp/detail/sequence.h", + "cuda/include/thrust/system/omp/detail/set_operations.h", + "cuda/include/thrust/system/omp/detail/sort.h", + "cuda/include/thrust/system/omp/detail/sort.inl", + "cuda/include/thrust/system/omp/detail/swap_ranges.h", + "cuda/include/thrust/system/omp/detail/tabulate.h", + "cuda/include/thrust/system/omp/detail/temporary_buffer.h", + "cuda/include/thrust/system/omp/detail/transform.h", + "cuda/include/thrust/system/omp/detail/transform_reduce.h", + "cuda/include/thrust/system/omp/detail/transform_scan.h", + "cuda/include/thrust/system/omp/detail/uninitialized_copy.h", + "cuda/include/thrust/system/omp/detail/uninitialized_fill.h", + "cuda/include/thrust/system/omp/detail/unique.h", + "cuda/include/thrust/system/omp/detail/unique.inl", + "cuda/include/thrust/system/omp/detail/unique_by_key.h", + "cuda/include/thrust/system/omp/detail/unique_by_key.inl", + "cuda/include/thrust/system/omp/detail/vector.inl", + "cuda/include/thrust/system/omp/execution_policy.h", + "cuda/include/thrust/system/omp/memory.h", + "cuda/include/thrust/system/omp/vector.h", + "cuda/include/thrust/system/system_error.h", + "cuda/include/thrust/system/tbb/detail/adjacent_difference.h", + "cuda/include/thrust/system/tbb/detail/assign_value.h", + "cuda/include/thrust/system/tbb/detail/binary_search.h", + "cuda/include/thrust/system/tbb/detail/copy.h", + "cuda/include/thrust/system/tbb/detail/copy.inl", + "cuda/include/thrust/system/tbb/detail/copy_if.h", + "cuda/include/thrust/system/tbb/detail/copy_if.inl", + "cuda/include/thrust/system/tbb/detail/count.h", + "cuda/include/thrust/system/tbb/detail/equal.h", + "cuda/include/thrust/system/tbb/detail/execution_policy.h", + "cuda/include/thrust/system/tbb/detail/extrema.h", + "cuda/include/thrust/system/tbb/detail/fill.h", + "cuda/include/thrust/system/tbb/detail/find.h", + "cuda/include/thrust/system/tbb/detail/for_each.h", + "cuda/include/thrust/system/tbb/detail/for_each.inl", + "cuda/include/thrust/system/tbb/detail/gather.h", + "cuda/include/thrust/system/tbb/detail/generate.h", + "cuda/include/thrust/system/tbb/detail/get_value.h", + "cuda/include/thrust/system/tbb/detail/inner_product.h", + "cuda/include/thrust/system/tbb/detail/iter_swap.h", + "cuda/include/thrust/system/tbb/detail/logical.h", + "cuda/include/thrust/system/tbb/detail/malloc_and_free.h", + "cuda/include/thrust/system/tbb/detail/memory.inl", + "cuda/include/thrust/system/tbb/detail/merge.h", + "cuda/include/thrust/system/tbb/detail/merge.inl", + "cuda/include/thrust/system/tbb/detail/mismatch.h", + "cuda/include/thrust/system/tbb/detail/par.h", + "cuda/include/thrust/system/tbb/detail/partition.h", + "cuda/include/thrust/system/tbb/detail/partition.inl", + "cuda/include/thrust/system/tbb/detail/reduce.h", + "cuda/include/thrust/system/tbb/detail/reduce.inl", + "cuda/include/thrust/system/tbb/detail/reduce_by_key.h", + "cuda/include/thrust/system/tbb/detail/reduce_by_key.inl", + "cuda/include/thrust/system/tbb/detail/reduce_intervals.h", + "cuda/include/thrust/system/tbb/detail/remove.h", + "cuda/include/thrust/system/tbb/detail/remove.inl", + "cuda/include/thrust/system/tbb/detail/replace.h", + "cuda/include/thrust/system/tbb/detail/reverse.h", + "cuda/include/thrust/system/tbb/detail/scan.h", + "cuda/include/thrust/system/tbb/detail/scan.inl", + "cuda/include/thrust/system/tbb/detail/scan_by_key.h", + "cuda/include/thrust/system/tbb/detail/scatter.h", + "cuda/include/thrust/system/tbb/detail/sequence.h", + "cuda/include/thrust/system/tbb/detail/set_operations.h", + "cuda/include/thrust/system/tbb/detail/sort.h", + "cuda/include/thrust/system/tbb/detail/sort.inl", + "cuda/include/thrust/system/tbb/detail/swap_ranges.h", + "cuda/include/thrust/system/tbb/detail/tabulate.h", + "cuda/include/thrust/system/tbb/detail/temporary_buffer.h", + "cuda/include/thrust/system/tbb/detail/transform.h", + "cuda/include/thrust/system/tbb/detail/transform_reduce.h", + "cuda/include/thrust/system/tbb/detail/transform_scan.h", + "cuda/include/thrust/system/tbb/detail/uninitialized_copy.h", + "cuda/include/thrust/system/tbb/detail/uninitialized_fill.h", + "cuda/include/thrust/system/tbb/detail/unique.h", + "cuda/include/thrust/system/tbb/detail/unique.inl", + "cuda/include/thrust/system/tbb/detail/unique_by_key.h", + "cuda/include/thrust/system/tbb/detail/unique_by_key.inl", + "cuda/include/thrust/system/tbb/detail/vector.inl", + "cuda/include/thrust/system/tbb/execution_policy.h", + "cuda/include/thrust/system/tbb/memory.h", + "cuda/include/thrust/system/tbb/vector.h", + "cuda/include/thrust/system_error.h", + "cuda/include/thrust/tabulate.h", + "cuda/include/thrust/transform.h", + "cuda/include/thrust/transform_reduce.h", + "cuda/include/thrust/transform_scan.h", + "cuda/include/thrust/tuple.h", + "cuda/include/thrust/uninitialized_copy.h", + "cuda/include/thrust/uninitialized_fill.h", + "cuda/include/thrust/unique.h", + "cuda/include/thrust/version.h", + "cuda/include/vector_functions.h", + "cuda/include/vector_functions.hpp", + "cuda/include/vector_types.h", + ], + cmd = """ +if [ -d "$(@D)/extras" ]; then rm $(@D)/extras -drf; fi && if [ -d "$(@D)/include" ]; then rm $(@D)/include -drf; fi && if [ -d "$(@D)/lib" ]; then rm $(@D)/lib -drf; fi && if [ -d "$(@D)/nvvm" ]; then rm $(@D)/nvvm -drf; fi && cp -f "/usr/local/cuda-10.0/include/CL/cl.h" "$(@D)/cuda/include/CL/cl.h" && cp -f "/usr/local/cuda-10.0/include/CL/cl.hpp" "$(@D)/cuda/include/CL/cl.hpp" && cp -f "/usr/local/cuda-10.0/include/CL/cl_egl.h" "$(@D)/cuda/include/CL/cl_egl.h" && cp -f "/usr/local/cuda-10.0/include/CL/cl_ext.h" "$(@D)/cuda/include/CL/cl_ext.h" && cp -f "/usr/local/cuda-10.0/include/CL/cl_gl.h" "$(@D)/cuda/include/CL/cl_gl.h" && cp -f "/usr/local/cuda-10.0/include/CL/cl_gl_ext.h" "$(@D)/cuda/include/CL/cl_gl_ext.h" && cp -f "/usr/local/cuda-10.0/include/CL/cl_platform.h" "$(@D)/cuda/include/CL/cl_platform.h" && cp -f "/usr/local/cuda-10.0/include/CL/opencl.h" "$(@D)/cuda/include/CL/opencl.h" && cp -f "/usr/local/cuda-10.0/include/builtin_types.h" "$(@D)/cuda/include/builtin_types.h" && cp -f "/usr/local/cuda-10.0/include/channel_descriptor.h" "$(@D)/cuda/include/channel_descriptor.h" && cp -f "/usr/local/cuda-10.0/include/common_functions.h" "$(@D)/cuda/include/common_functions.h" && cp -f "/usr/local/cuda-10.0/include/cooperative_groups.h" "$(@D)/cuda/include/cooperative_groups.h" && cp -f "/usr/local/cuda-10.0/include/cooperative_groups_helpers.h" "$(@D)/cuda/include/cooperative_groups_helpers.h" && cp -f "/usr/local/cuda-10.0/include/crt/common_functions.h" "$(@D)/cuda/include/crt/common_functions.h" && cp -f "/usr/local/cuda-10.0/include/crt/device_double_functions.h" "$(@D)/cuda/include/crt/device_double_functions.h" && cp -f "/usr/local/cuda-10.0/include/crt/device_double_functions.hpp" "$(@D)/cuda/include/crt/device_double_functions.hpp" && cp -f "/usr/local/cuda-10.0/include/crt/device_functions.h" "$(@D)/cuda/include/crt/device_functions.h" && cp -f "/usr/local/cuda-10.0/include/crt/device_functions.hpp" "$(@D)/cuda/include/crt/device_functions.hpp" && cp -f "/usr/local/cuda-10.0/include/crt/func_macro.h" "$(@D)/cuda/include/crt/func_macro.h" && cp -f "/usr/local/cuda-10.0/include/crt/host_config.h" "$(@D)/cuda/include/crt/host_config.h" && cp -f "/usr/local/cuda-10.0/include/crt/host_defines.h" "$(@D)/cuda/include/crt/host_defines.h" && cp -f "/usr/local/cuda-10.0/include/crt/host_runtime.h" "$(@D)/cuda/include/crt/host_runtime.h" && cp -f "/usr/local/cuda-10.0/include/crt/math_functions.h" "$(@D)/cuda/include/crt/math_functions.h" && cp -f "/usr/local/cuda-10.0/include/crt/math_functions.hpp" "$(@D)/cuda/include/crt/math_functions.hpp" && cp -f "/usr/local/cuda-10.0/include/crt/mma.h" "$(@D)/cuda/include/crt/mma.h" && cp -f "/usr/local/cuda-10.0/include/crt/mma.hpp" "$(@D)/cuda/include/crt/mma.hpp" && cp -f "/usr/local/cuda-10.0/include/crt/nvfunctional" "$(@D)/cuda/include/crt/nvfunctional" && cp -f "/usr/local/cuda-10.0/include/crt/sm_70_rt.h" "$(@D)/cuda/include/crt/sm_70_rt.h" && cp -f "/usr/local/cuda-10.0/include/crt/sm_70_rt.hpp" "$(@D)/cuda/include/crt/sm_70_rt.hpp" && cp -f "/usr/local/cuda-10.0/include/crt/storage_class.h" "$(@D)/cuda/include/crt/storage_class.h" && cp -f "/usr/local/cuda-10.0/include/cuComplex.h" "$(@D)/cuda/include/cuComplex.h" && cp -f "/usr/local/cuda-10.0/include/cublas.h" "$(@D)/cuda/include/cublas.h" && cp -f "/usr/local/cuda-10.0/include/cublasXt.h" "$(@D)/cuda/include/cublasXt.h" && cp -f "/usr/local/cuda-10.0/include/cublas_api.h" "$(@D)/cuda/include/cublas_api.h" && cp -f "/usr/local/cuda-10.0/include/cublas_v2.h" "$(@D)/cuda/include/cublas_v2.h" && cp -f "/usr/local/cuda-10.0/include/cuda.h" "$(@D)/cuda/include/cuda.h" && cp -f "/usr/local/cuda-10.0/include/cudaEGL.h" "$(@D)/cuda/include/cudaEGL.h" && cp -f "/usr/local/cuda-10.0/include/cudaGL.h" "$(@D)/cuda/include/cudaGL.h" && cp -f "/usr/local/cuda-10.0/include/cudaProfiler.h" "$(@D)/cuda/include/cudaProfiler.h" && cp -f "/usr/local/cuda-10.0/include/cudaVDPAU.h" "$(@D)/cuda/include/cudaVDPAU.h" && cp -f "/usr/local/cuda-10.0/include/cuda_device_runtime_api.h" "$(@D)/cuda/include/cuda_device_runtime_api.h" && cp -f "/usr/local/cuda-10.0/include/cuda_egl_interop.h" "$(@D)/cuda/include/cuda_egl_interop.h" && cp -f "/usr/local/cuda-10.0/include/cuda_fp16.h" "$(@D)/cuda/include/cuda_fp16.h" && cp -f "/usr/local/cuda-10.0/include/cuda_fp16.hpp" "$(@D)/cuda/include/cuda_fp16.hpp" && cp -f "/usr/local/cuda-10.0/include/cuda_gl_interop.h" "$(@D)/cuda/include/cuda_gl_interop.h" && cp -f "/usr/local/cuda-10.0/include/cuda_occupancy.h" "$(@D)/cuda/include/cuda_occupancy.h" && cp -f "/usr/local/cuda-10.0/include/cuda_profiler_api.h" "$(@D)/cuda/include/cuda_profiler_api.h" && cp -f "/usr/local/cuda-10.0/include/cuda_runtime.h" "$(@D)/cuda/include/cuda_runtime.h" && cp -f "/usr/local/cuda-10.0/include/cuda_runtime_api.h" "$(@D)/cuda/include/cuda_runtime_api.h" && cp -f "/usr/local/cuda-10.0/include/cuda_surface_types.h" "$(@D)/cuda/include/cuda_surface_types.h" && cp -f "/usr/local/cuda-10.0/include/cuda_texture_types.h" "$(@D)/cuda/include/cuda_texture_types.h" && cp -f "/usr/local/cuda-10.0/include/cuda_vdpau_interop.h" "$(@D)/cuda/include/cuda_vdpau_interop.h" && cp -f "/usr/local/cuda-10.0/include/cudalibxt.h" "$(@D)/cuda/include/cudalibxt.h" && cp -f "/usr/local/cuda-10.0/include/cudart_platform.h" "$(@D)/cuda/include/cudart_platform.h" && cp -f "/usr/local/cuda-10.0/include/cufft.h" "$(@D)/cuda/include/cufft.h" && cp -f "/usr/local/cuda-10.0/include/cufftXt.h" "$(@D)/cuda/include/cufftXt.h" && cp -f "/usr/local/cuda-10.0/include/cufftw.h" "$(@D)/cuda/include/cufftw.h" && cp -f "/usr/local/cuda-10.0/include/curand.h" "$(@D)/cuda/include/curand.h" && cp -f "/usr/local/cuda-10.0/include/curand_discrete.h" "$(@D)/cuda/include/curand_discrete.h" && cp -f "/usr/local/cuda-10.0/include/curand_discrete2.h" "$(@D)/cuda/include/curand_discrete2.h" && cp -f "/usr/local/cuda-10.0/include/curand_globals.h" "$(@D)/cuda/include/curand_globals.h" && cp -f "/usr/local/cuda-10.0/include/curand_kernel.h" "$(@D)/cuda/include/curand_kernel.h" && cp -f "/usr/local/cuda-10.0/include/curand_lognormal.h" "$(@D)/cuda/include/curand_lognormal.h" && cp -f "/usr/local/cuda-10.0/include/curand_mrg32k3a.h" "$(@D)/cuda/include/curand_mrg32k3a.h" && cp -f "/usr/local/cuda-10.0/include/curand_mtgp32.h" "$(@D)/cuda/include/curand_mtgp32.h" && cp -f "/usr/local/cuda-10.0/include/curand_mtgp32_host.h" "$(@D)/cuda/include/curand_mtgp32_host.h" && cp -f "/usr/local/cuda-10.0/include/curand_mtgp32_kernel.h" "$(@D)/cuda/include/curand_mtgp32_kernel.h" && cp -f "/usr/local/cuda-10.0/include/curand_mtgp32dc_p_11213.h" "$(@D)/cuda/include/curand_mtgp32dc_p_11213.h" && cp -f "/usr/local/cuda-10.0/include/curand_normal.h" "$(@D)/cuda/include/curand_normal.h" && cp -f "/usr/local/cuda-10.0/include/curand_normal_static.h" "$(@D)/cuda/include/curand_normal_static.h" && cp -f "/usr/local/cuda-10.0/include/curand_philox4x32_x.h" "$(@D)/cuda/include/curand_philox4x32_x.h" && cp -f "/usr/local/cuda-10.0/include/curand_poisson.h" "$(@D)/cuda/include/curand_poisson.h" && cp -f "/usr/local/cuda-10.0/include/curand_precalc.h" "$(@D)/cuda/include/curand_precalc.h" && cp -f "/usr/local/cuda-10.0/include/curand_uniform.h" "$(@D)/cuda/include/curand_uniform.h" && cp -f "/usr/local/cuda-10.0/include/cusolverDn.h" "$(@D)/cuda/include/cusolverDn.h" && cp -f "/usr/local/cuda-10.0/include/cusolverRf.h" "$(@D)/cuda/include/cusolverRf.h" && cp -f "/usr/local/cuda-10.0/include/cusolverSp.h" "$(@D)/cuda/include/cusolverSp.h" && cp -f "/usr/local/cuda-10.0/include/cusolverSp_LOWLEVEL_PREVIEW.h" "$(@D)/cuda/include/cusolverSp_LOWLEVEL_PREVIEW.h" && cp -f "/usr/local/cuda-10.0/include/cusolver_common.h" "$(@D)/cuda/include/cusolver_common.h" && cp -f "/usr/local/cuda-10.0/include/cusparse.h" "$(@D)/cuda/include/cusparse.h" && cp -f "/usr/local/cuda-10.0/include/cusparse_v2.h" "$(@D)/cuda/include/cusparse_v2.h" && cp -f "/usr/local/cuda-10.0/include/device_atomic_functions.h" "$(@D)/cuda/include/device_atomic_functions.h" && cp -f "/usr/local/cuda-10.0/include/device_atomic_functions.hpp" "$(@D)/cuda/include/device_atomic_functions.hpp" && cp -f "/usr/local/cuda-10.0/include/device_double_functions.h" "$(@D)/cuda/include/device_double_functions.h" && cp -f "/usr/local/cuda-10.0/include/device_functions.h" "$(@D)/cuda/include/device_functions.h" && cp -f "/usr/local/cuda-10.0/include/device_launch_parameters.h" "$(@D)/cuda/include/device_launch_parameters.h" && cp -f "/usr/local/cuda-10.0/include/device_types.h" "$(@D)/cuda/include/device_types.h" && cp -f "/usr/local/cuda-10.0/include/driver_functions.h" "$(@D)/cuda/include/driver_functions.h" && cp -f "/usr/local/cuda-10.0/include/driver_types.h" "$(@D)/cuda/include/driver_types.h" && cp -f "/usr/local/cuda-10.0/include/fatBinaryCtl.h" "$(@D)/cuda/include/fatBinaryCtl.h" && cp -f "/usr/local/cuda-10.0/include/fatbinary.h" "$(@D)/cuda/include/fatbinary.h" && cp -f "/usr/local/cuda-10.0/include/host_config.h" "$(@D)/cuda/include/host_config.h" && cp -f "/usr/local/cuda-10.0/include/host_defines.h" "$(@D)/cuda/include/host_defines.h" && cp -f "/usr/local/cuda-10.0/include/library_types.h" "$(@D)/cuda/include/library_types.h" && cp -f "/usr/local/cuda-10.0/include/math_constants.h" "$(@D)/cuda/include/math_constants.h" && cp -f "/usr/local/cuda-10.0/include/math_functions.h" "$(@D)/cuda/include/math_functions.h" && cp -f "/usr/local/cuda-10.0/include/mma.h" "$(@D)/cuda/include/mma.h" && cp -f "/usr/local/cuda-10.0/include/npp.h" "$(@D)/cuda/include/npp.h" && cp -f "/usr/local/cuda-10.0/include/nppcore.h" "$(@D)/cuda/include/nppcore.h" && cp -f "/usr/local/cuda-10.0/include/nppdefs.h" "$(@D)/cuda/include/nppdefs.h" && cp -f "/usr/local/cuda-10.0/include/nppi.h" "$(@D)/cuda/include/nppi.h" && cp -f "/usr/local/cuda-10.0/include/nppi_arithmetic_and_logical_operations.h" "$(@D)/cuda/include/nppi_arithmetic_and_logical_operations.h" && cp -f "/usr/local/cuda-10.0/include/nppi_color_conversion.h" "$(@D)/cuda/include/nppi_color_conversion.h" && cp -f "/usr/local/cuda-10.0/include/nppi_compression_functions.h" "$(@D)/cuda/include/nppi_compression_functions.h" && cp -f "/usr/local/cuda-10.0/include/nppi_computer_vision.h" "$(@D)/cuda/include/nppi_computer_vision.h" && cp -f "/usr/local/cuda-10.0/include/nppi_data_exchange_and_initialization.h" "$(@D)/cuda/include/nppi_data_exchange_and_initialization.h" && cp -f "/usr/local/cuda-10.0/include/nppi_filtering_functions.h" "$(@D)/cuda/include/nppi_filtering_functions.h" && cp -f "/usr/local/cuda-10.0/include/nppi_geometry_transforms.h" "$(@D)/cuda/include/nppi_geometry_transforms.h" && cp -f "/usr/local/cuda-10.0/include/nppi_linear_transforms.h" "$(@D)/cuda/include/nppi_linear_transforms.h" && cp -f "/usr/local/cuda-10.0/include/nppi_morphological_operations.h" "$(@D)/cuda/include/nppi_morphological_operations.h" && cp -f "/usr/local/cuda-10.0/include/nppi_statistics_functions.h" "$(@D)/cuda/include/nppi_statistics_functions.h" && cp -f "/usr/local/cuda-10.0/include/nppi_support_functions.h" "$(@D)/cuda/include/nppi_support_functions.h" && cp -f "/usr/local/cuda-10.0/include/nppi_threshold_and_compare_operations.h" "$(@D)/cuda/include/nppi_threshold_and_compare_operations.h" && cp -f "/usr/local/cuda-10.0/include/npps.h" "$(@D)/cuda/include/npps.h" && cp -f "/usr/local/cuda-10.0/include/npps_arithmetic_and_logical_operations.h" "$(@D)/cuda/include/npps_arithmetic_and_logical_operations.h" && cp -f "/usr/local/cuda-10.0/include/npps_conversion_functions.h" "$(@D)/cuda/include/npps_conversion_functions.h" && cp -f "/usr/local/cuda-10.0/include/npps_filtering_functions.h" "$(@D)/cuda/include/npps_filtering_functions.h" && cp -f "/usr/local/cuda-10.0/include/npps_initialization.h" "$(@D)/cuda/include/npps_initialization.h" && cp -f "/usr/local/cuda-10.0/include/npps_statistics_functions.h" "$(@D)/cuda/include/npps_statistics_functions.h" && cp -f "/usr/local/cuda-10.0/include/npps_support_functions.h" "$(@D)/cuda/include/npps_support_functions.h" && cp -f "/usr/local/cuda-10.0/include/nppversion.h" "$(@D)/cuda/include/nppversion.h" && cp -f "/usr/local/cuda-10.0/include/nvToolsExt.h" "$(@D)/cuda/include/nvToolsExt.h" && cp -f "/usr/local/cuda-10.0/include/nvToolsExtCuda.h" "$(@D)/cuda/include/nvToolsExtCuda.h" && cp -f "/usr/local/cuda-10.0/include/nvToolsExtCudaRt.h" "$(@D)/cuda/include/nvToolsExtCudaRt.h" && cp -f "/usr/local/cuda-10.0/include/nvToolsExtMeta.h" "$(@D)/cuda/include/nvToolsExtMeta.h" && cp -f "/usr/local/cuda-10.0/include/nvToolsExtSync.h" "$(@D)/cuda/include/nvToolsExtSync.h" && cp -f "/usr/local/cuda-10.0/include/nvblas.h" "$(@D)/cuda/include/nvblas.h" && cp -f "/usr/local/cuda-10.0/include/nvfunctional" "$(@D)/cuda/include/nvfunctional" && cp -f "/usr/local/cuda-10.0/include/nvgraph.h" "$(@D)/cuda/include/nvgraph.h" && cp -f "/usr/local/cuda-10.0/include/nvjpeg.h" "$(@D)/cuda/include/nvjpeg.h" && cp -f "/usr/local/cuda-10.0/include/nvml.h" "$(@D)/cuda/include/nvml.h" && cp -f "/usr/local/cuda-10.0/include/nvrtc.h" "$(@D)/cuda/include/nvrtc.h" && cp -f "/usr/local/cuda-10.0/include/nvtx3/nvToolsExt.h" "$(@D)/cuda/include/nvtx3/nvToolsExt.h" && cp -f "/usr/local/cuda-10.0/include/nvtx3/nvToolsExtCuda.h" "$(@D)/cuda/include/nvtx3/nvToolsExtCuda.h" && cp -f "/usr/local/cuda-10.0/include/nvtx3/nvToolsExtCudaRt.h" "$(@D)/cuda/include/nvtx3/nvToolsExtCudaRt.h" && cp -f "/usr/local/cuda-10.0/include/nvtx3/nvToolsExtOpenCL.h" "$(@D)/cuda/include/nvtx3/nvToolsExtOpenCL.h" && cp -f "/usr/local/cuda-10.0/include/nvtx3/nvToolsExtSync.h" "$(@D)/cuda/include/nvtx3/nvToolsExtSync.h" && cp -f "/usr/local/cuda-10.0/include/nvtx3/nvtxDetail/nvtxImpl.h" "$(@D)/cuda/include/nvtx3/nvtxDetail/nvtxImpl.h" && cp -f "/usr/local/cuda-10.0/include/nvtx3/nvtxDetail/nvtxImplCore.h" "$(@D)/cuda/include/nvtx3/nvtxDetail/nvtxImplCore.h" && cp -f "/usr/local/cuda-10.0/include/nvtx3/nvtxDetail/nvtxImplCudaRt_v3.h" "$(@D)/cuda/include/nvtx3/nvtxDetail/nvtxImplCudaRt_v3.h" && cp -f "/usr/local/cuda-10.0/include/nvtx3/nvtxDetail/nvtxImplCuda_v3.h" "$(@D)/cuda/include/nvtx3/nvtxDetail/nvtxImplCuda_v3.h" && cp -f "/usr/local/cuda-10.0/include/nvtx3/nvtxDetail/nvtxImplOpenCL_v3.h" "$(@D)/cuda/include/nvtx3/nvtxDetail/nvtxImplOpenCL_v3.h" && cp -f "/usr/local/cuda-10.0/include/nvtx3/nvtxDetail/nvtxImplSync_v3.h" "$(@D)/cuda/include/nvtx3/nvtxDetail/nvtxImplSync_v3.h" && cp -f "/usr/local/cuda-10.0/include/nvtx3/nvtxDetail/nvtxInit.h" "$(@D)/cuda/include/nvtx3/nvtxDetail/nvtxInit.h" && cp -f "/usr/local/cuda-10.0/include/nvtx3/nvtxDetail/nvtxInitDecls.h" "$(@D)/cuda/include/nvtx3/nvtxDetail/nvtxInitDecls.h" && cp -f "/usr/local/cuda-10.0/include/nvtx3/nvtxDetail/nvtxInitDefs.h" "$(@D)/cuda/include/nvtx3/nvtxDetail/nvtxInitDefs.h" && cp -f "/usr/local/cuda-10.0/include/nvtx3/nvtxDetail/nvtxLinkOnce.h" "$(@D)/cuda/include/nvtx3/nvtxDetail/nvtxLinkOnce.h" && cp -f "/usr/local/cuda-10.0/include/nvtx3/nvtxDetail/nvtxTypes.h" "$(@D)/cuda/include/nvtx3/nvtxDetail/nvtxTypes.h" && cp -f "/usr/local/cuda-10.0/include/sm_20_atomic_functions.h" "$(@D)/cuda/include/sm_20_atomic_functions.h" && cp -f "/usr/local/cuda-10.0/include/sm_20_atomic_functions.hpp" "$(@D)/cuda/include/sm_20_atomic_functions.hpp" && cp -f "/usr/local/cuda-10.0/include/sm_20_intrinsics.h" "$(@D)/cuda/include/sm_20_intrinsics.h" && cp -f "/usr/local/cuda-10.0/include/sm_20_intrinsics.hpp" "$(@D)/cuda/include/sm_20_intrinsics.hpp" && cp -f "/usr/local/cuda-10.0/include/sm_30_intrinsics.h" "$(@D)/cuda/include/sm_30_intrinsics.h" && cp -f "/usr/local/cuda-10.0/include/sm_30_intrinsics.hpp" "$(@D)/cuda/include/sm_30_intrinsics.hpp" && cp -f "/usr/local/cuda-10.0/include/sm_32_atomic_functions.h" "$(@D)/cuda/include/sm_32_atomic_functions.h" && cp -f "/usr/local/cuda-10.0/include/sm_32_atomic_functions.hpp" "$(@D)/cuda/include/sm_32_atomic_functions.hpp" && cp -f "/usr/local/cuda-10.0/include/sm_32_intrinsics.h" "$(@D)/cuda/include/sm_32_intrinsics.h" && cp -f "/usr/local/cuda-10.0/include/sm_32_intrinsics.hpp" "$(@D)/cuda/include/sm_32_intrinsics.hpp" && cp -f "/usr/local/cuda-10.0/include/sm_35_atomic_functions.h" "$(@D)/cuda/include/sm_35_atomic_functions.h" && cp -f "/usr/local/cuda-10.0/include/sm_35_intrinsics.h" "$(@D)/cuda/include/sm_35_intrinsics.h" && cp -f "/usr/local/cuda-10.0/include/sm_60_atomic_functions.h" "$(@D)/cuda/include/sm_60_atomic_functions.h" && cp -f "/usr/local/cuda-10.0/include/sm_60_atomic_functions.hpp" "$(@D)/cuda/include/sm_60_atomic_functions.hpp" && cp -f "/usr/local/cuda-10.0/include/sm_61_intrinsics.h" "$(@D)/cuda/include/sm_61_intrinsics.h" && cp -f "/usr/local/cuda-10.0/include/sm_61_intrinsics.hpp" "$(@D)/cuda/include/sm_61_intrinsics.hpp" && cp -f "/usr/local/cuda-10.0/include/sobol_direction_vectors.h" "$(@D)/cuda/include/sobol_direction_vectors.h" && cp -f "/usr/local/cuda-10.0/include/surface_functions.h" "$(@D)/cuda/include/surface_functions.h" && cp -f "/usr/local/cuda-10.0/include/surface_functions.hpp" "$(@D)/cuda/include/surface_functions.hpp" && cp -f "/usr/local/cuda-10.0/include/surface_indirect_functions.h" "$(@D)/cuda/include/surface_indirect_functions.h" && cp -f "/usr/local/cuda-10.0/include/surface_indirect_functions.hpp" "$(@D)/cuda/include/surface_indirect_functions.hpp" && cp -f "/usr/local/cuda-10.0/include/surface_types.h" "$(@D)/cuda/include/surface_types.h" && cp -f "/usr/local/cuda-10.0/include/texture_fetch_functions.h" "$(@D)/cuda/include/texture_fetch_functions.h" && cp -f "/usr/local/cuda-10.0/include/texture_fetch_functions.hpp" "$(@D)/cuda/include/texture_fetch_functions.hpp" && cp -f "/usr/local/cuda-10.0/include/texture_indirect_functions.h" "$(@D)/cuda/include/texture_indirect_functions.h" && cp -f "/usr/local/cuda-10.0/include/texture_indirect_functions.hpp" "$(@D)/cuda/include/texture_indirect_functions.hpp" && cp -f "/usr/local/cuda-10.0/include/texture_types.h" "$(@D)/cuda/include/texture_types.h" && cp -f "/usr/local/cuda-10.0/include/thrust/adjacent_difference.h" "$(@D)/cuda/include/thrust/adjacent_difference.h" && cp -f "/usr/local/cuda-10.0/include/thrust/advance.h" "$(@D)/cuda/include/thrust/advance.h" && cp -f "/usr/local/cuda-10.0/include/thrust/binary_search.h" "$(@D)/cuda/include/thrust/binary_search.h" && cp -f "/usr/local/cuda-10.0/include/thrust/complex.h" "$(@D)/cuda/include/thrust/complex.h" && cp -f "/usr/local/cuda-10.0/include/thrust/copy.h" "$(@D)/cuda/include/thrust/copy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/count.h" "$(@D)/cuda/include/thrust/count.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/adjacent_difference.inl" "$(@D)/cuda/include/thrust/detail/adjacent_difference.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/advance.inl" "$(@D)/cuda/include/thrust/detail/advance.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/alignment.h" "$(@D)/cuda/include/thrust/detail/alignment.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/allocator/allocator_traits.h" "$(@D)/cuda/include/thrust/detail/allocator/allocator_traits.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/allocator/allocator_traits.inl" "$(@D)/cuda/include/thrust/detail/allocator/allocator_traits.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/allocator/copy_construct_range.h" "$(@D)/cuda/include/thrust/detail/allocator/copy_construct_range.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/allocator/copy_construct_range.inl" "$(@D)/cuda/include/thrust/detail/allocator/copy_construct_range.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/allocator/default_construct_range.h" "$(@D)/cuda/include/thrust/detail/allocator/default_construct_range.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/allocator/default_construct_range.inl" "$(@D)/cuda/include/thrust/detail/allocator/default_construct_range.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/allocator/destroy_range.h" "$(@D)/cuda/include/thrust/detail/allocator/destroy_range.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/allocator/destroy_range.inl" "$(@D)/cuda/include/thrust/detail/allocator/destroy_range.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/allocator/fill_construct_range.h" "$(@D)/cuda/include/thrust/detail/allocator/fill_construct_range.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/allocator/fill_construct_range.inl" "$(@D)/cuda/include/thrust/detail/allocator/fill_construct_range.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/allocator/malloc_allocator.h" "$(@D)/cuda/include/thrust/detail/allocator/malloc_allocator.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/allocator/malloc_allocator.inl" "$(@D)/cuda/include/thrust/detail/allocator/malloc_allocator.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/allocator/no_throw_allocator.h" "$(@D)/cuda/include/thrust/detail/allocator/no_throw_allocator.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/allocator/tagged_allocator.h" "$(@D)/cuda/include/thrust/detail/allocator/tagged_allocator.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/allocator/tagged_allocator.inl" "$(@D)/cuda/include/thrust/detail/allocator/tagged_allocator.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/allocator/temporary_allocator.h" "$(@D)/cuda/include/thrust/detail/allocator/temporary_allocator.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/allocator/temporary_allocator.inl" "$(@D)/cuda/include/thrust/detail/allocator/temporary_allocator.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/binary_search.inl" "$(@D)/cuda/include/thrust/detail/binary_search.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/complex/arithmetic.h" "$(@D)/cuda/include/thrust/detail/complex/arithmetic.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/complex/c99math.h" "$(@D)/cuda/include/thrust/detail/complex/c99math.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/complex/catrig.h" "$(@D)/cuda/include/thrust/detail/complex/catrig.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/complex/catrigf.h" "$(@D)/cuda/include/thrust/detail/complex/catrigf.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/complex/ccosh.h" "$(@D)/cuda/include/thrust/detail/complex/ccosh.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/complex/ccoshf.h" "$(@D)/cuda/include/thrust/detail/complex/ccoshf.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/complex/cexp.h" "$(@D)/cuda/include/thrust/detail/complex/cexp.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/complex/cexpf.h" "$(@D)/cuda/include/thrust/detail/complex/cexpf.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/complex/clog.h" "$(@D)/cuda/include/thrust/detail/complex/clog.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/complex/clogf.h" "$(@D)/cuda/include/thrust/detail/complex/clogf.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/complex/complex.inl" "$(@D)/cuda/include/thrust/detail/complex/complex.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/complex/cpow.h" "$(@D)/cuda/include/thrust/detail/complex/cpow.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/complex/cproj.h" "$(@D)/cuda/include/thrust/detail/complex/cproj.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/complex/csinh.h" "$(@D)/cuda/include/thrust/detail/complex/csinh.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/complex/csinhf.h" "$(@D)/cuda/include/thrust/detail/complex/csinhf.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/complex/csqrt.h" "$(@D)/cuda/include/thrust/detail/complex/csqrt.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/complex/csqrtf.h" "$(@D)/cuda/include/thrust/detail/complex/csqrtf.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/complex/ctanh.h" "$(@D)/cuda/include/thrust/detail/complex/ctanh.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/complex/ctanhf.h" "$(@D)/cuda/include/thrust/detail/complex/ctanhf.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/complex/math_private.h" "$(@D)/cuda/include/thrust/detail/complex/math_private.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/complex/stream.h" "$(@D)/cuda/include/thrust/detail/complex/stream.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/config.h" "$(@D)/cuda/include/thrust/detail/config.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/config/compiler.h" "$(@D)/cuda/include/thrust/detail/config/compiler.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/config/compiler_fence.h" "$(@D)/cuda/include/thrust/detail/config/compiler_fence.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/config/config.h" "$(@D)/cuda/include/thrust/detail/config/config.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/config/debug.h" "$(@D)/cuda/include/thrust/detail/config/debug.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/config/device_system.h" "$(@D)/cuda/include/thrust/detail/config/device_system.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/config/exec_check_disable.h" "$(@D)/cuda/include/thrust/detail/config/exec_check_disable.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/config/forceinline.h" "$(@D)/cuda/include/thrust/detail/config/forceinline.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/config/global_workarounds.h" "$(@D)/cuda/include/thrust/detail/config/global_workarounds.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/config/host_device.h" "$(@D)/cuda/include/thrust/detail/config/host_device.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/config/host_system.h" "$(@D)/cuda/include/thrust/detail/config/host_system.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/config/simple_defines.h" "$(@D)/cuda/include/thrust/detail/config/simple_defines.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/contiguous_storage.h" "$(@D)/cuda/include/thrust/detail/contiguous_storage.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/contiguous_storage.inl" "$(@D)/cuda/include/thrust/detail/contiguous_storage.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/copy.h" "$(@D)/cuda/include/thrust/detail/copy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/copy.inl" "$(@D)/cuda/include/thrust/detail/copy.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/copy_if.h" "$(@D)/cuda/include/thrust/detail/copy_if.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/copy_if.inl" "$(@D)/cuda/include/thrust/detail/copy_if.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/count.inl" "$(@D)/cuda/include/thrust/detail/count.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/cstdint.h" "$(@D)/cuda/include/thrust/detail/cstdint.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/device_delete.inl" "$(@D)/cuda/include/thrust/detail/device_delete.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/device_free.inl" "$(@D)/cuda/include/thrust/detail/device_free.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/device_malloc.inl" "$(@D)/cuda/include/thrust/detail/device_malloc.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/device_new.inl" "$(@D)/cuda/include/thrust/detail/device_new.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/device_ptr.inl" "$(@D)/cuda/include/thrust/detail/device_ptr.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/device_reference.inl" "$(@D)/cuda/include/thrust/detail/device_reference.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/device_vector.inl" "$(@D)/cuda/include/thrust/detail/device_vector.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/dispatch/is_trivial_copy.h" "$(@D)/cuda/include/thrust/detail/dispatch/is_trivial_copy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/distance.inl" "$(@D)/cuda/include/thrust/detail/distance.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/equal.inl" "$(@D)/cuda/include/thrust/detail/equal.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/execute_with_allocator.h" "$(@D)/cuda/include/thrust/detail/execute_with_allocator.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/execution_policy.h" "$(@D)/cuda/include/thrust/detail/execution_policy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/extrema.inl" "$(@D)/cuda/include/thrust/detail/extrema.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/fill.inl" "$(@D)/cuda/include/thrust/detail/fill.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/find.inl" "$(@D)/cuda/include/thrust/detail/find.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/for_each.inl" "$(@D)/cuda/include/thrust/detail/for_each.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/function.h" "$(@D)/cuda/include/thrust/detail/function.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/functional.inl" "$(@D)/cuda/include/thrust/detail/functional.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/functional/actor.h" "$(@D)/cuda/include/thrust/detail/functional/actor.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/functional/actor.inl" "$(@D)/cuda/include/thrust/detail/functional/actor.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/functional/argument.h" "$(@D)/cuda/include/thrust/detail/functional/argument.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/functional/composite.h" "$(@D)/cuda/include/thrust/detail/functional/composite.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/functional/operators.h" "$(@D)/cuda/include/thrust/detail/functional/operators.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/functional/operators/arithmetic_operators.h" "$(@D)/cuda/include/thrust/detail/functional/operators/arithmetic_operators.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/functional/operators/assignment_operator.h" "$(@D)/cuda/include/thrust/detail/functional/operators/assignment_operator.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/functional/operators/bitwise_operators.h" "$(@D)/cuda/include/thrust/detail/functional/operators/bitwise_operators.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/functional/operators/compound_assignment_operators.h" "$(@D)/cuda/include/thrust/detail/functional/operators/compound_assignment_operators.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/functional/operators/logical_operators.h" "$(@D)/cuda/include/thrust/detail/functional/operators/logical_operators.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/functional/operators/operator_adaptors.h" "$(@D)/cuda/include/thrust/detail/functional/operators/operator_adaptors.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/functional/operators/relational_operators.h" "$(@D)/cuda/include/thrust/detail/functional/operators/relational_operators.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/functional/placeholder.h" "$(@D)/cuda/include/thrust/detail/functional/placeholder.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/functional/value.h" "$(@D)/cuda/include/thrust/detail/functional/value.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/gather.inl" "$(@D)/cuda/include/thrust/detail/gather.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/generate.inl" "$(@D)/cuda/include/thrust/detail/generate.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/get_iterator_value.h" "$(@D)/cuda/include/thrust/detail/get_iterator_value.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/host_vector.inl" "$(@D)/cuda/include/thrust/detail/host_vector.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/inner_product.inl" "$(@D)/cuda/include/thrust/detail/inner_product.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/integer_math.h" "$(@D)/cuda/include/thrust/detail/integer_math.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/integer_traits.h" "$(@D)/cuda/include/thrust/detail/integer_traits.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/internal_functional.h" "$(@D)/cuda/include/thrust/detail/internal_functional.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/logical.inl" "$(@D)/cuda/include/thrust/detail/logical.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/malloc_and_free.h" "$(@D)/cuda/include/thrust/detail/malloc_and_free.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/merge.inl" "$(@D)/cuda/include/thrust/detail/merge.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/minmax.h" "$(@D)/cuda/include/thrust/detail/minmax.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/mismatch.inl" "$(@D)/cuda/include/thrust/detail/mismatch.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/mpl/math.h" "$(@D)/cuda/include/thrust/detail/mpl/math.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/numeric_traits.h" "$(@D)/cuda/include/thrust/detail/numeric_traits.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/overlapped_copy.h" "$(@D)/cuda/include/thrust/detail/overlapped_copy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/pair.inl" "$(@D)/cuda/include/thrust/detail/pair.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/partition.inl" "$(@D)/cuda/include/thrust/detail/partition.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/pointer.h" "$(@D)/cuda/include/thrust/detail/pointer.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/pointer.inl" "$(@D)/cuda/include/thrust/detail/pointer.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/preprocessor.h" "$(@D)/cuda/include/thrust/detail/preprocessor.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/range/head_flags.h" "$(@D)/cuda/include/thrust/detail/range/head_flags.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/range/tail_flags.h" "$(@D)/cuda/include/thrust/detail/range/tail_flags.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/raw_pointer_cast.h" "$(@D)/cuda/include/thrust/detail/raw_pointer_cast.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/raw_reference_cast.h" "$(@D)/cuda/include/thrust/detail/raw_reference_cast.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/reduce.inl" "$(@D)/cuda/include/thrust/detail/reduce.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/reference.h" "$(@D)/cuda/include/thrust/detail/reference.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/reference.inl" "$(@D)/cuda/include/thrust/detail/reference.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/reference_forward_declaration.h" "$(@D)/cuda/include/thrust/detail/reference_forward_declaration.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/remove.inl" "$(@D)/cuda/include/thrust/detail/remove.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/replace.inl" "$(@D)/cuda/include/thrust/detail/replace.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/reverse.inl" "$(@D)/cuda/include/thrust/detail/reverse.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/scan.inl" "$(@D)/cuda/include/thrust/detail/scan.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/scatter.inl" "$(@D)/cuda/include/thrust/detail/scatter.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/seq.h" "$(@D)/cuda/include/thrust/detail/seq.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/sequence.inl" "$(@D)/cuda/include/thrust/detail/sequence.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/set_operations.inl" "$(@D)/cuda/include/thrust/detail/set_operations.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/sort.inl" "$(@D)/cuda/include/thrust/detail/sort.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/static_assert.h" "$(@D)/cuda/include/thrust/detail/static_assert.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/static_map.h" "$(@D)/cuda/include/thrust/detail/static_map.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/swap.h" "$(@D)/cuda/include/thrust/detail/swap.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/swap.inl" "$(@D)/cuda/include/thrust/detail/swap.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/swap_ranges.inl" "$(@D)/cuda/include/thrust/detail/swap_ranges.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/tabulate.inl" "$(@D)/cuda/include/thrust/detail/tabulate.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/temporary_array.h" "$(@D)/cuda/include/thrust/detail/temporary_array.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/temporary_array.inl" "$(@D)/cuda/include/thrust/detail/temporary_array.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/temporary_buffer.h" "$(@D)/cuda/include/thrust/detail/temporary_buffer.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/transform.inl" "$(@D)/cuda/include/thrust/detail/transform.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/transform_reduce.inl" "$(@D)/cuda/include/thrust/detail/transform_reduce.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/transform_scan.inl" "$(@D)/cuda/include/thrust/detail/transform_scan.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/trivial_sequence.h" "$(@D)/cuda/include/thrust/detail/trivial_sequence.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/tuple.inl" "$(@D)/cuda/include/thrust/detail/tuple.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/tuple_meta_transform.h" "$(@D)/cuda/include/thrust/detail/tuple_meta_transform.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/tuple_transform.h" "$(@D)/cuda/include/thrust/detail/tuple_transform.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/type_traits.h" "$(@D)/cuda/include/thrust/detail/type_traits.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/type_traits/algorithm/intermediate_type_from_function_and_iterators.h" "$(@D)/cuda/include/thrust/detail/type_traits/algorithm/intermediate_type_from_function_and_iterators.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/type_traits/function_traits.h" "$(@D)/cuda/include/thrust/detail/type_traits/function_traits.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/type_traits/has_member_function.h" "$(@D)/cuda/include/thrust/detail/type_traits/has_member_function.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/type_traits/has_nested_type.h" "$(@D)/cuda/include/thrust/detail/type_traits/has_nested_type.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/type_traits/has_trivial_assign.h" "$(@D)/cuda/include/thrust/detail/type_traits/has_trivial_assign.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/type_traits/is_call_possible.h" "$(@D)/cuda/include/thrust/detail/type_traits/is_call_possible.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/type_traits/is_metafunction_defined.h" "$(@D)/cuda/include/thrust/detail/type_traits/is_metafunction_defined.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/type_traits/iterator/is_discard_iterator.h" "$(@D)/cuda/include/thrust/detail/type_traits/iterator/is_discard_iterator.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/type_traits/iterator/is_output_iterator.h" "$(@D)/cuda/include/thrust/detail/type_traits/iterator/is_output_iterator.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/type_traits/minimum_type.h" "$(@D)/cuda/include/thrust/detail/type_traits/minimum_type.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/type_traits/pointer_traits.h" "$(@D)/cuda/include/thrust/detail/type_traits/pointer_traits.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/type_traits/result_of_adaptable_function.h" "$(@D)/cuda/include/thrust/detail/type_traits/result_of_adaptable_function.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/uninitialized_copy.inl" "$(@D)/cuda/include/thrust/detail/uninitialized_copy.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/uninitialized_fill.inl" "$(@D)/cuda/include/thrust/detail/uninitialized_fill.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/unique.inl" "$(@D)/cuda/include/thrust/detail/unique.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/use_default.h" "$(@D)/cuda/include/thrust/detail/use_default.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/util/align.h" "$(@D)/cuda/include/thrust/detail/util/align.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/util/blocking.h" "$(@D)/cuda/include/thrust/detail/util/blocking.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/vector_base.h" "$(@D)/cuda/include/thrust/detail/vector_base.h" && cp -f "/usr/local/cuda-10.0/include/thrust/detail/vector_base.inl" "$(@D)/cuda/include/thrust/detail/vector_base.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/device_allocator.h" "$(@D)/cuda/include/thrust/device_allocator.h" && cp -f "/usr/local/cuda-10.0/include/thrust/device_delete.h" "$(@D)/cuda/include/thrust/device_delete.h" && cp -f "/usr/local/cuda-10.0/include/thrust/device_free.h" "$(@D)/cuda/include/thrust/device_free.h" && cp -f "/usr/local/cuda-10.0/include/thrust/device_malloc.h" "$(@D)/cuda/include/thrust/device_malloc.h" && cp -f "/usr/local/cuda-10.0/include/thrust/device_malloc_allocator.h" "$(@D)/cuda/include/thrust/device_malloc_allocator.h" && cp -f "/usr/local/cuda-10.0/include/thrust/device_new.h" "$(@D)/cuda/include/thrust/device_new.h" && cp -f "/usr/local/cuda-10.0/include/thrust/device_new_allocator.h" "$(@D)/cuda/include/thrust/device_new_allocator.h" && cp -f "/usr/local/cuda-10.0/include/thrust/device_ptr.h" "$(@D)/cuda/include/thrust/device_ptr.h" && cp -f "/usr/local/cuda-10.0/include/thrust/device_reference.h" "$(@D)/cuda/include/thrust/device_reference.h" && cp -f "/usr/local/cuda-10.0/include/thrust/device_vector.h" "$(@D)/cuda/include/thrust/device_vector.h" && cp -f "/usr/local/cuda-10.0/include/thrust/distance.h" "$(@D)/cuda/include/thrust/distance.h" && cp -f "/usr/local/cuda-10.0/include/thrust/equal.h" "$(@D)/cuda/include/thrust/equal.h" && cp -f "/usr/local/cuda-10.0/include/thrust/execution_policy.h" "$(@D)/cuda/include/thrust/execution_policy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/extrema.h" "$(@D)/cuda/include/thrust/extrema.h" && cp -f "/usr/local/cuda-10.0/include/thrust/fill.h" "$(@D)/cuda/include/thrust/fill.h" && cp -f "/usr/local/cuda-10.0/include/thrust/find.h" "$(@D)/cuda/include/thrust/find.h" && cp -f "/usr/local/cuda-10.0/include/thrust/for_each.h" "$(@D)/cuda/include/thrust/for_each.h" && cp -f "/usr/local/cuda-10.0/include/thrust/functional.h" "$(@D)/cuda/include/thrust/functional.h" && cp -f "/usr/local/cuda-10.0/include/thrust/gather.h" "$(@D)/cuda/include/thrust/gather.h" && cp -f "/usr/local/cuda-10.0/include/thrust/generate.h" "$(@D)/cuda/include/thrust/generate.h" && cp -f "/usr/local/cuda-10.0/include/thrust/host_vector.h" "$(@D)/cuda/include/thrust/host_vector.h" && cp -f "/usr/local/cuda-10.0/include/thrust/inner_product.h" "$(@D)/cuda/include/thrust/inner_product.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/constant_iterator.h" "$(@D)/cuda/include/thrust/iterator/constant_iterator.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/counting_iterator.h" "$(@D)/cuda/include/thrust/iterator/counting_iterator.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/any_assign.h" "$(@D)/cuda/include/thrust/iterator/detail/any_assign.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/any_system_tag.h" "$(@D)/cuda/include/thrust/iterator/detail/any_system_tag.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/constant_iterator_base.h" "$(@D)/cuda/include/thrust/iterator/detail/constant_iterator_base.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/counting_iterator.inl" "$(@D)/cuda/include/thrust/iterator/detail/counting_iterator.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/device_system_tag.h" "$(@D)/cuda/include/thrust/iterator/detail/device_system_tag.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/discard_iterator_base.h" "$(@D)/cuda/include/thrust/iterator/detail/discard_iterator_base.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/distance_from_result.h" "$(@D)/cuda/include/thrust/iterator/detail/distance_from_result.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/host_system_tag.h" "$(@D)/cuda/include/thrust/iterator/detail/host_system_tag.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/is_iterator_category.h" "$(@D)/cuda/include/thrust/iterator/detail/is_iterator_category.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/is_trivial_iterator.h" "$(@D)/cuda/include/thrust/iterator/detail/is_trivial_iterator.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/iterator_adaptor_base.h" "$(@D)/cuda/include/thrust/iterator/detail/iterator_adaptor_base.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/iterator_category_to_system.h" "$(@D)/cuda/include/thrust/iterator/detail/iterator_category_to_system.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/iterator_category_to_traversal.h" "$(@D)/cuda/include/thrust/iterator/detail/iterator_category_to_traversal.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/iterator_category_with_system_and_traversal.h" "$(@D)/cuda/include/thrust/iterator/detail/iterator_category_with_system_and_traversal.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/iterator_facade_category.h" "$(@D)/cuda/include/thrust/iterator/detail/iterator_facade_category.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/iterator_traits.inl" "$(@D)/cuda/include/thrust/iterator/detail/iterator_traits.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/iterator_traversal_tags.h" "$(@D)/cuda/include/thrust/iterator/detail/iterator_traversal_tags.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/join_iterator.h" "$(@D)/cuda/include/thrust/iterator/detail/join_iterator.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/minimum_category.h" "$(@D)/cuda/include/thrust/iterator/detail/minimum_category.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/minimum_system.h" "$(@D)/cuda/include/thrust/iterator/detail/minimum_system.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/normal_iterator.h" "$(@D)/cuda/include/thrust/iterator/detail/normal_iterator.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/permutation_iterator_base.h" "$(@D)/cuda/include/thrust/iterator/detail/permutation_iterator_base.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/retag.h" "$(@D)/cuda/include/thrust/iterator/detail/retag.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/reverse_iterator.inl" "$(@D)/cuda/include/thrust/iterator/detail/reverse_iterator.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/reverse_iterator_base.h" "$(@D)/cuda/include/thrust/iterator/detail/reverse_iterator_base.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/tagged_iterator.h" "$(@D)/cuda/include/thrust/iterator/detail/tagged_iterator.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/transform_iterator.inl" "$(@D)/cuda/include/thrust/iterator/detail/transform_iterator.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/transform_output_iterator.inl" "$(@D)/cuda/include/thrust/iterator/detail/transform_output_iterator.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/tuple_of_iterator_references.h" "$(@D)/cuda/include/thrust/iterator/detail/tuple_of_iterator_references.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/universal_categories.h" "$(@D)/cuda/include/thrust/iterator/detail/universal_categories.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/zip_iterator.inl" "$(@D)/cuda/include/thrust/iterator/detail/zip_iterator.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/detail/zip_iterator_base.h" "$(@D)/cuda/include/thrust/iterator/detail/zip_iterator_base.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/discard_iterator.h" "$(@D)/cuda/include/thrust/iterator/discard_iterator.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/iterator_adaptor.h" "$(@D)/cuda/include/thrust/iterator/iterator_adaptor.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/iterator_categories.h" "$(@D)/cuda/include/thrust/iterator/iterator_categories.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/iterator_facade.h" "$(@D)/cuda/include/thrust/iterator/iterator_facade.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/iterator_traits.h" "$(@D)/cuda/include/thrust/iterator/iterator_traits.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/permutation_iterator.h" "$(@D)/cuda/include/thrust/iterator/permutation_iterator.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/retag.h" "$(@D)/cuda/include/thrust/iterator/retag.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/reverse_iterator.h" "$(@D)/cuda/include/thrust/iterator/reverse_iterator.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/transform_iterator.h" "$(@D)/cuda/include/thrust/iterator/transform_iterator.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/transform_output_iterator.h" "$(@D)/cuda/include/thrust/iterator/transform_output_iterator.h" && cp -f "/usr/local/cuda-10.0/include/thrust/iterator/zip_iterator.h" "$(@D)/cuda/include/thrust/iterator/zip_iterator.h" && cp -f "/usr/local/cuda-10.0/include/thrust/logical.h" "$(@D)/cuda/include/thrust/logical.h" && cp -f "/usr/local/cuda-10.0/include/thrust/memory.h" "$(@D)/cuda/include/thrust/memory.h" && cp -f "/usr/local/cuda-10.0/include/thrust/merge.h" "$(@D)/cuda/include/thrust/merge.h" && cp -f "/usr/local/cuda-10.0/include/thrust/mismatch.h" "$(@D)/cuda/include/thrust/mismatch.h" && cp -f "/usr/local/cuda-10.0/include/thrust/pair.h" "$(@D)/cuda/include/thrust/pair.h" && cp -f "/usr/local/cuda-10.0/include/thrust/partition.h" "$(@D)/cuda/include/thrust/partition.h" && cp -f "/usr/local/cuda-10.0/include/thrust/random.h" "$(@D)/cuda/include/thrust/random.h" && cp -f "/usr/local/cuda-10.0/include/thrust/random/detail/discard_block_engine.inl" "$(@D)/cuda/include/thrust/random/detail/discard_block_engine.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/random/detail/linear_congruential_engine.inl" "$(@D)/cuda/include/thrust/random/detail/linear_congruential_engine.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/random/detail/linear_congruential_engine_discard.h" "$(@D)/cuda/include/thrust/random/detail/linear_congruential_engine_discard.h" && cp -f "/usr/local/cuda-10.0/include/thrust/random/detail/linear_feedback_shift_engine.inl" "$(@D)/cuda/include/thrust/random/detail/linear_feedback_shift_engine.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/random/detail/linear_feedback_shift_engine_wordmask.h" "$(@D)/cuda/include/thrust/random/detail/linear_feedback_shift_engine_wordmask.h" && cp -f "/usr/local/cuda-10.0/include/thrust/random/detail/mod.h" "$(@D)/cuda/include/thrust/random/detail/mod.h" && cp -f "/usr/local/cuda-10.0/include/thrust/random/detail/normal_distribution.inl" "$(@D)/cuda/include/thrust/random/detail/normal_distribution.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/random/detail/normal_distribution_base.h" "$(@D)/cuda/include/thrust/random/detail/normal_distribution_base.h" && cp -f "/usr/local/cuda-10.0/include/thrust/random/detail/random_core_access.h" "$(@D)/cuda/include/thrust/random/detail/random_core_access.h" && cp -f "/usr/local/cuda-10.0/include/thrust/random/detail/subtract_with_carry_engine.inl" "$(@D)/cuda/include/thrust/random/detail/subtract_with_carry_engine.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/random/detail/uniform_int_distribution.inl" "$(@D)/cuda/include/thrust/random/detail/uniform_int_distribution.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/random/detail/uniform_real_distribution.inl" "$(@D)/cuda/include/thrust/random/detail/uniform_real_distribution.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/random/detail/xor_combine_engine.inl" "$(@D)/cuda/include/thrust/random/detail/xor_combine_engine.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/random/detail/xor_combine_engine_max.h" "$(@D)/cuda/include/thrust/random/detail/xor_combine_engine_max.h" && cp -f "/usr/local/cuda-10.0/include/thrust/random/discard_block_engine.h" "$(@D)/cuda/include/thrust/random/discard_block_engine.h" && cp -f "/usr/local/cuda-10.0/include/thrust/random/linear_congruential_engine.h" "$(@D)/cuda/include/thrust/random/linear_congruential_engine.h" && cp -f "/usr/local/cuda-10.0/include/thrust/random/linear_feedback_shift_engine.h" "$(@D)/cuda/include/thrust/random/linear_feedback_shift_engine.h" && cp -f "/usr/local/cuda-10.0/include/thrust/random/normal_distribution.h" "$(@D)/cuda/include/thrust/random/normal_distribution.h" && cp -f "/usr/local/cuda-10.0/include/thrust/random/subtract_with_carry_engine.h" "$(@D)/cuda/include/thrust/random/subtract_with_carry_engine.h" && cp -f "/usr/local/cuda-10.0/include/thrust/random/uniform_int_distribution.h" "$(@D)/cuda/include/thrust/random/uniform_int_distribution.h" && cp -f "/usr/local/cuda-10.0/include/thrust/random/uniform_real_distribution.h" "$(@D)/cuda/include/thrust/random/uniform_real_distribution.h" && cp -f "/usr/local/cuda-10.0/include/thrust/random/xor_combine_engine.h" "$(@D)/cuda/include/thrust/random/xor_combine_engine.h" && cp -f "/usr/local/cuda-10.0/include/thrust/reduce.h" "$(@D)/cuda/include/thrust/reduce.h" && cp -f "/usr/local/cuda-10.0/include/thrust/remove.h" "$(@D)/cuda/include/thrust/remove.h" && cp -f "/usr/local/cuda-10.0/include/thrust/replace.h" "$(@D)/cuda/include/thrust/replace.h" && cp -f "/usr/local/cuda-10.0/include/thrust/reverse.h" "$(@D)/cuda/include/thrust/reverse.h" && cp -f "/usr/local/cuda-10.0/include/thrust/scan.h" "$(@D)/cuda/include/thrust/scan.h" && cp -f "/usr/local/cuda-10.0/include/thrust/scatter.h" "$(@D)/cuda/include/thrust/scatter.h" && cp -f "/usr/local/cuda-10.0/include/thrust/sequence.h" "$(@D)/cuda/include/thrust/sequence.h" && cp -f "/usr/local/cuda-10.0/include/thrust/set_operations.h" "$(@D)/cuda/include/thrust/set_operations.h" && cp -f "/usr/local/cuda-10.0/include/thrust/sort.h" "$(@D)/cuda/include/thrust/sort.h" && cp -f "/usr/local/cuda-10.0/include/thrust/swap.h" "$(@D)/cuda/include/thrust/swap.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/adjacent_difference.h" "$(@D)/cuda/include/thrust/system/cpp/detail/adjacent_difference.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/assign_value.h" "$(@D)/cuda/include/thrust/system/cpp/detail/assign_value.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/binary_search.h" "$(@D)/cuda/include/thrust/system/cpp/detail/binary_search.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/copy.h" "$(@D)/cuda/include/thrust/system/cpp/detail/copy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/copy_if.h" "$(@D)/cuda/include/thrust/system/cpp/detail/copy_if.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/count.h" "$(@D)/cuda/include/thrust/system/cpp/detail/count.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/equal.h" "$(@D)/cuda/include/thrust/system/cpp/detail/equal.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/execution_policy.h" "$(@D)/cuda/include/thrust/system/cpp/detail/execution_policy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/extrema.h" "$(@D)/cuda/include/thrust/system/cpp/detail/extrema.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/fill.h" "$(@D)/cuda/include/thrust/system/cpp/detail/fill.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/find.h" "$(@D)/cuda/include/thrust/system/cpp/detail/find.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/for_each.h" "$(@D)/cuda/include/thrust/system/cpp/detail/for_each.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/gather.h" "$(@D)/cuda/include/thrust/system/cpp/detail/gather.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/generate.h" "$(@D)/cuda/include/thrust/system/cpp/detail/generate.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/get_value.h" "$(@D)/cuda/include/thrust/system/cpp/detail/get_value.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/inner_product.h" "$(@D)/cuda/include/thrust/system/cpp/detail/inner_product.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/iter_swap.h" "$(@D)/cuda/include/thrust/system/cpp/detail/iter_swap.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/logical.h" "$(@D)/cuda/include/thrust/system/cpp/detail/logical.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/malloc_and_free.h" "$(@D)/cuda/include/thrust/system/cpp/detail/malloc_and_free.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/memory.inl" "$(@D)/cuda/include/thrust/system/cpp/detail/memory.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/merge.h" "$(@D)/cuda/include/thrust/system/cpp/detail/merge.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/mismatch.h" "$(@D)/cuda/include/thrust/system/cpp/detail/mismatch.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/par.h" "$(@D)/cuda/include/thrust/system/cpp/detail/par.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/partition.h" "$(@D)/cuda/include/thrust/system/cpp/detail/partition.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/reduce.h" "$(@D)/cuda/include/thrust/system/cpp/detail/reduce.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/reduce_by_key.h" "$(@D)/cuda/include/thrust/system/cpp/detail/reduce_by_key.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/remove.h" "$(@D)/cuda/include/thrust/system/cpp/detail/remove.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/replace.h" "$(@D)/cuda/include/thrust/system/cpp/detail/replace.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/reverse.h" "$(@D)/cuda/include/thrust/system/cpp/detail/reverse.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/scan.h" "$(@D)/cuda/include/thrust/system/cpp/detail/scan.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/scan_by_key.h" "$(@D)/cuda/include/thrust/system/cpp/detail/scan_by_key.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/scatter.h" "$(@D)/cuda/include/thrust/system/cpp/detail/scatter.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/sequence.h" "$(@D)/cuda/include/thrust/system/cpp/detail/sequence.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/set_operations.h" "$(@D)/cuda/include/thrust/system/cpp/detail/set_operations.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/sort.h" "$(@D)/cuda/include/thrust/system/cpp/detail/sort.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/swap_ranges.h" "$(@D)/cuda/include/thrust/system/cpp/detail/swap_ranges.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/tabulate.h" "$(@D)/cuda/include/thrust/system/cpp/detail/tabulate.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/temporary_buffer.h" "$(@D)/cuda/include/thrust/system/cpp/detail/temporary_buffer.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/transform.h" "$(@D)/cuda/include/thrust/system/cpp/detail/transform.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/transform_reduce.h" "$(@D)/cuda/include/thrust/system/cpp/detail/transform_reduce.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/transform_scan.h" "$(@D)/cuda/include/thrust/system/cpp/detail/transform_scan.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/uninitialized_copy.h" "$(@D)/cuda/include/thrust/system/cpp/detail/uninitialized_copy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/uninitialized_fill.h" "$(@D)/cuda/include/thrust/system/cpp/detail/uninitialized_fill.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/unique.h" "$(@D)/cuda/include/thrust/system/cpp/detail/unique.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/unique_by_key.h" "$(@D)/cuda/include/thrust/system/cpp/detail/unique_by_key.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/detail/vector.inl" "$(@D)/cuda/include/thrust/system/cpp/detail/vector.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/execution_policy.h" "$(@D)/cuda/include/thrust/system/cpp/execution_policy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/memory.h" "$(@D)/cuda/include/thrust/system/cpp/memory.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cpp/vector.h" "$(@D)/cuda/include/thrust/system/cpp/vector.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/config.h" "$(@D)/cuda/include/thrust/system/cuda/config.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/adjacent_difference.h" "$(@D)/cuda/include/thrust/system/cuda/detail/adjacent_difference.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/assign_value.h" "$(@D)/cuda/include/thrust/system/cuda/detail/assign_value.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/binary_search.h" "$(@D)/cuda/include/thrust/system/cuda/detail/binary_search.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/copy.h" "$(@D)/cuda/include/thrust/system/cuda/detail/copy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/copy_if.h" "$(@D)/cuda/include/thrust/system/cuda/detail/copy_if.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/core/agent_launcher.h" "$(@D)/cuda/include/thrust/system/cuda/detail/core/agent_launcher.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/core/alignment.h" "$(@D)/cuda/include/thrust/system/cuda/detail/core/alignment.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/core/triple_chevron_launch.h" "$(@D)/cuda/include/thrust/system/cuda/detail/core/triple_chevron_launch.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/core/util.h" "$(@D)/cuda/include/thrust/system/cuda/detail/core/util.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/count.h" "$(@D)/cuda/include/thrust/system/cuda/detail/count.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cross_system.h" "$(@D)/cuda/include/thrust/system/cuda/detail/cross_system.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/agent/agent_histogram.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_histogram.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/agent/agent_radix_sort_downsweep.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_radix_sort_downsweep.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/agent/agent_radix_sort_upsweep.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_radix_sort_upsweep.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/agent/agent_reduce.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_reduce.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/agent/agent_reduce_by_key.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_reduce_by_key.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/agent/agent_rle.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_rle.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/agent/agent_scan.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_scan.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/agent/agent_segment_fixup.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_segment_fixup.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/agent/agent_select_if.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_select_if.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/agent/agent_spmv_orig.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_spmv_orig.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/agent/single_pass_scan_operators.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/single_pass_scan_operators.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/block/block_adjacent_difference.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_adjacent_difference.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/block/block_discontinuity.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_discontinuity.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/block/block_exchange.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_exchange.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/block/block_histogram.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_histogram.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/block/block_load.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_load.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/block/block_radix_rank.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_radix_rank.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/block/block_radix_sort.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_radix_sort.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/block/block_raking_layout.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_raking_layout.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/block/block_reduce.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_reduce.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/block/block_scan.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_scan.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/block/block_shuffle.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_shuffle.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/block/block_store.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_store.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/block/specializations/block_histogram_atomic.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_histogram_atomic.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/block/specializations/block_histogram_sort.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_histogram_sort.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/block/specializations/block_reduce_raking.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_reduce_raking.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/block/specializations/block_reduce_raking_commutative_only.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_reduce_raking_commutative_only.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/block/specializations/block_reduce_warp_reductions.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_reduce_warp_reductions.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_raking.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_raking.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_warp_scans.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_warp_scans.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_warp_scans2.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_warp_scans2.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_warp_scans3.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_warp_scans3.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/cub.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/cub.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/device/device_histogram.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_histogram.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/device/device_partition.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_partition.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/device/device_radix_sort.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_radix_sort.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/device/device_reduce.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_reduce.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/device/device_run_length_encode.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_run_length_encode.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/device/device_scan.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_scan.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/device/device_segmented_radix_sort.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_segmented_radix_sort.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/device/device_segmented_reduce.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_segmented_reduce.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/device/device_select.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_select.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/device/device_spmv.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_spmv.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_histogram.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_histogram.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_radix_sort.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_radix_sort.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_reduce.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_reduce.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_reduce_by_key.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_reduce_by_key.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_rle.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_rle.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_scan.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_scan.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_select_if.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_select_if.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_spmv_orig.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_spmv_orig.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/grid/grid_barrier.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/grid/grid_barrier.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/grid/grid_even_share.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/grid/grid_even_share.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/grid/grid_mapping.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/grid/grid_mapping.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/grid/grid_queue.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/grid/grid_queue.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/host/mutex.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/host/mutex.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/iterator/arg_index_input_iterator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/iterator/arg_index_input_iterator.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/iterator/cache_modified_input_iterator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/iterator/cache_modified_input_iterator.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/iterator/cache_modified_output_iterator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/iterator/cache_modified_output_iterator.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/iterator/constant_input_iterator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/iterator/constant_input_iterator.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/iterator/counting_input_iterator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/iterator/counting_input_iterator.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/iterator/discard_output_iterator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/iterator/discard_output_iterator.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/iterator/tex_obj_input_iterator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/iterator/tex_obj_input_iterator.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/iterator/tex_ref_input_iterator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/iterator/tex_ref_input_iterator.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/iterator/transform_input_iterator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/iterator/transform_input_iterator.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/thread/thread_load.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/thread/thread_load.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/thread/thread_operators.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/thread/thread_operators.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/thread/thread_reduce.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/thread/thread_reduce.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/thread/thread_scan.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/thread/thread_scan.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/thread/thread_search.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/thread/thread_search.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/thread/thread_store.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/thread/thread_store.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/util_allocator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/util_allocator.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/util_arch.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/util_arch.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/util_debug.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/util_debug.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/util_device.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/util_device.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/util_macro.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/util_macro.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/util_namespace.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/util_namespace.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/util_ptx.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/util_ptx.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/util_type.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/util_type.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/warp/specializations/warp_reduce_shfl.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/warp/specializations/warp_reduce_shfl.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/warp/specializations/warp_reduce_smem.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/warp/specializations/warp_reduce_smem.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/warp/specializations/warp_scan_shfl.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/warp/specializations/warp_scan_shfl.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/warp/specializations/warp_scan_smem.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/warp/specializations/warp_scan_smem.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/warp/warp_reduce.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/warp/warp_reduce.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/cub/warp/warp_scan.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/warp/warp_scan.cuh" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/equal.h" "$(@D)/cuda/include/thrust/system/cuda/detail/equal.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/error.inl" "$(@D)/cuda/include/thrust/system/cuda/detail/error.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/execution_policy.h" "$(@D)/cuda/include/thrust/system/cuda/detail/execution_policy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/extrema.h" "$(@D)/cuda/include/thrust/system/cuda/detail/extrema.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/fill.h" "$(@D)/cuda/include/thrust/system/cuda/detail/fill.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/find.h" "$(@D)/cuda/include/thrust/system/cuda/detail/find.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/for_each.h" "$(@D)/cuda/include/thrust/system/cuda/detail/for_each.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/gather.h" "$(@D)/cuda/include/thrust/system/cuda/detail/gather.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/generate.h" "$(@D)/cuda/include/thrust/system/cuda/detail/generate.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/get_value.h" "$(@D)/cuda/include/thrust/system/cuda/detail/get_value.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/guarded_cuda_runtime_api.h" "$(@D)/cuda/include/thrust/system/cuda/detail/guarded_cuda_runtime_api.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/guarded_driver_types.h" "$(@D)/cuda/include/thrust/system/cuda/detail/guarded_driver_types.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/inner_product.h" "$(@D)/cuda/include/thrust/system/cuda/detail/inner_product.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/internal/copy_cross_system.h" "$(@D)/cuda/include/thrust/system/cuda/detail/internal/copy_cross_system.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/internal/copy_device_to_device.h" "$(@D)/cuda/include/thrust/system/cuda/detail/internal/copy_device_to_device.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/iter_swap.h" "$(@D)/cuda/include/thrust/system/cuda/detail/iter_swap.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/logical.h" "$(@D)/cuda/include/thrust/system/cuda/detail/logical.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/malloc_and_free.h" "$(@D)/cuda/include/thrust/system/cuda/detail/malloc_and_free.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/memory.inl" "$(@D)/cuda/include/thrust/system/cuda/detail/memory.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/merge.h" "$(@D)/cuda/include/thrust/system/cuda/detail/merge.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/mismatch.h" "$(@D)/cuda/include/thrust/system/cuda/detail/mismatch.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/par.h" "$(@D)/cuda/include/thrust/system/cuda/detail/par.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/par_to_seq.h" "$(@D)/cuda/include/thrust/system/cuda/detail/par_to_seq.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/parallel_for.h" "$(@D)/cuda/include/thrust/system/cuda/detail/parallel_for.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/partition.h" "$(@D)/cuda/include/thrust/system/cuda/detail/partition.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/reduce.h" "$(@D)/cuda/include/thrust/system/cuda/detail/reduce.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/reduce_by_key.h" "$(@D)/cuda/include/thrust/system/cuda/detail/reduce_by_key.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/remove.h" "$(@D)/cuda/include/thrust/system/cuda/detail/remove.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/replace.h" "$(@D)/cuda/include/thrust/system/cuda/detail/replace.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/reverse.h" "$(@D)/cuda/include/thrust/system/cuda/detail/reverse.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/scan.h" "$(@D)/cuda/include/thrust/system/cuda/detail/scan.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/scan_by_key.h" "$(@D)/cuda/include/thrust/system/cuda/detail/scan_by_key.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/scatter.h" "$(@D)/cuda/include/thrust/system/cuda/detail/scatter.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/sequence.h" "$(@D)/cuda/include/thrust/system/cuda/detail/sequence.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/set_operations.h" "$(@D)/cuda/include/thrust/system/cuda/detail/set_operations.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/sort.h" "$(@D)/cuda/include/thrust/system/cuda/detail/sort.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/swap_ranges.h" "$(@D)/cuda/include/thrust/system/cuda/detail/swap_ranges.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/tabulate.h" "$(@D)/cuda/include/thrust/system/cuda/detail/tabulate.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/temporary_buffer.h" "$(@D)/cuda/include/thrust/system/cuda/detail/temporary_buffer.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/terminate.h" "$(@D)/cuda/include/thrust/system/cuda/detail/terminate.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/transform.h" "$(@D)/cuda/include/thrust/system/cuda/detail/transform.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/transform_reduce.h" "$(@D)/cuda/include/thrust/system/cuda/detail/transform_reduce.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/transform_scan.h" "$(@D)/cuda/include/thrust/system/cuda/detail/transform_scan.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/uninitialized_copy.h" "$(@D)/cuda/include/thrust/system/cuda/detail/uninitialized_copy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/uninitialized_fill.h" "$(@D)/cuda/include/thrust/system/cuda/detail/uninitialized_fill.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/unique.h" "$(@D)/cuda/include/thrust/system/cuda/detail/unique.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/unique_by_key.h" "$(@D)/cuda/include/thrust/system/cuda/detail/unique_by_key.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/util.h" "$(@D)/cuda/include/thrust/system/cuda/detail/util.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/detail/vector.inl" "$(@D)/cuda/include/thrust/system/cuda/detail/vector.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/error.h" "$(@D)/cuda/include/thrust/system/cuda/error.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/execution_policy.h" "$(@D)/cuda/include/thrust/system/cuda/execution_policy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/experimental/pinned_allocator.h" "$(@D)/cuda/include/thrust/system/cuda/experimental/pinned_allocator.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/memory.h" "$(@D)/cuda/include/thrust/system/cuda/memory.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/cuda/vector.h" "$(@D)/cuda/include/thrust/system/cuda/vector.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/adjacent_difference.h" "$(@D)/cuda/include/thrust/system/detail/adl/adjacent_difference.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/assign_value.h" "$(@D)/cuda/include/thrust/system/detail/adl/assign_value.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/binary_search.h" "$(@D)/cuda/include/thrust/system/detail/adl/binary_search.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/copy.h" "$(@D)/cuda/include/thrust/system/detail/adl/copy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/copy_if.h" "$(@D)/cuda/include/thrust/system/detail/adl/copy_if.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/count.h" "$(@D)/cuda/include/thrust/system/detail/adl/count.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/equal.h" "$(@D)/cuda/include/thrust/system/detail/adl/equal.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/extrema.h" "$(@D)/cuda/include/thrust/system/detail/adl/extrema.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/fill.h" "$(@D)/cuda/include/thrust/system/detail/adl/fill.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/find.h" "$(@D)/cuda/include/thrust/system/detail/adl/find.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/for_each.h" "$(@D)/cuda/include/thrust/system/detail/adl/for_each.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/gather.h" "$(@D)/cuda/include/thrust/system/detail/adl/gather.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/generate.h" "$(@D)/cuda/include/thrust/system/detail/adl/generate.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/get_value.h" "$(@D)/cuda/include/thrust/system/detail/adl/get_value.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/inner_product.h" "$(@D)/cuda/include/thrust/system/detail/adl/inner_product.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/iter_swap.h" "$(@D)/cuda/include/thrust/system/detail/adl/iter_swap.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/logical.h" "$(@D)/cuda/include/thrust/system/detail/adl/logical.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/malloc_and_free.h" "$(@D)/cuda/include/thrust/system/detail/adl/malloc_and_free.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/merge.h" "$(@D)/cuda/include/thrust/system/detail/adl/merge.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/mismatch.h" "$(@D)/cuda/include/thrust/system/detail/adl/mismatch.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/partition.h" "$(@D)/cuda/include/thrust/system/detail/adl/partition.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/reduce.h" "$(@D)/cuda/include/thrust/system/detail/adl/reduce.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/reduce_by_key.h" "$(@D)/cuda/include/thrust/system/detail/adl/reduce_by_key.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/remove.h" "$(@D)/cuda/include/thrust/system/detail/adl/remove.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/replace.h" "$(@D)/cuda/include/thrust/system/detail/adl/replace.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/reverse.h" "$(@D)/cuda/include/thrust/system/detail/adl/reverse.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/scan.h" "$(@D)/cuda/include/thrust/system/detail/adl/scan.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/scan_by_key.h" "$(@D)/cuda/include/thrust/system/detail/adl/scan_by_key.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/scatter.h" "$(@D)/cuda/include/thrust/system/detail/adl/scatter.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/sequence.h" "$(@D)/cuda/include/thrust/system/detail/adl/sequence.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/set_operations.h" "$(@D)/cuda/include/thrust/system/detail/adl/set_operations.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/sort.h" "$(@D)/cuda/include/thrust/system/detail/adl/sort.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/swap_ranges.h" "$(@D)/cuda/include/thrust/system/detail/adl/swap_ranges.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/tabulate.h" "$(@D)/cuda/include/thrust/system/detail/adl/tabulate.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/temporary_buffer.h" "$(@D)/cuda/include/thrust/system/detail/adl/temporary_buffer.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/transform.h" "$(@D)/cuda/include/thrust/system/detail/adl/transform.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/transform_reduce.h" "$(@D)/cuda/include/thrust/system/detail/adl/transform_reduce.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/transform_scan.h" "$(@D)/cuda/include/thrust/system/detail/adl/transform_scan.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/uninitialized_copy.h" "$(@D)/cuda/include/thrust/system/detail/adl/uninitialized_copy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/uninitialized_fill.h" "$(@D)/cuda/include/thrust/system/detail/adl/uninitialized_fill.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/unique.h" "$(@D)/cuda/include/thrust/system/detail/adl/unique.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/adl/unique_by_key.h" "$(@D)/cuda/include/thrust/system/detail/adl/unique_by_key.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/bad_alloc.h" "$(@D)/cuda/include/thrust/system/detail/bad_alloc.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/errno.h" "$(@D)/cuda/include/thrust/system/detail/errno.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/error_category.inl" "$(@D)/cuda/include/thrust/system/detail/error_category.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/error_code.inl" "$(@D)/cuda/include/thrust/system/detail/error_code.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/error_condition.inl" "$(@D)/cuda/include/thrust/system/detail/error_condition.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/adjacent_difference.h" "$(@D)/cuda/include/thrust/system/detail/generic/adjacent_difference.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/adjacent_difference.inl" "$(@D)/cuda/include/thrust/system/detail/generic/adjacent_difference.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/advance.h" "$(@D)/cuda/include/thrust/system/detail/generic/advance.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/advance.inl" "$(@D)/cuda/include/thrust/system/detail/generic/advance.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/binary_search.h" "$(@D)/cuda/include/thrust/system/detail/generic/binary_search.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/binary_search.inl" "$(@D)/cuda/include/thrust/system/detail/generic/binary_search.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/copy.h" "$(@D)/cuda/include/thrust/system/detail/generic/copy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/copy.inl" "$(@D)/cuda/include/thrust/system/detail/generic/copy.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/copy_if.h" "$(@D)/cuda/include/thrust/system/detail/generic/copy_if.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/copy_if.inl" "$(@D)/cuda/include/thrust/system/detail/generic/copy_if.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/count.h" "$(@D)/cuda/include/thrust/system/detail/generic/count.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/count.inl" "$(@D)/cuda/include/thrust/system/detail/generic/count.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/distance.h" "$(@D)/cuda/include/thrust/system/detail/generic/distance.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/distance.inl" "$(@D)/cuda/include/thrust/system/detail/generic/distance.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/equal.h" "$(@D)/cuda/include/thrust/system/detail/generic/equal.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/equal.inl" "$(@D)/cuda/include/thrust/system/detail/generic/equal.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/extrema.h" "$(@D)/cuda/include/thrust/system/detail/generic/extrema.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/extrema.inl" "$(@D)/cuda/include/thrust/system/detail/generic/extrema.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/fill.h" "$(@D)/cuda/include/thrust/system/detail/generic/fill.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/find.h" "$(@D)/cuda/include/thrust/system/detail/generic/find.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/find.inl" "$(@D)/cuda/include/thrust/system/detail/generic/find.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/for_each.h" "$(@D)/cuda/include/thrust/system/detail/generic/for_each.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/gather.h" "$(@D)/cuda/include/thrust/system/detail/generic/gather.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/gather.inl" "$(@D)/cuda/include/thrust/system/detail/generic/gather.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/generate.h" "$(@D)/cuda/include/thrust/system/detail/generic/generate.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/generate.inl" "$(@D)/cuda/include/thrust/system/detail/generic/generate.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/inner_product.h" "$(@D)/cuda/include/thrust/system/detail/generic/inner_product.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/inner_product.inl" "$(@D)/cuda/include/thrust/system/detail/generic/inner_product.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/logical.h" "$(@D)/cuda/include/thrust/system/detail/generic/logical.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/memory.h" "$(@D)/cuda/include/thrust/system/detail/generic/memory.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/memory.inl" "$(@D)/cuda/include/thrust/system/detail/generic/memory.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/merge.h" "$(@D)/cuda/include/thrust/system/detail/generic/merge.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/merge.inl" "$(@D)/cuda/include/thrust/system/detail/generic/merge.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/mismatch.h" "$(@D)/cuda/include/thrust/system/detail/generic/mismatch.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/mismatch.inl" "$(@D)/cuda/include/thrust/system/detail/generic/mismatch.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/partition.h" "$(@D)/cuda/include/thrust/system/detail/generic/partition.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/partition.inl" "$(@D)/cuda/include/thrust/system/detail/generic/partition.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/reduce.h" "$(@D)/cuda/include/thrust/system/detail/generic/reduce.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/reduce.inl" "$(@D)/cuda/include/thrust/system/detail/generic/reduce.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/reduce_by_key.h" "$(@D)/cuda/include/thrust/system/detail/generic/reduce_by_key.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/reduce_by_key.inl" "$(@D)/cuda/include/thrust/system/detail/generic/reduce_by_key.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/remove.h" "$(@D)/cuda/include/thrust/system/detail/generic/remove.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/remove.inl" "$(@D)/cuda/include/thrust/system/detail/generic/remove.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/replace.h" "$(@D)/cuda/include/thrust/system/detail/generic/replace.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/replace.inl" "$(@D)/cuda/include/thrust/system/detail/generic/replace.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/reverse.h" "$(@D)/cuda/include/thrust/system/detail/generic/reverse.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/reverse.inl" "$(@D)/cuda/include/thrust/system/detail/generic/reverse.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/scalar/binary_search.h" "$(@D)/cuda/include/thrust/system/detail/generic/scalar/binary_search.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/scalar/binary_search.inl" "$(@D)/cuda/include/thrust/system/detail/generic/scalar/binary_search.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/scan.h" "$(@D)/cuda/include/thrust/system/detail/generic/scan.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/scan.inl" "$(@D)/cuda/include/thrust/system/detail/generic/scan.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/scan_by_key.h" "$(@D)/cuda/include/thrust/system/detail/generic/scan_by_key.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/scan_by_key.inl" "$(@D)/cuda/include/thrust/system/detail/generic/scan_by_key.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/scatter.h" "$(@D)/cuda/include/thrust/system/detail/generic/scatter.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/scatter.inl" "$(@D)/cuda/include/thrust/system/detail/generic/scatter.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/select_system.h" "$(@D)/cuda/include/thrust/system/detail/generic/select_system.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/sequence.h" "$(@D)/cuda/include/thrust/system/detail/generic/sequence.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/sequence.inl" "$(@D)/cuda/include/thrust/system/detail/generic/sequence.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/set_operations.h" "$(@D)/cuda/include/thrust/system/detail/generic/set_operations.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/set_operations.inl" "$(@D)/cuda/include/thrust/system/detail/generic/set_operations.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/sort.h" "$(@D)/cuda/include/thrust/system/detail/generic/sort.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/sort.inl" "$(@D)/cuda/include/thrust/system/detail/generic/sort.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/swap_ranges.h" "$(@D)/cuda/include/thrust/system/detail/generic/swap_ranges.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/swap_ranges.inl" "$(@D)/cuda/include/thrust/system/detail/generic/swap_ranges.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/tabulate.h" "$(@D)/cuda/include/thrust/system/detail/generic/tabulate.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/tabulate.inl" "$(@D)/cuda/include/thrust/system/detail/generic/tabulate.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/tag.h" "$(@D)/cuda/include/thrust/system/detail/generic/tag.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/temporary_buffer.h" "$(@D)/cuda/include/thrust/system/detail/generic/temporary_buffer.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/temporary_buffer.inl" "$(@D)/cuda/include/thrust/system/detail/generic/temporary_buffer.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/transform.h" "$(@D)/cuda/include/thrust/system/detail/generic/transform.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/transform.inl" "$(@D)/cuda/include/thrust/system/detail/generic/transform.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/transform_reduce.h" "$(@D)/cuda/include/thrust/system/detail/generic/transform_reduce.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/transform_reduce.inl" "$(@D)/cuda/include/thrust/system/detail/generic/transform_reduce.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/transform_scan.h" "$(@D)/cuda/include/thrust/system/detail/generic/transform_scan.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/transform_scan.inl" "$(@D)/cuda/include/thrust/system/detail/generic/transform_scan.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/type_traits.h" "$(@D)/cuda/include/thrust/system/detail/generic/type_traits.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/uninitialized_copy.h" "$(@D)/cuda/include/thrust/system/detail/generic/uninitialized_copy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/uninitialized_copy.inl" "$(@D)/cuda/include/thrust/system/detail/generic/uninitialized_copy.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/uninitialized_fill.h" "$(@D)/cuda/include/thrust/system/detail/generic/uninitialized_fill.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/uninitialized_fill.inl" "$(@D)/cuda/include/thrust/system/detail/generic/uninitialized_fill.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/unique.h" "$(@D)/cuda/include/thrust/system/detail/generic/unique.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/unique.inl" "$(@D)/cuda/include/thrust/system/detail/generic/unique.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/unique_by_key.h" "$(@D)/cuda/include/thrust/system/detail/generic/unique_by_key.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/generic/unique_by_key.inl" "$(@D)/cuda/include/thrust/system/detail/generic/unique_by_key.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/internal/decompose.h" "$(@D)/cuda/include/thrust/system/detail/internal/decompose.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/adjacent_difference.h" "$(@D)/cuda/include/thrust/system/detail/sequential/adjacent_difference.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/assign_value.h" "$(@D)/cuda/include/thrust/system/detail/sequential/assign_value.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/binary_search.h" "$(@D)/cuda/include/thrust/system/detail/sequential/binary_search.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/copy.h" "$(@D)/cuda/include/thrust/system/detail/sequential/copy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/copy.inl" "$(@D)/cuda/include/thrust/system/detail/sequential/copy.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/copy_backward.h" "$(@D)/cuda/include/thrust/system/detail/sequential/copy_backward.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/copy_if.h" "$(@D)/cuda/include/thrust/system/detail/sequential/copy_if.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/count.h" "$(@D)/cuda/include/thrust/system/detail/sequential/count.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/equal.h" "$(@D)/cuda/include/thrust/system/detail/sequential/equal.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/execution_policy.h" "$(@D)/cuda/include/thrust/system/detail/sequential/execution_policy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/extrema.h" "$(@D)/cuda/include/thrust/system/detail/sequential/extrema.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/fill.h" "$(@D)/cuda/include/thrust/system/detail/sequential/fill.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/find.h" "$(@D)/cuda/include/thrust/system/detail/sequential/find.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/for_each.h" "$(@D)/cuda/include/thrust/system/detail/sequential/for_each.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/gather.h" "$(@D)/cuda/include/thrust/system/detail/sequential/gather.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/general_copy.h" "$(@D)/cuda/include/thrust/system/detail/sequential/general_copy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/generate.h" "$(@D)/cuda/include/thrust/system/detail/sequential/generate.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/get_value.h" "$(@D)/cuda/include/thrust/system/detail/sequential/get_value.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/inner_product.h" "$(@D)/cuda/include/thrust/system/detail/sequential/inner_product.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/insertion_sort.h" "$(@D)/cuda/include/thrust/system/detail/sequential/insertion_sort.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/iter_swap.h" "$(@D)/cuda/include/thrust/system/detail/sequential/iter_swap.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/logical.h" "$(@D)/cuda/include/thrust/system/detail/sequential/logical.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/malloc_and_free.h" "$(@D)/cuda/include/thrust/system/detail/sequential/malloc_and_free.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/merge.h" "$(@D)/cuda/include/thrust/system/detail/sequential/merge.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/merge.inl" "$(@D)/cuda/include/thrust/system/detail/sequential/merge.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/mismatch.h" "$(@D)/cuda/include/thrust/system/detail/sequential/mismatch.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/partition.h" "$(@D)/cuda/include/thrust/system/detail/sequential/partition.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/reduce.h" "$(@D)/cuda/include/thrust/system/detail/sequential/reduce.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/reduce_by_key.h" "$(@D)/cuda/include/thrust/system/detail/sequential/reduce_by_key.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/remove.h" "$(@D)/cuda/include/thrust/system/detail/sequential/remove.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/replace.h" "$(@D)/cuda/include/thrust/system/detail/sequential/replace.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/reverse.h" "$(@D)/cuda/include/thrust/system/detail/sequential/reverse.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/scan.h" "$(@D)/cuda/include/thrust/system/detail/sequential/scan.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/scan_by_key.h" "$(@D)/cuda/include/thrust/system/detail/sequential/scan_by_key.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/scatter.h" "$(@D)/cuda/include/thrust/system/detail/sequential/scatter.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/sequence.h" "$(@D)/cuda/include/thrust/system/detail/sequential/sequence.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/set_operations.h" "$(@D)/cuda/include/thrust/system/detail/sequential/set_operations.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/sort.h" "$(@D)/cuda/include/thrust/system/detail/sequential/sort.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/sort.inl" "$(@D)/cuda/include/thrust/system/detail/sequential/sort.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/stable_merge_sort.h" "$(@D)/cuda/include/thrust/system/detail/sequential/stable_merge_sort.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/stable_merge_sort.inl" "$(@D)/cuda/include/thrust/system/detail/sequential/stable_merge_sort.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/stable_primitive_sort.h" "$(@D)/cuda/include/thrust/system/detail/sequential/stable_primitive_sort.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/stable_primitive_sort.inl" "$(@D)/cuda/include/thrust/system/detail/sequential/stable_primitive_sort.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/stable_radix_sort.h" "$(@D)/cuda/include/thrust/system/detail/sequential/stable_radix_sort.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/stable_radix_sort.inl" "$(@D)/cuda/include/thrust/system/detail/sequential/stable_radix_sort.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/swap_ranges.h" "$(@D)/cuda/include/thrust/system/detail/sequential/swap_ranges.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/tabulate.h" "$(@D)/cuda/include/thrust/system/detail/sequential/tabulate.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/temporary_buffer.h" "$(@D)/cuda/include/thrust/system/detail/sequential/temporary_buffer.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/transform.h" "$(@D)/cuda/include/thrust/system/detail/sequential/transform.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/transform_reduce.h" "$(@D)/cuda/include/thrust/system/detail/sequential/transform_reduce.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/transform_scan.h" "$(@D)/cuda/include/thrust/system/detail/sequential/transform_scan.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/trivial_copy.h" "$(@D)/cuda/include/thrust/system/detail/sequential/trivial_copy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/uninitialized_copy.h" "$(@D)/cuda/include/thrust/system/detail/sequential/uninitialized_copy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/uninitialized_fill.h" "$(@D)/cuda/include/thrust/system/detail/sequential/uninitialized_fill.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/unique.h" "$(@D)/cuda/include/thrust/system/detail/sequential/unique.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/sequential/unique_by_key.h" "$(@D)/cuda/include/thrust/system/detail/sequential/unique_by_key.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/detail/system_error.inl" "$(@D)/cuda/include/thrust/system/detail/system_error.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/error_code.h" "$(@D)/cuda/include/thrust/system/error_code.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/adjacent_difference.h" "$(@D)/cuda/include/thrust/system/omp/detail/adjacent_difference.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/assign_value.h" "$(@D)/cuda/include/thrust/system/omp/detail/assign_value.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/binary_search.h" "$(@D)/cuda/include/thrust/system/omp/detail/binary_search.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/copy.h" "$(@D)/cuda/include/thrust/system/omp/detail/copy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/copy.inl" "$(@D)/cuda/include/thrust/system/omp/detail/copy.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/copy_if.h" "$(@D)/cuda/include/thrust/system/omp/detail/copy_if.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/copy_if.inl" "$(@D)/cuda/include/thrust/system/omp/detail/copy_if.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/count.h" "$(@D)/cuda/include/thrust/system/omp/detail/count.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/default_decomposition.h" "$(@D)/cuda/include/thrust/system/omp/detail/default_decomposition.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/default_decomposition.inl" "$(@D)/cuda/include/thrust/system/omp/detail/default_decomposition.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/equal.h" "$(@D)/cuda/include/thrust/system/omp/detail/equal.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/execution_policy.h" "$(@D)/cuda/include/thrust/system/omp/detail/execution_policy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/extrema.h" "$(@D)/cuda/include/thrust/system/omp/detail/extrema.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/fill.h" "$(@D)/cuda/include/thrust/system/omp/detail/fill.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/find.h" "$(@D)/cuda/include/thrust/system/omp/detail/find.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/for_each.h" "$(@D)/cuda/include/thrust/system/omp/detail/for_each.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/for_each.inl" "$(@D)/cuda/include/thrust/system/omp/detail/for_each.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/gather.h" "$(@D)/cuda/include/thrust/system/omp/detail/gather.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/generate.h" "$(@D)/cuda/include/thrust/system/omp/detail/generate.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/get_value.h" "$(@D)/cuda/include/thrust/system/omp/detail/get_value.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/inner_product.h" "$(@D)/cuda/include/thrust/system/omp/detail/inner_product.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/iter_swap.h" "$(@D)/cuda/include/thrust/system/omp/detail/iter_swap.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/logical.h" "$(@D)/cuda/include/thrust/system/omp/detail/logical.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/malloc_and_free.h" "$(@D)/cuda/include/thrust/system/omp/detail/malloc_and_free.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/memory.inl" "$(@D)/cuda/include/thrust/system/omp/detail/memory.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/merge.h" "$(@D)/cuda/include/thrust/system/omp/detail/merge.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/mismatch.h" "$(@D)/cuda/include/thrust/system/omp/detail/mismatch.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/par.h" "$(@D)/cuda/include/thrust/system/omp/detail/par.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/partition.h" "$(@D)/cuda/include/thrust/system/omp/detail/partition.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/partition.inl" "$(@D)/cuda/include/thrust/system/omp/detail/partition.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/reduce.h" "$(@D)/cuda/include/thrust/system/omp/detail/reduce.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/reduce.inl" "$(@D)/cuda/include/thrust/system/omp/detail/reduce.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/reduce_by_key.h" "$(@D)/cuda/include/thrust/system/omp/detail/reduce_by_key.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/reduce_by_key.inl" "$(@D)/cuda/include/thrust/system/omp/detail/reduce_by_key.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/reduce_intervals.h" "$(@D)/cuda/include/thrust/system/omp/detail/reduce_intervals.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/reduce_intervals.inl" "$(@D)/cuda/include/thrust/system/omp/detail/reduce_intervals.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/remove.h" "$(@D)/cuda/include/thrust/system/omp/detail/remove.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/remove.inl" "$(@D)/cuda/include/thrust/system/omp/detail/remove.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/replace.h" "$(@D)/cuda/include/thrust/system/omp/detail/replace.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/reverse.h" "$(@D)/cuda/include/thrust/system/omp/detail/reverse.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/scan.h" "$(@D)/cuda/include/thrust/system/omp/detail/scan.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/scan_by_key.h" "$(@D)/cuda/include/thrust/system/omp/detail/scan_by_key.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/scatter.h" "$(@D)/cuda/include/thrust/system/omp/detail/scatter.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/sequence.h" "$(@D)/cuda/include/thrust/system/omp/detail/sequence.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/set_operations.h" "$(@D)/cuda/include/thrust/system/omp/detail/set_operations.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/sort.h" "$(@D)/cuda/include/thrust/system/omp/detail/sort.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/sort.inl" "$(@D)/cuda/include/thrust/system/omp/detail/sort.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/swap_ranges.h" "$(@D)/cuda/include/thrust/system/omp/detail/swap_ranges.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/tabulate.h" "$(@D)/cuda/include/thrust/system/omp/detail/tabulate.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/temporary_buffer.h" "$(@D)/cuda/include/thrust/system/omp/detail/temporary_buffer.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/transform.h" "$(@D)/cuda/include/thrust/system/omp/detail/transform.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/transform_reduce.h" "$(@D)/cuda/include/thrust/system/omp/detail/transform_reduce.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/transform_scan.h" "$(@D)/cuda/include/thrust/system/omp/detail/transform_scan.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/uninitialized_copy.h" "$(@D)/cuda/include/thrust/system/omp/detail/uninitialized_copy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/uninitialized_fill.h" "$(@D)/cuda/include/thrust/system/omp/detail/uninitialized_fill.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/unique.h" "$(@D)/cuda/include/thrust/system/omp/detail/unique.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/unique.inl" "$(@D)/cuda/include/thrust/system/omp/detail/unique.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/unique_by_key.h" "$(@D)/cuda/include/thrust/system/omp/detail/unique_by_key.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/unique_by_key.inl" "$(@D)/cuda/include/thrust/system/omp/detail/unique_by_key.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/detail/vector.inl" "$(@D)/cuda/include/thrust/system/omp/detail/vector.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/execution_policy.h" "$(@D)/cuda/include/thrust/system/omp/execution_policy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/memory.h" "$(@D)/cuda/include/thrust/system/omp/memory.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/omp/vector.h" "$(@D)/cuda/include/thrust/system/omp/vector.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/system_error.h" "$(@D)/cuda/include/thrust/system/system_error.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/adjacent_difference.h" "$(@D)/cuda/include/thrust/system/tbb/detail/adjacent_difference.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/assign_value.h" "$(@D)/cuda/include/thrust/system/tbb/detail/assign_value.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/binary_search.h" "$(@D)/cuda/include/thrust/system/tbb/detail/binary_search.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/copy.h" "$(@D)/cuda/include/thrust/system/tbb/detail/copy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/copy.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/copy.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/copy_if.h" "$(@D)/cuda/include/thrust/system/tbb/detail/copy_if.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/copy_if.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/copy_if.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/count.h" "$(@D)/cuda/include/thrust/system/tbb/detail/count.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/equal.h" "$(@D)/cuda/include/thrust/system/tbb/detail/equal.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/execution_policy.h" "$(@D)/cuda/include/thrust/system/tbb/detail/execution_policy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/extrema.h" "$(@D)/cuda/include/thrust/system/tbb/detail/extrema.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/fill.h" "$(@D)/cuda/include/thrust/system/tbb/detail/fill.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/find.h" "$(@D)/cuda/include/thrust/system/tbb/detail/find.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/for_each.h" "$(@D)/cuda/include/thrust/system/tbb/detail/for_each.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/for_each.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/for_each.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/gather.h" "$(@D)/cuda/include/thrust/system/tbb/detail/gather.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/generate.h" "$(@D)/cuda/include/thrust/system/tbb/detail/generate.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/get_value.h" "$(@D)/cuda/include/thrust/system/tbb/detail/get_value.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/inner_product.h" "$(@D)/cuda/include/thrust/system/tbb/detail/inner_product.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/iter_swap.h" "$(@D)/cuda/include/thrust/system/tbb/detail/iter_swap.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/logical.h" "$(@D)/cuda/include/thrust/system/tbb/detail/logical.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/malloc_and_free.h" "$(@D)/cuda/include/thrust/system/tbb/detail/malloc_and_free.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/memory.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/memory.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/merge.h" "$(@D)/cuda/include/thrust/system/tbb/detail/merge.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/merge.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/merge.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/mismatch.h" "$(@D)/cuda/include/thrust/system/tbb/detail/mismatch.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/par.h" "$(@D)/cuda/include/thrust/system/tbb/detail/par.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/partition.h" "$(@D)/cuda/include/thrust/system/tbb/detail/partition.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/partition.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/partition.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/reduce.h" "$(@D)/cuda/include/thrust/system/tbb/detail/reduce.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/reduce.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/reduce.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/reduce_by_key.h" "$(@D)/cuda/include/thrust/system/tbb/detail/reduce_by_key.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/reduce_by_key.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/reduce_by_key.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/reduce_intervals.h" "$(@D)/cuda/include/thrust/system/tbb/detail/reduce_intervals.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/remove.h" "$(@D)/cuda/include/thrust/system/tbb/detail/remove.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/remove.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/remove.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/replace.h" "$(@D)/cuda/include/thrust/system/tbb/detail/replace.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/reverse.h" "$(@D)/cuda/include/thrust/system/tbb/detail/reverse.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/scan.h" "$(@D)/cuda/include/thrust/system/tbb/detail/scan.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/scan.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/scan.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/scan_by_key.h" "$(@D)/cuda/include/thrust/system/tbb/detail/scan_by_key.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/scatter.h" "$(@D)/cuda/include/thrust/system/tbb/detail/scatter.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/sequence.h" "$(@D)/cuda/include/thrust/system/tbb/detail/sequence.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/set_operations.h" "$(@D)/cuda/include/thrust/system/tbb/detail/set_operations.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/sort.h" "$(@D)/cuda/include/thrust/system/tbb/detail/sort.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/sort.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/sort.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/swap_ranges.h" "$(@D)/cuda/include/thrust/system/tbb/detail/swap_ranges.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/tabulate.h" "$(@D)/cuda/include/thrust/system/tbb/detail/tabulate.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/temporary_buffer.h" "$(@D)/cuda/include/thrust/system/tbb/detail/temporary_buffer.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/transform.h" "$(@D)/cuda/include/thrust/system/tbb/detail/transform.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/transform_reduce.h" "$(@D)/cuda/include/thrust/system/tbb/detail/transform_reduce.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/transform_scan.h" "$(@D)/cuda/include/thrust/system/tbb/detail/transform_scan.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/uninitialized_copy.h" "$(@D)/cuda/include/thrust/system/tbb/detail/uninitialized_copy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/uninitialized_fill.h" "$(@D)/cuda/include/thrust/system/tbb/detail/uninitialized_fill.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/unique.h" "$(@D)/cuda/include/thrust/system/tbb/detail/unique.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/unique.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/unique.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/unique_by_key.h" "$(@D)/cuda/include/thrust/system/tbb/detail/unique_by_key.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/unique_by_key.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/unique_by_key.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/detail/vector.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/vector.inl" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/execution_policy.h" "$(@D)/cuda/include/thrust/system/tbb/execution_policy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/memory.h" "$(@D)/cuda/include/thrust/system/tbb/memory.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system/tbb/vector.h" "$(@D)/cuda/include/thrust/system/tbb/vector.h" && cp -f "/usr/local/cuda-10.0/include/thrust/system_error.h" "$(@D)/cuda/include/thrust/system_error.h" && cp -f "/usr/local/cuda-10.0/include/thrust/tabulate.h" "$(@D)/cuda/include/thrust/tabulate.h" && cp -f "/usr/local/cuda-10.0/include/thrust/transform.h" "$(@D)/cuda/include/thrust/transform.h" && cp -f "/usr/local/cuda-10.0/include/thrust/transform_reduce.h" "$(@D)/cuda/include/thrust/transform_reduce.h" && cp -f "/usr/local/cuda-10.0/include/thrust/transform_scan.h" "$(@D)/cuda/include/thrust/transform_scan.h" && cp -f "/usr/local/cuda-10.0/include/thrust/tuple.h" "$(@D)/cuda/include/thrust/tuple.h" && cp -f "/usr/local/cuda-10.0/include/thrust/uninitialized_copy.h" "$(@D)/cuda/include/thrust/uninitialized_copy.h" && cp -f "/usr/local/cuda-10.0/include/thrust/uninitialized_fill.h" "$(@D)/cuda/include/thrust/uninitialized_fill.h" && cp -f "/usr/local/cuda-10.0/include/thrust/unique.h" "$(@D)/cuda/include/thrust/unique.h" && cp -f "/usr/local/cuda-10.0/include/thrust/version.h" "$(@D)/cuda/include/thrust/version.h" && cp -f "/usr/local/cuda-10.0/include/vector_functions.h" "$(@D)/cuda/include/vector_functions.h" && cp -f "/usr/local/cuda-10.0/include/vector_functions.hpp" "$(@D)/cuda/include/vector_functions.hpp" && cp -f "/usr/local/cuda-10.0/include/vector_types.h" "$(@D)/cuda/include/vector_types.h" + """, +) + +genrule( + name = "cuda-nvvm", + outs = [ + "cuda/nvvm/libdevice/libdevice.10.bc", + ], + cmd = """ +if [ -d "$(@D)/extras" ]; then rm $(@D)/extras -drf; fi && if [ -d "$(@D)/include" ]; then rm $(@D)/include -drf; fi && if [ -d "$(@D)/lib" ]; then rm $(@D)/lib -drf; fi && if [ -d "$(@D)/nvvm" ]; then rm $(@D)/nvvm -drf; fi && cp -f "/usr/local/cuda-10.0/nvvm/libdevice/libdevice.10.bc" "$(@D)//libdevice.10.bc" + """, +) + +genrule( + name = "cuda-extras", + outs = [ + "cuda/extras/CUPTI/include/GL/gl.h", + "cuda/extras/CUPTI/include/GL/glew.h", + "cuda/extras/CUPTI/include/GL/glext.h", + "cuda/extras/CUPTI/include/GL/glu.h", + "cuda/extras/CUPTI/include/GL/glut.h", + "cuda/extras/CUPTI/include/GL/glx.h", + "cuda/extras/CUPTI/include/GL/glxext.h", + "cuda/extras/CUPTI/include/GL/wglew.h", + "cuda/extras/CUPTI/include/GL/wglext.h", + "cuda/extras/CUPTI/include/cuda_stdint.h", + "cuda/extras/CUPTI/include/cupti.h", + "cuda/extras/CUPTI/include/cupti_activity.h", + "cuda/extras/CUPTI/include/cupti_callbacks.h", + "cuda/extras/CUPTI/include/cupti_driver_cbid.h", + "cuda/extras/CUPTI/include/cupti_events.h", + "cuda/extras/CUPTI/include/cupti_metrics.h", + "cuda/extras/CUPTI/include/cupti_nvtx_cbid.h", + "cuda/extras/CUPTI/include/cupti_result.h", + "cuda/extras/CUPTI/include/cupti_runtime_cbid.h", + "cuda/extras/CUPTI/include/cupti_version.h", + "cuda/extras/CUPTI/include/generated_cudaGL_meta.h", + "cuda/extras/CUPTI/include/generated_cudaVDPAU_meta.h", + "cuda/extras/CUPTI/include/generated_cuda_gl_interop_meta.h", + "cuda/extras/CUPTI/include/generated_cuda_meta.h", + "cuda/extras/CUPTI/include/generated_cuda_runtime_api_meta.h", + "cuda/extras/CUPTI/include/generated_cuda_vdpau_interop_meta.h", + "cuda/extras/CUPTI/include/generated_nvtx_meta.h", + "cuda/extras/CUPTI/include/openacc/cupti_openacc.h", + "cuda/extras/CUPTI/include/openmp/cupti_openmp.h", + "cuda/extras/CUPTI/include/openmp/ompt.h", + ], + cmd = """ +if [ -d "$(@D)/extras" ]; then rm $(@D)/extras -drf; fi && if [ -d "$(@D)/include" ]; then rm $(@D)/include -drf; fi && if [ -d "$(@D)/lib" ]; then rm $(@D)/lib -drf; fi && if [ -d "$(@D)/nvvm" ]; then rm $(@D)/nvvm -drf; fi && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/GL/gl.h" "$(@D)/cuda/extras/CUPTI/include/GL/gl.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/GL/glew.h" "$(@D)/cuda/extras/CUPTI/include/GL/glew.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/GL/glext.h" "$(@D)/cuda/extras/CUPTI/include/GL/glext.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/GL/glu.h" "$(@D)/cuda/extras/CUPTI/include/GL/glu.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/GL/glut.h" "$(@D)/cuda/extras/CUPTI/include/GL/glut.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/GL/glx.h" "$(@D)/cuda/extras/CUPTI/include/GL/glx.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/GL/glxext.h" "$(@D)/cuda/extras/CUPTI/include/GL/glxext.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/GL/wglew.h" "$(@D)/cuda/extras/CUPTI/include/GL/wglew.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/GL/wglext.h" "$(@D)/cuda/extras/CUPTI/include/GL/wglext.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/cuda_stdint.h" "$(@D)/cuda/extras/CUPTI/include/cuda_stdint.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/cupti.h" "$(@D)/cuda/extras/CUPTI/include/cupti.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/cupti_activity.h" "$(@D)/cuda/extras/CUPTI/include/cupti_activity.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/cupti_callbacks.h" "$(@D)/cuda/extras/CUPTI/include/cupti_callbacks.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/cupti_driver_cbid.h" "$(@D)/cuda/extras/CUPTI/include/cupti_driver_cbid.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/cupti_events.h" "$(@D)/cuda/extras/CUPTI/include/cupti_events.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/cupti_metrics.h" "$(@D)/cuda/extras/CUPTI/include/cupti_metrics.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/cupti_nvtx_cbid.h" "$(@D)/cuda/extras/CUPTI/include/cupti_nvtx_cbid.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/cupti_result.h" "$(@D)/cuda/extras/CUPTI/include/cupti_result.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/cupti_runtime_cbid.h" "$(@D)/cuda/extras/CUPTI/include/cupti_runtime_cbid.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/cupti_version.h" "$(@D)/cuda/extras/CUPTI/include/cupti_version.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/generated_cudaGL_meta.h" "$(@D)/cuda/extras/CUPTI/include/generated_cudaGL_meta.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/generated_cudaVDPAU_meta.h" "$(@D)/cuda/extras/CUPTI/include/generated_cudaVDPAU_meta.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/generated_cuda_gl_interop_meta.h" "$(@D)/cuda/extras/CUPTI/include/generated_cuda_gl_interop_meta.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/generated_cuda_meta.h" "$(@D)/cuda/extras/CUPTI/include/generated_cuda_meta.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/generated_cuda_runtime_api_meta.h" "$(@D)/cuda/extras/CUPTI/include/generated_cuda_runtime_api_meta.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/generated_cuda_vdpau_interop_meta.h" "$(@D)/cuda/extras/CUPTI/include/generated_cuda_vdpau_interop_meta.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/generated_nvtx_meta.h" "$(@D)/cuda/extras/CUPTI/include/generated_nvtx_meta.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/openacc/cupti_openacc.h" "$(@D)/cuda/extras/CUPTI/include/openacc/cupti_openacc.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/openmp/cupti_openmp.h" "$(@D)/cuda/extras/CUPTI/include/openmp/cupti_openmp.h" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/include/openmp/ompt.h" "$(@D)/cuda/extras/CUPTI/include/openmp/ompt.h" + """, +) + +genrule( + name = "cuda-lib", + outs = [ + "cuda/lib/libcuda.so", + "cuda/lib/libcudart.so.10.0", + "cuda/lib/libcudart_static.a", + "cuda/lib/libcublas.so.10.0", + "cuda/lib/libcusolver.so.10.0", + "cuda/lib/libcurand.so.10.0", + "cuda/lib/libcufft.so.10.0", + "cuda/lib/libcudnn.so.7", + "cuda/lib/libcupti.so.10.0", + ], + cmd = """ +if [ -d "$(@D)/extras" ]; then rm $(@D)/extras -drf; fi && if [ -d "$(@D)/include" ]; then rm $(@D)/include -drf; fi && if [ -d "$(@D)/lib" ]; then rm $(@D)/lib -drf; fi && if [ -d "$(@D)/nvvm" ]; then rm $(@D)/nvvm -drf; fi && cp -f "/usr/local/cuda-10.0/targets/x86_64-linux/lib/stubs/libcuda.so" "$(@D)/cuda/lib/libcuda.so" && cp -f "/usr/local/cuda-10.0/targets/x86_64-linux/lib/libcudart.so.10.0.130" "$(@D)/cuda/lib/libcudart.so.10.0" && cp -f "/usr/local/cuda-10.0/targets/x86_64-linux/lib/libcudart_static.a" "$(@D)/cuda/lib/libcudart_static.a" && cp -f "/usr/local/cuda-10.0/targets/x86_64-linux/lib/libcublas.so.10.0.130" "$(@D)/cuda/lib/libcublas.so.10.0" && cp -f "/usr/local/cuda-10.0/targets/x86_64-linux/lib/libcusolver.so.10.0.130" "$(@D)/cuda/lib/libcusolver.so.10.0" && cp -f "/usr/local/cuda-10.0/targets/x86_64-linux/lib/libcurand.so.10.0.130" "$(@D)/cuda/lib/libcurand.so.10.0" && cp -f "/usr/local/cuda-10.0/targets/x86_64-linux/lib/libcufft.so.10.0.145" "$(@D)/cuda/lib/libcufft.so.10.0" && cp -f "/usr/lib/x86_64-linux-gnu/libcudnn.so.7.3.1" "$(@D)/cuda/lib/libcudnn.so.7" && cp -f "/usr/local/cuda-10.0/extras/CUPTI/lib64/libcupti.so.10.0.130" "$(@D)/cuda/lib/libcupti.so.10.0" + """, +) + +genrule( + name = "cudnn-include", + outs = [ + "cuda/include/cudnn.h", + ], + cmd = """ +if [ -d "$(@D)/extras" ]; then rm $(@D)/extras -drf; fi && if [ -d "$(@D)/include" ]; then rm $(@D)/include -drf; fi && if [ -d "$(@D)/lib" ]; then rm $(@D)/lib -drf; fi && if [ -d "$(@D)/nvvm" ]; then rm $(@D)/nvvm -drf; fi && cp -f "/usr/include/cudnn.h" "$(@D)/cudnn.h" + """, +) diff --git a/third_party/toolchains/preconfig/ubuntu14.04/cuda10.0-cudnn7/cuda/build_defs.bzl b/third_party/toolchains/preconfig/ubuntu14.04/cuda10.0-cudnn7/cuda/build_defs.bzl new file mode 100755 index 0000000000..a53c891d8b --- /dev/null +++ b/third_party/toolchains/preconfig/ubuntu14.04/cuda10.0-cudnn7/cuda/build_defs.bzl @@ -0,0 +1,31 @@ +# Macros for building CUDA code. +def if_cuda(if_true, if_false = []): + """Shorthand for select()'ing on whether we're building with CUDA. + + Returns a select statement which evaluates to if_true if we're building + with CUDA enabled. Otherwise, the select statement evaluates to if_false. + + """ + return select({ + "@local_config_cuda//cuda:using_nvcc": if_true, + "@local_config_cuda//cuda:using_clang": if_true, + "//conditions:default": if_false, + }) + +def cuda_default_copts(): + """Default options for all CUDA compilations.""" + return if_cuda(["-x", "cuda", "-DGOOGLE_CUDA=1"] + []) + +def cuda_is_configured(): + """Returns true if CUDA was enabled during the configure process.""" + return True + +def if_cuda_is_configured(x): + """Tests if the CUDA was enabled during the configure process. + + Unlike if_cuda(), this does not require that we are building with + --config=cuda. Used to allow non-CUDA code to depend on CUDA libraries. + """ + if cuda_is_configured(): + return x + return [] diff --git a/third_party/toolchains/preconfig/ubuntu14.04/cuda10.0-cudnn7/cuda/cuda/cuda_config.h b/third_party/toolchains/preconfig/ubuntu14.04/cuda10.0-cudnn7/cuda/cuda/cuda_config.h new file mode 100755 index 0000000000..0934618e0b --- /dev/null +++ b/third_party/toolchains/preconfig/ubuntu14.04/cuda10.0-cudnn7/cuda/cuda/cuda_config.h @@ -0,0 +1,26 @@ +/* Copyright 2015 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 CUDA_CUDA_CONFIG_H_ +#define CUDA_CUDA_CONFIG_H_ + +#define TF_CUDA_CAPABILITIES CudaVersion("3.0") + +#define TF_CUDA_VERSION "10.0" +#define TF_CUDNN_VERSION "7" + +#define TF_CUDA_TOOLKIT_PATH "/usr/local/cuda-10.0" + +#endif // CUDA_CUDA_CONFIG_H_ diff --git a/third_party/toolchains/preconfig/ubuntu14.04/cuda9.0-cudnn7/cuda/BUILD b/third_party/toolchains/preconfig/ubuntu14.04/cuda9.0-cudnn7/cuda/BUILD index 247e0ace24..c6930904b5 100755 --- a/third_party/toolchains/preconfig/ubuntu14.04/cuda9.0-cudnn7/cuda/BUILD +++ b/third_party/toolchains/preconfig/ubuntu14.04/cuda9.0-cudnn7/cuda/BUILD @@ -1188,7 +1188,7 @@ genrule( "cuda/include/vector_types.h", ], cmd = """ -if [ -d "$(@D)/extras" ]; then rm $(@D)/extras -drf; fi && if [ -d "$(@D)/include" ]; then rm $(@D)/include -drf; fi && if [ -d "$(@D)/lib" ]; then rm $(@D)/lib -drf; fi && if [ -d "$(@D)/nvvm" ]; then rm $(@D)/nvvm -drf; fi && cp "/usr/local/cuda-9.0/include/CL/cl.h" "$(@D)/cuda/include/CL/cl.h" && cp "/usr/local/cuda-9.0/include/CL/cl.hpp" "$(@D)/cuda/include/CL/cl.hpp" && cp "/usr/local/cuda-9.0/include/CL/cl_egl.h" "$(@D)/cuda/include/CL/cl_egl.h" && cp "/usr/local/cuda-9.0/include/CL/cl_ext.h" "$(@D)/cuda/include/CL/cl_ext.h" && cp "/usr/local/cuda-9.0/include/CL/cl_gl.h" "$(@D)/cuda/include/CL/cl_gl.h" && cp "/usr/local/cuda-9.0/include/CL/cl_gl_ext.h" "$(@D)/cuda/include/CL/cl_gl_ext.h" && cp "/usr/local/cuda-9.0/include/CL/cl_platform.h" "$(@D)/cuda/include/CL/cl_platform.h" && cp "/usr/local/cuda-9.0/include/CL/opencl.h" "$(@D)/cuda/include/CL/opencl.h" && cp "/usr/local/cuda-9.0/include/builtin_types.h" "$(@D)/cuda/include/builtin_types.h" && cp "/usr/local/cuda-9.0/include/channel_descriptor.h" "$(@D)/cuda/include/channel_descriptor.h" && cp "/usr/local/cuda-9.0/include/common_functions.h" "$(@D)/cuda/include/common_functions.h" && cp "/usr/local/cuda-9.0/include/cooperative_groups.h" "$(@D)/cuda/include/cooperative_groups.h" && cp "/usr/local/cuda-9.0/include/cooperative_groups_helpers.h" "$(@D)/cuda/include/cooperative_groups_helpers.h" && cp "/usr/local/cuda-9.0/include/crt/common_functions.h" "$(@D)/cuda/include/crt/common_functions.h" && cp "/usr/local/cuda-9.0/include/crt/device_double_functions.h" "$(@D)/cuda/include/crt/device_double_functions.h" && cp "/usr/local/cuda-9.0/include/crt/device_double_functions.hpp" "$(@D)/cuda/include/crt/device_double_functions.hpp" && cp "/usr/local/cuda-9.0/include/crt/device_functions.h" "$(@D)/cuda/include/crt/device_functions.h" && cp "/usr/local/cuda-9.0/include/crt/device_functions.hpp" "$(@D)/cuda/include/crt/device_functions.hpp" && cp "/usr/local/cuda-9.0/include/crt/func_macro.h" "$(@D)/cuda/include/crt/func_macro.h" && cp "/usr/local/cuda-9.0/include/crt/host_config.h" "$(@D)/cuda/include/crt/host_config.h" && cp "/usr/local/cuda-9.0/include/crt/host_defines.h" "$(@D)/cuda/include/crt/host_defines.h" && cp "/usr/local/cuda-9.0/include/crt/host_runtime.h" "$(@D)/cuda/include/crt/host_runtime.h" && cp "/usr/local/cuda-9.0/include/crt/math_functions.h" "$(@D)/cuda/include/crt/math_functions.h" && cp "/usr/local/cuda-9.0/include/crt/math_functions.hpp" "$(@D)/cuda/include/crt/math_functions.hpp" && cp "/usr/local/cuda-9.0/include/crt/mma.h" "$(@D)/cuda/include/crt/mma.h" && cp "/usr/local/cuda-9.0/include/crt/mma.hpp" "$(@D)/cuda/include/crt/mma.hpp" && cp "/usr/local/cuda-9.0/include/crt/nvfunctional" "$(@D)/cuda/include/crt/nvfunctional" && cp "/usr/local/cuda-9.0/include/crt/sm_70_rt.h" "$(@D)/cuda/include/crt/sm_70_rt.h" && cp "/usr/local/cuda-9.0/include/crt/sm_70_rt.hpp" "$(@D)/cuda/include/crt/sm_70_rt.hpp" && cp "/usr/local/cuda-9.0/include/crt/storage_class.h" "$(@D)/cuda/include/crt/storage_class.h" && cp "/usr/local/cuda-9.0/include/cuComplex.h" "$(@D)/cuda/include/cuComplex.h" && cp "/usr/local/cuda-9.0/include/cublas.h" "$(@D)/cuda/include/cublas.h" && cp "/usr/local/cuda-9.0/include/cublasXt.h" "$(@D)/cuda/include/cublasXt.h" && cp "/usr/local/cuda-9.0/include/cublas_api.h" "$(@D)/cuda/include/cublas_api.h" && cp "/usr/local/cuda-9.0/include/cublas_v2.h" "$(@D)/cuda/include/cublas_v2.h" && cp "/usr/local/cuda-9.0/include/cuda.h" "$(@D)/cuda/include/cuda.h" && cp "/usr/local/cuda-9.0/include/cudaEGL.h" "$(@D)/cuda/include/cudaEGL.h" && cp "/usr/local/cuda-9.0/include/cudaGL.h" "$(@D)/cuda/include/cudaGL.h" && cp "/usr/local/cuda-9.0/include/cudaProfiler.h" "$(@D)/cuda/include/cudaProfiler.h" && cp "/usr/local/cuda-9.0/include/cudaVDPAU.h" "$(@D)/cuda/include/cudaVDPAU.h" && cp "/usr/local/cuda-9.0/include/cuda_device_runtime_api.h" "$(@D)/cuda/include/cuda_device_runtime_api.h" && cp "/usr/local/cuda-9.0/include/cuda_fp16.h" "$(@D)/cuda/include/cuda_fp16.h" && cp "/usr/local/cuda-9.0/include/cuda_fp16.hpp" "$(@D)/cuda/include/cuda_fp16.hpp" && cp "/usr/local/cuda-9.0/include/cuda_gl_interop.h" "$(@D)/cuda/include/cuda_gl_interop.h" && cp "/usr/local/cuda-9.0/include/cuda_occupancy.h" "$(@D)/cuda/include/cuda_occupancy.h" && cp "/usr/local/cuda-9.0/include/cuda_profiler_api.h" "$(@D)/cuda/include/cuda_profiler_api.h" && cp "/usr/local/cuda-9.0/include/cuda_runtime.h" "$(@D)/cuda/include/cuda_runtime.h" && cp "/usr/local/cuda-9.0/include/cuda_runtime_api.h" "$(@D)/cuda/include/cuda_runtime_api.h" && cp "/usr/local/cuda-9.0/include/cuda_surface_types.h" "$(@D)/cuda/include/cuda_surface_types.h" && cp "/usr/local/cuda-9.0/include/cuda_texture_types.h" "$(@D)/cuda/include/cuda_texture_types.h" && cp "/usr/local/cuda-9.0/include/cuda_vdpau_interop.h" "$(@D)/cuda/include/cuda_vdpau_interop.h" && cp "/usr/local/cuda-9.0/include/cudalibxt.h" "$(@D)/cuda/include/cudalibxt.h" && cp "/usr/local/cuda-9.0/include/cufft.h" "$(@D)/cuda/include/cufft.h" && cp "/usr/local/cuda-9.0/include/cufftXt.h" "$(@D)/cuda/include/cufftXt.h" && cp "/usr/local/cuda-9.0/include/cufftw.h" "$(@D)/cuda/include/cufftw.h" && cp "/usr/local/cuda-9.0/include/curand.h" "$(@D)/cuda/include/curand.h" && cp "/usr/local/cuda-9.0/include/curand_discrete.h" "$(@D)/cuda/include/curand_discrete.h" && cp "/usr/local/cuda-9.0/include/curand_discrete2.h" "$(@D)/cuda/include/curand_discrete2.h" && cp "/usr/local/cuda-9.0/include/curand_globals.h" "$(@D)/cuda/include/curand_globals.h" && cp "/usr/local/cuda-9.0/include/curand_kernel.h" "$(@D)/cuda/include/curand_kernel.h" && cp "/usr/local/cuda-9.0/include/curand_lognormal.h" "$(@D)/cuda/include/curand_lognormal.h" && cp "/usr/local/cuda-9.0/include/curand_mrg32k3a.h" "$(@D)/cuda/include/curand_mrg32k3a.h" && cp "/usr/local/cuda-9.0/include/curand_mtgp32.h" "$(@D)/cuda/include/curand_mtgp32.h" && cp "/usr/local/cuda-9.0/include/curand_mtgp32_host.h" "$(@D)/cuda/include/curand_mtgp32_host.h" && cp "/usr/local/cuda-9.0/include/curand_mtgp32_kernel.h" "$(@D)/cuda/include/curand_mtgp32_kernel.h" && cp "/usr/local/cuda-9.0/include/curand_mtgp32dc_p_11213.h" "$(@D)/cuda/include/curand_mtgp32dc_p_11213.h" && cp "/usr/local/cuda-9.0/include/curand_normal.h" "$(@D)/cuda/include/curand_normal.h" && cp "/usr/local/cuda-9.0/include/curand_normal_static.h" "$(@D)/cuda/include/curand_normal_static.h" && cp "/usr/local/cuda-9.0/include/curand_philox4x32_x.h" "$(@D)/cuda/include/curand_philox4x32_x.h" && cp "/usr/local/cuda-9.0/include/curand_poisson.h" "$(@D)/cuda/include/curand_poisson.h" && cp "/usr/local/cuda-9.0/include/curand_precalc.h" "$(@D)/cuda/include/curand_precalc.h" && cp "/usr/local/cuda-9.0/include/curand_uniform.h" "$(@D)/cuda/include/curand_uniform.h" && cp "/usr/local/cuda-9.0/include/cusolverDn.h" "$(@D)/cuda/include/cusolverDn.h" && cp "/usr/local/cuda-9.0/include/cusolverRf.h" "$(@D)/cuda/include/cusolverRf.h" && cp "/usr/local/cuda-9.0/include/cusolverSp.h" "$(@D)/cuda/include/cusolverSp.h" && cp "/usr/local/cuda-9.0/include/cusolverSp_LOWLEVEL_PREVIEW.h" "$(@D)/cuda/include/cusolverSp_LOWLEVEL_PREVIEW.h" && cp "/usr/local/cuda-9.0/include/cusolver_common.h" "$(@D)/cuda/include/cusolver_common.h" && cp "/usr/local/cuda-9.0/include/cusparse.h" "$(@D)/cuda/include/cusparse.h" && cp "/usr/local/cuda-9.0/include/cusparse_v2.h" "$(@D)/cuda/include/cusparse_v2.h" && cp "/usr/local/cuda-9.0/include/device_atomic_functions.h" "$(@D)/cuda/include/device_atomic_functions.h" && cp "/usr/local/cuda-9.0/include/device_atomic_functions.hpp" "$(@D)/cuda/include/device_atomic_functions.hpp" && cp "/usr/local/cuda-9.0/include/device_double_functions.h" "$(@D)/cuda/include/device_double_functions.h" && cp "/usr/local/cuda-9.0/include/device_double_functions.hpp" "$(@D)/cuda/include/device_double_functions.hpp" && cp "/usr/local/cuda-9.0/include/device_functions.h" "$(@D)/cuda/include/device_functions.h" && cp "/usr/local/cuda-9.0/include/device_functions.hpp" "$(@D)/cuda/include/device_functions.hpp" && cp "/usr/local/cuda-9.0/include/device_functions_decls.h" "$(@D)/cuda/include/device_functions_decls.h" && cp "/usr/local/cuda-9.0/include/device_launch_parameters.h" "$(@D)/cuda/include/device_launch_parameters.h" && cp "/usr/local/cuda-9.0/include/device_types.h" "$(@D)/cuda/include/device_types.h" && cp "/usr/local/cuda-9.0/include/driver_functions.h" "$(@D)/cuda/include/driver_functions.h" && cp "/usr/local/cuda-9.0/include/driver_types.h" "$(@D)/cuda/include/driver_types.h" && cp "/usr/local/cuda-9.0/include/dynlink_cuda.h" "$(@D)/cuda/include/dynlink_cuda.h" && cp "/usr/local/cuda-9.0/include/dynlink_cuda_cuda.h" "$(@D)/cuda/include/dynlink_cuda_cuda.h" && cp "/usr/local/cuda-9.0/include/dynlink_cuviddec.h" "$(@D)/cuda/include/dynlink_cuviddec.h" && cp "/usr/local/cuda-9.0/include/dynlink_nvcuvid.h" "$(@D)/cuda/include/dynlink_nvcuvid.h" && cp "/usr/local/cuda-9.0/include/fatBinaryCtl.h" "$(@D)/cuda/include/fatBinaryCtl.h" && cp "/usr/local/cuda-9.0/include/fatbinary.h" "$(@D)/cuda/include/fatbinary.h" && cp "/usr/local/cuda-9.0/include/host_config.h" "$(@D)/cuda/include/host_config.h" && cp "/usr/local/cuda-9.0/include/host_defines.h" "$(@D)/cuda/include/host_defines.h" && cp "/usr/local/cuda-9.0/include/library_types.h" "$(@D)/cuda/include/library_types.h" && cp "/usr/local/cuda-9.0/include/math_constants.h" "$(@D)/cuda/include/math_constants.h" && cp "/usr/local/cuda-9.0/include/math_functions.h" "$(@D)/cuda/include/math_functions.h" && cp "/usr/local/cuda-9.0/include/math_functions.hpp" "$(@D)/cuda/include/math_functions.hpp" && cp "/usr/local/cuda-9.0/include/math_functions_dbl_ptx3.h" "$(@D)/cuda/include/math_functions_dbl_ptx3.h" && cp "/usr/local/cuda-9.0/include/math_functions_dbl_ptx3.hpp" "$(@D)/cuda/include/math_functions_dbl_ptx3.hpp" && cp "/usr/local/cuda-9.0/include/mma.h" "$(@D)/cuda/include/mma.h" && cp "/usr/local/cuda-9.0/include/npp.h" "$(@D)/cuda/include/npp.h" && cp "/usr/local/cuda-9.0/include/nppcore.h" "$(@D)/cuda/include/nppcore.h" && cp "/usr/local/cuda-9.0/include/nppdefs.h" "$(@D)/cuda/include/nppdefs.h" && cp "/usr/local/cuda-9.0/include/nppi.h" "$(@D)/cuda/include/nppi.h" && cp "/usr/local/cuda-9.0/include/nppi_arithmetic_and_logical_operations.h" "$(@D)/cuda/include/nppi_arithmetic_and_logical_operations.h" && cp "/usr/local/cuda-9.0/include/nppi_color_conversion.h" "$(@D)/cuda/include/nppi_color_conversion.h" && cp "/usr/local/cuda-9.0/include/nppi_compression_functions.h" "$(@D)/cuda/include/nppi_compression_functions.h" && cp "/usr/local/cuda-9.0/include/nppi_computer_vision.h" "$(@D)/cuda/include/nppi_computer_vision.h" && cp "/usr/local/cuda-9.0/include/nppi_data_exchange_and_initialization.h" "$(@D)/cuda/include/nppi_data_exchange_and_initialization.h" && cp "/usr/local/cuda-9.0/include/nppi_filtering_functions.h" "$(@D)/cuda/include/nppi_filtering_functions.h" && cp "/usr/local/cuda-9.0/include/nppi_geometry_transforms.h" "$(@D)/cuda/include/nppi_geometry_transforms.h" && cp "/usr/local/cuda-9.0/include/nppi_linear_transforms.h" "$(@D)/cuda/include/nppi_linear_transforms.h" && cp "/usr/local/cuda-9.0/include/nppi_morphological_operations.h" "$(@D)/cuda/include/nppi_morphological_operations.h" && cp "/usr/local/cuda-9.0/include/nppi_statistics_functions.h" "$(@D)/cuda/include/nppi_statistics_functions.h" && cp "/usr/local/cuda-9.0/include/nppi_support_functions.h" "$(@D)/cuda/include/nppi_support_functions.h" && cp "/usr/local/cuda-9.0/include/nppi_threshold_and_compare_operations.h" "$(@D)/cuda/include/nppi_threshold_and_compare_operations.h" && cp "/usr/local/cuda-9.0/include/npps.h" "$(@D)/cuda/include/npps.h" && cp "/usr/local/cuda-9.0/include/npps_arithmetic_and_logical_operations.h" "$(@D)/cuda/include/npps_arithmetic_and_logical_operations.h" && cp "/usr/local/cuda-9.0/include/npps_conversion_functions.h" "$(@D)/cuda/include/npps_conversion_functions.h" && cp "/usr/local/cuda-9.0/include/npps_filtering_functions.h" "$(@D)/cuda/include/npps_filtering_functions.h" && cp "/usr/local/cuda-9.0/include/npps_initialization.h" "$(@D)/cuda/include/npps_initialization.h" && cp "/usr/local/cuda-9.0/include/npps_statistics_functions.h" "$(@D)/cuda/include/npps_statistics_functions.h" && cp "/usr/local/cuda-9.0/include/npps_support_functions.h" "$(@D)/cuda/include/npps_support_functions.h" && cp "/usr/local/cuda-9.0/include/nppversion.h" "$(@D)/cuda/include/nppversion.h" && cp "/usr/local/cuda-9.0/include/nvToolsExt.h" "$(@D)/cuda/include/nvToolsExt.h" && cp "/usr/local/cuda-9.0/include/nvToolsExtCuda.h" "$(@D)/cuda/include/nvToolsExtCuda.h" && cp "/usr/local/cuda-9.0/include/nvToolsExtCudaRt.h" "$(@D)/cuda/include/nvToolsExtCudaRt.h" && cp "/usr/local/cuda-9.0/include/nvToolsExtMeta.h" "$(@D)/cuda/include/nvToolsExtMeta.h" && cp "/usr/local/cuda-9.0/include/nvToolsExtSync.h" "$(@D)/cuda/include/nvToolsExtSync.h" && cp "/usr/local/cuda-9.0/include/nvblas.h" "$(@D)/cuda/include/nvblas.h" && cp "/usr/local/cuda-9.0/include/nvfunctional" "$(@D)/cuda/include/nvfunctional" && cp "/usr/local/cuda-9.0/include/nvgraph.h" "$(@D)/cuda/include/nvgraph.h" && cp "/usr/local/cuda-9.0/include/nvml.h" "$(@D)/cuda/include/nvml.h" && cp "/usr/local/cuda-9.0/include/nvrtc.h" "$(@D)/cuda/include/nvrtc.h" && cp "/usr/local/cuda-9.0/include/sm_20_atomic_functions.h" "$(@D)/cuda/include/sm_20_atomic_functions.h" && cp "/usr/local/cuda-9.0/include/sm_20_atomic_functions.hpp" "$(@D)/cuda/include/sm_20_atomic_functions.hpp" && cp "/usr/local/cuda-9.0/include/sm_20_intrinsics.h" "$(@D)/cuda/include/sm_20_intrinsics.h" && cp "/usr/local/cuda-9.0/include/sm_20_intrinsics.hpp" "$(@D)/cuda/include/sm_20_intrinsics.hpp" && cp "/usr/local/cuda-9.0/include/sm_30_intrinsics.h" "$(@D)/cuda/include/sm_30_intrinsics.h" && cp "/usr/local/cuda-9.0/include/sm_30_intrinsics.hpp" "$(@D)/cuda/include/sm_30_intrinsics.hpp" && cp "/usr/local/cuda-9.0/include/sm_32_atomic_functions.h" "$(@D)/cuda/include/sm_32_atomic_functions.h" && cp "/usr/local/cuda-9.0/include/sm_32_atomic_functions.hpp" "$(@D)/cuda/include/sm_32_atomic_functions.hpp" && cp "/usr/local/cuda-9.0/include/sm_32_intrinsics.h" "$(@D)/cuda/include/sm_32_intrinsics.h" && cp "/usr/local/cuda-9.0/include/sm_32_intrinsics.hpp" "$(@D)/cuda/include/sm_32_intrinsics.hpp" && cp "/usr/local/cuda-9.0/include/sm_35_atomic_functions.h" "$(@D)/cuda/include/sm_35_atomic_functions.h" && cp "/usr/local/cuda-9.0/include/sm_35_intrinsics.h" "$(@D)/cuda/include/sm_35_intrinsics.h" && cp "/usr/local/cuda-9.0/include/sm_60_atomic_functions.h" "$(@D)/cuda/include/sm_60_atomic_functions.h" && cp "/usr/local/cuda-9.0/include/sm_60_atomic_functions.hpp" "$(@D)/cuda/include/sm_60_atomic_functions.hpp" && cp "/usr/local/cuda-9.0/include/sm_61_intrinsics.h" "$(@D)/cuda/include/sm_61_intrinsics.h" && cp "/usr/local/cuda-9.0/include/sm_61_intrinsics.hpp" "$(@D)/cuda/include/sm_61_intrinsics.hpp" && cp "/usr/local/cuda-9.0/include/sobol_direction_vectors.h" "$(@D)/cuda/include/sobol_direction_vectors.h" && cp "/usr/local/cuda-9.0/include/surface_functions.h" "$(@D)/cuda/include/surface_functions.h" && cp "/usr/local/cuda-9.0/include/surface_functions.hpp" "$(@D)/cuda/include/surface_functions.hpp" && cp "/usr/local/cuda-9.0/include/surface_indirect_functions.h" "$(@D)/cuda/include/surface_indirect_functions.h" && cp "/usr/local/cuda-9.0/include/surface_indirect_functions.hpp" "$(@D)/cuda/include/surface_indirect_functions.hpp" && cp "/usr/local/cuda-9.0/include/surface_types.h" "$(@D)/cuda/include/surface_types.h" && cp "/usr/local/cuda-9.0/include/texture_fetch_functions.h" "$(@D)/cuda/include/texture_fetch_functions.h" && cp "/usr/local/cuda-9.0/include/texture_fetch_functions.hpp" "$(@D)/cuda/include/texture_fetch_functions.hpp" && cp "/usr/local/cuda-9.0/include/texture_indirect_functions.h" "$(@D)/cuda/include/texture_indirect_functions.h" && cp "/usr/local/cuda-9.0/include/texture_indirect_functions.hpp" "$(@D)/cuda/include/texture_indirect_functions.hpp" && cp "/usr/local/cuda-9.0/include/texture_types.h" "$(@D)/cuda/include/texture_types.h" && cp "/usr/local/cuda-9.0/include/thrust/adjacent_difference.h" "$(@D)/cuda/include/thrust/adjacent_difference.h" && cp "/usr/local/cuda-9.0/include/thrust/advance.h" "$(@D)/cuda/include/thrust/advance.h" && cp "/usr/local/cuda-9.0/include/thrust/binary_search.h" "$(@D)/cuda/include/thrust/binary_search.h" && cp "/usr/local/cuda-9.0/include/thrust/complex.h" "$(@D)/cuda/include/thrust/complex.h" && cp "/usr/local/cuda-9.0/include/thrust/copy.h" "$(@D)/cuda/include/thrust/copy.h" && cp "/usr/local/cuda-9.0/include/thrust/count.h" "$(@D)/cuda/include/thrust/count.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/adjacent_difference.inl" "$(@D)/cuda/include/thrust/detail/adjacent_difference.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/advance.inl" "$(@D)/cuda/include/thrust/detail/advance.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/allocator/allocator_traits.h" "$(@D)/cuda/include/thrust/detail/allocator/allocator_traits.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/allocator/allocator_traits.inl" "$(@D)/cuda/include/thrust/detail/allocator/allocator_traits.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/allocator/copy_construct_range.h" "$(@D)/cuda/include/thrust/detail/allocator/copy_construct_range.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/allocator/copy_construct_range.inl" "$(@D)/cuda/include/thrust/detail/allocator/copy_construct_range.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/allocator/default_construct_range.h" "$(@D)/cuda/include/thrust/detail/allocator/default_construct_range.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/allocator/default_construct_range.inl" "$(@D)/cuda/include/thrust/detail/allocator/default_construct_range.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/allocator/destroy_range.h" "$(@D)/cuda/include/thrust/detail/allocator/destroy_range.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/allocator/destroy_range.inl" "$(@D)/cuda/include/thrust/detail/allocator/destroy_range.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/allocator/fill_construct_range.h" "$(@D)/cuda/include/thrust/detail/allocator/fill_construct_range.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/allocator/fill_construct_range.inl" "$(@D)/cuda/include/thrust/detail/allocator/fill_construct_range.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/allocator/malloc_allocator.h" "$(@D)/cuda/include/thrust/detail/allocator/malloc_allocator.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/allocator/malloc_allocator.inl" "$(@D)/cuda/include/thrust/detail/allocator/malloc_allocator.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/allocator/no_throw_allocator.h" "$(@D)/cuda/include/thrust/detail/allocator/no_throw_allocator.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/allocator/tagged_allocator.h" "$(@D)/cuda/include/thrust/detail/allocator/tagged_allocator.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/allocator/tagged_allocator.inl" "$(@D)/cuda/include/thrust/detail/allocator/tagged_allocator.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/allocator/temporary_allocator.h" "$(@D)/cuda/include/thrust/detail/allocator/temporary_allocator.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/allocator/temporary_allocator.inl" "$(@D)/cuda/include/thrust/detail/allocator/temporary_allocator.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/binary_search.inl" "$(@D)/cuda/include/thrust/detail/binary_search.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/complex/arithmetic.h" "$(@D)/cuda/include/thrust/detail/complex/arithmetic.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/complex/c99math.h" "$(@D)/cuda/include/thrust/detail/complex/c99math.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/complex/catrig.h" "$(@D)/cuda/include/thrust/detail/complex/catrig.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/complex/catrigf.h" "$(@D)/cuda/include/thrust/detail/complex/catrigf.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/complex/ccosh.h" "$(@D)/cuda/include/thrust/detail/complex/ccosh.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/complex/ccoshf.h" "$(@D)/cuda/include/thrust/detail/complex/ccoshf.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/complex/cexp.h" "$(@D)/cuda/include/thrust/detail/complex/cexp.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/complex/cexpf.h" "$(@D)/cuda/include/thrust/detail/complex/cexpf.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/complex/clog.h" "$(@D)/cuda/include/thrust/detail/complex/clog.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/complex/clogf.h" "$(@D)/cuda/include/thrust/detail/complex/clogf.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/complex/complex.inl" "$(@D)/cuda/include/thrust/detail/complex/complex.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/complex/cpow.h" "$(@D)/cuda/include/thrust/detail/complex/cpow.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/complex/cpowf.h" "$(@D)/cuda/include/thrust/detail/complex/cpowf.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/complex/cproj.h" "$(@D)/cuda/include/thrust/detail/complex/cproj.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/complex/csinh.h" "$(@D)/cuda/include/thrust/detail/complex/csinh.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/complex/csinhf.h" "$(@D)/cuda/include/thrust/detail/complex/csinhf.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/complex/csqrt.h" "$(@D)/cuda/include/thrust/detail/complex/csqrt.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/complex/csqrtf.h" "$(@D)/cuda/include/thrust/detail/complex/csqrtf.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/complex/ctanh.h" "$(@D)/cuda/include/thrust/detail/complex/ctanh.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/complex/ctanhf.h" "$(@D)/cuda/include/thrust/detail/complex/ctanhf.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/complex/math_private.h" "$(@D)/cuda/include/thrust/detail/complex/math_private.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/complex/stream.h" "$(@D)/cuda/include/thrust/detail/complex/stream.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/config.h" "$(@D)/cuda/include/thrust/detail/config.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/config/compiler.h" "$(@D)/cuda/include/thrust/detail/config/compiler.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/config/compiler_fence.h" "$(@D)/cuda/include/thrust/detail/config/compiler_fence.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/config/config.h" "$(@D)/cuda/include/thrust/detail/config/config.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/config/debug.h" "$(@D)/cuda/include/thrust/detail/config/debug.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/config/device_system.h" "$(@D)/cuda/include/thrust/detail/config/device_system.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/config/exec_check_disable.h" "$(@D)/cuda/include/thrust/detail/config/exec_check_disable.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/config/forceinline.h" "$(@D)/cuda/include/thrust/detail/config/forceinline.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/config/global_workarounds.h" "$(@D)/cuda/include/thrust/detail/config/global_workarounds.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/config/host_device.h" "$(@D)/cuda/include/thrust/detail/config/host_device.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/config/host_system.h" "$(@D)/cuda/include/thrust/detail/config/host_system.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/config/simple_defines.h" "$(@D)/cuda/include/thrust/detail/config/simple_defines.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/contiguous_storage.h" "$(@D)/cuda/include/thrust/detail/contiguous_storage.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/contiguous_storage.inl" "$(@D)/cuda/include/thrust/detail/contiguous_storage.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/copy.h" "$(@D)/cuda/include/thrust/detail/copy.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/copy.inl" "$(@D)/cuda/include/thrust/detail/copy.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/copy_if.h" "$(@D)/cuda/include/thrust/detail/copy_if.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/copy_if.inl" "$(@D)/cuda/include/thrust/detail/copy_if.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/count.inl" "$(@D)/cuda/include/thrust/detail/count.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/cstdint.h" "$(@D)/cuda/include/thrust/detail/cstdint.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/device_delete.inl" "$(@D)/cuda/include/thrust/detail/device_delete.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/device_free.inl" "$(@D)/cuda/include/thrust/detail/device_free.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/device_malloc.inl" "$(@D)/cuda/include/thrust/detail/device_malloc.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/device_new.inl" "$(@D)/cuda/include/thrust/detail/device_new.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/device_ptr.inl" "$(@D)/cuda/include/thrust/detail/device_ptr.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/device_reference.inl" "$(@D)/cuda/include/thrust/detail/device_reference.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/device_vector.inl" "$(@D)/cuda/include/thrust/detail/device_vector.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/dispatch/is_trivial_copy.h" "$(@D)/cuda/include/thrust/detail/dispatch/is_trivial_copy.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/distance.inl" "$(@D)/cuda/include/thrust/detail/distance.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/equal.inl" "$(@D)/cuda/include/thrust/detail/equal.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/execute_with_allocator.h" "$(@D)/cuda/include/thrust/detail/execute_with_allocator.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/execution_policy.h" "$(@D)/cuda/include/thrust/detail/execution_policy.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/extrema.inl" "$(@D)/cuda/include/thrust/detail/extrema.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/fill.inl" "$(@D)/cuda/include/thrust/detail/fill.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/find.inl" "$(@D)/cuda/include/thrust/detail/find.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/for_each.inl" "$(@D)/cuda/include/thrust/detail/for_each.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/function.h" "$(@D)/cuda/include/thrust/detail/function.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/functional.inl" "$(@D)/cuda/include/thrust/detail/functional.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/functional/actor.h" "$(@D)/cuda/include/thrust/detail/functional/actor.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/functional/actor.inl" "$(@D)/cuda/include/thrust/detail/functional/actor.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/functional/argument.h" "$(@D)/cuda/include/thrust/detail/functional/argument.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/functional/composite.h" "$(@D)/cuda/include/thrust/detail/functional/composite.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/functional/operators.h" "$(@D)/cuda/include/thrust/detail/functional/operators.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/functional/operators/arithmetic_operators.h" "$(@D)/cuda/include/thrust/detail/functional/operators/arithmetic_operators.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/functional/operators/assignment_operator.h" "$(@D)/cuda/include/thrust/detail/functional/operators/assignment_operator.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/functional/operators/bitwise_operators.h" "$(@D)/cuda/include/thrust/detail/functional/operators/bitwise_operators.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/functional/operators/compound_assignment_operators.h" "$(@D)/cuda/include/thrust/detail/functional/operators/compound_assignment_operators.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/functional/operators/logical_operators.h" "$(@D)/cuda/include/thrust/detail/functional/operators/logical_operators.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/functional/operators/operator_adaptors.h" "$(@D)/cuda/include/thrust/detail/functional/operators/operator_adaptors.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/functional/operators/relational_operators.h" "$(@D)/cuda/include/thrust/detail/functional/operators/relational_operators.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/functional/placeholder.h" "$(@D)/cuda/include/thrust/detail/functional/placeholder.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/functional/value.h" "$(@D)/cuda/include/thrust/detail/functional/value.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/gather.inl" "$(@D)/cuda/include/thrust/detail/gather.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/generate.inl" "$(@D)/cuda/include/thrust/detail/generate.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/get_iterator_value.h" "$(@D)/cuda/include/thrust/detail/get_iterator_value.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/host_vector.inl" "$(@D)/cuda/include/thrust/detail/host_vector.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/inner_product.inl" "$(@D)/cuda/include/thrust/detail/inner_product.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/integer_math.h" "$(@D)/cuda/include/thrust/detail/integer_math.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/integer_traits.h" "$(@D)/cuda/include/thrust/detail/integer_traits.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/internal_functional.h" "$(@D)/cuda/include/thrust/detail/internal_functional.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/logical.inl" "$(@D)/cuda/include/thrust/detail/logical.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/malloc_and_free.h" "$(@D)/cuda/include/thrust/detail/malloc_and_free.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/merge.inl" "$(@D)/cuda/include/thrust/detail/merge.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/minmax.h" "$(@D)/cuda/include/thrust/detail/minmax.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/mismatch.inl" "$(@D)/cuda/include/thrust/detail/mismatch.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/mpl/math.h" "$(@D)/cuda/include/thrust/detail/mpl/math.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/numeric_traits.h" "$(@D)/cuda/include/thrust/detail/numeric_traits.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/overlapped_copy.h" "$(@D)/cuda/include/thrust/detail/overlapped_copy.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/pair.inl" "$(@D)/cuda/include/thrust/detail/pair.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/partition.inl" "$(@D)/cuda/include/thrust/detail/partition.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/pointer.h" "$(@D)/cuda/include/thrust/detail/pointer.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/pointer.inl" "$(@D)/cuda/include/thrust/detail/pointer.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/range/head_flags.h" "$(@D)/cuda/include/thrust/detail/range/head_flags.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/range/tail_flags.h" "$(@D)/cuda/include/thrust/detail/range/tail_flags.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/raw_pointer_cast.h" "$(@D)/cuda/include/thrust/detail/raw_pointer_cast.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/raw_reference_cast.h" "$(@D)/cuda/include/thrust/detail/raw_reference_cast.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/reduce.inl" "$(@D)/cuda/include/thrust/detail/reduce.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/reference.h" "$(@D)/cuda/include/thrust/detail/reference.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/reference.inl" "$(@D)/cuda/include/thrust/detail/reference.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/reference_forward_declaration.h" "$(@D)/cuda/include/thrust/detail/reference_forward_declaration.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/remove.inl" "$(@D)/cuda/include/thrust/detail/remove.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/replace.inl" "$(@D)/cuda/include/thrust/detail/replace.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/reverse.inl" "$(@D)/cuda/include/thrust/detail/reverse.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/scan.inl" "$(@D)/cuda/include/thrust/detail/scan.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/scatter.inl" "$(@D)/cuda/include/thrust/detail/scatter.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/seq.h" "$(@D)/cuda/include/thrust/detail/seq.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/sequence.inl" "$(@D)/cuda/include/thrust/detail/sequence.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/set_operations.inl" "$(@D)/cuda/include/thrust/detail/set_operations.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/sort.inl" "$(@D)/cuda/include/thrust/detail/sort.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/static_assert.h" "$(@D)/cuda/include/thrust/detail/static_assert.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/static_map.h" "$(@D)/cuda/include/thrust/detail/static_map.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/swap.h" "$(@D)/cuda/include/thrust/detail/swap.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/swap.inl" "$(@D)/cuda/include/thrust/detail/swap.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/swap_ranges.inl" "$(@D)/cuda/include/thrust/detail/swap_ranges.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/tabulate.inl" "$(@D)/cuda/include/thrust/detail/tabulate.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/temporary_array.h" "$(@D)/cuda/include/thrust/detail/temporary_array.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/temporary_array.inl" "$(@D)/cuda/include/thrust/detail/temporary_array.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/temporary_buffer.h" "$(@D)/cuda/include/thrust/detail/temporary_buffer.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/transform.inl" "$(@D)/cuda/include/thrust/detail/transform.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/transform_reduce.inl" "$(@D)/cuda/include/thrust/detail/transform_reduce.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/transform_scan.inl" "$(@D)/cuda/include/thrust/detail/transform_scan.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/trivial_sequence.h" "$(@D)/cuda/include/thrust/detail/trivial_sequence.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/tuple.inl" "$(@D)/cuda/include/thrust/detail/tuple.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/tuple_meta_transform.h" "$(@D)/cuda/include/thrust/detail/tuple_meta_transform.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/tuple_transform.h" "$(@D)/cuda/include/thrust/detail/tuple_transform.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/type_traits.h" "$(@D)/cuda/include/thrust/detail/type_traits.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/type_traits/algorithm/intermediate_type_from_function_and_iterators.h" "$(@D)/cuda/include/thrust/detail/type_traits/algorithm/intermediate_type_from_function_and_iterators.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/type_traits/function_traits.h" "$(@D)/cuda/include/thrust/detail/type_traits/function_traits.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/type_traits/has_member_function.h" "$(@D)/cuda/include/thrust/detail/type_traits/has_member_function.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/type_traits/has_nested_type.h" "$(@D)/cuda/include/thrust/detail/type_traits/has_nested_type.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/type_traits/has_trivial_assign.h" "$(@D)/cuda/include/thrust/detail/type_traits/has_trivial_assign.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/type_traits/is_call_possible.h" "$(@D)/cuda/include/thrust/detail/type_traits/is_call_possible.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/type_traits/is_metafunction_defined.h" "$(@D)/cuda/include/thrust/detail/type_traits/is_metafunction_defined.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/type_traits/iterator/is_discard_iterator.h" "$(@D)/cuda/include/thrust/detail/type_traits/iterator/is_discard_iterator.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/type_traits/iterator/is_output_iterator.h" "$(@D)/cuda/include/thrust/detail/type_traits/iterator/is_output_iterator.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/type_traits/minimum_type.h" "$(@D)/cuda/include/thrust/detail/type_traits/minimum_type.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/type_traits/pointer_traits.h" "$(@D)/cuda/include/thrust/detail/type_traits/pointer_traits.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/type_traits/result_of_adaptable_function.h" "$(@D)/cuda/include/thrust/detail/type_traits/result_of_adaptable_function.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/uninitialized_copy.inl" "$(@D)/cuda/include/thrust/detail/uninitialized_copy.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/uninitialized_fill.inl" "$(@D)/cuda/include/thrust/detail/uninitialized_fill.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/unique.inl" "$(@D)/cuda/include/thrust/detail/unique.inl" && cp "/usr/local/cuda-9.0/include/thrust/detail/use_default.h" "$(@D)/cuda/include/thrust/detail/use_default.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/util/align.h" "$(@D)/cuda/include/thrust/detail/util/align.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/util/blocking.h" "$(@D)/cuda/include/thrust/detail/util/blocking.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/vector_base.h" "$(@D)/cuda/include/thrust/detail/vector_base.h" && cp "/usr/local/cuda-9.0/include/thrust/detail/vector_base.inl" "$(@D)/cuda/include/thrust/detail/vector_base.inl" && cp "/usr/local/cuda-9.0/include/thrust/device_allocator.h" "$(@D)/cuda/include/thrust/device_allocator.h" && cp "/usr/local/cuda-9.0/include/thrust/device_delete.h" "$(@D)/cuda/include/thrust/device_delete.h" && cp "/usr/local/cuda-9.0/include/thrust/device_free.h" "$(@D)/cuda/include/thrust/device_free.h" && cp "/usr/local/cuda-9.0/include/thrust/device_malloc.h" "$(@D)/cuda/include/thrust/device_malloc.h" && cp "/usr/local/cuda-9.0/include/thrust/device_malloc_allocator.h" "$(@D)/cuda/include/thrust/device_malloc_allocator.h" && cp "/usr/local/cuda-9.0/include/thrust/device_new.h" "$(@D)/cuda/include/thrust/device_new.h" && cp "/usr/local/cuda-9.0/include/thrust/device_new_allocator.h" "$(@D)/cuda/include/thrust/device_new_allocator.h" && cp "/usr/local/cuda-9.0/include/thrust/device_ptr.h" "$(@D)/cuda/include/thrust/device_ptr.h" && cp "/usr/local/cuda-9.0/include/thrust/device_reference.h" "$(@D)/cuda/include/thrust/device_reference.h" && cp "/usr/local/cuda-9.0/include/thrust/device_vector.h" "$(@D)/cuda/include/thrust/device_vector.h" && cp "/usr/local/cuda-9.0/include/thrust/distance.h" "$(@D)/cuda/include/thrust/distance.h" && cp "/usr/local/cuda-9.0/include/thrust/equal.h" "$(@D)/cuda/include/thrust/equal.h" && cp "/usr/local/cuda-9.0/include/thrust/execution_policy.h" "$(@D)/cuda/include/thrust/execution_policy.h" && cp "/usr/local/cuda-9.0/include/thrust/extrema.h" "$(@D)/cuda/include/thrust/extrema.h" && cp "/usr/local/cuda-9.0/include/thrust/fill.h" "$(@D)/cuda/include/thrust/fill.h" && cp "/usr/local/cuda-9.0/include/thrust/find.h" "$(@D)/cuda/include/thrust/find.h" && cp "/usr/local/cuda-9.0/include/thrust/for_each.h" "$(@D)/cuda/include/thrust/for_each.h" && cp "/usr/local/cuda-9.0/include/thrust/functional.h" "$(@D)/cuda/include/thrust/functional.h" && cp "/usr/local/cuda-9.0/include/thrust/gather.h" "$(@D)/cuda/include/thrust/gather.h" && cp "/usr/local/cuda-9.0/include/thrust/generate.h" "$(@D)/cuda/include/thrust/generate.h" && cp "/usr/local/cuda-9.0/include/thrust/host_vector.h" "$(@D)/cuda/include/thrust/host_vector.h" && cp "/usr/local/cuda-9.0/include/thrust/inner_product.h" "$(@D)/cuda/include/thrust/inner_product.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/constant_iterator.h" "$(@D)/cuda/include/thrust/iterator/constant_iterator.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/counting_iterator.h" "$(@D)/cuda/include/thrust/iterator/counting_iterator.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/any_assign.h" "$(@D)/cuda/include/thrust/iterator/detail/any_assign.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/any_system_tag.h" "$(@D)/cuda/include/thrust/iterator/detail/any_system_tag.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/constant_iterator_base.h" "$(@D)/cuda/include/thrust/iterator/detail/constant_iterator_base.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/counting_iterator.inl" "$(@D)/cuda/include/thrust/iterator/detail/counting_iterator.inl" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/device_system_tag.h" "$(@D)/cuda/include/thrust/iterator/detail/device_system_tag.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/discard_iterator_base.h" "$(@D)/cuda/include/thrust/iterator/detail/discard_iterator_base.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/distance_from_result.h" "$(@D)/cuda/include/thrust/iterator/detail/distance_from_result.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/host_system_tag.h" "$(@D)/cuda/include/thrust/iterator/detail/host_system_tag.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/is_iterator_category.h" "$(@D)/cuda/include/thrust/iterator/detail/is_iterator_category.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/is_trivial_iterator.h" "$(@D)/cuda/include/thrust/iterator/detail/is_trivial_iterator.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/iterator_adaptor_base.h" "$(@D)/cuda/include/thrust/iterator/detail/iterator_adaptor_base.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/iterator_category_to_system.h" "$(@D)/cuda/include/thrust/iterator/detail/iterator_category_to_system.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/iterator_category_to_traversal.h" "$(@D)/cuda/include/thrust/iterator/detail/iterator_category_to_traversal.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/iterator_category_with_system_and_traversal.h" "$(@D)/cuda/include/thrust/iterator/detail/iterator_category_with_system_and_traversal.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/iterator_facade_category.h" "$(@D)/cuda/include/thrust/iterator/detail/iterator_facade_category.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/iterator_traits.inl" "$(@D)/cuda/include/thrust/iterator/detail/iterator_traits.inl" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/iterator_traversal_tags.h" "$(@D)/cuda/include/thrust/iterator/detail/iterator_traversal_tags.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/join_iterator.h" "$(@D)/cuda/include/thrust/iterator/detail/join_iterator.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/minimum_category.h" "$(@D)/cuda/include/thrust/iterator/detail/minimum_category.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/minimum_system.h" "$(@D)/cuda/include/thrust/iterator/detail/minimum_system.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/normal_iterator.h" "$(@D)/cuda/include/thrust/iterator/detail/normal_iterator.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/permutation_iterator_base.h" "$(@D)/cuda/include/thrust/iterator/detail/permutation_iterator_base.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/retag.h" "$(@D)/cuda/include/thrust/iterator/detail/retag.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/reverse_iterator.inl" "$(@D)/cuda/include/thrust/iterator/detail/reverse_iterator.inl" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/reverse_iterator_base.h" "$(@D)/cuda/include/thrust/iterator/detail/reverse_iterator_base.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/tagged_iterator.h" "$(@D)/cuda/include/thrust/iterator/detail/tagged_iterator.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/transform_iterator.inl" "$(@D)/cuda/include/thrust/iterator/detail/transform_iterator.inl" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/transform_output_iterator.inl" "$(@D)/cuda/include/thrust/iterator/detail/transform_output_iterator.inl" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/tuple_of_iterator_references.h" "$(@D)/cuda/include/thrust/iterator/detail/tuple_of_iterator_references.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/universal_categories.h" "$(@D)/cuda/include/thrust/iterator/detail/universal_categories.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/zip_iterator.inl" "$(@D)/cuda/include/thrust/iterator/detail/zip_iterator.inl" && cp "/usr/local/cuda-9.0/include/thrust/iterator/detail/zip_iterator_base.h" "$(@D)/cuda/include/thrust/iterator/detail/zip_iterator_base.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/discard_iterator.h" "$(@D)/cuda/include/thrust/iterator/discard_iterator.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/iterator_adaptor.h" "$(@D)/cuda/include/thrust/iterator/iterator_adaptor.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/iterator_categories.h" "$(@D)/cuda/include/thrust/iterator/iterator_categories.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/iterator_facade.h" "$(@D)/cuda/include/thrust/iterator/iterator_facade.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/iterator_traits.h" "$(@D)/cuda/include/thrust/iterator/iterator_traits.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/permutation_iterator.h" "$(@D)/cuda/include/thrust/iterator/permutation_iterator.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/retag.h" "$(@D)/cuda/include/thrust/iterator/retag.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/reverse_iterator.h" "$(@D)/cuda/include/thrust/iterator/reverse_iterator.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/transform_iterator.h" "$(@D)/cuda/include/thrust/iterator/transform_iterator.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/transform_output_iterator.h" "$(@D)/cuda/include/thrust/iterator/transform_output_iterator.h" && cp "/usr/local/cuda-9.0/include/thrust/iterator/zip_iterator.h" "$(@D)/cuda/include/thrust/iterator/zip_iterator.h" && cp "/usr/local/cuda-9.0/include/thrust/logical.h" "$(@D)/cuda/include/thrust/logical.h" && cp "/usr/local/cuda-9.0/include/thrust/memory.h" "$(@D)/cuda/include/thrust/memory.h" && cp "/usr/local/cuda-9.0/include/thrust/merge.h" "$(@D)/cuda/include/thrust/merge.h" && cp "/usr/local/cuda-9.0/include/thrust/mismatch.h" "$(@D)/cuda/include/thrust/mismatch.h" && cp "/usr/local/cuda-9.0/include/thrust/pair.h" "$(@D)/cuda/include/thrust/pair.h" && cp "/usr/local/cuda-9.0/include/thrust/partition.h" "$(@D)/cuda/include/thrust/partition.h" && cp "/usr/local/cuda-9.0/include/thrust/random.h" "$(@D)/cuda/include/thrust/random.h" && cp "/usr/local/cuda-9.0/include/thrust/random/detail/discard_block_engine.inl" "$(@D)/cuda/include/thrust/random/detail/discard_block_engine.inl" && cp "/usr/local/cuda-9.0/include/thrust/random/detail/linear_congruential_engine.inl" "$(@D)/cuda/include/thrust/random/detail/linear_congruential_engine.inl" && cp "/usr/local/cuda-9.0/include/thrust/random/detail/linear_congruential_engine_discard.h" "$(@D)/cuda/include/thrust/random/detail/linear_congruential_engine_discard.h" && cp "/usr/local/cuda-9.0/include/thrust/random/detail/linear_feedback_shift_engine.inl" "$(@D)/cuda/include/thrust/random/detail/linear_feedback_shift_engine.inl" && cp "/usr/local/cuda-9.0/include/thrust/random/detail/linear_feedback_shift_engine_wordmask.h" "$(@D)/cuda/include/thrust/random/detail/linear_feedback_shift_engine_wordmask.h" && cp "/usr/local/cuda-9.0/include/thrust/random/detail/mod.h" "$(@D)/cuda/include/thrust/random/detail/mod.h" && cp "/usr/local/cuda-9.0/include/thrust/random/detail/normal_distribution.inl" "$(@D)/cuda/include/thrust/random/detail/normal_distribution.inl" && cp "/usr/local/cuda-9.0/include/thrust/random/detail/normal_distribution_base.h" "$(@D)/cuda/include/thrust/random/detail/normal_distribution_base.h" && cp "/usr/local/cuda-9.0/include/thrust/random/detail/random_core_access.h" "$(@D)/cuda/include/thrust/random/detail/random_core_access.h" && cp "/usr/local/cuda-9.0/include/thrust/random/detail/subtract_with_carry_engine.inl" "$(@D)/cuda/include/thrust/random/detail/subtract_with_carry_engine.inl" && cp "/usr/local/cuda-9.0/include/thrust/random/detail/uniform_int_distribution.inl" "$(@D)/cuda/include/thrust/random/detail/uniform_int_distribution.inl" && cp "/usr/local/cuda-9.0/include/thrust/random/detail/uniform_real_distribution.inl" "$(@D)/cuda/include/thrust/random/detail/uniform_real_distribution.inl" && cp "/usr/local/cuda-9.0/include/thrust/random/detail/xor_combine_engine.inl" "$(@D)/cuda/include/thrust/random/detail/xor_combine_engine.inl" && cp "/usr/local/cuda-9.0/include/thrust/random/detail/xor_combine_engine_max.h" "$(@D)/cuda/include/thrust/random/detail/xor_combine_engine_max.h" && cp "/usr/local/cuda-9.0/include/thrust/random/discard_block_engine.h" "$(@D)/cuda/include/thrust/random/discard_block_engine.h" && cp "/usr/local/cuda-9.0/include/thrust/random/linear_congruential_engine.h" "$(@D)/cuda/include/thrust/random/linear_congruential_engine.h" && cp "/usr/local/cuda-9.0/include/thrust/random/linear_feedback_shift_engine.h" "$(@D)/cuda/include/thrust/random/linear_feedback_shift_engine.h" && cp "/usr/local/cuda-9.0/include/thrust/random/normal_distribution.h" "$(@D)/cuda/include/thrust/random/normal_distribution.h" && cp "/usr/local/cuda-9.0/include/thrust/random/subtract_with_carry_engine.h" "$(@D)/cuda/include/thrust/random/subtract_with_carry_engine.h" && cp "/usr/local/cuda-9.0/include/thrust/random/uniform_int_distribution.h" "$(@D)/cuda/include/thrust/random/uniform_int_distribution.h" && cp "/usr/local/cuda-9.0/include/thrust/random/uniform_real_distribution.h" "$(@D)/cuda/include/thrust/random/uniform_real_distribution.h" && cp "/usr/local/cuda-9.0/include/thrust/random/xor_combine_engine.h" "$(@D)/cuda/include/thrust/random/xor_combine_engine.h" && cp "/usr/local/cuda-9.0/include/thrust/reduce.h" "$(@D)/cuda/include/thrust/reduce.h" && cp "/usr/local/cuda-9.0/include/thrust/remove.h" "$(@D)/cuda/include/thrust/remove.h" && cp "/usr/local/cuda-9.0/include/thrust/replace.h" "$(@D)/cuda/include/thrust/replace.h" && cp "/usr/local/cuda-9.0/include/thrust/reverse.h" "$(@D)/cuda/include/thrust/reverse.h" && cp "/usr/local/cuda-9.0/include/thrust/scan.h" "$(@D)/cuda/include/thrust/scan.h" && cp "/usr/local/cuda-9.0/include/thrust/scatter.h" "$(@D)/cuda/include/thrust/scatter.h" && cp "/usr/local/cuda-9.0/include/thrust/sequence.h" "$(@D)/cuda/include/thrust/sequence.h" && cp "/usr/local/cuda-9.0/include/thrust/set_operations.h" "$(@D)/cuda/include/thrust/set_operations.h" && cp "/usr/local/cuda-9.0/include/thrust/sort.h" "$(@D)/cuda/include/thrust/sort.h" && cp "/usr/local/cuda-9.0/include/thrust/swap.h" "$(@D)/cuda/include/thrust/swap.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/adjacent_difference.h" "$(@D)/cuda/include/thrust/system/cpp/detail/adjacent_difference.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/assign_value.h" "$(@D)/cuda/include/thrust/system/cpp/detail/assign_value.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/binary_search.h" "$(@D)/cuda/include/thrust/system/cpp/detail/binary_search.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/copy.h" "$(@D)/cuda/include/thrust/system/cpp/detail/copy.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/copy_if.h" "$(@D)/cuda/include/thrust/system/cpp/detail/copy_if.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/count.h" "$(@D)/cuda/include/thrust/system/cpp/detail/count.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/equal.h" "$(@D)/cuda/include/thrust/system/cpp/detail/equal.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/execution_policy.h" "$(@D)/cuda/include/thrust/system/cpp/detail/execution_policy.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/extrema.h" "$(@D)/cuda/include/thrust/system/cpp/detail/extrema.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/fill.h" "$(@D)/cuda/include/thrust/system/cpp/detail/fill.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/find.h" "$(@D)/cuda/include/thrust/system/cpp/detail/find.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/for_each.h" "$(@D)/cuda/include/thrust/system/cpp/detail/for_each.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/gather.h" "$(@D)/cuda/include/thrust/system/cpp/detail/gather.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/generate.h" "$(@D)/cuda/include/thrust/system/cpp/detail/generate.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/get_value.h" "$(@D)/cuda/include/thrust/system/cpp/detail/get_value.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/inner_product.h" "$(@D)/cuda/include/thrust/system/cpp/detail/inner_product.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/iter_swap.h" "$(@D)/cuda/include/thrust/system/cpp/detail/iter_swap.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/logical.h" "$(@D)/cuda/include/thrust/system/cpp/detail/logical.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/malloc_and_free.h" "$(@D)/cuda/include/thrust/system/cpp/detail/malloc_and_free.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/memory.inl" "$(@D)/cuda/include/thrust/system/cpp/detail/memory.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/merge.h" "$(@D)/cuda/include/thrust/system/cpp/detail/merge.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/mismatch.h" "$(@D)/cuda/include/thrust/system/cpp/detail/mismatch.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/par.h" "$(@D)/cuda/include/thrust/system/cpp/detail/par.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/partition.h" "$(@D)/cuda/include/thrust/system/cpp/detail/partition.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/reduce.h" "$(@D)/cuda/include/thrust/system/cpp/detail/reduce.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/reduce_by_key.h" "$(@D)/cuda/include/thrust/system/cpp/detail/reduce_by_key.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/remove.h" "$(@D)/cuda/include/thrust/system/cpp/detail/remove.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/replace.h" "$(@D)/cuda/include/thrust/system/cpp/detail/replace.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/reverse.h" "$(@D)/cuda/include/thrust/system/cpp/detail/reverse.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/scan.h" "$(@D)/cuda/include/thrust/system/cpp/detail/scan.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/scan_by_key.h" "$(@D)/cuda/include/thrust/system/cpp/detail/scan_by_key.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/scatter.h" "$(@D)/cuda/include/thrust/system/cpp/detail/scatter.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/sequence.h" "$(@D)/cuda/include/thrust/system/cpp/detail/sequence.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/set_operations.h" "$(@D)/cuda/include/thrust/system/cpp/detail/set_operations.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/sort.h" "$(@D)/cuda/include/thrust/system/cpp/detail/sort.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/swap_ranges.h" "$(@D)/cuda/include/thrust/system/cpp/detail/swap_ranges.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/tabulate.h" "$(@D)/cuda/include/thrust/system/cpp/detail/tabulate.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/temporary_buffer.h" "$(@D)/cuda/include/thrust/system/cpp/detail/temporary_buffer.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/transform.h" "$(@D)/cuda/include/thrust/system/cpp/detail/transform.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/transform_reduce.h" "$(@D)/cuda/include/thrust/system/cpp/detail/transform_reduce.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/transform_scan.h" "$(@D)/cuda/include/thrust/system/cpp/detail/transform_scan.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/uninitialized_copy.h" "$(@D)/cuda/include/thrust/system/cpp/detail/uninitialized_copy.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/uninitialized_fill.h" "$(@D)/cuda/include/thrust/system/cpp/detail/uninitialized_fill.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/unique.h" "$(@D)/cuda/include/thrust/system/cpp/detail/unique.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/unique_by_key.h" "$(@D)/cuda/include/thrust/system/cpp/detail/unique_by_key.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/vector.inl" "$(@D)/cuda/include/thrust/system/cpp/detail/vector.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/execution_policy.h" "$(@D)/cuda/include/thrust/system/cpp/execution_policy.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/memory.h" "$(@D)/cuda/include/thrust/system/cpp/memory.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cpp/vector.h" "$(@D)/cuda/include/thrust/system/cpp/vector.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/config.h" "$(@D)/cuda/include/thrust/system/cuda/config.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/adjacent_difference.h" "$(@D)/cuda/include/thrust/system/cuda/detail/adjacent_difference.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/assign_value.h" "$(@D)/cuda/include/thrust/system/cuda/detail/assign_value.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/binary_search.h" "$(@D)/cuda/include/thrust/system/cuda/detail/binary_search.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/copy.h" "$(@D)/cuda/include/thrust/system/cuda/detail/copy.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/copy_if.h" "$(@D)/cuda/include/thrust/system/cuda/detail/copy_if.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/core/agent_launcher.h" "$(@D)/cuda/include/thrust/system/cuda/detail/core/agent_launcher.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/core/alignment.h" "$(@D)/cuda/include/thrust/system/cuda/detail/core/alignment.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/core/triple_chevron_launch.h" "$(@D)/cuda/include/thrust/system/cuda/detail/core/triple_chevron_launch.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/core/util.h" "$(@D)/cuda/include/thrust/system/cuda/detail/core/util.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/count.h" "$(@D)/cuda/include/thrust/system/cuda/detail/count.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cross_system.h" "$(@D)/cuda/include/thrust/system/cuda/detail/cross_system.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/agent/agent_histogram.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_histogram.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/agent/agent_radix_sort_downsweep.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_radix_sort_downsweep.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/agent/agent_radix_sort_upsweep.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_radix_sort_upsweep.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/agent/agent_reduce.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_reduce.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/agent/agent_reduce_by_key.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_reduce_by_key.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/agent/agent_rle.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_rle.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/agent/agent_scan.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_scan.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/agent/agent_segment_fixup.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_segment_fixup.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/agent/agent_select_if.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_select_if.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/agent/agent_spmv_csrt.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_spmv_csrt.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/agent/agent_spmv_orig.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_spmv_orig.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/agent/agent_spmv_row_based.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_spmv_row_based.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/agent/single_pass_scan_operators.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/single_pass_scan_operators.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/block_adjacent_difference.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_adjacent_difference.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/block_discontinuity.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_discontinuity.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/block_exchange.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_exchange.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/block_histogram.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_histogram.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/block_load.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_load.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/block_radix_rank.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_radix_rank.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/block_radix_sort.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_radix_sort.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/block_raking_layout.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_raking_layout.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/block_reduce.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_reduce.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/block_scan.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_scan.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/block_shuffle.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_shuffle.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/block_store.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_store.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/specializations/block_histogram_atomic.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_histogram_atomic.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/specializations/block_histogram_sort.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_histogram_sort.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/specializations/block_reduce_raking.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_reduce_raking.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/specializations/block_reduce_raking_commutative_only.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_reduce_raking_commutative_only.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/specializations/block_reduce_warp_reductions.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_reduce_warp_reductions.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_raking.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_raking.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_warp_scans.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_warp_scans.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_warp_scans2.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_warp_scans2.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_warp_scans3.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_warp_scans3.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/cub.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/cub.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/device_histogram.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_histogram.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/device_partition.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_partition.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/device_radix_sort.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_radix_sort.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/device_reduce.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_reduce.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/device_run_length_encode.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_run_length_encode.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/device_scan.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_scan.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/device_segmented_radix_sort.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_segmented_radix_sort.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/device_segmented_reduce.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_segmented_reduce.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/device_select.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_select.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/device_spmv.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_spmv.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_histogram.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_histogram.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_radix_sort.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_radix_sort.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_reduce.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_reduce.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_reduce_by_key.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_reduce_by_key.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_rle.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_rle.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_scan.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_scan.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_select_if.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_select_if.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_spmv_csrt.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_spmv_csrt.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_spmv_orig.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_spmv_orig.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_spmv_row_based.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_spmv_row_based.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/grid/grid_barrier.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/grid/grid_barrier.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/grid/grid_even_share.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/grid/grid_even_share.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/grid/grid_mapping.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/grid/grid_mapping.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/grid/grid_queue.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/grid/grid_queue.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/host/mutex.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/host/mutex.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/iterator/arg_index_input_iterator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/iterator/arg_index_input_iterator.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/iterator/cache_modified_input_iterator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/iterator/cache_modified_input_iterator.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/iterator/cache_modified_output_iterator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/iterator/cache_modified_output_iterator.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/iterator/constant_input_iterator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/iterator/constant_input_iterator.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/iterator/counting_input_iterator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/iterator/counting_input_iterator.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/iterator/discard_output_iterator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/iterator/discard_output_iterator.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/iterator/tex_obj_input_iterator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/iterator/tex_obj_input_iterator.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/iterator/tex_ref_input_iterator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/iterator/tex_ref_input_iterator.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/iterator/transform_input_iterator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/iterator/transform_input_iterator.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/thread/thread_load.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/thread/thread_load.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/thread/thread_operators.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/thread/thread_operators.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/thread/thread_reduce.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/thread/thread_reduce.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/thread/thread_scan.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/thread/thread_scan.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/thread/thread_search.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/thread/thread_search.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/thread/thread_store.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/thread/thread_store.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/util_allocator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/util_allocator.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/util_arch.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/util_arch.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/util_debug.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/util_debug.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/util_device.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/util_device.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/util_macro.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/util_macro.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/util_namespace.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/util_namespace.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/util_ptx.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/util_ptx.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/util_type.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/util_type.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/warp/specializations/warp_reduce_shfl.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/warp/specializations/warp_reduce_shfl.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/warp/specializations/warp_reduce_smem.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/warp/specializations/warp_reduce_smem.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/warp/specializations/warp_scan_shfl.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/warp/specializations/warp_scan_shfl.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/warp/specializations/warp_scan_smem.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/warp/specializations/warp_scan_smem.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/warp/warp_reduce.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/warp/warp_reduce.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/warp/warp_scan.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/warp/warp_scan.cuh" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/equal.h" "$(@D)/cuda/include/thrust/system/cuda/detail/equal.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/error.inl" "$(@D)/cuda/include/thrust/system/cuda/detail/error.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/execution_policy.h" "$(@D)/cuda/include/thrust/system/cuda/detail/execution_policy.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/extrema.h" "$(@D)/cuda/include/thrust/system/cuda/detail/extrema.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/fill.h" "$(@D)/cuda/include/thrust/system/cuda/detail/fill.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/find.h" "$(@D)/cuda/include/thrust/system/cuda/detail/find.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/for_each.h" "$(@D)/cuda/include/thrust/system/cuda/detail/for_each.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/gather.h" "$(@D)/cuda/include/thrust/system/cuda/detail/gather.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/generate.h" "$(@D)/cuda/include/thrust/system/cuda/detail/generate.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/get_value.h" "$(@D)/cuda/include/thrust/system/cuda/detail/get_value.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/guarded_cuda_runtime_api.h" "$(@D)/cuda/include/thrust/system/cuda/detail/guarded_cuda_runtime_api.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/guarded_driver_types.h" "$(@D)/cuda/include/thrust/system/cuda/detail/guarded_driver_types.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/inner_product.h" "$(@D)/cuda/include/thrust/system/cuda/detail/inner_product.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/internal/copy_cross_system.h" "$(@D)/cuda/include/thrust/system/cuda/detail/internal/copy_cross_system.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/internal/copy_device_to_device.h" "$(@D)/cuda/include/thrust/system/cuda/detail/internal/copy_device_to_device.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/iter_swap.h" "$(@D)/cuda/include/thrust/system/cuda/detail/iter_swap.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/logical.h" "$(@D)/cuda/include/thrust/system/cuda/detail/logical.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/malloc_and_free.h" "$(@D)/cuda/include/thrust/system/cuda/detail/malloc_and_free.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/memory.inl" "$(@D)/cuda/include/thrust/system/cuda/detail/memory.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/memory_buffer.h" "$(@D)/cuda/include/thrust/system/cuda/detail/memory_buffer.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/merge.h" "$(@D)/cuda/include/thrust/system/cuda/detail/merge.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/mismatch.h" "$(@D)/cuda/include/thrust/system/cuda/detail/mismatch.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/par.h" "$(@D)/cuda/include/thrust/system/cuda/detail/par.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/par_to_seq.h" "$(@D)/cuda/include/thrust/system/cuda/detail/par_to_seq.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/parallel_for.h" "$(@D)/cuda/include/thrust/system/cuda/detail/parallel_for.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/partition.h" "$(@D)/cuda/include/thrust/system/cuda/detail/partition.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/reduce.h" "$(@D)/cuda/include/thrust/system/cuda/detail/reduce.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/reduce_by_key.h" "$(@D)/cuda/include/thrust/system/cuda/detail/reduce_by_key.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/remove.h" "$(@D)/cuda/include/thrust/system/cuda/detail/remove.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/replace.h" "$(@D)/cuda/include/thrust/system/cuda/detail/replace.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/reverse.h" "$(@D)/cuda/include/thrust/system/cuda/detail/reverse.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/scan.h" "$(@D)/cuda/include/thrust/system/cuda/detail/scan.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/scan_by_key.h" "$(@D)/cuda/include/thrust/system/cuda/detail/scan_by_key.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/scatter.h" "$(@D)/cuda/include/thrust/system/cuda/detail/scatter.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/sequence.h" "$(@D)/cuda/include/thrust/system/cuda/detail/sequence.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/set_operations.h" "$(@D)/cuda/include/thrust/system/cuda/detail/set_operations.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/sort.h" "$(@D)/cuda/include/thrust/system/cuda/detail/sort.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/swap_ranges.h" "$(@D)/cuda/include/thrust/system/cuda/detail/swap_ranges.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/tabulate.h" "$(@D)/cuda/include/thrust/system/cuda/detail/tabulate.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/temporary_buffer.h" "$(@D)/cuda/include/thrust/system/cuda/detail/temporary_buffer.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/terminate.h" "$(@D)/cuda/include/thrust/system/cuda/detail/terminate.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/transform.h" "$(@D)/cuda/include/thrust/system/cuda/detail/transform.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/transform_reduce.h" "$(@D)/cuda/include/thrust/system/cuda/detail/transform_reduce.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/transform_scan.h" "$(@D)/cuda/include/thrust/system/cuda/detail/transform_scan.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/uninitialized_copy.h" "$(@D)/cuda/include/thrust/system/cuda/detail/uninitialized_copy.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/uninitialized_fill.h" "$(@D)/cuda/include/thrust/system/cuda/detail/uninitialized_fill.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/unique.h" "$(@D)/cuda/include/thrust/system/cuda/detail/unique.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/unique_by_key.h" "$(@D)/cuda/include/thrust/system/cuda/detail/unique_by_key.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/util.h" "$(@D)/cuda/include/thrust/system/cuda/detail/util.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/vector.inl" "$(@D)/cuda/include/thrust/system/cuda/detail/vector.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/error.h" "$(@D)/cuda/include/thrust/system/cuda/error.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/execution_policy.h" "$(@D)/cuda/include/thrust/system/cuda/execution_policy.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/experimental/pinned_allocator.h" "$(@D)/cuda/include/thrust/system/cuda/experimental/pinned_allocator.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/memory.h" "$(@D)/cuda/include/thrust/system/cuda/memory.h" && cp "/usr/local/cuda-9.0/include/thrust/system/cuda/vector.h" "$(@D)/cuda/include/thrust/system/cuda/vector.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/adjacent_difference.h" "$(@D)/cuda/include/thrust/system/detail/adl/adjacent_difference.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/assign_value.h" "$(@D)/cuda/include/thrust/system/detail/adl/assign_value.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/binary_search.h" "$(@D)/cuda/include/thrust/system/detail/adl/binary_search.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/copy.h" "$(@D)/cuda/include/thrust/system/detail/adl/copy.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/copy_if.h" "$(@D)/cuda/include/thrust/system/detail/adl/copy_if.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/count.h" "$(@D)/cuda/include/thrust/system/detail/adl/count.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/equal.h" "$(@D)/cuda/include/thrust/system/detail/adl/equal.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/extrema.h" "$(@D)/cuda/include/thrust/system/detail/adl/extrema.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/fill.h" "$(@D)/cuda/include/thrust/system/detail/adl/fill.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/find.h" "$(@D)/cuda/include/thrust/system/detail/adl/find.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/for_each.h" "$(@D)/cuda/include/thrust/system/detail/adl/for_each.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/gather.h" "$(@D)/cuda/include/thrust/system/detail/adl/gather.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/generate.h" "$(@D)/cuda/include/thrust/system/detail/adl/generate.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/get_value.h" "$(@D)/cuda/include/thrust/system/detail/adl/get_value.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/inner_product.h" "$(@D)/cuda/include/thrust/system/detail/adl/inner_product.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/iter_swap.h" "$(@D)/cuda/include/thrust/system/detail/adl/iter_swap.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/logical.h" "$(@D)/cuda/include/thrust/system/detail/adl/logical.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/malloc_and_free.h" "$(@D)/cuda/include/thrust/system/detail/adl/malloc_and_free.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/merge.h" "$(@D)/cuda/include/thrust/system/detail/adl/merge.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/mismatch.h" "$(@D)/cuda/include/thrust/system/detail/adl/mismatch.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/partition.h" "$(@D)/cuda/include/thrust/system/detail/adl/partition.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/reduce.h" "$(@D)/cuda/include/thrust/system/detail/adl/reduce.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/reduce_by_key.h" "$(@D)/cuda/include/thrust/system/detail/adl/reduce_by_key.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/remove.h" "$(@D)/cuda/include/thrust/system/detail/adl/remove.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/replace.h" "$(@D)/cuda/include/thrust/system/detail/adl/replace.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/reverse.h" "$(@D)/cuda/include/thrust/system/detail/adl/reverse.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/scan.h" "$(@D)/cuda/include/thrust/system/detail/adl/scan.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/scan_by_key.h" "$(@D)/cuda/include/thrust/system/detail/adl/scan_by_key.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/scatter.h" "$(@D)/cuda/include/thrust/system/detail/adl/scatter.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/sequence.h" "$(@D)/cuda/include/thrust/system/detail/adl/sequence.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/set_operations.h" "$(@D)/cuda/include/thrust/system/detail/adl/set_operations.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/sort.h" "$(@D)/cuda/include/thrust/system/detail/adl/sort.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/swap_ranges.h" "$(@D)/cuda/include/thrust/system/detail/adl/swap_ranges.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/tabulate.h" "$(@D)/cuda/include/thrust/system/detail/adl/tabulate.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/temporary_buffer.h" "$(@D)/cuda/include/thrust/system/detail/adl/temporary_buffer.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/transform.h" "$(@D)/cuda/include/thrust/system/detail/adl/transform.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/transform_reduce.h" "$(@D)/cuda/include/thrust/system/detail/adl/transform_reduce.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/transform_scan.h" "$(@D)/cuda/include/thrust/system/detail/adl/transform_scan.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/uninitialized_copy.h" "$(@D)/cuda/include/thrust/system/detail/adl/uninitialized_copy.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/uninitialized_fill.h" "$(@D)/cuda/include/thrust/system/detail/adl/uninitialized_fill.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/unique.h" "$(@D)/cuda/include/thrust/system/detail/adl/unique.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/adl/unique_by_key.h" "$(@D)/cuda/include/thrust/system/detail/adl/unique_by_key.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/bad_alloc.h" "$(@D)/cuda/include/thrust/system/detail/bad_alloc.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/errno.h" "$(@D)/cuda/include/thrust/system/detail/errno.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/error_category.inl" "$(@D)/cuda/include/thrust/system/detail/error_category.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/error_code.inl" "$(@D)/cuda/include/thrust/system/detail/error_code.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/error_condition.inl" "$(@D)/cuda/include/thrust/system/detail/error_condition.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/adjacent_difference.h" "$(@D)/cuda/include/thrust/system/detail/generic/adjacent_difference.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/adjacent_difference.inl" "$(@D)/cuda/include/thrust/system/detail/generic/adjacent_difference.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/advance.h" "$(@D)/cuda/include/thrust/system/detail/generic/advance.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/advance.inl" "$(@D)/cuda/include/thrust/system/detail/generic/advance.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/binary_search.h" "$(@D)/cuda/include/thrust/system/detail/generic/binary_search.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/binary_search.inl" "$(@D)/cuda/include/thrust/system/detail/generic/binary_search.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/copy.h" "$(@D)/cuda/include/thrust/system/detail/generic/copy.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/copy.inl" "$(@D)/cuda/include/thrust/system/detail/generic/copy.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/copy_if.h" "$(@D)/cuda/include/thrust/system/detail/generic/copy_if.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/copy_if.inl" "$(@D)/cuda/include/thrust/system/detail/generic/copy_if.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/count.h" "$(@D)/cuda/include/thrust/system/detail/generic/count.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/count.inl" "$(@D)/cuda/include/thrust/system/detail/generic/count.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/distance.h" "$(@D)/cuda/include/thrust/system/detail/generic/distance.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/distance.inl" "$(@D)/cuda/include/thrust/system/detail/generic/distance.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/equal.h" "$(@D)/cuda/include/thrust/system/detail/generic/equal.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/equal.inl" "$(@D)/cuda/include/thrust/system/detail/generic/equal.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/extrema.h" "$(@D)/cuda/include/thrust/system/detail/generic/extrema.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/extrema.inl" "$(@D)/cuda/include/thrust/system/detail/generic/extrema.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/fill.h" "$(@D)/cuda/include/thrust/system/detail/generic/fill.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/find.h" "$(@D)/cuda/include/thrust/system/detail/generic/find.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/find.inl" "$(@D)/cuda/include/thrust/system/detail/generic/find.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/for_each.h" "$(@D)/cuda/include/thrust/system/detail/generic/for_each.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/gather.h" "$(@D)/cuda/include/thrust/system/detail/generic/gather.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/gather.inl" "$(@D)/cuda/include/thrust/system/detail/generic/gather.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/generate.h" "$(@D)/cuda/include/thrust/system/detail/generic/generate.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/generate.inl" "$(@D)/cuda/include/thrust/system/detail/generic/generate.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/inner_product.h" "$(@D)/cuda/include/thrust/system/detail/generic/inner_product.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/inner_product.inl" "$(@D)/cuda/include/thrust/system/detail/generic/inner_product.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/logical.h" "$(@D)/cuda/include/thrust/system/detail/generic/logical.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/memory.h" "$(@D)/cuda/include/thrust/system/detail/generic/memory.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/memory.inl" "$(@D)/cuda/include/thrust/system/detail/generic/memory.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/merge.h" "$(@D)/cuda/include/thrust/system/detail/generic/merge.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/merge.inl" "$(@D)/cuda/include/thrust/system/detail/generic/merge.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/mismatch.h" "$(@D)/cuda/include/thrust/system/detail/generic/mismatch.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/mismatch.inl" "$(@D)/cuda/include/thrust/system/detail/generic/mismatch.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/partition.h" "$(@D)/cuda/include/thrust/system/detail/generic/partition.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/partition.inl" "$(@D)/cuda/include/thrust/system/detail/generic/partition.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/reduce.h" "$(@D)/cuda/include/thrust/system/detail/generic/reduce.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/reduce.inl" "$(@D)/cuda/include/thrust/system/detail/generic/reduce.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/reduce_by_key.h" "$(@D)/cuda/include/thrust/system/detail/generic/reduce_by_key.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/reduce_by_key.inl" "$(@D)/cuda/include/thrust/system/detail/generic/reduce_by_key.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/remove.h" "$(@D)/cuda/include/thrust/system/detail/generic/remove.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/remove.inl" "$(@D)/cuda/include/thrust/system/detail/generic/remove.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/replace.h" "$(@D)/cuda/include/thrust/system/detail/generic/replace.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/replace.inl" "$(@D)/cuda/include/thrust/system/detail/generic/replace.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/reverse.h" "$(@D)/cuda/include/thrust/system/detail/generic/reverse.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/reverse.inl" "$(@D)/cuda/include/thrust/system/detail/generic/reverse.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/scalar/binary_search.h" "$(@D)/cuda/include/thrust/system/detail/generic/scalar/binary_search.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/scalar/binary_search.inl" "$(@D)/cuda/include/thrust/system/detail/generic/scalar/binary_search.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/scan.h" "$(@D)/cuda/include/thrust/system/detail/generic/scan.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/scan.inl" "$(@D)/cuda/include/thrust/system/detail/generic/scan.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/scan_by_key.h" "$(@D)/cuda/include/thrust/system/detail/generic/scan_by_key.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/scan_by_key.inl" "$(@D)/cuda/include/thrust/system/detail/generic/scan_by_key.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/scatter.h" "$(@D)/cuda/include/thrust/system/detail/generic/scatter.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/scatter.inl" "$(@D)/cuda/include/thrust/system/detail/generic/scatter.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/select_system.h" "$(@D)/cuda/include/thrust/system/detail/generic/select_system.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/sequence.h" "$(@D)/cuda/include/thrust/system/detail/generic/sequence.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/sequence.inl" "$(@D)/cuda/include/thrust/system/detail/generic/sequence.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/set_operations.h" "$(@D)/cuda/include/thrust/system/detail/generic/set_operations.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/set_operations.inl" "$(@D)/cuda/include/thrust/system/detail/generic/set_operations.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/sort.h" "$(@D)/cuda/include/thrust/system/detail/generic/sort.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/sort.inl" "$(@D)/cuda/include/thrust/system/detail/generic/sort.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/swap_ranges.h" "$(@D)/cuda/include/thrust/system/detail/generic/swap_ranges.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/swap_ranges.inl" "$(@D)/cuda/include/thrust/system/detail/generic/swap_ranges.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/tabulate.h" "$(@D)/cuda/include/thrust/system/detail/generic/tabulate.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/tabulate.inl" "$(@D)/cuda/include/thrust/system/detail/generic/tabulate.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/tag.h" "$(@D)/cuda/include/thrust/system/detail/generic/tag.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/temporary_buffer.h" "$(@D)/cuda/include/thrust/system/detail/generic/temporary_buffer.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/temporary_buffer.inl" "$(@D)/cuda/include/thrust/system/detail/generic/temporary_buffer.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/transform.h" "$(@D)/cuda/include/thrust/system/detail/generic/transform.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/transform.inl" "$(@D)/cuda/include/thrust/system/detail/generic/transform.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/transform_reduce.h" "$(@D)/cuda/include/thrust/system/detail/generic/transform_reduce.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/transform_reduce.inl" "$(@D)/cuda/include/thrust/system/detail/generic/transform_reduce.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/transform_scan.h" "$(@D)/cuda/include/thrust/system/detail/generic/transform_scan.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/transform_scan.inl" "$(@D)/cuda/include/thrust/system/detail/generic/transform_scan.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/type_traits.h" "$(@D)/cuda/include/thrust/system/detail/generic/type_traits.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/uninitialized_copy.h" "$(@D)/cuda/include/thrust/system/detail/generic/uninitialized_copy.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/uninitialized_copy.inl" "$(@D)/cuda/include/thrust/system/detail/generic/uninitialized_copy.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/uninitialized_fill.h" "$(@D)/cuda/include/thrust/system/detail/generic/uninitialized_fill.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/uninitialized_fill.inl" "$(@D)/cuda/include/thrust/system/detail/generic/uninitialized_fill.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/unique.h" "$(@D)/cuda/include/thrust/system/detail/generic/unique.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/unique.inl" "$(@D)/cuda/include/thrust/system/detail/generic/unique.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/unique_by_key.h" "$(@D)/cuda/include/thrust/system/detail/generic/unique_by_key.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/generic/unique_by_key.inl" "$(@D)/cuda/include/thrust/system/detail/generic/unique_by_key.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/internal/decompose.h" "$(@D)/cuda/include/thrust/system/detail/internal/decompose.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/adjacent_difference.h" "$(@D)/cuda/include/thrust/system/detail/sequential/adjacent_difference.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/assign_value.h" "$(@D)/cuda/include/thrust/system/detail/sequential/assign_value.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/binary_search.h" "$(@D)/cuda/include/thrust/system/detail/sequential/binary_search.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/copy.h" "$(@D)/cuda/include/thrust/system/detail/sequential/copy.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/copy.inl" "$(@D)/cuda/include/thrust/system/detail/sequential/copy.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/copy_backward.h" "$(@D)/cuda/include/thrust/system/detail/sequential/copy_backward.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/copy_if.h" "$(@D)/cuda/include/thrust/system/detail/sequential/copy_if.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/count.h" "$(@D)/cuda/include/thrust/system/detail/sequential/count.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/equal.h" "$(@D)/cuda/include/thrust/system/detail/sequential/equal.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/execution_policy.h" "$(@D)/cuda/include/thrust/system/detail/sequential/execution_policy.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/extrema.h" "$(@D)/cuda/include/thrust/system/detail/sequential/extrema.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/fill.h" "$(@D)/cuda/include/thrust/system/detail/sequential/fill.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/find.h" "$(@D)/cuda/include/thrust/system/detail/sequential/find.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/for_each.h" "$(@D)/cuda/include/thrust/system/detail/sequential/for_each.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/gather.h" "$(@D)/cuda/include/thrust/system/detail/sequential/gather.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/general_copy.h" "$(@D)/cuda/include/thrust/system/detail/sequential/general_copy.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/generate.h" "$(@D)/cuda/include/thrust/system/detail/sequential/generate.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/get_value.h" "$(@D)/cuda/include/thrust/system/detail/sequential/get_value.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/inner_product.h" "$(@D)/cuda/include/thrust/system/detail/sequential/inner_product.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/insertion_sort.h" "$(@D)/cuda/include/thrust/system/detail/sequential/insertion_sort.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/iter_swap.h" "$(@D)/cuda/include/thrust/system/detail/sequential/iter_swap.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/logical.h" "$(@D)/cuda/include/thrust/system/detail/sequential/logical.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/malloc_and_free.h" "$(@D)/cuda/include/thrust/system/detail/sequential/malloc_and_free.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/merge.h" "$(@D)/cuda/include/thrust/system/detail/sequential/merge.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/merge.inl" "$(@D)/cuda/include/thrust/system/detail/sequential/merge.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/mismatch.h" "$(@D)/cuda/include/thrust/system/detail/sequential/mismatch.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/partition.h" "$(@D)/cuda/include/thrust/system/detail/sequential/partition.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/reduce.h" "$(@D)/cuda/include/thrust/system/detail/sequential/reduce.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/reduce_by_key.h" "$(@D)/cuda/include/thrust/system/detail/sequential/reduce_by_key.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/remove.h" "$(@D)/cuda/include/thrust/system/detail/sequential/remove.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/replace.h" "$(@D)/cuda/include/thrust/system/detail/sequential/replace.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/reverse.h" "$(@D)/cuda/include/thrust/system/detail/sequential/reverse.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/scan.h" "$(@D)/cuda/include/thrust/system/detail/sequential/scan.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/scan_by_key.h" "$(@D)/cuda/include/thrust/system/detail/sequential/scan_by_key.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/scatter.h" "$(@D)/cuda/include/thrust/system/detail/sequential/scatter.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/sequence.h" "$(@D)/cuda/include/thrust/system/detail/sequential/sequence.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/set_operations.h" "$(@D)/cuda/include/thrust/system/detail/sequential/set_operations.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/sort.h" "$(@D)/cuda/include/thrust/system/detail/sequential/sort.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/sort.inl" "$(@D)/cuda/include/thrust/system/detail/sequential/sort.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/stable_merge_sort.h" "$(@D)/cuda/include/thrust/system/detail/sequential/stable_merge_sort.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/stable_merge_sort.inl" "$(@D)/cuda/include/thrust/system/detail/sequential/stable_merge_sort.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/stable_primitive_sort.h" "$(@D)/cuda/include/thrust/system/detail/sequential/stable_primitive_sort.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/stable_primitive_sort.inl" "$(@D)/cuda/include/thrust/system/detail/sequential/stable_primitive_sort.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/stable_radix_sort.h" "$(@D)/cuda/include/thrust/system/detail/sequential/stable_radix_sort.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/stable_radix_sort.inl" "$(@D)/cuda/include/thrust/system/detail/sequential/stable_radix_sort.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/swap_ranges.h" "$(@D)/cuda/include/thrust/system/detail/sequential/swap_ranges.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/tabulate.h" "$(@D)/cuda/include/thrust/system/detail/sequential/tabulate.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/temporary_buffer.h" "$(@D)/cuda/include/thrust/system/detail/sequential/temporary_buffer.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/transform.h" "$(@D)/cuda/include/thrust/system/detail/sequential/transform.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/transform_reduce.h" "$(@D)/cuda/include/thrust/system/detail/sequential/transform_reduce.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/transform_scan.h" "$(@D)/cuda/include/thrust/system/detail/sequential/transform_scan.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/trivial_copy.h" "$(@D)/cuda/include/thrust/system/detail/sequential/trivial_copy.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/uninitialized_copy.h" "$(@D)/cuda/include/thrust/system/detail/sequential/uninitialized_copy.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/uninitialized_fill.h" "$(@D)/cuda/include/thrust/system/detail/sequential/uninitialized_fill.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/unique.h" "$(@D)/cuda/include/thrust/system/detail/sequential/unique.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/unique_by_key.h" "$(@D)/cuda/include/thrust/system/detail/sequential/unique_by_key.h" && cp "/usr/local/cuda-9.0/include/thrust/system/detail/system_error.inl" "$(@D)/cuda/include/thrust/system/detail/system_error.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/error_code.h" "$(@D)/cuda/include/thrust/system/error_code.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/adjacent_difference.h" "$(@D)/cuda/include/thrust/system/omp/detail/adjacent_difference.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/assign_value.h" "$(@D)/cuda/include/thrust/system/omp/detail/assign_value.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/binary_search.h" "$(@D)/cuda/include/thrust/system/omp/detail/binary_search.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/copy.h" "$(@D)/cuda/include/thrust/system/omp/detail/copy.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/copy.inl" "$(@D)/cuda/include/thrust/system/omp/detail/copy.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/copy_if.h" "$(@D)/cuda/include/thrust/system/omp/detail/copy_if.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/copy_if.inl" "$(@D)/cuda/include/thrust/system/omp/detail/copy_if.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/count.h" "$(@D)/cuda/include/thrust/system/omp/detail/count.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/default_decomposition.h" "$(@D)/cuda/include/thrust/system/omp/detail/default_decomposition.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/default_decomposition.inl" "$(@D)/cuda/include/thrust/system/omp/detail/default_decomposition.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/equal.h" "$(@D)/cuda/include/thrust/system/omp/detail/equal.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/execution_policy.h" "$(@D)/cuda/include/thrust/system/omp/detail/execution_policy.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/extrema.h" "$(@D)/cuda/include/thrust/system/omp/detail/extrema.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/fill.h" "$(@D)/cuda/include/thrust/system/omp/detail/fill.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/find.h" "$(@D)/cuda/include/thrust/system/omp/detail/find.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/for_each.h" "$(@D)/cuda/include/thrust/system/omp/detail/for_each.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/for_each.inl" "$(@D)/cuda/include/thrust/system/omp/detail/for_each.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/gather.h" "$(@D)/cuda/include/thrust/system/omp/detail/gather.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/generate.h" "$(@D)/cuda/include/thrust/system/omp/detail/generate.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/get_value.h" "$(@D)/cuda/include/thrust/system/omp/detail/get_value.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/inner_product.h" "$(@D)/cuda/include/thrust/system/omp/detail/inner_product.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/iter_swap.h" "$(@D)/cuda/include/thrust/system/omp/detail/iter_swap.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/logical.h" "$(@D)/cuda/include/thrust/system/omp/detail/logical.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/malloc_and_free.h" "$(@D)/cuda/include/thrust/system/omp/detail/malloc_and_free.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/memory.inl" "$(@D)/cuda/include/thrust/system/omp/detail/memory.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/merge.h" "$(@D)/cuda/include/thrust/system/omp/detail/merge.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/mismatch.h" "$(@D)/cuda/include/thrust/system/omp/detail/mismatch.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/par.h" "$(@D)/cuda/include/thrust/system/omp/detail/par.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/partition.h" "$(@D)/cuda/include/thrust/system/omp/detail/partition.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/partition.inl" "$(@D)/cuda/include/thrust/system/omp/detail/partition.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/reduce.h" "$(@D)/cuda/include/thrust/system/omp/detail/reduce.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/reduce.inl" "$(@D)/cuda/include/thrust/system/omp/detail/reduce.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/reduce_by_key.h" "$(@D)/cuda/include/thrust/system/omp/detail/reduce_by_key.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/reduce_by_key.inl" "$(@D)/cuda/include/thrust/system/omp/detail/reduce_by_key.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/reduce_intervals.h" "$(@D)/cuda/include/thrust/system/omp/detail/reduce_intervals.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/reduce_intervals.inl" "$(@D)/cuda/include/thrust/system/omp/detail/reduce_intervals.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/remove.h" "$(@D)/cuda/include/thrust/system/omp/detail/remove.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/remove.inl" "$(@D)/cuda/include/thrust/system/omp/detail/remove.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/replace.h" "$(@D)/cuda/include/thrust/system/omp/detail/replace.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/reverse.h" "$(@D)/cuda/include/thrust/system/omp/detail/reverse.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/scan.h" "$(@D)/cuda/include/thrust/system/omp/detail/scan.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/scan_by_key.h" "$(@D)/cuda/include/thrust/system/omp/detail/scan_by_key.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/scatter.h" "$(@D)/cuda/include/thrust/system/omp/detail/scatter.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/sequence.h" "$(@D)/cuda/include/thrust/system/omp/detail/sequence.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/set_operations.h" "$(@D)/cuda/include/thrust/system/omp/detail/set_operations.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/sort.h" "$(@D)/cuda/include/thrust/system/omp/detail/sort.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/sort.inl" "$(@D)/cuda/include/thrust/system/omp/detail/sort.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/swap_ranges.h" "$(@D)/cuda/include/thrust/system/omp/detail/swap_ranges.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/tabulate.h" "$(@D)/cuda/include/thrust/system/omp/detail/tabulate.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/temporary_buffer.h" "$(@D)/cuda/include/thrust/system/omp/detail/temporary_buffer.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/transform.h" "$(@D)/cuda/include/thrust/system/omp/detail/transform.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/transform_reduce.h" "$(@D)/cuda/include/thrust/system/omp/detail/transform_reduce.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/transform_scan.h" "$(@D)/cuda/include/thrust/system/omp/detail/transform_scan.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/uninitialized_copy.h" "$(@D)/cuda/include/thrust/system/omp/detail/uninitialized_copy.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/uninitialized_fill.h" "$(@D)/cuda/include/thrust/system/omp/detail/uninitialized_fill.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/unique.h" "$(@D)/cuda/include/thrust/system/omp/detail/unique.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/unique.inl" "$(@D)/cuda/include/thrust/system/omp/detail/unique.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/unique_by_key.h" "$(@D)/cuda/include/thrust/system/omp/detail/unique_by_key.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/unique_by_key.inl" "$(@D)/cuda/include/thrust/system/omp/detail/unique_by_key.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/detail/vector.inl" "$(@D)/cuda/include/thrust/system/omp/detail/vector.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/execution_policy.h" "$(@D)/cuda/include/thrust/system/omp/execution_policy.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/memory.h" "$(@D)/cuda/include/thrust/system/omp/memory.h" && cp "/usr/local/cuda-9.0/include/thrust/system/omp/vector.h" "$(@D)/cuda/include/thrust/system/omp/vector.h" && cp "/usr/local/cuda-9.0/include/thrust/system/system_error.h" "$(@D)/cuda/include/thrust/system/system_error.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/adjacent_difference.h" "$(@D)/cuda/include/thrust/system/tbb/detail/adjacent_difference.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/assign_value.h" "$(@D)/cuda/include/thrust/system/tbb/detail/assign_value.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/binary_search.h" "$(@D)/cuda/include/thrust/system/tbb/detail/binary_search.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/copy.h" "$(@D)/cuda/include/thrust/system/tbb/detail/copy.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/copy.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/copy.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/copy_if.h" "$(@D)/cuda/include/thrust/system/tbb/detail/copy_if.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/copy_if.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/copy_if.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/count.h" "$(@D)/cuda/include/thrust/system/tbb/detail/count.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/equal.h" "$(@D)/cuda/include/thrust/system/tbb/detail/equal.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/execution_policy.h" "$(@D)/cuda/include/thrust/system/tbb/detail/execution_policy.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/extrema.h" "$(@D)/cuda/include/thrust/system/tbb/detail/extrema.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/fill.h" "$(@D)/cuda/include/thrust/system/tbb/detail/fill.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/find.h" "$(@D)/cuda/include/thrust/system/tbb/detail/find.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/for_each.h" "$(@D)/cuda/include/thrust/system/tbb/detail/for_each.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/for_each.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/for_each.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/gather.h" "$(@D)/cuda/include/thrust/system/tbb/detail/gather.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/generate.h" "$(@D)/cuda/include/thrust/system/tbb/detail/generate.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/get_value.h" "$(@D)/cuda/include/thrust/system/tbb/detail/get_value.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/inner_product.h" "$(@D)/cuda/include/thrust/system/tbb/detail/inner_product.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/iter_swap.h" "$(@D)/cuda/include/thrust/system/tbb/detail/iter_swap.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/logical.h" "$(@D)/cuda/include/thrust/system/tbb/detail/logical.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/malloc_and_free.h" "$(@D)/cuda/include/thrust/system/tbb/detail/malloc_and_free.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/memory.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/memory.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/merge.h" "$(@D)/cuda/include/thrust/system/tbb/detail/merge.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/merge.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/merge.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/mismatch.h" "$(@D)/cuda/include/thrust/system/tbb/detail/mismatch.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/par.h" "$(@D)/cuda/include/thrust/system/tbb/detail/par.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/partition.h" "$(@D)/cuda/include/thrust/system/tbb/detail/partition.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/partition.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/partition.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/reduce.h" "$(@D)/cuda/include/thrust/system/tbb/detail/reduce.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/reduce.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/reduce.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/reduce_by_key.h" "$(@D)/cuda/include/thrust/system/tbb/detail/reduce_by_key.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/reduce_by_key.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/reduce_by_key.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/reduce_intervals.h" "$(@D)/cuda/include/thrust/system/tbb/detail/reduce_intervals.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/remove.h" "$(@D)/cuda/include/thrust/system/tbb/detail/remove.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/remove.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/remove.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/replace.h" "$(@D)/cuda/include/thrust/system/tbb/detail/replace.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/reverse.h" "$(@D)/cuda/include/thrust/system/tbb/detail/reverse.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/scan.h" "$(@D)/cuda/include/thrust/system/tbb/detail/scan.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/scan.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/scan.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/scan_by_key.h" "$(@D)/cuda/include/thrust/system/tbb/detail/scan_by_key.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/scatter.h" "$(@D)/cuda/include/thrust/system/tbb/detail/scatter.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/sequence.h" "$(@D)/cuda/include/thrust/system/tbb/detail/sequence.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/set_operations.h" "$(@D)/cuda/include/thrust/system/tbb/detail/set_operations.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/sort.h" "$(@D)/cuda/include/thrust/system/tbb/detail/sort.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/sort.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/sort.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/swap_ranges.h" "$(@D)/cuda/include/thrust/system/tbb/detail/swap_ranges.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/tabulate.h" "$(@D)/cuda/include/thrust/system/tbb/detail/tabulate.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/temporary_buffer.h" "$(@D)/cuda/include/thrust/system/tbb/detail/temporary_buffer.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/transform.h" "$(@D)/cuda/include/thrust/system/tbb/detail/transform.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/transform_reduce.h" "$(@D)/cuda/include/thrust/system/tbb/detail/transform_reduce.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/transform_scan.h" "$(@D)/cuda/include/thrust/system/tbb/detail/transform_scan.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/uninitialized_copy.h" "$(@D)/cuda/include/thrust/system/tbb/detail/uninitialized_copy.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/uninitialized_fill.h" "$(@D)/cuda/include/thrust/system/tbb/detail/uninitialized_fill.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/unique.h" "$(@D)/cuda/include/thrust/system/tbb/detail/unique.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/unique.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/unique.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/unique_by_key.h" "$(@D)/cuda/include/thrust/system/tbb/detail/unique_by_key.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/unique_by_key.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/unique_by_key.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/vector.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/vector.inl" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/execution_policy.h" "$(@D)/cuda/include/thrust/system/tbb/execution_policy.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/memory.h" "$(@D)/cuda/include/thrust/system/tbb/memory.h" && cp "/usr/local/cuda-9.0/include/thrust/system/tbb/vector.h" "$(@D)/cuda/include/thrust/system/tbb/vector.h" && cp "/usr/local/cuda-9.0/include/thrust/system_error.h" "$(@D)/cuda/include/thrust/system_error.h" && cp "/usr/local/cuda-9.0/include/thrust/tabulate.h" "$(@D)/cuda/include/thrust/tabulate.h" && cp "/usr/local/cuda-9.0/include/thrust/transform.h" "$(@D)/cuda/include/thrust/transform.h" && cp "/usr/local/cuda-9.0/include/thrust/transform_reduce.h" "$(@D)/cuda/include/thrust/transform_reduce.h" && cp "/usr/local/cuda-9.0/include/thrust/transform_scan.h" "$(@D)/cuda/include/thrust/transform_scan.h" && cp "/usr/local/cuda-9.0/include/thrust/tuple.h" "$(@D)/cuda/include/thrust/tuple.h" && cp "/usr/local/cuda-9.0/include/thrust/uninitialized_copy.h" "$(@D)/cuda/include/thrust/uninitialized_copy.h" && cp "/usr/local/cuda-9.0/include/thrust/uninitialized_fill.h" "$(@D)/cuda/include/thrust/uninitialized_fill.h" && cp "/usr/local/cuda-9.0/include/thrust/unique.h" "$(@D)/cuda/include/thrust/unique.h" && cp "/usr/local/cuda-9.0/include/thrust/version.h" "$(@D)/cuda/include/thrust/version.h" && cp "/usr/local/cuda-9.0/include/vector_functions.h" "$(@D)/cuda/include/vector_functions.h" && cp "/usr/local/cuda-9.0/include/vector_functions.hpp" "$(@D)/cuda/include/vector_functions.hpp" && cp "/usr/local/cuda-9.0/include/vector_types.h" "$(@D)/cuda/include/vector_types.h" +if [ -d "$(@D)/extras" ]; then rm $(@D)/extras -drf; fi && if [ -d "$(@D)/include" ]; then rm $(@D)/include -drf; fi && if [ -d "$(@D)/lib" ]; then rm $(@D)/lib -drf; fi && if [ -d "$(@D)/nvvm" ]; then rm $(@D)/nvvm -drf; fi && cp -f "/usr/local/cuda-9.0/include/CL/cl.h" "$(@D)/cuda/include/CL/cl.h" && cp -f "/usr/local/cuda-9.0/include/CL/cl.hpp" "$(@D)/cuda/include/CL/cl.hpp" && cp -f "/usr/local/cuda-9.0/include/CL/cl_egl.h" "$(@D)/cuda/include/CL/cl_egl.h" && cp -f "/usr/local/cuda-9.0/include/CL/cl_ext.h" "$(@D)/cuda/include/CL/cl_ext.h" && cp -f "/usr/local/cuda-9.0/include/CL/cl_gl.h" "$(@D)/cuda/include/CL/cl_gl.h" && cp -f "/usr/local/cuda-9.0/include/CL/cl_gl_ext.h" "$(@D)/cuda/include/CL/cl_gl_ext.h" && cp -f "/usr/local/cuda-9.0/include/CL/cl_platform.h" "$(@D)/cuda/include/CL/cl_platform.h" && cp -f "/usr/local/cuda-9.0/include/CL/opencl.h" "$(@D)/cuda/include/CL/opencl.h" && cp -f "/usr/local/cuda-9.0/include/builtin_types.h" "$(@D)/cuda/include/builtin_types.h" && cp -f "/usr/local/cuda-9.0/include/channel_descriptor.h" "$(@D)/cuda/include/channel_descriptor.h" && cp -f "/usr/local/cuda-9.0/include/common_functions.h" "$(@D)/cuda/include/common_functions.h" && cp -f "/usr/local/cuda-9.0/include/cooperative_groups.h" "$(@D)/cuda/include/cooperative_groups.h" && cp -f "/usr/local/cuda-9.0/include/cooperative_groups_helpers.h" "$(@D)/cuda/include/cooperative_groups_helpers.h" && cp -f "/usr/local/cuda-9.0/include/crt/common_functions.h" "$(@D)/cuda/include/crt/common_functions.h" && cp -f "/usr/local/cuda-9.0/include/crt/device_double_functions.h" "$(@D)/cuda/include/crt/device_double_functions.h" && cp -f "/usr/local/cuda-9.0/include/crt/device_double_functions.hpp" "$(@D)/cuda/include/crt/device_double_functions.hpp" && cp -f "/usr/local/cuda-9.0/include/crt/device_functions.h" "$(@D)/cuda/include/crt/device_functions.h" && cp -f "/usr/local/cuda-9.0/include/crt/device_functions.hpp" "$(@D)/cuda/include/crt/device_functions.hpp" && cp -f "/usr/local/cuda-9.0/include/crt/func_macro.h" "$(@D)/cuda/include/crt/func_macro.h" && cp -f "/usr/local/cuda-9.0/include/crt/host_config.h" "$(@D)/cuda/include/crt/host_config.h" && cp -f "/usr/local/cuda-9.0/include/crt/host_defines.h" "$(@D)/cuda/include/crt/host_defines.h" && cp -f "/usr/local/cuda-9.0/include/crt/host_runtime.h" "$(@D)/cuda/include/crt/host_runtime.h" && cp -f "/usr/local/cuda-9.0/include/crt/math_functions.h" "$(@D)/cuda/include/crt/math_functions.h" && cp -f "/usr/local/cuda-9.0/include/crt/math_functions.hpp" "$(@D)/cuda/include/crt/math_functions.hpp" && cp -f "/usr/local/cuda-9.0/include/crt/mma.h" "$(@D)/cuda/include/crt/mma.h" && cp -f "/usr/local/cuda-9.0/include/crt/mma.hpp" "$(@D)/cuda/include/crt/mma.hpp" && cp -f "/usr/local/cuda-9.0/include/crt/nvfunctional" "$(@D)/cuda/include/crt/nvfunctional" && cp -f "/usr/local/cuda-9.0/include/crt/sm_70_rt.h" "$(@D)/cuda/include/crt/sm_70_rt.h" && cp -f "/usr/local/cuda-9.0/include/crt/sm_70_rt.hpp" "$(@D)/cuda/include/crt/sm_70_rt.hpp" && cp -f "/usr/local/cuda-9.0/include/crt/storage_class.h" "$(@D)/cuda/include/crt/storage_class.h" && cp -f "/usr/local/cuda-9.0/include/cuComplex.h" "$(@D)/cuda/include/cuComplex.h" && cp -f "/usr/local/cuda-9.0/include/cublas.h" "$(@D)/cuda/include/cublas.h" && cp -f "/usr/local/cuda-9.0/include/cublasXt.h" "$(@D)/cuda/include/cublasXt.h" && cp -f "/usr/local/cuda-9.0/include/cublas_api.h" "$(@D)/cuda/include/cublas_api.h" && cp -f "/usr/local/cuda-9.0/include/cublas_v2.h" "$(@D)/cuda/include/cublas_v2.h" && cp -f "/usr/local/cuda-9.0/include/cuda.h" "$(@D)/cuda/include/cuda.h" && cp -f "/usr/local/cuda-9.0/include/cudaEGL.h" "$(@D)/cuda/include/cudaEGL.h" && cp -f "/usr/local/cuda-9.0/include/cudaGL.h" "$(@D)/cuda/include/cudaGL.h" && cp -f "/usr/local/cuda-9.0/include/cudaProfiler.h" "$(@D)/cuda/include/cudaProfiler.h" && cp -f "/usr/local/cuda-9.0/include/cudaVDPAU.h" "$(@D)/cuda/include/cudaVDPAU.h" && cp -f "/usr/local/cuda-9.0/include/cuda_device_runtime_api.h" "$(@D)/cuda/include/cuda_device_runtime_api.h" && cp -f "/usr/local/cuda-9.0/include/cuda_fp16.h" "$(@D)/cuda/include/cuda_fp16.h" && cp -f "/usr/local/cuda-9.0/include/cuda_fp16.hpp" "$(@D)/cuda/include/cuda_fp16.hpp" && cp -f "/usr/local/cuda-9.0/include/cuda_gl_interop.h" "$(@D)/cuda/include/cuda_gl_interop.h" && cp -f "/usr/local/cuda-9.0/include/cuda_occupancy.h" "$(@D)/cuda/include/cuda_occupancy.h" && cp -f "/usr/local/cuda-9.0/include/cuda_profiler_api.h" "$(@D)/cuda/include/cuda_profiler_api.h" && cp -f "/usr/local/cuda-9.0/include/cuda_runtime.h" "$(@D)/cuda/include/cuda_runtime.h" && cp -f "/usr/local/cuda-9.0/include/cuda_runtime_api.h" "$(@D)/cuda/include/cuda_runtime_api.h" && cp -f "/usr/local/cuda-9.0/include/cuda_surface_types.h" "$(@D)/cuda/include/cuda_surface_types.h" && cp -f "/usr/local/cuda-9.0/include/cuda_texture_types.h" "$(@D)/cuda/include/cuda_texture_types.h" && cp -f "/usr/local/cuda-9.0/include/cuda_vdpau_interop.h" "$(@D)/cuda/include/cuda_vdpau_interop.h" && cp -f "/usr/local/cuda-9.0/include/cudalibxt.h" "$(@D)/cuda/include/cudalibxt.h" && cp -f "/usr/local/cuda-9.0/include/cufft.h" "$(@D)/cuda/include/cufft.h" && cp -f "/usr/local/cuda-9.0/include/cufftXt.h" "$(@D)/cuda/include/cufftXt.h" && cp -f "/usr/local/cuda-9.0/include/cufftw.h" "$(@D)/cuda/include/cufftw.h" && cp -f "/usr/local/cuda-9.0/include/curand.h" "$(@D)/cuda/include/curand.h" && cp -f "/usr/local/cuda-9.0/include/curand_discrete.h" "$(@D)/cuda/include/curand_discrete.h" && cp -f "/usr/local/cuda-9.0/include/curand_discrete2.h" "$(@D)/cuda/include/curand_discrete2.h" && cp -f "/usr/local/cuda-9.0/include/curand_globals.h" "$(@D)/cuda/include/curand_globals.h" && cp -f "/usr/local/cuda-9.0/include/curand_kernel.h" "$(@D)/cuda/include/curand_kernel.h" && cp -f "/usr/local/cuda-9.0/include/curand_lognormal.h" "$(@D)/cuda/include/curand_lognormal.h" && cp -f "/usr/local/cuda-9.0/include/curand_mrg32k3a.h" "$(@D)/cuda/include/curand_mrg32k3a.h" && cp -f "/usr/local/cuda-9.0/include/curand_mtgp32.h" "$(@D)/cuda/include/curand_mtgp32.h" && cp -f "/usr/local/cuda-9.0/include/curand_mtgp32_host.h" "$(@D)/cuda/include/curand_mtgp32_host.h" && cp -f "/usr/local/cuda-9.0/include/curand_mtgp32_kernel.h" "$(@D)/cuda/include/curand_mtgp32_kernel.h" && cp -f "/usr/local/cuda-9.0/include/curand_mtgp32dc_p_11213.h" "$(@D)/cuda/include/curand_mtgp32dc_p_11213.h" && cp -f "/usr/local/cuda-9.0/include/curand_normal.h" "$(@D)/cuda/include/curand_normal.h" && cp -f "/usr/local/cuda-9.0/include/curand_normal_static.h" "$(@D)/cuda/include/curand_normal_static.h" && cp -f "/usr/local/cuda-9.0/include/curand_philox4x32_x.h" "$(@D)/cuda/include/curand_philox4x32_x.h" && cp -f "/usr/local/cuda-9.0/include/curand_poisson.h" "$(@D)/cuda/include/curand_poisson.h" && cp -f "/usr/local/cuda-9.0/include/curand_precalc.h" "$(@D)/cuda/include/curand_precalc.h" && cp -f "/usr/local/cuda-9.0/include/curand_uniform.h" "$(@D)/cuda/include/curand_uniform.h" && cp -f "/usr/local/cuda-9.0/include/cusolverDn.h" "$(@D)/cuda/include/cusolverDn.h" && cp -f "/usr/local/cuda-9.0/include/cusolverRf.h" "$(@D)/cuda/include/cusolverRf.h" && cp -f "/usr/local/cuda-9.0/include/cusolverSp.h" "$(@D)/cuda/include/cusolverSp.h" && cp -f "/usr/local/cuda-9.0/include/cusolverSp_LOWLEVEL_PREVIEW.h" "$(@D)/cuda/include/cusolverSp_LOWLEVEL_PREVIEW.h" && cp -f "/usr/local/cuda-9.0/include/cusolver_common.h" "$(@D)/cuda/include/cusolver_common.h" && cp -f "/usr/local/cuda-9.0/include/cusparse.h" "$(@D)/cuda/include/cusparse.h" && cp -f "/usr/local/cuda-9.0/include/cusparse_v2.h" "$(@D)/cuda/include/cusparse_v2.h" && cp -f "/usr/local/cuda-9.0/include/device_atomic_functions.h" "$(@D)/cuda/include/device_atomic_functions.h" && cp -f "/usr/local/cuda-9.0/include/device_atomic_functions.hpp" "$(@D)/cuda/include/device_atomic_functions.hpp" && cp -f "/usr/local/cuda-9.0/include/device_double_functions.h" "$(@D)/cuda/include/device_double_functions.h" && cp -f "/usr/local/cuda-9.0/include/device_double_functions.hpp" "$(@D)/cuda/include/device_double_functions.hpp" && cp -f "/usr/local/cuda-9.0/include/device_functions.h" "$(@D)/cuda/include/device_functions.h" && cp -f "/usr/local/cuda-9.0/include/device_functions.hpp" "$(@D)/cuda/include/device_functions.hpp" && cp -f "/usr/local/cuda-9.0/include/device_functions_decls.h" "$(@D)/cuda/include/device_functions_decls.h" && cp -f "/usr/local/cuda-9.0/include/device_launch_parameters.h" "$(@D)/cuda/include/device_launch_parameters.h" && cp -f "/usr/local/cuda-9.0/include/device_types.h" "$(@D)/cuda/include/device_types.h" && cp -f "/usr/local/cuda-9.0/include/driver_functions.h" "$(@D)/cuda/include/driver_functions.h" && cp -f "/usr/local/cuda-9.0/include/driver_types.h" "$(@D)/cuda/include/driver_types.h" && cp -f "/usr/local/cuda-9.0/include/dynlink_cuda.h" "$(@D)/cuda/include/dynlink_cuda.h" && cp -f "/usr/local/cuda-9.0/include/dynlink_cuda_cuda.h" "$(@D)/cuda/include/dynlink_cuda_cuda.h" && cp -f "/usr/local/cuda-9.0/include/dynlink_cuviddec.h" "$(@D)/cuda/include/dynlink_cuviddec.h" && cp -f "/usr/local/cuda-9.0/include/dynlink_nvcuvid.h" "$(@D)/cuda/include/dynlink_nvcuvid.h" && cp -f "/usr/local/cuda-9.0/include/fatBinaryCtl.h" "$(@D)/cuda/include/fatBinaryCtl.h" && cp -f "/usr/local/cuda-9.0/include/fatbinary.h" "$(@D)/cuda/include/fatbinary.h" && cp -f "/usr/local/cuda-9.0/include/host_config.h" "$(@D)/cuda/include/host_config.h" && cp -f "/usr/local/cuda-9.0/include/host_defines.h" "$(@D)/cuda/include/host_defines.h" && cp -f "/usr/local/cuda-9.0/include/library_types.h" "$(@D)/cuda/include/library_types.h" && cp -f "/usr/local/cuda-9.0/include/math_constants.h" "$(@D)/cuda/include/math_constants.h" && cp -f "/usr/local/cuda-9.0/include/math_functions.h" "$(@D)/cuda/include/math_functions.h" && cp -f "/usr/local/cuda-9.0/include/math_functions.hpp" "$(@D)/cuda/include/math_functions.hpp" && cp -f "/usr/local/cuda-9.0/include/math_functions_dbl_ptx3.h" "$(@D)/cuda/include/math_functions_dbl_ptx3.h" && cp -f "/usr/local/cuda-9.0/include/math_functions_dbl_ptx3.hpp" "$(@D)/cuda/include/math_functions_dbl_ptx3.hpp" && cp -f "/usr/local/cuda-9.0/include/mma.h" "$(@D)/cuda/include/mma.h" && cp -f "/usr/local/cuda-9.0/include/npp.h" "$(@D)/cuda/include/npp.h" && cp -f "/usr/local/cuda-9.0/include/nppcore.h" "$(@D)/cuda/include/nppcore.h" && cp -f "/usr/local/cuda-9.0/include/nppdefs.h" "$(@D)/cuda/include/nppdefs.h" && cp -f "/usr/local/cuda-9.0/include/nppi.h" "$(@D)/cuda/include/nppi.h" && cp -f "/usr/local/cuda-9.0/include/nppi_arithmetic_and_logical_operations.h" "$(@D)/cuda/include/nppi_arithmetic_and_logical_operations.h" && cp -f "/usr/local/cuda-9.0/include/nppi_color_conversion.h" "$(@D)/cuda/include/nppi_color_conversion.h" && cp -f "/usr/local/cuda-9.0/include/nppi_compression_functions.h" "$(@D)/cuda/include/nppi_compression_functions.h" && cp -f "/usr/local/cuda-9.0/include/nppi_computer_vision.h" "$(@D)/cuda/include/nppi_computer_vision.h" && cp -f "/usr/local/cuda-9.0/include/nppi_data_exchange_and_initialization.h" "$(@D)/cuda/include/nppi_data_exchange_and_initialization.h" && cp -f "/usr/local/cuda-9.0/include/nppi_filtering_functions.h" "$(@D)/cuda/include/nppi_filtering_functions.h" && cp -f "/usr/local/cuda-9.0/include/nppi_geometry_transforms.h" "$(@D)/cuda/include/nppi_geometry_transforms.h" && cp -f "/usr/local/cuda-9.0/include/nppi_linear_transforms.h" "$(@D)/cuda/include/nppi_linear_transforms.h" && cp -f "/usr/local/cuda-9.0/include/nppi_morphological_operations.h" "$(@D)/cuda/include/nppi_morphological_operations.h" && cp -f "/usr/local/cuda-9.0/include/nppi_statistics_functions.h" "$(@D)/cuda/include/nppi_statistics_functions.h" && cp -f "/usr/local/cuda-9.0/include/nppi_support_functions.h" "$(@D)/cuda/include/nppi_support_functions.h" && cp -f "/usr/local/cuda-9.0/include/nppi_threshold_and_compare_operations.h" "$(@D)/cuda/include/nppi_threshold_and_compare_operations.h" && cp -f "/usr/local/cuda-9.0/include/npps.h" "$(@D)/cuda/include/npps.h" && cp -f "/usr/local/cuda-9.0/include/npps_arithmetic_and_logical_operations.h" "$(@D)/cuda/include/npps_arithmetic_and_logical_operations.h" && cp -f "/usr/local/cuda-9.0/include/npps_conversion_functions.h" "$(@D)/cuda/include/npps_conversion_functions.h" && cp -f "/usr/local/cuda-9.0/include/npps_filtering_functions.h" "$(@D)/cuda/include/npps_filtering_functions.h" && cp -f "/usr/local/cuda-9.0/include/npps_initialization.h" "$(@D)/cuda/include/npps_initialization.h" && cp -f "/usr/local/cuda-9.0/include/npps_statistics_functions.h" "$(@D)/cuda/include/npps_statistics_functions.h" && cp -f "/usr/local/cuda-9.0/include/npps_support_functions.h" "$(@D)/cuda/include/npps_support_functions.h" && cp -f "/usr/local/cuda-9.0/include/nppversion.h" "$(@D)/cuda/include/nppversion.h" && cp -f "/usr/local/cuda-9.0/include/nvToolsExt.h" "$(@D)/cuda/include/nvToolsExt.h" && cp -f "/usr/local/cuda-9.0/include/nvToolsExtCuda.h" "$(@D)/cuda/include/nvToolsExtCuda.h" && cp -f "/usr/local/cuda-9.0/include/nvToolsExtCudaRt.h" "$(@D)/cuda/include/nvToolsExtCudaRt.h" && cp -f "/usr/local/cuda-9.0/include/nvToolsExtMeta.h" "$(@D)/cuda/include/nvToolsExtMeta.h" && cp -f "/usr/local/cuda-9.0/include/nvToolsExtSync.h" "$(@D)/cuda/include/nvToolsExtSync.h" && cp -f "/usr/local/cuda-9.0/include/nvblas.h" "$(@D)/cuda/include/nvblas.h" && cp -f "/usr/local/cuda-9.0/include/nvfunctional" "$(@D)/cuda/include/nvfunctional" && cp -f "/usr/local/cuda-9.0/include/nvgraph.h" "$(@D)/cuda/include/nvgraph.h" && cp -f "/usr/local/cuda-9.0/include/nvml.h" "$(@D)/cuda/include/nvml.h" && cp -f "/usr/local/cuda-9.0/include/nvrtc.h" "$(@D)/cuda/include/nvrtc.h" && cp -f "/usr/local/cuda-9.0/include/sm_20_atomic_functions.h" "$(@D)/cuda/include/sm_20_atomic_functions.h" && cp -f "/usr/local/cuda-9.0/include/sm_20_atomic_functions.hpp" "$(@D)/cuda/include/sm_20_atomic_functions.hpp" && cp -f "/usr/local/cuda-9.0/include/sm_20_intrinsics.h" "$(@D)/cuda/include/sm_20_intrinsics.h" && cp -f "/usr/local/cuda-9.0/include/sm_20_intrinsics.hpp" "$(@D)/cuda/include/sm_20_intrinsics.hpp" && cp -f "/usr/local/cuda-9.0/include/sm_30_intrinsics.h" "$(@D)/cuda/include/sm_30_intrinsics.h" && cp -f "/usr/local/cuda-9.0/include/sm_30_intrinsics.hpp" "$(@D)/cuda/include/sm_30_intrinsics.hpp" && cp -f "/usr/local/cuda-9.0/include/sm_32_atomic_functions.h" "$(@D)/cuda/include/sm_32_atomic_functions.h" && cp -f "/usr/local/cuda-9.0/include/sm_32_atomic_functions.hpp" "$(@D)/cuda/include/sm_32_atomic_functions.hpp" && cp -f "/usr/local/cuda-9.0/include/sm_32_intrinsics.h" "$(@D)/cuda/include/sm_32_intrinsics.h" && cp -f "/usr/local/cuda-9.0/include/sm_32_intrinsics.hpp" "$(@D)/cuda/include/sm_32_intrinsics.hpp" && cp -f "/usr/local/cuda-9.0/include/sm_35_atomic_functions.h" "$(@D)/cuda/include/sm_35_atomic_functions.h" && cp -f "/usr/local/cuda-9.0/include/sm_35_intrinsics.h" "$(@D)/cuda/include/sm_35_intrinsics.h" && cp -f "/usr/local/cuda-9.0/include/sm_60_atomic_functions.h" "$(@D)/cuda/include/sm_60_atomic_functions.h" && cp -f "/usr/local/cuda-9.0/include/sm_60_atomic_functions.hpp" "$(@D)/cuda/include/sm_60_atomic_functions.hpp" && cp -f "/usr/local/cuda-9.0/include/sm_61_intrinsics.h" "$(@D)/cuda/include/sm_61_intrinsics.h" && cp -f "/usr/local/cuda-9.0/include/sm_61_intrinsics.hpp" "$(@D)/cuda/include/sm_61_intrinsics.hpp" && cp -f "/usr/local/cuda-9.0/include/sobol_direction_vectors.h" "$(@D)/cuda/include/sobol_direction_vectors.h" && cp -f "/usr/local/cuda-9.0/include/surface_functions.h" "$(@D)/cuda/include/surface_functions.h" && cp -f "/usr/local/cuda-9.0/include/surface_functions.hpp" "$(@D)/cuda/include/surface_functions.hpp" && cp -f "/usr/local/cuda-9.0/include/surface_indirect_functions.h" "$(@D)/cuda/include/surface_indirect_functions.h" && cp -f "/usr/local/cuda-9.0/include/surface_indirect_functions.hpp" "$(@D)/cuda/include/surface_indirect_functions.hpp" && cp -f "/usr/local/cuda-9.0/include/surface_types.h" "$(@D)/cuda/include/surface_types.h" && cp -f "/usr/local/cuda-9.0/include/texture_fetch_functions.h" "$(@D)/cuda/include/texture_fetch_functions.h" && cp -f "/usr/local/cuda-9.0/include/texture_fetch_functions.hpp" "$(@D)/cuda/include/texture_fetch_functions.hpp" && cp -f "/usr/local/cuda-9.0/include/texture_indirect_functions.h" "$(@D)/cuda/include/texture_indirect_functions.h" && cp -f "/usr/local/cuda-9.0/include/texture_indirect_functions.hpp" "$(@D)/cuda/include/texture_indirect_functions.hpp" && cp -f "/usr/local/cuda-9.0/include/texture_types.h" "$(@D)/cuda/include/texture_types.h" && cp -f "/usr/local/cuda-9.0/include/thrust/adjacent_difference.h" "$(@D)/cuda/include/thrust/adjacent_difference.h" && cp -f "/usr/local/cuda-9.0/include/thrust/advance.h" "$(@D)/cuda/include/thrust/advance.h" && cp -f "/usr/local/cuda-9.0/include/thrust/binary_search.h" "$(@D)/cuda/include/thrust/binary_search.h" && cp -f "/usr/local/cuda-9.0/include/thrust/complex.h" "$(@D)/cuda/include/thrust/complex.h" && cp -f "/usr/local/cuda-9.0/include/thrust/copy.h" "$(@D)/cuda/include/thrust/copy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/count.h" "$(@D)/cuda/include/thrust/count.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/adjacent_difference.inl" "$(@D)/cuda/include/thrust/detail/adjacent_difference.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/advance.inl" "$(@D)/cuda/include/thrust/detail/advance.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/allocator/allocator_traits.h" "$(@D)/cuda/include/thrust/detail/allocator/allocator_traits.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/allocator/allocator_traits.inl" "$(@D)/cuda/include/thrust/detail/allocator/allocator_traits.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/allocator/copy_construct_range.h" "$(@D)/cuda/include/thrust/detail/allocator/copy_construct_range.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/allocator/copy_construct_range.inl" "$(@D)/cuda/include/thrust/detail/allocator/copy_construct_range.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/allocator/default_construct_range.h" "$(@D)/cuda/include/thrust/detail/allocator/default_construct_range.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/allocator/default_construct_range.inl" "$(@D)/cuda/include/thrust/detail/allocator/default_construct_range.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/allocator/destroy_range.h" "$(@D)/cuda/include/thrust/detail/allocator/destroy_range.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/allocator/destroy_range.inl" "$(@D)/cuda/include/thrust/detail/allocator/destroy_range.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/allocator/fill_construct_range.h" "$(@D)/cuda/include/thrust/detail/allocator/fill_construct_range.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/allocator/fill_construct_range.inl" "$(@D)/cuda/include/thrust/detail/allocator/fill_construct_range.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/allocator/malloc_allocator.h" "$(@D)/cuda/include/thrust/detail/allocator/malloc_allocator.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/allocator/malloc_allocator.inl" "$(@D)/cuda/include/thrust/detail/allocator/malloc_allocator.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/allocator/no_throw_allocator.h" "$(@D)/cuda/include/thrust/detail/allocator/no_throw_allocator.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/allocator/tagged_allocator.h" "$(@D)/cuda/include/thrust/detail/allocator/tagged_allocator.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/allocator/tagged_allocator.inl" "$(@D)/cuda/include/thrust/detail/allocator/tagged_allocator.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/allocator/temporary_allocator.h" "$(@D)/cuda/include/thrust/detail/allocator/temporary_allocator.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/allocator/temporary_allocator.inl" "$(@D)/cuda/include/thrust/detail/allocator/temporary_allocator.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/binary_search.inl" "$(@D)/cuda/include/thrust/detail/binary_search.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/complex/arithmetic.h" "$(@D)/cuda/include/thrust/detail/complex/arithmetic.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/complex/c99math.h" "$(@D)/cuda/include/thrust/detail/complex/c99math.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/complex/catrig.h" "$(@D)/cuda/include/thrust/detail/complex/catrig.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/complex/catrigf.h" "$(@D)/cuda/include/thrust/detail/complex/catrigf.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/complex/ccosh.h" "$(@D)/cuda/include/thrust/detail/complex/ccosh.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/complex/ccoshf.h" "$(@D)/cuda/include/thrust/detail/complex/ccoshf.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/complex/cexp.h" "$(@D)/cuda/include/thrust/detail/complex/cexp.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/complex/cexpf.h" "$(@D)/cuda/include/thrust/detail/complex/cexpf.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/complex/clog.h" "$(@D)/cuda/include/thrust/detail/complex/clog.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/complex/clogf.h" "$(@D)/cuda/include/thrust/detail/complex/clogf.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/complex/complex.inl" "$(@D)/cuda/include/thrust/detail/complex/complex.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/complex/cpow.h" "$(@D)/cuda/include/thrust/detail/complex/cpow.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/complex/cpowf.h" "$(@D)/cuda/include/thrust/detail/complex/cpowf.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/complex/cproj.h" "$(@D)/cuda/include/thrust/detail/complex/cproj.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/complex/csinh.h" "$(@D)/cuda/include/thrust/detail/complex/csinh.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/complex/csinhf.h" "$(@D)/cuda/include/thrust/detail/complex/csinhf.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/complex/csqrt.h" "$(@D)/cuda/include/thrust/detail/complex/csqrt.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/complex/csqrtf.h" "$(@D)/cuda/include/thrust/detail/complex/csqrtf.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/complex/ctanh.h" "$(@D)/cuda/include/thrust/detail/complex/ctanh.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/complex/ctanhf.h" "$(@D)/cuda/include/thrust/detail/complex/ctanhf.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/complex/math_private.h" "$(@D)/cuda/include/thrust/detail/complex/math_private.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/complex/stream.h" "$(@D)/cuda/include/thrust/detail/complex/stream.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/config.h" "$(@D)/cuda/include/thrust/detail/config.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/config/compiler.h" "$(@D)/cuda/include/thrust/detail/config/compiler.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/config/compiler_fence.h" "$(@D)/cuda/include/thrust/detail/config/compiler_fence.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/config/config.h" "$(@D)/cuda/include/thrust/detail/config/config.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/config/debug.h" "$(@D)/cuda/include/thrust/detail/config/debug.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/config/device_system.h" "$(@D)/cuda/include/thrust/detail/config/device_system.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/config/exec_check_disable.h" "$(@D)/cuda/include/thrust/detail/config/exec_check_disable.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/config/forceinline.h" "$(@D)/cuda/include/thrust/detail/config/forceinline.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/config/global_workarounds.h" "$(@D)/cuda/include/thrust/detail/config/global_workarounds.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/config/host_device.h" "$(@D)/cuda/include/thrust/detail/config/host_device.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/config/host_system.h" "$(@D)/cuda/include/thrust/detail/config/host_system.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/config/simple_defines.h" "$(@D)/cuda/include/thrust/detail/config/simple_defines.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/contiguous_storage.h" "$(@D)/cuda/include/thrust/detail/contiguous_storage.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/contiguous_storage.inl" "$(@D)/cuda/include/thrust/detail/contiguous_storage.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/copy.h" "$(@D)/cuda/include/thrust/detail/copy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/copy.inl" "$(@D)/cuda/include/thrust/detail/copy.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/copy_if.h" "$(@D)/cuda/include/thrust/detail/copy_if.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/copy_if.inl" "$(@D)/cuda/include/thrust/detail/copy_if.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/count.inl" "$(@D)/cuda/include/thrust/detail/count.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/cstdint.h" "$(@D)/cuda/include/thrust/detail/cstdint.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/device_delete.inl" "$(@D)/cuda/include/thrust/detail/device_delete.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/device_free.inl" "$(@D)/cuda/include/thrust/detail/device_free.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/device_malloc.inl" "$(@D)/cuda/include/thrust/detail/device_malloc.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/device_new.inl" "$(@D)/cuda/include/thrust/detail/device_new.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/device_ptr.inl" "$(@D)/cuda/include/thrust/detail/device_ptr.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/device_reference.inl" "$(@D)/cuda/include/thrust/detail/device_reference.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/device_vector.inl" "$(@D)/cuda/include/thrust/detail/device_vector.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/dispatch/is_trivial_copy.h" "$(@D)/cuda/include/thrust/detail/dispatch/is_trivial_copy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/distance.inl" "$(@D)/cuda/include/thrust/detail/distance.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/equal.inl" "$(@D)/cuda/include/thrust/detail/equal.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/execute_with_allocator.h" "$(@D)/cuda/include/thrust/detail/execute_with_allocator.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/execution_policy.h" "$(@D)/cuda/include/thrust/detail/execution_policy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/extrema.inl" "$(@D)/cuda/include/thrust/detail/extrema.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/fill.inl" "$(@D)/cuda/include/thrust/detail/fill.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/find.inl" "$(@D)/cuda/include/thrust/detail/find.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/for_each.inl" "$(@D)/cuda/include/thrust/detail/for_each.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/function.h" "$(@D)/cuda/include/thrust/detail/function.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/functional.inl" "$(@D)/cuda/include/thrust/detail/functional.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/functional/actor.h" "$(@D)/cuda/include/thrust/detail/functional/actor.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/functional/actor.inl" "$(@D)/cuda/include/thrust/detail/functional/actor.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/functional/argument.h" "$(@D)/cuda/include/thrust/detail/functional/argument.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/functional/composite.h" "$(@D)/cuda/include/thrust/detail/functional/composite.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/functional/operators.h" "$(@D)/cuda/include/thrust/detail/functional/operators.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/functional/operators/arithmetic_operators.h" "$(@D)/cuda/include/thrust/detail/functional/operators/arithmetic_operators.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/functional/operators/assignment_operator.h" "$(@D)/cuda/include/thrust/detail/functional/operators/assignment_operator.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/functional/operators/bitwise_operators.h" "$(@D)/cuda/include/thrust/detail/functional/operators/bitwise_operators.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/functional/operators/compound_assignment_operators.h" "$(@D)/cuda/include/thrust/detail/functional/operators/compound_assignment_operators.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/functional/operators/logical_operators.h" "$(@D)/cuda/include/thrust/detail/functional/operators/logical_operators.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/functional/operators/operator_adaptors.h" "$(@D)/cuda/include/thrust/detail/functional/operators/operator_adaptors.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/functional/operators/relational_operators.h" "$(@D)/cuda/include/thrust/detail/functional/operators/relational_operators.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/functional/placeholder.h" "$(@D)/cuda/include/thrust/detail/functional/placeholder.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/functional/value.h" "$(@D)/cuda/include/thrust/detail/functional/value.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/gather.inl" "$(@D)/cuda/include/thrust/detail/gather.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/generate.inl" "$(@D)/cuda/include/thrust/detail/generate.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/get_iterator_value.h" "$(@D)/cuda/include/thrust/detail/get_iterator_value.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/host_vector.inl" "$(@D)/cuda/include/thrust/detail/host_vector.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/inner_product.inl" "$(@D)/cuda/include/thrust/detail/inner_product.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/integer_math.h" "$(@D)/cuda/include/thrust/detail/integer_math.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/integer_traits.h" "$(@D)/cuda/include/thrust/detail/integer_traits.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/internal_functional.h" "$(@D)/cuda/include/thrust/detail/internal_functional.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/logical.inl" "$(@D)/cuda/include/thrust/detail/logical.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/malloc_and_free.h" "$(@D)/cuda/include/thrust/detail/malloc_and_free.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/merge.inl" "$(@D)/cuda/include/thrust/detail/merge.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/minmax.h" "$(@D)/cuda/include/thrust/detail/minmax.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/mismatch.inl" "$(@D)/cuda/include/thrust/detail/mismatch.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/mpl/math.h" "$(@D)/cuda/include/thrust/detail/mpl/math.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/numeric_traits.h" "$(@D)/cuda/include/thrust/detail/numeric_traits.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/overlapped_copy.h" "$(@D)/cuda/include/thrust/detail/overlapped_copy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/pair.inl" "$(@D)/cuda/include/thrust/detail/pair.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/partition.inl" "$(@D)/cuda/include/thrust/detail/partition.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/pointer.h" "$(@D)/cuda/include/thrust/detail/pointer.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/pointer.inl" "$(@D)/cuda/include/thrust/detail/pointer.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/range/head_flags.h" "$(@D)/cuda/include/thrust/detail/range/head_flags.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/range/tail_flags.h" "$(@D)/cuda/include/thrust/detail/range/tail_flags.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/raw_pointer_cast.h" "$(@D)/cuda/include/thrust/detail/raw_pointer_cast.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/raw_reference_cast.h" "$(@D)/cuda/include/thrust/detail/raw_reference_cast.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/reduce.inl" "$(@D)/cuda/include/thrust/detail/reduce.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/reference.h" "$(@D)/cuda/include/thrust/detail/reference.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/reference.inl" "$(@D)/cuda/include/thrust/detail/reference.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/reference_forward_declaration.h" "$(@D)/cuda/include/thrust/detail/reference_forward_declaration.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/remove.inl" "$(@D)/cuda/include/thrust/detail/remove.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/replace.inl" "$(@D)/cuda/include/thrust/detail/replace.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/reverse.inl" "$(@D)/cuda/include/thrust/detail/reverse.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/scan.inl" "$(@D)/cuda/include/thrust/detail/scan.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/scatter.inl" "$(@D)/cuda/include/thrust/detail/scatter.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/seq.h" "$(@D)/cuda/include/thrust/detail/seq.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/sequence.inl" "$(@D)/cuda/include/thrust/detail/sequence.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/set_operations.inl" "$(@D)/cuda/include/thrust/detail/set_operations.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/sort.inl" "$(@D)/cuda/include/thrust/detail/sort.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/static_assert.h" "$(@D)/cuda/include/thrust/detail/static_assert.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/static_map.h" "$(@D)/cuda/include/thrust/detail/static_map.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/swap.h" "$(@D)/cuda/include/thrust/detail/swap.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/swap.inl" "$(@D)/cuda/include/thrust/detail/swap.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/swap_ranges.inl" "$(@D)/cuda/include/thrust/detail/swap_ranges.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/tabulate.inl" "$(@D)/cuda/include/thrust/detail/tabulate.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/temporary_array.h" "$(@D)/cuda/include/thrust/detail/temporary_array.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/temporary_array.inl" "$(@D)/cuda/include/thrust/detail/temporary_array.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/temporary_buffer.h" "$(@D)/cuda/include/thrust/detail/temporary_buffer.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/transform.inl" "$(@D)/cuda/include/thrust/detail/transform.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/transform_reduce.inl" "$(@D)/cuda/include/thrust/detail/transform_reduce.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/transform_scan.inl" "$(@D)/cuda/include/thrust/detail/transform_scan.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/trivial_sequence.h" "$(@D)/cuda/include/thrust/detail/trivial_sequence.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/tuple.inl" "$(@D)/cuda/include/thrust/detail/tuple.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/tuple_meta_transform.h" "$(@D)/cuda/include/thrust/detail/tuple_meta_transform.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/tuple_transform.h" "$(@D)/cuda/include/thrust/detail/tuple_transform.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/type_traits.h" "$(@D)/cuda/include/thrust/detail/type_traits.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/type_traits/algorithm/intermediate_type_from_function_and_iterators.h" "$(@D)/cuda/include/thrust/detail/type_traits/algorithm/intermediate_type_from_function_and_iterators.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/type_traits/function_traits.h" "$(@D)/cuda/include/thrust/detail/type_traits/function_traits.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/type_traits/has_member_function.h" "$(@D)/cuda/include/thrust/detail/type_traits/has_member_function.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/type_traits/has_nested_type.h" "$(@D)/cuda/include/thrust/detail/type_traits/has_nested_type.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/type_traits/has_trivial_assign.h" "$(@D)/cuda/include/thrust/detail/type_traits/has_trivial_assign.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/type_traits/is_call_possible.h" "$(@D)/cuda/include/thrust/detail/type_traits/is_call_possible.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/type_traits/is_metafunction_defined.h" "$(@D)/cuda/include/thrust/detail/type_traits/is_metafunction_defined.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/type_traits/iterator/is_discard_iterator.h" "$(@D)/cuda/include/thrust/detail/type_traits/iterator/is_discard_iterator.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/type_traits/iterator/is_output_iterator.h" "$(@D)/cuda/include/thrust/detail/type_traits/iterator/is_output_iterator.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/type_traits/minimum_type.h" "$(@D)/cuda/include/thrust/detail/type_traits/minimum_type.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/type_traits/pointer_traits.h" "$(@D)/cuda/include/thrust/detail/type_traits/pointer_traits.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/type_traits/result_of_adaptable_function.h" "$(@D)/cuda/include/thrust/detail/type_traits/result_of_adaptable_function.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/uninitialized_copy.inl" "$(@D)/cuda/include/thrust/detail/uninitialized_copy.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/uninitialized_fill.inl" "$(@D)/cuda/include/thrust/detail/uninitialized_fill.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/unique.inl" "$(@D)/cuda/include/thrust/detail/unique.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/use_default.h" "$(@D)/cuda/include/thrust/detail/use_default.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/util/align.h" "$(@D)/cuda/include/thrust/detail/util/align.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/util/blocking.h" "$(@D)/cuda/include/thrust/detail/util/blocking.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/vector_base.h" "$(@D)/cuda/include/thrust/detail/vector_base.h" && cp -f "/usr/local/cuda-9.0/include/thrust/detail/vector_base.inl" "$(@D)/cuda/include/thrust/detail/vector_base.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/device_allocator.h" "$(@D)/cuda/include/thrust/device_allocator.h" && cp -f "/usr/local/cuda-9.0/include/thrust/device_delete.h" "$(@D)/cuda/include/thrust/device_delete.h" && cp -f "/usr/local/cuda-9.0/include/thrust/device_free.h" "$(@D)/cuda/include/thrust/device_free.h" && cp -f "/usr/local/cuda-9.0/include/thrust/device_malloc.h" "$(@D)/cuda/include/thrust/device_malloc.h" && cp -f "/usr/local/cuda-9.0/include/thrust/device_malloc_allocator.h" "$(@D)/cuda/include/thrust/device_malloc_allocator.h" && cp -f "/usr/local/cuda-9.0/include/thrust/device_new.h" "$(@D)/cuda/include/thrust/device_new.h" && cp -f "/usr/local/cuda-9.0/include/thrust/device_new_allocator.h" "$(@D)/cuda/include/thrust/device_new_allocator.h" && cp -f "/usr/local/cuda-9.0/include/thrust/device_ptr.h" "$(@D)/cuda/include/thrust/device_ptr.h" && cp -f "/usr/local/cuda-9.0/include/thrust/device_reference.h" "$(@D)/cuda/include/thrust/device_reference.h" && cp -f "/usr/local/cuda-9.0/include/thrust/device_vector.h" "$(@D)/cuda/include/thrust/device_vector.h" && cp -f "/usr/local/cuda-9.0/include/thrust/distance.h" "$(@D)/cuda/include/thrust/distance.h" && cp -f "/usr/local/cuda-9.0/include/thrust/equal.h" "$(@D)/cuda/include/thrust/equal.h" && cp -f "/usr/local/cuda-9.0/include/thrust/execution_policy.h" "$(@D)/cuda/include/thrust/execution_policy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/extrema.h" "$(@D)/cuda/include/thrust/extrema.h" && cp -f "/usr/local/cuda-9.0/include/thrust/fill.h" "$(@D)/cuda/include/thrust/fill.h" && cp -f "/usr/local/cuda-9.0/include/thrust/find.h" "$(@D)/cuda/include/thrust/find.h" && cp -f "/usr/local/cuda-9.0/include/thrust/for_each.h" "$(@D)/cuda/include/thrust/for_each.h" && cp -f "/usr/local/cuda-9.0/include/thrust/functional.h" "$(@D)/cuda/include/thrust/functional.h" && cp -f "/usr/local/cuda-9.0/include/thrust/gather.h" "$(@D)/cuda/include/thrust/gather.h" && cp -f "/usr/local/cuda-9.0/include/thrust/generate.h" "$(@D)/cuda/include/thrust/generate.h" && cp -f "/usr/local/cuda-9.0/include/thrust/host_vector.h" "$(@D)/cuda/include/thrust/host_vector.h" && cp -f "/usr/local/cuda-9.0/include/thrust/inner_product.h" "$(@D)/cuda/include/thrust/inner_product.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/constant_iterator.h" "$(@D)/cuda/include/thrust/iterator/constant_iterator.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/counting_iterator.h" "$(@D)/cuda/include/thrust/iterator/counting_iterator.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/any_assign.h" "$(@D)/cuda/include/thrust/iterator/detail/any_assign.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/any_system_tag.h" "$(@D)/cuda/include/thrust/iterator/detail/any_system_tag.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/constant_iterator_base.h" "$(@D)/cuda/include/thrust/iterator/detail/constant_iterator_base.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/counting_iterator.inl" "$(@D)/cuda/include/thrust/iterator/detail/counting_iterator.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/device_system_tag.h" "$(@D)/cuda/include/thrust/iterator/detail/device_system_tag.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/discard_iterator_base.h" "$(@D)/cuda/include/thrust/iterator/detail/discard_iterator_base.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/distance_from_result.h" "$(@D)/cuda/include/thrust/iterator/detail/distance_from_result.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/host_system_tag.h" "$(@D)/cuda/include/thrust/iterator/detail/host_system_tag.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/is_iterator_category.h" "$(@D)/cuda/include/thrust/iterator/detail/is_iterator_category.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/is_trivial_iterator.h" "$(@D)/cuda/include/thrust/iterator/detail/is_trivial_iterator.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/iterator_adaptor_base.h" "$(@D)/cuda/include/thrust/iterator/detail/iterator_adaptor_base.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/iterator_category_to_system.h" "$(@D)/cuda/include/thrust/iterator/detail/iterator_category_to_system.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/iterator_category_to_traversal.h" "$(@D)/cuda/include/thrust/iterator/detail/iterator_category_to_traversal.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/iterator_category_with_system_and_traversal.h" "$(@D)/cuda/include/thrust/iterator/detail/iterator_category_with_system_and_traversal.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/iterator_facade_category.h" "$(@D)/cuda/include/thrust/iterator/detail/iterator_facade_category.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/iterator_traits.inl" "$(@D)/cuda/include/thrust/iterator/detail/iterator_traits.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/iterator_traversal_tags.h" "$(@D)/cuda/include/thrust/iterator/detail/iterator_traversal_tags.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/join_iterator.h" "$(@D)/cuda/include/thrust/iterator/detail/join_iterator.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/minimum_category.h" "$(@D)/cuda/include/thrust/iterator/detail/minimum_category.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/minimum_system.h" "$(@D)/cuda/include/thrust/iterator/detail/minimum_system.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/normal_iterator.h" "$(@D)/cuda/include/thrust/iterator/detail/normal_iterator.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/permutation_iterator_base.h" "$(@D)/cuda/include/thrust/iterator/detail/permutation_iterator_base.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/retag.h" "$(@D)/cuda/include/thrust/iterator/detail/retag.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/reverse_iterator.inl" "$(@D)/cuda/include/thrust/iterator/detail/reverse_iterator.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/reverse_iterator_base.h" "$(@D)/cuda/include/thrust/iterator/detail/reverse_iterator_base.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/tagged_iterator.h" "$(@D)/cuda/include/thrust/iterator/detail/tagged_iterator.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/transform_iterator.inl" "$(@D)/cuda/include/thrust/iterator/detail/transform_iterator.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/transform_output_iterator.inl" "$(@D)/cuda/include/thrust/iterator/detail/transform_output_iterator.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/tuple_of_iterator_references.h" "$(@D)/cuda/include/thrust/iterator/detail/tuple_of_iterator_references.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/universal_categories.h" "$(@D)/cuda/include/thrust/iterator/detail/universal_categories.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/zip_iterator.inl" "$(@D)/cuda/include/thrust/iterator/detail/zip_iterator.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/detail/zip_iterator_base.h" "$(@D)/cuda/include/thrust/iterator/detail/zip_iterator_base.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/discard_iterator.h" "$(@D)/cuda/include/thrust/iterator/discard_iterator.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/iterator_adaptor.h" "$(@D)/cuda/include/thrust/iterator/iterator_adaptor.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/iterator_categories.h" "$(@D)/cuda/include/thrust/iterator/iterator_categories.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/iterator_facade.h" "$(@D)/cuda/include/thrust/iterator/iterator_facade.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/iterator_traits.h" "$(@D)/cuda/include/thrust/iterator/iterator_traits.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/permutation_iterator.h" "$(@D)/cuda/include/thrust/iterator/permutation_iterator.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/retag.h" "$(@D)/cuda/include/thrust/iterator/retag.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/reverse_iterator.h" "$(@D)/cuda/include/thrust/iterator/reverse_iterator.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/transform_iterator.h" "$(@D)/cuda/include/thrust/iterator/transform_iterator.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/transform_output_iterator.h" "$(@D)/cuda/include/thrust/iterator/transform_output_iterator.h" && cp -f "/usr/local/cuda-9.0/include/thrust/iterator/zip_iterator.h" "$(@D)/cuda/include/thrust/iterator/zip_iterator.h" && cp -f "/usr/local/cuda-9.0/include/thrust/logical.h" "$(@D)/cuda/include/thrust/logical.h" && cp -f "/usr/local/cuda-9.0/include/thrust/memory.h" "$(@D)/cuda/include/thrust/memory.h" && cp -f "/usr/local/cuda-9.0/include/thrust/merge.h" "$(@D)/cuda/include/thrust/merge.h" && cp -f "/usr/local/cuda-9.0/include/thrust/mismatch.h" "$(@D)/cuda/include/thrust/mismatch.h" && cp -f "/usr/local/cuda-9.0/include/thrust/pair.h" "$(@D)/cuda/include/thrust/pair.h" && cp -f "/usr/local/cuda-9.0/include/thrust/partition.h" "$(@D)/cuda/include/thrust/partition.h" && cp -f "/usr/local/cuda-9.0/include/thrust/random.h" "$(@D)/cuda/include/thrust/random.h" && cp -f "/usr/local/cuda-9.0/include/thrust/random/detail/discard_block_engine.inl" "$(@D)/cuda/include/thrust/random/detail/discard_block_engine.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/random/detail/linear_congruential_engine.inl" "$(@D)/cuda/include/thrust/random/detail/linear_congruential_engine.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/random/detail/linear_congruential_engine_discard.h" "$(@D)/cuda/include/thrust/random/detail/linear_congruential_engine_discard.h" && cp -f "/usr/local/cuda-9.0/include/thrust/random/detail/linear_feedback_shift_engine.inl" "$(@D)/cuda/include/thrust/random/detail/linear_feedback_shift_engine.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/random/detail/linear_feedback_shift_engine_wordmask.h" "$(@D)/cuda/include/thrust/random/detail/linear_feedback_shift_engine_wordmask.h" && cp -f "/usr/local/cuda-9.0/include/thrust/random/detail/mod.h" "$(@D)/cuda/include/thrust/random/detail/mod.h" && cp -f "/usr/local/cuda-9.0/include/thrust/random/detail/normal_distribution.inl" "$(@D)/cuda/include/thrust/random/detail/normal_distribution.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/random/detail/normal_distribution_base.h" "$(@D)/cuda/include/thrust/random/detail/normal_distribution_base.h" && cp -f "/usr/local/cuda-9.0/include/thrust/random/detail/random_core_access.h" "$(@D)/cuda/include/thrust/random/detail/random_core_access.h" && cp -f "/usr/local/cuda-9.0/include/thrust/random/detail/subtract_with_carry_engine.inl" "$(@D)/cuda/include/thrust/random/detail/subtract_with_carry_engine.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/random/detail/uniform_int_distribution.inl" "$(@D)/cuda/include/thrust/random/detail/uniform_int_distribution.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/random/detail/uniform_real_distribution.inl" "$(@D)/cuda/include/thrust/random/detail/uniform_real_distribution.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/random/detail/xor_combine_engine.inl" "$(@D)/cuda/include/thrust/random/detail/xor_combine_engine.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/random/detail/xor_combine_engine_max.h" "$(@D)/cuda/include/thrust/random/detail/xor_combine_engine_max.h" && cp -f "/usr/local/cuda-9.0/include/thrust/random/discard_block_engine.h" "$(@D)/cuda/include/thrust/random/discard_block_engine.h" && cp -f "/usr/local/cuda-9.0/include/thrust/random/linear_congruential_engine.h" "$(@D)/cuda/include/thrust/random/linear_congruential_engine.h" && cp -f "/usr/local/cuda-9.0/include/thrust/random/linear_feedback_shift_engine.h" "$(@D)/cuda/include/thrust/random/linear_feedback_shift_engine.h" && cp -f "/usr/local/cuda-9.0/include/thrust/random/normal_distribution.h" "$(@D)/cuda/include/thrust/random/normal_distribution.h" && cp -f "/usr/local/cuda-9.0/include/thrust/random/subtract_with_carry_engine.h" "$(@D)/cuda/include/thrust/random/subtract_with_carry_engine.h" && cp -f "/usr/local/cuda-9.0/include/thrust/random/uniform_int_distribution.h" "$(@D)/cuda/include/thrust/random/uniform_int_distribution.h" && cp -f "/usr/local/cuda-9.0/include/thrust/random/uniform_real_distribution.h" "$(@D)/cuda/include/thrust/random/uniform_real_distribution.h" && cp -f "/usr/local/cuda-9.0/include/thrust/random/xor_combine_engine.h" "$(@D)/cuda/include/thrust/random/xor_combine_engine.h" && cp -f "/usr/local/cuda-9.0/include/thrust/reduce.h" "$(@D)/cuda/include/thrust/reduce.h" && cp -f "/usr/local/cuda-9.0/include/thrust/remove.h" "$(@D)/cuda/include/thrust/remove.h" && cp -f "/usr/local/cuda-9.0/include/thrust/replace.h" "$(@D)/cuda/include/thrust/replace.h" && cp -f "/usr/local/cuda-9.0/include/thrust/reverse.h" "$(@D)/cuda/include/thrust/reverse.h" && cp -f "/usr/local/cuda-9.0/include/thrust/scan.h" "$(@D)/cuda/include/thrust/scan.h" && cp -f "/usr/local/cuda-9.0/include/thrust/scatter.h" "$(@D)/cuda/include/thrust/scatter.h" && cp -f "/usr/local/cuda-9.0/include/thrust/sequence.h" "$(@D)/cuda/include/thrust/sequence.h" && cp -f "/usr/local/cuda-9.0/include/thrust/set_operations.h" "$(@D)/cuda/include/thrust/set_operations.h" && cp -f "/usr/local/cuda-9.0/include/thrust/sort.h" "$(@D)/cuda/include/thrust/sort.h" && cp -f "/usr/local/cuda-9.0/include/thrust/swap.h" "$(@D)/cuda/include/thrust/swap.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/adjacent_difference.h" "$(@D)/cuda/include/thrust/system/cpp/detail/adjacent_difference.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/assign_value.h" "$(@D)/cuda/include/thrust/system/cpp/detail/assign_value.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/binary_search.h" "$(@D)/cuda/include/thrust/system/cpp/detail/binary_search.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/copy.h" "$(@D)/cuda/include/thrust/system/cpp/detail/copy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/copy_if.h" "$(@D)/cuda/include/thrust/system/cpp/detail/copy_if.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/count.h" "$(@D)/cuda/include/thrust/system/cpp/detail/count.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/equal.h" "$(@D)/cuda/include/thrust/system/cpp/detail/equal.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/execution_policy.h" "$(@D)/cuda/include/thrust/system/cpp/detail/execution_policy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/extrema.h" "$(@D)/cuda/include/thrust/system/cpp/detail/extrema.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/fill.h" "$(@D)/cuda/include/thrust/system/cpp/detail/fill.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/find.h" "$(@D)/cuda/include/thrust/system/cpp/detail/find.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/for_each.h" "$(@D)/cuda/include/thrust/system/cpp/detail/for_each.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/gather.h" "$(@D)/cuda/include/thrust/system/cpp/detail/gather.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/generate.h" "$(@D)/cuda/include/thrust/system/cpp/detail/generate.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/get_value.h" "$(@D)/cuda/include/thrust/system/cpp/detail/get_value.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/inner_product.h" "$(@D)/cuda/include/thrust/system/cpp/detail/inner_product.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/iter_swap.h" "$(@D)/cuda/include/thrust/system/cpp/detail/iter_swap.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/logical.h" "$(@D)/cuda/include/thrust/system/cpp/detail/logical.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/malloc_and_free.h" "$(@D)/cuda/include/thrust/system/cpp/detail/malloc_and_free.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/memory.inl" "$(@D)/cuda/include/thrust/system/cpp/detail/memory.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/merge.h" "$(@D)/cuda/include/thrust/system/cpp/detail/merge.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/mismatch.h" "$(@D)/cuda/include/thrust/system/cpp/detail/mismatch.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/par.h" "$(@D)/cuda/include/thrust/system/cpp/detail/par.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/partition.h" "$(@D)/cuda/include/thrust/system/cpp/detail/partition.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/reduce.h" "$(@D)/cuda/include/thrust/system/cpp/detail/reduce.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/reduce_by_key.h" "$(@D)/cuda/include/thrust/system/cpp/detail/reduce_by_key.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/remove.h" "$(@D)/cuda/include/thrust/system/cpp/detail/remove.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/replace.h" "$(@D)/cuda/include/thrust/system/cpp/detail/replace.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/reverse.h" "$(@D)/cuda/include/thrust/system/cpp/detail/reverse.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/scan.h" "$(@D)/cuda/include/thrust/system/cpp/detail/scan.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/scan_by_key.h" "$(@D)/cuda/include/thrust/system/cpp/detail/scan_by_key.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/scatter.h" "$(@D)/cuda/include/thrust/system/cpp/detail/scatter.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/sequence.h" "$(@D)/cuda/include/thrust/system/cpp/detail/sequence.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/set_operations.h" "$(@D)/cuda/include/thrust/system/cpp/detail/set_operations.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/sort.h" "$(@D)/cuda/include/thrust/system/cpp/detail/sort.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/swap_ranges.h" "$(@D)/cuda/include/thrust/system/cpp/detail/swap_ranges.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/tabulate.h" "$(@D)/cuda/include/thrust/system/cpp/detail/tabulate.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/temporary_buffer.h" "$(@D)/cuda/include/thrust/system/cpp/detail/temporary_buffer.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/transform.h" "$(@D)/cuda/include/thrust/system/cpp/detail/transform.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/transform_reduce.h" "$(@D)/cuda/include/thrust/system/cpp/detail/transform_reduce.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/transform_scan.h" "$(@D)/cuda/include/thrust/system/cpp/detail/transform_scan.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/uninitialized_copy.h" "$(@D)/cuda/include/thrust/system/cpp/detail/uninitialized_copy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/uninitialized_fill.h" "$(@D)/cuda/include/thrust/system/cpp/detail/uninitialized_fill.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/unique.h" "$(@D)/cuda/include/thrust/system/cpp/detail/unique.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/unique_by_key.h" "$(@D)/cuda/include/thrust/system/cpp/detail/unique_by_key.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/detail/vector.inl" "$(@D)/cuda/include/thrust/system/cpp/detail/vector.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/execution_policy.h" "$(@D)/cuda/include/thrust/system/cpp/execution_policy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/memory.h" "$(@D)/cuda/include/thrust/system/cpp/memory.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cpp/vector.h" "$(@D)/cuda/include/thrust/system/cpp/vector.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/config.h" "$(@D)/cuda/include/thrust/system/cuda/config.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/adjacent_difference.h" "$(@D)/cuda/include/thrust/system/cuda/detail/adjacent_difference.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/assign_value.h" "$(@D)/cuda/include/thrust/system/cuda/detail/assign_value.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/binary_search.h" "$(@D)/cuda/include/thrust/system/cuda/detail/binary_search.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/copy.h" "$(@D)/cuda/include/thrust/system/cuda/detail/copy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/copy_if.h" "$(@D)/cuda/include/thrust/system/cuda/detail/copy_if.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/core/agent_launcher.h" "$(@D)/cuda/include/thrust/system/cuda/detail/core/agent_launcher.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/core/alignment.h" "$(@D)/cuda/include/thrust/system/cuda/detail/core/alignment.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/core/triple_chevron_launch.h" "$(@D)/cuda/include/thrust/system/cuda/detail/core/triple_chevron_launch.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/core/util.h" "$(@D)/cuda/include/thrust/system/cuda/detail/core/util.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/count.h" "$(@D)/cuda/include/thrust/system/cuda/detail/count.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cross_system.h" "$(@D)/cuda/include/thrust/system/cuda/detail/cross_system.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/agent/agent_histogram.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_histogram.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/agent/agent_radix_sort_downsweep.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_radix_sort_downsweep.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/agent/agent_radix_sort_upsweep.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_radix_sort_upsweep.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/agent/agent_reduce.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_reduce.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/agent/agent_reduce_by_key.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_reduce_by_key.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/agent/agent_rle.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_rle.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/agent/agent_scan.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_scan.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/agent/agent_segment_fixup.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_segment_fixup.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/agent/agent_select_if.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_select_if.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/agent/agent_spmv_csrt.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_spmv_csrt.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/agent/agent_spmv_orig.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_spmv_orig.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/agent/agent_spmv_row_based.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/agent_spmv_row_based.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/agent/single_pass_scan_operators.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/agent/single_pass_scan_operators.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/block_adjacent_difference.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_adjacent_difference.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/block_discontinuity.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_discontinuity.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/block_exchange.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_exchange.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/block_histogram.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_histogram.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/block_load.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_load.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/block_radix_rank.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_radix_rank.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/block_radix_sort.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_radix_sort.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/block_raking_layout.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_raking_layout.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/block_reduce.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_reduce.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/block_scan.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_scan.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/block_shuffle.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_shuffle.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/block_store.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/block_store.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/specializations/block_histogram_atomic.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_histogram_atomic.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/specializations/block_histogram_sort.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_histogram_sort.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/specializations/block_reduce_raking.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_reduce_raking.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/specializations/block_reduce_raking_commutative_only.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_reduce_raking_commutative_only.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/specializations/block_reduce_warp_reductions.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_reduce_warp_reductions.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_raking.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_raking.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_warp_scans.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_warp_scans.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_warp_scans2.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_warp_scans2.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_warp_scans3.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/block/specializations/block_scan_warp_scans3.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/cub.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/cub.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/device_histogram.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_histogram.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/device_partition.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_partition.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/device_radix_sort.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_radix_sort.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/device_reduce.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_reduce.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/device_run_length_encode.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_run_length_encode.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/device_scan.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_scan.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/device_segmented_radix_sort.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_segmented_radix_sort.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/device_segmented_reduce.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_segmented_reduce.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/device_select.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_select.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/device_spmv.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/device_spmv.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_histogram.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_histogram.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_radix_sort.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_radix_sort.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_reduce.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_reduce.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_reduce_by_key.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_reduce_by_key.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_rle.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_rle.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_scan.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_scan.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_select_if.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_select_if.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_spmv_csrt.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_spmv_csrt.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_spmv_orig.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_spmv_orig.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_spmv_row_based.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/device/dispatch/dispatch_spmv_row_based.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/grid/grid_barrier.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/grid/grid_barrier.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/grid/grid_even_share.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/grid/grid_even_share.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/grid/grid_mapping.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/grid/grid_mapping.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/grid/grid_queue.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/grid/grid_queue.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/host/mutex.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/host/mutex.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/iterator/arg_index_input_iterator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/iterator/arg_index_input_iterator.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/iterator/cache_modified_input_iterator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/iterator/cache_modified_input_iterator.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/iterator/cache_modified_output_iterator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/iterator/cache_modified_output_iterator.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/iterator/constant_input_iterator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/iterator/constant_input_iterator.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/iterator/counting_input_iterator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/iterator/counting_input_iterator.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/iterator/discard_output_iterator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/iterator/discard_output_iterator.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/iterator/tex_obj_input_iterator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/iterator/tex_obj_input_iterator.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/iterator/tex_ref_input_iterator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/iterator/tex_ref_input_iterator.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/iterator/transform_input_iterator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/iterator/transform_input_iterator.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/thread/thread_load.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/thread/thread_load.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/thread/thread_operators.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/thread/thread_operators.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/thread/thread_reduce.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/thread/thread_reduce.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/thread/thread_scan.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/thread/thread_scan.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/thread/thread_search.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/thread/thread_search.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/thread/thread_store.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/thread/thread_store.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/util_allocator.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/util_allocator.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/util_arch.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/util_arch.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/util_debug.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/util_debug.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/util_device.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/util_device.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/util_macro.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/util_macro.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/util_namespace.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/util_namespace.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/util_ptx.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/util_ptx.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/util_type.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/util_type.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/warp/specializations/warp_reduce_shfl.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/warp/specializations/warp_reduce_shfl.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/warp/specializations/warp_reduce_smem.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/warp/specializations/warp_reduce_smem.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/warp/specializations/warp_scan_shfl.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/warp/specializations/warp_scan_shfl.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/warp/specializations/warp_scan_smem.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/warp/specializations/warp_scan_smem.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/warp/warp_reduce.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/warp/warp_reduce.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/cub/warp/warp_scan.cuh" "$(@D)/cuda/include/thrust/system/cuda/detail/cub/warp/warp_scan.cuh" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/equal.h" "$(@D)/cuda/include/thrust/system/cuda/detail/equal.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/error.inl" "$(@D)/cuda/include/thrust/system/cuda/detail/error.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/execution_policy.h" "$(@D)/cuda/include/thrust/system/cuda/detail/execution_policy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/extrema.h" "$(@D)/cuda/include/thrust/system/cuda/detail/extrema.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/fill.h" "$(@D)/cuda/include/thrust/system/cuda/detail/fill.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/find.h" "$(@D)/cuda/include/thrust/system/cuda/detail/find.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/for_each.h" "$(@D)/cuda/include/thrust/system/cuda/detail/for_each.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/gather.h" "$(@D)/cuda/include/thrust/system/cuda/detail/gather.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/generate.h" "$(@D)/cuda/include/thrust/system/cuda/detail/generate.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/get_value.h" "$(@D)/cuda/include/thrust/system/cuda/detail/get_value.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/guarded_cuda_runtime_api.h" "$(@D)/cuda/include/thrust/system/cuda/detail/guarded_cuda_runtime_api.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/guarded_driver_types.h" "$(@D)/cuda/include/thrust/system/cuda/detail/guarded_driver_types.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/inner_product.h" "$(@D)/cuda/include/thrust/system/cuda/detail/inner_product.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/internal/copy_cross_system.h" "$(@D)/cuda/include/thrust/system/cuda/detail/internal/copy_cross_system.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/internal/copy_device_to_device.h" "$(@D)/cuda/include/thrust/system/cuda/detail/internal/copy_device_to_device.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/iter_swap.h" "$(@D)/cuda/include/thrust/system/cuda/detail/iter_swap.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/logical.h" "$(@D)/cuda/include/thrust/system/cuda/detail/logical.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/malloc_and_free.h" "$(@D)/cuda/include/thrust/system/cuda/detail/malloc_and_free.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/memory.inl" "$(@D)/cuda/include/thrust/system/cuda/detail/memory.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/memory_buffer.h" "$(@D)/cuda/include/thrust/system/cuda/detail/memory_buffer.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/merge.h" "$(@D)/cuda/include/thrust/system/cuda/detail/merge.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/mismatch.h" "$(@D)/cuda/include/thrust/system/cuda/detail/mismatch.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/par.h" "$(@D)/cuda/include/thrust/system/cuda/detail/par.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/par_to_seq.h" "$(@D)/cuda/include/thrust/system/cuda/detail/par_to_seq.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/parallel_for.h" "$(@D)/cuda/include/thrust/system/cuda/detail/parallel_for.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/partition.h" "$(@D)/cuda/include/thrust/system/cuda/detail/partition.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/reduce.h" "$(@D)/cuda/include/thrust/system/cuda/detail/reduce.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/reduce_by_key.h" "$(@D)/cuda/include/thrust/system/cuda/detail/reduce_by_key.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/remove.h" "$(@D)/cuda/include/thrust/system/cuda/detail/remove.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/replace.h" "$(@D)/cuda/include/thrust/system/cuda/detail/replace.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/reverse.h" "$(@D)/cuda/include/thrust/system/cuda/detail/reverse.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/scan.h" "$(@D)/cuda/include/thrust/system/cuda/detail/scan.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/scan_by_key.h" "$(@D)/cuda/include/thrust/system/cuda/detail/scan_by_key.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/scatter.h" "$(@D)/cuda/include/thrust/system/cuda/detail/scatter.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/sequence.h" "$(@D)/cuda/include/thrust/system/cuda/detail/sequence.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/set_operations.h" "$(@D)/cuda/include/thrust/system/cuda/detail/set_operations.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/sort.h" "$(@D)/cuda/include/thrust/system/cuda/detail/sort.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/swap_ranges.h" "$(@D)/cuda/include/thrust/system/cuda/detail/swap_ranges.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/tabulate.h" "$(@D)/cuda/include/thrust/system/cuda/detail/tabulate.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/temporary_buffer.h" "$(@D)/cuda/include/thrust/system/cuda/detail/temporary_buffer.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/terminate.h" "$(@D)/cuda/include/thrust/system/cuda/detail/terminate.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/transform.h" "$(@D)/cuda/include/thrust/system/cuda/detail/transform.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/transform_reduce.h" "$(@D)/cuda/include/thrust/system/cuda/detail/transform_reduce.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/transform_scan.h" "$(@D)/cuda/include/thrust/system/cuda/detail/transform_scan.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/uninitialized_copy.h" "$(@D)/cuda/include/thrust/system/cuda/detail/uninitialized_copy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/uninitialized_fill.h" "$(@D)/cuda/include/thrust/system/cuda/detail/uninitialized_fill.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/unique.h" "$(@D)/cuda/include/thrust/system/cuda/detail/unique.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/unique_by_key.h" "$(@D)/cuda/include/thrust/system/cuda/detail/unique_by_key.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/util.h" "$(@D)/cuda/include/thrust/system/cuda/detail/util.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/detail/vector.inl" "$(@D)/cuda/include/thrust/system/cuda/detail/vector.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/error.h" "$(@D)/cuda/include/thrust/system/cuda/error.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/execution_policy.h" "$(@D)/cuda/include/thrust/system/cuda/execution_policy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/experimental/pinned_allocator.h" "$(@D)/cuda/include/thrust/system/cuda/experimental/pinned_allocator.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/memory.h" "$(@D)/cuda/include/thrust/system/cuda/memory.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/cuda/vector.h" "$(@D)/cuda/include/thrust/system/cuda/vector.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/adjacent_difference.h" "$(@D)/cuda/include/thrust/system/detail/adl/adjacent_difference.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/assign_value.h" "$(@D)/cuda/include/thrust/system/detail/adl/assign_value.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/binary_search.h" "$(@D)/cuda/include/thrust/system/detail/adl/binary_search.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/copy.h" "$(@D)/cuda/include/thrust/system/detail/adl/copy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/copy_if.h" "$(@D)/cuda/include/thrust/system/detail/adl/copy_if.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/count.h" "$(@D)/cuda/include/thrust/system/detail/adl/count.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/equal.h" "$(@D)/cuda/include/thrust/system/detail/adl/equal.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/extrema.h" "$(@D)/cuda/include/thrust/system/detail/adl/extrema.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/fill.h" "$(@D)/cuda/include/thrust/system/detail/adl/fill.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/find.h" "$(@D)/cuda/include/thrust/system/detail/adl/find.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/for_each.h" "$(@D)/cuda/include/thrust/system/detail/adl/for_each.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/gather.h" "$(@D)/cuda/include/thrust/system/detail/adl/gather.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/generate.h" "$(@D)/cuda/include/thrust/system/detail/adl/generate.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/get_value.h" "$(@D)/cuda/include/thrust/system/detail/adl/get_value.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/inner_product.h" "$(@D)/cuda/include/thrust/system/detail/adl/inner_product.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/iter_swap.h" "$(@D)/cuda/include/thrust/system/detail/adl/iter_swap.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/logical.h" "$(@D)/cuda/include/thrust/system/detail/adl/logical.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/malloc_and_free.h" "$(@D)/cuda/include/thrust/system/detail/adl/malloc_and_free.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/merge.h" "$(@D)/cuda/include/thrust/system/detail/adl/merge.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/mismatch.h" "$(@D)/cuda/include/thrust/system/detail/adl/mismatch.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/partition.h" "$(@D)/cuda/include/thrust/system/detail/adl/partition.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/reduce.h" "$(@D)/cuda/include/thrust/system/detail/adl/reduce.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/reduce_by_key.h" "$(@D)/cuda/include/thrust/system/detail/adl/reduce_by_key.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/remove.h" "$(@D)/cuda/include/thrust/system/detail/adl/remove.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/replace.h" "$(@D)/cuda/include/thrust/system/detail/adl/replace.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/reverse.h" "$(@D)/cuda/include/thrust/system/detail/adl/reverse.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/scan.h" "$(@D)/cuda/include/thrust/system/detail/adl/scan.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/scan_by_key.h" "$(@D)/cuda/include/thrust/system/detail/adl/scan_by_key.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/scatter.h" "$(@D)/cuda/include/thrust/system/detail/adl/scatter.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/sequence.h" "$(@D)/cuda/include/thrust/system/detail/adl/sequence.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/set_operations.h" "$(@D)/cuda/include/thrust/system/detail/adl/set_operations.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/sort.h" "$(@D)/cuda/include/thrust/system/detail/adl/sort.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/swap_ranges.h" "$(@D)/cuda/include/thrust/system/detail/adl/swap_ranges.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/tabulate.h" "$(@D)/cuda/include/thrust/system/detail/adl/tabulate.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/temporary_buffer.h" "$(@D)/cuda/include/thrust/system/detail/adl/temporary_buffer.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/transform.h" "$(@D)/cuda/include/thrust/system/detail/adl/transform.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/transform_reduce.h" "$(@D)/cuda/include/thrust/system/detail/adl/transform_reduce.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/transform_scan.h" "$(@D)/cuda/include/thrust/system/detail/adl/transform_scan.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/uninitialized_copy.h" "$(@D)/cuda/include/thrust/system/detail/adl/uninitialized_copy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/uninitialized_fill.h" "$(@D)/cuda/include/thrust/system/detail/adl/uninitialized_fill.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/unique.h" "$(@D)/cuda/include/thrust/system/detail/adl/unique.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/adl/unique_by_key.h" "$(@D)/cuda/include/thrust/system/detail/adl/unique_by_key.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/bad_alloc.h" "$(@D)/cuda/include/thrust/system/detail/bad_alloc.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/errno.h" "$(@D)/cuda/include/thrust/system/detail/errno.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/error_category.inl" "$(@D)/cuda/include/thrust/system/detail/error_category.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/error_code.inl" "$(@D)/cuda/include/thrust/system/detail/error_code.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/error_condition.inl" "$(@D)/cuda/include/thrust/system/detail/error_condition.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/adjacent_difference.h" "$(@D)/cuda/include/thrust/system/detail/generic/adjacent_difference.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/adjacent_difference.inl" "$(@D)/cuda/include/thrust/system/detail/generic/adjacent_difference.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/advance.h" "$(@D)/cuda/include/thrust/system/detail/generic/advance.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/advance.inl" "$(@D)/cuda/include/thrust/system/detail/generic/advance.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/binary_search.h" "$(@D)/cuda/include/thrust/system/detail/generic/binary_search.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/binary_search.inl" "$(@D)/cuda/include/thrust/system/detail/generic/binary_search.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/copy.h" "$(@D)/cuda/include/thrust/system/detail/generic/copy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/copy.inl" "$(@D)/cuda/include/thrust/system/detail/generic/copy.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/copy_if.h" "$(@D)/cuda/include/thrust/system/detail/generic/copy_if.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/copy_if.inl" "$(@D)/cuda/include/thrust/system/detail/generic/copy_if.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/count.h" "$(@D)/cuda/include/thrust/system/detail/generic/count.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/count.inl" "$(@D)/cuda/include/thrust/system/detail/generic/count.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/distance.h" "$(@D)/cuda/include/thrust/system/detail/generic/distance.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/distance.inl" "$(@D)/cuda/include/thrust/system/detail/generic/distance.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/equal.h" "$(@D)/cuda/include/thrust/system/detail/generic/equal.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/equal.inl" "$(@D)/cuda/include/thrust/system/detail/generic/equal.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/extrema.h" "$(@D)/cuda/include/thrust/system/detail/generic/extrema.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/extrema.inl" "$(@D)/cuda/include/thrust/system/detail/generic/extrema.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/fill.h" "$(@D)/cuda/include/thrust/system/detail/generic/fill.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/find.h" "$(@D)/cuda/include/thrust/system/detail/generic/find.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/find.inl" "$(@D)/cuda/include/thrust/system/detail/generic/find.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/for_each.h" "$(@D)/cuda/include/thrust/system/detail/generic/for_each.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/gather.h" "$(@D)/cuda/include/thrust/system/detail/generic/gather.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/gather.inl" "$(@D)/cuda/include/thrust/system/detail/generic/gather.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/generate.h" "$(@D)/cuda/include/thrust/system/detail/generic/generate.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/generate.inl" "$(@D)/cuda/include/thrust/system/detail/generic/generate.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/inner_product.h" "$(@D)/cuda/include/thrust/system/detail/generic/inner_product.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/inner_product.inl" "$(@D)/cuda/include/thrust/system/detail/generic/inner_product.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/logical.h" "$(@D)/cuda/include/thrust/system/detail/generic/logical.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/memory.h" "$(@D)/cuda/include/thrust/system/detail/generic/memory.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/memory.inl" "$(@D)/cuda/include/thrust/system/detail/generic/memory.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/merge.h" "$(@D)/cuda/include/thrust/system/detail/generic/merge.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/merge.inl" "$(@D)/cuda/include/thrust/system/detail/generic/merge.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/mismatch.h" "$(@D)/cuda/include/thrust/system/detail/generic/mismatch.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/mismatch.inl" "$(@D)/cuda/include/thrust/system/detail/generic/mismatch.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/partition.h" "$(@D)/cuda/include/thrust/system/detail/generic/partition.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/partition.inl" "$(@D)/cuda/include/thrust/system/detail/generic/partition.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/reduce.h" "$(@D)/cuda/include/thrust/system/detail/generic/reduce.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/reduce.inl" "$(@D)/cuda/include/thrust/system/detail/generic/reduce.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/reduce_by_key.h" "$(@D)/cuda/include/thrust/system/detail/generic/reduce_by_key.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/reduce_by_key.inl" "$(@D)/cuda/include/thrust/system/detail/generic/reduce_by_key.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/remove.h" "$(@D)/cuda/include/thrust/system/detail/generic/remove.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/remove.inl" "$(@D)/cuda/include/thrust/system/detail/generic/remove.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/replace.h" "$(@D)/cuda/include/thrust/system/detail/generic/replace.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/replace.inl" "$(@D)/cuda/include/thrust/system/detail/generic/replace.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/reverse.h" "$(@D)/cuda/include/thrust/system/detail/generic/reverse.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/reverse.inl" "$(@D)/cuda/include/thrust/system/detail/generic/reverse.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/scalar/binary_search.h" "$(@D)/cuda/include/thrust/system/detail/generic/scalar/binary_search.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/scalar/binary_search.inl" "$(@D)/cuda/include/thrust/system/detail/generic/scalar/binary_search.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/scan.h" "$(@D)/cuda/include/thrust/system/detail/generic/scan.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/scan.inl" "$(@D)/cuda/include/thrust/system/detail/generic/scan.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/scan_by_key.h" "$(@D)/cuda/include/thrust/system/detail/generic/scan_by_key.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/scan_by_key.inl" "$(@D)/cuda/include/thrust/system/detail/generic/scan_by_key.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/scatter.h" "$(@D)/cuda/include/thrust/system/detail/generic/scatter.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/scatter.inl" "$(@D)/cuda/include/thrust/system/detail/generic/scatter.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/select_system.h" "$(@D)/cuda/include/thrust/system/detail/generic/select_system.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/sequence.h" "$(@D)/cuda/include/thrust/system/detail/generic/sequence.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/sequence.inl" "$(@D)/cuda/include/thrust/system/detail/generic/sequence.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/set_operations.h" "$(@D)/cuda/include/thrust/system/detail/generic/set_operations.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/set_operations.inl" "$(@D)/cuda/include/thrust/system/detail/generic/set_operations.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/sort.h" "$(@D)/cuda/include/thrust/system/detail/generic/sort.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/sort.inl" "$(@D)/cuda/include/thrust/system/detail/generic/sort.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/swap_ranges.h" "$(@D)/cuda/include/thrust/system/detail/generic/swap_ranges.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/swap_ranges.inl" "$(@D)/cuda/include/thrust/system/detail/generic/swap_ranges.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/tabulate.h" "$(@D)/cuda/include/thrust/system/detail/generic/tabulate.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/tabulate.inl" "$(@D)/cuda/include/thrust/system/detail/generic/tabulate.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/tag.h" "$(@D)/cuda/include/thrust/system/detail/generic/tag.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/temporary_buffer.h" "$(@D)/cuda/include/thrust/system/detail/generic/temporary_buffer.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/temporary_buffer.inl" "$(@D)/cuda/include/thrust/system/detail/generic/temporary_buffer.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/transform.h" "$(@D)/cuda/include/thrust/system/detail/generic/transform.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/transform.inl" "$(@D)/cuda/include/thrust/system/detail/generic/transform.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/transform_reduce.h" "$(@D)/cuda/include/thrust/system/detail/generic/transform_reduce.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/transform_reduce.inl" "$(@D)/cuda/include/thrust/system/detail/generic/transform_reduce.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/transform_scan.h" "$(@D)/cuda/include/thrust/system/detail/generic/transform_scan.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/transform_scan.inl" "$(@D)/cuda/include/thrust/system/detail/generic/transform_scan.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/type_traits.h" "$(@D)/cuda/include/thrust/system/detail/generic/type_traits.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/uninitialized_copy.h" "$(@D)/cuda/include/thrust/system/detail/generic/uninitialized_copy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/uninitialized_copy.inl" "$(@D)/cuda/include/thrust/system/detail/generic/uninitialized_copy.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/uninitialized_fill.h" "$(@D)/cuda/include/thrust/system/detail/generic/uninitialized_fill.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/uninitialized_fill.inl" "$(@D)/cuda/include/thrust/system/detail/generic/uninitialized_fill.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/unique.h" "$(@D)/cuda/include/thrust/system/detail/generic/unique.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/unique.inl" "$(@D)/cuda/include/thrust/system/detail/generic/unique.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/unique_by_key.h" "$(@D)/cuda/include/thrust/system/detail/generic/unique_by_key.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/generic/unique_by_key.inl" "$(@D)/cuda/include/thrust/system/detail/generic/unique_by_key.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/internal/decompose.h" "$(@D)/cuda/include/thrust/system/detail/internal/decompose.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/adjacent_difference.h" "$(@D)/cuda/include/thrust/system/detail/sequential/adjacent_difference.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/assign_value.h" "$(@D)/cuda/include/thrust/system/detail/sequential/assign_value.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/binary_search.h" "$(@D)/cuda/include/thrust/system/detail/sequential/binary_search.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/copy.h" "$(@D)/cuda/include/thrust/system/detail/sequential/copy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/copy.inl" "$(@D)/cuda/include/thrust/system/detail/sequential/copy.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/copy_backward.h" "$(@D)/cuda/include/thrust/system/detail/sequential/copy_backward.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/copy_if.h" "$(@D)/cuda/include/thrust/system/detail/sequential/copy_if.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/count.h" "$(@D)/cuda/include/thrust/system/detail/sequential/count.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/equal.h" "$(@D)/cuda/include/thrust/system/detail/sequential/equal.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/execution_policy.h" "$(@D)/cuda/include/thrust/system/detail/sequential/execution_policy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/extrema.h" "$(@D)/cuda/include/thrust/system/detail/sequential/extrema.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/fill.h" "$(@D)/cuda/include/thrust/system/detail/sequential/fill.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/find.h" "$(@D)/cuda/include/thrust/system/detail/sequential/find.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/for_each.h" "$(@D)/cuda/include/thrust/system/detail/sequential/for_each.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/gather.h" "$(@D)/cuda/include/thrust/system/detail/sequential/gather.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/general_copy.h" "$(@D)/cuda/include/thrust/system/detail/sequential/general_copy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/generate.h" "$(@D)/cuda/include/thrust/system/detail/sequential/generate.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/get_value.h" "$(@D)/cuda/include/thrust/system/detail/sequential/get_value.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/inner_product.h" "$(@D)/cuda/include/thrust/system/detail/sequential/inner_product.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/insertion_sort.h" "$(@D)/cuda/include/thrust/system/detail/sequential/insertion_sort.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/iter_swap.h" "$(@D)/cuda/include/thrust/system/detail/sequential/iter_swap.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/logical.h" "$(@D)/cuda/include/thrust/system/detail/sequential/logical.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/malloc_and_free.h" "$(@D)/cuda/include/thrust/system/detail/sequential/malloc_and_free.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/merge.h" "$(@D)/cuda/include/thrust/system/detail/sequential/merge.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/merge.inl" "$(@D)/cuda/include/thrust/system/detail/sequential/merge.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/mismatch.h" "$(@D)/cuda/include/thrust/system/detail/sequential/mismatch.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/partition.h" "$(@D)/cuda/include/thrust/system/detail/sequential/partition.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/reduce.h" "$(@D)/cuda/include/thrust/system/detail/sequential/reduce.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/reduce_by_key.h" "$(@D)/cuda/include/thrust/system/detail/sequential/reduce_by_key.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/remove.h" "$(@D)/cuda/include/thrust/system/detail/sequential/remove.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/replace.h" "$(@D)/cuda/include/thrust/system/detail/sequential/replace.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/reverse.h" "$(@D)/cuda/include/thrust/system/detail/sequential/reverse.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/scan.h" "$(@D)/cuda/include/thrust/system/detail/sequential/scan.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/scan_by_key.h" "$(@D)/cuda/include/thrust/system/detail/sequential/scan_by_key.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/scatter.h" "$(@D)/cuda/include/thrust/system/detail/sequential/scatter.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/sequence.h" "$(@D)/cuda/include/thrust/system/detail/sequential/sequence.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/set_operations.h" "$(@D)/cuda/include/thrust/system/detail/sequential/set_operations.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/sort.h" "$(@D)/cuda/include/thrust/system/detail/sequential/sort.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/sort.inl" "$(@D)/cuda/include/thrust/system/detail/sequential/sort.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/stable_merge_sort.h" "$(@D)/cuda/include/thrust/system/detail/sequential/stable_merge_sort.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/stable_merge_sort.inl" "$(@D)/cuda/include/thrust/system/detail/sequential/stable_merge_sort.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/stable_primitive_sort.h" "$(@D)/cuda/include/thrust/system/detail/sequential/stable_primitive_sort.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/stable_primitive_sort.inl" "$(@D)/cuda/include/thrust/system/detail/sequential/stable_primitive_sort.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/stable_radix_sort.h" "$(@D)/cuda/include/thrust/system/detail/sequential/stable_radix_sort.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/stable_radix_sort.inl" "$(@D)/cuda/include/thrust/system/detail/sequential/stable_radix_sort.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/swap_ranges.h" "$(@D)/cuda/include/thrust/system/detail/sequential/swap_ranges.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/tabulate.h" "$(@D)/cuda/include/thrust/system/detail/sequential/tabulate.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/temporary_buffer.h" "$(@D)/cuda/include/thrust/system/detail/sequential/temporary_buffer.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/transform.h" "$(@D)/cuda/include/thrust/system/detail/sequential/transform.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/transform_reduce.h" "$(@D)/cuda/include/thrust/system/detail/sequential/transform_reduce.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/transform_scan.h" "$(@D)/cuda/include/thrust/system/detail/sequential/transform_scan.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/trivial_copy.h" "$(@D)/cuda/include/thrust/system/detail/sequential/trivial_copy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/uninitialized_copy.h" "$(@D)/cuda/include/thrust/system/detail/sequential/uninitialized_copy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/uninitialized_fill.h" "$(@D)/cuda/include/thrust/system/detail/sequential/uninitialized_fill.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/unique.h" "$(@D)/cuda/include/thrust/system/detail/sequential/unique.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/sequential/unique_by_key.h" "$(@D)/cuda/include/thrust/system/detail/sequential/unique_by_key.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/detail/system_error.inl" "$(@D)/cuda/include/thrust/system/detail/system_error.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/error_code.h" "$(@D)/cuda/include/thrust/system/error_code.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/adjacent_difference.h" "$(@D)/cuda/include/thrust/system/omp/detail/adjacent_difference.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/assign_value.h" "$(@D)/cuda/include/thrust/system/omp/detail/assign_value.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/binary_search.h" "$(@D)/cuda/include/thrust/system/omp/detail/binary_search.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/copy.h" "$(@D)/cuda/include/thrust/system/omp/detail/copy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/copy.inl" "$(@D)/cuda/include/thrust/system/omp/detail/copy.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/copy_if.h" "$(@D)/cuda/include/thrust/system/omp/detail/copy_if.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/copy_if.inl" "$(@D)/cuda/include/thrust/system/omp/detail/copy_if.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/count.h" "$(@D)/cuda/include/thrust/system/omp/detail/count.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/default_decomposition.h" "$(@D)/cuda/include/thrust/system/omp/detail/default_decomposition.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/default_decomposition.inl" "$(@D)/cuda/include/thrust/system/omp/detail/default_decomposition.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/equal.h" "$(@D)/cuda/include/thrust/system/omp/detail/equal.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/execution_policy.h" "$(@D)/cuda/include/thrust/system/omp/detail/execution_policy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/extrema.h" "$(@D)/cuda/include/thrust/system/omp/detail/extrema.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/fill.h" "$(@D)/cuda/include/thrust/system/omp/detail/fill.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/find.h" "$(@D)/cuda/include/thrust/system/omp/detail/find.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/for_each.h" "$(@D)/cuda/include/thrust/system/omp/detail/for_each.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/for_each.inl" "$(@D)/cuda/include/thrust/system/omp/detail/for_each.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/gather.h" "$(@D)/cuda/include/thrust/system/omp/detail/gather.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/generate.h" "$(@D)/cuda/include/thrust/system/omp/detail/generate.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/get_value.h" "$(@D)/cuda/include/thrust/system/omp/detail/get_value.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/inner_product.h" "$(@D)/cuda/include/thrust/system/omp/detail/inner_product.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/iter_swap.h" "$(@D)/cuda/include/thrust/system/omp/detail/iter_swap.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/logical.h" "$(@D)/cuda/include/thrust/system/omp/detail/logical.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/malloc_and_free.h" "$(@D)/cuda/include/thrust/system/omp/detail/malloc_and_free.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/memory.inl" "$(@D)/cuda/include/thrust/system/omp/detail/memory.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/merge.h" "$(@D)/cuda/include/thrust/system/omp/detail/merge.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/mismatch.h" "$(@D)/cuda/include/thrust/system/omp/detail/mismatch.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/par.h" "$(@D)/cuda/include/thrust/system/omp/detail/par.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/partition.h" "$(@D)/cuda/include/thrust/system/omp/detail/partition.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/partition.inl" "$(@D)/cuda/include/thrust/system/omp/detail/partition.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/reduce.h" "$(@D)/cuda/include/thrust/system/omp/detail/reduce.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/reduce.inl" "$(@D)/cuda/include/thrust/system/omp/detail/reduce.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/reduce_by_key.h" "$(@D)/cuda/include/thrust/system/omp/detail/reduce_by_key.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/reduce_by_key.inl" "$(@D)/cuda/include/thrust/system/omp/detail/reduce_by_key.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/reduce_intervals.h" "$(@D)/cuda/include/thrust/system/omp/detail/reduce_intervals.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/reduce_intervals.inl" "$(@D)/cuda/include/thrust/system/omp/detail/reduce_intervals.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/remove.h" "$(@D)/cuda/include/thrust/system/omp/detail/remove.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/remove.inl" "$(@D)/cuda/include/thrust/system/omp/detail/remove.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/replace.h" "$(@D)/cuda/include/thrust/system/omp/detail/replace.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/reverse.h" "$(@D)/cuda/include/thrust/system/omp/detail/reverse.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/scan.h" "$(@D)/cuda/include/thrust/system/omp/detail/scan.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/scan_by_key.h" "$(@D)/cuda/include/thrust/system/omp/detail/scan_by_key.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/scatter.h" "$(@D)/cuda/include/thrust/system/omp/detail/scatter.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/sequence.h" "$(@D)/cuda/include/thrust/system/omp/detail/sequence.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/set_operations.h" "$(@D)/cuda/include/thrust/system/omp/detail/set_operations.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/sort.h" "$(@D)/cuda/include/thrust/system/omp/detail/sort.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/sort.inl" "$(@D)/cuda/include/thrust/system/omp/detail/sort.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/swap_ranges.h" "$(@D)/cuda/include/thrust/system/omp/detail/swap_ranges.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/tabulate.h" "$(@D)/cuda/include/thrust/system/omp/detail/tabulate.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/temporary_buffer.h" "$(@D)/cuda/include/thrust/system/omp/detail/temporary_buffer.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/transform.h" "$(@D)/cuda/include/thrust/system/omp/detail/transform.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/transform_reduce.h" "$(@D)/cuda/include/thrust/system/omp/detail/transform_reduce.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/transform_scan.h" "$(@D)/cuda/include/thrust/system/omp/detail/transform_scan.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/uninitialized_copy.h" "$(@D)/cuda/include/thrust/system/omp/detail/uninitialized_copy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/uninitialized_fill.h" "$(@D)/cuda/include/thrust/system/omp/detail/uninitialized_fill.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/unique.h" "$(@D)/cuda/include/thrust/system/omp/detail/unique.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/unique.inl" "$(@D)/cuda/include/thrust/system/omp/detail/unique.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/unique_by_key.h" "$(@D)/cuda/include/thrust/system/omp/detail/unique_by_key.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/unique_by_key.inl" "$(@D)/cuda/include/thrust/system/omp/detail/unique_by_key.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/detail/vector.inl" "$(@D)/cuda/include/thrust/system/omp/detail/vector.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/execution_policy.h" "$(@D)/cuda/include/thrust/system/omp/execution_policy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/memory.h" "$(@D)/cuda/include/thrust/system/omp/memory.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/omp/vector.h" "$(@D)/cuda/include/thrust/system/omp/vector.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/system_error.h" "$(@D)/cuda/include/thrust/system/system_error.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/adjacent_difference.h" "$(@D)/cuda/include/thrust/system/tbb/detail/adjacent_difference.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/assign_value.h" "$(@D)/cuda/include/thrust/system/tbb/detail/assign_value.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/binary_search.h" "$(@D)/cuda/include/thrust/system/tbb/detail/binary_search.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/copy.h" "$(@D)/cuda/include/thrust/system/tbb/detail/copy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/copy.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/copy.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/copy_if.h" "$(@D)/cuda/include/thrust/system/tbb/detail/copy_if.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/copy_if.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/copy_if.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/count.h" "$(@D)/cuda/include/thrust/system/tbb/detail/count.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/equal.h" "$(@D)/cuda/include/thrust/system/tbb/detail/equal.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/execution_policy.h" "$(@D)/cuda/include/thrust/system/tbb/detail/execution_policy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/extrema.h" "$(@D)/cuda/include/thrust/system/tbb/detail/extrema.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/fill.h" "$(@D)/cuda/include/thrust/system/tbb/detail/fill.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/find.h" "$(@D)/cuda/include/thrust/system/tbb/detail/find.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/for_each.h" "$(@D)/cuda/include/thrust/system/tbb/detail/for_each.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/for_each.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/for_each.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/gather.h" "$(@D)/cuda/include/thrust/system/tbb/detail/gather.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/generate.h" "$(@D)/cuda/include/thrust/system/tbb/detail/generate.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/get_value.h" "$(@D)/cuda/include/thrust/system/tbb/detail/get_value.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/inner_product.h" "$(@D)/cuda/include/thrust/system/tbb/detail/inner_product.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/iter_swap.h" "$(@D)/cuda/include/thrust/system/tbb/detail/iter_swap.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/logical.h" "$(@D)/cuda/include/thrust/system/tbb/detail/logical.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/malloc_and_free.h" "$(@D)/cuda/include/thrust/system/tbb/detail/malloc_and_free.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/memory.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/memory.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/merge.h" "$(@D)/cuda/include/thrust/system/tbb/detail/merge.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/merge.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/merge.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/mismatch.h" "$(@D)/cuda/include/thrust/system/tbb/detail/mismatch.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/par.h" "$(@D)/cuda/include/thrust/system/tbb/detail/par.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/partition.h" "$(@D)/cuda/include/thrust/system/tbb/detail/partition.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/partition.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/partition.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/reduce.h" "$(@D)/cuda/include/thrust/system/tbb/detail/reduce.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/reduce.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/reduce.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/reduce_by_key.h" "$(@D)/cuda/include/thrust/system/tbb/detail/reduce_by_key.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/reduce_by_key.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/reduce_by_key.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/reduce_intervals.h" "$(@D)/cuda/include/thrust/system/tbb/detail/reduce_intervals.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/remove.h" "$(@D)/cuda/include/thrust/system/tbb/detail/remove.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/remove.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/remove.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/replace.h" "$(@D)/cuda/include/thrust/system/tbb/detail/replace.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/reverse.h" "$(@D)/cuda/include/thrust/system/tbb/detail/reverse.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/scan.h" "$(@D)/cuda/include/thrust/system/tbb/detail/scan.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/scan.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/scan.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/scan_by_key.h" "$(@D)/cuda/include/thrust/system/tbb/detail/scan_by_key.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/scatter.h" "$(@D)/cuda/include/thrust/system/tbb/detail/scatter.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/sequence.h" "$(@D)/cuda/include/thrust/system/tbb/detail/sequence.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/set_operations.h" "$(@D)/cuda/include/thrust/system/tbb/detail/set_operations.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/sort.h" "$(@D)/cuda/include/thrust/system/tbb/detail/sort.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/sort.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/sort.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/swap_ranges.h" "$(@D)/cuda/include/thrust/system/tbb/detail/swap_ranges.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/tabulate.h" "$(@D)/cuda/include/thrust/system/tbb/detail/tabulate.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/temporary_buffer.h" "$(@D)/cuda/include/thrust/system/tbb/detail/temporary_buffer.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/transform.h" "$(@D)/cuda/include/thrust/system/tbb/detail/transform.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/transform_reduce.h" "$(@D)/cuda/include/thrust/system/tbb/detail/transform_reduce.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/transform_scan.h" "$(@D)/cuda/include/thrust/system/tbb/detail/transform_scan.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/uninitialized_copy.h" "$(@D)/cuda/include/thrust/system/tbb/detail/uninitialized_copy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/uninitialized_fill.h" "$(@D)/cuda/include/thrust/system/tbb/detail/uninitialized_fill.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/unique.h" "$(@D)/cuda/include/thrust/system/tbb/detail/unique.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/unique.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/unique.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/unique_by_key.h" "$(@D)/cuda/include/thrust/system/tbb/detail/unique_by_key.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/unique_by_key.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/unique_by_key.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/detail/vector.inl" "$(@D)/cuda/include/thrust/system/tbb/detail/vector.inl" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/execution_policy.h" "$(@D)/cuda/include/thrust/system/tbb/execution_policy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/memory.h" "$(@D)/cuda/include/thrust/system/tbb/memory.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system/tbb/vector.h" "$(@D)/cuda/include/thrust/system/tbb/vector.h" && cp -f "/usr/local/cuda-9.0/include/thrust/system_error.h" "$(@D)/cuda/include/thrust/system_error.h" && cp -f "/usr/local/cuda-9.0/include/thrust/tabulate.h" "$(@D)/cuda/include/thrust/tabulate.h" && cp -f "/usr/local/cuda-9.0/include/thrust/transform.h" "$(@D)/cuda/include/thrust/transform.h" && cp -f "/usr/local/cuda-9.0/include/thrust/transform_reduce.h" "$(@D)/cuda/include/thrust/transform_reduce.h" && cp -f "/usr/local/cuda-9.0/include/thrust/transform_scan.h" "$(@D)/cuda/include/thrust/transform_scan.h" && cp -f "/usr/local/cuda-9.0/include/thrust/tuple.h" "$(@D)/cuda/include/thrust/tuple.h" && cp -f "/usr/local/cuda-9.0/include/thrust/uninitialized_copy.h" "$(@D)/cuda/include/thrust/uninitialized_copy.h" && cp -f "/usr/local/cuda-9.0/include/thrust/uninitialized_fill.h" "$(@D)/cuda/include/thrust/uninitialized_fill.h" && cp -f "/usr/local/cuda-9.0/include/thrust/unique.h" "$(@D)/cuda/include/thrust/unique.h" && cp -f "/usr/local/cuda-9.0/include/thrust/version.h" "$(@D)/cuda/include/thrust/version.h" && cp -f "/usr/local/cuda-9.0/include/vector_functions.h" "$(@D)/cuda/include/vector_functions.h" && cp -f "/usr/local/cuda-9.0/include/vector_functions.hpp" "$(@D)/cuda/include/vector_functions.hpp" && cp -f "/usr/local/cuda-9.0/include/vector_types.h" "$(@D)/cuda/include/vector_types.h" """, ) @@ -1198,7 +1198,7 @@ genrule( "cuda/nvvm/libdevice/libdevice.10.bc", ], cmd = """ -if [ -d "$(@D)/extras" ]; then rm $(@D)/extras -drf; fi && if [ -d "$(@D)/include" ]; then rm $(@D)/include -drf; fi && if [ -d "$(@D)/lib" ]; then rm $(@D)/lib -drf; fi && if [ -d "$(@D)/nvvm" ]; then rm $(@D)/nvvm -drf; fi && cp "/usr/local/cuda-9.0/nvvm/libdevice/libdevice.10.bc" "$(@D)//libdevice.10.bc" +if [ -d "$(@D)/extras" ]; then rm $(@D)/extras -drf; fi && if [ -d "$(@D)/include" ]; then rm $(@D)/include -drf; fi && if [ -d "$(@D)/lib" ]; then rm $(@D)/lib -drf; fi && if [ -d "$(@D)/nvvm" ]; then rm $(@D)/nvvm -drf; fi && cp -f "/usr/local/cuda-9.0/nvvm/libdevice/libdevice.10.bc" "$(@D)//libdevice.10.bc" """, ) @@ -1235,7 +1235,7 @@ genrule( "cuda/extras/CUPTI/include/openacc/cupti_openacc.h", ], cmd = """ -if [ -d "$(@D)/extras" ]; then rm $(@D)/extras -drf; fi && if [ -d "$(@D)/include" ]; then rm $(@D)/include -drf; fi && if [ -d "$(@D)/lib" ]; then rm $(@D)/lib -drf; fi && if [ -d "$(@D)/nvvm" ]; then rm $(@D)/nvvm -drf; fi && cp "/usr/local/cuda-9.0/extras/CUPTI/include/GL/gl.h" "$(@D)/cuda/extras/CUPTI/include/GL/gl.h" && cp "/usr/local/cuda-9.0/extras/CUPTI/include/GL/glew.h" "$(@D)/cuda/extras/CUPTI/include/GL/glew.h" && cp "/usr/local/cuda-9.0/extras/CUPTI/include/GL/glext.h" "$(@D)/cuda/extras/CUPTI/include/GL/glext.h" && cp "/usr/local/cuda-9.0/extras/CUPTI/include/GL/glu.h" "$(@D)/cuda/extras/CUPTI/include/GL/glu.h" && cp "/usr/local/cuda-9.0/extras/CUPTI/include/GL/glut.h" "$(@D)/cuda/extras/CUPTI/include/GL/glut.h" && cp "/usr/local/cuda-9.0/extras/CUPTI/include/GL/glx.h" "$(@D)/cuda/extras/CUPTI/include/GL/glx.h" && cp "/usr/local/cuda-9.0/extras/CUPTI/include/GL/glxext.h" "$(@D)/cuda/extras/CUPTI/include/GL/glxext.h" && cp "/usr/local/cuda-9.0/extras/CUPTI/include/GL/wglew.h" "$(@D)/cuda/extras/CUPTI/include/GL/wglew.h" && cp "/usr/local/cuda-9.0/extras/CUPTI/include/GL/wglext.h" "$(@D)/cuda/extras/CUPTI/include/GL/wglext.h" && cp "/usr/local/cuda-9.0/extras/CUPTI/include/cuda_stdint.h" "$(@D)/cuda/extras/CUPTI/include/cuda_stdint.h" && cp "/usr/local/cuda-9.0/extras/CUPTI/include/cupti.h" "$(@D)/cuda/extras/CUPTI/include/cupti.h" && cp "/usr/local/cuda-9.0/extras/CUPTI/include/cupti_activity.h" "$(@D)/cuda/extras/CUPTI/include/cupti_activity.h" && cp "/usr/local/cuda-9.0/extras/CUPTI/include/cupti_callbacks.h" "$(@D)/cuda/extras/CUPTI/include/cupti_callbacks.h" && cp "/usr/local/cuda-9.0/extras/CUPTI/include/cupti_driver_cbid.h" "$(@D)/cuda/extras/CUPTI/include/cupti_driver_cbid.h" && cp "/usr/local/cuda-9.0/extras/CUPTI/include/cupti_events.h" "$(@D)/cuda/extras/CUPTI/include/cupti_events.h" && cp "/usr/local/cuda-9.0/extras/CUPTI/include/cupti_metrics.h" "$(@D)/cuda/extras/CUPTI/include/cupti_metrics.h" && cp "/usr/local/cuda-9.0/extras/CUPTI/include/cupti_nvtx_cbid.h" "$(@D)/cuda/extras/CUPTI/include/cupti_nvtx_cbid.h" && cp "/usr/local/cuda-9.0/extras/CUPTI/include/cupti_result.h" "$(@D)/cuda/extras/CUPTI/include/cupti_result.h" && cp "/usr/local/cuda-9.0/extras/CUPTI/include/cupti_runtime_cbid.h" "$(@D)/cuda/extras/CUPTI/include/cupti_runtime_cbid.h" && cp "/usr/local/cuda-9.0/extras/CUPTI/include/cupti_version.h" "$(@D)/cuda/extras/CUPTI/include/cupti_version.h" && cp "/usr/local/cuda-9.0/extras/CUPTI/include/generated_cudaGL_meta.h" "$(@D)/cuda/extras/CUPTI/include/generated_cudaGL_meta.h" && cp "/usr/local/cuda-9.0/extras/CUPTI/include/generated_cudaVDPAU_meta.h" "$(@D)/cuda/extras/CUPTI/include/generated_cudaVDPAU_meta.h" && cp "/usr/local/cuda-9.0/extras/CUPTI/include/generated_cuda_gl_interop_meta.h" "$(@D)/cuda/extras/CUPTI/include/generated_cuda_gl_interop_meta.h" && cp "/usr/local/cuda-9.0/extras/CUPTI/include/generated_cuda_meta.h" "$(@D)/cuda/extras/CUPTI/include/generated_cuda_meta.h" && cp "/usr/local/cuda-9.0/extras/CUPTI/include/generated_cuda_runtime_api_meta.h" "$(@D)/cuda/extras/CUPTI/include/generated_cuda_runtime_api_meta.h" && cp "/usr/local/cuda-9.0/extras/CUPTI/include/generated_cuda_vdpau_interop_meta.h" "$(@D)/cuda/extras/CUPTI/include/generated_cuda_vdpau_interop_meta.h" && cp "/usr/local/cuda-9.0/extras/CUPTI/include/generated_nvtx_meta.h" "$(@D)/cuda/extras/CUPTI/include/generated_nvtx_meta.h" && cp "/usr/local/cuda-9.0/extras/CUPTI/include/openacc/cupti_openacc.h" "$(@D)/cuda/extras/CUPTI/include/openacc/cupti_openacc.h" +if [ -d "$(@D)/extras" ]; then rm $(@D)/extras -drf; fi && if [ -d "$(@D)/include" ]; then rm $(@D)/include -drf; fi && if [ -d "$(@D)/lib" ]; then rm $(@D)/lib -drf; fi && if [ -d "$(@D)/nvvm" ]; then rm $(@D)/nvvm -drf; fi && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/GL/gl.h" "$(@D)/cuda/extras/CUPTI/include/GL/gl.h" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/GL/glew.h" "$(@D)/cuda/extras/CUPTI/include/GL/glew.h" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/GL/glext.h" "$(@D)/cuda/extras/CUPTI/include/GL/glext.h" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/GL/glu.h" "$(@D)/cuda/extras/CUPTI/include/GL/glu.h" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/GL/glut.h" "$(@D)/cuda/extras/CUPTI/include/GL/glut.h" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/GL/glx.h" "$(@D)/cuda/extras/CUPTI/include/GL/glx.h" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/GL/glxext.h" "$(@D)/cuda/extras/CUPTI/include/GL/glxext.h" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/GL/wglew.h" "$(@D)/cuda/extras/CUPTI/include/GL/wglew.h" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/GL/wglext.h" "$(@D)/cuda/extras/CUPTI/include/GL/wglext.h" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/cuda_stdint.h" "$(@D)/cuda/extras/CUPTI/include/cuda_stdint.h" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/cupti.h" "$(@D)/cuda/extras/CUPTI/include/cupti.h" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/cupti_activity.h" "$(@D)/cuda/extras/CUPTI/include/cupti_activity.h" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/cupti_callbacks.h" "$(@D)/cuda/extras/CUPTI/include/cupti_callbacks.h" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/cupti_driver_cbid.h" "$(@D)/cuda/extras/CUPTI/include/cupti_driver_cbid.h" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/cupti_events.h" "$(@D)/cuda/extras/CUPTI/include/cupti_events.h" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/cupti_metrics.h" "$(@D)/cuda/extras/CUPTI/include/cupti_metrics.h" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/cupti_nvtx_cbid.h" "$(@D)/cuda/extras/CUPTI/include/cupti_nvtx_cbid.h" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/cupti_result.h" "$(@D)/cuda/extras/CUPTI/include/cupti_result.h" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/cupti_runtime_cbid.h" "$(@D)/cuda/extras/CUPTI/include/cupti_runtime_cbid.h" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/cupti_version.h" "$(@D)/cuda/extras/CUPTI/include/cupti_version.h" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/generated_cudaGL_meta.h" "$(@D)/cuda/extras/CUPTI/include/generated_cudaGL_meta.h" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/generated_cudaVDPAU_meta.h" "$(@D)/cuda/extras/CUPTI/include/generated_cudaVDPAU_meta.h" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/generated_cuda_gl_interop_meta.h" "$(@D)/cuda/extras/CUPTI/include/generated_cuda_gl_interop_meta.h" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/generated_cuda_meta.h" "$(@D)/cuda/extras/CUPTI/include/generated_cuda_meta.h" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/generated_cuda_runtime_api_meta.h" "$(@D)/cuda/extras/CUPTI/include/generated_cuda_runtime_api_meta.h" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/generated_cuda_vdpau_interop_meta.h" "$(@D)/cuda/extras/CUPTI/include/generated_cuda_vdpau_interop_meta.h" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/generated_nvtx_meta.h" "$(@D)/cuda/extras/CUPTI/include/generated_nvtx_meta.h" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/include/openacc/cupti_openacc.h" "$(@D)/cuda/extras/CUPTI/include/openacc/cupti_openacc.h" """, ) @@ -1253,7 +1253,7 @@ genrule( "cuda/lib/libcupti.so.9.0", ], cmd = """ -if [ -d "$(@D)/extras" ]; then rm $(@D)/extras -drf; fi && if [ -d "$(@D)/include" ]; then rm $(@D)/include -drf; fi && if [ -d "$(@D)/lib" ]; then rm $(@D)/lib -drf; fi && if [ -d "$(@D)/nvvm" ]; then rm $(@D)/nvvm -drf; fi && cp "/usr/local/cuda-9.0/targets/x86_64-linux/lib/stubs/libcuda.so" "$(@D)/cuda/lib/libcuda.so" && cp "/usr/local/cuda-9.0/targets/x86_64-linux/lib/libcudart.so.9.0.176" "$(@D)/cuda/lib/libcudart.so.9.0" && cp "/usr/local/cuda-9.0/targets/x86_64-linux/lib/libcudart_static.a" "$(@D)/cuda/lib/libcudart_static.a" && cp "/usr/local/cuda-9.0/targets/x86_64-linux/lib/libcublas.so.9.0.480" "$(@D)/cuda/lib/libcublas.so.9.0" && cp "/usr/local/cuda-9.0/targets/x86_64-linux/lib/libcusolver.so.9.0.176" "$(@D)/cuda/lib/libcusolver.so.9.0" && cp "/usr/local/cuda-9.0/targets/x86_64-linux/lib/libcurand.so.9.0.176" "$(@D)/cuda/lib/libcurand.so.9.0" && cp "/usr/local/cuda-9.0/targets/x86_64-linux/lib/libcufft.so.9.0.176" "$(@D)/cuda/lib/libcufft.so.9.0" && cp "/usr/lib/x86_64-linux-gnu/libcudnn.so.7.1.4" "$(@D)/cuda/lib/libcudnn.so.7" && cp "/usr/local/cuda/extras/CUPTI/lib64/libcupti.so.9.0.176" "$(@D)/cuda/lib/libcupti.so.9.0" +if [ -d "$(@D)/extras" ]; then rm $(@D)/extras -drf; fi && if [ -d "$(@D)/include" ]; then rm $(@D)/include -drf; fi && if [ -d "$(@D)/lib" ]; then rm $(@D)/lib -drf; fi && if [ -d "$(@D)/nvvm" ]; then rm $(@D)/nvvm -drf; fi && cp -f "/usr/local/cuda-9.0/targets/x86_64-linux/lib/stubs/libcuda.so" "$(@D)/cuda/lib/libcuda.so" && cp -f "/usr/local/cuda-9.0/targets/x86_64-linux/lib/libcudart.so.9.0.176" "$(@D)/cuda/lib/libcudart.so.9.0" && cp -f "/usr/local/cuda-9.0/targets/x86_64-linux/lib/libcudart_static.a" "$(@D)/cuda/lib/libcudart_static.a" && cp -f "/usr/local/cuda-9.0/targets/x86_64-linux/lib/libcublas.so.9.0.480" "$(@D)/cuda/lib/libcublas.so.9.0" && cp -f "/usr/local/cuda-9.0/targets/x86_64-linux/lib/libcusolver.so.9.0.176" "$(@D)/cuda/lib/libcusolver.so.9.0" && cp -f "/usr/local/cuda-9.0/targets/x86_64-linux/lib/libcurand.so.9.0.176" "$(@D)/cuda/lib/libcurand.so.9.0" && cp -f "/usr/local/cuda-9.0/targets/x86_64-linux/lib/libcufft.so.9.0.176" "$(@D)/cuda/lib/libcufft.so.9.0" && cp -f "/usr/lib/x86_64-linux-gnu/libcudnn.so.7.1.4" "$(@D)/cuda/lib/libcudnn.so.7" && cp -f "/usr/local/cuda-9.0/extras/CUPTI/lib64/libcupti.so.9.0.176" "$(@D)/cuda/lib/libcupti.so.9.0" """, ) @@ -1263,6 +1263,6 @@ genrule( "cuda/include/cudnn.h", ], cmd = """ -if [ -d "$(@D)/extras" ]; then rm $(@D)/extras -drf; fi && if [ -d "$(@D)/include" ]; then rm $(@D)/include -drf; fi && if [ -d "$(@D)/lib" ]; then rm $(@D)/lib -drf; fi && if [ -d "$(@D)/nvvm" ]; then rm $(@D)/nvvm -drf; fi && cp "/usr/include/cudnn.h" "$(@D)/cudnn.h" +if [ -d "$(@D)/extras" ]; then rm $(@D)/extras -drf; fi && if [ -d "$(@D)/include" ]; then rm $(@D)/include -drf; fi && if [ -d "$(@D)/lib" ]; then rm $(@D)/lib -drf; fi && if [ -d "$(@D)/nvvm" ]; then rm $(@D)/nvvm -drf; fi && cp -f "/usr/include/cudnn.h" "$(@D)/cudnn.h" """, ) diff --git a/third_party/toolchains/preconfig/ubuntu14.04/cuda9.0-cudnn7/cuda/build_defs.bzl b/third_party/toolchains/preconfig/ubuntu14.04/cuda9.0-cudnn7/cuda/build_defs.bzl index 5c6703aab4..a53c891d8b 100755 --- a/third_party/toolchains/preconfig/ubuntu14.04/cuda9.0-cudnn7/cuda/build_defs.bzl +++ b/third_party/toolchains/preconfig/ubuntu14.04/cuda9.0-cudnn7/cuda/build_defs.bzl @@ -9,15 +9,13 @@ def if_cuda(if_true, if_false = []): return select({ "@local_config_cuda//cuda:using_nvcc": if_true, "@local_config_cuda//cuda:using_clang": if_true, - "//conditions:default": if_false + "//conditions:default": if_false, }) - def cuda_default_copts(): """Default options for all CUDA compilations.""" return if_cuda(["-x", "cuda", "-DGOOGLE_CUDA=1"] + []) - def cuda_is_configured(): """Returns true if CUDA was enabled during the configure process.""" return True @@ -29,5 +27,5 @@ def if_cuda_is_configured(x): --config=cuda. Used to allow non-CUDA code to depend on CUDA libraries. """ if cuda_is_configured(): - return x + return x return [] diff --git a/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda10.0/BUILD b/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda10.0/BUILD new file mode 100755 index 0000000000..6442e7628a --- /dev/null +++ b/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda10.0/BUILD @@ -0,0 +1,87 @@ +licenses(["restricted"]) + +package(default_visibility = ["//visibility:public"]) + +toolchain( + name = "toolchain-linux-x86_64", + exec_compatible_with = [ + "@bazel_tools//platforms:linux", + "@bazel_tools//platforms:x86_64", + ], + target_compatible_with = [ + "@bazel_tools//platforms:linux", + "@bazel_tools//platforms:x86_64", + ], + toolchain = ":cc-compiler-local", + toolchain_type = "@bazel_tools//tools/cpp:toolchain_type", +) + +cc_toolchain_suite( + name = "toolchain", + toolchains = { + "local|compiler": ":cc-compiler-local", + "darwin|compiler": ":cc-compiler-darwin", + "x64_windows|msvc-cl": ":cc-compiler-windows", + }, +) + +cc_toolchain( + name = "cc-compiler-local", + all_files = ":crosstool_wrapper_driver_is_not_gcc", + compiler_files = ":empty", + cpu = "local", + dwp_files = ":empty", + dynamic_runtime_libs = [":empty"], + linker_files = ":crosstool_wrapper_driver_is_not_gcc", + objcopy_files = ":empty", + static_runtime_libs = [":empty"], + strip_files = ":empty", + # To support linker flags that need to go to the start of command line + # we need the toolchain to support parameter files. Parameter files are + # last on the command line and contain all shared libraries to link, so all + # regular options will be left of them. + supports_param_files = 1, +) + +cc_toolchain( + name = "cc-compiler-darwin", + all_files = ":crosstool_wrapper_driver_is_not_gcc", + compiler_files = ":empty", + cpu = "darwin", + dwp_files = ":empty", + dynamic_runtime_libs = [":empty"], + linker_files = ":crosstool_wrapper_driver_is_not_gcc", + objcopy_files = ":empty", + static_runtime_libs = [":empty"], + strip_files = ":empty", + supports_param_files = 0, +) + +cc_toolchain( + name = "cc-compiler-windows", + all_files = ":windows_msvc_wrapper_files", + compiler_files = ":empty", + cpu = "x64_windows", + dwp_files = ":empty", + dynamic_runtime_libs = [":empty"], + linker_files = ":windows_msvc_wrapper_files", + objcopy_files = ":empty", + static_runtime_libs = [":empty"], + strip_files = ":empty", + supports_param_files = 1, +) + +filegroup( + name = "empty", + srcs = [], +) + +filegroup( + name = "crosstool_wrapper_driver_is_not_gcc", + srcs = ["clang/bin/crosstool_wrapper_driver_is_not_gcc"], +) + +filegroup( + name = "windows_msvc_wrapper_files", + srcs = glob(["windows/msvc_*"]), +) diff --git a/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda10.0/CROSSTOOL b/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda10.0/CROSSTOOL new file mode 100755 index 0000000000..1c2e8bcae6 --- /dev/null +++ b/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda10.0/CROSSTOOL @@ -0,0 +1,1431 @@ +major_version: "local" +minor_version: "" +default_target_cpu: "same_as_host" + +default_toolchain { + cpu: "k8" + toolchain_identifier: "local_linux" +} +default_toolchain { + cpu: "piii" + toolchain_identifier: "local_linux" +} +default_toolchain { + cpu: "arm" + toolchain_identifier: "local_linux" +} +default_toolchain { + cpu: "darwin" + toolchain_identifier: "local_darwin" +} +default_toolchain { + cpu: "ppc" + toolchain_identifier: "local_linux" +} +default_toolchain { + cpu: "x64_windows" + toolchain_identifier: "local_windows" +} + +toolchain { + abi_version: "local" + abi_libc_version: "local" + compiler: "compiler" + host_system_name: "local" + needsPic: true + target_libc: "local" + target_cpu: "local" + target_system_name: "local" + toolchain_identifier: "local_linux" + + feature { + name: "c++11" + flag_set { + action: "c++-compile" + flag_group { + flag: "-std=c++11" + } + } + } + + feature { + name: "stdlib" + flag_set { + action: "c++-link-executable" + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "-lstdc++" + } + } + } + + feature { + name: "determinism" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + # Make C++ compilation deterministic. Use linkstamping instead of these + # compiler symbols. + flag: "-Wno-builtin-macro-redefined" + flag: "-D__DATE__=\"redacted\"" + flag: "-D__TIMESTAMP__=\"redacted\"" + flag: "-D__TIME__=\"redacted\"" + } + } + } + + feature { + name: "alwayslink" + flag_set { + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + action: "c++-link-executable" + flag_group { + flag: "-Wl,-no-as-needed" + } + } + } + + # This feature will be enabled for builds that support pic by bazel. + feature { + name: "pic" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + expand_if_all_available: "pic" + flag: "-fPIC" + } + flag_group { + expand_if_none_available: "pic" + flag: "-fPIE" + } + } + } + + # Security hardening on by default. + feature { + name: "hardening" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + # Conservative choice; -D_FORTIFY_SOURCE=2 may be unsafe in some cases. + # We need to undef it before redefining it as some distributions now + # have it enabled by default. + flag: "-U_FORTIFY_SOURCE" + flag: "-D_FORTIFY_SOURCE=1" + flag: "-fstack-protector" + } + } + flag_set { + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "-Wl,-z,relro,-z,now" + } + } + flag_set { + action: "c++-link-executable" + flag_group { + flag: "-pie" + flag: "-Wl,-z,relro,-z,now" + } + } + } + + feature { + name: "warnings" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + # All warnings are enabled. Maybe enable -Werror as well? + flag: "-Wall" + + } + } + } + + # Keep stack frames for debugging, even in opt mode. + feature { + name: "frame-pointer" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + flag: "-fno-omit-frame-pointer" + } + } + } + + feature { + name: "build-id" + flag_set { + action: "c++-link-executable" + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + flag_group { + # Stamp the binary with a unique identifier. + flag: "-Wl,--build-id=md5" + flag: "-Wl,--hash-style=gnu" + } + } + } + + feature { + name: "no-canonical-prefixes" + flag_set { + action: "c-compile" + action: "c++-compile" + action: "c++-link-executable" + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "-no-canonical-prefixes" + flag: "-fno-canonical-system-headers" + } + } + } + + feature { + name: "disable-assertions" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + flag: "-DNDEBUG" + } + } + } + + feature { + name: "linker-bin-path" + + flag_set { + action: "c++-link-executable" + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "-B/usr/bin" + } + } + } + + feature { + name: "common" + implies: "stdlib" + implies: "c++11" + implies: "determinism" + implies: "alwayslink" + implies: "hardening" + implies: "warnings" + implies: "frame-pointer" + implies: "build-id" + implies: "no-canonical-prefixes" + implies: "linker-bin-path" + } + + feature { + name: "opt" + implies: "common" + implies: "disable-assertions" + + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + # No debug symbols. + # Maybe we should enable https://gcc.gnu.org/wiki/DebugFission for opt + # or even generally? However, that can't happen here, as it requires + # special handling in Bazel. + flag: "-g0" + + # Conservative choice for -O + # -O3 can increase binary size and even slow down the resulting binaries. + # Profile first and / or use FDO if you need better performance than this. + flag: "-O2" + + # Removal of unused code and data at link time (can this increase binary size in some cases?). + flag: "-ffunction-sections" + flag: "-fdata-sections" + } + } + flag_set { + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + action: "c++-link-executable" + flag_group { + flag: "-Wl,--gc-sections" + } + } + } + + feature { + name: "fastbuild" + implies: "common" + } + + feature { + name: "dbg" + implies: "common" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + flag: "-g" + } + } + } + + # Set clang as a C/C++ compiler. + tool_path { name: "gcc" path: "clang/bin/crosstool_wrapper_driver_is_not_gcc" } + + # Use the default system toolchain for everything else. + tool_path { name: "ar" path: "/usr/bin/ar" } + tool_path { name: "compat-ld" path: "/usr/bin/ld" } + tool_path { name: "cpp" path: "/usr/bin/cpp" } + tool_path { name: "dwp" path: "/usr/bin/dwp" } + tool_path { name: "gcov" path: "/usr/bin/gcov" } + tool_path { name: "ld" path: "/usr/bin/ld" } + tool_path { name: "nm" path: "/usr/bin/nm" } + tool_path { name: "objcopy" path: "/usr/bin/objcopy" } + tool_path { name: "objdump" path: "/usr/bin/objdump" } + tool_path { name: "strip" path: "/usr/bin/strip" } + + # Enabled dynamic linking. + linking_mode_flags { mode: DYNAMIC } + + cxx_builtin_include_directory: "/usr/include/c++/4.8" + cxx_builtin_include_directory: "/usr/include/x86_64-linux-gnu/c++/4.8" + cxx_builtin_include_directory: "/usr/include/c++/4.8/backward" + cxx_builtin_include_directory: "/usr/lib/gcc/x86_64-linux-gnu/4.8/include" + cxx_builtin_include_directory: "/usr/local/include" + cxx_builtin_include_directory: "/usr/lib/gcc/x86_64-linux-gnu/4.8/include-fixed" + cxx_builtin_include_directory: "/usr/include/x86_64-linux-gnu" + cxx_builtin_include_directory: "/usr/include" + cxx_builtin_include_directory: "/usr/local/cuda-10.0/targets/x86_64-linux/include" + cxx_builtin_include_directory: "/usr/local/cuda-10.0/include" + cxx_builtin_include_directory: "/usr/local/cuda-10.0/extras/CUPTI/include" + cxx_builtin_include_directory: "/usr/include" +} + +toolchain { + abi_version: "local" + abi_libc_version: "local" + compiler: "compiler" + host_system_name: "local" + needsPic: true + target_libc: "macosx" + target_cpu: "darwin" + target_system_name: "local" + toolchain_identifier: "local_darwin" + feature { + name: "c++11" + flag_set { + action: "c++-compile" + flag_group { + flag: "-std=c++11" + } + } + } + + feature { + name: "stdlib" + flag_set { + action: "c++-link-executable" + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "-lc++" + } + } + } + + feature { + name: "determinism" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + # Make C++ compilation deterministic. Use linkstamping instead of these + # compiler symbols. + flag: "-Wno-builtin-macro-redefined" + flag: "-D__DATE__=\"redacted\"" + flag: "-D__TIMESTAMP__=\"redacted\"" + flag: "-D__TIME__=\"redacted\"" + } + } + } + + # This feature will be enabled for builds that support pic by bazel. + feature { + name: "pic" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + expand_if_all_available: "pic" + flag: "-fPIC" + } + flag_group { + expand_if_none_available: "pic" + flag: "-fPIE" + } + } + } + + # Security hardening on by default. + feature { + name: "hardening" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + # Conservative choice; -D_FORTIFY_SOURCE=2 may be unsafe in some cases. + # We need to undef it before redefining it as some distributions now + # have it enabled by default. + flag: "-U_FORTIFY_SOURCE" + flag: "-D_FORTIFY_SOURCE=1" + flag: "-fstack-protector" + } + } + flag_set { + action: "c++-link-executable" + flag_group { + flag: "-pie" + } + } + } + + feature { + name: "warnings" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + # All warnings are enabled. Maybe enable -Werror as well? + flag: "-Wall" + + } + } + } + + # Keep stack frames for debugging, even in opt mode. + feature { + name: "frame-pointer" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + flag: "-fno-omit-frame-pointer" + } + } + } + + feature { + name: "no-canonical-prefixes" + flag_set { + action: "c-compile" + action: "c++-compile" + action: "c++-link-executable" + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag:"-no-canonical-prefixes" + } + } + } + + feature { + name: "disable-assertions" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + flag: "-DNDEBUG" + } + } + } + + feature { + name: "linker-bin-path" + + flag_set { + action: "c++-link-executable" + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "-B/usr/bin" + } + } + } + + feature { + name: "undefined-dynamic" + flag_set { + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + action: "c++-link-executable" + flag_group { + flag: "-undefined" + flag: "dynamic_lookup" + } + } + } + + feature { + name: "common" + implies: "stdlib" + implies: "c++11" + implies: "determinism" + implies: "hardening" + implies: "warnings" + implies: "frame-pointer" + implies: "no-canonical-prefixes" + implies: "linker-bin-path" + implies: "undefined-dynamic" + } + + feature { + name: "opt" + implies: "common" + implies: "disable-assertions" + + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + # No debug symbols. + # Maybe we should enable https://gcc.gnu.org/wiki/DebugFission for opt + # or even generally? However, that can't happen here, as it requires + # special handling in Bazel. + flag: "-g0" + + # Conservative choice for -O + # -O3 can increase binary size and even slow down the resulting binaries. + # Profile first and / or use FDO if you need better performance than this. + flag: "-O2" + + # Removal of unused code and data at link time (can this increase binary size in some cases?). + flag: "-ffunction-sections" + flag: "-fdata-sections" + } + } + } + + feature { + name: "fastbuild" + implies: "common" + } + + feature { + name: "dbg" + implies: "common" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + flag: "-g" + } + } + } + + # Set clang as a C/C++ compiler. + tool_path { name: "gcc" path: "clang/bin/crosstool_wrapper_driver_is_not_gcc" } + + # Use the default system toolchain for everything else. + tool_path { name: "ar" path: "/usr/bin/libtool" } + tool_path { name: "compat-ld" path: "/usr/bin/ld" } + tool_path { name: "cpp" path: "/usr/bin/cpp" } + tool_path { name: "dwp" path: "/usr/bin/dwp" } + tool_path { name: "gcov" path: "/usr/bin/gcov" } + tool_path { name: "ld" path: "/usr/bin/ld" } + tool_path { name: "nm" path: "/usr/bin/nm" } + tool_path { name: "objcopy" path: "/usr/bin/objcopy" } + tool_path { name: "objdump" path: "/usr/bin/objdump" } + tool_path { name: "strip" path: "/usr/bin/strip" } + + # Enabled dynamic linking. + linking_mode_flags { mode: DYNAMIC } + + cxx_builtin_include_directory: "/usr/include/c++/4.8" + cxx_builtin_include_directory: "/usr/include/x86_64-linux-gnu/c++/4.8" + cxx_builtin_include_directory: "/usr/include/c++/4.8/backward" + cxx_builtin_include_directory: "/usr/lib/gcc/x86_64-linux-gnu/4.8/include" + cxx_builtin_include_directory: "/usr/local/include" + cxx_builtin_include_directory: "/usr/lib/gcc/x86_64-linux-gnu/4.8/include-fixed" + cxx_builtin_include_directory: "/usr/include/x86_64-linux-gnu" + cxx_builtin_include_directory: "/usr/include" + cxx_builtin_include_directory: "/usr/local/cuda-10.0/targets/x86_64-linux/include" + cxx_builtin_include_directory: "/usr/local/cuda-10.0/include" + cxx_builtin_include_directory: "/usr/local/cuda-10.0/extras/CUPTI/include" + cxx_builtin_include_directory: "/usr/include" +} + +toolchain { + toolchain_identifier: "local_windows" + host_system_name: "local" + target_system_name: "local" + + abi_version: "local" + abi_libc_version: "local" + target_cpu: "x64_windows" + compiler: "msvc-cl" + target_libc: "msvcrt" + + + + tool_path { + name: "ar" + path: "" + } + tool_path { + name: "ml" + path: "" + } + tool_path { + name: "cpp" + path: "" + } + tool_path { + name: "gcc" + path: "" + } + tool_path { + name: "gcov" + path: "wrapper/bin/msvc_nop.bat" + } + tool_path { + name: "ld" + path: "" + } + tool_path { + name: "nm" + path: "wrapper/bin/msvc_nop.bat" + } + tool_path { + name: "objcopy" + path: "wrapper/bin/msvc_nop.bat" + } + tool_path { + name: "objdump" + path: "wrapper/bin/msvc_nop.bat" + } + tool_path { + name: "strip" + path: "wrapper/bin/msvc_nop.bat" + } + supports_interface_shared_objects: true + + # TODO(pcloudy): Review those flags below, they should be defined by cl.exe + compiler_flag: "/DCOMPILER_MSVC" + + # Don't define min/max macros in windows.h. + compiler_flag: "/DNOMINMAX" + + # Platform defines. + compiler_flag: "/D_WIN32_WINNT=0x0600" + # Turn off warning messages. + compiler_flag: "/D_CRT_SECURE_NO_DEPRECATE" + compiler_flag: "/D_CRT_SECURE_NO_WARNINGS" + compiler_flag: "/D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS" + + # Useful options to have on for compilation. + # Increase the capacity of object files to 2^32 sections. + compiler_flag: "/bigobj" + # Allocate 500MB for precomputed headers. + compiler_flag: "/Zm500" + # Use unsigned char by default. + compiler_flag: "/J" + # Use function level linking. + compiler_flag: "/Gy" + # Use string pooling. + compiler_flag: "/GF" + # Catch C++ exceptions only and tell the compiler to assume that functions declared + # as extern "C" never throw a C++ exception. + compiler_flag: "/EHsc" + + # Globally disabled warnings. + # Don't warn about elements of array being be default initialized. + compiler_flag: "/wd4351" + # Don't warn about no matching delete found. + compiler_flag: "/wd4291" + # Don't warn about diamond inheritance patterns. + compiler_flag: "/wd4250" + # Don't warn about insecure functions (e.g. non _s functions). + compiler_flag: "/wd4996" + + linker_flag: "/MACHINE:X64" + + feature { + name: "no_legacy_features" + } + + # Suppress startup banner. + feature { + name: "nologo" + flag_set { + action: "c-compile" + action: "c++-compile" + action: "c++-module-compile" + action: "c++-module-codegen" + action: "c++-header-parsing" + action: "assemble" + action: "preprocess-assemble" + action: "c++-link-executable" + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + action: "c++-link-static-library" + flag_group { + flag: "/nologo" + } + } + } + + feature { + name: 'has_configured_linker_path' + } + + # This feature indicates strip is not supported, building stripped binary will just result a copy of orignial binary + feature { + name: 'no_stripping' + } + + # This feature indicates this is a toolchain targeting Windows. + feature { + name: 'targets_windows' + implies: 'copy_dynamic_libraries_to_binary' + enabled: true + } + + feature { + name: 'copy_dynamic_libraries_to_binary' + } + + action_config { + config_name: 'assemble' + action_name: 'assemble' + tool { + tool_path: '' + } + implies: 'compiler_input_flags' + implies: 'compiler_output_flags' + implies: 'nologo' + implies: 'msvc_env' + implies: 'sysroot' + } + + action_config { + config_name: 'preprocess-assemble' + action_name: 'preprocess-assemble' + tool { + tool_path: '' + } + implies: 'compiler_input_flags' + implies: 'compiler_output_flags' + implies: 'nologo' + implies: 'msvc_env' + implies: 'sysroot' + } + + action_config { + config_name: 'c-compile' + action_name: 'c-compile' + tool { + tool_path: '' + } + implies: 'compiler_input_flags' + implies: 'compiler_output_flags' + implies: 'legacy_compile_flags' + implies: 'nologo' + implies: 'msvc_env' + implies: 'parse_showincludes' + implies: 'user_compile_flags' + implies: 'sysroot' + implies: 'unfiltered_compile_flags' + } + + action_config { + config_name: 'c++-compile' + action_name: 'c++-compile' + tool { + tool_path: '' + } + implies: 'compiler_input_flags' + implies: 'compiler_output_flags' + implies: 'legacy_compile_flags' + implies: 'nologo' + implies: 'msvc_env' + implies: 'parse_showincludes' + implies: 'user_compile_flags' + implies: 'sysroot' + implies: 'unfiltered_compile_flags' + } + + action_config { + config_name: 'c++-link-executable' + action_name: 'c++-link-executable' + tool { + tool_path: '' + } + implies: 'nologo' + implies: 'linkstamps' + implies: 'output_execpath_flags' + implies: 'input_param_flags' + implies: 'user_link_flags' + implies: 'legacy_link_flags' + implies: 'linker_subsystem_flag' + implies: 'linker_param_file' + implies: 'msvc_env' + implies: 'no_stripping' + } + + action_config { + config_name: 'c++-link-dynamic-library' + action_name: 'c++-link-dynamic-library' + tool { + tool_path: '' + } + implies: 'nologo' + implies: 'shared_flag' + implies: 'linkstamps' + implies: 'output_execpath_flags' + implies: 'input_param_flags' + implies: 'user_link_flags' + implies: 'legacy_link_flags' + implies: 'linker_subsystem_flag' + implies: 'linker_param_file' + implies: 'msvc_env' + implies: 'no_stripping' + implies: 'has_configured_linker_path' + implies: 'def_file' + } + + action_config { + config_name: 'c++-link-nodeps-dynamic-library' + action_name: 'c++-link-nodeps-dynamic-library' + tool { + tool_path: '' + } + implies: 'nologo' + implies: 'shared_flag' + implies: 'linkstamps' + implies: 'output_execpath_flags' + implies: 'input_param_flags' + implies: 'user_link_flags' + implies: 'legacy_link_flags' + implies: 'linker_subsystem_flag' + implies: 'linker_param_file' + implies: 'msvc_env' + implies: 'no_stripping' + implies: 'has_configured_linker_path' + implies: 'def_file' + } + + action_config { + config_name: 'c++-link-static-library' + action_name: 'c++-link-static-library' + tool { + tool_path: '' + } + implies: 'nologo' + implies: 'archiver_flags' + implies: 'input_param_flags' + implies: 'linker_param_file' + implies: 'msvc_env' + } + + # TODO(b/65151735): Remove legacy_compile_flags feature when legacy fields are + # not used in this crosstool + feature { + name: 'legacy_compile_flags' + flag_set { + expand_if_all_available: 'legacy_compile_flags' + action: 'preprocess-assemble' + action: 'c-compile' + action: 'c++-compile' + action: 'c++-header-parsing' + action: 'c++-module-compile' + action: 'c++-module-codegen' + flag_group { + iterate_over: 'legacy_compile_flags' + flag: '%{legacy_compile_flags}' + } + } + } + + feature { + name: "msvc_env" + env_set { + action: "c-compile" + action: "c++-compile" + action: "c++-module-compile" + action: "c++-module-codegen" + action: "c++-header-parsing" + action: "assemble" + action: "preprocess-assemble" + action: "c++-link-executable" + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + action: "c++-link-static-library" + env_entry { + key: "PATH" + value: "" + } + env_entry { + key: "INCLUDE" + value: "" + } + env_entry { + key: "LIB" + value: "" + } + env_entry { + key: "TMP" + value: "" + } + env_entry { + key: "TEMP" + value: "" + } + } + } + + feature { + name: 'include_paths' + flag_set { + action: "assemble" + action: 'preprocess-assemble' + action: 'c-compile' + action: 'c++-compile' + action: 'c++-header-parsing' + action: 'c++-module-compile' + flag_group { + iterate_over: 'quote_include_paths' + flag: '/I%{quote_include_paths}' + } + flag_group { + iterate_over: 'include_paths' + flag: '/I%{include_paths}' + } + flag_group { + iterate_over: 'system_include_paths' + flag: '/I%{system_include_paths}' + } + } + } + + feature { + name: "preprocessor_defines" + flag_set { + action: "assemble" + action: "preprocess-assemble" + action: "c-compile" + action: "c++-compile" + action: "c++-header-parsing" + action: "c++-module-compile" + flag_group { + flag: "/D%{preprocessor_defines}" + iterate_over: "preprocessor_defines" + } + } + } + + # Tell Bazel to parse the output of /showIncludes + feature { + name: 'parse_showincludes' + flag_set { + action: 'preprocess-assemble' + action: 'c-compile' + action: 'c++-compile' + action: 'c++-module-compile' + action: 'c++-header-parsing' + flag_group { + flag: "/showIncludes" + } + } + } + + + feature { + name: 'generate_pdb_file' + requires: { + feature: 'dbg' + } + requires: { + feature: 'fastbuild' + } + } + + feature { + name: 'shared_flag' + flag_set { + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: '/DLL' + } + } + } + + feature { + name: 'linkstamps' + flag_set { + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + expand_if_all_available: 'linkstamp_paths' + flag_group { + iterate_over: 'linkstamp_paths' + flag: '%{linkstamp_paths}' + } + } + } + + feature { + name: 'output_execpath_flags' + flag_set { + expand_if_all_available: 'output_execpath' + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: '/OUT:%{output_execpath}' + } + } + } + + feature { + name: 'archiver_flags' + flag_set { + expand_if_all_available: 'output_execpath' + action: 'c++-link-static-library' + flag_group { + flag: '/OUT:%{output_execpath}' + } + } + } + + feature { + name: 'input_param_flags' + flag_set { + expand_if_all_available: 'interface_library_output_path' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "/IMPLIB:%{interface_library_output_path}" + } + } + flag_set { + expand_if_all_available: 'libopts' + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + iterate_over: 'libopts' + flag: '%{libopts}' + } + } + flag_set { + expand_if_all_available: 'libraries_to_link' + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + action: 'c++-link-static-library' + flag_group { + iterate_over: 'libraries_to_link' + flag_group { + expand_if_equal: { + variable: 'libraries_to_link.type' + value: 'object_file_group' + } + iterate_over: 'libraries_to_link.object_files' + flag_group { + flag: '%{libraries_to_link.object_files}' + } + } + flag_group { + expand_if_equal: { + variable: 'libraries_to_link.type' + value: 'object_file' + } + flag_group { + flag: '%{libraries_to_link.name}' + } + } + flag_group { + expand_if_equal: { + variable: 'libraries_to_link.type' + value: 'interface_library' + } + flag_group { + flag: '%{libraries_to_link.name}' + } + } + flag_group { + expand_if_equal: { + variable: 'libraries_to_link.type' + value: 'static_library' + } + flag_group { + expand_if_false: 'libraries_to_link.is_whole_archive' + flag: '%{libraries_to_link.name}' + } + flag_group { + expand_if_true: 'libraries_to_link.is_whole_archive' + flag: '/WHOLEARCHIVE:%{libraries_to_link.name}' + } + } + } + } + } + + # Since this feature is declared earlier in the CROSSTOOL than + # "user_link_flags", this feature will be applied prior to it anwyhere they + # are both implied. And since "user_link_flags" contains the linkopts from + # the build rule, this allows the user to override the /SUBSYSTEM in the BUILD + # file. + feature { + name: 'linker_subsystem_flag' + flag_set { + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: '/SUBSYSTEM:CONSOLE' + } + } + } + + # The "user_link_flags" contains user-defined linkopts (from build rules) + # so it should be defined after features that declare user-overridable flags. + # For example the "linker_subsystem_flag" defines a default "/SUBSYSTEM" flag + # but we want to let the user override it, therefore "link_flag_subsystem" is + # defined earlier in the CROSSTOOL file than "user_link_flags". + feature { + name: 'user_link_flags' + flag_set { + expand_if_all_available: 'user_link_flags' + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + iterate_over: 'user_link_flags' + flag: '%{user_link_flags}' + } + } + } + feature { + name: 'legacy_link_flags' + flag_set { + expand_if_all_available: 'legacy_link_flags' + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + iterate_over: 'legacy_link_flags' + flag: '%{legacy_link_flags}' + } + } + } + + feature { + name: 'linker_param_file' + flag_set { + expand_if_all_available: 'linker_param_file' + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + action: 'c++-link-static-library' + flag_group { + flag: '@%{linker_param_file}' + } + } + } + + feature { + name: 'static_link_msvcrt' + } + + feature { + name: 'static_link_msvcrt_no_debug' + flag_set { + action: 'c-compile' + action: 'c++-compile' + flag_group { + flag: "/MT" + } + } + flag_set { + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "/DEFAULTLIB:libcmt.lib" + } + } + requires: { feature: 'fastbuild'} + requires: { feature: 'opt'} + } + + feature { + name: 'dynamic_link_msvcrt_no_debug' + flag_set { + action: 'c-compile' + action: 'c++-compile' + flag_group { + flag: "/MD" + } + } + flag_set { + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "/DEFAULTLIB:msvcrt.lib" + } + } + requires: { feature: 'fastbuild'} + requires: { feature: 'opt'} + } + + feature { + name: 'static_link_msvcrt_debug' + flag_set { + action: 'c-compile' + action: 'c++-compile' + flag_group { + flag: "/MTd" + } + } + flag_set { + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "/DEFAULTLIB:libcmtd.lib" + } + } + requires: { feature: 'dbg'} + } + + feature { + name: 'dynamic_link_msvcrt_debug' + flag_set { + action: 'c-compile' + action: 'c++-compile' + flag_group { + flag: "/MDd" + } + } + flag_set { + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "/DEFAULTLIB:msvcrtd.lib" + } + } + requires: { feature: 'dbg'} + } + + feature { + name: 'dbg' + flag_set { + action: 'c-compile' + action: 'c++-compile' + flag_group { + flag: "/Od" + flag: "/Z7" + flag: "/DDEBUG" + } + } + flag_set { + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "/DEBUG:FULL" + flag: "/INCREMENTAL:NO" + } + } + implies: 'generate_pdb_file' + } + + feature { + name: 'fastbuild' + flag_set { + action: 'c-compile' + action: 'c++-compile' + flag_group { + flag: "/Od" + flag: "/Z7" + flag: "/DDEBUG" + } + } + flag_set { + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "/DEBUG:FASTLINK" + flag: "/INCREMENTAL:NO" + } + } + implies: 'generate_pdb_file' + } + + feature { + name: 'opt' + flag_set { + action: 'c-compile' + action: 'c++-compile' + flag_group { + flag: "/O2" + flag: "/DNDEBUG" + } + } + } + + feature { + name: 'user_compile_flags' + flag_set { + expand_if_all_available: 'user_compile_flags' + action: 'preprocess-assemble' + action: 'c-compile' + action: 'c++-compile' + action: 'c++-header-parsing' + action: 'c++-module-compile' + action: 'c++-module-codegen' + flag_group { + iterate_over: 'user_compile_flags' + flag: '%{user_compile_flags}' + } + } + } + + feature { + name: 'sysroot' + flag_set { + expand_if_all_available: 'sysroot' + action: 'assemble' + action: 'preprocess-assemble' + action: 'c-compile' + action: 'c++-compile' + action: 'c++-header-parsing' + action: 'c++-module-compile' + action: 'c++-module-codegen' + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + iterate_over: 'sysroot' + flag: '--sysroot=%{sysroot}' + } + } + } + + feature { + name: 'unfiltered_compile_flags' + flag_set { + expand_if_all_available: 'unfiltered_compile_flags' + action: 'preprocess-assemble' + action: 'c-compile' + action: 'c++-compile' + action: 'c++-header-parsing' + action: 'c++-module-compile' + action: 'c++-module-codegen' + flag_group { + iterate_over: 'unfiltered_compile_flags' + flag: '%{unfiltered_compile_flags}' + } + } + } + + feature { + name: 'compiler_output_flags' + flag_set { + action: 'assemble' + flag_group { + expand_if_all_available: 'output_file' + expand_if_none_available: 'output_assembly_file' + expand_if_none_available: 'output_preprocess_file' + flag: '/Fo%{output_file}' + flag: '/Zi' + } + } + flag_set { + action: 'preprocess-assemble' + action: 'c-compile' + action: 'c++-compile' + action: 'c++-header-parsing' + action: 'c++-module-compile' + action: 'c++-module-codegen' + flag_group { + expand_if_all_available: 'output_file' + expand_if_none_available: 'output_assembly_file' + expand_if_none_available: 'output_preprocess_file' + flag: '/Fo%{output_file}' + } + flag_group { + expand_if_all_available: 'output_file' + expand_if_all_available: 'output_assembly_file' + flag: '/Fa%{output_file}' + } + flag_group { + expand_if_all_available: 'output_file' + expand_if_all_available: 'output_preprocess_file' + flag: '/P' + flag: '/Fi%{output_file}' + } + } + } + + feature { + name: 'compiler_input_flags' + flag_set { + action: 'assemble' + action: 'preprocess-assemble' + action: 'c-compile' + action: 'c++-compile' + action: 'c++-header-parsing' + action: 'c++-module-compile' + action: 'c++-module-codegen' + flag_group { + expand_if_all_available: 'source_file' + flag: '/c' + flag: '%{source_file}' + } + } + } + + feature { + name : 'def_file', + flag_set { + expand_if_all_available: 'def_file_path' + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "/DEF:%{def_file_path}" + # We can specify a different DLL name in DEF file, /ignore:4070 suppresses + # the warning message about DLL name doesn't match the default one. + # See https://msdn.microsoft.com/en-us/library/sfkk2fz7.aspx + flag: "/ignore:4070" + } + } + } + + feature { + name: 'windows_export_all_symbols' + } + + feature { + name: 'no_windows_export_all_symbols' + } + + linking_mode_flags { mode: DYNAMIC } +} diff --git a/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda10.0/clang/bin/crosstool_wrapper_driver_is_not_gcc b/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda10.0/clang/bin/crosstool_wrapper_driver_is_not_gcc new file mode 100755 index 0000000000..7ae59e9967 --- /dev/null +++ b/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda10.0/clang/bin/crosstool_wrapper_driver_is_not_gcc @@ -0,0 +1,264 @@ +#!/usr/bin/env python +# Copyright 2015 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. +# ============================================================================== + +"""Crosstool wrapper for compiling CUDA programs. + +SYNOPSIS: + crosstool_wrapper_is_not_gcc [options passed in by cc_library() + or cc_binary() rule] + +DESCRIPTION: + This script is expected to be called by the cc_library() or cc_binary() bazel + rules. When the option "-x cuda" is present in the list of arguments passed + to this script, it invokes the nvcc CUDA compiler. Most arguments are passed + as is as a string to --compiler-options of nvcc. When "-x cuda" is not + present, this wrapper invokes hybrid_driver_is_not_gcc with the input + arguments as is. + +NOTES: + Changes to the contents of this file must be propagated from + //third_party/gpus/crosstool/crosstool_wrapper_is_not_gcc to + //third_party/gpus/crosstool/v*/*/clang/bin/crosstool_wrapper_is_not_gcc +""" + +from __future__ import print_function + +__author__ = 'keveman@google.com (Manjunath Kudlur)' + +from argparse import ArgumentParser +import os +import subprocess +import re +import sys +import pipes + +# Template values set by cuda_autoconf. +CPU_COMPILER = ('/usr/bin/gcc') +GCC_HOST_COMPILER_PATH = ('/usr/bin/gcc') + +NVCC_PATH = '/usr/local/cuda-10.0/bin/nvcc' +PREFIX_DIR = os.path.dirname(GCC_HOST_COMPILER_PATH) +NVCC_VERSION = '10.0' + +def Log(s): + print('gpus/crosstool: {0}'.format(s)) + + +def GetOptionValue(argv, option): + """Extract the list of values for option from the argv list. + + Args: + argv: A list of strings, possibly the argv passed to main(). + option: The option whose value to extract, without the leading '-'. + + Returns: + A list of values, either directly following the option, + (eg., -opt val1 val2) or values collected from multiple occurrences of + the option (eg., -opt val1 -opt val2). + """ + + parser = ArgumentParser() + parser.add_argument('-' + option, nargs='*', action='append') + args, _ = parser.parse_known_args(argv) + if not args or not vars(args)[option]: + return [] + else: + return sum(vars(args)[option], []) + + +def GetHostCompilerOptions(argv): + """Collect the -isystem, -iquote, and --sysroot option values from argv. + + Args: + argv: A list of strings, possibly the argv passed to main(). + + Returns: + The string that can be used as the --compiler-options to nvcc. + """ + + parser = ArgumentParser() + parser.add_argument('-isystem', nargs='*', action='append') + parser.add_argument('-iquote', nargs='*', action='append') + parser.add_argument('--sysroot', nargs=1) + parser.add_argument('-g', nargs='*', action='append') + parser.add_argument('-fno-canonical-system-headers', action='store_true') + + args, _ = parser.parse_known_args(argv) + + opts = '' + + if args.isystem: + opts += ' -isystem ' + ' -isystem '.join(sum(args.isystem, [])) + if args.iquote: + opts += ' -iquote ' + ' -iquote '.join(sum(args.iquote, [])) + if args.g: + opts += ' -g' + ' -g'.join(sum(args.g, [])) + if args.fno_canonical_system_headers: + opts += ' -fno-canonical-system-headers' + if args.sysroot: + opts += ' --sysroot ' + args.sysroot[0] + + return opts + +def _update_options(nvcc_options): + if NVCC_VERSION in ("7.0",): + return nvcc_options + + update_options = { "relaxed-constexpr" : "expt-relaxed-constexpr" } + return [ update_options[opt] if opt in update_options else opt + for opt in nvcc_options ] + +def GetNvccOptions(argv): + """Collect the -nvcc_options values from argv. + + Args: + argv: A list of strings, possibly the argv passed to main(). + + Returns: + The string that can be passed directly to nvcc. + """ + + parser = ArgumentParser() + parser.add_argument('-nvcc_options', nargs='*', action='append') + + args, _ = parser.parse_known_args(argv) + + if args.nvcc_options: + options = _update_options(sum(args.nvcc_options, [])) + return ' '.join(['--'+a for a in options]) + return '' + + +def InvokeNvcc(argv, log=False): + """Call nvcc with arguments assembled from argv. + + Args: + argv: A list of strings, possibly the argv passed to main(). + log: True if logging is requested. + + Returns: + The return value of calling os.system('nvcc ' + args) + """ + + host_compiler_options = GetHostCompilerOptions(argv) + nvcc_compiler_options = GetNvccOptions(argv) + opt_option = GetOptionValue(argv, 'O') + m_options = GetOptionValue(argv, 'm') + m_options = ''.join([' -m' + m for m in m_options if m in ['32', '64']]) + include_options = GetOptionValue(argv, 'I') + out_file = GetOptionValue(argv, 'o') + depfiles = GetOptionValue(argv, 'MF') + defines = GetOptionValue(argv, 'D') + defines = ''.join([' -D' + define for define in defines]) + undefines = GetOptionValue(argv, 'U') + undefines = ''.join([' -U' + define for define in undefines]) + std_options = GetOptionValue(argv, 'std') + # currently only c++11 is supported by Cuda 7.0 std argument + nvcc_allowed_std_options = ["c++11"] + std_options = ''.join([' -std=' + define + for define in std_options if define in nvcc_allowed_std_options]) + + # The list of source files get passed after the -c option. I don't know of + # any other reliable way to just get the list of source files to be compiled. + src_files = GetOptionValue(argv, 'c') + + # Pass -w through from host to nvcc, but don't do anything fancier with + # warnings-related flags, since they're not necessarily the same across + # compilers. + warning_options = ' -w' if '-w' in argv else '' + + if len(src_files) == 0: + return 1 + if len(out_file) != 1: + return 1 + + opt = (' -O2' if (len(opt_option) > 0 and int(opt_option[0]) > 0) + else ' -g -G') + + includes = (' -I ' + ' -I '.join(include_options) + if len(include_options) > 0 + else '') + + # Unfortunately, there are other options that have -c prefix too. + # So allowing only those look like C/C++ files. + src_files = [f for f in src_files if + re.search('\.cpp$|\.cc$|\.c$|\.cxx$|\.C$', f)] + srcs = ' '.join(src_files) + out = ' -o ' + out_file[0] + + supported_cuda_compute_capabilities = [ "3.0" ] + nvccopts = '-D_FORCE_INLINES ' + for capability in supported_cuda_compute_capabilities: + capability = capability.replace('.', '') + nvccopts += r'-gencode=arch=compute_%s,\"code=sm_%s,compute_%s\" ' % ( + capability, capability, capability) + nvccopts += ' ' + nvcc_compiler_options + nvccopts += undefines + nvccopts += defines + nvccopts += std_options + nvccopts += m_options + nvccopts += warning_options + + if depfiles: + # Generate the dependency file + depfile = depfiles[0] + cmd = (NVCC_PATH + ' ' + nvccopts + + ' --compiler-options "' + host_compiler_options + '"' + + ' --compiler-bindir=' + GCC_HOST_COMPILER_PATH + + ' -I .' + + ' -x cu ' + opt + includes + ' ' + srcs + ' -M -o ' + depfile) + if log: Log(cmd) + exit_status = os.system(cmd) + if exit_status != 0: + return exit_status + + cmd = (NVCC_PATH + ' ' + nvccopts + + ' --compiler-options "' + host_compiler_options + ' -fPIC"' + + ' --compiler-bindir=' + GCC_HOST_COMPILER_PATH + + ' -I .' + + ' -x cu ' + opt + includes + ' -c ' + srcs + out) + + # TODO(zhengxq): for some reason, 'gcc' needs this help to find 'as'. + # Need to investigate and fix. + cmd = 'PATH=' + PREFIX_DIR + ':$PATH ' + cmd + if log: Log(cmd) + return os.system(cmd) + + +def main(): + parser = ArgumentParser() + parser.add_argument('-x', nargs=1) + parser.add_argument('--cuda_log', action='store_true') + args, leftover = parser.parse_known_args(sys.argv[1:]) + + if args.x and args.x[0] == 'cuda': + if args.cuda_log: Log('-x cuda') + leftover = [pipes.quote(s) for s in leftover] + if args.cuda_log: Log('using nvcc') + return InvokeNvcc(leftover, log=args.cuda_log) + + # Strip our flags before passing through to the CPU compiler for files which + # are not -x cuda. We can't just pass 'leftover' because it also strips -x. + # We not only want to pass -x to the CPU compiler, but also keep it in its + # relative location in the argv list (the compiler is actually sensitive to + # this). + cpu_compiler_flags = [flag for flag in sys.argv[1:] + if not flag.startswith(('--cuda_log'))] + + return subprocess.call([CPU_COMPILER] + cpu_compiler_flags) + +if __name__ == '__main__': + sys.exit(main()) diff --git a/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda10.0/windows/msvc_wrapper_for_nvcc.bat b/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda10.0/windows/msvc_wrapper_for_nvcc.bat new file mode 100755 index 0000000000..e896e654fd --- /dev/null +++ b/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda10.0/windows/msvc_wrapper_for_nvcc.bat @@ -0,0 +1,20 @@ +:: Copyright 2015 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. +:: ============================================================================= + +:: Invoke msvc_wrapper_for_nvcc.py, which is located in the same directory. +@echo OFF +set arg0=%~0 +for %%F in ("%arg0%") do set DRIVER_BIN=%%~dpF +"/usr/bin/python3" -B "%DRIVER_BIN%\msvc_wrapper_for_nvcc.py" %* diff --git a/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda10.0/windows/msvc_wrapper_for_nvcc.py b/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda10.0/windows/msvc_wrapper_for_nvcc.py new file mode 100755 index 0000000000..00483951af --- /dev/null +++ b/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda10.0/windows/msvc_wrapper_for_nvcc.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python +# Copyright 2015 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. +# ============================================================================== + +"""Crosstool wrapper for compiling CUDA programs with nvcc on Windows. + +DESCRIPTION: + This script is the Windows version of //third_party/gpus/crosstool/crosstool_wrapper_is_not_gcc +""" + +from __future__ import print_function + +from argparse import ArgumentParser +import os +import subprocess +import re +import sys +import pipes + +# Template values set by cuda_autoconf. +CPU_COMPILER = ('/usr/bin/gcc') +GCC_HOST_COMPILER_PATH = ('/usr/bin/gcc') + +NVCC_PATH = '/usr/local/cuda-10.0/bin/nvcc' +NVCC_VERSION = '10.0' +NVCC_TEMP_DIR = "C:\\Windows\\Temp\\nvcc_inter_files_tmp_dir" +supported_cuda_compute_capabilities = [ "3.0" ] + +def Log(s): + print('gpus/crosstool: {0}'.format(s)) + + +def GetOptionValue(argv, option): + """Extract the list of values for option from options. + + Args: + option: The option whose value to extract, without the leading '/'. + + Returns: + 1. A list of values, either directly following the option, + (eg., /opt val1 val2) or values collected from multiple occurrences of + the option (eg., /opt val1 /opt val2). + 2. The leftover options. + """ + + parser = ArgumentParser(prefix_chars='/') + parser.add_argument('/' + option, nargs='*', action='append') + args, leftover = parser.parse_known_args(argv) + if args and vars(args)[option]: + return (sum(vars(args)[option], []), leftover) + return ([], leftover) + +def _update_options(nvcc_options): + if NVCC_VERSION in ("7.0",): + return nvcc_options + + update_options = { "relaxed-constexpr" : "expt-relaxed-constexpr" } + return [ update_options[opt] if opt in update_options else opt + for opt in nvcc_options ] + +def GetNvccOptions(argv): + """Collect the -nvcc_options values from argv. + + Args: + argv: A list of strings, possibly the argv passed to main(). + + Returns: + 1. The string that can be passed directly to nvcc. + 2. The leftover options. + """ + + parser = ArgumentParser() + parser.add_argument('-nvcc_options', nargs='*', action='append') + + args, leftover = parser.parse_known_args(argv) + + if args.nvcc_options: + options = _update_options(sum(args.nvcc_options, [])) + return (['--' + a for a in options], leftover) + return ([], leftover) + + +def InvokeNvcc(argv, log=False): + """Call nvcc with arguments assembled from argv. + + Args: + argv: A list of strings, possibly the argv passed to main(). + log: True if logging is requested. + + Returns: + The return value of calling os.system('nvcc ' + args) + """ + + src_files = [f for f in argv if + re.search('\.cpp$|\.cc$|\.c$|\.cxx$|\.C$', f)] + if len(src_files) == 0: + raise Error('No source files found for cuda compilation.') + + out_file = [ f for f in argv if f.startswith('/Fo') ] + if len(out_file) != 1: + raise Error('Please sepecify exactly one output file for cuda compilation.') + out = ['-o', out_file[0][len('/Fo'):]] + + nvcc_compiler_options, argv = GetNvccOptions(argv) + + opt_option, argv = GetOptionValue(argv, 'O') + opt = ['-g', '-G'] + if (len(opt_option) > 0 and opt_option[0] != 'd'): + opt = ['-O2'] + + include_options, argv = GetOptionValue(argv, 'I') + includes = ["-I " + include for include in include_options] + + defines, argv = GetOptionValue(argv, 'D') + defines = ['-D' + define for define in defines] + + undefines, argv = GetOptionValue(argv, 'U') + undefines = ['-U' + define for define in undefines] + + # The rest of the unrecongized options should be passed to host compiler + host_compiler_options = [option for option in argv if option not in (src_files + out_file)] + + m_options = ["-m64"] + + nvccopts = ['-D_FORCE_INLINES'] + for capability in supported_cuda_compute_capabilities: + capability = capability.replace('.', '') + nvccopts += [r'-gencode=arch=compute_%s,"code=sm_%s,compute_%s"' % ( + capability, capability, capability)] + nvccopts += nvcc_compiler_options + nvccopts += undefines + nvccopts += defines + nvccopts += m_options + nvccopts += ['--compiler-options="' + " ".join(host_compiler_options) + '"'] + nvccopts += ['-x', 'cu'] + opt + includes + out + ['-c'] + src_files + # If we don't specify --keep-dir, nvcc will generate intermediate files under TEMP + # Put them under NVCC_TEMP_DIR instead, then Bazel can ignore files under NVCC_TEMP_DIR during dependency check + # http://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html#options-for-guiding-compiler-driver + # Different actions are sharing NVCC_TEMP_DIR, so we cannot remove it if the directory already exists. + if os.path.isfile(NVCC_TEMP_DIR): + os.remove(NVCC_TEMP_DIR) + if not os.path.exists(NVCC_TEMP_DIR): + os.makedirs(NVCC_TEMP_DIR) + nvccopts += ['--keep', '--keep-dir', NVCC_TEMP_DIR] + cmd = [NVCC_PATH] + nvccopts + if log: + Log(cmd) + proc = subprocess.Popen(cmd, + stdout=sys.stdout, + stderr=sys.stderr, + env=os.environ.copy(), + shell=True) + proc.wait() + return proc.returncode + +def main(): + parser = ArgumentParser() + parser.add_argument('-x', nargs=1) + parser.add_argument('--cuda_log', action='store_true') + args, leftover = parser.parse_known_args(sys.argv[1:]) + + if args.x and args.x[0] == 'cuda': + if args.cuda_log: Log('-x cuda') + leftover = [pipes.quote(s) for s in leftover] + if args.cuda_log: Log('using nvcc') + return InvokeNvcc(leftover, log=args.cuda_log) + + # Strip our flags before passing through to the CPU compiler for files which + # are not -x cuda. We can't just pass 'leftover' because it also strips -x. + # We not only want to pass -x to the CPU compiler, but also keep it in its + # relative location in the argv list (the compiler is actually sensitive to + # this). + cpu_compiler_flags = [flag for flag in sys.argv[1:] + if not flag.startswith(('--cuda_log')) + and not flag.startswith(('-nvcc_options'))] + + return subprocess.call([CPU_COMPILER] + cpu_compiler_flags) + +if __name__ == '__main__': + sys.exit(main()) diff --git a/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/BUILD b/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/BUILD new file mode 100755 index 0000000000..6442e7628a --- /dev/null +++ b/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/BUILD @@ -0,0 +1,87 @@ +licenses(["restricted"]) + +package(default_visibility = ["//visibility:public"]) + +toolchain( + name = "toolchain-linux-x86_64", + exec_compatible_with = [ + "@bazel_tools//platforms:linux", + "@bazel_tools//platforms:x86_64", + ], + target_compatible_with = [ + "@bazel_tools//platforms:linux", + "@bazel_tools//platforms:x86_64", + ], + toolchain = ":cc-compiler-local", + toolchain_type = "@bazel_tools//tools/cpp:toolchain_type", +) + +cc_toolchain_suite( + name = "toolchain", + toolchains = { + "local|compiler": ":cc-compiler-local", + "darwin|compiler": ":cc-compiler-darwin", + "x64_windows|msvc-cl": ":cc-compiler-windows", + }, +) + +cc_toolchain( + name = "cc-compiler-local", + all_files = ":crosstool_wrapper_driver_is_not_gcc", + compiler_files = ":empty", + cpu = "local", + dwp_files = ":empty", + dynamic_runtime_libs = [":empty"], + linker_files = ":crosstool_wrapper_driver_is_not_gcc", + objcopy_files = ":empty", + static_runtime_libs = [":empty"], + strip_files = ":empty", + # To support linker flags that need to go to the start of command line + # we need the toolchain to support parameter files. Parameter files are + # last on the command line and contain all shared libraries to link, so all + # regular options will be left of them. + supports_param_files = 1, +) + +cc_toolchain( + name = "cc-compiler-darwin", + all_files = ":crosstool_wrapper_driver_is_not_gcc", + compiler_files = ":empty", + cpu = "darwin", + dwp_files = ":empty", + dynamic_runtime_libs = [":empty"], + linker_files = ":crosstool_wrapper_driver_is_not_gcc", + objcopy_files = ":empty", + static_runtime_libs = [":empty"], + strip_files = ":empty", + supports_param_files = 0, +) + +cc_toolchain( + name = "cc-compiler-windows", + all_files = ":windows_msvc_wrapper_files", + compiler_files = ":empty", + cpu = "x64_windows", + dwp_files = ":empty", + dynamic_runtime_libs = [":empty"], + linker_files = ":windows_msvc_wrapper_files", + objcopy_files = ":empty", + static_runtime_libs = [":empty"], + strip_files = ":empty", + supports_param_files = 1, +) + +filegroup( + name = "empty", + srcs = [], +) + +filegroup( + name = "crosstool_wrapper_driver_is_not_gcc", + srcs = ["clang/bin/crosstool_wrapper_driver_is_not_gcc"], +) + +filegroup( + name = "windows_msvc_wrapper_files", + srcs = glob(["windows/msvc_*"]), +) diff --git a/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/CROSSTOOL b/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/CROSSTOOL new file mode 100755 index 0000000000..0d89a539b8 --- /dev/null +++ b/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/CROSSTOOL @@ -0,0 +1,1431 @@ +major_version: "local" +minor_version: "" +default_target_cpu: "same_as_host" + +default_toolchain { + cpu: "k8" + toolchain_identifier: "local_linux" +} +default_toolchain { + cpu: "piii" + toolchain_identifier: "local_linux" +} +default_toolchain { + cpu: "arm" + toolchain_identifier: "local_linux" +} +default_toolchain { + cpu: "darwin" + toolchain_identifier: "local_darwin" +} +default_toolchain { + cpu: "ppc" + toolchain_identifier: "local_linux" +} +default_toolchain { + cpu: "x64_windows" + toolchain_identifier: "local_windows" +} + +toolchain { + abi_version: "local" + abi_libc_version: "local" + compiler: "compiler" + host_system_name: "local" + needsPic: true + target_libc: "local" + target_cpu: "local" + target_system_name: "local" + toolchain_identifier: "local_linux" + + feature { + name: "c++11" + flag_set { + action: "c++-compile" + flag_group { + flag: "-std=c++11" + } + } + } + + feature { + name: "stdlib" + flag_set { + action: "c++-link-executable" + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "-lstdc++" + } + } + } + + feature { + name: "determinism" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + # Make C++ compilation deterministic. Use linkstamping instead of these + # compiler symbols. + flag: "-Wno-builtin-macro-redefined" + flag: "-D__DATE__=\"redacted\"" + flag: "-D__TIMESTAMP__=\"redacted\"" + flag: "-D__TIME__=\"redacted\"" + } + } + } + + feature { + name: "alwayslink" + flag_set { + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + action: "c++-link-executable" + flag_group { + flag: "-Wl,-no-as-needed" + } + } + } + + # This feature will be enabled for builds that support pic by bazel. + feature { + name: "pic" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + expand_if_all_available: "pic" + flag: "-fPIC" + } + flag_group { + expand_if_none_available: "pic" + flag: "-fPIE" + } + } + } + + # Security hardening on by default. + feature { + name: "hardening" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + # Conservative choice; -D_FORTIFY_SOURCE=2 may be unsafe in some cases. + # We need to undef it before redefining it as some distributions now + # have it enabled by default. + flag: "-U_FORTIFY_SOURCE" + flag: "-D_FORTIFY_SOURCE=1" + flag: "-fstack-protector" + } + } + flag_set { + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "-Wl,-z,relro,-z,now" + } + } + flag_set { + action: "c++-link-executable" + flag_group { + flag: "-pie" + flag: "-Wl,-z,relro,-z,now" + } + } + } + + feature { + name: "warnings" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + # All warnings are enabled. Maybe enable -Werror as well? + flag: "-Wall" + + } + } + } + + # Keep stack frames for debugging, even in opt mode. + feature { + name: "frame-pointer" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + flag: "-fno-omit-frame-pointer" + } + } + } + + feature { + name: "build-id" + flag_set { + action: "c++-link-executable" + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + flag_group { + # Stamp the binary with a unique identifier. + flag: "-Wl,--build-id=md5" + flag: "-Wl,--hash-style=gnu" + } + } + } + + feature { + name: "no-canonical-prefixes" + flag_set { + action: "c-compile" + action: "c++-compile" + action: "c++-link-executable" + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "-no-canonical-prefixes" + flag: "-fno-canonical-system-headers" + } + } + } + + feature { + name: "disable-assertions" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + flag: "-DNDEBUG" + } + } + } + + feature { + name: "linker-bin-path" + + flag_set { + action: "c++-link-executable" + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "-B/usr/bin" + } + } + } + + feature { + name: "common" + implies: "stdlib" + implies: "c++11" + implies: "determinism" + implies: "alwayslink" + implies: "hardening" + implies: "warnings" + implies: "frame-pointer" + implies: "build-id" + implies: "no-canonical-prefixes" + implies: "linker-bin-path" + } + + feature { + name: "opt" + implies: "common" + implies: "disable-assertions" + + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + # No debug symbols. + # Maybe we should enable https://gcc.gnu.org/wiki/DebugFission for opt + # or even generally? However, that can't happen here, as it requires + # special handling in Bazel. + flag: "-g0" + + # Conservative choice for -O + # -O3 can increase binary size and even slow down the resulting binaries. + # Profile first and / or use FDO if you need better performance than this. + flag: "-O2" + + # Removal of unused code and data at link time (can this increase binary size in some cases?). + flag: "-ffunction-sections" + flag: "-fdata-sections" + } + } + flag_set { + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + action: "c++-link-executable" + flag_group { + flag: "-Wl,--gc-sections" + } + } + } + + feature { + name: "fastbuild" + implies: "common" + } + + feature { + name: "dbg" + implies: "common" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + flag: "-g" + } + } + } + + # Set clang as a C/C++ compiler. + tool_path { name: "gcc" path: "clang/bin/crosstool_wrapper_driver_is_not_gcc" } + + # Use the default system toolchain for everything else. + tool_path { name: "ar" path: "/usr/bin/ar" } + tool_path { name: "compat-ld" path: "/usr/bin/ld" } + tool_path { name: "cpp" path: "/usr/bin/cpp" } + tool_path { name: "dwp" path: "/usr/bin/dwp" } + tool_path { name: "gcov" path: "/usr/bin/gcov" } + tool_path { name: "ld" path: "/usr/bin/ld" } + tool_path { name: "nm" path: "/usr/bin/nm" } + tool_path { name: "objcopy" path: "/usr/bin/objcopy" } + tool_path { name: "objdump" path: "/usr/bin/objdump" } + tool_path { name: "strip" path: "/usr/bin/strip" } + + # Enabled dynamic linking. + linking_mode_flags { mode: DYNAMIC } + + cxx_builtin_include_directory: "/usr/include/c++/4.8" + cxx_builtin_include_directory: "/usr/include/x86_64-linux-gnu/c++/4.8" + cxx_builtin_include_directory: "/usr/include/c++/4.8/backward" + cxx_builtin_include_directory: "/usr/lib/gcc/x86_64-linux-gnu/4.8/include" + cxx_builtin_include_directory: "/usr/local/include" + cxx_builtin_include_directory: "/usr/lib/gcc/x86_64-linux-gnu/4.8/include-fixed" + cxx_builtin_include_directory: "/usr/include/x86_64-linux-gnu" + cxx_builtin_include_directory: "/usr/include" + cxx_builtin_include_directory: "/usr/local/cuda-9.0/targets/x86_64-linux/include" + cxx_builtin_include_directory: "/usr/local/cuda-9.0/include" + cxx_builtin_include_directory: "/usr/local/cuda-9.0/extras/CUPTI/include" + cxx_builtin_include_directory: "/usr/include" +} + +toolchain { + abi_version: "local" + abi_libc_version: "local" + compiler: "compiler" + host_system_name: "local" + needsPic: true + target_libc: "macosx" + target_cpu: "darwin" + target_system_name: "local" + toolchain_identifier: "local_darwin" + feature { + name: "c++11" + flag_set { + action: "c++-compile" + flag_group { + flag: "-std=c++11" + } + } + } + + feature { + name: "stdlib" + flag_set { + action: "c++-link-executable" + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "-lc++" + } + } + } + + feature { + name: "determinism" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + # Make C++ compilation deterministic. Use linkstamping instead of these + # compiler symbols. + flag: "-Wno-builtin-macro-redefined" + flag: "-D__DATE__=\"redacted\"" + flag: "-D__TIMESTAMP__=\"redacted\"" + flag: "-D__TIME__=\"redacted\"" + } + } + } + + # This feature will be enabled for builds that support pic by bazel. + feature { + name: "pic" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + expand_if_all_available: "pic" + flag: "-fPIC" + } + flag_group { + expand_if_none_available: "pic" + flag: "-fPIE" + } + } + } + + # Security hardening on by default. + feature { + name: "hardening" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + # Conservative choice; -D_FORTIFY_SOURCE=2 may be unsafe in some cases. + # We need to undef it before redefining it as some distributions now + # have it enabled by default. + flag: "-U_FORTIFY_SOURCE" + flag: "-D_FORTIFY_SOURCE=1" + flag: "-fstack-protector" + } + } + flag_set { + action: "c++-link-executable" + flag_group { + flag: "-pie" + } + } + } + + feature { + name: "warnings" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + # All warnings are enabled. Maybe enable -Werror as well? + flag: "-Wall" + + } + } + } + + # Keep stack frames for debugging, even in opt mode. + feature { + name: "frame-pointer" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + flag: "-fno-omit-frame-pointer" + } + } + } + + feature { + name: "no-canonical-prefixes" + flag_set { + action: "c-compile" + action: "c++-compile" + action: "c++-link-executable" + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag:"-no-canonical-prefixes" + } + } + } + + feature { + name: "disable-assertions" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + flag: "-DNDEBUG" + } + } + } + + feature { + name: "linker-bin-path" + + flag_set { + action: "c++-link-executable" + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "-B/usr/bin" + } + } + } + + feature { + name: "undefined-dynamic" + flag_set { + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + action: "c++-link-executable" + flag_group { + flag: "-undefined" + flag: "dynamic_lookup" + } + } + } + + feature { + name: "common" + implies: "stdlib" + implies: "c++11" + implies: "determinism" + implies: "hardening" + implies: "warnings" + implies: "frame-pointer" + implies: "no-canonical-prefixes" + implies: "linker-bin-path" + implies: "undefined-dynamic" + } + + feature { + name: "opt" + implies: "common" + implies: "disable-assertions" + + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + # No debug symbols. + # Maybe we should enable https://gcc.gnu.org/wiki/DebugFission for opt + # or even generally? However, that can't happen here, as it requires + # special handling in Bazel. + flag: "-g0" + + # Conservative choice for -O + # -O3 can increase binary size and even slow down the resulting binaries. + # Profile first and / or use FDO if you need better performance than this. + flag: "-O2" + + # Removal of unused code and data at link time (can this increase binary size in some cases?). + flag: "-ffunction-sections" + flag: "-fdata-sections" + } + } + } + + feature { + name: "fastbuild" + implies: "common" + } + + feature { + name: "dbg" + implies: "common" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + flag: "-g" + } + } + } + + # Set clang as a C/C++ compiler. + tool_path { name: "gcc" path: "clang/bin/crosstool_wrapper_driver_is_not_gcc" } + + # Use the default system toolchain for everything else. + tool_path { name: "ar" path: "/usr/bin/libtool" } + tool_path { name: "compat-ld" path: "/usr/bin/ld" } + tool_path { name: "cpp" path: "/usr/bin/cpp" } + tool_path { name: "dwp" path: "/usr/bin/dwp" } + tool_path { name: "gcov" path: "/usr/bin/gcov" } + tool_path { name: "ld" path: "/usr/bin/ld" } + tool_path { name: "nm" path: "/usr/bin/nm" } + tool_path { name: "objcopy" path: "/usr/bin/objcopy" } + tool_path { name: "objdump" path: "/usr/bin/objdump" } + tool_path { name: "strip" path: "/usr/bin/strip" } + + # Enabled dynamic linking. + linking_mode_flags { mode: DYNAMIC } + + cxx_builtin_include_directory: "/usr/include/c++/4.8" + cxx_builtin_include_directory: "/usr/include/x86_64-linux-gnu/c++/4.8" + cxx_builtin_include_directory: "/usr/include/c++/4.8/backward" + cxx_builtin_include_directory: "/usr/lib/gcc/x86_64-linux-gnu/4.8/include" + cxx_builtin_include_directory: "/usr/local/include" + cxx_builtin_include_directory: "/usr/lib/gcc/x86_64-linux-gnu/4.8/include-fixed" + cxx_builtin_include_directory: "/usr/include/x86_64-linux-gnu" + cxx_builtin_include_directory: "/usr/include" + cxx_builtin_include_directory: "/usr/local/cuda-9.0/targets/x86_64-linux/include" + cxx_builtin_include_directory: "/usr/local/cuda-9.0/include" + cxx_builtin_include_directory: "/usr/local/cuda-9.0/extras/CUPTI/include" + cxx_builtin_include_directory: "/usr/include" +} + +toolchain { + toolchain_identifier: "local_windows" + host_system_name: "local" + target_system_name: "local" + + abi_version: "local" + abi_libc_version: "local" + target_cpu: "x64_windows" + compiler: "msvc-cl" + target_libc: "msvcrt" + + + + tool_path { + name: "ar" + path: "" + } + tool_path { + name: "ml" + path: "" + } + tool_path { + name: "cpp" + path: "" + } + tool_path { + name: "gcc" + path: "" + } + tool_path { + name: "gcov" + path: "wrapper/bin/msvc_nop.bat" + } + tool_path { + name: "ld" + path: "" + } + tool_path { + name: "nm" + path: "wrapper/bin/msvc_nop.bat" + } + tool_path { + name: "objcopy" + path: "wrapper/bin/msvc_nop.bat" + } + tool_path { + name: "objdump" + path: "wrapper/bin/msvc_nop.bat" + } + tool_path { + name: "strip" + path: "wrapper/bin/msvc_nop.bat" + } + supports_interface_shared_objects: true + + # TODO(pcloudy): Review those flags below, they should be defined by cl.exe + compiler_flag: "/DCOMPILER_MSVC" + + # Don't define min/max macros in windows.h. + compiler_flag: "/DNOMINMAX" + + # Platform defines. + compiler_flag: "/D_WIN32_WINNT=0x0600" + # Turn off warning messages. + compiler_flag: "/D_CRT_SECURE_NO_DEPRECATE" + compiler_flag: "/D_CRT_SECURE_NO_WARNINGS" + compiler_flag: "/D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS" + + # Useful options to have on for compilation. + # Increase the capacity of object files to 2^32 sections. + compiler_flag: "/bigobj" + # Allocate 500MB for precomputed headers. + compiler_flag: "/Zm500" + # Use unsigned char by default. + compiler_flag: "/J" + # Use function level linking. + compiler_flag: "/Gy" + # Use string pooling. + compiler_flag: "/GF" + # Catch C++ exceptions only and tell the compiler to assume that functions declared + # as extern "C" never throw a C++ exception. + compiler_flag: "/EHsc" + + # Globally disabled warnings. + # Don't warn about elements of array being be default initialized. + compiler_flag: "/wd4351" + # Don't warn about no matching delete found. + compiler_flag: "/wd4291" + # Don't warn about diamond inheritance patterns. + compiler_flag: "/wd4250" + # Don't warn about insecure functions (e.g. non _s functions). + compiler_flag: "/wd4996" + + linker_flag: "/MACHINE:X64" + + feature { + name: "no_legacy_features" + } + + # Suppress startup banner. + feature { + name: "nologo" + flag_set { + action: "c-compile" + action: "c++-compile" + action: "c++-module-compile" + action: "c++-module-codegen" + action: "c++-header-parsing" + action: "assemble" + action: "preprocess-assemble" + action: "c++-link-executable" + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + action: "c++-link-static-library" + flag_group { + flag: "/nologo" + } + } + } + + feature { + name: 'has_configured_linker_path' + } + + # This feature indicates strip is not supported, building stripped binary will just result a copy of orignial binary + feature { + name: 'no_stripping' + } + + # This feature indicates this is a toolchain targeting Windows. + feature { + name: 'targets_windows' + implies: 'copy_dynamic_libraries_to_binary' + enabled: true + } + + feature { + name: 'copy_dynamic_libraries_to_binary' + } + + action_config { + config_name: 'assemble' + action_name: 'assemble' + tool { + tool_path: '' + } + implies: 'compiler_input_flags' + implies: 'compiler_output_flags' + implies: 'nologo' + implies: 'msvc_env' + implies: 'sysroot' + } + + action_config { + config_name: 'preprocess-assemble' + action_name: 'preprocess-assemble' + tool { + tool_path: '' + } + implies: 'compiler_input_flags' + implies: 'compiler_output_flags' + implies: 'nologo' + implies: 'msvc_env' + implies: 'sysroot' + } + + action_config { + config_name: 'c-compile' + action_name: 'c-compile' + tool { + tool_path: '' + } + implies: 'compiler_input_flags' + implies: 'compiler_output_flags' + implies: 'legacy_compile_flags' + implies: 'nologo' + implies: 'msvc_env' + implies: 'parse_showincludes' + implies: 'user_compile_flags' + implies: 'sysroot' + implies: 'unfiltered_compile_flags' + } + + action_config { + config_name: 'c++-compile' + action_name: 'c++-compile' + tool { + tool_path: '' + } + implies: 'compiler_input_flags' + implies: 'compiler_output_flags' + implies: 'legacy_compile_flags' + implies: 'nologo' + implies: 'msvc_env' + implies: 'parse_showincludes' + implies: 'user_compile_flags' + implies: 'sysroot' + implies: 'unfiltered_compile_flags' + } + + action_config { + config_name: 'c++-link-executable' + action_name: 'c++-link-executable' + tool { + tool_path: '' + } + implies: 'nologo' + implies: 'linkstamps' + implies: 'output_execpath_flags' + implies: 'input_param_flags' + implies: 'user_link_flags' + implies: 'legacy_link_flags' + implies: 'linker_subsystem_flag' + implies: 'linker_param_file' + implies: 'msvc_env' + implies: 'no_stripping' + } + + action_config { + config_name: 'c++-link-dynamic-library' + action_name: 'c++-link-dynamic-library' + tool { + tool_path: '' + } + implies: 'nologo' + implies: 'shared_flag' + implies: 'linkstamps' + implies: 'output_execpath_flags' + implies: 'input_param_flags' + implies: 'user_link_flags' + implies: 'legacy_link_flags' + implies: 'linker_subsystem_flag' + implies: 'linker_param_file' + implies: 'msvc_env' + implies: 'no_stripping' + implies: 'has_configured_linker_path' + implies: 'def_file' + } + + action_config { + config_name: 'c++-link-nodeps-dynamic-library' + action_name: 'c++-link-nodeps-dynamic-library' + tool { + tool_path: '' + } + implies: 'nologo' + implies: 'shared_flag' + implies: 'linkstamps' + implies: 'output_execpath_flags' + implies: 'input_param_flags' + implies: 'user_link_flags' + implies: 'legacy_link_flags' + implies: 'linker_subsystem_flag' + implies: 'linker_param_file' + implies: 'msvc_env' + implies: 'no_stripping' + implies: 'has_configured_linker_path' + implies: 'def_file' + } + + action_config { + config_name: 'c++-link-static-library' + action_name: 'c++-link-static-library' + tool { + tool_path: '' + } + implies: 'nologo' + implies: 'archiver_flags' + implies: 'input_param_flags' + implies: 'linker_param_file' + implies: 'msvc_env' + } + + # TODO(b/65151735): Remove legacy_compile_flags feature when legacy fields are + # not used in this crosstool + feature { + name: 'legacy_compile_flags' + flag_set { + expand_if_all_available: 'legacy_compile_flags' + action: 'preprocess-assemble' + action: 'c-compile' + action: 'c++-compile' + action: 'c++-header-parsing' + action: 'c++-module-compile' + action: 'c++-module-codegen' + flag_group { + iterate_over: 'legacy_compile_flags' + flag: '%{legacy_compile_flags}' + } + } + } + + feature { + name: "msvc_env" + env_set { + action: "c-compile" + action: "c++-compile" + action: "c++-module-compile" + action: "c++-module-codegen" + action: "c++-header-parsing" + action: "assemble" + action: "preprocess-assemble" + action: "c++-link-executable" + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + action: "c++-link-static-library" + env_entry { + key: "PATH" + value: "" + } + env_entry { + key: "INCLUDE" + value: "" + } + env_entry { + key: "LIB" + value: "" + } + env_entry { + key: "TMP" + value: "" + } + env_entry { + key: "TEMP" + value: "" + } + } + } + + feature { + name: 'include_paths' + flag_set { + action: "assemble" + action: 'preprocess-assemble' + action: 'c-compile' + action: 'c++-compile' + action: 'c++-header-parsing' + action: 'c++-module-compile' + flag_group { + iterate_over: 'quote_include_paths' + flag: '/I%{quote_include_paths}' + } + flag_group { + iterate_over: 'include_paths' + flag: '/I%{include_paths}' + } + flag_group { + iterate_over: 'system_include_paths' + flag: '/I%{system_include_paths}' + } + } + } + + feature { + name: "preprocessor_defines" + flag_set { + action: "assemble" + action: "preprocess-assemble" + action: "c-compile" + action: "c++-compile" + action: "c++-header-parsing" + action: "c++-module-compile" + flag_group { + flag: "/D%{preprocessor_defines}" + iterate_over: "preprocessor_defines" + } + } + } + + # Tell Bazel to parse the output of /showIncludes + feature { + name: 'parse_showincludes' + flag_set { + action: 'preprocess-assemble' + action: 'c-compile' + action: 'c++-compile' + action: 'c++-module-compile' + action: 'c++-header-parsing' + flag_group { + flag: "/showIncludes" + } + } + } + + + feature { + name: 'generate_pdb_file' + requires: { + feature: 'dbg' + } + requires: { + feature: 'fastbuild' + } + } + + feature { + name: 'shared_flag' + flag_set { + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: '/DLL' + } + } + } + + feature { + name: 'linkstamps' + flag_set { + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + expand_if_all_available: 'linkstamp_paths' + flag_group { + iterate_over: 'linkstamp_paths' + flag: '%{linkstamp_paths}' + } + } + } + + feature { + name: 'output_execpath_flags' + flag_set { + expand_if_all_available: 'output_execpath' + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: '/OUT:%{output_execpath}' + } + } + } + + feature { + name: 'archiver_flags' + flag_set { + expand_if_all_available: 'output_execpath' + action: 'c++-link-static-library' + flag_group { + flag: '/OUT:%{output_execpath}' + } + } + } + + feature { + name: 'input_param_flags' + flag_set { + expand_if_all_available: 'interface_library_output_path' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "/IMPLIB:%{interface_library_output_path}" + } + } + flag_set { + expand_if_all_available: 'libopts' + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + iterate_over: 'libopts' + flag: '%{libopts}' + } + } + flag_set { + expand_if_all_available: 'libraries_to_link' + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + action: 'c++-link-static-library' + flag_group { + iterate_over: 'libraries_to_link' + flag_group { + expand_if_equal: { + variable: 'libraries_to_link.type' + value: 'object_file_group' + } + iterate_over: 'libraries_to_link.object_files' + flag_group { + flag: '%{libraries_to_link.object_files}' + } + } + flag_group { + expand_if_equal: { + variable: 'libraries_to_link.type' + value: 'object_file' + } + flag_group { + flag: '%{libraries_to_link.name}' + } + } + flag_group { + expand_if_equal: { + variable: 'libraries_to_link.type' + value: 'interface_library' + } + flag_group { + flag: '%{libraries_to_link.name}' + } + } + flag_group { + expand_if_equal: { + variable: 'libraries_to_link.type' + value: 'static_library' + } + flag_group { + expand_if_false: 'libraries_to_link.is_whole_archive' + flag: '%{libraries_to_link.name}' + } + flag_group { + expand_if_true: 'libraries_to_link.is_whole_archive' + flag: '/WHOLEARCHIVE:%{libraries_to_link.name}' + } + } + } + } + } + + # Since this feature is declared earlier in the CROSSTOOL than + # "user_link_flags", this feature will be applied prior to it anwyhere they + # are both implied. And since "user_link_flags" contains the linkopts from + # the build rule, this allows the user to override the /SUBSYSTEM in the BUILD + # file. + feature { + name: 'linker_subsystem_flag' + flag_set { + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: '/SUBSYSTEM:CONSOLE' + } + } + } + + # The "user_link_flags" contains user-defined linkopts (from build rules) + # so it should be defined after features that declare user-overridable flags. + # For example the "linker_subsystem_flag" defines a default "/SUBSYSTEM" flag + # but we want to let the user override it, therefore "link_flag_subsystem" is + # defined earlier in the CROSSTOOL file than "user_link_flags". + feature { + name: 'user_link_flags' + flag_set { + expand_if_all_available: 'user_link_flags' + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + iterate_over: 'user_link_flags' + flag: '%{user_link_flags}' + } + } + } + feature { + name: 'legacy_link_flags' + flag_set { + expand_if_all_available: 'legacy_link_flags' + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + iterate_over: 'legacy_link_flags' + flag: '%{legacy_link_flags}' + } + } + } + + feature { + name: 'linker_param_file' + flag_set { + expand_if_all_available: 'linker_param_file' + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + action: 'c++-link-static-library' + flag_group { + flag: '@%{linker_param_file}' + } + } + } + + feature { + name: 'static_link_msvcrt' + } + + feature { + name: 'static_link_msvcrt_no_debug' + flag_set { + action: 'c-compile' + action: 'c++-compile' + flag_group { + flag: "/MT" + } + } + flag_set { + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "/DEFAULTLIB:libcmt.lib" + } + } + requires: { feature: 'fastbuild'} + requires: { feature: 'opt'} + } + + feature { + name: 'dynamic_link_msvcrt_no_debug' + flag_set { + action: 'c-compile' + action: 'c++-compile' + flag_group { + flag: "/MD" + } + } + flag_set { + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "/DEFAULTLIB:msvcrt.lib" + } + } + requires: { feature: 'fastbuild'} + requires: { feature: 'opt'} + } + + feature { + name: 'static_link_msvcrt_debug' + flag_set { + action: 'c-compile' + action: 'c++-compile' + flag_group { + flag: "/MTd" + } + } + flag_set { + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "/DEFAULTLIB:libcmtd.lib" + } + } + requires: { feature: 'dbg'} + } + + feature { + name: 'dynamic_link_msvcrt_debug' + flag_set { + action: 'c-compile' + action: 'c++-compile' + flag_group { + flag: "/MDd" + } + } + flag_set { + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "/DEFAULTLIB:msvcrtd.lib" + } + } + requires: { feature: 'dbg'} + } + + feature { + name: 'dbg' + flag_set { + action: 'c-compile' + action: 'c++-compile' + flag_group { + flag: "/Od" + flag: "/Z7" + flag: "/DDEBUG" + } + } + flag_set { + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "/DEBUG:FULL" + flag: "/INCREMENTAL:NO" + } + } + implies: 'generate_pdb_file' + } + + feature { + name: 'fastbuild' + flag_set { + action: 'c-compile' + action: 'c++-compile' + flag_group { + flag: "/Od" + flag: "/Z7" + flag: "/DDEBUG" + } + } + flag_set { + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "/DEBUG:FASTLINK" + flag: "/INCREMENTAL:NO" + } + } + implies: 'generate_pdb_file' + } + + feature { + name: 'opt' + flag_set { + action: 'c-compile' + action: 'c++-compile' + flag_group { + flag: "/O2" + flag: "/DNDEBUG" + } + } + } + + feature { + name: 'user_compile_flags' + flag_set { + expand_if_all_available: 'user_compile_flags' + action: 'preprocess-assemble' + action: 'c-compile' + action: 'c++-compile' + action: 'c++-header-parsing' + action: 'c++-module-compile' + action: 'c++-module-codegen' + flag_group { + iterate_over: 'user_compile_flags' + flag: '%{user_compile_flags}' + } + } + } + + feature { + name: 'sysroot' + flag_set { + expand_if_all_available: 'sysroot' + action: 'assemble' + action: 'preprocess-assemble' + action: 'c-compile' + action: 'c++-compile' + action: 'c++-header-parsing' + action: 'c++-module-compile' + action: 'c++-module-codegen' + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + iterate_over: 'sysroot' + flag: '--sysroot=%{sysroot}' + } + } + } + + feature { + name: 'unfiltered_compile_flags' + flag_set { + expand_if_all_available: 'unfiltered_compile_flags' + action: 'preprocess-assemble' + action: 'c-compile' + action: 'c++-compile' + action: 'c++-header-parsing' + action: 'c++-module-compile' + action: 'c++-module-codegen' + flag_group { + iterate_over: 'unfiltered_compile_flags' + flag: '%{unfiltered_compile_flags}' + } + } + } + + feature { + name: 'compiler_output_flags' + flag_set { + action: 'assemble' + flag_group { + expand_if_all_available: 'output_file' + expand_if_none_available: 'output_assembly_file' + expand_if_none_available: 'output_preprocess_file' + flag: '/Fo%{output_file}' + flag: '/Zi' + } + } + flag_set { + action: 'preprocess-assemble' + action: 'c-compile' + action: 'c++-compile' + action: 'c++-header-parsing' + action: 'c++-module-compile' + action: 'c++-module-codegen' + flag_group { + expand_if_all_available: 'output_file' + expand_if_none_available: 'output_assembly_file' + expand_if_none_available: 'output_preprocess_file' + flag: '/Fo%{output_file}' + } + flag_group { + expand_if_all_available: 'output_file' + expand_if_all_available: 'output_assembly_file' + flag: '/Fa%{output_file}' + } + flag_group { + expand_if_all_available: 'output_file' + expand_if_all_available: 'output_preprocess_file' + flag: '/P' + flag: '/Fi%{output_file}' + } + } + } + + feature { + name: 'compiler_input_flags' + flag_set { + action: 'assemble' + action: 'preprocess-assemble' + action: 'c-compile' + action: 'c++-compile' + action: 'c++-header-parsing' + action: 'c++-module-compile' + action: 'c++-module-codegen' + flag_group { + expand_if_all_available: 'source_file' + flag: '/c' + flag: '%{source_file}' + } + } + } + + feature { + name : 'def_file', + flag_set { + expand_if_all_available: 'def_file_path' + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "/DEF:%{def_file_path}" + # We can specify a different DLL name in DEF file, /ignore:4070 suppresses + # the warning message about DLL name doesn't match the default one. + # See https://msdn.microsoft.com/en-us/library/sfkk2fz7.aspx + flag: "/ignore:4070" + } + } + } + + feature { + name: 'windows_export_all_symbols' + } + + feature { + name: 'no_windows_export_all_symbols' + } + + linking_mode_flags { mode: DYNAMIC } +} diff --git a/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/clang/bin/crosstool_wrapper_driver_is_not_gcc b/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/clang/bin/crosstool_wrapper_driver_is_not_gcc new file mode 100755 index 0000000000..63893d3722 --- /dev/null +++ b/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/clang/bin/crosstool_wrapper_driver_is_not_gcc @@ -0,0 +1,264 @@ +#!/usr/bin/env python +# Copyright 2015 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. +# ============================================================================== + +"""Crosstool wrapper for compiling CUDA programs. + +SYNOPSIS: + crosstool_wrapper_is_not_gcc [options passed in by cc_library() + or cc_binary() rule] + +DESCRIPTION: + This script is expected to be called by the cc_library() or cc_binary() bazel + rules. When the option "-x cuda" is present in the list of arguments passed + to this script, it invokes the nvcc CUDA compiler. Most arguments are passed + as is as a string to --compiler-options of nvcc. When "-x cuda" is not + present, this wrapper invokes hybrid_driver_is_not_gcc with the input + arguments as is. + +NOTES: + Changes to the contents of this file must be propagated from + //third_party/gpus/crosstool/crosstool_wrapper_is_not_gcc to + //third_party/gpus/crosstool/v*/*/clang/bin/crosstool_wrapper_is_not_gcc +""" + +from __future__ import print_function + +__author__ = 'keveman@google.com (Manjunath Kudlur)' + +from argparse import ArgumentParser +import os +import subprocess +import re +import sys +import pipes + +# Template values set by cuda_autoconf. +CPU_COMPILER = ('/usr/bin/gcc') +GCC_HOST_COMPILER_PATH = ('/usr/bin/gcc') + +NVCC_PATH = '/usr/local/cuda-9.0/bin/nvcc' +PREFIX_DIR = os.path.dirname(GCC_HOST_COMPILER_PATH) +NVCC_VERSION = '9.0' + +def Log(s): + print('gpus/crosstool: {0}'.format(s)) + + +def GetOptionValue(argv, option): + """Extract the list of values for option from the argv list. + + Args: + argv: A list of strings, possibly the argv passed to main(). + option: The option whose value to extract, without the leading '-'. + + Returns: + A list of values, either directly following the option, + (eg., -opt val1 val2) or values collected from multiple occurrences of + the option (eg., -opt val1 -opt val2). + """ + + parser = ArgumentParser() + parser.add_argument('-' + option, nargs='*', action='append') + args, _ = parser.parse_known_args(argv) + if not args or not vars(args)[option]: + return [] + else: + return sum(vars(args)[option], []) + + +def GetHostCompilerOptions(argv): + """Collect the -isystem, -iquote, and --sysroot option values from argv. + + Args: + argv: A list of strings, possibly the argv passed to main(). + + Returns: + The string that can be used as the --compiler-options to nvcc. + """ + + parser = ArgumentParser() + parser.add_argument('-isystem', nargs='*', action='append') + parser.add_argument('-iquote', nargs='*', action='append') + parser.add_argument('--sysroot', nargs=1) + parser.add_argument('-g', nargs='*', action='append') + parser.add_argument('-fno-canonical-system-headers', action='store_true') + + args, _ = parser.parse_known_args(argv) + + opts = '' + + if args.isystem: + opts += ' -isystem ' + ' -isystem '.join(sum(args.isystem, [])) + if args.iquote: + opts += ' -iquote ' + ' -iquote '.join(sum(args.iquote, [])) + if args.g: + opts += ' -g' + ' -g'.join(sum(args.g, [])) + if args.fno_canonical_system_headers: + opts += ' -fno-canonical-system-headers' + if args.sysroot: + opts += ' --sysroot ' + args.sysroot[0] + + return opts + +def _update_options(nvcc_options): + if NVCC_VERSION in ("7.0",): + return nvcc_options + + update_options = { "relaxed-constexpr" : "expt-relaxed-constexpr" } + return [ update_options[opt] if opt in update_options else opt + for opt in nvcc_options ] + +def GetNvccOptions(argv): + """Collect the -nvcc_options values from argv. + + Args: + argv: A list of strings, possibly the argv passed to main(). + + Returns: + The string that can be passed directly to nvcc. + """ + + parser = ArgumentParser() + parser.add_argument('-nvcc_options', nargs='*', action='append') + + args, _ = parser.parse_known_args(argv) + + if args.nvcc_options: + options = _update_options(sum(args.nvcc_options, [])) + return ' '.join(['--'+a for a in options]) + return '' + + +def InvokeNvcc(argv, log=False): + """Call nvcc with arguments assembled from argv. + + Args: + argv: A list of strings, possibly the argv passed to main(). + log: True if logging is requested. + + Returns: + The return value of calling os.system('nvcc ' + args) + """ + + host_compiler_options = GetHostCompilerOptions(argv) + nvcc_compiler_options = GetNvccOptions(argv) + opt_option = GetOptionValue(argv, 'O') + m_options = GetOptionValue(argv, 'm') + m_options = ''.join([' -m' + m for m in m_options if m in ['32', '64']]) + include_options = GetOptionValue(argv, 'I') + out_file = GetOptionValue(argv, 'o') + depfiles = GetOptionValue(argv, 'MF') + defines = GetOptionValue(argv, 'D') + defines = ''.join([' -D' + define for define in defines]) + undefines = GetOptionValue(argv, 'U') + undefines = ''.join([' -U' + define for define in undefines]) + std_options = GetOptionValue(argv, 'std') + # currently only c++11 is supported by Cuda 7.0 std argument + nvcc_allowed_std_options = ["c++11"] + std_options = ''.join([' -std=' + define + for define in std_options if define in nvcc_allowed_std_options]) + + # The list of source files get passed after the -c option. I don't know of + # any other reliable way to just get the list of source files to be compiled. + src_files = GetOptionValue(argv, 'c') + + # Pass -w through from host to nvcc, but don't do anything fancier with + # warnings-related flags, since they're not necessarily the same across + # compilers. + warning_options = ' -w' if '-w' in argv else '' + + if len(src_files) == 0: + return 1 + if len(out_file) != 1: + return 1 + + opt = (' -O2' if (len(opt_option) > 0 and int(opt_option[0]) > 0) + else ' -g -G') + + includes = (' -I ' + ' -I '.join(include_options) + if len(include_options) > 0 + else '') + + # Unfortunately, there are other options that have -c prefix too. + # So allowing only those look like C/C++ files. + src_files = [f for f in src_files if + re.search('\.cpp$|\.cc$|\.c$|\.cxx$|\.C$', f)] + srcs = ' '.join(src_files) + out = ' -o ' + out_file[0] + + supported_cuda_compute_capabilities = [ "3.0" ] + nvccopts = '-D_FORCE_INLINES ' + for capability in supported_cuda_compute_capabilities: + capability = capability.replace('.', '') + nvccopts += r'-gencode=arch=compute_%s,\"code=sm_%s,compute_%s\" ' % ( + capability, capability, capability) + nvccopts += ' ' + nvcc_compiler_options + nvccopts += undefines + nvccopts += defines + nvccopts += std_options + nvccopts += m_options + nvccopts += warning_options + + if depfiles: + # Generate the dependency file + depfile = depfiles[0] + cmd = (NVCC_PATH + ' ' + nvccopts + + ' --compiler-options "' + host_compiler_options + '"' + + ' --compiler-bindir=' + GCC_HOST_COMPILER_PATH + + ' -I .' + + ' -x cu ' + opt + includes + ' ' + srcs + ' -M -o ' + depfile) + if log: Log(cmd) + exit_status = os.system(cmd) + if exit_status != 0: + return exit_status + + cmd = (NVCC_PATH + ' ' + nvccopts + + ' --compiler-options "' + host_compiler_options + ' -fPIC"' + + ' --compiler-bindir=' + GCC_HOST_COMPILER_PATH + + ' -I .' + + ' -x cu ' + opt + includes + ' -c ' + srcs + out) + + # TODO(zhengxq): for some reason, 'gcc' needs this help to find 'as'. + # Need to investigate and fix. + cmd = 'PATH=' + PREFIX_DIR + ':$PATH ' + cmd + if log: Log(cmd) + return os.system(cmd) + + +def main(): + parser = ArgumentParser() + parser.add_argument('-x', nargs=1) + parser.add_argument('--cuda_log', action='store_true') + args, leftover = parser.parse_known_args(sys.argv[1:]) + + if args.x and args.x[0] == 'cuda': + if args.cuda_log: Log('-x cuda') + leftover = [pipes.quote(s) for s in leftover] + if args.cuda_log: Log('using nvcc') + return InvokeNvcc(leftover, log=args.cuda_log) + + # Strip our flags before passing through to the CPU compiler for files which + # are not -x cuda. We can't just pass 'leftover' because it also strips -x. + # We not only want to pass -x to the CPU compiler, but also keep it in its + # relative location in the argv list (the compiler is actually sensitive to + # this). + cpu_compiler_flags = [flag for flag in sys.argv[1:] + if not flag.startswith(('--cuda_log'))] + + return subprocess.call([CPU_COMPILER] + cpu_compiler_flags) + +if __name__ == '__main__': + sys.exit(main()) diff --git a/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/windows/msvc_wrapper_for_nvcc.bat b/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/windows/msvc_wrapper_for_nvcc.bat new file mode 100755 index 0000000000..e896e654fd --- /dev/null +++ b/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/windows/msvc_wrapper_for_nvcc.bat @@ -0,0 +1,20 @@ +:: Copyright 2015 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. +:: ============================================================================= + +:: Invoke msvc_wrapper_for_nvcc.py, which is located in the same directory. +@echo OFF +set arg0=%~0 +for %%F in ("%arg0%") do set DRIVER_BIN=%%~dpF +"/usr/bin/python3" -B "%DRIVER_BIN%\msvc_wrapper_for_nvcc.py" %* diff --git a/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/windows/msvc_wrapper_for_nvcc.py b/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/windows/msvc_wrapper_for_nvcc.py new file mode 100755 index 0000000000..859b3196d5 --- /dev/null +++ b/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/windows/msvc_wrapper_for_nvcc.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python +# Copyright 2015 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. +# ============================================================================== + +"""Crosstool wrapper for compiling CUDA programs with nvcc on Windows. + +DESCRIPTION: + This script is the Windows version of //third_party/gpus/crosstool/crosstool_wrapper_is_not_gcc +""" + +from __future__ import print_function + +from argparse import ArgumentParser +import os +import subprocess +import re +import sys +import pipes + +# Template values set by cuda_autoconf. +CPU_COMPILER = ('/usr/bin/gcc') +GCC_HOST_COMPILER_PATH = ('/usr/bin/gcc') + +NVCC_PATH = '/usr/local/cuda-9.0/bin/nvcc' +NVCC_VERSION = '9.0' +NVCC_TEMP_DIR = "C:\\Windows\\Temp\\nvcc_inter_files_tmp_dir" +supported_cuda_compute_capabilities = [ "3.0" ] + +def Log(s): + print('gpus/crosstool: {0}'.format(s)) + + +def GetOptionValue(argv, option): + """Extract the list of values for option from options. + + Args: + option: The option whose value to extract, without the leading '/'. + + Returns: + 1. A list of values, either directly following the option, + (eg., /opt val1 val2) or values collected from multiple occurrences of + the option (eg., /opt val1 /opt val2). + 2. The leftover options. + """ + + parser = ArgumentParser(prefix_chars='/') + parser.add_argument('/' + option, nargs='*', action='append') + args, leftover = parser.parse_known_args(argv) + if args and vars(args)[option]: + return (sum(vars(args)[option], []), leftover) + return ([], leftover) + +def _update_options(nvcc_options): + if NVCC_VERSION in ("7.0",): + return nvcc_options + + update_options = { "relaxed-constexpr" : "expt-relaxed-constexpr" } + return [ update_options[opt] if opt in update_options else opt + for opt in nvcc_options ] + +def GetNvccOptions(argv): + """Collect the -nvcc_options values from argv. + + Args: + argv: A list of strings, possibly the argv passed to main(). + + Returns: + 1. The string that can be passed directly to nvcc. + 2. The leftover options. + """ + + parser = ArgumentParser() + parser.add_argument('-nvcc_options', nargs='*', action='append') + + args, leftover = parser.parse_known_args(argv) + + if args.nvcc_options: + options = _update_options(sum(args.nvcc_options, [])) + return (['--' + a for a in options], leftover) + return ([], leftover) + + +def InvokeNvcc(argv, log=False): + """Call nvcc with arguments assembled from argv. + + Args: + argv: A list of strings, possibly the argv passed to main(). + log: True if logging is requested. + + Returns: + The return value of calling os.system('nvcc ' + args) + """ + + src_files = [f for f in argv if + re.search('\.cpp$|\.cc$|\.c$|\.cxx$|\.C$', f)] + if len(src_files) == 0: + raise Error('No source files found for cuda compilation.') + + out_file = [ f for f in argv if f.startswith('/Fo') ] + if len(out_file) != 1: + raise Error('Please sepecify exactly one output file for cuda compilation.') + out = ['-o', out_file[0][len('/Fo'):]] + + nvcc_compiler_options, argv = GetNvccOptions(argv) + + opt_option, argv = GetOptionValue(argv, 'O') + opt = ['-g', '-G'] + if (len(opt_option) > 0 and opt_option[0] != 'd'): + opt = ['-O2'] + + include_options, argv = GetOptionValue(argv, 'I') + includes = ["-I " + include for include in include_options] + + defines, argv = GetOptionValue(argv, 'D') + defines = ['-D' + define for define in defines] + + undefines, argv = GetOptionValue(argv, 'U') + undefines = ['-U' + define for define in undefines] + + # The rest of the unrecongized options should be passed to host compiler + host_compiler_options = [option for option in argv if option not in (src_files + out_file)] + + m_options = ["-m64"] + + nvccopts = ['-D_FORCE_INLINES'] + for capability in supported_cuda_compute_capabilities: + capability = capability.replace('.', '') + nvccopts += [r'-gencode=arch=compute_%s,"code=sm_%s,compute_%s"' % ( + capability, capability, capability)] + nvccopts += nvcc_compiler_options + nvccopts += undefines + nvccopts += defines + nvccopts += m_options + nvccopts += ['--compiler-options="' + " ".join(host_compiler_options) + '"'] + nvccopts += ['-x', 'cu'] + opt + includes + out + ['-c'] + src_files + # If we don't specify --keep-dir, nvcc will generate intermediate files under TEMP + # Put them under NVCC_TEMP_DIR instead, then Bazel can ignore files under NVCC_TEMP_DIR during dependency check + # http://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html#options-for-guiding-compiler-driver + # Different actions are sharing NVCC_TEMP_DIR, so we cannot remove it if the directory already exists. + if os.path.isfile(NVCC_TEMP_DIR): + os.remove(NVCC_TEMP_DIR) + if not os.path.exists(NVCC_TEMP_DIR): + os.makedirs(NVCC_TEMP_DIR) + nvccopts += ['--keep', '--keep-dir', NVCC_TEMP_DIR] + cmd = [NVCC_PATH] + nvccopts + if log: + Log(cmd) + proc = subprocess.Popen(cmd, + stdout=sys.stdout, + stderr=sys.stderr, + env=os.environ.copy(), + shell=True) + proc.wait() + return proc.returncode + +def main(): + parser = ArgumentParser() + parser.add_argument('-x', nargs=1) + parser.add_argument('--cuda_log', action='store_true') + args, leftover = parser.parse_known_args(sys.argv[1:]) + + if args.x and args.x[0] == 'cuda': + if args.cuda_log: Log('-x cuda') + leftover = [pipes.quote(s) for s in leftover] + if args.cuda_log: Log('using nvcc') + return InvokeNvcc(leftover, log=args.cuda_log) + + # Strip our flags before passing through to the CPU compiler for files which + # are not -x cuda. We can't just pass 'leftover' because it also strips -x. + # We not only want to pass -x to the CPU compiler, but also keep it in its + # relative location in the argv list (the compiler is actually sensitive to + # this). + cpu_compiler_flags = [flag for flag in sys.argv[1:] + if not flag.startswith(('--cuda_log')) + and not flag.startswith(('-nvcc_options'))] + + return subprocess.call([CPU_COMPILER] + cpu_compiler_flags) + +if __name__ == '__main__': + sys.exit(main()) diff --git a/third_party/toolchains/preconfig/ubuntu14.04/py3/BUILD b/third_party/toolchains/preconfig/ubuntu14.04/py3/BUILD index e021df9e1e..460c879d32 100755 --- a/third_party/toolchains/preconfig/ubuntu14.04/py3/BUILD +++ b/third_party/toolchains/preconfig/ubuntu14.04/py3/BUILD @@ -136,7 +136,7 @@ genrule( "python_include/weakrefobject.h", ], cmd = """ -cp "/usr/include/python3.4m/Python-ast.h" "$(@D)/python_include/Python-ast.h" && cp "/usr/include/python3.4m/Python.h" "$(@D)/python_include/Python.h" && cp "/usr/include/python3.4m/abstract.h" "$(@D)/python_include/abstract.h" && cp "/usr/include/python3.4m/accu.h" "$(@D)/python_include/accu.h" && cp "/usr/include/python3.4m/asdl.h" "$(@D)/python_include/asdl.h" && cp "/usr/include/python3.4m/ast.h" "$(@D)/python_include/ast.h" && cp "/usr/include/python3.4m/bitset.h" "$(@D)/python_include/bitset.h" && cp "/usr/include/python3.4m/bltinmodule.h" "$(@D)/python_include/bltinmodule.h" && cp "/usr/include/python3.4m/boolobject.h" "$(@D)/python_include/boolobject.h" && cp "/usr/include/python3.4m/bytearrayobject.h" "$(@D)/python_include/bytearrayobject.h" && cp "/usr/include/python3.4m/bytes_methods.h" "$(@D)/python_include/bytes_methods.h" && cp "/usr/include/python3.4m/bytesobject.h" "$(@D)/python_include/bytesobject.h" && cp "/usr/include/python3.4m/cellobject.h" "$(@D)/python_include/cellobject.h" && cp "/usr/include/python3.4m/ceval.h" "$(@D)/python_include/ceval.h" && cp "/usr/include/python3.4m/classobject.h" "$(@D)/python_include/classobject.h" && cp "/usr/include/python3.4m/code.h" "$(@D)/python_include/code.h" && cp "/usr/include/python3.4m/codecs.h" "$(@D)/python_include/codecs.h" && cp "/usr/include/python3.4m/compile.h" "$(@D)/python_include/compile.h" && cp "/usr/include/python3.4m/complexobject.h" "$(@D)/python_include/complexobject.h" && cp "/usr/include/python3.4m/datetime.h" "$(@D)/python_include/datetime.h" && cp "/usr/include/python3.4m/descrobject.h" "$(@D)/python_include/descrobject.h" && cp "/usr/include/python3.4m/dictobject.h" "$(@D)/python_include/dictobject.h" && cp "/usr/include/python3.4m/dtoa.h" "$(@D)/python_include/dtoa.h" && cp "/usr/include/python3.4m/dynamic_annotations.h" "$(@D)/python_include/dynamic_annotations.h" && cp "/usr/include/python3.4m/enumobject.h" "$(@D)/python_include/enumobject.h" && cp "/usr/include/python3.4m/errcode.h" "$(@D)/python_include/errcode.h" && cp "/usr/include/python3.4m/eval.h" "$(@D)/python_include/eval.h" && cp "/usr/include/python3.4m/fileobject.h" "$(@D)/python_include/fileobject.h" && cp "/usr/include/python3.4m/fileutils.h" "$(@D)/python_include/fileutils.h" && cp "/usr/include/python3.4m/floatobject.h" "$(@D)/python_include/floatobject.h" && cp "/usr/include/python3.4m/frameobject.h" "$(@D)/python_include/frameobject.h" && cp "/usr/include/python3.4m/funcobject.h" "$(@D)/python_include/funcobject.h" && cp "/usr/include/python3.4m/genobject.h" "$(@D)/python_include/genobject.h" && cp "/usr/include/python3.4m/graminit.h" "$(@D)/python_include/graminit.h" && cp "/usr/include/python3.4m/grammar.h" "$(@D)/python_include/grammar.h" && cp "/usr/include/python3.4m/import.h" "$(@D)/python_include/import.h" && cp "/usr/include/python3.4m/intrcheck.h" "$(@D)/python_include/intrcheck.h" && cp "/usr/include/python3.4m/iterobject.h" "$(@D)/python_include/iterobject.h" && cp "/usr/include/python3.4m/listobject.h" "$(@D)/python_include/listobject.h" && cp "/usr/include/python3.4m/longintrepr.h" "$(@D)/python_include/longintrepr.h" && cp "/usr/include/python3.4m/longobject.h" "$(@D)/python_include/longobject.h" && cp "/usr/include/python3.4m/marshal.h" "$(@D)/python_include/marshal.h" && cp "/usr/include/python3.4m/memoryobject.h" "$(@D)/python_include/memoryobject.h" && cp "/usr/include/python3.4m/metagrammar.h" "$(@D)/python_include/metagrammar.h" && cp "/usr/include/python3.4m/methodobject.h" "$(@D)/python_include/methodobject.h" && cp "/usr/include/python3.4m/modsupport.h" "$(@D)/python_include/modsupport.h" && cp "/usr/include/python3.4m/moduleobject.h" "$(@D)/python_include/moduleobject.h" && cp "/usr/include/python3.4m/namespaceobject.h" "$(@D)/python_include/namespaceobject.h" && cp "/usr/include/python3.4m/node.h" "$(@D)/python_include/node.h" && cp "/usr/include/python3.4m/object.h" "$(@D)/python_include/object.h" && cp "/usr/include/python3.4m/objimpl.h" "$(@D)/python_include/objimpl.h" && cp "/usr/include/python3.4m/opcode.h" "$(@D)/python_include/opcode.h" && cp "/usr/include/python3.4m/osdefs.h" "$(@D)/python_include/osdefs.h" && cp "/usr/include/python3.4m/parsetok.h" "$(@D)/python_include/parsetok.h" && cp "/usr/include/python3.4m/patchlevel.h" "$(@D)/python_include/patchlevel.h" && cp "/usr/include/python3.4m/pgen.h" "$(@D)/python_include/pgen.h" && cp "/usr/include/python3.4m/pgenheaders.h" "$(@D)/python_include/pgenheaders.h" && cp "/usr/include/python3.4m/py_curses.h" "$(@D)/python_include/py_curses.h" && cp "/usr/include/python3.4m/pyarena.h" "$(@D)/python_include/pyarena.h" && cp "/usr/include/python3.4m/pyatomic.h" "$(@D)/python_include/pyatomic.h" && cp "/usr/include/python3.4m/pycapsule.h" "$(@D)/python_include/pycapsule.h" && cp "/usr/include/python3.4m/pyconfig.h" "$(@D)/python_include/pyconfig.h" && cp "/usr/include/python3.4m/pyctype.h" "$(@D)/python_include/pyctype.h" && cp "/usr/include/python3.4m/pydebug.h" "$(@D)/python_include/pydebug.h" && cp "/usr/include/python3.4m/pyerrors.h" "$(@D)/python_include/pyerrors.h" && cp "/usr/include/python3.4m/pyexpat.h" "$(@D)/python_include/pyexpat.h" && cp "/usr/include/python3.4m/pyfpe.h" "$(@D)/python_include/pyfpe.h" && cp "/usr/include/python3.4m/pygetopt.h" "$(@D)/python_include/pygetopt.h" && cp "/usr/include/python3.4m/pyhash.h" "$(@D)/python_include/pyhash.h" && cp "/usr/include/python3.4m/pymacconfig.h" "$(@D)/python_include/pymacconfig.h" && cp "/usr/include/python3.4m/pymacro.h" "$(@D)/python_include/pymacro.h" && cp "/usr/include/python3.4m/pymath.h" "$(@D)/python_include/pymath.h" && cp "/usr/include/python3.4m/pymem.h" "$(@D)/python_include/pymem.h" && cp "/usr/include/python3.4m/pyport.h" "$(@D)/python_include/pyport.h" && cp "/usr/include/python3.4m/pystate.h" "$(@D)/python_include/pystate.h" && cp "/usr/include/python3.4m/pystrcmp.h" "$(@D)/python_include/pystrcmp.h" && cp "/usr/include/python3.4m/pystrtod.h" "$(@D)/python_include/pystrtod.h" && cp "/usr/include/python3.4m/pythonrun.h" "$(@D)/python_include/pythonrun.h" && cp "/usr/include/python3.4m/pythread.h" "$(@D)/python_include/pythread.h" && cp "/usr/include/python3.4m/pytime.h" "$(@D)/python_include/pytime.h" && cp "/usr/include/python3.4m/rangeobject.h" "$(@D)/python_include/rangeobject.h" && cp "/usr/include/python3.4m/setobject.h" "$(@D)/python_include/setobject.h" && cp "/usr/include/python3.4m/sliceobject.h" "$(@D)/python_include/sliceobject.h" && cp "/usr/include/python3.4m/structmember.h" "$(@D)/python_include/structmember.h" && cp "/usr/include/python3.4m/structseq.h" "$(@D)/python_include/structseq.h" && cp "/usr/include/python3.4m/symtable.h" "$(@D)/python_include/symtable.h" && cp "/usr/include/python3.4m/sysmodule.h" "$(@D)/python_include/sysmodule.h" && cp "/usr/include/python3.4m/token.h" "$(@D)/python_include/token.h" && cp "/usr/include/python3.4m/traceback.h" "$(@D)/python_include/traceback.h" && cp "/usr/include/python3.4m/tupleobject.h" "$(@D)/python_include/tupleobject.h" && cp "/usr/include/python3.4m/typeslots.h" "$(@D)/python_include/typeslots.h" && cp "/usr/include/python3.4m/ucnhash.h" "$(@D)/python_include/ucnhash.h" && cp "/usr/include/python3.4m/unicodeobject.h" "$(@D)/python_include/unicodeobject.h" && cp "/usr/include/python3.4m/warnings.h" "$(@D)/python_include/warnings.h" && cp "/usr/include/python3.4m/weakrefobject.h" "$(@D)/python_include/weakrefobject.h" +cp -f "/usr/include/python3.4m/Python-ast.h" "$(@D)/python_include/Python-ast.h" && cp -f "/usr/include/python3.4m/Python.h" "$(@D)/python_include/Python.h" && cp -f "/usr/include/python3.4m/abstract.h" "$(@D)/python_include/abstract.h" && cp -f "/usr/include/python3.4m/accu.h" "$(@D)/python_include/accu.h" && cp -f "/usr/include/python3.4m/asdl.h" "$(@D)/python_include/asdl.h" && cp -f "/usr/include/python3.4m/ast.h" "$(@D)/python_include/ast.h" && cp -f "/usr/include/python3.4m/bitset.h" "$(@D)/python_include/bitset.h" && cp -f "/usr/include/python3.4m/bltinmodule.h" "$(@D)/python_include/bltinmodule.h" && cp -f "/usr/include/python3.4m/boolobject.h" "$(@D)/python_include/boolobject.h" && cp -f "/usr/include/python3.4m/bytearrayobject.h" "$(@D)/python_include/bytearrayobject.h" && cp -f "/usr/include/python3.4m/bytes_methods.h" "$(@D)/python_include/bytes_methods.h" && cp -f "/usr/include/python3.4m/bytesobject.h" "$(@D)/python_include/bytesobject.h" && cp -f "/usr/include/python3.4m/cellobject.h" "$(@D)/python_include/cellobject.h" && cp -f "/usr/include/python3.4m/ceval.h" "$(@D)/python_include/ceval.h" && cp -f "/usr/include/python3.4m/classobject.h" "$(@D)/python_include/classobject.h" && cp -f "/usr/include/python3.4m/code.h" "$(@D)/python_include/code.h" && cp -f "/usr/include/python3.4m/codecs.h" "$(@D)/python_include/codecs.h" && cp -f "/usr/include/python3.4m/compile.h" "$(@D)/python_include/compile.h" && cp -f "/usr/include/python3.4m/complexobject.h" "$(@D)/python_include/complexobject.h" && cp -f "/usr/include/python3.4m/datetime.h" "$(@D)/python_include/datetime.h" && cp -f "/usr/include/python3.4m/descrobject.h" "$(@D)/python_include/descrobject.h" && cp -f "/usr/include/python3.4m/dictobject.h" "$(@D)/python_include/dictobject.h" && cp -f "/usr/include/python3.4m/dtoa.h" "$(@D)/python_include/dtoa.h" && cp -f "/usr/include/python3.4m/dynamic_annotations.h" "$(@D)/python_include/dynamic_annotations.h" && cp -f "/usr/include/python3.4m/enumobject.h" "$(@D)/python_include/enumobject.h" && cp -f "/usr/include/python3.4m/errcode.h" "$(@D)/python_include/errcode.h" && cp -f "/usr/include/python3.4m/eval.h" "$(@D)/python_include/eval.h" && cp -f "/usr/include/python3.4m/fileobject.h" "$(@D)/python_include/fileobject.h" && cp -f "/usr/include/python3.4m/fileutils.h" "$(@D)/python_include/fileutils.h" && cp -f "/usr/include/python3.4m/floatobject.h" "$(@D)/python_include/floatobject.h" && cp -f "/usr/include/python3.4m/frameobject.h" "$(@D)/python_include/frameobject.h" && cp -f "/usr/include/python3.4m/funcobject.h" "$(@D)/python_include/funcobject.h" && cp -f "/usr/include/python3.4m/genobject.h" "$(@D)/python_include/genobject.h" && cp -f "/usr/include/python3.4m/graminit.h" "$(@D)/python_include/graminit.h" && cp -f "/usr/include/python3.4m/grammar.h" "$(@D)/python_include/grammar.h" && cp -f "/usr/include/python3.4m/import.h" "$(@D)/python_include/import.h" && cp -f "/usr/include/python3.4m/intrcheck.h" "$(@D)/python_include/intrcheck.h" && cp -f "/usr/include/python3.4m/iterobject.h" "$(@D)/python_include/iterobject.h" && cp -f "/usr/include/python3.4m/listobject.h" "$(@D)/python_include/listobject.h" && cp -f "/usr/include/python3.4m/longintrepr.h" "$(@D)/python_include/longintrepr.h" && cp -f "/usr/include/python3.4m/longobject.h" "$(@D)/python_include/longobject.h" && cp -f "/usr/include/python3.4m/marshal.h" "$(@D)/python_include/marshal.h" && cp -f "/usr/include/python3.4m/memoryobject.h" "$(@D)/python_include/memoryobject.h" && cp -f "/usr/include/python3.4m/metagrammar.h" "$(@D)/python_include/metagrammar.h" && cp -f "/usr/include/python3.4m/methodobject.h" "$(@D)/python_include/methodobject.h" && cp -f "/usr/include/python3.4m/modsupport.h" "$(@D)/python_include/modsupport.h" && cp -f "/usr/include/python3.4m/moduleobject.h" "$(@D)/python_include/moduleobject.h" && cp -f "/usr/include/python3.4m/namespaceobject.h" "$(@D)/python_include/namespaceobject.h" && cp -f "/usr/include/python3.4m/node.h" "$(@D)/python_include/node.h" && cp -f "/usr/include/python3.4m/object.h" "$(@D)/python_include/object.h" && cp -f "/usr/include/python3.4m/objimpl.h" "$(@D)/python_include/objimpl.h" && cp -f "/usr/include/python3.4m/opcode.h" "$(@D)/python_include/opcode.h" && cp -f "/usr/include/python3.4m/osdefs.h" "$(@D)/python_include/osdefs.h" && cp -f "/usr/include/python3.4m/parsetok.h" "$(@D)/python_include/parsetok.h" && cp -f "/usr/include/python3.4m/patchlevel.h" "$(@D)/python_include/patchlevel.h" && cp -f "/usr/include/python3.4m/pgen.h" "$(@D)/python_include/pgen.h" && cp -f "/usr/include/python3.4m/pgenheaders.h" "$(@D)/python_include/pgenheaders.h" && cp -f "/usr/include/python3.4m/py_curses.h" "$(@D)/python_include/py_curses.h" && cp -f "/usr/include/python3.4m/pyarena.h" "$(@D)/python_include/pyarena.h" && cp -f "/usr/include/python3.4m/pyatomic.h" "$(@D)/python_include/pyatomic.h" && cp -f "/usr/include/python3.4m/pycapsule.h" "$(@D)/python_include/pycapsule.h" && cp -f "/usr/include/python3.4m/pyconfig.h" "$(@D)/python_include/pyconfig.h" && cp -f "/usr/include/python3.4m/pyctype.h" "$(@D)/python_include/pyctype.h" && cp -f "/usr/include/python3.4m/pydebug.h" "$(@D)/python_include/pydebug.h" && cp -f "/usr/include/python3.4m/pyerrors.h" "$(@D)/python_include/pyerrors.h" && cp -f "/usr/include/python3.4m/pyexpat.h" "$(@D)/python_include/pyexpat.h" && cp -f "/usr/include/python3.4m/pyfpe.h" "$(@D)/python_include/pyfpe.h" && cp -f "/usr/include/python3.4m/pygetopt.h" "$(@D)/python_include/pygetopt.h" && cp -f "/usr/include/python3.4m/pyhash.h" "$(@D)/python_include/pyhash.h" && cp -f "/usr/include/python3.4m/pymacconfig.h" "$(@D)/python_include/pymacconfig.h" && cp -f "/usr/include/python3.4m/pymacro.h" "$(@D)/python_include/pymacro.h" && cp -f "/usr/include/python3.4m/pymath.h" "$(@D)/python_include/pymath.h" && cp -f "/usr/include/python3.4m/pymem.h" "$(@D)/python_include/pymem.h" && cp -f "/usr/include/python3.4m/pyport.h" "$(@D)/python_include/pyport.h" && cp -f "/usr/include/python3.4m/pystate.h" "$(@D)/python_include/pystate.h" && cp -f "/usr/include/python3.4m/pystrcmp.h" "$(@D)/python_include/pystrcmp.h" && cp -f "/usr/include/python3.4m/pystrtod.h" "$(@D)/python_include/pystrtod.h" && cp -f "/usr/include/python3.4m/pythonrun.h" "$(@D)/python_include/pythonrun.h" && cp -f "/usr/include/python3.4m/pythread.h" "$(@D)/python_include/pythread.h" && cp -f "/usr/include/python3.4m/pytime.h" "$(@D)/python_include/pytime.h" && cp -f "/usr/include/python3.4m/rangeobject.h" "$(@D)/python_include/rangeobject.h" && cp -f "/usr/include/python3.4m/setobject.h" "$(@D)/python_include/setobject.h" && cp -f "/usr/include/python3.4m/sliceobject.h" "$(@D)/python_include/sliceobject.h" && cp -f "/usr/include/python3.4m/structmember.h" "$(@D)/python_include/structmember.h" && cp -f "/usr/include/python3.4m/structseq.h" "$(@D)/python_include/structseq.h" && cp -f "/usr/include/python3.4m/symtable.h" "$(@D)/python_include/symtable.h" && cp -f "/usr/include/python3.4m/sysmodule.h" "$(@D)/python_include/sysmodule.h" && cp -f "/usr/include/python3.4m/token.h" "$(@D)/python_include/token.h" && cp -f "/usr/include/python3.4m/traceback.h" "$(@D)/python_include/traceback.h" && cp -f "/usr/include/python3.4m/tupleobject.h" "$(@D)/python_include/tupleobject.h" && cp -f "/usr/include/python3.4m/typeslots.h" "$(@D)/python_include/typeslots.h" && cp -f "/usr/include/python3.4m/ucnhash.h" "$(@D)/python_include/ucnhash.h" && cp -f "/usr/include/python3.4m/unicodeobject.h" "$(@D)/python_include/unicodeobject.h" && cp -f "/usr/include/python3.4m/warnings.h" "$(@D)/python_include/warnings.h" && cp -f "/usr/include/python3.4m/weakrefobject.h" "$(@D)/python_include/weakrefobject.h" """, ) @@ -171,6 +171,6 @@ genrule( "numpy_include/numpy/utils.h", ], cmd = """ -cp "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/__multiarray_api.h" "$(@D)/numpy_include/numpy/__multiarray_api.h" && cp "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/__ufunc_api.h" "$(@D)/numpy_include/numpy/__ufunc_api.h" && cp "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/_neighborhood_iterator_imp.h" "$(@D)/numpy_include/numpy/_neighborhood_iterator_imp.h" && cp "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/_numpyconfig.h" "$(@D)/numpy_include/numpy/_numpyconfig.h" && cp "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/arrayobject.h" "$(@D)/numpy_include/numpy/arrayobject.h" && cp "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/arrayscalars.h" "$(@D)/numpy_include/numpy/arrayscalars.h" && cp "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/halffloat.h" "$(@D)/numpy_include/numpy/halffloat.h" && cp "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/multiarray_api.txt" "$(@D)/numpy_include/numpy/multiarray_api.txt" && cp "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/ndarrayobject.h" "$(@D)/numpy_include/numpy/ndarrayobject.h" && cp "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/ndarraytypes.h" "$(@D)/numpy_include/numpy/ndarraytypes.h" && cp "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/noprefix.h" "$(@D)/numpy_include/numpy/noprefix.h" && cp "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h" "$(@D)/numpy_include/numpy/npy_1_7_deprecated_api.h" && cp "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/npy_3kcompat.h" "$(@D)/numpy_include/numpy/npy_3kcompat.h" && cp "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/npy_common.h" "$(@D)/numpy_include/numpy/npy_common.h" && cp "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/npy_cpu.h" "$(@D)/numpy_include/numpy/npy_cpu.h" && cp "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/npy_endian.h" "$(@D)/numpy_include/numpy/npy_endian.h" && cp "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/npy_interrupt.h" "$(@D)/numpy_include/numpy/npy_interrupt.h" && cp "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/npy_math.h" "$(@D)/numpy_include/numpy/npy_math.h" && cp "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/npy_no_deprecated_api.h" "$(@D)/numpy_include/numpy/npy_no_deprecated_api.h" && cp "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/npy_os.h" "$(@D)/numpy_include/numpy/npy_os.h" && cp "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/numpyconfig.h" "$(@D)/numpy_include/numpy/numpyconfig.h" && cp "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/old_defines.h" "$(@D)/numpy_include/numpy/old_defines.h" && cp "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/oldnumeric.h" "$(@D)/numpy_include/numpy/oldnumeric.h" && cp "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/ufunc_api.txt" "$(@D)/numpy_include/numpy/ufunc_api.txt" && cp "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/ufuncobject.h" "$(@D)/numpy_include/numpy/ufuncobject.h" && cp "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/utils.h" "$(@D)/numpy_include/numpy/utils.h" +cp -f "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/__multiarray_api.h" "$(@D)/numpy_include/numpy/__multiarray_api.h" && cp -f "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/__ufunc_api.h" "$(@D)/numpy_include/numpy/__ufunc_api.h" && cp -f "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/_neighborhood_iterator_imp.h" "$(@D)/numpy_include/numpy/_neighborhood_iterator_imp.h" && cp -f "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/_numpyconfig.h" "$(@D)/numpy_include/numpy/_numpyconfig.h" && cp -f "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/arrayobject.h" "$(@D)/numpy_include/numpy/arrayobject.h" && cp -f "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/arrayscalars.h" "$(@D)/numpy_include/numpy/arrayscalars.h" && cp -f "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/halffloat.h" "$(@D)/numpy_include/numpy/halffloat.h" && cp -f "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/multiarray_api.txt" "$(@D)/numpy_include/numpy/multiarray_api.txt" && cp -f "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/ndarrayobject.h" "$(@D)/numpy_include/numpy/ndarrayobject.h" && cp -f "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/ndarraytypes.h" "$(@D)/numpy_include/numpy/ndarraytypes.h" && cp -f "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/noprefix.h" "$(@D)/numpy_include/numpy/noprefix.h" && cp -f "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h" "$(@D)/numpy_include/numpy/npy_1_7_deprecated_api.h" && cp -f "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/npy_3kcompat.h" "$(@D)/numpy_include/numpy/npy_3kcompat.h" && cp -f "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/npy_common.h" "$(@D)/numpy_include/numpy/npy_common.h" && cp -f "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/npy_cpu.h" "$(@D)/numpy_include/numpy/npy_cpu.h" && cp -f "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/npy_endian.h" "$(@D)/numpy_include/numpy/npy_endian.h" && cp -f "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/npy_interrupt.h" "$(@D)/numpy_include/numpy/npy_interrupt.h" && cp -f "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/npy_math.h" "$(@D)/numpy_include/numpy/npy_math.h" && cp -f "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/npy_no_deprecated_api.h" "$(@D)/numpy_include/numpy/npy_no_deprecated_api.h" && cp -f "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/npy_os.h" "$(@D)/numpy_include/numpy/npy_os.h" && cp -f "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/numpyconfig.h" "$(@D)/numpy_include/numpy/numpyconfig.h" && cp -f "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/old_defines.h" "$(@D)/numpy_include/numpy/old_defines.h" && cp -f "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/oldnumeric.h" "$(@D)/numpy_include/numpy/oldnumeric.h" && cp -f "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/ufunc_api.txt" "$(@D)/numpy_include/numpy/ufunc_api.txt" && cp -f "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/ufuncobject.h" "$(@D)/numpy_include/numpy/ufuncobject.h" && cp -f "/usr/local/lib/python3.4/dist-packages/numpy/core/include/numpy/utils.h" "$(@D)/numpy_include/numpy/utils.h" """, ) diff --git a/third_party/toolchains/preconfig/win_1803/BUILD b/third_party/toolchains/preconfig/win_1803/BUILD index 45209d260d..ac599bc2f3 100644 --- a/third_party/toolchains/preconfig/win_1803/BUILD +++ b/third_party/toolchains/preconfig/win_1803/BUILD @@ -17,7 +17,7 @@ platform( remote_execution_properties = """ properties:{ name:"container-image" - value:"docker://gcr.io/tensorflow-testing/tf-rbe-win@sha256:bd22c6bfff6afc1fa4304ec4411df2410d93645494117585332a4e2258358422" + value:"docker://gcr.io/tensorflow-testing/tf-rbe-win@sha256:fbc5713566011cc27fc3651183a6e7c2fd56fc6f006618c53f8fc71e742feebd" } properties:{ name: "OSFamily" value: "Windows" diff --git a/tools/bazel.rc b/tools/bazel.rc index 8c2052ee8a..1fdf51f53e 100644 --- a/tools/bazel.rc +++ b/tools/bazel.rc @@ -72,6 +72,7 @@ build:nogcp --define=no_gcp_support=true build:nohdfs --define=no_hdfs_support=true build:nokafka --define=no_kafka_support=true build:noignite --define=no_ignite_support=true +build:nonccl --define=no_nccl_support=true build --define=use_fast_cpp_protos=true build --define=allow_oversize_protos=true -- GitLab From 9ab5ff388ea286cea990e29b5c97d6c6e000cac5 Mon Sep 17 00:00:00 2001 From: Steve Nesae Date: Wed, 5 Dec 2018 10:52:12 -0600 Subject: [PATCH 030/622] fixed readme --- .../micro/examples/micro_speech/apollo3/README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/README.md b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/README.md index 967b833501..66fa176a9b 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/README.md +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/README.md @@ -1,7 +1,3 @@ -# TODO - -* preprocessor_cmsis_test_bin - # Description of Apollo3 Makefile targets * **pushbutton_cmsis_speech_test_bin**: -- GitLab From 35c76cde763b7a083e147e38db89404d374b499e Mon Sep 17 00:00:00 2001 From: Steve Nesae Date: Wed, 5 Dec 2018 10:59:13 -0600 Subject: [PATCH 031/622] checking out upstream/master files not captured in previous merge --- .../contrib/tensorrt/convert/convert_graph.cc | 5 +- .../contrib/tensorrt/kernels/trt_engine_op.cc | 5 +- .../contrib/tensorrt/segment/segment.cc | 8 +-- tensorflow/core/kernels/mkl_avgpooling_op.cc | 35 +++++++------ tensorflow/core/kernels/mkl_conv_ops.cc | 52 +++++++++++-------- tensorflow/core/kernels/mkl_maxpooling_op.cc | 34 ++++++------ tensorflow/python/eager/pywrap_tensor.cc | 8 --- tensorflow/python/eager/tensor_test.py | 12 ----- tensorflow/python/keras/BUILD | 37 ------------- tensorflow/python/keras/integration_test.py | 9 ---- .../training/checkpointable/util_test.py | 12 ----- tensorflow/tools/docs/parser.py | 2 +- 12 files changed, 77 insertions(+), 142 deletions(-) diff --git a/tensorflow/contrib/tensorrt/convert/convert_graph.cc b/tensorflow/contrib/tensorrt/convert/convert_graph.cc index 812948bb30..3b32f72bc1 100644 --- a/tensorflow/contrib/tensorrt/convert/convert_graph.cc +++ b/tensorflow/contrib/tensorrt/convert/convert_graph.cc @@ -892,8 +892,9 @@ tensorflow::Status ConvertAfterShapes(ConversionParams& params) { TrtCandidateSelector candidate_selector(*params.graph_properties, params.precision_mode); TF_RETURN_IF_ERROR(tensorrt::segment::SegmentGraph( - &graph, std::bind(&TrtCandidateSelector::IsTensorRTCandidate, - &candidate_selector, std::placeholders::_1), + &graph, + std::bind(&TrtCandidateSelector::IsTensorRTCandidate, &candidate_selector, + std::placeholders::_1), // Input validation is already done by TrtCandidateSelector, so we don't // need to check the input edges. [](const Edge* edge) { return true; }, OutputEdgeValidator(), diff --git a/tensorflow/contrib/tensorrt/kernels/trt_engine_op.cc b/tensorflow/contrib/tensorrt/kernels/trt_engine_op.cc index a5e09f73ac..bad568644b 100644 --- a/tensorflow/contrib/tensorrt/kernels/trt_engine_op.cc +++ b/tensorflow/contrib/tensorrt/kernels/trt_engine_op.cc @@ -252,8 +252,9 @@ int TRTEngineOp::GetEngineBatch(OpKernelContext* ctx) { cached_engine_batches_.push_back(num_batch); VLOG(1) << "Running with batch size " << num_batch; } else { - string msg = StrCat("Engine buffer is full. buffer limit=", - max_cached_engines_, ", current entries="); + string msg = + StrCat("Engine buffer is full. buffer limit=", max_cached_engines_, + ", current entries="); for (auto i : cached_engine_batches_) StrAppend(&msg, i, ","); StrAppend(&msg, " requested batch=", num_batch); LOG(WARNING) << msg; diff --git a/tensorflow/contrib/tensorrt/segment/segment.cc b/tensorflow/contrib/tensorrt/segment/segment.cc index d8f63779e6..6abc5226cc 100644 --- a/tensorflow/contrib/tensorrt/segment/segment.cc +++ b/tensorflow/contrib/tensorrt/segment/segment.cc @@ -460,8 +460,7 @@ tensorflow::Status SegmentGraph( std::vector order; order.reserve(graph->num_node_ids()); StableDFS(*graph, /*reverse=*/false, {graph->source_node()}, - /*enter=*/nullptr, - [&order](const SimpleNode* n) { + /*enter=*/nullptr, [&order](const SimpleNode* n) { order.push_back(n); return true; }); @@ -570,7 +569,7 @@ tensorflow::Status SegmentGraph( std::set& segment_nodes = itr.second; VLOG(1) << "Segment original size: " << segment_nodes.size(); while (true) { - std::deque in_nodes_que, out_nodes_que; + std::deque in_nodes_que, out_nodes_que; // Find an input node that is not eligible and add it to the queue. // Nodes that has no incoming edges should not be treated as "input", // as there are really no inputs to them. Similar for output nodes. @@ -616,7 +615,8 @@ tensorflow::Status SegmentGraph( // their outputs. In this way, for common cases the number of removed // nodes should be minimum. auto remove_nodes = [&segment_nodes]( - bool is_input_nodes, std::deque* que) { + bool is_input_nodes, + std::deque* que) { // Run a BFS on the queue to find all the input/output nodes. std::set visited; std::set logged(que->begin(), que->end()); diff --git a/tensorflow/core/kernels/mkl_avgpooling_op.cc b/tensorflow/core/kernels/mkl_avgpooling_op.cc index 939cbd6f96..28825e1a9c 100644 --- a/tensorflow/core/kernels/mkl_avgpooling_op.cc +++ b/tensorflow/core/kernels/mkl_avgpooling_op.cc @@ -357,10 +357,11 @@ class MklAvgPoolingGradOp : public OpKernel { if (!outbackprop_in_mkl_format) { // For avgpooling, tensor_in_shape should have 1 dimension, and 4 // elements. - OP_REQUIRES(context, tensor_in_shape.dims() == 1 && - tensor_in_shape.NumElements() == 4, - errors::InvalidArgument("original input shape must be " - "1-dimensional and 4 elements")); + OP_REQUIRES( + context, + tensor_in_shape.dims() == 1 && tensor_in_shape.NumElements() == 4, + errors::InvalidArgument("original input shape must be " + "1-dimensional and 4 elements")); // For avgpooling, out_backprop should have 4 dimensions. OP_REQUIRES( @@ -551,9 +552,9 @@ class MklAvgPoolingOp : public MklPoolingForwardOpBase { output_max->flat()(0) = max_input; } } catch (mkldnn::error& e) { - string error_msg = "Status: " + std::to_string(e.status) + ", message: " + - string(e.message) + ", in file " + string(__FILE__) + - ":" + std::to_string(__LINE__); + string error_msg = "Status: " + std::to_string(e.status) + + ", message: " + string(e.message) + ", in file " + + string(__FILE__) + ":" + std::to_string(__LINE__); OP_REQUIRES_OK( context, errors::Aborted("Operation received an exception:", error_msg)); @@ -653,9 +654,9 @@ class MklAvgPoolingGradOp : public MklPoolingBackwardOpBase { // execute pooling op pooling_bwd->Execute(diff_dst_data, diff_src_data); } catch (mkldnn::error& e) { - string error_msg = "Status: " + std::to_string(e.status) + ", message: " + - string(e.message) + ", in file " + string(__FILE__) + - ":" + std::to_string(__LINE__); + string error_msg = "Status: " + std::to_string(e.status) + + ", message: " + string(e.message) + ", in file " + + string(__FILE__) + ":" + std::to_string(__LINE__); OP_REQUIRES_OK(context, errors::Aborted("Compute received an exception:", error_msg)); } @@ -674,13 +675,15 @@ class MklAvgPoolingGradOp : public MklPoolingBackwardOpBase { const MklDnnShape& original_input_mkl_shape, const MklDnnShape& input_gradient_mkl_shape) { if (!original_input_mkl_shape.IsMklTensor()) { - OP_REQUIRES(context, tensor_in_shape.dims() == 1 && - tensor_in_shape.NumElements() == 4, - errors::InvalidArgument("original input shape must be " - "1-dimensional and 4 elements")); + OP_REQUIRES( + context, + tensor_in_shape.dims() == 1 && tensor_in_shape.NumElements() == 4, + errors::InvalidArgument("original input shape must be " + "1-dimensional and 4 elements")); } else { - OP_REQUIRES(context, original_input_mkl_shape.GetDimension() == 1 && - original_input_mkl_shape.DimSize(0) == 4, + OP_REQUIRES(context, + original_input_mkl_shape.GetDimension() == 1 && + original_input_mkl_shape.DimSize(0) == 4, errors::InvalidArgument("original input shape must be " "1-dimensional and 4 elements")); } diff --git a/tensorflow/core/kernels/mkl_conv_ops.cc b/tensorflow/core/kernels/mkl_conv_ops.cc index 75f08956b4..4b0ced3340 100644 --- a/tensorflow/core/kernels/mkl_conv_ops.cc +++ b/tensorflow/core/kernels/mkl_conv_ops.cc @@ -465,18 +465,19 @@ class MklConvOp : public OpKernel { filter.shape().DebugString())); for (int i = 0; i < 3; i++) { - OP_REQUIRES(context, FastBoundsCheck(filter.dim_size(i), - std::numeric_limits::max()), - errors::InvalidArgument("filter too large")); + OP_REQUIRES( + context, + FastBoundsCheck(filter.dim_size(i), std::numeric_limits::max()), + errors::InvalidArgument("filter too large")); } const int64 input_depth = input_in_mkl_format ? GetMklTensorDim(mkl_context.input_shape, 'C') : GetTensorDim(input, data_format_, 'C'); - OP_REQUIRES( - context, input_depth == filter.dim_size(2), - errors::InvalidArgument("input and filter must have the same depth: ", - input_depth, " vs ", filter.dim_size(2))); + OP_REQUIRES(context, input_depth == filter.dim_size(2), + errors::InvalidArgument( + "input and filter must have the same depth: ", input_depth, + " vs ", filter.dim_size(2))); // The last dimension for filter is out_depth. const int out_depth = static_cast(filter.dim_size(3)); @@ -485,9 +486,10 @@ class MklConvOp : public OpKernel { const int64 input_rows_raw = input_in_mkl_format ? GetMklTensorDim(mkl_context.input_shape, 'H') : GetTensorDim(input, data_format_, 'H'); - OP_REQUIRES(context, FastBoundsCheck(input_rows_raw, - std::numeric_limits::max()), - errors::InvalidArgument("Input rows too large")); + OP_REQUIRES( + context, + FastBoundsCheck(input_rows_raw, std::numeric_limits::max()), + errors::InvalidArgument("Input rows too large")); const int input_rows = static_cast(input_rows_raw); const int filter_rows = static_cast(filter.dim_size(0)); @@ -496,9 +498,10 @@ class MklConvOp : public OpKernel { const int64 input_cols_raw = input_in_mkl_format ? GetMklTensorDim(mkl_context.input_shape, 'W') : GetTensorDim(input, data_format_, 'W'); - OP_REQUIRES(context, FastBoundsCheck(input_cols_raw, - std::numeric_limits::max()), - errors::InvalidArgument("Input cols too large")); + OP_REQUIRES( + context, + FastBoundsCheck(input_cols_raw, std::numeric_limits::max()), + errors::InvalidArgument("Input cols too large")); const int input_cols = static_cast(input_cols_raw); const int filter_cols = static_cast(filter.dim_size(1)); @@ -506,9 +509,10 @@ class MklConvOp : public OpKernel { const int64 input_batch_raw = input_in_mkl_format ? GetMklTensorDim(mkl_context.input_shape, 'N') : GetTensorDim(input, data_format_, 'N'); - OP_REQUIRES(context, FastBoundsCheck(input_batch_raw, - std::numeric_limits::max()), - errors::InvalidArgument("batch is too large")); + OP_REQUIRES( + context, + FastBoundsCheck(input_batch_raw, std::numeric_limits::max()), + errors::InvalidArgument("batch is too large")); const int batch = static_cast(input_batch_raw); // For now we take the stride from the second and third dimensions only (we @@ -889,15 +893,17 @@ class MklConvOp : public OpKernel { OP_REQUIRES(context, dilations_.size() == 5, errors::InvalidArgument("Dilation rates field must " "specify 5 dimensions")); - OP_REQUIRES(context, (GetTensorDim(dilations_, data_format_, 'N') == 1 && - GetTensorDim(dilations_, data_format_, 'C') == 1), + OP_REQUIRES(context, + (GetTensorDim(dilations_, data_format_, 'N') == 1 && + GetTensorDim(dilations_, data_format_, 'C') == 1), errors::InvalidArgument( "Current implementation does not yet support " "dilations rates in the batch and depth dimensions.")); OP_REQUIRES( - context, (GetTensorDim(dilations_, data_format_, '0') > 0 && - GetTensorDim(dilations_, data_format_, '1') > 0 && - GetTensorDim(dilations_, data_format_, '2') > 0), + context, + (GetTensorDim(dilations_, data_format_, '0') > 0 && + GetTensorDim(dilations_, data_format_, '1') > 0 && + GetTensorDim(dilations_, data_format_, '2') > 0), errors::InvalidArgument("Dilated rates should be larger than 0.")); } } @@ -1533,8 +1539,8 @@ class MklQuantizedConv2DSumReluOp const float max_filter = context->input(5 + bias_index_offset).flat()(0); - reorder_sum_scale = - 255.0 * 127.0 / (std::max(std::abs(max_input), std::abs(min_input)) * + reorder_sum_scale = 255.0 * 127.0 / + (std::max(std::abs(max_input), std::abs(min_input)) * std::max(std::abs(max_filter), std::abs(min_filter))); std::vector scales; scales.push_back(reorder_sum_scale); diff --git a/tensorflow/core/kernels/mkl_maxpooling_op.cc b/tensorflow/core/kernels/mkl_maxpooling_op.cc index 0697251c7d..cb494f6c3e 100644 --- a/tensorflow/core/kernels/mkl_maxpooling_op.cc +++ b/tensorflow/core/kernels/mkl_maxpooling_op.cc @@ -399,18 +399,19 @@ class MklMaxPoolingGradOp : public OpKernel { if (workspace_enabled == false) { if (convert_input != nullptr) { if (input_in_mkl_format == false) { - CHECK_EQ( - dnnConversionExecute_F32( - convert_input, const_cast(static_cast( - tensor_in.flat().data())), - input_buf), - E_SUCCESS); + CHECK_EQ(dnnConversionExecute_F32( + convert_input, + const_cast(static_cast( + tensor_in.flat().data())), + input_buf), + E_SUCCESS); CHECK_EQ(dnnDelete_F32(convert_input), E_SUCCESS); convert_input = nullptr; } else { input_shape.GetConvertedFlatData( - lt_input_prim, const_cast(static_cast( - tensor_in.flat().data())), + lt_input_prim, + const_cast( + static_cast(tensor_in.flat().data())), input_buf); } pooling_resfwd[dnnResourceSrc] = input_buf; @@ -455,8 +456,9 @@ class MklMaxPoolingGradOp : public OpKernel { CHECK_EQ(dnnDelete_F32(convert_outbackprop), E_SUCCESS); } else { output_backprop_shape.GetConvertedFlatData( - lt_outbackprop_prim, const_cast(static_cast( - out_backprop.flat().data())), + lt_outbackprop_prim, + const_cast( + static_cast(out_backprop.flat().data())), outbackprop_buf); } pooling_res[dnnResourceDiffDst] = outbackprop_buf; @@ -637,9 +639,9 @@ class MklMaxPoolingOp : public MklPoolingForwardOpBase { pooling_fwd->Execute(src_data, dst_data, ws_data); } } catch (mkldnn::error& e) { - string error_msg = "Status: " + std::to_string(e.status) + ", message: " + - string(e.message) + ", in file " + string(__FILE__) + - ":" + std::to_string(__LINE__); + string error_msg = "Status: " + std::to_string(e.status) + + ", message: " + string(e.message) + ", in file " + + string(__FILE__) + ":" + std::to_string(__LINE__); OP_REQUIRES_OK(context, errors::Aborted("Compute received an exception:", error_msg)); } @@ -781,9 +783,9 @@ class MklMaxPoolingGradOp : public MklPoolingBackwardOpBase { // execute pooling pooling_bwd->Execute(diff_dst_data, diff_src_data, ws_data); } catch (mkldnn::error& e) { - string error_msg = "Status:" + std::to_string(e.status) + ", message: " + - string(e.message) + ". in file " + string(__FILE__) + - ":" + std::to_string(__LINE__); + string error_msg = "Status:" + std::to_string(e.status) + + ", message: " + string(e.message) + ". in file " + + string(__FILE__) + ":" + std::to_string(__LINE__); OP_REQUIRES_OK(context, errors::Aborted("Compute received an exception:", error_msg)); } diff --git a/tensorflow/python/eager/pywrap_tensor.cc b/tensorflow/python/eager/pywrap_tensor.cc index 206b96eef6..0d0f70d543 100644 --- a/tensorflow/python/eager/pywrap_tensor.cc +++ b/tensorflow/python/eager/pywrap_tensor.cc @@ -220,14 +220,6 @@ TFE_TensorHandle* ConvertToEagerTensor(PyObject* value, PyObject* dtype) { return nullptr; } } - tensorflow::Safe_PyObjectPtr value_decrefer; - if (PyArray_CheckAnyScalarExact(value)) { - // Convert numpy scalars to numpy arrays. - value = PyArray_FromScalar(value, nullptr); - // The returned value needs to be DECREF'd, but the original value was - // created in python code, and doesn't need to be DECREF'd. - value_decrefer.reset(value); - } if (PyArray_Check(value)) { int desired_np_dtype = -1; if (desired_dtype >= 0) { diff --git a/tensorflow/python/eager/tensor_test.py b/tensorflow/python/eager/tensor_test.py index 0ee2ff68c2..25442ff048 100644 --- a/tensorflow/python/eager/tensor_test.py +++ b/tensorflow/python/eager/tensor_test.py @@ -95,18 +95,6 @@ class TFETensorTest(test_util.TensorFlowTestCase): t = _create_tensor(values) self.assertAllEqual(values, t) - @test_util.assert_no_new_pyobjects_executing_eagerly - def testNumpyDtypeSurvivesThroughTensorConversion(self): - scalar_creators = [np.int32, np.int64, np.float32, np.float64] - conversion_functions = [ops.convert_to_tensor, constant_op.constant] - - for scalar_creator in scalar_creators: - for conversion_function in conversion_functions: - np_val = scalar_creator(3) - tensor_val = conversion_function(np_val) - self.assertEqual(tensor_val.numpy().dtype, np_val.dtype) - self.assertEqual(tensor_val.numpy(), np_val) - def testNumpyValueWithCast(self): values = np.array([3.0], dtype=np.float32) t = _create_tensor(values, dtype=dtypes.float64) diff --git a/tensorflow/python/keras/BUILD b/tensorflow/python/keras/BUILD index 3a27f75f4a..602cf79362 100755 --- a/tensorflow/python/keras/BUILD +++ b/tensorflow/python/keras/BUILD @@ -548,19 +548,6 @@ cuda_py_test( shard_count = 4, ) -cuda_py_test( - name = "unified_lstm_test", - size = "medium", - srcs = ["layers/unified_lstm_test.py"], - additional_deps = [ - ":keras", - "@absl_py//absl/testing:parameterized", - "//third_party/py/numpy", - "//tensorflow/python:client_testlib", - ], - shard_count = 4, -) - py_test( name = "serialization_test", size = "small", @@ -648,17 +635,6 @@ py_test( ], ) -py_test( - name = "tf_utils_test", - size = "small", - srcs = ["utils/tf_utils_test.py"], - srcs_version = "PY2AND3", - deps = [ - ":keras", - "//tensorflow/python:client_testlib", - ], -) - py_test( name = "io_utils_test", size = "small", @@ -811,19 +787,6 @@ py_test( ], ) -py_test( - name = "training_dataset_test", - size = "medium", - srcs = ["engine/training_dataset_test.py"], - srcs_version = "PY2AND3", - deps = [ - ":keras", - "//tensorflow/python:client_testlib", - "//third_party/py/numpy", - "@absl_py//absl/testing:parameterized", - ], -) - py_test( name = "training_generator_test", size = "enormous", diff --git a/tensorflow/python/keras/integration_test.py b/tensorflow/python/keras/integration_test.py index 23f5438505..f1a0932613 100644 --- a/tensorflow/python/keras/integration_test.py +++ b/tensorflow/python/keras/integration_test.py @@ -320,15 +320,6 @@ class KerasIntegrationTest(test.TestCase): verbose=0) self.assertGreater(history.history['val_acc'][-1], 0.7) - def test_regularizers_with_get_variable(self): - # Test case for GitHub issue 22470. - with self.cached_session(): - v = variable_scope.get_variable( - "v", - shape=[4, 4], - initializer=keras.initializers.glorot_uniform(), - regularizer=keras.regularizers.l2(0.)) - if __name__ == '__main__': test.main() diff --git a/tensorflow/python/training/checkpointable/util_test.py b/tensorflow/python/training/checkpointable/util_test.py index de9cac0863..78047eda90 100644 --- a/tensorflow/python/training/checkpointable/util_test.py +++ b/tensorflow/python/training/checkpointable/util_test.py @@ -44,7 +44,6 @@ from tensorflow.python.ops import variable_scope from tensorflow.python.ops import variables from tensorflow.python.training import adam from tensorflow.python.training import checkpoint_management -from tensorflow.python.training import momentum from tensorflow.python.training import saver as saver_lib from tensorflow.python.training import training_util from tensorflow.python.training.checkpointable import base @@ -199,17 +198,6 @@ class InterfaceTests(test.TestCase): with self.assertRaises(NotImplementedError): checkpoint_reversed.save(prefix) - @test_util.run_in_graph_and_eager_modes(assert_no_eager_garbage=True) - def test_object_graph_no_attributes(self): - root = tracking.Checkpointable() - root.v = resource_variable_ops.ResourceVariable(1.) - root.opt = momentum.MomentumOptimizer(0.01, 0.5) - root.opt.minimize(root.v.read_value) - object_graph = checkpointable_utils.make_object_graph_without_attributes( - root) - # Four objects: Root, v, opt, and a slot variable for v - self.assertEqual(4, len(object_graph.nodes)) - class _MirroringSaveable(saver_lib.BaseSaverBuilder.SaveableObject): diff --git a/tensorflow/tools/docs/parser.py b/tensorflow/tools/docs/parser.py index 6dc18ee8dc..83b4bf8128 100644 --- a/tensorflow/tools/docs/parser.py +++ b/tensorflow/tools/docs/parser.py @@ -39,7 +39,7 @@ def is_free_function(py_object, full_name, index): """Check if input is a free function (and not a class- or static method). Args: - py_object: The object in question. + py_object: The the object in question. full_name: The full name of the object, like `tf.module.symbol`. index: The {full_name:py_object} dictionary for the public API. -- GitLab From c506f4befdde4fbb1259bbf326336aa74f835191 Mon Sep 17 00:00:00 2001 From: Steve Nesae Date: Wed, 5 Dec 2018 11:54:44 -0600 Subject: [PATCH 032/622] prep for pull request --- .../micro/examples/micro_speech/preprocessor_test.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor_test.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor_test.cc index d9b0c48ba3..e8b49f67e3 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor_test.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor_test.cc @@ -22,7 +22,6 @@ limitations under the License. #include "tensorflow/lite/experimental/micro/micro_error_reporter.h" #include "tensorflow/lite/experimental/micro/testing/micro_test.h" - TF_LITE_MICRO_TESTS_BEGIN TF_LITE_MICRO_TEST(TestPreprocessor) { -- GitLab From bb804ddf78cd7583e6dad16e93611a73f40a3a2e Mon Sep 17 00:00:00 2001 From: Steve Nesae Date: Wed, 5 Dec 2018 12:00:48 -0600 Subject: [PATCH 033/622] prep for pull request --- tensorflow/lite/experimental/micro/tools/make/Makefile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tensorflow/lite/experimental/micro/tools/make/Makefile b/tensorflow/lite/experimental/micro/tools/make/Makefile index 3f6f927358..30a51899e5 100644 --- a/tensorflow/lite/experimental/micro/tools/make/Makefile +++ b/tensorflow/lite/experimental/micro/tools/make/Makefile @@ -77,9 +77,7 @@ tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor_test.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/no_30ms_sample_data.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/yes_30ms_sample_data.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/no_power_spectrum_data.cc \ -tensorflow/lite/experimental/micro/examples/micro_speech/yes_power_spectrum_data.cc \ -tensorflow/lite/experimental/micro/examples/micro_speech/yes_waveform.cc \ -tensorflow/lite/experimental/micro/examples/micro_speech/yes_features_data.cc +tensorflow/lite/experimental/micro/examples/micro_speech/yes_power_spectrum_data.cc PREPROCESSOR_REFERENCE_TEST_SRCS = \ $(PREPROCESSOR_TEST_SRCS) \ -- GitLab From 5a2d4b053ed368c713e83121c73bd251e85300b9 Mon Sep 17 00:00:00 2001 From: Steve Nesae Date: Thu, 6 Dec 2018 14:59:10 -0600 Subject: [PATCH 034/622] Added pushbutton sources to makefile --- .../experimental/micro/tools/make/Makefile | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tensorflow/lite/experimental/micro/tools/make/Makefile b/tensorflow/lite/experimental/micro/tools/make/Makefile index 30a51899e5..63bbdc84ba 100644 --- a/tensorflow/lite/experimental/micro/tools/make/Makefile +++ b/tensorflow/lite/experimental/micro/tools/make/Makefile @@ -115,6 +115,8 @@ include $(wildcard $(MAKEFILE_DIR)/targets/*_makefile.inc) ALL_SRCS := \ $(MICRO_SPEECH_TEST_SRCS) \ + $(PUSHBUTTON_MICRO_SPEECH_TEST_SRCS) \ + $(PUSHBUTTON_CMSIS_SPEECH_TEST_SRCS) \ $(PREPROCESSOR_REFERENCE_TEST_SRCS) \ $(PREPROCESSOR_FIXED_TEST_SRCS) \ $(MICROLITE_CC_SRCS) \ @@ -145,6 +147,13 @@ AR := $(CC_PREFIX)${TARGET_TOOLCHAIN_PREFIX}ar MICRO_SPEECH_TEST_OBJS := $(addprefix $(OBJDIR), \ $(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(MICRO_SPEECH_TEST_SRCS)))) +PUSHBUTTON_MICRO_SPEECH_TEST_OBJS := $(addprefix $(OBJDIR), \ +$(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(PUSHBUTTON_MICRO_SPEECH_TEST_SRCS)))) + +PUSHBUTTON_CMSIS_SPEECH_TEST_OBJS := $(addprefix $(OBJDIR), \ +$(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(PUSHBUTTON_CMSIS_SPEECH_TEST_SRCS))) \ +arm_bitreversal2.o) + PREPROCESSOR_REFERENCE_TEST_OBJS := $(addprefix $(OBJDIR), \ $(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(PREPROCESSOR_REFERENCE_TEST_SRCS)))) @@ -200,6 +209,31 @@ micro_speech_test_bin: $(MICRO_SPEECH_TEST_BINARY).bin test_micro_speech: $(MICRO_SPEECH_TEST_BINARY) $(TEST_SCRIPT) $(MICRO_SPEECH_TEST_BINARY) '~~~ALL TESTS PASSED~~~' +$(PUSHBUTTON_MICRO_SPEECH_TEST_BINARY): $(PUSHBUTTON_MICRO_SPEECH_TEST_OBJS) $(MICROLITE_LIB_PATH) + @mkdir -p $(dir $@) + $(CXX) $(CXXFLAGS) $(INCLUDES) \ + -o $(PUSHBUTTON_MICRO_SPEECH_TEST_BINARY) $(PUSHBUTTON_MICRO_SPEECH_TEST_OBJS) \ + $(LIBFLAGS) $(MICROLITE_LIB_PATH) $(LDFLAGS) $(MICROLITE_LIBS) + +pushbutton_micro_speech_test: $(PUSHBUTTON_MICRO_SPEECH_TEST_BINARY) +pushbutton_micro_speech_test_bin: $(PUSHBUTTON_MICRO_SPEECH_TEST_BINARY).bin + +test_pushbutton_micro_speech: $(PUSHBUTTON_MICRO_SPEECH_TEST_BINARY) + $(TEST_SCRIPT) $(PUSHBUTTON_MICRO_SPEECH_TEST_BINARY) '~~~ALL TESTS PASSED~~~' + + +$(PUSHBUTTON_CMSIS_SPEECH_TEST_BINARY): $(PUSHBUTTON_CMSIS_SPEECH_TEST_OBJS) $(MICROLITE_LIB_PATH) + @mkdir -p $(dir $@) + $(CXX) $(CXXFLAGS) $(INCLUDES) \ + -o $(PUSHBUTTON_CMSIS_SPEECH_TEST_BINARY) $(PUSHBUTTON_CMSIS_SPEECH_TEST_OBJS) \ + $(LIBFLAGS) $(MICROLITE_LIB_PATH) $(LDFLAGS) $(MICROLITE_LIBS) + +pushbutton_cmsis_speech_test: $(PUSHBUTTON_CMSIS_SPEECH_TEST_BINARY) +pushbutton_cmsis_speech_test_bin: $(PUSHBUTTON_CMSIS_SPEECH_TEST_BINARY).bin + +test_pushbutton_cmsis_speech: $(PUSHBUTTON_CMSIS_SPEECH_TEST_BINARY) + $(TEST_SCRIPT) $(PUSHBUTTON_CMSIS_SPEECH_TEST_BINARY) '~~~ALL TESTS PASSED~~~' + $(PREPROCESSOR_REFERENCE_TEST_BINARY): $(PREPROCESSOR_REFERENCE_TEST_OBJS) $(MICROLITE_LIB_PATH) @mkdir -p $(dir $@) $(CXX) $(CXXFLAGS) $(INCLUDES) \ -- GitLab From fc19d28247a87dbce66993e2effd2e9d0ac3652d Mon Sep 17 00:00:00 2001 From: hari Date: Fri, 7 Dec 2018 10:38:01 -0800 Subject: [PATCH 035/622] New files for Tensorflow Lite Micro Speech Example targetting the Eta Compute ECM3531 evaluation board --- .../micro/testing/test_ecm3531_binary.sh | 1 + .../tools/make/targets/ecm3531/README.txt | 32 + .../micro/tools/make/targets/ecm3531/_main.c | 97 +++ .../tools/make/targets/ecm3531/ecm3531.lds | 88 +++ .../tools/make/targets/ecm3531/load_program | 35 ++ .../tools/make/targets/ecm3531/startup.c | 592 ++++++++++++++++++ .../tools/make/targets/ecm3531_makefile.inc | 103 +++ 7 files changed, 948 insertions(+) create mode 100755 tensorflow/lite/experimental/micro/testing/test_ecm3531_binary.sh create mode 100644 tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/README.txt create mode 100644 tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/_main.c create mode 100644 tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/ecm3531.lds create mode 100755 tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/load_program create mode 100644 tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/startup.c create mode 100644 tensorflow/lite/experimental/micro/tools/make/targets/ecm3531_makefile.inc diff --git a/tensorflow/lite/experimental/micro/testing/test_ecm3531_binary.sh b/tensorflow/lite/experimental/micro/testing/test_ecm3531_binary.sh new file mode 100755 index 0000000000..6d7af08d9f --- /dev/null +++ b/tensorflow/lite/experimental/micro/testing/test_ecm3531_binary.sh @@ -0,0 +1 @@ +echo "Testing should be performed on the ECM3531 EVB" diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/README.txt b/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/README.txt new file mode 100644 index 0000000000..655340ceff --- /dev/null +++ b/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/README.txt @@ -0,0 +1,32 @@ +Running The Micro Speech Example On Eta Compute's ECM3531EVB + +This code will enable you to compile and execute the Tensorflow Lite Micro Speech Example on Eta Computes's low power ECM3531 chip. + + +GETTING STARTED: + +1. Download the Tensorflow code from Github and follow anyinstructions there to download other dependencies. + +2. Download the Eta Compute SDK, version 0.0.17. + +3. Install the Arm compiler arm-none-eabi-gcc, version = arm-none-eabi-gcc (GNU Tools for Arm Embedded Processors 7-2018-q2-update) 7.3.1 20180622 (release) [ARM/embedded-7-branch revision 261907] + +4. Edit the file tensorflow/lite/experimental/micro/tools/make/targets/ecm3531_makefile.inc so that the variable ETA_SDK points to the location where the Eta Compute SDK is installed, and the variable GCC_ARM points to the Arm compiler. + +5. Compile the code with the command "make -f tensorflow/lite/experimental/micro/tools/make/Makefile TARGET=ecm3531 test". This will create the executable tensorflow/lite/experimental/micro/tools/make/gen/ecm3531_cortex-m3/bin/micro_speech_test. + +6. Connect the board to the host computer, start PuTTY (Connection type = Serial, Speed = 11520, Data bits = 8, Stop bits = 1, Parity = None), and load the executable with ocd. A sample script for loading the image is provided in tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/load_program. + +The following will be printed on the Uart: + +Testing TestInvoke +Ran successfully + +/ tests passed +~~~ALL TESTS PASSED~~~ + + + +CONTACT INFORMATION: + +Contact info@etacompute.com for more information on obtaining the Eta Compute SDK and evalution board. diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/_main.c b/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/_main.c new file mode 100644 index 0000000000..5a595c26b3 --- /dev/null +++ b/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/_main.c @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (C) 2018 Eta Compute, Inc + * + * 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 +#include +#include "eta_chip.h" +#include "eta_csp.h" +#include "eta_csp_uart.h" +#include "eta_csp_gpio.h" +#include "eta_csp_io.h" +#include "eta_csp_timer.h" +#include "eta_csp_socctrl.h" +#include "eta_csp_buck.h" +#include "eta_csp_rtc.h" +#include "eta_csp_sys_clock.h" +#include +#include "eta_bsp.h" +#include "eta_csp_pwr.h" +#include + + +tUart g_sUart0 = {eUartNum0, eUartBaud115200}; +tUart g_sUart1 = {eUartNum1, eUartBaud115200}; + +int init_main(int); +void EtaPrintExecutionTime(uint64_t); + + +//***************************************************************************** +// +// The entry point for the application. +// +//***************************************************************************** +extern int main(int argc, char**argv); + +void DebugLog(const char* s) { EtaCspIoPrintf( "%s", s); } +void DebugLogInt32(int32_t i) { EtaCspIoPrintf( "%d", i); } +void DebugLogUInt32(uint32_t i) { EtaCspIoPrintf( "%d", i); } +void DebugLogHex(uint32_t i) { EtaCspIoPrintf( "0x%8x", i); } +void DebugLogFloat(float i) { EtaCspIoPrintf( "%f", i); } + + +int _main(void) +{ + uint64_t time_ms; + + EtaCspInit(); //initialize csp registers + EtaCspGpioInit(); //initialize gpios + EtaCspUartInit(&g_sUart1, eUartNum0, eUartBaud115200, eUartFlowControlHardware); //initialize Uart + EtaCspBuckInit(ETA_BSP_VDD_IO_SETTING, eBuckAo600Mv, eBuckM3Frequency60Mhz, eBuckMemVoltage900Mv);//set M3 freq + EtaCspTimerInitMs(); //start timer + main(0, NULL); //Call to Tensorflow; this will print if test was successful. + time_ms = EtaCspTimerCountGetMs(); //read time + EtaPrintExecutionTime(time_ms); //print execution time + +} + + + +void EtaPrintExecutionTime(uint64_t time_ms) +{ + uint8_t c; + int k1; + char time_string[] = "00000"; + + EtaCspIoPrintf("Execution time (msec) = "); + if (time_ms < 100000) //Convert time to a string + { + for(k1 = 0; k1 < 5; k1++){ + c = time_ms % 10; + time_ms = time_ms/10; + time_string[k1] = (char)(0x30 + c); + } + for(k1 = 4; k1 > 0; k1--) { //print out 1 char at a time + EtaCspUartPutc(&g_sUart1, time_string[k1]); + } + } + else{ + EtaCspIoPrintf("Execution time exceeds 100 sec\n"); + } + EtaCspIoPrintf("\n\n"); + +} diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/ecm3531.lds b/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/ecm3531.lds new file mode 100644 index 0000000000..2c77ebcc1d --- /dev/null +++ b/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/ecm3531.lds @@ -0,0 +1,88 @@ +/******************************************************************************* + * + * Copyright (C) 2018 Eta Compute, Inc + * + * 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. + * + ******************************************************************************/ + + +/* + * linker script for use with ECM3531 + * All sections must map to 128KBytes of SRAM beginning at 0x10000000 + * + */ + + /* + * Indicate to the linker the entry point. + */ +ENTRY(ResetISR) + +/* + * SRAM is at 0x10000000 of length 0x00020000 + */ +MEMORY +{ + SRAM (RWX) : ORIGIN = 0x10000000, LENGTH = 0x00020000 +} + +SECTIONS +{ + .text : + { + _text = .; + KEEP(*(.vectors)) + . = ALIGN(0x4); + *(.text*) + . = ALIGN(0x4); + *(.rodata*) + . = ALIGN(0x4); + _etext = .; + } > SRAM= 0 + .dummy : + { + . = ALIGN(0x4); + _eftext = .; + } > SRAM + .datax : + { + _datax = .; + KEEP(*(.mainStack)) + . += 12288; + _edatax = .; + _stack_top = .; + . += 4; + } > SRAM + .data : + AT (ADDR(.text) + SIZEOF(.text) ) + { + _data = .; + *(.data*) + KEEP(*(.mainHeap)) + _edata = .; + } > SRAM + + .bss : + { + _bss = .; + *(.bss*) + *(COMMON) + _ebss = .; + } > SRAM + .ARM.exidx : + { + *(.ARM.exidx*) + } + +} + diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/load_program b/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/load_program new file mode 100755 index 0000000000..5d4974ba6e --- /dev/null +++ b/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/load_program @@ -0,0 +1,35 @@ +#!/usr/bin/python3 + +import os +import telnetlib + +def send_ocd_cmd(line): + ocd_sock.write(bytes(line,encoding = 'utf-8')) + print(ocd_sock.read_until(b'> ').decode('utf-8'), end='') + +def get_ocd_response(): + print(ocd_sock.read_until(b'> ').decode('utf-8'), end='') + +#get hooked up to openocd daemon +ocd_sock = telnetlib.Telnet(host='localhost', port=4444) +get_ocd_response() # clean it out + +# git path to project elf file +cur_dir = os.getcwd() +elf_file = cur_dir + '/../../gen/ecm3531_cortex-m3/bin/' + 'micro_speech_test' +print("elf_file = ",elf_file) + + +# use these to download and run the elf fle +ocd_commands = ["halt\n", + "load_image {}\n".format(elf_file), + "mww 0x1001FFF8 0xDEADBEEF\n", + "mww 0x1001FFFC 0xC369A517\n", + "reset\n"] + +# OK now do what we came here for!!! +for x in ocd_commands: + print(x) + send_ocd_cmd(x) + + diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/startup.c b/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/startup.c new file mode 100644 index 0000000000..da9f7a4b0e --- /dev/null +++ b/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/startup.c @@ -0,0 +1,592 @@ +/******************************************************************************* + * + * Copyright (C) 2018 Eta Compute, Inc + * + * 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 +#include "eta_chip.h" +#include "memio.h" + + +//#pragma GCC optimize ("align-functions=16") + + +#ifndef NULL +#define NULL (0) +#endif + + +//***************************************************************************** +// +// Macro for hardware access, both direct and via the bit-band region. +// +//***************************************************************************** + +#define NAKED +//#define NAKED __attribute__((naked)) + +int _main(int argc, char *argv[]); +void set_vtor(void); +void * startup_get_my_pc(void); + +//***************************************************************************** +// Forward DECLS for interrupt service routines (ISR) +//***************************************************************************** +extern void ResetISR(void) __attribute__((weak, alias("default_ResetISR"))); +extern void NmiSR(void) __attribute__((weak, alias("default_NmiSR"))); +extern void FaultISR(void) __attribute__((weak, alias("default_FaultISR"))); + +extern void DebugMonitor_ISR(void) __attribute__((weak, alias("default_DebugMonitor_ISR"))); +extern void SVCall_ISR(void) __attribute__((weak, alias("default_SVCall_ISR"))); +extern void PENDSV_ISR(void) __attribute__((weak, alias("default_PENDSV_ISR"))); + +extern void SYSTICK_ISR(void) __attribute__((weak, alias("default_SYSTICK_ISR"))); + +extern void GPIO0_ISR(void) __attribute__((weak, alias("default_GPIO0_ISR"))); +extern void GPIO1_ISR(void) __attribute__((weak, alias("default_GPIO1_ISR"))); +extern void TIMER0_ISR(void) __attribute__((weak, alias("default_TIMER0_ISR"))); +extern void TIMER1_ISR(void) __attribute__((weak, alias("default_TIMER1_ISR"))); +extern void UART0_ISR(void) __attribute__((weak, alias("default_UART0_ISR"))); +extern void UART1_ISR(void) __attribute__((weak, alias("default_UART1_ISR"))); +extern void SPI0_ISR(void) __attribute__((weak, alias("default_SPI0_ISR"))); +extern void SPI1_ISR(void) __attribute__((weak, alias("default_SPI1_ISR"))); +extern void I2C0_ISR(void) __attribute__((weak, alias("default_I2C0_ISR"))); +extern void I2C1_ISR(void) __attribute__((weak, alias("default_I2C1_ISR"))); +extern void RTC0_ISR(void) __attribute__((weak, alias("default_RTC0_ISR"))); +extern void RTC1_ISR(void) __attribute__((weak, alias("default_RTC1_ISR"))); +extern void DSP_ISR(void) __attribute__((weak, alias("default_DSP_ISR"))); +extern void ADC_ISR(void) __attribute__((weak, alias("default_ADC_ISR"))); +extern void SW0_ISR(void) __attribute__((weak, alias("default_SW0_ISR"))); +extern void SW1_ISR(void) __attribute__((weak, alias("default_SW1_ISR"))); +extern void PWM_ISR(void) __attribute__((weak, alias("default_PWM_ISR"))); +extern void WDT_ISR(void) __attribute__((weak, alias("default_WDT_ISR"))); +extern void RTC_TMR_ISR(void) __attribute__((weak, alias("default_RTC_TMR_ISR"))); + + +extern void SW2_ISR(void) __attribute__((weak, alias("default_SW1_ISR"))); +extern void SW3_ISR(void) __attribute__((weak, alias("default_SW1_ISR"))); +extern void SW4_ISR(void) __attribute__((weak, alias("default_SW1_ISR"))); +extern void SW5_ISR(void) __attribute__((weak, alias("default_SW1_ISR"))); +extern void SW6_ISR(void) __attribute__((weak, alias("default_SW1_ISR"))); + + +extern void IntDefaultHandler(void) __attribute__((weak)) NAKED; + +//***************************************************************************** +// +// Reserve space for the system stack. +// +//***************************************************************************** +extern uint32_t _stack_top; +//__attribute__ ((section(".mainStack"), used)) +//static uint32_t pui32Stack[2048]; +#define STARTUP_STACK_TOP (&_stack_top) + + + +//***************************************************************************** +// VECTOR TABLE +//***************************************************************************** +__attribute__ ((section(".vectors"), used)) +void (* const gVectors[])(void) = +{ + //(void (*)(void))((uint32_t)pui32Stack + sizeof(pui32Stack)), // Stack pointer + (void *)STARTUP_STACK_TOP, + ResetISR, // Reset handler + NmiSR, // The NMI handler + FaultISR, // The hard fault handler + IntDefaultHandler, // 4 The MPU fault handler + IntDefaultHandler, // 5 The bus fault handler + IntDefaultHandler, // 6 The usage fault handler + 0, // 7 Reserved + 0, // 8 Reserved + 0, // 9 Reserved + 0, // 10 Reserved + SVCall_ISR, // 11 SVCall handler + DebugMonitor_ISR, // 12 Debug monitor handler + 0, // 13 Reserved + PENDSV_ISR, // 14 The PendSV handler + SYSTICK_ISR, // 15 The SysTick handler + + // external interrupt service routines (ISR) + GPIO0_ISR, // 16 GPIO Port A [ 0] + GPIO1_ISR, // 17 GPIO Port B [ 1] + TIMER0_ISR, // 18 Timer 0 [ 2] + TIMER1_ISR, // 19 Timer 1 [ 3] + UART0_ISR, // 20 UART 0 [ 4] + UART1_ISR, // 21 UART 1 [ 5] + SPI0_ISR, // 22 SPI0 [ 6] + SPI1_ISR, // 23 SPI1 [ 7] + I2C0_ISR, // 24 I2C 0 [ 8] + I2C1_ISR, // 25 I2C 1 [ 9] + RTC0_ISR, // 26 RTC 0 [10] + RTC1_ISR, // 27 RTC 1 [11] + DSP_ISR, // 28 DSP MAILBOX [12] + ADC_ISR, // 29 ADC [13] + PWM_ISR, // 32 PWM [14] + WDT_ISR, // 33 WDT [15] + RTC_TMR_ISR, // 34 RTC [16] + + SW0_ISR, // 30 Software Interrupt 0 [17] + SW1_ISR, // 31 Software Interrupt 1 [18] + SW2_ISR, // 35 Software Interrupt 2 [19] + SW3_ISR, // 36 Software Interrupt 3 [20] + SW4_ISR, // 37 Software Interrupt 4 [21] + SW5_ISR, // 38 Software Interrupt 5 [22] + SW6_ISR, // 39 Software Interrupt 6 [23] + + + +}; + +//***************************************************************************** +// +// The following are constructs created by the linker, indicating where the +// the "data" and "bss" segments reside in memory. The initializers for the +// for the "data" segment resides immediately following the "text" segment. +// +//***************************************************************************** +extern uint32_t _etext; +extern uint32_t _eftext; +extern uint32_t _data; +extern uint32_t _edata; +extern uint32_t _bss; +extern uint32_t _ebss; + +// +// And here are the weak interrupt handlers. +// +void +default_NmiSR (void) +{ + __asm( + " movs r0, #2"); + while(1) + { + } +} + + +void +default_FaultISR (void) +{ + __asm( + " movs r0, #3"); + MEMIO32(0x1001FFF0) = 0xbad0beef; // near the top of 128KB of SRAM + MEMIO32(0x1001FFF4) = 0xbad1beef; // near the top of 128KB of SRAM + while(1) + { + __asm( + " BKPT #1"); + } +} + + +void +IntDefaultHandler (void) +{ + __asm( + " movs r0, #20"); + while(1) + { + __asm( + " BKPT #1"); + } +} + + +void +default_SVCall_ISR(void) +{ + __asm( + " movs r0, #11"); + while(1) + { + __asm( + " BKPT #11"); + } +} + + +void +default_DebugMonitor_ISR(void) +{ + __asm( + " movs r0, #12"); + while(1) + { + __asm( + " BKPT #12"); + } +} + +void +default_PENDSV_ISR(void) +{ + __asm( + " movs r0, #14"); + while(1) + { + __asm( + " BKPT #14"); + } +} + + +void +default_SYSTICK_ISR(void) +{ + __asm( + " movs r0, #15"); + while(1) + { + __asm( + " BKPT #15"); + } +} + + + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +void +default_SPI0_ISR(void) +{ + __asm( + " movs r0, #16"); + while(1) + { + __asm( + " BKPT #16"); + } +} + +void +default_SPI1_ISR(void) +{ + __asm( + " movs r0, #16"); + while(1) + { + __asm( + " BKPT #16"); + } +} + +void +default_I2C0_ISR(void) +{ + __asm( + " movs r0, #16"); + while(1) + { + __asm( + " BKPT #16"); + } +} + +void +default_I2C1_ISR(void) +{ + __asm( + " movs r0, #16"); + while(1) + { + __asm( + " BKPT #16"); + } +} + +void +default_UART0_ISR(void) +{ + __asm( + " movs r0, #16"); + while(1) + { + __asm( + " BKPT #16"); + } +} + +void +default_UART1_ISR(void) +{ + __asm( + " movs r0, #16"); + while(1) + { + __asm( + " BKPT #16"); + } +} + +void +default_GPIO0_ISR(void) +{ + __asm( + " movs r0, #16"); + while(1) + { + __asm( + " BKPT #16"); + } +} + +void +default_GPIO1_ISR(void) +{ + __asm( + " movs r0, #16"); + while(1) + { + __asm( + " BKPT #16"); + } +} + +void +default_ADC_ISR(void) +{ + __asm( + " movs r0, #16"); + while(1) + { + __asm( + " BKPT #16"); + } +} + +void +default_DSP_ISR(void) +{ + __asm( + " movs r0, #16"); + while(1) + { + __asm( + " BKPT #16"); + } +} + +void +default_TIMER0_ISR(void) +{ + __asm( + " movs r0, #16"); + while(1) + { + __asm( + " BKPT #16"); + } +} + +void +default_TIMER1_ISR(void) +{ + __asm( + " movs r0, #16"); + while(1) + { + __asm( + " BKPT #16"); + } +} + +void +default_RTC0_ISR(void) +{ + __asm( + " movs r0, #16"); + while(1) + { + __asm( + " BKPT #16"); + } +} + +void +default_RTC1_ISR(void) +{ + __asm( + " movs r0, #16"); + while(1) + { + __asm( + " BKPT #16"); + } +} + +void +default_PWM_ISR(void) +{ + __asm( + " movs r0, #16"); + while(1) + { + __asm( + " BKPT #16"); + } +} + +void +default_WDT_ISR(void) +{ + __asm( + " movs r0, #16"); + while(1) + { + __asm( + " BKPT #16"); + } +} + +void +default_RTC_TMR_ISR(void) +{ + __asm( + " movs r0, #16"); + while(1) + { + __asm( + " BKPT #16"); + } +} + +void +default_SW0_ISR(void) +{ + __asm( + " movs r0, #16"); + while(1) + { + __asm( + " BKPT #16"); + } +} + +void +default_SW1_ISR(void) +{ + __asm( + " movs r0, #17"); + while(1) + { + __asm( + " BKPT #17"); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Reset ISR +//////////////////////////////////////////////////////////////////////////////// +void +default_ResetISR (void) +{ + int rc; + bool bRunningInFlash; + + set_vtor(); + + bRunningInFlash = + ((((uint32_t) startup_get_my_pc()) & 0xFF000000) == 0x01000000); + + if(( ! REG_RTC_AO_CSR.BF.WARM_START_MODE) || bRunningInFlash) + { + + // + // Copy any .ro bytes to .data so that initialized global variables + // are actually properly initialized. + // + __asm(" ldr r0, =_eftext\n" + " ldr r1, =_data\n" + " ldr r2, =_edata\n" + "ro_copy_loop:\n" + " ldr r3, [r0], #4\n" + " str r3, [r1], #4\n" + " cmp r1, r2\n" + " ble ro_copy_loop\n"); + + // + // Zero fill the .bss section. + // + __asm(" ldr r0, =_bss\n" + " ldr r1, =_ebss\n" + " mov r2, #0\n" + "bss_zero_loop:\n" + " cmp r0, r1\n" + " it lt\n" + " strlt r2, [r0], #4\n" + " blt bss_zero_loop\n"); + } + + // + // call the main routine barefoot, i.e. without the normal CRTC0 entry + // point. + // + rc = _main(0,NULL); + + // + // If main ever returns, trap it here and wake up the debugger if it is + // connected. + // + while(1) // for FPGA/real chip use + { + __asm( + " BKPT #1"); + } + +} + + +//////////////////////////////////////////////////////////////////////////////// +// get my PC +//////////////////////////////////////////////////////////////////////////////// +void * startup_get_my_pc(void) +{ + void *pc; + asm("mov %0, pc" : "=r"(pc)); + return pc; +} + +//////////////////////////////////////////////////////////////////////////////// +// get my SP +//////////////////////////////////////////////////////////////////////////////// +void * startup_get_my_sp(void) +{ + void *sp; + asm("mov %0, sp" : "=r"(sp)); + return sp; +} + +//////////////////////////////////////////////////////////////////////////////// +// Set VTOR based on PC +//////////////////////////////////////////////////////////////////////////////// +void set_vtor(void) +{ + __asm(" ldr r0, =0xe000ed08\n" + " ldr r1, =0xFF000000\n" + " mov r2, lr\n" + " and r1, r2\n" + " str r1, [r0]\n"); + + return; +} + + + + + diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531_makefile.inc b/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531_makefile.inc new file mode 100644 index 0000000000..baae58f87e --- /dev/null +++ b/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531_makefile.inc @@ -0,0 +1,103 @@ +# Settings for eta ecm3531 platform +ifeq ($(TARGET), ecm3531) + TARGET_ARCH := cortex-m3 + TARGET_TOOLCHAIN_PREFIX := arm-none-eabi- + ETA_SDK := /home/hari/TensaiSDK-v0.0.17/soc/ + GCC_ARM := /home/hari/Downloads/gcc-arm-none-eabi-7-2018-q2-update/ + + ifeq ($(wildcard $(ETA_SDK)),) + $(error Path to ETA SDK is not set (ETA_SDK)) + endif + + ifeq ($(wildcard $(GCC_ARM)),) + $(error Path to gcc arm compiler is not set (GCC_ARM)) + endif + + PLATFORM_FLAGS = \ + -DFIRMWARE_BUILD \ + -DGEMMLOWP_ALLOW_SLOW_SCALAR_FALLBACK \ + -DTF_LITE_STATIC_MEMORY \ + -DTF_LITE_MCU_DEBUG_LOG \ + -fno-rtti \ + -fmessage-length=0 \ + -fno-exceptions \ + -fno-unwind-tables \ + -fno-builtin \ + -ffunction-sections \ + -fdata-sections \ + -funsigned-char \ + -MMD \ + -mcpu=cortex-m3 \ + -mthumb \ + -mlittle-endian \ + -mno-unaligned-access \ + -std=gnu++11 \ + -Wvla \ + -Wall \ + -Wextra \ + -Wno-unused-parameter \ + -Wno-missing-field-initializers \ + -Wno-write-strings \ + -Wno-sign-compare \ + -fno-delete-null-pointer-checks \ + -fomit-frame-pointer \ + -fpermissive \ + -nostdlib \ + -g \ + -Os + CXXFLAGS += $(PLATFORM_FLAGS) + CCFLAGS += $(PLATFORM_FLAGS) +# Adding the --specs=nano.specs flag causes the linker to use libc_nano.a +# instead of libc.a. This gets rid of lots of errors with various pieces +# of the exception unwinding code not being found. Not clear why it is +# trying to link in this code to begin with, though. + LDFLAGS += \ + -mthumb -mcpu=cortex-m3 \ + -nostartfiles -static \ + -Wl,--gc-sections -Wl,--entry,ResetISR \ + -Wl,--start-group -lm -lc -lgcc -Wl,--end-group \ + -fno-exceptions \ + -nostdlib --specs=nano.specs -t -lstdc++ -lc -lnosys -lm \ + -Wl,-T,$(MAKEFILE_DIR)/targets/ecm3531/ecm3531.lds \ + -Wl,-Map=$(MAKEFILE_DIR)/gen/$(TARGET).map,--cref + BUILD_TYPE := micro + MICROLITE_LIBS := \ + $(GCC_ARM)/lib/gcc/arm-none-eabi/7.3.1/thumb/v7e-m/fpv4-sp/softfp/crtbegin.o \ + -lm + INCLUDES += \ + -isystem$(MAKEFILE_DIR)/downloads/cmsis/CMSIS/Core/Include/ \ + -I$(GCC_ARM)/arm-none-eabi/include/ \ + -I$(ETA_SDK)/ecm3531/boards/eta_evb/projects/m3/common/inc/ \ + -I$(ETA_SDK)/ecm3531/m3/reg/inc/ \ + -I$(ETA_SDK)/ecm3531/m3/csp/inc/ \ + -I$(ETA_SDK)/ecm3531/common/csp/inc/ \ + -I$(ETA_SDK)/common/inc/ \ + -I$(ETA_SDK)/../utils/inc/ \ + -I$(ETA_SDK)/ecm3531/boards/eta_evb/eta_bsp/inc + + # _main.c contains application and target specific initialization, like + # setting clock speed, default uart setups, etc. and an implementation + # of the DebugLog interfaces. + MICROLITE_CC_SRCS += \ + $(MAKEFILE_DIR)/targets/ecm3531/startup.c \ + $(MAKEFILE_DIR)/targets/ecm3531/_main.c \ + $(wildcard $(ETA_SDK)/ecm3531/boards/eta_evb/projects/m3/common/src/*.c) \ + $(wildcard $(ETA_SDK)/ecm3531/m3/csp/src/*.c) \ + $(wildcard $(ETA_SDK)/ecm3531/m3/csp/src/*.s) \ + + TEST_SCRIPT := tensorflow/lite/experimental/micro/testing/test_ecm3531_binary.sh + # These are tests that don't currently work on the blue pill. + EXCLUDED_TESTS := \ + tensorflow/lite/experimental/micro/micro_interpreter_test.cc \ + tensorflow/lite/experimental/micro/simple_tensor_allocator_test.cc + MICROLITE_TEST_SRCS := $(filter-out $(EXCLUDED_TESTS), $(MICROLITE_TEST_SRCS)) + +# These are microcontroller-specific rules for converting the ELF output +# of the linker into a binary image that can be loaded directly. +OBJCOPY := $(TARGET_TOOLCHAIN_PREFIX)objcopy + +$(BINDIR)/%.bin: $(BINDIR)/% + @mkdir -p $(dir $@) + $(OBJCOPY) $< $@ -O binary + +endif -- GitLab From 035955852708fba07565ead298cd54ad64ab1a55 Mon Sep 17 00:00:00 2001 From: Jacky Ko Date: Sun, 9 Dec 2018 18:33:50 +0800 Subject: [PATCH 036/622] static lib name change --- tensorflow/contrib/cmake/external/abseil_cpp.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tensorflow/contrib/cmake/external/abseil_cpp.cmake b/tensorflow/contrib/cmake/external/abseil_cpp.cmake index 8b76f37858..539c5cbb76 100644 --- a/tensorflow/contrib/cmake/external/abseil_cpp.cmake +++ b/tensorflow/contrib/cmake/external/abseil_cpp.cmake @@ -48,7 +48,7 @@ else (systemlib_ABSEIL_CPP) set(abseil_cpp_STATIC_LIBRARIES ${abseil_cpp_BUILD}/absl/base/Release/absl_base.lib ${abseil_cpp_BUILD}/absl/base/Release/absl_dynamic_annotations.lib - ${abseil_cpp_BUILD}/absl/base/Release/absl_internal_malloc_internal.lib + ${abseil_cpp_BUILD}/absl/base/Release/absl_malloc_internal.lib ${abseil_cpp_BUILD}/absl/strings/Release/absl_strings.lib ${abseil_cpp_BUILD}/absl/strings/Release/str_format_internal.lib ${abseil_cpp_BUILD}/absl/types/Release/absl_bad_optional_access.lib) @@ -94,6 +94,8 @@ else (systemlib_ABSEIL_CPP) ) include_directories(${abseil_cpp_INCLUDE_DIR}) + message(STATUS ${abseil_cpp_INCLUDE_DIR}) + list(APPEND tensorflow_EXTERNAL_LIBRARIES ${abseil_cpp_STATIC_LIBRARIES}) list(APPEND tensorflow_EXTERNAL_DEPENDENCIES abseil_cpp_build) -- GitLab From 5a4871af05392c2e5744515a4c67bc6f0f420943 Mon Sep 17 00:00:00 2001 From: fo40225 Date: Sat, 1 Sep 2018 00:58:14 +0800 Subject: [PATCH 037/622] fix AttributeError: 'module' object has no attribute '???'on windows python 2.7 --- tensorflow/api_template_v1.__init__.py | 3 ++- tensorflow/contrib/cmake/python_modules.txt | 3 +++ tensorflow/contrib/cmake/tf_python.cmake | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tensorflow/api_template_v1.__init__.py b/tensorflow/api_template_v1.__init__.py index 65bdb6cb1b..b9b21bad50 100644 --- a/tensorflow/api_template_v1.__init__.py +++ b/tensorflow/api_template_v1.__init__.py @@ -40,7 +40,8 @@ if '__all__' in vars(): vars()['__all__'].append('contrib') from tensorflow.python.platform import flags # pylint: disable=g-import-not-at-top -app.flags = flags # pylint: disable=undefined-variable +from tensorflow.python.platform import app # pylint: disable=g-import-not-at-top +app.flags = flags # Make sure directory containing top level submodules is in # the __path__ so that "from tensorflow.foo import bar" works. diff --git a/tensorflow/contrib/cmake/python_modules.txt b/tensorflow/contrib/cmake/python_modules.txt index 96160568fa..21ae9a08a6 100644 --- a/tensorflow/contrib/cmake/python_modules.txt +++ b/tensorflow/contrib/cmake/python_modules.txt @@ -1,6 +1,9 @@ # python_sanity_test.py will complain about invalid or missing entries # problematic entries can be commented for temporary whitelisting tensorflow +tensorflow/compiler +tensorflow/compiler/xla +tensorflow/compiler/xla/service tensorflow/core tensorflow/core/example tensorflow/core/framework diff --git a/tensorflow/contrib/cmake/tf_python.cmake b/tensorflow/contrib/cmake/tf_python.cmake index 8faccf8d55..1fe8795ddf 100755 --- a/tensorflow/contrib/cmake/tf_python.cmake +++ b/tensorflow/contrib/cmake/tf_python.cmake @@ -802,6 +802,7 @@ add_custom_command( # tensorflow/__init__.py depends on files generated in this step. So, remove it while # this step is running since the files aren't there yet. COMMAND ${CMAKE_COMMAND} -E remove -f ${CMAKE_CURRENT_BINARY_DIR}/tf_python/tensorflow/__init__.py + COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/tf_python/tensorflow/__init__.py # Run create_python_api.py to generate API init files. COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}/tf_python "${PY_RUNTIME_ENV}" ${PYTHON_EXECUTABLE} -- GitLab From cc69b82bbcc68c21db839cca4f6fec043f6005aa Mon Sep 17 00:00:00 2001 From: Jacky Ko Date: Sun, 9 Dec 2018 19:08:32 +0800 Subject: [PATCH 038/622] abseil use master branch --- tensorflow/contrib/cmake/external/abseil_cpp.cmake | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tensorflow/contrib/cmake/external/abseil_cpp.cmake b/tensorflow/contrib/cmake/external/abseil_cpp.cmake index 539c5cbb76..f64b60fd5e 100644 --- a/tensorflow/contrib/cmake/external/abseil_cpp.cmake +++ b/tensorflow/contrib/cmake/external/abseil_cpp.cmake @@ -39,8 +39,8 @@ else (systemlib_ABSEIL_CPP) include (ExternalProject) set(abseil_cpp_INCLUDE_DIR ${CMAKE_BINARY_DIR}/abseil_cpp/src/abseil_cpp_build) - set(abseil_cpp_URL https://github.com/abseil/abseil-cpp/archive/e01d95528ea2137a4a27a88d1f57c6cb260aafed.tar.gz) - set(abseil_cpp_HASH SHA256=84043ed402d2a2a6ba4cdddb7e85118b1158fd81fe4ac3a14adc343d054c1e2e) + set(abseil_cpp_URL https://github.com/abseil/abseil-cpp.git) + set(abseil_cpp_TAG master) set(abseil_cpp_BUILD ${CMAKE_BINARY_DIR}/abseil_cpp/src/abseil_cpp_build) if(WIN32) @@ -48,7 +48,7 @@ else (systemlib_ABSEIL_CPP) set(abseil_cpp_STATIC_LIBRARIES ${abseil_cpp_BUILD}/absl/base/Release/absl_base.lib ${abseil_cpp_BUILD}/absl/base/Release/absl_dynamic_annotations.lib - ${abseil_cpp_BUILD}/absl/base/Release/absl_malloc_internal.lib + ${abseil_cpp_BUILD}/absl/base/Release/absl_internal_malloc_internal.lib ${abseil_cpp_BUILD}/absl/strings/Release/absl_strings.lib ${abseil_cpp_BUILD}/absl/strings/Release/str_format_internal.lib ${abseil_cpp_BUILD}/absl/types/Release/absl_bad_optional_access.lib) @@ -79,8 +79,7 @@ else (systemlib_ABSEIL_CPP) ExternalProject_Add(abseil_cpp_build PREFIX abseil_cpp - URL ${abseil_cpp_URL} - URL_HASH ${abseil_cpp_HASH} + GIT_REPOSITORY ${abseil_cpp_URL} DOWNLOAD_DIR "${DOWNLOAD_LOCATION}" BUILD_IN_SOURCE 1 BUILD_BYPRODUCTS ${abseil_cpp_STATIC_LIBRARIES} -- GitLab From 956b77a2a8f8db7a57b818bc0c06669fd395d56d Mon Sep 17 00:00:00 2001 From: Jacky Ko Date: Sun, 9 Dec 2018 19:31:05 +0800 Subject: [PATCH 039/622] abseil lib linkage update --- tensorflow/contrib/cmake/external/abseil_cpp.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tensorflow/contrib/cmake/external/abseil_cpp.cmake b/tensorflow/contrib/cmake/external/abseil_cpp.cmake index f64b60fd5e..eefa7d3f03 100644 --- a/tensorflow/contrib/cmake/external/abseil_cpp.cmake +++ b/tensorflow/contrib/cmake/external/abseil_cpp.cmake @@ -49,6 +49,8 @@ else (systemlib_ABSEIL_CPP) ${abseil_cpp_BUILD}/absl/base/Release/absl_base.lib ${abseil_cpp_BUILD}/absl/base/Release/absl_dynamic_annotations.lib ${abseil_cpp_BUILD}/absl/base/Release/absl_internal_malloc_internal.lib + ${abseil_cpp_BUILD}/absl/base/Release/absl_internal_throw_delegate.lib + ${abseil_cpp_BUILD}/absl/numeric/Release/absl_int128.lib ${abseil_cpp_BUILD}/absl/strings/Release/absl_strings.lib ${abseil_cpp_BUILD}/absl/strings/Release/str_format_internal.lib ${abseil_cpp_BUILD}/absl/types/Release/absl_bad_optional_access.lib) -- GitLab From 7578e120de2a3a5282ced8d41881f19363f83466 Mon Sep 17 00:00:00 2001 From: Dan Jarvis Date: Thu, 23 Nov 2017 13:06:02 -0500 Subject: [PATCH 040/622] Fix crash on closing the app when classifier failed to initialize When testing on an API 21 emulator, the classifier fails to initialize. `E/TfLiteCameraDemo: Failed to initialize an image classifier.` In this situation, the app crashes when pressing Back to exit. Here's the cause: ``` java.lang.NullPointerException: Attempt to invoke virtual method 'void com.example.android.tflitecamerademo.ImageClassifier.close()' on a null object reference at com.example.android.tflitecamerademo.Camera2BasicFragment.onDestroy(Camera2BasicFragment.java:331) at android.app.Fragment.performDestroy(Fragment.java:2266) ``` The fix is to check for null before calling `.close()`. I'll investigate why the classifier is failing to initialize separately. :-) --- .../android/tflitecamerademo/Camera2BasicFragment.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tensorflow/lite/java/demo/app/src/main/java/com/example/android/tflitecamerademo/Camera2BasicFragment.java b/tensorflow/lite/java/demo/app/src/main/java/com/example/android/tflitecamerademo/Camera2BasicFragment.java index 165d335101..a7b3440536 100644 --- a/tensorflow/lite/java/demo/app/src/main/java/com/example/android/tflitecamerademo/Camera2BasicFragment.java +++ b/tensorflow/lite/java/demo/app/src/main/java/com/example/android/tflitecamerademo/Camera2BasicFragment.java @@ -476,7 +476,9 @@ public class Camera2BasicFragment extends Fragment @Override public void onDestroy() { - classifier.close(); + if (classifier != null) { + classifier.close(); + } super.onDestroy(); } -- GitLab From 9192d06bb62b11c40b1b5d3d5ed2e91adaacf6d8 Mon Sep 17 00:00:00 2001 From: hari Date: Mon, 10 Dec 2018 11:55:05 -0800 Subject: [PATCH 041/622] fixed grammer --- .../experimental/micro/tools/make/targets/ecm3531/README.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/README.txt b/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/README.txt index 655340ceff..3cb74a7243 100644 --- a/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/README.txt +++ b/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/README.txt @@ -5,7 +5,7 @@ This code will enable you to compile and execute the Tensorflow Lite Micro Speec GETTING STARTED: -1. Download the Tensorflow code from Github and follow anyinstructions there to download other dependencies. +1. Download the Tensorflow code from Github and follow instructions there to download other dependencies. 2. Download the Eta Compute SDK, version 0.0.17. -- GitLab From 374ad6b77c8edc8f5be6c12b510cd6a698894c29 Mon Sep 17 00:00:00 2001 From: hari Date: Mon, 10 Dec 2018 11:55:51 -0800 Subject: [PATCH 042/622] conform to Google coding standards --- .../micro/tools/make/targets/ecm3531/_main.c | 120 +++++++++--------- 1 file changed, 59 insertions(+), 61 deletions(-) diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/_main.c b/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/_main.c index 5a595c26b3..93941e119f 100644 --- a/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/_main.c +++ b/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/_main.c @@ -1,37 +1,42 @@ -/******************************************************************************* - * Copyright (C) 2018 Eta Compute, Inc - * - * 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. - * - ******************************************************************************/ +/* Copyright 2015 The TensorFlow Authors. All Rights Reserved. -#include +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. +==============================================================================*/ + +/* This is file contains the entry point to the application and is called after + startup. + The GPIOs, Uart and timer are intialized and Tensorflow is invoked with the + call to main(). + Tensorflow will print out if the tests have passed or failed and the + execution time is also + printed. */ + +#include +#include #include +#include +#include "eta_bsp.h" #include "eta_chip.h" #include "eta_csp.h" -#include "eta_csp_uart.h" +#include "eta_csp_buck.h" #include "eta_csp_gpio.h" #include "eta_csp_io.h" -#include "eta_csp_timer.h" -#include "eta_csp_socctrl.h" -#include "eta_csp_buck.h" +#include "eta_csp_pwr.h" #include "eta_csp_rtc.h" +#include "eta_csp_socctrl.h" #include "eta_csp_sys_clock.h" -#include -#include "eta_bsp.h" -#include "eta_csp_pwr.h" -#include - +#include "eta_csp_timer.h" +#include "eta_csp_uart.h" tUart g_sUart0 = {eUartNum0, eUartBaud115200}; tUart g_sUart1 = {eUartNum1, eUartBaud115200}; @@ -39,59 +44,52 @@ tUart g_sUart1 = {eUartNum1, eUartBaud115200}; int init_main(int); void EtaPrintExecutionTime(uint64_t); - //***************************************************************************** // // The entry point for the application. // //***************************************************************************** -extern int main(int argc, char**argv); - -void DebugLog(const char* s) { EtaCspIoPrintf( "%s", s); } -void DebugLogInt32(int32_t i) { EtaCspIoPrintf( "%d", i); } -void DebugLogUInt32(uint32_t i) { EtaCspIoPrintf( "%d", i); } -void DebugLogHex(uint32_t i) { EtaCspIoPrintf( "0x%8x", i); } -void DebugLogFloat(float i) { EtaCspIoPrintf( "%f", i); } - - -int _main(void) -{ - uint64_t time_ms; - - EtaCspInit(); //initialize csp registers - EtaCspGpioInit(); //initialize gpios - EtaCspUartInit(&g_sUart1, eUartNum0, eUartBaud115200, eUartFlowControlHardware); //initialize Uart - EtaCspBuckInit(ETA_BSP_VDD_IO_SETTING, eBuckAo600Mv, eBuckM3Frequency60Mhz, eBuckMemVoltage900Mv);//set M3 freq - EtaCspTimerInitMs(); //start timer - main(0, NULL); //Call to Tensorflow; this will print if test was successful. - time_ms = EtaCspTimerCountGetMs(); //read time - EtaPrintExecutionTime(time_ms); //print execution time - +extern int main(int argc, char** argv); + +void DebugLog(const char* s) { EtaCspIoPrintf("%s", s); } +void DebugLogInt32(int32_t i) { EtaCspIoPrintf("%d", i); } +void DebugLogUInt32(uint32_t i) { EtaCspIoPrintf("%d", i); } +void DebugLogHex(uint32_t i) { EtaCspIoPrintf("0x%8x", i); } +void DebugLogFloat(float i) { EtaCspIoPrintf("%f", i); } + +int _main(void) { + uint64_t time_ms; + + EtaCspInit(); // initialize csp registers + EtaCspGpioInit(); // initialize gpios + EtaCspUartInit(&g_sUart1, eUartNum0, eUartBaud115200, + eUartFlowControlHardware); // initialize Uart + EtaCspBuckInit(ETA_BSP_VDD_IO_SETTING, eBuckAo600Mv, eBuckM3Frequency60Mhz, + eBuckMemVoltage900Mv); // set M3 freq + EtaCspTimerInitMs(); // start timer + main(0, NULL); // Call to Tensorflow; this will print if test was successful. + time_ms = EtaCspTimerCountGetMs(); // read time + EtaPrintExecutionTime(time_ms); // print execution time } - - -void EtaPrintExecutionTime(uint64_t time_ms) -{ +void EtaPrintExecutionTime(uint64_t time_ms) { uint8_t c; int k1; char time_string[] = "00000"; EtaCspIoPrintf("Execution time (msec) = "); - if (time_ms < 100000) //Convert time to a string + if (time_ms < 100000) // Convert time to a string { - for(k1 = 0; k1 < 5; k1++){ + for (k1 = 0; k1 < 5; k1++) { c = time_ms % 10; - time_ms = time_ms/10; + time_ms = time_ms / 10; time_string[k1] = (char)(0x30 + c); } - for(k1 = 4; k1 > 0; k1--) { //print out 1 char at a time + for (k1 = 4; k1 > 0; k1--) { // print out 1 char at a time EtaCspUartPutc(&g_sUart1, time_string[k1]); } + } else { + EtaCspIoPrintf("Execution time exceeds 100 sec\n"); } - else{ - EtaCspIoPrintf("Execution time exceeds 100 sec\n"); - } EtaCspIoPrintf("\n\n"); - } -- GitLab From 6042bbb3a9d87ea88b778761b7c1b20a2bc81237 Mon Sep 17 00:00:00 2001 From: hari Date: Mon, 10 Dec 2018 11:56:08 -0800 Subject: [PATCH 043/622] conform to Google coding standards --- .../tools/make/targets/ecm3531/startup.c | 771 +++++++----------- 1 file changed, 304 insertions(+), 467 deletions(-) diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/startup.c b/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/startup.c index da9f7a4b0e..04b9abc3aa 100644 --- a/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/startup.c +++ b/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/startup.c @@ -1,89 +1,79 @@ -/******************************************************************************* - * - * Copyright (C) 2018 Eta Compute, Inc - * - * 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. - * - ******************************************************************************/ +/* Copyright 2015 The TensorFlow Authors. All Rights Reserved. -#include -#include "eta_chip.h" -#include "memio.h" +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 -//#pragma GCC optimize ("align-functions=16") +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 +#include "eta_chip.h" +#include "memio.h" -#ifndef NULL -#define NULL (0) +#ifndef NULL +#define NULL (0) #endif - //***************************************************************************** // // Macro for hardware access, both direct and via the bit-band region. // //***************************************************************************** -#define NAKED -//#define NAKED __attribute__((naked)) - int _main(int argc, char *argv[]); void set_vtor(void); -void * startup_get_my_pc(void); +void *startup_get_my_pc(void); //***************************************************************************** // Forward DECLS for interrupt service routines (ISR) //***************************************************************************** -extern void ResetISR(void) __attribute__((weak, alias("default_ResetISR"))); -extern void NmiSR(void) __attribute__((weak, alias("default_NmiSR"))); -extern void FaultISR(void) __attribute__((weak, alias("default_FaultISR"))); - -extern void DebugMonitor_ISR(void) __attribute__((weak, alias("default_DebugMonitor_ISR"))); -extern void SVCall_ISR(void) __attribute__((weak, alias("default_SVCall_ISR"))); -extern void PENDSV_ISR(void) __attribute__((weak, alias("default_PENDSV_ISR"))); - -extern void SYSTICK_ISR(void) __attribute__((weak, alias("default_SYSTICK_ISR"))); - -extern void GPIO0_ISR(void) __attribute__((weak, alias("default_GPIO0_ISR"))); -extern void GPIO1_ISR(void) __attribute__((weak, alias("default_GPIO1_ISR"))); -extern void TIMER0_ISR(void) __attribute__((weak, alias("default_TIMER0_ISR"))); -extern void TIMER1_ISR(void) __attribute__((weak, alias("default_TIMER1_ISR"))); -extern void UART0_ISR(void) __attribute__((weak, alias("default_UART0_ISR"))); -extern void UART1_ISR(void) __attribute__((weak, alias("default_UART1_ISR"))); -extern void SPI0_ISR(void) __attribute__((weak, alias("default_SPI0_ISR"))); -extern void SPI1_ISR(void) __attribute__((weak, alias("default_SPI1_ISR"))); -extern void I2C0_ISR(void) __attribute__((weak, alias("default_I2C0_ISR"))); -extern void I2C1_ISR(void) __attribute__((weak, alias("default_I2C1_ISR"))); -extern void RTC0_ISR(void) __attribute__((weak, alias("default_RTC0_ISR"))); -extern void RTC1_ISR(void) __attribute__((weak, alias("default_RTC1_ISR"))); -extern void DSP_ISR(void) __attribute__((weak, alias("default_DSP_ISR"))); -extern void ADC_ISR(void) __attribute__((weak, alias("default_ADC_ISR"))); -extern void SW0_ISR(void) __attribute__((weak, alias("default_SW0_ISR"))); -extern void SW1_ISR(void) __attribute__((weak, alias("default_SW1_ISR"))); -extern void PWM_ISR(void) __attribute__((weak, alias("default_PWM_ISR"))); -extern void WDT_ISR(void) __attribute__((weak, alias("default_WDT_ISR"))); -extern void RTC_TMR_ISR(void) __attribute__((weak, alias("default_RTC_TMR_ISR"))); - - -extern void SW2_ISR(void) __attribute__((weak, alias("default_SW1_ISR"))); -extern void SW3_ISR(void) __attribute__((weak, alias("default_SW1_ISR"))); -extern void SW4_ISR(void) __attribute__((weak, alias("default_SW1_ISR"))); -extern void SW5_ISR(void) __attribute__((weak, alias("default_SW1_ISR"))); -extern void SW6_ISR(void) __attribute__((weak, alias("default_SW1_ISR"))); - - -extern void IntDefaultHandler(void) __attribute__((weak)) NAKED; +extern void ResetISR(void) __attribute__((weak, alias("default_ResetISR"))); +extern void NmiSR(void) __attribute__((weak, alias("default_NmiSR"))); +extern void FaultISR(void) __attribute__((weak, alias("default_FaultISR"))); + +extern void DebugMonitor_ISR(void) + __attribute__((weak, alias("default_DebugMonitor_ISR"))); +extern void SVCall_ISR(void) __attribute__((weak, alias("default_SVCall_ISR"))); +extern void PENDSV_ISR(void) __attribute__((weak, alias("default_PENDSV_ISR"))); + +extern void SYSTICK_ISR(void) + __attribute__((weak, alias("default_SYSTICK_ISR"))); + +extern void GPIO0_ISR(void) __attribute__((weak, alias("default_GPIO0_ISR"))); +extern void GPIO1_ISR(void) __attribute__((weak, alias("default_GPIO1_ISR"))); +extern void TIMER0_ISR(void) __attribute__((weak, alias("default_TIMER0_ISR"))); +extern void TIMER1_ISR(void) __attribute__((weak, alias("default_TIMER1_ISR"))); +extern void UART0_ISR(void) __attribute__((weak, alias("default_UART0_ISR"))); +extern void UART1_ISR(void) __attribute__((weak, alias("default_UART1_ISR"))); +extern void SPI0_ISR(void) __attribute__((weak, alias("default_SPI0_ISR"))); +extern void SPI1_ISR(void) __attribute__((weak, alias("default_SPI1_ISR"))); +extern void I2C0_ISR(void) __attribute__((weak, alias("default_I2C0_ISR"))); +extern void I2C1_ISR(void) __attribute__((weak, alias("default_I2C1_ISR"))); +extern void RTC0_ISR(void) __attribute__((weak, alias("default_RTC0_ISR"))); +extern void RTC1_ISR(void) __attribute__((weak, alias("default_RTC1_ISR"))); +extern void DSP_ISR(void) __attribute__((weak, alias("default_DSP_ISR"))); +extern void ADC_ISR(void) __attribute__((weak, alias("default_ADC_ISR"))); +extern void SW0_ISR(void) __attribute__((weak, alias("default_SW0_ISR"))); +extern void SW1_ISR(void) __attribute__((weak, alias("default_SW1_ISR"))); +extern void PWM_ISR(void) __attribute__((weak, alias("default_PWM_ISR"))); +extern void WDT_ISR(void) __attribute__((weak, alias("default_WDT_ISR"))); +extern void RTC_TMR_ISR(void) + __attribute__((weak, alias("default_RTC_TMR_ISR"))); + +extern void SW2_ISR(void) __attribute__((weak, alias("default_SW1_ISR"))); +extern void SW3_ISR(void) __attribute__((weak, alias("default_SW1_ISR"))); +extern void SW4_ISR(void) __attribute__((weak, alias("default_SW1_ISR"))); +extern void SW5_ISR(void) __attribute__((weak, alias("default_SW1_ISR"))); +extern void SW6_ISR(void) __attribute__((weak, alias("default_SW1_ISR"))); + +extern void IntDefaultHandler(void) __attribute__((weak)); //***************************************************************************** // @@ -92,63 +82,58 @@ extern void IntDefaultHandler(void) __attribute__((weak)) NAKED; //***************************************************************************** extern uint32_t _stack_top; //__attribute__ ((section(".mainStack"), used)) -//static uint32_t pui32Stack[2048]; +// static uint32_t pui32Stack[2048]; #define STARTUP_STACK_TOP (&_stack_top) - - //***************************************************************************** // VECTOR TABLE //***************************************************************************** -__attribute__ ((section(".vectors"), used)) -void (* const gVectors[])(void) = -{ - //(void (*)(void))((uint32_t)pui32Stack + sizeof(pui32Stack)), // Stack pointer - (void *)STARTUP_STACK_TOP, - ResetISR, // Reset handler - NmiSR, // The NMI handler - FaultISR, // The hard fault handler - IntDefaultHandler, // 4 The MPU fault handler - IntDefaultHandler, // 5 The bus fault handler - IntDefaultHandler, // 6 The usage fault handler - 0, // 7 Reserved - 0, // 8 Reserved - 0, // 9 Reserved - 0, // 10 Reserved - SVCall_ISR, // 11 SVCall handler - DebugMonitor_ISR, // 12 Debug monitor handler - 0, // 13 Reserved - PENDSV_ISR, // 14 The PendSV handler - SYSTICK_ISR, // 15 The SysTick handler - - // external interrupt service routines (ISR) - GPIO0_ISR, // 16 GPIO Port A [ 0] - GPIO1_ISR, // 17 GPIO Port B [ 1] - TIMER0_ISR, // 18 Timer 0 [ 2] - TIMER1_ISR, // 19 Timer 1 [ 3] - UART0_ISR, // 20 UART 0 [ 4] - UART1_ISR, // 21 UART 1 [ 5] - SPI0_ISR, // 22 SPI0 [ 6] - SPI1_ISR, // 23 SPI1 [ 7] - I2C0_ISR, // 24 I2C 0 [ 8] - I2C1_ISR, // 25 I2C 1 [ 9] - RTC0_ISR, // 26 RTC 0 [10] - RTC1_ISR, // 27 RTC 1 [11] - DSP_ISR, // 28 DSP MAILBOX [12] - ADC_ISR, // 29 ADC [13] - PWM_ISR, // 32 PWM [14] - WDT_ISR, // 33 WDT [15] - RTC_TMR_ISR, // 34 RTC [16] - - SW0_ISR, // 30 Software Interrupt 0 [17] - SW1_ISR, // 31 Software Interrupt 1 [18] - SW2_ISR, // 35 Software Interrupt 2 [19] - SW3_ISR, // 36 Software Interrupt 3 [20] - SW4_ISR, // 37 Software Interrupt 4 [21] - SW5_ISR, // 38 Software Interrupt 5 [22] - SW6_ISR, // 39 Software Interrupt 6 [23] - - +__attribute__((section(".vectors"), used)) void (*const gVectors[])(void) = { + //(void (*)(void))((uint32_t)pui32Stack + sizeof(pui32Stack)), // Stack + //pointer + (void *)STARTUP_STACK_TOP, + ResetISR, // Reset handler + NmiSR, // The NMI handler + FaultISR, // The hard fault handler + IntDefaultHandler, // 4 The MPU fault handler + IntDefaultHandler, // 5 The bus fault handler + IntDefaultHandler, // 6 The usage fault handler + 0, // 7 Reserved + 0, // 8 Reserved + 0, // 9 Reserved + 0, // 10 Reserved + SVCall_ISR, // 11 SVCall handler + DebugMonitor_ISR, // 12 Debug monitor handler + 0, // 13 Reserved + PENDSV_ISR, // 14 The PendSV handler + SYSTICK_ISR, // 15 The SysTick handler + + // external interrupt service routines (ISR) + GPIO0_ISR, // 16 GPIO Port A [ 0] + GPIO1_ISR, // 17 GPIO Port B [ 1] + TIMER0_ISR, // 18 Timer 0 [ 2] + TIMER1_ISR, // 19 Timer 1 [ 3] + UART0_ISR, // 20 UART 0 [ 4] + UART1_ISR, // 21 UART 1 [ 5] + SPI0_ISR, // 22 SPI0 [ 6] + SPI1_ISR, // 23 SPI1 [ 7] + I2C0_ISR, // 24 I2C 0 [ 8] + I2C1_ISR, // 25 I2C 1 [ 9] + RTC0_ISR, // 26 RTC 0 [10] + RTC1_ISR, // 27 RTC 1 [11] + DSP_ISR, // 28 DSP MAILBOX [12] + ADC_ISR, // 29 ADC [13] + PWM_ISR, // 32 PWM [14] + WDT_ISR, // 33 WDT [15] + RTC_TMR_ISR, // 34 RTC [16] + + SW0_ISR, // 30 Software Interrupt 0 [17] + SW1_ISR, // 31 Software Interrupt 1 [18] + SW2_ISR, // 35 Software Interrupt 2 [19] + SW3_ISR, // 36 Software Interrupt 3 [20] + SW4_ISR, // 37 Software Interrupt 4 [21] + SW5_ISR, // 38 Software Interrupt 5 [22] + SW6_ISR, // 39 Software Interrupt 6 [23] }; @@ -169,424 +154,276 @@ extern uint32_t _ebss; // // And here are the weak interrupt handlers. // -void -default_NmiSR (void) -{ - __asm( - " movs r0, #2"); - while(1) - { - } +void default_NmiSR(void) { + __asm(" movs r0, #2"); + while (1) { + } } - -void -default_FaultISR (void) -{ - __asm( - " movs r0, #3"); - MEMIO32(0x1001FFF0) = 0xbad0beef; // near the top of 128KB of SRAM - MEMIO32(0x1001FFF4) = 0xbad1beef; // near the top of 128KB of SRAM - while(1) - { - __asm( - " BKPT #1"); - } +void default_FaultISR(void) { + __asm(" movs r0, #3"); + MEMIO32(0x1001FFF0) = 0xbad0beef; // near the top of 128KB of SRAM + MEMIO32(0x1001FFF4) = 0xbad1beef; // near the top of 128KB of SRAM + while (1) { + __asm(" BKPT #1"); + } } - -void -IntDefaultHandler (void) -{ - __asm( - " movs r0, #20"); - while(1) - { - __asm( - " BKPT #1"); - } +void IntDefaultHandler(void) { + __asm(" movs r0, #20"); + while (1) { + __asm(" BKPT #1"); + } } - -void -default_SVCall_ISR(void) -{ - __asm( - " movs r0, #11"); - while(1) - { - __asm( - " BKPT #11"); - } +void default_SVCall_ISR(void) { + __asm(" movs r0, #11"); + while (1) { + __asm(" BKPT #11"); + } } - -void -default_DebugMonitor_ISR(void) -{ - __asm( - " movs r0, #12"); - while(1) - { - __asm( - " BKPT #12"); - } +void default_DebugMonitor_ISR(void) { + __asm(" movs r0, #12"); + while (1) { + __asm(" BKPT #12"); + } } -void -default_PENDSV_ISR(void) -{ - __asm( - " movs r0, #14"); - while(1) - { - __asm( - " BKPT #14"); - } +void default_PENDSV_ISR(void) { + __asm(" movs r0, #14"); + while (1) { + __asm(" BKPT #14"); + } } - -void -default_SYSTICK_ISR(void) -{ - __asm( - " movs r0, #15"); - while(1) - { - __asm( - " BKPT #15"); - } +void default_SYSTICK_ISR(void) { + __asm(" movs r0, #15"); + while (1) { + __asm(" BKPT #15"); + } } - - //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -void -default_SPI0_ISR(void) -{ - __asm( - " movs r0, #16"); - while(1) - { - __asm( - " BKPT #16"); - } +void default_SPI0_ISR(void) { + __asm(" movs r0, #16"); + while (1) { + __asm(" BKPT #16"); + } } -void -default_SPI1_ISR(void) -{ - __asm( - " movs r0, #16"); - while(1) - { - __asm( - " BKPT #16"); - } +void default_SPI1_ISR(void) { + __asm(" movs r0, #16"); + while (1) { + __asm(" BKPT #16"); + } } -void -default_I2C0_ISR(void) -{ - __asm( - " movs r0, #16"); - while(1) - { - __asm( - " BKPT #16"); - } +void default_I2C0_ISR(void) { + __asm(" movs r0, #16"); + while (1) { + __asm(" BKPT #16"); + } } -void -default_I2C1_ISR(void) -{ - __asm( - " movs r0, #16"); - while(1) - { - __asm( - " BKPT #16"); - } +void default_I2C1_ISR(void) { + __asm(" movs r0, #16"); + while (1) { + __asm(" BKPT #16"); + } } -void -default_UART0_ISR(void) -{ - __asm( - " movs r0, #16"); - while(1) - { - __asm( - " BKPT #16"); - } +void default_UART0_ISR(void) { + __asm(" movs r0, #16"); + while (1) { + __asm(" BKPT #16"); + } } -void -default_UART1_ISR(void) -{ - __asm( - " movs r0, #16"); - while(1) - { - __asm( - " BKPT #16"); - } +void default_UART1_ISR(void) { + __asm(" movs r0, #16"); + while (1) { + __asm(" BKPT #16"); + } } -void -default_GPIO0_ISR(void) -{ - __asm( - " movs r0, #16"); - while(1) - { - __asm( - " BKPT #16"); - } +void default_GPIO0_ISR(void) { + __asm(" movs r0, #16"); + while (1) { + __asm(" BKPT #16"); + } } -void -default_GPIO1_ISR(void) -{ - __asm( - " movs r0, #16"); - while(1) - { - __asm( - " BKPT #16"); - } +void default_GPIO1_ISR(void) { + __asm(" movs r0, #16"); + while (1) { + __asm(" BKPT #16"); + } } -void -default_ADC_ISR(void) -{ - __asm( - " movs r0, #16"); - while(1) - { - __asm( - " BKPT #16"); - } +void default_ADC_ISR(void) { + __asm(" movs r0, #16"); + while (1) { + __asm(" BKPT #16"); + } } -void -default_DSP_ISR(void) -{ - __asm( - " movs r0, #16"); - while(1) - { - __asm( - " BKPT #16"); - } +void default_DSP_ISR(void) { + __asm(" movs r0, #16"); + while (1) { + __asm(" BKPT #16"); + } } -void -default_TIMER0_ISR(void) -{ - __asm( - " movs r0, #16"); - while(1) - { - __asm( - " BKPT #16"); - } +void default_TIMER0_ISR(void) { + __asm(" movs r0, #16"); + while (1) { + __asm(" BKPT #16"); + } } -void -default_TIMER1_ISR(void) -{ - __asm( - " movs r0, #16"); - while(1) - { - __asm( - " BKPT #16"); - } +void default_TIMER1_ISR(void) { + __asm(" movs r0, #16"); + while (1) { + __asm(" BKPT #16"); + } } -void -default_RTC0_ISR(void) -{ - __asm( - " movs r0, #16"); - while(1) - { - __asm( - " BKPT #16"); - } +void default_RTC0_ISR(void) { + __asm(" movs r0, #16"); + while (1) { + __asm(" BKPT #16"); + } } -void -default_RTC1_ISR(void) -{ - __asm( - " movs r0, #16"); - while(1) - { - __asm( - " BKPT #16"); - } +void default_RTC1_ISR(void) { + __asm(" movs r0, #16"); + while (1) { + __asm(" BKPT #16"); + } } -void -default_PWM_ISR(void) -{ - __asm( - " movs r0, #16"); - while(1) - { - __asm( - " BKPT #16"); - } +void default_PWM_ISR(void) { + __asm(" movs r0, #16"); + while (1) { + __asm(" BKPT #16"); + } } -void -default_WDT_ISR(void) -{ - __asm( - " movs r0, #16"); - while(1) - { - __asm( - " BKPT #16"); - } +void default_WDT_ISR(void) { + __asm(" movs r0, #16"); + while (1) { + __asm(" BKPT #16"); + } } -void -default_RTC_TMR_ISR(void) -{ - __asm( - " movs r0, #16"); - while(1) - { - __asm( - " BKPT #16"); - } +void default_RTC_TMR_ISR(void) { + __asm(" movs r0, #16"); + while (1) { + __asm(" BKPT #16"); + } } -void -default_SW0_ISR(void) -{ - __asm( - " movs r0, #16"); - while(1) - { - __asm( - " BKPT #16"); - } +void default_SW0_ISR(void) { + __asm(" movs r0, #16"); + while (1) { + __asm(" BKPT #16"); + } } -void -default_SW1_ISR(void) -{ - __asm( - " movs r0, #17"); - while(1) - { - __asm( - " BKPT #17"); - } +void default_SW1_ISR(void) { + __asm(" movs r0, #17"); + while (1) { + __asm(" BKPT #17"); + } } //////////////////////////////////////////////////////////////////////////////// // Reset ISR //////////////////////////////////////////////////////////////////////////////// -void -default_ResetISR (void) -{ - int rc; - bool bRunningInFlash; - - set_vtor(); - - bRunningInFlash = - ((((uint32_t) startup_get_my_pc()) & 0xFF000000) == 0x01000000); - - if(( ! REG_RTC_AO_CSR.BF.WARM_START_MODE) || bRunningInFlash) - { - - // - // Copy any .ro bytes to .data so that initialized global variables - // are actually properly initialized. - // - __asm(" ldr r0, =_eftext\n" - " ldr r1, =_data\n" - " ldr r2, =_edata\n" - "ro_copy_loop:\n" - " ldr r3, [r0], #4\n" - " str r3, [r1], #4\n" - " cmp r1, r2\n" - " ble ro_copy_loop\n"); - - // - // Zero fill the .bss section. - // - __asm(" ldr r0, =_bss\n" - " ldr r1, =_ebss\n" - " mov r2, #0\n" - "bss_zero_loop:\n" - " cmp r0, r1\n" - " it lt\n" - " strlt r2, [r0], #4\n" - " blt bss_zero_loop\n"); - } - - // - // call the main routine barefoot, i.e. without the normal CRTC0 entry - // point. - // - rc = _main(0,NULL); - - // - // If main ever returns, trap it here and wake up the debugger if it is - // connected. - // - while(1) // for FPGA/real chip use - { - __asm( - " BKPT #1"); - } - -} +void default_ResetISR(void) { + int rc; + bool bRunningInFlash; + + set_vtor(); + bRunningInFlash = + ((((uint32_t)startup_get_my_pc()) & 0xFF000000) == 0x01000000); + + if ((!REG_RTC_AO_CSR.BF.WARM_START_MODE) || bRunningInFlash) { + // + // Copy any .ro bytes to .data so that initialized global variables + // are actually properly initialized. + // + __asm( + " ldr r0, =_eftext\n" + " ldr r1, =_data\n" + " ldr r2, =_edata\n" + "ro_copy_loop:\n" + " ldr r3, [r0], #4\n" + " str r3, [r1], #4\n" + " cmp r1, r2\n" + " ble ro_copy_loop\n"); + + // + // Zero fill the .bss section. + // + __asm( + " ldr r0, =_bss\n" + " ldr r1, =_ebss\n" + " mov r2, #0\n" + "bss_zero_loop:\n" + " cmp r0, r1\n" + " it lt\n" + " strlt r2, [r0], #4\n" + " blt bss_zero_loop\n"); + } + + // + // call the main routine barefoot, i.e. without the normal CRTC0 entry + // point. + // + rc = _main(0, NULL); + + // + // If main ever returns, trap it here and wake up the debugger if it is + // connected. + // + while (1) // for FPGA/real chip use + { + __asm(" BKPT #1"); + } +} //////////////////////////////////////////////////////////////////////////////// // get my PC //////////////////////////////////////////////////////////////////////////////// -void * startup_get_my_pc(void) -{ - void *pc; - asm("mov %0, pc" : "=r"(pc)); - return pc; +void *startup_get_my_pc(void) { + void *pc; + asm("mov %0, pc" : "=r"(pc)); + return pc; } //////////////////////////////////////////////////////////////////////////////// // get my SP //////////////////////////////////////////////////////////////////////////////// -void * startup_get_my_sp(void) -{ - void *sp; - asm("mov %0, sp" : "=r"(sp)); - return sp; +void *startup_get_my_sp(void) { + void *sp; + asm("mov %0, sp" : "=r"(sp)); + return sp; } //////////////////////////////////////////////////////////////////////////////// // Set VTOR based on PC //////////////////////////////////////////////////////////////////////////////// -void set_vtor(void) -{ - __asm(" ldr r0, =0xe000ed08\n" - " ldr r1, =0xFF000000\n" - " mov r2, lr\n" - " and r1, r2\n" - " str r1, [r0]\n"); - - return; +void set_vtor(void) { + __asm( + " ldr r0, =0xe000ed08\n" + " ldr r1, =0xFF000000\n" + " mov r2, lr\n" + " and r1, r2\n" + " str r1, [r0]\n"); + + return; } - - - - - -- GitLab From 7fbd07114922c1d132e323f7bea14126b336f86e Mon Sep 17 00:00:00 2001 From: hari Date: Mon, 10 Dec 2018 11:56:50 -0800 Subject: [PATCH 044/622] added copyright statement --- .../tools/make/targets/ecm3531/ecm3531.lds | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/ecm3531.lds b/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/ecm3531.lds index 2c77ebcc1d..af34f988f2 100644 --- a/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/ecm3531.lds +++ b/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/ecm3531.lds @@ -1,20 +1,17 @@ -/******************************************************************************* - * - * Copyright (C) 2018 Eta Compute, Inc - * - * 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. - * - ******************************************************************************/ +/* Copyright 2015 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. +==============================================================================*/ /* -- GitLab From ac0d13b64d260b89822a73637e43c9098205f20f Mon Sep 17 00:00:00 2001 From: hari Date: Mon, 10 Dec 2018 11:58:00 -0800 Subject: [PATCH 045/622] added copyright statement and comment for usage --- .../tools/make/targets/ecm3531/load_program | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/load_program b/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/load_program index 5d4974ba6e..ac1f49962a 100755 --- a/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/load_program +++ b/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/load_program @@ -1,4 +1,22 @@ #!/usr/bin/python3 +#Usage: cd to the directory tensorflow/lite/experimental/micro/tools/make/targets/ecm3531 and type ./load_prgram to load the executable tensorflow/lite/experimental/micro/tools/make/gen/ecm3531_cortex-m3/bin/micro_speech_test into SRAM +# +# +# Copyright 2015 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. +#============================================================================== + import os import telnetlib -- GitLab From 6f5e6dc40f30b34af97e3813b046e572d8125fea Mon Sep 17 00:00:00 2001 From: hari Date: Mon, 10 Dec 2018 12:06:12 -0800 Subject: [PATCH 046/622] added brief comment about this file --- .../experimental/micro/tools/make/targets/ecm3531/startup.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/startup.c b/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/startup.c index 04b9abc3aa..32d817ba48 100644 --- a/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/startup.c +++ b/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/startup.c @@ -13,6 +13,9 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ +/* This file is called at power up time to initialize the chip. It in turn +calls _main() which is the entry point into the application */ + #include #include "eta_chip.h" #include "memio.h" @@ -90,7 +93,7 @@ extern uint32_t _stack_top; //***************************************************************************** __attribute__((section(".vectors"), used)) void (*const gVectors[])(void) = { //(void (*)(void))((uint32_t)pui32Stack + sizeof(pui32Stack)), // Stack - //pointer + // pointer (void *)STARTUP_STACK_TOP, ResetISR, // Reset handler NmiSR, // The NMI handler -- GitLab From 16f454f95e9d9823145d6b00a7e007afd0ea569b Mon Sep 17 00:00:00 2001 From: Luke Han Date: Tue, 11 Dec 2018 11:03:05 +0900 Subject: [PATCH 047/622] fix typo in scatter_nd_add docstring --- tensorflow/python/ops/state_ops.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tensorflow/python/ops/state_ops.py b/tensorflow/python/ops/state_ops.py index 3ac69c1c20..25b31c698e 100644 --- a/tensorflow/python/ops/state_ops.py +++ b/tensorflow/python/ops/state_ops.py @@ -462,9 +462,8 @@ def scatter_nd_add(ref, indices, updates, use_locking=False, name=None): updates: A `Tensor`. Must have the same type as `ref`. A tensor of updated values to add to ref. use_locking: An optional `bool`. Defaults to `False`. - An optional bool. Defaults to True. If True, the assignment will - be protected by a lock; otherwise the behavior is undefined, - but may exhibit less contention. + If True, the assignment will be protected by a lock; + otherwise the behavior is undefined, but may exhibit less contention. name: A name for the operation (optional). Returns: -- GitLab From f0f7ed323983ab2cb157bf782950b6e2238b9f6b Mon Sep 17 00:00:00 2001 From: Jacky Ko Date: Tue, 11 Dec 2018 20:35:50 +0800 Subject: [PATCH 048/622] add abseil time library linking --- tensorflow/contrib/cmake/external/abseil_cpp.cmake | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tensorflow/contrib/cmake/external/abseil_cpp.cmake b/tensorflow/contrib/cmake/external/abseil_cpp.cmake index b85fd48f0f..6c6a5df7f7 100644 --- a/tensorflow/contrib/cmake/external/abseil_cpp.cmake +++ b/tensorflow/contrib/cmake/external/abseil_cpp.cmake @@ -31,8 +31,8 @@ if (systemlib_ABSEIL_CPP) message(STATUS " abseil_cpp includes: ${ABSEIL_CPP_INCLUDE_DIR}") message(STATUS " abseil_cpp libraries: ${ABSEIL_CPP_LIBRARIES}") - add_custom_target(abseil_cpp) - list(APPEND tensorflow_EXTERNAL_DEPENDENCIES abseil_cpp) + add_custom_target(abseil_cpp_build) + list(APPEND tensorflow_EXTERNAL_DEPENDENCIES abseil_cpp_build) else (systemlib_ABSEIL_CPP) @@ -53,6 +53,7 @@ else (systemlib_ABSEIL_CPP) ${abseil_cpp_BUILD}/absl/numeric/Release/absl_int128.lib ${abseil_cpp_BUILD}/absl/strings/Release/absl_strings.lib ${abseil_cpp_BUILD}/absl/strings/Release/str_format_internal.lib + ${abseil_cpp_BUILD}/absl/time/Release/absl_time.lib ${abseil_cpp_BUILD}/absl/types/Release/absl_bad_optional_access.lib) else() set(abseil_cpp_STATIC_LIBRARIES @@ -64,6 +65,7 @@ else (systemlib_ABSEIL_CPP) ${abseil_cpp_BUILD}/absl/numeric/absl_int128.lib ${abseil_cpp_BUILD}/absl/strings/absl_strings.lib ${abseil_cpp_BUILD}/absl/strings/str_format_internal.lib + ${abseil_cpp_BUILD}/absl/time/absl_time.lib ${abseil_cpp_BUILD}/absl/types/absl_bad_optional_access.lib) endif() else() @@ -76,14 +78,18 @@ else (systemlib_ABSEIL_CPP) ${abseil_cpp_BUILD}/absl/numeric/libabsl_int128.a ${abseil_cpp_BUILD}/absl/strings/libabsl_strings.a ${abseil_cpp_BUILD}/absl/strings/libstr_format_internal.a + ${abseil_cpp_BUILD}/absl/time/libabsl_time.a ${abseil_cpp_BUILD}/absl/types/libabsl_bad_optional_access.a) endif() - ExternalProject_Add(abseil_cpp + ExternalProject_Add(abseil_cpp_build PREFIX abseil_cpp GIT_REPOSITORY ${abseil_cpp_URL} DOWNLOAD_DIR "${DOWNLOAD_LOCATION}" + BUILD_IN_SOURCE 1 BUILD_BYPRODUCTS ${abseil_cpp_STATIC_LIBRARIES} + BUILD_COMMAND ${CMAKE_COMMAND} --build . --config Release + COMMAND ${CMAKE_COMMAND} --build . --config Release INSTALL_COMMAND "" CMAKE_CACHE_ARGS -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=${tensorflow_ENABLE_POSITION_INDEPENDENT_CODE} @@ -96,6 +102,6 @@ else (systemlib_ABSEIL_CPP) list(APPEND tensorflow_EXTERNAL_LIBRARIES ${abseil_cpp_STATIC_LIBRARIES}) - list(APPEND tensorflow_EXTERNAL_DEPENDENCIES abseil_cpp) + list(APPEND tensorflow_EXTERNAL_DEPENDENCIES abseil_cpp_build) endif (systemlib_ABSEIL_CPP) \ No newline at end of file -- GitLab From 9e91a0899f116e8bfec46922221c45765346ce2e Mon Sep 17 00:00:00 2001 From: Yves-Noel Weweler Date: Wed, 12 Dec 2018 15:27:09 +0100 Subject: [PATCH 049/622] Update tensorflow.data.experimental goldens for v1 Add the drop_remainder argument for bucket_by_sequence_length to the goldens. --- .../tools/api/golden/v1/tensorflow.data.experimental.pbtxt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.pbtxt index ad10b82283..b8ba3e341f 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.data.experimental.pbtxt @@ -54,7 +54,7 @@ tf_module { } member_method { name: "bucket_by_sequence_length" - argspec: "args=[\'element_length_func\', \'bucket_boundaries\', \'bucket_batch_sizes\', \'padded_shapes\', \'padding_values\', \'pad_to_bucket_boundary\', \'no_padding\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'False\', \'False\'], " + argspec: "args=[\'element_length_func\', \'bucket_boundaries\', \'bucket_batch_sizes\', \'padded_shapes\', \'padding_values\', \'pad_to_bucket_boundary\', \'no_padding\', \'drop_remainder\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'False\', \'False\', \'False\'], " } member_method { name: "choose_from_datasets" -- GitLab From c266087d4d086913194bd621cad125853eca8fd6 Mon Sep 17 00:00:00 2001 From: Yves-Noel Weweler Date: Wed, 12 Dec 2018 15:29:25 +0100 Subject: [PATCH 050/622] Update tensorflow.data.experimental goldens for v2 Add the drop_remainder argument for bucket_by_sequence_length to the goldens. --- .../tools/api/golden/v2/tensorflow.data.experimental.pbtxt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.pbtxt index ad10b82283..b8ba3e341f 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.data.experimental.pbtxt @@ -54,7 +54,7 @@ tf_module { } member_method { name: "bucket_by_sequence_length" - argspec: "args=[\'element_length_func\', \'bucket_boundaries\', \'bucket_batch_sizes\', \'padded_shapes\', \'padding_values\', \'pad_to_bucket_boundary\', \'no_padding\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'False\', \'False\'], " + argspec: "args=[\'element_length_func\', \'bucket_boundaries\', \'bucket_batch_sizes\', \'padded_shapes\', \'padding_values\', \'pad_to_bucket_boundary\', \'no_padding\', \'drop_remainder\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'False\', \'False\', \'False\'], " } member_method { name: "choose_from_datasets" -- GitLab From f0ffba31ed278e2ada5537b54575ea05af1091a9 Mon Sep 17 00:00:00 2001 From: Siju Date: Thu, 13 Dec 2018 10:49:46 +0530 Subject: [PATCH 051/622] Update output_init_files_test.py --- tensorflow/python/tools/api/generator/output_init_files_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/python/tools/api/generator/output_init_files_test.py b/tensorflow/python/tools/api/generator/output_init_files_test.py index ab154af910..7013f007e5 100644 --- a/tensorflow/python/tools/api/generator/output_init_files_test.py +++ b/tensorflow/python/tools/api/generator/output_init_files_test.py @@ -45,7 +45,7 @@ def _get_modules(package, attr_name, constants_attr_name): API constant names. Returns: - Set of TensorFow API modules. + Set of TensorFlow API modules. """ modules = set() # TODO(annarev): split up the logic in create_python_api.py so that -- GitLab From 9d5ac5c511dca4a959014b6f9882309bc9bd703f Mon Sep 17 00:00:00 2001 From: Yves-Noel Weweler Date: Thu, 13 Dec 2018 22:40:14 +0100 Subject: [PATCH 052/622] Fix typos --- tensorflow/python/data/experimental/ops/grouping.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tensorflow/python/data/experimental/ops/grouping.py b/tensorflow/python/data/experimental/ops/grouping.py index 71e4b3391f..2eef36d0c8 100644 --- a/tensorflow/python/data/experimental/ops/grouping.py +++ b/tensorflow/python/data/experimental/ops/grouping.py @@ -162,8 +162,8 @@ def bucket_by_sequence_length(element_length_func, no_padding: `bool`, indicates whether to pad the batch features (features need to be either of type `tf.SparseTensor` or of same shape). drop_remainder: (Optional.) A `tf.bool` scalar `tf.Tensor`, representing - whether the last batch should be dropped in the case its has fewer than - batch_size` elements; the default behavior is not to drop the smaller + whether the last batch should be dropped in the case it has fewer than + `batch_size` elements; the default behavior is not to drop the smaller batch. Returns: -- GitLab From c8c5f69e9a4d2424ba4cc603daebd4c24e5128d4 Mon Sep 17 00:00:00 2001 From: Asim Shankar Date: Thu, 13 Dec 2018 14:45:48 -0800 Subject: [PATCH 053/622] [Go]: Fixup paths to protocol buffers. Without this: go generate github.com/tensorflow/tensorflow/tensorflow/go/op would fail with: ../genop/internal/api_def_map.go:34:2: cannot find package "github.com/tensorflow/tensorflow/tensorflow/go/genop/internal/proto/tensorflow/core/framework_go_proto" in any of: /usr/local/go/src/github.com/tensorflow/tensorflow/tensorflow/go/genop/internal/proto/tensorflow/core/framework_go_proto (from $GOROOT) /go/src/github.com/tensorflow/tensorflow/tensorflow/go/genop/internal/proto/tensorflow/core/framework_go_proto (from $GOPATH) This breakage was probably introduced by https://github.com/tensorflow/tensorflow/pull/17262 --- tensorflow/go/genop/internal/api_def_map.go | 2 +- tensorflow/go/genop/internal/genop.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tensorflow/go/genop/internal/api_def_map.go b/tensorflow/go/genop/internal/api_def_map.go index 8600452b47..0bbd88b61c 100644 --- a/tensorflow/go/genop/internal/api_def_map.go +++ b/tensorflow/go/genop/internal/api_def_map.go @@ -31,7 +31,7 @@ import ( "unsafe" "github.com/golang/protobuf/proto" - pb "github.com/tensorflow/tensorflow/tensorflow/go/genop/internal/proto/tensorflow/core/framework_go_proto" + pb "github.com/tensorflow/tensorflow/tensorflow/go/genop/internal/proto/github.com/tensorflow/tensorflow/tensorflow/go/core/framework" ) // Encapsulates a collection of API definitions. diff --git a/tensorflow/go/genop/internal/genop.go b/tensorflow/go/genop/internal/genop.go index fb81631218..1c05715a1a 100644 --- a/tensorflow/go/genop/internal/genop.go +++ b/tensorflow/go/genop/internal/genop.go @@ -47,7 +47,7 @@ import ( "unsafe" "github.com/golang/protobuf/proto" - pb "github.com/tensorflow/tensorflow/tensorflow/go/genop/internal/proto/tensorflow/core/framework_go_proto" + pb "github.com/tensorflow/tensorflow/tensorflow/go/genop/internal/proto/github.com/tensorflow/tensorflow/tensorflow/go/core/framework" ) // GenerateFunctionsForRegisteredOps writes a Go source code file to w -- GitLab From 1674bd08ce0c49a75a0f01c18ef940fa012124a8 Mon Sep 17 00:00:00 2001 From: Ben Barsdell Date: Thu, 13 Dec 2018 15:09:54 -0800 Subject: [PATCH 054/622] [XLA:GPU] Add sm_75 to LLVM supported procs list - This is required to prevent fallback to the default sm version when running on GPUs with the Turing architecture. --- .../xla/service/gpu/llvm_gpu_backend/nvptx_backend_lib.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/tensorflow/compiler/xla/service/gpu/llvm_gpu_backend/nvptx_backend_lib.cc b/tensorflow/compiler/xla/service/gpu/llvm_gpu_backend/nvptx_backend_lib.cc index bd53b90b42..eddaa877f2 100644 --- a/tensorflow/compiler/xla/service/gpu/llvm_gpu_backend/nvptx_backend_lib.cc +++ b/tensorflow/compiler/xla/service/gpu/llvm_gpu_backend/nvptx_backend_lib.cc @@ -125,6 +125,7 @@ static string GetSmName(std::pair compute_capability) { {{6, 2}, 62}, {{7, 0}, 70}, {{7, 2}, 72}, + {{7, 5}, 75}, }); int sm_version = 30; auto it = m->find(compute_capability); -- GitLab From c1fc5d5036d850be852ec473f32292ca973f5bed Mon Sep 17 00:00:00 2001 From: Yves-Noel Weweler Date: Thu, 13 Dec 2018 22:57:22 +0100 Subject: [PATCH 055/622] Rewrite grouping tests to be @parameterized tests --- .../data/experimental/kernel_tests/BUILD | 1 + .../bucket_by_sequence_length_test.py | 60 ++++++++++++------- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/tensorflow/python/data/experimental/kernel_tests/BUILD b/tensorflow/python/data/experimental/kernel_tests/BUILD index d7ca5a70e4..897e949b0f 100644 --- a/tensorflow/python/data/experimental/kernel_tests/BUILD +++ b/tensorflow/python/data/experimental/kernel_tests/BUILD @@ -23,6 +23,7 @@ py_test( "//tensorflow/python/data/kernel_tests:test_base", "//tensorflow/python/data/ops:dataset_ops", "//third_party/py/numpy", + "@absl_py//absl/testing:parameterized", ], ) diff --git a/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py b/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py index fab79619a0..bcb7ef9496 100644 --- a/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py @@ -19,6 +19,8 @@ from __future__ import print_function import random +from absl.testing import parameterized + from tensorflow.python.data.experimental.ops import grouping from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops @@ -69,9 +71,13 @@ def _get_record_shape(sparse): return tensor_shape.TensorShape([None]) -class BucketBySequenceLengthTest(test_base.DatasetTestBase): +class BucketBySequenceLengthTest(test_base.DatasetTestBase, parameterized.TestCase): - def testBucketDropReminder(self): + @parameterized.named_parameters( + ("WithoutPadding", True), + ("WithPadding", False), + ) + def testBucketDropReminder(self, param_no_padding): boundaries = [10, 20, 30] batch_sizes = [10, 8, 4, 2] @@ -192,10 +198,13 @@ class BucketBySequenceLengthTest(test_base.DatasetTestBase): .format(sorted(expected_lengths), sorted(generated_lengths))) - for no_padding in (True, False): - _test_bucket_by_padding(no_padding) + _test_bucket_by_padding(param_no_padding) - def testBucket(self): + @parameterized.named_parameters( + ("WithoutPadding", True), + ("WithPadding", False), + ) + def testBucket(self, param_no_padding): boundaries = [10, 20, 30] batch_sizes = [10, 8, 4, 2] @@ -251,8 +260,7 @@ class BucketBySequenceLengthTest(test_base.DatasetTestBase): self.assertEqual(sorted(batch_sizes), sorted(batch_sizes_val)) self.assertEqual(sorted(lengths), sorted(lengths_val)) - for no_padding in (True, False): - _test_bucket_by_padding(no_padding) + _test_bucket_by_padding(param_no_padding) def testPadToBoundary(self): @@ -336,7 +344,11 @@ class BucketBySequenceLengthTest(test_base.DatasetTestBase): self.assertAllEqual(batches[4], [[1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]) - def testTupleElements(self): + @parameterized.named_parameters( + ("WithoutPadding", True), + ("WithPadding", False), + ) + def testTupleElements(self, param_no_padding): def build_dataset(sparse): def _generator(): @@ -364,10 +376,13 @@ class BucketBySequenceLengthTest(test_base.DatasetTestBase): self.assertEqual([None, None], shapes[0].as_list()) self.assertEqual([None], shapes[1].as_list()) - for no_padding in (True, False): - _test_tuple_elements_by_padding(no_padding) + _test_tuple_elements_by_padding(param_no_padding) - def testBucketSparse(self): + @parameterized.named_parameters( + ("DoDropRemainder", True), + ("DoNotDropRemainder", False), + ) + def testBucketSparse(self, param_drop_remainder): """Tests bucketing of sparse tensors (case where `no_padding` == True). Test runs on following dataset: @@ -435,18 +450,17 @@ class BucketBySequenceLengthTest(test_base.DatasetTestBase): all_sparse_tensors.add(sprs_tensor) return all_sparse_tensors - for drop_remainder in (True, False): - dataset = _build_dataset() - boundaries = range(min_len + bucket_size + 1, max_len, bucket_size) - dataset = dataset.apply(grouping.bucket_by_sequence_length( - _element_length_fn, - boundaries, - [batch_size] * (len(boundaries) + 1), - no_padding=True, - drop_remainder=drop_remainder)) - batches = _compute_batches(dataset) - expected_batches = _compute_expected_batches(drop_remainder) - self.assertEqual(batches, expected_batches) + dataset = _build_dataset() + boundaries = range(min_len + bucket_size + 1, max_len, bucket_size) + dataset = dataset.apply(grouping.bucket_by_sequence_length( + _element_length_fn, + boundaries, + [batch_size] * (len(boundaries) + 1), + no_padding=True, + drop_remainder=param_drop_remainder)) + batches = _compute_batches(dataset) + expected_batches = _compute_expected_batches(param_drop_remainder) + self.assertEqual(batches, expected_batches) if __name__ == "__main__": -- GitLab From 3eb35ec8372abc93a043146e55b936392c2ba54c Mon Sep 17 00:00:00 2001 From: Pete Warden Date: Mon, 17 Dec 2018 14:13:52 -0800 Subject: [PATCH 056/622] Delete test_ecm3531_binary.sh Causes a missing-copyright error on internal verification --- .../lite/experimental/micro/testing/test_ecm3531_binary.sh | 1 - 1 file changed, 1 deletion(-) delete mode 100755 tensorflow/lite/experimental/micro/testing/test_ecm3531_binary.sh diff --git a/tensorflow/lite/experimental/micro/testing/test_ecm3531_binary.sh b/tensorflow/lite/experimental/micro/testing/test_ecm3531_binary.sh deleted file mode 100755 index 6d7af08d9f..0000000000 --- a/tensorflow/lite/experimental/micro/testing/test_ecm3531_binary.sh +++ /dev/null @@ -1 +0,0 @@ -echo "Testing should be performed on the ECM3531 EVB" -- GitLab From 64fa6b3108633fdfb0efbe8398cbf69b2a156cc7 Mon Sep 17 00:00:00 2001 From: Arpit Shah Date: Tue, 18 Dec 2018 08:18:25 -0600 Subject: [PATCH 057/622] Changed copy right headers --- .../examples/micro_speech/CMSIS/hanning.cc | 28 +++++++++---------- .../examples/micro_speech/CMSIS/hanning.h | 28 +++++++++---------- .../examples/micro_speech/CMSIS/sin_1k.cc | 28 +++++++++---------- .../examples/micro_speech/CMSIS/sin_1k.h | 28 +++++++++---------- .../apollo3/captured_data_to_wav.py | 13 +++++---- .../micro_speech/apollo3/compare_1k.py | 13 +++++---- .../micro_speech/apollo3/preprocessor_1k.cc | 7 ++--- .../apollo3/preprocessor_1k_cmsis_test.cmd | 15 +++------- .../apollo3/preprocessor_1k_micro_test.cmd | 15 +++------- .../apollo3/preprocessor_test.cmd | 13 ++------- .../apollo3/pushbutton_cmsis_scores.cmd | 15 +++------- .../apollo3/pushbutton_cmsis_voice.cmd | 15 +++++----- .../micro_speech/apollo3/pushbutton_main.c | 7 ++--- .../micro_speech/apollo3/pushbutton_test.cc | 7 ++--- 14 files changed, 99 insertions(+), 133 deletions(-) diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.cc index 32aa5b2b7e..deb30ffd88 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.cc @@ -1,17 +1,17 @@ -/* SPDX-License-Identifier: Apache-2.0 - * - * 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 - * - * 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. - */ +/* Copyright 2018 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/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.h" diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.h b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.h index 0982f33c48..e7d9c5c858 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.h +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.h @@ -1,17 +1,17 @@ -/* SPDX-License-Identifier: Apache-2.0 - * - * 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 - * - * 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. - */ +/* Copyright 2018 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_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_HANNING_H_ #define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_HANNING_H_ diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.cc index be66e5f548..3523d3ab76 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.cc @@ -1,17 +1,17 @@ -/* SPDX-License-Identifier: Apache-2.0 - * - * 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 - * - * 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. - */ +/* Copyright 2018 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/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.h" diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.h b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.h index 645e262aa1..653a6f5830 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.h +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.h @@ -1,17 +1,17 @@ -/* SPDX-License-Identifier: Apache-2.0 - * - * 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 - * - * 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. - */ +/* Copyright 2018 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_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_SIN_1K_H_ #define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_SIN_1K_H_ diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/captured_data_to_wav.py b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/captured_data_to_wav.py index 84b6259452..a04b849656 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/captured_data_to_wav.py +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/captured_data_to_wav.py @@ -1,16 +1,17 @@ -# SPDX-License-Identifier: Apache-2.0 +# Copyright 2018 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. +# 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 # -# www.apache.org/licenses/LICENSE-2.0 +# 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. +# 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. +# ============================================================================== import numpy as np import re diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/compare_1k.py b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/compare_1k.py index 9c91560d50..93fb36fd01 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/compare_1k.py +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/compare_1k.py @@ -1,16 +1,17 @@ -# SPDX-License-Identifier: Apache-2.0 +# Copyright 2018 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. +# 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 # -# www.apache.org/licenses/LICENSE-2.0 +# 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. +# 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. +# ============================================================================== import numpy as np import re diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k.cc index 31fae1f2dc..5246e00296 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k.cc @@ -1,8 +1,3 @@ -/* This file is a modification of the Tensorflow Micro Lite file preprocessor.cc - * We have retained the original copyright and header information, in - * accordance with the Apache 2.0 license terms. - */ - /* Copyright 2018 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,6 +13,8 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ +/* This file is a modification of the Tensorflow Micro Lite file preprocessor.cc */ + #include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.h" #include "tensorflow/lite/c/c_api_internal.h" #include "tensorflow/lite/experimental/micro/micro_error_reporter.h" diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k_cmsis_test.cmd b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k_cmsis_test.cmd index 0f701660a8..5cc7a82c05 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k_cmsis_test.cmd +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k_cmsis_test.cmd @@ -1,16 +1,9 @@ -# SPDX-License-Identifier: Apache-2.0 -# -# 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 -# -# 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. +# 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. +# ============================================================================== # Needs to be compiled with -O0 file ../../../tools/make/gen/apollo3evb_cortex-m4/bin/preprocessor_1k_cmsis_test @@ -37,7 +30,7 @@ commands dump verilog memory cmsis_power_avg.txt output output+42 c end -break preprocessor_1k.cc:53 +break preprocessor_1k.cc:50 commands print count end diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k_micro_test.cmd b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k_micro_test.cmd index 35c602d78c..dc9cd4f0a4 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k_micro_test.cmd +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k_micro_test.cmd @@ -1,16 +1,9 @@ -# SPDX-License-Identifier: Apache-2.0 -# -# 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 -# -# 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. +# 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. +# ============================================================================== # Needs to be run when compiled with -O0 file ../../../tools/make/gen/apollo3evb_cortex-m4/bin/preprocessor_1k_micro_test @@ -25,7 +18,7 @@ dump verilog value micro_power.txt power_spectrum dump verilog memory micro_power_avg.txt output output+42 c end -break preprocessor_1k.cc:53 +break preprocessor_1k.cc:50 commands print count end diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_test.cmd b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_test.cmd index 4458af17d6..bd2048e80a 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_test.cmd +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_test.cmd @@ -1,16 +1,9 @@ -# SPDX-License-Identifier: Apache-2.0 -# -# 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 -# -# 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. +# 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. +# ============================================================================== file ../../gen/apollo3evb_cortex-m4/bin/preprocessor_test target remote localhost:2331 diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_cmsis_scores.cmd b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_cmsis_scores.cmd index db299f7277..e14ea2d83c 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_cmsis_scores.cmd +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_cmsis_scores.cmd @@ -1,22 +1,15 @@ -# SPDX-License-Identifier: Apache-2.0 -# -# 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 -# -# 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. +# 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. +# ============================================================================== file ../../../tools/make/gen/apollo3evb_cortex-m4/bin/pushbutton_cmsis_speech_test target remote localhost:2331 load ../../../tools/make/gen/apollo3evb_cortex-m4/bin/pushbutton_cmsis_speech_test monitor reset -break pushbutton_main.c:325 +break pushbutton_main.c:322 commands printf "Silence score: %d\n", g_silence_score printf "Unknown score: %d\n", g_unknown_score diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_cmsis_voice.cmd b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_cmsis_voice.cmd index 8cd1a7e90e..b49f9d2d92 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_cmsis_voice.cmd +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_cmsis_voice.cmd @@ -1,22 +1,23 @@ -# SPDX-License-Identifier: Apache-2.0 +# Copyright 2018 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. +# 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 # -# www.apache.org/licenses/LICENSE-2.0 +# 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. +# 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. +# ============================================================================== file ../../../tools/make/gen/apollo3evb_cortex-m4/bin/pushbutton_cmsis_speech_test target remote localhost:2331 load ../../../tools/make/gen/apollo3evb_cortex-m4/bin/pushbutton_cmsis_speech_test monitor reset -break pushbutton_main.c:316 +break pushbutton_main.c:313 commands dump verilog value captured_data.txt captured_data c diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_main.c b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_main.c index 82337e63da..d06c81be66 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_main.c +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_main.c @@ -1,8 +1,3 @@ -/* This file is a modification of the Tensorflow Micro Lite file _main.c - * We have retained the original copyright and header information, in - * accordance with the Apache 2.0 license terms. - */ - /* Copyright 2018 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,6 +13,8 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ +/* This file is a modification of the Tensorflow Micro Lite file _main.c */ + #include #include "am_mcu_apollo.h" // Defines AM_CMSIS_REGS #include "am_bsp.h" diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_test.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_test.cc index ce4de4dbd8..47f273c511 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_test.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_test.cc @@ -1,8 +1,3 @@ -/* This file is a modification of the Tensorflow Micro Lite file micro_speech_test.cc - * We have retained the original copyright and header information, in - * accordance with the Apache 2.0 license terms. - */ - /* Copyright 2018 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,6 +13,8 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ +/* This file is a modification of the Tensorflow Micro Lite file micro_speech_test.cc */ + #include "tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.h" #include "tensorflow/lite/c/c_api_internal.h" #include "tensorflow/lite/experimental/micro/micro_error_reporter.h" -- GitLab From 26dbfbd5bba462cb8820566ec2659f221be2968d Mon Sep 17 00:00:00 2001 From: Arpit Shah Date: Tue, 18 Dec 2018 13:54:00 -0600 Subject: [PATCH 058/622] Moved copy right files out of repo --- .../examples/micro_speech/CMSIS/README.md | 7 +- .../CMSIS/arm_cmplx_mag_squared_q10p6.c | 141 - .../CMSIS/arm_cmplx_mag_squared_q10p6.h | 33 - .../micro_speech/CMSIS/preprocessor.cc | 2 +- .../examples/micro_speech/apollo3/README.md | 5 +- .../examples/micro_speech/apollo3/apollo3.h | 23332 ---------------- .../micro_speech/apollo3/preprocessor_1k.cc | 4 +- .../micro_speech/apollo3/system_apollo3.c | 116 - .../micro_speech/apollo3/system_apollo3.h | 72 - .../micro/tools/make/download_dependencies.sh | 5 + .../make/targets/apollo3evb_makefile.inc | 12 +- 11 files changed, 24 insertions(+), 23705 deletions(-) delete mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/arm_cmplx_mag_squared_q10p6.c delete mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/arm_cmplx_mag_squared_q10p6.h delete mode 100755 tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/apollo3.h delete mode 100755 tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/system_apollo3.c delete mode 100755 tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/system_apollo3.h diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/README.md b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/README.md index fde48374c8..6b9339df36 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/README.md +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/README.md @@ -1,10 +1,13 @@ # Description of files -* **arm_cmplx_mag_squared_q10p6.c**: Modified version of the ARM CMSIS function [arm_cmplx_mag_squared.c](http://arm-software.github.io/CMSIS_5/DSP/html/group__cmplx__mag__squared.html#ga45537f576102d960d467eb722b8431f2). The modification is that we have changed the amount of right-shift to make sure our data is in the correct range. We redistribute because the original content was created with the Apache 2.0 license. -* **arm_cmplx_mag_squared_q10p6.h**: Header file for arm_cmplx_mag_squared_q10p6.c * **create_constants.py**: Python file used to create hanning.cc, hanning.h, sin_1k.cc, and sin_1k.h * **hanning.cc**: Precomputed [Hann window](https://en.wikipedia.org/wiki/Hann_function) for use in the preprocessor. This file is created in ../create_constants.py * **hanning.h**: Header file fro hanning.cc * **preprocessor.cc**: CMSIS version of the preprocessor * **sin_1k.cc**: A 1 kHZ sinusoid used for comparing the CMSIS preprocessor with the Micro-Lite fixed_point preprocessor * **sin_1k.h**: Header file for sin_1k.cc + +# Description of externally downloaded files in ../CMSIS_ext + +* **arm_cmplx_mag_squared_q10p6.c**: Modified version of the ARM CMSIS function [arm_cmplx_mag_squared.c](http://arm-software.github.io/CMSIS_5/DSP/html/group__cmplx__mag__squared.html#ga45537f576102d960d467eb722b8431f2). The modification is that we have changed the amount of right-shift to make sure our data is in the correct range. We redistribute because the original content was created with the Apache 2.0 license. +* **arm_cmplx_mag_squared_q10p6.h**: Header file for arm_cmplx_mag_squared_q10p6.c diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/arm_cmplx_mag_squared_q10p6.c b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/arm_cmplx_mag_squared_q10p6.c deleted file mode 100644 index b050f6048d..0000000000 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/arm_cmplx_mag_squared_q10p6.c +++ /dev/null @@ -1,141 +0,0 @@ -/* This file is a modification of the ARM CMSIS library file arm_cmplx_mag_squared_q15.c - * We have retained the original copyright and header information, in - * accordance with the Apache 2.0 license terms. - */ - -/* ---------------------------------------------------------------------- - * Project: CMSIS DSP Library - * Title: arm_cmplx_mag_squared_q15.c - * Description: Q15 complex magnitude squared - * - * $Date: 27. January 2017 - * $Revision: V.1.5.1 - * - * Target Processor: Cortex-M cores - * -------------------------------------------------------------------- */ -/* - * Copyright (C) 2010-2017 ARM Limited or its affiliates. All rights reserved. - * - * SPDX-License-Identifier: Apache-2.0 - * - * 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 - * - * 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 "arm_math.h" - -/** - * @ingroup groupCmplxMath - */ - -/** - * @addtogroup cmplx_mag_squared - * @{ - */ - -/** - * @brief Q15 complex magnitude squared - * @param *pSrc points to the complex input vector - * @param *pDst points to the real output vector - * @param numSamples number of complex samples in the input vector - * @return none. - * - * Scaling and Overflow Behavior: - * \par - * The function implements 1.15 by 1.15 multiplications and finally output is converted into 3.13 format. - */ - -void arm_cmplx_mag_squared_q10p6( - q15_t * pSrc, - q15_t * pDst, - uint32_t numSamples) -{ - q31_t acc0, acc1; /* Accumulators */ - -#if defined (ARM_MATH_DSP) - - /* Run the below code for Cortex-M4 and Cortex-M3 */ - uint32_t blkCnt; /* loop counter */ - q31_t in1, in2, in3, in4; - q31_t acc2, acc3; - - /*loop Unrolling */ - blkCnt = numSamples >> 2U; - - /* First part of the processing with loop unrolling. Compute 4 outputs at a time. - ** a second loop below computes the remaining 1 to 3 samples. */ - while (blkCnt > 0U) - { - /* C[0] = (A[0] * A[0] + A[1] * A[1]) */ - in1 = *__SIMD32(pSrc)++; - in2 = *__SIMD32(pSrc)++; - in3 = *__SIMD32(pSrc)++; - in4 = *__SIMD32(pSrc)++; - - acc0 = __SMUAD(in1, in1); - acc1 = __SMUAD(in2, in2); - acc2 = __SMUAD(in3, in3); - acc3 = __SMUAD(in4, in4); - - /* store the result in 3.13 format in the destination buffer. */ - *pDst++ = (q15_t) (acc0 >> 6); - *pDst++ = (q15_t) (acc1 >> 6); - *pDst++ = (q15_t) (acc2 >> 6); - *pDst++ = (q15_t) (acc3 >> 6); - - /* Decrement the loop counter */ - blkCnt--; - } - - /* If the numSamples is not a multiple of 4, compute any remaining output samples here. - ** No loop unrolling is used. */ - blkCnt = numSamples % 0x4U; - - while (blkCnt > 0U) - { - /* C[0] = (A[0] * A[0] + A[1] * A[1]) */ - in1 = *__SIMD32(pSrc)++; - acc0 = __SMUAD(in1, in1); - - /* store the result in 3.13 format in the destination buffer. */ - *pDst++ = (q15_t) (acc0 >> 6); - - /* Decrement the loop counter */ - blkCnt--; - } - -#else - - /* Run the below code for Cortex-M0 */ - q15_t real, imag; /* Temporary variables to store real and imaginary values */ - - while (numSamples > 0U) - { - /* out = ((real * real) + (imag * imag)) */ - real = *pSrc++; - imag = *pSrc++; - acc0 = (real * real); - acc1 = (imag * imag); - /* store the result in 3.13 format in the destination buffer. */ - *pDst++ = (q15_t) (((q63_t) acc0 + acc1) >> 6); - - /* Decrement the loop counter */ - numSamples--; - } - -#endif /* #if defined (ARM_MATH_DSP) */ - -} - -/** - * @} end of cmplx_mag_squared group - */ diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/arm_cmplx_mag_squared_q10p6.h b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/arm_cmplx_mag_squared_q10p6.h deleted file mode 100644 index 24144615cc..0000000000 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/arm_cmplx_mag_squared_q10p6.h +++ /dev/null @@ -1,33 +0,0 @@ -/* This file is a modification of the ARM CMSIS library file arm_math.h - * We have retained the original copyright and header information, in - * accordance with the Apache 2.0 license terms. - */ - -/****************************************************************************** - * @file arm_math.h - * @brief Public header file for CMSIS DSP LibraryU - * @version V1.5.3 - * @date 10. January 2018 - ******************************************************************************/ -/* - * Copyright (C) 2010-2017 ARM Limited or its affiliates. All rights reserved. - * - * SPDX-License-Identifier: Apache-2.0 - * - * 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 - * - * 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. - */ - -void arm_cmplx_mag_squared_q10p6( - q15_t * pSrc, - q15_t * pDst, - uint32_t numSamples); diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.cc index 6bc3c4cb77..3e31f96aa3 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.cc @@ -21,7 +21,7 @@ extern "C" { #define FFT_SIZE_DIV2 256 #include #include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.h" - #include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/arm_cmplx_mag_squared_q10p6.h" + #include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS_ext/arm_cmplx_mag_squared_q10p6.h" } #include "tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.h" diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/README.md b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/README.md index 66fa176a9b..d3cfb88028 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/README.md +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/README.md @@ -46,7 +46,6 @@ # Description of files * **.gitignore**: Git should ignore \*.txt and \*.wav files that result from experiments run in this directory -* **apollo3.h**: Apollo 3 version of the [CMSIS Device Header File (device.h)](https://www.keil.com/pack/doc/CMSIS/Core/html/device_h_pg.html). Available in the [Ambiq Keil Pack](http://s3.ambiqmicro.com/pack/AmbiqMicro.Apollo_DFP.1.1.0.pack). * **captured\_data\_to\_wav.py**: Python script that parses a text file containing data dumped from GDB (specifically the verilog format) and creates a \*.wav file using [PySoundFile](https://pysoundfile.readthedocs.io/en/0.9.0/). * **compare\_1k.py**: This script compares the intermediate variables and final outputs of the micro-lite fixed-point preprocessor function and the CMSIS version of this function. The stimulus provided to each preprocessor is the same: a 1 kHz sinusoid. * **get\_yesno\_data.cmd**: A GDB command file that runs preprocessor_test (where TARGET=apollo3evb) and dumps the calculated data for the "yes" and "no" input wavfeorms to text files @@ -59,6 +58,10 @@ * **pushbutton_cmsis_voice.cmd**: GDB command file that runs pushbutton_cmsis_speech_test_bin. Dumps the recorded 1 second of audio to captured_data.txt, which can then be processed by the python file captured_data_to_wav.py. * **pushbutton_main.c**: Source file containing program point of entry \_main() for the pushbutton\_\* tests. Contains Interrupt Service Routines for PDM data capture and pushbuttons. Calls the main() function of pushbutton_test.cc * **pushbutton_test.cc**: Source file containing main() function for the pushbutton\_\* tests. main() calls the preprocessor function and the neural net inference function. + +# Description of externally downloaded files in ../apollo3_ext + +* **apollo3.h**: Apollo 3 version of the [CMSIS Device Header File (device.h)](https://www.keil.com/pack/doc/CMSIS/Core/html/device_h_pg.html). Available in the [Ambiq Keil Pack](http://s3.ambiqmicro.com/pack/AmbiqMicro.Apollo_DFP.1.1.0.pack). * **system_apollo3.c**: Apollo 3 version of the [CMSIS System Configuration File system\_\.c](https://www.keil.com/pack/doc/CMSIS/Core/html/system_c_pg.html). Available in the [Ambiq Keil Pack](http://s3.ambiqmicro.com/pack/AmbiqMicro.Apollo_DFP.1.1.0.pack). * **system_apollo3.h**: Apollo 3 version of the [CMSIS System Configuration File system\_\.h](https://www.keil.com/pack/doc/CMSIS/Core/html/system_c_pg.html). Available in the [Ambiq Keil Pack](http://s3.ambiqmicro.com/pack/AmbiqMicro.Apollo_DFP.1.1.0.pack). diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/apollo3.h b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/apollo3.h deleted file mode 100755 index af22270e32..0000000000 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/apollo3.h +++ /dev/null @@ -1,23332 +0,0 @@ -/* - * Copyright (C) 2015-2017, Ambiq Micro - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of itscontributors may be used to endorse - * or promote products derived from thissoftware without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @file apollo3.h - * @brief CMSIS HeaderFile - * @version 1.0 - * @date 10. August 2018 - * @note Generated by SVDConv V3.3.18 on Friday, 10.08.2018 16:52:09 - * from File 'apollo3.svd', - * last modified on Friday, 10.08.2018 20:01:31 - */ - - - -/** @addtogroup Ambiq Micro - * @{ - */ - - -/** @addtogroup apollo3 - * @{ - */ - - -#ifndef APOLLO3_H -#define APOLLO3_H - -#ifdef __cplusplus -extern "C" { -#endif - - -/** @addtogroup Configuration_of_CMSIS - * @{ - */ - - - -/* =========================================================================================================================== */ -/* ================ Interrupt Number Definition ================ */ -/* =========================================================================================================================== */ - -typedef enum { -/* ======================================= ARM Cortex-M4 Specific Interrupt Numbers ======================================== */ - Reset_IRQn = -15, /*!< -15 Reset Vector, invoked on Power up and warm reset */ - NonMaskableInt_IRQn = -14, /*!< -14 Non maskable Interrupt, cannot be stopped or preempted */ - HardFault_IRQn = -13, /*!< -13 Hard Fault, all classes of Fault */ - MemoryManagement_IRQn = -12, /*!< -12 Memory Management, MPU mismatch, including Access Violation - and No Match */ - BusFault_IRQn = -11, /*!< -11 Bus Fault, Pre-Fetch-, Memory Access Fault, other address/memory - related Fault */ - UsageFault_IRQn = -10, /*!< -10 Usage Fault, i.e. Undef Instruction, Illegal State Transition */ - SVCall_IRQn = -5, /*!< -5 System Service Call via SVC instruction */ - DebugMonitor_IRQn = -4, /*!< -4 Debug Monitor */ - PendSV_IRQn = -2, /*!< -2 Pendable request for system service */ - SysTick_IRQn = -1, /*!< -1 System Tick Timer */ -/* ========================================== apollo3 Specific Interrupt Numbers =========================================== */ - BROWNOUT_IRQn = 0, /*!< 0 BROWNOUT */ - WDT_IRQn = 1, /*!< 1 WDT */ - RTC_IRQn = 2, /*!< 2 RTC */ - VCOMP_IRQn = 3, /*!< 3 VCOMP */ - IOSLAVE_IRQn = 4, /*!< 4 IOSLAVE */ - IOSLAVEACC_IRQn = 5, /*!< 5 IOSLAVEACC */ - IOMSTR0_IRQn = 6, /*!< 6 IOMSTR0 */ - IOMSTR1_IRQn = 7, /*!< 7 IOMSTR1 */ - IOMSTR2_IRQn = 8, /*!< 8 IOMSTR2 */ - IOMSTR3_IRQn = 9, /*!< 9 IOMSTR3 */ - IOMSTR4_IRQn = 10, /*!< 10 IOMSTR4 */ - IOMSTR5_IRQn = 11, /*!< 11 IOMSTR5 */ - BLE_IRQn = 12, /*!< 12 BLE */ - GPIO_IRQn = 13, /*!< 13 GPIO */ - CTIMER_IRQn = 14, /*!< 14 CTIMER */ - UART0_IRQn = 15, /*!< 15 UART0 */ - UART1_IRQn = 16, /*!< 16 UART1 */ - SCARD_IRQn = 17, /*!< 17 SCARD */ - ADC_IRQn = 18, /*!< 18 ADC */ - PDM_IRQn = 19, /*!< 19 PDM */ - MSPI_IRQn = 20, /*!< 20 MSPI */ - STIMER_IRQn = 22, /*!< 22 STIMER */ - STIMER_CMPR0_IRQn = 23, /*!< 23 STIMER_CMPR0 */ - STIMER_CMPR1_IRQn = 24, /*!< 24 STIMER_CMPR1 */ - STIMER_CMPR2_IRQn = 25, /*!< 25 STIMER_CMPR2 */ - STIMER_CMPR3_IRQn = 26, /*!< 26 STIMER_CMPR3 */ - STIMER_CMPR4_IRQn = 27, /*!< 27 STIMER_CMPR4 */ - STIMER_CMPR5_IRQn = 28, /*!< 28 STIMER_CMPR5 */ - STIMER_CMPR6_IRQn = 29, /*!< 29 STIMER_CMPR6 */ - STIMER_CMPR7_IRQn = 30, /*!< 30 STIMER_CMPR7 */ - CLKGEN_IRQn = 31 /*!< 31 CLKGEN */ -} IRQn_Type; - - - -/* =========================================================================================================================== */ -/* ================ Processor and Core Peripheral Section ================ */ -/* =========================================================================================================================== */ - -/* =========================== Configuration of the ARM Cortex-M4 Processor and Core Peripherals =========================== */ -#define __CM4_REV 0x0100U /*!< CM4 Core Revision */ -#define __NVIC_PRIO_BITS 3 /*!< Number of Bits used for Priority Levels */ -#define __Vendor_SysTickConfig 0 /*!< Set to 1 if different SysTick Config is used */ -#define __MPU_PRESENT 1 /*!< MPU present or not */ -#define __FPU_PRESENT 1 /*!< FPU present or not */ - - -/** @} */ /* End of group Configuration_of_CMSIS */ - -#include "core_cm4.h" /*!< ARM Cortex-M4 processor and core peripherals */ -#include "system_apollo3.h" /*!< apollo3 System */ - -#ifndef __IM /*!< Fallback for older CMSIS versions */ - #define __IM __I -#endif -#ifndef __OM /*!< Fallback for older CMSIS versions */ - #define __OM __O -#endif -#ifndef __IOM /*!< Fallback for older CMSIS versions */ - #define __IOM __IO -#endif - - -/* ======================================== Start of section using anonymous unions ======================================== */ -#if defined (__CC_ARM) - #pragma push - #pragma anon_unions -#elif defined (__ICCARM__) - #pragma language=extended -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wc11-extensions" - #pragma clang diagnostic ignored "-Wreserved-id-macro" - #pragma clang diagnostic ignored "-Wgnu-anonymous-struct" - #pragma clang diagnostic ignored "-Wnested-anon-types" -#elif defined (__GNUC__) - /* anonymous unions are enabled by default */ -#elif defined (__TMS470__) - /* anonymous unions are enabled by default */ -#elif defined (__TASKING__) - #pragma warning 586 -#elif defined (__CSMC__) - /* anonymous unions are enabled by default */ -#else - #warning Not supported compiler type -#endif - - -/* =========================================================================================================================== */ -/* ================ Device Specific Peripheral Section ================ */ -/* =========================================================================================================================== */ - - -/** @addtogroup Device_Peripheral_peripherals - * @{ - */ - - - -/* =========================================================================================================================== */ -/* ================ ADC ================ */ -/* =========================================================================================================================== */ - - -/** - * @brief Analog Digital Converter Control (ADC) - */ - -typedef struct { /*!< (@ 0x50010000) ADC Structure */ - - union { - __IOM uint32_t CFG; /*!< (@ 0x00000000) Configuration Register */ - - struct { - __IOM uint32_t ADCEN : 1; /*!< [0..0] This bit enables the ADC module. While the ADC is enabled, - the ADCCFG and SLOT Configuration regsiter settings must - remain stable and unchanged. All configuration register - settings, slot configuration settings and window comparison - settings should be written prior to setting the ADCEN bit - to '1'. */ - __IM uint32_t : 1; - __IOM uint32_t RPTEN : 1; /*!< [2..2] This bit enables Repeating Scan Mode. */ - __IOM uint32_t LPMODE : 1; /*!< [3..3] Select power mode to enter between active scans. */ - __IOM uint32_t CKMODE : 1; /*!< [4..4] Clock mode register */ - __IM uint32_t : 3; - __IOM uint32_t REFSEL : 2; /*!< [9..8] Select the ADC reference voltage. */ - __IM uint32_t : 2; - __IOM uint32_t DFIFORDEN : 1; /*!< [12..12] Destructive FIFO Read Enable. Setting this will enable - FIFO pop upon reading the FIFOPR register. */ - __IM uint32_t : 3; - __IOM uint32_t TRIGSEL : 3; /*!< [18..16] Select the ADC trigger source. */ - __IOM uint32_t TRIGPOL : 1; /*!< [19..19] This bit selects the ADC trigger polarity for external - off chip triggers. */ - __IM uint32_t : 4; - __IOM uint32_t CLKSEL : 2; /*!< [25..24] Select the source and frequency for the ADC clock. - All values not enumerated below are undefined. */ - } CFG_b; - } ; - - union { - __IOM uint32_t STAT; /*!< (@ 0x00000004) ADC Power Status */ - - struct { - __IOM uint32_t PWDSTAT : 1; /*!< [0..0] Indicates the power-status of the ADC. */ - } STAT_b; - } ; - - union { - __IOM uint32_t SWT; /*!< (@ 0x00000008) Software trigger */ - - struct { - __IOM uint32_t SWT : 8; /*!< [7..0] Writing 0x37 to this register generates a software trigger. */ - } SWT_b; - } ; - - union { - __IOM uint32_t SL0CFG; /*!< (@ 0x0000000C) Slot 0 Configuration Register */ - - struct { - __IOM uint32_t SLEN0 : 1; /*!< [0..0] This bit enables slot 0 for ADC conversions. */ - __IOM uint32_t WCEN0 : 1; /*!< [1..1] This bit enables the window compare function for slot - 0. */ - __IM uint32_t : 6; - __IOM uint32_t CHSEL0 : 4; /*!< [11..8] Select one of the 14 channel inputs for this slot. */ - __IM uint32_t : 4; - __IOM uint32_t PRMODE0 : 2; /*!< [17..16] Set the Precision Mode For Slot. */ - __IM uint32_t : 6; - __IOM uint32_t ADSEL0 : 3; /*!< [26..24] Select the number of measurements to average in the - accumulate divide module for this slot. */ - } SL0CFG_b; - } ; - - union { - __IOM uint32_t SL1CFG; /*!< (@ 0x00000010) Slot 1 Configuration Register */ - - struct { - __IOM uint32_t SLEN1 : 1; /*!< [0..0] This bit enables slot 1 for ADC conversions. */ - __IOM uint32_t WCEN1 : 1; /*!< [1..1] This bit enables the window compare function for slot - 1. */ - __IM uint32_t : 6; - __IOM uint32_t CHSEL1 : 4; /*!< [11..8] Select one of the 14 channel inputs for this slot. */ - __IM uint32_t : 4; - __IOM uint32_t PRMODE1 : 2; /*!< [17..16] Set the Precision Mode For Slot. */ - __IM uint32_t : 6; - __IOM uint32_t ADSEL1 : 3; /*!< [26..24] Select the number of measurements to average in the - accumulate divide module for this slot. */ - } SL1CFG_b; - } ; - - union { - __IOM uint32_t SL2CFG; /*!< (@ 0x00000014) Slot 2 Configuration Register */ - - struct { - __IOM uint32_t SLEN2 : 1; /*!< [0..0] This bit enables slot 2 for ADC conversions. */ - __IOM uint32_t WCEN2 : 1; /*!< [1..1] This bit enables the window compare function for slot - 2. */ - __IM uint32_t : 6; - __IOM uint32_t CHSEL2 : 4; /*!< [11..8] Select one of the 14 channel inputs for this slot. */ - __IM uint32_t : 4; - __IOM uint32_t PRMODE2 : 2; /*!< [17..16] Set the Precision Mode For Slot. */ - __IM uint32_t : 6; - __IOM uint32_t ADSEL2 : 3; /*!< [26..24] Select the number of measurements to average in the - accumulate divide module for this slot. */ - } SL2CFG_b; - } ; - - union { - __IOM uint32_t SL3CFG; /*!< (@ 0x00000018) Slot 3 Configuration Register */ - - struct { - __IOM uint32_t SLEN3 : 1; /*!< [0..0] This bit enables slot 3 for ADC conversions. */ - __IOM uint32_t WCEN3 : 1; /*!< [1..1] This bit enables the window compare function for slot - 3. */ - __IM uint32_t : 6; - __IOM uint32_t CHSEL3 : 4; /*!< [11..8] Select one of the 14 channel inputs for this slot. */ - __IM uint32_t : 4; - __IOM uint32_t PRMODE3 : 2; /*!< [17..16] Set the Precision Mode For Slot. */ - __IM uint32_t : 6; - __IOM uint32_t ADSEL3 : 3; /*!< [26..24] Select the number of measurements to average in the - accumulate divide module for this slot. */ - } SL3CFG_b; - } ; - - union { - __IOM uint32_t SL4CFG; /*!< (@ 0x0000001C) Slot 4 Configuration Register */ - - struct { - __IOM uint32_t SLEN4 : 1; /*!< [0..0] This bit enables slot 4 for ADC conversions. */ - __IOM uint32_t WCEN4 : 1; /*!< [1..1] This bit enables the window compare function for slot - 4. */ - __IM uint32_t : 6; - __IOM uint32_t CHSEL4 : 4; /*!< [11..8] Select one of the 14 channel inputs for this slot. */ - __IM uint32_t : 4; - __IOM uint32_t PRMODE4 : 2; /*!< [17..16] Set the Precision Mode For Slot. */ - __IM uint32_t : 6; - __IOM uint32_t ADSEL4 : 3; /*!< [26..24] Select the number of measurements to average in the - accumulate divide module for this slot. */ - } SL4CFG_b; - } ; - - union { - __IOM uint32_t SL5CFG; /*!< (@ 0x00000020) Slot 5 Configuration Register */ - - struct { - __IOM uint32_t SLEN5 : 1; /*!< [0..0] This bit enables slot 5 for ADC conversions. */ - __IOM uint32_t WCEN5 : 1; /*!< [1..1] This bit enables the window compare function for slot - 5. */ - __IM uint32_t : 6; - __IOM uint32_t CHSEL5 : 4; /*!< [11..8] Select one of the 14 channel inputs for this slot. */ - __IM uint32_t : 4; - __IOM uint32_t PRMODE5 : 2; /*!< [17..16] Set the Precision Mode For Slot. */ - __IM uint32_t : 6; - __IOM uint32_t ADSEL5 : 3; /*!< [26..24] Select number of measurements to average in the accumulate - divide module for this slot. */ - } SL5CFG_b; - } ; - - union { - __IOM uint32_t SL6CFG; /*!< (@ 0x00000024) Slot 6 Configuration Register */ - - struct { - __IOM uint32_t SLEN6 : 1; /*!< [0..0] This bit enables slot 6 for ADC conversions. */ - __IOM uint32_t WCEN6 : 1; /*!< [1..1] This bit enables the window compare function for slot - 6. */ - __IM uint32_t : 6; - __IOM uint32_t CHSEL6 : 4; /*!< [11..8] Select one of the 14 channel inputs for this slot. */ - __IM uint32_t : 4; - __IOM uint32_t PRMODE6 : 2; /*!< [17..16] Set the Precision Mode For Slot. */ - __IM uint32_t : 6; - __IOM uint32_t ADSEL6 : 3; /*!< [26..24] Select the number of measurements to average in the - accumulate divide module for this slot. */ - } SL6CFG_b; - } ; - - union { - __IOM uint32_t SL7CFG; /*!< (@ 0x00000028) Slot 7 Configuration Register */ - - struct { - __IOM uint32_t SLEN7 : 1; /*!< [0..0] This bit enables slot 7 for ADC conversions. */ - __IOM uint32_t WCEN7 : 1; /*!< [1..1] This bit enables the window compare function for slot - 7. */ - __IM uint32_t : 6; - __IOM uint32_t CHSEL7 : 4; /*!< [11..8] Select one of the 14 channel inputs for this slot. */ - __IM uint32_t : 4; - __IOM uint32_t PRMODE7 : 2; /*!< [17..16] Set the Precision Mode For Slot. */ - __IM uint32_t : 6; - __IOM uint32_t ADSEL7 : 3; /*!< [26..24] Select the number of measurements to average in the - accumulate divide module for this slot. */ - } SL7CFG_b; - } ; - - union { - __IOM uint32_t WULIM; /*!< (@ 0x0000002C) Window Comparator Upper Limits Register */ - - struct { - __IOM uint32_t ULIM : 20; /*!< [19..0] Sets the upper limit for the window comparator. */ - } WULIM_b; - } ; - - union { - __IOM uint32_t WLLIM; /*!< (@ 0x00000030) Window Comparator Lower Limits Register */ - - struct { - __IOM uint32_t LLIM : 20; /*!< [19..0] Sets the lower limit for the window comparator. */ - } WLLIM_b; - } ; - - union { - __IOM uint32_t SCWLIM; /*!< (@ 0x00000034) Scale Window Comparator Limits */ - - struct { - __IOM uint32_t SCWLIMEN : 1; /*!< [0..0] Scale the window limits compare values per precision - mode. When set to 0x0 (default), the values in the 20-bit - limits registers will compare directly with the FIFO values - regardless of the precision mode the slot is configured - to. When set to 0x1, the compare values will be divided - by the difference in precision bits while performing the - window limit comparisons. */ - } SCWLIM_b; - } ; - - union { - __IOM uint32_t FIFO; /*!< (@ 0x00000038) FIFO Data and Valid Count Register */ - - struct { - __IOM uint32_t DATA : 20; /*!< [19..0] Oldest data in the FIFO. */ - __IOM uint32_t COUNT : 8; /*!< [27..20] Number of valid entries in the ADC FIFO. */ - __IOM uint32_t SLOTNUM : 3; /*!< [30..28] Slot number associated with this FIFO data. */ - __IOM uint32_t RSVD : 1; /*!< [31..31] RESERVED. */ - } FIFO_b; - } ; - - union { - __IOM uint32_t FIFOPR; /*!< (@ 0x0000003C) FIFO Data and Valid Count Register */ - - struct { - __IOM uint32_t DATA : 20; /*!< [19..0] Oldest data in the FIFO. */ - __IOM uint32_t COUNT : 8; /*!< [27..20] Number of valid entries in the ADC FIFO. */ - __IOM uint32_t SLOTNUMPR : 3; /*!< [30..28] Slot number associated with this FIFO data. */ - __IOM uint32_t RSVDPR : 1; /*!< [31..31] RESERVED. */ - } FIFOPR_b; - } ; - __IM uint32_t RESERVED[112]; - - union { - __IOM uint32_t INTEN; /*!< (@ 0x00000200) ADC Interrupt registers: Enable */ - - struct { - __IOM uint32_t CNVCMP : 1; /*!< [0..0] ADC conversion complete interrupt. */ - __IOM uint32_t SCNCMP : 1; /*!< [1..1] ADC scan complete interrupt. */ - __IOM uint32_t FIFOOVR1 : 1; /*!< [2..2] FIFO 75 percent full interrupt. */ - __IOM uint32_t FIFOOVR2 : 1; /*!< [3..3] FIFO 100 percent full interrupt. */ - __IOM uint32_t WCEXC : 1; /*!< [4..4] Window comparator voltage excursion interrupt. */ - __IOM uint32_t WCINC : 1; /*!< [5..5] Window comparator voltage incursion interrupt. */ - __IOM uint32_t DCMP : 1; /*!< [6..6] DMA Transfer Complete */ - __IOM uint32_t DERR : 1; /*!< [7..7] DMA Error Condition */ - } INTEN_b; - } ; - - union { - __IOM uint32_t INTSTAT; /*!< (@ 0x00000204) ADC Interrupt registers: Status */ - - struct { - __IOM uint32_t CNVCMP : 1; /*!< [0..0] ADC conversion complete interrupt. */ - __IOM uint32_t SCNCMP : 1; /*!< [1..1] ADC scan complete interrupt. */ - __IOM uint32_t FIFOOVR1 : 1; /*!< [2..2] FIFO 75 percent full interrupt. */ - __IOM uint32_t FIFOOVR2 : 1; /*!< [3..3] FIFO 100 percent full interrupt. */ - __IOM uint32_t WCEXC : 1; /*!< [4..4] Window comparator voltage excursion interrupt. */ - __IOM uint32_t WCINC : 1; /*!< [5..5] Window comparator voltage incursion interrupt. */ - __IOM uint32_t DCMP : 1; /*!< [6..6] DMA Transfer Complete */ - __IOM uint32_t DERR : 1; /*!< [7..7] DMA Error Condition */ - } INTSTAT_b; - } ; - - union { - __IOM uint32_t INTCLR; /*!< (@ 0x00000208) ADC Interrupt registers: Clear */ - - struct { - __IOM uint32_t CNVCMP : 1; /*!< [0..0] ADC conversion complete interrupt. */ - __IOM uint32_t SCNCMP : 1; /*!< [1..1] ADC scan complete interrupt. */ - __IOM uint32_t FIFOOVR1 : 1; /*!< [2..2] FIFO 75 percent full interrupt. */ - __IOM uint32_t FIFOOVR2 : 1; /*!< [3..3] FIFO 100 percent full interrupt. */ - __IOM uint32_t WCEXC : 1; /*!< [4..4] Window comparator voltage excursion interrupt. */ - __IOM uint32_t WCINC : 1; /*!< [5..5] Window comparator voltage incursion interrupt. */ - __IOM uint32_t DCMP : 1; /*!< [6..6] DMA Transfer Complete */ - __IOM uint32_t DERR : 1; /*!< [7..7] DMA Error Condition */ - } INTCLR_b; - } ; - - union { - __IOM uint32_t INTSET; /*!< (@ 0x0000020C) ADC Interrupt registers: Set */ - - struct { - __IOM uint32_t CNVCMP : 1; /*!< [0..0] ADC conversion complete interrupt. */ - __IOM uint32_t SCNCMP : 1; /*!< [1..1] ADC scan complete interrupt. */ - __IOM uint32_t FIFOOVR1 : 1; /*!< [2..2] FIFO 75 percent full interrupt. */ - __IOM uint32_t FIFOOVR2 : 1; /*!< [3..3] FIFO 100 percent full interrupt. */ - __IOM uint32_t WCEXC : 1; /*!< [4..4] Window comparator voltage excursion interrupt. */ - __IOM uint32_t WCINC : 1; /*!< [5..5] Window comparator voltage incursion interrupt. */ - __IOM uint32_t DCMP : 1; /*!< [6..6] DMA Transfer Complete */ - __IOM uint32_t DERR : 1; /*!< [7..7] DMA Error Condition */ - } INTSET_b; - } ; - __IM uint32_t RESERVED1[12]; - - union { - __IOM uint32_t DMATRIGEN; /*!< (@ 0x00000240) DMA Trigger Enable Register */ - - struct { - __IOM uint32_t DFIFO75 : 1; /*!< [0..0] Trigger DMA upon FIFO 75 percent Full */ - __IOM uint32_t DFIFOFULL : 1; /*!< [1..1] Trigger DMA upon FIFO 100 percent Full */ - } DMATRIGEN_b; - } ; - - union { - __IOM uint32_t DMATRIGSTAT; /*!< (@ 0x00000244) DMA Trigger Status Register */ - - struct { - __IOM uint32_t D75STAT : 1; /*!< [0..0] Triggered DMA from FIFO 75 percent Full */ - __IOM uint32_t DFULLSTAT : 1; /*!< [1..1] Triggered DMA from FIFO 100 percent Full */ - } DMATRIGSTAT_b; - } ; - __IM uint32_t RESERVED2[14]; - - union { - __IOM uint32_t DMACFG; /*!< (@ 0x00000280) DMA Configuration Register */ - - struct { - __IOM uint32_t DMAEN : 1; /*!< [0..0] DMA Enable */ - __IM uint32_t : 1; - __IOM uint32_t DMADIR : 1; /*!< [2..2] Direction */ - __IM uint32_t : 5; - __IOM uint32_t DMAPRI : 1; /*!< [8..8] Sets the Priority of the DMA request */ - __IOM uint32_t DMADYNPRI : 1; /*!< [9..9] Enables dynamic priority based on FIFO fullness. When - FIFO is full, priority is automatically set to HIGH. Otherwise, - DMAPRI is used. */ - __IM uint32_t : 6; - __IOM uint32_t DMAHONSTAT : 1; /*!< [16..16] Halt New ADC conversions until DMA Status DMAERR and - DMACPL Cleared. */ - __IOM uint32_t DMAMSK : 1; /*!< [17..17] Mask the FIFOCNT and SLOTNUM when transferring FIFO - contents to memory */ - __IOM uint32_t DPWROFF : 1; /*!< [18..18] Power Off the ADC System upon DMACPL. */ - } DMACFG_b; - } ; - __IM uint32_t RESERVED3; - - union { - __IOM uint32_t DMATOTCOUNT; /*!< (@ 0x00000288) DMA Total Transfer Count */ - - struct { - __IM uint32_t : 2; - __IOM uint32_t TOTCOUNT : 16; /*!< [17..2] Total Transfer Count */ - } DMATOTCOUNT_b; - } ; - - union { - __IOM uint32_t DMATARGADDR; /*!< (@ 0x0000028C) DMA Target Address Register */ - - struct { - __IOM uint32_t LTARGADDR : 19; /*!< [18..0] DMA Target Address */ - __IOM uint32_t UTARGADDR : 13; /*!< [31..19] SRAM Target */ - } DMATARGADDR_b; - } ; - - union { - __IOM uint32_t DMASTAT; /*!< (@ 0x00000290) DMA Status Register */ - - struct { - __IOM uint32_t DMATIP : 1; /*!< [0..0] DMA Transfer In Progress */ - __IOM uint32_t DMACPL : 1; /*!< [1..1] DMA Transfer Complete */ - __IOM uint32_t DMAERR : 1; /*!< [2..2] DMA Error */ - } DMASTAT_b; - } ; -} ADC_Type; /*!< Size = 660 (0x294) */ - - - -/* =========================================================================================================================== */ -/* ================ APBDMA ================ */ -/* =========================================================================================================================== */ - - -/** - * @brief APB DMA Register Interfaces (APBDMA) - */ - -typedef struct { /*!< (@ 0x40011000) APBDMA Structure */ - - union { - __IOM uint32_t BBVALUE; /*!< (@ 0x00000000) Control Register */ - - struct { - __IOM uint32_t DATAOUT : 8; /*!< [7..0] Data Output Values */ - __IM uint32_t : 8; - __IOM uint32_t PIN : 8; /*!< [23..16] PIO values */ - } BBVALUE_b; - } ; - - union { - __IOM uint32_t BBSETCLEAR; /*!< (@ 0x00000004) Set/Clear Register */ - - struct { - __IOM uint32_t SET : 8; /*!< [7..0] Write 1 to Set PIO value (set hier priority than clear - if both bit set) */ - __IM uint32_t : 8; - __IOM uint32_t CLEAR : 8; /*!< [23..16] Write 1 to Clear PIO value */ - } BBSETCLEAR_b; - } ; - - union { - __IOM uint32_t BBINPUT; /*!< (@ 0x00000008) PIO Input Values */ - - struct { - __IOM uint32_t DATAIN : 8; /*!< [7..0] PIO values */ - } BBINPUT_b; - } ; - __IM uint32_t RESERVED[5]; - - union { - __IOM uint32_t DEBUGDATA; /*!< (@ 0x00000020) PIO Input Values */ - - struct { - __IOM uint32_t DEBUGDATA : 32; /*!< [31..0] Debug Data */ - } DEBUGDATA_b; - } ; - __IM uint32_t RESERVED1[7]; - - union { - __IOM uint32_t DEBUG; /*!< (@ 0x00000040) PIO Input Values */ - - struct { - __IOM uint32_t DEBUGEN : 4; /*!< [3..0] Debug Enable */ - } DEBUG_b; - } ; -} APBDMA_Type; /*!< Size = 68 (0x44) */ - - - -/* =========================================================================================================================== */ -/* ================ BLEIF ================ */ -/* =========================================================================================================================== */ - - -/** - * @brief BLE Interface (BLEIF) - */ - -typedef struct { /*!< (@ 0x5000C000) BLEIF Structure */ - - union { - __IOM uint32_t FIFO; /*!< (@ 0x00000000) FIFO Access Port */ - - struct { - __IOM uint32_t FIFO : 32; /*!< [31..0] FIFO direct access. Only locations 0 - 3F will return - valid information. */ - } FIFO_b; - } ; - __IM uint32_t RESERVED[63]; - - union { - __IOM uint32_t FIFOPTR; /*!< (@ 0x00000100) FIFO size and remaining slots open values */ - - struct { - __IOM uint32_t FIFO0SIZ : 8; /*!< [7..0] The number of valid data bytes currently in the FIFO - 0 (written by MCU, read by interface) */ - __IOM uint32_t FIFO0REM : 8; /*!< [15..8] The number of remaining data bytes slots currently in - FIFO 0 (written by MCU, read by interface) */ - __IOM uint32_t FIFO1SIZ : 8; /*!< [23..16] The number of valid data bytes currently in FIFO 1 - (written by interface, read by MCU) */ - __IOM uint32_t FIFO1REM : 8; /*!< [31..24] The number of remaining data bytes slots currently - in FIFO 1 (written by interface, read by MCU) */ - } FIFOPTR_b; - } ; - - union { - __IOM uint32_t FIFOTHR; /*!< (@ 0x00000104) FIFO Threshold Configuration */ - - struct { - __IOM uint32_t FIFORTHR : 6; /*!< [5..0] FIFO read threshold in bytes. A value of 0 will disable - the read FIFO level from activating the threshold interrupt. - If this field is non-zero, it will trigger a threshold - interrupt when the read fifo contains FIFORTHR valid bytes - of data, as indicated by the FIFO1SIZ field. This is intended - to signal when a data transfer of FIFORTHR bytes can be - done from the IOM module to the host via the read fifo - to support large IOM read operations. */ - __IM uint32_t : 2; - __IOM uint32_t FIFOWTHR : 6; /*!< [13..8] FIFO write threshold in bytes. A value of 0 will disable - the write FIFO level from activating the threshold interrupt. - If this field is non-zero, it will trigger a threshold - interrupt when the write fifo contains FIFOWTHR free bytes, - as indicated by the FIFO0REM field. This is intended to - signal when a transfer of FIFOWTHR bytes can be done from - the host to the IOM write fifo to support large IOM write - operations. */ - } FIFOTHR_b; - } ; - - union { - __IOM uint32_t FIFOPOP; /*!< (@ 0x00000108) FIFO POP register */ - - struct { - __IOM uint32_t FIFODOUT : 32; /*!< [31..0] This register will return the read data indicated by - the current read pointer on reads. If the POPWR control - bit in the FIFOCTRL register is reset (0), the fifo read - pointer will be advanced by one word as a result of the - read.If the POPWR bit is set (1), the fifo read pointer - will only be advanced after a write operation to this register. - The write data is ignored for this register.If less than - a even word multiple is available, and the command is completed, - the module will return the word containing */ - } FIFOPOP_b; - } ; - - union { - __IOM uint32_t FIFOPUSH; /*!< (@ 0x0000010C) FIFO PUSH register */ - - struct { - __IOM uint32_t FIFODIN : 32; /*!< [31..0] This register is used to write the FIFORAM in FIFO mode - and will cause a push event to occur to the next open slot - within the FIFORAM. Writing to this register will cause - the write point to increment by 1 word(4 bytes). */ - } FIFOPUSH_b; - } ; - - union { - __IOM uint32_t FIFOCTRL; /*!< (@ 0x00000110) FIFO Control Register */ - - struct { - __IOM uint32_t POPWR : 1; /*!< [0..0] Selects the mode in which 'pop' events are done for the - fifo read operations. A value of '1' will prevent a pop - event on a read operation, and will require a write to - the FIFOPOP register to create a pop event.A value of '0' - in this register will allow a pop event to occur on the - read of the FIFOPOP register, and may cause inadvertant - fifo pops when used in a debugging mode. */ - __IOM uint32_t FIFORSTN : 1; /*!< [1..1] Active low manual reset of the fifo. Write to 0 to reset - fifo, and then write to 1 to remove the reset. */ - } FIFOCTRL_b; - } ; - - union { - __IOM uint32_t FIFOLOC; /*!< (@ 0x00000114) FIFO Pointers */ - - struct { - __IOM uint32_t FIFOWPTR : 4; /*!< [3..0] Current FIFO write pointer. Value is the index into the - outgoing FIFO (FIFO0), which is used during write operations - to external devices. */ - __IM uint32_t : 4; - __IOM uint32_t FIFORPTR : 4; /*!< [11..8] Current FIFO read pointer. Used to index into the incoming - FIFO (FIFO1), which is used to store read data returned - from external devices during a read operation. */ - } FIFOLOC_b; - } ; - __IM uint32_t RESERVED1[58]; - - union { - __IOM uint32_t CLKCFG; /*!< (@ 0x00000200) I/O Clock Configuration */ - - struct { - __IOM uint32_t IOCLKEN : 1; /*!< [0..0] Enable for the interface clock. Must be enabled prior - to executing any IO operations. */ - __IM uint32_t : 7; - __IOM uint32_t FSEL : 3; /*!< [10..8] Select the input clock frequency. */ - __IOM uint32_t CLK32KEN : 1; /*!< [11..11] Enable for the 32Khz clock to the BLE module */ - __IOM uint32_t DIV3 : 1; /*!< [12..12] Enable of the divide by 3 of the source IOCLK. */ - } CLKCFG_b; - } ; - __IM uint32_t RESERVED2[2]; - - union { - __IOM uint32_t CMD; /*!< (@ 0x0000020C) Command and offset Register */ - - struct { - __IOM uint32_t CMD : 5; /*!< [4..0] Command for submodule. */ - __IOM uint32_t OFFSETCNT : 2; /*!< [6..5] Number of offset bytes to use for the command - 0, 1, - 2, 3 are valid selections. The second (byte 1) and third - byte (byte 2) are read from the OFFSETHI register, and - the low order byte is pulled from this register in the - OFFSETLO field.Offset bytes are transmitted highest byte - first. EG if offsetcnt == 3, OFFSETHI[15:8] will be transmitted - first, then OFFSETHI[7:0] then OFFSETLO.If offsetcnt == - 2, OFFSETHI[7:0] will be transmitted, then OFFSETLO.If - offsetcnt == 1, only OFFSETLO will be transmitted. */ - __IOM uint32_t CONT : 1; /*!< [7..7] Contine to hold the bus after the current transaction - if set to a 1 with a new command issued. */ - __IOM uint32_t TSIZE : 12; /*!< [19..8] Defines the transaction size in bytes. The offset transfer - is not included in this size. */ - __IOM uint32_t CMDSEL : 2; /*!< [21..20] Command Specific selection information */ - __IM uint32_t : 2; - __IOM uint32_t OFFSETLO : 8; /*!< [31..24] This register holds the low order byte of offset to - be used in the transaction. The number of offset bytes - to use is set with bits 1:0 of the command. Offset bytes - are transferred starting from the highest byte first. */ - } CMD_b; - } ; - - union { - __IOM uint32_t CMDRPT; /*!< (@ 0x00000210) Command Repeat Register */ - - struct { - __IOM uint32_t CMDRPT : 5; /*!< [4..0] Count of number of times to repeat the next command. */ - } CMDRPT_b; - } ; - - union { - __IOM uint32_t OFFSETHI; /*!< (@ 0x00000214) High order offset bytes */ - - struct { - __IOM uint32_t OFFSETHI : 16; /*!< [15..0] Holds the high order bytes of the 2 or 3 byte offset - phase of a transaction. */ - } OFFSETHI_b; - } ; - - union { - __IOM uint32_t CMDSTAT; /*!< (@ 0x00000218) Command status */ - - struct { - __IOM uint32_t CCMD : 5; /*!< [4..0] current command that is being executed */ - __IOM uint32_t CMDSTAT : 3; /*!< [7..5] The current status of the command execution. */ - __IOM uint32_t CTSIZE : 12; /*!< [19..8] The current number of bytes still to be transferred - with this command. This field will count down to zero. */ - } CMDSTAT_b; - } ; - __IM uint32_t RESERVED3; - - union { - __IOM uint32_t INTEN; /*!< (@ 0x00000220) IO Master Interrupts: Enable */ - - struct { - __IOM uint32_t CMDCMP : 1; /*!< [0..0] Command Complete interrupt. Asserted when the current - operation has completed. For repeated commands, this will - only be asserted when the final repeated command is completed. */ - __IOM uint32_t THR : 1; /*!< [1..1] FIFO Threshold interrupt. For write operations, asserted - when the number of free bytes in the write FIFO equals - or exceeds the WTHR field.For read operations, asserted - when the number of valid bytes in the read FIFO equals - of exceeds the value set in the RTHR field. */ - __IOM uint32_t FUNDFL : 1; /*!< [2..2] Read FIFO Underflow interrupt. Asserted when a pop operation - is done to a empty read FIFO. */ - __IOM uint32_t FOVFL : 1; /*!< [3..3] Write FIFO Overflow interrupt. This occurs when software - tries to write to a full fifo. The current operation does - not stop. */ - __IOM uint32_t B2MST : 1; /*!< [4..4] B2M State change interrupt. Asserted on any change in - the B2M_STATE signal from the BLE Core. */ - __IOM uint32_t IACC : 1; /*!< [5..5] illegal FIFO access interrupt. Asserted when there is - a overflow or underflow event */ - __IOM uint32_t ICMD : 1; /*!< [6..6] illegal command interrupt. Asserted when a command is - written when an active command is in progress. */ - __IOM uint32_t BLECIRQ : 1; /*!< [7..7] BLE Core IRQ signal. Asserted when the BLE_IRQ signal - from the BLE Core is asserted, indicating the availability - of read data from the BLE Core. */ - __IOM uint32_t BLECSSTAT : 1; /*!< [8..8] BLE Core SPI Status interrupt. Asserted when the SPI_STATUS - signal from the BLE Core is asserted, indicating that SPI - writes can be done to the BLE Core.Transfers to the BLE - Core should only be done when this signal is high. */ - __IOM uint32_t DCMP : 1; /*!< [9..9] DMA Complete. Processing of the DMA operation has completed - and the DMA submodule is returned into the idle state */ - __IOM uint32_t DERR : 1; /*!< [10..10] DMA Error encountered during the processing of the - DMA command. The DMA error could occur when the memory - access specified in the DMA operation is not available - or incorrectly specified. */ - __IOM uint32_t CQPAUSED : 1; /*!< [11..11] Command queue is paused due to an active event enabled - in the PAUSEEN register. The interrupt is posted when the - event is enabled within the PAUSEEN register, the mask - is active in the CQIRQMASK field and the event occurs. */ - __IOM uint32_t CQUPD : 1; /*!< [12..12] Command queue write operation executed a register write - with the register address bit 0 set to 1. The low address - bits in the CQ address fields are unused and bit 0 can - be used to trigger an interrupt to indicate when this register - write is performed by the CQ operation. */ - __IOM uint32_t CQERR : 1; /*!< [13..13] Command queue error during processing. When an error - occurs, the system will stop processing and halt operations - to allow software to take recovery actions */ - __IOM uint32_t B2MSLEEP : 1; /*!< [14..14] The B2M_STATE from the BLE Core transitioned into the - sleep state */ - __IOM uint32_t B2MACTIVE : 1; /*!< [15..15] The B2M_STATE from the BLE Core transitioned into the - active state */ - __IOM uint32_t B2MSHUTDN : 1; /*!< [16..16] The B2M_STATE from the BLE Core transitioned into shutdown - state */ - } INTEN_b; - } ; - - union { - __IOM uint32_t INTSTAT; /*!< (@ 0x00000224) IO Master Interrupts: Status */ - - struct { - __IOM uint32_t CMDCMP : 1; /*!< [0..0] Command Complete interrupt. Asserted when the current - operation has completed. For repeated commands, this will - only be asserted when the final repeated command is completed. */ - __IOM uint32_t THR : 1; /*!< [1..1] FIFO Threshold interrupt. For write operations, asserted - when the number of free bytes in the write FIFO equals - or exceeds the WTHR field.For read operations, asserted - when the number of valid bytes in the read FIFO equals - of exceeds the value set in the RTHR field. */ - __IOM uint32_t FUNDFL : 1; /*!< [2..2] Read FIFO Underflow interrupt. Asserted when a pop operation - is done to a empty read FIFO. */ - __IOM uint32_t FOVFL : 1; /*!< [3..3] Write FIFO Overflow interrupt. This occurs when software - tries to write to a full fifo. The current operation does - not stop. */ - __IOM uint32_t B2MST : 1; /*!< [4..4] B2M State change interrupt. Asserted on any change in - the B2M_STATE signal from the BLE Core. */ - __IOM uint32_t IACC : 1; /*!< [5..5] illegal FIFO access interrupt. Asserted when there is - a overflow or underflow event */ - __IOM uint32_t ICMD : 1; /*!< [6..6] illegal command interrupt. Asserted when a command is - written when an active command is in progress. */ - __IOM uint32_t BLECIRQ : 1; /*!< [7..7] BLE Core IRQ signal. Asserted when the BLE_IRQ signal - from the BLE Core is asserted, indicating the availability - of read data from the BLE Core. */ - __IOM uint32_t BLECSSTAT : 1; /*!< [8..8] BLE Core SPI Status interrupt. Asserted when the SPI_STATUS - signal from the BLE Core is asserted, indicating that SPI - writes can be done to the BLE Core.Transfers to the BLE - Core should only be done when this signal is high. */ - __IOM uint32_t DCMP : 1; /*!< [9..9] DMA Complete. Processing of the DMA operation has completed - and the DMA submodule is returned into the idle state */ - __IOM uint32_t DERR : 1; /*!< [10..10] DMA Error encountered during the processing of the - DMA command. The DMA error could occur when the memory - access specified in the DMA operation is not available - or incorrectly specified. */ - __IOM uint32_t CQPAUSED : 1; /*!< [11..11] Command queue is paused due to an active event enabled - in the PAUSEEN register. The interrupt is posted when the - event is enabled within the PAUSEEN register, the mask - is active in the CQIRQMASK field and the event occurs. */ - __IOM uint32_t CQUPD : 1; /*!< [12..12] Command queue write operation executed a register write - with the register address bit 0 set to 1. The low address - bits in the CQ address fields are unused and bit 0 can - be used to trigger an interrupt to indicate when this register - write is performed by the CQ operation. */ - __IOM uint32_t CQERR : 1; /*!< [13..13] Command queue error during processing. When an error - occurs, the system will stop processing and halt operations - to allow software to take recovery actions */ - __IOM uint32_t B2MSLEEP : 1; /*!< [14..14] The B2M_STATE from the BLE Core transitioned into the - sleep state */ - __IOM uint32_t B2MACTIVE : 1; /*!< [15..15] The B2M_STATE from the BLE Core transitioned into the - active state */ - __IOM uint32_t B2MSHUTDN : 1; /*!< [16..16] The B2M_STATE from the BLE Core transitioned into shutdown - state */ - } INTSTAT_b; - } ; - - union { - __IOM uint32_t INTCLR; /*!< (@ 0x00000228) IO Master Interrupts: Clear */ - - struct { - __IOM uint32_t CMDCMP : 1; /*!< [0..0] Command Complete interrupt. Asserted when the current - operation has completed. For repeated commands, this will - only be asserted when the final repeated command is completed. */ - __IOM uint32_t THR : 1; /*!< [1..1] FIFO Threshold interrupt. For write operations, asserted - when the number of free bytes in the write FIFO equals - or exceeds the WTHR field.For read operations, asserted - when the number of valid bytes in the read FIFO equals - of exceeds the value set in the RTHR field. */ - __IOM uint32_t FUNDFL : 1; /*!< [2..2] Read FIFO Underflow interrupt. Asserted when a pop operation - is done to a empty read FIFO. */ - __IOM uint32_t FOVFL : 1; /*!< [3..3] Write FIFO Overflow interrupt. This occurs when software - tries to write to a full fifo. The current operation does - not stop. */ - __IOM uint32_t B2MST : 1; /*!< [4..4] B2M State change interrupt. Asserted on any change in - the B2M_STATE signal from the BLE Core. */ - __IOM uint32_t IACC : 1; /*!< [5..5] illegal FIFO access interrupt. Asserted when there is - a overflow or underflow event */ - __IOM uint32_t ICMD : 1; /*!< [6..6] illegal command interrupt. Asserted when a command is - written when an active command is in progress. */ - __IOM uint32_t BLECIRQ : 1; /*!< [7..7] BLE Core IRQ signal. Asserted when the BLE_IRQ signal - from the BLE Core is asserted, indicating the availability - of read data from the BLE Core. */ - __IOM uint32_t BLECSSTAT : 1; /*!< [8..8] BLE Core SPI Status interrupt. Asserted when the SPI_STATUS - signal from the BLE Core is asserted, indicating that SPI - writes can be done to the BLE Core.Transfers to the BLE - Core should only be done when this signal is high. */ - __IOM uint32_t DCMP : 1; /*!< [9..9] DMA Complete. Processing of the DMA operation has completed - and the DMA submodule is returned into the idle state */ - __IOM uint32_t DERR : 1; /*!< [10..10] DMA Error encountered during the processing of the - DMA command. The DMA error could occur when the memory - access specified in the DMA operation is not available - or incorrectly specified. */ - __IOM uint32_t CQPAUSED : 1; /*!< [11..11] Command queue is paused due to an active event enabled - in the PAUSEEN register. The interrupt is posted when the - event is enabled within the PAUSEEN register, the mask - is active in the CQIRQMASK field and the event occurs. */ - __IOM uint32_t CQUPD : 1; /*!< [12..12] Command queue write operation executed a register write - with the register address bit 0 set to 1. The low address - bits in the CQ address fields are unused and bit 0 can - be used to trigger an interrupt to indicate when this register - write is performed by the CQ operation. */ - __IOM uint32_t CQERR : 1; /*!< [13..13] Command queue error during processing. When an error - occurs, the system will stop processing and halt operations - to allow software to take recovery actions */ - __IOM uint32_t B2MSLEEP : 1; /*!< [14..14] The B2M_STATE from the BLE Core transitioned into the - sleep state */ - __IOM uint32_t B2MACTIVE : 1; /*!< [15..15] The B2M_STATE from the BLE Core transitioned into the - active state */ - __IOM uint32_t B2MSHUTDN : 1; /*!< [16..16] The B2M_STATE from the BLE Core transitioned into shutdown - state */ - } INTCLR_b; - } ; - - union { - __IOM uint32_t INTSET; /*!< (@ 0x0000022C) IO Master Interrupts: Set */ - - struct { - __IOM uint32_t CMDCMP : 1; /*!< [0..0] Command Complete interrupt. Asserted when the current - operation has completed. For repeated commands, this will - only be asserted when the final repeated command is completed. */ - __IOM uint32_t THR : 1; /*!< [1..1] FIFO Threshold interrupt. For write operations, asserted - when the number of free bytes in the write FIFO equals - or exceeds the WTHR field.For read operations, asserted - when the number of valid bytes in the read FIFO equals - of exceeds the value set in the RTHR field. */ - __IOM uint32_t FUNDFL : 1; /*!< [2..2] Read FIFO Underflow interrupt. Asserted when a pop operation - is done to a empty read FIFO. */ - __IOM uint32_t FOVFL : 1; /*!< [3..3] Write FIFO Overflow interrupt. This occurs when software - tries to write to a full fifo. The current operation does - not stop. */ - __IOM uint32_t B2MST : 1; /*!< [4..4] B2M State change interrupt. Asserted on any change in - the B2M_STATE signal from the BLE Core. */ - __IOM uint32_t IACC : 1; /*!< [5..5] illegal FIFO access interrupt. Asserted when there is - a overflow or underflow event */ - __IOM uint32_t ICMD : 1; /*!< [6..6] illegal command interrupt. Asserted when a command is - written when an active command is in progress. */ - __IOM uint32_t BLECIRQ : 1; /*!< [7..7] BLE Core IRQ signal. Asserted when the BLE_IRQ signal - from the BLE Core is asserted, indicating the availability - of read data from the BLE Core. */ - __IOM uint32_t BLECSSTAT : 1; /*!< [8..8] BLE Core SPI Status interrupt. Asserted when the SPI_STATUS - signal from the BLE Core is asserted, indicating that SPI - writes can be done to the BLE Core.Transfers to the BLE - Core should only be done when this signal is high. */ - __IOM uint32_t DCMP : 1; /*!< [9..9] DMA Complete. Processing of the DMA operation has completed - and the DMA submodule is returned into the idle state */ - __IOM uint32_t DERR : 1; /*!< [10..10] DMA Error encountered during the processing of the - DMA command. The DMA error could occur when the memory - access specified in the DMA operation is not available - or incorrectly specified. */ - __IOM uint32_t CQPAUSED : 1; /*!< [11..11] Command queue is paused due to an active event enabled - in the PAUSEEN register. The interrupt is posted when the - event is enabled within the PAUSEEN register, the mask - is active in the CQIRQMASK field and the event occurs. */ - __IOM uint32_t CQUPD : 1; /*!< [12..12] Command queue write operation executed a register write - with the register address bit 0 set to 1. The low address - bits in the CQ address fields are unused and bit 0 can - be used to trigger an interrupt to indicate when this register - write is performed by the CQ operation. */ - __IOM uint32_t CQERR : 1; /*!< [13..13] Command queue error during processing. When an error - occurs, the system will stop processing and halt operations - to allow software to take recovery actions */ - __IOM uint32_t B2MSLEEP : 1; /*!< [14..14] The B2M_STATE from the BLE Core transitioned into the - sleep state */ - __IOM uint32_t B2MACTIVE : 1; /*!< [15..15] The B2M_STATE from the BLE Core transitioned into the - active state */ - __IOM uint32_t B2MSHUTDN : 1; /*!< [16..16] The B2M_STATE from the BLE Core transitioned into shutdown - state */ - } INTSET_b; - } ; - - union { - __IOM uint32_t DMATRIGEN; /*!< (@ 0x00000230) DMA Trigger Enable Register */ - - struct { - __IOM uint32_t DCMDCMPEN : 1; /*!< [0..0] Trigger DMA upon command complete. Enables the trigger - of the DMA when a command is completed. When this event - is triggered, the number of words transferred will be the - lesser of the remaining TOTCOUNT bytes, or the number of - bytes in the FIFO when the command completed. If this is - disabled, and the number of bytes in the FIFO is equal - or greater than the TOTCOUNT bytes, a transfer of TOTCOUNT - bytes will be done to ensure read data is stored when the - DMA is completed. */ - __IOM uint32_t DTHREN : 1; /*!< [1..1] Trigger DMA upon THR level reached. For M2P DMA operations - (IOM writes), the trigger will assert when the write FIFO - has (WTHR/4) number of words free in the write FIFO, and - will transfer (WTHR/4) number of wordsor, if the number - of words left to transfer is less than the WTHR value, - will transfer the remaining byte count.For P2M DMA operations, - the trigger will assert when the read FIFO has (RTHR/4) - words available in the read FIFO, and will transfer (RTHR/4) - words to SRAM. This trigger will NOT asser */ - } DMATRIGEN_b; - } ; - - union { - __IOM uint32_t DMATRIGSTAT; /*!< (@ 0x00000234) DMA Trigger Status Register */ - - struct { - __IOM uint32_t DCMDCMP : 1; /*!< [0..0] Triggered DMA from Command complete event. Bit is read - only and can be cleared by disabling the DCMDCMP trigger - enable or by disabling DMA. */ - __IOM uint32_t DTHR : 1; /*!< [1..1] Triggered DMA from THR event. Bit is read only and can - be cleared by disabling the DTHR trigger enable or by disabling - DMA. */ - __IOM uint32_t DTOTCMP : 1; /*!< [2..2] DMA triggered when DCMDCMP = 0, and the amount of data - in the FIFO was enough to complete the DMA operation (greater - than or equal to current TOTCOUNT) when the command completed. - This trigger is default active when the DCMDCMP trigger - isdisabled and there is enough data in the FIFO to complete - the DMA operation. */ - } DMATRIGSTAT_b; - } ; - - union { - __IOM uint32_t DMACFG; /*!< (@ 0x00000238) DMA Configuration Register */ - - struct { - __IOM uint32_t DMAEN : 1; /*!< [0..0] DMA Enable. Setting this bit to EN will start the DMA - operation. This should be the last DMA related register - set prior to issuing the command */ - __IOM uint32_t DMADIR : 1; /*!< [1..1] Direction */ - __IM uint32_t : 6; - __IOM uint32_t DMAPRI : 1; /*!< [8..8] Sets the Priority of the DMA request */ - __IOM uint32_t DPWROFF : 1; /*!< [9..9] Power off module after DMA is complete. If this bit is - active, the module will request to power off the supply - it is attached to. If there are other units still requiring - power from the same domain, power down will not be performed. */ - } DMACFG_b; - } ; - - union { - __IOM uint32_t DMATOTCOUNT; /*!< (@ 0x0000023C) DMA Total Transfer Count */ - - struct { - __IOM uint32_t TOTCOUNT : 12; /*!< [11..0] Triggered DMA from Command complete event occured. Bit - is read only and can be cleared by disabling the DTHR trigger - enable or by disabling DMA. */ - } DMATOTCOUNT_b; - } ; - - union { - __IOM uint32_t DMATARGADDR; /*!< (@ 0x00000240) DMA Target Address Register */ - - struct { - __IOM uint32_t TARGADDR : 20; /*!< [19..0] Bits [19:0] of the target byte address for source of - DMA (either read or write). The address can be any byte - alignment, and does not have to be word aligned. In cases - of non-word aligned addresses, the DMA logic will take - care for ensuring only the target bytes are read/written. */ - __IM uint32_t : 8; - __IOM uint32_t TARGADDR28 : 1; /*!< [28..28] Bit 28 of the target byte address for source of DMA - (either read or write). In cases of non-word aligned addresses, - the DMA logic will take care for ensuring only the target - bytes are read/written.Setting to '1' will select the SRAM. - Setting to '0' will select the flash */ - } DMATARGADDR_b; - } ; - - union { - __IOM uint32_t DMASTAT; /*!< (@ 0x00000244) DMA Status Register */ - - struct { - __IOM uint32_t DMATIP : 1; /*!< [0..0] DMA Transfer In Progress indicator. 1 will indicate that - a DMA transfer is active. The DMA transfer may be waiting - on data, transferring data, or waiting for priority.All - of these will be indicated with a 1. A 0 will indicate - that the DMA is fully complete and no further transactions - will be done. This bit is read only. */ - __IOM uint32_t DMACPL : 1; /*!< [1..1] DMA Transfer Complete. This signals the end of the DMA - operation. This bit can be cleared by writing to 0. */ - __IOM uint32_t DMAERR : 1; /*!< [2..2] DMA Error. This active high bit signals that an error - was encountered during the DMA operation. */ - } DMASTAT_b; - } ; - - union { - __IOM uint32_t CQCFG; /*!< (@ 0x00000248) Command Queue Configuration Register */ - - struct { - __IOM uint32_t CQEN : 1; /*!< [0..0] Command queue enable. When set, will enable the processing - of the command queue and fetches of address/data pairs - will proceed from the word address within the CQADDR register. - Can be disabledusing a CQ executed write to this bit as - well. */ - __IOM uint32_t CQPRI : 1; /*!< [1..1] Sets the Priority of the command queue dma request. */ - } CQCFG_b; - } ; - - union { - __IOM uint32_t CQADDR; /*!< (@ 0x0000024C) CQ Target Read Address Register */ - - struct { - __IM uint32_t : 2; - __IOM uint32_t CQADDR : 18; /*!< [19..2] Bits 19:2 of target byte address for source of CQ (read - only). The buffer must be aligned on a word boundary */ - __IM uint32_t : 8; - __IOM uint32_t CQADDR28 : 1; /*!< [28..28] Bit 28 of target byte address for source of CQ (read - only). Used to denote Flash (0) or SRAM (1) access */ - } CQADDR_b; - } ; - - union { - __IOM uint32_t CQSTAT; /*!< (@ 0x00000250) Command Queue Status Register */ - - struct { - __IOM uint32_t CQTIP : 1; /*!< [0..0] Command queue Transfer In Progress indicator. 1 will - indicate that a CQ transfer is active and this will remain - active even when paused waiting for external event. */ - __IOM uint32_t CQPAUSED : 1; /*!< [1..1] Command queue operation is currently paused. */ - __IOM uint32_t CQERR : 1; /*!< [2..2] Command queue processing Error. This active high bit - signals that an error was encountered during the CQ operation. */ - } CQSTAT_b; - } ; - - union { - __IOM uint32_t CQFLAGS; /*!< (@ 0x00000254) Command Queue Flag Register */ - - struct { - __IOM uint32_t CQFLAGS : 16; /*!< [15..0] Current flag status (read-only). Bits [7:0] are software - controllable and bits [15:8] are hardware status. */ - __IOM uint32_t CQIRQMASK : 16; /*!< [31..16] Provides for a per-bit mask of the flags used to invoke - an interrupt. A '1' in the bit position will enable the - pause event to trigger the interrupt, if the CQWT_int interrupt - is enabled.Bits definitions are the same as CQPAUSE */ - } CQFLAGS_b; - } ; - - union { - __IOM uint32_t CQSETCLEAR; /*!< (@ 0x00000258) Command Queue Flag Set/Clear Register */ - - struct { - __IOM uint32_t CQFSET : 8; /*!< [7..0] Set CQFlag status bits. Will set to 1 the value of any - SWFLAG with a '1' in the corresponding bit position of - this field */ - __IOM uint32_t CQFTGL : 8; /*!< [15..8] Toggle the indicated bit. Will toggle the value of any - SWFLAG with a '1' in the corresponding bit position of - this field */ - __IOM uint32_t CQFCLR : 8; /*!< [23..16] Clear CQFlag status bits. Will clear to 0 any SWFLAG - with a '1' in the corresponding bit position of this field */ - } CQSETCLEAR_b; - } ; - - union { - __IOM uint32_t CQPAUSEEN; /*!< (@ 0x0000025C) Command Queue Pause Enable Register */ - - struct { - __IOM uint32_t CQPEN : 16; /*!< [15..0] Enables the specified event to pause command processing - when active */ - } CQPAUSEEN_b; - } ; - - union { - __IOM uint32_t CQCURIDX; /*!< (@ 0x00000260) IOM Command Queue current index value . Compared - to the CQENDIDX reg contents to generate - the IDXEQ Pause event for command queue */ - - struct { - __IOM uint32_t CQCURIDX : 8; /*!< [7..0] Holds 8 bits of data that will be compared with the CQENDIX - register field. If the values match, the IDXEQ pause event - will be activated, which will cause the pausing of command - quue operation if the IDXEQ bit is enabled in CQPAUSEEN. */ - } CQCURIDX_b; - } ; - - union { - __IOM uint32_t CQENDIDX; /*!< (@ 0x00000264) IOM Command Queue current index value . Compared - to the CQCURIDX reg contents to generate - the IDXEQ Pause event for command queue */ - - struct { - __IOM uint32_t CQENDIDX : 8; /*!< [7..0] Holds 8 bits of data that will be compared with the CQCURIX - register field. If the values match, the IDXEQ pause event - will be activated, which will cause the pausing of command - quue operation if the IDXEQ bit is enabled in CQPAUSEEN. */ - } CQENDIDX_b; - } ; - - union { - __IOM uint32_t STATUS; /*!< (@ 0x00000268) IOM Module Status Register */ - - struct { - __IOM uint32_t ERR : 1; /*!< [0..0] Bit has been deprecated. Please refer to the other error - indicators. This will always return 0. */ - __IOM uint32_t CMDACT : 1; /*!< [1..1] Indicates if the active I/O Command is currently processing - a transaction, or command is complete, but the FIFO pointers - are still syncronizing internally. This bit will go high - atthe start of the transaction, and will go low when the - command is complete, and the data and pointers within the - FIFO have been syncronized. */ - __IOM uint32_t IDLEST : 1; /*!< [2..2] indicates if the active I/O state machine is IDLE. Note - - The state machine could be in idle state due to holdoffs - from data availability, or as the command gets propagated - into the logic from the registers. */ - } STATUS_b; - } ; - __IM uint32_t RESERVED4[37]; - - union { - __IOM uint32_t MSPICFG; /*!< (@ 0x00000300) SPI module master configuration */ - - struct { - __IOM uint32_t SPOL : 1; /*!< [0..0] This bit selects SPI polarity. */ - __IOM uint32_t SPHA : 1; /*!< [1..1] Selects the SPI phase; When 1, will shift the sampling - edge by 1/2 clock. */ - __IOM uint32_t FULLDUP : 1; /*!< [2..2] Full Duplex mode. Capture read data during writes operations */ - __IM uint32_t : 13; - __IOM uint32_t WTFC : 1; /*!< [16..16] Enables flow control of new write transactions based - on the SPI_STATUS signal from the BLE Core. */ - __IOM uint32_t RDFC : 1; /*!< [17..17] Enables flow control of new read transactions based - on the SPI_STATUS signal from the BLE Core. */ - __IM uint32_t : 3; - __IOM uint32_t WTFCPOL : 1; /*!< [21..21] Selects the write flow control signal polarity. The - transfers are halted when the selected flow control signal - is OPPOSITE polarity of this bit. (For example: WTFCPOL - = 0 will allow a SPI_STATUS=1 to pause transfers). */ - __IOM uint32_t RDFCPOL : 1; /*!< [22..22] Selects the read flow control signal polarity. When - set, the clock will be held low until the flow control - is de-asserted. */ - __IOM uint32_t SPILSB : 1; /*!< [23..23] Selects data transfer as MSB first (0) or LSB first - (1) for the data portion of the SPI transaction. The offset - bytes are always transmitted MSB first. */ - __IOM uint32_t DINDLY : 3; /*!< [26..24] Delay tap to use for the input signal (MISO). This - gives more hold time on the input data. */ - __IOM uint32_t DOUTDLY : 3; /*!< [29..27] Delay tap to use for the output signal (MOSI). This - give more hold time on the output data. */ - __IOM uint32_t MSPIRST : 1; /*!< [30..30] Bit is deprecated. setting it will have no effect. */ - } MSPICFG_b; - } ; - - union { - __IOM uint32_t BLECFG; /*!< (@ 0x00000304) BLE Core Control */ - - struct { - __IOM uint32_t PWRSMEN : 1; /*!< [0..0] Enable the power state machine for automatic sequencing - and control of power states of the BLE Core module. */ - __IOM uint32_t BLERSTN : 1; /*!< [1..1] Reset line to the BLE Core. This will reset the BLE core - when asserted ('0') and must be written to '1' prior to - performing any BTLE related operations to the core. */ - __IOM uint32_t WAKEUPCTL : 2; /*!< [3..2] WAKE signal override. Controls the source of the WAKE - signal to the BLE Core. */ - __IOM uint32_t DCDCFLGCTL : 2; /*!< [5..4] DCDCFLG signal override. The value of this field will - be sent to the BLE Core when the PWRSM is off. Otherwise, - the value is supplied from internal logic. */ - __IOM uint32_t BLEHREQCTL : 2; /*!< [7..6] BLEH power on request override. The value of this field - will be sent to the BLE Core when the PWRSM is off. Otherwise, - the value is supplied from internal logic. */ - __IOM uint32_t WT4ACTOFF : 1; /*!< [8..8] Debug control of BLEIF power state machine. Allows transition - into the active state in the BLEIF state without waiting - for dcdc req from BLE Core. */ - __IOM uint32_t MCUFRCSLP : 1; /*!< [9..9] Force power state machine to go to the sleep state. Intended - for debug only. Has no effect on the actual BLE Core state, - only the state of the BLEIF interface state machine. */ - __IOM uint32_t FRCCLK : 1; /*!< [10..10] Force the clock in the BLEIF to be always running */ - __IOM uint32_t STAYASLEEP : 1; /*!< [11..11] Set to prevent the BLE power control module from waking - up the BLE Core after going into power down. To be used - for graceful shutdown, set by software prior to powering - off and will allow assertion of reset from sleep state. */ - __IOM uint32_t PWRISOCTL : 2; /*!< [13..12] Configuration of BLEH isolation control for power related - signals. */ - __IOM uint32_t SPIISOCTL : 2; /*!< [15..14] Configuration of BLEH isolation controls for SPI related - signals. */ - } BLECFG_b; - } ; - - union { - __IOM uint32_t PWRCMD; /*!< (@ 0x00000308) BLE Power command interface */ - - struct { - __IOM uint32_t WAKEREQ : 1; /*!< [0..0] Wake request from the MCU. When asserted (1), the BLE - Interface logic will assert the wakeup request signal to - the BLE Core. Only recognized when in the sleep state */ - __IOM uint32_t RESTART : 1; /*!< [1..1] Restart the BLE Core after going into the shutdown state. - Only valid when in the shutdown state. */ - } PWRCMD_b; - } ; - - union { - __IOM uint32_t BSTATUS; /*!< (@ 0x0000030C) BLE Core status */ - - struct { - __IOM uint32_t B2MSTATE : 3; /*!< [2..0] State of the BLE Core logic. */ - __IOM uint32_t SPISTATUS : 1; /*!< [3..3] Value of the SPISTATUS signal from the BLE Core. The - signal is asserted when the BLE Core is able to accept - write data via the SPI interface. Data should be transmitted - to theBLE core only when this signal is 1. The hardware - will automatically wait for this signal prior to performing - a write operation if flow control is active. */ - __IOM uint32_t DCDCREQ : 1; /*!< [4..4] Value of the DCDCREQ signal from the BLE Core. The DCDCREQ - signal is sent from the core to the BLEIF module when the - BLE core requires BLEH power to be active. When activated, - this isindicated by DCDCFLAG going to 1. */ - __IOM uint32_t DCDCFLAG : 1; /*!< [5..5] Value of the DCDCFLAG signal to the BLE Core. The DCDCFLAG - is a signal to the BLE Core indicating that the BLEH ppower - is active. */ - __IOM uint32_t WAKEUP : 1; /*!< [6..6] Value of the WAKEUP signal to the BLE Core . The WAKEUP - signals is sent from the BLEIF to the BLECORE to request - the BLE Core transition from sleep state to active state. */ - __IOM uint32_t BLEIRQ : 1; /*!< [7..7] Status of the BLEIRQ signal from the BLE Core. A value - of 1 idicates that read data is available in the core and - a read operation needs to be performed. */ - __IOM uint32_t PWRST : 3; /*!< [10..8] Current status of the power state machine */ - __IOM uint32_t BLEHACK : 1; /*!< [11..11] Value of the BLEHACK signal from the power control - unit. If the signal is '1', the BLEH power is active and - ready for use. */ - __IOM uint32_t BLEHREQ : 1; /*!< [12..12] Value of the BLEHREQ signal to the power control unit. - The BLEHREQ signal is sent from the BLEIF module to the - power control module to request the BLEH power up. When - the BLEHACK signal is asserted,BLEH power is stable and - ready for use. */ - } BSTATUS_b; - } ; - __IM uint32_t RESERVED5[64]; - - union { - __IOM uint32_t BLEDBG; /*!< (@ 0x00000410) BLEIF Master Debug Register */ - - struct { - __IOM uint32_t DBGEN : 1; /*!< [0..0] Debug Enable. Setting this bit will enable the update - of data within this register, otherwise it is clock gated - for power savings */ - __IOM uint32_t IOCLKON : 1; /*!< [1..1] IOCLK debug clock control. Enable IO_CLK to be active - when this bit is '1'. Otherwise, the clock is controlled - with gating from the logic as needed. */ - __IOM uint32_t APBCLKON : 1; /*!< [2..2] APBCLK debug clock control. Enable APB_CLK to be active - when this bit is '1'. Otherwise, the clock is controlled - with gating from the logic as needed. */ - __IOM uint32_t DBGDATA : 29; /*!< [31..3] Debug data */ - } BLEDBG_b; - } ; -} BLEIF_Type; /*!< Size = 1044 (0x414) */ - - - -/* =========================================================================================================================== */ -/* ================ CACHECTRL ================ */ -/* =========================================================================================================================== */ - - -/** - * @brief Flash Cache Controller (CACHECTRL) - */ - -typedef struct { /*!< (@ 0x40018000) CACHECTRL Structure */ - - union { - __IOM uint32_t CACHECFG; /*!< (@ 0x00000000) Flash Cache Control Register */ - - struct { - __IOM uint32_t ENABLE : 1; /*!< [0..0] Enables the flash cache controller and enables power - to the cache SRAMs. The ICACHE_ENABLE and DCACHE_ENABLE - should be set to enable caching for each type of access. */ - __IOM uint32_t LRU : 1; /*!< [1..1] Sets the cache repleacment policy. 0=LRR (least recently - replaced), 1=LRU (least recently used). LRR minimizes writes - to the TAG SRAM. */ - __IOM uint32_t ENABLE_NC0 : 1; /*!< [2..2] Enable Non-cacheable region 0. See NCR0 registers to - define the region. */ - __IOM uint32_t ENABLE_NC1 : 1; /*!< [3..3] Enable Non-cacheable region 1. See NCR1 registers to - define the region. */ - __IOM uint32_t CONFIG : 4; /*!< [7..4] Sets the cache configuration */ - __IOM uint32_t ICACHE_ENABLE : 1; /*!< [8..8] Enable Flash Instruction Caching */ - __IOM uint32_t DCACHE_ENABLE : 1; /*!< [9..9] Enable Flash Data Caching. */ - __IOM uint32_t CACHE_CLKGATE : 1; /*!< [10..10] Enable clock gating of cache TAG RAM. Software should - enable this bit for optimal power efficiency. */ - __IOM uint32_t CACHE_LS : 1; /*!< [11..11] Enable LS (light sleep) of cache RAMs. Software should - DISABLE this bit since cache activity is too high to benefit - from LS usage. */ - __IM uint32_t : 8; - __IOM uint32_t DATA_CLKGATE : 1; /*!< [20..20] Enable aggressive clock gating of entire data array. - This bit should be set to 1 for optimal power efficiency. */ - __IM uint32_t : 3; - __IOM uint32_t ENABLE_MONITOR : 1; /*!< [24..24] Enable Cache Monitoring Stats. Cache monitoring consumes - additional power and should only be enabled when profiling - code and counters will increment when this bit is set. - Counter values will be retained when this is set to 0, - allowing software to enable/disable counting for multiple - code segments. */ - } CACHECFG_b; - } ; - - union { - __IOM uint32_t FLASHCFG; /*!< (@ 0x00000004) Flash Control Register */ - - struct { - __IOM uint32_t RD_WAIT : 4; /*!< [3..0] Sets read waitstates for normal (fast) operation. A value - of 1 is recommended. */ - __IOM uint32_t SEDELAY : 3; /*!< [6..4] Sets SE delay (flash address setup). A value of 5 is - recommended. */ - __IM uint32_t : 1; - __IOM uint32_t LPM_RD_WAIT : 4; /*!< [11..8] Sets flash waitstates when in LPM Mode 2 (RD_WAIT in - LPM mode 2 only) */ - __IOM uint32_t LPMMODE : 2; /*!< [13..12] Controls flash low power modes (control of LPM pin). */ - } FLASHCFG_b; - } ; - - union { - __IOM uint32_t CTRL; /*!< (@ 0x00000008) Cache Control */ - - struct { - __IOM uint32_t INVALIDATE : 1; /*!< [0..0] Writing a 1 to this bitfield invalidates the flash cache - contents. */ - __IOM uint32_t RESET_STAT : 1; /*!< [1..1] Reset Cache Statistics. When written to a 1, the cache - monitor counters will be cleared. The monitor counters - can be reset only when the CACHECFG.ENABLE_MONITOR bit - is set. */ - __IOM uint32_t CACHE_READY : 1; /*!< [2..2] Cache Ready Status (enabled and not processing an invalidate - operation) */ - __IM uint32_t : 1; - __IOM uint32_t FLASH0_SLM_STATUS : 1; /*!< [4..4] Flash Sleep Mode Status. 1 indicates that flash0 is in - sleep mode, 0 indicates flash0 is in normal mode. */ - __IOM uint32_t FLASH0_SLM_DISABLE : 1; /*!< [5..5] Disable Flash Sleep Mode. Write 1 to wake flash0 from - sleep mode (reading the array will also automatically wake - it). */ - __IOM uint32_t FLASH0_SLM_ENABLE : 1; /*!< [6..6] Enable Flash Sleep Mode. Write to 1 to put flash 0 into - sleep mode. NOTE: there is a 5us latency after waking flash - until the first access will be returned. */ - __IM uint32_t : 1; - __IOM uint32_t FLASH1_SLM_STATUS : 1; /*!< [8..8] Flash Sleep Mode Status. 1 indicates that flash1 is in - sleep mode, 0 indicates flash1 is in normal mode. */ - __IOM uint32_t FLASH1_SLM_DISABLE : 1; /*!< [9..9] Disable Flash Sleep Mode. Write 1 to wake flash1 from - sleep mode (reading the array will also automatically wake - it). */ - __IOM uint32_t FLASH1_SLM_ENABLE : 1; /*!< [10..10] Enable Flash Sleep Mode. Write to 1 to put flash 1 - into sleep mode. NOTE: there is a 5us latency after waking - flash until the first access will be returned. */ - } CTRL_b; - } ; - __IM uint32_t RESERVED; - - union { - __IOM uint32_t NCR0START; /*!< (@ 0x00000010) Flash Cache Noncachable Region 0 Start */ - - struct { - __IM uint32_t : 4; - __IOM uint32_t ADDR : 23; /*!< [26..4] Start address for non-cacheable region 0 */ - } NCR0START_b; - } ; - - union { - __IOM uint32_t NCR0END; /*!< (@ 0x00000014) Flash Cache Noncachable Region 0 End */ - - struct { - __IM uint32_t : 4; - __IOM uint32_t ADDR : 23; /*!< [26..4] End address for non-cacheable region 0 */ - } NCR0END_b; - } ; - - union { - __IOM uint32_t NCR1START; /*!< (@ 0x00000018) Flash Cache Noncachable Region 1 Start */ - - struct { - __IM uint32_t : 4; - __IOM uint32_t ADDR : 23; /*!< [26..4] Start address for non-cacheable region 1 */ - } NCR1START_b; - } ; - - union { - __IOM uint32_t NCR1END; /*!< (@ 0x0000001C) Flash Cache Noncachable Region 1 End */ - - struct { - __IM uint32_t : 4; - __IOM uint32_t ADDR : 23; /*!< [26..4] End address for non-cacheable region 1 */ - } NCR1END_b; - } ; - __IM uint32_t RESERVED1[8]; - - union { - __IOM uint32_t DMON0; /*!< (@ 0x00000040) Data Cache Total Accesses */ - - struct { - __IOM uint32_t DACCESS_COUNT : 32; /*!< [31..0] Total accesses to data cache. All performance metrics - should be relative to the number of accesses performed. */ - } DMON0_b; - } ; - - union { - __IOM uint32_t DMON1; /*!< (@ 0x00000044) Data Cache Tag Lookups */ - - struct { - __IOM uint32_t DLOOKUP_COUNT : 32; /*!< [31..0] Total tag lookups from data cache. */ - } DMON1_b; - } ; - - union { - __IOM uint32_t DMON2; /*!< (@ 0x00000048) Data Cache Hits */ - - struct { - __IOM uint32_t DHIT_COUNT : 32; /*!< [31..0] Cache hits from lookup operations. */ - } DMON2_b; - } ; - - union { - __IOM uint32_t DMON3; /*!< (@ 0x0000004C) Data Cache Line Hits */ - - struct { - __IOM uint32_t DLINE_COUNT : 32; /*!< [31..0] Cache hits from line cache */ - } DMON3_b; - } ; - - union { - __IOM uint32_t IMON0; /*!< (@ 0x00000050) Instruction Cache Total Accesses */ - - struct { - __IOM uint32_t IACCESS_COUNT : 32; /*!< [31..0] Total accesses to Instruction cache */ - } IMON0_b; - } ; - - union { - __IOM uint32_t IMON1; /*!< (@ 0x00000054) Instruction Cache Tag Lookups */ - - struct { - __IOM uint32_t ILOOKUP_COUNT : 32; /*!< [31..0] Total tag lookups from Instruction cache */ - } IMON1_b; - } ; - - union { - __IOM uint32_t IMON2; /*!< (@ 0x00000058) Instruction Cache Hits */ - - struct { - __IOM uint32_t IHIT_COUNT : 32; /*!< [31..0] Cache hits from lookup operations */ - } IMON2_b; - } ; - - union { - __IOM uint32_t IMON3; /*!< (@ 0x0000005C) Instruction Cache Line Hits */ - - struct { - __IOM uint32_t ILINE_COUNT : 32; /*!< [31..0] Cache hits from line cache */ - } IMON3_b; - } ; -} CACHECTRL_Type; /*!< Size = 96 (0x60) */ - - - -/* =========================================================================================================================== */ -/* ================ CLKGEN ================ */ -/* =========================================================================================================================== */ - - -/** - * @brief Clock Generator (CLKGEN) - */ - -typedef struct { /*!< (@ 0x40004000) CLKGEN Structure */ - - union { - __IOM uint32_t CALXT; /*!< (@ 0x00000000) XT Oscillator Control */ - - struct { - __IOM uint32_t CALXT : 11; /*!< [10..0] XT Oscillator calibration value. This register will - enable the hardware to increase or decrease the number - of cycles in a 16KHz clock derived from the original 32KHz - version. The most significant bit is the sign. A '1' is - a reduction, and a '0' is an addition. This calibration - value will add or reduce the number of cycles programmed - here across a 32 second interval. The maximum value that - is effective is from -1024 to 1023. */ - } CALXT_b; - } ; - - union { - __IOM uint32_t CALRC; /*!< (@ 0x00000004) RC Oscillator Control */ - - struct { - __IOM uint32_t CALRC : 18; /*!< [17..0] LFRC Oscillator calibration value. This register will - enable the hardware to increase or decrease the number - of cycles in a 512 Hz clock derived from the original 1024 - version. The most significant bit is the sign. A '1' is - a reduction, and a '0' is an addition. This calibration - value will add or reduce the number of cycles programmed - here across a 32 second interval. The range is from -131072 - (decimal) to 131071 (decimal). This register is normally - used in conjuction with ACALCTR register. The CAL */ - } CALRC_b; - } ; - - union { - __IOM uint32_t ACALCTR; /*!< (@ 0x00000008) Autocalibration Counter */ - - struct { - __IOM uint32_t ACALCTR : 24; /*!< [23..0] Autocalibration Counter result. Bits 17 down to 0 of - this is feed directly to the CALRC register if ACAL register - in OCTRL register is set to 1024SEC or 512SEC. */ - } ACALCTR_b; - } ; - - union { - __IOM uint32_t OCTRL; /*!< (@ 0x0000000C) Oscillator Control */ - - struct { - __IOM uint32_t STOPXT : 1; /*!< [0..0] Stop the XT Oscillator to the RTC */ - __IOM uint32_t STOPRC : 1; /*!< [1..1] Stop the LFRC Oscillator to the RTC */ - __IM uint32_t : 4; - __IOM uint32_t FOS : 1; /*!< [6..6] Oscillator switch on failure function. If this is set, - then LFRC clock source will switch from XT to RC. */ - __IOM uint32_t OSEL : 1; /*!< [7..7] Selects the RTC oscillator (1 => LFRC, 0 => XT) */ - __IOM uint32_t ACAL : 3; /*!< [10..8] Autocalibration control. This selects the source to - be used in the autocalibration flow. This flow can also - be used to measure an internal clock against an external - clock source, with the external clock normally used as - the reference. */ - } OCTRL_b; - } ; - - union { - __IOM uint32_t CLKOUT; /*!< (@ 0x00000010) CLKOUT Frequency Select */ - - struct { - __IOM uint32_t CKSEL : 6; /*!< [5..0] CLKOUT signal select */ - __IM uint32_t : 1; - __IOM uint32_t CKEN : 1; /*!< [7..7] Enable the CLKOUT signal */ - } CLKOUT_b; - } ; - - union { - __IOM uint32_t CLKKEY; /*!< (@ 0x00000014) Key Register for Clock Control Register */ - - struct { - __IOM uint32_t CLKKEY : 32; /*!< [31..0] Key register value. */ - } CLKKEY_b; - } ; - - union { - __IOM uint32_t CCTRL; /*!< (@ 0x00000018) HFRC Clock Control */ - - struct { - __IOM uint32_t CORESEL : 1; /*!< [0..0] Core Clock divisor */ - } CCTRL_b; - } ; - - union { - __IOM uint32_t STATUS; /*!< (@ 0x0000001C) Clock Generator Status */ - - struct { - __IOM uint32_t OMODE : 1; /*!< [0..0] Current RTC oscillator (1 => LFRC, 0 => XT). After an - RTC oscillator change, it may take up to 2 seconds for - this field to reflect the new oscillator. */ - __IOM uint32_t OSCF : 1; /*!< [1..1] XT Oscillator is enabled but not oscillating */ - } STATUS_b; - } ; - - union { - __IOM uint32_t HFADJ; /*!< (@ 0x00000020) HFRC Adjustment */ - - struct { - __IOM uint32_t HFADJEN : 1; /*!< [0..0] HFRC adjustment control */ - __IOM uint32_t HFADJCK : 3; /*!< [3..1] Repeat period for HFRC adjustment */ - __IM uint32_t : 4; - __IOM uint32_t HFXTADJ : 12; /*!< [19..8] Target HFRC adjustment value. */ - __IOM uint32_t HFWARMUP : 1; /*!< [20..20] XT warmup period for HFRC adjustment */ - __IOM uint32_t HFADJGAIN : 3; /*!< [23..21] Gain control for HFRC adjustment */ - } HFADJ_b; - } ; - __IM uint32_t RESERVED; - - union { - __IOM uint32_t CLOCKENSTAT; /*!< (@ 0x00000028) Clock Enable Status */ - - struct { - __IOM uint32_t CLOCKENSTAT : 32; /*!< [31..0] Clock enable status */ - } CLOCKENSTAT_b; - } ; - - union { - __IOM uint32_t CLOCKEN2STAT; /*!< (@ 0x0000002C) Clock Enable Status */ - - struct { - __IOM uint32_t CLOCKEN2STAT : 32; /*!< [31..0] Clock enable status 2 */ - } CLOCKEN2STAT_b; - } ; - - union { - __IOM uint32_t CLOCKEN3STAT; /*!< (@ 0x00000030) Clock Enable Status */ - - struct { - __IOM uint32_t CLOCKEN3STAT : 32; /*!< [31..0] Clock enable status 3 */ - } CLOCKEN3STAT_b; - } ; - - union { - __IOM uint32_t FREQCTRL; /*!< (@ 0x00000034) HFRC Frequency Control register */ - - struct { - __IOM uint32_t BURSTREQ : 1; /*!< [0..0] Frequency Burst Enable Request */ - __IOM uint32_t BURSTACK : 1; /*!< [1..1] Frequency Burst Request Acknowledge. Frequency burst - requested is always acknowledged whether burst is granted - or not depending on feature enable. */ - __IOM uint32_t BURSTSTATUS : 1; /*!< [2..2] This represents frequency burst status. */ - } FREQCTRL_b; - } ; - __IM uint32_t RESERVED1; - - union { - __IOM uint32_t BLEBUCKTONADJ; /*!< (@ 0x0000003C) BLE BUCK TON ADJUST */ - - struct { - __IOM uint32_t TONLOWTHRESHOLD : 10; /*!< [9..0] TON ADJUST LOW THRESHOLD. Suggested values are #A(94KHz) - #15(47KHz) #53(12Khz) #14D(3Khz) */ - __IOM uint32_t TONHIGHTHRESHOLD : 10; /*!< [19..10] TON ADJUST HIGH THRESHOLD. Suggested values are #15(94KHz) - #2A(47Khz) #A6(12Khz) #29A(3Khz) */ - __IOM uint32_t TONADJUSTPERIOD : 2; /*!< [21..20] TON ADJUST PERIOD */ - __IOM uint32_t TONADJUSTEN : 1; /*!< [22..22] TON ADJUST ENABLE */ - __IOM uint32_t ZEROLENDETECTTRIM : 4; /*!< [26..23] BLEBUCK ZERO LENGTH DETECT TRIM */ - __IOM uint32_t ZEROLENDETECTEN : 1; /*!< [27..27] BLEBUCK ZERO LENGTH DETECT ENABLE */ - } BLEBUCKTONADJ_b; - } ; - __IM uint32_t RESERVED2[48]; - - union { - __IOM uint32_t INTRPTEN; /*!< (@ 0x00000100) CLKGEN Interrupt Register: Enable */ - - struct { - __IOM uint32_t ACF : 1; /*!< [0..0] Autocalibration Fail interrupt */ - __IOM uint32_t ACC : 1; /*!< [1..1] Autocalibration Complete interrupt */ - __IOM uint32_t OF : 1; /*!< [2..2] XT Oscillator Fail interrupt */ - } INTRPTEN_b; - } ; - - union { - __IOM uint32_t INTRPTSTAT; /*!< (@ 0x00000104) CLKGEN Interrupt Register: Status */ - - struct { - __IOM uint32_t ACF : 1; /*!< [0..0] Autocalibration Fail interrupt */ - __IOM uint32_t ACC : 1; /*!< [1..1] Autocalibration Complete interrupt */ - __IOM uint32_t OF : 1; /*!< [2..2] XT Oscillator Fail interrupt */ - } INTRPTSTAT_b; - } ; - - union { - __IOM uint32_t INTRPTCLR; /*!< (@ 0x00000108) CLKGEN Interrupt Register: Clear */ - - struct { - __IOM uint32_t ACF : 1; /*!< [0..0] Autocalibration Fail interrupt */ - __IOM uint32_t ACC : 1; /*!< [1..1] Autocalibration Complete interrupt */ - __IOM uint32_t OF : 1; /*!< [2..2] XT Oscillator Fail interrupt */ - } INTRPTCLR_b; - } ; - - union { - __IOM uint32_t INTRPTSET; /*!< (@ 0x0000010C) CLKGEN Interrupt Register: Set */ - - struct { - __IOM uint32_t ACF : 1; /*!< [0..0] Autocalibration Fail interrupt */ - __IOM uint32_t ACC : 1; /*!< [1..1] Autocalibration Complete interrupt */ - __IOM uint32_t OF : 1; /*!< [2..2] XT Oscillator Fail interrupt */ - } INTRPTSET_b; - } ; -} CLKGEN_Type; /*!< Size = 272 (0x110) */ - - - -/* =========================================================================================================================== */ -/* ================ CTIMER ================ */ -/* =========================================================================================================================== */ - - -/** - * @brief Counter/Timer (CTIMER) - */ - -typedef struct { /*!< (@ 0x40008000) CTIMER Structure */ - - union { - __IOM uint32_t TMR0; /*!< (@ 0x00000000) Counter/Timer Register */ - - struct { - __IOM uint32_t CTTMRA0 : 16; /*!< [15..0] Counter/Timer A0. */ - __IOM uint32_t CTTMRB0 : 16; /*!< [31..16] Counter/Timer B0. */ - } TMR0_b; - } ; - - union { - __IOM uint32_t CMPRA0; /*!< (@ 0x00000004) Counter/Timer A0 Compare Registers */ - - struct { - __IOM uint32_t CMPR0A0 : 16; /*!< [15..0] Counter/Timer A0 Compare Register 0. Holds the lower - limit for timer half A. */ - __IOM uint32_t CMPR1A0 : 16; /*!< [31..16] Counter/Timer A0 Compare Register 1. Holds the upper - limit for timer half A. */ - } CMPRA0_b; - } ; - - union { - __IOM uint32_t CMPRB0; /*!< (@ 0x00000008) Counter/Timer B0 Compare Registers */ - - struct { - __IOM uint32_t CMPR0B0 : 16; /*!< [15..0] Counter/Timer B0 Compare Register 0. Holds the lower - limit for timer half B. */ - __IOM uint32_t CMPR1B0 : 16; /*!< [31..16] Counter/Timer B0 Compare Register 1. Holds the upper - limit for timer half B. */ - } CMPRB0_b; - } ; - - union { - __IOM uint32_t CTRL0; /*!< (@ 0x0000000C) Counter/Timer Control */ - - struct { - __IOM uint32_t TMRA0EN : 1; /*!< [0..0] Counter/Timer A0 Enable bit. */ - __IOM uint32_t TMRA0CLK : 5; /*!< [5..1] Counter/Timer A0 Clock Select. */ - __IOM uint32_t TMRA0FN : 3; /*!< [8..6] Counter/Timer A0 Function Select. */ - __IOM uint32_t TMRA0IE0 : 1; /*!< [9..9] Counter/Timer A0 Interrupt Enable bit based on COMPR0. */ - __IOM uint32_t TMRA0IE1 : 1; /*!< [10..10] Counter/Timer A0 Interrupt Enable bit based on COMPR1. */ - __IOM uint32_t TMRA0CLR : 1; /*!< [11..11] Counter/Timer A0 Clear bit. */ - __IOM uint32_t TMRA0POL : 1; /*!< [12..12] Counter/Timer A0 output polarity. */ - __IM uint32_t : 3; - __IOM uint32_t TMRB0EN : 1; /*!< [16..16] Counter/Timer B0 Enable bit. */ - __IOM uint32_t TMRB0CLK : 5; /*!< [21..17] Counter/Timer B0 Clock Select. */ - __IOM uint32_t TMRB0FN : 3; /*!< [24..22] Counter/Timer B0 Function Select. */ - __IOM uint32_t TMRB0IE0 : 1; /*!< [25..25] Counter/Timer B0 Interrupt Enable bit for COMPR0. */ - __IOM uint32_t TMRB0IE1 : 1; /*!< [26..26] Counter/Timer B0 Interrupt Enable bit for COMPR1. */ - __IOM uint32_t TMRB0CLR : 1; /*!< [27..27] Counter/Timer B0 Clear bit. */ - __IOM uint32_t TMRB0POL : 1; /*!< [28..28] Counter/Timer B0 output polarity. */ - __IM uint32_t : 2; - __IOM uint32_t CTLINK0 : 1; /*!< [31..31] Counter/Timer A0/B0 Link bit. */ - } CTRL0_b; - } ; - __IM uint32_t RESERVED; - - union { - __IOM uint32_t CMPRAUXA0; /*!< (@ 0x00000014) Counter/Timer A0 Compare Registers */ - - struct { - __IOM uint32_t CMPR2A0 : 16; /*!< [15..0] Counter/Timer A0 Compare Register 2. Holds the lower - limit for timer half A. */ - __IOM uint32_t CMPR3A0 : 16; /*!< [31..16] Counter/Timer A0 Compare Register 3. Holds the upper - limit for timer half A. */ - } CMPRAUXA0_b; - } ; - - union { - __IOM uint32_t CMPRAUXB0; /*!< (@ 0x00000018) Counter/Timer B0 Compare Registers */ - - struct { - __IOM uint32_t CMPR2B0 : 16; /*!< [15..0] Counter/Timer B0 Compare Register 2. Holds the lower - limit for timer half B. */ - __IOM uint32_t CMPR3B0 : 16; /*!< [31..16] Counter/Timer B0 Compare Register 3. Holds the upper - limit for timer half B. */ - } CMPRAUXB0_b; - } ; - - union { - __IOM uint32_t AUX0; /*!< (@ 0x0000001C) Counter/Timer Auxiliary */ - - struct { - __IOM uint32_t TMRA0LMT : 7; /*!< [6..0] Counter/Timer A0 Pattern Limit Count. */ - __IOM uint32_t TMRA0TRIG : 4; /*!< [10..7] Counter/Timer A0 Trigger Select. */ - __IOM uint32_t TMRA0NOSYNC : 1; /*!< [11..11] Source clock synchronization control. */ - __IOM uint32_t TMRA0TINV : 1; /*!< [12..12] Counter/Timer A0 Invert on trigger. */ - __IOM uint32_t TMRA0POL23 : 1; /*!< [13..13] Counter/Timer A0 Upper output polarity */ - __IOM uint32_t TMRA0EN23 : 1; /*!< [14..14] Counter/Timer A0 Upper compare enable. */ - __IM uint32_t : 1; - __IOM uint32_t TMRB0LMT : 6; /*!< [21..16] Counter/Timer B0 Pattern Limit Count. */ - __IM uint32_t : 1; - __IOM uint32_t TMRB0TRIG : 4; /*!< [26..23] Counter/Timer B0 Trigger Select. */ - __IOM uint32_t TMRB0NOSYNC : 1; /*!< [27..27] Source clock synchronization control. */ - __IOM uint32_t TMRB0TINV : 1; /*!< [28..28] Counter/Timer B0 Invert on trigger. */ - __IOM uint32_t TMRB0POL23 : 1; /*!< [29..29] Upper output polarity */ - __IOM uint32_t TMRB0EN23 : 1; /*!< [30..30] Counter/Timer B0 Upper compare enable. */ - } AUX0_b; - } ; - - union { - __IOM uint32_t TMR1; /*!< (@ 0x00000020) Counter/Timer Register */ - - struct { - __IOM uint32_t CTTMRA1 : 16; /*!< [15..0] Counter/Timer A1. */ - __IOM uint32_t CTTMRB1 : 16; /*!< [31..16] Counter/Timer B1. */ - } TMR1_b; - } ; - - union { - __IOM uint32_t CMPRA1; /*!< (@ 0x00000024) Counter/Timer A1 Compare Registers */ - - struct { - __IOM uint32_t CMPR0A1 : 16; /*!< [15..0] Counter/Timer A1 Compare Register 0. */ - __IOM uint32_t CMPR1A1 : 16; /*!< [31..16] Counter/Timer A1 Compare Register 1. */ - } CMPRA1_b; - } ; - - union { - __IOM uint32_t CMPRB1; /*!< (@ 0x00000028) Counter/Timer B1 Compare Registers */ - - struct { - __IOM uint32_t CMPR0B1 : 16; /*!< [15..0] Counter/Timer B1 Compare Register 0. */ - __IOM uint32_t CMPR1B1 : 16; /*!< [31..16] Counter/Timer B1 Compare Register 1. */ - } CMPRB1_b; - } ; - - union { - __IOM uint32_t CTRL1; /*!< (@ 0x0000002C) Counter/Timer Control */ - - struct { - __IOM uint32_t TMRA1EN : 1; /*!< [0..0] Counter/Timer A1 Enable bit. */ - __IOM uint32_t TMRA1CLK : 5; /*!< [5..1] Counter/Timer A1 Clock Select. */ - __IOM uint32_t TMRA1FN : 3; /*!< [8..6] Counter/Timer A1 Function Select. */ - __IOM uint32_t TMRA1IE0 : 1; /*!< [9..9] Counter/Timer A1 Interrupt Enable bit based on COMPR0. */ - __IOM uint32_t TMRA1IE1 : 1; /*!< [10..10] Counter/Timer A1 Interrupt Enable bit based on COMPR1. */ - __IOM uint32_t TMRA1CLR : 1; /*!< [11..11] Counter/Timer A1 Clear bit. */ - __IOM uint32_t TMRA1POL : 1; /*!< [12..12] Counter/Timer A1 output polarity. */ - __IM uint32_t : 3; - __IOM uint32_t TMRB1EN : 1; /*!< [16..16] Counter/Timer B1 Enable bit. */ - __IOM uint32_t TMRB1CLK : 5; /*!< [21..17] Counter/Timer B1 Clock Select. */ - __IOM uint32_t TMRB1FN : 3; /*!< [24..22] Counter/Timer B1 Function Select. */ - __IOM uint32_t TMRB1IE0 : 1; /*!< [25..25] Counter/Timer B1 Interrupt Enable bit for COMPR0. */ - __IOM uint32_t TMRB1IE1 : 1; /*!< [26..26] Counter/Timer B1 Interrupt Enable bit for COMPR1. */ - __IOM uint32_t TMRB1CLR : 1; /*!< [27..27] Counter/Timer B1 Clear bit. */ - __IOM uint32_t TMRB1POL : 1; /*!< [28..28] Counter/Timer B1 output polarity. */ - __IM uint32_t : 2; - __IOM uint32_t CTLINK1 : 1; /*!< [31..31] Counter/Timer A1/B1 Link bit. */ - } CTRL1_b; - } ; - __IM uint32_t RESERVED1; - - union { - __IOM uint32_t CMPRAUXA1; /*!< (@ 0x00000034) Counter/Timer A1 Compare Registers */ - - struct { - __IOM uint32_t CMPR2A1 : 16; /*!< [15..0] Counter/Timer A1 Compare Register 2. Holds the lower - limit for timer half A. */ - __IOM uint32_t CMPR3A1 : 16; /*!< [31..16] Counter/Timer A1 Compare Register 3. Holds the upper - limit for timer half A. */ - } CMPRAUXA1_b; - } ; - - union { - __IOM uint32_t CMPRAUXB1; /*!< (@ 0x00000038) Counter/Timer B1 Compare Registers */ - - struct { - __IOM uint32_t CMPR2B1 : 16; /*!< [15..0] Counter/Timer B1 Compare Register 2. Holds the lower - limit for timer half B. */ - __IOM uint32_t CMPR3B1 : 16; /*!< [31..16] Counter/Timer B1 Compare Register 3. Holds the upper - limit for timer half B. */ - } CMPRAUXB1_b; - } ; - - union { - __IOM uint32_t AUX1; /*!< (@ 0x0000003C) Counter/Timer Auxiliary */ - - struct { - __IOM uint32_t TMRA1LMT : 7; /*!< [6..0] Counter/Timer A1 Pattern Limit Count. */ - __IOM uint32_t TMRA1TRIG : 4; /*!< [10..7] Counter/Timer A1 Trigger Select. */ - __IOM uint32_t TMRA1NOSYNC : 1; /*!< [11..11] Source clock synchronization control. */ - __IOM uint32_t TMRA1TINV : 1; /*!< [12..12] Counter/Timer A1 Invert on trigger. */ - __IOM uint32_t TMRA1POL23 : 1; /*!< [13..13] Counter/Timer A1 Upper output polarity */ - __IOM uint32_t TMRA1EN23 : 1; /*!< [14..14] Counter/Timer A1 Upper compare enable. */ - __IM uint32_t : 1; - __IOM uint32_t TMRB1LMT : 6; /*!< [21..16] Counter/Timer B1 Pattern Limit Count. */ - __IM uint32_t : 1; - __IOM uint32_t TMRB1TRIG : 4; /*!< [26..23] Counter/Timer B1 Trigger Select. */ - __IOM uint32_t TMRB1NOSYNC : 1; /*!< [27..27] Source clock synchronization control. */ - __IOM uint32_t TMRB1TINV : 1; /*!< [28..28] Counter/Timer B1 Invert on trigger. */ - __IOM uint32_t TMRB1POL23 : 1; /*!< [29..29] Upper output polarity */ - __IOM uint32_t TMRB1EN23 : 1; /*!< [30..30] Counter/Timer B1 Upper compare enable. */ - } AUX1_b; - } ; - - union { - __IOM uint32_t TMR2; /*!< (@ 0x00000040) Counter/Timer Register */ - - struct { - __IOM uint32_t CTTMRA2 : 16; /*!< [15..0] Counter/Timer A2. */ - __IOM uint32_t CTTMRB2 : 16; /*!< [31..16] Counter/Timer B2. */ - } TMR2_b; - } ; - - union { - __IOM uint32_t CMPRA2; /*!< (@ 0x00000044) Counter/Timer A2 Compare Registers */ - - struct { - __IOM uint32_t CMPR0A2 : 16; /*!< [15..0] Counter/Timer A2 Compare Register 0. */ - __IOM uint32_t CMPR1A2 : 16; /*!< [31..16] Counter/Timer A2 Compare Register 1. */ - } CMPRA2_b; - } ; - - union { - __IOM uint32_t CMPRB2; /*!< (@ 0x00000048) Counter/Timer B2 Compare Registers */ - - struct { - __IOM uint32_t CMPR0B2 : 16; /*!< [15..0] Counter/Timer B2 Compare Register 0. */ - __IOM uint32_t CMPR1B2 : 16; /*!< [31..16] Counter/Timer B2 Compare Register 1. */ - } CMPRB2_b; - } ; - - union { - __IOM uint32_t CTRL2; /*!< (@ 0x0000004C) Counter/Timer Control */ - - struct { - __IOM uint32_t TMRA2EN : 1; /*!< [0..0] Counter/Timer A2 Enable bit. */ - __IOM uint32_t TMRA2CLK : 5; /*!< [5..1] Counter/Timer A2 Clock Select. */ - __IOM uint32_t TMRA2FN : 3; /*!< [8..6] Counter/Timer A2 Function Select. */ - __IOM uint32_t TMRA2IE0 : 1; /*!< [9..9] Counter/Timer A2 Interrupt Enable bit based on COMPR0. */ - __IOM uint32_t TMRA2IE1 : 1; /*!< [10..10] Counter/Timer A2 Interrupt Enable bit based on COMPR1. */ - __IOM uint32_t TMRA2CLR : 1; /*!< [11..11] Counter/Timer A2 Clear bit. */ - __IOM uint32_t TMRA2POL : 1; /*!< [12..12] Counter/Timer A2 output polarity. */ - __IM uint32_t : 3; - __IOM uint32_t TMRB2EN : 1; /*!< [16..16] Counter/Timer B2 Enable bit. */ - __IOM uint32_t TMRB2CLK : 5; /*!< [21..17] Counter/Timer B2 Clock Select. */ - __IOM uint32_t TMRB2FN : 3; /*!< [24..22] Counter/Timer B2 Function Select. */ - __IOM uint32_t TMRB2IE0 : 1; /*!< [25..25] Counter/Timer B2 Interrupt Enable bit for COMPR0. */ - __IOM uint32_t TMRB2IE1 : 1; /*!< [26..26] Counter/Timer B2 Interrupt Enable bit for COMPR1. */ - __IOM uint32_t TMRB2CLR : 1; /*!< [27..27] Counter/Timer B2 Clear bit. */ - __IOM uint32_t TMRB2POL : 1; /*!< [28..28] Counter/Timer B2 output polarity. */ - __IM uint32_t : 2; - __IOM uint32_t CTLINK2 : 1; /*!< [31..31] Counter/Timer A2/B2 Link bit. */ - } CTRL2_b; - } ; - __IM uint32_t RESERVED2; - - union { - __IOM uint32_t CMPRAUXA2; /*!< (@ 0x00000054) Counter/Timer A2 Compare Registers */ - - struct { - __IOM uint32_t CMPR2A2 : 16; /*!< [15..0] Counter/Timer A2 Compare Register 2. Holds the lower - limit for timer half A. */ - __IOM uint32_t CMPR3A2 : 16; /*!< [31..16] Counter/Timer A2 Compare Register 3. Holds the upper - limit for timer half A. */ - } CMPRAUXA2_b; - } ; - - union { - __IOM uint32_t CMPRAUXB2; /*!< (@ 0x00000058) Counter/Timer B2 Compare Registers */ - - struct { - __IOM uint32_t CMPR2B2 : 16; /*!< [15..0] Counter/Timer B2 Compare Register 2. Holds the lower - limit for timer half B. */ - __IOM uint32_t CMPR3B2 : 16; /*!< [31..16] Counter/Timer B2 Compare Register 3. Holds the upper - limit for timer half B. */ - } CMPRAUXB2_b; - } ; - - union { - __IOM uint32_t AUX2; /*!< (@ 0x0000005C) Counter/Timer Auxiliary */ - - struct { - __IOM uint32_t TMRA2LMT : 7; /*!< [6..0] Counter/Timer A2 Pattern Limit Count. */ - __IOM uint32_t TMRA2TRIG : 4; /*!< [10..7] Counter/Timer A2 Trigger Select. */ - __IOM uint32_t TMRA2NOSYNC : 1; /*!< [11..11] Source clock synchronization control. */ - __IOM uint32_t TMRA2TINV : 1; /*!< [12..12] Counter/Timer A2 Invert on trigger. */ - __IOM uint32_t TMRA2POL23 : 1; /*!< [13..13] Counter/Timer A2 Upper output polarity */ - __IOM uint32_t TMRA2EN23 : 1; /*!< [14..14] Counter/Timer A2 Upper compare enable. */ - __IM uint32_t : 1; - __IOM uint32_t TMRB2LMT : 6; /*!< [21..16] Counter/Timer B2 Pattern Limit Count. */ - __IM uint32_t : 1; - __IOM uint32_t TMRB2TRIG : 4; /*!< [26..23] Counter/Timer B2 Trigger Select. */ - __IOM uint32_t TMRB2NOSYNC : 1; /*!< [27..27] Source clock synchronization control. */ - __IOM uint32_t TMRB2TINV : 1; /*!< [28..28] Counter/Timer B2 Invert on trigger. */ - __IOM uint32_t TMRB2POL23 : 1; /*!< [29..29] Upper output polarity */ - __IOM uint32_t TMRB2EN23 : 1; /*!< [30..30] Counter/Timer B2 Upper compare enable. */ - } AUX2_b; - } ; - - union { - __IOM uint32_t TMR3; /*!< (@ 0x00000060) Counter/Timer Register */ - - struct { - __IOM uint32_t CTTMRA3 : 16; /*!< [15..0] Counter/Timer A3. */ - __IOM uint32_t CTTMRB3 : 16; /*!< [31..16] Counter/Timer B3. */ - } TMR3_b; - } ; - - union { - __IOM uint32_t CMPRA3; /*!< (@ 0x00000064) Counter/Timer A3 Compare Registers */ - - struct { - __IOM uint32_t CMPR0A3 : 16; /*!< [15..0] Counter/Timer A3 Compare Register 0. */ - __IOM uint32_t CMPR1A3 : 16; /*!< [31..16] Counter/Timer A3 Compare Register 1. */ - } CMPRA3_b; - } ; - - union { - __IOM uint32_t CMPRB3; /*!< (@ 0x00000068) Counter/Timer B3 Compare Registers */ - - struct { - __IOM uint32_t CMPR0B3 : 16; /*!< [15..0] Counter/Timer B3 Compare Register 0. */ - __IOM uint32_t CMPR1B3 : 16; /*!< [31..16] Counter/Timer B3 Compare Register 1. */ - } CMPRB3_b; - } ; - - union { - __IOM uint32_t CTRL3; /*!< (@ 0x0000006C) Counter/Timer Control */ - - struct { - __IOM uint32_t TMRA3EN : 1; /*!< [0..0] Counter/Timer A3 Enable bit. */ - __IOM uint32_t TMRA3CLK : 5; /*!< [5..1] Counter/Timer A3 Clock Select. */ - __IOM uint32_t TMRA3FN : 3; /*!< [8..6] Counter/Timer A3 Function Select. */ - __IOM uint32_t TMRA3IE0 : 1; /*!< [9..9] Counter/Timer A3 Interrupt Enable bit based on COMPR0. */ - __IOM uint32_t TMRA3IE1 : 1; /*!< [10..10] Counter/Timer A3 Interrupt Enable bit based on COMPR1. */ - __IOM uint32_t TMRA3CLR : 1; /*!< [11..11] Counter/Timer A3 Clear bit. */ - __IOM uint32_t TMRA3POL : 1; /*!< [12..12] Counter/Timer A3 output polarity. */ - __IM uint32_t : 2; - __IOM uint32_t ADCEN : 1; /*!< [15..15] Special Timer A3 enable for ADC function. */ - __IOM uint32_t TMRB3EN : 1; /*!< [16..16] Counter/Timer B3 Enable bit. */ - __IOM uint32_t TMRB3CLK : 5; /*!< [21..17] Counter/Timer B3 Clock Select. */ - __IOM uint32_t TMRB3FN : 3; /*!< [24..22] Counter/Timer B3 Function Select. */ - __IOM uint32_t TMRB3IE0 : 1; /*!< [25..25] Counter/Timer B3 Interrupt Enable bit for COMPR0. */ - __IOM uint32_t TMRB3IE1 : 1; /*!< [26..26] Counter/Timer B3 Interrupt Enable bit for COMPR1. */ - __IOM uint32_t TMRB3CLR : 1; /*!< [27..27] Counter/Timer B3 Clear bit. */ - __IOM uint32_t TMRB3POL : 1; /*!< [28..28] Counter/Timer B3 output polarity. */ - __IM uint32_t : 2; - __IOM uint32_t CTLINK3 : 1; /*!< [31..31] Counter/Timer A3/B3 Link bit. */ - } CTRL3_b; - } ; - __IM uint32_t RESERVED3; - - union { - __IOM uint32_t CMPRAUXA3; /*!< (@ 0x00000074) Counter/Timer A3 Compare Registers */ - - struct { - __IOM uint32_t CMPR2A3 : 16; /*!< [15..0] Counter/Timer A3 Compare Register 2. Holds the lower - limit for timer half A. */ - __IOM uint32_t CMPR3A3 : 16; /*!< [31..16] Counter/Timer A3 Compare Register 3. Holds the upper - limit for timer half A. */ - } CMPRAUXA3_b; - } ; - - union { - __IOM uint32_t CMPRAUXB3; /*!< (@ 0x00000078) Counter/Timer B3 Compare Registers */ - - struct { - __IOM uint32_t CMPR2B3 : 16; /*!< [15..0] Counter/Timer B3 Compare Register 2. Holds the lower - limit for timer half B. */ - __IOM uint32_t CMPR3B3 : 16; /*!< [31..16] Counter/Timer B3 Compare Register 3. Holds the upper - limit for timer half B. */ - } CMPRAUXB3_b; - } ; - - union { - __IOM uint32_t AUX3; /*!< (@ 0x0000007C) Counter/Timer Auxiliary */ - - struct { - __IOM uint32_t TMRA3LMT : 7; /*!< [6..0] Counter/Timer A3 Pattern Limit Count. */ - __IOM uint32_t TMRA3TRIG : 4; /*!< [10..7] Counter/Timer A3 Trigger Select. */ - __IOM uint32_t TMRA3NOSYNC : 1; /*!< [11..11] Source clock synchronization control. */ - __IOM uint32_t TMRA3TINV : 1; /*!< [12..12] Counter/Timer A3 Invert on trigger. */ - __IOM uint32_t TMRA3POL23 : 1; /*!< [13..13] Counter/Timer A3 Upper output polarity */ - __IOM uint32_t TMRA3EN23 : 1; /*!< [14..14] Counter/Timer A3 Upper compare enable. */ - __IM uint32_t : 1; - __IOM uint32_t TMRB3LMT : 6; /*!< [21..16] Counter/Timer B3 Pattern Limit Count. */ - __IM uint32_t : 1; - __IOM uint32_t TMRB3TRIG : 4; /*!< [26..23] Counter/Timer B3 Trigger Select. */ - __IOM uint32_t TMRB3NOSYNC : 1; /*!< [27..27] Source clock synchronization control. */ - __IOM uint32_t TMRB3TINV : 1; /*!< [28..28] Counter/Timer B3 Invert on trigger. */ - __IOM uint32_t TMRB3POL23 : 1; /*!< [29..29] Upper output polarity */ - __IOM uint32_t TMRB3EN23 : 1; /*!< [30..30] Counter/Timer B3 Upper compare enable. */ - } AUX3_b; - } ; - - union { - __IOM uint32_t TMR4; /*!< (@ 0x00000080) Counter/Timer Register */ - - struct { - __IOM uint32_t CTTMRA4 : 16; /*!< [15..0] Counter/Timer A4. */ - __IOM uint32_t CTTMRB4 : 16; /*!< [31..16] Counter/Timer B4. */ - } TMR4_b; - } ; - - union { - __IOM uint32_t CMPRA4; /*!< (@ 0x00000084) Counter/Timer A4 Compare Registers */ - - struct { - __IOM uint32_t CMPR0A4 : 16; /*!< [15..0] Counter/Timer A4 Compare Register 0. Holds the lower - limit for timer half A. */ - __IOM uint32_t CMPR1A4 : 16; /*!< [31..16] Counter/Timer A4 Compare Register 1. Holds the upper - limit for timer half A. */ - } CMPRA4_b; - } ; - - union { - __IOM uint32_t CMPRB4; /*!< (@ 0x00000088) Counter/Timer B4 Compare Registers */ - - struct { - __IOM uint32_t CMPR0B4 : 16; /*!< [15..0] Counter/Timer B4 Compare Register 0. Holds the lower - limit for timer half B. */ - __IOM uint32_t CMPR1B4 : 16; /*!< [31..16] Counter/Timer B4 Compare Register 1. Holds the upper - limit for timer half B. */ - } CMPRB4_b; - } ; - - union { - __IOM uint32_t CTRL4; /*!< (@ 0x0000008C) Counter/Timer Control */ - - struct { - __IOM uint32_t TMRA4EN : 1; /*!< [0..0] Counter/Timer A4 Enable bit. */ - __IOM uint32_t TMRA4CLK : 5; /*!< [5..1] Counter/Timer A4 Clock Select. */ - __IOM uint32_t TMRA4FN : 3; /*!< [8..6] Counter/Timer A4 Function Select. */ - __IOM uint32_t TMRA4IE0 : 1; /*!< [9..9] Counter/Timer A4 Interrupt Enable bit based on COMPR0. */ - __IOM uint32_t TMRA4IE1 : 1; /*!< [10..10] Counter/Timer A4 Interrupt Enable bit based on COMPR1. */ - __IOM uint32_t TMRA4CLR : 1; /*!< [11..11] Counter/Timer A4 Clear bit. */ - __IOM uint32_t TMRA4POL : 1; /*!< [12..12] Counter/Timer A4 output polarity. */ - __IM uint32_t : 3; - __IOM uint32_t TMRB4EN : 1; /*!< [16..16] Counter/Timer B4 Enable bit. */ - __IOM uint32_t TMRB4CLK : 5; /*!< [21..17] Counter/Timer B4 Clock Select. */ - __IOM uint32_t TMRB4FN : 3; /*!< [24..22] Counter/Timer B4 Function Select. */ - __IOM uint32_t TMRB4IE0 : 1; /*!< [25..25] Counter/Timer B4 Interrupt Enable bit for COMPR0. */ - __IOM uint32_t TMRB4IE1 : 1; /*!< [26..26] Counter/Timer B4 Interrupt Enable bit for COMPR1. */ - __IOM uint32_t TMRB4CLR : 1; /*!< [27..27] Counter/Timer B4 Clear bit. */ - __IOM uint32_t TMRB4POL : 1; /*!< [28..28] Counter/Timer B4 output polarity. */ - __IM uint32_t : 2; - __IOM uint32_t CTLINK4 : 1; /*!< [31..31] Counter/Timer A4/B4 Link bit. */ - } CTRL4_b; - } ; - __IM uint32_t RESERVED4; - - union { - __IOM uint32_t CMPRAUXA4; /*!< (@ 0x00000094) Counter/Timer A4 Compare Registers */ - - struct { - __IOM uint32_t CMPR2A4 : 16; /*!< [15..0] Counter/Timer A4 Compare Register 2. Holds the lower - limit for timer half A. */ - __IOM uint32_t CMPR3A4 : 16; /*!< [31..16] Counter/Timer A4 Compare Register 3. Holds the upper - limit for timer half A. */ - } CMPRAUXA4_b; - } ; - - union { - __IOM uint32_t CMPRAUXB4; /*!< (@ 0x00000098) Counter/Timer B4 Compare Registers */ - - struct { - __IOM uint32_t CMPR2B4 : 16; /*!< [15..0] Counter/Timer B4 Compare Register 2. Holds the lower - limit for timer half B. */ - __IOM uint32_t CMPR3B4 : 16; /*!< [31..16] Counter/Timer B4 Compare Register 3. Holds the upper - limit for timer half B. */ - } CMPRAUXB4_b; - } ; - - union { - __IOM uint32_t AUX4; /*!< (@ 0x0000009C) Counter/Timer Auxiliary */ - - struct { - __IOM uint32_t TMRA4LMT : 7; /*!< [6..0] Counter/Timer A4 Pattern Limit Count. */ - __IOM uint32_t TMRA4TRIG : 4; /*!< [10..7] Counter/Timer A4 Trigger Select. */ - __IOM uint32_t TMRA4NOSYNC : 1; /*!< [11..11] Source clock synchronization control. */ - __IOM uint32_t TMRA4TINV : 1; /*!< [12..12] Counter/Timer A4 Invert on trigger. */ - __IOM uint32_t TMRA4POL23 : 1; /*!< [13..13] Counter/Timer A4 Upper output polarity */ - __IOM uint32_t TMRA4EN23 : 1; /*!< [14..14] Counter/Timer A4 Upper compare enable. */ - __IM uint32_t : 1; - __IOM uint32_t TMRB4LMT : 6; /*!< [21..16] Counter/Timer B4 Pattern Limit Count. */ - __IM uint32_t : 1; - __IOM uint32_t TMRB4TRIG : 4; /*!< [26..23] Counter/Timer B4 Trigger Select. */ - __IOM uint32_t TMRB4NOSYNC : 1; /*!< [27..27] Source clock synchronization control. */ - __IOM uint32_t TMRB4TINV : 1; /*!< [28..28] Counter/Timer B4 Invert on trigger. */ - __IOM uint32_t TMRB4POL23 : 1; /*!< [29..29] Upper output polarity */ - __IOM uint32_t TMRB4EN23 : 1; /*!< [30..30] Counter/Timer B4 Upper compare enable. */ - } AUX4_b; - } ; - - union { - __IOM uint32_t TMR5; /*!< (@ 0x000000A0) Counter/Timer Register */ - - struct { - __IOM uint32_t CTTMRA5 : 16; /*!< [15..0] Counter/Timer A5. */ - __IOM uint32_t CTTMRB5 : 16; /*!< [31..16] Counter/Timer B5. */ - } TMR5_b; - } ; - - union { - __IOM uint32_t CMPRA5; /*!< (@ 0x000000A4) Counter/Timer A5 Compare Registers */ - - struct { - __IOM uint32_t CMPR0A5 : 16; /*!< [15..0] Counter/Timer A5 Compare Register 0. */ - __IOM uint32_t CMPR1A5 : 16; /*!< [31..16] Counter/Timer A5 Compare Register 1. */ - } CMPRA5_b; - } ; - - union { - __IOM uint32_t CMPRB5; /*!< (@ 0x000000A8) Counter/Timer B5 Compare Registers */ - - struct { - __IOM uint32_t CMPR0B5 : 16; /*!< [15..0] Counter/Timer B5 Compare Register 0. */ - __IOM uint32_t CMPR1B5 : 16; /*!< [31..16] Counter/Timer B5 Compare Register 1. */ - } CMPRB5_b; - } ; - - union { - __IOM uint32_t CTRL5; /*!< (@ 0x000000AC) Counter/Timer Control */ - - struct { - __IOM uint32_t TMRA5EN : 1; /*!< [0..0] Counter/Timer A5 Enable bit. */ - __IOM uint32_t TMRA5CLK : 5; /*!< [5..1] Counter/Timer A5 Clock Select. */ - __IOM uint32_t TMRA5FN : 3; /*!< [8..6] Counter/Timer A5 Function Select. */ - __IOM uint32_t TMRA5IE0 : 1; /*!< [9..9] Counter/Timer A5 Interrupt Enable bit based on COMPR0. */ - __IOM uint32_t TMRA5IE1 : 1; /*!< [10..10] Counter/Timer A5 Interrupt Enable bit based on COMPR1. */ - __IOM uint32_t TMRA5CLR : 1; /*!< [11..11] Counter/Timer A5 Clear bit. */ - __IOM uint32_t TMRA5POL : 1; /*!< [12..12] Counter/Timer A5 output polarity. */ - __IM uint32_t : 3; - __IOM uint32_t TMRB5EN : 1; /*!< [16..16] Counter/Timer B5 Enable bit. */ - __IOM uint32_t TMRB5CLK : 5; /*!< [21..17] Counter/Timer B5 Clock Select. */ - __IOM uint32_t TMRB5FN : 3; /*!< [24..22] Counter/Timer B5 Function Select. */ - __IOM uint32_t TMRB5IE0 : 1; /*!< [25..25] Counter/Timer B5 Interrupt Enable bit for COMPR0. */ - __IOM uint32_t TMRB5IE1 : 1; /*!< [26..26] Counter/Timer B5 Interrupt Enable bit for COMPR1. */ - __IOM uint32_t TMRB5CLR : 1; /*!< [27..27] Counter/Timer B5 Clear bit. */ - __IOM uint32_t TMRB5POL : 1; /*!< [28..28] Counter/Timer B5 output polarity. */ - __IM uint32_t : 2; - __IOM uint32_t CTLINK5 : 1; /*!< [31..31] Counter/Timer A5/B5 Link bit. */ - } CTRL5_b; - } ; - __IM uint32_t RESERVED5; - - union { - __IOM uint32_t CMPRAUXA5; /*!< (@ 0x000000B4) Counter/Timer A5 Compare Registers */ - - struct { - __IOM uint32_t CMPR2A5 : 16; /*!< [15..0] Counter/Timer A5 Compare Register 2. Holds the lower - limit for timer half A. */ - __IOM uint32_t CMPR3A5 : 16; /*!< [31..16] Counter/Timer A5 Compare Register 3. Holds the upper - limit for timer half A. */ - } CMPRAUXA5_b; - } ; - - union { - __IOM uint32_t CMPRAUXB5; /*!< (@ 0x000000B8) Counter/Timer B5 Compare Registers */ - - struct { - __IOM uint32_t CMPR2B5 : 16; /*!< [15..0] Counter/Timer B5 Compare Register 2. Holds the lower - limit for timer half B. */ - __IOM uint32_t CMPR3B5 : 16; /*!< [31..16] Counter/Timer B5 Compare Register 3. Holds the upper - limit for timer half B. */ - } CMPRAUXB5_b; - } ; - - union { - __IOM uint32_t AUX5; /*!< (@ 0x000000BC) Counter/Timer Auxiliary */ - - struct { - __IOM uint32_t TMRA5LMT : 7; /*!< [6..0] Counter/Timer A5 Pattern Limit Count. */ - __IOM uint32_t TMRA5TRIG : 4; /*!< [10..7] Counter/Timer A5 Trigger Select. */ - __IOM uint32_t TMRA5NOSYNC : 1; /*!< [11..11] Source clock synchronization control. */ - __IOM uint32_t TMRA5TINV : 1; /*!< [12..12] Counter/Timer A5 Invert on trigger. */ - __IOM uint32_t TMRA5POL23 : 1; /*!< [13..13] Counter/Timer A5 Upper output polarity */ - __IOM uint32_t TMRA5EN23 : 1; /*!< [14..14] Counter/Timer A5 Upper compare enable. */ - __IM uint32_t : 1; - __IOM uint32_t TMRB5LMT : 6; /*!< [21..16] Counter/Timer B5 Pattern Limit Count. */ - __IM uint32_t : 1; - __IOM uint32_t TMRB5TRIG : 4; /*!< [26..23] Counter/Timer B5 Trigger Select. */ - __IOM uint32_t TMRB5NOSYNC : 1; /*!< [27..27] Source clock synchronization control. */ - __IOM uint32_t TMRB5TINV : 1; /*!< [28..28] Counter/Timer B5 Invert on trigger. */ - __IOM uint32_t TMRB5POL23 : 1; /*!< [29..29] Upper output polarity */ - __IOM uint32_t TMRB5EN23 : 1; /*!< [30..30] Counter/Timer B5 Upper compare enable. */ - } AUX5_b; - } ; - - union { - __IOM uint32_t TMR6; /*!< (@ 0x000000C0) Counter/Timer Register */ - - struct { - __IOM uint32_t CTTMRA6 : 16; /*!< [15..0] Counter/Timer A6. */ - __IOM uint32_t CTTMRB6 : 16; /*!< [31..16] Counter/Timer B6. */ - } TMR6_b; - } ; - - union { - __IOM uint32_t CMPRA6; /*!< (@ 0x000000C4) Counter/Timer A6 Compare Registers */ - - struct { - __IOM uint32_t CMPR0A6 : 16; /*!< [15..0] Counter/Timer A6 Compare Register 0. */ - __IOM uint32_t CMPR1A6 : 16; /*!< [31..16] Counter/Timer A6 Compare Register 1. */ - } CMPRA6_b; - } ; - - union { - __IOM uint32_t CMPRB6; /*!< (@ 0x000000C8) Counter/Timer B6 Compare Registers */ - - struct { - __IOM uint32_t CMPR0B6 : 16; /*!< [15..0] Counter/Timer B6 Compare Register 0. */ - __IOM uint32_t CMPR1B6 : 16; /*!< [31..16] Counter/Timer B6 Compare Register 1. */ - } CMPRB6_b; - } ; - - union { - __IOM uint32_t CTRL6; /*!< (@ 0x000000CC) Counter/Timer Control */ - - struct { - __IOM uint32_t TMRA6EN : 1; /*!< [0..0] Counter/Timer A6 Enable bit. */ - __IOM uint32_t TMRA6CLK : 5; /*!< [5..1] Counter/Timer A6 Clock Select. */ - __IOM uint32_t TMRA6FN : 3; /*!< [8..6] Counter/Timer A6 Function Select. */ - __IOM uint32_t TMRA6IE0 : 1; /*!< [9..9] Counter/Timer A6 Interrupt Enable bit based on COMPR0. */ - __IOM uint32_t TMRA6IE1 : 1; /*!< [10..10] Counter/Timer A6 Interrupt Enable bit based on COMPR1. */ - __IOM uint32_t TMRA6CLR : 1; /*!< [11..11] Counter/Timer A6 Clear bit. */ - __IOM uint32_t TMRA6POL : 1; /*!< [12..12] Counter/Timer A6 output polarity. */ - __IM uint32_t : 3; - __IOM uint32_t TMRB6EN : 1; /*!< [16..16] Counter/Timer B6 Enable bit. */ - __IOM uint32_t TMRB6CLK : 5; /*!< [21..17] Counter/Timer B6 Clock Select. */ - __IOM uint32_t TMRB6FN : 3; /*!< [24..22] Counter/Timer B6 Function Select. */ - __IOM uint32_t TMRB6IE0 : 1; /*!< [25..25] Counter/Timer B6 Interrupt Enable bit for COMPR0. */ - __IOM uint32_t TMRB6IE1 : 1; /*!< [26..26] Counter/Timer B6 Interrupt Enable bit for COMPR1. */ - __IOM uint32_t TMRB6CLR : 1; /*!< [27..27] Counter/Timer B6 Clear bit. */ - __IOM uint32_t TMRB6POL : 1; /*!< [28..28] Counter/Timer B6 output polarity. */ - __IM uint32_t : 2; - __IOM uint32_t CTLINK6 : 1; /*!< [31..31] Counter/Timer A6/B6 Link bit. */ - } CTRL6_b; - } ; - __IM uint32_t RESERVED6; - - union { - __IOM uint32_t CMPRAUXA6; /*!< (@ 0x000000D4) Counter/Timer A6 Compare Registers */ - - struct { - __IOM uint32_t CMPR2A6 : 16; /*!< [15..0] Counter/Timer A6 Compare Register 2. Holds the lower - limit for timer half A. */ - __IOM uint32_t CMPR3A6 : 16; /*!< [31..16] Counter/Timer A6 Compare Register 3. Holds the upper - limit for timer half A. */ - } CMPRAUXA6_b; - } ; - - union { - __IOM uint32_t CMPRAUXB6; /*!< (@ 0x000000D8) Counter/Timer B6 Compare Registers */ - - struct { - __IOM uint32_t CMPR2B6 : 16; /*!< [15..0] Counter/Timer B6 Compare Register 2. Holds the lower - limit for timer half B. */ - __IOM uint32_t CMPR3B6 : 16; /*!< [31..16] Counter/Timer B6 Compare Register 3. Holds the upper - limit for timer half B. */ - } CMPRAUXB6_b; - } ; - - union { - __IOM uint32_t AUX6; /*!< (@ 0x000000DC) Counter/Timer Auxiliary */ - - struct { - __IOM uint32_t TMRA6LMT : 7; /*!< [6..0] Counter/Timer A6 Pattern Limit Count. */ - __IOM uint32_t TMRA6TRIG : 4; /*!< [10..7] Counter/Timer A6 Trigger Select. */ - __IOM uint32_t TMRA6NOSYNC : 1; /*!< [11..11] Source clock synchronization control. */ - __IOM uint32_t TMRA6TINV : 1; /*!< [12..12] Counter/Timer A6 Invert on trigger. */ - __IOM uint32_t TMRA6POL23 : 1; /*!< [13..13] Counter/Timer A6 Upper output polarity */ - __IOM uint32_t TMRA6EN23 : 1; /*!< [14..14] Counter/Timer A6 Upper compare enable. */ - __IM uint32_t : 1; - __IOM uint32_t TMRB6LMT : 6; /*!< [21..16] Counter/Timer B6 Pattern Limit Count. */ - __IM uint32_t : 1; - __IOM uint32_t TMRB6TRIG : 4; /*!< [26..23] Counter/Timer B6 Trigger Select. */ - __IOM uint32_t TMRB6NOSYNC : 1; /*!< [27..27] Source clock synchronization control. */ - __IOM uint32_t TMRB6TINV : 1; /*!< [28..28] Counter/Timer B6 Invert on trigger. */ - __IOM uint32_t TMRB6POL23 : 1; /*!< [29..29] Upper output polarity */ - __IOM uint32_t TMRB6EN23 : 1; /*!< [30..30] Counter/Timer B6 Upper compare enable. */ - } AUX6_b; - } ; - - union { - __IOM uint32_t TMR7; /*!< (@ 0x000000E0) Counter/Timer Register */ - - struct { - __IOM uint32_t CTTMRA7 : 16; /*!< [15..0] Counter/Timer A7. */ - __IOM uint32_t CTTMRB7 : 16; /*!< [31..16] Counter/Timer B7. */ - } TMR7_b; - } ; - - union { - __IOM uint32_t CMPRA7; /*!< (@ 0x000000E4) Counter/Timer A7 Compare Registers */ - - struct { - __IOM uint32_t CMPR0A7 : 16; /*!< [15..0] Counter/Timer A7 Compare Register 0. */ - __IOM uint32_t CMPR1A7 : 16; /*!< [31..16] Counter/Timer A7 Compare Register 1. */ - } CMPRA7_b; - } ; - - union { - __IOM uint32_t CMPRB7; /*!< (@ 0x000000E8) Counter/Timer B7 Compare Registers */ - - struct { - __IOM uint32_t CMPR0B7 : 16; /*!< [15..0] Counter/Timer B3 Compare Register 0. */ - __IOM uint32_t CMPR1B7 : 16; /*!< [31..16] Counter/Timer B3 Compare Register 1. */ - } CMPRB7_b; - } ; - - union { - __IOM uint32_t CTRL7; /*!< (@ 0x000000EC) Counter/Timer Control */ - - struct { - __IOM uint32_t TMRA7EN : 1; /*!< [0..0] Counter/Timer A7 Enable bit. */ - __IOM uint32_t TMRA7CLK : 5; /*!< [5..1] Counter/Timer A7 Clock Select. */ - __IOM uint32_t TMRA7FN : 3; /*!< [8..6] Counter/Timer A7 Function Select. */ - __IOM uint32_t TMRA7IE0 : 1; /*!< [9..9] Counter/Timer A7 Interrupt Enable bit based on COMPR0. */ - __IOM uint32_t TMRA7IE1 : 1; /*!< [10..10] Counter/Timer A7 Interrupt Enable bit based on COMPR1. */ - __IOM uint32_t TMRA7CLR : 1; /*!< [11..11] Counter/Timer A7 Clear bit. */ - __IOM uint32_t TMRA7POL : 1; /*!< [12..12] Counter/Timer A7 output polarity. */ - __IM uint32_t : 3; - __IOM uint32_t TMRB7EN : 1; /*!< [16..16] Counter/Timer B7 Enable bit. */ - __IOM uint32_t TMRB7CLK : 5; /*!< [21..17] Counter/Timer B7 Clock Select. */ - __IOM uint32_t TMRB7FN : 3; /*!< [24..22] Counter/Timer B7 Function Select. */ - __IOM uint32_t TMRB7IE0 : 1; /*!< [25..25] Counter/Timer B7 Interrupt Enable bit for COMPR0. */ - __IOM uint32_t TMRB7IE1 : 1; /*!< [26..26] Counter/Timer B7 Interrupt Enable bit for COMPR1. */ - __IOM uint32_t TMRB7CLR : 1; /*!< [27..27] Counter/Timer B7 Clear bit. */ - __IOM uint32_t TMRB7POL : 1; /*!< [28..28] Counter/Timer B7 output polarity. */ - __IM uint32_t : 2; - __IOM uint32_t CTLINK7 : 1; /*!< [31..31] Counter/Timer A7/B7 Link bit. */ - } CTRL7_b; - } ; - __IM uint32_t RESERVED7; - - union { - __IOM uint32_t CMPRAUXA7; /*!< (@ 0x000000F4) Counter/Timer A7 Compare Registers */ - - struct { - __IOM uint32_t CMPR2A7 : 16; /*!< [15..0] Counter/Timer A7 Compare Register 2. Holds the lower - limit for timer half A. */ - __IOM uint32_t CMPR3A7 : 16; /*!< [31..16] Counter/Timer A7 Compare Register 3. Holds the upper - limit for timer half A. */ - } CMPRAUXA7_b; - } ; - - union { - __IOM uint32_t CMPRAUXB7; /*!< (@ 0x000000F8) Counter/Timer B7 Compare Registers */ - - struct { - __IOM uint32_t CMPR2B7 : 16; /*!< [15..0] Counter/Timer B7 Compare Register 2. Holds the lower - limit for timer half B. */ - __IOM uint32_t CMPR3B7 : 16; /*!< [31..16] Counter/Timer B7 Compare Register 3. Holds the upper - limit for timer half B. */ - } CMPRAUXB7_b; - } ; - - union { - __IOM uint32_t AUX7; /*!< (@ 0x000000FC) Counter/Timer Auxiliary */ - - struct { - __IOM uint32_t TMRA7LMT : 7; /*!< [6..0] Counter/Timer A7 Pattern Limit Count. */ - __IOM uint32_t TMRA7TRIG : 4; /*!< [10..7] Counter/Timer A7 Trigger Select. */ - __IOM uint32_t TMRA7NOSYNC : 1; /*!< [11..11] Source clock synchronization control. */ - __IOM uint32_t TMRA7TINV : 1; /*!< [12..12] Counter/Timer A7 Invert on trigger. */ - __IOM uint32_t TMRA7POL23 : 1; /*!< [13..13] Counter/Timer A7 Upper output polarity */ - __IOM uint32_t TMRA7EN23 : 1; /*!< [14..14] Counter/Timer A7 Upper compare enable. */ - __IM uint32_t : 1; - __IOM uint32_t TMRB7LMT : 6; /*!< [21..16] Counter/Timer B7 Pattern Limit Count. */ - __IM uint32_t : 1; - __IOM uint32_t TMRB7TRIG : 4; /*!< [26..23] Counter/Timer B7 Trigger Select. */ - __IOM uint32_t TMRB7NOSYNC : 1; /*!< [27..27] Source clock synchronization control. */ - __IOM uint32_t TMRB7TINV : 1; /*!< [28..28] Counter/Timer B7 Invert on trigger. */ - __IOM uint32_t TMRB7POL23 : 1; /*!< [29..29] Upper output polarity */ - __IOM uint32_t TMRB7EN23 : 1; /*!< [30..30] Counter/Timer B7 Upper compare enable. */ - } AUX7_b; - } ; - - union { - __IOM uint32_t GLOBEN; /*!< (@ 0x00000100) Counter/Timer Global Enable */ - - struct { - __IOM uint32_t ENA0 : 1; /*!< [0..0] Alternate enable for A0 */ - __IOM uint32_t ENB0 : 1; /*!< [1..1] Alternate enable for B0 */ - __IOM uint32_t ENA1 : 1; /*!< [2..2] Alternate enable for A1 */ - __IOM uint32_t ENB1 : 1; /*!< [3..3] Alternate enable for B1 */ - __IOM uint32_t ENA2 : 1; /*!< [4..4] Alternate enable for A2 */ - __IOM uint32_t ENB2 : 1; /*!< [5..5] Alternate enable for B2 */ - __IOM uint32_t ENA3 : 1; /*!< [6..6] Alternate enable for A3 */ - __IOM uint32_t ENB3 : 1; /*!< [7..7] Alternate enable for B3. */ - __IOM uint32_t ENA4 : 1; /*!< [8..8] Alternate enable for A4 */ - __IOM uint32_t ENB4 : 1; /*!< [9..9] Alternate enable for B4 */ - __IOM uint32_t ENA5 : 1; /*!< [10..10] Alternate enable for A5 */ - __IOM uint32_t ENB5 : 1; /*!< [11..11] Alternate enable for B5 */ - __IOM uint32_t ENA6 : 1; /*!< [12..12] Alternate enable for A6 */ - __IOM uint32_t ENB6 : 1; /*!< [13..13] Alternate enable for B6 */ - __IOM uint32_t ENA7 : 1; /*!< [14..14] Alternate enable for A7 */ - __IOM uint32_t ENB7 : 1; /*!< [15..15] Alternate enable for B7. */ - } GLOBEN_b; - } ; - - union { - __IOM uint32_t OUTCFG0; /*!< (@ 0x00000104) Counter/Timer Output Config 0 */ - - struct { - __IOM uint32_t CFG0 : 3; /*!< [2..0] Pad output 0 configuration */ - __IOM uint32_t CFG1 : 3; /*!< [5..3] Pad output 1 configuration */ - __IOM uint32_t CFG2 : 3; /*!< [8..6] Pad output 2 configuration */ - __IOM uint32_t CFG3 : 3; /*!< [11..9] Pad output 3 configuration */ - __IOM uint32_t CFG4 : 3; /*!< [14..12] Pad output 4 configuration */ - __IM uint32_t : 1; - __IOM uint32_t CFG5 : 3; /*!< [18..16] Pad output 5 configuration */ - __IOM uint32_t CFG6 : 3; /*!< [21..19] Pad output 6 configuration */ - __IOM uint32_t CFG7 : 3; /*!< [24..22] Pad output 7 configuration */ - __IOM uint32_t CFG8 : 3; /*!< [27..25] Pad output 8 configuration */ - __IOM uint32_t CFG9 : 3; /*!< [30..28] Pad output 9 configuration */ - } OUTCFG0_b; - } ; - - union { - __IOM uint32_t OUTCFG1; /*!< (@ 0x00000108) Counter/Timer Output Config 1 */ - - struct { - __IOM uint32_t CFG10 : 3; /*!< [2..0] Pad output 10 configuration */ - __IOM uint32_t CFG11 : 3; /*!< [5..3] Pad output 11 configuration */ - __IOM uint32_t CFG12 : 3; /*!< [8..6] Pad output 12 configuration */ - __IOM uint32_t CFG13 : 3; /*!< [11..9] Pad output 13 configuration */ - __IOM uint32_t CFG14 : 3; /*!< [14..12] Pad output 14 configuration */ - __IM uint32_t : 1; - __IOM uint32_t CFG15 : 3; /*!< [18..16] Pad output 15 configuration */ - __IOM uint32_t CFG16 : 3; /*!< [21..19] Pad output 16 configuration */ - __IOM uint32_t CFG17 : 3; /*!< [24..22] Pad output 17 configuration */ - __IOM uint32_t CFG18 : 3; /*!< [27..25] Pad output 18 configuration */ - __IOM uint32_t CFG19 : 3; /*!< [30..28] Pad output 19 configuration */ - } OUTCFG1_b; - } ; - - union { - __IOM uint32_t OUTCFG2; /*!< (@ 0x0000010C) Counter/Timer Output Config 2 */ - - struct { - __IOM uint32_t CFG20 : 3; /*!< [2..0] Pad output 20 configuration */ - __IOM uint32_t CFG21 : 3; /*!< [5..3] Pad output 21 configuration */ - __IOM uint32_t CFG22 : 3; /*!< [8..6] Pad output 22 configuration */ - __IOM uint32_t CFG23 : 3; /*!< [11..9] Pad output 23 configuration */ - __IOM uint32_t CFG24 : 3; /*!< [14..12] Pad output 24 configuration */ - __IM uint32_t : 1; - __IOM uint32_t CFG25 : 3; /*!< [18..16] Pad output 25 configuration */ - __IOM uint32_t CFG26 : 3; /*!< [21..19] Pad output 26 configuration */ - __IOM uint32_t CFG27 : 3; /*!< [24..22] Pad output 27 configuration */ - __IOM uint32_t CFG28 : 3; /*!< [27..25] Pad output 28 configuration */ - __IOM uint32_t CFG29 : 3; /*!< [30..28] Pad output 29 configuration */ - } OUTCFG2_b; - } ; - __IM uint32_t RESERVED8; - - union { - __IOM uint32_t OUTCFG3; /*!< (@ 0x00000114) Counter/Timer Output Config 3 */ - - struct { - __IOM uint32_t CFG30 : 3; /*!< [2..0] Pad output 30 configuration */ - __IOM uint32_t CFG31 : 3; /*!< [5..3] Pad output 31 configuration */ - } OUTCFG3_b; - } ; - - union { - __IOM uint32_t INCFG; /*!< (@ 0x00000118) Counter/Timer Input Config */ - - struct { - __IOM uint32_t CFGA0 : 1; /*!< [0..0] CTIMER A0 input configuration */ - __IOM uint32_t CFGB0 : 1; /*!< [1..1] CTIMER B0 input configuration */ - __IOM uint32_t CFGA1 : 1; /*!< [2..2] CTIMER A1 input configuration */ - __IOM uint32_t CFGB1 : 1; /*!< [3..3] CTIMER B1 input configuration */ - __IOM uint32_t CFGA2 : 1; /*!< [4..4] CTIMER A2 input configuration */ - __IOM uint32_t CFGB2 : 1; /*!< [5..5] CTIMER B2 input configuration */ - __IOM uint32_t CFGA3 : 1; /*!< [6..6] CTIMER A3 input configuration */ - __IOM uint32_t CFGB3 : 1; /*!< [7..7] CTIMER B3 input configuration */ - __IOM uint32_t CFGA4 : 1; /*!< [8..8] CTIMER A4 input configuration */ - __IOM uint32_t CFGB4 : 1; /*!< [9..9] CTIMER B4 input configuration */ - __IOM uint32_t CFGA5 : 1; /*!< [10..10] CTIMER A5 input configuration */ - __IOM uint32_t CFGB5 : 1; /*!< [11..11] CTIMER B5 input configuration */ - __IOM uint32_t CFGA6 : 1; /*!< [12..12] CTIMER A6 input configuration */ - __IOM uint32_t CFGB6 : 1; /*!< [13..13] CTIMER B6 input configuration */ - __IOM uint32_t CFGA7 : 1; /*!< [14..14] CTIMER A7 input configuration */ - __IOM uint32_t CFGB7 : 1; /*!< [15..15] CTIMER B7 input configuration */ - } INCFG_b; - } ; - __IM uint32_t RESERVED9[9]; - - union { - __IOM uint32_t STCFG; /*!< (@ 0x00000140) Configuration Register */ - - struct { - __IOM uint32_t CLKSEL : 4; /*!< [3..0] Selects an appropriate clock source and divider to use - for the System Timer clock. */ - __IM uint32_t : 4; - __IOM uint32_t COMPARE_A_EN : 1; /*!< [8..8] Selects whether compare is enabled for the corresponding - SCMPR register. If compare is enabled, the interrupt status - is set once the comparision is met. */ - __IOM uint32_t COMPARE_B_EN : 1; /*!< [9..9] Selects whether compare is enabled for the corresponding - SCMPR register. If compare is enabled, the interrupt status - is set once the comparision is met. */ - __IOM uint32_t COMPARE_C_EN : 1; /*!< [10..10] Selects whether compare is enabled for the corresponding - SCMPR register. If compare is enabled, the interrupt status - is set once the comparision is met. */ - __IOM uint32_t COMPARE_D_EN : 1; /*!< [11..11] Selects whether compare is enabled for the corresponding - SCMPR register. If compare is enabled, the interrupt status - is set once the comparision is met. */ - __IOM uint32_t COMPARE_E_EN : 1; /*!< [12..12] Selects whether compare is enabled for the corresponding - SCMPR register. If compare is enabled, the interrupt status - is set once the comparision is met. */ - __IOM uint32_t COMPARE_F_EN : 1; /*!< [13..13] Selects whether compare is enabled for the corresponding - SCMPR register. If compare is enabled, the interrupt status - is set once the comparision is met. */ - __IOM uint32_t COMPARE_G_EN : 1; /*!< [14..14] Selects whether compare is enabled for the corresponding - SCMPR register. If compare is enabled, the interrupt status - is set once the comparision is met. */ - __IOM uint32_t COMPARE_H_EN : 1; /*!< [15..15] Selects whether compare is enabled for the corresponding - SCMPR register. If compare is enabled, the interrupt status - is set once the comparision is met. */ - __IM uint32_t : 14; - __IOM uint32_t CLEAR : 1; /*!< [30..30] Set this bit to one to clear the System Timer register. - If this bit is set to '1', the system timer register will - stay cleared. It needs to be set to '0' for the system - timer to start running. */ - __IOM uint32_t FREEZE : 1; /*!< [31..31] Set this bit to one to freeze the clock input to the - COUNTER register. Once frozen, the value can be safely - written from the MCU. Unfreeze to resume. */ - } STCFG_b; - } ; - - union { - __IOM uint32_t STTMR; /*!< (@ 0x00000144) System Timer Count Register (Real Time Counter) */ - - struct { - __IOM uint32_t STTMR : 32; /*!< [31..0] Value of the 32-bit counter as it ticks over. */ - } STTMR_b; - } ; - - union { - __IOM uint32_t CAPTURECONTROL; /*!< (@ 0x00000148) Capture Control Register */ - - struct { - __IOM uint32_t CAPTURE0 : 1; /*!< [0..0] Selects whether capture is enabled for the specified - capture register. */ - __IOM uint32_t CAPTURE1 : 1; /*!< [1..1] Selects whether capture is enabled for the specified - capture register. */ - __IOM uint32_t CAPTURE2 : 1; /*!< [2..2] Selects whether capture is enabled for the specified - capture register. */ - __IOM uint32_t CAPTURE3 : 1; /*!< [3..3] Selects whether capture is enabled for the specified - capture register. */ - } CAPTURECONTROL_b; - } ; - __IM uint32_t RESERVED10; - - union { - __IOM uint32_t SCMPR0; /*!< (@ 0x00000150) Compare Register A */ - - struct { - __IOM uint32_t SCMPR0 : 32; /*!< [31..0] Compare this value to the value in the COUNTER register - according to the match criterion, as selected in the COMPARE_A_EN - bit in the REG_CTIMER_STCGF register. */ - } SCMPR0_b; - } ; - - union { - __IOM uint32_t SCMPR1; /*!< (@ 0x00000154) Compare Register B */ - - struct { - __IOM uint32_t SCMPR1 : 32; /*!< [31..0] Compare this value to the value in the COUNTER register - according to the match criterion, as selected in the COMPARE_B_EN - bit in the REG_CTIMER_STCGF register. */ - } SCMPR1_b; - } ; - - union { - __IOM uint32_t SCMPR2; /*!< (@ 0x00000158) Compare Register C */ - - struct { - __IOM uint32_t SCMPR2 : 32; /*!< [31..0] Compare this value to the value in the COUNTER register - according to the match criterion, as selected in the COMPARE_C_EN - bit in the REG_CTIMER_STCGF register. */ - } SCMPR2_b; - } ; - - union { - __IOM uint32_t SCMPR3; /*!< (@ 0x0000015C) Compare Register D */ - - struct { - __IOM uint32_t SCMPR3 : 32; /*!< [31..0] Compare this value to the value in the COUNTER register - according to the match criterion, as selected in the COMPARE_D_EN - bit in the REG_CTIMER_STCGF register. */ - } SCMPR3_b; - } ; - - union { - __IOM uint32_t SCMPR4; /*!< (@ 0x00000160) Compare Register E */ - - struct { - __IOM uint32_t SCMPR4 : 32; /*!< [31..0] Compare this value to the value in the COUNTER register - according to the match criterion, as selected in the COMPARE_E_EN - bit in the REG_CTIMER_STCGF register. */ - } SCMPR4_b; - } ; - - union { - __IOM uint32_t SCMPR5; /*!< (@ 0x00000164) Compare Register F */ - - struct { - __IOM uint32_t SCMPR5 : 32; /*!< [31..0] Compare this value to the value in the COUNTER register - according to the match criterion, as selected in the COMPARE_F_EN - bit in the REG_CTIMER_STCGF register. */ - } SCMPR5_b; - } ; - - union { - __IOM uint32_t SCMPR6; /*!< (@ 0x00000168) Compare Register G */ - - struct { - __IOM uint32_t SCMPR6 : 32; /*!< [31..0] Compare this value to the value in the COUNTER register - according to the match criterion, as selected in the COMPARE_G_EN - bit in the REG_CTIMER_STCGF register. */ - } SCMPR6_b; - } ; - - union { - __IOM uint32_t SCMPR7; /*!< (@ 0x0000016C) Compare Register H */ - - struct { - __IOM uint32_t SCMPR7 : 32; /*!< [31..0] Compare this value to the value in the COUNTER register - according to the match criterion, as selected in the COMPARE_H_EN - bit in the REG_CTIMER_STCGF register. */ - } SCMPR7_b; - } ; - __IM uint32_t RESERVED11[28]; - - union { - __IOM uint32_t SCAPT0; /*!< (@ 0x000001E0) Capture Register A */ - - struct { - __IOM uint32_t SCAPT0 : 32; /*!< [31..0] Whenever the event is detected, the value in the COUNTER - is copied into this register and the corresponding interrupt - status bit is set. */ - } SCAPT0_b; - } ; - - union { - __IOM uint32_t SCAPT1; /*!< (@ 0x000001E4) Capture Register B */ - - struct { - __IOM uint32_t SCAPT1 : 32; /*!< [31..0] Whenever the event is detected, the value in the COUNTER - is copied into this register and the corresponding interrupt - status bit is set. */ - } SCAPT1_b; - } ; - - union { - __IOM uint32_t SCAPT2; /*!< (@ 0x000001E8) Capture Register C */ - - struct { - __IOM uint32_t SCAPT2 : 32; /*!< [31..0] Whenever the event is detected, the value in the COUNTER - is copied into this register and the corresponding interrupt - status bit is set. */ - } SCAPT2_b; - } ; - - union { - __IOM uint32_t SCAPT3; /*!< (@ 0x000001EC) Capture Register D */ - - struct { - __IOM uint32_t SCAPT3 : 32; /*!< [31..0] Whenever the event is detected, the value in the COUNTER - is copied into this register and the corresponding interrupt - status bit is set. */ - } SCAPT3_b; - } ; - - union { - __IOM uint32_t SNVR0; /*!< (@ 0x000001F0) System Timer NVRAM_A Register */ - - struct { - __IOM uint32_t SNVR0 : 32; /*!< [31..0] Value of the 32-bit counter as it ticks over. */ - } SNVR0_b; - } ; - - union { - __IOM uint32_t SNVR1; /*!< (@ 0x000001F4) System Timer NVRAM_B Register */ - - struct { - __IOM uint32_t SNVR1 : 32; /*!< [31..0] Value of the 32-bit counter as it ticks over. */ - } SNVR1_b; - } ; - - union { - __IOM uint32_t SNVR2; /*!< (@ 0x000001F8) System Timer NVRAM_C Register */ - - struct { - __IOM uint32_t SNVR2 : 32; /*!< [31..0] Value of the 32-bit counter as it ticks over. */ - } SNVR2_b; - } ; - - union { - __IOM uint32_t SNVR3; /*!< (@ 0x000001FC) System Timer NVRAM_D Register */ - - struct { - __IOM uint32_t SNVR3 : 32; /*!< [31..0] Value of the 32-bit counter as it ticks over. */ - } SNVR3_b; - } ; - - union { - __IOM uint32_t INTEN; /*!< (@ 0x00000200) Counter/Timer Interrupts: Enable */ - - struct { - __IOM uint32_t CTMRA0C0INT : 1; /*!< [0..0] Counter/Timer A0 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB0C0INT : 1; /*!< [1..1] Counter/Timer B0 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA1C0INT : 1; /*!< [2..2] Counter/Timer A1 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB1C0INT : 1; /*!< [3..3] Counter/Timer B1 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA2C0INT : 1; /*!< [4..4] Counter/Timer A2 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB2C0INT : 1; /*!< [5..5] Counter/Timer B2 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA3C0INT : 1; /*!< [6..6] Counter/Timer A3 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB3C0INT : 1; /*!< [7..7] Counter/Timer B3 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA4C0INT : 1; /*!< [8..8] Counter/Timer A4 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB4C0INT : 1; /*!< [9..9] Counter/Timer B4 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA5C0INT : 1; /*!< [10..10] Counter/Timer A5 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB5C0INT : 1; /*!< [11..11] Counter/Timer B5 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA6C0INT : 1; /*!< [12..12] Counter/Timer A6 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB6C0INT : 1; /*!< [13..13] Counter/Timer B6 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA7C0INT : 1; /*!< [14..14] Counter/Timer A7 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB7C0INT : 1; /*!< [15..15] Counter/Timer B7 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA0C1INT : 1; /*!< [16..16] Counter/Timer A0 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB0C1INT : 1; /*!< [17..17] Counter/Timer B0 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA1C1INT : 1; /*!< [18..18] Counter/Timer A1 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB1C1INT : 1; /*!< [19..19] Counter/Timer B1 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA2C1INT : 1; /*!< [20..20] Counter/Timer A2 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB2C1INT : 1; /*!< [21..21] Counter/Timer B2 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA3C1INT : 1; /*!< [22..22] Counter/Timer A3 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB3C1INT : 1; /*!< [23..23] Counter/Timer B3 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA4C1INT : 1; /*!< [24..24] Counter/Timer A4 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB4C1INT : 1; /*!< [25..25] Counter/Timer B4 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA5C1INT : 1; /*!< [26..26] Counter/Timer A5 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB5C1INT : 1; /*!< [27..27] Counter/Timer B5 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA6C1INT : 1; /*!< [28..28] Counter/Timer A6 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB6C1INT : 1; /*!< [29..29] Counter/Timer B6 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA7C1INT : 1; /*!< [30..30] Counter/Timer A7 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB7C1INT : 1; /*!< [31..31] Counter/Timer B7 interrupt based on COMPR1. */ - } INTEN_b; - } ; - - union { - __IOM uint32_t INTSTAT; /*!< (@ 0x00000204) Counter/Timer Interrupts: Status */ - - struct { - __IOM uint32_t CTMRA0C0INT : 1; /*!< [0..0] Counter/Timer A0 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB0C0INT : 1; /*!< [1..1] Counter/Timer B0 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA1C0INT : 1; /*!< [2..2] Counter/Timer A1 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB1C0INT : 1; /*!< [3..3] Counter/Timer B1 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA2C0INT : 1; /*!< [4..4] Counter/Timer A2 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB2C0INT : 1; /*!< [5..5] Counter/Timer B2 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA3C0INT : 1; /*!< [6..6] Counter/Timer A3 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB3C0INT : 1; /*!< [7..7] Counter/Timer B3 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA4C0INT : 1; /*!< [8..8] Counter/Timer A4 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB4C0INT : 1; /*!< [9..9] Counter/Timer B4 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA5C0INT : 1; /*!< [10..10] Counter/Timer A5 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB5C0INT : 1; /*!< [11..11] Counter/Timer B5 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA6C0INT : 1; /*!< [12..12] Counter/Timer A6 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB6C0INT : 1; /*!< [13..13] Counter/Timer B6 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA7C0INT : 1; /*!< [14..14] Counter/Timer A7 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB7C0INT : 1; /*!< [15..15] Counter/Timer B7 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA0C1INT : 1; /*!< [16..16] Counter/Timer A0 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB0C1INT : 1; /*!< [17..17] Counter/Timer B0 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA1C1INT : 1; /*!< [18..18] Counter/Timer A1 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB1C1INT : 1; /*!< [19..19] Counter/Timer B1 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA2C1INT : 1; /*!< [20..20] Counter/Timer A2 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB2C1INT : 1; /*!< [21..21] Counter/Timer B2 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA3C1INT : 1; /*!< [22..22] Counter/Timer A3 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB3C1INT : 1; /*!< [23..23] Counter/Timer B3 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA4C1INT : 1; /*!< [24..24] Counter/Timer A4 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB4C1INT : 1; /*!< [25..25] Counter/Timer B4 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA5C1INT : 1; /*!< [26..26] Counter/Timer A5 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB5C1INT : 1; /*!< [27..27] Counter/Timer B5 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA6C1INT : 1; /*!< [28..28] Counter/Timer A6 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB6C1INT : 1; /*!< [29..29] Counter/Timer B6 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA7C1INT : 1; /*!< [30..30] Counter/Timer A7 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB7C1INT : 1; /*!< [31..31] Counter/Timer B7 interrupt based on COMPR1. */ - } INTSTAT_b; - } ; - - union { - __IOM uint32_t INTCLR; /*!< (@ 0x00000208) Counter/Timer Interrupts: Clear */ - - struct { - __IOM uint32_t CTMRA0C0INT : 1; /*!< [0..0] Counter/Timer A0 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB0C0INT : 1; /*!< [1..1] Counter/Timer B0 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA1C0INT : 1; /*!< [2..2] Counter/Timer A1 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB1C0INT : 1; /*!< [3..3] Counter/Timer B1 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA2C0INT : 1; /*!< [4..4] Counter/Timer A2 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB2C0INT : 1; /*!< [5..5] Counter/Timer B2 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA3C0INT : 1; /*!< [6..6] Counter/Timer A3 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB3C0INT : 1; /*!< [7..7] Counter/Timer B3 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA4C0INT : 1; /*!< [8..8] Counter/Timer A4 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB4C0INT : 1; /*!< [9..9] Counter/Timer B4 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA5C0INT : 1; /*!< [10..10] Counter/Timer A5 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB5C0INT : 1; /*!< [11..11] Counter/Timer B5 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA6C0INT : 1; /*!< [12..12] Counter/Timer A6 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB6C0INT : 1; /*!< [13..13] Counter/Timer B6 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA7C0INT : 1; /*!< [14..14] Counter/Timer A7 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB7C0INT : 1; /*!< [15..15] Counter/Timer B7 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA0C1INT : 1; /*!< [16..16] Counter/Timer A0 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB0C1INT : 1; /*!< [17..17] Counter/Timer B0 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA1C1INT : 1; /*!< [18..18] Counter/Timer A1 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB1C1INT : 1; /*!< [19..19] Counter/Timer B1 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA2C1INT : 1; /*!< [20..20] Counter/Timer A2 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB2C1INT : 1; /*!< [21..21] Counter/Timer B2 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA3C1INT : 1; /*!< [22..22] Counter/Timer A3 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB3C1INT : 1; /*!< [23..23] Counter/Timer B3 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA4C1INT : 1; /*!< [24..24] Counter/Timer A4 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB4C1INT : 1; /*!< [25..25] Counter/Timer B4 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA5C1INT : 1; /*!< [26..26] Counter/Timer A5 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB5C1INT : 1; /*!< [27..27] Counter/Timer B5 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA6C1INT : 1; /*!< [28..28] Counter/Timer A6 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB6C1INT : 1; /*!< [29..29] Counter/Timer B6 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA7C1INT : 1; /*!< [30..30] Counter/Timer A7 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB7C1INT : 1; /*!< [31..31] Counter/Timer B7 interrupt based on COMPR1. */ - } INTCLR_b; - } ; - - union { - __IOM uint32_t INTSET; /*!< (@ 0x0000020C) Counter/Timer Interrupts: Set */ - - struct { - __IOM uint32_t CTMRA0C0INT : 1; /*!< [0..0] Counter/Timer A0 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB0C0INT : 1; /*!< [1..1] Counter/Timer B0 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA1C0INT : 1; /*!< [2..2] Counter/Timer A1 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB1C0INT : 1; /*!< [3..3] Counter/Timer B1 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA2C0INT : 1; /*!< [4..4] Counter/Timer A2 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB2C0INT : 1; /*!< [5..5] Counter/Timer B2 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA3C0INT : 1; /*!< [6..6] Counter/Timer A3 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB3C0INT : 1; /*!< [7..7] Counter/Timer B3 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA4C0INT : 1; /*!< [8..8] Counter/Timer A4 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB4C0INT : 1; /*!< [9..9] Counter/Timer B4 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA5C0INT : 1; /*!< [10..10] Counter/Timer A5 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB5C0INT : 1; /*!< [11..11] Counter/Timer B5 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA6C0INT : 1; /*!< [12..12] Counter/Timer A6 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB6C0INT : 1; /*!< [13..13] Counter/Timer B6 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA7C0INT : 1; /*!< [14..14] Counter/Timer A7 interrupt based on COMPR0. */ - __IOM uint32_t CTMRB7C0INT : 1; /*!< [15..15] Counter/Timer B7 interrupt based on COMPR0. */ - __IOM uint32_t CTMRA0C1INT : 1; /*!< [16..16] Counter/Timer A0 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB0C1INT : 1; /*!< [17..17] Counter/Timer B0 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA1C1INT : 1; /*!< [18..18] Counter/Timer A1 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB1C1INT : 1; /*!< [19..19] Counter/Timer B1 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA2C1INT : 1; /*!< [20..20] Counter/Timer A2 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB2C1INT : 1; /*!< [21..21] Counter/Timer B2 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA3C1INT : 1; /*!< [22..22] Counter/Timer A3 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB3C1INT : 1; /*!< [23..23] Counter/Timer B3 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA4C1INT : 1; /*!< [24..24] Counter/Timer A4 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB4C1INT : 1; /*!< [25..25] Counter/Timer B4 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA5C1INT : 1; /*!< [26..26] Counter/Timer A5 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB5C1INT : 1; /*!< [27..27] Counter/Timer B5 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA6C1INT : 1; /*!< [28..28] Counter/Timer A6 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB6C1INT : 1; /*!< [29..29] Counter/Timer B6 interrupt based on COMPR1. */ - __IOM uint32_t CTMRA7C1INT : 1; /*!< [30..30] Counter/Timer A7 interrupt based on COMPR1. */ - __IOM uint32_t CTMRB7C1INT : 1; /*!< [31..31] Counter/Timer B7 interrupt based on COMPR1. */ - } INTSET_b; - } ; - __IM uint32_t RESERVED12[60]; - - union { - __IOM uint32_t STMINTEN; /*!< (@ 0x00000300) STIMER Interrupt registers: Enable */ - - struct { - __IOM uint32_t COMPAREA : 1; /*!< [0..0] COUNTER is greater than or equal to COMPARE register - A. */ - __IOM uint32_t COMPAREB : 1; /*!< [1..1] COUNTER is greater than or equal to COMPARE register - B. */ - __IOM uint32_t COMPAREC : 1; /*!< [2..2] COUNTER is greater than or equal to COMPARE register - C. */ - __IOM uint32_t COMPARED : 1; /*!< [3..3] COUNTER is greater than or equal to COMPARE register - D. */ - __IOM uint32_t COMPAREE : 1; /*!< [4..4] COUNTER is greater than or equal to COMPARE register - E. */ - __IOM uint32_t COMPAREF : 1; /*!< [5..5] COUNTER is greater than or equal to COMPARE register - F. */ - __IOM uint32_t COMPAREG : 1; /*!< [6..6] COUNTER is greater than or equal to COMPARE register - G. */ - __IOM uint32_t COMPAREH : 1; /*!< [7..7] COUNTER is greater than or equal to COMPARE register - H. */ - __IOM uint32_t OVERFLOW : 1; /*!< [8..8] COUNTER over flowed from 0xFFFFFFFF back to 0x00000000. */ - __IOM uint32_t CAPTUREA : 1; /*!< [9..9] CAPTURE register A has grabbed the value in the counter */ - __IOM uint32_t CAPTUREB : 1; /*!< [10..10] CAPTURE register B has grabbed the value in the counter */ - __IOM uint32_t CAPTUREC : 1; /*!< [11..11] CAPTURE register C has grabbed the value in the counter */ - __IOM uint32_t CAPTURED : 1; /*!< [12..12] CAPTURE register D has grabbed the value in the counter */ - } STMINTEN_b; - } ; - - union { - __IOM uint32_t STMINTSTAT; /*!< (@ 0x00000304) STIMER Interrupt registers: Status */ - - struct { - __IOM uint32_t COMPAREA : 1; /*!< [0..0] COUNTER is greater than or equal to COMPARE register - A. */ - __IOM uint32_t COMPAREB : 1; /*!< [1..1] COUNTER is greater than or equal to COMPARE register - B. */ - __IOM uint32_t COMPAREC : 1; /*!< [2..2] COUNTER is greater than or equal to COMPARE register - C. */ - __IOM uint32_t COMPARED : 1; /*!< [3..3] COUNTER is greater than or equal to COMPARE register - D. */ - __IOM uint32_t COMPAREE : 1; /*!< [4..4] COUNTER is greater than or equal to COMPARE register - E. */ - __IOM uint32_t COMPAREF : 1; /*!< [5..5] COUNTER is greater than or equal to COMPARE register - F. */ - __IOM uint32_t COMPAREG : 1; /*!< [6..6] COUNTER is greater than or equal to COMPARE register - G. */ - __IOM uint32_t COMPAREH : 1; /*!< [7..7] COUNTER is greater than or equal to COMPARE register - H. */ - __IOM uint32_t OVERFLOW : 1; /*!< [8..8] COUNTER over flowed from 0xFFFFFFFF back to 0x00000000. */ - __IOM uint32_t CAPTUREA : 1; /*!< [9..9] CAPTURE register A has grabbed the value in the counter */ - __IOM uint32_t CAPTUREB : 1; /*!< [10..10] CAPTURE register B has grabbed the value in the counter */ - __IOM uint32_t CAPTUREC : 1; /*!< [11..11] CAPTURE register C has grabbed the value in the counter */ - __IOM uint32_t CAPTURED : 1; /*!< [12..12] CAPTURE register D has grabbed the value in the counter */ - } STMINTSTAT_b; - } ; - - union { - __IOM uint32_t STMINTCLR; /*!< (@ 0x00000308) STIMER Interrupt registers: Clear */ - - struct { - __IOM uint32_t COMPAREA : 1; /*!< [0..0] COUNTER is greater than or equal to COMPARE register - A. */ - __IOM uint32_t COMPAREB : 1; /*!< [1..1] COUNTER is greater than or equal to COMPARE register - B. */ - __IOM uint32_t COMPAREC : 1; /*!< [2..2] COUNTER is greater than or equal to COMPARE register - C. */ - __IOM uint32_t COMPARED : 1; /*!< [3..3] COUNTER is greater than or equal to COMPARE register - D. */ - __IOM uint32_t COMPAREE : 1; /*!< [4..4] COUNTER is greater than or equal to COMPARE register - E. */ - __IOM uint32_t COMPAREF : 1; /*!< [5..5] COUNTER is greater than or equal to COMPARE register - F. */ - __IOM uint32_t COMPAREG : 1; /*!< [6..6] COUNTER is greater than or equal to COMPARE register - G. */ - __IOM uint32_t COMPAREH : 1; /*!< [7..7] COUNTER is greater than or equal to COMPARE register - H. */ - __IOM uint32_t OVERFLOW : 1; /*!< [8..8] COUNTER over flowed from 0xFFFFFFFF back to 0x00000000. */ - __IOM uint32_t CAPTUREA : 1; /*!< [9..9] CAPTURE register A has grabbed the value in the counter */ - __IOM uint32_t CAPTUREB : 1; /*!< [10..10] CAPTURE register B has grabbed the value in the counter */ - __IOM uint32_t CAPTUREC : 1; /*!< [11..11] CAPTURE register C has grabbed the value in the counter */ - __IOM uint32_t CAPTURED : 1; /*!< [12..12] CAPTURE register D has grabbed the value in the counter */ - } STMINTCLR_b; - } ; - - union { - __IOM uint32_t STMINTSET; /*!< (@ 0x0000030C) STIMER Interrupt registers: Set */ - - struct { - __IOM uint32_t COMPAREA : 1; /*!< [0..0] COUNTER is greater than or equal to COMPARE register - A. */ - __IOM uint32_t COMPAREB : 1; /*!< [1..1] COUNTER is greater than or equal to COMPARE register - B. */ - __IOM uint32_t COMPAREC : 1; /*!< [2..2] COUNTER is greater than or equal to COMPARE register - C. */ - __IOM uint32_t COMPARED : 1; /*!< [3..3] COUNTER is greater than or equal to COMPARE register - D. */ - __IOM uint32_t COMPAREE : 1; /*!< [4..4] COUNTER is greater than or equal to COMPARE register - E. */ - __IOM uint32_t COMPAREF : 1; /*!< [5..5] COUNTER is greater than or equal to COMPARE register - F. */ - __IOM uint32_t COMPAREG : 1; /*!< [6..6] COUNTER is greater than or equal to COMPARE register - G. */ - __IOM uint32_t COMPAREH : 1; /*!< [7..7] COUNTER is greater than or equal to COMPARE register - H. */ - __IOM uint32_t OVERFLOW : 1; /*!< [8..8] COUNTER over flowed from 0xFFFFFFFF back to 0x00000000. */ - __IOM uint32_t CAPTUREA : 1; /*!< [9..9] CAPTURE register A has grabbed the value in the counter */ - __IOM uint32_t CAPTUREB : 1; /*!< [10..10] CAPTURE register B has grabbed the value in the counter */ - __IOM uint32_t CAPTUREC : 1; /*!< [11..11] CAPTURE register C has grabbed the value in the counter */ - __IOM uint32_t CAPTURED : 1; /*!< [12..12] CAPTURE register D has grabbed the value in the counter */ - } STMINTSET_b; - } ; -} CTIMER_Type; /*!< Size = 784 (0x310) */ - - - -/* =========================================================================================================================== */ -/* ================ GPIO ================ */ -/* =========================================================================================================================== */ - - -/** - * @brief General Purpose IO (GPIO) - */ - -typedef struct { /*!< (@ 0x40010000) GPIO Structure */ - - union { - __IOM uint32_t PADREGA; /*!< (@ 0x00000000) Pad Configuration Register A (Pads 0-3) */ - - struct { - __IOM uint32_t PAD0PULL : 1; /*!< [0..0] Pad 0 pullup enable */ - __IOM uint32_t PAD0INPEN : 1; /*!< [1..1] Pad 0 input enable */ - __IOM uint32_t PAD0STRNG : 1; /*!< [2..2] Pad 0 drive strength */ - __IOM uint32_t PAD0FNCSEL : 3; /*!< [5..3] Pad 0 function select */ - __IOM uint32_t PAD0RSEL : 2; /*!< [7..6] Pad 0 pullup resistor selection. */ - __IOM uint32_t PAD1PULL : 1; /*!< [8..8] Pad 1 pullup enable */ - __IOM uint32_t PAD1INPEN : 1; /*!< [9..9] Pad 1 input enable */ - __IOM uint32_t PAD1STRNG : 1; /*!< [10..10] Pad 1 drive strength */ - __IOM uint32_t PAD1FNCSEL : 3; /*!< [13..11] Pad 1 function select */ - __IOM uint32_t PAD1RSEL : 2; /*!< [15..14] Pad 1 pullup resistor selection. */ - __IOM uint32_t PAD2PULL : 1; /*!< [16..16] Pad 2 pullup enable */ - __IOM uint32_t PAD2INPEN : 1; /*!< [17..17] Pad 2 input enable */ - __IOM uint32_t PAD2STRNG : 1; /*!< [18..18] Pad 2 drive strength */ - __IOM uint32_t PAD2FNCSEL : 3; /*!< [21..19] Pad 2 function select */ - __IM uint32_t : 2; - __IOM uint32_t PAD3PULL : 1; /*!< [24..24] Pad 3 pullup enable */ - __IOM uint32_t PAD3INPEN : 1; /*!< [25..25] Pad 3 input enable. */ - __IOM uint32_t PAD3STRNG : 1; /*!< [26..26] Pad 3 drive strength. */ - __IOM uint32_t PAD3FNCSEL : 3; /*!< [29..27] Pad 3 function select */ - __IOM uint32_t PAD3PWRUP : 1; /*!< [30..30] Pad 3 VDD power switch enable */ - } PADREGA_b; - } ; - - union { - __IOM uint32_t PADREGB; /*!< (@ 0x00000004) Pad Configuration Register B (Pads 4-7) */ - - struct { - __IOM uint32_t PAD4PULL : 1; /*!< [0..0] Pad 4 pullup enable */ - __IOM uint32_t PAD4INPEN : 1; /*!< [1..1] Pad 4 input enable */ - __IOM uint32_t PAD4STRNG : 1; /*!< [2..2] Pad 4 drive strength */ - __IOM uint32_t PAD4FNCSEL : 3; /*!< [5..3] Pad 4 function select */ - __IM uint32_t : 2; - __IOM uint32_t PAD5PULL : 1; /*!< [8..8] Pad 5 pullup enable */ - __IOM uint32_t PAD5INPEN : 1; /*!< [9..9] Pad 5 input enable */ - __IOM uint32_t PAD5STRNG : 1; /*!< [10..10] Pad 5 drive strength */ - __IOM uint32_t PAD5FNCSEL : 3; /*!< [13..11] Pad 5 function select */ - __IOM uint32_t PAD5RSEL : 2; /*!< [15..14] Pad 5 pullup resistor selection. */ - __IOM uint32_t PAD6PULL : 1; /*!< [16..16] Pad 6 pullup enable */ - __IOM uint32_t PAD6INPEN : 1; /*!< [17..17] Pad 6 input enable */ - __IOM uint32_t PAD6STRNG : 1; /*!< [18..18] Pad 6 drive strength */ - __IOM uint32_t PAD6FNCSEL : 3; /*!< [21..19] Pad 6 function select */ - __IOM uint32_t PAD6RSEL : 2; /*!< [23..22] Pad 6 pullup resistor selection. */ - __IOM uint32_t PAD7PULL : 1; /*!< [24..24] Pad 7 pullup enable */ - __IOM uint32_t PAD7INPEN : 1; /*!< [25..25] Pad 7 input enable */ - __IOM uint32_t PAD7STRNG : 1; /*!< [26..26] Pad 7 drive strength */ - __IOM uint32_t PAD7FNCSEL : 3; /*!< [29..27] Pad 7 function select */ - } PADREGB_b; - } ; - - union { - __IOM uint32_t PADREGC; /*!< (@ 0x00000008) Pad Configuration Register C (Pads 8-11) */ - - struct { - __IOM uint32_t PAD8PULL : 1; /*!< [0..0] Pad 8 pullup enable */ - __IOM uint32_t PAD8INPEN : 1; /*!< [1..1] Pad 8 input enable */ - __IOM uint32_t PAD8STRNG : 1; /*!< [2..2] Pad 8 drive strength */ - __IOM uint32_t PAD8FNCSEL : 3; /*!< [5..3] Pad 8 function select */ - __IOM uint32_t PAD8RSEL : 2; /*!< [7..6] Pad 8 pullup resistor selection. */ - __IOM uint32_t PAD9PULL : 1; /*!< [8..8] Pad 9 pullup enable */ - __IOM uint32_t PAD9INPEN : 1; /*!< [9..9] Pad 9 input enable */ - __IOM uint32_t PAD9STRNG : 1; /*!< [10..10] Pad 9 drive strength */ - __IOM uint32_t PAD9FNCSEL : 3; /*!< [13..11] Pad 9 function select */ - __IOM uint32_t PAD9RSEL : 2; /*!< [15..14] Pad 9 pullup resistor selection */ - __IOM uint32_t PAD10PULL : 1; /*!< [16..16] Pad 10 pullup enable */ - __IOM uint32_t PAD10INPEN : 1; /*!< [17..17] Pad 10 input enable */ - __IOM uint32_t PAD10STRNG : 1; /*!< [18..18] Pad 10 drive strength */ - __IOM uint32_t PAD10FNCSEL : 3; /*!< [21..19] Pad 10 function select */ - __IM uint32_t : 2; - __IOM uint32_t PAD11PULL : 1; /*!< [24..24] Pad 11 pullup enable */ - __IOM uint32_t PAD11INPEN : 1; /*!< [25..25] Pad 11 input enable */ - __IOM uint32_t PAD11STRNG : 1; /*!< [26..26] Pad 11 drive strength */ - __IOM uint32_t PAD11FNCSEL : 3; /*!< [29..27] Pad 11 function select */ - } PADREGC_b; - } ; - - union { - __IOM uint32_t PADREGD; /*!< (@ 0x0000000C) Pad Configuration Register D (Pads 12-15) */ - - struct { - __IOM uint32_t PAD12PULL : 1; /*!< [0..0] Pad 12 pullup enable */ - __IOM uint32_t PAD12INPEN : 1; /*!< [1..1] Pad 12 input enable */ - __IOM uint32_t PAD12STRNG : 1; /*!< [2..2] Pad 12 drive strength */ - __IOM uint32_t PAD12FNCSEL : 3; /*!< [5..3] Pad 12 function select */ - __IM uint32_t : 2; - __IOM uint32_t PAD13PULL : 1; /*!< [8..8] Pad 13 pullup enable */ - __IOM uint32_t PAD13INPEN : 1; /*!< [9..9] Pad 13 input enable */ - __IOM uint32_t PAD13STRNG : 1; /*!< [10..10] Pad 13 drive strength */ - __IOM uint32_t PAD13FNCSEL : 3; /*!< [13..11] Pad 13 function select */ - __IM uint32_t : 2; - __IOM uint32_t PAD14PULL : 1; /*!< [16..16] Pad 14 pullup enable */ - __IOM uint32_t PAD14INPEN : 1; /*!< [17..17] Pad 14 input enable */ - __IOM uint32_t PAD14STRNG : 1; /*!< [18..18] Pad 14 drive strength */ - __IOM uint32_t PAD14FNCSEL : 3; /*!< [21..19] Pad 14 function select */ - __IM uint32_t : 2; - __IOM uint32_t PAD15PULL : 1; /*!< [24..24] Pad 15 pullup enable */ - __IOM uint32_t PAD15INPEN : 1; /*!< [25..25] Pad 15 input enable */ - __IOM uint32_t PAD15STRNG : 1; /*!< [26..26] Pad 15 drive strength */ - __IOM uint32_t PAD15FNCSEL : 3; /*!< [29..27] Pad 15 function select */ - } PADREGD_b; - } ; - - union { - __IOM uint32_t PADREGE; /*!< (@ 0x00000010) Pad Configuration Register E (Pads 16-19) */ - - struct { - __IOM uint32_t PAD16PULL : 1; /*!< [0..0] Pad 16 pullup enable */ - __IOM uint32_t PAD16INPEN : 1; /*!< [1..1] Pad 16 input enable */ - __IOM uint32_t PAD16STRNG : 1; /*!< [2..2] Pad 16 drive strength */ - __IOM uint32_t PAD16FNCSEL : 3; /*!< [5..3] Pad 16 function select */ - __IM uint32_t : 2; - __IOM uint32_t PAD17PULL : 1; /*!< [8..8] Pad 17 pullup enable */ - __IOM uint32_t PAD17INPEN : 1; /*!< [9..9] Pad 17 input enable */ - __IOM uint32_t PAD17STRNG : 1; /*!< [10..10] Pad 17 drive strength */ - __IOM uint32_t PAD17FNCSEL : 3; /*!< [13..11] Pad 17 function select */ - __IM uint32_t : 2; - __IOM uint32_t PAD18PULL : 1; /*!< [16..16] Pad 18 pullup enable */ - __IOM uint32_t PAD18INPEN : 1; /*!< [17..17] Pad 18 input enable */ - __IOM uint32_t PAD18STRNG : 1; /*!< [18..18] Pad 18 drive strength */ - __IOM uint32_t PAD18FNCSEL : 3; /*!< [21..19] Pad 18 function select */ - __IM uint32_t : 2; - __IOM uint32_t PAD19PULL : 1; /*!< [24..24] Pad 19 pullup enable */ - __IOM uint32_t PAD19INPEN : 1; /*!< [25..25] Pad 19 input enable */ - __IOM uint32_t PAD19STRNG : 1; /*!< [26..26] Pad 19 drive strength */ - __IOM uint32_t PAD19FNCSEL : 3; /*!< [29..27] Pad 19 function select */ - } PADREGE_b; - } ; - - union { - __IOM uint32_t PADREGF; /*!< (@ 0x00000014) Pad Configuration Register F (Pads 20-23) */ - - struct { - __IOM uint32_t PAD20PULL : 1; /*!< [0..0] Pad 20 pulldown enable */ - __IOM uint32_t PAD20INPEN : 1; /*!< [1..1] Pad 20 input enable */ - __IOM uint32_t PAD20STRNG : 1; /*!< [2..2] Pad 20 drive strength */ - __IOM uint32_t PAD20FNCSEL : 3; /*!< [5..3] Pad 20 function select */ - __IM uint32_t : 2; - __IOM uint32_t PAD21PULL : 1; /*!< [8..8] Pad 21 pullup enable */ - __IOM uint32_t PAD21INPEN : 1; /*!< [9..9] Pad 21 input enable */ - __IOM uint32_t PAD21STRNG : 1; /*!< [10..10] Pad 21 drive strength */ - __IOM uint32_t PAD21FNCSEL : 3; /*!< [13..11] Pad 21 function select */ - __IM uint32_t : 2; - __IOM uint32_t PAD22PULL : 1; /*!< [16..16] Pad 22 pullup enable */ - __IOM uint32_t PAD22INPEN : 1; /*!< [17..17] Pad 22 input enable */ - __IOM uint32_t PAD22STRNG : 1; /*!< [18..18] Pad 22 drive strength */ - __IOM uint32_t PAD22FNCSEL : 3; /*!< [21..19] Pad 22 function select */ - __IM uint32_t : 2; - __IOM uint32_t PAD23PULL : 1; /*!< [24..24] Pad 23 pullup enable */ - __IOM uint32_t PAD23INPEN : 1; /*!< [25..25] Pad 23 input enable */ - __IOM uint32_t PAD23STRNG : 1; /*!< [26..26] Pad 23 drive strength */ - __IOM uint32_t PAD23FNCSEL : 3; /*!< [29..27] Pad 23 function select */ - } PADREGF_b; - } ; - - union { - __IOM uint32_t PADREGG; /*!< (@ 0x00000018) Pad Configuration Register G (Pads 24-27) */ - - struct { - __IOM uint32_t PAD24PULL : 1; /*!< [0..0] Pad 24 pullup enable */ - __IOM uint32_t PAD24INPEN : 1; /*!< [1..1] Pad 24 input enable */ - __IOM uint32_t PAD24STRNG : 1; /*!< [2..2] Pad 24 drive strength */ - __IOM uint32_t PAD24FNCSEL : 3; /*!< [5..3] Pad 24 function select */ - __IM uint32_t : 2; - __IOM uint32_t PAD25PULL : 1; /*!< [8..8] Pad 25 pullup enable */ - __IOM uint32_t PAD25INPEN : 1; /*!< [9..9] Pad 25 input enable */ - __IOM uint32_t PAD25STRNG : 1; /*!< [10..10] Pad 25 drive strength */ - __IOM uint32_t PAD25FNCSEL : 3; /*!< [13..11] Pad 25 function select */ - __IOM uint32_t PAD25RSEL : 2; /*!< [15..14] Pad 25 pullup resistor selection. */ - __IOM uint32_t PAD26PULL : 1; /*!< [16..16] Pad 26 pullup enable */ - __IOM uint32_t PAD26INPEN : 1; /*!< [17..17] Pad 26 input enable */ - __IOM uint32_t PAD26STRNG : 1; /*!< [18..18] Pad 26 drive strength */ - __IOM uint32_t PAD26FNCSEL : 3; /*!< [21..19] Pad 26 function select */ - __IM uint32_t : 2; - __IOM uint32_t PAD27PULL : 1; /*!< [24..24] Pad 27 pullup enable */ - __IOM uint32_t PAD27INPEN : 1; /*!< [25..25] Pad 27 input enable */ - __IOM uint32_t PAD27STRNG : 1; /*!< [26..26] Pad 27 drive strength */ - __IOM uint32_t PAD27FNCSEL : 3; /*!< [29..27] Pad 27 function select */ - __IOM uint32_t PAD27RSEL : 2; /*!< [31..30] Pad 27 pullup resistor selection. */ - } PADREGG_b; - } ; - - union { - __IOM uint32_t PADREGH; /*!< (@ 0x0000001C) Pad Configuration Register H (Pads 28-31) */ - - struct { - __IOM uint32_t PAD28PULL : 1; /*!< [0..0] Pad 28 pullup enable */ - __IOM uint32_t PAD28INPEN : 1; /*!< [1..1] Pad 28 input enable */ - __IOM uint32_t PAD28STRNG : 1; /*!< [2..2] Pad 28 drive strength */ - __IOM uint32_t PAD28FNCSEL : 3; /*!< [5..3] Pad 28 function select */ - __IM uint32_t : 2; - __IOM uint32_t PAD29PULL : 1; /*!< [8..8] Pad 29 pullup enable */ - __IOM uint32_t PAD29INPEN : 1; /*!< [9..9] Pad 29 input enable */ - __IOM uint32_t PAD29STRNG : 1; /*!< [10..10] Pad 29 drive strength */ - __IOM uint32_t PAD29FNCSEL : 3; /*!< [13..11] Pad 29 function select */ - __IM uint32_t : 2; - __IOM uint32_t PAD30PULL : 1; /*!< [16..16] Pad 30 pullup enable */ - __IOM uint32_t PAD30INPEN : 1; /*!< [17..17] Pad 30 input enable */ - __IOM uint32_t PAD30STRNG : 1; /*!< [18..18] Pad 30 drive strength */ - __IOM uint32_t PAD30FNCSEL : 3; /*!< [21..19] Pad 30 function select */ - __IM uint32_t : 2; - __IOM uint32_t PAD31PULL : 1; /*!< [24..24] Pad 31 pullup enable */ - __IOM uint32_t PAD31INPEN : 1; /*!< [25..25] Pad 31 input enable */ - __IOM uint32_t PAD31STRNG : 1; /*!< [26..26] Pad 31 drive strength */ - __IOM uint32_t PAD31FNCSEL : 3; /*!< [29..27] Pad 31 function select */ - } PADREGH_b; - } ; - - union { - __IOM uint32_t PADREGI; /*!< (@ 0x00000020) Pad Configuration Register I (Pads 32-25) */ - - struct { - __IOM uint32_t PAD32PULL : 1; /*!< [0..0] Pad 32 pullup enable */ - __IOM uint32_t PAD32INPEN : 1; /*!< [1..1] Pad 32 input enable */ - __IOM uint32_t PAD32STRNG : 1; /*!< [2..2] Pad 32 drive strength */ - __IOM uint32_t PAD32FNCSEL : 3; /*!< [5..3] Pad 32 function select */ - __IM uint32_t : 2; - __IOM uint32_t PAD33PULL : 1; /*!< [8..8] Pad 33 pullup enable */ - __IOM uint32_t PAD33INPEN : 1; /*!< [9..9] Pad 33 input enable */ - __IOM uint32_t PAD33STRNG : 1; /*!< [10..10] Pad 33 drive strength */ - __IOM uint32_t PAD33FNCSEL : 3; /*!< [13..11] Pad 33 function select */ - __IM uint32_t : 2; - __IOM uint32_t PAD34PULL : 1; /*!< [16..16] Pad 34 pullup enable */ - __IOM uint32_t PAD34INPEN : 1; /*!< [17..17] Pad 34 input enable */ - __IOM uint32_t PAD34STRNG : 1; /*!< [18..18] Pad 34 drive strength */ - __IOM uint32_t PAD34FNCSEL : 3; /*!< [21..19] Pad 34 function select */ - __IM uint32_t : 2; - __IOM uint32_t PAD35PULL : 1; /*!< [24..24] Pad 35 pullup enable */ - __IOM uint32_t PAD35INPEN : 1; /*!< [25..25] Pad 35 input enable */ - __IOM uint32_t PAD35STRNG : 1; /*!< [26..26] Pad 35 drive strength */ - __IOM uint32_t PAD35FNCSEL : 3; /*!< [29..27] Pad 35 function select */ - } PADREGI_b; - } ; - - union { - __IOM uint32_t PADREGJ; /*!< (@ 0x00000024) Pad Configuration Register J (Pads 36-39) */ - - struct { - __IOM uint32_t PAD36PULL : 1; /*!< [0..0] Pad 36 pullup enable */ - __IOM uint32_t PAD36INPEN : 1; /*!< [1..1] Pad 36 input enable */ - __IOM uint32_t PAD36STRNG : 1; /*!< [2..2] Pad 36 drive strength */ - __IOM uint32_t PAD36FNCSEL : 3; /*!< [5..3] Pad 36 function select */ - __IOM uint32_t PAD36PWRUP : 1; /*!< [6..6] Pad 36 VDD power switch enable */ - __IM uint32_t : 1; - __IOM uint32_t PAD37PULL : 1; /*!< [8..8] Pad 37 pullup enable */ - __IOM uint32_t PAD37INPEN : 1; /*!< [9..9] Pad 37 input enable */ - __IOM uint32_t PAD37STRNG : 1; /*!< [10..10] Pad 37 drive strength */ - __IOM uint32_t PAD37FNCSEL : 3; /*!< [13..11] Pad 37 function select */ - __IM uint32_t : 1; - __IOM uint32_t PAD37PWRDN : 1; /*!< [15..15] Pad 37 VSS power switch enable */ - __IOM uint32_t PAD38PULL : 1; /*!< [16..16] Pad 38 pullup enable */ - __IOM uint32_t PAD38INPEN : 1; /*!< [17..17] Pad 38 input enable */ - __IOM uint32_t PAD38STRNG : 1; /*!< [18..18] Pad 38 drive strength */ - __IOM uint32_t PAD38FNCSEL : 3; /*!< [21..19] Pad 38 function select */ - __IM uint32_t : 2; - __IOM uint32_t PAD39PULL : 1; /*!< [24..24] Pad 39 pullup enable */ - __IOM uint32_t PAD39INPEN : 1; /*!< [25..25] Pad 39 input enable */ - __IOM uint32_t PAD39STRNG : 1; /*!< [26..26] Pad 39 drive strength */ - __IOM uint32_t PAD39FNCSEL : 3; /*!< [29..27] Pad 39 function select */ - __IOM uint32_t PAD39RSEL : 2; /*!< [31..30] Pad 39 pullup resistor selection. */ - } PADREGJ_b; - } ; - - union { - __IOM uint32_t PADREGK; /*!< (@ 0x00000028) Pad Configuration Register K (Pads 40-43) */ - - struct { - __IOM uint32_t PAD40PULL : 1; /*!< [0..0] Pad 40 pullup enable */ - __IOM uint32_t PAD40INPEN : 1; /*!< [1..1] Pad 40 input enable */ - __IOM uint32_t PAD40STRNG : 1; /*!< [2..2] Pad 40 drive strength */ - __IOM uint32_t PAD40FNCSEL : 3; /*!< [5..3] Pad 40 function select */ - __IOM uint32_t PAD40RSEL : 2; /*!< [7..6] Pad 40 pullup resistor selection. */ - __IOM uint32_t PAD41PULL : 1; /*!< [8..8] Pad 41 pullup enable */ - __IOM uint32_t PAD41INPEN : 1; /*!< [9..9] Pad 41 input enable */ - __IOM uint32_t PAD41STRNG : 1; /*!< [10..10] Pad 41 drive strength */ - __IOM uint32_t PAD41FNCSEL : 3; /*!< [13..11] Pad 41 function select */ - __IM uint32_t : 1; - __IOM uint32_t PAD41PWRDN : 1; /*!< [15..15] Pad 41 power switch enable */ - __IOM uint32_t PAD42PULL : 1; /*!< [16..16] Pad 42 pullup enable */ - __IOM uint32_t PAD42INPEN : 1; /*!< [17..17] Pad 42 input enable */ - __IOM uint32_t PAD42STRNG : 1; /*!< [18..18] Pad 42 drive strength */ - __IOM uint32_t PAD42FNCSEL : 3; /*!< [21..19] Pad 42 function select */ - __IOM uint32_t PAD42RSEL : 2; /*!< [23..22] Pad 42 pullup resistor selection. */ - __IOM uint32_t PAD43PULL : 1; /*!< [24..24] Pad 43 pullup enable */ - __IOM uint32_t PAD43INPEN : 1; /*!< [25..25] Pad 43 input enable */ - __IOM uint32_t PAD43STRNG : 1; /*!< [26..26] Pad 43 drive strength */ - __IOM uint32_t PAD43FNCSEL : 3; /*!< [29..27] Pad 43 function select */ - __IOM uint32_t PAD43RSEL : 2; /*!< [31..30] Pad 43 pullup resistor selection. */ - } PADREGK_b; - } ; - - union { - __IOM uint32_t PADREGL; /*!< (@ 0x0000002C) Pad Configuration Register L (Pads 44-47) */ - - struct { - __IOM uint32_t PAD44PULL : 1; /*!< [0..0] Pad 44 pullup enable */ - __IOM uint32_t PAD44INPEN : 1; /*!< [1..1] Pad 44 input enable */ - __IOM uint32_t PAD44STRNG : 1; /*!< [2..2] Pad 44 drive strength */ - __IOM uint32_t PAD44FNCSEL : 3; /*!< [5..3] Pad 44 function select */ - __IM uint32_t : 2; - __IOM uint32_t PAD45PULL : 1; /*!< [8..8] Pad 45 pullup enable */ - __IOM uint32_t PAD45INPEN : 1; /*!< [9..9] Pad 45 input enable */ - __IOM uint32_t PAD45STRNG : 1; /*!< [10..10] Pad 45 drive strength */ - __IOM uint32_t PAD45FNCSEL : 3; /*!< [13..11] Pad 45 function select */ - __IM uint32_t : 2; - __IOM uint32_t PAD46PULL : 1; /*!< [16..16] Pad 46 pullup enable */ - __IOM uint32_t PAD46INPEN : 1; /*!< [17..17] Pad 46 input enable */ - __IOM uint32_t PAD46STRNG : 1; /*!< [18..18] Pad 46 drive strength */ - __IOM uint32_t PAD46FNCSEL : 3; /*!< [21..19] Pad 46 function select */ - __IM uint32_t : 2; - __IOM uint32_t PAD47PULL : 1; /*!< [24..24] Pad 47 pullup enable */ - __IOM uint32_t PAD47INPEN : 1; /*!< [25..25] Pad 47 input enable */ - __IOM uint32_t PAD47STRNG : 1; /*!< [26..26] Pad 47 drive strength */ - __IOM uint32_t PAD47FNCSEL : 3; /*!< [29..27] Pad 47 function select */ - } PADREGL_b; - } ; - - union { - __IOM uint32_t PADREGM; /*!< (@ 0x00000030) Pad Configuration Register M (Pads 47-48) */ - - struct { - __IOM uint32_t PAD48PULL : 1; /*!< [0..0] Pad 48 pullup enable */ - __IOM uint32_t PAD48INPEN : 1; /*!< [1..1] Pad 48 input enable */ - __IOM uint32_t PAD48STRNG : 1; /*!< [2..2] Pad 48 drive strength */ - __IOM uint32_t PAD48FNCSEL : 3; /*!< [5..3] Pad 48 function select */ - __IOM uint32_t PAD48RSEL : 2; /*!< [7..6] Pad 48 pullup resistor selection. */ - __IOM uint32_t PAD49PULL : 1; /*!< [8..8] Pad 49 pullup enable */ - __IOM uint32_t PAD49INPEN : 1; /*!< [9..9] Pad 49 input enable */ - __IOM uint32_t PAD49STRNG : 1; /*!< [10..10] Pad 49 drive strength */ - __IOM uint32_t PAD49FNCSEL : 3; /*!< [13..11] Pad 49 function select */ - __IOM uint32_t PAD49RSEL : 2; /*!< [15..14] Pad 49 pullup resistor selection. */ - } PADREGM_b; - } ; - __IM uint32_t RESERVED[3]; - - union { - __IOM uint32_t CFGA; /*!< (@ 0x00000040) GPIO Configuration Register A (Pads 0-7) */ - - struct { - __IOM uint32_t GPIO0INCFG : 1; /*!< [0..0] GPIO0 input enable. */ - __IOM uint32_t GPIO0OUTCFG : 2; /*!< [2..1] GPIO0 output configuration. */ - __IOM uint32_t GPIO0INTD : 1; /*!< [3..3] GPIO0 interrupt direction. */ - __IOM uint32_t GPIO1INCFG : 1; /*!< [4..4] GPIO1 input enable. */ - __IOM uint32_t GPIO1OUTCFG : 2; /*!< [6..5] GPIO1 output configuration. */ - __IOM uint32_t GPIO1INTD : 1; /*!< [7..7] GPIO1 interrupt direction. */ - __IOM uint32_t GPIO2INCFG : 1; /*!< [8..8] GPIO2 input enable. */ - __IOM uint32_t GPIO2OUTCFG : 2; /*!< [10..9] GPIO2 output configuration. */ - __IOM uint32_t GPIO2INTD : 1; /*!< [11..11] GPIO2 interrupt direction. */ - __IOM uint32_t GPIO3INCFG : 1; /*!< [12..12] GPIO3 input enable. */ - __IOM uint32_t GPIO3OUTCFG : 2; /*!< [14..13] GPIO3 output configuration. */ - __IOM uint32_t GPIO3INTD : 1; /*!< [15..15] GPIO3 interrupt direction. */ - __IOM uint32_t GPIO4INCFG : 1; /*!< [16..16] GPIO4 input enable. */ - __IOM uint32_t GPIO4OUTCFG : 2; /*!< [18..17] GPIO4 output configuration. */ - __IOM uint32_t GPIO4INTD : 1; /*!< [19..19] GPIO4 interrupt direction. */ - __IOM uint32_t GPIO5INCFG : 1; /*!< [20..20] GPIO5 input enable. */ - __IOM uint32_t GPIO5OUTCFG : 2; /*!< [22..21] GPIO5 output configuration. */ - __IOM uint32_t GPIO5INTD : 1; /*!< [23..23] GPIO5 interrupt direction. */ - __IOM uint32_t GPIO6INCFG : 1; /*!< [24..24] GPIO6 input enable. */ - __IOM uint32_t GPIO6OUTCFG : 2; /*!< [26..25] GPIO6 output configuration. */ - __IOM uint32_t GPIO6INTD : 1; /*!< [27..27] GPIO6 interrupt direction. */ - __IOM uint32_t GPIO7INCFG : 1; /*!< [28..28] GPIO7 input enable. */ - __IOM uint32_t GPIO7OUTCFG : 2; /*!< [30..29] GPIO7 output configuration. */ - __IOM uint32_t GPIO7INTD : 1; /*!< [31..31] GPIO7 interrupt direction, nCE polarity. */ - } CFGA_b; - } ; - - union { - __IOM uint32_t CFGB; /*!< (@ 0x00000044) GPIO Configuration Register B (Pads 8-15) */ - - struct { - __IOM uint32_t GPIO8INCFG : 1; /*!< [0..0] GPIO8 input enable. */ - __IOM uint32_t GPIO8OUTCFG : 2; /*!< [2..1] GPIO8 output configuration. */ - __IOM uint32_t GPIO8INTD : 1; /*!< [3..3] GPIO8 interrupt direction. */ - __IOM uint32_t GPIO9INCFG : 1; /*!< [4..4] GPIO9 input enable. */ - __IOM uint32_t GPIO9OUTCFG : 2; /*!< [6..5] GPIO9 output configuration. */ - __IOM uint32_t GPIO9INTD : 1; /*!< [7..7] GPIO9 interrupt direction. */ - __IOM uint32_t GPIO10INCFG : 1; /*!< [8..8] GPIO10 input enable. */ - __IOM uint32_t GPIO10OUTCFG : 2; /*!< [10..9] GPIO10 output configuration. */ - __IOM uint32_t GPIO10INTD : 1; /*!< [11..11] GPIO10 interrupt direction. */ - __IOM uint32_t GPIO11INCFG : 1; /*!< [12..12] GPIO11 input enable. */ - __IOM uint32_t GPIO11OUTCFG : 2; /*!< [14..13] GPIO11 output configuration. */ - __IOM uint32_t GPIO11INTD : 1; /*!< [15..15] GPIO11 interrupt direction. */ - __IOM uint32_t GPIO12INCFG : 1; /*!< [16..16] GPIO12 input enable. */ - __IOM uint32_t GPIO12OUTCFG : 2; /*!< [18..17] GPIO12 output configuration. */ - __IOM uint32_t GPIO12INTD : 1; /*!< [19..19] GPIO12 interrupt direction. */ - __IOM uint32_t GPIO13INCFG : 1; /*!< [20..20] GPIO13 input enable. */ - __IOM uint32_t GPIO13OUTCFG : 2; /*!< [22..21] GPIO13 output configuration. */ - __IOM uint32_t GPIO13INTD : 1; /*!< [23..23] GPIO13 interrupt direction. */ - __IOM uint32_t GPIO14INCFG : 1; /*!< [24..24] GPIO14 input enable. */ - __IOM uint32_t GPIO14OUTCFG : 2; /*!< [26..25] GPIO14 output configuration. */ - __IOM uint32_t GPIO14INTD : 1; /*!< [27..27] GPIO14 interrupt direction. */ - __IOM uint32_t GPIO15INCFG : 1; /*!< [28..28] GPIO15 input enable. */ - __IOM uint32_t GPIO15OUTCFG : 2; /*!< [30..29] GPIO15 output configuration. */ - __IOM uint32_t GPIO15INTD : 1; /*!< [31..31] GPIO15 interrupt direction. */ - } CFGB_b; - } ; - - union { - __IOM uint32_t CFGC; /*!< (@ 0x00000048) GPIO Configuration Register C (Pads 16-23) */ - - struct { - __IOM uint32_t GPIO16INCFG : 1; /*!< [0..0] GPIO16 input enable. */ - __IOM uint32_t GPIO16OUTCFG : 2; /*!< [2..1] GPIO16 output configuration. */ - __IOM uint32_t GPIO16INTD : 1; /*!< [3..3] GPIO16 interrupt direction. */ - __IOM uint32_t GPIO17INCFG : 1; /*!< [4..4] GPIO17 input enable. */ - __IOM uint32_t GPIO17OUTCFG : 2; /*!< [6..5] GPIO17 output configuration. */ - __IOM uint32_t GPIO17INTD : 1; /*!< [7..7] GPIO17 interrupt direction. */ - __IOM uint32_t GPIO18INCFG : 1; /*!< [8..8] GPIO18 input enable. */ - __IOM uint32_t GPIO18OUTCFG : 2; /*!< [10..9] GPIO18 output configuration. */ - __IOM uint32_t GPIO18INTD : 1; /*!< [11..11] GPIO18 interrupt direction. */ - __IOM uint32_t GPIO19INCFG : 1; /*!< [12..12] GPIO19 input enable. */ - __IOM uint32_t GPIO19OUTCFG : 2; /*!< [14..13] GPIO19 output configuration. */ - __IOM uint32_t GPIO19INTD : 1; /*!< [15..15] GPIO19 interrupt direction. */ - __IOM uint32_t GPIO20INCFG : 1; /*!< [16..16] GPIO20 input enable. */ - __IOM uint32_t GPIO20OUTCFG : 2; /*!< [18..17] GPIO20 output configuration. */ - __IOM uint32_t GPIO20INTD : 1; /*!< [19..19] GPIO20 interrupt direction. */ - __IOM uint32_t GPIO21INCFG : 1; /*!< [20..20] GPIO21 input enable. */ - __IOM uint32_t GPIO21OUTCFG : 2; /*!< [22..21] GPIO21 output configuration. */ - __IOM uint32_t GPIO21INTD : 1; /*!< [23..23] GPIO21 interrupt direction. */ - __IOM uint32_t GPIO22INCFG : 1; /*!< [24..24] GPIO22 input enable. */ - __IOM uint32_t GPIO22OUTCFG : 2; /*!< [26..25] GPIO22 output configuration. */ - __IOM uint32_t GPIO22INTD : 1; /*!< [27..27] GPIO22 interrupt direction. */ - __IOM uint32_t GPIO23INCFG : 1; /*!< [28..28] GPIO23 input enable. */ - __IOM uint32_t GPIO23OUTCFG : 2; /*!< [30..29] GPIO23 output configuration. */ - __IOM uint32_t GPIO23INTD : 1; /*!< [31..31] GPIO23 interrupt direction. */ - } CFGC_b; - } ; - - union { - __IOM uint32_t CFGD; /*!< (@ 0x0000004C) GPIO Configuration Register D (Pads 24-31) */ - - struct { - __IOM uint32_t GPIO24INCFG : 1; /*!< [0..0] GPIO24 input enable. */ - __IOM uint32_t GPIO24OUTCFG : 2; /*!< [2..1] GPIO24 output configuration. */ - __IOM uint32_t GPIO24INTD : 1; /*!< [3..3] GPIO24 interrupt direction. */ - __IOM uint32_t GPIO25INCFG : 1; /*!< [4..4] GPIO25 input enable. */ - __IOM uint32_t GPIO25OUTCFG : 2; /*!< [6..5] GPIO25 output configuration. */ - __IOM uint32_t GPIO25INTD : 1; /*!< [7..7] GPIO25 interrupt direction. */ - __IOM uint32_t GPIO26INCFG : 1; /*!< [8..8] GPIO26 input enable. */ - __IOM uint32_t GPIO26OUTCFG : 2; /*!< [10..9] GPIO26 output configuration. */ - __IOM uint32_t GPIO26INTD : 1; /*!< [11..11] GPIO26 interrupt direction. */ - __IOM uint32_t GPIO27INCFG : 1; /*!< [12..12] GPIO27 input enable. */ - __IOM uint32_t GPIO27OUTCFG : 2; /*!< [14..13] GPIO27 output configuration. */ - __IOM uint32_t GPIO27INTD : 1; /*!< [15..15] GPIO27 interrupt direction. */ - __IOM uint32_t GPIO28INCFG : 1; /*!< [16..16] GPIO28 input enable. */ - __IOM uint32_t GPIO28OUTCFG : 2; /*!< [18..17] GPIO28 output configuration. */ - __IOM uint32_t GPIO28INTD : 1; /*!< [19..19] GPIO28 interrupt direction. */ - __IOM uint32_t GPIO29INCFG : 1; /*!< [20..20] GPIO29 input enable. */ - __IOM uint32_t GPIO29OUTCFG : 2; /*!< [22..21] GPIO29 output configuration. */ - __IOM uint32_t GPIO29INTD : 1; /*!< [23..23] GPIO29 interrupt direction. */ - __IOM uint32_t GPIO30INCFG : 1; /*!< [24..24] GPIO30 input enable. */ - __IOM uint32_t GPIO30OUTCFG : 2; /*!< [26..25] GPIO30 output configuration. */ - __IOM uint32_t GPIO30INTD : 1; /*!< [27..27] GPIO30 interrupt direction. */ - __IOM uint32_t GPIO31INCFG : 1; /*!< [28..28] GPIO31 input enable. */ - __IOM uint32_t GPIO31OUTCFG : 2; /*!< [30..29] GPIO31 output configuration. */ - __IOM uint32_t GPIO31INTD : 1; /*!< [31..31] GPIO31 interrupt direction. */ - } CFGD_b; - } ; - - union { - __IOM uint32_t CFGE; /*!< (@ 0x00000050) GPIO Configuration Register E (Pads 32-39) */ - - struct { - __IOM uint32_t GPIO32INCFG : 1; /*!< [0..0] GPIO32 input enable. */ - __IOM uint32_t GPIO32OUTCFG : 2; /*!< [2..1] GPIO32 output configuration. */ - __IOM uint32_t GPIO32INTD : 1; /*!< [3..3] GPIO32 interrupt direction. */ - __IOM uint32_t GPIO33INCFG : 1; /*!< [4..4] GPIO33 input enable. */ - __IOM uint32_t GPIO33OUTCFG : 2; /*!< [6..5] GPIO33 output configuration. */ - __IOM uint32_t GPIO33INTD : 1; /*!< [7..7] GPIO33 interrupt direction. */ - __IOM uint32_t GPIO34INCFG : 1; /*!< [8..8] GPIO34 input enable. */ - __IOM uint32_t GPIO34OUTCFG : 2; /*!< [10..9] GPIO34 output configuration. */ - __IOM uint32_t GPIO34INTD : 1; /*!< [11..11] GPIO34 interrupt direction. */ - __IOM uint32_t GPIO35INCFG : 1; /*!< [12..12] GPIO35 input enable. */ - __IOM uint32_t GPIO35OUTCFG : 2; /*!< [14..13] GPIO35 output configuration. */ - __IOM uint32_t GPIO35INTD : 1; /*!< [15..15] GPIO35 interrupt direction. */ - __IOM uint32_t GPIO36INCFG : 1; /*!< [16..16] GPIO36 input enable. */ - __IOM uint32_t GPIO36OUTCFG : 2; /*!< [18..17] GPIO36 output configuration. */ - __IOM uint32_t GPIO36INTD : 1; /*!< [19..19] GPIO36 interrupt direction. */ - __IOM uint32_t GPIO37INCFG : 1; /*!< [20..20] GPIO37 input enable. */ - __IOM uint32_t GPIO37OUTCFG : 2; /*!< [22..21] GPIO37 output configuration. */ - __IOM uint32_t GPIO37INTD : 1; /*!< [23..23] GPIO37 interrupt direction. */ - __IOM uint32_t GPIO38INCFG : 1; /*!< [24..24] GPIO38 input enable. */ - __IOM uint32_t GPIO38OUTCFG : 2; /*!< [26..25] GPIO38 output configuration. */ - __IOM uint32_t GPIO38INTD : 1; /*!< [27..27] GPIO38 interrupt direction. */ - __IOM uint32_t GPIO39INCFG : 1; /*!< [28..28] GPIO39 input enable. */ - __IOM uint32_t GPIO39OUTCFG : 2; /*!< [30..29] GPIO39 output configuration. */ - __IOM uint32_t GPIO39INTD : 1; /*!< [31..31] GPIO39 interrupt direction. */ - } CFGE_b; - } ; - - union { - __IOM uint32_t CFGF; /*!< (@ 0x00000054) GPIO Configuration Register F (Pads 40 -47) */ - - struct { - __IOM uint32_t GPIO40INCFG : 1; /*!< [0..0] GPIO40 input enable. */ - __IOM uint32_t GPIO40OUTCFG : 2; /*!< [2..1] GPIO40 output configuration. */ - __IOM uint32_t GPIO40INTD : 1; /*!< [3..3] GPIO40 interrupt direction. */ - __IOM uint32_t GPIO41INCFG : 1; /*!< [4..4] GPIO41 input enable. */ - __IOM uint32_t GPIO41OUTCFG : 2; /*!< [6..5] GPIO41 output configuration. */ - __IOM uint32_t GPIO41INTD : 1; /*!< [7..7] GPIO41 interrupt direction. */ - __IOM uint32_t GPIO42INCFG : 1; /*!< [8..8] GPIO42 input enable. */ - __IOM uint32_t GPIO42OUTCFG : 2; /*!< [10..9] GPIO42 output configuration. */ - __IOM uint32_t GPIO42INTD : 1; /*!< [11..11] GPIO42 interrupt direction. */ - __IOM uint32_t GPIO43INCFG : 1; /*!< [12..12] GPIO43 input enable. */ - __IOM uint32_t GPIO43OUTCFG : 2; /*!< [14..13] GPIO43 output configuration. */ - __IOM uint32_t GPIO43INTD : 1; /*!< [15..15] GPIO43 interrupt direction. */ - __IOM uint32_t GPIO44INCFG : 1; /*!< [16..16] GPIO44 input enable. */ - __IOM uint32_t GPIO44OUTCFG : 2; /*!< [18..17] GPIO44 output configuration. */ - __IOM uint32_t GPIO44INTD : 1; /*!< [19..19] GPIO44 interrupt direction. */ - __IOM uint32_t GPIO45INCFG : 1; /*!< [20..20] GPIO45 input enable. */ - __IOM uint32_t GPIO45OUTCFG : 2; /*!< [22..21] GPIO45 output configuration. */ - __IOM uint32_t GPIO45INTD : 1; /*!< [23..23] GPIO45 interrupt direction. */ - __IOM uint32_t GPIO46INCFG : 1; /*!< [24..24] GPIO46 input enable. */ - __IOM uint32_t GPIO46OUTCFG : 2; /*!< [26..25] GPIO46 output configuration. */ - __IOM uint32_t GPIO46INTD : 1; /*!< [27..27] GPIO46 interrupt direction. */ - __IOM uint32_t GPIO47INCFG : 1; /*!< [28..28] GPIO47 input enable. */ - __IOM uint32_t GPIO47OUTCFG : 2; /*!< [30..29] GPIO47 output configuration. */ - __IOM uint32_t GPIO47INTD : 1; /*!< [31..31] GPIO47 interrupt direction. */ - } CFGF_b; - } ; - - union { - __IOM uint32_t CFGG; /*!< (@ 0x00000058) GPIO Configuration Register G (Pads 48-49) */ - - struct { - __IOM uint32_t GPIO48INCFG : 1; /*!< [0..0] GPIO48 input enable. */ - __IOM uint32_t GPIO48OUTCFG : 2; /*!< [2..1] GPIO48 output configuration. */ - __IOM uint32_t GPIO48INTD : 1; /*!< [3..3] GPIO48 interrupt direction. */ - __IOM uint32_t GPIO49INCFG : 1; /*!< [4..4] GPIO49 input enable. */ - __IOM uint32_t GPIO49OUTCFG : 2; /*!< [6..5] GPIO49 output configuration. */ - __IOM uint32_t GPIO49INTD : 1; /*!< [7..7] GPIO49 interrupt direction. */ - } CFGG_b; - } ; - __IM uint32_t RESERVED1; - - union { - __IOM uint32_t PADKEY; /*!< (@ 0x00000060) Key Register for all pad configuration registers */ - - struct { - __IOM uint32_t PADKEY : 32; /*!< [31..0] Key register value. */ - } PADKEY_b; - } ; - __IM uint32_t RESERVED2[7]; - - union { - __IOM uint32_t RDA; /*!< (@ 0x00000080) GPIO Input Register A */ - - struct { - __IOM uint32_t RDA : 32; /*!< [31..0] GPIO31-0 read data. */ - } RDA_b; - } ; - - union { - __IOM uint32_t RDB; /*!< (@ 0x00000084) GPIO Input Register B */ - - struct { - __IOM uint32_t RDB : 18; /*!< [17..0] GPIO49-32 read data. */ - } RDB_b; - } ; - - union { - __IOM uint32_t WTA; /*!< (@ 0x00000088) GPIO Output Register A */ - - struct { - __IOM uint32_t WTA : 32; /*!< [31..0] GPIO31-0 write data. */ - } WTA_b; - } ; - - union { - __IOM uint32_t WTB; /*!< (@ 0x0000008C) GPIO Output Register B */ - - struct { - __IOM uint32_t WTB : 18; /*!< [17..0] GPIO49-32 write data. */ - } WTB_b; - } ; - - union { - __IOM uint32_t WTSA; /*!< (@ 0x00000090) GPIO Output Register A Set */ - - struct { - __IOM uint32_t WTSA : 32; /*!< [31..0] Set the GPIO31-0 write data. */ - } WTSA_b; - } ; - - union { - __IOM uint32_t WTSB; /*!< (@ 0x00000094) GPIO Output Register B Set */ - - struct { - __IOM uint32_t WTSB : 18; /*!< [17..0] Set the GPIO49-32 write data. */ - } WTSB_b; - } ; - - union { - __IOM uint32_t WTCA; /*!< (@ 0x00000098) GPIO Output Register A Clear */ - - struct { - __IOM uint32_t WTCA : 32; /*!< [31..0] Clear the GPIO31-0 write data. */ - } WTCA_b; - } ; - - union { - __IOM uint32_t WTCB; /*!< (@ 0x0000009C) GPIO Output Register B Clear */ - - struct { - __IOM uint32_t WTCB : 18; /*!< [17..0] Clear the GPIO49-32 write data. */ - } WTCB_b; - } ; - - union { - __IOM uint32_t ENA; /*!< (@ 0x000000A0) GPIO Enable Register A */ - - struct { - __IOM uint32_t ENA : 32; /*!< [31..0] GPIO31-0 output enables */ - } ENA_b; - } ; - - union { - __IOM uint32_t ENB; /*!< (@ 0x000000A4) GPIO Enable Register B */ - - struct { - __IOM uint32_t ENB : 18; /*!< [17..0] GPIO49-32 output enables */ - } ENB_b; - } ; - - union { - __IOM uint32_t ENSA; /*!< (@ 0x000000A8) GPIO Enable Register A Set */ - - struct { - __IOM uint32_t ENSA : 32; /*!< [31..0] Set the GPIO31-0 output enables */ - } ENSA_b; - } ; - - union { - __IOM uint32_t ENSB; /*!< (@ 0x000000AC) GPIO Enable Register B Set */ - - struct { - __IOM uint32_t ENSB : 18; /*!< [17..0] Set the GPIO49-32 output enables */ - } ENSB_b; - } ; - __IM uint32_t RESERVED3; - - union { - __IOM uint32_t ENCA; /*!< (@ 0x000000B4) GPIO Enable Register A Clear */ - - struct { - __IOM uint32_t ENCA : 32; /*!< [31..0] Clear the GPIO31-0 output enables */ - } ENCA_b; - } ; - - union { - __IOM uint32_t ENCB; /*!< (@ 0x000000B8) GPIO Enable Register B Clear */ - - struct { - __IOM uint32_t ENCB : 18; /*!< [17..0] Clear the GPIO49-32 output enables */ - } ENCB_b; - } ; - - union { - __IOM uint32_t STMRCAP; /*!< (@ 0x000000BC) STIMER Capture Control */ - - struct { - __IOM uint32_t STSEL0 : 6; /*!< [5..0] STIMER Capture 0 Select. */ - __IOM uint32_t STPOL0 : 1; /*!< [6..6] STIMER Capture 0 Polarity. */ - __IM uint32_t : 1; - __IOM uint32_t STSEL1 : 6; /*!< [13..8] STIMER Capture 1 Select. */ - __IOM uint32_t STPOL1 : 1; /*!< [14..14] STIMER Capture 1 Polarity. */ - __IM uint32_t : 1; - __IOM uint32_t STSEL2 : 6; /*!< [21..16] STIMER Capture 2 Select. */ - __IOM uint32_t STPOL2 : 1; /*!< [22..22] STIMER Capture 2 Polarity. */ - __IM uint32_t : 1; - __IOM uint32_t STSEL3 : 6; /*!< [29..24] STIMER Capture 3 Select. */ - __IOM uint32_t STPOL3 : 1; /*!< [30..30] STIMER Capture 3 Polarity. */ - } STMRCAP_b; - } ; - - union { - __IOM uint32_t IOM0IRQ; /*!< (@ 0x000000C0) IOM0 Flow Control IRQ Select */ - - struct { - __IOM uint32_t IOM0IRQ : 6; /*!< [5..0] IOMSTR0 IRQ pad select. */ - } IOM0IRQ_b; - } ; - - union { - __IOM uint32_t IOM1IRQ; /*!< (@ 0x000000C4) IOM1 Flow Control IRQ Select */ - - struct { - __IOM uint32_t IOM1IRQ : 6; /*!< [5..0] IOMSTR1 IRQ pad select. */ - } IOM1IRQ_b; - } ; - - union { - __IOM uint32_t IOM2IRQ; /*!< (@ 0x000000C8) IOM2 Flow Control IRQ Select */ - - struct { - __IOM uint32_t IOM2IRQ : 6; /*!< [5..0] IOMSTR2 IRQ pad select. */ - } IOM2IRQ_b; - } ; - - union { - __IOM uint32_t IOM3IRQ; /*!< (@ 0x000000CC) IOM3 Flow Control IRQ Select */ - - struct { - __IOM uint32_t IOM3IRQ : 6; /*!< [5..0] IOMSTR3 IRQ pad select. */ - } IOM3IRQ_b; - } ; - - union { - __IOM uint32_t IOM4IRQ; /*!< (@ 0x000000D0) IOM4 Flow Control IRQ Select */ - - struct { - __IOM uint32_t IOM4IRQ : 6; /*!< [5..0] IOMSTR4 IRQ pad select. */ - } IOM4IRQ_b; - } ; - - union { - __IOM uint32_t IOM5IRQ; /*!< (@ 0x000000D4) IOM5 Flow Control IRQ Select */ - - struct { - __IOM uint32_t IOM5IRQ : 6; /*!< [5..0] IOMSTR5 IRQ pad select. */ - } IOM5IRQ_b; - } ; - - union { - __IOM uint32_t BLEIFIRQ; /*!< (@ 0x000000D8) BLEIF Flow Control IRQ Select */ - - struct { - __IOM uint32_t BLEIFIRQ : 6; /*!< [5..0] BLEIF IRQ pad select. */ - } BLEIFIRQ_b; - } ; - - union { - __IOM uint32_t GPIOOBS; /*!< (@ 0x000000DC) GPIO Observation Mode Sample register */ - - struct { - __IOM uint32_t OBS_DATA : 16; /*!< [15..0] Sample of the data output on the GPIO observation port. - May have async sampling issues, as the data is not synronized - to the read operation. Intended for debug purposes only */ - } GPIOOBS_b; - } ; - - union { - __IOM uint32_t ALTPADCFGA; /*!< (@ 0x000000E0) Alternate Pad Configuration reg0 (Pads 3,2,1,0) */ - - struct { - __IOM uint32_t PAD0_DS1 : 1; /*!< [0..0] Pad 0 high order drive strength selection. Used in conjunction - with PAD0STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD0_SR : 1; /*!< [4..4] Pad 0 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD1_DS1 : 1; /*!< [8..8] Pad 1 high order drive strength selection. Used in conjunction - with PAD1STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD1_SR : 1; /*!< [12..12] Pad 1 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD2_DS1 : 1; /*!< [16..16] Pad 2 high order drive strength selection. Used in - conjunction with PAD2STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD2_SR : 1; /*!< [20..20] Pad 2 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD3_DS1 : 1; /*!< [24..24] Pad 3 high order drive strength selection. Used in - conjunction with PAD3STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD3_SR : 1; /*!< [28..28] Pad 3 slew rate selection. */ - } ALTPADCFGA_b; - } ; - - union { - __IOM uint32_t ALTPADCFGB; /*!< (@ 0x000000E4) Alternate Pad Configuration reg1 (Pads 7,6,5,4) */ - - struct { - __IOM uint32_t PAD4_DS1 : 1; /*!< [0..0] Pad 4 high order drive strength selection. Used in conjunction - with PAD4STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD4_SR : 1; /*!< [4..4] Pad 4 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD5_DS1 : 1; /*!< [8..8] Pad 5 high order drive strength selection. Used in conjunction - with PAD5STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD5_SR : 1; /*!< [12..12] Pad 5 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD6_DS1 : 1; /*!< [16..16] Pad 6 high order drive strength selection. Used in - conjunction with PAD6STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD6_SR : 1; /*!< [20..20] Pad 6 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD7_DS1 : 1; /*!< [24..24] Pad 7 high order drive strength selection. Used in - conjunction with PAD7STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD7_SR : 1; /*!< [28..28] Pad 7 slew rate selection. */ - } ALTPADCFGB_b; - } ; - - union { - __IOM uint32_t ALTPADCFGC; /*!< (@ 0x000000E8) Alternate Pad Configuration reg2 (Pads 11,10,9,8) */ - - struct { - __IOM uint32_t PAD8_DS1 : 1; /*!< [0..0] Pad 8 high order drive strength selection. Used in conjunction - with PAD8STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD8_SR : 1; /*!< [4..4] Pad 8 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD9_DS1 : 1; /*!< [8..8] Pad 9 high order drive strength selection. Used in conjunction - with PAD9STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD9_SR : 1; /*!< [12..12] Pad 9 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD10_DS1 : 1; /*!< [16..16] Pad 10 high order drive strength selection. Used in - conjunction with PAD10STRNG field to set the pad drive - strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD10_SR : 1; /*!< [20..20] Pad 10 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD11_DS1 : 1; /*!< [24..24] Pad 11 high order drive strength selection. Used in - conjunction with PAD11STRNG field to set the pad drive - strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD11_SR : 1; /*!< [28..28] Pad 11 slew rate selection. */ - } ALTPADCFGC_b; - } ; - - union { - __IOM uint32_t ALTPADCFGD; /*!< (@ 0x000000EC) Alternate Pad Configuration reg3 (Pads 15,14,13,12) */ - - struct { - __IOM uint32_t PAD12_DS1 : 1; /*!< [0..0] Pad 12 high order drive strength selection. Used in conjunction - with PAD12STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD12_SR : 1; /*!< [4..4] Pad 12 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD13_DS1 : 1; /*!< [8..8] Pad 13 high order drive strength selection. Used in conjunction - with PAD13STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD13_SR : 1; /*!< [12..12] Pad 13 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD14_DS1 : 1; /*!< [16..16] Pad 14 high order drive strength selection. Used in - conjunction with PAD14STRNG field to set the pad drive - strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD14_SR : 1; /*!< [20..20] Pad 14 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD15_DS1 : 1; /*!< [24..24] Pad 15 high order drive strength selection. Used in - conjunction with PAD15STRNG field to set the pad drive - strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD15_SR : 1; /*!< [28..28] Pad 15 slew rate selection. */ - } ALTPADCFGD_b; - } ; - - union { - __IOM uint32_t ALTPADCFGE; /*!< (@ 0x000000F0) Alternate Pad Configuration reg4 (Pads 19,18,17,16) */ - - struct { - __IOM uint32_t PAD16_DS1 : 1; /*!< [0..0] Pad 16 high order drive strength selection. Used in conjunction - with PAD16STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD16_SR : 1; /*!< [4..4] Pad 16 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD17_DS1 : 1; /*!< [8..8] Pad 17 high order drive strength selection. Used in conjunction - with PAD17STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD17_SR : 1; /*!< [12..12] Pad 17 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD18_DS1 : 1; /*!< [16..16] Pad 18 high order drive strength selection. Used in - conjunction with PAD18STRNG field to set the pad drive - strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD18_SR : 1; /*!< [20..20] Pad 18 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD19_DS1 : 1; /*!< [24..24] Pad 19 high order drive strength selection. Used in - conjunction with PAD19STRNG field to set the pad drive - strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD19_SR : 1; /*!< [28..28] Pad 19 slew rate selection. */ - } ALTPADCFGE_b; - } ; - - union { - __IOM uint32_t ALTPADCFGF; /*!< (@ 0x000000F4) Alternate Pad Configuration reg5 (Pads 23,22,21,20) */ - - struct { - __IOM uint32_t PAD20_DS1 : 1; /*!< [0..0] Pad 20 high order drive strength selection. Used in conjunction - with PAD20STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD20_SR : 1; /*!< [4..4] Pad 20 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD21_DS1 : 1; /*!< [8..8] Pad 21 high order drive strength selection. Used in conjunction - with PAD21STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD21_SR : 1; /*!< [12..12] Pad 21 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD22_DS1 : 1; /*!< [16..16] Pad 22 high order drive strength selection. Used in - conjunction with PAD22STRNG field to set the pad drive - strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD22_SR : 1; /*!< [20..20] Pad 22 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD23_DS1 : 1; /*!< [24..24] Pad 23 high order drive strength selection. Used in - conjunction with PAD23STRNG field to set the pad drive - strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD23_SR : 1; /*!< [28..28] Pad 23 slew rate selection. */ - } ALTPADCFGF_b; - } ; - - union { - __IOM uint32_t ALTPADCFGG; /*!< (@ 0x000000F8) Alternate Pad Configuration reg6 (Pads 27,26,25,24) */ - - struct { - __IOM uint32_t PAD24_DS1 : 1; /*!< [0..0] Pad 24 high order drive strength selection. Used in conjunction - with PAD24STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD24_SR : 1; /*!< [4..4] Pad 24 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD25_DS1 : 1; /*!< [8..8] Pad 25 high order drive strength selection. Used in conjunction - with PAD25STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD25_SR : 1; /*!< [12..12] Pad 25 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD26_DS1 : 1; /*!< [16..16] Pad 26 high order drive strength selection. Used in - conjunction with PAD26STRNG field to set the pad drive - strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD26_SR : 1; /*!< [20..20] Pad 26 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD27_DS1 : 1; /*!< [24..24] Pad 27 high order drive strength selection. Used in - conjunction with PAD27STRNG field to set the pad drive - strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD27_SR : 1; /*!< [28..28] Pad 27 slew rate selection. */ - } ALTPADCFGG_b; - } ; - - union { - __IOM uint32_t ALTPADCFGH; /*!< (@ 0x000000FC) Alternate Pad Configuration reg7 (Pads 31,30,29,28) */ - - struct { - __IOM uint32_t PAD28_DS1 : 1; /*!< [0..0] Pad 28 high order drive strength selection. Used in conjunction - with PAD28STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD28_SR : 1; /*!< [4..4] Pad 28 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD29_DS1 : 1; /*!< [8..8] Pad 29 high order drive strength selection. Used in conjunction - with PAD29STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD29_SR : 1; /*!< [12..12] Pad 29 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD30_DS1 : 1; /*!< [16..16] Pad 30 high order drive strength selection. Used in - conjunction with PAD30STRNG field to set the pad drive - strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD30_SR : 1; /*!< [20..20] Pad 30 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD31_DS1 : 1; /*!< [24..24] Pad 31 high order drive strength selection. Used in - conjunction with PAD31STRNG field to set the pad drive - strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD31_SR : 1; /*!< [28..28] Pad 31 slew rate selection. */ - } ALTPADCFGH_b; - } ; - - union { - __IOM uint32_t ALTPADCFGI; /*!< (@ 0x00000100) Alternate Pad Configuration reg8 (Pads 35,34,33,32) */ - - struct { - __IOM uint32_t PAD32_DS1 : 1; /*!< [0..0] Pad 32 high order drive strength selection. Used in conjunction - with PAD32STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD32_SR : 1; /*!< [4..4] Pad 32 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD33_DS1 : 1; /*!< [8..8] Pad 33 high order drive strength selection. Used in conjunction - with PAD33STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD33_SR : 1; /*!< [12..12] Pad 33 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD34_DS1 : 1; /*!< [16..16] Pad 34 high order drive strength selection. Used in - conjunction with PAD34STRNG field to set the pad drive - strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD34_SR : 1; /*!< [20..20] Pad 34 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD35_DS1 : 1; /*!< [24..24] Pad 35 high order drive strength selection. Used in - conjunction with PAD35STRNG field to set the pad drive - strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD35_SR : 1; /*!< [28..28] Pad 35 slew rate selection. */ - } ALTPADCFGI_b; - } ; - - union { - __IOM uint32_t ALTPADCFGJ; /*!< (@ 0x00000104) Alternate Pad Configuration reg9 (Pads 39,38,37,36) */ - - struct { - __IOM uint32_t PAD36_DS1 : 1; /*!< [0..0] Pad 36 high order drive strength selection. Used in conjunction - with PAD36STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD36_SR : 1; /*!< [4..4] Pad 36 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD37_DS1 : 1; /*!< [8..8] Pad 37 high order drive strength selection. Used in conjunction - with PAD37STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD37_SR : 1; /*!< [12..12] Pad 37 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD38_DS1 : 1; /*!< [16..16] Pad 38 high order drive strength selection. Used in - conjunction with PAD38STRNG field to set the pad drive - strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD38_SR : 1; /*!< [20..20] Pad 38 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD39_DS1 : 1; /*!< [24..24] Pad 39 high order drive strength selection. Used in - conjunction with PAD39STRNG field to set the pad drive - strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD39_SR : 1; /*!< [28..28] Pad 39 slew rate selection. */ - } ALTPADCFGJ_b; - } ; - - union { - __IOM uint32_t ALTPADCFGK; /*!< (@ 0x00000108) Alternate Pad Configuration reg10 (Pads 43,42,41,40) */ - - struct { - __IOM uint32_t PAD40_DS1 : 1; /*!< [0..0] Pad 40 high order drive strength selection. Used in conjunction - with PAD40STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD40_SR : 1; /*!< [4..4] Pad 40 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD41_DS1 : 1; /*!< [8..8] Pad 41 high order drive strength selection. Used in conjunction - with PAD41STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD41_SR : 1; /*!< [12..12] Pad 41 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD42_DS1 : 1; /*!< [16..16] Pad 42 high order drive strength selection. Used in - conjunction with PAD42STRNG field to set the pad drive - strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD42_SR : 1; /*!< [20..20] Pad 42 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD43_DS1 : 1; /*!< [24..24] Pad 43 high order drive strength selection. Used in - conjunction with PAD43STRNG field to set the pad drive - strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD43_SR : 1; /*!< [28..28] Pad 43 slew rate selection. */ - } ALTPADCFGK_b; - } ; - - union { - __IOM uint32_t ALTPADCFGL; /*!< (@ 0x0000010C) Alternate Pad Configuration reg11 (Pads 47,46,45,44) */ - - struct { - __IOM uint32_t PAD44_DS1 : 1; /*!< [0..0] Pad 44 high order drive strength selection. Used in conjunction - with PAD44STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD44_SR : 1; /*!< [4..4] Pad 44 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD45_DS1 : 1; /*!< [8..8] Pad 45 high order drive strength selection. Used in conjunction - with PAD45STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD45_SR : 1; /*!< [12..12] Pad 45 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD46_DS1 : 1; /*!< [16..16] Pad 46 high order drive strength selection. Used in - conjunction with PAD46STRNG field to set the pad drive - strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD46_SR : 1; /*!< [20..20] Pad 46 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD47_DS1 : 1; /*!< [24..24] Pad 47 high order drive strength selection. Used in - conjunction with PAD47STRNG field to set the pad drive - strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD47_SR : 1; /*!< [28..28] Pad 47 slew rate selection. */ - } ALTPADCFGL_b; - } ; - - union { - __IOM uint32_t ALTPADCFGM; /*!< (@ 0x00000110) Alternate Pad Configuration reg12 (Pads 49,48) */ - - struct { - __IOM uint32_t PAD48_DS1 : 1; /*!< [0..0] Pad 48 high order drive strength selection. Used in conjunction - with PAD48STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD48_SR : 1; /*!< [4..4] Pad 48 slew rate selection. */ - __IM uint32_t : 3; - __IOM uint32_t PAD49_DS1 : 1; /*!< [8..8] Pad 49 high order drive strength selection. Used in conjunction - with PAD49STRNG field to set the pad drive strength. */ - __IM uint32_t : 3; - __IOM uint32_t PAD49_SR : 1; /*!< [12..12] Pad 49 slew rate selection. */ - } ALTPADCFGM_b; - } ; - - union { - __IOM uint32_t SCDET; /*!< (@ 0x00000114) SCARD Card Detect select */ - - struct { - __IOM uint32_t SCDET : 6; /*!< [5..0] SCARD card detect pad select. */ - } SCDET_b; - } ; - - union { - __IOM uint32_t CTENCFG; /*!< (@ 0x00000118) Counter/Timer Enable Config */ - - struct { - __IOM uint32_t EN0 : 1; /*!< [0..0] CT0 Enable */ - __IOM uint32_t EN1 : 1; /*!< [1..1] CT1 Enable */ - __IOM uint32_t EN2 : 1; /*!< [2..2] CT2 Enable */ - __IOM uint32_t EN3 : 1; /*!< [3..3] CT3 Enable */ - __IOM uint32_t EN4 : 1; /*!< [4..4] CT4 Enable */ - __IOM uint32_t EN5 : 1; /*!< [5..5] CT5 Enable */ - __IOM uint32_t EN6 : 1; /*!< [6..6] CT6 Enable */ - __IOM uint32_t EN7 : 1; /*!< [7..7] CT7 Enable */ - __IOM uint32_t EN8 : 1; /*!< [8..8] CT8 Enable */ - __IOM uint32_t EN9 : 1; /*!< [9..9] CT9 Enable */ - __IOM uint32_t EN10 : 1; /*!< [10..10] CT10 Enable */ - __IOM uint32_t EN11 : 1; /*!< [11..11] CT11 Enable */ - __IOM uint32_t EN12 : 1; /*!< [12..12] CT12 Enable */ - __IOM uint32_t EN13 : 1; /*!< [13..13] CT13 Enable */ - __IOM uint32_t EN14 : 1; /*!< [14..14] CT14 Enable */ - __IOM uint32_t EN15 : 1; /*!< [15..15] CT15 Enable */ - __IOM uint32_t EN16 : 1; /*!< [16..16] CT16 Enable */ - __IOM uint32_t EN17 : 1; /*!< [17..17] CT17 Enable */ - __IOM uint32_t EN18 : 1; /*!< [18..18] CT18 Enable */ - __IOM uint32_t EN19 : 1; /*!< [19..19] CT19 Enable */ - __IOM uint32_t EN20 : 1; /*!< [20..20] CT20 Enable */ - __IOM uint32_t EN21 : 1; /*!< [21..21] CT21 Enable */ - __IOM uint32_t EN22 : 1; /*!< [22..22] CT22 Enable */ - __IOM uint32_t EN23 : 1; /*!< [23..23] CT23 Enable */ - __IOM uint32_t EN24 : 1; /*!< [24..24] CT24 Enable */ - __IOM uint32_t EN25 : 1; /*!< [25..25] CT25 Enable */ - __IOM uint32_t EN26 : 1; /*!< [26..26] CT26 Enable */ - __IOM uint32_t EN27 : 1; /*!< [27..27] CT27 Enable */ - __IOM uint32_t EN28 : 1; /*!< [28..28] CT28 Enable */ - __IOM uint32_t EN29 : 1; /*!< [29..29] CT29 Enable */ - __IOM uint32_t EN30 : 1; /*!< [30..30] CT30 Enable */ - __IOM uint32_t EN31 : 1; /*!< [31..31] CT31 Enable */ - } CTENCFG_b; - } ; - __IM uint32_t RESERVED4[57]; - - union { - __IOM uint32_t INT0EN; /*!< (@ 0x00000200) GPIO Interrupt Registers 31-0: Enable */ - - struct { - __IOM uint32_t GPIO0 : 1; /*!< [0..0] GPIO0 interrupt. */ - __IOM uint32_t GPIO1 : 1; /*!< [1..1] GPIO1 interrupt. */ - __IOM uint32_t GPIO2 : 1; /*!< [2..2] GPIO2 interrupt. */ - __IOM uint32_t GPIO3 : 1; /*!< [3..3] GPIO3 interrupt. */ - __IOM uint32_t GPIO4 : 1; /*!< [4..4] GPIO4 interrupt. */ - __IOM uint32_t GPIO5 : 1; /*!< [5..5] GPIO5 interrupt. */ - __IOM uint32_t GPIO6 : 1; /*!< [6..6] GPIO6 interrupt. */ - __IOM uint32_t GPIO7 : 1; /*!< [7..7] GPIO7 interrupt. */ - __IOM uint32_t GPIO8 : 1; /*!< [8..8] GPIO8 interrupt. */ - __IOM uint32_t GPIO9 : 1; /*!< [9..9] GPIO9 interrupt. */ - __IOM uint32_t GPIO10 : 1; /*!< [10..10] GPIO10 interrupt. */ - __IOM uint32_t GPIO11 : 1; /*!< [11..11] GPIO11 interrupt. */ - __IOM uint32_t GPIO12 : 1; /*!< [12..12] GPIO12 interrupt. */ - __IOM uint32_t GPIO13 : 1; /*!< [13..13] GPIO13 interrupt. */ - __IOM uint32_t GPIO14 : 1; /*!< [14..14] GPIO14 interrupt. */ - __IOM uint32_t GPIO15 : 1; /*!< [15..15] GPIO15 interrupt. */ - __IOM uint32_t GPIO16 : 1; /*!< [16..16] GPIO16 interrupt. */ - __IOM uint32_t GPIO17 : 1; /*!< [17..17] GPIO17 interrupt. */ - __IOM uint32_t GPIO18 : 1; /*!< [18..18] GPIO18interrupt. */ - __IOM uint32_t GPIO19 : 1; /*!< [19..19] GPIO19 interrupt. */ - __IOM uint32_t GPIO20 : 1; /*!< [20..20] GPIO20 interrupt. */ - __IOM uint32_t GPIO21 : 1; /*!< [21..21] GPIO21 interrupt. */ - __IOM uint32_t GPIO22 : 1; /*!< [22..22] GPIO22 interrupt. */ - __IOM uint32_t GPIO23 : 1; /*!< [23..23] GPIO23 interrupt. */ - __IOM uint32_t GPIO24 : 1; /*!< [24..24] GPIO24 interrupt. */ - __IOM uint32_t GPIO25 : 1; /*!< [25..25] GPIO25 interrupt. */ - __IOM uint32_t GPIO26 : 1; /*!< [26..26] GPIO26 interrupt. */ - __IOM uint32_t GPIO27 : 1; /*!< [27..27] GPIO27 interrupt. */ - __IOM uint32_t GPIO28 : 1; /*!< [28..28] GPIO28 interrupt. */ - __IOM uint32_t GPIO29 : 1; /*!< [29..29] GPIO29 interrupt. */ - __IOM uint32_t GPIO30 : 1; /*!< [30..30] GPIO30 interrupt. */ - __IOM uint32_t GPIO31 : 1; /*!< [31..31] GPIO31 interrupt. */ - } INT0EN_b; - } ; - - union { - __IOM uint32_t INT0STAT; /*!< (@ 0x00000204) GPIO Interrupt Registers 31-0: Status */ - - struct { - __IOM uint32_t GPIO0 : 1; /*!< [0..0] GPIO0 interrupt. */ - __IOM uint32_t GPIO1 : 1; /*!< [1..1] GPIO1 interrupt. */ - __IOM uint32_t GPIO2 : 1; /*!< [2..2] GPIO2 interrupt. */ - __IOM uint32_t GPIO3 : 1; /*!< [3..3] GPIO3 interrupt. */ - __IOM uint32_t GPIO4 : 1; /*!< [4..4] GPIO4 interrupt. */ - __IOM uint32_t GPIO5 : 1; /*!< [5..5] GPIO5 interrupt. */ - __IOM uint32_t GPIO6 : 1; /*!< [6..6] GPIO6 interrupt. */ - __IOM uint32_t GPIO7 : 1; /*!< [7..7] GPIO7 interrupt. */ - __IOM uint32_t GPIO8 : 1; /*!< [8..8] GPIO8 interrupt. */ - __IOM uint32_t GPIO9 : 1; /*!< [9..9] GPIO9 interrupt. */ - __IOM uint32_t GPIO10 : 1; /*!< [10..10] GPIO10 interrupt. */ - __IOM uint32_t GPIO11 : 1; /*!< [11..11] GPIO11 interrupt. */ - __IOM uint32_t GPIO12 : 1; /*!< [12..12] GPIO12 interrupt. */ - __IOM uint32_t GPIO13 : 1; /*!< [13..13] GPIO13 interrupt. */ - __IOM uint32_t GPIO14 : 1; /*!< [14..14] GPIO14 interrupt. */ - __IOM uint32_t GPIO15 : 1; /*!< [15..15] GPIO15 interrupt. */ - __IOM uint32_t GPIO16 : 1; /*!< [16..16] GPIO16 interrupt. */ - __IOM uint32_t GPIO17 : 1; /*!< [17..17] GPIO17 interrupt. */ - __IOM uint32_t GPIO18 : 1; /*!< [18..18] GPIO18interrupt. */ - __IOM uint32_t GPIO19 : 1; /*!< [19..19] GPIO19 interrupt. */ - __IOM uint32_t GPIO20 : 1; /*!< [20..20] GPIO20 interrupt. */ - __IOM uint32_t GPIO21 : 1; /*!< [21..21] GPIO21 interrupt. */ - __IOM uint32_t GPIO22 : 1; /*!< [22..22] GPIO22 interrupt. */ - __IOM uint32_t GPIO23 : 1; /*!< [23..23] GPIO23 interrupt. */ - __IOM uint32_t GPIO24 : 1; /*!< [24..24] GPIO24 interrupt. */ - __IOM uint32_t GPIO25 : 1; /*!< [25..25] GPIO25 interrupt. */ - __IOM uint32_t GPIO26 : 1; /*!< [26..26] GPIO26 interrupt. */ - __IOM uint32_t GPIO27 : 1; /*!< [27..27] GPIO27 interrupt. */ - __IOM uint32_t GPIO28 : 1; /*!< [28..28] GPIO28 interrupt. */ - __IOM uint32_t GPIO29 : 1; /*!< [29..29] GPIO29 interrupt. */ - __IOM uint32_t GPIO30 : 1; /*!< [30..30] GPIO30 interrupt. */ - __IOM uint32_t GPIO31 : 1; /*!< [31..31] GPIO31 interrupt. */ - } INT0STAT_b; - } ; - - union { - __IOM uint32_t INT0CLR; /*!< (@ 0x00000208) GPIO Interrupt Registers 31-0: Clear */ - - struct { - __IOM uint32_t GPIO0 : 1; /*!< [0..0] GPIO0 interrupt. */ - __IOM uint32_t GPIO1 : 1; /*!< [1..1] GPIO1 interrupt. */ - __IOM uint32_t GPIO2 : 1; /*!< [2..2] GPIO2 interrupt. */ - __IOM uint32_t GPIO3 : 1; /*!< [3..3] GPIO3 interrupt. */ - __IOM uint32_t GPIO4 : 1; /*!< [4..4] GPIO4 interrupt. */ - __IOM uint32_t GPIO5 : 1; /*!< [5..5] GPIO5 interrupt. */ - __IOM uint32_t GPIO6 : 1; /*!< [6..6] GPIO6 interrupt. */ - __IOM uint32_t GPIO7 : 1; /*!< [7..7] GPIO7 interrupt. */ - __IOM uint32_t GPIO8 : 1; /*!< [8..8] GPIO8 interrupt. */ - __IOM uint32_t GPIO9 : 1; /*!< [9..9] GPIO9 interrupt. */ - __IOM uint32_t GPIO10 : 1; /*!< [10..10] GPIO10 interrupt. */ - __IOM uint32_t GPIO11 : 1; /*!< [11..11] GPIO11 interrupt. */ - __IOM uint32_t GPIO12 : 1; /*!< [12..12] GPIO12 interrupt. */ - __IOM uint32_t GPIO13 : 1; /*!< [13..13] GPIO13 interrupt. */ - __IOM uint32_t GPIO14 : 1; /*!< [14..14] GPIO14 interrupt. */ - __IOM uint32_t GPIO15 : 1; /*!< [15..15] GPIO15 interrupt. */ - __IOM uint32_t GPIO16 : 1; /*!< [16..16] GPIO16 interrupt. */ - __IOM uint32_t GPIO17 : 1; /*!< [17..17] GPIO17 interrupt. */ - __IOM uint32_t GPIO18 : 1; /*!< [18..18] GPIO18interrupt. */ - __IOM uint32_t GPIO19 : 1; /*!< [19..19] GPIO19 interrupt. */ - __IOM uint32_t GPIO20 : 1; /*!< [20..20] GPIO20 interrupt. */ - __IOM uint32_t GPIO21 : 1; /*!< [21..21] GPIO21 interrupt. */ - __IOM uint32_t GPIO22 : 1; /*!< [22..22] GPIO22 interrupt. */ - __IOM uint32_t GPIO23 : 1; /*!< [23..23] GPIO23 interrupt. */ - __IOM uint32_t GPIO24 : 1; /*!< [24..24] GPIO24 interrupt. */ - __IOM uint32_t GPIO25 : 1; /*!< [25..25] GPIO25 interrupt. */ - __IOM uint32_t GPIO26 : 1; /*!< [26..26] GPIO26 interrupt. */ - __IOM uint32_t GPIO27 : 1; /*!< [27..27] GPIO27 interrupt. */ - __IOM uint32_t GPIO28 : 1; /*!< [28..28] GPIO28 interrupt. */ - __IOM uint32_t GPIO29 : 1; /*!< [29..29] GPIO29 interrupt. */ - __IOM uint32_t GPIO30 : 1; /*!< [30..30] GPIO30 interrupt. */ - __IOM uint32_t GPIO31 : 1; /*!< [31..31] GPIO31 interrupt. */ - } INT0CLR_b; - } ; - - union { - __IOM uint32_t INT0SET; /*!< (@ 0x0000020C) GPIO Interrupt Registers 31-0: Set */ - - struct { - __IOM uint32_t GPIO0 : 1; /*!< [0..0] GPIO0 interrupt. */ - __IOM uint32_t GPIO1 : 1; /*!< [1..1] GPIO1 interrupt. */ - __IOM uint32_t GPIO2 : 1; /*!< [2..2] GPIO2 interrupt. */ - __IOM uint32_t GPIO3 : 1; /*!< [3..3] GPIO3 interrupt. */ - __IOM uint32_t GPIO4 : 1; /*!< [4..4] GPIO4 interrupt. */ - __IOM uint32_t GPIO5 : 1; /*!< [5..5] GPIO5 interrupt. */ - __IOM uint32_t GPIO6 : 1; /*!< [6..6] GPIO6 interrupt. */ - __IOM uint32_t GPIO7 : 1; /*!< [7..7] GPIO7 interrupt. */ - __IOM uint32_t GPIO8 : 1; /*!< [8..8] GPIO8 interrupt. */ - __IOM uint32_t GPIO9 : 1; /*!< [9..9] GPIO9 interrupt. */ - __IOM uint32_t GPIO10 : 1; /*!< [10..10] GPIO10 interrupt. */ - __IOM uint32_t GPIO11 : 1; /*!< [11..11] GPIO11 interrupt. */ - __IOM uint32_t GPIO12 : 1; /*!< [12..12] GPIO12 interrupt. */ - __IOM uint32_t GPIO13 : 1; /*!< [13..13] GPIO13 interrupt. */ - __IOM uint32_t GPIO14 : 1; /*!< [14..14] GPIO14 interrupt. */ - __IOM uint32_t GPIO15 : 1; /*!< [15..15] GPIO15 interrupt. */ - __IOM uint32_t GPIO16 : 1; /*!< [16..16] GPIO16 interrupt. */ - __IOM uint32_t GPIO17 : 1; /*!< [17..17] GPIO17 interrupt. */ - __IOM uint32_t GPIO18 : 1; /*!< [18..18] GPIO18interrupt. */ - __IOM uint32_t GPIO19 : 1; /*!< [19..19] GPIO19 interrupt. */ - __IOM uint32_t GPIO20 : 1; /*!< [20..20] GPIO20 interrupt. */ - __IOM uint32_t GPIO21 : 1; /*!< [21..21] GPIO21 interrupt. */ - __IOM uint32_t GPIO22 : 1; /*!< [22..22] GPIO22 interrupt. */ - __IOM uint32_t GPIO23 : 1; /*!< [23..23] GPIO23 interrupt. */ - __IOM uint32_t GPIO24 : 1; /*!< [24..24] GPIO24 interrupt. */ - __IOM uint32_t GPIO25 : 1; /*!< [25..25] GPIO25 interrupt. */ - __IOM uint32_t GPIO26 : 1; /*!< [26..26] GPIO26 interrupt. */ - __IOM uint32_t GPIO27 : 1; /*!< [27..27] GPIO27 interrupt. */ - __IOM uint32_t GPIO28 : 1; /*!< [28..28] GPIO28 interrupt. */ - __IOM uint32_t GPIO29 : 1; /*!< [29..29] GPIO29 interrupt. */ - __IOM uint32_t GPIO30 : 1; /*!< [30..30] GPIO30 interrupt. */ - __IOM uint32_t GPIO31 : 1; /*!< [31..31] GPIO31 interrupt. */ - } INT0SET_b; - } ; - - union { - __IOM uint32_t INT1EN; /*!< (@ 0x00000210) GPIO Interrupt Registers 49-32: Enable */ - - struct { - __IOM uint32_t GPIO32 : 1; /*!< [0..0] GPIO32 interrupt. */ - __IOM uint32_t GPIO33 : 1; /*!< [1..1] GPIO33 interrupt. */ - __IOM uint32_t GPIO34 : 1; /*!< [2..2] GPIO34 interrupt. */ - __IOM uint32_t GPIO35 : 1; /*!< [3..3] GPIO35 interrupt. */ - __IOM uint32_t GPIO36 : 1; /*!< [4..4] GPIO36 interrupt. */ - __IOM uint32_t GPIO37 : 1; /*!< [5..5] GPIO37 interrupt. */ - __IOM uint32_t GPIO38 : 1; /*!< [6..6] GPIO38 interrupt. */ - __IOM uint32_t GPIO39 : 1; /*!< [7..7] GPIO39 interrupt. */ - __IOM uint32_t GPIO40 : 1; /*!< [8..8] GPIO40 interrupt. */ - __IOM uint32_t GPIO41 : 1; /*!< [9..9] GPIO41 interrupt. */ - __IOM uint32_t GPIO42 : 1; /*!< [10..10] GPIO42 interrupt. */ - __IOM uint32_t GPIO43 : 1; /*!< [11..11] GPIO43 interrupt. */ - __IOM uint32_t GPIO44 : 1; /*!< [12..12] GPIO44 interrupt. */ - __IOM uint32_t GPIO45 : 1; /*!< [13..13] GPIO45 interrupt. */ - __IOM uint32_t GPIO46 : 1; /*!< [14..14] GPIO46 interrupt. */ - __IOM uint32_t GPIO47 : 1; /*!< [15..15] GPIO47 interrupt. */ - __IOM uint32_t GPIO48 : 1; /*!< [16..16] GPIO48 interrupt. */ - __IOM uint32_t GPIO49 : 1; /*!< [17..17] GPIO49 interrupt. */ - } INT1EN_b; - } ; - - union { - __IOM uint32_t INT1STAT; /*!< (@ 0x00000214) GPIO Interrupt Registers 49-32: Status */ - - struct { - __IOM uint32_t GPIO32 : 1; /*!< [0..0] GPIO32 interrupt. */ - __IOM uint32_t GPIO33 : 1; /*!< [1..1] GPIO33 interrupt. */ - __IOM uint32_t GPIO34 : 1; /*!< [2..2] GPIO34 interrupt. */ - __IOM uint32_t GPIO35 : 1; /*!< [3..3] GPIO35 interrupt. */ - __IOM uint32_t GPIO36 : 1; /*!< [4..4] GPIO36 interrupt. */ - __IOM uint32_t GPIO37 : 1; /*!< [5..5] GPIO37 interrupt. */ - __IOM uint32_t GPIO38 : 1; /*!< [6..6] GPIO38 interrupt. */ - __IOM uint32_t GPIO39 : 1; /*!< [7..7] GPIO39 interrupt. */ - __IOM uint32_t GPIO40 : 1; /*!< [8..8] GPIO40 interrupt. */ - __IOM uint32_t GPIO41 : 1; /*!< [9..9] GPIO41 interrupt. */ - __IOM uint32_t GPIO42 : 1; /*!< [10..10] GPIO42 interrupt. */ - __IOM uint32_t GPIO43 : 1; /*!< [11..11] GPIO43 interrupt. */ - __IOM uint32_t GPIO44 : 1; /*!< [12..12] GPIO44 interrupt. */ - __IOM uint32_t GPIO45 : 1; /*!< [13..13] GPIO45 interrupt. */ - __IOM uint32_t GPIO46 : 1; /*!< [14..14] GPIO46 interrupt. */ - __IOM uint32_t GPIO47 : 1; /*!< [15..15] GPIO47 interrupt. */ - __IOM uint32_t GPIO48 : 1; /*!< [16..16] GPIO48 interrupt. */ - __IOM uint32_t GPIO49 : 1; /*!< [17..17] GPIO49 interrupt. */ - } INT1STAT_b; - } ; - - union { - __IOM uint32_t INT1CLR; /*!< (@ 0x00000218) GPIO Interrupt Registers 49-32: Clear */ - - struct { - __IOM uint32_t GPIO32 : 1; /*!< [0..0] GPIO32 interrupt. */ - __IOM uint32_t GPIO33 : 1; /*!< [1..1] GPIO33 interrupt. */ - __IOM uint32_t GPIO34 : 1; /*!< [2..2] GPIO34 interrupt. */ - __IOM uint32_t GPIO35 : 1; /*!< [3..3] GPIO35 interrupt. */ - __IOM uint32_t GPIO36 : 1; /*!< [4..4] GPIO36 interrupt. */ - __IOM uint32_t GPIO37 : 1; /*!< [5..5] GPIO37 interrupt. */ - __IOM uint32_t GPIO38 : 1; /*!< [6..6] GPIO38 interrupt. */ - __IOM uint32_t GPIO39 : 1; /*!< [7..7] GPIO39 interrupt. */ - __IOM uint32_t GPIO40 : 1; /*!< [8..8] GPIO40 interrupt. */ - __IOM uint32_t GPIO41 : 1; /*!< [9..9] GPIO41 interrupt. */ - __IOM uint32_t GPIO42 : 1; /*!< [10..10] GPIO42 interrupt. */ - __IOM uint32_t GPIO43 : 1; /*!< [11..11] GPIO43 interrupt. */ - __IOM uint32_t GPIO44 : 1; /*!< [12..12] GPIO44 interrupt. */ - __IOM uint32_t GPIO45 : 1; /*!< [13..13] GPIO45 interrupt. */ - __IOM uint32_t GPIO46 : 1; /*!< [14..14] GPIO46 interrupt. */ - __IOM uint32_t GPIO47 : 1; /*!< [15..15] GPIO47 interrupt. */ - __IOM uint32_t GPIO48 : 1; /*!< [16..16] GPIO48 interrupt. */ - __IOM uint32_t GPIO49 : 1; /*!< [17..17] GPIO49 interrupt. */ - } INT1CLR_b; - } ; - - union { - __IOM uint32_t INT1SET; /*!< (@ 0x0000021C) GPIO Interrupt Registers 49-32: Set */ - - struct { - __IOM uint32_t GPIO32 : 1; /*!< [0..0] GPIO32 interrupt. */ - __IOM uint32_t GPIO33 : 1; /*!< [1..1] GPIO33 interrupt. */ - __IOM uint32_t GPIO34 : 1; /*!< [2..2] GPIO34 interrupt. */ - __IOM uint32_t GPIO35 : 1; /*!< [3..3] GPIO35 interrupt. */ - __IOM uint32_t GPIO36 : 1; /*!< [4..4] GPIO36 interrupt. */ - __IOM uint32_t GPIO37 : 1; /*!< [5..5] GPIO37 interrupt. */ - __IOM uint32_t GPIO38 : 1; /*!< [6..6] GPIO38 interrupt. */ - __IOM uint32_t GPIO39 : 1; /*!< [7..7] GPIO39 interrupt. */ - __IOM uint32_t GPIO40 : 1; /*!< [8..8] GPIO40 interrupt. */ - __IOM uint32_t GPIO41 : 1; /*!< [9..9] GPIO41 interrupt. */ - __IOM uint32_t GPIO42 : 1; /*!< [10..10] GPIO42 interrupt. */ - __IOM uint32_t GPIO43 : 1; /*!< [11..11] GPIO43 interrupt. */ - __IOM uint32_t GPIO44 : 1; /*!< [12..12] GPIO44 interrupt. */ - __IOM uint32_t GPIO45 : 1; /*!< [13..13] GPIO45 interrupt. */ - __IOM uint32_t GPIO46 : 1; /*!< [14..14] GPIO46 interrupt. */ - __IOM uint32_t GPIO47 : 1; /*!< [15..15] GPIO47 interrupt. */ - __IOM uint32_t GPIO48 : 1; /*!< [16..16] GPIO48 interrupt. */ - __IOM uint32_t GPIO49 : 1; /*!< [17..17] GPIO49 interrupt. */ - } INT1SET_b; - } ; -} GPIO_Type; /*!< Size = 544 (0x220) */ - - - -/* =========================================================================================================================== */ -/* ================ IOM0 ================ */ -/* =========================================================================================================================== */ - - -/** - * @brief IO Peripheral Master (IOM0) - */ - -typedef struct { /*!< (@ 0x50004000) IOM0 Structure */ - - union { - __IOM uint32_t FIFO; /*!< (@ 0x00000000) FIFO Access Port */ - - struct { - __IOM uint32_t FIFO : 32; /*!< [31..0] FIFO direct access. Only locations 0 - 3F will return - valid information. */ - } FIFO_b; - } ; - __IM uint32_t RESERVED[63]; - - union { - __IOM uint32_t FIFOPTR; /*!< (@ 0x00000100) FIFO size and remaining slots open values */ - - struct { - __IOM uint32_t FIFO0SIZ : 8; /*!< [7..0] The number of valid data bytes currently in the FIFO - 0 (written by MCU, read by interface) */ - __IOM uint32_t FIFO0REM : 8; /*!< [15..8] The number of remaining data bytes slots currently in - FIFO 0 (written by MCU, read by interface) */ - __IOM uint32_t FIFO1SIZ : 8; /*!< [23..16] The number of valid data bytes currently in FIFO 1 - (written by interface, read by MCU) */ - __IOM uint32_t FIFO1REM : 8; /*!< [31..24] The number of remaining data bytes slots currently - in FIFO 1 (written by interface, read by MCU) */ - } FIFOPTR_b; - } ; - - union { - __IOM uint32_t FIFOTHR; /*!< (@ 0x00000104) FIFO Threshold Configuration */ - - struct { - __IOM uint32_t FIFORTHR : 6; /*!< [5..0] FIFO read threshold in bytes. A value of 0 will disable - the read FIFO level from activating the threshold interrupt. - If this field is non-zero, it will trigger a threshold - interrupt when the read fifo contains FIFORTHR valid bytes - of data, as indicated by the FIFO1SIZ field. This is intended - to signal when a data transfer of FIFORTHR bytes can be - done from the IOM module to the host via the read fifo - to support large IOM read operations. */ - __IM uint32_t : 2; - __IOM uint32_t FIFOWTHR : 6; /*!< [13..8] FIFO write threshold in bytes. A value of 0 will disable - the write FIFO level from activating the threshold interrupt. - If this field is non-zero, it will trigger a threshold - interrupt when the write fifo contains FIFOWTHR free bytes, - as indicated by the FIFO0REM field. This is intended to - signal when a transfer of FIFOWTHR bytes can be done from - the host to the IOM write fifo to support large IOM write - operations. */ - } FIFOTHR_b; - } ; - - union { - __IOM uint32_t FIFOPOP; /*!< (@ 0x00000108) FIFO POP register */ - - struct { - __IOM uint32_t FIFODOUT : 32; /*!< [31..0] This register will return the read data indicated by - the current read pointer on reads. If the POPWR control - bit in the FIFOCTRL register is reset (0), the fifo read - pointer will be advanced by one word as a result of the - read.If the POPWR bit is set (1), the fifo read pointer - will only be advanced after a write operation to this register. - The write data is ignored for this register.If less than - a even word multiple is available, and the command is completed, - the module will return the word containing */ - } FIFOPOP_b; - } ; - - union { - __IOM uint32_t FIFOPUSH; /*!< (@ 0x0000010C) FIFO PUSH register */ - - struct { - __IOM uint32_t FIFODIN : 32; /*!< [31..0] This register is used to write the FIFORAM in FIFO mode - and will cause a push event to occur to the next open slot - within the FIFORAM. Writing to this register will cause - the write point to increment by 1 word(4 bytes). */ - } FIFOPUSH_b; - } ; - - union { - __IOM uint32_t FIFOCTRL; /*!< (@ 0x00000110) FIFO Control Register */ - - struct { - __IOM uint32_t POPWR : 1; /*!< [0..0] Selects the mode in which 'pop' events are done for the - fifo read operations. A value of '1' will prevent a pop - event on a read operation, and will require a write to - the FIFOPOP register to create a pop event.A value of '0' - in this register will allow a pop event to occur on the - read of the FIFOPOP register, and may cause inadvertant - fifo pops when used in a debugging mode. */ - __IOM uint32_t FIFORSTN : 1; /*!< [1..1] Active low manual reset of the fifo. Write to 0 to reset - fifo, and then write to 1 to remove the reset. */ - } FIFOCTRL_b; - } ; - - union { - __IOM uint32_t FIFOLOC; /*!< (@ 0x00000114) FIFO Pointers */ - - struct { - __IOM uint32_t FIFOWPTR : 4; /*!< [3..0] Current FIFO write pointer. Value is the index into the - outgoing FIFO (FIFO0), which is used during write operations - to external devices. */ - __IM uint32_t : 4; - __IOM uint32_t FIFORPTR : 4; /*!< [11..8] Current FIFO read pointer. Used to index into the incoming - FIFO (FIFO1), which is used to store read data returned - from external devices during a read operation. */ - } FIFOLOC_b; - } ; - __IM uint32_t RESERVED1[58]; - - union { - __IOM uint32_t INTEN; /*!< (@ 0x00000200) IO Master Interrupts: Enable */ - - struct { - __IOM uint32_t CMDCMP : 1; /*!< [0..0] Command Complete interrupt. Asserted when the current - operation has completed. For repeated commands, this will - only be asserted when the final repeated command is completed. */ - __IOM uint32_t THR : 1; /*!< [1..1] FIFO Threshold interrupt. For write operations, asserted - when the number of free bytes in the write FIFO equals - or exceeds the WTHR field.For read operations, asserted - when the number of valid bytes in the read FIFO equals - of exceeds the value set in the RTHR field. */ - __IOM uint32_t FUNDFL : 1; /*!< [2..2] Read FIFO Underflow interrupt. This occurs when software - tries to pop from an empty fifo. */ - __IOM uint32_t FOVFL : 1; /*!< [3..3] Write FIFO Overflow interrupt. This occurs when software - tries to write to a full fifo. The current operation does - not stop. */ - __IOM uint32_t NAK : 1; /*!< [4..4] I2C NAK interrupt. Asserted when an unexpected NAK has - been received on the I2C bus. */ - __IOM uint32_t IACC : 1; /*!< [5..5] illegal FIFO access interrupt. Asserted when there is - a overflow or underflow event */ - __IOM uint32_t ICMD : 1; /*!< [6..6] illegal command interrupt. Asserted when a command is - written when an active command is in progress. */ - __IOM uint32_t START : 1; /*!< [7..7] START command interrupt. Asserted when another master - on the bus has signaled a START command. */ - __IOM uint32_t STOP : 1; /*!< [8..8] STOP command interrupt. Asserted when another master - on the bus has signaled a STOP command. */ - __IOM uint32_t ARB : 1; /*!< [9..9] Arbitration loss interrupt. Asserted when arbitration - is enabled and has been lost to another master on the bus. */ - __IOM uint32_t DCMP : 1; /*!< [10..10] DMA Complete. Processing of the DMA operation has completed - and the DMA submodule is returned into the idle state */ - __IOM uint32_t DERR : 1; /*!< [11..11] DMA Error encountered during the processing of the - DMA command. The DMA error could occur when the memory - access specified in the DMA operation is not available - or incorrectly specified. */ - __IOM uint32_t CQPAUSED : 1; /*!< [12..12] Command queue is paused due to an active event enabled - in the PAUSEEN register. The interrupt is posted when the - event is enabled within the PAUSEEN register, the mask - is active in the CQIRQMASK field and the event occurs. */ - __IOM uint32_t CQUPD : 1; /*!< [13..13] CQ write operation performed a register write with - the register address bit 0 set to 1. The low address bits - in the CQ address fields are unused and bit 0 can be used - to trigger an interrupt to indicate when this register - write is performed by the CQ operation. */ - __IOM uint32_t CQERR : 1; /*!< [14..14] Error during command queue operations */ - } INTEN_b; - } ; - - union { - __IOM uint32_t INTSTAT; /*!< (@ 0x00000204) IO Master Interrupts: Status */ - - struct { - __IOM uint32_t CMDCMP : 1; /*!< [0..0] Command Complete interrupt. Asserted when the current - operation has completed. For repeated commands, this will - only be asserted when the final repeated command is completed. */ - __IOM uint32_t THR : 1; /*!< [1..1] FIFO Threshold interrupt. For write operations, asserted - when the number of free bytes in the write FIFO equals - or exceeds the WTHR field.For read operations, asserted - when the number of valid bytes in the read FIFO equals - of exceeds the value set in the RTHR field. */ - __IOM uint32_t FUNDFL : 1; /*!< [2..2] Read FIFO Underflow interrupt. This occurs when software - tries to pop from an empty fifo. */ - __IOM uint32_t FOVFL : 1; /*!< [3..3] Write FIFO Overflow interrupt. This occurs when software - tries to write to a full fifo. The current operation does - not stop. */ - __IOM uint32_t NAK : 1; /*!< [4..4] I2C NAK interrupt. Asserted when an unexpected NAK has - been received on the I2C bus. */ - __IOM uint32_t IACC : 1; /*!< [5..5] illegal FIFO access interrupt. Asserted when there is - a overflow or underflow event */ - __IOM uint32_t ICMD : 1; /*!< [6..6] illegal command interrupt. Asserted when a command is - written when an active command is in progress. */ - __IOM uint32_t START : 1; /*!< [7..7] START command interrupt. Asserted when another master - on the bus has signaled a START command. */ - __IOM uint32_t STOP : 1; /*!< [8..8] STOP command interrupt. Asserted when another master - on the bus has signaled a STOP command. */ - __IOM uint32_t ARB : 1; /*!< [9..9] Arbitration loss interrupt. Asserted when arbitration - is enabled and has been lost to another master on the bus. */ - __IOM uint32_t DCMP : 1; /*!< [10..10] DMA Complete. Processing of the DMA operation has completed - and the DMA submodule is returned into the idle state */ - __IOM uint32_t DERR : 1; /*!< [11..11] DMA Error encountered during the processing of the - DMA command. The DMA error could occur when the memory - access specified in the DMA operation is not available - or incorrectly specified. */ - __IOM uint32_t CQPAUSED : 1; /*!< [12..12] Command queue is paused due to an active event enabled - in the PAUSEEN register. The interrupt is posted when the - event is enabled within the PAUSEEN register, the mask - is active in the CQIRQMASK field and the event occurs. */ - __IOM uint32_t CQUPD : 1; /*!< [13..13] CQ write operation performed a register write with - the register address bit 0 set to 1. The low address bits - in the CQ address fields are unused and bit 0 can be used - to trigger an interrupt to indicate when this register - write is performed by the CQ operation. */ - __IOM uint32_t CQERR : 1; /*!< [14..14] Error during command queue operations */ - } INTSTAT_b; - } ; - - union { - __IOM uint32_t INTCLR; /*!< (@ 0x00000208) IO Master Interrupts: Clear */ - - struct { - __IOM uint32_t CMDCMP : 1; /*!< [0..0] Command Complete interrupt. Asserted when the current - operation has completed. For repeated commands, this will - only be asserted when the final repeated command is completed. */ - __IOM uint32_t THR : 1; /*!< [1..1] FIFO Threshold interrupt. For write operations, asserted - when the number of free bytes in the write FIFO equals - or exceeds the WTHR field.For read operations, asserted - when the number of valid bytes in the read FIFO equals - of exceeds the value set in the RTHR field. */ - __IOM uint32_t FUNDFL : 1; /*!< [2..2] Read FIFO Underflow interrupt. This occurs when software - tries to pop from an empty fifo. */ - __IOM uint32_t FOVFL : 1; /*!< [3..3] Write FIFO Overflow interrupt. This occurs when software - tries to write to a full fifo. The current operation does - not stop. */ - __IOM uint32_t NAK : 1; /*!< [4..4] I2C NAK interrupt. Asserted when an unexpected NAK has - been received on the I2C bus. */ - __IOM uint32_t IACC : 1; /*!< [5..5] illegal FIFO access interrupt. Asserted when there is - a overflow or underflow event */ - __IOM uint32_t ICMD : 1; /*!< [6..6] illegal command interrupt. Asserted when a command is - written when an active command is in progress. */ - __IOM uint32_t START : 1; /*!< [7..7] START command interrupt. Asserted when another master - on the bus has signaled a START command. */ - __IOM uint32_t STOP : 1; /*!< [8..8] STOP command interrupt. Asserted when another master - on the bus has signaled a STOP command. */ - __IOM uint32_t ARB : 1; /*!< [9..9] Arbitration loss interrupt. Asserted when arbitration - is enabled and has been lost to another master on the bus. */ - __IOM uint32_t DCMP : 1; /*!< [10..10] DMA Complete. Processing of the DMA operation has completed - and the DMA submodule is returned into the idle state */ - __IOM uint32_t DERR : 1; /*!< [11..11] DMA Error encountered during the processing of the - DMA command. The DMA error could occur when the memory - access specified in the DMA operation is not available - or incorrectly specified. */ - __IOM uint32_t CQPAUSED : 1; /*!< [12..12] Command queue is paused due to an active event enabled - in the PAUSEEN register. The interrupt is posted when the - event is enabled within the PAUSEEN register, the mask - is active in the CQIRQMASK field and the event occurs. */ - __IOM uint32_t CQUPD : 1; /*!< [13..13] CQ write operation performed a register write with - the register address bit 0 set to 1. The low address bits - in the CQ address fields are unused and bit 0 can be used - to trigger an interrupt to indicate when this register - write is performed by the CQ operation. */ - __IOM uint32_t CQERR : 1; /*!< [14..14] Error during command queue operations */ - } INTCLR_b; - } ; - - union { - __IOM uint32_t INTSET; /*!< (@ 0x0000020C) IO Master Interrupts: Set */ - - struct { - __IOM uint32_t CMDCMP : 1; /*!< [0..0] Command Complete interrupt. Asserted when the current - operation has completed. For repeated commands, this will - only be asserted when the final repeated command is completed. */ - __IOM uint32_t THR : 1; /*!< [1..1] FIFO Threshold interrupt. For write operations, asserted - when the number of free bytes in the write FIFO equals - or exceeds the WTHR field.For read operations, asserted - when the number of valid bytes in the read FIFO equals - of exceeds the value set in the RTHR field. */ - __IOM uint32_t FUNDFL : 1; /*!< [2..2] Read FIFO Underflow interrupt. This occurs when software - tries to pop from an empty fifo. */ - __IOM uint32_t FOVFL : 1; /*!< [3..3] Write FIFO Overflow interrupt. This occurs when software - tries to write to a full fifo. The current operation does - not stop. */ - __IOM uint32_t NAK : 1; /*!< [4..4] I2C NAK interrupt. Asserted when an unexpected NAK has - been received on the I2C bus. */ - __IOM uint32_t IACC : 1; /*!< [5..5] illegal FIFO access interrupt. Asserted when there is - a overflow or underflow event */ - __IOM uint32_t ICMD : 1; /*!< [6..6] illegal command interrupt. Asserted when a command is - written when an active command is in progress. */ - __IOM uint32_t START : 1; /*!< [7..7] START command interrupt. Asserted when another master - on the bus has signaled a START command. */ - __IOM uint32_t STOP : 1; /*!< [8..8] STOP command interrupt. Asserted when another master - on the bus has signaled a STOP command. */ - __IOM uint32_t ARB : 1; /*!< [9..9] Arbitration loss interrupt. Asserted when arbitration - is enabled and has been lost to another master on the bus. */ - __IOM uint32_t DCMP : 1; /*!< [10..10] DMA Complete. Processing of the DMA operation has completed - and the DMA submodule is returned into the idle state */ - __IOM uint32_t DERR : 1; /*!< [11..11] DMA Error encountered during the processing of the - DMA command. The DMA error could occur when the memory - access specified in the DMA operation is not available - or incorrectly specified. */ - __IOM uint32_t CQPAUSED : 1; /*!< [12..12] Command queue is paused due to an active event enabled - in the PAUSEEN register. The interrupt is posted when the - event is enabled within the PAUSEEN register, the mask - is active in the CQIRQMASK field and the event occurs. */ - __IOM uint32_t CQUPD : 1; /*!< [13..13] CQ write operation performed a register write with - the register address bit 0 set to 1. The low address bits - in the CQ address fields are unused and bit 0 can be used - to trigger an interrupt to indicate when this register - write is performed by the CQ operation. */ - __IOM uint32_t CQERR : 1; /*!< [14..14] Error during command queue operations */ - } INTSET_b; - } ; - - union { - __IOM uint32_t CLKCFG; /*!< (@ 0x00000210) I/O Clock Configuration */ - - struct { - __IOM uint32_t IOCLKEN : 1; /*!< [0..0] Enable for the interface clock. Must be enabled prior - to executing any IO operations. */ - __IM uint32_t : 7; - __IOM uint32_t FSEL : 3; /*!< [10..8] Select the input clock frequency. */ - __IOM uint32_t DIV3 : 1; /*!< [11..11] Enable divide by 3 of the source IOCLK. Division by - 3 is done before the DIVEN programmable divider, and if - enabledwill provide the divided by 3 clock as the source - to the programmable divider. */ - __IOM uint32_t DIVEN : 1; /*!< [12..12] Enable clock division by TOTPER and LOWPER */ - __IM uint32_t : 3; - __IOM uint32_t LOWPER : 8; /*!< [23..16] Clock low clock count minus 1. This provides the number - of clocks the divided clock will be low when the DIVEN - = 1.Only applicable when DIVEN = 1. */ - __IOM uint32_t TOTPER : 8; /*!< [31..24] Clock total clock count minus 1. This provides the - total period of the divided clock -1 when the DIVEN is - active. Thesource clock is selected by FSEL. Only applicable - when DIVEN = 1. */ - } CLKCFG_b; - } ; - - union { - __IOM uint32_t SUBMODCTRL; /*!< (@ 0x00000214) Submodule control */ - - struct { - __IOM uint32_t SMOD0EN : 1; /*!< [0..0] Submodule 0 enable (1) or disable (0) */ - __IOM uint32_t SMOD0TYPE : 3; /*!< [3..1] Submodule 0 module type. This is the SPI Master interface. */ - __IOM uint32_t SMOD1EN : 1; /*!< [4..4] Submodule 1 enable (1) or disable (0) */ - __IOM uint32_t SMOD1TYPE : 3; /*!< [7..5] Submodule 0 module type. This is the I2C Master interface */ - } SUBMODCTRL_b; - } ; - - union { - __IOM uint32_t CMD; /*!< (@ 0x00000218) Command and offset Register */ - - struct { - __IOM uint32_t CMD : 5; /*!< [4..0] Command for submodule. */ - __IOM uint32_t OFFSETCNT : 2; /*!< [6..5] Number of offset bytes to use for the command - 0, 1, - 2, 3 are valid selections. The second (byte 1) and third - byte (byte 2) are read from the OFFSETHI register, and - the low order byte is pulled from this register in the - OFFSETLO field.Offset bytes are transmitted highest byte - first. EG if offsetcnt == 3, OFFSETHI[15:8] will be transmitted - first, then OFFSETHI[7:0] then OFFSETLO.If offsetcnt == - 2, OFFSETHI[7:0] will be transmitted, then OFFSETLO.If - offsetcnt == 1, only OFFSETLO will be transmitted. */ - __IOM uint32_t CONT : 1; /*!< [7..7] Contine to hold the bus after the current transaction - if set to a 1 with a new command issued. */ - __IOM uint32_t TSIZE : 12; /*!< [19..8] Defines the transaction size in bytes. The offset transfer - is not included in this size. */ - __IOM uint32_t CMDSEL : 2; /*!< [21..20] Command Specific selection information. Not used in - Master I2C. Used as CEn select for Master SPI transactions */ - __IM uint32_t : 2; - __IOM uint32_t OFFSETLO : 8; /*!< [31..24] This register holds the low order byte of offset to - be used in the transaction. The number of offset bytes - to use is set with bits 1:0 of the command. */ - } CMD_b; - } ; - - union { - __IOM uint32_t CMDRPT; /*!< (@ 0x0000021C) Command Repeat Register */ - - struct { - __IOM uint32_t CMDRPT : 5; /*!< [4..0] Count of number of times to repeat the next command. */ - } CMDRPT_b; - } ; - - union { - __IOM uint32_t OFFSETHI; /*!< (@ 0x00000220) High order 2 bytes of 3 byte offset for IO transaction */ - - struct { - __IOM uint32_t OFFSETHI : 16; /*!< [15..0] Holds the high order 2 bytes of the 3 byte addressing/offset - field to use with IO commands. The number of offset bytes - to use is specified in the command register */ - } OFFSETHI_b; - } ; - - union { - __IOM uint32_t CMDSTAT; /*!< (@ 0x00000224) Command status */ - - struct { - __IOM uint32_t CCMD : 5; /*!< [4..0] current command that is being executed */ - __IOM uint32_t CMDSTAT : 3; /*!< [7..5] The current status of the command execution. */ - __IOM uint32_t CTSIZE : 12; /*!< [19..8] The current number of bytes still to be transferred - with this command. This field will count down to zero. */ - } CMDSTAT_b; - } ; - __IM uint32_t RESERVED2[6]; - - union { - __IOM uint32_t DMATRIGEN; /*!< (@ 0x00000240) DMA Trigger Enable Register */ - - struct { - __IOM uint32_t DCMDCMPEN : 1; /*!< [0..0] Trigger DMA upon command complete. Enables the trigger - of the DMA when a command is completed. When this event - is triggered, the number of words transferred will be the - lesser of the remaining TOTCOUNT bytes, or */ - __IOM uint32_t DTHREN : 1; /*!< [1..1] Trigger DMA upon THR level reached. For M2P DMA operations - (IOM writes), the trigger will assert when the write FIFO - has (WTHR/4) number of words free in the write FIFO, and - will transfer (WTHR/4) number of wordsor, if the number - of words left to transfer is less than the WTHR value, - will transfer the remaining byte count.For P2M DMA operations, - the trigger will assert when the read FIFO has (RTHR/4) - words available in the read FIFO, and will transfer (RTHR/4) - words to SRAM. This trigger will NOT asser */ - } DMATRIGEN_b; - } ; - - union { - __IOM uint32_t DMATRIGSTAT; /*!< (@ 0x00000244) DMA Trigger Status Register */ - - struct { - __IOM uint32_t DCMDCMP : 1; /*!< [0..0] Triggered DMA from Command complete event. Bit is read - only and can be cleared by disabling the DCMDCMP trigger - enable or by disabling DMA. */ - __IOM uint32_t DTHR : 1; /*!< [1..1] Triggered DMA from THR event. Bit is read only and can - be cleared by disabling the DTHR trigger enable or by disabling - DMA. */ - __IOM uint32_t DTOTCMP : 1; /*!< [2..2] DMA triggered when DCMDCMP = 0, and the amount of data - in the FIFO was enough to complete the DMA operation (greater - than or equal to current TOTCOUNT) when the command completed. - This trigger is default active when the DCMDCMP trigger - isdisabled and there is enough data in the FIFO to complete - the DMA operation. */ - } DMATRIGSTAT_b; - } ; - __IM uint32_t RESERVED3[14]; - - union { - __IOM uint32_t DMACFG; /*!< (@ 0x00000280) DMA Configuration Register */ - - struct { - __IOM uint32_t DMAEN : 1; /*!< [0..0] DMA Enable. Setting this bit to EN will start the DMA - operation. This should be the last DMA related register - set prior to issuing the command */ - __IOM uint32_t DMADIR : 1; /*!< [1..1] Direction */ - __IM uint32_t : 6; - __IOM uint32_t DMAPRI : 1; /*!< [8..8] Sets the Priority of the DMA request */ - __IOM uint32_t DPWROFF : 1; /*!< [9..9] Power off module after DMA is complete. If this bit is - active, the module will request to power off the supply - it is attached to. If there are other units still requiring - power from the same domain, power down will not be performed. */ - } DMACFG_b; - } ; - __IM uint32_t RESERVED4; - - union { - __IOM uint32_t DMATOTCOUNT; /*!< (@ 0x00000288) DMA Total Transfer Count */ - - struct { - __IOM uint32_t TOTCOUNT : 12; /*!< [11..0] Triggered DMA from Command complete event occured. Bit - is read only and can be cleared by disabling the DTHR trigger - enable or by disabling DMA. */ - } DMATOTCOUNT_b; - } ; - - union { - __IOM uint32_t DMATARGADDR; /*!< (@ 0x0000028C) DMA Target Address Register */ - - struct { - __IOM uint32_t TARGADDR : 20; /*!< [19..0] Bits [19:0] of the target byte address for source of - DMA (either read or write). The address can be any byte - alignment, and does not have to be word aligned. In cases - of non-word aligned addresses, the DMA logic will take - care for ensuring only the target bytes are read/written. */ - __IM uint32_t : 8; - __IOM uint32_t TARGADDR28 : 1; /*!< [28..28] Bit 28 of the target byte address for source of DMA - (either read or write). In cases of non-word aligned addresses, - the DMA logic will take care for ensuring only the target - bytes are read/written.Setting to '1' will select the SRAM. - Setting to '0' will select the flash */ - } DMATARGADDR_b; - } ; - - union { - __IOM uint32_t DMASTAT; /*!< (@ 0x00000290) DMA Status Register */ - - struct { - __IOM uint32_t DMATIP : 1; /*!< [0..0] DMA Transfer In Progress indicator. 1 will indicate that - a DMA transfer is active. The DMA transfer may be waiting - on data, transferring data, or waiting for priority.All - of these will be indicated with a 1. A 0 will indicate - that the DMA is fully complete and no further transactions - will be done. This bit is read only. */ - __IOM uint32_t DMACPL : 1; /*!< [1..1] DMA Transfer Complete. This signals the end of the DMA - operation. This bit can be cleared by writing to 0, and - will also be cleared when a new DMA is started. */ - __IOM uint32_t DMAERR : 1; /*!< [2..2] DMA Error. This active high bit signals an error was - encountered during the DMA operation. The bit can be cleared - by writing to 0. Once set, this bit will remain set until - cleared by software. */ - } DMASTAT_b; - } ; - - union { - __IOM uint32_t CQCFG; /*!< (@ 0x00000294) Command Queue Configuration Register */ - - struct { - __IOM uint32_t CQEN : 1; /*!< [0..0] Command queue enable. When set, will enable the processing - of the command queue and fetches of address/data pairs - will proceed from the word address within the CQADDR register. - Can be disabledusing a CQ executed write to this bit as - well. */ - __IOM uint32_t CQPRI : 1; /*!< [1..1] Sets the Priority of the command queue dma request */ - } CQCFG_b; - } ; - - union { - __IOM uint32_t CQADDR; /*!< (@ 0x00000298) CQ Target Read Address Register */ - - struct { - __IM uint32_t : 2; - __IOM uint32_t CQADDR : 18; /*!< [19..2] Bits 19:2 of target byte address for source of CQ (read - only). The buffer must be aligned on a word boundary */ - __IM uint32_t : 8; - __IOM uint32_t CQADDR28 : 1; /*!< [28..28] Bit 28 of target byte address for source of CQ (read - only). Used to denote Flash (0) or SRAM (1) access */ - } CQADDR_b; - } ; - - union { - __IOM uint32_t CQSTAT; /*!< (@ 0x0000029C) Command Queue Status Register */ - - struct { - __IOM uint32_t CQTIP : 1; /*!< [0..0] Command queue Transfer In Progress indicator. 1 will - indicate that a CQ transfer is active and this will remain - active even when paused waiting for external event. */ - __IOM uint32_t CQPAUSED : 1; /*!< [1..1] Command queue operation is currently paused. */ - __IOM uint32_t CQERR : 1; /*!< [2..2] Command queue processing Error. This active high bit - signals that an error was encountered during the CQ operation. */ - } CQSTAT_b; - } ; - - union { - __IOM uint32_t CQFLAGS; /*!< (@ 0x000002A0) Command Queue Flag Register */ - - struct { - __IOM uint32_t CQFLAGS : 16; /*!< [15..0] Current flag status (read-only). Bits [7:0] are software - controllable and bits [15:8] are hardware status. */ - __IOM uint32_t CQIRQMASK : 16; /*!< [31..16] Mask the bits used to generate the command queue interrupt. - A '1' in the bit position will enable the pause event to - trigger the interrupt, if the CQWT_int interrupt is enabled. - Bits definitions are the same as CQPAUSE */ - } CQFLAGS_b; - } ; - - union { - __IOM uint32_t CQSETCLEAR; /*!< (@ 0x000002A4) Command Queue Flag Set/Clear Register */ - - struct { - __IOM uint32_t CQFSET : 8; /*!< [7..0] Set CQFlag status bits. Will set to 1 the value of any - SWFLAG with a '1' in the corresponding bit position of - this field */ - __IOM uint32_t CQFTGL : 8; /*!< [15..8] Toggle the indicated bit. Will toggle the value of any - SWFLAG with a '1' in the corresponding bit position of - this field */ - __IOM uint32_t CQFCLR : 8; /*!< [23..16] Clear CQFlag status bits. Will clear to 0 any SWFLAG - with a '1' in the corresponding bit position of this field */ - } CQSETCLEAR_b; - } ; - - union { - __IOM uint32_t CQPAUSEEN; /*!< (@ 0x000002A8) Command Queue Pause Enable Register */ - - struct { - __IOM uint32_t CQPEN : 16; /*!< [15..0] Enables the specified event to pause command processing - when active */ - } CQPAUSEEN_b; - } ; - - union { - __IOM uint32_t CQCURIDX; /*!< (@ 0x000002AC) IOM Command Queue current index value . Compared - to the CQENDIDX reg contents to generate - the IDXEQ Pause event for command queue */ - - struct { - __IOM uint32_t CQCURIDX : 8; /*!< [7..0] Holds 8 bits of data that will be compared with the CQENDIX - register field. If the values match, the IDXEQ pause event - will be activated, which will cause the pausing of command - quue operation if the IDXEQ bit is enabled in CQPAUSEEN. */ - } CQCURIDX_b; - } ; - - union { - __IOM uint32_t CQENDIDX; /*!< (@ 0x000002B0) IOM Command Queue current index value . Compared - to the CQCURIDX reg contents to generate - the IDXEQ Pause event for command queue */ - - struct { - __IOM uint32_t CQENDIDX : 8; /*!< [7..0] Holds 8 bits of data that will be compared with the CQCURIX - register field. If the values match, the IDXEQ pause event - will be activated, which will cause the pausing of command - quue operation if the IDXEQ bit is enabled in CQPAUSEEN. */ - } CQENDIDX_b; - } ; - - union { - __IOM uint32_t STATUS; /*!< (@ 0x000002B4) IOM Module Status Register */ - - struct { - __IOM uint32_t ERR : 1; /*!< [0..0] Bit has been deprecated. Please refer to the other error - indicators. This will always return 0. */ - __IOM uint32_t CMDACT : 1; /*!< [1..1] Indicates if the active I/O Command is currently processing - a transaction, or command is complete, but the FIFO pointers - are still syncronizing internally. This bit will go high - atthe start of the transaction, and will go low when the - command is complete, and the data and pointers within the - FIFO have been syncronized. */ - __IOM uint32_t IDLEST : 1; /*!< [2..2] indicates if the active I/O state machine is IDLE. Note - - The state machine could be in idle state due to holdoffs - from data availability, or as the command gets propagated - into the logic from the registers. */ - } STATUS_b; - } ; - __IM uint32_t RESERVED5[18]; - - union { - __IOM uint32_t MSPICFG; /*!< (@ 0x00000300) SPI module master configuration */ - - struct { - __IOM uint32_t SPOL : 1; /*!< [0..0] selects SPI polarity. */ - __IOM uint32_t SPHA : 1; /*!< [1..1] selects SPI phase. */ - __IOM uint32_t FULLDUP : 1; /*!< [2..2] Enables full duplex mode for Master SPI write operations. - Data will be captured simultaneously into the read fifo */ - __IM uint32_t : 13; - __IOM uint32_t WTFC : 1; /*!< [16..16] enables write mode flow control. */ - __IOM uint32_t RDFC : 1; /*!< [17..17] enables read mode flow control. */ - __IOM uint32_t MOSIINV : 1; /*!< [18..18] inverts MOSI when flow control is enabled. */ - __IM uint32_t : 1; - __IOM uint32_t WTFCIRQ : 1; /*!< [20..20] selects the write mode flow control signal. */ - __IOM uint32_t WTFCPOL : 1; /*!< [21..21] selects the write flow control signal polarity. The - transfers are halted when the selected flow control signal - is OPPOSITE polarity of bit. (For example: WTFCPOL = 0 - will allow a IRQ=1 to pause transfers). */ - __IOM uint32_t RDFCPOL : 1; /*!< [22..22] selects the read flow control signal polarity. */ - __IOM uint32_t SPILSB : 1; /*!< [23..23] Selects data transfer as MSB first (0) or LSB first - (1) for the data portion of the SPI transaction. The offset - bytes are always transmitted MSB first. */ - __IOM uint32_t DINDLY : 3; /*!< [26..24] Delay tap to use for the input signal (MISO). This - gives more hold time on the input data. */ - __IOM uint32_t DOUTDLY : 3; /*!< [29..27] Delay tap to use for the output signal (MOSI). This - give more hold time on the output data */ - __IOM uint32_t MSPIRST : 1; /*!< [30..30] Not used. To reset the module, toggle the SMOD_EN for - the module */ - } MSPICFG_b; - } ; - __IM uint32_t RESERVED6[63]; - - union { - __IOM uint32_t MI2CCFG; /*!< (@ 0x00000400) I2C Master configuration */ - - struct { - __IOM uint32_t ADDRSZ : 1; /*!< [0..0] Sets the I2C master device address size to either 7b - (0) or 10b (1). */ - __IOM uint32_t I2CLSB : 1; /*!< [1..1] Direction of data transmit and receive, MSB(0) or LSB(1) - first. Default per I2C specification is MSB first. This - applies to both read and write data, and read data will - be bit */ - __IOM uint32_t ARBEN : 1; /*!< [2..2] Enables multi-master arbitration for the I2C master. - If the bus is known to have only a single master, this - function can be disabled to save clock cycles on I2C transactions */ - __IM uint32_t : 1; - __IOM uint32_t SDADLY : 2; /*!< [5..4] Delay to enable on the SDA output. Values are 0x0-0x3. */ - __IOM uint32_t MI2CRST : 1; /*!< [6..6] Not used. To reset the module, toggle the SMOD_EN for - the module */ - __IM uint32_t : 1; - __IOM uint32_t SCLENDLY : 4; /*!< [11..8] Number of IOCLK cycles to delay the rising edge of the - SCL output en (clock will go low on this edge). Used to - allow clock shaping. */ - __IOM uint32_t SDAENDLY : 4; /*!< [15..12] Number of IOCLK cycles to delay the SDA output en (all - transitions affected). Used to delay data relative to clock */ - __IOM uint32_t SMPCNT : 8; /*!< [23..16] Number of Base clk cycles to wait before sampling the - SCL clock to determine if a clock stretch event has occured */ - __IOM uint32_t STRDIS : 1; /*!< [24..24] Disable detection of clock stretch events smaller than - 1 cycle */ - } MI2CCFG_b; - } ; - - union { - __IOM uint32_t DEVCFG; /*!< (@ 0x00000404) I2C Device Configuration register */ - - struct { - __IOM uint32_t DEVADDR : 10; /*!< [9..0] I2C address of the device that the Master will use to - target for read/write operations. This can be either a - 7b or 10b address. */ - } DEVCFG_b; - } ; - __IM uint32_t RESERVED7[2]; - - union { - __IOM uint32_t IOMDBG; /*!< (@ 0x00000410) IOM Debug Register */ - - struct { - __IOM uint32_t DBGEN : 1; /*!< [0..0] Debug Enable. Setting bit will enable the update of data - within this register, otherwise it is clock gated for power - savings */ - __IOM uint32_t IOCLKON : 1; /*!< [1..1] IOCLK debug clock control. Enable IO_CLK to be active - when this bit is '1'. Otherwise, the clock is controlled - with gating from the logic as needed. */ - __IOM uint32_t APBCLKON : 1; /*!< [2..2] APBCLK debug clock control. Enable APB_CLK to be active - when this bit is '1'. Otherwise, the clock is controlled - with gating from the logic as needed. */ - __IOM uint32_t DBGDATA : 29; /*!< [31..3] Debug control for various options. DBGDATA[1:0] is used - to select between different debug data available in the - DBG0 and DBG1 registers. */ - } IOMDBG_b; - } ; -} IOM0_Type; /*!< Size = 1044 (0x414) */ - - - -/* =========================================================================================================================== */ -/* ================ IOSLAVE ================ */ -/* =========================================================================================================================== */ - - -/** - * @brief I2C/SPI Slave (IOSLAVE) - */ - -typedef struct { /*!< (@ 0x50000000) IOSLAVE Structure */ - __IM uint32_t RESERVED[64]; - - union { - __IOM uint32_t FIFOPTR; /*!< (@ 0x00000100) Current FIFO Pointer */ - - struct { - __IOM uint32_t FIFOPTR : 8; /*!< [7..0] Current FIFO pointer. */ - __IOM uint32_t FIFOSIZ : 8; /*!< [15..8] The number of bytes currently in the hardware FIFO. */ - } FIFOPTR_b; - } ; - - union { - __IOM uint32_t FIFOCFG; /*!< (@ 0x00000104) FIFO Configuration */ - - struct { - __IOM uint32_t FIFOBASE : 5; /*!< [4..0] These bits hold the base address of the I/O FIFO in 8 - byte segments. The IO Slave FIFO is situated in LRAM at - (FIFOBASE*8) to (FIFOMAX*8-1). */ - __IM uint32_t : 3; - __IOM uint32_t FIFOMAX : 6; /*!< [13..8] These bits hold the maximum FIFO address in 8 byte segments. - It is also the beginning of the RAM area of the LRAM. Note - that no RAM area is configured if FIFOMAX is set to 0x1F. */ - __IM uint32_t : 10; - __IOM uint32_t ROBASE : 6; /*!< [29..24] Defines the read-only area. The IO Slave read-only - area is situated in LRAM at (ROBASE*8) to (FIFOBASE*8-1) */ - } FIFOCFG_b; - } ; - - union { - __IOM uint32_t FIFOTHR; /*!< (@ 0x00000108) FIFO Threshold Configuration */ - - struct { - __IOM uint32_t FIFOTHR : 8; /*!< [7..0] FIFO size interrupt threshold. */ - } FIFOTHR_b; - } ; - - union { - __IOM uint32_t FUPD; /*!< (@ 0x0000010C) FIFO Update Status */ - - struct { - __IOM uint32_t FIFOUPD : 1; /*!< [0..0] This bit indicates that a FIFO update is underway. */ - __IOM uint32_t IOREAD : 1; /*!< [1..1] This bitfield indicates an IO read is active. */ - } FUPD_b; - } ; - - union { - __IOM uint32_t FIFOCTR; /*!< (@ 0x00000110) Overall FIFO Counter */ - - struct { - __IOM uint32_t FIFOCTR : 10; /*!< [9..0] Virtual FIFO byte count */ - } FIFOCTR_b; - } ; - - union { - __IOM uint32_t FIFOINC; /*!< (@ 0x00000114) Overall FIFO Counter Increment */ - - struct { - __IOM uint32_t FIFOINC : 10; /*!< [9..0] Increment the Overall FIFO Counter by this value on a - write */ - } FIFOINC_b; - } ; - - union { - __IOM uint32_t CFG; /*!< (@ 0x00000118) I/O Slave Configuration */ - - struct { - __IOM uint32_t IFCSEL : 1; /*!< [0..0] This bit selects the I/O interface. */ - __IOM uint32_t SPOL : 1; /*!< [1..1] This bit selects SPI polarity. */ - __IOM uint32_t LSB : 1; /*!< [2..2] This bit selects the transfer bit ordering. */ - __IM uint32_t : 1; - __IOM uint32_t STARTRD : 1; /*!< [4..4] This bit holds the cycle to initiate an I/O RAM read. */ - __IM uint32_t : 3; - __IOM uint32_t I2CADDR : 12; /*!< [19..8] 7-bit or 10-bit I2C device address. */ - __IM uint32_t : 11; - __IOM uint32_t IFCEN : 1; /*!< [31..31] IOSLAVE interface enable. */ - } CFG_b; - } ; - - union { - __IOM uint32_t PRENC; /*!< (@ 0x0000011C) I/O Slave Interrupt Priority Encode */ - - struct { - __IOM uint32_t PRENC : 5; /*!< [4..0] These bits hold the priority encode of the REGACC interrupts. */ - } PRENC_b; - } ; - - union { - __IOM uint32_t IOINTCTL; /*!< (@ 0x00000120) I/O Interrupt Control */ - - struct { - __IOM uint32_t IOINTEN : 8; /*!< [7..0] These read-only bits indicate whether the IOINT interrupts - are enabled. */ - __IOM uint32_t IOINT : 8; /*!< [15..8] These bits read the IOINT interrupts. */ - __IOM uint32_t IOINTCLR : 1; /*!< [16..16] This bit clears all of the IOINT interrupts when written - with a 1. */ - __IM uint32_t : 7; - __IOM uint32_t IOINTSET : 8; /*!< [31..24] These bits set the IOINT interrupts when written with - a 1. */ - } IOINTCTL_b; - } ; - - union { - __IOM uint32_t GENADD; /*!< (@ 0x00000124) General Address Data */ - - struct { - __IOM uint32_t GADATA : 8; /*!< [7..0] The data supplied on the last General Address reference. */ - } GENADD_b; - } ; - __IM uint32_t RESERVED1[54]; - - union { - __IOM uint32_t INTEN; /*!< (@ 0x00000200) IO Slave Interrupts: Enable */ - - struct { - __IOM uint32_t FSIZE : 1; /*!< [0..0] FIFO Size interrupt. */ - __IOM uint32_t FOVFL : 1; /*!< [1..1] FIFO Overflow interrupt. */ - __IOM uint32_t FUNDFL : 1; /*!< [2..2] FIFO Underflow interrupt. */ - __IOM uint32_t FRDERR : 1; /*!< [3..3] FIFO Read Error interrupt. */ - __IOM uint32_t GENAD : 1; /*!< [4..4] I2C General Address interrupt. */ - __IOM uint32_t IOINTW : 1; /*!< [5..5] IO Write interrupt. */ - __IOM uint32_t XCMPRF : 1; /*!< [6..6] Transfer complete interrupt, read from FIFO space. */ - __IOM uint32_t XCMPRR : 1; /*!< [7..7] Transfer complete interrupt, read from register space. */ - __IOM uint32_t XCMPWF : 1; /*!< [8..8] Transfer complete interrupt, write to FIFO space. */ - __IOM uint32_t XCMPWR : 1; /*!< [9..9] Transfer complete interrupt, write to register space. */ - } INTEN_b; - } ; - - union { - __IOM uint32_t INTSTAT; /*!< (@ 0x00000204) IO Slave Interrupts: Status */ - - struct { - __IOM uint32_t FSIZE : 1; /*!< [0..0] FIFO Size interrupt. */ - __IOM uint32_t FOVFL : 1; /*!< [1..1] FIFO Overflow interrupt. */ - __IOM uint32_t FUNDFL : 1; /*!< [2..2] FIFO Underflow interrupt. */ - __IOM uint32_t FRDERR : 1; /*!< [3..3] FIFO Read Error interrupt. */ - __IOM uint32_t GENAD : 1; /*!< [4..4] I2C General Address interrupt. */ - __IOM uint32_t IOINTW : 1; /*!< [5..5] IO Write interrupt. */ - __IOM uint32_t XCMPRF : 1; /*!< [6..6] Transfer complete interrupt, read from FIFO space. */ - __IOM uint32_t XCMPRR : 1; /*!< [7..7] Transfer complete interrupt, read from register space. */ - __IOM uint32_t XCMPWF : 1; /*!< [8..8] Transfer complete interrupt, write to FIFO space. */ - __IOM uint32_t XCMPWR : 1; /*!< [9..9] Transfer complete interrupt, write to register space. */ - } INTSTAT_b; - } ; - - union { - __IOM uint32_t INTCLR; /*!< (@ 0x00000208) IO Slave Interrupts: Clear */ - - struct { - __IOM uint32_t FSIZE : 1; /*!< [0..0] FIFO Size interrupt. */ - __IOM uint32_t FOVFL : 1; /*!< [1..1] FIFO Overflow interrupt. */ - __IOM uint32_t FUNDFL : 1; /*!< [2..2] FIFO Underflow interrupt. */ - __IOM uint32_t FRDERR : 1; /*!< [3..3] FIFO Read Error interrupt. */ - __IOM uint32_t GENAD : 1; /*!< [4..4] I2C General Address interrupt. */ - __IOM uint32_t IOINTW : 1; /*!< [5..5] IO Write interrupt. */ - __IOM uint32_t XCMPRF : 1; /*!< [6..6] Transfer complete interrupt, read from FIFO space. */ - __IOM uint32_t XCMPRR : 1; /*!< [7..7] Transfer complete interrupt, read from register space. */ - __IOM uint32_t XCMPWF : 1; /*!< [8..8] Transfer complete interrupt, write to FIFO space. */ - __IOM uint32_t XCMPWR : 1; /*!< [9..9] Transfer complete interrupt, write to register space. */ - } INTCLR_b; - } ; - - union { - __IOM uint32_t INTSET; /*!< (@ 0x0000020C) IO Slave Interrupts: Set */ - - struct { - __IOM uint32_t FSIZE : 1; /*!< [0..0] FIFO Size interrupt. */ - __IOM uint32_t FOVFL : 1; /*!< [1..1] FIFO Overflow interrupt. */ - __IOM uint32_t FUNDFL : 1; /*!< [2..2] FIFO Underflow interrupt. */ - __IOM uint32_t FRDERR : 1; /*!< [3..3] FIFO Read Error interrupt. */ - __IOM uint32_t GENAD : 1; /*!< [4..4] I2C General Address interrupt. */ - __IOM uint32_t IOINTW : 1; /*!< [5..5] IO Write interrupt. */ - __IOM uint32_t XCMPRF : 1; /*!< [6..6] Transfer complete interrupt, read from FIFO space. */ - __IOM uint32_t XCMPRR : 1; /*!< [7..7] Transfer complete interrupt, read from register space. */ - __IOM uint32_t XCMPWF : 1; /*!< [8..8] Transfer complete interrupt, write to FIFO space. */ - __IOM uint32_t XCMPWR : 1; /*!< [9..9] Transfer complete interrupt, write to register space. */ - } INTSET_b; - } ; - - union { - __IOM uint32_t REGACCINTEN; /*!< (@ 0x00000210) Register Access Interrupts: Enable */ - - struct { - __IOM uint32_t REGACC : 32; /*!< [31..0] Register access interrupts. */ - } REGACCINTEN_b; - } ; - - union { - __IOM uint32_t REGACCINTSTAT; /*!< (@ 0x00000214) Register Access Interrupts: Status */ - - struct { - __IOM uint32_t REGACC : 32; /*!< [31..0] Register access interrupts. */ - } REGACCINTSTAT_b; - } ; - - union { - __IOM uint32_t REGACCINTCLR; /*!< (@ 0x00000218) Register Access Interrupts: Clear */ - - struct { - __IOM uint32_t REGACC : 32; /*!< [31..0] Register access interrupts. */ - } REGACCINTCLR_b; - } ; - - union { - __IOM uint32_t REGACCINTSET; /*!< (@ 0x0000021C) Register Access Interrupts: Set */ - - struct { - __IOM uint32_t REGACC : 32; /*!< [31..0] Register access interrupts. */ - } REGACCINTSET_b; - } ; -} IOSLAVE_Type; /*!< Size = 544 (0x220) */ - - - -/* =========================================================================================================================== */ -/* ================ MCUCTRL ================ */ -/* =========================================================================================================================== */ - - -/** - * @brief MCU Miscellaneous Control Logic (MCUCTRL) - */ - -typedef struct { /*!< (@ 0x40020000) MCUCTRL Structure */ - - union { - __IOM uint32_t CHIPPN; /*!< (@ 0x00000000) Chip Information Register */ - - struct { - __IOM uint32_t PARTNUM : 32; /*!< [31..0] BCD part number. */ - } CHIPPN_b; - } ; - - union { - __IOM uint32_t CHIPID0; /*!< (@ 0x00000004) Unique Chip ID 0 */ - - struct { - __IOM uint32_t CHIPID0 : 32; /*!< [31..0] Unique chip ID 0. */ - } CHIPID0_b; - } ; - - union { - __IOM uint32_t CHIPID1; /*!< (@ 0x00000008) Unique Chip ID 1 */ - - struct { - __IOM uint32_t CHIPID1 : 32; /*!< [31..0] Unique chip ID 1. */ - } CHIPID1_b; - } ; - - union { - __IOM uint32_t CHIPREV; /*!< (@ 0x0000000C) Chip Revision */ - - struct { - __IOM uint32_t REVMIN : 4; /*!< [3..0] Minor Revision ID. */ - __IOM uint32_t REVMAJ : 4; /*!< [7..4] Major Revision ID. */ - __IOM uint32_t SIPART : 12; /*!< [19..8] Silicon Part ID */ - } CHIPREV_b; - } ; - - union { - __IOM uint32_t VENDORID; /*!< (@ 0x00000010) Unique Vendor ID */ - - struct { - __IOM uint32_t VENDORID : 32; /*!< [31..0] Unique Vendor ID */ - } VENDORID_b; - } ; - - union { - __IOM uint32_t SKU; /*!< (@ 0x00000014) Unique Chip SKU */ - - struct { - __IOM uint32_t ALLOWBURST : 1; /*!< [0..0] Allow Burst feature */ - __IOM uint32_t ALLOWBLE : 1; /*!< [1..1] Allow BLE feature */ - __IOM uint32_t SECBOOT : 1; /*!< [2..2] Secure boot feature allowed */ - } SKU_b; - } ; - - union { - __IOM uint32_t FEATUREENABLE; /*!< (@ 0x00000018) Feature Enable on Burst and BLE */ - - struct { - __IOM uint32_t BLEREQ : 1; /*!< [0..0] Controls the BLE functionality */ - __IOM uint32_t BLEACK : 1; /*!< [1..1] ACK for BLEREQ */ - __IOM uint32_t BLEAVAIL : 1; /*!< [2..2] AVAILABILITY of the BLE functionality */ - __IM uint32_t : 1; - __IOM uint32_t BURSTREQ : 1; /*!< [4..4] Controls the Burst functionality */ - __IOM uint32_t BURSTACK : 1; /*!< [5..5] ACK for BURSTREQ */ - __IOM uint32_t BURSTAVAIL : 1; /*!< [6..6] Availability of Burst functionality */ - } FEATUREENABLE_b; - } ; - __IM uint32_t RESERVED; - - union { - __IOM uint32_t DEBUGGER; /*!< (@ 0x00000020) Debugger Control */ - - struct { - __IOM uint32_t LOCKOUT : 1; /*!< [0..0] Lockout of debugger (SWD). */ - } DEBUGGER_b; - } ; - __IM uint32_t RESERVED1[55]; - - union { - __IOM uint32_t BODCTRL; /*!< (@ 0x00000100) BOD control Register */ - - struct { - __IOM uint32_t BODLPWD : 1; /*!< [0..0] BODL Power Down. */ - __IOM uint32_t BODHPWD : 1; /*!< [1..1] BODH Power Down. */ - __IOM uint32_t BODCPWD : 1; /*!< [2..2] BODC Power Down. */ - __IOM uint32_t BODFPWD : 1; /*!< [3..3] BODF Power Down. */ - __IOM uint32_t BODLVREFSEL : 1; /*!< [4..4] BODL External Reference Select. Note: the SWE mux select - in PWRSEQ2SWE must be set for this to take effect. */ - __IOM uint32_t BODHVREFSEL : 1; /*!< [5..5] BODH External Reference Select. Note: the SWE mux select - in PWRSEQ2SWE must be set for this to take effect. */ - } BODCTRL_b; - } ; - - union { - __IOM uint32_t ADCPWRDLY; /*!< (@ 0x00000104) ADC Power Up Delay Control */ - - struct { - __IOM uint32_t ADCPWR0 : 8; /*!< [7..0] ADC Reference Buffer Power Enable delay in 64 ADC CLK - increments for ADC_CLKSEL = 0x1, 32 ADC CLOCK increments - for ADC_CLKSEL = 0x2. */ - __IOM uint32_t ADCPWR1 : 8; /*!< [15..8] ADC Reference Keeper enable delay in 16 ADC CLK increments - for ADC_CLKSEL = 0x1, 8 ADC CLOCK increments for ADC_CLKSEL - = 0x2. */ - } ADCPWRDLY_b; - } ; - __IM uint32_t RESERVED2; - - union { - __IOM uint32_t ADCCAL; /*!< (@ 0x0000010C) ADC Calibration Control */ - - struct { - __IOM uint32_t CALONPWRUP : 1; /*!< [0..0] Run ADC Calibration on initial power up sequence */ - __IOM uint32_t ADCCALIBRATED : 1; /*!< [1..1] Status for ADC Calibration */ - } ADCCAL_b; - } ; - - union { - __IOM uint32_t ADCBATTLOAD; /*!< (@ 0x00000110) ADC Battery Load Enable */ - - struct { - __IOM uint32_t BATTLOAD : 1; /*!< [0..0] Enable the ADC battery load resistor */ - } ADCBATTLOAD_b; - } ; - __IM uint32_t RESERVED3; - - union { - __IOM uint32_t ADCTRIM; /*!< (@ 0x00000118) ADC Trims */ - - struct { - __IOM uint32_t ADCREFKEEPIBTRIM : 2; /*!< [1..0] ADC Reference Ibias trim */ - __IM uint32_t : 4; - __IOM uint32_t ADCREFBUFTRIM : 5; /*!< [10..6] ADC Reference buffer trim */ - __IOM uint32_t ADCRFBUFIBTRIM : 2; /*!< [12..11] ADC reference buffer input bias trim */ - } ADCTRIM_b; - } ; - - union { - __IOM uint32_t ADCREFCOMP; /*!< (@ 0x0000011C) ADC Referece Keeper and Comparator Control */ - - struct { - __IOM uint32_t ADC_REFCOMP_OUT : 1; /*!< [0..0] Output of the ADC reference comparator */ - __IM uint32_t : 7; - __IOM uint32_t ADCREFKEEPTRIM : 5; /*!< [12..8] ADC Reference Keeper Trim */ - __IM uint32_t : 3; - __IOM uint32_t ADCRFCMPEN : 1; /*!< [16..16] ADC Reference comparator power down */ - } ADCREFCOMP_b; - } ; - - union { - __IOM uint32_t XTALCTRL; /*!< (@ 0x00000120) XTAL Oscillator Control */ - - struct { - __IOM uint32_t XTALSWE : 1; /*!< [0..0] XTAL Software Override Enable. */ - __IOM uint32_t FDBKDSBLXTAL : 1; /*!< [1..1] XTAL Oscillator Disable Feedback. */ - __IOM uint32_t BYPCMPRXTAL : 1; /*!< [2..2] XTAL Oscillator Bypass Comparator. */ - __IOM uint32_t PDNBCOREXTAL : 1; /*!< [3..3] XTAL Oscillator Power Down Core. */ - __IOM uint32_t PDNBCMPRXTAL : 1; /*!< [4..4] XTAL Oscillator Power Down Comparator. */ - __IOM uint32_t PWDBODXTAL : 1; /*!< [5..5] XTAL Power down on brown out. */ - __IOM uint32_t XTALIBUFTRIM : 2; /*!< [7..6] XTAL IBUFF trim */ - __IOM uint32_t XTALICOMPTRIM : 2; /*!< [9..8] XTAL ICOMP trim */ - } XTALCTRL_b; - } ; - - union { - __IOM uint32_t XTALGENCTRL; /*!< (@ 0x00000124) XTAL Oscillator General Control */ - - struct { - __IOM uint32_t ACWARMUP : 2; /*!< [1..0] Auto-calibration delay control */ - __IOM uint32_t XTALBIASTRIM : 6; /*!< [7..2] XTAL BIAS trim */ - __IOM uint32_t XTALKSBIASTRIM : 6; /*!< [13..8] XTAL IBIAS Kick start trim. This trim value is used - during the startup process to enable a faster lock. */ - } XTALGENCTRL_b; - } ; - __IM uint32_t RESERVED4[28]; - - union { - __IOM uint32_t MISCCTRL; /*!< (@ 0x00000198) Miscellaneous control register. */ - - struct { - __IOM uint32_t RESERVED_RW_0 : 5; /*!< [4..0] Reserved bits, always leave unchanged. The MISCCTRL register - must be modified via atomic RMW, leaving this bitfield - completely unmodified. Failure to do so will result in - unpredictable behavior. */ - __IOM uint32_t BLE_RESETN : 1; /*!< [5..5] BLE reset signal. */ - } MISCCTRL_b; - } ; - __IM uint32_t RESERVED5; - - union { - __IOM uint32_t BOOTLOADER; /*!< (@ 0x000001A0) Bootloader and secure boot functions */ - - struct { - __IOM uint32_t BOOTLOADERLOW : 1; /*!< [0..0] Determines whether the bootloader code is visible at - address 0x00000000 or not. Resets to 1, write 1 to clear. */ - __IOM uint32_t SBLOCK : 1; /*!< [1..1] Secure boot lock. Always resets to 1, write 1 to clear. - Enables system visibility to bootloader until set. */ - __IOM uint32_t PROTLOCK : 1; /*!< [2..2] Flash protection lock. Always resets to 1, write 1 to - clear. Enables writes to flash protection register set. */ - __IM uint32_t : 23; - __IOM uint32_t SECBOOTFEATURE : 2; /*!< [27..26] Indicates whether the secure boot feature is enabled. */ - __IOM uint32_t SECBOOT : 2; /*!< [29..28] Indicates whether the secure boot on cold reset is - enabled */ - __IOM uint32_t SECBOOTONRST : 2; /*!< [31..30] Indicates whether the secure boot on warm reset is - enabled */ - } BOOTLOADER_b; - } ; - - union { - __IOM uint32_t SHADOWVALID; /*!< (@ 0x000001A4) Register to indicate whether the shadow registers - have been successfully loaded from the Flash - Information Space. */ - - struct { - __IOM uint32_t VALID : 1; /*!< [0..0] Indicates whether the shadow registers contain valid - data from the Flash Information Space. */ - __IOM uint32_t BLDSLEEP : 1; /*!< [1..1] Indicates whether the bootloader should sleep or deep - sleep if no image loaded. */ - __IOM uint32_t INFO0_VALID : 1; /*!< [2..2] Indicates whether info0 contains valid data */ - } SHADOWVALID_b; - } ; - __IM uint32_t RESERVED6[2]; - - union { - __IOM uint32_t SCRATCH0; /*!< (@ 0x000001B0) Scratch register that is not reset by any reset */ - - struct { - __IOM uint32_t SCRATCH0 : 32; /*!< [31..0] Scratch register 0. */ - } SCRATCH0_b; - } ; - - union { - __IOM uint32_t SCRATCH1; /*!< (@ 0x000001B4) Scratch register that is not reset by any reset */ - - struct { - __IOM uint32_t SCRATCH1 : 32; /*!< [31..0] Scratch register 1. */ - } SCRATCH1_b; - } ; - __IM uint32_t RESERVED7[2]; - - union { - __IOM uint32_t ICODEFAULTADDR; /*!< (@ 0x000001C0) ICODE bus address which was present when a bus - fault occurred. */ - - struct { - __IOM uint32_t ICODEFAULTADDR : 32; /*!< [31..0] The ICODE bus address observed when a Bus Fault occurred. - Once an address is captured in this field, it is held until - the corresponding Fault Observed bit is cleared in the - FAULTSTATUS register. */ - } ICODEFAULTADDR_b; - } ; - - union { - __IOM uint32_t DCODEFAULTADDR; /*!< (@ 0x000001C4) DCODE bus address which was present when a bus - fault occurred. */ - - struct { - __IOM uint32_t DCODEFAULTADDR : 32; /*!< [31..0] The DCODE bus address observed when a Bus Fault occurred. - Once an address is captured in this field, it is held until - the corresponding Fault Observed bit is cleared in the - FAULTSTATUS register. */ - } DCODEFAULTADDR_b; - } ; - - union { - __IOM uint32_t SYSFAULTADDR; /*!< (@ 0x000001C8) System bus address which was present when a bus - fault occurred. */ - - struct { - __IOM uint32_t SYSFAULTADDR : 32; /*!< [31..0] SYS bus address observed when a Bus Fault occurred. - Once an address is captured in this field, it is held until - the corresponding Fault Observed bit is cleared in the - FAULTSTATUS register. */ - } SYSFAULTADDR_b; - } ; - - union { - __IOM uint32_t FAULTSTATUS; /*!< (@ 0x000001CC) Reflects the status of the bus decoders' fault - detection. Any write to this register will - clear all of the status bits within the - register. */ - - struct { - __IOM uint32_t ICODEFAULT : 1; /*!< [0..0] The ICODE Bus Decoder Fault Detected bit. When set, a - fault has been detected, and the ICODEFAULTADDR register - will contain the bus address which generated the fault. */ - __IOM uint32_t DCODEFAULT : 1; /*!< [1..1] DCODE Bus Decoder Fault Detected bit. When set, a fault - has been detected, and the DCODEFAULTADDR register will - contain the bus address which generated the fault. */ - __IOM uint32_t SYSFAULT : 1; /*!< [2..2] SYS Bus Decoder Fault Detected bit. When set, a fault - has been detected, and the SYSFAULTADDR register will contain - the bus address which generated the fault. */ - } FAULTSTATUS_b; - } ; - - union { - __IOM uint32_t FAULTCAPTUREEN; /*!< (@ 0x000001D0) Enable the fault capture registers */ - - struct { - __IOM uint32_t FAULTCAPTUREEN : 1; /*!< [0..0] Fault Capture Enable field. When set, the Fault Capture - monitors are enabled and addresses which generate a hard - fault are captured into the FAULTADDR registers. */ - } FAULTCAPTUREEN_b; - } ; - __IM uint32_t RESERVED8[11]; - - union { - __IOM uint32_t DBGR1; /*!< (@ 0x00000200) Read-only debug register 1 */ - - struct { - __IOM uint32_t ONETO8 : 32; /*!< [31..0] Read-only register for communication validation */ - } DBGR1_b; - } ; - - union { - __IOM uint32_t DBGR2; /*!< (@ 0x00000204) Read-only debug register 2 */ - - struct { - __IOM uint32_t COOLCODE : 32; /*!< [31..0] Read-only register for communication validation */ - } DBGR2_b; - } ; - __IM uint32_t RESERVED9[6]; - - union { - __IOM uint32_t PMUENABLE; /*!< (@ 0x00000220) Control bit to enable/disable the PMU */ - - struct { - __IOM uint32_t ENABLE : 1; /*!< [0..0] PMU Enable Control bit. When set, the MCU's PMU will - place the MCU into the lowest power consuming Deep Sleep - mode upon execution of a WFI instruction (dependent on - the setting of the SLEEPDEEP bit in the ARM SCR register). - When cleared, regardless of the requested sleep mode, the - PMU will not enter the lowest power Deep Sleep mode, instead - entering the Sleep mode. */ - } PMUENABLE_b; - } ; - __IM uint32_t RESERVED10[11]; - - union { - __IOM uint32_t TPIUCTRL; /*!< (@ 0x00000250) TPIU Control Register. Determines the clock enable - and frequency for the M4's TPIU interface. */ - - struct { - __IOM uint32_t ENABLE : 1; /*!< [0..0] TPIU Enable field. When set, the ARM M4 TPIU is enabled - and data can be streamed out of the MCU's SWO port using - the ARM ITM and TPIU modules. */ - __IM uint32_t : 7; - __IOM uint32_t CLKSEL : 3; /*!< [10..8] This field selects the frequency of the ARM M4 TPIU - port. */ - } TPIUCTRL_b; - } ; - __IM uint32_t RESERVED11[4]; - - union { - __IOM uint32_t OTAPOINTER; /*!< (@ 0x00000264) OTA (Over the Air) Update Pointer/Status. Reset - only by POA */ - - struct { - __IOM uint32_t OTAVALID : 1; /*!< [0..0] Indicates that an OTA update is valid */ - __IOM uint32_t OTASBLUPDATE : 1; /*!< [1..1] Indicates that the sbl_init has been updated */ - __IOM uint32_t OTAPOINTER : 30; /*!< [31..2] Flash page pointer with updated OTA image */ - } OTAPOINTER_b; - } ; - __IM uint32_t RESERVED12[6]; - - union { - __IOM uint32_t APBDMACTRL; /*!< (@ 0x00000280) DMA Control Register. Determines misc settings - for DMA operation */ - - struct { - __IOM uint32_t DMA_ENABLE : 1; /*!< [0..0] Enable the DMA controller. When disabled, DMA requests - will be ignored by the controller */ - __IOM uint32_t DECODEABORT : 1; /*!< [1..1] APB Decode Abort. When set, the APB bridge will issue - a data abort (bus fault) on transactions to peripherals - that are powered down. When set to 0, writes are quietly - discarded and reads return 0. */ - __IM uint32_t : 6; - __IOM uint32_t HYSTERESIS : 8; /*!< [15..8] This field determines how long the DMA will remain active - during deep sleep before shutting down and returning the - system to full deep sleep. Values are based on a 94KHz - clock and are roughly 10us increments for a range of ~10us - to 2.55ms */ - } APBDMACTRL_b; - } ; - - union { - __IOM uint32_t SRAMMODE; /*!< (@ 0x00000284) SRAM Controller mode bits */ - - struct { - __IOM uint32_t IPREFETCH : 1; /*!< [0..0] When set, instruction accesses to the SRAM banks will - be prefetched (normally 2 cycle read access). Generally, - this mode bit should be set for improved performance when - executing instructions from SRAM. */ - __IOM uint32_t IPREFETCH_CACHE : 1; /*!< [1..1] Secondary prefetch feature that will cache prefetched - data across bus waitstates (requires IPREFETCH to be set). */ - __IM uint32_t : 2; - __IOM uint32_t DPREFETCH : 1; /*!< [4..4] When set, data bus accesses to the SRAM banks will be - prefetched (normally 2 cycle read access). Use of this - mode bit is only recommended if the work flow has a large - number of sequential accesses. */ - __IOM uint32_t DPREFETCH_CACHE : 1; /*!< [5..5] Secondary prefetch feature that will cache prefetched - data across bus waitstates (requires DPREFETCH to be set). */ - } SRAMMODE_b; - } ; - __IM uint32_t RESERVED13[48]; - - union { - __IOM uint32_t KEXTCLKSEL; /*!< (@ 0x00000348) Key Register to enable the use of external clock - selects via the EXTCLKSEL reg */ - - struct { - __IOM uint32_t KEXTCLKSEL : 32; /*!< [31..0] Key register value. */ - } KEXTCLKSEL_b; - } ; - __IM uint32_t RESERVED14[4]; - - union { - __IOM uint32_t SIMOBUCK4; /*!< (@ 0x0000035C) SIMO Buck Control Reg1 */ - - struct { - __IOM uint32_t SIMOBUCKMEMLPLOWTONTRIM : 4;/*!< [3..0] simobuck_mem_lp_low_ton_trim */ - __IOM uint32_t SIMOBUCKMEMACTDRVSTRTRIM : 2;/*!< [5..4] simobuck_mem_act_drvstr_trim */ - __IOM uint32_t SIMOBUCKMEMLPDRVSTRTRIM : 2;/*!< [7..6] simobuck_mem_lp_drvstr_trim */ - __IOM uint32_t SIMOBUCKMEMLEAKAGETRIM : 2;/*!< [9..8] simobuck_mem_leakage_trim */ - __IOM uint32_t SIMOBUCKZXTRIM : 4; /*!< [13..10] simobuck_zx_trim */ - __IOM uint32_t SIMOBUCKUVLOCNTRTRIM : 3; /*!< [16..14] simobuck_uvlo_cntr_trim */ - __IOM uint32_t SIMOBUCKUVLODRVSTRTRIM : 3;/*!< [19..17] simobuck_uvlo_drvstr_trim */ - __IOM uint32_t SIMOBUCKEXTCLKSEL : 1; /*!< [20..20] simobuck_extclk_sel */ - __IOM uint32_t SIMOBUCKCLKDIVSEL : 2; /*!< [22..21] simobuck_clkdiv_sel */ - __IOM uint32_t SIMOBUCKCOMP2LPEN : 1; /*!< [23..23] simobuck_comp2_lp_en */ - __IOM uint32_t SIMOBUCKCOMP2TIMEOUTEN : 1;/*!< [24..24] simobuck_comp2_timeout_en */ - __IOM uint32_t SIMOBUCKPRIORITYSEL : 1; /*!< [25..25] simobuck_priority_sel */ - __IOM uint32_t SIMOBUCKUVLOMODE : 2; /*!< [27..26] simobuck_uvlo_mode */ - __IOM uint32_t SIMOBUCKIBIASTRIM : 4; /*!< [31..28] simobuck_bias_trim */ - } SIMOBUCK4_b; - } ; - __IM uint32_t RESERVED15[2]; - - union { - __IOM uint32_t BLEBUCK2; /*!< (@ 0x00000368) BLEBUCK2 Control Reg */ - - struct { - __IOM uint32_t BLEBUCKTONLOWTRIM : 6; /*!< [5..0] blebuck_ton_low_trim */ - __IOM uint32_t BLEBUCKTONHITRIM : 6; /*!< [11..6] blebuck_ton_hi_trim */ - __IOM uint32_t BLEBUCKTOND2ATRIM : 6; /*!< [17..12] blebuck_ton_trim */ - } BLEBUCK2_b; - } ; - __IM uint32_t RESERVED16[13]; - - union { - __IOM uint32_t FLASHWPROT0; /*!< (@ 0x000003A0) Flash Write Protection Bits */ - - struct { - __IOM uint32_t FW0BITS : 32; /*!< [31..0] Write protect flash 0x00000000 - 0x0007FFFF. Each bit - provides write protection for 16KB chunks of flash data - space. Bits are cleared by writing a 1 to the bit. When - read, 0 indicates the region is protected. Bits are sticky - (can be set when PROTLOCK is 1, but only cleared by reset) */ - } FLASHWPROT0_b; - } ; - - union { - __IOM uint32_t FLASHWPROT1; /*!< (@ 0x000003A4) Flash Write Protection Bits */ - - struct { - __IOM uint32_t FW1BITS : 32; /*!< [31..0] Write protect flash 0x00080000 - 0x000FFFFF. Each bit - provides write protection for 16KB chunks of flash data - space. Bits are cleared by writing a 1 to the bit. When - read, 0 indicates the region is protected. Bits are sticky - (can be set when PROTLOCK is 1, but only cleared by reset) */ - } FLASHWPROT1_b; - } ; - __IM uint32_t RESERVED17[2]; - - union { - __IOM uint32_t FLASHRPROT0; /*!< (@ 0x000003B0) Flash Read Protection Bits */ - - struct { - __IOM uint32_t FR0BITS : 32; /*!< [31..0] Copy (read) protect flash 0x00000000 - 0x0007FFFF. Each - bit provides read protection for 16KB chunks of flash. - Bits are cleared by writing a 1 to the bit. When read, - 0 indicates the region is protected. Bits are sticky (can - be set when PROTLOCK is 1, but only cleared by reset) */ - } FLASHRPROT0_b; - } ; - - union { - __IOM uint32_t FLASHRPROT1; /*!< (@ 0x000003B4) Flash Read Protection Bits */ - - struct { - __IOM uint32_t FR1BITS : 32; /*!< [31..0] Copy (read) protect flash 0x00080000 - 0x000FFFFF. Each - bit provides read protection for 16KB chunks of flash. - Bits are cleared by writing a 1 to the bit. When read, - 0 indicates the region is protected. Bits are sticky (can - be set when PROTLOCK is 1, but only cleared by reset) */ - } FLASHRPROT1_b; - } ; - __IM uint32_t RESERVED18[2]; - - union { - __IOM uint32_t DMASRAMWRITEPROTECT0; /*!< (@ 0x000003C0) SRAM write-protection bits. */ - - struct { - __IOM uint32_t DMA_WPROT0 : 32; /*!< [31..0] Write protect SRAM from DMA. Each bit provides write - protection for an 8KB region of memory. When set to 1, - the region will be protected from DMA writes, when set - to 0, DMA may write the region. */ - } DMASRAMWRITEPROTECT0_b; - } ; - - union { - __IOM uint32_t DMASRAMWRITEPROTECT1; /*!< (@ 0x000003C4) SRAM write-protection bits. */ - - struct { - __IOM uint32_t DMA_WPROT1 : 16; /*!< [15..0] Write protect SRAM from DMA. Each bit provides write - protection for an 8KB region of memory. When set to 1, - the region will be protected from DMA writes, when set - to 0, DMA may write the region. */ - } DMASRAMWRITEPROTECT1_b; - } ; - __IM uint32_t RESERVED19[2]; - - union { - __IOM uint32_t DMASRAMREADPROTECT0; /*!< (@ 0x000003D0) SRAM read-protection bits. */ - - struct { - __IOM uint32_t DMA_RPROT0 : 32; /*!< [31..0] Read protect SRAM from DMA. Each bit provides write - protection for an 8KB region of memory. When set to 1, - the region will be protected from DMA reads, when set to - 0, DMA may read the region. */ - } DMASRAMREADPROTECT0_b; - } ; - - union { - __IOM uint32_t DMASRAMREADPROTECT1; /*!< (@ 0x000003D4) SRAM read-protection bits. */ - - struct { - __IOM uint32_t DMA_RPROT1 : 16; /*!< [15..0] Read protect SRAM from DMA. Each bit provides write - protection for an 8KB region of memory. When set to 1, - the region will be protected from DMA reads, when set to - 0, DMA may read the region. */ - } DMASRAMREADPROTECT1_b; - } ; -} MCUCTRL_Type; /*!< Size = 984 (0x3d8) */ - - - -/* =========================================================================================================================== */ -/* ================ MSPI ================ */ -/* =========================================================================================================================== */ - - -/** - * @brief Multibit SPI Master (MSPI) - */ - -typedef struct { /*!< (@ 0x50014000) MSPI Structure */ - - union { - __IOM uint32_t CTRL; /*!< (@ 0x00000000) MSPI PIO Transfer Control/Status Register */ - - struct { - __IOM uint32_t START : 1; /*!< [0..0] Write to 1 to initiate a PIO transaction on the bus (typically - the entire register should be written at once with this - bit set). */ - __IOM uint32_t STATUS : 1; /*!< [1..1] Command status: 1 indicates command has completed. Cleared - by writing 1 to this bit or starting a new transfer. */ - __IOM uint32_t BUSY : 1; /*!< [2..2] Command status: 1 indicates controller is busy (command - in progress) */ - __IOM uint32_t QUADCMD : 1; /*!< [3..3] Flag indicating that the operation is a command that - should be replicated to both devices in paired QUAD mode. - This is typically only used when reading/writing configuration - registers in paired flash devices (do not set for memory - transfers). */ - __IM uint32_t : 1; - __IOM uint32_t CONT : 1; /*!< [5..5] Continuation transfer. When 1, indicates that the MSPI - will hold CE low after the transaction completes. This - is included for compatibility with IOM module since the - MSPI transfer module can handle most cases in a single - transfer. NOTE: CONT functionality only works with CLKDIV=2 - (24 MHz). */ - __IOM uint32_t BIGENDIAN : 1; /*!< [6..6] 1 indicates data in FIFO is in big endian format (MSB - first); 0 indicates little endian data (default, LSB first). */ - __IOM uint32_t ENTURN : 1; /*!< [7..7] Indicates whether TX->RX turnaround cycles should be - enabled for this operation (see TURNAROUND field in CFG - register). */ - __IOM uint32_t SENDA : 1; /*!< [8..8] Indicates whether an address phase should be sent (see - ADDR register and ASIZE field in CFG register) */ - __IOM uint32_t SENDI : 1; /*!< [9..9] Indicates whether an instruction phase should be sent - (see INSTR field and ISIZE field in CFG register) */ - __IOM uint32_t TXRX : 1; /*!< [10..10] 1 Indicates a TX operation, 0 indicates an RX operation - of XFERBYTES */ - __IOM uint32_t PIOSCRAMBLE : 1; /*!< [11..11] Enables data scrambling for PIO opertions. This should - only be used for data operations and never for commands - to a device. */ - __IM uint32_t : 4; - __IOM uint32_t XFERBYTES : 16; /*!< [31..16] Number of bytes to transmit or receive (based on TXRX - bit) */ - } CTRL_b; - } ; - - union { - __IOM uint32_t CFG; /*!< (@ 0x00000004) MSPI Transfer Configuration Register */ - - struct { - __IOM uint32_t DEVCFG : 4; /*!< [3..0] Flash configuration for XIP and AUTO DMA operations. - Controls value for SER (Slave Enable) for XIP operations - and address generation for DMA/XIP modes. Also used to - configure SPIFRF (frame format). */ - __IOM uint32_t ASIZE : 2; /*!< [5..4] Address Size. Address bytes to send from ADDR register - name = A1 value = 0x0 desc = Send one address byteenum - name = A2 value = 0x1 desc = Send two address bytesenum - name = A3 value = 0x2 desc = Send three address bytesenum - name = A4 value = 0x3 desc = Send four address bytes */ - __IOM uint32_t ISIZE : 1; /*!< [6..6] Instruction Sizeenum name = I8 value = 0x0 desc = Instruction - is 1 byteenum name = I16 value = 0x1 desc = Instruction - is 2 bytes */ - __IOM uint32_t SEPIO : 1; /*!< [7..7] Separate IO configuration. This bit should be set when - the target device has separate MOSI and MISO pins. Respective - IN/OUT bits below should be set to map pins. */ - __IOM uint32_t TURNAROUND : 6; /*!< [13..8] Number of turnaound cycles (for TX->RX transitions). - Qualified by ENTURN or XIPENTURN bit field. */ - __IM uint32_t : 2; - __IOM uint32_t CPHA : 1; /*!< [16..16] Serial clock phase. */ - __IOM uint32_t CPOL : 1; /*!< [17..17] Serial clock polarity. */ - } CFG_b; - } ; - - union { - __IOM uint32_t ADDR; /*!< (@ 0x00000008) MSPI Transfer Address Register */ - - struct { - __IOM uint32_t ADDR : 32; /*!< [31..0] Optional Address field to send (after optional instruction - field) - qualified by ASIZE in CMD register. NOTE: This - register is aliased to DMADEVADDR. */ - } ADDR_b; - } ; - - union { - __IOM uint32_t INSTR; /*!< (@ 0x0000000C) MSPI Transfer Instruction */ - - struct { - __IOM uint32_t INSTR : 16; /*!< [15..0] Optional Instruction field to send (1st byte) - qualified - by ISEND/ISIZE */ - } INSTR_b; - } ; - - union { - __IOM uint32_t TXFIFO; /*!< (@ 0x00000010) TX Data FIFO */ - - struct { - __IOM uint32_t TXFIFO : 32; /*!< [31..0] Data to be transmitted. Data should normall be aligned - to the LSB (pad the upper bits with zeros) unless BIGENDIAN - is set. */ - } TXFIFO_b; - } ; - - union { - __IOM uint32_t RXFIFO; /*!< (@ 0x00000014) RX Data FIFO */ - - struct { - __IOM uint32_t RXFIFO : 32; /*!< [31..0] Receive data. Data is aligned to the LSB (padded zeros - on upper bits) unless BIGENDIAN is set. */ - } RXFIFO_b; - } ; - - union { - __IOM uint32_t TXENTRIES; /*!< (@ 0x00000018) TX FIFO Entries */ - - struct { - __IOM uint32_t TXENTRIES : 5; /*!< [4..0] Number of 32-bit words/entries in TX FIFO */ - } TXENTRIES_b; - } ; - - union { - __IOM uint32_t RXENTRIES; /*!< (@ 0x0000001C) RX FIFO Entries */ - - struct { - __IOM uint32_t RXENTRIES : 5; /*!< [4..0] Number of 32-bit words/entries in RX FIFO */ - } RXENTRIES_b; - } ; - - union { - __IOM uint32_t THRESHOLD; /*!< (@ 0x00000020) TX/RX FIFO Threshhold Levels */ - - struct { - __IOM uint32_t TXTHRESH : 5; /*!< [4..0] Number of entries in TX FIFO that cause TXF interrupt */ - __IM uint32_t : 3; - __IOM uint32_t RXTHRESH : 5; /*!< [12..8] Number of entries in TX FIFO that cause RXE interrupt */ - } THRESHOLD_b; - } ; - __IM uint32_t RESERVED[55]; - - union { - __IOM uint32_t MSPICFG; /*!< (@ 0x00000100) MSPI Module Configuration */ - - struct { - __IOM uint32_t APBCLK : 1; /*!< [0..0] Enable continuous APB clock. For power-efficient operation, - APBCLK should be set to 0. */ - __IOM uint32_t RXCAP : 1; /*!< [1..1] Controls RX data capture phase. A setting of 0 (NORMAL) - captures read data at the normal capture point relative - to the internal clock launch point. However, to accomodate - chip/pad/board delays, a setting of RXCAP of 1 is expected - to be used to align the capture point with the return data - window. This bit is used in conjunction with RXNEG to provide - 4 unique capture points, all about 10ns apart. */ - __IOM uint32_t RXNEG : 1; /*!< [2..2] Adjusts the RX capture phase to the negedge of the 48MHz - internal clock (~10ns early). For normal operation, it - is expected that RXNEG will be set to 0. */ - __IOM uint32_t TXNEG : 1; /*!< [3..3] Launches TX data a half clock cycle (~10ns) early. This - should normally be programmed to zero (NORMAL). */ - __IOM uint32_t IOMSEL : 3; /*!< [6..4] Selects which IOM is selected for CQ handshake status. */ - __IM uint32_t : 1; - __IOM uint32_t CLKDIV : 6; /*!< [13..8] Clock Divider. Allows dividing 48 MHz base clock by - integer multiples. Enumerations are provided for common - frequency, but any integer divide from 48 MHz is allowed. - Odd divide ratios will result in a 33/66 percent duty cycle - with a long low clock pulse (to allow longer round-trip - for read data). */ - __IM uint32_t : 15; - __IOM uint32_t FIFORESET : 1; /*!< [29..29] Reset MSPI FIFO (active high). 1=reset FIFO, 0=normal - operation. May be used to manually flush the FIFO in error - handling. */ - __IOM uint32_t IPRSTN : 1; /*!< [30..30] IP block reset. Write to 0 to put the transfer module - in reset or 1 for normal operation. This may be required - after error conditions to clear the transfer on the bus. */ - __IOM uint32_t PRSTN : 1; /*!< [31..31] Peripheral reset. Master reset to the entire MSPI module - (DMA, XIP, and transfer state machines). 1=normal operation, - 0=in reset. */ - } MSPICFG_b; - } ; - - union { - __IOM uint32_t PADCFG; /*!< (@ 0x00000104) MSPI Output Pad Configuration */ - - struct { - __IOM uint32_t OUT3 : 1; /*!< [0..0] Output pad 3 configuration. 0=data[3] 1=CLK */ - __IOM uint32_t OUT4 : 1; /*!< [1..1] Output pad 4 configuration. 0=data[4] 1=data[0] */ - __IOM uint32_t OUT5 : 1; /*!< [2..2] Output pad 5 configuration. 0=data[5] 1=data[1] */ - __IOM uint32_t OUT6 : 1; /*!< [3..3] Output pad 6 configuration. 0=data[6] 1=data[2] */ - __IOM uint32_t OUT7 : 1; /*!< [4..4] Output pad 7 configuration. 0=data[7] 1=data[3] */ - __IM uint32_t : 11; - __IOM uint32_t IN0 : 2; /*!< [17..16] Data Input pad 0 pin muxing: 0=pad[0] 1=pad[4] 2=pad[1] - 3=pad[5] */ - __IOM uint32_t IN1 : 1; /*!< [18..18] Data Input pad 1 pin muxing: 0=pad[1] 1=pad[5] */ - __IOM uint32_t IN2 : 1; /*!< [19..19] Data Input pad 2 pin muxing: 0=pad[2] 1=pad[6] */ - __IOM uint32_t IN3 : 1; /*!< [20..20] Data Input pad 3 pin muxing: 0=pad[3] 1=pad[7] */ - __IOM uint32_t REVCS : 1; /*!< [21..21] Reverse CS connections. Allows CS1 to be associated - with lower data lanes and CS0 to be associated with upper - data lines */ - } PADCFG_b; - } ; - - union { - __IOM uint32_t PADOUTEN; /*!< (@ 0x00000108) MSPI Output Enable Pad Configuration */ - - struct { - __IOM uint32_t OUTEN : 9; /*!< [8..0] Output pad enable configuration. Indicates which pads - should be driven. Bits [3:0] are Quad0 data, [7:4] are - Quad1 data, and [8] is clock. */ - } PADOUTEN_b; - } ; - - union { - __IOM uint32_t FLASH; /*!< (@ 0x0000010C) Configuration for XIP/DMA support of SPI flash - modules. */ - - struct { - __IOM uint32_t XIPEN : 1; /*!< [0..0] Enable the XIP (eXecute In Place) function which effectively - enables the address decoding of the MSPI device in the - flash/cache address space at address 0x04000000-0x07FFFFFF. */ - __IM uint32_t : 1; - __IOM uint32_t XIPACK : 2; /*!< [3..2] Controls transmission of Micron XIP acknowledge cycles - (Micron Flash devices only) */ - __IOM uint32_t XIPBIGENDIAN : 1; /*!< [4..4] Indicates whether XIP/AUTO DMA data transfers are in - big or little endian format */ - __IOM uint32_t XIPENTURN : 1; /*!< [5..5] Indicates whether XIP/AUTO DMA operations should enable - TX->RX turnaround cycles */ - __IOM uint32_t XIPSENDA : 1; /*!< [6..6] Indicates whether XIP/AUTO DMA operations should send - an an address phase (see DMADEVADDR register and ASIZE - field in CFG) */ - __IOM uint32_t XIPSENDI : 1; /*!< [7..7] Indicates whether XIP/AUTO DMA operations should send - an instruction (see READINSTR field and ISIZE field in - CFG) */ - __IOM uint32_t XIPMIXED : 3; /*!< [10..8] Reserved. Set to 0x0 */ - __IM uint32_t : 5; - __IOM uint32_t WRITEINSTR : 8; /*!< [23..16] Write command sent for DMA operations */ - __IOM uint32_t READINSTR : 8; /*!< [31..24] Read command sent to flash for DMA/XIP operations */ - } FLASH_b; - } ; - __IM uint32_t RESERVED1[4]; - - union { - __IOM uint32_t SCRAMBLING; /*!< (@ 0x00000120) External Flash Scrambling Controls */ - - struct { - __IOM uint32_t SCRSTART : 10; /*!< [9..0] Scrambling region start address [25:16] (64K block granularity). - The START block is the FIRST block included in the scrambled - address range. */ - __IM uint32_t : 6; - __IOM uint32_t SCREND : 10; /*!< [25..16] Scrambling region end address [25:16] (64K block granularity). - The END block is the LAST block included in the scrambled - address range. */ - __IM uint32_t : 5; - __IOM uint32_t SCRENABLE : 1; /*!< [31..31] Enables Data Scrambling Region. When 1 reads and writes - to the range will be scrambled. When 0, data will be read/written - unmodified. Address range is specified in 64K granularity - and the START/END ranges are included within the range. */ - } SCRAMBLING_b; - } ; - __IM uint32_t RESERVED2[55]; - - union { - __IOM uint32_t INTEN; /*!< (@ 0x00000200) MSPI Master Interrupts: Enable */ - - struct { - __IOM uint32_t CMDCMP : 1; /*!< [0..0] Transfer complete. Note that DMA and CQ operations are - layered, so CMDCMP, DCMP, and CQ* can all be signalled - simultaneously */ - __IOM uint32_t TXE : 1; /*!< [1..1] Transmit FIFO empty. */ - __IOM uint32_t TXO : 1; /*!< [2..2] Transmit FIFO Overflow (only occurs when SW writes to - a full FIFO). */ - __IOM uint32_t RXU : 1; /*!< [3..3] Receive FIFO underflow (only occurs when SW reads from - an empty FIFO) */ - __IOM uint32_t RXO : 1; /*!< [4..4] Receive FIFO overflow (cannot happen in MSPI design -- - MSPI bus pins will stall) */ - __IOM uint32_t RXF : 1; /*!< [5..5] Receive FIFO full */ - __IOM uint32_t DCMP : 1; /*!< [6..6] DMA Complete Interrupt */ - __IOM uint32_t DERR : 1; /*!< [7..7] DMA Error Interrupt */ - __IOM uint32_t CQCMP : 1; /*!< [8..8] Command Queue Complete Interrupt */ - __IOM uint32_t CQUPD : 1; /*!< [9..9] Command Queue Update Interrupt. Issued whenever the CQ - performs an operation where address bit[0] is set. Useful - for triggering CURIDX interrupts. */ - __IOM uint32_t CQPAUSED : 1; /*!< [10..10] Command Queue is Paused. */ - __IOM uint32_t CQERR : 1; /*!< [11..11] Command Queue Error Interrupt */ - __IOM uint32_t SCRERR : 1; /*!< [12..12] Scrambling Alignment Error. Scrambling operations must - be aligned to word (4-byte) start address. */ - } INTEN_b; - } ; - - union { - __IOM uint32_t INTSTAT; /*!< (@ 0x00000204) MSPI Master Interrupts: Status */ - - struct { - __IOM uint32_t CMDCMP : 1; /*!< [0..0] Transfer complete. Note that DMA and CQ operations are - layered, so CMDCMP, DCMP, and CQ* can all be signalled - simultaneously */ - __IOM uint32_t TXE : 1; /*!< [1..1] Transmit FIFO empty. */ - __IOM uint32_t TXO : 1; /*!< [2..2] Transmit FIFO Overflow (only occurs when SW writes to - a full FIFO). */ - __IOM uint32_t RXU : 1; /*!< [3..3] Receive FIFO underflow (only occurs when SW reads from - an empty FIFO) */ - __IOM uint32_t RXO : 1; /*!< [4..4] Receive FIFO overflow (cannot happen in MSPI design -- - MSPI bus pins will stall) */ - __IOM uint32_t RXF : 1; /*!< [5..5] Receive FIFO full */ - __IOM uint32_t DCMP : 1; /*!< [6..6] DMA Complete Interrupt */ - __IOM uint32_t DERR : 1; /*!< [7..7] DMA Error Interrupt */ - __IOM uint32_t CQCMP : 1; /*!< [8..8] Command Queue Complete Interrupt */ - __IOM uint32_t CQUPD : 1; /*!< [9..9] Command Queue Update Interrupt. Issued whenever the CQ - performs an operation where address bit[0] is set. Useful - for triggering CURIDX interrupts. */ - __IOM uint32_t CQPAUSED : 1; /*!< [10..10] Command Queue is Paused. */ - __IOM uint32_t CQERR : 1; /*!< [11..11] Command Queue Error Interrupt */ - __IOM uint32_t SCRERR : 1; /*!< [12..12] Scrambling Alignment Error. Scrambling operations must - be aligned to word (4-byte) start address. */ - } INTSTAT_b; - } ; - - union { - __IOM uint32_t INTCLR; /*!< (@ 0x00000208) MSPI Master Interrupts: Clear */ - - struct { - __IOM uint32_t CMDCMP : 1; /*!< [0..0] Transfer complete. Note that DMA and CQ operations are - layered, so CMDCMP, DCMP, and CQ* can all be signalled - simultaneously */ - __IOM uint32_t TXE : 1; /*!< [1..1] Transmit FIFO empty. */ - __IOM uint32_t TXO : 1; /*!< [2..2] Transmit FIFO Overflow (only occurs when SW writes to - a full FIFO). */ - __IOM uint32_t RXU : 1; /*!< [3..3] Receive FIFO underflow (only occurs when SW reads from - an empty FIFO) */ - __IOM uint32_t RXO : 1; /*!< [4..4] Receive FIFO overflow (cannot happen in MSPI design -- - MSPI bus pins will stall) */ - __IOM uint32_t RXF : 1; /*!< [5..5] Receive FIFO full */ - __IOM uint32_t DCMP : 1; /*!< [6..6] DMA Complete Interrupt */ - __IOM uint32_t DERR : 1; /*!< [7..7] DMA Error Interrupt */ - __IOM uint32_t CQCMP : 1; /*!< [8..8] Command Queue Complete Interrupt */ - __IOM uint32_t CQUPD : 1; /*!< [9..9] Command Queue Update Interrupt. Issued whenever the CQ - performs an operation where address bit[0] is set. Useful - for triggering CURIDX interrupts. */ - __IOM uint32_t CQPAUSED : 1; /*!< [10..10] Command Queue is Paused. */ - __IOM uint32_t CQERR : 1; /*!< [11..11] Command Queue Error Interrupt */ - __IOM uint32_t SCRERR : 1; /*!< [12..12] Scrambling Alignment Error. Scrambling operations must - be aligned to word (4-byte) start address. */ - } INTCLR_b; - } ; - - union { - __IOM uint32_t INTSET; /*!< (@ 0x0000020C) MSPI Master Interrupts: Set */ - - struct { - __IOM uint32_t CMDCMP : 1; /*!< [0..0] Transfer complete. Note that DMA and CQ operations are - layered, so CMDCMP, DCMP, and CQ* can all be signalled - simultaneously */ - __IOM uint32_t TXE : 1; /*!< [1..1] Transmit FIFO empty. */ - __IOM uint32_t TXO : 1; /*!< [2..2] Transmit FIFO Overflow (only occurs when SW writes to - a full FIFO). */ - __IOM uint32_t RXU : 1; /*!< [3..3] Receive FIFO underflow (only occurs when SW reads from - an empty FIFO) */ - __IOM uint32_t RXO : 1; /*!< [4..4] Receive FIFO overflow (cannot happen in MSPI design -- - MSPI bus pins will stall) */ - __IOM uint32_t RXF : 1; /*!< [5..5] Receive FIFO full */ - __IOM uint32_t DCMP : 1; /*!< [6..6] DMA Complete Interrupt */ - __IOM uint32_t DERR : 1; /*!< [7..7] DMA Error Interrupt */ - __IOM uint32_t CQCMP : 1; /*!< [8..8] Command Queue Complete Interrupt */ - __IOM uint32_t CQUPD : 1; /*!< [9..9] Command Queue Update Interrupt. Issued whenever the CQ - performs an operation where address bit[0] is set. Useful - for triggering CURIDX interrupts. */ - __IOM uint32_t CQPAUSED : 1; /*!< [10..10] Command Queue is Paused. */ - __IOM uint32_t CQERR : 1; /*!< [11..11] Command Queue Error Interrupt */ - __IOM uint32_t SCRERR : 1; /*!< [12..12] Scrambling Alignment Error. Scrambling operations must - be aligned to word (4-byte) start address. */ - } INTSET_b; - } ; - __IM uint32_t RESERVED3[16]; - - union { - __IOM uint32_t DMACFG; /*!< (@ 0x00000250) DMA Configuration Register */ - - struct { - __IOM uint32_t DMAEN : 2; /*!< [1..0] DMA Enable. Setting this bit to EN will start the DMA - operation */ - __IOM uint32_t DMADIR : 1; /*!< [2..2] Direction */ - __IOM uint32_t DMAPRI : 2; /*!< [4..3] Sets the Priority of the DMA request */ - __IM uint32_t : 13; - __IOM uint32_t DMAPWROFF : 1; /*!< [18..18] Power off MSPI domain upon completion of DMA operation. */ - } DMACFG_b; - } ; - - union { - __IOM uint32_t DMASTAT; /*!< (@ 0x00000254) DMA Status Register */ - - struct { - __IOM uint32_t DMATIP : 1; /*!< [0..0] DMA Transfer In Progress indicator. 1 will indicate that - a DMA transfer is active. The DMA transfer may be waiting - on data, transferring data, or waiting for priority. All - of these will be indicated with a 1. A 0 will indicate - that the DMA is fully complete and no further transactions - will be done. */ - __IOM uint32_t DMACPL : 1; /*!< [1..1] DMA Transfer Complete. This signals the end of the DMA - operation. */ - __IOM uint32_t DMAERR : 1; /*!< [2..2] DMA Error. This active high bit signals that an error - was encountered during the DMA operation. */ - __IOM uint32_t SCRERR : 1; /*!< [3..3] Scrambling Access Alignment Error. This active high bit - signals that a scrambling operation was specified for a - non-word aligned DEVADDR. */ - } DMASTAT_b; - } ; - - union { - __IOM uint32_t DMATARGADDR; /*!< (@ 0x00000258) DMA Target Address Register */ - - struct { - __IOM uint32_t TARGADDR : 32; /*!< [31..0] Target byte address for source of DMA (either read or - write). In cases of non-word aligned addresses, the DMA - logic will take care for ensuring only the target bytes - are read/written. */ - } DMATARGADDR_b; - } ; - - union { - __IOM uint32_t DMADEVADDR; /*!< (@ 0x0000025C) DMA Device Address Register */ - - struct { - __IOM uint32_t DEVADDR : 32; /*!< [31..0] SPI Device address for automated DMA transactions (both - read and write). */ - } DMADEVADDR_b; - } ; - - union { - __IOM uint32_t DMATOTCOUNT; /*!< (@ 0x00000260) DMA Total Transfer Count */ - - struct { - __IOM uint32_t TOTCOUNT : 16; /*!< [15..0] Total Transfer Count in bytes. */ - } DMATOTCOUNT_b; - } ; - - union { - __IOM uint32_t DMABCOUNT; /*!< (@ 0x00000264) DMA BYTE Transfer Count */ - - struct { - __IOM uint32_t BCOUNT : 8; /*!< [7..0] Burst transfer size in bytes. This is the number of bytes - transferred when a FIFO trigger event occurs. Recommended - values are 16 or 32. */ - } DMABCOUNT_b; - } ; - __IM uint32_t RESERVED4[4]; - - union { - __IOM uint32_t DMATHRESH; /*!< (@ 0x00000278) DMA Transmit Trigger Threshhold */ - - struct { - __IOM uint32_t DMATHRESH : 4; /*!< [3..0] DMA transfer FIFO level trigger. For read operations, - DMA is triggered when the FIFO level is greater than this - value. For write operations, DMA is triggered when the - FIFO level is less than this level. Each DMA operation - will consist of BCOUNT bytes. */ - } DMATHRESH_b; - } ; - __IM uint32_t RESERVED5[9]; - - union { - __IOM uint32_t CQCFG; /*!< (@ 0x000002A0) Command Queue Configuration Register */ - - struct { - __IOM uint32_t CQEN : 1; /*!< [0..0] Command queue enable. When set, will enable the processing - of the command queue */ - __IOM uint32_t CQPRI : 1; /*!< [1..1] Sets the Priority of the command queue dma request */ - __IOM uint32_t CQPWROFF : 1; /*!< [2..2] Power off MSPI domain upon completion of DMA operation. */ - __IOM uint32_t CQAUTOCLEARMASK : 1; /*!< [3..3] Eanble clear of CQMASK after each pause operation. This - may be useful when using software flags to pause CQ. */ - } CQCFG_b; - } ; - __IM uint32_t RESERVED6; - - union { - __IOM uint32_t CQADDR; /*!< (@ 0x000002A8) CQ Target Read Address Register */ - - struct { - __IOM uint32_t CQADDR : 29; /*!< [28..0] Address of command queue buffer in SRAM or flash. The - buffer address must be aligned to a word boundary. */ - } CQADDR_b; - } ; - - union { - __IOM uint32_t CQSTAT; /*!< (@ 0x000002AC) Command Queue Status Register */ - - struct { - __IOM uint32_t CQTIP : 1; /*!< [0..0] Command queue Transfer In Progress indicator. 1 will - indicate that a CQ transfer is active and this will remain - active even when paused waiting for external event. */ - __IOM uint32_t CQCPL : 1; /*!< [1..1] Command queue operation Complete. This signals the end - of the command queue operation. */ - __IOM uint32_t CQERR : 1; /*!< [2..2] Command queue processing Error. This active high bit - signals that an error was encountered during the CQ operation. */ - __IOM uint32_t CQPAUSED : 1; /*!< [3..3] Command queue is currently paused status. */ - } CQSTAT_b; - } ; - - union { - __IOM uint32_t CQFLAGS; /*!< (@ 0x000002B0) Command Queue Flag Register */ - - struct { - __IOM uint32_t CQFLAGS : 16; /*!< [15..0] Current flag status (read-only). Bits [7:0] are software - controllable and bits [15:8] are hardware status. */ - } CQFLAGS_b; - } ; - - union { - __IOM uint32_t CQSETCLEAR; /*!< (@ 0x000002B4) Command Queue Flag Set/Clear Register */ - - struct { - __IOM uint32_t CQFSET : 8; /*!< [7..0] Set CQFlag status bits. Set has priority over clear if - both are high. */ - __IOM uint32_t CQFTOGGLE : 8; /*!< [15..8] Toggle CQFlag status bits */ - __IOM uint32_t CQFCLR : 8; /*!< [23..16] Clear CQFlag status bits. */ - } CQSETCLEAR_b; - } ; - - union { - __IOM uint32_t CQPAUSE; /*!< (@ 0x000002B8) Command Queue Pause Mask Register */ - - struct { - __IOM uint32_t CQMASK : 16; /*!< [15..0] CQ will pause processing until all specified events - are satisfied. */ - } CQPAUSE_b; - } ; - __IM uint32_t RESERVED7; - - union { - __IOM uint32_t CQCURIDX; /*!< (@ 0x000002C0) Command Queue Current Index */ - - struct { - __IOM uint32_t CQCURIDX : 8; /*!< [7..0] Can be used to indicate the current position of the command - queue by having CQ operations write this field. A CQ hardware - status flag indicates when CURIDX and ENDIDX are not equal, - allowing SW to pause the CQ processing until the end index - is updated. */ - } CQCURIDX_b; - } ; - - union { - __IOM uint32_t CQENDIDX; /*!< (@ 0x000002C4) Command Queue End Index */ - - struct { - __IOM uint32_t CQENDIDX : 8; /*!< [7..0] Can be used to indicate the end position of the command - queue. A CQ hardware status bit indices when CURIDX != - ENDIDX so that the CQ can be paused when it reaches the - end pointer. */ - } CQENDIDX_b; - } ; -} MSPI_Type; /*!< Size = 712 (0x2c8) */ - - - -/* =========================================================================================================================== */ -/* ================ PDM ================ */ -/* =========================================================================================================================== */ - - -/** - * @brief PDM Audio (PDM) - */ - -typedef struct { /*!< (@ 0x50011000) PDM Structure */ - - union { - __IOM uint32_t PCFG; /*!< (@ 0x00000000) PDM Configuration Register */ - - struct { - __IOM uint32_t PDMCOREEN : 1; /*!< [0..0] Data Streaming Control. */ - __IOM uint32_t SOFTMUTE : 1; /*!< [1..1] Soft mute control. */ - __IOM uint32_t CYCLES : 3; /*!< [4..2] Number of clocks during gain-setting changes. */ - __IOM uint32_t HPCUTOFF : 4; /*!< [8..5] High pass filter coefficients. */ - __IOM uint32_t ADCHPD : 1; /*!< [9..9] High pass filter control. */ - __IOM uint32_t SINCRATE : 7; /*!< [16..10] SINC decimation rate. */ - __IOM uint32_t MCLKDIV : 2; /*!< [18..17] PDM_CLK frequency divisor. */ - __IM uint32_t : 2; - __IOM uint32_t PGALEFT : 5; /*!< [25..21] Left channel PGA gain. */ - __IOM uint32_t PGARIGHT : 5; /*!< [30..26] Right channel PGA gain. */ - __IOM uint32_t LRSWAP : 1; /*!< [31..31] Left/right channel swap. */ - } PCFG_b; - } ; - - union { - __IOM uint32_t VCFG; /*!< (@ 0x00000004) Voice Configuration Register */ - - struct { - __IM uint32_t : 3; - __IOM uint32_t CHSET : 2; /*!< [4..3] Set PCM channels. */ - __IM uint32_t : 3; - __IOM uint32_t PCMPACK : 1; /*!< [8..8] PCM data packing enable. */ - __IM uint32_t : 7; - __IOM uint32_t SELAP : 1; /*!< [16..16] Select PDM input clock source. */ - __IOM uint32_t DMICKDEL : 1; /*!< [17..17] PDM clock sampling delay. */ - __IM uint32_t : 1; - __IOM uint32_t BCLKINV : 1; /*!< [19..19] I2S BCLK input inversion. */ - __IOM uint32_t I2SEN : 1; /*!< [20..20] I2S interface enable. */ - __IM uint32_t : 5; - __IOM uint32_t PDMCLKEN : 1; /*!< [26..26] Enable the serial clock. */ - __IOM uint32_t PDMCLKSEL : 3; /*!< [29..27] Select the PDM input clock. */ - __IOM uint32_t RSTB : 1; /*!< [30..30] Reset the IP core. */ - __IOM uint32_t IOCLKEN : 1; /*!< [31..31] Enable the IO clock. */ - } VCFG_b; - } ; - - union { - __IOM uint32_t VOICESTAT; /*!< (@ 0x00000008) Voice Status Register */ - - struct { - __IOM uint32_t FIFOCNT : 6; /*!< [5..0] Valid 32-bit entries currently in the FIFO. */ - } VOICESTAT_b; - } ; - - union { - __IOM uint32_t FIFOREAD; /*!< (@ 0x0000000C) FIFO Read */ - - struct { - __IOM uint32_t FIFOREAD : 32; /*!< [31..0] FIFO read data. */ - } FIFOREAD_b; - } ; - - union { - __IOM uint32_t FIFOFLUSH; /*!< (@ 0x00000010) FIFO Flush */ - - struct { - __IOM uint32_t FIFOFLUSH : 1; /*!< [0..0] FIFO FLUSH. */ - } FIFOFLUSH_b; - } ; - - union { - __IOM uint32_t FIFOTHR; /*!< (@ 0x00000014) FIFO Threshold */ - - struct { - __IOM uint32_t FIFOTHR : 5; /*!< [4..0] FIFO Threshold value. When the FIFO count is equal to, - or larger than this value (in words), a THR interrupt is - generated (if enabled) */ - } FIFOTHR_b; - } ; - __IM uint32_t RESERVED[122]; - - union { - __IOM uint32_t INTEN; /*!< (@ 0x00000200) IO Master Interrupts: Enable */ - - struct { - __IOM uint32_t THR : 1; /*!< [0..0] This is the FIFO threshold interrupt. */ - __IOM uint32_t OVF : 1; /*!< [1..1] This is the FIFO overflow interrupt. */ - __IOM uint32_t UNDFL : 1; /*!< [2..2] This is the FIFO underflow interrupt. */ - __IOM uint32_t DCMP : 1; /*!< [3..3] DMA completed a transfer */ - __IOM uint32_t DERR : 1; /*!< [4..4] DMA Error receieved */ - } INTEN_b; - } ; - - union { - __IOM uint32_t INTSTAT; /*!< (@ 0x00000204) IO Master Interrupts: Status */ - - struct { - __IOM uint32_t THR : 1; /*!< [0..0] This is the FIFO threshold interrupt. */ - __IOM uint32_t OVF : 1; /*!< [1..1] This is the FIFO overflow interrupt. */ - __IOM uint32_t UNDFL : 1; /*!< [2..2] This is the FIFO underflow interrupt. */ - __IOM uint32_t DCMP : 1; /*!< [3..3] DMA completed a transfer */ - __IOM uint32_t DERR : 1; /*!< [4..4] DMA Error receieved */ - } INTSTAT_b; - } ; - - union { - __IOM uint32_t INTCLR; /*!< (@ 0x00000208) IO Master Interrupts: Clear */ - - struct { - __IOM uint32_t THR : 1; /*!< [0..0] This is the FIFO threshold interrupt. */ - __IOM uint32_t OVF : 1; /*!< [1..1] This is the FIFO overflow interrupt. */ - __IOM uint32_t UNDFL : 1; /*!< [2..2] This is the FIFO underflow interrupt. */ - __IOM uint32_t DCMP : 1; /*!< [3..3] DMA completed a transfer */ - __IOM uint32_t DERR : 1; /*!< [4..4] DMA Error receieved */ - } INTCLR_b; - } ; - - union { - __IOM uint32_t INTSET; /*!< (@ 0x0000020C) IO Master Interrupts: Set */ - - struct { - __IOM uint32_t THR : 1; /*!< [0..0] This is the FIFO threshold interrupt. */ - __IOM uint32_t OVF : 1; /*!< [1..1] This is the FIFO overflow interrupt. */ - __IOM uint32_t UNDFL : 1; /*!< [2..2] This is the FIFO underflow interrupt. */ - __IOM uint32_t DCMP : 1; /*!< [3..3] DMA completed a transfer */ - __IOM uint32_t DERR : 1; /*!< [4..4] DMA Error receieved */ - } INTSET_b; - } ; - __IM uint32_t RESERVED1[12]; - - union { - __IOM uint32_t DMATRIGEN; /*!< (@ 0x00000240) DMA Trigger Enable Register */ - - struct { - __IOM uint32_t DTHR : 1; /*!< [0..0] Trigger DMA upon when FIFO iss filled to level indicated - by the FIFO THRESHOLD,at granularity of 16 bytes only */ - __IOM uint32_t DTHR90 : 1; /*!< [1..1] Trigger DMA at FIFO 90 percent full. This signal is also - used internally for AUTOHIP function */ - } DMATRIGEN_b; - } ; - - union { - __IOM uint32_t DMATRIGSTAT; /*!< (@ 0x00000244) DMA Trigger Status Register */ - - struct { - __IOM uint32_t DTHRSTAT : 1; /*!< [0..0] Triggered DMA from FIFO reaching threshold */ - __IOM uint32_t DTHR90STAT : 1; /*!< [1..1] Triggered DMA from FIFO reaching 90 percent full */ - } DMATRIGSTAT_b; - } ; - __IM uint32_t RESERVED2[14]; - - union { - __IOM uint32_t DMACFG; /*!< (@ 0x00000280) DMA Configuration Register */ - - struct { - __IOM uint32_t DMAEN : 1; /*!< [0..0] DMA Enable */ - __IM uint32_t : 1; - __IOM uint32_t DMADIR : 1; /*!< [2..2] Direction */ - __IM uint32_t : 5; - __IOM uint32_t DMAPRI : 1; /*!< [8..8] Sets the Priority of the DMA request */ - __IOM uint32_t DAUTOHIP : 1; /*!< [9..9] Raise priority to high on fifo full, and DMAPRI set to - low */ - __IOM uint32_t DPWROFF : 1; /*!< [10..10] Power Off the ADC System upon DMACPL. */ - } DMACFG_b; - } ; - __IM uint32_t RESERVED3; - - union { - __IOM uint32_t DMATOTCOUNT; /*!< (@ 0x00000288) DMA Total Transfer Count */ - - struct { - __IOM uint32_t TOTCOUNT : 20; /*!< [19..0] Total Transfer Count. The transfer count must be a multiple - of the THR setting to avoid DMA overruns. */ - } DMATOTCOUNT_b; - } ; - - union { - __IOM uint32_t DMATARGADDR; /*!< (@ 0x0000028C) DMA Target Address Register */ - - struct { - __IOM uint32_t LTARGADDR : 20; /*!< [19..0] DMA Target Address. This register is not updated with - the current address of the DMA, but will remain static - with the original address during the DMA transfer. */ - __IOM uint32_t UTARGADDR : 12; /*!< [31..20] SRAM Target */ - } DMATARGADDR_b; - } ; - - union { - __IOM uint32_t DMASTAT; /*!< (@ 0x00000290) DMA Status Register */ - - struct { - __IOM uint32_t DMATIP : 1; /*!< [0..0] DMA Transfer In Progress */ - __IOM uint32_t DMACPL : 1; /*!< [1..1] DMA Transfer Complete */ - __IOM uint32_t DMAERR : 1; /*!< [2..2] DMA Error */ - } DMASTAT_b; - } ; -} PDM_Type; /*!< Size = 660 (0x294) */ - - - -/* =========================================================================================================================== */ -/* ================ PWRCTRL ================ */ -/* =========================================================================================================================== */ - - -/** - * @brief PWR Controller Register Bank (PWRCTRL) - */ - -typedef struct { /*!< (@ 0x40021000) PWRCTRL Structure */ - - union { - __IOM uint32_t SUPPLYSRC; /*!< (@ 0x00000000) Voltage Regulator Select Register */ - - struct { - __IOM uint32_t BLEBUCKEN : 1; /*!< [0..0] Enables and Selects the BLE Buck as the supply for the - BLE power domain or for Burst LDO. It takes the initial - value from Customer INFO space. Buck will be powered up - only if there is an active request for BLEH domain or Burst - mode and appropriate feature is allowed. */ - } SUPPLYSRC_b; - } ; - - union { - __IOM uint32_t SUPPLYSTATUS; /*!< (@ 0x00000004) Voltage Regulators status */ - - struct { - __IOM uint32_t SIMOBUCKON : 1; /*!< [0..0] Indicates whether the Core/Mem low-voltage domains are - supplied from the LDO or the Buck. */ - __IOM uint32_t BLEBUCKON : 1; /*!< [1..1] Indicates whether the BLE (if supported) domain and burst - (if supported) domain is supplied from the LDO or the Buck. - Buck will be powered up only if there is an active request - for BLEH domain or Burst mode and appropriate reature is - allowed. */ - } SUPPLYSTATUS_b; - } ; - - union { - __IOM uint32_t DEVPWREN; /*!< (@ 0x00000008) Device Power Enables */ - - struct { - __IOM uint32_t PWRIOS : 1; /*!< [0..0] Power up IO Slave */ - __IOM uint32_t PWRIOM0 : 1; /*!< [1..1] Power up IO Master 0 */ - __IOM uint32_t PWRIOM1 : 1; /*!< [2..2] Power up IO Master 1 */ - __IOM uint32_t PWRIOM2 : 1; /*!< [3..3] Power up IO Master 2 */ - __IOM uint32_t PWRIOM3 : 1; /*!< [4..4] Power up IO Master 3 */ - __IOM uint32_t PWRIOM4 : 1; /*!< [5..5] Power up IO Master 4 */ - __IOM uint32_t PWRIOM5 : 1; /*!< [6..6] Power up IO Master 5 */ - __IOM uint32_t PWRUART0 : 1; /*!< [7..7] Power up UART Controller 0 */ - __IOM uint32_t PWRUART1 : 1; /*!< [8..8] Power up UART Controller 1 */ - __IOM uint32_t PWRADC : 1; /*!< [9..9] Power up ADC Digital Controller */ - __IOM uint32_t PWRSCARD : 1; /*!< [10..10] Power up SCARD Controller */ - __IOM uint32_t PWRMSPI : 1; /*!< [11..11] Power up MSPI Controller */ - __IOM uint32_t PWRPDM : 1; /*!< [12..12] Power up PDM block */ - __IOM uint32_t PWRBLEL : 1; /*!< [13..13] Power up BLE controller */ - } DEVPWREN_b; - } ; - - union { - __IOM uint32_t MEMPWDINSLEEP; /*!< (@ 0x0000000C) Powerdown SRAM banks in Deep Sleep mode */ - - struct { - __IOM uint32_t DTCMPWDSLP : 3; /*!< [2..0] power down DTCM in deep sleep */ - __IOM uint32_t SRAMPWDSLP : 10; /*!< [12..3] Selects which SRAM banks are powered down in deep sleep - mode, causing the contents of the bank to be lost. */ - __IOM uint32_t FLASH0PWDSLP : 1; /*!< [13..13] Powerdown flash0 in deep sleep */ - __IOM uint32_t FLASH1PWDSLP : 1; /*!< [14..14] Powerdown flash1 in deep sleep */ - __IM uint32_t : 16; - __IOM uint32_t CACHEPWDSLP : 1; /*!< [31..31] power down cache in deep sleep */ - } MEMPWDINSLEEP_b; - } ; - - union { - __IOM uint32_t MEMPWREN; /*!< (@ 0x00000010) Enables individual banks of the MEMORY array */ - - struct { - __IOM uint32_t DTCM : 3; /*!< [2..0] Power up DTCM */ - __IOM uint32_t SRAM : 10; /*!< [12..3] Power up SRAM groups */ - __IOM uint32_t FLASH0 : 1; /*!< [13..13] Power up Flash0 */ - __IOM uint32_t FLASH1 : 1; /*!< [14..14] Power up Flash1 */ - __IM uint32_t : 15; - __IOM uint32_t CACHEB0 : 1; /*!< [30..30] Power up Cache Bank 0. This works in conjunction with - Cache enable from flash_cache module. To power up cache - bank0, cache has to be enabled and this bit has to be set. */ - __IOM uint32_t CACHEB2 : 1; /*!< [31..31] Power up Cache Bank 2. This works in conjunction with - Cache enable from flash_cache module. To power up cache - bank2, cache has to be enabled and this bit has to be set. */ - } MEMPWREN_b; - } ; - - union { - __IOM uint32_t MEMPWRSTATUS; /*!< (@ 0x00000014) Mem Power ON Status */ - - struct { - __IOM uint32_t DTCM00 : 1; /*!< [0..0] This bit is 1 if power is supplied to DTCM GROUP0_0 */ - __IOM uint32_t DTCM01 : 1; /*!< [1..1] This bit is 1 if power is supplied to DTCM GROUP0_1 */ - __IOM uint32_t DTCM1 : 1; /*!< [2..2] This bit is 1 if power is supplied to DTCM GROUP1 */ - __IOM uint32_t SRAM0 : 1; /*!< [3..3] This bit is 1 if power is supplied to SRAM GROUP0 */ - __IOM uint32_t SRAM1 : 1; /*!< [4..4] This bit is 1 if power is supplied to SRAM GROUP1 */ - __IOM uint32_t SRAM2 : 1; /*!< [5..5] This bit is 1 if power is supplied to SRAM GROUP2 */ - __IOM uint32_t SRAM3 : 1; /*!< [6..6] This bit is 1 if power is supplied to SRAM GROUP3 */ - __IOM uint32_t SRAM4 : 1; /*!< [7..7] This bit is 1 if power is supplied to SRAM GROUP4 */ - __IOM uint32_t SRAM5 : 1; /*!< [8..8] This bit is 1 if power is supplied to SRAM GROUP5 */ - __IOM uint32_t SRAM6 : 1; /*!< [9..9] This bit is 1 if power is supplied to SRAM GROUP6 */ - __IOM uint32_t SRAM7 : 1; /*!< [10..10] This bit is 1 if power is supplied to SRAM GROUP7 */ - __IOM uint32_t SRAM8 : 1; /*!< [11..11] This bit is 1 if power is supplied to SRAM GROUP8 */ - __IOM uint32_t SRAM9 : 1; /*!< [12..12] This bit is 1 if power is supplied to SRAM GROUP9 */ - __IOM uint32_t FLASH0 : 1; /*!< [13..13] This bit is 1 if power is supplied to FLASH 0 */ - __IOM uint32_t FLASH1 : 1; /*!< [14..14] This bit is 1 if power is supplied to FLASH 1 */ - __IOM uint32_t CACHEB0 : 1; /*!< [15..15] This bit is 1 if power is supplied to Cache Bank 0 */ - __IOM uint32_t CACHEB2 : 1; /*!< [16..16] This bit is 1 if power is supplied to Cache Bank 2 */ - } MEMPWRSTATUS_b; - } ; - - union { - __IOM uint32_t DEVPWRSTATUS; /*!< (@ 0x00000018) Device Power ON Status */ - - struct { - __IOM uint32_t MCUL : 1; /*!< [0..0] This bit is 1 if power is supplied to MCUL */ - __IOM uint32_t MCUH : 1; /*!< [1..1] This bit is 1 if power is supplied to MCUH */ - __IOM uint32_t HCPA : 1; /*!< [2..2] This bit is 1 if power is supplied to HCPA domain (IO - SLAVE, UART0, UART1, SCARD) */ - __IOM uint32_t HCPB : 1; /*!< [3..3] This bit is 1 if power is supplied to HCPB domain (IO - MASTER 0, 1, 2) */ - __IOM uint32_t HCPC : 1; /*!< [4..4] This bit is 1 if power is supplied to HCPC domain (IO - MASTER4, 5, 6) */ - __IOM uint32_t PWRADC : 1; /*!< [5..5] This bit is 1 if power is supplied to ADC */ - __IOM uint32_t PWRMSPI : 1; /*!< [6..6] This bit is 1 if power is supplied to MSPI */ - __IOM uint32_t PWRPDM : 1; /*!< [7..7] This bit is 1 if power is supplied to PDM */ - __IOM uint32_t BLEL : 1; /*!< [8..8] This bit is 1 if power is supplied to BLEL */ - __IOM uint32_t BLEH : 1; /*!< [9..9] This bit is 1 if power is supplied to BLEH */ - __IM uint32_t : 19; - __IOM uint32_t CORESLEEP : 1; /*!< [29..29] This bit is 1 if CORE has been in SLEEP State. Write - '1' to this bit to clear it. */ - __IOM uint32_t COREDEEPSLEEP : 1; /*!< [30..30] This bit is 1 if CORE has been in Deep Sleep. Write - '1' to this bit to clear it. */ - __IOM uint32_t SYSDEEPSLEEP : 1; /*!< [31..31] This bit is 1 if SYSTEM has been in Deep Sleep. Write - '1' to this bit to clear it. */ - } DEVPWRSTATUS_b; - } ; - - union { - __IOM uint32_t SRAMCTRL; /*!< (@ 0x0000001C) SRAM Control register */ - - struct { - __IM uint32_t : 1; - __IOM uint32_t SRAMCLKGATE : 1; /*!< [1..1] This bit is 1 if clock gating is allowed for individual - system SRAMs */ - __IOM uint32_t SRAMMASTERCLKGATE : 1; /*!< [2..2] This bit is 1 when the master clock gate is enabled (top-level - clock gate for entire SRAM block) */ - __IM uint32_t : 5; - __IOM uint32_t SRAMLIGHTSLEEP : 12; /*!< [19..8] Light Sleep enable for each TCM/SRAM bank. When 1, corresponding - bank will be put into light sleep. For optimal power, banks - should be put into light sleep while the system is active - but the bank has minimal or no accesses. */ - } SRAMCTRL_b; - } ; - - union { - __IOM uint32_t ADCSTATUS; /*!< (@ 0x00000020) Power Status Register for ADC Block */ - - struct { - __IOM uint32_t ADCPWD : 1; /*!< [0..0] This bit indicates that the ADC is powered down */ - __IOM uint32_t BGTPWD : 1; /*!< [1..1] This bit indicates that the ADC Band Gap is powered down */ - __IOM uint32_t VPTATPWD : 1; /*!< [2..2] This bit indicates that the ADC temperature sensor input - buffer is powered down */ - __IOM uint32_t VBATPWD : 1; /*!< [3..3] This bit indicates that the ADC VBAT resistor divider - is powered down */ - __IOM uint32_t REFKEEPPWD : 1; /*!< [4..4] This bit indicates that the ADC REFKEEP is powered down */ - __IOM uint32_t REFBUFPWD : 1; /*!< [5..5] This bit indicates that the ADC REFBUF is powered down */ - } ADCSTATUS_b; - } ; - - union { - __IOM uint32_t MISC; /*!< (@ 0x00000024) Power Optimization Control Bits */ - - struct { - __IOM uint32_t SIMOBUCKEN : 1; /*!< [0..0] Enables and Selects the SIMO Buck as the supply for the - low-voltage power domain. It takes the initial value from - the bit set in Customer INFO space. */ - __IOM uint32_t FORCECOREVRLPPDM : 1; /*!< [1..1] Control bit to enable the core VR to go into LP mode - with HCPA/B/C/MSPI are powered off but PDM is powered on */ - __IOM uint32_t FORCECOREVRLPTIMERS : 1; /*!< [2..2] Control Bit to force Core VR to LP mode in deep sleep - even when hfrc based ctimer or stimer is running. */ - __IOM uint32_t FORCEMEMVRLPTIMERS : 1; /*!< [3..3] Control Bit to force Mem VR to LP mode in deep sleep - even when hfrc based ctimer or stimer is running. */ - __IOM uint32_t FORCEMEMVRADC : 2; /*!< [5..4] Control Bit to force mem VR to LP or ACT mode in deep - sleep when ADC is powered ON. 0x3 results in picking LP - mode. */ - __IOM uint32_t MEMVRLPBLE : 1; /*!< [6..6] Control Bit to let Mem VR go to lp mode in deep sleep - even when BLEL or BLEH is powered on given none of the - other domains require it. */ - __IOM uint32_t FORCEBLEBUCKACT : 1; /*!< [7..7] Control Bit to enable BLE Buck to be in active state - when BLE Buck is enabled. Default behavior is to be in - active only when Burst or BLEH power on are requested. */ - } MISC_b; - } ; - - union { - __IOM uint32_t DEVPWREVENTEN; /*!< (@ 0x00000028) Event enable register to control which DEVPWRSTATUS - bits are routed to event input of CPU. */ - - struct { - __IOM uint32_t MCULEVEN : 1; /*!< [0..0] Control MCUL power-on status event */ - __IOM uint32_t MCUHEVEN : 1; /*!< [1..1] Control MCUH power-on status event */ - __IOM uint32_t HCPAEVEN : 1; /*!< [2..2] Control HCPA power-on status event */ - __IOM uint32_t HCPBEVEN : 1; /*!< [3..3] Control HCPB power-on status event */ - __IOM uint32_t HCPCEVEN : 1; /*!< [4..4] Control HCPC power-on status event */ - __IOM uint32_t ADCEVEN : 1; /*!< [5..5] Control ADC power-on status event */ - __IOM uint32_t MSPIEVEN : 1; /*!< [6..6] Control MSPI power-on status event */ - __IOM uint32_t PDMEVEN : 1; /*!< [7..7] Control PDM power-on status event */ - __IOM uint32_t BLELEVEN : 1; /*!< [8..8] Control BLE power-on status event */ - __IM uint32_t : 20; - __IOM uint32_t BLEFEATUREEVEN : 1; /*!< [29..29] Control BLEFEATURE status event */ - __IOM uint32_t BURSTFEATUREEVEN : 1; /*!< [30..30] Control BURSTFEATURE status event */ - __IOM uint32_t BURSTEVEN : 1; /*!< [31..31] Control BURST status event */ - } DEVPWREVENTEN_b; - } ; - - union { - __IOM uint32_t MEMPWREVENTEN; /*!< (@ 0x0000002C) Event enable register to control which MEMPWRSTATUS - bits are routed to event input of CPU. */ - - struct { - __IOM uint32_t DTCMEN : 3; /*!< [2..0] Enable DTCM power-on status event */ - __IOM uint32_t SRAMEN : 10; /*!< [12..3] Control SRAM power-on status event */ - __IOM uint32_t FLASH0EN : 1; /*!< [13..13] Control Flash power-on status event */ - __IOM uint32_t FLASH1EN : 1; /*!< [14..14] Control Flash power-on status event */ - __IM uint32_t : 15; - __IOM uint32_t CACHEB0EN : 1; /*!< [30..30] Control CACHE BANK 0 power-on status event */ - __IOM uint32_t CACHEB2EN : 1; /*!< [31..31] Control CACHEB2 power-on status event */ - } MEMPWREVENTEN_b; - } ; -} PWRCTRL_Type; /*!< Size = 48 (0x30) */ - - - -/* =========================================================================================================================== */ -/* ================ RSTGEN ================ */ -/* =========================================================================================================================== */ - - -/** - * @brief MCU Reset Generator (RSTGEN) - */ - -typedef struct { /*!< (@ 0x40000000) RSTGEN Structure */ - - union { - __IOM uint32_t CFG; /*!< (@ 0x00000000) Configuration Register */ - - struct { - __IOM uint32_t BODHREN : 1; /*!< [0..0] Brown out high (2.1v) reset enable. */ - __IOM uint32_t WDREN : 1; /*!< [1..1] Watchdog Timer Reset Enable. NOTE: The WDT module must - also be configured for WDT reset. This includes enabling - the RESEN bit in WDTCFG register in Watch dog timer block. */ - } CFG_b; - } ; - - union { - __IOM uint32_t SWPOI; /*!< (@ 0x00000004) Software POI Reset */ - - struct { - __IOM uint32_t SWPOIKEY : 8; /*!< [7..0] 0x1B generates a software POI reset. This is a write-only - register. Reading from this register will yield only all - 0s. */ - } SWPOI_b; - } ; - - union { - __IOM uint32_t SWPOR; /*!< (@ 0x00000008) Software POR Reset */ - - struct { - __IOM uint32_t SWPORKEY : 8; /*!< [7..0] 0xD4 generates a software POR reset. */ - } SWPOR_b; - } ; - __IM uint32_t RESERVED[2]; - - union { - __IOM uint32_t TPIURST; /*!< (@ 0x00000014) TPIU reset */ - - struct { - __IOM uint32_t TPIURST : 1; /*!< [0..0] Static reset for the TPIU. Write to '1' to assert reset - to TPIU. Write to '0' to clear the reset. */ - } TPIURST_b; - } ; - __IM uint32_t RESERVED1[122]; - - union { - __IOM uint32_t INTEN; /*!< (@ 0x00000200) Reset Interrupt register: Enable */ - - struct { - __IOM uint32_t BODH : 1; /*!< [0..0] Enables an interrupt that triggers when VCC is below - BODH level. */ - } INTEN_b; - } ; - - union { - __IOM uint32_t INTSTAT; /*!< (@ 0x00000204) Reset Interrupt register: Status */ - - struct { - __IOM uint32_t BODH : 1; /*!< [0..0] Enables an interrupt that triggers when VCC is below - BODH level. */ - } INTSTAT_b; - } ; - - union { - __IOM uint32_t INTCLR; /*!< (@ 0x00000208) Reset Interrupt register: Clear */ - - struct { - __IOM uint32_t BODH : 1; /*!< [0..0] Enables an interrupt that triggers when VCC is below - BODH level. */ - } INTCLR_b; - } ; - - union { - __IOM uint32_t INTSET; /*!< (@ 0x0000020C) Reset Interrupt register: Set */ - - struct { - __IOM uint32_t BODH : 1; /*!< [0..0] Enables an interrupt that triggers when VCC is below - BODH level. */ - } INTSET_b; - } ; - __IM uint32_t RESERVED2[67107708]; - - union { - __IOM uint32_t STAT; /*!< (@ 0x0FFFF000) Status Register (SBL) */ - - struct { - __IOM uint32_t EXRSTAT : 1; /*!< [0..0] Reset was initiated by an External Reset (SBL). */ - __IOM uint32_t PORSTAT : 1; /*!< [1..1] Reset was initiated by a Power-On Reset (SBL). */ - __IOM uint32_t BORSTAT : 1; /*!< [2..2] Reset was initiated by a Brown-Out Reset (SBL). */ - __IOM uint32_t SWRSTAT : 1; /*!< [3..3] Reset was a initiated by SW POR or AIRCR Reset (SBL). */ - __IOM uint32_t POIRSTAT : 1; /*!< [4..4] Reset was a initiated by Software POI Reset (SBL). */ - __IOM uint32_t DBGRSTAT : 1; /*!< [5..5] Reset was a initiated by Debugger Reset (SBL). */ - __IOM uint32_t WDRSTAT : 1; /*!< [6..6] Reset was initiated by a Watchdog Timer Reset (SBL). */ - __IOM uint32_t BOUSTAT : 1; /*!< [7..7] An Unregulated Supply Brownout Event occurred (SBL). */ - __IOM uint32_t BOCSTAT : 1; /*!< [8..8] A Core Regulator Brownout Event occurred (SBL). */ - __IOM uint32_t BOFSTAT : 1; /*!< [9..9] A Memory Regulator Brownout Event occurred (SBL). */ - __IOM uint32_t BOBSTAT : 1; /*!< [10..10] A BLE/Burst Regulator Brownout Event occurred (SBL). */ - __IM uint32_t : 19; - __IOM uint32_t FBOOT : 1; /*!< [30..30] Set if current boot was initiated by soft reset and - resulted in Fast Boot (SBL). */ - __IOM uint32_t SBOOT : 1; /*!< [31..31] Set when booting securely (SBL). */ - } STAT_b; - } ; -} RSTGEN_Type; /*!< Size = 268431364 (0xffff004) */ - - - -/* =========================================================================================================================== */ -/* ================ RTC ================ */ -/* =========================================================================================================================== */ - - -/** - * @brief Real Time Clock (RTC) - */ - -typedef struct { /*!< (@ 0x40004200) RTC Structure */ - __IM uint32_t RESERVED[16]; - - union { - __IOM uint32_t CTRLOW; /*!< (@ 0x00000040) RTC Counters Lower */ - - struct { - __IOM uint32_t CTR100 : 8; /*!< [7..0] 100ths of a second Counter */ - __IOM uint32_t CTRSEC : 7; /*!< [14..8] Seconds Counter */ - __IM uint32_t : 1; - __IOM uint32_t CTRMIN : 7; /*!< [22..16] Minutes Counter */ - __IM uint32_t : 1; - __IOM uint32_t CTRHR : 6; /*!< [29..24] Hours Counter */ - } CTRLOW_b; - } ; - - union { - __IOM uint32_t CTRUP; /*!< (@ 0x00000044) RTC Counters Upper */ - - struct { - __IOM uint32_t CTRDATE : 6; /*!< [5..0] Date Counter */ - __IM uint32_t : 2; - __IOM uint32_t CTRMO : 5; /*!< [12..8] Months Counter */ - __IM uint32_t : 3; - __IOM uint32_t CTRYR : 8; /*!< [23..16] Years Counter */ - __IOM uint32_t CTRWKDY : 3; /*!< [26..24] Weekdays Counter */ - __IOM uint32_t CB : 1; /*!< [27..27] Century */ - __IOM uint32_t CEB : 1; /*!< [28..28] Century enable */ - __IM uint32_t : 2; - __IOM uint32_t CTERR : 1; /*!< [31..31] Counter read error status. Error is triggered when - software reads the lower word of the counters, and fails - to read the upper counter within 1/100 second. This is - because when the lower counter is read, the upper counter - is held off from incrementing until it is read so that - the full time stamp can be read. */ - } CTRUP_b; - } ; - - union { - __IOM uint32_t ALMLOW; /*!< (@ 0x00000048) RTC Alarms Lower */ - - struct { - __IOM uint32_t ALM100 : 8; /*!< [7..0] 100ths of a second Alarm */ - __IOM uint32_t ALMSEC : 7; /*!< [14..8] Seconds Alarm */ - __IM uint32_t : 1; - __IOM uint32_t ALMMIN : 7; /*!< [22..16] Minutes Alarm */ - __IM uint32_t : 1; - __IOM uint32_t ALMHR : 6; /*!< [29..24] Hours Alarm */ - } ALMLOW_b; - } ; - - union { - __IOM uint32_t ALMUP; /*!< (@ 0x0000004C) RTC Alarms Upper */ - - struct { - __IOM uint32_t ALMDATE : 6; /*!< [5..0] Date Alarm */ - __IM uint32_t : 2; - __IOM uint32_t ALMMO : 5; /*!< [12..8] Months Alarm */ - __IM uint32_t : 3; - __IOM uint32_t ALMWKDY : 3; /*!< [18..16] Weekdays Alarm */ - } ALMUP_b; - } ; - - union { - __IOM uint32_t RTCCTL; /*!< (@ 0x00000050) RTC Control Register */ - - struct { - __IOM uint32_t WRTC : 1; /*!< [0..0] Counter write control */ - __IOM uint32_t RPT : 3; /*!< [3..1] Alarm repeat interval */ - __IOM uint32_t RSTOP : 1; /*!< [4..4] RTC input clock control */ - __IOM uint32_t HR1224 : 1; /*!< [5..5] Hours Counter mode */ - } RTCCTL_b; - } ; - __IM uint32_t RESERVED1[43]; - - union { - __IOM uint32_t INTEN; /*!< (@ 0x00000100) RTC Interrupt Register: Enable */ - - struct { - __IOM uint32_t ALM : 1; /*!< [0..0] RTC Alarm interrupt */ - } INTEN_b; - } ; - - union { - __IOM uint32_t INTSTAT; /*!< (@ 0x00000104) RTC Interrupt Register: Status */ - - struct { - __IOM uint32_t ALM : 1; /*!< [0..0] RTC Alarm interrupt */ - } INTSTAT_b; - } ; - - union { - __IOM uint32_t INTCLR; /*!< (@ 0x00000108) RTC Interrupt Register: Clear */ - - struct { - __IOM uint32_t ALM : 1; /*!< [0..0] RTC Alarm interrupt */ - } INTCLR_b; - } ; - - union { - __IOM uint32_t INTSET; /*!< (@ 0x0000010C) RTC Interrupt Register: Set */ - - struct { - __IOM uint32_t ALM : 1; /*!< [0..0] RTC Alarm interrupt */ - } INTSET_b; - } ; -} RTC_Type; /*!< Size = 272 (0x110) */ - - - -/* =========================================================================================================================== */ -/* ================ SCARD ================ */ -/* =========================================================================================================================== */ - - -/** - * @brief Serial ISO7816 (SCARD) - */ - -typedef struct { /*!< (@ 0x40080000) SCARD Structure */ - - union { - __IOM uint32_t SR; /*!< (@ 0x00000000) ISO7816 interrupt status */ - - struct { - __IOM uint32_t FNE : 1; /*!< [0..0] RX FIFO not empty. */ - __IOM uint32_t TBERBF : 1; /*!< [1..1] FIFO empty (transmit) or full (receive). */ - __IOM uint32_t FER : 1; /*!< [2..2] Framing error. */ - __IOM uint32_t OVR : 1; /*!< [3..3] RX FIFO overflow. */ - __IOM uint32_t PE : 1; /*!< [4..4] Parity Error. */ - __IOM uint32_t FT2REND : 1; /*!< [5..5] TX to RX finished. */ - __IOM uint32_t FHF : 1; /*!< [6..6] FIFO Half Full. */ - } SR_b; - } ; - __IM uint32_t RESERVED[3]; - - union { - __IOM uint32_t DR; /*!< (@ 0x00000010) ISO7816 data */ - - struct { - __IOM uint32_t DR : 8; /*!< [7..0] Data register. */ - } DR_b; - } ; - __IM uint32_t RESERVED1[3]; - - union { - __IOM uint32_t SR1; /*!< (@ 0x00000020) ISO7816 interrupt status 1 */ - - struct { - __IOM uint32_t ECNTOVER : 1; /*!< [0..0] ETU counter overflow. */ - __IOM uint32_t PRL : 1; /*!< [1..1] Card insert/remove. */ - __IOM uint32_t SYNCEND : 1; /*!< [2..2] Write complete synchronization. */ - __IOM uint32_t IDLE : 1; /*!< [3..3] ISO7816 idle. */ - } SR1_b; - } ; - __IM uint32_t RESERVED2[5]; - - union { - __IOM uint32_t RETXCNTRMI; /*!< (@ 0x00000038) ISO7816 resent count inquiry */ - - struct { - __IOM uint32_t RETXCNTRMI : 4; /*!< [3..0] Resent count inquiry register. */ - } RETXCNTRMI_b; - } ; - __IM uint32_t RESERVED3[49]; - - union { - __IOM uint32_t CLKCTRL; /*!< (@ 0x00000100) Clock Control */ - - struct { - __IOM uint32_t CLKEN : 1; /*!< [0..0] Enable the serial source clock for SCARD. */ - __IOM uint32_t APBCLKEN : 1; /*!< [1..1] Enable the SCARD APB clock to run continuously. */ - } CLKCTRL_b; - } ; -} SCARD_Type; /*!< Size = 260 (0x104) */ - - - -/* =========================================================================================================================== */ -/* ================ SECURITY ================ */ -/* =========================================================================================================================== */ - - -/** - * @brief Security Interfaces (SECURITY) - */ - -typedef struct { /*!< (@ 0x40030000) SECURITY Structure */ - - union { - __IOM uint32_t CTRL; /*!< (@ 0x00000000) Control Register */ - - struct { - __IOM uint32_t ENABLE : 1; /*!< [0..0] Function Enable. Software should set the ENABLE bit to - initiate a CRC operation. Hardware will clear the ENABLE - bit upon completion. */ - __IM uint32_t : 3; - __IOM uint32_t FUNCTION : 4; /*!< [7..4] Function Select */ - __IM uint32_t : 23; - __IOM uint32_t CRCERROR : 1; /*!< [31..31] CRC Error Status - Set to 1 if an error occurs during - a CRC operation. Cleared when CTRL register is written - (with any value). Usually indicates an invalid address - range. */ - } CTRL_b; - } ; - __IM uint32_t RESERVED[3]; - - union { - __IOM uint32_t SRCADDR; /*!< (@ 0x00000010) Source Addresss */ - - struct { - __IOM uint32_t ADDR : 32; /*!< [31..0] Source Buffer Address. Address may be byte aligned, - but the length must be a multiple of 4 bits. */ - } SRCADDR_b; - } ; - __IM uint32_t RESERVED1[3]; - - union { - __IOM uint32_t LEN; /*!< (@ 0x00000020) Length */ - - struct { - __IM uint32_t : 2; - __IOM uint32_t LEN : 18; /*!< [19..2] Buffer size (bottom two bits assumed to be zero to ensure - a multiple of 4 bytes) */ - } LEN_b; - } ; - __IM uint32_t RESERVED2[3]; - - union { - __IOM uint32_t RESULT; /*!< (@ 0x00000030) CRC Seed/Result Register */ - - struct { - __IOM uint32_t CRC : 32; /*!< [31..0] CRC Seed/Result. Software must seed the CRC with 0xFFFFFFFF - before starting a CRC operation (unless the CRC is continued - from a previous operation). */ - } RESULT_b; - } ; - __IM uint32_t RESERVED3[17]; - - union { - __IOM uint32_t LOCKCTRL; /*!< (@ 0x00000078) LOCK Control Register */ - - struct { - __IOM uint32_t SELECT : 8; /*!< [7..0] LOCK Function Select register. */ - } LOCKCTRL_b; - } ; - - union { - __IOM uint32_t LOCKSTAT; /*!< (@ 0x0000007C) LOCK Status Register */ - - struct { - __IOM uint32_t STATUS : 32; /*!< [31..0] LOCK Status register. This register is a bitmask for - which resources are currently unlocked. These bits are - one-hot per resource. */ - } LOCKSTAT_b; - } ; - - union { - __IOM uint32_t KEY0; /*!< (@ 0x00000080) Key0 Register */ - - struct { - __IOM uint32_t KEY0 : 32; /*!< [31..0] Bits [31:0] of the 128-bit key should be written to - this register. To protect key values, the register always - returns 0x00000000. */ - } KEY0_b; - } ; - - union { - __IOM uint32_t KEY1; /*!< (@ 0x00000084) Key1 Register */ - - struct { - __IOM uint32_t KEY1 : 32; /*!< [31..0] Bits [63:32] of the 128-bit key should be written to - this register. To protect key values, the register always - returns 0x00000000. */ - } KEY1_b; - } ; - - union { - __IOM uint32_t KEY2; /*!< (@ 0x00000088) Key2 Register */ - - struct { - __IOM uint32_t KEY2 : 32; /*!< [31..0] Bits [95:64] of the 128-bit key should be written to - this register. To protect key values, the register always - returns 0x00000000. */ - } KEY2_b; - } ; - - union { - __IOM uint32_t KEY3; /*!< (@ 0x0000008C) Key3 Register */ - - struct { - __IOM uint32_t KEY3 : 32; /*!< [31..0] Bits [127:96] of the 128-bit key should be written to - this register. To protect key values, the register always - returns 0x00000000. */ - } KEY3_b; - } ; -} SECURITY_Type; /*!< Size = 144 (0x90) */ - - - -/* =========================================================================================================================== */ -/* ================ UART0 ================ */ -/* =========================================================================================================================== */ - - -/** - * @brief Serial UART (UART0) - */ - -typedef struct { /*!< (@ 0x4001C000) UART0 Structure */ - - union { - __IOM uint32_t DR; /*!< (@ 0x00000000) UART Data Register */ - - struct { - __IOM uint32_t DATA : 8; /*!< [7..0] This is the UART data port. */ - __IOM uint32_t FEDATA : 1; /*!< [8..8] This is the framing error indicator. */ - __IOM uint32_t PEDATA : 1; /*!< [9..9] This is the parity error indicator. */ - __IOM uint32_t BEDATA : 1; /*!< [10..10] This is the break error indicator. */ - __IOM uint32_t OEDATA : 1; /*!< [11..11] This is the overrun error indicator. */ - } DR_b; - } ; - - union { - __IOM uint32_t RSR; /*!< (@ 0x00000004) UART Status Register */ - - struct { - __IOM uint32_t FESTAT : 1; /*!< [0..0] This is the framing error indicator. */ - __IOM uint32_t PESTAT : 1; /*!< [1..1] This is the parity error indicator. */ - __IOM uint32_t BESTAT : 1; /*!< [2..2] This is the break error indicator. */ - __IOM uint32_t OESTAT : 1; /*!< [3..3] This is the overrun error indicator. */ - } RSR_b; - } ; - __IM uint32_t RESERVED[4]; - - union { - __IOM uint32_t FR; /*!< (@ 0x00000018) Flag Register */ - - struct { - __IOM uint32_t CTS : 1; /*!< [0..0] This bit holds the clear to send indicator. */ - __IOM uint32_t DSR : 1; /*!< [1..1] This bit holds the data set ready indicator. */ - __IOM uint32_t DCD : 1; /*!< [2..2] This bit holds the data carrier detect indicator. */ - __IOM uint32_t BUSY : 1; /*!< [3..3] This bit holds the busy indicator. */ - __IOM uint32_t RXFE : 1; /*!< [4..4] This bit holds the receive FIFO empty indicator. */ - __IOM uint32_t TXFF : 1; /*!< [5..5] This bit holds the transmit FIFO full indicator. */ - __IOM uint32_t RXFF : 1; /*!< [6..6] This bit holds the receive FIFO full indicator. */ - __IOM uint32_t TXFE : 1; /*!< [7..7] This bit holds the transmit FIFO empty indicator. */ - __IOM uint32_t TXBUSY : 1; /*!< [8..8] This bit holds the transmit BUSY indicator. */ - } FR_b; - } ; - __IM uint32_t RESERVED1; - - union { - __IOM uint32_t ILPR; /*!< (@ 0x00000020) IrDA Counter */ - - struct { - __IOM uint32_t ILPDVSR : 8; /*!< [7..0] These bits hold the IrDA counter divisor. */ - } ILPR_b; - } ; - - union { - __IOM uint32_t IBRD; /*!< (@ 0x00000024) Integer Baud Rate Divisor */ - - struct { - __IOM uint32_t DIVINT : 16; /*!< [15..0] These bits hold the baud integer divisor. */ - } IBRD_b; - } ; - - union { - __IOM uint32_t FBRD; /*!< (@ 0x00000028) Fractional Baud Rate Divisor */ - - struct { - __IOM uint32_t DIVFRAC : 6; /*!< [5..0] These bits hold the baud fractional divisor. */ - } FBRD_b; - } ; - - union { - __IOM uint32_t LCRH; /*!< (@ 0x0000002C) Line Control High */ - - struct { - __IOM uint32_t BRK : 1; /*!< [0..0] This bit holds the break set. */ - __IOM uint32_t PEN : 1; /*!< [1..1] This bit holds the parity enable. */ - __IOM uint32_t EPS : 1; /*!< [2..2] This bit holds the even parity select. */ - __IOM uint32_t STP2 : 1; /*!< [3..3] This bit holds the two stop bits select. */ - __IOM uint32_t FEN : 1; /*!< [4..4] This bit holds the FIFO enable. */ - __IOM uint32_t WLEN : 2; /*!< [6..5] These bits hold the write length. */ - __IOM uint32_t SPS : 1; /*!< [7..7] This bit holds the stick parity select. */ - } LCRH_b; - } ; - - union { - __IOM uint32_t CR; /*!< (@ 0x00000030) Control Register */ - - struct { - __IOM uint32_t UARTEN : 1; /*!< [0..0] This bit is the UART enable. */ - __IOM uint32_t SIREN : 1; /*!< [1..1] This bit is the SIR ENDEC enable. */ - __IOM uint32_t SIRLP : 1; /*!< [2..2] This bit is the SIR low power select. */ - __IOM uint32_t CLKEN : 1; /*!< [3..3] This bit is the UART clock enable. */ - __IOM uint32_t CLKSEL : 3; /*!< [6..4] This bitfield is the UART clock select. */ - __IOM uint32_t LBE : 1; /*!< [7..7] This bit is the loopback enable. */ - __IOM uint32_t TXE : 1; /*!< [8..8] This bit is the transmit enable. */ - __IOM uint32_t RXE : 1; /*!< [9..9] This bit is the receive enable. */ - __IOM uint32_t DTR : 1; /*!< [10..10] This bit enables data transmit ready. */ - __IOM uint32_t RTS : 1; /*!< [11..11] This bit enables request to send. */ - __IOM uint32_t OUT1 : 1; /*!< [12..12] This bit holds modem Out1. */ - __IOM uint32_t OUT2 : 1; /*!< [13..13] This bit holds modem Out2. */ - __IOM uint32_t RTSEN : 1; /*!< [14..14] This bit enables RTS hardware flow control. */ - __IOM uint32_t CTSEN : 1; /*!< [15..15] This bit enables CTS hardware flow control. */ - } CR_b; - } ; - - union { - __IOM uint32_t IFLS; /*!< (@ 0x00000034) FIFO Interrupt Level Select */ - - struct { - __IOM uint32_t TXIFLSEL : 3; /*!< [2..0] These bits hold the transmit FIFO interrupt level. */ - __IOM uint32_t RXIFLSEL : 3; /*!< [5..3] These bits hold the receive FIFO interrupt level. */ - } IFLS_b; - } ; - - union { - __IOM uint32_t IER; /*!< (@ 0x00000038) Interrupt Enable */ - - struct { - __IOM uint32_t TXCMPMIM : 1; /*!< [0..0] This bit holds the modem TXCMP interrupt enable. */ - __IOM uint32_t CTSMIM : 1; /*!< [1..1] This bit holds the modem CTS interrupt enable. */ - __IOM uint32_t DCDMIM : 1; /*!< [2..2] This bit holds the modem DCD interrupt enable. */ - __IOM uint32_t DSRMIM : 1; /*!< [3..3] This bit holds the modem DSR interrupt enable. */ - __IOM uint32_t RXIM : 1; /*!< [4..4] This bit holds the receive interrupt enable. */ - __IOM uint32_t TXIM : 1; /*!< [5..5] This bit holds the transmit interrupt enable. */ - __IOM uint32_t RTIM : 1; /*!< [6..6] This bit holds the receive timeout interrupt enable. */ - __IOM uint32_t FEIM : 1; /*!< [7..7] This bit holds the framing error interrupt enable. */ - __IOM uint32_t PEIM : 1; /*!< [8..8] This bit holds the parity error interrupt enable. */ - __IOM uint32_t BEIM : 1; /*!< [9..9] This bit holds the break error interrupt enable. */ - __IOM uint32_t OEIM : 1; /*!< [10..10] This bit holds the overflow interrupt enable. */ - } IER_b; - } ; - - union { - __IOM uint32_t IES; /*!< (@ 0x0000003C) Interrupt Status */ - - struct { - __IOM uint32_t TXCMPMRIS : 1; /*!< [0..0] This bit holds the modem TXCMP interrupt status. */ - __IOM uint32_t CTSMRIS : 1; /*!< [1..1] This bit holds the modem CTS interrupt status. */ - __IOM uint32_t DCDMRIS : 1; /*!< [2..2] This bit holds the modem DCD interrupt status. */ - __IOM uint32_t DSRMRIS : 1; /*!< [3..3] This bit holds the modem DSR interrupt status. */ - __IOM uint32_t RXRIS : 1; /*!< [4..4] This bit holds the receive interrupt status. */ - __IOM uint32_t TXRIS : 1; /*!< [5..5] This bit holds the transmit interrupt status. */ - __IOM uint32_t RTRIS : 1; /*!< [6..6] This bit holds the receive timeout interrupt status. */ - __IOM uint32_t FERIS : 1; /*!< [7..7] This bit holds the framing error interrupt status. */ - __IOM uint32_t PERIS : 1; /*!< [8..8] This bit holds the parity error interrupt status. */ - __IOM uint32_t BERIS : 1; /*!< [9..9] This bit holds the break error interrupt status. */ - __IOM uint32_t OERIS : 1; /*!< [10..10] This bit holds the overflow interrupt status. */ - } IES_b; - } ; - - union { - __IOM uint32_t MIS; /*!< (@ 0x00000040) Masked Interrupt Status */ - - struct { - __IOM uint32_t TXCMPMMIS : 1; /*!< [0..0] This bit holds the modem TXCMP interrupt status masked. */ - __IOM uint32_t CTSMMIS : 1; /*!< [1..1] This bit holds the modem CTS interrupt status masked. */ - __IOM uint32_t DCDMMIS : 1; /*!< [2..2] This bit holds the modem DCD interrupt status masked. */ - __IOM uint32_t DSRMMIS : 1; /*!< [3..3] This bit holds the modem DSR interrupt status masked. */ - __IOM uint32_t RXMIS : 1; /*!< [4..4] This bit holds the receive interrupt status masked. */ - __IOM uint32_t TXMIS : 1; /*!< [5..5] This bit holds the transmit interrupt status masked. */ - __IOM uint32_t RTMIS : 1; /*!< [6..6] This bit holds the receive timeout interrupt status masked. */ - __IOM uint32_t FEMIS : 1; /*!< [7..7] This bit holds the framing error interrupt status masked. */ - __IOM uint32_t PEMIS : 1; /*!< [8..8] This bit holds the parity error interrupt status masked. */ - __IOM uint32_t BEMIS : 1; /*!< [9..9] This bit holds the break error interrupt status masked. */ - __IOM uint32_t OEMIS : 1; /*!< [10..10] This bit holds the overflow interrupt status masked. */ - } MIS_b; - } ; - - union { - __IOM uint32_t IEC; /*!< (@ 0x00000044) Interrupt Clear */ - - struct { - __IOM uint32_t TXCMPMIC : 1; /*!< [0..0] This bit holds the modem TXCMP interrupt clear. */ - __IOM uint32_t CTSMIC : 1; /*!< [1..1] This bit holds the modem CTS interrupt clear. */ - __IOM uint32_t DCDMIC : 1; /*!< [2..2] This bit holds the modem DCD interrupt clear. */ - __IOM uint32_t DSRMIC : 1; /*!< [3..3] This bit holds the modem DSR interrupt clear. */ - __IOM uint32_t RXIC : 1; /*!< [4..4] This bit holds the receive interrupt clear. */ - __IOM uint32_t TXIC : 1; /*!< [5..5] This bit holds the transmit interrupt clear. */ - __IOM uint32_t RTIC : 1; /*!< [6..6] This bit holds the receive timeout interrupt clear. */ - __IOM uint32_t FEIC : 1; /*!< [7..7] This bit holds the framing error interrupt clear. */ - __IOM uint32_t PEIC : 1; /*!< [8..8] This bit holds the parity error interrupt clear. */ - __IOM uint32_t BEIC : 1; /*!< [9..9] This bit holds the break error interrupt clear. */ - __IOM uint32_t OEIC : 1; /*!< [10..10] This bit holds the overflow interrupt clear. */ - } IEC_b; - } ; -} UART0_Type; /*!< Size = 72 (0x48) */ - - - -/* =========================================================================================================================== */ -/* ================ VCOMP ================ */ -/* =========================================================================================================================== */ - - -/** - * @brief Voltage Comparator (VCOMP) - */ - -typedef struct { /*!< (@ 0x4000C000) VCOMP Structure */ - - union { - __IOM uint32_t CFG; /*!< (@ 0x00000000) Configuration Register */ - - struct { - __IOM uint32_t PSEL : 2; /*!< [1..0] This bitfield selects the positive input to the comparator. */ - __IM uint32_t : 6; - __IOM uint32_t NSEL : 2; /*!< [9..8] This bitfield selects the negative input to the comparator. */ - __IM uint32_t : 6; - __IOM uint32_t LVLSEL : 4; /*!< [19..16] When the reference input NSEL is set to NSEL_DAC, this - bitfield selects the voltage level for the negative input - to the comparator. */ - } CFG_b; - } ; - - union { - __IOM uint32_t STAT; /*!< (@ 0x00000004) Status Register */ - - struct { - __IOM uint32_t CMPOUT : 1; /*!< [0..0] This bit is 1 if the positive input of the comparator - is greater than the negative input. */ - __IOM uint32_t PWDSTAT : 1; /*!< [1..1] This bit indicates the power down state of the voltage - comparator. */ - } STAT_b; - } ; - - union { - __IOM uint32_t PWDKEY; /*!< (@ 0x00000008) Key Register for Powering Down the Voltage Comparator */ - - struct { - __IOM uint32_t PWDKEY : 32; /*!< [31..0] Key register value. */ - } PWDKEY_b; - } ; - __IM uint32_t RESERVED[125]; - - union { - __IOM uint32_t INTEN; /*!< (@ 0x00000200) Voltage Comparator Interrupt registers: Enable */ - - struct { - __IOM uint32_t OUTLOW : 1; /*!< [0..0] This bit is the vcompout low interrupt. */ - __IOM uint32_t OUTHI : 1; /*!< [1..1] This bit is the vcompout high interrupt. */ - } INTEN_b; - } ; - - union { - __IOM uint32_t INTSTAT; /*!< (@ 0x00000204) Voltage Comparator Interrupt registers: Status */ - - struct { - __IOM uint32_t OUTLOW : 1; /*!< [0..0] This bit is the vcompout low interrupt. */ - __IOM uint32_t OUTHI : 1; /*!< [1..1] This bit is the vcompout high interrupt. */ - } INTSTAT_b; - } ; - - union { - __IOM uint32_t INTCLR; /*!< (@ 0x00000208) Voltage Comparator Interrupt registers: Clear */ - - struct { - __IOM uint32_t OUTLOW : 1; /*!< [0..0] This bit is the vcompout low interrupt. */ - __IOM uint32_t OUTHI : 1; /*!< [1..1] This bit is the vcompout high interrupt. */ - } INTCLR_b; - } ; - - union { - __IOM uint32_t INTSET; /*!< (@ 0x0000020C) Voltage Comparator Interrupt registers: Set */ - - struct { - __IOM uint32_t OUTLOW : 1; /*!< [0..0] This bit is the vcompout low interrupt. */ - __IOM uint32_t OUTHI : 1; /*!< [1..1] This bit is the vcompout high interrupt. */ - } INTSET_b; - } ; -} VCOMP_Type; /*!< Size = 528 (0x210) */ - - - -/* =========================================================================================================================== */ -/* ================ WDT ================ */ -/* =========================================================================================================================== */ - - -/** - * @brief Watchdog Timer (WDT) - */ - -typedef struct { /*!< (@ 0x40024000) WDT Structure */ - - union { - __IOM uint32_t CFG; /*!< (@ 0x00000000) Configuration Register */ - - struct { - __IOM uint32_t WDTEN : 1; /*!< [0..0] This bitfield enables the WDT. */ - __IOM uint32_t INTEN : 1; /*!< [1..1] This bitfield enables the WDT interrupt. Note : This - bit must be set before the interrupt status bit will reflect - a watchdog timer expiration. The IER interrupt register - must also be enabled for a WDT interrupt to be sent to - the NVIC. */ - __IOM uint32_t RESEN : 1; /*!< [2..2] This bitfield enables the WDT reset. This needs to be - set together with the WDREN bit in REG_RSTGEN_CFG register - (in reset gen) to trigger the reset. */ - __IM uint32_t : 5; - __IOM uint32_t RESVAL : 8; /*!< [15..8] This bitfield is the compare value for counter bits - 7:0 to generate a watchdog reset. This will cause a software - reset. */ - __IOM uint32_t INTVAL : 8; /*!< [23..16] This bitfield is the compare value for counter bits - 7:0 to generate a watchdog interrupt. */ - __IOM uint32_t CLKSEL : 3; /*!< [26..24] Select the frequency for the WDT. All values not enumerated - below are undefined. */ - } CFG_b; - } ; - - union { - __IOM uint32_t RSTRT; /*!< (@ 0x00000004) Restart the watchdog timer. */ - - struct { - __IOM uint32_t RSTRT : 8; /*!< [7..0] Writing 0xB2 to WDTRSTRT restarts the watchdog timer. - This is a write only register. Reading this register will - only provide all 0. */ - } RSTRT_b; - } ; - - union { - __IOM uint32_t LOCK; /*!< (@ 0x00000008) Locks the WDT */ - - struct { - __IOM uint32_t LOCK : 8; /*!< [7..0] Writing 0x3A locks the watchdog timer. Once locked, the - WDTCFG reg cannot be written and WDTEN is set. */ - } LOCK_b; - } ; - - union { - __IOM uint32_t COUNT; /*!< (@ 0x0000000C) Current Counter Value for WDT */ - - struct { - __IOM uint32_t COUNT : 8; /*!< [7..0] Read-Only current value of the WDT counter */ - } COUNT_b; - } ; - __IM uint32_t RESERVED[124]; - - union { - __IOM uint32_t INTEN; /*!< (@ 0x00000200) WDT Interrupt register: Enable */ - - struct { - __IOM uint32_t WDTINT : 1; /*!< [0..0] Watchdog Timer Interrupt. */ - } INTEN_b; - } ; - - union { - __IOM uint32_t INTSTAT; /*!< (@ 0x00000204) WDT Interrupt register: Status */ - - struct { - __IOM uint32_t WDTINT : 1; /*!< [0..0] Watchdog Timer Interrupt. */ - } INTSTAT_b; - } ; - - union { - __IOM uint32_t INTCLR; /*!< (@ 0x00000208) WDT Interrupt register: Clear */ - - struct { - __IOM uint32_t WDTINT : 1; /*!< [0..0] Watchdog Timer Interrupt. */ - } INTCLR_b; - } ; - - union { - __IOM uint32_t INTSET; /*!< (@ 0x0000020C) WDT Interrupt register: Set */ - - struct { - __IOM uint32_t WDTINT : 1; /*!< [0..0] Watchdog Timer Interrupt. */ - } INTSET_b; - } ; -} WDT_Type; /*!< Size = 528 (0x210) */ - - -/** @} */ /* End of group Device_Peripheral_peripherals */ - - -/* =========================================================================================================================== */ -/* ================ Device Specific Peripheral Address Map ================ */ -/* =========================================================================================================================== */ - - -/** @addtogroup Device_Peripheral_peripheralAddr - * @{ - */ - -#define ADC_BASE 0x50010000UL -#define APBDMA_BASE 0x40011000UL -#define BLEIF_BASE 0x5000C000UL -#define CACHECTRL_BASE 0x40018000UL -#define CLKGEN_BASE 0x40004000UL -#define CTIMER_BASE 0x40008000UL -#define GPIO_BASE 0x40010000UL -#define IOM0_BASE 0x50004000UL -#define IOM1_BASE 0x50005000UL -#define IOM2_BASE 0x50006000UL -#define IOM3_BASE 0x50007000UL -#define IOM4_BASE 0x50008000UL -#define IOM5_BASE 0x50009000UL -#define IOSLAVE_BASE 0x50000000UL -#define MCUCTRL_BASE 0x40020000UL -#define MSPI_BASE 0x50014000UL -#define PDM_BASE 0x50011000UL -#define PWRCTRL_BASE 0x40021000UL -#define RSTGEN_BASE 0x40000000UL -#define RTC_BASE 0x40004200UL -#define SCARD_BASE 0x40080000UL -#define SECURITY_BASE 0x40030000UL -#define UART0_BASE 0x4001C000UL -#define UART1_BASE 0x4001D000UL -#define VCOMP_BASE 0x4000C000UL -#define WDT_BASE 0x40024000UL - -/** @} */ /* End of group Device_Peripheral_peripheralAddr */ - - -/* =========================================================================================================================== */ -/* ================ Peripheral declaration ================ */ -/* =========================================================================================================================== */ - - -/** @addtogroup Device_Peripheral_declaration - * @{ - */ - -#define ADC ((ADC_Type*) ADC_BASE) -#define APBDMA ((APBDMA_Type*) APBDMA_BASE) -#define BLEIF ((BLEIF_Type*) BLEIF_BASE) -#define CACHECTRL ((CACHECTRL_Type*) CACHECTRL_BASE) -#define CLKGEN ((CLKGEN_Type*) CLKGEN_BASE) -#define CTIMER ((CTIMER_Type*) CTIMER_BASE) -#define GPIO ((GPIO_Type*) GPIO_BASE) -#define IOM0 ((IOM0_Type*) IOM0_BASE) -#define IOM1 ((IOM0_Type*) IOM1_BASE) -#define IOM2 ((IOM0_Type*) IOM2_BASE) -#define IOM3 ((IOM0_Type*) IOM3_BASE) -#define IOM4 ((IOM0_Type*) IOM4_BASE) -#define IOM5 ((IOM0_Type*) IOM5_BASE) -#define IOSLAVE ((IOSLAVE_Type*) IOSLAVE_BASE) -#define MCUCTRL ((MCUCTRL_Type*) MCUCTRL_BASE) -#define MSPI ((MSPI_Type*) MSPI_BASE) -#define PDM ((PDM_Type*) PDM_BASE) -#define PWRCTRL ((PWRCTRL_Type*) PWRCTRL_BASE) -#define RSTGEN ((RSTGEN_Type*) RSTGEN_BASE) -#define RTC ((RTC_Type*) RTC_BASE) -#define SCARD ((SCARD_Type*) SCARD_BASE) -#define SECURITY ((SECURITY_Type*) SECURITY_BASE) -#define UART0 ((UART0_Type*) UART0_BASE) -#define UART1 ((UART0_Type*) UART1_BASE) -#define VCOMP ((VCOMP_Type*) VCOMP_BASE) -#define WDT ((WDT_Type*) WDT_BASE) - -/** @} */ /* End of group Device_Peripheral_declaration */ - - -/* ========================================= End of section using anonymous unions ========================================= */ -#if defined (__CC_ARM) - #pragma pop -#elif defined (__ICCARM__) - /* leave anonymous unions enabled */ -#elif (__ARMCC_VERSION >= 6010050) - #pragma clang diagnostic pop -#elif defined (__GNUC__) - /* anonymous unions are enabled by default */ -#elif defined (__TMS470__) - /* anonymous unions are enabled by default */ -#elif defined (__TASKING__) - #pragma warning restore -#elif defined (__CSMC__) - /* anonymous unions are enabled by default */ -#endif - - -/* =========================================================================================================================== */ -/* ================ Pos/Mask Peripheral Section ================ */ -/* =========================================================================================================================== */ - - -/** @addtogroup PosMask_peripherals - * @{ - */ - - - -/* =========================================================================================================================== */ -/* ================ ADC ================ */ -/* =========================================================================================================================== */ - -/* ========================================================== CFG ========================================================== */ -#define ADC_CFG_CLKSEL_Pos (24UL) /*!< ADC CFG: CLKSEL (Bit 24) */ -#define ADC_CFG_CLKSEL_Msk (0x3000000UL) /*!< ADC CFG: CLKSEL (Bitfield-Mask: 0x03) */ -#define ADC_CFG_TRIGPOL_Pos (19UL) /*!< ADC CFG: TRIGPOL (Bit 19) */ -#define ADC_CFG_TRIGPOL_Msk (0x80000UL) /*!< ADC CFG: TRIGPOL (Bitfield-Mask: 0x01) */ -#define ADC_CFG_TRIGSEL_Pos (16UL) /*!< ADC CFG: TRIGSEL (Bit 16) */ -#define ADC_CFG_TRIGSEL_Msk (0x70000UL) /*!< ADC CFG: TRIGSEL (Bitfield-Mask: 0x07) */ -#define ADC_CFG_DFIFORDEN_Pos (12UL) /*!< ADC CFG: DFIFORDEN (Bit 12) */ -#define ADC_CFG_DFIFORDEN_Msk (0x1000UL) /*!< ADC CFG: DFIFORDEN (Bitfield-Mask: 0x01) */ -#define ADC_CFG_REFSEL_Pos (8UL) /*!< ADC CFG: REFSEL (Bit 8) */ -#define ADC_CFG_REFSEL_Msk (0x300UL) /*!< ADC CFG: REFSEL (Bitfield-Mask: 0x03) */ -#define ADC_CFG_CKMODE_Pos (4UL) /*!< ADC CFG: CKMODE (Bit 4) */ -#define ADC_CFG_CKMODE_Msk (0x10UL) /*!< ADC CFG: CKMODE (Bitfield-Mask: 0x01) */ -#define ADC_CFG_LPMODE_Pos (3UL) /*!< ADC CFG: LPMODE (Bit 3) */ -#define ADC_CFG_LPMODE_Msk (0x8UL) /*!< ADC CFG: LPMODE (Bitfield-Mask: 0x01) */ -#define ADC_CFG_RPTEN_Pos (2UL) /*!< ADC CFG: RPTEN (Bit 2) */ -#define ADC_CFG_RPTEN_Msk (0x4UL) /*!< ADC CFG: RPTEN (Bitfield-Mask: 0x01) */ -#define ADC_CFG_ADCEN_Pos (0UL) /*!< ADC CFG: ADCEN (Bit 0) */ -#define ADC_CFG_ADCEN_Msk (0x1UL) /*!< ADC CFG: ADCEN (Bitfield-Mask: 0x01) */ -/* ========================================================= STAT ========================================================== */ -#define ADC_STAT_PWDSTAT_Pos (0UL) /*!< ADC STAT: PWDSTAT (Bit 0) */ -#define ADC_STAT_PWDSTAT_Msk (0x1UL) /*!< ADC STAT: PWDSTAT (Bitfield-Mask: 0x01) */ -/* ========================================================== SWT ========================================================== */ -#define ADC_SWT_SWT_Pos (0UL) /*!< ADC SWT: SWT (Bit 0) */ -#define ADC_SWT_SWT_Msk (0xffUL) /*!< ADC SWT: SWT (Bitfield-Mask: 0xff) */ -/* ======================================================== SL0CFG ========================================================= */ -#define ADC_SL0CFG_ADSEL0_Pos (24UL) /*!< ADC SL0CFG: ADSEL0 (Bit 24) */ -#define ADC_SL0CFG_ADSEL0_Msk (0x7000000UL) /*!< ADC SL0CFG: ADSEL0 (Bitfield-Mask: 0x07) */ -#define ADC_SL0CFG_PRMODE0_Pos (16UL) /*!< ADC SL0CFG: PRMODE0 (Bit 16) */ -#define ADC_SL0CFG_PRMODE0_Msk (0x30000UL) /*!< ADC SL0CFG: PRMODE0 (Bitfield-Mask: 0x03) */ -#define ADC_SL0CFG_CHSEL0_Pos (8UL) /*!< ADC SL0CFG: CHSEL0 (Bit 8) */ -#define ADC_SL0CFG_CHSEL0_Msk (0xf00UL) /*!< ADC SL0CFG: CHSEL0 (Bitfield-Mask: 0x0f) */ -#define ADC_SL0CFG_WCEN0_Pos (1UL) /*!< ADC SL0CFG: WCEN0 (Bit 1) */ -#define ADC_SL0CFG_WCEN0_Msk (0x2UL) /*!< ADC SL0CFG: WCEN0 (Bitfield-Mask: 0x01) */ -#define ADC_SL0CFG_SLEN0_Pos (0UL) /*!< ADC SL0CFG: SLEN0 (Bit 0) */ -#define ADC_SL0CFG_SLEN0_Msk (0x1UL) /*!< ADC SL0CFG: SLEN0 (Bitfield-Mask: 0x01) */ -/* ======================================================== SL1CFG ========================================================= */ -#define ADC_SL1CFG_ADSEL1_Pos (24UL) /*!< ADC SL1CFG: ADSEL1 (Bit 24) */ -#define ADC_SL1CFG_ADSEL1_Msk (0x7000000UL) /*!< ADC SL1CFG: ADSEL1 (Bitfield-Mask: 0x07) */ -#define ADC_SL1CFG_PRMODE1_Pos (16UL) /*!< ADC SL1CFG: PRMODE1 (Bit 16) */ -#define ADC_SL1CFG_PRMODE1_Msk (0x30000UL) /*!< ADC SL1CFG: PRMODE1 (Bitfield-Mask: 0x03) */ -#define ADC_SL1CFG_CHSEL1_Pos (8UL) /*!< ADC SL1CFG: CHSEL1 (Bit 8) */ -#define ADC_SL1CFG_CHSEL1_Msk (0xf00UL) /*!< ADC SL1CFG: CHSEL1 (Bitfield-Mask: 0x0f) */ -#define ADC_SL1CFG_WCEN1_Pos (1UL) /*!< ADC SL1CFG: WCEN1 (Bit 1) */ -#define ADC_SL1CFG_WCEN1_Msk (0x2UL) /*!< ADC SL1CFG: WCEN1 (Bitfield-Mask: 0x01) */ -#define ADC_SL1CFG_SLEN1_Pos (0UL) /*!< ADC SL1CFG: SLEN1 (Bit 0) */ -#define ADC_SL1CFG_SLEN1_Msk (0x1UL) /*!< ADC SL1CFG: SLEN1 (Bitfield-Mask: 0x01) */ -/* ======================================================== SL2CFG ========================================================= */ -#define ADC_SL2CFG_ADSEL2_Pos (24UL) /*!< ADC SL2CFG: ADSEL2 (Bit 24) */ -#define ADC_SL2CFG_ADSEL2_Msk (0x7000000UL) /*!< ADC SL2CFG: ADSEL2 (Bitfield-Mask: 0x07) */ -#define ADC_SL2CFG_PRMODE2_Pos (16UL) /*!< ADC SL2CFG: PRMODE2 (Bit 16) */ -#define ADC_SL2CFG_PRMODE2_Msk (0x30000UL) /*!< ADC SL2CFG: PRMODE2 (Bitfield-Mask: 0x03) */ -#define ADC_SL2CFG_CHSEL2_Pos (8UL) /*!< ADC SL2CFG: CHSEL2 (Bit 8) */ -#define ADC_SL2CFG_CHSEL2_Msk (0xf00UL) /*!< ADC SL2CFG: CHSEL2 (Bitfield-Mask: 0x0f) */ -#define ADC_SL2CFG_WCEN2_Pos (1UL) /*!< ADC SL2CFG: WCEN2 (Bit 1) */ -#define ADC_SL2CFG_WCEN2_Msk (0x2UL) /*!< ADC SL2CFG: WCEN2 (Bitfield-Mask: 0x01) */ -#define ADC_SL2CFG_SLEN2_Pos (0UL) /*!< ADC SL2CFG: SLEN2 (Bit 0) */ -#define ADC_SL2CFG_SLEN2_Msk (0x1UL) /*!< ADC SL2CFG: SLEN2 (Bitfield-Mask: 0x01) */ -/* ======================================================== SL3CFG ========================================================= */ -#define ADC_SL3CFG_ADSEL3_Pos (24UL) /*!< ADC SL3CFG: ADSEL3 (Bit 24) */ -#define ADC_SL3CFG_ADSEL3_Msk (0x7000000UL) /*!< ADC SL3CFG: ADSEL3 (Bitfield-Mask: 0x07) */ -#define ADC_SL3CFG_PRMODE3_Pos (16UL) /*!< ADC SL3CFG: PRMODE3 (Bit 16) */ -#define ADC_SL3CFG_PRMODE3_Msk (0x30000UL) /*!< ADC SL3CFG: PRMODE3 (Bitfield-Mask: 0x03) */ -#define ADC_SL3CFG_CHSEL3_Pos (8UL) /*!< ADC SL3CFG: CHSEL3 (Bit 8) */ -#define ADC_SL3CFG_CHSEL3_Msk (0xf00UL) /*!< ADC SL3CFG: CHSEL3 (Bitfield-Mask: 0x0f) */ -#define ADC_SL3CFG_WCEN3_Pos (1UL) /*!< ADC SL3CFG: WCEN3 (Bit 1) */ -#define ADC_SL3CFG_WCEN3_Msk (0x2UL) /*!< ADC SL3CFG: WCEN3 (Bitfield-Mask: 0x01) */ -#define ADC_SL3CFG_SLEN3_Pos (0UL) /*!< ADC SL3CFG: SLEN3 (Bit 0) */ -#define ADC_SL3CFG_SLEN3_Msk (0x1UL) /*!< ADC SL3CFG: SLEN3 (Bitfield-Mask: 0x01) */ -/* ======================================================== SL4CFG ========================================================= */ -#define ADC_SL4CFG_ADSEL4_Pos (24UL) /*!< ADC SL4CFG: ADSEL4 (Bit 24) */ -#define ADC_SL4CFG_ADSEL4_Msk (0x7000000UL) /*!< ADC SL4CFG: ADSEL4 (Bitfield-Mask: 0x07) */ -#define ADC_SL4CFG_PRMODE4_Pos (16UL) /*!< ADC SL4CFG: PRMODE4 (Bit 16) */ -#define ADC_SL4CFG_PRMODE4_Msk (0x30000UL) /*!< ADC SL4CFG: PRMODE4 (Bitfield-Mask: 0x03) */ -#define ADC_SL4CFG_CHSEL4_Pos (8UL) /*!< ADC SL4CFG: CHSEL4 (Bit 8) */ -#define ADC_SL4CFG_CHSEL4_Msk (0xf00UL) /*!< ADC SL4CFG: CHSEL4 (Bitfield-Mask: 0x0f) */ -#define ADC_SL4CFG_WCEN4_Pos (1UL) /*!< ADC SL4CFG: WCEN4 (Bit 1) */ -#define ADC_SL4CFG_WCEN4_Msk (0x2UL) /*!< ADC SL4CFG: WCEN4 (Bitfield-Mask: 0x01) */ -#define ADC_SL4CFG_SLEN4_Pos (0UL) /*!< ADC SL4CFG: SLEN4 (Bit 0) */ -#define ADC_SL4CFG_SLEN4_Msk (0x1UL) /*!< ADC SL4CFG: SLEN4 (Bitfield-Mask: 0x01) */ -/* ======================================================== SL5CFG ========================================================= */ -#define ADC_SL5CFG_ADSEL5_Pos (24UL) /*!< ADC SL5CFG: ADSEL5 (Bit 24) */ -#define ADC_SL5CFG_ADSEL5_Msk (0x7000000UL) /*!< ADC SL5CFG: ADSEL5 (Bitfield-Mask: 0x07) */ -#define ADC_SL5CFG_PRMODE5_Pos (16UL) /*!< ADC SL5CFG: PRMODE5 (Bit 16) */ -#define ADC_SL5CFG_PRMODE5_Msk (0x30000UL) /*!< ADC SL5CFG: PRMODE5 (Bitfield-Mask: 0x03) */ -#define ADC_SL5CFG_CHSEL5_Pos (8UL) /*!< ADC SL5CFG: CHSEL5 (Bit 8) */ -#define ADC_SL5CFG_CHSEL5_Msk (0xf00UL) /*!< ADC SL5CFG: CHSEL5 (Bitfield-Mask: 0x0f) */ -#define ADC_SL5CFG_WCEN5_Pos (1UL) /*!< ADC SL5CFG: WCEN5 (Bit 1) */ -#define ADC_SL5CFG_WCEN5_Msk (0x2UL) /*!< ADC SL5CFG: WCEN5 (Bitfield-Mask: 0x01) */ -#define ADC_SL5CFG_SLEN5_Pos (0UL) /*!< ADC SL5CFG: SLEN5 (Bit 0) */ -#define ADC_SL5CFG_SLEN5_Msk (0x1UL) /*!< ADC SL5CFG: SLEN5 (Bitfield-Mask: 0x01) */ -/* ======================================================== SL6CFG ========================================================= */ -#define ADC_SL6CFG_ADSEL6_Pos (24UL) /*!< ADC SL6CFG: ADSEL6 (Bit 24) */ -#define ADC_SL6CFG_ADSEL6_Msk (0x7000000UL) /*!< ADC SL6CFG: ADSEL6 (Bitfield-Mask: 0x07) */ -#define ADC_SL6CFG_PRMODE6_Pos (16UL) /*!< ADC SL6CFG: PRMODE6 (Bit 16) */ -#define ADC_SL6CFG_PRMODE6_Msk (0x30000UL) /*!< ADC SL6CFG: PRMODE6 (Bitfield-Mask: 0x03) */ -#define ADC_SL6CFG_CHSEL6_Pos (8UL) /*!< ADC SL6CFG: CHSEL6 (Bit 8) */ -#define ADC_SL6CFG_CHSEL6_Msk (0xf00UL) /*!< ADC SL6CFG: CHSEL6 (Bitfield-Mask: 0x0f) */ -#define ADC_SL6CFG_WCEN6_Pos (1UL) /*!< ADC SL6CFG: WCEN6 (Bit 1) */ -#define ADC_SL6CFG_WCEN6_Msk (0x2UL) /*!< ADC SL6CFG: WCEN6 (Bitfield-Mask: 0x01) */ -#define ADC_SL6CFG_SLEN6_Pos (0UL) /*!< ADC SL6CFG: SLEN6 (Bit 0) */ -#define ADC_SL6CFG_SLEN6_Msk (0x1UL) /*!< ADC SL6CFG: SLEN6 (Bitfield-Mask: 0x01) */ -/* ======================================================== SL7CFG ========================================================= */ -#define ADC_SL7CFG_ADSEL7_Pos (24UL) /*!< ADC SL7CFG: ADSEL7 (Bit 24) */ -#define ADC_SL7CFG_ADSEL7_Msk (0x7000000UL) /*!< ADC SL7CFG: ADSEL7 (Bitfield-Mask: 0x07) */ -#define ADC_SL7CFG_PRMODE7_Pos (16UL) /*!< ADC SL7CFG: PRMODE7 (Bit 16) */ -#define ADC_SL7CFG_PRMODE7_Msk (0x30000UL) /*!< ADC SL7CFG: PRMODE7 (Bitfield-Mask: 0x03) */ -#define ADC_SL7CFG_CHSEL7_Pos (8UL) /*!< ADC SL7CFG: CHSEL7 (Bit 8) */ -#define ADC_SL7CFG_CHSEL7_Msk (0xf00UL) /*!< ADC SL7CFG: CHSEL7 (Bitfield-Mask: 0x0f) */ -#define ADC_SL7CFG_WCEN7_Pos (1UL) /*!< ADC SL7CFG: WCEN7 (Bit 1) */ -#define ADC_SL7CFG_WCEN7_Msk (0x2UL) /*!< ADC SL7CFG: WCEN7 (Bitfield-Mask: 0x01) */ -#define ADC_SL7CFG_SLEN7_Pos (0UL) /*!< ADC SL7CFG: SLEN7 (Bit 0) */ -#define ADC_SL7CFG_SLEN7_Msk (0x1UL) /*!< ADC SL7CFG: SLEN7 (Bitfield-Mask: 0x01) */ -/* ========================================================= WULIM ========================================================= */ -#define ADC_WULIM_ULIM_Pos (0UL) /*!< ADC WULIM: ULIM (Bit 0) */ -#define ADC_WULIM_ULIM_Msk (0xfffffUL) /*!< ADC WULIM: ULIM (Bitfield-Mask: 0xfffff) */ -/* ========================================================= WLLIM ========================================================= */ -#define ADC_WLLIM_LLIM_Pos (0UL) /*!< ADC WLLIM: LLIM (Bit 0) */ -#define ADC_WLLIM_LLIM_Msk (0xfffffUL) /*!< ADC WLLIM: LLIM (Bitfield-Mask: 0xfffff) */ -/* ======================================================== SCWLIM ========================================================= */ -#define ADC_SCWLIM_SCWLIMEN_Pos (0UL) /*!< ADC SCWLIM: SCWLIMEN (Bit 0) */ -#define ADC_SCWLIM_SCWLIMEN_Msk (0x1UL) /*!< ADC SCWLIM: SCWLIMEN (Bitfield-Mask: 0x01) */ -/* ========================================================= FIFO ========================================================== */ -#define ADC_FIFO_RSVD_Pos (31UL) /*!< ADC FIFO: RSVD (Bit 31) */ -#define ADC_FIFO_RSVD_Msk (0x80000000UL) /*!< ADC FIFO: RSVD (Bitfield-Mask: 0x01) */ -#define ADC_FIFO_SLOTNUM_Pos (28UL) /*!< ADC FIFO: SLOTNUM (Bit 28) */ -#define ADC_FIFO_SLOTNUM_Msk (0x70000000UL) /*!< ADC FIFO: SLOTNUM (Bitfield-Mask: 0x07) */ -#define ADC_FIFO_COUNT_Pos (20UL) /*!< ADC FIFO: COUNT (Bit 20) */ -#define ADC_FIFO_COUNT_Msk (0xff00000UL) /*!< ADC FIFO: COUNT (Bitfield-Mask: 0xff) */ -#define ADC_FIFO_DATA_Pos (0UL) /*!< ADC FIFO: DATA (Bit 0) */ -#define ADC_FIFO_DATA_Msk (0xfffffUL) /*!< ADC FIFO: DATA (Bitfield-Mask: 0xfffff) */ -/* ======================================================== FIFOPR ========================================================= */ -#define ADC_FIFOPR_RSVDPR_Pos (31UL) /*!< ADC FIFOPR: RSVDPR (Bit 31) */ -#define ADC_FIFOPR_RSVDPR_Msk (0x80000000UL) /*!< ADC FIFOPR: RSVDPR (Bitfield-Mask: 0x01) */ -#define ADC_FIFOPR_SLOTNUMPR_Pos (28UL) /*!< ADC FIFOPR: SLOTNUMPR (Bit 28) */ -#define ADC_FIFOPR_SLOTNUMPR_Msk (0x70000000UL) /*!< ADC FIFOPR: SLOTNUMPR (Bitfield-Mask: 0x07) */ -#define ADC_FIFOPR_COUNT_Pos (20UL) /*!< ADC FIFOPR: COUNT (Bit 20) */ -#define ADC_FIFOPR_COUNT_Msk (0xff00000UL) /*!< ADC FIFOPR: COUNT (Bitfield-Mask: 0xff) */ -#define ADC_FIFOPR_DATA_Pos (0UL) /*!< ADC FIFOPR: DATA (Bit 0) */ -#define ADC_FIFOPR_DATA_Msk (0xfffffUL) /*!< ADC FIFOPR: DATA (Bitfield-Mask: 0xfffff) */ -/* ========================================================= INTEN ========================================================= */ -#define ADC_INTEN_DERR_Pos (7UL) /*!< ADC INTEN: DERR (Bit 7) */ -#define ADC_INTEN_DERR_Msk (0x80UL) /*!< ADC INTEN: DERR (Bitfield-Mask: 0x01) */ -#define ADC_INTEN_DCMP_Pos (6UL) /*!< ADC INTEN: DCMP (Bit 6) */ -#define ADC_INTEN_DCMP_Msk (0x40UL) /*!< ADC INTEN: DCMP (Bitfield-Mask: 0x01) */ -#define ADC_INTEN_WCINC_Pos (5UL) /*!< ADC INTEN: WCINC (Bit 5) */ -#define ADC_INTEN_WCINC_Msk (0x20UL) /*!< ADC INTEN: WCINC (Bitfield-Mask: 0x01) */ -#define ADC_INTEN_WCEXC_Pos (4UL) /*!< ADC INTEN: WCEXC (Bit 4) */ -#define ADC_INTEN_WCEXC_Msk (0x10UL) /*!< ADC INTEN: WCEXC (Bitfield-Mask: 0x01) */ -#define ADC_INTEN_FIFOOVR2_Pos (3UL) /*!< ADC INTEN: FIFOOVR2 (Bit 3) */ -#define ADC_INTEN_FIFOOVR2_Msk (0x8UL) /*!< ADC INTEN: FIFOOVR2 (Bitfield-Mask: 0x01) */ -#define ADC_INTEN_FIFOOVR1_Pos (2UL) /*!< ADC INTEN: FIFOOVR1 (Bit 2) */ -#define ADC_INTEN_FIFOOVR1_Msk (0x4UL) /*!< ADC INTEN: FIFOOVR1 (Bitfield-Mask: 0x01) */ -#define ADC_INTEN_SCNCMP_Pos (1UL) /*!< ADC INTEN: SCNCMP (Bit 1) */ -#define ADC_INTEN_SCNCMP_Msk (0x2UL) /*!< ADC INTEN: SCNCMP (Bitfield-Mask: 0x01) */ -#define ADC_INTEN_CNVCMP_Pos (0UL) /*!< ADC INTEN: CNVCMP (Bit 0) */ -#define ADC_INTEN_CNVCMP_Msk (0x1UL) /*!< ADC INTEN: CNVCMP (Bitfield-Mask: 0x01) */ -/* ======================================================== INTSTAT ======================================================== */ -#define ADC_INTSTAT_DERR_Pos (7UL) /*!< ADC INTSTAT: DERR (Bit 7) */ -#define ADC_INTSTAT_DERR_Msk (0x80UL) /*!< ADC INTSTAT: DERR (Bitfield-Mask: 0x01) */ -#define ADC_INTSTAT_DCMP_Pos (6UL) /*!< ADC INTSTAT: DCMP (Bit 6) */ -#define ADC_INTSTAT_DCMP_Msk (0x40UL) /*!< ADC INTSTAT: DCMP (Bitfield-Mask: 0x01) */ -#define ADC_INTSTAT_WCINC_Pos (5UL) /*!< ADC INTSTAT: WCINC (Bit 5) */ -#define ADC_INTSTAT_WCINC_Msk (0x20UL) /*!< ADC INTSTAT: WCINC (Bitfield-Mask: 0x01) */ -#define ADC_INTSTAT_WCEXC_Pos (4UL) /*!< ADC INTSTAT: WCEXC (Bit 4) */ -#define ADC_INTSTAT_WCEXC_Msk (0x10UL) /*!< ADC INTSTAT: WCEXC (Bitfield-Mask: 0x01) */ -#define ADC_INTSTAT_FIFOOVR2_Pos (3UL) /*!< ADC INTSTAT: FIFOOVR2 (Bit 3) */ -#define ADC_INTSTAT_FIFOOVR2_Msk (0x8UL) /*!< ADC INTSTAT: FIFOOVR2 (Bitfield-Mask: 0x01) */ -#define ADC_INTSTAT_FIFOOVR1_Pos (2UL) /*!< ADC INTSTAT: FIFOOVR1 (Bit 2) */ -#define ADC_INTSTAT_FIFOOVR1_Msk (0x4UL) /*!< ADC INTSTAT: FIFOOVR1 (Bitfield-Mask: 0x01) */ -#define ADC_INTSTAT_SCNCMP_Pos (1UL) /*!< ADC INTSTAT: SCNCMP (Bit 1) */ -#define ADC_INTSTAT_SCNCMP_Msk (0x2UL) /*!< ADC INTSTAT: SCNCMP (Bitfield-Mask: 0x01) */ -#define ADC_INTSTAT_CNVCMP_Pos (0UL) /*!< ADC INTSTAT: CNVCMP (Bit 0) */ -#define ADC_INTSTAT_CNVCMP_Msk (0x1UL) /*!< ADC INTSTAT: CNVCMP (Bitfield-Mask: 0x01) */ -/* ======================================================== INTCLR ========================================================= */ -#define ADC_INTCLR_DERR_Pos (7UL) /*!< ADC INTCLR: DERR (Bit 7) */ -#define ADC_INTCLR_DERR_Msk (0x80UL) /*!< ADC INTCLR: DERR (Bitfield-Mask: 0x01) */ -#define ADC_INTCLR_DCMP_Pos (6UL) /*!< ADC INTCLR: DCMP (Bit 6) */ -#define ADC_INTCLR_DCMP_Msk (0x40UL) /*!< ADC INTCLR: DCMP (Bitfield-Mask: 0x01) */ -#define ADC_INTCLR_WCINC_Pos (5UL) /*!< ADC INTCLR: WCINC (Bit 5) */ -#define ADC_INTCLR_WCINC_Msk (0x20UL) /*!< ADC INTCLR: WCINC (Bitfield-Mask: 0x01) */ -#define ADC_INTCLR_WCEXC_Pos (4UL) /*!< ADC INTCLR: WCEXC (Bit 4) */ -#define ADC_INTCLR_WCEXC_Msk (0x10UL) /*!< ADC INTCLR: WCEXC (Bitfield-Mask: 0x01) */ -#define ADC_INTCLR_FIFOOVR2_Pos (3UL) /*!< ADC INTCLR: FIFOOVR2 (Bit 3) */ -#define ADC_INTCLR_FIFOOVR2_Msk (0x8UL) /*!< ADC INTCLR: FIFOOVR2 (Bitfield-Mask: 0x01) */ -#define ADC_INTCLR_FIFOOVR1_Pos (2UL) /*!< ADC INTCLR: FIFOOVR1 (Bit 2) */ -#define ADC_INTCLR_FIFOOVR1_Msk (0x4UL) /*!< ADC INTCLR: FIFOOVR1 (Bitfield-Mask: 0x01) */ -#define ADC_INTCLR_SCNCMP_Pos (1UL) /*!< ADC INTCLR: SCNCMP (Bit 1) */ -#define ADC_INTCLR_SCNCMP_Msk (0x2UL) /*!< ADC INTCLR: SCNCMP (Bitfield-Mask: 0x01) */ -#define ADC_INTCLR_CNVCMP_Pos (0UL) /*!< ADC INTCLR: CNVCMP (Bit 0) */ -#define ADC_INTCLR_CNVCMP_Msk (0x1UL) /*!< ADC INTCLR: CNVCMP (Bitfield-Mask: 0x01) */ -/* ======================================================== INTSET ========================================================= */ -#define ADC_INTSET_DERR_Pos (7UL) /*!< ADC INTSET: DERR (Bit 7) */ -#define ADC_INTSET_DERR_Msk (0x80UL) /*!< ADC INTSET: DERR (Bitfield-Mask: 0x01) */ -#define ADC_INTSET_DCMP_Pos (6UL) /*!< ADC INTSET: DCMP (Bit 6) */ -#define ADC_INTSET_DCMP_Msk (0x40UL) /*!< ADC INTSET: DCMP (Bitfield-Mask: 0x01) */ -#define ADC_INTSET_WCINC_Pos (5UL) /*!< ADC INTSET: WCINC (Bit 5) */ -#define ADC_INTSET_WCINC_Msk (0x20UL) /*!< ADC INTSET: WCINC (Bitfield-Mask: 0x01) */ -#define ADC_INTSET_WCEXC_Pos (4UL) /*!< ADC INTSET: WCEXC (Bit 4) */ -#define ADC_INTSET_WCEXC_Msk (0x10UL) /*!< ADC INTSET: WCEXC (Bitfield-Mask: 0x01) */ -#define ADC_INTSET_FIFOOVR2_Pos (3UL) /*!< ADC INTSET: FIFOOVR2 (Bit 3) */ -#define ADC_INTSET_FIFOOVR2_Msk (0x8UL) /*!< ADC INTSET: FIFOOVR2 (Bitfield-Mask: 0x01) */ -#define ADC_INTSET_FIFOOVR1_Pos (2UL) /*!< ADC INTSET: FIFOOVR1 (Bit 2) */ -#define ADC_INTSET_FIFOOVR1_Msk (0x4UL) /*!< ADC INTSET: FIFOOVR1 (Bitfield-Mask: 0x01) */ -#define ADC_INTSET_SCNCMP_Pos (1UL) /*!< ADC INTSET: SCNCMP (Bit 1) */ -#define ADC_INTSET_SCNCMP_Msk (0x2UL) /*!< ADC INTSET: SCNCMP (Bitfield-Mask: 0x01) */ -#define ADC_INTSET_CNVCMP_Pos (0UL) /*!< ADC INTSET: CNVCMP (Bit 0) */ -#define ADC_INTSET_CNVCMP_Msk (0x1UL) /*!< ADC INTSET: CNVCMP (Bitfield-Mask: 0x01) */ -/* ======================================================= DMATRIGEN ======================================================= */ -#define ADC_DMATRIGEN_DFIFOFULL_Pos (1UL) /*!< ADC DMATRIGEN: DFIFOFULL (Bit 1) */ -#define ADC_DMATRIGEN_DFIFOFULL_Msk (0x2UL) /*!< ADC DMATRIGEN: DFIFOFULL (Bitfield-Mask: 0x01) */ -#define ADC_DMATRIGEN_DFIFO75_Pos (0UL) /*!< ADC DMATRIGEN: DFIFO75 (Bit 0) */ -#define ADC_DMATRIGEN_DFIFO75_Msk (0x1UL) /*!< ADC DMATRIGEN: DFIFO75 (Bitfield-Mask: 0x01) */ -/* ====================================================== DMATRIGSTAT ====================================================== */ -#define ADC_DMATRIGSTAT_DFULLSTAT_Pos (1UL) /*!< ADC DMATRIGSTAT: DFULLSTAT (Bit 1) */ -#define ADC_DMATRIGSTAT_DFULLSTAT_Msk (0x2UL) /*!< ADC DMATRIGSTAT: DFULLSTAT (Bitfield-Mask: 0x01) */ -#define ADC_DMATRIGSTAT_D75STAT_Pos (0UL) /*!< ADC DMATRIGSTAT: D75STAT (Bit 0) */ -#define ADC_DMATRIGSTAT_D75STAT_Msk (0x1UL) /*!< ADC DMATRIGSTAT: D75STAT (Bitfield-Mask: 0x01) */ -/* ======================================================== DMACFG ========================================================= */ -#define ADC_DMACFG_DPWROFF_Pos (18UL) /*!< ADC DMACFG: DPWROFF (Bit 18) */ -#define ADC_DMACFG_DPWROFF_Msk (0x40000UL) /*!< ADC DMACFG: DPWROFF (Bitfield-Mask: 0x01) */ -#define ADC_DMACFG_DMAMSK_Pos (17UL) /*!< ADC DMACFG: DMAMSK (Bit 17) */ -#define ADC_DMACFG_DMAMSK_Msk (0x20000UL) /*!< ADC DMACFG: DMAMSK (Bitfield-Mask: 0x01) */ -#define ADC_DMACFG_DMAHONSTAT_Pos (16UL) /*!< ADC DMACFG: DMAHONSTAT (Bit 16) */ -#define ADC_DMACFG_DMAHONSTAT_Msk (0x10000UL) /*!< ADC DMACFG: DMAHONSTAT (Bitfield-Mask: 0x01) */ -#define ADC_DMACFG_DMADYNPRI_Pos (9UL) /*!< ADC DMACFG: DMADYNPRI (Bit 9) */ -#define ADC_DMACFG_DMADYNPRI_Msk (0x200UL) /*!< ADC DMACFG: DMADYNPRI (Bitfield-Mask: 0x01) */ -#define ADC_DMACFG_DMAPRI_Pos (8UL) /*!< ADC DMACFG: DMAPRI (Bit 8) */ -#define ADC_DMACFG_DMAPRI_Msk (0x100UL) /*!< ADC DMACFG: DMAPRI (Bitfield-Mask: 0x01) */ -#define ADC_DMACFG_DMADIR_Pos (2UL) /*!< ADC DMACFG: DMADIR (Bit 2) */ -#define ADC_DMACFG_DMADIR_Msk (0x4UL) /*!< ADC DMACFG: DMADIR (Bitfield-Mask: 0x01) */ -#define ADC_DMACFG_DMAEN_Pos (0UL) /*!< ADC DMACFG: DMAEN (Bit 0) */ -#define ADC_DMACFG_DMAEN_Msk (0x1UL) /*!< ADC DMACFG: DMAEN (Bitfield-Mask: 0x01) */ -/* ====================================================== DMATOTCOUNT ====================================================== */ -#define ADC_DMATOTCOUNT_TOTCOUNT_Pos (2UL) /*!< ADC DMATOTCOUNT: TOTCOUNT (Bit 2) */ -#define ADC_DMATOTCOUNT_TOTCOUNT_Msk (0x3fffcUL) /*!< ADC DMATOTCOUNT: TOTCOUNT (Bitfield-Mask: 0xffff) */ -/* ====================================================== DMATARGADDR ====================================================== */ -#define ADC_DMATARGADDR_UTARGADDR_Pos (19UL) /*!< ADC DMATARGADDR: UTARGADDR (Bit 19) */ -#define ADC_DMATARGADDR_UTARGADDR_Msk (0xfff80000UL) /*!< ADC DMATARGADDR: UTARGADDR (Bitfield-Mask: 0x1fff) */ -#define ADC_DMATARGADDR_LTARGADDR_Pos (0UL) /*!< ADC DMATARGADDR: LTARGADDR (Bit 0) */ -#define ADC_DMATARGADDR_LTARGADDR_Msk (0x7ffffUL) /*!< ADC DMATARGADDR: LTARGADDR (Bitfield-Mask: 0x7ffff) */ -/* ======================================================== DMASTAT ======================================================== */ -#define ADC_DMASTAT_DMAERR_Pos (2UL) /*!< ADC DMASTAT: DMAERR (Bit 2) */ -#define ADC_DMASTAT_DMAERR_Msk (0x4UL) /*!< ADC DMASTAT: DMAERR (Bitfield-Mask: 0x01) */ -#define ADC_DMASTAT_DMACPL_Pos (1UL) /*!< ADC DMASTAT: DMACPL (Bit 1) */ -#define ADC_DMASTAT_DMACPL_Msk (0x2UL) /*!< ADC DMASTAT: DMACPL (Bitfield-Mask: 0x01) */ -#define ADC_DMASTAT_DMATIP_Pos (0UL) /*!< ADC DMASTAT: DMATIP (Bit 0) */ -#define ADC_DMASTAT_DMATIP_Msk (0x1UL) /*!< ADC DMASTAT: DMATIP (Bitfield-Mask: 0x01) */ - - -/* =========================================================================================================================== */ -/* ================ APBDMA ================ */ -/* =========================================================================================================================== */ - -/* ======================================================== BBVALUE ======================================================== */ -#define APBDMA_BBVALUE_PIN_Pos (16UL) /*!< APBDMA BBVALUE: PIN (Bit 16) */ -#define APBDMA_BBVALUE_PIN_Msk (0xff0000UL) /*!< APBDMA BBVALUE: PIN (Bitfield-Mask: 0xff) */ -#define APBDMA_BBVALUE_DATAOUT_Pos (0UL) /*!< APBDMA BBVALUE: DATAOUT (Bit 0) */ -#define APBDMA_BBVALUE_DATAOUT_Msk (0xffUL) /*!< APBDMA BBVALUE: DATAOUT (Bitfield-Mask: 0xff) */ -/* ====================================================== BBSETCLEAR ======================================================= */ -#define APBDMA_BBSETCLEAR_CLEAR_Pos (16UL) /*!< APBDMA BBSETCLEAR: CLEAR (Bit 16) */ -#define APBDMA_BBSETCLEAR_CLEAR_Msk (0xff0000UL) /*!< APBDMA BBSETCLEAR: CLEAR (Bitfield-Mask: 0xff) */ -#define APBDMA_BBSETCLEAR_SET_Pos (0UL) /*!< APBDMA BBSETCLEAR: SET (Bit 0) */ -#define APBDMA_BBSETCLEAR_SET_Msk (0xffUL) /*!< APBDMA BBSETCLEAR: SET (Bitfield-Mask: 0xff) */ -/* ======================================================== BBINPUT ======================================================== */ -#define APBDMA_BBINPUT_DATAIN_Pos (0UL) /*!< APBDMA BBINPUT: DATAIN (Bit 0) */ -#define APBDMA_BBINPUT_DATAIN_Msk (0xffUL) /*!< APBDMA BBINPUT: DATAIN (Bitfield-Mask: 0xff) */ -/* ======================================================= DEBUGDATA ======================================================= */ -#define APBDMA_DEBUGDATA_DEBUGDATA_Pos (0UL) /*!< APBDMA DEBUGDATA: DEBUGDATA (Bit 0) */ -#define APBDMA_DEBUGDATA_DEBUGDATA_Msk (0xffffffffUL) /*!< APBDMA DEBUGDATA: DEBUGDATA (Bitfield-Mask: 0xffffffff) */ -/* ========================================================= DEBUG ========================================================= */ -#define APBDMA_DEBUG_DEBUGEN_Pos (0UL) /*!< APBDMA DEBUG: DEBUGEN (Bit 0) */ -#define APBDMA_DEBUG_DEBUGEN_Msk (0xfUL) /*!< APBDMA DEBUG: DEBUGEN (Bitfield-Mask: 0x0f) */ - - -/* =========================================================================================================================== */ -/* ================ BLEIF ================ */ -/* =========================================================================================================================== */ - -/* ========================================================= FIFO ========================================================== */ -#define BLEIF_FIFO_FIFO_Pos (0UL) /*!< BLEIF FIFO: FIFO (Bit 0) */ -#define BLEIF_FIFO_FIFO_Msk (0xffffffffUL) /*!< BLEIF FIFO: FIFO (Bitfield-Mask: 0xffffffff) */ -/* ======================================================== FIFOPTR ======================================================== */ -#define BLEIF_FIFOPTR_FIFO1REM_Pos (24UL) /*!< BLEIF FIFOPTR: FIFO1REM (Bit 24) */ -#define BLEIF_FIFOPTR_FIFO1REM_Msk (0xff000000UL) /*!< BLEIF FIFOPTR: FIFO1REM (Bitfield-Mask: 0xff) */ -#define BLEIF_FIFOPTR_FIFO1SIZ_Pos (16UL) /*!< BLEIF FIFOPTR: FIFO1SIZ (Bit 16) */ -#define BLEIF_FIFOPTR_FIFO1SIZ_Msk (0xff0000UL) /*!< BLEIF FIFOPTR: FIFO1SIZ (Bitfield-Mask: 0xff) */ -#define BLEIF_FIFOPTR_FIFO0REM_Pos (8UL) /*!< BLEIF FIFOPTR: FIFO0REM (Bit 8) */ -#define BLEIF_FIFOPTR_FIFO0REM_Msk (0xff00UL) /*!< BLEIF FIFOPTR: FIFO0REM (Bitfield-Mask: 0xff) */ -#define BLEIF_FIFOPTR_FIFO0SIZ_Pos (0UL) /*!< BLEIF FIFOPTR: FIFO0SIZ (Bit 0) */ -#define BLEIF_FIFOPTR_FIFO0SIZ_Msk (0xffUL) /*!< BLEIF FIFOPTR: FIFO0SIZ (Bitfield-Mask: 0xff) */ -/* ======================================================== FIFOTHR ======================================================== */ -#define BLEIF_FIFOTHR_FIFOWTHR_Pos (8UL) /*!< BLEIF FIFOTHR: FIFOWTHR (Bit 8) */ -#define BLEIF_FIFOTHR_FIFOWTHR_Msk (0x3f00UL) /*!< BLEIF FIFOTHR: FIFOWTHR (Bitfield-Mask: 0x3f) */ -#define BLEIF_FIFOTHR_FIFORTHR_Pos (0UL) /*!< BLEIF FIFOTHR: FIFORTHR (Bit 0) */ -#define BLEIF_FIFOTHR_FIFORTHR_Msk (0x3fUL) /*!< BLEIF FIFOTHR: FIFORTHR (Bitfield-Mask: 0x3f) */ -/* ======================================================== FIFOPOP ======================================================== */ -#define BLEIF_FIFOPOP_FIFODOUT_Pos (0UL) /*!< BLEIF FIFOPOP: FIFODOUT (Bit 0) */ -#define BLEIF_FIFOPOP_FIFODOUT_Msk (0xffffffffUL) /*!< BLEIF FIFOPOP: FIFODOUT (Bitfield-Mask: 0xffffffff) */ -/* ======================================================= FIFOPUSH ======================================================== */ -#define BLEIF_FIFOPUSH_FIFODIN_Pos (0UL) /*!< BLEIF FIFOPUSH: FIFODIN (Bit 0) */ -#define BLEIF_FIFOPUSH_FIFODIN_Msk (0xffffffffUL) /*!< BLEIF FIFOPUSH: FIFODIN (Bitfield-Mask: 0xffffffff) */ -/* ======================================================= FIFOCTRL ======================================================== */ -#define BLEIF_FIFOCTRL_FIFORSTN_Pos (1UL) /*!< BLEIF FIFOCTRL: FIFORSTN (Bit 1) */ -#define BLEIF_FIFOCTRL_FIFORSTN_Msk (0x2UL) /*!< BLEIF FIFOCTRL: FIFORSTN (Bitfield-Mask: 0x01) */ -#define BLEIF_FIFOCTRL_POPWR_Pos (0UL) /*!< BLEIF FIFOCTRL: POPWR (Bit 0) */ -#define BLEIF_FIFOCTRL_POPWR_Msk (0x1UL) /*!< BLEIF FIFOCTRL: POPWR (Bitfield-Mask: 0x01) */ -/* ======================================================== FIFOLOC ======================================================== */ -#define BLEIF_FIFOLOC_FIFORPTR_Pos (8UL) /*!< BLEIF FIFOLOC: FIFORPTR (Bit 8) */ -#define BLEIF_FIFOLOC_FIFORPTR_Msk (0xf00UL) /*!< BLEIF FIFOLOC: FIFORPTR (Bitfield-Mask: 0x0f) */ -#define BLEIF_FIFOLOC_FIFOWPTR_Pos (0UL) /*!< BLEIF FIFOLOC: FIFOWPTR (Bit 0) */ -#define BLEIF_FIFOLOC_FIFOWPTR_Msk (0xfUL) /*!< BLEIF FIFOLOC: FIFOWPTR (Bitfield-Mask: 0x0f) */ -/* ======================================================== CLKCFG ========================================================= */ -#define BLEIF_CLKCFG_DIV3_Pos (12UL) /*!< BLEIF CLKCFG: DIV3 (Bit 12) */ -#define BLEIF_CLKCFG_DIV3_Msk (0x1000UL) /*!< BLEIF CLKCFG: DIV3 (Bitfield-Mask: 0x01) */ -#define BLEIF_CLKCFG_CLK32KEN_Pos (11UL) /*!< BLEIF CLKCFG: CLK32KEN (Bit 11) */ -#define BLEIF_CLKCFG_CLK32KEN_Msk (0x800UL) /*!< BLEIF CLKCFG: CLK32KEN (Bitfield-Mask: 0x01) */ -#define BLEIF_CLKCFG_FSEL_Pos (8UL) /*!< BLEIF CLKCFG: FSEL (Bit 8) */ -#define BLEIF_CLKCFG_FSEL_Msk (0x700UL) /*!< BLEIF CLKCFG: FSEL (Bitfield-Mask: 0x07) */ -#define BLEIF_CLKCFG_IOCLKEN_Pos (0UL) /*!< BLEIF CLKCFG: IOCLKEN (Bit 0) */ -#define BLEIF_CLKCFG_IOCLKEN_Msk (0x1UL) /*!< BLEIF CLKCFG: IOCLKEN (Bitfield-Mask: 0x01) */ -/* ========================================================== CMD ========================================================== */ -#define BLEIF_CMD_OFFSETLO_Pos (24UL) /*!< BLEIF CMD: OFFSETLO (Bit 24) */ -#define BLEIF_CMD_OFFSETLO_Msk (0xff000000UL) /*!< BLEIF CMD: OFFSETLO (Bitfield-Mask: 0xff) */ -#define BLEIF_CMD_CMDSEL_Pos (20UL) /*!< BLEIF CMD: CMDSEL (Bit 20) */ -#define BLEIF_CMD_CMDSEL_Msk (0x300000UL) /*!< BLEIF CMD: CMDSEL (Bitfield-Mask: 0x03) */ -#define BLEIF_CMD_TSIZE_Pos (8UL) /*!< BLEIF CMD: TSIZE (Bit 8) */ -#define BLEIF_CMD_TSIZE_Msk (0xfff00UL) /*!< BLEIF CMD: TSIZE (Bitfield-Mask: 0xfff) */ -#define BLEIF_CMD_CONT_Pos (7UL) /*!< BLEIF CMD: CONT (Bit 7) */ -#define BLEIF_CMD_CONT_Msk (0x80UL) /*!< BLEIF CMD: CONT (Bitfield-Mask: 0x01) */ -#define BLEIF_CMD_OFFSETCNT_Pos (5UL) /*!< BLEIF CMD: OFFSETCNT (Bit 5) */ -#define BLEIF_CMD_OFFSETCNT_Msk (0x60UL) /*!< BLEIF CMD: OFFSETCNT (Bitfield-Mask: 0x03) */ -#define BLEIF_CMD_CMD_Pos (0UL) /*!< BLEIF CMD: CMD (Bit 0) */ -#define BLEIF_CMD_CMD_Msk (0x1fUL) /*!< BLEIF CMD: CMD (Bitfield-Mask: 0x1f) */ -/* ======================================================== CMDRPT ========================================================= */ -#define BLEIF_CMDRPT_CMDRPT_Pos (0UL) /*!< BLEIF CMDRPT: CMDRPT (Bit 0) */ -#define BLEIF_CMDRPT_CMDRPT_Msk (0x1fUL) /*!< BLEIF CMDRPT: CMDRPT (Bitfield-Mask: 0x1f) */ -/* ======================================================= OFFSETHI ======================================================== */ -#define BLEIF_OFFSETHI_OFFSETHI_Pos (0UL) /*!< BLEIF OFFSETHI: OFFSETHI (Bit 0) */ -#define BLEIF_OFFSETHI_OFFSETHI_Msk (0xffffUL) /*!< BLEIF OFFSETHI: OFFSETHI (Bitfield-Mask: 0xffff) */ -/* ======================================================== CMDSTAT ======================================================== */ -#define BLEIF_CMDSTAT_CTSIZE_Pos (8UL) /*!< BLEIF CMDSTAT: CTSIZE (Bit 8) */ -#define BLEIF_CMDSTAT_CTSIZE_Msk (0xfff00UL) /*!< BLEIF CMDSTAT: CTSIZE (Bitfield-Mask: 0xfff) */ -#define BLEIF_CMDSTAT_CMDSTAT_Pos (5UL) /*!< BLEIF CMDSTAT: CMDSTAT (Bit 5) */ -#define BLEIF_CMDSTAT_CMDSTAT_Msk (0xe0UL) /*!< BLEIF CMDSTAT: CMDSTAT (Bitfield-Mask: 0x07) */ -#define BLEIF_CMDSTAT_CCMD_Pos (0UL) /*!< BLEIF CMDSTAT: CCMD (Bit 0) */ -#define BLEIF_CMDSTAT_CCMD_Msk (0x1fUL) /*!< BLEIF CMDSTAT: CCMD (Bitfield-Mask: 0x1f) */ -/* ========================================================= INTEN ========================================================= */ -#define BLEIF_INTEN_B2MSHUTDN_Pos (16UL) /*!< BLEIF INTEN: B2MSHUTDN (Bit 16) */ -#define BLEIF_INTEN_B2MSHUTDN_Msk (0x10000UL) /*!< BLEIF INTEN: B2MSHUTDN (Bitfield-Mask: 0x01) */ -#define BLEIF_INTEN_B2MACTIVE_Pos (15UL) /*!< BLEIF INTEN: B2MACTIVE (Bit 15) */ -#define BLEIF_INTEN_B2MACTIVE_Msk (0x8000UL) /*!< BLEIF INTEN: B2MACTIVE (Bitfield-Mask: 0x01) */ -#define BLEIF_INTEN_B2MSLEEP_Pos (14UL) /*!< BLEIF INTEN: B2MSLEEP (Bit 14) */ -#define BLEIF_INTEN_B2MSLEEP_Msk (0x4000UL) /*!< BLEIF INTEN: B2MSLEEP (Bitfield-Mask: 0x01) */ -#define BLEIF_INTEN_CQERR_Pos (13UL) /*!< BLEIF INTEN: CQERR (Bit 13) */ -#define BLEIF_INTEN_CQERR_Msk (0x2000UL) /*!< BLEIF INTEN: CQERR (Bitfield-Mask: 0x01) */ -#define BLEIF_INTEN_CQUPD_Pos (12UL) /*!< BLEIF INTEN: CQUPD (Bit 12) */ -#define BLEIF_INTEN_CQUPD_Msk (0x1000UL) /*!< BLEIF INTEN: CQUPD (Bitfield-Mask: 0x01) */ -#define BLEIF_INTEN_CQPAUSED_Pos (11UL) /*!< BLEIF INTEN: CQPAUSED (Bit 11) */ -#define BLEIF_INTEN_CQPAUSED_Msk (0x800UL) /*!< BLEIF INTEN: CQPAUSED (Bitfield-Mask: 0x01) */ -#define BLEIF_INTEN_DERR_Pos (10UL) /*!< BLEIF INTEN: DERR (Bit 10) */ -#define BLEIF_INTEN_DERR_Msk (0x400UL) /*!< BLEIF INTEN: DERR (Bitfield-Mask: 0x01) */ -#define BLEIF_INTEN_DCMP_Pos (9UL) /*!< BLEIF INTEN: DCMP (Bit 9) */ -#define BLEIF_INTEN_DCMP_Msk (0x200UL) /*!< BLEIF INTEN: DCMP (Bitfield-Mask: 0x01) */ -#define BLEIF_INTEN_BLECSSTAT_Pos (8UL) /*!< BLEIF INTEN: BLECSSTAT (Bit 8) */ -#define BLEIF_INTEN_BLECSSTAT_Msk (0x100UL) /*!< BLEIF INTEN: BLECSSTAT (Bitfield-Mask: 0x01) */ -#define BLEIF_INTEN_BLECIRQ_Pos (7UL) /*!< BLEIF INTEN: BLECIRQ (Bit 7) */ -#define BLEIF_INTEN_BLECIRQ_Msk (0x80UL) /*!< BLEIF INTEN: BLECIRQ (Bitfield-Mask: 0x01) */ -#define BLEIF_INTEN_ICMD_Pos (6UL) /*!< BLEIF INTEN: ICMD (Bit 6) */ -#define BLEIF_INTEN_ICMD_Msk (0x40UL) /*!< BLEIF INTEN: ICMD (Bitfield-Mask: 0x01) */ -#define BLEIF_INTEN_IACC_Pos (5UL) /*!< BLEIF INTEN: IACC (Bit 5) */ -#define BLEIF_INTEN_IACC_Msk (0x20UL) /*!< BLEIF INTEN: IACC (Bitfield-Mask: 0x01) */ -#define BLEIF_INTEN_B2MST_Pos (4UL) /*!< BLEIF INTEN: B2MST (Bit 4) */ -#define BLEIF_INTEN_B2MST_Msk (0x10UL) /*!< BLEIF INTEN: B2MST (Bitfield-Mask: 0x01) */ -#define BLEIF_INTEN_FOVFL_Pos (3UL) /*!< BLEIF INTEN: FOVFL (Bit 3) */ -#define BLEIF_INTEN_FOVFL_Msk (0x8UL) /*!< BLEIF INTEN: FOVFL (Bitfield-Mask: 0x01) */ -#define BLEIF_INTEN_FUNDFL_Pos (2UL) /*!< BLEIF INTEN: FUNDFL (Bit 2) */ -#define BLEIF_INTEN_FUNDFL_Msk (0x4UL) /*!< BLEIF INTEN: FUNDFL (Bitfield-Mask: 0x01) */ -#define BLEIF_INTEN_THR_Pos (1UL) /*!< BLEIF INTEN: THR (Bit 1) */ -#define BLEIF_INTEN_THR_Msk (0x2UL) /*!< BLEIF INTEN: THR (Bitfield-Mask: 0x01) */ -#define BLEIF_INTEN_CMDCMP_Pos (0UL) /*!< BLEIF INTEN: CMDCMP (Bit 0) */ -#define BLEIF_INTEN_CMDCMP_Msk (0x1UL) /*!< BLEIF INTEN: CMDCMP (Bitfield-Mask: 0x01) */ -/* ======================================================== INTSTAT ======================================================== */ -#define BLEIF_INTSTAT_B2MSHUTDN_Pos (16UL) /*!< BLEIF INTSTAT: B2MSHUTDN (Bit 16) */ -#define BLEIF_INTSTAT_B2MSHUTDN_Msk (0x10000UL) /*!< BLEIF INTSTAT: B2MSHUTDN (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSTAT_B2MACTIVE_Pos (15UL) /*!< BLEIF INTSTAT: B2MACTIVE (Bit 15) */ -#define BLEIF_INTSTAT_B2MACTIVE_Msk (0x8000UL) /*!< BLEIF INTSTAT: B2MACTIVE (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSTAT_B2MSLEEP_Pos (14UL) /*!< BLEIF INTSTAT: B2MSLEEP (Bit 14) */ -#define BLEIF_INTSTAT_B2MSLEEP_Msk (0x4000UL) /*!< BLEIF INTSTAT: B2MSLEEP (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSTAT_CQERR_Pos (13UL) /*!< BLEIF INTSTAT: CQERR (Bit 13) */ -#define BLEIF_INTSTAT_CQERR_Msk (0x2000UL) /*!< BLEIF INTSTAT: CQERR (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSTAT_CQUPD_Pos (12UL) /*!< BLEIF INTSTAT: CQUPD (Bit 12) */ -#define BLEIF_INTSTAT_CQUPD_Msk (0x1000UL) /*!< BLEIF INTSTAT: CQUPD (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSTAT_CQPAUSED_Pos (11UL) /*!< BLEIF INTSTAT: CQPAUSED (Bit 11) */ -#define BLEIF_INTSTAT_CQPAUSED_Msk (0x800UL) /*!< BLEIF INTSTAT: CQPAUSED (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSTAT_DERR_Pos (10UL) /*!< BLEIF INTSTAT: DERR (Bit 10) */ -#define BLEIF_INTSTAT_DERR_Msk (0x400UL) /*!< BLEIF INTSTAT: DERR (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSTAT_DCMP_Pos (9UL) /*!< BLEIF INTSTAT: DCMP (Bit 9) */ -#define BLEIF_INTSTAT_DCMP_Msk (0x200UL) /*!< BLEIF INTSTAT: DCMP (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSTAT_BLECSSTAT_Pos (8UL) /*!< BLEIF INTSTAT: BLECSSTAT (Bit 8) */ -#define BLEIF_INTSTAT_BLECSSTAT_Msk (0x100UL) /*!< BLEIF INTSTAT: BLECSSTAT (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSTAT_BLECIRQ_Pos (7UL) /*!< BLEIF INTSTAT: BLECIRQ (Bit 7) */ -#define BLEIF_INTSTAT_BLECIRQ_Msk (0x80UL) /*!< BLEIF INTSTAT: BLECIRQ (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSTAT_ICMD_Pos (6UL) /*!< BLEIF INTSTAT: ICMD (Bit 6) */ -#define BLEIF_INTSTAT_ICMD_Msk (0x40UL) /*!< BLEIF INTSTAT: ICMD (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSTAT_IACC_Pos (5UL) /*!< BLEIF INTSTAT: IACC (Bit 5) */ -#define BLEIF_INTSTAT_IACC_Msk (0x20UL) /*!< BLEIF INTSTAT: IACC (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSTAT_B2MST_Pos (4UL) /*!< BLEIF INTSTAT: B2MST (Bit 4) */ -#define BLEIF_INTSTAT_B2MST_Msk (0x10UL) /*!< BLEIF INTSTAT: B2MST (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSTAT_FOVFL_Pos (3UL) /*!< BLEIF INTSTAT: FOVFL (Bit 3) */ -#define BLEIF_INTSTAT_FOVFL_Msk (0x8UL) /*!< BLEIF INTSTAT: FOVFL (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSTAT_FUNDFL_Pos (2UL) /*!< BLEIF INTSTAT: FUNDFL (Bit 2) */ -#define BLEIF_INTSTAT_FUNDFL_Msk (0x4UL) /*!< BLEIF INTSTAT: FUNDFL (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSTAT_THR_Pos (1UL) /*!< BLEIF INTSTAT: THR (Bit 1) */ -#define BLEIF_INTSTAT_THR_Msk (0x2UL) /*!< BLEIF INTSTAT: THR (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSTAT_CMDCMP_Pos (0UL) /*!< BLEIF INTSTAT: CMDCMP (Bit 0) */ -#define BLEIF_INTSTAT_CMDCMP_Msk (0x1UL) /*!< BLEIF INTSTAT: CMDCMP (Bitfield-Mask: 0x01) */ -/* ======================================================== INTCLR ========================================================= */ -#define BLEIF_INTCLR_B2MSHUTDN_Pos (16UL) /*!< BLEIF INTCLR: B2MSHUTDN (Bit 16) */ -#define BLEIF_INTCLR_B2MSHUTDN_Msk (0x10000UL) /*!< BLEIF INTCLR: B2MSHUTDN (Bitfield-Mask: 0x01) */ -#define BLEIF_INTCLR_B2MACTIVE_Pos (15UL) /*!< BLEIF INTCLR: B2MACTIVE (Bit 15) */ -#define BLEIF_INTCLR_B2MACTIVE_Msk (0x8000UL) /*!< BLEIF INTCLR: B2MACTIVE (Bitfield-Mask: 0x01) */ -#define BLEIF_INTCLR_B2MSLEEP_Pos (14UL) /*!< BLEIF INTCLR: B2MSLEEP (Bit 14) */ -#define BLEIF_INTCLR_B2MSLEEP_Msk (0x4000UL) /*!< BLEIF INTCLR: B2MSLEEP (Bitfield-Mask: 0x01) */ -#define BLEIF_INTCLR_CQERR_Pos (13UL) /*!< BLEIF INTCLR: CQERR (Bit 13) */ -#define BLEIF_INTCLR_CQERR_Msk (0x2000UL) /*!< BLEIF INTCLR: CQERR (Bitfield-Mask: 0x01) */ -#define BLEIF_INTCLR_CQUPD_Pos (12UL) /*!< BLEIF INTCLR: CQUPD (Bit 12) */ -#define BLEIF_INTCLR_CQUPD_Msk (0x1000UL) /*!< BLEIF INTCLR: CQUPD (Bitfield-Mask: 0x01) */ -#define BLEIF_INTCLR_CQPAUSED_Pos (11UL) /*!< BLEIF INTCLR: CQPAUSED (Bit 11) */ -#define BLEIF_INTCLR_CQPAUSED_Msk (0x800UL) /*!< BLEIF INTCLR: CQPAUSED (Bitfield-Mask: 0x01) */ -#define BLEIF_INTCLR_DERR_Pos (10UL) /*!< BLEIF INTCLR: DERR (Bit 10) */ -#define BLEIF_INTCLR_DERR_Msk (0x400UL) /*!< BLEIF INTCLR: DERR (Bitfield-Mask: 0x01) */ -#define BLEIF_INTCLR_DCMP_Pos (9UL) /*!< BLEIF INTCLR: DCMP (Bit 9) */ -#define BLEIF_INTCLR_DCMP_Msk (0x200UL) /*!< BLEIF INTCLR: DCMP (Bitfield-Mask: 0x01) */ -#define BLEIF_INTCLR_BLECSSTAT_Pos (8UL) /*!< BLEIF INTCLR: BLECSSTAT (Bit 8) */ -#define BLEIF_INTCLR_BLECSSTAT_Msk (0x100UL) /*!< BLEIF INTCLR: BLECSSTAT (Bitfield-Mask: 0x01) */ -#define BLEIF_INTCLR_BLECIRQ_Pos (7UL) /*!< BLEIF INTCLR: BLECIRQ (Bit 7) */ -#define BLEIF_INTCLR_BLECIRQ_Msk (0x80UL) /*!< BLEIF INTCLR: BLECIRQ (Bitfield-Mask: 0x01) */ -#define BLEIF_INTCLR_ICMD_Pos (6UL) /*!< BLEIF INTCLR: ICMD (Bit 6) */ -#define BLEIF_INTCLR_ICMD_Msk (0x40UL) /*!< BLEIF INTCLR: ICMD (Bitfield-Mask: 0x01) */ -#define BLEIF_INTCLR_IACC_Pos (5UL) /*!< BLEIF INTCLR: IACC (Bit 5) */ -#define BLEIF_INTCLR_IACC_Msk (0x20UL) /*!< BLEIF INTCLR: IACC (Bitfield-Mask: 0x01) */ -#define BLEIF_INTCLR_B2MST_Pos (4UL) /*!< BLEIF INTCLR: B2MST (Bit 4) */ -#define BLEIF_INTCLR_B2MST_Msk (0x10UL) /*!< BLEIF INTCLR: B2MST (Bitfield-Mask: 0x01) */ -#define BLEIF_INTCLR_FOVFL_Pos (3UL) /*!< BLEIF INTCLR: FOVFL (Bit 3) */ -#define BLEIF_INTCLR_FOVFL_Msk (0x8UL) /*!< BLEIF INTCLR: FOVFL (Bitfield-Mask: 0x01) */ -#define BLEIF_INTCLR_FUNDFL_Pos (2UL) /*!< BLEIF INTCLR: FUNDFL (Bit 2) */ -#define BLEIF_INTCLR_FUNDFL_Msk (0x4UL) /*!< BLEIF INTCLR: FUNDFL (Bitfield-Mask: 0x01) */ -#define BLEIF_INTCLR_THR_Pos (1UL) /*!< BLEIF INTCLR: THR (Bit 1) */ -#define BLEIF_INTCLR_THR_Msk (0x2UL) /*!< BLEIF INTCLR: THR (Bitfield-Mask: 0x01) */ -#define BLEIF_INTCLR_CMDCMP_Pos (0UL) /*!< BLEIF INTCLR: CMDCMP (Bit 0) */ -#define BLEIF_INTCLR_CMDCMP_Msk (0x1UL) /*!< BLEIF INTCLR: CMDCMP (Bitfield-Mask: 0x01) */ -/* ======================================================== INTSET ========================================================= */ -#define BLEIF_INTSET_B2MSHUTDN_Pos (16UL) /*!< BLEIF INTSET: B2MSHUTDN (Bit 16) */ -#define BLEIF_INTSET_B2MSHUTDN_Msk (0x10000UL) /*!< BLEIF INTSET: B2MSHUTDN (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSET_B2MACTIVE_Pos (15UL) /*!< BLEIF INTSET: B2MACTIVE (Bit 15) */ -#define BLEIF_INTSET_B2MACTIVE_Msk (0x8000UL) /*!< BLEIF INTSET: B2MACTIVE (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSET_B2MSLEEP_Pos (14UL) /*!< BLEIF INTSET: B2MSLEEP (Bit 14) */ -#define BLEIF_INTSET_B2MSLEEP_Msk (0x4000UL) /*!< BLEIF INTSET: B2MSLEEP (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSET_CQERR_Pos (13UL) /*!< BLEIF INTSET: CQERR (Bit 13) */ -#define BLEIF_INTSET_CQERR_Msk (0x2000UL) /*!< BLEIF INTSET: CQERR (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSET_CQUPD_Pos (12UL) /*!< BLEIF INTSET: CQUPD (Bit 12) */ -#define BLEIF_INTSET_CQUPD_Msk (0x1000UL) /*!< BLEIF INTSET: CQUPD (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSET_CQPAUSED_Pos (11UL) /*!< BLEIF INTSET: CQPAUSED (Bit 11) */ -#define BLEIF_INTSET_CQPAUSED_Msk (0x800UL) /*!< BLEIF INTSET: CQPAUSED (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSET_DERR_Pos (10UL) /*!< BLEIF INTSET: DERR (Bit 10) */ -#define BLEIF_INTSET_DERR_Msk (0x400UL) /*!< BLEIF INTSET: DERR (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSET_DCMP_Pos (9UL) /*!< BLEIF INTSET: DCMP (Bit 9) */ -#define BLEIF_INTSET_DCMP_Msk (0x200UL) /*!< BLEIF INTSET: DCMP (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSET_BLECSSTAT_Pos (8UL) /*!< BLEIF INTSET: BLECSSTAT (Bit 8) */ -#define BLEIF_INTSET_BLECSSTAT_Msk (0x100UL) /*!< BLEIF INTSET: BLECSSTAT (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSET_BLECIRQ_Pos (7UL) /*!< BLEIF INTSET: BLECIRQ (Bit 7) */ -#define BLEIF_INTSET_BLECIRQ_Msk (0x80UL) /*!< BLEIF INTSET: BLECIRQ (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSET_ICMD_Pos (6UL) /*!< BLEIF INTSET: ICMD (Bit 6) */ -#define BLEIF_INTSET_ICMD_Msk (0x40UL) /*!< BLEIF INTSET: ICMD (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSET_IACC_Pos (5UL) /*!< BLEIF INTSET: IACC (Bit 5) */ -#define BLEIF_INTSET_IACC_Msk (0x20UL) /*!< BLEIF INTSET: IACC (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSET_B2MST_Pos (4UL) /*!< BLEIF INTSET: B2MST (Bit 4) */ -#define BLEIF_INTSET_B2MST_Msk (0x10UL) /*!< BLEIF INTSET: B2MST (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSET_FOVFL_Pos (3UL) /*!< BLEIF INTSET: FOVFL (Bit 3) */ -#define BLEIF_INTSET_FOVFL_Msk (0x8UL) /*!< BLEIF INTSET: FOVFL (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSET_FUNDFL_Pos (2UL) /*!< BLEIF INTSET: FUNDFL (Bit 2) */ -#define BLEIF_INTSET_FUNDFL_Msk (0x4UL) /*!< BLEIF INTSET: FUNDFL (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSET_THR_Pos (1UL) /*!< BLEIF INTSET: THR (Bit 1) */ -#define BLEIF_INTSET_THR_Msk (0x2UL) /*!< BLEIF INTSET: THR (Bitfield-Mask: 0x01) */ -#define BLEIF_INTSET_CMDCMP_Pos (0UL) /*!< BLEIF INTSET: CMDCMP (Bit 0) */ -#define BLEIF_INTSET_CMDCMP_Msk (0x1UL) /*!< BLEIF INTSET: CMDCMP (Bitfield-Mask: 0x01) */ -/* ======================================================= DMATRIGEN ======================================================= */ -#define BLEIF_DMATRIGEN_DTHREN_Pos (1UL) /*!< BLEIF DMATRIGEN: DTHREN (Bit 1) */ -#define BLEIF_DMATRIGEN_DTHREN_Msk (0x2UL) /*!< BLEIF DMATRIGEN: DTHREN (Bitfield-Mask: 0x01) */ -#define BLEIF_DMATRIGEN_DCMDCMPEN_Pos (0UL) /*!< BLEIF DMATRIGEN: DCMDCMPEN (Bit 0) */ -#define BLEIF_DMATRIGEN_DCMDCMPEN_Msk (0x1UL) /*!< BLEIF DMATRIGEN: DCMDCMPEN (Bitfield-Mask: 0x01) */ -/* ====================================================== DMATRIGSTAT ====================================================== */ -#define BLEIF_DMATRIGSTAT_DTOTCMP_Pos (2UL) /*!< BLEIF DMATRIGSTAT: DTOTCMP (Bit 2) */ -#define BLEIF_DMATRIGSTAT_DTOTCMP_Msk (0x4UL) /*!< BLEIF DMATRIGSTAT: DTOTCMP (Bitfield-Mask: 0x01) */ -#define BLEIF_DMATRIGSTAT_DTHR_Pos (1UL) /*!< BLEIF DMATRIGSTAT: DTHR (Bit 1) */ -#define BLEIF_DMATRIGSTAT_DTHR_Msk (0x2UL) /*!< BLEIF DMATRIGSTAT: DTHR (Bitfield-Mask: 0x01) */ -#define BLEIF_DMATRIGSTAT_DCMDCMP_Pos (0UL) /*!< BLEIF DMATRIGSTAT: DCMDCMP (Bit 0) */ -#define BLEIF_DMATRIGSTAT_DCMDCMP_Msk (0x1UL) /*!< BLEIF DMATRIGSTAT: DCMDCMP (Bitfield-Mask: 0x01) */ -/* ======================================================== DMACFG ========================================================= */ -#define BLEIF_DMACFG_DPWROFF_Pos (9UL) /*!< BLEIF DMACFG: DPWROFF (Bit 9) */ -#define BLEIF_DMACFG_DPWROFF_Msk (0x200UL) /*!< BLEIF DMACFG: DPWROFF (Bitfield-Mask: 0x01) */ -#define BLEIF_DMACFG_DMAPRI_Pos (8UL) /*!< BLEIF DMACFG: DMAPRI (Bit 8) */ -#define BLEIF_DMACFG_DMAPRI_Msk (0x100UL) /*!< BLEIF DMACFG: DMAPRI (Bitfield-Mask: 0x01) */ -#define BLEIF_DMACFG_DMADIR_Pos (1UL) /*!< BLEIF DMACFG: DMADIR (Bit 1) */ -#define BLEIF_DMACFG_DMADIR_Msk (0x2UL) /*!< BLEIF DMACFG: DMADIR (Bitfield-Mask: 0x01) */ -#define BLEIF_DMACFG_DMAEN_Pos (0UL) /*!< BLEIF DMACFG: DMAEN (Bit 0) */ -#define BLEIF_DMACFG_DMAEN_Msk (0x1UL) /*!< BLEIF DMACFG: DMAEN (Bitfield-Mask: 0x01) */ -/* ====================================================== DMATOTCOUNT ====================================================== */ -#define BLEIF_DMATOTCOUNT_TOTCOUNT_Pos (0UL) /*!< BLEIF DMATOTCOUNT: TOTCOUNT (Bit 0) */ -#define BLEIF_DMATOTCOUNT_TOTCOUNT_Msk (0xfffUL) /*!< BLEIF DMATOTCOUNT: TOTCOUNT (Bitfield-Mask: 0xfff) */ -/* ====================================================== DMATARGADDR ====================================================== */ -#define BLEIF_DMATARGADDR_TARGADDR28_Pos (28UL) /*!< BLEIF DMATARGADDR: TARGADDR28 (Bit 28) */ -#define BLEIF_DMATARGADDR_TARGADDR28_Msk (0x10000000UL) /*!< BLEIF DMATARGADDR: TARGADDR28 (Bitfield-Mask: 0x01) */ -#define BLEIF_DMATARGADDR_TARGADDR_Pos (0UL) /*!< BLEIF DMATARGADDR: TARGADDR (Bit 0) */ -#define BLEIF_DMATARGADDR_TARGADDR_Msk (0xfffffUL) /*!< BLEIF DMATARGADDR: TARGADDR (Bitfield-Mask: 0xfffff) */ -/* ======================================================== DMASTAT ======================================================== */ -#define BLEIF_DMASTAT_DMAERR_Pos (2UL) /*!< BLEIF DMASTAT: DMAERR (Bit 2) */ -#define BLEIF_DMASTAT_DMAERR_Msk (0x4UL) /*!< BLEIF DMASTAT: DMAERR (Bitfield-Mask: 0x01) */ -#define BLEIF_DMASTAT_DMACPL_Pos (1UL) /*!< BLEIF DMASTAT: DMACPL (Bit 1) */ -#define BLEIF_DMASTAT_DMACPL_Msk (0x2UL) /*!< BLEIF DMASTAT: DMACPL (Bitfield-Mask: 0x01) */ -#define BLEIF_DMASTAT_DMATIP_Pos (0UL) /*!< BLEIF DMASTAT: DMATIP (Bit 0) */ -#define BLEIF_DMASTAT_DMATIP_Msk (0x1UL) /*!< BLEIF DMASTAT: DMATIP (Bitfield-Mask: 0x01) */ -/* ========================================================= CQCFG ========================================================= */ -#define BLEIF_CQCFG_CQPRI_Pos (1UL) /*!< BLEIF CQCFG: CQPRI (Bit 1) */ -#define BLEIF_CQCFG_CQPRI_Msk (0x2UL) /*!< BLEIF CQCFG: CQPRI (Bitfield-Mask: 0x01) */ -#define BLEIF_CQCFG_CQEN_Pos (0UL) /*!< BLEIF CQCFG: CQEN (Bit 0) */ -#define BLEIF_CQCFG_CQEN_Msk (0x1UL) /*!< BLEIF CQCFG: CQEN (Bitfield-Mask: 0x01) */ -/* ======================================================== CQADDR ========================================================= */ -#define BLEIF_CQADDR_CQADDR28_Pos (28UL) /*!< BLEIF CQADDR: CQADDR28 (Bit 28) */ -#define BLEIF_CQADDR_CQADDR28_Msk (0x10000000UL) /*!< BLEIF CQADDR: CQADDR28 (Bitfield-Mask: 0x01) */ -#define BLEIF_CQADDR_CQADDR_Pos (2UL) /*!< BLEIF CQADDR: CQADDR (Bit 2) */ -#define BLEIF_CQADDR_CQADDR_Msk (0xffffcUL) /*!< BLEIF CQADDR: CQADDR (Bitfield-Mask: 0x3ffff) */ -/* ======================================================== CQSTAT ========================================================= */ -#define BLEIF_CQSTAT_CQERR_Pos (2UL) /*!< BLEIF CQSTAT: CQERR (Bit 2) */ -#define BLEIF_CQSTAT_CQERR_Msk (0x4UL) /*!< BLEIF CQSTAT: CQERR (Bitfield-Mask: 0x01) */ -#define BLEIF_CQSTAT_CQPAUSED_Pos (1UL) /*!< BLEIF CQSTAT: CQPAUSED (Bit 1) */ -#define BLEIF_CQSTAT_CQPAUSED_Msk (0x2UL) /*!< BLEIF CQSTAT: CQPAUSED (Bitfield-Mask: 0x01) */ -#define BLEIF_CQSTAT_CQTIP_Pos (0UL) /*!< BLEIF CQSTAT: CQTIP (Bit 0) */ -#define BLEIF_CQSTAT_CQTIP_Msk (0x1UL) /*!< BLEIF CQSTAT: CQTIP (Bitfield-Mask: 0x01) */ -/* ======================================================== CQFLAGS ======================================================== */ -#define BLEIF_CQFLAGS_CQIRQMASK_Pos (16UL) /*!< BLEIF CQFLAGS: CQIRQMASK (Bit 16) */ -#define BLEIF_CQFLAGS_CQIRQMASK_Msk (0xffff0000UL) /*!< BLEIF CQFLAGS: CQIRQMASK (Bitfield-Mask: 0xffff) */ -#define BLEIF_CQFLAGS_CQFLAGS_Pos (0UL) /*!< BLEIF CQFLAGS: CQFLAGS (Bit 0) */ -#define BLEIF_CQFLAGS_CQFLAGS_Msk (0xffffUL) /*!< BLEIF CQFLAGS: CQFLAGS (Bitfield-Mask: 0xffff) */ -/* ====================================================== CQSETCLEAR ======================================================= */ -#define BLEIF_CQSETCLEAR_CQFCLR_Pos (16UL) /*!< BLEIF CQSETCLEAR: CQFCLR (Bit 16) */ -#define BLEIF_CQSETCLEAR_CQFCLR_Msk (0xff0000UL) /*!< BLEIF CQSETCLEAR: CQFCLR (Bitfield-Mask: 0xff) */ -#define BLEIF_CQSETCLEAR_CQFTGL_Pos (8UL) /*!< BLEIF CQSETCLEAR: CQFTGL (Bit 8) */ -#define BLEIF_CQSETCLEAR_CQFTGL_Msk (0xff00UL) /*!< BLEIF CQSETCLEAR: CQFTGL (Bitfield-Mask: 0xff) */ -#define BLEIF_CQSETCLEAR_CQFSET_Pos (0UL) /*!< BLEIF CQSETCLEAR: CQFSET (Bit 0) */ -#define BLEIF_CQSETCLEAR_CQFSET_Msk (0xffUL) /*!< BLEIF CQSETCLEAR: CQFSET (Bitfield-Mask: 0xff) */ -/* ======================================================= CQPAUSEEN ======================================================= */ -#define BLEIF_CQPAUSEEN_CQPEN_Pos (0UL) /*!< BLEIF CQPAUSEEN: CQPEN (Bit 0) */ -#define BLEIF_CQPAUSEEN_CQPEN_Msk (0xffffUL) /*!< BLEIF CQPAUSEEN: CQPEN (Bitfield-Mask: 0xffff) */ -/* ======================================================= CQCURIDX ======================================================== */ -#define BLEIF_CQCURIDX_CQCURIDX_Pos (0UL) /*!< BLEIF CQCURIDX: CQCURIDX (Bit 0) */ -#define BLEIF_CQCURIDX_CQCURIDX_Msk (0xffUL) /*!< BLEIF CQCURIDX: CQCURIDX (Bitfield-Mask: 0xff) */ -/* ======================================================= CQENDIDX ======================================================== */ -#define BLEIF_CQENDIDX_CQENDIDX_Pos (0UL) /*!< BLEIF CQENDIDX: CQENDIDX (Bit 0) */ -#define BLEIF_CQENDIDX_CQENDIDX_Msk (0xffUL) /*!< BLEIF CQENDIDX: CQENDIDX (Bitfield-Mask: 0xff) */ -/* ======================================================== STATUS ========================================================= */ -#define BLEIF_STATUS_IDLEST_Pos (2UL) /*!< BLEIF STATUS: IDLEST (Bit 2) */ -#define BLEIF_STATUS_IDLEST_Msk (0x4UL) /*!< BLEIF STATUS: IDLEST (Bitfield-Mask: 0x01) */ -#define BLEIF_STATUS_CMDACT_Pos (1UL) /*!< BLEIF STATUS: CMDACT (Bit 1) */ -#define BLEIF_STATUS_CMDACT_Msk (0x2UL) /*!< BLEIF STATUS: CMDACT (Bitfield-Mask: 0x01) */ -#define BLEIF_STATUS_ERR_Pos (0UL) /*!< BLEIF STATUS: ERR (Bit 0) */ -#define BLEIF_STATUS_ERR_Msk (0x1UL) /*!< BLEIF STATUS: ERR (Bitfield-Mask: 0x01) */ -/* ======================================================== MSPICFG ======================================================== */ -#define BLEIF_MSPICFG_MSPIRST_Pos (30UL) /*!< BLEIF MSPICFG: MSPIRST (Bit 30) */ -#define BLEIF_MSPICFG_MSPIRST_Msk (0x40000000UL) /*!< BLEIF MSPICFG: MSPIRST (Bitfield-Mask: 0x01) */ -#define BLEIF_MSPICFG_DOUTDLY_Pos (27UL) /*!< BLEIF MSPICFG: DOUTDLY (Bit 27) */ -#define BLEIF_MSPICFG_DOUTDLY_Msk (0x38000000UL) /*!< BLEIF MSPICFG: DOUTDLY (Bitfield-Mask: 0x07) */ -#define BLEIF_MSPICFG_DINDLY_Pos (24UL) /*!< BLEIF MSPICFG: DINDLY (Bit 24) */ -#define BLEIF_MSPICFG_DINDLY_Msk (0x7000000UL) /*!< BLEIF MSPICFG: DINDLY (Bitfield-Mask: 0x07) */ -#define BLEIF_MSPICFG_SPILSB_Pos (23UL) /*!< BLEIF MSPICFG: SPILSB (Bit 23) */ -#define BLEIF_MSPICFG_SPILSB_Msk (0x800000UL) /*!< BLEIF MSPICFG: SPILSB (Bitfield-Mask: 0x01) */ -#define BLEIF_MSPICFG_RDFCPOL_Pos (22UL) /*!< BLEIF MSPICFG: RDFCPOL (Bit 22) */ -#define BLEIF_MSPICFG_RDFCPOL_Msk (0x400000UL) /*!< BLEIF MSPICFG: RDFCPOL (Bitfield-Mask: 0x01) */ -#define BLEIF_MSPICFG_WTFCPOL_Pos (21UL) /*!< BLEIF MSPICFG: WTFCPOL (Bit 21) */ -#define BLEIF_MSPICFG_WTFCPOL_Msk (0x200000UL) /*!< BLEIF MSPICFG: WTFCPOL (Bitfield-Mask: 0x01) */ -#define BLEIF_MSPICFG_RDFC_Pos (17UL) /*!< BLEIF MSPICFG: RDFC (Bit 17) */ -#define BLEIF_MSPICFG_RDFC_Msk (0x20000UL) /*!< BLEIF MSPICFG: RDFC (Bitfield-Mask: 0x01) */ -#define BLEIF_MSPICFG_WTFC_Pos (16UL) /*!< BLEIF MSPICFG: WTFC (Bit 16) */ -#define BLEIF_MSPICFG_WTFC_Msk (0x10000UL) /*!< BLEIF MSPICFG: WTFC (Bitfield-Mask: 0x01) */ -#define BLEIF_MSPICFG_FULLDUP_Pos (2UL) /*!< BLEIF MSPICFG: FULLDUP (Bit 2) */ -#define BLEIF_MSPICFG_FULLDUP_Msk (0x4UL) /*!< BLEIF MSPICFG: FULLDUP (Bitfield-Mask: 0x01) */ -#define BLEIF_MSPICFG_SPHA_Pos (1UL) /*!< BLEIF MSPICFG: SPHA (Bit 1) */ -#define BLEIF_MSPICFG_SPHA_Msk (0x2UL) /*!< BLEIF MSPICFG: SPHA (Bitfield-Mask: 0x01) */ -#define BLEIF_MSPICFG_SPOL_Pos (0UL) /*!< BLEIF MSPICFG: SPOL (Bit 0) */ -#define BLEIF_MSPICFG_SPOL_Msk (0x1UL) /*!< BLEIF MSPICFG: SPOL (Bitfield-Mask: 0x01) */ -/* ======================================================== BLECFG ========================================================= */ -#define BLEIF_BLECFG_SPIISOCTL_Pos (14UL) /*!< BLEIF BLECFG: SPIISOCTL (Bit 14) */ -#define BLEIF_BLECFG_SPIISOCTL_Msk (0xc000UL) /*!< BLEIF BLECFG: SPIISOCTL (Bitfield-Mask: 0x03) */ -#define BLEIF_BLECFG_PWRISOCTL_Pos (12UL) /*!< BLEIF BLECFG: PWRISOCTL (Bit 12) */ -#define BLEIF_BLECFG_PWRISOCTL_Msk (0x3000UL) /*!< BLEIF BLECFG: PWRISOCTL (Bitfield-Mask: 0x03) */ -#define BLEIF_BLECFG_STAYASLEEP_Pos (11UL) /*!< BLEIF BLECFG: STAYASLEEP (Bit 11) */ -#define BLEIF_BLECFG_STAYASLEEP_Msk (0x800UL) /*!< BLEIF BLECFG: STAYASLEEP (Bitfield-Mask: 0x01) */ -#define BLEIF_BLECFG_FRCCLK_Pos (10UL) /*!< BLEIF BLECFG: FRCCLK (Bit 10) */ -#define BLEIF_BLECFG_FRCCLK_Msk (0x400UL) /*!< BLEIF BLECFG: FRCCLK (Bitfield-Mask: 0x01) */ -#define BLEIF_BLECFG_MCUFRCSLP_Pos (9UL) /*!< BLEIF BLECFG: MCUFRCSLP (Bit 9) */ -#define BLEIF_BLECFG_MCUFRCSLP_Msk (0x200UL) /*!< BLEIF BLECFG: MCUFRCSLP (Bitfield-Mask: 0x01) */ -#define BLEIF_BLECFG_WT4ACTOFF_Pos (8UL) /*!< BLEIF BLECFG: WT4ACTOFF (Bit 8) */ -#define BLEIF_BLECFG_WT4ACTOFF_Msk (0x100UL) /*!< BLEIF BLECFG: WT4ACTOFF (Bitfield-Mask: 0x01) */ -#define BLEIF_BLECFG_BLEHREQCTL_Pos (6UL) /*!< BLEIF BLECFG: BLEHREQCTL (Bit 6) */ -#define BLEIF_BLECFG_BLEHREQCTL_Msk (0xc0UL) /*!< BLEIF BLECFG: BLEHREQCTL (Bitfield-Mask: 0x03) */ -#define BLEIF_BLECFG_DCDCFLGCTL_Pos (4UL) /*!< BLEIF BLECFG: DCDCFLGCTL (Bit 4) */ -#define BLEIF_BLECFG_DCDCFLGCTL_Msk (0x30UL) /*!< BLEIF BLECFG: DCDCFLGCTL (Bitfield-Mask: 0x03) */ -#define BLEIF_BLECFG_WAKEUPCTL_Pos (2UL) /*!< BLEIF BLECFG: WAKEUPCTL (Bit 2) */ -#define BLEIF_BLECFG_WAKEUPCTL_Msk (0xcUL) /*!< BLEIF BLECFG: WAKEUPCTL (Bitfield-Mask: 0x03) */ -#define BLEIF_BLECFG_BLERSTN_Pos (1UL) /*!< BLEIF BLECFG: BLERSTN (Bit 1) */ -#define BLEIF_BLECFG_BLERSTN_Msk (0x2UL) /*!< BLEIF BLECFG: BLERSTN (Bitfield-Mask: 0x01) */ -#define BLEIF_BLECFG_PWRSMEN_Pos (0UL) /*!< BLEIF BLECFG: PWRSMEN (Bit 0) */ -#define BLEIF_BLECFG_PWRSMEN_Msk (0x1UL) /*!< BLEIF BLECFG: PWRSMEN (Bitfield-Mask: 0x01) */ -/* ======================================================== PWRCMD ========================================================= */ -#define BLEIF_PWRCMD_RESTART_Pos (1UL) /*!< BLEIF PWRCMD: RESTART (Bit 1) */ -#define BLEIF_PWRCMD_RESTART_Msk (0x2UL) /*!< BLEIF PWRCMD: RESTART (Bitfield-Mask: 0x01) */ -#define BLEIF_PWRCMD_WAKEREQ_Pos (0UL) /*!< BLEIF PWRCMD: WAKEREQ (Bit 0) */ -#define BLEIF_PWRCMD_WAKEREQ_Msk (0x1UL) /*!< BLEIF PWRCMD: WAKEREQ (Bitfield-Mask: 0x01) */ -/* ======================================================== BSTATUS ======================================================== */ -#define BLEIF_BSTATUS_BLEHREQ_Pos (12UL) /*!< BLEIF BSTATUS: BLEHREQ (Bit 12) */ -#define BLEIF_BSTATUS_BLEHREQ_Msk (0x1000UL) /*!< BLEIF BSTATUS: BLEHREQ (Bitfield-Mask: 0x01) */ -#define BLEIF_BSTATUS_BLEHACK_Pos (11UL) /*!< BLEIF BSTATUS: BLEHACK (Bit 11) */ -#define BLEIF_BSTATUS_BLEHACK_Msk (0x800UL) /*!< BLEIF BSTATUS: BLEHACK (Bitfield-Mask: 0x01) */ -#define BLEIF_BSTATUS_PWRST_Pos (8UL) /*!< BLEIF BSTATUS: PWRST (Bit 8) */ -#define BLEIF_BSTATUS_PWRST_Msk (0x700UL) /*!< BLEIF BSTATUS: PWRST (Bitfield-Mask: 0x07) */ -#define BLEIF_BSTATUS_BLEIRQ_Pos (7UL) /*!< BLEIF BSTATUS: BLEIRQ (Bit 7) */ -#define BLEIF_BSTATUS_BLEIRQ_Msk (0x80UL) /*!< BLEIF BSTATUS: BLEIRQ (Bitfield-Mask: 0x01) */ -#define BLEIF_BSTATUS_WAKEUP_Pos (6UL) /*!< BLEIF BSTATUS: WAKEUP (Bit 6) */ -#define BLEIF_BSTATUS_WAKEUP_Msk (0x40UL) /*!< BLEIF BSTATUS: WAKEUP (Bitfield-Mask: 0x01) */ -#define BLEIF_BSTATUS_DCDCFLAG_Pos (5UL) /*!< BLEIF BSTATUS: DCDCFLAG (Bit 5) */ -#define BLEIF_BSTATUS_DCDCFLAG_Msk (0x20UL) /*!< BLEIF BSTATUS: DCDCFLAG (Bitfield-Mask: 0x01) */ -#define BLEIF_BSTATUS_DCDCREQ_Pos (4UL) /*!< BLEIF BSTATUS: DCDCREQ (Bit 4) */ -#define BLEIF_BSTATUS_DCDCREQ_Msk (0x10UL) /*!< BLEIF BSTATUS: DCDCREQ (Bitfield-Mask: 0x01) */ -#define BLEIF_BSTATUS_SPISTATUS_Pos (3UL) /*!< BLEIF BSTATUS: SPISTATUS (Bit 3) */ -#define BLEIF_BSTATUS_SPISTATUS_Msk (0x8UL) /*!< BLEIF BSTATUS: SPISTATUS (Bitfield-Mask: 0x01) */ -#define BLEIF_BSTATUS_B2MSTATE_Pos (0UL) /*!< BLEIF BSTATUS: B2MSTATE (Bit 0) */ -#define BLEIF_BSTATUS_B2MSTATE_Msk (0x7UL) /*!< BLEIF BSTATUS: B2MSTATE (Bitfield-Mask: 0x07) */ -/* ======================================================== BLEDBG ========================================================= */ -#define BLEIF_BLEDBG_DBGDATA_Pos (3UL) /*!< BLEIF BLEDBG: DBGDATA (Bit 3) */ -#define BLEIF_BLEDBG_DBGDATA_Msk (0xfffffff8UL) /*!< BLEIF BLEDBG: DBGDATA (Bitfield-Mask: 0x1fffffff) */ -#define BLEIF_BLEDBG_APBCLKON_Pos (2UL) /*!< BLEIF BLEDBG: APBCLKON (Bit 2) */ -#define BLEIF_BLEDBG_APBCLKON_Msk (0x4UL) /*!< BLEIF BLEDBG: APBCLKON (Bitfield-Mask: 0x01) */ -#define BLEIF_BLEDBG_IOCLKON_Pos (1UL) /*!< BLEIF BLEDBG: IOCLKON (Bit 1) */ -#define BLEIF_BLEDBG_IOCLKON_Msk (0x2UL) /*!< BLEIF BLEDBG: IOCLKON (Bitfield-Mask: 0x01) */ -#define BLEIF_BLEDBG_DBGEN_Pos (0UL) /*!< BLEIF BLEDBG: DBGEN (Bit 0) */ -#define BLEIF_BLEDBG_DBGEN_Msk (0x1UL) /*!< BLEIF BLEDBG: DBGEN (Bitfield-Mask: 0x01) */ - - -/* =========================================================================================================================== */ -/* ================ CACHECTRL ================ */ -/* =========================================================================================================================== */ - -/* ======================================================= CACHECFG ======================================================== */ -#define CACHECTRL_CACHECFG_ENABLE_MONITOR_Pos (24UL) /*!< CACHECTRL CACHECFG: ENABLE_MONITOR (Bit 24) */ -#define CACHECTRL_CACHECFG_ENABLE_MONITOR_Msk (0x1000000UL) /*!< CACHECTRL CACHECFG: ENABLE_MONITOR (Bitfield-Mask: 0x01) */ -#define CACHECTRL_CACHECFG_DATA_CLKGATE_Pos (20UL) /*!< CACHECTRL CACHECFG: DATA_CLKGATE (Bit 20) */ -#define CACHECTRL_CACHECFG_DATA_CLKGATE_Msk (0x100000UL) /*!< CACHECTRL CACHECFG: DATA_CLKGATE (Bitfield-Mask: 0x01) */ -#define CACHECTRL_CACHECFG_CACHE_LS_Pos (11UL) /*!< CACHECTRL CACHECFG: CACHE_LS (Bit 11) */ -#define CACHECTRL_CACHECFG_CACHE_LS_Msk (0x800UL) /*!< CACHECTRL CACHECFG: CACHE_LS (Bitfield-Mask: 0x01) */ -#define CACHECTRL_CACHECFG_CACHE_CLKGATE_Pos (10UL) /*!< CACHECTRL CACHECFG: CACHE_CLKGATE (Bit 10) */ -#define CACHECTRL_CACHECFG_CACHE_CLKGATE_Msk (0x400UL) /*!< CACHECTRL CACHECFG: CACHE_CLKGATE (Bitfield-Mask: 0x01) */ -#define CACHECTRL_CACHECFG_DCACHE_ENABLE_Pos (9UL) /*!< CACHECTRL CACHECFG: DCACHE_ENABLE (Bit 9) */ -#define CACHECTRL_CACHECFG_DCACHE_ENABLE_Msk (0x200UL) /*!< CACHECTRL CACHECFG: DCACHE_ENABLE (Bitfield-Mask: 0x01) */ -#define CACHECTRL_CACHECFG_ICACHE_ENABLE_Pos (8UL) /*!< CACHECTRL CACHECFG: ICACHE_ENABLE (Bit 8) */ -#define CACHECTRL_CACHECFG_ICACHE_ENABLE_Msk (0x100UL) /*!< CACHECTRL CACHECFG: ICACHE_ENABLE (Bitfield-Mask: 0x01) */ -#define CACHECTRL_CACHECFG_CONFIG_Pos (4UL) /*!< CACHECTRL CACHECFG: CONFIG (Bit 4) */ -#define CACHECTRL_CACHECFG_CONFIG_Msk (0xf0UL) /*!< CACHECTRL CACHECFG: CONFIG (Bitfield-Mask: 0x0f) */ -#define CACHECTRL_CACHECFG_ENABLE_NC1_Pos (3UL) /*!< CACHECTRL CACHECFG: ENABLE_NC1 (Bit 3) */ -#define CACHECTRL_CACHECFG_ENABLE_NC1_Msk (0x8UL) /*!< CACHECTRL CACHECFG: ENABLE_NC1 (Bitfield-Mask: 0x01) */ -#define CACHECTRL_CACHECFG_ENABLE_NC0_Pos (2UL) /*!< CACHECTRL CACHECFG: ENABLE_NC0 (Bit 2) */ -#define CACHECTRL_CACHECFG_ENABLE_NC0_Msk (0x4UL) /*!< CACHECTRL CACHECFG: ENABLE_NC0 (Bitfield-Mask: 0x01) */ -#define CACHECTRL_CACHECFG_LRU_Pos (1UL) /*!< CACHECTRL CACHECFG: LRU (Bit 1) */ -#define CACHECTRL_CACHECFG_LRU_Msk (0x2UL) /*!< CACHECTRL CACHECFG: LRU (Bitfield-Mask: 0x01) */ -#define CACHECTRL_CACHECFG_ENABLE_Pos (0UL) /*!< CACHECTRL CACHECFG: ENABLE (Bit 0) */ -#define CACHECTRL_CACHECFG_ENABLE_Msk (0x1UL) /*!< CACHECTRL CACHECFG: ENABLE (Bitfield-Mask: 0x01) */ -/* ======================================================= FLASHCFG ======================================================== */ -#define CACHECTRL_FLASHCFG_LPMMODE_Pos (12UL) /*!< CACHECTRL FLASHCFG: LPMMODE (Bit 12) */ -#define CACHECTRL_FLASHCFG_LPMMODE_Msk (0x3000UL) /*!< CACHECTRL FLASHCFG: LPMMODE (Bitfield-Mask: 0x03) */ -#define CACHECTRL_FLASHCFG_LPM_RD_WAIT_Pos (8UL) /*!< CACHECTRL FLASHCFG: LPM_RD_WAIT (Bit 8) */ -#define CACHECTRL_FLASHCFG_LPM_RD_WAIT_Msk (0xf00UL) /*!< CACHECTRL FLASHCFG: LPM_RD_WAIT (Bitfield-Mask: 0x0f) */ -#define CACHECTRL_FLASHCFG_SEDELAY_Pos (4UL) /*!< CACHECTRL FLASHCFG: SEDELAY (Bit 4) */ -#define CACHECTRL_FLASHCFG_SEDELAY_Msk (0x70UL) /*!< CACHECTRL FLASHCFG: SEDELAY (Bitfield-Mask: 0x07) */ -#define CACHECTRL_FLASHCFG_RD_WAIT_Pos (0UL) /*!< CACHECTRL FLASHCFG: RD_WAIT (Bit 0) */ -#define CACHECTRL_FLASHCFG_RD_WAIT_Msk (0xfUL) /*!< CACHECTRL FLASHCFG: RD_WAIT (Bitfield-Mask: 0x0f) */ -/* ========================================================= CTRL ========================================================== */ -#define CACHECTRL_CTRL_FLASH1_SLM_ENABLE_Pos (10UL) /*!< CACHECTRL CTRL: FLASH1_SLM_ENABLE (Bit 10) */ -#define CACHECTRL_CTRL_FLASH1_SLM_ENABLE_Msk (0x400UL) /*!< CACHECTRL CTRL: FLASH1_SLM_ENABLE (Bitfield-Mask: 0x01) */ -#define CACHECTRL_CTRL_FLASH1_SLM_DISABLE_Pos (9UL) /*!< CACHECTRL CTRL: FLASH1_SLM_DISABLE (Bit 9) */ -#define CACHECTRL_CTRL_FLASH1_SLM_DISABLE_Msk (0x200UL) /*!< CACHECTRL CTRL: FLASH1_SLM_DISABLE (Bitfield-Mask: 0x01) */ -#define CACHECTRL_CTRL_FLASH1_SLM_STATUS_Pos (8UL) /*!< CACHECTRL CTRL: FLASH1_SLM_STATUS (Bit 8) */ -#define CACHECTRL_CTRL_FLASH1_SLM_STATUS_Msk (0x100UL) /*!< CACHECTRL CTRL: FLASH1_SLM_STATUS (Bitfield-Mask: 0x01) */ -#define CACHECTRL_CTRL_FLASH0_SLM_ENABLE_Pos (6UL) /*!< CACHECTRL CTRL: FLASH0_SLM_ENABLE (Bit 6) */ -#define CACHECTRL_CTRL_FLASH0_SLM_ENABLE_Msk (0x40UL) /*!< CACHECTRL CTRL: FLASH0_SLM_ENABLE (Bitfield-Mask: 0x01) */ -#define CACHECTRL_CTRL_FLASH0_SLM_DISABLE_Pos (5UL) /*!< CACHECTRL CTRL: FLASH0_SLM_DISABLE (Bit 5) */ -#define CACHECTRL_CTRL_FLASH0_SLM_DISABLE_Msk (0x20UL) /*!< CACHECTRL CTRL: FLASH0_SLM_DISABLE (Bitfield-Mask: 0x01) */ -#define CACHECTRL_CTRL_FLASH0_SLM_STATUS_Pos (4UL) /*!< CACHECTRL CTRL: FLASH0_SLM_STATUS (Bit 4) */ -#define CACHECTRL_CTRL_FLASH0_SLM_STATUS_Msk (0x10UL) /*!< CACHECTRL CTRL: FLASH0_SLM_STATUS (Bitfield-Mask: 0x01) */ -#define CACHECTRL_CTRL_CACHE_READY_Pos (2UL) /*!< CACHECTRL CTRL: CACHE_READY (Bit 2) */ -#define CACHECTRL_CTRL_CACHE_READY_Msk (0x4UL) /*!< CACHECTRL CTRL: CACHE_READY (Bitfield-Mask: 0x01) */ -#define CACHECTRL_CTRL_RESET_STAT_Pos (1UL) /*!< CACHECTRL CTRL: RESET_STAT (Bit 1) */ -#define CACHECTRL_CTRL_RESET_STAT_Msk (0x2UL) /*!< CACHECTRL CTRL: RESET_STAT (Bitfield-Mask: 0x01) */ -#define CACHECTRL_CTRL_INVALIDATE_Pos (0UL) /*!< CACHECTRL CTRL: INVALIDATE (Bit 0) */ -#define CACHECTRL_CTRL_INVALIDATE_Msk (0x1UL) /*!< CACHECTRL CTRL: INVALIDATE (Bitfield-Mask: 0x01) */ -/* ======================================================= NCR0START ======================================================= */ -#define CACHECTRL_NCR0START_ADDR_Pos (4UL) /*!< CACHECTRL NCR0START: ADDR (Bit 4) */ -#define CACHECTRL_NCR0START_ADDR_Msk (0x7fffff0UL) /*!< CACHECTRL NCR0START: ADDR (Bitfield-Mask: 0x7fffff) */ -/* ======================================================== NCR0END ======================================================== */ -#define CACHECTRL_NCR0END_ADDR_Pos (4UL) /*!< CACHECTRL NCR0END: ADDR (Bit 4) */ -#define CACHECTRL_NCR0END_ADDR_Msk (0x7fffff0UL) /*!< CACHECTRL NCR0END: ADDR (Bitfield-Mask: 0x7fffff) */ -/* ======================================================= NCR1START ======================================================= */ -#define CACHECTRL_NCR1START_ADDR_Pos (4UL) /*!< CACHECTRL NCR1START: ADDR (Bit 4) */ -#define CACHECTRL_NCR1START_ADDR_Msk (0x7fffff0UL) /*!< CACHECTRL NCR1START: ADDR (Bitfield-Mask: 0x7fffff) */ -/* ======================================================== NCR1END ======================================================== */ -#define CACHECTRL_NCR1END_ADDR_Pos (4UL) /*!< CACHECTRL NCR1END: ADDR (Bit 4) */ -#define CACHECTRL_NCR1END_ADDR_Msk (0x7fffff0UL) /*!< CACHECTRL NCR1END: ADDR (Bitfield-Mask: 0x7fffff) */ -/* ========================================================= DMON0 ========================================================= */ -#define CACHECTRL_DMON0_DACCESS_COUNT_Pos (0UL) /*!< CACHECTRL DMON0: DACCESS_COUNT (Bit 0) */ -#define CACHECTRL_DMON0_DACCESS_COUNT_Msk (0xffffffffUL) /*!< CACHECTRL DMON0: DACCESS_COUNT (Bitfield-Mask: 0xffffffff) */ -/* ========================================================= DMON1 ========================================================= */ -#define CACHECTRL_DMON1_DLOOKUP_COUNT_Pos (0UL) /*!< CACHECTRL DMON1: DLOOKUP_COUNT (Bit 0) */ -#define CACHECTRL_DMON1_DLOOKUP_COUNT_Msk (0xffffffffUL) /*!< CACHECTRL DMON1: DLOOKUP_COUNT (Bitfield-Mask: 0xffffffff) */ -/* ========================================================= DMON2 ========================================================= */ -#define CACHECTRL_DMON2_DHIT_COUNT_Pos (0UL) /*!< CACHECTRL DMON2: DHIT_COUNT (Bit 0) */ -#define CACHECTRL_DMON2_DHIT_COUNT_Msk (0xffffffffUL) /*!< CACHECTRL DMON2: DHIT_COUNT (Bitfield-Mask: 0xffffffff) */ -/* ========================================================= DMON3 ========================================================= */ -#define CACHECTRL_DMON3_DLINE_COUNT_Pos (0UL) /*!< CACHECTRL DMON3: DLINE_COUNT (Bit 0) */ -#define CACHECTRL_DMON3_DLINE_COUNT_Msk (0xffffffffUL) /*!< CACHECTRL DMON3: DLINE_COUNT (Bitfield-Mask: 0xffffffff) */ -/* ========================================================= IMON0 ========================================================= */ -#define CACHECTRL_IMON0_IACCESS_COUNT_Pos (0UL) /*!< CACHECTRL IMON0: IACCESS_COUNT (Bit 0) */ -#define CACHECTRL_IMON0_IACCESS_COUNT_Msk (0xffffffffUL) /*!< CACHECTRL IMON0: IACCESS_COUNT (Bitfield-Mask: 0xffffffff) */ -/* ========================================================= IMON1 ========================================================= */ -#define CACHECTRL_IMON1_ILOOKUP_COUNT_Pos (0UL) /*!< CACHECTRL IMON1: ILOOKUP_COUNT (Bit 0) */ -#define CACHECTRL_IMON1_ILOOKUP_COUNT_Msk (0xffffffffUL) /*!< CACHECTRL IMON1: ILOOKUP_COUNT (Bitfield-Mask: 0xffffffff) */ -/* ========================================================= IMON2 ========================================================= */ -#define CACHECTRL_IMON2_IHIT_COUNT_Pos (0UL) /*!< CACHECTRL IMON2: IHIT_COUNT (Bit 0) */ -#define CACHECTRL_IMON2_IHIT_COUNT_Msk (0xffffffffUL) /*!< CACHECTRL IMON2: IHIT_COUNT (Bitfield-Mask: 0xffffffff) */ -/* ========================================================= IMON3 ========================================================= */ -#define CACHECTRL_IMON3_ILINE_COUNT_Pos (0UL) /*!< CACHECTRL IMON3: ILINE_COUNT (Bit 0) */ -#define CACHECTRL_IMON3_ILINE_COUNT_Msk (0xffffffffUL) /*!< CACHECTRL IMON3: ILINE_COUNT (Bitfield-Mask: 0xffffffff) */ - - -/* =========================================================================================================================== */ -/* ================ CLKGEN ================ */ -/* =========================================================================================================================== */ - -/* ========================================================= CALXT ========================================================= */ -#define CLKGEN_CALXT_CALXT_Pos (0UL) /*!< CLKGEN CALXT: CALXT (Bit 0) */ -#define CLKGEN_CALXT_CALXT_Msk (0x7ffUL) /*!< CLKGEN CALXT: CALXT (Bitfield-Mask: 0x7ff) */ -/* ========================================================= CALRC ========================================================= */ -#define CLKGEN_CALRC_CALRC_Pos (0UL) /*!< CLKGEN CALRC: CALRC (Bit 0) */ -#define CLKGEN_CALRC_CALRC_Msk (0x3ffffUL) /*!< CLKGEN CALRC: CALRC (Bitfield-Mask: 0x3ffff) */ -/* ======================================================== ACALCTR ======================================================== */ -#define CLKGEN_ACALCTR_ACALCTR_Pos (0UL) /*!< CLKGEN ACALCTR: ACALCTR (Bit 0) */ -#define CLKGEN_ACALCTR_ACALCTR_Msk (0xffffffUL) /*!< CLKGEN ACALCTR: ACALCTR (Bitfield-Mask: 0xffffff) */ -/* ========================================================= OCTRL ========================================================= */ -#define CLKGEN_OCTRL_ACAL_Pos (8UL) /*!< CLKGEN OCTRL: ACAL (Bit 8) */ -#define CLKGEN_OCTRL_ACAL_Msk (0x700UL) /*!< CLKGEN OCTRL: ACAL (Bitfield-Mask: 0x07) */ -#define CLKGEN_OCTRL_OSEL_Pos (7UL) /*!< CLKGEN OCTRL: OSEL (Bit 7) */ -#define CLKGEN_OCTRL_OSEL_Msk (0x80UL) /*!< CLKGEN OCTRL: OSEL (Bitfield-Mask: 0x01) */ -#define CLKGEN_OCTRL_FOS_Pos (6UL) /*!< CLKGEN OCTRL: FOS (Bit 6) */ -#define CLKGEN_OCTRL_FOS_Msk (0x40UL) /*!< CLKGEN OCTRL: FOS (Bitfield-Mask: 0x01) */ -#define CLKGEN_OCTRL_STOPRC_Pos (1UL) /*!< CLKGEN OCTRL: STOPRC (Bit 1) */ -#define CLKGEN_OCTRL_STOPRC_Msk (0x2UL) /*!< CLKGEN OCTRL: STOPRC (Bitfield-Mask: 0x01) */ -#define CLKGEN_OCTRL_STOPXT_Pos (0UL) /*!< CLKGEN OCTRL: STOPXT (Bit 0) */ -#define CLKGEN_OCTRL_STOPXT_Msk (0x1UL) /*!< CLKGEN OCTRL: STOPXT (Bitfield-Mask: 0x01) */ -/* ======================================================== CLKOUT ========================================================= */ -#define CLKGEN_CLKOUT_CKEN_Pos (7UL) /*!< CLKGEN CLKOUT: CKEN (Bit 7) */ -#define CLKGEN_CLKOUT_CKEN_Msk (0x80UL) /*!< CLKGEN CLKOUT: CKEN (Bitfield-Mask: 0x01) */ -#define CLKGEN_CLKOUT_CKSEL_Pos (0UL) /*!< CLKGEN CLKOUT: CKSEL (Bit 0) */ -#define CLKGEN_CLKOUT_CKSEL_Msk (0x3fUL) /*!< CLKGEN CLKOUT: CKSEL (Bitfield-Mask: 0x3f) */ -/* ======================================================== CLKKEY ========================================================= */ -#define CLKGEN_CLKKEY_CLKKEY_Pos (0UL) /*!< CLKGEN CLKKEY: CLKKEY (Bit 0) */ -#define CLKGEN_CLKKEY_CLKKEY_Msk (0xffffffffUL) /*!< CLKGEN CLKKEY: CLKKEY (Bitfield-Mask: 0xffffffff) */ -/* ========================================================= CCTRL ========================================================= */ -#define CLKGEN_CCTRL_CORESEL_Pos (0UL) /*!< CLKGEN CCTRL: CORESEL (Bit 0) */ -#define CLKGEN_CCTRL_CORESEL_Msk (0x1UL) /*!< CLKGEN CCTRL: CORESEL (Bitfield-Mask: 0x01) */ -/* ======================================================== STATUS ========================================================= */ -#define CLKGEN_STATUS_OSCF_Pos (1UL) /*!< CLKGEN STATUS: OSCF (Bit 1) */ -#define CLKGEN_STATUS_OSCF_Msk (0x2UL) /*!< CLKGEN STATUS: OSCF (Bitfield-Mask: 0x01) */ -#define CLKGEN_STATUS_OMODE_Pos (0UL) /*!< CLKGEN STATUS: OMODE (Bit 0) */ -#define CLKGEN_STATUS_OMODE_Msk (0x1UL) /*!< CLKGEN STATUS: OMODE (Bitfield-Mask: 0x01) */ -/* ========================================================= HFADJ ========================================================= */ -#define CLKGEN_HFADJ_HFADJGAIN_Pos (21UL) /*!< CLKGEN HFADJ: HFADJGAIN (Bit 21) */ -#define CLKGEN_HFADJ_HFADJGAIN_Msk (0xe00000UL) /*!< CLKGEN HFADJ: HFADJGAIN (Bitfield-Mask: 0x07) */ -#define CLKGEN_HFADJ_HFWARMUP_Pos (20UL) /*!< CLKGEN HFADJ: HFWARMUP (Bit 20) */ -#define CLKGEN_HFADJ_HFWARMUP_Msk (0x100000UL) /*!< CLKGEN HFADJ: HFWARMUP (Bitfield-Mask: 0x01) */ -#define CLKGEN_HFADJ_HFXTADJ_Pos (8UL) /*!< CLKGEN HFADJ: HFXTADJ (Bit 8) */ -#define CLKGEN_HFADJ_HFXTADJ_Msk (0xfff00UL) /*!< CLKGEN HFADJ: HFXTADJ (Bitfield-Mask: 0xfff) */ -#define CLKGEN_HFADJ_HFADJCK_Pos (1UL) /*!< CLKGEN HFADJ: HFADJCK (Bit 1) */ -#define CLKGEN_HFADJ_HFADJCK_Msk (0xeUL) /*!< CLKGEN HFADJ: HFADJCK (Bitfield-Mask: 0x07) */ -#define CLKGEN_HFADJ_HFADJEN_Pos (0UL) /*!< CLKGEN HFADJ: HFADJEN (Bit 0) */ -#define CLKGEN_HFADJ_HFADJEN_Msk (0x1UL) /*!< CLKGEN HFADJ: HFADJEN (Bitfield-Mask: 0x01) */ -/* ====================================================== CLOCKENSTAT ====================================================== */ -#define CLKGEN_CLOCKENSTAT_CLOCKENSTAT_Pos (0UL) /*!< CLKGEN CLOCKENSTAT: CLOCKENSTAT (Bit 0) */ -#define CLKGEN_CLOCKENSTAT_CLOCKENSTAT_Msk (0xffffffffUL) /*!< CLKGEN CLOCKENSTAT: CLOCKENSTAT (Bitfield-Mask: 0xffffffff) */ -/* ===================================================== CLOCKEN2STAT ====================================================== */ -#define CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_Pos (0UL) /*!< CLKGEN CLOCKEN2STAT: CLOCKEN2STAT (Bit 0) */ -#define CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_Msk (0xffffffffUL) /*!< CLKGEN CLOCKEN2STAT: CLOCKEN2STAT (Bitfield-Mask: 0xffffffff) */ -/* ===================================================== CLOCKEN3STAT ====================================================== */ -#define CLKGEN_CLOCKEN3STAT_CLOCKEN3STAT_Pos (0UL) /*!< CLKGEN CLOCKEN3STAT: CLOCKEN3STAT (Bit 0) */ -#define CLKGEN_CLOCKEN3STAT_CLOCKEN3STAT_Msk (0xffffffffUL) /*!< CLKGEN CLOCKEN3STAT: CLOCKEN3STAT (Bitfield-Mask: 0xffffffff) */ -/* ======================================================= FREQCTRL ======================================================== */ -#define CLKGEN_FREQCTRL_BURSTSTATUS_Pos (2UL) /*!< CLKGEN FREQCTRL: BURSTSTATUS (Bit 2) */ -#define CLKGEN_FREQCTRL_BURSTSTATUS_Msk (0x4UL) /*!< CLKGEN FREQCTRL: BURSTSTATUS (Bitfield-Mask: 0x01) */ -#define CLKGEN_FREQCTRL_BURSTACK_Pos (1UL) /*!< CLKGEN FREQCTRL: BURSTACK (Bit 1) */ -#define CLKGEN_FREQCTRL_BURSTACK_Msk (0x2UL) /*!< CLKGEN FREQCTRL: BURSTACK (Bitfield-Mask: 0x01) */ -#define CLKGEN_FREQCTRL_BURSTREQ_Pos (0UL) /*!< CLKGEN FREQCTRL: BURSTREQ (Bit 0) */ -#define CLKGEN_FREQCTRL_BURSTREQ_Msk (0x1UL) /*!< CLKGEN FREQCTRL: BURSTREQ (Bitfield-Mask: 0x01) */ -/* ===================================================== BLEBUCKTONADJ ===================================================== */ -#define CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTEN_Pos (27UL) /*!< CLKGEN BLEBUCKTONADJ: ZEROLENDETECTEN (Bit 27) */ -#define CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTEN_Msk (0x8000000UL) /*!< CLKGEN BLEBUCKTONADJ: ZEROLENDETECTEN (Bitfield-Mask: 0x01) */ -#define CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_Pos (23UL) /*!< CLKGEN BLEBUCKTONADJ: ZEROLENDETECTTRIM (Bit 23) */ -#define CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_Msk (0x7800000UL) /*!< CLKGEN BLEBUCKTONADJ: ZEROLENDETECTTRIM (Bitfield-Mask: 0x0f) */ -#define CLKGEN_BLEBUCKTONADJ_TONADJUSTEN_Pos (22UL) /*!< CLKGEN BLEBUCKTONADJ: TONADJUSTEN (Bit 22) */ -#define CLKGEN_BLEBUCKTONADJ_TONADJUSTEN_Msk (0x400000UL) /*!< CLKGEN BLEBUCKTONADJ: TONADJUSTEN (Bitfield-Mask: 0x01) */ -#define CLKGEN_BLEBUCKTONADJ_TONADJUSTPERIOD_Pos (20UL) /*!< CLKGEN BLEBUCKTONADJ: TONADJUSTPERIOD (Bit 20) */ -#define CLKGEN_BLEBUCKTONADJ_TONADJUSTPERIOD_Msk (0x300000UL) /*!< CLKGEN BLEBUCKTONADJ: TONADJUSTPERIOD (Bitfield-Mask: 0x03) */ -#define CLKGEN_BLEBUCKTONADJ_TONHIGHTHRESHOLD_Pos (10UL) /*!< CLKGEN BLEBUCKTONADJ: TONHIGHTHRESHOLD (Bit 10) */ -#define CLKGEN_BLEBUCKTONADJ_TONHIGHTHRESHOLD_Msk (0xffc00UL) /*!< CLKGEN BLEBUCKTONADJ: TONHIGHTHRESHOLD (Bitfield-Mask: 0x3ff) */ -#define CLKGEN_BLEBUCKTONADJ_TONLOWTHRESHOLD_Pos (0UL) /*!< CLKGEN BLEBUCKTONADJ: TONLOWTHRESHOLD (Bit 0) */ -#define CLKGEN_BLEBUCKTONADJ_TONLOWTHRESHOLD_Msk (0x3ffUL) /*!< CLKGEN BLEBUCKTONADJ: TONLOWTHRESHOLD (Bitfield-Mask: 0x3ff) */ -/* ======================================================= INTRPTEN ======================================================== */ -#define CLKGEN_INTRPTEN_OF_Pos (2UL) /*!< CLKGEN INTRPTEN: OF (Bit 2) */ -#define CLKGEN_INTRPTEN_OF_Msk (0x4UL) /*!< CLKGEN INTRPTEN: OF (Bitfield-Mask: 0x01) */ -#define CLKGEN_INTRPTEN_ACC_Pos (1UL) /*!< CLKGEN INTRPTEN: ACC (Bit 1) */ -#define CLKGEN_INTRPTEN_ACC_Msk (0x2UL) /*!< CLKGEN INTRPTEN: ACC (Bitfield-Mask: 0x01) */ -#define CLKGEN_INTRPTEN_ACF_Pos (0UL) /*!< CLKGEN INTRPTEN: ACF (Bit 0) */ -#define CLKGEN_INTRPTEN_ACF_Msk (0x1UL) /*!< CLKGEN INTRPTEN: ACF (Bitfield-Mask: 0x01) */ -/* ====================================================== INTRPTSTAT ======================================================= */ -#define CLKGEN_INTRPTSTAT_OF_Pos (2UL) /*!< CLKGEN INTRPTSTAT: OF (Bit 2) */ -#define CLKGEN_INTRPTSTAT_OF_Msk (0x4UL) /*!< CLKGEN INTRPTSTAT: OF (Bitfield-Mask: 0x01) */ -#define CLKGEN_INTRPTSTAT_ACC_Pos (1UL) /*!< CLKGEN INTRPTSTAT: ACC (Bit 1) */ -#define CLKGEN_INTRPTSTAT_ACC_Msk (0x2UL) /*!< CLKGEN INTRPTSTAT: ACC (Bitfield-Mask: 0x01) */ -#define CLKGEN_INTRPTSTAT_ACF_Pos (0UL) /*!< CLKGEN INTRPTSTAT: ACF (Bit 0) */ -#define CLKGEN_INTRPTSTAT_ACF_Msk (0x1UL) /*!< CLKGEN INTRPTSTAT: ACF (Bitfield-Mask: 0x01) */ -/* ======================================================= INTRPTCLR ======================================================= */ -#define CLKGEN_INTRPTCLR_OF_Pos (2UL) /*!< CLKGEN INTRPTCLR: OF (Bit 2) */ -#define CLKGEN_INTRPTCLR_OF_Msk (0x4UL) /*!< CLKGEN INTRPTCLR: OF (Bitfield-Mask: 0x01) */ -#define CLKGEN_INTRPTCLR_ACC_Pos (1UL) /*!< CLKGEN INTRPTCLR: ACC (Bit 1) */ -#define CLKGEN_INTRPTCLR_ACC_Msk (0x2UL) /*!< CLKGEN INTRPTCLR: ACC (Bitfield-Mask: 0x01) */ -#define CLKGEN_INTRPTCLR_ACF_Pos (0UL) /*!< CLKGEN INTRPTCLR: ACF (Bit 0) */ -#define CLKGEN_INTRPTCLR_ACF_Msk (0x1UL) /*!< CLKGEN INTRPTCLR: ACF (Bitfield-Mask: 0x01) */ -/* ======================================================= INTRPTSET ======================================================= */ -#define CLKGEN_INTRPTSET_OF_Pos (2UL) /*!< CLKGEN INTRPTSET: OF (Bit 2) */ -#define CLKGEN_INTRPTSET_OF_Msk (0x4UL) /*!< CLKGEN INTRPTSET: OF (Bitfield-Mask: 0x01) */ -#define CLKGEN_INTRPTSET_ACC_Pos (1UL) /*!< CLKGEN INTRPTSET: ACC (Bit 1) */ -#define CLKGEN_INTRPTSET_ACC_Msk (0x2UL) /*!< CLKGEN INTRPTSET: ACC (Bitfield-Mask: 0x01) */ -#define CLKGEN_INTRPTSET_ACF_Pos (0UL) /*!< CLKGEN INTRPTSET: ACF (Bit 0) */ -#define CLKGEN_INTRPTSET_ACF_Msk (0x1UL) /*!< CLKGEN INTRPTSET: ACF (Bitfield-Mask: 0x01) */ - - -/* =========================================================================================================================== */ -/* ================ CTIMER ================ */ -/* =========================================================================================================================== */ - -/* ========================================================= TMR0 ========================================================== */ -#define CTIMER_TMR0_CTTMRB0_Pos (16UL) /*!< CTIMER TMR0: CTTMRB0 (Bit 16) */ -#define CTIMER_TMR0_CTTMRB0_Msk (0xffff0000UL) /*!< CTIMER TMR0: CTTMRB0 (Bitfield-Mask: 0xffff) */ -#define CTIMER_TMR0_CTTMRA0_Pos (0UL) /*!< CTIMER TMR0: CTTMRA0 (Bit 0) */ -#define CTIMER_TMR0_CTTMRA0_Msk (0xffffUL) /*!< CTIMER TMR0: CTTMRA0 (Bitfield-Mask: 0xffff) */ -/* ======================================================== CMPRA0 ========================================================= */ -#define CTIMER_CMPRA0_CMPR1A0_Pos (16UL) /*!< CTIMER CMPRA0: CMPR1A0 (Bit 16) */ -#define CTIMER_CMPRA0_CMPR1A0_Msk (0xffff0000UL) /*!< CTIMER CMPRA0: CMPR1A0 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRA0_CMPR0A0_Pos (0UL) /*!< CTIMER CMPRA0: CMPR0A0 (Bit 0) */ -#define CTIMER_CMPRA0_CMPR0A0_Msk (0xffffUL) /*!< CTIMER CMPRA0: CMPR0A0 (Bitfield-Mask: 0xffff) */ -/* ======================================================== CMPRB0 ========================================================= */ -#define CTIMER_CMPRB0_CMPR1B0_Pos (16UL) /*!< CTIMER CMPRB0: CMPR1B0 (Bit 16) */ -#define CTIMER_CMPRB0_CMPR1B0_Msk (0xffff0000UL) /*!< CTIMER CMPRB0: CMPR1B0 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRB0_CMPR0B0_Pos (0UL) /*!< CTIMER CMPRB0: CMPR0B0 (Bit 0) */ -#define CTIMER_CMPRB0_CMPR0B0_Msk (0xffffUL) /*!< CTIMER CMPRB0: CMPR0B0 (Bitfield-Mask: 0xffff) */ -/* ========================================================= CTRL0 ========================================================= */ -#define CTIMER_CTRL0_CTLINK0_Pos (31UL) /*!< CTIMER CTRL0: CTLINK0 (Bit 31) */ -#define CTIMER_CTRL0_CTLINK0_Msk (0x80000000UL) /*!< CTIMER CTRL0: CTLINK0 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL0_TMRB0POL_Pos (28UL) /*!< CTIMER CTRL0: TMRB0POL (Bit 28) */ -#define CTIMER_CTRL0_TMRB0POL_Msk (0x10000000UL) /*!< CTIMER CTRL0: TMRB0POL (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL0_TMRB0CLR_Pos (27UL) /*!< CTIMER CTRL0: TMRB0CLR (Bit 27) */ -#define CTIMER_CTRL0_TMRB0CLR_Msk (0x8000000UL) /*!< CTIMER CTRL0: TMRB0CLR (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL0_TMRB0IE1_Pos (26UL) /*!< CTIMER CTRL0: TMRB0IE1 (Bit 26) */ -#define CTIMER_CTRL0_TMRB0IE1_Msk (0x4000000UL) /*!< CTIMER CTRL0: TMRB0IE1 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL0_TMRB0IE0_Pos (25UL) /*!< CTIMER CTRL0: TMRB0IE0 (Bit 25) */ -#define CTIMER_CTRL0_TMRB0IE0_Msk (0x2000000UL) /*!< CTIMER CTRL0: TMRB0IE0 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL0_TMRB0FN_Pos (22UL) /*!< CTIMER CTRL0: TMRB0FN (Bit 22) */ -#define CTIMER_CTRL0_TMRB0FN_Msk (0x1c00000UL) /*!< CTIMER CTRL0: TMRB0FN (Bitfield-Mask: 0x07) */ -#define CTIMER_CTRL0_TMRB0CLK_Pos (17UL) /*!< CTIMER CTRL0: TMRB0CLK (Bit 17) */ -#define CTIMER_CTRL0_TMRB0CLK_Msk (0x3e0000UL) /*!< CTIMER CTRL0: TMRB0CLK (Bitfield-Mask: 0x1f) */ -#define CTIMER_CTRL0_TMRB0EN_Pos (16UL) /*!< CTIMER CTRL0: TMRB0EN (Bit 16) */ -#define CTIMER_CTRL0_TMRB0EN_Msk (0x10000UL) /*!< CTIMER CTRL0: TMRB0EN (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL0_TMRA0POL_Pos (12UL) /*!< CTIMER CTRL0: TMRA0POL (Bit 12) */ -#define CTIMER_CTRL0_TMRA0POL_Msk (0x1000UL) /*!< CTIMER CTRL0: TMRA0POL (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL0_TMRA0CLR_Pos (11UL) /*!< CTIMER CTRL0: TMRA0CLR (Bit 11) */ -#define CTIMER_CTRL0_TMRA0CLR_Msk (0x800UL) /*!< CTIMER CTRL0: TMRA0CLR (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL0_TMRA0IE1_Pos (10UL) /*!< CTIMER CTRL0: TMRA0IE1 (Bit 10) */ -#define CTIMER_CTRL0_TMRA0IE1_Msk (0x400UL) /*!< CTIMER CTRL0: TMRA0IE1 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL0_TMRA0IE0_Pos (9UL) /*!< CTIMER CTRL0: TMRA0IE0 (Bit 9) */ -#define CTIMER_CTRL0_TMRA0IE0_Msk (0x200UL) /*!< CTIMER CTRL0: TMRA0IE0 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL0_TMRA0FN_Pos (6UL) /*!< CTIMER CTRL0: TMRA0FN (Bit 6) */ -#define CTIMER_CTRL0_TMRA0FN_Msk (0x1c0UL) /*!< CTIMER CTRL0: TMRA0FN (Bitfield-Mask: 0x07) */ -#define CTIMER_CTRL0_TMRA0CLK_Pos (1UL) /*!< CTIMER CTRL0: TMRA0CLK (Bit 1) */ -#define CTIMER_CTRL0_TMRA0CLK_Msk (0x3eUL) /*!< CTIMER CTRL0: TMRA0CLK (Bitfield-Mask: 0x1f) */ -#define CTIMER_CTRL0_TMRA0EN_Pos (0UL) /*!< CTIMER CTRL0: TMRA0EN (Bit 0) */ -#define CTIMER_CTRL0_TMRA0EN_Msk (0x1UL) /*!< CTIMER CTRL0: TMRA0EN (Bitfield-Mask: 0x01) */ -/* ======================================================= CMPRAUXA0 ======================================================= */ -#define CTIMER_CMPRAUXA0_CMPR3A0_Pos (16UL) /*!< CTIMER CMPRAUXA0: CMPR3A0 (Bit 16) */ -#define CTIMER_CMPRAUXA0_CMPR3A0_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXA0: CMPR3A0 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRAUXA0_CMPR2A0_Pos (0UL) /*!< CTIMER CMPRAUXA0: CMPR2A0 (Bit 0) */ -#define CTIMER_CMPRAUXA0_CMPR2A0_Msk (0xffffUL) /*!< CTIMER CMPRAUXA0: CMPR2A0 (Bitfield-Mask: 0xffff) */ -/* ======================================================= CMPRAUXB0 ======================================================= */ -#define CTIMER_CMPRAUXB0_CMPR3B0_Pos (16UL) /*!< CTIMER CMPRAUXB0: CMPR3B0 (Bit 16) */ -#define CTIMER_CMPRAUXB0_CMPR3B0_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXB0: CMPR3B0 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRAUXB0_CMPR2B0_Pos (0UL) /*!< CTIMER CMPRAUXB0: CMPR2B0 (Bit 0) */ -#define CTIMER_CMPRAUXB0_CMPR2B0_Msk (0xffffUL) /*!< CTIMER CMPRAUXB0: CMPR2B0 (Bitfield-Mask: 0xffff) */ -/* ========================================================= AUX0 ========================================================== */ -#define CTIMER_AUX0_TMRB0EN23_Pos (30UL) /*!< CTIMER AUX0: TMRB0EN23 (Bit 30) */ -#define CTIMER_AUX0_TMRB0EN23_Msk (0x40000000UL) /*!< CTIMER AUX0: TMRB0EN23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX0_TMRB0POL23_Pos (29UL) /*!< CTIMER AUX0: TMRB0POL23 (Bit 29) */ -#define CTIMER_AUX0_TMRB0POL23_Msk (0x20000000UL) /*!< CTIMER AUX0: TMRB0POL23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX0_TMRB0TINV_Pos (28UL) /*!< CTIMER AUX0: TMRB0TINV (Bit 28) */ -#define CTIMER_AUX0_TMRB0TINV_Msk (0x10000000UL) /*!< CTIMER AUX0: TMRB0TINV (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX0_TMRB0NOSYNC_Pos (27UL) /*!< CTIMER AUX0: TMRB0NOSYNC (Bit 27) */ -#define CTIMER_AUX0_TMRB0NOSYNC_Msk (0x8000000UL) /*!< CTIMER AUX0: TMRB0NOSYNC (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX0_TMRB0TRIG_Pos (23UL) /*!< CTIMER AUX0: TMRB0TRIG (Bit 23) */ -#define CTIMER_AUX0_TMRB0TRIG_Msk (0x7800000UL) /*!< CTIMER AUX0: TMRB0TRIG (Bitfield-Mask: 0x0f) */ -#define CTIMER_AUX0_TMRB0LMT_Pos (16UL) /*!< CTIMER AUX0: TMRB0LMT (Bit 16) */ -#define CTIMER_AUX0_TMRB0LMT_Msk (0x3f0000UL) /*!< CTIMER AUX0: TMRB0LMT (Bitfield-Mask: 0x3f) */ -#define CTIMER_AUX0_TMRA0EN23_Pos (14UL) /*!< CTIMER AUX0: TMRA0EN23 (Bit 14) */ -#define CTIMER_AUX0_TMRA0EN23_Msk (0x4000UL) /*!< CTIMER AUX0: TMRA0EN23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX0_TMRA0POL23_Pos (13UL) /*!< CTIMER AUX0: TMRA0POL23 (Bit 13) */ -#define CTIMER_AUX0_TMRA0POL23_Msk (0x2000UL) /*!< CTIMER AUX0: TMRA0POL23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX0_TMRA0TINV_Pos (12UL) /*!< CTIMER AUX0: TMRA0TINV (Bit 12) */ -#define CTIMER_AUX0_TMRA0TINV_Msk (0x1000UL) /*!< CTIMER AUX0: TMRA0TINV (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX0_TMRA0NOSYNC_Pos (11UL) /*!< CTIMER AUX0: TMRA0NOSYNC (Bit 11) */ -#define CTIMER_AUX0_TMRA0NOSYNC_Msk (0x800UL) /*!< CTIMER AUX0: TMRA0NOSYNC (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX0_TMRA0TRIG_Pos (7UL) /*!< CTIMER AUX0: TMRA0TRIG (Bit 7) */ -#define CTIMER_AUX0_TMRA0TRIG_Msk (0x780UL) /*!< CTIMER AUX0: TMRA0TRIG (Bitfield-Mask: 0x0f) */ -#define CTIMER_AUX0_TMRA0LMT_Pos (0UL) /*!< CTIMER AUX0: TMRA0LMT (Bit 0) */ -#define CTIMER_AUX0_TMRA0LMT_Msk (0x7fUL) /*!< CTIMER AUX0: TMRA0LMT (Bitfield-Mask: 0x7f) */ -/* ========================================================= TMR1 ========================================================== */ -#define CTIMER_TMR1_CTTMRB1_Pos (16UL) /*!< CTIMER TMR1: CTTMRB1 (Bit 16) */ -#define CTIMER_TMR1_CTTMRB1_Msk (0xffff0000UL) /*!< CTIMER TMR1: CTTMRB1 (Bitfield-Mask: 0xffff) */ -#define CTIMER_TMR1_CTTMRA1_Pos (0UL) /*!< CTIMER TMR1: CTTMRA1 (Bit 0) */ -#define CTIMER_TMR1_CTTMRA1_Msk (0xffffUL) /*!< CTIMER TMR1: CTTMRA1 (Bitfield-Mask: 0xffff) */ -/* ======================================================== CMPRA1 ========================================================= */ -#define CTIMER_CMPRA1_CMPR1A1_Pos (16UL) /*!< CTIMER CMPRA1: CMPR1A1 (Bit 16) */ -#define CTIMER_CMPRA1_CMPR1A1_Msk (0xffff0000UL) /*!< CTIMER CMPRA1: CMPR1A1 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRA1_CMPR0A1_Pos (0UL) /*!< CTIMER CMPRA1: CMPR0A1 (Bit 0) */ -#define CTIMER_CMPRA1_CMPR0A1_Msk (0xffffUL) /*!< CTIMER CMPRA1: CMPR0A1 (Bitfield-Mask: 0xffff) */ -/* ======================================================== CMPRB1 ========================================================= */ -#define CTIMER_CMPRB1_CMPR1B1_Pos (16UL) /*!< CTIMER CMPRB1: CMPR1B1 (Bit 16) */ -#define CTIMER_CMPRB1_CMPR1B1_Msk (0xffff0000UL) /*!< CTIMER CMPRB1: CMPR1B1 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRB1_CMPR0B1_Pos (0UL) /*!< CTIMER CMPRB1: CMPR0B1 (Bit 0) */ -#define CTIMER_CMPRB1_CMPR0B1_Msk (0xffffUL) /*!< CTIMER CMPRB1: CMPR0B1 (Bitfield-Mask: 0xffff) */ -/* ========================================================= CTRL1 ========================================================= */ -#define CTIMER_CTRL1_CTLINK1_Pos (31UL) /*!< CTIMER CTRL1: CTLINK1 (Bit 31) */ -#define CTIMER_CTRL1_CTLINK1_Msk (0x80000000UL) /*!< CTIMER CTRL1: CTLINK1 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL1_TMRB1POL_Pos (28UL) /*!< CTIMER CTRL1: TMRB1POL (Bit 28) */ -#define CTIMER_CTRL1_TMRB1POL_Msk (0x10000000UL) /*!< CTIMER CTRL1: TMRB1POL (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL1_TMRB1CLR_Pos (27UL) /*!< CTIMER CTRL1: TMRB1CLR (Bit 27) */ -#define CTIMER_CTRL1_TMRB1CLR_Msk (0x8000000UL) /*!< CTIMER CTRL1: TMRB1CLR (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL1_TMRB1IE1_Pos (26UL) /*!< CTIMER CTRL1: TMRB1IE1 (Bit 26) */ -#define CTIMER_CTRL1_TMRB1IE1_Msk (0x4000000UL) /*!< CTIMER CTRL1: TMRB1IE1 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL1_TMRB1IE0_Pos (25UL) /*!< CTIMER CTRL1: TMRB1IE0 (Bit 25) */ -#define CTIMER_CTRL1_TMRB1IE0_Msk (0x2000000UL) /*!< CTIMER CTRL1: TMRB1IE0 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL1_TMRB1FN_Pos (22UL) /*!< CTIMER CTRL1: TMRB1FN (Bit 22) */ -#define CTIMER_CTRL1_TMRB1FN_Msk (0x1c00000UL) /*!< CTIMER CTRL1: TMRB1FN (Bitfield-Mask: 0x07) */ -#define CTIMER_CTRL1_TMRB1CLK_Pos (17UL) /*!< CTIMER CTRL1: TMRB1CLK (Bit 17) */ -#define CTIMER_CTRL1_TMRB1CLK_Msk (0x3e0000UL) /*!< CTIMER CTRL1: TMRB1CLK (Bitfield-Mask: 0x1f) */ -#define CTIMER_CTRL1_TMRB1EN_Pos (16UL) /*!< CTIMER CTRL1: TMRB1EN (Bit 16) */ -#define CTIMER_CTRL1_TMRB1EN_Msk (0x10000UL) /*!< CTIMER CTRL1: TMRB1EN (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL1_TMRA1POL_Pos (12UL) /*!< CTIMER CTRL1: TMRA1POL (Bit 12) */ -#define CTIMER_CTRL1_TMRA1POL_Msk (0x1000UL) /*!< CTIMER CTRL1: TMRA1POL (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL1_TMRA1CLR_Pos (11UL) /*!< CTIMER CTRL1: TMRA1CLR (Bit 11) */ -#define CTIMER_CTRL1_TMRA1CLR_Msk (0x800UL) /*!< CTIMER CTRL1: TMRA1CLR (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL1_TMRA1IE1_Pos (10UL) /*!< CTIMER CTRL1: TMRA1IE1 (Bit 10) */ -#define CTIMER_CTRL1_TMRA1IE1_Msk (0x400UL) /*!< CTIMER CTRL1: TMRA1IE1 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL1_TMRA1IE0_Pos (9UL) /*!< CTIMER CTRL1: TMRA1IE0 (Bit 9) */ -#define CTIMER_CTRL1_TMRA1IE0_Msk (0x200UL) /*!< CTIMER CTRL1: TMRA1IE0 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL1_TMRA1FN_Pos (6UL) /*!< CTIMER CTRL1: TMRA1FN (Bit 6) */ -#define CTIMER_CTRL1_TMRA1FN_Msk (0x1c0UL) /*!< CTIMER CTRL1: TMRA1FN (Bitfield-Mask: 0x07) */ -#define CTIMER_CTRL1_TMRA1CLK_Pos (1UL) /*!< CTIMER CTRL1: TMRA1CLK (Bit 1) */ -#define CTIMER_CTRL1_TMRA1CLK_Msk (0x3eUL) /*!< CTIMER CTRL1: TMRA1CLK (Bitfield-Mask: 0x1f) */ -#define CTIMER_CTRL1_TMRA1EN_Pos (0UL) /*!< CTIMER CTRL1: TMRA1EN (Bit 0) */ -#define CTIMER_CTRL1_TMRA1EN_Msk (0x1UL) /*!< CTIMER CTRL1: TMRA1EN (Bitfield-Mask: 0x01) */ -/* ======================================================= CMPRAUXA1 ======================================================= */ -#define CTIMER_CMPRAUXA1_CMPR3A1_Pos (16UL) /*!< CTIMER CMPRAUXA1: CMPR3A1 (Bit 16) */ -#define CTIMER_CMPRAUXA1_CMPR3A1_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXA1: CMPR3A1 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRAUXA1_CMPR2A1_Pos (0UL) /*!< CTIMER CMPRAUXA1: CMPR2A1 (Bit 0) */ -#define CTIMER_CMPRAUXA1_CMPR2A1_Msk (0xffffUL) /*!< CTIMER CMPRAUXA1: CMPR2A1 (Bitfield-Mask: 0xffff) */ -/* ======================================================= CMPRAUXB1 ======================================================= */ -#define CTIMER_CMPRAUXB1_CMPR3B1_Pos (16UL) /*!< CTIMER CMPRAUXB1: CMPR3B1 (Bit 16) */ -#define CTIMER_CMPRAUXB1_CMPR3B1_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXB1: CMPR3B1 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRAUXB1_CMPR2B1_Pos (0UL) /*!< CTIMER CMPRAUXB1: CMPR2B1 (Bit 0) */ -#define CTIMER_CMPRAUXB1_CMPR2B1_Msk (0xffffUL) /*!< CTIMER CMPRAUXB1: CMPR2B1 (Bitfield-Mask: 0xffff) */ -/* ========================================================= AUX1 ========================================================== */ -#define CTIMER_AUX1_TMRB1EN23_Pos (30UL) /*!< CTIMER AUX1: TMRB1EN23 (Bit 30) */ -#define CTIMER_AUX1_TMRB1EN23_Msk (0x40000000UL) /*!< CTIMER AUX1: TMRB1EN23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX1_TMRB1POL23_Pos (29UL) /*!< CTIMER AUX1: TMRB1POL23 (Bit 29) */ -#define CTIMER_AUX1_TMRB1POL23_Msk (0x20000000UL) /*!< CTIMER AUX1: TMRB1POL23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX1_TMRB1TINV_Pos (28UL) /*!< CTIMER AUX1: TMRB1TINV (Bit 28) */ -#define CTIMER_AUX1_TMRB1TINV_Msk (0x10000000UL) /*!< CTIMER AUX1: TMRB1TINV (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX1_TMRB1NOSYNC_Pos (27UL) /*!< CTIMER AUX1: TMRB1NOSYNC (Bit 27) */ -#define CTIMER_AUX1_TMRB1NOSYNC_Msk (0x8000000UL) /*!< CTIMER AUX1: TMRB1NOSYNC (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX1_TMRB1TRIG_Pos (23UL) /*!< CTIMER AUX1: TMRB1TRIG (Bit 23) */ -#define CTIMER_AUX1_TMRB1TRIG_Msk (0x7800000UL) /*!< CTIMER AUX1: TMRB1TRIG (Bitfield-Mask: 0x0f) */ -#define CTIMER_AUX1_TMRB1LMT_Pos (16UL) /*!< CTIMER AUX1: TMRB1LMT (Bit 16) */ -#define CTIMER_AUX1_TMRB1LMT_Msk (0x3f0000UL) /*!< CTIMER AUX1: TMRB1LMT (Bitfield-Mask: 0x3f) */ -#define CTIMER_AUX1_TMRA1EN23_Pos (14UL) /*!< CTIMER AUX1: TMRA1EN23 (Bit 14) */ -#define CTIMER_AUX1_TMRA1EN23_Msk (0x4000UL) /*!< CTIMER AUX1: TMRA1EN23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX1_TMRA1POL23_Pos (13UL) /*!< CTIMER AUX1: TMRA1POL23 (Bit 13) */ -#define CTIMER_AUX1_TMRA1POL23_Msk (0x2000UL) /*!< CTIMER AUX1: TMRA1POL23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX1_TMRA1TINV_Pos (12UL) /*!< CTIMER AUX1: TMRA1TINV (Bit 12) */ -#define CTIMER_AUX1_TMRA1TINV_Msk (0x1000UL) /*!< CTIMER AUX1: TMRA1TINV (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX1_TMRA1NOSYNC_Pos (11UL) /*!< CTIMER AUX1: TMRA1NOSYNC (Bit 11) */ -#define CTIMER_AUX1_TMRA1NOSYNC_Msk (0x800UL) /*!< CTIMER AUX1: TMRA1NOSYNC (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX1_TMRA1TRIG_Pos (7UL) /*!< CTIMER AUX1: TMRA1TRIG (Bit 7) */ -#define CTIMER_AUX1_TMRA1TRIG_Msk (0x780UL) /*!< CTIMER AUX1: TMRA1TRIG (Bitfield-Mask: 0x0f) */ -#define CTIMER_AUX1_TMRA1LMT_Pos (0UL) /*!< CTIMER AUX1: TMRA1LMT (Bit 0) */ -#define CTIMER_AUX1_TMRA1LMT_Msk (0x7fUL) /*!< CTIMER AUX1: TMRA1LMT (Bitfield-Mask: 0x7f) */ -/* ========================================================= TMR2 ========================================================== */ -#define CTIMER_TMR2_CTTMRB2_Pos (16UL) /*!< CTIMER TMR2: CTTMRB2 (Bit 16) */ -#define CTIMER_TMR2_CTTMRB2_Msk (0xffff0000UL) /*!< CTIMER TMR2: CTTMRB2 (Bitfield-Mask: 0xffff) */ -#define CTIMER_TMR2_CTTMRA2_Pos (0UL) /*!< CTIMER TMR2: CTTMRA2 (Bit 0) */ -#define CTIMER_TMR2_CTTMRA2_Msk (0xffffUL) /*!< CTIMER TMR2: CTTMRA2 (Bitfield-Mask: 0xffff) */ -/* ======================================================== CMPRA2 ========================================================= */ -#define CTIMER_CMPRA2_CMPR1A2_Pos (16UL) /*!< CTIMER CMPRA2: CMPR1A2 (Bit 16) */ -#define CTIMER_CMPRA2_CMPR1A2_Msk (0xffff0000UL) /*!< CTIMER CMPRA2: CMPR1A2 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRA2_CMPR0A2_Pos (0UL) /*!< CTIMER CMPRA2: CMPR0A2 (Bit 0) */ -#define CTIMER_CMPRA2_CMPR0A2_Msk (0xffffUL) /*!< CTIMER CMPRA2: CMPR0A2 (Bitfield-Mask: 0xffff) */ -/* ======================================================== CMPRB2 ========================================================= */ -#define CTIMER_CMPRB2_CMPR1B2_Pos (16UL) /*!< CTIMER CMPRB2: CMPR1B2 (Bit 16) */ -#define CTIMER_CMPRB2_CMPR1B2_Msk (0xffff0000UL) /*!< CTIMER CMPRB2: CMPR1B2 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRB2_CMPR0B2_Pos (0UL) /*!< CTIMER CMPRB2: CMPR0B2 (Bit 0) */ -#define CTIMER_CMPRB2_CMPR0B2_Msk (0xffffUL) /*!< CTIMER CMPRB2: CMPR0B2 (Bitfield-Mask: 0xffff) */ -/* ========================================================= CTRL2 ========================================================= */ -#define CTIMER_CTRL2_CTLINK2_Pos (31UL) /*!< CTIMER CTRL2: CTLINK2 (Bit 31) */ -#define CTIMER_CTRL2_CTLINK2_Msk (0x80000000UL) /*!< CTIMER CTRL2: CTLINK2 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL2_TMRB2POL_Pos (28UL) /*!< CTIMER CTRL2: TMRB2POL (Bit 28) */ -#define CTIMER_CTRL2_TMRB2POL_Msk (0x10000000UL) /*!< CTIMER CTRL2: TMRB2POL (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL2_TMRB2CLR_Pos (27UL) /*!< CTIMER CTRL2: TMRB2CLR (Bit 27) */ -#define CTIMER_CTRL2_TMRB2CLR_Msk (0x8000000UL) /*!< CTIMER CTRL2: TMRB2CLR (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL2_TMRB2IE1_Pos (26UL) /*!< CTIMER CTRL2: TMRB2IE1 (Bit 26) */ -#define CTIMER_CTRL2_TMRB2IE1_Msk (0x4000000UL) /*!< CTIMER CTRL2: TMRB2IE1 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL2_TMRB2IE0_Pos (25UL) /*!< CTIMER CTRL2: TMRB2IE0 (Bit 25) */ -#define CTIMER_CTRL2_TMRB2IE0_Msk (0x2000000UL) /*!< CTIMER CTRL2: TMRB2IE0 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL2_TMRB2FN_Pos (22UL) /*!< CTIMER CTRL2: TMRB2FN (Bit 22) */ -#define CTIMER_CTRL2_TMRB2FN_Msk (0x1c00000UL) /*!< CTIMER CTRL2: TMRB2FN (Bitfield-Mask: 0x07) */ -#define CTIMER_CTRL2_TMRB2CLK_Pos (17UL) /*!< CTIMER CTRL2: TMRB2CLK (Bit 17) */ -#define CTIMER_CTRL2_TMRB2CLK_Msk (0x3e0000UL) /*!< CTIMER CTRL2: TMRB2CLK (Bitfield-Mask: 0x1f) */ -#define CTIMER_CTRL2_TMRB2EN_Pos (16UL) /*!< CTIMER CTRL2: TMRB2EN (Bit 16) */ -#define CTIMER_CTRL2_TMRB2EN_Msk (0x10000UL) /*!< CTIMER CTRL2: TMRB2EN (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL2_TMRA2POL_Pos (12UL) /*!< CTIMER CTRL2: TMRA2POL (Bit 12) */ -#define CTIMER_CTRL2_TMRA2POL_Msk (0x1000UL) /*!< CTIMER CTRL2: TMRA2POL (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL2_TMRA2CLR_Pos (11UL) /*!< CTIMER CTRL2: TMRA2CLR (Bit 11) */ -#define CTIMER_CTRL2_TMRA2CLR_Msk (0x800UL) /*!< CTIMER CTRL2: TMRA2CLR (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL2_TMRA2IE1_Pos (10UL) /*!< CTIMER CTRL2: TMRA2IE1 (Bit 10) */ -#define CTIMER_CTRL2_TMRA2IE1_Msk (0x400UL) /*!< CTIMER CTRL2: TMRA2IE1 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL2_TMRA2IE0_Pos (9UL) /*!< CTIMER CTRL2: TMRA2IE0 (Bit 9) */ -#define CTIMER_CTRL2_TMRA2IE0_Msk (0x200UL) /*!< CTIMER CTRL2: TMRA2IE0 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL2_TMRA2FN_Pos (6UL) /*!< CTIMER CTRL2: TMRA2FN (Bit 6) */ -#define CTIMER_CTRL2_TMRA2FN_Msk (0x1c0UL) /*!< CTIMER CTRL2: TMRA2FN (Bitfield-Mask: 0x07) */ -#define CTIMER_CTRL2_TMRA2CLK_Pos (1UL) /*!< CTIMER CTRL2: TMRA2CLK (Bit 1) */ -#define CTIMER_CTRL2_TMRA2CLK_Msk (0x3eUL) /*!< CTIMER CTRL2: TMRA2CLK (Bitfield-Mask: 0x1f) */ -#define CTIMER_CTRL2_TMRA2EN_Pos (0UL) /*!< CTIMER CTRL2: TMRA2EN (Bit 0) */ -#define CTIMER_CTRL2_TMRA2EN_Msk (0x1UL) /*!< CTIMER CTRL2: TMRA2EN (Bitfield-Mask: 0x01) */ -/* ======================================================= CMPRAUXA2 ======================================================= */ -#define CTIMER_CMPRAUXA2_CMPR3A2_Pos (16UL) /*!< CTIMER CMPRAUXA2: CMPR3A2 (Bit 16) */ -#define CTIMER_CMPRAUXA2_CMPR3A2_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXA2: CMPR3A2 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRAUXA2_CMPR2A2_Pos (0UL) /*!< CTIMER CMPRAUXA2: CMPR2A2 (Bit 0) */ -#define CTIMER_CMPRAUXA2_CMPR2A2_Msk (0xffffUL) /*!< CTIMER CMPRAUXA2: CMPR2A2 (Bitfield-Mask: 0xffff) */ -/* ======================================================= CMPRAUXB2 ======================================================= */ -#define CTIMER_CMPRAUXB2_CMPR3B2_Pos (16UL) /*!< CTIMER CMPRAUXB2: CMPR3B2 (Bit 16) */ -#define CTIMER_CMPRAUXB2_CMPR3B2_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXB2: CMPR3B2 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRAUXB2_CMPR2B2_Pos (0UL) /*!< CTIMER CMPRAUXB2: CMPR2B2 (Bit 0) */ -#define CTIMER_CMPRAUXB2_CMPR2B2_Msk (0xffffUL) /*!< CTIMER CMPRAUXB2: CMPR2B2 (Bitfield-Mask: 0xffff) */ -/* ========================================================= AUX2 ========================================================== */ -#define CTIMER_AUX2_TMRB2EN23_Pos (30UL) /*!< CTIMER AUX2: TMRB2EN23 (Bit 30) */ -#define CTIMER_AUX2_TMRB2EN23_Msk (0x40000000UL) /*!< CTIMER AUX2: TMRB2EN23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX2_TMRB2POL23_Pos (29UL) /*!< CTIMER AUX2: TMRB2POL23 (Bit 29) */ -#define CTIMER_AUX2_TMRB2POL23_Msk (0x20000000UL) /*!< CTIMER AUX2: TMRB2POL23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX2_TMRB2TINV_Pos (28UL) /*!< CTIMER AUX2: TMRB2TINV (Bit 28) */ -#define CTIMER_AUX2_TMRB2TINV_Msk (0x10000000UL) /*!< CTIMER AUX2: TMRB2TINV (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX2_TMRB2NOSYNC_Pos (27UL) /*!< CTIMER AUX2: TMRB2NOSYNC (Bit 27) */ -#define CTIMER_AUX2_TMRB2NOSYNC_Msk (0x8000000UL) /*!< CTIMER AUX2: TMRB2NOSYNC (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX2_TMRB2TRIG_Pos (23UL) /*!< CTIMER AUX2: TMRB2TRIG (Bit 23) */ -#define CTIMER_AUX2_TMRB2TRIG_Msk (0x7800000UL) /*!< CTIMER AUX2: TMRB2TRIG (Bitfield-Mask: 0x0f) */ -#define CTIMER_AUX2_TMRB2LMT_Pos (16UL) /*!< CTIMER AUX2: TMRB2LMT (Bit 16) */ -#define CTIMER_AUX2_TMRB2LMT_Msk (0x3f0000UL) /*!< CTIMER AUX2: TMRB2LMT (Bitfield-Mask: 0x3f) */ -#define CTIMER_AUX2_TMRA2EN23_Pos (14UL) /*!< CTIMER AUX2: TMRA2EN23 (Bit 14) */ -#define CTIMER_AUX2_TMRA2EN23_Msk (0x4000UL) /*!< CTIMER AUX2: TMRA2EN23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX2_TMRA2POL23_Pos (13UL) /*!< CTIMER AUX2: TMRA2POL23 (Bit 13) */ -#define CTIMER_AUX2_TMRA2POL23_Msk (0x2000UL) /*!< CTIMER AUX2: TMRA2POL23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX2_TMRA2TINV_Pos (12UL) /*!< CTIMER AUX2: TMRA2TINV (Bit 12) */ -#define CTIMER_AUX2_TMRA2TINV_Msk (0x1000UL) /*!< CTIMER AUX2: TMRA2TINV (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX2_TMRA2NOSYNC_Pos (11UL) /*!< CTIMER AUX2: TMRA2NOSYNC (Bit 11) */ -#define CTIMER_AUX2_TMRA2NOSYNC_Msk (0x800UL) /*!< CTIMER AUX2: TMRA2NOSYNC (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX2_TMRA2TRIG_Pos (7UL) /*!< CTIMER AUX2: TMRA2TRIG (Bit 7) */ -#define CTIMER_AUX2_TMRA2TRIG_Msk (0x780UL) /*!< CTIMER AUX2: TMRA2TRIG (Bitfield-Mask: 0x0f) */ -#define CTIMER_AUX2_TMRA2LMT_Pos (0UL) /*!< CTIMER AUX2: TMRA2LMT (Bit 0) */ -#define CTIMER_AUX2_TMRA2LMT_Msk (0x7fUL) /*!< CTIMER AUX2: TMRA2LMT (Bitfield-Mask: 0x7f) */ -/* ========================================================= TMR3 ========================================================== */ -#define CTIMER_TMR3_CTTMRB3_Pos (16UL) /*!< CTIMER TMR3: CTTMRB3 (Bit 16) */ -#define CTIMER_TMR3_CTTMRB3_Msk (0xffff0000UL) /*!< CTIMER TMR3: CTTMRB3 (Bitfield-Mask: 0xffff) */ -#define CTIMER_TMR3_CTTMRA3_Pos (0UL) /*!< CTIMER TMR3: CTTMRA3 (Bit 0) */ -#define CTIMER_TMR3_CTTMRA3_Msk (0xffffUL) /*!< CTIMER TMR3: CTTMRA3 (Bitfield-Mask: 0xffff) */ -/* ======================================================== CMPRA3 ========================================================= */ -#define CTIMER_CMPRA3_CMPR1A3_Pos (16UL) /*!< CTIMER CMPRA3: CMPR1A3 (Bit 16) */ -#define CTIMER_CMPRA3_CMPR1A3_Msk (0xffff0000UL) /*!< CTIMER CMPRA3: CMPR1A3 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRA3_CMPR0A3_Pos (0UL) /*!< CTIMER CMPRA3: CMPR0A3 (Bit 0) */ -#define CTIMER_CMPRA3_CMPR0A3_Msk (0xffffUL) /*!< CTIMER CMPRA3: CMPR0A3 (Bitfield-Mask: 0xffff) */ -/* ======================================================== CMPRB3 ========================================================= */ -#define CTIMER_CMPRB3_CMPR1B3_Pos (16UL) /*!< CTIMER CMPRB3: CMPR1B3 (Bit 16) */ -#define CTIMER_CMPRB3_CMPR1B3_Msk (0xffff0000UL) /*!< CTIMER CMPRB3: CMPR1B3 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRB3_CMPR0B3_Pos (0UL) /*!< CTIMER CMPRB3: CMPR0B3 (Bit 0) */ -#define CTIMER_CMPRB3_CMPR0B3_Msk (0xffffUL) /*!< CTIMER CMPRB3: CMPR0B3 (Bitfield-Mask: 0xffff) */ -/* ========================================================= CTRL3 ========================================================= */ -#define CTIMER_CTRL3_CTLINK3_Pos (31UL) /*!< CTIMER CTRL3: CTLINK3 (Bit 31) */ -#define CTIMER_CTRL3_CTLINK3_Msk (0x80000000UL) /*!< CTIMER CTRL3: CTLINK3 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL3_TMRB3POL_Pos (28UL) /*!< CTIMER CTRL3: TMRB3POL (Bit 28) */ -#define CTIMER_CTRL3_TMRB3POL_Msk (0x10000000UL) /*!< CTIMER CTRL3: TMRB3POL (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL3_TMRB3CLR_Pos (27UL) /*!< CTIMER CTRL3: TMRB3CLR (Bit 27) */ -#define CTIMER_CTRL3_TMRB3CLR_Msk (0x8000000UL) /*!< CTIMER CTRL3: TMRB3CLR (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL3_TMRB3IE1_Pos (26UL) /*!< CTIMER CTRL3: TMRB3IE1 (Bit 26) */ -#define CTIMER_CTRL3_TMRB3IE1_Msk (0x4000000UL) /*!< CTIMER CTRL3: TMRB3IE1 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL3_TMRB3IE0_Pos (25UL) /*!< CTIMER CTRL3: TMRB3IE0 (Bit 25) */ -#define CTIMER_CTRL3_TMRB3IE0_Msk (0x2000000UL) /*!< CTIMER CTRL3: TMRB3IE0 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL3_TMRB3FN_Pos (22UL) /*!< CTIMER CTRL3: TMRB3FN (Bit 22) */ -#define CTIMER_CTRL3_TMRB3FN_Msk (0x1c00000UL) /*!< CTIMER CTRL3: TMRB3FN (Bitfield-Mask: 0x07) */ -#define CTIMER_CTRL3_TMRB3CLK_Pos (17UL) /*!< CTIMER CTRL3: TMRB3CLK (Bit 17) */ -#define CTIMER_CTRL3_TMRB3CLK_Msk (0x3e0000UL) /*!< CTIMER CTRL3: TMRB3CLK (Bitfield-Mask: 0x1f) */ -#define CTIMER_CTRL3_TMRB3EN_Pos (16UL) /*!< CTIMER CTRL3: TMRB3EN (Bit 16) */ -#define CTIMER_CTRL3_TMRB3EN_Msk (0x10000UL) /*!< CTIMER CTRL3: TMRB3EN (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL3_ADCEN_Pos (15UL) /*!< CTIMER CTRL3: ADCEN (Bit 15) */ -#define CTIMER_CTRL3_ADCEN_Msk (0x8000UL) /*!< CTIMER CTRL3: ADCEN (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL3_TMRA3POL_Pos (12UL) /*!< CTIMER CTRL3: TMRA3POL (Bit 12) */ -#define CTIMER_CTRL3_TMRA3POL_Msk (0x1000UL) /*!< CTIMER CTRL3: TMRA3POL (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL3_TMRA3CLR_Pos (11UL) /*!< CTIMER CTRL3: TMRA3CLR (Bit 11) */ -#define CTIMER_CTRL3_TMRA3CLR_Msk (0x800UL) /*!< CTIMER CTRL3: TMRA3CLR (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL3_TMRA3IE1_Pos (10UL) /*!< CTIMER CTRL3: TMRA3IE1 (Bit 10) */ -#define CTIMER_CTRL3_TMRA3IE1_Msk (0x400UL) /*!< CTIMER CTRL3: TMRA3IE1 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL3_TMRA3IE0_Pos (9UL) /*!< CTIMER CTRL3: TMRA3IE0 (Bit 9) */ -#define CTIMER_CTRL3_TMRA3IE0_Msk (0x200UL) /*!< CTIMER CTRL3: TMRA3IE0 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL3_TMRA3FN_Pos (6UL) /*!< CTIMER CTRL3: TMRA3FN (Bit 6) */ -#define CTIMER_CTRL3_TMRA3FN_Msk (0x1c0UL) /*!< CTIMER CTRL3: TMRA3FN (Bitfield-Mask: 0x07) */ -#define CTIMER_CTRL3_TMRA3CLK_Pos (1UL) /*!< CTIMER CTRL3: TMRA3CLK (Bit 1) */ -#define CTIMER_CTRL3_TMRA3CLK_Msk (0x3eUL) /*!< CTIMER CTRL3: TMRA3CLK (Bitfield-Mask: 0x1f) */ -#define CTIMER_CTRL3_TMRA3EN_Pos (0UL) /*!< CTIMER CTRL3: TMRA3EN (Bit 0) */ -#define CTIMER_CTRL3_TMRA3EN_Msk (0x1UL) /*!< CTIMER CTRL3: TMRA3EN (Bitfield-Mask: 0x01) */ -/* ======================================================= CMPRAUXA3 ======================================================= */ -#define CTIMER_CMPRAUXA3_CMPR3A3_Pos (16UL) /*!< CTIMER CMPRAUXA3: CMPR3A3 (Bit 16) */ -#define CTIMER_CMPRAUXA3_CMPR3A3_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXA3: CMPR3A3 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRAUXA3_CMPR2A3_Pos (0UL) /*!< CTIMER CMPRAUXA3: CMPR2A3 (Bit 0) */ -#define CTIMER_CMPRAUXA3_CMPR2A3_Msk (0xffffUL) /*!< CTIMER CMPRAUXA3: CMPR2A3 (Bitfield-Mask: 0xffff) */ -/* ======================================================= CMPRAUXB3 ======================================================= */ -#define CTIMER_CMPRAUXB3_CMPR3B3_Pos (16UL) /*!< CTIMER CMPRAUXB3: CMPR3B3 (Bit 16) */ -#define CTIMER_CMPRAUXB3_CMPR3B3_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXB3: CMPR3B3 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRAUXB3_CMPR2B3_Pos (0UL) /*!< CTIMER CMPRAUXB3: CMPR2B3 (Bit 0) */ -#define CTIMER_CMPRAUXB3_CMPR2B3_Msk (0xffffUL) /*!< CTIMER CMPRAUXB3: CMPR2B3 (Bitfield-Mask: 0xffff) */ -/* ========================================================= AUX3 ========================================================== */ -#define CTIMER_AUX3_TMRB3EN23_Pos (30UL) /*!< CTIMER AUX3: TMRB3EN23 (Bit 30) */ -#define CTIMER_AUX3_TMRB3EN23_Msk (0x40000000UL) /*!< CTIMER AUX3: TMRB3EN23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX3_TMRB3POL23_Pos (29UL) /*!< CTIMER AUX3: TMRB3POL23 (Bit 29) */ -#define CTIMER_AUX3_TMRB3POL23_Msk (0x20000000UL) /*!< CTIMER AUX3: TMRB3POL23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX3_TMRB3TINV_Pos (28UL) /*!< CTIMER AUX3: TMRB3TINV (Bit 28) */ -#define CTIMER_AUX3_TMRB3TINV_Msk (0x10000000UL) /*!< CTIMER AUX3: TMRB3TINV (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX3_TMRB3NOSYNC_Pos (27UL) /*!< CTIMER AUX3: TMRB3NOSYNC (Bit 27) */ -#define CTIMER_AUX3_TMRB3NOSYNC_Msk (0x8000000UL) /*!< CTIMER AUX3: TMRB3NOSYNC (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX3_TMRB3TRIG_Pos (23UL) /*!< CTIMER AUX3: TMRB3TRIG (Bit 23) */ -#define CTIMER_AUX3_TMRB3TRIG_Msk (0x7800000UL) /*!< CTIMER AUX3: TMRB3TRIG (Bitfield-Mask: 0x0f) */ -#define CTIMER_AUX3_TMRB3LMT_Pos (16UL) /*!< CTIMER AUX3: TMRB3LMT (Bit 16) */ -#define CTIMER_AUX3_TMRB3LMT_Msk (0x3f0000UL) /*!< CTIMER AUX3: TMRB3LMT (Bitfield-Mask: 0x3f) */ -#define CTIMER_AUX3_TMRA3EN23_Pos (14UL) /*!< CTIMER AUX3: TMRA3EN23 (Bit 14) */ -#define CTIMER_AUX3_TMRA3EN23_Msk (0x4000UL) /*!< CTIMER AUX3: TMRA3EN23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX3_TMRA3POL23_Pos (13UL) /*!< CTIMER AUX3: TMRA3POL23 (Bit 13) */ -#define CTIMER_AUX3_TMRA3POL23_Msk (0x2000UL) /*!< CTIMER AUX3: TMRA3POL23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX3_TMRA3TINV_Pos (12UL) /*!< CTIMER AUX3: TMRA3TINV (Bit 12) */ -#define CTIMER_AUX3_TMRA3TINV_Msk (0x1000UL) /*!< CTIMER AUX3: TMRA3TINV (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX3_TMRA3NOSYNC_Pos (11UL) /*!< CTIMER AUX3: TMRA3NOSYNC (Bit 11) */ -#define CTIMER_AUX3_TMRA3NOSYNC_Msk (0x800UL) /*!< CTIMER AUX3: TMRA3NOSYNC (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX3_TMRA3TRIG_Pos (7UL) /*!< CTIMER AUX3: TMRA3TRIG (Bit 7) */ -#define CTIMER_AUX3_TMRA3TRIG_Msk (0x780UL) /*!< CTIMER AUX3: TMRA3TRIG (Bitfield-Mask: 0x0f) */ -#define CTIMER_AUX3_TMRA3LMT_Pos (0UL) /*!< CTIMER AUX3: TMRA3LMT (Bit 0) */ -#define CTIMER_AUX3_TMRA3LMT_Msk (0x7fUL) /*!< CTIMER AUX3: TMRA3LMT (Bitfield-Mask: 0x7f) */ -/* ========================================================= TMR4 ========================================================== */ -#define CTIMER_TMR4_CTTMRB4_Pos (16UL) /*!< CTIMER TMR4: CTTMRB4 (Bit 16) */ -#define CTIMER_TMR4_CTTMRB4_Msk (0xffff0000UL) /*!< CTIMER TMR4: CTTMRB4 (Bitfield-Mask: 0xffff) */ -#define CTIMER_TMR4_CTTMRA4_Pos (0UL) /*!< CTIMER TMR4: CTTMRA4 (Bit 0) */ -#define CTIMER_TMR4_CTTMRA4_Msk (0xffffUL) /*!< CTIMER TMR4: CTTMRA4 (Bitfield-Mask: 0xffff) */ -/* ======================================================== CMPRA4 ========================================================= */ -#define CTIMER_CMPRA4_CMPR1A4_Pos (16UL) /*!< CTIMER CMPRA4: CMPR1A4 (Bit 16) */ -#define CTIMER_CMPRA4_CMPR1A4_Msk (0xffff0000UL) /*!< CTIMER CMPRA4: CMPR1A4 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRA4_CMPR0A4_Pos (0UL) /*!< CTIMER CMPRA4: CMPR0A4 (Bit 0) */ -#define CTIMER_CMPRA4_CMPR0A4_Msk (0xffffUL) /*!< CTIMER CMPRA4: CMPR0A4 (Bitfield-Mask: 0xffff) */ -/* ======================================================== CMPRB4 ========================================================= */ -#define CTIMER_CMPRB4_CMPR1B4_Pos (16UL) /*!< CTIMER CMPRB4: CMPR1B4 (Bit 16) */ -#define CTIMER_CMPRB4_CMPR1B4_Msk (0xffff0000UL) /*!< CTIMER CMPRB4: CMPR1B4 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRB4_CMPR0B4_Pos (0UL) /*!< CTIMER CMPRB4: CMPR0B4 (Bit 0) */ -#define CTIMER_CMPRB4_CMPR0B4_Msk (0xffffUL) /*!< CTIMER CMPRB4: CMPR0B4 (Bitfield-Mask: 0xffff) */ -/* ========================================================= CTRL4 ========================================================= */ -#define CTIMER_CTRL4_CTLINK4_Pos (31UL) /*!< CTIMER CTRL4: CTLINK4 (Bit 31) */ -#define CTIMER_CTRL4_CTLINK4_Msk (0x80000000UL) /*!< CTIMER CTRL4: CTLINK4 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL4_TMRB4POL_Pos (28UL) /*!< CTIMER CTRL4: TMRB4POL (Bit 28) */ -#define CTIMER_CTRL4_TMRB4POL_Msk (0x10000000UL) /*!< CTIMER CTRL4: TMRB4POL (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL4_TMRB4CLR_Pos (27UL) /*!< CTIMER CTRL4: TMRB4CLR (Bit 27) */ -#define CTIMER_CTRL4_TMRB4CLR_Msk (0x8000000UL) /*!< CTIMER CTRL4: TMRB4CLR (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL4_TMRB4IE1_Pos (26UL) /*!< CTIMER CTRL4: TMRB4IE1 (Bit 26) */ -#define CTIMER_CTRL4_TMRB4IE1_Msk (0x4000000UL) /*!< CTIMER CTRL4: TMRB4IE1 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL4_TMRB4IE0_Pos (25UL) /*!< CTIMER CTRL4: TMRB4IE0 (Bit 25) */ -#define CTIMER_CTRL4_TMRB4IE0_Msk (0x2000000UL) /*!< CTIMER CTRL4: TMRB4IE0 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL4_TMRB4FN_Pos (22UL) /*!< CTIMER CTRL4: TMRB4FN (Bit 22) */ -#define CTIMER_CTRL4_TMRB4FN_Msk (0x1c00000UL) /*!< CTIMER CTRL4: TMRB4FN (Bitfield-Mask: 0x07) */ -#define CTIMER_CTRL4_TMRB4CLK_Pos (17UL) /*!< CTIMER CTRL4: TMRB4CLK (Bit 17) */ -#define CTIMER_CTRL4_TMRB4CLK_Msk (0x3e0000UL) /*!< CTIMER CTRL4: TMRB4CLK (Bitfield-Mask: 0x1f) */ -#define CTIMER_CTRL4_TMRB4EN_Pos (16UL) /*!< CTIMER CTRL4: TMRB4EN (Bit 16) */ -#define CTIMER_CTRL4_TMRB4EN_Msk (0x10000UL) /*!< CTIMER CTRL4: TMRB4EN (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL4_TMRA4POL_Pos (12UL) /*!< CTIMER CTRL4: TMRA4POL (Bit 12) */ -#define CTIMER_CTRL4_TMRA4POL_Msk (0x1000UL) /*!< CTIMER CTRL4: TMRA4POL (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL4_TMRA4CLR_Pos (11UL) /*!< CTIMER CTRL4: TMRA4CLR (Bit 11) */ -#define CTIMER_CTRL4_TMRA4CLR_Msk (0x800UL) /*!< CTIMER CTRL4: TMRA4CLR (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL4_TMRA4IE1_Pos (10UL) /*!< CTIMER CTRL4: TMRA4IE1 (Bit 10) */ -#define CTIMER_CTRL4_TMRA4IE1_Msk (0x400UL) /*!< CTIMER CTRL4: TMRA4IE1 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL4_TMRA4IE0_Pos (9UL) /*!< CTIMER CTRL4: TMRA4IE0 (Bit 9) */ -#define CTIMER_CTRL4_TMRA4IE0_Msk (0x200UL) /*!< CTIMER CTRL4: TMRA4IE0 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL4_TMRA4FN_Pos (6UL) /*!< CTIMER CTRL4: TMRA4FN (Bit 6) */ -#define CTIMER_CTRL4_TMRA4FN_Msk (0x1c0UL) /*!< CTIMER CTRL4: TMRA4FN (Bitfield-Mask: 0x07) */ -#define CTIMER_CTRL4_TMRA4CLK_Pos (1UL) /*!< CTIMER CTRL4: TMRA4CLK (Bit 1) */ -#define CTIMER_CTRL4_TMRA4CLK_Msk (0x3eUL) /*!< CTIMER CTRL4: TMRA4CLK (Bitfield-Mask: 0x1f) */ -#define CTIMER_CTRL4_TMRA4EN_Pos (0UL) /*!< CTIMER CTRL4: TMRA4EN (Bit 0) */ -#define CTIMER_CTRL4_TMRA4EN_Msk (0x1UL) /*!< CTIMER CTRL4: TMRA4EN (Bitfield-Mask: 0x01) */ -/* ======================================================= CMPRAUXA4 ======================================================= */ -#define CTIMER_CMPRAUXA4_CMPR3A4_Pos (16UL) /*!< CTIMER CMPRAUXA4: CMPR3A4 (Bit 16) */ -#define CTIMER_CMPRAUXA4_CMPR3A4_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXA4: CMPR3A4 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRAUXA4_CMPR2A4_Pos (0UL) /*!< CTIMER CMPRAUXA4: CMPR2A4 (Bit 0) */ -#define CTIMER_CMPRAUXA4_CMPR2A4_Msk (0xffffUL) /*!< CTIMER CMPRAUXA4: CMPR2A4 (Bitfield-Mask: 0xffff) */ -/* ======================================================= CMPRAUXB4 ======================================================= */ -#define CTIMER_CMPRAUXB4_CMPR3B4_Pos (16UL) /*!< CTIMER CMPRAUXB4: CMPR3B4 (Bit 16) */ -#define CTIMER_CMPRAUXB4_CMPR3B4_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXB4: CMPR3B4 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRAUXB4_CMPR2B4_Pos (0UL) /*!< CTIMER CMPRAUXB4: CMPR2B4 (Bit 0) */ -#define CTIMER_CMPRAUXB4_CMPR2B4_Msk (0xffffUL) /*!< CTIMER CMPRAUXB4: CMPR2B4 (Bitfield-Mask: 0xffff) */ -/* ========================================================= AUX4 ========================================================== */ -#define CTIMER_AUX4_TMRB4EN23_Pos (30UL) /*!< CTIMER AUX4: TMRB4EN23 (Bit 30) */ -#define CTIMER_AUX4_TMRB4EN23_Msk (0x40000000UL) /*!< CTIMER AUX4: TMRB4EN23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX4_TMRB4POL23_Pos (29UL) /*!< CTIMER AUX4: TMRB4POL23 (Bit 29) */ -#define CTIMER_AUX4_TMRB4POL23_Msk (0x20000000UL) /*!< CTIMER AUX4: TMRB4POL23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX4_TMRB4TINV_Pos (28UL) /*!< CTIMER AUX4: TMRB4TINV (Bit 28) */ -#define CTIMER_AUX4_TMRB4TINV_Msk (0x10000000UL) /*!< CTIMER AUX4: TMRB4TINV (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX4_TMRB4NOSYNC_Pos (27UL) /*!< CTIMER AUX4: TMRB4NOSYNC (Bit 27) */ -#define CTIMER_AUX4_TMRB4NOSYNC_Msk (0x8000000UL) /*!< CTIMER AUX4: TMRB4NOSYNC (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX4_TMRB4TRIG_Pos (23UL) /*!< CTIMER AUX4: TMRB4TRIG (Bit 23) */ -#define CTIMER_AUX4_TMRB4TRIG_Msk (0x7800000UL) /*!< CTIMER AUX4: TMRB4TRIG (Bitfield-Mask: 0x0f) */ -#define CTIMER_AUX4_TMRB4LMT_Pos (16UL) /*!< CTIMER AUX4: TMRB4LMT (Bit 16) */ -#define CTIMER_AUX4_TMRB4LMT_Msk (0x3f0000UL) /*!< CTIMER AUX4: TMRB4LMT (Bitfield-Mask: 0x3f) */ -#define CTIMER_AUX4_TMRA4EN23_Pos (14UL) /*!< CTIMER AUX4: TMRA4EN23 (Bit 14) */ -#define CTIMER_AUX4_TMRA4EN23_Msk (0x4000UL) /*!< CTIMER AUX4: TMRA4EN23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX4_TMRA4POL23_Pos (13UL) /*!< CTIMER AUX4: TMRA4POL23 (Bit 13) */ -#define CTIMER_AUX4_TMRA4POL23_Msk (0x2000UL) /*!< CTIMER AUX4: TMRA4POL23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX4_TMRA4TINV_Pos (12UL) /*!< CTIMER AUX4: TMRA4TINV (Bit 12) */ -#define CTIMER_AUX4_TMRA4TINV_Msk (0x1000UL) /*!< CTIMER AUX4: TMRA4TINV (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX4_TMRA4NOSYNC_Pos (11UL) /*!< CTIMER AUX4: TMRA4NOSYNC (Bit 11) */ -#define CTIMER_AUX4_TMRA4NOSYNC_Msk (0x800UL) /*!< CTIMER AUX4: TMRA4NOSYNC (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX4_TMRA4TRIG_Pos (7UL) /*!< CTIMER AUX4: TMRA4TRIG (Bit 7) */ -#define CTIMER_AUX4_TMRA4TRIG_Msk (0x780UL) /*!< CTIMER AUX4: TMRA4TRIG (Bitfield-Mask: 0x0f) */ -#define CTIMER_AUX4_TMRA4LMT_Pos (0UL) /*!< CTIMER AUX4: TMRA4LMT (Bit 0) */ -#define CTIMER_AUX4_TMRA4LMT_Msk (0x7fUL) /*!< CTIMER AUX4: TMRA4LMT (Bitfield-Mask: 0x7f) */ -/* ========================================================= TMR5 ========================================================== */ -#define CTIMER_TMR5_CTTMRB5_Pos (16UL) /*!< CTIMER TMR5: CTTMRB5 (Bit 16) */ -#define CTIMER_TMR5_CTTMRB5_Msk (0xffff0000UL) /*!< CTIMER TMR5: CTTMRB5 (Bitfield-Mask: 0xffff) */ -#define CTIMER_TMR5_CTTMRA5_Pos (0UL) /*!< CTIMER TMR5: CTTMRA5 (Bit 0) */ -#define CTIMER_TMR5_CTTMRA5_Msk (0xffffUL) /*!< CTIMER TMR5: CTTMRA5 (Bitfield-Mask: 0xffff) */ -/* ======================================================== CMPRA5 ========================================================= */ -#define CTIMER_CMPRA5_CMPR1A5_Pos (16UL) /*!< CTIMER CMPRA5: CMPR1A5 (Bit 16) */ -#define CTIMER_CMPRA5_CMPR1A5_Msk (0xffff0000UL) /*!< CTIMER CMPRA5: CMPR1A5 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRA5_CMPR0A5_Pos (0UL) /*!< CTIMER CMPRA5: CMPR0A5 (Bit 0) */ -#define CTIMER_CMPRA5_CMPR0A5_Msk (0xffffUL) /*!< CTIMER CMPRA5: CMPR0A5 (Bitfield-Mask: 0xffff) */ -/* ======================================================== CMPRB5 ========================================================= */ -#define CTIMER_CMPRB5_CMPR1B5_Pos (16UL) /*!< CTIMER CMPRB5: CMPR1B5 (Bit 16) */ -#define CTIMER_CMPRB5_CMPR1B5_Msk (0xffff0000UL) /*!< CTIMER CMPRB5: CMPR1B5 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRB5_CMPR0B5_Pos (0UL) /*!< CTIMER CMPRB5: CMPR0B5 (Bit 0) */ -#define CTIMER_CMPRB5_CMPR0B5_Msk (0xffffUL) /*!< CTIMER CMPRB5: CMPR0B5 (Bitfield-Mask: 0xffff) */ -/* ========================================================= CTRL5 ========================================================= */ -#define CTIMER_CTRL5_CTLINK5_Pos (31UL) /*!< CTIMER CTRL5: CTLINK5 (Bit 31) */ -#define CTIMER_CTRL5_CTLINK5_Msk (0x80000000UL) /*!< CTIMER CTRL5: CTLINK5 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL5_TMRB5POL_Pos (28UL) /*!< CTIMER CTRL5: TMRB5POL (Bit 28) */ -#define CTIMER_CTRL5_TMRB5POL_Msk (0x10000000UL) /*!< CTIMER CTRL5: TMRB5POL (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL5_TMRB5CLR_Pos (27UL) /*!< CTIMER CTRL5: TMRB5CLR (Bit 27) */ -#define CTIMER_CTRL5_TMRB5CLR_Msk (0x8000000UL) /*!< CTIMER CTRL5: TMRB5CLR (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL5_TMRB5IE1_Pos (26UL) /*!< CTIMER CTRL5: TMRB5IE1 (Bit 26) */ -#define CTIMER_CTRL5_TMRB5IE1_Msk (0x4000000UL) /*!< CTIMER CTRL5: TMRB5IE1 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL5_TMRB5IE0_Pos (25UL) /*!< CTIMER CTRL5: TMRB5IE0 (Bit 25) */ -#define CTIMER_CTRL5_TMRB5IE0_Msk (0x2000000UL) /*!< CTIMER CTRL5: TMRB5IE0 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL5_TMRB5FN_Pos (22UL) /*!< CTIMER CTRL5: TMRB5FN (Bit 22) */ -#define CTIMER_CTRL5_TMRB5FN_Msk (0x1c00000UL) /*!< CTIMER CTRL5: TMRB5FN (Bitfield-Mask: 0x07) */ -#define CTIMER_CTRL5_TMRB5CLK_Pos (17UL) /*!< CTIMER CTRL5: TMRB5CLK (Bit 17) */ -#define CTIMER_CTRL5_TMRB5CLK_Msk (0x3e0000UL) /*!< CTIMER CTRL5: TMRB5CLK (Bitfield-Mask: 0x1f) */ -#define CTIMER_CTRL5_TMRB5EN_Pos (16UL) /*!< CTIMER CTRL5: TMRB5EN (Bit 16) */ -#define CTIMER_CTRL5_TMRB5EN_Msk (0x10000UL) /*!< CTIMER CTRL5: TMRB5EN (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL5_TMRA5POL_Pos (12UL) /*!< CTIMER CTRL5: TMRA5POL (Bit 12) */ -#define CTIMER_CTRL5_TMRA5POL_Msk (0x1000UL) /*!< CTIMER CTRL5: TMRA5POL (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL5_TMRA5CLR_Pos (11UL) /*!< CTIMER CTRL5: TMRA5CLR (Bit 11) */ -#define CTIMER_CTRL5_TMRA5CLR_Msk (0x800UL) /*!< CTIMER CTRL5: TMRA5CLR (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL5_TMRA5IE1_Pos (10UL) /*!< CTIMER CTRL5: TMRA5IE1 (Bit 10) */ -#define CTIMER_CTRL5_TMRA5IE1_Msk (0x400UL) /*!< CTIMER CTRL5: TMRA5IE1 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL5_TMRA5IE0_Pos (9UL) /*!< CTIMER CTRL5: TMRA5IE0 (Bit 9) */ -#define CTIMER_CTRL5_TMRA5IE0_Msk (0x200UL) /*!< CTIMER CTRL5: TMRA5IE0 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL5_TMRA5FN_Pos (6UL) /*!< CTIMER CTRL5: TMRA5FN (Bit 6) */ -#define CTIMER_CTRL5_TMRA5FN_Msk (0x1c0UL) /*!< CTIMER CTRL5: TMRA5FN (Bitfield-Mask: 0x07) */ -#define CTIMER_CTRL5_TMRA5CLK_Pos (1UL) /*!< CTIMER CTRL5: TMRA5CLK (Bit 1) */ -#define CTIMER_CTRL5_TMRA5CLK_Msk (0x3eUL) /*!< CTIMER CTRL5: TMRA5CLK (Bitfield-Mask: 0x1f) */ -#define CTIMER_CTRL5_TMRA5EN_Pos (0UL) /*!< CTIMER CTRL5: TMRA5EN (Bit 0) */ -#define CTIMER_CTRL5_TMRA5EN_Msk (0x1UL) /*!< CTIMER CTRL5: TMRA5EN (Bitfield-Mask: 0x01) */ -/* ======================================================= CMPRAUXA5 ======================================================= */ -#define CTIMER_CMPRAUXA5_CMPR3A5_Pos (16UL) /*!< CTIMER CMPRAUXA5: CMPR3A5 (Bit 16) */ -#define CTIMER_CMPRAUXA5_CMPR3A5_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXA5: CMPR3A5 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRAUXA5_CMPR2A5_Pos (0UL) /*!< CTIMER CMPRAUXA5: CMPR2A5 (Bit 0) */ -#define CTIMER_CMPRAUXA5_CMPR2A5_Msk (0xffffUL) /*!< CTIMER CMPRAUXA5: CMPR2A5 (Bitfield-Mask: 0xffff) */ -/* ======================================================= CMPRAUXB5 ======================================================= */ -#define CTIMER_CMPRAUXB5_CMPR3B5_Pos (16UL) /*!< CTIMER CMPRAUXB5: CMPR3B5 (Bit 16) */ -#define CTIMER_CMPRAUXB5_CMPR3B5_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXB5: CMPR3B5 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRAUXB5_CMPR2B5_Pos (0UL) /*!< CTIMER CMPRAUXB5: CMPR2B5 (Bit 0) */ -#define CTIMER_CMPRAUXB5_CMPR2B5_Msk (0xffffUL) /*!< CTIMER CMPRAUXB5: CMPR2B5 (Bitfield-Mask: 0xffff) */ -/* ========================================================= AUX5 ========================================================== */ -#define CTIMER_AUX5_TMRB5EN23_Pos (30UL) /*!< CTIMER AUX5: TMRB5EN23 (Bit 30) */ -#define CTIMER_AUX5_TMRB5EN23_Msk (0x40000000UL) /*!< CTIMER AUX5: TMRB5EN23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX5_TMRB5POL23_Pos (29UL) /*!< CTIMER AUX5: TMRB5POL23 (Bit 29) */ -#define CTIMER_AUX5_TMRB5POL23_Msk (0x20000000UL) /*!< CTIMER AUX5: TMRB5POL23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX5_TMRB5TINV_Pos (28UL) /*!< CTIMER AUX5: TMRB5TINV (Bit 28) */ -#define CTIMER_AUX5_TMRB5TINV_Msk (0x10000000UL) /*!< CTIMER AUX5: TMRB5TINV (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX5_TMRB5NOSYNC_Pos (27UL) /*!< CTIMER AUX5: TMRB5NOSYNC (Bit 27) */ -#define CTIMER_AUX5_TMRB5NOSYNC_Msk (0x8000000UL) /*!< CTIMER AUX5: TMRB5NOSYNC (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX5_TMRB5TRIG_Pos (23UL) /*!< CTIMER AUX5: TMRB5TRIG (Bit 23) */ -#define CTIMER_AUX5_TMRB5TRIG_Msk (0x7800000UL) /*!< CTIMER AUX5: TMRB5TRIG (Bitfield-Mask: 0x0f) */ -#define CTIMER_AUX5_TMRB5LMT_Pos (16UL) /*!< CTIMER AUX5: TMRB5LMT (Bit 16) */ -#define CTIMER_AUX5_TMRB5LMT_Msk (0x3f0000UL) /*!< CTIMER AUX5: TMRB5LMT (Bitfield-Mask: 0x3f) */ -#define CTIMER_AUX5_TMRA5EN23_Pos (14UL) /*!< CTIMER AUX5: TMRA5EN23 (Bit 14) */ -#define CTIMER_AUX5_TMRA5EN23_Msk (0x4000UL) /*!< CTIMER AUX5: TMRA5EN23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX5_TMRA5POL23_Pos (13UL) /*!< CTIMER AUX5: TMRA5POL23 (Bit 13) */ -#define CTIMER_AUX5_TMRA5POL23_Msk (0x2000UL) /*!< CTIMER AUX5: TMRA5POL23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX5_TMRA5TINV_Pos (12UL) /*!< CTIMER AUX5: TMRA5TINV (Bit 12) */ -#define CTIMER_AUX5_TMRA5TINV_Msk (0x1000UL) /*!< CTIMER AUX5: TMRA5TINV (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX5_TMRA5NOSYNC_Pos (11UL) /*!< CTIMER AUX5: TMRA5NOSYNC (Bit 11) */ -#define CTIMER_AUX5_TMRA5NOSYNC_Msk (0x800UL) /*!< CTIMER AUX5: TMRA5NOSYNC (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX5_TMRA5TRIG_Pos (7UL) /*!< CTIMER AUX5: TMRA5TRIG (Bit 7) */ -#define CTIMER_AUX5_TMRA5TRIG_Msk (0x780UL) /*!< CTIMER AUX5: TMRA5TRIG (Bitfield-Mask: 0x0f) */ -#define CTIMER_AUX5_TMRA5LMT_Pos (0UL) /*!< CTIMER AUX5: TMRA5LMT (Bit 0) */ -#define CTIMER_AUX5_TMRA5LMT_Msk (0x7fUL) /*!< CTIMER AUX5: TMRA5LMT (Bitfield-Mask: 0x7f) */ -/* ========================================================= TMR6 ========================================================== */ -#define CTIMER_TMR6_CTTMRB6_Pos (16UL) /*!< CTIMER TMR6: CTTMRB6 (Bit 16) */ -#define CTIMER_TMR6_CTTMRB6_Msk (0xffff0000UL) /*!< CTIMER TMR6: CTTMRB6 (Bitfield-Mask: 0xffff) */ -#define CTIMER_TMR6_CTTMRA6_Pos (0UL) /*!< CTIMER TMR6: CTTMRA6 (Bit 0) */ -#define CTIMER_TMR6_CTTMRA6_Msk (0xffffUL) /*!< CTIMER TMR6: CTTMRA6 (Bitfield-Mask: 0xffff) */ -/* ======================================================== CMPRA6 ========================================================= */ -#define CTIMER_CMPRA6_CMPR1A6_Pos (16UL) /*!< CTIMER CMPRA6: CMPR1A6 (Bit 16) */ -#define CTIMER_CMPRA6_CMPR1A6_Msk (0xffff0000UL) /*!< CTIMER CMPRA6: CMPR1A6 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRA6_CMPR0A6_Pos (0UL) /*!< CTIMER CMPRA6: CMPR0A6 (Bit 0) */ -#define CTIMER_CMPRA6_CMPR0A6_Msk (0xffffUL) /*!< CTIMER CMPRA6: CMPR0A6 (Bitfield-Mask: 0xffff) */ -/* ======================================================== CMPRB6 ========================================================= */ -#define CTIMER_CMPRB6_CMPR1B6_Pos (16UL) /*!< CTIMER CMPRB6: CMPR1B6 (Bit 16) */ -#define CTIMER_CMPRB6_CMPR1B6_Msk (0xffff0000UL) /*!< CTIMER CMPRB6: CMPR1B6 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRB6_CMPR0B6_Pos (0UL) /*!< CTIMER CMPRB6: CMPR0B6 (Bit 0) */ -#define CTIMER_CMPRB6_CMPR0B6_Msk (0xffffUL) /*!< CTIMER CMPRB6: CMPR0B6 (Bitfield-Mask: 0xffff) */ -/* ========================================================= CTRL6 ========================================================= */ -#define CTIMER_CTRL6_CTLINK6_Pos (31UL) /*!< CTIMER CTRL6: CTLINK6 (Bit 31) */ -#define CTIMER_CTRL6_CTLINK6_Msk (0x80000000UL) /*!< CTIMER CTRL6: CTLINK6 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL6_TMRB6POL_Pos (28UL) /*!< CTIMER CTRL6: TMRB6POL (Bit 28) */ -#define CTIMER_CTRL6_TMRB6POL_Msk (0x10000000UL) /*!< CTIMER CTRL6: TMRB6POL (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL6_TMRB6CLR_Pos (27UL) /*!< CTIMER CTRL6: TMRB6CLR (Bit 27) */ -#define CTIMER_CTRL6_TMRB6CLR_Msk (0x8000000UL) /*!< CTIMER CTRL6: TMRB6CLR (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL6_TMRB6IE1_Pos (26UL) /*!< CTIMER CTRL6: TMRB6IE1 (Bit 26) */ -#define CTIMER_CTRL6_TMRB6IE1_Msk (0x4000000UL) /*!< CTIMER CTRL6: TMRB6IE1 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL6_TMRB6IE0_Pos (25UL) /*!< CTIMER CTRL6: TMRB6IE0 (Bit 25) */ -#define CTIMER_CTRL6_TMRB6IE0_Msk (0x2000000UL) /*!< CTIMER CTRL6: TMRB6IE0 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL6_TMRB6FN_Pos (22UL) /*!< CTIMER CTRL6: TMRB6FN (Bit 22) */ -#define CTIMER_CTRL6_TMRB6FN_Msk (0x1c00000UL) /*!< CTIMER CTRL6: TMRB6FN (Bitfield-Mask: 0x07) */ -#define CTIMER_CTRL6_TMRB6CLK_Pos (17UL) /*!< CTIMER CTRL6: TMRB6CLK (Bit 17) */ -#define CTIMER_CTRL6_TMRB6CLK_Msk (0x3e0000UL) /*!< CTIMER CTRL6: TMRB6CLK (Bitfield-Mask: 0x1f) */ -#define CTIMER_CTRL6_TMRB6EN_Pos (16UL) /*!< CTIMER CTRL6: TMRB6EN (Bit 16) */ -#define CTIMER_CTRL6_TMRB6EN_Msk (0x10000UL) /*!< CTIMER CTRL6: TMRB6EN (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL6_TMRA6POL_Pos (12UL) /*!< CTIMER CTRL6: TMRA6POL (Bit 12) */ -#define CTIMER_CTRL6_TMRA6POL_Msk (0x1000UL) /*!< CTIMER CTRL6: TMRA6POL (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL6_TMRA6CLR_Pos (11UL) /*!< CTIMER CTRL6: TMRA6CLR (Bit 11) */ -#define CTIMER_CTRL6_TMRA6CLR_Msk (0x800UL) /*!< CTIMER CTRL6: TMRA6CLR (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL6_TMRA6IE1_Pos (10UL) /*!< CTIMER CTRL6: TMRA6IE1 (Bit 10) */ -#define CTIMER_CTRL6_TMRA6IE1_Msk (0x400UL) /*!< CTIMER CTRL6: TMRA6IE1 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL6_TMRA6IE0_Pos (9UL) /*!< CTIMER CTRL6: TMRA6IE0 (Bit 9) */ -#define CTIMER_CTRL6_TMRA6IE0_Msk (0x200UL) /*!< CTIMER CTRL6: TMRA6IE0 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL6_TMRA6FN_Pos (6UL) /*!< CTIMER CTRL6: TMRA6FN (Bit 6) */ -#define CTIMER_CTRL6_TMRA6FN_Msk (0x1c0UL) /*!< CTIMER CTRL6: TMRA6FN (Bitfield-Mask: 0x07) */ -#define CTIMER_CTRL6_TMRA6CLK_Pos (1UL) /*!< CTIMER CTRL6: TMRA6CLK (Bit 1) */ -#define CTIMER_CTRL6_TMRA6CLK_Msk (0x3eUL) /*!< CTIMER CTRL6: TMRA6CLK (Bitfield-Mask: 0x1f) */ -#define CTIMER_CTRL6_TMRA6EN_Pos (0UL) /*!< CTIMER CTRL6: TMRA6EN (Bit 0) */ -#define CTIMER_CTRL6_TMRA6EN_Msk (0x1UL) /*!< CTIMER CTRL6: TMRA6EN (Bitfield-Mask: 0x01) */ -/* ======================================================= CMPRAUXA6 ======================================================= */ -#define CTIMER_CMPRAUXA6_CMPR3A6_Pos (16UL) /*!< CTIMER CMPRAUXA6: CMPR3A6 (Bit 16) */ -#define CTIMER_CMPRAUXA6_CMPR3A6_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXA6: CMPR3A6 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRAUXA6_CMPR2A6_Pos (0UL) /*!< CTIMER CMPRAUXA6: CMPR2A6 (Bit 0) */ -#define CTIMER_CMPRAUXA6_CMPR2A6_Msk (0xffffUL) /*!< CTIMER CMPRAUXA6: CMPR2A6 (Bitfield-Mask: 0xffff) */ -/* ======================================================= CMPRAUXB6 ======================================================= */ -#define CTIMER_CMPRAUXB6_CMPR3B6_Pos (16UL) /*!< CTIMER CMPRAUXB6: CMPR3B6 (Bit 16) */ -#define CTIMER_CMPRAUXB6_CMPR3B6_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXB6: CMPR3B6 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRAUXB6_CMPR2B6_Pos (0UL) /*!< CTIMER CMPRAUXB6: CMPR2B6 (Bit 0) */ -#define CTIMER_CMPRAUXB6_CMPR2B6_Msk (0xffffUL) /*!< CTIMER CMPRAUXB6: CMPR2B6 (Bitfield-Mask: 0xffff) */ -/* ========================================================= AUX6 ========================================================== */ -#define CTIMER_AUX6_TMRB6EN23_Pos (30UL) /*!< CTIMER AUX6: TMRB6EN23 (Bit 30) */ -#define CTIMER_AUX6_TMRB6EN23_Msk (0x40000000UL) /*!< CTIMER AUX6: TMRB6EN23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX6_TMRB6POL23_Pos (29UL) /*!< CTIMER AUX6: TMRB6POL23 (Bit 29) */ -#define CTIMER_AUX6_TMRB6POL23_Msk (0x20000000UL) /*!< CTIMER AUX6: TMRB6POL23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX6_TMRB6TINV_Pos (28UL) /*!< CTIMER AUX6: TMRB6TINV (Bit 28) */ -#define CTIMER_AUX6_TMRB6TINV_Msk (0x10000000UL) /*!< CTIMER AUX6: TMRB6TINV (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX6_TMRB6NOSYNC_Pos (27UL) /*!< CTIMER AUX6: TMRB6NOSYNC (Bit 27) */ -#define CTIMER_AUX6_TMRB6NOSYNC_Msk (0x8000000UL) /*!< CTIMER AUX6: TMRB6NOSYNC (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX6_TMRB6TRIG_Pos (23UL) /*!< CTIMER AUX6: TMRB6TRIG (Bit 23) */ -#define CTIMER_AUX6_TMRB6TRIG_Msk (0x7800000UL) /*!< CTIMER AUX6: TMRB6TRIG (Bitfield-Mask: 0x0f) */ -#define CTIMER_AUX6_TMRB6LMT_Pos (16UL) /*!< CTIMER AUX6: TMRB6LMT (Bit 16) */ -#define CTIMER_AUX6_TMRB6LMT_Msk (0x3f0000UL) /*!< CTIMER AUX6: TMRB6LMT (Bitfield-Mask: 0x3f) */ -#define CTIMER_AUX6_TMRA6EN23_Pos (14UL) /*!< CTIMER AUX6: TMRA6EN23 (Bit 14) */ -#define CTIMER_AUX6_TMRA6EN23_Msk (0x4000UL) /*!< CTIMER AUX6: TMRA6EN23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX6_TMRA6POL23_Pos (13UL) /*!< CTIMER AUX6: TMRA6POL23 (Bit 13) */ -#define CTIMER_AUX6_TMRA6POL23_Msk (0x2000UL) /*!< CTIMER AUX6: TMRA6POL23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX6_TMRA6TINV_Pos (12UL) /*!< CTIMER AUX6: TMRA6TINV (Bit 12) */ -#define CTIMER_AUX6_TMRA6TINV_Msk (0x1000UL) /*!< CTIMER AUX6: TMRA6TINV (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX6_TMRA6NOSYNC_Pos (11UL) /*!< CTIMER AUX6: TMRA6NOSYNC (Bit 11) */ -#define CTIMER_AUX6_TMRA6NOSYNC_Msk (0x800UL) /*!< CTIMER AUX6: TMRA6NOSYNC (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX6_TMRA6TRIG_Pos (7UL) /*!< CTIMER AUX6: TMRA6TRIG (Bit 7) */ -#define CTIMER_AUX6_TMRA6TRIG_Msk (0x780UL) /*!< CTIMER AUX6: TMRA6TRIG (Bitfield-Mask: 0x0f) */ -#define CTIMER_AUX6_TMRA6LMT_Pos (0UL) /*!< CTIMER AUX6: TMRA6LMT (Bit 0) */ -#define CTIMER_AUX6_TMRA6LMT_Msk (0x7fUL) /*!< CTIMER AUX6: TMRA6LMT (Bitfield-Mask: 0x7f) */ -/* ========================================================= TMR7 ========================================================== */ -#define CTIMER_TMR7_CTTMRB7_Pos (16UL) /*!< CTIMER TMR7: CTTMRB7 (Bit 16) */ -#define CTIMER_TMR7_CTTMRB7_Msk (0xffff0000UL) /*!< CTIMER TMR7: CTTMRB7 (Bitfield-Mask: 0xffff) */ -#define CTIMER_TMR7_CTTMRA7_Pos (0UL) /*!< CTIMER TMR7: CTTMRA7 (Bit 0) */ -#define CTIMER_TMR7_CTTMRA7_Msk (0xffffUL) /*!< CTIMER TMR7: CTTMRA7 (Bitfield-Mask: 0xffff) */ -/* ======================================================== CMPRA7 ========================================================= */ -#define CTIMER_CMPRA7_CMPR1A7_Pos (16UL) /*!< CTIMER CMPRA7: CMPR1A7 (Bit 16) */ -#define CTIMER_CMPRA7_CMPR1A7_Msk (0xffff0000UL) /*!< CTIMER CMPRA7: CMPR1A7 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRA7_CMPR0A7_Pos (0UL) /*!< CTIMER CMPRA7: CMPR0A7 (Bit 0) */ -#define CTIMER_CMPRA7_CMPR0A7_Msk (0xffffUL) /*!< CTIMER CMPRA7: CMPR0A7 (Bitfield-Mask: 0xffff) */ -/* ======================================================== CMPRB7 ========================================================= */ -#define CTIMER_CMPRB7_CMPR1B7_Pos (16UL) /*!< CTIMER CMPRB7: CMPR1B7 (Bit 16) */ -#define CTIMER_CMPRB7_CMPR1B7_Msk (0xffff0000UL) /*!< CTIMER CMPRB7: CMPR1B7 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRB7_CMPR0B7_Pos (0UL) /*!< CTIMER CMPRB7: CMPR0B7 (Bit 0) */ -#define CTIMER_CMPRB7_CMPR0B7_Msk (0xffffUL) /*!< CTIMER CMPRB7: CMPR0B7 (Bitfield-Mask: 0xffff) */ -/* ========================================================= CTRL7 ========================================================= */ -#define CTIMER_CTRL7_CTLINK7_Pos (31UL) /*!< CTIMER CTRL7: CTLINK7 (Bit 31) */ -#define CTIMER_CTRL7_CTLINK7_Msk (0x80000000UL) /*!< CTIMER CTRL7: CTLINK7 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL7_TMRB7POL_Pos (28UL) /*!< CTIMER CTRL7: TMRB7POL (Bit 28) */ -#define CTIMER_CTRL7_TMRB7POL_Msk (0x10000000UL) /*!< CTIMER CTRL7: TMRB7POL (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL7_TMRB7CLR_Pos (27UL) /*!< CTIMER CTRL7: TMRB7CLR (Bit 27) */ -#define CTIMER_CTRL7_TMRB7CLR_Msk (0x8000000UL) /*!< CTIMER CTRL7: TMRB7CLR (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL7_TMRB7IE1_Pos (26UL) /*!< CTIMER CTRL7: TMRB7IE1 (Bit 26) */ -#define CTIMER_CTRL7_TMRB7IE1_Msk (0x4000000UL) /*!< CTIMER CTRL7: TMRB7IE1 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL7_TMRB7IE0_Pos (25UL) /*!< CTIMER CTRL7: TMRB7IE0 (Bit 25) */ -#define CTIMER_CTRL7_TMRB7IE0_Msk (0x2000000UL) /*!< CTIMER CTRL7: TMRB7IE0 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL7_TMRB7FN_Pos (22UL) /*!< CTIMER CTRL7: TMRB7FN (Bit 22) */ -#define CTIMER_CTRL7_TMRB7FN_Msk (0x1c00000UL) /*!< CTIMER CTRL7: TMRB7FN (Bitfield-Mask: 0x07) */ -#define CTIMER_CTRL7_TMRB7CLK_Pos (17UL) /*!< CTIMER CTRL7: TMRB7CLK (Bit 17) */ -#define CTIMER_CTRL7_TMRB7CLK_Msk (0x3e0000UL) /*!< CTIMER CTRL7: TMRB7CLK (Bitfield-Mask: 0x1f) */ -#define CTIMER_CTRL7_TMRB7EN_Pos (16UL) /*!< CTIMER CTRL7: TMRB7EN (Bit 16) */ -#define CTIMER_CTRL7_TMRB7EN_Msk (0x10000UL) /*!< CTIMER CTRL7: TMRB7EN (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL7_TMRA7POL_Pos (12UL) /*!< CTIMER CTRL7: TMRA7POL (Bit 12) */ -#define CTIMER_CTRL7_TMRA7POL_Msk (0x1000UL) /*!< CTIMER CTRL7: TMRA7POL (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL7_TMRA7CLR_Pos (11UL) /*!< CTIMER CTRL7: TMRA7CLR (Bit 11) */ -#define CTIMER_CTRL7_TMRA7CLR_Msk (0x800UL) /*!< CTIMER CTRL7: TMRA7CLR (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL7_TMRA7IE1_Pos (10UL) /*!< CTIMER CTRL7: TMRA7IE1 (Bit 10) */ -#define CTIMER_CTRL7_TMRA7IE1_Msk (0x400UL) /*!< CTIMER CTRL7: TMRA7IE1 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL7_TMRA7IE0_Pos (9UL) /*!< CTIMER CTRL7: TMRA7IE0 (Bit 9) */ -#define CTIMER_CTRL7_TMRA7IE0_Msk (0x200UL) /*!< CTIMER CTRL7: TMRA7IE0 (Bitfield-Mask: 0x01) */ -#define CTIMER_CTRL7_TMRA7FN_Pos (6UL) /*!< CTIMER CTRL7: TMRA7FN (Bit 6) */ -#define CTIMER_CTRL7_TMRA7FN_Msk (0x1c0UL) /*!< CTIMER CTRL7: TMRA7FN (Bitfield-Mask: 0x07) */ -#define CTIMER_CTRL7_TMRA7CLK_Pos (1UL) /*!< CTIMER CTRL7: TMRA7CLK (Bit 1) */ -#define CTIMER_CTRL7_TMRA7CLK_Msk (0x3eUL) /*!< CTIMER CTRL7: TMRA7CLK (Bitfield-Mask: 0x1f) */ -#define CTIMER_CTRL7_TMRA7EN_Pos (0UL) /*!< CTIMER CTRL7: TMRA7EN (Bit 0) */ -#define CTIMER_CTRL7_TMRA7EN_Msk (0x1UL) /*!< CTIMER CTRL7: TMRA7EN (Bitfield-Mask: 0x01) */ -/* ======================================================= CMPRAUXA7 ======================================================= */ -#define CTIMER_CMPRAUXA7_CMPR3A7_Pos (16UL) /*!< CTIMER CMPRAUXA7: CMPR3A7 (Bit 16) */ -#define CTIMER_CMPRAUXA7_CMPR3A7_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXA7: CMPR3A7 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRAUXA7_CMPR2A7_Pos (0UL) /*!< CTIMER CMPRAUXA7: CMPR2A7 (Bit 0) */ -#define CTIMER_CMPRAUXA7_CMPR2A7_Msk (0xffffUL) /*!< CTIMER CMPRAUXA7: CMPR2A7 (Bitfield-Mask: 0xffff) */ -/* ======================================================= CMPRAUXB7 ======================================================= */ -#define CTIMER_CMPRAUXB7_CMPR3B7_Pos (16UL) /*!< CTIMER CMPRAUXB7: CMPR3B7 (Bit 16) */ -#define CTIMER_CMPRAUXB7_CMPR3B7_Msk (0xffff0000UL) /*!< CTIMER CMPRAUXB7: CMPR3B7 (Bitfield-Mask: 0xffff) */ -#define CTIMER_CMPRAUXB7_CMPR2B7_Pos (0UL) /*!< CTIMER CMPRAUXB7: CMPR2B7 (Bit 0) */ -#define CTIMER_CMPRAUXB7_CMPR2B7_Msk (0xffffUL) /*!< CTIMER CMPRAUXB7: CMPR2B7 (Bitfield-Mask: 0xffff) */ -/* ========================================================= AUX7 ========================================================== */ -#define CTIMER_AUX7_TMRB7EN23_Pos (30UL) /*!< CTIMER AUX7: TMRB7EN23 (Bit 30) */ -#define CTIMER_AUX7_TMRB7EN23_Msk (0x40000000UL) /*!< CTIMER AUX7: TMRB7EN23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX7_TMRB7POL23_Pos (29UL) /*!< CTIMER AUX7: TMRB7POL23 (Bit 29) */ -#define CTIMER_AUX7_TMRB7POL23_Msk (0x20000000UL) /*!< CTIMER AUX7: TMRB7POL23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX7_TMRB7TINV_Pos (28UL) /*!< CTIMER AUX7: TMRB7TINV (Bit 28) */ -#define CTIMER_AUX7_TMRB7TINV_Msk (0x10000000UL) /*!< CTIMER AUX7: TMRB7TINV (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX7_TMRB7NOSYNC_Pos (27UL) /*!< CTIMER AUX7: TMRB7NOSYNC (Bit 27) */ -#define CTIMER_AUX7_TMRB7NOSYNC_Msk (0x8000000UL) /*!< CTIMER AUX7: TMRB7NOSYNC (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX7_TMRB7TRIG_Pos (23UL) /*!< CTIMER AUX7: TMRB7TRIG (Bit 23) */ -#define CTIMER_AUX7_TMRB7TRIG_Msk (0x7800000UL) /*!< CTIMER AUX7: TMRB7TRIG (Bitfield-Mask: 0x0f) */ -#define CTIMER_AUX7_TMRB7LMT_Pos (16UL) /*!< CTIMER AUX7: TMRB7LMT (Bit 16) */ -#define CTIMER_AUX7_TMRB7LMT_Msk (0x3f0000UL) /*!< CTIMER AUX7: TMRB7LMT (Bitfield-Mask: 0x3f) */ -#define CTIMER_AUX7_TMRA7EN23_Pos (14UL) /*!< CTIMER AUX7: TMRA7EN23 (Bit 14) */ -#define CTIMER_AUX7_TMRA7EN23_Msk (0x4000UL) /*!< CTIMER AUX7: TMRA7EN23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX7_TMRA7POL23_Pos (13UL) /*!< CTIMER AUX7: TMRA7POL23 (Bit 13) */ -#define CTIMER_AUX7_TMRA7POL23_Msk (0x2000UL) /*!< CTIMER AUX7: TMRA7POL23 (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX7_TMRA7TINV_Pos (12UL) /*!< CTIMER AUX7: TMRA7TINV (Bit 12) */ -#define CTIMER_AUX7_TMRA7TINV_Msk (0x1000UL) /*!< CTIMER AUX7: TMRA7TINV (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX7_TMRA7NOSYNC_Pos (11UL) /*!< CTIMER AUX7: TMRA7NOSYNC (Bit 11) */ -#define CTIMER_AUX7_TMRA7NOSYNC_Msk (0x800UL) /*!< CTIMER AUX7: TMRA7NOSYNC (Bitfield-Mask: 0x01) */ -#define CTIMER_AUX7_TMRA7TRIG_Pos (7UL) /*!< CTIMER AUX7: TMRA7TRIG (Bit 7) */ -#define CTIMER_AUX7_TMRA7TRIG_Msk (0x780UL) /*!< CTIMER AUX7: TMRA7TRIG (Bitfield-Mask: 0x0f) */ -#define CTIMER_AUX7_TMRA7LMT_Pos (0UL) /*!< CTIMER AUX7: TMRA7LMT (Bit 0) */ -#define CTIMER_AUX7_TMRA7LMT_Msk (0x7fUL) /*!< CTIMER AUX7: TMRA7LMT (Bitfield-Mask: 0x7f) */ -/* ======================================================== GLOBEN ========================================================= */ -#define CTIMER_GLOBEN_ENB7_Pos (15UL) /*!< CTIMER GLOBEN: ENB7 (Bit 15) */ -#define CTIMER_GLOBEN_ENB7_Msk (0x8000UL) /*!< CTIMER GLOBEN: ENB7 (Bitfield-Mask: 0x01) */ -#define CTIMER_GLOBEN_ENA7_Pos (14UL) /*!< CTIMER GLOBEN: ENA7 (Bit 14) */ -#define CTIMER_GLOBEN_ENA7_Msk (0x4000UL) /*!< CTIMER GLOBEN: ENA7 (Bitfield-Mask: 0x01) */ -#define CTIMER_GLOBEN_ENB6_Pos (13UL) /*!< CTIMER GLOBEN: ENB6 (Bit 13) */ -#define CTIMER_GLOBEN_ENB6_Msk (0x2000UL) /*!< CTIMER GLOBEN: ENB6 (Bitfield-Mask: 0x01) */ -#define CTIMER_GLOBEN_ENA6_Pos (12UL) /*!< CTIMER GLOBEN: ENA6 (Bit 12) */ -#define CTIMER_GLOBEN_ENA6_Msk (0x1000UL) /*!< CTIMER GLOBEN: ENA6 (Bitfield-Mask: 0x01) */ -#define CTIMER_GLOBEN_ENB5_Pos (11UL) /*!< CTIMER GLOBEN: ENB5 (Bit 11) */ -#define CTIMER_GLOBEN_ENB5_Msk (0x800UL) /*!< CTIMER GLOBEN: ENB5 (Bitfield-Mask: 0x01) */ -#define CTIMER_GLOBEN_ENA5_Pos (10UL) /*!< CTIMER GLOBEN: ENA5 (Bit 10) */ -#define CTIMER_GLOBEN_ENA5_Msk (0x400UL) /*!< CTIMER GLOBEN: ENA5 (Bitfield-Mask: 0x01) */ -#define CTIMER_GLOBEN_ENB4_Pos (9UL) /*!< CTIMER GLOBEN: ENB4 (Bit 9) */ -#define CTIMER_GLOBEN_ENB4_Msk (0x200UL) /*!< CTIMER GLOBEN: ENB4 (Bitfield-Mask: 0x01) */ -#define CTIMER_GLOBEN_ENA4_Pos (8UL) /*!< CTIMER GLOBEN: ENA4 (Bit 8) */ -#define CTIMER_GLOBEN_ENA4_Msk (0x100UL) /*!< CTIMER GLOBEN: ENA4 (Bitfield-Mask: 0x01) */ -#define CTIMER_GLOBEN_ENB3_Pos (7UL) /*!< CTIMER GLOBEN: ENB3 (Bit 7) */ -#define CTIMER_GLOBEN_ENB3_Msk (0x80UL) /*!< CTIMER GLOBEN: ENB3 (Bitfield-Mask: 0x01) */ -#define CTIMER_GLOBEN_ENA3_Pos (6UL) /*!< CTIMER GLOBEN: ENA3 (Bit 6) */ -#define CTIMER_GLOBEN_ENA3_Msk (0x40UL) /*!< CTIMER GLOBEN: ENA3 (Bitfield-Mask: 0x01) */ -#define CTIMER_GLOBEN_ENB2_Pos (5UL) /*!< CTIMER GLOBEN: ENB2 (Bit 5) */ -#define CTIMER_GLOBEN_ENB2_Msk (0x20UL) /*!< CTIMER GLOBEN: ENB2 (Bitfield-Mask: 0x01) */ -#define CTIMER_GLOBEN_ENA2_Pos (4UL) /*!< CTIMER GLOBEN: ENA2 (Bit 4) */ -#define CTIMER_GLOBEN_ENA2_Msk (0x10UL) /*!< CTIMER GLOBEN: ENA2 (Bitfield-Mask: 0x01) */ -#define CTIMER_GLOBEN_ENB1_Pos (3UL) /*!< CTIMER GLOBEN: ENB1 (Bit 3) */ -#define CTIMER_GLOBEN_ENB1_Msk (0x8UL) /*!< CTIMER GLOBEN: ENB1 (Bitfield-Mask: 0x01) */ -#define CTIMER_GLOBEN_ENA1_Pos (2UL) /*!< CTIMER GLOBEN: ENA1 (Bit 2) */ -#define CTIMER_GLOBEN_ENA1_Msk (0x4UL) /*!< CTIMER GLOBEN: ENA1 (Bitfield-Mask: 0x01) */ -#define CTIMER_GLOBEN_ENB0_Pos (1UL) /*!< CTIMER GLOBEN: ENB0 (Bit 1) */ -#define CTIMER_GLOBEN_ENB0_Msk (0x2UL) /*!< CTIMER GLOBEN: ENB0 (Bitfield-Mask: 0x01) */ -#define CTIMER_GLOBEN_ENA0_Pos (0UL) /*!< CTIMER GLOBEN: ENA0 (Bit 0) */ -#define CTIMER_GLOBEN_ENA0_Msk (0x1UL) /*!< CTIMER GLOBEN: ENA0 (Bitfield-Mask: 0x01) */ -/* ======================================================== OUTCFG0 ======================================================== */ -#define CTIMER_OUTCFG0_CFG9_Pos (28UL) /*!< CTIMER OUTCFG0: CFG9 (Bit 28) */ -#define CTIMER_OUTCFG0_CFG9_Msk (0x70000000UL) /*!< CTIMER OUTCFG0: CFG9 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG0_CFG8_Pos (25UL) /*!< CTIMER OUTCFG0: CFG8 (Bit 25) */ -#define CTIMER_OUTCFG0_CFG8_Msk (0xe000000UL) /*!< CTIMER OUTCFG0: CFG8 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG0_CFG7_Pos (22UL) /*!< CTIMER OUTCFG0: CFG7 (Bit 22) */ -#define CTIMER_OUTCFG0_CFG7_Msk (0x1c00000UL) /*!< CTIMER OUTCFG0: CFG7 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG0_CFG6_Pos (19UL) /*!< CTIMER OUTCFG0: CFG6 (Bit 19) */ -#define CTIMER_OUTCFG0_CFG6_Msk (0x380000UL) /*!< CTIMER OUTCFG0: CFG6 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG0_CFG5_Pos (16UL) /*!< CTIMER OUTCFG0: CFG5 (Bit 16) */ -#define CTIMER_OUTCFG0_CFG5_Msk (0x70000UL) /*!< CTIMER OUTCFG0: CFG5 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG0_CFG4_Pos (12UL) /*!< CTIMER OUTCFG0: CFG4 (Bit 12) */ -#define CTIMER_OUTCFG0_CFG4_Msk (0x7000UL) /*!< CTIMER OUTCFG0: CFG4 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG0_CFG3_Pos (9UL) /*!< CTIMER OUTCFG0: CFG3 (Bit 9) */ -#define CTIMER_OUTCFG0_CFG3_Msk (0xe00UL) /*!< CTIMER OUTCFG0: CFG3 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG0_CFG2_Pos (6UL) /*!< CTIMER OUTCFG0: CFG2 (Bit 6) */ -#define CTIMER_OUTCFG0_CFG2_Msk (0x1c0UL) /*!< CTIMER OUTCFG0: CFG2 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG0_CFG1_Pos (3UL) /*!< CTIMER OUTCFG0: CFG1 (Bit 3) */ -#define CTIMER_OUTCFG0_CFG1_Msk (0x38UL) /*!< CTIMER OUTCFG0: CFG1 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG0_CFG0_Pos (0UL) /*!< CTIMER OUTCFG0: CFG0 (Bit 0) */ -#define CTIMER_OUTCFG0_CFG0_Msk (0x7UL) /*!< CTIMER OUTCFG0: CFG0 (Bitfield-Mask: 0x07) */ -/* ======================================================== OUTCFG1 ======================================================== */ -#define CTIMER_OUTCFG1_CFG19_Pos (28UL) /*!< CTIMER OUTCFG1: CFG19 (Bit 28) */ -#define CTIMER_OUTCFG1_CFG19_Msk (0x70000000UL) /*!< CTIMER OUTCFG1: CFG19 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG1_CFG18_Pos (25UL) /*!< CTIMER OUTCFG1: CFG18 (Bit 25) */ -#define CTIMER_OUTCFG1_CFG18_Msk (0xe000000UL) /*!< CTIMER OUTCFG1: CFG18 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG1_CFG17_Pos (22UL) /*!< CTIMER OUTCFG1: CFG17 (Bit 22) */ -#define CTIMER_OUTCFG1_CFG17_Msk (0x1c00000UL) /*!< CTIMER OUTCFG1: CFG17 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG1_CFG16_Pos (19UL) /*!< CTIMER OUTCFG1: CFG16 (Bit 19) */ -#define CTIMER_OUTCFG1_CFG16_Msk (0x380000UL) /*!< CTIMER OUTCFG1: CFG16 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG1_CFG15_Pos (16UL) /*!< CTIMER OUTCFG1: CFG15 (Bit 16) */ -#define CTIMER_OUTCFG1_CFG15_Msk (0x70000UL) /*!< CTIMER OUTCFG1: CFG15 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG1_CFG14_Pos (12UL) /*!< CTIMER OUTCFG1: CFG14 (Bit 12) */ -#define CTIMER_OUTCFG1_CFG14_Msk (0x7000UL) /*!< CTIMER OUTCFG1: CFG14 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG1_CFG13_Pos (9UL) /*!< CTIMER OUTCFG1: CFG13 (Bit 9) */ -#define CTIMER_OUTCFG1_CFG13_Msk (0xe00UL) /*!< CTIMER OUTCFG1: CFG13 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG1_CFG12_Pos (6UL) /*!< CTIMER OUTCFG1: CFG12 (Bit 6) */ -#define CTIMER_OUTCFG1_CFG12_Msk (0x1c0UL) /*!< CTIMER OUTCFG1: CFG12 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG1_CFG11_Pos (3UL) /*!< CTIMER OUTCFG1: CFG11 (Bit 3) */ -#define CTIMER_OUTCFG1_CFG11_Msk (0x38UL) /*!< CTIMER OUTCFG1: CFG11 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG1_CFG10_Pos (0UL) /*!< CTIMER OUTCFG1: CFG10 (Bit 0) */ -#define CTIMER_OUTCFG1_CFG10_Msk (0x7UL) /*!< CTIMER OUTCFG1: CFG10 (Bitfield-Mask: 0x07) */ -/* ======================================================== OUTCFG2 ======================================================== */ -#define CTIMER_OUTCFG2_CFG29_Pos (28UL) /*!< CTIMER OUTCFG2: CFG29 (Bit 28) */ -#define CTIMER_OUTCFG2_CFG29_Msk (0x70000000UL) /*!< CTIMER OUTCFG2: CFG29 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG2_CFG28_Pos (25UL) /*!< CTIMER OUTCFG2: CFG28 (Bit 25) */ -#define CTIMER_OUTCFG2_CFG28_Msk (0xe000000UL) /*!< CTIMER OUTCFG2: CFG28 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG2_CFG27_Pos (22UL) /*!< CTIMER OUTCFG2: CFG27 (Bit 22) */ -#define CTIMER_OUTCFG2_CFG27_Msk (0x1c00000UL) /*!< CTIMER OUTCFG2: CFG27 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG2_CFG26_Pos (19UL) /*!< CTIMER OUTCFG2: CFG26 (Bit 19) */ -#define CTIMER_OUTCFG2_CFG26_Msk (0x380000UL) /*!< CTIMER OUTCFG2: CFG26 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG2_CFG25_Pos (16UL) /*!< CTIMER OUTCFG2: CFG25 (Bit 16) */ -#define CTIMER_OUTCFG2_CFG25_Msk (0x70000UL) /*!< CTIMER OUTCFG2: CFG25 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG2_CFG24_Pos (12UL) /*!< CTIMER OUTCFG2: CFG24 (Bit 12) */ -#define CTIMER_OUTCFG2_CFG24_Msk (0x7000UL) /*!< CTIMER OUTCFG2: CFG24 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG2_CFG23_Pos (9UL) /*!< CTIMER OUTCFG2: CFG23 (Bit 9) */ -#define CTIMER_OUTCFG2_CFG23_Msk (0xe00UL) /*!< CTIMER OUTCFG2: CFG23 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG2_CFG22_Pos (6UL) /*!< CTIMER OUTCFG2: CFG22 (Bit 6) */ -#define CTIMER_OUTCFG2_CFG22_Msk (0x1c0UL) /*!< CTIMER OUTCFG2: CFG22 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG2_CFG21_Pos (3UL) /*!< CTIMER OUTCFG2: CFG21 (Bit 3) */ -#define CTIMER_OUTCFG2_CFG21_Msk (0x38UL) /*!< CTIMER OUTCFG2: CFG21 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG2_CFG20_Pos (0UL) /*!< CTIMER OUTCFG2: CFG20 (Bit 0) */ -#define CTIMER_OUTCFG2_CFG20_Msk (0x7UL) /*!< CTIMER OUTCFG2: CFG20 (Bitfield-Mask: 0x07) */ -/* ======================================================== OUTCFG3 ======================================================== */ -#define CTIMER_OUTCFG3_CFG31_Pos (3UL) /*!< CTIMER OUTCFG3: CFG31 (Bit 3) */ -#define CTIMER_OUTCFG3_CFG31_Msk (0x38UL) /*!< CTIMER OUTCFG3: CFG31 (Bitfield-Mask: 0x07) */ -#define CTIMER_OUTCFG3_CFG30_Pos (0UL) /*!< CTIMER OUTCFG3: CFG30 (Bit 0) */ -#define CTIMER_OUTCFG3_CFG30_Msk (0x7UL) /*!< CTIMER OUTCFG3: CFG30 (Bitfield-Mask: 0x07) */ -/* ========================================================= INCFG ========================================================= */ -#define CTIMER_INCFG_CFGB7_Pos (15UL) /*!< CTIMER INCFG: CFGB7 (Bit 15) */ -#define CTIMER_INCFG_CFGB7_Msk (0x8000UL) /*!< CTIMER INCFG: CFGB7 (Bitfield-Mask: 0x01) */ -#define CTIMER_INCFG_CFGA7_Pos (14UL) /*!< CTIMER INCFG: CFGA7 (Bit 14) */ -#define CTIMER_INCFG_CFGA7_Msk (0x4000UL) /*!< CTIMER INCFG: CFGA7 (Bitfield-Mask: 0x01) */ -#define CTIMER_INCFG_CFGB6_Pos (13UL) /*!< CTIMER INCFG: CFGB6 (Bit 13) */ -#define CTIMER_INCFG_CFGB6_Msk (0x2000UL) /*!< CTIMER INCFG: CFGB6 (Bitfield-Mask: 0x01) */ -#define CTIMER_INCFG_CFGA6_Pos (12UL) /*!< CTIMER INCFG: CFGA6 (Bit 12) */ -#define CTIMER_INCFG_CFGA6_Msk (0x1000UL) /*!< CTIMER INCFG: CFGA6 (Bitfield-Mask: 0x01) */ -#define CTIMER_INCFG_CFGB5_Pos (11UL) /*!< CTIMER INCFG: CFGB5 (Bit 11) */ -#define CTIMER_INCFG_CFGB5_Msk (0x800UL) /*!< CTIMER INCFG: CFGB5 (Bitfield-Mask: 0x01) */ -#define CTIMER_INCFG_CFGA5_Pos (10UL) /*!< CTIMER INCFG: CFGA5 (Bit 10) */ -#define CTIMER_INCFG_CFGA5_Msk (0x400UL) /*!< CTIMER INCFG: CFGA5 (Bitfield-Mask: 0x01) */ -#define CTIMER_INCFG_CFGB4_Pos (9UL) /*!< CTIMER INCFG: CFGB4 (Bit 9) */ -#define CTIMER_INCFG_CFGB4_Msk (0x200UL) /*!< CTIMER INCFG: CFGB4 (Bitfield-Mask: 0x01) */ -#define CTIMER_INCFG_CFGA4_Pos (8UL) /*!< CTIMER INCFG: CFGA4 (Bit 8) */ -#define CTIMER_INCFG_CFGA4_Msk (0x100UL) /*!< CTIMER INCFG: CFGA4 (Bitfield-Mask: 0x01) */ -#define CTIMER_INCFG_CFGB3_Pos (7UL) /*!< CTIMER INCFG: CFGB3 (Bit 7) */ -#define CTIMER_INCFG_CFGB3_Msk (0x80UL) /*!< CTIMER INCFG: CFGB3 (Bitfield-Mask: 0x01) */ -#define CTIMER_INCFG_CFGA3_Pos (6UL) /*!< CTIMER INCFG: CFGA3 (Bit 6) */ -#define CTIMER_INCFG_CFGA3_Msk (0x40UL) /*!< CTIMER INCFG: CFGA3 (Bitfield-Mask: 0x01) */ -#define CTIMER_INCFG_CFGB2_Pos (5UL) /*!< CTIMER INCFG: CFGB2 (Bit 5) */ -#define CTIMER_INCFG_CFGB2_Msk (0x20UL) /*!< CTIMER INCFG: CFGB2 (Bitfield-Mask: 0x01) */ -#define CTIMER_INCFG_CFGA2_Pos (4UL) /*!< CTIMER INCFG: CFGA2 (Bit 4) */ -#define CTIMER_INCFG_CFGA2_Msk (0x10UL) /*!< CTIMER INCFG: CFGA2 (Bitfield-Mask: 0x01) */ -#define CTIMER_INCFG_CFGB1_Pos (3UL) /*!< CTIMER INCFG: CFGB1 (Bit 3) */ -#define CTIMER_INCFG_CFGB1_Msk (0x8UL) /*!< CTIMER INCFG: CFGB1 (Bitfield-Mask: 0x01) */ -#define CTIMER_INCFG_CFGA1_Pos (2UL) /*!< CTIMER INCFG: CFGA1 (Bit 2) */ -#define CTIMER_INCFG_CFGA1_Msk (0x4UL) /*!< CTIMER INCFG: CFGA1 (Bitfield-Mask: 0x01) */ -#define CTIMER_INCFG_CFGB0_Pos (1UL) /*!< CTIMER INCFG: CFGB0 (Bit 1) */ -#define CTIMER_INCFG_CFGB0_Msk (0x2UL) /*!< CTIMER INCFG: CFGB0 (Bitfield-Mask: 0x01) */ -#define CTIMER_INCFG_CFGA0_Pos (0UL) /*!< CTIMER INCFG: CFGA0 (Bit 0) */ -#define CTIMER_INCFG_CFGA0_Msk (0x1UL) /*!< CTIMER INCFG: CFGA0 (Bitfield-Mask: 0x01) */ -/* ========================================================= STCFG ========================================================= */ -#define CTIMER_STCFG_FREEZE_Pos (31UL) /*!< CTIMER STCFG: FREEZE (Bit 31) */ -#define CTIMER_STCFG_FREEZE_Msk (0x80000000UL) /*!< CTIMER STCFG: FREEZE (Bitfield-Mask: 0x01) */ -#define CTIMER_STCFG_CLEAR_Pos (30UL) /*!< CTIMER STCFG: CLEAR (Bit 30) */ -#define CTIMER_STCFG_CLEAR_Msk (0x40000000UL) /*!< CTIMER STCFG: CLEAR (Bitfield-Mask: 0x01) */ -#define CTIMER_STCFG_COMPARE_H_EN_Pos (15UL) /*!< CTIMER STCFG: COMPARE_H_EN (Bit 15) */ -#define CTIMER_STCFG_COMPARE_H_EN_Msk (0x8000UL) /*!< CTIMER STCFG: COMPARE_H_EN (Bitfield-Mask: 0x01) */ -#define CTIMER_STCFG_COMPARE_G_EN_Pos (14UL) /*!< CTIMER STCFG: COMPARE_G_EN (Bit 14) */ -#define CTIMER_STCFG_COMPARE_G_EN_Msk (0x4000UL) /*!< CTIMER STCFG: COMPARE_G_EN (Bitfield-Mask: 0x01) */ -#define CTIMER_STCFG_COMPARE_F_EN_Pos (13UL) /*!< CTIMER STCFG: COMPARE_F_EN (Bit 13) */ -#define CTIMER_STCFG_COMPARE_F_EN_Msk (0x2000UL) /*!< CTIMER STCFG: COMPARE_F_EN (Bitfield-Mask: 0x01) */ -#define CTIMER_STCFG_COMPARE_E_EN_Pos (12UL) /*!< CTIMER STCFG: COMPARE_E_EN (Bit 12) */ -#define CTIMER_STCFG_COMPARE_E_EN_Msk (0x1000UL) /*!< CTIMER STCFG: COMPARE_E_EN (Bitfield-Mask: 0x01) */ -#define CTIMER_STCFG_COMPARE_D_EN_Pos (11UL) /*!< CTIMER STCFG: COMPARE_D_EN (Bit 11) */ -#define CTIMER_STCFG_COMPARE_D_EN_Msk (0x800UL) /*!< CTIMER STCFG: COMPARE_D_EN (Bitfield-Mask: 0x01) */ -#define CTIMER_STCFG_COMPARE_C_EN_Pos (10UL) /*!< CTIMER STCFG: COMPARE_C_EN (Bit 10) */ -#define CTIMER_STCFG_COMPARE_C_EN_Msk (0x400UL) /*!< CTIMER STCFG: COMPARE_C_EN (Bitfield-Mask: 0x01) */ -#define CTIMER_STCFG_COMPARE_B_EN_Pos (9UL) /*!< CTIMER STCFG: COMPARE_B_EN (Bit 9) */ -#define CTIMER_STCFG_COMPARE_B_EN_Msk (0x200UL) /*!< CTIMER STCFG: COMPARE_B_EN (Bitfield-Mask: 0x01) */ -#define CTIMER_STCFG_COMPARE_A_EN_Pos (8UL) /*!< CTIMER STCFG: COMPARE_A_EN (Bit 8) */ -#define CTIMER_STCFG_COMPARE_A_EN_Msk (0x100UL) /*!< CTIMER STCFG: COMPARE_A_EN (Bitfield-Mask: 0x01) */ -#define CTIMER_STCFG_CLKSEL_Pos (0UL) /*!< CTIMER STCFG: CLKSEL (Bit 0) */ -#define CTIMER_STCFG_CLKSEL_Msk (0xfUL) /*!< CTIMER STCFG: CLKSEL (Bitfield-Mask: 0x0f) */ -/* ========================================================= STTMR ========================================================= */ -#define CTIMER_STTMR_STTMR_Pos (0UL) /*!< CTIMER STTMR: STTMR (Bit 0) */ -#define CTIMER_STTMR_STTMR_Msk (0xffffffffUL) /*!< CTIMER STTMR: STTMR (Bitfield-Mask: 0xffffffff) */ -/* ==================================================== CAPTURECONTROL ===================================================== */ -#define CTIMER_CAPTURECONTROL_CAPTURE3_Pos (3UL) /*!< CTIMER CAPTURECONTROL: CAPTURE3 (Bit 3) */ -#define CTIMER_CAPTURECONTROL_CAPTURE3_Msk (0x8UL) /*!< CTIMER CAPTURECONTROL: CAPTURE3 (Bitfield-Mask: 0x01) */ -#define CTIMER_CAPTURECONTROL_CAPTURE2_Pos (2UL) /*!< CTIMER CAPTURECONTROL: CAPTURE2 (Bit 2) */ -#define CTIMER_CAPTURECONTROL_CAPTURE2_Msk (0x4UL) /*!< CTIMER CAPTURECONTROL: CAPTURE2 (Bitfield-Mask: 0x01) */ -#define CTIMER_CAPTURECONTROL_CAPTURE1_Pos (1UL) /*!< CTIMER CAPTURECONTROL: CAPTURE1 (Bit 1) */ -#define CTIMER_CAPTURECONTROL_CAPTURE1_Msk (0x2UL) /*!< CTIMER CAPTURECONTROL: CAPTURE1 (Bitfield-Mask: 0x01) */ -#define CTIMER_CAPTURECONTROL_CAPTURE0_Pos (0UL) /*!< CTIMER CAPTURECONTROL: CAPTURE0 (Bit 0) */ -#define CTIMER_CAPTURECONTROL_CAPTURE0_Msk (0x1UL) /*!< CTIMER CAPTURECONTROL: CAPTURE0 (Bitfield-Mask: 0x01) */ -/* ======================================================== SCMPR0 ========================================================= */ -#define CTIMER_SCMPR0_SCMPR0_Pos (0UL) /*!< CTIMER SCMPR0: SCMPR0 (Bit 0) */ -#define CTIMER_SCMPR0_SCMPR0_Msk (0xffffffffUL) /*!< CTIMER SCMPR0: SCMPR0 (Bitfield-Mask: 0xffffffff) */ -/* ======================================================== SCMPR1 ========================================================= */ -#define CTIMER_SCMPR1_SCMPR1_Pos (0UL) /*!< CTIMER SCMPR1: SCMPR1 (Bit 0) */ -#define CTIMER_SCMPR1_SCMPR1_Msk (0xffffffffUL) /*!< CTIMER SCMPR1: SCMPR1 (Bitfield-Mask: 0xffffffff) */ -/* ======================================================== SCMPR2 ========================================================= */ -#define CTIMER_SCMPR2_SCMPR2_Pos (0UL) /*!< CTIMER SCMPR2: SCMPR2 (Bit 0) */ -#define CTIMER_SCMPR2_SCMPR2_Msk (0xffffffffUL) /*!< CTIMER SCMPR2: SCMPR2 (Bitfield-Mask: 0xffffffff) */ -/* ======================================================== SCMPR3 ========================================================= */ -#define CTIMER_SCMPR3_SCMPR3_Pos (0UL) /*!< CTIMER SCMPR3: SCMPR3 (Bit 0) */ -#define CTIMER_SCMPR3_SCMPR3_Msk (0xffffffffUL) /*!< CTIMER SCMPR3: SCMPR3 (Bitfield-Mask: 0xffffffff) */ -/* ======================================================== SCMPR4 ========================================================= */ -#define CTIMER_SCMPR4_SCMPR4_Pos (0UL) /*!< CTIMER SCMPR4: SCMPR4 (Bit 0) */ -#define CTIMER_SCMPR4_SCMPR4_Msk (0xffffffffUL) /*!< CTIMER SCMPR4: SCMPR4 (Bitfield-Mask: 0xffffffff) */ -/* ======================================================== SCMPR5 ========================================================= */ -#define CTIMER_SCMPR5_SCMPR5_Pos (0UL) /*!< CTIMER SCMPR5: SCMPR5 (Bit 0) */ -#define CTIMER_SCMPR5_SCMPR5_Msk (0xffffffffUL) /*!< CTIMER SCMPR5: SCMPR5 (Bitfield-Mask: 0xffffffff) */ -/* ======================================================== SCMPR6 ========================================================= */ -#define CTIMER_SCMPR6_SCMPR6_Pos (0UL) /*!< CTIMER SCMPR6: SCMPR6 (Bit 0) */ -#define CTIMER_SCMPR6_SCMPR6_Msk (0xffffffffUL) /*!< CTIMER SCMPR6: SCMPR6 (Bitfield-Mask: 0xffffffff) */ -/* ======================================================== SCMPR7 ========================================================= */ -#define CTIMER_SCMPR7_SCMPR7_Pos (0UL) /*!< CTIMER SCMPR7: SCMPR7 (Bit 0) */ -#define CTIMER_SCMPR7_SCMPR7_Msk (0xffffffffUL) /*!< CTIMER SCMPR7: SCMPR7 (Bitfield-Mask: 0xffffffff) */ -/* ======================================================== SCAPT0 ========================================================= */ -#define CTIMER_SCAPT0_SCAPT0_Pos (0UL) /*!< CTIMER SCAPT0: SCAPT0 (Bit 0) */ -#define CTIMER_SCAPT0_SCAPT0_Msk (0xffffffffUL) /*!< CTIMER SCAPT0: SCAPT0 (Bitfield-Mask: 0xffffffff) */ -/* ======================================================== SCAPT1 ========================================================= */ -#define CTIMER_SCAPT1_SCAPT1_Pos (0UL) /*!< CTIMER SCAPT1: SCAPT1 (Bit 0) */ -#define CTIMER_SCAPT1_SCAPT1_Msk (0xffffffffUL) /*!< CTIMER SCAPT1: SCAPT1 (Bitfield-Mask: 0xffffffff) */ -/* ======================================================== SCAPT2 ========================================================= */ -#define CTIMER_SCAPT2_SCAPT2_Pos (0UL) /*!< CTIMER SCAPT2: SCAPT2 (Bit 0) */ -#define CTIMER_SCAPT2_SCAPT2_Msk (0xffffffffUL) /*!< CTIMER SCAPT2: SCAPT2 (Bitfield-Mask: 0xffffffff) */ -/* ======================================================== SCAPT3 ========================================================= */ -#define CTIMER_SCAPT3_SCAPT3_Pos (0UL) /*!< CTIMER SCAPT3: SCAPT3 (Bit 0) */ -#define CTIMER_SCAPT3_SCAPT3_Msk (0xffffffffUL) /*!< CTIMER SCAPT3: SCAPT3 (Bitfield-Mask: 0xffffffff) */ -/* ========================================================= SNVR0 ========================================================= */ -#define CTIMER_SNVR0_SNVR0_Pos (0UL) /*!< CTIMER SNVR0: SNVR0 (Bit 0) */ -#define CTIMER_SNVR0_SNVR0_Msk (0xffffffffUL) /*!< CTIMER SNVR0: SNVR0 (Bitfield-Mask: 0xffffffff) */ -/* ========================================================= SNVR1 ========================================================= */ -#define CTIMER_SNVR1_SNVR1_Pos (0UL) /*!< CTIMER SNVR1: SNVR1 (Bit 0) */ -#define CTIMER_SNVR1_SNVR1_Msk (0xffffffffUL) /*!< CTIMER SNVR1: SNVR1 (Bitfield-Mask: 0xffffffff) */ -/* ========================================================= SNVR2 ========================================================= */ -#define CTIMER_SNVR2_SNVR2_Pos (0UL) /*!< CTIMER SNVR2: SNVR2 (Bit 0) */ -#define CTIMER_SNVR2_SNVR2_Msk (0xffffffffUL) /*!< CTIMER SNVR2: SNVR2 (Bitfield-Mask: 0xffffffff) */ -/* ========================================================= SNVR3 ========================================================= */ -#define CTIMER_SNVR3_SNVR3_Pos (0UL) /*!< CTIMER SNVR3: SNVR3 (Bit 0) */ -#define CTIMER_SNVR3_SNVR3_Msk (0xffffffffUL) /*!< CTIMER SNVR3: SNVR3 (Bitfield-Mask: 0xffffffff) */ -/* ========================================================= INTEN ========================================================= */ -#define CTIMER_INTEN_CTMRB7C1INT_Pos (31UL) /*!< CTIMER INTEN: CTMRB7C1INT (Bit 31) */ -#define CTIMER_INTEN_CTMRB7C1INT_Msk (0x80000000UL) /*!< CTIMER INTEN: CTMRB7C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRA7C1INT_Pos (30UL) /*!< CTIMER INTEN: CTMRA7C1INT (Bit 30) */ -#define CTIMER_INTEN_CTMRA7C1INT_Msk (0x40000000UL) /*!< CTIMER INTEN: CTMRA7C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRB6C1INT_Pos (29UL) /*!< CTIMER INTEN: CTMRB6C1INT (Bit 29) */ -#define CTIMER_INTEN_CTMRB6C1INT_Msk (0x20000000UL) /*!< CTIMER INTEN: CTMRB6C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRA6C1INT_Pos (28UL) /*!< CTIMER INTEN: CTMRA6C1INT (Bit 28) */ -#define CTIMER_INTEN_CTMRA6C1INT_Msk (0x10000000UL) /*!< CTIMER INTEN: CTMRA6C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRB5C1INT_Pos (27UL) /*!< CTIMER INTEN: CTMRB5C1INT (Bit 27) */ -#define CTIMER_INTEN_CTMRB5C1INT_Msk (0x8000000UL) /*!< CTIMER INTEN: CTMRB5C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRA5C1INT_Pos (26UL) /*!< CTIMER INTEN: CTMRA5C1INT (Bit 26) */ -#define CTIMER_INTEN_CTMRA5C1INT_Msk (0x4000000UL) /*!< CTIMER INTEN: CTMRA5C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRB4C1INT_Pos (25UL) /*!< CTIMER INTEN: CTMRB4C1INT (Bit 25) */ -#define CTIMER_INTEN_CTMRB4C1INT_Msk (0x2000000UL) /*!< CTIMER INTEN: CTMRB4C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRA4C1INT_Pos (24UL) /*!< CTIMER INTEN: CTMRA4C1INT (Bit 24) */ -#define CTIMER_INTEN_CTMRA4C1INT_Msk (0x1000000UL) /*!< CTIMER INTEN: CTMRA4C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRB3C1INT_Pos (23UL) /*!< CTIMER INTEN: CTMRB3C1INT (Bit 23) */ -#define CTIMER_INTEN_CTMRB3C1INT_Msk (0x800000UL) /*!< CTIMER INTEN: CTMRB3C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRA3C1INT_Pos (22UL) /*!< CTIMER INTEN: CTMRA3C1INT (Bit 22) */ -#define CTIMER_INTEN_CTMRA3C1INT_Msk (0x400000UL) /*!< CTIMER INTEN: CTMRA3C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRB2C1INT_Pos (21UL) /*!< CTIMER INTEN: CTMRB2C1INT (Bit 21) */ -#define CTIMER_INTEN_CTMRB2C1INT_Msk (0x200000UL) /*!< CTIMER INTEN: CTMRB2C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRA2C1INT_Pos (20UL) /*!< CTIMER INTEN: CTMRA2C1INT (Bit 20) */ -#define CTIMER_INTEN_CTMRA2C1INT_Msk (0x100000UL) /*!< CTIMER INTEN: CTMRA2C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRB1C1INT_Pos (19UL) /*!< CTIMER INTEN: CTMRB1C1INT (Bit 19) */ -#define CTIMER_INTEN_CTMRB1C1INT_Msk (0x80000UL) /*!< CTIMER INTEN: CTMRB1C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRA1C1INT_Pos (18UL) /*!< CTIMER INTEN: CTMRA1C1INT (Bit 18) */ -#define CTIMER_INTEN_CTMRA1C1INT_Msk (0x40000UL) /*!< CTIMER INTEN: CTMRA1C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRB0C1INT_Pos (17UL) /*!< CTIMER INTEN: CTMRB0C1INT (Bit 17) */ -#define CTIMER_INTEN_CTMRB0C1INT_Msk (0x20000UL) /*!< CTIMER INTEN: CTMRB0C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRA0C1INT_Pos (16UL) /*!< CTIMER INTEN: CTMRA0C1INT (Bit 16) */ -#define CTIMER_INTEN_CTMRA0C1INT_Msk (0x10000UL) /*!< CTIMER INTEN: CTMRA0C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRB7C0INT_Pos (15UL) /*!< CTIMER INTEN: CTMRB7C0INT (Bit 15) */ -#define CTIMER_INTEN_CTMRB7C0INT_Msk (0x8000UL) /*!< CTIMER INTEN: CTMRB7C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRA7C0INT_Pos (14UL) /*!< CTIMER INTEN: CTMRA7C0INT (Bit 14) */ -#define CTIMER_INTEN_CTMRA7C0INT_Msk (0x4000UL) /*!< CTIMER INTEN: CTMRA7C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRB6C0INT_Pos (13UL) /*!< CTIMER INTEN: CTMRB6C0INT (Bit 13) */ -#define CTIMER_INTEN_CTMRB6C0INT_Msk (0x2000UL) /*!< CTIMER INTEN: CTMRB6C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRA6C0INT_Pos (12UL) /*!< CTIMER INTEN: CTMRA6C0INT (Bit 12) */ -#define CTIMER_INTEN_CTMRA6C0INT_Msk (0x1000UL) /*!< CTIMER INTEN: CTMRA6C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRB5C0INT_Pos (11UL) /*!< CTIMER INTEN: CTMRB5C0INT (Bit 11) */ -#define CTIMER_INTEN_CTMRB5C0INT_Msk (0x800UL) /*!< CTIMER INTEN: CTMRB5C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRA5C0INT_Pos (10UL) /*!< CTIMER INTEN: CTMRA5C0INT (Bit 10) */ -#define CTIMER_INTEN_CTMRA5C0INT_Msk (0x400UL) /*!< CTIMER INTEN: CTMRA5C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRB4C0INT_Pos (9UL) /*!< CTIMER INTEN: CTMRB4C0INT (Bit 9) */ -#define CTIMER_INTEN_CTMRB4C0INT_Msk (0x200UL) /*!< CTIMER INTEN: CTMRB4C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRA4C0INT_Pos (8UL) /*!< CTIMER INTEN: CTMRA4C0INT (Bit 8) */ -#define CTIMER_INTEN_CTMRA4C0INT_Msk (0x100UL) /*!< CTIMER INTEN: CTMRA4C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRB3C0INT_Pos (7UL) /*!< CTIMER INTEN: CTMRB3C0INT (Bit 7) */ -#define CTIMER_INTEN_CTMRB3C0INT_Msk (0x80UL) /*!< CTIMER INTEN: CTMRB3C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRA3C0INT_Pos (6UL) /*!< CTIMER INTEN: CTMRA3C0INT (Bit 6) */ -#define CTIMER_INTEN_CTMRA3C0INT_Msk (0x40UL) /*!< CTIMER INTEN: CTMRA3C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRB2C0INT_Pos (5UL) /*!< CTIMER INTEN: CTMRB2C0INT (Bit 5) */ -#define CTIMER_INTEN_CTMRB2C0INT_Msk (0x20UL) /*!< CTIMER INTEN: CTMRB2C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRA2C0INT_Pos (4UL) /*!< CTIMER INTEN: CTMRA2C0INT (Bit 4) */ -#define CTIMER_INTEN_CTMRA2C0INT_Msk (0x10UL) /*!< CTIMER INTEN: CTMRA2C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRB1C0INT_Pos (3UL) /*!< CTIMER INTEN: CTMRB1C0INT (Bit 3) */ -#define CTIMER_INTEN_CTMRB1C0INT_Msk (0x8UL) /*!< CTIMER INTEN: CTMRB1C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRA1C0INT_Pos (2UL) /*!< CTIMER INTEN: CTMRA1C0INT (Bit 2) */ -#define CTIMER_INTEN_CTMRA1C0INT_Msk (0x4UL) /*!< CTIMER INTEN: CTMRA1C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRB0C0INT_Pos (1UL) /*!< CTIMER INTEN: CTMRB0C0INT (Bit 1) */ -#define CTIMER_INTEN_CTMRB0C0INT_Msk (0x2UL) /*!< CTIMER INTEN: CTMRB0C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTEN_CTMRA0C0INT_Pos (0UL) /*!< CTIMER INTEN: CTMRA0C0INT (Bit 0) */ -#define CTIMER_INTEN_CTMRA0C0INT_Msk (0x1UL) /*!< CTIMER INTEN: CTMRA0C0INT (Bitfield-Mask: 0x01) */ -/* ======================================================== INTSTAT ======================================================== */ -#define CTIMER_INTSTAT_CTMRB7C1INT_Pos (31UL) /*!< CTIMER INTSTAT: CTMRB7C1INT (Bit 31) */ -#define CTIMER_INTSTAT_CTMRB7C1INT_Msk (0x80000000UL) /*!< CTIMER INTSTAT: CTMRB7C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRA7C1INT_Pos (30UL) /*!< CTIMER INTSTAT: CTMRA7C1INT (Bit 30) */ -#define CTIMER_INTSTAT_CTMRA7C1INT_Msk (0x40000000UL) /*!< CTIMER INTSTAT: CTMRA7C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRB6C1INT_Pos (29UL) /*!< CTIMER INTSTAT: CTMRB6C1INT (Bit 29) */ -#define CTIMER_INTSTAT_CTMRB6C1INT_Msk (0x20000000UL) /*!< CTIMER INTSTAT: CTMRB6C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRA6C1INT_Pos (28UL) /*!< CTIMER INTSTAT: CTMRA6C1INT (Bit 28) */ -#define CTIMER_INTSTAT_CTMRA6C1INT_Msk (0x10000000UL) /*!< CTIMER INTSTAT: CTMRA6C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRB5C1INT_Pos (27UL) /*!< CTIMER INTSTAT: CTMRB5C1INT (Bit 27) */ -#define CTIMER_INTSTAT_CTMRB5C1INT_Msk (0x8000000UL) /*!< CTIMER INTSTAT: CTMRB5C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRA5C1INT_Pos (26UL) /*!< CTIMER INTSTAT: CTMRA5C1INT (Bit 26) */ -#define CTIMER_INTSTAT_CTMRA5C1INT_Msk (0x4000000UL) /*!< CTIMER INTSTAT: CTMRA5C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRB4C1INT_Pos (25UL) /*!< CTIMER INTSTAT: CTMRB4C1INT (Bit 25) */ -#define CTIMER_INTSTAT_CTMRB4C1INT_Msk (0x2000000UL) /*!< CTIMER INTSTAT: CTMRB4C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRA4C1INT_Pos (24UL) /*!< CTIMER INTSTAT: CTMRA4C1INT (Bit 24) */ -#define CTIMER_INTSTAT_CTMRA4C1INT_Msk (0x1000000UL) /*!< CTIMER INTSTAT: CTMRA4C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRB3C1INT_Pos (23UL) /*!< CTIMER INTSTAT: CTMRB3C1INT (Bit 23) */ -#define CTIMER_INTSTAT_CTMRB3C1INT_Msk (0x800000UL) /*!< CTIMER INTSTAT: CTMRB3C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRA3C1INT_Pos (22UL) /*!< CTIMER INTSTAT: CTMRA3C1INT (Bit 22) */ -#define CTIMER_INTSTAT_CTMRA3C1INT_Msk (0x400000UL) /*!< CTIMER INTSTAT: CTMRA3C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRB2C1INT_Pos (21UL) /*!< CTIMER INTSTAT: CTMRB2C1INT (Bit 21) */ -#define CTIMER_INTSTAT_CTMRB2C1INT_Msk (0x200000UL) /*!< CTIMER INTSTAT: CTMRB2C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRA2C1INT_Pos (20UL) /*!< CTIMER INTSTAT: CTMRA2C1INT (Bit 20) */ -#define CTIMER_INTSTAT_CTMRA2C1INT_Msk (0x100000UL) /*!< CTIMER INTSTAT: CTMRA2C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRB1C1INT_Pos (19UL) /*!< CTIMER INTSTAT: CTMRB1C1INT (Bit 19) */ -#define CTIMER_INTSTAT_CTMRB1C1INT_Msk (0x80000UL) /*!< CTIMER INTSTAT: CTMRB1C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRA1C1INT_Pos (18UL) /*!< CTIMER INTSTAT: CTMRA1C1INT (Bit 18) */ -#define CTIMER_INTSTAT_CTMRA1C1INT_Msk (0x40000UL) /*!< CTIMER INTSTAT: CTMRA1C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRB0C1INT_Pos (17UL) /*!< CTIMER INTSTAT: CTMRB0C1INT (Bit 17) */ -#define CTIMER_INTSTAT_CTMRB0C1INT_Msk (0x20000UL) /*!< CTIMER INTSTAT: CTMRB0C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRA0C1INT_Pos (16UL) /*!< CTIMER INTSTAT: CTMRA0C1INT (Bit 16) */ -#define CTIMER_INTSTAT_CTMRA0C1INT_Msk (0x10000UL) /*!< CTIMER INTSTAT: CTMRA0C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRB7C0INT_Pos (15UL) /*!< CTIMER INTSTAT: CTMRB7C0INT (Bit 15) */ -#define CTIMER_INTSTAT_CTMRB7C0INT_Msk (0x8000UL) /*!< CTIMER INTSTAT: CTMRB7C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRA7C0INT_Pos (14UL) /*!< CTIMER INTSTAT: CTMRA7C0INT (Bit 14) */ -#define CTIMER_INTSTAT_CTMRA7C0INT_Msk (0x4000UL) /*!< CTIMER INTSTAT: CTMRA7C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRB6C0INT_Pos (13UL) /*!< CTIMER INTSTAT: CTMRB6C0INT (Bit 13) */ -#define CTIMER_INTSTAT_CTMRB6C0INT_Msk (0x2000UL) /*!< CTIMER INTSTAT: CTMRB6C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRA6C0INT_Pos (12UL) /*!< CTIMER INTSTAT: CTMRA6C0INT (Bit 12) */ -#define CTIMER_INTSTAT_CTMRA6C0INT_Msk (0x1000UL) /*!< CTIMER INTSTAT: CTMRA6C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRB5C0INT_Pos (11UL) /*!< CTIMER INTSTAT: CTMRB5C0INT (Bit 11) */ -#define CTIMER_INTSTAT_CTMRB5C0INT_Msk (0x800UL) /*!< CTIMER INTSTAT: CTMRB5C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRA5C0INT_Pos (10UL) /*!< CTIMER INTSTAT: CTMRA5C0INT (Bit 10) */ -#define CTIMER_INTSTAT_CTMRA5C0INT_Msk (0x400UL) /*!< CTIMER INTSTAT: CTMRA5C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRB4C0INT_Pos (9UL) /*!< CTIMER INTSTAT: CTMRB4C0INT (Bit 9) */ -#define CTIMER_INTSTAT_CTMRB4C0INT_Msk (0x200UL) /*!< CTIMER INTSTAT: CTMRB4C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRA4C0INT_Pos (8UL) /*!< CTIMER INTSTAT: CTMRA4C0INT (Bit 8) */ -#define CTIMER_INTSTAT_CTMRA4C0INT_Msk (0x100UL) /*!< CTIMER INTSTAT: CTMRA4C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRB3C0INT_Pos (7UL) /*!< CTIMER INTSTAT: CTMRB3C0INT (Bit 7) */ -#define CTIMER_INTSTAT_CTMRB3C0INT_Msk (0x80UL) /*!< CTIMER INTSTAT: CTMRB3C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRA3C0INT_Pos (6UL) /*!< CTIMER INTSTAT: CTMRA3C0INT (Bit 6) */ -#define CTIMER_INTSTAT_CTMRA3C0INT_Msk (0x40UL) /*!< CTIMER INTSTAT: CTMRA3C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRB2C0INT_Pos (5UL) /*!< CTIMER INTSTAT: CTMRB2C0INT (Bit 5) */ -#define CTIMER_INTSTAT_CTMRB2C0INT_Msk (0x20UL) /*!< CTIMER INTSTAT: CTMRB2C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRA2C0INT_Pos (4UL) /*!< CTIMER INTSTAT: CTMRA2C0INT (Bit 4) */ -#define CTIMER_INTSTAT_CTMRA2C0INT_Msk (0x10UL) /*!< CTIMER INTSTAT: CTMRA2C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRB1C0INT_Pos (3UL) /*!< CTIMER INTSTAT: CTMRB1C0INT (Bit 3) */ -#define CTIMER_INTSTAT_CTMRB1C0INT_Msk (0x8UL) /*!< CTIMER INTSTAT: CTMRB1C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRA1C0INT_Pos (2UL) /*!< CTIMER INTSTAT: CTMRA1C0INT (Bit 2) */ -#define CTIMER_INTSTAT_CTMRA1C0INT_Msk (0x4UL) /*!< CTIMER INTSTAT: CTMRA1C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRB0C0INT_Pos (1UL) /*!< CTIMER INTSTAT: CTMRB0C0INT (Bit 1) */ -#define CTIMER_INTSTAT_CTMRB0C0INT_Msk (0x2UL) /*!< CTIMER INTSTAT: CTMRB0C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSTAT_CTMRA0C0INT_Pos (0UL) /*!< CTIMER INTSTAT: CTMRA0C0INT (Bit 0) */ -#define CTIMER_INTSTAT_CTMRA0C0INT_Msk (0x1UL) /*!< CTIMER INTSTAT: CTMRA0C0INT (Bitfield-Mask: 0x01) */ -/* ======================================================== INTCLR ========================================================= */ -#define CTIMER_INTCLR_CTMRB7C1INT_Pos (31UL) /*!< CTIMER INTCLR: CTMRB7C1INT (Bit 31) */ -#define CTIMER_INTCLR_CTMRB7C1INT_Msk (0x80000000UL) /*!< CTIMER INTCLR: CTMRB7C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRA7C1INT_Pos (30UL) /*!< CTIMER INTCLR: CTMRA7C1INT (Bit 30) */ -#define CTIMER_INTCLR_CTMRA7C1INT_Msk (0x40000000UL) /*!< CTIMER INTCLR: CTMRA7C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRB6C1INT_Pos (29UL) /*!< CTIMER INTCLR: CTMRB6C1INT (Bit 29) */ -#define CTIMER_INTCLR_CTMRB6C1INT_Msk (0x20000000UL) /*!< CTIMER INTCLR: CTMRB6C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRA6C1INT_Pos (28UL) /*!< CTIMER INTCLR: CTMRA6C1INT (Bit 28) */ -#define CTIMER_INTCLR_CTMRA6C1INT_Msk (0x10000000UL) /*!< CTIMER INTCLR: CTMRA6C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRB5C1INT_Pos (27UL) /*!< CTIMER INTCLR: CTMRB5C1INT (Bit 27) */ -#define CTIMER_INTCLR_CTMRB5C1INT_Msk (0x8000000UL) /*!< CTIMER INTCLR: CTMRB5C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRA5C1INT_Pos (26UL) /*!< CTIMER INTCLR: CTMRA5C1INT (Bit 26) */ -#define CTIMER_INTCLR_CTMRA5C1INT_Msk (0x4000000UL) /*!< CTIMER INTCLR: CTMRA5C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRB4C1INT_Pos (25UL) /*!< CTIMER INTCLR: CTMRB4C1INT (Bit 25) */ -#define CTIMER_INTCLR_CTMRB4C1INT_Msk (0x2000000UL) /*!< CTIMER INTCLR: CTMRB4C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRA4C1INT_Pos (24UL) /*!< CTIMER INTCLR: CTMRA4C1INT (Bit 24) */ -#define CTIMER_INTCLR_CTMRA4C1INT_Msk (0x1000000UL) /*!< CTIMER INTCLR: CTMRA4C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRB3C1INT_Pos (23UL) /*!< CTIMER INTCLR: CTMRB3C1INT (Bit 23) */ -#define CTIMER_INTCLR_CTMRB3C1INT_Msk (0x800000UL) /*!< CTIMER INTCLR: CTMRB3C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRA3C1INT_Pos (22UL) /*!< CTIMER INTCLR: CTMRA3C1INT (Bit 22) */ -#define CTIMER_INTCLR_CTMRA3C1INT_Msk (0x400000UL) /*!< CTIMER INTCLR: CTMRA3C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRB2C1INT_Pos (21UL) /*!< CTIMER INTCLR: CTMRB2C1INT (Bit 21) */ -#define CTIMER_INTCLR_CTMRB2C1INT_Msk (0x200000UL) /*!< CTIMER INTCLR: CTMRB2C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRA2C1INT_Pos (20UL) /*!< CTIMER INTCLR: CTMRA2C1INT (Bit 20) */ -#define CTIMER_INTCLR_CTMRA2C1INT_Msk (0x100000UL) /*!< CTIMER INTCLR: CTMRA2C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRB1C1INT_Pos (19UL) /*!< CTIMER INTCLR: CTMRB1C1INT (Bit 19) */ -#define CTIMER_INTCLR_CTMRB1C1INT_Msk (0x80000UL) /*!< CTIMER INTCLR: CTMRB1C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRA1C1INT_Pos (18UL) /*!< CTIMER INTCLR: CTMRA1C1INT (Bit 18) */ -#define CTIMER_INTCLR_CTMRA1C1INT_Msk (0x40000UL) /*!< CTIMER INTCLR: CTMRA1C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRB0C1INT_Pos (17UL) /*!< CTIMER INTCLR: CTMRB0C1INT (Bit 17) */ -#define CTIMER_INTCLR_CTMRB0C1INT_Msk (0x20000UL) /*!< CTIMER INTCLR: CTMRB0C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRA0C1INT_Pos (16UL) /*!< CTIMER INTCLR: CTMRA0C1INT (Bit 16) */ -#define CTIMER_INTCLR_CTMRA0C1INT_Msk (0x10000UL) /*!< CTIMER INTCLR: CTMRA0C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRB7C0INT_Pos (15UL) /*!< CTIMER INTCLR: CTMRB7C0INT (Bit 15) */ -#define CTIMER_INTCLR_CTMRB7C0INT_Msk (0x8000UL) /*!< CTIMER INTCLR: CTMRB7C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRA7C0INT_Pos (14UL) /*!< CTIMER INTCLR: CTMRA7C0INT (Bit 14) */ -#define CTIMER_INTCLR_CTMRA7C0INT_Msk (0x4000UL) /*!< CTIMER INTCLR: CTMRA7C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRB6C0INT_Pos (13UL) /*!< CTIMER INTCLR: CTMRB6C0INT (Bit 13) */ -#define CTIMER_INTCLR_CTMRB6C0INT_Msk (0x2000UL) /*!< CTIMER INTCLR: CTMRB6C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRA6C0INT_Pos (12UL) /*!< CTIMER INTCLR: CTMRA6C0INT (Bit 12) */ -#define CTIMER_INTCLR_CTMRA6C0INT_Msk (0x1000UL) /*!< CTIMER INTCLR: CTMRA6C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRB5C0INT_Pos (11UL) /*!< CTIMER INTCLR: CTMRB5C0INT (Bit 11) */ -#define CTIMER_INTCLR_CTMRB5C0INT_Msk (0x800UL) /*!< CTIMER INTCLR: CTMRB5C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRA5C0INT_Pos (10UL) /*!< CTIMER INTCLR: CTMRA5C0INT (Bit 10) */ -#define CTIMER_INTCLR_CTMRA5C0INT_Msk (0x400UL) /*!< CTIMER INTCLR: CTMRA5C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRB4C0INT_Pos (9UL) /*!< CTIMER INTCLR: CTMRB4C0INT (Bit 9) */ -#define CTIMER_INTCLR_CTMRB4C0INT_Msk (0x200UL) /*!< CTIMER INTCLR: CTMRB4C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRA4C0INT_Pos (8UL) /*!< CTIMER INTCLR: CTMRA4C0INT (Bit 8) */ -#define CTIMER_INTCLR_CTMRA4C0INT_Msk (0x100UL) /*!< CTIMER INTCLR: CTMRA4C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRB3C0INT_Pos (7UL) /*!< CTIMER INTCLR: CTMRB3C0INT (Bit 7) */ -#define CTIMER_INTCLR_CTMRB3C0INT_Msk (0x80UL) /*!< CTIMER INTCLR: CTMRB3C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRA3C0INT_Pos (6UL) /*!< CTIMER INTCLR: CTMRA3C0INT (Bit 6) */ -#define CTIMER_INTCLR_CTMRA3C0INT_Msk (0x40UL) /*!< CTIMER INTCLR: CTMRA3C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRB2C0INT_Pos (5UL) /*!< CTIMER INTCLR: CTMRB2C0INT (Bit 5) */ -#define CTIMER_INTCLR_CTMRB2C0INT_Msk (0x20UL) /*!< CTIMER INTCLR: CTMRB2C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRA2C0INT_Pos (4UL) /*!< CTIMER INTCLR: CTMRA2C0INT (Bit 4) */ -#define CTIMER_INTCLR_CTMRA2C0INT_Msk (0x10UL) /*!< CTIMER INTCLR: CTMRA2C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRB1C0INT_Pos (3UL) /*!< CTIMER INTCLR: CTMRB1C0INT (Bit 3) */ -#define CTIMER_INTCLR_CTMRB1C0INT_Msk (0x8UL) /*!< CTIMER INTCLR: CTMRB1C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRA1C0INT_Pos (2UL) /*!< CTIMER INTCLR: CTMRA1C0INT (Bit 2) */ -#define CTIMER_INTCLR_CTMRA1C0INT_Msk (0x4UL) /*!< CTIMER INTCLR: CTMRA1C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRB0C0INT_Pos (1UL) /*!< CTIMER INTCLR: CTMRB0C0INT (Bit 1) */ -#define CTIMER_INTCLR_CTMRB0C0INT_Msk (0x2UL) /*!< CTIMER INTCLR: CTMRB0C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTCLR_CTMRA0C0INT_Pos (0UL) /*!< CTIMER INTCLR: CTMRA0C0INT (Bit 0) */ -#define CTIMER_INTCLR_CTMRA0C0INT_Msk (0x1UL) /*!< CTIMER INTCLR: CTMRA0C0INT (Bitfield-Mask: 0x01) */ -/* ======================================================== INTSET ========================================================= */ -#define CTIMER_INTSET_CTMRB7C1INT_Pos (31UL) /*!< CTIMER INTSET: CTMRB7C1INT (Bit 31) */ -#define CTIMER_INTSET_CTMRB7C1INT_Msk (0x80000000UL) /*!< CTIMER INTSET: CTMRB7C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRA7C1INT_Pos (30UL) /*!< CTIMER INTSET: CTMRA7C1INT (Bit 30) */ -#define CTIMER_INTSET_CTMRA7C1INT_Msk (0x40000000UL) /*!< CTIMER INTSET: CTMRA7C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRB6C1INT_Pos (29UL) /*!< CTIMER INTSET: CTMRB6C1INT (Bit 29) */ -#define CTIMER_INTSET_CTMRB6C1INT_Msk (0x20000000UL) /*!< CTIMER INTSET: CTMRB6C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRA6C1INT_Pos (28UL) /*!< CTIMER INTSET: CTMRA6C1INT (Bit 28) */ -#define CTIMER_INTSET_CTMRA6C1INT_Msk (0x10000000UL) /*!< CTIMER INTSET: CTMRA6C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRB5C1INT_Pos (27UL) /*!< CTIMER INTSET: CTMRB5C1INT (Bit 27) */ -#define CTIMER_INTSET_CTMRB5C1INT_Msk (0x8000000UL) /*!< CTIMER INTSET: CTMRB5C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRA5C1INT_Pos (26UL) /*!< CTIMER INTSET: CTMRA5C1INT (Bit 26) */ -#define CTIMER_INTSET_CTMRA5C1INT_Msk (0x4000000UL) /*!< CTIMER INTSET: CTMRA5C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRB4C1INT_Pos (25UL) /*!< CTIMER INTSET: CTMRB4C1INT (Bit 25) */ -#define CTIMER_INTSET_CTMRB4C1INT_Msk (0x2000000UL) /*!< CTIMER INTSET: CTMRB4C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRA4C1INT_Pos (24UL) /*!< CTIMER INTSET: CTMRA4C1INT (Bit 24) */ -#define CTIMER_INTSET_CTMRA4C1INT_Msk (0x1000000UL) /*!< CTIMER INTSET: CTMRA4C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRB3C1INT_Pos (23UL) /*!< CTIMER INTSET: CTMRB3C1INT (Bit 23) */ -#define CTIMER_INTSET_CTMRB3C1INT_Msk (0x800000UL) /*!< CTIMER INTSET: CTMRB3C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRA3C1INT_Pos (22UL) /*!< CTIMER INTSET: CTMRA3C1INT (Bit 22) */ -#define CTIMER_INTSET_CTMRA3C1INT_Msk (0x400000UL) /*!< CTIMER INTSET: CTMRA3C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRB2C1INT_Pos (21UL) /*!< CTIMER INTSET: CTMRB2C1INT (Bit 21) */ -#define CTIMER_INTSET_CTMRB2C1INT_Msk (0x200000UL) /*!< CTIMER INTSET: CTMRB2C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRA2C1INT_Pos (20UL) /*!< CTIMER INTSET: CTMRA2C1INT (Bit 20) */ -#define CTIMER_INTSET_CTMRA2C1INT_Msk (0x100000UL) /*!< CTIMER INTSET: CTMRA2C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRB1C1INT_Pos (19UL) /*!< CTIMER INTSET: CTMRB1C1INT (Bit 19) */ -#define CTIMER_INTSET_CTMRB1C1INT_Msk (0x80000UL) /*!< CTIMER INTSET: CTMRB1C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRA1C1INT_Pos (18UL) /*!< CTIMER INTSET: CTMRA1C1INT (Bit 18) */ -#define CTIMER_INTSET_CTMRA1C1INT_Msk (0x40000UL) /*!< CTIMER INTSET: CTMRA1C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRB0C1INT_Pos (17UL) /*!< CTIMER INTSET: CTMRB0C1INT (Bit 17) */ -#define CTIMER_INTSET_CTMRB0C1INT_Msk (0x20000UL) /*!< CTIMER INTSET: CTMRB0C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRA0C1INT_Pos (16UL) /*!< CTIMER INTSET: CTMRA0C1INT (Bit 16) */ -#define CTIMER_INTSET_CTMRA0C1INT_Msk (0x10000UL) /*!< CTIMER INTSET: CTMRA0C1INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRB7C0INT_Pos (15UL) /*!< CTIMER INTSET: CTMRB7C0INT (Bit 15) */ -#define CTIMER_INTSET_CTMRB7C0INT_Msk (0x8000UL) /*!< CTIMER INTSET: CTMRB7C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRA7C0INT_Pos (14UL) /*!< CTIMER INTSET: CTMRA7C0INT (Bit 14) */ -#define CTIMER_INTSET_CTMRA7C0INT_Msk (0x4000UL) /*!< CTIMER INTSET: CTMRA7C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRB6C0INT_Pos (13UL) /*!< CTIMER INTSET: CTMRB6C0INT (Bit 13) */ -#define CTIMER_INTSET_CTMRB6C0INT_Msk (0x2000UL) /*!< CTIMER INTSET: CTMRB6C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRA6C0INT_Pos (12UL) /*!< CTIMER INTSET: CTMRA6C0INT (Bit 12) */ -#define CTIMER_INTSET_CTMRA6C0INT_Msk (0x1000UL) /*!< CTIMER INTSET: CTMRA6C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRB5C0INT_Pos (11UL) /*!< CTIMER INTSET: CTMRB5C0INT (Bit 11) */ -#define CTIMER_INTSET_CTMRB5C0INT_Msk (0x800UL) /*!< CTIMER INTSET: CTMRB5C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRA5C0INT_Pos (10UL) /*!< CTIMER INTSET: CTMRA5C0INT (Bit 10) */ -#define CTIMER_INTSET_CTMRA5C0INT_Msk (0x400UL) /*!< CTIMER INTSET: CTMRA5C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRB4C0INT_Pos (9UL) /*!< CTIMER INTSET: CTMRB4C0INT (Bit 9) */ -#define CTIMER_INTSET_CTMRB4C0INT_Msk (0x200UL) /*!< CTIMER INTSET: CTMRB4C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRA4C0INT_Pos (8UL) /*!< CTIMER INTSET: CTMRA4C0INT (Bit 8) */ -#define CTIMER_INTSET_CTMRA4C0INT_Msk (0x100UL) /*!< CTIMER INTSET: CTMRA4C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRB3C0INT_Pos (7UL) /*!< CTIMER INTSET: CTMRB3C0INT (Bit 7) */ -#define CTIMER_INTSET_CTMRB3C0INT_Msk (0x80UL) /*!< CTIMER INTSET: CTMRB3C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRA3C0INT_Pos (6UL) /*!< CTIMER INTSET: CTMRA3C0INT (Bit 6) */ -#define CTIMER_INTSET_CTMRA3C0INT_Msk (0x40UL) /*!< CTIMER INTSET: CTMRA3C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRB2C0INT_Pos (5UL) /*!< CTIMER INTSET: CTMRB2C0INT (Bit 5) */ -#define CTIMER_INTSET_CTMRB2C0INT_Msk (0x20UL) /*!< CTIMER INTSET: CTMRB2C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRA2C0INT_Pos (4UL) /*!< CTIMER INTSET: CTMRA2C0INT (Bit 4) */ -#define CTIMER_INTSET_CTMRA2C0INT_Msk (0x10UL) /*!< CTIMER INTSET: CTMRA2C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRB1C0INT_Pos (3UL) /*!< CTIMER INTSET: CTMRB1C0INT (Bit 3) */ -#define CTIMER_INTSET_CTMRB1C0INT_Msk (0x8UL) /*!< CTIMER INTSET: CTMRB1C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRA1C0INT_Pos (2UL) /*!< CTIMER INTSET: CTMRA1C0INT (Bit 2) */ -#define CTIMER_INTSET_CTMRA1C0INT_Msk (0x4UL) /*!< CTIMER INTSET: CTMRA1C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRB0C0INT_Pos (1UL) /*!< CTIMER INTSET: CTMRB0C0INT (Bit 1) */ -#define CTIMER_INTSET_CTMRB0C0INT_Msk (0x2UL) /*!< CTIMER INTSET: CTMRB0C0INT (Bitfield-Mask: 0x01) */ -#define CTIMER_INTSET_CTMRA0C0INT_Pos (0UL) /*!< CTIMER INTSET: CTMRA0C0INT (Bit 0) */ -#define CTIMER_INTSET_CTMRA0C0INT_Msk (0x1UL) /*!< CTIMER INTSET: CTMRA0C0INT (Bitfield-Mask: 0x01) */ -/* ======================================================= STMINTEN ======================================================== */ -#define CTIMER_STMINTEN_CAPTURED_Pos (12UL) /*!< CTIMER STMINTEN: CAPTURED (Bit 12) */ -#define CTIMER_STMINTEN_CAPTURED_Msk (0x1000UL) /*!< CTIMER STMINTEN: CAPTURED (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTEN_CAPTUREC_Pos (11UL) /*!< CTIMER STMINTEN: CAPTUREC (Bit 11) */ -#define CTIMER_STMINTEN_CAPTUREC_Msk (0x800UL) /*!< CTIMER STMINTEN: CAPTUREC (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTEN_CAPTUREB_Pos (10UL) /*!< CTIMER STMINTEN: CAPTUREB (Bit 10) */ -#define CTIMER_STMINTEN_CAPTUREB_Msk (0x400UL) /*!< CTIMER STMINTEN: CAPTUREB (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTEN_CAPTUREA_Pos (9UL) /*!< CTIMER STMINTEN: CAPTUREA (Bit 9) */ -#define CTIMER_STMINTEN_CAPTUREA_Msk (0x200UL) /*!< CTIMER STMINTEN: CAPTUREA (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTEN_OVERFLOW_Pos (8UL) /*!< CTIMER STMINTEN: OVERFLOW (Bit 8) */ -#define CTIMER_STMINTEN_OVERFLOW_Msk (0x100UL) /*!< CTIMER STMINTEN: OVERFLOW (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTEN_COMPAREH_Pos (7UL) /*!< CTIMER STMINTEN: COMPAREH (Bit 7) */ -#define CTIMER_STMINTEN_COMPAREH_Msk (0x80UL) /*!< CTIMER STMINTEN: COMPAREH (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTEN_COMPAREG_Pos (6UL) /*!< CTIMER STMINTEN: COMPAREG (Bit 6) */ -#define CTIMER_STMINTEN_COMPAREG_Msk (0x40UL) /*!< CTIMER STMINTEN: COMPAREG (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTEN_COMPAREF_Pos (5UL) /*!< CTIMER STMINTEN: COMPAREF (Bit 5) */ -#define CTIMER_STMINTEN_COMPAREF_Msk (0x20UL) /*!< CTIMER STMINTEN: COMPAREF (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTEN_COMPAREE_Pos (4UL) /*!< CTIMER STMINTEN: COMPAREE (Bit 4) */ -#define CTIMER_STMINTEN_COMPAREE_Msk (0x10UL) /*!< CTIMER STMINTEN: COMPAREE (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTEN_COMPARED_Pos (3UL) /*!< CTIMER STMINTEN: COMPARED (Bit 3) */ -#define CTIMER_STMINTEN_COMPARED_Msk (0x8UL) /*!< CTIMER STMINTEN: COMPARED (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTEN_COMPAREC_Pos (2UL) /*!< CTIMER STMINTEN: COMPAREC (Bit 2) */ -#define CTIMER_STMINTEN_COMPAREC_Msk (0x4UL) /*!< CTIMER STMINTEN: COMPAREC (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTEN_COMPAREB_Pos (1UL) /*!< CTIMER STMINTEN: COMPAREB (Bit 1) */ -#define CTIMER_STMINTEN_COMPAREB_Msk (0x2UL) /*!< CTIMER STMINTEN: COMPAREB (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTEN_COMPAREA_Pos (0UL) /*!< CTIMER STMINTEN: COMPAREA (Bit 0) */ -#define CTIMER_STMINTEN_COMPAREA_Msk (0x1UL) /*!< CTIMER STMINTEN: COMPAREA (Bitfield-Mask: 0x01) */ -/* ====================================================== STMINTSTAT ======================================================= */ -#define CTIMER_STMINTSTAT_CAPTURED_Pos (12UL) /*!< CTIMER STMINTSTAT: CAPTURED (Bit 12) */ -#define CTIMER_STMINTSTAT_CAPTURED_Msk (0x1000UL) /*!< CTIMER STMINTSTAT: CAPTURED (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTSTAT_CAPTUREC_Pos (11UL) /*!< CTIMER STMINTSTAT: CAPTUREC (Bit 11) */ -#define CTIMER_STMINTSTAT_CAPTUREC_Msk (0x800UL) /*!< CTIMER STMINTSTAT: CAPTUREC (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTSTAT_CAPTUREB_Pos (10UL) /*!< CTIMER STMINTSTAT: CAPTUREB (Bit 10) */ -#define CTIMER_STMINTSTAT_CAPTUREB_Msk (0x400UL) /*!< CTIMER STMINTSTAT: CAPTUREB (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTSTAT_CAPTUREA_Pos (9UL) /*!< CTIMER STMINTSTAT: CAPTUREA (Bit 9) */ -#define CTIMER_STMINTSTAT_CAPTUREA_Msk (0x200UL) /*!< CTIMER STMINTSTAT: CAPTUREA (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTSTAT_OVERFLOW_Pos (8UL) /*!< CTIMER STMINTSTAT: OVERFLOW (Bit 8) */ -#define CTIMER_STMINTSTAT_OVERFLOW_Msk (0x100UL) /*!< CTIMER STMINTSTAT: OVERFLOW (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTSTAT_COMPAREH_Pos (7UL) /*!< CTIMER STMINTSTAT: COMPAREH (Bit 7) */ -#define CTIMER_STMINTSTAT_COMPAREH_Msk (0x80UL) /*!< CTIMER STMINTSTAT: COMPAREH (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTSTAT_COMPAREG_Pos (6UL) /*!< CTIMER STMINTSTAT: COMPAREG (Bit 6) */ -#define CTIMER_STMINTSTAT_COMPAREG_Msk (0x40UL) /*!< CTIMER STMINTSTAT: COMPAREG (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTSTAT_COMPAREF_Pos (5UL) /*!< CTIMER STMINTSTAT: COMPAREF (Bit 5) */ -#define CTIMER_STMINTSTAT_COMPAREF_Msk (0x20UL) /*!< CTIMER STMINTSTAT: COMPAREF (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTSTAT_COMPAREE_Pos (4UL) /*!< CTIMER STMINTSTAT: COMPAREE (Bit 4) */ -#define CTIMER_STMINTSTAT_COMPAREE_Msk (0x10UL) /*!< CTIMER STMINTSTAT: COMPAREE (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTSTAT_COMPARED_Pos (3UL) /*!< CTIMER STMINTSTAT: COMPARED (Bit 3) */ -#define CTIMER_STMINTSTAT_COMPARED_Msk (0x8UL) /*!< CTIMER STMINTSTAT: COMPARED (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTSTAT_COMPAREC_Pos (2UL) /*!< CTIMER STMINTSTAT: COMPAREC (Bit 2) */ -#define CTIMER_STMINTSTAT_COMPAREC_Msk (0x4UL) /*!< CTIMER STMINTSTAT: COMPAREC (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTSTAT_COMPAREB_Pos (1UL) /*!< CTIMER STMINTSTAT: COMPAREB (Bit 1) */ -#define CTIMER_STMINTSTAT_COMPAREB_Msk (0x2UL) /*!< CTIMER STMINTSTAT: COMPAREB (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTSTAT_COMPAREA_Pos (0UL) /*!< CTIMER STMINTSTAT: COMPAREA (Bit 0) */ -#define CTIMER_STMINTSTAT_COMPAREA_Msk (0x1UL) /*!< CTIMER STMINTSTAT: COMPAREA (Bitfield-Mask: 0x01) */ -/* ======================================================= STMINTCLR ======================================================= */ -#define CTIMER_STMINTCLR_CAPTURED_Pos (12UL) /*!< CTIMER STMINTCLR: CAPTURED (Bit 12) */ -#define CTIMER_STMINTCLR_CAPTURED_Msk (0x1000UL) /*!< CTIMER STMINTCLR: CAPTURED (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTCLR_CAPTUREC_Pos (11UL) /*!< CTIMER STMINTCLR: CAPTUREC (Bit 11) */ -#define CTIMER_STMINTCLR_CAPTUREC_Msk (0x800UL) /*!< CTIMER STMINTCLR: CAPTUREC (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTCLR_CAPTUREB_Pos (10UL) /*!< CTIMER STMINTCLR: CAPTUREB (Bit 10) */ -#define CTIMER_STMINTCLR_CAPTUREB_Msk (0x400UL) /*!< CTIMER STMINTCLR: CAPTUREB (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTCLR_CAPTUREA_Pos (9UL) /*!< CTIMER STMINTCLR: CAPTUREA (Bit 9) */ -#define CTIMER_STMINTCLR_CAPTUREA_Msk (0x200UL) /*!< CTIMER STMINTCLR: CAPTUREA (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTCLR_OVERFLOW_Pos (8UL) /*!< CTIMER STMINTCLR: OVERFLOW (Bit 8) */ -#define CTIMER_STMINTCLR_OVERFLOW_Msk (0x100UL) /*!< CTIMER STMINTCLR: OVERFLOW (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTCLR_COMPAREH_Pos (7UL) /*!< CTIMER STMINTCLR: COMPAREH (Bit 7) */ -#define CTIMER_STMINTCLR_COMPAREH_Msk (0x80UL) /*!< CTIMER STMINTCLR: COMPAREH (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTCLR_COMPAREG_Pos (6UL) /*!< CTIMER STMINTCLR: COMPAREG (Bit 6) */ -#define CTIMER_STMINTCLR_COMPAREG_Msk (0x40UL) /*!< CTIMER STMINTCLR: COMPAREG (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTCLR_COMPAREF_Pos (5UL) /*!< CTIMER STMINTCLR: COMPAREF (Bit 5) */ -#define CTIMER_STMINTCLR_COMPAREF_Msk (0x20UL) /*!< CTIMER STMINTCLR: COMPAREF (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTCLR_COMPAREE_Pos (4UL) /*!< CTIMER STMINTCLR: COMPAREE (Bit 4) */ -#define CTIMER_STMINTCLR_COMPAREE_Msk (0x10UL) /*!< CTIMER STMINTCLR: COMPAREE (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTCLR_COMPARED_Pos (3UL) /*!< CTIMER STMINTCLR: COMPARED (Bit 3) */ -#define CTIMER_STMINTCLR_COMPARED_Msk (0x8UL) /*!< CTIMER STMINTCLR: COMPARED (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTCLR_COMPAREC_Pos (2UL) /*!< CTIMER STMINTCLR: COMPAREC (Bit 2) */ -#define CTIMER_STMINTCLR_COMPAREC_Msk (0x4UL) /*!< CTIMER STMINTCLR: COMPAREC (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTCLR_COMPAREB_Pos (1UL) /*!< CTIMER STMINTCLR: COMPAREB (Bit 1) */ -#define CTIMER_STMINTCLR_COMPAREB_Msk (0x2UL) /*!< CTIMER STMINTCLR: COMPAREB (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTCLR_COMPAREA_Pos (0UL) /*!< CTIMER STMINTCLR: COMPAREA (Bit 0) */ -#define CTIMER_STMINTCLR_COMPAREA_Msk (0x1UL) /*!< CTIMER STMINTCLR: COMPAREA (Bitfield-Mask: 0x01) */ -/* ======================================================= STMINTSET ======================================================= */ -#define CTIMER_STMINTSET_CAPTURED_Pos (12UL) /*!< CTIMER STMINTSET: CAPTURED (Bit 12) */ -#define CTIMER_STMINTSET_CAPTURED_Msk (0x1000UL) /*!< CTIMER STMINTSET: CAPTURED (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTSET_CAPTUREC_Pos (11UL) /*!< CTIMER STMINTSET: CAPTUREC (Bit 11) */ -#define CTIMER_STMINTSET_CAPTUREC_Msk (0x800UL) /*!< CTIMER STMINTSET: CAPTUREC (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTSET_CAPTUREB_Pos (10UL) /*!< CTIMER STMINTSET: CAPTUREB (Bit 10) */ -#define CTIMER_STMINTSET_CAPTUREB_Msk (0x400UL) /*!< CTIMER STMINTSET: CAPTUREB (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTSET_CAPTUREA_Pos (9UL) /*!< CTIMER STMINTSET: CAPTUREA (Bit 9) */ -#define CTIMER_STMINTSET_CAPTUREA_Msk (0x200UL) /*!< CTIMER STMINTSET: CAPTUREA (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTSET_OVERFLOW_Pos (8UL) /*!< CTIMER STMINTSET: OVERFLOW (Bit 8) */ -#define CTIMER_STMINTSET_OVERFLOW_Msk (0x100UL) /*!< CTIMER STMINTSET: OVERFLOW (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTSET_COMPAREH_Pos (7UL) /*!< CTIMER STMINTSET: COMPAREH (Bit 7) */ -#define CTIMER_STMINTSET_COMPAREH_Msk (0x80UL) /*!< CTIMER STMINTSET: COMPAREH (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTSET_COMPAREG_Pos (6UL) /*!< CTIMER STMINTSET: COMPAREG (Bit 6) */ -#define CTIMER_STMINTSET_COMPAREG_Msk (0x40UL) /*!< CTIMER STMINTSET: COMPAREG (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTSET_COMPAREF_Pos (5UL) /*!< CTIMER STMINTSET: COMPAREF (Bit 5) */ -#define CTIMER_STMINTSET_COMPAREF_Msk (0x20UL) /*!< CTIMER STMINTSET: COMPAREF (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTSET_COMPAREE_Pos (4UL) /*!< CTIMER STMINTSET: COMPAREE (Bit 4) */ -#define CTIMER_STMINTSET_COMPAREE_Msk (0x10UL) /*!< CTIMER STMINTSET: COMPAREE (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTSET_COMPARED_Pos (3UL) /*!< CTIMER STMINTSET: COMPARED (Bit 3) */ -#define CTIMER_STMINTSET_COMPARED_Msk (0x8UL) /*!< CTIMER STMINTSET: COMPARED (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTSET_COMPAREC_Pos (2UL) /*!< CTIMER STMINTSET: COMPAREC (Bit 2) */ -#define CTIMER_STMINTSET_COMPAREC_Msk (0x4UL) /*!< CTIMER STMINTSET: COMPAREC (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTSET_COMPAREB_Pos (1UL) /*!< CTIMER STMINTSET: COMPAREB (Bit 1) */ -#define CTIMER_STMINTSET_COMPAREB_Msk (0x2UL) /*!< CTIMER STMINTSET: COMPAREB (Bitfield-Mask: 0x01) */ -#define CTIMER_STMINTSET_COMPAREA_Pos (0UL) /*!< CTIMER STMINTSET: COMPAREA (Bit 0) */ -#define CTIMER_STMINTSET_COMPAREA_Msk (0x1UL) /*!< CTIMER STMINTSET: COMPAREA (Bitfield-Mask: 0x01) */ - - -/* =========================================================================================================================== */ -/* ================ GPIO ================ */ -/* =========================================================================================================================== */ - -/* ======================================================== PADREGA ======================================================== */ -#define GPIO_PADREGA_PAD3PWRUP_Pos (30UL) /*!< GPIO PADREGA: PAD3PWRUP (Bit 30) */ -#define GPIO_PADREGA_PAD3PWRUP_Msk (0x40000000UL) /*!< GPIO PADREGA: PAD3PWRUP (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGA_PAD3FNCSEL_Pos (27UL) /*!< GPIO PADREGA: PAD3FNCSEL (Bit 27) */ -#define GPIO_PADREGA_PAD3FNCSEL_Msk (0x38000000UL) /*!< GPIO PADREGA: PAD3FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGA_PAD3STRNG_Pos (26UL) /*!< GPIO PADREGA: PAD3STRNG (Bit 26) */ -#define GPIO_PADREGA_PAD3STRNG_Msk (0x4000000UL) /*!< GPIO PADREGA: PAD3STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGA_PAD3INPEN_Pos (25UL) /*!< GPIO PADREGA: PAD3INPEN (Bit 25) */ -#define GPIO_PADREGA_PAD3INPEN_Msk (0x2000000UL) /*!< GPIO PADREGA: PAD3INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGA_PAD3PULL_Pos (24UL) /*!< GPIO PADREGA: PAD3PULL (Bit 24) */ -#define GPIO_PADREGA_PAD3PULL_Msk (0x1000000UL) /*!< GPIO PADREGA: PAD3PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGA_PAD2FNCSEL_Pos (19UL) /*!< GPIO PADREGA: PAD2FNCSEL (Bit 19) */ -#define GPIO_PADREGA_PAD2FNCSEL_Msk (0x380000UL) /*!< GPIO PADREGA: PAD2FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGA_PAD2STRNG_Pos (18UL) /*!< GPIO PADREGA: PAD2STRNG (Bit 18) */ -#define GPIO_PADREGA_PAD2STRNG_Msk (0x40000UL) /*!< GPIO PADREGA: PAD2STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGA_PAD2INPEN_Pos (17UL) /*!< GPIO PADREGA: PAD2INPEN (Bit 17) */ -#define GPIO_PADREGA_PAD2INPEN_Msk (0x20000UL) /*!< GPIO PADREGA: PAD2INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGA_PAD2PULL_Pos (16UL) /*!< GPIO PADREGA: PAD2PULL (Bit 16) */ -#define GPIO_PADREGA_PAD2PULL_Msk (0x10000UL) /*!< GPIO PADREGA: PAD2PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGA_PAD1RSEL_Pos (14UL) /*!< GPIO PADREGA: PAD1RSEL (Bit 14) */ -#define GPIO_PADREGA_PAD1RSEL_Msk (0xc000UL) /*!< GPIO PADREGA: PAD1RSEL (Bitfield-Mask: 0x03) */ -#define GPIO_PADREGA_PAD1FNCSEL_Pos (11UL) /*!< GPIO PADREGA: PAD1FNCSEL (Bit 11) */ -#define GPIO_PADREGA_PAD1FNCSEL_Msk (0x3800UL) /*!< GPIO PADREGA: PAD1FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGA_PAD1STRNG_Pos (10UL) /*!< GPIO PADREGA: PAD1STRNG (Bit 10) */ -#define GPIO_PADREGA_PAD1STRNG_Msk (0x400UL) /*!< GPIO PADREGA: PAD1STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGA_PAD1INPEN_Pos (9UL) /*!< GPIO PADREGA: PAD1INPEN (Bit 9) */ -#define GPIO_PADREGA_PAD1INPEN_Msk (0x200UL) /*!< GPIO PADREGA: PAD1INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGA_PAD1PULL_Pos (8UL) /*!< GPIO PADREGA: PAD1PULL (Bit 8) */ -#define GPIO_PADREGA_PAD1PULL_Msk (0x100UL) /*!< GPIO PADREGA: PAD1PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGA_PAD0RSEL_Pos (6UL) /*!< GPIO PADREGA: PAD0RSEL (Bit 6) */ -#define GPIO_PADREGA_PAD0RSEL_Msk (0xc0UL) /*!< GPIO PADREGA: PAD0RSEL (Bitfield-Mask: 0x03) */ -#define GPIO_PADREGA_PAD0FNCSEL_Pos (3UL) /*!< GPIO PADREGA: PAD0FNCSEL (Bit 3) */ -#define GPIO_PADREGA_PAD0FNCSEL_Msk (0x38UL) /*!< GPIO PADREGA: PAD0FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGA_PAD0STRNG_Pos (2UL) /*!< GPIO PADREGA: PAD0STRNG (Bit 2) */ -#define GPIO_PADREGA_PAD0STRNG_Msk (0x4UL) /*!< GPIO PADREGA: PAD0STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGA_PAD0INPEN_Pos (1UL) /*!< GPIO PADREGA: PAD0INPEN (Bit 1) */ -#define GPIO_PADREGA_PAD0INPEN_Msk (0x2UL) /*!< GPIO PADREGA: PAD0INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGA_PAD0PULL_Pos (0UL) /*!< GPIO PADREGA: PAD0PULL (Bit 0) */ -#define GPIO_PADREGA_PAD0PULL_Msk (0x1UL) /*!< GPIO PADREGA: PAD0PULL (Bitfield-Mask: 0x01) */ -/* ======================================================== PADREGB ======================================================== */ -#define GPIO_PADREGB_PAD7FNCSEL_Pos (27UL) /*!< GPIO PADREGB: PAD7FNCSEL (Bit 27) */ -#define GPIO_PADREGB_PAD7FNCSEL_Msk (0x38000000UL) /*!< GPIO PADREGB: PAD7FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGB_PAD7STRNG_Pos (26UL) /*!< GPIO PADREGB: PAD7STRNG (Bit 26) */ -#define GPIO_PADREGB_PAD7STRNG_Msk (0x4000000UL) /*!< GPIO PADREGB: PAD7STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGB_PAD7INPEN_Pos (25UL) /*!< GPIO PADREGB: PAD7INPEN (Bit 25) */ -#define GPIO_PADREGB_PAD7INPEN_Msk (0x2000000UL) /*!< GPIO PADREGB: PAD7INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGB_PAD7PULL_Pos (24UL) /*!< GPIO PADREGB: PAD7PULL (Bit 24) */ -#define GPIO_PADREGB_PAD7PULL_Msk (0x1000000UL) /*!< GPIO PADREGB: PAD7PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGB_PAD6RSEL_Pos (22UL) /*!< GPIO PADREGB: PAD6RSEL (Bit 22) */ -#define GPIO_PADREGB_PAD6RSEL_Msk (0xc00000UL) /*!< GPIO PADREGB: PAD6RSEL (Bitfield-Mask: 0x03) */ -#define GPIO_PADREGB_PAD6FNCSEL_Pos (19UL) /*!< GPIO PADREGB: PAD6FNCSEL (Bit 19) */ -#define GPIO_PADREGB_PAD6FNCSEL_Msk (0x380000UL) /*!< GPIO PADREGB: PAD6FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGB_PAD6STRNG_Pos (18UL) /*!< GPIO PADREGB: PAD6STRNG (Bit 18) */ -#define GPIO_PADREGB_PAD6STRNG_Msk (0x40000UL) /*!< GPIO PADREGB: PAD6STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGB_PAD6INPEN_Pos (17UL) /*!< GPIO PADREGB: PAD6INPEN (Bit 17) */ -#define GPIO_PADREGB_PAD6INPEN_Msk (0x20000UL) /*!< GPIO PADREGB: PAD6INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGB_PAD6PULL_Pos (16UL) /*!< GPIO PADREGB: PAD6PULL (Bit 16) */ -#define GPIO_PADREGB_PAD6PULL_Msk (0x10000UL) /*!< GPIO PADREGB: PAD6PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGB_PAD5RSEL_Pos (14UL) /*!< GPIO PADREGB: PAD5RSEL (Bit 14) */ -#define GPIO_PADREGB_PAD5RSEL_Msk (0xc000UL) /*!< GPIO PADREGB: PAD5RSEL (Bitfield-Mask: 0x03) */ -#define GPIO_PADREGB_PAD5FNCSEL_Pos (11UL) /*!< GPIO PADREGB: PAD5FNCSEL (Bit 11) */ -#define GPIO_PADREGB_PAD5FNCSEL_Msk (0x3800UL) /*!< GPIO PADREGB: PAD5FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGB_PAD5STRNG_Pos (10UL) /*!< GPIO PADREGB: PAD5STRNG (Bit 10) */ -#define GPIO_PADREGB_PAD5STRNG_Msk (0x400UL) /*!< GPIO PADREGB: PAD5STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGB_PAD5INPEN_Pos (9UL) /*!< GPIO PADREGB: PAD5INPEN (Bit 9) */ -#define GPIO_PADREGB_PAD5INPEN_Msk (0x200UL) /*!< GPIO PADREGB: PAD5INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGB_PAD5PULL_Pos (8UL) /*!< GPIO PADREGB: PAD5PULL (Bit 8) */ -#define GPIO_PADREGB_PAD5PULL_Msk (0x100UL) /*!< GPIO PADREGB: PAD5PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGB_PAD4FNCSEL_Pos (3UL) /*!< GPIO PADREGB: PAD4FNCSEL (Bit 3) */ -#define GPIO_PADREGB_PAD4FNCSEL_Msk (0x38UL) /*!< GPIO PADREGB: PAD4FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGB_PAD4STRNG_Pos (2UL) /*!< GPIO PADREGB: PAD4STRNG (Bit 2) */ -#define GPIO_PADREGB_PAD4STRNG_Msk (0x4UL) /*!< GPIO PADREGB: PAD4STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGB_PAD4INPEN_Pos (1UL) /*!< GPIO PADREGB: PAD4INPEN (Bit 1) */ -#define GPIO_PADREGB_PAD4INPEN_Msk (0x2UL) /*!< GPIO PADREGB: PAD4INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGB_PAD4PULL_Pos (0UL) /*!< GPIO PADREGB: PAD4PULL (Bit 0) */ -#define GPIO_PADREGB_PAD4PULL_Msk (0x1UL) /*!< GPIO PADREGB: PAD4PULL (Bitfield-Mask: 0x01) */ -/* ======================================================== PADREGC ======================================================== */ -#define GPIO_PADREGC_PAD11FNCSEL_Pos (27UL) /*!< GPIO PADREGC: PAD11FNCSEL (Bit 27) */ -#define GPIO_PADREGC_PAD11FNCSEL_Msk (0x38000000UL) /*!< GPIO PADREGC: PAD11FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGC_PAD11STRNG_Pos (26UL) /*!< GPIO PADREGC: PAD11STRNG (Bit 26) */ -#define GPIO_PADREGC_PAD11STRNG_Msk (0x4000000UL) /*!< GPIO PADREGC: PAD11STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGC_PAD11INPEN_Pos (25UL) /*!< GPIO PADREGC: PAD11INPEN (Bit 25) */ -#define GPIO_PADREGC_PAD11INPEN_Msk (0x2000000UL) /*!< GPIO PADREGC: PAD11INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGC_PAD11PULL_Pos (24UL) /*!< GPIO PADREGC: PAD11PULL (Bit 24) */ -#define GPIO_PADREGC_PAD11PULL_Msk (0x1000000UL) /*!< GPIO PADREGC: PAD11PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGC_PAD10FNCSEL_Pos (19UL) /*!< GPIO PADREGC: PAD10FNCSEL (Bit 19) */ -#define GPIO_PADREGC_PAD10FNCSEL_Msk (0x380000UL) /*!< GPIO PADREGC: PAD10FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGC_PAD10STRNG_Pos (18UL) /*!< GPIO PADREGC: PAD10STRNG (Bit 18) */ -#define GPIO_PADREGC_PAD10STRNG_Msk (0x40000UL) /*!< GPIO PADREGC: PAD10STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGC_PAD10INPEN_Pos (17UL) /*!< GPIO PADREGC: PAD10INPEN (Bit 17) */ -#define GPIO_PADREGC_PAD10INPEN_Msk (0x20000UL) /*!< GPIO PADREGC: PAD10INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGC_PAD10PULL_Pos (16UL) /*!< GPIO PADREGC: PAD10PULL (Bit 16) */ -#define GPIO_PADREGC_PAD10PULL_Msk (0x10000UL) /*!< GPIO PADREGC: PAD10PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGC_PAD9RSEL_Pos (14UL) /*!< GPIO PADREGC: PAD9RSEL (Bit 14) */ -#define GPIO_PADREGC_PAD9RSEL_Msk (0xc000UL) /*!< GPIO PADREGC: PAD9RSEL (Bitfield-Mask: 0x03) */ -#define GPIO_PADREGC_PAD9FNCSEL_Pos (11UL) /*!< GPIO PADREGC: PAD9FNCSEL (Bit 11) */ -#define GPIO_PADREGC_PAD9FNCSEL_Msk (0x3800UL) /*!< GPIO PADREGC: PAD9FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGC_PAD9STRNG_Pos (10UL) /*!< GPIO PADREGC: PAD9STRNG (Bit 10) */ -#define GPIO_PADREGC_PAD9STRNG_Msk (0x400UL) /*!< GPIO PADREGC: PAD9STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGC_PAD9INPEN_Pos (9UL) /*!< GPIO PADREGC: PAD9INPEN (Bit 9) */ -#define GPIO_PADREGC_PAD9INPEN_Msk (0x200UL) /*!< GPIO PADREGC: PAD9INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGC_PAD9PULL_Pos (8UL) /*!< GPIO PADREGC: PAD9PULL (Bit 8) */ -#define GPIO_PADREGC_PAD9PULL_Msk (0x100UL) /*!< GPIO PADREGC: PAD9PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGC_PAD8RSEL_Pos (6UL) /*!< GPIO PADREGC: PAD8RSEL (Bit 6) */ -#define GPIO_PADREGC_PAD8RSEL_Msk (0xc0UL) /*!< GPIO PADREGC: PAD8RSEL (Bitfield-Mask: 0x03) */ -#define GPIO_PADREGC_PAD8FNCSEL_Pos (3UL) /*!< GPIO PADREGC: PAD8FNCSEL (Bit 3) */ -#define GPIO_PADREGC_PAD8FNCSEL_Msk (0x38UL) /*!< GPIO PADREGC: PAD8FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGC_PAD8STRNG_Pos (2UL) /*!< GPIO PADREGC: PAD8STRNG (Bit 2) */ -#define GPIO_PADREGC_PAD8STRNG_Msk (0x4UL) /*!< GPIO PADREGC: PAD8STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGC_PAD8INPEN_Pos (1UL) /*!< GPIO PADREGC: PAD8INPEN (Bit 1) */ -#define GPIO_PADREGC_PAD8INPEN_Msk (0x2UL) /*!< GPIO PADREGC: PAD8INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGC_PAD8PULL_Pos (0UL) /*!< GPIO PADREGC: PAD8PULL (Bit 0) */ -#define GPIO_PADREGC_PAD8PULL_Msk (0x1UL) /*!< GPIO PADREGC: PAD8PULL (Bitfield-Mask: 0x01) */ -/* ======================================================== PADREGD ======================================================== */ -#define GPIO_PADREGD_PAD15FNCSEL_Pos (27UL) /*!< GPIO PADREGD: PAD15FNCSEL (Bit 27) */ -#define GPIO_PADREGD_PAD15FNCSEL_Msk (0x38000000UL) /*!< GPIO PADREGD: PAD15FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGD_PAD15STRNG_Pos (26UL) /*!< GPIO PADREGD: PAD15STRNG (Bit 26) */ -#define GPIO_PADREGD_PAD15STRNG_Msk (0x4000000UL) /*!< GPIO PADREGD: PAD15STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGD_PAD15INPEN_Pos (25UL) /*!< GPIO PADREGD: PAD15INPEN (Bit 25) */ -#define GPIO_PADREGD_PAD15INPEN_Msk (0x2000000UL) /*!< GPIO PADREGD: PAD15INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGD_PAD15PULL_Pos (24UL) /*!< GPIO PADREGD: PAD15PULL (Bit 24) */ -#define GPIO_PADREGD_PAD15PULL_Msk (0x1000000UL) /*!< GPIO PADREGD: PAD15PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGD_PAD14FNCSEL_Pos (19UL) /*!< GPIO PADREGD: PAD14FNCSEL (Bit 19) */ -#define GPIO_PADREGD_PAD14FNCSEL_Msk (0x380000UL) /*!< GPIO PADREGD: PAD14FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGD_PAD14STRNG_Pos (18UL) /*!< GPIO PADREGD: PAD14STRNG (Bit 18) */ -#define GPIO_PADREGD_PAD14STRNG_Msk (0x40000UL) /*!< GPIO PADREGD: PAD14STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGD_PAD14INPEN_Pos (17UL) /*!< GPIO PADREGD: PAD14INPEN (Bit 17) */ -#define GPIO_PADREGD_PAD14INPEN_Msk (0x20000UL) /*!< GPIO PADREGD: PAD14INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGD_PAD14PULL_Pos (16UL) /*!< GPIO PADREGD: PAD14PULL (Bit 16) */ -#define GPIO_PADREGD_PAD14PULL_Msk (0x10000UL) /*!< GPIO PADREGD: PAD14PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGD_PAD13FNCSEL_Pos (11UL) /*!< GPIO PADREGD: PAD13FNCSEL (Bit 11) */ -#define GPIO_PADREGD_PAD13FNCSEL_Msk (0x3800UL) /*!< GPIO PADREGD: PAD13FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGD_PAD13STRNG_Pos (10UL) /*!< GPIO PADREGD: PAD13STRNG (Bit 10) */ -#define GPIO_PADREGD_PAD13STRNG_Msk (0x400UL) /*!< GPIO PADREGD: PAD13STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGD_PAD13INPEN_Pos (9UL) /*!< GPIO PADREGD: PAD13INPEN (Bit 9) */ -#define GPIO_PADREGD_PAD13INPEN_Msk (0x200UL) /*!< GPIO PADREGD: PAD13INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGD_PAD13PULL_Pos (8UL) /*!< GPIO PADREGD: PAD13PULL (Bit 8) */ -#define GPIO_PADREGD_PAD13PULL_Msk (0x100UL) /*!< GPIO PADREGD: PAD13PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGD_PAD12FNCSEL_Pos (3UL) /*!< GPIO PADREGD: PAD12FNCSEL (Bit 3) */ -#define GPIO_PADREGD_PAD12FNCSEL_Msk (0x38UL) /*!< GPIO PADREGD: PAD12FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGD_PAD12STRNG_Pos (2UL) /*!< GPIO PADREGD: PAD12STRNG (Bit 2) */ -#define GPIO_PADREGD_PAD12STRNG_Msk (0x4UL) /*!< GPIO PADREGD: PAD12STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGD_PAD12INPEN_Pos (1UL) /*!< GPIO PADREGD: PAD12INPEN (Bit 1) */ -#define GPIO_PADREGD_PAD12INPEN_Msk (0x2UL) /*!< GPIO PADREGD: PAD12INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGD_PAD12PULL_Pos (0UL) /*!< GPIO PADREGD: PAD12PULL (Bit 0) */ -#define GPIO_PADREGD_PAD12PULL_Msk (0x1UL) /*!< GPIO PADREGD: PAD12PULL (Bitfield-Mask: 0x01) */ -/* ======================================================== PADREGE ======================================================== */ -#define GPIO_PADREGE_PAD19FNCSEL_Pos (27UL) /*!< GPIO PADREGE: PAD19FNCSEL (Bit 27) */ -#define GPIO_PADREGE_PAD19FNCSEL_Msk (0x38000000UL) /*!< GPIO PADREGE: PAD19FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGE_PAD19STRNG_Pos (26UL) /*!< GPIO PADREGE: PAD19STRNG (Bit 26) */ -#define GPIO_PADREGE_PAD19STRNG_Msk (0x4000000UL) /*!< GPIO PADREGE: PAD19STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGE_PAD19INPEN_Pos (25UL) /*!< GPIO PADREGE: PAD19INPEN (Bit 25) */ -#define GPIO_PADREGE_PAD19INPEN_Msk (0x2000000UL) /*!< GPIO PADREGE: PAD19INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGE_PAD19PULL_Pos (24UL) /*!< GPIO PADREGE: PAD19PULL (Bit 24) */ -#define GPIO_PADREGE_PAD19PULL_Msk (0x1000000UL) /*!< GPIO PADREGE: PAD19PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGE_PAD18FNCSEL_Pos (19UL) /*!< GPIO PADREGE: PAD18FNCSEL (Bit 19) */ -#define GPIO_PADREGE_PAD18FNCSEL_Msk (0x380000UL) /*!< GPIO PADREGE: PAD18FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGE_PAD18STRNG_Pos (18UL) /*!< GPIO PADREGE: PAD18STRNG (Bit 18) */ -#define GPIO_PADREGE_PAD18STRNG_Msk (0x40000UL) /*!< GPIO PADREGE: PAD18STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGE_PAD18INPEN_Pos (17UL) /*!< GPIO PADREGE: PAD18INPEN (Bit 17) */ -#define GPIO_PADREGE_PAD18INPEN_Msk (0x20000UL) /*!< GPIO PADREGE: PAD18INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGE_PAD18PULL_Pos (16UL) /*!< GPIO PADREGE: PAD18PULL (Bit 16) */ -#define GPIO_PADREGE_PAD18PULL_Msk (0x10000UL) /*!< GPIO PADREGE: PAD18PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGE_PAD17FNCSEL_Pos (11UL) /*!< GPIO PADREGE: PAD17FNCSEL (Bit 11) */ -#define GPIO_PADREGE_PAD17FNCSEL_Msk (0x3800UL) /*!< GPIO PADREGE: PAD17FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGE_PAD17STRNG_Pos (10UL) /*!< GPIO PADREGE: PAD17STRNG (Bit 10) */ -#define GPIO_PADREGE_PAD17STRNG_Msk (0x400UL) /*!< GPIO PADREGE: PAD17STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGE_PAD17INPEN_Pos (9UL) /*!< GPIO PADREGE: PAD17INPEN (Bit 9) */ -#define GPIO_PADREGE_PAD17INPEN_Msk (0x200UL) /*!< GPIO PADREGE: PAD17INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGE_PAD17PULL_Pos (8UL) /*!< GPIO PADREGE: PAD17PULL (Bit 8) */ -#define GPIO_PADREGE_PAD17PULL_Msk (0x100UL) /*!< GPIO PADREGE: PAD17PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGE_PAD16FNCSEL_Pos (3UL) /*!< GPIO PADREGE: PAD16FNCSEL (Bit 3) */ -#define GPIO_PADREGE_PAD16FNCSEL_Msk (0x38UL) /*!< GPIO PADREGE: PAD16FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGE_PAD16STRNG_Pos (2UL) /*!< GPIO PADREGE: PAD16STRNG (Bit 2) */ -#define GPIO_PADREGE_PAD16STRNG_Msk (0x4UL) /*!< GPIO PADREGE: PAD16STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGE_PAD16INPEN_Pos (1UL) /*!< GPIO PADREGE: PAD16INPEN (Bit 1) */ -#define GPIO_PADREGE_PAD16INPEN_Msk (0x2UL) /*!< GPIO PADREGE: PAD16INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGE_PAD16PULL_Pos (0UL) /*!< GPIO PADREGE: PAD16PULL (Bit 0) */ -#define GPIO_PADREGE_PAD16PULL_Msk (0x1UL) /*!< GPIO PADREGE: PAD16PULL (Bitfield-Mask: 0x01) */ -/* ======================================================== PADREGF ======================================================== */ -#define GPIO_PADREGF_PAD23FNCSEL_Pos (27UL) /*!< GPIO PADREGF: PAD23FNCSEL (Bit 27) */ -#define GPIO_PADREGF_PAD23FNCSEL_Msk (0x38000000UL) /*!< GPIO PADREGF: PAD23FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGF_PAD23STRNG_Pos (26UL) /*!< GPIO PADREGF: PAD23STRNG (Bit 26) */ -#define GPIO_PADREGF_PAD23STRNG_Msk (0x4000000UL) /*!< GPIO PADREGF: PAD23STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGF_PAD23INPEN_Pos (25UL) /*!< GPIO PADREGF: PAD23INPEN (Bit 25) */ -#define GPIO_PADREGF_PAD23INPEN_Msk (0x2000000UL) /*!< GPIO PADREGF: PAD23INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGF_PAD23PULL_Pos (24UL) /*!< GPIO PADREGF: PAD23PULL (Bit 24) */ -#define GPIO_PADREGF_PAD23PULL_Msk (0x1000000UL) /*!< GPIO PADREGF: PAD23PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGF_PAD22FNCSEL_Pos (19UL) /*!< GPIO PADREGF: PAD22FNCSEL (Bit 19) */ -#define GPIO_PADREGF_PAD22FNCSEL_Msk (0x380000UL) /*!< GPIO PADREGF: PAD22FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGF_PAD22STRNG_Pos (18UL) /*!< GPIO PADREGF: PAD22STRNG (Bit 18) */ -#define GPIO_PADREGF_PAD22STRNG_Msk (0x40000UL) /*!< GPIO PADREGF: PAD22STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGF_PAD22INPEN_Pos (17UL) /*!< GPIO PADREGF: PAD22INPEN (Bit 17) */ -#define GPIO_PADREGF_PAD22INPEN_Msk (0x20000UL) /*!< GPIO PADREGF: PAD22INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGF_PAD22PULL_Pos (16UL) /*!< GPIO PADREGF: PAD22PULL (Bit 16) */ -#define GPIO_PADREGF_PAD22PULL_Msk (0x10000UL) /*!< GPIO PADREGF: PAD22PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGF_PAD21FNCSEL_Pos (11UL) /*!< GPIO PADREGF: PAD21FNCSEL (Bit 11) */ -#define GPIO_PADREGF_PAD21FNCSEL_Msk (0x3800UL) /*!< GPIO PADREGF: PAD21FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGF_PAD21STRNG_Pos (10UL) /*!< GPIO PADREGF: PAD21STRNG (Bit 10) */ -#define GPIO_PADREGF_PAD21STRNG_Msk (0x400UL) /*!< GPIO PADREGF: PAD21STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGF_PAD21INPEN_Pos (9UL) /*!< GPIO PADREGF: PAD21INPEN (Bit 9) */ -#define GPIO_PADREGF_PAD21INPEN_Msk (0x200UL) /*!< GPIO PADREGF: PAD21INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGF_PAD21PULL_Pos (8UL) /*!< GPIO PADREGF: PAD21PULL (Bit 8) */ -#define GPIO_PADREGF_PAD21PULL_Msk (0x100UL) /*!< GPIO PADREGF: PAD21PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGF_PAD20FNCSEL_Pos (3UL) /*!< GPIO PADREGF: PAD20FNCSEL (Bit 3) */ -#define GPIO_PADREGF_PAD20FNCSEL_Msk (0x38UL) /*!< GPIO PADREGF: PAD20FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGF_PAD20STRNG_Pos (2UL) /*!< GPIO PADREGF: PAD20STRNG (Bit 2) */ -#define GPIO_PADREGF_PAD20STRNG_Msk (0x4UL) /*!< GPIO PADREGF: PAD20STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGF_PAD20INPEN_Pos (1UL) /*!< GPIO PADREGF: PAD20INPEN (Bit 1) */ -#define GPIO_PADREGF_PAD20INPEN_Msk (0x2UL) /*!< GPIO PADREGF: PAD20INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGF_PAD20PULL_Pos (0UL) /*!< GPIO PADREGF: PAD20PULL (Bit 0) */ -#define GPIO_PADREGF_PAD20PULL_Msk (0x1UL) /*!< GPIO PADREGF: PAD20PULL (Bitfield-Mask: 0x01) */ -/* ======================================================== PADREGG ======================================================== */ -#define GPIO_PADREGG_PAD27RSEL_Pos (30UL) /*!< GPIO PADREGG: PAD27RSEL (Bit 30) */ -#define GPIO_PADREGG_PAD27RSEL_Msk (0xc0000000UL) /*!< GPIO PADREGG: PAD27RSEL (Bitfield-Mask: 0x03) */ -#define GPIO_PADREGG_PAD27FNCSEL_Pos (27UL) /*!< GPIO PADREGG: PAD27FNCSEL (Bit 27) */ -#define GPIO_PADREGG_PAD27FNCSEL_Msk (0x38000000UL) /*!< GPIO PADREGG: PAD27FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGG_PAD27STRNG_Pos (26UL) /*!< GPIO PADREGG: PAD27STRNG (Bit 26) */ -#define GPIO_PADREGG_PAD27STRNG_Msk (0x4000000UL) /*!< GPIO PADREGG: PAD27STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGG_PAD27INPEN_Pos (25UL) /*!< GPIO PADREGG: PAD27INPEN (Bit 25) */ -#define GPIO_PADREGG_PAD27INPEN_Msk (0x2000000UL) /*!< GPIO PADREGG: PAD27INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGG_PAD27PULL_Pos (24UL) /*!< GPIO PADREGG: PAD27PULL (Bit 24) */ -#define GPIO_PADREGG_PAD27PULL_Msk (0x1000000UL) /*!< GPIO PADREGG: PAD27PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGG_PAD26FNCSEL_Pos (19UL) /*!< GPIO PADREGG: PAD26FNCSEL (Bit 19) */ -#define GPIO_PADREGG_PAD26FNCSEL_Msk (0x380000UL) /*!< GPIO PADREGG: PAD26FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGG_PAD26STRNG_Pos (18UL) /*!< GPIO PADREGG: PAD26STRNG (Bit 18) */ -#define GPIO_PADREGG_PAD26STRNG_Msk (0x40000UL) /*!< GPIO PADREGG: PAD26STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGG_PAD26INPEN_Pos (17UL) /*!< GPIO PADREGG: PAD26INPEN (Bit 17) */ -#define GPIO_PADREGG_PAD26INPEN_Msk (0x20000UL) /*!< GPIO PADREGG: PAD26INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGG_PAD26PULL_Pos (16UL) /*!< GPIO PADREGG: PAD26PULL (Bit 16) */ -#define GPIO_PADREGG_PAD26PULL_Msk (0x10000UL) /*!< GPIO PADREGG: PAD26PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGG_PAD25RSEL_Pos (14UL) /*!< GPIO PADREGG: PAD25RSEL (Bit 14) */ -#define GPIO_PADREGG_PAD25RSEL_Msk (0xc000UL) /*!< GPIO PADREGG: PAD25RSEL (Bitfield-Mask: 0x03) */ -#define GPIO_PADREGG_PAD25FNCSEL_Pos (11UL) /*!< GPIO PADREGG: PAD25FNCSEL (Bit 11) */ -#define GPIO_PADREGG_PAD25FNCSEL_Msk (0x3800UL) /*!< GPIO PADREGG: PAD25FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGG_PAD25STRNG_Pos (10UL) /*!< GPIO PADREGG: PAD25STRNG (Bit 10) */ -#define GPIO_PADREGG_PAD25STRNG_Msk (0x400UL) /*!< GPIO PADREGG: PAD25STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGG_PAD25INPEN_Pos (9UL) /*!< GPIO PADREGG: PAD25INPEN (Bit 9) */ -#define GPIO_PADREGG_PAD25INPEN_Msk (0x200UL) /*!< GPIO PADREGG: PAD25INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGG_PAD25PULL_Pos (8UL) /*!< GPIO PADREGG: PAD25PULL (Bit 8) */ -#define GPIO_PADREGG_PAD25PULL_Msk (0x100UL) /*!< GPIO PADREGG: PAD25PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGG_PAD24FNCSEL_Pos (3UL) /*!< GPIO PADREGG: PAD24FNCSEL (Bit 3) */ -#define GPIO_PADREGG_PAD24FNCSEL_Msk (0x38UL) /*!< GPIO PADREGG: PAD24FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGG_PAD24STRNG_Pos (2UL) /*!< GPIO PADREGG: PAD24STRNG (Bit 2) */ -#define GPIO_PADREGG_PAD24STRNG_Msk (0x4UL) /*!< GPIO PADREGG: PAD24STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGG_PAD24INPEN_Pos (1UL) /*!< GPIO PADREGG: PAD24INPEN (Bit 1) */ -#define GPIO_PADREGG_PAD24INPEN_Msk (0x2UL) /*!< GPIO PADREGG: PAD24INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGG_PAD24PULL_Pos (0UL) /*!< GPIO PADREGG: PAD24PULL (Bit 0) */ -#define GPIO_PADREGG_PAD24PULL_Msk (0x1UL) /*!< GPIO PADREGG: PAD24PULL (Bitfield-Mask: 0x01) */ -/* ======================================================== PADREGH ======================================================== */ -#define GPIO_PADREGH_PAD31FNCSEL_Pos (27UL) /*!< GPIO PADREGH: PAD31FNCSEL (Bit 27) */ -#define GPIO_PADREGH_PAD31FNCSEL_Msk (0x38000000UL) /*!< GPIO PADREGH: PAD31FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGH_PAD31STRNG_Pos (26UL) /*!< GPIO PADREGH: PAD31STRNG (Bit 26) */ -#define GPIO_PADREGH_PAD31STRNG_Msk (0x4000000UL) /*!< GPIO PADREGH: PAD31STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGH_PAD31INPEN_Pos (25UL) /*!< GPIO PADREGH: PAD31INPEN (Bit 25) */ -#define GPIO_PADREGH_PAD31INPEN_Msk (0x2000000UL) /*!< GPIO PADREGH: PAD31INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGH_PAD31PULL_Pos (24UL) /*!< GPIO PADREGH: PAD31PULL (Bit 24) */ -#define GPIO_PADREGH_PAD31PULL_Msk (0x1000000UL) /*!< GPIO PADREGH: PAD31PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGH_PAD30FNCSEL_Pos (19UL) /*!< GPIO PADREGH: PAD30FNCSEL (Bit 19) */ -#define GPIO_PADREGH_PAD30FNCSEL_Msk (0x380000UL) /*!< GPIO PADREGH: PAD30FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGH_PAD30STRNG_Pos (18UL) /*!< GPIO PADREGH: PAD30STRNG (Bit 18) */ -#define GPIO_PADREGH_PAD30STRNG_Msk (0x40000UL) /*!< GPIO PADREGH: PAD30STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGH_PAD30INPEN_Pos (17UL) /*!< GPIO PADREGH: PAD30INPEN (Bit 17) */ -#define GPIO_PADREGH_PAD30INPEN_Msk (0x20000UL) /*!< GPIO PADREGH: PAD30INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGH_PAD30PULL_Pos (16UL) /*!< GPIO PADREGH: PAD30PULL (Bit 16) */ -#define GPIO_PADREGH_PAD30PULL_Msk (0x10000UL) /*!< GPIO PADREGH: PAD30PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGH_PAD29FNCSEL_Pos (11UL) /*!< GPIO PADREGH: PAD29FNCSEL (Bit 11) */ -#define GPIO_PADREGH_PAD29FNCSEL_Msk (0x3800UL) /*!< GPIO PADREGH: PAD29FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGH_PAD29STRNG_Pos (10UL) /*!< GPIO PADREGH: PAD29STRNG (Bit 10) */ -#define GPIO_PADREGH_PAD29STRNG_Msk (0x400UL) /*!< GPIO PADREGH: PAD29STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGH_PAD29INPEN_Pos (9UL) /*!< GPIO PADREGH: PAD29INPEN (Bit 9) */ -#define GPIO_PADREGH_PAD29INPEN_Msk (0x200UL) /*!< GPIO PADREGH: PAD29INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGH_PAD29PULL_Pos (8UL) /*!< GPIO PADREGH: PAD29PULL (Bit 8) */ -#define GPIO_PADREGH_PAD29PULL_Msk (0x100UL) /*!< GPIO PADREGH: PAD29PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGH_PAD28FNCSEL_Pos (3UL) /*!< GPIO PADREGH: PAD28FNCSEL (Bit 3) */ -#define GPIO_PADREGH_PAD28FNCSEL_Msk (0x38UL) /*!< GPIO PADREGH: PAD28FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGH_PAD28STRNG_Pos (2UL) /*!< GPIO PADREGH: PAD28STRNG (Bit 2) */ -#define GPIO_PADREGH_PAD28STRNG_Msk (0x4UL) /*!< GPIO PADREGH: PAD28STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGH_PAD28INPEN_Pos (1UL) /*!< GPIO PADREGH: PAD28INPEN (Bit 1) */ -#define GPIO_PADREGH_PAD28INPEN_Msk (0x2UL) /*!< GPIO PADREGH: PAD28INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGH_PAD28PULL_Pos (0UL) /*!< GPIO PADREGH: PAD28PULL (Bit 0) */ -#define GPIO_PADREGH_PAD28PULL_Msk (0x1UL) /*!< GPIO PADREGH: PAD28PULL (Bitfield-Mask: 0x01) */ -/* ======================================================== PADREGI ======================================================== */ -#define GPIO_PADREGI_PAD35FNCSEL_Pos (27UL) /*!< GPIO PADREGI: PAD35FNCSEL (Bit 27) */ -#define GPIO_PADREGI_PAD35FNCSEL_Msk (0x38000000UL) /*!< GPIO PADREGI: PAD35FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGI_PAD35STRNG_Pos (26UL) /*!< GPIO PADREGI: PAD35STRNG (Bit 26) */ -#define GPIO_PADREGI_PAD35STRNG_Msk (0x4000000UL) /*!< GPIO PADREGI: PAD35STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGI_PAD35INPEN_Pos (25UL) /*!< GPIO PADREGI: PAD35INPEN (Bit 25) */ -#define GPIO_PADREGI_PAD35INPEN_Msk (0x2000000UL) /*!< GPIO PADREGI: PAD35INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGI_PAD35PULL_Pos (24UL) /*!< GPIO PADREGI: PAD35PULL (Bit 24) */ -#define GPIO_PADREGI_PAD35PULL_Msk (0x1000000UL) /*!< GPIO PADREGI: PAD35PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGI_PAD34FNCSEL_Pos (19UL) /*!< GPIO PADREGI: PAD34FNCSEL (Bit 19) */ -#define GPIO_PADREGI_PAD34FNCSEL_Msk (0x380000UL) /*!< GPIO PADREGI: PAD34FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGI_PAD34STRNG_Pos (18UL) /*!< GPIO PADREGI: PAD34STRNG (Bit 18) */ -#define GPIO_PADREGI_PAD34STRNG_Msk (0x40000UL) /*!< GPIO PADREGI: PAD34STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGI_PAD34INPEN_Pos (17UL) /*!< GPIO PADREGI: PAD34INPEN (Bit 17) */ -#define GPIO_PADREGI_PAD34INPEN_Msk (0x20000UL) /*!< GPIO PADREGI: PAD34INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGI_PAD34PULL_Pos (16UL) /*!< GPIO PADREGI: PAD34PULL (Bit 16) */ -#define GPIO_PADREGI_PAD34PULL_Msk (0x10000UL) /*!< GPIO PADREGI: PAD34PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGI_PAD33FNCSEL_Pos (11UL) /*!< GPIO PADREGI: PAD33FNCSEL (Bit 11) */ -#define GPIO_PADREGI_PAD33FNCSEL_Msk (0x3800UL) /*!< GPIO PADREGI: PAD33FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGI_PAD33STRNG_Pos (10UL) /*!< GPIO PADREGI: PAD33STRNG (Bit 10) */ -#define GPIO_PADREGI_PAD33STRNG_Msk (0x400UL) /*!< GPIO PADREGI: PAD33STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGI_PAD33INPEN_Pos (9UL) /*!< GPIO PADREGI: PAD33INPEN (Bit 9) */ -#define GPIO_PADREGI_PAD33INPEN_Msk (0x200UL) /*!< GPIO PADREGI: PAD33INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGI_PAD33PULL_Pos (8UL) /*!< GPIO PADREGI: PAD33PULL (Bit 8) */ -#define GPIO_PADREGI_PAD33PULL_Msk (0x100UL) /*!< GPIO PADREGI: PAD33PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGI_PAD32FNCSEL_Pos (3UL) /*!< GPIO PADREGI: PAD32FNCSEL (Bit 3) */ -#define GPIO_PADREGI_PAD32FNCSEL_Msk (0x38UL) /*!< GPIO PADREGI: PAD32FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGI_PAD32STRNG_Pos (2UL) /*!< GPIO PADREGI: PAD32STRNG (Bit 2) */ -#define GPIO_PADREGI_PAD32STRNG_Msk (0x4UL) /*!< GPIO PADREGI: PAD32STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGI_PAD32INPEN_Pos (1UL) /*!< GPIO PADREGI: PAD32INPEN (Bit 1) */ -#define GPIO_PADREGI_PAD32INPEN_Msk (0x2UL) /*!< GPIO PADREGI: PAD32INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGI_PAD32PULL_Pos (0UL) /*!< GPIO PADREGI: PAD32PULL (Bit 0) */ -#define GPIO_PADREGI_PAD32PULL_Msk (0x1UL) /*!< GPIO PADREGI: PAD32PULL (Bitfield-Mask: 0x01) */ -/* ======================================================== PADREGJ ======================================================== */ -#define GPIO_PADREGJ_PAD39RSEL_Pos (30UL) /*!< GPIO PADREGJ: PAD39RSEL (Bit 30) */ -#define GPIO_PADREGJ_PAD39RSEL_Msk (0xc0000000UL) /*!< GPIO PADREGJ: PAD39RSEL (Bitfield-Mask: 0x03) */ -#define GPIO_PADREGJ_PAD39FNCSEL_Pos (27UL) /*!< GPIO PADREGJ: PAD39FNCSEL (Bit 27) */ -#define GPIO_PADREGJ_PAD39FNCSEL_Msk (0x38000000UL) /*!< GPIO PADREGJ: PAD39FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGJ_PAD39STRNG_Pos (26UL) /*!< GPIO PADREGJ: PAD39STRNG (Bit 26) */ -#define GPIO_PADREGJ_PAD39STRNG_Msk (0x4000000UL) /*!< GPIO PADREGJ: PAD39STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGJ_PAD39INPEN_Pos (25UL) /*!< GPIO PADREGJ: PAD39INPEN (Bit 25) */ -#define GPIO_PADREGJ_PAD39INPEN_Msk (0x2000000UL) /*!< GPIO PADREGJ: PAD39INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGJ_PAD39PULL_Pos (24UL) /*!< GPIO PADREGJ: PAD39PULL (Bit 24) */ -#define GPIO_PADREGJ_PAD39PULL_Msk (0x1000000UL) /*!< GPIO PADREGJ: PAD39PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGJ_PAD38FNCSEL_Pos (19UL) /*!< GPIO PADREGJ: PAD38FNCSEL (Bit 19) */ -#define GPIO_PADREGJ_PAD38FNCSEL_Msk (0x380000UL) /*!< GPIO PADREGJ: PAD38FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGJ_PAD38STRNG_Pos (18UL) /*!< GPIO PADREGJ: PAD38STRNG (Bit 18) */ -#define GPIO_PADREGJ_PAD38STRNG_Msk (0x40000UL) /*!< GPIO PADREGJ: PAD38STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGJ_PAD38INPEN_Pos (17UL) /*!< GPIO PADREGJ: PAD38INPEN (Bit 17) */ -#define GPIO_PADREGJ_PAD38INPEN_Msk (0x20000UL) /*!< GPIO PADREGJ: PAD38INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGJ_PAD38PULL_Pos (16UL) /*!< GPIO PADREGJ: PAD38PULL (Bit 16) */ -#define GPIO_PADREGJ_PAD38PULL_Msk (0x10000UL) /*!< GPIO PADREGJ: PAD38PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGJ_PAD37PWRDN_Pos (15UL) /*!< GPIO PADREGJ: PAD37PWRDN (Bit 15) */ -#define GPIO_PADREGJ_PAD37PWRDN_Msk (0x8000UL) /*!< GPIO PADREGJ: PAD37PWRDN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGJ_PAD37FNCSEL_Pos (11UL) /*!< GPIO PADREGJ: PAD37FNCSEL (Bit 11) */ -#define GPIO_PADREGJ_PAD37FNCSEL_Msk (0x3800UL) /*!< GPIO PADREGJ: PAD37FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGJ_PAD37STRNG_Pos (10UL) /*!< GPIO PADREGJ: PAD37STRNG (Bit 10) */ -#define GPIO_PADREGJ_PAD37STRNG_Msk (0x400UL) /*!< GPIO PADREGJ: PAD37STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGJ_PAD37INPEN_Pos (9UL) /*!< GPIO PADREGJ: PAD37INPEN (Bit 9) */ -#define GPIO_PADREGJ_PAD37INPEN_Msk (0x200UL) /*!< GPIO PADREGJ: PAD37INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGJ_PAD37PULL_Pos (8UL) /*!< GPIO PADREGJ: PAD37PULL (Bit 8) */ -#define GPIO_PADREGJ_PAD37PULL_Msk (0x100UL) /*!< GPIO PADREGJ: PAD37PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGJ_PAD36PWRUP_Pos (6UL) /*!< GPIO PADREGJ: PAD36PWRUP (Bit 6) */ -#define GPIO_PADREGJ_PAD36PWRUP_Msk (0x40UL) /*!< GPIO PADREGJ: PAD36PWRUP (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGJ_PAD36FNCSEL_Pos (3UL) /*!< GPIO PADREGJ: PAD36FNCSEL (Bit 3) */ -#define GPIO_PADREGJ_PAD36FNCSEL_Msk (0x38UL) /*!< GPIO PADREGJ: PAD36FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGJ_PAD36STRNG_Pos (2UL) /*!< GPIO PADREGJ: PAD36STRNG (Bit 2) */ -#define GPIO_PADREGJ_PAD36STRNG_Msk (0x4UL) /*!< GPIO PADREGJ: PAD36STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGJ_PAD36INPEN_Pos (1UL) /*!< GPIO PADREGJ: PAD36INPEN (Bit 1) */ -#define GPIO_PADREGJ_PAD36INPEN_Msk (0x2UL) /*!< GPIO PADREGJ: PAD36INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGJ_PAD36PULL_Pos (0UL) /*!< GPIO PADREGJ: PAD36PULL (Bit 0) */ -#define GPIO_PADREGJ_PAD36PULL_Msk (0x1UL) /*!< GPIO PADREGJ: PAD36PULL (Bitfield-Mask: 0x01) */ -/* ======================================================== PADREGK ======================================================== */ -#define GPIO_PADREGK_PAD43RSEL_Pos (30UL) /*!< GPIO PADREGK: PAD43RSEL (Bit 30) */ -#define GPIO_PADREGK_PAD43RSEL_Msk (0xc0000000UL) /*!< GPIO PADREGK: PAD43RSEL (Bitfield-Mask: 0x03) */ -#define GPIO_PADREGK_PAD43FNCSEL_Pos (27UL) /*!< GPIO PADREGK: PAD43FNCSEL (Bit 27) */ -#define GPIO_PADREGK_PAD43FNCSEL_Msk (0x38000000UL) /*!< GPIO PADREGK: PAD43FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGK_PAD43STRNG_Pos (26UL) /*!< GPIO PADREGK: PAD43STRNG (Bit 26) */ -#define GPIO_PADREGK_PAD43STRNG_Msk (0x4000000UL) /*!< GPIO PADREGK: PAD43STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGK_PAD43INPEN_Pos (25UL) /*!< GPIO PADREGK: PAD43INPEN (Bit 25) */ -#define GPIO_PADREGK_PAD43INPEN_Msk (0x2000000UL) /*!< GPIO PADREGK: PAD43INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGK_PAD43PULL_Pos (24UL) /*!< GPIO PADREGK: PAD43PULL (Bit 24) */ -#define GPIO_PADREGK_PAD43PULL_Msk (0x1000000UL) /*!< GPIO PADREGK: PAD43PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGK_PAD42RSEL_Pos (22UL) /*!< GPIO PADREGK: PAD42RSEL (Bit 22) */ -#define GPIO_PADREGK_PAD42RSEL_Msk (0xc00000UL) /*!< GPIO PADREGK: PAD42RSEL (Bitfield-Mask: 0x03) */ -#define GPIO_PADREGK_PAD42FNCSEL_Pos (19UL) /*!< GPIO PADREGK: PAD42FNCSEL (Bit 19) */ -#define GPIO_PADREGK_PAD42FNCSEL_Msk (0x380000UL) /*!< GPIO PADREGK: PAD42FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGK_PAD42STRNG_Pos (18UL) /*!< GPIO PADREGK: PAD42STRNG (Bit 18) */ -#define GPIO_PADREGK_PAD42STRNG_Msk (0x40000UL) /*!< GPIO PADREGK: PAD42STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGK_PAD42INPEN_Pos (17UL) /*!< GPIO PADREGK: PAD42INPEN (Bit 17) */ -#define GPIO_PADREGK_PAD42INPEN_Msk (0x20000UL) /*!< GPIO PADREGK: PAD42INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGK_PAD42PULL_Pos (16UL) /*!< GPIO PADREGK: PAD42PULL (Bit 16) */ -#define GPIO_PADREGK_PAD42PULL_Msk (0x10000UL) /*!< GPIO PADREGK: PAD42PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGK_PAD41PWRDN_Pos (15UL) /*!< GPIO PADREGK: PAD41PWRDN (Bit 15) */ -#define GPIO_PADREGK_PAD41PWRDN_Msk (0x8000UL) /*!< GPIO PADREGK: PAD41PWRDN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGK_PAD41FNCSEL_Pos (11UL) /*!< GPIO PADREGK: PAD41FNCSEL (Bit 11) */ -#define GPIO_PADREGK_PAD41FNCSEL_Msk (0x3800UL) /*!< GPIO PADREGK: PAD41FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGK_PAD41STRNG_Pos (10UL) /*!< GPIO PADREGK: PAD41STRNG (Bit 10) */ -#define GPIO_PADREGK_PAD41STRNG_Msk (0x400UL) /*!< GPIO PADREGK: PAD41STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGK_PAD41INPEN_Pos (9UL) /*!< GPIO PADREGK: PAD41INPEN (Bit 9) */ -#define GPIO_PADREGK_PAD41INPEN_Msk (0x200UL) /*!< GPIO PADREGK: PAD41INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGK_PAD41PULL_Pos (8UL) /*!< GPIO PADREGK: PAD41PULL (Bit 8) */ -#define GPIO_PADREGK_PAD41PULL_Msk (0x100UL) /*!< GPIO PADREGK: PAD41PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGK_PAD40RSEL_Pos (6UL) /*!< GPIO PADREGK: PAD40RSEL (Bit 6) */ -#define GPIO_PADREGK_PAD40RSEL_Msk (0xc0UL) /*!< GPIO PADREGK: PAD40RSEL (Bitfield-Mask: 0x03) */ -#define GPIO_PADREGK_PAD40FNCSEL_Pos (3UL) /*!< GPIO PADREGK: PAD40FNCSEL (Bit 3) */ -#define GPIO_PADREGK_PAD40FNCSEL_Msk (0x38UL) /*!< GPIO PADREGK: PAD40FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGK_PAD40STRNG_Pos (2UL) /*!< GPIO PADREGK: PAD40STRNG (Bit 2) */ -#define GPIO_PADREGK_PAD40STRNG_Msk (0x4UL) /*!< GPIO PADREGK: PAD40STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGK_PAD40INPEN_Pos (1UL) /*!< GPIO PADREGK: PAD40INPEN (Bit 1) */ -#define GPIO_PADREGK_PAD40INPEN_Msk (0x2UL) /*!< GPIO PADREGK: PAD40INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGK_PAD40PULL_Pos (0UL) /*!< GPIO PADREGK: PAD40PULL (Bit 0) */ -#define GPIO_PADREGK_PAD40PULL_Msk (0x1UL) /*!< GPIO PADREGK: PAD40PULL (Bitfield-Mask: 0x01) */ -/* ======================================================== PADREGL ======================================================== */ -#define GPIO_PADREGL_PAD47FNCSEL_Pos (27UL) /*!< GPIO PADREGL: PAD47FNCSEL (Bit 27) */ -#define GPIO_PADREGL_PAD47FNCSEL_Msk (0x38000000UL) /*!< GPIO PADREGL: PAD47FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGL_PAD47STRNG_Pos (26UL) /*!< GPIO PADREGL: PAD47STRNG (Bit 26) */ -#define GPIO_PADREGL_PAD47STRNG_Msk (0x4000000UL) /*!< GPIO PADREGL: PAD47STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGL_PAD47INPEN_Pos (25UL) /*!< GPIO PADREGL: PAD47INPEN (Bit 25) */ -#define GPIO_PADREGL_PAD47INPEN_Msk (0x2000000UL) /*!< GPIO PADREGL: PAD47INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGL_PAD47PULL_Pos (24UL) /*!< GPIO PADREGL: PAD47PULL (Bit 24) */ -#define GPIO_PADREGL_PAD47PULL_Msk (0x1000000UL) /*!< GPIO PADREGL: PAD47PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGL_PAD46FNCSEL_Pos (19UL) /*!< GPIO PADREGL: PAD46FNCSEL (Bit 19) */ -#define GPIO_PADREGL_PAD46FNCSEL_Msk (0x380000UL) /*!< GPIO PADREGL: PAD46FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGL_PAD46STRNG_Pos (18UL) /*!< GPIO PADREGL: PAD46STRNG (Bit 18) */ -#define GPIO_PADREGL_PAD46STRNG_Msk (0x40000UL) /*!< GPIO PADREGL: PAD46STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGL_PAD46INPEN_Pos (17UL) /*!< GPIO PADREGL: PAD46INPEN (Bit 17) */ -#define GPIO_PADREGL_PAD46INPEN_Msk (0x20000UL) /*!< GPIO PADREGL: PAD46INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGL_PAD46PULL_Pos (16UL) /*!< GPIO PADREGL: PAD46PULL (Bit 16) */ -#define GPIO_PADREGL_PAD46PULL_Msk (0x10000UL) /*!< GPIO PADREGL: PAD46PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGL_PAD45FNCSEL_Pos (11UL) /*!< GPIO PADREGL: PAD45FNCSEL (Bit 11) */ -#define GPIO_PADREGL_PAD45FNCSEL_Msk (0x3800UL) /*!< GPIO PADREGL: PAD45FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGL_PAD45STRNG_Pos (10UL) /*!< GPIO PADREGL: PAD45STRNG (Bit 10) */ -#define GPIO_PADREGL_PAD45STRNG_Msk (0x400UL) /*!< GPIO PADREGL: PAD45STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGL_PAD45INPEN_Pos (9UL) /*!< GPIO PADREGL: PAD45INPEN (Bit 9) */ -#define GPIO_PADREGL_PAD45INPEN_Msk (0x200UL) /*!< GPIO PADREGL: PAD45INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGL_PAD45PULL_Pos (8UL) /*!< GPIO PADREGL: PAD45PULL (Bit 8) */ -#define GPIO_PADREGL_PAD45PULL_Msk (0x100UL) /*!< GPIO PADREGL: PAD45PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGL_PAD44FNCSEL_Pos (3UL) /*!< GPIO PADREGL: PAD44FNCSEL (Bit 3) */ -#define GPIO_PADREGL_PAD44FNCSEL_Msk (0x38UL) /*!< GPIO PADREGL: PAD44FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGL_PAD44STRNG_Pos (2UL) /*!< GPIO PADREGL: PAD44STRNG (Bit 2) */ -#define GPIO_PADREGL_PAD44STRNG_Msk (0x4UL) /*!< GPIO PADREGL: PAD44STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGL_PAD44INPEN_Pos (1UL) /*!< GPIO PADREGL: PAD44INPEN (Bit 1) */ -#define GPIO_PADREGL_PAD44INPEN_Msk (0x2UL) /*!< GPIO PADREGL: PAD44INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGL_PAD44PULL_Pos (0UL) /*!< GPIO PADREGL: PAD44PULL (Bit 0) */ -#define GPIO_PADREGL_PAD44PULL_Msk (0x1UL) /*!< GPIO PADREGL: PAD44PULL (Bitfield-Mask: 0x01) */ -/* ======================================================== PADREGM ======================================================== */ -#define GPIO_PADREGM_PAD49RSEL_Pos (14UL) /*!< GPIO PADREGM: PAD49RSEL (Bit 14) */ -#define GPIO_PADREGM_PAD49RSEL_Msk (0xc000UL) /*!< GPIO PADREGM: PAD49RSEL (Bitfield-Mask: 0x03) */ -#define GPIO_PADREGM_PAD49FNCSEL_Pos (11UL) /*!< GPIO PADREGM: PAD49FNCSEL (Bit 11) */ -#define GPIO_PADREGM_PAD49FNCSEL_Msk (0x3800UL) /*!< GPIO PADREGM: PAD49FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGM_PAD49STRNG_Pos (10UL) /*!< GPIO PADREGM: PAD49STRNG (Bit 10) */ -#define GPIO_PADREGM_PAD49STRNG_Msk (0x400UL) /*!< GPIO PADREGM: PAD49STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGM_PAD49INPEN_Pos (9UL) /*!< GPIO PADREGM: PAD49INPEN (Bit 9) */ -#define GPIO_PADREGM_PAD49INPEN_Msk (0x200UL) /*!< GPIO PADREGM: PAD49INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGM_PAD49PULL_Pos (8UL) /*!< GPIO PADREGM: PAD49PULL (Bit 8) */ -#define GPIO_PADREGM_PAD49PULL_Msk (0x100UL) /*!< GPIO PADREGM: PAD49PULL (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGM_PAD48RSEL_Pos (6UL) /*!< GPIO PADREGM: PAD48RSEL (Bit 6) */ -#define GPIO_PADREGM_PAD48RSEL_Msk (0xc0UL) /*!< GPIO PADREGM: PAD48RSEL (Bitfield-Mask: 0x03) */ -#define GPIO_PADREGM_PAD48FNCSEL_Pos (3UL) /*!< GPIO PADREGM: PAD48FNCSEL (Bit 3) */ -#define GPIO_PADREGM_PAD48FNCSEL_Msk (0x38UL) /*!< GPIO PADREGM: PAD48FNCSEL (Bitfield-Mask: 0x07) */ -#define GPIO_PADREGM_PAD48STRNG_Pos (2UL) /*!< GPIO PADREGM: PAD48STRNG (Bit 2) */ -#define GPIO_PADREGM_PAD48STRNG_Msk (0x4UL) /*!< GPIO PADREGM: PAD48STRNG (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGM_PAD48INPEN_Pos (1UL) /*!< GPIO PADREGM: PAD48INPEN (Bit 1) */ -#define GPIO_PADREGM_PAD48INPEN_Msk (0x2UL) /*!< GPIO PADREGM: PAD48INPEN (Bitfield-Mask: 0x01) */ -#define GPIO_PADREGM_PAD48PULL_Pos (0UL) /*!< GPIO PADREGM: PAD48PULL (Bit 0) */ -#define GPIO_PADREGM_PAD48PULL_Msk (0x1UL) /*!< GPIO PADREGM: PAD48PULL (Bitfield-Mask: 0x01) */ -/* ========================================================= CFGA ========================================================== */ -#define GPIO_CFGA_GPIO7INTD_Pos (31UL) /*!< GPIO CFGA: GPIO7INTD (Bit 31) */ -#define GPIO_CFGA_GPIO7INTD_Msk (0x80000000UL) /*!< GPIO CFGA: GPIO7INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGA_GPIO7OUTCFG_Pos (29UL) /*!< GPIO CFGA: GPIO7OUTCFG (Bit 29) */ -#define GPIO_CFGA_GPIO7OUTCFG_Msk (0x60000000UL) /*!< GPIO CFGA: GPIO7OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGA_GPIO7INCFG_Pos (28UL) /*!< GPIO CFGA: GPIO7INCFG (Bit 28) */ -#define GPIO_CFGA_GPIO7INCFG_Msk (0x10000000UL) /*!< GPIO CFGA: GPIO7INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGA_GPIO6INTD_Pos (27UL) /*!< GPIO CFGA: GPIO6INTD (Bit 27) */ -#define GPIO_CFGA_GPIO6INTD_Msk (0x8000000UL) /*!< GPIO CFGA: GPIO6INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGA_GPIO6OUTCFG_Pos (25UL) /*!< GPIO CFGA: GPIO6OUTCFG (Bit 25) */ -#define GPIO_CFGA_GPIO6OUTCFG_Msk (0x6000000UL) /*!< GPIO CFGA: GPIO6OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGA_GPIO6INCFG_Pos (24UL) /*!< GPIO CFGA: GPIO6INCFG (Bit 24) */ -#define GPIO_CFGA_GPIO6INCFG_Msk (0x1000000UL) /*!< GPIO CFGA: GPIO6INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGA_GPIO5INTD_Pos (23UL) /*!< GPIO CFGA: GPIO5INTD (Bit 23) */ -#define GPIO_CFGA_GPIO5INTD_Msk (0x800000UL) /*!< GPIO CFGA: GPIO5INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGA_GPIO5OUTCFG_Pos (21UL) /*!< GPIO CFGA: GPIO5OUTCFG (Bit 21) */ -#define GPIO_CFGA_GPIO5OUTCFG_Msk (0x600000UL) /*!< GPIO CFGA: GPIO5OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGA_GPIO5INCFG_Pos (20UL) /*!< GPIO CFGA: GPIO5INCFG (Bit 20) */ -#define GPIO_CFGA_GPIO5INCFG_Msk (0x100000UL) /*!< GPIO CFGA: GPIO5INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGA_GPIO4INTD_Pos (19UL) /*!< GPIO CFGA: GPIO4INTD (Bit 19) */ -#define GPIO_CFGA_GPIO4INTD_Msk (0x80000UL) /*!< GPIO CFGA: GPIO4INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGA_GPIO4OUTCFG_Pos (17UL) /*!< GPIO CFGA: GPIO4OUTCFG (Bit 17) */ -#define GPIO_CFGA_GPIO4OUTCFG_Msk (0x60000UL) /*!< GPIO CFGA: GPIO4OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGA_GPIO4INCFG_Pos (16UL) /*!< GPIO CFGA: GPIO4INCFG (Bit 16) */ -#define GPIO_CFGA_GPIO4INCFG_Msk (0x10000UL) /*!< GPIO CFGA: GPIO4INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGA_GPIO3INTD_Pos (15UL) /*!< GPIO CFGA: GPIO3INTD (Bit 15) */ -#define GPIO_CFGA_GPIO3INTD_Msk (0x8000UL) /*!< GPIO CFGA: GPIO3INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGA_GPIO3OUTCFG_Pos (13UL) /*!< GPIO CFGA: GPIO3OUTCFG (Bit 13) */ -#define GPIO_CFGA_GPIO3OUTCFG_Msk (0x6000UL) /*!< GPIO CFGA: GPIO3OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGA_GPIO3INCFG_Pos (12UL) /*!< GPIO CFGA: GPIO3INCFG (Bit 12) */ -#define GPIO_CFGA_GPIO3INCFG_Msk (0x1000UL) /*!< GPIO CFGA: GPIO3INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGA_GPIO2INTD_Pos (11UL) /*!< GPIO CFGA: GPIO2INTD (Bit 11) */ -#define GPIO_CFGA_GPIO2INTD_Msk (0x800UL) /*!< GPIO CFGA: GPIO2INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGA_GPIO2OUTCFG_Pos (9UL) /*!< GPIO CFGA: GPIO2OUTCFG (Bit 9) */ -#define GPIO_CFGA_GPIO2OUTCFG_Msk (0x600UL) /*!< GPIO CFGA: GPIO2OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGA_GPIO2INCFG_Pos (8UL) /*!< GPIO CFGA: GPIO2INCFG (Bit 8) */ -#define GPIO_CFGA_GPIO2INCFG_Msk (0x100UL) /*!< GPIO CFGA: GPIO2INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGA_GPIO1INTD_Pos (7UL) /*!< GPIO CFGA: GPIO1INTD (Bit 7) */ -#define GPIO_CFGA_GPIO1INTD_Msk (0x80UL) /*!< GPIO CFGA: GPIO1INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGA_GPIO1OUTCFG_Pos (5UL) /*!< GPIO CFGA: GPIO1OUTCFG (Bit 5) */ -#define GPIO_CFGA_GPIO1OUTCFG_Msk (0x60UL) /*!< GPIO CFGA: GPIO1OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGA_GPIO1INCFG_Pos (4UL) /*!< GPIO CFGA: GPIO1INCFG (Bit 4) */ -#define GPIO_CFGA_GPIO1INCFG_Msk (0x10UL) /*!< GPIO CFGA: GPIO1INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGA_GPIO0INTD_Pos (3UL) /*!< GPIO CFGA: GPIO0INTD (Bit 3) */ -#define GPIO_CFGA_GPIO0INTD_Msk (0x8UL) /*!< GPIO CFGA: GPIO0INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGA_GPIO0OUTCFG_Pos (1UL) /*!< GPIO CFGA: GPIO0OUTCFG (Bit 1) */ -#define GPIO_CFGA_GPIO0OUTCFG_Msk (0x6UL) /*!< GPIO CFGA: GPIO0OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGA_GPIO0INCFG_Pos (0UL) /*!< GPIO CFGA: GPIO0INCFG (Bit 0) */ -#define GPIO_CFGA_GPIO0INCFG_Msk (0x1UL) /*!< GPIO CFGA: GPIO0INCFG (Bitfield-Mask: 0x01) */ -/* ========================================================= CFGB ========================================================== */ -#define GPIO_CFGB_GPIO15INTD_Pos (31UL) /*!< GPIO CFGB: GPIO15INTD (Bit 31) */ -#define GPIO_CFGB_GPIO15INTD_Msk (0x80000000UL) /*!< GPIO CFGB: GPIO15INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGB_GPIO15OUTCFG_Pos (29UL) /*!< GPIO CFGB: GPIO15OUTCFG (Bit 29) */ -#define GPIO_CFGB_GPIO15OUTCFG_Msk (0x60000000UL) /*!< GPIO CFGB: GPIO15OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGB_GPIO15INCFG_Pos (28UL) /*!< GPIO CFGB: GPIO15INCFG (Bit 28) */ -#define GPIO_CFGB_GPIO15INCFG_Msk (0x10000000UL) /*!< GPIO CFGB: GPIO15INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGB_GPIO14INTD_Pos (27UL) /*!< GPIO CFGB: GPIO14INTD (Bit 27) */ -#define GPIO_CFGB_GPIO14INTD_Msk (0x8000000UL) /*!< GPIO CFGB: GPIO14INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGB_GPIO14OUTCFG_Pos (25UL) /*!< GPIO CFGB: GPIO14OUTCFG (Bit 25) */ -#define GPIO_CFGB_GPIO14OUTCFG_Msk (0x6000000UL) /*!< GPIO CFGB: GPIO14OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGB_GPIO14INCFG_Pos (24UL) /*!< GPIO CFGB: GPIO14INCFG (Bit 24) */ -#define GPIO_CFGB_GPIO14INCFG_Msk (0x1000000UL) /*!< GPIO CFGB: GPIO14INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGB_GPIO13INTD_Pos (23UL) /*!< GPIO CFGB: GPIO13INTD (Bit 23) */ -#define GPIO_CFGB_GPIO13INTD_Msk (0x800000UL) /*!< GPIO CFGB: GPIO13INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGB_GPIO13OUTCFG_Pos (21UL) /*!< GPIO CFGB: GPIO13OUTCFG (Bit 21) */ -#define GPIO_CFGB_GPIO13OUTCFG_Msk (0x600000UL) /*!< GPIO CFGB: GPIO13OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGB_GPIO13INCFG_Pos (20UL) /*!< GPIO CFGB: GPIO13INCFG (Bit 20) */ -#define GPIO_CFGB_GPIO13INCFG_Msk (0x100000UL) /*!< GPIO CFGB: GPIO13INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGB_GPIO12INTD_Pos (19UL) /*!< GPIO CFGB: GPIO12INTD (Bit 19) */ -#define GPIO_CFGB_GPIO12INTD_Msk (0x80000UL) /*!< GPIO CFGB: GPIO12INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGB_GPIO12OUTCFG_Pos (17UL) /*!< GPIO CFGB: GPIO12OUTCFG (Bit 17) */ -#define GPIO_CFGB_GPIO12OUTCFG_Msk (0x60000UL) /*!< GPIO CFGB: GPIO12OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGB_GPIO12INCFG_Pos (16UL) /*!< GPIO CFGB: GPIO12INCFG (Bit 16) */ -#define GPIO_CFGB_GPIO12INCFG_Msk (0x10000UL) /*!< GPIO CFGB: GPIO12INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGB_GPIO11INTD_Pos (15UL) /*!< GPIO CFGB: GPIO11INTD (Bit 15) */ -#define GPIO_CFGB_GPIO11INTD_Msk (0x8000UL) /*!< GPIO CFGB: GPIO11INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGB_GPIO11OUTCFG_Pos (13UL) /*!< GPIO CFGB: GPIO11OUTCFG (Bit 13) */ -#define GPIO_CFGB_GPIO11OUTCFG_Msk (0x6000UL) /*!< GPIO CFGB: GPIO11OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGB_GPIO11INCFG_Pos (12UL) /*!< GPIO CFGB: GPIO11INCFG (Bit 12) */ -#define GPIO_CFGB_GPIO11INCFG_Msk (0x1000UL) /*!< GPIO CFGB: GPIO11INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGB_GPIO10INTD_Pos (11UL) /*!< GPIO CFGB: GPIO10INTD (Bit 11) */ -#define GPIO_CFGB_GPIO10INTD_Msk (0x800UL) /*!< GPIO CFGB: GPIO10INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGB_GPIO10OUTCFG_Pos (9UL) /*!< GPIO CFGB: GPIO10OUTCFG (Bit 9) */ -#define GPIO_CFGB_GPIO10OUTCFG_Msk (0x600UL) /*!< GPIO CFGB: GPIO10OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGB_GPIO10INCFG_Pos (8UL) /*!< GPIO CFGB: GPIO10INCFG (Bit 8) */ -#define GPIO_CFGB_GPIO10INCFG_Msk (0x100UL) /*!< GPIO CFGB: GPIO10INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGB_GPIO9INTD_Pos (7UL) /*!< GPIO CFGB: GPIO9INTD (Bit 7) */ -#define GPIO_CFGB_GPIO9INTD_Msk (0x80UL) /*!< GPIO CFGB: GPIO9INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGB_GPIO9OUTCFG_Pos (5UL) /*!< GPIO CFGB: GPIO9OUTCFG (Bit 5) */ -#define GPIO_CFGB_GPIO9OUTCFG_Msk (0x60UL) /*!< GPIO CFGB: GPIO9OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGB_GPIO9INCFG_Pos (4UL) /*!< GPIO CFGB: GPIO9INCFG (Bit 4) */ -#define GPIO_CFGB_GPIO9INCFG_Msk (0x10UL) /*!< GPIO CFGB: GPIO9INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGB_GPIO8INTD_Pos (3UL) /*!< GPIO CFGB: GPIO8INTD (Bit 3) */ -#define GPIO_CFGB_GPIO8INTD_Msk (0x8UL) /*!< GPIO CFGB: GPIO8INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGB_GPIO8OUTCFG_Pos (1UL) /*!< GPIO CFGB: GPIO8OUTCFG (Bit 1) */ -#define GPIO_CFGB_GPIO8OUTCFG_Msk (0x6UL) /*!< GPIO CFGB: GPIO8OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGB_GPIO8INCFG_Pos (0UL) /*!< GPIO CFGB: GPIO8INCFG (Bit 0) */ -#define GPIO_CFGB_GPIO8INCFG_Msk (0x1UL) /*!< GPIO CFGB: GPIO8INCFG (Bitfield-Mask: 0x01) */ -/* ========================================================= CFGC ========================================================== */ -#define GPIO_CFGC_GPIO23INTD_Pos (31UL) /*!< GPIO CFGC: GPIO23INTD (Bit 31) */ -#define GPIO_CFGC_GPIO23INTD_Msk (0x80000000UL) /*!< GPIO CFGC: GPIO23INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGC_GPIO23OUTCFG_Pos (29UL) /*!< GPIO CFGC: GPIO23OUTCFG (Bit 29) */ -#define GPIO_CFGC_GPIO23OUTCFG_Msk (0x60000000UL) /*!< GPIO CFGC: GPIO23OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGC_GPIO23INCFG_Pos (28UL) /*!< GPIO CFGC: GPIO23INCFG (Bit 28) */ -#define GPIO_CFGC_GPIO23INCFG_Msk (0x10000000UL) /*!< GPIO CFGC: GPIO23INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGC_GPIO22INTD_Pos (27UL) /*!< GPIO CFGC: GPIO22INTD (Bit 27) */ -#define GPIO_CFGC_GPIO22INTD_Msk (0x8000000UL) /*!< GPIO CFGC: GPIO22INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGC_GPIO22OUTCFG_Pos (25UL) /*!< GPIO CFGC: GPIO22OUTCFG (Bit 25) */ -#define GPIO_CFGC_GPIO22OUTCFG_Msk (0x6000000UL) /*!< GPIO CFGC: GPIO22OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGC_GPIO22INCFG_Pos (24UL) /*!< GPIO CFGC: GPIO22INCFG (Bit 24) */ -#define GPIO_CFGC_GPIO22INCFG_Msk (0x1000000UL) /*!< GPIO CFGC: GPIO22INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGC_GPIO21INTD_Pos (23UL) /*!< GPIO CFGC: GPIO21INTD (Bit 23) */ -#define GPIO_CFGC_GPIO21INTD_Msk (0x800000UL) /*!< GPIO CFGC: GPIO21INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGC_GPIO21OUTCFG_Pos (21UL) /*!< GPIO CFGC: GPIO21OUTCFG (Bit 21) */ -#define GPIO_CFGC_GPIO21OUTCFG_Msk (0x600000UL) /*!< GPIO CFGC: GPIO21OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGC_GPIO21INCFG_Pos (20UL) /*!< GPIO CFGC: GPIO21INCFG (Bit 20) */ -#define GPIO_CFGC_GPIO21INCFG_Msk (0x100000UL) /*!< GPIO CFGC: GPIO21INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGC_GPIO20INTD_Pos (19UL) /*!< GPIO CFGC: GPIO20INTD (Bit 19) */ -#define GPIO_CFGC_GPIO20INTD_Msk (0x80000UL) /*!< GPIO CFGC: GPIO20INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGC_GPIO20OUTCFG_Pos (17UL) /*!< GPIO CFGC: GPIO20OUTCFG (Bit 17) */ -#define GPIO_CFGC_GPIO20OUTCFG_Msk (0x60000UL) /*!< GPIO CFGC: GPIO20OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGC_GPIO20INCFG_Pos (16UL) /*!< GPIO CFGC: GPIO20INCFG (Bit 16) */ -#define GPIO_CFGC_GPIO20INCFG_Msk (0x10000UL) /*!< GPIO CFGC: GPIO20INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGC_GPIO19INTD_Pos (15UL) /*!< GPIO CFGC: GPIO19INTD (Bit 15) */ -#define GPIO_CFGC_GPIO19INTD_Msk (0x8000UL) /*!< GPIO CFGC: GPIO19INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGC_GPIO19OUTCFG_Pos (13UL) /*!< GPIO CFGC: GPIO19OUTCFG (Bit 13) */ -#define GPIO_CFGC_GPIO19OUTCFG_Msk (0x6000UL) /*!< GPIO CFGC: GPIO19OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGC_GPIO19INCFG_Pos (12UL) /*!< GPIO CFGC: GPIO19INCFG (Bit 12) */ -#define GPIO_CFGC_GPIO19INCFG_Msk (0x1000UL) /*!< GPIO CFGC: GPIO19INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGC_GPIO18INTD_Pos (11UL) /*!< GPIO CFGC: GPIO18INTD (Bit 11) */ -#define GPIO_CFGC_GPIO18INTD_Msk (0x800UL) /*!< GPIO CFGC: GPIO18INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGC_GPIO18OUTCFG_Pos (9UL) /*!< GPIO CFGC: GPIO18OUTCFG (Bit 9) */ -#define GPIO_CFGC_GPIO18OUTCFG_Msk (0x600UL) /*!< GPIO CFGC: GPIO18OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGC_GPIO18INCFG_Pos (8UL) /*!< GPIO CFGC: GPIO18INCFG (Bit 8) */ -#define GPIO_CFGC_GPIO18INCFG_Msk (0x100UL) /*!< GPIO CFGC: GPIO18INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGC_GPIO17INTD_Pos (7UL) /*!< GPIO CFGC: GPIO17INTD (Bit 7) */ -#define GPIO_CFGC_GPIO17INTD_Msk (0x80UL) /*!< GPIO CFGC: GPIO17INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGC_GPIO17OUTCFG_Pos (5UL) /*!< GPIO CFGC: GPIO17OUTCFG (Bit 5) */ -#define GPIO_CFGC_GPIO17OUTCFG_Msk (0x60UL) /*!< GPIO CFGC: GPIO17OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGC_GPIO17INCFG_Pos (4UL) /*!< GPIO CFGC: GPIO17INCFG (Bit 4) */ -#define GPIO_CFGC_GPIO17INCFG_Msk (0x10UL) /*!< GPIO CFGC: GPIO17INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGC_GPIO16INTD_Pos (3UL) /*!< GPIO CFGC: GPIO16INTD (Bit 3) */ -#define GPIO_CFGC_GPIO16INTD_Msk (0x8UL) /*!< GPIO CFGC: GPIO16INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGC_GPIO16OUTCFG_Pos (1UL) /*!< GPIO CFGC: GPIO16OUTCFG (Bit 1) */ -#define GPIO_CFGC_GPIO16OUTCFG_Msk (0x6UL) /*!< GPIO CFGC: GPIO16OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGC_GPIO16INCFG_Pos (0UL) /*!< GPIO CFGC: GPIO16INCFG (Bit 0) */ -#define GPIO_CFGC_GPIO16INCFG_Msk (0x1UL) /*!< GPIO CFGC: GPIO16INCFG (Bitfield-Mask: 0x01) */ -/* ========================================================= CFGD ========================================================== */ -#define GPIO_CFGD_GPIO31INTD_Pos (31UL) /*!< GPIO CFGD: GPIO31INTD (Bit 31) */ -#define GPIO_CFGD_GPIO31INTD_Msk (0x80000000UL) /*!< GPIO CFGD: GPIO31INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGD_GPIO31OUTCFG_Pos (29UL) /*!< GPIO CFGD: GPIO31OUTCFG (Bit 29) */ -#define GPIO_CFGD_GPIO31OUTCFG_Msk (0x60000000UL) /*!< GPIO CFGD: GPIO31OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGD_GPIO31INCFG_Pos (28UL) /*!< GPIO CFGD: GPIO31INCFG (Bit 28) */ -#define GPIO_CFGD_GPIO31INCFG_Msk (0x10000000UL) /*!< GPIO CFGD: GPIO31INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGD_GPIO30INTD_Pos (27UL) /*!< GPIO CFGD: GPIO30INTD (Bit 27) */ -#define GPIO_CFGD_GPIO30INTD_Msk (0x8000000UL) /*!< GPIO CFGD: GPIO30INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGD_GPIO30OUTCFG_Pos (25UL) /*!< GPIO CFGD: GPIO30OUTCFG (Bit 25) */ -#define GPIO_CFGD_GPIO30OUTCFG_Msk (0x6000000UL) /*!< GPIO CFGD: GPIO30OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGD_GPIO30INCFG_Pos (24UL) /*!< GPIO CFGD: GPIO30INCFG (Bit 24) */ -#define GPIO_CFGD_GPIO30INCFG_Msk (0x1000000UL) /*!< GPIO CFGD: GPIO30INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGD_GPIO29INTD_Pos (23UL) /*!< GPIO CFGD: GPIO29INTD (Bit 23) */ -#define GPIO_CFGD_GPIO29INTD_Msk (0x800000UL) /*!< GPIO CFGD: GPIO29INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGD_GPIO29OUTCFG_Pos (21UL) /*!< GPIO CFGD: GPIO29OUTCFG (Bit 21) */ -#define GPIO_CFGD_GPIO29OUTCFG_Msk (0x600000UL) /*!< GPIO CFGD: GPIO29OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGD_GPIO29INCFG_Pos (20UL) /*!< GPIO CFGD: GPIO29INCFG (Bit 20) */ -#define GPIO_CFGD_GPIO29INCFG_Msk (0x100000UL) /*!< GPIO CFGD: GPIO29INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGD_GPIO28INTD_Pos (19UL) /*!< GPIO CFGD: GPIO28INTD (Bit 19) */ -#define GPIO_CFGD_GPIO28INTD_Msk (0x80000UL) /*!< GPIO CFGD: GPIO28INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGD_GPIO28OUTCFG_Pos (17UL) /*!< GPIO CFGD: GPIO28OUTCFG (Bit 17) */ -#define GPIO_CFGD_GPIO28OUTCFG_Msk (0x60000UL) /*!< GPIO CFGD: GPIO28OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGD_GPIO28INCFG_Pos (16UL) /*!< GPIO CFGD: GPIO28INCFG (Bit 16) */ -#define GPIO_CFGD_GPIO28INCFG_Msk (0x10000UL) /*!< GPIO CFGD: GPIO28INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGD_GPIO27INTD_Pos (15UL) /*!< GPIO CFGD: GPIO27INTD (Bit 15) */ -#define GPIO_CFGD_GPIO27INTD_Msk (0x8000UL) /*!< GPIO CFGD: GPIO27INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGD_GPIO27OUTCFG_Pos (13UL) /*!< GPIO CFGD: GPIO27OUTCFG (Bit 13) */ -#define GPIO_CFGD_GPIO27OUTCFG_Msk (0x6000UL) /*!< GPIO CFGD: GPIO27OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGD_GPIO27INCFG_Pos (12UL) /*!< GPIO CFGD: GPIO27INCFG (Bit 12) */ -#define GPIO_CFGD_GPIO27INCFG_Msk (0x1000UL) /*!< GPIO CFGD: GPIO27INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGD_GPIO26INTD_Pos (11UL) /*!< GPIO CFGD: GPIO26INTD (Bit 11) */ -#define GPIO_CFGD_GPIO26INTD_Msk (0x800UL) /*!< GPIO CFGD: GPIO26INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGD_GPIO26OUTCFG_Pos (9UL) /*!< GPIO CFGD: GPIO26OUTCFG (Bit 9) */ -#define GPIO_CFGD_GPIO26OUTCFG_Msk (0x600UL) /*!< GPIO CFGD: GPIO26OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGD_GPIO26INCFG_Pos (8UL) /*!< GPIO CFGD: GPIO26INCFG (Bit 8) */ -#define GPIO_CFGD_GPIO26INCFG_Msk (0x100UL) /*!< GPIO CFGD: GPIO26INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGD_GPIO25INTD_Pos (7UL) /*!< GPIO CFGD: GPIO25INTD (Bit 7) */ -#define GPIO_CFGD_GPIO25INTD_Msk (0x80UL) /*!< GPIO CFGD: GPIO25INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGD_GPIO25OUTCFG_Pos (5UL) /*!< GPIO CFGD: GPIO25OUTCFG (Bit 5) */ -#define GPIO_CFGD_GPIO25OUTCFG_Msk (0x60UL) /*!< GPIO CFGD: GPIO25OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGD_GPIO25INCFG_Pos (4UL) /*!< GPIO CFGD: GPIO25INCFG (Bit 4) */ -#define GPIO_CFGD_GPIO25INCFG_Msk (0x10UL) /*!< GPIO CFGD: GPIO25INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGD_GPIO24INTD_Pos (3UL) /*!< GPIO CFGD: GPIO24INTD (Bit 3) */ -#define GPIO_CFGD_GPIO24INTD_Msk (0x8UL) /*!< GPIO CFGD: GPIO24INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGD_GPIO24OUTCFG_Pos (1UL) /*!< GPIO CFGD: GPIO24OUTCFG (Bit 1) */ -#define GPIO_CFGD_GPIO24OUTCFG_Msk (0x6UL) /*!< GPIO CFGD: GPIO24OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGD_GPIO24INCFG_Pos (0UL) /*!< GPIO CFGD: GPIO24INCFG (Bit 0) */ -#define GPIO_CFGD_GPIO24INCFG_Msk (0x1UL) /*!< GPIO CFGD: GPIO24INCFG (Bitfield-Mask: 0x01) */ -/* ========================================================= CFGE ========================================================== */ -#define GPIO_CFGE_GPIO39INTD_Pos (31UL) /*!< GPIO CFGE: GPIO39INTD (Bit 31) */ -#define GPIO_CFGE_GPIO39INTD_Msk (0x80000000UL) /*!< GPIO CFGE: GPIO39INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGE_GPIO39OUTCFG_Pos (29UL) /*!< GPIO CFGE: GPIO39OUTCFG (Bit 29) */ -#define GPIO_CFGE_GPIO39OUTCFG_Msk (0x60000000UL) /*!< GPIO CFGE: GPIO39OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGE_GPIO39INCFG_Pos (28UL) /*!< GPIO CFGE: GPIO39INCFG (Bit 28) */ -#define GPIO_CFGE_GPIO39INCFG_Msk (0x10000000UL) /*!< GPIO CFGE: GPIO39INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGE_GPIO38INTD_Pos (27UL) /*!< GPIO CFGE: GPIO38INTD (Bit 27) */ -#define GPIO_CFGE_GPIO38INTD_Msk (0x8000000UL) /*!< GPIO CFGE: GPIO38INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGE_GPIO38OUTCFG_Pos (25UL) /*!< GPIO CFGE: GPIO38OUTCFG (Bit 25) */ -#define GPIO_CFGE_GPIO38OUTCFG_Msk (0x6000000UL) /*!< GPIO CFGE: GPIO38OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGE_GPIO38INCFG_Pos (24UL) /*!< GPIO CFGE: GPIO38INCFG (Bit 24) */ -#define GPIO_CFGE_GPIO38INCFG_Msk (0x1000000UL) /*!< GPIO CFGE: GPIO38INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGE_GPIO37INTD_Pos (23UL) /*!< GPIO CFGE: GPIO37INTD (Bit 23) */ -#define GPIO_CFGE_GPIO37INTD_Msk (0x800000UL) /*!< GPIO CFGE: GPIO37INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGE_GPIO37OUTCFG_Pos (21UL) /*!< GPIO CFGE: GPIO37OUTCFG (Bit 21) */ -#define GPIO_CFGE_GPIO37OUTCFG_Msk (0x600000UL) /*!< GPIO CFGE: GPIO37OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGE_GPIO37INCFG_Pos (20UL) /*!< GPIO CFGE: GPIO37INCFG (Bit 20) */ -#define GPIO_CFGE_GPIO37INCFG_Msk (0x100000UL) /*!< GPIO CFGE: GPIO37INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGE_GPIO36INTD_Pos (19UL) /*!< GPIO CFGE: GPIO36INTD (Bit 19) */ -#define GPIO_CFGE_GPIO36INTD_Msk (0x80000UL) /*!< GPIO CFGE: GPIO36INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGE_GPIO36OUTCFG_Pos (17UL) /*!< GPIO CFGE: GPIO36OUTCFG (Bit 17) */ -#define GPIO_CFGE_GPIO36OUTCFG_Msk (0x60000UL) /*!< GPIO CFGE: GPIO36OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGE_GPIO36INCFG_Pos (16UL) /*!< GPIO CFGE: GPIO36INCFG (Bit 16) */ -#define GPIO_CFGE_GPIO36INCFG_Msk (0x10000UL) /*!< GPIO CFGE: GPIO36INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGE_GPIO35INTD_Pos (15UL) /*!< GPIO CFGE: GPIO35INTD (Bit 15) */ -#define GPIO_CFGE_GPIO35INTD_Msk (0x8000UL) /*!< GPIO CFGE: GPIO35INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGE_GPIO35OUTCFG_Pos (13UL) /*!< GPIO CFGE: GPIO35OUTCFG (Bit 13) */ -#define GPIO_CFGE_GPIO35OUTCFG_Msk (0x6000UL) /*!< GPIO CFGE: GPIO35OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGE_GPIO35INCFG_Pos (12UL) /*!< GPIO CFGE: GPIO35INCFG (Bit 12) */ -#define GPIO_CFGE_GPIO35INCFG_Msk (0x1000UL) /*!< GPIO CFGE: GPIO35INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGE_GPIO34INTD_Pos (11UL) /*!< GPIO CFGE: GPIO34INTD (Bit 11) */ -#define GPIO_CFGE_GPIO34INTD_Msk (0x800UL) /*!< GPIO CFGE: GPIO34INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGE_GPIO34OUTCFG_Pos (9UL) /*!< GPIO CFGE: GPIO34OUTCFG (Bit 9) */ -#define GPIO_CFGE_GPIO34OUTCFG_Msk (0x600UL) /*!< GPIO CFGE: GPIO34OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGE_GPIO34INCFG_Pos (8UL) /*!< GPIO CFGE: GPIO34INCFG (Bit 8) */ -#define GPIO_CFGE_GPIO34INCFG_Msk (0x100UL) /*!< GPIO CFGE: GPIO34INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGE_GPIO33INTD_Pos (7UL) /*!< GPIO CFGE: GPIO33INTD (Bit 7) */ -#define GPIO_CFGE_GPIO33INTD_Msk (0x80UL) /*!< GPIO CFGE: GPIO33INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGE_GPIO33OUTCFG_Pos (5UL) /*!< GPIO CFGE: GPIO33OUTCFG (Bit 5) */ -#define GPIO_CFGE_GPIO33OUTCFG_Msk (0x60UL) /*!< GPIO CFGE: GPIO33OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGE_GPIO33INCFG_Pos (4UL) /*!< GPIO CFGE: GPIO33INCFG (Bit 4) */ -#define GPIO_CFGE_GPIO33INCFG_Msk (0x10UL) /*!< GPIO CFGE: GPIO33INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGE_GPIO32INTD_Pos (3UL) /*!< GPIO CFGE: GPIO32INTD (Bit 3) */ -#define GPIO_CFGE_GPIO32INTD_Msk (0x8UL) /*!< GPIO CFGE: GPIO32INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGE_GPIO32OUTCFG_Pos (1UL) /*!< GPIO CFGE: GPIO32OUTCFG (Bit 1) */ -#define GPIO_CFGE_GPIO32OUTCFG_Msk (0x6UL) /*!< GPIO CFGE: GPIO32OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGE_GPIO32INCFG_Pos (0UL) /*!< GPIO CFGE: GPIO32INCFG (Bit 0) */ -#define GPIO_CFGE_GPIO32INCFG_Msk (0x1UL) /*!< GPIO CFGE: GPIO32INCFG (Bitfield-Mask: 0x01) */ -/* ========================================================= CFGF ========================================================== */ -#define GPIO_CFGF_GPIO47INTD_Pos (31UL) /*!< GPIO CFGF: GPIO47INTD (Bit 31) */ -#define GPIO_CFGF_GPIO47INTD_Msk (0x80000000UL) /*!< GPIO CFGF: GPIO47INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGF_GPIO47OUTCFG_Pos (29UL) /*!< GPIO CFGF: GPIO47OUTCFG (Bit 29) */ -#define GPIO_CFGF_GPIO47OUTCFG_Msk (0x60000000UL) /*!< GPIO CFGF: GPIO47OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGF_GPIO47INCFG_Pos (28UL) /*!< GPIO CFGF: GPIO47INCFG (Bit 28) */ -#define GPIO_CFGF_GPIO47INCFG_Msk (0x10000000UL) /*!< GPIO CFGF: GPIO47INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGF_GPIO46INTD_Pos (27UL) /*!< GPIO CFGF: GPIO46INTD (Bit 27) */ -#define GPIO_CFGF_GPIO46INTD_Msk (0x8000000UL) /*!< GPIO CFGF: GPIO46INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGF_GPIO46OUTCFG_Pos (25UL) /*!< GPIO CFGF: GPIO46OUTCFG (Bit 25) */ -#define GPIO_CFGF_GPIO46OUTCFG_Msk (0x6000000UL) /*!< GPIO CFGF: GPIO46OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGF_GPIO46INCFG_Pos (24UL) /*!< GPIO CFGF: GPIO46INCFG (Bit 24) */ -#define GPIO_CFGF_GPIO46INCFG_Msk (0x1000000UL) /*!< GPIO CFGF: GPIO46INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGF_GPIO45INTD_Pos (23UL) /*!< GPIO CFGF: GPIO45INTD (Bit 23) */ -#define GPIO_CFGF_GPIO45INTD_Msk (0x800000UL) /*!< GPIO CFGF: GPIO45INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGF_GPIO45OUTCFG_Pos (21UL) /*!< GPIO CFGF: GPIO45OUTCFG (Bit 21) */ -#define GPIO_CFGF_GPIO45OUTCFG_Msk (0x600000UL) /*!< GPIO CFGF: GPIO45OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGF_GPIO45INCFG_Pos (20UL) /*!< GPIO CFGF: GPIO45INCFG (Bit 20) */ -#define GPIO_CFGF_GPIO45INCFG_Msk (0x100000UL) /*!< GPIO CFGF: GPIO45INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGF_GPIO44INTD_Pos (19UL) /*!< GPIO CFGF: GPIO44INTD (Bit 19) */ -#define GPIO_CFGF_GPIO44INTD_Msk (0x80000UL) /*!< GPIO CFGF: GPIO44INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGF_GPIO44OUTCFG_Pos (17UL) /*!< GPIO CFGF: GPIO44OUTCFG (Bit 17) */ -#define GPIO_CFGF_GPIO44OUTCFG_Msk (0x60000UL) /*!< GPIO CFGF: GPIO44OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGF_GPIO44INCFG_Pos (16UL) /*!< GPIO CFGF: GPIO44INCFG (Bit 16) */ -#define GPIO_CFGF_GPIO44INCFG_Msk (0x10000UL) /*!< GPIO CFGF: GPIO44INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGF_GPIO43INTD_Pos (15UL) /*!< GPIO CFGF: GPIO43INTD (Bit 15) */ -#define GPIO_CFGF_GPIO43INTD_Msk (0x8000UL) /*!< GPIO CFGF: GPIO43INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGF_GPIO43OUTCFG_Pos (13UL) /*!< GPIO CFGF: GPIO43OUTCFG (Bit 13) */ -#define GPIO_CFGF_GPIO43OUTCFG_Msk (0x6000UL) /*!< GPIO CFGF: GPIO43OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGF_GPIO43INCFG_Pos (12UL) /*!< GPIO CFGF: GPIO43INCFG (Bit 12) */ -#define GPIO_CFGF_GPIO43INCFG_Msk (0x1000UL) /*!< GPIO CFGF: GPIO43INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGF_GPIO42INTD_Pos (11UL) /*!< GPIO CFGF: GPIO42INTD (Bit 11) */ -#define GPIO_CFGF_GPIO42INTD_Msk (0x800UL) /*!< GPIO CFGF: GPIO42INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGF_GPIO42OUTCFG_Pos (9UL) /*!< GPIO CFGF: GPIO42OUTCFG (Bit 9) */ -#define GPIO_CFGF_GPIO42OUTCFG_Msk (0x600UL) /*!< GPIO CFGF: GPIO42OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGF_GPIO42INCFG_Pos (8UL) /*!< GPIO CFGF: GPIO42INCFG (Bit 8) */ -#define GPIO_CFGF_GPIO42INCFG_Msk (0x100UL) /*!< GPIO CFGF: GPIO42INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGF_GPIO41INTD_Pos (7UL) /*!< GPIO CFGF: GPIO41INTD (Bit 7) */ -#define GPIO_CFGF_GPIO41INTD_Msk (0x80UL) /*!< GPIO CFGF: GPIO41INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGF_GPIO41OUTCFG_Pos (5UL) /*!< GPIO CFGF: GPIO41OUTCFG (Bit 5) */ -#define GPIO_CFGF_GPIO41OUTCFG_Msk (0x60UL) /*!< GPIO CFGF: GPIO41OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGF_GPIO41INCFG_Pos (4UL) /*!< GPIO CFGF: GPIO41INCFG (Bit 4) */ -#define GPIO_CFGF_GPIO41INCFG_Msk (0x10UL) /*!< GPIO CFGF: GPIO41INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGF_GPIO40INTD_Pos (3UL) /*!< GPIO CFGF: GPIO40INTD (Bit 3) */ -#define GPIO_CFGF_GPIO40INTD_Msk (0x8UL) /*!< GPIO CFGF: GPIO40INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGF_GPIO40OUTCFG_Pos (1UL) /*!< GPIO CFGF: GPIO40OUTCFG (Bit 1) */ -#define GPIO_CFGF_GPIO40OUTCFG_Msk (0x6UL) /*!< GPIO CFGF: GPIO40OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGF_GPIO40INCFG_Pos (0UL) /*!< GPIO CFGF: GPIO40INCFG (Bit 0) */ -#define GPIO_CFGF_GPIO40INCFG_Msk (0x1UL) /*!< GPIO CFGF: GPIO40INCFG (Bitfield-Mask: 0x01) */ -/* ========================================================= CFGG ========================================================== */ -#define GPIO_CFGG_GPIO49INTD_Pos (7UL) /*!< GPIO CFGG: GPIO49INTD (Bit 7) */ -#define GPIO_CFGG_GPIO49INTD_Msk (0x80UL) /*!< GPIO CFGG: GPIO49INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGG_GPIO49OUTCFG_Pos (5UL) /*!< GPIO CFGG: GPIO49OUTCFG (Bit 5) */ -#define GPIO_CFGG_GPIO49OUTCFG_Msk (0x60UL) /*!< GPIO CFGG: GPIO49OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGG_GPIO49INCFG_Pos (4UL) /*!< GPIO CFGG: GPIO49INCFG (Bit 4) */ -#define GPIO_CFGG_GPIO49INCFG_Msk (0x10UL) /*!< GPIO CFGG: GPIO49INCFG (Bitfield-Mask: 0x01) */ -#define GPIO_CFGG_GPIO48INTD_Pos (3UL) /*!< GPIO CFGG: GPIO48INTD (Bit 3) */ -#define GPIO_CFGG_GPIO48INTD_Msk (0x8UL) /*!< GPIO CFGG: GPIO48INTD (Bitfield-Mask: 0x01) */ -#define GPIO_CFGG_GPIO48OUTCFG_Pos (1UL) /*!< GPIO CFGG: GPIO48OUTCFG (Bit 1) */ -#define GPIO_CFGG_GPIO48OUTCFG_Msk (0x6UL) /*!< GPIO CFGG: GPIO48OUTCFG (Bitfield-Mask: 0x03) */ -#define GPIO_CFGG_GPIO48INCFG_Pos (0UL) /*!< GPIO CFGG: GPIO48INCFG (Bit 0) */ -#define GPIO_CFGG_GPIO48INCFG_Msk (0x1UL) /*!< GPIO CFGG: GPIO48INCFG (Bitfield-Mask: 0x01) */ -/* ======================================================== PADKEY ========================================================= */ -#define GPIO_PADKEY_PADKEY_Pos (0UL) /*!< GPIO PADKEY: PADKEY (Bit 0) */ -#define GPIO_PADKEY_PADKEY_Msk (0xffffffffUL) /*!< GPIO PADKEY: PADKEY (Bitfield-Mask: 0xffffffff) */ -/* ========================================================== RDA ========================================================== */ -#define GPIO_RDA_RDA_Pos (0UL) /*!< GPIO RDA: RDA (Bit 0) */ -#define GPIO_RDA_RDA_Msk (0xffffffffUL) /*!< GPIO RDA: RDA (Bitfield-Mask: 0xffffffff) */ -/* ========================================================== RDB ========================================================== */ -#define GPIO_RDB_RDB_Pos (0UL) /*!< GPIO RDB: RDB (Bit 0) */ -#define GPIO_RDB_RDB_Msk (0x3ffffUL) /*!< GPIO RDB: RDB (Bitfield-Mask: 0x3ffff) */ -/* ========================================================== WTA ========================================================== */ -#define GPIO_WTA_WTA_Pos (0UL) /*!< GPIO WTA: WTA (Bit 0) */ -#define GPIO_WTA_WTA_Msk (0xffffffffUL) /*!< GPIO WTA: WTA (Bitfield-Mask: 0xffffffff) */ -/* ========================================================== WTB ========================================================== */ -#define GPIO_WTB_WTB_Pos (0UL) /*!< GPIO WTB: WTB (Bit 0) */ -#define GPIO_WTB_WTB_Msk (0x3ffffUL) /*!< GPIO WTB: WTB (Bitfield-Mask: 0x3ffff) */ -/* ========================================================= WTSA ========================================================== */ -#define GPIO_WTSA_WTSA_Pos (0UL) /*!< GPIO WTSA: WTSA (Bit 0) */ -#define GPIO_WTSA_WTSA_Msk (0xffffffffUL) /*!< GPIO WTSA: WTSA (Bitfield-Mask: 0xffffffff) */ -/* ========================================================= WTSB ========================================================== */ -#define GPIO_WTSB_WTSB_Pos (0UL) /*!< GPIO WTSB: WTSB (Bit 0) */ -#define GPIO_WTSB_WTSB_Msk (0x3ffffUL) /*!< GPIO WTSB: WTSB (Bitfield-Mask: 0x3ffff) */ -/* ========================================================= WTCA ========================================================== */ -#define GPIO_WTCA_WTCA_Pos (0UL) /*!< GPIO WTCA: WTCA (Bit 0) */ -#define GPIO_WTCA_WTCA_Msk (0xffffffffUL) /*!< GPIO WTCA: WTCA (Bitfield-Mask: 0xffffffff) */ -/* ========================================================= WTCB ========================================================== */ -#define GPIO_WTCB_WTCB_Pos (0UL) /*!< GPIO WTCB: WTCB (Bit 0) */ -#define GPIO_WTCB_WTCB_Msk (0x3ffffUL) /*!< GPIO WTCB: WTCB (Bitfield-Mask: 0x3ffff) */ -/* ========================================================== ENA ========================================================== */ -#define GPIO_ENA_ENA_Pos (0UL) /*!< GPIO ENA: ENA (Bit 0) */ -#define GPIO_ENA_ENA_Msk (0xffffffffUL) /*!< GPIO ENA: ENA (Bitfield-Mask: 0xffffffff) */ -/* ========================================================== ENB ========================================================== */ -#define GPIO_ENB_ENB_Pos (0UL) /*!< GPIO ENB: ENB (Bit 0) */ -#define GPIO_ENB_ENB_Msk (0x3ffffUL) /*!< GPIO ENB: ENB (Bitfield-Mask: 0x3ffff) */ -/* ========================================================= ENSA ========================================================== */ -#define GPIO_ENSA_ENSA_Pos (0UL) /*!< GPIO ENSA: ENSA (Bit 0) */ -#define GPIO_ENSA_ENSA_Msk (0xffffffffUL) /*!< GPIO ENSA: ENSA (Bitfield-Mask: 0xffffffff) */ -/* ========================================================= ENSB ========================================================== */ -#define GPIO_ENSB_ENSB_Pos (0UL) /*!< GPIO ENSB: ENSB (Bit 0) */ -#define GPIO_ENSB_ENSB_Msk (0x3ffffUL) /*!< GPIO ENSB: ENSB (Bitfield-Mask: 0x3ffff) */ -/* ========================================================= ENCA ========================================================== */ -#define GPIO_ENCA_ENCA_Pos (0UL) /*!< GPIO ENCA: ENCA (Bit 0) */ -#define GPIO_ENCA_ENCA_Msk (0xffffffffUL) /*!< GPIO ENCA: ENCA (Bitfield-Mask: 0xffffffff) */ -/* ========================================================= ENCB ========================================================== */ -#define GPIO_ENCB_ENCB_Pos (0UL) /*!< GPIO ENCB: ENCB (Bit 0) */ -#define GPIO_ENCB_ENCB_Msk (0x3ffffUL) /*!< GPIO ENCB: ENCB (Bitfield-Mask: 0x3ffff) */ -/* ======================================================== STMRCAP ======================================================== */ -#define GPIO_STMRCAP_STPOL3_Pos (30UL) /*!< GPIO STMRCAP: STPOL3 (Bit 30) */ -#define GPIO_STMRCAP_STPOL3_Msk (0x40000000UL) /*!< GPIO STMRCAP: STPOL3 (Bitfield-Mask: 0x01) */ -#define GPIO_STMRCAP_STSEL3_Pos (24UL) /*!< GPIO STMRCAP: STSEL3 (Bit 24) */ -#define GPIO_STMRCAP_STSEL3_Msk (0x3f000000UL) /*!< GPIO STMRCAP: STSEL3 (Bitfield-Mask: 0x3f) */ -#define GPIO_STMRCAP_STPOL2_Pos (22UL) /*!< GPIO STMRCAP: STPOL2 (Bit 22) */ -#define GPIO_STMRCAP_STPOL2_Msk (0x400000UL) /*!< GPIO STMRCAP: STPOL2 (Bitfield-Mask: 0x01) */ -#define GPIO_STMRCAP_STSEL2_Pos (16UL) /*!< GPIO STMRCAP: STSEL2 (Bit 16) */ -#define GPIO_STMRCAP_STSEL2_Msk (0x3f0000UL) /*!< GPIO STMRCAP: STSEL2 (Bitfield-Mask: 0x3f) */ -#define GPIO_STMRCAP_STPOL1_Pos (14UL) /*!< GPIO STMRCAP: STPOL1 (Bit 14) */ -#define GPIO_STMRCAP_STPOL1_Msk (0x4000UL) /*!< GPIO STMRCAP: STPOL1 (Bitfield-Mask: 0x01) */ -#define GPIO_STMRCAP_STSEL1_Pos (8UL) /*!< GPIO STMRCAP: STSEL1 (Bit 8) */ -#define GPIO_STMRCAP_STSEL1_Msk (0x3f00UL) /*!< GPIO STMRCAP: STSEL1 (Bitfield-Mask: 0x3f) */ -#define GPIO_STMRCAP_STPOL0_Pos (6UL) /*!< GPIO STMRCAP: STPOL0 (Bit 6) */ -#define GPIO_STMRCAP_STPOL0_Msk (0x40UL) /*!< GPIO STMRCAP: STPOL0 (Bitfield-Mask: 0x01) */ -#define GPIO_STMRCAP_STSEL0_Pos (0UL) /*!< GPIO STMRCAP: STSEL0 (Bit 0) */ -#define GPIO_STMRCAP_STSEL0_Msk (0x3fUL) /*!< GPIO STMRCAP: STSEL0 (Bitfield-Mask: 0x3f) */ -/* ======================================================== IOM0IRQ ======================================================== */ -#define GPIO_IOM0IRQ_IOM0IRQ_Pos (0UL) /*!< GPIO IOM0IRQ: IOM0IRQ (Bit 0) */ -#define GPIO_IOM0IRQ_IOM0IRQ_Msk (0x3fUL) /*!< GPIO IOM0IRQ: IOM0IRQ (Bitfield-Mask: 0x3f) */ -/* ======================================================== IOM1IRQ ======================================================== */ -#define GPIO_IOM1IRQ_IOM1IRQ_Pos (0UL) /*!< GPIO IOM1IRQ: IOM1IRQ (Bit 0) */ -#define GPIO_IOM1IRQ_IOM1IRQ_Msk (0x3fUL) /*!< GPIO IOM1IRQ: IOM1IRQ (Bitfield-Mask: 0x3f) */ -/* ======================================================== IOM2IRQ ======================================================== */ -#define GPIO_IOM2IRQ_IOM2IRQ_Pos (0UL) /*!< GPIO IOM2IRQ: IOM2IRQ (Bit 0) */ -#define GPIO_IOM2IRQ_IOM2IRQ_Msk (0x3fUL) /*!< GPIO IOM2IRQ: IOM2IRQ (Bitfield-Mask: 0x3f) */ -/* ======================================================== IOM3IRQ ======================================================== */ -#define GPIO_IOM3IRQ_IOM3IRQ_Pos (0UL) /*!< GPIO IOM3IRQ: IOM3IRQ (Bit 0) */ -#define GPIO_IOM3IRQ_IOM3IRQ_Msk (0x3fUL) /*!< GPIO IOM3IRQ: IOM3IRQ (Bitfield-Mask: 0x3f) */ -/* ======================================================== IOM4IRQ ======================================================== */ -#define GPIO_IOM4IRQ_IOM4IRQ_Pos (0UL) /*!< GPIO IOM4IRQ: IOM4IRQ (Bit 0) */ -#define GPIO_IOM4IRQ_IOM4IRQ_Msk (0x3fUL) /*!< GPIO IOM4IRQ: IOM4IRQ (Bitfield-Mask: 0x3f) */ -/* ======================================================== IOM5IRQ ======================================================== */ -#define GPIO_IOM5IRQ_IOM5IRQ_Pos (0UL) /*!< GPIO IOM5IRQ: IOM5IRQ (Bit 0) */ -#define GPIO_IOM5IRQ_IOM5IRQ_Msk (0x3fUL) /*!< GPIO IOM5IRQ: IOM5IRQ (Bitfield-Mask: 0x3f) */ -/* ======================================================= BLEIFIRQ ======================================================== */ -#define GPIO_BLEIFIRQ_BLEIFIRQ_Pos (0UL) /*!< GPIO BLEIFIRQ: BLEIFIRQ (Bit 0) */ -#define GPIO_BLEIFIRQ_BLEIFIRQ_Msk (0x3fUL) /*!< GPIO BLEIFIRQ: BLEIFIRQ (Bitfield-Mask: 0x3f) */ -/* ======================================================== GPIOOBS ======================================================== */ -#define GPIO_GPIOOBS_OBS_DATA_Pos (0UL) /*!< GPIO GPIOOBS: OBS_DATA (Bit 0) */ -#define GPIO_GPIOOBS_OBS_DATA_Msk (0xffffUL) /*!< GPIO GPIOOBS: OBS_DATA (Bitfield-Mask: 0xffff) */ -/* ====================================================== ALTPADCFGA ======================================================= */ -#define GPIO_ALTPADCFGA_PAD3_SR_Pos (28UL) /*!< GPIO ALTPADCFGA: PAD3_SR (Bit 28) */ -#define GPIO_ALTPADCFGA_PAD3_SR_Msk (0x10000000UL) /*!< GPIO ALTPADCFGA: PAD3_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGA_PAD3_DS1_Pos (24UL) /*!< GPIO ALTPADCFGA: PAD3_DS1 (Bit 24) */ -#define GPIO_ALTPADCFGA_PAD3_DS1_Msk (0x1000000UL) /*!< GPIO ALTPADCFGA: PAD3_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGA_PAD2_SR_Pos (20UL) /*!< GPIO ALTPADCFGA: PAD2_SR (Bit 20) */ -#define GPIO_ALTPADCFGA_PAD2_SR_Msk (0x100000UL) /*!< GPIO ALTPADCFGA: PAD2_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGA_PAD2_DS1_Pos (16UL) /*!< GPIO ALTPADCFGA: PAD2_DS1 (Bit 16) */ -#define GPIO_ALTPADCFGA_PAD2_DS1_Msk (0x10000UL) /*!< GPIO ALTPADCFGA: PAD2_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGA_PAD1_SR_Pos (12UL) /*!< GPIO ALTPADCFGA: PAD1_SR (Bit 12) */ -#define GPIO_ALTPADCFGA_PAD1_SR_Msk (0x1000UL) /*!< GPIO ALTPADCFGA: PAD1_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGA_PAD1_DS1_Pos (8UL) /*!< GPIO ALTPADCFGA: PAD1_DS1 (Bit 8) */ -#define GPIO_ALTPADCFGA_PAD1_DS1_Msk (0x100UL) /*!< GPIO ALTPADCFGA: PAD1_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGA_PAD0_SR_Pos (4UL) /*!< GPIO ALTPADCFGA: PAD0_SR (Bit 4) */ -#define GPIO_ALTPADCFGA_PAD0_SR_Msk (0x10UL) /*!< GPIO ALTPADCFGA: PAD0_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGA_PAD0_DS1_Pos (0UL) /*!< GPIO ALTPADCFGA: PAD0_DS1 (Bit 0) */ -#define GPIO_ALTPADCFGA_PAD0_DS1_Msk (0x1UL) /*!< GPIO ALTPADCFGA: PAD0_DS1 (Bitfield-Mask: 0x01) */ -/* ====================================================== ALTPADCFGB ======================================================= */ -#define GPIO_ALTPADCFGB_PAD7_SR_Pos (28UL) /*!< GPIO ALTPADCFGB: PAD7_SR (Bit 28) */ -#define GPIO_ALTPADCFGB_PAD7_SR_Msk (0x10000000UL) /*!< GPIO ALTPADCFGB: PAD7_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGB_PAD7_DS1_Pos (24UL) /*!< GPIO ALTPADCFGB: PAD7_DS1 (Bit 24) */ -#define GPIO_ALTPADCFGB_PAD7_DS1_Msk (0x1000000UL) /*!< GPIO ALTPADCFGB: PAD7_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGB_PAD6_SR_Pos (20UL) /*!< GPIO ALTPADCFGB: PAD6_SR (Bit 20) */ -#define GPIO_ALTPADCFGB_PAD6_SR_Msk (0x100000UL) /*!< GPIO ALTPADCFGB: PAD6_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGB_PAD6_DS1_Pos (16UL) /*!< GPIO ALTPADCFGB: PAD6_DS1 (Bit 16) */ -#define GPIO_ALTPADCFGB_PAD6_DS1_Msk (0x10000UL) /*!< GPIO ALTPADCFGB: PAD6_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGB_PAD5_SR_Pos (12UL) /*!< GPIO ALTPADCFGB: PAD5_SR (Bit 12) */ -#define GPIO_ALTPADCFGB_PAD5_SR_Msk (0x1000UL) /*!< GPIO ALTPADCFGB: PAD5_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGB_PAD5_DS1_Pos (8UL) /*!< GPIO ALTPADCFGB: PAD5_DS1 (Bit 8) */ -#define GPIO_ALTPADCFGB_PAD5_DS1_Msk (0x100UL) /*!< GPIO ALTPADCFGB: PAD5_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGB_PAD4_SR_Pos (4UL) /*!< GPIO ALTPADCFGB: PAD4_SR (Bit 4) */ -#define GPIO_ALTPADCFGB_PAD4_SR_Msk (0x10UL) /*!< GPIO ALTPADCFGB: PAD4_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGB_PAD4_DS1_Pos (0UL) /*!< GPIO ALTPADCFGB: PAD4_DS1 (Bit 0) */ -#define GPIO_ALTPADCFGB_PAD4_DS1_Msk (0x1UL) /*!< GPIO ALTPADCFGB: PAD4_DS1 (Bitfield-Mask: 0x01) */ -/* ====================================================== ALTPADCFGC ======================================================= */ -#define GPIO_ALTPADCFGC_PAD11_SR_Pos (28UL) /*!< GPIO ALTPADCFGC: PAD11_SR (Bit 28) */ -#define GPIO_ALTPADCFGC_PAD11_SR_Msk (0x10000000UL) /*!< GPIO ALTPADCFGC: PAD11_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGC_PAD11_DS1_Pos (24UL) /*!< GPIO ALTPADCFGC: PAD11_DS1 (Bit 24) */ -#define GPIO_ALTPADCFGC_PAD11_DS1_Msk (0x1000000UL) /*!< GPIO ALTPADCFGC: PAD11_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGC_PAD10_SR_Pos (20UL) /*!< GPIO ALTPADCFGC: PAD10_SR (Bit 20) */ -#define GPIO_ALTPADCFGC_PAD10_SR_Msk (0x100000UL) /*!< GPIO ALTPADCFGC: PAD10_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGC_PAD10_DS1_Pos (16UL) /*!< GPIO ALTPADCFGC: PAD10_DS1 (Bit 16) */ -#define GPIO_ALTPADCFGC_PAD10_DS1_Msk (0x10000UL) /*!< GPIO ALTPADCFGC: PAD10_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGC_PAD9_SR_Pos (12UL) /*!< GPIO ALTPADCFGC: PAD9_SR (Bit 12) */ -#define GPIO_ALTPADCFGC_PAD9_SR_Msk (0x1000UL) /*!< GPIO ALTPADCFGC: PAD9_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGC_PAD9_DS1_Pos (8UL) /*!< GPIO ALTPADCFGC: PAD9_DS1 (Bit 8) */ -#define GPIO_ALTPADCFGC_PAD9_DS1_Msk (0x100UL) /*!< GPIO ALTPADCFGC: PAD9_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGC_PAD8_SR_Pos (4UL) /*!< GPIO ALTPADCFGC: PAD8_SR (Bit 4) */ -#define GPIO_ALTPADCFGC_PAD8_SR_Msk (0x10UL) /*!< GPIO ALTPADCFGC: PAD8_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGC_PAD8_DS1_Pos (0UL) /*!< GPIO ALTPADCFGC: PAD8_DS1 (Bit 0) */ -#define GPIO_ALTPADCFGC_PAD8_DS1_Msk (0x1UL) /*!< GPIO ALTPADCFGC: PAD8_DS1 (Bitfield-Mask: 0x01) */ -/* ====================================================== ALTPADCFGD ======================================================= */ -#define GPIO_ALTPADCFGD_PAD15_SR_Pos (28UL) /*!< GPIO ALTPADCFGD: PAD15_SR (Bit 28) */ -#define GPIO_ALTPADCFGD_PAD15_SR_Msk (0x10000000UL) /*!< GPIO ALTPADCFGD: PAD15_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGD_PAD15_DS1_Pos (24UL) /*!< GPIO ALTPADCFGD: PAD15_DS1 (Bit 24) */ -#define GPIO_ALTPADCFGD_PAD15_DS1_Msk (0x1000000UL) /*!< GPIO ALTPADCFGD: PAD15_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGD_PAD14_SR_Pos (20UL) /*!< GPIO ALTPADCFGD: PAD14_SR (Bit 20) */ -#define GPIO_ALTPADCFGD_PAD14_SR_Msk (0x100000UL) /*!< GPIO ALTPADCFGD: PAD14_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGD_PAD14_DS1_Pos (16UL) /*!< GPIO ALTPADCFGD: PAD14_DS1 (Bit 16) */ -#define GPIO_ALTPADCFGD_PAD14_DS1_Msk (0x10000UL) /*!< GPIO ALTPADCFGD: PAD14_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGD_PAD13_SR_Pos (12UL) /*!< GPIO ALTPADCFGD: PAD13_SR (Bit 12) */ -#define GPIO_ALTPADCFGD_PAD13_SR_Msk (0x1000UL) /*!< GPIO ALTPADCFGD: PAD13_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGD_PAD13_DS1_Pos (8UL) /*!< GPIO ALTPADCFGD: PAD13_DS1 (Bit 8) */ -#define GPIO_ALTPADCFGD_PAD13_DS1_Msk (0x100UL) /*!< GPIO ALTPADCFGD: PAD13_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGD_PAD12_SR_Pos (4UL) /*!< GPIO ALTPADCFGD: PAD12_SR (Bit 4) */ -#define GPIO_ALTPADCFGD_PAD12_SR_Msk (0x10UL) /*!< GPIO ALTPADCFGD: PAD12_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGD_PAD12_DS1_Pos (0UL) /*!< GPIO ALTPADCFGD: PAD12_DS1 (Bit 0) */ -#define GPIO_ALTPADCFGD_PAD12_DS1_Msk (0x1UL) /*!< GPIO ALTPADCFGD: PAD12_DS1 (Bitfield-Mask: 0x01) */ -/* ====================================================== ALTPADCFGE ======================================================= */ -#define GPIO_ALTPADCFGE_PAD19_SR_Pos (28UL) /*!< GPIO ALTPADCFGE: PAD19_SR (Bit 28) */ -#define GPIO_ALTPADCFGE_PAD19_SR_Msk (0x10000000UL) /*!< GPIO ALTPADCFGE: PAD19_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGE_PAD19_DS1_Pos (24UL) /*!< GPIO ALTPADCFGE: PAD19_DS1 (Bit 24) */ -#define GPIO_ALTPADCFGE_PAD19_DS1_Msk (0x1000000UL) /*!< GPIO ALTPADCFGE: PAD19_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGE_PAD18_SR_Pos (20UL) /*!< GPIO ALTPADCFGE: PAD18_SR (Bit 20) */ -#define GPIO_ALTPADCFGE_PAD18_SR_Msk (0x100000UL) /*!< GPIO ALTPADCFGE: PAD18_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGE_PAD18_DS1_Pos (16UL) /*!< GPIO ALTPADCFGE: PAD18_DS1 (Bit 16) */ -#define GPIO_ALTPADCFGE_PAD18_DS1_Msk (0x10000UL) /*!< GPIO ALTPADCFGE: PAD18_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGE_PAD17_SR_Pos (12UL) /*!< GPIO ALTPADCFGE: PAD17_SR (Bit 12) */ -#define GPIO_ALTPADCFGE_PAD17_SR_Msk (0x1000UL) /*!< GPIO ALTPADCFGE: PAD17_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGE_PAD17_DS1_Pos (8UL) /*!< GPIO ALTPADCFGE: PAD17_DS1 (Bit 8) */ -#define GPIO_ALTPADCFGE_PAD17_DS1_Msk (0x100UL) /*!< GPIO ALTPADCFGE: PAD17_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGE_PAD16_SR_Pos (4UL) /*!< GPIO ALTPADCFGE: PAD16_SR (Bit 4) */ -#define GPIO_ALTPADCFGE_PAD16_SR_Msk (0x10UL) /*!< GPIO ALTPADCFGE: PAD16_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGE_PAD16_DS1_Pos (0UL) /*!< GPIO ALTPADCFGE: PAD16_DS1 (Bit 0) */ -#define GPIO_ALTPADCFGE_PAD16_DS1_Msk (0x1UL) /*!< GPIO ALTPADCFGE: PAD16_DS1 (Bitfield-Mask: 0x01) */ -/* ====================================================== ALTPADCFGF ======================================================= */ -#define GPIO_ALTPADCFGF_PAD23_SR_Pos (28UL) /*!< GPIO ALTPADCFGF: PAD23_SR (Bit 28) */ -#define GPIO_ALTPADCFGF_PAD23_SR_Msk (0x10000000UL) /*!< GPIO ALTPADCFGF: PAD23_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGF_PAD23_DS1_Pos (24UL) /*!< GPIO ALTPADCFGF: PAD23_DS1 (Bit 24) */ -#define GPIO_ALTPADCFGF_PAD23_DS1_Msk (0x1000000UL) /*!< GPIO ALTPADCFGF: PAD23_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGF_PAD22_SR_Pos (20UL) /*!< GPIO ALTPADCFGF: PAD22_SR (Bit 20) */ -#define GPIO_ALTPADCFGF_PAD22_SR_Msk (0x100000UL) /*!< GPIO ALTPADCFGF: PAD22_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGF_PAD22_DS1_Pos (16UL) /*!< GPIO ALTPADCFGF: PAD22_DS1 (Bit 16) */ -#define GPIO_ALTPADCFGF_PAD22_DS1_Msk (0x10000UL) /*!< GPIO ALTPADCFGF: PAD22_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGF_PAD21_SR_Pos (12UL) /*!< GPIO ALTPADCFGF: PAD21_SR (Bit 12) */ -#define GPIO_ALTPADCFGF_PAD21_SR_Msk (0x1000UL) /*!< GPIO ALTPADCFGF: PAD21_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGF_PAD21_DS1_Pos (8UL) /*!< GPIO ALTPADCFGF: PAD21_DS1 (Bit 8) */ -#define GPIO_ALTPADCFGF_PAD21_DS1_Msk (0x100UL) /*!< GPIO ALTPADCFGF: PAD21_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGF_PAD20_SR_Pos (4UL) /*!< GPIO ALTPADCFGF: PAD20_SR (Bit 4) */ -#define GPIO_ALTPADCFGF_PAD20_SR_Msk (0x10UL) /*!< GPIO ALTPADCFGF: PAD20_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGF_PAD20_DS1_Pos (0UL) /*!< GPIO ALTPADCFGF: PAD20_DS1 (Bit 0) */ -#define GPIO_ALTPADCFGF_PAD20_DS1_Msk (0x1UL) /*!< GPIO ALTPADCFGF: PAD20_DS1 (Bitfield-Mask: 0x01) */ -/* ====================================================== ALTPADCFGG ======================================================= */ -#define GPIO_ALTPADCFGG_PAD27_SR_Pos (28UL) /*!< GPIO ALTPADCFGG: PAD27_SR (Bit 28) */ -#define GPIO_ALTPADCFGG_PAD27_SR_Msk (0x10000000UL) /*!< GPIO ALTPADCFGG: PAD27_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGG_PAD27_DS1_Pos (24UL) /*!< GPIO ALTPADCFGG: PAD27_DS1 (Bit 24) */ -#define GPIO_ALTPADCFGG_PAD27_DS1_Msk (0x1000000UL) /*!< GPIO ALTPADCFGG: PAD27_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGG_PAD26_SR_Pos (20UL) /*!< GPIO ALTPADCFGG: PAD26_SR (Bit 20) */ -#define GPIO_ALTPADCFGG_PAD26_SR_Msk (0x100000UL) /*!< GPIO ALTPADCFGG: PAD26_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGG_PAD26_DS1_Pos (16UL) /*!< GPIO ALTPADCFGG: PAD26_DS1 (Bit 16) */ -#define GPIO_ALTPADCFGG_PAD26_DS1_Msk (0x10000UL) /*!< GPIO ALTPADCFGG: PAD26_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGG_PAD25_SR_Pos (12UL) /*!< GPIO ALTPADCFGG: PAD25_SR (Bit 12) */ -#define GPIO_ALTPADCFGG_PAD25_SR_Msk (0x1000UL) /*!< GPIO ALTPADCFGG: PAD25_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGG_PAD25_DS1_Pos (8UL) /*!< GPIO ALTPADCFGG: PAD25_DS1 (Bit 8) */ -#define GPIO_ALTPADCFGG_PAD25_DS1_Msk (0x100UL) /*!< GPIO ALTPADCFGG: PAD25_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGG_PAD24_SR_Pos (4UL) /*!< GPIO ALTPADCFGG: PAD24_SR (Bit 4) */ -#define GPIO_ALTPADCFGG_PAD24_SR_Msk (0x10UL) /*!< GPIO ALTPADCFGG: PAD24_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGG_PAD24_DS1_Pos (0UL) /*!< GPIO ALTPADCFGG: PAD24_DS1 (Bit 0) */ -#define GPIO_ALTPADCFGG_PAD24_DS1_Msk (0x1UL) /*!< GPIO ALTPADCFGG: PAD24_DS1 (Bitfield-Mask: 0x01) */ -/* ====================================================== ALTPADCFGH ======================================================= */ -#define GPIO_ALTPADCFGH_PAD31_SR_Pos (28UL) /*!< GPIO ALTPADCFGH: PAD31_SR (Bit 28) */ -#define GPIO_ALTPADCFGH_PAD31_SR_Msk (0x10000000UL) /*!< GPIO ALTPADCFGH: PAD31_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGH_PAD31_DS1_Pos (24UL) /*!< GPIO ALTPADCFGH: PAD31_DS1 (Bit 24) */ -#define GPIO_ALTPADCFGH_PAD31_DS1_Msk (0x1000000UL) /*!< GPIO ALTPADCFGH: PAD31_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGH_PAD30_SR_Pos (20UL) /*!< GPIO ALTPADCFGH: PAD30_SR (Bit 20) */ -#define GPIO_ALTPADCFGH_PAD30_SR_Msk (0x100000UL) /*!< GPIO ALTPADCFGH: PAD30_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGH_PAD30_DS1_Pos (16UL) /*!< GPIO ALTPADCFGH: PAD30_DS1 (Bit 16) */ -#define GPIO_ALTPADCFGH_PAD30_DS1_Msk (0x10000UL) /*!< GPIO ALTPADCFGH: PAD30_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGH_PAD29_SR_Pos (12UL) /*!< GPIO ALTPADCFGH: PAD29_SR (Bit 12) */ -#define GPIO_ALTPADCFGH_PAD29_SR_Msk (0x1000UL) /*!< GPIO ALTPADCFGH: PAD29_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGH_PAD29_DS1_Pos (8UL) /*!< GPIO ALTPADCFGH: PAD29_DS1 (Bit 8) */ -#define GPIO_ALTPADCFGH_PAD29_DS1_Msk (0x100UL) /*!< GPIO ALTPADCFGH: PAD29_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGH_PAD28_SR_Pos (4UL) /*!< GPIO ALTPADCFGH: PAD28_SR (Bit 4) */ -#define GPIO_ALTPADCFGH_PAD28_SR_Msk (0x10UL) /*!< GPIO ALTPADCFGH: PAD28_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGH_PAD28_DS1_Pos (0UL) /*!< GPIO ALTPADCFGH: PAD28_DS1 (Bit 0) */ -#define GPIO_ALTPADCFGH_PAD28_DS1_Msk (0x1UL) /*!< GPIO ALTPADCFGH: PAD28_DS1 (Bitfield-Mask: 0x01) */ -/* ====================================================== ALTPADCFGI ======================================================= */ -#define GPIO_ALTPADCFGI_PAD35_SR_Pos (28UL) /*!< GPIO ALTPADCFGI: PAD35_SR (Bit 28) */ -#define GPIO_ALTPADCFGI_PAD35_SR_Msk (0x10000000UL) /*!< GPIO ALTPADCFGI: PAD35_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGI_PAD35_DS1_Pos (24UL) /*!< GPIO ALTPADCFGI: PAD35_DS1 (Bit 24) */ -#define GPIO_ALTPADCFGI_PAD35_DS1_Msk (0x1000000UL) /*!< GPIO ALTPADCFGI: PAD35_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGI_PAD34_SR_Pos (20UL) /*!< GPIO ALTPADCFGI: PAD34_SR (Bit 20) */ -#define GPIO_ALTPADCFGI_PAD34_SR_Msk (0x100000UL) /*!< GPIO ALTPADCFGI: PAD34_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGI_PAD34_DS1_Pos (16UL) /*!< GPIO ALTPADCFGI: PAD34_DS1 (Bit 16) */ -#define GPIO_ALTPADCFGI_PAD34_DS1_Msk (0x10000UL) /*!< GPIO ALTPADCFGI: PAD34_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGI_PAD33_SR_Pos (12UL) /*!< GPIO ALTPADCFGI: PAD33_SR (Bit 12) */ -#define GPIO_ALTPADCFGI_PAD33_SR_Msk (0x1000UL) /*!< GPIO ALTPADCFGI: PAD33_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGI_PAD33_DS1_Pos (8UL) /*!< GPIO ALTPADCFGI: PAD33_DS1 (Bit 8) */ -#define GPIO_ALTPADCFGI_PAD33_DS1_Msk (0x100UL) /*!< GPIO ALTPADCFGI: PAD33_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGI_PAD32_SR_Pos (4UL) /*!< GPIO ALTPADCFGI: PAD32_SR (Bit 4) */ -#define GPIO_ALTPADCFGI_PAD32_SR_Msk (0x10UL) /*!< GPIO ALTPADCFGI: PAD32_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGI_PAD32_DS1_Pos (0UL) /*!< GPIO ALTPADCFGI: PAD32_DS1 (Bit 0) */ -#define GPIO_ALTPADCFGI_PAD32_DS1_Msk (0x1UL) /*!< GPIO ALTPADCFGI: PAD32_DS1 (Bitfield-Mask: 0x01) */ -/* ====================================================== ALTPADCFGJ ======================================================= */ -#define GPIO_ALTPADCFGJ_PAD39_SR_Pos (28UL) /*!< GPIO ALTPADCFGJ: PAD39_SR (Bit 28) */ -#define GPIO_ALTPADCFGJ_PAD39_SR_Msk (0x10000000UL) /*!< GPIO ALTPADCFGJ: PAD39_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGJ_PAD39_DS1_Pos (24UL) /*!< GPIO ALTPADCFGJ: PAD39_DS1 (Bit 24) */ -#define GPIO_ALTPADCFGJ_PAD39_DS1_Msk (0x1000000UL) /*!< GPIO ALTPADCFGJ: PAD39_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGJ_PAD38_SR_Pos (20UL) /*!< GPIO ALTPADCFGJ: PAD38_SR (Bit 20) */ -#define GPIO_ALTPADCFGJ_PAD38_SR_Msk (0x100000UL) /*!< GPIO ALTPADCFGJ: PAD38_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGJ_PAD38_DS1_Pos (16UL) /*!< GPIO ALTPADCFGJ: PAD38_DS1 (Bit 16) */ -#define GPIO_ALTPADCFGJ_PAD38_DS1_Msk (0x10000UL) /*!< GPIO ALTPADCFGJ: PAD38_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGJ_PAD37_SR_Pos (12UL) /*!< GPIO ALTPADCFGJ: PAD37_SR (Bit 12) */ -#define GPIO_ALTPADCFGJ_PAD37_SR_Msk (0x1000UL) /*!< GPIO ALTPADCFGJ: PAD37_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGJ_PAD37_DS1_Pos (8UL) /*!< GPIO ALTPADCFGJ: PAD37_DS1 (Bit 8) */ -#define GPIO_ALTPADCFGJ_PAD37_DS1_Msk (0x100UL) /*!< GPIO ALTPADCFGJ: PAD37_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGJ_PAD36_SR_Pos (4UL) /*!< GPIO ALTPADCFGJ: PAD36_SR (Bit 4) */ -#define GPIO_ALTPADCFGJ_PAD36_SR_Msk (0x10UL) /*!< GPIO ALTPADCFGJ: PAD36_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGJ_PAD36_DS1_Pos (0UL) /*!< GPIO ALTPADCFGJ: PAD36_DS1 (Bit 0) */ -#define GPIO_ALTPADCFGJ_PAD36_DS1_Msk (0x1UL) /*!< GPIO ALTPADCFGJ: PAD36_DS1 (Bitfield-Mask: 0x01) */ -/* ====================================================== ALTPADCFGK ======================================================= */ -#define GPIO_ALTPADCFGK_PAD43_SR_Pos (28UL) /*!< GPIO ALTPADCFGK: PAD43_SR (Bit 28) */ -#define GPIO_ALTPADCFGK_PAD43_SR_Msk (0x10000000UL) /*!< GPIO ALTPADCFGK: PAD43_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGK_PAD43_DS1_Pos (24UL) /*!< GPIO ALTPADCFGK: PAD43_DS1 (Bit 24) */ -#define GPIO_ALTPADCFGK_PAD43_DS1_Msk (0x1000000UL) /*!< GPIO ALTPADCFGK: PAD43_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGK_PAD42_SR_Pos (20UL) /*!< GPIO ALTPADCFGK: PAD42_SR (Bit 20) */ -#define GPIO_ALTPADCFGK_PAD42_SR_Msk (0x100000UL) /*!< GPIO ALTPADCFGK: PAD42_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGK_PAD42_DS1_Pos (16UL) /*!< GPIO ALTPADCFGK: PAD42_DS1 (Bit 16) */ -#define GPIO_ALTPADCFGK_PAD42_DS1_Msk (0x10000UL) /*!< GPIO ALTPADCFGK: PAD42_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGK_PAD41_SR_Pos (12UL) /*!< GPIO ALTPADCFGK: PAD41_SR (Bit 12) */ -#define GPIO_ALTPADCFGK_PAD41_SR_Msk (0x1000UL) /*!< GPIO ALTPADCFGK: PAD41_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGK_PAD41_DS1_Pos (8UL) /*!< GPIO ALTPADCFGK: PAD41_DS1 (Bit 8) */ -#define GPIO_ALTPADCFGK_PAD41_DS1_Msk (0x100UL) /*!< GPIO ALTPADCFGK: PAD41_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGK_PAD40_SR_Pos (4UL) /*!< GPIO ALTPADCFGK: PAD40_SR (Bit 4) */ -#define GPIO_ALTPADCFGK_PAD40_SR_Msk (0x10UL) /*!< GPIO ALTPADCFGK: PAD40_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGK_PAD40_DS1_Pos (0UL) /*!< GPIO ALTPADCFGK: PAD40_DS1 (Bit 0) */ -#define GPIO_ALTPADCFGK_PAD40_DS1_Msk (0x1UL) /*!< GPIO ALTPADCFGK: PAD40_DS1 (Bitfield-Mask: 0x01) */ -/* ====================================================== ALTPADCFGL ======================================================= */ -#define GPIO_ALTPADCFGL_PAD47_SR_Pos (28UL) /*!< GPIO ALTPADCFGL: PAD47_SR (Bit 28) */ -#define GPIO_ALTPADCFGL_PAD47_SR_Msk (0x10000000UL) /*!< GPIO ALTPADCFGL: PAD47_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGL_PAD47_DS1_Pos (24UL) /*!< GPIO ALTPADCFGL: PAD47_DS1 (Bit 24) */ -#define GPIO_ALTPADCFGL_PAD47_DS1_Msk (0x1000000UL) /*!< GPIO ALTPADCFGL: PAD47_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGL_PAD46_SR_Pos (20UL) /*!< GPIO ALTPADCFGL: PAD46_SR (Bit 20) */ -#define GPIO_ALTPADCFGL_PAD46_SR_Msk (0x100000UL) /*!< GPIO ALTPADCFGL: PAD46_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGL_PAD46_DS1_Pos (16UL) /*!< GPIO ALTPADCFGL: PAD46_DS1 (Bit 16) */ -#define GPIO_ALTPADCFGL_PAD46_DS1_Msk (0x10000UL) /*!< GPIO ALTPADCFGL: PAD46_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGL_PAD45_SR_Pos (12UL) /*!< GPIO ALTPADCFGL: PAD45_SR (Bit 12) */ -#define GPIO_ALTPADCFGL_PAD45_SR_Msk (0x1000UL) /*!< GPIO ALTPADCFGL: PAD45_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGL_PAD45_DS1_Pos (8UL) /*!< GPIO ALTPADCFGL: PAD45_DS1 (Bit 8) */ -#define GPIO_ALTPADCFGL_PAD45_DS1_Msk (0x100UL) /*!< GPIO ALTPADCFGL: PAD45_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGL_PAD44_SR_Pos (4UL) /*!< GPIO ALTPADCFGL: PAD44_SR (Bit 4) */ -#define GPIO_ALTPADCFGL_PAD44_SR_Msk (0x10UL) /*!< GPIO ALTPADCFGL: PAD44_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGL_PAD44_DS1_Pos (0UL) /*!< GPIO ALTPADCFGL: PAD44_DS1 (Bit 0) */ -#define GPIO_ALTPADCFGL_PAD44_DS1_Msk (0x1UL) /*!< GPIO ALTPADCFGL: PAD44_DS1 (Bitfield-Mask: 0x01) */ -/* ====================================================== ALTPADCFGM ======================================================= */ -#define GPIO_ALTPADCFGM_PAD49_SR_Pos (12UL) /*!< GPIO ALTPADCFGM: PAD49_SR (Bit 12) */ -#define GPIO_ALTPADCFGM_PAD49_SR_Msk (0x1000UL) /*!< GPIO ALTPADCFGM: PAD49_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGM_PAD49_DS1_Pos (8UL) /*!< GPIO ALTPADCFGM: PAD49_DS1 (Bit 8) */ -#define GPIO_ALTPADCFGM_PAD49_DS1_Msk (0x100UL) /*!< GPIO ALTPADCFGM: PAD49_DS1 (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGM_PAD48_SR_Pos (4UL) /*!< GPIO ALTPADCFGM: PAD48_SR (Bit 4) */ -#define GPIO_ALTPADCFGM_PAD48_SR_Msk (0x10UL) /*!< GPIO ALTPADCFGM: PAD48_SR (Bitfield-Mask: 0x01) */ -#define GPIO_ALTPADCFGM_PAD48_DS1_Pos (0UL) /*!< GPIO ALTPADCFGM: PAD48_DS1 (Bit 0) */ -#define GPIO_ALTPADCFGM_PAD48_DS1_Msk (0x1UL) /*!< GPIO ALTPADCFGM: PAD48_DS1 (Bitfield-Mask: 0x01) */ -/* ========================================================= SCDET ========================================================= */ -#define GPIO_SCDET_SCDET_Pos (0UL) /*!< GPIO SCDET: SCDET (Bit 0) */ -#define GPIO_SCDET_SCDET_Msk (0x3fUL) /*!< GPIO SCDET: SCDET (Bitfield-Mask: 0x3f) */ -/* ======================================================== CTENCFG ======================================================== */ -#define GPIO_CTENCFG_EN31_Pos (31UL) /*!< GPIO CTENCFG: EN31 (Bit 31) */ -#define GPIO_CTENCFG_EN31_Msk (0x80000000UL) /*!< GPIO CTENCFG: EN31 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN30_Pos (30UL) /*!< GPIO CTENCFG: EN30 (Bit 30) */ -#define GPIO_CTENCFG_EN30_Msk (0x40000000UL) /*!< GPIO CTENCFG: EN30 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN29_Pos (29UL) /*!< GPIO CTENCFG: EN29 (Bit 29) */ -#define GPIO_CTENCFG_EN29_Msk (0x20000000UL) /*!< GPIO CTENCFG: EN29 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN28_Pos (28UL) /*!< GPIO CTENCFG: EN28 (Bit 28) */ -#define GPIO_CTENCFG_EN28_Msk (0x10000000UL) /*!< GPIO CTENCFG: EN28 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN27_Pos (27UL) /*!< GPIO CTENCFG: EN27 (Bit 27) */ -#define GPIO_CTENCFG_EN27_Msk (0x8000000UL) /*!< GPIO CTENCFG: EN27 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN26_Pos (26UL) /*!< GPIO CTENCFG: EN26 (Bit 26) */ -#define GPIO_CTENCFG_EN26_Msk (0x4000000UL) /*!< GPIO CTENCFG: EN26 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN25_Pos (25UL) /*!< GPIO CTENCFG: EN25 (Bit 25) */ -#define GPIO_CTENCFG_EN25_Msk (0x2000000UL) /*!< GPIO CTENCFG: EN25 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN24_Pos (24UL) /*!< GPIO CTENCFG: EN24 (Bit 24) */ -#define GPIO_CTENCFG_EN24_Msk (0x1000000UL) /*!< GPIO CTENCFG: EN24 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN23_Pos (23UL) /*!< GPIO CTENCFG: EN23 (Bit 23) */ -#define GPIO_CTENCFG_EN23_Msk (0x800000UL) /*!< GPIO CTENCFG: EN23 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN22_Pos (22UL) /*!< GPIO CTENCFG: EN22 (Bit 22) */ -#define GPIO_CTENCFG_EN22_Msk (0x400000UL) /*!< GPIO CTENCFG: EN22 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN21_Pos (21UL) /*!< GPIO CTENCFG: EN21 (Bit 21) */ -#define GPIO_CTENCFG_EN21_Msk (0x200000UL) /*!< GPIO CTENCFG: EN21 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN20_Pos (20UL) /*!< GPIO CTENCFG: EN20 (Bit 20) */ -#define GPIO_CTENCFG_EN20_Msk (0x100000UL) /*!< GPIO CTENCFG: EN20 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN19_Pos (19UL) /*!< GPIO CTENCFG: EN19 (Bit 19) */ -#define GPIO_CTENCFG_EN19_Msk (0x80000UL) /*!< GPIO CTENCFG: EN19 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN18_Pos (18UL) /*!< GPIO CTENCFG: EN18 (Bit 18) */ -#define GPIO_CTENCFG_EN18_Msk (0x40000UL) /*!< GPIO CTENCFG: EN18 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN17_Pos (17UL) /*!< GPIO CTENCFG: EN17 (Bit 17) */ -#define GPIO_CTENCFG_EN17_Msk (0x20000UL) /*!< GPIO CTENCFG: EN17 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN16_Pos (16UL) /*!< GPIO CTENCFG: EN16 (Bit 16) */ -#define GPIO_CTENCFG_EN16_Msk (0x10000UL) /*!< GPIO CTENCFG: EN16 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN15_Pos (15UL) /*!< GPIO CTENCFG: EN15 (Bit 15) */ -#define GPIO_CTENCFG_EN15_Msk (0x8000UL) /*!< GPIO CTENCFG: EN15 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN14_Pos (14UL) /*!< GPIO CTENCFG: EN14 (Bit 14) */ -#define GPIO_CTENCFG_EN14_Msk (0x4000UL) /*!< GPIO CTENCFG: EN14 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN13_Pos (13UL) /*!< GPIO CTENCFG: EN13 (Bit 13) */ -#define GPIO_CTENCFG_EN13_Msk (0x2000UL) /*!< GPIO CTENCFG: EN13 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN12_Pos (12UL) /*!< GPIO CTENCFG: EN12 (Bit 12) */ -#define GPIO_CTENCFG_EN12_Msk (0x1000UL) /*!< GPIO CTENCFG: EN12 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN11_Pos (11UL) /*!< GPIO CTENCFG: EN11 (Bit 11) */ -#define GPIO_CTENCFG_EN11_Msk (0x800UL) /*!< GPIO CTENCFG: EN11 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN10_Pos (10UL) /*!< GPIO CTENCFG: EN10 (Bit 10) */ -#define GPIO_CTENCFG_EN10_Msk (0x400UL) /*!< GPIO CTENCFG: EN10 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN9_Pos (9UL) /*!< GPIO CTENCFG: EN9 (Bit 9) */ -#define GPIO_CTENCFG_EN9_Msk (0x200UL) /*!< GPIO CTENCFG: EN9 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN8_Pos (8UL) /*!< GPIO CTENCFG: EN8 (Bit 8) */ -#define GPIO_CTENCFG_EN8_Msk (0x100UL) /*!< GPIO CTENCFG: EN8 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN7_Pos (7UL) /*!< GPIO CTENCFG: EN7 (Bit 7) */ -#define GPIO_CTENCFG_EN7_Msk (0x80UL) /*!< GPIO CTENCFG: EN7 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN6_Pos (6UL) /*!< GPIO CTENCFG: EN6 (Bit 6) */ -#define GPIO_CTENCFG_EN6_Msk (0x40UL) /*!< GPIO CTENCFG: EN6 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN5_Pos (5UL) /*!< GPIO CTENCFG: EN5 (Bit 5) */ -#define GPIO_CTENCFG_EN5_Msk (0x20UL) /*!< GPIO CTENCFG: EN5 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN4_Pos (4UL) /*!< GPIO CTENCFG: EN4 (Bit 4) */ -#define GPIO_CTENCFG_EN4_Msk (0x10UL) /*!< GPIO CTENCFG: EN4 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN3_Pos (3UL) /*!< GPIO CTENCFG: EN3 (Bit 3) */ -#define GPIO_CTENCFG_EN3_Msk (0x8UL) /*!< GPIO CTENCFG: EN3 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN2_Pos (2UL) /*!< GPIO CTENCFG: EN2 (Bit 2) */ -#define GPIO_CTENCFG_EN2_Msk (0x4UL) /*!< GPIO CTENCFG: EN2 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN1_Pos (1UL) /*!< GPIO CTENCFG: EN1 (Bit 1) */ -#define GPIO_CTENCFG_EN1_Msk (0x2UL) /*!< GPIO CTENCFG: EN1 (Bitfield-Mask: 0x01) */ -#define GPIO_CTENCFG_EN0_Pos (0UL) /*!< GPIO CTENCFG: EN0 (Bit 0) */ -#define GPIO_CTENCFG_EN0_Msk (0x1UL) /*!< GPIO CTENCFG: EN0 (Bitfield-Mask: 0x01) */ -/* ======================================================== INT0EN ========================================================= */ -#define GPIO_INT0EN_GPIO31_Pos (31UL) /*!< GPIO INT0EN: GPIO31 (Bit 31) */ -#define GPIO_INT0EN_GPIO31_Msk (0x80000000UL) /*!< GPIO INT0EN: GPIO31 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO30_Pos (30UL) /*!< GPIO INT0EN: GPIO30 (Bit 30) */ -#define GPIO_INT0EN_GPIO30_Msk (0x40000000UL) /*!< GPIO INT0EN: GPIO30 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO29_Pos (29UL) /*!< GPIO INT0EN: GPIO29 (Bit 29) */ -#define GPIO_INT0EN_GPIO29_Msk (0x20000000UL) /*!< GPIO INT0EN: GPIO29 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO28_Pos (28UL) /*!< GPIO INT0EN: GPIO28 (Bit 28) */ -#define GPIO_INT0EN_GPIO28_Msk (0x10000000UL) /*!< GPIO INT0EN: GPIO28 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO27_Pos (27UL) /*!< GPIO INT0EN: GPIO27 (Bit 27) */ -#define GPIO_INT0EN_GPIO27_Msk (0x8000000UL) /*!< GPIO INT0EN: GPIO27 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO26_Pos (26UL) /*!< GPIO INT0EN: GPIO26 (Bit 26) */ -#define GPIO_INT0EN_GPIO26_Msk (0x4000000UL) /*!< GPIO INT0EN: GPIO26 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO25_Pos (25UL) /*!< GPIO INT0EN: GPIO25 (Bit 25) */ -#define GPIO_INT0EN_GPIO25_Msk (0x2000000UL) /*!< GPIO INT0EN: GPIO25 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO24_Pos (24UL) /*!< GPIO INT0EN: GPIO24 (Bit 24) */ -#define GPIO_INT0EN_GPIO24_Msk (0x1000000UL) /*!< GPIO INT0EN: GPIO24 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO23_Pos (23UL) /*!< GPIO INT0EN: GPIO23 (Bit 23) */ -#define GPIO_INT0EN_GPIO23_Msk (0x800000UL) /*!< GPIO INT0EN: GPIO23 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO22_Pos (22UL) /*!< GPIO INT0EN: GPIO22 (Bit 22) */ -#define GPIO_INT0EN_GPIO22_Msk (0x400000UL) /*!< GPIO INT0EN: GPIO22 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO21_Pos (21UL) /*!< GPIO INT0EN: GPIO21 (Bit 21) */ -#define GPIO_INT0EN_GPIO21_Msk (0x200000UL) /*!< GPIO INT0EN: GPIO21 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO20_Pos (20UL) /*!< GPIO INT0EN: GPIO20 (Bit 20) */ -#define GPIO_INT0EN_GPIO20_Msk (0x100000UL) /*!< GPIO INT0EN: GPIO20 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO19_Pos (19UL) /*!< GPIO INT0EN: GPIO19 (Bit 19) */ -#define GPIO_INT0EN_GPIO19_Msk (0x80000UL) /*!< GPIO INT0EN: GPIO19 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO18_Pos (18UL) /*!< GPIO INT0EN: GPIO18 (Bit 18) */ -#define GPIO_INT0EN_GPIO18_Msk (0x40000UL) /*!< GPIO INT0EN: GPIO18 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO17_Pos (17UL) /*!< GPIO INT0EN: GPIO17 (Bit 17) */ -#define GPIO_INT0EN_GPIO17_Msk (0x20000UL) /*!< GPIO INT0EN: GPIO17 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO16_Pos (16UL) /*!< GPIO INT0EN: GPIO16 (Bit 16) */ -#define GPIO_INT0EN_GPIO16_Msk (0x10000UL) /*!< GPIO INT0EN: GPIO16 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO15_Pos (15UL) /*!< GPIO INT0EN: GPIO15 (Bit 15) */ -#define GPIO_INT0EN_GPIO15_Msk (0x8000UL) /*!< GPIO INT0EN: GPIO15 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO14_Pos (14UL) /*!< GPIO INT0EN: GPIO14 (Bit 14) */ -#define GPIO_INT0EN_GPIO14_Msk (0x4000UL) /*!< GPIO INT0EN: GPIO14 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO13_Pos (13UL) /*!< GPIO INT0EN: GPIO13 (Bit 13) */ -#define GPIO_INT0EN_GPIO13_Msk (0x2000UL) /*!< GPIO INT0EN: GPIO13 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO12_Pos (12UL) /*!< GPIO INT0EN: GPIO12 (Bit 12) */ -#define GPIO_INT0EN_GPIO12_Msk (0x1000UL) /*!< GPIO INT0EN: GPIO12 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO11_Pos (11UL) /*!< GPIO INT0EN: GPIO11 (Bit 11) */ -#define GPIO_INT0EN_GPIO11_Msk (0x800UL) /*!< GPIO INT0EN: GPIO11 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO10_Pos (10UL) /*!< GPIO INT0EN: GPIO10 (Bit 10) */ -#define GPIO_INT0EN_GPIO10_Msk (0x400UL) /*!< GPIO INT0EN: GPIO10 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO9_Pos (9UL) /*!< GPIO INT0EN: GPIO9 (Bit 9) */ -#define GPIO_INT0EN_GPIO9_Msk (0x200UL) /*!< GPIO INT0EN: GPIO9 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO8_Pos (8UL) /*!< GPIO INT0EN: GPIO8 (Bit 8) */ -#define GPIO_INT0EN_GPIO8_Msk (0x100UL) /*!< GPIO INT0EN: GPIO8 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO7_Pos (7UL) /*!< GPIO INT0EN: GPIO7 (Bit 7) */ -#define GPIO_INT0EN_GPIO7_Msk (0x80UL) /*!< GPIO INT0EN: GPIO7 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO6_Pos (6UL) /*!< GPIO INT0EN: GPIO6 (Bit 6) */ -#define GPIO_INT0EN_GPIO6_Msk (0x40UL) /*!< GPIO INT0EN: GPIO6 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO5_Pos (5UL) /*!< GPIO INT0EN: GPIO5 (Bit 5) */ -#define GPIO_INT0EN_GPIO5_Msk (0x20UL) /*!< GPIO INT0EN: GPIO5 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO4_Pos (4UL) /*!< GPIO INT0EN: GPIO4 (Bit 4) */ -#define GPIO_INT0EN_GPIO4_Msk (0x10UL) /*!< GPIO INT0EN: GPIO4 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO3_Pos (3UL) /*!< GPIO INT0EN: GPIO3 (Bit 3) */ -#define GPIO_INT0EN_GPIO3_Msk (0x8UL) /*!< GPIO INT0EN: GPIO3 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO2_Pos (2UL) /*!< GPIO INT0EN: GPIO2 (Bit 2) */ -#define GPIO_INT0EN_GPIO2_Msk (0x4UL) /*!< GPIO INT0EN: GPIO2 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO1_Pos (1UL) /*!< GPIO INT0EN: GPIO1 (Bit 1) */ -#define GPIO_INT0EN_GPIO1_Msk (0x2UL) /*!< GPIO INT0EN: GPIO1 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0EN_GPIO0_Pos (0UL) /*!< GPIO INT0EN: GPIO0 (Bit 0) */ -#define GPIO_INT0EN_GPIO0_Msk (0x1UL) /*!< GPIO INT0EN: GPIO0 (Bitfield-Mask: 0x01) */ -/* ======================================================= INT0STAT ======================================================== */ -#define GPIO_INT0STAT_GPIO31_Pos (31UL) /*!< GPIO INT0STAT: GPIO31 (Bit 31) */ -#define GPIO_INT0STAT_GPIO31_Msk (0x80000000UL) /*!< GPIO INT0STAT: GPIO31 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO30_Pos (30UL) /*!< GPIO INT0STAT: GPIO30 (Bit 30) */ -#define GPIO_INT0STAT_GPIO30_Msk (0x40000000UL) /*!< GPIO INT0STAT: GPIO30 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO29_Pos (29UL) /*!< GPIO INT0STAT: GPIO29 (Bit 29) */ -#define GPIO_INT0STAT_GPIO29_Msk (0x20000000UL) /*!< GPIO INT0STAT: GPIO29 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO28_Pos (28UL) /*!< GPIO INT0STAT: GPIO28 (Bit 28) */ -#define GPIO_INT0STAT_GPIO28_Msk (0x10000000UL) /*!< GPIO INT0STAT: GPIO28 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO27_Pos (27UL) /*!< GPIO INT0STAT: GPIO27 (Bit 27) */ -#define GPIO_INT0STAT_GPIO27_Msk (0x8000000UL) /*!< GPIO INT0STAT: GPIO27 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO26_Pos (26UL) /*!< GPIO INT0STAT: GPIO26 (Bit 26) */ -#define GPIO_INT0STAT_GPIO26_Msk (0x4000000UL) /*!< GPIO INT0STAT: GPIO26 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO25_Pos (25UL) /*!< GPIO INT0STAT: GPIO25 (Bit 25) */ -#define GPIO_INT0STAT_GPIO25_Msk (0x2000000UL) /*!< GPIO INT0STAT: GPIO25 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO24_Pos (24UL) /*!< GPIO INT0STAT: GPIO24 (Bit 24) */ -#define GPIO_INT0STAT_GPIO24_Msk (0x1000000UL) /*!< GPIO INT0STAT: GPIO24 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO23_Pos (23UL) /*!< GPIO INT0STAT: GPIO23 (Bit 23) */ -#define GPIO_INT0STAT_GPIO23_Msk (0x800000UL) /*!< GPIO INT0STAT: GPIO23 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO22_Pos (22UL) /*!< GPIO INT0STAT: GPIO22 (Bit 22) */ -#define GPIO_INT0STAT_GPIO22_Msk (0x400000UL) /*!< GPIO INT0STAT: GPIO22 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO21_Pos (21UL) /*!< GPIO INT0STAT: GPIO21 (Bit 21) */ -#define GPIO_INT0STAT_GPIO21_Msk (0x200000UL) /*!< GPIO INT0STAT: GPIO21 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO20_Pos (20UL) /*!< GPIO INT0STAT: GPIO20 (Bit 20) */ -#define GPIO_INT0STAT_GPIO20_Msk (0x100000UL) /*!< GPIO INT0STAT: GPIO20 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO19_Pos (19UL) /*!< GPIO INT0STAT: GPIO19 (Bit 19) */ -#define GPIO_INT0STAT_GPIO19_Msk (0x80000UL) /*!< GPIO INT0STAT: GPIO19 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO18_Pos (18UL) /*!< GPIO INT0STAT: GPIO18 (Bit 18) */ -#define GPIO_INT0STAT_GPIO18_Msk (0x40000UL) /*!< GPIO INT0STAT: GPIO18 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO17_Pos (17UL) /*!< GPIO INT0STAT: GPIO17 (Bit 17) */ -#define GPIO_INT0STAT_GPIO17_Msk (0x20000UL) /*!< GPIO INT0STAT: GPIO17 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO16_Pos (16UL) /*!< GPIO INT0STAT: GPIO16 (Bit 16) */ -#define GPIO_INT0STAT_GPIO16_Msk (0x10000UL) /*!< GPIO INT0STAT: GPIO16 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO15_Pos (15UL) /*!< GPIO INT0STAT: GPIO15 (Bit 15) */ -#define GPIO_INT0STAT_GPIO15_Msk (0x8000UL) /*!< GPIO INT0STAT: GPIO15 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO14_Pos (14UL) /*!< GPIO INT0STAT: GPIO14 (Bit 14) */ -#define GPIO_INT0STAT_GPIO14_Msk (0x4000UL) /*!< GPIO INT0STAT: GPIO14 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO13_Pos (13UL) /*!< GPIO INT0STAT: GPIO13 (Bit 13) */ -#define GPIO_INT0STAT_GPIO13_Msk (0x2000UL) /*!< GPIO INT0STAT: GPIO13 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO12_Pos (12UL) /*!< GPIO INT0STAT: GPIO12 (Bit 12) */ -#define GPIO_INT0STAT_GPIO12_Msk (0x1000UL) /*!< GPIO INT0STAT: GPIO12 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO11_Pos (11UL) /*!< GPIO INT0STAT: GPIO11 (Bit 11) */ -#define GPIO_INT0STAT_GPIO11_Msk (0x800UL) /*!< GPIO INT0STAT: GPIO11 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO10_Pos (10UL) /*!< GPIO INT0STAT: GPIO10 (Bit 10) */ -#define GPIO_INT0STAT_GPIO10_Msk (0x400UL) /*!< GPIO INT0STAT: GPIO10 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO9_Pos (9UL) /*!< GPIO INT0STAT: GPIO9 (Bit 9) */ -#define GPIO_INT0STAT_GPIO9_Msk (0x200UL) /*!< GPIO INT0STAT: GPIO9 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO8_Pos (8UL) /*!< GPIO INT0STAT: GPIO8 (Bit 8) */ -#define GPIO_INT0STAT_GPIO8_Msk (0x100UL) /*!< GPIO INT0STAT: GPIO8 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO7_Pos (7UL) /*!< GPIO INT0STAT: GPIO7 (Bit 7) */ -#define GPIO_INT0STAT_GPIO7_Msk (0x80UL) /*!< GPIO INT0STAT: GPIO7 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO6_Pos (6UL) /*!< GPIO INT0STAT: GPIO6 (Bit 6) */ -#define GPIO_INT0STAT_GPIO6_Msk (0x40UL) /*!< GPIO INT0STAT: GPIO6 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO5_Pos (5UL) /*!< GPIO INT0STAT: GPIO5 (Bit 5) */ -#define GPIO_INT0STAT_GPIO5_Msk (0x20UL) /*!< GPIO INT0STAT: GPIO5 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO4_Pos (4UL) /*!< GPIO INT0STAT: GPIO4 (Bit 4) */ -#define GPIO_INT0STAT_GPIO4_Msk (0x10UL) /*!< GPIO INT0STAT: GPIO4 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO3_Pos (3UL) /*!< GPIO INT0STAT: GPIO3 (Bit 3) */ -#define GPIO_INT0STAT_GPIO3_Msk (0x8UL) /*!< GPIO INT0STAT: GPIO3 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO2_Pos (2UL) /*!< GPIO INT0STAT: GPIO2 (Bit 2) */ -#define GPIO_INT0STAT_GPIO2_Msk (0x4UL) /*!< GPIO INT0STAT: GPIO2 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO1_Pos (1UL) /*!< GPIO INT0STAT: GPIO1 (Bit 1) */ -#define GPIO_INT0STAT_GPIO1_Msk (0x2UL) /*!< GPIO INT0STAT: GPIO1 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0STAT_GPIO0_Pos (0UL) /*!< GPIO INT0STAT: GPIO0 (Bit 0) */ -#define GPIO_INT0STAT_GPIO0_Msk (0x1UL) /*!< GPIO INT0STAT: GPIO0 (Bitfield-Mask: 0x01) */ -/* ======================================================== INT0CLR ======================================================== */ -#define GPIO_INT0CLR_GPIO31_Pos (31UL) /*!< GPIO INT0CLR: GPIO31 (Bit 31) */ -#define GPIO_INT0CLR_GPIO31_Msk (0x80000000UL) /*!< GPIO INT0CLR: GPIO31 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO30_Pos (30UL) /*!< GPIO INT0CLR: GPIO30 (Bit 30) */ -#define GPIO_INT0CLR_GPIO30_Msk (0x40000000UL) /*!< GPIO INT0CLR: GPIO30 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO29_Pos (29UL) /*!< GPIO INT0CLR: GPIO29 (Bit 29) */ -#define GPIO_INT0CLR_GPIO29_Msk (0x20000000UL) /*!< GPIO INT0CLR: GPIO29 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO28_Pos (28UL) /*!< GPIO INT0CLR: GPIO28 (Bit 28) */ -#define GPIO_INT0CLR_GPIO28_Msk (0x10000000UL) /*!< GPIO INT0CLR: GPIO28 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO27_Pos (27UL) /*!< GPIO INT0CLR: GPIO27 (Bit 27) */ -#define GPIO_INT0CLR_GPIO27_Msk (0x8000000UL) /*!< GPIO INT0CLR: GPIO27 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO26_Pos (26UL) /*!< GPIO INT0CLR: GPIO26 (Bit 26) */ -#define GPIO_INT0CLR_GPIO26_Msk (0x4000000UL) /*!< GPIO INT0CLR: GPIO26 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO25_Pos (25UL) /*!< GPIO INT0CLR: GPIO25 (Bit 25) */ -#define GPIO_INT0CLR_GPIO25_Msk (0x2000000UL) /*!< GPIO INT0CLR: GPIO25 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO24_Pos (24UL) /*!< GPIO INT0CLR: GPIO24 (Bit 24) */ -#define GPIO_INT0CLR_GPIO24_Msk (0x1000000UL) /*!< GPIO INT0CLR: GPIO24 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO23_Pos (23UL) /*!< GPIO INT0CLR: GPIO23 (Bit 23) */ -#define GPIO_INT0CLR_GPIO23_Msk (0x800000UL) /*!< GPIO INT0CLR: GPIO23 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO22_Pos (22UL) /*!< GPIO INT0CLR: GPIO22 (Bit 22) */ -#define GPIO_INT0CLR_GPIO22_Msk (0x400000UL) /*!< GPIO INT0CLR: GPIO22 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO21_Pos (21UL) /*!< GPIO INT0CLR: GPIO21 (Bit 21) */ -#define GPIO_INT0CLR_GPIO21_Msk (0x200000UL) /*!< GPIO INT0CLR: GPIO21 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO20_Pos (20UL) /*!< GPIO INT0CLR: GPIO20 (Bit 20) */ -#define GPIO_INT0CLR_GPIO20_Msk (0x100000UL) /*!< GPIO INT0CLR: GPIO20 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO19_Pos (19UL) /*!< GPIO INT0CLR: GPIO19 (Bit 19) */ -#define GPIO_INT0CLR_GPIO19_Msk (0x80000UL) /*!< GPIO INT0CLR: GPIO19 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO18_Pos (18UL) /*!< GPIO INT0CLR: GPIO18 (Bit 18) */ -#define GPIO_INT0CLR_GPIO18_Msk (0x40000UL) /*!< GPIO INT0CLR: GPIO18 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO17_Pos (17UL) /*!< GPIO INT0CLR: GPIO17 (Bit 17) */ -#define GPIO_INT0CLR_GPIO17_Msk (0x20000UL) /*!< GPIO INT0CLR: GPIO17 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO16_Pos (16UL) /*!< GPIO INT0CLR: GPIO16 (Bit 16) */ -#define GPIO_INT0CLR_GPIO16_Msk (0x10000UL) /*!< GPIO INT0CLR: GPIO16 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO15_Pos (15UL) /*!< GPIO INT0CLR: GPIO15 (Bit 15) */ -#define GPIO_INT0CLR_GPIO15_Msk (0x8000UL) /*!< GPIO INT0CLR: GPIO15 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO14_Pos (14UL) /*!< GPIO INT0CLR: GPIO14 (Bit 14) */ -#define GPIO_INT0CLR_GPIO14_Msk (0x4000UL) /*!< GPIO INT0CLR: GPIO14 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO13_Pos (13UL) /*!< GPIO INT0CLR: GPIO13 (Bit 13) */ -#define GPIO_INT0CLR_GPIO13_Msk (0x2000UL) /*!< GPIO INT0CLR: GPIO13 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO12_Pos (12UL) /*!< GPIO INT0CLR: GPIO12 (Bit 12) */ -#define GPIO_INT0CLR_GPIO12_Msk (0x1000UL) /*!< GPIO INT0CLR: GPIO12 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO11_Pos (11UL) /*!< GPIO INT0CLR: GPIO11 (Bit 11) */ -#define GPIO_INT0CLR_GPIO11_Msk (0x800UL) /*!< GPIO INT0CLR: GPIO11 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO10_Pos (10UL) /*!< GPIO INT0CLR: GPIO10 (Bit 10) */ -#define GPIO_INT0CLR_GPIO10_Msk (0x400UL) /*!< GPIO INT0CLR: GPIO10 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO9_Pos (9UL) /*!< GPIO INT0CLR: GPIO9 (Bit 9) */ -#define GPIO_INT0CLR_GPIO9_Msk (0x200UL) /*!< GPIO INT0CLR: GPIO9 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO8_Pos (8UL) /*!< GPIO INT0CLR: GPIO8 (Bit 8) */ -#define GPIO_INT0CLR_GPIO8_Msk (0x100UL) /*!< GPIO INT0CLR: GPIO8 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO7_Pos (7UL) /*!< GPIO INT0CLR: GPIO7 (Bit 7) */ -#define GPIO_INT0CLR_GPIO7_Msk (0x80UL) /*!< GPIO INT0CLR: GPIO7 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO6_Pos (6UL) /*!< GPIO INT0CLR: GPIO6 (Bit 6) */ -#define GPIO_INT0CLR_GPIO6_Msk (0x40UL) /*!< GPIO INT0CLR: GPIO6 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO5_Pos (5UL) /*!< GPIO INT0CLR: GPIO5 (Bit 5) */ -#define GPIO_INT0CLR_GPIO5_Msk (0x20UL) /*!< GPIO INT0CLR: GPIO5 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO4_Pos (4UL) /*!< GPIO INT0CLR: GPIO4 (Bit 4) */ -#define GPIO_INT0CLR_GPIO4_Msk (0x10UL) /*!< GPIO INT0CLR: GPIO4 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO3_Pos (3UL) /*!< GPIO INT0CLR: GPIO3 (Bit 3) */ -#define GPIO_INT0CLR_GPIO3_Msk (0x8UL) /*!< GPIO INT0CLR: GPIO3 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO2_Pos (2UL) /*!< GPIO INT0CLR: GPIO2 (Bit 2) */ -#define GPIO_INT0CLR_GPIO2_Msk (0x4UL) /*!< GPIO INT0CLR: GPIO2 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO1_Pos (1UL) /*!< GPIO INT0CLR: GPIO1 (Bit 1) */ -#define GPIO_INT0CLR_GPIO1_Msk (0x2UL) /*!< GPIO INT0CLR: GPIO1 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0CLR_GPIO0_Pos (0UL) /*!< GPIO INT0CLR: GPIO0 (Bit 0) */ -#define GPIO_INT0CLR_GPIO0_Msk (0x1UL) /*!< GPIO INT0CLR: GPIO0 (Bitfield-Mask: 0x01) */ -/* ======================================================== INT0SET ======================================================== */ -#define GPIO_INT0SET_GPIO31_Pos (31UL) /*!< GPIO INT0SET: GPIO31 (Bit 31) */ -#define GPIO_INT0SET_GPIO31_Msk (0x80000000UL) /*!< GPIO INT0SET: GPIO31 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO30_Pos (30UL) /*!< GPIO INT0SET: GPIO30 (Bit 30) */ -#define GPIO_INT0SET_GPIO30_Msk (0x40000000UL) /*!< GPIO INT0SET: GPIO30 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO29_Pos (29UL) /*!< GPIO INT0SET: GPIO29 (Bit 29) */ -#define GPIO_INT0SET_GPIO29_Msk (0x20000000UL) /*!< GPIO INT0SET: GPIO29 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO28_Pos (28UL) /*!< GPIO INT0SET: GPIO28 (Bit 28) */ -#define GPIO_INT0SET_GPIO28_Msk (0x10000000UL) /*!< GPIO INT0SET: GPIO28 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO27_Pos (27UL) /*!< GPIO INT0SET: GPIO27 (Bit 27) */ -#define GPIO_INT0SET_GPIO27_Msk (0x8000000UL) /*!< GPIO INT0SET: GPIO27 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO26_Pos (26UL) /*!< GPIO INT0SET: GPIO26 (Bit 26) */ -#define GPIO_INT0SET_GPIO26_Msk (0x4000000UL) /*!< GPIO INT0SET: GPIO26 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO25_Pos (25UL) /*!< GPIO INT0SET: GPIO25 (Bit 25) */ -#define GPIO_INT0SET_GPIO25_Msk (0x2000000UL) /*!< GPIO INT0SET: GPIO25 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO24_Pos (24UL) /*!< GPIO INT0SET: GPIO24 (Bit 24) */ -#define GPIO_INT0SET_GPIO24_Msk (0x1000000UL) /*!< GPIO INT0SET: GPIO24 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO23_Pos (23UL) /*!< GPIO INT0SET: GPIO23 (Bit 23) */ -#define GPIO_INT0SET_GPIO23_Msk (0x800000UL) /*!< GPIO INT0SET: GPIO23 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO22_Pos (22UL) /*!< GPIO INT0SET: GPIO22 (Bit 22) */ -#define GPIO_INT0SET_GPIO22_Msk (0x400000UL) /*!< GPIO INT0SET: GPIO22 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO21_Pos (21UL) /*!< GPIO INT0SET: GPIO21 (Bit 21) */ -#define GPIO_INT0SET_GPIO21_Msk (0x200000UL) /*!< GPIO INT0SET: GPIO21 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO20_Pos (20UL) /*!< GPIO INT0SET: GPIO20 (Bit 20) */ -#define GPIO_INT0SET_GPIO20_Msk (0x100000UL) /*!< GPIO INT0SET: GPIO20 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO19_Pos (19UL) /*!< GPIO INT0SET: GPIO19 (Bit 19) */ -#define GPIO_INT0SET_GPIO19_Msk (0x80000UL) /*!< GPIO INT0SET: GPIO19 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO18_Pos (18UL) /*!< GPIO INT0SET: GPIO18 (Bit 18) */ -#define GPIO_INT0SET_GPIO18_Msk (0x40000UL) /*!< GPIO INT0SET: GPIO18 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO17_Pos (17UL) /*!< GPIO INT0SET: GPIO17 (Bit 17) */ -#define GPIO_INT0SET_GPIO17_Msk (0x20000UL) /*!< GPIO INT0SET: GPIO17 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO16_Pos (16UL) /*!< GPIO INT0SET: GPIO16 (Bit 16) */ -#define GPIO_INT0SET_GPIO16_Msk (0x10000UL) /*!< GPIO INT0SET: GPIO16 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO15_Pos (15UL) /*!< GPIO INT0SET: GPIO15 (Bit 15) */ -#define GPIO_INT0SET_GPIO15_Msk (0x8000UL) /*!< GPIO INT0SET: GPIO15 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO14_Pos (14UL) /*!< GPIO INT0SET: GPIO14 (Bit 14) */ -#define GPIO_INT0SET_GPIO14_Msk (0x4000UL) /*!< GPIO INT0SET: GPIO14 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO13_Pos (13UL) /*!< GPIO INT0SET: GPIO13 (Bit 13) */ -#define GPIO_INT0SET_GPIO13_Msk (0x2000UL) /*!< GPIO INT0SET: GPIO13 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO12_Pos (12UL) /*!< GPIO INT0SET: GPIO12 (Bit 12) */ -#define GPIO_INT0SET_GPIO12_Msk (0x1000UL) /*!< GPIO INT0SET: GPIO12 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO11_Pos (11UL) /*!< GPIO INT0SET: GPIO11 (Bit 11) */ -#define GPIO_INT0SET_GPIO11_Msk (0x800UL) /*!< GPIO INT0SET: GPIO11 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO10_Pos (10UL) /*!< GPIO INT0SET: GPIO10 (Bit 10) */ -#define GPIO_INT0SET_GPIO10_Msk (0x400UL) /*!< GPIO INT0SET: GPIO10 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO9_Pos (9UL) /*!< GPIO INT0SET: GPIO9 (Bit 9) */ -#define GPIO_INT0SET_GPIO9_Msk (0x200UL) /*!< GPIO INT0SET: GPIO9 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO8_Pos (8UL) /*!< GPIO INT0SET: GPIO8 (Bit 8) */ -#define GPIO_INT0SET_GPIO8_Msk (0x100UL) /*!< GPIO INT0SET: GPIO8 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO7_Pos (7UL) /*!< GPIO INT0SET: GPIO7 (Bit 7) */ -#define GPIO_INT0SET_GPIO7_Msk (0x80UL) /*!< GPIO INT0SET: GPIO7 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO6_Pos (6UL) /*!< GPIO INT0SET: GPIO6 (Bit 6) */ -#define GPIO_INT0SET_GPIO6_Msk (0x40UL) /*!< GPIO INT0SET: GPIO6 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO5_Pos (5UL) /*!< GPIO INT0SET: GPIO5 (Bit 5) */ -#define GPIO_INT0SET_GPIO5_Msk (0x20UL) /*!< GPIO INT0SET: GPIO5 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO4_Pos (4UL) /*!< GPIO INT0SET: GPIO4 (Bit 4) */ -#define GPIO_INT0SET_GPIO4_Msk (0x10UL) /*!< GPIO INT0SET: GPIO4 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO3_Pos (3UL) /*!< GPIO INT0SET: GPIO3 (Bit 3) */ -#define GPIO_INT0SET_GPIO3_Msk (0x8UL) /*!< GPIO INT0SET: GPIO3 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO2_Pos (2UL) /*!< GPIO INT0SET: GPIO2 (Bit 2) */ -#define GPIO_INT0SET_GPIO2_Msk (0x4UL) /*!< GPIO INT0SET: GPIO2 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO1_Pos (1UL) /*!< GPIO INT0SET: GPIO1 (Bit 1) */ -#define GPIO_INT0SET_GPIO1_Msk (0x2UL) /*!< GPIO INT0SET: GPIO1 (Bitfield-Mask: 0x01) */ -#define GPIO_INT0SET_GPIO0_Pos (0UL) /*!< GPIO INT0SET: GPIO0 (Bit 0) */ -#define GPIO_INT0SET_GPIO0_Msk (0x1UL) /*!< GPIO INT0SET: GPIO0 (Bitfield-Mask: 0x01) */ -/* ======================================================== INT1EN ========================================================= */ -#define GPIO_INT1EN_GPIO49_Pos (17UL) /*!< GPIO INT1EN: GPIO49 (Bit 17) */ -#define GPIO_INT1EN_GPIO49_Msk (0x20000UL) /*!< GPIO INT1EN: GPIO49 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1EN_GPIO48_Pos (16UL) /*!< GPIO INT1EN: GPIO48 (Bit 16) */ -#define GPIO_INT1EN_GPIO48_Msk (0x10000UL) /*!< GPIO INT1EN: GPIO48 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1EN_GPIO47_Pos (15UL) /*!< GPIO INT1EN: GPIO47 (Bit 15) */ -#define GPIO_INT1EN_GPIO47_Msk (0x8000UL) /*!< GPIO INT1EN: GPIO47 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1EN_GPIO46_Pos (14UL) /*!< GPIO INT1EN: GPIO46 (Bit 14) */ -#define GPIO_INT1EN_GPIO46_Msk (0x4000UL) /*!< GPIO INT1EN: GPIO46 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1EN_GPIO45_Pos (13UL) /*!< GPIO INT1EN: GPIO45 (Bit 13) */ -#define GPIO_INT1EN_GPIO45_Msk (0x2000UL) /*!< GPIO INT1EN: GPIO45 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1EN_GPIO44_Pos (12UL) /*!< GPIO INT1EN: GPIO44 (Bit 12) */ -#define GPIO_INT1EN_GPIO44_Msk (0x1000UL) /*!< GPIO INT1EN: GPIO44 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1EN_GPIO43_Pos (11UL) /*!< GPIO INT1EN: GPIO43 (Bit 11) */ -#define GPIO_INT1EN_GPIO43_Msk (0x800UL) /*!< GPIO INT1EN: GPIO43 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1EN_GPIO42_Pos (10UL) /*!< GPIO INT1EN: GPIO42 (Bit 10) */ -#define GPIO_INT1EN_GPIO42_Msk (0x400UL) /*!< GPIO INT1EN: GPIO42 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1EN_GPIO41_Pos (9UL) /*!< GPIO INT1EN: GPIO41 (Bit 9) */ -#define GPIO_INT1EN_GPIO41_Msk (0x200UL) /*!< GPIO INT1EN: GPIO41 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1EN_GPIO40_Pos (8UL) /*!< GPIO INT1EN: GPIO40 (Bit 8) */ -#define GPIO_INT1EN_GPIO40_Msk (0x100UL) /*!< GPIO INT1EN: GPIO40 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1EN_GPIO39_Pos (7UL) /*!< GPIO INT1EN: GPIO39 (Bit 7) */ -#define GPIO_INT1EN_GPIO39_Msk (0x80UL) /*!< GPIO INT1EN: GPIO39 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1EN_GPIO38_Pos (6UL) /*!< GPIO INT1EN: GPIO38 (Bit 6) */ -#define GPIO_INT1EN_GPIO38_Msk (0x40UL) /*!< GPIO INT1EN: GPIO38 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1EN_GPIO37_Pos (5UL) /*!< GPIO INT1EN: GPIO37 (Bit 5) */ -#define GPIO_INT1EN_GPIO37_Msk (0x20UL) /*!< GPIO INT1EN: GPIO37 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1EN_GPIO36_Pos (4UL) /*!< GPIO INT1EN: GPIO36 (Bit 4) */ -#define GPIO_INT1EN_GPIO36_Msk (0x10UL) /*!< GPIO INT1EN: GPIO36 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1EN_GPIO35_Pos (3UL) /*!< GPIO INT1EN: GPIO35 (Bit 3) */ -#define GPIO_INT1EN_GPIO35_Msk (0x8UL) /*!< GPIO INT1EN: GPIO35 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1EN_GPIO34_Pos (2UL) /*!< GPIO INT1EN: GPIO34 (Bit 2) */ -#define GPIO_INT1EN_GPIO34_Msk (0x4UL) /*!< GPIO INT1EN: GPIO34 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1EN_GPIO33_Pos (1UL) /*!< GPIO INT1EN: GPIO33 (Bit 1) */ -#define GPIO_INT1EN_GPIO33_Msk (0x2UL) /*!< GPIO INT1EN: GPIO33 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1EN_GPIO32_Pos (0UL) /*!< GPIO INT1EN: GPIO32 (Bit 0) */ -#define GPIO_INT1EN_GPIO32_Msk (0x1UL) /*!< GPIO INT1EN: GPIO32 (Bitfield-Mask: 0x01) */ -/* ======================================================= INT1STAT ======================================================== */ -#define GPIO_INT1STAT_GPIO49_Pos (17UL) /*!< GPIO INT1STAT: GPIO49 (Bit 17) */ -#define GPIO_INT1STAT_GPIO49_Msk (0x20000UL) /*!< GPIO INT1STAT: GPIO49 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1STAT_GPIO48_Pos (16UL) /*!< GPIO INT1STAT: GPIO48 (Bit 16) */ -#define GPIO_INT1STAT_GPIO48_Msk (0x10000UL) /*!< GPIO INT1STAT: GPIO48 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1STAT_GPIO47_Pos (15UL) /*!< GPIO INT1STAT: GPIO47 (Bit 15) */ -#define GPIO_INT1STAT_GPIO47_Msk (0x8000UL) /*!< GPIO INT1STAT: GPIO47 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1STAT_GPIO46_Pos (14UL) /*!< GPIO INT1STAT: GPIO46 (Bit 14) */ -#define GPIO_INT1STAT_GPIO46_Msk (0x4000UL) /*!< GPIO INT1STAT: GPIO46 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1STAT_GPIO45_Pos (13UL) /*!< GPIO INT1STAT: GPIO45 (Bit 13) */ -#define GPIO_INT1STAT_GPIO45_Msk (0x2000UL) /*!< GPIO INT1STAT: GPIO45 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1STAT_GPIO44_Pos (12UL) /*!< GPIO INT1STAT: GPIO44 (Bit 12) */ -#define GPIO_INT1STAT_GPIO44_Msk (0x1000UL) /*!< GPIO INT1STAT: GPIO44 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1STAT_GPIO43_Pos (11UL) /*!< GPIO INT1STAT: GPIO43 (Bit 11) */ -#define GPIO_INT1STAT_GPIO43_Msk (0x800UL) /*!< GPIO INT1STAT: GPIO43 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1STAT_GPIO42_Pos (10UL) /*!< GPIO INT1STAT: GPIO42 (Bit 10) */ -#define GPIO_INT1STAT_GPIO42_Msk (0x400UL) /*!< GPIO INT1STAT: GPIO42 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1STAT_GPIO41_Pos (9UL) /*!< GPIO INT1STAT: GPIO41 (Bit 9) */ -#define GPIO_INT1STAT_GPIO41_Msk (0x200UL) /*!< GPIO INT1STAT: GPIO41 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1STAT_GPIO40_Pos (8UL) /*!< GPIO INT1STAT: GPIO40 (Bit 8) */ -#define GPIO_INT1STAT_GPIO40_Msk (0x100UL) /*!< GPIO INT1STAT: GPIO40 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1STAT_GPIO39_Pos (7UL) /*!< GPIO INT1STAT: GPIO39 (Bit 7) */ -#define GPIO_INT1STAT_GPIO39_Msk (0x80UL) /*!< GPIO INT1STAT: GPIO39 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1STAT_GPIO38_Pos (6UL) /*!< GPIO INT1STAT: GPIO38 (Bit 6) */ -#define GPIO_INT1STAT_GPIO38_Msk (0x40UL) /*!< GPIO INT1STAT: GPIO38 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1STAT_GPIO37_Pos (5UL) /*!< GPIO INT1STAT: GPIO37 (Bit 5) */ -#define GPIO_INT1STAT_GPIO37_Msk (0x20UL) /*!< GPIO INT1STAT: GPIO37 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1STAT_GPIO36_Pos (4UL) /*!< GPIO INT1STAT: GPIO36 (Bit 4) */ -#define GPIO_INT1STAT_GPIO36_Msk (0x10UL) /*!< GPIO INT1STAT: GPIO36 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1STAT_GPIO35_Pos (3UL) /*!< GPIO INT1STAT: GPIO35 (Bit 3) */ -#define GPIO_INT1STAT_GPIO35_Msk (0x8UL) /*!< GPIO INT1STAT: GPIO35 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1STAT_GPIO34_Pos (2UL) /*!< GPIO INT1STAT: GPIO34 (Bit 2) */ -#define GPIO_INT1STAT_GPIO34_Msk (0x4UL) /*!< GPIO INT1STAT: GPIO34 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1STAT_GPIO33_Pos (1UL) /*!< GPIO INT1STAT: GPIO33 (Bit 1) */ -#define GPIO_INT1STAT_GPIO33_Msk (0x2UL) /*!< GPIO INT1STAT: GPIO33 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1STAT_GPIO32_Pos (0UL) /*!< GPIO INT1STAT: GPIO32 (Bit 0) */ -#define GPIO_INT1STAT_GPIO32_Msk (0x1UL) /*!< GPIO INT1STAT: GPIO32 (Bitfield-Mask: 0x01) */ -/* ======================================================== INT1CLR ======================================================== */ -#define GPIO_INT1CLR_GPIO49_Pos (17UL) /*!< GPIO INT1CLR: GPIO49 (Bit 17) */ -#define GPIO_INT1CLR_GPIO49_Msk (0x20000UL) /*!< GPIO INT1CLR: GPIO49 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1CLR_GPIO48_Pos (16UL) /*!< GPIO INT1CLR: GPIO48 (Bit 16) */ -#define GPIO_INT1CLR_GPIO48_Msk (0x10000UL) /*!< GPIO INT1CLR: GPIO48 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1CLR_GPIO47_Pos (15UL) /*!< GPIO INT1CLR: GPIO47 (Bit 15) */ -#define GPIO_INT1CLR_GPIO47_Msk (0x8000UL) /*!< GPIO INT1CLR: GPIO47 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1CLR_GPIO46_Pos (14UL) /*!< GPIO INT1CLR: GPIO46 (Bit 14) */ -#define GPIO_INT1CLR_GPIO46_Msk (0x4000UL) /*!< GPIO INT1CLR: GPIO46 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1CLR_GPIO45_Pos (13UL) /*!< GPIO INT1CLR: GPIO45 (Bit 13) */ -#define GPIO_INT1CLR_GPIO45_Msk (0x2000UL) /*!< GPIO INT1CLR: GPIO45 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1CLR_GPIO44_Pos (12UL) /*!< GPIO INT1CLR: GPIO44 (Bit 12) */ -#define GPIO_INT1CLR_GPIO44_Msk (0x1000UL) /*!< GPIO INT1CLR: GPIO44 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1CLR_GPIO43_Pos (11UL) /*!< GPIO INT1CLR: GPIO43 (Bit 11) */ -#define GPIO_INT1CLR_GPIO43_Msk (0x800UL) /*!< GPIO INT1CLR: GPIO43 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1CLR_GPIO42_Pos (10UL) /*!< GPIO INT1CLR: GPIO42 (Bit 10) */ -#define GPIO_INT1CLR_GPIO42_Msk (0x400UL) /*!< GPIO INT1CLR: GPIO42 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1CLR_GPIO41_Pos (9UL) /*!< GPIO INT1CLR: GPIO41 (Bit 9) */ -#define GPIO_INT1CLR_GPIO41_Msk (0x200UL) /*!< GPIO INT1CLR: GPIO41 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1CLR_GPIO40_Pos (8UL) /*!< GPIO INT1CLR: GPIO40 (Bit 8) */ -#define GPIO_INT1CLR_GPIO40_Msk (0x100UL) /*!< GPIO INT1CLR: GPIO40 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1CLR_GPIO39_Pos (7UL) /*!< GPIO INT1CLR: GPIO39 (Bit 7) */ -#define GPIO_INT1CLR_GPIO39_Msk (0x80UL) /*!< GPIO INT1CLR: GPIO39 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1CLR_GPIO38_Pos (6UL) /*!< GPIO INT1CLR: GPIO38 (Bit 6) */ -#define GPIO_INT1CLR_GPIO38_Msk (0x40UL) /*!< GPIO INT1CLR: GPIO38 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1CLR_GPIO37_Pos (5UL) /*!< GPIO INT1CLR: GPIO37 (Bit 5) */ -#define GPIO_INT1CLR_GPIO37_Msk (0x20UL) /*!< GPIO INT1CLR: GPIO37 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1CLR_GPIO36_Pos (4UL) /*!< GPIO INT1CLR: GPIO36 (Bit 4) */ -#define GPIO_INT1CLR_GPIO36_Msk (0x10UL) /*!< GPIO INT1CLR: GPIO36 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1CLR_GPIO35_Pos (3UL) /*!< GPIO INT1CLR: GPIO35 (Bit 3) */ -#define GPIO_INT1CLR_GPIO35_Msk (0x8UL) /*!< GPIO INT1CLR: GPIO35 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1CLR_GPIO34_Pos (2UL) /*!< GPIO INT1CLR: GPIO34 (Bit 2) */ -#define GPIO_INT1CLR_GPIO34_Msk (0x4UL) /*!< GPIO INT1CLR: GPIO34 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1CLR_GPIO33_Pos (1UL) /*!< GPIO INT1CLR: GPIO33 (Bit 1) */ -#define GPIO_INT1CLR_GPIO33_Msk (0x2UL) /*!< GPIO INT1CLR: GPIO33 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1CLR_GPIO32_Pos (0UL) /*!< GPIO INT1CLR: GPIO32 (Bit 0) */ -#define GPIO_INT1CLR_GPIO32_Msk (0x1UL) /*!< GPIO INT1CLR: GPIO32 (Bitfield-Mask: 0x01) */ -/* ======================================================== INT1SET ======================================================== */ -#define GPIO_INT1SET_GPIO49_Pos (17UL) /*!< GPIO INT1SET: GPIO49 (Bit 17) */ -#define GPIO_INT1SET_GPIO49_Msk (0x20000UL) /*!< GPIO INT1SET: GPIO49 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1SET_GPIO48_Pos (16UL) /*!< GPIO INT1SET: GPIO48 (Bit 16) */ -#define GPIO_INT1SET_GPIO48_Msk (0x10000UL) /*!< GPIO INT1SET: GPIO48 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1SET_GPIO47_Pos (15UL) /*!< GPIO INT1SET: GPIO47 (Bit 15) */ -#define GPIO_INT1SET_GPIO47_Msk (0x8000UL) /*!< GPIO INT1SET: GPIO47 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1SET_GPIO46_Pos (14UL) /*!< GPIO INT1SET: GPIO46 (Bit 14) */ -#define GPIO_INT1SET_GPIO46_Msk (0x4000UL) /*!< GPIO INT1SET: GPIO46 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1SET_GPIO45_Pos (13UL) /*!< GPIO INT1SET: GPIO45 (Bit 13) */ -#define GPIO_INT1SET_GPIO45_Msk (0x2000UL) /*!< GPIO INT1SET: GPIO45 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1SET_GPIO44_Pos (12UL) /*!< GPIO INT1SET: GPIO44 (Bit 12) */ -#define GPIO_INT1SET_GPIO44_Msk (0x1000UL) /*!< GPIO INT1SET: GPIO44 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1SET_GPIO43_Pos (11UL) /*!< GPIO INT1SET: GPIO43 (Bit 11) */ -#define GPIO_INT1SET_GPIO43_Msk (0x800UL) /*!< GPIO INT1SET: GPIO43 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1SET_GPIO42_Pos (10UL) /*!< GPIO INT1SET: GPIO42 (Bit 10) */ -#define GPIO_INT1SET_GPIO42_Msk (0x400UL) /*!< GPIO INT1SET: GPIO42 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1SET_GPIO41_Pos (9UL) /*!< GPIO INT1SET: GPIO41 (Bit 9) */ -#define GPIO_INT1SET_GPIO41_Msk (0x200UL) /*!< GPIO INT1SET: GPIO41 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1SET_GPIO40_Pos (8UL) /*!< GPIO INT1SET: GPIO40 (Bit 8) */ -#define GPIO_INT1SET_GPIO40_Msk (0x100UL) /*!< GPIO INT1SET: GPIO40 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1SET_GPIO39_Pos (7UL) /*!< GPIO INT1SET: GPIO39 (Bit 7) */ -#define GPIO_INT1SET_GPIO39_Msk (0x80UL) /*!< GPIO INT1SET: GPIO39 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1SET_GPIO38_Pos (6UL) /*!< GPIO INT1SET: GPIO38 (Bit 6) */ -#define GPIO_INT1SET_GPIO38_Msk (0x40UL) /*!< GPIO INT1SET: GPIO38 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1SET_GPIO37_Pos (5UL) /*!< GPIO INT1SET: GPIO37 (Bit 5) */ -#define GPIO_INT1SET_GPIO37_Msk (0x20UL) /*!< GPIO INT1SET: GPIO37 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1SET_GPIO36_Pos (4UL) /*!< GPIO INT1SET: GPIO36 (Bit 4) */ -#define GPIO_INT1SET_GPIO36_Msk (0x10UL) /*!< GPIO INT1SET: GPIO36 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1SET_GPIO35_Pos (3UL) /*!< GPIO INT1SET: GPIO35 (Bit 3) */ -#define GPIO_INT1SET_GPIO35_Msk (0x8UL) /*!< GPIO INT1SET: GPIO35 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1SET_GPIO34_Pos (2UL) /*!< GPIO INT1SET: GPIO34 (Bit 2) */ -#define GPIO_INT1SET_GPIO34_Msk (0x4UL) /*!< GPIO INT1SET: GPIO34 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1SET_GPIO33_Pos (1UL) /*!< GPIO INT1SET: GPIO33 (Bit 1) */ -#define GPIO_INT1SET_GPIO33_Msk (0x2UL) /*!< GPIO INT1SET: GPIO33 (Bitfield-Mask: 0x01) */ -#define GPIO_INT1SET_GPIO32_Pos (0UL) /*!< GPIO INT1SET: GPIO32 (Bit 0) */ -#define GPIO_INT1SET_GPIO32_Msk (0x1UL) /*!< GPIO INT1SET: GPIO32 (Bitfield-Mask: 0x01) */ - - -/* =========================================================================================================================== */ -/* ================ IOM0 ================ */ -/* =========================================================================================================================== */ - -/* ========================================================= FIFO ========================================================== */ -#define IOM0_FIFO_FIFO_Pos (0UL) /*!< IOM0 FIFO: FIFO (Bit 0) */ -#define IOM0_FIFO_FIFO_Msk (0xffffffffUL) /*!< IOM0 FIFO: FIFO (Bitfield-Mask: 0xffffffff) */ -/* ======================================================== FIFOPTR ======================================================== */ -#define IOM0_FIFOPTR_FIFO1REM_Pos (24UL) /*!< IOM0 FIFOPTR: FIFO1REM (Bit 24) */ -#define IOM0_FIFOPTR_FIFO1REM_Msk (0xff000000UL) /*!< IOM0 FIFOPTR: FIFO1REM (Bitfield-Mask: 0xff) */ -#define IOM0_FIFOPTR_FIFO1SIZ_Pos (16UL) /*!< IOM0 FIFOPTR: FIFO1SIZ (Bit 16) */ -#define IOM0_FIFOPTR_FIFO1SIZ_Msk (0xff0000UL) /*!< IOM0 FIFOPTR: FIFO1SIZ (Bitfield-Mask: 0xff) */ -#define IOM0_FIFOPTR_FIFO0REM_Pos (8UL) /*!< IOM0 FIFOPTR: FIFO0REM (Bit 8) */ -#define IOM0_FIFOPTR_FIFO0REM_Msk (0xff00UL) /*!< IOM0 FIFOPTR: FIFO0REM (Bitfield-Mask: 0xff) */ -#define IOM0_FIFOPTR_FIFO0SIZ_Pos (0UL) /*!< IOM0 FIFOPTR: FIFO0SIZ (Bit 0) */ -#define IOM0_FIFOPTR_FIFO0SIZ_Msk (0xffUL) /*!< IOM0 FIFOPTR: FIFO0SIZ (Bitfield-Mask: 0xff) */ -/* ======================================================== FIFOTHR ======================================================== */ -#define IOM0_FIFOTHR_FIFOWTHR_Pos (8UL) /*!< IOM0 FIFOTHR: FIFOWTHR (Bit 8) */ -#define IOM0_FIFOTHR_FIFOWTHR_Msk (0x3f00UL) /*!< IOM0 FIFOTHR: FIFOWTHR (Bitfield-Mask: 0x3f) */ -#define IOM0_FIFOTHR_FIFORTHR_Pos (0UL) /*!< IOM0 FIFOTHR: FIFORTHR (Bit 0) */ -#define IOM0_FIFOTHR_FIFORTHR_Msk (0x3fUL) /*!< IOM0 FIFOTHR: FIFORTHR (Bitfield-Mask: 0x3f) */ -/* ======================================================== FIFOPOP ======================================================== */ -#define IOM0_FIFOPOP_FIFODOUT_Pos (0UL) /*!< IOM0 FIFOPOP: FIFODOUT (Bit 0) */ -#define IOM0_FIFOPOP_FIFODOUT_Msk (0xffffffffUL) /*!< IOM0 FIFOPOP: FIFODOUT (Bitfield-Mask: 0xffffffff) */ -/* ======================================================= FIFOPUSH ======================================================== */ -#define IOM0_FIFOPUSH_FIFODIN_Pos (0UL) /*!< IOM0 FIFOPUSH: FIFODIN (Bit 0) */ -#define IOM0_FIFOPUSH_FIFODIN_Msk (0xffffffffUL) /*!< IOM0 FIFOPUSH: FIFODIN (Bitfield-Mask: 0xffffffff) */ -/* ======================================================= FIFOCTRL ======================================================== */ -#define IOM0_FIFOCTRL_FIFORSTN_Pos (1UL) /*!< IOM0 FIFOCTRL: FIFORSTN (Bit 1) */ -#define IOM0_FIFOCTRL_FIFORSTN_Msk (0x2UL) /*!< IOM0 FIFOCTRL: FIFORSTN (Bitfield-Mask: 0x01) */ -#define IOM0_FIFOCTRL_POPWR_Pos (0UL) /*!< IOM0 FIFOCTRL: POPWR (Bit 0) */ -#define IOM0_FIFOCTRL_POPWR_Msk (0x1UL) /*!< IOM0 FIFOCTRL: POPWR (Bitfield-Mask: 0x01) */ -/* ======================================================== FIFOLOC ======================================================== */ -#define IOM0_FIFOLOC_FIFORPTR_Pos (8UL) /*!< IOM0 FIFOLOC: FIFORPTR (Bit 8) */ -#define IOM0_FIFOLOC_FIFORPTR_Msk (0xf00UL) /*!< IOM0 FIFOLOC: FIFORPTR (Bitfield-Mask: 0x0f) */ -#define IOM0_FIFOLOC_FIFOWPTR_Pos (0UL) /*!< IOM0 FIFOLOC: FIFOWPTR (Bit 0) */ -#define IOM0_FIFOLOC_FIFOWPTR_Msk (0xfUL) /*!< IOM0 FIFOLOC: FIFOWPTR (Bitfield-Mask: 0x0f) */ -/* ========================================================= INTEN ========================================================= */ -#define IOM0_INTEN_CQERR_Pos (14UL) /*!< IOM0 INTEN: CQERR (Bit 14) */ -#define IOM0_INTEN_CQERR_Msk (0x4000UL) /*!< IOM0 INTEN: CQERR (Bitfield-Mask: 0x01) */ -#define IOM0_INTEN_CQUPD_Pos (13UL) /*!< IOM0 INTEN: CQUPD (Bit 13) */ -#define IOM0_INTEN_CQUPD_Msk (0x2000UL) /*!< IOM0 INTEN: CQUPD (Bitfield-Mask: 0x01) */ -#define IOM0_INTEN_CQPAUSED_Pos (12UL) /*!< IOM0 INTEN: CQPAUSED (Bit 12) */ -#define IOM0_INTEN_CQPAUSED_Msk (0x1000UL) /*!< IOM0 INTEN: CQPAUSED (Bitfield-Mask: 0x01) */ -#define IOM0_INTEN_DERR_Pos (11UL) /*!< IOM0 INTEN: DERR (Bit 11) */ -#define IOM0_INTEN_DERR_Msk (0x800UL) /*!< IOM0 INTEN: DERR (Bitfield-Mask: 0x01) */ -#define IOM0_INTEN_DCMP_Pos (10UL) /*!< IOM0 INTEN: DCMP (Bit 10) */ -#define IOM0_INTEN_DCMP_Msk (0x400UL) /*!< IOM0 INTEN: DCMP (Bitfield-Mask: 0x01) */ -#define IOM0_INTEN_ARB_Pos (9UL) /*!< IOM0 INTEN: ARB (Bit 9) */ -#define IOM0_INTEN_ARB_Msk (0x200UL) /*!< IOM0 INTEN: ARB (Bitfield-Mask: 0x01) */ -#define IOM0_INTEN_STOP_Pos (8UL) /*!< IOM0 INTEN: STOP (Bit 8) */ -#define IOM0_INTEN_STOP_Msk (0x100UL) /*!< IOM0 INTEN: STOP (Bitfield-Mask: 0x01) */ -#define IOM0_INTEN_START_Pos (7UL) /*!< IOM0 INTEN: START (Bit 7) */ -#define IOM0_INTEN_START_Msk (0x80UL) /*!< IOM0 INTEN: START (Bitfield-Mask: 0x01) */ -#define IOM0_INTEN_ICMD_Pos (6UL) /*!< IOM0 INTEN: ICMD (Bit 6) */ -#define IOM0_INTEN_ICMD_Msk (0x40UL) /*!< IOM0 INTEN: ICMD (Bitfield-Mask: 0x01) */ -#define IOM0_INTEN_IACC_Pos (5UL) /*!< IOM0 INTEN: IACC (Bit 5) */ -#define IOM0_INTEN_IACC_Msk (0x20UL) /*!< IOM0 INTEN: IACC (Bitfield-Mask: 0x01) */ -#define IOM0_INTEN_NAK_Pos (4UL) /*!< IOM0 INTEN: NAK (Bit 4) */ -#define IOM0_INTEN_NAK_Msk (0x10UL) /*!< IOM0 INTEN: NAK (Bitfield-Mask: 0x01) */ -#define IOM0_INTEN_FOVFL_Pos (3UL) /*!< IOM0 INTEN: FOVFL (Bit 3) */ -#define IOM0_INTEN_FOVFL_Msk (0x8UL) /*!< IOM0 INTEN: FOVFL (Bitfield-Mask: 0x01) */ -#define IOM0_INTEN_FUNDFL_Pos (2UL) /*!< IOM0 INTEN: FUNDFL (Bit 2) */ -#define IOM0_INTEN_FUNDFL_Msk (0x4UL) /*!< IOM0 INTEN: FUNDFL (Bitfield-Mask: 0x01) */ -#define IOM0_INTEN_THR_Pos (1UL) /*!< IOM0 INTEN: THR (Bit 1) */ -#define IOM0_INTEN_THR_Msk (0x2UL) /*!< IOM0 INTEN: THR (Bitfield-Mask: 0x01) */ -#define IOM0_INTEN_CMDCMP_Pos (0UL) /*!< IOM0 INTEN: CMDCMP (Bit 0) */ -#define IOM0_INTEN_CMDCMP_Msk (0x1UL) /*!< IOM0 INTEN: CMDCMP (Bitfield-Mask: 0x01) */ -/* ======================================================== INTSTAT ======================================================== */ -#define IOM0_INTSTAT_CQERR_Pos (14UL) /*!< IOM0 INTSTAT: CQERR (Bit 14) */ -#define IOM0_INTSTAT_CQERR_Msk (0x4000UL) /*!< IOM0 INTSTAT: CQERR (Bitfield-Mask: 0x01) */ -#define IOM0_INTSTAT_CQUPD_Pos (13UL) /*!< IOM0 INTSTAT: CQUPD (Bit 13) */ -#define IOM0_INTSTAT_CQUPD_Msk (0x2000UL) /*!< IOM0 INTSTAT: CQUPD (Bitfield-Mask: 0x01) */ -#define IOM0_INTSTAT_CQPAUSED_Pos (12UL) /*!< IOM0 INTSTAT: CQPAUSED (Bit 12) */ -#define IOM0_INTSTAT_CQPAUSED_Msk (0x1000UL) /*!< IOM0 INTSTAT: CQPAUSED (Bitfield-Mask: 0x01) */ -#define IOM0_INTSTAT_DERR_Pos (11UL) /*!< IOM0 INTSTAT: DERR (Bit 11) */ -#define IOM0_INTSTAT_DERR_Msk (0x800UL) /*!< IOM0 INTSTAT: DERR (Bitfield-Mask: 0x01) */ -#define IOM0_INTSTAT_DCMP_Pos (10UL) /*!< IOM0 INTSTAT: DCMP (Bit 10) */ -#define IOM0_INTSTAT_DCMP_Msk (0x400UL) /*!< IOM0 INTSTAT: DCMP (Bitfield-Mask: 0x01) */ -#define IOM0_INTSTAT_ARB_Pos (9UL) /*!< IOM0 INTSTAT: ARB (Bit 9) */ -#define IOM0_INTSTAT_ARB_Msk (0x200UL) /*!< IOM0 INTSTAT: ARB (Bitfield-Mask: 0x01) */ -#define IOM0_INTSTAT_STOP_Pos (8UL) /*!< IOM0 INTSTAT: STOP (Bit 8) */ -#define IOM0_INTSTAT_STOP_Msk (0x100UL) /*!< IOM0 INTSTAT: STOP (Bitfield-Mask: 0x01) */ -#define IOM0_INTSTAT_START_Pos (7UL) /*!< IOM0 INTSTAT: START (Bit 7) */ -#define IOM0_INTSTAT_START_Msk (0x80UL) /*!< IOM0 INTSTAT: START (Bitfield-Mask: 0x01) */ -#define IOM0_INTSTAT_ICMD_Pos (6UL) /*!< IOM0 INTSTAT: ICMD (Bit 6) */ -#define IOM0_INTSTAT_ICMD_Msk (0x40UL) /*!< IOM0 INTSTAT: ICMD (Bitfield-Mask: 0x01) */ -#define IOM0_INTSTAT_IACC_Pos (5UL) /*!< IOM0 INTSTAT: IACC (Bit 5) */ -#define IOM0_INTSTAT_IACC_Msk (0x20UL) /*!< IOM0 INTSTAT: IACC (Bitfield-Mask: 0x01) */ -#define IOM0_INTSTAT_NAK_Pos (4UL) /*!< IOM0 INTSTAT: NAK (Bit 4) */ -#define IOM0_INTSTAT_NAK_Msk (0x10UL) /*!< IOM0 INTSTAT: NAK (Bitfield-Mask: 0x01) */ -#define IOM0_INTSTAT_FOVFL_Pos (3UL) /*!< IOM0 INTSTAT: FOVFL (Bit 3) */ -#define IOM0_INTSTAT_FOVFL_Msk (0x8UL) /*!< IOM0 INTSTAT: FOVFL (Bitfield-Mask: 0x01) */ -#define IOM0_INTSTAT_FUNDFL_Pos (2UL) /*!< IOM0 INTSTAT: FUNDFL (Bit 2) */ -#define IOM0_INTSTAT_FUNDFL_Msk (0x4UL) /*!< IOM0 INTSTAT: FUNDFL (Bitfield-Mask: 0x01) */ -#define IOM0_INTSTAT_THR_Pos (1UL) /*!< IOM0 INTSTAT: THR (Bit 1) */ -#define IOM0_INTSTAT_THR_Msk (0x2UL) /*!< IOM0 INTSTAT: THR (Bitfield-Mask: 0x01) */ -#define IOM0_INTSTAT_CMDCMP_Pos (0UL) /*!< IOM0 INTSTAT: CMDCMP (Bit 0) */ -#define IOM0_INTSTAT_CMDCMP_Msk (0x1UL) /*!< IOM0 INTSTAT: CMDCMP (Bitfield-Mask: 0x01) */ -/* ======================================================== INTCLR ========================================================= */ -#define IOM0_INTCLR_CQERR_Pos (14UL) /*!< IOM0 INTCLR: CQERR (Bit 14) */ -#define IOM0_INTCLR_CQERR_Msk (0x4000UL) /*!< IOM0 INTCLR: CQERR (Bitfield-Mask: 0x01) */ -#define IOM0_INTCLR_CQUPD_Pos (13UL) /*!< IOM0 INTCLR: CQUPD (Bit 13) */ -#define IOM0_INTCLR_CQUPD_Msk (0x2000UL) /*!< IOM0 INTCLR: CQUPD (Bitfield-Mask: 0x01) */ -#define IOM0_INTCLR_CQPAUSED_Pos (12UL) /*!< IOM0 INTCLR: CQPAUSED (Bit 12) */ -#define IOM0_INTCLR_CQPAUSED_Msk (0x1000UL) /*!< IOM0 INTCLR: CQPAUSED (Bitfield-Mask: 0x01) */ -#define IOM0_INTCLR_DERR_Pos (11UL) /*!< IOM0 INTCLR: DERR (Bit 11) */ -#define IOM0_INTCLR_DERR_Msk (0x800UL) /*!< IOM0 INTCLR: DERR (Bitfield-Mask: 0x01) */ -#define IOM0_INTCLR_DCMP_Pos (10UL) /*!< IOM0 INTCLR: DCMP (Bit 10) */ -#define IOM0_INTCLR_DCMP_Msk (0x400UL) /*!< IOM0 INTCLR: DCMP (Bitfield-Mask: 0x01) */ -#define IOM0_INTCLR_ARB_Pos (9UL) /*!< IOM0 INTCLR: ARB (Bit 9) */ -#define IOM0_INTCLR_ARB_Msk (0x200UL) /*!< IOM0 INTCLR: ARB (Bitfield-Mask: 0x01) */ -#define IOM0_INTCLR_STOP_Pos (8UL) /*!< IOM0 INTCLR: STOP (Bit 8) */ -#define IOM0_INTCLR_STOP_Msk (0x100UL) /*!< IOM0 INTCLR: STOP (Bitfield-Mask: 0x01) */ -#define IOM0_INTCLR_START_Pos (7UL) /*!< IOM0 INTCLR: START (Bit 7) */ -#define IOM0_INTCLR_START_Msk (0x80UL) /*!< IOM0 INTCLR: START (Bitfield-Mask: 0x01) */ -#define IOM0_INTCLR_ICMD_Pos (6UL) /*!< IOM0 INTCLR: ICMD (Bit 6) */ -#define IOM0_INTCLR_ICMD_Msk (0x40UL) /*!< IOM0 INTCLR: ICMD (Bitfield-Mask: 0x01) */ -#define IOM0_INTCLR_IACC_Pos (5UL) /*!< IOM0 INTCLR: IACC (Bit 5) */ -#define IOM0_INTCLR_IACC_Msk (0x20UL) /*!< IOM0 INTCLR: IACC (Bitfield-Mask: 0x01) */ -#define IOM0_INTCLR_NAK_Pos (4UL) /*!< IOM0 INTCLR: NAK (Bit 4) */ -#define IOM0_INTCLR_NAK_Msk (0x10UL) /*!< IOM0 INTCLR: NAK (Bitfield-Mask: 0x01) */ -#define IOM0_INTCLR_FOVFL_Pos (3UL) /*!< IOM0 INTCLR: FOVFL (Bit 3) */ -#define IOM0_INTCLR_FOVFL_Msk (0x8UL) /*!< IOM0 INTCLR: FOVFL (Bitfield-Mask: 0x01) */ -#define IOM0_INTCLR_FUNDFL_Pos (2UL) /*!< IOM0 INTCLR: FUNDFL (Bit 2) */ -#define IOM0_INTCLR_FUNDFL_Msk (0x4UL) /*!< IOM0 INTCLR: FUNDFL (Bitfield-Mask: 0x01) */ -#define IOM0_INTCLR_THR_Pos (1UL) /*!< IOM0 INTCLR: THR (Bit 1) */ -#define IOM0_INTCLR_THR_Msk (0x2UL) /*!< IOM0 INTCLR: THR (Bitfield-Mask: 0x01) */ -#define IOM0_INTCLR_CMDCMP_Pos (0UL) /*!< IOM0 INTCLR: CMDCMP (Bit 0) */ -#define IOM0_INTCLR_CMDCMP_Msk (0x1UL) /*!< IOM0 INTCLR: CMDCMP (Bitfield-Mask: 0x01) */ -/* ======================================================== INTSET ========================================================= */ -#define IOM0_INTSET_CQERR_Pos (14UL) /*!< IOM0 INTSET: CQERR (Bit 14) */ -#define IOM0_INTSET_CQERR_Msk (0x4000UL) /*!< IOM0 INTSET: CQERR (Bitfield-Mask: 0x01) */ -#define IOM0_INTSET_CQUPD_Pos (13UL) /*!< IOM0 INTSET: CQUPD (Bit 13) */ -#define IOM0_INTSET_CQUPD_Msk (0x2000UL) /*!< IOM0 INTSET: CQUPD (Bitfield-Mask: 0x01) */ -#define IOM0_INTSET_CQPAUSED_Pos (12UL) /*!< IOM0 INTSET: CQPAUSED (Bit 12) */ -#define IOM0_INTSET_CQPAUSED_Msk (0x1000UL) /*!< IOM0 INTSET: CQPAUSED (Bitfield-Mask: 0x01) */ -#define IOM0_INTSET_DERR_Pos (11UL) /*!< IOM0 INTSET: DERR (Bit 11) */ -#define IOM0_INTSET_DERR_Msk (0x800UL) /*!< IOM0 INTSET: DERR (Bitfield-Mask: 0x01) */ -#define IOM0_INTSET_DCMP_Pos (10UL) /*!< IOM0 INTSET: DCMP (Bit 10) */ -#define IOM0_INTSET_DCMP_Msk (0x400UL) /*!< IOM0 INTSET: DCMP (Bitfield-Mask: 0x01) */ -#define IOM0_INTSET_ARB_Pos (9UL) /*!< IOM0 INTSET: ARB (Bit 9) */ -#define IOM0_INTSET_ARB_Msk (0x200UL) /*!< IOM0 INTSET: ARB (Bitfield-Mask: 0x01) */ -#define IOM0_INTSET_STOP_Pos (8UL) /*!< IOM0 INTSET: STOP (Bit 8) */ -#define IOM0_INTSET_STOP_Msk (0x100UL) /*!< IOM0 INTSET: STOP (Bitfield-Mask: 0x01) */ -#define IOM0_INTSET_START_Pos (7UL) /*!< IOM0 INTSET: START (Bit 7) */ -#define IOM0_INTSET_START_Msk (0x80UL) /*!< IOM0 INTSET: START (Bitfield-Mask: 0x01) */ -#define IOM0_INTSET_ICMD_Pos (6UL) /*!< IOM0 INTSET: ICMD (Bit 6) */ -#define IOM0_INTSET_ICMD_Msk (0x40UL) /*!< IOM0 INTSET: ICMD (Bitfield-Mask: 0x01) */ -#define IOM0_INTSET_IACC_Pos (5UL) /*!< IOM0 INTSET: IACC (Bit 5) */ -#define IOM0_INTSET_IACC_Msk (0x20UL) /*!< IOM0 INTSET: IACC (Bitfield-Mask: 0x01) */ -#define IOM0_INTSET_NAK_Pos (4UL) /*!< IOM0 INTSET: NAK (Bit 4) */ -#define IOM0_INTSET_NAK_Msk (0x10UL) /*!< IOM0 INTSET: NAK (Bitfield-Mask: 0x01) */ -#define IOM0_INTSET_FOVFL_Pos (3UL) /*!< IOM0 INTSET: FOVFL (Bit 3) */ -#define IOM0_INTSET_FOVFL_Msk (0x8UL) /*!< IOM0 INTSET: FOVFL (Bitfield-Mask: 0x01) */ -#define IOM0_INTSET_FUNDFL_Pos (2UL) /*!< IOM0 INTSET: FUNDFL (Bit 2) */ -#define IOM0_INTSET_FUNDFL_Msk (0x4UL) /*!< IOM0 INTSET: FUNDFL (Bitfield-Mask: 0x01) */ -#define IOM0_INTSET_THR_Pos (1UL) /*!< IOM0 INTSET: THR (Bit 1) */ -#define IOM0_INTSET_THR_Msk (0x2UL) /*!< IOM0 INTSET: THR (Bitfield-Mask: 0x01) */ -#define IOM0_INTSET_CMDCMP_Pos (0UL) /*!< IOM0 INTSET: CMDCMP (Bit 0) */ -#define IOM0_INTSET_CMDCMP_Msk (0x1UL) /*!< IOM0 INTSET: CMDCMP (Bitfield-Mask: 0x01) */ -/* ======================================================== CLKCFG ========================================================= */ -#define IOM0_CLKCFG_TOTPER_Pos (24UL) /*!< IOM0 CLKCFG: TOTPER (Bit 24) */ -#define IOM0_CLKCFG_TOTPER_Msk (0xff000000UL) /*!< IOM0 CLKCFG: TOTPER (Bitfield-Mask: 0xff) */ -#define IOM0_CLKCFG_LOWPER_Pos (16UL) /*!< IOM0 CLKCFG: LOWPER (Bit 16) */ -#define IOM0_CLKCFG_LOWPER_Msk (0xff0000UL) /*!< IOM0 CLKCFG: LOWPER (Bitfield-Mask: 0xff) */ -#define IOM0_CLKCFG_DIVEN_Pos (12UL) /*!< IOM0 CLKCFG: DIVEN (Bit 12) */ -#define IOM0_CLKCFG_DIVEN_Msk (0x1000UL) /*!< IOM0 CLKCFG: DIVEN (Bitfield-Mask: 0x01) */ -#define IOM0_CLKCFG_DIV3_Pos (11UL) /*!< IOM0 CLKCFG: DIV3 (Bit 11) */ -#define IOM0_CLKCFG_DIV3_Msk (0x800UL) /*!< IOM0 CLKCFG: DIV3 (Bitfield-Mask: 0x01) */ -#define IOM0_CLKCFG_FSEL_Pos (8UL) /*!< IOM0 CLKCFG: FSEL (Bit 8) */ -#define IOM0_CLKCFG_FSEL_Msk (0x700UL) /*!< IOM0 CLKCFG: FSEL (Bitfield-Mask: 0x07) */ -#define IOM0_CLKCFG_IOCLKEN_Pos (0UL) /*!< IOM0 CLKCFG: IOCLKEN (Bit 0) */ -#define IOM0_CLKCFG_IOCLKEN_Msk (0x1UL) /*!< IOM0 CLKCFG: IOCLKEN (Bitfield-Mask: 0x01) */ -/* ====================================================== SUBMODCTRL ======================================================= */ -#define IOM0_SUBMODCTRL_SMOD1TYPE_Pos (5UL) /*!< IOM0 SUBMODCTRL: SMOD1TYPE (Bit 5) */ -#define IOM0_SUBMODCTRL_SMOD1TYPE_Msk (0xe0UL) /*!< IOM0 SUBMODCTRL: SMOD1TYPE (Bitfield-Mask: 0x07) */ -#define IOM0_SUBMODCTRL_SMOD1EN_Pos (4UL) /*!< IOM0 SUBMODCTRL: SMOD1EN (Bit 4) */ -#define IOM0_SUBMODCTRL_SMOD1EN_Msk (0x10UL) /*!< IOM0 SUBMODCTRL: SMOD1EN (Bitfield-Mask: 0x01) */ -#define IOM0_SUBMODCTRL_SMOD0TYPE_Pos (1UL) /*!< IOM0 SUBMODCTRL: SMOD0TYPE (Bit 1) */ -#define IOM0_SUBMODCTRL_SMOD0TYPE_Msk (0xeUL) /*!< IOM0 SUBMODCTRL: SMOD0TYPE (Bitfield-Mask: 0x07) */ -#define IOM0_SUBMODCTRL_SMOD0EN_Pos (0UL) /*!< IOM0 SUBMODCTRL: SMOD0EN (Bit 0) */ -#define IOM0_SUBMODCTRL_SMOD0EN_Msk (0x1UL) /*!< IOM0 SUBMODCTRL: SMOD0EN (Bitfield-Mask: 0x01) */ -/* ========================================================== CMD ========================================================== */ -#define IOM0_CMD_OFFSETLO_Pos (24UL) /*!< IOM0 CMD: OFFSETLO (Bit 24) */ -#define IOM0_CMD_OFFSETLO_Msk (0xff000000UL) /*!< IOM0 CMD: OFFSETLO (Bitfield-Mask: 0xff) */ -#define IOM0_CMD_CMDSEL_Pos (20UL) /*!< IOM0 CMD: CMDSEL (Bit 20) */ -#define IOM0_CMD_CMDSEL_Msk (0x300000UL) /*!< IOM0 CMD: CMDSEL (Bitfield-Mask: 0x03) */ -#define IOM0_CMD_TSIZE_Pos (8UL) /*!< IOM0 CMD: TSIZE (Bit 8) */ -#define IOM0_CMD_TSIZE_Msk (0xfff00UL) /*!< IOM0 CMD: TSIZE (Bitfield-Mask: 0xfff) */ -#define IOM0_CMD_CONT_Pos (7UL) /*!< IOM0 CMD: CONT (Bit 7) */ -#define IOM0_CMD_CONT_Msk (0x80UL) /*!< IOM0 CMD: CONT (Bitfield-Mask: 0x01) */ -#define IOM0_CMD_OFFSETCNT_Pos (5UL) /*!< IOM0 CMD: OFFSETCNT (Bit 5) */ -#define IOM0_CMD_OFFSETCNT_Msk (0x60UL) /*!< IOM0 CMD: OFFSETCNT (Bitfield-Mask: 0x03) */ -#define IOM0_CMD_CMD_Pos (0UL) /*!< IOM0 CMD: CMD (Bit 0) */ -#define IOM0_CMD_CMD_Msk (0x1fUL) /*!< IOM0 CMD: CMD (Bitfield-Mask: 0x1f) */ -/* ======================================================== CMDRPT ========================================================= */ -#define IOM0_CMDRPT_CMDRPT_Pos (0UL) /*!< IOM0 CMDRPT: CMDRPT (Bit 0) */ -#define IOM0_CMDRPT_CMDRPT_Msk (0x1fUL) /*!< IOM0 CMDRPT: CMDRPT (Bitfield-Mask: 0x1f) */ -/* ======================================================= OFFSETHI ======================================================== */ -#define IOM0_OFFSETHI_OFFSETHI_Pos (0UL) /*!< IOM0 OFFSETHI: OFFSETHI (Bit 0) */ -#define IOM0_OFFSETHI_OFFSETHI_Msk (0xffffUL) /*!< IOM0 OFFSETHI: OFFSETHI (Bitfield-Mask: 0xffff) */ -/* ======================================================== CMDSTAT ======================================================== */ -#define IOM0_CMDSTAT_CTSIZE_Pos (8UL) /*!< IOM0 CMDSTAT: CTSIZE (Bit 8) */ -#define IOM0_CMDSTAT_CTSIZE_Msk (0xfff00UL) /*!< IOM0 CMDSTAT: CTSIZE (Bitfield-Mask: 0xfff) */ -#define IOM0_CMDSTAT_CMDSTAT_Pos (5UL) /*!< IOM0 CMDSTAT: CMDSTAT (Bit 5) */ -#define IOM0_CMDSTAT_CMDSTAT_Msk (0xe0UL) /*!< IOM0 CMDSTAT: CMDSTAT (Bitfield-Mask: 0x07) */ -#define IOM0_CMDSTAT_CCMD_Pos (0UL) /*!< IOM0 CMDSTAT: CCMD (Bit 0) */ -#define IOM0_CMDSTAT_CCMD_Msk (0x1fUL) /*!< IOM0 CMDSTAT: CCMD (Bitfield-Mask: 0x1f) */ -/* ======================================================= DMATRIGEN ======================================================= */ -#define IOM0_DMATRIGEN_DTHREN_Pos (1UL) /*!< IOM0 DMATRIGEN: DTHREN (Bit 1) */ -#define IOM0_DMATRIGEN_DTHREN_Msk (0x2UL) /*!< IOM0 DMATRIGEN: DTHREN (Bitfield-Mask: 0x01) */ -#define IOM0_DMATRIGEN_DCMDCMPEN_Pos (0UL) /*!< IOM0 DMATRIGEN: DCMDCMPEN (Bit 0) */ -#define IOM0_DMATRIGEN_DCMDCMPEN_Msk (0x1UL) /*!< IOM0 DMATRIGEN: DCMDCMPEN (Bitfield-Mask: 0x01) */ -/* ====================================================== DMATRIGSTAT ====================================================== */ -#define IOM0_DMATRIGSTAT_DTOTCMP_Pos (2UL) /*!< IOM0 DMATRIGSTAT: DTOTCMP (Bit 2) */ -#define IOM0_DMATRIGSTAT_DTOTCMP_Msk (0x4UL) /*!< IOM0 DMATRIGSTAT: DTOTCMP (Bitfield-Mask: 0x01) */ -#define IOM0_DMATRIGSTAT_DTHR_Pos (1UL) /*!< IOM0 DMATRIGSTAT: DTHR (Bit 1) */ -#define IOM0_DMATRIGSTAT_DTHR_Msk (0x2UL) /*!< IOM0 DMATRIGSTAT: DTHR (Bitfield-Mask: 0x01) */ -#define IOM0_DMATRIGSTAT_DCMDCMP_Pos (0UL) /*!< IOM0 DMATRIGSTAT: DCMDCMP (Bit 0) */ -#define IOM0_DMATRIGSTAT_DCMDCMP_Msk (0x1UL) /*!< IOM0 DMATRIGSTAT: DCMDCMP (Bitfield-Mask: 0x01) */ -/* ======================================================== DMACFG ========================================================= */ -#define IOM0_DMACFG_DPWROFF_Pos (9UL) /*!< IOM0 DMACFG: DPWROFF (Bit 9) */ -#define IOM0_DMACFG_DPWROFF_Msk (0x200UL) /*!< IOM0 DMACFG: DPWROFF (Bitfield-Mask: 0x01) */ -#define IOM0_DMACFG_DMAPRI_Pos (8UL) /*!< IOM0 DMACFG: DMAPRI (Bit 8) */ -#define IOM0_DMACFG_DMAPRI_Msk (0x100UL) /*!< IOM0 DMACFG: DMAPRI (Bitfield-Mask: 0x01) */ -#define IOM0_DMACFG_DMADIR_Pos (1UL) /*!< IOM0 DMACFG: DMADIR (Bit 1) */ -#define IOM0_DMACFG_DMADIR_Msk (0x2UL) /*!< IOM0 DMACFG: DMADIR (Bitfield-Mask: 0x01) */ -#define IOM0_DMACFG_DMAEN_Pos (0UL) /*!< IOM0 DMACFG: DMAEN (Bit 0) */ -#define IOM0_DMACFG_DMAEN_Msk (0x1UL) /*!< IOM0 DMACFG: DMAEN (Bitfield-Mask: 0x01) */ -/* ====================================================== DMATOTCOUNT ====================================================== */ -#define IOM0_DMATOTCOUNT_TOTCOUNT_Pos (0UL) /*!< IOM0 DMATOTCOUNT: TOTCOUNT (Bit 0) */ -#define IOM0_DMATOTCOUNT_TOTCOUNT_Msk (0xfffUL) /*!< IOM0 DMATOTCOUNT: TOTCOUNT (Bitfield-Mask: 0xfff) */ -/* ====================================================== DMATARGADDR ====================================================== */ -#define IOM0_DMATARGADDR_TARGADDR28_Pos (28UL) /*!< IOM0 DMATARGADDR: TARGADDR28 (Bit 28) */ -#define IOM0_DMATARGADDR_TARGADDR28_Msk (0x10000000UL) /*!< IOM0 DMATARGADDR: TARGADDR28 (Bitfield-Mask: 0x01) */ -#define IOM0_DMATARGADDR_TARGADDR_Pos (0UL) /*!< IOM0 DMATARGADDR: TARGADDR (Bit 0) */ -#define IOM0_DMATARGADDR_TARGADDR_Msk (0xfffffUL) /*!< IOM0 DMATARGADDR: TARGADDR (Bitfield-Mask: 0xfffff) */ -/* ======================================================== DMASTAT ======================================================== */ -#define IOM0_DMASTAT_DMAERR_Pos (2UL) /*!< IOM0 DMASTAT: DMAERR (Bit 2) */ -#define IOM0_DMASTAT_DMAERR_Msk (0x4UL) /*!< IOM0 DMASTAT: DMAERR (Bitfield-Mask: 0x01) */ -#define IOM0_DMASTAT_DMACPL_Pos (1UL) /*!< IOM0 DMASTAT: DMACPL (Bit 1) */ -#define IOM0_DMASTAT_DMACPL_Msk (0x2UL) /*!< IOM0 DMASTAT: DMACPL (Bitfield-Mask: 0x01) */ -#define IOM0_DMASTAT_DMATIP_Pos (0UL) /*!< IOM0 DMASTAT: DMATIP (Bit 0) */ -#define IOM0_DMASTAT_DMATIP_Msk (0x1UL) /*!< IOM0 DMASTAT: DMATIP (Bitfield-Mask: 0x01) */ -/* ========================================================= CQCFG ========================================================= */ -#define IOM0_CQCFG_CQPRI_Pos (1UL) /*!< IOM0 CQCFG: CQPRI (Bit 1) */ -#define IOM0_CQCFG_CQPRI_Msk (0x2UL) /*!< IOM0 CQCFG: CQPRI (Bitfield-Mask: 0x01) */ -#define IOM0_CQCFG_CQEN_Pos (0UL) /*!< IOM0 CQCFG: CQEN (Bit 0) */ -#define IOM0_CQCFG_CQEN_Msk (0x1UL) /*!< IOM0 CQCFG: CQEN (Bitfield-Mask: 0x01) */ -/* ======================================================== CQADDR ========================================================= */ -#define IOM0_CQADDR_CQADDR28_Pos (28UL) /*!< IOM0 CQADDR: CQADDR28 (Bit 28) */ -#define IOM0_CQADDR_CQADDR28_Msk (0x10000000UL) /*!< IOM0 CQADDR: CQADDR28 (Bitfield-Mask: 0x01) */ -#define IOM0_CQADDR_CQADDR_Pos (2UL) /*!< IOM0 CQADDR: CQADDR (Bit 2) */ -#define IOM0_CQADDR_CQADDR_Msk (0xffffcUL) /*!< IOM0 CQADDR: CQADDR (Bitfield-Mask: 0x3ffff) */ -/* ======================================================== CQSTAT ========================================================= */ -#define IOM0_CQSTAT_CQERR_Pos (2UL) /*!< IOM0 CQSTAT: CQERR (Bit 2) */ -#define IOM0_CQSTAT_CQERR_Msk (0x4UL) /*!< IOM0 CQSTAT: CQERR (Bitfield-Mask: 0x01) */ -#define IOM0_CQSTAT_CQPAUSED_Pos (1UL) /*!< IOM0 CQSTAT: CQPAUSED (Bit 1) */ -#define IOM0_CQSTAT_CQPAUSED_Msk (0x2UL) /*!< IOM0 CQSTAT: CQPAUSED (Bitfield-Mask: 0x01) */ -#define IOM0_CQSTAT_CQTIP_Pos (0UL) /*!< IOM0 CQSTAT: CQTIP (Bit 0) */ -#define IOM0_CQSTAT_CQTIP_Msk (0x1UL) /*!< IOM0 CQSTAT: CQTIP (Bitfield-Mask: 0x01) */ -/* ======================================================== CQFLAGS ======================================================== */ -#define IOM0_CQFLAGS_CQIRQMASK_Pos (16UL) /*!< IOM0 CQFLAGS: CQIRQMASK (Bit 16) */ -#define IOM0_CQFLAGS_CQIRQMASK_Msk (0xffff0000UL) /*!< IOM0 CQFLAGS: CQIRQMASK (Bitfield-Mask: 0xffff) */ -#define IOM0_CQFLAGS_CQFLAGS_Pos (0UL) /*!< IOM0 CQFLAGS: CQFLAGS (Bit 0) */ -#define IOM0_CQFLAGS_CQFLAGS_Msk (0xffffUL) /*!< IOM0 CQFLAGS: CQFLAGS (Bitfield-Mask: 0xffff) */ -/* ====================================================== CQSETCLEAR ======================================================= */ -#define IOM0_CQSETCLEAR_CQFCLR_Pos (16UL) /*!< IOM0 CQSETCLEAR: CQFCLR (Bit 16) */ -#define IOM0_CQSETCLEAR_CQFCLR_Msk (0xff0000UL) /*!< IOM0 CQSETCLEAR: CQFCLR (Bitfield-Mask: 0xff) */ -#define IOM0_CQSETCLEAR_CQFTGL_Pos (8UL) /*!< IOM0 CQSETCLEAR: CQFTGL (Bit 8) */ -#define IOM0_CQSETCLEAR_CQFTGL_Msk (0xff00UL) /*!< IOM0 CQSETCLEAR: CQFTGL (Bitfield-Mask: 0xff) */ -#define IOM0_CQSETCLEAR_CQFSET_Pos (0UL) /*!< IOM0 CQSETCLEAR: CQFSET (Bit 0) */ -#define IOM0_CQSETCLEAR_CQFSET_Msk (0xffUL) /*!< IOM0 CQSETCLEAR: CQFSET (Bitfield-Mask: 0xff) */ -/* ======================================================= CQPAUSEEN ======================================================= */ -#define IOM0_CQPAUSEEN_CQPEN_Pos (0UL) /*!< IOM0 CQPAUSEEN: CQPEN (Bit 0) */ -#define IOM0_CQPAUSEEN_CQPEN_Msk (0xffffUL) /*!< IOM0 CQPAUSEEN: CQPEN (Bitfield-Mask: 0xffff) */ -/* ======================================================= CQCURIDX ======================================================== */ -#define IOM0_CQCURIDX_CQCURIDX_Pos (0UL) /*!< IOM0 CQCURIDX: CQCURIDX (Bit 0) */ -#define IOM0_CQCURIDX_CQCURIDX_Msk (0xffUL) /*!< IOM0 CQCURIDX: CQCURIDX (Bitfield-Mask: 0xff) */ -/* ======================================================= CQENDIDX ======================================================== */ -#define IOM0_CQENDIDX_CQENDIDX_Pos (0UL) /*!< IOM0 CQENDIDX: CQENDIDX (Bit 0) */ -#define IOM0_CQENDIDX_CQENDIDX_Msk (0xffUL) /*!< IOM0 CQENDIDX: CQENDIDX (Bitfield-Mask: 0xff) */ -/* ======================================================== STATUS ========================================================= */ -#define IOM0_STATUS_IDLEST_Pos (2UL) /*!< IOM0 STATUS: IDLEST (Bit 2) */ -#define IOM0_STATUS_IDLEST_Msk (0x4UL) /*!< IOM0 STATUS: IDLEST (Bitfield-Mask: 0x01) */ -#define IOM0_STATUS_CMDACT_Pos (1UL) /*!< IOM0 STATUS: CMDACT (Bit 1) */ -#define IOM0_STATUS_CMDACT_Msk (0x2UL) /*!< IOM0 STATUS: CMDACT (Bitfield-Mask: 0x01) */ -#define IOM0_STATUS_ERR_Pos (0UL) /*!< IOM0 STATUS: ERR (Bit 0) */ -#define IOM0_STATUS_ERR_Msk (0x1UL) /*!< IOM0 STATUS: ERR (Bitfield-Mask: 0x01) */ -/* ======================================================== MSPICFG ======================================================== */ -#define IOM0_MSPICFG_MSPIRST_Pos (30UL) /*!< IOM0 MSPICFG: MSPIRST (Bit 30) */ -#define IOM0_MSPICFG_MSPIRST_Msk (0x40000000UL) /*!< IOM0 MSPICFG: MSPIRST (Bitfield-Mask: 0x01) */ -#define IOM0_MSPICFG_DOUTDLY_Pos (27UL) /*!< IOM0 MSPICFG: DOUTDLY (Bit 27) */ -#define IOM0_MSPICFG_DOUTDLY_Msk (0x38000000UL) /*!< IOM0 MSPICFG: DOUTDLY (Bitfield-Mask: 0x07) */ -#define IOM0_MSPICFG_DINDLY_Pos (24UL) /*!< IOM0 MSPICFG: DINDLY (Bit 24) */ -#define IOM0_MSPICFG_DINDLY_Msk (0x7000000UL) /*!< IOM0 MSPICFG: DINDLY (Bitfield-Mask: 0x07) */ -#define IOM0_MSPICFG_SPILSB_Pos (23UL) /*!< IOM0 MSPICFG: SPILSB (Bit 23) */ -#define IOM0_MSPICFG_SPILSB_Msk (0x800000UL) /*!< IOM0 MSPICFG: SPILSB (Bitfield-Mask: 0x01) */ -#define IOM0_MSPICFG_RDFCPOL_Pos (22UL) /*!< IOM0 MSPICFG: RDFCPOL (Bit 22) */ -#define IOM0_MSPICFG_RDFCPOL_Msk (0x400000UL) /*!< IOM0 MSPICFG: RDFCPOL (Bitfield-Mask: 0x01) */ -#define IOM0_MSPICFG_WTFCPOL_Pos (21UL) /*!< IOM0 MSPICFG: WTFCPOL (Bit 21) */ -#define IOM0_MSPICFG_WTFCPOL_Msk (0x200000UL) /*!< IOM0 MSPICFG: WTFCPOL (Bitfield-Mask: 0x01) */ -#define IOM0_MSPICFG_WTFCIRQ_Pos (20UL) /*!< IOM0 MSPICFG: WTFCIRQ (Bit 20) */ -#define IOM0_MSPICFG_WTFCIRQ_Msk (0x100000UL) /*!< IOM0 MSPICFG: WTFCIRQ (Bitfield-Mask: 0x01) */ -#define IOM0_MSPICFG_MOSIINV_Pos (18UL) /*!< IOM0 MSPICFG: MOSIINV (Bit 18) */ -#define IOM0_MSPICFG_MOSIINV_Msk (0x40000UL) /*!< IOM0 MSPICFG: MOSIINV (Bitfield-Mask: 0x01) */ -#define IOM0_MSPICFG_RDFC_Pos (17UL) /*!< IOM0 MSPICFG: RDFC (Bit 17) */ -#define IOM0_MSPICFG_RDFC_Msk (0x20000UL) /*!< IOM0 MSPICFG: RDFC (Bitfield-Mask: 0x01) */ -#define IOM0_MSPICFG_WTFC_Pos (16UL) /*!< IOM0 MSPICFG: WTFC (Bit 16) */ -#define IOM0_MSPICFG_WTFC_Msk (0x10000UL) /*!< IOM0 MSPICFG: WTFC (Bitfield-Mask: 0x01) */ -#define IOM0_MSPICFG_FULLDUP_Pos (2UL) /*!< IOM0 MSPICFG: FULLDUP (Bit 2) */ -#define IOM0_MSPICFG_FULLDUP_Msk (0x4UL) /*!< IOM0 MSPICFG: FULLDUP (Bitfield-Mask: 0x01) */ -#define IOM0_MSPICFG_SPHA_Pos (1UL) /*!< IOM0 MSPICFG: SPHA (Bit 1) */ -#define IOM0_MSPICFG_SPHA_Msk (0x2UL) /*!< IOM0 MSPICFG: SPHA (Bitfield-Mask: 0x01) */ -#define IOM0_MSPICFG_SPOL_Pos (0UL) /*!< IOM0 MSPICFG: SPOL (Bit 0) */ -#define IOM0_MSPICFG_SPOL_Msk (0x1UL) /*!< IOM0 MSPICFG: SPOL (Bitfield-Mask: 0x01) */ -/* ======================================================== MI2CCFG ======================================================== */ -#define IOM0_MI2CCFG_STRDIS_Pos (24UL) /*!< IOM0 MI2CCFG: STRDIS (Bit 24) */ -#define IOM0_MI2CCFG_STRDIS_Msk (0x1000000UL) /*!< IOM0 MI2CCFG: STRDIS (Bitfield-Mask: 0x01) */ -#define IOM0_MI2CCFG_SMPCNT_Pos (16UL) /*!< IOM0 MI2CCFG: SMPCNT (Bit 16) */ -#define IOM0_MI2CCFG_SMPCNT_Msk (0xff0000UL) /*!< IOM0 MI2CCFG: SMPCNT (Bitfield-Mask: 0xff) */ -#define IOM0_MI2CCFG_SDAENDLY_Pos (12UL) /*!< IOM0 MI2CCFG: SDAENDLY (Bit 12) */ -#define IOM0_MI2CCFG_SDAENDLY_Msk (0xf000UL) /*!< IOM0 MI2CCFG: SDAENDLY (Bitfield-Mask: 0x0f) */ -#define IOM0_MI2CCFG_SCLENDLY_Pos (8UL) /*!< IOM0 MI2CCFG: SCLENDLY (Bit 8) */ -#define IOM0_MI2CCFG_SCLENDLY_Msk (0xf00UL) /*!< IOM0 MI2CCFG: SCLENDLY (Bitfield-Mask: 0x0f) */ -#define IOM0_MI2CCFG_MI2CRST_Pos (6UL) /*!< IOM0 MI2CCFG: MI2CRST (Bit 6) */ -#define IOM0_MI2CCFG_MI2CRST_Msk (0x40UL) /*!< IOM0 MI2CCFG: MI2CRST (Bitfield-Mask: 0x01) */ -#define IOM0_MI2CCFG_SDADLY_Pos (4UL) /*!< IOM0 MI2CCFG: SDADLY (Bit 4) */ -#define IOM0_MI2CCFG_SDADLY_Msk (0x30UL) /*!< IOM0 MI2CCFG: SDADLY (Bitfield-Mask: 0x03) */ -#define IOM0_MI2CCFG_ARBEN_Pos (2UL) /*!< IOM0 MI2CCFG: ARBEN (Bit 2) */ -#define IOM0_MI2CCFG_ARBEN_Msk (0x4UL) /*!< IOM0 MI2CCFG: ARBEN (Bitfield-Mask: 0x01) */ -#define IOM0_MI2CCFG_I2CLSB_Pos (1UL) /*!< IOM0 MI2CCFG: I2CLSB (Bit 1) */ -#define IOM0_MI2CCFG_I2CLSB_Msk (0x2UL) /*!< IOM0 MI2CCFG: I2CLSB (Bitfield-Mask: 0x01) */ -#define IOM0_MI2CCFG_ADDRSZ_Pos (0UL) /*!< IOM0 MI2CCFG: ADDRSZ (Bit 0) */ -#define IOM0_MI2CCFG_ADDRSZ_Msk (0x1UL) /*!< IOM0 MI2CCFG: ADDRSZ (Bitfield-Mask: 0x01) */ -/* ======================================================== DEVCFG ========================================================= */ -#define IOM0_DEVCFG_DEVADDR_Pos (0UL) /*!< IOM0 DEVCFG: DEVADDR (Bit 0) */ -#define IOM0_DEVCFG_DEVADDR_Msk (0x3ffUL) /*!< IOM0 DEVCFG: DEVADDR (Bitfield-Mask: 0x3ff) */ -/* ======================================================== IOMDBG ========================================================= */ -#define IOM0_IOMDBG_DBGDATA_Pos (3UL) /*!< IOM0 IOMDBG: DBGDATA (Bit 3) */ -#define IOM0_IOMDBG_DBGDATA_Msk (0xfffffff8UL) /*!< IOM0 IOMDBG: DBGDATA (Bitfield-Mask: 0x1fffffff) */ -#define IOM0_IOMDBG_APBCLKON_Pos (2UL) /*!< IOM0 IOMDBG: APBCLKON (Bit 2) */ -#define IOM0_IOMDBG_APBCLKON_Msk (0x4UL) /*!< IOM0 IOMDBG: APBCLKON (Bitfield-Mask: 0x01) */ -#define IOM0_IOMDBG_IOCLKON_Pos (1UL) /*!< IOM0 IOMDBG: IOCLKON (Bit 1) */ -#define IOM0_IOMDBG_IOCLKON_Msk (0x2UL) /*!< IOM0 IOMDBG: IOCLKON (Bitfield-Mask: 0x01) */ -#define IOM0_IOMDBG_DBGEN_Pos (0UL) /*!< IOM0 IOMDBG: DBGEN (Bit 0) */ -#define IOM0_IOMDBG_DBGEN_Msk (0x1UL) /*!< IOM0 IOMDBG: DBGEN (Bitfield-Mask: 0x01) */ - - -/* =========================================================================================================================== */ -/* ================ IOSLAVE ================ */ -/* =========================================================================================================================== */ - -/* ======================================================== FIFOPTR ======================================================== */ -#define IOSLAVE_FIFOPTR_FIFOSIZ_Pos (8UL) /*!< IOSLAVE FIFOPTR: FIFOSIZ (Bit 8) */ -#define IOSLAVE_FIFOPTR_FIFOSIZ_Msk (0xff00UL) /*!< IOSLAVE FIFOPTR: FIFOSIZ (Bitfield-Mask: 0xff) */ -#define IOSLAVE_FIFOPTR_FIFOPTR_Pos (0UL) /*!< IOSLAVE FIFOPTR: FIFOPTR (Bit 0) */ -#define IOSLAVE_FIFOPTR_FIFOPTR_Msk (0xffUL) /*!< IOSLAVE FIFOPTR: FIFOPTR (Bitfield-Mask: 0xff) */ -/* ======================================================== FIFOCFG ======================================================== */ -#define IOSLAVE_FIFOCFG_ROBASE_Pos (24UL) /*!< IOSLAVE FIFOCFG: ROBASE (Bit 24) */ -#define IOSLAVE_FIFOCFG_ROBASE_Msk (0x3f000000UL) /*!< IOSLAVE FIFOCFG: ROBASE (Bitfield-Mask: 0x3f) */ -#define IOSLAVE_FIFOCFG_FIFOMAX_Pos (8UL) /*!< IOSLAVE FIFOCFG: FIFOMAX (Bit 8) */ -#define IOSLAVE_FIFOCFG_FIFOMAX_Msk (0x3f00UL) /*!< IOSLAVE FIFOCFG: FIFOMAX (Bitfield-Mask: 0x3f) */ -#define IOSLAVE_FIFOCFG_FIFOBASE_Pos (0UL) /*!< IOSLAVE FIFOCFG: FIFOBASE (Bit 0) */ -#define IOSLAVE_FIFOCFG_FIFOBASE_Msk (0x1fUL) /*!< IOSLAVE FIFOCFG: FIFOBASE (Bitfield-Mask: 0x1f) */ -/* ======================================================== FIFOTHR ======================================================== */ -#define IOSLAVE_FIFOTHR_FIFOTHR_Pos (0UL) /*!< IOSLAVE FIFOTHR: FIFOTHR (Bit 0) */ -#define IOSLAVE_FIFOTHR_FIFOTHR_Msk (0xffUL) /*!< IOSLAVE FIFOTHR: FIFOTHR (Bitfield-Mask: 0xff) */ -/* ========================================================= FUPD ========================================================== */ -#define IOSLAVE_FUPD_IOREAD_Pos (1UL) /*!< IOSLAVE FUPD: IOREAD (Bit 1) */ -#define IOSLAVE_FUPD_IOREAD_Msk (0x2UL) /*!< IOSLAVE FUPD: IOREAD (Bitfield-Mask: 0x01) */ -#define IOSLAVE_FUPD_FIFOUPD_Pos (0UL) /*!< IOSLAVE FUPD: FIFOUPD (Bit 0) */ -#define IOSLAVE_FUPD_FIFOUPD_Msk (0x1UL) /*!< IOSLAVE FUPD: FIFOUPD (Bitfield-Mask: 0x01) */ -/* ======================================================== FIFOCTR ======================================================== */ -#define IOSLAVE_FIFOCTR_FIFOCTR_Pos (0UL) /*!< IOSLAVE FIFOCTR: FIFOCTR (Bit 0) */ -#define IOSLAVE_FIFOCTR_FIFOCTR_Msk (0x3ffUL) /*!< IOSLAVE FIFOCTR: FIFOCTR (Bitfield-Mask: 0x3ff) */ -/* ======================================================== FIFOINC ======================================================== */ -#define IOSLAVE_FIFOINC_FIFOINC_Pos (0UL) /*!< IOSLAVE FIFOINC: FIFOINC (Bit 0) */ -#define IOSLAVE_FIFOINC_FIFOINC_Msk (0x3ffUL) /*!< IOSLAVE FIFOINC: FIFOINC (Bitfield-Mask: 0x3ff) */ -/* ========================================================== CFG ========================================================== */ -#define IOSLAVE_CFG_IFCEN_Pos (31UL) /*!< IOSLAVE CFG: IFCEN (Bit 31) */ -#define IOSLAVE_CFG_IFCEN_Msk (0x80000000UL) /*!< IOSLAVE CFG: IFCEN (Bitfield-Mask: 0x01) */ -#define IOSLAVE_CFG_I2CADDR_Pos (8UL) /*!< IOSLAVE CFG: I2CADDR (Bit 8) */ -#define IOSLAVE_CFG_I2CADDR_Msk (0xfff00UL) /*!< IOSLAVE CFG: I2CADDR (Bitfield-Mask: 0xfff) */ -#define IOSLAVE_CFG_STARTRD_Pos (4UL) /*!< IOSLAVE CFG: STARTRD (Bit 4) */ -#define IOSLAVE_CFG_STARTRD_Msk (0x10UL) /*!< IOSLAVE CFG: STARTRD (Bitfield-Mask: 0x01) */ -#define IOSLAVE_CFG_LSB_Pos (2UL) /*!< IOSLAVE CFG: LSB (Bit 2) */ -#define IOSLAVE_CFG_LSB_Msk (0x4UL) /*!< IOSLAVE CFG: LSB (Bitfield-Mask: 0x01) */ -#define IOSLAVE_CFG_SPOL_Pos (1UL) /*!< IOSLAVE CFG: SPOL (Bit 1) */ -#define IOSLAVE_CFG_SPOL_Msk (0x2UL) /*!< IOSLAVE CFG: SPOL (Bitfield-Mask: 0x01) */ -#define IOSLAVE_CFG_IFCSEL_Pos (0UL) /*!< IOSLAVE CFG: IFCSEL (Bit 0) */ -#define IOSLAVE_CFG_IFCSEL_Msk (0x1UL) /*!< IOSLAVE CFG: IFCSEL (Bitfield-Mask: 0x01) */ -/* ========================================================= PRENC ========================================================= */ -#define IOSLAVE_PRENC_PRENC_Pos (0UL) /*!< IOSLAVE PRENC: PRENC (Bit 0) */ -#define IOSLAVE_PRENC_PRENC_Msk (0x1fUL) /*!< IOSLAVE PRENC: PRENC (Bitfield-Mask: 0x1f) */ -/* ======================================================= IOINTCTL ======================================================== */ -#define IOSLAVE_IOINTCTL_IOINTSET_Pos (24UL) /*!< IOSLAVE IOINTCTL: IOINTSET (Bit 24) */ -#define IOSLAVE_IOINTCTL_IOINTSET_Msk (0xff000000UL) /*!< IOSLAVE IOINTCTL: IOINTSET (Bitfield-Mask: 0xff) */ -#define IOSLAVE_IOINTCTL_IOINTCLR_Pos (16UL) /*!< IOSLAVE IOINTCTL: IOINTCLR (Bit 16) */ -#define IOSLAVE_IOINTCTL_IOINTCLR_Msk (0x10000UL) /*!< IOSLAVE IOINTCTL: IOINTCLR (Bitfield-Mask: 0x01) */ -#define IOSLAVE_IOINTCTL_IOINT_Pos (8UL) /*!< IOSLAVE IOINTCTL: IOINT (Bit 8) */ -#define IOSLAVE_IOINTCTL_IOINT_Msk (0xff00UL) /*!< IOSLAVE IOINTCTL: IOINT (Bitfield-Mask: 0xff) */ -#define IOSLAVE_IOINTCTL_IOINTEN_Pos (0UL) /*!< IOSLAVE IOINTCTL: IOINTEN (Bit 0) */ -#define IOSLAVE_IOINTCTL_IOINTEN_Msk (0xffUL) /*!< IOSLAVE IOINTCTL: IOINTEN (Bitfield-Mask: 0xff) */ -/* ======================================================== GENADD ========================================================= */ -#define IOSLAVE_GENADD_GADATA_Pos (0UL) /*!< IOSLAVE GENADD: GADATA (Bit 0) */ -#define IOSLAVE_GENADD_GADATA_Msk (0xffUL) /*!< IOSLAVE GENADD: GADATA (Bitfield-Mask: 0xff) */ -/* ========================================================= INTEN ========================================================= */ -#define IOSLAVE_INTEN_XCMPWR_Pos (9UL) /*!< IOSLAVE INTEN: XCMPWR (Bit 9) */ -#define IOSLAVE_INTEN_XCMPWR_Msk (0x200UL) /*!< IOSLAVE INTEN: XCMPWR (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTEN_XCMPWF_Pos (8UL) /*!< IOSLAVE INTEN: XCMPWF (Bit 8) */ -#define IOSLAVE_INTEN_XCMPWF_Msk (0x100UL) /*!< IOSLAVE INTEN: XCMPWF (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTEN_XCMPRR_Pos (7UL) /*!< IOSLAVE INTEN: XCMPRR (Bit 7) */ -#define IOSLAVE_INTEN_XCMPRR_Msk (0x80UL) /*!< IOSLAVE INTEN: XCMPRR (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTEN_XCMPRF_Pos (6UL) /*!< IOSLAVE INTEN: XCMPRF (Bit 6) */ -#define IOSLAVE_INTEN_XCMPRF_Msk (0x40UL) /*!< IOSLAVE INTEN: XCMPRF (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTEN_IOINTW_Pos (5UL) /*!< IOSLAVE INTEN: IOINTW (Bit 5) */ -#define IOSLAVE_INTEN_IOINTW_Msk (0x20UL) /*!< IOSLAVE INTEN: IOINTW (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTEN_GENAD_Pos (4UL) /*!< IOSLAVE INTEN: GENAD (Bit 4) */ -#define IOSLAVE_INTEN_GENAD_Msk (0x10UL) /*!< IOSLAVE INTEN: GENAD (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTEN_FRDERR_Pos (3UL) /*!< IOSLAVE INTEN: FRDERR (Bit 3) */ -#define IOSLAVE_INTEN_FRDERR_Msk (0x8UL) /*!< IOSLAVE INTEN: FRDERR (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTEN_FUNDFL_Pos (2UL) /*!< IOSLAVE INTEN: FUNDFL (Bit 2) */ -#define IOSLAVE_INTEN_FUNDFL_Msk (0x4UL) /*!< IOSLAVE INTEN: FUNDFL (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTEN_FOVFL_Pos (1UL) /*!< IOSLAVE INTEN: FOVFL (Bit 1) */ -#define IOSLAVE_INTEN_FOVFL_Msk (0x2UL) /*!< IOSLAVE INTEN: FOVFL (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTEN_FSIZE_Pos (0UL) /*!< IOSLAVE INTEN: FSIZE (Bit 0) */ -#define IOSLAVE_INTEN_FSIZE_Msk (0x1UL) /*!< IOSLAVE INTEN: FSIZE (Bitfield-Mask: 0x01) */ -/* ======================================================== INTSTAT ======================================================== */ -#define IOSLAVE_INTSTAT_XCMPWR_Pos (9UL) /*!< IOSLAVE INTSTAT: XCMPWR (Bit 9) */ -#define IOSLAVE_INTSTAT_XCMPWR_Msk (0x200UL) /*!< IOSLAVE INTSTAT: XCMPWR (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTSTAT_XCMPWF_Pos (8UL) /*!< IOSLAVE INTSTAT: XCMPWF (Bit 8) */ -#define IOSLAVE_INTSTAT_XCMPWF_Msk (0x100UL) /*!< IOSLAVE INTSTAT: XCMPWF (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTSTAT_XCMPRR_Pos (7UL) /*!< IOSLAVE INTSTAT: XCMPRR (Bit 7) */ -#define IOSLAVE_INTSTAT_XCMPRR_Msk (0x80UL) /*!< IOSLAVE INTSTAT: XCMPRR (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTSTAT_XCMPRF_Pos (6UL) /*!< IOSLAVE INTSTAT: XCMPRF (Bit 6) */ -#define IOSLAVE_INTSTAT_XCMPRF_Msk (0x40UL) /*!< IOSLAVE INTSTAT: XCMPRF (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTSTAT_IOINTW_Pos (5UL) /*!< IOSLAVE INTSTAT: IOINTW (Bit 5) */ -#define IOSLAVE_INTSTAT_IOINTW_Msk (0x20UL) /*!< IOSLAVE INTSTAT: IOINTW (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTSTAT_GENAD_Pos (4UL) /*!< IOSLAVE INTSTAT: GENAD (Bit 4) */ -#define IOSLAVE_INTSTAT_GENAD_Msk (0x10UL) /*!< IOSLAVE INTSTAT: GENAD (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTSTAT_FRDERR_Pos (3UL) /*!< IOSLAVE INTSTAT: FRDERR (Bit 3) */ -#define IOSLAVE_INTSTAT_FRDERR_Msk (0x8UL) /*!< IOSLAVE INTSTAT: FRDERR (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTSTAT_FUNDFL_Pos (2UL) /*!< IOSLAVE INTSTAT: FUNDFL (Bit 2) */ -#define IOSLAVE_INTSTAT_FUNDFL_Msk (0x4UL) /*!< IOSLAVE INTSTAT: FUNDFL (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTSTAT_FOVFL_Pos (1UL) /*!< IOSLAVE INTSTAT: FOVFL (Bit 1) */ -#define IOSLAVE_INTSTAT_FOVFL_Msk (0x2UL) /*!< IOSLAVE INTSTAT: FOVFL (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTSTAT_FSIZE_Pos (0UL) /*!< IOSLAVE INTSTAT: FSIZE (Bit 0) */ -#define IOSLAVE_INTSTAT_FSIZE_Msk (0x1UL) /*!< IOSLAVE INTSTAT: FSIZE (Bitfield-Mask: 0x01) */ -/* ======================================================== INTCLR ========================================================= */ -#define IOSLAVE_INTCLR_XCMPWR_Pos (9UL) /*!< IOSLAVE INTCLR: XCMPWR (Bit 9) */ -#define IOSLAVE_INTCLR_XCMPWR_Msk (0x200UL) /*!< IOSLAVE INTCLR: XCMPWR (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTCLR_XCMPWF_Pos (8UL) /*!< IOSLAVE INTCLR: XCMPWF (Bit 8) */ -#define IOSLAVE_INTCLR_XCMPWF_Msk (0x100UL) /*!< IOSLAVE INTCLR: XCMPWF (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTCLR_XCMPRR_Pos (7UL) /*!< IOSLAVE INTCLR: XCMPRR (Bit 7) */ -#define IOSLAVE_INTCLR_XCMPRR_Msk (0x80UL) /*!< IOSLAVE INTCLR: XCMPRR (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTCLR_XCMPRF_Pos (6UL) /*!< IOSLAVE INTCLR: XCMPRF (Bit 6) */ -#define IOSLAVE_INTCLR_XCMPRF_Msk (0x40UL) /*!< IOSLAVE INTCLR: XCMPRF (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTCLR_IOINTW_Pos (5UL) /*!< IOSLAVE INTCLR: IOINTW (Bit 5) */ -#define IOSLAVE_INTCLR_IOINTW_Msk (0x20UL) /*!< IOSLAVE INTCLR: IOINTW (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTCLR_GENAD_Pos (4UL) /*!< IOSLAVE INTCLR: GENAD (Bit 4) */ -#define IOSLAVE_INTCLR_GENAD_Msk (0x10UL) /*!< IOSLAVE INTCLR: GENAD (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTCLR_FRDERR_Pos (3UL) /*!< IOSLAVE INTCLR: FRDERR (Bit 3) */ -#define IOSLAVE_INTCLR_FRDERR_Msk (0x8UL) /*!< IOSLAVE INTCLR: FRDERR (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTCLR_FUNDFL_Pos (2UL) /*!< IOSLAVE INTCLR: FUNDFL (Bit 2) */ -#define IOSLAVE_INTCLR_FUNDFL_Msk (0x4UL) /*!< IOSLAVE INTCLR: FUNDFL (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTCLR_FOVFL_Pos (1UL) /*!< IOSLAVE INTCLR: FOVFL (Bit 1) */ -#define IOSLAVE_INTCLR_FOVFL_Msk (0x2UL) /*!< IOSLAVE INTCLR: FOVFL (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTCLR_FSIZE_Pos (0UL) /*!< IOSLAVE INTCLR: FSIZE (Bit 0) */ -#define IOSLAVE_INTCLR_FSIZE_Msk (0x1UL) /*!< IOSLAVE INTCLR: FSIZE (Bitfield-Mask: 0x01) */ -/* ======================================================== INTSET ========================================================= */ -#define IOSLAVE_INTSET_XCMPWR_Pos (9UL) /*!< IOSLAVE INTSET: XCMPWR (Bit 9) */ -#define IOSLAVE_INTSET_XCMPWR_Msk (0x200UL) /*!< IOSLAVE INTSET: XCMPWR (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTSET_XCMPWF_Pos (8UL) /*!< IOSLAVE INTSET: XCMPWF (Bit 8) */ -#define IOSLAVE_INTSET_XCMPWF_Msk (0x100UL) /*!< IOSLAVE INTSET: XCMPWF (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTSET_XCMPRR_Pos (7UL) /*!< IOSLAVE INTSET: XCMPRR (Bit 7) */ -#define IOSLAVE_INTSET_XCMPRR_Msk (0x80UL) /*!< IOSLAVE INTSET: XCMPRR (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTSET_XCMPRF_Pos (6UL) /*!< IOSLAVE INTSET: XCMPRF (Bit 6) */ -#define IOSLAVE_INTSET_XCMPRF_Msk (0x40UL) /*!< IOSLAVE INTSET: XCMPRF (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTSET_IOINTW_Pos (5UL) /*!< IOSLAVE INTSET: IOINTW (Bit 5) */ -#define IOSLAVE_INTSET_IOINTW_Msk (0x20UL) /*!< IOSLAVE INTSET: IOINTW (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTSET_GENAD_Pos (4UL) /*!< IOSLAVE INTSET: GENAD (Bit 4) */ -#define IOSLAVE_INTSET_GENAD_Msk (0x10UL) /*!< IOSLAVE INTSET: GENAD (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTSET_FRDERR_Pos (3UL) /*!< IOSLAVE INTSET: FRDERR (Bit 3) */ -#define IOSLAVE_INTSET_FRDERR_Msk (0x8UL) /*!< IOSLAVE INTSET: FRDERR (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTSET_FUNDFL_Pos (2UL) /*!< IOSLAVE INTSET: FUNDFL (Bit 2) */ -#define IOSLAVE_INTSET_FUNDFL_Msk (0x4UL) /*!< IOSLAVE INTSET: FUNDFL (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTSET_FOVFL_Pos (1UL) /*!< IOSLAVE INTSET: FOVFL (Bit 1) */ -#define IOSLAVE_INTSET_FOVFL_Msk (0x2UL) /*!< IOSLAVE INTSET: FOVFL (Bitfield-Mask: 0x01) */ -#define IOSLAVE_INTSET_FSIZE_Pos (0UL) /*!< IOSLAVE INTSET: FSIZE (Bit 0) */ -#define IOSLAVE_INTSET_FSIZE_Msk (0x1UL) /*!< IOSLAVE INTSET: FSIZE (Bitfield-Mask: 0x01) */ -/* ====================================================== REGACCINTEN ====================================================== */ -#define IOSLAVE_REGACCINTEN_REGACC_Pos (0UL) /*!< IOSLAVE REGACCINTEN: REGACC (Bit 0) */ -#define IOSLAVE_REGACCINTEN_REGACC_Msk (0xffffffffUL) /*!< IOSLAVE REGACCINTEN: REGACC (Bitfield-Mask: 0xffffffff) */ -/* ===================================================== REGACCINTSTAT ===================================================== */ -#define IOSLAVE_REGACCINTSTAT_REGACC_Pos (0UL) /*!< IOSLAVE REGACCINTSTAT: REGACC (Bit 0) */ -#define IOSLAVE_REGACCINTSTAT_REGACC_Msk (0xffffffffUL) /*!< IOSLAVE REGACCINTSTAT: REGACC (Bitfield-Mask: 0xffffffff) */ -/* ===================================================== REGACCINTCLR ====================================================== */ -#define IOSLAVE_REGACCINTCLR_REGACC_Pos (0UL) /*!< IOSLAVE REGACCINTCLR: REGACC (Bit 0) */ -#define IOSLAVE_REGACCINTCLR_REGACC_Msk (0xffffffffUL) /*!< IOSLAVE REGACCINTCLR: REGACC (Bitfield-Mask: 0xffffffff) */ -/* ===================================================== REGACCINTSET ====================================================== */ -#define IOSLAVE_REGACCINTSET_REGACC_Pos (0UL) /*!< IOSLAVE REGACCINTSET: REGACC (Bit 0) */ -#define IOSLAVE_REGACCINTSET_REGACC_Msk (0xffffffffUL) /*!< IOSLAVE REGACCINTSET: REGACC (Bitfield-Mask: 0xffffffff) */ - - -/* =========================================================================================================================== */ -/* ================ MCUCTRL ================ */ -/* =========================================================================================================================== */ - -/* ======================================================== CHIPPN ========================================================= */ -#define MCUCTRL_CHIPPN_PARTNUM_Pos (0UL) /*!< MCUCTRL CHIPPN: PARTNUM (Bit 0) */ -#define MCUCTRL_CHIPPN_PARTNUM_Msk (0xffffffffUL) /*!< MCUCTRL CHIPPN: PARTNUM (Bitfield-Mask: 0xffffffff) */ -/* ======================================================== CHIPID0 ======================================================== */ -#define MCUCTRL_CHIPID0_CHIPID0_Pos (0UL) /*!< MCUCTRL CHIPID0: CHIPID0 (Bit 0) */ -#define MCUCTRL_CHIPID0_CHIPID0_Msk (0xffffffffUL) /*!< MCUCTRL CHIPID0: CHIPID0 (Bitfield-Mask: 0xffffffff) */ -/* ======================================================== CHIPID1 ======================================================== */ -#define MCUCTRL_CHIPID1_CHIPID1_Pos (0UL) /*!< MCUCTRL CHIPID1: CHIPID1 (Bit 0) */ -#define MCUCTRL_CHIPID1_CHIPID1_Msk (0xffffffffUL) /*!< MCUCTRL CHIPID1: CHIPID1 (Bitfield-Mask: 0xffffffff) */ -/* ======================================================== CHIPREV ======================================================== */ -#define MCUCTRL_CHIPREV_SIPART_Pos (8UL) /*!< MCUCTRL CHIPREV: SIPART (Bit 8) */ -#define MCUCTRL_CHIPREV_SIPART_Msk (0xfff00UL) /*!< MCUCTRL CHIPREV: SIPART (Bitfield-Mask: 0xfff) */ -#define MCUCTRL_CHIPREV_REVMAJ_Pos (4UL) /*!< MCUCTRL CHIPREV: REVMAJ (Bit 4) */ -#define MCUCTRL_CHIPREV_REVMAJ_Msk (0xf0UL) /*!< MCUCTRL CHIPREV: REVMAJ (Bitfield-Mask: 0x0f) */ -#define MCUCTRL_CHIPREV_REVMIN_Pos (0UL) /*!< MCUCTRL CHIPREV: REVMIN (Bit 0) */ -#define MCUCTRL_CHIPREV_REVMIN_Msk (0xfUL) /*!< MCUCTRL CHIPREV: REVMIN (Bitfield-Mask: 0x0f) */ -/* ======================================================= VENDORID ======================================================== */ -#define MCUCTRL_VENDORID_VENDORID_Pos (0UL) /*!< MCUCTRL VENDORID: VENDORID (Bit 0) */ -#define MCUCTRL_VENDORID_VENDORID_Msk (0xffffffffUL) /*!< MCUCTRL VENDORID: VENDORID (Bitfield-Mask: 0xffffffff) */ -/* ========================================================== SKU ========================================================== */ -#define MCUCTRL_SKU_SECBOOT_Pos (2UL) /*!< MCUCTRL SKU: SECBOOT (Bit 2) */ -#define MCUCTRL_SKU_SECBOOT_Msk (0x4UL) /*!< MCUCTRL SKU: SECBOOT (Bitfield-Mask: 0x01) */ -#define MCUCTRL_SKU_ALLOWBLE_Pos (1UL) /*!< MCUCTRL SKU: ALLOWBLE (Bit 1) */ -#define MCUCTRL_SKU_ALLOWBLE_Msk (0x2UL) /*!< MCUCTRL SKU: ALLOWBLE (Bitfield-Mask: 0x01) */ -#define MCUCTRL_SKU_ALLOWBURST_Pos (0UL) /*!< MCUCTRL SKU: ALLOWBURST (Bit 0) */ -#define MCUCTRL_SKU_ALLOWBURST_Msk (0x1UL) /*!< MCUCTRL SKU: ALLOWBURST (Bitfield-Mask: 0x01) */ -/* ===================================================== FEATUREENABLE ===================================================== */ -#define MCUCTRL_FEATUREENABLE_BURSTAVAIL_Pos (6UL) /*!< MCUCTRL FEATUREENABLE: BURSTAVAIL (Bit 6) */ -#define MCUCTRL_FEATUREENABLE_BURSTAVAIL_Msk (0x40UL) /*!< MCUCTRL FEATUREENABLE: BURSTAVAIL (Bitfield-Mask: 0x01) */ -#define MCUCTRL_FEATUREENABLE_BURSTACK_Pos (5UL) /*!< MCUCTRL FEATUREENABLE: BURSTACK (Bit 5) */ -#define MCUCTRL_FEATUREENABLE_BURSTACK_Msk (0x20UL) /*!< MCUCTRL FEATUREENABLE: BURSTACK (Bitfield-Mask: 0x01) */ -#define MCUCTRL_FEATUREENABLE_BURSTREQ_Pos (4UL) /*!< MCUCTRL FEATUREENABLE: BURSTREQ (Bit 4) */ -#define MCUCTRL_FEATUREENABLE_BURSTREQ_Msk (0x10UL) /*!< MCUCTRL FEATUREENABLE: BURSTREQ (Bitfield-Mask: 0x01) */ -#define MCUCTRL_FEATUREENABLE_BLEAVAIL_Pos (2UL) /*!< MCUCTRL FEATUREENABLE: BLEAVAIL (Bit 2) */ -#define MCUCTRL_FEATUREENABLE_BLEAVAIL_Msk (0x4UL) /*!< MCUCTRL FEATUREENABLE: BLEAVAIL (Bitfield-Mask: 0x01) */ -#define MCUCTRL_FEATUREENABLE_BLEACK_Pos (1UL) /*!< MCUCTRL FEATUREENABLE: BLEACK (Bit 1) */ -#define MCUCTRL_FEATUREENABLE_BLEACK_Msk (0x2UL) /*!< MCUCTRL FEATUREENABLE: BLEACK (Bitfield-Mask: 0x01) */ -#define MCUCTRL_FEATUREENABLE_BLEREQ_Pos (0UL) /*!< MCUCTRL FEATUREENABLE: BLEREQ (Bit 0) */ -#define MCUCTRL_FEATUREENABLE_BLEREQ_Msk (0x1UL) /*!< MCUCTRL FEATUREENABLE: BLEREQ (Bitfield-Mask: 0x01) */ -/* ======================================================= DEBUGGER ======================================================== */ -#define MCUCTRL_DEBUGGER_LOCKOUT_Pos (0UL) /*!< MCUCTRL DEBUGGER: LOCKOUT (Bit 0) */ -#define MCUCTRL_DEBUGGER_LOCKOUT_Msk (0x1UL) /*!< MCUCTRL DEBUGGER: LOCKOUT (Bitfield-Mask: 0x01) */ -/* ======================================================== BODCTRL ======================================================== */ -#define MCUCTRL_BODCTRL_BODHVREFSEL_Pos (5UL) /*!< MCUCTRL BODCTRL: BODHVREFSEL (Bit 5) */ -#define MCUCTRL_BODCTRL_BODHVREFSEL_Msk (0x20UL) /*!< MCUCTRL BODCTRL: BODHVREFSEL (Bitfield-Mask: 0x01) */ -#define MCUCTRL_BODCTRL_BODLVREFSEL_Pos (4UL) /*!< MCUCTRL BODCTRL: BODLVREFSEL (Bit 4) */ -#define MCUCTRL_BODCTRL_BODLVREFSEL_Msk (0x10UL) /*!< MCUCTRL BODCTRL: BODLVREFSEL (Bitfield-Mask: 0x01) */ -#define MCUCTRL_BODCTRL_BODFPWD_Pos (3UL) /*!< MCUCTRL BODCTRL: BODFPWD (Bit 3) */ -#define MCUCTRL_BODCTRL_BODFPWD_Msk (0x8UL) /*!< MCUCTRL BODCTRL: BODFPWD (Bitfield-Mask: 0x01) */ -#define MCUCTRL_BODCTRL_BODCPWD_Pos (2UL) /*!< MCUCTRL BODCTRL: BODCPWD (Bit 2) */ -#define MCUCTRL_BODCTRL_BODCPWD_Msk (0x4UL) /*!< MCUCTRL BODCTRL: BODCPWD (Bitfield-Mask: 0x01) */ -#define MCUCTRL_BODCTRL_BODHPWD_Pos (1UL) /*!< MCUCTRL BODCTRL: BODHPWD (Bit 1) */ -#define MCUCTRL_BODCTRL_BODHPWD_Msk (0x2UL) /*!< MCUCTRL BODCTRL: BODHPWD (Bitfield-Mask: 0x01) */ -#define MCUCTRL_BODCTRL_BODLPWD_Pos (0UL) /*!< MCUCTRL BODCTRL: BODLPWD (Bit 0) */ -#define MCUCTRL_BODCTRL_BODLPWD_Msk (0x1UL) /*!< MCUCTRL BODCTRL: BODLPWD (Bitfield-Mask: 0x01) */ -/* ======================================================= ADCPWRDLY ======================================================= */ -#define MCUCTRL_ADCPWRDLY_ADCPWR1_Pos (8UL) /*!< MCUCTRL ADCPWRDLY: ADCPWR1 (Bit 8) */ -#define MCUCTRL_ADCPWRDLY_ADCPWR1_Msk (0xff00UL) /*!< MCUCTRL ADCPWRDLY: ADCPWR1 (Bitfield-Mask: 0xff) */ -#define MCUCTRL_ADCPWRDLY_ADCPWR0_Pos (0UL) /*!< MCUCTRL ADCPWRDLY: ADCPWR0 (Bit 0) */ -#define MCUCTRL_ADCPWRDLY_ADCPWR0_Msk (0xffUL) /*!< MCUCTRL ADCPWRDLY: ADCPWR0 (Bitfield-Mask: 0xff) */ -/* ======================================================== ADCCAL ========================================================= */ -#define MCUCTRL_ADCCAL_ADCCALIBRATED_Pos (1UL) /*!< MCUCTRL ADCCAL: ADCCALIBRATED (Bit 1) */ -#define MCUCTRL_ADCCAL_ADCCALIBRATED_Msk (0x2UL) /*!< MCUCTRL ADCCAL: ADCCALIBRATED (Bitfield-Mask: 0x01) */ -#define MCUCTRL_ADCCAL_CALONPWRUP_Pos (0UL) /*!< MCUCTRL ADCCAL: CALONPWRUP (Bit 0) */ -#define MCUCTRL_ADCCAL_CALONPWRUP_Msk (0x1UL) /*!< MCUCTRL ADCCAL: CALONPWRUP (Bitfield-Mask: 0x01) */ -/* ====================================================== ADCBATTLOAD ====================================================== */ -#define MCUCTRL_ADCBATTLOAD_BATTLOAD_Pos (0UL) /*!< MCUCTRL ADCBATTLOAD: BATTLOAD (Bit 0) */ -#define MCUCTRL_ADCBATTLOAD_BATTLOAD_Msk (0x1UL) /*!< MCUCTRL ADCBATTLOAD: BATTLOAD (Bitfield-Mask: 0x01) */ -/* ======================================================== ADCTRIM ======================================================== */ -#define MCUCTRL_ADCTRIM_ADCRFBUFIBTRIM_Pos (11UL) /*!< MCUCTRL ADCTRIM: ADCRFBUFIBTRIM (Bit 11) */ -#define MCUCTRL_ADCTRIM_ADCRFBUFIBTRIM_Msk (0x1800UL) /*!< MCUCTRL ADCTRIM: ADCRFBUFIBTRIM (Bitfield-Mask: 0x03) */ -#define MCUCTRL_ADCTRIM_ADCREFBUFTRIM_Pos (6UL) /*!< MCUCTRL ADCTRIM: ADCREFBUFTRIM (Bit 6) */ -#define MCUCTRL_ADCTRIM_ADCREFBUFTRIM_Msk (0x7c0UL) /*!< MCUCTRL ADCTRIM: ADCREFBUFTRIM (Bitfield-Mask: 0x1f) */ -#define MCUCTRL_ADCTRIM_ADCREFKEEPIBTRIM_Pos (0UL) /*!< MCUCTRL ADCTRIM: ADCREFKEEPIBTRIM (Bit 0) */ -#define MCUCTRL_ADCTRIM_ADCREFKEEPIBTRIM_Msk (0x3UL) /*!< MCUCTRL ADCTRIM: ADCREFKEEPIBTRIM (Bitfield-Mask: 0x03) */ -/* ====================================================== ADCREFCOMP ======================================================= */ -#define MCUCTRL_ADCREFCOMP_ADCRFCMPEN_Pos (16UL) /*!< MCUCTRL ADCREFCOMP: ADCRFCMPEN (Bit 16) */ -#define MCUCTRL_ADCREFCOMP_ADCRFCMPEN_Msk (0x10000UL) /*!< MCUCTRL ADCREFCOMP: ADCRFCMPEN (Bitfield-Mask: 0x01) */ -#define MCUCTRL_ADCREFCOMP_ADCREFKEEPTRIM_Pos (8UL) /*!< MCUCTRL ADCREFCOMP: ADCREFKEEPTRIM (Bit 8) */ -#define MCUCTRL_ADCREFCOMP_ADCREFKEEPTRIM_Msk (0x1f00UL) /*!< MCUCTRL ADCREFCOMP: ADCREFKEEPTRIM (Bitfield-Mask: 0x1f) */ -#define MCUCTRL_ADCREFCOMP_ADC_REFCOMP_OUT_Pos (0UL) /*!< MCUCTRL ADCREFCOMP: ADC_REFCOMP_OUT (Bit 0) */ -#define MCUCTRL_ADCREFCOMP_ADC_REFCOMP_OUT_Msk (0x1UL) /*!< MCUCTRL ADCREFCOMP: ADC_REFCOMP_OUT (Bitfield-Mask: 0x01) */ -/* ======================================================= XTALCTRL ======================================================== */ -#define MCUCTRL_XTALCTRL_XTALICOMPTRIM_Pos (8UL) /*!< MCUCTRL XTALCTRL: XTALICOMPTRIM (Bit 8) */ -#define MCUCTRL_XTALCTRL_XTALICOMPTRIM_Msk (0x300UL) /*!< MCUCTRL XTALCTRL: XTALICOMPTRIM (Bitfield-Mask: 0x03) */ -#define MCUCTRL_XTALCTRL_XTALIBUFTRIM_Pos (6UL) /*!< MCUCTRL XTALCTRL: XTALIBUFTRIM (Bit 6) */ -#define MCUCTRL_XTALCTRL_XTALIBUFTRIM_Msk (0xc0UL) /*!< MCUCTRL XTALCTRL: XTALIBUFTRIM (Bitfield-Mask: 0x03) */ -#define MCUCTRL_XTALCTRL_PWDBODXTAL_Pos (5UL) /*!< MCUCTRL XTALCTRL: PWDBODXTAL (Bit 5) */ -#define MCUCTRL_XTALCTRL_PWDBODXTAL_Msk (0x20UL) /*!< MCUCTRL XTALCTRL: PWDBODXTAL (Bitfield-Mask: 0x01) */ -#define MCUCTRL_XTALCTRL_PDNBCMPRXTAL_Pos (4UL) /*!< MCUCTRL XTALCTRL: PDNBCMPRXTAL (Bit 4) */ -#define MCUCTRL_XTALCTRL_PDNBCMPRXTAL_Msk (0x10UL) /*!< MCUCTRL XTALCTRL: PDNBCMPRXTAL (Bitfield-Mask: 0x01) */ -#define MCUCTRL_XTALCTRL_PDNBCOREXTAL_Pos (3UL) /*!< MCUCTRL XTALCTRL: PDNBCOREXTAL (Bit 3) */ -#define MCUCTRL_XTALCTRL_PDNBCOREXTAL_Msk (0x8UL) /*!< MCUCTRL XTALCTRL: PDNBCOREXTAL (Bitfield-Mask: 0x01) */ -#define MCUCTRL_XTALCTRL_BYPCMPRXTAL_Pos (2UL) /*!< MCUCTRL XTALCTRL: BYPCMPRXTAL (Bit 2) */ -#define MCUCTRL_XTALCTRL_BYPCMPRXTAL_Msk (0x4UL) /*!< MCUCTRL XTALCTRL: BYPCMPRXTAL (Bitfield-Mask: 0x01) */ -#define MCUCTRL_XTALCTRL_FDBKDSBLXTAL_Pos (1UL) /*!< MCUCTRL XTALCTRL: FDBKDSBLXTAL (Bit 1) */ -#define MCUCTRL_XTALCTRL_FDBKDSBLXTAL_Msk (0x2UL) /*!< MCUCTRL XTALCTRL: FDBKDSBLXTAL (Bitfield-Mask: 0x01) */ -#define MCUCTRL_XTALCTRL_XTALSWE_Pos (0UL) /*!< MCUCTRL XTALCTRL: XTALSWE (Bit 0) */ -#define MCUCTRL_XTALCTRL_XTALSWE_Msk (0x1UL) /*!< MCUCTRL XTALCTRL: XTALSWE (Bitfield-Mask: 0x01) */ -/* ====================================================== XTALGENCTRL ====================================================== */ -#define MCUCTRL_XTALGENCTRL_XTALKSBIASTRIM_Pos (8UL) /*!< MCUCTRL XTALGENCTRL: XTALKSBIASTRIM (Bit 8) */ -#define MCUCTRL_XTALGENCTRL_XTALKSBIASTRIM_Msk (0x3f00UL) /*!< MCUCTRL XTALGENCTRL: XTALKSBIASTRIM (Bitfield-Mask: 0x3f) */ -#define MCUCTRL_XTALGENCTRL_XTALBIASTRIM_Pos (2UL) /*!< MCUCTRL XTALGENCTRL: XTALBIASTRIM (Bit 2) */ -#define MCUCTRL_XTALGENCTRL_XTALBIASTRIM_Msk (0xfcUL) /*!< MCUCTRL XTALGENCTRL: XTALBIASTRIM (Bitfield-Mask: 0x3f) */ -#define MCUCTRL_XTALGENCTRL_ACWARMUP_Pos (0UL) /*!< MCUCTRL XTALGENCTRL: ACWARMUP (Bit 0) */ -#define MCUCTRL_XTALGENCTRL_ACWARMUP_Msk (0x3UL) /*!< MCUCTRL XTALGENCTRL: ACWARMUP (Bitfield-Mask: 0x03) */ -/* ======================================================= MISCCTRL ======================================================== */ -#define MCUCTRL_MISCCTRL_BLE_RESETN_Pos (5UL) /*!< MCUCTRL MISCCTRL: BLE_RESETN (Bit 5) */ -#define MCUCTRL_MISCCTRL_BLE_RESETN_Msk (0x20UL) /*!< MCUCTRL MISCCTRL: BLE_RESETN (Bitfield-Mask: 0x01) */ -#define MCUCTRL_MISCCTRL_RESERVED_RW_0_Pos (0UL) /*!< MCUCTRL MISCCTRL: RESERVED_RW_0 (Bit 0) */ -#define MCUCTRL_MISCCTRL_RESERVED_RW_0_Msk (0x1fUL) /*!< MCUCTRL MISCCTRL: RESERVED_RW_0 (Bitfield-Mask: 0x1f) */ -/* ====================================================== BOOTLOADER ======================================================= */ -#define MCUCTRL_BOOTLOADER_SECBOOTONRST_Pos (30UL) /*!< MCUCTRL BOOTLOADER: SECBOOTONRST (Bit 30) */ -#define MCUCTRL_BOOTLOADER_SECBOOTONRST_Msk (0xc0000000UL) /*!< MCUCTRL BOOTLOADER: SECBOOTONRST (Bitfield-Mask: 0x03) */ -#define MCUCTRL_BOOTLOADER_SECBOOT_Pos (28UL) /*!< MCUCTRL BOOTLOADER: SECBOOT (Bit 28) */ -#define MCUCTRL_BOOTLOADER_SECBOOT_Msk (0x30000000UL) /*!< MCUCTRL BOOTLOADER: SECBOOT (Bitfield-Mask: 0x03) */ -#define MCUCTRL_BOOTLOADER_SECBOOTFEATURE_Pos (26UL) /*!< MCUCTRL BOOTLOADER: SECBOOTFEATURE (Bit 26) */ -#define MCUCTRL_BOOTLOADER_SECBOOTFEATURE_Msk (0xc000000UL) /*!< MCUCTRL BOOTLOADER: SECBOOTFEATURE (Bitfield-Mask: 0x03) */ -#define MCUCTRL_BOOTLOADER_PROTLOCK_Pos (2UL) /*!< MCUCTRL BOOTLOADER: PROTLOCK (Bit 2) */ -#define MCUCTRL_BOOTLOADER_PROTLOCK_Msk (0x4UL) /*!< MCUCTRL BOOTLOADER: PROTLOCK (Bitfield-Mask: 0x01) */ -#define MCUCTRL_BOOTLOADER_SBLOCK_Pos (1UL) /*!< MCUCTRL BOOTLOADER: SBLOCK (Bit 1) */ -#define MCUCTRL_BOOTLOADER_SBLOCK_Msk (0x2UL) /*!< MCUCTRL BOOTLOADER: SBLOCK (Bitfield-Mask: 0x01) */ -#define MCUCTRL_BOOTLOADER_BOOTLOADERLOW_Pos (0UL) /*!< MCUCTRL BOOTLOADER: BOOTLOADERLOW (Bit 0) */ -#define MCUCTRL_BOOTLOADER_BOOTLOADERLOW_Msk (0x1UL) /*!< MCUCTRL BOOTLOADER: BOOTLOADERLOW (Bitfield-Mask: 0x01) */ -/* ====================================================== SHADOWVALID ====================================================== */ -#define MCUCTRL_SHADOWVALID_INFO0_VALID_Pos (2UL) /*!< MCUCTRL SHADOWVALID: INFO0_VALID (Bit 2) */ -#define MCUCTRL_SHADOWVALID_INFO0_VALID_Msk (0x4UL) /*!< MCUCTRL SHADOWVALID: INFO0_VALID (Bitfield-Mask: 0x01) */ -#define MCUCTRL_SHADOWVALID_BLDSLEEP_Pos (1UL) /*!< MCUCTRL SHADOWVALID: BLDSLEEP (Bit 1) */ -#define MCUCTRL_SHADOWVALID_BLDSLEEP_Msk (0x2UL) /*!< MCUCTRL SHADOWVALID: BLDSLEEP (Bitfield-Mask: 0x01) */ -#define MCUCTRL_SHADOWVALID_VALID_Pos (0UL) /*!< MCUCTRL SHADOWVALID: VALID (Bit 0) */ -#define MCUCTRL_SHADOWVALID_VALID_Msk (0x1UL) /*!< MCUCTRL SHADOWVALID: VALID (Bitfield-Mask: 0x01) */ -/* ======================================================= SCRATCH0 ======================================================== */ -#define MCUCTRL_SCRATCH0_SCRATCH0_Pos (0UL) /*!< MCUCTRL SCRATCH0: SCRATCH0 (Bit 0) */ -#define MCUCTRL_SCRATCH0_SCRATCH0_Msk (0xffffffffUL) /*!< MCUCTRL SCRATCH0: SCRATCH0 (Bitfield-Mask: 0xffffffff) */ -/* ======================================================= SCRATCH1 ======================================================== */ -#define MCUCTRL_SCRATCH1_SCRATCH1_Pos (0UL) /*!< MCUCTRL SCRATCH1: SCRATCH1 (Bit 0) */ -#define MCUCTRL_SCRATCH1_SCRATCH1_Msk (0xffffffffUL) /*!< MCUCTRL SCRATCH1: SCRATCH1 (Bitfield-Mask: 0xffffffff) */ -/* ==================================================== ICODEFAULTADDR ===================================================== */ -#define MCUCTRL_ICODEFAULTADDR_ICODEFAULTADDR_Pos (0UL) /*!< MCUCTRL ICODEFAULTADDR: ICODEFAULTADDR (Bit 0) */ -#define MCUCTRL_ICODEFAULTADDR_ICODEFAULTADDR_Msk (0xffffffffUL) /*!< MCUCTRL ICODEFAULTADDR: ICODEFAULTADDR (Bitfield-Mask: 0xffffffff) */ -/* ==================================================== DCODEFAULTADDR ===================================================== */ -#define MCUCTRL_DCODEFAULTADDR_DCODEFAULTADDR_Pos (0UL) /*!< MCUCTRL DCODEFAULTADDR: DCODEFAULTADDR (Bit 0) */ -#define MCUCTRL_DCODEFAULTADDR_DCODEFAULTADDR_Msk (0xffffffffUL) /*!< MCUCTRL DCODEFAULTADDR: DCODEFAULTADDR (Bitfield-Mask: 0xffffffff) */ -/* ===================================================== SYSFAULTADDR ====================================================== */ -#define MCUCTRL_SYSFAULTADDR_SYSFAULTADDR_Pos (0UL) /*!< MCUCTRL SYSFAULTADDR: SYSFAULTADDR (Bit 0) */ -#define MCUCTRL_SYSFAULTADDR_SYSFAULTADDR_Msk (0xffffffffUL) /*!< MCUCTRL SYSFAULTADDR: SYSFAULTADDR (Bitfield-Mask: 0xffffffff) */ -/* ====================================================== FAULTSTATUS ====================================================== */ -#define MCUCTRL_FAULTSTATUS_SYSFAULT_Pos (2UL) /*!< MCUCTRL FAULTSTATUS: SYSFAULT (Bit 2) */ -#define MCUCTRL_FAULTSTATUS_SYSFAULT_Msk (0x4UL) /*!< MCUCTRL FAULTSTATUS: SYSFAULT (Bitfield-Mask: 0x01) */ -#define MCUCTRL_FAULTSTATUS_DCODEFAULT_Pos (1UL) /*!< MCUCTRL FAULTSTATUS: DCODEFAULT (Bit 1) */ -#define MCUCTRL_FAULTSTATUS_DCODEFAULT_Msk (0x2UL) /*!< MCUCTRL FAULTSTATUS: DCODEFAULT (Bitfield-Mask: 0x01) */ -#define MCUCTRL_FAULTSTATUS_ICODEFAULT_Pos (0UL) /*!< MCUCTRL FAULTSTATUS: ICODEFAULT (Bit 0) */ -#define MCUCTRL_FAULTSTATUS_ICODEFAULT_Msk (0x1UL) /*!< MCUCTRL FAULTSTATUS: ICODEFAULT (Bitfield-Mask: 0x01) */ -/* ==================================================== FAULTCAPTUREEN ===================================================== */ -#define MCUCTRL_FAULTCAPTUREEN_FAULTCAPTUREEN_Pos (0UL) /*!< MCUCTRL FAULTCAPTUREEN: FAULTCAPTUREEN (Bit 0) */ -#define MCUCTRL_FAULTCAPTUREEN_FAULTCAPTUREEN_Msk (0x1UL) /*!< MCUCTRL FAULTCAPTUREEN: FAULTCAPTUREEN (Bitfield-Mask: 0x01) */ -/* ========================================================= DBGR1 ========================================================= */ -#define MCUCTRL_DBGR1_ONETO8_Pos (0UL) /*!< MCUCTRL DBGR1: ONETO8 (Bit 0) */ -#define MCUCTRL_DBGR1_ONETO8_Msk (0xffffffffUL) /*!< MCUCTRL DBGR1: ONETO8 (Bitfield-Mask: 0xffffffff) */ -/* ========================================================= DBGR2 ========================================================= */ -#define MCUCTRL_DBGR2_COOLCODE_Pos (0UL) /*!< MCUCTRL DBGR2: COOLCODE (Bit 0) */ -#define MCUCTRL_DBGR2_COOLCODE_Msk (0xffffffffUL) /*!< MCUCTRL DBGR2: COOLCODE (Bitfield-Mask: 0xffffffff) */ -/* ======================================================= PMUENABLE ======================================================= */ -#define MCUCTRL_PMUENABLE_ENABLE_Pos (0UL) /*!< MCUCTRL PMUENABLE: ENABLE (Bit 0) */ -#define MCUCTRL_PMUENABLE_ENABLE_Msk (0x1UL) /*!< MCUCTRL PMUENABLE: ENABLE (Bitfield-Mask: 0x01) */ -/* ======================================================= TPIUCTRL ======================================================== */ -#define MCUCTRL_TPIUCTRL_CLKSEL_Pos (8UL) /*!< MCUCTRL TPIUCTRL: CLKSEL (Bit 8) */ -#define MCUCTRL_TPIUCTRL_CLKSEL_Msk (0x700UL) /*!< MCUCTRL TPIUCTRL: CLKSEL (Bitfield-Mask: 0x07) */ -#define MCUCTRL_TPIUCTRL_ENABLE_Pos (0UL) /*!< MCUCTRL TPIUCTRL: ENABLE (Bit 0) */ -#define MCUCTRL_TPIUCTRL_ENABLE_Msk (0x1UL) /*!< MCUCTRL TPIUCTRL: ENABLE (Bitfield-Mask: 0x01) */ -/* ====================================================== OTAPOINTER ======================================================= */ -#define MCUCTRL_OTAPOINTER_OTAPOINTER_Pos (2UL) /*!< MCUCTRL OTAPOINTER: OTAPOINTER (Bit 2) */ -#define MCUCTRL_OTAPOINTER_OTAPOINTER_Msk (0xfffffffcUL) /*!< MCUCTRL OTAPOINTER: OTAPOINTER (Bitfield-Mask: 0x3fffffff) */ -#define MCUCTRL_OTAPOINTER_OTASBLUPDATE_Pos (1UL) /*!< MCUCTRL OTAPOINTER: OTASBLUPDATE (Bit 1) */ -#define MCUCTRL_OTAPOINTER_OTASBLUPDATE_Msk (0x2UL) /*!< MCUCTRL OTAPOINTER: OTASBLUPDATE (Bitfield-Mask: 0x01) */ -#define MCUCTRL_OTAPOINTER_OTAVALID_Pos (0UL) /*!< MCUCTRL OTAPOINTER: OTAVALID (Bit 0) */ -#define MCUCTRL_OTAPOINTER_OTAVALID_Msk (0x1UL) /*!< MCUCTRL OTAPOINTER: OTAVALID (Bitfield-Mask: 0x01) */ -/* ====================================================== APBDMACTRL ======================================================= */ -#define MCUCTRL_APBDMACTRL_HYSTERESIS_Pos (8UL) /*!< MCUCTRL APBDMACTRL: HYSTERESIS (Bit 8) */ -#define MCUCTRL_APBDMACTRL_HYSTERESIS_Msk (0xff00UL) /*!< MCUCTRL APBDMACTRL: HYSTERESIS (Bitfield-Mask: 0xff) */ -#define MCUCTRL_APBDMACTRL_DECODEABORT_Pos (1UL) /*!< MCUCTRL APBDMACTRL: DECODEABORT (Bit 1) */ -#define MCUCTRL_APBDMACTRL_DECODEABORT_Msk (0x2UL) /*!< MCUCTRL APBDMACTRL: DECODEABORT (Bitfield-Mask: 0x01) */ -#define MCUCTRL_APBDMACTRL_DMA_ENABLE_Pos (0UL) /*!< MCUCTRL APBDMACTRL: DMA_ENABLE (Bit 0) */ -#define MCUCTRL_APBDMACTRL_DMA_ENABLE_Msk (0x1UL) /*!< MCUCTRL APBDMACTRL: DMA_ENABLE (Bitfield-Mask: 0x01) */ -/* ======================================================= SRAMMODE ======================================================== */ -#define MCUCTRL_SRAMMODE_DPREFETCH_CACHE_Pos (5UL) /*!< MCUCTRL SRAMMODE: DPREFETCH_CACHE (Bit 5) */ -#define MCUCTRL_SRAMMODE_DPREFETCH_CACHE_Msk (0x20UL) /*!< MCUCTRL SRAMMODE: DPREFETCH_CACHE (Bitfield-Mask: 0x01) */ -#define MCUCTRL_SRAMMODE_DPREFETCH_Pos (4UL) /*!< MCUCTRL SRAMMODE: DPREFETCH (Bit 4) */ -#define MCUCTRL_SRAMMODE_DPREFETCH_Msk (0x10UL) /*!< MCUCTRL SRAMMODE: DPREFETCH (Bitfield-Mask: 0x01) */ -#define MCUCTRL_SRAMMODE_IPREFETCH_CACHE_Pos (1UL) /*!< MCUCTRL SRAMMODE: IPREFETCH_CACHE (Bit 1) */ -#define MCUCTRL_SRAMMODE_IPREFETCH_CACHE_Msk (0x2UL) /*!< MCUCTRL SRAMMODE: IPREFETCH_CACHE (Bitfield-Mask: 0x01) */ -#define MCUCTRL_SRAMMODE_IPREFETCH_Pos (0UL) /*!< MCUCTRL SRAMMODE: IPREFETCH (Bit 0) */ -#define MCUCTRL_SRAMMODE_IPREFETCH_Msk (0x1UL) /*!< MCUCTRL SRAMMODE: IPREFETCH (Bitfield-Mask: 0x01) */ -/* ====================================================== KEXTCLKSEL ======================================================= */ -#define MCUCTRL_KEXTCLKSEL_KEXTCLKSEL_Pos (0UL) /*!< MCUCTRL KEXTCLKSEL: KEXTCLKSEL (Bit 0) */ -#define MCUCTRL_KEXTCLKSEL_KEXTCLKSEL_Msk (0xffffffffUL) /*!< MCUCTRL KEXTCLKSEL: KEXTCLKSEL (Bitfield-Mask: 0xffffffff) */ -/* ======================================================= SIMOBUCK4 ======================================================= */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKIBIASTRIM_Pos (28UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKIBIASTRIM (Bit 28) */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKIBIASTRIM_Msk (0xf0000000UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKIBIASTRIM (Bitfield-Mask: 0x0f) */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKUVLOMODE_Pos (26UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKUVLOMODE (Bit 26) */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKUVLOMODE_Msk (0xc000000UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKUVLOMODE (Bitfield-Mask: 0x03) */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKPRIORITYSEL_Pos (25UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKPRIORITYSEL (Bit 25) */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKPRIORITYSEL_Msk (0x2000000UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKPRIORITYSEL (Bitfield-Mask: 0x01) */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKCOMP2TIMEOUTEN_Pos (24UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKCOMP2TIMEOUTEN (Bit 24) */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKCOMP2TIMEOUTEN_Msk (0x1000000UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKCOMP2TIMEOUTEN (Bitfield-Mask: 0x01) */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKCOMP2LPEN_Pos (23UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKCOMP2LPEN (Bit 23) */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKCOMP2LPEN_Msk (0x800000UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKCOMP2LPEN (Bitfield-Mask: 0x01) */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKCLKDIVSEL_Pos (21UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKCLKDIVSEL (Bit 21) */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKCLKDIVSEL_Msk (0x600000UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKCLKDIVSEL (Bitfield-Mask: 0x03) */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKEXTCLKSEL_Pos (20UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKEXTCLKSEL (Bit 20) */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKEXTCLKSEL_Msk (0x100000UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKEXTCLKSEL (Bitfield-Mask: 0x01) */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKUVLODRVSTRTRIM_Pos (17UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKUVLODRVSTRTRIM (Bit 17) */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKUVLODRVSTRTRIM_Msk (0xe0000UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKUVLODRVSTRTRIM (Bitfield-Mask: 0x07) */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKUVLOCNTRTRIM_Pos (14UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKUVLOCNTRTRIM (Bit 14) */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKUVLOCNTRTRIM_Msk (0x1c000UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKUVLOCNTRTRIM (Bitfield-Mask: 0x07) */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKZXTRIM_Pos (10UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKZXTRIM (Bit 10) */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKZXTRIM_Msk (0x3c00UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKZXTRIM (Bitfield-Mask: 0x0f) */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKMEMLEAKAGETRIM_Pos (8UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKMEMLEAKAGETRIM (Bit 8) */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKMEMLEAKAGETRIM_Msk (0x300UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKMEMLEAKAGETRIM (Bitfield-Mask: 0x03) */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKMEMLPDRVSTRTRIM_Pos (6UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKMEMLPDRVSTRTRIM (Bit 6) */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKMEMLPDRVSTRTRIM_Msk (0xc0UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKMEMLPDRVSTRTRIM (Bitfield-Mask: 0x03) */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKMEMACTDRVSTRTRIM_Pos (4UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKMEMACTDRVSTRTRIM (Bit 4) */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKMEMACTDRVSTRTRIM_Msk (0x30UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKMEMACTDRVSTRTRIM (Bitfield-Mask: 0x03) */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKMEMLPLOWTONTRIM_Pos (0UL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKMEMLPLOWTONTRIM (Bit 0) */ -#define MCUCTRL_SIMOBUCK4_SIMOBUCKMEMLPLOWTONTRIM_Msk (0xfUL) /*!< MCUCTRL SIMOBUCK4: SIMOBUCKMEMLPLOWTONTRIM (Bitfield-Mask: 0x0f) */ -/* ======================================================= BLEBUCK2 ======================================================== */ -#define MCUCTRL_BLEBUCK2_BLEBUCKTOND2ATRIM_Pos (12UL) /*!< MCUCTRL BLEBUCK2: BLEBUCKTOND2ATRIM (Bit 12) */ -#define MCUCTRL_BLEBUCK2_BLEBUCKTOND2ATRIM_Msk (0x3f000UL) /*!< MCUCTRL BLEBUCK2: BLEBUCKTOND2ATRIM (Bitfield-Mask: 0x3f) */ -#define MCUCTRL_BLEBUCK2_BLEBUCKTONHITRIM_Pos (6UL) /*!< MCUCTRL BLEBUCK2: BLEBUCKTONHITRIM (Bit 6) */ -#define MCUCTRL_BLEBUCK2_BLEBUCKTONHITRIM_Msk (0xfc0UL) /*!< MCUCTRL BLEBUCK2: BLEBUCKTONHITRIM (Bitfield-Mask: 0x3f) */ -#define MCUCTRL_BLEBUCK2_BLEBUCKTONLOWTRIM_Pos (0UL) /*!< MCUCTRL BLEBUCK2: BLEBUCKTONLOWTRIM (Bit 0) */ -#define MCUCTRL_BLEBUCK2_BLEBUCKTONLOWTRIM_Msk (0x3fUL) /*!< MCUCTRL BLEBUCK2: BLEBUCKTONLOWTRIM (Bitfield-Mask: 0x3f) */ -/* ====================================================== FLASHWPROT0 ====================================================== */ -#define MCUCTRL_FLASHWPROT0_FW0BITS_Pos (0UL) /*!< MCUCTRL FLASHWPROT0: FW0BITS (Bit 0) */ -#define MCUCTRL_FLASHWPROT0_FW0BITS_Msk (0xffffffffUL) /*!< MCUCTRL FLASHWPROT0: FW0BITS (Bitfield-Mask: 0xffffffff) */ -/* ====================================================== FLASHWPROT1 ====================================================== */ -#define MCUCTRL_FLASHWPROT1_FW1BITS_Pos (0UL) /*!< MCUCTRL FLASHWPROT1: FW1BITS (Bit 0) */ -#define MCUCTRL_FLASHWPROT1_FW1BITS_Msk (0xffffffffUL) /*!< MCUCTRL FLASHWPROT1: FW1BITS (Bitfield-Mask: 0xffffffff) */ -/* ====================================================== FLASHRPROT0 ====================================================== */ -#define MCUCTRL_FLASHRPROT0_FR0BITS_Pos (0UL) /*!< MCUCTRL FLASHRPROT0: FR0BITS (Bit 0) */ -#define MCUCTRL_FLASHRPROT0_FR0BITS_Msk (0xffffffffUL) /*!< MCUCTRL FLASHRPROT0: FR0BITS (Bitfield-Mask: 0xffffffff) */ -/* ====================================================== FLASHRPROT1 ====================================================== */ -#define MCUCTRL_FLASHRPROT1_FR1BITS_Pos (0UL) /*!< MCUCTRL FLASHRPROT1: FR1BITS (Bit 0) */ -#define MCUCTRL_FLASHRPROT1_FR1BITS_Msk (0xffffffffUL) /*!< MCUCTRL FLASHRPROT1: FR1BITS (Bitfield-Mask: 0xffffffff) */ -/* ================================================= DMASRAMWRITEPROTECT0 ================================================== */ -#define MCUCTRL_DMASRAMWRITEPROTECT0_DMA_WPROT0_Pos (0UL) /*!< MCUCTRL DMASRAMWRITEPROTECT0: DMA_WPROT0 (Bit 0) */ -#define MCUCTRL_DMASRAMWRITEPROTECT0_DMA_WPROT0_Msk (0xffffffffUL) /*!< MCUCTRL DMASRAMWRITEPROTECT0: DMA_WPROT0 (Bitfield-Mask: 0xffffffff) */ -/* ================================================= DMASRAMWRITEPROTECT1 ================================================== */ -#define MCUCTRL_DMASRAMWRITEPROTECT1_DMA_WPROT1_Pos (0UL) /*!< MCUCTRL DMASRAMWRITEPROTECT1: DMA_WPROT1 (Bit 0) */ -#define MCUCTRL_DMASRAMWRITEPROTECT1_DMA_WPROT1_Msk (0xffffUL) /*!< MCUCTRL DMASRAMWRITEPROTECT1: DMA_WPROT1 (Bitfield-Mask: 0xffff) */ -/* ================================================== DMASRAMREADPROTECT0 ================================================== */ -#define MCUCTRL_DMASRAMREADPROTECT0_DMA_RPROT0_Pos (0UL) /*!< MCUCTRL DMASRAMREADPROTECT0: DMA_RPROT0 (Bit 0) */ -#define MCUCTRL_DMASRAMREADPROTECT0_DMA_RPROT0_Msk (0xffffffffUL) /*!< MCUCTRL DMASRAMREADPROTECT0: DMA_RPROT0 (Bitfield-Mask: 0xffffffff) */ -/* ================================================== DMASRAMREADPROTECT1 ================================================== */ -#define MCUCTRL_DMASRAMREADPROTECT1_DMA_RPROT1_Pos (0UL) /*!< MCUCTRL DMASRAMREADPROTECT1: DMA_RPROT1 (Bit 0) */ -#define MCUCTRL_DMASRAMREADPROTECT1_DMA_RPROT1_Msk (0xffffUL) /*!< MCUCTRL DMASRAMREADPROTECT1: DMA_RPROT1 (Bitfield-Mask: 0xffff) */ - - -/* =========================================================================================================================== */ -/* ================ MSPI ================ */ -/* =========================================================================================================================== */ - -/* ========================================================= CTRL ========================================================== */ -#define MSPI_CTRL_XFERBYTES_Pos (16UL) /*!< MSPI CTRL: XFERBYTES (Bit 16) */ -#define MSPI_CTRL_XFERBYTES_Msk (0xffff0000UL) /*!< MSPI CTRL: XFERBYTES (Bitfield-Mask: 0xffff) */ -#define MSPI_CTRL_PIOSCRAMBLE_Pos (11UL) /*!< MSPI CTRL: PIOSCRAMBLE (Bit 11) */ -#define MSPI_CTRL_PIOSCRAMBLE_Msk (0x800UL) /*!< MSPI CTRL: PIOSCRAMBLE (Bitfield-Mask: 0x01) */ -#define MSPI_CTRL_TXRX_Pos (10UL) /*!< MSPI CTRL: TXRX (Bit 10) */ -#define MSPI_CTRL_TXRX_Msk (0x400UL) /*!< MSPI CTRL: TXRX (Bitfield-Mask: 0x01) */ -#define MSPI_CTRL_SENDI_Pos (9UL) /*!< MSPI CTRL: SENDI (Bit 9) */ -#define MSPI_CTRL_SENDI_Msk (0x200UL) /*!< MSPI CTRL: SENDI (Bitfield-Mask: 0x01) */ -#define MSPI_CTRL_SENDA_Pos (8UL) /*!< MSPI CTRL: SENDA (Bit 8) */ -#define MSPI_CTRL_SENDA_Msk (0x100UL) /*!< MSPI CTRL: SENDA (Bitfield-Mask: 0x01) */ -#define MSPI_CTRL_ENTURN_Pos (7UL) /*!< MSPI CTRL: ENTURN (Bit 7) */ -#define MSPI_CTRL_ENTURN_Msk (0x80UL) /*!< MSPI CTRL: ENTURN (Bitfield-Mask: 0x01) */ -#define MSPI_CTRL_BIGENDIAN_Pos (6UL) /*!< MSPI CTRL: BIGENDIAN (Bit 6) */ -#define MSPI_CTRL_BIGENDIAN_Msk (0x40UL) /*!< MSPI CTRL: BIGENDIAN (Bitfield-Mask: 0x01) */ -#define MSPI_CTRL_CONT_Pos (5UL) /*!< MSPI CTRL: CONT (Bit 5) */ -#define MSPI_CTRL_CONT_Msk (0x20UL) /*!< MSPI CTRL: CONT (Bitfield-Mask: 0x01) */ -#define MSPI_CTRL_QUADCMD_Pos (3UL) /*!< MSPI CTRL: QUADCMD (Bit 3) */ -#define MSPI_CTRL_QUADCMD_Msk (0x8UL) /*!< MSPI CTRL: QUADCMD (Bitfield-Mask: 0x01) */ -#define MSPI_CTRL_BUSY_Pos (2UL) /*!< MSPI CTRL: BUSY (Bit 2) */ -#define MSPI_CTRL_BUSY_Msk (0x4UL) /*!< MSPI CTRL: BUSY (Bitfield-Mask: 0x01) */ -#define MSPI_CTRL_STATUS_Pos (1UL) /*!< MSPI CTRL: STATUS (Bit 1) */ -#define MSPI_CTRL_STATUS_Msk (0x2UL) /*!< MSPI CTRL: STATUS (Bitfield-Mask: 0x01) */ -#define MSPI_CTRL_START_Pos (0UL) /*!< MSPI CTRL: START (Bit 0) */ -#define MSPI_CTRL_START_Msk (0x1UL) /*!< MSPI CTRL: START (Bitfield-Mask: 0x01) */ -/* ========================================================== CFG ========================================================== */ -#define MSPI_CFG_CPOL_Pos (17UL) /*!< MSPI CFG: CPOL (Bit 17) */ -#define MSPI_CFG_CPOL_Msk (0x20000UL) /*!< MSPI CFG: CPOL (Bitfield-Mask: 0x01) */ -#define MSPI_CFG_CPHA_Pos (16UL) /*!< MSPI CFG: CPHA (Bit 16) */ -#define MSPI_CFG_CPHA_Msk (0x10000UL) /*!< MSPI CFG: CPHA (Bitfield-Mask: 0x01) */ -#define MSPI_CFG_TURNAROUND_Pos (8UL) /*!< MSPI CFG: TURNAROUND (Bit 8) */ -#define MSPI_CFG_TURNAROUND_Msk (0x3f00UL) /*!< MSPI CFG: TURNAROUND (Bitfield-Mask: 0x3f) */ -#define MSPI_CFG_SEPIO_Pos (7UL) /*!< MSPI CFG: SEPIO (Bit 7) */ -#define MSPI_CFG_SEPIO_Msk (0x80UL) /*!< MSPI CFG: SEPIO (Bitfield-Mask: 0x01) */ -#define MSPI_CFG_ISIZE_Pos (6UL) /*!< MSPI CFG: ISIZE (Bit 6) */ -#define MSPI_CFG_ISIZE_Msk (0x40UL) /*!< MSPI CFG: ISIZE (Bitfield-Mask: 0x01) */ -#define MSPI_CFG_ASIZE_Pos (4UL) /*!< MSPI CFG: ASIZE (Bit 4) */ -#define MSPI_CFG_ASIZE_Msk (0x30UL) /*!< MSPI CFG: ASIZE (Bitfield-Mask: 0x03) */ -#define MSPI_CFG_DEVCFG_Pos (0UL) /*!< MSPI CFG: DEVCFG (Bit 0) */ -#define MSPI_CFG_DEVCFG_Msk (0xfUL) /*!< MSPI CFG: DEVCFG (Bitfield-Mask: 0x0f) */ -/* ========================================================= ADDR ========================================================== */ -#define MSPI_ADDR_ADDR_Pos (0UL) /*!< MSPI ADDR: ADDR (Bit 0) */ -#define MSPI_ADDR_ADDR_Msk (0xffffffffUL) /*!< MSPI ADDR: ADDR (Bitfield-Mask: 0xffffffff) */ -/* ========================================================= INSTR ========================================================= */ -#define MSPI_INSTR_INSTR_Pos (0UL) /*!< MSPI INSTR: INSTR (Bit 0) */ -#define MSPI_INSTR_INSTR_Msk (0xffffUL) /*!< MSPI INSTR: INSTR (Bitfield-Mask: 0xffff) */ -/* ======================================================== TXFIFO ========================================================= */ -#define MSPI_TXFIFO_TXFIFO_Pos (0UL) /*!< MSPI TXFIFO: TXFIFO (Bit 0) */ -#define MSPI_TXFIFO_TXFIFO_Msk (0xffffffffUL) /*!< MSPI TXFIFO: TXFIFO (Bitfield-Mask: 0xffffffff) */ -/* ======================================================== RXFIFO ========================================================= */ -#define MSPI_RXFIFO_RXFIFO_Pos (0UL) /*!< MSPI RXFIFO: RXFIFO (Bit 0) */ -#define MSPI_RXFIFO_RXFIFO_Msk (0xffffffffUL) /*!< MSPI RXFIFO: RXFIFO (Bitfield-Mask: 0xffffffff) */ -/* ======================================================= TXENTRIES ======================================================= */ -#define MSPI_TXENTRIES_TXENTRIES_Pos (0UL) /*!< MSPI TXENTRIES: TXENTRIES (Bit 0) */ -#define MSPI_TXENTRIES_TXENTRIES_Msk (0x1fUL) /*!< MSPI TXENTRIES: TXENTRIES (Bitfield-Mask: 0x1f) */ -/* ======================================================= RXENTRIES ======================================================= */ -#define MSPI_RXENTRIES_RXENTRIES_Pos (0UL) /*!< MSPI RXENTRIES: RXENTRIES (Bit 0) */ -#define MSPI_RXENTRIES_RXENTRIES_Msk (0x1fUL) /*!< MSPI RXENTRIES: RXENTRIES (Bitfield-Mask: 0x1f) */ -/* ======================================================= THRESHOLD ======================================================= */ -#define MSPI_THRESHOLD_RXTHRESH_Pos (8UL) /*!< MSPI THRESHOLD: RXTHRESH (Bit 8) */ -#define MSPI_THRESHOLD_RXTHRESH_Msk (0x1f00UL) /*!< MSPI THRESHOLD: RXTHRESH (Bitfield-Mask: 0x1f) */ -#define MSPI_THRESHOLD_TXTHRESH_Pos (0UL) /*!< MSPI THRESHOLD: TXTHRESH (Bit 0) */ -#define MSPI_THRESHOLD_TXTHRESH_Msk (0x1fUL) /*!< MSPI THRESHOLD: TXTHRESH (Bitfield-Mask: 0x1f) */ -/* ======================================================== MSPICFG ======================================================== */ -#define MSPI_MSPICFG_PRSTN_Pos (31UL) /*!< MSPI MSPICFG: PRSTN (Bit 31) */ -#define MSPI_MSPICFG_PRSTN_Msk (0x80000000UL) /*!< MSPI MSPICFG: PRSTN (Bitfield-Mask: 0x01) */ -#define MSPI_MSPICFG_IPRSTN_Pos (30UL) /*!< MSPI MSPICFG: IPRSTN (Bit 30) */ -#define MSPI_MSPICFG_IPRSTN_Msk (0x40000000UL) /*!< MSPI MSPICFG: IPRSTN (Bitfield-Mask: 0x01) */ -#define MSPI_MSPICFG_FIFORESET_Pos (29UL) /*!< MSPI MSPICFG: FIFORESET (Bit 29) */ -#define MSPI_MSPICFG_FIFORESET_Msk (0x20000000UL) /*!< MSPI MSPICFG: FIFORESET (Bitfield-Mask: 0x01) */ -#define MSPI_MSPICFG_CLKDIV_Pos (8UL) /*!< MSPI MSPICFG: CLKDIV (Bit 8) */ -#define MSPI_MSPICFG_CLKDIV_Msk (0x3f00UL) /*!< MSPI MSPICFG: CLKDIV (Bitfield-Mask: 0x3f) */ -#define MSPI_MSPICFG_IOMSEL_Pos (4UL) /*!< MSPI MSPICFG: IOMSEL (Bit 4) */ -#define MSPI_MSPICFG_IOMSEL_Msk (0x70UL) /*!< MSPI MSPICFG: IOMSEL (Bitfield-Mask: 0x07) */ -#define MSPI_MSPICFG_TXNEG_Pos (3UL) /*!< MSPI MSPICFG: TXNEG (Bit 3) */ -#define MSPI_MSPICFG_TXNEG_Msk (0x8UL) /*!< MSPI MSPICFG: TXNEG (Bitfield-Mask: 0x01) */ -#define MSPI_MSPICFG_RXNEG_Pos (2UL) /*!< MSPI MSPICFG: RXNEG (Bit 2) */ -#define MSPI_MSPICFG_RXNEG_Msk (0x4UL) /*!< MSPI MSPICFG: RXNEG (Bitfield-Mask: 0x01) */ -#define MSPI_MSPICFG_RXCAP_Pos (1UL) /*!< MSPI MSPICFG: RXCAP (Bit 1) */ -#define MSPI_MSPICFG_RXCAP_Msk (0x2UL) /*!< MSPI MSPICFG: RXCAP (Bitfield-Mask: 0x01) */ -#define MSPI_MSPICFG_APBCLK_Pos (0UL) /*!< MSPI MSPICFG: APBCLK (Bit 0) */ -#define MSPI_MSPICFG_APBCLK_Msk (0x1UL) /*!< MSPI MSPICFG: APBCLK (Bitfield-Mask: 0x01) */ -/* ======================================================== PADCFG ========================================================= */ -#define MSPI_PADCFG_REVCS_Pos (21UL) /*!< MSPI PADCFG: REVCS (Bit 21) */ -#define MSPI_PADCFG_REVCS_Msk (0x200000UL) /*!< MSPI PADCFG: REVCS (Bitfield-Mask: 0x01) */ -#define MSPI_PADCFG_IN3_Pos (20UL) /*!< MSPI PADCFG: IN3 (Bit 20) */ -#define MSPI_PADCFG_IN3_Msk (0x100000UL) /*!< MSPI PADCFG: IN3 (Bitfield-Mask: 0x01) */ -#define MSPI_PADCFG_IN2_Pos (19UL) /*!< MSPI PADCFG: IN2 (Bit 19) */ -#define MSPI_PADCFG_IN2_Msk (0x80000UL) /*!< MSPI PADCFG: IN2 (Bitfield-Mask: 0x01) */ -#define MSPI_PADCFG_IN1_Pos (18UL) /*!< MSPI PADCFG: IN1 (Bit 18) */ -#define MSPI_PADCFG_IN1_Msk (0x40000UL) /*!< MSPI PADCFG: IN1 (Bitfield-Mask: 0x01) */ -#define MSPI_PADCFG_IN0_Pos (16UL) /*!< MSPI PADCFG: IN0 (Bit 16) */ -#define MSPI_PADCFG_IN0_Msk (0x30000UL) /*!< MSPI PADCFG: IN0 (Bitfield-Mask: 0x03) */ -#define MSPI_PADCFG_OUT7_Pos (4UL) /*!< MSPI PADCFG: OUT7 (Bit 4) */ -#define MSPI_PADCFG_OUT7_Msk (0x10UL) /*!< MSPI PADCFG: OUT7 (Bitfield-Mask: 0x01) */ -#define MSPI_PADCFG_OUT6_Pos (3UL) /*!< MSPI PADCFG: OUT6 (Bit 3) */ -#define MSPI_PADCFG_OUT6_Msk (0x8UL) /*!< MSPI PADCFG: OUT6 (Bitfield-Mask: 0x01) */ -#define MSPI_PADCFG_OUT5_Pos (2UL) /*!< MSPI PADCFG: OUT5 (Bit 2) */ -#define MSPI_PADCFG_OUT5_Msk (0x4UL) /*!< MSPI PADCFG: OUT5 (Bitfield-Mask: 0x01) */ -#define MSPI_PADCFG_OUT4_Pos (1UL) /*!< MSPI PADCFG: OUT4 (Bit 1) */ -#define MSPI_PADCFG_OUT4_Msk (0x2UL) /*!< MSPI PADCFG: OUT4 (Bitfield-Mask: 0x01) */ -#define MSPI_PADCFG_OUT3_Pos (0UL) /*!< MSPI PADCFG: OUT3 (Bit 0) */ -#define MSPI_PADCFG_OUT3_Msk (0x1UL) /*!< MSPI PADCFG: OUT3 (Bitfield-Mask: 0x01) */ -/* ======================================================= PADOUTEN ======================================================== */ -#define MSPI_PADOUTEN_OUTEN_Pos (0UL) /*!< MSPI PADOUTEN: OUTEN (Bit 0) */ -#define MSPI_PADOUTEN_OUTEN_Msk (0x1ffUL) /*!< MSPI PADOUTEN: OUTEN (Bitfield-Mask: 0x1ff) */ -/* ========================================================= FLASH ========================================================= */ -#define MSPI_FLASH_READINSTR_Pos (24UL) /*!< MSPI FLASH: READINSTR (Bit 24) */ -#define MSPI_FLASH_READINSTR_Msk (0xff000000UL) /*!< MSPI FLASH: READINSTR (Bitfield-Mask: 0xff) */ -#define MSPI_FLASH_WRITEINSTR_Pos (16UL) /*!< MSPI FLASH: WRITEINSTR (Bit 16) */ -#define MSPI_FLASH_WRITEINSTR_Msk (0xff0000UL) /*!< MSPI FLASH: WRITEINSTR (Bitfield-Mask: 0xff) */ -#define MSPI_FLASH_XIPMIXED_Pos (8UL) /*!< MSPI FLASH: XIPMIXED (Bit 8) */ -#define MSPI_FLASH_XIPMIXED_Msk (0x700UL) /*!< MSPI FLASH: XIPMIXED (Bitfield-Mask: 0x07) */ -#define MSPI_FLASH_XIPSENDI_Pos (7UL) /*!< MSPI FLASH: XIPSENDI (Bit 7) */ -#define MSPI_FLASH_XIPSENDI_Msk (0x80UL) /*!< MSPI FLASH: XIPSENDI (Bitfield-Mask: 0x01) */ -#define MSPI_FLASH_XIPSENDA_Pos (6UL) /*!< MSPI FLASH: XIPSENDA (Bit 6) */ -#define MSPI_FLASH_XIPSENDA_Msk (0x40UL) /*!< MSPI FLASH: XIPSENDA (Bitfield-Mask: 0x01) */ -#define MSPI_FLASH_XIPENTURN_Pos (5UL) /*!< MSPI FLASH: XIPENTURN (Bit 5) */ -#define MSPI_FLASH_XIPENTURN_Msk (0x20UL) /*!< MSPI FLASH: XIPENTURN (Bitfield-Mask: 0x01) */ -#define MSPI_FLASH_XIPBIGENDIAN_Pos (4UL) /*!< MSPI FLASH: XIPBIGENDIAN (Bit 4) */ -#define MSPI_FLASH_XIPBIGENDIAN_Msk (0x10UL) /*!< MSPI FLASH: XIPBIGENDIAN (Bitfield-Mask: 0x01) */ -#define MSPI_FLASH_XIPACK_Pos (2UL) /*!< MSPI FLASH: XIPACK (Bit 2) */ -#define MSPI_FLASH_XIPACK_Msk (0xcUL) /*!< MSPI FLASH: XIPACK (Bitfield-Mask: 0x03) */ -#define MSPI_FLASH_XIPEN_Pos (0UL) /*!< MSPI FLASH: XIPEN (Bit 0) */ -#define MSPI_FLASH_XIPEN_Msk (0x1UL) /*!< MSPI FLASH: XIPEN (Bitfield-Mask: 0x01) */ -/* ====================================================== SCRAMBLING ======================================================= */ -#define MSPI_SCRAMBLING_SCRENABLE_Pos (31UL) /*!< MSPI SCRAMBLING: SCRENABLE (Bit 31) */ -#define MSPI_SCRAMBLING_SCRENABLE_Msk (0x80000000UL) /*!< MSPI SCRAMBLING: SCRENABLE (Bitfield-Mask: 0x01) */ -#define MSPI_SCRAMBLING_SCREND_Pos (16UL) /*!< MSPI SCRAMBLING: SCREND (Bit 16) */ -#define MSPI_SCRAMBLING_SCREND_Msk (0x3ff0000UL) /*!< MSPI SCRAMBLING: SCREND (Bitfield-Mask: 0x3ff) */ -#define MSPI_SCRAMBLING_SCRSTART_Pos (0UL) /*!< MSPI SCRAMBLING: SCRSTART (Bit 0) */ -#define MSPI_SCRAMBLING_SCRSTART_Msk (0x3ffUL) /*!< MSPI SCRAMBLING: SCRSTART (Bitfield-Mask: 0x3ff) */ -/* ========================================================= INTEN ========================================================= */ -#define MSPI_INTEN_SCRERR_Pos (12UL) /*!< MSPI INTEN: SCRERR (Bit 12) */ -#define MSPI_INTEN_SCRERR_Msk (0x1000UL) /*!< MSPI INTEN: SCRERR (Bitfield-Mask: 0x01) */ -#define MSPI_INTEN_CQERR_Pos (11UL) /*!< MSPI INTEN: CQERR (Bit 11) */ -#define MSPI_INTEN_CQERR_Msk (0x800UL) /*!< MSPI INTEN: CQERR (Bitfield-Mask: 0x01) */ -#define MSPI_INTEN_CQPAUSED_Pos (10UL) /*!< MSPI INTEN: CQPAUSED (Bit 10) */ -#define MSPI_INTEN_CQPAUSED_Msk (0x400UL) /*!< MSPI INTEN: CQPAUSED (Bitfield-Mask: 0x01) */ -#define MSPI_INTEN_CQUPD_Pos (9UL) /*!< MSPI INTEN: CQUPD (Bit 9) */ -#define MSPI_INTEN_CQUPD_Msk (0x200UL) /*!< MSPI INTEN: CQUPD (Bitfield-Mask: 0x01) */ -#define MSPI_INTEN_CQCMP_Pos (8UL) /*!< MSPI INTEN: CQCMP (Bit 8) */ -#define MSPI_INTEN_CQCMP_Msk (0x100UL) /*!< MSPI INTEN: CQCMP (Bitfield-Mask: 0x01) */ -#define MSPI_INTEN_DERR_Pos (7UL) /*!< MSPI INTEN: DERR (Bit 7) */ -#define MSPI_INTEN_DERR_Msk (0x80UL) /*!< MSPI INTEN: DERR (Bitfield-Mask: 0x01) */ -#define MSPI_INTEN_DCMP_Pos (6UL) /*!< MSPI INTEN: DCMP (Bit 6) */ -#define MSPI_INTEN_DCMP_Msk (0x40UL) /*!< MSPI INTEN: DCMP (Bitfield-Mask: 0x01) */ -#define MSPI_INTEN_RXF_Pos (5UL) /*!< MSPI INTEN: RXF (Bit 5) */ -#define MSPI_INTEN_RXF_Msk (0x20UL) /*!< MSPI INTEN: RXF (Bitfield-Mask: 0x01) */ -#define MSPI_INTEN_RXO_Pos (4UL) /*!< MSPI INTEN: RXO (Bit 4) */ -#define MSPI_INTEN_RXO_Msk (0x10UL) /*!< MSPI INTEN: RXO (Bitfield-Mask: 0x01) */ -#define MSPI_INTEN_RXU_Pos (3UL) /*!< MSPI INTEN: RXU (Bit 3) */ -#define MSPI_INTEN_RXU_Msk (0x8UL) /*!< MSPI INTEN: RXU (Bitfield-Mask: 0x01) */ -#define MSPI_INTEN_TXO_Pos (2UL) /*!< MSPI INTEN: TXO (Bit 2) */ -#define MSPI_INTEN_TXO_Msk (0x4UL) /*!< MSPI INTEN: TXO (Bitfield-Mask: 0x01) */ -#define MSPI_INTEN_TXE_Pos (1UL) /*!< MSPI INTEN: TXE (Bit 1) */ -#define MSPI_INTEN_TXE_Msk (0x2UL) /*!< MSPI INTEN: TXE (Bitfield-Mask: 0x01) */ -#define MSPI_INTEN_CMDCMP_Pos (0UL) /*!< MSPI INTEN: CMDCMP (Bit 0) */ -#define MSPI_INTEN_CMDCMP_Msk (0x1UL) /*!< MSPI INTEN: CMDCMP (Bitfield-Mask: 0x01) */ -/* ======================================================== INTSTAT ======================================================== */ -#define MSPI_INTSTAT_SCRERR_Pos (12UL) /*!< MSPI INTSTAT: SCRERR (Bit 12) */ -#define MSPI_INTSTAT_SCRERR_Msk (0x1000UL) /*!< MSPI INTSTAT: SCRERR (Bitfield-Mask: 0x01) */ -#define MSPI_INTSTAT_CQERR_Pos (11UL) /*!< MSPI INTSTAT: CQERR (Bit 11) */ -#define MSPI_INTSTAT_CQERR_Msk (0x800UL) /*!< MSPI INTSTAT: CQERR (Bitfield-Mask: 0x01) */ -#define MSPI_INTSTAT_CQPAUSED_Pos (10UL) /*!< MSPI INTSTAT: CQPAUSED (Bit 10) */ -#define MSPI_INTSTAT_CQPAUSED_Msk (0x400UL) /*!< MSPI INTSTAT: CQPAUSED (Bitfield-Mask: 0x01) */ -#define MSPI_INTSTAT_CQUPD_Pos (9UL) /*!< MSPI INTSTAT: CQUPD (Bit 9) */ -#define MSPI_INTSTAT_CQUPD_Msk (0x200UL) /*!< MSPI INTSTAT: CQUPD (Bitfield-Mask: 0x01) */ -#define MSPI_INTSTAT_CQCMP_Pos (8UL) /*!< MSPI INTSTAT: CQCMP (Bit 8) */ -#define MSPI_INTSTAT_CQCMP_Msk (0x100UL) /*!< MSPI INTSTAT: CQCMP (Bitfield-Mask: 0x01) */ -#define MSPI_INTSTAT_DERR_Pos (7UL) /*!< MSPI INTSTAT: DERR (Bit 7) */ -#define MSPI_INTSTAT_DERR_Msk (0x80UL) /*!< MSPI INTSTAT: DERR (Bitfield-Mask: 0x01) */ -#define MSPI_INTSTAT_DCMP_Pos (6UL) /*!< MSPI INTSTAT: DCMP (Bit 6) */ -#define MSPI_INTSTAT_DCMP_Msk (0x40UL) /*!< MSPI INTSTAT: DCMP (Bitfield-Mask: 0x01) */ -#define MSPI_INTSTAT_RXF_Pos (5UL) /*!< MSPI INTSTAT: RXF (Bit 5) */ -#define MSPI_INTSTAT_RXF_Msk (0x20UL) /*!< MSPI INTSTAT: RXF (Bitfield-Mask: 0x01) */ -#define MSPI_INTSTAT_RXO_Pos (4UL) /*!< MSPI INTSTAT: RXO (Bit 4) */ -#define MSPI_INTSTAT_RXO_Msk (0x10UL) /*!< MSPI INTSTAT: RXO (Bitfield-Mask: 0x01) */ -#define MSPI_INTSTAT_RXU_Pos (3UL) /*!< MSPI INTSTAT: RXU (Bit 3) */ -#define MSPI_INTSTAT_RXU_Msk (0x8UL) /*!< MSPI INTSTAT: RXU (Bitfield-Mask: 0x01) */ -#define MSPI_INTSTAT_TXO_Pos (2UL) /*!< MSPI INTSTAT: TXO (Bit 2) */ -#define MSPI_INTSTAT_TXO_Msk (0x4UL) /*!< MSPI INTSTAT: TXO (Bitfield-Mask: 0x01) */ -#define MSPI_INTSTAT_TXE_Pos (1UL) /*!< MSPI INTSTAT: TXE (Bit 1) */ -#define MSPI_INTSTAT_TXE_Msk (0x2UL) /*!< MSPI INTSTAT: TXE (Bitfield-Mask: 0x01) */ -#define MSPI_INTSTAT_CMDCMP_Pos (0UL) /*!< MSPI INTSTAT: CMDCMP (Bit 0) */ -#define MSPI_INTSTAT_CMDCMP_Msk (0x1UL) /*!< MSPI INTSTAT: CMDCMP (Bitfield-Mask: 0x01) */ -/* ======================================================== INTCLR ========================================================= */ -#define MSPI_INTCLR_SCRERR_Pos (12UL) /*!< MSPI INTCLR: SCRERR (Bit 12) */ -#define MSPI_INTCLR_SCRERR_Msk (0x1000UL) /*!< MSPI INTCLR: SCRERR (Bitfield-Mask: 0x01) */ -#define MSPI_INTCLR_CQERR_Pos (11UL) /*!< MSPI INTCLR: CQERR (Bit 11) */ -#define MSPI_INTCLR_CQERR_Msk (0x800UL) /*!< MSPI INTCLR: CQERR (Bitfield-Mask: 0x01) */ -#define MSPI_INTCLR_CQPAUSED_Pos (10UL) /*!< MSPI INTCLR: CQPAUSED (Bit 10) */ -#define MSPI_INTCLR_CQPAUSED_Msk (0x400UL) /*!< MSPI INTCLR: CQPAUSED (Bitfield-Mask: 0x01) */ -#define MSPI_INTCLR_CQUPD_Pos (9UL) /*!< MSPI INTCLR: CQUPD (Bit 9) */ -#define MSPI_INTCLR_CQUPD_Msk (0x200UL) /*!< MSPI INTCLR: CQUPD (Bitfield-Mask: 0x01) */ -#define MSPI_INTCLR_CQCMP_Pos (8UL) /*!< MSPI INTCLR: CQCMP (Bit 8) */ -#define MSPI_INTCLR_CQCMP_Msk (0x100UL) /*!< MSPI INTCLR: CQCMP (Bitfield-Mask: 0x01) */ -#define MSPI_INTCLR_DERR_Pos (7UL) /*!< MSPI INTCLR: DERR (Bit 7) */ -#define MSPI_INTCLR_DERR_Msk (0x80UL) /*!< MSPI INTCLR: DERR (Bitfield-Mask: 0x01) */ -#define MSPI_INTCLR_DCMP_Pos (6UL) /*!< MSPI INTCLR: DCMP (Bit 6) */ -#define MSPI_INTCLR_DCMP_Msk (0x40UL) /*!< MSPI INTCLR: DCMP (Bitfield-Mask: 0x01) */ -#define MSPI_INTCLR_RXF_Pos (5UL) /*!< MSPI INTCLR: RXF (Bit 5) */ -#define MSPI_INTCLR_RXF_Msk (0x20UL) /*!< MSPI INTCLR: RXF (Bitfield-Mask: 0x01) */ -#define MSPI_INTCLR_RXO_Pos (4UL) /*!< MSPI INTCLR: RXO (Bit 4) */ -#define MSPI_INTCLR_RXO_Msk (0x10UL) /*!< MSPI INTCLR: RXO (Bitfield-Mask: 0x01) */ -#define MSPI_INTCLR_RXU_Pos (3UL) /*!< MSPI INTCLR: RXU (Bit 3) */ -#define MSPI_INTCLR_RXU_Msk (0x8UL) /*!< MSPI INTCLR: RXU (Bitfield-Mask: 0x01) */ -#define MSPI_INTCLR_TXO_Pos (2UL) /*!< MSPI INTCLR: TXO (Bit 2) */ -#define MSPI_INTCLR_TXO_Msk (0x4UL) /*!< MSPI INTCLR: TXO (Bitfield-Mask: 0x01) */ -#define MSPI_INTCLR_TXE_Pos (1UL) /*!< MSPI INTCLR: TXE (Bit 1) */ -#define MSPI_INTCLR_TXE_Msk (0x2UL) /*!< MSPI INTCLR: TXE (Bitfield-Mask: 0x01) */ -#define MSPI_INTCLR_CMDCMP_Pos (0UL) /*!< MSPI INTCLR: CMDCMP (Bit 0) */ -#define MSPI_INTCLR_CMDCMP_Msk (0x1UL) /*!< MSPI INTCLR: CMDCMP (Bitfield-Mask: 0x01) */ -/* ======================================================== INTSET ========================================================= */ -#define MSPI_INTSET_SCRERR_Pos (12UL) /*!< MSPI INTSET: SCRERR (Bit 12) */ -#define MSPI_INTSET_SCRERR_Msk (0x1000UL) /*!< MSPI INTSET: SCRERR (Bitfield-Mask: 0x01) */ -#define MSPI_INTSET_CQERR_Pos (11UL) /*!< MSPI INTSET: CQERR (Bit 11) */ -#define MSPI_INTSET_CQERR_Msk (0x800UL) /*!< MSPI INTSET: CQERR (Bitfield-Mask: 0x01) */ -#define MSPI_INTSET_CQPAUSED_Pos (10UL) /*!< MSPI INTSET: CQPAUSED (Bit 10) */ -#define MSPI_INTSET_CQPAUSED_Msk (0x400UL) /*!< MSPI INTSET: CQPAUSED (Bitfield-Mask: 0x01) */ -#define MSPI_INTSET_CQUPD_Pos (9UL) /*!< MSPI INTSET: CQUPD (Bit 9) */ -#define MSPI_INTSET_CQUPD_Msk (0x200UL) /*!< MSPI INTSET: CQUPD (Bitfield-Mask: 0x01) */ -#define MSPI_INTSET_CQCMP_Pos (8UL) /*!< MSPI INTSET: CQCMP (Bit 8) */ -#define MSPI_INTSET_CQCMP_Msk (0x100UL) /*!< MSPI INTSET: CQCMP (Bitfield-Mask: 0x01) */ -#define MSPI_INTSET_DERR_Pos (7UL) /*!< MSPI INTSET: DERR (Bit 7) */ -#define MSPI_INTSET_DERR_Msk (0x80UL) /*!< MSPI INTSET: DERR (Bitfield-Mask: 0x01) */ -#define MSPI_INTSET_DCMP_Pos (6UL) /*!< MSPI INTSET: DCMP (Bit 6) */ -#define MSPI_INTSET_DCMP_Msk (0x40UL) /*!< MSPI INTSET: DCMP (Bitfield-Mask: 0x01) */ -#define MSPI_INTSET_RXF_Pos (5UL) /*!< MSPI INTSET: RXF (Bit 5) */ -#define MSPI_INTSET_RXF_Msk (0x20UL) /*!< MSPI INTSET: RXF (Bitfield-Mask: 0x01) */ -#define MSPI_INTSET_RXO_Pos (4UL) /*!< MSPI INTSET: RXO (Bit 4) */ -#define MSPI_INTSET_RXO_Msk (0x10UL) /*!< MSPI INTSET: RXO (Bitfield-Mask: 0x01) */ -#define MSPI_INTSET_RXU_Pos (3UL) /*!< MSPI INTSET: RXU (Bit 3) */ -#define MSPI_INTSET_RXU_Msk (0x8UL) /*!< MSPI INTSET: RXU (Bitfield-Mask: 0x01) */ -#define MSPI_INTSET_TXO_Pos (2UL) /*!< MSPI INTSET: TXO (Bit 2) */ -#define MSPI_INTSET_TXO_Msk (0x4UL) /*!< MSPI INTSET: TXO (Bitfield-Mask: 0x01) */ -#define MSPI_INTSET_TXE_Pos (1UL) /*!< MSPI INTSET: TXE (Bit 1) */ -#define MSPI_INTSET_TXE_Msk (0x2UL) /*!< MSPI INTSET: TXE (Bitfield-Mask: 0x01) */ -#define MSPI_INTSET_CMDCMP_Pos (0UL) /*!< MSPI INTSET: CMDCMP (Bit 0) */ -#define MSPI_INTSET_CMDCMP_Msk (0x1UL) /*!< MSPI INTSET: CMDCMP (Bitfield-Mask: 0x01) */ -/* ======================================================== DMACFG ========================================================= */ -#define MSPI_DMACFG_DMAPWROFF_Pos (18UL) /*!< MSPI DMACFG: DMAPWROFF (Bit 18) */ -#define MSPI_DMACFG_DMAPWROFF_Msk (0x40000UL) /*!< MSPI DMACFG: DMAPWROFF (Bitfield-Mask: 0x01) */ -#define MSPI_DMACFG_DMAPRI_Pos (3UL) /*!< MSPI DMACFG: DMAPRI (Bit 3) */ -#define MSPI_DMACFG_DMAPRI_Msk (0x18UL) /*!< MSPI DMACFG: DMAPRI (Bitfield-Mask: 0x03) */ -#define MSPI_DMACFG_DMADIR_Pos (2UL) /*!< MSPI DMACFG: DMADIR (Bit 2) */ -#define MSPI_DMACFG_DMADIR_Msk (0x4UL) /*!< MSPI DMACFG: DMADIR (Bitfield-Mask: 0x01) */ -#define MSPI_DMACFG_DMAEN_Pos (0UL) /*!< MSPI DMACFG: DMAEN (Bit 0) */ -#define MSPI_DMACFG_DMAEN_Msk (0x3UL) /*!< MSPI DMACFG: DMAEN (Bitfield-Mask: 0x03) */ -/* ======================================================== DMASTAT ======================================================== */ -#define MSPI_DMASTAT_SCRERR_Pos (3UL) /*!< MSPI DMASTAT: SCRERR (Bit 3) */ -#define MSPI_DMASTAT_SCRERR_Msk (0x8UL) /*!< MSPI DMASTAT: SCRERR (Bitfield-Mask: 0x01) */ -#define MSPI_DMASTAT_DMAERR_Pos (2UL) /*!< MSPI DMASTAT: DMAERR (Bit 2) */ -#define MSPI_DMASTAT_DMAERR_Msk (0x4UL) /*!< MSPI DMASTAT: DMAERR (Bitfield-Mask: 0x01) */ -#define MSPI_DMASTAT_DMACPL_Pos (1UL) /*!< MSPI DMASTAT: DMACPL (Bit 1) */ -#define MSPI_DMASTAT_DMACPL_Msk (0x2UL) /*!< MSPI DMASTAT: DMACPL (Bitfield-Mask: 0x01) */ -#define MSPI_DMASTAT_DMATIP_Pos (0UL) /*!< MSPI DMASTAT: DMATIP (Bit 0) */ -#define MSPI_DMASTAT_DMATIP_Msk (0x1UL) /*!< MSPI DMASTAT: DMATIP (Bitfield-Mask: 0x01) */ -/* ====================================================== DMATARGADDR ====================================================== */ -#define MSPI_DMATARGADDR_TARGADDR_Pos (0UL) /*!< MSPI DMATARGADDR: TARGADDR (Bit 0) */ -#define MSPI_DMATARGADDR_TARGADDR_Msk (0xffffffffUL) /*!< MSPI DMATARGADDR: TARGADDR (Bitfield-Mask: 0xffffffff) */ -/* ====================================================== DMADEVADDR ======================================================= */ -#define MSPI_DMADEVADDR_DEVADDR_Pos (0UL) /*!< MSPI DMADEVADDR: DEVADDR (Bit 0) */ -#define MSPI_DMADEVADDR_DEVADDR_Msk (0xffffffffUL) /*!< MSPI DMADEVADDR: DEVADDR (Bitfield-Mask: 0xffffffff) */ -/* ====================================================== DMATOTCOUNT ====================================================== */ -#define MSPI_DMATOTCOUNT_TOTCOUNT_Pos (0UL) /*!< MSPI DMATOTCOUNT: TOTCOUNT (Bit 0) */ -#define MSPI_DMATOTCOUNT_TOTCOUNT_Msk (0xffffUL) /*!< MSPI DMATOTCOUNT: TOTCOUNT (Bitfield-Mask: 0xffff) */ -/* ======================================================= DMABCOUNT ======================================================= */ -#define MSPI_DMABCOUNT_BCOUNT_Pos (0UL) /*!< MSPI DMABCOUNT: BCOUNT (Bit 0) */ -#define MSPI_DMABCOUNT_BCOUNT_Msk (0xffUL) /*!< MSPI DMABCOUNT: BCOUNT (Bitfield-Mask: 0xff) */ -/* ======================================================= DMATHRESH ======================================================= */ -#define MSPI_DMATHRESH_DMATHRESH_Pos (0UL) /*!< MSPI DMATHRESH: DMATHRESH (Bit 0) */ -#define MSPI_DMATHRESH_DMATHRESH_Msk (0xfUL) /*!< MSPI DMATHRESH: DMATHRESH (Bitfield-Mask: 0x0f) */ -/* ========================================================= CQCFG ========================================================= */ -#define MSPI_CQCFG_CQAUTOCLEARMASK_Pos (3UL) /*!< MSPI CQCFG: CQAUTOCLEARMASK (Bit 3) */ -#define MSPI_CQCFG_CQAUTOCLEARMASK_Msk (0x8UL) /*!< MSPI CQCFG: CQAUTOCLEARMASK (Bitfield-Mask: 0x01) */ -#define MSPI_CQCFG_CQPWROFF_Pos (2UL) /*!< MSPI CQCFG: CQPWROFF (Bit 2) */ -#define MSPI_CQCFG_CQPWROFF_Msk (0x4UL) /*!< MSPI CQCFG: CQPWROFF (Bitfield-Mask: 0x01) */ -#define MSPI_CQCFG_CQPRI_Pos (1UL) /*!< MSPI CQCFG: CQPRI (Bit 1) */ -#define MSPI_CQCFG_CQPRI_Msk (0x2UL) /*!< MSPI CQCFG: CQPRI (Bitfield-Mask: 0x01) */ -#define MSPI_CQCFG_CQEN_Pos (0UL) /*!< MSPI CQCFG: CQEN (Bit 0) */ -#define MSPI_CQCFG_CQEN_Msk (0x1UL) /*!< MSPI CQCFG: CQEN (Bitfield-Mask: 0x01) */ -/* ======================================================== CQADDR ========================================================= */ -#define MSPI_CQADDR_CQADDR_Pos (0UL) /*!< MSPI CQADDR: CQADDR (Bit 0) */ -#define MSPI_CQADDR_CQADDR_Msk (0x1fffffffUL) /*!< MSPI CQADDR: CQADDR (Bitfield-Mask: 0x1fffffff) */ -/* ======================================================== CQSTAT ========================================================= */ -#define MSPI_CQSTAT_CQPAUSED_Pos (3UL) /*!< MSPI CQSTAT: CQPAUSED (Bit 3) */ -#define MSPI_CQSTAT_CQPAUSED_Msk (0x8UL) /*!< MSPI CQSTAT: CQPAUSED (Bitfield-Mask: 0x01) */ -#define MSPI_CQSTAT_CQERR_Pos (2UL) /*!< MSPI CQSTAT: CQERR (Bit 2) */ -#define MSPI_CQSTAT_CQERR_Msk (0x4UL) /*!< MSPI CQSTAT: CQERR (Bitfield-Mask: 0x01) */ -#define MSPI_CQSTAT_CQCPL_Pos (1UL) /*!< MSPI CQSTAT: CQCPL (Bit 1) */ -#define MSPI_CQSTAT_CQCPL_Msk (0x2UL) /*!< MSPI CQSTAT: CQCPL (Bitfield-Mask: 0x01) */ -#define MSPI_CQSTAT_CQTIP_Pos (0UL) /*!< MSPI CQSTAT: CQTIP (Bit 0) */ -#define MSPI_CQSTAT_CQTIP_Msk (0x1UL) /*!< MSPI CQSTAT: CQTIP (Bitfield-Mask: 0x01) */ -/* ======================================================== CQFLAGS ======================================================== */ -#define MSPI_CQFLAGS_CQFLAGS_Pos (0UL) /*!< MSPI CQFLAGS: CQFLAGS (Bit 0) */ -#define MSPI_CQFLAGS_CQFLAGS_Msk (0xffffUL) /*!< MSPI CQFLAGS: CQFLAGS (Bitfield-Mask: 0xffff) */ -/* ====================================================== CQSETCLEAR ======================================================= */ -#define MSPI_CQSETCLEAR_CQFCLR_Pos (16UL) /*!< MSPI CQSETCLEAR: CQFCLR (Bit 16) */ -#define MSPI_CQSETCLEAR_CQFCLR_Msk (0xff0000UL) /*!< MSPI CQSETCLEAR: CQFCLR (Bitfield-Mask: 0xff) */ -#define MSPI_CQSETCLEAR_CQFTOGGLE_Pos (8UL) /*!< MSPI CQSETCLEAR: CQFTOGGLE (Bit 8) */ -#define MSPI_CQSETCLEAR_CQFTOGGLE_Msk (0xff00UL) /*!< MSPI CQSETCLEAR: CQFTOGGLE (Bitfield-Mask: 0xff) */ -#define MSPI_CQSETCLEAR_CQFSET_Pos (0UL) /*!< MSPI CQSETCLEAR: CQFSET (Bit 0) */ -#define MSPI_CQSETCLEAR_CQFSET_Msk (0xffUL) /*!< MSPI CQSETCLEAR: CQFSET (Bitfield-Mask: 0xff) */ -/* ======================================================== CQPAUSE ======================================================== */ -#define MSPI_CQPAUSE_CQMASK_Pos (0UL) /*!< MSPI CQPAUSE: CQMASK (Bit 0) */ -#define MSPI_CQPAUSE_CQMASK_Msk (0xffffUL) /*!< MSPI CQPAUSE: CQMASK (Bitfield-Mask: 0xffff) */ -/* ======================================================= CQCURIDX ======================================================== */ -#define MSPI_CQCURIDX_CQCURIDX_Pos (0UL) /*!< MSPI CQCURIDX: CQCURIDX (Bit 0) */ -#define MSPI_CQCURIDX_CQCURIDX_Msk (0xffUL) /*!< MSPI CQCURIDX: CQCURIDX (Bitfield-Mask: 0xff) */ -/* ======================================================= CQENDIDX ======================================================== */ -#define MSPI_CQENDIDX_CQENDIDX_Pos (0UL) /*!< MSPI CQENDIDX: CQENDIDX (Bit 0) */ -#define MSPI_CQENDIDX_CQENDIDX_Msk (0xffUL) /*!< MSPI CQENDIDX: CQENDIDX (Bitfield-Mask: 0xff) */ - - -/* =========================================================================================================================== */ -/* ================ PDM ================ */ -/* =========================================================================================================================== */ - -/* ========================================================= PCFG ========================================================== */ -#define PDM_PCFG_LRSWAP_Pos (31UL) /*!< PDM PCFG: LRSWAP (Bit 31) */ -#define PDM_PCFG_LRSWAP_Msk (0x80000000UL) /*!< PDM PCFG: LRSWAP (Bitfield-Mask: 0x01) */ -#define PDM_PCFG_PGARIGHT_Pos (26UL) /*!< PDM PCFG: PGARIGHT (Bit 26) */ -#define PDM_PCFG_PGARIGHT_Msk (0x7c000000UL) /*!< PDM PCFG: PGARIGHT (Bitfield-Mask: 0x1f) */ -#define PDM_PCFG_PGALEFT_Pos (21UL) /*!< PDM PCFG: PGALEFT (Bit 21) */ -#define PDM_PCFG_PGALEFT_Msk (0x3e00000UL) /*!< PDM PCFG: PGALEFT (Bitfield-Mask: 0x1f) */ -#define PDM_PCFG_MCLKDIV_Pos (17UL) /*!< PDM PCFG: MCLKDIV (Bit 17) */ -#define PDM_PCFG_MCLKDIV_Msk (0x60000UL) /*!< PDM PCFG: MCLKDIV (Bitfield-Mask: 0x03) */ -#define PDM_PCFG_SINCRATE_Pos (10UL) /*!< PDM PCFG: SINCRATE (Bit 10) */ -#define PDM_PCFG_SINCRATE_Msk (0x1fc00UL) /*!< PDM PCFG: SINCRATE (Bitfield-Mask: 0x7f) */ -#define PDM_PCFG_ADCHPD_Pos (9UL) /*!< PDM PCFG: ADCHPD (Bit 9) */ -#define PDM_PCFG_ADCHPD_Msk (0x200UL) /*!< PDM PCFG: ADCHPD (Bitfield-Mask: 0x01) */ -#define PDM_PCFG_HPCUTOFF_Pos (5UL) /*!< PDM PCFG: HPCUTOFF (Bit 5) */ -#define PDM_PCFG_HPCUTOFF_Msk (0x1e0UL) /*!< PDM PCFG: HPCUTOFF (Bitfield-Mask: 0x0f) */ -#define PDM_PCFG_CYCLES_Pos (2UL) /*!< PDM PCFG: CYCLES (Bit 2) */ -#define PDM_PCFG_CYCLES_Msk (0x1cUL) /*!< PDM PCFG: CYCLES (Bitfield-Mask: 0x07) */ -#define PDM_PCFG_SOFTMUTE_Pos (1UL) /*!< PDM PCFG: SOFTMUTE (Bit 1) */ -#define PDM_PCFG_SOFTMUTE_Msk (0x2UL) /*!< PDM PCFG: SOFTMUTE (Bitfield-Mask: 0x01) */ -#define PDM_PCFG_PDMCOREEN_Pos (0UL) /*!< PDM PCFG: PDMCOREEN (Bit 0) */ -#define PDM_PCFG_PDMCOREEN_Msk (0x1UL) /*!< PDM PCFG: PDMCOREEN (Bitfield-Mask: 0x01) */ -/* ========================================================= VCFG ========================================================== */ -#define PDM_VCFG_IOCLKEN_Pos (31UL) /*!< PDM VCFG: IOCLKEN (Bit 31) */ -#define PDM_VCFG_IOCLKEN_Msk (0x80000000UL) /*!< PDM VCFG: IOCLKEN (Bitfield-Mask: 0x01) */ -#define PDM_VCFG_RSTB_Pos (30UL) /*!< PDM VCFG: RSTB (Bit 30) */ -#define PDM_VCFG_RSTB_Msk (0x40000000UL) /*!< PDM VCFG: RSTB (Bitfield-Mask: 0x01) */ -#define PDM_VCFG_PDMCLKSEL_Pos (27UL) /*!< PDM VCFG: PDMCLKSEL (Bit 27) */ -#define PDM_VCFG_PDMCLKSEL_Msk (0x38000000UL) /*!< PDM VCFG: PDMCLKSEL (Bitfield-Mask: 0x07) */ -#define PDM_VCFG_PDMCLKEN_Pos (26UL) /*!< PDM VCFG: PDMCLKEN (Bit 26) */ -#define PDM_VCFG_PDMCLKEN_Msk (0x4000000UL) /*!< PDM VCFG: PDMCLKEN (Bitfield-Mask: 0x01) */ -#define PDM_VCFG_I2SEN_Pos (20UL) /*!< PDM VCFG: I2SEN (Bit 20) */ -#define PDM_VCFG_I2SEN_Msk (0x100000UL) /*!< PDM VCFG: I2SEN (Bitfield-Mask: 0x01) */ -#define PDM_VCFG_BCLKINV_Pos (19UL) /*!< PDM VCFG: BCLKINV (Bit 19) */ -#define PDM_VCFG_BCLKINV_Msk (0x80000UL) /*!< PDM VCFG: BCLKINV (Bitfield-Mask: 0x01) */ -#define PDM_VCFG_DMICKDEL_Pos (17UL) /*!< PDM VCFG: DMICKDEL (Bit 17) */ -#define PDM_VCFG_DMICKDEL_Msk (0x20000UL) /*!< PDM VCFG: DMICKDEL (Bitfield-Mask: 0x01) */ -#define PDM_VCFG_SELAP_Pos (16UL) /*!< PDM VCFG: SELAP (Bit 16) */ -#define PDM_VCFG_SELAP_Msk (0x10000UL) /*!< PDM VCFG: SELAP (Bitfield-Mask: 0x01) */ -#define PDM_VCFG_PCMPACK_Pos (8UL) /*!< PDM VCFG: PCMPACK (Bit 8) */ -#define PDM_VCFG_PCMPACK_Msk (0x100UL) /*!< PDM VCFG: PCMPACK (Bitfield-Mask: 0x01) */ -#define PDM_VCFG_CHSET_Pos (3UL) /*!< PDM VCFG: CHSET (Bit 3) */ -#define PDM_VCFG_CHSET_Msk (0x18UL) /*!< PDM VCFG: CHSET (Bitfield-Mask: 0x03) */ -/* ======================================================= VOICESTAT ======================================================= */ -#define PDM_VOICESTAT_FIFOCNT_Pos (0UL) /*!< PDM VOICESTAT: FIFOCNT (Bit 0) */ -#define PDM_VOICESTAT_FIFOCNT_Msk (0x3fUL) /*!< PDM VOICESTAT: FIFOCNT (Bitfield-Mask: 0x3f) */ -/* ======================================================= FIFOREAD ======================================================== */ -#define PDM_FIFOREAD_FIFOREAD_Pos (0UL) /*!< PDM FIFOREAD: FIFOREAD (Bit 0) */ -#define PDM_FIFOREAD_FIFOREAD_Msk (0xffffffffUL) /*!< PDM FIFOREAD: FIFOREAD (Bitfield-Mask: 0xffffffff) */ -/* ======================================================= FIFOFLUSH ======================================================= */ -#define PDM_FIFOFLUSH_FIFOFLUSH_Pos (0UL) /*!< PDM FIFOFLUSH: FIFOFLUSH (Bit 0) */ -#define PDM_FIFOFLUSH_FIFOFLUSH_Msk (0x1UL) /*!< PDM FIFOFLUSH: FIFOFLUSH (Bitfield-Mask: 0x01) */ -/* ======================================================== FIFOTHR ======================================================== */ -#define PDM_FIFOTHR_FIFOTHR_Pos (0UL) /*!< PDM FIFOTHR: FIFOTHR (Bit 0) */ -#define PDM_FIFOTHR_FIFOTHR_Msk (0x1fUL) /*!< PDM FIFOTHR: FIFOTHR (Bitfield-Mask: 0x1f) */ -/* ========================================================= INTEN ========================================================= */ -#define PDM_INTEN_DERR_Pos (4UL) /*!< PDM INTEN: DERR (Bit 4) */ -#define PDM_INTEN_DERR_Msk (0x10UL) /*!< PDM INTEN: DERR (Bitfield-Mask: 0x01) */ -#define PDM_INTEN_DCMP_Pos (3UL) /*!< PDM INTEN: DCMP (Bit 3) */ -#define PDM_INTEN_DCMP_Msk (0x8UL) /*!< PDM INTEN: DCMP (Bitfield-Mask: 0x01) */ -#define PDM_INTEN_UNDFL_Pos (2UL) /*!< PDM INTEN: UNDFL (Bit 2) */ -#define PDM_INTEN_UNDFL_Msk (0x4UL) /*!< PDM INTEN: UNDFL (Bitfield-Mask: 0x01) */ -#define PDM_INTEN_OVF_Pos (1UL) /*!< PDM INTEN: OVF (Bit 1) */ -#define PDM_INTEN_OVF_Msk (0x2UL) /*!< PDM INTEN: OVF (Bitfield-Mask: 0x01) */ -#define PDM_INTEN_THR_Pos (0UL) /*!< PDM INTEN: THR (Bit 0) */ -#define PDM_INTEN_THR_Msk (0x1UL) /*!< PDM INTEN: THR (Bitfield-Mask: 0x01) */ -/* ======================================================== INTSTAT ======================================================== */ -#define PDM_INTSTAT_DERR_Pos (4UL) /*!< PDM INTSTAT: DERR (Bit 4) */ -#define PDM_INTSTAT_DERR_Msk (0x10UL) /*!< PDM INTSTAT: DERR (Bitfield-Mask: 0x01) */ -#define PDM_INTSTAT_DCMP_Pos (3UL) /*!< PDM INTSTAT: DCMP (Bit 3) */ -#define PDM_INTSTAT_DCMP_Msk (0x8UL) /*!< PDM INTSTAT: DCMP (Bitfield-Mask: 0x01) */ -#define PDM_INTSTAT_UNDFL_Pos (2UL) /*!< PDM INTSTAT: UNDFL (Bit 2) */ -#define PDM_INTSTAT_UNDFL_Msk (0x4UL) /*!< PDM INTSTAT: UNDFL (Bitfield-Mask: 0x01) */ -#define PDM_INTSTAT_OVF_Pos (1UL) /*!< PDM INTSTAT: OVF (Bit 1) */ -#define PDM_INTSTAT_OVF_Msk (0x2UL) /*!< PDM INTSTAT: OVF (Bitfield-Mask: 0x01) */ -#define PDM_INTSTAT_THR_Pos (0UL) /*!< PDM INTSTAT: THR (Bit 0) */ -#define PDM_INTSTAT_THR_Msk (0x1UL) /*!< PDM INTSTAT: THR (Bitfield-Mask: 0x01) */ -/* ======================================================== INTCLR ========================================================= */ -#define PDM_INTCLR_DERR_Pos (4UL) /*!< PDM INTCLR: DERR (Bit 4) */ -#define PDM_INTCLR_DERR_Msk (0x10UL) /*!< PDM INTCLR: DERR (Bitfield-Mask: 0x01) */ -#define PDM_INTCLR_DCMP_Pos (3UL) /*!< PDM INTCLR: DCMP (Bit 3) */ -#define PDM_INTCLR_DCMP_Msk (0x8UL) /*!< PDM INTCLR: DCMP (Bitfield-Mask: 0x01) */ -#define PDM_INTCLR_UNDFL_Pos (2UL) /*!< PDM INTCLR: UNDFL (Bit 2) */ -#define PDM_INTCLR_UNDFL_Msk (0x4UL) /*!< PDM INTCLR: UNDFL (Bitfield-Mask: 0x01) */ -#define PDM_INTCLR_OVF_Pos (1UL) /*!< PDM INTCLR: OVF (Bit 1) */ -#define PDM_INTCLR_OVF_Msk (0x2UL) /*!< PDM INTCLR: OVF (Bitfield-Mask: 0x01) */ -#define PDM_INTCLR_THR_Pos (0UL) /*!< PDM INTCLR: THR (Bit 0) */ -#define PDM_INTCLR_THR_Msk (0x1UL) /*!< PDM INTCLR: THR (Bitfield-Mask: 0x01) */ -/* ======================================================== INTSET ========================================================= */ -#define PDM_INTSET_DERR_Pos (4UL) /*!< PDM INTSET: DERR (Bit 4) */ -#define PDM_INTSET_DERR_Msk (0x10UL) /*!< PDM INTSET: DERR (Bitfield-Mask: 0x01) */ -#define PDM_INTSET_DCMP_Pos (3UL) /*!< PDM INTSET: DCMP (Bit 3) */ -#define PDM_INTSET_DCMP_Msk (0x8UL) /*!< PDM INTSET: DCMP (Bitfield-Mask: 0x01) */ -#define PDM_INTSET_UNDFL_Pos (2UL) /*!< PDM INTSET: UNDFL (Bit 2) */ -#define PDM_INTSET_UNDFL_Msk (0x4UL) /*!< PDM INTSET: UNDFL (Bitfield-Mask: 0x01) */ -#define PDM_INTSET_OVF_Pos (1UL) /*!< PDM INTSET: OVF (Bit 1) */ -#define PDM_INTSET_OVF_Msk (0x2UL) /*!< PDM INTSET: OVF (Bitfield-Mask: 0x01) */ -#define PDM_INTSET_THR_Pos (0UL) /*!< PDM INTSET: THR (Bit 0) */ -#define PDM_INTSET_THR_Msk (0x1UL) /*!< PDM INTSET: THR (Bitfield-Mask: 0x01) */ -/* ======================================================= DMATRIGEN ======================================================= */ -#define PDM_DMATRIGEN_DTHR90_Pos (1UL) /*!< PDM DMATRIGEN: DTHR90 (Bit 1) */ -#define PDM_DMATRIGEN_DTHR90_Msk (0x2UL) /*!< PDM DMATRIGEN: DTHR90 (Bitfield-Mask: 0x01) */ -#define PDM_DMATRIGEN_DTHR_Pos (0UL) /*!< PDM DMATRIGEN: DTHR (Bit 0) */ -#define PDM_DMATRIGEN_DTHR_Msk (0x1UL) /*!< PDM DMATRIGEN: DTHR (Bitfield-Mask: 0x01) */ -/* ====================================================== DMATRIGSTAT ====================================================== */ -#define PDM_DMATRIGSTAT_DTHR90STAT_Pos (1UL) /*!< PDM DMATRIGSTAT: DTHR90STAT (Bit 1) */ -#define PDM_DMATRIGSTAT_DTHR90STAT_Msk (0x2UL) /*!< PDM DMATRIGSTAT: DTHR90STAT (Bitfield-Mask: 0x01) */ -#define PDM_DMATRIGSTAT_DTHRSTAT_Pos (0UL) /*!< PDM DMATRIGSTAT: DTHRSTAT (Bit 0) */ -#define PDM_DMATRIGSTAT_DTHRSTAT_Msk (0x1UL) /*!< PDM DMATRIGSTAT: DTHRSTAT (Bitfield-Mask: 0x01) */ -/* ======================================================== DMACFG ========================================================= */ -#define PDM_DMACFG_DPWROFF_Pos (10UL) /*!< PDM DMACFG: DPWROFF (Bit 10) */ -#define PDM_DMACFG_DPWROFF_Msk (0x400UL) /*!< PDM DMACFG: DPWROFF (Bitfield-Mask: 0x01) */ -#define PDM_DMACFG_DAUTOHIP_Pos (9UL) /*!< PDM DMACFG: DAUTOHIP (Bit 9) */ -#define PDM_DMACFG_DAUTOHIP_Msk (0x200UL) /*!< PDM DMACFG: DAUTOHIP (Bitfield-Mask: 0x01) */ -#define PDM_DMACFG_DMAPRI_Pos (8UL) /*!< PDM DMACFG: DMAPRI (Bit 8) */ -#define PDM_DMACFG_DMAPRI_Msk (0x100UL) /*!< PDM DMACFG: DMAPRI (Bitfield-Mask: 0x01) */ -#define PDM_DMACFG_DMADIR_Pos (2UL) /*!< PDM DMACFG: DMADIR (Bit 2) */ -#define PDM_DMACFG_DMADIR_Msk (0x4UL) /*!< PDM DMACFG: DMADIR (Bitfield-Mask: 0x01) */ -#define PDM_DMACFG_DMAEN_Pos (0UL) /*!< PDM DMACFG: DMAEN (Bit 0) */ -#define PDM_DMACFG_DMAEN_Msk (0x1UL) /*!< PDM DMACFG: DMAEN (Bitfield-Mask: 0x01) */ -/* ====================================================== DMATOTCOUNT ====================================================== */ -#define PDM_DMATOTCOUNT_TOTCOUNT_Pos (0UL) /*!< PDM DMATOTCOUNT: TOTCOUNT (Bit 0) */ -#define PDM_DMATOTCOUNT_TOTCOUNT_Msk (0xfffffUL) /*!< PDM DMATOTCOUNT: TOTCOUNT (Bitfield-Mask: 0xfffff) */ -/* ====================================================== DMATARGADDR ====================================================== */ -#define PDM_DMATARGADDR_UTARGADDR_Pos (20UL) /*!< PDM DMATARGADDR: UTARGADDR (Bit 20) */ -#define PDM_DMATARGADDR_UTARGADDR_Msk (0xfff00000UL) /*!< PDM DMATARGADDR: UTARGADDR (Bitfield-Mask: 0xfff) */ -#define PDM_DMATARGADDR_LTARGADDR_Pos (0UL) /*!< PDM DMATARGADDR: LTARGADDR (Bit 0) */ -#define PDM_DMATARGADDR_LTARGADDR_Msk (0xfffffUL) /*!< PDM DMATARGADDR: LTARGADDR (Bitfield-Mask: 0xfffff) */ -/* ======================================================== DMASTAT ======================================================== */ -#define PDM_DMASTAT_DMAERR_Pos (2UL) /*!< PDM DMASTAT: DMAERR (Bit 2) */ -#define PDM_DMASTAT_DMAERR_Msk (0x4UL) /*!< PDM DMASTAT: DMAERR (Bitfield-Mask: 0x01) */ -#define PDM_DMASTAT_DMACPL_Pos (1UL) /*!< PDM DMASTAT: DMACPL (Bit 1) */ -#define PDM_DMASTAT_DMACPL_Msk (0x2UL) /*!< PDM DMASTAT: DMACPL (Bitfield-Mask: 0x01) */ -#define PDM_DMASTAT_DMATIP_Pos (0UL) /*!< PDM DMASTAT: DMATIP (Bit 0) */ -#define PDM_DMASTAT_DMATIP_Msk (0x1UL) /*!< PDM DMASTAT: DMATIP (Bitfield-Mask: 0x01) */ - - -/* =========================================================================================================================== */ -/* ================ PWRCTRL ================ */ -/* =========================================================================================================================== */ - -/* ======================================================= SUPPLYSRC ======================================================= */ -#define PWRCTRL_SUPPLYSRC_BLEBUCKEN_Pos (0UL) /*!< PWRCTRL SUPPLYSRC: BLEBUCKEN (Bit 0) */ -#define PWRCTRL_SUPPLYSRC_BLEBUCKEN_Msk (0x1UL) /*!< PWRCTRL SUPPLYSRC: BLEBUCKEN (Bitfield-Mask: 0x01) */ -/* ===================================================== SUPPLYSTATUS ====================================================== */ -#define PWRCTRL_SUPPLYSTATUS_BLEBUCKON_Pos (1UL) /*!< PWRCTRL SUPPLYSTATUS: BLEBUCKON (Bit 1) */ -#define PWRCTRL_SUPPLYSTATUS_BLEBUCKON_Msk (0x2UL) /*!< PWRCTRL SUPPLYSTATUS: BLEBUCKON (Bitfield-Mask: 0x01) */ -#define PWRCTRL_SUPPLYSTATUS_SIMOBUCKON_Pos (0UL) /*!< PWRCTRL SUPPLYSTATUS: SIMOBUCKON (Bit 0) */ -#define PWRCTRL_SUPPLYSTATUS_SIMOBUCKON_Msk (0x1UL) /*!< PWRCTRL SUPPLYSTATUS: SIMOBUCKON (Bitfield-Mask: 0x01) */ -/* ======================================================= DEVPWREN ======================================================== */ -#define PWRCTRL_DEVPWREN_PWRBLEL_Pos (13UL) /*!< PWRCTRL DEVPWREN: PWRBLEL (Bit 13) */ -#define PWRCTRL_DEVPWREN_PWRBLEL_Msk (0x2000UL) /*!< PWRCTRL DEVPWREN: PWRBLEL (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWREN_PWRPDM_Pos (12UL) /*!< PWRCTRL DEVPWREN: PWRPDM (Bit 12) */ -#define PWRCTRL_DEVPWREN_PWRPDM_Msk (0x1000UL) /*!< PWRCTRL DEVPWREN: PWRPDM (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWREN_PWRMSPI_Pos (11UL) /*!< PWRCTRL DEVPWREN: PWRMSPI (Bit 11) */ -#define PWRCTRL_DEVPWREN_PWRMSPI_Msk (0x800UL) /*!< PWRCTRL DEVPWREN: PWRMSPI (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWREN_PWRSCARD_Pos (10UL) /*!< PWRCTRL DEVPWREN: PWRSCARD (Bit 10) */ -#define PWRCTRL_DEVPWREN_PWRSCARD_Msk (0x400UL) /*!< PWRCTRL DEVPWREN: PWRSCARD (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWREN_PWRADC_Pos (9UL) /*!< PWRCTRL DEVPWREN: PWRADC (Bit 9) */ -#define PWRCTRL_DEVPWREN_PWRADC_Msk (0x200UL) /*!< PWRCTRL DEVPWREN: PWRADC (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWREN_PWRUART1_Pos (8UL) /*!< PWRCTRL DEVPWREN: PWRUART1 (Bit 8) */ -#define PWRCTRL_DEVPWREN_PWRUART1_Msk (0x100UL) /*!< PWRCTRL DEVPWREN: PWRUART1 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWREN_PWRUART0_Pos (7UL) /*!< PWRCTRL DEVPWREN: PWRUART0 (Bit 7) */ -#define PWRCTRL_DEVPWREN_PWRUART0_Msk (0x80UL) /*!< PWRCTRL DEVPWREN: PWRUART0 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWREN_PWRIOM5_Pos (6UL) /*!< PWRCTRL DEVPWREN: PWRIOM5 (Bit 6) */ -#define PWRCTRL_DEVPWREN_PWRIOM5_Msk (0x40UL) /*!< PWRCTRL DEVPWREN: PWRIOM5 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWREN_PWRIOM4_Pos (5UL) /*!< PWRCTRL DEVPWREN: PWRIOM4 (Bit 5) */ -#define PWRCTRL_DEVPWREN_PWRIOM4_Msk (0x20UL) /*!< PWRCTRL DEVPWREN: PWRIOM4 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWREN_PWRIOM3_Pos (4UL) /*!< PWRCTRL DEVPWREN: PWRIOM3 (Bit 4) */ -#define PWRCTRL_DEVPWREN_PWRIOM3_Msk (0x10UL) /*!< PWRCTRL DEVPWREN: PWRIOM3 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWREN_PWRIOM2_Pos (3UL) /*!< PWRCTRL DEVPWREN: PWRIOM2 (Bit 3) */ -#define PWRCTRL_DEVPWREN_PWRIOM2_Msk (0x8UL) /*!< PWRCTRL DEVPWREN: PWRIOM2 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWREN_PWRIOM1_Pos (2UL) /*!< PWRCTRL DEVPWREN: PWRIOM1 (Bit 2) */ -#define PWRCTRL_DEVPWREN_PWRIOM1_Msk (0x4UL) /*!< PWRCTRL DEVPWREN: PWRIOM1 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWREN_PWRIOM0_Pos (1UL) /*!< PWRCTRL DEVPWREN: PWRIOM0 (Bit 1) */ -#define PWRCTRL_DEVPWREN_PWRIOM0_Msk (0x2UL) /*!< PWRCTRL DEVPWREN: PWRIOM0 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWREN_PWRIOS_Pos (0UL) /*!< PWRCTRL DEVPWREN: PWRIOS (Bit 0) */ -#define PWRCTRL_DEVPWREN_PWRIOS_Msk (0x1UL) /*!< PWRCTRL DEVPWREN: PWRIOS (Bitfield-Mask: 0x01) */ -/* ===================================================== MEMPWDINSLEEP ===================================================== */ -#define PWRCTRL_MEMPWDINSLEEP_CACHEPWDSLP_Pos (31UL) /*!< PWRCTRL MEMPWDINSLEEP: CACHEPWDSLP (Bit 31) */ -#define PWRCTRL_MEMPWDINSLEEP_CACHEPWDSLP_Msk (0x80000000UL) /*!< PWRCTRL MEMPWDINSLEEP: CACHEPWDSLP (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MEMPWDINSLEEP_FLASH1PWDSLP_Pos (14UL) /*!< PWRCTRL MEMPWDINSLEEP: FLASH1PWDSLP (Bit 14) */ -#define PWRCTRL_MEMPWDINSLEEP_FLASH1PWDSLP_Msk (0x4000UL) /*!< PWRCTRL MEMPWDINSLEEP: FLASH1PWDSLP (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MEMPWDINSLEEP_FLASH0PWDSLP_Pos (13UL) /*!< PWRCTRL MEMPWDINSLEEP: FLASH0PWDSLP (Bit 13) */ -#define PWRCTRL_MEMPWDINSLEEP_FLASH0PWDSLP_Msk (0x2000UL) /*!< PWRCTRL MEMPWDINSLEEP: FLASH0PWDSLP (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_Pos (3UL) /*!< PWRCTRL MEMPWDINSLEEP: SRAMPWDSLP (Bit 3) */ -#define PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_Msk (0x1ff8UL) /*!< PWRCTRL MEMPWDINSLEEP: SRAMPWDSLP (Bitfield-Mask: 0x3ff) */ -#define PWRCTRL_MEMPWDINSLEEP_DTCMPWDSLP_Pos (0UL) /*!< PWRCTRL MEMPWDINSLEEP: DTCMPWDSLP (Bit 0) */ -#define PWRCTRL_MEMPWDINSLEEP_DTCMPWDSLP_Msk (0x7UL) /*!< PWRCTRL MEMPWDINSLEEP: DTCMPWDSLP (Bitfield-Mask: 0x07) */ -/* ======================================================= MEMPWREN ======================================================== */ -#define PWRCTRL_MEMPWREN_CACHEB2_Pos (31UL) /*!< PWRCTRL MEMPWREN: CACHEB2 (Bit 31) */ -#define PWRCTRL_MEMPWREN_CACHEB2_Msk (0x80000000UL) /*!< PWRCTRL MEMPWREN: CACHEB2 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MEMPWREN_CACHEB0_Pos (30UL) /*!< PWRCTRL MEMPWREN: CACHEB0 (Bit 30) */ -#define PWRCTRL_MEMPWREN_CACHEB0_Msk (0x40000000UL) /*!< PWRCTRL MEMPWREN: CACHEB0 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MEMPWREN_FLASH1_Pos (14UL) /*!< PWRCTRL MEMPWREN: FLASH1 (Bit 14) */ -#define PWRCTRL_MEMPWREN_FLASH1_Msk (0x4000UL) /*!< PWRCTRL MEMPWREN: FLASH1 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MEMPWREN_FLASH0_Pos (13UL) /*!< PWRCTRL MEMPWREN: FLASH0 (Bit 13) */ -#define PWRCTRL_MEMPWREN_FLASH0_Msk (0x2000UL) /*!< PWRCTRL MEMPWREN: FLASH0 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MEMPWREN_SRAM_Pos (3UL) /*!< PWRCTRL MEMPWREN: SRAM (Bit 3) */ -#define PWRCTRL_MEMPWREN_SRAM_Msk (0x1ff8UL) /*!< PWRCTRL MEMPWREN: SRAM (Bitfield-Mask: 0x3ff) */ -#define PWRCTRL_MEMPWREN_DTCM_Pos (0UL) /*!< PWRCTRL MEMPWREN: DTCM (Bit 0) */ -#define PWRCTRL_MEMPWREN_DTCM_Msk (0x7UL) /*!< PWRCTRL MEMPWREN: DTCM (Bitfield-Mask: 0x07) */ -/* ===================================================== MEMPWRSTATUS ====================================================== */ -#define PWRCTRL_MEMPWRSTATUS_CACHEB2_Pos (16UL) /*!< PWRCTRL MEMPWRSTATUS: CACHEB2 (Bit 16) */ -#define PWRCTRL_MEMPWRSTATUS_CACHEB2_Msk (0x10000UL) /*!< PWRCTRL MEMPWRSTATUS: CACHEB2 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MEMPWRSTATUS_CACHEB0_Pos (15UL) /*!< PWRCTRL MEMPWRSTATUS: CACHEB0 (Bit 15) */ -#define PWRCTRL_MEMPWRSTATUS_CACHEB0_Msk (0x8000UL) /*!< PWRCTRL MEMPWRSTATUS: CACHEB0 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MEMPWRSTATUS_FLASH1_Pos (14UL) /*!< PWRCTRL MEMPWRSTATUS: FLASH1 (Bit 14) */ -#define PWRCTRL_MEMPWRSTATUS_FLASH1_Msk (0x4000UL) /*!< PWRCTRL MEMPWRSTATUS: FLASH1 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MEMPWRSTATUS_FLASH0_Pos (13UL) /*!< PWRCTRL MEMPWRSTATUS: FLASH0 (Bit 13) */ -#define PWRCTRL_MEMPWRSTATUS_FLASH0_Msk (0x2000UL) /*!< PWRCTRL MEMPWRSTATUS: FLASH0 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MEMPWRSTATUS_SRAM9_Pos (12UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM9 (Bit 12) */ -#define PWRCTRL_MEMPWRSTATUS_SRAM9_Msk (0x1000UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM9 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MEMPWRSTATUS_SRAM8_Pos (11UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM8 (Bit 11) */ -#define PWRCTRL_MEMPWRSTATUS_SRAM8_Msk (0x800UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM8 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MEMPWRSTATUS_SRAM7_Pos (10UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM7 (Bit 10) */ -#define PWRCTRL_MEMPWRSTATUS_SRAM7_Msk (0x400UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM7 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MEMPWRSTATUS_SRAM6_Pos (9UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM6 (Bit 9) */ -#define PWRCTRL_MEMPWRSTATUS_SRAM6_Msk (0x200UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM6 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MEMPWRSTATUS_SRAM5_Pos (8UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM5 (Bit 8) */ -#define PWRCTRL_MEMPWRSTATUS_SRAM5_Msk (0x100UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM5 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MEMPWRSTATUS_SRAM4_Pos (7UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM4 (Bit 7) */ -#define PWRCTRL_MEMPWRSTATUS_SRAM4_Msk (0x80UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM4 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MEMPWRSTATUS_SRAM3_Pos (6UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM3 (Bit 6) */ -#define PWRCTRL_MEMPWRSTATUS_SRAM3_Msk (0x40UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM3 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MEMPWRSTATUS_SRAM2_Pos (5UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM2 (Bit 5) */ -#define PWRCTRL_MEMPWRSTATUS_SRAM2_Msk (0x20UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM2 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MEMPWRSTATUS_SRAM1_Pos (4UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM1 (Bit 4) */ -#define PWRCTRL_MEMPWRSTATUS_SRAM1_Msk (0x10UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM1 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MEMPWRSTATUS_SRAM0_Pos (3UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM0 (Bit 3) */ -#define PWRCTRL_MEMPWRSTATUS_SRAM0_Msk (0x8UL) /*!< PWRCTRL MEMPWRSTATUS: SRAM0 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MEMPWRSTATUS_DTCM1_Pos (2UL) /*!< PWRCTRL MEMPWRSTATUS: DTCM1 (Bit 2) */ -#define PWRCTRL_MEMPWRSTATUS_DTCM1_Msk (0x4UL) /*!< PWRCTRL MEMPWRSTATUS: DTCM1 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MEMPWRSTATUS_DTCM01_Pos (1UL) /*!< PWRCTRL MEMPWRSTATUS: DTCM01 (Bit 1) */ -#define PWRCTRL_MEMPWRSTATUS_DTCM01_Msk (0x2UL) /*!< PWRCTRL MEMPWRSTATUS: DTCM01 (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MEMPWRSTATUS_DTCM00_Pos (0UL) /*!< PWRCTRL MEMPWRSTATUS: DTCM00 (Bit 0) */ -#define PWRCTRL_MEMPWRSTATUS_DTCM00_Msk (0x1UL) /*!< PWRCTRL MEMPWRSTATUS: DTCM00 (Bitfield-Mask: 0x01) */ -/* ===================================================== DEVPWRSTATUS ====================================================== */ -#define PWRCTRL_DEVPWRSTATUS_SYSDEEPSLEEP_Pos (31UL) /*!< PWRCTRL DEVPWRSTATUS: SYSDEEPSLEEP (Bit 31) */ -#define PWRCTRL_DEVPWRSTATUS_SYSDEEPSLEEP_Msk (0x80000000UL) /*!< PWRCTRL DEVPWRSTATUS: SYSDEEPSLEEP (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWRSTATUS_COREDEEPSLEEP_Pos (30UL) /*!< PWRCTRL DEVPWRSTATUS: COREDEEPSLEEP (Bit 30) */ -#define PWRCTRL_DEVPWRSTATUS_COREDEEPSLEEP_Msk (0x40000000UL) /*!< PWRCTRL DEVPWRSTATUS: COREDEEPSLEEP (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWRSTATUS_CORESLEEP_Pos (29UL) /*!< PWRCTRL DEVPWRSTATUS: CORESLEEP (Bit 29) */ -#define PWRCTRL_DEVPWRSTATUS_CORESLEEP_Msk (0x20000000UL) /*!< PWRCTRL DEVPWRSTATUS: CORESLEEP (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWRSTATUS_BLEH_Pos (9UL) /*!< PWRCTRL DEVPWRSTATUS: BLEH (Bit 9) */ -#define PWRCTRL_DEVPWRSTATUS_BLEH_Msk (0x200UL) /*!< PWRCTRL DEVPWRSTATUS: BLEH (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWRSTATUS_BLEL_Pos (8UL) /*!< PWRCTRL DEVPWRSTATUS: BLEL (Bit 8) */ -#define PWRCTRL_DEVPWRSTATUS_BLEL_Msk (0x100UL) /*!< PWRCTRL DEVPWRSTATUS: BLEL (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWRSTATUS_PWRPDM_Pos (7UL) /*!< PWRCTRL DEVPWRSTATUS: PWRPDM (Bit 7) */ -#define PWRCTRL_DEVPWRSTATUS_PWRPDM_Msk (0x80UL) /*!< PWRCTRL DEVPWRSTATUS: PWRPDM (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWRSTATUS_PWRMSPI_Pos (6UL) /*!< PWRCTRL DEVPWRSTATUS: PWRMSPI (Bit 6) */ -#define PWRCTRL_DEVPWRSTATUS_PWRMSPI_Msk (0x40UL) /*!< PWRCTRL DEVPWRSTATUS: PWRMSPI (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWRSTATUS_PWRADC_Pos (5UL) /*!< PWRCTRL DEVPWRSTATUS: PWRADC (Bit 5) */ -#define PWRCTRL_DEVPWRSTATUS_PWRADC_Msk (0x20UL) /*!< PWRCTRL DEVPWRSTATUS: PWRADC (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWRSTATUS_HCPC_Pos (4UL) /*!< PWRCTRL DEVPWRSTATUS: HCPC (Bit 4) */ -#define PWRCTRL_DEVPWRSTATUS_HCPC_Msk (0x10UL) /*!< PWRCTRL DEVPWRSTATUS: HCPC (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWRSTATUS_HCPB_Pos (3UL) /*!< PWRCTRL DEVPWRSTATUS: HCPB (Bit 3) */ -#define PWRCTRL_DEVPWRSTATUS_HCPB_Msk (0x8UL) /*!< PWRCTRL DEVPWRSTATUS: HCPB (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWRSTATUS_HCPA_Pos (2UL) /*!< PWRCTRL DEVPWRSTATUS: HCPA (Bit 2) */ -#define PWRCTRL_DEVPWRSTATUS_HCPA_Msk (0x4UL) /*!< PWRCTRL DEVPWRSTATUS: HCPA (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWRSTATUS_MCUH_Pos (1UL) /*!< PWRCTRL DEVPWRSTATUS: MCUH (Bit 1) */ -#define PWRCTRL_DEVPWRSTATUS_MCUH_Msk (0x2UL) /*!< PWRCTRL DEVPWRSTATUS: MCUH (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWRSTATUS_MCUL_Pos (0UL) /*!< PWRCTRL DEVPWRSTATUS: MCUL (Bit 0) */ -#define PWRCTRL_DEVPWRSTATUS_MCUL_Msk (0x1UL) /*!< PWRCTRL DEVPWRSTATUS: MCUL (Bitfield-Mask: 0x01) */ -/* ======================================================= SRAMCTRL ======================================================== */ -#define PWRCTRL_SRAMCTRL_SRAMLIGHTSLEEP_Pos (8UL) /*!< PWRCTRL SRAMCTRL: SRAMLIGHTSLEEP (Bit 8) */ -#define PWRCTRL_SRAMCTRL_SRAMLIGHTSLEEP_Msk (0xfff00UL) /*!< PWRCTRL SRAMCTRL: SRAMLIGHTSLEEP (Bitfield-Mask: 0xfff) */ -#define PWRCTRL_SRAMCTRL_SRAMMASTERCLKGATE_Pos (2UL) /*!< PWRCTRL SRAMCTRL: SRAMMASTERCLKGATE (Bit 2) */ -#define PWRCTRL_SRAMCTRL_SRAMMASTERCLKGATE_Msk (0x4UL) /*!< PWRCTRL SRAMCTRL: SRAMMASTERCLKGATE (Bitfield-Mask: 0x01) */ -#define PWRCTRL_SRAMCTRL_SRAMCLKGATE_Pos (1UL) /*!< PWRCTRL SRAMCTRL: SRAMCLKGATE (Bit 1) */ -#define PWRCTRL_SRAMCTRL_SRAMCLKGATE_Msk (0x2UL) /*!< PWRCTRL SRAMCTRL: SRAMCLKGATE (Bitfield-Mask: 0x01) */ -/* ======================================================= ADCSTATUS ======================================================= */ -#define PWRCTRL_ADCSTATUS_REFBUFPWD_Pos (5UL) /*!< PWRCTRL ADCSTATUS: REFBUFPWD (Bit 5) */ -#define PWRCTRL_ADCSTATUS_REFBUFPWD_Msk (0x20UL) /*!< PWRCTRL ADCSTATUS: REFBUFPWD (Bitfield-Mask: 0x01) */ -#define PWRCTRL_ADCSTATUS_REFKEEPPWD_Pos (4UL) /*!< PWRCTRL ADCSTATUS: REFKEEPPWD (Bit 4) */ -#define PWRCTRL_ADCSTATUS_REFKEEPPWD_Msk (0x10UL) /*!< PWRCTRL ADCSTATUS: REFKEEPPWD (Bitfield-Mask: 0x01) */ -#define PWRCTRL_ADCSTATUS_VBATPWD_Pos (3UL) /*!< PWRCTRL ADCSTATUS: VBATPWD (Bit 3) */ -#define PWRCTRL_ADCSTATUS_VBATPWD_Msk (0x8UL) /*!< PWRCTRL ADCSTATUS: VBATPWD (Bitfield-Mask: 0x01) */ -#define PWRCTRL_ADCSTATUS_VPTATPWD_Pos (2UL) /*!< PWRCTRL ADCSTATUS: VPTATPWD (Bit 2) */ -#define PWRCTRL_ADCSTATUS_VPTATPWD_Msk (0x4UL) /*!< PWRCTRL ADCSTATUS: VPTATPWD (Bitfield-Mask: 0x01) */ -#define PWRCTRL_ADCSTATUS_BGTPWD_Pos (1UL) /*!< PWRCTRL ADCSTATUS: BGTPWD (Bit 1) */ -#define PWRCTRL_ADCSTATUS_BGTPWD_Msk (0x2UL) /*!< PWRCTRL ADCSTATUS: BGTPWD (Bitfield-Mask: 0x01) */ -#define PWRCTRL_ADCSTATUS_ADCPWD_Pos (0UL) /*!< PWRCTRL ADCSTATUS: ADCPWD (Bit 0) */ -#define PWRCTRL_ADCSTATUS_ADCPWD_Msk (0x1UL) /*!< PWRCTRL ADCSTATUS: ADCPWD (Bitfield-Mask: 0x01) */ -/* ========================================================= MISC ========================================================== */ -#define PWRCTRL_MISC_FORCEBLEBUCKACT_Pos (7UL) /*!< PWRCTRL MISC: FORCEBLEBUCKACT (Bit 7) */ -#define PWRCTRL_MISC_FORCEBLEBUCKACT_Msk (0x80UL) /*!< PWRCTRL MISC: FORCEBLEBUCKACT (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MISC_MEMVRLPBLE_Pos (6UL) /*!< PWRCTRL MISC: MEMVRLPBLE (Bit 6) */ -#define PWRCTRL_MISC_MEMVRLPBLE_Msk (0x40UL) /*!< PWRCTRL MISC: MEMVRLPBLE (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MISC_FORCEMEMVRADC_Pos (4UL) /*!< PWRCTRL MISC: FORCEMEMVRADC (Bit 4) */ -#define PWRCTRL_MISC_FORCEMEMVRADC_Msk (0x30UL) /*!< PWRCTRL MISC: FORCEMEMVRADC (Bitfield-Mask: 0x03) */ -#define PWRCTRL_MISC_FORCEMEMVRLPTIMERS_Pos (3UL) /*!< PWRCTRL MISC: FORCEMEMVRLPTIMERS (Bit 3) */ -#define PWRCTRL_MISC_FORCEMEMVRLPTIMERS_Msk (0x8UL) /*!< PWRCTRL MISC: FORCEMEMVRLPTIMERS (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MISC_FORCECOREVRLPTIMERS_Pos (2UL) /*!< PWRCTRL MISC: FORCECOREVRLPTIMERS (Bit 2) */ -#define PWRCTRL_MISC_FORCECOREVRLPTIMERS_Msk (0x4UL) /*!< PWRCTRL MISC: FORCECOREVRLPTIMERS (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MISC_FORCECOREVRLPPDM_Pos (1UL) /*!< PWRCTRL MISC: FORCECOREVRLPPDM (Bit 1) */ -#define PWRCTRL_MISC_FORCECOREVRLPPDM_Msk (0x2UL) /*!< PWRCTRL MISC: FORCECOREVRLPPDM (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MISC_SIMOBUCKEN_Pos (0UL) /*!< PWRCTRL MISC: SIMOBUCKEN (Bit 0) */ -#define PWRCTRL_MISC_SIMOBUCKEN_Msk (0x1UL) /*!< PWRCTRL MISC: SIMOBUCKEN (Bitfield-Mask: 0x01) */ -/* ===================================================== DEVPWREVENTEN ===================================================== */ -#define PWRCTRL_DEVPWREVENTEN_BURSTEVEN_Pos (31UL) /*!< PWRCTRL DEVPWREVENTEN: BURSTEVEN (Bit 31) */ -#define PWRCTRL_DEVPWREVENTEN_BURSTEVEN_Msk (0x80000000UL) /*!< PWRCTRL DEVPWREVENTEN: BURSTEVEN (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWREVENTEN_BURSTFEATUREEVEN_Pos (30UL) /*!< PWRCTRL DEVPWREVENTEN: BURSTFEATUREEVEN (Bit 30) */ -#define PWRCTRL_DEVPWREVENTEN_BURSTFEATUREEVEN_Msk (0x40000000UL) /*!< PWRCTRL DEVPWREVENTEN: BURSTFEATUREEVEN (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWREVENTEN_BLEFEATUREEVEN_Pos (29UL) /*!< PWRCTRL DEVPWREVENTEN: BLEFEATUREEVEN (Bit 29) */ -#define PWRCTRL_DEVPWREVENTEN_BLEFEATUREEVEN_Msk (0x20000000UL) /*!< PWRCTRL DEVPWREVENTEN: BLEFEATUREEVEN (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWREVENTEN_BLELEVEN_Pos (8UL) /*!< PWRCTRL DEVPWREVENTEN: BLELEVEN (Bit 8) */ -#define PWRCTRL_DEVPWREVENTEN_BLELEVEN_Msk (0x100UL) /*!< PWRCTRL DEVPWREVENTEN: BLELEVEN (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWREVENTEN_PDMEVEN_Pos (7UL) /*!< PWRCTRL DEVPWREVENTEN: PDMEVEN (Bit 7) */ -#define PWRCTRL_DEVPWREVENTEN_PDMEVEN_Msk (0x80UL) /*!< PWRCTRL DEVPWREVENTEN: PDMEVEN (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWREVENTEN_MSPIEVEN_Pos (6UL) /*!< PWRCTRL DEVPWREVENTEN: MSPIEVEN (Bit 6) */ -#define PWRCTRL_DEVPWREVENTEN_MSPIEVEN_Msk (0x40UL) /*!< PWRCTRL DEVPWREVENTEN: MSPIEVEN (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWREVENTEN_ADCEVEN_Pos (5UL) /*!< PWRCTRL DEVPWREVENTEN: ADCEVEN (Bit 5) */ -#define PWRCTRL_DEVPWREVENTEN_ADCEVEN_Msk (0x20UL) /*!< PWRCTRL DEVPWREVENTEN: ADCEVEN (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWREVENTEN_HCPCEVEN_Pos (4UL) /*!< PWRCTRL DEVPWREVENTEN: HCPCEVEN (Bit 4) */ -#define PWRCTRL_DEVPWREVENTEN_HCPCEVEN_Msk (0x10UL) /*!< PWRCTRL DEVPWREVENTEN: HCPCEVEN (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWREVENTEN_HCPBEVEN_Pos (3UL) /*!< PWRCTRL DEVPWREVENTEN: HCPBEVEN (Bit 3) */ -#define PWRCTRL_DEVPWREVENTEN_HCPBEVEN_Msk (0x8UL) /*!< PWRCTRL DEVPWREVENTEN: HCPBEVEN (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWREVENTEN_HCPAEVEN_Pos (2UL) /*!< PWRCTRL DEVPWREVENTEN: HCPAEVEN (Bit 2) */ -#define PWRCTRL_DEVPWREVENTEN_HCPAEVEN_Msk (0x4UL) /*!< PWRCTRL DEVPWREVENTEN: HCPAEVEN (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWREVENTEN_MCUHEVEN_Pos (1UL) /*!< PWRCTRL DEVPWREVENTEN: MCUHEVEN (Bit 1) */ -#define PWRCTRL_DEVPWREVENTEN_MCUHEVEN_Msk (0x2UL) /*!< PWRCTRL DEVPWREVENTEN: MCUHEVEN (Bitfield-Mask: 0x01) */ -#define PWRCTRL_DEVPWREVENTEN_MCULEVEN_Pos (0UL) /*!< PWRCTRL DEVPWREVENTEN: MCULEVEN (Bit 0) */ -#define PWRCTRL_DEVPWREVENTEN_MCULEVEN_Msk (0x1UL) /*!< PWRCTRL DEVPWREVENTEN: MCULEVEN (Bitfield-Mask: 0x01) */ -/* ===================================================== MEMPWREVENTEN ===================================================== */ -#define PWRCTRL_MEMPWREVENTEN_CACHEB2EN_Pos (31UL) /*!< PWRCTRL MEMPWREVENTEN: CACHEB2EN (Bit 31) */ -#define PWRCTRL_MEMPWREVENTEN_CACHEB2EN_Msk (0x80000000UL) /*!< PWRCTRL MEMPWREVENTEN: CACHEB2EN (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MEMPWREVENTEN_CACHEB0EN_Pos (30UL) /*!< PWRCTRL MEMPWREVENTEN: CACHEB0EN (Bit 30) */ -#define PWRCTRL_MEMPWREVENTEN_CACHEB0EN_Msk (0x40000000UL) /*!< PWRCTRL MEMPWREVENTEN: CACHEB0EN (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MEMPWREVENTEN_FLASH1EN_Pos (14UL) /*!< PWRCTRL MEMPWREVENTEN: FLASH1EN (Bit 14) */ -#define PWRCTRL_MEMPWREVENTEN_FLASH1EN_Msk (0x4000UL) /*!< PWRCTRL MEMPWREVENTEN: FLASH1EN (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MEMPWREVENTEN_FLASH0EN_Pos (13UL) /*!< PWRCTRL MEMPWREVENTEN: FLASH0EN (Bit 13) */ -#define PWRCTRL_MEMPWREVENTEN_FLASH0EN_Msk (0x2000UL) /*!< PWRCTRL MEMPWREVENTEN: FLASH0EN (Bitfield-Mask: 0x01) */ -#define PWRCTRL_MEMPWREVENTEN_SRAMEN_Pos (3UL) /*!< PWRCTRL MEMPWREVENTEN: SRAMEN (Bit 3) */ -#define PWRCTRL_MEMPWREVENTEN_SRAMEN_Msk (0x1ff8UL) /*!< PWRCTRL MEMPWREVENTEN: SRAMEN (Bitfield-Mask: 0x3ff) */ -#define PWRCTRL_MEMPWREVENTEN_DTCMEN_Pos (0UL) /*!< PWRCTRL MEMPWREVENTEN: DTCMEN (Bit 0) */ -#define PWRCTRL_MEMPWREVENTEN_DTCMEN_Msk (0x7UL) /*!< PWRCTRL MEMPWREVENTEN: DTCMEN (Bitfield-Mask: 0x07) */ - - -/* =========================================================================================================================== */ -/* ================ RSTGEN ================ */ -/* =========================================================================================================================== */ - -/* ========================================================== CFG ========================================================== */ -#define RSTGEN_CFG_WDREN_Pos (1UL) /*!< RSTGEN CFG: WDREN (Bit 1) */ -#define RSTGEN_CFG_WDREN_Msk (0x2UL) /*!< RSTGEN CFG: WDREN (Bitfield-Mask: 0x01) */ -#define RSTGEN_CFG_BODHREN_Pos (0UL) /*!< RSTGEN CFG: BODHREN (Bit 0) */ -#define RSTGEN_CFG_BODHREN_Msk (0x1UL) /*!< RSTGEN CFG: BODHREN (Bitfield-Mask: 0x01) */ -/* ========================================================= SWPOI ========================================================= */ -#define RSTGEN_SWPOI_SWPOIKEY_Pos (0UL) /*!< RSTGEN SWPOI: SWPOIKEY (Bit 0) */ -#define RSTGEN_SWPOI_SWPOIKEY_Msk (0xffUL) /*!< RSTGEN SWPOI: SWPOIKEY (Bitfield-Mask: 0xff) */ -/* ========================================================= SWPOR ========================================================= */ -#define RSTGEN_SWPOR_SWPORKEY_Pos (0UL) /*!< RSTGEN SWPOR: SWPORKEY (Bit 0) */ -#define RSTGEN_SWPOR_SWPORKEY_Msk (0xffUL) /*!< RSTGEN SWPOR: SWPORKEY (Bitfield-Mask: 0xff) */ -/* ======================================================== TPIURST ======================================================== */ -#define RSTGEN_TPIURST_TPIURST_Pos (0UL) /*!< RSTGEN TPIURST: TPIURST (Bit 0) */ -#define RSTGEN_TPIURST_TPIURST_Msk (0x1UL) /*!< RSTGEN TPIURST: TPIURST (Bitfield-Mask: 0x01) */ -/* ========================================================= INTEN ========================================================= */ -#define RSTGEN_INTEN_BODH_Pos (0UL) /*!< RSTGEN INTEN: BODH (Bit 0) */ -#define RSTGEN_INTEN_BODH_Msk (0x1UL) /*!< RSTGEN INTEN: BODH (Bitfield-Mask: 0x01) */ -/* ======================================================== INTSTAT ======================================================== */ -#define RSTGEN_INTSTAT_BODH_Pos (0UL) /*!< RSTGEN INTSTAT: BODH (Bit 0) */ -#define RSTGEN_INTSTAT_BODH_Msk (0x1UL) /*!< RSTGEN INTSTAT: BODH (Bitfield-Mask: 0x01) */ -/* ======================================================== INTCLR ========================================================= */ -#define RSTGEN_INTCLR_BODH_Pos (0UL) /*!< RSTGEN INTCLR: BODH (Bit 0) */ -#define RSTGEN_INTCLR_BODH_Msk (0x1UL) /*!< RSTGEN INTCLR: BODH (Bitfield-Mask: 0x01) */ -/* ======================================================== INTSET ========================================================= */ -#define RSTGEN_INTSET_BODH_Pos (0UL) /*!< RSTGEN INTSET: BODH (Bit 0) */ -#define RSTGEN_INTSET_BODH_Msk (0x1UL) /*!< RSTGEN INTSET: BODH (Bitfield-Mask: 0x01) */ -/* ========================================================= STAT ========================================================== */ -#define RSTGEN_STAT_SBOOT_Pos (31UL) /*!< RSTGEN STAT: SBOOT (Bit 31) */ -#define RSTGEN_STAT_SBOOT_Msk (0x80000000UL) /*!< RSTGEN STAT: SBOOT (Bitfield-Mask: 0x01) */ -#define RSTGEN_STAT_FBOOT_Pos (30UL) /*!< RSTGEN STAT: FBOOT (Bit 30) */ -#define RSTGEN_STAT_FBOOT_Msk (0x40000000UL) /*!< RSTGEN STAT: FBOOT (Bitfield-Mask: 0x01) */ -#define RSTGEN_STAT_BOBSTAT_Pos (10UL) /*!< RSTGEN STAT: BOBSTAT (Bit 10) */ -#define RSTGEN_STAT_BOBSTAT_Msk (0x400UL) /*!< RSTGEN STAT: BOBSTAT (Bitfield-Mask: 0x01) */ -#define RSTGEN_STAT_BOFSTAT_Pos (9UL) /*!< RSTGEN STAT: BOFSTAT (Bit 9) */ -#define RSTGEN_STAT_BOFSTAT_Msk (0x200UL) /*!< RSTGEN STAT: BOFSTAT (Bitfield-Mask: 0x01) */ -#define RSTGEN_STAT_BOCSTAT_Pos (8UL) /*!< RSTGEN STAT: BOCSTAT (Bit 8) */ -#define RSTGEN_STAT_BOCSTAT_Msk (0x100UL) /*!< RSTGEN STAT: BOCSTAT (Bitfield-Mask: 0x01) */ -#define RSTGEN_STAT_BOUSTAT_Pos (7UL) /*!< RSTGEN STAT: BOUSTAT (Bit 7) */ -#define RSTGEN_STAT_BOUSTAT_Msk (0x80UL) /*!< RSTGEN STAT: BOUSTAT (Bitfield-Mask: 0x01) */ -#define RSTGEN_STAT_WDRSTAT_Pos (6UL) /*!< RSTGEN STAT: WDRSTAT (Bit 6) */ -#define RSTGEN_STAT_WDRSTAT_Msk (0x40UL) /*!< RSTGEN STAT: WDRSTAT (Bitfield-Mask: 0x01) */ -#define RSTGEN_STAT_DBGRSTAT_Pos (5UL) /*!< RSTGEN STAT: DBGRSTAT (Bit 5) */ -#define RSTGEN_STAT_DBGRSTAT_Msk (0x20UL) /*!< RSTGEN STAT: DBGRSTAT (Bitfield-Mask: 0x01) */ -#define RSTGEN_STAT_POIRSTAT_Pos (4UL) /*!< RSTGEN STAT: POIRSTAT (Bit 4) */ -#define RSTGEN_STAT_POIRSTAT_Msk (0x10UL) /*!< RSTGEN STAT: POIRSTAT (Bitfield-Mask: 0x01) */ -#define RSTGEN_STAT_SWRSTAT_Pos (3UL) /*!< RSTGEN STAT: SWRSTAT (Bit 3) */ -#define RSTGEN_STAT_SWRSTAT_Msk (0x8UL) /*!< RSTGEN STAT: SWRSTAT (Bitfield-Mask: 0x01) */ -#define RSTGEN_STAT_BORSTAT_Pos (2UL) /*!< RSTGEN STAT: BORSTAT (Bit 2) */ -#define RSTGEN_STAT_BORSTAT_Msk (0x4UL) /*!< RSTGEN STAT: BORSTAT (Bitfield-Mask: 0x01) */ -#define RSTGEN_STAT_PORSTAT_Pos (1UL) /*!< RSTGEN STAT: PORSTAT (Bit 1) */ -#define RSTGEN_STAT_PORSTAT_Msk (0x2UL) /*!< RSTGEN STAT: PORSTAT (Bitfield-Mask: 0x01) */ -#define RSTGEN_STAT_EXRSTAT_Pos (0UL) /*!< RSTGEN STAT: EXRSTAT (Bit 0) */ -#define RSTGEN_STAT_EXRSTAT_Msk (0x1UL) /*!< RSTGEN STAT: EXRSTAT (Bitfield-Mask: 0x01) */ - - -/* =========================================================================================================================== */ -/* ================ RTC ================ */ -/* =========================================================================================================================== */ - -/* ======================================================== CTRLOW ========================================================= */ -#define RTC_CTRLOW_CTRHR_Pos (24UL) /*!< RTC CTRLOW: CTRHR (Bit 24) */ -#define RTC_CTRLOW_CTRHR_Msk (0x3f000000UL) /*!< RTC CTRLOW: CTRHR (Bitfield-Mask: 0x3f) */ -#define RTC_CTRLOW_CTRMIN_Pos (16UL) /*!< RTC CTRLOW: CTRMIN (Bit 16) */ -#define RTC_CTRLOW_CTRMIN_Msk (0x7f0000UL) /*!< RTC CTRLOW: CTRMIN (Bitfield-Mask: 0x7f) */ -#define RTC_CTRLOW_CTRSEC_Pos (8UL) /*!< RTC CTRLOW: CTRSEC (Bit 8) */ -#define RTC_CTRLOW_CTRSEC_Msk (0x7f00UL) /*!< RTC CTRLOW: CTRSEC (Bitfield-Mask: 0x7f) */ -#define RTC_CTRLOW_CTR100_Pos (0UL) /*!< RTC CTRLOW: CTR100 (Bit 0) */ -#define RTC_CTRLOW_CTR100_Msk (0xffUL) /*!< RTC CTRLOW: CTR100 (Bitfield-Mask: 0xff) */ -/* ========================================================= CTRUP ========================================================= */ -#define RTC_CTRUP_CTERR_Pos (31UL) /*!< RTC CTRUP: CTERR (Bit 31) */ -#define RTC_CTRUP_CTERR_Msk (0x80000000UL) /*!< RTC CTRUP: CTERR (Bitfield-Mask: 0x01) */ -#define RTC_CTRUP_CEB_Pos (28UL) /*!< RTC CTRUP: CEB (Bit 28) */ -#define RTC_CTRUP_CEB_Msk (0x10000000UL) /*!< RTC CTRUP: CEB (Bitfield-Mask: 0x01) */ -#define RTC_CTRUP_CB_Pos (27UL) /*!< RTC CTRUP: CB (Bit 27) */ -#define RTC_CTRUP_CB_Msk (0x8000000UL) /*!< RTC CTRUP: CB (Bitfield-Mask: 0x01) */ -#define RTC_CTRUP_CTRWKDY_Pos (24UL) /*!< RTC CTRUP: CTRWKDY (Bit 24) */ -#define RTC_CTRUP_CTRWKDY_Msk (0x7000000UL) /*!< RTC CTRUP: CTRWKDY (Bitfield-Mask: 0x07) */ -#define RTC_CTRUP_CTRYR_Pos (16UL) /*!< RTC CTRUP: CTRYR (Bit 16) */ -#define RTC_CTRUP_CTRYR_Msk (0xff0000UL) /*!< RTC CTRUP: CTRYR (Bitfield-Mask: 0xff) */ -#define RTC_CTRUP_CTRMO_Pos (8UL) /*!< RTC CTRUP: CTRMO (Bit 8) */ -#define RTC_CTRUP_CTRMO_Msk (0x1f00UL) /*!< RTC CTRUP: CTRMO (Bitfield-Mask: 0x1f) */ -#define RTC_CTRUP_CTRDATE_Pos (0UL) /*!< RTC CTRUP: CTRDATE (Bit 0) */ -#define RTC_CTRUP_CTRDATE_Msk (0x3fUL) /*!< RTC CTRUP: CTRDATE (Bitfield-Mask: 0x3f) */ -/* ======================================================== ALMLOW ========================================================= */ -#define RTC_ALMLOW_ALMHR_Pos (24UL) /*!< RTC ALMLOW: ALMHR (Bit 24) */ -#define RTC_ALMLOW_ALMHR_Msk (0x3f000000UL) /*!< RTC ALMLOW: ALMHR (Bitfield-Mask: 0x3f) */ -#define RTC_ALMLOW_ALMMIN_Pos (16UL) /*!< RTC ALMLOW: ALMMIN (Bit 16) */ -#define RTC_ALMLOW_ALMMIN_Msk (0x7f0000UL) /*!< RTC ALMLOW: ALMMIN (Bitfield-Mask: 0x7f) */ -#define RTC_ALMLOW_ALMSEC_Pos (8UL) /*!< RTC ALMLOW: ALMSEC (Bit 8) */ -#define RTC_ALMLOW_ALMSEC_Msk (0x7f00UL) /*!< RTC ALMLOW: ALMSEC (Bitfield-Mask: 0x7f) */ -#define RTC_ALMLOW_ALM100_Pos (0UL) /*!< RTC ALMLOW: ALM100 (Bit 0) */ -#define RTC_ALMLOW_ALM100_Msk (0xffUL) /*!< RTC ALMLOW: ALM100 (Bitfield-Mask: 0xff) */ -/* ========================================================= ALMUP ========================================================= */ -#define RTC_ALMUP_ALMWKDY_Pos (16UL) /*!< RTC ALMUP: ALMWKDY (Bit 16) */ -#define RTC_ALMUP_ALMWKDY_Msk (0x70000UL) /*!< RTC ALMUP: ALMWKDY (Bitfield-Mask: 0x07) */ -#define RTC_ALMUP_ALMMO_Pos (8UL) /*!< RTC ALMUP: ALMMO (Bit 8) */ -#define RTC_ALMUP_ALMMO_Msk (0x1f00UL) /*!< RTC ALMUP: ALMMO (Bitfield-Mask: 0x1f) */ -#define RTC_ALMUP_ALMDATE_Pos (0UL) /*!< RTC ALMUP: ALMDATE (Bit 0) */ -#define RTC_ALMUP_ALMDATE_Msk (0x3fUL) /*!< RTC ALMUP: ALMDATE (Bitfield-Mask: 0x3f) */ -/* ======================================================== RTCCTL ========================================================= */ -#define RTC_RTCCTL_HR1224_Pos (5UL) /*!< RTC RTCCTL: HR1224 (Bit 5) */ -#define RTC_RTCCTL_HR1224_Msk (0x20UL) /*!< RTC RTCCTL: HR1224 (Bitfield-Mask: 0x01) */ -#define RTC_RTCCTL_RSTOP_Pos (4UL) /*!< RTC RTCCTL: RSTOP (Bit 4) */ -#define RTC_RTCCTL_RSTOP_Msk (0x10UL) /*!< RTC RTCCTL: RSTOP (Bitfield-Mask: 0x01) */ -#define RTC_RTCCTL_RPT_Pos (1UL) /*!< RTC RTCCTL: RPT (Bit 1) */ -#define RTC_RTCCTL_RPT_Msk (0xeUL) /*!< RTC RTCCTL: RPT (Bitfield-Mask: 0x07) */ -#define RTC_RTCCTL_WRTC_Pos (0UL) /*!< RTC RTCCTL: WRTC (Bit 0) */ -#define RTC_RTCCTL_WRTC_Msk (0x1UL) /*!< RTC RTCCTL: WRTC (Bitfield-Mask: 0x01) */ -/* ========================================================= INTEN ========================================================= */ -#define RTC_INTEN_ALM_Pos (0UL) /*!< RTC INTEN: ALM (Bit 0) */ -#define RTC_INTEN_ALM_Msk (0x1UL) /*!< RTC INTEN: ALM (Bitfield-Mask: 0x01) */ -/* ======================================================== INTSTAT ======================================================== */ -#define RTC_INTSTAT_ALM_Pos (0UL) /*!< RTC INTSTAT: ALM (Bit 0) */ -#define RTC_INTSTAT_ALM_Msk (0x1UL) /*!< RTC INTSTAT: ALM (Bitfield-Mask: 0x01) */ -/* ======================================================== INTCLR ========================================================= */ -#define RTC_INTCLR_ALM_Pos (0UL) /*!< RTC INTCLR: ALM (Bit 0) */ -#define RTC_INTCLR_ALM_Msk (0x1UL) /*!< RTC INTCLR: ALM (Bitfield-Mask: 0x01) */ -/* ======================================================== INTSET ========================================================= */ -#define RTC_INTSET_ALM_Pos (0UL) /*!< RTC INTSET: ALM (Bit 0) */ -#define RTC_INTSET_ALM_Msk (0x1UL) /*!< RTC INTSET: ALM (Bitfield-Mask: 0x01) */ - - -/* =========================================================================================================================== */ -/* ================ SCARD ================ */ -/* =========================================================================================================================== */ - -/* ========================================================== SR =========================================================== */ -#define SCARD_SR_FHF_Pos (6UL) /*!< SCARD SR: FHF (Bit 6) */ -#define SCARD_SR_FHF_Msk (0x40UL) /*!< SCARD SR: FHF (Bitfield-Mask: 0x01) */ -#define SCARD_SR_FT2REND_Pos (5UL) /*!< SCARD SR: FT2REND (Bit 5) */ -#define SCARD_SR_FT2REND_Msk (0x20UL) /*!< SCARD SR: FT2REND (Bitfield-Mask: 0x01) */ -#define SCARD_SR_PE_Pos (4UL) /*!< SCARD SR: PE (Bit 4) */ -#define SCARD_SR_PE_Msk (0x10UL) /*!< SCARD SR: PE (Bitfield-Mask: 0x01) */ -#define SCARD_SR_OVR_Pos (3UL) /*!< SCARD SR: OVR (Bit 3) */ -#define SCARD_SR_OVR_Msk (0x8UL) /*!< SCARD SR: OVR (Bitfield-Mask: 0x01) */ -#define SCARD_SR_FER_Pos (2UL) /*!< SCARD SR: FER (Bit 2) */ -#define SCARD_SR_FER_Msk (0x4UL) /*!< SCARD SR: FER (Bitfield-Mask: 0x01) */ -#define SCARD_SR_TBERBF_Pos (1UL) /*!< SCARD SR: TBERBF (Bit 1) */ -#define SCARD_SR_TBERBF_Msk (0x2UL) /*!< SCARD SR: TBERBF (Bitfield-Mask: 0x01) */ -#define SCARD_SR_FNE_Pos (0UL) /*!< SCARD SR: FNE (Bit 0) */ -#define SCARD_SR_FNE_Msk (0x1UL) /*!< SCARD SR: FNE (Bitfield-Mask: 0x01) */ -/* ========================================================== DR =========================================================== */ -#define SCARD_DR_DR_Pos (0UL) /*!< SCARD DR: DR (Bit 0) */ -#define SCARD_DR_DR_Msk (0xffUL) /*!< SCARD DR: DR (Bitfield-Mask: 0xff) */ -/* ========================================================== SR1 ========================================================== */ -#define SCARD_SR1_IDLE_Pos (3UL) /*!< SCARD SR1: IDLE (Bit 3) */ -#define SCARD_SR1_IDLE_Msk (0x8UL) /*!< SCARD SR1: IDLE (Bitfield-Mask: 0x01) */ -#define SCARD_SR1_SYNCEND_Pos (2UL) /*!< SCARD SR1: SYNCEND (Bit 2) */ -#define SCARD_SR1_SYNCEND_Msk (0x4UL) /*!< SCARD SR1: SYNCEND (Bitfield-Mask: 0x01) */ -#define SCARD_SR1_PRL_Pos (1UL) /*!< SCARD SR1: PRL (Bit 1) */ -#define SCARD_SR1_PRL_Msk (0x2UL) /*!< SCARD SR1: PRL (Bitfield-Mask: 0x01) */ -#define SCARD_SR1_ECNTOVER_Pos (0UL) /*!< SCARD SR1: ECNTOVER (Bit 0) */ -#define SCARD_SR1_ECNTOVER_Msk (0x1UL) /*!< SCARD SR1: ECNTOVER (Bitfield-Mask: 0x01) */ -/* ====================================================== RETXCNTRMI ======================================================= */ -#define SCARD_RETXCNTRMI_RETXCNTRMI_Pos (0UL) /*!< SCARD RETXCNTRMI: RETXCNTRMI (Bit 0) */ -#define SCARD_RETXCNTRMI_RETXCNTRMI_Msk (0xfUL) /*!< SCARD RETXCNTRMI: RETXCNTRMI (Bitfield-Mask: 0x0f) */ -/* ======================================================== CLKCTRL ======================================================== */ -#define SCARD_CLKCTRL_APBCLKEN_Pos (1UL) /*!< SCARD CLKCTRL: APBCLKEN (Bit 1) */ -#define SCARD_CLKCTRL_APBCLKEN_Msk (0x2UL) /*!< SCARD CLKCTRL: APBCLKEN (Bitfield-Mask: 0x01) */ -#define SCARD_CLKCTRL_CLKEN_Pos (0UL) /*!< SCARD CLKCTRL: CLKEN (Bit 0) */ -#define SCARD_CLKCTRL_CLKEN_Msk (0x1UL) /*!< SCARD CLKCTRL: CLKEN (Bitfield-Mask: 0x01) */ - - -/* =========================================================================================================================== */ -/* ================ SECURITY ================ */ -/* =========================================================================================================================== */ - -/* ========================================================= CTRL ========================================================== */ -#define SECURITY_CTRL_CRCERROR_Pos (31UL) /*!< SECURITY CTRL: CRCERROR (Bit 31) */ -#define SECURITY_CTRL_CRCERROR_Msk (0x80000000UL) /*!< SECURITY CTRL: CRCERROR (Bitfield-Mask: 0x01) */ -#define SECURITY_CTRL_FUNCTION_Pos (4UL) /*!< SECURITY CTRL: FUNCTION (Bit 4) */ -#define SECURITY_CTRL_FUNCTION_Msk (0xf0UL) /*!< SECURITY CTRL: FUNCTION (Bitfield-Mask: 0x0f) */ -#define SECURITY_CTRL_ENABLE_Pos (0UL) /*!< SECURITY CTRL: ENABLE (Bit 0) */ -#define SECURITY_CTRL_ENABLE_Msk (0x1UL) /*!< SECURITY CTRL: ENABLE (Bitfield-Mask: 0x01) */ -/* ======================================================== SRCADDR ======================================================== */ -#define SECURITY_SRCADDR_ADDR_Pos (0UL) /*!< SECURITY SRCADDR: ADDR (Bit 0) */ -#define SECURITY_SRCADDR_ADDR_Msk (0xffffffffUL) /*!< SECURITY SRCADDR: ADDR (Bitfield-Mask: 0xffffffff) */ -/* ========================================================== LEN ========================================================== */ -#define SECURITY_LEN_LEN_Pos (2UL) /*!< SECURITY LEN: LEN (Bit 2) */ -#define SECURITY_LEN_LEN_Msk (0xffffcUL) /*!< SECURITY LEN: LEN (Bitfield-Mask: 0x3ffff) */ -/* ======================================================== RESULT ========================================================= */ -#define SECURITY_RESULT_CRC_Pos (0UL) /*!< SECURITY RESULT: CRC (Bit 0) */ -#define SECURITY_RESULT_CRC_Msk (0xffffffffUL) /*!< SECURITY RESULT: CRC (Bitfield-Mask: 0xffffffff) */ -/* ======================================================= LOCKCTRL ======================================================== */ -#define SECURITY_LOCKCTRL_SELECT_Pos (0UL) /*!< SECURITY LOCKCTRL: SELECT (Bit 0) */ -#define SECURITY_LOCKCTRL_SELECT_Msk (0xffUL) /*!< SECURITY LOCKCTRL: SELECT (Bitfield-Mask: 0xff) */ -/* ======================================================= LOCKSTAT ======================================================== */ -#define SECURITY_LOCKSTAT_STATUS_Pos (0UL) /*!< SECURITY LOCKSTAT: STATUS (Bit 0) */ -#define SECURITY_LOCKSTAT_STATUS_Msk (0xffffffffUL) /*!< SECURITY LOCKSTAT: STATUS (Bitfield-Mask: 0xffffffff) */ -/* ========================================================= KEY0 ========================================================== */ -#define SECURITY_KEY0_KEY0_Pos (0UL) /*!< SECURITY KEY0: KEY0 (Bit 0) */ -#define SECURITY_KEY0_KEY0_Msk (0xffffffffUL) /*!< SECURITY KEY0: KEY0 (Bitfield-Mask: 0xffffffff) */ -/* ========================================================= KEY1 ========================================================== */ -#define SECURITY_KEY1_KEY1_Pos (0UL) /*!< SECURITY KEY1: KEY1 (Bit 0) */ -#define SECURITY_KEY1_KEY1_Msk (0xffffffffUL) /*!< SECURITY KEY1: KEY1 (Bitfield-Mask: 0xffffffff) */ -/* ========================================================= KEY2 ========================================================== */ -#define SECURITY_KEY2_KEY2_Pos (0UL) /*!< SECURITY KEY2: KEY2 (Bit 0) */ -#define SECURITY_KEY2_KEY2_Msk (0xffffffffUL) /*!< SECURITY KEY2: KEY2 (Bitfield-Mask: 0xffffffff) */ -/* ========================================================= KEY3 ========================================================== */ -#define SECURITY_KEY3_KEY3_Pos (0UL) /*!< SECURITY KEY3: KEY3 (Bit 0) */ -#define SECURITY_KEY3_KEY3_Msk (0xffffffffUL) /*!< SECURITY KEY3: KEY3 (Bitfield-Mask: 0xffffffff) */ - - -/* =========================================================================================================================== */ -/* ================ UART0 ================ */ -/* =========================================================================================================================== */ - -/* ========================================================== DR =========================================================== */ -#define UART0_DR_OEDATA_Pos (11UL) /*!< UART0 DR: OEDATA (Bit 11) */ -#define UART0_DR_OEDATA_Msk (0x800UL) /*!< UART0 DR: OEDATA (Bitfield-Mask: 0x01) */ -#define UART0_DR_BEDATA_Pos (10UL) /*!< UART0 DR: BEDATA (Bit 10) */ -#define UART0_DR_BEDATA_Msk (0x400UL) /*!< UART0 DR: BEDATA (Bitfield-Mask: 0x01) */ -#define UART0_DR_PEDATA_Pos (9UL) /*!< UART0 DR: PEDATA (Bit 9) */ -#define UART0_DR_PEDATA_Msk (0x200UL) /*!< UART0 DR: PEDATA (Bitfield-Mask: 0x01) */ -#define UART0_DR_FEDATA_Pos (8UL) /*!< UART0 DR: FEDATA (Bit 8) */ -#define UART0_DR_FEDATA_Msk (0x100UL) /*!< UART0 DR: FEDATA (Bitfield-Mask: 0x01) */ -#define UART0_DR_DATA_Pos (0UL) /*!< UART0 DR: DATA (Bit 0) */ -#define UART0_DR_DATA_Msk (0xffUL) /*!< UART0 DR: DATA (Bitfield-Mask: 0xff) */ -/* ========================================================== RSR ========================================================== */ -#define UART0_RSR_OESTAT_Pos (3UL) /*!< UART0 RSR: OESTAT (Bit 3) */ -#define UART0_RSR_OESTAT_Msk (0x8UL) /*!< UART0 RSR: OESTAT (Bitfield-Mask: 0x01) */ -#define UART0_RSR_BESTAT_Pos (2UL) /*!< UART0 RSR: BESTAT (Bit 2) */ -#define UART0_RSR_BESTAT_Msk (0x4UL) /*!< UART0 RSR: BESTAT (Bitfield-Mask: 0x01) */ -#define UART0_RSR_PESTAT_Pos (1UL) /*!< UART0 RSR: PESTAT (Bit 1) */ -#define UART0_RSR_PESTAT_Msk (0x2UL) /*!< UART0 RSR: PESTAT (Bitfield-Mask: 0x01) */ -#define UART0_RSR_FESTAT_Pos (0UL) /*!< UART0 RSR: FESTAT (Bit 0) */ -#define UART0_RSR_FESTAT_Msk (0x1UL) /*!< UART0 RSR: FESTAT (Bitfield-Mask: 0x01) */ -/* ========================================================== FR =========================================================== */ -#define UART0_FR_TXBUSY_Pos (8UL) /*!< UART0 FR: TXBUSY (Bit 8) */ -#define UART0_FR_TXBUSY_Msk (0x100UL) /*!< UART0 FR: TXBUSY (Bitfield-Mask: 0x01) */ -#define UART0_FR_TXFE_Pos (7UL) /*!< UART0 FR: TXFE (Bit 7) */ -#define UART0_FR_TXFE_Msk (0x80UL) /*!< UART0 FR: TXFE (Bitfield-Mask: 0x01) */ -#define UART0_FR_RXFF_Pos (6UL) /*!< UART0 FR: RXFF (Bit 6) */ -#define UART0_FR_RXFF_Msk (0x40UL) /*!< UART0 FR: RXFF (Bitfield-Mask: 0x01) */ -#define UART0_FR_TXFF_Pos (5UL) /*!< UART0 FR: TXFF (Bit 5) */ -#define UART0_FR_TXFF_Msk (0x20UL) /*!< UART0 FR: TXFF (Bitfield-Mask: 0x01) */ -#define UART0_FR_RXFE_Pos (4UL) /*!< UART0 FR: RXFE (Bit 4) */ -#define UART0_FR_RXFE_Msk (0x10UL) /*!< UART0 FR: RXFE (Bitfield-Mask: 0x01) */ -#define UART0_FR_BUSY_Pos (3UL) /*!< UART0 FR: BUSY (Bit 3) */ -#define UART0_FR_BUSY_Msk (0x8UL) /*!< UART0 FR: BUSY (Bitfield-Mask: 0x01) */ -#define UART0_FR_DCD_Pos (2UL) /*!< UART0 FR: DCD (Bit 2) */ -#define UART0_FR_DCD_Msk (0x4UL) /*!< UART0 FR: DCD (Bitfield-Mask: 0x01) */ -#define UART0_FR_DSR_Pos (1UL) /*!< UART0 FR: DSR (Bit 1) */ -#define UART0_FR_DSR_Msk (0x2UL) /*!< UART0 FR: DSR (Bitfield-Mask: 0x01) */ -#define UART0_FR_CTS_Pos (0UL) /*!< UART0 FR: CTS (Bit 0) */ -#define UART0_FR_CTS_Msk (0x1UL) /*!< UART0 FR: CTS (Bitfield-Mask: 0x01) */ -/* ========================================================= ILPR ========================================================== */ -#define UART0_ILPR_ILPDVSR_Pos (0UL) /*!< UART0 ILPR: ILPDVSR (Bit 0) */ -#define UART0_ILPR_ILPDVSR_Msk (0xffUL) /*!< UART0 ILPR: ILPDVSR (Bitfield-Mask: 0xff) */ -/* ========================================================= IBRD ========================================================== */ -#define UART0_IBRD_DIVINT_Pos (0UL) /*!< UART0 IBRD: DIVINT (Bit 0) */ -#define UART0_IBRD_DIVINT_Msk (0xffffUL) /*!< UART0 IBRD: DIVINT (Bitfield-Mask: 0xffff) */ -/* ========================================================= FBRD ========================================================== */ -#define UART0_FBRD_DIVFRAC_Pos (0UL) /*!< UART0 FBRD: DIVFRAC (Bit 0) */ -#define UART0_FBRD_DIVFRAC_Msk (0x3fUL) /*!< UART0 FBRD: DIVFRAC (Bitfield-Mask: 0x3f) */ -/* ========================================================= LCRH ========================================================== */ -#define UART0_LCRH_SPS_Pos (7UL) /*!< UART0 LCRH: SPS (Bit 7) */ -#define UART0_LCRH_SPS_Msk (0x80UL) /*!< UART0 LCRH: SPS (Bitfield-Mask: 0x01) */ -#define UART0_LCRH_WLEN_Pos (5UL) /*!< UART0 LCRH: WLEN (Bit 5) */ -#define UART0_LCRH_WLEN_Msk (0x60UL) /*!< UART0 LCRH: WLEN (Bitfield-Mask: 0x03) */ -#define UART0_LCRH_FEN_Pos (4UL) /*!< UART0 LCRH: FEN (Bit 4) */ -#define UART0_LCRH_FEN_Msk (0x10UL) /*!< UART0 LCRH: FEN (Bitfield-Mask: 0x01) */ -#define UART0_LCRH_STP2_Pos (3UL) /*!< UART0 LCRH: STP2 (Bit 3) */ -#define UART0_LCRH_STP2_Msk (0x8UL) /*!< UART0 LCRH: STP2 (Bitfield-Mask: 0x01) */ -#define UART0_LCRH_EPS_Pos (2UL) /*!< UART0 LCRH: EPS (Bit 2) */ -#define UART0_LCRH_EPS_Msk (0x4UL) /*!< UART0 LCRH: EPS (Bitfield-Mask: 0x01) */ -#define UART0_LCRH_PEN_Pos (1UL) /*!< UART0 LCRH: PEN (Bit 1) */ -#define UART0_LCRH_PEN_Msk (0x2UL) /*!< UART0 LCRH: PEN (Bitfield-Mask: 0x01) */ -#define UART0_LCRH_BRK_Pos (0UL) /*!< UART0 LCRH: BRK (Bit 0) */ -#define UART0_LCRH_BRK_Msk (0x1UL) /*!< UART0 LCRH: BRK (Bitfield-Mask: 0x01) */ -/* ========================================================== CR =========================================================== */ -#define UART0_CR_CTSEN_Pos (15UL) /*!< UART0 CR: CTSEN (Bit 15) */ -#define UART0_CR_CTSEN_Msk (0x8000UL) /*!< UART0 CR: CTSEN (Bitfield-Mask: 0x01) */ -#define UART0_CR_RTSEN_Pos (14UL) /*!< UART0 CR: RTSEN (Bit 14) */ -#define UART0_CR_RTSEN_Msk (0x4000UL) /*!< UART0 CR: RTSEN (Bitfield-Mask: 0x01) */ -#define UART0_CR_OUT2_Pos (13UL) /*!< UART0 CR: OUT2 (Bit 13) */ -#define UART0_CR_OUT2_Msk (0x2000UL) /*!< UART0 CR: OUT2 (Bitfield-Mask: 0x01) */ -#define UART0_CR_OUT1_Pos (12UL) /*!< UART0 CR: OUT1 (Bit 12) */ -#define UART0_CR_OUT1_Msk (0x1000UL) /*!< UART0 CR: OUT1 (Bitfield-Mask: 0x01) */ -#define UART0_CR_RTS_Pos (11UL) /*!< UART0 CR: RTS (Bit 11) */ -#define UART0_CR_RTS_Msk (0x800UL) /*!< UART0 CR: RTS (Bitfield-Mask: 0x01) */ -#define UART0_CR_DTR_Pos (10UL) /*!< UART0 CR: DTR (Bit 10) */ -#define UART0_CR_DTR_Msk (0x400UL) /*!< UART0 CR: DTR (Bitfield-Mask: 0x01) */ -#define UART0_CR_RXE_Pos (9UL) /*!< UART0 CR: RXE (Bit 9) */ -#define UART0_CR_RXE_Msk (0x200UL) /*!< UART0 CR: RXE (Bitfield-Mask: 0x01) */ -#define UART0_CR_TXE_Pos (8UL) /*!< UART0 CR: TXE (Bit 8) */ -#define UART0_CR_TXE_Msk (0x100UL) /*!< UART0 CR: TXE (Bitfield-Mask: 0x01) */ -#define UART0_CR_LBE_Pos (7UL) /*!< UART0 CR: LBE (Bit 7) */ -#define UART0_CR_LBE_Msk (0x80UL) /*!< UART0 CR: LBE (Bitfield-Mask: 0x01) */ -#define UART0_CR_CLKSEL_Pos (4UL) /*!< UART0 CR: CLKSEL (Bit 4) */ -#define UART0_CR_CLKSEL_Msk (0x70UL) /*!< UART0 CR: CLKSEL (Bitfield-Mask: 0x07) */ -#define UART0_CR_CLKEN_Pos (3UL) /*!< UART0 CR: CLKEN (Bit 3) */ -#define UART0_CR_CLKEN_Msk (0x8UL) /*!< UART0 CR: CLKEN (Bitfield-Mask: 0x01) */ -#define UART0_CR_SIRLP_Pos (2UL) /*!< UART0 CR: SIRLP (Bit 2) */ -#define UART0_CR_SIRLP_Msk (0x4UL) /*!< UART0 CR: SIRLP (Bitfield-Mask: 0x01) */ -#define UART0_CR_SIREN_Pos (1UL) /*!< UART0 CR: SIREN (Bit 1) */ -#define UART0_CR_SIREN_Msk (0x2UL) /*!< UART0 CR: SIREN (Bitfield-Mask: 0x01) */ -#define UART0_CR_UARTEN_Pos (0UL) /*!< UART0 CR: UARTEN (Bit 0) */ -#define UART0_CR_UARTEN_Msk (0x1UL) /*!< UART0 CR: UARTEN (Bitfield-Mask: 0x01) */ -/* ========================================================= IFLS ========================================================== */ -#define UART0_IFLS_RXIFLSEL_Pos (3UL) /*!< UART0 IFLS: RXIFLSEL (Bit 3) */ -#define UART0_IFLS_RXIFLSEL_Msk (0x38UL) /*!< UART0 IFLS: RXIFLSEL (Bitfield-Mask: 0x07) */ -#define UART0_IFLS_TXIFLSEL_Pos (0UL) /*!< UART0 IFLS: TXIFLSEL (Bit 0) */ -#define UART0_IFLS_TXIFLSEL_Msk (0x7UL) /*!< UART0 IFLS: TXIFLSEL (Bitfield-Mask: 0x07) */ -/* ========================================================== IER ========================================================== */ -#define UART0_IER_OEIM_Pos (10UL) /*!< UART0 IER: OEIM (Bit 10) */ -#define UART0_IER_OEIM_Msk (0x400UL) /*!< UART0 IER: OEIM (Bitfield-Mask: 0x01) */ -#define UART0_IER_BEIM_Pos (9UL) /*!< UART0 IER: BEIM (Bit 9) */ -#define UART0_IER_BEIM_Msk (0x200UL) /*!< UART0 IER: BEIM (Bitfield-Mask: 0x01) */ -#define UART0_IER_PEIM_Pos (8UL) /*!< UART0 IER: PEIM (Bit 8) */ -#define UART0_IER_PEIM_Msk (0x100UL) /*!< UART0 IER: PEIM (Bitfield-Mask: 0x01) */ -#define UART0_IER_FEIM_Pos (7UL) /*!< UART0 IER: FEIM (Bit 7) */ -#define UART0_IER_FEIM_Msk (0x80UL) /*!< UART0 IER: FEIM (Bitfield-Mask: 0x01) */ -#define UART0_IER_RTIM_Pos (6UL) /*!< UART0 IER: RTIM (Bit 6) */ -#define UART0_IER_RTIM_Msk (0x40UL) /*!< UART0 IER: RTIM (Bitfield-Mask: 0x01) */ -#define UART0_IER_TXIM_Pos (5UL) /*!< UART0 IER: TXIM (Bit 5) */ -#define UART0_IER_TXIM_Msk (0x20UL) /*!< UART0 IER: TXIM (Bitfield-Mask: 0x01) */ -#define UART0_IER_RXIM_Pos (4UL) /*!< UART0 IER: RXIM (Bit 4) */ -#define UART0_IER_RXIM_Msk (0x10UL) /*!< UART0 IER: RXIM (Bitfield-Mask: 0x01) */ -#define UART0_IER_DSRMIM_Pos (3UL) /*!< UART0 IER: DSRMIM (Bit 3) */ -#define UART0_IER_DSRMIM_Msk (0x8UL) /*!< UART0 IER: DSRMIM (Bitfield-Mask: 0x01) */ -#define UART0_IER_DCDMIM_Pos (2UL) /*!< UART0 IER: DCDMIM (Bit 2) */ -#define UART0_IER_DCDMIM_Msk (0x4UL) /*!< UART0 IER: DCDMIM (Bitfield-Mask: 0x01) */ -#define UART0_IER_CTSMIM_Pos (1UL) /*!< UART0 IER: CTSMIM (Bit 1) */ -#define UART0_IER_CTSMIM_Msk (0x2UL) /*!< UART0 IER: CTSMIM (Bitfield-Mask: 0x01) */ -#define UART0_IER_TXCMPMIM_Pos (0UL) /*!< UART0 IER: TXCMPMIM (Bit 0) */ -#define UART0_IER_TXCMPMIM_Msk (0x1UL) /*!< UART0 IER: TXCMPMIM (Bitfield-Mask: 0x01) */ -/* ========================================================== IES ========================================================== */ -#define UART0_IES_OERIS_Pos (10UL) /*!< UART0 IES: OERIS (Bit 10) */ -#define UART0_IES_OERIS_Msk (0x400UL) /*!< UART0 IES: OERIS (Bitfield-Mask: 0x01) */ -#define UART0_IES_BERIS_Pos (9UL) /*!< UART0 IES: BERIS (Bit 9) */ -#define UART0_IES_BERIS_Msk (0x200UL) /*!< UART0 IES: BERIS (Bitfield-Mask: 0x01) */ -#define UART0_IES_PERIS_Pos (8UL) /*!< UART0 IES: PERIS (Bit 8) */ -#define UART0_IES_PERIS_Msk (0x100UL) /*!< UART0 IES: PERIS (Bitfield-Mask: 0x01) */ -#define UART0_IES_FERIS_Pos (7UL) /*!< UART0 IES: FERIS (Bit 7) */ -#define UART0_IES_FERIS_Msk (0x80UL) /*!< UART0 IES: FERIS (Bitfield-Mask: 0x01) */ -#define UART0_IES_RTRIS_Pos (6UL) /*!< UART0 IES: RTRIS (Bit 6) */ -#define UART0_IES_RTRIS_Msk (0x40UL) /*!< UART0 IES: RTRIS (Bitfield-Mask: 0x01) */ -#define UART0_IES_TXRIS_Pos (5UL) /*!< UART0 IES: TXRIS (Bit 5) */ -#define UART0_IES_TXRIS_Msk (0x20UL) /*!< UART0 IES: TXRIS (Bitfield-Mask: 0x01) */ -#define UART0_IES_RXRIS_Pos (4UL) /*!< UART0 IES: RXRIS (Bit 4) */ -#define UART0_IES_RXRIS_Msk (0x10UL) /*!< UART0 IES: RXRIS (Bitfield-Mask: 0x01) */ -#define UART0_IES_DSRMRIS_Pos (3UL) /*!< UART0 IES: DSRMRIS (Bit 3) */ -#define UART0_IES_DSRMRIS_Msk (0x8UL) /*!< UART0 IES: DSRMRIS (Bitfield-Mask: 0x01) */ -#define UART0_IES_DCDMRIS_Pos (2UL) /*!< UART0 IES: DCDMRIS (Bit 2) */ -#define UART0_IES_DCDMRIS_Msk (0x4UL) /*!< UART0 IES: DCDMRIS (Bitfield-Mask: 0x01) */ -#define UART0_IES_CTSMRIS_Pos (1UL) /*!< UART0 IES: CTSMRIS (Bit 1) */ -#define UART0_IES_CTSMRIS_Msk (0x2UL) /*!< UART0 IES: CTSMRIS (Bitfield-Mask: 0x01) */ -#define UART0_IES_TXCMPMRIS_Pos (0UL) /*!< UART0 IES: TXCMPMRIS (Bit 0) */ -#define UART0_IES_TXCMPMRIS_Msk (0x1UL) /*!< UART0 IES: TXCMPMRIS (Bitfield-Mask: 0x01) */ -/* ========================================================== MIS ========================================================== */ -#define UART0_MIS_OEMIS_Pos (10UL) /*!< UART0 MIS: OEMIS (Bit 10) */ -#define UART0_MIS_OEMIS_Msk (0x400UL) /*!< UART0 MIS: OEMIS (Bitfield-Mask: 0x01) */ -#define UART0_MIS_BEMIS_Pos (9UL) /*!< UART0 MIS: BEMIS (Bit 9) */ -#define UART0_MIS_BEMIS_Msk (0x200UL) /*!< UART0 MIS: BEMIS (Bitfield-Mask: 0x01) */ -#define UART0_MIS_PEMIS_Pos (8UL) /*!< UART0 MIS: PEMIS (Bit 8) */ -#define UART0_MIS_PEMIS_Msk (0x100UL) /*!< UART0 MIS: PEMIS (Bitfield-Mask: 0x01) */ -#define UART0_MIS_FEMIS_Pos (7UL) /*!< UART0 MIS: FEMIS (Bit 7) */ -#define UART0_MIS_FEMIS_Msk (0x80UL) /*!< UART0 MIS: FEMIS (Bitfield-Mask: 0x01) */ -#define UART0_MIS_RTMIS_Pos (6UL) /*!< UART0 MIS: RTMIS (Bit 6) */ -#define UART0_MIS_RTMIS_Msk (0x40UL) /*!< UART0 MIS: RTMIS (Bitfield-Mask: 0x01) */ -#define UART0_MIS_TXMIS_Pos (5UL) /*!< UART0 MIS: TXMIS (Bit 5) */ -#define UART0_MIS_TXMIS_Msk (0x20UL) /*!< UART0 MIS: TXMIS (Bitfield-Mask: 0x01) */ -#define UART0_MIS_RXMIS_Pos (4UL) /*!< UART0 MIS: RXMIS (Bit 4) */ -#define UART0_MIS_RXMIS_Msk (0x10UL) /*!< UART0 MIS: RXMIS (Bitfield-Mask: 0x01) */ -#define UART0_MIS_DSRMMIS_Pos (3UL) /*!< UART0 MIS: DSRMMIS (Bit 3) */ -#define UART0_MIS_DSRMMIS_Msk (0x8UL) /*!< UART0 MIS: DSRMMIS (Bitfield-Mask: 0x01) */ -#define UART0_MIS_DCDMMIS_Pos (2UL) /*!< UART0 MIS: DCDMMIS (Bit 2) */ -#define UART0_MIS_DCDMMIS_Msk (0x4UL) /*!< UART0 MIS: DCDMMIS (Bitfield-Mask: 0x01) */ -#define UART0_MIS_CTSMMIS_Pos (1UL) /*!< UART0 MIS: CTSMMIS (Bit 1) */ -#define UART0_MIS_CTSMMIS_Msk (0x2UL) /*!< UART0 MIS: CTSMMIS (Bitfield-Mask: 0x01) */ -#define UART0_MIS_TXCMPMMIS_Pos (0UL) /*!< UART0 MIS: TXCMPMMIS (Bit 0) */ -#define UART0_MIS_TXCMPMMIS_Msk (0x1UL) /*!< UART0 MIS: TXCMPMMIS (Bitfield-Mask: 0x01) */ -/* ========================================================== IEC ========================================================== */ -#define UART0_IEC_OEIC_Pos (10UL) /*!< UART0 IEC: OEIC (Bit 10) */ -#define UART0_IEC_OEIC_Msk (0x400UL) /*!< UART0 IEC: OEIC (Bitfield-Mask: 0x01) */ -#define UART0_IEC_BEIC_Pos (9UL) /*!< UART0 IEC: BEIC (Bit 9) */ -#define UART0_IEC_BEIC_Msk (0x200UL) /*!< UART0 IEC: BEIC (Bitfield-Mask: 0x01) */ -#define UART0_IEC_PEIC_Pos (8UL) /*!< UART0 IEC: PEIC (Bit 8) */ -#define UART0_IEC_PEIC_Msk (0x100UL) /*!< UART0 IEC: PEIC (Bitfield-Mask: 0x01) */ -#define UART0_IEC_FEIC_Pos (7UL) /*!< UART0 IEC: FEIC (Bit 7) */ -#define UART0_IEC_FEIC_Msk (0x80UL) /*!< UART0 IEC: FEIC (Bitfield-Mask: 0x01) */ -#define UART0_IEC_RTIC_Pos (6UL) /*!< UART0 IEC: RTIC (Bit 6) */ -#define UART0_IEC_RTIC_Msk (0x40UL) /*!< UART0 IEC: RTIC (Bitfield-Mask: 0x01) */ -#define UART0_IEC_TXIC_Pos (5UL) /*!< UART0 IEC: TXIC (Bit 5) */ -#define UART0_IEC_TXIC_Msk (0x20UL) /*!< UART0 IEC: TXIC (Bitfield-Mask: 0x01) */ -#define UART0_IEC_RXIC_Pos (4UL) /*!< UART0 IEC: RXIC (Bit 4) */ -#define UART0_IEC_RXIC_Msk (0x10UL) /*!< UART0 IEC: RXIC (Bitfield-Mask: 0x01) */ -#define UART0_IEC_DSRMIC_Pos (3UL) /*!< UART0 IEC: DSRMIC (Bit 3) */ -#define UART0_IEC_DSRMIC_Msk (0x8UL) /*!< UART0 IEC: DSRMIC (Bitfield-Mask: 0x01) */ -#define UART0_IEC_DCDMIC_Pos (2UL) /*!< UART0 IEC: DCDMIC (Bit 2) */ -#define UART0_IEC_DCDMIC_Msk (0x4UL) /*!< UART0 IEC: DCDMIC (Bitfield-Mask: 0x01) */ -#define UART0_IEC_CTSMIC_Pos (1UL) /*!< UART0 IEC: CTSMIC (Bit 1) */ -#define UART0_IEC_CTSMIC_Msk (0x2UL) /*!< UART0 IEC: CTSMIC (Bitfield-Mask: 0x01) */ -#define UART0_IEC_TXCMPMIC_Pos (0UL) /*!< UART0 IEC: TXCMPMIC (Bit 0) */ -#define UART0_IEC_TXCMPMIC_Msk (0x1UL) /*!< UART0 IEC: TXCMPMIC (Bitfield-Mask: 0x01) */ - - -/* =========================================================================================================================== */ -/* ================ VCOMP ================ */ -/* =========================================================================================================================== */ - -/* ========================================================== CFG ========================================================== */ -#define VCOMP_CFG_LVLSEL_Pos (16UL) /*!< VCOMP CFG: LVLSEL (Bit 16) */ -#define VCOMP_CFG_LVLSEL_Msk (0xf0000UL) /*!< VCOMP CFG: LVLSEL (Bitfield-Mask: 0x0f) */ -#define VCOMP_CFG_NSEL_Pos (8UL) /*!< VCOMP CFG: NSEL (Bit 8) */ -#define VCOMP_CFG_NSEL_Msk (0x300UL) /*!< VCOMP CFG: NSEL (Bitfield-Mask: 0x03) */ -#define VCOMP_CFG_PSEL_Pos (0UL) /*!< VCOMP CFG: PSEL (Bit 0) */ -#define VCOMP_CFG_PSEL_Msk (0x3UL) /*!< VCOMP CFG: PSEL (Bitfield-Mask: 0x03) */ -/* ========================================================= STAT ========================================================== */ -#define VCOMP_STAT_PWDSTAT_Pos (1UL) /*!< VCOMP STAT: PWDSTAT (Bit 1) */ -#define VCOMP_STAT_PWDSTAT_Msk (0x2UL) /*!< VCOMP STAT: PWDSTAT (Bitfield-Mask: 0x01) */ -#define VCOMP_STAT_CMPOUT_Pos (0UL) /*!< VCOMP STAT: CMPOUT (Bit 0) */ -#define VCOMP_STAT_CMPOUT_Msk (0x1UL) /*!< VCOMP STAT: CMPOUT (Bitfield-Mask: 0x01) */ -/* ======================================================== PWDKEY ========================================================= */ -#define VCOMP_PWDKEY_PWDKEY_Pos (0UL) /*!< VCOMP PWDKEY: PWDKEY (Bit 0) */ -#define VCOMP_PWDKEY_PWDKEY_Msk (0xffffffffUL) /*!< VCOMP PWDKEY: PWDKEY (Bitfield-Mask: 0xffffffff) */ -/* ========================================================= INTEN ========================================================= */ -#define VCOMP_INTEN_OUTHI_Pos (1UL) /*!< VCOMP INTEN: OUTHI (Bit 1) */ -#define VCOMP_INTEN_OUTHI_Msk (0x2UL) /*!< VCOMP INTEN: OUTHI (Bitfield-Mask: 0x01) */ -#define VCOMP_INTEN_OUTLOW_Pos (0UL) /*!< VCOMP INTEN: OUTLOW (Bit 0) */ -#define VCOMP_INTEN_OUTLOW_Msk (0x1UL) /*!< VCOMP INTEN: OUTLOW (Bitfield-Mask: 0x01) */ -/* ======================================================== INTSTAT ======================================================== */ -#define VCOMP_INTSTAT_OUTHI_Pos (1UL) /*!< VCOMP INTSTAT: OUTHI (Bit 1) */ -#define VCOMP_INTSTAT_OUTHI_Msk (0x2UL) /*!< VCOMP INTSTAT: OUTHI (Bitfield-Mask: 0x01) */ -#define VCOMP_INTSTAT_OUTLOW_Pos (0UL) /*!< VCOMP INTSTAT: OUTLOW (Bit 0) */ -#define VCOMP_INTSTAT_OUTLOW_Msk (0x1UL) /*!< VCOMP INTSTAT: OUTLOW (Bitfield-Mask: 0x01) */ -/* ======================================================== INTCLR ========================================================= */ -#define VCOMP_INTCLR_OUTHI_Pos (1UL) /*!< VCOMP INTCLR: OUTHI (Bit 1) */ -#define VCOMP_INTCLR_OUTHI_Msk (0x2UL) /*!< VCOMP INTCLR: OUTHI (Bitfield-Mask: 0x01) */ -#define VCOMP_INTCLR_OUTLOW_Pos (0UL) /*!< VCOMP INTCLR: OUTLOW (Bit 0) */ -#define VCOMP_INTCLR_OUTLOW_Msk (0x1UL) /*!< VCOMP INTCLR: OUTLOW (Bitfield-Mask: 0x01) */ -/* ======================================================== INTSET ========================================================= */ -#define VCOMP_INTSET_OUTHI_Pos (1UL) /*!< VCOMP INTSET: OUTHI (Bit 1) */ -#define VCOMP_INTSET_OUTHI_Msk (0x2UL) /*!< VCOMP INTSET: OUTHI (Bitfield-Mask: 0x01) */ -#define VCOMP_INTSET_OUTLOW_Pos (0UL) /*!< VCOMP INTSET: OUTLOW (Bit 0) */ -#define VCOMP_INTSET_OUTLOW_Msk (0x1UL) /*!< VCOMP INTSET: OUTLOW (Bitfield-Mask: 0x01) */ - - -/* =========================================================================================================================== */ -/* ================ WDT ================ */ -/* =========================================================================================================================== */ - -/* ========================================================== CFG ========================================================== */ -#define WDT_CFG_CLKSEL_Pos (24UL) /*!< WDT CFG: CLKSEL (Bit 24) */ -#define WDT_CFG_CLKSEL_Msk (0x7000000UL) /*!< WDT CFG: CLKSEL (Bitfield-Mask: 0x07) */ -#define WDT_CFG_INTVAL_Pos (16UL) /*!< WDT CFG: INTVAL (Bit 16) */ -#define WDT_CFG_INTVAL_Msk (0xff0000UL) /*!< WDT CFG: INTVAL (Bitfield-Mask: 0xff) */ -#define WDT_CFG_RESVAL_Pos (8UL) /*!< WDT CFG: RESVAL (Bit 8) */ -#define WDT_CFG_RESVAL_Msk (0xff00UL) /*!< WDT CFG: RESVAL (Bitfield-Mask: 0xff) */ -#define WDT_CFG_RESEN_Pos (2UL) /*!< WDT CFG: RESEN (Bit 2) */ -#define WDT_CFG_RESEN_Msk (0x4UL) /*!< WDT CFG: RESEN (Bitfield-Mask: 0x01) */ -#define WDT_CFG_INTEN_Pos (1UL) /*!< WDT CFG: INTEN (Bit 1) */ -#define WDT_CFG_INTEN_Msk (0x2UL) /*!< WDT CFG: INTEN (Bitfield-Mask: 0x01) */ -#define WDT_CFG_WDTEN_Pos (0UL) /*!< WDT CFG: WDTEN (Bit 0) */ -#define WDT_CFG_WDTEN_Msk (0x1UL) /*!< WDT CFG: WDTEN (Bitfield-Mask: 0x01) */ -/* ========================================================= RSTRT ========================================================= */ -#define WDT_RSTRT_RSTRT_Pos (0UL) /*!< WDT RSTRT: RSTRT (Bit 0) */ -#define WDT_RSTRT_RSTRT_Msk (0xffUL) /*!< WDT RSTRT: RSTRT (Bitfield-Mask: 0xff) */ -/* ========================================================= LOCK ========================================================== */ -#define WDT_LOCK_LOCK_Pos (0UL) /*!< WDT LOCK: LOCK (Bit 0) */ -#define WDT_LOCK_LOCK_Msk (0xffUL) /*!< WDT LOCK: LOCK (Bitfield-Mask: 0xff) */ -/* ========================================================= COUNT ========================================================= */ -#define WDT_COUNT_COUNT_Pos (0UL) /*!< WDT COUNT: COUNT (Bit 0) */ -#define WDT_COUNT_COUNT_Msk (0xffUL) /*!< WDT COUNT: COUNT (Bitfield-Mask: 0xff) */ -/* ========================================================= INTEN ========================================================= */ -#define WDT_INTEN_WDTINT_Pos (0UL) /*!< WDT INTEN: WDTINT (Bit 0) */ -#define WDT_INTEN_WDTINT_Msk (0x1UL) /*!< WDT INTEN: WDTINT (Bitfield-Mask: 0x01) */ -/* ======================================================== INTSTAT ======================================================== */ -#define WDT_INTSTAT_WDTINT_Pos (0UL) /*!< WDT INTSTAT: WDTINT (Bit 0) */ -#define WDT_INTSTAT_WDTINT_Msk (0x1UL) /*!< WDT INTSTAT: WDTINT (Bitfield-Mask: 0x01) */ -/* ======================================================== INTCLR ========================================================= */ -#define WDT_INTCLR_WDTINT_Pos (0UL) /*!< WDT INTCLR: WDTINT (Bit 0) */ -#define WDT_INTCLR_WDTINT_Msk (0x1UL) /*!< WDT INTCLR: WDTINT (Bitfield-Mask: 0x01) */ -/* ======================================================== INTSET ========================================================= */ -#define WDT_INTSET_WDTINT_Pos (0UL) /*!< WDT INTSET: WDTINT (Bit 0) */ -#define WDT_INTSET_WDTINT_Msk (0x1UL) /*!< WDT INTSET: WDTINT (Bitfield-Mask: 0x01) */ - -/** @} */ /* End of group PosMask_peripherals */ - - -/* =========================================================================================================================== */ -/* ================ Enumerated Values Peripheral Section ================ */ -/* =========================================================================================================================== */ - - -/** @addtogroup EnumValue_peripherals - * @{ - */ - - - -/* =========================================================================================================================== */ -/* ================ ADC ================ */ -/* =========================================================================================================================== */ - -/* ========================================================== CFG ========================================================== */ -/* ================================================ ADC CFG CLKSEL [24..25] ================================================ */ -typedef enum { /*!< ADC_CFG_CLKSEL */ - ADC_CFG_CLKSEL_OFF = 0, /*!< OFF : Off mode. The HFRC or HFRC_DIV2 clock must be selected - for the ADC to function. The ADC controller - automatically shuts off the clock in it's - low power modes. When setting ADCEN to '0', - the CLKSEL should remain set to one of the - two clock selects for proper power down - sequencing. value. */ - ADC_CFG_CLKSEL_HFRC = 1, /*!< HFRC : HFRC Core Clock divided by (CORESEL+1) value. */ - ADC_CFG_CLKSEL_HFRC_DIV2 = 2, /*!< HFRC_DIV2 : HFRC Core Clock / 2 further divided by (CORESEL+1) - value. */ -} ADC_CFG_CLKSEL_Enum; - -/* =============================================== ADC CFG TRIGPOL [19..19] ================================================ */ -typedef enum { /*!< ADC_CFG_TRIGPOL */ - ADC_CFG_TRIGPOL_RISING_EDGE = 0, /*!< RISING_EDGE : Trigger on rising edge. value. */ - ADC_CFG_TRIGPOL_FALLING_EDGE = 1, /*!< FALLING_EDGE : Trigger on falling edge. value. */ -} ADC_CFG_TRIGPOL_Enum; - -/* =============================================== ADC CFG TRIGSEL [16..18] ================================================ */ -typedef enum { /*!< ADC_CFG_TRIGSEL */ - ADC_CFG_TRIGSEL_EXT0 = 0, /*!< EXT0 : Off chip External Trigger0 (ADC_ET0) value. */ - ADC_CFG_TRIGSEL_EXT1 = 1, /*!< EXT1 : Off chip External Trigger1 (ADC_ET1) value. */ - ADC_CFG_TRIGSEL_EXT2 = 2, /*!< EXT2 : Off chip External Trigger2 (ADC_ET2) value. */ - ADC_CFG_TRIGSEL_EXT3 = 3, /*!< EXT3 : Off chip External Trigger3 (ADC_ET3) value. */ - ADC_CFG_TRIGSEL_VCOMP = 4, /*!< VCOMP : Voltage Comparator Output value. */ - ADC_CFG_TRIGSEL_SWT = 7, /*!< SWT : Software Trigger value. */ -} ADC_CFG_TRIGSEL_Enum; - -/* ============================================== ADC CFG DFIFORDEN [12..12] =============================================== */ -typedef enum { /*!< ADC_CFG_DFIFORDEN */ - ADC_CFG_DFIFORDEN_DIS = 0, /*!< DIS : Destructive Reads are prevented. Reads to the FIFOPR register - will not POP an entry off the FIFO. value. */ - ADC_CFG_DFIFORDEN_EN = 1, /*!< EN : Reads to the FIFOPR registger will automatically pop an - entry off the FIFO. value. */ -} ADC_CFG_DFIFORDEN_Enum; - -/* ================================================= ADC CFG REFSEL [8..9] ================================================= */ -typedef enum { /*!< ADC_CFG_REFSEL */ - ADC_CFG_REFSEL_INT2P0 = 0, /*!< INT2P0 : Internal 2.0V Bandgap Reference Voltage value. */ - ADC_CFG_REFSEL_INT1P5 = 1, /*!< INT1P5 : Internal 1.5V Bandgap Reference Voltage value. */ - ADC_CFG_REFSEL_EXT2P0 = 2, /*!< EXT2P0 : Off Chip 2.0V Reference value. */ - ADC_CFG_REFSEL_EXT1P5 = 3, /*!< EXT1P5 : Off Chip 1.5V Reference value. */ -} ADC_CFG_REFSEL_Enum; - -/* ================================================= ADC CFG CKMODE [4..4] ================================================= */ -typedef enum { /*!< ADC_CFG_CKMODE */ - ADC_CFG_CKMODE_LPCKMODE = 0, /*!< LPCKMODE : Disable the clock between scans for LPMODE0. Set - LPCKMODE to 0x1 while configuring the ADC. value. */ - ADC_CFG_CKMODE_LLCKMODE = 1, /*!< LLCKMODE : Low Latency Clock Mode. When set, HFRC and the adc_clk - will remain on while in functioning in LPMODE0. value. */ -} ADC_CFG_CKMODE_Enum; - -/* ================================================= ADC CFG LPMODE [3..3] ================================================= */ -typedef enum { /*!< ADC_CFG_LPMODE */ - ADC_CFG_LPMODE_MODE0 = 0, /*!< MODE0 : Low Power Mode 0. Leaves the ADC fully powered between - scans with minimum latency between a trigger event and - sample data collection. value. */ - ADC_CFG_LPMODE_MODE1 = 1, /*!< MODE1 : Low Power Mode 1. Powers down all circuity and clocks - associated with the ADC until the next trigger event. Between - scans, the reference buffer requires up to 50us of delay - from a scan trigger event before the conversion will commence - while operating in this mode. value. */ -} ADC_CFG_LPMODE_Enum; - -/* ================================================= ADC CFG RPTEN [2..2] ================================================== */ -typedef enum { /*!< ADC_CFG_RPTEN */ - ADC_CFG_RPTEN_SINGLE_SCAN = 0, /*!< SINGLE_SCAN : In Single Scan Mode, the ADC will complete a single - scan upon each trigger event. value. */ - ADC_CFG_RPTEN_REPEATING_SCAN = 1, /*!< REPEATING_SCAN : In Repeating Scan Mode, the ADC will complete - it's first scan upon the initial trigger event and all - subsequent scans will occur at regular intervals defined - by the configuration programmed for the CTTMRA3 internal - timer until the timer is disabled or the ADC is disabled. - When disabling the ADC (setting ADCEN to '0'), the RPTEN - bit should be cleared. value. */ -} ADC_CFG_RPTEN_Enum; - -/* ================================================= ADC CFG ADCEN [0..0] ================================================== */ -typedef enum { /*!< ADC_CFG_ADCEN */ - ADC_CFG_ADCEN_DIS = 0, /*!< DIS : Disable the ADC module. value. */ - ADC_CFG_ADCEN_EN = 1, /*!< EN : Enable the ADC module. value. */ -} ADC_CFG_ADCEN_Enum; - -/* ========================================================= STAT ========================================================== */ -/* ================================================ ADC STAT PWDSTAT [0..0] ================================================ */ -typedef enum { /*!< ADC_STAT_PWDSTAT */ - ADC_STAT_PWDSTAT_ON = 0, /*!< ON : Powered on. value. */ - ADC_STAT_PWDSTAT_POWERED_DOWN = 1, /*!< POWERED_DOWN : ADC Low Power Mode 1. value. */ -} ADC_STAT_PWDSTAT_Enum; - -/* ========================================================== SWT ========================================================== */ -/* ================================================== ADC SWT SWT [0..7] =================================================== */ -typedef enum { /*!< ADC_SWT_SWT */ - ADC_SWT_SWT_GEN_SW_TRIGGER = 55, /*!< GEN_SW_TRIGGER : Writing this value generates a software trigger. - value. */ -} ADC_SWT_SWT_Enum; - -/* ======================================================== SL0CFG ========================================================= */ -/* ============================================== ADC SL0CFG ADSEL0 [24..26] =============================================== */ -typedef enum { /*!< ADC_SL0CFG_ADSEL0 */ - ADC_SL0CFG_ADSEL0_AVG_1_MSRMT = 0, /*!< AVG_1_MSRMT : Average in 1 measurement in the accumulate divide - module for this slot. value. */ - ADC_SL0CFG_ADSEL0_AVG_2_MSRMTS = 1, /*!< AVG_2_MSRMTS : Average in 2 measurements in the accumulate divide - module for this slot. value. */ - ADC_SL0CFG_ADSEL0_AVG_4_MSRMTS = 2, /*!< AVG_4_MSRMTS : Average in 4 measurements in the accumulate divide - module for this slot. value. */ - ADC_SL0CFG_ADSEL0_AVG_8_MSRMT = 3, /*!< AVG_8_MSRMT : Average in 8 measurements in the accumulate divide - module for this slot. value. */ - ADC_SL0CFG_ADSEL0_AVG_16_MSRMTS = 4, /*!< AVG_16_MSRMTS : Average in 16 measurements in the accumulate - divide module for this slot. value. */ - ADC_SL0CFG_ADSEL0_AVG_32_MSRMTS = 5, /*!< AVG_32_MSRMTS : Average in 32 measurements in the accumulate - divide module for this slot. value. */ - ADC_SL0CFG_ADSEL0_AVG_64_MSRMTS = 6, /*!< AVG_64_MSRMTS : Average in 64 measurements in the accumulate - divide module for this slot. value. */ - ADC_SL0CFG_ADSEL0_AVG_128_MSRMTS = 7, /*!< AVG_128_MSRMTS : Average in 128 measurements in the accumulate - divide module for this slot. value. */ -} ADC_SL0CFG_ADSEL0_Enum; - -/* ============================================== ADC SL0CFG PRMODE0 [16..17] ============================================== */ -typedef enum { /*!< ADC_SL0CFG_PRMODE0 */ - ADC_SL0CFG_PRMODE0_P14B = 0, /*!< P14B : 14-bit precision mode value. */ - ADC_SL0CFG_PRMODE0_P12B = 1, /*!< P12B : 12-bit precision mode value. */ - ADC_SL0CFG_PRMODE0_P10B = 2, /*!< P10B : 10-bit precision mode value. */ - ADC_SL0CFG_PRMODE0_P8B = 3, /*!< P8B : 8-bit precision mode value. */ -} ADC_SL0CFG_PRMODE0_Enum; - -/* =============================================== ADC SL0CFG CHSEL0 [8..11] =============================================== */ -typedef enum { /*!< ADC_SL0CFG_CHSEL0 */ - ADC_SL0CFG_CHSEL0_SE0 = 0, /*!< SE0 : single ended external GPIO connection to pad16. value. */ - ADC_SL0CFG_CHSEL0_SE1 = 1, /*!< SE1 : single ended external GPIO connection to pad29. value. */ - ADC_SL0CFG_CHSEL0_SE2 = 2, /*!< SE2 : single ended external GPIO connection to pad11. value. */ - ADC_SL0CFG_CHSEL0_SE3 = 3, /*!< SE3 : single ended external GPIO connection to pad31. value. */ - ADC_SL0CFG_CHSEL0_SE4 = 4, /*!< SE4 : single ended external GPIO connection to pad32. value. */ - ADC_SL0CFG_CHSEL0_SE5 = 5, /*!< SE5 : single ended external GPIO connection to pad33. value. */ - ADC_SL0CFG_CHSEL0_SE6 = 6, /*!< SE6 : single ended external GPIO connection to pad34. value. */ - ADC_SL0CFG_CHSEL0_SE7 = 7, /*!< SE7 : single ended external GPIO connection to pad35. value. */ - ADC_SL0CFG_CHSEL0_SE8 = 8, /*!< SE8 : single ended external GPIO connection to pad13. value. */ - ADC_SL0CFG_CHSEL0_SE9 = 9, /*!< SE9 : single ended external GPIO connection to pad12. value. */ - ADC_SL0CFG_CHSEL0_DF0 = 10, /*!< DF0 : differential external GPIO connections to pad12(N) and - pad13(P). value. */ - ADC_SL0CFG_CHSEL0_DF1 = 11, /*!< DF1 : differential external GPIO connections to pad15(N) and - pad14(P). value. */ - ADC_SL0CFG_CHSEL0_TEMP = 12, /*!< TEMP : internal temperature sensor. value. */ - ADC_SL0CFG_CHSEL0_BATT = 13, /*!< BATT : internal voltage divide-by-3 connection. value. */ - ADC_SL0CFG_CHSEL0_VSS = 14, /*!< VSS : Input VSS value. */ -} ADC_SL0CFG_CHSEL0_Enum; - -/* ================================================ ADC SL0CFG WCEN0 [1..1] ================================================ */ -typedef enum { /*!< ADC_SL0CFG_WCEN0 */ - ADC_SL0CFG_WCEN0_WCEN = 1, /*!< WCEN : Enable the window compare for slot 0. value. */ -} ADC_SL0CFG_WCEN0_Enum; - -/* ================================================ ADC SL0CFG SLEN0 [0..0] ================================================ */ -typedef enum { /*!< ADC_SL0CFG_SLEN0 */ - ADC_SL0CFG_SLEN0_SLEN = 1, /*!< SLEN : Enable slot 0 for ADC conversions. value. */ -} ADC_SL0CFG_SLEN0_Enum; - -/* ======================================================== SL1CFG ========================================================= */ -/* ============================================== ADC SL1CFG ADSEL1 [24..26] =============================================== */ -typedef enum { /*!< ADC_SL1CFG_ADSEL1 */ - ADC_SL1CFG_ADSEL1_AVG_1_MSRMT = 0, /*!< AVG_1_MSRMT : Average in 1 measurement in the accumulate divide - module for this slot. value. */ - ADC_SL1CFG_ADSEL1_AVG_2_MSRMTS = 1, /*!< AVG_2_MSRMTS : Average in 2 measurements in the accumulate divide - module for this slot. value. */ - ADC_SL1CFG_ADSEL1_AVG_4_MSRMTS = 2, /*!< AVG_4_MSRMTS : Average in 4 measurements in the accumulate divide - module for this slot. value. */ - ADC_SL1CFG_ADSEL1_AVG_8_MSRMT = 3, /*!< AVG_8_MSRMT : Average in 8 measurements in the accumulate divide - module for this slot. value. */ - ADC_SL1CFG_ADSEL1_AVG_16_MSRMTS = 4, /*!< AVG_16_MSRMTS : Average in 16 measurements in the accumulate - divide module for this slot. value. */ - ADC_SL1CFG_ADSEL1_AVG_32_MSRMTS = 5, /*!< AVG_32_MSRMTS : Average in 32 measurements in the accumulate - divide module for this slot. value. */ - ADC_SL1CFG_ADSEL1_AVG_64_MSRMTS = 6, /*!< AVG_64_MSRMTS : Average in 64 measurements in the accumulate - divide module for this slot. value. */ - ADC_SL1CFG_ADSEL1_AVG_128_MSRMTS = 7, /*!< AVG_128_MSRMTS : Average in 128 measurements in the accumulate - divide module for this slot. value. */ -} ADC_SL1CFG_ADSEL1_Enum; - -/* ============================================== ADC SL1CFG PRMODE1 [16..17] ============================================== */ -typedef enum { /*!< ADC_SL1CFG_PRMODE1 */ - ADC_SL1CFG_PRMODE1_P14B = 0, /*!< P14B : 14-bit precision mode value. */ - ADC_SL1CFG_PRMODE1_P12B = 1, /*!< P12B : 12-bit precision mode value. */ - ADC_SL1CFG_PRMODE1_P10B = 2, /*!< P10B : 10-bit precision mode value. */ - ADC_SL1CFG_PRMODE1_P8B = 3, /*!< P8B : 8-bit precision mode value. */ -} ADC_SL1CFG_PRMODE1_Enum; - -/* =============================================== ADC SL1CFG CHSEL1 [8..11] =============================================== */ -typedef enum { /*!< ADC_SL1CFG_CHSEL1 */ - ADC_SL1CFG_CHSEL1_SE0 = 0, /*!< SE0 : single ended external GPIO connection to pad16. value. */ - ADC_SL1CFG_CHSEL1_SE1 = 1, /*!< SE1 : single ended external GPIO connection to pad29. value. */ - ADC_SL1CFG_CHSEL1_SE2 = 2, /*!< SE2 : single ended external GPIO connection to pad11. value. */ - ADC_SL1CFG_CHSEL1_SE3 = 3, /*!< SE3 : single ended external GPIO connection to pad31. value. */ - ADC_SL1CFG_CHSEL1_SE4 = 4, /*!< SE4 : single ended external GPIO connection to pad32. value. */ - ADC_SL1CFG_CHSEL1_SE5 = 5, /*!< SE5 : single ended external GPIO connection to pad33. value. */ - ADC_SL1CFG_CHSEL1_SE6 = 6, /*!< SE6 : single ended external GPIO connection to pad34. value. */ - ADC_SL1CFG_CHSEL1_SE7 = 7, /*!< SE7 : single ended external GPIO connection to pad35. value. */ - ADC_SL1CFG_CHSEL1_SE8 = 8, /*!< SE8 : single ended external GPIO connection to pad13. value. */ - ADC_SL1CFG_CHSEL1_SE9 = 9, /*!< SE9 : single ended external GPIO connection to pad12. value. */ - ADC_SL1CFG_CHSEL1_DF0 = 10, /*!< DF0 : differential external GPIO connections to pad12(N) and - pad13(P). value. */ - ADC_SL1CFG_CHSEL1_DF1 = 11, /*!< DF1 : differential external GPIO connections to pad15(N) and - pad14(P). value. */ - ADC_SL1CFG_CHSEL1_TEMP = 12, /*!< TEMP : internal temperature sensor. value. */ - ADC_SL1CFG_CHSEL1_BATT = 13, /*!< BATT : internal voltage divide-by-3 connection. value. */ - ADC_SL1CFG_CHSEL1_VSS = 14, /*!< VSS : Input VSS value. */ -} ADC_SL1CFG_CHSEL1_Enum; - -/* ================================================ ADC SL1CFG WCEN1 [1..1] ================================================ */ -typedef enum { /*!< ADC_SL1CFG_WCEN1 */ - ADC_SL1CFG_WCEN1_WCEN = 1, /*!< WCEN : Enable the window compare for slot 1. value. */ -} ADC_SL1CFG_WCEN1_Enum; - -/* ================================================ ADC SL1CFG SLEN1 [0..0] ================================================ */ -typedef enum { /*!< ADC_SL1CFG_SLEN1 */ - ADC_SL1CFG_SLEN1_SLEN = 1, /*!< SLEN : Enable slot 1 for ADC conversions. value. */ -} ADC_SL1CFG_SLEN1_Enum; - -/* ======================================================== SL2CFG ========================================================= */ -/* ============================================== ADC SL2CFG ADSEL2 [24..26] =============================================== */ -typedef enum { /*!< ADC_SL2CFG_ADSEL2 */ - ADC_SL2CFG_ADSEL2_AVG_1_MSRMT = 0, /*!< AVG_1_MSRMT : Average in 1 measurement in the accumulate divide - module for this slot. value. */ - ADC_SL2CFG_ADSEL2_AVG_2_MSRMTS = 1, /*!< AVG_2_MSRMTS : Average in 2 measurements in the accumulate divide - module for this slot. value. */ - ADC_SL2CFG_ADSEL2_AVG_4_MSRMTS = 2, /*!< AVG_4_MSRMTS : Average in 4 measurements in the accumulate divide - module for this slot. value. */ - ADC_SL2CFG_ADSEL2_AVG_8_MSRMT = 3, /*!< AVG_8_MSRMT : Average in 8 measurements in the accumulate divide - module for this slot. value. */ - ADC_SL2CFG_ADSEL2_AVG_16_MSRMTS = 4, /*!< AVG_16_MSRMTS : Average in 16 measurements in the accumulate - divide module for this slot. value. */ - ADC_SL2CFG_ADSEL2_AVG_32_MSRMTS = 5, /*!< AVG_32_MSRMTS : Average in 32 measurements in the accumulate - divide module for this slot. value. */ - ADC_SL2CFG_ADSEL2_AVG_64_MSRMTS = 6, /*!< AVG_64_MSRMTS : Average in 64 measurements in the accumulate - divide module for this slot. value. */ - ADC_SL2CFG_ADSEL2_AVG_128_MSRMTS = 7, /*!< AVG_128_MSRMTS : Average in 128 measurements in the accumulate - divide module for this slot. value. */ -} ADC_SL2CFG_ADSEL2_Enum; - -/* ============================================== ADC SL2CFG PRMODE2 [16..17] ============================================== */ -typedef enum { /*!< ADC_SL2CFG_PRMODE2 */ - ADC_SL2CFG_PRMODE2_P14B = 0, /*!< P14B : 14-bit precision mode value. */ - ADC_SL2CFG_PRMODE2_P12B = 1, /*!< P12B : 12-bit precision mode value. */ - ADC_SL2CFG_PRMODE2_P10B = 2, /*!< P10B : 10-bit precision mode value. */ - ADC_SL2CFG_PRMODE2_P8B = 3, /*!< P8B : 8-bit precision mode value. */ -} ADC_SL2CFG_PRMODE2_Enum; - -/* =============================================== ADC SL2CFG CHSEL2 [8..11] =============================================== */ -typedef enum { /*!< ADC_SL2CFG_CHSEL2 */ - ADC_SL2CFG_CHSEL2_SE0 = 0, /*!< SE0 : single ended external GPIO connection to pad16. value. */ - ADC_SL2CFG_CHSEL2_SE1 = 1, /*!< SE1 : single ended external GPIO connection to pad29. value. */ - ADC_SL2CFG_CHSEL2_SE2 = 2, /*!< SE2 : single ended external GPIO connection to pad11. value. */ - ADC_SL2CFG_CHSEL2_SE3 = 3, /*!< SE3 : single ended external GPIO connection to pad31. value. */ - ADC_SL2CFG_CHSEL2_SE4 = 4, /*!< SE4 : single ended external GPIO connection to pad32. value. */ - ADC_SL2CFG_CHSEL2_SE5 = 5, /*!< SE5 : single ended external GPIO connection to pad33. value. */ - ADC_SL2CFG_CHSEL2_SE6 = 6, /*!< SE6 : single ended external GPIO connection to pad34. value. */ - ADC_SL2CFG_CHSEL2_SE7 = 7, /*!< SE7 : single ended external GPIO connection to pad35. value. */ - ADC_SL2CFG_CHSEL2_SE8 = 8, /*!< SE8 : single ended external GPIO connection to pad13. value. */ - ADC_SL2CFG_CHSEL2_SE9 = 9, /*!< SE9 : single ended external GPIO connection to pad12. value. */ - ADC_SL2CFG_CHSEL2_DF0 = 10, /*!< DF0 : differential external GPIO connections to pad12(N) and - pad13(P). value. */ - ADC_SL2CFG_CHSEL2_DF1 = 11, /*!< DF1 : differential external GPIO connections to pad15(N) and - pad14(P). value. */ - ADC_SL2CFG_CHSEL2_TEMP = 12, /*!< TEMP : internal temperature sensor. value. */ - ADC_SL2CFG_CHSEL2_BATT = 13, /*!< BATT : internal voltage divide-by-3 connection. value. */ - ADC_SL2CFG_CHSEL2_VSS = 14, /*!< VSS : Input VSS value. */ -} ADC_SL2CFG_CHSEL2_Enum; - -/* ================================================ ADC SL2CFG WCEN2 [1..1] ================================================ */ -typedef enum { /*!< ADC_SL2CFG_WCEN2 */ - ADC_SL2CFG_WCEN2_WCEN = 1, /*!< WCEN : Enable the window compare for slot 2. value. */ -} ADC_SL2CFG_WCEN2_Enum; - -/* ================================================ ADC SL2CFG SLEN2 [0..0] ================================================ */ -typedef enum { /*!< ADC_SL2CFG_SLEN2 */ - ADC_SL2CFG_SLEN2_SLEN = 1, /*!< SLEN : Enable slot 2 for ADC conversions. value. */ -} ADC_SL2CFG_SLEN2_Enum; - -/* ======================================================== SL3CFG ========================================================= */ -/* ============================================== ADC SL3CFG ADSEL3 [24..26] =============================================== */ -typedef enum { /*!< ADC_SL3CFG_ADSEL3 */ - ADC_SL3CFG_ADSEL3_AVG_1_MSRMT = 0, /*!< AVG_1_MSRMT : Average in 1 measurement in the accumulate divide - module for this slot. value. */ - ADC_SL3CFG_ADSEL3_AVG_2_MSRMTS = 1, /*!< AVG_2_MSRMTS : Average in 2 measurements in the accumulate divide - module for this slot. value. */ - ADC_SL3CFG_ADSEL3_AVG_4_MSRMTS = 2, /*!< AVG_4_MSRMTS : Average in 4 measurements in the accumulate divide - module for this slot. value. */ - ADC_SL3CFG_ADSEL3_AVG_8_MSRMT = 3, /*!< AVG_8_MSRMT : Average in 8 measurements in the accumulate divide - module for this slot. value. */ - ADC_SL3CFG_ADSEL3_AVG_16_MSRMTS = 4, /*!< AVG_16_MSRMTS : Average in 16 measurements in the accumulate - divide module for this slot. value. */ - ADC_SL3CFG_ADSEL3_AVG_32_MSRMTS = 5, /*!< AVG_32_MSRMTS : Average in 32 measurements in the accumulate - divide module for this slot. value. */ - ADC_SL3CFG_ADSEL3_AVG_64_MSRMTS = 6, /*!< AVG_64_MSRMTS : Average in 64 measurements in the accumulate - divide module for this slot. value. */ - ADC_SL3CFG_ADSEL3_AVG_128_MSRMTS = 7, /*!< AVG_128_MSRMTS : Average in 128 measurements in the accumulate - divide module for this slot. value. */ -} ADC_SL3CFG_ADSEL3_Enum; - -/* ============================================== ADC SL3CFG PRMODE3 [16..17] ============================================== */ -typedef enum { /*!< ADC_SL3CFG_PRMODE3 */ - ADC_SL3CFG_PRMODE3_P14B = 0, /*!< P14B : 14-bit precision mode value. */ - ADC_SL3CFG_PRMODE3_P12B = 1, /*!< P12B : 12-bit precision mode value. */ - ADC_SL3CFG_PRMODE3_P10B = 2, /*!< P10B : 10-bit precision mode value. */ - ADC_SL3CFG_PRMODE3_P8B = 3, /*!< P8B : 8-bit precision mode value. */ -} ADC_SL3CFG_PRMODE3_Enum; - -/* =============================================== ADC SL3CFG CHSEL3 [8..11] =============================================== */ -typedef enum { /*!< ADC_SL3CFG_CHSEL3 */ - ADC_SL3CFG_CHSEL3_SE0 = 0, /*!< SE0 : single ended external GPIO connection to pad16. value. */ - ADC_SL3CFG_CHSEL3_SE1 = 1, /*!< SE1 : single ended external GPIO connection to pad29. value. */ - ADC_SL3CFG_CHSEL3_SE2 = 2, /*!< SE2 : single ended external GPIO connection to pad11. value. */ - ADC_SL3CFG_CHSEL3_SE3 = 3, /*!< SE3 : single ended external GPIO connection to pad31. value. */ - ADC_SL3CFG_CHSEL3_SE4 = 4, /*!< SE4 : single ended external GPIO connection to pad32. value. */ - ADC_SL3CFG_CHSEL3_SE5 = 5, /*!< SE5 : single ended external GPIO connection to pad33. value. */ - ADC_SL3CFG_CHSEL3_SE6 = 6, /*!< SE6 : single ended external GPIO connection to pad34. value. */ - ADC_SL3CFG_CHSEL3_SE7 = 7, /*!< SE7 : single ended external GPIO connection to pad35. value. */ - ADC_SL3CFG_CHSEL3_SE8 = 8, /*!< SE8 : single ended external GPIO connection to pad13. value. */ - ADC_SL3CFG_CHSEL3_SE9 = 9, /*!< SE9 : single ended external GPIO connection to pad12. value. */ - ADC_SL3CFG_CHSEL3_DF0 = 10, /*!< DF0 : differential external GPIO connections to pad12(N) and - pad13(P). value. */ - ADC_SL3CFG_CHSEL3_DF1 = 11, /*!< DF1 : differential external GPIO connections to pad15(N) and - pad14(P). value. */ - ADC_SL3CFG_CHSEL3_TEMP = 12, /*!< TEMP : internal temperature sensor. value. */ - ADC_SL3CFG_CHSEL3_BATT = 13, /*!< BATT : internal voltage divide-by-3 connection. value. */ - ADC_SL3CFG_CHSEL3_VSS = 14, /*!< VSS : Input VSS value. */ -} ADC_SL3CFG_CHSEL3_Enum; - -/* ================================================ ADC SL3CFG WCEN3 [1..1] ================================================ */ -typedef enum { /*!< ADC_SL3CFG_WCEN3 */ - ADC_SL3CFG_WCEN3_WCEN = 1, /*!< WCEN : Enable the window compare for slot 3. value. */ -} ADC_SL3CFG_WCEN3_Enum; - -/* ================================================ ADC SL3CFG SLEN3 [0..0] ================================================ */ -typedef enum { /*!< ADC_SL3CFG_SLEN3 */ - ADC_SL3CFG_SLEN3_SLEN = 1, /*!< SLEN : Enable slot 3 for ADC conversions. value. */ -} ADC_SL3CFG_SLEN3_Enum; - -/* ======================================================== SL4CFG ========================================================= */ -/* ============================================== ADC SL4CFG ADSEL4 [24..26] =============================================== */ -typedef enum { /*!< ADC_SL4CFG_ADSEL4 */ - ADC_SL4CFG_ADSEL4_AVG_1_MSRMT = 0, /*!< AVG_1_MSRMT : Average in 1 measurement in the accumulate divide - module for this slot. value. */ - ADC_SL4CFG_ADSEL4_AVG_2_MSRMTS = 1, /*!< AVG_2_MSRMTS : Average in 2 measurements in the accumulate divide - module for this slot. value. */ - ADC_SL4CFG_ADSEL4_AVG_4_MSRMTS = 2, /*!< AVG_4_MSRMTS : Average in 4 measurements in the accumulate divide - module for this slot. value. */ - ADC_SL4CFG_ADSEL4_AVG_8_MSRMT = 3, /*!< AVG_8_MSRMT : Average in 8 measurements in the accumulate divide - module for this slot. value. */ - ADC_SL4CFG_ADSEL4_AVG_16_MSRMTS = 4, /*!< AVG_16_MSRMTS : Average in 16 measurements in the accumulate - divide module for this slot. value. */ - ADC_SL4CFG_ADSEL4_AVG_32_MSRMTS = 5, /*!< AVG_32_MSRMTS : Average in 32 measurements in the accumulate - divide module for this slot. value. */ - ADC_SL4CFG_ADSEL4_AVG_64_MSRMTS = 6, /*!< AVG_64_MSRMTS : Average in 64 measurements in the accumulate - divide module for this slot. value. */ - ADC_SL4CFG_ADSEL4_AVG_128_MSRMTS = 7, /*!< AVG_128_MSRMTS : Average in 128 measurements in the accumulate - divide module for this slot. value. */ -} ADC_SL4CFG_ADSEL4_Enum; - -/* ============================================== ADC SL4CFG PRMODE4 [16..17] ============================================== */ -typedef enum { /*!< ADC_SL4CFG_PRMODE4 */ - ADC_SL4CFG_PRMODE4_P14B = 0, /*!< P14B : 14-bit precision mode value. */ - ADC_SL4CFG_PRMODE4_P12B = 1, /*!< P12B : 12-bit precision mode value. */ - ADC_SL4CFG_PRMODE4_P10B = 2, /*!< P10B : 10-bit precision mode value. */ - ADC_SL4CFG_PRMODE4_P8B = 3, /*!< P8B : 8-bit precision mode value. */ -} ADC_SL4CFG_PRMODE4_Enum; - -/* =============================================== ADC SL4CFG CHSEL4 [8..11] =============================================== */ -typedef enum { /*!< ADC_SL4CFG_CHSEL4 */ - ADC_SL4CFG_CHSEL4_SE0 = 0, /*!< SE0 : single ended external GPIO connection to pad16. value. */ - ADC_SL4CFG_CHSEL4_SE1 = 1, /*!< SE1 : single ended external GPIO connection to pad29. value. */ - ADC_SL4CFG_CHSEL4_SE2 = 2, /*!< SE2 : single ended external GPIO connection to pad11. value. */ - ADC_SL4CFG_CHSEL4_SE3 = 3, /*!< SE3 : single ended external GPIO connection to pad31. value. */ - ADC_SL4CFG_CHSEL4_SE4 = 4, /*!< SE4 : single ended external GPIO connection to pad32. value. */ - ADC_SL4CFG_CHSEL4_SE5 = 5, /*!< SE5 : single ended external GPIO connection to pad33. value. */ - ADC_SL4CFG_CHSEL4_SE6 = 6, /*!< SE6 : single ended external GPIO connection to pad34. value. */ - ADC_SL4CFG_CHSEL4_SE7 = 7, /*!< SE7 : single ended external GPIO connection to pad35. value. */ - ADC_SL4CFG_CHSEL4_SE8 = 8, /*!< SE8 : single ended external GPIO connection to pad13. value. */ - ADC_SL4CFG_CHSEL4_SE9 = 9, /*!< SE9 : single ended external GPIO connection to pad12. value. */ - ADC_SL4CFG_CHSEL4_DF0 = 10, /*!< DF0 : differential external GPIO connections to pad12(N) and - pad13(P). value. */ - ADC_SL4CFG_CHSEL4_DF1 = 11, /*!< DF1 : differential external GPIO connections to pad15(N) and - pad14(P). value. */ - ADC_SL4CFG_CHSEL4_TEMP = 12, /*!< TEMP : internal temperature sensor. value. */ - ADC_SL4CFG_CHSEL4_BATT = 13, /*!< BATT : internal voltage divide-by-3 connection. value. */ - ADC_SL4CFG_CHSEL4_VSS = 14, /*!< VSS : Input VSS value. */ -} ADC_SL4CFG_CHSEL4_Enum; - -/* ================================================ ADC SL4CFG WCEN4 [1..1] ================================================ */ -typedef enum { /*!< ADC_SL4CFG_WCEN4 */ - ADC_SL4CFG_WCEN4_WCEN = 1, /*!< WCEN : Enable the window compare for slot 4. value. */ -} ADC_SL4CFG_WCEN4_Enum; - -/* ================================================ ADC SL4CFG SLEN4 [0..0] ================================================ */ -typedef enum { /*!< ADC_SL4CFG_SLEN4 */ - ADC_SL4CFG_SLEN4_SLEN = 1, /*!< SLEN : Enable slot 4 for ADC conversions. value. */ -} ADC_SL4CFG_SLEN4_Enum; - -/* ======================================================== SL5CFG ========================================================= */ -/* ============================================== ADC SL5CFG ADSEL5 [24..26] =============================================== */ -typedef enum { /*!< ADC_SL5CFG_ADSEL5 */ - ADC_SL5CFG_ADSEL5_AVG_1_MSRMT = 0, /*!< AVG_1_MSRMT : Average in 1 measurement in the accumulate divide - module for this slot. value. */ - ADC_SL5CFG_ADSEL5_AVG_2_MSRMTS = 1, /*!< AVG_2_MSRMTS : Average in 2 measurements in the accumulate divide - module for this slot. value. */ - ADC_SL5CFG_ADSEL5_AVG_4_MSRMTS = 2, /*!< AVG_4_MSRMTS : Average in 4 measurements in the accumulate divide - module for this slot. value. */ - ADC_SL5CFG_ADSEL5_AVG_8_MSRMT = 3, /*!< AVG_8_MSRMT : Average in 8 measurements in the accumulate divide - module for this slot. value. */ - ADC_SL5CFG_ADSEL5_AVG_16_MSRMTS = 4, /*!< AVG_16_MSRMTS : Average in 16 measurements in the accumulate - divide module for this slot. value. */ - ADC_SL5CFG_ADSEL5_AVG_32_MSRMTS = 5, /*!< AVG_32_MSRMTS : Average in 32 measurements in the accumulate - divide module for this slot. value. */ - ADC_SL5CFG_ADSEL5_AVG_64_MSRMTS = 6, /*!< AVG_64_MSRMTS : Average in 64 measurements in the accumulate - divide module for this slot. value. */ - ADC_SL5CFG_ADSEL5_AVG_128_MSRMTS = 7, /*!< AVG_128_MSRMTS : Average in 128 measurements in the accumulate - divide module for this slot. value. */ -} ADC_SL5CFG_ADSEL5_Enum; - -/* ============================================== ADC SL5CFG PRMODE5 [16..17] ============================================== */ -typedef enum { /*!< ADC_SL5CFG_PRMODE5 */ - ADC_SL5CFG_PRMODE5_P14B = 0, /*!< P14B : 14-bit precision mode value. */ - ADC_SL5CFG_PRMODE5_P12B = 1, /*!< P12B : 12-bit precision mode value. */ - ADC_SL5CFG_PRMODE5_P10B = 2, /*!< P10B : 10-bit precision mode value. */ - ADC_SL5CFG_PRMODE5_P8B = 3, /*!< P8B : 8-bit precision mode value. */ -} ADC_SL5CFG_PRMODE5_Enum; - -/* =============================================== ADC SL5CFG CHSEL5 [8..11] =============================================== */ -typedef enum { /*!< ADC_SL5CFG_CHSEL5 */ - ADC_SL5CFG_CHSEL5_SE0 = 0, /*!< SE0 : single ended external GPIO connection to pad16. value. */ - ADC_SL5CFG_CHSEL5_SE1 = 1, /*!< SE1 : single ended external GPIO connection to pad29. value. */ - ADC_SL5CFG_CHSEL5_SE2 = 2, /*!< SE2 : single ended external GPIO connection to pad11. value. */ - ADC_SL5CFG_CHSEL5_SE3 = 3, /*!< SE3 : single ended external GPIO connection to pad31. value. */ - ADC_SL5CFG_CHSEL5_SE4 = 4, /*!< SE4 : single ended external GPIO connection to pad32. value. */ - ADC_SL5CFG_CHSEL5_SE5 = 5, /*!< SE5 : single ended external GPIO connection to pad33. value. */ - ADC_SL5CFG_CHSEL5_SE6 = 6, /*!< SE6 : single ended external GPIO connection to pad34. value. */ - ADC_SL5CFG_CHSEL5_SE7 = 7, /*!< SE7 : single ended external GPIO connection to pad35. value. */ - ADC_SL5CFG_CHSEL5_SE8 = 8, /*!< SE8 : single ended external GPIO connection to pad13. value. */ - ADC_SL5CFG_CHSEL5_SE9 = 9, /*!< SE9 : single ended external GPIO connection to pad12. value. */ - ADC_SL5CFG_CHSEL5_DF0 = 10, /*!< DF0 : differential external GPIO connections to pad12(N) and - pad13(P). value. */ - ADC_SL5CFG_CHSEL5_DF1 = 11, /*!< DF1 : differential external GPIO connections to pad15(N) and - pad14(P). value. */ - ADC_SL5CFG_CHSEL5_TEMP = 12, /*!< TEMP : internal temperature sensor. value. */ - ADC_SL5CFG_CHSEL5_BATT = 13, /*!< BATT : internal voltage divide-by-3 connection. value. */ - ADC_SL5CFG_CHSEL5_VSS = 14, /*!< VSS : Input VSS value. */ -} ADC_SL5CFG_CHSEL5_Enum; - -/* ================================================ ADC SL5CFG WCEN5 [1..1] ================================================ */ -typedef enum { /*!< ADC_SL5CFG_WCEN5 */ - ADC_SL5CFG_WCEN5_WCEN = 1, /*!< WCEN : Enable the window compare for slot 5. value. */ -} ADC_SL5CFG_WCEN5_Enum; - -/* ================================================ ADC SL5CFG SLEN5 [0..0] ================================================ */ -typedef enum { /*!< ADC_SL5CFG_SLEN5 */ - ADC_SL5CFG_SLEN5_SLEN = 1, /*!< SLEN : Enable slot 5 for ADC conversions. value. */ -} ADC_SL5CFG_SLEN5_Enum; - -/* ======================================================== SL6CFG ========================================================= */ -/* ============================================== ADC SL6CFG ADSEL6 [24..26] =============================================== */ -typedef enum { /*!< ADC_SL6CFG_ADSEL6 */ - ADC_SL6CFG_ADSEL6_AVG_1_MSRMT = 0, /*!< AVG_1_MSRMT : Average in 1 measurement in the accumulate divide - module for this slot. value. */ - ADC_SL6CFG_ADSEL6_AVG_2_MSRMTS = 1, /*!< AVG_2_MSRMTS : Average in 2 measurements in the accumulate divide - module for this slot. value. */ - ADC_SL6CFG_ADSEL6_AVG_4_MSRMTS = 2, /*!< AVG_4_MSRMTS : Average in 4 measurements in the accumulate divide - module for this slot. value. */ - ADC_SL6CFG_ADSEL6_AVG_8_MSRMT = 3, /*!< AVG_8_MSRMT : Average in 8 measurements in the accumulate divide - module for this slot. value. */ - ADC_SL6CFG_ADSEL6_AVG_16_MSRMTS = 4, /*!< AVG_16_MSRMTS : Average in 16 measurements in the accumulate - divide module for this slot. value. */ - ADC_SL6CFG_ADSEL6_AVG_32_MSRMTS = 5, /*!< AVG_32_MSRMTS : Average in 32 measurements in the accumulate - divide module for this slot. value. */ - ADC_SL6CFG_ADSEL6_AVG_64_MSRMTS = 6, /*!< AVG_64_MSRMTS : Average in 64 measurements in the accumulate - divide module for this slot. value. */ - ADC_SL6CFG_ADSEL6_AVG_128_MSRMTS = 7, /*!< AVG_128_MSRMTS : Average in 128 measurements in the accumulate - divide module for this slot. value. */ -} ADC_SL6CFG_ADSEL6_Enum; - -/* ============================================== ADC SL6CFG PRMODE6 [16..17] ============================================== */ -typedef enum { /*!< ADC_SL6CFG_PRMODE6 */ - ADC_SL6CFG_PRMODE6_P14B = 0, /*!< P14B : 14-bit precision mode value. */ - ADC_SL6CFG_PRMODE6_P12B = 1, /*!< P12B : 12-bit precision mode value. */ - ADC_SL6CFG_PRMODE6_P10B = 2, /*!< P10B : 10-bit precision mode value. */ - ADC_SL6CFG_PRMODE6_P8B = 3, /*!< P8B : 8-bit precision mode value. */ -} ADC_SL6CFG_PRMODE6_Enum; - -/* =============================================== ADC SL6CFG CHSEL6 [8..11] =============================================== */ -typedef enum { /*!< ADC_SL6CFG_CHSEL6 */ - ADC_SL6CFG_CHSEL6_SE0 = 0, /*!< SE0 : single ended external GPIO connection to pad16. value. */ - ADC_SL6CFG_CHSEL6_SE1 = 1, /*!< SE1 : single ended external GPIO connection to pad29. value. */ - ADC_SL6CFG_CHSEL6_SE2 = 2, /*!< SE2 : single ended external GPIO connection to pad11. value. */ - ADC_SL6CFG_CHSEL6_SE3 = 3, /*!< SE3 : single ended external GPIO connection to pad31. value. */ - ADC_SL6CFG_CHSEL6_SE4 = 4, /*!< SE4 : single ended external GPIO connection to pad32. value. */ - ADC_SL6CFG_CHSEL6_SE5 = 5, /*!< SE5 : single ended external GPIO connection to pad33. value. */ - ADC_SL6CFG_CHSEL6_SE6 = 6, /*!< SE6 : single ended external GPIO connection to pad34. value. */ - ADC_SL6CFG_CHSEL6_SE7 = 7, /*!< SE7 : single ended external GPIO connection to pad35. value. */ - ADC_SL6CFG_CHSEL6_SE8 = 8, /*!< SE8 : single ended external GPIO connection to pad13. value. */ - ADC_SL6CFG_CHSEL6_SE9 = 9, /*!< SE9 : single ended external GPIO connection to pad12. value. */ - ADC_SL6CFG_CHSEL6_DF0 = 10, /*!< DF0 : differential external GPIO connections to pad12(N) and - pad13(P). value. */ - ADC_SL6CFG_CHSEL6_DF1 = 11, /*!< DF1 : differential external GPIO connections to pad15(N) and - pad14(P). value. */ - ADC_SL6CFG_CHSEL6_TEMP = 12, /*!< TEMP : internal temperature sensor. value. */ - ADC_SL6CFG_CHSEL6_BATT = 13, /*!< BATT : internal voltage divide-by-3 connection. value. */ - ADC_SL6CFG_CHSEL6_VSS = 14, /*!< VSS : Input VSS value. */ -} ADC_SL6CFG_CHSEL6_Enum; - -/* ================================================ ADC SL6CFG WCEN6 [1..1] ================================================ */ -typedef enum { /*!< ADC_SL6CFG_WCEN6 */ - ADC_SL6CFG_WCEN6_WCEN = 1, /*!< WCEN : Enable the window compare for slot 6. value. */ -} ADC_SL6CFG_WCEN6_Enum; - -/* ================================================ ADC SL6CFG SLEN6 [0..0] ================================================ */ -typedef enum { /*!< ADC_SL6CFG_SLEN6 */ - ADC_SL6CFG_SLEN6_SLEN = 1, /*!< SLEN : Enable slot 6 for ADC conversions. value. */ -} ADC_SL6CFG_SLEN6_Enum; - -/* ======================================================== SL7CFG ========================================================= */ -/* ============================================== ADC SL7CFG ADSEL7 [24..26] =============================================== */ -typedef enum { /*!< ADC_SL7CFG_ADSEL7 */ - ADC_SL7CFG_ADSEL7_AVG_1_MSRMT = 0, /*!< AVG_1_MSRMT : Average in 1 measurement in the accumulate divide - module for this slot. value. */ - ADC_SL7CFG_ADSEL7_AVG_2_MSRMTS = 1, /*!< AVG_2_MSRMTS : Average in 2 measurements in the accumulate divide - module for this slot. value. */ - ADC_SL7CFG_ADSEL7_AVG_4_MSRMTS = 2, /*!< AVG_4_MSRMTS : Average in 4 measurements in the accumulate divide - module for this slot. value. */ - ADC_SL7CFG_ADSEL7_AVG_8_MSRMT = 3, /*!< AVG_8_MSRMT : Average in 8 measurements in the accumulate divide - module for this slot. value. */ - ADC_SL7CFG_ADSEL7_AVG_16_MSRMTS = 4, /*!< AVG_16_MSRMTS : Average in 16 measurements in the accumulate - divide module for this slot. value. */ - ADC_SL7CFG_ADSEL7_AVG_32_MSRMTS = 5, /*!< AVG_32_MSRMTS : Average in 32 measurements in the accumulate - divide module for this slot. value. */ - ADC_SL7CFG_ADSEL7_AVG_64_MSRMTS = 6, /*!< AVG_64_MSRMTS : Average in 64 measurements in the accumulate - divide module for this slot. value. */ - ADC_SL7CFG_ADSEL7_AVG_128_MSRMTS = 7, /*!< AVG_128_MSRMTS : Average in 128 measurements in the accumulate - divide module for this slot. value. */ -} ADC_SL7CFG_ADSEL7_Enum; - -/* ============================================== ADC SL7CFG PRMODE7 [16..17] ============================================== */ -typedef enum { /*!< ADC_SL7CFG_PRMODE7 */ - ADC_SL7CFG_PRMODE7_P14B = 0, /*!< P14B : 14-bit precision mode value. */ - ADC_SL7CFG_PRMODE7_P12B = 1, /*!< P12B : 12-bit precision mode value. */ - ADC_SL7CFG_PRMODE7_P10B = 2, /*!< P10B : 10-bit precision mode value. */ - ADC_SL7CFG_PRMODE7_P8B = 3, /*!< P8B : 8-bit precision mode value. */ -} ADC_SL7CFG_PRMODE7_Enum; - -/* =============================================== ADC SL7CFG CHSEL7 [8..11] =============================================== */ -typedef enum { /*!< ADC_SL7CFG_CHSEL7 */ - ADC_SL7CFG_CHSEL7_SE0 = 0, /*!< SE0 : single ended external GPIO connection to pad16. value. */ - ADC_SL7CFG_CHSEL7_SE1 = 1, /*!< SE1 : single ended external GPIO connection to pad29. value. */ - ADC_SL7CFG_CHSEL7_SE2 = 2, /*!< SE2 : single ended external GPIO connection to pad11. value. */ - ADC_SL7CFG_CHSEL7_SE3 = 3, /*!< SE3 : single ended external GPIO connection to pad31. value. */ - ADC_SL7CFG_CHSEL7_SE4 = 4, /*!< SE4 : single ended external GPIO connection to pad32. value. */ - ADC_SL7CFG_CHSEL7_SE5 = 5, /*!< SE5 : single ended external GPIO connection to pad33. value. */ - ADC_SL7CFG_CHSEL7_SE6 = 6, /*!< SE6 : single ended external GPIO connection to pad34. value. */ - ADC_SL7CFG_CHSEL7_SE7 = 7, /*!< SE7 : single ended external GPIO connection to pad35. value. */ - ADC_SL7CFG_CHSEL7_SE8 = 8, /*!< SE8 : single ended external GPIO connection to pad13. value. */ - ADC_SL7CFG_CHSEL7_SE9 = 9, /*!< SE9 : single ended external GPIO connection to pad12. value. */ - ADC_SL7CFG_CHSEL7_DF0 = 10, /*!< DF0 : differential external GPIO connections to pad12(N) and - pad13(P). value. */ - ADC_SL7CFG_CHSEL7_DF1 = 11, /*!< DF1 : differential external GPIO connections to pad15(N) and - pad14(P). value. */ - ADC_SL7CFG_CHSEL7_TEMP = 12, /*!< TEMP : internal temperature sensor. value. */ - ADC_SL7CFG_CHSEL7_BATT = 13, /*!< BATT : internal voltage divide-by-3 connection. value. */ - ADC_SL7CFG_CHSEL7_VSS = 14, /*!< VSS : Input VSS value. */ -} ADC_SL7CFG_CHSEL7_Enum; - -/* ================================================ ADC SL7CFG WCEN7 [1..1] ================================================ */ -typedef enum { /*!< ADC_SL7CFG_WCEN7 */ - ADC_SL7CFG_WCEN7_WCEN = 1, /*!< WCEN : Enable the window compare for slot 7. value. */ -} ADC_SL7CFG_WCEN7_Enum; - -/* ================================================ ADC SL7CFG SLEN7 [0..0] ================================================ */ -typedef enum { /*!< ADC_SL7CFG_SLEN7 */ - ADC_SL7CFG_SLEN7_SLEN = 1, /*!< SLEN : Enable slot 7 for ADC conversions. value. */ -} ADC_SL7CFG_SLEN7_Enum; - -/* ========================================================= WULIM ========================================================= */ -/* ========================================================= WLLIM ========================================================= */ -/* ======================================================== SCWLIM ========================================================= */ -/* ========================================================= FIFO ========================================================== */ -/* ======================================================== FIFOPR ========================================================= */ -/* ========================================================= INTEN ========================================================= */ -/* ================================================= ADC INTEN DERR [7..7] ================================================= */ -typedef enum { /*!< ADC_INTEN_DERR */ - ADC_INTEN_DERR_DMAERROR = 1, /*!< DMAERROR : DMA Error Condition Occurred value. */ -} ADC_INTEN_DERR_Enum; - -/* ================================================= ADC INTEN DCMP [6..6] ================================================= */ -typedef enum { /*!< ADC_INTEN_DCMP */ - ADC_INTEN_DCMP_DMACOMPLETE = 1, /*!< DMACOMPLETE : DMA Completed a transfer value. */ -} ADC_INTEN_DCMP_Enum; - -/* ================================================ ADC INTEN WCINC [5..5] ================================================= */ -typedef enum { /*!< ADC_INTEN_WCINC */ - ADC_INTEN_WCINC_WCINCINT = 1, /*!< WCINCINT : Window comparitor voltage incursion interrupt. value. */ -} ADC_INTEN_WCINC_Enum; - -/* ================================================ ADC INTEN WCEXC [4..4] ================================================= */ -typedef enum { /*!< ADC_INTEN_WCEXC */ - ADC_INTEN_WCEXC_WCEXCINT = 1, /*!< WCEXCINT : Window comparitor voltage excursion interrupt. value. */ -} ADC_INTEN_WCEXC_Enum; - -/* =============================================== ADC INTEN FIFOOVR2 [3..3] =============================================== */ -typedef enum { /*!< ADC_INTEN_FIFOOVR2 */ - ADC_INTEN_FIFOOVR2_FIFOFULLINT = 1, /*!< FIFOFULLINT : FIFO 100 percent full interrupt. value. */ -} ADC_INTEN_FIFOOVR2_Enum; - -/* =============================================== ADC INTEN FIFOOVR1 [2..2] =============================================== */ -typedef enum { /*!< ADC_INTEN_FIFOOVR1 */ - ADC_INTEN_FIFOOVR1_FIFO75INT = 1, /*!< FIFO75INT : FIFO 75 percent full interrupt. value. */ -} ADC_INTEN_FIFOOVR1_Enum; - -/* ================================================ ADC INTEN SCNCMP [1..1] ================================================ */ -typedef enum { /*!< ADC_INTEN_SCNCMP */ - ADC_INTEN_SCNCMP_SCNCMPINT = 1, /*!< SCNCMPINT : ADC scan complete interrupt. value. */ -} ADC_INTEN_SCNCMP_Enum; - -/* ================================================ ADC INTEN CNVCMP [0..0] ================================================ */ -typedef enum { /*!< ADC_INTEN_CNVCMP */ - ADC_INTEN_CNVCMP_CNVCMPINT = 1, /*!< CNVCMPINT : ADC conversion complete interrupt. value. */ -} ADC_INTEN_CNVCMP_Enum; - -/* ======================================================== INTSTAT ======================================================== */ -/* ================================================ ADC INTSTAT DERR [7..7] ================================================ */ -typedef enum { /*!< ADC_INTSTAT_DERR */ - ADC_INTSTAT_DERR_DMAERROR = 1, /*!< DMAERROR : DMA Error Condition Occurred value. */ -} ADC_INTSTAT_DERR_Enum; - -/* ================================================ ADC INTSTAT DCMP [6..6] ================================================ */ -typedef enum { /*!< ADC_INTSTAT_DCMP */ - ADC_INTSTAT_DCMP_DMACOMPLETE = 1, /*!< DMACOMPLETE : DMA Completed a transfer value. */ -} ADC_INTSTAT_DCMP_Enum; - -/* =============================================== ADC INTSTAT WCINC [5..5] ================================================ */ -typedef enum { /*!< ADC_INTSTAT_WCINC */ - ADC_INTSTAT_WCINC_WCINCINT = 1, /*!< WCINCINT : Window comparitor voltage incursion interrupt. value. */ -} ADC_INTSTAT_WCINC_Enum; - -/* =============================================== ADC INTSTAT WCEXC [4..4] ================================================ */ -typedef enum { /*!< ADC_INTSTAT_WCEXC */ - ADC_INTSTAT_WCEXC_WCEXCINT = 1, /*!< WCEXCINT : Window comparitor voltage excursion interrupt. value. */ -} ADC_INTSTAT_WCEXC_Enum; - -/* ============================================== ADC INTSTAT FIFOOVR2 [3..3] ============================================== */ -typedef enum { /*!< ADC_INTSTAT_FIFOOVR2 */ - ADC_INTSTAT_FIFOOVR2_FIFOFULLINT = 1, /*!< FIFOFULLINT : FIFO 100 percent full interrupt. value. */ -} ADC_INTSTAT_FIFOOVR2_Enum; - -/* ============================================== ADC INTSTAT FIFOOVR1 [2..2] ============================================== */ -typedef enum { /*!< ADC_INTSTAT_FIFOOVR1 */ - ADC_INTSTAT_FIFOOVR1_FIFO75INT = 1, /*!< FIFO75INT : FIFO 75 percent full interrupt. value. */ -} ADC_INTSTAT_FIFOOVR1_Enum; - -/* =============================================== ADC INTSTAT SCNCMP [1..1] =============================================== */ -typedef enum { /*!< ADC_INTSTAT_SCNCMP */ - ADC_INTSTAT_SCNCMP_SCNCMPINT = 1, /*!< SCNCMPINT : ADC scan complete interrupt. value. */ -} ADC_INTSTAT_SCNCMP_Enum; - -/* =============================================== ADC INTSTAT CNVCMP [0..0] =============================================== */ -typedef enum { /*!< ADC_INTSTAT_CNVCMP */ - ADC_INTSTAT_CNVCMP_CNVCMPINT = 1, /*!< CNVCMPINT : ADC conversion complete interrupt. value. */ -} ADC_INTSTAT_CNVCMP_Enum; - -/* ======================================================== INTCLR ========================================================= */ -/* ================================================ ADC INTCLR DERR [7..7] ================================================= */ -typedef enum { /*!< ADC_INTCLR_DERR */ - ADC_INTCLR_DERR_DMAERROR = 1, /*!< DMAERROR : DMA Error Condition Occurred value. */ -} ADC_INTCLR_DERR_Enum; - -/* ================================================ ADC INTCLR DCMP [6..6] ================================================= */ -typedef enum { /*!< ADC_INTCLR_DCMP */ - ADC_INTCLR_DCMP_DMACOMPLETE = 1, /*!< DMACOMPLETE : DMA Completed a transfer value. */ -} ADC_INTCLR_DCMP_Enum; - -/* ================================================ ADC INTCLR WCINC [5..5] ================================================ */ -typedef enum { /*!< ADC_INTCLR_WCINC */ - ADC_INTCLR_WCINC_WCINCINT = 1, /*!< WCINCINT : Window comparitor voltage incursion interrupt. value. */ -} ADC_INTCLR_WCINC_Enum; - -/* ================================================ ADC INTCLR WCEXC [4..4] ================================================ */ -typedef enum { /*!< ADC_INTCLR_WCEXC */ - ADC_INTCLR_WCEXC_WCEXCINT = 1, /*!< WCEXCINT : Window comparitor voltage excursion interrupt. value. */ -} ADC_INTCLR_WCEXC_Enum; - -/* ============================================== ADC INTCLR FIFOOVR2 [3..3] =============================================== */ -typedef enum { /*!< ADC_INTCLR_FIFOOVR2 */ - ADC_INTCLR_FIFOOVR2_FIFOFULLINT = 1, /*!< FIFOFULLINT : FIFO 100 percent full interrupt. value. */ -} ADC_INTCLR_FIFOOVR2_Enum; - -/* ============================================== ADC INTCLR FIFOOVR1 [2..2] =============================================== */ -typedef enum { /*!< ADC_INTCLR_FIFOOVR1 */ - ADC_INTCLR_FIFOOVR1_FIFO75INT = 1, /*!< FIFO75INT : FIFO 75 percent full interrupt. value. */ -} ADC_INTCLR_FIFOOVR1_Enum; - -/* =============================================== ADC INTCLR SCNCMP [1..1] ================================================ */ -typedef enum { /*!< ADC_INTCLR_SCNCMP */ - ADC_INTCLR_SCNCMP_SCNCMPINT = 1, /*!< SCNCMPINT : ADC scan complete interrupt. value. */ -} ADC_INTCLR_SCNCMP_Enum; - -/* =============================================== ADC INTCLR CNVCMP [0..0] ================================================ */ -typedef enum { /*!< ADC_INTCLR_CNVCMP */ - ADC_INTCLR_CNVCMP_CNVCMPINT = 1, /*!< CNVCMPINT : ADC conversion complete interrupt. value. */ -} ADC_INTCLR_CNVCMP_Enum; - -/* ======================================================== INTSET ========================================================= */ -/* ================================================ ADC INTSET DERR [7..7] ================================================= */ -typedef enum { /*!< ADC_INTSET_DERR */ - ADC_INTSET_DERR_DMAERROR = 1, /*!< DMAERROR : DMA Error Condition Occurred value. */ -} ADC_INTSET_DERR_Enum; - -/* ================================================ ADC INTSET DCMP [6..6] ================================================= */ -typedef enum { /*!< ADC_INTSET_DCMP */ - ADC_INTSET_DCMP_DMACOMPLETE = 1, /*!< DMACOMPLETE : DMA Completed a transfer value. */ -} ADC_INTSET_DCMP_Enum; - -/* ================================================ ADC INTSET WCINC [5..5] ================================================ */ -typedef enum { /*!< ADC_INTSET_WCINC */ - ADC_INTSET_WCINC_WCINCINT = 1, /*!< WCINCINT : Window comparitor voltage incursion interrupt. value. */ -} ADC_INTSET_WCINC_Enum; - -/* ================================================ ADC INTSET WCEXC [4..4] ================================================ */ -typedef enum { /*!< ADC_INTSET_WCEXC */ - ADC_INTSET_WCEXC_WCEXCINT = 1, /*!< WCEXCINT : Window comparitor voltage excursion interrupt. value. */ -} ADC_INTSET_WCEXC_Enum; - -/* ============================================== ADC INTSET FIFOOVR2 [3..3] =============================================== */ -typedef enum { /*!< ADC_INTSET_FIFOOVR2 */ - ADC_INTSET_FIFOOVR2_FIFOFULLINT = 1, /*!< FIFOFULLINT : FIFO 100 percent full interrupt. value. */ -} ADC_INTSET_FIFOOVR2_Enum; - -/* ============================================== ADC INTSET FIFOOVR1 [2..2] =============================================== */ -typedef enum { /*!< ADC_INTSET_FIFOOVR1 */ - ADC_INTSET_FIFOOVR1_FIFO75INT = 1, /*!< FIFO75INT : FIFO 75 percent full interrupt. value. */ -} ADC_INTSET_FIFOOVR1_Enum; - -/* =============================================== ADC INTSET SCNCMP [1..1] ================================================ */ -typedef enum { /*!< ADC_INTSET_SCNCMP */ - ADC_INTSET_SCNCMP_SCNCMPINT = 1, /*!< SCNCMPINT : ADC scan complete interrupt. value. */ -} ADC_INTSET_SCNCMP_Enum; - -/* =============================================== ADC INTSET CNVCMP [0..0] ================================================ */ -typedef enum { /*!< ADC_INTSET_CNVCMP */ - ADC_INTSET_CNVCMP_CNVCMPINT = 1, /*!< CNVCMPINT : ADC conversion complete interrupt. value. */ -} ADC_INTSET_CNVCMP_Enum; - -/* ======================================================= DMATRIGEN ======================================================= */ -/* ====================================================== DMATRIGSTAT ====================================================== */ -/* ======================================================== DMACFG ========================================================= */ -/* ============================================== ADC DMACFG DMAMSK [17..17] =============================================== */ -typedef enum { /*!< ADC_DMACFG_DMAMSK */ - ADC_DMACFG_DMAMSK_DIS = 0, /*!< DIS : FIFO Contents are copied directly to memory without modification. - value. */ - ADC_DMACFG_DMAMSK_EN = 1, /*!< EN : Only the FIFODATA contents are copied to memory on DMA - transfers. The SLOTNUM and FIFOCNT contents are cleared - to zero. value. */ -} ADC_DMACFG_DMAMSK_Enum; - -/* ============================================ ADC DMACFG DMAHONSTAT [16..16] ============================================= */ -typedef enum { /*!< ADC_DMACFG_DMAHONSTAT */ - ADC_DMACFG_DMAHONSTAT_DIS = 0, /*!< DIS : ADC conversions will continue regardless of DMA status - register value. */ - ADC_DMACFG_DMAHONSTAT_EN = 1, /*!< EN : ADC conversions will not progress if DMAERR or DMACPL bits - in DMA status register are set. value. */ -} ADC_DMACFG_DMAHONSTAT_Enum; - -/* ============================================== ADC DMACFG DMADYNPRI [9..9] ============================================== */ -typedef enum { /*!< ADC_DMACFG_DMADYNPRI */ - ADC_DMACFG_DMADYNPRI_DIS = 0, /*!< DIS : Disable dynamic priority (use DMAPRI setting only) value. */ - ADC_DMACFG_DMADYNPRI_EN = 1, /*!< EN : Enable dynamic priority value. */ -} ADC_DMACFG_DMADYNPRI_Enum; - -/* =============================================== ADC DMACFG DMAPRI [8..8] ================================================ */ -typedef enum { /*!< ADC_DMACFG_DMAPRI */ - ADC_DMACFG_DMAPRI_LOW = 0, /*!< LOW : Low Priority (service as best effort) value. */ - ADC_DMACFG_DMAPRI_HIGH = 1, /*!< HIGH : High Priority (service immediately) value. */ -} ADC_DMACFG_DMAPRI_Enum; - -/* =============================================== ADC DMACFG DMADIR [2..2] ================================================ */ -typedef enum { /*!< ADC_DMACFG_DMADIR */ - ADC_DMACFG_DMADIR_P2M = 0, /*!< P2M : Peripheral to Memory (SRAM) transaction value. */ - ADC_DMACFG_DMADIR_M2P = 1, /*!< M2P : Memory to Peripheral transaction value. */ -} ADC_DMACFG_DMADIR_Enum; - -/* ================================================ ADC DMACFG DMAEN [0..0] ================================================ */ -typedef enum { /*!< ADC_DMACFG_DMAEN */ - ADC_DMACFG_DMAEN_DIS = 0, /*!< DIS : Disable DMA Function value. */ - ADC_DMACFG_DMAEN_EN = 1, /*!< EN : Enable DMA Function value. */ -} ADC_DMACFG_DMAEN_Enum; - -/* ====================================================== DMATOTCOUNT ====================================================== */ -/* ====================================================== DMATARGADDR ====================================================== */ -/* ======================================================== DMASTAT ======================================================== */ - - -/* =========================================================================================================================== */ -/* ================ APBDMA ================ */ -/* =========================================================================================================================== */ - -/* ======================================================== BBVALUE ======================================================== */ -/* ====================================================== BBSETCLEAR ======================================================= */ -/* ======================================================== BBINPUT ======================================================== */ -/* ======================================================= DEBUGDATA ======================================================= */ -/* ========================================================= DEBUG ========================================================= */ -/* ============================================== APBDMA DEBUG DEBUGEN [0..3] ============================================== */ -typedef enum { /*!< APBDMA_DEBUG_DEBUGEN */ - APBDMA_DEBUG_DEBUGEN_OFF = 0, /*!< OFF : Debug Disabled value. */ - APBDMA_DEBUG_DEBUGEN_ARB = 1, /*!< ARB : Debug Arb values value. */ -} APBDMA_DEBUG_DEBUGEN_Enum; - - - -/* =========================================================================================================================== */ -/* ================ BLEIF ================ */ -/* =========================================================================================================================== */ - -/* ========================================================= FIFO ========================================================== */ -/* ======================================================== FIFOPTR ======================================================== */ -/* ======================================================== FIFOTHR ======================================================== */ -/* ======================================================== FIFOPOP ======================================================== */ -/* ======================================================= FIFOPUSH ======================================================== */ -/* ======================================================= FIFOCTRL ======================================================== */ -/* ======================================================== FIFOLOC ======================================================== */ -/* ======================================================== CLKCFG ========================================================= */ -/* =============================================== BLEIF CLKCFG FSEL [8..10] =============================================== */ -typedef enum { /*!< BLEIF_CLKCFG_FSEL */ - BLEIF_CLKCFG_FSEL_MIN_PWR = 0, /*!< MIN_PWR : Selects the minimum power clock. This setting should - be used whenever the IOM is not active. value. */ - BLEIF_CLKCFG_FSEL_HFRC = 1, /*!< HFRC : Selects the HFRC as the input clock. value. */ - BLEIF_CLKCFG_FSEL_HFRC_DIV2 = 2, /*!< HFRC_DIV2 : Selects the HFRC / 2 as the input clock. value. */ - BLEIF_CLKCFG_FSEL_HFRC_DIV4 = 3, /*!< HFRC_DIV4 : Selects the HFRC / 4 as the input clock. value. */ - BLEIF_CLKCFG_FSEL_HFRC_DIV8 = 4, /*!< HFRC_DIV8 : Selects the HFRC / 8 as the input clock. value. */ - BLEIF_CLKCFG_FSEL_HFRC_DIV16 = 5, /*!< HFRC_DIV16 : Selects the HFRC / 16 as the input clock. value. */ - BLEIF_CLKCFG_FSEL_HFRC_DIV32 = 6, /*!< HFRC_DIV32 : Selects the HFRC / 32 as the input clock. value. */ - BLEIF_CLKCFG_FSEL_HFRC_DIV64 = 7, /*!< HFRC_DIV64 : Selects the HFRC / 64 as the input clock. value. */ -} BLEIF_CLKCFG_FSEL_Enum; - -/* ========================================================== CMD ========================================================== */ -/* ================================================= BLEIF CMD CMD [0..4] ================================================== */ -typedef enum { /*!< BLEIF_CMD_CMD */ - BLEIF_CMD_CMD_WRITE = 1, /*!< WRITE : Write command using count of offset bytes specified - in the OFFSETCNT field value. */ - BLEIF_CMD_CMD_READ = 2, /*!< READ : Read command using count of offset bytes specified in - the OFFSETCNT field value. */ -} BLEIF_CMD_CMD_Enum; - -/* ======================================================== CMDRPT ========================================================= */ -/* ======================================================= OFFSETHI ======================================================== */ -/* ======================================================== CMDSTAT ======================================================== */ -/* ============================================= BLEIF CMDSTAT CMDSTAT [5..7] ============================================== */ -typedef enum { /*!< BLEIF_CMDSTAT_CMDSTAT */ - BLEIF_CMDSTAT_CMDSTAT_ERR = 1, /*!< ERR : Error encountered with command value. */ - BLEIF_CMDSTAT_CMDSTAT_ACTIVE = 2, /*!< ACTIVE : Actively processing command value. */ - BLEIF_CMDSTAT_CMDSTAT_IDLE = 4, /*!< IDLE : Idle state, no active command, no error value. */ - BLEIF_CMDSTAT_CMDSTAT_WAIT = 6, /*!< WAIT : Command in progress, but waiting on data from host value. */ -} BLEIF_CMDSTAT_CMDSTAT_Enum; - -/* ========================================================= INTEN ========================================================= */ -/* ======================================================== INTSTAT ======================================================== */ -/* ======================================================== INTCLR ========================================================= */ -/* ======================================================== INTSET ========================================================= */ -/* ======================================================= DMATRIGEN ======================================================= */ -/* ====================================================== DMATRIGSTAT ====================================================== */ -/* ======================================================== DMACFG ========================================================= */ -/* ============================================== BLEIF DMACFG DPWROFF [9..9] ============================================== */ -typedef enum { /*!< BLEIF_DMACFG_DPWROFF */ - BLEIF_DMACFG_DPWROFF_DIS = 0, /*!< DIS : Power off disabled value. */ - BLEIF_DMACFG_DPWROFF_EN = 1, /*!< EN : Power off enabled value. */ -} BLEIF_DMACFG_DPWROFF_Enum; - -/* ============================================== BLEIF DMACFG DMAPRI [8..8] =============================================== */ -typedef enum { /*!< BLEIF_DMACFG_DMAPRI */ - BLEIF_DMACFG_DMAPRI_LOW = 0, /*!< LOW : Low Priority (service as best effort) value. */ - BLEIF_DMACFG_DMAPRI_HIGH = 1, /*!< HIGH : High Priority (service immediately) value. */ -} BLEIF_DMACFG_DMAPRI_Enum; - -/* ============================================== BLEIF DMACFG DMADIR [1..1] =============================================== */ -typedef enum { /*!< BLEIF_DMACFG_DMADIR */ - BLEIF_DMACFG_DMADIR_P2M = 0, /*!< P2M : Peripheral to Memory (SRAM) transaction. To be set when - doing IOM read operations, ie reading data from external - devices. value. */ - BLEIF_DMACFG_DMADIR_M2P = 1, /*!< M2P : Memory to Peripheral transaction. To be set when doing - IOM write operations, ie writing data to external devices. - value. */ -} BLEIF_DMACFG_DMADIR_Enum; - -/* =============================================== BLEIF DMACFG DMAEN [0..0] =============================================== */ -typedef enum { /*!< BLEIF_DMACFG_DMAEN */ - BLEIF_DMACFG_DMAEN_DIS = 0, /*!< DIS : Disable DMA Function value. */ - BLEIF_DMACFG_DMAEN_EN = 1, /*!< EN : Enable DMA Function value. */ -} BLEIF_DMACFG_DMAEN_Enum; - -/* ====================================================== DMATOTCOUNT ====================================================== */ -/* ====================================================== DMATARGADDR ====================================================== */ -/* ======================================================== DMASTAT ======================================================== */ -/* ========================================================= CQCFG ========================================================= */ -/* =============================================== BLEIF CQCFG CQPRI [1..1] ================================================ */ -typedef enum { /*!< BLEIF_CQCFG_CQPRI */ - BLEIF_CQCFG_CQPRI_LOW = 0, /*!< LOW : Low Priority (service as best effort) value. */ - BLEIF_CQCFG_CQPRI_HIGH = 1, /*!< HIGH : High Priority (service immediately) value. */ -} BLEIF_CQCFG_CQPRI_Enum; - -/* ================================================ BLEIF CQCFG CQEN [0..0] ================================================ */ -typedef enum { /*!< BLEIF_CQCFG_CQEN */ - BLEIF_CQCFG_CQEN_DIS = 0, /*!< DIS : Disable CQ Function value. */ - BLEIF_CQCFG_CQEN_EN = 1, /*!< EN : Enable CQ Function value. */ -} BLEIF_CQCFG_CQEN_Enum; - -/* ======================================================== CQADDR ========================================================= */ -/* ======================================================== CQSTAT ========================================================= */ -/* ======================================================== CQFLAGS ======================================================== */ -/* ====================================================== CQSETCLEAR ======================================================= */ -/* ======================================================= CQPAUSEEN ======================================================= */ -/* ============================================= BLEIF CQPAUSEEN CQPEN [0..15] ============================================= */ -typedef enum { /*!< BLEIF_CQPAUSEEN_CQPEN */ - BLEIF_CQPAUSEEN_CQPEN_CNTEQ = 32768, /*!< CNTEQ : Pauses command queue processing when HWCNT matches SWCNT - value. */ - BLEIF_CQPAUSEEN_CQPEN_BLEXOREN = 16384, /*!< BLEXOREN : Pause command queue when input BLE bit XORed with - SWFLAG4 is '1' value. */ - BLEIF_CQPAUSEEN_CQPEN_IOMXOREN = 8192, /*!< IOMXOREN : Pause command queue when input IOM bit XORed with - SWFLAG3 is '1' value. */ - BLEIF_CQPAUSEEN_CQPEN_GPIOXOREN = 4096, /*!< GPIOXOREN : Pause command queue when input GPIO irq_bit XORed - with SWFLAG2 is '1' value. */ - BLEIF_CQPAUSEEN_CQPEN_MSPI1XNOREN = 2048, /*!< MSPI1XNOREN : Pause command queue when input MSPI1 bit XNORed - with SWFLAG1 is '1' value. */ - BLEIF_CQPAUSEEN_CQPEN_MSPI0XNOREN = 1024, /*!< MSPI0XNOREN : Pause command queue when input MSPI0 bit XNORed - with SWFLAG0 is '1' value. */ - BLEIF_CQPAUSEEN_CQPEN_MSPI1XOREN = 512, /*!< MSPI1XOREN : Pause command queue when input MSPI1 bit XORed - with SWFLAG1 is '1' value. */ - BLEIF_CQPAUSEEN_CQPEN_MSPI0XOREN = 256, /*!< MSPI0XOREN : Pause command queue when input MSPI0 bit XORed - with SWFLAG0 is '1' value. */ - BLEIF_CQPAUSEEN_CQPEN_SWFLAGEN7 = 128, /*!< SWFLAGEN7 : Pause the command queue when software flag bit 7 - is '1'. value. */ - BLEIF_CQPAUSEEN_CQPEN_SWFLAGEN6 = 64, /*!< SWFLAGEN6 : Pause the command queue when software flag bit 7 - is '1' value. */ - BLEIF_CQPAUSEEN_CQPEN_SWFLAGEN5 = 32, /*!< SWFLAGEN5 : Pause the command queue when software flag bit 7 - is '1' value. */ - BLEIF_CQPAUSEEN_CQPEN_SWFLAGEN4 = 16, /*!< SWFLAGEN4 : Pause the command queue when software flag bit 7 - is '1' value. */ - BLEIF_CQPAUSEEN_CQPEN_SWFLAGEN3 = 8, /*!< SWFLAGEN3 : Pause the command queue when software flag bit 7 - is '1' value. */ - BLEIF_CQPAUSEEN_CQPEN_SWFLAGEN2 = 4, /*!< SWFLAGEN2 : Pause the command queue when software flag bit 7 - is '1' value. */ - BLEIF_CQPAUSEEN_CQPEN_SWFLAGEN1 = 2, /*!< SWFLAGEN1 : Pause the command queue when software flag bit 7 - is '1' value. */ - BLEIF_CQPAUSEEN_CQPEN_SWFLGEN0 = 1, /*!< SWFLGEN0 : Pause the command queue when software flag bit 7 - is '1' value. */ -} BLEIF_CQPAUSEEN_CQPEN_Enum; - -/* ======================================================= CQCURIDX ======================================================== */ -/* ======================================================= CQENDIDX ======================================================== */ -/* ======================================================== STATUS ========================================================= */ -/* ============================================== BLEIF STATUS IDLEST [2..2] =============================================== */ -typedef enum { /*!< BLEIF_STATUS_IDLEST */ - BLEIF_STATUS_IDLEST_IDLE = 1, /*!< IDLE : The I/O state machine is in the idle state. value. */ -} BLEIF_STATUS_IDLEST_Enum; - -/* ============================================== BLEIF STATUS CMDACT [1..1] =============================================== */ -typedef enum { /*!< BLEIF_STATUS_CMDACT */ - BLEIF_STATUS_CMDACT_ACTIVE = 1, /*!< ACTIVE : An I/O command is active. Indicates the active module - has an active command and is processing this. De-asserted - when the command is completed. value. */ -} BLEIF_STATUS_CMDACT_Enum; - -/* ================================================ BLEIF STATUS ERR [0..0] ================================================ */ -typedef enum { /*!< BLEIF_STATUS_ERR */ - BLEIF_STATUS_ERR_ERROR = 1, /*!< ERROR : Bit has been deprecated and will always return 0. value. */ -} BLEIF_STATUS_ERR_Enum; - -/* ======================================================== MSPICFG ======================================================== */ -/* ============================================= BLEIF MSPICFG SPILSB [23..23] ============================================= */ -typedef enum { /*!< BLEIF_MSPICFG_SPILSB */ - BLEIF_MSPICFG_SPILSB_MSB = 0, /*!< MSB : Send and receive MSB bit first value. */ - BLEIF_MSPICFG_SPILSB_LSB = 1, /*!< LSB : Send and receive LSB bit first value. */ -} BLEIF_MSPICFG_SPILSB_Enum; - -/* ============================================ BLEIF MSPICFG RDFCPOL [22..22] ============================================= */ -typedef enum { /*!< BLEIF_MSPICFG_RDFCPOL */ - BLEIF_MSPICFG_RDFCPOL_NORMAL = 0, /*!< NORMAL : SPI_STATUS signal from BLE Core high(1) creates flow - control and new read spi transactions will not be started - until the signal goes low.(default) value. */ - BLEIF_MSPICFG_RDFCPOL_INVERTED = 1, /*!< INVERTED : SPI_STATUS signal from BLE Core low(0) creates flow - control and new read spi transactions will not be started - until the signal goes high. value. */ -} BLEIF_MSPICFG_RDFCPOL_Enum; - -/* ============================================ BLEIF MSPICFG WTFCPOL [21..21] ============================================= */ -typedef enum { /*!< BLEIF_MSPICFG_WTFCPOL */ - BLEIF_MSPICFG_WTFCPOL_NORMAL = 0, /*!< NORMAL : SPI_STATUS signal from BLE Core high(1) creates flow - control and new write spi transactions will not be started - until the signal goes low.(default) value. */ - BLEIF_MSPICFG_WTFCPOL_INVERTED = 1, /*!< INVERTED : SPI_STATUS signal from BLE Core high(1) creates low(0) - control and new write spi transactions will not be started - until the signal goes high. value. */ -} BLEIF_MSPICFG_WTFCPOL_Enum; - -/* ============================================== BLEIF MSPICFG RDFC [17..17] ============================================== */ -typedef enum { /*!< BLEIF_MSPICFG_RDFC */ - BLEIF_MSPICFG_RDFC_DIS = 0, /*!< DIS : Read mode flow control disabled. value. */ - BLEIF_MSPICFG_RDFC_EN = 1, /*!< EN : Read mode flow control enabled. value. */ -} BLEIF_MSPICFG_RDFC_Enum; - -/* ============================================== BLEIF MSPICFG WTFC [16..16] ============================================== */ -typedef enum { /*!< BLEIF_MSPICFG_WTFC */ - BLEIF_MSPICFG_WTFC_DIS = 0, /*!< DIS : Write mode flow control disabled. value. */ - BLEIF_MSPICFG_WTFC_EN = 1, /*!< EN : Write mode flow control enabled. value. */ -} BLEIF_MSPICFG_WTFC_Enum; - -/* =============================================== BLEIF MSPICFG SPHA [1..1] =============================================== */ -typedef enum { /*!< BLEIF_MSPICFG_SPHA */ - BLEIF_MSPICFG_SPHA_SAMPLE_LEADING_EDGE = 0, /*!< SAMPLE_LEADING_EDGE : Sample on the leading (first) clock edge, - rising or falling dependant on the value of SPOL value. */ - BLEIF_MSPICFG_SPHA_SAMPLE_TRAILING_EDGE = 1, /*!< SAMPLE_TRAILING_EDGE : Sample on the trailing (second) clock - edge, rising of falling dependant on the value of SPOL - value. */ -} BLEIF_MSPICFG_SPHA_Enum; - -/* =============================================== BLEIF MSPICFG SPOL [0..0] =============================================== */ -typedef enum { /*!< BLEIF_MSPICFG_SPOL */ - BLEIF_MSPICFG_SPOL_CLK_BASE_0 = 0, /*!< CLK_BASE_0 : The initial value of the clock is 0. value. */ - BLEIF_MSPICFG_SPOL_CLK_BASE_1 = 1, /*!< CLK_BASE_1 : The initial value of the clock is 1. value. */ -} BLEIF_MSPICFG_SPOL_Enum; - -/* ======================================================== BLECFG ========================================================= */ -/* ============================================ BLEIF BLECFG SPIISOCTL [14..15] ============================================ */ -typedef enum { /*!< BLEIF_BLECFG_SPIISOCTL */ - BLEIF_BLECFG_SPIISOCTL_ON = 3, /*!< ON : SPI signals from BLE Core to/from MCU Core are isolated. - value. */ - BLEIF_BLECFG_SPIISOCTL_OFF = 2, /*!< OFF : SPI signals from BLE Core to/from MCU Core are not isolated. - value. */ - BLEIF_BLECFG_SPIISOCTL_AUTO = 0, /*!< AUTO : SPI signals from BLE Core to/from MCU Core are automatically - isolated by the logic value. */ -} BLEIF_BLECFG_SPIISOCTL_Enum; - -/* ============================================ BLEIF BLECFG PWRISOCTL [12..13] ============================================ */ -typedef enum { /*!< BLEIF_BLECFG_PWRISOCTL */ - BLEIF_BLECFG_PWRISOCTL_ON = 3, /*!< ON : BLEH power signal isolation to on (isolated). value. */ - BLEIF_BLECFG_PWRISOCTL_OFF = 2, /*!< OFF : BLEH power signal isolation to off (not isolated). value. */ - BLEIF_BLECFG_PWRISOCTL_AUTO = 0, /*!< AUTO : BLEH Power signal isolation is controlled automatically - through the interface logic value. */ -} BLEIF_BLECFG_PWRISOCTL_Enum; - -/* ============================================ BLEIF BLECFG BLEHREQCTL [6..7] ============================================= */ -typedef enum { /*!< BLEIF_BLECFG_BLEHREQCTL */ - BLEIF_BLECFG_BLEHREQCTL_ON = 3, /*!< ON : BLEH Power-on reg signal is set to on (1). value. */ - BLEIF_BLECFG_BLEHREQCTL_OFF = 2, /*!< OFF : BLEH Power-on signal is set to off (0). value. */ - BLEIF_BLECFG_BLEHREQCTL_AUTO = 0, /*!< AUTO : BLEH Power-on signal is controlled by the PWRSM logic - and automatically controlled value. */ -} BLEIF_BLECFG_BLEHREQCTL_Enum; - -/* ============================================ BLEIF BLECFG DCDCFLGCTL [4..5] ============================================= */ -typedef enum { /*!< BLEIF_BLECFG_DCDCFLGCTL */ - BLEIF_BLECFG_DCDCFLGCTL_ON = 3, /*!< ON : DCDC Flag signal is set to on (1). value. */ - BLEIF_BLECFG_DCDCFLGCTL_OFF = 2, /*!< OFF : DCDC Flag signal is set to off (0). value. */ - BLEIF_BLECFG_DCDCFLGCTL_AUTO = 0, /*!< AUTO : DCDC Flag signal is controlled by the PWRSM logic and - automatically controlled value. */ -} BLEIF_BLECFG_DCDCFLGCTL_Enum; - -/* ============================================= BLEIF BLECFG WAKEUPCTL [2..3] ============================================= */ -typedef enum { /*!< BLEIF_BLECFG_WAKEUPCTL */ - BLEIF_BLECFG_WAKEUPCTL_ON = 3, /*!< ON : Wake signal is set to on (1). value. */ - BLEIF_BLECFG_WAKEUPCTL_OFF = 2, /*!< OFF : Wake signal is set to off (0). value. */ - BLEIF_BLECFG_WAKEUPCTL_AUTO = 0, /*!< AUTO : Wake signal is controlled by the PWRSM logic and automatically - controlled value. */ -} BLEIF_BLECFG_WAKEUPCTL_Enum; - -/* ============================================== BLEIF BLECFG BLERSTN [1..1] ============================================== */ -typedef enum { /*!< BLEIF_BLECFG_BLERSTN */ - BLEIF_BLECFG_BLERSTN_ACTIVE = 1, /*!< ACTIVE : The reset signal is active (0) value. */ - BLEIF_BLECFG_BLERSTN_INACTIVE = 0, /*!< INACTIVE : The reset signal is inactive (1) value. */ -} BLEIF_BLECFG_BLERSTN_Enum; - -/* ============================================== BLEIF BLECFG PWRSMEN [0..0] ============================================== */ -typedef enum { /*!< BLEIF_BLECFG_PWRSMEN */ - BLEIF_BLECFG_PWRSMEN_ON = 1, /*!< ON : Internal power state machine is enabled and will sequence - the BLEH power domain as indicated in the design document. - Overrides for the power signals are not enabled. value. */ - BLEIF_BLECFG_PWRSMEN_OFF = 0, /*!< OFF : Internal power state machine is disabled and will not - sequence the BLEH power domain. The values of the overrides - will be used to drive the output sequencing signals value. */ -} BLEIF_BLECFG_PWRSMEN_Enum; - -/* ======================================================== PWRCMD ========================================================= */ -/* ======================================================== BSTATUS ======================================================== */ -/* ============================================== BLEIF BSTATUS PWRST [8..10] ============================================== */ -typedef enum { /*!< BLEIF_BSTATUS_PWRST */ - BLEIF_BSTATUS_PWRST_OFF = 0, /*!< OFF : Internal power state machine is disabled and will not - sequence the BLEH power domain. The values of the overrides - will be used to drive the output sequencing signals value. */ - BLEIF_BSTATUS_PWRST_INIT = 1, /*!< INIT : Initialization state. BLEH not powered value. */ - BLEIF_BSTATUS_PWRST_PWRON = 2, /*!< PWRON : Waiting for the powerup of the BLEH value. */ - BLEIF_BSTATUS_PWRST_ACTIVE = 3, /*!< ACTIVE : The BLE Core is powered and active value. */ - BLEIF_BSTATUS_PWRST_SLEEP = 6, /*!< SLEEP : The BLE Core has entered sleep mode and the power request - is inactive value. */ - BLEIF_BSTATUS_PWRST_SHUTDOWN = 4, /*!< SHUTDOWN : The BLE Core is in shutdown mode value. */ -} BLEIF_BSTATUS_PWRST_Enum; - -/* ============================================= BLEIF BSTATUS B2MSTATE [0..2] ============================================= */ -typedef enum { /*!< BLEIF_BSTATUS_B2MSTATE */ - BLEIF_BSTATUS_B2MSTATE_RESET = 0, /*!< RESET : Reset State value. */ - BLEIF_BSTATUS_B2MSTATE_Sleep = 1, /*!< Sleep : Sleep state. value. */ - BLEIF_BSTATUS_B2MSTATE_Standby = 2, /*!< Standby : Standby State value. */ - BLEIF_BSTATUS_B2MSTATE_Idle = 3, /*!< Idle : Idle state value. */ - BLEIF_BSTATUS_B2MSTATE_Active = 4, /*!< Active : Active state. value. */ -} BLEIF_BSTATUS_B2MSTATE_Enum; - -/* ======================================================== BLEDBG ========================================================= */ - - -/* =========================================================================================================================== */ -/* ================ CACHECTRL ================ */ -/* =========================================================================================================================== */ - -/* ======================================================= CACHECFG ======================================================== */ -/* =========================================== CACHECTRL CACHECFG CONFIG [4..7] ============================================ */ -typedef enum { /*!< CACHECTRL_CACHECFG_CONFIG */ - CACHECTRL_CACHECFG_CONFIG_W1_128B_512E = 4, /*!< W1_128B_512E : Direct mapped, 128-bit linesize, 512 entries - (4 SRAMs active) value. */ - CACHECTRL_CACHECFG_CONFIG_W2_128B_512E = 5, /*!< W2_128B_512E : Two-way set associative, 128-bit linesize, 512 - entries (8 SRAMs active) value. */ - CACHECTRL_CACHECFG_CONFIG_W1_128B_1024E = 8, /*!< W1_128B_1024E : Direct mapped, 128-bit linesize, 1024 entries - (8 SRAMs active) value. */ -} CACHECTRL_CACHECFG_CONFIG_Enum; - -/* ======================================================= FLASHCFG ======================================================== */ -/* ========================================== CACHECTRL FLASHCFG LPMMODE [12..13] ========================================== */ -typedef enum { /*!< CACHECTRL_FLASHCFG_LPMMODE */ - CACHECTRL_FLASHCFG_LPMMODE_NEVER = 0, /*!< NEVER : High power mode (LPM not used). value. */ - CACHECTRL_FLASHCFG_LPMMODE_STANDBY = 1, /*!< STANDBY : Fast Standby mode. LPM deasserted for read operations, - but asserted while flash IDLE. value. */ - CACHECTRL_FLASHCFG_LPMMODE_ALWAYS = 2, /*!< ALWAYS : Low Power mode. LPM always asserted for reads. LPM_RD_WAIT - must be programmed to accomodate longer read access times. - value. */ -} CACHECTRL_FLASHCFG_LPMMODE_Enum; - -/* ========================================================= CTRL ========================================================== */ -/* =========================================== CACHECTRL CTRL RESET_STAT [1..1] ============================================ */ -typedef enum { /*!< CACHECTRL_CTRL_RESET_STAT */ - CACHECTRL_CTRL_RESET_STAT_CLEAR = 1, /*!< CLEAR : Clear Cache Stats value. */ -} CACHECTRL_CTRL_RESET_STAT_Enum; - -/* ======================================================= NCR0START ======================================================= */ -/* ======================================================== NCR0END ======================================================== */ -/* ======================================================= NCR1START ======================================================= */ -/* ======================================================== NCR1END ======================================================== */ -/* ========================================================= DMON0 ========================================================= */ -/* ========================================================= DMON1 ========================================================= */ -/* ========================================================= DMON2 ========================================================= */ -/* ========================================================= DMON3 ========================================================= */ -/* ========================================================= IMON0 ========================================================= */ -/* ========================================================= IMON1 ========================================================= */ -/* ========================================================= IMON2 ========================================================= */ -/* ========================================================= IMON3 ========================================================= */ - - -/* =========================================================================================================================== */ -/* ================ CLKGEN ================ */ -/* =========================================================================================================================== */ - -/* ========================================================= CALXT ========================================================= */ -/* ========================================================= CALRC ========================================================= */ -/* ======================================================== ACALCTR ======================================================== */ -/* ========================================================= OCTRL ========================================================= */ -/* =============================================== CLKGEN OCTRL ACAL [8..10] =============================================== */ -typedef enum { /*!< CLKGEN_OCTRL_ACAL */ - CLKGEN_OCTRL_ACAL_DIS = 0, /*!< DIS : Disable Autocalibration value. */ - CLKGEN_OCTRL_ACAL_1024SEC = 2, /*!< 1024SEC : Autocalibrate every 1024 seconds. Once autocalibration - is done, an interrupt will be triggered at the end of 1024 - seconds. value. */ - CLKGEN_OCTRL_ACAL_512SEC = 3, /*!< 512SEC : Autocalibrate every 512 seconds. Once autocalibration - is done, an interrupt will be trigged at the end of 512 - seconds. value. */ - CLKGEN_OCTRL_ACAL_XTFREQ = 6, /*!< XTFREQ : Frequency measurement using XT. The XT clock is normally - considered much more accurate than the LFRC clock source. - value. */ - CLKGEN_OCTRL_ACAL_EXTFREQ = 7, /*!< EXTFREQ : Frequency measurement using external clock. value. */ -} CLKGEN_OCTRL_ACAL_Enum; - -/* =============================================== CLKGEN OCTRL OSEL [7..7] ================================================ */ -typedef enum { /*!< CLKGEN_OCTRL_OSEL */ - CLKGEN_OCTRL_OSEL_RTC_XT = 0, /*!< RTC_XT : RTC uses the XT value. */ - CLKGEN_OCTRL_OSEL_RTC_LFRC = 1, /*!< RTC_LFRC : RTC uses the LFRC value. */ -} CLKGEN_OCTRL_OSEL_Enum; - -/* ================================================ CLKGEN OCTRL FOS [6..6] ================================================ */ -typedef enum { /*!< CLKGEN_OCTRL_FOS */ - CLKGEN_OCTRL_FOS_DIS = 0, /*!< DIS : Disable the oscillator switch on failure function. value. */ - CLKGEN_OCTRL_FOS_EN = 1, /*!< EN : Enable the oscillator switch on failure function. value. */ -} CLKGEN_OCTRL_FOS_Enum; - -/* ============================================== CLKGEN OCTRL STOPRC [1..1] =============================================== */ -typedef enum { /*!< CLKGEN_OCTRL_STOPRC */ - CLKGEN_OCTRL_STOPRC_EN = 0, /*!< EN : Enable the LFRC Oscillator to drive the RTC value. */ - CLKGEN_OCTRL_STOPRC_STOP = 1, /*!< STOP : Stop the LFRC Oscillator when driving the RTC value. */ -} CLKGEN_OCTRL_STOPRC_Enum; - -/* ============================================== CLKGEN OCTRL STOPXT [0..0] =============================================== */ -typedef enum { /*!< CLKGEN_OCTRL_STOPXT */ - CLKGEN_OCTRL_STOPXT_EN = 0, /*!< EN : Enable the XT Oscillator to drive the RTC value. */ - CLKGEN_OCTRL_STOPXT_STOP = 1, /*!< STOP : Stop the XT Oscillator when driving the RTC value. */ -} CLKGEN_OCTRL_STOPXT_Enum; - -/* ======================================================== CLKOUT ========================================================= */ -/* =============================================== CLKGEN CLKOUT CKEN [7..7] =============================================== */ -typedef enum { /*!< CLKGEN_CLKOUT_CKEN */ - CLKGEN_CLKOUT_CKEN_DIS = 0, /*!< DIS : Disable CLKOUT value. */ - CLKGEN_CLKOUT_CKEN_EN = 1, /*!< EN : Enable CLKOUT value. */ -} CLKGEN_CLKOUT_CKEN_Enum; - -/* ============================================== CLKGEN CLKOUT CKSEL [0..5] =============================================== */ -typedef enum { /*!< CLKGEN_CLKOUT_CKSEL */ - CLKGEN_CLKOUT_CKSEL_LFRC = 0, /*!< LFRC : LFRC value. */ - CLKGEN_CLKOUT_CKSEL_XT_DIV2 = 1, /*!< XT_DIV2 : XT / 2 value. */ - CLKGEN_CLKOUT_CKSEL_XT_DIV4 = 2, /*!< XT_DIV4 : XT / 4 value. */ - CLKGEN_CLKOUT_CKSEL_XT_DIV8 = 3, /*!< XT_DIV8 : XT / 8 value. */ - CLKGEN_CLKOUT_CKSEL_XT_DIV16 = 4, /*!< XT_DIV16 : XT / 16 value. */ - CLKGEN_CLKOUT_CKSEL_XT_DIV32 = 5, /*!< XT_DIV32 : XT / 32 value. */ - CLKGEN_CLKOUT_CKSEL_RTC_1Hz = 16, /*!< RTC_1Hz : 1 Hz as selected in RTC value. */ - CLKGEN_CLKOUT_CKSEL_XT_DIV2M = 22, /*!< XT_DIV2M : XT / 2^21 value. */ - CLKGEN_CLKOUT_CKSEL_XT = 23, /*!< XT : XT value. */ - CLKGEN_CLKOUT_CKSEL_CG_100Hz = 24, /*!< CG_100Hz : 100 Hz as selected in CLKGEN value. */ - CLKGEN_CLKOUT_CKSEL_HFRC = 25, /*!< HFRC : HFRC value. */ - CLKGEN_CLKOUT_CKSEL_HFRC_DIV4 = 26, /*!< HFRC_DIV4 : HFRC / 4 value. */ - CLKGEN_CLKOUT_CKSEL_HFRC_DIV8 = 27, /*!< HFRC_DIV8 : HFRC / 8 value. */ - CLKGEN_CLKOUT_CKSEL_HFRC_DIV16 = 28, /*!< HFRC_DIV16 : HFRC / 16 value. */ - CLKGEN_CLKOUT_CKSEL_HFRC_DIV64 = 29, /*!< HFRC_DIV64 : HFRC / 64 value. */ - CLKGEN_CLKOUT_CKSEL_HFRC_DIV128 = 30, /*!< HFRC_DIV128 : HFRC / 128 value. */ - CLKGEN_CLKOUT_CKSEL_HFRC_DIV256 = 31, /*!< HFRC_DIV256 : HFRC / 256 value. */ - CLKGEN_CLKOUT_CKSEL_HFRC_DIV512 = 32, /*!< HFRC_DIV512 : HFRC / 512 value. */ - CLKGEN_CLKOUT_CKSEL_FLASH_CLK = 34, /*!< FLASH_CLK : Flash Clock value. */ - CLKGEN_CLKOUT_CKSEL_LFRC_DIV2 = 35, /*!< LFRC_DIV2 : LFRC / 2 value. */ - CLKGEN_CLKOUT_CKSEL_LFRC_DIV32 = 36, /*!< LFRC_DIV32 : LFRC / 32 value. */ - CLKGEN_CLKOUT_CKSEL_LFRC_DIV512 = 37, /*!< LFRC_DIV512 : LFRC / 512 value. */ - CLKGEN_CLKOUT_CKSEL_LFRC_DIV32K = 38, /*!< LFRC_DIV32K : LFRC / 32768 value. */ - CLKGEN_CLKOUT_CKSEL_XT_DIV256 = 39, /*!< XT_DIV256 : XT / 256 value. */ - CLKGEN_CLKOUT_CKSEL_XT_DIV8K = 40, /*!< XT_DIV8K : XT / 8192 value. */ - CLKGEN_CLKOUT_CKSEL_XT_DIV64K = 41, /*!< XT_DIV64K : XT / 2^16 value. */ - CLKGEN_CLKOUT_CKSEL_ULFRC_DIV16 = 42, /*!< ULFRC_DIV16 : Uncal LFRC / 16 value. */ - CLKGEN_CLKOUT_CKSEL_ULFRC_DIV128 = 43, /*!< ULFRC_DIV128 : Uncal LFRC / 128 value. */ - CLKGEN_CLKOUT_CKSEL_ULFRC_1Hz = 44, /*!< ULFRC_1Hz : Uncal LFRC / 1024 value. */ - CLKGEN_CLKOUT_CKSEL_ULFRC_DIV4K = 45, /*!< ULFRC_DIV4K : Uncal LFRC / 4096 value. */ - CLKGEN_CLKOUT_CKSEL_ULFRC_DIV1M = 46, /*!< ULFRC_DIV1M : Uncal LFRC / 2^20 value. */ - CLKGEN_CLKOUT_CKSEL_HFRC_DIV64K = 47, /*!< HFRC_DIV64K : HFRC / 2^16 value. */ - CLKGEN_CLKOUT_CKSEL_HFRC_DIV16M = 48, /*!< HFRC_DIV16M : HFRC / 2^24 value. */ - CLKGEN_CLKOUT_CKSEL_LFRC_DIV1M = 49, /*!< LFRC_DIV1M : LFRC / 2^20 value. */ - CLKGEN_CLKOUT_CKSEL_HFRCNE = 50, /*!< HFRCNE : HFRC (not autoenabled) value. */ - CLKGEN_CLKOUT_CKSEL_HFRCNE_DIV8 = 51, /*!< HFRCNE_DIV8 : HFRC / 8 (not autoenabled) value. */ - CLKGEN_CLKOUT_CKSEL_XTNE = 53, /*!< XTNE : XT (not autoenabled) value. */ - CLKGEN_CLKOUT_CKSEL_XTNE_DIV16 = 54, /*!< XTNE_DIV16 : XT / 16 (not autoenabled) value. */ - CLKGEN_CLKOUT_CKSEL_LFRCNE_DIV32 = 55, /*!< LFRCNE_DIV32 : LFRC / 32 (not autoenabled) value. */ - CLKGEN_CLKOUT_CKSEL_LFRCNE = 57, /*!< LFRCNE : LFRC (not autoenabled) - Default for undefined values - value. */ -} CLKGEN_CLKOUT_CKSEL_Enum; - -/* ======================================================== CLKKEY ========================================================= */ -/* ============================================= CLKGEN CLKKEY CLKKEY [0..31] ============================================== */ -typedef enum { /*!< CLKGEN_CLKKEY_CLKKEY */ - CLKGEN_CLKKEY_CLKKEY_Key = 71, /*!< Key : Key value. */ -} CLKGEN_CLKKEY_CLKKEY_Enum; - -/* ========================================================= CCTRL ========================================================= */ -/* ============================================== CLKGEN CCTRL CORESEL [0..0] ============================================== */ -typedef enum { /*!< CLKGEN_CCTRL_CORESEL */ - CLKGEN_CCTRL_CORESEL_HFRC = 0, /*!< HFRC : Core Clock is HFRC value. */ - CLKGEN_CCTRL_CORESEL_HFRC_DIV2 = 1, /*!< HFRC_DIV2 : Core Clock is HFRC / 2 value. */ -} CLKGEN_CCTRL_CORESEL_Enum; - -/* ======================================================== STATUS ========================================================= */ -/* ========================================================= HFADJ ========================================================= */ -/* ============================================ CLKGEN HFADJ HFADJGAIN [21..23] ============================================ */ -typedef enum { /*!< CLKGEN_HFADJ_HFADJGAIN */ - CLKGEN_HFADJ_HFADJGAIN_Gain_of_1 = 0, /*!< Gain_of_1 : HF Adjust with Gain of 1 value. */ - CLKGEN_HFADJ_HFADJGAIN_Gain_of_1_in_2 = 1, /*!< Gain_of_1_in_2 : HF Adjust with Gain of 0.5 value. */ - CLKGEN_HFADJ_HFADJGAIN_Gain_of_1_in_4 = 2, /*!< Gain_of_1_in_4 : HF Adjust with Gain of 0.25 value. */ - CLKGEN_HFADJ_HFADJGAIN_Gain_of_1_in_8 = 3, /*!< Gain_of_1_in_8 : HF Adjust with Gain of 0.125 value. */ - CLKGEN_HFADJ_HFADJGAIN_Gain_of_1_in_16 = 4, /*!< Gain_of_1_in_16 : HF Adjust with Gain of 0.0625 value. */ - CLKGEN_HFADJ_HFADJGAIN_Gain_of_1_in_32 = 5, /*!< Gain_of_1_in_32 : HF Adjust with Gain of 0.03125 value. */ -} CLKGEN_HFADJ_HFADJGAIN_Enum; - -/* ============================================ CLKGEN HFADJ HFWARMUP [20..20] ============================================= */ -typedef enum { /*!< CLKGEN_HFADJ_HFWARMUP */ - CLKGEN_HFADJ_HFWARMUP_1SEC = 0, /*!< 1SEC : Autoadjust XT warmup period = 1-2 seconds value. */ - CLKGEN_HFADJ_HFWARMUP_2SEC = 1, /*!< 2SEC : Autoadjust XT warmup period = 2-4 seconds value. */ -} CLKGEN_HFADJ_HFWARMUP_Enum; - -/* ============================================== CLKGEN HFADJ HFADJCK [1..3] ============================================== */ -typedef enum { /*!< CLKGEN_HFADJ_HFADJCK */ - CLKGEN_HFADJ_HFADJCK_4SEC = 0, /*!< 4SEC : Autoadjust repeat period = 4 seconds value. */ - CLKGEN_HFADJ_HFADJCK_16SEC = 1, /*!< 16SEC : Autoadjust repeat period = 16 seconds value. */ - CLKGEN_HFADJ_HFADJCK_32SEC = 2, /*!< 32SEC : Autoadjust repeat period = 32 seconds value. */ - CLKGEN_HFADJ_HFADJCK_64SEC = 3, /*!< 64SEC : Autoadjust repeat period = 64 seconds value. */ - CLKGEN_HFADJ_HFADJCK_128SEC = 4, /*!< 128SEC : Autoadjust repeat period = 128 seconds value. */ - CLKGEN_HFADJ_HFADJCK_256SEC = 5, /*!< 256SEC : Autoadjust repeat period = 256 seconds value. */ - CLKGEN_HFADJ_HFADJCK_512SEC = 6, /*!< 512SEC : Autoadjust repeat period = 512 seconds value. */ - CLKGEN_HFADJ_HFADJCK_1024SEC = 7, /*!< 1024SEC : Autoadjust repeat period = 1024 seconds value. */ -} CLKGEN_HFADJ_HFADJCK_Enum; - -/* ============================================== CLKGEN HFADJ HFADJEN [0..0] ============================================== */ -typedef enum { /*!< CLKGEN_HFADJ_HFADJEN */ - CLKGEN_HFADJ_HFADJEN_DIS = 0, /*!< DIS : Disable the HFRC adjustment value. */ - CLKGEN_HFADJ_HFADJEN_EN = 1, /*!< EN : Enable the HFRC adjustment value. */ -} CLKGEN_HFADJ_HFADJEN_Enum; - -/* ====================================================== CLOCKENSTAT ====================================================== */ -/* ======================================== CLKGEN CLOCKENSTAT CLOCKENSTAT [0..31] ========================================= */ -typedef enum { /*!< CLKGEN_CLOCKENSTAT_CLOCKENSTAT */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_ADC_CLKEN = 1, /*!< ADC_CLKEN : Clock enable for the ADC. value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_APBDMA_ACTIVITY_CLKEN = 2,/*!< APBDMA_ACTIVITY_CLKEN : Clock enable for the APBDMA ACTIVITY - value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_APBDMA_AOH_CLKEN = 4,/*!< APBDMA_AOH_CLKEN : Clock enable for the APBDMA AOH DOMAIN value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_APBDMA_AOL_CLKEN = 8,/*!< APBDMA_AOL_CLKEN : Clock enable for the APBDMA AOL DOMAIN value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_APBDMA_APB_CLKEN = 16,/*!< APBDMA_APB_CLKEN : Clock enable for the APBDMA_APB value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_APBDMA_BLEL_CLKEN = 32,/*!< APBDMA_BLEL_CLKEN : Clock enable for the APBDMA_BLEL value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_APBDMA_HCPA_CLKEN = 64,/*!< APBDMA_HCPA_CLKEN : Clock enable for the APBDMA_HCPA value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_APBDMA_HCPB_CLKEN = 128,/*!< APBDMA_HCPB_CLKEN : Clock enable for the APBDMA_HCPB value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_APBDMA_HCPC_CLKEN = 256,/*!< APBDMA_HCPC_CLKEN : Clock enable for the APBDMA_HCPC value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_APBDMA_MSPI_CLKEN = 512,/*!< APBDMA_MSPI_CLKEN : Clock enable for the APBDMA_MSPI value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_APBDMA_PDM_CLKEN = 1024,/*!< APBDMA_PDM_CLKEN : Clock enable for the APBDMA_PDM value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_BLEIF_CLK_CLKEN = 2048,/*!< BLEIF_CLK_CLKEN : Clock enable for the BLEIF value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_BLEIF_CLK32K_CLKEN = 4096,/*!< BLEIF_CLK32K_CLKEN : Clock enable for the BLEIF 32khZ CLOCK - value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER_CLKEN = 8192,/*!< CTIMER_CLKEN : Clock enable for the CTIMER BLOCK value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER0A_CLKEN = 16384,/*!< CTIMER0A_CLKEN : Clock enable for the CTIMER0A value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER0B_CLKEN = 32768,/*!< CTIMER0B_CLKEN : Clock enable for the CTIMER0B value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER1A_CLKEN = 65536,/*!< CTIMER1A_CLKEN : Clock enable for the CTIMER1A value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER1B_CLKEN = 131072,/*!< CTIMER1B_CLKEN : Clock enable for the CTIMER1B value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER2A_CLKEN = 262144,/*!< CTIMER2A_CLKEN : Clock enable for the CTIMER2A value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER2B_CLKEN = 524288,/*!< CTIMER2B_CLKEN : Clock enable for the CTIMER2B value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER3A_CLKEN = 1048576,/*!< CTIMER3A_CLKEN : Clock enable for the CTIMER3A value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER3B_CLKEN = 2097152,/*!< CTIMER3B_CLKEN : Clock enable for the CTIMER3B value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER4A_CLKEN = 4194304,/*!< CTIMER4A_CLKEN : Clock enable for the CTIMER4A value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER4B_CLKEN = 8388608,/*!< CTIMER4B_CLKEN : Clock enable for the CTIMER4B value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER5A_CLKEN = 16777216,/*!< CTIMER5A_CLKEN : Clock enable for the CTIMER5A value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER5B_CLKEN = 33554432,/*!< CTIMER5B_CLKEN : Clock enable for the CTIMER5B value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER6A_CLKEN = 67108864,/*!< CTIMER6A_CLKEN : Clock enable for the CTIMER6A value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER6B_CLKEN = 134217728,/*!< CTIMER6B_CLKEN : Clock enable for the CTIMER6B value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER7A_CLKEN = 268435456,/*!< CTIMER7A_CLKEN : Clock enable for the CTIMER7A value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_CTIMER7B_CLKEN = 536870912,/*!< CTIMER7B_CLKEN : Clock enable for the CTIMER7B value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_DAP_CLKEN = 1073741824,/*!< DAP_CLKEN : Clock enable for the DAP value. */ - CLKGEN_CLOCKENSTAT_CLOCKENSTAT_IOMSTRIFC0_CLKEN = -2147483648,/*!< IOMSTRIFC0_CLKEN : Clock enable for the IOMSTRIFC0 value. */ -} CLKGEN_CLOCKENSTAT_CLOCKENSTAT_Enum; - -/* ===================================================== CLOCKEN2STAT ====================================================== */ -/* ======================================= CLKGEN CLOCKEN2STAT CLOCKEN2STAT [0..31] ======================================== */ -typedef enum { /*!< CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT */ - CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_IOMSTRIFC1_CLKEN = 1,/*!< IOMSTRIFC1_CLKEN : Clock enable for the IO MASTER 1 IFC INTERFACE - value. */ - CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_IOMSTRIFC2_CLKEN = 2,/*!< IOMSTRIFC2_CLKEN : Clock enable for the IO MASTER 2 IFC INTERFACE - value. */ - CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_IOMSTRIFC3_CLKEN = 4,/*!< IOMSTRIFC3_CLKEN : Clock enable for the IO MASTER 3 IFC INTERFACE - value. */ - CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_IOMSTRIFC4_CLKEN = 8,/*!< IOMSTRIFC4_CLKEN : Clock enable for the IO MASTER 4 IFC INTERFACE - value. */ - CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_IOMSTRIFC5_CLKEN = 16,/*!< IOMSTRIFC5_CLKEN : Clock enable for the IO MASTER 5 IFC INTERFACE - value. */ - CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_PDM_CLKEN = 32,/*!< PDM_CLKEN : Clock enable for the PDM value. */ - CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_PDMIFC_CLKEN = 64,/*!< PDMIFC_CLKEN : Clock enable for the PDM INTERFACE value. */ - CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_PWRCTRL_CLKEN = 128,/*!< PWRCTRL_CLKEN : Clock enable for the PWRCTRL value. */ - CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_RSTGEN_CLKEN = 256,/*!< RSTGEN_CLKEN : Clock enable for the RSTGEN value. */ - CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_SCARD_CLKEN = 512,/*!< SCARD_CLKEN : Clock enable for the SCARD value. */ - CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_SCARD_ALTAPB_CLKEN = 1024,/*!< SCARD_ALTAPB_CLKEN : Clock enable for the SCARD ALTAPB value. */ - CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_STIMER_CNT_CLKEN = 2048,/*!< STIMER_CNT_CLKEN : Clock enable for the STIMER_CNT_CLKEN value. */ - CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_TPIU_CLKEN = 4096,/*!< TPIU_CLKEN : Clock enable for the TPIU_CLKEN value. */ - CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_UART0HF_CLKEN = 8192,/*!< UART0HF_CLKEN : Clock enable for the UART0 HF value. */ - CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_UART1HF_CLKEN = 16384,/*!< UART1HF_CLKEN : Clock enable for the UART1 HF value. */ - CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_XT_32KHZ_EN = 1073741824,/*!< XT_32KHZ_EN : Clock enable for the XT 32KHZ value. */ - CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_FORCEHFRC = -2147483648,/*!< FORCEHFRC : HFRC is forced on Status. value. */ -} CLKGEN_CLOCKEN2STAT_CLOCKEN2STAT_Enum; - -/* ===================================================== CLOCKEN3STAT ====================================================== */ -/* ======================================= CLKGEN CLOCKEN3STAT CLOCKEN3STAT [0..31] ======================================== */ -typedef enum { /*!< CLKGEN_CLOCKEN3STAT_CLOCKEN3STAT */ - CLKGEN_CLOCKEN3STAT_CLOCKEN3STAT_XTAL_enabled = 16777216,/*!< XTAL_enabled : XTAL is enabled value. */ - CLKGEN_CLOCKEN3STAT_CLOCKEN3STAT_HFRC_enabled = 33554432,/*!< HFRC_enabled : HFRC is enabled value. */ - CLKGEN_CLOCKEN3STAT_CLOCKEN3STAT_HFADJEN = 67108864,/*!< HFADJEN : HFRC Adjust enabled value. */ - CLKGEN_CLOCKEN3STAT_CLOCKEN3STAT_HFRC_en_out = 134217728,/*!< HFRC_en_out : HFRC Enabled out value. */ - CLKGEN_CLOCKEN3STAT_CLOCKEN3STAT_RTC_XT = 268435456,/*!< RTC_XT : RTC use XT value. */ - CLKGEN_CLOCKEN3STAT_CLOCKEN3STAT_clkout_xtal_en = 536870912,/*!< clkout_xtal_en : XTAL clkout enabled value. */ - CLKGEN_CLOCKEN3STAT_CLOCKEN3STAT_clkout_hfrc_en = 1073741824,/*!< clkout_hfrc_en : HFRC clkout enabled value. */ - CLKGEN_CLOCKEN3STAT_CLOCKEN3STAT_flashclk_en = -2147483648,/*!< flashclk_en : Flash clk is enabled value. */ -} CLKGEN_CLOCKEN3STAT_CLOCKEN3STAT_Enum; - -/* ======================================================= FREQCTRL ======================================================== */ -/* ============================================ CLKGEN FREQCTRL BURSTREQ [0..0] ============================================ */ -typedef enum { /*!< CLKGEN_FREQCTRL_BURSTREQ */ - CLKGEN_FREQCTRL_BURSTREQ_DIS = 0, /*!< DIS : Frequency for ARM core stays at 48MHz value. */ - CLKGEN_FREQCTRL_BURSTREQ_EN = 1, /*!< EN : Frequency for ARM core is increased to 96MHz value. */ -} CLKGEN_FREQCTRL_BURSTREQ_Enum; - -/* ===================================================== BLEBUCKTONADJ ===================================================== */ -/* ===================================== CLKGEN BLEBUCKTONADJ ZEROLENDETECTEN [27..27] ===================================== */ -typedef enum { /*!< CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTEN */ - CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTEN_DIS = 0, /*!< DIS : Disable Zero Length Detect value. */ - CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTEN_EN = 1, /*!< EN : Enable Zero Length Detect value. */ -} CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTEN_Enum; - -/* ==================================== CLKGEN BLEBUCKTONADJ ZEROLENDETECTTRIM [23..26] ==================================== */ -typedef enum { /*!< CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM */ - CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_SetF = 15,/*!< SetF : Indicator send when the BLE BUCK asserts blebuck_comp1 - for about 81us (10 percent margin of error) or more value. */ - CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_SetE = 14,/*!< SetE : Indicator send when the BLE BUCK asserts blebuck_comp1 - for about 75.6us (10 percent margin of error) or more value. */ - CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_SetD = 13,/*!< SetD : Indicator send when the BLE BUCK asserts blebuck_comp1 - for about 70.2us (10 percent margin of error) or more value. */ - CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_SetC = 12,/*!< SetC : Indicator send when the BLE BUCK asserts blebuck_comp1 - for about 64.8us (10 percent margin of error) or more value. */ - CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_SetB = 11,/*!< SetB : Indicator send when the BLE BUCK asserts blebuck_comp1 - for about 59.4us (10 percent margin of error) or more value. */ - CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_SetA = 10,/*!< SetA : Indicator send when the BLE BUCK asserts blebuck_comp1 - for about 54.0us (10 percent margin of error) or more value. */ - CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_Set9 = 9,/*!< Set9 : Indicator send when the BLE BUCK asserts blebuck_comp1 - for about 48.6us (10 percent margin of error) or more value. */ - CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_Set8 = 8,/*!< Set8 : Indicator send when the BLE BUCK asserts blebuck_comp1 - for about 43.2us (10 percent margin of error) or more value. */ - CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_Set7 = 7,/*!< Set7 : Indicator send when the BLE BUCK asserts blebuck_comp1 - for about 37.8us (10 percent margin of error) or more value. */ - CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_Set6 = 6,/*!< Set6 : Indicator send when the BLE BUCK asserts blebuck_comp1 - for about 32.4us (10 percent margin of error) or more value. */ - CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_Set5 = 5,/*!< Set5 : Indicator send when the BLE BUCK asserts blebuck_comp1 - for about 27.0us (10 percent margin of error) or more value. */ - CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_Set4 = 4,/*!< Set4 : Indicator send when the BLE BUCK asserts blebuck_comp1 - for about 21.6us (10 percent margin of error) or more value. */ - CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_Set3 = 3,/*!< Set3 : Indicator send when the BLE BUCK asserts blebuck_comp1 - for about 16.2us (10 percent margin of error) or more value. */ - CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_Set2 = 2,/*!< Set2 : Indicator send when the BLE BUCK asserts blebuck_comp1 - for about 10.8us (10 percent margin of error) or more value. */ - CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_Set1 = 1,/*!< Set1 : Indicator send when the BLE BUCK asserts blebuck_comp1 - for about 5.4us (10 percent margin of error) or more value. */ - CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_Set0 = 0,/*!< Set0 : Indicator send when the BLE BUCK asserts blebuck_comp1 - for about 2.0us (10 percent margin of error) or more value. */ -} CLKGEN_BLEBUCKTONADJ_ZEROLENDETECTTRIM_Enum; - -/* ======================================= CLKGEN BLEBUCKTONADJ TONADJUSTEN [22..22] ======================================= */ -typedef enum { /*!< CLKGEN_BLEBUCKTONADJ_TONADJUSTEN */ - CLKGEN_BLEBUCKTONADJ_TONADJUSTEN_DIS = 0, /*!< DIS : Disable Adjust for BLE BUCK TON trim value. */ - CLKGEN_BLEBUCKTONADJ_TONADJUSTEN_EN = 1, /*!< EN : Enable Adjust for BLE BUCK TON trim value. */ -} CLKGEN_BLEBUCKTONADJ_TONADJUSTEN_Enum; - -/* ===================================== CLKGEN BLEBUCKTONADJ TONADJUSTPERIOD [20..21] ===================================== */ -typedef enum { /*!< CLKGEN_BLEBUCKTONADJ_TONADJUSTPERIOD */ - CLKGEN_BLEBUCKTONADJ_TONADJUSTPERIOD_HFRC_3KHz = 3,/*!< HFRC_3KHz : Adjust done for every 1 3KHz period value. */ - CLKGEN_BLEBUCKTONADJ_TONADJUSTPERIOD_HFRC_12KHz = 2,/*!< HFRC_12KHz : Adjust done for every 1 12KHz period value. */ - CLKGEN_BLEBUCKTONADJ_TONADJUSTPERIOD_HFRC_47KHz = 1,/*!< HFRC_47KHz : Adjust done for every 1 47KHz period value. */ - CLKGEN_BLEBUCKTONADJ_TONADJUSTPERIOD_HFRC_94KHz = 0,/*!< HFRC_94KHz : Adjust done for every 1 94KHz period value. */ -} CLKGEN_BLEBUCKTONADJ_TONADJUSTPERIOD_Enum; - -/* ======================================================= INTRPTEN ======================================================== */ -/* ====================================================== INTRPTSTAT ======================================================= */ -/* ======================================================= INTRPTCLR ======================================================= */ -/* ======================================================= INTRPTSET ======================================================= */ - - -/* =========================================================================================================================== */ -/* ================ CTIMER ================ */ -/* =========================================================================================================================== */ - -/* ========================================================= TMR0 ========================================================== */ -/* ======================================================== CMPRA0 ========================================================= */ -/* ======================================================== CMPRB0 ========================================================= */ -/* ========================================================= CTRL0 ========================================================= */ -/* ============================================= CTIMER CTRL0 CTLINK0 [31..31] ============================================= */ -typedef enum { /*!< CTIMER_CTRL0_CTLINK0 */ - CTIMER_CTRL0_CTLINK0_TWO_16BIT_TIMERS = 0, /*!< TWO_16BIT_TIMERS : Use A0/B0 timers as two independent 16-bit - timers (default). value. */ - CTIMER_CTRL0_CTLINK0_32BIT_TIMER = 1, /*!< 32BIT_TIMER : Link A0/B0 timers into a single 32-bit timer. - value. */ -} CTIMER_CTRL0_CTLINK0_Enum; - -/* ============================================ CTIMER CTRL0 TMRB0POL [28..28] ============================================= */ -typedef enum { /*!< CTIMER_CTRL0_TMRB0POL */ - CTIMER_CTRL0_TMRB0POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINB0 pin is the same as the - timer output. value. */ - CTIMER_CTRL0_TMRB0POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINB0 pin is the inverse of - the timer output. value. */ -} CTIMER_CTRL0_TMRB0POL_Enum; - -/* ============================================ CTIMER CTRL0 TMRB0CLR [27..27] ============================================= */ -typedef enum { /*!< CTIMER_CTRL0_TMRB0CLR */ - CTIMER_CTRL0_TMRB0CLR_RUN = 0, /*!< RUN : Allow counter/timer B0 to run value. */ - CTIMER_CTRL0_TMRB0CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer B0 at 0x0000. value. */ -} CTIMER_CTRL0_TMRB0CLR_Enum; - -/* ============================================ CTIMER CTRL0 TMRB0IE1 [26..26] ============================================= */ -typedef enum { /*!< CTIMER_CTRL0_TMRB0IE1 */ - CTIMER_CTRL0_TMRB0IE1_DIS = 0, /*!< DIS : Disable counter/timer B0 from generating an interrupt - based on COMPR1. value. */ - CTIMER_CTRL0_TMRB0IE1_EN = 1, /*!< EN : Enable counter/timer B0 to generate an interrupt based - on COMPR1. value. */ -} CTIMER_CTRL0_TMRB0IE1_Enum; - -/* ============================================ CTIMER CTRL0 TMRB0IE0 [25..25] ============================================= */ -typedef enum { /*!< CTIMER_CTRL0_TMRB0IE0 */ - CTIMER_CTRL0_TMRB0IE0_DIS = 0, /*!< DIS : Disable counter/timer B0 from generating an interrupt - based on COMPR0. value. */ - CTIMER_CTRL0_TMRB0IE0_EN = 1, /*!< EN : Enable counter/timer B0 to generate an interrupt based - on COMPR0 value. */ -} CTIMER_CTRL0_TMRB0IE0_Enum; - -/* ============================================= CTIMER CTRL0 TMRB0FN [22..24] ============================================= */ -typedef enum { /*!< CTIMER_CTRL0_TMRB0FN */ - CTIMER_CTRL0_TMRB0FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count - to CMPR0B0, stop. value. */ - CTIMER_CTRL0_TMRB0FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide - pulses). Count to CMPR0B0, restart. value. */ - CTIMER_CTRL0_TMRB0FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0B0, assert, - count to CMPR1B0, deassert, stop. value. */ - CTIMER_CTRL0_TMRB0FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0B0, assert, count - to CMPR1B0, deassert, restart. value. */ - CTIMER_CTRL0_TMRB0FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ - CTIMER_CTRL0_TMRB0FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ - CTIMER_CTRL0_TMRB0FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. - value. */ - CTIMER_CTRL0_TMRB0FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ -} CTIMER_CTRL0_TMRB0FN_Enum; - -/* ============================================ CTIMER CTRL0 TMRB0CLK [17..21] ============================================= */ -typedef enum { /*!< CTIMER_CTRL0_TMRB0CLK */ - CTIMER_CTRL0_TMRB0CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINB. value. */ - CTIMER_CTRL0_TMRB0CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ - CTIMER_CTRL0_TMRB0CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ - CTIMER_CTRL0_TMRB0CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ - CTIMER_CTRL0_TMRB0CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ - CTIMER_CTRL0_TMRB0CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ - CTIMER_CTRL0_TMRB0CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ - CTIMER_CTRL0_TMRB0CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ - CTIMER_CTRL0_TMRB0CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ - CTIMER_CTRL0_TMRB0CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ - CTIMER_CTRL0_TMRB0CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ - CTIMER_CTRL0_TMRB0CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ - CTIMER_CTRL0_TMRB0CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ - CTIMER_CTRL0_TMRB0CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ - CTIMER_CTRL0_TMRB0CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. - value. */ - CTIMER_CTRL0_TMRB0CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ - CTIMER_CTRL0_TMRB0CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ - CTIMER_CTRL0_TMRB0CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ - CTIMER_CTRL0_TMRB0CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ - CTIMER_CTRL0_TMRB0CLK_CTMRA0 = 20, /*!< CTMRA0 : Clock source is CTIMERA0 OUT. value. */ - CTIMER_CTRL0_TMRB0CLK_CTMRB1 = 21, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ - CTIMER_CTRL0_TMRB0CLK_CTMRA1 = 22, /*!< CTMRA1 : Clock source is CTIMERA1 OUT. value. */ - CTIMER_CTRL0_TMRB0CLK_CTMRA2 = 23, /*!< CTMRA2 : Clock source is CTIMERA2 OUT. value. */ - CTIMER_CTRL0_TMRB0CLK_CTMRB2 = 24, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ - CTIMER_CTRL0_TMRB0CLK_CTMRB3 = 25, /*!< CTMRB3 : Clock source is CTIMERB3 OUT. value. */ - CTIMER_CTRL0_TMRB0CLK_CTMRB4 = 26, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ - CTIMER_CTRL0_TMRB0CLK_CTMRB5 = 27, /*!< CTMRB5 : Clock source is CTIMERB5 OUT. value. */ - CTIMER_CTRL0_TMRB0CLK_CTMRB6 = 28, /*!< CTMRB6 : Clock source is CTIMERB6 OUT. value. */ - CTIMER_CTRL0_TMRB0CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ - CTIMER_CTRL0_TMRB0CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ - CTIMER_CTRL0_TMRB0CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ -} CTIMER_CTRL0_TMRB0CLK_Enum; - -/* ============================================= CTIMER CTRL0 TMRB0EN [16..16] ============================================= */ -typedef enum { /*!< CTIMER_CTRL0_TMRB0EN */ - CTIMER_CTRL0_TMRB0EN_DIS = 0, /*!< DIS : Counter/Timer B0 Disable. value. */ - CTIMER_CTRL0_TMRB0EN_EN = 1, /*!< EN : Counter/Timer B0 Enable. value. */ -} CTIMER_CTRL0_TMRB0EN_Enum; - -/* ============================================ CTIMER CTRL0 TMRA0POL [12..12] ============================================= */ -typedef enum { /*!< CTIMER_CTRL0_TMRA0POL */ - CTIMER_CTRL0_TMRA0POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINA0 pin is the same as the - timer output. value. */ - CTIMER_CTRL0_TMRA0POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINA0 pin is the inverse of - the timer output. value. */ -} CTIMER_CTRL0_TMRA0POL_Enum; - -/* ============================================ CTIMER CTRL0 TMRA0CLR [11..11] ============================================= */ -typedef enum { /*!< CTIMER_CTRL0_TMRA0CLR */ - CTIMER_CTRL0_TMRA0CLR_RUN = 0, /*!< RUN : Allow counter/timer A0 to run value. */ - CTIMER_CTRL0_TMRA0CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer A0 at 0x0000. value. */ -} CTIMER_CTRL0_TMRA0CLR_Enum; - -/* ============================================ CTIMER CTRL0 TMRA0IE1 [10..10] ============================================= */ -typedef enum { /*!< CTIMER_CTRL0_TMRA0IE1 */ - CTIMER_CTRL0_TMRA0IE1_DIS = 0, /*!< DIS : Disable counter/timer A0 from generating an interrupt - based on COMPR1. value. */ - CTIMER_CTRL0_TMRA0IE1_EN = 1, /*!< EN : Enable counter/timer A0 to generate an interrupt based - on COMPR1. value. */ -} CTIMER_CTRL0_TMRA0IE1_Enum; - -/* ============================================= CTIMER CTRL0 TMRA0IE0 [9..9] ============================================== */ -typedef enum { /*!< CTIMER_CTRL0_TMRA0IE0 */ - CTIMER_CTRL0_TMRA0IE0_DIS = 0, /*!< DIS : Disable counter/timer A0 from generating an interrupt - based on COMPR0. value. */ - CTIMER_CTRL0_TMRA0IE0_EN = 1, /*!< EN : Enable counter/timer A0 to generate an interrupt based - on COMPR0. value. */ -} CTIMER_CTRL0_TMRA0IE0_Enum; - -/* ============================================== CTIMER CTRL0 TMRA0FN [6..8] ============================================== */ -typedef enum { /*!< CTIMER_CTRL0_TMRA0FN */ - CTIMER_CTRL0_TMRA0FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count - to CMPR0A0, stop. value. */ - CTIMER_CTRL0_TMRA0FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide - pulses). Count to CMPR0A0, restart. value. */ - CTIMER_CTRL0_TMRA0FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0A0, assert, - count to CMPR1A0, deassert, stop. value. */ - CTIMER_CTRL0_TMRA0FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0A0, assert, count - to CMPR1A0, deassert, restart. value. */ - CTIMER_CTRL0_TMRA0FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ - CTIMER_CTRL0_TMRA0FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ - CTIMER_CTRL0_TMRA0FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. - value. */ - CTIMER_CTRL0_TMRA0FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ -} CTIMER_CTRL0_TMRA0FN_Enum; - -/* ============================================= CTIMER CTRL0 TMRA0CLK [1..5] ============================================== */ -typedef enum { /*!< CTIMER_CTRL0_TMRA0CLK */ - CTIMER_CTRL0_TMRA0CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINA. value. */ - CTIMER_CTRL0_TMRA0CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ - CTIMER_CTRL0_TMRA0CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ - CTIMER_CTRL0_TMRA0CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ - CTIMER_CTRL0_TMRA0CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ - CTIMER_CTRL0_TMRA0CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ - CTIMER_CTRL0_TMRA0CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ - CTIMER_CTRL0_TMRA0CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ - CTIMER_CTRL0_TMRA0CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ - CTIMER_CTRL0_TMRA0CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ - CTIMER_CTRL0_TMRA0CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ - CTIMER_CTRL0_TMRA0CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ - CTIMER_CTRL0_TMRA0CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ - CTIMER_CTRL0_TMRA0CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ - CTIMER_CTRL0_TMRA0CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. - value. */ - CTIMER_CTRL0_TMRA0CLK_HCLK_DIV4 = 15, /*!< HCLK_DIV4 : Clock source is HCLK / 4. value. */ - CTIMER_CTRL0_TMRA0CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ - CTIMER_CTRL0_TMRA0CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ - CTIMER_CTRL0_TMRA0CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ - CTIMER_CTRL0_TMRA0CLK_CTMRB0 = 20, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ - CTIMER_CTRL0_TMRA0CLK_CTMRA1 = 21, /*!< CTMRA1 : Clock source is CTIMERA1 OUT. value. */ - CTIMER_CTRL0_TMRA0CLK_CTMRB1 = 22, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ - CTIMER_CTRL0_TMRA0CLK_CTMRA2 = 23, /*!< CTMRA2 : Clock source is CTIMERA2 OUT. value. */ - CTIMER_CTRL0_TMRA0CLK_CTMRB2 = 24, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ - CTIMER_CTRL0_TMRA0CLK_CTMRB3 = 25, /*!< CTMRB3 : Clock source is CTIMERB3 OUT. value. */ - CTIMER_CTRL0_TMRA0CLK_CTMRB4 = 26, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ - CTIMER_CTRL0_TMRA0CLK_CTMRB5 = 27, /*!< CTMRB5 : Clock source is CTIMERB5 OUT. value. */ - CTIMER_CTRL0_TMRA0CLK_CTMRB6 = 28, /*!< CTMRB6 : Clock source is CTIMERB6 OUT. value. */ - CTIMER_CTRL0_TMRA0CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ - CTIMER_CTRL0_TMRA0CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ - CTIMER_CTRL0_TMRA0CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ -} CTIMER_CTRL0_TMRA0CLK_Enum; - -/* ============================================== CTIMER CTRL0 TMRA0EN [0..0] ============================================== */ -typedef enum { /*!< CTIMER_CTRL0_TMRA0EN */ - CTIMER_CTRL0_TMRA0EN_DIS = 0, /*!< DIS : Counter/Timer A0 Disable. value. */ - CTIMER_CTRL0_TMRA0EN_EN = 1, /*!< EN : Counter/Timer A0 Enable. value. */ -} CTIMER_CTRL0_TMRA0EN_Enum; - -/* ======================================================= CMPRAUXA0 ======================================================= */ -/* ======================================================= CMPRAUXB0 ======================================================= */ -/* ========================================================= AUX0 ========================================================== */ -/* ============================================ CTIMER AUX0 TMRB0EN23 [30..30] ============================================= */ -typedef enum { /*!< CTIMER_AUX0_TMRB0EN23 */ - CTIMER_AUX0_TMRB0EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ - CTIMER_AUX0_TMRB0EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ -} CTIMER_AUX0_TMRB0EN23_Enum; - -/* ============================================ CTIMER AUX0 TMRB0POL23 [29..29] ============================================ */ -typedef enum { /*!< CTIMER_AUX0_TMRB0POL23 */ - CTIMER_AUX0_TMRB0POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ - CTIMER_AUX0_TMRB0POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ -} CTIMER_AUX0_TMRB0POL23_Enum; - -/* ============================================ CTIMER AUX0 TMRB0TINV [28..28] ============================================= */ -typedef enum { /*!< CTIMER_AUX0_TMRB0TINV */ - CTIMER_AUX0_TMRB0TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ - CTIMER_AUX0_TMRB0TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ -} CTIMER_AUX0_TMRB0TINV_Enum; - -/* =========================================== CTIMER AUX0 TMRB0NOSYNC [27..27] ============================================ */ -typedef enum { /*!< CTIMER_AUX0_TMRB0NOSYNC */ - CTIMER_AUX0_TMRB0NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ - CTIMER_AUX0_TMRB0NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ -} CTIMER_AUX0_TMRB0NOSYNC_Enum; - -/* ============================================ CTIMER AUX0 TMRB0TRIG [23..26] ============================================= */ -typedef enum { /*!< CTIMER_AUX0_TMRB0TRIG */ - CTIMER_AUX0_TMRB0TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ - CTIMER_AUX0_TMRB0TRIG_A0OUT = 1, /*!< A0OUT : Trigger source is CTIMERA0 OUT. value. */ - CTIMER_AUX0_TMRB0TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ - CTIMER_AUX0_TMRB0TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ - CTIMER_AUX0_TMRB0TRIG_B2OUT = 4, /*!< B2OUT : Trigger source is CTIMERB2 OUT. value. */ - CTIMER_AUX0_TMRB0TRIG_B5OUT = 5, /*!< B5OUT : Trigger source is CTIMERB5 OUT. value. */ - CTIMER_AUX0_TMRB0TRIG_A4OUT = 6, /*!< A4OUT : Trigger source is CTIMERA4 OUT. value. */ - CTIMER_AUX0_TMRB0TRIG_B4OUT = 7, /*!< B4OUT : Trigger source is CTIMERB4 OUT. value. */ - CTIMER_AUX0_TMRB0TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ - CTIMER_AUX0_TMRB0TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ - CTIMER_AUX0_TMRB0TRIG_B7OUT2 = 10, /*!< B7OUT2 : Trigger source is CTIMERB7 OUT2. value. */ - CTIMER_AUX0_TMRB0TRIG_A2OUT2 = 11, /*!< A2OUT2 : Trigger source is CTIMERA2 OUT2. value. */ - CTIMER_AUX0_TMRB0TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ - CTIMER_AUX0_TMRB0TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ - CTIMER_AUX0_TMRB0TRIG_B5OUT2DUAL = 14, /*!< B5OUT2DUAL : Trigger source is CTIMERB5 OUT2, dual edge. value. */ - CTIMER_AUX0_TMRB0TRIG_A5OUT2DUAL = 15, /*!< A5OUT2DUAL : Trigger source is CTIMERA5 OUT2, dual edge. value. */ -} CTIMER_AUX0_TMRB0TRIG_Enum; - -/* ============================================ CTIMER AUX0 TMRA0EN23 [14..14] ============================================= */ -typedef enum { /*!< CTIMER_AUX0_TMRA0EN23 */ - CTIMER_AUX0_TMRA0EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ - CTIMER_AUX0_TMRA0EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ -} CTIMER_AUX0_TMRA0EN23_Enum; - -/* ============================================ CTIMER AUX0 TMRA0POL23 [13..13] ============================================ */ -typedef enum { /*!< CTIMER_AUX0_TMRA0POL23 */ - CTIMER_AUX0_TMRA0POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ - CTIMER_AUX0_TMRA0POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ -} CTIMER_AUX0_TMRA0POL23_Enum; - -/* ============================================ CTIMER AUX0 TMRA0TINV [12..12] ============================================= */ -typedef enum { /*!< CTIMER_AUX0_TMRA0TINV */ - CTIMER_AUX0_TMRA0TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ - CTIMER_AUX0_TMRA0TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ -} CTIMER_AUX0_TMRA0TINV_Enum; - -/* =========================================== CTIMER AUX0 TMRA0NOSYNC [11..11] ============================================ */ -typedef enum { /*!< CTIMER_AUX0_TMRA0NOSYNC */ - CTIMER_AUX0_TMRA0NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ - CTIMER_AUX0_TMRA0NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ -} CTIMER_AUX0_TMRA0NOSYNC_Enum; - -/* ============================================= CTIMER AUX0 TMRA0TRIG [7..10] ============================================= */ -typedef enum { /*!< CTIMER_AUX0_TMRA0TRIG */ - CTIMER_AUX0_TMRA0TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ - CTIMER_AUX0_TMRA0TRIG_B0OUT = 1, /*!< B0OUT : Trigger source is CTIMERB0 OUT. value. */ - CTIMER_AUX0_TMRA0TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ - CTIMER_AUX0_TMRA0TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ - CTIMER_AUX0_TMRA0TRIG_A1OUT = 4, /*!< A1OUT : Trigger source is CTIMERA1 OUT. value. */ - CTIMER_AUX0_TMRA0TRIG_B1OUT = 5, /*!< B1OUT : Trigger source is CTIMERB1 OUT. value. */ - CTIMER_AUX0_TMRA0TRIG_A5OUT = 6, /*!< A5OUT : Trigger source is CTIMERA5 OUT. value. */ - CTIMER_AUX0_TMRA0TRIG_B5OUT = 7, /*!< B5OUT : Trigger source is CTIMERB5 OUT. value. */ - CTIMER_AUX0_TMRA0TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ - CTIMER_AUX0_TMRA0TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ - CTIMER_AUX0_TMRA0TRIG_B6OUT2 = 10, /*!< B6OUT2 : Trigger source is CTIMERB6 OUT2. value. */ - CTIMER_AUX0_TMRA0TRIG_A2OUT2 = 11, /*!< A2OUT2 : Trigger source is CTIMERA2 OUT2. value. */ - CTIMER_AUX0_TMRA0TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ - CTIMER_AUX0_TMRA0TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ - CTIMER_AUX0_TMRA0TRIG_B4OUT2DUAL = 14, /*!< B4OUT2DUAL : Trigger source is CTIMERB4 OUT2, dual edge. value. */ - CTIMER_AUX0_TMRA0TRIG_A4OUT2DUAL = 15, /*!< A4OUT2DUAL : Trigger source is CTIMERA4 OUT2, dual edge. value. */ -} CTIMER_AUX0_TMRA0TRIG_Enum; - -/* ========================================================= TMR1 ========================================================== */ -/* ======================================================== CMPRA1 ========================================================= */ -/* ======================================================== CMPRB1 ========================================================= */ -/* ========================================================= CTRL1 ========================================================= */ -/* ============================================= CTIMER CTRL1 CTLINK1 [31..31] ============================================= */ -typedef enum { /*!< CTIMER_CTRL1_CTLINK1 */ - CTIMER_CTRL1_CTLINK1_TWO_16BIT_TIMERS = 0, /*!< TWO_16BIT_TIMERS : Use A1/B1 timers as two independent 16-bit - timers (default). value. */ - CTIMER_CTRL1_CTLINK1_32BIT_TIMER = 1, /*!< 32BIT_TIMER : Link A1/B1 timers into a single 32-bit timer. - value. */ -} CTIMER_CTRL1_CTLINK1_Enum; - -/* ============================================ CTIMER CTRL1 TMRB1POL [28..28] ============================================= */ -typedef enum { /*!< CTIMER_CTRL1_TMRB1POL */ - CTIMER_CTRL1_TMRB1POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINB1 pin is the same as the - timer output. value. */ - CTIMER_CTRL1_TMRB1POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINB1 pin is the inverse of - the timer output. value. */ -} CTIMER_CTRL1_TMRB1POL_Enum; - -/* ============================================ CTIMER CTRL1 TMRB1CLR [27..27] ============================================= */ -typedef enum { /*!< CTIMER_CTRL1_TMRB1CLR */ - CTIMER_CTRL1_TMRB1CLR_RUN = 0, /*!< RUN : Allow counter/timer B1 to run value. */ - CTIMER_CTRL1_TMRB1CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer B1 at 0x0000. value. */ -} CTIMER_CTRL1_TMRB1CLR_Enum; - -/* ============================================ CTIMER CTRL1 TMRB1IE1 [26..26] ============================================= */ -typedef enum { /*!< CTIMER_CTRL1_TMRB1IE1 */ - CTIMER_CTRL1_TMRB1IE1_DIS = 0, /*!< DIS : Disable counter/timer B1 from generating an interrupt - based on COMPR1. value. */ - CTIMER_CTRL1_TMRB1IE1_EN = 1, /*!< EN : Enable counter/timer B1 to generate an interrupt based - on COMPR1. value. */ -} CTIMER_CTRL1_TMRB1IE1_Enum; - -/* ============================================ CTIMER CTRL1 TMRB1IE0 [25..25] ============================================= */ -typedef enum { /*!< CTIMER_CTRL1_TMRB1IE0 */ - CTIMER_CTRL1_TMRB1IE0_DIS = 0, /*!< DIS : Disable counter/timer B1 from generating an interrupt - based on COMPR0. value. */ - CTIMER_CTRL1_TMRB1IE0_EN = 1, /*!< EN : Enable counter/timer B1 to generate an interrupt based - on COMPR0 value. */ -} CTIMER_CTRL1_TMRB1IE0_Enum; - -/* ============================================= CTIMER CTRL1 TMRB1FN [22..24] ============================================= */ -typedef enum { /*!< CTIMER_CTRL1_TMRB1FN */ - CTIMER_CTRL1_TMRB1FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count - to CMPR0B1, stop. value. */ - CTIMER_CTRL1_TMRB1FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide - pulses). Count to CMPR0B1, restart. value. */ - CTIMER_CTRL1_TMRB1FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0B1, assert, - count to CMPR1B1, deassert, stop. value. */ - CTIMER_CTRL1_TMRB1FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0B1, assert, count - to CMPR1B1, deassert, restart. value. */ - CTIMER_CTRL1_TMRB1FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ - CTIMER_CTRL1_TMRB1FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ - CTIMER_CTRL1_TMRB1FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. - value. */ - CTIMER_CTRL1_TMRB1FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ -} CTIMER_CTRL1_TMRB1FN_Enum; - -/* ============================================ CTIMER CTRL1 TMRB1CLK [17..21] ============================================= */ -typedef enum { /*!< CTIMER_CTRL1_TMRB1CLK */ - CTIMER_CTRL1_TMRB1CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINB. value. */ - CTIMER_CTRL1_TMRB1CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ - CTIMER_CTRL1_TMRB1CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ - CTIMER_CTRL1_TMRB1CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ - CTIMER_CTRL1_TMRB1CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ - CTIMER_CTRL1_TMRB1CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ - CTIMER_CTRL1_TMRB1CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ - CTIMER_CTRL1_TMRB1CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ - CTIMER_CTRL1_TMRB1CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ - CTIMER_CTRL1_TMRB1CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ - CTIMER_CTRL1_TMRB1CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ - CTIMER_CTRL1_TMRB1CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ - CTIMER_CTRL1_TMRB1CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ - CTIMER_CTRL1_TMRB1CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ - CTIMER_CTRL1_TMRB1CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. - value. */ - CTIMER_CTRL1_TMRB1CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ - CTIMER_CTRL1_TMRB1CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ - CTIMER_CTRL1_TMRB1CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ - CTIMER_CTRL1_TMRB1CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ - CTIMER_CTRL1_TMRB1CLK_CTMRA1 = 20, /*!< CTMRA1 : Clock source is CTIMERA1 OUT. value. */ - CTIMER_CTRL1_TMRB1CLK_CTMRA0 = 21, /*!< CTMRA0 : Clock source is CTIMERA0 OUT. value. */ - CTIMER_CTRL1_TMRB1CLK_CTMRB0 = 22, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ - CTIMER_CTRL1_TMRB1CLK_CTMRA2 = 23, /*!< CTMRA2 : Clock source is CTIMERA2 OUT. value. */ - CTIMER_CTRL1_TMRB1CLK_CTMRB2 = 24, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ - CTIMER_CTRL1_TMRB1CLK_CTMRB3 = 25, /*!< CTMRB3 : Clock source is CTIMERB3 OUT. value. */ - CTIMER_CTRL1_TMRB1CLK_CTMRB4 = 26, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ - CTIMER_CTRL1_TMRB1CLK_CTMRB5 = 27, /*!< CTMRB5 : Clock source is CTIMERB5 OUT. value. */ - CTIMER_CTRL1_TMRB1CLK_CTMRB6 = 28, /*!< CTMRB6 : Clock source is CTIMERB6 OUT. value. */ - CTIMER_CTRL1_TMRB1CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ - CTIMER_CTRL1_TMRB1CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ - CTIMER_CTRL1_TMRB1CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ -} CTIMER_CTRL1_TMRB1CLK_Enum; - -/* ============================================= CTIMER CTRL1 TMRB1EN [16..16] ============================================= */ -typedef enum { /*!< CTIMER_CTRL1_TMRB1EN */ - CTIMER_CTRL1_TMRB1EN_DIS = 0, /*!< DIS : Counter/Timer B1 Disable. value. */ - CTIMER_CTRL1_TMRB1EN_EN = 1, /*!< EN : Counter/Timer B1 Enable. value. */ -} CTIMER_CTRL1_TMRB1EN_Enum; - -/* ============================================ CTIMER CTRL1 TMRA1POL [12..12] ============================================= */ -typedef enum { /*!< CTIMER_CTRL1_TMRA1POL */ - CTIMER_CTRL1_TMRA1POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINA1 pin is the same as the - timer output. value. */ - CTIMER_CTRL1_TMRA1POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINA1 pin is the inverse of - the timer output. value. */ -} CTIMER_CTRL1_TMRA1POL_Enum; - -/* ============================================ CTIMER CTRL1 TMRA1CLR [11..11] ============================================= */ -typedef enum { /*!< CTIMER_CTRL1_TMRA1CLR */ - CTIMER_CTRL1_TMRA1CLR_RUN = 0, /*!< RUN : Allow counter/timer A1 to run value. */ - CTIMER_CTRL1_TMRA1CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer A1 at 0x0000. value. */ -} CTIMER_CTRL1_TMRA1CLR_Enum; - -/* ============================================ CTIMER CTRL1 TMRA1IE1 [10..10] ============================================= */ -typedef enum { /*!< CTIMER_CTRL1_TMRA1IE1 */ - CTIMER_CTRL1_TMRA1IE1_DIS = 0, /*!< DIS : Disable counter/timer A1 from generating an interrupt - based on COMPR1. value. */ - CTIMER_CTRL1_TMRA1IE1_EN = 1, /*!< EN : Enable counter/timer A1 to generate an interrupt based - on COMPR1. value. */ -} CTIMER_CTRL1_TMRA1IE1_Enum; - -/* ============================================= CTIMER CTRL1 TMRA1IE0 [9..9] ============================================== */ -typedef enum { /*!< CTIMER_CTRL1_TMRA1IE0 */ - CTIMER_CTRL1_TMRA1IE0_DIS = 0, /*!< DIS : Disable counter/timer A1 from generating an interrupt - based on COMPR0. value. */ - CTIMER_CTRL1_TMRA1IE0_EN = 1, /*!< EN : Enable counter/timer A1 to generate an interrupt based - on COMPR0. value. */ -} CTIMER_CTRL1_TMRA1IE0_Enum; - -/* ============================================== CTIMER CTRL1 TMRA1FN [6..8] ============================================== */ -typedef enum { /*!< CTIMER_CTRL1_TMRA1FN */ - CTIMER_CTRL1_TMRA1FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count - to CMPR0A1, stop. value. */ - CTIMER_CTRL1_TMRA1FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide - pulses). Count to CMPR0A1, restart. value. */ - CTIMER_CTRL1_TMRA1FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0A1, assert, - count to CMPR1A1, deassert, stop. value. */ - CTIMER_CTRL1_TMRA1FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0A1, assert, count - to CMPR1A1, deassert, restart. value. */ - CTIMER_CTRL1_TMRA1FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ - CTIMER_CTRL1_TMRA1FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ - CTIMER_CTRL1_TMRA1FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. - value. */ - CTIMER_CTRL1_TMRA1FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ -} CTIMER_CTRL1_TMRA1FN_Enum; - -/* ============================================= CTIMER CTRL1 TMRA1CLK [1..5] ============================================== */ -typedef enum { /*!< CTIMER_CTRL1_TMRA1CLK */ - CTIMER_CTRL1_TMRA1CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINA. value. */ - CTIMER_CTRL1_TMRA1CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ - CTIMER_CTRL1_TMRA1CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ - CTIMER_CTRL1_TMRA1CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ - CTIMER_CTRL1_TMRA1CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ - CTIMER_CTRL1_TMRA1CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ - CTIMER_CTRL1_TMRA1CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ - CTIMER_CTRL1_TMRA1CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ - CTIMER_CTRL1_TMRA1CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ - CTIMER_CTRL1_TMRA1CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ - CTIMER_CTRL1_TMRA1CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ - CTIMER_CTRL1_TMRA1CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ - CTIMER_CTRL1_TMRA1CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ - CTIMER_CTRL1_TMRA1CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ - CTIMER_CTRL1_TMRA1CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. - value. */ - CTIMER_CTRL1_TMRA1CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ - CTIMER_CTRL1_TMRA1CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ - CTIMER_CTRL1_TMRA1CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ - CTIMER_CTRL1_TMRA1CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ - CTIMER_CTRL1_TMRA1CLK_CTMRB1 = 20, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ - CTIMER_CTRL1_TMRA1CLK_CTMRA0 = 21, /*!< CTMRA0 : Clock source is CTIMERA0 OUT. value. */ - CTIMER_CTRL1_TMRA1CLK_CTMRB0 = 22, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ - CTIMER_CTRL1_TMRA1CLK_CTMRA2 = 23, /*!< CTMRA2 : Clock source is CTIMERA2 OUT. value. */ - CTIMER_CTRL1_TMRA1CLK_CTMRB2 = 24, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ - CTIMER_CTRL1_TMRA1CLK_CTMRB3 = 25, /*!< CTMRB3 : Clock source is CTIMERB3 OUT. value. */ - CTIMER_CTRL1_TMRA1CLK_CTMRB4 = 26, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ - CTIMER_CTRL1_TMRA1CLK_CTMRB5 = 27, /*!< CTMRB5 : Clock source is CTIMERB5 OUT. value. */ - CTIMER_CTRL1_TMRA1CLK_CTMRB6 = 28, /*!< CTMRB6 : Clock source is CTIMERB6 OUT. value. */ - CTIMER_CTRL1_TMRA1CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ - CTIMER_CTRL1_TMRA1CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ - CTIMER_CTRL1_TMRA1CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ -} CTIMER_CTRL1_TMRA1CLK_Enum; - -/* ============================================== CTIMER CTRL1 TMRA1EN [0..0] ============================================== */ -typedef enum { /*!< CTIMER_CTRL1_TMRA1EN */ - CTIMER_CTRL1_TMRA1EN_DIS = 0, /*!< DIS : Counter/Timer A1 Disable. value. */ - CTIMER_CTRL1_TMRA1EN_EN = 1, /*!< EN : Counter/Timer A1 Enable. value. */ -} CTIMER_CTRL1_TMRA1EN_Enum; - -/* ======================================================= CMPRAUXA1 ======================================================= */ -/* ======================================================= CMPRAUXB1 ======================================================= */ -/* ========================================================= AUX1 ========================================================== */ -/* ============================================ CTIMER AUX1 TMRB1EN23 [30..30] ============================================= */ -typedef enum { /*!< CTIMER_AUX1_TMRB1EN23 */ - CTIMER_AUX1_TMRB1EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ - CTIMER_AUX1_TMRB1EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ -} CTIMER_AUX1_TMRB1EN23_Enum; - -/* ============================================ CTIMER AUX1 TMRB1POL23 [29..29] ============================================ */ -typedef enum { /*!< CTIMER_AUX1_TMRB1POL23 */ - CTIMER_AUX1_TMRB1POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ - CTIMER_AUX1_TMRB1POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ -} CTIMER_AUX1_TMRB1POL23_Enum; - -/* ============================================ CTIMER AUX1 TMRB1TINV [28..28] ============================================= */ -typedef enum { /*!< CTIMER_AUX1_TMRB1TINV */ - CTIMER_AUX1_TMRB1TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ - CTIMER_AUX1_TMRB1TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ -} CTIMER_AUX1_TMRB1TINV_Enum; - -/* =========================================== CTIMER AUX1 TMRB1NOSYNC [27..27] ============================================ */ -typedef enum { /*!< CTIMER_AUX1_TMRB1NOSYNC */ - CTIMER_AUX1_TMRB1NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ - CTIMER_AUX1_TMRB1NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ -} CTIMER_AUX1_TMRB1NOSYNC_Enum; - -/* ============================================ CTIMER AUX1 TMRB1TRIG [23..26] ============================================= */ -typedef enum { /*!< CTIMER_AUX1_TMRB1TRIG */ - CTIMER_AUX1_TMRB1TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ - CTIMER_AUX1_TMRB1TRIG_A1OUT = 1, /*!< A1OUT : Trigger source is CTIMERA1 OUT. value. */ - CTIMER_AUX1_TMRB1TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ - CTIMER_AUX1_TMRB1TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ - CTIMER_AUX1_TMRB1TRIG_A6OUT = 4, /*!< A6OUT : Trigger source is CTIMERA6 OUT. value. */ - CTIMER_AUX1_TMRB1TRIG_B6OUT = 5, /*!< B6OUT : Trigger source is CTIMERB6 OUT. value. */ - CTIMER_AUX1_TMRB1TRIG_A0OUT = 6, /*!< A0OUT : Trigger source is CTIMERA0 OUT. value. */ - CTIMER_AUX1_TMRB1TRIG_B0OUT = 7, /*!< B0OUT : Trigger source is CTIMERB0 OUT. value. */ - CTIMER_AUX1_TMRB1TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ - CTIMER_AUX1_TMRB1TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ - CTIMER_AUX1_TMRB1TRIG_A4OUT2 = 10, /*!< A4OUT2 : Trigger source is CTIMERA4 OUT2. value. */ - CTIMER_AUX1_TMRB1TRIG_B4OUT2 = 11, /*!< B4OUT2 : Trigger source is CTIMERB4 OUT2. value. */ - CTIMER_AUX1_TMRB1TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ - CTIMER_AUX1_TMRB1TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ - CTIMER_AUX1_TMRB1TRIG_B5OUT2DUAL = 14, /*!< B5OUT2DUAL : Trigger source is CTIMERB5 OUT2, dual edge. value. */ - CTIMER_AUX1_TMRB1TRIG_A5OUT2DUAL = 15, /*!< A5OUT2DUAL : Trigger source is CTIMERA5 OUT2, dual edge. value. */ -} CTIMER_AUX1_TMRB1TRIG_Enum; - -/* ============================================ CTIMER AUX1 TMRA1EN23 [14..14] ============================================= */ -typedef enum { /*!< CTIMER_AUX1_TMRA1EN23 */ - CTIMER_AUX1_TMRA1EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ - CTIMER_AUX1_TMRA1EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ -} CTIMER_AUX1_TMRA1EN23_Enum; - -/* ============================================ CTIMER AUX1 TMRA1POL23 [13..13] ============================================ */ -typedef enum { /*!< CTIMER_AUX1_TMRA1POL23 */ - CTIMER_AUX1_TMRA1POL23_NORMAL = 0, /*!< NORMAL : Upper output normal polarity value. */ - CTIMER_AUX1_TMRA1POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ -} CTIMER_AUX1_TMRA1POL23_Enum; - -/* ============================================ CTIMER AUX1 TMRA1TINV [12..12] ============================================= */ -typedef enum { /*!< CTIMER_AUX1_TMRA1TINV */ - CTIMER_AUX1_TMRA1TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ - CTIMER_AUX1_TMRA1TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ -} CTIMER_AUX1_TMRA1TINV_Enum; - -/* =========================================== CTIMER AUX1 TMRA1NOSYNC [11..11] ============================================ */ -typedef enum { /*!< CTIMER_AUX1_TMRA1NOSYNC */ - CTIMER_AUX1_TMRA1NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ - CTIMER_AUX1_TMRA1NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ -} CTIMER_AUX1_TMRA1NOSYNC_Enum; - -/* ============================================= CTIMER AUX1 TMRA1TRIG [7..10] ============================================= */ -typedef enum { /*!< CTIMER_AUX1_TMRA1TRIG */ - CTIMER_AUX1_TMRA1TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ - CTIMER_AUX1_TMRA1TRIG_B1OUT = 1, /*!< B1OUT : Trigger source is CTIMERB1 OUT. value. */ - CTIMER_AUX1_TMRA1TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ - CTIMER_AUX1_TMRA1TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ - CTIMER_AUX1_TMRA1TRIG_A0OUT = 4, /*!< A0OUT : Trigger source is CTIMERA0 OUT. value. */ - CTIMER_AUX1_TMRA1TRIG_B0OUT = 5, /*!< B0OUT : Trigger source is CTIMERB0 OUT. value. */ - CTIMER_AUX1_TMRA1TRIG_A5OUT = 6, /*!< A5OUT : Trigger source is CTIMERA5 OUT. value. */ - CTIMER_AUX1_TMRA1TRIG_B5OUT = 7, /*!< B5OUT : Trigger source is CTIMERB5 OUT. value. */ - CTIMER_AUX1_TMRA1TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ - CTIMER_AUX1_TMRA1TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ - CTIMER_AUX1_TMRA1TRIG_A4OUT2 = 10, /*!< A4OUT2 : Trigger source is CTIMERA4 OUT2. value. */ - CTIMER_AUX1_TMRA1TRIG_B4OUT2 = 11, /*!< B4OUT2 : Trigger source is CTIMERB4 OUT2. value. */ - CTIMER_AUX1_TMRA1TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ - CTIMER_AUX1_TMRA1TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ - CTIMER_AUX1_TMRA1TRIG_B5OUT2DUAL = 14, /*!< B5OUT2DUAL : Trigger source is CTIMERB5 OUT2, dual edge. value. */ - CTIMER_AUX1_TMRA1TRIG_A5OUT2DUAL = 15, /*!< A5OUT2DUAL : Trigger source is CTIMERA5 OUT2, dual edge. value. */ -} CTIMER_AUX1_TMRA1TRIG_Enum; - -/* ========================================================= TMR2 ========================================================== */ -/* ======================================================== CMPRA2 ========================================================= */ -/* ======================================================== CMPRB2 ========================================================= */ -/* ========================================================= CTRL2 ========================================================= */ -/* ============================================= CTIMER CTRL2 CTLINK2 [31..31] ============================================= */ -typedef enum { /*!< CTIMER_CTRL2_CTLINK2 */ - CTIMER_CTRL2_CTLINK2_TWO_16BIT_TIMERS = 0, /*!< TWO_16BIT_TIMERS : Use A2/B2 timers as two independent 16-bit - timers (default). value. */ - CTIMER_CTRL2_CTLINK2_32BIT_TIMER = 1, /*!< 32BIT_TIMER : Link A2/B2 timers into a single 32-bit timer. - value. */ -} CTIMER_CTRL2_CTLINK2_Enum; - -/* ============================================ CTIMER CTRL2 TMRB2POL [28..28] ============================================= */ -typedef enum { /*!< CTIMER_CTRL2_TMRB2POL */ - CTIMER_CTRL2_TMRB2POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINB2 pin is the same as the - timer output. value. */ - CTIMER_CTRL2_TMRB2POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINB2 pin is the inverse of - the timer output. value. */ -} CTIMER_CTRL2_TMRB2POL_Enum; - -/* ============================================ CTIMER CTRL2 TMRB2CLR [27..27] ============================================= */ -typedef enum { /*!< CTIMER_CTRL2_TMRB2CLR */ - CTIMER_CTRL2_TMRB2CLR_RUN = 0, /*!< RUN : Allow counter/timer B2 to run value. */ - CTIMER_CTRL2_TMRB2CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer B2 at 0x0000. value. */ -} CTIMER_CTRL2_TMRB2CLR_Enum; - -/* ============================================ CTIMER CTRL2 TMRB2IE1 [26..26] ============================================= */ -typedef enum { /*!< CTIMER_CTRL2_TMRB2IE1 */ - CTIMER_CTRL2_TMRB2IE1_DIS = 0, /*!< DIS : Disable counter/timer B2 from generating an interrupt - based on COMPR1. value. */ - CTIMER_CTRL2_TMRB2IE1_EN = 1, /*!< EN : Enable counter/timer B2 to generate an interrupt based - on COMPR1. value. */ -} CTIMER_CTRL2_TMRB2IE1_Enum; - -/* ============================================ CTIMER CTRL2 TMRB2IE0 [25..25] ============================================= */ -typedef enum { /*!< CTIMER_CTRL2_TMRB2IE0 */ - CTIMER_CTRL2_TMRB2IE0_DIS = 0, /*!< DIS : Disable counter/timer B2 from generating an interrupt - based on COMPR0. value. */ - CTIMER_CTRL2_TMRB2IE0_EN = 1, /*!< EN : Enable counter/timer B2 to generate an interrupt based - on COMPR0 value. */ -} CTIMER_CTRL2_TMRB2IE0_Enum; - -/* ============================================= CTIMER CTRL2 TMRB2FN [22..24] ============================================= */ -typedef enum { /*!< CTIMER_CTRL2_TMRB2FN */ - CTIMER_CTRL2_TMRB2FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count - to CMPR0B2, stop. value. */ - CTIMER_CTRL2_TMRB2FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide - pulses). Count to CMPR0B2, restart. value. */ - CTIMER_CTRL2_TMRB2FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0B2, assert, - count to CMPR1B2, deassert, stop. value. */ - CTIMER_CTRL2_TMRB2FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0B2, assert, count - to CMPR1B2, deassert, restart. value. */ - CTIMER_CTRL2_TMRB2FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ - CTIMER_CTRL2_TMRB2FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ - CTIMER_CTRL2_TMRB2FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. - value. */ - CTIMER_CTRL2_TMRB2FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ -} CTIMER_CTRL2_TMRB2FN_Enum; - -/* ============================================ CTIMER CTRL2 TMRB2CLK [17..21] ============================================= */ -typedef enum { /*!< CTIMER_CTRL2_TMRB2CLK */ - CTIMER_CTRL2_TMRB2CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINB. value. */ - CTIMER_CTRL2_TMRB2CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ - CTIMER_CTRL2_TMRB2CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ - CTIMER_CTRL2_TMRB2CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ - CTIMER_CTRL2_TMRB2CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ - CTIMER_CTRL2_TMRB2CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ - CTIMER_CTRL2_TMRB2CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ - CTIMER_CTRL2_TMRB2CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ - CTIMER_CTRL2_TMRB2CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ - CTIMER_CTRL2_TMRB2CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ - CTIMER_CTRL2_TMRB2CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ - CTIMER_CTRL2_TMRB2CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ - CTIMER_CTRL2_TMRB2CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ - CTIMER_CTRL2_TMRB2CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ - CTIMER_CTRL2_TMRB2CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. - value. */ - CTIMER_CTRL2_TMRB2CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ - CTIMER_CTRL2_TMRB2CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ - CTIMER_CTRL2_TMRB2CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ - CTIMER_CTRL2_TMRB2CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ - CTIMER_CTRL2_TMRB2CLK_CTMRA2 = 20, /*!< CTMRA2 : Clock source is CTIMERA2 OUT. value. */ - CTIMER_CTRL2_TMRB2CLK_CTMRB3 = 21, /*!< CTMRB3 : Clock source is CTIMERA3 OUT. value. */ - CTIMER_CTRL2_TMRB2CLK_CTMRA3 = 22, /*!< CTMRA3 : Clock source is CTIMERB3 OUT. value. */ - CTIMER_CTRL2_TMRB2CLK_CTMRA4 = 23, /*!< CTMRA4 : Clock source is CTIMERA4 OUT. value. */ - CTIMER_CTRL2_TMRB2CLK_CTMRB4 = 24, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ - CTIMER_CTRL2_TMRB2CLK_CTMRB0 = 25, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ - CTIMER_CTRL2_TMRB2CLK_CTMRB1 = 26, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ - CTIMER_CTRL2_TMRB2CLK_CTMRB5 = 27, /*!< CTMRB5 : Clock source is CTIMERB5 OUT. value. */ - CTIMER_CTRL2_TMRB2CLK_CTMRB6 = 28, /*!< CTMRB6 : Clock source is CTIMERB6 OUT. value. */ - CTIMER_CTRL2_TMRB2CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ - CTIMER_CTRL2_TMRB2CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ - CTIMER_CTRL2_TMRB2CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ -} CTIMER_CTRL2_TMRB2CLK_Enum; - -/* ============================================= CTIMER CTRL2 TMRB2EN [16..16] ============================================= */ -typedef enum { /*!< CTIMER_CTRL2_TMRB2EN */ - CTIMER_CTRL2_TMRB2EN_DIS = 0, /*!< DIS : Counter/Timer B2 Disable. value. */ - CTIMER_CTRL2_TMRB2EN_EN = 1, /*!< EN : Counter/Timer B2 Enable. value. */ -} CTIMER_CTRL2_TMRB2EN_Enum; - -/* ============================================ CTIMER CTRL2 TMRA2POL [12..12] ============================================= */ -typedef enum { /*!< CTIMER_CTRL2_TMRA2POL */ - CTIMER_CTRL2_TMRA2POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINA2 pin is the same as the - timer output. value. */ - CTIMER_CTRL2_TMRA2POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINA2 pin is the inverse of - the timer output. value. */ -} CTIMER_CTRL2_TMRA2POL_Enum; - -/* ============================================ CTIMER CTRL2 TMRA2CLR [11..11] ============================================= */ -typedef enum { /*!< CTIMER_CTRL2_TMRA2CLR */ - CTIMER_CTRL2_TMRA2CLR_RUN = 0, /*!< RUN : Allow counter/timer A2 to run value. */ - CTIMER_CTRL2_TMRA2CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer A2 at 0x0000. value. */ -} CTIMER_CTRL2_TMRA2CLR_Enum; - -/* ============================================ CTIMER CTRL2 TMRA2IE1 [10..10] ============================================= */ -typedef enum { /*!< CTIMER_CTRL2_TMRA2IE1 */ - CTIMER_CTRL2_TMRA2IE1_DIS = 0, /*!< DIS : Disable counter/timer A2 from generating an interrupt - based on COMPR1. value. */ - CTIMER_CTRL2_TMRA2IE1_EN = 1, /*!< EN : Enable counter/timer A2 to generate an interrupt based - on COMPR1. value. */ -} CTIMER_CTRL2_TMRA2IE1_Enum; - -/* ============================================= CTIMER CTRL2 TMRA2IE0 [9..9] ============================================== */ -typedef enum { /*!< CTIMER_CTRL2_TMRA2IE0 */ - CTIMER_CTRL2_TMRA2IE0_DIS = 0, /*!< DIS : Disable counter/timer A2 from generating an interrupt - based on COMPR0. value. */ - CTIMER_CTRL2_TMRA2IE0_EN = 1, /*!< EN : Enable counter/timer A2 to generate an interrupt based - on COMPR0. value. */ -} CTIMER_CTRL2_TMRA2IE0_Enum; - -/* ============================================== CTIMER CTRL2 TMRA2FN [6..8] ============================================== */ -typedef enum { /*!< CTIMER_CTRL2_TMRA2FN */ - CTIMER_CTRL2_TMRA2FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count - to CMPR0A2, stop. value. */ - CTIMER_CTRL2_TMRA2FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide - pulses). Count to CMPR0A2, restart. value. */ - CTIMER_CTRL2_TMRA2FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0A2, assert, - count to CMPR1A2, deassert, stop. value. */ - CTIMER_CTRL2_TMRA2FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0A2, assert, count - to CMPR1A2, deassert, restart. value. */ - CTIMER_CTRL2_TMRA2FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ - CTIMER_CTRL2_TMRA2FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ - CTIMER_CTRL2_TMRA2FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. - value. */ - CTIMER_CTRL2_TMRA2FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ -} CTIMER_CTRL2_TMRA2FN_Enum; - -/* ============================================= CTIMER CTRL2 TMRA2CLK [1..5] ============================================== */ -typedef enum { /*!< CTIMER_CTRL2_TMRA2CLK */ - CTIMER_CTRL2_TMRA2CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINA. value. */ - CTIMER_CTRL2_TMRA2CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ - CTIMER_CTRL2_TMRA2CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ - CTIMER_CTRL2_TMRA2CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ - CTIMER_CTRL2_TMRA2CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ - CTIMER_CTRL2_TMRA2CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ - CTIMER_CTRL2_TMRA2CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ - CTIMER_CTRL2_TMRA2CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ - CTIMER_CTRL2_TMRA2CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ - CTIMER_CTRL2_TMRA2CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ - CTIMER_CTRL2_TMRA2CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ - CTIMER_CTRL2_TMRA2CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ - CTIMER_CTRL2_TMRA2CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ - CTIMER_CTRL2_TMRA2CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ - CTIMER_CTRL2_TMRA2CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. - value. */ - CTIMER_CTRL2_TMRA2CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ - CTIMER_CTRL2_TMRA2CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ - CTIMER_CTRL2_TMRA2CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ - CTIMER_CTRL2_TMRA2CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ - CTIMER_CTRL2_TMRA2CLK_CTMRB2 = 20, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ - CTIMER_CTRL2_TMRA2CLK_CTMRB3 = 21, /*!< CTMRB3 : Clock source is CTIMERA3 OUT. value. */ - CTIMER_CTRL2_TMRA2CLK_CTMRA3 = 22, /*!< CTMRA3 : Clock source is CTIMERB3 OUT. value. */ - CTIMER_CTRL2_TMRA2CLK_CTMRA4 = 23, /*!< CTMRA4 : Clock source is CTIMERA4 OUT. value. */ - CTIMER_CTRL2_TMRA2CLK_CTMRB4 = 24, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ - CTIMER_CTRL2_TMRA2CLK_CTMRB0 = 25, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ - CTIMER_CTRL2_TMRA2CLK_CTMRB1 = 26, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ - CTIMER_CTRL2_TMRA2CLK_CTMRB5 = 27, /*!< CTMRB5 : Clock source is CTIMERB5 OUT. value. */ - CTIMER_CTRL2_TMRA2CLK_CTMRB6 = 28, /*!< CTMRB6 : Clock source is CTIMERB6 OUT. value. */ - CTIMER_CTRL2_TMRA2CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ - CTIMER_CTRL2_TMRA2CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ - CTIMER_CTRL2_TMRA2CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ -} CTIMER_CTRL2_TMRA2CLK_Enum; - -/* ============================================== CTIMER CTRL2 TMRA2EN [0..0] ============================================== */ -typedef enum { /*!< CTIMER_CTRL2_TMRA2EN */ - CTIMER_CTRL2_TMRA2EN_DIS = 0, /*!< DIS : Counter/Timer A2 Disable. value. */ - CTIMER_CTRL2_TMRA2EN_EN = 1, /*!< EN : Counter/Timer A2 Enable. value. */ -} CTIMER_CTRL2_TMRA2EN_Enum; - -/* ======================================================= CMPRAUXA2 ======================================================= */ -/* ======================================================= CMPRAUXB2 ======================================================= */ -/* ========================================================= AUX2 ========================================================== */ -/* ============================================ CTIMER AUX2 TMRB2EN23 [30..30] ============================================= */ -typedef enum { /*!< CTIMER_AUX2_TMRB2EN23 */ - CTIMER_AUX2_TMRB2EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ - CTIMER_AUX2_TMRB2EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ -} CTIMER_AUX2_TMRB2EN23_Enum; - -/* ============================================ CTIMER AUX2 TMRB2POL23 [29..29] ============================================ */ -typedef enum { /*!< CTIMER_AUX2_TMRB2POL23 */ - CTIMER_AUX2_TMRB2POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ - CTIMER_AUX2_TMRB2POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ -} CTIMER_AUX2_TMRB2POL23_Enum; - -/* ============================================ CTIMER AUX2 TMRB2TINV [28..28] ============================================= */ -typedef enum { /*!< CTIMER_AUX2_TMRB2TINV */ - CTIMER_AUX2_TMRB2TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ - CTIMER_AUX2_TMRB2TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ -} CTIMER_AUX2_TMRB2TINV_Enum; - -/* =========================================== CTIMER AUX2 TMRB2NOSYNC [27..27] ============================================ */ -typedef enum { /*!< CTIMER_AUX2_TMRB2NOSYNC */ - CTIMER_AUX2_TMRB2NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ - CTIMER_AUX2_TMRB2NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ -} CTIMER_AUX2_TMRB2NOSYNC_Enum; - -/* ============================================ CTIMER AUX2 TMRB2TRIG [23..26] ============================================= */ -typedef enum { /*!< CTIMER_AUX2_TMRB2TRIG */ - CTIMER_AUX2_TMRB2TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ - CTIMER_AUX2_TMRB2TRIG_A2OUT = 1, /*!< A2OUT : Trigger source is CTIMERA2 OUT. value. */ - CTIMER_AUX2_TMRB2TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ - CTIMER_AUX2_TMRB2TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ - CTIMER_AUX2_TMRB2TRIG_A1OUT = 4, /*!< A1OUT : Trigger source is CTIMERA1 OUT. value. */ - CTIMER_AUX2_TMRB2TRIG_B1OUT = 5, /*!< B1OUT : Trigger source is CTIMERB1 OUT. value. */ - CTIMER_AUX2_TMRB2TRIG_A4OUT = 6, /*!< A4OUT : Trigger source is CTIMERA4 OUT. value. */ - CTIMER_AUX2_TMRB2TRIG_B4OUT = 7, /*!< B4OUT : Trigger source is CTIMERB4 OUT. value. */ - CTIMER_AUX2_TMRB2TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ - CTIMER_AUX2_TMRB2TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ - CTIMER_AUX2_TMRB2TRIG_A5OUT2 = 10, /*!< A5OUT2 : Trigger source is CTIMERA5 OUT2. value. */ - CTIMER_AUX2_TMRB2TRIG_B5OUT2 = 11, /*!< B5OUT2 : Trigger source is CTIMERB5 OUT2. value. */ - CTIMER_AUX2_TMRB2TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ - CTIMER_AUX2_TMRB2TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ - CTIMER_AUX2_TMRB2TRIG_B4OUT2DUAL = 14, /*!< B4OUT2DUAL : Trigger source is CTIMERB4 OUT2, dual edge. value. */ - CTIMER_AUX2_TMRB2TRIG_A4OUT2DUAL = 15, /*!< A4OUT2DUAL : Trigger source is CTIMERA4 OUT2, dual edge. value. */ -} CTIMER_AUX2_TMRB2TRIG_Enum; - -/* ============================================ CTIMER AUX2 TMRA2EN23 [14..14] ============================================= */ -typedef enum { /*!< CTIMER_AUX2_TMRA2EN23 */ - CTIMER_AUX2_TMRA2EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ - CTIMER_AUX2_TMRA2EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ -} CTIMER_AUX2_TMRA2EN23_Enum; - -/* ============================================ CTIMER AUX2 TMRA2POL23 [13..13] ============================================ */ -typedef enum { /*!< CTIMER_AUX2_TMRA2POL23 */ - CTIMER_AUX2_TMRA2POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ - CTIMER_AUX2_TMRA2POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ -} CTIMER_AUX2_TMRA2POL23_Enum; - -/* ============================================ CTIMER AUX2 TMRA2TINV [12..12] ============================================= */ -typedef enum { /*!< CTIMER_AUX2_TMRA2TINV */ - CTIMER_AUX2_TMRA2TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ - CTIMER_AUX2_TMRA2TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ -} CTIMER_AUX2_TMRA2TINV_Enum; - -/* =========================================== CTIMER AUX2 TMRA2NOSYNC [11..11] ============================================ */ -typedef enum { /*!< CTIMER_AUX2_TMRA2NOSYNC */ - CTIMER_AUX2_TMRA2NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ - CTIMER_AUX2_TMRA2NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ -} CTIMER_AUX2_TMRA2NOSYNC_Enum; - -/* ============================================= CTIMER AUX2 TMRA2TRIG [7..10] ============================================= */ -typedef enum { /*!< CTIMER_AUX2_TMRA2TRIG */ - CTIMER_AUX2_TMRA2TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ - CTIMER_AUX2_TMRA2TRIG_B2OUT = 1, /*!< B2OUT : Trigger source is CTIMERB2 OUT. value. */ - CTIMER_AUX2_TMRA2TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ - CTIMER_AUX2_TMRA2TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ - CTIMER_AUX2_TMRA2TRIG_A0OUT = 4, /*!< A0OUT : Trigger source is CTIMERA0 OUT. value. */ - CTIMER_AUX2_TMRA2TRIG_B0OUT = 5, /*!< B0OUT : Trigger source is CTIMERB0 OUT. value. */ - CTIMER_AUX2_TMRA2TRIG_A4OUT = 6, /*!< A4OUT : Trigger source is CTIMERA4 OUT. value. */ - CTIMER_AUX2_TMRA2TRIG_B4OUT = 7, /*!< B4OUT : Trigger source is CTIMERB4 OUT. value. */ - CTIMER_AUX2_TMRA2TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ - CTIMER_AUX2_TMRA2TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ - CTIMER_AUX2_TMRA2TRIG_A5OUT2 = 10, /*!< A5OUT2 : Trigger source is CTIMERA5 OUT2. value. */ - CTIMER_AUX2_TMRA2TRIG_B5OUT2 = 11, /*!< B5OUT2 : Trigger source is CTIMERB5 OUT2. value. */ - CTIMER_AUX2_TMRA2TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ - CTIMER_AUX2_TMRA2TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ - CTIMER_AUX2_TMRA2TRIG_B4OUT2DUAL = 14, /*!< B4OUT2DUAL : Trigger source is CTIMERB4 OUT2, dual edge. value. */ - CTIMER_AUX2_TMRA2TRIG_A4OUT2DUAL = 15, /*!< A4OUT2DUAL : Trigger source is CTIMERA4 OUT2, dual edge. value. */ -} CTIMER_AUX2_TMRA2TRIG_Enum; - -/* ========================================================= TMR3 ========================================================== */ -/* ======================================================== CMPRA3 ========================================================= */ -/* ======================================================== CMPRB3 ========================================================= */ -/* ========================================================= CTRL3 ========================================================= */ -/* ============================================= CTIMER CTRL3 CTLINK3 [31..31] ============================================= */ -typedef enum { /*!< CTIMER_CTRL3_CTLINK3 */ - CTIMER_CTRL3_CTLINK3_TWO_16BIT_TIMERS = 0, /*!< TWO_16BIT_TIMERS : Use A3/B3 timers as two independent 16-bit - timers (default). value. */ - CTIMER_CTRL3_CTLINK3_32BIT_TIMER = 1, /*!< 32BIT_TIMER : Link A3/B3 timers into a single 32-bit timer. - value. */ -} CTIMER_CTRL3_CTLINK3_Enum; - -/* ============================================ CTIMER CTRL3 TMRB3POL [28..28] ============================================= */ -typedef enum { /*!< CTIMER_CTRL3_TMRB3POL */ - CTIMER_CTRL3_TMRB3POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINB3 pin is the same as the - timer output. value. */ - CTIMER_CTRL3_TMRB3POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINB3 pin is the inverse of - the timer output. value. */ -} CTIMER_CTRL3_TMRB3POL_Enum; - -/* ============================================ CTIMER CTRL3 TMRB3CLR [27..27] ============================================= */ -typedef enum { /*!< CTIMER_CTRL3_TMRB3CLR */ - CTIMER_CTRL3_TMRB3CLR_RUN = 0, /*!< RUN : Allow counter/timer B3 to run value. */ - CTIMER_CTRL3_TMRB3CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer B3 at 0x0000. value. */ -} CTIMER_CTRL3_TMRB3CLR_Enum; - -/* ============================================ CTIMER CTRL3 TMRB3IE1 [26..26] ============================================= */ -typedef enum { /*!< CTIMER_CTRL3_TMRB3IE1 */ - CTIMER_CTRL3_TMRB3IE1_DIS = 0, /*!< DIS : Disable counter/timer B3 from generating an interrupt - based on COMPR1. value. */ - CTIMER_CTRL3_TMRB3IE1_EN = 1, /*!< EN : Enable counter/timer B3 to generate an interrupt based - on COMPR1. value. */ -} CTIMER_CTRL3_TMRB3IE1_Enum; - -/* ============================================ CTIMER CTRL3 TMRB3IE0 [25..25] ============================================= */ -typedef enum { /*!< CTIMER_CTRL3_TMRB3IE0 */ - CTIMER_CTRL3_TMRB3IE0_DIS = 0, /*!< DIS : Disable counter/timer B3 from generating an interrupt - based on COMPR0. value. */ - CTIMER_CTRL3_TMRB3IE0_EN = 1, /*!< EN : Enable counter/timer B3 to generate an interrupt based - on COMPR0 value. */ -} CTIMER_CTRL3_TMRB3IE0_Enum; - -/* ============================================= CTIMER CTRL3 TMRB3FN [22..24] ============================================= */ -typedef enum { /*!< CTIMER_CTRL3_TMRB3FN */ - CTIMER_CTRL3_TMRB3FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count - to CMPR0B3, stop. value. */ - CTIMER_CTRL3_TMRB3FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide - pulses). Count to CMPR0B3, restart. value. */ - CTIMER_CTRL3_TMRB3FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0B3, assert, - count to CMPR1B3, deassert, stop. value. */ - CTIMER_CTRL3_TMRB3FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0B3, assert, count - to CMPR1B3, deassert, restart. value. */ - CTIMER_CTRL3_TMRB3FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ - CTIMER_CTRL3_TMRB3FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ - CTIMER_CTRL3_TMRB3FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. - value. */ - CTIMER_CTRL3_TMRB3FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ -} CTIMER_CTRL3_TMRB3FN_Enum; - -/* ============================================ CTIMER CTRL3 TMRB3CLK [17..21] ============================================= */ -typedef enum { /*!< CTIMER_CTRL3_TMRB3CLK */ - CTIMER_CTRL3_TMRB3CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINB. value. */ - CTIMER_CTRL3_TMRB3CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ - CTIMER_CTRL3_TMRB3CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ - CTIMER_CTRL3_TMRB3CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ - CTIMER_CTRL3_TMRB3CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ - CTIMER_CTRL3_TMRB3CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ - CTIMER_CTRL3_TMRB3CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ - CTIMER_CTRL3_TMRB3CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ - CTIMER_CTRL3_TMRB3CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ - CTIMER_CTRL3_TMRB3CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ - CTIMER_CTRL3_TMRB3CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ - CTIMER_CTRL3_TMRB3CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ - CTIMER_CTRL3_TMRB3CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ - CTIMER_CTRL3_TMRB3CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ - CTIMER_CTRL3_TMRB3CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. - value. */ - CTIMER_CTRL3_TMRB3CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ - CTIMER_CTRL3_TMRB3CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ - CTIMER_CTRL3_TMRB3CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ - CTIMER_CTRL3_TMRB3CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ - CTIMER_CTRL3_TMRB3CLK_CTMRA3 = 20, /*!< CTMRA3 : Clock source is CTIMERA3 OUT. value. */ - CTIMER_CTRL3_TMRB3CLK_CTMRA2 = 21, /*!< CTMRA2 : Clock source is CTIMERA2 OUT. value. */ - CTIMER_CTRL3_TMRB3CLK_CTMRB2 = 22, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ - CTIMER_CTRL3_TMRB3CLK_CTMRA4 = 23, /*!< CTMRA4 : Clock source is CTIMERA4 OUT. value. */ - CTIMER_CTRL3_TMRB3CLK_CTMRB4 = 24, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ - CTIMER_CTRL3_TMRB3CLK_CTMRB0 = 25, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ - CTIMER_CTRL3_TMRB3CLK_CTMRB1 = 26, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ - CTIMER_CTRL3_TMRB3CLK_CTMRB5 = 27, /*!< CTMRB5 : Clock source is CTIMERB5 OUT. value. */ - CTIMER_CTRL3_TMRB3CLK_CTMRB6 = 28, /*!< CTMRB6 : Clock source is CTIMERB6 OUT. value. */ - CTIMER_CTRL3_TMRB3CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ - CTIMER_CTRL3_TMRB3CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ - CTIMER_CTRL3_TMRB3CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ -} CTIMER_CTRL3_TMRB3CLK_Enum; - -/* ============================================= CTIMER CTRL3 TMRB3EN [16..16] ============================================= */ -typedef enum { /*!< CTIMER_CTRL3_TMRB3EN */ - CTIMER_CTRL3_TMRB3EN_DIS = 0, /*!< DIS : Counter/Timer B3 Disable. value. */ - CTIMER_CTRL3_TMRB3EN_EN = 1, /*!< EN : Counter/Timer B3 Enable. value. */ -} CTIMER_CTRL3_TMRB3EN_Enum; - -/* ============================================ CTIMER CTRL3 TMRA3POL [12..12] ============================================= */ -typedef enum { /*!< CTIMER_CTRL3_TMRA3POL */ - CTIMER_CTRL3_TMRA3POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINA3 pin is the same as the - timer output. value. */ - CTIMER_CTRL3_TMRA3POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINA3 pin is the inverse of - the timer output. value. */ -} CTIMER_CTRL3_TMRA3POL_Enum; - -/* ============================================ CTIMER CTRL3 TMRA3CLR [11..11] ============================================= */ -typedef enum { /*!< CTIMER_CTRL3_TMRA3CLR */ - CTIMER_CTRL3_TMRA3CLR_RUN = 0, /*!< RUN : Allow counter/timer A3 to run value. */ - CTIMER_CTRL3_TMRA3CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer A3 at 0x0000. value. */ -} CTIMER_CTRL3_TMRA3CLR_Enum; - -/* ============================================ CTIMER CTRL3 TMRA3IE1 [10..10] ============================================= */ -typedef enum { /*!< CTIMER_CTRL3_TMRA3IE1 */ - CTIMER_CTRL3_TMRA3IE1_DIS = 0, /*!< DIS : Disable counter/timer A3 from generating an interrupt - based on COMPR1. value. */ - CTIMER_CTRL3_TMRA3IE1_EN = 1, /*!< EN : Enable counter/timer A3 to generate an interrupt based - on COMPR1. value. */ -} CTIMER_CTRL3_TMRA3IE1_Enum; - -/* ============================================= CTIMER CTRL3 TMRA3IE0 [9..9] ============================================== */ -typedef enum { /*!< CTIMER_CTRL3_TMRA3IE0 */ - CTIMER_CTRL3_TMRA3IE0_DIS = 0, /*!< DIS : Disable counter/timer A3 from generating an interrupt - based on COMPR0. value. */ - CTIMER_CTRL3_TMRA3IE0_EN = 1, /*!< EN : Enable counter/timer A3 to generate an interrupt based - on COMPR0. value. */ -} CTIMER_CTRL3_TMRA3IE0_Enum; - -/* ============================================== CTIMER CTRL3 TMRA3FN [6..8] ============================================== */ -typedef enum { /*!< CTIMER_CTRL3_TMRA3FN */ - CTIMER_CTRL3_TMRA3FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count - to CMPR0A3, stop. value. */ - CTIMER_CTRL3_TMRA3FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide - pulses). Count to CMPR0A3, restart. value. */ - CTIMER_CTRL3_TMRA3FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0A3, assert, - count to CMPR1A3, deassert, stop. value. */ - CTIMER_CTRL3_TMRA3FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0A3, assert, count - to CMPR1A3, deassert, restart. value. */ - CTIMER_CTRL3_TMRA3FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ - CTIMER_CTRL3_TMRA3FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ - CTIMER_CTRL3_TMRA3FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. - value. */ - CTIMER_CTRL3_TMRA3FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ -} CTIMER_CTRL3_TMRA3FN_Enum; - -/* ============================================= CTIMER CTRL3 TMRA3CLK [1..5] ============================================== */ -typedef enum { /*!< CTIMER_CTRL3_TMRA3CLK */ - CTIMER_CTRL3_TMRA3CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINA. value. */ - CTIMER_CTRL3_TMRA3CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ - CTIMER_CTRL3_TMRA3CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ - CTIMER_CTRL3_TMRA3CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ - CTIMER_CTRL3_TMRA3CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ - CTIMER_CTRL3_TMRA3CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ - CTIMER_CTRL3_TMRA3CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ - CTIMER_CTRL3_TMRA3CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ - CTIMER_CTRL3_TMRA3CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ - CTIMER_CTRL3_TMRA3CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ - CTIMER_CTRL3_TMRA3CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ - CTIMER_CTRL3_TMRA3CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ - CTIMER_CTRL3_TMRA3CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ - CTIMER_CTRL3_TMRA3CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ - CTIMER_CTRL3_TMRA3CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. - value. */ - CTIMER_CTRL3_TMRA3CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ - CTIMER_CTRL3_TMRA3CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ - CTIMER_CTRL3_TMRA3CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ - CTIMER_CTRL3_TMRA3CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ - CTIMER_CTRL3_TMRA3CLK_CTMRB3 = 20, /*!< CTMRB3 : Clock source is CTIMERB3 OUT. value. */ - CTIMER_CTRL3_TMRA3CLK_CTMRA2 = 21, /*!< CTMRA2 : Clock source is CTIMERA2 OUT. value. */ - CTIMER_CTRL3_TMRA3CLK_CTMRB2 = 22, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ - CTIMER_CTRL3_TMRA3CLK_CTMRA4 = 23, /*!< CTMRA4 : Clock source is CTIMERA4 OUT. value. */ - CTIMER_CTRL3_TMRA3CLK_CTMRB4 = 24, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ - CTIMER_CTRL3_TMRA3CLK_CTMRB0 = 25, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ - CTIMER_CTRL3_TMRA3CLK_CTMRB1 = 26, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ - CTIMER_CTRL3_TMRA3CLK_CTMRB5 = 27, /*!< CTMRB5 : Clock source is CTIMERB5 OUT. value. */ - CTIMER_CTRL3_TMRA3CLK_CTMRB6 = 28, /*!< CTMRB6 : Clock source is CTIMERB6 OUT. value. */ - CTIMER_CTRL3_TMRA3CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ - CTIMER_CTRL3_TMRA3CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ - CTIMER_CTRL3_TMRA3CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ -} CTIMER_CTRL3_TMRA3CLK_Enum; - -/* ============================================== CTIMER CTRL3 TMRA3EN [0..0] ============================================== */ -typedef enum { /*!< CTIMER_CTRL3_TMRA3EN */ - CTIMER_CTRL3_TMRA3EN_DIS = 0, /*!< DIS : Counter/Timer A3 Disable. value. */ - CTIMER_CTRL3_TMRA3EN_EN = 1, /*!< EN : Counter/Timer A3 Enable. value. */ -} CTIMER_CTRL3_TMRA3EN_Enum; - -/* ======================================================= CMPRAUXA3 ======================================================= */ -/* ======================================================= CMPRAUXB3 ======================================================= */ -/* ========================================================= AUX3 ========================================================== */ -/* ============================================ CTIMER AUX3 TMRB3EN23 [30..30] ============================================= */ -typedef enum { /*!< CTIMER_AUX3_TMRB3EN23 */ - CTIMER_AUX3_TMRB3EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ - CTIMER_AUX3_TMRB3EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ -} CTIMER_AUX3_TMRB3EN23_Enum; - -/* ============================================ CTIMER AUX3 TMRB3POL23 [29..29] ============================================ */ -typedef enum { /*!< CTIMER_AUX3_TMRB3POL23 */ - CTIMER_AUX3_TMRB3POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ - CTIMER_AUX3_TMRB3POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ -} CTIMER_AUX3_TMRB3POL23_Enum; - -/* ============================================ CTIMER AUX3 TMRB3TINV [28..28] ============================================= */ -typedef enum { /*!< CTIMER_AUX3_TMRB3TINV */ - CTIMER_AUX3_TMRB3TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ - CTIMER_AUX3_TMRB3TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ -} CTIMER_AUX3_TMRB3TINV_Enum; - -/* =========================================== CTIMER AUX3 TMRB3NOSYNC [27..27] ============================================ */ -typedef enum { /*!< CTIMER_AUX3_TMRB3NOSYNC */ - CTIMER_AUX3_TMRB3NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ - CTIMER_AUX3_TMRB3NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ -} CTIMER_AUX3_TMRB3NOSYNC_Enum; - -/* ============================================ CTIMER AUX3 TMRB3TRIG [23..26] ============================================= */ -typedef enum { /*!< CTIMER_AUX3_TMRB3TRIG */ - CTIMER_AUX3_TMRB3TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ - CTIMER_AUX3_TMRB3TRIG_A3OUT = 1, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ - CTIMER_AUX3_TMRB3TRIG_B2OUT = 2, /*!< B2OUT : Trigger source is CTIMERB2 OUT. value. */ - CTIMER_AUX3_TMRB3TRIG_A2OUT = 3, /*!< A2OUT : Trigger source is CTIMERA2 OUT. value. */ - CTIMER_AUX3_TMRB3TRIG_A4OUT = 4, /*!< A4OUT : Trigger source is CTIMERA4 OUT. value. */ - CTIMER_AUX3_TMRB3TRIG_B4OUT = 5, /*!< B4OUT : Trigger source is CTIMERB4 OUT. value. */ - CTIMER_AUX3_TMRB3TRIG_A6OUT = 6, /*!< A6OUT : Trigger source is CTIMERA6 OUT. value. */ - CTIMER_AUX3_TMRB3TRIG_B6OUT = 7, /*!< B6OUT : Trigger source is CTIMERB6 OUT. value. */ - CTIMER_AUX3_TMRB3TRIG_B5OUT2 = 8, /*!< B5OUT2 : Trigger source is CTIMERB5 OUT2. value. */ - CTIMER_AUX3_TMRB3TRIG_A5OUT2 = 9, /*!< A5OUT2 : Trigger source is CTIMERA5 OUT2. value. */ - CTIMER_AUX3_TMRB3TRIG_A1OUT2 = 10, /*!< A1OUT2 : Trigger source is CTIMERA1 OUT2. value. */ - CTIMER_AUX3_TMRB3TRIG_B1OUT2 = 11, /*!< B1OUT2 : Trigger source is CTIMERB1 OUT2. value. */ - CTIMER_AUX3_TMRB3TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ - CTIMER_AUX3_TMRB3TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ - CTIMER_AUX3_TMRB3TRIG_B2OUT2DUAL = 14, /*!< B2OUT2DUAL : Trigger source is CTIMERB2 OUT2, dual edge. value. */ - CTIMER_AUX3_TMRB3TRIG_A2OUT2DUAL = 15, /*!< A2OUT2DUAL : Trigger source is CTIMERA2 OUT2, dual edge. value. */ -} CTIMER_AUX3_TMRB3TRIG_Enum; - -/* ============================================ CTIMER AUX3 TMRA3EN23 [14..14] ============================================= */ -typedef enum { /*!< CTIMER_AUX3_TMRA3EN23 */ - CTIMER_AUX3_TMRA3EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ - CTIMER_AUX3_TMRA3EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ -} CTIMER_AUX3_TMRA3EN23_Enum; - -/* ============================================ CTIMER AUX3 TMRA3POL23 [13..13] ============================================ */ -typedef enum { /*!< CTIMER_AUX3_TMRA3POL23 */ - CTIMER_AUX3_TMRA3POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ - CTIMER_AUX3_TMRA3POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ -} CTIMER_AUX3_TMRA3POL23_Enum; - -/* ============================================ CTIMER AUX3 TMRA3TINV [12..12] ============================================= */ -typedef enum { /*!< CTIMER_AUX3_TMRA3TINV */ - CTIMER_AUX3_TMRA3TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ - CTIMER_AUX3_TMRA3TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ -} CTIMER_AUX3_TMRA3TINV_Enum; - -/* =========================================== CTIMER AUX3 TMRA3NOSYNC [11..11] ============================================ */ -typedef enum { /*!< CTIMER_AUX3_TMRA3NOSYNC */ - CTIMER_AUX3_TMRA3NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ - CTIMER_AUX3_TMRA3NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ -} CTIMER_AUX3_TMRA3NOSYNC_Enum; - -/* ============================================= CTIMER AUX3 TMRA3TRIG [7..10] ============================================= */ -typedef enum { /*!< CTIMER_AUX3_TMRA3TRIG */ - CTIMER_AUX3_TMRA3TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ - CTIMER_AUX3_TMRA3TRIG_B3OUT = 1, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ - CTIMER_AUX3_TMRA3TRIG_B2OUT = 2, /*!< B2OUT : Trigger source is CTIMERB2 OUT. value. */ - CTIMER_AUX3_TMRA3TRIG_A2OUT = 3, /*!< A2OUT : Trigger source is CTIMERA2 OUT. value. */ - CTIMER_AUX3_TMRA3TRIG_A4OUT = 4, /*!< A4OUT : Trigger source is CTIMERA4 OUT. value. */ - CTIMER_AUX3_TMRA3TRIG_B4OUT = 5, /*!< B4OUT : Trigger source is CTIMERB4 OUT. value. */ - CTIMER_AUX3_TMRA3TRIG_A7OUT = 6, /*!< A7OUT : Trigger source is CTIMERA7 OUT. value. */ - CTIMER_AUX3_TMRA3TRIG_B7OUT = 7, /*!< B7OUT : Trigger source is CTIMERB7 OUT. value. */ - CTIMER_AUX3_TMRA3TRIG_B5OUT2 = 8, /*!< B5OUT2 : Trigger source is CTIMERB5 OUT2. value. */ - CTIMER_AUX3_TMRA3TRIG_A5OUT2 = 9, /*!< A5OUT2 : Trigger source is CTIMERA5 OUT2. value. */ - CTIMER_AUX3_TMRA3TRIG_A1OUT2 = 10, /*!< A1OUT2 : Trigger source is CTIMERA1 OUT2. value. */ - CTIMER_AUX3_TMRA3TRIG_B1OUT2 = 11, /*!< B1OUT2 : Trigger source is CTIMERB1 OUT2. value. */ - CTIMER_AUX3_TMRA3TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ - CTIMER_AUX3_TMRA3TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ - CTIMER_AUX3_TMRA3TRIG_B2OUT2DUAL = 14, /*!< B2OUT2DUAL : Trigger source is CTIMERB2 OUT2, dual edge. value. */ - CTIMER_AUX3_TMRA3TRIG_A2OUT2DUAL = 15, /*!< A2OUT2DUAL : Trigger source is CTIMERA2 OUT2, dual edge. value. */ -} CTIMER_AUX3_TMRA3TRIG_Enum; - -/* ========================================================= TMR4 ========================================================== */ -/* ======================================================== CMPRA4 ========================================================= */ -/* ======================================================== CMPRB4 ========================================================= */ -/* ========================================================= CTRL4 ========================================================= */ -/* ============================================= CTIMER CTRL4 CTLINK4 [31..31] ============================================= */ -typedef enum { /*!< CTIMER_CTRL4_CTLINK4 */ - CTIMER_CTRL4_CTLINK4_TWO_16BIT_TIMERS = 0, /*!< TWO_16BIT_TIMERS : Use A4/B4 timers as two independent 16-bit - timers (default). value. */ - CTIMER_CTRL4_CTLINK4_32BIT_TIMER = 1, /*!< 32BIT_TIMER : Link A4/B4 timers into a single 32-bit timer. - value. */ -} CTIMER_CTRL4_CTLINK4_Enum; - -/* ============================================ CTIMER CTRL4 TMRB4POL [28..28] ============================================= */ -typedef enum { /*!< CTIMER_CTRL4_TMRB4POL */ - CTIMER_CTRL4_TMRB4POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINB4 pin is the same as the - timer output. value. */ - CTIMER_CTRL4_TMRB4POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINB4 pin is the inverse of - the timer output. value. */ -} CTIMER_CTRL4_TMRB4POL_Enum; - -/* ============================================ CTIMER CTRL4 TMRB4CLR [27..27] ============================================= */ -typedef enum { /*!< CTIMER_CTRL4_TMRB4CLR */ - CTIMER_CTRL4_TMRB4CLR_RUN = 0, /*!< RUN : Allow counter/timer B4 to run value. */ - CTIMER_CTRL4_TMRB4CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer B4 at 0x0000. value. */ -} CTIMER_CTRL4_TMRB4CLR_Enum; - -/* ============================================ CTIMER CTRL4 TMRB4IE1 [26..26] ============================================= */ -typedef enum { /*!< CTIMER_CTRL4_TMRB4IE1 */ - CTIMER_CTRL4_TMRB4IE1_DIS = 0, /*!< DIS : Disable counter/timer B4 from generating an interrupt - based on COMPR1. value. */ - CTIMER_CTRL4_TMRB4IE1_EN = 1, /*!< EN : Enable counter/timer B4 to generate an interrupt based - on COMPR1. value. */ -} CTIMER_CTRL4_TMRB4IE1_Enum; - -/* ============================================ CTIMER CTRL4 TMRB4IE0 [25..25] ============================================= */ -typedef enum { /*!< CTIMER_CTRL4_TMRB4IE0 */ - CTIMER_CTRL4_TMRB4IE0_DIS = 0, /*!< DIS : Disable counter/timer B4 from generating an interrupt - based on COMPR0. value. */ - CTIMER_CTRL4_TMRB4IE0_EN = 1, /*!< EN : Enable counter/timer B4 to generate an interrupt based - on COMPR0 value. */ -} CTIMER_CTRL4_TMRB4IE0_Enum; - -/* ============================================= CTIMER CTRL4 TMRB4FN [22..24] ============================================= */ -typedef enum { /*!< CTIMER_CTRL4_TMRB4FN */ - CTIMER_CTRL4_TMRB4FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count - to CMPR0B4, stop. value. */ - CTIMER_CTRL4_TMRB4FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide - pulses). Count to CMPR0B4, restart. value. */ - CTIMER_CTRL4_TMRB4FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0B4, assert, - count to CMPR1B4, deassert, stop. value. */ - CTIMER_CTRL4_TMRB4FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0B4, assert, count - to CMPR1B4, deassert, restart. value. */ - CTIMER_CTRL4_TMRB4FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ - CTIMER_CTRL4_TMRB4FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ - CTIMER_CTRL4_TMRB4FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. - value. */ - CTIMER_CTRL4_TMRB4FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ -} CTIMER_CTRL4_TMRB4FN_Enum; - -/* ============================================ CTIMER CTRL4 TMRB4CLK [17..21] ============================================= */ -typedef enum { /*!< CTIMER_CTRL4_TMRB4CLK */ - CTIMER_CTRL4_TMRB4CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINB. value. */ - CTIMER_CTRL4_TMRB4CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ - CTIMER_CTRL4_TMRB4CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ - CTIMER_CTRL4_TMRB4CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ - CTIMER_CTRL4_TMRB4CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ - CTIMER_CTRL4_TMRB4CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ - CTIMER_CTRL4_TMRB4CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ - CTIMER_CTRL4_TMRB4CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ - CTIMER_CTRL4_TMRB4CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ - CTIMER_CTRL4_TMRB4CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ - CTIMER_CTRL4_TMRB4CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ - CTIMER_CTRL4_TMRB4CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ - CTIMER_CTRL4_TMRB4CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ - CTIMER_CTRL4_TMRB4CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ - CTIMER_CTRL4_TMRB4CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. - value. */ - CTIMER_CTRL4_TMRB4CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ - CTIMER_CTRL4_TMRB4CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ - CTIMER_CTRL4_TMRB4CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ - CTIMER_CTRL4_TMRB4CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ - CTIMER_CTRL4_TMRB4CLK_CTMRA4 = 20, /*!< CTMRA4 : Clock source is CTIMERA4 OUT. value. */ - CTIMER_CTRL4_TMRB4CLK_CTMRA1 = 21, /*!< CTMRA1 : Clock source is CTIMERA1 OUT. value. */ - CTIMER_CTRL4_TMRB4CLK_CTMRB1 = 22, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ - CTIMER_CTRL4_TMRB4CLK_CTMRA5 = 23, /*!< CTMRA5 : Clock source is CTIMERA5 OUT. value. */ - CTIMER_CTRL4_TMRB4CLK_CTMRB5 = 24, /*!< CTMRB5 : Clock source is CTIMERB5 OUT. value. */ - CTIMER_CTRL4_TMRB4CLK_CTMRB0 = 25, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ - CTIMER_CTRL4_TMRB4CLK_CTMRB2 = 26, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ - CTIMER_CTRL4_TMRB4CLK_CTMRB3 = 27, /*!< CTMRB3 : Clock source is CTIMERB3 OUT. value. */ - CTIMER_CTRL4_TMRB4CLK_CTMRB6 = 28, /*!< CTMRB6 : Clock source is CTIMERB6 OUT. value. */ - CTIMER_CTRL4_TMRB4CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ - CTIMER_CTRL4_TMRB4CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ - CTIMER_CTRL4_TMRB4CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ -} CTIMER_CTRL4_TMRB4CLK_Enum; - -/* ============================================= CTIMER CTRL4 TMRB4EN [16..16] ============================================= */ -typedef enum { /*!< CTIMER_CTRL4_TMRB4EN */ - CTIMER_CTRL4_TMRB4EN_DIS = 0, /*!< DIS : Counter/Timer B4 Disable. value. */ - CTIMER_CTRL4_TMRB4EN_EN = 1, /*!< EN : Counter/Timer B4 Enable. value. */ -} CTIMER_CTRL4_TMRB4EN_Enum; - -/* ============================================ CTIMER CTRL4 TMRA4POL [12..12] ============================================= */ -typedef enum { /*!< CTIMER_CTRL4_TMRA4POL */ - CTIMER_CTRL4_TMRA4POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINA4 pin is the same as the - timer output. value. */ - CTIMER_CTRL4_TMRA4POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINA4 pin is the inverse of - the timer output. value. */ -} CTIMER_CTRL4_TMRA4POL_Enum; - -/* ============================================ CTIMER CTRL4 TMRA4CLR [11..11] ============================================= */ -typedef enum { /*!< CTIMER_CTRL4_TMRA4CLR */ - CTIMER_CTRL4_TMRA4CLR_RUN = 0, /*!< RUN : Allow counter/timer A4 to run value. */ - CTIMER_CTRL4_TMRA4CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer A4 at 0x0000. value. */ -} CTIMER_CTRL4_TMRA4CLR_Enum; - -/* ============================================ CTIMER CTRL4 TMRA4IE1 [10..10] ============================================= */ -typedef enum { /*!< CTIMER_CTRL4_TMRA4IE1 */ - CTIMER_CTRL4_TMRA4IE1_DIS = 0, /*!< DIS : Disable counter/timer A4 from generating an interrupt - based on COMPR1. value. */ - CTIMER_CTRL4_TMRA4IE1_EN = 1, /*!< EN : Enable counter/timer A4 to generate an interrupt based - on COMPR1. value. */ -} CTIMER_CTRL4_TMRA4IE1_Enum; - -/* ============================================= CTIMER CTRL4 TMRA4IE0 [9..9] ============================================== */ -typedef enum { /*!< CTIMER_CTRL4_TMRA4IE0 */ - CTIMER_CTRL4_TMRA4IE0_DIS = 0, /*!< DIS : Disable counter/timer A4 from generating an interrupt - based on COMPR0. value. */ - CTIMER_CTRL4_TMRA4IE0_EN = 1, /*!< EN : Enable counter/timer A4 to generate an interrupt based - on COMPR0. value. */ -} CTIMER_CTRL4_TMRA4IE0_Enum; - -/* ============================================== CTIMER CTRL4 TMRA4FN [6..8] ============================================== */ -typedef enum { /*!< CTIMER_CTRL4_TMRA4FN */ - CTIMER_CTRL4_TMRA4FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count - to CMPR0A4, stop. value. */ - CTIMER_CTRL4_TMRA4FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide - pulses). Count to CMPR0A4, restart. value. */ - CTIMER_CTRL4_TMRA4FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0A4, assert, - count to CMPR1A4, deassert, stop. value. */ - CTIMER_CTRL4_TMRA4FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0A4, assert, count - to CMPR1A4, deassert, restart. value. */ - CTIMER_CTRL4_TMRA4FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ - CTIMER_CTRL4_TMRA4FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ - CTIMER_CTRL4_TMRA4FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. - value. */ - CTIMER_CTRL4_TMRA4FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ -} CTIMER_CTRL4_TMRA4FN_Enum; - -/* ============================================= CTIMER CTRL4 TMRA4CLK [1..5] ============================================== */ -typedef enum { /*!< CTIMER_CTRL4_TMRA4CLK */ - CTIMER_CTRL4_TMRA4CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINA. value. */ - CTIMER_CTRL4_TMRA4CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ - CTIMER_CTRL4_TMRA4CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ - CTIMER_CTRL4_TMRA4CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ - CTIMER_CTRL4_TMRA4CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ - CTIMER_CTRL4_TMRA4CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ - CTIMER_CTRL4_TMRA4CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ - CTIMER_CTRL4_TMRA4CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ - CTIMER_CTRL4_TMRA4CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ - CTIMER_CTRL4_TMRA4CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ - CTIMER_CTRL4_TMRA4CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ - CTIMER_CTRL4_TMRA4CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ - CTIMER_CTRL4_TMRA4CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ - CTIMER_CTRL4_TMRA4CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ - CTIMER_CTRL4_TMRA4CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. - value. */ - CTIMER_CTRL4_TMRA4CLK_HCLK_DIV4 = 15, /*!< HCLK_DIV4 : Clock source is HCLK / 4. value. */ - CTIMER_CTRL4_TMRA4CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ - CTIMER_CTRL4_TMRA4CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ - CTIMER_CTRL4_TMRA4CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ - CTIMER_CTRL4_TMRA4CLK_CTMRB4 = 20, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ - CTIMER_CTRL4_TMRA4CLK_CTMRA1 = 21, /*!< CTMRA1 : Clock source is CTIMERA1 OUT. value. */ - CTIMER_CTRL4_TMRA4CLK_CTMRB1 = 22, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ - CTIMER_CTRL4_TMRA4CLK_CTMRA5 = 23, /*!< CTMRA5 : Clock source is CTIMERA5 OUT. value. */ - CTIMER_CTRL4_TMRA4CLK_CTMRB5 = 24, /*!< CTMRB5 : Clock source is CTIMERB5 OUT. value. */ - CTIMER_CTRL4_TMRA4CLK_CTMRB0 = 25, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ - CTIMER_CTRL4_TMRA4CLK_CTMRB2 = 26, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ - CTIMER_CTRL4_TMRA4CLK_CTMRB3 = 27, /*!< CTMRB3 : Clock source is CTIMERB3 OUT. value. */ - CTIMER_CTRL4_TMRA4CLK_CTMRB6 = 28, /*!< CTMRB6 : Clock source is CTIMERB6 OUT. value. */ - CTIMER_CTRL4_TMRA4CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ - CTIMER_CTRL4_TMRA4CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ - CTIMER_CTRL4_TMRA4CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ -} CTIMER_CTRL4_TMRA4CLK_Enum; - -/* ============================================== CTIMER CTRL4 TMRA4EN [0..0] ============================================== */ -typedef enum { /*!< CTIMER_CTRL4_TMRA4EN */ - CTIMER_CTRL4_TMRA4EN_DIS = 0, /*!< DIS : Counter/Timer A4 Disable. value. */ - CTIMER_CTRL4_TMRA4EN_EN = 1, /*!< EN : Counter/Timer A4 Enable. value. */ -} CTIMER_CTRL4_TMRA4EN_Enum; - -/* ======================================================= CMPRAUXA4 ======================================================= */ -/* ======================================================= CMPRAUXB4 ======================================================= */ -/* ========================================================= AUX4 ========================================================== */ -/* ============================================ CTIMER AUX4 TMRB4EN23 [30..30] ============================================= */ -typedef enum { /*!< CTIMER_AUX4_TMRB4EN23 */ - CTIMER_AUX4_TMRB4EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ - CTIMER_AUX4_TMRB4EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ -} CTIMER_AUX4_TMRB4EN23_Enum; - -/* ============================================ CTIMER AUX4 TMRB4POL23 [29..29] ============================================ */ -typedef enum { /*!< CTIMER_AUX4_TMRB4POL23 */ - CTIMER_AUX4_TMRB4POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ - CTIMER_AUX4_TMRB4POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ -} CTIMER_AUX4_TMRB4POL23_Enum; - -/* ============================================ CTIMER AUX4 TMRB4TINV [28..28] ============================================= */ -typedef enum { /*!< CTIMER_AUX4_TMRB4TINV */ - CTIMER_AUX4_TMRB4TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ - CTIMER_AUX4_TMRB4TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ -} CTIMER_AUX4_TMRB4TINV_Enum; - -/* =========================================== CTIMER AUX4 TMRB4NOSYNC [27..27] ============================================ */ -typedef enum { /*!< CTIMER_AUX4_TMRB4NOSYNC */ - CTIMER_AUX4_TMRB4NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ - CTIMER_AUX4_TMRB4NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ -} CTIMER_AUX4_TMRB4NOSYNC_Enum; - -/* ============================================ CTIMER AUX4 TMRB4TRIG [23..26] ============================================= */ -typedef enum { /*!< CTIMER_AUX4_TMRB4TRIG */ - CTIMER_AUX4_TMRB4TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ - CTIMER_AUX4_TMRB4TRIG_A4OUT = 1, /*!< A4OUT : Trigger source is CTIMERA4 OUT. value. */ - CTIMER_AUX4_TMRB4TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ - CTIMER_AUX4_TMRB4TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ - CTIMER_AUX4_TMRB4TRIG_A7OUT = 4, /*!< A7OUT : Trigger source is CTIMERA7 OUT. value. */ - CTIMER_AUX4_TMRB4TRIG_B7OUT = 5, /*!< B7OUT : Trigger source is CTIMERB7 OUT. value. */ - CTIMER_AUX4_TMRB4TRIG_A1OUT = 6, /*!< A1OUT : Trigger source is CTIMERA1 OUT. value. */ - CTIMER_AUX4_TMRB4TRIG_B1OUT = 7, /*!< B1OUT : Trigger source is CTIMERB1 OUT. value. */ - CTIMER_AUX4_TMRB4TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ - CTIMER_AUX4_TMRB4TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ - CTIMER_AUX4_TMRB4TRIG_A1OUT2 = 10, /*!< A1OUT2 : Trigger source is CTIMERA1 OUT2. value. */ - CTIMER_AUX4_TMRB4TRIG_B1OUT2 = 11, /*!< B1OUT2 : Trigger source is CTIMERB1 OUT2. value. */ - CTIMER_AUX4_TMRB4TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ - CTIMER_AUX4_TMRB4TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ - CTIMER_AUX4_TMRB4TRIG_B5OUT2DUAL = 14, /*!< B5OUT2DUAL : Trigger source is CTIMERB5 OUT2, dual edge. value. */ - CTIMER_AUX4_TMRB4TRIG_A5OUT2DUAL = 15, /*!< A5OUT2DUAL : Trigger source is CTIMERA5 OUT2, dual edge. value. */ -} CTIMER_AUX4_TMRB4TRIG_Enum; - -/* ============================================ CTIMER AUX4 TMRA4EN23 [14..14] ============================================= */ -typedef enum { /*!< CTIMER_AUX4_TMRA4EN23 */ - CTIMER_AUX4_TMRA4EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ - CTIMER_AUX4_TMRA4EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ -} CTIMER_AUX4_TMRA4EN23_Enum; - -/* ============================================ CTIMER AUX4 TMRA4POL23 [13..13] ============================================ */ -typedef enum { /*!< CTIMER_AUX4_TMRA4POL23 */ - CTIMER_AUX4_TMRA4POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ - CTIMER_AUX4_TMRA4POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ -} CTIMER_AUX4_TMRA4POL23_Enum; - -/* ============================================ CTIMER AUX4 TMRA4TINV [12..12] ============================================= */ -typedef enum { /*!< CTIMER_AUX4_TMRA4TINV */ - CTIMER_AUX4_TMRA4TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ - CTIMER_AUX4_TMRA4TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ -} CTIMER_AUX4_TMRA4TINV_Enum; - -/* =========================================== CTIMER AUX4 TMRA4NOSYNC [11..11] ============================================ */ -typedef enum { /*!< CTIMER_AUX4_TMRA4NOSYNC */ - CTIMER_AUX4_TMRA4NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ - CTIMER_AUX4_TMRA4NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ -} CTIMER_AUX4_TMRA4NOSYNC_Enum; - -/* ============================================= CTIMER AUX4 TMRA4TRIG [7..10] ============================================= */ -typedef enum { /*!< CTIMER_AUX4_TMRA4TRIG */ - CTIMER_AUX4_TMRA4TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ - CTIMER_AUX4_TMRA4TRIG_B4OUT = 1, /*!< B4OUT : Trigger source is CTIMERB4 OUT. value. */ - CTIMER_AUX4_TMRA4TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ - CTIMER_AUX4_TMRA4TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ - CTIMER_AUX4_TMRA4TRIG_A6OUT = 4, /*!< A6OUT : Trigger source is CTIMERA6 OUT. value. */ - CTIMER_AUX4_TMRA4TRIG_B6OUT = 5, /*!< B6OUT : Trigger source is CTIMERB6 OUT. value. */ - CTIMER_AUX4_TMRA4TRIG_A2OUT = 6, /*!< A2OUT : Trigger source is CTIMERA2 OUT. value. */ - CTIMER_AUX4_TMRA4TRIG_B2OUT = 7, /*!< B2OUT : Trigger source is CTIMERB2 OUT. value. */ - CTIMER_AUX4_TMRA4TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ - CTIMER_AUX4_TMRA4TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ - CTIMER_AUX4_TMRA4TRIG_A1OUT2 = 10, /*!< A1OUT2 : Trigger source is CTIMERA1 OUT2. value. */ - CTIMER_AUX4_TMRA4TRIG_B1OUT2 = 11, /*!< B1OUT2 : Trigger source is CTIMERB1 OUT2. value. */ - CTIMER_AUX4_TMRA4TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ - CTIMER_AUX4_TMRA4TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ - CTIMER_AUX4_TMRA4TRIG_B5OUT2DUAL = 14, /*!< B5OUT2DUAL : Trigger source is CTIMERB5 OUT2, dual edge. value. */ - CTIMER_AUX4_TMRA4TRIG_A5OUT2DUAL = 15, /*!< A5OUT2DUAL : Trigger source is CTIMERA5 OUT2, dual edge. value. */ -} CTIMER_AUX4_TMRA4TRIG_Enum; - -/* ========================================================= TMR5 ========================================================== */ -/* ======================================================== CMPRA5 ========================================================= */ -/* ======================================================== CMPRB5 ========================================================= */ -/* ========================================================= CTRL5 ========================================================= */ -/* ============================================= CTIMER CTRL5 CTLINK5 [31..31] ============================================= */ -typedef enum { /*!< CTIMER_CTRL5_CTLINK5 */ - CTIMER_CTRL5_CTLINK5_TWO_16BIT_TIMERS = 0, /*!< TWO_16BIT_TIMERS : Use A5/B5 timers as two independent 16-bit - timers (default). value. */ - CTIMER_CTRL5_CTLINK5_32BIT_TIMER = 1, /*!< 32BIT_TIMER : Link A5/B5 timers into a single 32-bit timer. - value. */ -} CTIMER_CTRL5_CTLINK5_Enum; - -/* ============================================ CTIMER CTRL5 TMRB5POL [28..28] ============================================= */ -typedef enum { /*!< CTIMER_CTRL5_TMRB5POL */ - CTIMER_CTRL5_TMRB5POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINB5 pin is the same as the - timer output. value. */ - CTIMER_CTRL5_TMRB5POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINB5 pin is the inverse of - the timer output. value. */ -} CTIMER_CTRL5_TMRB5POL_Enum; - -/* ============================================ CTIMER CTRL5 TMRB5CLR [27..27] ============================================= */ -typedef enum { /*!< CTIMER_CTRL5_TMRB5CLR */ - CTIMER_CTRL5_TMRB5CLR_RUN = 0, /*!< RUN : Allow counter/timer B5 to run value. */ - CTIMER_CTRL5_TMRB5CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer B5 at 0x0000. value. */ -} CTIMER_CTRL5_TMRB5CLR_Enum; - -/* ============================================ CTIMER CTRL5 TMRB5IE1 [26..26] ============================================= */ -typedef enum { /*!< CTIMER_CTRL5_TMRB5IE1 */ - CTIMER_CTRL5_TMRB5IE1_DIS = 0, /*!< DIS : Disable counter/timer B5 from generating an interrupt - based on COMPR1. value. */ - CTIMER_CTRL5_TMRB5IE1_EN = 1, /*!< EN : Enable counter/timer B5 to generate an interrupt based - on COMPR1. value. */ -} CTIMER_CTRL5_TMRB5IE1_Enum; - -/* ============================================ CTIMER CTRL5 TMRB5IE0 [25..25] ============================================= */ -typedef enum { /*!< CTIMER_CTRL5_TMRB5IE0 */ - CTIMER_CTRL5_TMRB5IE0_DIS = 0, /*!< DIS : Disable counter/timer B5 from generating an interrupt - based on COMPR0. value. */ - CTIMER_CTRL5_TMRB5IE0_EN = 1, /*!< EN : Enable counter/timer B5 to generate an interrupt based - on COMPR0 value. */ -} CTIMER_CTRL5_TMRB5IE0_Enum; - -/* ============================================= CTIMER CTRL5 TMRB5FN [22..24] ============================================= */ -typedef enum { /*!< CTIMER_CTRL5_TMRB5FN */ - CTIMER_CTRL5_TMRB5FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count - to CMPR0B5, stop. value. */ - CTIMER_CTRL5_TMRB5FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide - pulses). Count to CMPR0B5, restart. value. */ - CTIMER_CTRL5_TMRB5FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0B5, assert, - count to CMPR1B5, deassert, stop. value. */ - CTIMER_CTRL5_TMRB5FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0B5, assert, count - to CMPR1B5, deassert, restart. value. */ - CTIMER_CTRL5_TMRB5FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ - CTIMER_CTRL5_TMRB5FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ - CTIMER_CTRL5_TMRB5FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. - value. */ - CTIMER_CTRL5_TMRB5FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ -} CTIMER_CTRL5_TMRB5FN_Enum; - -/* ============================================ CTIMER CTRL5 TMRB5CLK [17..21] ============================================= */ -typedef enum { /*!< CTIMER_CTRL5_TMRB5CLK */ - CTIMER_CTRL5_TMRB5CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINB. value. */ - CTIMER_CTRL5_TMRB5CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ - CTIMER_CTRL5_TMRB5CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ - CTIMER_CTRL5_TMRB5CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ - CTIMER_CTRL5_TMRB5CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ - CTIMER_CTRL5_TMRB5CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ - CTIMER_CTRL5_TMRB5CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ - CTIMER_CTRL5_TMRB5CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ - CTIMER_CTRL5_TMRB5CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ - CTIMER_CTRL5_TMRB5CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ - CTIMER_CTRL5_TMRB5CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ - CTIMER_CTRL5_TMRB5CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ - CTIMER_CTRL5_TMRB5CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ - CTIMER_CTRL5_TMRB5CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ - CTIMER_CTRL5_TMRB5CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. - value. */ - CTIMER_CTRL5_TMRB5CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ - CTIMER_CTRL5_TMRB5CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ - CTIMER_CTRL5_TMRB5CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ - CTIMER_CTRL5_TMRB5CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ - CTIMER_CTRL5_TMRB5CLK_CTMRA5 = 20, /*!< CTMRA5 : Clock source is CTIMERA5 OUT. value. */ - CTIMER_CTRL5_TMRB5CLK_CTMRA0 = 21, /*!< CTMRA0 : Clock source is CTIMERA0 OUT. value. */ - CTIMER_CTRL5_TMRB5CLK_CTMRB0 = 22, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ - CTIMER_CTRL5_TMRB5CLK_CTMRA6 = 23, /*!< CTMRA6 : Clock source is CTIMERA6 OUT. value. */ - CTIMER_CTRL5_TMRB5CLK_CTMRB6 = 24, /*!< CTMRB6 : Clock source is CTIMERB6 OUT. value. */ - CTIMER_CTRL5_TMRB5CLK_CTMRB1 = 25, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ - CTIMER_CTRL5_TMRB5CLK_CTMRB2 = 26, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ - CTIMER_CTRL5_TMRB5CLK_CTMRB3 = 27, /*!< CTMRB3 : Clock source is CTIMERB3 OUT. value. */ - CTIMER_CTRL5_TMRB5CLK_CTMRB4 = 28, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ - CTIMER_CTRL5_TMRB5CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ - CTIMER_CTRL5_TMRB5CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ - CTIMER_CTRL5_TMRB5CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ -} CTIMER_CTRL5_TMRB5CLK_Enum; - -/* ============================================= CTIMER CTRL5 TMRB5EN [16..16] ============================================= */ -typedef enum { /*!< CTIMER_CTRL5_TMRB5EN */ - CTIMER_CTRL5_TMRB5EN_DIS = 0, /*!< DIS : Counter/Timer B5 Disable. value. */ - CTIMER_CTRL5_TMRB5EN_EN = 1, /*!< EN : Counter/Timer B5 Enable. value. */ -} CTIMER_CTRL5_TMRB5EN_Enum; - -/* ============================================ CTIMER CTRL5 TMRA5POL [12..12] ============================================= */ -typedef enum { /*!< CTIMER_CTRL5_TMRA5POL */ - CTIMER_CTRL5_TMRA5POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINA5 pin is the same as the - timer output. value. */ - CTIMER_CTRL5_TMRA5POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINA5 pin is the inverse of - the timer output. value. */ -} CTIMER_CTRL5_TMRA5POL_Enum; - -/* ============================================ CTIMER CTRL5 TMRA5CLR [11..11] ============================================= */ -typedef enum { /*!< CTIMER_CTRL5_TMRA5CLR */ - CTIMER_CTRL5_TMRA5CLR_RUN = 0, /*!< RUN : Allow counter/timer A5 to run value. */ - CTIMER_CTRL5_TMRA5CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer A5 at 0x0000. value. */ -} CTIMER_CTRL5_TMRA5CLR_Enum; - -/* ============================================ CTIMER CTRL5 TMRA5IE1 [10..10] ============================================= */ -typedef enum { /*!< CTIMER_CTRL5_TMRA5IE1 */ - CTIMER_CTRL5_TMRA5IE1_DIS = 0, /*!< DIS : Disable counter/timer A5 from generating an interrupt - based on COMPR1. value. */ - CTIMER_CTRL5_TMRA5IE1_EN = 1, /*!< EN : Enable counter/timer A5 to generate an interrupt based - on COMPR1. value. */ -} CTIMER_CTRL5_TMRA5IE1_Enum; - -/* ============================================= CTIMER CTRL5 TMRA5IE0 [9..9] ============================================== */ -typedef enum { /*!< CTIMER_CTRL5_TMRA5IE0 */ - CTIMER_CTRL5_TMRA5IE0_DIS = 0, /*!< DIS : Disable counter/timer A5 from generating an interrupt - based on COMPR0. value. */ - CTIMER_CTRL5_TMRA5IE0_EN = 1, /*!< EN : Enable counter/timer A5 to generate an interrupt based - on COMPR0. value. */ -} CTIMER_CTRL5_TMRA5IE0_Enum; - -/* ============================================== CTIMER CTRL5 TMRA5FN [6..8] ============================================== */ -typedef enum { /*!< CTIMER_CTRL5_TMRA5FN */ - CTIMER_CTRL5_TMRA5FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count - to CMPR0A5, stop. value. */ - CTIMER_CTRL5_TMRA5FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide - pulses). Count to CMPR0A5, restart. value. */ - CTIMER_CTRL5_TMRA5FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0A5, assert, - count to CMPR1A5, deassert, stop. value. */ - CTIMER_CTRL5_TMRA5FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0A5, assert, count - to CMPR1A5, deassert, restart. value. */ - CTIMER_CTRL5_TMRA5FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ - CTIMER_CTRL5_TMRA5FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ - CTIMER_CTRL5_TMRA5FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. - value. */ - CTIMER_CTRL5_TMRA5FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ -} CTIMER_CTRL5_TMRA5FN_Enum; - -/* ============================================= CTIMER CTRL5 TMRA5CLK [1..5] ============================================== */ -typedef enum { /*!< CTIMER_CTRL5_TMRA5CLK */ - CTIMER_CTRL5_TMRA5CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINA. value. */ - CTIMER_CTRL5_TMRA5CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ - CTIMER_CTRL5_TMRA5CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ - CTIMER_CTRL5_TMRA5CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ - CTIMER_CTRL5_TMRA5CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ - CTIMER_CTRL5_TMRA5CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ - CTIMER_CTRL5_TMRA5CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ - CTIMER_CTRL5_TMRA5CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ - CTIMER_CTRL5_TMRA5CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ - CTIMER_CTRL5_TMRA5CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ - CTIMER_CTRL5_TMRA5CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ - CTIMER_CTRL5_TMRA5CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ - CTIMER_CTRL5_TMRA5CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ - CTIMER_CTRL5_TMRA5CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ - CTIMER_CTRL5_TMRA5CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. - value. */ - CTIMER_CTRL5_TMRA5CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ - CTIMER_CTRL5_TMRA5CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ - CTIMER_CTRL5_TMRA5CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ - CTIMER_CTRL5_TMRA5CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ - CTIMER_CTRL5_TMRA5CLK_CTMRB5 = 20, /*!< CTMRB5 : Clock source is CTIMERB5 OUT. value. */ - CTIMER_CTRL5_TMRA5CLK_CTMRA0 = 21, /*!< CTMRA0 : Clock source is CTIMERA0 OUT. value. */ - CTIMER_CTRL5_TMRA5CLK_CTMRB0 = 22, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ - CTIMER_CTRL5_TMRA5CLK_CTMRA6 = 23, /*!< CTMRA6 : Clock source is CTIMERA6 OUT. value. */ - CTIMER_CTRL5_TMRA5CLK_CTMRB6 = 24, /*!< CTMRB6 : Clock source is CTIMERB6 OUT. value. */ - CTIMER_CTRL5_TMRA5CLK_CTMRB1 = 25, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ - CTIMER_CTRL5_TMRA5CLK_CTMRB2 = 26, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ - CTIMER_CTRL5_TMRA5CLK_CTMRB3 = 27, /*!< CTMRB3 : Clock source is CTIMERB3 OUT. value. */ - CTIMER_CTRL5_TMRA5CLK_CTMRB4 = 28, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ - CTIMER_CTRL5_TMRA5CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ - CTIMER_CTRL5_TMRA5CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ - CTIMER_CTRL5_TMRA5CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ -} CTIMER_CTRL5_TMRA5CLK_Enum; - -/* ============================================== CTIMER CTRL5 TMRA5EN [0..0] ============================================== */ -typedef enum { /*!< CTIMER_CTRL5_TMRA5EN */ - CTIMER_CTRL5_TMRA5EN_DIS = 0, /*!< DIS : Counter/Timer A5 Disable. value. */ - CTIMER_CTRL5_TMRA5EN_EN = 1, /*!< EN : Counter/Timer A5 Enable. value. */ -} CTIMER_CTRL5_TMRA5EN_Enum; - -/* ======================================================= CMPRAUXA5 ======================================================= */ -/* ======================================================= CMPRAUXB5 ======================================================= */ -/* ========================================================= AUX5 ========================================================== */ -/* ============================================ CTIMER AUX5 TMRB5EN23 [30..30] ============================================= */ -typedef enum { /*!< CTIMER_AUX5_TMRB5EN23 */ - CTIMER_AUX5_TMRB5EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ - CTIMER_AUX5_TMRB5EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ -} CTIMER_AUX5_TMRB5EN23_Enum; - -/* ============================================ CTIMER AUX5 TMRB5POL23 [29..29] ============================================ */ -typedef enum { /*!< CTIMER_AUX5_TMRB5POL23 */ - CTIMER_AUX5_TMRB5POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ - CTIMER_AUX5_TMRB5POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ -} CTIMER_AUX5_TMRB5POL23_Enum; - -/* ============================================ CTIMER AUX5 TMRB5TINV [28..28] ============================================= */ -typedef enum { /*!< CTIMER_AUX5_TMRB5TINV */ - CTIMER_AUX5_TMRB5TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ - CTIMER_AUX5_TMRB5TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ -} CTIMER_AUX5_TMRB5TINV_Enum; - -/* =========================================== CTIMER AUX5 TMRB5NOSYNC [27..27] ============================================ */ -typedef enum { /*!< CTIMER_AUX5_TMRB5NOSYNC */ - CTIMER_AUX5_TMRB5NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ - CTIMER_AUX5_TMRB5NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ -} CTIMER_AUX5_TMRB5NOSYNC_Enum; - -/* ============================================ CTIMER AUX5 TMRB5TRIG [23..26] ============================================= */ -typedef enum { /*!< CTIMER_AUX5_TMRB5TRIG */ - CTIMER_AUX5_TMRB5TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ - CTIMER_AUX5_TMRB5TRIG_A5OUT = 1, /*!< A5OUT : Trigger source is CTIMERA5 OUT. value. */ - CTIMER_AUX5_TMRB5TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ - CTIMER_AUX5_TMRB5TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ - CTIMER_AUX5_TMRB5TRIG_A6OUT = 4, /*!< A6OUT : Trigger source is CTIMERA6 OUT. value. */ - CTIMER_AUX5_TMRB5TRIG_B6OUT = 5, /*!< B6OUT : Trigger source is CTIMERB6 OUT. value. */ - CTIMER_AUX5_TMRB5TRIG_A1OUT = 6, /*!< A1OUT : Trigger source is CTIMERA1 OUT. value. */ - CTIMER_AUX5_TMRB5TRIG_B1OUT = 7, /*!< B1OUT : Trigger source is CTIMERB1 OUT. value. */ - CTIMER_AUX5_TMRB5TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ - CTIMER_AUX5_TMRB5TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ - CTIMER_AUX5_TMRB5TRIG_A0OUT2 = 10, /*!< A0OUT2 : Trigger source is CTIMERA0 OUT2. value. */ - CTIMER_AUX5_TMRB5TRIG_B0OUT2 = 11, /*!< B0OUT2 : Trigger source is CTIMERB0 OUT2. value. */ - CTIMER_AUX5_TMRB5TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ - CTIMER_AUX5_TMRB5TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ - CTIMER_AUX5_TMRB5TRIG_B4OUT2DUAL = 14, /*!< B4OUT2DUAL : Trigger source is CTIMERB4 OUT2, dual edge. value. */ - CTIMER_AUX5_TMRB5TRIG_A4OUT2DUAL = 15, /*!< A4OUT2DUAL : Trigger source is CTIMERA4 OUT2, dual edge. value. */ -} CTIMER_AUX5_TMRB5TRIG_Enum; - -/* ============================================ CTIMER AUX5 TMRA5EN23 [14..14] ============================================= */ -typedef enum { /*!< CTIMER_AUX5_TMRA5EN23 */ - CTIMER_AUX5_TMRA5EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ - CTIMER_AUX5_TMRA5EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ -} CTIMER_AUX5_TMRA5EN23_Enum; - -/* ============================================ CTIMER AUX5 TMRA5POL23 [13..13] ============================================ */ -typedef enum { /*!< CTIMER_AUX5_TMRA5POL23 */ - CTIMER_AUX5_TMRA5POL23_NORMAL = 0, /*!< NORMAL : Upper output normal polarity value. */ - CTIMER_AUX5_TMRA5POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ -} CTIMER_AUX5_TMRA5POL23_Enum; - -/* ============================================ CTIMER AUX5 TMRA5TINV [12..12] ============================================= */ -typedef enum { /*!< CTIMER_AUX5_TMRA5TINV */ - CTIMER_AUX5_TMRA5TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ - CTIMER_AUX5_TMRA5TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ -} CTIMER_AUX5_TMRA5TINV_Enum; - -/* =========================================== CTIMER AUX5 TMRA5NOSYNC [11..11] ============================================ */ -typedef enum { /*!< CTIMER_AUX5_TMRA5NOSYNC */ - CTIMER_AUX5_TMRA5NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ - CTIMER_AUX5_TMRA5NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ -} CTIMER_AUX5_TMRA5NOSYNC_Enum; - -/* ============================================= CTIMER AUX5 TMRA5TRIG [7..10] ============================================= */ -typedef enum { /*!< CTIMER_AUX5_TMRA5TRIG */ - CTIMER_AUX5_TMRA5TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ - CTIMER_AUX5_TMRA5TRIG_B5OUT = 1, /*!< B5OUT : Trigger source is CTIMERB5 OUT. value. */ - CTIMER_AUX5_TMRA5TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ - CTIMER_AUX5_TMRA5TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ - CTIMER_AUX5_TMRA5TRIG_A4OUT = 4, /*!< A4OUT : Trigger source is CTIMERA4 OUT. value. */ - CTIMER_AUX5_TMRA5TRIG_B4OUT = 5, /*!< B4OUT : Trigger source is CTIMERB4 OUT. value. */ - CTIMER_AUX5_TMRA5TRIG_A2OUT = 6, /*!< A2OUT : Trigger source is CTIMERA2 OUT. value. */ - CTIMER_AUX5_TMRA5TRIG_B2OUT = 7, /*!< B2OUT : Trigger source is CTIMERB2 OUT. value. */ - CTIMER_AUX5_TMRA5TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ - CTIMER_AUX5_TMRA5TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ - CTIMER_AUX5_TMRA5TRIG_A0OUT2 = 10, /*!< A0OUT2 : Trigger source is CTIMERA0 OUT2. value. */ - CTIMER_AUX5_TMRA5TRIG_B0OUT2 = 11, /*!< B0OUT2 : Trigger source is CTIMERB0 OUT2. value. */ - CTIMER_AUX5_TMRA5TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ - CTIMER_AUX5_TMRA5TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ - CTIMER_AUX5_TMRA5TRIG_B4OUT2DUAL = 14, /*!< B4OUT2DUAL : Trigger source is CTIMERB4 OUT2, dual edge. value. */ - CTIMER_AUX5_TMRA5TRIG_A4OUT2DUAL = 15, /*!< A4OUT2DUAL : Trigger source is CTIMERA4 OUT2, dual edge. value. */ -} CTIMER_AUX5_TMRA5TRIG_Enum; - -/* ========================================================= TMR6 ========================================================== */ -/* ======================================================== CMPRA6 ========================================================= */ -/* ======================================================== CMPRB6 ========================================================= */ -/* ========================================================= CTRL6 ========================================================= */ -/* ============================================= CTIMER CTRL6 CTLINK6 [31..31] ============================================= */ -typedef enum { /*!< CTIMER_CTRL6_CTLINK6 */ - CTIMER_CTRL6_CTLINK6_TWO_16BIT_TIMERS = 0, /*!< TWO_16BIT_TIMERS : Use A6/B6 timers as two independent 16-bit - timers (default). value. */ - CTIMER_CTRL6_CTLINK6_32BIT_TIMER = 1, /*!< 32BIT_TIMER : Link A6/B6 timers into a single 32-bit timer. - value. */ -} CTIMER_CTRL6_CTLINK6_Enum; - -/* ============================================ CTIMER CTRL6 TMRB6POL [28..28] ============================================= */ -typedef enum { /*!< CTIMER_CTRL6_TMRB6POL */ - CTIMER_CTRL6_TMRB6POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINB6 pin is the same as the - timer output. value. */ - CTIMER_CTRL6_TMRB6POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINB6 pin is the inverse of - the timer output. value. */ -} CTIMER_CTRL6_TMRB6POL_Enum; - -/* ============================================ CTIMER CTRL6 TMRB6CLR [27..27] ============================================= */ -typedef enum { /*!< CTIMER_CTRL6_TMRB6CLR */ - CTIMER_CTRL6_TMRB6CLR_RUN = 0, /*!< RUN : Allow counter/timer B6 to run value. */ - CTIMER_CTRL6_TMRB6CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer B6 at 0x0000. value. */ -} CTIMER_CTRL6_TMRB6CLR_Enum; - -/* ============================================ CTIMER CTRL6 TMRB6IE1 [26..26] ============================================= */ -typedef enum { /*!< CTIMER_CTRL6_TMRB6IE1 */ - CTIMER_CTRL6_TMRB6IE1_DIS = 0, /*!< DIS : Disable counter/timer B6 from generating an interrupt - based on COMPR1. value. */ - CTIMER_CTRL6_TMRB6IE1_EN = 1, /*!< EN : Enable counter/timer B6 to generate an interrupt based - on COMPR1. value. */ -} CTIMER_CTRL6_TMRB6IE1_Enum; - -/* ============================================ CTIMER CTRL6 TMRB6IE0 [25..25] ============================================= */ -typedef enum { /*!< CTIMER_CTRL6_TMRB6IE0 */ - CTIMER_CTRL6_TMRB6IE0_DIS = 0, /*!< DIS : Disable counter/timer B6 from generating an interrupt - based on COMPR0. value. */ - CTIMER_CTRL6_TMRB6IE0_EN = 1, /*!< EN : Enable counter/timer B6 to generate an interrupt based - on COMPR0 value. */ -} CTIMER_CTRL6_TMRB6IE0_Enum; - -/* ============================================= CTIMER CTRL6 TMRB6FN [22..24] ============================================= */ -typedef enum { /*!< CTIMER_CTRL6_TMRB6FN */ - CTIMER_CTRL6_TMRB6FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count - to CMPR0B6, stop. value. */ - CTIMER_CTRL6_TMRB6FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide - pulses). Count to CMPR0B6, restart. value. */ - CTIMER_CTRL6_TMRB6FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0B6, assert, - count to CMPR1B6, deassert, stop. value. */ - CTIMER_CTRL6_TMRB6FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0B6, assert, count - to CMPR1B6, deassert, restart. value. */ - CTIMER_CTRL6_TMRB6FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ - CTIMER_CTRL6_TMRB6FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ - CTIMER_CTRL6_TMRB6FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. - value. */ - CTIMER_CTRL6_TMRB6FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ -} CTIMER_CTRL6_TMRB6FN_Enum; - -/* ============================================ CTIMER CTRL6 TMRB6CLK [17..21] ============================================= */ -typedef enum { /*!< CTIMER_CTRL6_TMRB6CLK */ - CTIMER_CTRL6_TMRB6CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINB. value. */ - CTIMER_CTRL6_TMRB6CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ - CTIMER_CTRL6_TMRB6CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ - CTIMER_CTRL6_TMRB6CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ - CTIMER_CTRL6_TMRB6CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ - CTIMER_CTRL6_TMRB6CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ - CTIMER_CTRL6_TMRB6CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ - CTIMER_CTRL6_TMRB6CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ - CTIMER_CTRL6_TMRB6CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ - CTIMER_CTRL6_TMRB6CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ - CTIMER_CTRL6_TMRB6CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ - CTIMER_CTRL6_TMRB6CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ - CTIMER_CTRL6_TMRB6CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ - CTIMER_CTRL6_TMRB6CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ - CTIMER_CTRL6_TMRB6CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. - value. */ - CTIMER_CTRL6_TMRB6CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ - CTIMER_CTRL6_TMRB6CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ - CTIMER_CTRL6_TMRB6CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ - CTIMER_CTRL6_TMRB6CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ - CTIMER_CTRL6_TMRB6CLK_CTMRA6 = 20, /*!< CTMRA6 : Clock source is CTIMERA6 OUT. value. */ - CTIMER_CTRL6_TMRB6CLK_CTMRA3 = 21, /*!< CTMRA3 : Clock source is CTIMERA3 OUT. value. */ - CTIMER_CTRL6_TMRB6CLK_CTMRB3 = 22, /*!< CTMRB3 : Clock source is CTIMERB3 OUT. value. */ - CTIMER_CTRL6_TMRB6CLK_CTMRA7 = 23, /*!< CTMRA7 : Clock source is CTIMERA7 OUT. value. */ - CTIMER_CTRL6_TMRB6CLK_CTMRB7 = 24, /*!< CTMRB7 : Clock source is CTIMERB7 OUT. value. */ - CTIMER_CTRL6_TMRB6CLK_CTMRB0 = 25, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ - CTIMER_CTRL6_TMRB6CLK_CTMRB1 = 26, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ - CTIMER_CTRL6_TMRB6CLK_CTMRB2 = 27, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ - CTIMER_CTRL6_TMRB6CLK_CTMRB4 = 28, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ - CTIMER_CTRL6_TMRB6CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ - CTIMER_CTRL6_TMRB6CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ - CTIMER_CTRL6_TMRB6CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ -} CTIMER_CTRL6_TMRB6CLK_Enum; - -/* ============================================= CTIMER CTRL6 TMRB6EN [16..16] ============================================= */ -typedef enum { /*!< CTIMER_CTRL6_TMRB6EN */ - CTIMER_CTRL6_TMRB6EN_DIS = 0, /*!< DIS : Counter/Timer B6 Disable. value. */ - CTIMER_CTRL6_TMRB6EN_EN = 1, /*!< EN : Counter/Timer B6 Enable. value. */ -} CTIMER_CTRL6_TMRB6EN_Enum; - -/* ============================================ CTIMER CTRL6 TMRA6POL [12..12] ============================================= */ -typedef enum { /*!< CTIMER_CTRL6_TMRA6POL */ - CTIMER_CTRL6_TMRA6POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINA6 pin is the same as the - timer output. value. */ - CTIMER_CTRL6_TMRA6POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINA6 pin is the inverse of - the timer output. value. */ -} CTIMER_CTRL6_TMRA6POL_Enum; - -/* ============================================ CTIMER CTRL6 TMRA6CLR [11..11] ============================================= */ -typedef enum { /*!< CTIMER_CTRL6_TMRA6CLR */ - CTIMER_CTRL6_TMRA6CLR_RUN = 0, /*!< RUN : Allow counter/timer A6 to run value. */ - CTIMER_CTRL6_TMRA6CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer A6 at 0x0000. value. */ -} CTIMER_CTRL6_TMRA6CLR_Enum; - -/* ============================================ CTIMER CTRL6 TMRA6IE1 [10..10] ============================================= */ -typedef enum { /*!< CTIMER_CTRL6_TMRA6IE1 */ - CTIMER_CTRL6_TMRA6IE1_DIS = 0, /*!< DIS : Disable counter/timer A6 from generating an interrupt - based on COMPR1. value. */ - CTIMER_CTRL6_TMRA6IE1_EN = 1, /*!< EN : Enable counter/timer A6 to generate an interrupt based - on COMPR1. value. */ -} CTIMER_CTRL6_TMRA6IE1_Enum; - -/* ============================================= CTIMER CTRL6 TMRA6IE0 [9..9] ============================================== */ -typedef enum { /*!< CTIMER_CTRL6_TMRA6IE0 */ - CTIMER_CTRL6_TMRA6IE0_DIS = 0, /*!< DIS : Disable counter/timer A6 from generating an interrupt - based on COMPR0. value. */ - CTIMER_CTRL6_TMRA6IE0_EN = 1, /*!< EN : Enable counter/timer A6 to generate an interrupt based - on COMPR0. value. */ -} CTIMER_CTRL6_TMRA6IE0_Enum; - -/* ============================================== CTIMER CTRL6 TMRA6FN [6..8] ============================================== */ -typedef enum { /*!< CTIMER_CTRL6_TMRA6FN */ - CTIMER_CTRL6_TMRA6FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count - to CMPR0A6, stop. value. */ - CTIMER_CTRL6_TMRA6FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide - pulses). Count to CMPR0A6, restart. value. */ - CTIMER_CTRL6_TMRA6FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0A6, assert, - count to CMPR1A6, deassert, stop. value. */ - CTIMER_CTRL6_TMRA6FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0A6, assert, count - to CMPR1A6, deassert, restart. value. */ - CTIMER_CTRL6_TMRA6FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ - CTIMER_CTRL6_TMRA6FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ - CTIMER_CTRL6_TMRA6FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. - value. */ - CTIMER_CTRL6_TMRA6FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ -} CTIMER_CTRL6_TMRA6FN_Enum; - -/* ============================================= CTIMER CTRL6 TMRA6CLK [1..5] ============================================== */ -typedef enum { /*!< CTIMER_CTRL6_TMRA6CLK */ - CTIMER_CTRL6_TMRA6CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINA. value. */ - CTIMER_CTRL6_TMRA6CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ - CTIMER_CTRL6_TMRA6CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ - CTIMER_CTRL6_TMRA6CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ - CTIMER_CTRL6_TMRA6CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ - CTIMER_CTRL6_TMRA6CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ - CTIMER_CTRL6_TMRA6CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ - CTIMER_CTRL6_TMRA6CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ - CTIMER_CTRL6_TMRA6CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ - CTIMER_CTRL6_TMRA6CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ - CTIMER_CTRL6_TMRA6CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ - CTIMER_CTRL6_TMRA6CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ - CTIMER_CTRL6_TMRA6CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ - CTIMER_CTRL6_TMRA6CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ - CTIMER_CTRL6_TMRA6CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. - value. */ - CTIMER_CTRL6_TMRA6CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ - CTIMER_CTRL6_TMRA6CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ - CTIMER_CTRL6_TMRA6CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ - CTIMER_CTRL6_TMRA6CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ - CTIMER_CTRL6_TMRA6CLK_CTMRB6 = 20, /*!< CTMRB6 : Clock source is CTIMERB6 OUT. value. */ - CTIMER_CTRL6_TMRA6CLK_CTMRA3 = 21, /*!< CTMRA3 : Clock source is CTIMERA3 OUT. value. */ - CTIMER_CTRL6_TMRA6CLK_CTMRB3 = 22, /*!< CTMRB3 : Clock source is CTIMERB3 OUT. value. */ - CTIMER_CTRL6_TMRA6CLK_CTMRA7 = 23, /*!< CTMRA7 : Clock source is CTIMERA7 OUT. value. */ - CTIMER_CTRL6_TMRA6CLK_CTMRB7 = 24, /*!< CTMRB7 : Clock source is CTIMERB7 OUT. value. */ - CTIMER_CTRL6_TMRA6CLK_CTMRB0 = 25, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ - CTIMER_CTRL6_TMRA6CLK_CTMRB1 = 26, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ - CTIMER_CTRL6_TMRA6CLK_CTMRB2 = 27, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ - CTIMER_CTRL6_TMRA6CLK_CTMRB4 = 28, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ - CTIMER_CTRL6_TMRA6CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ - CTIMER_CTRL6_TMRA6CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ - CTIMER_CTRL6_TMRA6CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ -} CTIMER_CTRL6_TMRA6CLK_Enum; - -/* ============================================== CTIMER CTRL6 TMRA6EN [0..0] ============================================== */ -typedef enum { /*!< CTIMER_CTRL6_TMRA6EN */ - CTIMER_CTRL6_TMRA6EN_DIS = 0, /*!< DIS : Counter/Timer A6 Disable. value. */ - CTIMER_CTRL6_TMRA6EN_EN = 1, /*!< EN : Counter/Timer A6 Enable. value. */ -} CTIMER_CTRL6_TMRA6EN_Enum; - -/* ======================================================= CMPRAUXA6 ======================================================= */ -/* ======================================================= CMPRAUXB6 ======================================================= */ -/* ========================================================= AUX6 ========================================================== */ -/* ============================================ CTIMER AUX6 TMRB6EN23 [30..30] ============================================= */ -typedef enum { /*!< CTIMER_AUX6_TMRB6EN23 */ - CTIMER_AUX6_TMRB6EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ - CTIMER_AUX6_TMRB6EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ -} CTIMER_AUX6_TMRB6EN23_Enum; - -/* ============================================ CTIMER AUX6 TMRB6POL23 [29..29] ============================================ */ -typedef enum { /*!< CTIMER_AUX6_TMRB6POL23 */ - CTIMER_AUX6_TMRB6POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ - CTIMER_AUX6_TMRB6POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ -} CTIMER_AUX6_TMRB6POL23_Enum; - -/* ============================================ CTIMER AUX6 TMRB6TINV [28..28] ============================================= */ -typedef enum { /*!< CTIMER_AUX6_TMRB6TINV */ - CTIMER_AUX6_TMRB6TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ - CTIMER_AUX6_TMRB6TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ -} CTIMER_AUX6_TMRB6TINV_Enum; - -/* =========================================== CTIMER AUX6 TMRB6NOSYNC [27..27] ============================================ */ -typedef enum { /*!< CTIMER_AUX6_TMRB6NOSYNC */ - CTIMER_AUX6_TMRB6NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ - CTIMER_AUX6_TMRB6NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ -} CTIMER_AUX6_TMRB6NOSYNC_Enum; - -/* ============================================ CTIMER AUX6 TMRB6TRIG [23..26] ============================================= */ -typedef enum { /*!< CTIMER_AUX6_TMRB6TRIG */ - CTIMER_AUX6_TMRB6TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ - CTIMER_AUX6_TMRB6TRIG_A6OUT = 1, /*!< A6OUT : Trigger source is CTIMERA6 OUT. value. */ - CTIMER_AUX6_TMRB6TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ - CTIMER_AUX6_TMRB6TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ - CTIMER_AUX6_TMRB6TRIG_A4OUT = 4, /*!< A4OUT : Trigger source is CTIMERA4 OUT. value. */ - CTIMER_AUX6_TMRB6TRIG_B4OUT = 5, /*!< B4OUT : Trigger source is CTIMERB4 OUT. value. */ - CTIMER_AUX6_TMRB6TRIG_A1OUT = 6, /*!< A1OUT : Trigger source is CTIMERA1 OUT. value. */ - CTIMER_AUX6_TMRB6TRIG_B1OUT = 7, /*!< B1OUT : Trigger source is CTIMERB1 OUT. value. */ - CTIMER_AUX6_TMRB6TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ - CTIMER_AUX6_TMRB6TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ - CTIMER_AUX6_TMRB6TRIG_A2OUT2 = 10, /*!< A2OUT2 : Trigger source is CTIMERA2 OUT2. value. */ - CTIMER_AUX6_TMRB6TRIG_B2OUT2 = 11, /*!< B2OUT2 : Trigger source is CTIMERB2 OUT2. value. */ - CTIMER_AUX6_TMRB6TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ - CTIMER_AUX6_TMRB6TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ - CTIMER_AUX6_TMRB6TRIG_B0OUT2DUAL = 14, /*!< B0OUT2DUAL : Trigger source is CTIMERB0 OUT2, dual edge. value. */ - CTIMER_AUX6_TMRB6TRIG_A0OUT2DUAL = 15, /*!< A0OUT2DUAL : Trigger source is CTIMERA0 OUT2, dual edge. value. */ -} CTIMER_AUX6_TMRB6TRIG_Enum; - -/* ============================================ CTIMER AUX6 TMRA6EN23 [14..14] ============================================= */ -typedef enum { /*!< CTIMER_AUX6_TMRA6EN23 */ - CTIMER_AUX6_TMRA6EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ - CTIMER_AUX6_TMRA6EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ -} CTIMER_AUX6_TMRA6EN23_Enum; - -/* ============================================ CTIMER AUX6 TMRA6POL23 [13..13] ============================================ */ -typedef enum { /*!< CTIMER_AUX6_TMRA6POL23 */ - CTIMER_AUX6_TMRA6POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ - CTIMER_AUX6_TMRA6POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ -} CTIMER_AUX6_TMRA6POL23_Enum; - -/* ============================================ CTIMER AUX6 TMRA6TINV [12..12] ============================================= */ -typedef enum { /*!< CTIMER_AUX6_TMRA6TINV */ - CTIMER_AUX6_TMRA6TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ - CTIMER_AUX6_TMRA6TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ -} CTIMER_AUX6_TMRA6TINV_Enum; - -/* =========================================== CTIMER AUX6 TMRA6NOSYNC [11..11] ============================================ */ -typedef enum { /*!< CTIMER_AUX6_TMRA6NOSYNC */ - CTIMER_AUX6_TMRA6NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ - CTIMER_AUX6_TMRA6NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ -} CTIMER_AUX6_TMRA6NOSYNC_Enum; - -/* ============================================= CTIMER AUX6 TMRA6TRIG [7..10] ============================================= */ -typedef enum { /*!< CTIMER_AUX6_TMRA6TRIG */ - CTIMER_AUX6_TMRA6TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ - CTIMER_AUX6_TMRA6TRIG_B6OUT = 1, /*!< B6OUT : Trigger source is CTIMERB6 OUT. value. */ - CTIMER_AUX6_TMRA6TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ - CTIMER_AUX6_TMRA6TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ - CTIMER_AUX6_TMRA6TRIG_A5OUT = 4, /*!< A5OUT : Trigger source is CTIMERA5 OUT. value. */ - CTIMER_AUX6_TMRA6TRIG_B5OUT = 5, /*!< B5OUT : Trigger source is CTIMERB5 OUT. value. */ - CTIMER_AUX6_TMRA6TRIG_A1OUT = 6, /*!< A1OUT : Trigger source is CTIMERA1 OUT. value. */ - CTIMER_AUX6_TMRA6TRIG_B1OUT = 7, /*!< B1OUT : Trigger source is CTIMERB1 OUT. value. */ - CTIMER_AUX6_TMRA6TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ - CTIMER_AUX6_TMRA6TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ - CTIMER_AUX6_TMRA6TRIG_A2OUT2 = 10, /*!< A2OUT2 : Trigger source is CTIMERA2 OUT2. value. */ - CTIMER_AUX6_TMRA6TRIG_B2OUT2 = 11, /*!< B2OUT2 : Trigger source is CTIMERBb OUT2. value. */ - CTIMER_AUX6_TMRA6TRIG_A5OUT2DUAL = 12, /*!< A5OUT2DUAL : Trigger source is CTIMERA5 OUT2, dual edge. value. */ - CTIMER_AUX6_TMRA6TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ - CTIMER_AUX6_TMRA6TRIG_B0OUT2DUAL = 14, /*!< B0OUT2DUAL : Trigger source is CTIMERB0 OUT2, dual edge. value. */ - CTIMER_AUX6_TMRA6TRIG_A0OUT2DUAL = 15, /*!< A0OUT2DUAL : Trigger source is CTIMERA0 OUT2, dual edge. value. */ -} CTIMER_AUX6_TMRA6TRIG_Enum; - -/* ========================================================= TMR7 ========================================================== */ -/* ======================================================== CMPRA7 ========================================================= */ -/* ======================================================== CMPRB7 ========================================================= */ -/* ========================================================= CTRL7 ========================================================= */ -/* ============================================= CTIMER CTRL7 CTLINK7 [31..31] ============================================= */ -typedef enum { /*!< CTIMER_CTRL7_CTLINK7 */ - CTIMER_CTRL7_CTLINK7_TWO_16BIT_TIMERS = 0, /*!< TWO_16BIT_TIMERS : Use A7/B7 timers as two independent 16-bit - timers (default). value. */ - CTIMER_CTRL7_CTLINK7_32BIT_TIMER = 1, /*!< 32BIT_TIMER : Link A7/B7 timers into a single 32-bit timer. - value. */ -} CTIMER_CTRL7_CTLINK7_Enum; - -/* ============================================ CTIMER CTRL7 TMRB7POL [28..28] ============================================= */ -typedef enum { /*!< CTIMER_CTRL7_TMRB7POL */ - CTIMER_CTRL7_TMRB7POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINB7 pin is the same as the - timer output. value. */ - CTIMER_CTRL7_TMRB7POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINB7 pin is the inverse of - the timer output. value. */ -} CTIMER_CTRL7_TMRB7POL_Enum; - -/* ============================================ CTIMER CTRL7 TMRB7CLR [27..27] ============================================= */ -typedef enum { /*!< CTIMER_CTRL7_TMRB7CLR */ - CTIMER_CTRL7_TMRB7CLR_RUN = 0, /*!< RUN : Allow counter/timer B7 to run value. */ - CTIMER_CTRL7_TMRB7CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer B7 at 0x0000. value. */ -} CTIMER_CTRL7_TMRB7CLR_Enum; - -/* ============================================ CTIMER CTRL7 TMRB7IE1 [26..26] ============================================= */ -typedef enum { /*!< CTIMER_CTRL7_TMRB7IE1 */ - CTIMER_CTRL7_TMRB7IE1_DIS = 0, /*!< DIS : Disable counter/timer B7 from generating an interrupt - based on COMPR1. value. */ - CTIMER_CTRL7_TMRB7IE1_EN = 1, /*!< EN : Enable counter/timer B7 to generate an interrupt based - on COMPR1. value. */ -} CTIMER_CTRL7_TMRB7IE1_Enum; - -/* ============================================ CTIMER CTRL7 TMRB7IE0 [25..25] ============================================= */ -typedef enum { /*!< CTIMER_CTRL7_TMRB7IE0 */ - CTIMER_CTRL7_TMRB7IE0_DIS = 0, /*!< DIS : Disable counter/timer B7 from generating an interrupt - based on COMPR0. value. */ - CTIMER_CTRL7_TMRB7IE0_EN = 1, /*!< EN : Enable counter/timer B7 to generate an interrupt based - on COMPR0 value. */ -} CTIMER_CTRL7_TMRB7IE0_Enum; - -/* ============================================= CTIMER CTRL7 TMRB7FN [22..24] ============================================= */ -typedef enum { /*!< CTIMER_CTRL7_TMRB7FN */ - CTIMER_CTRL7_TMRB7FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count - to CMPR0B7, stop. value. */ - CTIMER_CTRL7_TMRB7FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide - pulses). Count to CMPR0B7, restart. value. */ - CTIMER_CTRL7_TMRB7FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0B7, assert, - count to CMPR1B7, deassert, stop. value. */ - CTIMER_CTRL7_TMRB7FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0B7, assert, count - to CMPR1B7, deassert, restart. value. */ - CTIMER_CTRL7_TMRB7FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ - CTIMER_CTRL7_TMRB7FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ - CTIMER_CTRL7_TMRB7FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. - value. */ - CTIMER_CTRL7_TMRB7FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ -} CTIMER_CTRL7_TMRB7FN_Enum; - -/* ============================================ CTIMER CTRL7 TMRB7CLK [17..21] ============================================= */ -typedef enum { /*!< CTIMER_CTRL7_TMRB7CLK */ - CTIMER_CTRL7_TMRB7CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINB. value. */ - CTIMER_CTRL7_TMRB7CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ - CTIMER_CTRL7_TMRB7CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ - CTIMER_CTRL7_TMRB7CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ - CTIMER_CTRL7_TMRB7CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ - CTIMER_CTRL7_TMRB7CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ - CTIMER_CTRL7_TMRB7CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ - CTIMER_CTRL7_TMRB7CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ - CTIMER_CTRL7_TMRB7CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ - CTIMER_CTRL7_TMRB7CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ - CTIMER_CTRL7_TMRB7CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ - CTIMER_CTRL7_TMRB7CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ - CTIMER_CTRL7_TMRB7CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ - CTIMER_CTRL7_TMRB7CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ - CTIMER_CTRL7_TMRB7CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. - value. */ - CTIMER_CTRL7_TMRB7CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ - CTIMER_CTRL7_TMRB7CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ - CTIMER_CTRL7_TMRB7CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ - CTIMER_CTRL7_TMRB7CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ - CTIMER_CTRL7_TMRB7CLK_CTMRA7 = 20, /*!< CTMRA7 : Clock source is CTIMERA7 OUT. value. */ - CTIMER_CTRL7_TMRB7CLK_CTMRA2 = 21, /*!< CTMRA2 : Clock source is CTIMERA2 OUT. value. */ - CTIMER_CTRL7_TMRB7CLK_CTMRB2 = 22, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ - CTIMER_CTRL7_TMRB7CLK_CTMRA0 = 23, /*!< CTMRA0 : Clock source is CTIMERA0 OUT. value. */ - CTIMER_CTRL7_TMRB7CLK_CTMRB0 = 24, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ - CTIMER_CTRL7_TMRB7CLK_CTMRB1 = 25, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ - CTIMER_CTRL7_TMRB7CLK_CTMRB3 = 26, /*!< CTMRB3 : Clock source is CTIMERB3 OUT. value. */ - CTIMER_CTRL7_TMRB7CLK_CTMRB4 = 27, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ - CTIMER_CTRL7_TMRB7CLK_CTMRB5 = 28, /*!< CTMRB5 : Clock source is CTIMERB5 OUT. value. */ - CTIMER_CTRL7_TMRB7CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ - CTIMER_CTRL7_TMRB7CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ - CTIMER_CTRL7_TMRB7CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ -} CTIMER_CTRL7_TMRB7CLK_Enum; - -/* ============================================= CTIMER CTRL7 TMRB7EN [16..16] ============================================= */ -typedef enum { /*!< CTIMER_CTRL7_TMRB7EN */ - CTIMER_CTRL7_TMRB7EN_DIS = 0, /*!< DIS : Counter/Timer B7 Disable. value. */ - CTIMER_CTRL7_TMRB7EN_EN = 1, /*!< EN : Counter/Timer B7 Enable. value. */ -} CTIMER_CTRL7_TMRB7EN_Enum; - -/* ============================================ CTIMER CTRL7 TMRA7POL [12..12] ============================================= */ -typedef enum { /*!< CTIMER_CTRL7_TMRA7POL */ - CTIMER_CTRL7_TMRA7POL_NORMAL = 0, /*!< NORMAL : The polarity of the TMRPINA7 pin is the same as the - timer output. value. */ - CTIMER_CTRL7_TMRA7POL_INVERTED = 1, /*!< INVERTED : The polarity of the TMRPINA7 pin is the inverse of - the timer output. value. */ -} CTIMER_CTRL7_TMRA7POL_Enum; - -/* ============================================ CTIMER CTRL7 TMRA7CLR [11..11] ============================================= */ -typedef enum { /*!< CTIMER_CTRL7_TMRA7CLR */ - CTIMER_CTRL7_TMRA7CLR_RUN = 0, /*!< RUN : Allow counter/timer A7 to run value. */ - CTIMER_CTRL7_TMRA7CLR_CLEAR = 1, /*!< CLEAR : Holds counter/timer A7 at 0x0000. value. */ -} CTIMER_CTRL7_TMRA7CLR_Enum; - -/* ============================================ CTIMER CTRL7 TMRA7IE1 [10..10] ============================================= */ -typedef enum { /*!< CTIMER_CTRL7_TMRA7IE1 */ - CTIMER_CTRL7_TMRA7IE1_DIS = 0, /*!< DIS : Disable counter/timer A7 from generating an interrupt - based on COMPR1. value. */ - CTIMER_CTRL7_TMRA7IE1_EN = 1, /*!< EN : Enable counter/timer A7 to generate an interrupt based - on COMPR1. value. */ -} CTIMER_CTRL7_TMRA7IE1_Enum; - -/* ============================================= CTIMER CTRL7 TMRA7IE0 [9..9] ============================================== */ -typedef enum { /*!< CTIMER_CTRL7_TMRA7IE0 */ - CTIMER_CTRL7_TMRA7IE0_DIS = 0, /*!< DIS : Disable counter/timer A7 from generating an interrupt - based on COMPR0. value. */ - CTIMER_CTRL7_TMRA7IE0_EN = 1, /*!< EN : Enable counter/timer A7 to generate an interrupt based - on COMPR0. value. */ -} CTIMER_CTRL7_TMRA7IE0_Enum; - -/* ============================================== CTIMER CTRL7 TMRA7FN [6..8] ============================================== */ -typedef enum { /*!< CTIMER_CTRL7_TMRA7FN */ - CTIMER_CTRL7_TMRA7FN_SINGLECOUNT = 0, /*!< SINGLECOUNT : Single count (output toggles and sticks). Count - to CMPR0A7, stop. value. */ - CTIMER_CTRL7_TMRA7FN_REPEATEDCOUNT = 1, /*!< REPEATEDCOUNT : Repeated count (periodic 1-clock-cycle-wide - pulses). Count to CMPR0A7, restart. value. */ - CTIMER_CTRL7_TMRA7FN_PULSE_ONCE = 2, /*!< PULSE_ONCE : Pulse once (aka one-shot). Count to CMPR0A7, assert, - count to CMPR1A7, deassert, stop. value. */ - CTIMER_CTRL7_TMRA7FN_PULSE_CONT = 3, /*!< PULSE_CONT : Pulse continously. Count to CMPR0A7, assert, count - to CMPR1A7, deassert, restart. value. */ - CTIMER_CTRL7_TMRA7FN_SINGLEPATTERN = 4, /*!< SINGLEPATTERN : Single pattern. value. */ - CTIMER_CTRL7_TMRA7FN_REPEATPATTERN = 5, /*!< REPEATPATTERN : Repeated pattern. value. */ - CTIMER_CTRL7_TMRA7FN_CONTINUOUS = 6, /*!< CONTINUOUS : Continuous run (aka Free Run). Count continuously. - value. */ - CTIMER_CTRL7_TMRA7FN_ALTPWN = 7, /*!< ALTPWN : Alternate PWM value. */ -} CTIMER_CTRL7_TMRA7FN_Enum; - -/* ============================================= CTIMER CTRL7 TMRA7CLK [1..5] ============================================== */ -typedef enum { /*!< CTIMER_CTRL7_TMRA7CLK */ - CTIMER_CTRL7_TMRA7CLK_TMRPIN = 0, /*!< TMRPIN : Clock source is TMRPINA. value. */ - CTIMER_CTRL7_TMRA7CLK_HFRC_DIV4 = 1, /*!< HFRC_DIV4 : Clock source is the HFRC / 4 value. */ - CTIMER_CTRL7_TMRA7CLK_HFRC_DIV16 = 2, /*!< HFRC_DIV16 : Clock source is HFRC / 16 value. */ - CTIMER_CTRL7_TMRA7CLK_HFRC_DIV256 = 3, /*!< HFRC_DIV256 : Clock source is HFRC / 256 value. */ - CTIMER_CTRL7_TMRA7CLK_HFRC_DIV1024 = 4, /*!< HFRC_DIV1024 : Clock source is HFRC / 1024 value. */ - CTIMER_CTRL7_TMRA7CLK_HFRC_DIV4K = 5, /*!< HFRC_DIV4K : Clock source is HFRC / 4096 value. */ - CTIMER_CTRL7_TMRA7CLK_XT = 6, /*!< XT : Clock source is the XT (uncalibrated). value. */ - CTIMER_CTRL7_TMRA7CLK_XT_DIV2 = 7, /*!< XT_DIV2 : Clock source is XT / 2 value. */ - CTIMER_CTRL7_TMRA7CLK_XT_DIV16 = 8, /*!< XT_DIV16 : Clock source is XT / 16 value. */ - CTIMER_CTRL7_TMRA7CLK_XT_DIV128 = 9, /*!< XT_DIV128 : Clock source is XT / 128 value. */ - CTIMER_CTRL7_TMRA7CLK_LFRC_DIV2 = 10, /*!< LFRC_DIV2 : Clock source is LFRC / 2 value. */ - CTIMER_CTRL7_TMRA7CLK_LFRC_DIV32 = 11, /*!< LFRC_DIV32 : Clock source is LFRC / 32 value. */ - CTIMER_CTRL7_TMRA7CLK_LFRC_DIV1K = 12, /*!< LFRC_DIV1K : Clock source is LFRC / 1024 value. */ - CTIMER_CTRL7_TMRA7CLK_LFRC = 13, /*!< LFRC : Clock source is LFRC value. */ - CTIMER_CTRL7_TMRA7CLK_RTC_100HZ = 14, /*!< RTC_100HZ : Clock source is 100 Hz from the current RTC oscillator. - value. */ - CTIMER_CTRL7_TMRA7CLK_HCLK = 15, /*!< HCLK : Clock source is HCLK. value. */ - CTIMER_CTRL7_TMRA7CLK_XT_DIV4 = 16, /*!< XT_DIV4 : Clock source is XT / 4 value. */ - CTIMER_CTRL7_TMRA7CLK_XT_DIV8 = 17, /*!< XT_DIV8 : Clock source is XT / 8 value. */ - CTIMER_CTRL7_TMRA7CLK_XT_DIV32 = 18, /*!< XT_DIV32 : Clock source is XT / 32 value. */ - CTIMER_CTRL7_TMRA7CLK_CTMRB7 = 20, /*!< CTMRB7 : Clock source is CTIMERB7 OUT. value. */ - CTIMER_CTRL7_TMRA7CLK_CTMRA2 = 21, /*!< CTMRA2 : Clock source is CTIMERA2 OUT. value. */ - CTIMER_CTRL7_TMRA7CLK_CTMRB2 = 22, /*!< CTMRB2 : Clock source is CTIMERB2 OUT. value. */ - CTIMER_CTRL7_TMRA7CLK_CTMRA0 = 23, /*!< CTMRA0 : Clock source is CTIMERA0 OUT. value. */ - CTIMER_CTRL7_TMRA7CLK_CTMRB0 = 24, /*!< CTMRB0 : Clock source is CTIMERB0 OUT. value. */ - CTIMER_CTRL7_TMRA7CLK_CTMRB1 = 25, /*!< CTMRB1 : Clock source is CTIMERB1 OUT. value. */ - CTIMER_CTRL7_TMRA7CLK_CTMRB3 = 26, /*!< CTMRB3 : Clock source is CTIMERB3 OUT. value. */ - CTIMER_CTRL7_TMRA7CLK_CTMRB4 = 27, /*!< CTMRB4 : Clock source is CTIMERB4 OUT. value. */ - CTIMER_CTRL7_TMRA7CLK_CTMRB5 = 28, /*!< CTMRB5 : Clock source is CTIMERB5 OUT. value. */ - CTIMER_CTRL7_TMRA7CLK_BUCKBLE = 29, /*!< BUCKBLE : Clock source is BLE buck converter TON pulses. value. */ - CTIMER_CTRL7_TMRA7CLK_BUCKB = 30, /*!< BUCKB : Clock source is Memory buck converter TON pulses. value. */ - CTIMER_CTRL7_TMRA7CLK_BUCKA = 31, /*!< BUCKA : Clock source is CPU buck converter TON pulses. value. */ -} CTIMER_CTRL7_TMRA7CLK_Enum; - -/* ============================================== CTIMER CTRL7 TMRA7EN [0..0] ============================================== */ -typedef enum { /*!< CTIMER_CTRL7_TMRA7EN */ - CTIMER_CTRL7_TMRA7EN_DIS = 0, /*!< DIS : Counter/Timer A7 Disable. value. */ - CTIMER_CTRL7_TMRA7EN_EN = 1, /*!< EN : Counter/Timer A7 Enable. value. */ -} CTIMER_CTRL7_TMRA7EN_Enum; - -/* ======================================================= CMPRAUXA7 ======================================================= */ -/* ======================================================= CMPRAUXB7 ======================================================= */ -/* ========================================================= AUX7 ========================================================== */ -/* ============================================ CTIMER AUX7 TMRB7EN23 [30..30] ============================================= */ -typedef enum { /*!< CTIMER_AUX7_TMRB7EN23 */ - CTIMER_AUX7_TMRB7EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ - CTIMER_AUX7_TMRB7EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ -} CTIMER_AUX7_TMRB7EN23_Enum; - -/* ============================================ CTIMER AUX7 TMRB7POL23 [29..29] ============================================ */ -typedef enum { /*!< CTIMER_AUX7_TMRB7POL23 */ - CTIMER_AUX7_TMRB7POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ - CTIMER_AUX7_TMRB7POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ -} CTIMER_AUX7_TMRB7POL23_Enum; - -/* ============================================ CTIMER AUX7 TMRB7TINV [28..28] ============================================= */ -typedef enum { /*!< CTIMER_AUX7_TMRB7TINV */ - CTIMER_AUX7_TMRB7TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ - CTIMER_AUX7_TMRB7TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ -} CTIMER_AUX7_TMRB7TINV_Enum; - -/* =========================================== CTIMER AUX7 TMRB7NOSYNC [27..27] ============================================ */ -typedef enum { /*!< CTIMER_AUX7_TMRB7NOSYNC */ - CTIMER_AUX7_TMRB7NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ - CTIMER_AUX7_TMRB7NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ -} CTIMER_AUX7_TMRB7NOSYNC_Enum; - -/* ============================================ CTIMER AUX7 TMRB7TRIG [23..26] ============================================= */ -typedef enum { /*!< CTIMER_AUX7_TMRB7TRIG */ - CTIMER_AUX7_TMRB7TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ - CTIMER_AUX7_TMRB7TRIG_A7OUT = 1, /*!< A7OUT : Trigger source is CTIMERA7 OUT. value. */ - CTIMER_AUX7_TMRB7TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ - CTIMER_AUX7_TMRB7TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ - CTIMER_AUX7_TMRB7TRIG_A5OUT = 4, /*!< A5OUT : Trigger source is CTIMERA5 OUT. value. */ - CTIMER_AUX7_TMRB7TRIG_B5OUT = 5, /*!< B5OUT : Trigger source is CTIMERB5 OUT. value. */ - CTIMER_AUX7_TMRB7TRIG_A2OUT = 6, /*!< A2OUT : Trigger source is CTIMERA2 OUT. value. */ - CTIMER_AUX7_TMRB7TRIG_B2OUT = 7, /*!< B2OUT : Trigger source is CTIMERB2 OUT. value. */ - CTIMER_AUX7_TMRB7TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ - CTIMER_AUX7_TMRB7TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ - CTIMER_AUX7_TMRB7TRIG_A2OUT2 = 10, /*!< A2OUT2 : Trigger source is CTIMERA2 OUT2. value. */ - CTIMER_AUX7_TMRB7TRIG_B2OUT2 = 11, /*!< B2OUT2 : Trigger source is CTIMERB2 OUT2. value. */ - CTIMER_AUX7_TMRB7TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ - CTIMER_AUX7_TMRB7TRIG_A7OUT2DUAL = 13, /*!< A7OUT2DUAL : Trigger source is CTIMERA7 OUT2, dual edge. value. */ - CTIMER_AUX7_TMRB7TRIG_B1OUT2DUAL = 14, /*!< B1OUT2DUAL : Trigger source is CTIMERB1 OUT2, dual edge. value. */ - CTIMER_AUX7_TMRB7TRIG_A1OUT2DUAL = 15, /*!< A1OUT2DUAL : Trigger source is CTIMERA1 OUT2, dual edge. value. */ -} CTIMER_AUX7_TMRB7TRIG_Enum; - -/* ============================================ CTIMER AUX7 TMRA7EN23 [14..14] ============================================= */ -typedef enum { /*!< CTIMER_AUX7_TMRA7EN23 */ - CTIMER_AUX7_TMRA7EN23_DIS = 1, /*!< DIS : Disable enhanced functions. value. */ - CTIMER_AUX7_TMRA7EN23_EN = 0, /*!< EN : Enable enhanced functions. value. */ -} CTIMER_AUX7_TMRA7EN23_Enum; - -/* ============================================ CTIMER AUX7 TMRA7POL23 [13..13] ============================================ */ -typedef enum { /*!< CTIMER_AUX7_TMRA7POL23 */ - CTIMER_AUX7_TMRA7POL23_NORM = 0, /*!< NORM : Upper output normal polarity value. */ - CTIMER_AUX7_TMRA7POL23_INV = 1, /*!< INV : Upper output inverted polarity. value. */ -} CTIMER_AUX7_TMRA7POL23_Enum; - -/* ============================================ CTIMER AUX7 TMRA7TINV [12..12] ============================================= */ -typedef enum { /*!< CTIMER_AUX7_TMRA7TINV */ - CTIMER_AUX7_TMRA7TINV_DIS = 0, /*!< DIS : Disable invert on trigger value. */ - CTIMER_AUX7_TMRA7TINV_EN = 1, /*!< EN : Enable invert on trigger value. */ -} CTIMER_AUX7_TMRA7TINV_Enum; - -/* =========================================== CTIMER AUX7 TMRA7NOSYNC [11..11] ============================================ */ -typedef enum { /*!< CTIMER_AUX7_TMRA7NOSYNC */ - CTIMER_AUX7_TMRA7NOSYNC_DIS = 0, /*!< DIS : Synchronization on source clock value. */ - CTIMER_AUX7_TMRA7NOSYNC_NOSYNC = 1, /*!< NOSYNC : No synchronization on source clock value. */ -} CTIMER_AUX7_TMRA7NOSYNC_Enum; - -/* ============================================= CTIMER AUX7 TMRA7TRIG [7..10] ============================================= */ -typedef enum { /*!< CTIMER_AUX7_TMRA7TRIG */ - CTIMER_AUX7_TMRA7TRIG_DIS = 0, /*!< DIS : Trigger source is disabled. value. */ - CTIMER_AUX7_TMRA7TRIG_B7OUT = 1, /*!< B7OUT : Trigger source is CTIMERB7 OUT. value. */ - CTIMER_AUX7_TMRA7TRIG_B3OUT = 2, /*!< B3OUT : Trigger source is CTIMERB3 OUT. value. */ - CTIMER_AUX7_TMRA7TRIG_A3OUT = 3, /*!< A3OUT : Trigger source is CTIMERA3 OUT. value. */ - CTIMER_AUX7_TMRA7TRIG_A1OUT = 4, /*!< A1OUT : Trigger source is CTIMERA1 OUT. value. */ - CTIMER_AUX7_TMRA7TRIG_B1OUT = 5, /*!< B1OUT : Trigger source is CTIMERB1 OUT. value. */ - CTIMER_AUX7_TMRA7TRIG_A4OUT = 6, /*!< A4OUT : Trigger source is CTIMERA4 OUT. value. */ - CTIMER_AUX7_TMRA7TRIG_B4OUT = 7, /*!< B4OUT : Trigger source is CTIMERB4 OUT. value. */ - CTIMER_AUX7_TMRA7TRIG_B3OUT2 = 8, /*!< B3OUT2 : Trigger source is CTIMERB3 OUT2. value. */ - CTIMER_AUX7_TMRA7TRIG_A3OUT2 = 9, /*!< A3OUT2 : Trigger source is CTIMERA3 OUT2. value. */ - CTIMER_AUX7_TMRA7TRIG_A2OUT2 = 10, /*!< A2OUT2 : Trigger source is CTIMERA2 OUT2. value. */ - CTIMER_AUX7_TMRA7TRIG_B2OUT2 = 11, /*!< B2OUT2 : Trigger source is CTIMERB2 OUT2. value. */ - CTIMER_AUX7_TMRA7TRIG_A6OUT2DUAL = 12, /*!< A6OUT2DUAL : Trigger source is CTIMERA6 OUT2, dual edge. value. */ - CTIMER_AUX7_TMRA7TRIG_A5OUT2DUAL = 13, /*!< A5OUT2DUAL : Trigger source is CTIMERA5 OUT2, dual edge. value. */ - CTIMER_AUX7_TMRA7TRIG_B4OUT2DUAL = 14, /*!< B4OUT2DUAL : Trigger source is CTIMERB4 OUT2, dual edge. value. */ - CTIMER_AUX7_TMRA7TRIG_A4OUT2DUAL = 15, /*!< A4OUT2DUAL : Trigger source is CTIMERA4 OUT2, dual edge. value. */ -} CTIMER_AUX7_TMRA7TRIG_Enum; - -/* ======================================================== GLOBEN ========================================================= */ -/* ============================================== CTIMER GLOBEN ENB7 [15..15] ============================================== */ -typedef enum { /*!< CTIMER_GLOBEN_ENB7 */ - CTIMER_GLOBEN_ENB7_LCO = 1, /*!< LCO : Use local enable. value. */ - CTIMER_GLOBEN_ENB7_DIS = 0, /*!< DIS : Disable CTIMER. value. */ -} CTIMER_GLOBEN_ENB7_Enum; - -/* ============================================== CTIMER GLOBEN ENA7 [14..14] ============================================== */ -typedef enum { /*!< CTIMER_GLOBEN_ENA7 */ - CTIMER_GLOBEN_ENA7_LCO = 1, /*!< LCO : Use local enable. value. */ - CTIMER_GLOBEN_ENA7_DIS = 0, /*!< DIS : Disable CTIMER. value. */ -} CTIMER_GLOBEN_ENA7_Enum; - -/* ============================================== CTIMER GLOBEN ENB6 [13..13] ============================================== */ -typedef enum { /*!< CTIMER_GLOBEN_ENB6 */ - CTIMER_GLOBEN_ENB6_LCO = 1, /*!< LCO : Use local enable. value. */ - CTIMER_GLOBEN_ENB6_DIS = 0, /*!< DIS : Disable CTIMER. value. */ -} CTIMER_GLOBEN_ENB6_Enum; - -/* ============================================== CTIMER GLOBEN ENA6 [12..12] ============================================== */ -typedef enum { /*!< CTIMER_GLOBEN_ENA6 */ - CTIMER_GLOBEN_ENA6_LCO = 1, /*!< LCO : Use local enable. value. */ - CTIMER_GLOBEN_ENA6_DIS = 0, /*!< DIS : Disable CTIMER. value. */ -} CTIMER_GLOBEN_ENA6_Enum; - -/* ============================================== CTIMER GLOBEN ENB5 [11..11] ============================================== */ -typedef enum { /*!< CTIMER_GLOBEN_ENB5 */ - CTIMER_GLOBEN_ENB5_LCO = 1, /*!< LCO : Use local enable. value. */ - CTIMER_GLOBEN_ENB5_DIS = 0, /*!< DIS : Disable CTIMER. value. */ -} CTIMER_GLOBEN_ENB5_Enum; - -/* ============================================== CTIMER GLOBEN ENA5 [10..10] ============================================== */ -typedef enum { /*!< CTIMER_GLOBEN_ENA5 */ - CTIMER_GLOBEN_ENA5_LCO = 1, /*!< LCO : Use local enable. value. */ - CTIMER_GLOBEN_ENA5_DIS = 0, /*!< DIS : Disable CTIMER. value. */ -} CTIMER_GLOBEN_ENA5_Enum; - -/* =============================================== CTIMER GLOBEN ENB4 [9..9] =============================================== */ -typedef enum { /*!< CTIMER_GLOBEN_ENB4 */ - CTIMER_GLOBEN_ENB4_LCO = 1, /*!< LCO : Use local enable. value. */ - CTIMER_GLOBEN_ENB4_DIS = 0, /*!< DIS : Disable CTIMER. value. */ -} CTIMER_GLOBEN_ENB4_Enum; - -/* =============================================== CTIMER GLOBEN ENA4 [8..8] =============================================== */ -typedef enum { /*!< CTIMER_GLOBEN_ENA4 */ - CTIMER_GLOBEN_ENA4_LCO = 1, /*!< LCO : Use local enable. value. */ - CTIMER_GLOBEN_ENA4_DIS = 0, /*!< DIS : Disable CTIMER. value. */ -} CTIMER_GLOBEN_ENA4_Enum; - -/* =============================================== CTIMER GLOBEN ENB3 [7..7] =============================================== */ -typedef enum { /*!< CTIMER_GLOBEN_ENB3 */ - CTIMER_GLOBEN_ENB3_LCO = 1, /*!< LCO : Use local enable. value. */ - CTIMER_GLOBEN_ENB3_DIS = 0, /*!< DIS : Disable CTIMER. value. */ -} CTIMER_GLOBEN_ENB3_Enum; - -/* =============================================== CTIMER GLOBEN ENA3 [6..6] =============================================== */ -typedef enum { /*!< CTIMER_GLOBEN_ENA3 */ - CTIMER_GLOBEN_ENA3_LCO = 1, /*!< LCO : Use local enable. value. */ - CTIMER_GLOBEN_ENA3_DIS = 0, /*!< DIS : Disable CTIMER. value. */ -} CTIMER_GLOBEN_ENA3_Enum; - -/* =============================================== CTIMER GLOBEN ENB2 [5..5] =============================================== */ -typedef enum { /*!< CTIMER_GLOBEN_ENB2 */ - CTIMER_GLOBEN_ENB2_LCO = 1, /*!< LCO : Use local enable. value. */ - CTIMER_GLOBEN_ENB2_DIS = 0, /*!< DIS : Disable CTIMER. value. */ -} CTIMER_GLOBEN_ENB2_Enum; - -/* =============================================== CTIMER GLOBEN ENA2 [4..4] =============================================== */ -typedef enum { /*!< CTIMER_GLOBEN_ENA2 */ - CTIMER_GLOBEN_ENA2_LCO = 1, /*!< LCO : Use local enable. value. */ - CTIMER_GLOBEN_ENA2_DIS = 0, /*!< DIS : Disable CTIMER. value. */ -} CTIMER_GLOBEN_ENA2_Enum; - -/* =============================================== CTIMER GLOBEN ENB1 [3..3] =============================================== */ -typedef enum { /*!< CTIMER_GLOBEN_ENB1 */ - CTIMER_GLOBEN_ENB1_LCO = 1, /*!< LCO : Use local enable. value. */ - CTIMER_GLOBEN_ENB1_DIS = 0, /*!< DIS : Disable CTIMER. value. */ -} CTIMER_GLOBEN_ENB1_Enum; - -/* =============================================== CTIMER GLOBEN ENA1 [2..2] =============================================== */ -typedef enum { /*!< CTIMER_GLOBEN_ENA1 */ - CTIMER_GLOBEN_ENA1_LCO = 1, /*!< LCO : Use local enable. value. */ - CTIMER_GLOBEN_ENA1_DIS = 0, /*!< DIS : Disable CTIMER. value. */ -} CTIMER_GLOBEN_ENA1_Enum; - -/* =============================================== CTIMER GLOBEN ENB0 [1..1] =============================================== */ -typedef enum { /*!< CTIMER_GLOBEN_ENB0 */ - CTIMER_GLOBEN_ENB0_LCO = 1, /*!< LCO : Use local enable. value. */ - CTIMER_GLOBEN_ENB0_DIS = 0, /*!< DIS : Disable CTIMER. value. */ -} CTIMER_GLOBEN_ENB0_Enum; - -/* =============================================== CTIMER GLOBEN ENA0 [0..0] =============================================== */ -typedef enum { /*!< CTIMER_GLOBEN_ENA0 */ - CTIMER_GLOBEN_ENA0_LCO = 1, /*!< LCO : Use local enable. value. */ - CTIMER_GLOBEN_ENA0_DIS = 0, /*!< DIS : Disable CTIMER. value. */ -} CTIMER_GLOBEN_ENA0_Enum; - -/* ======================================================== OUTCFG0 ======================================================== */ -/* ============================================= CTIMER OUTCFG0 CFG9 [28..30] ============================================== */ -typedef enum { /*!< CTIMER_OUTCFG0_CFG9 */ - CTIMER_OUTCFG0_CFG9_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG0_CFG9_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG0_CFG9_B0OUT = 5, /*!< B0OUT : Output is B0OUT. value. */ - CTIMER_OUTCFG0_CFG9_A4OUT = 4, /*!< A4OUT : Output is A4OUT. value. */ - CTIMER_OUTCFG0_CFG9_A2OUT = 3, /*!< A2OUT : Output is A2OUT. value. */ - CTIMER_OUTCFG0_CFG9_A2OUT2 = 2, /*!< A2OUT2 : Output is A2OUT2 value. */ - CTIMER_OUTCFG0_CFG9_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG0_CFG9_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG0_CFG9_Enum; - -/* ============================================= CTIMER OUTCFG0 CFG8 [25..27] ============================================== */ -typedef enum { /*!< CTIMER_OUTCFG0_CFG8 */ - CTIMER_OUTCFG0_CFG8_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG0_CFG8_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG0_CFG8_B6OUT = 5, /*!< B6OUT : Output is B6OUT. value. */ - CTIMER_OUTCFG0_CFG8_A4OUT2 = 4, /*!< A4OUT2 : Output is A4OUT2. value. */ - CTIMER_OUTCFG0_CFG8_A3OUT2 = 3, /*!< A3OUT2 : Output is A3OUT. value. */ - CTIMER_OUTCFG0_CFG8_A2OUT = 2, /*!< A2OUT : Output is A2OUT value. */ - CTIMER_OUTCFG0_CFG8_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG0_CFG8_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG0_CFG8_Enum; - -/* ============================================= CTIMER OUTCFG0 CFG7 [22..24] ============================================== */ -typedef enum { /*!< CTIMER_OUTCFG0_CFG7 */ - CTIMER_OUTCFG0_CFG7_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG0_CFG7_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG0_CFG7_A7OUT = 5, /*!< A7OUT : Output is A7OUT. value. */ - CTIMER_OUTCFG0_CFG7_B5OUT = 4, /*!< B5OUT : Output is B5OUT. value. */ - CTIMER_OUTCFG0_CFG7_B1OUT = 3, /*!< B1OUT : Output is B1OUT. value. */ - CTIMER_OUTCFG0_CFG7_B1OUT2 = 2, /*!< B1OUT2 : Output is B1OUT2 value. */ - CTIMER_OUTCFG0_CFG7_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG0_CFG7_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG0_CFG7_Enum; - -/* ============================================= CTIMER OUTCFG0 CFG6 [19..21] ============================================== */ -typedef enum { /*!< CTIMER_OUTCFG0_CFG6 */ - CTIMER_OUTCFG0_CFG6_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG0_CFG6_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG0_CFG6_B7OUT = 5, /*!< B7OUT : Output is B7OUT. value. */ - CTIMER_OUTCFG0_CFG6_B5OUT2 = 4, /*!< B5OUT2 : Output is B5OUT2. value. */ - CTIMER_OUTCFG0_CFG6_A1OUT = 3, /*!< A1OUT : Output is A1OUT. value. */ - CTIMER_OUTCFG0_CFG6_B1OUT = 2, /*!< B1OUT : Output is B1OUT value. */ - CTIMER_OUTCFG0_CFG6_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG0_CFG6_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG0_CFG6_Enum; - -/* ============================================= CTIMER OUTCFG0 CFG5 [16..18] ============================================== */ -typedef enum { /*!< CTIMER_OUTCFG0_CFG5 */ - CTIMER_OUTCFG0_CFG5_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG0_CFG5_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG0_CFG5_A7OUT = 5, /*!< A7OUT : Output is A7OUT. value. */ - CTIMER_OUTCFG0_CFG5_B6OUT = 4, /*!< B6OUT : Output is A5OUT. value. */ - CTIMER_OUTCFG0_CFG5_A1OUT = 3, /*!< A1OUT : Output is A1OUT. value. */ - CTIMER_OUTCFG0_CFG5_A1OUT2 = 2, /*!< A1OUT2 : Output is A1OUT2 value. */ - CTIMER_OUTCFG0_CFG5_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG0_CFG5_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG0_CFG5_Enum; - -/* ============================================= CTIMER OUTCFG0 CFG4 [12..14] ============================================== */ -typedef enum { /*!< CTIMER_OUTCFG0_CFG4 */ - CTIMER_OUTCFG0_CFG4_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG0_CFG4_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG0_CFG4_B5OUT = 5, /*!< B5OUT : Output is B5OUT. value. */ - CTIMER_OUTCFG0_CFG4_A5OUT2 = 4, /*!< A5OUT2 : Output is A5OUT2. value. */ - CTIMER_OUTCFG0_CFG4_A2OUT2 = 3, /*!< A2OUT2 : Output is A2OUT2. value. */ - CTIMER_OUTCFG0_CFG4_A1OUT = 2, /*!< A1OUT : Output is A1OUT value. */ - CTIMER_OUTCFG0_CFG4_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG0_CFG4_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG0_CFG4_Enum; - -/* ============================================== CTIMER OUTCFG0 CFG3 [9..11] ============================================== */ -typedef enum { /*!< CTIMER_OUTCFG0_CFG3 */ - CTIMER_OUTCFG0_CFG3_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG0_CFG3_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG0_CFG3_A6OUT = 5, /*!< A6OUT : Output is A6OUT. value. */ - CTIMER_OUTCFG0_CFG3_A1OUT = 4, /*!< A1OUT : Output is A1OUT. value. */ - CTIMER_OUTCFG0_CFG3_B0OUT = 3, /*!< B0OUT : Output is B0OUT. value. */ - CTIMER_OUTCFG0_CFG3_B0OUT2 = 2, /*!< B0OUT2 : Output is B0OUT2 value. */ - CTIMER_OUTCFG0_CFG3_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG0_CFG3_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG0_CFG3_Enum; - -/* ============================================== CTIMER OUTCFG0 CFG2 [6..8] =============================================== */ -typedef enum { /*!< CTIMER_OUTCFG0_CFG2 */ - CTIMER_OUTCFG0_CFG2_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG0_CFG2_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG0_CFG2_A7OUT = 5, /*!< A7OUT : Output is A7OUT. value. */ - CTIMER_OUTCFG0_CFG2_B6OUT2 = 4, /*!< B6OUT2 : Output is B6OUT2. value. */ - CTIMER_OUTCFG0_CFG2_B1OUT2 = 3, /*!< B1OUT2 : Output is B1OUT2. value. */ - CTIMER_OUTCFG0_CFG2_B0OUT = 2, /*!< B0OUT : Output is B0OUT value. */ - CTIMER_OUTCFG0_CFG2_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG0_CFG2_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG0_CFG2_Enum; - -/* ============================================== CTIMER OUTCFG0 CFG1 [3..5] =============================================== */ -typedef enum { /*!< CTIMER_OUTCFG0_CFG1 */ - CTIMER_OUTCFG0_CFG1_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG0_CFG1_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG0_CFG1_B7OUT2 = 5, /*!< B7OUT2 : Output is B7OUT2. value. */ - CTIMER_OUTCFG0_CFG1_A5OUT = 4, /*!< A5OUT : Output is A5OUT. value. */ - CTIMER_OUTCFG0_CFG1_A0OUT = 3, /*!< A0OUT : Output is A0OUT. value. */ - CTIMER_OUTCFG0_CFG1_A0OUT2 = 2, /*!< A0OUT2 : Output is A0OUT2 value. */ - CTIMER_OUTCFG0_CFG1_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG0_CFG1_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG0_CFG1_Enum; - -/* ============================================== CTIMER OUTCFG0 CFG0 [0..2] =============================================== */ -typedef enum { /*!< CTIMER_OUTCFG0_CFG0 */ - CTIMER_OUTCFG0_CFG0_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG0_CFG0_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG0_CFG0_A6OUT = 5, /*!< A6OUT : Output is A6OUT. value. */ - CTIMER_OUTCFG0_CFG0_A5OUT2 = 4, /*!< A5OUT2 : Output is A5OUT2. value. */ - CTIMER_OUTCFG0_CFG0_B2OUT2 = 3, /*!< B2OUT2 : Output is B2OUT2. value. */ - CTIMER_OUTCFG0_CFG0_A0OUT = 2, /*!< A0OUT : Output is A0OUT value. */ - CTIMER_OUTCFG0_CFG0_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG0_CFG0_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG0_CFG0_Enum; - -/* ======================================================== OUTCFG1 ======================================================== */ -/* ============================================= CTIMER OUTCFG1 CFG19 [28..30] ============================================= */ -typedef enum { /*!< CTIMER_OUTCFG1_CFG19 */ - CTIMER_OUTCFG1_CFG19_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG1_CFG19_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG1_CFG19_B1OUT2 = 5, /*!< B1OUT2 : Output is B1OUT2. value. */ - CTIMER_OUTCFG1_CFG19_B4OUT = 4, /*!< B4OUT : Output is B4OUT. value. */ - CTIMER_OUTCFG1_CFG19_A2OUT = 3, /*!< A2OUT : Output is A2OUT. value. */ - CTIMER_OUTCFG1_CFG19_B4OUT2 = 2, /*!< B4OUT2 : Output is B4OUT2 value. */ - CTIMER_OUTCFG1_CFG19_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG1_CFG19_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG1_CFG19_Enum; - -/* ============================================= CTIMER OUTCFG1 CFG18 [25..27] ============================================= */ -typedef enum { /*!< CTIMER_OUTCFG1_CFG18 */ - CTIMER_OUTCFG1_CFG18_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG1_CFG18_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG1_CFG18_A3OUT2 = 5, /*!< A3OUT2 : Output is A3OUT2. value. */ - CTIMER_OUTCFG1_CFG18_A0OUT = 4, /*!< A0OUT : Output is A0OUT. value. */ - CTIMER_OUTCFG1_CFG18_B0OUT = 3, /*!< B0OUT : Output is B0OUT. value. */ - CTIMER_OUTCFG1_CFG18_B4OUT = 2, /*!< B4OUT : Output is B4OUT value. */ - CTIMER_OUTCFG1_CFG18_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG1_CFG18_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG1_CFG18_Enum; - -/* ============================================= CTIMER OUTCFG1 CFG17 [22..24] ============================================= */ -typedef enum { /*!< CTIMER_OUTCFG1_CFG17 */ - CTIMER_OUTCFG1_CFG17_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG1_CFG17_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG1_CFG17_A1OUT2 = 5, /*!< A1OUT2 : Output is A1OUT2. value. */ - CTIMER_OUTCFG1_CFG17_A4OUT = 4, /*!< A4OUT : Output is A4OUT. value. */ - CTIMER_OUTCFG1_CFG17_B7OUT = 3, /*!< B7OUT : Output is B7OUT. value. */ - CTIMER_OUTCFG1_CFG17_A4OUT2 = 2, /*!< A4OUT2 : Output is A4OUT2 value. */ - CTIMER_OUTCFG1_CFG17_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG1_CFG17_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG1_CFG17_Enum; - -/* ============================================= CTIMER OUTCFG1 CFG16 [19..21] ============================================= */ -typedef enum { /*!< CTIMER_OUTCFG1_CFG16 */ - CTIMER_OUTCFG1_CFG16_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG1_CFG16_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG1_CFG16_B3OUT2 = 5, /*!< B3OUT2 : Output is B3OUT2. value. */ - CTIMER_OUTCFG1_CFG16_A0OUT2 = 4, /*!< A0OUT2 : Output is A0OUT2. value. */ - CTIMER_OUTCFG1_CFG16_A0OUT = 3, /*!< A0OUT : Output is A0OUT. value. */ - CTIMER_OUTCFG1_CFG16_A4OUT = 2, /*!< A4OUT : Output is A4OUT value. */ - CTIMER_OUTCFG1_CFG16_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG1_CFG16_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG1_CFG16_Enum; - -/* ============================================= CTIMER OUTCFG1 CFG15 [16..18] ============================================= */ -typedef enum { /*!< CTIMER_OUTCFG1_CFG15 */ - CTIMER_OUTCFG1_CFG15_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG1_CFG15_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG1_CFG15_A4OUT2 = 5, /*!< A4OUT2 : Output is A4OUT2. value. */ - CTIMER_OUTCFG1_CFG15_A7OUT = 4, /*!< A7OUT : Output is B1OUT. value. */ - CTIMER_OUTCFG1_CFG15_B3OUT = 3, /*!< B3OUT : Output is B3OUT. value. */ - CTIMER_OUTCFG1_CFG15_B3OUT2 = 2, /*!< B3OUT2 : Output is B3OUT2 value. */ - CTIMER_OUTCFG1_CFG15_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG1_CFG15_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG1_CFG15_Enum; - -/* ============================================= CTIMER OUTCFG1 CFG14 [12..14] ============================================= */ -typedef enum { /*!< CTIMER_OUTCFG1_CFG14 */ - CTIMER_OUTCFG1_CFG14_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG1_CFG14_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG1_CFG14_A7OUT = 5, /*!< A7OUT : Output is A7OUT. value. */ - CTIMER_OUTCFG1_CFG14_B7OUT2 = 4, /*!< B7OUT2 : Output is B7OUT2. value. */ - CTIMER_OUTCFG1_CFG14_B1OUT = 3, /*!< B1OUT : Output is B1OUT. value. */ - CTIMER_OUTCFG1_CFG14_B3OUT = 2, /*!< B3OUT : Output is B3OUT value. */ - CTIMER_OUTCFG1_CFG14_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG1_CFG14_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG1_CFG14_Enum; - -/* ============================================= CTIMER OUTCFG1 CFG13 [9..11] ============================================== */ -typedef enum { /*!< CTIMER_OUTCFG1_CFG13 */ - CTIMER_OUTCFG1_CFG13_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG1_CFG13_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG1_CFG13_B4OUT2 = 5, /*!< B4OUT2 : Output is B4OUT2. value. */ - CTIMER_OUTCFG1_CFG13_A6OUT = 4, /*!< A6OUT : Output is B1OUT. value. */ - CTIMER_OUTCFG1_CFG13_A3OUT = 3, /*!< A3OUT : Output is A3OUT. value. */ - CTIMER_OUTCFG1_CFG13_A3OUT2 = 2, /*!< A3OUT2 : Output is A3OUT2 value. */ - CTIMER_OUTCFG1_CFG13_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG1_CFG13_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG1_CFG13_Enum; - -/* ============================================== CTIMER OUTCFG1 CFG12 [6..8] ============================================== */ -typedef enum { /*!< CTIMER_OUTCFG1_CFG12 */ - CTIMER_OUTCFG1_CFG12_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG1_CFG12_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG1_CFG12_B6OUT2 = 5, /*!< B6OUT2 : Output is B6OUT2. value. */ - CTIMER_OUTCFG1_CFG12_B0OUT2 = 4, /*!< B0OUT2 : Output is B0OUT2. value. */ - CTIMER_OUTCFG1_CFG12_B1OUT = 3, /*!< B1OUT : Output is B1OUT. value. */ - CTIMER_OUTCFG1_CFG12_A3OUT = 2, /*!< A3OUT : Output is A3OUT value. */ - CTIMER_OUTCFG1_CFG12_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG1_CFG12_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG1_CFG12_Enum; - -/* ============================================== CTIMER OUTCFG1 CFG11 [3..5] ============================================== */ -typedef enum { /*!< CTIMER_OUTCFG1_CFG11 */ - CTIMER_OUTCFG1_CFG11_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG1_CFG11_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG1_CFG11_B5OUT2 = 5, /*!< B5OUT2 : Output is B5OUT2. value. */ - CTIMER_OUTCFG1_CFG11_B4OUT = 4, /*!< B4OUT : Output is B4OUT. value. */ - CTIMER_OUTCFG1_CFG11_B2OUT = 3, /*!< B2OUT : Output is B2OUT. value. */ - CTIMER_OUTCFG1_CFG11_B2OUT2 = 2, /*!< B2OUT2 : Output is B2OUT2 value. */ - CTIMER_OUTCFG1_CFG11_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG1_CFG11_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG1_CFG11_Enum; - -/* ============================================== CTIMER OUTCFG1 CFG10 [0..2] ============================================== */ -typedef enum { /*!< CTIMER_OUTCFG1_CFG10 */ - CTIMER_OUTCFG1_CFG10_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG1_CFG10_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG1_CFG10_A6OUT = 5, /*!< A6OUT : Output is A6OUT. value. */ - CTIMER_OUTCFG1_CFG10_B4OUT2 = 4, /*!< B4OUT2 : Output is B4OUT2. value. */ - CTIMER_OUTCFG1_CFG10_B3OUT2 = 3, /*!< B3OUT2 : Output is B3OUT2. value. */ - CTIMER_OUTCFG1_CFG10_B2OUT = 2, /*!< B2OUT : Output is B2OUT value. */ - CTIMER_OUTCFG1_CFG10_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG1_CFG10_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG1_CFG10_Enum; - -/* ======================================================== OUTCFG2 ======================================================== */ -/* ============================================= CTIMER OUTCFG2 CFG29 [28..30] ============================================= */ -typedef enum { /*!< CTIMER_OUTCFG2_CFG29 */ - CTIMER_OUTCFG2_CFG29_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG2_CFG29_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG2_CFG29_A3OUT2 = 5, /*!< A3OUT2 : Output is A3OUT2. value. */ - CTIMER_OUTCFG2_CFG29_A7OUT = 4, /*!< A7OUT : Output is A7OUT. value. */ - CTIMER_OUTCFG2_CFG29_A1OUT = 3, /*!< A1OUT : Output is A1OUT. value. */ - CTIMER_OUTCFG2_CFG29_B5OUT2 = 2, /*!< B5OUT2 : Output is B5OUT2 value. */ - CTIMER_OUTCFG2_CFG29_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG2_CFG29_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG2_CFG29_Enum; - -/* ============================================= CTIMER OUTCFG2 CFG28 [25..27] ============================================= */ -typedef enum { /*!< CTIMER_OUTCFG2_CFG28 */ - CTIMER_OUTCFG2_CFG28_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG2_CFG28_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG2_CFG28_B0OUT2 = 5, /*!< B0OUT2 : Output is B0OUT2. value. */ - CTIMER_OUTCFG2_CFG28_A5OUT2 = 4, /*!< A5OUT2 : Output is A5OUT2. value. */ - CTIMER_OUTCFG2_CFG28_A3OUT = 3, /*!< A3OUT : Output is A3OUT. value. */ - CTIMER_OUTCFG2_CFG28_A7OUT = 2, /*!< A7OUT : Output is A7OUT value. */ - CTIMER_OUTCFG2_CFG28_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG2_CFG28_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG2_CFG28_Enum; - -/* ============================================= CTIMER OUTCFG2 CFG27 [22..24] ============================================= */ -typedef enum { /*!< CTIMER_OUTCFG2_CFG27 */ - CTIMER_OUTCFG2_CFG27_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG2_CFG27_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG2_CFG27_B2OUT2 = 5, /*!< B2OUT2 : Output is B2OUT2. value. */ - CTIMER_OUTCFG2_CFG27_B6OUT = 4, /*!< B6OUT : Output is B6OUT. value. */ - CTIMER_OUTCFG2_CFG27_A1OUT = 3, /*!< A1OUT : Output is A1OUT. value. */ - CTIMER_OUTCFG2_CFG27_B6OUT2 = 2, /*!< B6OUT2 : Output is B6OUT2 value. */ - CTIMER_OUTCFG2_CFG27_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG2_CFG27_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG2_CFG27_Enum; - -/* ============================================= CTIMER OUTCFG2 CFG26 [19..21] ============================================= */ -typedef enum { /*!< CTIMER_OUTCFG2_CFG26 */ - CTIMER_OUTCFG2_CFG26_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG2_CFG26_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG2_CFG26_A1OUT2 = 5, /*!< A1OUT2 : Output is A1OUT2. value. */ - CTIMER_OUTCFG2_CFG26_A5OUT = 4, /*!< A5OUT : Output is A5OUT. value. */ - CTIMER_OUTCFG2_CFG26_B2OUT = 3, /*!< B2OUT : Output is B2OUT. value. */ - CTIMER_OUTCFG2_CFG26_B6OUT = 2, /*!< B6OUT : Output is B6OUT value. */ - CTIMER_OUTCFG2_CFG26_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG2_CFG26_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG2_CFG26_Enum; - -/* ============================================= CTIMER OUTCFG2 CFG25 [16..18] ============================================= */ -typedef enum { /*!< CTIMER_OUTCFG2_CFG25 */ - CTIMER_OUTCFG2_CFG25_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG2_CFG25_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG2_CFG25_A2OUT2 = 5, /*!< A2OUT2 : Output is A2OUT2. value. */ - CTIMER_OUTCFG2_CFG25_A6OUT = 4, /*!< A6OUT : Output is A6OUT. value. */ - CTIMER_OUTCFG2_CFG25_B2OUT = 3, /*!< B2OUT : Output is B2OUT. value. */ - CTIMER_OUTCFG2_CFG25_B4OUT2 = 2, /*!< B4OUT2 : Output is B4OUT2 value. */ - CTIMER_OUTCFG2_CFG25_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG2_CFG25_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG2_CFG25_Enum; - -/* ============================================= CTIMER OUTCFG2 CFG24 [12..14] ============================================= */ -typedef enum { /*!< CTIMER_OUTCFG2_CFG24 */ - CTIMER_OUTCFG2_CFG24_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG2_CFG24_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG2_CFG24_B1OUT2 = 5, /*!< B1OUT2 : Output is B1OUT2. value. */ - CTIMER_OUTCFG2_CFG24_A1OUT = 4, /*!< A1OUT : Output is A1OUT. value. */ - CTIMER_OUTCFG2_CFG24_A2OUT = 3, /*!< A2OUT : Output is A2OUT. value. */ - CTIMER_OUTCFG2_CFG24_A6OUT = 2, /*!< A6OUT : Output is A6OUT value. */ - CTIMER_OUTCFG2_CFG24_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG2_CFG24_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG2_CFG24_Enum; - -/* ============================================= CTIMER OUTCFG2 CFG23 [9..11] ============================================== */ -typedef enum { /*!< CTIMER_OUTCFG2_CFG23 */ - CTIMER_OUTCFG2_CFG23_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG2_CFG23_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG2_CFG23_B0OUT2 = 5, /*!< B0OUT2 : Output is B0OUT2. value. */ - CTIMER_OUTCFG2_CFG23_A5OUT = 4, /*!< A5OUT : Output is A5OUT. value. */ - CTIMER_OUTCFG2_CFG23_A7OUT = 3, /*!< A7OUT : Output is B1OUT. value. */ - CTIMER_OUTCFG2_CFG23_B5OUT2 = 2, /*!< B5OUT2 : Output is B5OUT2 value. */ - CTIMER_OUTCFG2_CFG23_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG2_CFG23_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG2_CFG23_Enum; - -/* ============================================== CTIMER OUTCFG2 CFG22 [6..8] ============================================== */ -typedef enum { /*!< CTIMER_OUTCFG2_CFG22 */ - CTIMER_OUTCFG2_CFG22_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG2_CFG22_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG2_CFG22_A2OUT2 = 5, /*!< A2OUT2 : Output is A2OUT2. value. */ - CTIMER_OUTCFG2_CFG22_A1OUT = 4, /*!< A1OUT : Output is A1OUT. value. */ - CTIMER_OUTCFG2_CFG22_A6OUT = 3, /*!< A6OUT : Output is B1OUT. value. */ - CTIMER_OUTCFG2_CFG22_B5OUT = 2, /*!< B5OUT : Output is B5OUT value. */ - CTIMER_OUTCFG2_CFG22_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG2_CFG22_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG2_CFG22_Enum; - -/* ============================================== CTIMER OUTCFG2 CFG21 [3..5] ============================================== */ -typedef enum { /*!< CTIMER_OUTCFG2_CFG21 */ - CTIMER_OUTCFG2_CFG21_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG2_CFG21_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG2_CFG21_A0OUT2 = 5, /*!< A0OUT2 : Output is A0OUT2. value. */ - CTIMER_OUTCFG2_CFG21_B5OUT = 4, /*!< B5OUT : Output is B5OUT. value. */ - CTIMER_OUTCFG2_CFG21_A1OUT = 3, /*!< A1OUT : Output is A1OUT. value. */ - CTIMER_OUTCFG2_CFG21_A5OUT2 = 2, /*!< A5OUT2 : Output is A5OUT2 value. */ - CTIMER_OUTCFG2_CFG21_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG2_CFG21_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG2_CFG21_Enum; - -/* ============================================== CTIMER OUTCFG2 CFG20 [0..2] ============================================== */ -typedef enum { /*!< CTIMER_OUTCFG2_CFG20 */ - CTIMER_OUTCFG2_CFG20_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG2_CFG20_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG2_CFG20_B2OUT2 = 5, /*!< B2OUT2 : Output is B2OUT2. value. */ - CTIMER_OUTCFG2_CFG20_A1OUT2 = 4, /*!< A1OUT2 : Output is A1OUT2. value. */ - CTIMER_OUTCFG2_CFG20_A1OUT = 3, /*!< A1OUT : Output is A1OUT. value. */ - CTIMER_OUTCFG2_CFG20_A5OUT = 2, /*!< A5OUT : Output is A5OUT value. */ - CTIMER_OUTCFG2_CFG20_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG2_CFG20_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG2_CFG20_Enum; - -/* ======================================================== OUTCFG3 ======================================================== */ -/* ============================================== CTIMER OUTCFG3 CFG31 [3..5] ============================================== */ -typedef enum { /*!< CTIMER_OUTCFG3_CFG31 */ - CTIMER_OUTCFG3_CFG31_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG3_CFG31_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG3_CFG31_B3OUT2 = 5, /*!< B3OUT2 : Output is B3OUT2. value. */ - CTIMER_OUTCFG3_CFG31_B7OUT = 4, /*!< B7OUT : Output is B7OUT. value. */ - CTIMER_OUTCFG3_CFG31_A6OUT = 3, /*!< A6OUT : Output is A6OUT. value. */ - CTIMER_OUTCFG3_CFG31_B7OUT2 = 2, /*!< B7OUT2 : Output is B7OUT2 value. */ - CTIMER_OUTCFG3_CFG31_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG3_CFG31_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG3_CFG31_Enum; - -/* ============================================== CTIMER OUTCFG3 CFG30 [0..2] ============================================== */ -typedef enum { /*!< CTIMER_OUTCFG3_CFG30 */ - CTIMER_OUTCFG3_CFG30_A7OUT2 = 7, /*!< A7OUT2 : Output is A7OUT2. value. */ - CTIMER_OUTCFG3_CFG30_A6OUT2 = 6, /*!< A6OUT2 : Output is A6OUT2. value. */ - CTIMER_OUTCFG3_CFG30_A0OUT2 = 5, /*!< A0OUT2 : Output is A0OUT2. value. */ - CTIMER_OUTCFG3_CFG30_A4OUT2 = 4, /*!< A4OUT2 : Output is A4OUT2. value. */ - CTIMER_OUTCFG3_CFG30_B3OUT = 3, /*!< B3OUT : Output is B3OUT. value. */ - CTIMER_OUTCFG3_CFG30_B7OUT = 2, /*!< B7OUT : Output is B7OUT value. */ - CTIMER_OUTCFG3_CFG30_ONE = 1, /*!< ONE : Force output to 1. value. */ - CTIMER_OUTCFG3_CFG30_ZERO = 0, /*!< ZERO : Force output to 0 value. */ -} CTIMER_OUTCFG3_CFG30_Enum; - -/* ========================================================= INCFG ========================================================= */ -/* ============================================== CTIMER INCFG CFGB7 [15..15] ============================================== */ -typedef enum { /*!< CTIMER_INCFG_CFGB7 */ - CTIMER_INCFG_CFGB7_CT31 = 1, /*!< CT31 : Input is CT31 value. */ - CTIMER_INCFG_CFGB7_CT30 = 0, /*!< CT30 : Input is CT30 value. */ -} CTIMER_INCFG_CFGB7_Enum; - -/* ============================================== CTIMER INCFG CFGA7 [14..14] ============================================== */ -typedef enum { /*!< CTIMER_INCFG_CFGA7 */ - CTIMER_INCFG_CFGA7_CT29 = 1, /*!< CT29 : Input is CT29 value. */ - CTIMER_INCFG_CFGA7_CT28 = 0, /*!< CT28 : Input is CT28 value. */ -} CTIMER_INCFG_CFGA7_Enum; - -/* ============================================== CTIMER INCFG CFGB6 [13..13] ============================================== */ -typedef enum { /*!< CTIMER_INCFG_CFGB6 */ - CTIMER_INCFG_CFGB6_CT27 = 1, /*!< CT27 : Input is CT27 value. */ - CTIMER_INCFG_CFGB6_CT26 = 0, /*!< CT26 : Input is CT26 value. */ -} CTIMER_INCFG_CFGB6_Enum; - -/* ============================================== CTIMER INCFG CFGA6 [12..12] ============================================== */ -typedef enum { /*!< CTIMER_INCFG_CFGA6 */ - CTIMER_INCFG_CFGA6_CT25 = 1, /*!< CT25 : Input is CT25 value. */ - CTIMER_INCFG_CFGA6_CT24 = 0, /*!< CT24 : Input is CT24 value. */ -} CTIMER_INCFG_CFGA6_Enum; - -/* ============================================== CTIMER INCFG CFGB5 [11..11] ============================================== */ -typedef enum { /*!< CTIMER_INCFG_CFGB5 */ - CTIMER_INCFG_CFGB5_CT23 = 1, /*!< CT23 : Input is CT23 value. */ - CTIMER_INCFG_CFGB5_CT22 = 0, /*!< CT22 : Input is CT22 value. */ -} CTIMER_INCFG_CFGB5_Enum; - -/* ============================================== CTIMER INCFG CFGA5 [10..10] ============================================== */ -typedef enum { /*!< CTIMER_INCFG_CFGA5 */ - CTIMER_INCFG_CFGA5_CT21 = 1, /*!< CT21 : Input is CT21 value. */ - CTIMER_INCFG_CFGA5_CT20 = 0, /*!< CT20 : Input is CT20 value. */ -} CTIMER_INCFG_CFGA5_Enum; - -/* =============================================== CTIMER INCFG CFGB4 [9..9] =============================================== */ -typedef enum { /*!< CTIMER_INCFG_CFGB4 */ - CTIMER_INCFG_CFGB4_CT19 = 1, /*!< CT19 : Input is CT19 value. */ - CTIMER_INCFG_CFGB4_CT18 = 0, /*!< CT18 : Input is CT18 value. */ -} CTIMER_INCFG_CFGB4_Enum; - -/* =============================================== CTIMER INCFG CFGA4 [8..8] =============================================== */ -typedef enum { /*!< CTIMER_INCFG_CFGA4 */ - CTIMER_INCFG_CFGA4_CT17 = 1, /*!< CT17 : Input is CT17 value. */ - CTIMER_INCFG_CFGA4_CT16 = 0, /*!< CT16 : Input is CT16 value. */ -} CTIMER_INCFG_CFGA4_Enum; - -/* =============================================== CTIMER INCFG CFGB3 [7..7] =============================================== */ -typedef enum { /*!< CTIMER_INCFG_CFGB3 */ - CTIMER_INCFG_CFGB3_CT15 = 1, /*!< CT15 : Input is CT15 value. */ - CTIMER_INCFG_CFGB3_CT14 = 0, /*!< CT14 : Input is CT14 value. */ -} CTIMER_INCFG_CFGB3_Enum; - -/* =============================================== CTIMER INCFG CFGA3 [6..6] =============================================== */ -typedef enum { /*!< CTIMER_INCFG_CFGA3 */ - CTIMER_INCFG_CFGA3_CT13 = 1, /*!< CT13 : Input is CT13 value. */ - CTIMER_INCFG_CFGA3_CT12 = 0, /*!< CT12 : Input is CT12 value. */ -} CTIMER_INCFG_CFGA3_Enum; - -/* =============================================== CTIMER INCFG CFGB2 [5..5] =============================================== */ -typedef enum { /*!< CTIMER_INCFG_CFGB2 */ - CTIMER_INCFG_CFGB2_CT11 = 1, /*!< CT11 : Input is CT11 value. */ - CTIMER_INCFG_CFGB2_CT10 = 0, /*!< CT10 : Input is CT10 value. */ -} CTIMER_INCFG_CFGB2_Enum; - -/* =============================================== CTIMER INCFG CFGA2 [4..4] =============================================== */ -typedef enum { /*!< CTIMER_INCFG_CFGA2 */ - CTIMER_INCFG_CFGA2_CT9 = 1, /*!< CT9 : Input is CT9 value. */ - CTIMER_INCFG_CFGA2_CT8 = 0, /*!< CT8 : Input is CT8 value. */ -} CTIMER_INCFG_CFGA2_Enum; - -/* =============================================== CTIMER INCFG CFGB1 [3..3] =============================================== */ -typedef enum { /*!< CTIMER_INCFG_CFGB1 */ - CTIMER_INCFG_CFGB1_CT7 = 1, /*!< CT7 : Input is CT7 value. */ - CTIMER_INCFG_CFGB1_CT6 = 0, /*!< CT6 : Input is CT6 value. */ -} CTIMER_INCFG_CFGB1_Enum; - -/* =============================================== CTIMER INCFG CFGA1 [2..2] =============================================== */ -typedef enum { /*!< CTIMER_INCFG_CFGA1 */ - CTIMER_INCFG_CFGA1_CT5 = 1, /*!< CT5 : Input is CT5 value. */ - CTIMER_INCFG_CFGA1_CT4 = 0, /*!< CT4 : Input is CT4 value. */ -} CTIMER_INCFG_CFGA1_Enum; - -/* =============================================== CTIMER INCFG CFGB0 [1..1] =============================================== */ -typedef enum { /*!< CTIMER_INCFG_CFGB0 */ - CTIMER_INCFG_CFGB0_CT3 = 1, /*!< CT3 : Input is CT3 value. */ - CTIMER_INCFG_CFGB0_CT2 = 0, /*!< CT2 : Input is CT2 value. */ -} CTIMER_INCFG_CFGB0_Enum; - -/* =============================================== CTIMER INCFG CFGA0 [0..0] =============================================== */ -typedef enum { /*!< CTIMER_INCFG_CFGA0 */ - CTIMER_INCFG_CFGA0_CT1 = 1, /*!< CT1 : Input is CT1 value. */ - CTIMER_INCFG_CFGA0_CT0 = 0, /*!< CT0 : Input is CT0 value. */ -} CTIMER_INCFG_CFGA0_Enum; - -/* ========================================================= STCFG ========================================================= */ -/* ============================================= CTIMER STCFG FREEZE [31..31] ============================================== */ -typedef enum { /*!< CTIMER_STCFG_FREEZE */ - CTIMER_STCFG_FREEZE_THAW = 0, /*!< THAW : Let the COUNTER register run on its input clock. value. */ - CTIMER_STCFG_FREEZE_FREEZE = 1, /*!< FREEZE : Stop the COUNTER register for loading. value. */ -} CTIMER_STCFG_FREEZE_Enum; - -/* ============================================== CTIMER STCFG CLEAR [30..30] ============================================== */ -typedef enum { /*!< CTIMER_STCFG_CLEAR */ - CTIMER_STCFG_CLEAR_RUN = 0, /*!< RUN : Let the COUNTER register run on its input clock. value. */ - CTIMER_STCFG_CLEAR_CLEAR = 1, /*!< CLEAR : Stop the COUNTER register for loading. value. */ -} CTIMER_STCFG_CLEAR_Enum; - -/* ========================================== CTIMER STCFG COMPARE_H_EN [15..15] =========================================== */ -typedef enum { /*!< CTIMER_STCFG_COMPARE_H_EN */ - CTIMER_STCFG_COMPARE_H_EN_DISABLE = 0, /*!< DISABLE : Compare H disabled. value. */ - CTIMER_STCFG_COMPARE_H_EN_ENABLE = 1, /*!< ENABLE : Compare H enabled. value. */ -} CTIMER_STCFG_COMPARE_H_EN_Enum; - -/* ========================================== CTIMER STCFG COMPARE_G_EN [14..14] =========================================== */ -typedef enum { /*!< CTIMER_STCFG_COMPARE_G_EN */ - CTIMER_STCFG_COMPARE_G_EN_DISABLE = 0, /*!< DISABLE : Compare G disabled. value. */ - CTIMER_STCFG_COMPARE_G_EN_ENABLE = 1, /*!< ENABLE : Compare G enabled. value. */ -} CTIMER_STCFG_COMPARE_G_EN_Enum; - -/* ========================================== CTIMER STCFG COMPARE_F_EN [13..13] =========================================== */ -typedef enum { /*!< CTIMER_STCFG_COMPARE_F_EN */ - CTIMER_STCFG_COMPARE_F_EN_DISABLE = 0, /*!< DISABLE : Compare F disabled. value. */ - CTIMER_STCFG_COMPARE_F_EN_ENABLE = 1, /*!< ENABLE : Compare F enabled. value. */ -} CTIMER_STCFG_COMPARE_F_EN_Enum; - -/* ========================================== CTIMER STCFG COMPARE_E_EN [12..12] =========================================== */ -typedef enum { /*!< CTIMER_STCFG_COMPARE_E_EN */ - CTIMER_STCFG_COMPARE_E_EN_DISABLE = 0, /*!< DISABLE : Compare E disabled. value. */ - CTIMER_STCFG_COMPARE_E_EN_ENABLE = 1, /*!< ENABLE : Compare E enabled. value. */ -} CTIMER_STCFG_COMPARE_E_EN_Enum; - -/* ========================================== CTIMER STCFG COMPARE_D_EN [11..11] =========================================== */ -typedef enum { /*!< CTIMER_STCFG_COMPARE_D_EN */ - CTIMER_STCFG_COMPARE_D_EN_DISABLE = 0, /*!< DISABLE : Compare D disabled. value. */ - CTIMER_STCFG_COMPARE_D_EN_ENABLE = 1, /*!< ENABLE : Compare D enabled. value. */ -} CTIMER_STCFG_COMPARE_D_EN_Enum; - -/* ========================================== CTIMER STCFG COMPARE_C_EN [10..10] =========================================== */ -typedef enum { /*!< CTIMER_STCFG_COMPARE_C_EN */ - CTIMER_STCFG_COMPARE_C_EN_DISABLE = 0, /*!< DISABLE : Compare C disabled. value. */ - CTIMER_STCFG_COMPARE_C_EN_ENABLE = 1, /*!< ENABLE : Compare C enabled. value. */ -} CTIMER_STCFG_COMPARE_C_EN_Enum; - -/* =========================================== CTIMER STCFG COMPARE_B_EN [9..9] ============================================ */ -typedef enum { /*!< CTIMER_STCFG_COMPARE_B_EN */ - CTIMER_STCFG_COMPARE_B_EN_DISABLE = 0, /*!< DISABLE : Compare B disabled. value. */ - CTIMER_STCFG_COMPARE_B_EN_ENABLE = 1, /*!< ENABLE : Compare B enabled. value. */ -} CTIMER_STCFG_COMPARE_B_EN_Enum; - -/* =========================================== CTIMER STCFG COMPARE_A_EN [8..8] ============================================ */ -typedef enum { /*!< CTIMER_STCFG_COMPARE_A_EN */ - CTIMER_STCFG_COMPARE_A_EN_DISABLE = 0, /*!< DISABLE : Compare A disabled. value. */ - CTIMER_STCFG_COMPARE_A_EN_ENABLE = 1, /*!< ENABLE : Compare A enabled. value. */ -} CTIMER_STCFG_COMPARE_A_EN_Enum; - -/* ============================================== CTIMER STCFG CLKSEL [0..3] =============================================== */ -typedef enum { /*!< CTIMER_STCFG_CLKSEL */ - CTIMER_STCFG_CLKSEL_NOCLK = 0, /*!< NOCLK : No clock enabled. value. */ - CTIMER_STCFG_CLKSEL_HFRC_DIV16 = 1, /*!< HFRC_DIV16 : 3MHz from the HFRC clock divider. value. */ - CTIMER_STCFG_CLKSEL_HFRC_DIV256 = 2, /*!< HFRC_DIV256 : 187.5KHz from the HFRC clock divider. value. */ - CTIMER_STCFG_CLKSEL_XTAL_DIV1 = 3, /*!< XTAL_DIV1 : 32768Hz from the crystal oscillator. value. */ - CTIMER_STCFG_CLKSEL_XTAL_DIV2 = 4, /*!< XTAL_DIV2 : 16384Hz from the crystal oscillator. value. */ - CTIMER_STCFG_CLKSEL_XTAL_DIV32 = 5, /*!< XTAL_DIV32 : 1024Hz from the crystal oscillator. value. */ - CTIMER_STCFG_CLKSEL_LFRC_DIV1 = 6, /*!< LFRC_DIV1 : Approximately 1KHz from the LFRC oscillator (uncalibrated). - value. */ - CTIMER_STCFG_CLKSEL_CTIMER0A = 7, /*!< CTIMER0A : Use CTIMER 0 section A as a prescaler for the clock - source. value. */ - CTIMER_STCFG_CLKSEL_CTIMER0B = 8, /*!< CTIMER0B : Use CTIMER 0 section B (or A and B linked together) - as a prescaler for the clock source. value. */ -} CTIMER_STCFG_CLKSEL_Enum; - -/* ========================================================= STTMR ========================================================= */ -/* ==================================================== CAPTURECONTROL ===================================================== */ -/* ========================================= CTIMER CAPTURECONTROL CAPTURE3 [3..3] ========================================= */ -typedef enum { /*!< CTIMER_CAPTURECONTROL_CAPTURE3 */ - CTIMER_CAPTURECONTROL_CAPTURE3_DISABLE = 0, /*!< DISABLE : Capture function disabled. value. */ - CTIMER_CAPTURECONTROL_CAPTURE3_ENABLE = 1, /*!< ENABLE : Capture function enabled. value. */ -} CTIMER_CAPTURECONTROL_CAPTURE3_Enum; - -/* ========================================= CTIMER CAPTURECONTROL CAPTURE2 [2..2] ========================================= */ -typedef enum { /*!< CTIMER_CAPTURECONTROL_CAPTURE2 */ - CTIMER_CAPTURECONTROL_CAPTURE2_DISABLE = 0, /*!< DISABLE : Capture function disabled. value. */ - CTIMER_CAPTURECONTROL_CAPTURE2_ENABLE = 1, /*!< ENABLE : Capture function enabled. value. */ -} CTIMER_CAPTURECONTROL_CAPTURE2_Enum; - -/* ========================================= CTIMER CAPTURECONTROL CAPTURE1 [1..1] ========================================= */ -typedef enum { /*!< CTIMER_CAPTURECONTROL_CAPTURE1 */ - CTIMER_CAPTURECONTROL_CAPTURE1_DISABLE = 0, /*!< DISABLE : Capture function disabled. value. */ - CTIMER_CAPTURECONTROL_CAPTURE1_ENABLE = 1, /*!< ENABLE : Capture function enabled. value. */ -} CTIMER_CAPTURECONTROL_CAPTURE1_Enum; - -/* ========================================= CTIMER CAPTURECONTROL CAPTURE0 [0..0] ========================================= */ -typedef enum { /*!< CTIMER_CAPTURECONTROL_CAPTURE0 */ - CTIMER_CAPTURECONTROL_CAPTURE0_DISABLE = 0, /*!< DISABLE : Capture function disabled. value. */ - CTIMER_CAPTURECONTROL_CAPTURE0_ENABLE = 1, /*!< ENABLE : Capture function enabled. value. */ -} CTIMER_CAPTURECONTROL_CAPTURE0_Enum; - -/* ======================================================== SCMPR0 ========================================================= */ -/* ======================================================== SCMPR1 ========================================================= */ -/* ======================================================== SCMPR2 ========================================================= */ -/* ======================================================== SCMPR3 ========================================================= */ -/* ======================================================== SCMPR4 ========================================================= */ -/* ======================================================== SCMPR5 ========================================================= */ -/* ======================================================== SCMPR6 ========================================================= */ -/* ======================================================== SCMPR7 ========================================================= */ -/* ======================================================== SCAPT0 ========================================================= */ -/* ======================================================== SCAPT1 ========================================================= */ -/* ======================================================== SCAPT2 ========================================================= */ -/* ======================================================== SCAPT3 ========================================================= */ -/* ========================================================= SNVR0 ========================================================= */ -/* ========================================================= SNVR1 ========================================================= */ -/* ========================================================= SNVR2 ========================================================= */ -/* ========================================================= SNVR3 ========================================================= */ -/* ========================================================= INTEN ========================================================= */ -/* ======================================================== INTSTAT ======================================================== */ -/* ======================================================== INTCLR ========================================================= */ -/* ======================================================== INTSET ========================================================= */ -/* ======================================================= STMINTEN ======================================================== */ -/* =========================================== CTIMER STMINTEN CAPTURED [12..12] =========================================== */ -typedef enum { /*!< CTIMER_STMINTEN_CAPTURED */ - CTIMER_STMINTEN_CAPTURED_CAPD_INT = 1, /*!< CAPD_INT : Capture D interrupt status bit was set. value. */ -} CTIMER_STMINTEN_CAPTURED_Enum; - -/* =========================================== CTIMER STMINTEN CAPTUREC [11..11] =========================================== */ -typedef enum { /*!< CTIMER_STMINTEN_CAPTUREC */ - CTIMER_STMINTEN_CAPTUREC_CAPC_INT = 1, /*!< CAPC_INT : CAPTURE C interrupt status bit was set. value. */ -} CTIMER_STMINTEN_CAPTUREC_Enum; - -/* =========================================== CTIMER STMINTEN CAPTUREB [10..10] =========================================== */ -typedef enum { /*!< CTIMER_STMINTEN_CAPTUREB */ - CTIMER_STMINTEN_CAPTUREB_CAPB_INT = 1, /*!< CAPB_INT : CAPTURE B interrupt status bit was set. value. */ -} CTIMER_STMINTEN_CAPTUREB_Enum; - -/* ============================================ CTIMER STMINTEN CAPTUREA [9..9] ============================================ */ -typedef enum { /*!< CTIMER_STMINTEN_CAPTUREA */ - CTIMER_STMINTEN_CAPTUREA_CAPA_INT = 1, /*!< CAPA_INT : CAPTURE A interrupt status bit was set. value. */ -} CTIMER_STMINTEN_CAPTUREA_Enum; - -/* ============================================ CTIMER STMINTEN OVERFLOW [8..8] ============================================ */ -typedef enum { /*!< CTIMER_STMINTEN_OVERFLOW */ - CTIMER_STMINTEN_OVERFLOW_OFLOW_INT = 1, /*!< OFLOW_INT : Overflow interrupt status bit was set. value. */ -} CTIMER_STMINTEN_OVERFLOW_Enum; - -/* ============================================ CTIMER STMINTEN COMPAREH [7..7] ============================================ */ -typedef enum { /*!< CTIMER_STMINTEN_COMPAREH */ - CTIMER_STMINTEN_COMPAREH_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTEN_COMPAREH_Enum; - -/* ============================================ CTIMER STMINTEN COMPAREG [6..6] ============================================ */ -typedef enum { /*!< CTIMER_STMINTEN_COMPAREG */ - CTIMER_STMINTEN_COMPAREG_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTEN_COMPAREG_Enum; - -/* ============================================ CTIMER STMINTEN COMPAREF [5..5] ============================================ */ -typedef enum { /*!< CTIMER_STMINTEN_COMPAREF */ - CTIMER_STMINTEN_COMPAREF_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTEN_COMPAREF_Enum; - -/* ============================================ CTIMER STMINTEN COMPAREE [4..4] ============================================ */ -typedef enum { /*!< CTIMER_STMINTEN_COMPAREE */ - CTIMER_STMINTEN_COMPAREE_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTEN_COMPAREE_Enum; - -/* ============================================ CTIMER STMINTEN COMPARED [3..3] ============================================ */ -typedef enum { /*!< CTIMER_STMINTEN_COMPARED */ - CTIMER_STMINTEN_COMPARED_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTEN_COMPARED_Enum; - -/* ============================================ CTIMER STMINTEN COMPAREC [2..2] ============================================ */ -typedef enum { /*!< CTIMER_STMINTEN_COMPAREC */ - CTIMER_STMINTEN_COMPAREC_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTEN_COMPAREC_Enum; - -/* ============================================ CTIMER STMINTEN COMPAREB [1..1] ============================================ */ -typedef enum { /*!< CTIMER_STMINTEN_COMPAREB */ - CTIMER_STMINTEN_COMPAREB_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTEN_COMPAREB_Enum; - -/* ============================================ CTIMER STMINTEN COMPAREA [0..0] ============================================ */ -typedef enum { /*!< CTIMER_STMINTEN_COMPAREA */ - CTIMER_STMINTEN_COMPAREA_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTEN_COMPAREA_Enum; - -/* ====================================================== STMINTSTAT ======================================================= */ -/* ========================================== CTIMER STMINTSTAT CAPTURED [12..12] ========================================== */ -typedef enum { /*!< CTIMER_STMINTSTAT_CAPTURED */ - CTIMER_STMINTSTAT_CAPTURED_CAPD_INT = 1, /*!< CAPD_INT : Capture D interrupt status bit was set. value. */ -} CTIMER_STMINTSTAT_CAPTURED_Enum; - -/* ========================================== CTIMER STMINTSTAT CAPTUREC [11..11] ========================================== */ -typedef enum { /*!< CTIMER_STMINTSTAT_CAPTUREC */ - CTIMER_STMINTSTAT_CAPTUREC_CAPC_INT = 1, /*!< CAPC_INT : CAPTURE C interrupt status bit was set. value. */ -} CTIMER_STMINTSTAT_CAPTUREC_Enum; - -/* ========================================== CTIMER STMINTSTAT CAPTUREB [10..10] ========================================== */ -typedef enum { /*!< CTIMER_STMINTSTAT_CAPTUREB */ - CTIMER_STMINTSTAT_CAPTUREB_CAPB_INT = 1, /*!< CAPB_INT : CAPTURE B interrupt status bit was set. value. */ -} CTIMER_STMINTSTAT_CAPTUREB_Enum; - -/* =========================================== CTIMER STMINTSTAT CAPTUREA [9..9] =========================================== */ -typedef enum { /*!< CTIMER_STMINTSTAT_CAPTUREA */ - CTIMER_STMINTSTAT_CAPTUREA_CAPA_INT = 1, /*!< CAPA_INT : CAPTURE A interrupt status bit was set. value. */ -} CTIMER_STMINTSTAT_CAPTUREA_Enum; - -/* =========================================== CTIMER STMINTSTAT OVERFLOW [8..8] =========================================== */ -typedef enum { /*!< CTIMER_STMINTSTAT_OVERFLOW */ - CTIMER_STMINTSTAT_OVERFLOW_OFLOW_INT = 1, /*!< OFLOW_INT : Overflow interrupt status bit was set. value. */ -} CTIMER_STMINTSTAT_OVERFLOW_Enum; - -/* =========================================== CTIMER STMINTSTAT COMPAREH [7..7] =========================================== */ -typedef enum { /*!< CTIMER_STMINTSTAT_COMPAREH */ - CTIMER_STMINTSTAT_COMPAREH_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTSTAT_COMPAREH_Enum; - -/* =========================================== CTIMER STMINTSTAT COMPAREG [6..6] =========================================== */ -typedef enum { /*!< CTIMER_STMINTSTAT_COMPAREG */ - CTIMER_STMINTSTAT_COMPAREG_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTSTAT_COMPAREG_Enum; - -/* =========================================== CTIMER STMINTSTAT COMPAREF [5..5] =========================================== */ -typedef enum { /*!< CTIMER_STMINTSTAT_COMPAREF */ - CTIMER_STMINTSTAT_COMPAREF_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTSTAT_COMPAREF_Enum; - -/* =========================================== CTIMER STMINTSTAT COMPAREE [4..4] =========================================== */ -typedef enum { /*!< CTIMER_STMINTSTAT_COMPAREE */ - CTIMER_STMINTSTAT_COMPAREE_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTSTAT_COMPAREE_Enum; - -/* =========================================== CTIMER STMINTSTAT COMPARED [3..3] =========================================== */ -typedef enum { /*!< CTIMER_STMINTSTAT_COMPARED */ - CTIMER_STMINTSTAT_COMPARED_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTSTAT_COMPARED_Enum; - -/* =========================================== CTIMER STMINTSTAT COMPAREC [2..2] =========================================== */ -typedef enum { /*!< CTIMER_STMINTSTAT_COMPAREC */ - CTIMER_STMINTSTAT_COMPAREC_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTSTAT_COMPAREC_Enum; - -/* =========================================== CTIMER STMINTSTAT COMPAREB [1..1] =========================================== */ -typedef enum { /*!< CTIMER_STMINTSTAT_COMPAREB */ - CTIMER_STMINTSTAT_COMPAREB_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTSTAT_COMPAREB_Enum; - -/* =========================================== CTIMER STMINTSTAT COMPAREA [0..0] =========================================== */ -typedef enum { /*!< CTIMER_STMINTSTAT_COMPAREA */ - CTIMER_STMINTSTAT_COMPAREA_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTSTAT_COMPAREA_Enum; - -/* ======================================================= STMINTCLR ======================================================= */ -/* ========================================== CTIMER STMINTCLR CAPTURED [12..12] =========================================== */ -typedef enum { /*!< CTIMER_STMINTCLR_CAPTURED */ - CTIMER_STMINTCLR_CAPTURED_CAPD_INT = 1, /*!< CAPD_INT : Capture D interrupt status bit was set. value. */ -} CTIMER_STMINTCLR_CAPTURED_Enum; - -/* ========================================== CTIMER STMINTCLR CAPTUREC [11..11] =========================================== */ -typedef enum { /*!< CTIMER_STMINTCLR_CAPTUREC */ - CTIMER_STMINTCLR_CAPTUREC_CAPC_INT = 1, /*!< CAPC_INT : CAPTURE C interrupt status bit was set. value. */ -} CTIMER_STMINTCLR_CAPTUREC_Enum; - -/* ========================================== CTIMER STMINTCLR CAPTUREB [10..10] =========================================== */ -typedef enum { /*!< CTIMER_STMINTCLR_CAPTUREB */ - CTIMER_STMINTCLR_CAPTUREB_CAPB_INT = 1, /*!< CAPB_INT : CAPTURE B interrupt status bit was set. value. */ -} CTIMER_STMINTCLR_CAPTUREB_Enum; - -/* =========================================== CTIMER STMINTCLR CAPTUREA [9..9] ============================================ */ -typedef enum { /*!< CTIMER_STMINTCLR_CAPTUREA */ - CTIMER_STMINTCLR_CAPTUREA_CAPA_INT = 1, /*!< CAPA_INT : CAPTURE A interrupt status bit was set. value. */ -} CTIMER_STMINTCLR_CAPTUREA_Enum; - -/* =========================================== CTIMER STMINTCLR OVERFLOW [8..8] ============================================ */ -typedef enum { /*!< CTIMER_STMINTCLR_OVERFLOW */ - CTIMER_STMINTCLR_OVERFLOW_OFLOW_INT = 1, /*!< OFLOW_INT : Overflow interrupt status bit was set. value. */ -} CTIMER_STMINTCLR_OVERFLOW_Enum; - -/* =========================================== CTIMER STMINTCLR COMPAREH [7..7] ============================================ */ -typedef enum { /*!< CTIMER_STMINTCLR_COMPAREH */ - CTIMER_STMINTCLR_COMPAREH_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTCLR_COMPAREH_Enum; - -/* =========================================== CTIMER STMINTCLR COMPAREG [6..6] ============================================ */ -typedef enum { /*!< CTIMER_STMINTCLR_COMPAREG */ - CTIMER_STMINTCLR_COMPAREG_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTCLR_COMPAREG_Enum; - -/* =========================================== CTIMER STMINTCLR COMPAREF [5..5] ============================================ */ -typedef enum { /*!< CTIMER_STMINTCLR_COMPAREF */ - CTIMER_STMINTCLR_COMPAREF_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTCLR_COMPAREF_Enum; - -/* =========================================== CTIMER STMINTCLR COMPAREE [4..4] ============================================ */ -typedef enum { /*!< CTIMER_STMINTCLR_COMPAREE */ - CTIMER_STMINTCLR_COMPAREE_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTCLR_COMPAREE_Enum; - -/* =========================================== CTIMER STMINTCLR COMPARED [3..3] ============================================ */ -typedef enum { /*!< CTIMER_STMINTCLR_COMPARED */ - CTIMER_STMINTCLR_COMPARED_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTCLR_COMPARED_Enum; - -/* =========================================== CTIMER STMINTCLR COMPAREC [2..2] ============================================ */ -typedef enum { /*!< CTIMER_STMINTCLR_COMPAREC */ - CTIMER_STMINTCLR_COMPAREC_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTCLR_COMPAREC_Enum; - -/* =========================================== CTIMER STMINTCLR COMPAREB [1..1] ============================================ */ -typedef enum { /*!< CTIMER_STMINTCLR_COMPAREB */ - CTIMER_STMINTCLR_COMPAREB_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTCLR_COMPAREB_Enum; - -/* =========================================== CTIMER STMINTCLR COMPAREA [0..0] ============================================ */ -typedef enum { /*!< CTIMER_STMINTCLR_COMPAREA */ - CTIMER_STMINTCLR_COMPAREA_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTCLR_COMPAREA_Enum; - -/* ======================================================= STMINTSET ======================================================= */ -/* ========================================== CTIMER STMINTSET CAPTURED [12..12] =========================================== */ -typedef enum { /*!< CTIMER_STMINTSET_CAPTURED */ - CTIMER_STMINTSET_CAPTURED_CAPD_INT = 1, /*!< CAPD_INT : Capture D interrupt status bit was set. value. */ -} CTIMER_STMINTSET_CAPTURED_Enum; - -/* ========================================== CTIMER STMINTSET CAPTUREC [11..11] =========================================== */ -typedef enum { /*!< CTIMER_STMINTSET_CAPTUREC */ - CTIMER_STMINTSET_CAPTUREC_CAPC_INT = 1, /*!< CAPC_INT : CAPTURE C interrupt status bit was set. value. */ -} CTIMER_STMINTSET_CAPTUREC_Enum; - -/* ========================================== CTIMER STMINTSET CAPTUREB [10..10] =========================================== */ -typedef enum { /*!< CTIMER_STMINTSET_CAPTUREB */ - CTIMER_STMINTSET_CAPTUREB_CAPB_INT = 1, /*!< CAPB_INT : CAPTURE B interrupt status bit was set. value. */ -} CTIMER_STMINTSET_CAPTUREB_Enum; - -/* =========================================== CTIMER STMINTSET CAPTUREA [9..9] ============================================ */ -typedef enum { /*!< CTIMER_STMINTSET_CAPTUREA */ - CTIMER_STMINTSET_CAPTUREA_CAPA_INT = 1, /*!< CAPA_INT : CAPTURE A interrupt status bit was set. value. */ -} CTIMER_STMINTSET_CAPTUREA_Enum; - -/* =========================================== CTIMER STMINTSET OVERFLOW [8..8] ============================================ */ -typedef enum { /*!< CTIMER_STMINTSET_OVERFLOW */ - CTIMER_STMINTSET_OVERFLOW_OFLOW_INT = 1, /*!< OFLOW_INT : Overflow interrupt status bit was set. value. */ -} CTIMER_STMINTSET_OVERFLOW_Enum; - -/* =========================================== CTIMER STMINTSET COMPAREH [7..7] ============================================ */ -typedef enum { /*!< CTIMER_STMINTSET_COMPAREH */ - CTIMER_STMINTSET_COMPAREH_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTSET_COMPAREH_Enum; - -/* =========================================== CTIMER STMINTSET COMPAREG [6..6] ============================================ */ -typedef enum { /*!< CTIMER_STMINTSET_COMPAREG */ - CTIMER_STMINTSET_COMPAREG_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTSET_COMPAREG_Enum; - -/* =========================================== CTIMER STMINTSET COMPAREF [5..5] ============================================ */ -typedef enum { /*!< CTIMER_STMINTSET_COMPAREF */ - CTIMER_STMINTSET_COMPAREF_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTSET_COMPAREF_Enum; - -/* =========================================== CTIMER STMINTSET COMPAREE [4..4] ============================================ */ -typedef enum { /*!< CTIMER_STMINTSET_COMPAREE */ - CTIMER_STMINTSET_COMPAREE_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTSET_COMPAREE_Enum; - -/* =========================================== CTIMER STMINTSET COMPARED [3..3] ============================================ */ -typedef enum { /*!< CTIMER_STMINTSET_COMPARED */ - CTIMER_STMINTSET_COMPARED_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTSET_COMPARED_Enum; - -/* =========================================== CTIMER STMINTSET COMPAREC [2..2] ============================================ */ -typedef enum { /*!< CTIMER_STMINTSET_COMPAREC */ - CTIMER_STMINTSET_COMPAREC_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTSET_COMPAREC_Enum; - -/* =========================================== CTIMER STMINTSET COMPAREB [1..1] ============================================ */ -typedef enum { /*!< CTIMER_STMINTSET_COMPAREB */ - CTIMER_STMINTSET_COMPAREB_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTSET_COMPAREB_Enum; - -/* =========================================== CTIMER STMINTSET COMPAREA [0..0] ============================================ */ -typedef enum { /*!< CTIMER_STMINTSET_COMPAREA */ - CTIMER_STMINTSET_COMPAREA_COMPARED = 1, /*!< COMPARED : COUNTER greater than or equal to COMPARE register. - value. */ -} CTIMER_STMINTSET_COMPAREA_Enum; - - - -/* =========================================================================================================================== */ -/* ================ GPIO ================ */ -/* =========================================================================================================================== */ - -/* ======================================================== PADREGA ======================================================== */ -/* ============================================ GPIO PADREGA PAD3PWRUP [30..30] ============================================ */ -typedef enum { /*!< GPIO_PADREGA_PAD3PWRUP */ - GPIO_PADREGA_PAD3PWRUP_DIS = 0, /*!< DIS : Power switch disabled value. */ - GPIO_PADREGA_PAD3PWRUP_EN = 1, /*!< EN : Power switch enabled (switched to VDD) value. */ -} GPIO_PADREGA_PAD3PWRUP_Enum; - -/* =========================================== GPIO PADREGA PAD3FNCSEL [27..29] ============================================ */ -typedef enum { /*!< GPIO_PADREGA_PAD3FNCSEL */ - GPIO_PADREGA_PAD3FNCSEL_UA0RTS = 0, /*!< UA0RTS : Configure as the UART0 RTS output value. */ - GPIO_PADREGA_PAD3FNCSEL_SLnCE = 1, /*!< SLnCE : Configure as the IOSLAVE SPI nCE signal value. */ - GPIO_PADREGA_PAD3FNCSEL_NCE3 = 2, /*!< NCE3 : IOM/MSPI nCE group 3 value. */ - GPIO_PADREGA_PAD3FNCSEL_GPIO3 = 3, /*!< GPIO3 : Configure as GPIO3 value. */ - GPIO_PADREGA_PAD3FNCSEL_MSPI7 = 5, /*!< MSPI7 : MSPI data connection 7 value. */ - GPIO_PADREGA_PAD3FNCSEL_TRIG1 = 6, /*!< TRIG1 : Configure as the ADC Trigger 1 signal value. */ - GPIO_PADREGA_PAD3FNCSEL_I2S_WCLK = 7, /*!< I2S_WCLK : Configure as the PDM I2S Word Clock input value. */ -} GPIO_PADREGA_PAD3FNCSEL_Enum; - -/* ============================================ GPIO PADREGA PAD3STRNG [26..26] ============================================ */ -typedef enum { /*!< GPIO_PADREGA_PAD3STRNG */ - GPIO_PADREGA_PAD3STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGA_PAD3STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGA_PAD3STRNG_Enum; - -/* ============================================ GPIO PADREGA PAD3INPEN [25..25] ============================================ */ -typedef enum { /*!< GPIO_PADREGA_PAD3INPEN */ - GPIO_PADREGA_PAD3INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGA_PAD3INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGA_PAD3INPEN_Enum; - -/* ============================================ GPIO PADREGA PAD3PULL [24..24] ============================================= */ -typedef enum { /*!< GPIO_PADREGA_PAD3PULL */ - GPIO_PADREGA_PAD3PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGA_PAD3PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGA_PAD3PULL_Enum; - -/* =========================================== GPIO PADREGA PAD2FNCSEL [19..21] ============================================ */ -typedef enum { /*!< GPIO_PADREGA_PAD2FNCSEL */ - GPIO_PADREGA_PAD2FNCSEL_SLMISO = 1, /*!< SLMISO : Configure as the IOSLAVE SPI MISO signal value. */ - GPIO_PADREGA_PAD2FNCSEL_UART0RX = 2, /*!< UART0RX : Configure as the UART0 RX input value. */ - GPIO_PADREGA_PAD2FNCSEL_GPIO2 = 3, /*!< GPIO2 : Configure as GPIO2 value. */ - GPIO_PADREGA_PAD2FNCSEL_MSPI6 = 5, /*!< MSPI6 : CMSPI data connection 6 value. */ - GPIO_PADREGA_PAD2FNCSEL_NCE2 = 7, /*!< NCE2 : IOM/MSPI nCE group 2 value. */ -} GPIO_PADREGA_PAD2FNCSEL_Enum; - -/* ============================================ GPIO PADREGA PAD2STRNG [18..18] ============================================ */ -typedef enum { /*!< GPIO_PADREGA_PAD2STRNG */ - GPIO_PADREGA_PAD2STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGA_PAD2STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGA_PAD2STRNG_Enum; - -/* ============================================ GPIO PADREGA PAD2INPEN [17..17] ============================================ */ -typedef enum { /*!< GPIO_PADREGA_PAD2INPEN */ - GPIO_PADREGA_PAD2INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGA_PAD2INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGA_PAD2INPEN_Enum; - -/* ============================================ GPIO PADREGA PAD2PULL [16..16] ============================================= */ -typedef enum { /*!< GPIO_PADREGA_PAD2PULL */ - GPIO_PADREGA_PAD2PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGA_PAD2PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGA_PAD2PULL_Enum; - -/* ============================================ GPIO PADREGA PAD1RSEL [14..15] ============================================= */ -typedef enum { /*!< GPIO_PADREGA_PAD1RSEL */ - GPIO_PADREGA_PAD1RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ - GPIO_PADREGA_PAD1RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ - GPIO_PADREGA_PAD1RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ - GPIO_PADREGA_PAD1RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ -} GPIO_PADREGA_PAD1RSEL_Enum; - -/* =========================================== GPIO PADREGA PAD1FNCSEL [11..13] ============================================ */ -typedef enum { /*!< GPIO_PADREGA_PAD1FNCSEL */ - GPIO_PADREGA_PAD1FNCSEL_SLSDAWIR3 = 0, /*!< SLSDAWIR3 : Configure as the IOSLAVE I2C SDA or SPI WIR3 signal - value. */ - GPIO_PADREGA_PAD1FNCSEL_SLMOSI = 1, /*!< SLMOSI : Configure as the IOSLAVE SPI MOSI signal value. */ - GPIO_PADREGA_PAD1FNCSEL_UART0TX = 2, /*!< UART0TX : Configure as the UART0 TX output signal value. */ - GPIO_PADREGA_PAD1FNCSEL_GPIO1 = 3, /*!< GPIO1 : Configure as GPIO1 value. */ - GPIO_PADREGA_PAD1FNCSEL_MSPI5 = 5, /*!< MSPI5 : MSPI data connection 5 value. */ - GPIO_PADREGA_PAD1FNCSEL_NCE1 = 7, /*!< NCE1 : IOM/MSPI nCE group 1 value. */ -} GPIO_PADREGA_PAD1FNCSEL_Enum; - -/* ============================================ GPIO PADREGA PAD1STRNG [10..10] ============================================ */ -typedef enum { /*!< GPIO_PADREGA_PAD1STRNG */ - GPIO_PADREGA_PAD1STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGA_PAD1STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGA_PAD1STRNG_Enum; - -/* ============================================= GPIO PADREGA PAD1INPEN [9..9] ============================================= */ -typedef enum { /*!< GPIO_PADREGA_PAD1INPEN */ - GPIO_PADREGA_PAD1INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGA_PAD1INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGA_PAD1INPEN_Enum; - -/* ============================================= GPIO PADREGA PAD1PULL [8..8] ============================================== */ -typedef enum { /*!< GPIO_PADREGA_PAD1PULL */ - GPIO_PADREGA_PAD1PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGA_PAD1PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGA_PAD1PULL_Enum; - -/* ============================================= GPIO PADREGA PAD0RSEL [6..7] ============================================== */ -typedef enum { /*!< GPIO_PADREGA_PAD0RSEL */ - GPIO_PADREGA_PAD0RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ - GPIO_PADREGA_PAD0RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ - GPIO_PADREGA_PAD0RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ - GPIO_PADREGA_PAD0RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ -} GPIO_PADREGA_PAD0RSEL_Enum; - -/* ============================================ GPIO PADREGA PAD0FNCSEL [3..5] ============================================= */ -typedef enum { /*!< GPIO_PADREGA_PAD0FNCSEL */ - GPIO_PADREGA_PAD0FNCSEL_SLSCL = 0, /*!< SLSCL : Configure as the IOSLAVE I2C SCL signal value. */ - GPIO_PADREGA_PAD0FNCSEL_SLSCK = 1, /*!< SLSCK : Configure as the IOSLAVE SPI SCK signal value. */ - GPIO_PADREGA_PAD0FNCSEL_CLKOUT = 2, /*!< CLKOUT : Configure as the CLKOUT signal value. */ - GPIO_PADREGA_PAD0FNCSEL_GPIO0 = 3, /*!< GPIO0 : Configure as GPIO0 value. */ - GPIO_PADREGA_PAD0FNCSEL_MSPI4 = 5, /*!< MSPI4 : MSPI data connection 4 value. */ - GPIO_PADREGA_PAD0FNCSEL_NCE0 = 7, /*!< NCE0 : IOM/MSPI nCE group 0 value. */ -} GPIO_PADREGA_PAD0FNCSEL_Enum; - -/* ============================================= GPIO PADREGA PAD0STRNG [2..2] ============================================= */ -typedef enum { /*!< GPIO_PADREGA_PAD0STRNG */ - GPIO_PADREGA_PAD0STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGA_PAD0STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGA_PAD0STRNG_Enum; - -/* ============================================= GPIO PADREGA PAD0INPEN [1..1] ============================================= */ -typedef enum { /*!< GPIO_PADREGA_PAD0INPEN */ - GPIO_PADREGA_PAD0INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGA_PAD0INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGA_PAD0INPEN_Enum; - -/* ============================================= GPIO PADREGA PAD0PULL [0..0] ============================================== */ -typedef enum { /*!< GPIO_PADREGA_PAD0PULL */ - GPIO_PADREGA_PAD0PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGA_PAD0PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGA_PAD0PULL_Enum; - -/* ======================================================== PADREGB ======================================================== */ -/* =========================================== GPIO PADREGB PAD7FNCSEL [27..29] ============================================ */ -typedef enum { /*!< GPIO_PADREGB_PAD7FNCSEL */ - GPIO_PADREGB_PAD7FNCSEL_NCE7 = 0, /*!< NCE7 : IOM/MSPI nCE group 7 value. */ - GPIO_PADREGB_PAD7FNCSEL_M0MOSI = 1, /*!< M0MOSI : Configure as the IOMSTR0 SPI MOSI signal value. */ - GPIO_PADREGB_PAD7FNCSEL_CLKOUT = 2, /*!< CLKOUT : Configure as the CLKOUT signal value. */ - GPIO_PADREGB_PAD7FNCSEL_GPIO7 = 3, /*!< GPIO7 : Configure as GPIO7 value. */ - GPIO_PADREGB_PAD7FNCSEL_TRIG0 = 4, /*!< TRIG0 : Configure as the ADC Trigger 0 signal value. */ - GPIO_PADREGB_PAD7FNCSEL_UART0TX = 5, /*!< UART0TX : Configure as the UART0 TX output signal value. */ - GPIO_PADREGB_PAD7FNCSEL_CT19 = 7, /*!< CT19 : CTIMER connection 19 value. */ -} GPIO_PADREGB_PAD7FNCSEL_Enum; - -/* ============================================ GPIO PADREGB PAD7STRNG [26..26] ============================================ */ -typedef enum { /*!< GPIO_PADREGB_PAD7STRNG */ - GPIO_PADREGB_PAD7STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGB_PAD7STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGB_PAD7STRNG_Enum; - -/* ============================================ GPIO PADREGB PAD7INPEN [25..25] ============================================ */ -typedef enum { /*!< GPIO_PADREGB_PAD7INPEN */ - GPIO_PADREGB_PAD7INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGB_PAD7INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGB_PAD7INPEN_Enum; - -/* ============================================ GPIO PADREGB PAD7PULL [24..24] ============================================= */ -typedef enum { /*!< GPIO_PADREGB_PAD7PULL */ - GPIO_PADREGB_PAD7PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGB_PAD7PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGB_PAD7PULL_Enum; - -/* ============================================ GPIO PADREGB PAD6RSEL [22..23] ============================================= */ -typedef enum { /*!< GPIO_PADREGB_PAD6RSEL */ - GPIO_PADREGB_PAD6RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ - GPIO_PADREGB_PAD6RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ - GPIO_PADREGB_PAD6RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ - GPIO_PADREGB_PAD6RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ -} GPIO_PADREGB_PAD6RSEL_Enum; - -/* =========================================== GPIO PADREGB PAD6FNCSEL [19..21] ============================================ */ -typedef enum { /*!< GPIO_PADREGB_PAD6FNCSEL */ - GPIO_PADREGB_PAD6FNCSEL_M0SDAWIR3 = 0, /*!< M0SDAWIR3 : Configure as the IOMSTR0 I2C SDA or SPI WIR3 signal - value. */ - GPIO_PADREGB_PAD6FNCSEL_M0MISO = 1, /*!< M0MISO : Configure as the IOMSTR0 SPI MISO signal value. */ - GPIO_PADREGB_PAD6FNCSEL_UA0CTS = 2, /*!< UA0CTS : Configure as the UART0 CTS input signal value. */ - GPIO_PADREGB_PAD6FNCSEL_GPIO6 = 3, /*!< GPIO6 : Configure as GPIO6 value. */ - GPIO_PADREGB_PAD6FNCSEL_CT10 = 5, /*!< CT10 : CTIMER connection 10 value. */ - GPIO_PADREGB_PAD6FNCSEL_I2S_DAT = 7, /*!< I2S_DAT : Configure as the PDM I2S Data output signal value. */ -} GPIO_PADREGB_PAD6FNCSEL_Enum; - -/* ============================================ GPIO PADREGB PAD6STRNG [18..18] ============================================ */ -typedef enum { /*!< GPIO_PADREGB_PAD6STRNG */ - GPIO_PADREGB_PAD6STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGB_PAD6STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGB_PAD6STRNG_Enum; - -/* ============================================ GPIO PADREGB PAD6INPEN [17..17] ============================================ */ -typedef enum { /*!< GPIO_PADREGB_PAD6INPEN */ - GPIO_PADREGB_PAD6INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGB_PAD6INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGB_PAD6INPEN_Enum; - -/* ============================================ GPIO PADREGB PAD6PULL [16..16] ============================================= */ -typedef enum { /*!< GPIO_PADREGB_PAD6PULL */ - GPIO_PADREGB_PAD6PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGB_PAD6PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGB_PAD6PULL_Enum; - -/* ============================================ GPIO PADREGB PAD5RSEL [14..15] ============================================= */ -typedef enum { /*!< GPIO_PADREGB_PAD5RSEL */ - GPIO_PADREGB_PAD5RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ - GPIO_PADREGB_PAD5RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ - GPIO_PADREGB_PAD5RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ - GPIO_PADREGB_PAD5RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ -} GPIO_PADREGB_PAD5RSEL_Enum; - -/* =========================================== GPIO PADREGB PAD5FNCSEL [11..13] ============================================ */ -typedef enum { /*!< GPIO_PADREGB_PAD5FNCSEL */ - GPIO_PADREGB_PAD5FNCSEL_M0SCL = 0, /*!< M0SCL : Configure as the IOMSTR0 I2C SCL signal value. */ - GPIO_PADREGB_PAD5FNCSEL_M0SCK = 1, /*!< M0SCK : Configure as the IOMSTR0 SPI SCK signal value. */ - GPIO_PADREGB_PAD5FNCSEL_UA0RTS = 2, /*!< UA0RTS : Configure as the UART0 RTS signal output value. */ - GPIO_PADREGB_PAD5FNCSEL_GPIO5 = 3, /*!< GPIO5 : Configure as GPIO5 value. */ - GPIO_PADREGB_PAD5FNCSEL_EXTHFA = 5, /*!< EXTHFA : Configure as the External HFA input clock value. */ - GPIO_PADREGB_PAD5FNCSEL_CT8 = 7, /*!< CT8 : CTIMER connection 8 value. */ -} GPIO_PADREGB_PAD5FNCSEL_Enum; - -/* ============================================ GPIO PADREGB PAD5STRNG [10..10] ============================================ */ -typedef enum { /*!< GPIO_PADREGB_PAD5STRNG */ - GPIO_PADREGB_PAD5STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGB_PAD5STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGB_PAD5STRNG_Enum; - -/* ============================================= GPIO PADREGB PAD5INPEN [9..9] ============================================= */ -typedef enum { /*!< GPIO_PADREGB_PAD5INPEN */ - GPIO_PADREGB_PAD5INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGB_PAD5INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGB_PAD5INPEN_Enum; - -/* ============================================= GPIO PADREGB PAD5PULL [8..8] ============================================== */ -typedef enum { /*!< GPIO_PADREGB_PAD5PULL */ - GPIO_PADREGB_PAD5PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGB_PAD5PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGB_PAD5PULL_Enum; - -/* ============================================ GPIO PADREGB PAD4FNCSEL [3..5] ============================================= */ -typedef enum { /*!< GPIO_PADREGB_PAD4FNCSEL */ - GPIO_PADREGB_PAD4FNCSEL_UA0CTS = 0, /*!< UA0CTS : Configure as the UART0 CTS input signal value. */ - GPIO_PADREGB_PAD4FNCSEL_SLINT = 1, /*!< SLINT : Configure as the IOSLAVE interrupt out signal value. */ - GPIO_PADREGB_PAD4FNCSEL_NCE4 = 2, /*!< NCE4 : IOM/SPI nCE group 4 value. */ - GPIO_PADREGB_PAD4FNCSEL_GPIO4 = 3, /*!< GPIO4 : Configure as GPIO4 value. */ - GPIO_PADREGB_PAD4FNCSEL_UART0RX = 5, /*!< UART0RX : Configure as the UART0 RX input value. */ - GPIO_PADREGB_PAD4FNCSEL_CT17 = 6, /*!< CT17 : CTIMER connection 17 value. */ - GPIO_PADREGB_PAD4FNCSEL_MSPI2 = 7, /*!< MSPI2 : MSPI data connection 2 value. */ -} GPIO_PADREGB_PAD4FNCSEL_Enum; - -/* ============================================= GPIO PADREGB PAD4STRNG [2..2] ============================================= */ -typedef enum { /*!< GPIO_PADREGB_PAD4STRNG */ - GPIO_PADREGB_PAD4STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGB_PAD4STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGB_PAD4STRNG_Enum; - -/* ============================================= GPIO PADREGB PAD4INPEN [1..1] ============================================= */ -typedef enum { /*!< GPIO_PADREGB_PAD4INPEN */ - GPIO_PADREGB_PAD4INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGB_PAD4INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGB_PAD4INPEN_Enum; - -/* ============================================= GPIO PADREGB PAD4PULL [0..0] ============================================== */ -typedef enum { /*!< GPIO_PADREGB_PAD4PULL */ - GPIO_PADREGB_PAD4PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGB_PAD4PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGB_PAD4PULL_Enum; - -/* ======================================================== PADREGC ======================================================== */ -/* =========================================== GPIO PADREGC PAD11FNCSEL [27..29] =========================================== */ -typedef enum { /*!< GPIO_PADREGC_PAD11FNCSEL */ - GPIO_PADREGC_PAD11FNCSEL_ADCSE2 = 0, /*!< ADCSE2 : Configure as the analog input for ADC single ended - input 2 value. */ - GPIO_PADREGC_PAD11FNCSEL_NCE11 = 1, /*!< NCE11 : IOM/MSPI nCE group 11 value. */ - GPIO_PADREGC_PAD11FNCSEL_CT31 = 2, /*!< CT31 : CTIMER connection 31 value. */ - GPIO_PADREGC_PAD11FNCSEL_GPIO11 = 3, /*!< GPIO11 : Configure as GPIO11 value. */ - GPIO_PADREGC_PAD11FNCSEL_SLINT = 4, /*!< SLINT : Configure as the IOSLAVE interrupt out signal value. */ - GPIO_PADREGC_PAD11FNCSEL_UA1CTS = 5, /*!< UA1CTS : Configure as the UART1 CTS input signal value. */ - GPIO_PADREGC_PAD11FNCSEL_UART0RX = 6, /*!< UART0RX : Configure as the UART0 RX input signal value. */ - GPIO_PADREGC_PAD11FNCSEL_PDM_DATA = 7, /*!< PDM_DATA : Configure as the PDM Data input signal value. */ -} GPIO_PADREGC_PAD11FNCSEL_Enum; - -/* =========================================== GPIO PADREGC PAD11STRNG [26..26] ============================================ */ -typedef enum { /*!< GPIO_PADREGC_PAD11STRNG */ - GPIO_PADREGC_PAD11STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGC_PAD11STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGC_PAD11STRNG_Enum; - -/* =========================================== GPIO PADREGC PAD11INPEN [25..25] ============================================ */ -typedef enum { /*!< GPIO_PADREGC_PAD11INPEN */ - GPIO_PADREGC_PAD11INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGC_PAD11INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGC_PAD11INPEN_Enum; - -/* ============================================ GPIO PADREGC PAD11PULL [24..24] ============================================ */ -typedef enum { /*!< GPIO_PADREGC_PAD11PULL */ - GPIO_PADREGC_PAD11PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGC_PAD11PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGC_PAD11PULL_Enum; - -/* =========================================== GPIO PADREGC PAD10FNCSEL [19..21] =========================================== */ -typedef enum { /*!< GPIO_PADREGC_PAD10FNCSEL */ - GPIO_PADREGC_PAD10FNCSEL_M1MOSI = 1, /*!< M1MOSI : Configure as the IOMSTR1 SPI MOSI signal value. */ - GPIO_PADREGC_PAD10FNCSEL_NCE10 = 2, /*!< NCE10 : IOM/MSPI nCE group 10 value. */ - GPIO_PADREGC_PAD10FNCSEL_GPIO10 = 3, /*!< GPIO10 : Configure as GPIO10 value. */ - GPIO_PADREGC_PAD10FNCSEL_PDMCLK = 4, /*!< PDMCLK : PDM serial clock out value. */ - GPIO_PADREGC_PAD10FNCSEL_UA1RTS = 5, /*!< UA1RTS : Configure as the UART1 RTS output signal value. */ -} GPIO_PADREGC_PAD10FNCSEL_Enum; - -/* =========================================== GPIO PADREGC PAD10STRNG [18..18] ============================================ */ -typedef enum { /*!< GPIO_PADREGC_PAD10STRNG */ - GPIO_PADREGC_PAD10STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGC_PAD10STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGC_PAD10STRNG_Enum; - -/* =========================================== GPIO PADREGC PAD10INPEN [17..17] ============================================ */ -typedef enum { /*!< GPIO_PADREGC_PAD10INPEN */ - GPIO_PADREGC_PAD10INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGC_PAD10INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGC_PAD10INPEN_Enum; - -/* ============================================ GPIO PADREGC PAD10PULL [16..16] ============================================ */ -typedef enum { /*!< GPIO_PADREGC_PAD10PULL */ - GPIO_PADREGC_PAD10PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGC_PAD10PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGC_PAD10PULL_Enum; - -/* ============================================ GPIO PADREGC PAD9RSEL [14..15] ============================================= */ -typedef enum { /*!< GPIO_PADREGC_PAD9RSEL */ - GPIO_PADREGC_PAD9RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ - GPIO_PADREGC_PAD9RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ - GPIO_PADREGC_PAD9RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ - GPIO_PADREGC_PAD9RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ -} GPIO_PADREGC_PAD9RSEL_Enum; - -/* =========================================== GPIO PADREGC PAD9FNCSEL [11..13] ============================================ */ -typedef enum { /*!< GPIO_PADREGC_PAD9FNCSEL */ - GPIO_PADREGC_PAD9FNCSEL_M1SDAWIR3 = 0, /*!< M1SDAWIR3 : Configure as the IOMSTR1 I2C SDA or SPI WIR3 signal - value. */ - GPIO_PADREGC_PAD9FNCSEL_M1MISO = 1, /*!< M1MISO : Configure as the IOMSTR1 SPI MISO signal value. */ - GPIO_PADREGC_PAD9FNCSEL_NCE9 = 2, /*!< NCE9 : IOM/MSPI nCE group 9 value. */ - GPIO_PADREGC_PAD9FNCSEL_GPIO9 = 3, /*!< GPIO9 : Configure as GPIO9 value. */ - GPIO_PADREGC_PAD9FNCSEL_SCCIO = 4, /*!< SCCIO : SCARD data I/O connection value. */ - GPIO_PADREGC_PAD9FNCSEL_UART1RX = 6, /*!< UART1RX : Configure as UART1 RX input signal value. */ -} GPIO_PADREGC_PAD9FNCSEL_Enum; - -/* ============================================ GPIO PADREGC PAD9STRNG [10..10] ============================================ */ -typedef enum { /*!< GPIO_PADREGC_PAD9STRNG */ - GPIO_PADREGC_PAD9STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGC_PAD9STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGC_PAD9STRNG_Enum; - -/* ============================================= GPIO PADREGC PAD9INPEN [9..9] ============================================= */ -typedef enum { /*!< GPIO_PADREGC_PAD9INPEN */ - GPIO_PADREGC_PAD9INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGC_PAD9INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGC_PAD9INPEN_Enum; - -/* ============================================= GPIO PADREGC PAD9PULL [8..8] ============================================== */ -typedef enum { /*!< GPIO_PADREGC_PAD9PULL */ - GPIO_PADREGC_PAD9PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGC_PAD9PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGC_PAD9PULL_Enum; - -/* ============================================= GPIO PADREGC PAD8RSEL [6..7] ============================================== */ -typedef enum { /*!< GPIO_PADREGC_PAD8RSEL */ - GPIO_PADREGC_PAD8RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ - GPIO_PADREGC_PAD8RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ - GPIO_PADREGC_PAD8RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ - GPIO_PADREGC_PAD8RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ -} GPIO_PADREGC_PAD8RSEL_Enum; - -/* ============================================ GPIO PADREGC PAD8FNCSEL [3..5] ============================================= */ -typedef enum { /*!< GPIO_PADREGC_PAD8FNCSEL */ - GPIO_PADREGC_PAD8FNCSEL_M1SCL = 0, /*!< M1SCL : Configure as the IOMSTR1 I2C SCL signal value. */ - GPIO_PADREGC_PAD8FNCSEL_M1SCK = 1, /*!< M1SCK : Configure as the IOMSTR1 SPI SCK signal value. */ - GPIO_PADREGC_PAD8FNCSEL_NCE8 = 2, /*!< NCE8 : IOM/MSPI nCE group 8 value. */ - GPIO_PADREGC_PAD8FNCSEL_GPIO8 = 3, /*!< GPIO8 : Configure as GPIO8 value. */ - GPIO_PADREGC_PAD8FNCSEL_SCCLK = 4, /*!< SCCLK : SCARD serial clock output value. */ - GPIO_PADREGC_PAD8FNCSEL_UART1TX = 6, /*!< UART1TX : Configure as the UART1 TX output signal value. */ -} GPIO_PADREGC_PAD8FNCSEL_Enum; - -/* ============================================= GPIO PADREGC PAD8STRNG [2..2] ============================================= */ -typedef enum { /*!< GPIO_PADREGC_PAD8STRNG */ - GPIO_PADREGC_PAD8STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGC_PAD8STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGC_PAD8STRNG_Enum; - -/* ============================================= GPIO PADREGC PAD8INPEN [1..1] ============================================= */ -typedef enum { /*!< GPIO_PADREGC_PAD8INPEN */ - GPIO_PADREGC_PAD8INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGC_PAD8INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGC_PAD8INPEN_Enum; - -/* ============================================= GPIO PADREGC PAD8PULL [0..0] ============================================== */ -typedef enum { /*!< GPIO_PADREGC_PAD8PULL */ - GPIO_PADREGC_PAD8PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGC_PAD8PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGC_PAD8PULL_Enum; - -/* ======================================================== PADREGD ======================================================== */ -/* =========================================== GPIO PADREGD PAD15FNCSEL [27..29] =========================================== */ -typedef enum { /*!< GPIO_PADREGD_PAD15FNCSEL */ - GPIO_PADREGD_PAD15FNCSEL_ADCD1N = 0, /*!< ADCD1N : Configure as the analog ADC differential pair 1 N input - signal value. */ - GPIO_PADREGD_PAD15FNCSEL_NCE15 = 1, /*!< NCE15 : IOM/MSPI nCE group 15 value. */ - GPIO_PADREGD_PAD15FNCSEL_UART1RX = 2, /*!< UART1RX : Configure as the UART1 RX signal value. */ - GPIO_PADREGD_PAD15FNCSEL_GPIO15 = 3, /*!< GPIO15 : Configure as GPIO15 value. */ - GPIO_PADREGD_PAD15FNCSEL_PDMDATA = 4, /*!< PDMDATA : PDM serial data input value. */ - GPIO_PADREGD_PAD15FNCSEL_EXTXT = 5, /*!< EXTXT : Configure as the external XTAL oscillator input value. */ - GPIO_PADREGD_PAD15FNCSEL_SWDIO = 6, /*!< SWDIO : Configure as an alternate port for the SWDIO I/O signal - value. */ - GPIO_PADREGD_PAD15FNCSEL_SWO = 7, /*!< SWO : Configure as an SWO (Serial Wire Trace output) value. */ -} GPIO_PADREGD_PAD15FNCSEL_Enum; - -/* =========================================== GPIO PADREGD PAD15STRNG [26..26] ============================================ */ -typedef enum { /*!< GPIO_PADREGD_PAD15STRNG */ - GPIO_PADREGD_PAD15STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGD_PAD15STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGD_PAD15STRNG_Enum; - -/* =========================================== GPIO PADREGD PAD15INPEN [25..25] ============================================ */ -typedef enum { /*!< GPIO_PADREGD_PAD15INPEN */ - GPIO_PADREGD_PAD15INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGD_PAD15INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGD_PAD15INPEN_Enum; - -/* ============================================ GPIO PADREGD PAD15PULL [24..24] ============================================ */ -typedef enum { /*!< GPIO_PADREGD_PAD15PULL */ - GPIO_PADREGD_PAD15PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGD_PAD15PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGD_PAD15PULL_Enum; - -/* =========================================== GPIO PADREGD PAD14FNCSEL [19..21] =========================================== */ -typedef enum { /*!< GPIO_PADREGD_PAD14FNCSEL */ - GPIO_PADREGD_PAD14FNCSEL_ADCD1P = 0, /*!< ADCD1P : Configure as the analog ADC differential pair 1 P input - signal value. */ - GPIO_PADREGD_PAD14FNCSEL_NCE14 = 1, /*!< NCE14 : IOM/MSPI nCE group 14 value. */ - GPIO_PADREGD_PAD14FNCSEL_UART1TX = 2, /*!< UART1TX : Configure as the UART1 TX output signal value. */ - GPIO_PADREGD_PAD14FNCSEL_GPIO14 = 3, /*!< GPIO14 : Configure as GPIO14 value. */ - GPIO_PADREGD_PAD14FNCSEL_PDMCLK = 4, /*!< PDMCLK : PDM serial clock output value. */ - GPIO_PADREGD_PAD14FNCSEL_EXTHFS = 5, /*!< EXTHFS : Configure as the External HFRC oscillator input select - value. */ - GPIO_PADREGD_PAD14FNCSEL_SWDCK = 6, /*!< SWDCK : Configure as the alternate input for the SWDCK input - signal value. */ - GPIO_PADREGD_PAD14FNCSEL_32kHzXT = 7, /*!< 32kHzXT : Configure as the 32kHz crystal output signal value. */ -} GPIO_PADREGD_PAD14FNCSEL_Enum; - -/* =========================================== GPIO PADREGD PAD14STRNG [18..18] ============================================ */ -typedef enum { /*!< GPIO_PADREGD_PAD14STRNG */ - GPIO_PADREGD_PAD14STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGD_PAD14STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGD_PAD14STRNG_Enum; - -/* =========================================== GPIO PADREGD PAD14INPEN [17..17] ============================================ */ -typedef enum { /*!< GPIO_PADREGD_PAD14INPEN */ - GPIO_PADREGD_PAD14INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGD_PAD14INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGD_PAD14INPEN_Enum; - -/* ============================================ GPIO PADREGD PAD14PULL [16..16] ============================================ */ -typedef enum { /*!< GPIO_PADREGD_PAD14PULL */ - GPIO_PADREGD_PAD14PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGD_PAD14PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGD_PAD14PULL_Enum; - -/* =========================================== GPIO PADREGD PAD13FNCSEL [11..13] =========================================== */ -typedef enum { /*!< GPIO_PADREGD_PAD13FNCSEL */ - GPIO_PADREGD_PAD13FNCSEL_ADCD0PSE8 = 0, /*!< ADCD0PSE8 : Configure as the ADC Differential pair 0 P, or Single - Ended input 8 analog input signal. Determination of the - D0P vs SE8 usage is done when the particular channel is - selected within the ADC module value. */ - GPIO_PADREGD_PAD13FNCSEL_NCE13 = 1, /*!< NCE13 : IOM/MSPI nCE group 13 value. */ - GPIO_PADREGD_PAD13FNCSEL_CT2 = 2, /*!< CT2 : CTIMER connection 2 value. */ - GPIO_PADREGD_PAD13FNCSEL_GPIO13 = 3, /*!< GPIO13 : Configure as GPIO13 value. */ - GPIO_PADREGD_PAD13FNCSEL_I2SBCLK = 4, /*!< I2SBCLK : I2C interface bit clock value. */ - GPIO_PADREGD_PAD13FNCSEL_EXTHFB = 5, /*!< EXTHFB : Configure as the external HFRC oscillator input value. */ - GPIO_PADREGD_PAD13FNCSEL_UA0RTS = 6, /*!< UA0RTS : Configure as the UART0 RTS signal output value. */ - GPIO_PADREGD_PAD13FNCSEL_UART1RX = 7, /*!< UART1RX : Configure as the UART1 RX input signal value. */ -} GPIO_PADREGD_PAD13FNCSEL_Enum; - -/* =========================================== GPIO PADREGD PAD13STRNG [10..10] ============================================ */ -typedef enum { /*!< GPIO_PADREGD_PAD13STRNG */ - GPIO_PADREGD_PAD13STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGD_PAD13STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGD_PAD13STRNG_Enum; - -/* ============================================ GPIO PADREGD PAD13INPEN [9..9] ============================================= */ -typedef enum { /*!< GPIO_PADREGD_PAD13INPEN */ - GPIO_PADREGD_PAD13INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGD_PAD13INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGD_PAD13INPEN_Enum; - -/* ============================================= GPIO PADREGD PAD13PULL [8..8] ============================================= */ -typedef enum { /*!< GPIO_PADREGD_PAD13PULL */ - GPIO_PADREGD_PAD13PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGD_PAD13PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGD_PAD13PULL_Enum; - -/* ============================================ GPIO PADREGD PAD12FNCSEL [3..5] ============================================ */ -typedef enum { /*!< GPIO_PADREGD_PAD12FNCSEL */ - GPIO_PADREGD_PAD12FNCSEL_ADCD0NSE9 = 0, /*!< ADCD0NSE9 : Configure as the ADC Differential pair 0 N, or Single - Ended input 9 analog input signal. Determination of the - D0N vs SE9 usage is done when the particular channel is - selected within the ADC module value. */ - GPIO_PADREGD_PAD12FNCSEL_NCE12 = 1, /*!< NCE12 : IOM/MSPI nCE group 12 value. */ - GPIO_PADREGD_PAD12FNCSEL_CT0 = 2, /*!< CT0 : CTIMER connection 0 value. */ - GPIO_PADREGD_PAD12FNCSEL_GPIO12 = 3, /*!< GPIO12 : Configure as GPIO12 value. */ - GPIO_PADREGD_PAD12FNCSEL_SLnCE = 4, /*!< SLnCE : Configure as the IOSLAVE SPI nCE signal value. */ - GPIO_PADREGD_PAD12FNCSEL_PDMCLK = 5, /*!< PDMCLK : PDM serial clock output value. */ - GPIO_PADREGD_PAD12FNCSEL_UA0CTS = 6, /*!< UA0CTS : Configure as the UART0 CTS input signal value. */ - GPIO_PADREGD_PAD12FNCSEL_UART1TX = 7, /*!< UART1TX : Configure as the UART1 TX output signal value. */ -} GPIO_PADREGD_PAD12FNCSEL_Enum; - -/* ============================================ GPIO PADREGD PAD12STRNG [2..2] ============================================= */ -typedef enum { /*!< GPIO_PADREGD_PAD12STRNG */ - GPIO_PADREGD_PAD12STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGD_PAD12STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGD_PAD12STRNG_Enum; - -/* ============================================ GPIO PADREGD PAD12INPEN [1..1] ============================================= */ -typedef enum { /*!< GPIO_PADREGD_PAD12INPEN */ - GPIO_PADREGD_PAD12INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGD_PAD12INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGD_PAD12INPEN_Enum; - -/* ============================================= GPIO PADREGD PAD12PULL [0..0] ============================================= */ -typedef enum { /*!< GPIO_PADREGD_PAD12PULL */ - GPIO_PADREGD_PAD12PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGD_PAD12PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGD_PAD12PULL_Enum; - -/* ======================================================== PADREGE ======================================================== */ -/* =========================================== GPIO PADREGE PAD19FNCSEL [27..29] =========================================== */ -typedef enum { /*!< GPIO_PADREGE_PAD19FNCSEL */ - GPIO_PADREGE_PAD19FNCSEL_CMPRF0 = 0, /*!< CMPRF0 : Configure as the analog comparator reference 0 signal - value. */ - GPIO_PADREGE_PAD19FNCSEL_NCE19 = 1, /*!< NCE19 : IOM/MSPI nCE group 19 value. */ - GPIO_PADREGE_PAD19FNCSEL_CT6 = 2, /*!< CT6 : CTIMER conenction 6 value. */ - GPIO_PADREGE_PAD19FNCSEL_GPIO19 = 3, /*!< GPIO19 : Configure as GPIO19 value. */ - GPIO_PADREGE_PAD19FNCSEL_SCCLK = 4, /*!< SCCLK : SCARD serial clock value. */ - GPIO_PADREGE_PAD19FNCSEL_ANATEST1 = 5, /*!< ANATEST1 : Configure as the ANATEST1 I/O signal value. */ - GPIO_PADREGE_PAD19FNCSEL_UART1RX = 6, /*!< UART1RX : Configure as the UART1 RX input signal value. */ - GPIO_PADREGE_PAD19FNCSEL_I2SBCLK = 7, /*!< I2SBCLK : Configure as the PDM I2S bit clock input signal value. */ -} GPIO_PADREGE_PAD19FNCSEL_Enum; - -/* =========================================== GPIO PADREGE PAD19STRNG [26..26] ============================================ */ -typedef enum { /*!< GPIO_PADREGE_PAD19STRNG */ - GPIO_PADREGE_PAD19STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGE_PAD19STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGE_PAD19STRNG_Enum; - -/* =========================================== GPIO PADREGE PAD19INPEN [25..25] ============================================ */ -typedef enum { /*!< GPIO_PADREGE_PAD19INPEN */ - GPIO_PADREGE_PAD19INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGE_PAD19INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGE_PAD19INPEN_Enum; - -/* ============================================ GPIO PADREGE PAD19PULL [24..24] ============================================ */ -typedef enum { /*!< GPIO_PADREGE_PAD19PULL */ - GPIO_PADREGE_PAD19PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGE_PAD19PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGE_PAD19PULL_Enum; - -/* =========================================== GPIO PADREGE PAD18FNCSEL [19..21] =========================================== */ -typedef enum { /*!< GPIO_PADREGE_PAD18FNCSEL */ - GPIO_PADREGE_PAD18FNCSEL_CMPIN1 = 0, /*!< CMPIN1 : Configure as the analog comparator input 1 signal value. */ - GPIO_PADREGE_PAD18FNCSEL_NCE18 = 1, /*!< NCE18 : IOM/MSPI nCE group 18 value. */ - GPIO_PADREGE_PAD18FNCSEL_CT4 = 2, /*!< CT4 : CTIMER connection 4 value. */ - GPIO_PADREGE_PAD18FNCSEL_GPIO18 = 3, /*!< GPIO18 : Configure as GPIO18 value. */ - GPIO_PADREGE_PAD18FNCSEL_UA0RTS = 4, /*!< UA0RTS : Configure as UART0 RTS output signal value. */ - GPIO_PADREGE_PAD18FNCSEL_ANATEST2 = 5, /*!< ANATEST2 : Configure as ANATEST2 I/O signal value. */ - GPIO_PADREGE_PAD18FNCSEL_UART1TX = 6, /*!< UART1TX : Configure as UART1 TX output signal value. */ - GPIO_PADREGE_PAD18FNCSEL_SCCIO = 7, /*!< SCCIO : SCARD data input/output connectin value. */ -} GPIO_PADREGE_PAD18FNCSEL_Enum; - -/* =========================================== GPIO PADREGE PAD18STRNG [18..18] ============================================ */ -typedef enum { /*!< GPIO_PADREGE_PAD18STRNG */ - GPIO_PADREGE_PAD18STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGE_PAD18STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGE_PAD18STRNG_Enum; - -/* =========================================== GPIO PADREGE PAD18INPEN [17..17] ============================================ */ -typedef enum { /*!< GPIO_PADREGE_PAD18INPEN */ - GPIO_PADREGE_PAD18INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGE_PAD18INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGE_PAD18INPEN_Enum; - -/* ============================================ GPIO PADREGE PAD18PULL [16..16] ============================================ */ -typedef enum { /*!< GPIO_PADREGE_PAD18PULL */ - GPIO_PADREGE_PAD18PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGE_PAD18PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGE_PAD18PULL_Enum; - -/* =========================================== GPIO PADREGE PAD17FNCSEL [11..13] =========================================== */ -typedef enum { /*!< GPIO_PADREGE_PAD17FNCSEL */ - GPIO_PADREGE_PAD17FNCSEL_CMPRF1 = 0, /*!< CMPRF1 : Configure as the analog comparator reference signal - 1 input signal value. */ - GPIO_PADREGE_PAD17FNCSEL_NCE17 = 1, /*!< NCE17 : IOM/MSPI nCE group 17 value. */ - GPIO_PADREGE_PAD17FNCSEL_TRIG1 = 2, /*!< TRIG1 : Configure as the ADC Trigger 1 signal value. */ - GPIO_PADREGE_PAD17FNCSEL_GPIO17 = 3, /*!< GPIO17 : Configure as GPIO17 value. */ - GPIO_PADREGE_PAD17FNCSEL_SCCCLK = 4, /*!< SCCCLK : SCARD serial clock output value. */ - GPIO_PADREGE_PAD17FNCSEL_UART0RX = 6, /*!< UART0RX : Configure as UART0 RX input signal value. */ - GPIO_PADREGE_PAD17FNCSEL_UA1CTS = 7, /*!< UA1CTS : Configure as UART1 CTS input signal value. */ -} GPIO_PADREGE_PAD17FNCSEL_Enum; - -/* =========================================== GPIO PADREGE PAD17STRNG [10..10] ============================================ */ -typedef enum { /*!< GPIO_PADREGE_PAD17STRNG */ - GPIO_PADREGE_PAD17STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGE_PAD17STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGE_PAD17STRNG_Enum; - -/* ============================================ GPIO PADREGE PAD17INPEN [9..9] ============================================= */ -typedef enum { /*!< GPIO_PADREGE_PAD17INPEN */ - GPIO_PADREGE_PAD17INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGE_PAD17INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGE_PAD17INPEN_Enum; - -/* ============================================= GPIO PADREGE PAD17PULL [8..8] ============================================= */ -typedef enum { /*!< GPIO_PADREGE_PAD17PULL */ - GPIO_PADREGE_PAD17PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGE_PAD17PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGE_PAD17PULL_Enum; - -/* ============================================ GPIO PADREGE PAD16FNCSEL [3..5] ============================================ */ -typedef enum { /*!< GPIO_PADREGE_PAD16FNCSEL */ - GPIO_PADREGE_PAD16FNCSEL_ADCSE0 = 0, /*!< ADCSE0 : Configure as the analog ADC single ended port 0 input - signal value. */ - GPIO_PADREGE_PAD16FNCSEL_NCE16 = 1, /*!< NCE16 : IOM/MSPI nCE group 16 value. */ - GPIO_PADREGE_PAD16FNCSEL_TRIG0 = 2, /*!< TRIG0 : Configure as the ADC Trigger 0 signal value. */ - GPIO_PADREGE_PAD16FNCSEL_GPIO16 = 3, /*!< GPIO16 : Configure as GPIO16 value. */ - GPIO_PADREGE_PAD16FNCSEL_SCCRST = 4, /*!< SCCRST : SCARD reset output value. */ - GPIO_PADREGE_PAD16FNCSEL_CMPIN0 = 5, /*!< CMPIN0 : Configure as comparator input 0 signal value. */ - GPIO_PADREGE_PAD16FNCSEL_UART0TX = 6, /*!< UART0TX : Configure as UART0 TX output signal value. */ - GPIO_PADREGE_PAD16FNCSEL_UA1RTS = 7, /*!< UA1RTS : Configure as UART1 RTS output signal value. */ -} GPIO_PADREGE_PAD16FNCSEL_Enum; - -/* ============================================ GPIO PADREGE PAD16STRNG [2..2] ============================================= */ -typedef enum { /*!< GPIO_PADREGE_PAD16STRNG */ - GPIO_PADREGE_PAD16STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGE_PAD16STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGE_PAD16STRNG_Enum; - -/* ============================================ GPIO PADREGE PAD16INPEN [1..1] ============================================= */ -typedef enum { /*!< GPIO_PADREGE_PAD16INPEN */ - GPIO_PADREGE_PAD16INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGE_PAD16INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGE_PAD16INPEN_Enum; - -/* ============================================= GPIO PADREGE PAD16PULL [0..0] ============================================= */ -typedef enum { /*!< GPIO_PADREGE_PAD16PULL */ - GPIO_PADREGE_PAD16PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGE_PAD16PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGE_PAD16PULL_Enum; - -/* ======================================================== PADREGF ======================================================== */ -/* =========================================== GPIO PADREGF PAD23FNCSEL [27..29] =========================================== */ -typedef enum { /*!< GPIO_PADREGF_PAD23FNCSEL */ - GPIO_PADREGF_PAD23FNCSEL_UART0RX = 0, /*!< UART0RX : Configure as the UART0 RX signal value. */ - GPIO_PADREGF_PAD23FNCSEL_NCE23 = 1, /*!< NCE23 : IOM/MSPI nCE group 23 value. */ - GPIO_PADREGF_PAD23FNCSEL_CT14 = 2, /*!< CT14 : CTIMER connection 14 value. */ - GPIO_PADREGF_PAD23FNCSEL_GPIO23 = 3, /*!< GPIO23 : Configure as GPIO23 value. */ - GPIO_PADREGF_PAD23FNCSEL_I2SWCLK = 4, /*!< I2SWCLK : I2S word clock input value. */ - GPIO_PADREGF_PAD23FNCSEL_CMPOUT = 5, /*!< CMPOUT : Configure as voltage comparitor output value. */ - GPIO_PADREGF_PAD23FNCSEL_MSPI3 = 6, /*!< MSPI3 : MSPI data connection 3 value. */ - GPIO_PADREGF_PAD23FNCSEL_EXTXT = 7, /*!< EXTXT : External XTAL osacillatgor input value. */ -} GPIO_PADREGF_PAD23FNCSEL_Enum; - -/* =========================================== GPIO PADREGF PAD23STRNG [26..26] ============================================ */ -typedef enum { /*!< GPIO_PADREGF_PAD23STRNG */ - GPIO_PADREGF_PAD23STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGF_PAD23STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGF_PAD23STRNG_Enum; - -/* =========================================== GPIO PADREGF PAD23INPEN [25..25] ============================================ */ -typedef enum { /*!< GPIO_PADREGF_PAD23INPEN */ - GPIO_PADREGF_PAD23INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGF_PAD23INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGF_PAD23INPEN_Enum; - -/* ============================================ GPIO PADREGF PAD23PULL [24..24] ============================================ */ -typedef enum { /*!< GPIO_PADREGF_PAD23PULL */ - GPIO_PADREGF_PAD23PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGF_PAD23PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGF_PAD23PULL_Enum; - -/* =========================================== GPIO PADREGF PAD22FNCSEL [19..21] =========================================== */ -typedef enum { /*!< GPIO_PADREGF_PAD22FNCSEL */ - GPIO_PADREGF_PAD22FNCSEL_UART0TX = 0, /*!< UART0TX : Configure as the UART0 TX signal value. */ - GPIO_PADREGF_PAD22FNCSEL_NCE22 = 1, /*!< NCE22 : IOM/MSPI nCE group 22 value. */ - GPIO_PADREGF_PAD22FNCSEL_CT12 = 2, /*!< CT12 : CTIMER connection 12 value. */ - GPIO_PADREGF_PAD22FNCSEL_GPIO22 = 3, /*!< GPIO22 : Configure as GPIO22 value. */ - GPIO_PADREGF_PAD22FNCSEL_PDM_CLK = 4, /*!< PDM_CLK : Configure as the PDM CLK output value. */ - GPIO_PADREGF_PAD22FNCSEL_EXTLF = 5, /*!< EXTLF : External LFRC input value. */ - GPIO_PADREGF_PAD22FNCSEL_MSPI0 = 6, /*!< MSPI0 : MSPI data connection 0 value. */ - GPIO_PADREGF_PAD22FNCSEL_SWO = 7, /*!< SWO : Configure as the serial trace data output signal value. */ -} GPIO_PADREGF_PAD22FNCSEL_Enum; - -/* =========================================== GPIO PADREGF PAD22STRNG [18..18] ============================================ */ -typedef enum { /*!< GPIO_PADREGF_PAD22STRNG */ - GPIO_PADREGF_PAD22STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGF_PAD22STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGF_PAD22STRNG_Enum; - -/* =========================================== GPIO PADREGF PAD22INPEN [17..17] ============================================ */ -typedef enum { /*!< GPIO_PADREGF_PAD22INPEN */ - GPIO_PADREGF_PAD22INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGF_PAD22INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGF_PAD22INPEN_Enum; - -/* ============================================ GPIO PADREGF PAD22PULL [16..16] ============================================ */ -typedef enum { /*!< GPIO_PADREGF_PAD22PULL */ - GPIO_PADREGF_PAD22PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGF_PAD22PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGF_PAD22PULL_Enum; - -/* =========================================== GPIO PADREGF PAD21FNCSEL [11..13] =========================================== */ -typedef enum { /*!< GPIO_PADREGF_PAD21FNCSEL */ - GPIO_PADREGF_PAD21FNCSEL_SWDIO = 0, /*!< SWDIO : Configure as the serial wire debug data signal value. */ - GPIO_PADREGF_PAD21FNCSEL_NCE21 = 1, /*!< NCE21 : IOM/MSPI nCE group 21 value. */ - GPIO_PADREGF_PAD21FNCSEL_GPIO21 = 3, /*!< GPIO21 : Configure as GPIO21 value. */ - GPIO_PADREGF_PAD21FNCSEL_UART0RX = 4, /*!< UART0RX : Configure as UART0 RX input signal value. */ - GPIO_PADREGF_PAD21FNCSEL_UART1RX = 5, /*!< UART1RX : Configure as UART1 RX input signal value. */ - GPIO_PADREGF_PAD21FNCSEL_I2SBCLK = 6, /*!< I2SBCLK : I2S byte clock input value. */ - GPIO_PADREGF_PAD21FNCSEL_UA1CTS = 7, /*!< UA1CTS : Configure as UART1 CTS input signal value. */ -} GPIO_PADREGF_PAD21FNCSEL_Enum; - -/* =========================================== GPIO PADREGF PAD21STRNG [10..10] ============================================ */ -typedef enum { /*!< GPIO_PADREGF_PAD21STRNG */ - GPIO_PADREGF_PAD21STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGF_PAD21STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGF_PAD21STRNG_Enum; - -/* ============================================ GPIO PADREGF PAD21INPEN [9..9] ============================================= */ -typedef enum { /*!< GPIO_PADREGF_PAD21INPEN */ - GPIO_PADREGF_PAD21INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGF_PAD21INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGF_PAD21INPEN_Enum; - -/* ============================================= GPIO PADREGF PAD21PULL [8..8] ============================================= */ -typedef enum { /*!< GPIO_PADREGF_PAD21PULL */ - GPIO_PADREGF_PAD21PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGF_PAD21PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGF_PAD21PULL_Enum; - -/* ============================================ GPIO PADREGF PAD20FNCSEL [3..5] ============================================ */ -typedef enum { /*!< GPIO_PADREGF_PAD20FNCSEL */ - GPIO_PADREGF_PAD20FNCSEL_SWDCK = 0, /*!< SWDCK : Configure as the serial wire debug clock signal value. */ - GPIO_PADREGF_PAD20FNCSEL_NCE20 = 1, /*!< NCE20 : IOM/MSPI nCE group 20 value. */ - GPIO_PADREGF_PAD20FNCSEL_GPIO20 = 3, /*!< GPIO20 : Configure as GPIO20 value. */ - GPIO_PADREGF_PAD20FNCSEL_UART0TX = 4, /*!< UART0TX : Configure as UART0 TX output signal value. */ - GPIO_PADREGF_PAD20FNCSEL_UART1TX = 5, /*!< UART1TX : Configure as UART1 TX output signal value. */ - GPIO_PADREGF_PAD20FNCSEL_I2SBCLK = 6, /*!< I2SBCLK : I2S byte clock input value. */ - GPIO_PADREGF_PAD20FNCSEL_UA1RTS = 7, /*!< UA1RTS : Configure as UART1 RTS output signal value. */ -} GPIO_PADREGF_PAD20FNCSEL_Enum; - -/* ============================================ GPIO PADREGF PAD20STRNG [2..2] ============================================= */ -typedef enum { /*!< GPIO_PADREGF_PAD20STRNG */ - GPIO_PADREGF_PAD20STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGF_PAD20STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGF_PAD20STRNG_Enum; - -/* ============================================ GPIO PADREGF PAD20INPEN [1..1] ============================================= */ -typedef enum { /*!< GPIO_PADREGF_PAD20INPEN */ - GPIO_PADREGF_PAD20INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGF_PAD20INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGF_PAD20INPEN_Enum; - -/* ============================================= GPIO PADREGF PAD20PULL [0..0] ============================================= */ -typedef enum { /*!< GPIO_PADREGF_PAD20PULL */ - GPIO_PADREGF_PAD20PULL_DIS = 0, /*!< DIS : Pulldown disabled value. */ - GPIO_PADREGF_PAD20PULL_EN = 1, /*!< EN : Pulldown enabled value. */ -} GPIO_PADREGF_PAD20PULL_Enum; - -/* ======================================================== PADREGG ======================================================== */ -/* ============================================ GPIO PADREGG PAD27RSEL [30..31] ============================================ */ -typedef enum { /*!< GPIO_PADREGG_PAD27RSEL */ - GPIO_PADREGG_PAD27RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ - GPIO_PADREGG_PAD27RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ - GPIO_PADREGG_PAD27RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ - GPIO_PADREGG_PAD27RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ -} GPIO_PADREGG_PAD27RSEL_Enum; - -/* =========================================== GPIO PADREGG PAD27FNCSEL [27..29] =========================================== */ -typedef enum { /*!< GPIO_PADREGG_PAD27FNCSEL */ - GPIO_PADREGG_PAD27FNCSEL_UART0RX = 0, /*!< UART0RX : Configure as UART0 RX input signal value. */ - GPIO_PADREGG_PAD27FNCSEL_NCE27 = 1, /*!< NCE27 : IOM/MSPI nCE group 27 value. */ - GPIO_PADREGG_PAD27FNCSEL_CT5 = 2, /*!< CT5 : CTIMER connection 5 value. */ - GPIO_PADREGG_PAD27FNCSEL_GPIO27 = 3, /*!< GPIO27 : Configure as GPIO27 value. */ - GPIO_PADREGG_PAD27FNCSEL_M2SCL = 4, /*!< M2SCL : Configure as I2C clock I/O signal from IOMSTR2 value. */ - GPIO_PADREGG_PAD27FNCSEL_M2SCK = 5, /*!< M2SCK : Configure as SPI clock output signal from IOMSTR2 value. */ -} GPIO_PADREGG_PAD27FNCSEL_Enum; - -/* =========================================== GPIO PADREGG PAD27STRNG [26..26] ============================================ */ -typedef enum { /*!< GPIO_PADREGG_PAD27STRNG */ - GPIO_PADREGG_PAD27STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGG_PAD27STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGG_PAD27STRNG_Enum; - -/* =========================================== GPIO PADREGG PAD27INPEN [25..25] ============================================ */ -typedef enum { /*!< GPIO_PADREGG_PAD27INPEN */ - GPIO_PADREGG_PAD27INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGG_PAD27INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGG_PAD27INPEN_Enum; - -/* ============================================ GPIO PADREGG PAD27PULL [24..24] ============================================ */ -typedef enum { /*!< GPIO_PADREGG_PAD27PULL */ - GPIO_PADREGG_PAD27PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGG_PAD27PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGG_PAD27PULL_Enum; - -/* =========================================== GPIO PADREGG PAD26FNCSEL [19..21] =========================================== */ -typedef enum { /*!< GPIO_PADREGG_PAD26FNCSEL */ - GPIO_PADREGG_PAD26FNCSEL_EXTHF = 0, /*!< EXTHF : Configure as the external HFRC oscillator input value. */ - GPIO_PADREGG_PAD26FNCSEL_NCE26 = 1, /*!< NCE26 : IOM/MSPI nCE group 26 value. */ - GPIO_PADREGG_PAD26FNCSEL_CT3 = 2, /*!< CT3 : CTIMER connection 3 value. */ - GPIO_PADREGG_PAD26FNCSEL_GPIO26 = 3, /*!< GPIO26 : Configure as GPIO26 value. */ - GPIO_PADREGG_PAD26FNCSEL_SCCRST = 4, /*!< SCCRST : SCARD reset output value. */ - GPIO_PADREGG_PAD26FNCSEL_MSPI1 = 5, /*!< MSPI1 : MSPI data connection 1 value. */ - GPIO_PADREGG_PAD26FNCSEL_UART0TX = 6, /*!< UART0TX : Configure as UART0 TX output signal value. */ - GPIO_PADREGG_PAD26FNCSEL_UA1CTS = 7, /*!< UA1CTS : Configure as UART1 CTS input signal value. */ -} GPIO_PADREGG_PAD26FNCSEL_Enum; - -/* =========================================== GPIO PADREGG PAD26STRNG [18..18] ============================================ */ -typedef enum { /*!< GPIO_PADREGG_PAD26STRNG */ - GPIO_PADREGG_PAD26STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGG_PAD26STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGG_PAD26STRNG_Enum; - -/* =========================================== GPIO PADREGG PAD26INPEN [17..17] ============================================ */ -typedef enum { /*!< GPIO_PADREGG_PAD26INPEN */ - GPIO_PADREGG_PAD26INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGG_PAD26INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGG_PAD26INPEN_Enum; - -/* ============================================ GPIO PADREGG PAD26PULL [16..16] ============================================ */ -typedef enum { /*!< GPIO_PADREGG_PAD26PULL */ - GPIO_PADREGG_PAD26PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGG_PAD26PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGG_PAD26PULL_Enum; - -/* ============================================ GPIO PADREGG PAD25RSEL [14..15] ============================================ */ -typedef enum { /*!< GPIO_PADREGG_PAD25RSEL */ - GPIO_PADREGG_PAD25RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ - GPIO_PADREGG_PAD25RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ - GPIO_PADREGG_PAD25RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ - GPIO_PADREGG_PAD25RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ -} GPIO_PADREGG_PAD25RSEL_Enum; - -/* =========================================== GPIO PADREGG PAD25FNCSEL [11..13] =========================================== */ -typedef enum { /*!< GPIO_PADREGG_PAD25FNCSEL */ - GPIO_PADREGG_PAD25FNCSEL_UART1RX = 0, /*!< UART1RX : Configure as UART1 RX input signal value. */ - GPIO_PADREGG_PAD25FNCSEL_NCE25 = 1, /*!< NCE25 : IOM/MSPI nCE group 25 value. */ - GPIO_PADREGG_PAD25FNCSEL_CT1 = 2, /*!< CT1 : CTIMER connection 1 value. */ - GPIO_PADREGG_PAD25FNCSEL_GPIO25 = 3, /*!< GPIO25 : Configure as GPIO25 value. */ - GPIO_PADREGG_PAD25FNCSEL_M2SDAWIR3 = 4, /*!< M2SDAWIR3 : Configure as the IOMSTR2 I2C SDA or SPI WIR3 signal - value. */ - GPIO_PADREGG_PAD25FNCSEL_M2MISO = 5, /*!< M2MISO : Configure as the IOMSTR2 SPI MISO input signal value. */ -} GPIO_PADREGG_PAD25FNCSEL_Enum; - -/* =========================================== GPIO PADREGG PAD25STRNG [10..10] ============================================ */ -typedef enum { /*!< GPIO_PADREGG_PAD25STRNG */ - GPIO_PADREGG_PAD25STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGG_PAD25STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGG_PAD25STRNG_Enum; - -/* ============================================ GPIO PADREGG PAD25INPEN [9..9] ============================================= */ -typedef enum { /*!< GPIO_PADREGG_PAD25INPEN */ - GPIO_PADREGG_PAD25INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGG_PAD25INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGG_PAD25INPEN_Enum; - -/* ============================================= GPIO PADREGG PAD25PULL [8..8] ============================================= */ -typedef enum { /*!< GPIO_PADREGG_PAD25PULL */ - GPIO_PADREGG_PAD25PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGG_PAD25PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGG_PAD25PULL_Enum; - -/* ============================================ GPIO PADREGG PAD24FNCSEL [3..5] ============================================ */ -typedef enum { /*!< GPIO_PADREGG_PAD24FNCSEL */ - GPIO_PADREGG_PAD24FNCSEL_UART1TX = 0, /*!< UART1TX : Configure as UART1 TX output signal value. */ - GPIO_PADREGG_PAD24FNCSEL_NCE24 = 1, /*!< NCE24 : IOM/MSPI nCE group 24 value. */ - GPIO_PADREGG_PAD24FNCSEL_MSPI8 = 2, /*!< MSPI8 : MSPI data connection 8 value. */ - GPIO_PADREGG_PAD24FNCSEL_GPIO24 = 3, /*!< GPIO24 : Configure as GPIO24 value. */ - GPIO_PADREGG_PAD24FNCSEL_UA0CTS = 4, /*!< UA0CTS : Configure as UART0 CTS input signal value. */ - GPIO_PADREGG_PAD24FNCSEL_CT21 = 5, /*!< CT21 : CTIMER connection 21 value. */ - GPIO_PADREGG_PAD24FNCSEL_32kHzXT = 6, /*!< 32kHzXT : Configure as the 32kHz crystal output signal value. */ - GPIO_PADREGG_PAD24FNCSEL_SWO = 7, /*!< SWO : Configure as the serial trace data output signal value. */ -} GPIO_PADREGG_PAD24FNCSEL_Enum; - -/* ============================================ GPIO PADREGG PAD24STRNG [2..2] ============================================= */ -typedef enum { /*!< GPIO_PADREGG_PAD24STRNG */ - GPIO_PADREGG_PAD24STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGG_PAD24STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGG_PAD24STRNG_Enum; - -/* ============================================ GPIO PADREGG PAD24INPEN [1..1] ============================================= */ -typedef enum { /*!< GPIO_PADREGG_PAD24INPEN */ - GPIO_PADREGG_PAD24INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGG_PAD24INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGG_PAD24INPEN_Enum; - -/* ============================================= GPIO PADREGG PAD24PULL [0..0] ============================================= */ -typedef enum { /*!< GPIO_PADREGG_PAD24PULL */ - GPIO_PADREGG_PAD24PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGG_PAD24PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGG_PAD24PULL_Enum; - -/* ======================================================== PADREGH ======================================================== */ -/* =========================================== GPIO PADREGH PAD31FNCSEL [27..29] =========================================== */ -typedef enum { /*!< GPIO_PADREGH_PAD31FNCSEL */ - GPIO_PADREGH_PAD31FNCSEL_ADCSE3 = 0, /*!< ADCSE3 : Configure as the analog input for ADC single ended - input 3 value. */ - GPIO_PADREGH_PAD31FNCSEL_NCE31 = 1, /*!< NCE31 : IOM/MSPI nCE group 31 value. */ - GPIO_PADREGH_PAD31FNCSEL_CT13 = 2, /*!< CT13 : CTIMER connection 13 value. */ - GPIO_PADREGH_PAD31FNCSEL_GPIO31 = 3, /*!< GPIO31 : Configure as GPIO31 value. */ - GPIO_PADREGH_PAD31FNCSEL_UART0RX = 4, /*!< UART0RX : Configure as the UART0 RX input signal value. */ - GPIO_PADREGH_PAD31FNCSEL_SCCCLK = 5, /*!< SCCCLK : SCARD serial clock output value. */ - GPIO_PADREGH_PAD31FNCSEL_UA1RTS = 7, /*!< UA1RTS : Configure as UART1 RTS output signal value. */ -} GPIO_PADREGH_PAD31FNCSEL_Enum; - -/* =========================================== GPIO PADREGH PAD31STRNG [26..26] ============================================ */ -typedef enum { /*!< GPIO_PADREGH_PAD31STRNG */ - GPIO_PADREGH_PAD31STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGH_PAD31STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGH_PAD31STRNG_Enum; - -/* =========================================== GPIO PADREGH PAD31INPEN [25..25] ============================================ */ -typedef enum { /*!< GPIO_PADREGH_PAD31INPEN */ - GPIO_PADREGH_PAD31INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGH_PAD31INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGH_PAD31INPEN_Enum; - -/* ============================================ GPIO PADREGH PAD31PULL [24..24] ============================================ */ -typedef enum { /*!< GPIO_PADREGH_PAD31PULL */ - GPIO_PADREGH_PAD31PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGH_PAD31PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGH_PAD31PULL_Enum; - -/* =========================================== GPIO PADREGH PAD30FNCSEL [19..21] =========================================== */ -typedef enum { /*!< GPIO_PADREGH_PAD30FNCSEL */ - GPIO_PADREGH_PAD30FNCSEL_ANATEST1 = 0, /*!< ANATEST1 : Configure as the ANATEST1 I/O signal value. */ - GPIO_PADREGH_PAD30FNCSEL_NCE30 = 1, /*!< NCE30 : IOM/MSPI nCE group 30 value. */ - GPIO_PADREGH_PAD30FNCSEL_CT11 = 2, /*!< CT11 : CTIMER connection 11 value. */ - GPIO_PADREGH_PAD30FNCSEL_GPIO30 = 3, /*!< GPIO30 : Configure as GPIO30 value. */ - GPIO_PADREGH_PAD30FNCSEL_UART0TX = 4, /*!< UART0TX : Configure as UART0 TX output signal value. */ - GPIO_PADREGH_PAD30FNCSEL_UA1RTS = 5, /*!< UA1RTS : Configure as UART1 RTS output signal value. */ - GPIO_PADREGH_PAD30FNCSEL_I2S_DAT = 7, /*!< I2S_DAT : Configure as the PDM I2S Data output signal value. */ -} GPIO_PADREGH_PAD30FNCSEL_Enum; - -/* =========================================== GPIO PADREGH PAD30STRNG [18..18] ============================================ */ -typedef enum { /*!< GPIO_PADREGH_PAD30STRNG */ - GPIO_PADREGH_PAD30STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGH_PAD30STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGH_PAD30STRNG_Enum; - -/* =========================================== GPIO PADREGH PAD30INPEN [17..17] ============================================ */ -typedef enum { /*!< GPIO_PADREGH_PAD30INPEN */ - GPIO_PADREGH_PAD30INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGH_PAD30INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGH_PAD30INPEN_Enum; - -/* ============================================ GPIO PADREGH PAD30PULL [16..16] ============================================ */ -typedef enum { /*!< GPIO_PADREGH_PAD30PULL */ - GPIO_PADREGH_PAD30PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGH_PAD30PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGH_PAD30PULL_Enum; - -/* =========================================== GPIO PADREGH PAD29FNCSEL [11..13] =========================================== */ -typedef enum { /*!< GPIO_PADREGH_PAD29FNCSEL */ - GPIO_PADREGH_PAD29FNCSEL_ADCSE1 = 0, /*!< ADCSE1 : Configure as the analog input for ADC single ended - input 1 value. */ - GPIO_PADREGH_PAD29FNCSEL_NCE29 = 1, /*!< NCE29 : IOM/MSPI nCE group 29 value. */ - GPIO_PADREGH_PAD29FNCSEL_CT9 = 2, /*!< CT9 : CTIMER connection 9 value. */ - GPIO_PADREGH_PAD29FNCSEL_GPIO29 = 3, /*!< GPIO29 : Configure as GPIO29 value. */ - GPIO_PADREGH_PAD29FNCSEL_UA0CTS = 4, /*!< UA0CTS : Configure as the UART0 CTS input signal value. */ - GPIO_PADREGH_PAD29FNCSEL_UA1CTS = 5, /*!< UA1CTS : Configure as the UART1 CTS input signal value. */ - GPIO_PADREGH_PAD29FNCSEL_UART0RX = 6, /*!< UART0RX : Configure as the UART0 RX input signal value. */ - GPIO_PADREGH_PAD29FNCSEL_PDM_DATA = 7, /*!< PDM_DATA : Configure as PDM DATA input value. */ -} GPIO_PADREGH_PAD29FNCSEL_Enum; - -/* =========================================== GPIO PADREGH PAD29STRNG [10..10] ============================================ */ -typedef enum { /*!< GPIO_PADREGH_PAD29STRNG */ - GPIO_PADREGH_PAD29STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGH_PAD29STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGH_PAD29STRNG_Enum; - -/* ============================================ GPIO PADREGH PAD29INPEN [9..9] ============================================= */ -typedef enum { /*!< GPIO_PADREGH_PAD29INPEN */ - GPIO_PADREGH_PAD29INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGH_PAD29INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGH_PAD29INPEN_Enum; - -/* ============================================= GPIO PADREGH PAD29PULL [8..8] ============================================= */ -typedef enum { /*!< GPIO_PADREGH_PAD29PULL */ - GPIO_PADREGH_PAD29PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGH_PAD29PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGH_PAD29PULL_Enum; - -/* ============================================ GPIO PADREGH PAD28FNCSEL [3..5] ============================================ */ -typedef enum { /*!< GPIO_PADREGH_PAD28FNCSEL */ - GPIO_PADREGH_PAD28FNCSEL_I2S_WCLK = 0, /*!< I2S_WCLK : Configure as the PDM I2S Word Clock input value. */ - GPIO_PADREGH_PAD28FNCSEL_NCE28 = 1, /*!< NCE28 : IOM/MSPI nCE group 28 value. */ - GPIO_PADREGH_PAD28FNCSEL_CT7 = 2, /*!< CT7 : CTIMER connection 7 value. */ - GPIO_PADREGH_PAD28FNCSEL_GPIO28 = 3, /*!< GPIO28 : Configure as GPIO28 value. */ - GPIO_PADREGH_PAD28FNCSEL_M2MOSI = 5, /*!< M2MOSI : Configure as the IOMSTR2 SPI MOSI output signal value. */ - GPIO_PADREGH_PAD28FNCSEL_UART0TX = 6, /*!< UART0TX : Configure as the UART0 TX output signal value. */ -} GPIO_PADREGH_PAD28FNCSEL_Enum; - -/* ============================================ GPIO PADREGH PAD28STRNG [2..2] ============================================= */ -typedef enum { /*!< GPIO_PADREGH_PAD28STRNG */ - GPIO_PADREGH_PAD28STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGH_PAD28STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGH_PAD28STRNG_Enum; - -/* ============================================ GPIO PADREGH PAD28INPEN [1..1] ============================================= */ -typedef enum { /*!< GPIO_PADREGH_PAD28INPEN */ - GPIO_PADREGH_PAD28INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGH_PAD28INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGH_PAD28INPEN_Enum; - -/* ============================================= GPIO PADREGH PAD28PULL [0..0] ============================================= */ -typedef enum { /*!< GPIO_PADREGH_PAD28PULL */ - GPIO_PADREGH_PAD28PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGH_PAD28PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGH_PAD28PULL_Enum; - -/* ======================================================== PADREGI ======================================================== */ -/* =========================================== GPIO PADREGI PAD35FNCSEL [27..29] =========================================== */ -typedef enum { /*!< GPIO_PADREGI_PAD35FNCSEL */ - GPIO_PADREGI_PAD35FNCSEL_ADCSE7 = 0, /*!< ADCSE7 : Configure as the analog input for ADC single ended - input 7 value. */ - GPIO_PADREGI_PAD35FNCSEL_NCE35 = 1, /*!< NCE35 : IOM/MSPI nCE group 35 value. */ - GPIO_PADREGI_PAD35FNCSEL_UART1TX = 2, /*!< UART1TX : Configure as the UART1 TX signal value. */ - GPIO_PADREGI_PAD35FNCSEL_GPIO35 = 3, /*!< GPIO35 : Configure as GPIO35 value. */ - GPIO_PADREGI_PAD35FNCSEL_I2SDAT = 4, /*!< I2SDAT : I2S serial data output value. */ - GPIO_PADREGI_PAD35FNCSEL_CT27 = 5, /*!< CT27 : CTIMER connection 27 value. */ - GPIO_PADREGI_PAD35FNCSEL_UA0RTS = 6, /*!< UA0RTS : Configure as the UART0 RTS output value. */ -} GPIO_PADREGI_PAD35FNCSEL_Enum; - -/* =========================================== GPIO PADREGI PAD35STRNG [26..26] ============================================ */ -typedef enum { /*!< GPIO_PADREGI_PAD35STRNG */ - GPIO_PADREGI_PAD35STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGI_PAD35STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGI_PAD35STRNG_Enum; - -/* =========================================== GPIO PADREGI PAD35INPEN [25..25] ============================================ */ -typedef enum { /*!< GPIO_PADREGI_PAD35INPEN */ - GPIO_PADREGI_PAD35INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGI_PAD35INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGI_PAD35INPEN_Enum; - -/* ============================================ GPIO PADREGI PAD35PULL [24..24] ============================================ */ -typedef enum { /*!< GPIO_PADREGI_PAD35PULL */ - GPIO_PADREGI_PAD35PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGI_PAD35PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGI_PAD35PULL_Enum; - -/* =========================================== GPIO PADREGI PAD34FNCSEL [19..21] =========================================== */ -typedef enum { /*!< GPIO_PADREGI_PAD34FNCSEL */ - GPIO_PADREGI_PAD34FNCSEL_ADCSE6 = 0, /*!< ADCSE6 : Configure as the analog input for ADC single ended - input 6 value. */ - GPIO_PADREGI_PAD34FNCSEL_NCE34 = 1, /*!< NCE34 : IOM/MSPI nCE group 34 value. */ - GPIO_PADREGI_PAD34FNCSEL_UA1RTS = 2, /*!< UA1RTS : Configure as the UART1 RTS output value. */ - GPIO_PADREGI_PAD34FNCSEL_GPIO34 = 3, /*!< GPIO34 : Configure as GPIO34 value. */ - GPIO_PADREGI_PAD34FNCSEL_CMPRF2 = 4, /*!< CMPRF2 : Configure as the analog comparator reference 2 signal - value. */ - GPIO_PADREGI_PAD34FNCSEL_UA0RTS = 5, /*!< UA0RTS : Configure as the UART0 RTS output value. */ - GPIO_PADREGI_PAD34FNCSEL_UART0RX = 6, /*!< UART0RX : Configure as the UART0 RX input value. */ - GPIO_PADREGI_PAD34FNCSEL_PDMDATA = 7, /*!< PDMDATA : PDM serial data input value. */ -} GPIO_PADREGI_PAD34FNCSEL_Enum; - -/* =========================================== GPIO PADREGI PAD34STRNG [18..18] ============================================ */ -typedef enum { /*!< GPIO_PADREGI_PAD34STRNG */ - GPIO_PADREGI_PAD34STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGI_PAD34STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGI_PAD34STRNG_Enum; - -/* =========================================== GPIO PADREGI PAD34INPEN [17..17] ============================================ */ -typedef enum { /*!< GPIO_PADREGI_PAD34INPEN */ - GPIO_PADREGI_PAD34INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGI_PAD34INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGI_PAD34INPEN_Enum; - -/* ============================================ GPIO PADREGI PAD34PULL [16..16] ============================================ */ -typedef enum { /*!< GPIO_PADREGI_PAD34PULL */ - GPIO_PADREGI_PAD34PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGI_PAD34PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGI_PAD34PULL_Enum; - -/* =========================================== GPIO PADREGI PAD33FNCSEL [11..13] =========================================== */ -typedef enum { /*!< GPIO_PADREGI_PAD33FNCSEL */ - GPIO_PADREGI_PAD33FNCSEL_ADCSE5 = 0, /*!< ADCSE5 : Configure as the analog ADC single ended port 5 input - signal value. */ - GPIO_PADREGI_PAD33FNCSEL_NCE33 = 1, /*!< NCE33 : IOM/MSPI nCE group 33 value. */ - GPIO_PADREGI_PAD33FNCSEL_32kHzXT = 2, /*!< 32kHzXT : Configure as the 32kHz crystal output signal value. */ - GPIO_PADREGI_PAD33FNCSEL_GPIO33 = 3, /*!< GPIO33 : Configure as GPIO33 value. */ - GPIO_PADREGI_PAD33FNCSEL_UA0CTS = 5, /*!< UA0CTS : Configure as the UART0 CTS input value. */ - GPIO_PADREGI_PAD33FNCSEL_CT23 = 6, /*!< CT23 : CTIMER connection 23 value. */ - GPIO_PADREGI_PAD33FNCSEL_SWO = 7, /*!< SWO : Configure as the serial trace data output signal value. */ -} GPIO_PADREGI_PAD33FNCSEL_Enum; - -/* =========================================== GPIO PADREGI PAD33STRNG [10..10] ============================================ */ -typedef enum { /*!< GPIO_PADREGI_PAD33STRNG */ - GPIO_PADREGI_PAD33STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGI_PAD33STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGI_PAD33STRNG_Enum; - -/* ============================================ GPIO PADREGI PAD33INPEN [9..9] ============================================= */ -typedef enum { /*!< GPIO_PADREGI_PAD33INPEN */ - GPIO_PADREGI_PAD33INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGI_PAD33INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGI_PAD33INPEN_Enum; - -/* ============================================= GPIO PADREGI PAD33PULL [8..8] ============================================= */ -typedef enum { /*!< GPIO_PADREGI_PAD33PULL */ - GPIO_PADREGI_PAD33PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGI_PAD33PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGI_PAD33PULL_Enum; - -/* ============================================ GPIO PADREGI PAD32FNCSEL [3..5] ============================================ */ -typedef enum { /*!< GPIO_PADREGI_PAD32FNCSEL */ - GPIO_PADREGI_PAD32FNCSEL_ADCSE4 = 0, /*!< ADCSE4 : Configure as the analog input for ADC single ended - input 4 value. */ - GPIO_PADREGI_PAD32FNCSEL_NCE32 = 1, /*!< NCE32 : IOM/MSPI nCE group 32 value. */ - GPIO_PADREGI_PAD32FNCSEL_CT15 = 2, /*!< CT15 : CTIMER connection 15 value. */ - GPIO_PADREGI_PAD32FNCSEL_GPIO32 = 3, /*!< GPIO32 : Configure as GPIO32 value. */ - GPIO_PADREGI_PAD32FNCSEL_SCCIO = 4, /*!< SCCIO : SCARD serial data input/output value. */ - GPIO_PADREGI_PAD32FNCSEL_EXTLF = 5, /*!< EXTLF : External input to the LFRC oscillator value. */ - GPIO_PADREGI_PAD32FNCSEL_UA1CTS = 7, /*!< UA1CTS : Configure as the UART1 CTS input value. */ -} GPIO_PADREGI_PAD32FNCSEL_Enum; - -/* ============================================ GPIO PADREGI PAD32STRNG [2..2] ============================================= */ -typedef enum { /*!< GPIO_PADREGI_PAD32STRNG */ - GPIO_PADREGI_PAD32STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGI_PAD32STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGI_PAD32STRNG_Enum; - -/* ============================================ GPIO PADREGI PAD32INPEN [1..1] ============================================= */ -typedef enum { /*!< GPIO_PADREGI_PAD32INPEN */ - GPIO_PADREGI_PAD32INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGI_PAD32INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGI_PAD32INPEN_Enum; - -/* ============================================= GPIO PADREGI PAD32PULL [0..0] ============================================= */ -typedef enum { /*!< GPIO_PADREGI_PAD32PULL */ - GPIO_PADREGI_PAD32PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGI_PAD32PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGI_PAD32PULL_Enum; - -/* ======================================================== PADREGJ ======================================================== */ -/* ============================================ GPIO PADREGJ PAD39RSEL [30..31] ============================================ */ -typedef enum { /*!< GPIO_PADREGJ_PAD39RSEL */ - GPIO_PADREGJ_PAD39RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ - GPIO_PADREGJ_PAD39RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ - GPIO_PADREGJ_PAD39RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ - GPIO_PADREGJ_PAD39RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ -} GPIO_PADREGJ_PAD39RSEL_Enum; - -/* =========================================== GPIO PADREGJ PAD39FNCSEL [27..29] =========================================== */ -typedef enum { /*!< GPIO_PADREGJ_PAD39FNCSEL */ - GPIO_PADREGJ_PAD39FNCSEL_UART0TX = 0, /*!< UART0TX : Configure as the UART0 TX output signal value. */ - GPIO_PADREGJ_PAD39FNCSEL_UART1TX = 1, /*!< UART1TX : Configure as the UART1 TX output signal value. */ - GPIO_PADREGJ_PAD39FNCSEL_CT25 = 2, /*!< CT25 : CTIMER connection 25 value. */ - GPIO_PADREGJ_PAD39FNCSEL_GPIO39 = 3, /*!< GPIO39 : Configure as GPIO39 value. */ - GPIO_PADREGJ_PAD39FNCSEL_M4SCL = 4, /*!< M4SCL : Configure as the IOMSTR4 I2C SCL signal value. */ - GPIO_PADREGJ_PAD39FNCSEL_M4SCK = 5, /*!< M4SCK : Configure as the IOMSTR4 SPI SCK signal value. */ -} GPIO_PADREGJ_PAD39FNCSEL_Enum; - -/* =========================================== GPIO PADREGJ PAD39STRNG [26..26] ============================================ */ -typedef enum { /*!< GPIO_PADREGJ_PAD39STRNG */ - GPIO_PADREGJ_PAD39STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGJ_PAD39STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGJ_PAD39STRNG_Enum; - -/* =========================================== GPIO PADREGJ PAD39INPEN [25..25] ============================================ */ -typedef enum { /*!< GPIO_PADREGJ_PAD39INPEN */ - GPIO_PADREGJ_PAD39INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGJ_PAD39INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGJ_PAD39INPEN_Enum; - -/* ============================================ GPIO PADREGJ PAD39PULL [24..24] ============================================ */ -typedef enum { /*!< GPIO_PADREGJ_PAD39PULL */ - GPIO_PADREGJ_PAD39PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGJ_PAD39PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGJ_PAD39PULL_Enum; - -/* =========================================== GPIO PADREGJ PAD38FNCSEL [19..21] =========================================== */ -typedef enum { /*!< GPIO_PADREGJ_PAD38FNCSEL */ - GPIO_PADREGJ_PAD38FNCSEL_TRIG3 = 0, /*!< TRIG3 : Configure as the ADC Trigger 3 signal value. */ - GPIO_PADREGJ_PAD38FNCSEL_NCE38 = 1, /*!< NCE38 : IOM/MSPI nCE group 38 value. */ - GPIO_PADREGJ_PAD38FNCSEL_UA0CTS = 2, /*!< UA0CTS : Configure as the UART0 CTS signal value. */ - GPIO_PADREGJ_PAD38FNCSEL_GPIO38 = 3, /*!< GPIO38 : Configure as GPIO38 value. */ - GPIO_PADREGJ_PAD38FNCSEL_M3MOSI = 5, /*!< M3MOSI : Configure as the IOMSTR3 SPI MOSI output signal value. */ - GPIO_PADREGJ_PAD38FNCSEL_UART1RX = 6, /*!< UART1RX : Configure as the UART1 RX input signal value. */ -} GPIO_PADREGJ_PAD38FNCSEL_Enum; - -/* =========================================== GPIO PADREGJ PAD38STRNG [18..18] ============================================ */ -typedef enum { /*!< GPIO_PADREGJ_PAD38STRNG */ - GPIO_PADREGJ_PAD38STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGJ_PAD38STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGJ_PAD38STRNG_Enum; - -/* =========================================== GPIO PADREGJ PAD38INPEN [17..17] ============================================ */ -typedef enum { /*!< GPIO_PADREGJ_PAD38INPEN */ - GPIO_PADREGJ_PAD38INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGJ_PAD38INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGJ_PAD38INPEN_Enum; - -/* ============================================ GPIO PADREGJ PAD38PULL [16..16] ============================================ */ -typedef enum { /*!< GPIO_PADREGJ_PAD38PULL */ - GPIO_PADREGJ_PAD38PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGJ_PAD38PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGJ_PAD38PULL_Enum; - -/* =========================================== GPIO PADREGJ PAD37PWRDN [15..15] ============================================ */ -typedef enum { /*!< GPIO_PADREGJ_PAD37PWRDN */ - GPIO_PADREGJ_PAD37PWRDN_DIS = 0, /*!< DIS : Power switch disabled value. */ - GPIO_PADREGJ_PAD37PWRDN_EN = 1, /*!< EN : Power switch enabled (switch to GND) value. */ -} GPIO_PADREGJ_PAD37PWRDN_Enum; - -/* =========================================== GPIO PADREGJ PAD37FNCSEL [11..13] =========================================== */ -typedef enum { /*!< GPIO_PADREGJ_PAD37FNCSEL */ - GPIO_PADREGJ_PAD37FNCSEL_TRIG2 = 0, /*!< TRIG2 : Configure as the ADC Trigger 2 signal value. */ - GPIO_PADREGJ_PAD37FNCSEL_NCE37 = 1, /*!< NCE37 : IOM/MSPI nCE group 37 value. */ - GPIO_PADREGJ_PAD37FNCSEL_UA0RTS = 2, /*!< UA0RTS : Configure as the UART0 RTS output signal value. */ - GPIO_PADREGJ_PAD37FNCSEL_GPIO37 = 3, /*!< GPIO37 : Configure as GPIO37 value. */ - GPIO_PADREGJ_PAD37FNCSEL_SCCIO = 4, /*!< SCCIO : SCARD serial data input/output value. */ - GPIO_PADREGJ_PAD37FNCSEL_UART1TX = 5, /*!< UART1TX : Configure as the UART1 TX output signal value. */ - GPIO_PADREGJ_PAD37FNCSEL_PDMCLK = 6, /*!< PDMCLK : Configure as the PDM CLK output signal value. */ - GPIO_PADREGJ_PAD37FNCSEL_CT29 = 7, /*!< CT29 : CTIMER connection 29 value. */ -} GPIO_PADREGJ_PAD37FNCSEL_Enum; - -/* =========================================== GPIO PADREGJ PAD37STRNG [10..10] ============================================ */ -typedef enum { /*!< GPIO_PADREGJ_PAD37STRNG */ - GPIO_PADREGJ_PAD37STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGJ_PAD37STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGJ_PAD37STRNG_Enum; - -/* ============================================ GPIO PADREGJ PAD37INPEN [9..9] ============================================= */ -typedef enum { /*!< GPIO_PADREGJ_PAD37INPEN */ - GPIO_PADREGJ_PAD37INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGJ_PAD37INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGJ_PAD37INPEN_Enum; - -/* ============================================= GPIO PADREGJ PAD37PULL [8..8] ============================================= */ -typedef enum { /*!< GPIO_PADREGJ_PAD37PULL */ - GPIO_PADREGJ_PAD37PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGJ_PAD37PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGJ_PAD37PULL_Enum; - -/* ============================================ GPIO PADREGJ PAD36PWRUP [6..6] ============================================= */ -typedef enum { /*!< GPIO_PADREGJ_PAD36PWRUP */ - GPIO_PADREGJ_PAD36PWRUP_DIS = 0, /*!< DIS : Power switch disabled value. */ - GPIO_PADREGJ_PAD36PWRUP_EN = 1, /*!< EN : Power switch enabled (switched to VDD) value. */ -} GPIO_PADREGJ_PAD36PWRUP_Enum; - -/* ============================================ GPIO PADREGJ PAD36FNCSEL [3..5] ============================================ */ -typedef enum { /*!< GPIO_PADREGJ_PAD36FNCSEL */ - GPIO_PADREGJ_PAD36FNCSEL_TRIG1 = 0, /*!< TRIG1 : Configure as the ADC Trigger 1 signal value. */ - GPIO_PADREGJ_PAD36FNCSEL_NCE36 = 1, /*!< NCE36 : IOM/MSPI nCE group 36 value. */ - GPIO_PADREGJ_PAD36FNCSEL_UART1RX = 2, /*!< UART1RX : Configure as the UART1 RX input signal value. */ - GPIO_PADREGJ_PAD36FNCSEL_GPIO36 = 3, /*!< GPIO36 : Configure as GPIO36 value. */ - GPIO_PADREGJ_PAD36FNCSEL_32kHzXT = 4, /*!< 32kHzXT : Configure as the 32kHz output clock from the crystal - value. */ - GPIO_PADREGJ_PAD36FNCSEL_UA1CTS = 5, /*!< UA1CTS : Configure as the UART1 CTS input signal value. */ - GPIO_PADREGJ_PAD36FNCSEL_UA0CTS = 6, /*!< UA0CTS : Configure as the UART0 CTS input signal value. */ - GPIO_PADREGJ_PAD36FNCSEL_PDMDATA = 7, /*!< PDMDATA : PDM serial data input value. */ -} GPIO_PADREGJ_PAD36FNCSEL_Enum; - -/* ============================================ GPIO PADREGJ PAD36STRNG [2..2] ============================================= */ -typedef enum { /*!< GPIO_PADREGJ_PAD36STRNG */ - GPIO_PADREGJ_PAD36STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGJ_PAD36STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGJ_PAD36STRNG_Enum; - -/* ============================================ GPIO PADREGJ PAD36INPEN [1..1] ============================================= */ -typedef enum { /*!< GPIO_PADREGJ_PAD36INPEN */ - GPIO_PADREGJ_PAD36INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGJ_PAD36INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGJ_PAD36INPEN_Enum; - -/* ============================================= GPIO PADREGJ PAD36PULL [0..0] ============================================= */ -typedef enum { /*!< GPIO_PADREGJ_PAD36PULL */ - GPIO_PADREGJ_PAD36PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGJ_PAD36PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGJ_PAD36PULL_Enum; - -/* ======================================================== PADREGK ======================================================== */ -/* ============================================ GPIO PADREGK PAD43RSEL [30..31] ============================================ */ -typedef enum { /*!< GPIO_PADREGK_PAD43RSEL */ - GPIO_PADREGK_PAD43RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ - GPIO_PADREGK_PAD43RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ - GPIO_PADREGK_PAD43RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ - GPIO_PADREGK_PAD43RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ -} GPIO_PADREGK_PAD43RSEL_Enum; - -/* =========================================== GPIO PADREGK PAD43FNCSEL [27..29] =========================================== */ -typedef enum { /*!< GPIO_PADREGK_PAD43FNCSEL */ - GPIO_PADREGK_PAD43FNCSEL_UART1RX = 0, /*!< UART1RX : Configure as the UART1 RX input signal value. */ - GPIO_PADREGK_PAD43FNCSEL_NCE43 = 1, /*!< NCE43 : IOM/MSPI nCE group 43 value. */ - GPIO_PADREGK_PAD43FNCSEL_CT18 = 2, /*!< CT18 : CTIMER connection 18 value. */ - GPIO_PADREGK_PAD43FNCSEL_GPIO43 = 3, /*!< GPIO43 : Configure as GPIO43 value. */ - GPIO_PADREGK_PAD43FNCSEL_M3SDAWIR3 = 4, /*!< M3SDAWIR3 : Configure as the IOMSTR3 I2C SDA or SPI WIR3 signal - value. */ - GPIO_PADREGK_PAD43FNCSEL_M3MISO = 5, /*!< M3MISO : Configure as the IOMSTR3 SPI MISO signal value. */ -} GPIO_PADREGK_PAD43FNCSEL_Enum; - -/* =========================================== GPIO PADREGK PAD43STRNG [26..26] ============================================ */ -typedef enum { /*!< GPIO_PADREGK_PAD43STRNG */ - GPIO_PADREGK_PAD43STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGK_PAD43STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGK_PAD43STRNG_Enum; - -/* =========================================== GPIO PADREGK PAD43INPEN [25..25] ============================================ */ -typedef enum { /*!< GPIO_PADREGK_PAD43INPEN */ - GPIO_PADREGK_PAD43INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGK_PAD43INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGK_PAD43INPEN_Enum; - -/* ============================================ GPIO PADREGK PAD43PULL [24..24] ============================================ */ -typedef enum { /*!< GPIO_PADREGK_PAD43PULL */ - GPIO_PADREGK_PAD43PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGK_PAD43PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGK_PAD43PULL_Enum; - -/* ============================================ GPIO PADREGK PAD42RSEL [22..23] ============================================ */ -typedef enum { /*!< GPIO_PADREGK_PAD42RSEL */ - GPIO_PADREGK_PAD42RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ - GPIO_PADREGK_PAD42RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ - GPIO_PADREGK_PAD42RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ - GPIO_PADREGK_PAD42RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ -} GPIO_PADREGK_PAD42RSEL_Enum; - -/* =========================================== GPIO PADREGK PAD42FNCSEL [19..21] =========================================== */ -typedef enum { /*!< GPIO_PADREGK_PAD42FNCSEL */ - GPIO_PADREGK_PAD42FNCSEL_UART1TX = 0, /*!< UART1TX : Configure as the UART1 TX output signal value. */ - GPIO_PADREGK_PAD42FNCSEL_NCE42 = 1, /*!< NCE42 : IOM/MSPI nCE group 42 value. */ - GPIO_PADREGK_PAD42FNCSEL_CT16 = 2, /*!< CT16 : CTIMER connection 16 value. */ - GPIO_PADREGK_PAD42FNCSEL_GPIO42 = 3, /*!< GPIO42 : Configure as GPIO42 value. */ - GPIO_PADREGK_PAD42FNCSEL_M3SCL = 4, /*!< M3SCL : Configure as the IOMSTR3 I2C SCL clock I/O signal value. */ - GPIO_PADREGK_PAD42FNCSEL_M3SCK = 5, /*!< M3SCK : Configure as the IOMSTR3 SPI SCK output value. */ -} GPIO_PADREGK_PAD42FNCSEL_Enum; - -/* =========================================== GPIO PADREGK PAD42STRNG [18..18] ============================================ */ -typedef enum { /*!< GPIO_PADREGK_PAD42STRNG */ - GPIO_PADREGK_PAD42STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGK_PAD42STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGK_PAD42STRNG_Enum; - -/* =========================================== GPIO PADREGK PAD42INPEN [17..17] ============================================ */ -typedef enum { /*!< GPIO_PADREGK_PAD42INPEN */ - GPIO_PADREGK_PAD42INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGK_PAD42INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGK_PAD42INPEN_Enum; - -/* ============================================ GPIO PADREGK PAD42PULL [16..16] ============================================ */ -typedef enum { /*!< GPIO_PADREGK_PAD42PULL */ - GPIO_PADREGK_PAD42PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGK_PAD42PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGK_PAD42PULL_Enum; - -/* =========================================== GPIO PADREGK PAD41PWRDN [15..15] ============================================ */ -typedef enum { /*!< GPIO_PADREGK_PAD41PWRDN */ - GPIO_PADREGK_PAD41PWRDN_DIS = 0, /*!< DIS : Power switch disabled value. */ - GPIO_PADREGK_PAD41PWRDN_EN = 1, /*!< EN : Power switch enabled (Switch pad to VSS) value. */ -} GPIO_PADREGK_PAD41PWRDN_Enum; - -/* =========================================== GPIO PADREGK PAD41FNCSEL [11..13] =========================================== */ -typedef enum { /*!< GPIO_PADREGK_PAD41FNCSEL */ - GPIO_PADREGK_PAD41FNCSEL_NCE41 = 0, /*!< NCE41 : IOM/MSPI nCE group 41 value. */ - GPIO_PADREGK_PAD41FNCSEL_SWO = 2, /*!< SWO : Configure as the serial wire debug SWO signal value. */ - GPIO_PADREGK_PAD41FNCSEL_GPIO41 = 3, /*!< GPIO41 : Configure as GPIO41 value. */ - GPIO_PADREGK_PAD41FNCSEL_I2SWCLK = 4, /*!< I2SWCLK : I2S word clock input value. */ - GPIO_PADREGK_PAD41FNCSEL_UA1RTS = 5, /*!< UA1RTS : Configure as the UART1 RTS output signal value. */ - GPIO_PADREGK_PAD41FNCSEL_UART0TX = 6, /*!< UART0TX : Configure as the UART0 TX output signal value. */ - GPIO_PADREGK_PAD41FNCSEL_UA0RTS = 7, /*!< UA0RTS : Configure as the UART0 RTS output signal value. */ -} GPIO_PADREGK_PAD41FNCSEL_Enum; - -/* =========================================== GPIO PADREGK PAD41STRNG [10..10] ============================================ */ -typedef enum { /*!< GPIO_PADREGK_PAD41STRNG */ - GPIO_PADREGK_PAD41STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGK_PAD41STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGK_PAD41STRNG_Enum; - -/* ============================================ GPIO PADREGK PAD41INPEN [9..9] ============================================= */ -typedef enum { /*!< GPIO_PADREGK_PAD41INPEN */ - GPIO_PADREGK_PAD41INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGK_PAD41INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGK_PAD41INPEN_Enum; - -/* ============================================= GPIO PADREGK PAD41PULL [8..8] ============================================= */ -typedef enum { /*!< GPIO_PADREGK_PAD41PULL */ - GPIO_PADREGK_PAD41PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGK_PAD41PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGK_PAD41PULL_Enum; - -/* ============================================= GPIO PADREGK PAD40RSEL [6..7] ============================================= */ -typedef enum { /*!< GPIO_PADREGK_PAD40RSEL */ - GPIO_PADREGK_PAD40RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ - GPIO_PADREGK_PAD40RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ - GPIO_PADREGK_PAD40RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ - GPIO_PADREGK_PAD40RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ -} GPIO_PADREGK_PAD40RSEL_Enum; - -/* ============================================ GPIO PADREGK PAD40FNCSEL [3..5] ============================================ */ -typedef enum { /*!< GPIO_PADREGK_PAD40FNCSEL */ - GPIO_PADREGK_PAD40FNCSEL_UART0RX = 0, /*!< UART0RX : Configure as the UART0 RX input signal value. */ - GPIO_PADREGK_PAD40FNCSEL_UART1RX = 1, /*!< UART1RX : Configure as the UART1 RX input signal value. */ - GPIO_PADREGK_PAD40FNCSEL_TRIG0 = 2, /*!< TRIG0 : Configure as the ADC Trigger 0 signal value. */ - GPIO_PADREGK_PAD40FNCSEL_GPIO40 = 3, /*!< GPIO40 : Configure as GPIO40 value. */ - GPIO_PADREGK_PAD40FNCSEL_M4SDAWIR3 = 4, /*!< M4SDAWIR3 : Configure as the IOMSTR4 I2C SDA or SPI WIR3 signal - value. */ - GPIO_PADREGK_PAD40FNCSEL_M4MISO = 5, /*!< M4MISO : Configure as the IOMSTR4 SPI MISO input signal value. */ -} GPIO_PADREGK_PAD40FNCSEL_Enum; - -/* ============================================ GPIO PADREGK PAD40STRNG [2..2] ============================================= */ -typedef enum { /*!< GPIO_PADREGK_PAD40STRNG */ - GPIO_PADREGK_PAD40STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGK_PAD40STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGK_PAD40STRNG_Enum; - -/* ============================================ GPIO PADREGK PAD40INPEN [1..1] ============================================= */ -typedef enum { /*!< GPIO_PADREGK_PAD40INPEN */ - GPIO_PADREGK_PAD40INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGK_PAD40INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGK_PAD40INPEN_Enum; - -/* ============================================= GPIO PADREGK PAD40PULL [0..0] ============================================= */ -typedef enum { /*!< GPIO_PADREGK_PAD40PULL */ - GPIO_PADREGK_PAD40PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGK_PAD40PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGK_PAD40PULL_Enum; - -/* ======================================================== PADREGL ======================================================== */ -/* =========================================== GPIO PADREGL PAD47FNCSEL [27..29] =========================================== */ -typedef enum { /*!< GPIO_PADREGL_PAD47FNCSEL */ - GPIO_PADREGL_PAD47FNCSEL_32kHzXT = 0, /*!< 32kHzXT : Configure as the 32kHz output clock from the crystal - value. */ - GPIO_PADREGL_PAD47FNCSEL_NCE47 = 1, /*!< NCE47 : IOM/MSPI nCE group 47 value. */ - GPIO_PADREGL_PAD47FNCSEL_CT26 = 2, /*!< CT26 : CTIMER connection 26 value. */ - GPIO_PADREGL_PAD47FNCSEL_GPIO47 = 3, /*!< GPIO47 : Configure as GPIO47 value. */ - GPIO_PADREGL_PAD47FNCSEL_M5MOSI = 5, /*!< M5MOSI : Configure as the IOMSTR5 SPI MOSI output signal value. */ - GPIO_PADREGL_PAD47FNCSEL_UART1RX = 6, /*!< UART1RX : Configure as the UART1 RX input signal value. */ -} GPIO_PADREGL_PAD47FNCSEL_Enum; - -/* =========================================== GPIO PADREGL PAD47STRNG [26..26] ============================================ */ -typedef enum { /*!< GPIO_PADREGL_PAD47STRNG */ - GPIO_PADREGL_PAD47STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGL_PAD47STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGL_PAD47STRNG_Enum; - -/* =========================================== GPIO PADREGL PAD47INPEN [25..25] ============================================ */ -typedef enum { /*!< GPIO_PADREGL_PAD47INPEN */ - GPIO_PADREGL_PAD47INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGL_PAD47INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGL_PAD47INPEN_Enum; - -/* ============================================ GPIO PADREGL PAD47PULL [24..24] ============================================ */ -typedef enum { /*!< GPIO_PADREGL_PAD47PULL */ - GPIO_PADREGL_PAD47PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGL_PAD47PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGL_PAD47PULL_Enum; - -/* =========================================== GPIO PADREGL PAD46FNCSEL [19..21] =========================================== */ -typedef enum { /*!< GPIO_PADREGL_PAD46FNCSEL */ - GPIO_PADREGL_PAD46FNCSEL_32khz_XT = 0, /*!< 32khz_XT : Configure as the 32kHz output clock from the crystal - value. */ - GPIO_PADREGL_PAD46FNCSEL_NCE46 = 1, /*!< NCE46 : IOM/MSPI nCE group 46 value. */ - GPIO_PADREGL_PAD46FNCSEL_CT24 = 2, /*!< CT24 : CTIMER connection 24 value. */ - GPIO_PADREGL_PAD46FNCSEL_GPIO46 = 3, /*!< GPIO46 : Configure as GPIO46 value. */ - GPIO_PADREGL_PAD46FNCSEL_SCCRST = 4, /*!< SCCRST : SCARD reset output value. */ - GPIO_PADREGL_PAD46FNCSEL_PDMCLK = 5, /*!< PDMCLK : PDM serial clock output value. */ - GPIO_PADREGL_PAD46FNCSEL_UART1TX = 6, /*!< UART1TX : Configure as the UART1 TX output signal value. */ - GPIO_PADREGL_PAD46FNCSEL_SWO = 7, /*!< SWO : Configure as the serial wire debug SWO signal value. */ -} GPIO_PADREGL_PAD46FNCSEL_Enum; - -/* =========================================== GPIO PADREGL PAD46STRNG [18..18] ============================================ */ -typedef enum { /*!< GPIO_PADREGL_PAD46STRNG */ - GPIO_PADREGL_PAD46STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGL_PAD46STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGL_PAD46STRNG_Enum; - -/* =========================================== GPIO PADREGL PAD46INPEN [17..17] ============================================ */ -typedef enum { /*!< GPIO_PADREGL_PAD46INPEN */ - GPIO_PADREGL_PAD46INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGL_PAD46INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGL_PAD46INPEN_Enum; - -/* ============================================ GPIO PADREGL PAD46PULL [16..16] ============================================ */ -typedef enum { /*!< GPIO_PADREGL_PAD46PULL */ - GPIO_PADREGL_PAD46PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGL_PAD46PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGL_PAD46PULL_Enum; - -/* =========================================== GPIO PADREGL PAD45FNCSEL [11..13] =========================================== */ -typedef enum { /*!< GPIO_PADREGL_PAD45FNCSEL */ - GPIO_PADREGL_PAD45FNCSEL_UA1CTS = 0, /*!< UA1CTS : Configure as the UART1 CTS input signal value. */ - GPIO_PADREGL_PAD45FNCSEL_NCE45 = 1, /*!< NCE45 : IOM/MSPI nCE group 45 value. */ - GPIO_PADREGL_PAD45FNCSEL_CT22 = 2, /*!< CT22 : CTIMER connection 22 value. */ - GPIO_PADREGL_PAD45FNCSEL_GPIO45 = 3, /*!< GPIO45 : Configure as GPIO45 value. */ - GPIO_PADREGL_PAD45FNCSEL_I2SDAT = 4, /*!< I2SDAT : I2S serial data output value. */ - GPIO_PADREGL_PAD45FNCSEL_PDMDATA = 5, /*!< PDMDATA : PDM serial data input value. */ - GPIO_PADREGL_PAD45FNCSEL_UART0RX = 6, /*!< UART0RX : Configure as the SPI channel 5 nCE signal from IOMSTR5 - value. */ - GPIO_PADREGL_PAD45FNCSEL_SWO = 7, /*!< SWO : Configure as the serial wire debug SWO signal value. */ -} GPIO_PADREGL_PAD45FNCSEL_Enum; - -/* =========================================== GPIO PADREGL PAD45STRNG [10..10] ============================================ */ -typedef enum { /*!< GPIO_PADREGL_PAD45STRNG */ - GPIO_PADREGL_PAD45STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGL_PAD45STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGL_PAD45STRNG_Enum; - -/* ============================================ GPIO PADREGL PAD45INPEN [9..9] ============================================= */ -typedef enum { /*!< GPIO_PADREGL_PAD45INPEN */ - GPIO_PADREGL_PAD45INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGL_PAD45INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGL_PAD45INPEN_Enum; - -/* ============================================= GPIO PADREGL PAD45PULL [8..8] ============================================= */ -typedef enum { /*!< GPIO_PADREGL_PAD45PULL */ - GPIO_PADREGL_PAD45PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGL_PAD45PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGL_PAD45PULL_Enum; - -/* ============================================ GPIO PADREGL PAD44FNCSEL [3..5] ============================================ */ -typedef enum { /*!< GPIO_PADREGL_PAD44FNCSEL */ - GPIO_PADREGL_PAD44FNCSEL_UA1RTS = 0, /*!< UA1RTS : Configure as the UART1 RTS output signal value. */ - GPIO_PADREGL_PAD44FNCSEL_NCE44 = 1, /*!< NCE44 : IOM/MSPI nCE group 44 value. */ - GPIO_PADREGL_PAD44FNCSEL_CT20 = 2, /*!< CT20 : CTIMER connection 20 value. */ - GPIO_PADREGL_PAD44FNCSEL_GPIO44 = 3, /*!< GPIO44 : Configure as GPIO44 value. */ - GPIO_PADREGL_PAD44FNCSEL_M4MOSI = 5, /*!< M4MOSI : Configure as the IOMSTR4 SPI MOSI signal value. */ - GPIO_PADREGL_PAD44FNCSEL_M5nCE6 = 6, /*!< M5nCE6 : Configure as the SPI channel 6 nCE signal from IOMSTR5 - value. */ -} GPIO_PADREGL_PAD44FNCSEL_Enum; - -/* ============================================ GPIO PADREGL PAD44STRNG [2..2] ============================================= */ -typedef enum { /*!< GPIO_PADREGL_PAD44STRNG */ - GPIO_PADREGL_PAD44STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGL_PAD44STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGL_PAD44STRNG_Enum; - -/* ============================================ GPIO PADREGL PAD44INPEN [1..1] ============================================= */ -typedef enum { /*!< GPIO_PADREGL_PAD44INPEN */ - GPIO_PADREGL_PAD44INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGL_PAD44INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGL_PAD44INPEN_Enum; - -/* ============================================= GPIO PADREGL PAD44PULL [0..0] ============================================= */ -typedef enum { /*!< GPIO_PADREGL_PAD44PULL */ - GPIO_PADREGL_PAD44PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGL_PAD44PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGL_PAD44PULL_Enum; - -/* ======================================================== PADREGM ======================================================== */ -/* ============================================ GPIO PADREGM PAD49RSEL [14..15] ============================================ */ -typedef enum { /*!< GPIO_PADREGM_PAD49RSEL */ - GPIO_PADREGM_PAD49RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ - GPIO_PADREGM_PAD49RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ - GPIO_PADREGM_PAD49RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ - GPIO_PADREGM_PAD49RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ -} GPIO_PADREGM_PAD49RSEL_Enum; - -/* =========================================== GPIO PADREGM PAD49FNCSEL [11..13] =========================================== */ -typedef enum { /*!< GPIO_PADREGM_PAD49FNCSEL */ - GPIO_PADREGM_PAD49FNCSEL_UART0RX = 0, /*!< UART0RX : Configure as the UART0 RX input signal value. */ - GPIO_PADREGM_PAD49FNCSEL_NCE49 = 1, /*!< NCE49 : IOM/MSPPI nCE group 49 value. */ - GPIO_PADREGM_PAD49FNCSEL_CT30 = 2, /*!< CT30 : CTIMER connection 30 value. */ - GPIO_PADREGM_PAD49FNCSEL_GPIO49 = 3, /*!< GPIO49 : Configure as GPIO49 value. */ - GPIO_PADREGM_PAD49FNCSEL_M5SDAWIR3 = 4, /*!< M5SDAWIR3 : Configure as the IOMSTR5 I2C SDA or SPI WIR3 signal - value. */ - GPIO_PADREGM_PAD49FNCSEL_M5MISO = 5, /*!< M5MISO : Configure as the IOMSTR5 SPI MISO input signal value. */ -} GPIO_PADREGM_PAD49FNCSEL_Enum; - -/* =========================================== GPIO PADREGM PAD49STRNG [10..10] ============================================ */ -typedef enum { /*!< GPIO_PADREGM_PAD49STRNG */ - GPIO_PADREGM_PAD49STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGM_PAD49STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGM_PAD49STRNG_Enum; - -/* ============================================ GPIO PADREGM PAD49INPEN [9..9] ============================================= */ -typedef enum { /*!< GPIO_PADREGM_PAD49INPEN */ - GPIO_PADREGM_PAD49INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGM_PAD49INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGM_PAD49INPEN_Enum; - -/* ============================================= GPIO PADREGM PAD49PULL [8..8] ============================================= */ -typedef enum { /*!< GPIO_PADREGM_PAD49PULL */ - GPIO_PADREGM_PAD49PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGM_PAD49PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGM_PAD49PULL_Enum; - -/* ============================================= GPIO PADREGM PAD48RSEL [6..7] ============================================= */ -typedef enum { /*!< GPIO_PADREGM_PAD48RSEL */ - GPIO_PADREGM_PAD48RSEL_PULL1_5K = 0, /*!< PULL1_5K : Pullup is ~1.5 KOhms value. */ - GPIO_PADREGM_PAD48RSEL_PULL6K = 1, /*!< PULL6K : Pullup is ~6 KOhms value. */ - GPIO_PADREGM_PAD48RSEL_PULL12K = 2, /*!< PULL12K : Pullup is ~12 KOhms value. */ - GPIO_PADREGM_PAD48RSEL_PULL24K = 3, /*!< PULL24K : Pullup is ~24 KOhms value. */ -} GPIO_PADREGM_PAD48RSEL_Enum; - -/* ============================================ GPIO PADREGM PAD48FNCSEL [3..5] ============================================ */ -typedef enum { /*!< GPIO_PADREGM_PAD48FNCSEL */ - GPIO_PADREGM_PAD48FNCSEL_UART0TX = 0, /*!< UART0TX : Configure as the UART0 TX output signal value. */ - GPIO_PADREGM_PAD48FNCSEL_NCE48 = 1, /*!< NCE48 : IOM/MSPI nCE group 48 value. */ - GPIO_PADREGM_PAD48FNCSEL_CT28 = 2, /*!< CT28 : CTIMER conenction 28 value. */ - GPIO_PADREGM_PAD48FNCSEL_GPIO48 = 3, /*!< GPIO48 : Configure as GPIO48 value. */ - GPIO_PADREGM_PAD48FNCSEL_M5SCL = 4, /*!< M5SCL : Configure as the IOMSTR5 I2C SCL clock I/O signal value. */ - GPIO_PADREGM_PAD48FNCSEL_M5SCK = 5, /*!< M5SCK : Configure as the IOMSTR5 SPI SCK output value. */ -} GPIO_PADREGM_PAD48FNCSEL_Enum; - -/* ============================================ GPIO PADREGM PAD48STRNG [2..2] ============================================= */ -typedef enum { /*!< GPIO_PADREGM_PAD48STRNG */ - GPIO_PADREGM_PAD48STRNG_LOW = 0, /*!< LOW : Low drive strength value. */ - GPIO_PADREGM_PAD48STRNG_HIGH = 1, /*!< HIGH : High drive strength value. */ -} GPIO_PADREGM_PAD48STRNG_Enum; - -/* ============================================ GPIO PADREGM PAD48INPEN [1..1] ============================================= */ -typedef enum { /*!< GPIO_PADREGM_PAD48INPEN */ - GPIO_PADREGM_PAD48INPEN_DIS = 0, /*!< DIS : Pad input disabled value. */ - GPIO_PADREGM_PAD48INPEN_EN = 1, /*!< EN : Pad input enabled value. */ -} GPIO_PADREGM_PAD48INPEN_Enum; - -/* ============================================= GPIO PADREGM PAD48PULL [0..0] ============================================= */ -typedef enum { /*!< GPIO_PADREGM_PAD48PULL */ - GPIO_PADREGM_PAD48PULL_DIS = 0, /*!< DIS : Pullup disabled value. */ - GPIO_PADREGM_PAD48PULL_EN = 1, /*!< EN : Pullup enabled value. */ -} GPIO_PADREGM_PAD48PULL_Enum; - -/* ========================================================= CFGA ========================================================== */ -/* ============================================= GPIO CFGA GPIO7INTD [31..31] ============================================== */ -typedef enum { /*!< GPIO_CFGA_GPIO7INTD */ - GPIO_CFGA_GPIO7INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x0 - nCE polarity active low value. */ - GPIO_CFGA_GPIO7INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x0 - nCE polarity active high value. */ -} GPIO_CFGA_GPIO7INTD_Enum; - -/* ============================================ GPIO CFGA GPIO7OUTCFG [29..30] ============================================= */ -typedef enum { /*!< GPIO_CFGA_GPIO7OUTCFG */ - GPIO_CFGA_GPIO7OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGA_GPIO7OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGA_GPIO7OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGA_GPIO7OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGA_GPIO7OUTCFG_Enum; - -/* ============================================= GPIO CFGA GPIO7INCFG [28..28] ============================================= */ -typedef enum { /*!< GPIO_CFGA_GPIO7INCFG */ - GPIO_CFGA_GPIO7INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGA_GPIO7INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGA_GPIO7INCFG_Enum; - -/* ============================================= GPIO CFGA GPIO6INTD [27..27] ============================================== */ -typedef enum { /*!< GPIO_CFGA_GPIO6INTD */ - GPIO_CFGA_GPIO6INTD_INTDIS = 0, /*!< INTDIS : INCFG = 1 - No interrupt on GPIO transition value. */ - GPIO_CFGA_GPIO6INTD_INTBOTH = 1, /*!< INTBOTH : INCFG = 1 - Interrupt on either low to high or high - to low GPIO transition value. */ -} GPIO_CFGA_GPIO6INTD_Enum; - -/* ============================================ GPIO CFGA GPIO6OUTCFG [25..26] ============================================= */ -typedef enum { /*!< GPIO_CFGA_GPIO6OUTCFG */ - GPIO_CFGA_GPIO6OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGA_GPIO6OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGA_GPIO6OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGA_GPIO6OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGA_GPIO6OUTCFG_Enum; - -/* ============================================= GPIO CFGA GPIO6INCFG [24..24] ============================================= */ -typedef enum { /*!< GPIO_CFGA_GPIO6INCFG */ - GPIO_CFGA_GPIO6INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGA_GPIO6INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGA_GPIO6INCFG_Enum; - -/* ============================================= GPIO CFGA GPIO5INTD [23..23] ============================================== */ -typedef enum { /*!< GPIO_CFGA_GPIO5INTD */ - GPIO_CFGA_GPIO5INTD_INTDIS = 0, /*!< INTDIS : INCFG = 1 - No interrupt on GPIO transition value. */ - GPIO_CFGA_GPIO5INTD_INTBOTH = 1, /*!< INTBOTH : INCFG = 1 - Interrupt on either low to high or high - to low GPIO transition value. */ -} GPIO_CFGA_GPIO5INTD_Enum; - -/* ============================================ GPIO CFGA GPIO5OUTCFG [21..22] ============================================= */ -typedef enum { /*!< GPIO_CFGA_GPIO5OUTCFG */ - GPIO_CFGA_GPIO5OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGA_GPIO5OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGA_GPIO5OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGA_GPIO5OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGA_GPIO5OUTCFG_Enum; - -/* ============================================= GPIO CFGA GPIO5INCFG [20..20] ============================================= */ -typedef enum { /*!< GPIO_CFGA_GPIO5INCFG */ - GPIO_CFGA_GPIO5INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGA_GPIO5INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGA_GPIO5INCFG_Enum; - -/* ============================================= GPIO CFGA GPIO4INTD [19..19] ============================================== */ -typedef enum { /*!< GPIO_CFGA_GPIO4INTD */ - GPIO_CFGA_GPIO4INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x2 - nCE polarity active low value. */ - GPIO_CFGA_GPIO4INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x2 - nCE polarity active high value. */ -} GPIO_CFGA_GPIO4INTD_Enum; - -/* ============================================ GPIO CFGA GPIO4OUTCFG [17..18] ============================================= */ -typedef enum { /*!< GPIO_CFGA_GPIO4OUTCFG */ - GPIO_CFGA_GPIO4OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGA_GPIO4OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGA_GPIO4OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGA_GPIO4OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGA_GPIO4OUTCFG_Enum; - -/* ============================================= GPIO CFGA GPIO4INCFG [16..16] ============================================= */ -typedef enum { /*!< GPIO_CFGA_GPIO4INCFG */ - GPIO_CFGA_GPIO4INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGA_GPIO4INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGA_GPIO4INCFG_Enum; - -/* ============================================= GPIO CFGA GPIO3INTD [15..15] ============================================== */ -typedef enum { /*!< GPIO_CFGA_GPIO3INTD */ - GPIO_CFGA_GPIO3INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x2 - nCE polarity active low value. */ - GPIO_CFGA_GPIO3INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x2 - nCE polarity active high value. */ -} GPIO_CFGA_GPIO3INTD_Enum; - -/* ============================================ GPIO CFGA GPIO3OUTCFG [13..14] ============================================= */ -typedef enum { /*!< GPIO_CFGA_GPIO3OUTCFG */ - GPIO_CFGA_GPIO3OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGA_GPIO3OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGA_GPIO3OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGA_GPIO3OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGA_GPIO3OUTCFG_Enum; - -/* ============================================= GPIO CFGA GPIO3INCFG [12..12] ============================================= */ -typedef enum { /*!< GPIO_CFGA_GPIO3INCFG */ - GPIO_CFGA_GPIO3INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGA_GPIO3INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGA_GPIO3INCFG_Enum; - -/* ============================================= GPIO CFGA GPIO2INTD [11..11] ============================================== */ -typedef enum { /*!< GPIO_CFGA_GPIO2INTD */ - GPIO_CFGA_GPIO2INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x7 - nCE polarity active low value. */ - GPIO_CFGA_GPIO2INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x7 - nCE polarity active high value. */ -} GPIO_CFGA_GPIO2INTD_Enum; - -/* ============================================= GPIO CFGA GPIO2OUTCFG [9..10] ============================================= */ -typedef enum { /*!< GPIO_CFGA_GPIO2OUTCFG */ - GPIO_CFGA_GPIO2OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGA_GPIO2OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGA_GPIO2OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGA_GPIO2OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGA_GPIO2OUTCFG_Enum; - -/* ============================================== GPIO CFGA GPIO2INCFG [8..8] ============================================== */ -typedef enum { /*!< GPIO_CFGA_GPIO2INCFG */ - GPIO_CFGA_GPIO2INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGA_GPIO2INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGA_GPIO2INCFG_Enum; - -/* ============================================== GPIO CFGA GPIO1INTD [7..7] =============================================== */ -typedef enum { /*!< GPIO_CFGA_GPIO1INTD */ - GPIO_CFGA_GPIO1INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x7 - nCE polarity active low value. */ - GPIO_CFGA_GPIO1INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x7 - nCE polarity active high value. */ -} GPIO_CFGA_GPIO1INTD_Enum; - -/* ============================================= GPIO CFGA GPIO1OUTCFG [5..6] ============================================== */ -typedef enum { /*!< GPIO_CFGA_GPIO1OUTCFG */ - GPIO_CFGA_GPIO1OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGA_GPIO1OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGA_GPIO1OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGA_GPIO1OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGA_GPIO1OUTCFG_Enum; - -/* ============================================== GPIO CFGA GPIO1INCFG [4..4] ============================================== */ -typedef enum { /*!< GPIO_CFGA_GPIO1INCFG */ - GPIO_CFGA_GPIO1INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGA_GPIO1INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGA_GPIO1INCFG_Enum; - -/* ============================================== GPIO CFGA GPIO0INTD [3..3] =============================================== */ -typedef enum { /*!< GPIO_CFGA_GPIO0INTD */ - GPIO_CFGA_GPIO0INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x7 - nCE polarity active low value. */ - GPIO_CFGA_GPIO0INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x7 - nCE polarity active high value. */ -} GPIO_CFGA_GPIO0INTD_Enum; - -/* ============================================= GPIO CFGA GPIO0OUTCFG [1..2] ============================================== */ -typedef enum { /*!< GPIO_CFGA_GPIO0OUTCFG */ - GPIO_CFGA_GPIO0OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGA_GPIO0OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGA_GPIO0OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGA_GPIO0OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGA_GPIO0OUTCFG_Enum; - -/* ============================================== GPIO CFGA GPIO0INCFG [0..0] ============================================== */ -typedef enum { /*!< GPIO_CFGA_GPIO0INCFG */ - GPIO_CFGA_GPIO0INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGA_GPIO0INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGA_GPIO0INCFG_Enum; - -/* ========================================================= CFGB ========================================================== */ -/* ============================================= GPIO CFGB GPIO15INTD [31..31] ============================================= */ -typedef enum { /*!< GPIO_CFGB_GPIO15INTD */ - GPIO_CFGB_GPIO15INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGB_GPIO15INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGB_GPIO15INTD_Enum; - -/* ============================================ GPIO CFGB GPIO15OUTCFG [29..30] ============================================ */ -typedef enum { /*!< GPIO_CFGB_GPIO15OUTCFG */ - GPIO_CFGB_GPIO15OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGB_GPIO15OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGB_GPIO15OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGB_GPIO15OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGB_GPIO15OUTCFG_Enum; - -/* ============================================ GPIO CFGB GPIO15INCFG [28..28] ============================================= */ -typedef enum { /*!< GPIO_CFGB_GPIO15INCFG */ - GPIO_CFGB_GPIO15INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGB_GPIO15INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGB_GPIO15INCFG_Enum; - -/* ============================================= GPIO CFGB GPIO14INTD [27..27] ============================================= */ -typedef enum { /*!< GPIO_CFGB_GPIO14INTD */ - GPIO_CFGB_GPIO14INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGB_GPIO14INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGB_GPIO14INTD_Enum; - -/* ============================================ GPIO CFGB GPIO14OUTCFG [25..26] ============================================ */ -typedef enum { /*!< GPIO_CFGB_GPIO14OUTCFG */ - GPIO_CFGB_GPIO14OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGB_GPIO14OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGB_GPIO14OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGB_GPIO14OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGB_GPIO14OUTCFG_Enum; - -/* ============================================ GPIO CFGB GPIO14INCFG [24..24] ============================================= */ -typedef enum { /*!< GPIO_CFGB_GPIO14INCFG */ - GPIO_CFGB_GPIO14INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGB_GPIO14INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGB_GPIO14INCFG_Enum; - -/* ============================================= GPIO CFGB GPIO13INTD [23..23] ============================================= */ -typedef enum { /*!< GPIO_CFGB_GPIO13INTD */ - GPIO_CFGB_GPIO13INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGB_GPIO13INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGB_GPIO13INTD_Enum; - -/* ============================================ GPIO CFGB GPIO13OUTCFG [21..22] ============================================ */ -typedef enum { /*!< GPIO_CFGB_GPIO13OUTCFG */ - GPIO_CFGB_GPIO13OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGB_GPIO13OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGB_GPIO13OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGB_GPIO13OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGB_GPIO13OUTCFG_Enum; - -/* ============================================ GPIO CFGB GPIO13INCFG [20..20] ============================================= */ -typedef enum { /*!< GPIO_CFGB_GPIO13INCFG */ - GPIO_CFGB_GPIO13INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGB_GPIO13INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGB_GPIO13INCFG_Enum; - -/* ============================================= GPIO CFGB GPIO12INTD [19..19] ============================================= */ -typedef enum { /*!< GPIO_CFGB_GPIO12INTD */ - GPIO_CFGB_GPIO12INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGB_GPIO12INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGB_GPIO12INTD_Enum; - -/* ============================================ GPIO CFGB GPIO12OUTCFG [17..18] ============================================ */ -typedef enum { /*!< GPIO_CFGB_GPIO12OUTCFG */ - GPIO_CFGB_GPIO12OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGB_GPIO12OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGB_GPIO12OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGB_GPIO12OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGB_GPIO12OUTCFG_Enum; - -/* ============================================ GPIO CFGB GPIO12INCFG [16..16] ============================================= */ -typedef enum { /*!< GPIO_CFGB_GPIO12INCFG */ - GPIO_CFGB_GPIO12INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGB_GPIO12INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGB_GPIO12INCFG_Enum; - -/* ============================================= GPIO CFGB GPIO11INTD [15..15] ============================================= */ -typedef enum { /*!< GPIO_CFGB_GPIO11INTD */ - GPIO_CFGB_GPIO11INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGB_GPIO11INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGB_GPIO11INTD_Enum; - -/* ============================================ GPIO CFGB GPIO11OUTCFG [13..14] ============================================ */ -typedef enum { /*!< GPIO_CFGB_GPIO11OUTCFG */ - GPIO_CFGB_GPIO11OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGB_GPIO11OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGB_GPIO11OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGB_GPIO11OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGB_GPIO11OUTCFG_Enum; - -/* ============================================ GPIO CFGB GPIO11INCFG [12..12] ============================================= */ -typedef enum { /*!< GPIO_CFGB_GPIO11INCFG */ - GPIO_CFGB_GPIO11INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGB_GPIO11INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGB_GPIO11INCFG_Enum; - -/* ============================================= GPIO CFGB GPIO10INTD [11..11] ============================================= */ -typedef enum { /*!< GPIO_CFGB_GPIO10INTD */ - GPIO_CFGB_GPIO10INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x2 - nCE polarity active low value. */ - GPIO_CFGB_GPIO10INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x2 - nCE polarity active high value. */ -} GPIO_CFGB_GPIO10INTD_Enum; - -/* ============================================ GPIO CFGB GPIO10OUTCFG [9..10] ============================================= */ -typedef enum { /*!< GPIO_CFGB_GPIO10OUTCFG */ - GPIO_CFGB_GPIO10OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGB_GPIO10OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGB_GPIO10OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGB_GPIO10OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGB_GPIO10OUTCFG_Enum; - -/* ============================================= GPIO CFGB GPIO10INCFG [8..8] ============================================== */ -typedef enum { /*!< GPIO_CFGB_GPIO10INCFG */ - GPIO_CFGB_GPIO10INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGB_GPIO10INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGB_GPIO10INCFG_Enum; - -/* ============================================== GPIO CFGB GPIO9INTD [7..7] =============================================== */ -typedef enum { /*!< GPIO_CFGB_GPIO9INTD */ - GPIO_CFGB_GPIO9INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x2 - nCE polarity active low value. */ - GPIO_CFGB_GPIO9INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x2 - nCE polarity active high value. */ -} GPIO_CFGB_GPIO9INTD_Enum; - -/* ============================================= GPIO CFGB GPIO9OUTCFG [5..6] ============================================== */ -typedef enum { /*!< GPIO_CFGB_GPIO9OUTCFG */ - GPIO_CFGB_GPIO9OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGB_GPIO9OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGB_GPIO9OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGB_GPIO9OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGB_GPIO9OUTCFG_Enum; - -/* ============================================== GPIO CFGB GPIO9INCFG [4..4] ============================================== */ -typedef enum { /*!< GPIO_CFGB_GPIO9INCFG */ - GPIO_CFGB_GPIO9INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGB_GPIO9INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGB_GPIO9INCFG_Enum; - -/* ============================================== GPIO CFGB GPIO8INTD [3..3] =============================================== */ -typedef enum { /*!< GPIO_CFGB_GPIO8INTD */ - GPIO_CFGB_GPIO8INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x2 - nCE polarity active low value. */ - GPIO_CFGB_GPIO8INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x2 - nCE polarity active high value. */ -} GPIO_CFGB_GPIO8INTD_Enum; - -/* ============================================= GPIO CFGB GPIO8OUTCFG [1..2] ============================================== */ -typedef enum { /*!< GPIO_CFGB_GPIO8OUTCFG */ - GPIO_CFGB_GPIO8OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGB_GPIO8OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGB_GPIO8OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGB_GPIO8OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGB_GPIO8OUTCFG_Enum; - -/* ============================================== GPIO CFGB GPIO8INCFG [0..0] ============================================== */ -typedef enum { /*!< GPIO_CFGB_GPIO8INCFG */ - GPIO_CFGB_GPIO8INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGB_GPIO8INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGB_GPIO8INCFG_Enum; - -/* ========================================================= CFGC ========================================================== */ -/* ============================================= GPIO CFGC GPIO23INTD [31..31] ============================================= */ -typedef enum { /*!< GPIO_CFGC_GPIO23INTD */ - GPIO_CFGC_GPIO23INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGC_GPIO23INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGC_GPIO23INTD_Enum; - -/* ============================================ GPIO CFGC GPIO23OUTCFG [29..30] ============================================ */ -typedef enum { /*!< GPIO_CFGC_GPIO23OUTCFG */ - GPIO_CFGC_GPIO23OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGC_GPIO23OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGC_GPIO23OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGC_GPIO23OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGC_GPIO23OUTCFG_Enum; - -/* ============================================ GPIO CFGC GPIO23INCFG [28..28] ============================================= */ -typedef enum { /*!< GPIO_CFGC_GPIO23INCFG */ - GPIO_CFGC_GPIO23INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGC_GPIO23INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGC_GPIO23INCFG_Enum; - -/* ============================================= GPIO CFGC GPIO22INTD [27..27] ============================================= */ -typedef enum { /*!< GPIO_CFGC_GPIO22INTD */ - GPIO_CFGC_GPIO22INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGC_GPIO22INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGC_GPIO22INTD_Enum; - -/* ============================================ GPIO CFGC GPIO22OUTCFG [25..26] ============================================ */ -typedef enum { /*!< GPIO_CFGC_GPIO22OUTCFG */ - GPIO_CFGC_GPIO22OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGC_GPIO22OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGC_GPIO22OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGC_GPIO22OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGC_GPIO22OUTCFG_Enum; - -/* ============================================ GPIO CFGC GPIO22INCFG [24..24] ============================================= */ -typedef enum { /*!< GPIO_CFGC_GPIO22INCFG */ - GPIO_CFGC_GPIO22INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGC_GPIO22INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGC_GPIO22INCFG_Enum; - -/* ============================================= GPIO CFGC GPIO21INTD [23..23] ============================================= */ -typedef enum { /*!< GPIO_CFGC_GPIO21INTD */ - GPIO_CFGC_GPIO21INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGC_GPIO21INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGC_GPIO21INTD_Enum; - -/* ============================================ GPIO CFGC GPIO21OUTCFG [21..22] ============================================ */ -typedef enum { /*!< GPIO_CFGC_GPIO21OUTCFG */ - GPIO_CFGC_GPIO21OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGC_GPIO21OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGC_GPIO21OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGC_GPIO21OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGC_GPIO21OUTCFG_Enum; - -/* ============================================ GPIO CFGC GPIO21INCFG [20..20] ============================================= */ -typedef enum { /*!< GPIO_CFGC_GPIO21INCFG */ - GPIO_CFGC_GPIO21INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGC_GPIO21INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGC_GPIO21INCFG_Enum; - -/* ============================================= GPIO CFGC GPIO20INTD [19..19] ============================================= */ -typedef enum { /*!< GPIO_CFGC_GPIO20INTD */ - GPIO_CFGC_GPIO20INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGC_GPIO20INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGC_GPIO20INTD_Enum; - -/* ============================================ GPIO CFGC GPIO20OUTCFG [17..18] ============================================ */ -typedef enum { /*!< GPIO_CFGC_GPIO20OUTCFG */ - GPIO_CFGC_GPIO20OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGC_GPIO20OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGC_GPIO20OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGC_GPIO20OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGC_GPIO20OUTCFG_Enum; - -/* ============================================ GPIO CFGC GPIO20INCFG [16..16] ============================================= */ -typedef enum { /*!< GPIO_CFGC_GPIO20INCFG */ - GPIO_CFGC_GPIO20INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGC_GPIO20INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGC_GPIO20INCFG_Enum; - -/* ============================================= GPIO CFGC GPIO19INTD [15..15] ============================================= */ -typedef enum { /*!< GPIO_CFGC_GPIO19INTD */ - GPIO_CFGC_GPIO19INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGC_GPIO19INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGC_GPIO19INTD_Enum; - -/* ============================================ GPIO CFGC GPIO19OUTCFG [13..14] ============================================ */ -typedef enum { /*!< GPIO_CFGC_GPIO19OUTCFG */ - GPIO_CFGC_GPIO19OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGC_GPIO19OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGC_GPIO19OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGC_GPIO19OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGC_GPIO19OUTCFG_Enum; - -/* ============================================ GPIO CFGC GPIO19INCFG [12..12] ============================================= */ -typedef enum { /*!< GPIO_CFGC_GPIO19INCFG */ - GPIO_CFGC_GPIO19INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGC_GPIO19INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGC_GPIO19INCFG_Enum; - -/* ============================================= GPIO CFGC GPIO18INTD [11..11] ============================================= */ -typedef enum { /*!< GPIO_CFGC_GPIO18INTD */ - GPIO_CFGC_GPIO18INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGC_GPIO18INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGC_GPIO18INTD_Enum; - -/* ============================================ GPIO CFGC GPIO18OUTCFG [9..10] ============================================= */ -typedef enum { /*!< GPIO_CFGC_GPIO18OUTCFG */ - GPIO_CFGC_GPIO18OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGC_GPIO18OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGC_GPIO18OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGC_GPIO18OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGC_GPIO18OUTCFG_Enum; - -/* ============================================= GPIO CFGC GPIO18INCFG [8..8] ============================================== */ -typedef enum { /*!< GPIO_CFGC_GPIO18INCFG */ - GPIO_CFGC_GPIO18INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGC_GPIO18INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGC_GPIO18INCFG_Enum; - -/* ============================================== GPIO CFGC GPIO17INTD [7..7] ============================================== */ -typedef enum { /*!< GPIO_CFGC_GPIO17INTD */ - GPIO_CFGC_GPIO17INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGC_GPIO17INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGC_GPIO17INTD_Enum; - -/* ============================================= GPIO CFGC GPIO17OUTCFG [5..6] ============================================= */ -typedef enum { /*!< GPIO_CFGC_GPIO17OUTCFG */ - GPIO_CFGC_GPIO17OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGC_GPIO17OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGC_GPIO17OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGC_GPIO17OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGC_GPIO17OUTCFG_Enum; - -/* ============================================= GPIO CFGC GPIO17INCFG [4..4] ============================================== */ -typedef enum { /*!< GPIO_CFGC_GPIO17INCFG */ - GPIO_CFGC_GPIO17INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGC_GPIO17INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGC_GPIO17INCFG_Enum; - -/* ============================================== GPIO CFGC GPIO16INTD [3..3] ============================================== */ -typedef enum { /*!< GPIO_CFGC_GPIO16INTD */ - GPIO_CFGC_GPIO16INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGC_GPIO16INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGC_GPIO16INTD_Enum; - -/* ============================================= GPIO CFGC GPIO16OUTCFG [1..2] ============================================= */ -typedef enum { /*!< GPIO_CFGC_GPIO16OUTCFG */ - GPIO_CFGC_GPIO16OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGC_GPIO16OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGC_GPIO16OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGC_GPIO16OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGC_GPIO16OUTCFG_Enum; - -/* ============================================= GPIO CFGC GPIO16INCFG [0..0] ============================================== */ -typedef enum { /*!< GPIO_CFGC_GPIO16INCFG */ - GPIO_CFGC_GPIO16INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGC_GPIO16INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGC_GPIO16INCFG_Enum; - -/* ========================================================= CFGD ========================================================== */ -/* ============================================= GPIO CFGD GPIO31INTD [31..31] ============================================= */ -typedef enum { /*!< GPIO_CFGD_GPIO31INTD */ - GPIO_CFGD_GPIO31INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGD_GPIO31INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGD_GPIO31INTD_Enum; - -/* ============================================ GPIO CFGD GPIO31OUTCFG [29..30] ============================================ */ -typedef enum { /*!< GPIO_CFGD_GPIO31OUTCFG */ - GPIO_CFGD_GPIO31OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGD_GPIO31OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGD_GPIO31OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGD_GPIO31OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGD_GPIO31OUTCFG_Enum; - -/* ============================================ GPIO CFGD GPIO31INCFG [28..28] ============================================= */ -typedef enum { /*!< GPIO_CFGD_GPIO31INCFG */ - GPIO_CFGD_GPIO31INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGD_GPIO31INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGD_GPIO31INCFG_Enum; - -/* ============================================= GPIO CFGD GPIO30INTD [27..27] ============================================= */ -typedef enum { /*!< GPIO_CFGD_GPIO30INTD */ - GPIO_CFGD_GPIO30INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGD_GPIO30INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGD_GPIO30INTD_Enum; - -/* ============================================ GPIO CFGD GPIO30OUTCFG [25..26] ============================================ */ -typedef enum { /*!< GPIO_CFGD_GPIO30OUTCFG */ - GPIO_CFGD_GPIO30OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGD_GPIO30OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGD_GPIO30OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGD_GPIO30OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGD_GPIO30OUTCFG_Enum; - -/* ============================================ GPIO CFGD GPIO30INCFG [24..24] ============================================= */ -typedef enum { /*!< GPIO_CFGD_GPIO30INCFG */ - GPIO_CFGD_GPIO30INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGD_GPIO30INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGD_GPIO30INCFG_Enum; - -/* ============================================= GPIO CFGD GPIO29INTD [23..23] ============================================= */ -typedef enum { /*!< GPIO_CFGD_GPIO29INTD */ - GPIO_CFGD_GPIO29INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGD_GPIO29INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGD_GPIO29INTD_Enum; - -/* ============================================ GPIO CFGD GPIO29OUTCFG [21..22] ============================================ */ -typedef enum { /*!< GPIO_CFGD_GPIO29OUTCFG */ - GPIO_CFGD_GPIO29OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGD_GPIO29OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGD_GPIO29OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGD_GPIO29OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGD_GPIO29OUTCFG_Enum; - -/* ============================================ GPIO CFGD GPIO29INCFG [20..20] ============================================= */ -typedef enum { /*!< GPIO_CFGD_GPIO29INCFG */ - GPIO_CFGD_GPIO29INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGD_GPIO29INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGD_GPIO29INCFG_Enum; - -/* ============================================= GPIO CFGD GPIO28INTD [19..19] ============================================= */ -typedef enum { /*!< GPIO_CFGD_GPIO28INTD */ - GPIO_CFGD_GPIO28INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGD_GPIO28INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGD_GPIO28INTD_Enum; - -/* ============================================ GPIO CFGD GPIO28OUTCFG [17..18] ============================================ */ -typedef enum { /*!< GPIO_CFGD_GPIO28OUTCFG */ - GPIO_CFGD_GPIO28OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGD_GPIO28OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGD_GPIO28OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGD_GPIO28OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGD_GPIO28OUTCFG_Enum; - -/* ============================================ GPIO CFGD GPIO28INCFG [16..16] ============================================= */ -typedef enum { /*!< GPIO_CFGD_GPIO28INCFG */ - GPIO_CFGD_GPIO28INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGD_GPIO28INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGD_GPIO28INCFG_Enum; - -/* ============================================= GPIO CFGD GPIO27INTD [15..15] ============================================= */ -typedef enum { /*!< GPIO_CFGD_GPIO27INTD */ - GPIO_CFGD_GPIO27INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGD_GPIO27INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGD_GPIO27INTD_Enum; - -/* ============================================ GPIO CFGD GPIO27OUTCFG [13..14] ============================================ */ -typedef enum { /*!< GPIO_CFGD_GPIO27OUTCFG */ - GPIO_CFGD_GPIO27OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGD_GPIO27OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGD_GPIO27OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGD_GPIO27OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGD_GPIO27OUTCFG_Enum; - -/* ============================================ GPIO CFGD GPIO27INCFG [12..12] ============================================= */ -typedef enum { /*!< GPIO_CFGD_GPIO27INCFG */ - GPIO_CFGD_GPIO27INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGD_GPIO27INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGD_GPIO27INCFG_Enum; - -/* ============================================= GPIO CFGD GPIO26INTD [11..11] ============================================= */ -typedef enum { /*!< GPIO_CFGD_GPIO26INTD */ - GPIO_CFGD_GPIO26INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGD_GPIO26INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGD_GPIO26INTD_Enum; - -/* ============================================ GPIO CFGD GPIO26OUTCFG [9..10] ============================================= */ -typedef enum { /*!< GPIO_CFGD_GPIO26OUTCFG */ - GPIO_CFGD_GPIO26OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGD_GPIO26OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGD_GPIO26OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGD_GPIO26OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGD_GPIO26OUTCFG_Enum; - -/* ============================================= GPIO CFGD GPIO26INCFG [8..8] ============================================== */ -typedef enum { /*!< GPIO_CFGD_GPIO26INCFG */ - GPIO_CFGD_GPIO26INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGD_GPIO26INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGD_GPIO26INCFG_Enum; - -/* ============================================== GPIO CFGD GPIO25INTD [7..7] ============================================== */ -typedef enum { /*!< GPIO_CFGD_GPIO25INTD */ - GPIO_CFGD_GPIO25INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGD_GPIO25INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGD_GPIO25INTD_Enum; - -/* ============================================= GPIO CFGD GPIO25OUTCFG [5..6] ============================================= */ -typedef enum { /*!< GPIO_CFGD_GPIO25OUTCFG */ - GPIO_CFGD_GPIO25OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGD_GPIO25OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGD_GPIO25OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGD_GPIO25OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGD_GPIO25OUTCFG_Enum; - -/* ============================================= GPIO CFGD GPIO25INCFG [4..4] ============================================== */ -typedef enum { /*!< GPIO_CFGD_GPIO25INCFG */ - GPIO_CFGD_GPIO25INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGD_GPIO25INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGD_GPIO25INCFG_Enum; - -/* ============================================== GPIO CFGD GPIO24INTD [3..3] ============================================== */ -typedef enum { /*!< GPIO_CFGD_GPIO24INTD */ - GPIO_CFGD_GPIO24INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGD_GPIO24INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGD_GPIO24INTD_Enum; - -/* ============================================= GPIO CFGD GPIO24OUTCFG [1..2] ============================================= */ -typedef enum { /*!< GPIO_CFGD_GPIO24OUTCFG */ - GPIO_CFGD_GPIO24OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGD_GPIO24OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGD_GPIO24OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGD_GPIO24OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGD_GPIO24OUTCFG_Enum; - -/* ============================================= GPIO CFGD GPIO24INCFG [0..0] ============================================== */ -typedef enum { /*!< GPIO_CFGD_GPIO24INCFG */ - GPIO_CFGD_GPIO24INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGD_GPIO24INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGD_GPIO24INCFG_Enum; - -/* ========================================================= CFGE ========================================================== */ -/* ============================================= GPIO CFGE GPIO39INTD [31..31] ============================================= */ -typedef enum { /*!< GPIO_CFGE_GPIO39INTD */ - GPIO_CFGE_GPIO39INTD_INTDIS = 0, /*!< INTDIS : INCFG = 1 - No interrupt on GPIO transition value. */ - GPIO_CFGE_GPIO39INTD_INTBOTH = 1, /*!< INTBOTH : INCFG = 1 - Interrupt on either low to high or high - to low GPIO transition value. */ -} GPIO_CFGE_GPIO39INTD_Enum; - -/* ============================================ GPIO CFGE GPIO39OUTCFG [29..30] ============================================ */ -typedef enum { /*!< GPIO_CFGE_GPIO39OUTCFG */ - GPIO_CFGE_GPIO39OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGE_GPIO39OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGE_GPIO39OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGE_GPIO39OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGE_GPIO39OUTCFG_Enum; - -/* ============================================ GPIO CFGE GPIO39INCFG [28..28] ============================================= */ -typedef enum { /*!< GPIO_CFGE_GPIO39INCFG */ - GPIO_CFGE_GPIO39INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGE_GPIO39INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGE_GPIO39INCFG_Enum; - -/* ============================================= GPIO CFGE GPIO38INTD [27..27] ============================================= */ -typedef enum { /*!< GPIO_CFGE_GPIO38INTD */ - GPIO_CFGE_GPIO38INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGE_GPIO38INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGE_GPIO38INTD_Enum; - -/* ============================================ GPIO CFGE GPIO38OUTCFG [25..26] ============================================ */ -typedef enum { /*!< GPIO_CFGE_GPIO38OUTCFG */ - GPIO_CFGE_GPIO38OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGE_GPIO38OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGE_GPIO38OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGE_GPIO38OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGE_GPIO38OUTCFG_Enum; - -/* ============================================ GPIO CFGE GPIO38INCFG [24..24] ============================================= */ -typedef enum { /*!< GPIO_CFGE_GPIO38INCFG */ - GPIO_CFGE_GPIO38INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGE_GPIO38INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGE_GPIO38INCFG_Enum; - -/* ============================================= GPIO CFGE GPIO37INTD [23..23] ============================================= */ -typedef enum { /*!< GPIO_CFGE_GPIO37INTD */ - GPIO_CFGE_GPIO37INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGE_GPIO37INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGE_GPIO37INTD_Enum; - -/* ============================================ GPIO CFGE GPIO37OUTCFG [21..22] ============================================ */ -typedef enum { /*!< GPIO_CFGE_GPIO37OUTCFG */ - GPIO_CFGE_GPIO37OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGE_GPIO37OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGE_GPIO37OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGE_GPIO37OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGE_GPIO37OUTCFG_Enum; - -/* ============================================ GPIO CFGE GPIO37INCFG [20..20] ============================================= */ -typedef enum { /*!< GPIO_CFGE_GPIO37INCFG */ - GPIO_CFGE_GPIO37INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGE_GPIO37INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGE_GPIO37INCFG_Enum; - -/* ============================================= GPIO CFGE GPIO36INTD [19..19] ============================================= */ -typedef enum { /*!< GPIO_CFGE_GPIO36INTD */ - GPIO_CFGE_GPIO36INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGE_GPIO36INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGE_GPIO36INTD_Enum; - -/* ============================================ GPIO CFGE GPIO36OUTCFG [17..18] ============================================ */ -typedef enum { /*!< GPIO_CFGE_GPIO36OUTCFG */ - GPIO_CFGE_GPIO36OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGE_GPIO36OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGE_GPIO36OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGE_GPIO36OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGE_GPIO36OUTCFG_Enum; - -/* ============================================ GPIO CFGE GPIO36INCFG [16..16] ============================================= */ -typedef enum { /*!< GPIO_CFGE_GPIO36INCFG */ - GPIO_CFGE_GPIO36INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGE_GPIO36INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGE_GPIO36INCFG_Enum; - -/* ============================================= GPIO CFGE GPIO35INTD [15..15] ============================================= */ -typedef enum { /*!< GPIO_CFGE_GPIO35INTD */ - GPIO_CFGE_GPIO35INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGE_GPIO35INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGE_GPIO35INTD_Enum; - -/* ============================================ GPIO CFGE GPIO35OUTCFG [13..14] ============================================ */ -typedef enum { /*!< GPIO_CFGE_GPIO35OUTCFG */ - GPIO_CFGE_GPIO35OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGE_GPIO35OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGE_GPIO35OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGE_GPIO35OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGE_GPIO35OUTCFG_Enum; - -/* ============================================ GPIO CFGE GPIO35INCFG [12..12] ============================================= */ -typedef enum { /*!< GPIO_CFGE_GPIO35INCFG */ - GPIO_CFGE_GPIO35INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGE_GPIO35INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGE_GPIO35INCFG_Enum; - -/* ============================================= GPIO CFGE GPIO34INTD [11..11] ============================================= */ -typedef enum { /*!< GPIO_CFGE_GPIO34INTD */ - GPIO_CFGE_GPIO34INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGE_GPIO34INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGE_GPIO34INTD_Enum; - -/* ============================================ GPIO CFGE GPIO34OUTCFG [9..10] ============================================= */ -typedef enum { /*!< GPIO_CFGE_GPIO34OUTCFG */ - GPIO_CFGE_GPIO34OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGE_GPIO34OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGE_GPIO34OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGE_GPIO34OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGE_GPIO34OUTCFG_Enum; - -/* ============================================= GPIO CFGE GPIO34INCFG [8..8] ============================================== */ -typedef enum { /*!< GPIO_CFGE_GPIO34INCFG */ - GPIO_CFGE_GPIO34INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGE_GPIO34INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGE_GPIO34INCFG_Enum; - -/* ============================================== GPIO CFGE GPIO33INTD [7..7] ============================================== */ -typedef enum { /*!< GPIO_CFGE_GPIO33INTD */ - GPIO_CFGE_GPIO33INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGE_GPIO33INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGE_GPIO33INTD_Enum; - -/* ============================================= GPIO CFGE GPIO33OUTCFG [5..6] ============================================= */ -typedef enum { /*!< GPIO_CFGE_GPIO33OUTCFG */ - GPIO_CFGE_GPIO33OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGE_GPIO33OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGE_GPIO33OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGE_GPIO33OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGE_GPIO33OUTCFG_Enum; - -/* ============================================= GPIO CFGE GPIO33INCFG [4..4] ============================================== */ -typedef enum { /*!< GPIO_CFGE_GPIO33INCFG */ - GPIO_CFGE_GPIO33INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGE_GPIO33INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGE_GPIO33INCFG_Enum; - -/* ============================================== GPIO CFGE GPIO32INTD [3..3] ============================================== */ -typedef enum { /*!< GPIO_CFGE_GPIO32INTD */ - GPIO_CFGE_GPIO32INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGE_GPIO32INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGE_GPIO32INTD_Enum; - -/* ============================================= GPIO CFGE GPIO32OUTCFG [1..2] ============================================= */ -typedef enum { /*!< GPIO_CFGE_GPIO32OUTCFG */ - GPIO_CFGE_GPIO32OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGE_GPIO32OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGE_GPIO32OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGE_GPIO32OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGE_GPIO32OUTCFG_Enum; - -/* ============================================= GPIO CFGE GPIO32INCFG [0..0] ============================================== */ -typedef enum { /*!< GPIO_CFGE_GPIO32INCFG */ - GPIO_CFGE_GPIO32INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGE_GPIO32INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGE_GPIO32INCFG_Enum; - -/* ========================================================= CFGF ========================================================== */ -/* ============================================= GPIO CFGF GPIO47INTD [31..31] ============================================= */ -typedef enum { /*!< GPIO_CFGF_GPIO47INTD */ - GPIO_CFGF_GPIO47INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGF_GPIO47INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGF_GPIO47INTD_Enum; - -/* ============================================ GPIO CFGF GPIO47OUTCFG [29..30] ============================================ */ -typedef enum { /*!< GPIO_CFGF_GPIO47OUTCFG */ - GPIO_CFGF_GPIO47OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGF_GPIO47OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGF_GPIO47OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGF_GPIO47OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGF_GPIO47OUTCFG_Enum; - -/* ============================================ GPIO CFGF GPIO47INCFG [28..28] ============================================= */ -typedef enum { /*!< GPIO_CFGF_GPIO47INCFG */ - GPIO_CFGF_GPIO47INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGF_GPIO47INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGF_GPIO47INCFG_Enum; - -/* ============================================= GPIO CFGF GPIO46INTD [27..27] ============================================= */ -typedef enum { /*!< GPIO_CFGF_GPIO46INTD */ - GPIO_CFGF_GPIO46INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGF_GPIO46INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGF_GPIO46INTD_Enum; - -/* ============================================ GPIO CFGF GPIO46OUTCFG [25..26] ============================================ */ -typedef enum { /*!< GPIO_CFGF_GPIO46OUTCFG */ - GPIO_CFGF_GPIO46OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGF_GPIO46OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGF_GPIO46OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGF_GPIO46OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGF_GPIO46OUTCFG_Enum; - -/* ============================================ GPIO CFGF GPIO46INCFG [24..24] ============================================= */ -typedef enum { /*!< GPIO_CFGF_GPIO46INCFG */ - GPIO_CFGF_GPIO46INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGF_GPIO46INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGF_GPIO46INCFG_Enum; - -/* ============================================= GPIO CFGF GPIO45INTD [23..23] ============================================= */ -typedef enum { /*!< GPIO_CFGF_GPIO45INTD */ - GPIO_CFGF_GPIO45INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGF_GPIO45INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGF_GPIO45INTD_Enum; - -/* ============================================ GPIO CFGF GPIO45OUTCFG [21..22] ============================================ */ -typedef enum { /*!< GPIO_CFGF_GPIO45OUTCFG */ - GPIO_CFGF_GPIO45OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGF_GPIO45OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGF_GPIO45OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGF_GPIO45OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGF_GPIO45OUTCFG_Enum; - -/* ============================================ GPIO CFGF GPIO45INCFG [20..20] ============================================= */ -typedef enum { /*!< GPIO_CFGF_GPIO45INCFG */ - GPIO_CFGF_GPIO45INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGF_GPIO45INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGF_GPIO45INCFG_Enum; - -/* ============================================= GPIO CFGF GPIO44INTD [19..19] ============================================= */ -typedef enum { /*!< GPIO_CFGF_GPIO44INTD */ - GPIO_CFGF_GPIO44INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGF_GPIO44INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGF_GPIO44INTD_Enum; - -/* ============================================ GPIO CFGF GPIO44OUTCFG [17..18] ============================================ */ -typedef enum { /*!< GPIO_CFGF_GPIO44OUTCFG */ - GPIO_CFGF_GPIO44OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGF_GPIO44OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGF_GPIO44OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGF_GPIO44OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGF_GPIO44OUTCFG_Enum; - -/* ============================================ GPIO CFGF GPIO44INCFG [16..16] ============================================= */ -typedef enum { /*!< GPIO_CFGF_GPIO44INCFG */ - GPIO_CFGF_GPIO44INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGF_GPIO44INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGF_GPIO44INCFG_Enum; - -/* ============================================= GPIO CFGF GPIO43INTD [15..15] ============================================= */ -typedef enum { /*!< GPIO_CFGF_GPIO43INTD */ - GPIO_CFGF_GPIO43INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGF_GPIO43INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGF_GPIO43INTD_Enum; - -/* ============================================ GPIO CFGF GPIO43OUTCFG [13..14] ============================================ */ -typedef enum { /*!< GPIO_CFGF_GPIO43OUTCFG */ - GPIO_CFGF_GPIO43OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGF_GPIO43OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGF_GPIO43OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGF_GPIO43OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGF_GPIO43OUTCFG_Enum; - -/* ============================================ GPIO CFGF GPIO43INCFG [12..12] ============================================= */ -typedef enum { /*!< GPIO_CFGF_GPIO43INCFG */ - GPIO_CFGF_GPIO43INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGF_GPIO43INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGF_GPIO43INCFG_Enum; - -/* ============================================= GPIO CFGF GPIO42INTD [11..11] ============================================= */ -typedef enum { /*!< GPIO_CFGF_GPIO42INTD */ - GPIO_CFGF_GPIO42INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGF_GPIO42INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGF_GPIO42INTD_Enum; - -/* ============================================ GPIO CFGF GPIO42OUTCFG [9..10] ============================================= */ -typedef enum { /*!< GPIO_CFGF_GPIO42OUTCFG */ - GPIO_CFGF_GPIO42OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGF_GPIO42OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGF_GPIO42OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGF_GPIO42OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGF_GPIO42OUTCFG_Enum; - -/* ============================================= GPIO CFGF GPIO42INCFG [8..8] ============================================== */ -typedef enum { /*!< GPIO_CFGF_GPIO42INCFG */ - GPIO_CFGF_GPIO42INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGF_GPIO42INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGF_GPIO42INCFG_Enum; - -/* ============================================== GPIO CFGF GPIO41INTD [7..7] ============================================== */ -typedef enum { /*!< GPIO_CFGF_GPIO41INTD */ - GPIO_CFGF_GPIO41INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x0 - nCE polarity active low value. */ - GPIO_CFGF_GPIO41INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x0 - nCE polarity active high value. */ -} GPIO_CFGF_GPIO41INTD_Enum; - -/* ============================================= GPIO CFGF GPIO41OUTCFG [5..6] ============================================= */ -typedef enum { /*!< GPIO_CFGF_GPIO41OUTCFG */ - GPIO_CFGF_GPIO41OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGF_GPIO41OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGF_GPIO41OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGF_GPIO41OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGF_GPIO41OUTCFG_Enum; - -/* ============================================= GPIO CFGF GPIO41INCFG [4..4] ============================================== */ -typedef enum { /*!< GPIO_CFGF_GPIO41INCFG */ - GPIO_CFGF_GPIO41INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGF_GPIO41INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGF_GPIO41INCFG_Enum; - -/* ============================================== GPIO CFGF GPIO40INTD [3..3] ============================================== */ -typedef enum { /*!< GPIO_CFGF_GPIO40INTD */ - GPIO_CFGF_GPIO40INTD_INTDIS = 0, /*!< INTDIS : INCFG = 1 - No interrupt on GPIO transition value. */ - GPIO_CFGF_GPIO40INTD_INTBOTH = 1, /*!< INTBOTH : INCFG = 1 - Interrupt on either low to high or high - to low GPIO transition value. */ -} GPIO_CFGF_GPIO40INTD_Enum; - -/* ============================================= GPIO CFGF GPIO40OUTCFG [1..2] ============================================= */ -typedef enum { /*!< GPIO_CFGF_GPIO40OUTCFG */ - GPIO_CFGF_GPIO40OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGF_GPIO40OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGF_GPIO40OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGF_GPIO40OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGF_GPIO40OUTCFG_Enum; - -/* ============================================= GPIO CFGF GPIO40INCFG [0..0] ============================================== */ -typedef enum { /*!< GPIO_CFGF_GPIO40INCFG */ - GPIO_CFGF_GPIO40INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGF_GPIO40INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGF_GPIO40INCFG_Enum; - -/* ========================================================= CFGG ========================================================== */ -/* ============================================== GPIO CFGG GPIO49INTD [7..7] ============================================== */ -typedef enum { /*!< GPIO_CFGG_GPIO49INTD */ - GPIO_CFGG_GPIO49INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGG_GPIO49INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGG_GPIO49INTD_Enum; - -/* ============================================= GPIO CFGG GPIO49OUTCFG [5..6] ============================================= */ -typedef enum { /*!< GPIO_CFGG_GPIO49OUTCFG */ - GPIO_CFGG_GPIO49OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGG_GPIO49OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGG_GPIO49OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGG_GPIO49OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGG_GPIO49OUTCFG_Enum; - -/* ============================================= GPIO CFGG GPIO49INCFG [4..4] ============================================== */ -typedef enum { /*!< GPIO_CFGG_GPIO49INCFG */ - GPIO_CFGG_GPIO49INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGG_GPIO49INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGG_GPIO49INCFG_Enum; - -/* ============================================== GPIO CFGG GPIO48INTD [3..3] ============================================== */ -typedef enum { /*!< GPIO_CFGG_GPIO48INTD */ - GPIO_CFGG_GPIO48INTD_nCELOW = 0, /*!< nCELOW : FNCSEL = 0x1 - nCE polarity active low value. */ - GPIO_CFGG_GPIO48INTD_nCEHIGH = 1, /*!< nCEHIGH : FNCSEL = 0x1 - nCE polarity active high value. */ -} GPIO_CFGG_GPIO48INTD_Enum; - -/* ============================================= GPIO CFGG GPIO48OUTCFG [1..2] ============================================= */ -typedef enum { /*!< GPIO_CFGG_GPIO48OUTCFG */ - GPIO_CFGG_GPIO48OUTCFG_DIS = 0, /*!< DIS : FNCSEL = 0x3 - Output disabled value. */ - GPIO_CFGG_GPIO48OUTCFG_PUSHPULL = 1, /*!< PUSHPULL : FNCSEL = 0x3 - Output is push-pull value. */ - GPIO_CFGG_GPIO48OUTCFG_OD = 2, /*!< OD : FNCSEL = 0x3 - Output is open drain value. */ - GPIO_CFGG_GPIO48OUTCFG_TS = 3, /*!< TS : FNCSEL = 0x3 - Output is tri-state value. */ -} GPIO_CFGG_GPIO48OUTCFG_Enum; - -/* ============================================= GPIO CFGG GPIO48INCFG [0..0] ============================================== */ -typedef enum { /*!< GPIO_CFGG_GPIO48INCFG */ - GPIO_CFGG_GPIO48INCFG_READ = 0, /*!< READ : Read the GPIO pin data value. */ - GPIO_CFGG_GPIO48INCFG_RDZERO = 1, /*!< RDZERO : INTD = 0 - Readback will always be zero value. */ -} GPIO_CFGG_GPIO48INCFG_Enum; - -/* ======================================================== PADKEY ========================================================= */ -/* ============================================== GPIO PADKEY PADKEY [0..31] =============================================== */ -typedef enum { /*!< GPIO_PADKEY_PADKEY */ - GPIO_PADKEY_PADKEY_Key = 115, /*!< Key : Key value. */ -} GPIO_PADKEY_PADKEY_Enum; - -/* ========================================================== RDA ========================================================== */ -/* ========================================================== RDB ========================================================== */ -/* ========================================================== WTA ========================================================== */ -/* ========================================================== WTB ========================================================== */ -/* ========================================================= WTSA ========================================================== */ -/* ========================================================= WTSB ========================================================== */ -/* ========================================================= WTCA ========================================================== */ -/* ========================================================= WTCB ========================================================== */ -/* ========================================================== ENA ========================================================== */ -/* ========================================================== ENB ========================================================== */ -/* ========================================================= ENSA ========================================================== */ -/* ========================================================= ENSB ========================================================== */ -/* ========================================================= ENCA ========================================================== */ -/* ========================================================= ENCB ========================================================== */ -/* ======================================================== STMRCAP ======================================================== */ -/* ============================================= GPIO STMRCAP STPOL3 [30..30] ============================================== */ -typedef enum { /*!< GPIO_STMRCAP_STPOL3 */ - GPIO_STMRCAP_STPOL3_CAPLH = 0, /*!< CAPLH : Capture on low to high GPIO transition value. */ - GPIO_STMRCAP_STPOL3_CAPHL = 1, /*!< CAPHL : Capture on high to low GPIO transition value. */ -} GPIO_STMRCAP_STPOL3_Enum; - -/* ============================================= GPIO STMRCAP STPOL2 [22..22] ============================================== */ -typedef enum { /*!< GPIO_STMRCAP_STPOL2 */ - GPIO_STMRCAP_STPOL2_CAPLH = 0, /*!< CAPLH : Capture on low to high GPIO transition value. */ - GPIO_STMRCAP_STPOL2_CAPHL = 1, /*!< CAPHL : Capture on high to low GPIO transition value. */ -} GPIO_STMRCAP_STPOL2_Enum; - -/* ============================================= GPIO STMRCAP STPOL1 [14..14] ============================================== */ -typedef enum { /*!< GPIO_STMRCAP_STPOL1 */ - GPIO_STMRCAP_STPOL1_CAPLH = 0, /*!< CAPLH : Capture on low to high GPIO transition value. */ - GPIO_STMRCAP_STPOL1_CAPHL = 1, /*!< CAPHL : Capture on high to low GPIO transition value. */ -} GPIO_STMRCAP_STPOL1_Enum; - -/* ============================================== GPIO STMRCAP STPOL0 [6..6] =============================================== */ -typedef enum { /*!< GPIO_STMRCAP_STPOL0 */ - GPIO_STMRCAP_STPOL0_CAPLH = 0, /*!< CAPLH : Capture on low to high GPIO transition value. */ - GPIO_STMRCAP_STPOL0_CAPHL = 1, /*!< CAPHL : Capture on high to low GPIO transition value. */ -} GPIO_STMRCAP_STPOL0_Enum; - -/* ======================================================== IOM0IRQ ======================================================== */ -/* ======================================================== IOM1IRQ ======================================================== */ -/* ======================================================== IOM2IRQ ======================================================== */ -/* ======================================================== IOM3IRQ ======================================================== */ -/* ======================================================== IOM4IRQ ======================================================== */ -/* ======================================================== IOM5IRQ ======================================================== */ -/* ======================================================= BLEIFIRQ ======================================================== */ -/* ======================================================== GPIOOBS ======================================================== */ -/* ====================================================== ALTPADCFGA ======================================================= */ -/* =========================================== GPIO ALTPADCFGA PAD3_SR [28..28] ============================================ */ -typedef enum { /*!< GPIO_ALTPADCFGA_PAD3_SR */ - GPIO_ALTPADCFGA_PAD3_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGA_PAD3_SR_Enum; - -/* =========================================== GPIO ALTPADCFGA PAD2_SR [20..20] ============================================ */ -typedef enum { /*!< GPIO_ALTPADCFGA_PAD2_SR */ - GPIO_ALTPADCFGA_PAD2_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGA_PAD2_SR_Enum; - -/* =========================================== GPIO ALTPADCFGA PAD1_SR [12..12] ============================================ */ -typedef enum { /*!< GPIO_ALTPADCFGA_PAD1_SR */ - GPIO_ALTPADCFGA_PAD1_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGA_PAD1_SR_Enum; - -/* ============================================ GPIO ALTPADCFGA PAD0_SR [4..4] ============================================= */ -typedef enum { /*!< GPIO_ALTPADCFGA_PAD0_SR */ - GPIO_ALTPADCFGA_PAD0_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGA_PAD0_SR_Enum; - -/* ====================================================== ALTPADCFGB ======================================================= */ -/* =========================================== GPIO ALTPADCFGB PAD7_SR [28..28] ============================================ */ -typedef enum { /*!< GPIO_ALTPADCFGB_PAD7_SR */ - GPIO_ALTPADCFGB_PAD7_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGB_PAD7_SR_Enum; - -/* =========================================== GPIO ALTPADCFGB PAD6_SR [20..20] ============================================ */ -typedef enum { /*!< GPIO_ALTPADCFGB_PAD6_SR */ - GPIO_ALTPADCFGB_PAD6_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGB_PAD6_SR_Enum; - -/* =========================================== GPIO ALTPADCFGB PAD5_SR [12..12] ============================================ */ -typedef enum { /*!< GPIO_ALTPADCFGB_PAD5_SR */ - GPIO_ALTPADCFGB_PAD5_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGB_PAD5_SR_Enum; - -/* ============================================ GPIO ALTPADCFGB PAD4_SR [4..4] ============================================= */ -typedef enum { /*!< GPIO_ALTPADCFGB_PAD4_SR */ - GPIO_ALTPADCFGB_PAD4_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGB_PAD4_SR_Enum; - -/* ====================================================== ALTPADCFGC ======================================================= */ -/* =========================================== GPIO ALTPADCFGC PAD11_SR [28..28] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGC_PAD11_SR */ - GPIO_ALTPADCFGC_PAD11_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGC_PAD11_SR_Enum; - -/* =========================================== GPIO ALTPADCFGC PAD10_SR [20..20] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGC_PAD10_SR */ - GPIO_ALTPADCFGC_PAD10_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGC_PAD10_SR_Enum; - -/* =========================================== GPIO ALTPADCFGC PAD9_SR [12..12] ============================================ */ -typedef enum { /*!< GPIO_ALTPADCFGC_PAD9_SR */ - GPIO_ALTPADCFGC_PAD9_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGC_PAD9_SR_Enum; - -/* ============================================ GPIO ALTPADCFGC PAD8_SR [4..4] ============================================= */ -typedef enum { /*!< GPIO_ALTPADCFGC_PAD8_SR */ - GPIO_ALTPADCFGC_PAD8_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGC_PAD8_SR_Enum; - -/* ====================================================== ALTPADCFGD ======================================================= */ -/* =========================================== GPIO ALTPADCFGD PAD15_SR [28..28] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGD_PAD15_SR */ - GPIO_ALTPADCFGD_PAD15_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGD_PAD15_SR_Enum; - -/* =========================================== GPIO ALTPADCFGD PAD14_SR [20..20] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGD_PAD14_SR */ - GPIO_ALTPADCFGD_PAD14_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGD_PAD14_SR_Enum; - -/* =========================================== GPIO ALTPADCFGD PAD13_SR [12..12] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGD_PAD13_SR */ - GPIO_ALTPADCFGD_PAD13_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGD_PAD13_SR_Enum; - -/* ============================================ GPIO ALTPADCFGD PAD12_SR [4..4] ============================================ */ -typedef enum { /*!< GPIO_ALTPADCFGD_PAD12_SR */ - GPIO_ALTPADCFGD_PAD12_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGD_PAD12_SR_Enum; - -/* ====================================================== ALTPADCFGE ======================================================= */ -/* =========================================== GPIO ALTPADCFGE PAD19_SR [28..28] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGE_PAD19_SR */ - GPIO_ALTPADCFGE_PAD19_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGE_PAD19_SR_Enum; - -/* =========================================== GPIO ALTPADCFGE PAD18_SR [20..20] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGE_PAD18_SR */ - GPIO_ALTPADCFGE_PAD18_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGE_PAD18_SR_Enum; - -/* =========================================== GPIO ALTPADCFGE PAD17_SR [12..12] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGE_PAD17_SR */ - GPIO_ALTPADCFGE_PAD17_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGE_PAD17_SR_Enum; - -/* ============================================ GPIO ALTPADCFGE PAD16_SR [4..4] ============================================ */ -typedef enum { /*!< GPIO_ALTPADCFGE_PAD16_SR */ - GPIO_ALTPADCFGE_PAD16_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGE_PAD16_SR_Enum; - -/* ====================================================== ALTPADCFGF ======================================================= */ -/* =========================================== GPIO ALTPADCFGF PAD23_SR [28..28] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGF_PAD23_SR */ - GPIO_ALTPADCFGF_PAD23_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGF_PAD23_SR_Enum; - -/* =========================================== GPIO ALTPADCFGF PAD22_SR [20..20] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGF_PAD22_SR */ - GPIO_ALTPADCFGF_PAD22_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGF_PAD22_SR_Enum; - -/* =========================================== GPIO ALTPADCFGF PAD21_SR [12..12] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGF_PAD21_SR */ - GPIO_ALTPADCFGF_PAD21_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGF_PAD21_SR_Enum; - -/* ============================================ GPIO ALTPADCFGF PAD20_SR [4..4] ============================================ */ -typedef enum { /*!< GPIO_ALTPADCFGF_PAD20_SR */ - GPIO_ALTPADCFGF_PAD20_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGF_PAD20_SR_Enum; - -/* ====================================================== ALTPADCFGG ======================================================= */ -/* =========================================== GPIO ALTPADCFGG PAD27_SR [28..28] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGG_PAD27_SR */ - GPIO_ALTPADCFGG_PAD27_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGG_PAD27_SR_Enum; - -/* =========================================== GPIO ALTPADCFGG PAD26_SR [20..20] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGG_PAD26_SR */ - GPIO_ALTPADCFGG_PAD26_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGG_PAD26_SR_Enum; - -/* =========================================== GPIO ALTPADCFGG PAD25_SR [12..12] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGG_PAD25_SR */ - GPIO_ALTPADCFGG_PAD25_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGG_PAD25_SR_Enum; - -/* ============================================ GPIO ALTPADCFGG PAD24_SR [4..4] ============================================ */ -typedef enum { /*!< GPIO_ALTPADCFGG_PAD24_SR */ - GPIO_ALTPADCFGG_PAD24_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGG_PAD24_SR_Enum; - -/* ====================================================== ALTPADCFGH ======================================================= */ -/* =========================================== GPIO ALTPADCFGH PAD31_SR [28..28] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGH_PAD31_SR */ - GPIO_ALTPADCFGH_PAD31_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGH_PAD31_SR_Enum; - -/* =========================================== GPIO ALTPADCFGH PAD30_SR [20..20] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGH_PAD30_SR */ - GPIO_ALTPADCFGH_PAD30_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGH_PAD30_SR_Enum; - -/* =========================================== GPIO ALTPADCFGH PAD29_SR [12..12] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGH_PAD29_SR */ - GPIO_ALTPADCFGH_PAD29_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGH_PAD29_SR_Enum; - -/* ============================================ GPIO ALTPADCFGH PAD28_SR [4..4] ============================================ */ -typedef enum { /*!< GPIO_ALTPADCFGH_PAD28_SR */ - GPIO_ALTPADCFGH_PAD28_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGH_PAD28_SR_Enum; - -/* ====================================================== ALTPADCFGI ======================================================= */ -/* =========================================== GPIO ALTPADCFGI PAD35_SR [28..28] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGI_PAD35_SR */ - GPIO_ALTPADCFGI_PAD35_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGI_PAD35_SR_Enum; - -/* =========================================== GPIO ALTPADCFGI PAD34_SR [20..20] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGI_PAD34_SR */ - GPIO_ALTPADCFGI_PAD34_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGI_PAD34_SR_Enum; - -/* =========================================== GPIO ALTPADCFGI PAD33_SR [12..12] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGI_PAD33_SR */ - GPIO_ALTPADCFGI_PAD33_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGI_PAD33_SR_Enum; - -/* ============================================ GPIO ALTPADCFGI PAD32_SR [4..4] ============================================ */ -typedef enum { /*!< GPIO_ALTPADCFGI_PAD32_SR */ - GPIO_ALTPADCFGI_PAD32_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGI_PAD32_SR_Enum; - -/* ====================================================== ALTPADCFGJ ======================================================= */ -/* =========================================== GPIO ALTPADCFGJ PAD39_SR [28..28] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGJ_PAD39_SR */ - GPIO_ALTPADCFGJ_PAD39_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGJ_PAD39_SR_Enum; - -/* =========================================== GPIO ALTPADCFGJ PAD38_SR [20..20] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGJ_PAD38_SR */ - GPIO_ALTPADCFGJ_PAD38_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGJ_PAD38_SR_Enum; - -/* =========================================== GPIO ALTPADCFGJ PAD37_SR [12..12] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGJ_PAD37_SR */ - GPIO_ALTPADCFGJ_PAD37_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGJ_PAD37_SR_Enum; - -/* ============================================ GPIO ALTPADCFGJ PAD36_SR [4..4] ============================================ */ -typedef enum { /*!< GPIO_ALTPADCFGJ_PAD36_SR */ - GPIO_ALTPADCFGJ_PAD36_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGJ_PAD36_SR_Enum; - -/* ====================================================== ALTPADCFGK ======================================================= */ -/* =========================================== GPIO ALTPADCFGK PAD43_SR [28..28] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGK_PAD43_SR */ - GPIO_ALTPADCFGK_PAD43_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGK_PAD43_SR_Enum; - -/* =========================================== GPIO ALTPADCFGK PAD42_SR [20..20] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGK_PAD42_SR */ - GPIO_ALTPADCFGK_PAD42_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGK_PAD42_SR_Enum; - -/* =========================================== GPIO ALTPADCFGK PAD41_SR [12..12] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGK_PAD41_SR */ - GPIO_ALTPADCFGK_PAD41_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGK_PAD41_SR_Enum; - -/* ============================================ GPIO ALTPADCFGK PAD40_SR [4..4] ============================================ */ -typedef enum { /*!< GPIO_ALTPADCFGK_PAD40_SR */ - GPIO_ALTPADCFGK_PAD40_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGK_PAD40_SR_Enum; - -/* ====================================================== ALTPADCFGL ======================================================= */ -/* =========================================== GPIO ALTPADCFGL PAD47_SR [28..28] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGL_PAD47_SR */ - GPIO_ALTPADCFGL_PAD47_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGL_PAD47_SR_Enum; - -/* =========================================== GPIO ALTPADCFGL PAD46_SR [20..20] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGL_PAD46_SR */ - GPIO_ALTPADCFGL_PAD46_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGL_PAD46_SR_Enum; - -/* =========================================== GPIO ALTPADCFGL PAD45_SR [12..12] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGL_PAD45_SR */ - GPIO_ALTPADCFGL_PAD45_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGL_PAD45_SR_Enum; - -/* ============================================ GPIO ALTPADCFGL PAD44_SR [4..4] ============================================ */ -typedef enum { /*!< GPIO_ALTPADCFGL_PAD44_SR */ - GPIO_ALTPADCFGL_PAD44_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGL_PAD44_SR_Enum; - -/* ====================================================== ALTPADCFGM ======================================================= */ -/* =========================================== GPIO ALTPADCFGM PAD49_SR [12..12] =========================================== */ -typedef enum { /*!< GPIO_ALTPADCFGM_PAD49_SR */ - GPIO_ALTPADCFGM_PAD49_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGM_PAD49_SR_Enum; - -/* ============================================ GPIO ALTPADCFGM PAD48_SR [4..4] ============================================ */ -typedef enum { /*!< GPIO_ALTPADCFGM_PAD48_SR */ - GPIO_ALTPADCFGM_PAD48_SR_SR_EN = 1, /*!< SR_EN : Enables Slew rate control on pad value. */ -} GPIO_ALTPADCFGM_PAD48_SR_Enum; - -/* ========================================================= SCDET ========================================================= */ -/* ======================================================== CTENCFG ======================================================== */ -/* ============================================== GPIO CTENCFG EN31 [31..31] =============================================== */ -typedef enum { /*!< GPIO_CTENCFG_EN31 */ - GPIO_CTENCFG_EN31_DIS = 1, /*!< DIS : Disable CT31 for output value. */ - GPIO_CTENCFG_EN31_EN = 0, /*!< EN : Enable CT31 for output value. */ -} GPIO_CTENCFG_EN31_Enum; - -/* ============================================== GPIO CTENCFG EN30 [30..30] =============================================== */ -typedef enum { /*!< GPIO_CTENCFG_EN30 */ - GPIO_CTENCFG_EN30_DIS = 1, /*!< DIS : Disable CT30 for output value. */ - GPIO_CTENCFG_EN30_EN = 0, /*!< EN : Enable CT30 for output value. */ -} GPIO_CTENCFG_EN30_Enum; - -/* ============================================== GPIO CTENCFG EN29 [29..29] =============================================== */ -typedef enum { /*!< GPIO_CTENCFG_EN29 */ - GPIO_CTENCFG_EN29_DIS = 1, /*!< DIS : Disable CT29 for output value. */ - GPIO_CTENCFG_EN29_EN = 0, /*!< EN : Enable CT29 for output value. */ -} GPIO_CTENCFG_EN29_Enum; - -/* ============================================== GPIO CTENCFG EN28 [28..28] =============================================== */ -typedef enum { /*!< GPIO_CTENCFG_EN28 */ - GPIO_CTENCFG_EN28_DIS = 1, /*!< DIS : Disable CT28 for output value. */ - GPIO_CTENCFG_EN28_EN = 0, /*!< EN : Enable CT28 for output value. */ -} GPIO_CTENCFG_EN28_Enum; - -/* ============================================== GPIO CTENCFG EN27 [27..27] =============================================== */ -typedef enum { /*!< GPIO_CTENCFG_EN27 */ - GPIO_CTENCFG_EN27_DIS = 1, /*!< DIS : Disable CT27 for output value. */ - GPIO_CTENCFG_EN27_EN = 0, /*!< EN : Enable CT27 for output value. */ -} GPIO_CTENCFG_EN27_Enum; - -/* ============================================== GPIO CTENCFG EN26 [26..26] =============================================== */ -typedef enum { /*!< GPIO_CTENCFG_EN26 */ - GPIO_CTENCFG_EN26_DIS = 1, /*!< DIS : Disable CT26 for output value. */ - GPIO_CTENCFG_EN26_EN = 0, /*!< EN : Enable CT26 for output value. */ -} GPIO_CTENCFG_EN26_Enum; - -/* ============================================== GPIO CTENCFG EN25 [25..25] =============================================== */ -typedef enum { /*!< GPIO_CTENCFG_EN25 */ - GPIO_CTENCFG_EN25_DIS = 1, /*!< DIS : Disable CT25 for output value. */ - GPIO_CTENCFG_EN25_EN = 0, /*!< EN : Enable CT25 for output value. */ -} GPIO_CTENCFG_EN25_Enum; - -/* ============================================== GPIO CTENCFG EN24 [24..24] =============================================== */ -typedef enum { /*!< GPIO_CTENCFG_EN24 */ - GPIO_CTENCFG_EN24_DIS = 1, /*!< DIS : Disable CT24 for output value. */ - GPIO_CTENCFG_EN24_EN = 0, /*!< EN : Enable CT24 for output value. */ -} GPIO_CTENCFG_EN24_Enum; - -/* ============================================== GPIO CTENCFG EN23 [23..23] =============================================== */ -typedef enum { /*!< GPIO_CTENCFG_EN23 */ - GPIO_CTENCFG_EN23_DIS = 1, /*!< DIS : Disable CT23 for output value. */ - GPIO_CTENCFG_EN23_EN = 0, /*!< EN : Enable CT23 for output value. */ -} GPIO_CTENCFG_EN23_Enum; - -/* ============================================== GPIO CTENCFG EN22 [22..22] =============================================== */ -typedef enum { /*!< GPIO_CTENCFG_EN22 */ - GPIO_CTENCFG_EN22_DIS = 1, /*!< DIS : Disable CT22 for output value. */ - GPIO_CTENCFG_EN22_EN = 0, /*!< EN : Enable CT22 for output value. */ -} GPIO_CTENCFG_EN22_Enum; - -/* ============================================== GPIO CTENCFG EN21 [21..21] =============================================== */ -typedef enum { /*!< GPIO_CTENCFG_EN21 */ - GPIO_CTENCFG_EN21_DIS = 1, /*!< DIS : Disable CT21 for output value. */ - GPIO_CTENCFG_EN21_EN = 0, /*!< EN : Enable CT21 for output value. */ -} GPIO_CTENCFG_EN21_Enum; - -/* ============================================== GPIO CTENCFG EN20 [20..20] =============================================== */ -typedef enum { /*!< GPIO_CTENCFG_EN20 */ - GPIO_CTENCFG_EN20_DIS = 1, /*!< DIS : Disable CT20 for output value. */ - GPIO_CTENCFG_EN20_EN = 0, /*!< EN : Enable CT20 for output value. */ -} GPIO_CTENCFG_EN20_Enum; - -/* ============================================== GPIO CTENCFG EN19 [19..19] =============================================== */ -typedef enum { /*!< GPIO_CTENCFG_EN19 */ - GPIO_CTENCFG_EN19_DIS = 1, /*!< DIS : Disable CT19 for output value. */ - GPIO_CTENCFG_EN19_EN = 0, /*!< EN : Enable CT19 for output value. */ -} GPIO_CTENCFG_EN19_Enum; - -/* ============================================== GPIO CTENCFG EN18 [18..18] =============================================== */ -typedef enum { /*!< GPIO_CTENCFG_EN18 */ - GPIO_CTENCFG_EN18_DIS = 1, /*!< DIS : Disable CT18 for output value. */ - GPIO_CTENCFG_EN18_EN = 0, /*!< EN : Enable CT18 for output value. */ -} GPIO_CTENCFG_EN18_Enum; - -/* ============================================== GPIO CTENCFG EN17 [17..17] =============================================== */ -typedef enum { /*!< GPIO_CTENCFG_EN17 */ - GPIO_CTENCFG_EN17_DIS = 1, /*!< DIS : Disable CT17 for output value. */ - GPIO_CTENCFG_EN17_EN = 0, /*!< EN : Enable CT17 for output value. */ -} GPIO_CTENCFG_EN17_Enum; - -/* ============================================== GPIO CTENCFG EN16 [16..16] =============================================== */ -typedef enum { /*!< GPIO_CTENCFG_EN16 */ - GPIO_CTENCFG_EN16_DIS = 1, /*!< DIS : Disable CT16 for output value. */ - GPIO_CTENCFG_EN16_EN = 0, /*!< EN : Enable CT16 for output value. */ -} GPIO_CTENCFG_EN16_Enum; - -/* ============================================== GPIO CTENCFG EN15 [15..15] =============================================== */ -typedef enum { /*!< GPIO_CTENCFG_EN15 */ - GPIO_CTENCFG_EN15_DIS = 1, /*!< DIS : Disable CT15 for output value. */ - GPIO_CTENCFG_EN15_EN = 0, /*!< EN : Enable CT15 for output value. */ -} GPIO_CTENCFG_EN15_Enum; - -/* ============================================== GPIO CTENCFG EN14 [14..14] =============================================== */ -typedef enum { /*!< GPIO_CTENCFG_EN14 */ - GPIO_CTENCFG_EN14_DIS = 1, /*!< DIS : Disable CT14 for output value. */ - GPIO_CTENCFG_EN14_EN = 0, /*!< EN : Enable CT14 for output value. */ -} GPIO_CTENCFG_EN14_Enum; - -/* ============================================== GPIO CTENCFG EN13 [13..13] =============================================== */ -typedef enum { /*!< GPIO_CTENCFG_EN13 */ - GPIO_CTENCFG_EN13_DIS = 1, /*!< DIS : Disable CT13 for output value. */ - GPIO_CTENCFG_EN13_EN = 0, /*!< EN : Enable CT13 for output value. */ -} GPIO_CTENCFG_EN13_Enum; - -/* ============================================== GPIO CTENCFG EN12 [12..12] =============================================== */ -typedef enum { /*!< GPIO_CTENCFG_EN12 */ - GPIO_CTENCFG_EN12_DIS = 1, /*!< DIS : Disable CT12 for output value. */ - GPIO_CTENCFG_EN12_EN = 0, /*!< EN : Enable CT12 for output value. */ -} GPIO_CTENCFG_EN12_Enum; - -/* ============================================== GPIO CTENCFG EN11 [11..11] =============================================== */ -typedef enum { /*!< GPIO_CTENCFG_EN11 */ - GPIO_CTENCFG_EN11_DIS = 1, /*!< DIS : Disable CT11 for output value. */ - GPIO_CTENCFG_EN11_EN = 0, /*!< EN : Enable CT11 for output value. */ -} GPIO_CTENCFG_EN11_Enum; - -/* ============================================== GPIO CTENCFG EN10 [10..10] =============================================== */ -typedef enum { /*!< GPIO_CTENCFG_EN10 */ - GPIO_CTENCFG_EN10_DIS = 1, /*!< DIS : Disable CT10 for output value. */ - GPIO_CTENCFG_EN10_EN = 0, /*!< EN : Enable CT10 for output value. */ -} GPIO_CTENCFG_EN10_Enum; - -/* ================================================ GPIO CTENCFG EN9 [9..9] ================================================ */ -typedef enum { /*!< GPIO_CTENCFG_EN9 */ - GPIO_CTENCFG_EN9_DIS = 0, /*!< DIS : Disable CT9 for output value. */ -} GPIO_CTENCFG_EN9_Enum; - -/* ================================================ GPIO CTENCFG EN8 [8..8] ================================================ */ -typedef enum { /*!< GPIO_CTENCFG_EN8 */ - GPIO_CTENCFG_EN8_DIS = 1, /*!< DIS : Disable CT8 for output value. */ - GPIO_CTENCFG_EN8_EN = 0, /*!< EN : Enable CT8 for output value. */ -} GPIO_CTENCFG_EN8_Enum; - -/* ================================================ GPIO CTENCFG EN7 [7..7] ================================================ */ -typedef enum { /*!< GPIO_CTENCFG_EN7 */ - GPIO_CTENCFG_EN7_DIS = 1, /*!< DIS : Disable CT7 for output value. */ - GPIO_CTENCFG_EN7_EN = 0, /*!< EN : Enable CT7 for output value. */ -} GPIO_CTENCFG_EN7_Enum; - -/* ================================================ GPIO CTENCFG EN6 [6..6] ================================================ */ -typedef enum { /*!< GPIO_CTENCFG_EN6 */ - GPIO_CTENCFG_EN6_DIS = 1, /*!< DIS : Disable CT6 for output value. */ - GPIO_CTENCFG_EN6_EN = 0, /*!< EN : Enable CT6 for output value. */ -} GPIO_CTENCFG_EN6_Enum; - -/* ================================================ GPIO CTENCFG EN5 [5..5] ================================================ */ -typedef enum { /*!< GPIO_CTENCFG_EN5 */ - GPIO_CTENCFG_EN5_DIS = 1, /*!< DIS : Disable CT5 for output value. */ - GPIO_CTENCFG_EN5_EN = 0, /*!< EN : Enable CT5 for output value. */ -} GPIO_CTENCFG_EN5_Enum; - -/* ================================================ GPIO CTENCFG EN4 [4..4] ================================================ */ -typedef enum { /*!< GPIO_CTENCFG_EN4 */ - GPIO_CTENCFG_EN4_DIS = 1, /*!< DIS : Disable CT4 for output value. */ - GPIO_CTENCFG_EN4_EN = 0, /*!< EN : Enable CT4 for output value. */ -} GPIO_CTENCFG_EN4_Enum; - -/* ================================================ GPIO CTENCFG EN3 [3..3] ================================================ */ -typedef enum { /*!< GPIO_CTENCFG_EN3 */ - GPIO_CTENCFG_EN3_DIS = 1, /*!< DIS : Disable CT3 for output value. */ - GPIO_CTENCFG_EN3_EN = 0, /*!< EN : Enable CT3 for output value. */ -} GPIO_CTENCFG_EN3_Enum; - -/* ================================================ GPIO CTENCFG EN2 [2..2] ================================================ */ -typedef enum { /*!< GPIO_CTENCFG_EN2 */ - GPIO_CTENCFG_EN2_DIS = 1, /*!< DIS : Disable CT2 for output value. */ - GPIO_CTENCFG_EN2_EN = 0, /*!< EN : Enable CT2 for output value. */ -} GPIO_CTENCFG_EN2_Enum; - -/* ================================================ GPIO CTENCFG EN1 [1..1] ================================================ */ -typedef enum { /*!< GPIO_CTENCFG_EN1 */ - GPIO_CTENCFG_EN1_DIS = 1, /*!< DIS : Disable CT1 for output value. */ - GPIO_CTENCFG_EN1_EN = 0, /*!< EN : Enable CT1 for output value. */ -} GPIO_CTENCFG_EN1_Enum; - -/* ================================================ GPIO CTENCFG EN0 [0..0] ================================================ */ -typedef enum { /*!< GPIO_CTENCFG_EN0 */ - GPIO_CTENCFG_EN0_DIS = 1, /*!< DIS : Disable CT0 for output value. */ - GPIO_CTENCFG_EN0_EN = 0, /*!< EN : Enable CT0 for output value. */ -} GPIO_CTENCFG_EN0_Enum; - -/* ======================================================== INT0EN ========================================================= */ -/* ======================================================= INT0STAT ======================================================== */ -/* ======================================================== INT0CLR ======================================================== */ -/* ======================================================== INT0SET ======================================================== */ -/* ======================================================== INT1EN ========================================================= */ -/* ======================================================= INT1STAT ======================================================== */ -/* ======================================================== INT1CLR ======================================================== */ -/* ======================================================== INT1SET ======================================================== */ - - -/* =========================================================================================================================== */ -/* ================ IOM0 ================ */ -/* =========================================================================================================================== */ - -/* ========================================================= FIFO ========================================================== */ -/* ======================================================== FIFOPTR ======================================================== */ -/* ======================================================== FIFOTHR ======================================================== */ -/* ======================================================== FIFOPOP ======================================================== */ -/* ======================================================= FIFOPUSH ======================================================== */ -/* ======================================================= FIFOCTRL ======================================================== */ -/* ======================================================== FIFOLOC ======================================================== */ -/* ========================================================= INTEN ========================================================= */ -/* ======================================================== INTSTAT ======================================================== */ -/* ======================================================== INTCLR ========================================================= */ -/* ======================================================== INTSET ========================================================= */ -/* ======================================================== CLKCFG ========================================================= */ -/* ============================================== IOM0 CLKCFG DIVEN [12..12] =============================================== */ -typedef enum { /*!< IOM0_CLKCFG_DIVEN */ - IOM0_CLKCFG_DIVEN_DIS = 0, /*!< DIS : Disable TOTPER division. value. */ - IOM0_CLKCFG_DIVEN_EN = 1, /*!< EN : Enable TOTPER division. value. */ -} IOM0_CLKCFG_DIVEN_Enum; - -/* =============================================== IOM0 CLKCFG DIV3 [11..11] =============================================== */ -typedef enum { /*!< IOM0_CLKCFG_DIV3 */ - IOM0_CLKCFG_DIV3_DIS = 0, /*!< DIS : Select divide by 1. value. */ - IOM0_CLKCFG_DIV3_EN = 1, /*!< EN : Select divide by 3. value. */ -} IOM0_CLKCFG_DIV3_Enum; - -/* =============================================== IOM0 CLKCFG FSEL [8..10] ================================================ */ -typedef enum { /*!< IOM0_CLKCFG_FSEL */ - IOM0_CLKCFG_FSEL_MIN_PWR = 0, /*!< MIN_PWR : Selects the minimum power clock. This setting should - be used whenever the IOM is not active. value. */ - IOM0_CLKCFG_FSEL_HFRC = 1, /*!< HFRC : Selects the HFRC as the input clock. value. */ - IOM0_CLKCFG_FSEL_HFRC_DIV2 = 2, /*!< HFRC_DIV2 : Selects the HFRC / 2 as the input clock. value. */ - IOM0_CLKCFG_FSEL_HFRC_DIV4 = 3, /*!< HFRC_DIV4 : Selects the HFRC / 4 as the input clock. value. */ - IOM0_CLKCFG_FSEL_HFRC_DIV8 = 4, /*!< HFRC_DIV8 : Selects the HFRC / 8 as the input clock. value. */ - IOM0_CLKCFG_FSEL_HFRC_DIV16 = 5, /*!< HFRC_DIV16 : Selects the HFRC / 16 as the input clock. value. */ - IOM0_CLKCFG_FSEL_HFRC_DIV32 = 6, /*!< HFRC_DIV32 : Selects the HFRC / 32 as the input clock. value. */ - IOM0_CLKCFG_FSEL_HFRC_DIV64 = 7, /*!< HFRC_DIV64 : Selects the HFRC / 64 as the input clock. value. */ -} IOM0_CLKCFG_FSEL_Enum; - -/* ====================================================== SUBMODCTRL ======================================================= */ -/* =========================================== IOM0 SUBMODCTRL SMOD1TYPE [5..7] ============================================ */ -typedef enum { /*!< IOM0_SUBMODCTRL_SMOD1TYPE */ - IOM0_SUBMODCTRL_SMOD1TYPE_MSPI = 0, /*!< MSPI : SPI Master submodule value. */ - IOM0_SUBMODCTRL_SMOD1TYPE_I2C_MASTER = 1, /*!< I2C_MASTER : MI2C submodule value. */ - IOM0_SUBMODCTRL_SMOD1TYPE_SSPI = 2, /*!< SSPI : SPI Slave submodule value. */ - IOM0_SUBMODCTRL_SMOD1TYPE_SI2C = 3, /*!< SI2C : I2C Slave submodule value. */ - IOM0_SUBMODCTRL_SMOD1TYPE_NA = 7, /*!< NA : NOT INSTALLED value. */ -} IOM0_SUBMODCTRL_SMOD1TYPE_Enum; - -/* =========================================== IOM0 SUBMODCTRL SMOD0TYPE [1..3] ============================================ */ -typedef enum { /*!< IOM0_SUBMODCTRL_SMOD0TYPE */ - IOM0_SUBMODCTRL_SMOD0TYPE_SPI_MASTER = 0, /*!< SPI_MASTER : MSPI submodule value. */ - IOM0_SUBMODCTRL_SMOD0TYPE_I2C_MASTER = 1, /*!< I2C_MASTER : I2C Master submodule value. */ - IOM0_SUBMODCTRL_SMOD0TYPE_SSPI = 2, /*!< SSPI : SPI Slave submodule value. */ - IOM0_SUBMODCTRL_SMOD0TYPE_SI2C = 3, /*!< SI2C : I2C Slave submodule value. */ - IOM0_SUBMODCTRL_SMOD0TYPE_NA = 7, /*!< NA : NOT INSTALLED value. */ -} IOM0_SUBMODCTRL_SMOD0TYPE_Enum; - -/* ========================================================== CMD ========================================================== */ -/* ================================================== IOM0 CMD CMD [0..4] ================================================== */ -typedef enum { /*!< IOM0_CMD_CMD */ - IOM0_CMD_CMD_WRITE = 1, /*!< WRITE : Write command using count of offset bytes specified - in the OFFSETCNT field value. */ - IOM0_CMD_CMD_READ = 2, /*!< READ : Read command using count of offset bytes specified in - the OFFSETCNT field value. */ - IOM0_CMD_CMD_TMW = 3, /*!< TMW : SPI only. Test mode to do constant write operations. Useful - for debug and power measurements. Will continually send - data in OFFSET field value. */ - IOM0_CMD_CMD_TMR = 4, /*!< TMR : SPI Only. Test mode to do constant read operations. Useful - for debug and power measurements. Will continually read - data from external input value. */ -} IOM0_CMD_CMD_Enum; - -/* ======================================================== CMDRPT ========================================================= */ -/* ======================================================= OFFSETHI ======================================================== */ -/* ======================================================== CMDSTAT ======================================================== */ -/* ============================================== IOM0 CMDSTAT CMDSTAT [5..7] ============================================== */ -typedef enum { /*!< IOM0_CMDSTAT_CMDSTAT */ - IOM0_CMDSTAT_CMDSTAT_ERR = 1, /*!< ERR : Error encountered with command value. */ - IOM0_CMDSTAT_CMDSTAT_ACTIVE = 2, /*!< ACTIVE : Actively processing command value. */ - IOM0_CMDSTAT_CMDSTAT_IDLE = 4, /*!< IDLE : Idle state, no active command, no error value. */ - IOM0_CMDSTAT_CMDSTAT_WAIT = 6, /*!< WAIT : Command in progress, but waiting on data from host value. */ -} IOM0_CMDSTAT_CMDSTAT_Enum; - -/* ======================================================= DMATRIGEN ======================================================= */ -/* ====================================================== DMATRIGSTAT ====================================================== */ -/* ======================================================== DMACFG ========================================================= */ -/* ============================================== IOM0 DMACFG DPWROFF [9..9] =============================================== */ -typedef enum { /*!< IOM0_DMACFG_DPWROFF */ - IOM0_DMACFG_DPWROFF_DIS = 0, /*!< DIS : Power off disabled value. */ - IOM0_DMACFG_DPWROFF_EN = 1, /*!< EN : Power off enabled value. */ -} IOM0_DMACFG_DPWROFF_Enum; - -/* =============================================== IOM0 DMACFG DMAPRI [8..8] =============================================== */ -typedef enum { /*!< IOM0_DMACFG_DMAPRI */ - IOM0_DMACFG_DMAPRI_LOW = 0, /*!< LOW : Low Priority (service as best effort) value. */ - IOM0_DMACFG_DMAPRI_HIGH = 1, /*!< HIGH : High Priority (service immediately) value. */ -} IOM0_DMACFG_DMAPRI_Enum; - -/* =============================================== IOM0 DMACFG DMADIR [1..1] =============================================== */ -typedef enum { /*!< IOM0_DMACFG_DMADIR */ - IOM0_DMACFG_DMADIR_P2M = 0, /*!< P2M : Peripheral to Memory (SRAM) transaction. To be set when - doing IOM read operations, ie reading data from external - devices. value. */ - IOM0_DMACFG_DMADIR_M2P = 1, /*!< M2P : Memory to Peripheral transaction. To be set when doing - IOM write operations, ie writing data to external devices. - value. */ -} IOM0_DMACFG_DMADIR_Enum; - -/* =============================================== IOM0 DMACFG DMAEN [0..0] ================================================ */ -typedef enum { /*!< IOM0_DMACFG_DMAEN */ - IOM0_DMACFG_DMAEN_DIS = 0, /*!< DIS : Disable DMA Function value. */ - IOM0_DMACFG_DMAEN_EN = 1, /*!< EN : Enable DMA Function value. */ -} IOM0_DMACFG_DMAEN_Enum; - -/* ====================================================== DMATOTCOUNT ====================================================== */ -/* ====================================================== DMATARGADDR ====================================================== */ -/* ======================================================== DMASTAT ======================================================== */ -/* ========================================================= CQCFG ========================================================= */ -/* ================================================ IOM0 CQCFG CQPRI [1..1] ================================================ */ -typedef enum { /*!< IOM0_CQCFG_CQPRI */ - IOM0_CQCFG_CQPRI_LOW = 0, /*!< LOW : Low Priority (service as best effort) value. */ - IOM0_CQCFG_CQPRI_HIGH = 1, /*!< HIGH : High Priority (service immediately) value. */ -} IOM0_CQCFG_CQPRI_Enum; - -/* ================================================ IOM0 CQCFG CQEN [0..0] ================================================= */ -typedef enum { /*!< IOM0_CQCFG_CQEN */ - IOM0_CQCFG_CQEN_DIS = 0, /*!< DIS : Disable CQ Function value. */ - IOM0_CQCFG_CQEN_EN = 1, /*!< EN : Enable CQ Function value. */ -} IOM0_CQCFG_CQEN_Enum; - -/* ======================================================== CQADDR ========================================================= */ -/* ======================================================== CQSTAT ========================================================= */ -/* ======================================================== CQFLAGS ======================================================== */ -/* ====================================================== CQSETCLEAR ======================================================= */ -/* ======================================================= CQPAUSEEN ======================================================= */ -/* ============================================= IOM0 CQPAUSEEN CQPEN [0..15] ============================================== */ -typedef enum { /*!< IOM0_CQPAUSEEN_CQPEN */ - IOM0_CQPAUSEEN_CQPEN_IDXEQ = 32768, /*!< IDXEQ : Pauses the command queue when the current index matches - the last index value. */ - IOM0_CQPAUSEEN_CQPEN_BLEXOREN = 16384, /*!< BLEXOREN : Pause command queue when input BLE bit XORed with - SWFLAG4 is '1' value. */ - IOM0_CQPAUSEEN_CQPEN_IOMXOREN = 8192, /*!< IOMXOREN : Pause command queue when input IOM bit XORed with - SWFLAG3 is '1' value. */ - IOM0_CQPAUSEEN_CQPEN_GPIOXOREN = 4096, /*!< GPIOXOREN : Pause command queue when input GPIO irq_bit XORed - with SWFLAG2 is '1' value. */ - IOM0_CQPAUSEEN_CQPEN_MSPI1XNOREN = 2048, /*!< MSPI1XNOREN : Pause command queue when input MSPI1 bit XNORed - with SWFLAG1 is '1' value. */ - IOM0_CQPAUSEEN_CQPEN_MSPI0XNOREN = 1024, /*!< MSPI0XNOREN : Pause command queue when input MSPI0 bit XNORed - with SWFLAG0 is '1' value. */ - IOM0_CQPAUSEEN_CQPEN_MSPI1XOREN = 512, /*!< MSPI1XOREN : Pause command queue when input MSPI1 bit XORed - with SWFLAG1 is '1' value. */ - IOM0_CQPAUSEEN_CQPEN_MSPI0XOREN = 256, /*!< MSPI0XOREN : Pause command queue when input MSPI0 bit XORed - with SWFLAG0 is '1' value. */ - IOM0_CQPAUSEEN_CQPEN_SWFLAGEN7 = 128, /*!< SWFLAGEN7 : Pause the command queue when software flag bit 7 - is '1'. value. */ - IOM0_CQPAUSEEN_CQPEN_SWFLAGEN6 = 64, /*!< SWFLAGEN6 : Pause the command queue when software flag bit 6 - is '1' value. */ - IOM0_CQPAUSEEN_CQPEN_SWFLAGEN5 = 32, /*!< SWFLAGEN5 : Pause the command queue when software flag bit 5 - is '1' value. */ - IOM0_CQPAUSEEN_CQPEN_SWFLAGEN4 = 16, /*!< SWFLAGEN4 : Pause the command queue when software flag bit 4 - is '1' value. */ - IOM0_CQPAUSEEN_CQPEN_SWFLAGEN3 = 8, /*!< SWFLAGEN3 : Pause the command queue when software flag bit 3 - is '1' value. */ - IOM0_CQPAUSEEN_CQPEN_SWFLAGEN2 = 4, /*!< SWFLAGEN2 : Pause the command queue when software flag bit 2 - is '1' value. */ - IOM0_CQPAUSEEN_CQPEN_SWFLAGEN1 = 2, /*!< SWFLAGEN1 : Pause the command queue when software flag bit 1 - is '1' value. */ - IOM0_CQPAUSEEN_CQPEN_SWFLAGEN0 = 1, /*!< SWFLAGEN0 : Pause the command queue when software flag bit 0 - is '1' value. */ -} IOM0_CQPAUSEEN_CQPEN_Enum; - -/* ======================================================= CQCURIDX ======================================================== */ -/* ======================================================= CQENDIDX ======================================================== */ -/* ======================================================== STATUS ========================================================= */ -/* =============================================== IOM0 STATUS IDLEST [2..2] =============================================== */ -typedef enum { /*!< IOM0_STATUS_IDLEST */ - IOM0_STATUS_IDLEST_IDLE = 1, /*!< IDLE : The I/O state machine is in the idle state. value. */ -} IOM0_STATUS_IDLEST_Enum; - -/* =============================================== IOM0 STATUS CMDACT [1..1] =============================================== */ -typedef enum { /*!< IOM0_STATUS_CMDACT */ - IOM0_STATUS_CMDACT_ACTIVE = 1, /*!< ACTIVE : An I/O command is active. Indicates the active module - has an active command and is processing this. De-asserted - when the command is completed. value. */ -} IOM0_STATUS_CMDACT_Enum; - -/* ================================================ IOM0 STATUS ERR [0..0] ================================================= */ -typedef enum { /*!< IOM0_STATUS_ERR */ - IOM0_STATUS_ERR_ERROR = 1, /*!< ERROR : Bit has been deprecated and will always return 0. value. */ -} IOM0_STATUS_ERR_Enum; - -/* ======================================================== MSPICFG ======================================================== */ -/* ============================================= IOM0 MSPICFG SPILSB [23..23] ============================================== */ -typedef enum { /*!< IOM0_MSPICFG_SPILSB */ - IOM0_MSPICFG_SPILSB_MSB = 0, /*!< MSB : Send and receive MSB bit first value. */ - IOM0_MSPICFG_SPILSB_LSB = 1, /*!< LSB : Send and receive LSB bit first value. */ -} IOM0_MSPICFG_SPILSB_Enum; - -/* ============================================= IOM0 MSPICFG RDFCPOL [22..22] ============================================= */ -typedef enum { /*!< IOM0_MSPICFG_RDFCPOL */ - IOM0_MSPICFG_RDFCPOL_HIGH = 0, /*!< HIGH : Flow control signal high creates flow control. value. */ - IOM0_MSPICFG_RDFCPOL_LOW = 1, /*!< LOW : Flow control signal low creates flow control. value. */ -} IOM0_MSPICFG_RDFCPOL_Enum; - -/* ============================================= IOM0 MSPICFG WTFCPOL [21..21] ============================================= */ -typedef enum { /*!< IOM0_MSPICFG_WTFCPOL */ - IOM0_MSPICFG_WTFCPOL_HIGH = 0, /*!< HIGH : Flow control signal high(1) creates flow control and - byte transfers will stop until the flow control signal - goes low. value. */ - IOM0_MSPICFG_WTFCPOL_LOW = 1, /*!< LOW : Flow control signal low(0) creates flow control and byte - transfers will stop until the flow control signal goes - high(1). value. */ -} IOM0_MSPICFG_WTFCPOL_Enum; - -/* ============================================= IOM0 MSPICFG WTFCIRQ [20..20] ============================================= */ -typedef enum { /*!< IOM0_MSPICFG_WTFCIRQ */ - IOM0_MSPICFG_WTFCIRQ_MISO = 0, /*!< MISO : MISO is used as the write mode flow control signal. value. */ - IOM0_MSPICFG_WTFCIRQ_IRQ = 1, /*!< IRQ : IRQ is used as the write mode flow control signal. value. */ -} IOM0_MSPICFG_WTFCIRQ_Enum; - -/* ============================================= IOM0 MSPICFG MOSIINV [18..18] ============================================= */ -typedef enum { /*!< IOM0_MSPICFG_MOSIINV */ - IOM0_MSPICFG_MOSIINV_NORMAL = 0, /*!< NORMAL : MOSI is set to 0 in read mode and 1 in write mode. - value. */ - IOM0_MSPICFG_MOSIINV_INVERT = 1, /*!< INVERT : MOSI is set to 1 in read mode and 0 in write mode. - value. */ -} IOM0_MSPICFG_MOSIINV_Enum; - -/* ============================================== IOM0 MSPICFG RDFC [17..17] =============================================== */ -typedef enum { /*!< IOM0_MSPICFG_RDFC */ - IOM0_MSPICFG_RDFC_DIS = 0, /*!< DIS : Read mode flow control disabled. value. */ - IOM0_MSPICFG_RDFC_EN = 1, /*!< EN : Read mode flow control enabled. value. */ -} IOM0_MSPICFG_RDFC_Enum; - -/* ============================================== IOM0 MSPICFG WTFC [16..16] =============================================== */ -typedef enum { /*!< IOM0_MSPICFG_WTFC */ - IOM0_MSPICFG_WTFC_DIS = 0, /*!< DIS : Write mode flow control disabled. value. */ - IOM0_MSPICFG_WTFC_EN = 1, /*!< EN : Write mode flow control enabled. value. */ -} IOM0_MSPICFG_WTFC_Enum; - -/* =============================================== IOM0 MSPICFG SPHA [1..1] ================================================ */ -typedef enum { /*!< IOM0_MSPICFG_SPHA */ - IOM0_MSPICFG_SPHA_SAMPLE_LEADING_EDGE = 0, /*!< SAMPLE_LEADING_EDGE : Sample on the leading (first) clock edge. - value. */ - IOM0_MSPICFG_SPHA_SAMPLE_TRAILING_EDGE = 1, /*!< SAMPLE_TRAILING_EDGE : Sample on the trailing (second) clock - edge. value. */ -} IOM0_MSPICFG_SPHA_Enum; - -/* =============================================== IOM0 MSPICFG SPOL [0..0] ================================================ */ -typedef enum { /*!< IOM0_MSPICFG_SPOL */ - IOM0_MSPICFG_SPOL_CLK_BASE_0 = 0, /*!< CLK_BASE_0 : The base value of the clock is 0. value. */ - IOM0_MSPICFG_SPOL_CLK_BASE_1 = 1, /*!< CLK_BASE_1 : The base value of the clock is 1. value. */ -} IOM0_MSPICFG_SPOL_Enum; - -/* ======================================================== MI2CCFG ======================================================== */ -/* =============================================== IOM0 MI2CCFG ARBEN [2..2] =============================================== */ -typedef enum { /*!< IOM0_MI2CCFG_ARBEN */ - IOM0_MI2CCFG_ARBEN_ARBEN = 1, /*!< ARBEN : Enable multi-master bus arbitration support for this - i2c master value. */ - IOM0_MI2CCFG_ARBEN_ARBDIS = 0, /*!< ARBDIS : Disable multi-master bus arbitration support for this - i2c master value. */ -} IOM0_MI2CCFG_ARBEN_Enum; - -/* ============================================== IOM0 MI2CCFG I2CLSB [1..1] =============================================== */ -typedef enum { /*!< IOM0_MI2CCFG_I2CLSB */ - IOM0_MI2CCFG_I2CLSB_MSBFIRST = 0, /*!< MSBFIRST : Byte data is transmitted MSB first onto the bus/read - from the bus value. */ - IOM0_MI2CCFG_I2CLSB_LSBFIRST = 1, /*!< LSBFIRST : Byte data is transmitted LSB first onto the bus/read - from the bus value. */ -} IOM0_MI2CCFG_I2CLSB_Enum; - -/* ============================================== IOM0 MI2CCFG ADDRSZ [0..0] =============================================== */ -typedef enum { /*!< IOM0_MI2CCFG_ADDRSZ */ - IOM0_MI2CCFG_ADDRSZ_ADDRSZ7 = 0, /*!< ADDRSZ7 : Use 7b addressing for I2C master transactions value. */ - IOM0_MI2CCFG_ADDRSZ_ADDRSZ10 = 1, /*!< ADDRSZ10 : Use 10b addressing for I2C master transactions value. */ -} IOM0_MI2CCFG_ADDRSZ_Enum; - -/* ======================================================== DEVCFG ========================================================= */ -/* ======================================================== IOMDBG ========================================================= */ - - -/* =========================================================================================================================== */ -/* ================ IOSLAVE ================ */ -/* =========================================================================================================================== */ - -/* ======================================================== FIFOPTR ======================================================== */ -/* ======================================================== FIFOCFG ======================================================== */ -/* ======================================================== FIFOTHR ======================================================== */ -/* ========================================================= FUPD ========================================================== */ -/* ======================================================== FIFOCTR ======================================================== */ -/* ======================================================== FIFOINC ======================================================== */ -/* ========================================================== CFG ========================================================== */ -/* ============================================== IOSLAVE CFG IFCEN [31..31] =============================================== */ -typedef enum { /*!< IOSLAVE_CFG_IFCEN */ - IOSLAVE_CFG_IFCEN_DIS = 0, /*!< DIS : Disable the IOSLAVE value. */ - IOSLAVE_CFG_IFCEN_EN = 1, /*!< EN : Enable the IOSLAVE value. */ -} IOSLAVE_CFG_IFCEN_Enum; - -/* ============================================== IOSLAVE CFG STARTRD [4..4] =============================================== */ -typedef enum { /*!< IOSLAVE_CFG_STARTRD */ - IOSLAVE_CFG_STARTRD_LATE = 0, /*!< LATE : Initiate I/O RAM read late in each transferred byte. - value. */ - IOSLAVE_CFG_STARTRD_EARLY = 1, /*!< EARLY : Initiate I/O RAM read early in each transferred byte. - value. */ -} IOSLAVE_CFG_STARTRD_Enum; - -/* ================================================ IOSLAVE CFG LSB [2..2] ================================================= */ -typedef enum { /*!< IOSLAVE_CFG_LSB */ - IOSLAVE_CFG_LSB_MSB_FIRST = 0, /*!< MSB_FIRST : Data is assumed to be sent and received with MSB - first. value. */ - IOSLAVE_CFG_LSB_LSB_FIRST = 1, /*!< LSB_FIRST : Data is assumed to be sent and received with LSB - first. value. */ -} IOSLAVE_CFG_LSB_Enum; - -/* ================================================ IOSLAVE CFG SPOL [1..1] ================================================ */ -typedef enum { /*!< IOSLAVE_CFG_SPOL */ - IOSLAVE_CFG_SPOL_SPI_MODES_0_3 = 0, /*!< SPI_MODES_0_3 : Polarity 0, handles SPI modes 0 and 3. value. */ - IOSLAVE_CFG_SPOL_SPI_MODES_1_2 = 1, /*!< SPI_MODES_1_2 : Polarity 1, handles SPI modes 1 and 2. value. */ -} IOSLAVE_CFG_SPOL_Enum; - -/* =============================================== IOSLAVE CFG IFCSEL [0..0] =============================================== */ -typedef enum { /*!< IOSLAVE_CFG_IFCSEL */ - IOSLAVE_CFG_IFCSEL_I2C = 0, /*!< I2C : Selects I2C interface for the IO Slave. value. */ - IOSLAVE_CFG_IFCSEL_SPI = 1, /*!< SPI : Selects SPI interface for the IO Slave. value. */ -} IOSLAVE_CFG_IFCSEL_Enum; - -/* ========================================================= PRENC ========================================================= */ -/* ======================================================= IOINTCTL ======================================================== */ -/* ======================================================== GENADD ========================================================= */ -/* ========================================================= INTEN ========================================================= */ -/* ======================================================== INTSTAT ======================================================== */ -/* ======================================================== INTCLR ========================================================= */ -/* ======================================================== INTSET ========================================================= */ -/* ====================================================== REGACCINTEN ====================================================== */ -/* ===================================================== REGACCINTSTAT ===================================================== */ -/* ===================================================== REGACCINTCLR ====================================================== */ -/* ===================================================== REGACCINTSET ====================================================== */ - - -/* =========================================================================================================================== */ -/* ================ MCUCTRL ================ */ -/* =========================================================================================================================== */ - -/* ======================================================== CHIPPN ========================================================= */ -/* ============================================ MCUCTRL CHIPPN PARTNUM [0..31] ============================================= */ -typedef enum { /*!< MCUCTRL_CHIPPN_PARTNUM */ - MCUCTRL_CHIPPN_PARTNUM_APOLLO3 = 100663296,/*!< APOLLO3 : Apollo3 part number is 0x06xxxxxx. value. */ - MCUCTRL_CHIPPN_PARTNUM_APOLLO2 = 50331648,/*!< APOLLO2 : Apollo2 part number is 0x03xxxxxx. value. */ - MCUCTRL_CHIPPN_PARTNUM_APOLLO = 16777216,/*!< APOLLO : Apollo part number is 0x01xxxxxx. value. */ - MCUCTRL_CHIPPN_PARTNUM_PN_M = -16777216,/*!< PN_M : Mask for the part number field. value. */ - MCUCTRL_CHIPPN_PARTNUM_PN_S = 24, /*!< PN_S : Bit position for the part number field. value. */ - MCUCTRL_CHIPPN_PARTNUM_FLASHSIZE_M = 15728640,/*!< FLASHSIZE_M : Mask for the FLASH_SIZE field.Values:0: 16KB1: - 32KB2: 64KB3: 128KB4: 256KB5: 512KB6: 1MB7: 2MB value. */ - MCUCTRL_CHIPPN_PARTNUM_FLASHSIZE_S = 20, /*!< FLASHSIZE_S : Bit position for the FLASH_SIZE field. value. */ - MCUCTRL_CHIPPN_PARTNUM_SRAMSIZE_M = 983040,/*!< SRAMSIZE_M : Mask for the SRAM_SIZE field.Values:0: 16KB1: 32KB2: - 64KB3: 128KB4: 256KB5: 512KB6: 1MB7: 384KB value. */ - MCUCTRL_CHIPPN_PARTNUM_SRAMSIZE_S = 16, /*!< SRAMSIZE_S : Bit position for the SRAM_SIZE field. value. */ - MCUCTRL_CHIPPN_PARTNUM_REV_M = 65280, /*!< REV_M : Mask for the revision field. Bits [15:12] are major - rev, [11:8] are minor rev.Values:0: Major Rev A, Minor - Rev 01: Major Rev B, Minor Rev 1 value. */ - MCUCTRL_CHIPPN_PARTNUM_REV_S = 8, /*!< REV_S : Bit position for the revision field. value. */ - MCUCTRL_CHIPPN_PARTNUM_PKG_M = 192, /*!< PKG_M : Mask for the package field.Values:0: SIP1: QFN2: BGA3: - CSP value. */ - MCUCTRL_CHIPPN_PARTNUM_PKG_S = 6, /*!< PKG_S : Bit position for the package field. value. */ - MCUCTRL_CHIPPN_PARTNUM_PINS_M = 56, /*!< PINS_M : Mask for the pins field.Values:0: 25 pins1: 49 pins2: - 64 pins3: 81 pins value. */ - MCUCTRL_CHIPPN_PARTNUM_PINS_S = 3, /*!< PINS_S : Bit position for the pins field. value. */ - MCUCTRL_CHIPPN_PARTNUM_TEMP_S = 1, /*!< TEMP_S : Bit position for the temperature field. value. */ - MCUCTRL_CHIPPN_PARTNUM_QUAL_S = 0, /*!< QUAL_S : Bit position for the qualified field. value. */ -} MCUCTRL_CHIPPN_PARTNUM_Enum; - -/* ======================================================== CHIPID0 ======================================================== */ -/* ============================================ MCUCTRL CHIPID0 CHIPID0 [0..31] ============================================ */ -typedef enum { /*!< MCUCTRL_CHIPID0_CHIPID0 */ - MCUCTRL_CHIPID0_CHIPID0_APOLLO3 = 0, /*!< APOLLO3 : Apollo3 CHIPID0. value. */ -} MCUCTRL_CHIPID0_CHIPID0_Enum; - -/* ======================================================== CHIPID1 ======================================================== */ -/* ============================================ MCUCTRL CHIPID1 CHIPID1 [0..31] ============================================ */ -typedef enum { /*!< MCUCTRL_CHIPID1_CHIPID1 */ - MCUCTRL_CHIPID1_CHIPID1_APOLLO3 = 0, /*!< APOLLO3 : Apollo3 CHIPID1. value. */ -} MCUCTRL_CHIPID1_CHIPID1_Enum; - -/* ======================================================== CHIPREV ======================================================== */ -/* ============================================= MCUCTRL CHIPREV REVMAJ [4..7] ============================================= */ -typedef enum { /*!< MCUCTRL_CHIPREV_REVMAJ */ - MCUCTRL_CHIPREV_REVMAJ_A = 1, /*!< A : Apollo3 revision A value. */ -} MCUCTRL_CHIPREV_REVMAJ_Enum; - -/* ============================================= MCUCTRL CHIPREV REVMIN [0..3] ============================================= */ -typedef enum { /*!< MCUCTRL_CHIPREV_REVMIN */ - MCUCTRL_CHIPREV_REVMIN_REV1 = 2, /*!< REV1 : Apollo3 minor rev 1. value. */ - MCUCTRL_CHIPREV_REVMIN_REV0 = 1, /*!< REV0 : Apollo3 minor rev 0. Minor revision value, succeeding - minor revisions will increment from this value. value. */ -} MCUCTRL_CHIPREV_REVMIN_Enum; - -/* ======================================================= VENDORID ======================================================== */ -/* =========================================== MCUCTRL VENDORID VENDORID [0..31] =========================================== */ -typedef enum { /*!< MCUCTRL_VENDORID_VENDORID */ - MCUCTRL_VENDORID_VENDORID_AMBIQ = 1095582289,/*!< AMBIQ : Ambiq Vendor ID value. */ -} MCUCTRL_VENDORID_VENDORID_Enum; - -/* ========================================================== SKU ========================================================== */ -/* ===================================================== FEATUREENABLE ===================================================== */ -/* ======================================== MCUCTRL FEATUREENABLE BURSTAVAIL [6..6] ======================================== */ -typedef enum { /*!< MCUCTRL_FEATUREENABLE_BURSTAVAIL */ - MCUCTRL_FEATUREENABLE_BURSTAVAIL_AVAIL = 1, /*!< AVAIL : Burst functionality available value. */ - MCUCTRL_FEATUREENABLE_BURSTAVAIL_NOTAVAIL = 0,/*!< NOTAVAIL : Burst functionality not available value. */ -} MCUCTRL_FEATUREENABLE_BURSTAVAIL_Enum; - -/* ========================================= MCUCTRL FEATUREENABLE BURSTREQ [4..4] ========================================= */ -typedef enum { /*!< MCUCTRL_FEATUREENABLE_BURSTREQ */ - MCUCTRL_FEATUREENABLE_BURSTREQ_EN = 1, /*!< EN : Enable the Burst functionality value. */ - MCUCTRL_FEATUREENABLE_BURSTREQ_DIS = 0, /*!< DIS : Disable the Burst functionality value. */ -} MCUCTRL_FEATUREENABLE_BURSTREQ_Enum; - -/* ========================================= MCUCTRL FEATUREENABLE BLEAVAIL [2..2] ========================================= */ -typedef enum { /*!< MCUCTRL_FEATUREENABLE_BLEAVAIL */ - MCUCTRL_FEATUREENABLE_BLEAVAIL_AVAIL = 1, /*!< AVAIL : BLE functionality available value. */ - MCUCTRL_FEATUREENABLE_BLEAVAIL_NOTAVAIL = 0, /*!< NOTAVAIL : BLE functionality not available value. */ -} MCUCTRL_FEATUREENABLE_BLEAVAIL_Enum; - -/* ========================================== MCUCTRL FEATUREENABLE BLEREQ [0..0] ========================================== */ -typedef enum { /*!< MCUCTRL_FEATUREENABLE_BLEREQ */ - MCUCTRL_FEATUREENABLE_BLEREQ_EN = 1, /*!< EN : Enable the BLE functionality value. */ - MCUCTRL_FEATUREENABLE_BLEREQ_DIS = 0, /*!< DIS : Disable the BLE functionality value. */ -} MCUCTRL_FEATUREENABLE_BLEREQ_Enum; - -/* ======================================================= DEBUGGER ======================================================== */ -/* ======================================================== BODCTRL ======================================================== */ -/* ======================================================= ADCPWRDLY ======================================================= */ -/* ======================================================== ADCCAL ========================================================= */ -/* ========================================== MCUCTRL ADCCAL ADCCALIBRATED [1..1] ========================================== */ -typedef enum { /*!< MCUCTRL_ADCCAL_ADCCALIBRATED */ - MCUCTRL_ADCCAL_ADCCALIBRATED_FALSE = 0, /*!< FALSE : ADC is not calibrated value. */ - MCUCTRL_ADCCAL_ADCCALIBRATED_TRUE = 1, /*!< TRUE : ADC is calibrated value. */ -} MCUCTRL_ADCCAL_ADCCALIBRATED_Enum; - -/* =========================================== MCUCTRL ADCCAL CALONPWRUP [0..0] ============================================ */ -typedef enum { /*!< MCUCTRL_ADCCAL_CALONPWRUP */ - MCUCTRL_ADCCAL_CALONPWRUP_DIS = 0, /*!< DIS : Disable automatic calibration on initial power up value. */ - MCUCTRL_ADCCAL_CALONPWRUP_EN = 1, /*!< EN : Enable automatic calibration on initial power up value. */ -} MCUCTRL_ADCCAL_CALONPWRUP_Enum; - -/* ====================================================== ADCBATTLOAD ====================================================== */ -/* ========================================== MCUCTRL ADCBATTLOAD BATTLOAD [0..0] ========================================== */ -typedef enum { /*!< MCUCTRL_ADCBATTLOAD_BATTLOAD */ - MCUCTRL_ADCBATTLOAD_BATTLOAD_DIS = 0, /*!< DIS : Battery load is disconnected value. */ - MCUCTRL_ADCBATTLOAD_BATTLOAD_EN = 1, /*!< EN : Battery load is enabled value. */ -} MCUCTRL_ADCBATTLOAD_BATTLOAD_Enum; - -/* ======================================================== ADCTRIM ======================================================== */ -/* ====================================================== ADCREFCOMP ======================================================= */ -/* ======================================================= XTALCTRL ======================================================== */ -/* ========================================== MCUCTRL XTALCTRL PWDBODXTAL [5..5] =========================================== */ -typedef enum { /*!< MCUCTRL_XTALCTRL_PWDBODXTAL */ - MCUCTRL_XTALCTRL_PWDBODXTAL_PWRUPBOD = 0, /*!< PWRUPBOD : Power up xtal on BOD value. */ - MCUCTRL_XTALCTRL_PWDBODXTAL_PWRDNBOD = 1, /*!< PWRDNBOD : Power down XTAL on BOD. value. */ -} MCUCTRL_XTALCTRL_PWDBODXTAL_Enum; - -/* ========================================= MCUCTRL XTALCTRL PDNBCMPRXTAL [4..4] ========================================== */ -typedef enum { /*!< MCUCTRL_XTALCTRL_PDNBCMPRXTAL */ - MCUCTRL_XTALCTRL_PDNBCMPRXTAL_PWRUPCOMP = 1, /*!< PWRUPCOMP : Power up XTAL oscillator comparator. value. */ - MCUCTRL_XTALCTRL_PDNBCMPRXTAL_PWRDNCOMP = 0, /*!< PWRDNCOMP : Power down XTAL oscillator comparator. value. */ -} MCUCTRL_XTALCTRL_PDNBCMPRXTAL_Enum; - -/* ========================================= MCUCTRL XTALCTRL PDNBCOREXTAL [3..3] ========================================== */ -typedef enum { /*!< MCUCTRL_XTALCTRL_PDNBCOREXTAL */ - MCUCTRL_XTALCTRL_PDNBCOREXTAL_PWRUPCORE = 1, /*!< PWRUPCORE : Power up XTAL oscillator core. value. */ - MCUCTRL_XTALCTRL_PDNBCOREXTAL_PWRDNCORE = 0, /*!< PWRDNCORE : Power down XTAL oscillator core. value. */ -} MCUCTRL_XTALCTRL_PDNBCOREXTAL_Enum; - -/* ========================================== MCUCTRL XTALCTRL BYPCMPRXTAL [2..2] ========================================== */ -typedef enum { /*!< MCUCTRL_XTALCTRL_BYPCMPRXTAL */ - MCUCTRL_XTALCTRL_BYPCMPRXTAL_USECOMP = 0, /*!< USECOMP : Use the XTAL oscillator comparator. value. */ - MCUCTRL_XTALCTRL_BYPCMPRXTAL_BYPCOMP = 1, /*!< BYPCOMP : Bypass the XTAL oscillator comparator. value. */ -} MCUCTRL_XTALCTRL_BYPCMPRXTAL_Enum; - -/* ========================================= MCUCTRL XTALCTRL FDBKDSBLXTAL [1..1] ========================================== */ -typedef enum { /*!< MCUCTRL_XTALCTRL_FDBKDSBLXTAL */ - MCUCTRL_XTALCTRL_FDBKDSBLXTAL_EN = 0, /*!< EN : Enable XTAL oscillator comparator. value. */ - MCUCTRL_XTALCTRL_FDBKDSBLXTAL_DIS = 1, /*!< DIS : Disable XTAL oscillator comparator. value. */ -} MCUCTRL_XTALCTRL_FDBKDSBLXTAL_Enum; - -/* ============================================ MCUCTRL XTALCTRL XTALSWE [0..0] ============================================ */ -typedef enum { /*!< MCUCTRL_XTALCTRL_XTALSWE */ - MCUCTRL_XTALCTRL_XTALSWE_OVERRIDE_DIS = 0, /*!< OVERRIDE_DIS : XTAL Software Override Disable. value. */ - MCUCTRL_XTALCTRL_XTALSWE_OVERRIDE_EN = 1, /*!< OVERRIDE_EN : XTAL Software Override Enable. value. */ -} MCUCTRL_XTALCTRL_XTALSWE_Enum; - -/* ====================================================== XTALGENCTRL ====================================================== */ -/* ========================================== MCUCTRL XTALGENCTRL ACWARMUP [0..1] ========================================== */ -typedef enum { /*!< MCUCTRL_XTALGENCTRL_ACWARMUP */ - MCUCTRL_XTALGENCTRL_ACWARMUP_SEC1 = 0, /*!< SEC1 : Warmup period of 1-2 seconds value. */ - MCUCTRL_XTALGENCTRL_ACWARMUP_SEC2 = 1, /*!< SEC2 : Warmup period of 2-4 seconds value. */ - MCUCTRL_XTALGENCTRL_ACWARMUP_SEC4 = 2, /*!< SEC4 : Warmup period of 4-8 seconds value. */ - MCUCTRL_XTALGENCTRL_ACWARMUP_SEC8 = 3, /*!< SEC8 : Warmup period of 8-16 seconds value. */ -} MCUCTRL_XTALGENCTRL_ACWARMUP_Enum; - -/* ======================================================= MISCCTRL ======================================================== */ -/* ====================================================== BOOTLOADER ======================================================= */ -/* ======================================= MCUCTRL BOOTLOADER SECBOOTONRST [30..31] ======================================== */ -typedef enum { /*!< MCUCTRL_BOOTLOADER_SECBOOTONRST */ - MCUCTRL_BOOTLOADER_SECBOOTONRST_DISABLED = 0, /*!< DISABLED : Secure boot disabled value. */ - MCUCTRL_BOOTLOADER_SECBOOTONRST_ENABLED = 1, /*!< ENABLED : Secure boot enabled value. */ - MCUCTRL_BOOTLOADER_SECBOOTONRST_ERROR = 2, /*!< ERROR : Error in secure boot configuration value. */ -} MCUCTRL_BOOTLOADER_SECBOOTONRST_Enum; - -/* ========================================== MCUCTRL BOOTLOADER SECBOOT [28..29] ========================================== */ -typedef enum { /*!< MCUCTRL_BOOTLOADER_SECBOOT */ - MCUCTRL_BOOTLOADER_SECBOOT_DISABLED = 0, /*!< DISABLED : Secure boot disabled value. */ - MCUCTRL_BOOTLOADER_SECBOOT_ENABLED = 1, /*!< ENABLED : Secure boot enabled value. */ - MCUCTRL_BOOTLOADER_SECBOOT_ERROR = 2, /*!< ERROR : Error in secure boot configuration value. */ -} MCUCTRL_BOOTLOADER_SECBOOT_Enum; - -/* ====================================== MCUCTRL BOOTLOADER SECBOOTFEATURE [26..27] ======================================= */ -typedef enum { /*!< MCUCTRL_BOOTLOADER_SECBOOTFEATURE */ - MCUCTRL_BOOTLOADER_SECBOOTFEATURE_DISABLED = 0,/*!< DISABLED : Secure boot disabled value. */ - MCUCTRL_BOOTLOADER_SECBOOTFEATURE_ENABLED = 1,/*!< ENABLED : Secure boot enabled value. */ - MCUCTRL_BOOTLOADER_SECBOOTFEATURE_ERROR = 2, /*!< ERROR : Error in secure boot configuration value. */ -} MCUCTRL_BOOTLOADER_SECBOOTFEATURE_Enum; - -/* ========================================== MCUCTRL BOOTLOADER PROTLOCK [2..2] =========================================== */ -typedef enum { /*!< MCUCTRL_BOOTLOADER_PROTLOCK */ - MCUCTRL_BOOTLOADER_PROTLOCK_LOCK = 1, /*!< LOCK : Enable the secure boot lock value. */ -} MCUCTRL_BOOTLOADER_PROTLOCK_Enum; - -/* =========================================== MCUCTRL BOOTLOADER SBLOCK [1..1] ============================================ */ -typedef enum { /*!< MCUCTRL_BOOTLOADER_SBLOCK */ - MCUCTRL_BOOTLOADER_SBLOCK_LOCK = 1, /*!< LOCK : Enable the secure boot lock value. */ -} MCUCTRL_BOOTLOADER_SBLOCK_Enum; - -/* ======================================== MCUCTRL BOOTLOADER BOOTLOADERLOW [0..0] ======================================== */ -typedef enum { /*!< MCUCTRL_BOOTLOADER_BOOTLOADERLOW */ - MCUCTRL_BOOTLOADER_BOOTLOADERLOW_ADDR0 = 1, /*!< ADDR0 : Bootloader code at 0x00000000. value. */ -} MCUCTRL_BOOTLOADER_BOOTLOADERLOW_Enum; - -/* ====================================================== SHADOWVALID ====================================================== */ -/* ======================================== MCUCTRL SHADOWVALID INFO0_VALID [2..2] ========================================= */ -typedef enum { /*!< MCUCTRL_SHADOWVALID_INFO0_VALID */ - MCUCTRL_SHADOWVALID_INFO0_VALID_VALID = 1, /*!< VALID : Flash info0 (customer) space contains valid data. value. */ -} MCUCTRL_SHADOWVALID_INFO0_VALID_Enum; - -/* ========================================== MCUCTRL SHADOWVALID BLDSLEEP [1..1] ========================================== */ -typedef enum { /*!< MCUCTRL_SHADOWVALID_BLDSLEEP */ - MCUCTRL_SHADOWVALID_BLDSLEEP_DEEPSLEEP = 1, /*!< DEEPSLEEP : Bootloader will go to deep sleep if no flash image - loaded value. */ -} MCUCTRL_SHADOWVALID_BLDSLEEP_Enum; - -/* =========================================== MCUCTRL SHADOWVALID VALID [0..0] ============================================ */ -typedef enum { /*!< MCUCTRL_SHADOWVALID_VALID */ - MCUCTRL_SHADOWVALID_VALID_VALID = 1, /*!< VALID : Flash information space contains valid data. value. */ -} MCUCTRL_SHADOWVALID_VALID_Enum; - -/* ======================================================= SCRATCH0 ======================================================== */ -/* ======================================================= SCRATCH1 ======================================================== */ -/* ==================================================== ICODEFAULTADDR ===================================================== */ -/* ==================================================== DCODEFAULTADDR ===================================================== */ -/* ===================================================== SYSFAULTADDR ====================================================== */ -/* ====================================================== FAULTSTATUS ====================================================== */ -/* ========================================== MCUCTRL FAULTSTATUS SYSFAULT [2..2] ========================================== */ -typedef enum { /*!< MCUCTRL_FAULTSTATUS_SYSFAULT */ - MCUCTRL_FAULTSTATUS_SYSFAULT_NOFAULT = 0, /*!< NOFAULT : No bus fault has been detected. value. */ - MCUCTRL_FAULTSTATUS_SYSFAULT_FAULT = 1, /*!< FAULT : Bus fault detected. value. */ -} MCUCTRL_FAULTSTATUS_SYSFAULT_Enum; - -/* ========================================= MCUCTRL FAULTSTATUS DCODEFAULT [1..1] ========================================= */ -typedef enum { /*!< MCUCTRL_FAULTSTATUS_DCODEFAULT */ - MCUCTRL_FAULTSTATUS_DCODEFAULT_NOFAULT = 0, /*!< NOFAULT : No DCODE fault has been detected. value. */ - MCUCTRL_FAULTSTATUS_DCODEFAULT_FAULT = 1, /*!< FAULT : DCODE fault detected. value. */ -} MCUCTRL_FAULTSTATUS_DCODEFAULT_Enum; - -/* ========================================= MCUCTRL FAULTSTATUS ICODEFAULT [0..0] ========================================= */ -typedef enum { /*!< MCUCTRL_FAULTSTATUS_ICODEFAULT */ - MCUCTRL_FAULTSTATUS_ICODEFAULT_NOFAULT = 0, /*!< NOFAULT : No ICODE fault has been detected. value. */ - MCUCTRL_FAULTSTATUS_ICODEFAULT_FAULT = 1, /*!< FAULT : ICODE fault detected. value. */ -} MCUCTRL_FAULTSTATUS_ICODEFAULT_Enum; - -/* ==================================================== FAULTCAPTUREEN ===================================================== */ -/* ===================================== MCUCTRL FAULTCAPTUREEN FAULTCAPTUREEN [0..0] ====================================== */ -typedef enum { /*!< MCUCTRL_FAULTCAPTUREEN_FAULTCAPTUREEN */ - MCUCTRL_FAULTCAPTUREEN_FAULTCAPTUREEN_DIS = 0,/*!< DIS : Disable fault capture. value. */ - MCUCTRL_FAULTCAPTUREEN_FAULTCAPTUREEN_EN = 1, /*!< EN : Enable fault capture. value. */ -} MCUCTRL_FAULTCAPTUREEN_FAULTCAPTUREEN_Enum; - -/* ========================================================= DBGR1 ========================================================= */ -/* ========================================================= DBGR2 ========================================================= */ -/* ======================================================= PMUENABLE ======================================================= */ -/* ============================================ MCUCTRL PMUENABLE ENABLE [0..0] ============================================ */ -typedef enum { /*!< MCUCTRL_PMUENABLE_ENABLE */ - MCUCTRL_PMUENABLE_ENABLE_DIS = 0, /*!< DIS : Disable MCU power management. value. */ - MCUCTRL_PMUENABLE_ENABLE_EN = 1, /*!< EN : Enable MCU power management. value. */ -} MCUCTRL_PMUENABLE_ENABLE_Enum; - -/* ======================================================= TPIUCTRL ======================================================== */ -/* ============================================ MCUCTRL TPIUCTRL CLKSEL [8..10] ============================================ */ -typedef enum { /*!< MCUCTRL_TPIUCTRL_CLKSEL */ - MCUCTRL_TPIUCTRL_CLKSEL_LOWPWR = 0, /*!< LOWPWR : Low power state. value. */ - MCUCTRL_TPIUCTRL_CLKSEL_HFRCDIV2 = 1, /*!< HFRCDIV2 : Selects HFRC divided by 2 as the source TPIU clk - value. */ - MCUCTRL_TPIUCTRL_CLKSEL_HFRCDIV8 = 2, /*!< HFRCDIV8 : Selects HFRC divided by 8 as the source TPIU clk - value. */ - MCUCTRL_TPIUCTRL_CLKSEL_HFRCDIV16 = 3, /*!< HFRCDIV16 : Selects HFRC divided by 16 as the source TPIU clk - value. */ - MCUCTRL_TPIUCTRL_CLKSEL_HFRCDIV32 = 4, /*!< HFRCDIV32 : Selects HFRC divided by 32 as the source TPIU clk - value. */ -} MCUCTRL_TPIUCTRL_CLKSEL_Enum; - -/* ============================================ MCUCTRL TPIUCTRL ENABLE [0..0] ============================================= */ -typedef enum { /*!< MCUCTRL_TPIUCTRL_ENABLE */ - MCUCTRL_TPIUCTRL_ENABLE_DIS = 0, /*!< DIS : Disable the TPIU. value. */ - MCUCTRL_TPIUCTRL_ENABLE_EN = 1, /*!< EN : Enable the TPIU. value. */ -} MCUCTRL_TPIUCTRL_ENABLE_Enum; - -/* ====================================================== OTAPOINTER ======================================================= */ -/* ====================================================== APBDMACTRL ======================================================= */ -/* ========================================= MCUCTRL APBDMACTRL DECODEABORT [1..1] ========================================= */ -typedef enum { /*!< MCUCTRL_APBDMACTRL_DECODEABORT */ - MCUCTRL_APBDMACTRL_DECODEABORT_DISABLE = 0, /*!< DISABLE : Bus operations to powered down peripherals are quietly - discarded value. */ - MCUCTRL_APBDMACTRL_DECODEABORT_ENABLE = 1, /*!< ENABLE : Bus operations to powered down peripherals result in - a bus fault. value. */ -} MCUCTRL_APBDMACTRL_DECODEABORT_Enum; - -/* ========================================= MCUCTRL APBDMACTRL DMA_ENABLE [0..0] ========================================== */ -typedef enum { /*!< MCUCTRL_APBDMACTRL_DMA_ENABLE */ - MCUCTRL_APBDMACTRL_DMA_ENABLE_DISABLE = 0, /*!< DISABLE : DMA operations disabled value. */ - MCUCTRL_APBDMACTRL_DMA_ENABLE_ENABLE = 1, /*!< ENABLE : DMA operations enabled value. */ -} MCUCTRL_APBDMACTRL_DMA_ENABLE_Enum; - -/* ======================================================= SRAMMODE ======================================================== */ -/* ====================================================== KEXTCLKSEL ======================================================= */ -/* ========================================= MCUCTRL KEXTCLKSEL KEXTCLKSEL [0..31] ========================================= */ -typedef enum { /*!< MCUCTRL_KEXTCLKSEL_KEXTCLKSEL */ - MCUCTRL_KEXTCLKSEL_KEXTCLKSEL_Key = 83, /*!< Key : Key value. */ -} MCUCTRL_KEXTCLKSEL_KEXTCLKSEL_Enum; - -/* ======================================================= SIMOBUCK4 ======================================================= */ -/* ======================================================= BLEBUCK2 ======================================================== */ -/* ====================================================== FLASHWPROT0 ====================================================== */ -/* ====================================================== FLASHWPROT1 ====================================================== */ -/* ====================================================== FLASHRPROT0 ====================================================== */ -/* ====================================================== FLASHRPROT1 ====================================================== */ -/* ================================================= DMASRAMWRITEPROTECT0 ================================================== */ -/* ================================================= DMASRAMWRITEPROTECT1 ================================================== */ -/* ================================================== DMASRAMREADPROTECT0 ================================================== */ -/* ================================================== DMASRAMREADPROTECT1 ================================================== */ - - -/* =========================================================================================================================== */ -/* ================ MSPI ================ */ -/* =========================================================================================================================== */ - -/* ========================================================= CTRL ========================================================== */ -/* ========================================================== CFG ========================================================== */ -/* ================================================ MSPI CFG CPOL [17..17] ================================================= */ -typedef enum { /*!< MSPI_CFG_CPOL */ - MSPI_CFG_CPOL_LOW = 0, /*!< LOW : Clock inactive state is low. value. */ - MSPI_CFG_CPOL_HIGH = 1, /*!< HIGH : Clock inactive state is high. value. */ -} MSPI_CFG_CPOL_Enum; - -/* ================================================ MSPI CFG CPHA [16..16] ================================================= */ -typedef enum { /*!< MSPI_CFG_CPHA */ - MSPI_CFG_CPHA_MIDDLE = 0, /*!< MIDDLE : Clock toggles in middle of data bit. value. */ - MSPI_CFG_CPHA_START = 1, /*!< START : Clock toggles at start of data bit. value. */ -} MSPI_CFG_CPHA_Enum; - -/* ================================================ MSPI CFG DEVCFG [0..3] ================================================= */ -typedef enum { /*!< MSPI_CFG_DEVCFG */ - MSPI_CFG_DEVCFG_SERIAL0 = 1, /*!< SERIAL0 : Single bit SPI flash on chip select 0 value. */ - MSPI_CFG_DEVCFG_SERIAL1 = 2, /*!< SERIAL1 : Single bit SPI flash on chip select 1 value. */ - MSPI_CFG_DEVCFG_DUAL0 = 5, /*!< DUAL0 : Dual SPI flash on chip select 0 value. */ - MSPI_CFG_DEVCFG_DUAL1 = 6, /*!< DUAL1 : Dual bit SPI flash on chip select 1 value. */ - MSPI_CFG_DEVCFG_QUAD0 = 9, /*!< QUAD0 : Quad SPI flash on chip select 0 value. */ - MSPI_CFG_DEVCFG_QUAD1 = 10, /*!< QUAD1 : Quad SPI flash on chip select 1 value. */ - MSPI_CFG_DEVCFG_OCTAL0 = 13, /*!< OCTAL0 : Octal SPI flash on chip select 0 value. */ - MSPI_CFG_DEVCFG_OCTAL1 = 14, /*!< OCTAL1 : Octal SPI flash on chip select 1 value. */ - MSPI_CFG_DEVCFG_QUADPAIRED = 15, /*!< QUADPAIRED : Dual Quad SPI flash on chip selects 0/1. value. */ - MSPI_CFG_DEVCFG_QUADPAIRED_SERIAL = 3, /*!< QUADPAIRED_SERIAL : Dual Quad SPI flash on chip selects 0/1, - but transmit in serial mode for initialization operations - value. */ -} MSPI_CFG_DEVCFG_Enum; - -/* ========================================================= ADDR ========================================================== */ -/* ========================================================= INSTR ========================================================= */ -/* ======================================================== TXFIFO ========================================================= */ -/* ======================================================== RXFIFO ========================================================= */ -/* ======================================================= TXENTRIES ======================================================= */ -/* ======================================================= RXENTRIES ======================================================= */ -/* ======================================================= THRESHOLD ======================================================= */ -/* ======================================================== MSPICFG ======================================================== */ -/* ============================================== MSPI MSPICFG CLKDIV [8..13] ============================================== */ -typedef enum { /*!< MSPI_MSPICFG_CLKDIV */ - MSPI_MSPICFG_CLKDIV_CLK24 = 2, /*!< CLK24 : 24 MHz MSPI clock value. */ - MSPI_MSPICFG_CLKDIV_CLK12 = 4, /*!< CLK12 : 12 MHz MSPI clock value. */ - MSPI_MSPICFG_CLKDIV_CLK6 = 8, /*!< CLK6 : 6 MHz MSPI clock value. */ - MSPI_MSPICFG_CLKDIV_CLK3 = 16, /*!< CLK3 : 3 MHz MSPI clock value. */ - MSPI_MSPICFG_CLKDIV_CLK1_5 = 32, /*!< CLK1_5 : 1.5 MHz MSPI clock value. */ -} MSPI_MSPICFG_CLKDIV_Enum; - -/* ============================================== MSPI MSPICFG IOMSEL [4..6] =============================================== */ -typedef enum { /*!< MSPI_MSPICFG_IOMSEL */ - MSPI_MSPICFG_IOMSEL_IOM0 = 0, /*!< IOM0 : ERROR: desc VALUE MISSING value. */ - MSPI_MSPICFG_IOMSEL_IOM1 = 1, /*!< IOM1 : ERROR: desc VALUE MISSING value. */ - MSPI_MSPICFG_IOMSEL_IOM2 = 2, /*!< IOM2 : ERROR: desc VALUE MISSING value. */ - MSPI_MSPICFG_IOMSEL_IOM3 = 3, /*!< IOM3 : ERROR: desc VALUE MISSING value. */ - MSPI_MSPICFG_IOMSEL_IOM4 = 4, /*!< IOM4 : ERROR: desc VALUE MISSING value. */ - MSPI_MSPICFG_IOMSEL_IOM5 = 5, /*!< IOM5 : ERROR: desc VALUE MISSING value. */ - MSPI_MSPICFG_IOMSEL_DISABLED = 7, /*!< DISABLED : No IOM selected. Signals always zero. value. */ -} MSPI_MSPICFG_IOMSEL_Enum; - -/* =============================================== MSPI MSPICFG TXNEG [3..3] =============================================== */ -typedef enum { /*!< MSPI_MSPICFG_TXNEG */ - MSPI_MSPICFG_TXNEG_NORMAL = 0, /*!< NORMAL : TX launched from posedge internal clock value. */ - MSPI_MSPICFG_TXNEG_NEGEDGE = 1, /*!< NEGEDGE : TX data launched from negedge of internal clock value. */ -} MSPI_MSPICFG_TXNEG_Enum; - -/* =============================================== MSPI MSPICFG RXNEG [2..2] =============================================== */ -typedef enum { /*!< MSPI_MSPICFG_RXNEG */ - MSPI_MSPICFG_RXNEG_NORMAL = 0, /*!< NORMAL : RX data sampled on posedge of internal clock value. */ - MSPI_MSPICFG_RXNEG_NEGEDGE = 1, /*!< NEGEDGE : RX data sampled on negedge of internal clock value. */ -} MSPI_MSPICFG_RXNEG_Enum; - -/* =============================================== MSPI MSPICFG RXCAP [1..1] =============================================== */ -typedef enum { /*!< MSPI_MSPICFG_RXCAP */ - MSPI_MSPICFG_RXCAP_NORMAL = 0, /*!< NORMAL : RX Capture phase aligns with CPHA setting value. */ - MSPI_MSPICFG_RXCAP_DELAY = 1, /*!< DELAY : RX Capture phase is delayed from CPHA setting by one - clock edge value. */ -} MSPI_MSPICFG_RXCAP_Enum; - -/* ============================================== MSPI MSPICFG APBCLK [0..0] =============================================== */ -typedef enum { /*!< MSPI_MSPICFG_APBCLK */ - MSPI_MSPICFG_APBCLK_DIS = 0, /*!< DIS : Disable continuous clock. value. */ - MSPI_MSPICFG_APBCLK_EN = 1, /*!< EN : Enable continuous clock. value. */ -} MSPI_MSPICFG_APBCLK_Enum; - -/* ======================================================== PADCFG ========================================================= */ -/* ======================================================= PADOUTEN ======================================================== */ -/* ============================================== MSPI PADOUTEN OUTEN [0..8] =============================================== */ -typedef enum { /*!< MSPI_PADOUTEN_OUTEN */ - MSPI_PADOUTEN_OUTEN_QUAD0 = 271, /*!< QUAD0 : Quad0 (4 data + 1 clock) value. */ - MSPI_PADOUTEN_OUTEN_QUAD1 = 496, /*!< QUAD1 : Quad1 (4 data + 1 clock) value. */ - MSPI_PADOUTEN_OUTEN_OCTAL = 511, /*!< OCTAL : Octal (8 data + 1 clock) value. */ - MSPI_PADOUTEN_OUTEN_SERIAL0 = 259, /*!< SERIAL0 : Serial (2 data + 1 clock) value. */ -} MSPI_PADOUTEN_OUTEN_Enum; - -/* ========================================================= FLASH ========================================================= */ -/* =============================================== MSPI FLASH XIPACK [2..3] ================================================ */ -typedef enum { /*!< MSPI_FLASH_XIPACK */ - MSPI_FLASH_XIPACK_NOACK = 0, /*!< NOACK : No acknowledege sent. Data IOs are tristated the first - turnaround cycle value. */ - MSPI_FLASH_XIPACK_ACK = 2, /*!< ACK : Positive acknowledege sent. Data IOs are driven to 0 the - first turnaround cycle to acknowledge XIP mode value. */ - MSPI_FLASH_XIPACK_TERMINATE = 3, /*!< TERMINATE : Negative acknowledege sent. Data IOs are driven - to 1 the first turnaround cycle to terminate XIP mode. - XIPSENDI should be reenabled for the next transfer value. */ -} MSPI_FLASH_XIPACK_Enum; - -/* ====================================================== SCRAMBLING ======================================================= */ -/* ========================================================= INTEN ========================================================= */ -/* ======================================================== INTSTAT ======================================================== */ -/* ======================================================== INTCLR ========================================================= */ -/* ======================================================== INTSET ========================================================= */ -/* ======================================================== DMACFG ========================================================= */ -/* =============================================== MSPI DMACFG DMAPRI [3..4] =============================================== */ -typedef enum { /*!< MSPI_DMACFG_DMAPRI */ - MSPI_DMACFG_DMAPRI_LOW = 0, /*!< LOW : Low Priority (service as best effort) value. */ - MSPI_DMACFG_DMAPRI_HIGH = 1, /*!< HIGH : High Priority (service immediately) value. */ - MSPI_DMACFG_DMAPRI_AUTO = 2, /*!< AUTO : Auto Priority (priority raised once TX FIFO empties or - RX FIFO fills) value. */ -} MSPI_DMACFG_DMAPRI_Enum; - -/* =============================================== MSPI DMACFG DMADIR [2..2] =============================================== */ -typedef enum { /*!< MSPI_DMACFG_DMADIR */ - MSPI_DMACFG_DMADIR_P2M = 0, /*!< P2M : Peripheral to Memory (SRAM) transaction value. */ - MSPI_DMACFG_DMADIR_M2P = 1, /*!< M2P : Memory to Peripheral transaction value. */ -} MSPI_DMACFG_DMADIR_Enum; - -/* =============================================== MSPI DMACFG DMAEN [0..1] ================================================ */ -typedef enum { /*!< MSPI_DMACFG_DMAEN */ - MSPI_DMACFG_DMAEN_DIS = 0, /*!< DIS : Disable DMA Function value. */ - MSPI_DMACFG_DMAEN_EN = 3, /*!< EN : Enable HW controlled DMA Function to manage DMA to flash - devices. HW will automatically handle issuance of instruction/address - bytes based on settings in the FLASH register. value. */ -} MSPI_DMACFG_DMAEN_Enum; - -/* ======================================================== DMASTAT ======================================================== */ -/* ====================================================== DMATARGADDR ====================================================== */ -/* ====================================================== DMADEVADDR ======================================================= */ -/* ====================================================== DMATOTCOUNT ====================================================== */ -/* ======================================================= DMABCOUNT ======================================================= */ -/* ======================================================= DMATHRESH ======================================================= */ -/* ========================================================= CQCFG ========================================================= */ -/* ================================================ MSPI CQCFG CQPRI [1..1] ================================================ */ -typedef enum { /*!< MSPI_CQCFG_CQPRI */ - MSPI_CQCFG_CQPRI_LOW = 0, /*!< LOW : Low Priority (service as best effort) value. */ - MSPI_CQCFG_CQPRI_HIGH = 1, /*!< HIGH : High Priority (service immediately) value. */ -} MSPI_CQCFG_CQPRI_Enum; - -/* ================================================ MSPI CQCFG CQEN [0..0] ================================================= */ -typedef enum { /*!< MSPI_CQCFG_CQEN */ - MSPI_CQCFG_CQEN_DIS = 0, /*!< DIS : Disable CQ Function value. */ - MSPI_CQCFG_CQEN_EN = 1, /*!< EN : Enable CQ Function value. */ -} MSPI_CQCFG_CQEN_Enum; - -/* ======================================================== CQADDR ========================================================= */ -/* ======================================================== CQSTAT ========================================================= */ -/* ======================================================== CQFLAGS ======================================================== */ -/* ============================================= MSPI CQFLAGS CQFLAGS [0..15] ============================================== */ -typedef enum { /*!< MSPI_CQFLAGS_CQFLAGS */ - MSPI_CQFLAGS_CQFLAGS_STOP = 32768, /*!< STOP : CQ Stop Flag. When set, CQ processing will complete. - value. */ - MSPI_CQFLAGS_CQFLAGS_CQIDX = 16384, /*!< CQIDX : CQ Index Pointers (CURIDX/ENDIDX) match. value. */ - MSPI_CQFLAGS_CQFLAGS_DMACPL = 2048, /*!< DMACPL : DMA Complete Status (hardwired DMACPL bit in DMASTAT) - value. */ - MSPI_CQFLAGS_CQFLAGS_CMDCPL = 1024, /*!< CMDCPL : PIO Operation completed (STATUS bit in CTRL register) - value. */ - MSPI_CQFLAGS_CQFLAGS_IOM1READY = 512, /*!< IOM1READY : IOM Buffer 1 Ready Status (from selected IOM). This - status is the result of XOR'ing the IOM0START with the - incoming status from the IOM. When high, MSPI can send - to the buffer. value. */ - MSPI_CQFLAGS_CQFLAGS_IOM0READY = 256, /*!< IOM0READY : IOM Buffer 0 Ready Status (from selected IOM). This - status is the result of XOR'ing the IOM0START with the - incoming status from the IOM. When high, MSPI can send - to the buffer. value. */ - MSPI_CQFLAGS_CQFLAGS_SWFLAG7 = 128, /*!< SWFLAG7 : Software flag 7. Can be used by software to start/pause - operations value. */ - MSPI_CQFLAGS_CQFLAGS_SWFLAG6 = 64, /*!< SWFLAG6 : Software flag 6. Can be used by software to start/pause - operatoins value. */ - MSPI_CQFLAGS_CQFLAGS_SWFLAG5 = 32, /*!< SWFLAG5 : Software flag 5. Can be used by software to start/pause - operations value. */ - MSPI_CQFLAGS_CQFLAGS_SWFLAG4 = 16, /*!< SWFLAG4 : Software flag 4. Can be used by software to start/pause - operatoins value. */ - MSPI_CQFLAGS_CQFLAGS_SWFLAG3 = 8, /*!< SWFLAG3 : Software flag 3. Can be used by software to start/pause - operations value. */ - MSPI_CQFLAGS_CQFLAGS_SWFLAG2 = 4, /*!< SWFLAG2 : Software flag 2. Can be used by software to start/pause - operatoins value. */ - MSPI_CQFLAGS_CQFLAGS_SWFLAG1 = 2, /*!< SWFLAG1 : Software flag 1. Can be used by software to start/pause - operations value. */ - MSPI_CQFLAGS_CQFLAGS_SWFLAG0 = 1, /*!< SWFLAG0 : Software flag 0. Can be used by software to start/pause - operatoins value. */ -} MSPI_CQFLAGS_CQFLAGS_Enum; - -/* ====================================================== CQSETCLEAR ======================================================= */ -/* ======================================================== CQPAUSE ======================================================== */ -/* ============================================== MSPI CQPAUSE CQMASK [0..15] ============================================== */ -typedef enum { /*!< MSPI_CQPAUSE_CQMASK */ - MSPI_CQPAUSE_CQMASK_STOP = 32768, /*!< STOP : CQ Stop Flag. When set, CQ processing will complete. - value. */ - MSPI_CQPAUSE_CQMASK_CQIDX = 16384, /*!< CQIDX : CQ Index Pointers (CURIDX/ENDIDX) match. value. */ - MSPI_CQPAUSE_CQMASK_DMACPL = 2048, /*!< DMACPL : DMA Complete Status (hardwired DMACPL bit in DMASTAT) - value. */ - MSPI_CQPAUSE_CQMASK_CMDCPL = 1024, /*!< CMDCPL : PIO Operation completed (STATUS bit in CTRL register) - value. */ - MSPI_CQPAUSE_CQMASK_IOM1READY = 512, /*!< IOM1READY : IOM Buffer 1 Ready Status (from selected IOM). This - status is the result of XOR'ing the IOM0START with the - incoming status from the IOM. When high, MSPI can send - to the buffer. value. */ - MSPI_CQPAUSE_CQMASK_IOM0READY = 256, /*!< IOM0READY : IOM Buffer 0 Ready Status (from selected IOM). This - status is the result of XOR'ing the IOM0START with the - incoming status from the IOM. When high, MSPI can send - to the buffer. value. */ - MSPI_CQPAUSE_CQMASK_SWFLAG7 = 128, /*!< SWFLAG7 : Software flag 7. Can be used by software to start/pause - operations value. */ - MSPI_CQPAUSE_CQMASK_SWFLAG6 = 64, /*!< SWFLAG6 : Software flag 6. Can be used by software to start/pause - operatoins value. */ - MSPI_CQPAUSE_CQMASK_SWFLAG5 = 32, /*!< SWFLAG5 : Software flag 5. Can be used by software to start/pause - operations value. */ - MSPI_CQPAUSE_CQMASK_SWFLAG4 = 16, /*!< SWFLAG4 : Software flag 4. Can be used by software to start/pause - operatoins value. */ - MSPI_CQPAUSE_CQMASK_SWFLAG3 = 8, /*!< SWFLAG3 : Software flag 3. Can be used by software to start/pause - operations value. */ - MSPI_CQPAUSE_CQMASK_SWFLAG2 = 4, /*!< SWFLAG2 : Software flag 2. Can be used by software to start/pause - operatoins value. */ - MSPI_CQPAUSE_CQMASK_SWFLAG1 = 2, /*!< SWFLAG1 : Software flag 1. Can be used by software to start/pause - operations value. */ - MSPI_CQPAUSE_CQMASK_SWFLAG0 = 1, /*!< SWFLAG0 : Software flag 0. Can be used by software to start/pause - operatoins value. */ -} MSPI_CQPAUSE_CQMASK_Enum; - -/* ======================================================= CQCURIDX ======================================================== */ -/* ======================================================= CQENDIDX ======================================================== */ - - -/* =========================================================================================================================== */ -/* ================ PDM ================ */ -/* =========================================================================================================================== */ - -/* ========================================================= PCFG ========================================================== */ -/* =============================================== PDM PCFG LRSWAP [31..31] ================================================ */ -typedef enum { /*!< PDM_PCFG_LRSWAP */ - PDM_PCFG_LRSWAP_EN = 1, /*!< EN : Swap left and right channels (FIFO Read RIGHT_LEFT). value. */ - PDM_PCFG_LRSWAP_NOSWAP = 0, /*!< NOSWAP : No channel swapping (IFO Read LEFT_RIGHT). value. */ -} PDM_PCFG_LRSWAP_Enum; - -/* ============================================== PDM PCFG PGARIGHT [26..30] =============================================== */ -typedef enum { /*!< PDM_PCFG_PGARIGHT */ - PDM_PCFG_PGARIGHT_P405DB = 31, /*!< P405DB : 40.5 db gain. value. */ - PDM_PCFG_PGARIGHT_P390DB = 30, /*!< P390DB : 39.0 db gain. value. */ - PDM_PCFG_PGARIGHT_P375DB = 29, /*!< P375DB : 37.5 db gain. value. */ - PDM_PCFG_PGARIGHT_P360DB = 28, /*!< P360DB : 36.0 db gain. value. */ - PDM_PCFG_PGARIGHT_P345DB = 27, /*!< P345DB : 34.5 db gain. value. */ - PDM_PCFG_PGARIGHT_P330DB = 26, /*!< P330DB : 33.0 db gain. value. */ - PDM_PCFG_PGARIGHT_P315DB = 25, /*!< P315DB : 31.5 db gain. value. */ - PDM_PCFG_PGARIGHT_P300DB = 24, /*!< P300DB : 30.0 db gain. value. */ - PDM_PCFG_PGARIGHT_P285DB = 23, /*!< P285DB : 28.5 db gain. value. */ - PDM_PCFG_PGARIGHT_P270DB = 22, /*!< P270DB : 27.0 db gain. value. */ - PDM_PCFG_PGARIGHT_P255DB = 21, /*!< P255DB : 25.5 db gain. value. */ - PDM_PCFG_PGARIGHT_P240DB = 20, /*!< P240DB : 24.0 db gain. value. */ - PDM_PCFG_PGARIGHT_P225DB = 19, /*!< P225DB : 22.5 db gain. value. */ - PDM_PCFG_PGARIGHT_P210DB = 18, /*!< P210DB : 21.0 db gain. value. */ - PDM_PCFG_PGARIGHT_P195DB = 17, /*!< P195DB : 19.5 db gain. value. */ - PDM_PCFG_PGARIGHT_P180DB = 16, /*!< P180DB : 18.0 db gain. value. */ - PDM_PCFG_PGARIGHT_P165DB = 15, /*!< P165DB : 16.5 db gain. value. */ - PDM_PCFG_PGARIGHT_P150DB = 14, /*!< P150DB : 15.0 db gain. value. */ - PDM_PCFG_PGARIGHT_P135DB = 13, /*!< P135DB : 13.5 db gain. value. */ - PDM_PCFG_PGARIGHT_P120DB = 12, /*!< P120DB : 12.0 db gain. value. */ - PDM_PCFG_PGARIGHT_P105DB = 11, /*!< P105DB : 10.5 db gain. value. */ - PDM_PCFG_PGARIGHT_P90DB = 10, /*!< P90DB : 9.0 db gain. value. */ - PDM_PCFG_PGARIGHT_P75DB = 9, /*!< P75DB : 7.5 db gain. value. */ - PDM_PCFG_PGARIGHT_P60DB = 8, /*!< P60DB : 6.0 db gain. value. */ - PDM_PCFG_PGARIGHT_P45DB = 7, /*!< P45DB : 4.5 db gain. value. */ - PDM_PCFG_PGARIGHT_P30DB = 6, /*!< P30DB : 3.0 db gain. value. */ - PDM_PCFG_PGARIGHT_P15DB = 5, /*!< P15DB : 1.5 db gain. value. */ - PDM_PCFG_PGARIGHT_0DB = 4, /*!< 0DB : 0.0 db gain. value. */ - PDM_PCFG_PGARIGHT_M15DB = 3, /*!< M15DB : -1.5 db gain. value. */ - PDM_PCFG_PGARIGHT_M300DB = 2, /*!< M300DB : -3.0 db gain. value. */ - PDM_PCFG_PGARIGHT_M45DB = 1, /*!< M45DB : -4.5 db gain. value. */ - PDM_PCFG_PGARIGHT_M60DB = 0, /*!< M60DB : -6.0 db gain. value. */ -} PDM_PCFG_PGARIGHT_Enum; - -/* =============================================== PDM PCFG PGALEFT [21..25] =============================================== */ -typedef enum { /*!< PDM_PCFG_PGALEFT */ - PDM_PCFG_PGALEFT_P405DB = 31, /*!< P405DB : 40.5 db gain. value. */ - PDM_PCFG_PGALEFT_P390DB = 30, /*!< P390DB : 39.0 db gain. value. */ - PDM_PCFG_PGALEFT_P375DB = 29, /*!< P375DB : 37.5 db gain. value. */ - PDM_PCFG_PGALEFT_P360DB = 28, /*!< P360DB : 36.0 db gain. value. */ - PDM_PCFG_PGALEFT_P345DB = 27, /*!< P345DB : 34.5 db gain. value. */ - PDM_PCFG_PGALEFT_P330DB = 26, /*!< P330DB : 33.0 db gain. value. */ - PDM_PCFG_PGALEFT_P315DB = 25, /*!< P315DB : 31.5 db gain. value. */ - PDM_PCFG_PGALEFT_P300DB = 24, /*!< P300DB : 30.0 db gain. value. */ - PDM_PCFG_PGALEFT_P285DB = 23, /*!< P285DB : 28.5 db gain. value. */ - PDM_PCFG_PGALEFT_P270DB = 22, /*!< P270DB : 27.0 db gain. value. */ - PDM_PCFG_PGALEFT_P255DB = 21, /*!< P255DB : 25.5 db gain. value. */ - PDM_PCFG_PGALEFT_P240DB = 20, /*!< P240DB : 24.0 db gain. value. */ - PDM_PCFG_PGALEFT_P225DB = 19, /*!< P225DB : 22.5 db gain. value. */ - PDM_PCFG_PGALEFT_P210DB = 18, /*!< P210DB : 21.0 db gain. value. */ - PDM_PCFG_PGALEFT_P195DB = 17, /*!< P195DB : 19.5 db gain. value. */ - PDM_PCFG_PGALEFT_P180DB = 16, /*!< P180DB : 18.0 db gain. value. */ - PDM_PCFG_PGALEFT_P165DB = 15, /*!< P165DB : 16.5 db gain. value. */ - PDM_PCFG_PGALEFT_P150DB = 14, /*!< P150DB : 15.0 db gain. value. */ - PDM_PCFG_PGALEFT_P135DB = 13, /*!< P135DB : 13.5 db gain. value. */ - PDM_PCFG_PGALEFT_P120DB = 12, /*!< P120DB : 12.0 db gain. value. */ - PDM_PCFG_PGALEFT_P105DB = 11, /*!< P105DB : 10.5 db gain. value. */ - PDM_PCFG_PGALEFT_P90DB = 10, /*!< P90DB : 9.0 db gain. value. */ - PDM_PCFG_PGALEFT_P75DB = 9, /*!< P75DB : 7.5 db gain. value. */ - PDM_PCFG_PGALEFT_P60DB = 8, /*!< P60DB : 6.0 db gain. value. */ - PDM_PCFG_PGALEFT_P45DB = 7, /*!< P45DB : 4.5 db gain. value. */ - PDM_PCFG_PGALEFT_P30DB = 6, /*!< P30DB : 3.0 db gain. value. */ - PDM_PCFG_PGALEFT_P15DB = 5, /*!< P15DB : 1.5 db gain. value. */ - PDM_PCFG_PGALEFT_0DB = 4, /*!< 0DB : 0.0 db gain. value. */ - PDM_PCFG_PGALEFT_M15DB = 3, /*!< M15DB : -1.5 db gain. value. */ - PDM_PCFG_PGALEFT_M300DB = 2, /*!< M300DB : -3.0 db gain. value. */ - PDM_PCFG_PGALEFT_M45DB = 1, /*!< M45DB : -4.5 db gain. value. */ - PDM_PCFG_PGALEFT_M60DB = 0, /*!< M60DB : -6.0 db gain. value. */ -} PDM_PCFG_PGALEFT_Enum; - -/* =============================================== PDM PCFG MCLKDIV [17..18] =============================================== */ -typedef enum { /*!< PDM_PCFG_MCLKDIV */ - PDM_PCFG_MCLKDIV_MCKDIV4 = 3, /*!< MCKDIV4 : Divide input clock by 4 value. */ - PDM_PCFG_MCLKDIV_MCKDIV3 = 2, /*!< MCKDIV3 : Divide input clock by 3 value. */ - PDM_PCFG_MCLKDIV_MCKDIV2 = 1, /*!< MCKDIV2 : Divide input clock by 2 value. */ - PDM_PCFG_MCLKDIV_MCKDIV1 = 0, /*!< MCKDIV1 : Divide input clock by 1 value. */ -} PDM_PCFG_MCLKDIV_Enum; - -/* ================================================ PDM PCFG ADCHPD [9..9] ================================================= */ -typedef enum { /*!< PDM_PCFG_ADCHPD */ - PDM_PCFG_ADCHPD_EN = 1, /*!< EN : Enable high pass filter. value. */ - PDM_PCFG_ADCHPD_DIS = 0, /*!< DIS : Disable high pass filter. value. */ -} PDM_PCFG_ADCHPD_Enum; - -/* =============================================== PDM PCFG SOFTMUTE [1..1] ================================================ */ -typedef enum { /*!< PDM_PCFG_SOFTMUTE */ - PDM_PCFG_SOFTMUTE_EN = 1, /*!< EN : Enable Soft Mute. value. */ - PDM_PCFG_SOFTMUTE_DIS = 0, /*!< DIS : Disable Soft Mute. value. */ -} PDM_PCFG_SOFTMUTE_Enum; - -/* =============================================== PDM PCFG PDMCOREEN [0..0] =============================================== */ -typedef enum { /*!< PDM_PCFG_PDMCOREEN */ - PDM_PCFG_PDMCOREEN_EN = 1, /*!< EN : Enable Data Streaming. value. */ - PDM_PCFG_PDMCOREEN_DIS = 0, /*!< DIS : Disable Data Streaming. value. */ -} PDM_PCFG_PDMCOREEN_Enum; - -/* ========================================================= VCFG ========================================================== */ -/* =============================================== PDM VCFG IOCLKEN [31..31] =============================================== */ -typedef enum { /*!< PDM_VCFG_IOCLKEN */ - PDM_VCFG_IOCLKEN_DIS = 0, /*!< DIS : Disable FIFO read. value. */ - PDM_VCFG_IOCLKEN_EN = 1, /*!< EN : Enable FIFO read. value. */ -} PDM_VCFG_IOCLKEN_Enum; - -/* ================================================ PDM VCFG RSTB [30..30] ================================================= */ -typedef enum { /*!< PDM_VCFG_RSTB */ - PDM_VCFG_RSTB_RESET = 0, /*!< RESET : Reset the core. value. */ - PDM_VCFG_RSTB_NORM = 1, /*!< NORM : Enable the core. value. */ -} PDM_VCFG_RSTB_Enum; - -/* ============================================== PDM VCFG PDMCLKSEL [27..29] ============================================== */ -typedef enum { /*!< PDM_VCFG_PDMCLKSEL */ - PDM_VCFG_PDMCLKSEL_DISABLE = 0, /*!< DISABLE : Static value. value. */ - PDM_VCFG_PDMCLKSEL_12MHz = 1, /*!< 12MHz : PDM clock is 12 MHz. value. */ - PDM_VCFG_PDMCLKSEL_6MHz = 2, /*!< 6MHz : PDM clock is 6 MHz. value. */ - PDM_VCFG_PDMCLKSEL_3MHz = 3, /*!< 3MHz : PDM clock is 3 MHz. value. */ - PDM_VCFG_PDMCLKSEL_1_5MHz = 4, /*!< 1_5MHz : PDM clock is 1.5 MHz. value. */ - PDM_VCFG_PDMCLKSEL_750KHz = 5, /*!< 750KHz : PDM clock is 750 KHz. value. */ - PDM_VCFG_PDMCLKSEL_375KHz = 6, /*!< 375KHz : PDM clock is 375 KHz. value. */ - PDM_VCFG_PDMCLKSEL_187KHz = 7, /*!< 187KHz : PDM clock is 187.5 KHz. value. */ -} PDM_VCFG_PDMCLKSEL_Enum; - -/* ============================================== PDM VCFG PDMCLKEN [26..26] =============================================== */ -typedef enum { /*!< PDM_VCFG_PDMCLKEN */ - PDM_VCFG_PDMCLKEN_DIS = 0, /*!< DIS : Disable serial clock. value. */ - PDM_VCFG_PDMCLKEN_EN = 1, /*!< EN : Enable serial clock. value. */ -} PDM_VCFG_PDMCLKEN_Enum; - -/* ================================================ PDM VCFG I2SEN [20..20] ================================================ */ -typedef enum { /*!< PDM_VCFG_I2SEN */ - PDM_VCFG_I2SEN_DIS = 0, /*!< DIS : Disable I2S interface. value. */ - PDM_VCFG_I2SEN_EN = 1, /*!< EN : Enable I2S interface. value. */ -} PDM_VCFG_I2SEN_Enum; - -/* =============================================== PDM VCFG BCLKINV [19..19] =============================================== */ -typedef enum { /*!< PDM_VCFG_BCLKINV */ - PDM_VCFG_BCLKINV_INV = 0, /*!< INV : BCLK inverted. value. */ - PDM_VCFG_BCLKINV_NORM = 1, /*!< NORM : BCLK not inverted. value. */ -} PDM_VCFG_BCLKINV_Enum; - -/* ============================================== PDM VCFG DMICKDEL [17..17] =============================================== */ -typedef enum { /*!< PDM_VCFG_DMICKDEL */ - PDM_VCFG_DMICKDEL_0CYC = 0, /*!< 0CYC : No delay. value. */ - PDM_VCFG_DMICKDEL_1CYC = 1, /*!< 1CYC : 1 cycle delay. value. */ -} PDM_VCFG_DMICKDEL_Enum; - -/* ================================================ PDM VCFG SELAP [16..16] ================================================ */ -typedef enum { /*!< PDM_VCFG_SELAP */ - PDM_VCFG_SELAP_I2S = 1, /*!< I2S : Clock source from I2S BCLK. value. */ - PDM_VCFG_SELAP_INTERNAL = 0, /*!< INTERNAL : Clock source from internal clock generator. value. */ -} PDM_VCFG_SELAP_Enum; - -/* ================================================ PDM VCFG PCMPACK [8..8] ================================================ */ -typedef enum { /*!< PDM_VCFG_PCMPACK */ - PDM_VCFG_PCMPACK_DIS = 0, /*!< DIS : Disable PCM packing. value. */ - PDM_VCFG_PCMPACK_EN = 1, /*!< EN : Enable PCM packing. value. */ -} PDM_VCFG_PCMPACK_Enum; - -/* ================================================= PDM VCFG CHSET [3..4] ================================================= */ -typedef enum { /*!< PDM_VCFG_CHSET */ - PDM_VCFG_CHSET_DIS = 0, /*!< DIS : Channel disabled. value. */ - PDM_VCFG_CHSET_LEFT = 1, /*!< LEFT : Mono left channel. value. */ - PDM_VCFG_CHSET_RIGHT = 2, /*!< RIGHT : Mono right channel. value. */ - PDM_VCFG_CHSET_STEREO = 3, /*!< STEREO : Stereo channels. value. */ -} PDM_VCFG_CHSET_Enum; - -/* ======================================================= VOICESTAT ======================================================= */ -/* ======================================================= FIFOREAD ======================================================== */ -/* ======================================================= FIFOFLUSH ======================================================= */ -/* ======================================================== FIFOTHR ======================================================== */ -/* ========================================================= INTEN ========================================================= */ -/* ======================================================== INTSTAT ======================================================== */ -/* ======================================================== INTCLR ========================================================= */ -/* ======================================================== INTSET ========================================================= */ -/* ======================================================= DMATRIGEN ======================================================= */ -/* ====================================================== DMATRIGSTAT ====================================================== */ -/* ======================================================== DMACFG ========================================================= */ -/* =============================================== PDM DMACFG DMAPRI [8..8] ================================================ */ -typedef enum { /*!< PDM_DMACFG_DMAPRI */ - PDM_DMACFG_DMAPRI_LOW = 0, /*!< LOW : Low Priority (service as best effort) value. */ - PDM_DMACFG_DMAPRI_HIGH = 1, /*!< HIGH : High Priority (service immediately) value. */ -} PDM_DMACFG_DMAPRI_Enum; - -/* =============================================== PDM DMACFG DMADIR [2..2] ================================================ */ -typedef enum { /*!< PDM_DMACFG_DMADIR */ - PDM_DMACFG_DMADIR_P2M = 0, /*!< P2M : Peripheral to Memory (SRAM) transaction. THe PDM module - will only DMA to memory. value. */ - PDM_DMACFG_DMADIR_M2P = 1, /*!< M2P : Memory to Peripheral transaction. Not available for PDM - module value. */ -} PDM_DMACFG_DMADIR_Enum; - -/* ================================================ PDM DMACFG DMAEN [0..0] ================================================ */ -typedef enum { /*!< PDM_DMACFG_DMAEN */ - PDM_DMACFG_DMAEN_DIS = 0, /*!< DIS : Disable DMA Function value. */ - PDM_DMACFG_DMAEN_EN = 1, /*!< EN : Enable DMA Function value. */ -} PDM_DMACFG_DMAEN_Enum; - -/* ====================================================== DMATOTCOUNT ====================================================== */ -/* ====================================================== DMATARGADDR ====================================================== */ -/* ======================================================== DMASTAT ======================================================== */ - - -/* =========================================================================================================================== */ -/* ================ PWRCTRL ================ */ -/* =========================================================================================================================== */ - -/* ======================================================= SUPPLYSRC ======================================================= */ -/* ========================================== PWRCTRL SUPPLYSRC BLEBUCKEN [0..0] =========================================== */ -typedef enum { /*!< PWRCTRL_SUPPLYSRC_BLEBUCKEN */ - PWRCTRL_SUPPLYSRC_BLEBUCKEN_EN = 1, /*!< EN : Enable the BLE Buck. value. */ - PWRCTRL_SUPPLYSRC_BLEBUCKEN_DIS = 0, /*!< DIS : Disable the BLE Buck. value. */ -} PWRCTRL_SUPPLYSRC_BLEBUCKEN_Enum; - -/* ===================================================== SUPPLYSTATUS ====================================================== */ -/* ========================================= PWRCTRL SUPPLYSTATUS BLEBUCKON [1..1] ========================================= */ -typedef enum { /*!< PWRCTRL_SUPPLYSTATUS_BLEBUCKON */ - PWRCTRL_SUPPLYSTATUS_BLEBUCKON_LDO = 0, /*!< LDO : Indicates the the LDO is supplying the BLE/Burst power - domain value. */ - PWRCTRL_SUPPLYSTATUS_BLEBUCKON_BUCK = 1, /*!< BUCK : Indicates the the Buck is supplying the BLE/Burst power - domain value. */ -} PWRCTRL_SUPPLYSTATUS_BLEBUCKON_Enum; - -/* ======================================== PWRCTRL SUPPLYSTATUS SIMOBUCKON [0..0] ========================================= */ -typedef enum { /*!< PWRCTRL_SUPPLYSTATUS_SIMOBUCKON */ - PWRCTRL_SUPPLYSTATUS_SIMOBUCKON_OFF = 0, /*!< OFF : Indicates the the SIMO Buck is OFF. value. */ - PWRCTRL_SUPPLYSTATUS_SIMOBUCKON_ON = 1, /*!< ON : Indicates the the SIMO Buck is ON. value. */ -} PWRCTRL_SUPPLYSTATUS_SIMOBUCKON_Enum; - -/* ======================================================= DEVPWREN ======================================================== */ -/* =========================================== PWRCTRL DEVPWREN PWRBLEL [13..13] =========================================== */ -typedef enum { /*!< PWRCTRL_DEVPWREN_PWRBLEL */ - PWRCTRL_DEVPWREN_PWRBLEL_EN = 1, /*!< EN : Power up BLE controller value. */ - PWRCTRL_DEVPWREN_PWRBLEL_DIS = 0, /*!< DIS : Power down BLE controller value. */ -} PWRCTRL_DEVPWREN_PWRBLEL_Enum; - -/* =========================================== PWRCTRL DEVPWREN PWRPDM [12..12] ============================================ */ -typedef enum { /*!< PWRCTRL_DEVPWREN_PWRPDM */ - PWRCTRL_DEVPWREN_PWRPDM_EN = 1, /*!< EN : Power up PDM value. */ - PWRCTRL_DEVPWREN_PWRPDM_DIS = 0, /*!< DIS : Power down PDM value. */ -} PWRCTRL_DEVPWREN_PWRPDM_Enum; - -/* =========================================== PWRCTRL DEVPWREN PWRMSPI [11..11] =========================================== */ -typedef enum { /*!< PWRCTRL_DEVPWREN_PWRMSPI */ - PWRCTRL_DEVPWREN_PWRMSPI_EN = 1, /*!< EN : Power up MSPI value. */ - PWRCTRL_DEVPWREN_PWRMSPI_DIS = 0, /*!< DIS : Power down MSPI value. */ -} PWRCTRL_DEVPWREN_PWRMSPI_Enum; - -/* ========================================== PWRCTRL DEVPWREN PWRSCARD [10..10] =========================================== */ -typedef enum { /*!< PWRCTRL_DEVPWREN_PWRSCARD */ - PWRCTRL_DEVPWREN_PWRSCARD_EN = 1, /*!< EN : Power up SCARD value. */ - PWRCTRL_DEVPWREN_PWRSCARD_DIS = 0, /*!< DIS : Power down SCARD value. */ -} PWRCTRL_DEVPWREN_PWRSCARD_Enum; - -/* ============================================ PWRCTRL DEVPWREN PWRADC [9..9] ============================================= */ -typedef enum { /*!< PWRCTRL_DEVPWREN_PWRADC */ - PWRCTRL_DEVPWREN_PWRADC_EN = 1, /*!< EN : Power up ADC value. */ - PWRCTRL_DEVPWREN_PWRADC_DIS = 0, /*!< DIS : Power Down ADC value. */ -} PWRCTRL_DEVPWREN_PWRADC_Enum; - -/* =========================================== PWRCTRL DEVPWREN PWRUART1 [8..8] ============================================ */ -typedef enum { /*!< PWRCTRL_DEVPWREN_PWRUART1 */ - PWRCTRL_DEVPWREN_PWRUART1_EN = 1, /*!< EN : Power up UART 1 value. */ - PWRCTRL_DEVPWREN_PWRUART1_DIS = 0, /*!< DIS : Power down UART 1 value. */ -} PWRCTRL_DEVPWREN_PWRUART1_Enum; - -/* =========================================== PWRCTRL DEVPWREN PWRUART0 [7..7] ============================================ */ -typedef enum { /*!< PWRCTRL_DEVPWREN_PWRUART0 */ - PWRCTRL_DEVPWREN_PWRUART0_EN = 1, /*!< EN : Power up UART 0 value. */ - PWRCTRL_DEVPWREN_PWRUART0_DIS = 0, /*!< DIS : Power down UART 0 value. */ -} PWRCTRL_DEVPWREN_PWRUART0_Enum; - -/* ============================================ PWRCTRL DEVPWREN PWRIOM5 [6..6] ============================================ */ -typedef enum { /*!< PWRCTRL_DEVPWREN_PWRIOM5 */ - PWRCTRL_DEVPWREN_PWRIOM5_EN = 1, /*!< EN : Power up IO Master 5 value. */ - PWRCTRL_DEVPWREN_PWRIOM5_DIS = 0, /*!< DIS : Power down IO Master 5 value. */ -} PWRCTRL_DEVPWREN_PWRIOM5_Enum; - -/* ============================================ PWRCTRL DEVPWREN PWRIOM4 [5..5] ============================================ */ -typedef enum { /*!< PWRCTRL_DEVPWREN_PWRIOM4 */ - PWRCTRL_DEVPWREN_PWRIOM4_EN = 1, /*!< EN : Power up IO Master 4 value. */ - PWRCTRL_DEVPWREN_PWRIOM4_DIS = 0, /*!< DIS : Power down IO Master 4 value. */ -} PWRCTRL_DEVPWREN_PWRIOM4_Enum; - -/* ============================================ PWRCTRL DEVPWREN PWRIOM3 [4..4] ============================================ */ -typedef enum { /*!< PWRCTRL_DEVPWREN_PWRIOM3 */ - PWRCTRL_DEVPWREN_PWRIOM3_EN = 1, /*!< EN : Power up IO Master 3 value. */ - PWRCTRL_DEVPWREN_PWRIOM3_DIS = 0, /*!< DIS : Power down IO Master 3 value. */ -} PWRCTRL_DEVPWREN_PWRIOM3_Enum; - -/* ============================================ PWRCTRL DEVPWREN PWRIOM2 [3..3] ============================================ */ -typedef enum { /*!< PWRCTRL_DEVPWREN_PWRIOM2 */ - PWRCTRL_DEVPWREN_PWRIOM2_EN = 1, /*!< EN : Power up IO Master 2 value. */ - PWRCTRL_DEVPWREN_PWRIOM2_DIS = 0, /*!< DIS : Power down IO Master 2 value. */ -} PWRCTRL_DEVPWREN_PWRIOM2_Enum; - -/* ============================================ PWRCTRL DEVPWREN PWRIOM1 [2..2] ============================================ */ -typedef enum { /*!< PWRCTRL_DEVPWREN_PWRIOM1 */ - PWRCTRL_DEVPWREN_PWRIOM1_EN = 1, /*!< EN : Power up IO Master 1 value. */ - PWRCTRL_DEVPWREN_PWRIOM1_DIS = 0, /*!< DIS : Power down IO Master 1 value. */ -} PWRCTRL_DEVPWREN_PWRIOM1_Enum; - -/* ============================================ PWRCTRL DEVPWREN PWRIOM0 [1..1] ============================================ */ -typedef enum { /*!< PWRCTRL_DEVPWREN_PWRIOM0 */ - PWRCTRL_DEVPWREN_PWRIOM0_EN = 1, /*!< EN : Power up IO Master 0 value. */ - PWRCTRL_DEVPWREN_PWRIOM0_DIS = 0, /*!< DIS : Power down IO Master 0 value. */ -} PWRCTRL_DEVPWREN_PWRIOM0_Enum; - -/* ============================================ PWRCTRL DEVPWREN PWRIOS [0..0] ============================================= */ -typedef enum { /*!< PWRCTRL_DEVPWREN_PWRIOS */ - PWRCTRL_DEVPWREN_PWRIOS_EN = 1, /*!< EN : Power up IO slave value. */ - PWRCTRL_DEVPWREN_PWRIOS_DIS = 0, /*!< DIS : Power down IO slave value. */ -} PWRCTRL_DEVPWREN_PWRIOS_Enum; - -/* ===================================================== MEMPWDINSLEEP ===================================================== */ -/* ====================================== PWRCTRL MEMPWDINSLEEP CACHEPWDSLP [31..31] ======================================= */ -typedef enum { /*!< PWRCTRL_MEMPWDINSLEEP_CACHEPWDSLP */ - PWRCTRL_MEMPWDINSLEEP_CACHEPWDSLP_EN = 1, /*!< EN : Power down cache in deep sleep value. */ - PWRCTRL_MEMPWDINSLEEP_CACHEPWDSLP_DIS = 0, /*!< DIS : Retain cache in deep sleep value. */ -} PWRCTRL_MEMPWDINSLEEP_CACHEPWDSLP_Enum; - -/* ====================================== PWRCTRL MEMPWDINSLEEP FLASH1PWDSLP [14..14] ====================================== */ -typedef enum { /*!< PWRCTRL_MEMPWDINSLEEP_FLASH1PWDSLP */ - PWRCTRL_MEMPWDINSLEEP_FLASH1PWDSLP_EN = 1, /*!< EN : Flash1 is powered down during deepsleep value. */ - PWRCTRL_MEMPWDINSLEEP_FLASH1PWDSLP_DIS = 0, /*!< DIS : Flash1 is kept powered on during deepsleep value. */ -} PWRCTRL_MEMPWDINSLEEP_FLASH1PWDSLP_Enum; - -/* ====================================== PWRCTRL MEMPWDINSLEEP FLASH0PWDSLP [13..13] ====================================== */ -typedef enum { /*!< PWRCTRL_MEMPWDINSLEEP_FLASH0PWDSLP */ - PWRCTRL_MEMPWDINSLEEP_FLASH0PWDSLP_EN = 1, /*!< EN : Flash0 is powered down during deepsleep value. */ - PWRCTRL_MEMPWDINSLEEP_FLASH0PWDSLP_DIS = 0, /*!< DIS : Flash0 is kept powered on during deepsleep value. */ -} PWRCTRL_MEMPWDINSLEEP_FLASH0PWDSLP_Enum; - -/* ======================================= PWRCTRL MEMPWDINSLEEP SRAMPWDSLP [3..12] ======================================== */ -typedef enum { /*!< PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP */ - PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_NONE = 0, /*!< NONE : All banks retained value. */ - PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_GROUP0 = 1, /*!< GROUP0 : SRAM GROUP0 powered down (64KB-96KB) value. */ - PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_GROUP1 = 2, /*!< GROUP1 : SRAM GROUP1 powered down (96KB-128KB) value. */ - PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_GROUP2 = 4, /*!< GROUP2 : SRAM GROUP2 powered down (128KB-160KB) value. */ - PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_GROUP3 = 8, /*!< GROUP3 : SRAM GROUP3 powered down (160KB-192KB) value. */ - PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_GROUP4 = 16, /*!< GROUP4 : SRAM GROUP4 powered down (192KB-224KB) value. */ - PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_GROUP5 = 32, /*!< GROUP5 : SRAM GROUP5 powered down (224KB-256KB) value. */ - PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_GROUP6 = 64, /*!< GROUP6 : SRAM GROUP6 powered down (256KB-288KB) value. */ - PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_GROUP7 = 128,/*!< GROUP7 : SRAM GROUP7 powered down (288KB-320KB) value. */ - PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_GROUP8 = 256,/*!< GROUP8 : SRAM GROUP8 powered down (320KB-352KB) value. */ - PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_GROUP9 = 512,/*!< GROUP9 : SRAM GROUP9 powered down (352KB-384KB) value. */ - PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_SRAM64K = 3, /*!< SRAM64K : Powerdown lower 64k SRAM (64KB-128KB) value. */ - PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_SRAM128K = 15,/*!< SRAM128K : Powerdown lower 128k SRAM (64KB-192KB) value. */ - PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_ALLBUTLOWER32K = 1022,/*!< ALLBUTLOWER32K : All SRAM banks but lower 32k powered down (96KB-384KB). - value. */ - PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_ALLBUTLOWER64K = 1020,/*!< ALLBUTLOWER64K : All banks but lower 64k powered down. value. */ - PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_ALLBUTLOWER128K = 1008,/*!< ALLBUTLOWER128K : All banks but lower 128k powered down. value. */ - PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_ALL = 1023, /*!< ALL : All banks powered down. value. */ -} PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_Enum; - -/* ======================================== PWRCTRL MEMPWDINSLEEP DTCMPWDSLP [0..2] ======================================== */ -typedef enum { /*!< PWRCTRL_MEMPWDINSLEEP_DTCMPWDSLP */ - PWRCTRL_MEMPWDINSLEEP_DTCMPWDSLP_NONE = 0, /*!< NONE : All DTCM retained value. */ - PWRCTRL_MEMPWDINSLEEP_DTCMPWDSLP_GROUP0DTCM0 = 1,/*!< GROUP0DTCM0 : Group0_DTCM0 powered down in deep sleep (0KB-8KB) - value. */ - PWRCTRL_MEMPWDINSLEEP_DTCMPWDSLP_GROUP0DTCM1 = 2,/*!< GROUP0DTCM1 : Group0_DTCM1 powered down in deep sleep (8KB-32KB) - value. */ - PWRCTRL_MEMPWDINSLEEP_DTCMPWDSLP_GROUP0 = 3, /*!< GROUP0 : Both DTCMs in group0 are powered down in deep sleep - (0KB-32KB) value. */ - PWRCTRL_MEMPWDINSLEEP_DTCMPWDSLP_ALLBUTGROUP0DTCM0 = 6,/*!< ALLBUTGROUP0DTCM0 : Group1 and Group0_DTCM1 are powered down - in deep sleep (8KB-64KB) value. */ - PWRCTRL_MEMPWDINSLEEP_DTCMPWDSLP_GROUP1 = 4, /*!< GROUP1 : Group1 DTCM powered down in deep sleep (32KB-64KB) - value. */ - PWRCTRL_MEMPWDINSLEEP_DTCMPWDSLP_ALL = 7, /*!< ALL : All DTCMs powered down in deep sleep (0KB-64KB) value. */ -} PWRCTRL_MEMPWDINSLEEP_DTCMPWDSLP_Enum; - -/* ======================================================= MEMPWREN ======================================================== */ -/* =========================================== PWRCTRL MEMPWREN CACHEB2 [31..31] =========================================== */ -typedef enum { /*!< PWRCTRL_MEMPWREN_CACHEB2 */ - PWRCTRL_MEMPWREN_CACHEB2_EN = 1, /*!< EN : Power up Cache Bank 2 value. */ - PWRCTRL_MEMPWREN_CACHEB2_DIS = 0, /*!< DIS : Power down Cache Bank 2 value. */ -} PWRCTRL_MEMPWREN_CACHEB2_Enum; - -/* =========================================== PWRCTRL MEMPWREN CACHEB0 [30..30] =========================================== */ -typedef enum { /*!< PWRCTRL_MEMPWREN_CACHEB0 */ - PWRCTRL_MEMPWREN_CACHEB0_EN = 1, /*!< EN : Power up Cache Bank 0 value. */ - PWRCTRL_MEMPWREN_CACHEB0_DIS = 0, /*!< DIS : Power down Cache Bank 0 value. */ -} PWRCTRL_MEMPWREN_CACHEB0_Enum; - -/* =========================================== PWRCTRL MEMPWREN FLASH1 [14..14] ============================================ */ -typedef enum { /*!< PWRCTRL_MEMPWREN_FLASH1 */ - PWRCTRL_MEMPWREN_FLASH1_EN = 1, /*!< EN : Power up Flash1 value. */ - PWRCTRL_MEMPWREN_FLASH1_DIS = 0, /*!< DIS : Power down Flash1 value. */ -} PWRCTRL_MEMPWREN_FLASH1_Enum; - -/* =========================================== PWRCTRL MEMPWREN FLASH0 [13..13] ============================================ */ -typedef enum { /*!< PWRCTRL_MEMPWREN_FLASH0 */ - PWRCTRL_MEMPWREN_FLASH0_EN = 1, /*!< EN : Power up Flash0 value. */ - PWRCTRL_MEMPWREN_FLASH0_DIS = 0, /*!< DIS : Power down Flash0 value. */ -} PWRCTRL_MEMPWREN_FLASH0_Enum; - -/* ============================================= PWRCTRL MEMPWREN SRAM [3..12] ============================================= */ -typedef enum { /*!< PWRCTRL_MEMPWREN_SRAM */ - PWRCTRL_MEMPWREN_SRAM_NONE = 0, /*!< NONE : Do not power ON any of the SRAM banks value. */ - PWRCTRL_MEMPWREN_SRAM_GROUP0 = 1, /*!< GROUP0 : Power ON only SRAM group0 (0KB-32KB) value. */ - PWRCTRL_MEMPWREN_SRAM_GROUP1 = 2, /*!< GROUP1 : Power ON only SRAM group1 (32KB-64KB) value. */ - PWRCTRL_MEMPWREN_SRAM_GROUP2 = 4, /*!< GROUP2 : Power ON only SRAM group2 (64KB-96KB) value. */ - PWRCTRL_MEMPWREN_SRAM_GROUP3 = 8, /*!< GROUP3 : Power ON only SRAM group3 (96KB-128KB) value. */ - PWRCTRL_MEMPWREN_SRAM_GROUP4 = 16, /*!< GROUP4 : Power ON only SRAM group4 (128KB-160KB) value. */ - PWRCTRL_MEMPWREN_SRAM_GROUP5 = 32, /*!< GROUP5 : Power ON only SRAM group5 (160KB-192KB) value. */ - PWRCTRL_MEMPWREN_SRAM_GROUP6 = 64, /*!< GROUP6 : Power ON only SRAM group6 (192KB-224KB) value. */ - PWRCTRL_MEMPWREN_SRAM_GROUP7 = 128, /*!< GROUP7 : Power ON only SRAM group7 (224KB-256KB) value. */ - PWRCTRL_MEMPWREN_SRAM_GROUP8 = 256, /*!< GROUP8 : Power ON only SRAM group8 (256KB-288KB) value. */ - PWRCTRL_MEMPWREN_SRAM_GROUP9 = 512, /*!< GROUP9 : Power ON only SRAM group9 (288KB-320KB) value. */ - PWRCTRL_MEMPWREN_SRAM_SRAM64K = 3, /*!< SRAM64K : Power ON only lower 64k value. */ - PWRCTRL_MEMPWREN_SRAM_SRAM128K = 15, /*!< SRAM128K : Power ON only lower 128k value. */ - PWRCTRL_MEMPWREN_SRAM_SRAM256K = 255, /*!< SRAM256K : Power ON only lower 256k value. */ - PWRCTRL_MEMPWREN_SRAM_ALL = 1023, /*!< ALL : All SRAM banks (320K) powered ON value. */ -} PWRCTRL_MEMPWREN_SRAM_Enum; - -/* ============================================= PWRCTRL MEMPWREN DTCM [0..2] ============================================== */ -typedef enum { /*!< PWRCTRL_MEMPWREN_DTCM */ - PWRCTRL_MEMPWREN_DTCM_NONE = 0, /*!< NONE : Do not enable power to any DTCMs value. */ - PWRCTRL_MEMPWREN_DTCM_GROUP0DTCM0 = 1, /*!< GROUP0DTCM0 : Power ON only GROUP0_DTCM0 value. */ - PWRCTRL_MEMPWREN_DTCM_GROUP0DTCM1 = 2, /*!< GROUP0DTCM1 : Power ON only GROUP0_DTCM1 value. */ - PWRCTRL_MEMPWREN_DTCM_GROUP0 = 3, /*!< GROUP0 : Power ON only DTCMs in group0 value. */ - PWRCTRL_MEMPWREN_DTCM_GROUP1 = 4, /*!< GROUP1 : Power ON only DTCMs in group1 value. */ - PWRCTRL_MEMPWREN_DTCM_ALL = 7, /*!< ALL : Power ON all DTCMs value. */ -} PWRCTRL_MEMPWREN_DTCM_Enum; - -/* ===================================================== MEMPWRSTATUS ====================================================== */ -/* ===================================================== DEVPWRSTATUS ====================================================== */ -/* ======================================================= SRAMCTRL ======================================================== */ -/* ======================================== PWRCTRL SRAMCTRL SRAMLIGHTSLEEP [8..19] ======================================== */ -typedef enum { /*!< PWRCTRL_SRAMCTRL_SRAMLIGHTSLEEP */ - PWRCTRL_SRAMCTRL_SRAMLIGHTSLEEP_ALL = 255, /*!< ALL : Enable LIGHT SLEEP for ALL SRAMs value. */ - PWRCTRL_SRAMCTRL_SRAMLIGHTSLEEP_DIS = 0, /*!< DIS : Disables LIGHT SLEEP for ALL SRAMs value. */ -} PWRCTRL_SRAMCTRL_SRAMLIGHTSLEEP_Enum; - -/* ======================================= PWRCTRL SRAMCTRL SRAMMASTERCLKGATE [2..2] ======================================= */ -typedef enum { /*!< PWRCTRL_SRAMCTRL_SRAMMASTERCLKGATE */ - PWRCTRL_SRAMCTRL_SRAMMASTERCLKGATE_EN = 1, /*!< EN : Enable Master SRAM Clock Gate value. */ - PWRCTRL_SRAMCTRL_SRAMMASTERCLKGATE_DIS = 0, /*!< DIS : Disables Master SRAM Clock Gating value. */ -} PWRCTRL_SRAMCTRL_SRAMMASTERCLKGATE_Enum; - -/* ========================================== PWRCTRL SRAMCTRL SRAMCLKGATE [1..1] ========================================== */ -typedef enum { /*!< PWRCTRL_SRAMCTRL_SRAMCLKGATE */ - PWRCTRL_SRAMCTRL_SRAMCLKGATE_EN = 1, /*!< EN : Enable Individual SRAM Clock Gating value. */ - PWRCTRL_SRAMCTRL_SRAMCLKGATE_DIS = 0, /*!< DIS : Disables Individual SRAM Clock Gating value. */ -} PWRCTRL_SRAMCTRL_SRAMCLKGATE_Enum; - -/* ======================================================= ADCSTATUS ======================================================= */ -/* ========================================================= MISC ========================================================== */ -/* ============================================ PWRCTRL MISC MEMVRLPBLE [6..6] ============================================= */ -typedef enum { /*!< PWRCTRL_MISC_MEMVRLPBLE */ - PWRCTRL_MISC_MEMVRLPBLE_EN = 1, /*!< EN : Mem VR can go to lp mode even when BLE is powered on. value. */ - PWRCTRL_MISC_MEMVRLPBLE_DIS = 0, /*!< DIS : Mem VR will stay in active mode when BLE is powered on. - value. */ -} PWRCTRL_MISC_MEMVRLPBLE_Enum; - -/* =========================================== PWRCTRL MISC FORCEMEMVRADC [4..5] =========================================== */ -typedef enum { /*!< PWRCTRL_MISC_FORCEMEMVRADC */ - PWRCTRL_MISC_FORCEMEMVRADC_ACT = 2, /*!< ACT : In this mode if all the other domains but ADC are powered - down, mem VR will stay in ACT mode. value. */ - PWRCTRL_MISC_FORCEMEMVRADC_LP = 1, /*!< LP : In this mode if all the other domains but ADC are powered - down, mem VR will stay in LP mode. value. */ - PWRCTRL_MISC_FORCEMEMVRADC_DIS = 0, /*!< DIS : In this mode if all the other domains but ADC are powered - down, mem VR will duty cycle between active and LP modes - depending on ADC sampling. value. */ -} PWRCTRL_MISC_FORCEMEMVRADC_Enum; - -/* ============================================ PWRCTRL MISC SIMOBUCKEN [0..0] ============================================= */ -typedef enum { /*!< PWRCTRL_MISC_SIMOBUCKEN */ - PWRCTRL_MISC_SIMOBUCKEN_EN = 1, /*!< EN : Enable the SIMO Buck value. */ - PWRCTRL_MISC_SIMOBUCKEN_DIS = 0, /*!< DIS : Disable the SIMO Buck value. */ -} PWRCTRL_MISC_SIMOBUCKEN_Enum; - -/* ===================================================== DEVPWREVENTEN ===================================================== */ -/* ======================================= PWRCTRL DEVPWREVENTEN BURSTEVEN [31..31] ======================================== */ -typedef enum { /*!< PWRCTRL_DEVPWREVENTEN_BURSTEVEN */ - PWRCTRL_DEVPWREVENTEN_BURSTEVEN_EN = 1, /*!< EN : Enable BURST status event value. */ - PWRCTRL_DEVPWREVENTEN_BURSTEVEN_DIS = 0, /*!< DIS : Disable BURST status event value. */ -} PWRCTRL_DEVPWREVENTEN_BURSTEVEN_Enum; - -/* ==================================== PWRCTRL DEVPWREVENTEN BURSTFEATUREEVEN [30..30] ==================================== */ -typedef enum { /*!< PWRCTRL_DEVPWREVENTEN_BURSTFEATUREEVEN */ - PWRCTRL_DEVPWREVENTEN_BURSTFEATUREEVEN_EN = 1,/*!< EN : Enable BURSTFEATURE status event value. */ - PWRCTRL_DEVPWREVENTEN_BURSTFEATUREEVEN_DIS = 0,/*!< DIS : Disable BURSTFEATURE status event value. */ -} PWRCTRL_DEVPWREVENTEN_BURSTFEATUREEVEN_Enum; - -/* ===================================== PWRCTRL DEVPWREVENTEN BLEFEATUREEVEN [29..29] ===================================== */ -typedef enum { /*!< PWRCTRL_DEVPWREVENTEN_BLEFEATUREEVEN */ - PWRCTRL_DEVPWREVENTEN_BLEFEATUREEVEN_EN = 1, /*!< EN : Enable BLEFEATURE status event value. */ - PWRCTRL_DEVPWREVENTEN_BLEFEATUREEVEN_DIS = 0, /*!< DIS : Disable BLEFEATURE status event value. */ -} PWRCTRL_DEVPWREVENTEN_BLEFEATUREEVEN_Enum; - -/* ========================================= PWRCTRL DEVPWREVENTEN BLELEVEN [8..8] ========================================= */ -typedef enum { /*!< PWRCTRL_DEVPWREVENTEN_BLELEVEN */ - PWRCTRL_DEVPWREVENTEN_BLELEVEN_EN = 1, /*!< EN : Enable BLE power-on status event value. */ - PWRCTRL_DEVPWREVENTEN_BLELEVEN_DIS = 0, /*!< DIS : Disable BLE power-on status event value. */ -} PWRCTRL_DEVPWREVENTEN_BLELEVEN_Enum; - -/* ========================================= PWRCTRL DEVPWREVENTEN PDMEVEN [7..7] ========================================== */ -typedef enum { /*!< PWRCTRL_DEVPWREVENTEN_PDMEVEN */ - PWRCTRL_DEVPWREVENTEN_PDMEVEN_EN = 1, /*!< EN : Enable PDM power-on status event value. */ - PWRCTRL_DEVPWREVENTEN_PDMEVEN_DIS = 0, /*!< DIS : Disable PDM power-on status event value. */ -} PWRCTRL_DEVPWREVENTEN_PDMEVEN_Enum; - -/* ========================================= PWRCTRL DEVPWREVENTEN MSPIEVEN [6..6] ========================================= */ -typedef enum { /*!< PWRCTRL_DEVPWREVENTEN_MSPIEVEN */ - PWRCTRL_DEVPWREVENTEN_MSPIEVEN_EN = 1, /*!< EN : Enable MSPI power-on status event value. */ - PWRCTRL_DEVPWREVENTEN_MSPIEVEN_DIS = 0, /*!< DIS : Disable MSPI power-on status event value. */ -} PWRCTRL_DEVPWREVENTEN_MSPIEVEN_Enum; - -/* ========================================= PWRCTRL DEVPWREVENTEN ADCEVEN [5..5] ========================================== */ -typedef enum { /*!< PWRCTRL_DEVPWREVENTEN_ADCEVEN */ - PWRCTRL_DEVPWREVENTEN_ADCEVEN_EN = 1, /*!< EN : Enable ADC power-on status event value. */ - PWRCTRL_DEVPWREVENTEN_ADCEVEN_DIS = 0, /*!< DIS : Disable ADC power-on status event value. */ -} PWRCTRL_DEVPWREVENTEN_ADCEVEN_Enum; - -/* ========================================= PWRCTRL DEVPWREVENTEN HCPCEVEN [4..4] ========================================= */ -typedef enum { /*!< PWRCTRL_DEVPWREVENTEN_HCPCEVEN */ - PWRCTRL_DEVPWREVENTEN_HCPCEVEN_EN = 1, /*!< EN : Enable HCPC power-on status event value. */ - PWRCTRL_DEVPWREVENTEN_HCPCEVEN_DIS = 0, /*!< DIS : Disable HCPC power-on status event value. */ -} PWRCTRL_DEVPWREVENTEN_HCPCEVEN_Enum; - -/* ========================================= PWRCTRL DEVPWREVENTEN HCPBEVEN [3..3] ========================================= */ -typedef enum { /*!< PWRCTRL_DEVPWREVENTEN_HCPBEVEN */ - PWRCTRL_DEVPWREVENTEN_HCPBEVEN_EN = 1, /*!< EN : Enable HCPB power-on status event value. */ - PWRCTRL_DEVPWREVENTEN_HCPBEVEN_DIS = 0, /*!< DIS : Disable HCPB power-on status event value. */ -} PWRCTRL_DEVPWREVENTEN_HCPBEVEN_Enum; - -/* ========================================= PWRCTRL DEVPWREVENTEN HCPAEVEN [2..2] ========================================= */ -typedef enum { /*!< PWRCTRL_DEVPWREVENTEN_HCPAEVEN */ - PWRCTRL_DEVPWREVENTEN_HCPAEVEN_EN = 1, /*!< EN : Enable HCPA power-on status event value. */ - PWRCTRL_DEVPWREVENTEN_HCPAEVEN_DIS = 0, /*!< DIS : Disable HCPA power-on status event value. */ -} PWRCTRL_DEVPWREVENTEN_HCPAEVEN_Enum; - -/* ========================================= PWRCTRL DEVPWREVENTEN MCUHEVEN [1..1] ========================================= */ -typedef enum { /*!< PWRCTRL_DEVPWREVENTEN_MCUHEVEN */ - PWRCTRL_DEVPWREVENTEN_MCUHEVEN_EN = 1, /*!< EN : Enable MCHU power-on status event value. */ - PWRCTRL_DEVPWREVENTEN_MCUHEVEN_DIS = 0, /*!< DIS : Disable MCUH power-on status event value. */ -} PWRCTRL_DEVPWREVENTEN_MCUHEVEN_Enum; - -/* ========================================= PWRCTRL DEVPWREVENTEN MCULEVEN [0..0] ========================================= */ -typedef enum { /*!< PWRCTRL_DEVPWREVENTEN_MCULEVEN */ - PWRCTRL_DEVPWREVENTEN_MCULEVEN_EN = 1, /*!< EN : Enable MCUL power-on status event value. */ - PWRCTRL_DEVPWREVENTEN_MCULEVEN_DIS = 0, /*!< DIS : Disable MCUL power-on status event value. */ -} PWRCTRL_DEVPWREVENTEN_MCULEVEN_Enum; - -/* ===================================================== MEMPWREVENTEN ===================================================== */ -/* ======================================= PWRCTRL MEMPWREVENTEN CACHEB2EN [31..31] ======================================== */ -typedef enum { /*!< PWRCTRL_MEMPWREVENTEN_CACHEB2EN */ - PWRCTRL_MEMPWREVENTEN_CACHEB2EN_EN = 1, /*!< EN : Enable CACHE BANK 2 status event value. */ - PWRCTRL_MEMPWREVENTEN_CACHEB2EN_DIS = 0, /*!< DIS : Disable CACHE BANK 2 status event value. */ -} PWRCTRL_MEMPWREVENTEN_CACHEB2EN_Enum; - -/* ======================================= PWRCTRL MEMPWREVENTEN CACHEB0EN [30..30] ======================================== */ -typedef enum { /*!< PWRCTRL_MEMPWREVENTEN_CACHEB0EN */ - PWRCTRL_MEMPWREVENTEN_CACHEB0EN_EN = 1, /*!< EN : Enable CACHE BANK 0 status event value. */ - PWRCTRL_MEMPWREVENTEN_CACHEB0EN_DIS = 0, /*!< DIS : Disable CACHE BANK 0 status event value. */ -} PWRCTRL_MEMPWREVENTEN_CACHEB0EN_Enum; - -/* ======================================== PWRCTRL MEMPWREVENTEN FLASH1EN [14..14] ======================================== */ -typedef enum { /*!< PWRCTRL_MEMPWREVENTEN_FLASH1EN */ - PWRCTRL_MEMPWREVENTEN_FLASH1EN_EN = 1, /*!< EN : Enable FLASH status event value. */ - PWRCTRL_MEMPWREVENTEN_FLASH1EN_DIS = 0, /*!< DIS : Disables FLASH status event value. */ -} PWRCTRL_MEMPWREVENTEN_FLASH1EN_Enum; - -/* ======================================== PWRCTRL MEMPWREVENTEN FLASH0EN [13..13] ======================================== */ -typedef enum { /*!< PWRCTRL_MEMPWREVENTEN_FLASH0EN */ - PWRCTRL_MEMPWREVENTEN_FLASH0EN_EN = 1, /*!< EN : Enable FLASH status event value. */ - PWRCTRL_MEMPWREVENTEN_FLASH0EN_DIS = 0, /*!< DIS : Disables FLASH status event value. */ -} PWRCTRL_MEMPWREVENTEN_FLASH0EN_Enum; - -/* ========================================= PWRCTRL MEMPWREVENTEN SRAMEN [3..12] ========================================== */ -typedef enum { /*!< PWRCTRL_MEMPWREVENTEN_SRAMEN */ - PWRCTRL_MEMPWREVENTEN_SRAMEN_NONE = 0, /*!< NONE : Disable SRAM power-on status event value. */ - PWRCTRL_MEMPWREVENTEN_SRAMEN_GROUP0EN = 1, /*!< GROUP0EN : Enable SRAM group0 (0KB-32KB) power on status event - value. */ - PWRCTRL_MEMPWREVENTEN_SRAMEN_GROUP1EN = 2, /*!< GROUP1EN : Enable SRAM group1 (32KB-64KB) power on status event - value. */ - PWRCTRL_MEMPWREVENTEN_SRAMEN_GROUP2EN = 4, /*!< GROUP2EN : Enable SRAM group2 (64KB-96KB) power on status event - value. */ - PWRCTRL_MEMPWREVENTEN_SRAMEN_GROUP3EN = 8, /*!< GROUP3EN : Enable SRAM group3 (96KB-128KB) power on status event - value. */ - PWRCTRL_MEMPWREVENTEN_SRAMEN_GROUP4EN = 16, /*!< GROUP4EN : Enable SRAM group4 (128KB-160KB) power on status - event value. */ - PWRCTRL_MEMPWREVENTEN_SRAMEN_GROUP5EN = 32, /*!< GROUP5EN : Enable SRAM group5 (160KB-192KB) power on status - event value. */ - PWRCTRL_MEMPWREVENTEN_SRAMEN_GROUP6EN = 64, /*!< GROUP6EN : Enable SRAM group6 (192KB-224KB) power on status - event value. */ - PWRCTRL_MEMPWREVENTEN_SRAMEN_GROUP7EN = 128, /*!< GROUP7EN : Enable SRAM group7 (224KB-256KB) power on status - event value. */ - PWRCTRL_MEMPWREVENTEN_SRAMEN_GROUP8EN = 256, /*!< GROUP8EN : Enable SRAM group8 (256KB-288KB) power on status - event value. */ - PWRCTRL_MEMPWREVENTEN_SRAMEN_GROUP9EN = 512, /*!< GROUP9EN : Enable SRAM group9 (288KB-320KB) power on status - event value. */ -} PWRCTRL_MEMPWREVENTEN_SRAMEN_Enum; - -/* ========================================== PWRCTRL MEMPWREVENTEN DTCMEN [0..2] ========================================== */ -typedef enum { /*!< PWRCTRL_MEMPWREVENTEN_DTCMEN */ - PWRCTRL_MEMPWREVENTEN_DTCMEN_NONE = 0, /*!< NONE : Do not enable DTCM power-on status event value. */ - PWRCTRL_MEMPWREVENTEN_DTCMEN_GROUP0DTCM0EN = 1,/*!< GROUP0DTCM0EN : Enable GROUP0_DTCM0 power on status event value. */ - PWRCTRL_MEMPWREVENTEN_DTCMEN_GROUP0DTCM1EN = 2,/*!< GROUP0DTCM1EN : Enable GROUP0_DTCM1 power on status event value. */ - PWRCTRL_MEMPWREVENTEN_DTCMEN_GROUP0EN = 3, /*!< GROUP0EN : Enable DTCMs in group0 power on status event value. */ - PWRCTRL_MEMPWREVENTEN_DTCMEN_GROUP1EN = 4, /*!< GROUP1EN : Enable DTCMs in group1 power on status event value. */ - PWRCTRL_MEMPWREVENTEN_DTCMEN_ALL = 7, /*!< ALL : Enable all DTCM power on status event value. */ -} PWRCTRL_MEMPWREVENTEN_DTCMEN_Enum; - - - -/* =========================================================================================================================== */ -/* ================ RSTGEN ================ */ -/* =========================================================================================================================== */ - -/* ========================================================== CFG ========================================================== */ -/* ========================================================= SWPOI ========================================================= */ -/* ============================================= RSTGEN SWPOI SWPOIKEY [0..7] ============================================== */ -typedef enum { /*!< RSTGEN_SWPOI_SWPOIKEY */ - RSTGEN_SWPOI_SWPOIKEY_KEYVALUE = 27, /*!< KEYVALUE : Writing 0x1B key value generates a software POI reset. - value. */ -} RSTGEN_SWPOI_SWPOIKEY_Enum; - -/* ========================================================= SWPOR ========================================================= */ -/* ============================================= RSTGEN SWPOR SWPORKEY [0..7] ============================================== */ -typedef enum { /*!< RSTGEN_SWPOR_SWPORKEY */ - RSTGEN_SWPOR_SWPORKEY_KEYVALUE = 212, /*!< KEYVALUE : Writing 0xD4 key value generates a software POR reset. - value. */ -} RSTGEN_SWPOR_SWPORKEY_Enum; - -/* ======================================================== TPIURST ======================================================== */ -/* ========================================================= INTEN ========================================================= */ -/* ======================================================== INTSTAT ======================================================== */ -/* ======================================================== INTCLR ========================================================= */ -/* ======================================================== INTSET ========================================================= */ -/* ========================================================= STAT ========================================================== */ - - -/* =========================================================================================================================== */ -/* ================ RTC ================ */ -/* =========================================================================================================================== */ - -/* ======================================================== CTRLOW ========================================================= */ -/* ========================================================= CTRUP ========================================================= */ -/* =============================================== RTC CTRUP CTERR [31..31] ================================================ */ -typedef enum { /*!< RTC_CTRUP_CTERR */ - RTC_CTRUP_CTERR_NOERR = 0, /*!< NOERR : No read error occurred value. */ - RTC_CTRUP_CTERR_RDERR = 1, /*!< RDERR : Read error occurred value. */ -} RTC_CTRUP_CTERR_Enum; - -/* ================================================ RTC CTRUP CEB [28..28] ================================================= */ -typedef enum { /*!< RTC_CTRUP_CEB */ - RTC_CTRUP_CEB_DIS = 0, /*!< DIS : Disable the Century bit from changing value. */ - RTC_CTRUP_CEB_EN = 1, /*!< EN : Enable the Century bit to change value. */ -} RTC_CTRUP_CEB_Enum; - -/* ================================================= RTC CTRUP CB [27..27] ================================================= */ -typedef enum { /*!< RTC_CTRUP_CB */ - RTC_CTRUP_CB_2000 = 0, /*!< 2000 : Century is 2000s value. */ - RTC_CTRUP_CB_1900_2100 = 1, /*!< 1900_2100 : Century is 1900s/2100s value. */ -} RTC_CTRUP_CB_Enum; - -/* ======================================================== ALMLOW ========================================================= */ -/* ========================================================= ALMUP ========================================================= */ -/* ======================================================== RTCCTL ========================================================= */ -/* =============================================== RTC RTCCTL HR1224 [5..5] ================================================ */ -typedef enum { /*!< RTC_RTCCTL_HR1224 */ - RTC_RTCCTL_HR1224_24HR = 0, /*!< 24HR : Hours in 24 hour mode value. */ - RTC_RTCCTL_HR1224_12HR = 1, /*!< 12HR : Hours in 12 hour mode value. */ -} RTC_RTCCTL_HR1224_Enum; - -/* ================================================ RTC RTCCTL RSTOP [4..4] ================================================ */ -typedef enum { /*!< RTC_RTCCTL_RSTOP */ - RTC_RTCCTL_RSTOP_RUN = 0, /*!< RUN : Allow the RTC input clock to run value. */ - RTC_RTCCTL_RSTOP_STOP = 1, /*!< STOP : Stop the RTC input clock value. */ -} RTC_RTCCTL_RSTOP_Enum; - -/* ================================================= RTC RTCCTL RPT [1..3] ================================================= */ -typedef enum { /*!< RTC_RTCCTL_RPT */ - RTC_RTCCTL_RPT_DIS = 0, /*!< DIS : Alarm interrupt disabled value. */ - RTC_RTCCTL_RPT_YEAR = 1, /*!< YEAR : Interrupt every year value. */ - RTC_RTCCTL_RPT_MONTH = 2, /*!< MONTH : Interrupt every month value. */ - RTC_RTCCTL_RPT_WEEK = 3, /*!< WEEK : Interrupt every week value. */ - RTC_RTCCTL_RPT_DAY = 4, /*!< DAY : Interrupt every day value. */ - RTC_RTCCTL_RPT_HR = 5, /*!< HR : Interrupt every hour value. */ - RTC_RTCCTL_RPT_MIN = 6, /*!< MIN : Interrupt every minute value. */ - RTC_RTCCTL_RPT_SEC = 7, /*!< SEC : Interrupt every second/10th/100th value. */ -} RTC_RTCCTL_RPT_Enum; - -/* ================================================ RTC RTCCTL WRTC [0..0] ================================================= */ -typedef enum { /*!< RTC_RTCCTL_WRTC */ - RTC_RTCCTL_WRTC_DIS = 0, /*!< DIS : Counter writes are disabled value. */ - RTC_RTCCTL_WRTC_EN = 1, /*!< EN : Counter writes are enabled value. */ -} RTC_RTCCTL_WRTC_Enum; - -/* ========================================================= INTEN ========================================================= */ -/* ======================================================== INTSTAT ======================================================== */ -/* ======================================================== INTCLR ========================================================= */ -/* ======================================================== INTSET ========================================================= */ - - -/* =========================================================================================================================== */ -/* ================ SCARD ================ */ -/* =========================================================================================================================== */ - -/* ========================================================== SR =========================================================== */ -/* ========================================================== DR =========================================================== */ -/* ========================================================== SR1 ========================================================== */ -/* ====================================================== RETXCNTRMI ======================================================= */ -/* ======================================================== CLKCTRL ======================================================== */ - - -/* =========================================================================================================================== */ -/* ================ SECURITY ================ */ -/* =========================================================================================================================== */ - -/* ========================================================= CTRL ========================================================== */ -/* ============================================= SECURITY CTRL FUNCTION [4..7] ============================================= */ -typedef enum { /*!< SECURITY_CTRL_FUNCTION */ - SECURITY_CTRL_FUNCTION_CRC32 = 0, /*!< CRC32 : Perform CRC32 operation value. */ -} SECURITY_CTRL_FUNCTION_Enum; - -/* ======================================================== SRCADDR ======================================================== */ -/* ========================================================== LEN ========================================================== */ -/* ======================================================== RESULT ========================================================= */ -/* ======================================================= LOCKCTRL ======================================================== */ -/* ============================================ SECURITY LOCKCTRL SELECT [0..7] ============================================ */ -typedef enum { /*!< SECURITY_LOCKCTRL_SELECT */ - SECURITY_LOCKCTRL_SELECT_CUSTOMER_KEY = 1, /*!< CUSTOMER_KEY : Unlock Customer Key (access to top half of info0) - value. */ - SECURITY_LOCKCTRL_SELECT_NONE = 0, /*!< NONE : Lock Control should be set to NONE when not in use. value. */ -} SECURITY_LOCKCTRL_SELECT_Enum; - -/* ======================================================= LOCKSTAT ======================================================== */ -/* =========================================== SECURITY LOCKSTAT STATUS [0..31] ============================================ */ -typedef enum { /*!< SECURITY_LOCKSTAT_STATUS */ - SECURITY_LOCKSTAT_STATUS_CUSTOMER_KEY = 1, /*!< CUSTOMER_KEY : Customer Key is unlocked (access is granted to - top half of info0) value. */ - SECURITY_LOCKSTAT_STATUS_NONE = 0, /*!< NONE : No resources are unlocked value. */ -} SECURITY_LOCKSTAT_STATUS_Enum; - -/* ========================================================= KEY0 ========================================================== */ -/* ========================================================= KEY1 ========================================================== */ -/* ========================================================= KEY2 ========================================================== */ -/* ========================================================= KEY3 ========================================================== */ - - -/* =========================================================================================================================== */ -/* ================ UART0 ================ */ -/* =========================================================================================================================== */ - -/* ========================================================== DR =========================================================== */ -/* =============================================== UART0 DR OEDATA [11..11] ================================================ */ -typedef enum { /*!< UART0_DR_OEDATA */ - UART0_DR_OEDATA_NOERR = 0, /*!< NOERR : No error on UART OEDATA, overrun error indicator. value. */ - UART0_DR_OEDATA_ERR = 1, /*!< ERR : Error on UART OEDATA, overrun error indicator. value. */ -} UART0_DR_OEDATA_Enum; - -/* =============================================== UART0 DR BEDATA [10..10] ================================================ */ -typedef enum { /*!< UART0_DR_BEDATA */ - UART0_DR_BEDATA_NOERR = 0, /*!< NOERR : No error on UART BEDATA, break error indicator. value. */ - UART0_DR_BEDATA_ERR = 1, /*!< ERR : Error on UART BEDATA, break error indicator. value. */ -} UART0_DR_BEDATA_Enum; - -/* ================================================ UART0 DR PEDATA [9..9] ================================================= */ -typedef enum { /*!< UART0_DR_PEDATA */ - UART0_DR_PEDATA_NOERR = 0, /*!< NOERR : No error on UART PEDATA, parity error indicator. value. */ - UART0_DR_PEDATA_ERR = 1, /*!< ERR : Error on UART PEDATA, parity error indicator. value. */ -} UART0_DR_PEDATA_Enum; - -/* ================================================ UART0 DR FEDATA [8..8] ================================================= */ -typedef enum { /*!< UART0_DR_FEDATA */ - UART0_DR_FEDATA_NOERR = 0, /*!< NOERR : No error on UART FEDATA, framing error indicator. value. */ - UART0_DR_FEDATA_ERR = 1, /*!< ERR : Error on UART FEDATA, framing error indicator. value. */ -} UART0_DR_FEDATA_Enum; - -/* ========================================================== RSR ========================================================== */ -/* ================================================ UART0 RSR OESTAT [3..3] ================================================ */ -typedef enum { /*!< UART0_RSR_OESTAT */ - UART0_RSR_OESTAT_NOERR = 0, /*!< NOERR : No error on UART OESTAT, overrun error indicator. value. */ - UART0_RSR_OESTAT_ERR = 1, /*!< ERR : Error on UART OESTAT, overrun error indicator. value. */ -} UART0_RSR_OESTAT_Enum; - -/* ================================================ UART0 RSR BESTAT [2..2] ================================================ */ -typedef enum { /*!< UART0_RSR_BESTAT */ - UART0_RSR_BESTAT_NOERR = 0, /*!< NOERR : No error on UART BESTAT, break error indicator. value. */ - UART0_RSR_BESTAT_ERR = 1, /*!< ERR : Error on UART BESTAT, break error indicator. value. */ -} UART0_RSR_BESTAT_Enum; - -/* ================================================ UART0 RSR PESTAT [1..1] ================================================ */ -typedef enum { /*!< UART0_RSR_PESTAT */ - UART0_RSR_PESTAT_NOERR = 0, /*!< NOERR : No error on UART PESTAT, parity error indicator. value. */ - UART0_RSR_PESTAT_ERR = 1, /*!< ERR : Error on UART PESTAT, parity error indicator. value. */ -} UART0_RSR_PESTAT_Enum; - -/* ================================================ UART0 RSR FESTAT [0..0] ================================================ */ -typedef enum { /*!< UART0_RSR_FESTAT */ - UART0_RSR_FESTAT_NOERR = 0, /*!< NOERR : No error on UART FESTAT, framing error indicator. value. */ - UART0_RSR_FESTAT_ERR = 1, /*!< ERR : Error on UART FESTAT, framing error indicator. value. */ -} UART0_RSR_FESTAT_Enum; - -/* ========================================================== FR =========================================================== */ -/* ================================================= UART0 FR TXFE [7..7] ================================================== */ -typedef enum { /*!< UART0_FR_TXFE */ - UART0_FR_TXFE_XMTFIFO_EMPTY = 1, /*!< XMTFIFO_EMPTY : Transmit fifo is empty. value. */ -} UART0_FR_TXFE_Enum; - -/* ================================================= UART0 FR RXFF [6..6] ================================================== */ -typedef enum { /*!< UART0_FR_RXFF */ - UART0_FR_RXFF_RCVFIFO_FULL = 1, /*!< RCVFIFO_FULL : Receive fifo is full. value. */ -} UART0_FR_RXFF_Enum; - -/* ================================================= UART0 FR TXFF [5..5] ================================================== */ -typedef enum { /*!< UART0_FR_TXFF */ - UART0_FR_TXFF_XMTFIFO_FULL = 1, /*!< XMTFIFO_FULL : Transmit fifo is full. value. */ -} UART0_FR_TXFF_Enum; - -/* ================================================= UART0 FR RXFE [4..4] ================================================== */ -typedef enum { /*!< UART0_FR_RXFE */ - UART0_FR_RXFE_RCVFIFO_EMPTY = 1, /*!< RCVFIFO_EMPTY : Receive fifo is empty. value. */ -} UART0_FR_RXFE_Enum; - -/* ================================================= UART0 FR BUSY [3..3] ================================================== */ -typedef enum { /*!< UART0_FR_BUSY */ - UART0_FR_BUSY_BUSY = 1, /*!< BUSY : UART busy indicator. value. */ -} UART0_FR_BUSY_Enum; - -/* ================================================== UART0 FR DCD [2..2] ================================================== */ -typedef enum { /*!< UART0_FR_DCD */ - UART0_FR_DCD_DETECTED = 1, /*!< DETECTED : Data carrier detect detected. value. */ -} UART0_FR_DCD_Enum; - -/* ================================================== UART0 FR DSR [1..1] ================================================== */ -typedef enum { /*!< UART0_FR_DSR */ - UART0_FR_DSR_READY = 1, /*!< READY : Data set ready. value. */ -} UART0_FR_DSR_Enum; - -/* ================================================== UART0 FR CTS [0..0] ================================================== */ -typedef enum { /*!< UART0_FR_CTS */ - UART0_FR_CTS_CLEARTOSEND = 1, /*!< CLEARTOSEND : Clear to send is indicated. value. */ -} UART0_FR_CTS_Enum; - -/* ========================================================= ILPR ========================================================== */ -/* ========================================================= IBRD ========================================================== */ -/* ========================================================= FBRD ========================================================== */ -/* ========================================================= LCRH ========================================================== */ -/* ========================================================== CR =========================================================== */ -/* ================================================ UART0 CR CLKSEL [4..6] ================================================= */ -typedef enum { /*!< UART0_CR_CLKSEL */ - UART0_CR_CLKSEL_NOCLK = 0, /*!< NOCLK : No UART clock. This is the low power default. value. */ - UART0_CR_CLKSEL_24MHZ = 1, /*!< 24MHZ : 24 MHz clock. value. */ - UART0_CR_CLKSEL_12MHZ = 2, /*!< 12MHZ : 12 MHz clock. value. */ - UART0_CR_CLKSEL_6MHZ = 3, /*!< 6MHZ : 6 MHz clock. value. */ - UART0_CR_CLKSEL_3MHZ = 4, /*!< 3MHZ : 3 MHz clock. value. */ -} UART0_CR_CLKSEL_Enum; - -/* ========================================================= IFLS ========================================================== */ -/* ========================================================== IER ========================================================== */ -/* ========================================================== IES ========================================================== */ -/* ========================================================== MIS ========================================================== */ -/* ========================================================== IEC ========================================================== */ - - -/* =========================================================================================================================== */ -/* ================ VCOMP ================ */ -/* =========================================================================================================================== */ - -/* ========================================================== CFG ========================================================== */ -/* =============================================== VCOMP CFG LVLSEL [16..19] =============================================== */ -typedef enum { /*!< VCOMP_CFG_LVLSEL */ - VCOMP_CFG_LVLSEL_0P58V = 0, /*!< 0P58V : Set Reference input to 0.58 Volts. value. */ - VCOMP_CFG_LVLSEL_0P77V = 1, /*!< 0P77V : Set Reference input to 0.77 Volts. value. */ - VCOMP_CFG_LVLSEL_0P97V = 2, /*!< 0P97V : Set Reference input to 0.97 Volts. value. */ - VCOMP_CFG_LVLSEL_1P16V = 3, /*!< 1P16V : Set Reference input to 1.16 Volts. value. */ - VCOMP_CFG_LVLSEL_1P35V = 4, /*!< 1P35V : Set Reference input to 1.35 Volts. value. */ - VCOMP_CFG_LVLSEL_1P55V = 5, /*!< 1P55V : Set Reference input to 1.55 Volts. value. */ - VCOMP_CFG_LVLSEL_1P74V = 6, /*!< 1P74V : Set Reference input to 1.74 Volts. value. */ - VCOMP_CFG_LVLSEL_1P93V = 7, /*!< 1P93V : Set Reference input to 1.93 Volts. value. */ - VCOMP_CFG_LVLSEL_2P13V = 8, /*!< 2P13V : Set Reference input to 2.13 Volts. value. */ - VCOMP_CFG_LVLSEL_2P32V = 9, /*!< 2P32V : Set Reference input to 2.32 Volts. value. */ - VCOMP_CFG_LVLSEL_2P51V = 10, /*!< 2P51V : Set Reference input to 2.51 Volts. value. */ - VCOMP_CFG_LVLSEL_2P71V = 11, /*!< 2P71V : Set Reference input to 2.71 Volts. value. */ - VCOMP_CFG_LVLSEL_2P90V = 12, /*!< 2P90V : Set Reference input to 2.90 Volts. value. */ - VCOMP_CFG_LVLSEL_3P09V = 13, /*!< 3P09V : Set Reference input to 3.09 Volts. value. */ - VCOMP_CFG_LVLSEL_3P29V = 14, /*!< 3P29V : Set Reference input to 3.29 Volts. value. */ - VCOMP_CFG_LVLSEL_3P48V = 15, /*!< 3P48V : Set Reference input to 3.48 Volts. value. */ -} VCOMP_CFG_LVLSEL_Enum; - -/* ================================================= VCOMP CFG NSEL [8..9] ================================================= */ -typedef enum { /*!< VCOMP_CFG_NSEL */ - VCOMP_CFG_NSEL_VREFEXT1 = 0, /*!< VREFEXT1 : Use external reference 1 for reference input. value. */ - VCOMP_CFG_NSEL_VREFEXT2 = 1, /*!< VREFEXT2 : Use external reference 2 for reference input. value. */ - VCOMP_CFG_NSEL_VREFEXT3 = 2, /*!< VREFEXT3 : Use external reference 3 for reference input. value. */ - VCOMP_CFG_NSEL_DAC = 3, /*!< DAC : Use DAC output selected by LVLSEL for reference input. - value. */ -} VCOMP_CFG_NSEL_Enum; - -/* ================================================= VCOMP CFG PSEL [0..1] ================================================= */ -typedef enum { /*!< VCOMP_CFG_PSEL */ - VCOMP_CFG_PSEL_VDDADJ = 0, /*!< VDDADJ : Use VDDADJ for the positive input. value. */ - VCOMP_CFG_PSEL_VTEMP = 1, /*!< VTEMP : Use the temperature sensor output for the positive input. - Note: If this channel is selected for PSEL, the bandap - circuit required for temperature comparisons will automatically - turn on. The bandgap circuit requires 11us to stabalize. - value. */ - VCOMP_CFG_PSEL_VEXT1 = 2, /*!< VEXT1 : Use external voltage 0 for positive input. value. */ - VCOMP_CFG_PSEL_VEXT2 = 3, /*!< VEXT2 : Use external voltage 1 for positive input. value. */ -} VCOMP_CFG_PSEL_Enum; - -/* ========================================================= STAT ========================================================== */ -/* =============================================== VCOMP STAT PWDSTAT [1..1] =============================================== */ -typedef enum { /*!< VCOMP_STAT_PWDSTAT */ - VCOMP_STAT_PWDSTAT_POWERED_DOWN = 1, /*!< POWERED_DOWN : The voltage comparator is powered down. value. */ -} VCOMP_STAT_PWDSTAT_Enum; - -/* =============================================== VCOMP STAT CMPOUT [0..0] ================================================ */ -typedef enum { /*!< VCOMP_STAT_CMPOUT */ - VCOMP_STAT_CMPOUT_VOUT_LOW = 0, /*!< VOUT_LOW : The negative input of the comparator is greater than - the positive input. value. */ - VCOMP_STAT_CMPOUT_VOUT_HIGH = 1, /*!< VOUT_HIGH : The positive input of the comparator is greater - than the negative input. value. */ -} VCOMP_STAT_CMPOUT_Enum; - -/* ======================================================== PWDKEY ========================================================= */ -/* ============================================== VCOMP PWDKEY PWDKEY [0..31] ============================================== */ -typedef enum { /*!< VCOMP_PWDKEY_PWDKEY */ - VCOMP_PWDKEY_PWDKEY_Key = 55, /*!< Key : Key value. */ -} VCOMP_PWDKEY_PWDKEY_Enum; - -/* ========================================================= INTEN ========================================================= */ -/* ======================================================== INTSTAT ======================================================== */ -/* ======================================================== INTCLR ========================================================= */ -/* ======================================================== INTSET ========================================================= */ - - -/* =========================================================================================================================== */ -/* ================ WDT ================ */ -/* =========================================================================================================================== */ - -/* ========================================================== CFG ========================================================== */ -/* ================================================ WDT CFG CLKSEL [24..26] ================================================ */ -typedef enum { /*!< WDT_CFG_CLKSEL */ - WDT_CFG_CLKSEL_OFF = 0, /*!< OFF : Low Power Mode. This setting disables the watch dog timer. - value. */ - WDT_CFG_CLKSEL_128HZ = 1, /*!< 128HZ : 128 Hz LFRC clock. value. */ - WDT_CFG_CLKSEL_16HZ = 2, /*!< 16HZ : 16 Hz LFRC clock. value. */ - WDT_CFG_CLKSEL_1HZ = 3, /*!< 1HZ : 1 Hz LFRC clock. value. */ - WDT_CFG_CLKSEL_1_16HZ = 4, /*!< 1_16HZ : 1/16th Hz LFRC clock. value. */ -} WDT_CFG_CLKSEL_Enum; - -/* ========================================================= RSTRT ========================================================= */ -/* ================================================ WDT RSTRT RSTRT [0..7] ================================================= */ -typedef enum { /*!< WDT_RSTRT_RSTRT */ - WDT_RSTRT_RSTRT_KEYVALUE = 178, /*!< KEYVALUE : This is the key value to write to WDTRSTRT to restart - the WDT. This is a write only register. value. */ -} WDT_RSTRT_RSTRT_Enum; - -/* ========================================================= LOCK ========================================================== */ -/* ================================================= WDT LOCK LOCK [0..7] ================================================== */ -typedef enum { /*!< WDT_LOCK_LOCK */ - WDT_LOCK_LOCK_KEYVALUE = 58, /*!< KEYVALUE : This is the key value to write to WDTLOCK to lock - the WDT. value. */ -} WDT_LOCK_LOCK_Enum; - -/* ========================================================= COUNT ========================================================= */ -/* ========================================================= INTEN ========================================================= */ -/* ======================================================== INTSTAT ======================================================== */ -/* ======================================================== INTCLR ========================================================= */ -/* ======================================================== INTSET ========================================================= */ - -/** @} */ /* End of group EnumValue_peripherals */ - - -#ifdef __cplusplus -} -#endif - -#endif /* APOLLO3_H */ - - -/** @} */ /* End of group apollo3 */ - -/** @} */ /* End of group Ambiq Micro */ diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k.cc index 5246e00296..561c46ce33 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k.cc @@ -21,8 +21,8 @@ limitations under the License. #include "tensorflow/lite/experimental/micro/testing/micro_test.h" extern "C" { -#include "tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/system_apollo3.h" -#include "tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/apollo3.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/apollo3_ext/system_apollo3.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/apollo3_ext/apollo3.h" } #define output_data_size 43 diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/system_apollo3.c b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/system_apollo3.c deleted file mode 100755 index c7f1573563..0000000000 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/system_apollo3.c +++ /dev/null @@ -1,116 +0,0 @@ -//***************************************************************************** -// -//! @file system_apollo3.c -//! -//! @brief Ambiq Micro Apollo3 MCU specific functions. -// -//***************************************************************************** - -/* - * Copyright (C) 2015-2017, Ambiq Micro - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of itscontributors may be used to endorse - * or promote products derived from thissoftware without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @file apollo3.h - * @brief CMSIS HeaderFile - * @version 1.0 - * @date 10. August 2018 - * @note Generated by SVDConv V3.3.18 on Friday, 10.08.2018 16:52:09 - * from File 'apollo3.svd', - * last modified on Friday, 10.08.2018 20:01:31 - */ - - -#include -#include "system_apollo3.h" -#include "apollo3.h" - -//***************************************************************************** -// -// Defines -// -//***************************************************************************** - -// -// Clocks -// -#define __HSI (6000000UL) -#define __XTAL (32768UL) // Crystal Oscillator frequency -#define __SYS_OSC_CLK (48000000) // Main oscillator frequency -#define __SYSTEM_CLOCK (1*__SYS_OSC_CLK) - -// -// Initialize SystemCoreClock with the system core clock frequency value -// achieved after system intitialization. -// This means system core clock frequency after call to SystemInit() -// -uint32_t SystemCoreClock = __SYSTEM_CLOCK; // System Clock Frequency (Core Clock) - -//***************************************************************************** -// -//! @brief Set the global clock frequncy. -//! -//! This function sets the global clock frequency. -//! -//! @return None. -// -//***************************************************************************** -void -SystemCoreClockUpdate(void) -{ - // - // Calculate the system frequency based upon the current register settings. - // This function can be used to retrieve the system core clock frequeny - // after user changed register sittings. - // - SystemCoreClock = __SYS_OSC_CLK / (CLKGEN->CCTRL_b.CORESEL + 1); -} - -//***************************************************************************** -// -//! @brief Initialize the system. -//! -//! This function sets up the microcontroller system. -//! -//! @return None. -// -//***************************************************************************** -void -SystemInit(void) -{ - // - // Initialize the system - // Do not use global variables because this function is called before - // reaching pre-main. RW section maybe overwritten afterwards. - // - SystemCoreClock = __SYSTEM_CLOCK; - - CLKGEN->CLKKEY = 0x47; // Enable write to CCTRL - CLKGEN->CCTRL_b.CORESEL = 0; // Div by 1 for 48MHz - CLKGEN->CLKKEY = 0; // Disable write to CCTRL -} - diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/system_apollo3.h b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/system_apollo3.h deleted file mode 100755 index 7fd9b51d5a..0000000000 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/system_apollo3.h +++ /dev/null @@ -1,72 +0,0 @@ -//***************************************************************************** -// -//! @file system_Apollo3.h -//! -//! @brief Ambiq Micro Apollo3 MCU specific functions. -// -//***************************************************************************** - -/* - * Copyright (C) 2015-2017, Ambiq Micro - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of itscontributors may be used to endorse - * or promote products derived from thissoftware without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @file apollo3.h - * @brief CMSIS HeaderFile - * @version 1.0 - * @date 10. August 2018 - * @note Generated by SVDConv V3.3.18 on Friday, 10.08.2018 16:52:09 - * from File 'apollo3.svd', - * last modified on Friday, 10.08.2018 20:01:31 - */ - - -#ifndef SYSTEM_APOLLO3_H -#define SYSTEM_APOLLO3_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -extern uint32_t SystemCoreClock; // System Clock Frequency (Core Clock) - -//***************************************************************************** -// -// External function definitions -// -//***************************************************************************** -extern void SystemInit (void); -extern void SystemCoreClockUpdate (void); - -#ifdef __cplusplus -} -#endif - -#endif // SYSTEM_APOLLO3_H - diff --git a/tensorflow/lite/experimental/micro/tools/make/download_dependencies.sh b/tensorflow/lite/experimental/micro/tools/make/download_dependencies.sh index 6749858bdb..ba1fbb7fcf 100755 --- a/tensorflow/lite/experimental/micro/tools/make/download_dependencies.sh +++ b/tensorflow/lite/experimental/micro/tools/make/download_dependencies.sh @@ -21,6 +21,7 @@ cd "$SCRIPT_DIR/../../../../../.." DOWNLOADS_DIR=tensorflow/lite/experimental/micro/tools/make/downloads BZL_FILE_PATH=tensorflow/workspace.bzl +AP3_DOWNLOADS_DIR=tensorflow/lite/experimental/micro/examples/micro_speech # Ensure it is being run from repo root if [ ! -f $BZL_FILE_PATH ]; then @@ -33,6 +34,8 @@ GEMMLOWP_URL="https://github.com/google/gemmlowp/archive/719139ce755a0f31cbf1c37 FLATBUFFERS_URL="https://github.com/google/flatbuffers/archive/1f5eae5d6a135ff6811724f6c57f911d1f46bb15.tar.gz" CMSIS_URL="https://github.com/ARM-software/CMSIS_5/archive/5.4.0.zip" STM32_BARE_LIB_URL="https://github.com/google/stm32_bare_lib/archive/c07d611fb0af58450c5a3e0ab4d52b47f99bc82d.zip" +AP3_URL="https://github.com/AmbiqMicro/TFLiteMicro_Apollo3/archive/dfbcef9a57276c087d95aab7cb234f1d4c9eaaba.zip" +CUST_CMSIS_URL="https://github.com/AmbiqMicro/TFLiteMicro_CustCMSIS/archive/8f63966c5692e6a3a83956efd2e4aed77c4c9949.zip" download_and_extract() { local usage="Usage: download_and_extract URL DIR" @@ -69,5 +72,7 @@ download_and_extract "${GEMMLOWP_URL}" "${DOWNLOADS_DIR}/gemmlowp" download_and_extract "${FLATBUFFERS_URL}" "${DOWNLOADS_DIR}/flatbuffers" download_and_extract "${CMSIS_URL}" "${DOWNLOADS_DIR}/cmsis" download_and_extract "${STM32_BARE_LIB_URL}" "${DOWNLOADS_DIR}/stm32_bare_lib" +download_and_extract "${AP3_URL}" "${AP3_DOWNLOADS_DIR}/apollo3_ext" +download_and_extract "${CUST_CMSIS_URL}" "${AP3_DOWNLOADS_DIR}/CMSIS_ext" echo "download_dependencies.sh completed successfully." >&2 diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc index 3db4367a17..68ef79ea02 100644 --- a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc +++ b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc @@ -99,8 +99,10 @@ ifeq ($(TARGET), apollo3evb) $(CMSIS_SRC_DIR)/StatisticsFunctions/arm_mean_q15.c \ $(CMSIS_SRC_DIR)/StatisticsFunctions/arm_max_q7.c + AP3_EXT_MICRO_DIR := tensorflow/lite/experimental/micro/examples/micro_speech/apollo3_ext AP3_MICRO_DIR := tensorflow/lite/experimental/micro/examples/micro_speech/apollo3 CMSIS_DIR := tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS + CMSIS_EXT_DIR := tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS_ext MICRO_SPEECH_TEST_SRCS += \ $(AP3_MICRO_DIR)/_main.c @@ -108,16 +110,16 @@ ifeq ($(TARGET), apollo3evb) PREPROCESSOR_1K_CMSIS_TEST_SRCS := \ $(PREPROCESSOR_1K_SRCS) \ $(CMSIS_DIR)/preprocessor.cc \ - $(CMSIS_DIR)/arm_cmplx_mag_squared_q10p6.c \ + $(CMSIS_EXT_DIR)/arm_cmplx_mag_squared_q10p6.c \ $(CMSIS_DIR)/hanning.c \ - $(AP3_MICRO_DIR)/system_apollo3.c \ + $(AP3_EXT_MICRO_DIR)/system_apollo3.c \ $(AP3_MICRO_DIR)/_main.c \ $(CMSIS_SRCS) PREPROCESSOR_1K_MICRO_TEST_SRCS := \ $(PREPROCESSOR_1K_SRCS) \ - tensorflow/lite/experimental/micro/examples/micro_speech/fixed_point/preprocessor.cc \ - tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/system_apollo3.c \ + $(AP3_MICRO_DIR)/../fixed_point/preprocessor.cc \ + $(AP3_EXT_MICRO_DIR)/system_apollo3.c \ $(AP3_MICRO_DIR)/_main.c PUSHBUTTON_MICRO_SPEECH_TEST_SRCS := \ @@ -132,7 +134,7 @@ ifeq ($(TARGET), apollo3evb) $(AP3_MICRO_DIR)/pushbutton_test.cc \ $(AP3_MICRO_DIR)/../tiny_conv_model_data.cc \ $(CMSIS_DIR)/preprocessor.cc \ - $(CMSIS_DIR)/arm_cmplx_mag_squared_q10p6.c \ + $(CMSIS_EXT_DIR)/arm_cmplx_mag_squared_q10p6.c \ $(CMSIS_DIR)/hanning.c \ $(APOLLO3_SDK)/devices/am_devices_led.c \ $(CMSIS_SRCS) -- GitLab From c5720d1626c3c7e85069b0436bffacc8c6ca8122 Mon Sep 17 00:00:00 2001 From: Yves-Noel Weweler Date: Wed, 19 Dec 2018 11:54:28 +0100 Subject: [PATCH 059/622] Update testBucketDropReminder test case See #24071 --- .../bucket_by_sequence_length_test.py | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py b/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py index e0978676fd..a95d8e1049 100644 --- a/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py @@ -76,11 +76,12 @@ def _get_record_shape(sparse): @test_util.run_all_in_graph_and_eager_modes class BucketBySequenceLengthTest(test_base.DatasetTestBase, parameterized.TestCase): + # TODO(b/117581999): add eager coverage. @parameterized.named_parameters( ("WithoutPadding", True), ("WithPadding", False), ) - def testBucketDropReminder(self, param_no_padding): + def testSkipEagerBucketDropReminder(self, param_no_padding): boundaries = [10, 20, 30] batch_sizes = [10, 8, 4, 2] @@ -142,14 +143,15 @@ class BucketBySequenceLengthTest(test_base.DatasetTestBase, parameterized.TestCa batch_sizes, no_padding=no_padding, drop_remainder=True)) - batch, = dataset.make_one_shot_iterator().get_next() - - with self.cached_session() as sess: - batches = [] - for _ in range(n_expected_batches): - batches.append(self.evaluate(batch)) - with self.assertRaises(errors.OutOfRangeError): - self.evaluate(batch) + + get_next = self.getNext(dataset) + batches = [] + for _ in range(n_expected_batches): + batch, = self.evaluate(get_next()) + batches.append(batch) + + with self.assertRaises(errors.OutOfRangeError): + self.evaluate(get_next()) generated_lengths = [] -- GitLab From 9e92c2fe264debad1548554182dcc9af06d2e6ee Mon Sep 17 00:00:00 2001 From: Trevor Morris Date: Wed, 19 Dec 2018 13:48:25 -0800 Subject: [PATCH 060/622] Fix bug with renaming output bindings --- tensorflow/contrib/tensorrt/BUILD | 1 + .../contrib/tensorrt/convert/convert_nodes.cc | 17 +++++ .../tensorrt/test/identity_output_test.py | 72 +++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 tensorflow/contrib/tensorrt/test/identity_output_test.py diff --git a/tensorflow/contrib/tensorrt/BUILD b/tensorflow/contrib/tensorrt/BUILD index 784acce444..3e8c486d8c 100644 --- a/tensorflow/contrib/tensorrt/BUILD +++ b/tensorflow/contrib/tensorrt/BUILD @@ -491,6 +491,7 @@ cuda_py_tests( "test/binary_tensor_weight_broadcast_test.py", "test/concatenation_test.py", "test/const_broadcast_test.py", + "test/identity_output_test.py", "test/manual_test.py", "test/memory_alignment_test.py", "test/multi_connection_neighbor_engine_test.py", diff --git a/tensorflow/contrib/tensorrt/convert/convert_nodes.cc b/tensorflow/contrib/tensorrt/convert/convert_nodes.cc index adf8831b96..729afe6c64 100644 --- a/tensorflow/contrib/tensorrt/convert/convert_nodes.cc +++ b/tensorflow/contrib/tensorrt/convert/convert_nodes.cc @@ -879,6 +879,8 @@ Status Converter::ConvertNode(const NodeDef& node_def) { // We need to check the name before setting it. If the input is one of the // engine input, setting the name here will overwrite engine input // bindings which will cause runtime error. + // TODO(tmorris): Remove this work-around once we use TRT's IIdentityLayer + // in ConvertIdentity. if (output.is_tensor()) { const char* tensor_name = output.tensor()->getName(); if (!tensorflow::str_util::StartsWith(tensor_name, kInputPHName)) { @@ -939,6 +941,21 @@ Status Converter::RenameAndMarkOutputTensors( if (tensor == nullptr) { return errors::NotFound("Output tensor not found: ", output.first); } + // Check if this tensor has already been marked as an output. + // ConvertIdentity can cause the same tensor to be repeated in + // output_tensors, which can cause us to overwrite the name of the output + // tensor binding. For example, if we rename OutputPH_0 to OutputPH_1 then + // we won't be able to locate OutputPH_0 during runtime. To fix this, + // duplicate the tensor using no-op shuffle. + // TODO(tmorris): Remove this work-around once we use TRT's IIdentityLayer + // in ConvertIdentity. + if (tensorflow::str_util::StartsWith(tensor->getName(), kOutputPHName)) { + nvinfer1::IShuffleLayer* layer = network()->addShuffle(*tensor); + TFTRT_RETURN_ERROR_IF_NULLPTR( + layer, StrCat("Output Copy for ", tensor->getName())); + MarkQuantizationRangesAsInferrable(tensor, layer->getOutput(0)); + tensor = layer->getOutput(0); + } tensor->setName(output.second.c_str()); VLOG(1) << "Marking output tensor " << output.first << ", as output tensor " << output.second; diff --git a/tensorflow/contrib/tensorrt/test/identity_output_test.py b/tensorflow/contrib/tensorrt/test/identity_output_test.py new file mode 100644 index 0000000000..391434ba83 --- /dev/null +++ b/tensorflow/contrib/tensorrt/test/identity_output_test.py @@ -0,0 +1,72 @@ +# Copyright 2018 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. +# ============================================================================== +"""Model script to test TF-TensorRT integration.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +from tensorflow.contrib.tensorrt.test import tf_trt_integration_test_base as trt_test +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import gen_array_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import nn +from tensorflow.python.platform import test + + +class IdentityTest(trt_test.TfTrtIntegrationTestBase): + + def _ConstOp(self, shape): + return constant_op.constant(np.random.randn(*shape), dtype=dtypes.float32) + + def GetParams(self): + """Testing conversion of BiasAdd MatMul in TF-TRT conversion.""" + input_name = "input" + input_dims = [100, 32] + g = ops.Graph() + with g.as_default(): + x = array_ops.placeholder( + dtype=dtypes.float32, shape=input_dims, name=input_name) + + b = self._ConstOp((32, 4)) + x1 = math_ops.matmul(x, b) + b = self._ConstOp((1, 4)) + x1 = x1 + b + + out1 = array_ops.identity(x1, name='output1') + out2 = array_ops.identity(x1, name='output2') + iden1 = array_ops.identity(x1) + out3 = array_ops.identity(iden1, name='output3') + + return trt_test.TfTrtIntegrationTestParams( + gdef=g.as_graph_def(), + input_names=[input_name], + input_dims=[input_dims], + output_names=['output1', 'output2', 'output3'], + expected_output_dims=[(100, 4), (100, 4), (100, 4)]) + + + def ExpectedEnginesToBuild(self, run_params): + """Return the expected engines to build.""" + return ["TRTEngineOp_0"] + + +if __name__ == "__main__": + test.main() -- GitLab From cfe7df2cf064f9eacdf8ab30574ebe9ec6438544 Mon Sep 17 00:00:00 2001 From: Trevor Morris Date: Wed, 19 Dec 2018 14:14:28 -0800 Subject: [PATCH 061/622] Update comment in test --- tensorflow/contrib/tensorrt/test/identity_output_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/contrib/tensorrt/test/identity_output_test.py b/tensorflow/contrib/tensorrt/test/identity_output_test.py index 391434ba83..1aa05999bd 100644 --- a/tensorflow/contrib/tensorrt/test/identity_output_test.py +++ b/tensorflow/contrib/tensorrt/test/identity_output_test.py @@ -37,7 +37,7 @@ class IdentityTest(trt_test.TfTrtIntegrationTestBase): return constant_op.constant(np.random.randn(*shape), dtype=dtypes.float32) def GetParams(self): - """Testing conversion of BiasAdd MatMul in TF-TRT conversion.""" + """Testing engine with the same tensor repeated as output via identity.""" input_name = "input" input_dims = [100, 32] g = ops.Graph() -- GitLab From f03cf64b1295ea4e9e431a7a524cc23b2c1f3306 Mon Sep 17 00:00:00 2001 From: Trevor Morris Date: Wed, 19 Dec 2018 15:10:58 -0800 Subject: [PATCH 062/622] Explain usage of shuffle layer --- tensorflow/contrib/tensorrt/convert/convert_nodes.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/tensorflow/contrib/tensorrt/convert/convert_nodes.cc b/tensorflow/contrib/tensorrt/convert/convert_nodes.cc index 729afe6c64..2a402fe699 100644 --- a/tensorflow/contrib/tensorrt/convert/convert_nodes.cc +++ b/tensorflow/contrib/tensorrt/convert/convert_nodes.cc @@ -950,6 +950,7 @@ Status Converter::RenameAndMarkOutputTensors( // TODO(tmorris): Remove this work-around once we use TRT's IIdentityLayer // in ConvertIdentity. if (tensorflow::str_util::StartsWith(tensor->getName(), kOutputPHName)) { + // Using shuffle layer for identity by not setting reshape or transpose. nvinfer1::IShuffleLayer* layer = network()->addShuffle(*tensor); TFTRT_RETURN_ERROR_IF_NULLPTR( layer, StrCat("Output Copy for ", tensor->getName())); -- GitLab From c75023169590ef0534579758285d6a1d8ad54ad2 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Wed, 19 Dec 2018 23:38:51 +0000 Subject: [PATCH 063/622] Replace deprecated FastGFile with GFile FastGFile has been deprecated and replaced with GFile, though the example in speech_commands still uses FastGFile. This fix fix the issue to remove the deprecated warning: ``` WARNING:tensorflow:From :1: __init__ (from tensorflow.python.platform.gfile) is deprecated and will be removed in a future version. Instructions for updating: Use tf.gfile.GFile. ``` Signed-off-by: Yong Tang --- tensorflow/examples/speech_commands/label_wav.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/examples/speech_commands/label_wav.py b/tensorflow/examples/speech_commands/label_wav.py index 0017aec3a5..eb8323454c 100644 --- a/tensorflow/examples/speech_commands/label_wav.py +++ b/tensorflow/examples/speech_commands/label_wav.py @@ -45,7 +45,7 @@ FLAGS = None def load_graph(filename): """Unpersists graph from file as default graph.""" - with tf.gfile.FastGFile(filename, 'rb') as f: + with tf.gfile.GFile(filename, 'rb') as f: graph_def = tf.GraphDef() graph_def.ParseFromString(f.read()) tf.import_graph_def(graph_def, name='') -- GitLab From 4696da4bf9a586cf250b32a062da833af3ffd4a9 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Wed, 19 Dec 2018 23:41:52 +0000 Subject: [PATCH 064/622] Also replace FastGFile to GFile in label_wav_dir.py Signed-off-by: Yong Tang --- tensorflow/examples/speech_commands/label_wav_dir.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/examples/speech_commands/label_wav_dir.py b/tensorflow/examples/speech_commands/label_wav_dir.py index a34db512dd..2e1890c3e8 100644 --- a/tensorflow/examples/speech_commands/label_wav_dir.py +++ b/tensorflow/examples/speech_commands/label_wav_dir.py @@ -46,7 +46,7 @@ FLAGS = None def load_graph(filename): """Unpersists graph from file as default graph.""" - with tf.gfile.FastGFile(filename, 'rb') as f: + with tf.gfile.GFile(filename, 'rb') as f: graph_def = tf.GraphDef() graph_def.ParseFromString(f.read()) tf.import_graph_def(graph_def, name='') -- GitLab From 0f10d9fcd0c47b497d1bd5069f28d21f94fedc60 Mon Sep 17 00:00:00 2001 From: Trevor Morris Date: Wed, 19 Dec 2018 15:56:19 -0800 Subject: [PATCH 065/622] Improve comment --- tensorflow/contrib/tensorrt/test/identity_output_test.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tensorflow/contrib/tensorrt/test/identity_output_test.py b/tensorflow/contrib/tensorrt/test/identity_output_test.py index 1aa05999bd..2be9da9ede 100644 --- a/tensorflow/contrib/tensorrt/test/identity_output_test.py +++ b/tensorflow/contrib/tensorrt/test/identity_output_test.py @@ -12,7 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Model script to test TF-TensorRT integration.""" +"""This test checks a situation where the same tensor is considered as an output +multiple times because it has been duplicated by 2+ indentity ops. Previously, +the tensor would be renamed multiple times, overwriting the output binding name +which resulted in a runtime error when the binding would not be found. +""" from __future__ import absolute_import from __future__ import division -- GitLab From cc3e7217aeb92d289834fa5409f37a59f5918777 Mon Sep 17 00:00:00 2001 From: Arpit Shah Date: Wed, 19 Dec 2018 18:13:39 -0600 Subject: [PATCH 066/622] Fixed header --- .../examples/micro_speech/CMSIS/create_constants.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/create_constants.py b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/create_constants.py index c7cf8bf61b..732ae5f7f5 100755 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/create_constants.py +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/create_constants.py @@ -1,16 +1,17 @@ -# SPDX-License-Identifier: Apache-2.0 +# Copyright 2018 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. +# 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 # -# www.apache.org/licenses/LICENSE-2.0 +# 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. +# 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. +# ============================================================================== import soundfile as sf import numpy as np -- GitLab From 915b8783a05b0da7c30ba36531ce03c811930852 Mon Sep 17 00:00:00 2001 From: Clayne Robison Date: Wed, 19 Dec 2018 19:13:27 -0700 Subject: [PATCH 067/622] [Intel MKL] Remove TensorFlow lite test from the public CI. MKL does not support TF lite. --- tensorflow/tools/ci_build/linux/cpu/run_mkl.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/tools/ci_build/linux/cpu/run_mkl.sh b/tensorflow/tools/ci_build/linux/cpu/run_mkl.sh index 7be5f454ec..a8b73cbe0c 100755 --- a/tensorflow/tools/ci_build/linux/cpu/run_mkl.sh +++ b/tensorflow/tools/ci_build/linux/cpu/run_mkl.sh @@ -36,4 +36,4 @@ yes "" | $PYTHON_BIN_PATH configure.py bazel test --test_tag_filters=-no_oss,-oss_serial,-gpu,-benchmark-test --test_lang_filters=cc,py -k \ --jobs=${N_JOBS} --test_timeout 300,450,1200,3600 --build_tests_only \ --config=mkl --test_env=KMP_BLOCKTIME=0 --config=opt --test_output=errors -- \ - //tensorflow/... -//tensorflow/compiler/... -//tensorflow/contrib/... + //tensorflow/... -//tensorflow/compiler/... -//tensorflow/contrib/... -//tensorflow/lite/... -- GitLab From f4867d3e4fecdb1ca8a445addad383aa0292a3b3 Mon Sep 17 00:00:00 2001 From: Sami Kama Date: Wed, 19 Dec 2018 19:40:22 -0800 Subject: [PATCH 068/622] Initialize only CPU devices in optimize_dataset_op --- tensorflow/core/grappler/grappler_item_builder.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tensorflow/core/grappler/grappler_item_builder.cc b/tensorflow/core/grappler/grappler_item_builder.cc index 9224ee7849..a984efd10d 100644 --- a/tensorflow/core/grappler/grappler_item_builder.cc +++ b/tensorflow/core/grappler/grappler_item_builder.cc @@ -103,7 +103,11 @@ Status OptimizeGraph(const GraphDef& graph_def_arg, GraphDef* output_graph_def, // Instantiate all variables for function library runtime creation. std::vector> devices; - TF_RETURN_IF_ERROR(DeviceFactory::AddDevices( + // Only CPU device is used so instead of calling DeviceFactory::AddDevices() + // with dummy session config, which will conflict with user defined options and + // create unwanted devices, call cpu_factory->CreateDevices() to get CPU only devices. + DeviceFactory* cpu_factory = DeviceFactory::GetFactory("CPU"); + TF_RETURN_IF_ERROR(cpu_factory->CreateDevices( options, "/job:localhost/replica:0/task:0", &devices)); Device* cpu_device = devices[0].get(); std::unique_ptr dvc_mgr(new DeviceMgr(std::move(devices))); -- GitLab From 85ef6de9fcb5977bd738e10264f0641869594b83 Mon Sep 17 00:00:00 2001 From: Sami Kama Date: Wed, 19 Dec 2018 19:52:47 -0800 Subject: [PATCH 069/622] Fix clang format --- tensorflow/core/grappler/grappler_item_builder.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tensorflow/core/grappler/grappler_item_builder.cc b/tensorflow/core/grappler/grappler_item_builder.cc index a984efd10d..fc55fb5b3d 100644 --- a/tensorflow/core/grappler/grappler_item_builder.cc +++ b/tensorflow/core/grappler/grappler_item_builder.cc @@ -104,8 +104,9 @@ Status OptimizeGraph(const GraphDef& graph_def_arg, GraphDef* output_graph_def, // Instantiate all variables for function library runtime creation. std::vector> devices; // Only CPU device is used so instead of calling DeviceFactory::AddDevices() - // with dummy session config, which will conflict with user defined options and - // create unwanted devices, call cpu_factory->CreateDevices() to get CPU only devices. + // with dummy session config, which will conflict with user defined options + // and create unwanted devices, call cpu_factory->CreateDevices() to get CPU + // only devices. DeviceFactory* cpu_factory = DeviceFactory::GetFactory("CPU"); TF_RETURN_IF_ERROR(cpu_factory->CreateDevices( options, "/job:localhost/replica:0/task:0", &devices)); -- GitLab From 2565842e71e1665d8a4fc126edc40b37becfed71 Mon Sep 17 00:00:00 2001 From: Innovimax Date: Thu, 20 Dec 2018 14:11:27 +0100 Subject: [PATCH 070/622] fix typo --- tensorflow/java/src/gen/cc/op_gen_main.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/java/src/gen/cc/op_gen_main.cc b/tensorflow/java/src/gen/cc/op_gen_main.cc index 0d9e0883af..cf4bb03dad 100644 --- a/tensorflow/java/src/gen/cc/op_gen_main.cc +++ b/tensorflow/java/src/gen/cc/op_gen_main.cc @@ -35,7 +35,7 @@ const char kUsageHeader[] = "graph.\n\n" "Operation wrappers are generated under the path specified by the " "'--output_dir' argument. This path can be absolute or relative to the\n" - "current working directory and will be created if it does not exists.\n\n" + "current working directory and will be created if it does not exist.\n\n" "Note that the operations will not be available through the " "'org.tensorflow.op.Ops' API until the generated classes are compiled\n" "using an appropriate annotation processor.\n\n" -- GitLab From 6cbd27b8ffb0ee33ae0f3151a202ba8dd0fa22a9 Mon Sep 17 00:00:00 2001 From: Trevor Morris Date: Thu, 20 Dec 2018 10:26:17 -0800 Subject: [PATCH 071/622] Only apply size limit if size of tensor is increasing --- tensorflow/core/grappler/optimizers/constant_folding.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tensorflow/core/grappler/optimizers/constant_folding.cc b/tensorflow/core/grappler/optimizers/constant_folding.cc index 3882e3b3a9..bb7ed05e33 100644 --- a/tensorflow/core/grappler/optimizers/constant_folding.cc +++ b/tensorflow/core/grappler/optimizers/constant_folding.cc @@ -913,6 +913,7 @@ Status ConstantFolding::CreateNodeDef(const string& name, AttrValue attr_tensor; TensorProto* t = attr_tensor.mutable_tensor(); bool optimized = false; + const size_t original_size = tensor->TotalBytes(); size_t encoded_size; // Use the packed representation whenever possible to avoid generating large // graphdefs. Moreover, avoid repeating the last values if they're equal. @@ -980,11 +981,11 @@ Status ConstantFolding::CreateNodeDef(const string& name, } node->mutable_attr()->insert({"value", attr_tensor}); - if (encoded_size < 10 * 1024 * 1024) { - return Status::OK(); + if (encoded_size > original_size && encoded_size >= 10 * 1024 * 1024) { + return errors::InvalidArgument( + strings::StrCat("Can't fold ", name, ", its size would be too large")); } - return errors::InvalidArgument( - strings::StrCat("Can't fold ", name, ", its size would be too large")); + return Status::OK(); } Status ConstantFolding::EvaluateNode(const NodeDef& node, -- GitLab From a9ba4a47efb80d2951ee6deea8e2d8de56dbaaf9 Mon Sep 17 00:00:00 2001 From: vitor-alves Date: Thu, 20 Dec 2018 17:26:45 -0200 Subject: [PATCH 072/622] Typo --- tensorflow/lite/examples/label_image/label_image.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/lite/examples/label_image/label_image.md b/tensorflow/lite/examples/label_image/label_image.md index fd9f49918b..debe8ffbc5 100644 --- a/tensorflow/lite/examples/label_image/label_image.md +++ b/tensorflow/lite/examples/label_image/label_image.md @@ -51,7 +51,7 @@ average time: 100.986 ms 0.0235294: 514 cornet 0.0196078: 835 suit ``` -Run `interpreter->Invoker()` 100 times: +Run `interpreter->Invoke()` 100 times: ``` > ./label_image -c 100 Loaded model ./mobilenet_quant_v1_224.tflite -- GitLab From beed5ef6fc98f513f368e68ae08357a8db623ef0 Mon Sep 17 00:00:00 2001 From: Pooya Davoodi Date: Thu, 20 Dec 2018 11:38:58 -0800 Subject: [PATCH 073/622] TFTRT: Convert between str and unicode in py2 Also check for types before doing conversion. --- tensorflow/contrib/tensorrt/python/trt_convert.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tensorflow/contrib/tensorrt/python/trt_convert.py b/tensorflow/contrib/tensorrt/python/trt_convert.py index 203b2697ba..3d2b6b499d 100644 --- a/tensorflow/contrib/tensorrt/python/trt_convert.py +++ b/tensorflow/contrib/tensorrt/python/trt_convert.py @@ -46,11 +46,13 @@ from tensorflow.python.saved_model import tag_constants from tensorflow.python.training import saver if _six.PY2: - _to_bytes = lambda s: s - _to_string = lambda s: s + _to_bytes = lambda s: s.encode("utf-8", errors="surrogateescape") \ + if isinstance(s, unicode) else s + _to_string = lambda s: s.decode("utf-8") if isinstance(s, str) else s else: - _to_bytes = lambda s: s.encode("utf-8", errors="surrogateescape") - _to_string = lambda s: s.decode("utf-8") + _to_bytes = lambda s: s.encode("utf-8", errors="surrogateescape") \ + if isinstance(s, str) else s + _to_string = lambda s: s.decode("utf-8") if isinstance(s, bytes) else s class TrtPrecisionMode(object): -- GitLab From 58621c1db6ee0ac52aab0ff868f666454d2aa5a3 Mon Sep 17 00:00:00 2001 From: Trevor Morris Date: Thu, 20 Dec 2018 11:47:58 -0800 Subject: [PATCH 074/622] Calculate input size correctly --- .../core/grappler/optimizers/constant_folding.cc | 11 ++++++----- .../core/grappler/optimizers/constant_folding.h | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/tensorflow/core/grappler/optimizers/constant_folding.cc b/tensorflow/core/grappler/optimizers/constant_folding.cc index bb7ed05e33..5a053d3a89 100644 --- a/tensorflow/core/grappler/optimizers/constant_folding.cc +++ b/tensorflow/core/grappler/optimizers/constant_folding.cc @@ -902,7 +902,8 @@ DataType GetDataTypeFromNodeOrProps(const NodeDef& node, // static Status ConstantFolding::CreateNodeDef(const string& name, const TensorValue& tensor, - NodeDef* node) { + NodeDef* node, + size_t input_size) { node->set_name(name); node->set_op("Const"); @@ -913,7 +914,6 @@ Status ConstantFolding::CreateNodeDef(const string& name, AttrValue attr_tensor; TensorProto* t = attr_tensor.mutable_tensor(); bool optimized = false; - const size_t original_size = tensor->TotalBytes(); size_t encoded_size; // Use the packed representation whenever possible to avoid generating large // graphdefs. Moreover, avoid repeating the last values if they're equal. @@ -980,8 +980,7 @@ Status ConstantFolding::CreateNodeDef(const string& name, encoded_size = t->tensor_content().size(); } node->mutable_attr()->insert({"value", attr_tensor}); - - if (encoded_size > original_size && encoded_size >= 10 * 1024 * 1024) { + if (encoded_size > input_size && encoded_size >= 10 * 1024 * 1024) { return errors::InvalidArgument( strings::StrCat("Can't fold ", name, ", its size would be too large")); } @@ -1011,6 +1010,7 @@ Status ConstantFolding::EvaluateOneFoldable(const NodeDef& node, } }); + size_t input_size = 0; for (const auto& input : node.input()) { const TensorId input_tensor = ParseTensorName(input); if (input_tensor.index() < 0) { @@ -1025,6 +1025,7 @@ Status ConstantFolding::EvaluateOneFoldable(const NodeDef& node, } TF_RETURN_IF_ERROR(CheckAttrExists(*input_node, "value")); const TensorProto& raw_val = input_node->attr().at("value").tensor(); + input_size += raw_val.tensor_content().size(); Tensor* value = new Tensor(raw_val.dtype(), raw_val.tensor_shape()); CHECK(value->FromProto(raw_val)); inputs.emplace_back(value); @@ -1042,7 +1043,7 @@ Status ConstantFolding::EvaluateOneFoldable(const NodeDef& node, node_name = strings::StrCat(node_name, "-", i); } if (output_tensors[i].tensor) { - Status s = CreateNodeDef(node_name, output_tensors[i], &outputs->at(i)); + Status s = CreateNodeDef(node_name, output_tensors[i], &outputs->at(i), input_size); if (!s.ok()) { *result_too_large = true; return s; diff --git a/tensorflow/core/grappler/optimizers/constant_folding.h b/tensorflow/core/grappler/optimizers/constant_folding.h index 0b778882d7..8843ac161f 100644 --- a/tensorflow/core/grappler/optimizers/constant_folding.h +++ b/tensorflow/core/grappler/optimizers/constant_folding.h @@ -36,7 +36,7 @@ const char kConstantFoldingCtrl[] = "ConstantFoldingCtrl"; class ConstantFolding : public GraphOptimizer { public: static Status CreateNodeDef(const string& name, const TensorValue& tensor, - NodeDef* node); + NodeDef* node, size_t input_size = 0); static string AddControlDependency(const string& input_name, GraphDef* graph, NodeMap* node_map); -- GitLab From f20bc658490e5191c920f04336a030d13a77e8f5 Mon Sep 17 00:00:00 2001 From: Pete Warden Date: Thu, 20 Dec 2018 12:30:39 -0800 Subject: [PATCH 075/622] Updated download script and instructions to contain modified files away from regular sources --- tensorflow/lite/experimental/micro/README.md | 13 +++--- .../micro_speech/CMSIS/preprocessor.cc | 43 +++++++++---------- .../micro/tools/make/download_dependencies.sh | 32 ++++++++++++-- .../make/targets/apollo3evb_makefile.inc | 13 +++--- 4 files changed, 62 insertions(+), 39 deletions(-) diff --git a/tensorflow/lite/experimental/micro/README.md b/tensorflow/lite/experimental/micro/README.md index cc2a62cb8a..1df31b3c31 100644 --- a/tensorflow/lite/experimental/micro/README.md +++ b/tensorflow/lite/experimental/micro/README.md @@ -132,16 +132,13 @@ help make adding support for new platforms as easy as possible. Follow these steps to get the pushbutton yes/no example working on Apollo 3: 1. Make sure to run the "Getting Started" section before performing the following steps -2. Download Apollo3-SDK-2018.08.13 and place in tensorflow/lite/experimental/micro/tools/make/downloads -3. Copy and prepare files by running tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb/prep_apollo3_files.sh from the toplevel of git +2. Download Apollo3-SDK-2018.08.13 and place in `tensorflow/lite/experimental/micro/tools/make/downloads`. This is not yet publicly released, but you can contact ashah@ambiqmicro.com to request a copy. +3. Compile the project with the following command: make -f tensorflow/lite/experimental/micro/tools/make/Makefile TARGET=apollo3evb pushbutton_cmsis_speech_test_bin 4. Install [Segger JLink tools](https://www.segger.com/downloads/jlink/) -5. Make sure the [GNU Arm Embedded Toolchain (gcc-arm-none-eabi-7-2018-q2-update)](https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads) is installed to tensorflow/lite/experimental/micro/tools/make/downloads - 1. Confirm directory is in $PATH -6. Compile the project with the following command: make -f tensorflow/lite/experimental/micro/tools/make/Makefile TARGET=apollo3evb pushbutton_cmsis_speech_test_bin -7. Connect the Apollo3 EVB (with mic shield) to the computer and power it on -8. Start the GDB server in a new terminal with the following command: JLinkGDBServer -select USB -device AMA3B1KK-KBR -endian little -if SWD -speed 1000 -noir -noLocalhostOnly +5. Connect the Apollo3 EVB (with mic shield) to the computer and power it on +6. Start the GDB server in a new terminal with the following command: JLinkGDBServer -select USB -device AMA3B1KK-KBR -endian little -if SWD -speed 1000 -noir -noLocalhostOnly 1. The command has run successfully if you see the message "Waiting for GDB connection" -9. Back in the original terminal, run the program via the debugger +7. Back in the original terminal, run the program via the debugger 1. Navigate to tensorflow/lite/experimental/micro/examples/micro_speech/apollo3 2. Start gdb by entering the following command: arm-none-eabi-gdb 3. Run the command script by entering the following command: source pushbutton_cmsis_scores.cmd. This script does the following: diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.cc index 3e31f96aa3..5c6978b5ed 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/preprocessor.cc @@ -14,22 +14,20 @@ limitations under the License. ==============================================================================*/ extern "C" { - #define ARM_MATH_CM4 - #define IFFT_FLAG_R 0 - #define BIT_REVERSE_FLAG 1 - #define FFT_SIZE 512 - #define FFT_SIZE_DIV2 256 - #include - #include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.h" - #include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS_ext/arm_cmplx_mag_squared_q10p6.h" +#define ARM_MATH_CM4 +#define IFFT_FLAG_R 0 +#define BIT_REVERSE_FLAG 1 +#define FFT_SIZE 512 +#define FFT_SIZE_DIV2 256 +#include +#include "arm_cmplx_mag_squared_q10p6.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.h" } #include "tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.h" void quantize(q15_t* bufA, q15_t* bufB, uint8_t* output); - - q15_t bufA[FFT_SIZE]; q15_t bufB[FFT_SIZE]; arm_rfft_instance_q15 S_arm_fft; @@ -42,7 +40,7 @@ constexpr int kInputSize = 512; constexpr int kAverageWindowSize = 6; constexpr int kOutputSize = ((kInputSize / 2) + (kAverageWindowSize - 1)) / kAverageWindowSize; -} //namespace +} // namespace TfLiteStatus Preprocess(tflite::ErrorReporter* error_reporter, const int16_t* input, int input_size, int output_size, @@ -60,14 +58,15 @@ TfLiteStatus Preprocess(tflite::ErrorReporter* error_reporter, // 30ms at 16 kHz = 480 samples // We want to pad the rest of the 512-sample buffer with zeros - arm_mult_q15((q15_t *) input, g_hanning, bufB, 480); + arm_mult_q15((q15_t*)input, g_hanning, bufB, 480); int i; - for(i=480; i<512; i++) { + for (i = 480; i < 512; i++) { bufB[i] = 0; } // Should move init code outside of Preprocess() function - arm_math_status = arm_rfft_init_q15(&S_arm_fft, FFT_SIZE, IFFT_FLAG_R, BIT_REVERSE_FLAG); + arm_math_status = + arm_rfft_init_q15(&S_arm_fft, FFT_SIZE, IFFT_FLAG_R, BIT_REVERSE_FLAG); arm_rfft_q15(&S_arm_fft, bufB, bufA); // The rfft function packs data as follows: @@ -86,21 +85,21 @@ TfLiteStatus Preprocess(tflite::ErrorReporter* error_reporter, void quantize(q15_t* bufA, q15_t* bufB, uint8_t* output) { int i; - for (i=0; i<42; i++) { - arm_mean_q15(bufB+6*i, 6, bufA+i); + for (i = 0; i < 42; i++) { + arm_mean_q15(bufB + 6 * i, 6, bufA + i); } - arm_mean_q15(bufB+252, 5, bufA+42); + arm_mean_q15(bufB + 252, 5, bufA + 42); - for (i=0; i<43; i++) { - output[i] = (uint8_t) (bufA[i] >> 5); + for (i = 0; i < 43; i++) { + output[i] = (uint8_t)(bufA[i] >> 5); } } TfLiteStatus Preprocess_1sec(tflite::ErrorReporter* error_reporter, - const int16_t* input, uint8_t* output) { + const int16_t* input, uint8_t* output) { int i; - for(i=0; i<49; i++) { - Preprocess(error_reporter, input+i*320, 480, 43, output+i*43); + for (i = 0; i < 49; i++) { + Preprocess(error_reporter, input + i * 320, 480, 43, output + i * 43); } return kTfLiteOk; } diff --git a/tensorflow/lite/experimental/micro/tools/make/download_dependencies.sh b/tensorflow/lite/experimental/micro/tools/make/download_dependencies.sh index ba1fbb7fcf..43e481a590 100755 --- a/tensorflow/lite/experimental/micro/tools/make/download_dependencies.sh +++ b/tensorflow/lite/experimental/micro/tools/make/download_dependencies.sh @@ -21,7 +21,6 @@ cd "$SCRIPT_DIR/../../../../../.." DOWNLOADS_DIR=tensorflow/lite/experimental/micro/tools/make/downloads BZL_FILE_PATH=tensorflow/workspace.bzl -AP3_DOWNLOADS_DIR=tensorflow/lite/experimental/micro/examples/micro_speech # Ensure it is being run from repo root if [ ! -f $BZL_FILE_PATH ]; then @@ -36,6 +35,7 @@ CMSIS_URL="https://github.com/ARM-software/CMSIS_5/archive/5.4.0.zip" STM32_BARE_LIB_URL="https://github.com/google/stm32_bare_lib/archive/c07d611fb0af58450c5a3e0ab4d52b47f99bc82d.zip" AP3_URL="https://github.com/AmbiqMicro/TFLiteMicro_Apollo3/archive/dfbcef9a57276c087d95aab7cb234f1d4c9eaaba.zip" CUST_CMSIS_URL="https://github.com/AmbiqMicro/TFLiteMicro_CustCMSIS/archive/8f63966c5692e6a3a83956efd2e4aed77c4c9949.zip" +GCC_EMBEDDED_URL="https://developer.arm.com/-/media/Files/downloads/gnu-rm/7-2018q2/gcc-arm-none-eabi-7-2018-q2-update-linux.tar.bz2" download_and_extract() { local usage="Usage: download_and_extract URL DIR" @@ -45,6 +45,8 @@ download_and_extract() { mkdir -p "${dir}" if [[ "${url}" == *gz ]]; then curl -Ls "${url}" | tar -C "${dir}" --strip-components=1 -xz + elif [[ "${url}" == *bz2 ]]; then + curl -Ls "${url}" | tar -C "${dir}" --strip-components=1 -xj elif [[ "${url}" == *zip ]]; then tempdir=$(mktemp -d) tempdir2=$(mktemp -d) @@ -68,11 +70,35 @@ download_and_extract() { find "${dir}" -type f -name '*BUILD' -delete } +patch_apollo3_sdk() { + local ap3_dir="${1}" + if [ ! -f ${ap3_dir}/VERSION.txt ]; then + echo "Could not find ${ap3_dir}, skipping Apollo3 SDK"; + return; + fi + local src_dir=${ap3_dir}/boards/apollo3_evb/examples/hello_world/gcc + local dest_dir=${ap3_dir}/boards/apollo3_evb/examples/hello_world/gcc_patched + rm -rf ${dest_dir} + mkdir ${dest_dir} + cp "${src_dir}/startup_gcc.c" "${dest_dir}/startup_gcc.c" + cp "${src_dir}/hello_world.ld" "${dest_dir}/apollo3evb.ld" + sed -i -e '131s/1024/1024\*20/g' "${dest_dir}/startup_gcc.c" + sed -i -e 's/main/_main/g' "${dest_dir}/startup_gcc.c" + sed -i -e '3s/hello_world.ld/apollo3evb.ld/g' "${dest_dir}/apollo3evb.ld" + sed -i -e '3s/startup_gnu/startup_gcc/g' "${dest_dir}/apollo3evb.ld" + sed -i -e '6s/am_reset_isr/Reset_Handler/g' "${dest_dir}/apollo3evb.ld" + sed -i -e '22s/\*(.text\*)/\*(.text\*)\n\n\t\/\* These are the C++ global constructors. Stick them all here and\n\t \* then walk through the array in main() calling them all.\n\t \*\/\n\t_init_array_start = .;\n\tKEEP (\*(SORT(.init_array\*)))\n\t_init_array_end = .;\n\n\t\/\* XXX Currently not doing anything for global destructors. \*\/\n/g' "${dest_dir}/apollo3evb.ld" + sed -i -e "70s/} > SRAM/} > SRAM\n \/\* Add this to satisfy reference to symbol 'end' from libnosys.a(sbrk.o)\n \* to denote the HEAP start.\n \*\/\n end = .;/g" "${dest_dir}/apollo3evb.ld" + echo "Finished preparing Apollo3 files" +} + download_and_extract "${GEMMLOWP_URL}" "${DOWNLOADS_DIR}/gemmlowp" download_and_extract "${FLATBUFFERS_URL}" "${DOWNLOADS_DIR}/flatbuffers" download_and_extract "${CMSIS_URL}" "${DOWNLOADS_DIR}/cmsis" download_and_extract "${STM32_BARE_LIB_URL}" "${DOWNLOADS_DIR}/stm32_bare_lib" -download_and_extract "${AP3_URL}" "${AP3_DOWNLOADS_DIR}/apollo3_ext" -download_and_extract "${CUST_CMSIS_URL}" "${AP3_DOWNLOADS_DIR}/CMSIS_ext" +download_and_extract "${AP3_URL}" "${DOWNLOADS_DIR}/apollo3_ext" +patch_apollo3_sdk "${DOWNLOADS_DIR}/Apollo3-SDK-2018.08.13" +download_and_extract "${CUST_CMSIS_URL}" "${DOWNLOADS_DIR}/CMSIS_ext" +download_and_extract "${GCC_EMBEDDED_URL}" "${DOWNLOADS_DIR}/gcc_embedded" echo "download_dependencies.sh completed successfully." >&2 diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc index 68ef79ea02..de48d5d46f 100644 --- a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc +++ b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc @@ -1,5 +1,6 @@ # Settings for apollo3 evb platforms. ifeq ($(TARGET), apollo3evb) + export PATH := $(MAKEFILE_DIR)/downloads/gcc_embedded/bin/:$(PATH) TARGET_ARCH := cortex-m4 TARGET_TOOLCHAIN_PREFIX := arm-none-eabi- # Download the Ambiq Apollo3 SDK and set this variable to find the header @@ -7,7 +8,7 @@ ifeq ($(TARGET), apollo3evb) APOLLO3_SDK := $(MAKEFILE_DIR)/downloads/Apollo3-SDK-2018.08.13 # Need a pointer to the GNU ARM toolchain for crtbegin.o for the fp functions # with the softfp interfaces. - GCC_ARM := $(MAKEFILE_DIR)/downloads/gcc-arm-none-eabi-7-2018-q2-update/ + GCC_ARM := $(MAKEFILE_DIR)/downloads/gcc_embedded/ PLATFORM_FLAGS = \ -DPART_apollo3 \ @@ -54,7 +55,7 @@ ifeq ($(TARGET), apollo3evb) -Wl,--start-group -lm -lc -lgcc -Wl,--end-group \ -fno-exceptions \ -nostdlib --specs=nano.specs -t -lstdc++ -lc -lnosys -lm \ - -Wl,-T,$(MAKEFILE_DIR)/targets/apollo3evb/apollo3evb.ld \ + -Wl,-T,$(APOLLO3_SDK)/boards/apollo3_evb/examples/hello_world/gcc_patched/apollo3evb.ld \ -Wl,-Map=$(MAKEFILE_DIR)/gen/$(TARGET).map,--cref BUILD_TYPE := micro MICROLITE_LIBS := \ @@ -65,6 +66,7 @@ ifeq ($(TARGET), apollo3evb) INCLUDES += \ -isystem$(MAKEFILE_DIR)/downloads/cmsis/CMSIS/Core/Include/ \ -isystem$(MAKEFILE_DIR)/downloads/cmsis/CMSIS/DSP/Include/ \ + -I$(MAKEFILE_DIR)/downloads/CMSIS_ext/ \ -I$(GCC_ARM)/arm-none-eabi/ \ -I$(APOLLO3_SDK)/mcu/apollo3/ \ -I$(APOLLO3_SDK)/CMSIS/AmbiqMicro/Include/ \ @@ -81,7 +83,7 @@ ifeq ($(TARGET), apollo3evb) # setting clock speed, default uart setups, etc. and an implementation # of the DebugLog interfaces. MICROLITE_CC_SRCS += \ - $(MAKEFILE_DIR)/targets/apollo3evb/startup_gcc.c \ + $(APOLLO3_SDK)/boards/apollo3_evb/examples/hello_world/gcc_patched/startup_gcc.c \ $(APOLLO3_SDK)/utils/am_util_delay.c \ $(APOLLO3_SDK)/utils/am_util_faultisr.c \ $(APOLLO3_SDK)/utils/am_util_id.c \ @@ -102,7 +104,7 @@ ifeq ($(TARGET), apollo3evb) AP3_EXT_MICRO_DIR := tensorflow/lite/experimental/micro/examples/micro_speech/apollo3_ext AP3_MICRO_DIR := tensorflow/lite/experimental/micro/examples/micro_speech/apollo3 CMSIS_DIR := tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS - CMSIS_EXT_DIR := tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS_ext + CMSIS_EXT_DIR := $(MAKEFILE_DIR)/downloads/CMSIS_ext MICRO_SPEECH_TEST_SRCS += \ $(AP3_MICRO_DIR)/_main.c @@ -143,11 +145,10 @@ ifeq ($(TARGET), apollo3evb) $(AP3_MICRO_DIR)/_main.c TEST_SCRIPT := tensorflow/lite/experimental/log_test/test_apollo3evb_binary.sh - # These are tests that don't currently work on the blue pill. + # These are tests that don't currently work on the Apollo3 board. EXCLUDED_TESTS := \ tensorflow/lite/experimental/micro/micro_interpreter_test.cc \ tensorflow/lite/experimental/micro/simple_tensor_allocator_test.cc MICROLITE_TEST_SRCS := $(filter-out $(EXCLUDED_TESTS), $(MICROLITE_TEST_SRCS)) - endif -- GitLab From 63dcc4cc34fbb6e4de906e9e84e67645eefcfd3b Mon Sep 17 00:00:00 2001 From: mars20 Date: Thu, 20 Dec 2018 13:18:56 -0800 Subject: [PATCH 076/622] Adding a 'README' file for RISC-V --- tensorflow/lite/experimental/micro/riscv32_mcu/README.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 tensorflow/lite/experimental/micro/riscv32_mcu/README.md diff --git a/tensorflow/lite/experimental/micro/riscv32_mcu/README.md b/tensorflow/lite/experimental/micro/riscv32_mcu/README.md new file mode 100644 index 0000000000..82f1dcbb61 --- /dev/null +++ b/tensorflow/lite/experimental/micro/riscv32_mcu/README.md @@ -0,0 +1,5 @@ +# RISC-V MCU + +This folder contains TFLite kernel operations optimized for RISC-V micro controllers. + +It is designed to be portable even to 'bare metal', so it follows the same design goals as the micro experimental port. -- GitLab From 5dde9b79227bafc3ee7e75e9fa1d0b1b477fe62c Mon Sep 17 00:00:00 2001 From: Pete Warden Date: Thu, 20 Dec 2018 14:07:45 -0800 Subject: [PATCH 077/622] Moved targets to examples directory --- .../micro/examples/micro_speech/Makefile.inc | 3 + .../micro_speech/apollo3/Makefile.inc | 100 ++++++++++ .../micro_speech/apollo3/preprocessor_1k.cc | 21 +-- .../experimental/micro/tools/make/Makefile | 171 +----------------- .../micro/tools/make/download_dependencies.sh | 7 +- .../make/targets/apollo3evb_makefile.inc | 37 +--- 6 files changed, 118 insertions(+), 221 deletions(-) create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/Makefile.inc diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/Makefile.inc b/tensorflow/lite/experimental/micro/examples/micro_speech/Makefile.inc index 0e42329cad..0d9c9a56a7 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/Makefile.inc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/Makefile.inc @@ -151,3 +151,6 @@ $(MICRO_SPEECH_BINARY): $(MICRO_SPEECH_OBJS) $(MICROLITE_LIB_PATH) $(LIBFLAGS) $(MICROLITE_LIB_PATH) $(LDFLAGS) $(MICROLITE_LIBS) micro_speech: $(MICRO_SPEECH_BINARY) micro_speech_bin: $(MICRO_SPEECH_BINARY).bin + +# Find any platform-specific rules for this example. +include $(wildcard tensorflow/lite/experimental/micro/examples/micro_speech/*/Makefile.inc) diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/Makefile.inc b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/Makefile.inc new file mode 100644 index 0000000000..0aa362be00 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/Makefile.inc @@ -0,0 +1,100 @@ +# Settings for apollo3 evb platforms. +ifeq ($(TARGET), apollo3evb) + + PUSHBUTTON_MICRO_SPEECH_TEST_SRCS := \ + $(AP3_MICRO_DIR)/../preprocessor.cc \ + $(AP3_MICRO_DIR)/pushbutton_main.c \ + $(AP3_MICRO_DIR)/pushbutton_test.cc \ + $(AP3_MICRO_DIR)/../tiny_conv_model_data.cc \ + $(APOLLO3_SDK)/devices/am_devices_led.c + ALL_SRCS += $(PUSHBUTTON_MICRO_SPEECH_TEST_SRCS) + PUSHBUTTON_MICRO_SPEECH_TEST_OBJS := $(addprefix $(OBJDIR), \ + $(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(PUSHBUTTON_MICRO_SPEECH_TEST_SRCS)))) + PUSHBUTTON_MICRO_SPEECH_TEST_BINARY := $(BINDIR)pushbutton_micro_speech_test + $(PUSHBUTTON_MICRO_SPEECH_TEST_BINARY): $(PUSHBUTTON_MICRO_SPEECH_TEST_OBJS) $(MICROLITE_LIB_PATH) + @mkdir -p $(dir $@) + $(CXX) $(CXXFLAGS) $(INCLUDES) \ + -o $(PUSHBUTTON_MICRO_SPEECH_TEST_BINARY) $(PUSHBUTTON_MICRO_SPEECH_TEST_OBJS) \ + $(LIBFLAGS) $(MICROLITE_LIB_PATH) $(LDFLAGS) $(MICROLITE_LIBS) + pushbutton_micro_speech_test: $(PUSHBUTTON_MICRO_SPEECH_TEST_BINARY) + pushbutton_micro_speech_test_bin: $(PUSHBUTTON_MICRO_SPEECH_TEST_BINARY).bin + test_pushbutton_micro_speech: $(PUSHBUTTON_MICRO_SPEECH_TEST_BINARY) + $(TEST_SCRIPT) $(PUSHBUTTON_MICRO_SPEECH_TEST_BINARY) '~~~ALL TESTS PASSED~~~' + + PUSHBUTTON_CMSIS_SPEECH_TEST_SRCS := \ + $(AP3_MICRO_DIR)/pushbutton_main.c \ + $(AP3_MICRO_DIR)/pushbutton_test.cc \ + $(AP3_MICRO_DIR)/../tiny_conv_model_data.cc \ + $(CMSIS_DIR)/preprocessor.cc \ + $(CMSIS_EXT_DIR)/arm_cmplx_mag_squared_q10p6.c \ + $(CMSIS_DIR)/hanning.c \ + $(APOLLO3_SDK)/devices/am_devices_led.c \ + $(CMSIS_SRCS) + ALL_SRCS += $(PUSHBUTTON_CMSIS_SPEECH_TEST_SRCS) + PUSHBUTTON_CMSIS_SPEECH_TEST_OBJS := $(addprefix $(OBJDIR), \ + $(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(PUSHBUTTON_CMSIS_SPEECH_TEST_SRCS))) \ + arm_bitreversal2.o) + PUSHBUTTON_CMSIS_SPEECH_TEST_BINARY := $(BINDIR)pushbutton_cmsis_speech_test + $(PUSHBUTTON_CMSIS_SPEECH_TEST_BINARY): $(PUSHBUTTON_CMSIS_SPEECH_TEST_OBJS) $(MICROLITE_LIB_PATH) + @mkdir -p $(dir $@) + $(CXX) $(CXXFLAGS) $(INCLUDES) \ + -o $(PUSHBUTTON_CMSIS_SPEECH_TEST_BINARY) $(PUSHBUTTON_CMSIS_SPEECH_TEST_OBJS) \ + $(LIBFLAGS) $(MICROLITE_LIB_PATH) $(LDFLAGS) $(MICROLITE_LIBS) + pushbutton_cmsis_speech_test: $(PUSHBUTTON_CMSIS_SPEECH_TEST_BINARY) + pushbutton_cmsis_speech_test_bin: $(PUSHBUTTON_CMSIS_SPEECH_TEST_BINARY).bin + test_pushbutton_cmsis_speech: $(PUSHBUTTON_CMSIS_SPEECH_TEST_BINARY) + $(TEST_SCRIPT) $(PUSHBUTTON_CMSIS_SPEECH_TEST_BINARY) '~~~ALL TESTS PASSED~~~' + + PREPROCESSOR_1K_SRCS := \ + tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k.cc \ + tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.cc + + PREPROCESSOR_1K_MICRO_TEST_SRCS := \ + $(PREPROCESSOR_1K_SRCS) \ + $(AP3_MICRO_DIR)/../fixed_point/preprocessor.cc \ + $(AP3_EXT_MICRO_DIR)/system_apollo3.c \ + $(AP3_MICRO_DIR)/_main.c + ALL_SRCS += $(PREPROCESSOR_1K_MICRO_TEST_SRCS) + PREPROCESSOR_1K_MICRO_TEST_OBJS := $(addprefix $(OBJDIR), \ + $(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(PREPROCESSOR_1K_MICRO_TEST_SRCS)))) + PREPROCESSOR_1K_MICRO_TEST_BINARY := $(BINDIR)preprocessor_1k_micro_test + $(PREPROCESSOR_1K_MICRO_TEST_BINARY): $(PREPROCESSOR_1K_MICRO_TEST_OBJS) $(MICROLITE_LIB_PATH) + @mkdir -p $(dir $@) + $(CXX) $(CXXFLAGS) $(INCLUDES) \ + -o $(PREPROCESSOR_1K_MICRO_TEST_BINARY) $(PREPROCESSOR_1K_MICRO_TEST_OBJS) \ + $(LIBFLAGS) $(MICROLITE_LIB_PATH) $(LDFLAGS) $(MICROLITE_LIBS) + preprocessor_1k_micro_test: $(PREPROCESSOR_1K_MICRO_TEST_BINARY) + preprocessor_1k_micro_test_bin: $(PREPROCESSOR_1K_MICRO_TEST_BINARY).bin + test_preprocessor_1k_micro: $(PREPROCESSOR_1K_MICRO_TEST_BINARY) + $(TEST_SCRIPT) $(PREPROCESSOR_1K_MICRO_TEST_BINARY) '~~~ALL TESTS PASSED~~~' + + PREPROCESSOR_1K_CMSIS_TEST_SRCS := \ + $(PREPROCESSOR_1K_SRCS) \ + $(CMSIS_DIR)/preprocessor.cc \ + $(CMSIS_EXT_DIR)/arm_cmplx_mag_squared_q10p6.c \ + $(CMSIS_DIR)/hanning.c \ + $(AP3_EXT_MICRO_DIR)/system_apollo3.c \ + $(AP3_MICRO_DIR)/_main.c \ + $(CMSIS_SRCS) + ALL_SRCS += $(PREPROCESSOR_1K_CMSIS_TEST_SRCS) + PREPROCESSOR_1K_CMSIS_TEST_BINARY := $(BINDIR)preprocessor_1k_cmsis_test + PREPROCESSOR_1K_CMSIS_TEST_OBJS := $(addprefix $(OBJDIR), \ + $(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(PREPROCESSOR_1K_CMSIS_TEST_SRCS)))\ + arm_bitreversal2.o) + $(PREPROCESSOR_1K_CMSIS_TEST_BINARY): $(PREPROCESSOR_1K_CMSIS_TEST_OBJS) $(MICROLITE_LIB_PATH) + @mkdir -p $(dir $@) + $(CXX) $(CXXFLAGS) $(INCLUDES) \ + -o $(PREPROCESSOR_1K_CMSIS_TEST_BINARY) $(PREPROCESSOR_1K_CMSIS_TEST_OBJS) \ + $(LIBFLAGS) $(MICROLITE_LIB_PATH) $(LDFLAGS) $(MICROLITE_LIBS) + preprocessor_1k_cmsis_test: $(PREPROCESSOR_1K_CMSIS_TEST_BINARY) + preprocessor_1k_cmsis_test_bin: $(PREPROCESSOR_1K_CMSIS_TEST_BINARY).bin + test_preprocessor_1k_cmsis: $(PREPROCESSOR_1K_CMSIS_TEST_BINARY) + $(TEST_SCRIPT) $(PREPROCESSOR_1K_CMSIS_TEST_BINARY) '~~~ALL TESTS PASSED~~~' + + PREPROCESSOR_TEST_SRCS += \ + $(AP3_MICRO_DIR)/_main.c + + $(OBJDIR)arm_bitreversal2.o: + $(CXX) $(CXXFLAGS) $(INCLUDES) -c $(CMSIS_SRC_DIR)/TransformFunctions/arm_bitreversal2.S -o $(OBJDIR)arm_bitreversal2.o + +endif diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k.cc index 561c46ce33..007772e77a 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k.cc @@ -13,28 +13,29 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -/* This file is a modification of the Tensorflow Micro Lite file preprocessor.cc */ +/* This file is a modification of the Tensorflow Micro Lite file preprocessor.cc + */ -#include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.h" #include "tensorflow/lite/c/c_api_internal.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.h" #include "tensorflow/lite/experimental/micro/micro_error_reporter.h" #include "tensorflow/lite/experimental/micro/testing/micro_test.h" extern "C" { -#include "tensorflow/lite/experimental/micro/examples/micro_speech/apollo3_ext/system_apollo3.h" -#include "tensorflow/lite/experimental/micro/examples/micro_speech/apollo3_ext/apollo3.h" +#include "apollo3.h" +#include "system_apollo3.h" } #define output_data_size 43 int count; extern TfLiteStatus Preprocess(tflite::ErrorReporter* error_reporter, - const int16_t* input, int input_size, int output_size, - uint8_t* output); + const int16_t* input, int input_size, + int output_size, uint8_t* output); TF_LITE_MICRO_TESTS_BEGIN CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; -//DWT->LAR = 0xC5ACCE55; +// DWT->LAR = 0xC5ACCE55; DWT->CYCCNT = 0; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; @@ -43,12 +44,10 @@ TF_LITE_MICRO_TEST(TestPreprocessor) { tflite::ErrorReporter* error_reporter = µ_error_reporter; uint8_t calculated_data[output_data_size]; - TfLiteStatus yes_status = Preprocess( - error_reporter, g_sin_1k, g_sin_1k_size, - output_data_size, calculated_data); + TfLiteStatus yes_status = Preprocess(error_reporter, g_sin_1k, g_sin_1k_size, + output_data_size, calculated_data); count = DWT->CYCCNT; TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, yes_status); - } TF_LITE_MICRO_TESTS_END diff --git a/tensorflow/lite/experimental/micro/tools/make/Makefile b/tensorflow/lite/experimental/micro/tools/make/Makefile index 7223ea3d1e..6a7c875888 100644 --- a/tensorflow/lite/experimental/micro/tools/make/Makefile +++ b/tensorflow/lite/experimental/micro/tools/make/Makefile @@ -53,46 +53,6 @@ CC_PREFIX := # runtime that can be linked in to other programs. MICROLITE_LIB_NAME := libtensorflow-microlite.a -# Test binary for the microcontroller speech model. -MICRO_SPEECH_TEST_SRCS := \ -tensorflow/lite/experimental/micro/examples/micro_speech/micro_speech_test.cc \ -tensorflow/lite/experimental/micro/examples/micro_speech/tiny_conv_model_data.cc \ -tensorflow/lite/experimental/micro/examples/micro_speech/no_features_data.cc \ -tensorflow/lite/experimental/micro/examples/micro_speech/yes_features_data.cc - -# Test binary for the streaming microcontroller speech model. -PUSHBUTTON_MICRO_SPEECH_TEST_SRCS := \ -tensorflow/lite/experimental/micro/examples/micro_speech/micro_speech_test.cc \ -tensorflow/lite/experimental/micro/examples/micro_speech/tiny_conv_model_data.cc \ -tensorflow/lite/experimental/micro/examples/micro_speech/no_features_data.cc \ -tensorflow/lite/experimental/micro/examples/micro_speech/yes_features_data.cc - -# Test binary for the streaming microcontroller speech model. -PUSHBUTTON_CMSIS_SPEECH_TEST_SRCS := \ -tensorflow/lite/experimental/micro/examples/micro_speech/micro_speech_test.cc \ -tensorflow/lite/experimental/micro/examples/micro_speech/tiny_conv_model_data.cc - -# Test binary for the microcontroller speech model. -PREPROCESSOR_TEST_SRCS := \ -tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor_test.cc \ -tensorflow/lite/experimental/micro/examples/micro_speech/no_30ms_sample_data.cc \ -tensorflow/lite/experimental/micro/examples/micro_speech/yes_30ms_sample_data.cc \ -tensorflow/lite/experimental/micro/examples/micro_speech/no_power_spectrum_data.cc \ -tensorflow/lite/experimental/micro/examples/micro_speech/yes_power_spectrum_data.cc - -PREPROCESSOR_REFERENCE_TEST_SRCS = \ -$(PREPROCESSOR_TEST_SRCS) \ -tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.cc - -PREPROCESSOR_FIXED_TEST_SRCS += \ -$(PREPROCESSOR_TEST_SRCS) \ -tensorflow/lite/experimental/micro/examples/micro_speech/fixed_point/preprocessor.cc - -PREPROCESSOR_1K_SRCS := \ -tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k.cc \ -tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.cc - - MICROLITE_TEST_SRCS := \ $(wildcard tensorflow/lite/experimental/micro/*test.cc) \ $(wildcard tensorflow/lite/experimental/micro/kernels/*test.cc) @@ -115,14 +75,7 @@ MICROLITE_CC_SRCS := $(filter-out $(MICROLITE_TEST_SRCS), $(MICROLITE_CC_BASE_SR include $(wildcard $(MAKEFILE_DIR)/targets/*_makefile.inc) ALL_SRCS := \ - $(MICRO_SPEECH_TEST_SRCS) \ - $(PUSHBUTTON_MICRO_SPEECH_TEST_SRCS) \ - $(PUSHBUTTON_CMSIS_SPEECH_TEST_SRCS) \ - $(PREPROCESSOR_REFERENCE_TEST_SRCS) \ - $(PREPROCESSOR_FIXED_TEST_SRCS) \ $(MICROLITE_CC_SRCS) \ - $(PREPROCESSOR_1K_MICRO_TEST_SRCS) \ - $(PREPROCESSOR_1K_CMSIS_TEST_SRCS) \ $(MICROLITE_TEST_SRCS) # Where compiled objects are stored. @@ -133,40 +86,12 @@ LIBDIR := $(GENDIR)lib/ MICROLITE_LIB_PATH := $(LIBDIR)$(MICROLITE_LIB_NAME) -MICRO_SPEECH_TEST_BINARY := $(BINDIR)micro_speech_test -PUSHBUTTON_MICRO_SPEECH_TEST_BINARY := $(BINDIR)pushbutton_micro_speech_test -PUSHBUTTON_CMSIS_SPEECH_TEST_BINARY := $(BINDIR)pushbutton_cmsis_speech_test -PREPROCESSOR_REFERENCE_TEST_BINARY := $(BINDIR)preprocessor_reference_test -PREPROCESSOR_FIXED_TEST_BINARY := $(BINDIR)preprocessor_fixed_test -PREPROCESSOR_1K_MICRO_TEST_BINARY := $(BINDIR)preprocessor_1k_micro_test -PREPROCESSOR_1K_CMSIS_TEST_BINARY := $(BINDIR)preprocessor_1k_cmsis_test - CXX := $(CC_PREFIX)${TARGET_TOOLCHAIN_PREFIX}g++ CC := $(CC_PREFIX)${TARGET_TOOLCHAIN_PREFIX}gcc AR := $(CC_PREFIX)${TARGET_TOOLCHAIN_PREFIX}ar -MICRO_SPEECH_TEST_OBJS := $(addprefix $(OBJDIR), \ -$(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(patsubst %.S,%.o,$(MICRO_SPEECH_TEST_SRCS))))) - -PUSHBUTTON_MICRO_SPEECH_TEST_OBJS := $(addprefix $(OBJDIR), \ -$(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(PUSHBUTTON_MICRO_SPEECH_TEST_SRCS)))) - -PUSHBUTTON_CMSIS_SPEECH_TEST_OBJS := $(addprefix $(OBJDIR), \ -$(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(PUSHBUTTON_CMSIS_SPEECH_TEST_SRCS))) \ -arm_bitreversal2.o) - -PREPROCESSOR_REFERENCE_TEST_OBJS := $(addprefix $(OBJDIR), \ -$(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(PREPROCESSOR_REFERENCE_TEST_SRCS)))) - -PREPROCESSOR_FIXED_TEST_OBJS := $(addprefix $(OBJDIR), \ -$(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(PREPROCESSOR_FIXED_TEST_SRCS)))) - -PREPROCESSOR_1K_MICRO_TEST_OBJS := $(addprefix $(OBJDIR), \ -$(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(PREPROCESSOR_1K_MICRO_TEST_SRCS)))) - -PREPROCESSOR_1K_CMSIS_TEST_OBJS := $(addprefix $(OBJDIR), \ -$(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(PREPROCESSOR_1K_CMSIS_TEST_SRCS))) \ -arm_bitreversal2.o) +# Load the examples. +include $(wildcard tensorflow/lite/experimental/micro/examples/*/Makefile.inc) MICROLITE_LIB_OBJS := $(addprefix $(OBJDIR), \ $(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(MICROLITE_CC_SRCS)))) @@ -203,96 +128,6 @@ $(MICROLITE_LIB_PATH): tensorflow/lite/schema/schema_generated.h $(MICROLITE_LIB @mkdir -p $(dir $@) $(AR) $(ARFLAGS) $(MICROLITE_LIB_PATH) $(MICROLITE_LIB_OBJS) -$(MICRO_SPEECH_TEST_BINARY): $(MICRO_SPEECH_TEST_OBJS) $(MICROLITE_LIB_PATH) - @mkdir -p $(dir $@) - $(CXX) $(CXXFLAGS) $(INCLUDES) \ - -o $(MICRO_SPEECH_TEST_BINARY) $(MICRO_SPEECH_TEST_OBJS) \ - $(LIBFLAGS) $(MICROLITE_LIB_PATH) $(LDFLAGS) $(MICROLITE_LIBS) - -micro_speech_test: $(MICRO_SPEECH_TEST_BINARY) -micro_speech_test_bin: $(MICRO_SPEECH_TEST_BINARY).bin - -test_micro_speech: $(MICRO_SPEECH_TEST_BINARY) - $(TEST_SCRIPT) $(MICRO_SPEECH_TEST_BINARY) '~~~ALL TESTS PASSED~~~' - -$(PUSHBUTTON_MICRO_SPEECH_TEST_BINARY): $(PUSHBUTTON_MICRO_SPEECH_TEST_OBJS) $(MICROLITE_LIB_PATH) - @mkdir -p $(dir $@) - $(CXX) $(CXXFLAGS) $(INCLUDES) \ - -o $(PUSHBUTTON_MICRO_SPEECH_TEST_BINARY) $(PUSHBUTTON_MICRO_SPEECH_TEST_OBJS) \ - $(LIBFLAGS) $(MICROLITE_LIB_PATH) $(LDFLAGS) $(MICROLITE_LIBS) - -pushbutton_micro_speech_test: $(PUSHBUTTON_MICRO_SPEECH_TEST_BINARY) -pushbutton_micro_speech_test_bin: $(PUSHBUTTON_MICRO_SPEECH_TEST_BINARY).bin - -test_pushbutton_micro_speech: $(PUSHBUTTON_MICRO_SPEECH_TEST_BINARY) - $(TEST_SCRIPT) $(PUSHBUTTON_MICRO_SPEECH_TEST_BINARY) '~~~ALL TESTS PASSED~~~' - - -$(PUSHBUTTON_CMSIS_SPEECH_TEST_BINARY): $(PUSHBUTTON_CMSIS_SPEECH_TEST_OBJS) $(MICROLITE_LIB_PATH) - @mkdir -p $(dir $@) - $(CXX) $(CXXFLAGS) $(INCLUDES) \ - -o $(PUSHBUTTON_CMSIS_SPEECH_TEST_BINARY) $(PUSHBUTTON_CMSIS_SPEECH_TEST_OBJS) \ - $(LIBFLAGS) $(MICROLITE_LIB_PATH) $(LDFLAGS) $(MICROLITE_LIBS) - -pushbutton_cmsis_speech_test: $(PUSHBUTTON_CMSIS_SPEECH_TEST_BINARY) -pushbutton_cmsis_speech_test_bin: $(PUSHBUTTON_CMSIS_SPEECH_TEST_BINARY).bin - -test_pushbutton_cmsis_speech: $(PUSHBUTTON_CMSIS_SPEECH_TEST_BINARY) - $(TEST_SCRIPT) $(PUSHBUTTON_CMSIS_SPEECH_TEST_BINARY) '~~~ALL TESTS PASSED~~~' - -$(PREPROCESSOR_REFERENCE_TEST_BINARY): $(PREPROCESSOR_REFERENCE_TEST_OBJS) $(MICROLITE_LIB_PATH) - @mkdir -p $(dir $@) - $(CXX) $(CXXFLAGS) $(INCLUDES) \ - -o $(PREPROCESSOR_REFERENCE_TEST_BINARY) $(PREPROCESSOR_REFERENCE_TEST_OBJS) \ - $(LIBFLAGS) $(MICROLITE_LIB_PATH) $(LDFLAGS) $(MICROLITE_LIBS) - -preprocessor_reference_test: $(PREPROCESSOR_REFERENCE_TEST_BINARY) -preprocessor_reference_test_bin: $(PREPROCESSOR_REFERENCE_TEST_BINARY).bin - -test_preprocessor_reference: $(PREPROCESSOR_REFERENCE_TEST_BINARY) - $(TEST_SCRIPT) $(PREPROCESSOR_REFERENCE_TEST_BINARY) '~~~ALL TESTS PASSED~~~' - -$(PREPROCESSOR_FIXED_TEST_BINARY): $(PREPROCESSOR_FIXED_TEST_OBJS) $(MICROLITE_LIB_PATH) - @mkdir -p $(dir $@) - $(CXX) $(CXXFLAGS) $(INCLUDES) \ - -o $(PREPROCESSOR_FIXED_TEST_BINARY) $(PREPROCESSOR_FIXED_TEST_OBJS) \ - $(LIBFLAGS) $(MICROLITE_LIB_PATH) $(LDFLAGS) $(MICROLITE_LIBS) - -preprocessor_fixed_test: $(PREPROCESSOR_FIXED_TEST_BINARY) -preprocessor_fixed_test_bin: $(PREPROCESSOR_FIXED_TEST_BINARY).bin - -test_preprocessor_fixed: $(PREPROCESSOR_FIXED_TEST_BINARY) - $(TEST_SCRIPT) $(PREPROCESSOR_FIXED_TEST_BINARY) '~~~ALL TESTS PASSED~~~' - -$(PREPROCESSOR_1K_MICRO_TEST_BINARY): $(PREPROCESSOR_1K_MICRO_TEST_OBJS) $(MICROLITE_LIB_PATH) - @mkdir -p $(dir $@) - $(CXX) $(CXXFLAGS) $(INCLUDES) \ - -o $(PREPROCESSOR_1K_MICRO_TEST_BINARY) $(PREPROCESSOR_1K_MICRO_TEST_OBJS) \ - $(LIBFLAGS) $(MICROLITE_LIB_PATH) $(LDFLAGS) $(MICROLITE_LIBS) - -preprocessor_1k_micro_test: $(PREPROCESSOR_1K_MICRO_TEST_BINARY) -preprocessor_1k_micro_test_bin: $(PREPROCESSOR_1K_MICRO_TEST_BINARY).bin - -test_preprocessor_1k_micro: $(PREPROCESSOR_1K_MICRO_TEST_BINARY) - $(TEST_SCRIPT) $(PREPROCESSOR_1K_MICRO_TEST_BINARY) '~~~ALL TESTS PASSED~~~' - - -$(PREPROCESSOR_1K_CMSIS_TEST_BINARY): $(PREPROCESSOR_1K_CMSIS_TEST_OBJS) $(MICROLITE_LIB_PATH) - @mkdir -p $(dir $@) - $(CXX) $(CXXFLAGS) $(INCLUDES) \ - -o $(PREPROCESSOR_1K_CMSIS_TEST_BINARY) $(PREPROCESSOR_1K_CMSIS_TEST_OBJS) \ - $(LIBFLAGS) $(MICROLITE_LIB_PATH) $(LDFLAGS) $(MICROLITE_LIBS) - -preprocessor_1k_cmsis_test: $(PREPROCESSOR_1K_CMSIS_TEST_BINARY) -preprocessor_1k_cmsis_test_bin: $(PREPROCESSOR_1K_CMSIS_TEST_BINARY).bin - -test_preprocessor_1k_cmsis: $(PREPROCESSOR_1K_CMSIS_TEST_BINARY) - $(TEST_SCRIPT) $(PREPROCESSOR_1K_CMSIS_TEST_BINARY) '~~~ALL TESTS PASSED~~~' - - -$(OBJDIR)arm_bitreversal2.o: - $(CXX) $(CXXFLAGS) $(INCLUDES) -c $(CMSIS_SRC_DIR)/TransformFunctions/arm_bitreversal2.S -o $(OBJDIR)arm_bitreversal2.o - $(BINDIR)%_test : $(OBJDIR)%_test.o $(MICROLITE_LIB_PATH) @mkdir -p $(dir $@) $(CXX) $(CXXFLAGS) $(INCLUDES) \ @@ -310,8 +145,6 @@ $(BINDIR)%.bin: $(BINDIR)% @mkdir -p $(dir $@) $(OBJCOPY) $< $@ -O binary -$(info $(MICROLITE_TEST_TARGETS)) - test: test_micro_speech $(MICROLITE_TEST_TARGETS) # Gets rid of all generated files. diff --git a/tensorflow/lite/experimental/micro/tools/make/download_dependencies.sh b/tensorflow/lite/experimental/micro/tools/make/download_dependencies.sh index 81f1583bdc..82c15e32f6 100755 --- a/tensorflow/lite/experimental/micro/tools/make/download_dependencies.sh +++ b/tensorflow/lite/experimental/micro/tools/make/download_dependencies.sh @@ -33,14 +33,11 @@ GEMMLOWP_URL="https://github.com/google/gemmlowp/archive/719139ce755a0f31cbf1c37 FLATBUFFERS_URL="https://github.com/google/flatbuffers/archive/1f5eae5d6a135ff6811724f6c57f911d1f46bb15.tar.gz" CMSIS_URL="https://github.com/ARM-software/CMSIS_5/archive/5.4.0.zip" STM32_BARE_LIB_URL="https://github.com/google/stm32_bare_lib/archive/c07d611fb0af58450c5a3e0ab4d52b47f99bc82d.zip" -<<<<<<< HEAD +SIFIVE_FE310_LIB_URL="https://github.com/sifive/freedom-e-sdk/archive/baeeb8fd497a99b3c141d7494309ec2e64f19bdf.zip" +RISCV_TOOLCHAIN_URL="https://static.dev.sifive.com/dev-tools/riscv64-unknown-elf-gcc-20181030-x86_64-linux-ubuntu14.tar.gz" AP3_URL="https://github.com/AmbiqMicro/TFLiteMicro_Apollo3/archive/dfbcef9a57276c087d95aab7cb234f1d4c9eaaba.zip" CUST_CMSIS_URL="https://github.com/AmbiqMicro/TFLiteMicro_CustCMSIS/archive/8f63966c5692e6a3a83956efd2e4aed77c4c9949.zip" GCC_EMBEDDED_URL="https://developer.arm.com/-/media/Files/downloads/gnu-rm/7-2018q2/gcc-arm-none-eabi-7-2018-q2-update-linux.tar.bz2" -======= -SIFIVE_FE310_LIB_URL="https://github.com/sifive/freedom-e-sdk/archive/baeeb8fd497a99b3c141d7494309ec2e64f19bdf.zip" -RISCV_TOOLCHAIN_URL="https://static.dev.sifive.com/dev-tools/riscv64-unknown-elf-gcc-20181030-x86_64-linux-ubuntu14.tar.gz" ->>>>>>> upstream/master download_and_extract() { local usage="Usage: download_and_extract URL DIR" diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc index de48d5d46f..6ed402a623 100644 --- a/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc +++ b/tensorflow/lite/experimental/micro/tools/make/targets/apollo3evb_makefile.inc @@ -101,7 +101,7 @@ ifeq ($(TARGET), apollo3evb) $(CMSIS_SRC_DIR)/StatisticsFunctions/arm_mean_q15.c \ $(CMSIS_SRC_DIR)/StatisticsFunctions/arm_max_q7.c - AP3_EXT_MICRO_DIR := tensorflow/lite/experimental/micro/examples/micro_speech/apollo3_ext + AP3_EXT_MICRO_DIR := $(MAKEFILE_DIR)/downloads/apollo3_ext AP3_MICRO_DIR := tensorflow/lite/experimental/micro/examples/micro_speech/apollo3 CMSIS_DIR := tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS CMSIS_EXT_DIR := $(MAKEFILE_DIR)/downloads/CMSIS_ext @@ -109,41 +109,6 @@ ifeq ($(TARGET), apollo3evb) MICRO_SPEECH_TEST_SRCS += \ $(AP3_MICRO_DIR)/_main.c - PREPROCESSOR_1K_CMSIS_TEST_SRCS := \ - $(PREPROCESSOR_1K_SRCS) \ - $(CMSIS_DIR)/preprocessor.cc \ - $(CMSIS_EXT_DIR)/arm_cmplx_mag_squared_q10p6.c \ - $(CMSIS_DIR)/hanning.c \ - $(AP3_EXT_MICRO_DIR)/system_apollo3.c \ - $(AP3_MICRO_DIR)/_main.c \ - $(CMSIS_SRCS) - - PREPROCESSOR_1K_MICRO_TEST_SRCS := \ - $(PREPROCESSOR_1K_SRCS) \ - $(AP3_MICRO_DIR)/../fixed_point/preprocessor.cc \ - $(AP3_EXT_MICRO_DIR)/system_apollo3.c \ - $(AP3_MICRO_DIR)/_main.c - - PUSHBUTTON_MICRO_SPEECH_TEST_SRCS := \ - $(AP3_MICRO_DIR)/../preprocessor.cc \ - $(AP3_MICRO_DIR)/pushbutton_main.c \ - $(AP3_MICRO_DIR)/pushbutton_test.cc \ - $(AP3_MICRO_DIR)/../tiny_conv_model_data.cc \ - $(APOLLO3_SDK)/devices/am_devices_led.c - - PUSHBUTTON_CMSIS_SPEECH_TEST_SRCS := \ - $(AP3_MICRO_DIR)/pushbutton_main.c \ - $(AP3_MICRO_DIR)/pushbutton_test.cc \ - $(AP3_MICRO_DIR)/../tiny_conv_model_data.cc \ - $(CMSIS_DIR)/preprocessor.cc \ - $(CMSIS_EXT_DIR)/arm_cmplx_mag_squared_q10p6.c \ - $(CMSIS_DIR)/hanning.c \ - $(APOLLO3_SDK)/devices/am_devices_led.c \ - $(CMSIS_SRCS) - - PREPROCESSOR_TEST_SRCS += \ - $(AP3_MICRO_DIR)/_main.c - TEST_SCRIPT := tensorflow/lite/experimental/log_test/test_apollo3evb_binary.sh # These are tests that don't currently work on the Apollo3 board. EXCLUDED_TESTS := \ -- GitLab From b71a6b031adf0da61db59312847cccb675771b84 Mon Sep 17 00:00:00 2001 From: Trevor Morris Date: Thu, 20 Dec 2018 14:51:38 -0800 Subject: [PATCH 078/622] Fix input size, add comment --- tensorflow/core/grappler/optimizers/constant_folding.cc | 7 ++++--- tensorflow/core/grappler/optimizers/constant_folding.h | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tensorflow/core/grappler/optimizers/constant_folding.cc b/tensorflow/core/grappler/optimizers/constant_folding.cc index 5a053d3a89..a5f6f57747 100644 --- a/tensorflow/core/grappler/optimizers/constant_folding.cc +++ b/tensorflow/core/grappler/optimizers/constant_folding.cc @@ -1010,7 +1010,7 @@ Status ConstantFolding::EvaluateOneFoldable(const NodeDef& node, } }); - size_t input_size = 0; + size_t total_inputs_size = 0; for (const auto& input : node.input()) { const TensorId input_tensor = ParseTensorName(input); if (input_tensor.index() < 0) { @@ -1025,10 +1025,10 @@ Status ConstantFolding::EvaluateOneFoldable(const NodeDef& node, } TF_RETURN_IF_ERROR(CheckAttrExists(*input_node, "value")); const TensorProto& raw_val = input_node->attr().at("value").tensor(); - input_size += raw_val.tensor_content().size(); Tensor* value = new Tensor(raw_val.dtype(), raw_val.tensor_shape()); CHECK(value->FromProto(raw_val)); inputs.emplace_back(value); + total_inputs_size += value->TotalBytes(); } TF_RETURN_IF_ERROR(EvaluateNode(node, inputs, &output_tensors)); @@ -1043,7 +1043,8 @@ Status ConstantFolding::EvaluateOneFoldable(const NodeDef& node, node_name = strings::StrCat(node_name, "-", i); } if (output_tensors[i].tensor) { - Status s = CreateNodeDef(node_name, output_tensors[i], &outputs->at(i), input_size); + Status s = CreateNodeDef(node_name, output_tensors[i], &outputs->at(i), + total_inputs_size); if (!s.ok()) { *result_too_large = true; return s; diff --git a/tensorflow/core/grappler/optimizers/constant_folding.h b/tensorflow/core/grappler/optimizers/constant_folding.h index 8843ac161f..58a962ba9b 100644 --- a/tensorflow/core/grappler/optimizers/constant_folding.h +++ b/tensorflow/core/grappler/optimizers/constant_folding.h @@ -35,6 +35,8 @@ const char kConstantFoldingCtrl[] = "ConstantFoldingCtrl"; // Constant folding optimization for a graph. class ConstantFolding : public GraphOptimizer { public: + // The size limit will only be considered if the newly created node is greater + // than input_size (optional). static Status CreateNodeDef(const string& name, const TensorValue& tensor, NodeDef* node, size_t input_size = 0); static string AddControlDependency(const string& input_name, GraphDef* graph, -- GitLab From 676e0be5b166d08e4af5c3044e889c66de6837c9 Mon Sep 17 00:00:00 2001 From: Trevor Morris Date: Thu, 20 Dec 2018 15:16:11 -0800 Subject: [PATCH 079/622] Rename input_size -> original_size --- tensorflow/core/grappler/optimizers/constant_folding.cc | 5 +++-- tensorflow/core/grappler/optimizers/constant_folding.h | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tensorflow/core/grappler/optimizers/constant_folding.cc b/tensorflow/core/grappler/optimizers/constant_folding.cc index a5f6f57747..89be08800d 100644 --- a/tensorflow/core/grappler/optimizers/constant_folding.cc +++ b/tensorflow/core/grappler/optimizers/constant_folding.cc @@ -903,7 +903,7 @@ DataType GetDataTypeFromNodeOrProps(const NodeDef& node, Status ConstantFolding::CreateNodeDef(const string& name, const TensorValue& tensor, NodeDef* node, - size_t input_size) { + size_t original_size) { node->set_name(name); node->set_op("Const"); @@ -980,7 +980,8 @@ Status ConstantFolding::CreateNodeDef(const string& name, encoded_size = t->tensor_content().size(); } node->mutable_attr()->insert({"value", attr_tensor}); - if (encoded_size > input_size && encoded_size >= 10 * 1024 * 1024) { + + if (encoded_size > original_size && encoded_size >= 10 * 1024 * 1024) { return errors::InvalidArgument( strings::StrCat("Can't fold ", name, ", its size would be too large")); } diff --git a/tensorflow/core/grappler/optimizers/constant_folding.h b/tensorflow/core/grappler/optimizers/constant_folding.h index 58a962ba9b..bb86c4aeb3 100644 --- a/tensorflow/core/grappler/optimizers/constant_folding.h +++ b/tensorflow/core/grappler/optimizers/constant_folding.h @@ -36,9 +36,9 @@ const char kConstantFoldingCtrl[] = "ConstantFoldingCtrl"; class ConstantFolding : public GraphOptimizer { public: // The size limit will only be considered if the newly created node is greater - // than input_size (optional). + // than original_size (optional). static Status CreateNodeDef(const string& name, const TensorValue& tensor, - NodeDef* node, size_t input_size = 0); + NodeDef* node, size_t original_size = 0); static string AddControlDependency(const string& input_name, GraphDef* graph, NodeMap* node_map); -- GitLab From e387f58b820ff689eaf99dea0e5fd50865fc3a1c Mon Sep 17 00:00:00 2001 From: Pete Warden Date: Thu, 20 Dec 2018 16:02:17 -0800 Subject: [PATCH 080/622] Fixed formatting to match required style --- .../examples/micro_speech/CMSIS/hanning.cc | 92 +++++++++---------- .../examples/micro_speech/CMSIS/sin_1k.cc | 92 +++++++++---------- .../micro_speech/apollo3/pushbutton_test.cc | 17 ++-- .../examples/micro_speech/preprocessor.cc | 7 +- .../examples/micro_speech/preprocessor.h | 2 +- 5 files changed, 100 insertions(+), 110 deletions(-) diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.cc index deb30ffd88..e6a11ce52c 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.cc @@ -17,51 +17,47 @@ limitations under the License. const int g_hanning_size = 480; const int16_t g_hanning[480] = { - 0, 1, 5, 12, 22, 35, 50, 69, 90, 114, - 140, 170, 202, 237, 275, 316, 359, 405, 454, 506, - 560, 617, 677, 740, 805, 873, 943, 1016, 1092, 1171, - 1252, 1336, 1422, 1511, 1602, 1696, 1793, 1892, 1993, 2097, - 2204, 2312, 2424, 2537, 2653, 2772, 2893, 3016, 3141, 3269, - 3399, 3531, 3665, 3802, 3941, 4082, 4225, 4370, 4517, 4666, - 4817, 4971, 5126, 5283, 5442, 5603, 5765, 5930, 6096, 6265, - 6435, 6606, 6779, 6954, 7131, 7309, 7489, 7670, 7853, 8037, - 8223, 8410, 8598, 8788, 8979, 9171, 9365, 9560, 9756, 9953, - 10151, 10350, 10551, 10752, 10954, 11157, 11362, 11567, 11772, 11979, - 12186, 12395, 12603, 12813, 13023, 13233, 13445, 13656, 13868, 14081, - 14294, 14507, 14721, 14935, 15149, 15363, 15578, 15793, 16008, 16222, - 16437, 16652, 16867, 17082, 17297, 17511, 17725, 17939, 18153, 18367, - 18580, 18793, 19005, 19217, 19428, 19639, 19850, 20059, 20269, 20477, - 20685, 20892, 21098, 21303, 21508, 21712, 21914, 22116, 22317, 22517, - 22716, 22913, 23110, 23305, 23499, 23692, 23884, 24075, 24264, 24451, - 24638, 24823, 25006, 25188, 25369, 25548, 25725, 25901, 26075, 26247, - 26418, 26587, 26754, 26920, 27083, 27245, 27405, 27563, 27719, 27874, - 28026, 28176, 28324, 28470, 28614, 28756, 28896, 29034, 29169, 29303, - 29434, 29563, 29689, 29813, 29935, 30055, 30172, 30287, 30400, 30510, - 30617, 30723, 30825, 30926, 31023, 31119, 31211, 31301, 31389, 31474, - 31556, 31636, 31713, 31788, 31860, 31929, 31996, 32059, 32121, 32179, - 32235, 32288, 32338, 32386, 32430, 32472, 32512, 32548, 32582, 32613, - 32641, 32666, 32689, 32708, 32725, 32739, 32751, 32759, 32765, 32767, - 32767, 32765, 32759, 32751, 32739, 32725, 32708, 32689, 32666, 32641, - 32613, 32582, 32548, 32512, 32472, 32430, 32386, 32338, 32288, 32235, - 32179, 32121, 32059, 31996, 31929, 31860, 31788, 31713, 31636, 31556, - 31474, 31389, 31301, 31211, 31119, 31023, 30926, 30825, 30723, 30617, - 30510, 30400, 30287, 30172, 30055, 29935, 29813, 29689, 29563, 29434, - 29303, 29169, 29034, 28896, 28756, 28614, 28470, 28324, 28176, 28026, - 27874, 27719, 27563, 27405, 27245, 27083, 26920, 26754, 26587, 26418, - 26247, 26075, 25901, 25725, 25548, 25369, 25188, 25006, 24823, 24638, - 24451, 24264, 24075, 23884, 23692, 23499, 23305, 23110, 22913, 22716, - 22517, 22317, 22116, 21914, 21712, 21508, 21303, 21098, 20892, 20685, - 20477, 20269, 20059, 19850, 19639, 19428, 19217, 19005, 18793, 18580, - 18367, 18153, 17939, 17725, 17511, 17297, 17082, 16867, 16652, 16437, - 16222, 16008, 15793, 15578, 15363, 15149, 14935, 14721, 14507, 14294, - 14081, 13868, 13656, 13445, 13233, 13023, 12813, 12603, 12395, 12186, - 11979, 11772, 11567, 11362, 11157, 10954, 10752, 10551, 10350, 10151, - 9953, 9756, 9560, 9365, 9171, 8979, 8788, 8598, 8410, 8223, - 8037, 7853, 7670, 7489, 7309, 7131, 6954, 6779, 6606, 6435, - 6265, 6096, 5930, 5765, 5603, 5442, 5283, 5126, 4971, 4817, - 4666, 4517, 4370, 4225, 4082, 3941, 3802, 3665, 3531, 3399, - 3269, 3141, 3016, 2893, 2772, 2653, 2537, 2424, 2312, 2204, - 2097, 1993, 1892, 1793, 1696, 1602, 1511, 1422, 1336, 1252, - 1171, 1092, 1016, 943, 873, 805, 740, 677, 617, 560, - 506, 454, 405, 359, 316, 275, 237, 202, 170, 140, - 114, 90, 69, 50, 35, 22, 12, 5, 1, 0}; + 0, 1, 5, 12, 22, 35, 50, 69, 90, 114, 140, + 170, 202, 237, 275, 316, 359, 405, 454, 506, 560, 617, + 677, 740, 805, 873, 943, 1016, 1092, 1171, 1252, 1336, 1422, + 1511, 1602, 1696, 1793, 1892, 1993, 2097, 2204, 2312, 2424, 2537, + 2653, 2772, 2893, 3016, 3141, 3269, 3399, 3531, 3665, 3802, 3941, + 4082, 4225, 4370, 4517, 4666, 4817, 4971, 5126, 5283, 5442, 5603, + 5765, 5930, 6096, 6265, 6435, 6606, 6779, 6954, 7131, 7309, 7489, + 7670, 7853, 8037, 8223, 8410, 8598, 8788, 8979, 9171, 9365, 9560, + 9756, 9953, 10151, 10350, 10551, 10752, 10954, 11157, 11362, 11567, 11772, + 11979, 12186, 12395, 12603, 12813, 13023, 13233, 13445, 13656, 13868, 14081, + 14294, 14507, 14721, 14935, 15149, 15363, 15578, 15793, 16008, 16222, 16437, + 16652, 16867, 17082, 17297, 17511, 17725, 17939, 18153, 18367, 18580, 18793, + 19005, 19217, 19428, 19639, 19850, 20059, 20269, 20477, 20685, 20892, 21098, + 21303, 21508, 21712, 21914, 22116, 22317, 22517, 22716, 22913, 23110, 23305, + 23499, 23692, 23884, 24075, 24264, 24451, 24638, 24823, 25006, 25188, 25369, + 25548, 25725, 25901, 26075, 26247, 26418, 26587, 26754, 26920, 27083, 27245, + 27405, 27563, 27719, 27874, 28026, 28176, 28324, 28470, 28614, 28756, 28896, + 29034, 29169, 29303, 29434, 29563, 29689, 29813, 29935, 30055, 30172, 30287, + 30400, 30510, 30617, 30723, 30825, 30926, 31023, 31119, 31211, 31301, 31389, + 31474, 31556, 31636, 31713, 31788, 31860, 31929, 31996, 32059, 32121, 32179, + 32235, 32288, 32338, 32386, 32430, 32472, 32512, 32548, 32582, 32613, 32641, + 32666, 32689, 32708, 32725, 32739, 32751, 32759, 32765, 32767, 32767, 32765, + 32759, 32751, 32739, 32725, 32708, 32689, 32666, 32641, 32613, 32582, 32548, + 32512, 32472, 32430, 32386, 32338, 32288, 32235, 32179, 32121, 32059, 31996, + 31929, 31860, 31788, 31713, 31636, 31556, 31474, 31389, 31301, 31211, 31119, + 31023, 30926, 30825, 30723, 30617, 30510, 30400, 30287, 30172, 30055, 29935, + 29813, 29689, 29563, 29434, 29303, 29169, 29034, 28896, 28756, 28614, 28470, + 28324, 28176, 28026, 27874, 27719, 27563, 27405, 27245, 27083, 26920, 26754, + 26587, 26418, 26247, 26075, 25901, 25725, 25548, 25369, 25188, 25006, 24823, + 24638, 24451, 24264, 24075, 23884, 23692, 23499, 23305, 23110, 22913, 22716, + 22517, 22317, 22116, 21914, 21712, 21508, 21303, 21098, 20892, 20685, 20477, + 20269, 20059, 19850, 19639, 19428, 19217, 19005, 18793, 18580, 18367, 18153, + 17939, 17725, 17511, 17297, 17082, 16867, 16652, 16437, 16222, 16008, 15793, + 15578, 15363, 15149, 14935, 14721, 14507, 14294, 14081, 13868, 13656, 13445, + 13233, 13023, 12813, 12603, 12395, 12186, 11979, 11772, 11567, 11362, 11157, + 10954, 10752, 10551, 10350, 10151, 9953, 9756, 9560, 9365, 9171, 8979, + 8788, 8598, 8410, 8223, 8037, 7853, 7670, 7489, 7309, 7131, 6954, + 6779, 6606, 6435, 6265, 6096, 5930, 5765, 5603, 5442, 5283, 5126, + 4971, 4817, 4666, 4517, 4370, 4225, 4082, 3941, 3802, 3665, 3531, + 3399, 3269, 3141, 3016, 2893, 2772, 2653, 2537, 2424, 2312, 2204, + 2097, 1993, 1892, 1793, 1696, 1602, 1511, 1422, 1336, 1252, 1171, + 1092, 1016, 943, 873, 805, 740, 677, 617, 560, 506, 454, + 405, 359, 316, 275, 237, 202, 170, 140, 114, 90, 69, + 50, 35, 22, 12, 5, 1, 0}; diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.cc index 3523d3ab76..45e9f798ef 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.cc @@ -17,51 +17,47 @@ limitations under the License. const int g_sin_1k_size = 480; const int16_t g_sin_1k[480] = { - 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, - -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, - 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, - -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, - 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, - 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, - -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, - 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, - 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, - -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, - 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, - -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, - 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, - 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, - -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, - 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, - 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, - -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, - 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, - -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, - 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, - 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, - -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, - 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, - 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, - -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, - 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, - -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, - 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, - 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, - -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, - 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, - 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, - -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, - 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, - -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, - 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, - 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, - -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, - 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, - 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, - -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, - 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, - -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, - 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, - 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, - -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, - 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253}; + 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, -2317, + -3027, -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, + 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, 0, + 1253, 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, + -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, 2317, + 1253, 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, + 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, -3276, + -3027, -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, + 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, 2317, + 3027, 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, + -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, 0, + -1253, -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, + 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, -2317, + -1253, 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, + -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, 3276, + 3027, 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, + 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, -2317, + -3027, -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, + 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, 0, + 1253, 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, + -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, 2317, + 1253, 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, + 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, -3276, + -3027, -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, + 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, 2317, + 3027, 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, + -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, 0, + -1253, -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, + 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, -2317, + -1253, 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, + -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, 3276, + 3027, 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, + 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, -2317, + -3027, -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, + 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, 0, + 1253, 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, + -3276, -3027, -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, 2317, + 1253, 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, + 2317, 3027, 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, -3276, + -3027, -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, + 0, -1253, -2317, -3027, -3276, -3027, -2317, -1253, 0, 1253, 2317, + 3027, 3276, 3027, 2317, 1253, 0, -1253, -2317, -3027, -3276, -3027, + -2317, -1253, 0, 1253, 2317, 3027, 3276, 3027, 2317, 1253, 0, + -1253, -2317, -3027, -3276, -3027, -2317, -1253}; diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_test.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_test.cc index 47f273c511..95043f857b 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_test.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_test.cc @@ -13,15 +13,16 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -/* This file is a modification of the Tensorflow Micro Lite file micro_speech_test.cc */ +/* This file is a modification of the Tensorflow Micro Lite file + * micro_speech_test.cc */ -#include "tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.h" #include "tensorflow/lite/c/c_api_internal.h" -#include "tensorflow/lite/experimental/micro/micro_error_reporter.h" -#include "tensorflow/lite/experimental/micro/testing/micro_test.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.h" #include "tensorflow/lite/experimental/micro/examples/micro_speech/tiny_conv_model_data.h" #include "tensorflow/lite/experimental/micro/kernels/all_ops_resolver.h" +#include "tensorflow/lite/experimental/micro/micro_error_reporter.h" #include "tensorflow/lite/experimental/micro/micro_interpreter.h" +#include "tensorflow/lite/experimental/micro/testing/micro_test.h" #include "tensorflow/lite/schema/schema_generated.h" #include "tensorflow/lite/version.h" @@ -37,12 +38,11 @@ TF_LITE_MICRO_TEST(TestPreprocessor) { tflite::MicroErrorReporter micro_error_reporter; tflite::ErrorReporter* error_reporter = µ_error_reporter; - uint8_t preprocessed_data[43*49]; - TfLiteStatus preprocess_1sec_status = Preprocess_1sec( - error_reporter, captured_data, preprocessed_data); + uint8_t preprocessed_data[43 * 49]; + TfLiteStatus preprocess_1sec_status = + Preprocess_1sec(error_reporter, captured_data, preprocessed_data); TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, preprocess_1sec_status); - // Map the model into a usable data structure. This doesn't involve any // copying or parsing, it's a very lightweight operation. const tflite::Model* model = ::tflite::GetModel(g_tiny_conv_model_data); @@ -111,7 +111,6 @@ TF_LITE_MICRO_TEST(TestPreprocessor) { g_no_score = output->data.uint8[kNoIndex]; error_reporter->Report("Ran successfully\n"); - } TF_LITE_MICRO_TESTS_END diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.cc index 743d229224..7b7db173ab 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.cc @@ -145,11 +145,10 @@ TfLiteStatus Preprocess(tflite::ErrorReporter* error_reporter, } TfLiteStatus Preprocess_1sec(tflite::ErrorReporter* error_reporter, - const int16_t* input, uint8_t* output) { + const int16_t* input, uint8_t* output) { int i; - for(i=0; i<49; i++) { - Preprocess(error_reporter, input+i*320, 480, 43, output+i*43); + for (i = 0; i < 49; i++) { + Preprocess(error_reporter, input + i * 320, 480, 43, output + i * 43); } return kTfLiteOk; } - diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.h b/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.h index 0057b4505f..d710beecee 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.h +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.h @@ -29,6 +29,6 @@ TfLiteStatus Preprocess(tflite::ErrorReporter* error_reporter, uint8_t* output); TfLiteStatus Preprocess_1sec(tflite::ErrorReporter* error_reporter, - const int16_t* input, uint8_t* output); + const int16_t* input, uint8_t* output); #endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_PREPROCESSOR_H_ -- GitLab From 71bcf47503294a589db51852552b55ed1b5f4ddb Mon Sep 17 00:00:00 2001 From: Pete Warden Date: Thu, 20 Dec 2018 16:51:03 -0800 Subject: [PATCH 081/622] Formatting fixes for the C files --- .../examples/micro_speech/apollo3/_main.c | 188 ++++--- .../micro_speech/apollo3/pushbutton_main.c | 460 +++++++++--------- 2 files changed, 314 insertions(+), 334 deletions(-) diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/_main.c b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/_main.c index bd238ac55f..b49d5c50ff 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/_main.c +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/_main.c @@ -14,8 +14,8 @@ limitations under the License. ==============================================================================*/ #include -#include "am_mcu_apollo.h" // Defines AM_CMSIS_REGS #include "am_bsp.h" +#include "am_mcu_apollo.h" // Defines AM_CMSIS_REGS #include "am_util.h" //***************************************************************************** @@ -23,101 +23,95 @@ limitations under the License. // The entry point for the application. // //***************************************************************************** -extern int main(int argc, char**argv); - -void DebugLog(const char* s) { am_util_stdio_printf( "%s", s); } -void DebugLogInt32(int32_t i) { am_util_stdio_printf( "%d", i); } -void DebugLogUInt32(uint32_t i) { am_util_stdio_printf( "%d", i); } -void DebugLogHex(uint32_t i) { am_util_stdio_printf( "0x%8x", i); } -void DebugLogFloat(float i) { am_util_stdio_printf( "%f", i); } - -int _main(void) -{ - am_util_id_t sIdDevice; - uint32_t ui32StrBuf; - - // - // Set the clock frequency. - // - am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_SYSCLK_MAX, 0); - - // - // Set the default cache configuration - // - am_hal_cachectrl_config(&am_hal_cachectrl_defaults); - am_hal_cachectrl_enable(); - - // - // Configure the board for low power operation. - // - am_bsp_low_power_init(); - - // - // Initialize the printf interface for UART output - // - am_bsp_uart_printf_enable(); - - // - // Print the banner. - // - am_util_stdio_terminal_clear(); - am_util_stdio_printf("Hello World!\n\n"); - - // - // Print the device info. - // - am_util_id_device(&sIdDevice); - am_util_stdio_printf("Vendor Name: %s\n", sIdDevice.pui8VendorName); - am_util_stdio_printf("Device type: %s\n", sIdDevice.pui8DeviceName); - - - am_util_stdio_printf("Qualified: %s\n", - sIdDevice.sMcuCtrlDevice.ui32Qualified ? - "Yes" : "No"); - - am_util_stdio_printf("Device Info:\n" - "\tPart number: 0x%08X\n" - "\tChip ID0: 0x%08X\n" - "\tChip ID1: 0x%08X\n" - "\tRevision: 0x%08X (Rev%c%c)\n", - sIdDevice.sMcuCtrlDevice.ui32ChipPN, - sIdDevice.sMcuCtrlDevice.ui32ChipID0, - sIdDevice.sMcuCtrlDevice.ui32ChipID1, - sIdDevice.sMcuCtrlDevice.ui32ChipRev, - sIdDevice.ui8ChipRevMaj, sIdDevice.ui8ChipRevMin ); - - // - // If not a multiple of 1024 bytes, append a plus sign to the KB. - // - ui32StrBuf = ( sIdDevice.sMcuCtrlDevice.ui32FlashSize % 1024 ) ? '+' : 0; - am_util_stdio_printf("\tFlash size: %7d (%d KB%s)\n", - sIdDevice.sMcuCtrlDevice.ui32FlashSize, - sIdDevice.sMcuCtrlDevice.ui32FlashSize / 1024, - &ui32StrBuf); - - ui32StrBuf = ( sIdDevice.sMcuCtrlDevice.ui32SRAMSize % 1024 ) ? '+' : 0; - am_util_stdio_printf("\tSRAM size: %7d (%d KB%s)\n\n", - sIdDevice.sMcuCtrlDevice.ui32SRAMSize, - sIdDevice.sMcuCtrlDevice.ui32SRAMSize / 1024, - &ui32StrBuf); - - // - // Print the compiler version. - // - am_util_stdio_printf("App Compiler: %s\n", COMPILER_VERSION); +extern int main(int argc, char** argv); + +void DebugLog(const char* s) { am_util_stdio_printf("%s", s); } +void DebugLogInt32(int32_t i) { am_util_stdio_printf("%d", i); } +void DebugLogUInt32(uint32_t i) { am_util_stdio_printf("%d", i); } +void DebugLogHex(uint32_t i) { am_util_stdio_printf("0x%8x", i); } +void DebugLogFloat(float i) { am_util_stdio_printf("%f", i); } + +int _main(void) { + am_util_id_t sIdDevice; + uint32_t ui32StrBuf; + + // + // Set the clock frequency. + // + am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_SYSCLK_MAX, 0); + + // + // Set the default cache configuration + // + am_hal_cachectrl_config(&am_hal_cachectrl_defaults); + am_hal_cachectrl_enable(); + + // + // Configure the board for low power operation. + // + am_bsp_low_power_init(); + + // + // Initialize the printf interface for UART output + // + am_bsp_uart_printf_enable(); + + // + // Print the banner. + // + am_util_stdio_terminal_clear(); + am_util_stdio_printf("Hello World!\n\n"); + + // + // Print the device info. + // + am_util_id_device(&sIdDevice); + am_util_stdio_printf("Vendor Name: %s\n", sIdDevice.pui8VendorName); + am_util_stdio_printf("Device type: %s\n", sIdDevice.pui8DeviceName); + + am_util_stdio_printf("Qualified: %s\n", + sIdDevice.sMcuCtrlDevice.ui32Qualified ? "Yes" : "No"); + + am_util_stdio_printf( + "Device Info:\n" + "\tPart number: 0x%08X\n" + "\tChip ID0: 0x%08X\n" + "\tChip ID1: 0x%08X\n" + "\tRevision: 0x%08X (Rev%c%c)\n", + sIdDevice.sMcuCtrlDevice.ui32ChipPN, sIdDevice.sMcuCtrlDevice.ui32ChipID0, + sIdDevice.sMcuCtrlDevice.ui32ChipID1, + sIdDevice.sMcuCtrlDevice.ui32ChipRev, sIdDevice.ui8ChipRevMaj, + sIdDevice.ui8ChipRevMin); + + // + // If not a multiple of 1024 bytes, append a plus sign to the KB. + // + ui32StrBuf = (sIdDevice.sMcuCtrlDevice.ui32FlashSize % 1024) ? '+' : 0; + am_util_stdio_printf( + "\tFlash size: %7d (%d KB%s)\n", sIdDevice.sMcuCtrlDevice.ui32FlashSize, + sIdDevice.sMcuCtrlDevice.ui32FlashSize / 1024, &ui32StrBuf); + + ui32StrBuf = (sIdDevice.sMcuCtrlDevice.ui32SRAMSize % 1024) ? '+' : 0; + am_util_stdio_printf( + "\tSRAM size: %7d (%d KB%s)\n\n", sIdDevice.sMcuCtrlDevice.ui32SRAMSize, + sIdDevice.sMcuCtrlDevice.ui32SRAMSize / 1024, &ui32StrBuf); + + // + // Print the compiler version. + // + am_util_stdio_printf("App Compiler: %s\n", COMPILER_VERSION); #ifdef AM_PART_APOLLO3 - am_util_stdio_printf("HAL Compiler: %s\n", g_ui8HALcompiler); - am_util_stdio_printf("HAL SDK version: %d.%d.%d\n", - g_ui32HALversion.s.Major, - g_ui32HALversion.s.Minor, - g_ui32HALversion.s.Revision); - am_util_stdio_printf("HAL compiled with %s-style registers\n", - g_ui32HALversion.s.bAMREGS ? "AM_REG" : "CMSIS"); - - am_util_stdio_printf("&sIdDevice: 0x%x, &ui32StrBuf: 0x%x\n", &sIdDevice, &ui32StrBuf); - am_hal_security_info_t secInfo; - char sINFO[32]; - uint32_t ui32Status; -#endif // AM_PART_APOLLO3 - main(0, NULL); + am_util_stdio_printf("HAL Compiler: %s\n", g_ui8HALcompiler); + am_util_stdio_printf("HAL SDK version: %d.%d.%d\n", g_ui32HALversion.s.Major, + g_ui32HALversion.s.Minor, g_ui32HALversion.s.Revision); + am_util_stdio_printf("HAL compiled with %s-style registers\n", + g_ui32HALversion.s.bAMREGS ? "AM_REG" : "CMSIS"); + + am_util_stdio_printf("&sIdDevice: 0x%x, &ui32StrBuf: 0x%x\n", &sIdDevice, + &ui32StrBuf); + am_hal_security_info_t secInfo; + char sINFO[32]; + uint32_t ui32Status; +#endif // AM_PART_APOLLO3 + main(0, NULL); } diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_main.c b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_main.c index d06c81be66..afee38343b 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_main.c +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_main.c @@ -16,8 +16,8 @@ limitations under the License. /* This file is a modification of the Tensorflow Micro Lite file _main.c */ #include -#include "am_mcu_apollo.h" // Defines AM_CMSIS_REGS #include "am_bsp.h" +#include "am_mcu_apollo.h" // Defines AM_CMSIS_REGS #include "am_util.h" #define ARM_MATH_CM4 @@ -25,12 +25,12 @@ limitations under the License. //***************************************************************************** // Parameters -// +// // Total number of bytes transferred = 320*50*2 = 32000 //***************************************************************************** -#define FRAME_SIZE 320 // Capture one 320-sample (20-ms) frame at a time -#define NUM_FRAMES 50 // Number of frames in 1 second +#define FRAME_SIZE 320 // Capture one 320-sample (20-ms) frame at a time +#define NUM_FRAMES 50 // Number of frames in 1 second //***************************************************************************** // GLOBALS @@ -38,53 +38,51 @@ limitations under the License. volatile int16_t g_numFramesCaptured = 0; volatile bool g_bPDMDataReady = false; -int16_t captured_data[FRAME_SIZE*NUM_FRAMES]; // Location of 1-second data buffer +int16_t + captured_data[FRAME_SIZE * NUM_FRAMES]; // Location of 1-second data buffer extern uint8_t g_silence_score; extern uint8_t g_unknown_score; extern uint8_t g_yes_score; extern uint8_t g_no_score; q7_t g_scores[4] = {0}; - //***************************************************************************** // The entry point for the application. //***************************************************************************** -extern int main(int argc, char**argv); +extern int main(int argc, char** argv); -void DebugLog(const char* s) { am_util_stdio_printf( "%s", s); } -void DebugLogInt32(int32_t i) { am_util_stdio_printf( "%d", i); } -void DebugLogUInt32(uint32_t i) { am_util_stdio_printf( "%d", i); } -void DebugLogHex(uint32_t i) { am_util_stdio_printf( "0x%8x", i); } -void DebugLogFloat(float i) { am_util_stdio_printf( "%f", i); } +void DebugLog(const char* s) { am_util_stdio_printf("%s", s); } +void DebugLogInt32(int32_t i) { am_util_stdio_printf("%d", i); } +void DebugLogUInt32(uint32_t i) { am_util_stdio_printf("%d", i); } +void DebugLogHex(uint32_t i) { am_util_stdio_printf("0x%8x", i); } +void DebugLogFloat(float i) { am_util_stdio_printf("%f", i); } //***************************************************************************** // PDM configuration information. //***************************************************************************** -void *PDMHandle; - -am_hal_pdm_config_t g_sPdmConfig = -{ - .eClkDivider = AM_HAL_PDM_MCLKDIV_1, - .eLeftGain = AM_HAL_PDM_GAIN_P225DB, - .eRightGain = AM_HAL_PDM_GAIN_P225DB, - .ui32DecimationRate = 48, // OSR = 1500/16 = 96 = 2*SINCRATE --> SINC_RATE = 48 - .bHighPassEnable = 0, - .ui32HighPassCutoff = 0xB, - .ePDMClkSpeed = AM_HAL_PDM_CLK_1_5MHZ, - .bInvertI2SBCLK = 0, - .ePDMClkSource = AM_HAL_PDM_INTERNAL_CLK, - .bPDMSampleDelay = 0, - .bDataPacking = 1, - .ePCMChannels = AM_HAL_PDM_CHANNEL_RIGHT, - .bLRSwap = 0, +void* PDMHandle; + +am_hal_pdm_config_t g_sPdmConfig = { + .eClkDivider = AM_HAL_PDM_MCLKDIV_1, + .eLeftGain = AM_HAL_PDM_GAIN_P225DB, + .eRightGain = AM_HAL_PDM_GAIN_P225DB, + .ui32DecimationRate = + 48, // OSR = 1500/16 = 96 = 2*SINCRATE --> SINC_RATE = 48 + .bHighPassEnable = 0, + .ui32HighPassCutoff = 0xB, + .ePDMClkSpeed = AM_HAL_PDM_CLK_1_5MHZ, + .bInvertI2SBCLK = 0, + .ePDMClkSource = AM_HAL_PDM_INTERNAL_CLK, + .bPDMSampleDelay = 0, + .bDataPacking = 1, + .ePCMChannels = AM_HAL_PDM_CHANNEL_RIGHT, + .bLRSwap = 0, }; - //***************************************************************************** // BUTTON0 pin configuration settings. //***************************************************************************** -const am_hal_gpio_pincfg_t g_deepsleep_button0 = -{ +const am_hal_gpio_pincfg_t g_deepsleep_button0 = { .uFuncSel = 3, .eIntDir = AM_HAL_GPIO_PIN_INTDIR_LO2HI, .eGPInput = AM_HAL_GPIO_PIN_INPUT_ENABLE, @@ -93,46 +91,44 @@ const am_hal_gpio_pincfg_t g_deepsleep_button0 = //***************************************************************************** // PDM initialization. //***************************************************************************** -void pdm_init(void) -{ - // - // Initialize, power-up, and configure the PDM. - // - am_hal_pdm_initialize(0, &PDMHandle); - am_hal_pdm_power_control(PDMHandle, AM_HAL_PDM_POWER_ON, false); - am_hal_pdm_configure(PDMHandle, &g_sPdmConfig); - am_hal_pdm_enable(PDMHandle); - - // - // Configure the necessary pins. - // - am_hal_gpio_pincfg_t sPinCfg = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - -// ARPIT 181019 - //sPinCfg.uFuncSel = AM_HAL_PIN_10_PDMCLK; - //am_hal_gpio_pinconfig(10, sPinCfg); - sPinCfg.uFuncSel = AM_HAL_PIN_12_PDMCLK; - am_hal_gpio_pinconfig(12, sPinCfg); - - sPinCfg.uFuncSel = AM_HAL_PIN_11_PDMDATA; - am_hal_gpio_pinconfig(11, sPinCfg); - - //am_hal_gpio_state_write(14, AM_HAL_GPIO_OUTPUT_CLEAR); - //am_hal_gpio_pinconfig(14, g_AM_HAL_GPIO_OUTPUT); - - // - // Configure and enable PDM interrupts (set up to trigger on DMA - // completion). - // - am_hal_pdm_interrupt_enable(PDMHandle, (AM_HAL_PDM_INT_DERR - | AM_HAL_PDM_INT_DCMP - | AM_HAL_PDM_INT_UNDFL - | AM_HAL_PDM_INT_OVF)); +void pdm_init(void) { + // + // Initialize, power-up, and configure the PDM. + // + am_hal_pdm_initialize(0, &PDMHandle); + am_hal_pdm_power_control(PDMHandle, AM_HAL_PDM_POWER_ON, false); + am_hal_pdm_configure(PDMHandle, &g_sPdmConfig); + am_hal_pdm_enable(PDMHandle); + + // + // Configure the necessary pins. + // + am_hal_gpio_pincfg_t sPinCfg = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + // ARPIT 181019 + // sPinCfg.uFuncSel = AM_HAL_PIN_10_PDMCLK; + // am_hal_gpio_pinconfig(10, sPinCfg); + sPinCfg.uFuncSel = AM_HAL_PIN_12_PDMCLK; + am_hal_gpio_pinconfig(12, sPinCfg); + + sPinCfg.uFuncSel = AM_HAL_PIN_11_PDMDATA; + am_hal_gpio_pinconfig(11, sPinCfg); + + // am_hal_gpio_state_write(14, AM_HAL_GPIO_OUTPUT_CLEAR); + // am_hal_gpio_pinconfig(14, g_AM_HAL_GPIO_OUTPUT); + + // + // Configure and enable PDM interrupts (set up to trigger on DMA + // completion). + // + am_hal_pdm_interrupt_enable(PDMHandle, + (AM_HAL_PDM_INT_DERR | AM_HAL_PDM_INT_DCMP | + AM_HAL_PDM_INT_UNDFL | AM_HAL_PDM_INT_OVF)); #if AM_CMSIS_REGS - NVIC_EnableIRQ(PDM_IRQn); + NVIC_EnableIRQ(PDM_IRQn); #else - am_hal_interrupt_enable(AM_HAL_INTERRUPT_PDM); + am_hal_interrupt_enable(AM_HAL_INTERRUPT_PDM); #endif } @@ -141,196 +137,186 @@ void pdm_init(void) // Start a transaction to get some number of bytes from the PDM interface. // //***************************************************************************** -void pdm_data_get(void) -{ - // - // Configure DMA and target address. - // - am_hal_pdm_transfer_t sTransfer; - sTransfer.ui32TargetAddr = (uint32_t ) (&captured_data[FRAME_SIZE*g_numFramesCaptured]); - sTransfer.ui32TotalCount = 2*FRAME_SIZE; // Each sample is 2 bytes - - // - // Start the data transfer. - // - am_hal_pdm_dma_start(PDMHandle, &sTransfer); +void pdm_data_get(void) { + // + // Configure DMA and target address. + // + am_hal_pdm_transfer_t sTransfer; + sTransfer.ui32TargetAddr = + (uint32_t)(&captured_data[FRAME_SIZE * g_numFramesCaptured]); + sTransfer.ui32TotalCount = 2 * FRAME_SIZE; // Each sample is 2 bytes + + // + // Start the data transfer. + // + am_hal_pdm_dma_start(PDMHandle, &sTransfer); } - //***************************************************************************** // // PDM interrupt handler. // //***************************************************************************** -void am_pdm_isr(void) -{ - uint32_t ui32Status; - // - // Read the interrupt status. - // - am_hal_pdm_interrupt_status_get(PDMHandle, &ui32Status, true); - am_hal_pdm_interrupt_clear(PDMHandle, ui32Status); - - // - // Once our DMA transaction completes, send a flag to the main routine - // - if (ui32Status & AM_HAL_PDM_INT_DCMP) - g_bPDMDataReady = true; +void am_pdm_isr(void) { + uint32_t ui32Status; + // + // Read the interrupt status. + // + am_hal_pdm_interrupt_status_get(PDMHandle, &ui32Status, true); + am_hal_pdm_interrupt_clear(PDMHandle, ui32Status); + + // + // Once our DMA transaction completes, send a flag to the main routine + // + if (ui32Status & AM_HAL_PDM_INT_DCMP) g_bPDMDataReady = true; } - //***************************************************************************** // GPIO ISR // Will enable the PDM, set number of frames transferred to 0, and turn on LED //***************************************************************************** -void -am_gpio_isr(void) -{ - // - // Delay for debounce. - // - am_util_delay_ms(200); - - // - // Clear the GPIO Interrupt (write to clear). - // - am_hal_gpio_interrupt_clear(AM_HAL_GPIO_BIT(AM_BSP_GPIO_BUTTON0)); - - // Start audio transfer - am_hal_pdm_fifo_flush(PDMHandle); - pdm_data_get(); - am_hal_pdm_enable(PDMHandle); - - // - // Turn on LED 0 - // - am_devices_led_on(am_bsp_psLEDs, 0); +void am_gpio_isr(void) { + // + // Delay for debounce. + // + am_util_delay_ms(200); + + // + // Clear the GPIO Interrupt (write to clear). + // + am_hal_gpio_interrupt_clear(AM_HAL_GPIO_BIT(AM_BSP_GPIO_BUTTON0)); + + // Start audio transfer + am_hal_pdm_fifo_flush(PDMHandle); + pdm_data_get(); + am_hal_pdm_enable(PDMHandle); + + // + // Turn on LED 0 + // + am_devices_led_on(am_bsp_psLEDs, 0); } -int _main(void) -{ - am_util_id_t sIdDevice; - uint32_t ui32StrBuf; - - // - // Set the clock frequency. - // - am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_SYSCLK_MAX, 0); - - // - // Set the default cache configuration - // - am_hal_cachectrl_config(&am_hal_cachectrl_defaults); - am_hal_cachectrl_enable(); - - // - // Configure the board for low power operation. - // - am_bsp_low_power_init(); - - -#if defined(AM_BSP_NUM_BUTTONS) && defined(AM_BSP_NUM_LEDS) - // - // Configure the button pin. - // - am_hal_gpio_pinconfig(AM_BSP_GPIO_BUTTON0, g_deepsleep_button0); - - // - // Clear the GPIO Interrupt (write to clear). - // - am_hal_gpio_interrupt_clear(AM_HAL_GPIO_BIT(AM_BSP_GPIO_BUTTON0)); - - // - // Enable the GPIO/button interrupt. - // - am_hal_gpio_interrupt_enable(AM_HAL_GPIO_BIT(AM_BSP_GPIO_BUTTON0)); - - // - // Configure the LEDs. - // - am_devices_led_array_init(am_bsp_psLEDs, AM_BSP_NUM_LEDS); - - // - // Turn the LEDs off - // - for (int ix = 0; ix < AM_BSP_NUM_LEDS; ix++) - { - am_devices_led_off(am_bsp_psLEDs, ix); - } +int _main(void) { + am_util_id_t sIdDevice; + uint32_t ui32StrBuf; + + // + // Set the clock frequency. + // + am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_SYSCLK_MAX, 0); + + // + // Set the default cache configuration + // + am_hal_cachectrl_config(&am_hal_cachectrl_defaults); + am_hal_cachectrl_enable(); + + // + // Configure the board for low power operation. + // + am_bsp_low_power_init(); + +#if defined(AM_BSP_NUM_BUTTONS) && defined(AM_BSP_NUM_LEDS) + // + // Configure the button pin. + // + am_hal_gpio_pinconfig(AM_BSP_GPIO_BUTTON0, g_deepsleep_button0); + + // + // Clear the GPIO Interrupt (write to clear). + // + am_hal_gpio_interrupt_clear(AM_HAL_GPIO_BIT(AM_BSP_GPIO_BUTTON0)); + + // + // Enable the GPIO/button interrupt. + // + am_hal_gpio_interrupt_enable(AM_HAL_GPIO_BIT(AM_BSP_GPIO_BUTTON0)); + + // + // Configure the LEDs. + // + am_devices_led_array_init(am_bsp_psLEDs, AM_BSP_NUM_LEDS); + + // + // Turn the LEDs off + // + for (int ix = 0; ix < AM_BSP_NUM_LEDS; ix++) { + am_devices_led_off(am_bsp_psLEDs, ix); + } // am_devices_led_on(am_bsp_psLEDs, 1); -#endif // defined(AM_BSP_NUM_BUTTONS) && defined(AM_BSP_NUM_LEDS) +#endif // defined(AM_BSP_NUM_BUTTONS) && defined(AM_BSP_NUM_LEDS) #if AM_CMSIS_REGS - NVIC_EnableIRQ(GPIO_IRQn); -#else // AM_CMSIS_REGS - am_hal_interrupt_enable(AM_HAL_INTERRUPT_GPIO); -#endif // AM_CMSIS_REGS - - // - // Enable interrupts to the core. - // - am_hal_interrupt_master_enable(); - - // Turn on PDM - pdm_init(); - - // - // Initialize the printf interface for UART output - // - am_bsp_uart_printf_enable(); + NVIC_EnableIRQ(GPIO_IRQn); +#else // AM_CMSIS_REGS + am_hal_interrupt_enable(AM_HAL_INTERRUPT_GPIO); +#endif // AM_CMSIS_REGS + + // + // Enable interrupts to the core. + // + am_hal_interrupt_master_enable(); + + // Turn on PDM + pdm_init(); + + // + // Initialize the printf interface for UART output + // + am_bsp_uart_printf_enable(); + + // + // Print the banner. + // + am_util_stdio_terminal_clear(); + am_util_stdio_printf("Starting streaming test\n\n"); + + // Score variables + q7_t max_score = 0; + uint32_t max_score_index = 0; + + while (1) { + am_hal_interrupt_master_disable(); + + if (g_bPDMDataReady) { + g_bPDMDataReady = false; + g_numFramesCaptured++; + + if (g_numFramesCaptured < NUM_FRAMES) { + pdm_data_get(); // Start converting the next set of PCM samples. + } + + else { + g_numFramesCaptured = 0; + // am_hal_pdm_disable(PDMHandle); + am_devices_led_off(am_bsp_psLEDs, 0); + + main(0, NULL); + + g_scores[0] = (q7_t)g_silence_score - 128; + g_scores[1] = (q7_t)g_unknown_score - 128; + g_scores[2] = (q7_t)g_yes_score - 128; + g_scores[3] = (q7_t)g_no_score - 128; + + am_devices_led_off( + am_bsp_psLEDs, + max_score_index + 1); // Turn off LED for previous max score + arm_max_q7(g_scores, 4, &max_score, &max_score_index); + am_devices_led_on( + am_bsp_psLEDs, + max_score_index + 1); // Turn on LED for new max score + } + } // - // Print the banner. + // Go to Deep Sleep. // - am_util_stdio_terminal_clear(); - am_util_stdio_printf("Starting streaming test\n\n"); - - // Score variables - q7_t max_score = 0; - uint32_t max_score_index = 0; - - while(1) - { - - am_hal_interrupt_master_disable(); - - if (g_bPDMDataReady) - { - g_bPDMDataReady = false; - g_numFramesCaptured++; - - if (g_numFramesCaptured < NUM_FRAMES) { - pdm_data_get(); // Start converting the next set of PCM samples. - } - - else - { - g_numFramesCaptured = 0; - //am_hal_pdm_disable(PDMHandle); - am_devices_led_off(am_bsp_psLEDs, 0); + am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP); - main(0, NULL); - - g_scores[0] = (q7_t) g_silence_score - 128; - g_scores[1] = (q7_t) g_unknown_score - 128; - g_scores[2] = (q7_t) g_yes_score - 128; - g_scores[3] = (q7_t) g_no_score - 128; - - am_devices_led_off(am_bsp_psLEDs, max_score_index+1); // Turn off LED for previous max score - arm_max_q7(g_scores, 4, &max_score, &max_score_index); - am_devices_led_on(am_bsp_psLEDs, max_score_index+1); // Turn on LED for new max score - } - } - - // - // Go to Deep Sleep. - // - am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP); - - am_hal_interrupt_master_enable(); - - } + am_hal_interrupt_master_enable(); + } - //main(0, NULL); + // main(0, NULL); } -- GitLab From 7cae909b9608087d643e52418d162f7c5041f22b Mon Sep 17 00:00:00 2001 From: Siju Date: Fri, 21 Dec 2018 08:39:26 +0530 Subject: [PATCH 082/622] Update operation_semantics.md documentation issue --- tensorflow/compiler/xla/g3doc/operation_semantics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/compiler/xla/g3doc/operation_semantics.md b/tensorflow/compiler/xla/g3doc/operation_semantics.md index 9a9cd08c30..78d4617017 100644 --- a/tensorflow/compiler/xla/g3doc/operation_semantics.md +++ b/tensorflow/compiler/xla/g3doc/operation_semantics.md @@ -1095,7 +1095,7 @@ When `Op` is `Rem`, the sign of the result is taken from the dividend, and the absolute value of the result is always less than the divisor's absolute value. Integer division overflow (signed/unsigned division/remainder by zero or signed -divison/remainder of `INT_SMIN` with `-1`) produces an implementation defined +division/remainder of `INT_SMIN` with `-1`) produces an implementation defined value. An alternative variant with different-rank broadcasting support exists for these -- GitLab From 8da29925c65dc72b49f693942923519b38dd0242 Mon Sep 17 00:00:00 2001 From: Yves-Noel Weweler Date: Fri, 21 Dec 2018 15:58:22 +0100 Subject: [PATCH 083/622] Fix pylint warnings from Ubuntu Sanity test --- .../kernel_tests/bucket_by_sequence_length_test.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py b/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py index a95d8e1049..4f1ea9f2ee 100644 --- a/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py +++ b/tensorflow/python/data/experimental/kernel_tests/bucket_by_sequence_length_test.py @@ -74,12 +74,13 @@ def _get_record_shape(sparse): @test_util.run_all_in_graph_and_eager_modes -class BucketBySequenceLengthTest(test_base.DatasetTestBase, parameterized.TestCase): +class BucketBySequenceLengthTest(test_base.DatasetTestBase, + parameterized.TestCase): # TODO(b/117581999): add eager coverage. @parameterized.named_parameters( - ("WithoutPadding", True), - ("WithPadding", False), + ("WithoutPadding", True), + ("WithPadding", False), ) def testSkipEagerBucketDropReminder(self, param_no_padding): -- GitLab From ed99e8fdd6e714e58216e89ae039a6bde8b122df Mon Sep 17 00:00:00 2001 From: Arpit Shah Date: Fri, 21 Dec 2018 10:37:20 -0600 Subject: [PATCH 084/622] Fixed gdb cmd files after formatting changes --- .../micro_speech/apollo3/preprocessor_1k_cmsis_test.cmd | 8 ++++---- .../micro_speech/apollo3/pushbutton_cmsis_scores.cmd | 2 +- .../micro_speech/apollo3/pushbutton_cmsis_voice.cmd | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k_cmsis_test.cmd b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k_cmsis_test.cmd index 5cc7a82c05..6988057f37 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k_cmsis_test.cmd +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/preprocessor_1k_cmsis_test.cmd @@ -10,22 +10,22 @@ file ../../../tools/make/gen/apollo3evb_cortex-m4/bin/preprocessor_1k_cmsis_test target remote localhost:2331 load ../../../tools/make/gen/apollo3evb_cortex-m4/bin/preprocessor_1k_cmsis_test monitor reset -break preprocessor.cc:70 +break preprocessor.cc:68 commands dump verilog value cmsis_windowed_input.txt bufB c end -break preprocessor.cc:77 +break preprocessor.cc:76 commands dump verilog value cmsis_dft.txt bufA c end -break preprocessor.cc:82 +break preprocessor.cc:81 commands dump verilog value cmsis_power.txt bufB c end -break preprocessor.cc:84 +break preprocessor.cc:83 commands dump verilog memory cmsis_power_avg.txt output output+42 c diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_cmsis_scores.cmd b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_cmsis_scores.cmd index e14ea2d83c..ace278ff9a 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_cmsis_scores.cmd +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_cmsis_scores.cmd @@ -9,7 +9,7 @@ file ../../../tools/make/gen/apollo3evb_cortex-m4/bin/pushbutton_cmsis_speech_te target remote localhost:2331 load ../../../tools/make/gen/apollo3evb_cortex-m4/bin/pushbutton_cmsis_speech_test monitor reset -break pushbutton_main.c:322 +break pushbutton_main.c:307 commands printf "Silence score: %d\n", g_silence_score printf "Unknown score: %d\n", g_unknown_score diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_cmsis_voice.cmd b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_cmsis_voice.cmd index b49f9d2d92..5dea48e62a 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_cmsis_voice.cmd +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/apollo3/pushbutton_cmsis_voice.cmd @@ -17,7 +17,7 @@ file ../../../tools/make/gen/apollo3evb_cortex-m4/bin/pushbutton_cmsis_speech_te target remote localhost:2331 load ../../../tools/make/gen/apollo3evb_cortex-m4/bin/pushbutton_cmsis_speech_test monitor reset -break pushbutton_main.c:313 +break pushbutton_main.c:296 commands dump verilog value captured_data.txt captured_data c -- GitLab From 7859702e8a7093f242c167ea027cc227f1f9d048 Mon Sep 17 00:00:00 2001 From: Trevor Morris Date: Fri, 21 Dec 2018 08:57:29 -0800 Subject: [PATCH 085/622] Fix failing test in TRT4.0 - TRT bug with tensors rank < 3 in INT8 mode --- tensorflow/contrib/tensorrt/test/identity_output_test.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tensorflow/contrib/tensorrt/test/identity_output_test.py b/tensorflow/contrib/tensorrt/test/identity_output_test.py index 2be9da9ede..da7d70876d 100644 --- a/tensorflow/contrib/tensorrt/test/identity_output_test.py +++ b/tensorflow/contrib/tensorrt/test/identity_output_test.py @@ -71,6 +71,11 @@ class IdentityTest(trt_test.TfTrtIntegrationTestBase): """Return the expected engines to build.""" return ["TRTEngineOp_0"] + def ShouldRunTest(self, run_params): + """Whether to run the test.""" + # TODO(aaroey): Trt 4.0 forbids conversion for tensors with rank <3 in int8 + # mode, which is a bug. Re-enable this when trt library is fixed. + return not trt_test.IsQuantizationMode(run_params.precision_mode) if __name__ == "__main__": test.main() -- GitLab From 753a3a3d7fa79e390ac8c068dc52acd03e0e4187 Mon Sep 17 00:00:00 2001 From: Trevor Morris Date: Fri, 21 Dec 2018 09:07:40 -0800 Subject: [PATCH 086/622] Report size and size limit with error message --- tensorflow/core/grappler/optimizers/constant_folding.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tensorflow/core/grappler/optimizers/constant_folding.cc b/tensorflow/core/grappler/optimizers/constant_folding.cc index 89be08800d..8804dedad1 100644 --- a/tensorflow/core/grappler/optimizers/constant_folding.cc +++ b/tensorflow/core/grappler/optimizers/constant_folding.cc @@ -983,7 +983,8 @@ Status ConstantFolding::CreateNodeDef(const string& name, if (encoded_size > original_size && encoded_size >= 10 * 1024 * 1024) { return errors::InvalidArgument( - strings::StrCat("Can't fold ", name, ", its size would be too large")); + strings::StrCat("Can't fold ", name, ", its size would be too large (", + encoded_size, " >= ", 10 * 1024 * 1024, " bytes)")); } return Status::OK(); } -- GitLab From ab72f61de52f59887d262b23543cc5d5508e753e Mon Sep 17 00:00:00 2001 From: Trevor Morris Date: Fri, 21 Dec 2018 14:18:09 -0800 Subject: [PATCH 087/622] Fix clang-format --- tensorflow/core/grappler/optimizers/constant_folding.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tensorflow/core/grappler/optimizers/constant_folding.cc b/tensorflow/core/grappler/optimizers/constant_folding.cc index 8804dedad1..adaf3cd07a 100644 --- a/tensorflow/core/grappler/optimizers/constant_folding.cc +++ b/tensorflow/core/grappler/optimizers/constant_folding.cc @@ -901,8 +901,7 @@ DataType GetDataTypeFromNodeOrProps(const NodeDef& node, // static Status ConstantFolding::CreateNodeDef(const string& name, - const TensorValue& tensor, - NodeDef* node, + const TensorValue& tensor, NodeDef* node, size_t original_size) { node->set_name(name); node->set_op("Const"); -- GitLab From 6bd27704c472b8d1cbd64b600fe00f223e74b4f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=8C=AF=E5=8D=8E=20=28WANG=20Zhenhua=29?= Date: Sat, 22 Dec 2018 10:35:47 +0800 Subject: [PATCH 088/622] lite: remove memset in resize bilinear opt op memset() is not necessarily needed here since every element in output_data memory will be updated. --- tensorflow/lite/kernels/internal/optimized/optimized_ops.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/tensorflow/lite/kernels/internal/optimized/optimized_ops.h b/tensorflow/lite/kernels/internal/optimized/optimized_ops.h index 7bc6b324c5..a5374827e1 100644 --- a/tensorflow/lite/kernels/internal/optimized/optimized_ops.h +++ b/tensorflow/lite/kernels/internal/optimized/optimized_ops.h @@ -5387,9 +5387,6 @@ inline void ResizeBilinearGenericSmallChannel( int32 output_height, int32 output_width, float height_scale, float width_scale, const RuntimeShape& input_shape, const T* input_data, const RuntimeShape& output_shape, T* output_data) { - memset(output_data, 0, - batches * output_height * output_width * depth * sizeof(T)); - T* output_ptr = &output_data[0]; for (int b = 0; b < batches; ++b) { for (int y = 0; y < output_height; ++y) { -- GitLab From 315e009f917aebe04a754a046a57cb3ac1d10557 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=8C=AF=E5=8D=8E=20=28WANG=20Zhenhua=29?= Date: Sat, 22 Dec 2018 10:36:50 +0800 Subject: [PATCH 089/622] lite: perform std::floor before cast float to int This won't change the resulted value, but to have same code style in the context and reference_ops. --- tensorflow/lite/kernels/internal/optimized/optimized_ops.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/lite/kernels/internal/optimized/optimized_ops.h b/tensorflow/lite/kernels/internal/optimized/optimized_ops.h index a5374827e1..8f09fab4ca 100644 --- a/tensorflow/lite/kernels/internal/optimized/optimized_ops.h +++ b/tensorflow/lite/kernels/internal/optimized/optimized_ops.h @@ -5395,7 +5395,7 @@ inline void ResizeBilinearGenericSmallChannel( int32 y1 = std::min(y0 + 1, input_height - 1); for (int x = 0; x < output_width; ++x) { float input_x = x * width_scale; - int32 x0 = static_cast(input_x); + int32 x0 = static_cast(std::floor((input_x))); int32 x1 = std::min(x0 + 1, input_width - 1); int32 input_offset[4] = {Offset(input_shape, b, y0, x0, 0), -- GitLab From ab65c34bc6348828d7c682fdc4ad93bb1463eb2f Mon Sep 17 00:00:00 2001 From: Jakub Lipinski Date: Sat, 22 Dec 2018 17:07:35 +0100 Subject: [PATCH 090/622] Fixed as per the review --- .../lite/examples/ios/camera/CameraExampleViewController.mm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tensorflow/lite/examples/ios/camera/CameraExampleViewController.mm b/tensorflow/lite/examples/ios/camera/CameraExampleViewController.mm index 8b192fb5a4..b91b44500a 100644 --- a/tensorflow/lite/examples/ios/camera/CameraExampleViewController.mm +++ b/tensorflow/lite/examples/ios/camera/CameraExampleViewController.mm @@ -330,8 +330,10 @@ void ProcessInputWithQuantizedModel( const int output_tensor_index = interpreter->outputs()[0]; TfLiteTensor* output_tensor = interpreter->tensor(output_tensor_index); TfLiteIntArray* output_dims = output_tensor->dims; - assert(output_dims->size == 2); - const int output_size = output_dims->data[1]-output_dims->data[0]; + if(output_dims->size != 2 || output_dims->data[0] != 1) { + LOG(FATAL) << "Output of the model is in invalid format."; + } + const int output_size = output_dims->data[1]; const int kNumResults = 5; const float kThreshold = 0.1f; -- GitLab From c324dd333c93751facda0cd42bce3a54714662ee Mon Sep 17 00:00:00 2001 From: Vishwak Srinivasan Date: Mon, 24 Dec 2018 21:23:33 +0530 Subject: [PATCH 091/622] Close code environment in docstring for adjoint --- tensorflow/python/ops/linalg/linalg_impl.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tensorflow/python/ops/linalg/linalg_impl.py b/tensorflow/python/ops/linalg/linalg_impl.py index df2bd887cd..2259eaa65c 100644 --- a/tensorflow/python/ops/linalg/linalg_impl.py +++ b/tensorflow/python/ops/linalg/linalg_impl.py @@ -104,6 +104,7 @@ def adjoint(matrix, name=None): tf.linalg.adjoint(x) # [[1 - 1j, 4 - 4j], # [2 - 2j, 5 - 5j], # [3 - 3j, 6 - 6j]] + ``` Args: matrix: A `Tensor`. Must be `float16`, `float32`, `float64`, `complex64`, -- GitLab From e3ae0538377cda88f5411c89e39bf48a33aa1fa7 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Mon, 24 Dec 2018 22:36:17 +0000 Subject: [PATCH 092/622] Fix ValueError in tf.nn.nce_loss when weights and biases are float64 This fix tries to fix the issue raised in 24547 where ValueError was thrown when weights and biases are float64 This fix fixes 24547 Signed-off-by: Yong Tang --- tensorflow/python/ops/nn_impl.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tensorflow/python/ops/nn_impl.py b/tensorflow/python/ops/nn_impl.py index 841bac8bea..bc55b4e7f4 100644 --- a/tensorflow/python/ops/nn_impl.py +++ b/tensorflow/python/ops/nn_impl.py @@ -1380,6 +1380,7 @@ def _compute_sampled_logits(weights, # weights shape is [num_classes, dim] all_w = embedding_ops.embedding_lookup( weights, all_ids, partition_strategy=partition_strategy) + all_w = all_w if all_w.dtype == inputs.dtype else math_ops.cast(all_w, inputs.dtype) # true_w shape is [batch_size * num_true, dim] true_w = array_ops.slice(all_w, [0, 0], @@ -1397,6 +1398,7 @@ def _compute_sampled_logits(weights, # add the biases to the true and sampled logits. all_b = embedding_ops.embedding_lookup( biases, all_ids, partition_strategy=partition_strategy) + all_b = all_b if all_b.dtype == inputs.dtype else math_ops.cast(all_b, inputs.dtype) # true_b is a [batch_size * num_true] tensor # sampled_b is a [num_sampled] float tensor true_b = array_ops.slice(all_b, [0], array_ops.shape(labels_flat)) -- GitLab From 9ff969843f22e136694905a1d95892c5792ddde0 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Mon, 24 Dec 2018 22:44:05 +0000 Subject: [PATCH 093/622] Fix Line too long (88/80) issue Signed-off-by: Yong Tang --- tensorflow/python/ops/nn_impl.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tensorflow/python/ops/nn_impl.py b/tensorflow/python/ops/nn_impl.py index bc55b4e7f4..ec5ea44526 100644 --- a/tensorflow/python/ops/nn_impl.py +++ b/tensorflow/python/ops/nn_impl.py @@ -1380,7 +1380,8 @@ def _compute_sampled_logits(weights, # weights shape is [num_classes, dim] all_w = embedding_ops.embedding_lookup( weights, all_ids, partition_strategy=partition_strategy) - all_w = all_w if all_w.dtype == inputs.dtype else math_ops.cast(all_w, inputs.dtype) + if all_w.dtype != inputs.dtype: + all_w = math_ops.cast(all_w, inputs.dtype) # true_w shape is [batch_size * num_true, dim] true_w = array_ops.slice(all_w, [0, 0], @@ -1398,7 +1399,8 @@ def _compute_sampled_logits(weights, # add the biases to the true and sampled logits. all_b = embedding_ops.embedding_lookup( biases, all_ids, partition_strategy=partition_strategy) - all_b = all_b if all_b.dtype == inputs.dtype else math_ops.cast(all_b, inputs.dtype) + if all_b.dtype != inputs.dtype: + all_b = math_ops.cast(all_b, inputs.dtype) # true_b is a [batch_size * num_true] tensor # sampled_b is a [num_sampled] float tensor true_b = array_ops.slice(all_b, [0], array_ops.shape(labels_flat)) -- GitLab From daea84523e830396b1238ee554be618ee40f1082 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Wed, 26 Dec 2018 07:09:49 +0000 Subject: [PATCH 094/622] Fix the issue when PYTHON_LIB_PATH consists of multiple paths This fix tries to address the issue raise in 24567 where PYTHON_LIB_PATH does not support specifying multiple paths for bazel. This fix update python_configure.bzl so that it is possible to process multiple paths for PYTHON_LIB_PATH. This fix fixes 24567. Signed-off-by: Yong Tang --- third_party/py/python_configure.bzl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/third_party/py/python_configure.bzl b/third_party/py/python_configure.bzl index 53264630a1..fee7d27509 100644 --- a/third_party/py/python_configure.bzl +++ b/third_party/py/python_configure.bzl @@ -207,10 +207,13 @@ def _get_python_lib(repository_ctx, python_bin): def _check_python_lib(repository_ctx, python_lib): """Checks the python lib path.""" - cmd = 'test -d "%s" -a -x "%s"' % (python_lib, python_lib) - result = repository_ctx.execute([_get_bash_bin(repository_ctx), "-c", cmd]) - if result.return_code == 1: - _fail("Invalid python library path: %s" % python_lib) + return_codes = [] + for entry in python_lib.split(":"): + cmd = 'test -d "%s" -a -x "%s"' % (entry, entry) + result = repository_ctx.execute([_get_bash_bin(repository_ctx), "-c", cmd]) + return_codes.append(result.return_code) + if all(return_codes): + _fail("Invalid python library path: %s" % python_lib) def _check_python_bin(repository_ctx, python_bin): -- GitLab From 5ee938d44ae90c76acfcb0a3be8bd197ed6df81b Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Wed, 26 Dec 2018 07:18:49 +0000 Subject: [PATCH 095/622] Fix indentation to be consistent Signed-off-by: Yong Tang --- third_party/py/python_configure.bzl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/third_party/py/python_configure.bzl b/third_party/py/python_configure.bzl index fee7d27509..57d216cfe1 100644 --- a/third_party/py/python_configure.bzl +++ b/third_party/py/python_configure.bzl @@ -209,11 +209,11 @@ def _check_python_lib(repository_ctx, python_lib): """Checks the python lib path.""" return_codes = [] for entry in python_lib.split(":"): - cmd = 'test -d "%s" -a -x "%s"' % (entry, entry) - result = repository_ctx.execute([_get_bash_bin(repository_ctx), "-c", cmd]) - return_codes.append(result.return_code) + cmd = 'test -d "%s" -a -x "%s"' % (entry, entry) + result = repository_ctx.execute([_get_bash_bin(repository_ctx), "-c", cmd]) + return_codes.append(result.return_code) if all(return_codes): - _fail("Invalid python library path: %s" % python_lib) + _fail("Invalid python library path: %s" % python_lib) def _check_python_bin(repository_ctx, python_bin): -- GitLab From 2696015d8539ea68f27a519ac908ed538f8085ae Mon Sep 17 00:00:00 2001 From: Igor Ganichev Date: Wed, 26 Dec 2018 09:05:31 -0800 Subject: [PATCH 096/622] Colocate slot variables with their base variable in keras OptimizerV2 Not doing so results in errors because ops such as ResourceApplyRMSProp take base as well as slot variables. Such ops cannot be placed when the variable devices are different. PiperOrigin-RevId: 226924297 --- .../contrib/optimizer_v2/rmsprop_test.py | 53 +++++++++++++++++++ tensorflow/python/keras/optimizer_v2/BUILD | 1 + .../python/keras/optimizer_v2/optimizer_v2.py | 11 ++-- .../python/keras/optimizer_v2/rmsprop_test.py | 48 +++++++++++++++++ 4 files changed, 108 insertions(+), 5 deletions(-) diff --git a/tensorflow/contrib/optimizer_v2/rmsprop_test.py b/tensorflow/contrib/optimizer_v2/rmsprop_test.py index 202c1e9afc..ab47b74c65 100644 --- a/tensorflow/contrib/optimizer_v2/rmsprop_test.py +++ b/tensorflow/contrib/optimizer_v2/rmsprop_test.py @@ -25,10 +25,12 @@ from absl.testing import parameterized import numpy as np from tensorflow.contrib.optimizer_v2 import rmsprop +from tensorflow.python.eager import context from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import test_util +from tensorflow.python.ops import array_ops from tensorflow.python.ops import embedding_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import resource_variable_ops @@ -448,5 +450,56 @@ class RMSPropOptimizerTest(test.TestCase, parameterized.TestCase): ]), var1.eval()) +class SlotColocationTest(test.TestCase, parameterized.TestCase): + + @parameterized.parameters([True, False]) + @test_util.run_in_graph_and_eager_modes + def testRunMinimizeOnGPUForCPUVariables(self, use_resource): + if not context.context().num_gpus(): + self.skipTest("No GPUs found") + + with ops.device("/device:CPU:0"): + if use_resource: + var0 = resource_variable_ops.ResourceVariable([1.0, 2.0], + dtype=dtypes.float32) + var1 = resource_variable_ops.ResourceVariable([3.0, 4.0], + dtype=dtypes.float32) + global_step = resource_variable_ops.ResourceVariable( + array_ops.zeros([], dtypes.int64), name="global_step") + else: + var0 = variables.Variable([1.0, 2.0], dtype=dtypes.float32) + var1 = variables.Variable([3.0, 4.0], dtype=dtypes.float32) + global_step = variables.Variable( + array_ops.zeros([], dtypes.int64), name="global_step") + + def loss(): + return 5 * var0 + 3 * var1 + + opt = rmsprop.RMSPropOptimizer( + learning_rate=1.0, decay=0.9, momentum=0.5, epsilon=1.0) + + # Fetch params to validate initial values + self.evaluate(variables.global_variables_initializer()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) + + # Run 1 step through optimizer on GPU. + # Slot variables are created the first time optimizer is used on some + # variable. This tests that slot variables will be colocated with the base + # variable. + with ops.device("/device:GPU:0"): + # Note that for eager execution, minimize expects a function instead of a + # Tensor. + opt_op = opt.minimize(loss, global_step, [var0, var1]) + self.evaluate(variables.global_variables_initializer()) + self.evaluate(opt_op) + + # Validate updated params, All variables should have decreased. + self.assertTrue(all(v < 0.0 for v in self.evaluate(var0)), + msg="updated variables: %s" % self.evaluate(var0)) + self.assertTrue(all(v < 2.0 for v in self.evaluate(var1)), + msg="updated variables: %s" % self.evaluate(var1)) + + if __name__ == "__main__": test.main() diff --git a/tensorflow/python/keras/optimizer_v2/BUILD b/tensorflow/python/keras/optimizer_v2/BUILD index b8f0124941..7fa755147a 100644 --- a/tensorflow/python/keras/optimizer_v2/BUILD +++ b/tensorflow/python/keras/optimizer_v2/BUILD @@ -201,6 +201,7 @@ cuda_py_test( srcs = ["rmsprop_test.py"], additional_deps = [ ":optimizer_v2", + "@absl_py//absl/testing:parameterized", "//tensorflow/python/eager:def_function", "//tensorflow/python:client_testlib", "//tensorflow/python:framework", diff --git a/tensorflow/python/keras/optimizer_v2/optimizer_v2.py b/tensorflow/python/keras/optimizer_v2/optimizer_v2.py index d0f16f0b4f..7b784fd9af 100644 --- a/tensorflow/python/keras/optimizer_v2/optimizer_v2.py +++ b/tensorflow/python/keras/optimizer_v2/optimizer_v2.py @@ -443,11 +443,12 @@ class OptimizerV2(checkpointable.CheckpointableBase): initializer, shape=var.shape, dtype=var.dtype) else: initial_value = initializer - weight = tf_variables.Variable( - name="%s/%s" % (var._shared_name, slot_name), # pylint: disable=protected-access - dtype=var.dtype, - trainable=False, - initial_value=initial_value) + with ops._colocate_with_for_gradient(var, None): # pylint: disable=protected-access + weight = tf_variables.Variable( + name="%s/%s" % (var._shared_name, slot_name), # pylint: disable=protected-access + dtype=var.dtype, + trainable=False, + initial_value=initial_value) backend.track_variable(weight) slot_dict[slot_name] = weight self._restore_slot_variable( diff --git a/tensorflow/python/keras/optimizer_v2/rmsprop_test.py b/tensorflow/python/keras/optimizer_v2/rmsprop_test.py index 4d61cfbbc5..1b53f7e579 100644 --- a/tensorflow/python/keras/optimizer_v2/rmsprop_test.py +++ b/tensorflow/python/keras/optimizer_v2/rmsprop_test.py @@ -22,6 +22,7 @@ import copy import itertools import math +from absl.testing import parameterized import numpy as np from tensorflow.python.eager import context @@ -421,5 +422,52 @@ class RMSpropOptimizerTest(test.TestCase): self.assertEqual(opt_3.lr, 0.1) +class SlotColocationTest(test.TestCase, parameterized.TestCase): + + @parameterized.parameters([True, False]) + @test_util.run_in_graph_and_eager_modes + def testRunMinimizeOnGPUForCPUVariables(self, use_resource): + if not context.context().num_gpus(): + self.skipTest("No GPUs found") + + with ops.device("/device:CPU:0"): + if use_resource: + var0 = resource_variable_ops.ResourceVariable([1.0, 2.0], + dtype=dtypes.float32) + var1 = resource_variable_ops.ResourceVariable([3.0, 4.0], + dtype=dtypes.float32) + else: + var0 = variables.Variable([1.0, 2.0], dtype=dtypes.float32) + var1 = variables.Variable([3.0, 4.0], dtype=dtypes.float32) + + def loss(): + return 5 * var0 + 3 * var1 + + opt = rmsprop.RMSprop( + learning_rate=1.0, decay=0.9, momentum=0.5, epsilon=1.0) + + # Fetch params to validate initial values + self.evaluate(variables.global_variables_initializer()) + self.assertAllClose([1.0, 2.0], self.evaluate(var0)) + self.assertAllClose([3.0, 4.0], self.evaluate(var1)) + + # Run 1 step through optimizer on GPU. + # Slot variables are created the first time optimizer is used on some + # variable. This tests that slot variables will be colocated with the base + # variable. + with ops.device("/device:GPU:0"): + # Note that for eager execution, minimize expects a function instead of a + # Tensor. + opt_op = opt.minimize(loss, [var0, var1]) + self.evaluate(variables.global_variables_initializer()) + self.evaluate(opt_op) + + # Validate updated params, All variables should have decreased. + self.assertTrue(all(v < 0.0 for v in self.evaluate(var0)), + msg="updated variables: %s" % self.evaluate(var0)) + self.assertTrue(all(v < 2.0 for v in self.evaluate(var1)), + msg="updated variables: %s" % self.evaluate(var1)) + + if __name__ == "__main__": test.main() -- GitLab From daf0985028a8b0133e653e6fb9ee72a4bc1c2ed2 Mon Sep 17 00:00:00 2001 From: Karim Nosir Date: Wed, 26 Dec 2018 10:14:41 -0800 Subject: [PATCH 097/622] Add Unique op to the schema. PiperOrigin-RevId: 226930132 --- tensorflow/lite/builtin_ops.h | 1 + tensorflow/lite/c/builtin_op_data.h | 4 + .../lite/core/api/flatbuffer_conversions.cc | 14 ++ .../writer/option_writer_generator.cc | 2 + tensorflow/lite/nnapi_delegate.cc | 1 + tensorflow/lite/schema/schema.fbs | 6 + tensorflow/lite/schema/schema_generated.h | 141 +++++++++++++++++- 7 files changed, 163 insertions(+), 6 deletions(-) diff --git a/tensorflow/lite/builtin_ops.h b/tensorflow/lite/builtin_ops.h index f97d3ac4bf..ce73aa0f9c 100644 --- a/tensorflow/lite/builtin_ops.h +++ b/tensorflow/lite/builtin_ops.h @@ -128,6 +128,7 @@ typedef enum { kTfLiteBuiltinMirrorPad = 100, kTfLiteBuiltinAbs = 101, kTfLiteBuiltinSplitV = 102, + kTfLiteBuiltinUnique = 103, } TfLiteBuiltinOperator; #ifdef __cplusplus diff --git a/tensorflow/lite/c/builtin_op_data.h b/tensorflow/lite/c/builtin_op_data.h index 58e7221bc6..46ac656ecf 100644 --- a/tensorflow/lite/c/builtin_op_data.h +++ b/tensorflow/lite/c/builtin_op_data.h @@ -351,6 +351,10 @@ typedef struct { float alpha; } TfLiteLeakyReluParams; +typedef struct { + TfLiteType index_out_type; +} TfLiteUniqueParams; + #ifdef __cplusplus } // extern "C" #endif // __cplusplus diff --git a/tensorflow/lite/core/api/flatbuffer_conversions.cc b/tensorflow/lite/core/api/flatbuffer_conversions.cc index e73c4ce023..970e45bbdc 100644 --- a/tensorflow/lite/core/api/flatbuffer_conversions.cc +++ b/tensorflow/lite/core/api/flatbuffer_conversions.cc @@ -18,6 +18,8 @@ limitations under the License. #include #include "tensorflow/lite/c/builtin_op_data.h" +#include "tensorflow/lite/c/c_api_internal.h" +#include "tensorflow/lite/schema/schema_generated.h" namespace tflite { @@ -651,6 +653,18 @@ TfLiteStatus ParseOpData(const Operator* op, BuiltinOperator op_type, *builtin_data = reinterpret_cast(params); break; } + case BuiltinOperator_UNIQUE: { + TfLiteUniqueParams* params = allocator->AllocatePOD(); + auto* unique_params = op->builtin_options_as_UniqueOptions(); + if (unique_params != nullptr) { + params->index_out_type = + unique_params->idx_out_type() == tflite::TensorType_INT64 + ? TfLiteType::kTfLiteInt64 + : TfLiteType::kTfLiteInt32; + } + *builtin_data = reinterpret_cast(params); + break; + } // Below are the ops with no builtin_data strcture. case BuiltinOperator_ABS: diff --git a/tensorflow/lite/experimental/writer/option_writer_generator.cc b/tensorflow/lite/experimental/writer/option_writer_generator.cc index fa360a2f47..7374249476 100644 --- a/tensorflow/lite/experimental/writer/option_writer_generator.cc +++ b/tensorflow/lite/experimental/writer/option_writer_generator.cc @@ -69,6 +69,7 @@ static const char* param_structs[] = {"TfLiteConvParams", "TfLiteOneHotParams", "TfLiteLeakyReluParams", "TfLiteMirrorPaddingParams", + "TfLiteUniqueParams", nullptr}; } // namespace @@ -156,6 +157,7 @@ class OpOptionData { op_to_option_["UNIDIRECTIONAL_SEQUENCE_RNN"] = "SequenceRNNOptions"; op_to_option_["UNIDIRECTIONAL_SEQUENCE_RNN"] = "SequenceRNNOptions"; op_to_option_["MIRROR_PAD"] = ""; // TODO(karimnosseir): MirrorPadOptions. + op_to_option_["UNIQUE"] = ""; // TODO(karimnosseir): UniqueOptions. // Manually specified mappings between ops and options (none) op_to_option_["EMBEDDING_LOOKUP"] = ""; // TODO(aselle): maybe something else. diff --git a/tensorflow/lite/nnapi_delegate.cc b/tensorflow/lite/nnapi_delegate.cc index 26d75696a1..dc8e81cde7 100644 --- a/tensorflow/lite/nnapi_delegate.cc +++ b/tensorflow/lite/nnapi_delegate.cc @@ -686,6 +686,7 @@ TfLiteStatus AddOpsAndParams( case tflite::BuiltinOperator_MIRROR_PAD: case tflite::BuiltinOperator_ABS: case tflite::BuiltinOperator_SPLIT_V: + case tflite::BuiltinOperator_UNIQUE: logError("Op code %d is currently not delegated to NNAPI", builtin); return kTfLiteError; break; diff --git a/tensorflow/lite/schema/schema.fbs b/tensorflow/lite/schema/schema.fbs index 91d8049301..aca926a3b9 100644 --- a/tensorflow/lite/schema/schema.fbs +++ b/tensorflow/lite/schema/schema.fbs @@ -205,6 +205,7 @@ enum BuiltinOperator : byte { MIRROR_PAD = 100, ABS = 101, SPLIT_V = 102, + UNIQUE = 103, } // Options for the builtin operators. @@ -288,6 +289,7 @@ union BuiltinOptions { MirrorPadOptions, AbsOptions, SplitVOptions, + UniqueOptions, } enum Padding : byte { SAME, VALID } @@ -701,6 +703,10 @@ table MirrorPadOptions { mode:MirrorPadMode; } +table UniqueOptions { + idx_out_type:TensorType = INT32; +} + // An OperatorCode can be an enum value (BuiltinOperator) if the operator is a // builtin, or a string if the operator is custom. table OperatorCode { diff --git a/tensorflow/lite/schema/schema_generated.h b/tensorflow/lite/schema/schema_generated.h index 0883cce497..1d153cc717 100755 --- a/tensorflow/lite/schema/schema_generated.h +++ b/tensorflow/lite/schema/schema_generated.h @@ -268,6 +268,9 @@ struct SquaredDifferenceOptionsT; struct MirrorPadOptions; struct MirrorPadOptionsT; +struct UniqueOptions; +struct UniqueOptionsT; + struct OperatorCode; struct OperatorCodeT; @@ -520,11 +523,12 @@ enum BuiltinOperator { BuiltinOperator_MIRROR_PAD = 100, BuiltinOperator_ABS = 101, BuiltinOperator_SPLIT_V = 102, + BuiltinOperator_UNIQUE = 103, BuiltinOperator_MIN = BuiltinOperator_ADD, - BuiltinOperator_MAX = BuiltinOperator_SPLIT_V + BuiltinOperator_MAX = BuiltinOperator_UNIQUE }; -inline const BuiltinOperator (&EnumValuesBuiltinOperator())[102] { +inline const BuiltinOperator (&EnumValuesBuiltinOperator())[103] { static const BuiltinOperator values[] = { BuiltinOperator_ADD, BuiltinOperator_AVERAGE_POOL_2D, @@ -627,7 +631,8 @@ inline const BuiltinOperator (&EnumValuesBuiltinOperator())[102] { BuiltinOperator_SQUARED_DIFFERENCE, BuiltinOperator_MIRROR_PAD, BuiltinOperator_ABS, - BuiltinOperator_SPLIT_V + BuiltinOperator_SPLIT_V, + BuiltinOperator_UNIQUE }; return values; } @@ -737,6 +742,7 @@ inline const char * const *EnumNamesBuiltinOperator() { "MIRROR_PAD", "ABS", "SPLIT_V", + "UNIQUE", nullptr }; return names; @@ -828,11 +834,12 @@ enum BuiltinOptions { BuiltinOptions_MirrorPadOptions = 77, BuiltinOptions_AbsOptions = 78, BuiltinOptions_SplitVOptions = 79, + BuiltinOptions_UniqueOptions = 80, BuiltinOptions_MIN = BuiltinOptions_NONE, - BuiltinOptions_MAX = BuiltinOptions_SplitVOptions + BuiltinOptions_MAX = BuiltinOptions_UniqueOptions }; -inline const BuiltinOptions (&EnumValuesBuiltinOptions())[80] { +inline const BuiltinOptions (&EnumValuesBuiltinOptions())[81] { static const BuiltinOptions values[] = { BuiltinOptions_NONE, BuiltinOptions_Conv2DOptions, @@ -913,7 +920,8 @@ inline const BuiltinOptions (&EnumValuesBuiltinOptions())[80] { BuiltinOptions_SquaredDifferenceOptions, BuiltinOptions_MirrorPadOptions, BuiltinOptions_AbsOptions, - BuiltinOptions_SplitVOptions + BuiltinOptions_SplitVOptions, + BuiltinOptions_UniqueOptions }; return values; } @@ -1000,6 +1008,7 @@ inline const char * const *EnumNamesBuiltinOptions() { "MirrorPadOptions", "AbsOptions", "SplitVOptions", + "UniqueOptions", nullptr }; return names; @@ -1330,6 +1339,10 @@ template<> struct BuiltinOptionsTraits { static const BuiltinOptions enum_value = BuiltinOptions_SplitVOptions; }; +template<> struct BuiltinOptionsTraits { + static const BuiltinOptions enum_value = BuiltinOptions_UniqueOptions; +}; + struct BuiltinOptionsUnion { BuiltinOptions type; void *value; @@ -1993,6 +2006,14 @@ struct BuiltinOptionsUnion { return type == BuiltinOptions_SplitVOptions ? reinterpret_cast(value) : nullptr; } + UniqueOptionsT *AsUniqueOptions() { + return type == BuiltinOptions_UniqueOptions ? + reinterpret_cast(value) : nullptr; + } + const UniqueOptionsT *AsUniqueOptions() const { + return type == BuiltinOptions_UniqueOptions ? + reinterpret_cast(value) : nullptr; + } }; bool VerifyBuiltinOptions(flatbuffers::Verifier &verifier, const void *obj, BuiltinOptions type); @@ -7021,6 +7042,60 @@ inline flatbuffers::Offset CreateMirrorPadOptions( flatbuffers::Offset CreateMirrorPadOptions(flatbuffers::FlatBufferBuilder &_fbb, const MirrorPadOptionsT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +struct UniqueOptionsT : public flatbuffers::NativeTable { + typedef UniqueOptions TableType; + TensorType idx_out_type; + UniqueOptionsT() + : idx_out_type(TensorType_INT32) { + } +}; + +struct UniqueOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef UniqueOptionsT NativeTableType; + enum { + VT_IDX_OUT_TYPE = 4 + }; + TensorType idx_out_type() const { + return static_cast(GetField(VT_IDX_OUT_TYPE, 2)); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_IDX_OUT_TYPE) && + verifier.EndTable(); + } + UniqueOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(UniqueOptionsT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder &_fbb, const UniqueOptionsT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct UniqueOptionsBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_idx_out_type(TensorType idx_out_type) { + fbb_.AddElement(UniqueOptions::VT_IDX_OUT_TYPE, static_cast(idx_out_type), 2); + } + explicit UniqueOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + UniqueOptionsBuilder &operator=(const UniqueOptionsBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateUniqueOptions( + flatbuffers::FlatBufferBuilder &_fbb, + TensorType idx_out_type = TensorType_INT32) { + UniqueOptionsBuilder builder_(_fbb); + builder_.add_idx_out_type(idx_out_type); + return builder_.Finish(); +} + +flatbuffers::Offset CreateUniqueOptions(flatbuffers::FlatBufferBuilder &_fbb, const UniqueOptionsT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); + struct OperatorCodeT : public flatbuffers::NativeTable { typedef OperatorCode TableType; BuiltinOperator builtin_code; @@ -7391,6 +7466,9 @@ struct Operator FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const SplitVOptions *builtin_options_as_SplitVOptions() const { return builtin_options_type() == BuiltinOptions_SplitVOptions ? static_cast(builtin_options()) : nullptr; } + const UniqueOptions *builtin_options_as_UniqueOptions() const { + return builtin_options_type() == BuiltinOptions_UniqueOptions ? static_cast(builtin_options()) : nullptr; + } const flatbuffers::Vector *custom_options() const { return GetPointer *>(VT_CUSTOM_OPTIONS); } @@ -7738,6 +7816,10 @@ template<> inline const SplitVOptions *Operator::builtin_options_as inline const UniqueOptions *Operator::builtin_options_as() const { + return builtin_options_as_UniqueOptions(); +} + struct OperatorBuilder { flatbuffers::FlatBufferBuilder &fbb_; flatbuffers::uoffset_t start_; @@ -10356,6 +10438,32 @@ inline flatbuffers::Offset CreateMirrorPadOptions(flatbuffers: _mode); } +inline UniqueOptionsT *UniqueOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const { + auto _o = new UniqueOptionsT(); + UnPackTo(_o, _resolver); + return _o; +} + +inline void UniqueOptions::UnPackTo(UniqueOptionsT *_o, const flatbuffers::resolver_function_t *_resolver) const { + (void)_o; + (void)_resolver; + { auto _e = idx_out_type(); _o->idx_out_type = _e; }; +} + +inline flatbuffers::Offset UniqueOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const UniqueOptionsT* _o, const flatbuffers::rehasher_function_t *_rehasher) { + return CreateUniqueOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset CreateUniqueOptions(flatbuffers::FlatBufferBuilder &_fbb, const UniqueOptionsT *_o, const flatbuffers::rehasher_function_t *_rehasher) { + (void)_rehasher; + (void)_o; + struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const UniqueOptionsT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; + auto _idx_out_type = _o->idx_out_type; + return tflite::CreateUniqueOptions( + _fbb, + _idx_out_type); +} + inline OperatorCodeT *OperatorCode::UnPack(const flatbuffers::resolver_function_t *_resolver) const { auto _o = new OperatorCodeT(); UnPackTo(_o, _resolver); @@ -10930,6 +11038,10 @@ inline bool VerifyBuiltinOptions(flatbuffers::Verifier &verifier, const void *ob auto ptr = reinterpret_cast(obj); return verifier.VerifyTable(ptr); } + case BuiltinOptions_UniqueOptions: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } default: return false; } } @@ -11264,6 +11376,10 @@ inline void *BuiltinOptionsUnion::UnPack(const void *obj, BuiltinOptions type, c auto ptr = reinterpret_cast(obj); return ptr->UnPack(resolver); } + case BuiltinOptions_UniqueOptions: { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } default: return nullptr; } } @@ -11586,6 +11702,10 @@ inline flatbuffers::Offset BuiltinOptionsUnion::Pack(flatbuffers::FlatBuff auto ptr = reinterpret_cast(value); return CreateSplitVOptions(_fbb, ptr, _rehasher).Union(); } + case BuiltinOptions_UniqueOptions: { + auto ptr = reinterpret_cast(value); + return CreateUniqueOptions(_fbb, ptr, _rehasher).Union(); + } default: return 0; } } @@ -11908,6 +12028,10 @@ inline BuiltinOptionsUnion::BuiltinOptionsUnion(const BuiltinOptionsUnion &u) FL value = new SplitVOptionsT(*reinterpret_cast(u.value)); break; } + case BuiltinOptions_UniqueOptions: { + value = new UniqueOptionsT(*reinterpret_cast(u.value)); + break; + } default: break; } @@ -12310,6 +12434,11 @@ inline void BuiltinOptionsUnion::Reset() { delete ptr; break; } + case BuiltinOptions_UniqueOptions: { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } default: break; } value = nullptr; -- GitLab From c1d57ad8e1415e875f64d1f0a81e7c3768109afc Mon Sep 17 00:00:00 2001 From: Davide Libenzi Date: Wed, 26 Dec 2018 10:16:26 -0800 Subject: [PATCH 098/622] Fix typo. PiperOrigin-RevId: 226930269 --- tensorflow/compiler/xla/shape.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/compiler/xla/shape.h b/tensorflow/compiler/xla/shape.h index fb5dc8fba4..dc4cdc31a7 100644 --- a/tensorflow/compiler/xla/shape.h +++ b/tensorflow/compiler/xla/shape.h @@ -138,7 +138,7 @@ class Shape { string ShortDebugString() const { return ToProto().ShortDebugString(); } string DebugString() const { return ToProto().DebugString(); } - public: + private: // The element type of this shape (tuple, array, etc). PrimitiveType element_type_ = PRIMITIVE_TYPE_INVALID; -- GitLab From 87af907e8c4a9622f1b2f2dfec7856208ec1b2c0 Mon Sep 17 00:00:00 2001 From: Andy Ly Date: Wed, 26 Dec 2018 10:30:24 -0800 Subject: [PATCH 099/622] [Grappler] Update optimized_graph instead of graph_ in ConstantPushDown and MulConvPushDown of ConstantFolding optimizer. PiperOrigin-RevId: 226931428 --- .../grappler/optimizers/constant_folding.cc | 24 +++++++++++-------- .../grappler/optimizers/constant_folding.h | 5 ++-- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/tensorflow/core/grappler/optimizers/constant_folding.cc b/tensorflow/core/grappler/optimizers/constant_folding.cc index 1f24d35284..5908547383 100644 --- a/tensorflow/core/grappler/optimizers/constant_folding.cc +++ b/tensorflow/core/grappler/optimizers/constant_folding.cc @@ -1697,12 +1697,12 @@ Status ConstantFolding::SimplifyNode(bool use_shape_info, NodeDef* node, return Status::OK(); } - if (ConstantPushDown(node)) { + if (ConstantPushDown(optimized_graph, node)) { graph_modified_ = true; return Status::OK(); } - if (MulConvPushDown(node, *properties)) { + if (MulConvPushDown(optimized_graph, node, *properties)) { graph_modified_ = true; return Status::OK(); } @@ -2612,7 +2612,8 @@ bool ConstantFolding::ReduceDivToReciprocalMul(GraphDef* optimized_graph, return false; } -bool ConstantFolding::ConstantPushDown(NodeDef* node) { +bool ConstantFolding::ConstantPushDown(GraphDef* optimized_graph, + NodeDef* node) { // Consider the transformation // // + + = parent @@ -2680,10 +2681,10 @@ bool ConstantFolding::ConstantPushDown(NodeDef* node) { // edge. We can replace such a control edge with a control edge from A // to C. CHECK(MaybeRemoveControlInput(op_child_node->name(), const_child_node, - graph_, node_map_.get())); + optimized_graph, node_map_.get())); string other_leaf_input = left_leaf_is_constant ? op_child_node->input(0) : op_child_node->input(1); - MaybeAddControlInput(other_leaf_input, const_child_node, graph_, + MaybeAddControlInput(other_leaf_input, const_child_node, optimized_graph, node_map_.get()); } @@ -2700,7 +2701,7 @@ bool ConstantFolding::ConstantPushDown(NodeDef* node) { return false; } -bool ConstantFolding::MulConvPushDown(NodeDef* node, +bool ConstantFolding::MulConvPushDown(GraphDef* optimized_graph, NodeDef* node, const GraphProperties& properties) { // Push down multiplication on ConvND. // * ConvND @@ -2792,12 +2793,13 @@ bool ConstantFolding::MulConvPushDown(NodeDef* node, } // Make sure we don't introduce loops in the graph by removing control // dependencies from the conv2d node to c2. - NodeDef* conv_const_node = - conv_left_is_constant ? conv_left_child : conv_right_child; - if (MaybeRemoveControlInput(conv_node->name(), const_node, graph_, + string conv_const_input = + conv_left_is_constant ? conv_node->input(0) : conv_node->input(1); + if (MaybeRemoveControlInput(conv_node->name(), const_node, optimized_graph, node_map_.get())) { // Add a control dep from c1 to c2 to ensure c2 is in the right frame - *const_node->add_input() = AsControlDependency(*conv_const_node); + MaybeAddControlInput(conv_const_input, const_node, optimized_graph, + node_map_.get()); } conv_node->set_name(node->name()); @@ -2809,6 +2811,8 @@ bool ConstantFolding::MulConvPushDown(NodeDef* node, node_map_->UpdateInput(conv_node->name(), node->input(1), mul_new_name); conv_node->set_input(1, mul_new_name); } + NodeDef* conv_const_node = + conv_left_is_constant ? conv_left_child : conv_right_child; if (left_child_is_constant) { node->set_input(1, conv_const_node->name()); } else { diff --git a/tensorflow/core/grappler/optimizers/constant_folding.h b/tensorflow/core/grappler/optimizers/constant_folding.h index 0b778882d7..d6350512f8 100644 --- a/tensorflow/core/grappler/optimizers/constant_folding.h +++ b/tensorflow/core/grappler/optimizers/constant_folding.h @@ -124,11 +124,12 @@ class ConstantFolding : public GraphOptimizer { // Pushes down constants on '+' and '*' operators if applicable. Returns true // the transformation applied successfully. - bool ConstantPushDown(NodeDef* node); + bool ConstantPushDown(GraphDef* optimized_graph, NodeDef* node); // Aggregate constants present around a conv operator. Returns true if the // transformation was applied successfully. - bool MulConvPushDown(NodeDef* node, const GraphProperties& properties); + bool MulConvPushDown(GraphDef* optimized_graph, NodeDef* node, + const GraphProperties& properties); // Strength reduces floating point division by a constant Div(x, const) to // multiplication by the reciprocal Mul(x, Reciprocal(const)). -- GitLab From 9585116b80c985a790fb21fe7eb00def7916d092 Mon Sep 17 00:00:00 2001 From: Eugene Zhulenev Date: Wed, 26 Dec 2018 10:43:22 -0800 Subject: [PATCH 100/622] Add GraphTopologyView to efficiently traverse node-to-node connections. + Remove SimpleGraphView from topological sorting. PiperOrigin-RevId: 226932668 --- tensorflow/core/grappler/BUILD | 30 +++ .../core/grappler/costs/graph_properties.cc | 28 ++- .../core/grappler/graph_topology_view.cc | 163 +++++++++++++ .../core/grappler/graph_topology_view.h | 105 ++++++++ .../core/grappler/graph_topology_view_test.cc | 117 +++++++++ tensorflow/core/grappler/utils/BUILD | 6 +- .../core/grappler/utils/topological_sort.cc | 75 ++++-- .../core/grappler/utils/topological_sort.h | 30 ++- .../grappler/utils/topological_sort_test.cc | 230 +++++++++++------- 9 files changed, 665 insertions(+), 119 deletions(-) create mode 100644 tensorflow/core/grappler/graph_topology_view.cc create mode 100644 tensorflow/core/grappler/graph_topology_view.h create mode 100644 tensorflow/core/grappler/graph_topology_view_test.cc diff --git a/tensorflow/core/grappler/BUILD b/tensorflow/core/grappler/BUILD index 6de12192ba..4d0f02f4d6 100644 --- a/tensorflow/core/grappler/BUILD +++ b/tensorflow/core/grappler/BUILD @@ -62,6 +62,36 @@ tf_cuda_library( ], ) +cc_library( + name = "graph_topology_view", + srcs = ["graph_topology_view.cc"], + hdrs = ["graph_topology_view.h"], + visibility = ["//visibility:public"], + deps = [ + ":graph_view", + "//tensorflow/core:graph", + "//tensorflow/core:lib", + "//tensorflow/core:protos_all_cc", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:inlined_vector", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:optional", + "@com_google_absl//absl/types:span", + ], +) + +tf_cc_test( + name = "graph_topology_view_test", + srcs = ["graph_topology_view_test.cc"], + deps = [ + ":graph_topology_view", + ":graph_view", + "//tensorflow/core:protos_all_cc", + "//tensorflow/core:test", + "//tensorflow/core:test_main", + ], +) + cc_library( name = "graph_view", srcs = ["graph_view.cc"], diff --git a/tensorflow/core/grappler/costs/graph_properties.cc b/tensorflow/core/grappler/costs/graph_properties.cc index d699979896..cfbd340f08 100644 --- a/tensorflow/core/grappler/costs/graph_properties.cc +++ b/tensorflow/core/grappler/costs/graph_properties.cc @@ -425,9 +425,11 @@ NodeDef MakeConstNodeDefFromShape(InferenceContext* ic, // information is refined. class TopoQueue { public: - explicit TopoQueue(const std::unordered_map& topo_order) - : topo_order_(topo_order) {} + explicit TopoQueue(const std::vector& topo_order) + : topo_order_(TopoOrder(topo_order)) {} + void push(const NodeDef* n) { queue_.emplace(n, topo_order_.at(n)); } + const NodeDef* pop() { CHECK(!empty()); auto it = queue_.begin(); @@ -448,7 +450,18 @@ class TopoQueue { return lhs.second < rhs.second; } }; - const std::unordered_map& topo_order_; + + const std::unordered_map TopoOrder( + const std::vector& topo_order) const { + std::unordered_map map; + map.reserve(topo_order.size()); + for (int i = 0; i < topo_order.size(); ++i) { + map.emplace(topo_order[i], i); + } + return map; + } + + const std::unordered_map topo_order_; std::set queue_; }; @@ -1970,7 +1983,7 @@ Status GraphProperties::InferStatically(bool assume_valid_feeds, } std::unordered_map resource_handles; - std::vector> extra_deps; + std::vector extra_deps; for (const auto& resource : resources) { for (const NodeDef* src : resource.second.first) { resource_handles[src] = resource.first; @@ -1982,8 +1995,8 @@ Status GraphProperties::InferStatically(bool assume_valid_feeds, } } - std::unordered_map topo_order; - Status s = ComputeTopologicalOrder(item_.graph, &topo_order, &extra_deps); + std::vector topo_order; + Status s = ComputeTopologicalOrder(item_.graph, extra_deps, &topo_order); if (!s.ok()) { if (extra_deps.empty()) { return s; @@ -1992,8 +2005,7 @@ Status GraphProperties::InferStatically(bool assume_valid_feeds, // order. This will make the shape inference less precise but since this // isn't common it's not worth to figure out where to break the loop and // do a proper relaxation. - TF_RETURN_IF_ERROR( - ComputeTopologicalOrder(item_.graph, &topo_order, nullptr)); + TF_RETURN_IF_ERROR(ComputeTopologicalOrder(item_.graph, &topo_order)); } } diff --git a/tensorflow/core/grappler/graph_topology_view.cc b/tensorflow/core/grappler/graph_topology_view.cc new file mode 100644 index 0000000000..38ccfbaeb8 --- /dev/null +++ b/tensorflow/core/grappler/graph_topology_view.cc @@ -0,0 +1,163 @@ +/* Copyright 2018 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/grappler/graph_topology_view.h" + +#include + +#include "absl/container/flat_hash_map.h" +#include "absl/container/inlined_vector.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "absl/types/span.h" +#include "tensorflow/core/framework/graph.pb.h" +#include "tensorflow/core/framework/node_def.pb.h" + +namespace tensorflow { +namespace grappler { + +namespace { + +template +inline void SortAndRemoveDuplicates(T* v) { + std::sort(v->begin(), v->end()); + v->erase(std::unique(v->begin(), v->end()), v->end()); +} + +} // namespace + +Status GraphTopologyView::InitializeFromGraph( + const GraphDef& graph, + const absl::Span ephemeral_edges) { + if (graph_ != nullptr) { + return errors::InvalidArgument("GraphTopologyView is already initialized."); + } + + graph_ = &graph; + num_nodes_ = graph.node_size(); + index_to_node_name_.resize(num_nodes_); + node_name_to_index_.rehash(num_nodes_); + fanins_.resize(num_nodes_); + fanouts_.resize(num_nodes_); + + // Build map from name to index and vice versa. + for (int node_idx = 0; node_idx < num_nodes_; ++node_idx) { + const NodeDef& node = graph.node(node_idx); + node_name_to_index_.emplace(node.name(), node_idx); + index_to_node_name_.emplace_back(node.name()); + } + + // 1. Add ephemeral edges to the adjacency lists. + for (const GraphView::Edge& edge : ephemeral_edges) { + const auto src = node_name_to_index_.find(edge.src.node->name()); + if (src == node_name_to_index_.end()) { + return errors::InvalidArgument("Non-existent src node: ", + edge.src.node->name()); + } + const auto dst = node_name_to_index_.find(edge.dst.node->name()); + if (dst == node_name_to_index_.end()) { + return errors::InvalidArgument("Non-existent dst node: ", + edge.dst.node->name()); + } + const int src_idx = src->second; + const int dst_idx = dst->second; + fanins_[dst_idx].push_back(src_idx); + fanouts_[src_idx].push_back(dst_idx); + } + + // 2. Add graph edges to the adjacency lists. + for (int node_idx = 0; node_idx < num_nodes_; ++node_idx) { + const NodeDef& node = graph.node(node_idx); + fanins_[node_idx].reserve(node.input_size()); + + for (const string& input : node.input()) { + TensorId tensor = ParseTensorName(input); + const auto it = node_name_to_index_.find(tensor.node()); + if (it == node_name_to_index_.end()) { + return errors::InvalidArgument("Non-existent input ", input, + " for node ", node.name()); + } + const int input_idx = it->second; + fanins_[node_idx].push_back(input_idx); + fanouts_[input_idx].push_back(node_idx); + } + + // Dedup the input list while it's still hot in cache. + SortAndRemoveDuplicates(&fanins_[node_idx]); + } + + // Dedup outputs for all the graph nodes. + for (int node_idx = 0; node_idx < num_nodes_; ++node_idx) { + SortAndRemoveDuplicates(&fanouts_[node_idx]); + } + + return Status::OK(); +} + +Status GraphTopologyView::InitializeFromGraph(const GraphDef& graph) { + return InitializeFromGraph(graph, absl::Span()); +} + +bool GraphTopologyView::HasNode(const absl::string_view node_name) const { + DCHECK(is_initialized()) << "GraphTopologyView is not initialized"; + const auto it = node_name_to_index_.find(node_name); + return it != node_name_to_index_.end(); +} + +const NodeDef* GraphTopologyView::GetNode( + const absl::string_view node_name) const { + DCHECK(is_initialized()) << "GraphTopologyView is not initialized"; + const auto it = node_name_to_index_.find(node_name); + return it == node_name_to_index_.end() ? nullptr : &graph_->node(it->second); +} + +const NodeDef* GraphTopologyView::GetNode(int node_idx) const { + DCHECK(is_initialized()) << "GraphTopologyView is not initialized"; + DCHECK(node_idx >= 0 && node_idx < num_nodes_) << "node_idx is out of range"; + return &graph_->node(node_idx); +} + +const absl::optional GraphTopologyView::GetNodeIndex( + const absl::string_view node_name) const { + DCHECK(is_initialized()) << "GraphTopologyView is not initialized"; + const auto it = node_name_to_index_.find(node_name); + DCHECK(it != node_name_to_index_.end()) << "Node doesn't exist in a graph"; + return it == node_name_to_index_.end() ? absl::nullopt + : absl::make_optional(it->second); +} + +const absl::optional GraphTopologyView::GetNodeIndex( + const NodeDef& node) const { + return GetNodeIndex(node.name()); +} + +const absl::InlinedVector& GraphTopologyView::GetFanin( + int node_idx) const { + DCHECK(is_initialized()) << "GraphTopologyView is not initialized"; + const bool is_valid_node_idx = node_idx >= 0 && node_idx < num_nodes_; + DCHECK(is_valid_node_idx) << "node_idx is out of range"; + return is_valid_node_idx ? fanins_[node_idx] : empty_fanin_; +} + +const absl::InlinedVector& GraphTopologyView::GetFanout( + int node_idx) const { + DCHECK(is_initialized()) << "GraphTopologyView is not initialized"; + const bool is_valid_node_idx = node_idx >= 0 && node_idx < num_nodes_; + DCHECK(is_valid_node_idx) << "node_idx is out of range"; + return is_valid_node_idx ? fanouts_[node_idx] : empty_fanout_; +} + +} // end namespace grappler +} // end namespace tensorflow diff --git a/tensorflow/core/grappler/graph_topology_view.h b/tensorflow/core/grappler/graph_topology_view.h new file mode 100644 index 0000000000..1c222df4b6 --- /dev/null +++ b/tensorflow/core/grappler/graph_topology_view.h @@ -0,0 +1,105 @@ +/* Copyright 2018 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_GRAPPLER_GRAPH_TOPOLOGY_VIEW_H_ +#define TENSORFLOW_CORE_GRAPPLER_GRAPH_TOPOLOGY_VIEW_H_ + +#include "absl/container/flat_hash_map.h" +#include "absl/container/inlined_vector.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "absl/types/span.h" +#include "tensorflow/core/graph/tensor_id.h" +#include "tensorflow/core/grappler/graph_view.h" + +namespace tensorflow { +namespace grappler { + +// GraphTopologyView is a helper class to simplify `node-to-node` connectivity +// traversals. Regular `GraphView` simplifies `tensor-to-tensor` traversals: +// connections between output tensors and inputs of a consumer nodes. For the +// topology view we are focused on nodes connected to nodes, and it's irrelevant +// if this connection is formed by one or multiple individual tensors. +// +// Example: +// a = Placeholder(..) +// b = Placeholder(..) +// c = AddN([a, a, b]) +// +// GraphView edges: [a:0 -> c:0, a:0 -> c:1, b:0 -> c:3] +// GraphTopologyView edges: [a -> c, b -> c] +// +// GraphView is used for exploring single node fanins and fanouts, and +// GraphTopologyView is focused on efficient full graph traversals (computing +// graph node properties from transitive fanouts, etc...). +class GraphTopologyView { + public: + GraphTopologyView() = default; + + // Initialize graph topology view from the graph. It's possible to pass + // additional edges that do not exist in a graph, but must be respected when + // computing graph topology. Example: Tensorflow runtime allows concurrent + // execution of dequeue/enqueue ops from the same queue resource, but we might + // want to enforce ordering between them for the purpose of graph analysis. + Status InitializeFromGraph(const GraphDef& graph, + absl::Span ephemeral_edges); + Status InitializeFromGraph(const GraphDef& graph); + + bool is_initialized() const { return graph_ != nullptr; } + int num_nodes() const { return num_nodes_; } + const GraphDef* graph() const { return graph_; } + + // Returns true iff the node exists in the underlying graph. + bool HasNode(absl::string_view node_name) const; + + // Finds a node by name or returns `nullptr` if it's not in the graph. + const NodeDef* GetNode(absl::string_view node_name) const; + // Returns a node corresponding to the given node index. + const NodeDef* GetNode(int node_idx) const; + + // Returns a node index for the given node name, if the name exists in the + // underlying graph. Otherwise returns empty optional. + const absl::optional GetNodeIndex(absl::string_view node_name) const; + // Returns a node index for the given node, if the node belongs to the + // underlying graph. Otherwise returns empty optional. + const absl::optional GetNodeIndex(const NodeDef& node) const; + + // Returns all the node indexes that are in the direct fanin of the given + // node. If the `node_idx` is outside of [0, num_nodes_) returns empty vector. + const absl::InlinedVector& GetFanin(int node_idx) const; + // Returns all the node indexes that are in the direct fanout of the given + // node. If the `node_idx` is outside of [0, num_nodes_) returns empty vector. + const absl::InlinedVector& GetFanout(int node_idx) const; + + private: + // WARN: `graph_` must outlive this object and graph nodes must not be + // destructed, because node names captured with absl::string_view. + const GraphDef* graph_ = nullptr; // do not own + int num_nodes_ = 0; + std::vector index_to_node_name_; + absl::flat_hash_map node_name_to_index_; + std::vector> fanins_; // node_idx->input nodes + std::vector> fanouts_; // node_idx->output nodes + + // We need a valid reference to return from GetFanin/GetFanout if the + // `node_idx` argument is outside of the [0, num_nodes_) range. + absl::InlinedVector empty_fanin_; + absl::InlinedVector empty_fanout_; +}; + +} // end namespace grappler +} // end namespace tensorflow + +#endif // TENSORFLOW_CORE_GRAPPLER_GRAPH_TOPOLOGY_VIEW_H_ diff --git a/tensorflow/core/grappler/graph_topology_view_test.cc b/tensorflow/core/grappler/graph_topology_view_test.cc new file mode 100644 index 0000000000..36d3a2017c --- /dev/null +++ b/tensorflow/core/grappler/graph_topology_view_test.cc @@ -0,0 +1,117 @@ +/* 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/grappler/graph_topology_view.h" + +#include "tensorflow/core/framework/graph.pb.h" +#include "tensorflow/core/framework/node_def.pb.h" +#include "tensorflow/core/platform/test.h" + +namespace tensorflow { +namespace grappler { + +class GraphTopologyViewTest : public ::testing::Test { + protected: + using NodeConfig = std::pair>; + + static GraphDef CreateGraph(const std::vector& nodes) { + GraphDef graph; + + for (const NodeConfig& node : nodes) { + const auto& node_name = node.first; + const auto& node_inputs = node.second; + + NodeDef node_def; + node_def.set_name(node_name); + for (const string& input : node_inputs) { + node_def.add_input(input); + } + + *graph.add_node() = std::move(node_def); + } + + return graph; + } +}; + +TEST_F(GraphTopologyViewTest, SimpleGraph) { + const GraphDef graph = CreateGraph({ + {"a", {}}, // idx: 0 + {"b", {}}, // idx: 1 + {"c", {"a", "b"}}, // idx: 2 + {"d", {"a", "c"}}, // idx: 3 + }); + + GraphTopologyView graph_view; + TF_CHECK_OK(graph_view.InitializeFromGraph(graph)); + + EXPECT_TRUE(graph_view.is_initialized()); + + const NodeDef* a_by_name = graph_view.GetNode("a"); + const NodeDef* a_by_idx = graph_view.GetNode(0); + ASSERT_TRUE(a_by_name); + ASSERT_TRUE(a_by_idx); + EXPECT_EQ(a_by_name, a_by_idx); + + const NodeDef* b_by_name = graph_view.GetNode("b"); + const NodeDef* b_by_idx = graph_view.GetNode(1); + ASSERT_TRUE(b_by_name); + ASSERT_TRUE(b_by_idx); + EXPECT_EQ(b_by_name, b_by_idx); + + const absl::optional b_idx = graph_view.GetNodeIndex(*b_by_name); + ASSERT_TRUE(b_idx.has_value()); + EXPECT_EQ(b_idx.value(), 1); + + const absl::optional c_idx = graph_view.GetNodeIndex("c"); + ASSERT_TRUE(c_idx.has_value()); + EXPECT_EQ(c_idx.value(), 2); + + using Fanin = absl::InlinedVector; + EXPECT_EQ(graph_view.GetFanin(0), Fanin()); + EXPECT_EQ(graph_view.GetFanin(1), Fanin()); + EXPECT_EQ(graph_view.GetFanin(2), Fanin({0, 1})); + EXPECT_EQ(graph_view.GetFanin(3), Fanin({0, 2})); + + using Fanout = absl::InlinedVector; + EXPECT_EQ(graph_view.GetFanout(0), Fanout({2, 3})); + EXPECT_EQ(graph_view.GetFanout(1), Fanout({2})); + EXPECT_EQ(graph_view.GetFanout(2), Fanout({3})); + EXPECT_EQ(graph_view.GetFanout(3), Fanout()); +} + +TEST_F(GraphTopologyViewTest, GraphWithALoop) { + const GraphDef graph = CreateGraph({ + {"a", {}}, // idx: 0 + {"b", {}}, // idx: 1 + {"c", {"a", "b", "d"}}, // idx: 2 <<<--- 'c' and 'd' have a loop + {"d", {"a", "c"}}, // idx: 3 + }); + + GraphTopologyView graph_view; + TF_CHECK_OK(graph_view.InitializeFromGraph(graph)); + EXPECT_TRUE(graph_view.is_initialized()); + + using Fanin = absl::InlinedVector; + EXPECT_EQ(graph_view.GetFanin(2), Fanin({0, 1, 3})); + EXPECT_EQ(graph_view.GetFanin(3), Fanin({0, 2})); + + using Fanout = absl::InlinedVector; + EXPECT_EQ(graph_view.GetFanout(2), Fanout({3})); + EXPECT_EQ(graph_view.GetFanout(3), Fanout({2})); +} + +} // namespace grappler +} // namespace tensorflow diff --git a/tensorflow/core/grappler/utils/BUILD b/tensorflow/core/grappler/utils/BUILD index c0f19d3828..b6e70361f3 100644 --- a/tensorflow/core/grappler/utils/BUILD +++ b/tensorflow/core/grappler/utils/BUILD @@ -48,8 +48,11 @@ cc_library( "//tensorflow/core:lib", "//tensorflow/core:lib_internal", "//tensorflow/core:protos_all_cc", + "//tensorflow/core/grappler:graph_topology_view", + "//tensorflow/core/grappler:graph_view", "//tensorflow/core/grappler:op_types", "//tensorflow/core/grappler:utils", + "@com_google_absl//absl/types:span", ], ) @@ -58,10 +61,11 @@ tf_cc_test( srcs = ["topological_sort_test.cc"], deps = [ ":topological_sort", - "//tensorflow/core:lib_proto_parsing", + "//tensorflow/core:lib", "//tensorflow/core:protos_all_cc", "//tensorflow/core:test", "//tensorflow/core:test_main", + "@com_google_absl//absl/strings", ], ) diff --git a/tensorflow/core/grappler/utils/topological_sort.cc b/tensorflow/core/grappler/utils/topological_sort.cc index 63ca92c69e..a6d0f5037b 100644 --- a/tensorflow/core/grappler/utils/topological_sort.cc +++ b/tensorflow/core/grappler/utils/topological_sort.cc @@ -14,10 +14,15 @@ limitations under the License. ==============================================================================*/ #include "tensorflow/core/grappler/utils/topological_sort.h" + #include #include #include + +#include "absl/types/span.h" #include "tensorflow/core/framework/node_def.pb.h" +#include "tensorflow/core/grappler/graph_topology_view.h" +#include "tensorflow/core/grappler/graph_view.h" #include "tensorflow/core/grappler/op_types.h" #include "tensorflow/core/grappler/utils.h" #include "tensorflow/core/lib/core/status.h" @@ -25,27 +30,46 @@ limitations under the License. namespace tensorflow { namespace grappler { +namespace { + +std::vector MakeEphemeralEdges( + const absl::Span extra_dependencies) { + std::vector ephemeral_edges; + ephemeral_edges.reserve(extra_dependencies.size()); + for (const auto& dep : extra_dependencies) { + ephemeral_edges.emplace_back( + GraphView::OutputPort(dep.from, Graph::kControlSlot), + GraphView::InputPort(dep.to, Graph::kControlSlot)); + } + return ephemeral_edges; +} + // Kahn's algorithm is implemented. // For details, see https://en.wikipedia.org/wiki/Topological_sorting Status ComputeTopologicalOrder( - const GraphDef& graph, std::vector* ready_nodes, - const std::vector>* - extra_dependencies) { - SimpleGraphView graph_view; - TF_RETURN_IF_ERROR(graph_view.Initialize(graph, extra_dependencies)); + const GraphDef& graph, + const absl::Span extra_dependencies, + std::vector* ready_nodes) { + GraphTopologyView graph_view; + TF_RETURN_IF_ERROR(graph_view.InitializeFromGraph( + graph, MakeEphemeralEdges(extra_dependencies))); + + // Keep track of how many inputs are ready for the given node. + std::vector num_ready_inputs(graph.node_size(), 0); - ready_nodes->reserve(graph_view.num_nodes()); + // We'll push index of ready nodes to this output vector. + ready_nodes->reserve(graph.node_size()); int front = 0; int back = 0; - std::vector num_ready_inputs(graph_view.num_nodes(), 0); - for (int i = 0; i < graph_view.num_nodes(); i++) { - if (graph_view.inputs(i).empty()) { + + for (int i = 0; i < graph.node_size(); i++) { + if (graph_view.GetFanin(i).empty()) { ready_nodes->push_back(i); back++; } if (IsMerge(graph.node(i))) { - for (int input : graph_view.inputs(i)) { + for (int input : graph_view.GetFanin(i)) { if (IsNextIteration(graph.node(input))) { num_ready_inputs[i]++; } @@ -55,9 +79,9 @@ Status ComputeTopologicalOrder( while (front != back) { int ready_node = (*ready_nodes)[front]; - for (int fanout : graph_view.outputs(ready_node)) { + for (int fanout : graph_view.GetFanout(ready_node)) { ++num_ready_inputs[fanout]; - if (num_ready_inputs[fanout] == graph_view.inputs(fanout).size()) { + if (num_ready_inputs[fanout] == graph_view.GetFanin(fanout).size()) { ready_nodes->push_back(fanout); ++back; } @@ -72,23 +96,32 @@ Status ComputeTopologicalOrder( return Status::OK(); } +} // namespace + Status ComputeTopologicalOrder( - const GraphDef& graph, std::unordered_map* topo_order, - const std::vector>* - extra_dependencies) { + const GraphDef& graph, + const absl::Span extra_dependencies, + std::vector* topo_order) { std::vector ready_nodes; TF_RETURN_IF_ERROR( - ComputeTopologicalOrder(graph, &ready_nodes, extra_dependencies)); - topo_order->reserve(graph.node_size()); - for (int i = 0; i < ready_nodes.size(); ++i) { - (*topo_order)[&graph.node(ready_nodes[i])] = i; + ComputeTopologicalOrder(graph, extra_dependencies, &ready_nodes)); + + topo_order->reserve(ready_nodes.size()); + for (int ready_node_idx : ready_nodes) { + topo_order->emplace_back(&graph.node(ready_node_idx)); } + return Status::OK(); } +Status ComputeTopologicalOrder(const GraphDef& graph, + std::vector* topo_order) { + return ComputeTopologicalOrder(graph, {}, topo_order); +} + Status ReversedTopologicalSort(GraphDef* graph) { std::vector ready_nodes; - TF_RETURN_IF_ERROR(ComputeTopologicalOrder(*graph, &ready_nodes, nullptr)); + TF_RETURN_IF_ERROR(ComputeTopologicalOrder(*graph, {}, &ready_nodes)); std::reverse(ready_nodes.begin(), ready_nodes.end()); PermuteNodesInPlace(graph, &ready_nodes, /*invert_permutation=*/true); return Status::OK(); @@ -96,7 +129,7 @@ Status ReversedTopologicalSort(GraphDef* graph) { Status TopologicalSort(GraphDef* graph) { std::vector ready_nodes; - TF_RETURN_IF_ERROR(ComputeTopologicalOrder(*graph, &ready_nodes, nullptr)); + TF_RETURN_IF_ERROR(ComputeTopologicalOrder(*graph, {}, &ready_nodes)); PermuteNodesInPlace(graph, &ready_nodes, /*invert_permutation=*/true); return Status::OK(); } diff --git a/tensorflow/core/grappler/utils/topological_sort.h b/tensorflow/core/grappler/utils/topological_sort.h index b8cf897a32..dd4208dfff 100644 --- a/tensorflow/core/grappler/utils/topological_sort.h +++ b/tensorflow/core/grappler/utils/topological_sort.h @@ -16,22 +16,40 @@ limitations under the License. #ifndef TENSORFLOW_CORE_GRAPPLER_UTILS_TOPOLOGICAL_SORT_H_ #define TENSORFLOW_CORE_GRAPPLER_UTILS_TOPOLOGICAL_SORT_H_ +#include "absl/types/span.h" #include "tensorflow/core/framework/graph.pb.h" #include "tensorflow/core/lib/core/status.h" namespace tensorflow { namespace grappler { -// Compute a topological ordering for the graph nodes. +// TODO(ezhulenev, b/121379902): We should be consistent with GraphTopologyView +// and use `GraphView::Edge` to pass extra dependencies. +struct TopologicalDependency { + TopologicalDependency(const NodeDef* from, const NodeDef* to) + : from(from), to(to) {} + const NodeDef* from; + const NodeDef* to; +}; + +// Computes a topological ordering for the graph nodes and outputs nodes in the +// topological order to the `topo_order` output argument. +// +// It's possible to pass additional edges that do not exists in a graph, but +// must be respected when computing graph topological order. Example: Tensorflow +// runtime allows concurrent execution of dequeue/enqueue ops from the same +// queue resource, but we might want to enforce ordering between them. Status ComputeTopologicalOrder( - const GraphDef& graph, std::unordered_map* topo_order, - const std::vector>* - extra_dependencies); + const GraphDef& graph, + absl::Span extra_dependencies, + std::vector* topo_order); +Status ComputeTopologicalOrder(const GraphDef& graph, + std::vector* topo_order); -// Sort a graph in topological order. +// Sorts a graph in topological order. Status TopologicalSort(GraphDef* graph); -// Sort a graph in topological order and reverse it. +// Sorts a graph in topological order and reverse it. Status ReversedTopologicalSort(GraphDef* graph); } // namespace grappler diff --git a/tensorflow/core/grappler/utils/topological_sort_test.cc b/tensorflow/core/grappler/utils/topological_sort_test.cc index 48b7eb50bd..3868183c62 100644 --- a/tensorflow/core/grappler/utils/topological_sort_test.cc +++ b/tensorflow/core/grappler/utils/topological_sort_test.cc @@ -14,79 +14,94 @@ limitations under the License. ==============================================================================*/ #include "tensorflow/core/grappler/utils/topological_sort.h" +#include "absl/strings/str_cat.h" #include "tensorflow/core/framework/node_def.pb.h" #include "tensorflow/core/lib/core/status_test_util.h" +#include "tensorflow/core/lib/random/philox_random.h" +#include "tensorflow/core/lib/random/simple_philox.h" #include "tensorflow/core/platform/protobuf.h" #include "tensorflow/core/platform/test.h" +#include "tensorflow/core/platform/test_benchmark.h" namespace tensorflow { namespace grappler { -namespace { class TopologicalSortTest : public ::testing::Test { protected: - static NodeDef CreateNode(const string& name, - const std::vector& inputs) { - return CreateNode(name, "", inputs); - } - static NodeDef CreateNode(const string& name, const string& op, - const std::vector& inputs) { - NodeDef node; - node.set_name(name); - if (!op.empty()) { - node.set_op(op); + struct NodeConfig { + NodeConfig(string name, std::vector inputs) + : name(std::move(name)), inputs(std::move(inputs)) {} + NodeConfig(string name, string op, std::vector inputs) + : name(std::move(name)), op(std::move(op)), inputs(std::move(inputs)) {} + + string name; + string op; + std::vector inputs; + }; + + static GraphDef CreateGraph(const std::vector& nodes) { + GraphDef graph; + + for (const NodeConfig& node : nodes) { + NodeDef node_def; + node_def.set_name(node.name); + node_def.set_op(node.op); + for (const string& input : node.inputs) { + node_def.add_input(input); + } + *graph.add_node() = std::move(node_def); } - for (const string& input : inputs) { - node.add_input(input); - } - return node; + + return graph; } }; TEST_F(TopologicalSortTest, NoLoop) { - GraphDef graph; - *graph.add_node() = CreateNode("2", {"5"}); - *graph.add_node() = CreateNode("0", {"5", "4"}); - *graph.add_node() = CreateNode("1", {"4", "3"}); - *graph.add_node() = CreateNode("3", {"2"}); - *graph.add_node() = CreateNode("5", {}); - *graph.add_node() = CreateNode("4", {}); - - std::unordered_map topo_order; - TF_EXPECT_OK(ComputeTopologicalOrder(graph, &topo_order, nullptr)); + GraphDef graph = CreateGraph({ + {"2", {"5"}}, // + {"0", {"5", "4"}}, // + {"1", {"4", "3"}}, // + {"3", {"2"}}, // + {"5", {}}, // + {"4", {}} // + }); + + std::vector topo_order; + TF_EXPECT_OK(ComputeTopologicalOrder(graph, &topo_order)); const std::vector order = {"5", "4", "2", "0", "3", "1"}; - for (const auto& topo : topo_order) { - const string& node_name = topo.first->name(); - const int topo_order = topo.second; - std::cout << "Node " << node_name << " at order " << topo_order - << std::endl; - EXPECT_EQ(node_name, order[topo_order]); + + ASSERT_EQ(topo_order.size(), order.size()); + for (int i = 0; i < topo_order.size(); ++i) { + const NodeDef* node = topo_order[i]; + EXPECT_EQ(node->name(), order[i]); } TF_EXPECT_OK(TopologicalSort(&graph)); - for (int i = 0; i < order.size(); i++) { + for (int i = 0; i < topo_order.size(); i++) { EXPECT_EQ(graph.node(i).name(), order[i]); } } TEST_F(TopologicalSortTest, WithLoop) { - GraphDef graph; - // Create a loop - *graph.add_node() = CreateNode("2", "Merge", {"1", "5"}); - *graph.add_node() = CreateNode("3", "Switch", {"2"}); - *graph.add_node() = CreateNode("4", "Identity", {"3"}); - *graph.add_node() = CreateNode("5", "NextIteration", {"4"}); - *graph.add_node() = CreateNode("1", {}); - - std::unordered_map topo_order; - TF_EXPECT_OK(ComputeTopologicalOrder(graph, &topo_order, nullptr)); + GraphDef graph = CreateGraph({ + // Graph with a loop. + {"2", "Merge", {"1", "5"}}, // + {"3", "Switch", {"2"}}, // + {"4", "Identity", {"3"}}, // + {"5", "NextIteration", {"4"}}, // + {"1", {}} // + }); + + std::vector topo_order; + TF_EXPECT_OK(ComputeTopologicalOrder(graph, &topo_order)); const std::vector order = {"1", "2", "3", "4", "5"}; - for (const auto& topo : topo_order) { - const string& node_name = topo.first->name(); - const int topo_order = topo.second; - EXPECT_EQ(node_name, order[topo_order]); + + ASSERT_EQ(topo_order.size(), order.size()); + for (int i = 0; i < topo_order.size(); ++i) { + const NodeDef* node = topo_order[i]; + EXPECT_EQ(node->name(), order[i]); } TF_EXPECT_OK(TopologicalSort(&graph)); @@ -96,12 +111,13 @@ TEST_F(TopologicalSortTest, WithLoop) { } TEST_F(TopologicalSortTest, WithIllegalLoop) { - GraphDef graph; // A loop without Merge and NextIteration is illegal and the original node // order and graph will be preserved. - *graph.add_node() = CreateNode("2", {"1", "3"}); - *graph.add_node() = CreateNode("3", {"2"}); - *graph.add_node() = CreateNode("1", {}); + GraphDef graph = CreateGraph({ + {"2", {"1", "3"}}, // + {"3", {"2"}}, // + {"1", {}} // + }); EXPECT_FALSE(TopologicalSort(&graph).ok()); std::vector order = {"2", "3", "1"}; @@ -111,9 +127,10 @@ TEST_F(TopologicalSortTest, WithIllegalLoop) { } TEST_F(TopologicalSortTest, DuplicatedInputs) { - GraphDef graph; - *graph.add_node() = CreateNode("2", {"1", "1"}); - *graph.add_node() = CreateNode("1", {}); + GraphDef graph = CreateGraph({ + {"2", {"1", "1"}}, // + {"1", {}} // + }); TF_EXPECT_OK(TopologicalSort(&graph)); std::vector order = {"1", "2"}; @@ -123,12 +140,13 @@ TEST_F(TopologicalSortTest, DuplicatedInputs) { } TEST_F(TopologicalSortTest, Idempotent) { - GraphDef graph; - *graph.add_node() = CreateNode("1", {}); - *graph.add_node() = CreateNode("2", {}); - *graph.add_node() = CreateNode("3", {"1", "2"}); - *graph.add_node() = CreateNode("4", {"1", "3"}); - *graph.add_node() = CreateNode("5", {"2", "3"}); + GraphDef graph = CreateGraph({ + {"1", {}}, // + {"2", {}}, // + {"3", {"1", "2"}}, // + {"4", {"1", "3"}}, // + {"5", {"2", "3"}} // + }); TF_EXPECT_OK(TopologicalSort(&graph)); std::vector order = {"1", "2", "3", "4", "5"}; @@ -136,7 +154,7 @@ TEST_F(TopologicalSortTest, Idempotent) { EXPECT_EQ(graph.node(i).name(), order[i]); } - // Run topo sort again to verify that it is idenpotent. + // Run topo sort again to verify that it is idempotent. TF_EXPECT_OK(TopologicalSort(&graph)); for (int i = 0; i < order.size(); i++) { EXPECT_EQ(graph.node(i).name(), order[i]); @@ -144,35 +162,81 @@ TEST_F(TopologicalSortTest, Idempotent) { } TEST_F(TopologicalSortTest, ExtraDependencies) { - GraphDef graph; - *graph.add_node() = CreateNode("2", {"5"}); - *graph.add_node() = CreateNode("0", {"5", "4"}); - *graph.add_node() = CreateNode("1", {"4", "3"}); - *graph.add_node() = CreateNode("3", {"2"}); - *graph.add_node() = CreateNode("5", {}); - *graph.add_node() = CreateNode("4", {}); + GraphDef graph = CreateGraph({ + {"2", {"5"}}, // + {"0", {"5", "4"}}, // + {"1", {"4", "3"}}, // + {"3", {"2"}}, // + {"5", {}}, // + {"4", {}} // + }); // Add an edge from 4 to 5. - std::vector> extra_dependencies; - extra_dependencies.emplace_back(&graph.node(5), &graph.node(4)); - - std::unordered_map topo_order; - TF_EXPECT_OK( - ComputeTopologicalOrder(graph, &topo_order, &extra_dependencies)); - - const std::vector order = {"4", "5", "2", "0", "3", "1"}; - for (const auto& topo : topo_order) { - const string& node_name = topo.first->name(); - const int topo_order = topo.second; - EXPECT_EQ(node_name, order[topo_order]); + std::vector extra_dependencies; + extra_dependencies.push_back({&graph.node(5), &graph.node(4)}); + + std::vector topo_order; + TF_EXPECT_OK(ComputeTopologicalOrder(graph, extra_dependencies, &topo_order)); + + const std::vector valid_order_1 = {"4", "5", "2", "0", "3", "1"}; + const std::vector valid_order_2 = {"4", "5", "0", "2", "3", "1"}; + + ASSERT_EQ(topo_order.size(), valid_order_1.size()); + + std::vector computed_order(6, ""); + for (int i = 0; i < topo_order.size(); ++i) { + const NodeDef* node = topo_order[i]; + computed_order[i] = node->name(); } + EXPECT_TRUE(computed_order == valid_order_1 || + computed_order == valid_order_2); - // Add an edge from 0 to 4. This will create a loop - extra_dependencies.emplace_back(&graph.node(1), &graph.node(5)); + // Add an edge from `0` to `4`. This will create a loop. + extra_dependencies.push_back({&graph.node(1), &graph.node(5)}); EXPECT_FALSE( - ComputeTopologicalOrder(graph, &topo_order, &extra_dependencies).ok()); + ComputeTopologicalOrder(graph, extra_dependencies, &topo_order).ok()); +} + +static void BM_ComputeTopologicalOrder(int iters, int size) { + testing::StopTiming(); + + random::PhiloxRandom philox(0x12345); + random::SimplePhilox rnd(&philox); + + string prefix = "long_node_name_prefix_to_measure_string_copy_overhead"; + + GraphDef graph; + for (int i = 0; i < size; ++i) { + const string name = absl::StrCat(prefix, i); + const uint32 num_inputs = rnd.Uniform(std::min(i, 5)); + + NodeDef node; + node.set_name(name); + for (int n = 0; n < num_inputs; ++n) { + const uint32 input_node = rnd.Uniform(i); + node.add_input(absl::StrCat(prefix, input_node)); + } + + *graph.add_node() = std::move(node); + } + + testing::StartTiming(); + std::vector topo_order; + for (int i = 0; i < iters; i++) { + topo_order.clear(); + Status st = ComputeTopologicalOrder(graph, &topo_order); + CHECK(st.ok()) << "Failed to compute topological order"; + } + testing::StopTiming(); } +BENCHMARK(BM_ComputeTopologicalOrder) + ->Arg(10) + ->Arg(100) + ->Arg(1000) + ->Arg(10000) + ->Arg(25000) + ->Arg(50000) + ->Arg(100000); -} // namespace } // namespace grappler } // namespace tensorflow -- GitLab From 9392ffa09224f0a7735aa7076bee2024c39f1e69 Mon Sep 17 00:00:00 2001 From: Gaurav Jain Date: Wed, 26 Dec 2018 10:46:00 -0800 Subject: [PATCH 101/622] Improve compatibility of while_v2 with XLA tests Remove assumption where resource variables could not be included as outputs of the body. We instead iterate through the outputs to find the first resource variable index. Also loosen the requirements to specify maximum_iterations for XLA. PiperOrigin-RevId: 226932912 --- .../compiler/tf2xla/kernels/while_op.cc | 17 ++--- .../kernel_tests/control_flow_ops_py_test.py | 62 +++++++------------ tensorflow/python/ops/while_v2.py | 49 ++++++++------- 3 files changed, 54 insertions(+), 74 deletions(-) diff --git a/tensorflow/compiler/tf2xla/kernels/while_op.cc b/tensorflow/compiler/tf2xla/kernels/while_op.cc index b32683a682..941b04363f 100644 --- a/tensorflow/compiler/tf2xla/kernels/while_op.cc +++ b/tensorflow/compiler/tf2xla/kernels/while_op.cc @@ -291,20 +291,15 @@ void XlaWhileOp::Compile(XlaOpKernelContext* ctx) { xla::XlaOp while_result = xla::While(cond_wrapper, *body.computation, init); - auto while_shape_or = builder->GetShape(while_result); - OP_REQUIRES_OK(ctx, while_shape_or.status()); - auto count = xla::ShapeUtil::TupleElementCount(while_shape_or.ValueOrDie()); - int max_index = body.outputs.size() + body.resource_updates.size() - 1; - OP_REQUIRES( - ctx, max_index < count, - errors::Internal("Max tuple element requested (", max_index, - ") needs to be less than tuple size (", count, ")")); - - // Sets non-variable outputs. + // Sets non-variable outputs and determine when resource variables start. + int resource_index = 0; for (int i = 0; i < ctx->num_outputs(); ++i) { if (ctx->input_type(i) != DT_RESOURCE) { ctx->SetOutput(body.input_mapping[i], xla::GetTupleElement(while_result, i)); + ++resource_index; + } else { + break; } } if (has_token_input_output_) { @@ -326,7 +321,7 @@ void XlaWhileOp::Compile(XlaOpKernelContext* ctx) { XlaResource* resource; OP_REQUIRES_OK(ctx, ctx->GetResourceInput(update.input_index, &resource)); if (update.modified) { - int pos = body.outputs.size() + i; + int pos = resource_index + i; OP_REQUIRES_OK(ctx, resource->SetFromPack( arguments[update.input_index].tensor_array_gradients, diff --git a/tensorflow/python/kernel_tests/control_flow_ops_py_test.py b/tensorflow/python/kernel_tests/control_flow_ops_py_test.py index fa62acbfeb..2949038728 100644 --- a/tensorflow/python/kernel_tests/control_flow_ops_py_test.py +++ b/tensorflow/python/kernel_tests/control_flow_ops_py_test.py @@ -1183,6 +1183,8 @@ class ControlFlowTest(test.TestCase): @test_util.run_v1_only("b/120545219") def testInvalidMaximumIterationsWhileLoopGradientInXLAContext(self): + if control_flow_util.ENABLE_CONTROL_FLOW_V2: + self.skipTest("WhileV2 does lazy evaluation of maximum_iterations") v = constant_op.constant(1.0) def inner_body(i, x): @@ -1203,44 +1205,27 @@ class ControlFlowTest(test.TestCase): gs = gradients_impl.gradients(loop_no_xla, v) self.evaluate(gs) # This should execute without error. - if control_flow_util.ENABLE_CONTROL_FLOW_V2: - xla_context = control_flow_ops.XLAControlFlowContext() - xla_context.Enter() - with self.assertRaisesRegexp( - ValueError, - r"maximum_iterations is None. It is required and must be statically " - r"known \(e.g. a constant value or known shape dimension\) when " - r"building while_loop in XLA context."): - loop_no_maxiter = create_while_loop() - with self.assertRaisesRegexp( - ValueError, - r"maximum_iterations must be statically " - r"known \(e.g. a constant value or known shape dimension\) when " - r"building while_loop in XLA context."): - loop_with_maxiter = create_while_loop(maximum_iterations=2) - xla_context.Exit() - else: - xla_context = control_flow_ops.XLAControlFlowContext() - xla_context.Enter() - loop_no_maxiter = create_while_loop() - loop_with_maxiter = create_while_loop(maximum_iterations=2) - xla_context.Exit() + xla_context = control_flow_ops.XLAControlFlowContext() + xla_context.Enter() + loop_no_maxiter = create_while_loop() + loop_with_maxiter = create_while_loop(maximum_iterations=2) + xla_context.Exit() - with self.assertRaisesRegexp( - ValueError, - r"Cannot create a gradient accumulator for tensor '.+' inside " - r"XLA while_loop because maximum_iterations was not passed to " - r"the tf.while_loop call \('.+'\)."): - _ = gradients_impl.gradients(loop_no_maxiter, v) + with self.assertRaisesRegexp( + ValueError, + r"Cannot create a gradient accumulator for tensor '.+' inside " + r"XLA while_loop because maximum_iterations was not passed to " + r"the tf.while_loop call \('.+'\)."): + _ = gradients_impl.gradients(loop_no_maxiter, v) - with self.assertRaisesRegexp( - ValueError, - r"Cannot create a gradient accumulator for tensor '.+' inside XLA " - r"while_loop. maximum_iterations tensor '.+' for while_loop context " - r"'.+' must be statically known \(e.g. a constant value or known " - r"shape dimension\), or be defined at or outside the while loop " - r"context '.*' \(currently defined in '.*'\)"): - _ = gradients_impl.gradients(loop_with_maxiter, v) + with self.assertRaisesRegexp( + ValueError, + r"Cannot create a gradient accumulator for tensor '.+' inside XLA " + r"while_loop. maximum_iterations tensor '.+' for while_loop context " + r"'.+' must be statically known \(e.g. a constant value or known " + r"shape dimension\), or be defined at or outside the while loop " + r"context '.*' \(currently defined in '.*'\)"): + _ = gradients_impl.gradients(loop_with_maxiter, v) @test_util.run_v1_only("b/120545219") def testInvalidMaximumIterationsFromSiblingContextWhileLoopInXLAContext(self): @@ -1265,10 +1250,7 @@ class ControlFlowTest(test.TestCase): xla_context = control_flow_ops.XLAControlFlowContext() xla_context.Enter() with self.assertRaisesRegexp( - ValueError, - r"maximum_iterations must be statically known \(e.g. a constant value" - r" or known shape dimension\) when building while_loop in XLA " - r"context."): + ValueError, r"Tensor.*Placeholder:0.* must be from the same graph.*"): loop = create_while_loop() xla_context.Exit() else: diff --git a/tensorflow/python/ops/while_v2.py b/tensorflow/python/ops/while_v2.py index 295686f814..8547cb94a0 100644 --- a/tensorflow/python/ops/while_v2.py +++ b/tensorflow/python/ops/while_v2.py @@ -254,6 +254,7 @@ def _WhileGrad(op, *grads): # pylint: disable=invalid-name maximum_iterations = op.get_attr( "_maximum_iterations") if _is_in_xla_context() else None assert not _is_in_xla_context() or maximum_iterations is not None + maximum_iterations = _validate_and_convert_to_tensor(maximum_iterations) # Set the incoming gradient of non-trainable inputs to None. It is possible # that we receive non-None gradients for non-trainable types in nested while @@ -376,28 +377,30 @@ def _validate_and_convert_to_tensor(maximum_iterations): Raises: ValueError: If `maximum_iterations` is invalid. """ - if _is_in_xla_context(): - if maximum_iterations is None: - raise ValueError("maximum_iterations is None. It is required and must " - "be statically known (e.g. a constant value or known " - "shape dimension) when building while_loop in XLA " - "context.") - if isinstance(maximum_iterations, ops.Tensor): - # Get the constant value from the `maximum_iterations` tensor to avoid - # capturing a Const tensor from outside this graph. - maximum_iterations = tensor_util.constant_value(maximum_iterations) - if maximum_iterations is None: - raise ValueError("maximum_iterations must be statically known (e.g. a " - "constant value or known shape dimension) when " - "building while_loop in XLA context.") - - if maximum_iterations is not None: - # EmptyTensorList expects `max_num_elements` to be of type int32. - maximum_iterations = ops.convert_to_tensor( - maximum_iterations, dtype=dtypes.int32, name="maximum_iterations") - if maximum_iterations.shape.ndims != 0: - raise ValueError("maximum_iterations must be a scalar, saw shape: %s" % - maximum_iterations.shape) + if maximum_iterations is None: + return None + + if _is_in_xla_context() and isinstance(maximum_iterations, ops.Tensor): + # Get the constant value from the `maximum_iterations` tensor to avoid + # capturing a Const tensor from outside this graph. + value = tensor_util.constant_value(maximum_iterations) + if value is None: + # XLA requires maximum_iterations to be statically known (e.g. a + # constant value or known shape dimension) when intermediate values + # from the forward pass are needed in the gradients pass. However, + # maximum_iterations may not be required if the gradient isn't built + # or no intermediates are required, thus we return the tensor as is. + return maximum_iterations + + maximum_iterations = value + + # EmptyTensorList expects `max_num_elements` to be of type int32. + maximum_iterations = ops.convert_to_tensor( + maximum_iterations, dtype=dtypes.int32, name="maximum_iterations") + if maximum_iterations.shape.ndims != 0: + raise ValueError("maximum_iterations must be a scalar, saw shape: %s" % + maximum_iterations.shape) + return maximum_iterations @@ -815,7 +818,7 @@ def _copy_handle_data(src_tensors, tgt_tensors): def _maybe_set_maximum_iterations_attr(op, maximum_iterations): - if control_flow_util.IsInXLAContext(op): + if maximum_iterations is not None and control_flow_util.IsInXLAContext(op): # Store the maximum_iterations to use in the gradient pass. op._set_attr( # pylint: disable=protected-access "_maximum_iterations", -- GitLab From b58527bd2d5c7c3b072a629616ecefd27c4cdcce Mon Sep 17 00:00:00 2001 From: Jianwei Xie Date: Wed, 26 Dec 2018 11:03:02 -0800 Subject: [PATCH 102/622] Automated rollback of commit 2696015d8539ea68f27a519ac908ed538f8085ae PiperOrigin-RevId: 226934586 --- .../contrib/optimizer_v2/rmsprop_test.py | 53 ------------------- tensorflow/python/keras/optimizer_v2/BUILD | 1 - .../python/keras/optimizer_v2/optimizer_v2.py | 11 ++-- .../python/keras/optimizer_v2/rmsprop_test.py | 48 ----------------- 4 files changed, 5 insertions(+), 108 deletions(-) diff --git a/tensorflow/contrib/optimizer_v2/rmsprop_test.py b/tensorflow/contrib/optimizer_v2/rmsprop_test.py index ab47b74c65..202c1e9afc 100644 --- a/tensorflow/contrib/optimizer_v2/rmsprop_test.py +++ b/tensorflow/contrib/optimizer_v2/rmsprop_test.py @@ -25,12 +25,10 @@ from absl.testing import parameterized import numpy as np from tensorflow.contrib.optimizer_v2 import rmsprop -from tensorflow.python.eager import context from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import test_util -from tensorflow.python.ops import array_ops from tensorflow.python.ops import embedding_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import resource_variable_ops @@ -450,56 +448,5 @@ class RMSPropOptimizerTest(test.TestCase, parameterized.TestCase): ]), var1.eval()) -class SlotColocationTest(test.TestCase, parameterized.TestCase): - - @parameterized.parameters([True, False]) - @test_util.run_in_graph_and_eager_modes - def testRunMinimizeOnGPUForCPUVariables(self, use_resource): - if not context.context().num_gpus(): - self.skipTest("No GPUs found") - - with ops.device("/device:CPU:0"): - if use_resource: - var0 = resource_variable_ops.ResourceVariable([1.0, 2.0], - dtype=dtypes.float32) - var1 = resource_variable_ops.ResourceVariable([3.0, 4.0], - dtype=dtypes.float32) - global_step = resource_variable_ops.ResourceVariable( - array_ops.zeros([], dtypes.int64), name="global_step") - else: - var0 = variables.Variable([1.0, 2.0], dtype=dtypes.float32) - var1 = variables.Variable([3.0, 4.0], dtype=dtypes.float32) - global_step = variables.Variable( - array_ops.zeros([], dtypes.int64), name="global_step") - - def loss(): - return 5 * var0 + 3 * var1 - - opt = rmsprop.RMSPropOptimizer( - learning_rate=1.0, decay=0.9, momentum=0.5, epsilon=1.0) - - # Fetch params to validate initial values - self.evaluate(variables.global_variables_initializer()) - self.assertAllClose([1.0, 2.0], self.evaluate(var0)) - self.assertAllClose([3.0, 4.0], self.evaluate(var1)) - - # Run 1 step through optimizer on GPU. - # Slot variables are created the first time optimizer is used on some - # variable. This tests that slot variables will be colocated with the base - # variable. - with ops.device("/device:GPU:0"): - # Note that for eager execution, minimize expects a function instead of a - # Tensor. - opt_op = opt.minimize(loss, global_step, [var0, var1]) - self.evaluate(variables.global_variables_initializer()) - self.evaluate(opt_op) - - # Validate updated params, All variables should have decreased. - self.assertTrue(all(v < 0.0 for v in self.evaluate(var0)), - msg="updated variables: %s" % self.evaluate(var0)) - self.assertTrue(all(v < 2.0 for v in self.evaluate(var1)), - msg="updated variables: %s" % self.evaluate(var1)) - - if __name__ == "__main__": test.main() diff --git a/tensorflow/python/keras/optimizer_v2/BUILD b/tensorflow/python/keras/optimizer_v2/BUILD index 7fa755147a..b8f0124941 100644 --- a/tensorflow/python/keras/optimizer_v2/BUILD +++ b/tensorflow/python/keras/optimizer_v2/BUILD @@ -201,7 +201,6 @@ cuda_py_test( srcs = ["rmsprop_test.py"], additional_deps = [ ":optimizer_v2", - "@absl_py//absl/testing:parameterized", "//tensorflow/python/eager:def_function", "//tensorflow/python:client_testlib", "//tensorflow/python:framework", diff --git a/tensorflow/python/keras/optimizer_v2/optimizer_v2.py b/tensorflow/python/keras/optimizer_v2/optimizer_v2.py index 7b784fd9af..d0f16f0b4f 100644 --- a/tensorflow/python/keras/optimizer_v2/optimizer_v2.py +++ b/tensorflow/python/keras/optimizer_v2/optimizer_v2.py @@ -443,12 +443,11 @@ class OptimizerV2(checkpointable.CheckpointableBase): initializer, shape=var.shape, dtype=var.dtype) else: initial_value = initializer - with ops._colocate_with_for_gradient(var, None): # pylint: disable=protected-access - weight = tf_variables.Variable( - name="%s/%s" % (var._shared_name, slot_name), # pylint: disable=protected-access - dtype=var.dtype, - trainable=False, - initial_value=initial_value) + weight = tf_variables.Variable( + name="%s/%s" % (var._shared_name, slot_name), # pylint: disable=protected-access + dtype=var.dtype, + trainable=False, + initial_value=initial_value) backend.track_variable(weight) slot_dict[slot_name] = weight self._restore_slot_variable( diff --git a/tensorflow/python/keras/optimizer_v2/rmsprop_test.py b/tensorflow/python/keras/optimizer_v2/rmsprop_test.py index 1b53f7e579..4d61cfbbc5 100644 --- a/tensorflow/python/keras/optimizer_v2/rmsprop_test.py +++ b/tensorflow/python/keras/optimizer_v2/rmsprop_test.py @@ -22,7 +22,6 @@ import copy import itertools import math -from absl.testing import parameterized import numpy as np from tensorflow.python.eager import context @@ -422,52 +421,5 @@ class RMSpropOptimizerTest(test.TestCase): self.assertEqual(opt_3.lr, 0.1) -class SlotColocationTest(test.TestCase, parameterized.TestCase): - - @parameterized.parameters([True, False]) - @test_util.run_in_graph_and_eager_modes - def testRunMinimizeOnGPUForCPUVariables(self, use_resource): - if not context.context().num_gpus(): - self.skipTest("No GPUs found") - - with ops.device("/device:CPU:0"): - if use_resource: - var0 = resource_variable_ops.ResourceVariable([1.0, 2.0], - dtype=dtypes.float32) - var1 = resource_variable_ops.ResourceVariable([3.0, 4.0], - dtype=dtypes.float32) - else: - var0 = variables.Variable([1.0, 2.0], dtype=dtypes.float32) - var1 = variables.Variable([3.0, 4.0], dtype=dtypes.float32) - - def loss(): - return 5 * var0 + 3 * var1 - - opt = rmsprop.RMSprop( - learning_rate=1.0, decay=0.9, momentum=0.5, epsilon=1.0) - - # Fetch params to validate initial values - self.evaluate(variables.global_variables_initializer()) - self.assertAllClose([1.0, 2.0], self.evaluate(var0)) - self.assertAllClose([3.0, 4.0], self.evaluate(var1)) - - # Run 1 step through optimizer on GPU. - # Slot variables are created the first time optimizer is used on some - # variable. This tests that slot variables will be colocated with the base - # variable. - with ops.device("/device:GPU:0"): - # Note that for eager execution, minimize expects a function instead of a - # Tensor. - opt_op = opt.minimize(loss, [var0, var1]) - self.evaluate(variables.global_variables_initializer()) - self.evaluate(opt_op) - - # Validate updated params, All variables should have decreased. - self.assertTrue(all(v < 0.0 for v in self.evaluate(var0)), - msg="updated variables: %s" % self.evaluate(var0)) - self.assertTrue(all(v < 2.0 for v in self.evaluate(var1)), - msg="updated variables: %s" % self.evaluate(var1)) - - if __name__ == "__main__": test.main() -- GitLab From c220247fe3735944ce2686568196a443f640f6a4 Mon Sep 17 00:00:00 2001 From: Davide Libenzi Date: Wed, 26 Dec 2018 11:12:32 -0800 Subject: [PATCH 103/622] Allow input/output alias information to be populated via the XLA builder. PiperOrigin-RevId: 226935597 --- tensorflow/compiler/xla/client/xla_builder.cc | 34 ++++++++++++++++++- tensorflow/compiler/xla/client/xla_builder.h | 24 +++++++++++++ .../compiler/xla/client/xla_builder_test.cc | 26 ++++++++++++++ 3 files changed, 83 insertions(+), 1 deletion(-) diff --git a/tensorflow/compiler/xla/client/xla_builder.cc b/tensorflow/compiler/xla/client/xla_builder.cc index 7c72cdfeb5..1c6fe0ab5d 100644 --- a/tensorflow/compiler/xla/client/xla_builder.cc +++ b/tensorflow/compiler/xla/client/xla_builder.cc @@ -29,6 +29,7 @@ limitations under the License. #include "tensorflow/compiler/xla/client/sharding_builder.h" #include "tensorflow/compiler/xla/client/xla_computation.h" #include "tensorflow/compiler/xla/execution_options_util.h" +#include "tensorflow/compiler/xla/service/hlo_input_output_alias_config.h" #include "tensorflow/compiler/xla/service/hlo_instruction.h" #include "tensorflow/compiler/xla/service/hlo_opcode.h" #include "tensorflow/compiler/xla/service/shape_inference.h" @@ -310,7 +311,10 @@ StatusOr XlaBuilder::Build(int64 root_id) { module->add_computations()->Swap(&e.second); } module->add_computations()->Swap(&entry); - + if (!input_output_aliases_.empty()) { + TF_RETURN_IF_ERROR( + PopulateInputOutputAlias(module, program_shape, input_output_aliases_)); + } *(module->mutable_dynamic_parameter_binding()) = dynamic_parameter_binding_.ToProto(); @@ -323,6 +327,34 @@ StatusOr XlaBuilder::Build(int64 root_id) { return std::move(computation); } +/* static */ Status XlaBuilder::PopulateInputOutputAlias( + HloModuleProto* module, const ProgramShape& program_shape, + const std::vector& input_output_aliases) { + HloInputOutputAliasConfig config(program_shape.result()); + for (auto& alias : input_output_aliases) { + // The HloInputOutputAliasConfig does not do parameter validation as it only + // carries the result shape. Maybe it should be constructed with a + // ProgramShape to allow full validation. We will still get an error when + // trying to compile the HLO module, but would be better to have validation + // at this stage. + if (alias.param_number >= program_shape.parameters_size()) { + return InvalidArgument("Invalid parameter number %ld (total %ld)", + alias.param_number, + program_shape.parameters_size()); + } + const Shape& parameter_shape = program_shape.parameters(alias.param_number); + if (!ShapeUtil::IndexIsValid(parameter_shape, alias.param_index)) { + return InvalidArgument("Invalid parameter %ld index: %s", + alias.param_number, + alias.param_index.ToString().c_str()); + } + TF_RETURN_IF_ERROR(config.SetUpAlias(alias.output_index, alias.param_number, + alias.param_index)); + } + *module->mutable_input_output_alias() = config.ToProto(); + return Status::OK(); +} + StatusOr XlaBuilder::InDimBroadcast( const Shape& shape, const XlaOp& operand, absl::Span broadcast_dimensions) { diff --git a/tensorflow/compiler/xla/client/xla_builder.h b/tensorflow/compiler/xla/client/xla_builder.h index 6e9b025e5d..68ddf2cdd8 100644 --- a/tensorflow/compiler/xla/client/xla_builder.h +++ b/tensorflow/compiler/xla/client/xla_builder.h @@ -276,7 +276,22 @@ class XlaBuilder { int64 target_param_num, ShapeIndex target_param_index, int64 target_dim_num); + // Adds a new input/output alias. Since the input/ouput shape information are + // not available until the computation is built, and eventual error in the + // arguments of this API will be detected only at computation Build() time. + void SetUpAlias(const ShapeIndex& output_index, int64 param_number, + const ShapeIndex& param_index) { + input_output_aliases_.push_back({output_index, param_number, param_index}); + } + private: + // Describes an input/output alias as inserted by the SetUpAlias() API. + struct InputOutputAlias { + ShapeIndex output_index; + int64 param_number; + ShapeIndex param_index; + }; + // Build helper which takes the id of the root operation.. StatusOr Build(int64 root_id); @@ -730,6 +745,12 @@ class XlaBuilder { int64 GetNextId() { return ++next_id_; } + // Populates the module with the input/output alias information stored within + // the input_output_aliases vector. + static Status PopulateInputOutputAlias( + HloModuleProto* module, const ProgramShape& program_shape, + const std::vector& input_output_aliases); + string name_; // Name to use for the built computation. // The next sequential ID for every instruction/computation contained within @@ -749,6 +770,9 @@ class XlaBuilder { // Dynamic parameter configuration of this computation. DynamicParameterBinding dynamic_parameter_binding_; + // Holds the input/output alias information populated by the SetUpAlias() API. + std::vector input_output_aliases_; + // A map from XlaOp::Handle to the index in the instructions_ vector where the // instruction is held. absl::flat_hash_map handle_to_index_; diff --git a/tensorflow/compiler/xla/client/xla_builder_test.cc b/tensorflow/compiler/xla/client/xla_builder_test.cc index b3f5be300d..ba929a1200 100644 --- a/tensorflow/compiler/xla/client/xla_builder_test.cc +++ b/tensorflow/compiler/xla/client/xla_builder_test.cc @@ -455,5 +455,31 @@ TEST_F(XlaBuilderTest, AfterAllWithNonTokenOperands) { ::testing::HasSubstr("All operands to AfterAll must be tokens")); } +TEST_F(XlaBuilderTest, CheckInputOutputAlias) { + XlaBuilder b(TestName()); + auto p0 = Parameter(&b, 0, ShapeUtil::MakeShape(F32, {8, 4}), "p0"); + auto p1 = Parameter(&b, 1, ShapeUtil::MakeShape(F32, {8, 4}), "p1"); + auto add = Add(p0, p1); + auto sub = Sub(p0, p1); + auto root = Tuple(&b, {add, sub}); + + b.SetUpAlias({1}, 0, {}); + b.SetUpAlias({0}, 1, {}); + + TF_ASSERT_OK_AND_ASSIGN(auto module, BuildHloModule(&b, root)); + + const HloInputOutputAliasConfig& config = module->input_output_alias_config(); + EXPECT_TRUE(config.ParameterHasAlias(0, {})); + EXPECT_TRUE(config.ParameterHasAlias(1, {})); + + auto alias_p0 = config.GetAliasedOutput(0, {}); + ASSERT_TRUE(alias_p0.has_value()); + EXPECT_EQ(*alias_p0, ShapeIndex({1})); + + auto alias_p1 = config.GetAliasedOutput(1, {}); + ASSERT_TRUE(alias_p1.has_value()); + EXPECT_EQ(*alias_p1, ShapeIndex({0})); +} + } // namespace } // namespace xla -- GitLab From 79126ece739c8a97b20a2d399a505ab0a6bd9494 Mon Sep 17 00:00:00 2001 From: Justin Lebar Date: Wed, 26 Dec 2018 11:32:32 -0800 Subject: [PATCH 104/622] [XLA] Avoid fp division by 0 in literal_comparison.cc. Found by running hlo_evaluator_test with ubsan. PiperOrigin-RevId: 226937627 --- tensorflow/compiler/xla/literal_comparison.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tensorflow/compiler/xla/literal_comparison.cc b/tensorflow/compiler/xla/literal_comparison.cc index f4376d66af..258bc966b1 100644 --- a/tensorflow/compiler/xla/literal_comparison.cc +++ b/tensorflow/compiler/xla/literal_comparison.cc @@ -387,7 +387,14 @@ class NearComparator { rel_error = std::numeric_limits::infinity(); } else { abs_error = FpAbsoluteValue(actual - expected); - rel_error = abs_error / FpAbsoluteValue(expected); + + // Avoid division by 0 even though it's well-defined because ubsan can be + // configured to treat this as a fatal error. + if (expected != T{0}) { + rel_error = abs_error / FpAbsoluteValue(expected); + } else { + rel_error = std::numeric_limits::infinity(); + } } const bool is_abs_mismatch = abs_error > error_.abs; const bool is_rel_mismatch = rel_error > error_.rel; -- GitLab From 48f2893491fdc7352590a7d93b9ec317f2850c64 Mon Sep 17 00:00:00 2001 From: Justin Lebar Date: Wed, 26 Dec 2018 12:13:35 -0800 Subject: [PATCH 105/622] Pass -v when invoking FileCheck. FileCheck has learned to output the debugging information we were printing, plus other info. See https://reviews.llvm.org/rL349418. PiperOrigin-RevId: 226941909 --- tensorflow/compiler/xla/tests/filecheck.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tensorflow/compiler/xla/tests/filecheck.cc b/tensorflow/compiler/xla/tests/filecheck.cc index dcb469087e..1b0bebe2d0 100644 --- a/tensorflow/compiler/xla/tests/filecheck.cc +++ b/tensorflow/compiler/xla/tests/filecheck.cc @@ -48,7 +48,7 @@ StatusOr RunFileCheck(const string& input, const string& pattern) { tensorflow::SubProcess file_check_process; file_check_process.SetProgram(file_check_path, - {file_check_path, pattern_path}); + {file_check_path, "-v", pattern_path}); file_check_process.SetChannelAction(tensorflow::CHAN_STDIN, tensorflow::ACTION_PIPE); file_check_process.SetChannelAction(tensorflow::CHAN_STDERR, @@ -71,9 +71,7 @@ StatusOr RunFileCheck(const string& input, const string& pattern) { LOG(WARNING) << "NOTE: FileCheck binary does not exist!"; } - LOG(WARNING) << "FileCheck error: " << standard_error; - LOG(WARNING) << "FileCheck input was:"; - XLA_LOG_LINES(tensorflow::WARNING, input); + LOG(WARNING) << "FileCheck error:\n" << standard_error; LOG(WARNING) << "FileCheck pattern was:"; XLA_LOG_LINES(tensorflow::WARNING, pattern); } else if (!standard_error.empty()) { -- GitLab From 742778f04e0d28495025fc27051d861de67b285a Mon Sep 17 00:00:00 2001 From: Yuefeng Zhou Date: Wed, 26 Dec 2018 12:17:17 -0800 Subject: [PATCH 106/622] Enable collective ops in defun PiperOrigin-RevId: 226942243 --- tensorflow/core/kernels/partitioned_function_ops.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/tensorflow/core/kernels/partitioned_function_ops.cc b/tensorflow/core/kernels/partitioned_function_ops.cc index fbecd909be..96ade7900f 100644 --- a/tensorflow/core/kernels/partitioned_function_ops.cc +++ b/tensorflow/core/kernels/partitioned_function_ops.cc @@ -439,6 +439,7 @@ class PartitionedCallOp : public AsyncOpKernel { opts.step_container = ctx->step_container(); opts.cancellation_manager = ctx->cancellation_manager(); opts.stats_collector = ctx->stats_collector(); + opts.collective_executor = ctx->collective_executor(); // TODO(akshayka): Consider selecting a runner on a per-device basis, i.e., // using device-specific threadpools when available. opts.runner = ctx->runner(); -- GitLab From a1193c29544bebb74ded1933f725e287c0a8a94e Mon Sep 17 00:00:00 2001 From: Rachel Lim Date: Wed, 26 Dec 2018 12:39:23 -0800 Subject: [PATCH 107/622] [tf.data] Meta optimizer to manage tf.data optimization passes to perform optimizations in a meaningful order (instead of an arbitrary order as before). PiperOrigin-RevId: 226944242 --- .../core/grappler/optimizers/data/BUILD | 25 ++++ .../optimizers/data/meta_optimizer.cc | 125 ++++++++++++++++++ .../grappler/optimizers/data/meta_optimizer.h | 56 ++++++++ .../core/kernels/data/optimize_dataset_op.cc | 47 +++---- 4 files changed, 222 insertions(+), 31 deletions(-) create mode 100644 tensorflow/core/grappler/optimizers/data/meta_optimizer.cc create mode 100644 tensorflow/core/grappler/optimizers/data/meta_optimizer.h diff --git a/tensorflow/core/grappler/optimizers/data/BUILD b/tensorflow/core/grappler/optimizers/data/BUILD index 7593023ff4..57b0df3cb2 100644 --- a/tensorflow/core/grappler/optimizers/data/BUILD +++ b/tensorflow/core/grappler/optimizers/data/BUILD @@ -3,6 +3,30 @@ licenses(["notice"]) # Apache 2.0 load("//tensorflow:tensorflow.bzl", "tf_cc_test") load("//tensorflow/core:platform/default/build_config.bzl", "tf_protos_all") +cc_library( + name = "meta_optimizer", + srcs = ["meta_optimizer.cc"], + hdrs = [ + "meta_optimizer.h", + ], + visibility = ["//visibility:public"], + deps = [ + "@com_google_absl//absl/container:flat_hash_map", + "//tensorflow/core/grappler:grappler_item", + "//tensorflow/core:lib", + "//tensorflow/core/grappler/clusters:cluster", + "//tensorflow/core/grappler/optimizers:arithmetic_optimizer", + "//tensorflow/core/grappler/optimizers:model_pruner", + "//tensorflow/core/grappler/optimizers:shape_optimizer", + "//tensorflow/core/grappler/optimizers:dependency_optimizer", + "//tensorflow/core/grappler/optimizers:function_optimizer", + "//tensorflow/core/grappler/optimizers:custom_graph_optimizer", + "//tensorflow/core/grappler/optimizers:custom_graph_optimizer_registry", + "//tensorflow/core:lib_internal", + "//tensorflow/core:ptr_util", + ] + tf_protos_all(), +) + cc_library( name = "filter_fusion", srcs = ["filter_fusion.cc"], @@ -561,6 +585,7 @@ cc_library( ":map_fusion", ":map_parallelization", ":map_vectorization", + ":meta_optimizer", ":noop_elimination", ":shuffle_and_repeat_fusion", ], diff --git a/tensorflow/core/grappler/optimizers/data/meta_optimizer.cc b/tensorflow/core/grappler/optimizers/data/meta_optimizer.cc new file mode 100644 index 0000000000..0fc0cf43c6 --- /dev/null +++ b/tensorflow/core/grappler/optimizers/data/meta_optimizer.cc @@ -0,0 +1,125 @@ +/* Copyright 2018 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/grappler/optimizers/data/meta_optimizer.h" + +#include "tensorflow/core/grappler/clusters/cluster.h" +#include "tensorflow/core/grappler/grappler_item.h" +#include "tensorflow/core/grappler/optimizers/arithmetic_optimizer.h" +#include "tensorflow/core/grappler/optimizers/custom_graph_optimizer_registry.h" +#include "tensorflow/core/grappler/optimizers/dependency_optimizer.h" +#include "tensorflow/core/grappler/optimizers/function_optimizer.h" +#include "tensorflow/core/grappler/optimizers/model_pruner.h" +#include "tensorflow/core/grappler/optimizers/shape_optimizer.h" +#include "tensorflow/core/lib/gtl/map_util.h" +#include "tensorflow/core/util/ptr_util.h" + +namespace tensorflow { +namespace grappler { + +Status TFDataMetaOptimizer::Optimize(Cluster* cluster, const GrapplerItem& item, + GraphDef* output) { + // Stores the optimized item so far. + GrapplerItem optimized_item = item; + + // Perform optimizations in a meaningful order. + for (const auto& optimization : + {"noop_elimination", + "shuffle_and_repeat_fusion", + "map_fusion", + "filter_fusion", + "map_and_filter_fusion", + "hoist_random_uniform", + "map_parallelization", + "map_and_batch_fusion", + "map_vectorization", + "make_numa_aware", + "latency_all_edges", + "make_sloppy", + "pruning", + "function", + "shape", + "arithmetic", + "dependency"}) { + TF_RETURN_IF_ERROR( + ApplyOptimization(optimization, cluster, &optimized_item)); + } + + // Store the final result of all the optimizations in `output`. + output->Swap(&optimized_item.graph); + return Status::OK(); +} + +Status TFDataMetaOptimizer::ApplyOptimization(const string& name, + Cluster* cluster, + GrapplerItem* item) const { + GRAPPLER_RETURN_IF_DEADLINE_EXCEEDED(); + + const auto* optimizer = gtl::FindOrNull(enabled_optimizers_, name); + if (!optimizer) { + return Status::OK(); + } + + GraphDef result; + (*optimizer)->set_deadline_usec(this->deadline_usec()); + TF_RETURN_IF_ERROR((*optimizer)->Optimize(cluster, *item, &result)); + item->graph.Swap(&result); + + return Status::OK(); +} + +Status TFDataMetaOptimizer::Init( + const tensorflow::RewriterConfig_CustomGraphOptimizer* config) { + if (!config) return Status::OK(); + + // Initialize custom tf.data optimizers based on config. + auto& optimizers = config->parameter_map().at("optimizers").list().s(); + for (const auto& optimizer_name : optimizers) { + auto optimizer = + CustomGraphOptimizerRegistry::CreateByNameOrNull(optimizer_name); + if (optimizer) { + // None of our data optimizers implement a meaningful Init function. + // This returns an error in case any of them does. + TF_RETURN_IF_ERROR(optimizer->Init()); + enabled_optimizers_[optimizer_name] = std::move(optimizer); + } else { + // This should never happen. + return errors::Internal( + "Tried to register a dataset optimizer that doesn't exist: ", + optimizer_name); + } + } + + // Initialize standard grappler optimizers. + enabled_optimizers_["pruning"] = MakeUnique(); + enabled_optimizers_["function"] = + MakeUnique(RewriterConfig::ON); + enabled_optimizers_["shape"] = MakeUnique(); + enabled_optimizers_["arithmetic"] = MakeUnique(); + enabled_optimizers_["dependency"] = MakeUnique(); + + return Status::OK(); +} + +void TFDataMetaOptimizer::Feedback(Cluster* cluster, const GrapplerItem& item, + const GraphDef& optimize_output, + double result) { + // no-op +} + +REGISTER_GRAPH_OPTIMIZER_AS(TFDataMetaOptimizer, "tf_data_meta_optimizer"); + +} // end namespace grappler +} // end namespace tensorflow diff --git a/tensorflow/core/grappler/optimizers/data/meta_optimizer.h b/tensorflow/core/grappler/optimizers/data/meta_optimizer.h new file mode 100644 index 0000000000..c39ddda4cb --- /dev/null +++ b/tensorflow/core/grappler/optimizers/data/meta_optimizer.h @@ -0,0 +1,56 @@ +/* Copyright 2018 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_GRAPPLER_OPTIMIZERS_DATA_META_OPTIMIZER_H_ +#define TENSORFLOW_CORE_GRAPPLER_OPTIMIZERS_DATA_META_OPTIMIZER_H_ + +#include "absl/container/flat_hash_map.h" +#include "tensorflow/core/grappler/optimizers/custom_graph_optimizer.h" + +namespace tensorflow { +namespace grappler { + +// This optimizer performs tf.data-specific optimizations by invoking +// other optimizers. +class TFDataMetaOptimizer : public CustomGraphOptimizer { + public: + TFDataMetaOptimizer() = default; + ~TFDataMetaOptimizer() override = default; + + string name() const override { return "tf_data_meta_optimizer"; }; + + Status Init( + const tensorflow::RewriterConfig_CustomGraphOptimizer* config) override; + + Status Optimize(Cluster* cluster, const GrapplerItem& item, + GraphDef* output) override; + + void Feedback(Cluster* cluster, const GrapplerItem& item, + const GraphDef& optimize_output, double result) override; + + private: + absl::flat_hash_map> + enabled_optimizers_; + + // Applies an optimization with the specified name on `item`, and stores + // the result in `item.graph` + Status ApplyOptimization(const string& name, Cluster* cluster, + GrapplerItem* item) const; +}; + +} // end namespace grappler +} // end namespace tensorflow + +#endif // TENSORFLOW_CORE_GRAPPLER_OPTIMIZERS_DATA_META_OPTIMIZER_H_ diff --git a/tensorflow/core/kernels/data/optimize_dataset_op.cc b/tensorflow/core/kernels/data/optimize_dataset_op.cc index 9c50d8050a..04cc48a0be 100644 --- a/tensorflow/core/kernels/data/optimize_dataset_op.cc +++ b/tensorflow/core/kernels/data/optimize_dataset_op.cc @@ -40,6 +40,8 @@ namespace tensorflow { namespace data { namespace { +static const char* const kOptimizerName = "tf_data_meta_optimizer"; + // See documentation in ../../ops/dataset_ops.cc for a high-level // description of the following op. class OptimizeDatasetOp : public UnaryDatasetOpKernel { @@ -286,31 +288,6 @@ class OptimizeDatasetOp : public UnaryDatasetOpKernel { (*meta_graph_def.mutable_collection_def())["train_op"] = collection_def; // Create Grappler item. - tensorflow::ConfigProto config; - RewriterConfig& rewriter_config = - *config.mutable_graph_options()->mutable_rewrite_options(); - for (const string& optimization : optimizations_) { - rewriter_config.add_optimizers(optimization); - } - // If no optimizations were specified, supply a non-existent - // optimization to prevent Grappler from applying the default set of - // optimizations as some of them do not work out of the box at the - // moment (e.g. because we have no cost model for dataset ops). - if (optimizations_.empty()) { - rewriter_config.add_optimizers("non-existent"); - } else { - // If we apply custom dataset optimizers, explicitly trigger a subset of - // standard grappler optimizations to further optimize modified dataset - // graphs (e.g. performing constant folding on merged functions, - // removing unused graph nodes) - // TODO(b/118175421): This should be part of the tf.data optimization - // pass manager. - // TODO(b/120437209): Apply `constfold` optimization when it is fixed. - for (const auto& optimizer : - {"pruning", "function", "shape", "arithmetic", "dependency"}) { - rewriter_config.add_optimizers(optimizer); - } - } tensorflow::grappler::ItemConfig item_config; item_config.apply_optimizations = true; std::unique_ptr grappler_item = @@ -319,13 +296,21 @@ class OptimizeDatasetOp : public UnaryDatasetOpKernel { std::unordered_map device_map; tensorflow::grappler::VirtualCluster cluster(device_map); - // Run optimizer. - if (VLOG_IS_ON(2)) { - LOG(INFO) << "Performing the following optimizations:"; - for (const string& optimization : optimizations_) { - LOG(INFO) << " " << optimization; - } + // Run data optimizer using grappler's meta optimizer. + tensorflow::ConfigProto config; + RewriterConfig& rewriter_config = + *config.mutable_graph_options()->mutable_rewrite_options(); + rewriter_config.add_optimizers(kOptimizerName); + + auto custom_optimizer = rewriter_config.add_custom_optimizers(); + custom_optimizer->set_name(kOptimizerName); + auto* custom_optimizations_list = + (*custom_optimizer->mutable_parameter_map())["optimizers"] + .mutable_list(); + for (const auto& opt : optimizations_) { + custom_optimizations_list->add_s(opt); } + TF_RETURN_IF_ERROR(tensorflow::grappler::RunMetaOptimizer( *grappler_item, config, ctx->device(), &cluster, graph_def)); -- GitLab From 2d6039b2aeb6dfc5a90ec73316bf2bb75acf17e2 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Wed, 26 Dec 2018 20:57:10 +0000 Subject: [PATCH 108/622] Fix warning in losses_utils.py While debugging 24577 I noticed the following warning: ``` WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/utils/losses_utils.py:170: to_float (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version. Instructions for updating: Use tf.cast instead. ``` This fix fixes the warning. Signed-off-by: Yong Tang --- tensorflow/python/keras/utils/losses_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tensorflow/python/keras/utils/losses_utils.py b/tensorflow/python/keras/utils/losses_utils.py index fc4b4ac7df..f270ed1912 100644 --- a/tensorflow/python/keras/utils/losses_utils.py +++ b/tensorflow/python/keras/utils/losses_utils.py @@ -167,8 +167,8 @@ def compute_weighted_loss(losses, losses, None, sample_weight) losses = ops.convert_to_tensor(losses) input_dtype = losses.dtype - losses = math_ops.to_float(losses) - sample_weight = math_ops.to_float(sample_weight) + losses = math_ops.cast(losses, dtypes.float32) + sample_weight = math_ops.cast(sample_weight, dtypes.float32) try: # Broadcast weights if possible. -- GitLab From 16e42649493ffd3f6cc542bd3a16ebf80e9b26f2 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Wed, 26 Dec 2018 21:00:10 +0000 Subject: [PATCH 109/622] Added missing dtypes import Signed-off-by: Yong Tang --- tensorflow/python/keras/utils/losses_utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tensorflow/python/keras/utils/losses_utils.py b/tensorflow/python/keras/utils/losses_utils.py index f270ed1912..357334ffca 100644 --- a/tensorflow/python/keras/utils/losses_utils.py +++ b/tensorflow/python/keras/utils/losses_utils.py @@ -18,6 +18,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.keras import backend as K from tensorflow.python.ops import array_ops -- GitLab From 402da5c870bb7d73af0eeba68a250226e7bdcad2 Mon Sep 17 00:00:00 2001 From: Martin Wicke Date: Wed, 26 Dec 2018 12:59:26 -0800 Subject: [PATCH 110/622] Remove remaining dependencies from core to contrib (in v2 only). This is likely to break things in 2.0, only tests will tell. I fixed some of the things that will definitely break (saved_model stuff), but this definitely removes some kernels that used to be linked into some of the tools (specifically, graph_transforms), which we may have to put back. PiperOrigin-RevId: 226946051 --- tensorflow/core/kernels/BUILD | 10 +- .../core/platform/default/build_config.bzl | 4 +- tensorflow/lite/python/BUILD | 2 - tensorflow/python/debug/BUILD | 4 +- tensorflow/python/tools/BUILD | 16 ++- tensorflow/python/tools/saved_model_cli.py | 9 +- tensorflow/python/tools/saved_model_utils.py | 75 ++++++++++- .../python/tools/saved_model_utils_test.py | 116 ++++++++++++++++++ tensorflow/tools/graph_transforms/BUILD | 6 +- .../tools/pip_package/pip_smoke_test.py | 1 + 10 files changed, 224 insertions(+), 19 deletions(-) create mode 100644 tensorflow/python/tools/saved_model_utils_test.py diff --git a/tensorflow/core/kernels/BUILD b/tensorflow/core/kernels/BUILD index 6fa139e5df..8eed2cd0a8 100644 --- a/tensorflow/core/kernels/BUILD +++ b/tensorflow/core/kernels/BUILD @@ -31,6 +31,7 @@ load( "//tensorflow:tensorflow.bzl", "cc_header_only_library", "if_android", + "if_not_v2", "if_not_windows", "tf_cc_binary", "tf_cc_test", @@ -6877,15 +6878,16 @@ tf_kernel_library( name = "summary_kernels", srcs = ["summary_kernels.cc"], deps = [ - "//tensorflow/contrib/tensorboard/db:schema", - "//tensorflow/contrib/tensorboard/db:summary_db_writer", - "//tensorflow/contrib/tensorboard/db:summary_file_writer", "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:protos_all_cc", "//tensorflow/core:summary_ops_op_lib", "//tensorflow/core/lib/db:sqlite", - ], + ] + if_not_v2([ + "//tensorflow/contrib/tensorboard/db:schema", + "//tensorflow/contrib/tensorboard/db:summary_db_writer", + "//tensorflow/contrib/tensorboard/db:summary_file_writer", + ]), ) tf_kernel_library( diff --git a/tensorflow/core/platform/default/build_config.bzl b/tensorflow/core/platform/default/build_config.bzl index 769e289025..58fd0e2712 100644 --- a/tensorflow/core/platform/default/build_config.bzl +++ b/tensorflow/core/platform/default/build_config.bzl @@ -664,13 +664,14 @@ def tf_additional_cloud_op_deps(): "//tensorflow:linux_s390x": [], "//tensorflow:windows": [], "//tensorflow:no_gcp_support": [], + "//tensorflow:api_version_2": [], "//conditions:default": [ "//tensorflow/contrib/cloud:bigquery_reader_ops_op_lib", "//tensorflow/contrib/cloud:gcs_config_ops_op_lib", ], }) -# TODO(jart, jhseu): Delete when GCP is default on. +# TODO(jhseu): Delete when GCP is default on. def tf_additional_cloud_kernel_deps(): return select({ "//tensorflow:android": [], @@ -678,6 +679,7 @@ def tf_additional_cloud_kernel_deps(): "//tensorflow:linux_s390x": [], "//tensorflow:windows": [], "//tensorflow:no_gcp_support": [], + "//tensorflow:api_version_2": [], "//conditions:default": [ "//tensorflow/contrib/cloud/kernels:bigquery_reader_ops", "//tensorflow/contrib/cloud/kernels:gcs_config_ops", diff --git a/tensorflow/lite/python/BUILD b/tensorflow/lite/python/BUILD index e666812bd2..7bfd1a8996 100644 --- a/tensorflow/lite/python/BUILD +++ b/tensorflow/lite/python/BUILD @@ -115,8 +115,6 @@ py_library( srcs_version = "PY2AND3", visibility = ["//visibility:public"], deps = [ - "//tensorflow/contrib/framework:framework_py", - "//tensorflow/contrib/graph_editor:graph_editor_py", "//tensorflow/core:protos_all_py", "//tensorflow/python:framework", "//tensorflow/python:platform", diff --git a/tensorflow/python/debug/BUILD b/tensorflow/python/debug/BUILD index 1dcdb880f5..27a700f813 100644 --- a/tensorflow/python/debug/BUILD +++ b/tensorflow/python/debug/BUILD @@ -19,6 +19,7 @@ exports_files(["LICENSE"]) load("//tensorflow:tensorflow.bzl", "cuda_py_test") load("//tensorflow:tensorflow.bzl", "py_test") load("//tensorflow:tensorflow.bzl", "py_binary") +load("//tensorflow:tensorflow.bzl", "if_not_v2") load("//tensorflow:tensorflow.bzl", "if_not_windows") py_library( @@ -406,9 +407,10 @@ py_library( ":debug_errors", ":debug_fibonacci", ":debug_keras", + ] + if_not_v2([ ":debug_mnist", ":debug_tflearn_iris", - ], + ]), ) py_binary( diff --git a/tensorflow/python/tools/BUILD b/tensorflow/python/tools/BUILD index 901d6bc335..f1a911eb48 100644 --- a/tensorflow/python/tools/BUILD +++ b/tensorflow/python/tools/BUILD @@ -38,7 +38,20 @@ py_library( name = "saved_model_utils", srcs = ["saved_model_utils.py"], srcs_version = "PY2AND3", - deps = ["//tensorflow/contrib/saved_model:reader"], +) + +py_test( + name = "saved_model_utils_test", + size = "small", + srcs = ["saved_model_utils_test.py"], + srcs_version = "PY2AND3", + tags = ["no_windows"], # TODO: needs investigation on Windows + visibility = ["//visibility:private"], + deps = [ + ":saved_model_utils", + "//tensorflow/python:client_testlib", + "//tensorflow/python/saved_model", + ], ) py_library( @@ -250,7 +263,6 @@ py_binary( srcs_version = "PY2AND3", deps = [ ":saved_model_utils", - "//tensorflow/contrib/saved_model:saved_model_py", "//tensorflow/python", "//tensorflow/python/debug:local_cli_wrapper", ], diff --git a/tensorflow/python/tools/saved_model_cli.py b/tensorflow/python/tools/saved_model_cli.py index afc4e517cd..cdef42e2bf 100644 --- a/tensorflow/python/tools/saved_model_cli.py +++ b/tensorflow/python/tools/saved_model_cli.py @@ -30,9 +30,8 @@ import sys import warnings import numpy as np - from six import integer_types -from tensorflow.contrib.saved_model.python.saved_model import reader + from tensorflow.core.example import example_pb2 from tensorflow.core.framework import types_pb2 from tensorflow.python.client import session @@ -56,7 +55,7 @@ def _show_tag_sets(saved_model_dir): Args: saved_model_dir: Directory containing the SavedModel to inspect. """ - tag_sets = reader.get_saved_model_tag_sets(saved_model_dir) + tag_sets = saved_model_utils.get_saved_model_tag_sets(saved_model_dir) print('The given SavedModel contains the following tag-sets:') for tag_set in sorted(tag_sets): print(', '.join(sorted(tag_set))) @@ -190,7 +189,7 @@ def _show_all(saved_model_dir): Args: saved_model_dir: Directory containing the SavedModel to inspect. """ - tag_sets = reader.get_saved_model_tag_sets(saved_model_dir) + tag_sets = saved_model_utils.get_saved_model_tag_sets(saved_model_dir) for tag_set in sorted(tag_sets): print("\nMetaGraphDef with tag-set: '%s' " "contains the following SignatureDefs:" % ', '.join(tag_set)) @@ -654,7 +653,7 @@ def scan(args): scan_meta_graph_def( saved_model_utils.get_meta_graph_def(args.dir, args.tag_set)) else: - saved_model = reader.read_saved_model(args.dir) + saved_model = saved_model_utils.read_saved_model(args.dir) for meta_graph_def in saved_model.meta_graphs: scan_meta_graph_def(meta_graph_def) diff --git a/tensorflow/python/tools/saved_model_utils.py b/tensorflow/python/tools/saved_model_utils.py index c27d7a2658..17c4b8cb83 100644 --- a/tensorflow/python/tools/saved_model_utils.py +++ b/tensorflow/python/tools/saved_model_utils.py @@ -18,7 +18,78 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -from tensorflow.contrib.saved_model.python.saved_model import reader +import os + +from google.protobuf import message +from google.protobuf import text_format +from tensorflow.core.protobuf import saved_model_pb2 +from tensorflow.python.lib.io import file_io +from tensorflow.python.saved_model import constants +from tensorflow.python.util import compat + + +def read_saved_model(saved_model_dir): + """Reads the savedmodel.pb or savedmodel.pbtxt file containing `SavedModel`. + + Args: + saved_model_dir: Directory containing the SavedModel file. + + Returns: + A `SavedModel` protocol buffer. + + Raises: + IOError: If the file does not exist, or cannot be successfully parsed. + """ + # Build the path to the SavedModel in pbtxt format. + path_to_pbtxt = os.path.join( + compat.as_bytes(saved_model_dir), + compat.as_bytes(constants.SAVED_MODEL_FILENAME_PBTXT)) + # Build the path to the SavedModel in pb format. + path_to_pb = os.path.join( + compat.as_bytes(saved_model_dir), + compat.as_bytes(constants.SAVED_MODEL_FILENAME_PB)) + + # Ensure that the SavedModel exists at either path. + if not file_io.file_exists(path_to_pbtxt) and not file_io.file_exists( + path_to_pb): + raise IOError("SavedModel file does not exist at: %s" % saved_model_dir) + + # Parse the SavedModel protocol buffer. + saved_model = saved_model_pb2.SavedModel() + if file_io.file_exists(path_to_pb): + try: + file_content = file_io.FileIO(path_to_pb, "rb").read() + saved_model.ParseFromString(file_content) + return saved_model + except message.DecodeError as e: + raise IOError("Cannot parse file %s: %s." % (path_to_pb, str(e))) + elif file_io.file_exists(path_to_pbtxt): + try: + file_content = file_io.FileIO(path_to_pbtxt, "rb").read() + text_format.Merge(file_content.decode("utf-8"), saved_model) + return saved_model + except text_format.ParseError as e: + raise IOError("Cannot parse file %s: %s." % (path_to_pbtxt, str(e))) + else: + raise IOError("SavedModel file does not exist at: %s/{%s|%s}" % + (saved_model_dir, constants.SAVED_MODEL_FILENAME_PBTXT, + constants.SAVED_MODEL_FILENAME_PB)) + + +def get_saved_model_tag_sets(saved_model_dir): + """Retrieves all the tag-sets available in the SavedModel. + + Args: + saved_model_dir: Directory containing the SavedModel. + + Returns: + String representation of all tag-sets in the SavedModel. + """ + saved_model = read_saved_model(saved_model_dir) + all_tags = [] + for meta_graph_def in saved_model.meta_graphs: + all_tags.append(list(meta_graph_def.meta_info_def.tags)) + return all_tags def get_meta_graph_def(saved_model_dir, tag_set): @@ -39,7 +110,7 @@ def get_meta_graph_def(saved_model_dir, tag_set): Returns: A MetaGraphDef corresponding to the tag-set. """ - saved_model = reader.read_saved_model(saved_model_dir) + saved_model = read_saved_model(saved_model_dir) set_of_tags = set(tag_set.split(',')) for meta_graph_def in saved_model.meta_graphs: if set(meta_graph_def.meta_info_def.tags) == set_of_tags: diff --git a/tensorflow/python/tools/saved_model_utils_test.py b/tensorflow/python/tools/saved_model_utils_test.py new file mode 100644 index 0000000000..5512dea1f7 --- /dev/null +++ b/tensorflow/python/tools/saved_model_utils_test.py @@ -0,0 +1,116 @@ +# 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 SavedModel utils.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os + +from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util +from tensorflow.python.lib.io import file_io +from tensorflow.python.ops import variables +from tensorflow.python.platform import test +from tensorflow.python.saved_model import builder as saved_model_builder +from tensorflow.python.saved_model import tag_constants +from tensorflow.python.tools import saved_model_utils + + +def tearDownModule(): + file_io.delete_recursively(test.get_temp_dir()) + + +class SavedModelUtilTest(test.TestCase): + + def _init_and_validate_variable(self, sess, variable_name, variable_value): + v = variables.Variable(variable_value, name=variable_name) + sess.run(variables.global_variables_initializer()) + self.assertEqual(variable_value, v.eval()) + + @test_util.deprecated_graph_mode_only + def testReadSavedModelValid(self): + saved_model_dir = os.path.join(test.get_temp_dir(), "valid_saved_model") + builder = saved_model_builder.SavedModelBuilder(saved_model_dir) + with self.session(graph=ops.Graph()) as sess: + self._init_and_validate_variable(sess, "v", 42) + builder.add_meta_graph_and_variables(sess, [tag_constants.TRAINING]) + builder.save() + + actual_saved_model_pb = saved_model_utils.read_saved_model(saved_model_dir) + self.assertEqual(len(actual_saved_model_pb.meta_graphs), 1) + self.assertEqual( + len(actual_saved_model_pb.meta_graphs[0].meta_info_def.tags), 1) + self.assertEqual(actual_saved_model_pb.meta_graphs[0].meta_info_def.tags[0], + tag_constants.TRAINING) + + def testReadSavedModelInvalid(self): + saved_model_dir = os.path.join(test.get_temp_dir(), "invalid_saved_model") + with self.assertRaisesRegexp( + IOError, "SavedModel file does not exist at: %s" % saved_model_dir): + saved_model_utils.read_saved_model(saved_model_dir) + + @test_util.deprecated_graph_mode_only + def testGetSavedModelTagSets(self): + saved_model_dir = os.path.join(test.get_temp_dir(), "test_tags") + builder = saved_model_builder.SavedModelBuilder(saved_model_dir) + + # Graph with a single variable. SavedModel invoked to: + # - add with weights. + # - a single tag (from predefined constants). + with self.session(graph=ops.Graph()) as sess: + self._init_and_validate_variable(sess, "v", 42) + builder.add_meta_graph_and_variables(sess, [tag_constants.TRAINING]) + + # Graph that updates the single variable. SavedModel invoked to: + # - simply add the model (weights are not updated). + # - a single tag (from predefined constants). + with self.session(graph=ops.Graph()) as sess: + self._init_and_validate_variable(sess, "v", 43) + builder.add_meta_graph([tag_constants.SERVING]) + + # Graph that updates the single variable. SavedModel is invoked: + # - to add the model (weights are not updated). + # - multiple predefined tags. + with self.session(graph=ops.Graph()) as sess: + self._init_and_validate_variable(sess, "v", 44) + builder.add_meta_graph([tag_constants.SERVING, tag_constants.GPU]) + + # Graph that updates the single variable. SavedModel is invoked: + # - to add the model (weights are not updated). + # - multiple predefined tags for serving on TPU. + with self.session(graph=ops.Graph()) as sess: + self._init_and_validate_variable(sess, "v", 44) + builder.add_meta_graph([tag_constants.SERVING, tag_constants.TPU]) + + # Graph that updates the single variable. SavedModel is invoked: + # - to add the model (weights are not updated). + # - multiple custom tags. + with self.session(graph=ops.Graph()) as sess: + self._init_and_validate_variable(sess, "v", 45) + builder.add_meta_graph(["foo", "bar"]) + + # Save the SavedModel to disk. + builder.save() + + actual_tags = saved_model_utils.get_saved_model_tag_sets(saved_model_dir) + expected_tags = [["train"], ["serve"], ["serve", "gpu"], ["serve", "tpu"], + ["foo", "bar"]] + self.assertEqual(expected_tags, actual_tags) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/tools/graph_transforms/BUILD b/tensorflow/tools/graph_transforms/BUILD index eb1ed1f2ca..f229099e49 100644 --- a/tensorflow/tools/graph_transforms/BUILD +++ b/tensorflow/tools/graph_transforms/BUILD @@ -12,6 +12,7 @@ load( "tf_cc_binary", "tf_cc_test", "tf_py_test", + "if_not_v2", ) exports_files(["LICENSE"]) @@ -131,12 +132,13 @@ cc_library( "//tensorflow/core:lib", "//tensorflow/core:protos_all_cc", "//tensorflow/core:tensorflow", - "//tensorflow/contrib/rnn:gru_ops_op_lib", - "//tensorflow/contrib/rnn:lstm_ops_op_lib", "//tensorflow/core/kernels:quantization_utils", ] + if_not_windows([ "//tensorflow/core/kernels:remote_fused_graph_rewriter_transform", "//tensorflow/core/kernels/hexagon:hexagon_rewriter_transform", + ]) + if_not_v2([ + "//tensorflow/contrib/rnn:gru_ops_op_lib", + "//tensorflow/contrib/rnn:lstm_ops_op_lib", ]), alwayslink = 1, ) diff --git a/tensorflow/tools/pip_package/pip_smoke_test.py b/tensorflow/tools/pip_package/pip_smoke_test.py index ff821b8643..51d010c9e1 100644 --- a/tensorflow/tools/pip_package/pip_smoke_test.py +++ b/tensorflow/tools/pip_package/pip_smoke_test.py @@ -102,6 +102,7 @@ BLACKLIST = [ "//tensorflow/contrib/framework:checkpoint_ops_testdata", "//tensorflow/contrib/bayesflow:reinforce_simple_example", "//tensorflow/contrib/bayesflow:examples/reinforce_simple/reinforce_simple_example.py", # pylint:disable=line-too-long + "//tensorflow/contrib/saved_model:reader", # Not present in v2 "//tensorflow/contrib/timeseries/examples:predict", "//tensorflow/contrib/timeseries/examples:multivariate", "//tensorflow/contrib/timeseries/examples:known_anomaly", -- GitLab From 98bbee7afec8e197bbe56f5ba2804a6ea75d3317 Mon Sep 17 00:00:00 2001 From: Yanan Cao Date: Wed, 26 Dec 2018 13:08:28 -0800 Subject: [PATCH 111/622] Support nested structure as input to xla.compile() and tpu.rewrite() computation. PiperOrigin-RevId: 226947103 --- tensorflow/contrib/compiler/xla.py | 26 +++++++------- tensorflow/contrib/tpu/python/tpu/tpu.py | 44 ++++++++++++++++-------- 2 files changed, 43 insertions(+), 27 deletions(-) diff --git a/tensorflow/contrib/compiler/xla.py b/tensorflow/contrib/compiler/xla.py index f867cd15b6..4e07aa511b 100644 --- a/tensorflow/contrib/compiler/xla.py +++ b/tensorflow/contrib/compiler/xla.py @@ -34,6 +34,7 @@ from tensorflow.python.ops import variable_scope from tensorflow.python.platform import tf_logging as logging from tensorflow.python.util import compat from tensorflow.python.util import function_utils +from tensorflow.python.util import nest from tensorflow.python.util import tf_decorator from tensorflow.python.util import tf_inspect @@ -76,7 +77,9 @@ def compile(computation, inputs=None): # pylint: disable=redefined-builtin All `Operation`s returned from `computation` will be executed when evaluating any of the returned output tensors. - inputs: A list of input tensors or `None` (equivalent to an empty list). + inputs: A list of inputs or `None` (equivalent to an empty list). Each input + can be a nested structure containing values that are convertible to + tensors. Returns: A list of output tensors. @@ -260,17 +263,10 @@ def _compile_internal(computation, inputs=None): if not isinstance(inputs, collections.Sequence): raise TypeError('inputs must be a list') + # Flatten inputs. + flat_inputs = nest.flatten(inputs) # Converts inputs to Tensors. - inputs = [ops.convert_to_tensor(x) for x in inputs] - input_arity = len(inputs) - - arg_error = check_function_argument_count( - computation, input_arity, infeed_queue=None) - if arg_error is not None: - raise TypeError( - 'Supplied computation cannot be called with the specified inputs. You ' - 'specified %d inputs: %s, but the computation needs %s' % - (input_arity, str([i.name for i in inputs]), arg_error)) + flat_inputs = [ops.convert_to_tensor(x) for x in flat_inputs] cluster_name = ops.get_default_graph().unique_name('cluster') pivot = control_flow_ops.no_op(name=cluster_name + '/pivot') @@ -280,11 +276,15 @@ def _compile_internal(computation, inputs=None): # Add identity ops so even unused inputs are 'consumed' by the # computation. - computation_inputs = [ + flat_inputs = [ array_ops.identity(x, name='input_{}'.format(i)) - for i, x in enumerate(inputs) + for i, x in enumerate(flat_inputs) ] + # Re-pack flat_inputs in same structure as 'inputs'. + computation_inputs = nest.pack_sequence_as( + structure=inputs, flat_sequence=flat_inputs) + # Only resource variables work inside an XLA computation, so turn on # resource variables for the computation. vscope = variable_scope.get_variable_scope() diff --git a/tensorflow/contrib/tpu/python/tpu/tpu.py b/tensorflow/contrib/tpu/python/tpu/tpu.py index 9266d81cf5..53e5539ee6 100644 --- a/tensorflow/contrib/tpu/python/tpu/tpu.py +++ b/tensorflow/contrib/tpu/python/tpu/tpu.py @@ -36,6 +36,7 @@ from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import variable_scope from tensorflow.python.platform import tf_logging as logging from tensorflow.python.util import compat +from tensorflow.python.util import nest # Operations that indicate some error in the users graph, e.g. a placeholder @@ -487,7 +488,8 @@ def replicate(computation, computation: A Python function that builds the computation to replicate. inputs: A list of lists of input tensors or `None` (equivalent to `[[]]`), indexed by `[replica_num][input_num]`. All replicas must - have the same number of inputs. + have the same number of inputs. Each input can be a nested structure + containing values that are convertible to tensors. infeed_queue: If not `None`, the `InfeedQueue` from which to append a tuple of arguments as inputs to computation. device_assignment: If not `None`, a `DeviceAssignment` describing the @@ -526,7 +528,8 @@ def split_compile_and_replicate(computation, computation: A Python function that builds the computation to replicate. inputs: A list of lists of input tensors or `None` (equivalent to `[[]]`), indexed by `[replica_num][input_num]`. All replicas must - have the same number of inputs. + have the same number of inputs. Each input can be a nested structure + containing values that are convertible to tensors. infeed_queue: If not `None`, the `InfeedQueue` from which to append a tuple of arguments as inputs to computation. device_assignment: If not `None`, a `DeviceAssignment` describing the @@ -580,24 +583,32 @@ def split_compile_and_replicate(computation, if num_replicas == 0: return [] + # Checks all replicas have the same structure. + for i in xrange(1, num_replicas): + nest.assert_same_structure(inputs[0], inputs[i]) + + # Flatten inputs. + flat_inputs = [ + nest.flatten(per_replica_input) for per_replica_input in inputs + ] # Converts inputs to Tensors. - inputs = [[ops.convert_to_tensor(x) for x in inp] for inp in inputs] + flat_inputs = [[ops.convert_to_tensor(x) for x in inp] for inp in flat_inputs] # Verifies that all replicas have matching numbers and types of inputs - input_types = [x.dtype for x in inputs[0]] - input_arity = len(input_types) + flat_input_types = [x.dtype for x in flat_inputs[0]] + input_arity = len(inputs[0]) + flat_input_arity = len(flat_input_types) for i in range(num_replicas): if len(inputs[i]) != input_arity: raise ValueError("Replicas must have the same number of inputs. " "Replica 0 had {} inputs, replica {} had {} " "inputs.".format(input_arity, i, len(inputs[i]))) - types = [x.dtype for x in inputs[i]] - if types != input_types: - raise ValueError( - "Replicas must have matching input types. Replica 0 had " - "input types {}, replica {} had input types {}".format( - input_types, i, types)) + types = [x.dtype for x in flat_inputs[i]] + if types != flat_input_types: + raise ValueError("Replicas must have matching input types. Replica 0 had " + "input types {}, replica {} had input types {}".format( + flat_input_types, i, types)) arg_error = xla.check_function_argument_count( computation, input_arity, infeed_queue) @@ -620,8 +631,8 @@ def split_compile_and_replicate(computation, # Fan-in: Builds a TPUReplicatedInput node for each input. computation_inputs = [] - for i in range(0, input_arity): - replicas = [inputs[replica][i] for replica in xrange(num_replicas)] + for i in range(0, flat_input_arity): + replicas = [flat_inputs[replica][i] for replica in xrange(num_replicas)] computation_inputs.append( tpu_ops.tpu_replicated_input(replicas, name="input{}".format(i))) @@ -651,6 +662,10 @@ def split_compile_and_replicate(computation, i.op._set_attr("_tpu_input_identity", attr_value_pb2.AttrValue(b=True)) # pylint: enable=protected-access + # Unflatten the computation inputs to match original input structure. + computation_inputs = nest.pack_sequence_as( + structure=inputs[0], flat_sequence=computation_inputs) + # If there is an infeed queue, adds the dequeued values to the # computation's inputs. if infeed_queue is not None: @@ -1093,7 +1108,8 @@ def rewrite(computation, evaluating any of the returned output tensors, not just the ones returned. inputs: A list of input tensors or `None` (equivalent to an empty list). infeed_queue: If not `None`, the `InfeedQueue` from which to append a tuple - of arguments as inputs to `computation`. + of arguments as inputs to `computation`. Each input can be a nested + structure containing values that are convertible to tensors. device_assignment: if not `None`, a `DeviceAssignment` describing the mapping between logical cores in the computation with physical cores in the TPU topology. May be omitted for a single-core computation, in which -- GitLab From 83cb1f1c5ebf47793ad594f0ed368f9c1cc9e28d Mon Sep 17 00:00:00 2001 From: Alexandre Passos Date: Wed, 26 Dec 2018 13:12:33 -0800 Subject: [PATCH 112/622] disable_eager_execution actually disables eager execution This makes it somewhat less safe to call (as there can be eager tensorso out there which will mess up graph building) but makes it do what it says on the label. PiperOrigin-RevId: 226947417 --- tensorflow/python/compat/BUILD | 11 ++++++ .../python/compat/disable_v2_behavior_test.py | 39 +++++++++++++++++++ tensorflow/python/framework/ops.py | 3 ++ 3 files changed, 53 insertions(+) create mode 100644 tensorflow/python/compat/disable_v2_behavior_test.py diff --git a/tensorflow/python/compat/BUILD b/tensorflow/python/compat/BUILD index 9f2ce8c676..f9a57b9c05 100644 --- a/tensorflow/python/compat/BUILD +++ b/tensorflow/python/compat/BUILD @@ -24,3 +24,14 @@ tf_py_test( "//tensorflow/python:client_testlib", ], ) + +tf_py_test( + name = "disable_v2_behavior_test", + size = "small", + srcs = ["disable_v2_behavior_test.py"], + additional_deps = [ + ":compat", + "//tensorflow/python:framework", + "//tensorflow/python:client_testlib", + ], +) diff --git a/tensorflow/python/compat/disable_v2_behavior_test.py b/tensorflow/python/compat/disable_v2_behavior_test.py new file mode 100644 index 0000000000..221691b483 --- /dev/null +++ b/tensorflow/python/compat/disable_v2_behavior_test.py @@ -0,0 +1,39 @@ +# Copyright 2018 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 forward and backwards compatibility utilties.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.compat import compat +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import ops +from tensorflow.python.platform import test + + +class DisableV2BehaviorTest(test.TestCase): + + def test_basic(self): + t = constant_op.constant([1, 2, 3]) # creates a hidden context + self.assertTrue(isinstance(t, ops.EagerTensor)) + compat.disable_v2_behavior() + t = constant_op.constant([1, 2, 3]) + self.assertFalse(isinstance(t, ops.EagerTensor)) + + +if __name__ == '__main__': + compat.enable_v2_behavior() + test.main() diff --git a/tensorflow/python/framework/ops.py b/tensorflow/python/framework/ops.py index e7a9af4866..5dc8e418a2 100644 --- a/tensorflow/python/framework/ops.py +++ b/tensorflow/python/framework/ops.py @@ -5474,6 +5474,9 @@ def disable_eager_execution(): projects from TensorFlow 1.x to 2.x. """ context.default_execution_mode = context.GRAPH_MODE + c = context.context_safe() + if c is not None: + c._eager_context.is_eager = False # pylint: disable=protected-access def enable_eager_execution_internal(config=None, -- GitLab From 6d92ee85a804e3b6236806082f4f5cb85e2c8a1f Mon Sep 17 00:00:00 2001 From: Pete Warden Date: Wed, 26 Dec 2018 13:28:29 -0800 Subject: [PATCH 113/622] Integrate speech commands results over time to give more accurate predictions PiperOrigin-RevId: 226948751 --- .../micro/examples/micro_speech/BUILD | 31 ++- .../micro/examples/micro_speech/Makefile.inc | 24 +- .../examples/micro_speech/feature_provider.cc | 26 +-- .../examples/micro_speech/feature_provider.h | 4 + .../micro_speech/feature_provider_test.cc | 3 +- .../micro/examples/micro_speech/main.cc | 23 +- .../micro_speech/recognize_commands.cc | 139 ++++++++++++ .../micro_speech/recognize_commands.h | 158 +++++++++++++ .../micro_speech/recognize_commands_test.cc | 207 ++++++++++++++++++ .../lite/experimental/micro/kernels/BUILD | 19 -- .../micro/kernels/depthwise_conv_test.cc | 2 +- .../micro/kernels/fully_connected_test.cc | 2 +- .../micro/kernels/softmax_test.cc | 2 +- .../lite/experimental/micro/testing/BUILD | 2 + .../micro/{kernels => testing}/test_utils.h | 22 +- 15 files changed, 612 insertions(+), 52 deletions(-) create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/recognize_commands.cc create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/recognize_commands.h create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/recognize_commands_test.cc rename tensorflow/lite/experimental/micro/{kernels => testing}/test_utils.h (91%) diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/BUILD b/tensorflow/lite/experimental/micro/examples/micro_speech/BUILD index 799b2e5a5d..70eeac1458 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/BUILD +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/BUILD @@ -176,7 +176,6 @@ cc_library( ":audio_provider", ":model_settings", ":preprocessor_reference", - ":timer", "//tensorflow/lite/c:c_api_internal", "//tensorflow/lite/experimental/micro:micro_framework", ], @@ -191,7 +190,6 @@ tflite_micro_cc_test( ":audio_provider", ":feature_provider", ":model_settings", - ":timer", "//tensorflow/lite/c:c_api_internal", "//tensorflow/lite/experimental/micro:micro_framework", "//tensorflow/lite/experimental/micro/testing:micro_test", @@ -221,6 +219,34 @@ tflite_micro_cc_test( ], ) +cc_library( + name = "recognize_commands", + srcs = [ + "recognize_commands.cc", + ], + hdrs = [ + "recognize_commands.h", + ], + deps = [ + ":model_settings", + "//tensorflow/lite/c:c_api_internal", + "//tensorflow/lite/experimental/micro:micro_framework", + ], +) + +tflite_micro_cc_test( + name = "recognize_commands_test", + srcs = [ + "recognize_commands_test.cc", + ], + deps = [ + ":recognize_commands", + "//tensorflow/lite/c:c_api_internal", + "//tensorflow/lite/experimental/micro:micro_framework", + "//tensorflow/lite/experimental/micro/testing:micro_test", + ], +) + cc_binary( name = "micro_speech", srcs = [ @@ -232,6 +258,7 @@ cc_binary( ":features_test_data", ":model_settings", ":preprocessor_reference", + ":recognize_commands", ":timer", ":tiny_conv_model_data", "//tensorflow/lite:schema_fbs_version", diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/Makefile.inc b/tensorflow/lite/experimental/micro/examples/micro_speech/Makefile.inc index 0e42329cad..cce6ea8402 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/Makefile.inc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/Makefile.inc @@ -91,7 +91,6 @@ FEATURE_PROVIDER_TEST_SRCS := \ tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider_test.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.cc \ -tensorflow/lite/experimental/micro/examples/micro_speech/timer.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider.cc ALL_SRCS += $(FEATURE_PROVIDER_TEST_SRCS) @@ -128,6 +127,26 @@ timer_test_bin: $(TIMER_TEST_BINARY).bin test_timer: $(TIMER_TEST_BINARY) $(TEST_SCRIPT) $(TIMER_TEST_BINARY) '~~~ALL TESTS PASSED~~~' +# Tests the feature provider module. +RECOGNIZE_COMMANDS_TEST_SRCS := \ +tensorflow/lite/experimental/micro/examples/micro_speech/recognize_commands_test.cc \ +tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.cc \ +tensorflow/lite/experimental/micro/examples/micro_speech/recognize_commands.cc +ALL_SRCS += $(RECOGNIZE_COMMANDS_TEST_SRCS) +RECOGNIZE_COMMANDS_TEST_OBJS := $(addprefix $(OBJDIR), \ +$(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(RECOGNIZE_COMMANDS_TEST_SRCS)))) +RECOGNIZE_COMMANDS_TEST_BINARY := $(BINDIR)recognize_commands_test +ALL_BINARIES += $(RECOGNIZE_COMMANDS_TEST_BINARY) +$(RECOGNIZE_COMMANDS_TEST_BINARY): $(RECOGNIZE_COMMANDS_TEST_OBJS) $(MICROLITE_LIB_PATH) + @mkdir -p $(dir $@) + $(CXX) $(CXXFLAGS) $(INCLUDES) \ + -o $(RECOGNIZE_COMMANDS_TEST_BINARY) $(RECOGNIZE_COMMANDS_TEST_OBJS) \ + $(LIBFLAGS) $(MICROLITE_LIB_PATH) $(LDFLAGS) $(MICROLITE_LIBS) +recognize_commands_test: $(RECOGNIZE_COMMANDS_TEST_BINARY) +recognize_commands_test_bin: $(RECOGNIZE_COMMANDS_TEST_BINARY).bin +test_recognize_commands: $(RECOGNIZE_COMMANDS_TEST_BINARY) + $(TEST_SCRIPT) $(RECOGNIZE_COMMANDS_TEST_BINARY) '~~~ALL TESTS PASSED~~~' + # Builds a standalone speech command recognizer binary. MICRO_SPEECH_SRCS := \ tensorflow/lite/experimental/micro/examples/micro_speech/main.cc \ @@ -138,7 +157,8 @@ tensorflow/lite/experimental/micro/examples/micro_speech/timer.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/no_features_data.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/yes_features_data.cc \ -tensorflow/lite/experimental/micro/examples/micro_speech/tiny_conv_model_data.cc +tensorflow/lite/experimental/micro/examples/micro_speech/tiny_conv_model_data.cc \ +tensorflow/lite/experimental/micro/examples/micro_speech/recognize_commands.cc ALL_SRCS += $(MICRO_SPEECH_SRCS) MICRO_SPEECH_OBJS := $(addprefix $(OBJDIR), \ $(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(MICRO_SPEECH_SRCS)))) diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider.cc index c4c52ac0ff..7f9ece41dd 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider.cc @@ -18,20 +18,11 @@ limitations under the License. #include "tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.h" #include "tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h" #include "tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.h" -#include "tensorflow/lite/experimental/micro/examples/micro_speech/timer.h" - -namespace { -// Stores the timestamp for the previous fetch of audio data, so that we can -// avoid recalculating all the features from scratch if some earlier timeslices -// are still present. -int32_t g_last_time_in_ms = 0; -// Make sure we don't try to use cached information if this is the first call -// into the provider. -bool g_is_first_run = true; -} // namespace FeatureProvider::FeatureProvider(int feature_size, uint8_t* feature_data) - : feature_size_(feature_size), feature_data_(feature_data) { + : feature_size_(feature_size), + feature_data_(feature_data), + is_first_run_(true) { // Initialize the feature data to default values. for (int n = 0; n < feature_size_; ++n) { feature_data_[n] = 0; @@ -41,24 +32,23 @@ FeatureProvider::FeatureProvider(int feature_size, uint8_t* feature_data) FeatureProvider::~FeatureProvider() {} TfLiteStatus FeatureProvider::PopulateFeatureData( - tflite::ErrorReporter* error_reporter, int* how_many_new_slices) { + tflite::ErrorReporter* error_reporter, int32_t last_time_in_ms, + int32_t time_in_ms, int* how_many_new_slices) { if (feature_size_ != kFeatureElementCount) { error_reporter->Report("Requested feature_data_ size %d doesn't match %d", feature_size_, kFeatureElementCount); return kTfLiteError; } - const int32_t time_in_ms = TimeInMilliseconds(); // Quantize the time into steps as long as each window stride, so we can // figure out which audio data we need to fetch. - const int last_step = (g_last_time_in_ms / kFeatureSliceStrideMs); + const int last_step = (last_time_in_ms / kFeatureSliceStrideMs); const int current_step = (time_in_ms / kFeatureSliceStrideMs); - g_last_time_in_ms = time_in_ms; int slices_needed = current_step - last_step; // If this is the first call, make sure we don't use any cached information. - if (g_is_first_run) { - g_is_first_run = false; + if (is_first_run_) { + is_first_run_ = false; slices_needed = kFeatureSliceCount; } if (slices_needed > kFeatureSliceCount) { diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider.h b/tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider.h index a86c56ebf0..ee3a480e94 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider.h +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider.h @@ -38,11 +38,15 @@ class FeatureProvider { // Fills the feature data with information from audio inputs, and returns how // many feature slices were updated. TfLiteStatus PopulateFeatureData(tflite::ErrorReporter* error_reporter, + int32_t last_time_in_ms, int32_t time_in_ms, int* how_many_new_slices); private: int feature_size_; uint8_t* feature_data_; + // Make sure we don't try to use cached information if this is the first call + // into the provider. + bool is_first_run_; }; #endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_FEATURE_PROVIDER_H_ diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider_test.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider_test.cc index 1e52aec8d2..556cbfe799 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider_test.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider_test.cc @@ -30,7 +30,8 @@ TF_LITE_MICRO_TEST(TestFeatureProvider) { int how_many_new_slices = 0; TfLiteStatus populate_status = feature_provider.PopulateFeatureData( - error_reporter, &how_many_new_slices); + error_reporter, /* last_time_in_ms= */ 0, /* time_in_ms= */ 10000, + &how_many_new_slices); TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, populate_status); TF_LITE_MICRO_EXPECT_EQ(kFeatureSliceCount, how_many_new_slices); } diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/main.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/main.cc index 1890c25cf2..515f82fcbc 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/main.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/main.cc @@ -15,6 +15,8 @@ limitations under the License. #include "tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider.h" #include "tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/recognize_commands.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/timer.h" #include "tensorflow/lite/experimental/micro/examples/micro_speech/tiny_conv_model_data.h" #include "tensorflow/lite/experimental/micro/kernels/all_ops_resolver.h" #include "tensorflow/lite/experimental/micro/micro_error_reporter.h" @@ -68,16 +70,21 @@ int main(int argc, char* argv[]) { FeatureProvider feature_provider(kFeatureElementCount, model_input->data.uint8); + RecognizeCommands recognizer(error_reporter); + + int32_t previous_time = 0; // Keep reading and analysing audio data in an infinite loop. while (true) { // Fetch the spectrogram for the current time. + const int32_t current_time = TimeInMilliseconds(); int how_many_new_slices = 0; TfLiteStatus feature_status = feature_provider.PopulateFeatureData( - error_reporter, &how_many_new_slices); + error_reporter, previous_time, current_time, &how_many_new_slices); if (feature_status != kTfLiteOk) { error_reporter->Report("Feature generation failed"); return 1; } + previous_time = current_time; // If no new audio samples have been received since last time, don't bother // running the network model. if (how_many_new_slices == 0) { @@ -105,7 +112,19 @@ int main(int argc, char* argv[]) { } } - error_reporter->Report("Heard %s", kCategoryLabels[top_category_index]); + const char* found_command = nullptr; + uint8_t score = 0; + bool is_new_command = false; + TfLiteStatus process_status = recognizer.ProcessLatestResults( + output, current_time, &found_command, &score, &is_new_command); + if (process_status != kTfLiteOk) { + error_reporter->Report( + "RecognizeCommands::ProcessLatestResults() failed"); + return 1; + } + if (is_new_command) { + error_reporter->Report("Heard %s (%d)", found_command, score); + } } return 0; diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/recognize_commands.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/recognize_commands.cc new file mode 100644 index 0000000000..9366dc71e0 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/recognize_commands.cc @@ -0,0 +1,139 @@ +/* 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/lite/experimental/micro/examples/micro_speech/recognize_commands.h" + +#include + +RecognizeCommands::RecognizeCommands(tflite::ErrorReporter* error_reporter, + int32_t average_window_duration_ms, + uint8_t detection_threshold, + int32_t suppression_ms, + int32_t minimum_count) + : error_reporter_(error_reporter), + average_window_duration_ms_(average_window_duration_ms), + detection_threshold_(detection_threshold), + suppression_ms_(suppression_ms), + minimum_count_(minimum_count), + previous_results_(error_reporter) { + previous_top_label_ = "_silence_"; + previous_top_label_time_ = 0; +} + +TfLiteStatus RecognizeCommands::ProcessLatestResults( + const TfLiteTensor* latest_results, const int32_t current_time_ms, + const char** found_command, uint8_t* score, bool* is_new_command) { + if ((latest_results->dims->size != 2) || + (latest_results->dims->data[0] != 1) || + (latest_results->dims->data[1] != kCategoryCount)) { + error_reporter_->Report( + "The results for recognition should contain %d elements, but there are " + "%d in an %d-dimensional shape", + kCategoryCount, latest_results->dims->data[1], + latest_results->dims->size); + return kTfLiteError; + } + + if (latest_results->type != kTfLiteUInt8) { + error_reporter_->Report( + "The results for recognition should be uint8 elements, but are %d", + latest_results->type); + return kTfLiteError; + } + + if ((!previous_results_.empty()) && + (current_time_ms < previous_results_.front().time_)) { + error_reporter_->Report( + "Results must be fed in increasing time order, but received a " + "timestamp of %d that was earlier than the previous one of %d", + current_time_ms, previous_results_.front().time_); + return kTfLiteError; + } + + // Add the latest results to the head of the queue. + previous_results_.push_back({current_time_ms, latest_results->data.uint8}); + + // Prune any earlier results that are too old for the averaging window. + const int64_t time_limit = current_time_ms - average_window_duration_ms_; + while ((!previous_results_.empty()) && + previous_results_.front().time_ < time_limit) { + previous_results_.pop_front(); + } + + // If there are too few results, assume the result will be unreliable and + // bail. + const int64_t how_many_results = previous_results_.size(); + const int64_t earliest_time = previous_results_.front().time_; + const int64_t samples_duration = current_time_ms - earliest_time; + if ((how_many_results < minimum_count_) || + (samples_duration < (average_window_duration_ms_ / 4))) { + *found_command = previous_top_label_; + *score = 0; + *is_new_command = false; + return kTfLiteOk; + } + + // Calculate the average score across all the results in the window. + int32_t average_scores[kCategoryCount]; + for (int offset = 0; offset < previous_results_.size(); ++offset) { + PreviousResultsQueue::Result previous_result = + previous_results_.from_front(offset); + const uint8_t* scores = previous_result.scores_; + for (int i = 0; i < kCategoryCount; ++i) { + if (offset == 0) { + average_scores[i] = scores[i]; + } else { + average_scores[i] += scores[i]; + } + } + } + for (int i = 0; i < kCategoryCount; ++i) { + average_scores[i] /= how_many_results; + } + + // Find the current highest scoring category. + int current_top_index = 0; + int32_t current_top_score = 0; + for (int i = 0; i < kCategoryCount; ++i) { + if (average_scores[i] > current_top_score) { + current_top_score = average_scores[i]; + current_top_index = i; + } + } + const char* current_top_label = kCategoryLabels[current_top_index]; + + // If we've recently had another label trigger, assume one that occurs too + // soon afterwards is a bad result. + int64_t time_since_last_top; + if ((previous_top_label_ == kCategoryLabels[0]) || + (previous_top_label_time_ == std::numeric_limits::min())) { + time_since_last_top = std::numeric_limits::max(); + } else { + time_since_last_top = current_time_ms - previous_top_label_time_; + } + if ((current_top_score > detection_threshold_) && + (current_top_label != previous_top_label_) && + (time_since_last_top > suppression_ms_)) { + previous_top_label_ = current_top_label; + previous_top_label_time_ = current_time_ms; + *is_new_command = true; + } else { + *is_new_command = false; + } + *found_command = current_top_label; + *score = current_top_score; + + return kTfLiteOk; +} diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/recognize_commands.h b/tensorflow/lite/experimental/micro/examples/micro_speech/recognize_commands.h new file mode 100644 index 0000000000..adefffe850 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/recognize_commands.h @@ -0,0 +1,158 @@ +/* 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_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_RECOGNIZE_COMMANDS_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_RECOGNIZE_COMMANDS_H_ + +#include + +#include "tensorflow/lite/c/c_api_internal.h" +#include "tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h" +#include "tensorflow/lite/experimental/micro/micro_error_reporter.h" + +// Partial implementation of std::dequeue, just providing the functionality +// that's needed to keep a record of previous neural network results over a +// short time period, so they can be averaged together to produce a more +// accurate overall prediction. This doesn't use any dynamic memory allocation +// so it's a better fit for microcontroller applications, but this does mean +// there are hard limits on the number of results it can store. +class PreviousResultsQueue { + public: + PreviousResultsQueue(tflite::ErrorReporter* error_reporter) + : error_reporter_(error_reporter), front_index_(0), size_(0) {} + + // Data structure that holds an inference result, and the time when it + // was recorded. + struct Result { + Result() : time_(0), scores_() {} + Result(int32_t time, uint8_t* scores) : time_(time) { + for (int i = 0; i < kCategoryCount; ++i) { + scores_[i] = scores[i]; + } + } + int32_t time_; + uint8_t scores_[kCategoryCount]; + }; + + int size() { return size_; } + bool empty() { return size_ == 0; } + Result& front() { return results_[front_index_]; } + Result& back() { + int back_index = front_index_ + (size_ - 1); + if (back_index >= kMaxResults) { + back_index -= kMaxResults; + } + return results_[back_index]; + } + + void push_back(const Result& entry) { + if (size() >= kMaxResults) { + error_reporter_->Report( + "Couldn't push_back latest result, too many already!"); + return; + } + size_ += 1; + back() = entry; + } + + Result pop_front() { + if (size() <= 0) { + error_reporter_->Report("Couldn't pop_front result, none present!"); + return Result(); + } + Result result = front(); + front_index_ += 1; + if (front_index_ >= kMaxResults) { + front_index_ = 0; + } + size_ -= 1; + return result; + } + + // Most of the functions are duplicates of dequeue containers, but this + // is a helper that makes it easy to iterate through the contents of the + // queue. + Result& from_front(int offset) { + if ((offset < 0) || (offset >= size_)) { + error_reporter_->Report("Attempt to read beyond the end of the queue!"); + offset = size_ - 1; + } + int index = front_index_ + offset; + if (index >= kMaxResults) { + index -= kMaxResults; + } + return results_[index]; + } + + private: + tflite::ErrorReporter* error_reporter_; + static constexpr int kMaxResults = 50; + Result results_[kMaxResults]; + + int front_index_; + int size_; +}; + +// This class is designed to apply a very primitive decoding model on top of the +// instantaneous results from running an audio recognition model on a single +// window of samples. It applies smoothing over time so that noisy individual +// label scores are averaged, increasing the confidence that apparent matches +// are real. +// To use it, you should create a class object with the configuration you +// want, and then feed results from running a TensorFlow model into the +// processing method. The timestamp for each subsequent call should be +// increasing from the previous, since the class is designed to process a stream +// of data over time. +class RecognizeCommands { + public: + // labels should be a list of the strings associated with each one-hot score. + // The window duration controls the smoothing. Longer durations will give a + // higher confidence that the results are correct, but may miss some commands. + // The detection threshold has a similar effect, with high values increasing + // the precision at the cost of recall. The minimum count controls how many + // results need to be in the averaging window before it's seen as a reliable + // average. This prevents erroneous results when the averaging window is + // initially being populated for example. The suppression argument disables + // further recognitions for a set time after one has been triggered, which can + // help reduce spurious recognitions. + explicit RecognizeCommands(tflite::ErrorReporter* error_reporter, + int32_t average_window_duration_ms = 1000, + uint8_t detection_threshold = 51, + int32_t suppression_ms = 500, + int32_t minimum_count = 3); + + // Call this with the results of running a model on sample data. + TfLiteStatus ProcessLatestResults(const TfLiteTensor* latest_results, + const int32_t current_time_ms, + const char** found_command, uint8_t* score, + bool* is_new_command); + + private: + // Configuration + tflite::ErrorReporter* error_reporter_; + int32_t average_window_duration_ms_; + uint8_t detection_threshold_; + int32_t suppression_ms_; + int32_t minimum_count_; + + // Working variables + PreviousResultsQueue previous_results_; + int previous_results_head_; + int previous_results_tail_; + const char* previous_top_label_; + int32_t previous_top_label_time_; +}; + +#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_RECOGNIZE_COMMANDS_H_ diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/recognize_commands_test.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/recognize_commands_test.cc new file mode 100644 index 0000000000..f0cc73f10b --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/recognize_commands_test.cc @@ -0,0 +1,207 @@ +/* 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/lite/experimental/micro/examples/micro_speech/recognize_commands.h" + +#include "tensorflow/lite/experimental/micro/testing/micro_test.h" +#include "tensorflow/lite/experimental/micro/testing/test_utils.h" + +TF_LITE_MICRO_TESTS_BEGIN + +TF_LITE_MICRO_TEST(PreviousResultsQueueBasic) { + tflite::MicroErrorReporter micro_error_reporter; + tflite::ErrorReporter* error_reporter = µ_error_reporter; + + PreviousResultsQueue queue(error_reporter); + TF_LITE_MICRO_EXPECT_EQ(0, queue.size()); + + uint8_t scores_a[4] = {0, 0, 0, 1}; + queue.push_back({0, scores_a}); + TF_LITE_MICRO_EXPECT_EQ(1, queue.size()); + TF_LITE_MICRO_EXPECT_EQ(0, queue.front().time_); + TF_LITE_MICRO_EXPECT_EQ(0, queue.back().time_); + + uint8_t scores_b[4] = {0, 0, 1, 0}; + queue.push_back({1, scores_b}); + TF_LITE_MICRO_EXPECT_EQ(2, queue.size()); + TF_LITE_MICRO_EXPECT_EQ(0, queue.front().time_); + TF_LITE_MICRO_EXPECT_EQ(1, queue.back().time_); + + PreviousResultsQueue::Result pop_result = queue.pop_front(); + TF_LITE_MICRO_EXPECT_EQ(0, pop_result.time_); + TF_LITE_MICRO_EXPECT_EQ(1, queue.size()); + TF_LITE_MICRO_EXPECT_EQ(1, queue.front().time_); + TF_LITE_MICRO_EXPECT_EQ(1, queue.back().time_); + + uint8_t scores_c[4] = {0, 1, 0, 0}; + queue.push_back({2, scores_c}); + TF_LITE_MICRO_EXPECT_EQ(2, queue.size()); + TF_LITE_MICRO_EXPECT_EQ(1, queue.front().time_); + TF_LITE_MICRO_EXPECT_EQ(2, queue.back().time_); +} + +TF_LITE_MICRO_TEST(PreviousResultsQueuePushPop) { + tflite::MicroErrorReporter micro_error_reporter; + tflite::ErrorReporter* error_reporter = µ_error_reporter; + + PreviousResultsQueue queue(error_reporter); + TF_LITE_MICRO_EXPECT_EQ(0, queue.size()); + + for (int i = 0; i < 123; ++i) { + uint8_t scores[4] = {0, 0, 0, 1}; + queue.push_back({i, scores}); + TF_LITE_MICRO_EXPECT_EQ(1, queue.size()); + TF_LITE_MICRO_EXPECT_EQ(i, queue.front().time_); + TF_LITE_MICRO_EXPECT_EQ(i, queue.back().time_); + + PreviousResultsQueue::Result pop_result = queue.pop_front(); + TF_LITE_MICRO_EXPECT_EQ(i, pop_result.time_); + TF_LITE_MICRO_EXPECT_EQ(0, queue.size()); + } +} + +TF_LITE_MICRO_TEST(RecognizeCommandsTestBasic) { + tflite::MicroErrorReporter micro_error_reporter; + tflite::ErrorReporter* error_reporter = µ_error_reporter; + + RecognizeCommands recognize_commands(error_reporter); + + TfLiteTensor results = tflite::testing::CreateQuantizedTensor( + {255, 0, 0, 0}, tflite::testing::IntArrayFromInitializer({2, 1, 4}), + "input_tensor", 0.0f, 128.0f); + + const char* found_command; + uint8_t score; + bool is_new_command; + TF_LITE_MICRO_EXPECT_EQ( + kTfLiteOk, recognize_commands.ProcessLatestResults( + &results, 0, &found_command, &score, &is_new_command)); +} + +TF_LITE_MICRO_TEST(RecognizeCommandsTestFindCommands) { + tflite::MicroErrorReporter micro_error_reporter; + tflite::ErrorReporter* error_reporter = µ_error_reporter; + + RecognizeCommands recognize_commands(error_reporter, 1000, 51); + + TfLiteTensor yes_results = tflite::testing::CreateQuantizedTensor( + {0, 0, 255, 0}, tflite::testing::IntArrayFromInitializer({2, 1, 4}), + "input_tensor", 0.0f, 128.0f); + + bool has_found_new_command = false; + const char* new_command; + for (int i = 0; i < 10; ++i) { + const char* found_command; + uint8_t score; + bool is_new_command; + int32_t current_time_ms = 0 + (i * 100); + TF_LITE_MICRO_EXPECT_EQ( + kTfLiteOk, recognize_commands.ProcessLatestResults( + &yes_results, current_time_ms, &found_command, &score, + &is_new_command)); + if (is_new_command) { + TF_LITE_MICRO_EXPECT(!has_found_new_command); + has_found_new_command = true; + new_command = found_command; + } + } + TF_LITE_MICRO_EXPECT(has_found_new_command); + TF_LITE_MICRO_EXPECT_EQ(0, tflite::testing::TestStrcmp("yes", new_command)); + + TfLiteTensor no_results = tflite::testing::CreateQuantizedTensor( + {0, 0, 0, 255}, tflite::testing::IntArrayFromInitializer({2, 1, 4}), + "input_tensor", 0.0f, 128.0f); + has_found_new_command = false; + new_command = ""; + uint8_t score; + for (int i = 0; i < 10; ++i) { + const char* found_command; + bool is_new_command; + int32_t current_time_ms = 1000 + (i * 100); + TF_LITE_MICRO_EXPECT_EQ( + kTfLiteOk, recognize_commands.ProcessLatestResults( + &no_results, current_time_ms, &found_command, &score, + &is_new_command)); + if (is_new_command) { + TF_LITE_MICRO_EXPECT(!has_found_new_command); + has_found_new_command = true; + new_command = found_command; + } + } + TF_LITE_MICRO_EXPECT(has_found_new_command); + TF_LITE_MICRO_EXPECT_EQ(231, score); + TF_LITE_MICRO_EXPECT_EQ(0, tflite::testing::TestStrcmp("no", new_command)); +} + +TF_LITE_MICRO_TEST(RecognizeCommandsTestBadInputLength) { + tflite::MicroErrorReporter micro_error_reporter; + tflite::ErrorReporter* error_reporter = µ_error_reporter; + + RecognizeCommands recognize_commands(error_reporter, 1000, 51); + + TfLiteTensor bad_results = tflite::testing::CreateQuantizedTensor( + {0, 0, 255}, tflite::testing::IntArrayFromInitializer({2, 1, 3}), + "input_tensor", 0.0f, 128.0f); + + const char* found_command; + uint8_t score; + bool is_new_command; + TF_LITE_MICRO_EXPECT_NE( + kTfLiteOk, recognize_commands.ProcessLatestResults( + &bad_results, 0, &found_command, &score, &is_new_command)); +} + +TF_LITE_MICRO_TEST(RecognizeCommandsTestBadInputTimes) { + tflite::MicroErrorReporter micro_error_reporter; + tflite::ErrorReporter* error_reporter = µ_error_reporter; + + RecognizeCommands recognize_commands(error_reporter, 1000, 51); + + TfLiteTensor results = tflite::testing::CreateQuantizedTensor( + {0, 0, 255, 0}, tflite::testing::IntArrayFromInitializer({2, 1, 4}), + "input_tensor", 0.0f, 128.0f); + + const char* found_command; + uint8_t score; + bool is_new_command; + TF_LITE_MICRO_EXPECT_EQ( + kTfLiteOk, recognize_commands.ProcessLatestResults( + &results, 100, &found_command, &score, &is_new_command)); + TF_LITE_MICRO_EXPECT_NE( + kTfLiteOk, recognize_commands.ProcessLatestResults( + &results, 0, &found_command, &score, &is_new_command)); +} + +TF_LITE_MICRO_TEST(RecognizeCommandsTestTooFewInputs) { + tflite::MicroErrorReporter micro_error_reporter; + tflite::ErrorReporter* error_reporter = µ_error_reporter; + + RecognizeCommands recognize_commands(error_reporter, 1000, 51); + + TfLiteTensor results = tflite::testing::CreateQuantizedTensor( + {0, 0, 255, 0}, tflite::testing::IntArrayFromInitializer({2, 1, 4}), + "input_tensor", 0.0f, 128.0f); + + const char* found_command; + uint8_t score; + bool is_new_command; + TF_LITE_MICRO_EXPECT_EQ( + kTfLiteOk, recognize_commands.ProcessLatestResults( + &results, 100, &found_command, &score, &is_new_command)); + TF_LITE_MICRO_EXPECT_EQ(0, score); + TF_LITE_MICRO_EXPECT_EQ(false, is_new_command); +} + +TF_LITE_MICRO_TESTS_END diff --git a/tensorflow/lite/experimental/micro/kernels/BUILD b/tensorflow/lite/experimental/micro/kernels/BUILD index a54fd41760..47ac85c605 100644 --- a/tensorflow/lite/experimental/micro/kernels/BUILD +++ b/tensorflow/lite/experimental/micro/kernels/BUILD @@ -48,22 +48,6 @@ cc_library( ], ) -cc_library( - name = "test_utils", - srcs = [ - ], - hdrs = [ - "test_utils.h", - ], - copts = tflite_copts(), - deps = [ - "//tensorflow/lite/c:c_api_internal", - "//tensorflow/lite/core/api", - "//tensorflow/lite/experimental/micro:micro_framework", - "//tensorflow/lite/experimental/micro/testing:micro_test", - ], -) - tflite_micro_cc_test( name = "depthwise_conv_test", srcs = [ @@ -71,7 +55,6 @@ tflite_micro_cc_test( ], deps = [ ":all_ops_resolver", - ":test_utils", "//tensorflow/lite/c:c_api_internal", "//tensorflow/lite/experimental/micro:micro_framework", "//tensorflow/lite/experimental/micro/testing:micro_test", @@ -85,7 +68,6 @@ tflite_micro_cc_test( ], deps = [ ":all_ops_resolver", - ":test_utils", "//tensorflow/lite/c:c_api_internal", "//tensorflow/lite/experimental/micro:micro_framework", "//tensorflow/lite/experimental/micro/testing:micro_test", @@ -99,7 +81,6 @@ tflite_micro_cc_test( ], deps = [ ":all_ops_resolver", - ":test_utils", "//tensorflow/lite/c:c_api_internal", "//tensorflow/lite/experimental/micro:micro_framework", "//tensorflow/lite/experimental/micro/testing:micro_test", diff --git a/tensorflow/lite/experimental/micro/kernels/depthwise_conv_test.cc b/tensorflow/lite/experimental/micro/kernels/depthwise_conv_test.cc index f70437a4b9..05ba8798c0 100644 --- a/tensorflow/lite/experimental/micro/kernels/depthwise_conv_test.cc +++ b/tensorflow/lite/experimental/micro/kernels/depthwise_conv_test.cc @@ -16,9 +16,9 @@ limitations under the License. #include "tensorflow/lite/c/builtin_op_data.h" #include "tensorflow/lite/c/c_api_internal.h" #include "tensorflow/lite/experimental/micro/kernels/all_ops_resolver.h" -#include "tensorflow/lite/experimental/micro/kernels/test_utils.h" #include "tensorflow/lite/experimental/micro/simple_tensor_allocator.h" #include "tensorflow/lite/experimental/micro/testing/micro_test.h" +#include "tensorflow/lite/experimental/micro/testing/test_utils.h" namespace tflite { namespace testing { diff --git a/tensorflow/lite/experimental/micro/kernels/fully_connected_test.cc b/tensorflow/lite/experimental/micro/kernels/fully_connected_test.cc index 300f8aaf78..c2e1446848 100644 --- a/tensorflow/lite/experimental/micro/kernels/fully_connected_test.cc +++ b/tensorflow/lite/experimental/micro/kernels/fully_connected_test.cc @@ -16,9 +16,9 @@ limitations under the License. #include "tensorflow/lite/c/builtin_op_data.h" #include "tensorflow/lite/c/c_api_internal.h" #include "tensorflow/lite/experimental/micro/kernels/all_ops_resolver.h" -#include "tensorflow/lite/experimental/micro/kernels/test_utils.h" #include "tensorflow/lite/experimental/micro/simple_tensor_allocator.h" #include "tensorflow/lite/experimental/micro/testing/micro_test.h" +#include "tensorflow/lite/experimental/micro/testing/test_utils.h" namespace tflite { namespace testing { diff --git a/tensorflow/lite/experimental/micro/kernels/softmax_test.cc b/tensorflow/lite/experimental/micro/kernels/softmax_test.cc index 7253b3be8c..8933b6c0ed 100644 --- a/tensorflow/lite/experimental/micro/kernels/softmax_test.cc +++ b/tensorflow/lite/experimental/micro/kernels/softmax_test.cc @@ -16,9 +16,9 @@ limitations under the License. #include "tensorflow/lite/c/builtin_op_data.h" #include "tensorflow/lite/c/c_api_internal.h" #include "tensorflow/lite/experimental/micro/kernels/all_ops_resolver.h" -#include "tensorflow/lite/experimental/micro/kernels/test_utils.h" #include "tensorflow/lite/experimental/micro/simple_tensor_allocator.h" #include "tensorflow/lite/experimental/micro/testing/micro_test.h" +#include "tensorflow/lite/experimental/micro/testing/test_utils.h" namespace tflite { namespace testing { diff --git a/tensorflow/lite/experimental/micro/testing/BUILD b/tensorflow/lite/experimental/micro/testing/BUILD index 5a31a709ca..1623df5b86 100644 --- a/tensorflow/lite/experimental/micro/testing/BUILD +++ b/tensorflow/lite/experimental/micro/testing/BUILD @@ -10,8 +10,10 @@ cc_library( name = "micro_test", hdrs = [ "micro_test.h", + "test_utils.h", ], deps = [ + "//tensorflow/lite/c:c_api_internal", "//tensorflow/lite/experimental/micro:micro_framework", ], ) diff --git a/tensorflow/lite/experimental/micro/kernels/test_utils.h b/tensorflow/lite/experimental/micro/testing/test_utils.h similarity index 91% rename from tensorflow/lite/experimental/micro/kernels/test_utils.h rename to tensorflow/lite/experimental/micro/testing/test_utils.h index 95f2d8a9d2..e37eaf46e0 100644 --- a/tensorflow/lite/experimental/micro/kernels/test_utils.h +++ b/tensorflow/lite/experimental/micro/testing/test_utils.h @@ -12,8 +12,8 @@ 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_LITE_EXPERIMENTAL_MICRO_KERNELS_TEST_UTILS_H_ -#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_KERNELS_TEST_UTILS_H_ +#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_TESTING_TEST_UTILS_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_TESTING_TEST_UTILS_H_ #include #include @@ -21,8 +21,7 @@ limitations under the License. #include "tensorflow/lite/c/builtin_op_data.h" #include "tensorflow/lite/c/c_api_internal.h" -#include "tensorflow/lite/core/api/error_reporter.h" -#include "tensorflow/lite/experimental/micro/kernels/test_utils.h" +#include "tensorflow/lite/experimental/micro/micro_error_reporter.h" #include "tensorflow/lite/experimental/micro/testing/micro_test.h" namespace tflite { @@ -164,7 +163,20 @@ inline TfLiteTensor CreateQuantized32Tensor(std::initializer_list data, return CreateQuantized32Tensor(data.begin(), dims, name, min, max); } +// Do a simple string comparison for testing purposes, without requiring the +// standard C library. +inline int TestStrcmp(const char* a, const char* b) { + if ((a == nullptr) || (b == nullptr)) { + return -1; + } + while ((*a != 0) && (*a == *b)) { + a++; + b++; + } + return *(const unsigned char*)a - *(const unsigned char*)b; +} + } // namespace testing } // namespace tflite -#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_KERNELS_TEST_UTILS_H_ +#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_TESTING_TEST_UTILS_H_ -- GitLab From 97acfa47fac0ced993951cac267a69cdea3c2321 Mon Sep 17 00:00:00 2001 From: Alexandre Passos Date: Wed, 26 Dec 2018 14:21:43 -0800 Subject: [PATCH 114/622] map_fn in eager should respect the passed-in dtype PiperOrigin-RevId: 226953849 --- tensorflow/python/kernel_tests/functional_ops_test.py | 8 ++++++++ tensorflow/python/ops/tensor_array_ops.py | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/tensorflow/python/kernel_tests/functional_ops_test.py b/tensorflow/python/kernel_tests/functional_ops_test.py index 0d6a3cbd35..3b3db429d8 100644 --- a/tensorflow/python/kernel_tests/functional_ops_test.py +++ b/tensorflow/python/kernel_tests/functional_ops_test.py @@ -24,6 +24,7 @@ from tensorflow.core.framework import attr_value_pb2 from tensorflow.core.protobuf import config_pb2 from tensorflow.python.client import session from tensorflow.python.data.ops import iterator_ops +from tensorflow.python.eager import context from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors @@ -200,6 +201,13 @@ class FunctionalOpsTest(test.TestCase): self.assertAllEqual( np.array([(x + 3) * 2 for x in nums]), self.evaluate(r)) + def testMapDtypeEager(self): + with context.eager_mode(): + dtype = functional_ops.map_fn(lambda x: constant_op.constant(""), + constant_op.constant([]), + dtype=dtypes.string).dtype + self.assertEqual(dtype, dtypes.string) + def testMapSparseTensor(self): with self.cached_session(): with self.assertRaises(TypeError): diff --git a/tensorflow/python/ops/tensor_array_ops.py b/tensorflow/python/ops/tensor_array_ops.py index 37d5e6ae2a..aece7cfe25 100644 --- a/tensorflow/python/ops/tensor_array_ops.py +++ b/tensorflow/python/ops/tensor_array_ops.py @@ -858,7 +858,8 @@ class _EagerTensorArray(object): if self._tensor_array: for ix in range(len(self._tensor_array)): self._maybe_zero(ix) - return array_ops.stack(self._tensor_array, name=name) + return ops.convert_to_tensor( + self._tensor_array, name=name, dtype=self._dtype) def gather(self, indices, name=None): """See TensorArray.""" -- GitLab From b45d2f0a6badc029733250bc169fa57a0689fa02 Mon Sep 17 00:00:00 2001 From: Alexandre Passos Date: Wed, 26 Dec 2018 14:32:36 -0800 Subject: [PATCH 115/622] Undisable tests which pass with --test_env=TF2_BEHAVIOR=1 PiperOrigin-RevId: 226954758 --- tensorflow/python/keras/layers/unified_lstm_test.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tensorflow/python/keras/layers/unified_lstm_test.py b/tensorflow/python/keras/layers/unified_lstm_test.py index 7a66e2c240..811b8d128e 100644 --- a/tensorflow/python/keras/layers/unified_lstm_test.py +++ b/tensorflow/python/keras/layers/unified_lstm_test.py @@ -682,7 +682,6 @@ class LSTMLayerGraphOnlyTest(test.TestCase): class LSTMLayerV1OnlyTest(test.TestCase, parameterized.TestCase): - @test_util.run_v1_only('b/121278392') @test_util.run_in_graph_and_eager_modes(config=_config) def test_dropout_LSTM(self): num_samples = 2 @@ -698,7 +697,6 @@ class LSTMLayerV1OnlyTest(test.TestCase, parameterized.TestCase): }, input_shape=(num_samples, timesteps, embedding_dim)) - @test_util.run_v1_only('b/120941292') @test_util.run_in_graph_and_eager_modes(config=_config) def test_statefulness_LSTM(self): num_samples = 2 -- GitLab From fd463cd8c2351388070d1890d9726afcbd0f1f3a Mon Sep 17 00:00:00 2001 From: Yanan Cao Date: Wed, 26 Dec 2018 14:33:05 -0800 Subject: [PATCH 116/622] Automated rollback of commit 98bbee7afec8e197bbe56f5ba2804a6ea75d3317 PiperOrigin-RevId: 226954811 --- tensorflow/contrib/compiler/xla.py | 26 +++++++------- tensorflow/contrib/tpu/python/tpu/tpu.py | 44 ++++++++---------------- 2 files changed, 27 insertions(+), 43 deletions(-) diff --git a/tensorflow/contrib/compiler/xla.py b/tensorflow/contrib/compiler/xla.py index 4e07aa511b..f867cd15b6 100644 --- a/tensorflow/contrib/compiler/xla.py +++ b/tensorflow/contrib/compiler/xla.py @@ -34,7 +34,6 @@ from tensorflow.python.ops import variable_scope from tensorflow.python.platform import tf_logging as logging from tensorflow.python.util import compat from tensorflow.python.util import function_utils -from tensorflow.python.util import nest from tensorflow.python.util import tf_decorator from tensorflow.python.util import tf_inspect @@ -77,9 +76,7 @@ def compile(computation, inputs=None): # pylint: disable=redefined-builtin All `Operation`s returned from `computation` will be executed when evaluating any of the returned output tensors. - inputs: A list of inputs or `None` (equivalent to an empty list). Each input - can be a nested structure containing values that are convertible to - tensors. + inputs: A list of input tensors or `None` (equivalent to an empty list). Returns: A list of output tensors. @@ -263,10 +260,17 @@ def _compile_internal(computation, inputs=None): if not isinstance(inputs, collections.Sequence): raise TypeError('inputs must be a list') - # Flatten inputs. - flat_inputs = nest.flatten(inputs) # Converts inputs to Tensors. - flat_inputs = [ops.convert_to_tensor(x) for x in flat_inputs] + inputs = [ops.convert_to_tensor(x) for x in inputs] + input_arity = len(inputs) + + arg_error = check_function_argument_count( + computation, input_arity, infeed_queue=None) + if arg_error is not None: + raise TypeError( + 'Supplied computation cannot be called with the specified inputs. You ' + 'specified %d inputs: %s, but the computation needs %s' % + (input_arity, str([i.name for i in inputs]), arg_error)) cluster_name = ops.get_default_graph().unique_name('cluster') pivot = control_flow_ops.no_op(name=cluster_name + '/pivot') @@ -276,15 +280,11 @@ def _compile_internal(computation, inputs=None): # Add identity ops so even unused inputs are 'consumed' by the # computation. - flat_inputs = [ + computation_inputs = [ array_ops.identity(x, name='input_{}'.format(i)) - for i, x in enumerate(flat_inputs) + for i, x in enumerate(inputs) ] - # Re-pack flat_inputs in same structure as 'inputs'. - computation_inputs = nest.pack_sequence_as( - structure=inputs, flat_sequence=flat_inputs) - # Only resource variables work inside an XLA computation, so turn on # resource variables for the computation. vscope = variable_scope.get_variable_scope() diff --git a/tensorflow/contrib/tpu/python/tpu/tpu.py b/tensorflow/contrib/tpu/python/tpu/tpu.py index 53e5539ee6..9266d81cf5 100644 --- a/tensorflow/contrib/tpu/python/tpu/tpu.py +++ b/tensorflow/contrib/tpu/python/tpu/tpu.py @@ -36,7 +36,6 @@ from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import variable_scope from tensorflow.python.platform import tf_logging as logging from tensorflow.python.util import compat -from tensorflow.python.util import nest # Operations that indicate some error in the users graph, e.g. a placeholder @@ -488,8 +487,7 @@ def replicate(computation, computation: A Python function that builds the computation to replicate. inputs: A list of lists of input tensors or `None` (equivalent to `[[]]`), indexed by `[replica_num][input_num]`. All replicas must - have the same number of inputs. Each input can be a nested structure - containing values that are convertible to tensors. + have the same number of inputs. infeed_queue: If not `None`, the `InfeedQueue` from which to append a tuple of arguments as inputs to computation. device_assignment: If not `None`, a `DeviceAssignment` describing the @@ -528,8 +526,7 @@ def split_compile_and_replicate(computation, computation: A Python function that builds the computation to replicate. inputs: A list of lists of input tensors or `None` (equivalent to `[[]]`), indexed by `[replica_num][input_num]`. All replicas must - have the same number of inputs. Each input can be a nested structure - containing values that are convertible to tensors. + have the same number of inputs. infeed_queue: If not `None`, the `InfeedQueue` from which to append a tuple of arguments as inputs to computation. device_assignment: If not `None`, a `DeviceAssignment` describing the @@ -583,32 +580,24 @@ def split_compile_and_replicate(computation, if num_replicas == 0: return [] - # Checks all replicas have the same structure. - for i in xrange(1, num_replicas): - nest.assert_same_structure(inputs[0], inputs[i]) - - # Flatten inputs. - flat_inputs = [ - nest.flatten(per_replica_input) for per_replica_input in inputs - ] # Converts inputs to Tensors. - flat_inputs = [[ops.convert_to_tensor(x) for x in inp] for inp in flat_inputs] + inputs = [[ops.convert_to_tensor(x) for x in inp] for inp in inputs] # Verifies that all replicas have matching numbers and types of inputs - flat_input_types = [x.dtype for x in flat_inputs[0]] - input_arity = len(inputs[0]) - flat_input_arity = len(flat_input_types) + input_types = [x.dtype for x in inputs[0]] + input_arity = len(input_types) for i in range(num_replicas): if len(inputs[i]) != input_arity: raise ValueError("Replicas must have the same number of inputs. " "Replica 0 had {} inputs, replica {} had {} " "inputs.".format(input_arity, i, len(inputs[i]))) - types = [x.dtype for x in flat_inputs[i]] - if types != flat_input_types: - raise ValueError("Replicas must have matching input types. Replica 0 had " - "input types {}, replica {} had input types {}".format( - flat_input_types, i, types)) + types = [x.dtype for x in inputs[i]] + if types != input_types: + raise ValueError( + "Replicas must have matching input types. Replica 0 had " + "input types {}, replica {} had input types {}".format( + input_types, i, types)) arg_error = xla.check_function_argument_count( computation, input_arity, infeed_queue) @@ -631,8 +620,8 @@ def split_compile_and_replicate(computation, # Fan-in: Builds a TPUReplicatedInput node for each input. computation_inputs = [] - for i in range(0, flat_input_arity): - replicas = [flat_inputs[replica][i] for replica in xrange(num_replicas)] + for i in range(0, input_arity): + replicas = [inputs[replica][i] for replica in xrange(num_replicas)] computation_inputs.append( tpu_ops.tpu_replicated_input(replicas, name="input{}".format(i))) @@ -662,10 +651,6 @@ def split_compile_and_replicate(computation, i.op._set_attr("_tpu_input_identity", attr_value_pb2.AttrValue(b=True)) # pylint: enable=protected-access - # Unflatten the computation inputs to match original input structure. - computation_inputs = nest.pack_sequence_as( - structure=inputs[0], flat_sequence=computation_inputs) - # If there is an infeed queue, adds the dequeued values to the # computation's inputs. if infeed_queue is not None: @@ -1108,8 +1093,7 @@ def rewrite(computation, evaluating any of the returned output tensors, not just the ones returned. inputs: A list of input tensors or `None` (equivalent to an empty list). infeed_queue: If not `None`, the `InfeedQueue` from which to append a tuple - of arguments as inputs to `computation`. Each input can be a nested - structure containing values that are convertible to tensors. + of arguments as inputs to `computation`. device_assignment: if not `None`, a `DeviceAssignment` describing the mapping between logical cores in the computation with physical cores in the TPU topology. May be omitted for a single-core computation, in which -- GitLab From bc7d032d4f3d6fb0bb75d0ba61718395b02f9556 Mon Sep 17 00:00:00 2001 From: Gunhan Gulsoy Date: Wed, 26 Dec 2018 14:33:50 -0800 Subject: [PATCH 117/622] Remove dependency from kernels to audio ops op lib. PiperOrigin-RevId: 226954879 --- tensorflow/core/kernels/BUILD | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tensorflow/core/kernels/BUILD b/tensorflow/core/kernels/BUILD index 8eed2cd0a8..8e0e667e92 100644 --- a/tensorflow/core/kernels/BUILD +++ b/tensorflow/core/kernels/BUILD @@ -2479,7 +2479,6 @@ tf_kernel_library( prefix = "encode_wav_op", deps = [ ":bounds_check", - "//tensorflow/core:audio_ops_op_lib", "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", @@ -2491,7 +2490,6 @@ tf_kernel_library( name = "decode_wav_op", prefix = "decode_wav_op", deps = [ - "//tensorflow/core:audio_ops_op_lib", "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", @@ -5098,7 +5096,6 @@ tf_kernel_library( prefix = "spectrogram_op", deps = [ ":spectrogram", - "//tensorflow/core:audio_ops_op_lib", "//tensorflow/core:core_cpu", "//tensorflow/core:framework", "//tensorflow/core:lib", @@ -5216,7 +5213,6 @@ tf_kernel_library( prefix = "mfcc_op", deps = [ ":mfcc", - "//tensorflow/core:audio_ops_op_lib", "//tensorflow/core:core_cpu", "//tensorflow/core:framework", "//tensorflow/core:lib", -- GitLab From a2078ce6fd696fe4846dacb2982bceb21aa3245b Mon Sep 17 00:00:00 2001 From: Pavithra Vijay Date: Wed, 26 Dec 2018 14:59:18 -0800 Subject: [PATCH 118/622] Adding v2 APIs for some metrics and losses. PiperOrigin-RevId: 226957174 --- tensorflow/python/keras/losses.py | 4 + tensorflow/python/keras/metrics.py | 8 + ...flow.keras.losses.-categorical-hinge.pbtxt | 22 ++ ...rflow.keras.losses.-cosine-proximity.pbtxt | 22 ++ .../v1/tensorflow.keras.losses.-hinge.pbtxt | 22 ++ ...nsorflow.keras.losses.-squared-hinge.pbtxt | 22 ++ .../golden/v1/tensorflow.keras.losses.pbtxt | 16 ++ ...low.keras.metrics.-categorical-hinge.pbtxt | 198 ++++++++++++++++++ ...flow.keras.metrics.-cosine-proximity.pbtxt | 198 ++++++++++++++++++ .../v1/tensorflow.keras.metrics.-hinge.pbtxt | 198 ++++++++++++++++++ ...w.keras.metrics.-mean-absolute-error.pbtxt | 198 ++++++++++++++++++ ...rics.-mean-absolute-percentage-error.pbtxt | 198 ++++++++++++++++++ ...ow.keras.metrics.-mean-squared-error.pbtxt | 198 ++++++++++++++++++ ...rics.-mean-squared-logarithmic-error.pbtxt | 198 ++++++++++++++++++ ...sorflow.keras.metrics.-squared-hinge.pbtxt | 198 ++++++++++++++++++ .../golden/v1/tensorflow.keras.metrics.pbtxt | 32 +++ ...flow.keras.losses.-categorical-hinge.pbtxt | 22 ++ ...rflow.keras.losses.-cosine-proximity.pbtxt | 22 ++ .../v2/tensorflow.keras.losses.-hinge.pbtxt | 22 ++ ...nsorflow.keras.losses.-squared-hinge.pbtxt | 22 ++ .../golden/v2/tensorflow.keras.losses.pbtxt | 16 ++ ...low.keras.metrics.-categorical-hinge.pbtxt | 198 ++++++++++++++++++ ...flow.keras.metrics.-cosine-proximity.pbtxt | 198 ++++++++++++++++++ .../v2/tensorflow.keras.metrics.-hinge.pbtxt | 198 ++++++++++++++++++ ...w.keras.metrics.-mean-absolute-error.pbtxt | 198 ++++++++++++++++++ ...rics.-mean-absolute-percentage-error.pbtxt | 198 ++++++++++++++++++ ...ow.keras.metrics.-mean-squared-error.pbtxt | 198 ++++++++++++++++++ ...rics.-mean-squared-logarithmic-error.pbtxt | 198 ++++++++++++++++++ ...sorflow.keras.metrics.-squared-hinge.pbtxt | 198 ++++++++++++++++++ .../golden/v2/tensorflow.keras.metrics.pbtxt | 32 +++ 30 files changed, 3452 insertions(+) create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-categorical-hinge.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-cosine-proximity.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-hinge.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-squared-hinge.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-categorical-hinge.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-cosine-proximity.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-hinge.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-mean-absolute-error.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-mean-absolute-percentage-error.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-mean-squared-error.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-mean-squared-logarithmic-error.pbtxt create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-squared-hinge.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-categorical-hinge.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-cosine-proximity.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-hinge.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-squared-hinge.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-categorical-hinge.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-cosine-proximity.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-hinge.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-mean-absolute-error.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-mean-absolute-percentage-error.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-mean-squared-error.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-mean-squared-logarithmic-error.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-squared-hinge.pbtxt diff --git a/tensorflow/python/keras/losses.py b/tensorflow/python/keras/losses.py index 51ae935bb2..d05dfb03f1 100644 --- a/tensorflow/python/keras/losses.py +++ b/tensorflow/python/keras/losses.py @@ -395,6 +395,7 @@ class CategoricalCrossentropy(Loss): y_true, y_pred, from_logits=self.from_logits) +@keras_export('keras.losses.Hinge') class Hinge(Loss): """Computes the hinge loss between `y_true` and `y_pred`. @@ -429,6 +430,7 @@ class Hinge(Loss): return hinge(y_true, y_pred) +@keras_export('keras.losses.SquaredHinge') class SquaredHinge(Loss): """Computes the squared hinge loss between `y_true` and `y_pred`. @@ -463,6 +465,7 @@ class SquaredHinge(Loss): return squared_hinge(y_true, y_pred) +@keras_export('keras.losses.CategoricalHinge') class CategoricalHinge(Loss): """Computes the categorical hinge loss between `y_true` and `y_pred`. @@ -629,6 +632,7 @@ def cosine_proximity(y_true, y_pred): return -math_ops.reduce_sum(y_true * y_pred, axis=-1) +@keras_export('keras.losses.CosineProximity') class CosineProximity(Loss): """Computes the cosine distance between `y_true` and `y_pred`. diff --git a/tensorflow/python/keras/metrics.py b/tensorflow/python/keras/metrics.py index 7073323506..d8a81882fb 100644 --- a/tensorflow/python/keras/metrics.py +++ b/tensorflow/python/keras/metrics.py @@ -1219,6 +1219,7 @@ class SpecificityAtSensitivity(SensitivitySpecificityBase): self.tn[min_index] + self.fp[min_index]) +@keras_export('keras.metrics.CosineProximity') class CosineProximity(MeanMetricWrapper): """Computes the cosine distance between the labels and predictions. @@ -1256,6 +1257,7 @@ class CosineProximity(MeanMetricWrapper): return super(CosineProximity, cls).from_config(config) +@keras_export('keras.metrics.MeanAbsoluteError') class MeanAbsoluteError(MeanMetricWrapper): """Computes the mean absolute error between the labels and predictions. @@ -1288,6 +1290,7 @@ class MeanAbsoluteError(MeanMetricWrapper): return super(MeanAbsoluteError, cls).from_config(config) +@keras_export('keras.metrics.MeanAbsolutePercentageError') class MeanAbsolutePercentageError(MeanMetricWrapper): """Computes the mean absolute percentage error between `y_true` and `y_pred`. @@ -1321,6 +1324,7 @@ class MeanAbsolutePercentageError(MeanMetricWrapper): return super(MeanAbsolutePercentageError, cls).from_config(config) +@keras_export('keras.metrics.MeanSquaredError') class MeanSquaredError(MeanMetricWrapper): """Computes the mean squared error between `y_true` and `y_pred`. @@ -1354,6 +1358,7 @@ class MeanSquaredError(MeanMetricWrapper): return super(MeanSquaredError, cls).from_config(config) +@keras_export('keras.metrics.MeanSquaredLogarithmicError') class MeanSquaredLogarithmicError(MeanMetricWrapper): """Computes the mean squared logarithmic error between `y_true` and `y_pred`. @@ -1387,6 +1392,7 @@ class MeanSquaredLogarithmicError(MeanMetricWrapper): return super(MeanSquaredLogarithmicError, cls).from_config(config) +@keras_export('keras.metrics.Hinge') class Hinge(MeanMetricWrapper): """Computes the hinge metric between `y_true` and `y_pred`. @@ -1419,6 +1425,7 @@ class Hinge(MeanMetricWrapper): return super(Hinge, cls).from_config(config) +@keras_export('keras.metrics.SquaredHinge') class SquaredHinge(MeanMetricWrapper): """Computes the squared hinge metric between `y_true` and `y_pred`. @@ -1451,6 +1458,7 @@ class SquaredHinge(MeanMetricWrapper): return super(SquaredHinge, cls).from_config(config) +@keras_export('keras.metrics.CategoricalHinge') class CategoricalHinge(MeanMetricWrapper): """Computes the categorical hinge metric between `y_true` and `y_pred`. diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-categorical-hinge.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-categorical-hinge.pbtxt new file mode 100644 index 0000000000..4ba9e57bed --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-categorical-hinge.pbtxt @@ -0,0 +1,22 @@ +path: "tensorflow.keras.losses.CategoricalHinge" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member_method { + name: "__init__" + argspec: "args=[\'self\', \'reduction\', \'name\'], varargs=None, keywords=None, defaults=[\'sum_over_batch_size\', \'None\'], " + } + member_method { + name: "call" + argspec: "args=[\'self\', \'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-cosine-proximity.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-cosine-proximity.pbtxt new file mode 100644 index 0000000000..de35966f48 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-cosine-proximity.pbtxt @@ -0,0 +1,22 @@ +path: "tensorflow.keras.losses.CosineProximity" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member_method { + name: "__init__" + argspec: "args=[\'self\', \'reduction\', \'name\'], varargs=None, keywords=None, defaults=[\'sum_over_batch_size\', \'None\'], " + } + member_method { + name: "call" + argspec: "args=[\'self\', \'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-hinge.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-hinge.pbtxt new file mode 100644 index 0000000000..7b3c62d3be --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-hinge.pbtxt @@ -0,0 +1,22 @@ +path: "tensorflow.keras.losses.Hinge" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member_method { + name: "__init__" + argspec: "args=[\'self\', \'reduction\', \'name\'], varargs=None, keywords=None, defaults=[\'sum_over_batch_size\', \'None\'], " + } + member_method { + name: "call" + argspec: "args=[\'self\', \'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-squared-hinge.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-squared-hinge.pbtxt new file mode 100644 index 0000000000..f8badb2b6e --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-squared-hinge.pbtxt @@ -0,0 +1,22 @@ +path: "tensorflow.keras.losses.SquaredHinge" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member_method { + name: "__init__" + argspec: "args=[\'self\', \'reduction\', \'name\'], varargs=None, keywords=None, defaults=[\'sum_over_batch_size\', \'None\'], " + } + member_method { + name: "call" + argspec: "args=[\'self\', \'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.pbtxt index 9e26ddbdca..532d2b2f88 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.pbtxt @@ -8,6 +8,18 @@ tf_module { name: "CategoricalCrossentropy" mtype: "" } + member { + name: "CategoricalHinge" + mtype: "" + } + member { + name: "CosineProximity" + mtype: "" + } + member { + name: "Hinge" + mtype: "" + } member { name: "MeanAbsoluteError" mtype: "" @@ -24,6 +36,10 @@ tf_module { name: "MeanSquaredLogarithmicError" mtype: "" } + member { + name: "SquaredHinge" + mtype: "" + } member_method { name: "KLD" argspec: "args=[\'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-categorical-hinge.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-categorical-hinge.pbtxt new file mode 100644 index 0000000000..d173fc879f --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-categorical-hinge.pbtxt @@ -0,0 +1,198 @@ +path: "tensorflow.keras.metrics.CategoricalHinge" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "dynamic" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'categorical_hinge\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-cosine-proximity.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-cosine-proximity.pbtxt new file mode 100644 index 0000000000..d095ed42ef --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-cosine-proximity.pbtxt @@ -0,0 +1,198 @@ +path: "tensorflow.keras.metrics.CosineProximity" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "dynamic" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'cosine_proximity\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-hinge.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-hinge.pbtxt new file mode 100644 index 0000000000..7f13c75583 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-hinge.pbtxt @@ -0,0 +1,198 @@ +path: "tensorflow.keras.metrics.Hinge" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "dynamic" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'hinge\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-mean-absolute-error.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-mean-absolute-error.pbtxt new file mode 100644 index 0000000000..896320004c --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-mean-absolute-error.pbtxt @@ -0,0 +1,198 @@ +path: "tensorflow.keras.metrics.MeanAbsoluteError" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "dynamic" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'mean_absolute_error\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-mean-absolute-percentage-error.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-mean-absolute-percentage-error.pbtxt new file mode 100644 index 0000000000..37ab1a409b --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-mean-absolute-percentage-error.pbtxt @@ -0,0 +1,198 @@ +path: "tensorflow.keras.metrics.MeanAbsolutePercentageError" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "dynamic" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'mean_absolute_percentage_error\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-mean-squared-error.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-mean-squared-error.pbtxt new file mode 100644 index 0000000000..9025abbb0a --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-mean-squared-error.pbtxt @@ -0,0 +1,198 @@ +path: "tensorflow.keras.metrics.MeanSquaredError" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "dynamic" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'mean_squared_error\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-mean-squared-logarithmic-error.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-mean-squared-logarithmic-error.pbtxt new file mode 100644 index 0000000000..808853994d --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-mean-squared-logarithmic-error.pbtxt @@ -0,0 +1,198 @@ +path: "tensorflow.keras.metrics.MeanSquaredLogarithmicError" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "dynamic" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'mean_squared_logarithmic_error\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-squared-hinge.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-squared-hinge.pbtxt new file mode 100644 index 0000000000..38f94048cc --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-squared-hinge.pbtxt @@ -0,0 +1,198 @@ +path: "tensorflow.keras.metrics.SquaredHinge" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "dynamic" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'squared_hinge\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.pbtxt index 905021dd79..743a8ae34a 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.pbtxt @@ -12,6 +12,14 @@ tf_module { name: "CategoricalAccuracy" mtype: "" } + member { + name: "CategoricalHinge" + mtype: "" + } + member { + name: "CosineProximity" + mtype: "" + } member { name: "FalseNegatives" mtype: "" @@ -20,10 +28,30 @@ tf_module { name: "FalsePositives" mtype: "" } + member { + name: "Hinge" + mtype: "" + } member { name: "Mean" mtype: "" } + member { + name: "MeanAbsoluteError" + mtype: "" + } + member { + name: "MeanAbsolutePercentageError" + mtype: "" + } + member { + name: "MeanSquaredError" + mtype: "" + } + member { + name: "MeanSquaredLogarithmicError" + mtype: "" + } member { name: "Precision" mtype: "" @@ -44,6 +72,10 @@ tf_module { name: "SpecificityAtSensitivity" mtype: "" } + member { + name: "SquaredHinge" + mtype: "" + } member { name: "TrueNegatives" mtype: "" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-categorical-hinge.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-categorical-hinge.pbtxt new file mode 100644 index 0000000000..4ba9e57bed --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-categorical-hinge.pbtxt @@ -0,0 +1,22 @@ +path: "tensorflow.keras.losses.CategoricalHinge" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member_method { + name: "__init__" + argspec: "args=[\'self\', \'reduction\', \'name\'], varargs=None, keywords=None, defaults=[\'sum_over_batch_size\', \'None\'], " + } + member_method { + name: "call" + argspec: "args=[\'self\', \'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-cosine-proximity.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-cosine-proximity.pbtxt new file mode 100644 index 0000000000..de35966f48 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-cosine-proximity.pbtxt @@ -0,0 +1,22 @@ +path: "tensorflow.keras.losses.CosineProximity" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member_method { + name: "__init__" + argspec: "args=[\'self\', \'reduction\', \'name\'], varargs=None, keywords=None, defaults=[\'sum_over_batch_size\', \'None\'], " + } + member_method { + name: "call" + argspec: "args=[\'self\', \'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-hinge.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-hinge.pbtxt new file mode 100644 index 0000000000..7b3c62d3be --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-hinge.pbtxt @@ -0,0 +1,22 @@ +path: "tensorflow.keras.losses.Hinge" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member_method { + name: "__init__" + argspec: "args=[\'self\', \'reduction\', \'name\'], varargs=None, keywords=None, defaults=[\'sum_over_batch_size\', \'None\'], " + } + member_method { + name: "call" + argspec: "args=[\'self\', \'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-squared-hinge.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-squared-hinge.pbtxt new file mode 100644 index 0000000000..f8badb2b6e --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-squared-hinge.pbtxt @@ -0,0 +1,22 @@ +path: "tensorflow.keras.losses.SquaredHinge" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member_method { + name: "__init__" + argspec: "args=[\'self\', \'reduction\', \'name\'], varargs=None, keywords=None, defaults=[\'sum_over_batch_size\', \'None\'], " + } + member_method { + name: "call" + argspec: "args=[\'self\', \'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.pbtxt index c198096d25..ef4fd66a7a 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.pbtxt @@ -8,6 +8,18 @@ tf_module { name: "CategoricalCrossentropy" mtype: "" } + member { + name: "CategoricalHinge" + mtype: "" + } + member { + name: "CosineProximity" + mtype: "" + } + member { + name: "Hinge" + mtype: "" + } member { name: "MeanAbsoluteError" mtype: "" @@ -28,6 +40,10 @@ tf_module { name: "Reduction" mtype: "" } + member { + name: "SquaredHinge" + mtype: "" + } member_method { name: "KLD" argspec: "args=[\'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-categorical-hinge.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-categorical-hinge.pbtxt new file mode 100644 index 0000000000..d173fc879f --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-categorical-hinge.pbtxt @@ -0,0 +1,198 @@ +path: "tensorflow.keras.metrics.CategoricalHinge" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "dynamic" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'categorical_hinge\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-cosine-proximity.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-cosine-proximity.pbtxt new file mode 100644 index 0000000000..d095ed42ef --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-cosine-proximity.pbtxt @@ -0,0 +1,198 @@ +path: "tensorflow.keras.metrics.CosineProximity" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "dynamic" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'cosine_proximity\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-hinge.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-hinge.pbtxt new file mode 100644 index 0000000000..7f13c75583 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-hinge.pbtxt @@ -0,0 +1,198 @@ +path: "tensorflow.keras.metrics.Hinge" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "dynamic" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'hinge\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-mean-absolute-error.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-mean-absolute-error.pbtxt new file mode 100644 index 0000000000..896320004c --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-mean-absolute-error.pbtxt @@ -0,0 +1,198 @@ +path: "tensorflow.keras.metrics.MeanAbsoluteError" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "dynamic" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'mean_absolute_error\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-mean-absolute-percentage-error.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-mean-absolute-percentage-error.pbtxt new file mode 100644 index 0000000000..37ab1a409b --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-mean-absolute-percentage-error.pbtxt @@ -0,0 +1,198 @@ +path: "tensorflow.keras.metrics.MeanAbsolutePercentageError" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "dynamic" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'mean_absolute_percentage_error\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-mean-squared-error.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-mean-squared-error.pbtxt new file mode 100644 index 0000000000..9025abbb0a --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-mean-squared-error.pbtxt @@ -0,0 +1,198 @@ +path: "tensorflow.keras.metrics.MeanSquaredError" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "dynamic" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'mean_squared_error\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-mean-squared-logarithmic-error.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-mean-squared-logarithmic-error.pbtxt new file mode 100644 index 0000000000..808853994d --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-mean-squared-logarithmic-error.pbtxt @@ -0,0 +1,198 @@ +path: "tensorflow.keras.metrics.MeanSquaredLogarithmicError" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "dynamic" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'mean_squared_logarithmic_error\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-squared-hinge.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-squared-hinge.pbtxt new file mode 100644 index 0000000000..38f94048cc --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-squared-hinge.pbtxt @@ -0,0 +1,198 @@ +path: "tensorflow.keras.metrics.SquaredHinge" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "activity_regularizer" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "dynamic" + mtype: "" + } + member { + name: "inbound_nodes" + mtype: "" + } + member { + name: "input" + mtype: "" + } + member { + name: "input_mask" + mtype: "" + } + member { + name: "input_shape" + mtype: "" + } + member { + name: "losses" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "non_trainable_variables" + mtype: "" + } + member { + name: "non_trainable_weights" + mtype: "" + } + member { + name: "outbound_nodes" + mtype: "" + } + member { + name: "output" + mtype: "" + } + member { + name: "output_mask" + mtype: "" + } + member { + name: "output_shape" + mtype: "" + } + member { + name: "trainable_variables" + mtype: "" + } + member { + name: "trainable_weights" + mtype: "" + } + member { + name: "updates" + mtype: "" + } + member { + name: "variables" + mtype: "" + } + member { + name: "weights" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'squared_hinge\', \'None\'], " + } + member_method { + name: "add_loss" + argspec: "args=[\'self\', \'losses\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_metric" + argspec: "args=[\'self\', \'value\', \'aggregation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " + } + member_method { + name: "add_update" + argspec: "args=[\'self\', \'updates\', \'inputs\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "add_variable" + argspec: "args=[\'self\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "add_weight" + argspec: "args=[\'self\', \'name\', \'shape\', \'aggregation\', \'synchronization\', \'initializer\'], varargs=None, keywords=None, defaults=[\'()\', \'VariableAggregation.SUM\', \'VariableSynchronization.ON_READ\', \'None\'], " + } + member_method { + name: "apply" + argspec: "args=[\'self\', \'inputs\'], varargs=args, keywords=kwargs, defaults=None" + } + member_method { + name: "build" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "call" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=kwargs, defaults=None" + } + member_method { + name: "compute_mask" + argspec: "args=[\'self\', \'inputs\', \'mask\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "compute_output_shape" + argspec: "args=[\'self\', \'input_shape\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "count_params" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "from_config" + argspec: "args=[\'cls\', \'config\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_config" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_input_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_losses_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_mask_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_output_shape_at" + argspec: "args=[\'self\', \'node_index\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_updates_for" + argspec: "args=[\'self\', \'inputs\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "get_weights" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "reset_states" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "result" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "set_weights" + argspec: "args=[\'self\', \'weights\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "update_state" + argspec: "args=[\'self\', \'y_true\', \'y_pred\', \'sample_weight\'], varargs=None, keywords=None, defaults=[\'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.pbtxt index 905021dd79..743a8ae34a 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.pbtxt @@ -12,6 +12,14 @@ tf_module { name: "CategoricalAccuracy" mtype: "" } + member { + name: "CategoricalHinge" + mtype: "" + } + member { + name: "CosineProximity" + mtype: "" + } member { name: "FalseNegatives" mtype: "" @@ -20,10 +28,30 @@ tf_module { name: "FalsePositives" mtype: "" } + member { + name: "Hinge" + mtype: "" + } member { name: "Mean" mtype: "" } + member { + name: "MeanAbsoluteError" + mtype: "" + } + member { + name: "MeanAbsolutePercentageError" + mtype: "" + } + member { + name: "MeanSquaredError" + mtype: "" + } + member { + name: "MeanSquaredLogarithmicError" + mtype: "" + } member { name: "Precision" mtype: "" @@ -44,6 +72,10 @@ tf_module { name: "SpecificityAtSensitivity" mtype: "" } + member { + name: "SquaredHinge" + mtype: "" + } member { name: "TrueNegatives" mtype: "" -- GitLab From 45db95c0211272405d009d9f9d27bf14f469fe09 Mon Sep 17 00:00:00 2001 From: Tim Shen Date: Wed, 26 Dec 2018 15:11:04 -0800 Subject: [PATCH 119/622] Log convolution calls to the custom logger, if the profiling is on. PiperOrigin-RevId: 226958458 --- tensorflow/stream_executor/BUILD | 4 +- tensorflow/stream_executor/cuda/cuda_dnn.cc | 87 +++++++++++++++++++ tensorflow/stream_executor/dnn.cc | 16 ++++ tensorflow/stream_executor/dnn.h | 10 +++ tensorflow/stream_executor/dnn.proto | 26 ++++++ tensorflow/stream_executor/logging.proto | 7 ++ .../stream_executor/stream_executor_pimpl.cc | 28 +----- 7 files changed, 148 insertions(+), 30 deletions(-) diff --git a/tensorflow/stream_executor/BUILD b/tensorflow/stream_executor/BUILD index c43efc799c..4c4fe1c3f2 100644 --- a/tensorflow/stream_executor/BUILD +++ b/tensorflow/stream_executor/BUILD @@ -2,7 +2,6 @@ licenses(["restricted"]) load("@local_config_cuda//cuda:build_defs.bzl", "if_cuda_is_configured") load("//tensorflow/core:platform/default/build_config.bzl", "tf_proto_library") -load("//tensorflow/core:platform/default/build_config.bzl", "tf_additional_all_protos") load("//tensorflow/core:platform/default/build_config_root.bzl", "if_static") load("//tensorflow:tensorflow.bzl", "cc_header_only_library") @@ -20,7 +19,6 @@ tf_proto_library( srcs = ["dnn.proto"], cc_api_version = 2, default_header = True, - protodeps = tf_additional_all_protos(), ) tf_proto_library( @@ -28,7 +26,7 @@ tf_proto_library( srcs = ["logging.proto"], cc_api_version = 2, default_header = True, - protodeps = tf_additional_all_protos(), + protodeps = [":dnn_proto"], ) cc_library( diff --git a/tensorflow/stream_executor/cuda/cuda_dnn.cc b/tensorflow/stream_executor/cuda/cuda_dnn.cc index 249bad0c10..6c7ccc8152 100644 --- a/tensorflow/stream_executor/cuda/cuda_dnn.cc +++ b/tensorflow/stream_executor/cuda/cuda_dnn.cc @@ -23,6 +23,7 @@ limitations under the License. #include "third_party/eigen3/Eigen/Core" #include "tensorflow/core/lib/core/errors.h" #include "tensorflow/core/lib/strings/stringprintf.h" +#include "tensorflow/core/platform/logger.h" #include "tensorflow/core/util/env_var.h" #include "tensorflow/stream_executor/cuda/cuda_activation.h" #include "tensorflow/stream_executor/cuda/cuda_diagnostics.h" @@ -38,6 +39,7 @@ limitations under the License. #include "tensorflow/stream_executor/lib/initialize.h" #include "tensorflow/stream_executor/lib/mathutil.h" #include "tensorflow/stream_executor/lib/threadpool.h" +#include "tensorflow/stream_executor/logging.pb.h" #include "tensorflow/stream_executor/platform/logging.h" #include "tensorflow/stream_executor/plugin_registry.h" #include "tensorflow/stream_executor/scratch_allocator.h" @@ -2569,6 +2571,63 @@ bool ShouldIncludeWinogradNonfusedAlgo( } #endif +template +dnn::ConvolutionProto GenerateConvProto( + dnn::ConvolutionKind kind, const dnn::BatchDescriptor& input_descriptor, + const dnn::FilterDescriptor& filter_descriptor, + const dnn::BatchDescriptor& output_descriptor, dnn::AlgorithmDesc algorithm, + const dnn::ConvolutionDescriptor& convolution_descriptor, double conv_scale, + double side_value_scale, dnn::DataType acc_type, + dnn::ActivationMode activation) { + dnn::ConvolutionProto conv_config; + auto element_type = dnn::ToDataType::value; + + conv_config.set_kind(kind); + *conv_config.mutable_input() = input_descriptor.ToProto(element_type); + *conv_config.mutable_filter() = filter_descriptor.ToProto(element_type); + *conv_config.mutable_output() = output_descriptor.ToProto(element_type); + *conv_config.mutable_algorithm() = algorithm.ToProto(); + *conv_config.mutable_conv_desc() = convolution_descriptor.ToProto(); + conv_config.mutable_conv_desc()->set_compute_mode(acc_type); + conv_config.set_conv_scale(conv_scale); + conv_config.set_side_value_scale(side_value_scale); + conv_config.set_activation(activation); + return conv_config; +} + +void LogCudaProto(const dnn::ConvolutionProto& conv, + StreamExecutor* stream_executor) { + { + // For rolling-out, temporarily cap the number of logs per process. + // TODO(timshen): remove it. + static int count_down = 200; + if (count_down == 0) { + return; + } + count_down--; + } + + ConvLogEntry conv_log; + *conv_log.mutable_convolution() = conv; + auto info = conv_log.mutable_cuda_info(); + int cc_major, cc_minor; + stream_executor->GetDeviceDescription().cuda_compute_capability(&cc_major, + &cc_minor); + info->mutable_compute_capability()->set_major(cc_major); + info->mutable_compute_capability()->set_minor(cc_minor); + + if (auto* dnn = stream_executor->AsDnn()) { + port::StatusOr version_or = dnn->GetVersion(); + if (version_or.ok()) { + const auto& version = version_or.ValueOrDie(); + info->mutable_cudnn_version()->set_major(version.major_version()); + info->mutable_cudnn_version()->set_minor(version.minor_version()); + info->mutable_cudnn_version()->set_patch(version.patch()); + } + } + tensorflow::Logger::Singleton()->LogProto(conv_log); +} + } // namespace template @@ -2673,6 +2732,13 @@ port::Status CudnnSupport::DoConvolveImpl( output_profile_result->set_elapsed_time_in_ms( timer->GetElapsedMilliseconds()); output_profile_result->set_scratch_size(scratch.size()); + + LogCudaProto( + GenerateConvProto(dnn::ConvolutionKind::FORWARD, input_descriptor, + filter_descriptor, output_descriptor, algo_desc, + convolution_descriptor, dalpha, dbeta, + accumulator_type, dnn::ActivationMode::kNone), + stream->parent()); } return port::Status::OK(); @@ -2790,6 +2856,13 @@ port::Status CudnnSupport::DoFusedConvolveImpl( output_profile_result->set_elapsed_time_in_ms( timer->GetElapsedMilliseconds()); output_profile_result->set_scratch_size(scratch.size()); + + LogCudaProto(GenerateConvProto( + dnn::ConvolutionKind::FORWARD, conv_input_descriptor, + filter_descriptor, output_descriptor, algo_desc, + convolution_descriptor, conv_input_scale, side_input_scale, + accumulator_type, activation_mode), + stream->parent()); } return port::Status::OK(); @@ -3350,6 +3423,13 @@ port::Status CudnnSupport::DoConvolveBackwardDataImpl( output_profile_result->set_elapsed_time_in_ms( timer->GetElapsedMilliseconds()); output_profile_result->set_scratch_size(scratch.size()); + + LogCudaProto(GenerateConvProto( + dnn::ConvolutionKind::BACKWARD_DATA, input_descriptor, + filter_descriptor, output_descriptor, algo_desc, + convolution_descriptor, dalpha, dbeta, accumulator_type, + dnn::ActivationMode::kNone), + stream->parent()); } return port::Status::OK(); @@ -3548,6 +3628,13 @@ port::Status CudnnSupport::DoConvolveBackwardFilterImpl( output_profile_result->set_elapsed_time_in_ms( timer->GetElapsedMilliseconds()); output_profile_result->set_scratch_size(scratch.size()); + + LogCudaProto(GenerateConvProto( + dnn::ConvolutionKind::BACKWARD_FILTER, input_descriptor, + filter_descriptor, output_descriptor, algo_desc, + convolution_descriptor, dalpha, dbeta, accumulator_type, + dnn::ActivationMode::kNone), + stream->parent()); } return port::Status::OK(); diff --git a/tensorflow/stream_executor/dnn.cc b/tensorflow/stream_executor/dnn.cc index faa662211e..d91afaa638 100644 --- a/tensorflow/stream_executor/dnn.cc +++ b/tensorflow/stream_executor/dnn.cc @@ -368,6 +368,16 @@ BatchDescriptor BatchDescriptor::DepthConcatenateOutputDescriptor( return output; } +TensorDescriptorProto BatchDescriptor::ToProto(DataType data_type) const { + CHECK_EQ(0.0, value_max_); + CHECK_EQ(0.0, value_min_); + CHECK(quantized_activation_mode_ == QuantizedActivationMode::k8Bit); + + TensorDescriptorProto ret = tensor_; + ret.set_data_type(data_type); + return ret; +} + // -- FilterDescriptor FilterDescriptor::FilterDescriptor(int ndims) { @@ -434,6 +444,12 @@ int64 FilterDescriptor::ComputeWeightCount() const { return ret; } +TensorDescriptorProto FilterDescriptor::ToProto(DataType data_type) const { + TensorDescriptorProto ret = tensor_; + ret.set_data_type(data_type); + return ret; +} + // -- ConvolutionDescriptor ConvolutionDescriptor::ConvolutionDescriptor(int ndims) { diff --git a/tensorflow/stream_executor/dnn.h b/tensorflow/stream_executor/dnn.h index 33ca0ff65a..1001824ed5 100644 --- a/tensorflow/stream_executor/dnn.h +++ b/tensorflow/stream_executor/dnn.h @@ -248,6 +248,12 @@ class BatchDescriptor { string ToString() const; string ToShortString() const; + // Pre-condition: + // value_max_ == 0 + // value_min_ == 0 + // quantized_activation_mode_ == QuantizedActivationMode::k8Bit + TensorDescriptorProto ToProto(DataType data_type) const; + // Accessors. int64 count() const { return tensor_.dimensions(0); } int64 feature_map_count() const { return tensor_.dimensions(1); } @@ -420,6 +426,7 @@ class FilterDescriptor { string ToString() const; string ToShortString() const; + TensorDescriptorProto ToProto(DataType data_type) const; // Returns the number of weights required as parameters for a convolution // using this filter descriptor. @@ -509,6 +516,7 @@ class ConvolutionDescriptor { string ToString() const; string ToShortString() const; + ConvolutionDescriptorProto ToProto() const { return proto_; } ConvolutionDescriptor& set_zero_padding_height(int64 value) { SetDim(padding(), DimIndex::Y, value); @@ -745,6 +753,8 @@ class AlgorithmDesc { } uint64 hash() const; + AlgorithmProto ToProto() const { return proto_; } + private: AlgorithmProto proto_; }; diff --git a/tensorflow/stream_executor/dnn.proto b/tensorflow/stream_executor/dnn.proto index 56b079c3f5..11fb5d0f6a 100644 --- a/tensorflow/stream_executor/dnn.proto +++ b/tensorflow/stream_executor/dnn.proto @@ -66,6 +66,13 @@ enum ConvolutionMode { CONVOLUTION = 1; } +enum ConvolutionKind { + INVALID = 0; + FORWARD = 1; + BACKWARD_FILTER = 2; + BACKWARD_DATA = 3; +} + // Generic tensor representation. message TensorDescriptorProto { repeated int64 dimensions = 1; @@ -101,3 +108,22 @@ message ConvolutionDescriptorProto { int32 group_count = 5; ConvolutionMode convolution_mode = 6; } + +// A convolution. Currently it's only used for logging. In the future, we may +// want to use it in the API as well. +message ConvolutionProto { + ConvolutionKind kind = 1; + TensorDescriptorProto input = 2; + TensorDescriptorProto filter = 3; + TensorDescriptorProto output = 4; + AlgorithmProto algorithm = 5; + ConvolutionDescriptorProto conv_desc = 6; + + // result = conv_scale * conv(...) + side_value_scale * side_value. + // side_value is an arbitrary buffer if activation is not none. Otherwise, it + // has to be the result buffer (using its old values). + double conv_scale = 7; + double side_value_scale = 8; + + ActivationMode activation = 9; +} diff --git a/tensorflow/stream_executor/logging.proto b/tensorflow/stream_executor/logging.proto index 2c75500cda..4b183d9525 100644 --- a/tensorflow/stream_executor/logging.proto +++ b/tensorflow/stream_executor/logging.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package stream_executor; +import "tensorflow/stream_executor/dnn.proto"; + message CudnnVersion { int32 major = 1; int32 minor = 2; @@ -17,3 +19,8 @@ message CudaInfo { CudnnVersion cudnn_version = 1; ComputeCapability compute_capability = 2; } + +message ConvLogEntry { + CudaInfo cuda_info = 1; + dnn.ConvolutionProto convolution = 2; +} diff --git a/tensorflow/stream_executor/stream_executor_pimpl.cc b/tensorflow/stream_executor/stream_executor_pimpl.cc index cb67a906a8..439c73ec8f 100644 --- a/tensorflow/stream_executor/stream_executor_pimpl.cc +++ b/tensorflow/stream_executor/stream_executor_pimpl.cc @@ -23,7 +23,6 @@ limitations under the License. #include #include "absl/strings/str_cat.h" -#include "tensorflow/core/platform/logger.h" #include "tensorflow/core/util/env_var.h" #include "tensorflow/stream_executor/blas.h" #include "tensorflow/stream_executor/fft.h" @@ -34,7 +33,6 @@ limitations under the License. #include "tensorflow/stream_executor/lib/str_util.h" #include "tensorflow/stream_executor/lib/stringprintf.h" #include "tensorflow/stream_executor/lib/threadpool.h" -#include "tensorflow/stream_executor/logging.pb.h" #include "tensorflow/stream_executor/platform/port.h" #include "tensorflow/stream_executor/rng.h" #include "tensorflow/stream_executor/stream_executor_internal.h" @@ -221,31 +219,7 @@ StreamExecutor::~StreamExecutor() { port::Status StreamExecutor::Init(int device_ordinal, DeviceOptions device_options) { device_ordinal_ = device_ordinal; - TF_RETURN_IF_ERROR( - implementation_->Init(device_ordinal, std::move(device_options))); - - if (platform_kind_ == PlatformKind::kCuda) { - CudaInfo info; - - int cc_major, cc_minor; - GetDeviceDescription().cuda_compute_capability(&cc_major, &cc_minor); - info.mutable_compute_capability()->set_major(cc_major); - info.mutable_compute_capability()->set_minor(cc_minor); - - if (auto *dnn = AsDnn()) { - port::StatusOr version_or = dnn->GetVersion(); - if (version_or.ok()) { - const auto &version = version_or.ValueOrDie(); - info.mutable_cudnn_version()->set_major(version.major_version()); - info.mutable_cudnn_version()->set_minor(version.minor_version()); - info.mutable_cudnn_version()->set_patch(version.patch()); - } - } - - tensorflow::Logger::Singleton()->LogProto(info); - } - - return port::Status::OK(); + return implementation_->Init(device_ordinal, std::move(device_options)); } port::Status StreamExecutor::Init() { -- GitLab From 256318791e296b41252f6cd46112a635bed698de Mon Sep 17 00:00:00 2001 From: Stephan Lee Date: Wed, 26 Dec 2018 15:29:04 -0800 Subject: [PATCH 120/622] Fix GFile tf_export for v2. GFile was not properly exposed under io.gfile.GFile. PiperOrigin-RevId: 226959951 --- tensorflow/python/platform/gfile.py | 2 +- .../v1/tensorflow.io.gfile.-g-file.pbtxt | 58 +++++++++++++++++++ .../api/golden/v1/tensorflow.io.gfile.pbtxt | 4 ++ .../v2/tensorflow.io.gfile.-g-file.pbtxt | 58 +++++++++++++++++++ .../api/golden/v2/tensorflow.io.gfile.pbtxt | 4 ++ tensorflow/tools/compatibility/renames_v2.py | 4 +- 6 files changed, 127 insertions(+), 3 deletions(-) create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.io.gfile.-g-file.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.io.gfile.-g-file.pbtxt diff --git a/tensorflow/python/platform/gfile.py b/tensorflow/python/platform/gfile.py index d0159e9e98..dd2c615e9e 100644 --- a/tensorflow/python/platform/gfile.py +++ b/tensorflow/python/platform/gfile.py @@ -37,7 +37,7 @@ from tensorflow.python.util.deprecation import deprecated from tensorflow.python.util.tf_export import tf_export -@tf_export(v1=['gfile.GFile', 'gfile.Open'], v2=['io.gfile.GFile']) +@tf_export('io.gfile.GFile', v1=['gfile.GFile', 'gfile.Open', 'io.gfile.GFile']) class GFile(_FileIO): """File I/O wrappers without thread locking. diff --git a/tensorflow/tools/api/golden/v1/tensorflow.io.gfile.-g-file.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.io.gfile.-g-file.pbtxt new file mode 100644 index 0000000000..c6bf57a88f --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.io.gfile.-g-file.pbtxt @@ -0,0 +1,58 @@ +path: "tensorflow.io.gfile.GFile" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "mode" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'mode\'], varargs=None, keywords=None, defaults=[\'r\'], " + } + member_method { + name: "close" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "flush" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "next" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "read" + argspec: "args=[\'self\', \'n\'], varargs=None, keywords=None, defaults=[\'-1\'], " + } + member_method { + name: "readline" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "readlines" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "seek" + argspec: "args=[\'self\', \'offset\', \'whence\', \'position\'], varargs=None, keywords=None, defaults=[\'None\', \'0\', \'None\'], " + } + member_method { + name: "size" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "tell" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "write" + argspec: "args=[\'self\', \'file_content\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.io.gfile.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.io.gfile.pbtxt index cfa3372b12..a797c06ff3 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.io.gfile.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.io.gfile.pbtxt @@ -1,5 +1,9 @@ path: "tensorflow.io.gfile" tf_module { + member { + name: "GFile" + mtype: "" + } member_method { name: "copy" argspec: "args=[\'src\', \'dst\', \'overwrite\'], varargs=None, keywords=None, defaults=[\'False\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.io.gfile.-g-file.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.io.gfile.-g-file.pbtxt new file mode 100644 index 0000000000..c6bf57a88f --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.io.gfile.-g-file.pbtxt @@ -0,0 +1,58 @@ +path: "tensorflow.io.gfile.GFile" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "mode" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'name\', \'mode\'], varargs=None, keywords=None, defaults=[\'r\'], " + } + member_method { + name: "close" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "flush" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "next" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "read" + argspec: "args=[\'self\', \'n\'], varargs=None, keywords=None, defaults=[\'-1\'], " + } + member_method { + name: "readline" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "readlines" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "seek" + argspec: "args=[\'self\', \'offset\', \'whence\', \'position\'], varargs=None, keywords=None, defaults=[\'None\', \'0\', \'None\'], " + } + member_method { + name: "size" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "tell" + argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" + } + member_method { + name: "write" + argspec: "args=[\'self\', \'file_content\'], varargs=None, keywords=None, defaults=None" + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.io.gfile.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.io.gfile.pbtxt index cfa3372b12..a797c06ff3 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.io.gfile.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.io.gfile.pbtxt @@ -1,5 +1,9 @@ path: "tensorflow.io.gfile" tf_module { + member { + name: "GFile" + mtype: "" + } member_method { name: "copy" argspec: "args=[\'src\', \'dst\', \'overwrite\'], varargs=None, keywords=None, defaults=[\'False\'], " diff --git a/tensorflow/tools/compatibility/renames_v2.py b/tensorflow/tools/compatibility/renames_v2.py index dba4a5d5a3..32fb2a28cc 100644 --- a/tensorflow/tools/compatibility/renames_v2.py +++ b/tensorflow/tools/compatibility/renames_v2.py @@ -198,8 +198,8 @@ renames = { 'tf.get_variable': 'tf.compat.v1.get_variable', 'tf.get_variable_scope': 'tf.compat.v1.get_variable_scope', 'tf.gfile.FastGFile': 'tf.compat.v1.gfile.FastGFile', - 'tf.gfile.GFile': 'tf.compat.v1.gfile.GFile', - 'tf.gfile.Open': 'tf.compat.v1.gfile.Open', + 'tf.gfile.GFile': 'tf.io.gfile.GFile', + 'tf.gfile.Open': 'tf.io.gfile.GFile', 'tf.global_norm': 'tf.linalg.global_norm', 'tf.global_variables': 'tf.compat.v1.global_variables', 'tf.global_variables_initializer': 'tf.compat.v1.global_variables_initializer', -- GitLab From bd9a1af3e5ba795be7d8ca7e50d50d389cbd58b9 Mon Sep 17 00:00:00 2001 From: Martin Wicke Date: Wed, 26 Dec 2018 16:10:24 -0800 Subject: [PATCH 121/622] Fix breakage introduced by removing contrib dependencies. PiperOrigin-RevId: 226963770 --- tensorflow/contrib/framework/BUILD | 1 + 1 file changed, 1 insertion(+) diff --git a/tensorflow/contrib/framework/BUILD b/tensorflow/contrib/framework/BUILD index dad50a3a73..88a14a2a94 100644 --- a/tensorflow/contrib/framework/BUILD +++ b/tensorflow/contrib/framework/BUILD @@ -50,6 +50,7 @@ tf_custom_op_py_library( visibility = [ "//learning/brain:__subpackages__", "//tensorflow:__subpackages__", + "//tensorflow_estimator:__subpackages__", "//video/youtube/personalization:__subpackages__", ], deps = [ -- GitLab From badd347a0e159353fc0ea90e5d3936a13de610e7 Mon Sep 17 00:00:00 2001 From: Tong Shen Date: Wed, 26 Dec 2018 16:28:58 -0800 Subject: [PATCH 122/622] Use outside compilation in TPUEstimator.export_savedmodel(). PiperOrigin-RevId: 226965198 --- .../contrib/tpu/python/tpu/feature_column.py | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/tensorflow/contrib/tpu/python/tpu/feature_column.py b/tensorflow/contrib/tpu/python/tpu/feature_column.py index d5d00d628d..8edf131bc2 100644 --- a/tensorflow/contrib/tpu/python/tpu/feature_column.py +++ b/tensorflow/contrib/tpu/python/tpu/feature_column.py @@ -17,7 +17,6 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -import contextlib import math from tensorflow.contrib.tpu.python.tpu import tpu @@ -279,11 +278,10 @@ class _TPUEmbeddingColumn(_TPUBaseEmbeddingColumn, fc._EmbeddingColumn): def _get_dense_tensor(self, inputs, weight_collections=None, trainable=None): if tpu.under_tpu_inference_context(): - # TODO(shizhiw, b/112012627, b/112336539): Replace _outside_all_rewrites() - # with outside compilation. - with _outside_all_rewrites(): + def host_computation(): return fc._EmbeddingColumn._get_dense_tensor( self, inputs, weight_collections, trainable) + return tpu.outside_compilation(host_computation) if _is_running_on_cpu(): return fc._EmbeddingColumn._get_dense_tensor( @@ -300,13 +298,6 @@ class _TPUEmbeddingColumn(_TPUBaseEmbeddingColumn, fc._EmbeddingColumn): return tensor -@contextlib.contextmanager -def _outside_all_rewrites(): - """'Break out' of a tpu.rewrite() (or shard(), etc.).""" - with ops.control_dependencies(None): - yield - - class _TPUSharedEmbeddingColumn(_TPUBaseEmbeddingColumn, fc._SharedEmbeddingColumn): """Core Shared Embedding Column.""" @@ -385,11 +376,10 @@ class _TPUSharedEmbeddingColumn(_TPUBaseEmbeddingColumn, def _get_dense_tensor(self, inputs, weight_collections=None, trainable=None): if tpu.under_tpu_inference_context(): - # TODO(shizhiw, b/112012627, b/112336539): Replace _outside_all_rewrites() - # with outside compilation. - with _outside_all_rewrites(): + def host_computation(): return fc._SharedEmbeddingColumn._get_dense_tensor( self, inputs, weight_collections, trainable) + return tpu.outside_compilation(host_computation) if _is_running_on_cpu(): return fc._SharedEmbeddingColumn._get_dense_tensor( -- GitLab From 9b91d454a435fb2e2e5771168e4aba9d6e5a9474 Mon Sep 17 00:00:00 2001 From: Martin Wicke Date: Wed, 26 Dec 2018 16:32:42 -0800 Subject: [PATCH 123/622] Properly switch BatchNormalization layer on enable_v2. Moving v2 compatibility code to its own file (cleaner, but also to avoid circular dependencies). PiperOrigin-RevId: 226965517 --- tensorflow/api_template.__init__.py | 2 +- tensorflow/python/BUILD | 1 + tensorflow/python/__init__.py | 2 + tensorflow/python/compat/BUILD | 14 +++- tensorflow/python/compat/compat.py | 42 ------------ .../python/compat/disable_v2_behavior_test.py | 6 +- tensorflow/python/compat/v2_compat.py | 64 +++++++++++++++++++ .../python/keras/layers/normalization.py | 20 +++++- .../tools/api/golden/v1/tensorflow.pbtxt | 8 +++ tensorflow/tools/compatibility/renames_v2.py | 2 + 10 files changed, 111 insertions(+), 50 deletions(-) create mode 100644 tensorflow/python/compat/v2_compat.py diff --git a/tensorflow/api_template.__init__.py b/tensorflow/api_template.__init__.py index 2c0a745269..a93799bfe8 100644 --- a/tensorflow/api_template.__init__.py +++ b/tensorflow/api_template.__init__.py @@ -52,7 +52,7 @@ elif _tf_api_dir not in __path__: __path__.append(_tf_api_dir) # Enable TF2 behaviors -from tensorflow.python.compat import compat as _compat # pylint: disable=g-import-not-at-top +from tensorflow.python.compat import v2_compat as _compat # pylint: disable=g-import-not-at-top _compat.enable_v2_behavior() diff --git a/tensorflow/python/BUILD b/tensorflow/python/BUILD index b31396da4b..9c50865164 100644 --- a/tensorflow/python/BUILD +++ b/tensorflow/python/BUILD @@ -168,6 +168,7 @@ py_library( "//tensorflow/core:protos_all_py", "//tensorflow/lite/python:lite", "//tensorflow/python/compat", + "//tensorflow/python/compat:v2_compat", "//tensorflow/python/data", "//tensorflow/python/distribute", "//tensorflow/python/distribute:estimator_training", diff --git a/tensorflow/python/__init__.py b/tensorflow/python/__init__.py index 9f1e52b42b..207123cb14 100644 --- a/tensorflow/python/__init__.py +++ b/tensorflow/python/__init__.py @@ -121,6 +121,8 @@ from tensorflow.python.platform import resource_loader from tensorflow.python.platform import sysconfig from tensorflow.python.platform import test +from tensorflow.python.compat import v2_compat + from tensorflow.python.util.all_util import make_all from tensorflow.python.util.tf_export import tf_export diff --git a/tensorflow/python/compat/BUILD b/tensorflow/python/compat/BUILD index f9a57b9c05..87dd5d7f66 100644 --- a/tensorflow/python/compat/BUILD +++ b/tensorflow/python/compat/BUILD @@ -4,13 +4,23 @@ exports_files(["LICENSE"]) load("//tensorflow:tensorflow.bzl", "tf_py_test") +py_library( + name = "v2_compat", + srcs = ["v2_compat.py"], + srcs_version = "PY2AND3", + visibility = ["//tensorflow:internal"], + deps = [ + "//tensorflow/python:tf2", + "//tensorflow/python:util", + ], +) + py_library( name = "compat", srcs = ["compat.py"], srcs_version = "PY2AND3", visibility = ["//tensorflow:internal"], deps = [ - "//tensorflow/python:tf2", "//tensorflow/python:util", ], ) @@ -30,7 +40,7 @@ tf_py_test( size = "small", srcs = ["disable_v2_behavior_test.py"], additional_deps = [ - ":compat", + ":v2_compat", "//tensorflow/python:framework", "//tensorflow/python:client_testlib", ], diff --git a/tensorflow/python/compat/compat.py b/tensorflow/python/compat/compat.py index 3477576d2f..fb1a729578 100644 --- a/tensorflow/python/compat/compat.py +++ b/tensorflow/python/compat/compat.py @@ -24,11 +24,6 @@ from __future__ import print_function import datetime -from tensorflow.python import tf2 -from tensorflow.python.framework import ops -from tensorflow.python.framework import tensor_shape -from tensorflow.python.ops import variable_scope - from tensorflow.python.util import tf_contextlib from tensorflow.python.util.tf_export import tf_export @@ -138,40 +133,3 @@ def forward_compatibility_horizon(year, month, day): yield finally: _FORWARD_COMPATIBILITY_HORIZON = old_compat_date - - -@tf_export(v1=["enable_v2_behavior"]) -def enable_v2_behavior(): - """Enables TensorFlow 2.x behaviors. - - This function can be called at the beginning of the program (before `Tensors`, - `Graphs` or other structures have been created, and before devices have been - initialized. It switches all global behaviors that are different between - TensorFlow 1.x and 2.x to behave as intended for 2.x. - - This function is called in the main TensorFlow `__init__.py` file, user should - not need to call it, except during complex migrations. - """ - tf2.enable() # Switches TensorArrayV2 and control flow V2 - ops.enable_eager_execution() - tensor_shape.enable_v2_tensorshape() # Also switched by tf2 - variable_scope.enable_resource_variables() - - -@tf_export(v1=["disable_v2_behavior"]) -def disable_v2_behavior(): - """Disables TensorFlow 2.x behaviors. - - This function can be called at the beginning of the program (before `Tensors`, - `Graphs` or other structures have been created, and before devices have been - initialized. It switches all global behaviors that are different between - TensorFlow 1.x and 2.x to behave as intended for 1.x. - - User can call this function to disable 2.x behavior during complex migrations. - """ - tf2.disable() # Switches TensorArrayV2 and control flow V2 - ops.disable_eager_execution() - tensor_shape.disable_v2_tensorshape() # Also switched by tf2 - variable_scope.disable_resource_variables() - - diff --git a/tensorflow/python/compat/disable_v2_behavior_test.py b/tensorflow/python/compat/disable_v2_behavior_test.py index 221691b483..c247eac395 100644 --- a/tensorflow/python/compat/disable_v2_behavior_test.py +++ b/tensorflow/python/compat/disable_v2_behavior_test.py @@ -18,7 +18,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -from tensorflow.python.compat import compat +from tensorflow.python.compat import v2_compat from tensorflow.python.framework import constant_op from tensorflow.python.framework import ops from tensorflow.python.platform import test @@ -29,11 +29,11 @@ class DisableV2BehaviorTest(test.TestCase): def test_basic(self): t = constant_op.constant([1, 2, 3]) # creates a hidden context self.assertTrue(isinstance(t, ops.EagerTensor)) - compat.disable_v2_behavior() + v2_compat.disable_v2_behavior() t = constant_op.constant([1, 2, 3]) self.assertFalse(isinstance(t, ops.EagerTensor)) if __name__ == '__main__': - compat.enable_v2_behavior() + v2_compat.enable_v2_behavior() test.main() diff --git a/tensorflow/python/compat/v2_compat.py b/tensorflow/python/compat/v2_compat.py new file mode 100644 index 0000000000..8a94939ae1 --- /dev/null +++ b/tensorflow/python/compat/v2_compat.py @@ -0,0 +1,64 @@ +# Copyright 2018 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. +# ============================================================================== +"""Switching v2 features on and off.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python import tf2 +from tensorflow.python.framework import ops +from tensorflow.python.framework import tensor_shape +from tensorflow.python.keras.layers import normalization +from tensorflow.python.ops import variable_scope + +from tensorflow.python.util.tf_export import tf_export + + +@tf_export(v1=["enable_v2_behavior"]) +def enable_v2_behavior(): + """Enables TensorFlow 2.x behaviors. + + This function can be called at the beginning of the program (before `Tensors`, + `Graphs` or other structures have been created, and before devices have been + initialized. It switches all global behaviors that are different between + TensorFlow 1.x and 2.x to behave as intended for 2.x. + + This function is called in the main TensorFlow `__init__.py` file, user should + not need to call it, except during complex migrations. + """ + tf2.enable() # Switches TensorArrayV2 and control flow V2 + ops.enable_eager_execution() + tensor_shape.enable_v2_tensorshape() # Also switched by tf2 + variable_scope.enable_resource_variables() + normalization.enable_v2_batch_normalization() + + +@tf_export(v1=["disable_v2_behavior"]) +def disable_v2_behavior(): + """Disables TensorFlow 2.x behaviors. + + This function can be called at the beginning of the program (before `Tensors`, + `Graphs` or other structures have been created, and before devices have been + initialized. It switches all global behaviors that are different between + TensorFlow 1.x and 2.x to behave as intended for 1.x. + + User can call this function to disable 2.x behavior during complex migrations. + """ + tf2.disable() # Switches TensorArrayV2 and control flow V2 + ops.disable_eager_execution() + tensor_shape.disable_v2_tensorshape() # Also switched by tf2 + variable_scope.disable_resource_variables() + normalization.disable_v2_batch_normalization() diff --git a/tensorflow/python/keras/layers/normalization.py b/tensorflow/python/keras/layers/normalization.py index 2e68aedbed..84347d38ef 100644 --- a/tensorflow/python/keras/layers/normalization.py +++ b/tensorflow/python/keras/layers/normalization.py @@ -42,6 +42,7 @@ from tensorflow.python.ops import state_ops from tensorflow.python.ops import variables as tf_variables from tensorflow.python.platform import tf_logging as logging from tensorflow.python.util.tf_export import keras_export +from tensorflow.python.util.tf_export import tf_export @keras_export('keras.layers.BatchNormalization', v1=[]) @@ -797,7 +798,22 @@ class BatchNormalizationV1(BatchNormalizationV2): _USE_V2_BEHAVIOR = False -if tf2.enabled(): +BatchNormalization = None # pylint: disable=invalid-name + + +@tf_export(v1=['enable_v2_batch_normalization']) +def enable_v2_batch_normalization(): + global BatchNormalization # pylint: disable=invalid-name BatchNormalization = BatchNormalizationV2 -else: + + +@tf_export(v1=['disable_v2_batch_normalization']) +def disable_v2_batch_normalization(): + global BatchNormalization # pylint: disable=invalid-name BatchNormalization = BatchNormalizationV1 + + +if tf2.enabled(): + enable_v2_batch_normalization() +else: + disable_v2_batch_normalization() diff --git a/tensorflow/tools/api/golden/v1/tensorflow.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.pbtxt index caa15280d1..deb1d100e7 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.pbtxt @@ -1080,6 +1080,10 @@ tf_module { name: "disable_resource_variables" argspec: "args=[], varargs=None, keywords=None, defaults=None" } + member_method { + name: "disable_v2_batch_normalization" + argspec: "args=[], varargs=None, keywords=None, defaults=None" + } member_method { name: "disable_v2_behavior" argspec: "args=[], varargs=None, keywords=None, defaults=None" @@ -1124,6 +1128,10 @@ tf_module { name: "enable_resource_variables" argspec: "args=[], varargs=None, keywords=None, defaults=None" } + member_method { + name: "enable_v2_batch_normalization" + argspec: "args=[], varargs=None, keywords=None, defaults=None" + } member_method { name: "enable_v2_behavior" argspec: "args=[], varargs=None, keywords=None, defaults=None" diff --git a/tensorflow/tools/compatibility/renames_v2.py b/tensorflow/tools/compatibility/renames_v2.py index 32fb2a28cc..5ff8a86b91 100644 --- a/tensorflow/tools/compatibility/renames_v2.py +++ b/tensorflow/tools/compatibility/renames_v2.py @@ -145,6 +145,7 @@ renames = { 'tf.dimension_value': 'tf.compat.v1.dimension_value', 'tf.disable_eager_execution': 'tf.compat.v1.disable_eager_execution', 'tf.disable_resource_variables': 'tf.compat.v1.disable_resource_variables', + 'tf.disable_v2_batch_normalization': 'tf.compat.v1.disable_v2_batch_normalization', 'tf.disable_v2_behavior': 'tf.compat.v1.disable_v2_behavior', 'tf.disable_v2_tensorshape': 'tf.compat.v1.disable_v2_tensorshape', 'tf.distributions.Bernoulli': 'tf.compat.v1.distributions.Bernoulli', @@ -168,6 +169,7 @@ renames = { 'tf.div': 'tf.compat.v1.div', 'tf.enable_eager_execution': 'tf.compat.v1.enable_eager_execution', 'tf.enable_resource_variables': 'tf.compat.v1.enable_resource_variables', + 'tf.enable_v2_batch_normalization': 'tf.compat.v1.enable_v2_batch_normalization', 'tf.enable_v2_behavior': 'tf.compat.v1.enable_v2_behavior', 'tf.enable_v2_tensorshape': 'tf.compat.v1.enable_v2_tensorshape', 'tf.encode_base64': 'tf.io.encode_base64', -- GitLab From a17aabdc179aa0c4cb81a8b5ea4b5a5ac70545a0 Mon Sep 17 00:00:00 2001 From: Gaurav Jain Date: Wed, 26 Dec 2018 16:34:50 -0800 Subject: [PATCH 124/622] Expose scatter_nd_sub for resource variables. In addition, fix up consistency issues in the documentation of scatter_nd_add. PiperOrigin-RevId: 226965693 --- .../compiler/tf2xla/kernels/variable_ops.cc | 14 ++++ .../tf2xla/resource_operation_table.cc | 1 + .../api_def_ResourceScatterNdAdd.pbtxt | 24 +++---- .../api_def_ResourceScatterNdSub.pbtxt | 67 +++++++++++++++++++ .../base_api/api_def_ScatterNdAdd.pbtxt | 28 ++++---- .../base_api/api_def_ScatterNdSub.pbtxt | 22 +++--- .../api_def_ResourceScatterNdSub.pbtxt | 4 ++ tensorflow/core/kernels/scatter_nd_op.cc | 4 +- tensorflow/core/ops/state_ops.cc | 9 +++ .../resource_variable_ops_test.py | 15 ++++- .../kernel_tests/scatter_nd_ops_test.py | 2 +- tensorflow/python/ops/state_ops.py | 32 ++++----- 12 files changed, 166 insertions(+), 56 deletions(-) create mode 100644 tensorflow/core/api_def/base_api/api_def_ResourceScatterNdSub.pbtxt create mode 100644 tensorflow/core/api_def/python_api/api_def_ResourceScatterNdSub.pbtxt diff --git a/tensorflow/compiler/tf2xla/kernels/variable_ops.cc b/tensorflow/compiler/tf2xla/kernels/variable_ops.cc index 2c92a585f5..dfa09b1608 100644 --- a/tensorflow/compiler/tf2xla/kernels/variable_ops.cc +++ b/tensorflow/compiler/tf2xla/kernels/variable_ops.cc @@ -291,5 +291,19 @@ class ResourceScatterNdAddOp : public ResourceScatterOp { }; REGISTER_XLA_OP(Name("ResourceScatterNdAdd"), ResourceScatterNdAddOp); +class ResourceScatterNdSubOp : public ResourceScatterOp { + public: + explicit ResourceScatterNdSubOp(OpKernelConstruction* context) + : ResourceScatterOp(context, /*indices_are_vectors=*/true, + /*combiner=*/Combine) {} + + private: + static xla::XlaOp Combine(const xla::XlaOp& x, const xla::XlaOp& y, + xla::XlaBuilder* builder) { + return xla::Sub(x, y); + } +}; +REGISTER_XLA_OP(Name("ResourceScatterNdSub"), ResourceScatterNdSubOp); + } // namespace } // namespace tensorflow diff --git a/tensorflow/compiler/tf2xla/resource_operation_table.cc b/tensorflow/compiler/tf2xla/resource_operation_table.cc index ff9f1b9ccb..c20d6a5fd1 100644 --- a/tensorflow/compiler/tf2xla/resource_operation_table.cc +++ b/tensorflow/compiler/tf2xla/resource_operation_table.cc @@ -77,6 +77,7 @@ CreateResourceOpInfoMap() { add("ResourceScatterMin" , kReadWrite, kVariable); add("ResourceScatterMul" , kReadWrite, kVariable); add("ResourceScatterNdAdd" , kReadWrite, kVariable); + add("ResourceScatterNdSub" , kReadWrite, kVariable); add("ResourceScatterNdUpdate" , kReadWrite, kVariable); add("ResourceScatterSub" , kReadWrite, kVariable); add("ResourceScatterUpdate" , kReadWrite, kVariable); diff --git a/tensorflow/core/api_def/base_api/api_def_ResourceScatterNdAdd.pbtxt b/tensorflow/core/api_def/base_api/api_def_ResourceScatterNdAdd.pbtxt index d9c4d5a4a4..b0458207e6 100644 --- a/tensorflow/core/api_def/base_api/api_def_ResourceScatterNdAdd.pbtxt +++ b/tensorflow/core/api_def/base_api/api_def_ResourceScatterNdAdd.pbtxt @@ -28,10 +28,8 @@ be protected by a lock; otherwise the behavior is undefined, but may exhibit less contention. END } - summary: "Adds sparse `updates` to individual values or slices within a given" + summary: "Applies sparse addition to individual values or slices in a Variable." description: <

    This only creates the object. Computation is performed once - * {@link ANeuralNetworksExecution_startCompute} is invoked. - * - * The model should be constructed with calls to - * {@link ANeuralNetworksModel_addOperation} and - * {@link ANeuralNetworksModel_addOperand} - * - *

    {@link ANeuralNetworksModel_finish} should be called once the model - * has been fully constructed.

    - * - *

    {@link ANeuralNetworksModel_free} should be called once the model - * is no longer needed.

    - * - * @param model The {@link ANeuralNetworksModel} to be created. - * Set to NULL if unsuccessful. - * - * @return ANEURALNETWORKS_NO_ERROR if successful. - */ -inline int ANeuralNetworksModel_create(ANeuralNetworksModel** model) { - LOAD_FUNCTION(ANeuralNetworksModel_create); - EXECUTE_FUNCTION_RETURN(model); -} - -/** - * Destroy a model. - * - * The model need not have been finished by a call to - * {@link ANeuralNetworksModel_finish}. - * - * See {@link ANeuralNetworksModel} for information on multithreaded usage. - * - * @param model The model to be destroyed. Passing NULL is acceptable and - * results in no operation. - */ -inline void ANeuralNetworksModel_free(ANeuralNetworksModel* model) { - LOAD_FUNCTION(ANeuralNetworksModel_free); - EXECUTE_FUNCTION(model); -} - -/** - * Indicate that we have finished modifying a model. Required before - * calling {@link ANeuralNetworksCompilation_compile}. - * - * An application is responsible to make sure that no other thread uses - * the model at the same time. - * - * See {@link ANeuralNetworksModel} for information on multithreaded usage. - * - * @param model The model to be finished. - * - * @return ANEURALNETWORKS_NO_ERROR if successful. - */ -inline int ANeuralNetworksModel_finish(ANeuralNetworksModel* model) { - LOAD_FUNCTION(ANeuralNetworksModel_finish); - EXECUTE_FUNCTION_RETURN(model); -} - -/** - * Add an operand to a model. - * - * The order in which the operands are added is important. The first one added - * to a model will have the index value 0, the second 1, etc. These indexes are - * used as operand identifiers in {@link ANeuralNetworksModel_addOperation}, - * {@link ANeuralNetworksExecution_setInput}, - * {@link ANeuralNetworksExecution_setInputFromMemory}, - * {@link ANeuralNetworksExecution_setOutput}, - * {@link ANeuralNetworksExecution_setOutputFromMemory} and - * {@link ANeuralNetworksExecution_setOperandValue}. - * - * To build a model that can accommodate inputs of various sizes, as you may - * want to do for a CNN, set the size of the dimensions that will vary at run - * time to 0. If you do so, provide the full dimensions when calling - * {@link ANeuralNetworksExecution_setInput} or {@link - * ANeuralNetworksExecution_setInputFromMemory}. - * - * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has - * been called will return an error. - * - * See {@link ANeuralNetworksModel} for information on multithreaded usage. - * - * @param model The model to be modified. - * @param type The {@link ANeuralNetworksOperandType} that describes the shape - * of the operand. - * - * @return ANEURALNETWORKS_NO_ERROR if successful. - */ -inline int ANeuralNetworksModel_addOperand( - ANeuralNetworksModel* model, const ANeuralNetworksOperandType* type) { - LOAD_FUNCTION(ANeuralNetworksModel_addOperand); - EXECUTE_FUNCTION_RETURN(model, type); -} - -/** - * Sets an operand to a constant value. - * - * For scalar values, the content of buffer is copied into the model. - * - * For tensor values, a pointer to the buffer is stored within the model. - * The application is responsible for not changing the content of this region - * until all executions using this model have completed. As the data may - * be copied during processing, modifying the data after this call yields - * undefined results. - * - * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has - * been called will return an error. - * - * See {@link ANeuralNetworksModel} for information on multithreaded usage. - * - * @param model The model to be modified. - * @param index The index of the model operand we're setting. - * @param buffer A pointer to the data to use. - * @param length The size in bytes of the data value. - * - * @return ANEURALNETWORKS_NO_ERROR if successful. - */ -inline int ANeuralNetworksModel_setOperandValue(ANeuralNetworksModel* model, - int32_t index, - const void* buffer, - size_t length) { - LOAD_FUNCTION(ANeuralNetworksModel_setOperandValue); - EXECUTE_FUNCTION_RETURN(model, index, buffer, length); -} - -/** - * Sets an operand to a value stored in a memory object. - * - * The content of the memory is not copied. A reference to that memory is stored - * inside the model. The application is responsible for not changing the content - * of the memory region until all executions using this model have completed. - * As the data may be copied during processing, modifying the data after this - * call yields undefined results. - * - * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has - * been called will return an error. - * - * See {@link ANeuralNetworksModel} for information on multithreaded usage. - * - * @param model The model to be modified. - * @param index The index of the model operand we're setting. - * @param buffer A pointer to the data to use. - * @param memory The memory containing the data. - * @param offset This specifies the location of the data within the memory. - * The offset is in bytes from the start of memory. - * @param length The size in bytes of the data value. - * - * @return ANEURALNETWORKS_NO_ERROR if successful. - */ -inline int ANeuralNetworksModel_setOperandValueFromMemory( - ANeuralNetworksModel* model, int32_t index, - const ANeuralNetworksMemory* memory, size_t offset, size_t length) { - LOAD_FUNCTION(ANeuralNetworksModel_setOperandValueFromMemory); - EXECUTE_FUNCTION_RETURN(model, index, memory, offset, length); -} - -/** - * Add an operation to a model. - * - * @param model The model to be modified. - * @param type The type of the operation. - * @param inputCount The number of entries in the inputs array. - * @param inputs An array of indexes identifying each operand. - * @param outputCount The number of entries in the outputs array. - * @param outputs An array of indexes identifying each operand. - * - * The operands specified by inputs and outputs must have been - * previously added by calls to {@link ANeuralNetworksModel_addOperand}. - * - * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has - * been called will return an error. - * - * See {@link ANeuralNetworksModel} for information on multithreaded usage. - * - * @return ANEURALNETWORKS_NO_ERROR if successful. - */ -inline int ANeuralNetworksModel_addOperation(ANeuralNetworksModel* model, - ANeuralNetworksOperationType type, - uint32_t inputCount, - const uint32_t* inputs, - uint32_t outputCount, - const uint32_t* outputs) { - LOAD_FUNCTION(ANeuralNetworksModel_addOperation); - EXECUTE_FUNCTION_RETURN(model, type, inputCount, inputs, outputCount, - outputs); -} - -/** - * Specifies which operands will be the model's inputs and outputs. - * - * An operand cannot be used for both input and output. Doing so will - * return an error. - * - * @param model The model to be modified. - * @param inputCount The number of entries in the inputs array. - * @param inputs An array of indexes identifying the input operands. - * @param outputCount The number of entries in the outputs array. - * @param outputs An array of indexes identifying the output operands. - * - * The operands specified by inputs and outputs must have been - * previously added by calls to {@link ANeuralNetworksModel_addOperand}. - * - * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has - * been called will return an error. - * - * See {@link ANeuralNetworksModel} for information on multithreaded usage. - * - */ -inline int ANeuralNetworksModel_identifyInputsAndOutputs( - ANeuralNetworksModel* model, uint32_t inputCount, const uint32_t* inputs, - uint32_t outputCount, const uint32_t* outputs) { - LOAD_FUNCTION(ANeuralNetworksModel_identifyInputsAndOutputs); - EXECUTE_FUNCTION_RETURN(model, inputCount, inputs, outputCount, outputs); -} - -/** - * Specifies whether {@link ANEURALNETWORKS_TENSOR_FLOAT32} is allowed to be - * calculated with range and/or precision as low as that of the IEEE 754 16-bit - * floating-point format. By default, {@link ANEURALNETWORKS_TENSOR_FLOAT32} - * must be calculated using at least the range and precision of the IEEE 754 - * 32-bit floating-point format. - * - * @param model The model to be modified. - * @param allow 'true' indicates {@link ANEURALNETWORKS_TENSOR_FLOAT32} may be - * calculated with range and/or precision as low as that of the - * IEEE 754 16-bit floating point format. 'false' indicates - * {@link ANEURALNETWORKS_TENSOR_FLOAT32} must be calculated using - * at least the range and precision of the IEEE 754 32-bit floating - * point format. - * - * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has - * been called will return an error. - * - * Available since API level 28. - * - * See {@link ANeuralNetworksModel} for information on multithreaded usage. - */ -inline int ANeuralNetworksModel_relaxComputationFloat32toFloat16( - ANeuralNetworksModel* model, bool allow) { - LOAD_FUNCTION(ANeuralNetworksModel_relaxComputationFloat32toFloat16); - EXECUTE_FUNCTION_RETURN(model, allow); -} - -/** - * Create a {@link ANeuralNetworksCompilation} to compile the given model. - * This only creates the object. Compilation is only performed once - * {@link ANeuralNetworksCompilation_start} is invoked. - * - *

    The provided model must outlive the compilation.

    - * - * The model must already have been finished by a call to - * {@link ANeuralNetworksModel_finish}. - * - * See {@link ANeuralNetworksCompilation} for information on multithreaded - * usage. - * - * @param model The {@link ANeuralNetworksModel} to be compiled. - * @param compilation The newly created object or NULL if unsuccessful. - * - * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA - * if the model is invalid. - */ -inline int ANeuralNetworksCompilation_create( - ANeuralNetworksModel* model, ANeuralNetworksCompilation** compilation) { - LOAD_FUNCTION(ANeuralNetworksCompilation_create); - EXECUTE_FUNCTION_RETURN(model, compilation); -} - -/** - * Destroy a compilation. - * - *

    If called on a compilation for which - * {@link ANeuralNetworksCompilation_start} has been called, the - * function will return immediately but will mark the compilation to be deleted - * once the compilation completes. The {@link ANeuralNetworksCompilation_wait} - * will return ERROR_DELETED. - * - * See {@link ANeuralNetworksCompilation} for information on multithreaded - * usage. - * - * @param compilation The compilation to be destroyed. Passing NULL is - * acceptable and results in no operation. - */ -inline void ANeuralNetworksCompilation_free( - ANeuralNetworksCompilation* compilation) { - LOAD_FUNCTION(ANeuralNetworksCompilation_free); - EXECUTE_FUNCTION(compilation); -} - -/** - * Sets the execution preference. - * - *

    Provides guidance to the runtime when trade-offs are possible.

    - * - * See {@link ANeuralNetworksCompilation} for information on multithreaded - * usage. - * - * @param compilation The compilation to be modified. - * @param preference Either {@link PREFER_LOW_POWER}, - * {@link PREFER_SINGLE_FAST_ANSWER}, or - * {@link PREFER_SUSTAINED_SPEED}. - * - * @return ANEURALNETWORKS_NO_ERROR if successful. - */ -inline int ANeuralNetworksCompilation_setPreference( - ANeuralNetworksCompilation* compilation, int32_t preference) { - LOAD_FUNCTION(ANeuralNetworksCompilation_setPreference); - EXECUTE_FUNCTION_RETURN(compilation, preference); -} - -/** - * Waits until the compilation completes. - * - * More than one thread can wait on a compilation. When the compilation - * completes, all threads will be released. - * - * See {@link ANeuralNetworksCompilation} for information on multithreaded - * usage. - * - * @return ANEURALNETWORKS_NO_ERROR if the compilation completed normally. - */ -inline int ANeuralNetworksCompilation_finish( - ANeuralNetworksCompilation* compilation) { - LOAD_FUNCTION(ANeuralNetworksCompilation_finish); - EXECUTE_FUNCTION_RETURN(compilation); -} -/** - * Create a {@link ANeuralNetworksExecution} to apply the given compilation. - * This only creates the object. Computation is only performed once - * {@link ANeuralNetworksExecution_startCompute} is invoked. - * - *

    The provided compilation must outlive the execution.

    - * - * See {@link ANeuralNetworksExecution} for information on multithreaded usage. - * - * @param compilation The {@link ANeuralNetworksCompilation} to be evaluated. - * @param execution The newly created object or NULL if unsuccessful. - * - * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA - * if the compilation is invalid. - */ -inline int ANeuralNetworksExecution_create( - ANeuralNetworksCompilation* compilation, - ANeuralNetworksExecution** execution) { - LOAD_FUNCTION(ANeuralNetworksExecution_create); - EXECUTE_FUNCTION_RETURN(compilation, execution); -} - -/** - * Destroy an execution. - * - *

    If called on an execution for which - * {@link ANeuralNetworksExecution_startCompute} has been called, the - * function will return immediately but will mark the execution to be deleted - * once the computation completes. The {link ANeuralNetworksExecution_wait} - * will return ANEURALNETWORKS_ERROR_DELETED. - * - * See {@link ANeuralNetworksExecution} for information on multithreaded usage. - * - * @param execution The execution to be destroyed. Passing NULL is acceptable - * and results in no operation. - */ -inline void ANeuralNetworksExecution_free(ANeuralNetworksExecution* execution) { - LOAD_FUNCTION(ANeuralNetworksExecution_free); - EXECUTE_FUNCTION(execution); -} - -/** - * Associate a user buffer with an input of the model of the - * {@link ANeuralNetworksExecution}. - * - *

    The provided buffer must outlive the execution.

    - * - * See {@link ANeuralNetworksExecution} for information on multithreaded usage. - * - * @param execution The execution to be modified. - * @param index The index of the input argument we are setting. It is - * an index into the lists passed to - * {@link ANeuralNetworksModel_identifyInputsAndOutputs}. It is not - * the index associated with {@link - * ANeuralNetworksModel_addOperand}. - * @param type The type of the operand. This should be used to specify the - * dimensions that were set to 0 when the operand was added to the - * model. All other properties of the type must be the same as - * specified in the model. If the type is the same as specified - * when the model was built, NULL can be passed. - * @param buffer The buffer containing the data. - * @param length The length in bytes of the buffer. - * - * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA if - * the name is not recognized or the buffer is too small for the input. - */ -inline int ANeuralNetworksExecution_setInput( - ANeuralNetworksExecution* execution, int32_t index, - const ANeuralNetworksOperandType* type, const void* buffer, size_t length) { - LOAD_FUNCTION(ANeuralNetworksExecution_setInput); - EXECUTE_FUNCTION_RETURN(execution, index, type, buffer, length); -} - -/** - * Associate part of a memory object with an input of the model of the - * {@link ANeuralNetworksExecution}. - * - *

    The provided memory must outlive the execution.

    - * - * See {@link ANeuralNetworksExecution} for information on multithreaded usage. - * - * @param execution The execution to be modified. - * @param index The index of the input argument we are setting. It is - * an index into the lists passed to - * {@link ANeuralNetworksModel_identifyInputsAndOutputs}. It is not - * the index associated with {@link - * ANeuralNetworksModel_addOperand}. - * @param type The type of the operand. This can be used to specify the - * dimensions that were set to 0 when the operand was added to the - * model. All other values must be the same as specified in the - * model. If the type is the same as specified when the model - * was built, NULL can be passed. - * @param memory The memory containing the data. - * @param offset This specifies the location of the data within the memory. - * The offset is in bytes from the start of memory. - * @param length The size in bytes of the data value. - * - * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA if - * the name is not recognized or the buffer is too small for the input. - */ -inline int ANeuralNetworksExecution_setInputFromMemory( - ANeuralNetworksExecution* execution, int32_t index, - const ANeuralNetworksOperandType* type, const ANeuralNetworksMemory* memory, - size_t offset, size_t length) { - LOAD_FUNCTION(ANeuralNetworksExecution_setInputFromMemory); - EXECUTE_FUNCTION_RETURN(execution, index, type, memory, offset, length); -} +typedef int (*ASharedMemory_create_fn)(const char* name, size_t size); -/** - * Associate a user buffer with an output of the model of the - * {@link ANeuralNetworksExecution}. - * - *

    The provided buffer must outlive the execution.

    - * - * See {@link ANeuralNetworksExecution} for information on multithreaded usage. - * - * @param execution The execution to be modified. - * @param index The index of the output argument we are setting. It is - * an index into the lists passed to - * {@link ANeuralNetworksModel_identifyInputsAndOutputs}. It is not - * the index associated with {@link - * ANeuralNetworksModel_addOperand}. - * @param type The type of the operand. This can be used to specify the - * dimensions that were set to 0 when the operand was added to the - * model. All other values must be the same as specified in the - * model. If the type is the same as specified when the model - * was built, NULL can be passed. - * @param buffer The buffer where the data is to be written. - * @param length The length in bytes of the buffer. - * - * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA if - * the name is not recognized or the buffer is too small for the output. - */ -inline int ANeuralNetworksExecution_setOutput( - ANeuralNetworksExecution* execution, int32_t index, - const ANeuralNetworksOperandType* type, void* buffer, size_t length) { - LOAD_FUNCTION(ANeuralNetworksExecution_setOutput); - EXECUTE_FUNCTION_RETURN(execution, index, type, buffer, length); -} +struct NnApi { + bool nnapi_exists; + int32_t android_sdk_version; + + /** + * Creates a shared memory object from a file descriptor. + * + * The shared memory is backed by a file descriptor via mmap. + * See {@link ANeuralNetworksMemory} for a description on how to use + * this shared memory. + * + * @param size The requested size in bytes. + * Must not be larger than the file size. + * @param prot The desired memory protection for the mapping. + * It is either PROT_NONE or the bitwise OR of one or + * more of the following flags: PROT_READ, PROT_WRITE. + * @param fd The requested file descriptor. + * The file descriptor has to be mmap-able. The file + * descriptor will be duplicated. + * @param offset The offset to the beginning of the file of the area to map. + * The offset has to be aligned to a page size. + * @param memory The memory object to be created. + * Set to NULL if unsuccessful. + * + * @return ANEURALNETWORKS_NO_ERROR if the request completed normally. + */ + int (*ANeuralNetworksMemory_createFromFd)(size_t size, int protect, int fd, + size_t offset, + ANeuralNetworksMemory** memory); + + /** + * Delete a memory object. + * + * Destroys the object used by the run time to keep track of the memory. + * This will free the underlying actual memory if no other code has open + * handles to this memory. + * + * @param memory The memory object to be freed. + */ + void (*ANeuralNetworksMemory_free)(ANeuralNetworksMemory* memory); + + /** + * Create an empty {@link ANeuralNetworksModel}. + * + *

    This only creates the object. Computation is performed once + * {@link ANeuralNetworksExecution_startCompute} is invoked. + * + * The model should be constructed with calls to + * {@link ANeuralNetworksModel_addOperation} and + * {@link ANeuralNetworksModel_addOperand} + * + *

    {@link ANeuralNetworksModel_finish} should be called once the model + * has been fully constructed.

    + * + *

    {@link ANeuralNetworksModel_free} should be called once the model + * is no longer needed.

    + * + * @param model The {@link ANeuralNetworksModel} to be created. + * Set to NULL if unsuccessful. + * + * @return ANEURALNETWORKS_NO_ERROR if successful. + */ + int (*ANeuralNetworksModel_create)(ANeuralNetworksModel** model); + + /** + * Destroy a model. + * + * The model need not have been finished by a call to + * {@link ANeuralNetworksModel_finish}. + * + * See {@link ANeuralNetworksModel} for information on multithreaded usage. + * + * @param model The model to be destroyed. Passing NULL is acceptable and + * results in no operation. + */ + void (*ANeuralNetworksModel_free)(ANeuralNetworksModel* model); + + /** + * Indicate that we have finished modifying a model. Required before + * calling {@link ANeuralNetworksCompilation_compile}. + * + * An application is responsible to make sure that no other thread uses + * the model at the same time. + * + * See {@link ANeuralNetworksModel} for information on multithreaded usage. + * + * @param model The model to be finished. + * + * @return ANEURALNETWORKS_NO_ERROR if successful. + */ + int (*ANeuralNetworksModel_finish)(ANeuralNetworksModel* model); + + /** + * Add an operand to a model. + * + * The order in which the operands are added is important. The first one added + * to a model will have the index value 0, the second 1, etc. These indexes + * are used as operand identifiers in + * {@link ANeuralNetworksModel_addOperation}, + * {@link ANeuralNetworksExecution_setInput}, + * {@link ANeuralNetworksExecution_setInputFromMemory}, + * {@link ANeuralNetworksExecution_setOutput}, + * {@link ANeuralNetworksExecution_setOutputFromMemory} and + * {@link ANeuralNetworksExecution_setOperandValue}. + * + * To build a model that can accommodate inputs of various sizes, as you may + * want to do for a CNN, set the size of the dimensions that will vary at run + * time to 0. If you do so, provide the full dimensions when calling + * {@link ANeuralNetworksExecution_setInput} or {@link + * ANeuralNetworksExecution_setInputFromMemory}. + * + * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has + * been called will return an error. + * + * See {@link ANeuralNetworksModel} for information on multithreaded usage. + * + * @param model The model to be modified. + * @param type The {@link ANeuralNetworksOperandType} that describes the shape + * of the operand. + * + * @return ANEURALNETWORKS_NO_ERROR if successful. + */ + int (*ANeuralNetworksModel_addOperand)( + ANeuralNetworksModel* model, const ANeuralNetworksOperandType* type); + + /** + * Sets an operand to a constant value. + * + * For scalar values, the content of buffer is copied into the model. + * + * For tensor values, a pointer to the buffer is stored within the model. + * The application is responsible for not changing the content of this region + * until all executions using this model have completed. As the data may + * be copied during processing, modifying the data after this call yields + * undefined results. + * + * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has + * been called will return an error. + * + * See {@link ANeuralNetworksModel} for information on multithreaded usage. + * + * @param model The model to be modified. + * @param index The index of the model operand we're setting. + * @param buffer A pointer to the data to use. + * @param length The size in bytes of the data value. + * + * @return ANEURALNETWORKS_NO_ERROR if successful. + */ + int (*ANeuralNetworksModel_setOperandValue)(ANeuralNetworksModel* model, + int32_t index, const void* buffer, + size_t length); + + /** + * Sets an operand to a value stored in a memory object. + * + * The content of the memory is not copied. A reference to that memory is + * stored inside the model. The application is responsible for not changing + * the content of the memory region until all executions using this model have + * completed. + * As the data may be copied during processing, modifying the data after this + * call yields undefined results. + * + * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has + * been called will return an error. + * + * See {@link ANeuralNetworksModel} for information on multithreaded usage. + * + * @param model The model to be modified. + * @param index The index of the model operand we're setting. + * @param buffer A pointer to the data to use. + * @param memory The memory containing the data. + * @param offset This specifies the location of the data within the memory. + * The offset is in bytes from the start of memory. + * @param length The size in bytes of the data value. + * + * @return ANEURALNETWORKS_NO_ERROR if successful. + */ + int (*ANeuralNetworksModel_setOperandValueFromMemory)( + ANeuralNetworksModel* model, int32_t index, + const ANeuralNetworksMemory* memory, size_t offset, size_t length); + + /** + * Add an operation to a model. + * + * @param model The model to be modified. + * @param type The type of the operation. + * @param inputCount The number of entries in the inputs array. + * @param inputs An array of indexes identifying each operand. + * @param outputCount The number of entries in the outputs array. + * @param outputs An array of indexes identifying each operand. + * + * The operands specified by inputs and outputs must have been + * previously added by calls to {@link ANeuralNetworksModel_addOperand}. + * + * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has + * been called will return an error. + * + * See {@link ANeuralNetworksModel} for information on multithreaded usage. + * + * @return ANEURALNETWORKS_NO_ERROR if successful. + */ + int (*ANeuralNetworksModel_addOperation)(ANeuralNetworksModel* model, + ANeuralNetworksOperationType type, + uint32_t inputCount, + const uint32_t* inputs, + uint32_t outputCount, + const uint32_t* outputs); + + /** + * Specifies which operands will be the model's inputs and outputs. + * + * An operand cannot be used for both input and output. Doing so will + * return an error. + * + * @param model The model to be modified. + * @param inputCount The number of entries in the inputs array. + * @param inputs An array of indexes identifying the input operands. + * @param outputCount The number of entries in the outputs array. + * @param outputs An array of indexes identifying the output operands. + * + * The operands specified by inputs and outputs must have been + * previously added by calls to {@link ANeuralNetworksModel_addOperand}. + * + * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has + * been called will return an error. + * + * See {@link ANeuralNetworksModel} for information on multithreaded usage. + * + */ + int (*ANeuralNetworksModel_identifyInputsAndOutputs)( + ANeuralNetworksModel* model, uint32_t inputCount, const uint32_t* inputs, + uint32_t outputCount, const uint32_t* outputs); + + /** + * Specifies whether {@link ANEURALNETWORKS_TENSOR_FLOAT32} is allowed to be + * calculated with range and/or precision as low as that of the + * IEEE 754 16-bit floating-point format. By default, + * {@link ANEURALNETWORKS_TENSOR_FLOAT32} must be calculated using at least + * the range and precision of the IEEE 754 32-bit floating-point format. + * + * @param model The model to be modified. + * @param allow 'true' indicates {@link ANEURALNETWORKS_TENSOR_FLOAT32} may be + * calculated with range and/or precision as low as that of the + * IEEE 754 16-bit floating point format. 'false' indicates + * {@link ANEURALNETWORKS_TENSOR_FLOAT32} must be calculated + * using at least the range and precision of the IEEE 754 32-bit + * floating point format. + * + * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has + * been called will return an error. + * + * Available since API level 28. + * + * See {@link ANeuralNetworksModel} for information on multithreaded usage. + */ + int (*ANeuralNetworksModel_relaxComputationFloat32toFloat16)( + ANeuralNetworksModel* model, bool allow); + + /** + * Create a {@link ANeuralNetworksCompilation} to compile the given model. + * This only creates the object. Compilation is only performed once + * {@link ANeuralNetworksCompilation_start} is invoked. + * + *

    The provided model must outlive the compilation.

    + * + * The model must already have been finished by a call to + * {@link ANeuralNetworksModel_finish}. + * + * See {@link ANeuralNetworksCompilation} for information on multithreaded + * usage. + * + * @param model The {@link ANeuralNetworksModel} to be compiled. + * @param compilation The newly created object or NULL if unsuccessful. + * + * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA + * if the model is invalid. + */ + int (*ANeuralNetworksCompilation_create)( + ANeuralNetworksModel* model, ANeuralNetworksCompilation** compilation); + + /** + * Destroy a compilation. + * + *

    If called on a compilation for which + * {@link ANeuralNetworksCompilation_start} has been called, the + * function will return immediately but will mark the compilation to be + * deleted once the compilation completes. The + * {@link ANeuralNetworksCompilation_wait} will return ERROR_DELETED. + * + * See {@link ANeuralNetworksCompilation} for information on multithreaded + * usage. + * + * @param compilation The compilation to be destroyed. Passing NULL is + * acceptable and results in no operation. + */ + void (*ANeuralNetworksCompilation_free)( + ANeuralNetworksCompilation* compilation); + + /** + * Sets the execution preference. + * + *

    Provides guidance to the runtime when trade-offs are possible.

    + * + * See {@link ANeuralNetworksCompilation} for information on multithreaded + * usage. + * + * @param compilation The compilation to be modified. + * @param preference Either {@link PREFER_LOW_POWER}, + * {@link PREFER_SINGLE_FAST_ANSWER}, or + * {@link PREFER_SUSTAINED_SPEED}. + * + * @return ANEURALNETWORKS_NO_ERROR if successful. + */ + int (*ANeuralNetworksCompilation_setPreference)( + ANeuralNetworksCompilation* compilation, int32_t preference); + + /** + * Waits until the compilation completes. + * + * More than one thread can wait on a compilation. When the compilation + * completes, all threads will be released. + * + * See {@link ANeuralNetworksCompilation} for information on multithreaded + * usage. + * + * @return ANEURALNETWORKS_NO_ERROR if the compilation completed normally. + */ + int (*ANeuralNetworksCompilation_finish)( + ANeuralNetworksCompilation* compilation); + + /** + * Create a {@link ANeuralNetworksExecution} to apply the given compilation. + * This only creates the object. Computation is only performed once + * {@link ANeuralNetworksExecution_startCompute} is invoked. + * + *

    The provided compilation must outlive the execution.

    + * + * See {@link ANeuralNetworksExecution} for information on multithreaded + * usage. + * + * @param compilation The {@link ANeuralNetworksCompilation} to be evaluated. + * @param execution The newly created object or NULL if unsuccessful. + * + * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA + * if the compilation is invalid. + */ + int (*ANeuralNetworksExecution_create)( + ANeuralNetworksCompilation* compilation, + ANeuralNetworksExecution** execution); + + /** + * Destroy an execution. + * + *

    If called on an execution for which + * {@link ANeuralNetworksExecution_startCompute} has been called, the + * function will return immediately but will mark the execution to be deleted + * once the computation completes. The {link ANeuralNetworksExecution_wait} + * will return ANEURALNETWORKS_ERROR_DELETED. + * + * See {@link ANeuralNetworksExecution} for information on multithreaded + * usage. + * + * @param execution The execution to be destroyed. Passing NULL is acceptable + * and results in no operation. + */ + void (*ANeuralNetworksExecution_free)(ANeuralNetworksExecution* execution); + + /** + * Associate a user buffer with an input of the model of the + * {@link ANeuralNetworksExecution}. + * + *

    The provided buffer must outlive the execution.

    + * + * See {@link ANeuralNetworksExecution} for information on multithreaded + * usage. + * + * @param execution The execution to be modified. + * @param index The index of the input argument we are setting. It is + * an index into the lists passed to + * {@link ANeuralNetworksModel_identifyInputsAndOutputs}. It is + * not the index associated with {@link + * ANeuralNetworksModel_addOperand}. + * @param type The type of the operand. This should be used to specify the + * dimensions that were set to 0 when the operand was added to the + * model. All other properties of the type must be the same as + * specified in the model. If the type is the same as specified + * when the model was built, NULL can be passed. + * @param buffer The buffer containing the data. + * @param length The length in bytes of the buffer. + * + * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA if + * the name is not recognized or the buffer is too small for the input. + */ + int (*ANeuralNetworksExecution_setInput)( + ANeuralNetworksExecution* execution, int32_t index, + const ANeuralNetworksOperandType* type, const void* buffer, + size_t length); + + /** + * Associate part of a memory object with an input of the model of the + * {@link ANeuralNetworksExecution}. + * + *

    The provided memory must outlive the execution.

    + * + * See {@link ANeuralNetworksExecution} for information on multithreaded + * usage. + * + * @param execution The execution to be modified. + * @param index The index of the input argument we are setting. It is + * an index into the lists passed to + * {@link ANeuralNetworksModel_identifyInputsAndOutputs}. It is + * not the index associated with {@link + * ANeuralNetworksModel_addOperand}. + * @param type The type of the operand. This can be used to specify the + * dimensions that were set to 0 when the operand was added to the + * model. All other values must be the same as specified in the + * model. If the type is the same as specified when the model + * was built, NULL can be passed. + * @param memory The memory containing the data. + * @param offset This specifies the location of the data within the memory. + * The offset is in bytes from the start of memory. + * @param length The size in bytes of the data value. + * + * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA if + * the name is not recognized or the buffer is too small for the input. + */ + int (*ANeuralNetworksExecution_setInputFromMemory)( + ANeuralNetworksExecution* execution, int32_t index, + const ANeuralNetworksOperandType* type, + const ANeuralNetworksMemory* memory, size_t offset, size_t length); + + /** + * Associate a user buffer with an output of the model of the + * {@link ANeuralNetworksExecution}. + * + *

    The provided buffer must outlive the execution.

    + * + * See {@link ANeuralNetworksExecution} for information on multithreaded + * usage. + * + * @param execution The execution to be modified. + * @param index The index of the output argument we are setting. It is + * an index into the lists passed to + * {@link ANeuralNetworksModel_identifyInputsAndOutputs}. It is + * not the index associated with {@link + * ANeuralNetworksModel_addOperand}. + * @param type The type of the operand. This can be used to specify the + * dimensions that were set to 0 when the operand was added to the + * model. All other values must be the same as specified in the + * model. If the type is the same as specified when the model + * was built, NULL can be passed. + * @param buffer The buffer where the data is to be written. + * @param length The length in bytes of the buffer. + * + * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA if + * the name is not recognized or the buffer is too small for the output. + */ + int (*ANeuralNetworksExecution_setOutput)( + ANeuralNetworksExecution* execution, int32_t index, + const ANeuralNetworksOperandType* type, void* buffer, size_t length); + + /** + * Associate part of a memory object with an output of the model of the + * {@link ANeuralNetworksExecution}. + * + *

    The provided memory must outlive the execution.

    + * + * See {@link ANeuralNetworksExecution} for information on multithreaded + * usage. + * + * @param execution The execution to be modified. + * @param index The index of the output argument we are setting. It is + * an index into the lists passed to + * {@link ANeuralNetworksModel_identifyInputsAndOutputs}. It is + * not the index associated with {@link + * ANeuralNetworksModel_addOperand}. + * @param type The type of the operand. This can be used to specify the + * dimensions that were set to 0 when the operand was added to the + * model. All other values must be the same as specified in the + * model. If the type is the same as specified when the model + * was built, NULL can be passed. + * @param memory The memory where the data is to be stored. + * @param offset This specifies the location of the data within the memory. + * The offset is in bytes from the start of memory. + * @param length The length in bytes of the data value. + * + * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA if + * the name is not recognized or the buffer is too small for the output. + */ + int (*ANeuralNetworksExecution_setOutputFromMemory)( + ANeuralNetworksExecution* execution, int32_t index, + const ANeuralNetworksOperandType* type, + const ANeuralNetworksMemory* memory, size_t offset, size_t length); + + /** + * Schedule evaluation of the execution. + * + *

    Schedules evaluation of the execution. Once the model has been + * applied and the outputs are ready to be consumed, the execution will be + * signaled. Use {@link ANeuralNetworksExecution_wait} to wait for that + * signal. + *

    + * + * Multiple executions can be scheduled and evaluated concurrently, and + * compilations can be performed concurrently with executions. The runtime + * makes no guarantee on the ordering of the completion of compilations and + * executions. If it's important to the application, the application should + * enforce the ordering by using {@link ANeuralNetworksCompilation_wait} and + * {@link ANeuralNetworksExecution_wait}. + * + * ANeuralNetworksExecution_wait must be called to recuperate the resources + * used by the execution. + * + * See {@link ANeuralNetworksExecution} for information on multithreaded + * usage. + * + * @param execution The execution to be scheduled and executed. + * + * @return ANEURALNETWORKS_NO_ERROR if successful. + */ + int (*ANeuralNetworksExecution_startCompute)( + ANeuralNetworksExecution* execution, ANeuralNetworksEvent** event); + + /** + * Waits until the execution completes. + * + * More than one thread can wait on an event. When the execution completes, + * all threads will be released. + * + * See {@link ANeuralNetworksExecution} for information on multithreaded + * usage. + * + * @return ANEURALNETWORKS_NO_ERROR if the execution completed normally. + */ + int (*ANeuralNetworksEvent_wait)(ANeuralNetworksEvent* event); -/** - * Associate part of a memory object with an output of the model of the - * {@link ANeuralNetworksExecution}. - * - *

    The provided memory must outlive the execution.

    - * - * See {@link ANeuralNetworksExecution} for information on multithreaded usage. - * - * @param execution The execution to be modified. - * @param index The index of the output argument we are setting. It is - * an index into the lists passed to - * {@link ANeuralNetworksModel_identifyInputsAndOutputs}. It is not - * the index associated with {@link - * ANeuralNetworksModel_addOperand}. - * @param type The type of the operand. This can be used to specify the - * dimensions that were set to 0 when the operand was added to the - * model. All other values must be the same as specified in the - * model. If the type is the same as specified when the model - * was built, NULL can be passed. - * @param memory The memory where the data is to be stored. - * @param offset This specifies the location of the data within the memory. - * The offset is in bytes from the start of memory. - * @param length The length in bytes of the data value. - * - * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA if - * the name is not recognized or the buffer is too small for the output. - */ -inline int ANeuralNetworksExecution_setOutputFromMemory( - ANeuralNetworksExecution* execution, int32_t index, - const ANeuralNetworksOperandType* type, const ANeuralNetworksMemory* memory, - size_t offset, size_t length) { - LOAD_FUNCTION(ANeuralNetworksExecution_setOutputFromMemory); - EXECUTE_FUNCTION_RETURN(execution, index, type, memory, offset, length); -} + /** + * Destroys the event. + * + * See {@link ANeuralNetworksExecution} for information on multithreaded + * usage. + */ + void (*ANeuralNetworksEvent_free)(ANeuralNetworksEvent* event); -/** - * Schedule evaluation of the execution. - * - *

    Schedules evaluation of the execution. Once the model has been - * applied and the outputs are ready to be consumed, the execution will be - * signaled. Use {@link ANeuralNetworksExecution_wait} to wait for that signal. - *

    - * - * Multiple executions can be scheduled and evaluated concurrently, and - * compilations can be performed concurrently with executions. The runtime makes - * no guarantee on the ordering of the completion of compilations and - * executions. If it's important to the application, the application should - * enforce the ordering by using {@link ANeuralNetworksCompilation_wait} and - * {@link ANeuralNetworksExecution_wait}. - * - * ANeuralNetworksExecution_wait must be called to recuperate the resources used - * by the execution. - * - * See {@link ANeuralNetworksExecution} for information on multithreaded usage. - * - * @param execution The execution to be scheduled and executed. - * - * @return ANEURALNETWORKS_NO_ERROR if successful. - */ -inline int ANeuralNetworksExecution_startCompute( - ANeuralNetworksExecution* execution, ANeuralNetworksEvent** event) { - LOAD_FUNCTION(ANeuralNetworksExecution_startCompute); - EXECUTE_FUNCTION_RETURN(execution, event); -} + // ASharedMemory_create was added in Android 8.0, so safe to use with NNAPI + // which was added in 8.1. + int (*ASharedMemory_create)(const char* name, size_t size); -/** - * Waits until the execution completes. - * - * More than one thread can wait on an event. When the execution completes, - * all threads will be released. - * - * See {@link ANeuralNetworksExecution} for information on multithreaded usage. - * - * @return ANEURALNETWORKS_NO_ERROR if the execution completed normally. - */ -inline int ANeuralNetworksEvent_wait(ANeuralNetworksEvent* event) { - LOAD_FUNCTION(ANeuralNetworksEvent_wait); - EXECUTE_FUNCTION_RETURN(event); -} + /**/ +}; /** - * Destroys the event. - * - * See {@link ANeuralNetworksExecution} for information on multithreaded usage. + * Load the NNAPI implementation from the shared libraries. + * The NnApi structure is filled with all the pointers. If one function doesn't + * exist, a null pointer is stored. */ -inline void ANeuralNetworksEvent_free(ANeuralNetworksEvent* event) { - LOAD_FUNCTION(ANeuralNetworksEvent_free); - EXECUTE_FUNCTION(event); -} - -/**/ +const NnApi* NnApiImplementation(); #endif // TENSORFLOW_LITE_NNAPI_NEURALNETWORKSSHIM_H_ diff --git a/tensorflow/lite/nnapi/nnapi_lib_test.cc b/tensorflow/lite/nnapi/nnapi_lib_test.cc new file mode 100644 index 0000000000..aaf0ffaa8e --- /dev/null +++ b/tensorflow/lite/nnapi/nnapi_lib_test.cc @@ -0,0 +1,90 @@ +/* Copyright 2018 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 +#include "tensorflow/lite/nnapi/NeuralNetworksShim.h" + +namespace { + +TEST(NnapiLibTest, NnApiImplementation) { + const NnApi* nnapi = NnApiImplementation(); + EXPECT_NE(nnapi, nullptr); +#ifdef __ANDROID__ + EXPECT_TRUE(nnapi->nnapi_exists); + EXPECT_GT(nnapi->android_sdk_version, 0); + EXPECT_NE(nnapi->ANeuralNetworksMemory_createFromFd, nullptr); + EXPECT_NE(nnapi->ANeuralNetworksMemory_free, nullptr); + EXPECT_NE(nnapi->ANeuralNetworksModel_create, nullptr); + EXPECT_NE(nnapi->ANeuralNetworksModel_free, nullptr); + EXPECT_NE(nnapi->ANeuralNetworksModel_finish, nullptr); + EXPECT_NE(nnapi->ANeuralNetworksModel_addOperand, nullptr); + EXPECT_NE(nnapi->ANeuralNetworksModel_setOperandValue, nullptr); + EXPECT_NE(nnapi->ANeuralNetworksModel_setOperandValueFromMemory, nullptr); + EXPECT_NE(nnapi->ANeuralNetworksModel_addOperation, nullptr); + EXPECT_NE(nnapi->ANeuralNetworksModel_identifyInputsAndOutputs, nullptr); + if (nnapi->android_sdk_version >= 28) { + // relaxComputationFloat32toFloat16 only available with Android 9.0 (P). + EXPECT_NE(nnapi->ANeuralNetworksModel_relaxComputationFloat32toFloat16, + nullptr); + } else { + EXPECT_EQ(nnapi->ANeuralNetworksModel_relaxComputationFloat32toFloat16, + nullptr); + } + EXPECT_NE(nnapi->ANeuralNetworksCompilation_create, nullptr); + EXPECT_NE(nnapi->ANeuralNetworksCompilation_free, nullptr); + EXPECT_NE(nnapi->ANeuralNetworksCompilation_setPreference, nullptr); + EXPECT_NE(nnapi->ANeuralNetworksCompilation_finish, nullptr); + EXPECT_NE(nnapi->ANeuralNetworksExecution_create, nullptr); + EXPECT_NE(nnapi->ANeuralNetworksExecution_free, nullptr); + EXPECT_NE(nnapi->ANeuralNetworksExecution_setInput, nullptr); + EXPECT_NE(nnapi->ANeuralNetworksExecution_setInputFromMemory, nullptr); + EXPECT_NE(nnapi->ANeuralNetworksExecution_setOutput, nullptr); + EXPECT_NE(nnapi->ANeuralNetworksExecution_setOutputFromMemory, nullptr); + EXPECT_NE(nnapi->ANeuralNetworksExecution_startCompute, nullptr); + EXPECT_NE(nnapi->ANeuralNetworksEvent_wait, nullptr); + EXPECT_NE(nnapi->ANeuralNetworksEvent_free, nullptr); + EXPECT_NE(nnapi->ASharedMemory_create, nullptr); +#else + EXPECT_FALSE(nnapi->nnapi_exists); + EXPECT_EQ(nnapi->android_sdk_version, 0); + EXPECT_EQ(nnapi->ANeuralNetworksMemory_createFromFd, nullptr); + EXPECT_EQ(nnapi->ANeuralNetworksMemory_free, nullptr); + EXPECT_EQ(nnapi->ANeuralNetworksModel_create, nullptr); + EXPECT_EQ(nnapi->ANeuralNetworksModel_free, nullptr); + EXPECT_EQ(nnapi->ANeuralNetworksModel_finish, nullptr); + EXPECT_EQ(nnapi->ANeuralNetworksModel_addOperand, nullptr); + EXPECT_EQ(nnapi->ANeuralNetworksModel_setOperandValue, nullptr); + EXPECT_EQ(nnapi->ANeuralNetworksModel_setOperandValueFromMemory, nullptr); + EXPECT_EQ(nnapi->ANeuralNetworksModel_addOperation, nullptr); + EXPECT_EQ(nnapi->ANeuralNetworksModel_identifyInputsAndOutputs, nullptr); + EXPECT_EQ(nnapi->ANeuralNetworksModel_relaxComputationFloat32toFloat16, + nullptr); + EXPECT_EQ(nnapi->ANeuralNetworksCompilation_create, nullptr); + EXPECT_EQ(nnapi->ANeuralNetworksCompilation_free, nullptr); + EXPECT_EQ(nnapi->ANeuralNetworksCompilation_setPreference, nullptr); + EXPECT_EQ(nnapi->ANeuralNetworksCompilation_finish, nullptr); + EXPECT_EQ(nnapi->ANeuralNetworksExecution_create, nullptr); + EXPECT_EQ(nnapi->ANeuralNetworksExecution_free, nullptr); + EXPECT_EQ(nnapi->ANeuralNetworksExecution_setInput, nullptr); + EXPECT_EQ(nnapi->ANeuralNetworksExecution_setInputFromMemory, nullptr); + EXPECT_EQ(nnapi->ANeuralNetworksExecution_setOutput, nullptr); + EXPECT_EQ(nnapi->ANeuralNetworksExecution_setOutputFromMemory, nullptr); + EXPECT_EQ(nnapi->ANeuralNetworksExecution_startCompute, nullptr); + EXPECT_EQ(nnapi->ANeuralNetworksEvent_wait, nullptr); + EXPECT_EQ(nnapi->ANeuralNetworksEvent_free, nullptr); + EXPECT_EQ(nnapi->ASharedMemory_create, nullptr); +#endif +} + +} // namespace diff --git a/tensorflow/lite/nnapi_delegate.cc b/tensorflow/lite/nnapi_delegate.cc index dc8e81cde7..44b9930acc 100644 --- a/tensorflow/lite/nnapi_delegate.cc +++ b/tensorflow/lite/nnapi_delegate.cc @@ -84,56 +84,27 @@ void logError(const char* format, ...) { static const int64_t kOperandIdNotSet = -1; static const int64_t kOperandNotNeeded = -2; -namespace { - -int32_t GetAndroidSdkVersion() { -#ifdef __ANDROID__ - const char* sdkProp = "ro.build.version.sdk"; - char sdkVersion[PROP_VALUE_MAX]; - int length = __system_property_get(sdkProp, sdkVersion); - if (length != 0) { - for (int i = 0; i < length; ++i) { - int digit = sdkVersion[i] - '0'; - if (digit < 0 || digit > 9) { - // Non-numeric SDK version, assume it's higher then expected; - return 0xFFFF; - } - } - return atoi(sdkVersion); - } - FATAL("No %s prop", sdkProp); -#endif // __ANDROID__ - return 0; -} - -int32_t GetAndroidSdkVersionCached() { - static int32_t androidSdkVersion = GetAndroidSdkVersion(); - return androidSdkVersion; -} - -} // namespace - NNAPIAllocation::NNAPIAllocation(const char* filename, ErrorReporter* error_reporter) : MMAPAllocation(filename, error_reporter) { if (mmapped_buffer_ != MAP_FAILED) - CHECK_NN(ANeuralNetworksMemory_createFromFd(buffer_size_bytes_, PROT_READ, - mmap_fd_, 0, &handle_)); + CHECK_NN(NnApiImplementation()->ANeuralNetworksMemory_createFromFd( + buffer_size_bytes_, PROT_READ, mmap_fd_, 0, &handle_)); } NNAPIAllocation::~NNAPIAllocation() { if (handle_) { - ANeuralNetworksMemory_free(handle_); + NnApiImplementation()->ANeuralNetworksMemory_free(handle_); } } NNAPIDelegate::~NNAPIDelegate() { if (nn_compiled_model_) { - ANeuralNetworksCompilation_free(nn_compiled_model_); + NnApiImplementation()->ANeuralNetworksCompilation_free(nn_compiled_model_); nn_compiled_model_ = nullptr; } if (nn_model_) { - ANeuralNetworksModel_free(nn_model_); + NnApiImplementation()->ANeuralNetworksModel_free(nn_model_); nn_model_ = nullptr; // TODO(aselle): Is this thread-safe and callable multiple times? } @@ -145,6 +116,7 @@ TfLiteStatus addTensorOperands(tflite::Subgraph* subgraph, ANeuralNetworksModel* nn_model, uint32_t* no_of_operands_added, std::vector* nnapi_ids) { + const NnApi* nnapi = NnApiImplementation(); uint32_t next_id = 0; for (size_t i = 0; i < subgraph->tensors_size(); i++) { // Skip temporaries and RNN back-edges. @@ -198,24 +170,24 @@ TfLiteStatus addTensorOperands(tflite::Subgraph* subgraph, nn_type, static_cast(tensor->dims->size), reinterpret_cast(tensor->dims->data), scale, zeroPoint}; RETURN_ERROR_IF_NN_FAILED( - ANeuralNetworksModel_addOperand(nn_model, &operand_type)); + nnapi->ANeuralNetworksModel_addOperand(nn_model, &operand_type)); // TODO(aselle): Based on Michael's suggestion, limiting this to read // only memory if (tensor->allocation_type == kTfLiteMmapRo) { if (const NNAPIAllocation* alloc = dynamic_cast( static_cast(tensor->allocation))) { RETURN_ERROR_IF_NN_FAILED( - ANeuralNetworksModel_setOperandValueFromMemory( + nnapi->ANeuralNetworksModel_setOperandValueFromMemory( nn_model, next_id, alloc->memory(), alloc->offset(tensor->data.raw), tensor->bytes)); } else { - RETURN_ERROR_IF_NN_FAILED(ANeuralNetworksModel_setOperandValue( + RETURN_ERROR_IF_NN_FAILED(nnapi->ANeuralNetworksModel_setOperandValue( nn_model, next_id, tensor->data.raw, tensor->bytes)); } } else if (tensor->bytes == 0) { // These size 0 tensors are optional tensors reserved. - RETURN_ERROR_IF_NN_FAILED( - ANeuralNetworksModel_setOperandValue(nn_model, next_id, nullptr, 0)); + RETURN_ERROR_IF_NN_FAILED(nnapi->ANeuralNetworksModel_setOperandValue( + nn_model, next_id, nullptr, 0)); } ++next_id; @@ -244,6 +216,7 @@ TfLiteStatus AddOpsAndParams( uint32_t next_id, std::vector* model_state_inputs, std::vector* model_state_outputs, const std::vector& tensor_id_to_nnapi_id) { + const NnApi* nnapi = NnApiImplementation(); for (size_t i = 0; i < subgraph->nodes_size(); i++) { const auto* node_and_registration = subgraph->node_and_registration(i); const TfLiteNode& node = node_and_registration->first; @@ -258,21 +231,21 @@ TfLiteStatus AddOpsAndParams( MapAndAddTensorIds(node.outputs->data, node.outputs->size, &augmented_outputs, tensor_id_to_nnapi_id); - auto add_scalar_int32 = [&nn_model, &augmented_inputs, + auto add_scalar_int32 = [nnapi, &nn_model, &augmented_inputs, &next_id](int value) { ANeuralNetworksOperandType operand_type{.type = ANEURALNETWORKS_INT32}; - CHECK_NN(ANeuralNetworksModel_addOperand(nn_model, &operand_type)) - CHECK_NN(ANeuralNetworksModel_setOperandValue(nn_model, next_id, &value, - sizeof(int32_t))) + CHECK_NN(nnapi->ANeuralNetworksModel_addOperand(nn_model, &operand_type)) + CHECK_NN(nnapi->ANeuralNetworksModel_setOperandValue( + nn_model, next_id, &value, sizeof(int32_t))) augmented_inputs.push_back(next_id++); }; - auto add_scalar_float32 = [&nn_model, &augmented_inputs, + auto add_scalar_float32 = [nnapi, &nn_model, &augmented_inputs, &next_id](float value) { ANeuralNetworksOperandType operand_type{.type = ANEURALNETWORKS_FLOAT32}; - CHECK_NN(ANeuralNetworksModel_addOperand(nn_model, &operand_type)) - CHECK_NN(ANeuralNetworksModel_setOperandValue(nn_model, next_id, &value, - sizeof(float))) + CHECK_NN(nnapi->ANeuralNetworksModel_addOperand(nn_model, &operand_type)) + CHECK_NN(nnapi->ANeuralNetworksModel_setOperandValue( + nn_model, next_id, &value, sizeof(float))) augmented_inputs.push_back(next_id++); }; @@ -281,8 +254,8 @@ TfLiteStatus AddOpsAndParams( .type = ANEURALNETWORKS_TENSOR_INT32, .dimensionCount = 1, .dimensions = &num_values}; - CHECK_NN(ANeuralNetworksModel_addOperand(nn_model, &operand_type)) - CHECK_NN(ANeuralNetworksModel_setOperandValue( + CHECK_NN(nnapi->ANeuralNetworksModel_addOperand(nn_model, &operand_type)) + CHECK_NN(nnapi->ANeuralNetworksModel_setOperandValue( nn_model, next_id, values, sizeof(int32_t) * num_values)); augmented_inputs.push_back(next_id++); }; @@ -291,15 +264,16 @@ TfLiteStatus AddOpsAndParams( // For each state_out tensor, a corresponding state_in operand needs to be // created for NNAPI. auto duplicate_state_tensor_float32 = - [subgraph, &nn_model, &next_id, &augmented_inputs, &model_state_inputs, - &model_state_outputs](int tensor_id) { + [nnapi, subgraph, &nn_model, &next_id, &augmented_inputs, + &model_state_inputs, &model_state_outputs](int tensor_id) { const TfLiteTensor* tensor = subgraph->tensor(tensor_id); ANeuralNetworksOperandType operand_type{ ANEURALNETWORKS_TENSOR_FLOAT32, static_cast(tensor->dims->size), reinterpret_cast(tensor->dims->data), tensor->params.scale, tensor->params.zero_point}; - CHECK_NN(ANeuralNetworksModel_addOperand(nn_model, &operand_type)); + CHECK_NN( + nnapi->ANeuralNetworksModel_addOperand(nn_model, &operand_type)); augmented_inputs.push_back(next_id); model_state_inputs->push_back(next_id); model_state_outputs->push_back(tensor_id); @@ -388,7 +362,7 @@ TfLiteStatus AddOpsAndParams( }; // LSTM in NNAPI requires scratch tensor as an output operand. - auto add_lstm_scratch_tensor_float32 = [subgraph, &node, &nn_model, + auto add_lstm_scratch_tensor_float32 = [nnapi, subgraph, &node, &nn_model, &next_id, &augmented_outputs]() { if (node.temporaries->size == 0) return; int scratch_buffer_index = node.temporaries->data[0]; @@ -398,7 +372,7 @@ TfLiteStatus AddOpsAndParams( static_cast(tensor->dims->size), reinterpret_cast(tensor->dims->data), tensor->params.scale, tensor->params.zero_point}; - CHECK_NN(ANeuralNetworksModel_addOperand(nn_model, &operand_type)); + CHECK_NN(nnapi->ANeuralNetworksModel_addOperand(nn_model, &operand_type)); augmented_outputs.insert(augmented_outputs.begin(), next_id++); }; @@ -427,15 +401,16 @@ TfLiteStatus AddOpsAndParams( }; // Handle optional input tensors. - auto add_optional_tensors = [&nn_model, &augmented_inputs, + auto add_optional_tensors = [nnapi, &nn_model, &augmented_inputs, &next_id](int nn_type) { for (size_t idx = 0; idx < augmented_inputs.size(); idx++) { if (augmented_inputs[idx] == kOptionalTensor) { const std::vector dim = {0, 0}; ANeuralNetworksOperandType operand_type{nn_type, 2, dim.data(), 0, 0}; - CHECK_NN(ANeuralNetworksModel_addOperand(nn_model, &operand_type)) - CHECK_NN(ANeuralNetworksModel_setOperandValue(nn_model, next_id, - nullptr, 0)) + CHECK_NN( + nnapi->ANeuralNetworksModel_addOperand(nn_model, &operand_type)) + CHECK_NN(nnapi->ANeuralNetworksModel_setOperandValue( + nn_model, next_id, nullptr, 0)) augmented_inputs[idx] = next_id++; } } @@ -696,13 +671,13 @@ TfLiteStatus AddOpsAndParams( break; } - if (nnapi_version == 11 && GetAndroidSdkVersionCached() < 28) { + if (nnapi_version == 11 && nnapi->android_sdk_version < 28) { logError("Op %d needs NNAPI1.1", builtin); return kTfLiteError; } // Add the operation. - RETURN_ERROR_IF_NN_FAILED(ANeuralNetworksModel_addOperation( + RETURN_ERROR_IF_NN_FAILED(nnapi->ANeuralNetworksModel_addOperation( nn_model, nn_op_type, static_cast(augmented_inputs.size()), augmented_inputs.data(), static_cast(augmented_outputs.size()), @@ -714,9 +689,10 @@ TfLiteStatus AddOpsAndParams( TfLiteStatus NNAPIDelegate::BuildGraph(Subgraph* subgraph) { if (nn_model_ && nn_compiled_model_) return model_status_; + const NnApi* nnapi = NnApiImplementation(); // TODO(aselle): This is not correct. need to handle resize invalidation. if (!nn_model_) { - CHECK_NN(ANeuralNetworksModel_create(&nn_model_)); + CHECK_NN(nnapi->ANeuralNetworksModel_create(&nn_model_)); // Find which tensors should be added to NNAPI. TFLite has temporaries // and RNN back-edges which are are not valid for NNAPI. We look through all @@ -763,21 +739,22 @@ TfLiteStatus NNAPIDelegate::BuildGraph(Subgraph* subgraph) { model_states_outputs_.size(), &augmented_outputs, tensor_id_to_nnapi_id); - CHECK_NN(ANeuralNetworksModel_identifyInputsAndOutputs( + CHECK_NN(nnapi->ANeuralNetworksModel_identifyInputsAndOutputs( nn_model_, static_cast(augmented_inputs.size()), reinterpret_cast(augmented_inputs.data()), static_cast(augmented_outputs.size()), reinterpret_cast(augmented_outputs.data()))); - if (GetAndroidSdkVersionCached() >= 28) { - CHECK_NN(ANeuralNetworksModel_relaxComputationFloat32toFloat16( + if (nnapi->android_sdk_version >= 28) { + CHECK_NN(nnapi->ANeuralNetworksModel_relaxComputationFloat32toFloat16( nn_model_, subgraph->GetAllowFp16PrecisionForFp32())); } - CHECK_NN(ANeuralNetworksModel_finish(nn_model_)); + CHECK_NN(nnapi->ANeuralNetworksModel_finish(nn_model_)); } if (!nn_compiled_model_) { - CHECK_NN(ANeuralNetworksCompilation_create(nn_model_, &nn_compiled_model_)); - CHECK_NN(ANeuralNetworksCompilation_finish(nn_compiled_model_)); + CHECK_NN(nnapi->ANeuralNetworksCompilation_create(nn_model_, + &nn_compiled_model_)); + CHECK_NN(nnapi->ANeuralNetworksCompilation_finish(nn_compiled_model_)); } return kTfLiteOk; } @@ -793,8 +770,10 @@ TfLiteStatus NNAPIDelegate::Invoke(Subgraph* subgraph) { return model_status_; } + const NnApi* nnapi = NnApiImplementation(); ANeuralNetworksExecution* execution = nullptr; - CHECK_NN(ANeuralNetworksExecution_create(nn_compiled_model_, &execution)); + CHECK_NN( + nnapi->ANeuralNetworksExecution_create(nn_compiled_model_, &execution)); // Currently perform deep copy of input buffer for (size_t i = 0; i < subgraph->inputs().size(); i++) { @@ -802,7 +781,7 @@ TfLiteStatus NNAPIDelegate::Invoke(Subgraph* subgraph) { // TODO(aselle): Is this what we want or do we want input instead? // TODO(aselle): This should be called setInputValue maybe to be cons. TfLiteTensor* tensor = subgraph->tensor(input); - CHECK_NN(ANeuralNetworksExecution_setInput( + CHECK_NN(nnapi->ANeuralNetworksExecution_setInput( execution, i, nullptr, tensor->data.raw, tensor->bytes)); } @@ -810,7 +789,7 @@ TfLiteStatus NNAPIDelegate::Invoke(Subgraph* subgraph) { for (size_t i = 0; i < subgraph->outputs().size(); i++) { int output = subgraph->outputs()[i]; TfLiteTensor* tensor = subgraph->tensor(output); - CHECK_NN(ANeuralNetworksExecution_setOutput( + CHECK_NN(nnapi->ANeuralNetworksExecution_setOutput( execution, i, nullptr, tensor->data.raw, tensor->bytes)); } @@ -822,21 +801,21 @@ TfLiteStatus NNAPIDelegate::Invoke(Subgraph* subgraph) { // Here we are using a deep copy for state_in tensors so that we are not // reading and writing into the same buffer during a invocation. // TODO(miaowang): using double shared buffer to minimize the copies. - CHECK_NN(ANeuralNetworksExecution_setInput( + CHECK_NN(nnapi->ANeuralNetworksExecution_setInput( execution, i + subgraph->inputs().size(), nullptr, tensor->data.raw, tensor->bytes)); // Tell NNAPI where to output the state_out. - CHECK_NN(ANeuralNetworksExecution_setOutput( + CHECK_NN(nnapi->ANeuralNetworksExecution_setOutput( execution, i + subgraph->outputs().size(), nullptr, tensor->data.raw, tensor->bytes)); } // Currently use blocking compute. ANeuralNetworksEvent* event = nullptr; - CHECK_NN(ANeuralNetworksExecution_startCompute(execution, &event)); - CHECK_NN(ANeuralNetworksEvent_wait(event)); - ANeuralNetworksEvent_free(event); - ANeuralNetworksExecution_free(execution); + CHECK_NN(nnapi->ANeuralNetworksExecution_startCompute(execution, &event)); + CHECK_NN(nnapi->ANeuralNetworksEvent_wait(event)); + nnapi->ANeuralNetworksEvent_free(event); + nnapi->ANeuralNetworksExecution_free(execution); #if 0 printf("From the NN API:\n"); @@ -854,6 +833,8 @@ TfLiteStatus NNAPIDelegate::Invoke(Subgraph* subgraph) { return kTfLiteOk; } -bool NNAPIDelegate::IsSupported() { return NNAPIExists(); } +bool NNAPIDelegate::IsSupported() { + return NnApiImplementation()->nnapi_exists; +} } // namespace tflite -- GitLab From 185c0233e5533788fd5c4679acd0a4b64484dc03 Mon Sep 17 00:00:00 2001 From: Jian Li Date: Wed, 2 Jan 2019 13:42:04 -0800 Subject: [PATCH 259/622] Fix an error in neon tensor util. vget_high_s8 gets the high bits. PiperOrigin-RevId: 227574080 --- tensorflow/lite/kernels/internal/optimized/neon_tensor_utils.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/lite/kernels/internal/optimized/neon_tensor_utils.cc b/tensorflow/lite/kernels/internal/optimized/neon_tensor_utils.cc index cf40ebb241..cf1bda2661 100644 --- a/tensorflow/lite/kernels/internal/optimized/neon_tensor_utils.cc +++ b/tensorflow/lite/kernels/internal/optimized/neon_tensor_utils.cc @@ -144,7 +144,7 @@ void NeonMatrixBatchVectorMultiplyAccumulate( // registers). int16x8_t prod_16x8 = vmull_s8(vget_low_s8(s1_8x16), vget_low_s8(s2_8x16)); - // Multiply the high bits (i.e. the lower 8 8bit numbers in the + // Multiply the high bits (i.e. the higher 8 8bit numbers in the // registers), and accumulate with the result of the low bits product. // The assumption here is that overflow will not happen as we quantize // our values to be in the range [-127, 127]. As such the sum of the 2 -- GitLab From 9cb5ebb7dfbe262204cf153a84fb17cd4fc70f00 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Wed, 2 Jan 2019 13:58:06 -0800 Subject: [PATCH 260/622] Add a ragged rank op and register it to the op dispatcher PiperOrigin-RevId: 227576633 --- tensorflow/python/ops/array_ops.py | 1 + tensorflow/python/ops/ragged/BUILD | 13 +++ .../python/ops/ragged/ragged_array_ops.py | 30 +++++++ .../python/ops/ragged/ragged_dispatch.py | 1 + .../python/ops/ragged/ragged_dispatch_test.py | 4 + .../python/ops/ragged/ragged_rank_op_test.py | 89 +++++++++++++++++++ 6 files changed, 138 insertions(+) create mode 100644 tensorflow/python/ops/ragged/ragged_rank_op_test.py diff --git a/tensorflow/python/ops/array_ops.py b/tensorflow/python/ops/array_ops.py index 81ea473466..07a7e180fb 100644 --- a/tensorflow/python/ops/array_ops.py +++ b/tensorflow/python/ops/array_ops.py @@ -430,6 +430,7 @@ def size_internal(input, name=None, optimize=True, out_type=dtypes.int32): @tf_export("rank") +@dispatch.add_dispatch_support def rank(input, name=None): # pylint: disable=redefined-builtin """Returns the rank of a tensor. diff --git a/tensorflow/python/ops/ragged/BUILD b/tensorflow/python/ops/ragged/BUILD index 89b8c4a2b3..46f7fa62a3 100644 --- a/tensorflow/python/ops/ragged/BUILD +++ b/tensorflow/python/ops/ragged/BUILD @@ -720,6 +720,19 @@ py_test( ], ) +py_test( + name = "ragged_rank_op_test", + srcs = ["ragged_rank_op_test.py"], + srcs_version = "PY2AND3", + deps = [ + ":ragged_array_ops", + ":ragged_factory_ops", + ":ragged_test_util", + "//tensorflow/python:framework_test_lib", + "@absl_py//absl/testing:parameterized", + ], +) + py_test( name = "ragged_tile_op_test", srcs = ["ragged_tile_op_test.py"], diff --git a/tensorflow/python/ops/ragged/ragged_array_ops.py b/tensorflow/python/ops/ragged/ragged_array_ops.py index 3c5e697cdc..a6b2442f09 100644 --- a/tensorflow/python/ops/ragged/ragged_array_ops.py +++ b/tensorflow/python/ops/ragged/ragged_array_ops.py @@ -1226,3 +1226,33 @@ def _nrows(rt_input, out_type=dtypes.int64, name=None): else: with ops.name_scope(name, 'RaggedNRows', [rt_input]): return array_ops.shape(rt_input, out_type=out_type)[0] + + +#=============================================================================== +# ragged.rank +#=============================================================================== +def rank(input, name=None): # pylint: disable=redefined-builtin + """Returns the rank of a RaggedTensor. + + Returns a 0-D `int32` `Tensor` representing the rank of `input`. + + For example: + + ```python + # shape of tensor 't' is [2, None, None] + t = tf.ragged.constant([[[1], [2, 2]], [[3, 3, 3], [4, 4, 4, 4]]]) + tf.rank(t) # 3 + ``` + + Args: + input: A `RaggedTensor` + name: A name for the operation (optional). + + Returns: + A `Tensor` of type `int32`. + """ + with ops.name_scope(name, 'RaggedRank', [input]) as name: + if not ragged_tensor.is_ragged(input): + return array_ops.rank(input, name) + + return input.ragged_rank + array_ops.rank(input.flat_values) diff --git a/tensorflow/python/ops/ragged/ragged_dispatch.py b/tensorflow/python/ops/ragged/ragged_dispatch.py index b024bd9f07..52c4142346 100644 --- a/tensorflow/python/ops/ragged/ragged_dispatch.py +++ b/tensorflow/python/ops/ragged/ragged_dispatch.py @@ -418,6 +418,7 @@ _RAGGED_DISPATCH_OPS = [ (array_ops.gather, _ragged_gather_v1, ['params', 'indices']), (array_ops.gather_v2, ragged_array_ops.gather, ['params', 'indices']), (array_ops.gather_nd, ragged_array_ops.gather_nd, ['params', 'indices']), + (array_ops.rank, ragged_array_ops.rank, ['input']), (array_ops.stack, ragged_array_ops.stack, ['[values]']), (array_ops.tile, ragged_array_ops.tile, ['input']), (array_ops.where, ragged_array_ops.where, ['condition', 'x', 'y']), diff --git a/tensorflow/python/ops/ragged/ragged_dispatch_test.py b/tensorflow/python/ops/ragged/ragged_dispatch_test.py index 9d70470f05..0c546accc3 100644 --- a/tensorflow/python/ops/ragged/ragged_dispatch_test.py +++ b/tensorflow/python/ops/ragged/ragged_dispatch_test.py @@ -676,6 +676,10 @@ class RaggedElementwiseOpsTest(ragged_test_util.RaggedTensorTestCase, 1 }, expected=[False, True]), + dict( + op=array_ops.rank, + kwargs={'input': ragged_factory_ops.constant_value([[8, 3], [5]])}, + expected=2), ]) def testRaggedDispatch(self, op, expected, args=(), kwargs=None): if kwargs is None: kwargs = {} diff --git a/tensorflow/python/ops/ragged/ragged_rank_op_test.py b/tensorflow/python/ops/ragged/ragged_rank_op_test.py new file mode 100644 index 0000000000..54eee3bc04 --- /dev/null +++ b/tensorflow/python/ops/ragged/ragged_rank_op_test.py @@ -0,0 +1,89 @@ +# 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. +# ============================================================================== +"""Tests for ragged.rank op.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from absl.testing import parameterized +from tensorflow.python.framework import test_util +from tensorflow.python.ops.ragged import ragged_array_ops +from tensorflow.python.ops.ragged import ragged_factory_ops +from tensorflow.python.ops.ragged import ragged_test_util +from tensorflow.python.platform import googletest + + +@test_util.run_all_in_graph_and_eager_modes +class RaggedRankOpTest(ragged_test_util.RaggedTensorTestCase, + parameterized.TestCase): + + @parameterized.parameters([ + # Rank 0 + dict( + test_input=1, + expected_rank=0, + ), + # Rank 1 + dict( + test_input=[1], + expected_rank=1, + ), + dict( + test_input=[1, 2, 3, 4], + expected_rank=1, + ), + # Rank 2 + dict( + test_input=[[1], [2], [3]], + expected_rank=2, + ), + # Rank 3 + dict( + test_input=[[[1], [2, 3]], [[4], [5, 6, 7]]], + expected_rank=3, + ), + # Rank 3, ragged_rank=2 + dict( + test_input=[[[1], [2, 3], [10, 20]], + [[4], [5, 6, 7]]], + expected_rank=3, + ragged_rank=2, + ), + # Rank 4, ragged_rank=3 with dimensions: {2, (1, 2), (2), (1, 2)} + dict( + test_input=[[[[1], [2]]], + [[[3, 4], [5, 6]], [[7, 8], [9, 10]]]], + expected_rank=4, + ), + # Rank 4, ragged_rank=2 with dimensions: {2, (1, 2), (1, 2), 2} + dict( + test_input=[ + [[[1, 2]]], + [[[5, 6], [7, 8]], + [[9, 10], [11, 12]]]], + expected_rank=4, + ragged_rank=2, + ), + + ]) + def testRaggedRank(self, test_input, expected_rank, ragged_rank=None): + test_input = ragged_factory_ops.constant( + test_input, ragged_rank=ragged_rank) + self.assertAllEqual(ragged_array_ops.rank( + test_input), expected_rank) + + +if __name__ == '__main__': + googletest.main() -- GitLab From 0c2565de1108fc2063ec04b335ef7356298b7ad6 Mon Sep 17 00:00:00 2001 From: Skye Wanderman-Milne Date: Wed, 2 Jan 2019 14:13:27 -0800 Subject: [PATCH 261/622] Don't lower nested control flow if we're compiling to XLA. PiperOrigin-RevId: 227579512 --- .../python/kernel_tests/cond_v2_test.py | 46 ++++++++++++++++++- tensorflow/python/ops/control_flow_util.py | 9 ++++ tensorflow/python/ops/control_flow_util_v2.py | 2 +- 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/tensorflow/python/kernel_tests/cond_v2_test.py b/tensorflow/python/kernel_tests/cond_v2_test.py index 2c99026105..050da5ff6c 100644 --- a/tensorflow/python/kernel_tests/cond_v2_test.py +++ b/tensorflow/python/kernel_tests/cond_v2_test.py @@ -145,6 +145,22 @@ class CondV2Test(test.TestCase): self.assertEqual(cond_op.type, "If") return output, cond_op + def _createNestedCond(self, name): + """Like _createCond but creates a nested cond_v2 call as well.""" + pred = constant_op.constant(True, name="pred") + x = constant_op.constant(1.0, name="x") + + def true_fn(): + return cond_v2.cond_v2(pred, lambda: x, lambda: x + 1) + + def false_fn(): + return x + 2 + + output = cond_v2.cond_v2(pred, true_fn, false_fn, name=name) + cond_op = output.op.inputs[0].op + self.assertEqual(cond_op.type, "If") + return output, cond_op + def testDefaultName(self): with ops.Graph().as_default(): _, cond_op = self._createCond(None) @@ -645,9 +661,14 @@ class CondV2Test(test.TestCase): # Build the cond_v2 in an XLA context xla_context = control_flow_ops.XLAControlFlowContext() xla_context.Enter() - cond_output, _ = self._createCond("cond") + cond_output, cond_op = self._createCond("cond") xla_context.Exit() + # Check lowering attr is not set. + with self.assertRaises(ValueError): + cond_op.get_attr("_lower_using_switch_merge") + + # Check the actual graph that is run. run_options = config_pb2.RunOptions(output_partition_graphs=True) run_metadata = config_pb2.RunMetadata() sess.run(cond_output, options=run_options, run_metadata=run_metadata) @@ -672,6 +693,29 @@ class CondV2Test(test.TestCase): if_found, "An `If` op was not found, but the graph should not be lowered.") + @test_util.run_deprecated_v1 + def testNestedLoweringDisabledInXLA(self): + # Build the cond_v2 in an XLA context + xla_context = control_flow_ops.XLAControlFlowContext() + xla_context.Enter() + _, cond_op = self._createNestedCond("cond") + xla_context.Exit() + + # Check lowering attr is not set for either If node. + with self.assertRaises(ValueError): + cond_op.get_attr("_lower_using_switch_merge") + + nested_if_ops = [] + for func in ops.get_default_graph()._functions.values(): + nested_if_ops.extend(op for op in func._graph.get_operations() + if op.type == "If") + self.assertEqual(len(nested_if_ops), 1) + with self.assertRaises(ValueError): + nested_if_ops[0].get_attr("_lower_using_switch_merge") + + # TODO(skyewm): check the actual graphs that are run once we have a way to + # programmatically access those graphs. + @test_util.run_deprecated_v1 def testLoweringDisabledWithSingleThreadedExecutorContext(self): with self.session(graph=ops.Graph()) as sess: diff --git a/tensorflow/python/ops/control_flow_util.py b/tensorflow/python/ops/control_flow_util.py index 8f5442da5e..ff0dff0042 100644 --- a/tensorflow/python/ops/control_flow_util.py +++ b/tensorflow/python/ops/control_flow_util.py @@ -57,6 +57,15 @@ def InXlaContext(graph): return GetContainingXLAContext(ctxt) is not None +def GraphOrParentsInXlaContext(graph): + while True: + if InXlaContext(graph): return True + try: + graph = graph.outer_graph + except AttributeError: + return False + + def IsInWhileLoop(op): ctxt = op._get_control_flow_context() # pylint: disable=protected-access return GetContainingWhileContext(ctxt) is not None diff --git a/tensorflow/python/ops/control_flow_util_v2.py b/tensorflow/python/ops/control_flow_util_v2.py index 5f56850884..58917ad264 100644 --- a/tensorflow/python/ops/control_flow_util_v2.py +++ b/tensorflow/python/ops/control_flow_util_v2.py @@ -114,7 +114,7 @@ def maybe_set_lowering_attr(op): Args: op: An `If` or `While` Operation. """ - if (not control_flow_util.IsInXLAContext(op) and + if (not control_flow_util.GraphOrParentsInXlaContext(op.graph) and context.context().get_function_call_options().executor_type != "SINGLE_THREADED_EXECUTOR"): # pylint: disable=protected-access -- GitLab From eef699b9f8c158d51a00f127b1a2854d29c6751b Mon Sep 17 00:00:00 2001 From: Russell Power Date: Wed, 2 Jan 2019 14:22:57 -0800 Subject: [PATCH 262/622] Rename shutdown mode field to better fit behavior. PiperOrigin-RevId: 227581084 --- .../contrib/tpu/python/tpu/session_support.py | 16 ++++++++++------ tensorflow/core/util/event.proto | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/tensorflow/contrib/tpu/python/tpu/session_support.py b/tensorflow/contrib/tpu/python/tpu/session_support.py index 3e463823c8..f5735cecc3 100644 --- a/tensorflow/contrib/tpu/python/tpu/session_support.py +++ b/tensorflow/contrib/tpu/python/tpu/session_support.py @@ -185,7 +185,8 @@ def all_worker_devices(session): """Return a list of devices for each worker in the system.""" devices = session.list_devices() return [ - device.name for device in devices + device.name + for device in devices if ':CPU:' in device.name and 'coordinator' not in device.name ] @@ -255,12 +256,14 @@ class WatchdogManager(threading.Thread): self._worker_manager.configure( event_pb2.WorkerHeartbeatRequest( watchdog_config=event_pb2.WatchdogConfig( - timeout_ms=self.shutdown_timeout * 1000,))) + timeout_ms=self.shutdown_timeout * 1000,), + shutdown_mode=event_pb2.WAIT_FOR_COORDINATOR)) def configure_and_run(self): - logging.info('Enabling watchdog timer with %d second timeout ' - 'and %d second ping interval.', - self.shutdown_timeout, self.ping_interval) + logging.info( + 'Enabling watchdog timer with %d second timeout ' + 'and %d second ping interval.', self.shutdown_timeout, + self.ping_interval) self._reset_manager() self._running = True self.start() @@ -269,7 +272,8 @@ class WatchdogManager(threading.Thread): logging.info('Stopping worker watchdog.') self._worker_manager.configure( event_pb2.WorkerHeartbeatRequest( - watchdog_config=event_pb2.WatchdogConfig(timeout_ms=-1,))) + watchdog_config=event_pb2.WatchdogConfig(timeout_ms=-1,), + shutdown_mode=event_pb2.NOT_CONFIGURED)) self._running = False self.join() diff --git a/tensorflow/core/util/event.proto b/tensorflow/core/util/event.proto index 9ce85be551..2d3ae62777 100644 --- a/tensorflow/core/util/event.proto +++ b/tensorflow/core/util/event.proto @@ -95,7 +95,7 @@ enum WorkerHealth { // signal is received. enum WorkerShutdownMode { DEFAULT = 0; - SHUTDOWN_IMMEDIATELY = 1; + NOT_CONFIGURED = 1; WAIT_FOR_COORDINATOR = 2; } -- GitLab From 85ca0e9b815fc8438f1c10379c18393783021e15 Mon Sep 17 00:00:00 2001 From: "Joshua V. Dillon" Date: Wed, 2 Jan 2019 14:31:31 -0800 Subject: [PATCH 263/622] Remove tf.contrib.timeseries dependency on TF distributions. PiperOrigin-RevId: 227582617 --- .../timeseries/python/timeseries/BUILD | 3 +- .../timeseries/python/timeseries/ar_model.py | 19 ++++--------- .../python/timeseries/math_utils.py | 28 +++++++++++++++++++ .../timeseries/state_space_models/BUILD | 2 -- .../filtering_postprocessor.py | 8 ++---- .../state_space_models/kalman_filter.py | 9 +++--- 6 files changed, 43 insertions(+), 26 deletions(-) diff --git a/tensorflow/contrib/timeseries/python/timeseries/BUILD b/tensorflow/contrib/timeseries/python/timeseries/BUILD index 4b90b596b2..a0c3204d41 100644 --- a/tensorflow/contrib/timeseries/python/timeseries/BUILD +++ b/tensorflow/contrib/timeseries/python/timeseries/BUILD @@ -361,9 +361,10 @@ py_library( srcs_version = "PY2AND3", deps = [ ":feature_keys", + ":math_utils", ":model", ":model_utils", - "//tensorflow/contrib/distributions:distributions_py", + "//tensorflow/contrib/rnn:rnn_py", "//tensorflow/python:array_ops", "//tensorflow/python:check_ops", "//tensorflow/python:constant_op", diff --git a/tensorflow/contrib/timeseries/python/timeseries/ar_model.py b/tensorflow/contrib/timeseries/python/timeseries/ar_model.py index bcadf4094e..a8d5e1a49d 100644 --- a/tensorflow/contrib/timeseries/python/timeseries/ar_model.py +++ b/tensorflow/contrib/timeseries/python/timeseries/ar_model.py @@ -18,9 +18,8 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -from tensorflow.contrib import distributions - from tensorflow.contrib.rnn.python.ops import lstm_ops +from tensorflow.contrib.timeseries.python.timeseries import math_utils from tensorflow.contrib.timeseries.python.timeseries import model from tensorflow.contrib.timeseries.python.timeseries import model_utils from tensorflow.contrib.timeseries.python.timeseries.feature_keys import PredictionFeatures @@ -462,8 +461,8 @@ class ARModel(model.TimeSeriesModel): if self.loss == ARModel.NORMAL_LIKELIHOOD_LOSS: covariance = prediction_ops["covariance"] sigma = math_ops.sqrt(gen_math_ops.maximum(covariance, 1e-5)) - normal = distributions.Normal(loc=targets, scale=sigma) - loss_op = -math_ops.reduce_sum(normal.log_prob(prediction)) + loss_op = -math_ops.reduce_sum( + math_utils.normal_log_prob(targets, sigma, prediction)) else: assert self.loss == ARModel.SQUARED_LOSS, self.loss loss_op = math_ops.reduce_sum(math_ops.square(prediction - targets)) @@ -965,16 +964,11 @@ class AnomalyMixtureARModel(ARModel): anomaly_variance = prediction_ops["anomaly_params"] anomaly_sigma = math_ops.sqrt( gen_math_ops.maximum(anomaly_variance, 1e-5)) - normal = distributions.Normal(loc=targets, scale=anomaly_sigma) - log_prob = normal.log_prob(prediction) + log_prob = math_utils.normal_log_prob(targets, anomaly_sigma, prediction) else: assert self._anomaly_distribution == AnomalyMixtureARModel.CAUCHY_ANOMALY anomaly_scale = prediction_ops["anomaly_params"] - cauchy = distributions.StudentT( - df=array_ops.ones([], dtype=anomaly_scale.dtype), - loc=targets, - scale=anomaly_scale) - log_prob = cauchy.log_prob(prediction) + log_prob = math_utils.cauchy_log_prob(targets, anomaly_scale, prediction) return log_prob def loss_op(self, targets, prediction_ops): @@ -983,8 +977,7 @@ class AnomalyMixtureARModel(ARModel): covariance = prediction_ops["covariance"] # Normal data log probability. sigma = math_ops.sqrt(gen_math_ops.maximum(covariance, 1e-5)) - normal1 = distributions.Normal(loc=targets, scale=sigma) - log_prob1 = normal1.log_prob(prediction) + log_prob1 = math_utils.normal_log_prob(targets, sigma, prediction) log_prob1 += math_ops.log(1 - self._anomaly_prior_probability) # Anomaly log probability. log_prob2 = self._anomaly_log_prob(targets, prediction_ops) diff --git a/tensorflow/contrib/timeseries/python/timeseries/math_utils.py b/tensorflow/contrib/timeseries/python/timeseries/math_utils.py index aab3306438..b7375e5055 100644 --- a/tensorflow/contrib/timeseries/python/timeseries/math_utils.py +++ b/tensorflow/contrib/timeseries/python/timeseries/math_utils.py @@ -21,6 +21,8 @@ from __future__ import print_function import collections import math +import numpy as np + from tensorflow.contrib import lookup from tensorflow.contrib.layers.python.layers import layers @@ -43,6 +45,32 @@ from tensorflow.python.ops import variable_scope from tensorflow.python.util import nest +def normal_log_prob(loc, scale, x): + """Computes the Normal log pdf.""" + z = (x - loc) / scale + return -0.5 * (math_ops.square(z) + + np.log(2. * np.pi) + math_ops.log(scale)) + + +def cauchy_log_prob(loc, scale, x): + """Computes the Cauchy log pdf.""" + z = (x - loc) / scale + return (-np.log(np.pi) - math_ops.log(scale) - + math_ops.log1p(math_ops.square(z))) + + +def mvn_tril_log_prob(loc, scale_tril, x): + """Computes the MVN log pdf under tril scale. Doesn't handle batches.""" + x0 = x - loc + z = linalg_ops.matrix_triangular_solve( + scale_tril, x0[..., array_ops.newaxis])[..., 0] + log_det_cov = 2. * math_ops.reduce_sum(math_ops.log( + array_ops.matrix_diag_part(scale_tril)), axis=-1) + d = math_ops.cast(array_ops.shape(scale_tril)[-1], log_det_cov.dtype) + return -0.5 * (math_ops.reduce_sum(math_ops.square(z), axis=-1) + + d * np.log(2. * np.pi) + log_det_cov) + + def clip_covariance( covariance_matrix, maximum_variance_ratio, minimum_variance): """Enforce constraints on a covariance matrix to improve numerical stability. diff --git a/tensorflow/contrib/timeseries/python/timeseries/state_space_models/BUILD b/tensorflow/contrib/timeseries/python/timeseries/state_space_models/BUILD index 125750e763..cf5e749042 100644 --- a/tensorflow/contrib/timeseries/python/timeseries/state_space_models/BUILD +++ b/tensorflow/contrib/timeseries/python/timeseries/state_space_models/BUILD @@ -78,7 +78,6 @@ py_library( srcs = ["kalman_filter.py"], srcs_version = "PY2AND3", deps = [ - "//tensorflow/contrib/distributions:distributions_py", "//tensorflow/contrib/timeseries/python/timeseries:math_utils", "//tensorflow/python:array_ops", "//tensorflow/python:control_flow_ops", @@ -235,7 +234,6 @@ py_library( srcs = ["filtering_postprocessor.py"], srcs_version = "PY2AND3", deps = [ - "//tensorflow/contrib/distributions:distributions_py", "//tensorflow/contrib/timeseries/python/timeseries:math_utils", "//tensorflow/python:array_ops", "//tensorflow/python:check_ops", diff --git a/tensorflow/contrib/timeseries/python/timeseries/state_space_models/filtering_postprocessor.py b/tensorflow/contrib/timeseries/python/timeseries/state_space_models/filtering_postprocessor.py index e9e2ac0aaf..3fa2fbd9f7 100644 --- a/tensorflow/contrib/timeseries/python/timeseries/state_space_models/filtering_postprocessor.py +++ b/tensorflow/contrib/timeseries/python/timeseries/state_space_models/filtering_postprocessor.py @@ -22,8 +22,6 @@ import abc import six -from tensorflow.contrib import distributions - from tensorflow.contrib.timeseries.python.timeseries import math_utils from tensorflow.python.framework import dtypes @@ -91,10 +89,10 @@ def cauchy_alternative_to_gaussian(current_times, current_values, outputs): """ del current_times # unused cauchy_scale = math_utils.entropy_matched_cauchy_scale(outputs["covariance"]) - individual_log_pdfs = distributions.StudentT( - df=array_ops.ones([], dtype=current_values.dtype), + individual_log_pdfs = math_utils.cauchy_log_prob( loc=outputs["mean"], - scale=cauchy_scale).log_prob(current_values) + scale=cauchy_scale, + x=current_values) return math_ops.reduce_sum(individual_log_pdfs, axis=1) diff --git a/tensorflow/contrib/timeseries/python/timeseries/state_space_models/kalman_filter.py b/tensorflow/contrib/timeseries/python/timeseries/state_space_models/kalman_filter.py index a614386121..c0ec797bc5 100644 --- a/tensorflow/contrib/timeseries/python/timeseries/state_space_models/kalman_filter.py +++ b/tensorflow/contrib/timeseries/python/timeseries/state_space_models/kalman_filter.py @@ -18,8 +18,6 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -from tensorflow.contrib import distributions - from tensorflow.contrib.timeseries.python.timeseries import math_utils from tensorflow.python.framework import dtypes @@ -137,9 +135,10 @@ class KalmanFilter(object): with ops.control_dependencies([non_negative_assert]): observation_covariance_cholesky = linalg_ops.cholesky( symmetrized_observation_covariance) - log_prediction_prob = distributions.MultivariateNormalTriL( - predicted_observation, observation_covariance_cholesky).log_prob( - observation) + log_prediction_prob = math_utils.mvn_tril_log_prob( + loc=predicted_observation, + scale_tril=observation_covariance_cholesky, + x=observation) (posterior_state, posterior_state_var) = self.posterior_from_prior_state( prior_state=estimated_state, -- GitLab From 790a635a044c9b33103f1e02f57faf08c86eee36 Mon Sep 17 00:00:00 2001 From: Allen Lavoie Date: Wed, 2 Jan 2019 14:53:55 -0800 Subject: [PATCH 264/622] Stop Model.save_weights from printing a deprication warning (TF format) Also has it save relative paths in the checkpoint proto, which removes a behavior difference between it and CheckpointManager. PiperOrigin-RevId: 227586345 --- tensorflow/python/keras/engine/network.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tensorflow/python/keras/engine/network.py b/tensorflow/python/keras/engine/network.py index 0837ce6780..8e130aef40 100644 --- a/tensorflow/python/keras/engine/network.py +++ b/tensorflow/python/keras/engine/network.py @@ -1382,9 +1382,10 @@ class Network(base_layer.Layer): % (optimizer,)) self._checkpointable_saver.save(filepath, session=session) # Record this checkpoint so it's visible from tf.train.latest_checkpoint. - checkpoint_management.update_checkpoint_state( + checkpoint_management.update_checkpoint_state_internal( save_dir=os.path.dirname(filepath), model_checkpoint_path=filepath, + save_relative_paths=True, all_model_checkpoint_paths=[filepath]) def load_weights(self, filepath, by_name=False): -- GitLab From a7a3bbfcbf53381631dded0e293031e93d056ebc Mon Sep 17 00:00:00 2001 From: Eugene Zhulenev Date: Wed, 2 Jan 2019 15:00:14 -0800 Subject: [PATCH 265/622] [Grappler] Cleanup function/function_optimizer 1. Migrate to absl containers 2. Compute function signature hash without allocating temporary sorted containers PiperOrigin-RevId: 227587343 --- tensorflow/core/grappler/optimizers/BUILD | 2 + .../grappler/optimizers/function_optimizer.cc | 152 +++++++++--------- .../grappler/optimizers/meta_optimizer.cc | 19 +-- tensorflow/core/grappler/utils/BUILD | 4 + tensorflow/core/grappler/utils/functions.cc | 80 ++++----- tensorflow/core/grappler/utils/functions.h | 52 +++--- .../core/grappler/utils/functions_test.cc | 6 +- 7 files changed, 155 insertions(+), 160 deletions(-) diff --git a/tensorflow/core/grappler/optimizers/BUILD b/tensorflow/core/grappler/optimizers/BUILD index 3a1ab28dae..512c3b07b4 100644 --- a/tensorflow/core/grappler/optimizers/BUILD +++ b/tensorflow/core/grappler/optimizers/BUILD @@ -151,6 +151,8 @@ cc_library( "//tensorflow/core/grappler:utils", "//tensorflow/core/grappler/utils:functions", "@com_google_absl//absl/algorithm:container", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/memory", "@com_google_absl//absl/strings", ], diff --git a/tensorflow/core/grappler/optimizers/function_optimizer.cc b/tensorflow/core/grappler/optimizers/function_optimizer.cc index 73c950b3fc..d074676c3d 100644 --- a/tensorflow/core/grappler/optimizers/function_optimizer.cc +++ b/tensorflow/core/grappler/optimizers/function_optimizer.cc @@ -15,10 +15,11 @@ limitations under the License. #include "tensorflow/core/grappler/optimizers/function_optimizer.h" -#include #include #include "absl/algorithm/container.h" +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" #include "absl/memory/memory.h" #include "absl/strings/str_replace.h" #include "absl/strings/substitute.h" @@ -163,10 +164,10 @@ struct FunctionSpecializationSignature { string func_name; bool is_in_fetch_set; - gtl::FlatSet active_outputs; - std::unordered_map type_parameters; - std::unordered_map body_parameters; - std::unordered_map const_inputs; + absl::flat_hash_set active_outputs; + absl::flat_hash_map type_parameters; + absl::flat_hash_map body_parameters; + absl::flat_hash_map const_inputs; bool operator==(const FunctionSpecializationSignature& other) const { bool equals = func_name == other.func_name && @@ -189,48 +190,45 @@ struct FunctionSpecializationSignature { return true; } - // TODO(ezhulenev): Migrate to AbslHashValue. - // TODO(ezhulenev): Optimize performance by computing hashes of unordered - // values first, and then compute a hash of sorted hashes. - struct Hash { - uint64 operator()(FunctionSpecializationSignature const& s) const { - uint64 h = Hash64(s.func_name); - h = Hash64Combine(std::hash()(s.is_in_fetch_set), h); - - // Use std::set/std::map for deterministic iteration order. - - std::set active_outputs(s.active_outputs.begin(), - s.active_outputs.end()); - for (const auto& active_output : active_outputs) { - h = Hash64Combine(std::hash()(active_output), h); - } - - std::map types(s.type_parameters.begin(), - s.type_parameters.end()); - for (const auto& pair : types) { - AttrValue attr_value; - attr_value.set_type(pair.second); - h = Hash64Combine(Hash64(pair.first), h); - h = Hash64Combine(AttrValueHash(attr_value), h); - } - - std::map body(s.body_parameters.begin(), - s.body_parameters.end()); - for (const auto& pair : body) { - h = Hash64Combine(Hash64(pair.first), h); - h = Hash64Combine(FastAttrValueHash(pair.second), h); - } - - std::map inputs(s.const_inputs.begin(), - s.const_inputs.end()); - for (const auto& pair : inputs) { - h = Hash64Combine(std::hash()(pair.first), h); - h = Hash64Combine(Hash64(pair.second), h); - } - - return h; - } - }; + template + friend H AbslHashValue(H h, const FunctionSpecializationSignature& s) { + H base = H::combine(std::move(h), s.func_name, s.is_in_fetch_set); + + // First pre-compute hashes for all values in collections with + // non-deterministic iteration order. + std::vector hashes; + hashes.reserve(s.active_outputs.size() // + + s.type_parameters.size() * 2 // + + s.body_parameters.size() * 2 // + + s.const_inputs.size() * 2); + + absl::c_transform(s.active_outputs, std::back_inserter(hashes), + hash()); + + using TypeParam = std::pair; + absl::c_for_each(s.type_parameters, [&hashes](const TypeParam& type_param) { + AttrValue attr_value; + attr_value.set_type(type_param.second); + hashes.push_back(Hash64(type_param.first)); + hashes.push_back(AttrValueHash(attr_value)); + }); + + using BodyParam = std::pair; + absl::c_for_each(s.body_parameters, [&hashes](const BodyParam& body_param) { + hashes.push_back(Hash64(body_param.first)); + hashes.push_back(FastAttrValueHash(body_param.second)); + }); + + using ConstInput = std::pair; + absl::c_for_each(s.const_inputs, [&hashes](const ConstInput& const_input) { + hashes.push_back(hash()(const_input.first)); + hashes.push_back(Hash64(const_input.second)); + }); + + // Combine all pre-computed hashes in a deterministic order. + absl::c_sort(hashes); + return H::combine_contiguous(std::move(base), hashes.data(), hashes.size()); + } }; struct FunctionSpecialization { @@ -238,13 +236,13 @@ struct FunctionSpecialization { // True if the function caller node is in GrapplerItem fetch set. bool is_in_fetch_set; // Names of the tensors that were pushed down into the function body. - gtl::FlatSet const_inputs; + absl::flat_hash_set const_inputs; // Control dependencies of pushed down const inputs have to be attached to // function caller node. - gtl::FlatSet control_deps; + absl::flat_hash_set control_deps; // Output tensors (ports) that consumed by other nodes in the graph or in a // GrapplerItem fetch set. - gtl::FlatSet active_outputs; + absl::flat_hash_set active_outputs; // Mapping from original function output port to the output port of // specialized function. If function specialization changes the number of // function outputs it's required to update all node consumers. @@ -285,12 +283,13 @@ class FunctionOptimizerContext { return flr_; } - const gtl::FlatMap& + const absl::flat_hash_map& tensor_mapping() const { return tensor_mapping_; } - const gtl::FlatMap>& control_overrides() const { + const absl::flat_hash_map>& control_overrides() + const { return control_overrides_; } @@ -298,7 +297,9 @@ class FunctionOptimizerContext { const string& grappler_item_id() const { return grappler_item_id_; } - const gtl::FlatSet& fetch_tensors() const { return fetch_tensors_; } + const absl::flat_hash_set& fetch_tensors() const { + return fetch_tensors_; + } const DeviceSet* devices() const { // Create fake devices lazily only if we need a DeviceSet. @@ -365,7 +366,7 @@ class FunctionOptimizerContext { private: void InitializeTrulyConstNodes(const GrapplerItem& item) { - gtl::FlatSet feed_nodes; + absl::flat_hash_set feed_nodes; for (const auto& feed : item.feed) { feed_nodes.insert(NodeName(feed.first)); } @@ -411,7 +412,7 @@ class FunctionOptimizerContext { FunctionLibraryRuntime* flr_ = nullptr; // Fully defined names of the devices available to the GrapplerItem. - const gtl::FlatSet available_device_names_; + const absl::flat_hash_set available_device_names_; // List of available `FakedDevices` (lazily initialized, see devices()). mutable std::vector> available_devices_; @@ -421,16 +422,15 @@ class FunctionOptimizerContext { mutable DeviceSet available_device_set_; // Nodes that are Const and not in feed. - std::unordered_map truly_const_nodes_; + absl::flat_hash_map truly_const_nodes_; // Specialized functions. - std::unordered_map + absl::flat_hash_map specialized_functions_; // GrapplerItem.fetch is a vector of tensors. - gtl::FlatSet fetch_tensors_; // format: node_name:port - gtl::FlatSet fetch_nodes_; // format: node_name + absl::flat_hash_set fetch_tensors_; // format: node_name:port + absl::flat_hash_set fetch_nodes_; // format: node_name // After function inlining and specialization, the optimized graph might be in // invalid state, nodes can read from non-existing function call nodes that @@ -439,7 +439,7 @@ class FunctionOptimizerContext { // // Tensor mapping that has to be applied to the graph after all functions // optimizations (invalidated tensor id -> optimized graph tensor id). - gtl::FlatMap + absl::flat_hash_map tensor_mapping_; // When we inline a function into the optimized graph, we no longer have the @@ -448,7 +448,7 @@ class FunctionOptimizerContext { // to all side-effectful ops inside the function body. // // Invalidated function call node name -> Inlined side-effectful nodes - gtl::FlatMap> control_overrides_; + absl::flat_hash_map> control_overrides_; // Use graph view to find active outputs of the function caller nodes. GraphView graph_view_; @@ -472,10 +472,10 @@ const FunctionDef* FindFunctionCall(const FunctionOptimizerContext& ctx, return ctx.function_library().Find(node.op()); } -gtl::FlatSet GetActiveOutputs(const NodeDef& node, - const FunctionOptimizerContext& ctx, - int size_hint = 0) { - gtl::FlatSet active_outputs; +absl::flat_hash_set GetActiveOutputs(const NodeDef& node, + const FunctionOptimizerContext& ctx, + int size_hint = 0) { + absl::flat_hash_set active_outputs; active_outputs.reserve(static_cast(size_hint)); // 1. Output can be consumed by the other graph node. @@ -508,7 +508,7 @@ bool HasUnusedOutputs(const NodeDef& func_node, const FunctionDef& func, // number of output args is the same as number of possible function caller // node outputs. int num_outputs = func.signature().output_arg_size(); - const gtl::FlatSet active_outputs = + const absl::flat_hash_set active_outputs = GetActiveOutputs(func_node, ctx, /*size_hind*/ num_outputs); return active_outputs.size() != num_outputs; @@ -519,7 +519,7 @@ bool HasUnusedOutputs(const NodeDef& func_node, const FunctionDef& func, FunctionDefLibrary PruneFunctionLibrary(const FunctionLibraryDefinition& flib, const GraphDef& optimized_graph) { FunctionLibraryDefinition pruned_flib = - ReachableFunctionLibraryDefinition(flib, optimized_graph); + flib.ReachableDefinitions(optimized_graph); int pruned_functions = static_cast(pruned_flib.num_functions()) - static_cast(flib.num_functions()); @@ -534,8 +534,8 @@ FunctionDefLibrary PruneFunctionLibrary(const FunctionLibraryDefinition& flib, Status PushDownConstInputs(const NodeDef& func_node, const FunctionOptimizerContext& ctx, GrapplerFunctionItem* item, - gtl::FlatSet* const_inputs, - gtl::FlatSet* control_deps) { + absl::flat_hash_set* const_inputs, + absl::flat_hash_set* control_deps) { // Record node control dependencies in the control_deps set. const auto record_control_deps = [&](const NodeDef* const_input) { for (int i = const_input->input_size() - 1; i >= 0; --i) { @@ -585,7 +585,7 @@ void RemovePushedDownConstInputs(const FunctionSpecialization& specialization, // Attach control dependencies of pushed down const input to the caller node. if (!specialization.control_deps.empty()) { - gtl::FlatSet existing_control_deps; + absl::flat_hash_set existing_control_deps; for (const string& input : keep_inputs) { existing_control_deps.insert(AsControlDependency(NodeName(input))); @@ -797,8 +797,8 @@ Status SpecializeFunction(const NodeDef& func_node, const FunctionDef& func, // Push const inputs into the function body, and keep track of their control // dependencies. - gtl::FlatSet const_inputs; - gtl::FlatSet control_deps; + absl::flat_hash_set const_inputs; + absl::flat_hash_set control_deps; TF_RETURN_IF_ERROR(PushDownConstInputs(func_node, *ctx, &item, &const_inputs, &control_deps)); @@ -1005,7 +1005,7 @@ Status InlineDirectFunctionCall(const NodeDef& func_node, // Mapping from input placeholder name to function input position. int idx = 0; - std::unordered_map input_placeholders_idx; + absl::flat_hash_map input_placeholders_idx; for (const InputArgExpansion& input_arg : item.inputs()) { for (const string& placeholder : input_arg.placeholders) { input_placeholders_idx[placeholder] = idx++; @@ -1699,7 +1699,7 @@ Status FunctionOptimizer::Optimize(Cluster*, const GrapplerItem& item, if (!ctx.control_overrides().empty()) { for (NodeDef& node : *optimized_graph->mutable_node()) { // Keep track of new control inputs to the node. - gtl::FlatSet add_ctrl_inputs; + absl::flat_hash_set add_ctrl_inputs; // Remove all invalidated control inputs. for (int idx = 0; idx < node.input_size(); /* see below */) { diff --git a/tensorflow/core/grappler/optimizers/meta_optimizer.cc b/tensorflow/core/grappler/optimizers/meta_optimizer.cc index 67699b093d..a84bb1d62f 100644 --- a/tensorflow/core/grappler/optimizers/meta_optimizer.cc +++ b/tensorflow/core/grappler/optimizers/meta_optimizer.cc @@ -427,6 +427,14 @@ Status MetaOptimizer::Optimize(Cluster* cluster, const GrapplerItem& item, VLOG(1) << "Starting optimization for grappler item: " << item.id; optimization_results_.clear(); + // Constructs a FunctionLibraryDefinition with functions that are reachable + // from the nodes of the graph. + const auto minimized_flib = + [](const GraphDef& graph) -> FunctionLibraryDefinition { + return FunctionLibraryDefinition(OpRegistry::Global(), graph.library()) + .ReachableDefinitions(graph); + }; + // 0. Original graph might contain a huge function library, that is mostly // unused. This library copied over by each individual Grappler optimizer, // which adds a huge overhead. Before starting optimization passes we just @@ -436,11 +444,7 @@ Status MetaOptimizer::Optimize(Cluster* cluster, const GrapplerItem& item, GraphDef trimmed_graph; // do not copy graph with a potentially huge library *trimmed_graph.mutable_node() = item.graph.node(); *trimmed_graph.mutable_versions() = item.graph.versions(); - *trimmed_graph.mutable_library() = - grappler::ReachableFunctionLibraryDefinition( - FunctionLibraryDefinition(OpRegistry::Global(), item.graph.library()), - item.graph) - .ToProto(); + *trimmed_graph.mutable_library() = minimized_flib(item.graph).ToProto(); GrapplerItem trimmed_item = item.WithGraph(std::move(trimmed_graph)); @@ -472,10 +476,7 @@ Status MetaOptimizer::Optimize(Cluster* cluster, const GrapplerItem& item, } // 2. Optimize functions reachable from the optimized graph. - FunctionLibraryDefinition flib = ReachableFunctionLibraryDefinition( - FunctionLibraryDefinition(OpRegistry::Global(), - optimized_graph->library()), - *optimized_graph); + FunctionLibraryDefinition flib = minimized_flib(*optimized_graph); // Find functions for which we might need to compute a gradient at runtime. absl::flat_hash_set differentiable_functions; diff --git a/tensorflow/core/grappler/utils/BUILD b/tensorflow/core/grappler/utils/BUILD index cd69cf895c..89417f85c2 100644 --- a/tensorflow/core/grappler/utils/BUILD +++ b/tensorflow/core/grappler/utils/BUILD @@ -178,6 +178,9 @@ cc_library( "//tensorflow/core/grappler:grappler_item", "//tensorflow/core/grappler:op_types", "//tensorflow/core/grappler:utils", + "@com_google_absl//absl/algorithm:container", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/strings", ], ) @@ -196,6 +199,7 @@ tf_cc_test( "//tensorflow/core:test", "//tensorflow/core:test_main", "//tensorflow/core:testlib", + "@com_google_absl//absl/container:flat_hash_map", ], ) diff --git a/tensorflow/core/grappler/utils/functions.cc b/tensorflow/core/grappler/utils/functions.cc index f2894a942b..7c2180ae40 100644 --- a/tensorflow/core/grappler/utils/functions.cc +++ b/tensorflow/core/grappler/utils/functions.cc @@ -14,8 +14,9 @@ limitations under the License. ==============================================================================*/ #include "tensorflow/core/grappler/utils/functions.h" -#include - +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/strings/str_cat.h" #include "absl/strings/substitute.h" #include "tensorflow/core/framework/attr_value.pb.h" #include "tensorflow/core/framework/function.h" @@ -28,7 +29,6 @@ limitations under the License. #include "tensorflow/core/framework/versions.pb.h" #include "tensorflow/core/grappler/op_types.h" #include "tensorflow/core/grappler/utils.h" -#include "tensorflow/core/lib/gtl/map_util.h" #include "tensorflow/core/lib/strings/scanner.h" namespace tensorflow { @@ -76,16 +76,6 @@ Status ResolveFunctionBodyNodeAttrPlaceholders( } // namespace -FunctionLibraryDefinition ReachableFunctionLibraryDefinition( - const FunctionLibraryDefinition& flib, const GraphDef& graph) { - return flib.ReachableDefinitions(graph); -} - -FunctionLibraryDefinition ReachableFunctionLibraryDefinition( - const FunctionLibraryDefinition& flib, const FunctionDef& func) { - return flib.ReachableDefinitions(func); -} - void GrapplerFunctionConnectivity::RegisterInputArgExpansion( InputArgExpansion input_arg_expansion) { string input_name = input_arg_expansion.input_name; @@ -94,7 +84,7 @@ void GrapplerFunctionConnectivity::RegisterInputArgExpansion( for (int i = 0; i < placeholders.size(); ++i) { const string& placeholder = input_arg_expansion.placeholders[i]; input_arg_placeholders_.insert( - {placeholder, InputArgPlaceholder{input_name, /*input_position=*/i}}); + {placeholder, InputArgPlaceholder{input_name, /*input_index=*/i}}); } input_arg_expansions_.insert( {std::move(input_name), std::move(input_arg_expansion)}); @@ -193,7 +183,7 @@ Status GrapplerFunctionConnectivity::ExpandFunctionDefInput( // If position is not defined expand node output range for (int i = output_range.first; i < output_range.second; ++i) { graph_def_inputs->push_back( - i == 0 ? node_name : strings::StrCat(node_name, ":", i)); + i == 0 ? node_name : absl::StrCat(node_name, ":", i)); } } else { if (position > (output_range.second - output_range.first)) { @@ -203,7 +193,7 @@ Status GrapplerFunctionConnectivity::ExpandFunctionDefInput( } int pos = output_range.first + position; graph_def_inputs->push_back( - pos == 0 ? node_name : strings::StrCat(node_name, ":", pos)); + pos == 0 ? node_name : absl::StrCat(node_name, ":", pos)); } return Status::OK(); @@ -232,39 +222,39 @@ Status GrapplerFunctionConnectivity::ExpandNodeInputs( Status GrapplerFunctionConnectivity::AsFunctionDefInput( const string& graph_def_input, string* func_def_input) const { - using gtl::FindOrNull; - if (IsControlInput(graph_def_input)) { *func_def_input = graph_def_input; return Status::OK(); } - int position; - string node_name = ParseNodeName(graph_def_input, &position); - CHECK_GE(position, 0); + const TensorId tensor = ParseTensorName(graph_def_input); + DCHECK_GE(tensor.index(), 0); + + const absl::string_view node_name = tensor.node(); + const int index = tensor.index(); // Check if it's an input arg placeholder - if (position == 0) { - const InputArgPlaceholder* placeholder = - FindOrNull(input_arg_placeholders_, node_name); - if (placeholder != nullptr) { - *func_def_input = strings::StrCat(placeholder->input_name, ":", - placeholder->input_position); + if (tensor.index() == 0) { + const auto is_input_placeholder = input_arg_placeholders_.find(node_name); + if (is_input_placeholder != input_arg_placeholders_.end()) { + const InputArgPlaceholder& placeholder = is_input_placeholder->second; + *func_def_input = + absl::StrCat(placeholder.input_name, ":", placeholder.input_index); return Status::OK(); } } // It must be output from one of the function body nodes - const tensorflow::NameRangeMap* outputs_range_map = - FindOrNull(function_body_outputs_, node_name); - if (outputs_range_map != nullptr) { - for (const auto& el : *outputs_range_map) { + const auto is_body_output = function_body_outputs_.find(tensor.node()); + if (is_body_output != function_body_outputs_.end()) { + const tensorflow::NameRangeMap& outputs_range_map = is_body_output->second; + + for (const auto& el : outputs_range_map) { const auto& output_name = el.first; const auto& output_range = el.second; - if (position >= output_range.first && position < output_range.second) { - int pos = position - output_range.first; - *func_def_input = - strings::StrCat(node_name, ":", output_name, ":", pos); + if (index >= output_range.first && index < output_range.second) { + int pos = index - output_range.first; + *func_def_input = absl::StrCat(node_name, ":", output_name, ":", pos); return Status::OK(); } } @@ -426,7 +416,7 @@ bool IsParametrized(const FunctionDef& func) { Status InstantiationTypeParameters( const FunctionDef& func, const AttrSlice& func_instantiation_attr, - std::unordered_map* type_parameters) { + absl::flat_hash_map* type_parameters) { if (!type_parameters->empty()) { return errors::InvalidArgument("Type parameters output map must be empty"); } @@ -454,7 +444,7 @@ Status InstantiationTypeParameters( Status InstantiationBodyParameters( const FunctionDef& func, const AttrSlice& func_instantiation_attr, - std::unordered_map* body_parameters) { + absl::flat_hash_map* body_parameters) { if (!body_parameters->empty()) { return errors::InvalidArgument("Body parameters output map must be empty"); } @@ -514,8 +504,7 @@ Status MakeGrapplerFunctionItem(const FunctionDef& func, // Function body shares the library with the graph that instantiated it. We do // not need a full copy of the function library, just the reachable subset. - *function_body.mutable_library() = - ReachableFunctionLibraryDefinition(flib, func).ToProto(); + *function_body.mutable_library() = flib.ReachableDefinitions(func).ToProto(); VLOG(3) << absl::Substitute( "Deleted $0 unreachable functions from the Grappler function item " @@ -645,7 +634,7 @@ Status RegisterGrapplerFunctionConnectivity( return Status::OK(); } -Status ReplaceInputWithConst(const NodeDef& input_const, int input_position, +Status ReplaceInputWithConst(const NodeDef& input_const, int input_index, GrapplerFunctionItem* item) { if (!IsConstant(input_const)) { return errors::InvalidArgument("Input node ", input_const.name(), @@ -657,7 +646,7 @@ Status ReplaceInputWithConst(const NodeDef& input_const, int input_position, // Find input arg expansion and input placeholder position in it for the // given function input position. InputArgExpansion* input_arg_expansion = nullptr; - int placeholder_idx = input_position; + int placeholder_idx = input_index; for (InputArgExpansion& input : inputs) { if (placeholder_idx < input.placeholders.size()) { @@ -668,9 +657,8 @@ Status ReplaceInputWithConst(const NodeDef& input_const, int input_position, } if (input_arg_expansion == nullptr) { - return errors::InvalidArgument( - "Input placeholder not found: input_position=", input_position, - " function=", item->id); + return errors::InvalidArgument("Input placeholder not found: input_index=", + input_index, " function=", item->id); } // Delete placeholder from input expansion. @@ -699,7 +687,7 @@ Status ReplaceInputWithConst(const NodeDef& input_const, int input_position, return Status::OK(); } -Status RemoveUnusedOutputs(const gtl::FlatSet& active_outputs, +Status RemoveUnusedOutputs(const absl::flat_hash_set& active_outputs, GrapplerFunctionItem* item, std::vector>* output_mapping) { DCHECK(output_mapping->empty()); @@ -713,7 +701,7 @@ Status RemoveUnusedOutputs(const gtl::FlatSet& active_outputs, } } - gtl::FlatSet unused_output_args; + absl::flat_hash_set unused_output_args; const auto is_unused_output_arg = [&](const OutputArgExpansion& output) { return unused_output_args.find(&output) != unused_output_args.end(); diff --git a/tensorflow/core/grappler/utils/functions.h b/tensorflow/core/grappler/utils/functions.h index 038cf5f527..ce8a3e5ac7 100644 --- a/tensorflow/core/grappler/utils/functions.h +++ b/tensorflow/core/grappler/utils/functions.h @@ -18,7 +18,9 @@ limitations under the License. #include #include -#include + +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" #include "tensorflow/core/framework/attr_value.pb.h" #include "tensorflow/core/framework/function.h" #include "tensorflow/core/framework/function.pb.h" @@ -30,13 +32,6 @@ limitations under the License. namespace tensorflow { namespace grappler { -// Returns a copy of FunctionLibraryDefinition with subset of functions that are -// reachable from the nodes of the graph. -FunctionLibraryDefinition ReachableFunctionLibraryDefinition( - const FunctionLibraryDefinition& flib, const GraphDef& graph); -FunctionLibraryDefinition ReachableFunctionLibraryDefinition( - const FunctionLibraryDefinition& flib, const FunctionDef& func); - // Depending on the function instantiation attributes, input argument to the // function might be a single tensor, list of tensors of the same type, or a // list of tensors of different types. @@ -81,12 +76,12 @@ class GrapplerFunctionConnectivity { void RegisterFunctionBodyOutputs(const string& node_name, tensorflow::NameRangeMap&& outputs); - // Expand input encoded in FunctionDef format (name[:output][:position]) into + // Expands input encoded in FunctionDef format (name[:output][:position]) into // multiple inputs in GraphDef format (name[:position]). Status ExpandFunctionDefInput(const string& func_def_input, std::vector* graph_def_inputs) const; - // Update Node inputs from FunctionDef to GraphDef format. + // Updates Node inputs from FunctionDef to GraphDef format. Status ExpandNodeInputs(NodeDef* function_body_node) const; // When expanding inputs in function def format, single input might be @@ -96,29 +91,31 @@ class GrapplerFunctionConnectivity { // instantiation attributes and length of input args (and node def outputs) is // known. - // Map from GraphDef input format to FunctionDef input format using registered - // input arg expansion and function body outputs. + // Converts input name from GraphDef format (name[:position]) to the + // FunctionDef input format (name[:output][:position]) using registered input + // arg expansion and function body outputs. Status AsFunctionDefInput(const string& graph_def_input, string* func_def_input) const; - // Update Node inputs from GraphDef to FunctionDef format. + // Updates Node inputs from GraphDef to FunctionDef format. Status AsFunctionDefNode(NodeDef* function_body_node) const; private: // Mapping from input name to input arg expansion. - std::unordered_map input_arg_expansions_; + absl::flat_hash_map input_arg_expansions_; // Mapping from function body node name to output names range map. - std::unordered_map function_body_outputs_; + absl::flat_hash_map function_body_outputs_; + // For each placeholder added to the function instantiation graph, we keep a + // mapping back to the function input argument name and index. struct InputArgPlaceholder { - string input_name; // Name of the function input argument. - int input_position; // Index of a tensor in the function input argument - // expansion, it can be greater than `0` if input - // argument is a list of tensors (aka list(type)). + string input_name; // Name of the function input argument. + int input_index; // Index of a tensor in the function input argument + // expansion, it can be greater than `0` if input + // argument is a list of tensors (aka list(type)). }; - // Mapping from input arg placeholder to the function input tensor. - std::unordered_map input_arg_placeholders_; + absl::flat_hash_map input_arg_placeholders_; }; // Get Function type attributes using attributes of a node that instantiated @@ -172,7 +169,8 @@ class GrapplerFunctionItem : public GrapplerItem { friend Status ReplaceInputWithConst(const NodeDef&, int, GrapplerFunctionItem*); friend Status RemoveUnusedOutputs( - const gtl::FlatSet& active_outputs, GrapplerFunctionItem* item, + const absl::flat_hash_set& active_outputs, + GrapplerFunctionItem* item, std::vector>* output_mapping); GrapplerFunctionItem(string func_name, string description, @@ -191,7 +189,7 @@ class GrapplerFunctionItem : public GrapplerItem { std::set input_arg_placeholders_; - bool is_stateful_; + bool is_stateful_ = false; }; // Check if function input/output types are fully defined only at instantiation @@ -210,14 +208,14 @@ bool IsParametrized(const FunctionDef& func); // caller node. Return error if type can't be resolved. Status InstantiationTypeParameters( const FunctionDef& func, const AttrSlice& func_instantiation_attr, - std::unordered_map* type_parameters); + absl::flat_hash_map* type_parameters); // Resolve function instantiation body parameters (values for the function body // attr placeholders) from the attributes of the caller node. Return error if // type can't be resolved. Status InstantiationBodyParameters( const FunctionDef& func, const AttrSlice& func_instantiation_attr, - std::unordered_map* body_parameters); + absl::flat_hash_map* body_parameters); // Register GrapplerFunctionItem input arg expansion and function body outputs // in the GrapplerFunctionConnectivity. Use function library definition to @@ -227,7 +225,7 @@ Status RegisterGrapplerFunctionConnectivity( GrapplerFunctionConnectivity* connectivity); // Replace one of the function inputs with a constant. -Status ReplaceInputWithConst(const NodeDef& input_const, int input_position, +Status ReplaceInputWithConst(const NodeDef& input_const, int input_index, GrapplerFunctionItem* item); // Remove function output arguments that do not have any active outputs (output @@ -236,7 +234,7 @@ Status ReplaceInputWithConst(const NodeDef& input_const, int input_position, // potentially be connected to the same output argument (in case of tensor list // outputs). Add output mapping for all active outputs that changed it's output // position (std::pair). -Status RemoveUnusedOutputs(const gtl::FlatSet& active_outputs, +Status RemoveUnusedOutputs(const absl::flat_hash_set& active_outputs, GrapplerFunctionItem* item, std::vector>* output_mapping); diff --git a/tensorflow/core/grappler/utils/functions_test.cc b/tensorflow/core/grappler/utils/functions_test.cc index 5923850eca..29d6100d23 100644 --- a/tensorflow/core/grappler/utils/functions_test.cc +++ b/tensorflow/core/grappler/utils/functions_test.cc @@ -14,6 +14,8 @@ limitations under the License. ==============================================================================*/ #include "tensorflow/core/grappler/utils/functions.h" + +#include "absl/container/flat_hash_map.h" #include "tensorflow/cc/ops/standard_ops.h" #include "tensorflow/core/framework/function_testlib.h" #include "tensorflow/core/framework/node_def.pb.h" @@ -77,7 +79,7 @@ TEST_F(FunctionsTest, InstantiationParameters) { func_instantiation_attr["B"].set_type(DT_INT32); func_instantiation_attr["C"].set_type(DT_DOUBLE); - std::unordered_map type_parameters; + absl::flat_hash_map type_parameters; TF_EXPECT_OK(InstantiationTypeParameters( func, AttrSlice(&func_instantiation_attr), &type_parameters)); @@ -86,7 +88,7 @@ TEST_F(FunctionsTest, InstantiationParameters) { EXPECT_EQ(DT_INT32, type_parameters["B"]); EXPECT_EQ(DT_DOUBLE, type_parameters["C"]); - std::unordered_map body_parameters; + absl::flat_hash_map body_parameters; TF_EXPECT_OK(InstantiationBodyParameters( func, AttrSlice(&func_instantiation_attr), &body_parameters)); -- GitLab From 26d8486baa67d2c545aa4384f9d666dc7b9ad530 Mon Sep 17 00:00:00 2001 From: Rachel Lim Date: Wed, 2 Jan 2019 15:11:44 -0800 Subject: [PATCH 266/622] documentation fix PiperOrigin-RevId: 227589407 --- tensorflow/python/data/experimental/ops/optimization_options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/python/data/experimental/ops/optimization_options.py b/tensorflow/python/data/experimental/ops/optimization_options.py index 41a819d94b..eb95484ac5 100644 --- a/tensorflow/python/data/experimental/ops/optimization_options.py +++ b/tensorflow/python/data/experimental/ops/optimization_options.py @@ -33,7 +33,7 @@ class OptimizationOptions(options.OptionsBase): ```python options = tf.data.Options() options.experimental_optimization.map_vectorization = True - options.apply_default_optimizations = False + options.experimental_optimization.apply_default_optimizations = False dataset = dataset.with_options(options) ``` """ -- GitLab From 29bc2e98eaf1dd730574886ffe1b317e982323d4 Mon Sep 17 00:00:00 2001 From: Sourabh Bajaj Date: Wed, 2 Jan 2019 15:47:20 -0800 Subject: [PATCH 267/622] Validate that all variables under the model were created in the distribution strategy scope. PiperOrigin-RevId: 227595107 --- .../contrib/distribute/python/keras_test.py | 44 +++++++++++++++++++ tensorflow/python/keras/engine/training.py | 14 ++++++ 2 files changed, 58 insertions(+) diff --git a/tensorflow/contrib/distribute/python/keras_test.py b/tensorflow/contrib/distribute/python/keras_test.py index b99d11b923..ba5a5e6400 100644 --- a/tensorflow/contrib/distribute/python/keras_test.py +++ b/tensorflow/contrib/distribute/python/keras_test.py @@ -245,6 +245,18 @@ def all_strategy_combinations(): return strategy_minus_tpu_combinations() + tpu_strategy_combinations() +def all_strategy_combinations_minus_default(): + strategy_minus_default_combinations = combinations.combine( + distribution=[ + combinations.one_device_strategy, + combinations.mirrored_strategy_with_gpu_and_cpu, + combinations.mirrored_strategy_with_two_gpus, + combinations.core_mirrored_strategy_with_gpu_and_cpu, + combinations.core_mirrored_strategy_with_two_gpus], + mode=['graph', 'eager']) + return strategy_minus_default_combinations + tpu_strategy_combinations() + + # TODO(priyag): Add v2 optimizers here. def strategy_and_optimizer_combinations(): return combinations.times( @@ -1149,5 +1161,37 @@ class TestDistributionStrategyWithNormalizationLayer( np.testing.assert_allclose(out.std(), 1.0, atol=1e-1) +class TestDistributionStrategyVariableValidation(test.TestCase, + parameterized.TestCase): + + @combinations.generate(all_strategy_combinations_minus_default()) + def test_layer_outside_scope(self, distribution): + with self.cached_session(): + with self.assertRaisesRegexp( + ValueError, 'was not created in the distribution strategy'): + x = keras.layers.Input(shape=(3,), name='input') + y = keras.layers.Dense(4, name='dense')(x) + with distribution.scope(): + model = keras.Model(x, y) + optimizer = gradient_descent.GradientDescentOptimizer(0.001) + loss = 'mse' + metrics = ['mae', keras.metrics.CategoricalAccuracy()] + model.compile(optimizer, loss, metrics=metrics) + + @combinations.generate(all_strategy_combinations_minus_default()) + def test_model_outside_scope(self, distribution): + with self.cached_session(): + with self.assertRaisesRegexp( + ValueError, 'was not created in the distribution strategy'): + x = keras.layers.Input(shape=(3,), name='input') + y = keras.layers.Dense(4, name='dense')(x) + model = keras.Model(x, y) + with distribution.scope(): + optimizer = gradient_descent.GradientDescentOptimizer(0.001) + loss = 'mse' + metrics = ['mae', keras.metrics.CategoricalAccuracy()] + model.compile(optimizer, loss, metrics=metrics) + + if __name__ == '__main__': test.main() diff --git a/tensorflow/python/keras/engine/training.py b/tensorflow/python/keras/engine/training.py index be3afafd3e..1dc3b81cfc 100644 --- a/tensorflow/python/keras/engine/training.py +++ b/tensorflow/python/keras/engine/training.py @@ -548,6 +548,20 @@ class Model(Network): trainable_weights = self.trainable_weights self._collected_trainable_weights = trainable_weights + # Validate all variables were correctly created in distribution scope. + if self._distribution_strategy and not self._compile_distribution: + for v in self.variables: + if v.distribute_strategy is not self._distribution_strategy: + raise ValueError( + 'Variable (%s) was not created in the distribution strategy ' + 'scope of (%s). It is most likely due to not all layers or ' + 'the model or optimizer being created outside the distribution ' + 'strategy scope. Try to make sure your code looks similar ' + 'to the following.\n' + 'with strategy.scope():\n' + ' model=_create_model()\n' + ' model.compile(...)'% (v, self._distribution_strategy)) + @property def metrics(self): """Returns the model's metrics added using `compile`, `add_metric` APIs.""" -- GitLab From 6af0243ba6842330d7bcce6499287f6e9a640c9f Mon Sep 17 00:00:00 2001 From: Penporn Koanantakool Date: Wed, 2 Jan 2019 15:52:17 -0800 Subject: [PATCH 268/622] Fix a typo in Near. Before this change, when the values are not 'Near', the program reports the address of b instead of the value of b[i]. PiperOrigin-RevId: 227595818 --- tensorflow/core/framework/tensor_testutil.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/core/framework/tensor_testutil.h b/tensorflow/core/framework/tensor_testutil.h index 3163002851..b58292b3b0 100644 --- a/tensorflow/core/framework/tensor_testutil.h +++ b/tensorflow/core/framework/tensor_testutil.h @@ -206,7 +206,7 @@ struct Expector { const T* b = y.flat().data(); for (int i = 0; i < size; ++i) { EXPECT_TRUE(Near(a[i], b[i], abs_err)) - << "a = " << a[i] << " b = " << b << " index = " << i; + << "a = " << a[i] << " b = " << b[i] << " index = " << i; } } }; -- GitLab From f0dde544f263013867e8c66c82db85e66c42d530 Mon Sep 17 00:00:00 2001 From: Michael Kuperstein Date: Wed, 2 Jan 2019 16:00:27 -0800 Subject: [PATCH 269/622] [XLA] Don't go out of bounds in Shape::dynamic_dimensions_. PiperOrigin-RevId: 227596997 --- tensorflow/compiler/xla/shape.cc | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tensorflow/compiler/xla/shape.cc b/tensorflow/compiler/xla/shape.cc index c04a916154..f05d816268 100644 --- a/tensorflow/compiler/xla/shape.cc +++ b/tensorflow/compiler/xla/shape.cc @@ -27,7 +27,19 @@ Shape::Shape(const ShapeProto& shape_proto) { for (const int64 dimension : shape_proto.dimensions()) { add_dimensions(dimension); } - for (int i = 0; i < shape_proto.is_dynamic_dimension_size(); i++) { + // A malformed proto may have different is_dynamic_dimension_size and + // dimensions_size. Since C++ is evil, and we have no good way of bailing out + // in a constructor, conservatively trim the is_dynamic_dimension size. + // TODO(b/120111794): Make this a hard error when we have a factory method + // instead of a constructor. + if (shape_proto.dimensions_size() != + shape_proto.is_dynamic_dimension_size()) { + LOG(ERROR) << "Malformed shape proto: number of is_dynamic_dimension " + "fields does not match number of dimension fields"; + } + int64 num_dynamic_dimension_fields = std::min( + shape_proto.dimensions_size(), shape_proto.is_dynamic_dimension_size()); + for (int i = 0; i < num_dynamic_dimension_fields; i++) { dynamic_dimensions_[i] = shape_proto.is_dynamic_dimension(i); } tuple_shapes_.reserve(shape_proto.tuple_shapes_size()); -- GitLab From 0b6177c2fa509d951d4086eab88fc506792cfe43 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Wed, 2 Jan 2019 16:08:42 -0800 Subject: [PATCH 270/622] Fix make_initializable_iterator comment PiperOrigin-RevId: 227598505 --- tensorflow/python/data/ops/dataset_ops.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tensorflow/python/data/ops/dataset_ops.py b/tensorflow/python/data/ops/dataset_ops.py index 1ba00a8e69..61210f3a7f 100644 --- a/tensorflow/python/data/ops/dataset_ops.py +++ b/tensorflow/python/data/ops/dataset_ops.py @@ -1756,8 +1756,8 @@ def make_initializable_iterator(dataset): RuntimeError: If eager execution is enabled. """ try: - # Call the defined `make_one_shot_iterator()` if there is one, because some - # datasets (e.g. for prefetching) override its behavior. + # Call the defined `make_initializable_iterator()` if there is one, because + # some datasets (e.g. for prefetching) override its behavior. return dataset.make_initializable_iterator() except AttributeError: return DatasetV1Adapter(dataset).make_initializable_iterator() -- GitLab From dd3adf935a800e25132b187d65aa62e97504f50c Mon Sep 17 00:00:00 2001 From: Kay Zhu Date: Wed, 2 Jan 2019 16:11:52 -0800 Subject: [PATCH 271/622] [XLA] Enable interpreter backend test by default for xla/tests without having to using additional backend tags "enable_for_xla_interpreter". PiperOrigin-RevId: 227598976 --- tensorflow/compiler/xla/client/lib/BUILD | 6 - .../compiler/xla/service/hlo_evaluator.cc | 5 + .../compiler/xla/service/hlo_evaluator.h | 23 +++ .../xla/service/hlo_evaluator_typed_visitor.h | 32 ++-- .../compiler/xla/service/interpreter/BUILD | 3 + .../xla/service/interpreter/compiler.cc | 6 + tensorflow/compiler/xla/tests/BUILD | 144 ++++-------------- .../xla/tests/array_elementwise_ops_test.cc | 13 ++ .../compiler/xla/tests/bfloat16_test.cc | 8 +- tensorflow/compiler/xla/tests/client_test.cc | 5 +- .../xla/tests/gather_operation_test.cc | 4 +- .../xla/tests/local_client_execute_test.cc | 6 +- 12 files changed, 115 insertions(+), 140 deletions(-) diff --git a/tensorflow/compiler/xla/client/lib/BUILD b/tensorflow/compiler/xla/client/lib/BUILD index 966a581981..d863ebfea1 100644 --- a/tensorflow/compiler/xla/client/lib/BUILD +++ b/tensorflow/compiler/xla/client/lib/BUILD @@ -93,7 +93,6 @@ cc_library( xla_test( name = "constants_test", srcs = ["constants_test.cc"], - tags = ["enable_for_xla_interpreter"], deps = [ ":constants", "//tensorflow/compiler/xla:test", @@ -147,7 +146,6 @@ cc_library( xla_test( name = "math_test", srcs = ["math_test.cc"], - tags = ["enable_for_xla_interpreter"], deps = [ ":math", "//tensorflow/compiler/xla:literal_util", @@ -181,7 +179,6 @@ cc_library( xla_test( name = "matrix_test", srcs = ["matrix_test.cc"], - tags = ["enable_for_xla_interpreter"], deps = [ ":matrix", ":slicing", @@ -295,7 +292,6 @@ cc_library( xla_test( name = "slicing_test", srcs = ["slicing_test.cc"], - tags = ["enable_for_xla_interpreter"], deps = [ ":slicing", "//tensorflow/compiler/xla:literal_util", @@ -324,7 +320,6 @@ cc_library( xla_test( name = "sorting_test", srcs = ["sorting_test.cc"], - tags = ["enable_for_xla_interpreter"], deps = [ ":sorting", "//tensorflow/compiler/xla:test", @@ -354,7 +349,6 @@ xla_test( srcs = ["quantize_test.cc"], # TODO(b/122119490): re-enable TAP after fixing. tags = [ - "enable_for_xla_interpreter", "notap", ], deps = [ diff --git a/tensorflow/compiler/xla/service/hlo_evaluator.cc b/tensorflow/compiler/xla/service/hlo_evaluator.cc index 794b97a7e0..4f5484293c 100644 --- a/tensorflow/compiler/xla/service/hlo_evaluator.cc +++ b/tensorflow/compiler/xla/service/hlo_evaluator.cc @@ -138,6 +138,11 @@ StatusOr Compare(const Shape& shape, HloOpcode opcode, } // namespace +// Note that unsupported types by the typed visitor does not necessarily imply +// the non-typed HloEvaluator (parent evaluator) would not support them either +// in the type-agnostic handler. For e.g., HandleGetTupleElement in the parent +// type-agnostic evaluator will be able to accept Tuple primitive type, whereas +// HloEvaluatorTypedVisitor cannot. HloEvaluator::HloEvaluator(int64 max_loop_iterations) : max_loop_iterations_(max_loop_iterations) { typed_visitors_[PRED] = diff --git a/tensorflow/compiler/xla/service/hlo_evaluator.h b/tensorflow/compiler/xla/service/hlo_evaluator.h index 44d5e5c030..d8e3019576 100644 --- a/tensorflow/compiler/xla/service/hlo_evaluator.h +++ b/tensorflow/compiler/xla/service/hlo_evaluator.h @@ -211,6 +211,29 @@ class HloEvaluator : public DfsHloVisitorWithDefault { Status HandleReduce(HloInstruction* reduce) override; + // Unsupported HLOs, note some of them (such as BatchNorm*) are typically + // expanded in a semantic-preserving way into other HLOs by adding exanpsion + // HLO pass to the HLO optimization pass during compilation, which can then be + // handled by the evaluator. + Status HandleBatchNormGrad(HloInstruction* batch_norm_grad) override { + return Unimplemented("BatchNormGrad HLO is unsupported by the evaluator."); + }; + Status HandleBatchNormInference( + HloInstruction* batch_norm_inference) override { + return Unimplemented( + "BatchNormInference HLO is unsupported by the evaluator."); + }; + Status HandleBatchNormTraining(HloInstruction* batch_norm_training) override { + return Unimplemented( + "BatchNormTraining HLO is unsupported by the evaluator."); + }; + Status HandleInfeed(HloInstruction* infeed) override { + return Unimplemented("Infeed HLO is unsupported by the evaluator."); + }; + Status HandleOutfeed(HloInstruction* outfeed) override { + return Unimplemented("Outfeed HLO is unsupported by the evaluator."); + }; + // Returns the already-evaluated literal result for the instruction. // A Constant instruction is considered evaluated and its literal will be // returned directly without looking up the cache. diff --git a/tensorflow/compiler/xla/service/hlo_evaluator_typed_visitor.h b/tensorflow/compiler/xla/service/hlo_evaluator_typed_visitor.h index 6f3208be13..b1bbf49d4a 100644 --- a/tensorflow/compiler/xla/service/hlo_evaluator_typed_visitor.h +++ b/tensorflow/compiler/xla/service/hlo_evaluator_typed_visitor.h @@ -917,7 +917,11 @@ class HloEvaluatorTypedVisitor : public DfsHloVisitorWithDefault { Status HandleClamp(HloInstruction* clamp) { std::function clamp_op = [](ElementwiseT low, ElementwiseT value, ElementwiseT high) { - return std::fmin(high, std::fmax(value, low)); + if (std::isnan(low) || std::isnan(high)) { + return static_cast(NAN); + } + return static_cast( + std::fmin(high, std::fmax(value, low))); }; TF_ASSIGN_OR_RETURN( parent_->evaluated_[clamp], @@ -2664,12 +2668,13 @@ class HloEvaluatorTypedVisitor : public DfsHloVisitorWithDefault { return HandleReducePrecision(reduce_precision); } - template ::value || - std::is_same::value || - std::is_integral::value || - std::is_floating_point::value>::type* = nullptr> + template < + typename NativeT, + typename std::enable_if< + std::is_same::value || + std::is_same::value || + std::is_integral::value || is_complex_t::value || + std::is_floating_point::value>::type* = nullptr> Status HandleIota(HloInstruction* instruction) { auto* iota = Cast(instruction); const int64 iota_size = iota->shape().dimensions(iota->iota_dimension()); @@ -2700,12 +2705,13 @@ class HloEvaluatorTypedVisitor : public DfsHloVisitorWithDefault { return Status::OK(); } - template ::value || - std::is_same::value || - std::is_integral::value || - std::is_floating_point::value)>::type* = nullptr> + template < + typename NativeT, + typename std::enable_if< + !(std::is_same::value || + std::is_same::value || + std::is_integral::value || is_complex_t::value || + std::is_floating_point::value)>::type* = nullptr> Status HandleIota(HloInstruction* iota) { return UnsupportedTypeError(iota); } diff --git a/tensorflow/compiler/xla/service/interpreter/BUILD b/tensorflow/compiler/xla/service/interpreter/BUILD index a981d94a99..8999f38ccd 100644 --- a/tensorflow/compiler/xla/service/interpreter/BUILD +++ b/tensorflow/compiler/xla/service/interpreter/BUILD @@ -32,6 +32,7 @@ cc_library( "//tensorflow/compiler/xla:status_macros", "//tensorflow/compiler/xla:statusor", "//tensorflow/compiler/xla/service:algebraic_simplifier", + "//tensorflow/compiler/xla/service:batchnorm_expander", "//tensorflow/compiler/xla/service:compiler", "//tensorflow/compiler/xla/service:computation_placer", "//tensorflow/compiler/xla/service:executable", @@ -41,12 +42,14 @@ cc_library( "//tensorflow/compiler/xla/service:hlo_cost_analysis", "//tensorflow/compiler/xla/service:hlo_cse", "//tensorflow/compiler/xla/service:hlo_dce", + "//tensorflow/compiler/xla/service:hlo_get_dimension_size_rewriter", "//tensorflow/compiler/xla/service:hlo_module_config", "//tensorflow/compiler/xla/service:hlo_pass", "//tensorflow/compiler/xla/service:hlo_pass_pipeline", "//tensorflow/compiler/xla/service:hlo_subcomputation_unification", "//tensorflow/compiler/xla/service:layout_assignment", "//tensorflow/compiler/xla/service:map_inliner", + "//tensorflow/compiler/xla/service:reduce_precision_insertion", "//tensorflow/compiler/xla/service:reshape_mover", "//tensorflow/compiler/xla/service:while_loop_simplifier", "//tensorflow/core:lib", diff --git a/tensorflow/compiler/xla/service/interpreter/compiler.cc b/tensorflow/compiler/xla/service/interpreter/compiler.cc index d37ae94bf6..69f611a094 100644 --- a/tensorflow/compiler/xla/service/interpreter/compiler.cc +++ b/tensorflow/compiler/xla/service/interpreter/compiler.cc @@ -31,6 +31,7 @@ limitations under the License. #include "tensorflow/compiler/xla/service/interpreter/executable.h" #include "tensorflow/compiler/xla/service/layout_assignment.h" #include "tensorflow/compiler/xla/service/map_inliner.h" +#include "tensorflow/compiler/xla/service/reduce_precision_insertion.h" #include "tensorflow/compiler/xla/service/reshape_mover.h" #include "tensorflow/compiler/xla/service/while_loop_simplifier.h" #include "tensorflow/compiler/xla/status_macros.h" @@ -46,6 +47,11 @@ Status InterpreterCompiler::RunHloOptimization(HloModule* hlo_module) { pipeline.AddPass( hlo_module->mutable_entry_computation_layout(), LayoutAssignment::InstructionCanChangeLayout); + + ReducePrecisionInsertion::AddPasses( + &pipeline, hlo_module->config().debug_options(), + ReducePrecisionInsertion::PassTiming::BEFORE_OPTIMIZATION); + return pipeline.Run(hlo_module).status(); } diff --git a/tensorflow/compiler/xla/tests/BUILD b/tensorflow/compiler/xla/tests/BUILD index ee24d4d99c..578e716b59 100644 --- a/tensorflow/compiler/xla/tests/BUILD +++ b/tensorflow/compiler/xla/tests/BUILD @@ -276,9 +276,6 @@ cc_library( xla_test( name = "bad_rng_shape_validation_test", srcs = ["bad_rng_shape_validation_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:statusor", "//tensorflow/compiler/xla:test", @@ -344,9 +341,6 @@ xla_test( xla_test( name = "check_execution_arity_test", srcs = ["check_execution_arity_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:literal", "//tensorflow/compiler/xla:shape_util", @@ -367,9 +361,6 @@ xla_test( xla_test( name = "query_inferred_shape_test", srcs = ["query_inferred_shape_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:shape_util", "//tensorflow/compiler/xla:statusor", @@ -387,9 +378,6 @@ xla_test( xla_test( name = "while_test", srcs = ["while_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:literal", "//tensorflow/compiler/xla:shape_util", @@ -413,6 +401,10 @@ xla_test( xla_test( name = "xla_hlo_profile_test", srcs = ["xla_hlo_profile_test.cc"], + blacklisted_backends = [ + # Hlo profiles are not supported on the interpreter backend. + "interpreter", + ], deps = [ "//tensorflow/compiler/xla:array2d", "//tensorflow/compiler/xla:shape_util", @@ -436,9 +428,6 @@ xla_test( xla_test( name = "axpy_simple_test", srcs = ["axpy_simple_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla/client:local_client", "//tensorflow/compiler/xla/client:xla_builder", @@ -453,7 +442,6 @@ xla_test( xla_test( name = "map_test", srcs = ["map_test.cc"], - tags = ["enable_for_xla_interpreter"], deps = [ "//tensorflow/compiler/xla:array2d", "//tensorflow/compiler/xla:literal", @@ -506,9 +494,6 @@ xla_test( xla_test( name = "pred_test", srcs = ["pred_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:array2d", "//tensorflow/compiler/xla/client:local_client", @@ -524,9 +509,6 @@ xla_test( xla_test( name = "select_test", srcs = ["select_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:xla_data_proto", "//tensorflow/compiler/xla/client:global_data", @@ -544,7 +526,6 @@ xla_test( xla_test( name = "conditional_test", srcs = ["conditional_test.cc"], - tags = ["enable_for_xla_interpreter"], deps = [ "//tensorflow/compiler/xla:xla_data_proto", "//tensorflow/compiler/xla/client:global_data", @@ -562,7 +543,6 @@ xla_test( xla_test( name = "unary_op_test", srcs = ["unary_op_test.cc"], - tags = ["enable_for_xla_interpreter"], deps = [ "//tensorflow/compiler/xla:xla_data_proto", "//tensorflow/compiler/xla/client:global_data", @@ -623,9 +603,6 @@ xla_test( xla_test( name = "deconstruct_tuple_test", srcs = ["deconstruct_tuple_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:literal", "//tensorflow/compiler/xla:shape_util", @@ -648,7 +625,6 @@ xla_test( name = "array_elementwise_ops_test", srcs = ["array_elementwise_ops_test.cc"], shard_count = 25, - tags = ["enable_for_xla_interpreter"], deps = [ "//tensorflow/compiler/xla:array2d", "//tensorflow/compiler/xla:array3d", @@ -698,7 +674,6 @@ xla_test( xla_test( name = "reduce_precision_test", srcs = ["reduce_precision_test.cc"], - tags = ["enable_for_xla_interpreter"], deps = [ "//tensorflow/compiler/xla:array2d", "//tensorflow/compiler/xla:literal", @@ -725,7 +700,6 @@ xla_test( srcs = ["dot_operation_test.cc"], shard_count = 20, tags = [ - "enable_for_xla_interpreter", "optonly", ], deps = [ @@ -806,9 +780,6 @@ xla_test( xla_test( name = "transpose_test", srcs = ["transpose_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:array2d", "//tensorflow/compiler/xla:reference_util", @@ -828,9 +799,6 @@ xla_test( xla_test( name = "constants_test", srcs = ["constants_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:array2d", "//tensorflow/compiler/xla:array3d", @@ -951,6 +919,11 @@ xla_test( xla_test( name = "batch_normalization_test", srcs = ["batch_normalization_test.cc"], + blacklisted_backends = [ + # BatchNorm HLOs are not handled by the interpreter backend, and the + # BatchNorm expander is not run on the interpreter. + "interpreter", + ], shard_count = 40, deps = [ ":test_utils", @@ -1042,9 +1015,6 @@ xla_test( name = "slice_test", srcs = ["slice_test.cc"], shard_count = 40, - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:array2d", "//tensorflow/compiler/xla:reference_util", @@ -1065,9 +1035,6 @@ xla_test( xla_test( name = "multidimensional_slice_test", srcs = ["multidimensional_slice_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:array2d", "//tensorflow/compiler/xla:array3d", @@ -1085,9 +1052,6 @@ xla_test( name = "dynamic_ops_test", timeout = "moderate", srcs = ["dynamic_ops_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:array2d", "//tensorflow/compiler/xla:reference_util", @@ -1113,9 +1077,6 @@ xla_test( xla_test( name = "tuple_test", srcs = ["tuple_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:array2d", "//tensorflow/compiler/xla:literal", @@ -1139,9 +1100,6 @@ xla_test( xla_test( name = "vector_ops_reduce_test", srcs = ["vector_ops_reduce_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:array2d", "//tensorflow/compiler/xla:array3d", @@ -1162,7 +1120,6 @@ xla_test( srcs = ["reduce_test.cc"], shard_count = 40, tags = [ - "enable_for_xla_interpreter", "optonly", ], deps = [ @@ -1229,7 +1186,6 @@ xla_test( srcs = [], shard_count = 20, tags = [ - "enable_for_xla_interpreter", "optonly", ], xla_test_library_deps = [":reduce_window_test_library"], @@ -1241,7 +1197,6 @@ xla_test( timeout = "long", srcs = ["select_and_scatter_test.cc"], tags = [ - "enable_for_xla_interpreter", "optonly", ], deps = [ @@ -1267,9 +1222,6 @@ xla_test( xla_test( name = "copy_test", srcs = ["copy_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ ":client_library_test_base", "//tensorflow/compiler/xla:array2d", @@ -1290,9 +1242,6 @@ xla_test( xla_test( name = "reduce_hlo_test", srcs = ["reduce_hlo_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla/service:hlo_parser", "//tensorflow/compiler/xla/tests:hlo_test_base", @@ -1306,9 +1255,6 @@ xla_test( xla_test( name = "token_hlo_test", srcs = ["token_hlo_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla/service:hlo_parser", "//tensorflow/compiler/xla/service:hlo_verifier", @@ -1323,9 +1269,6 @@ xla_test( xla_test( name = "call_test", srcs = ["call_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:literal", "//tensorflow/compiler/xla:literal_util", @@ -1368,9 +1311,6 @@ xla_test( xla_test( name = "binop_scaling_test", srcs = ["binop_scaling_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:array2d", "//tensorflow/compiler/xla:array4d", @@ -1388,9 +1328,6 @@ xla_test( xla_test( name = "broadcast_simple_test", srcs = ["broadcast_simple_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:array2d", "//tensorflow/compiler/xla:array4d", @@ -1410,9 +1347,6 @@ xla_test( xla_test( name = "pad_test", srcs = ["pad_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:array2d", "//tensorflow/compiler/xla:array4d", @@ -1434,9 +1368,6 @@ xla_test( xla_test( name = "fmax_test", srcs = ["fmax_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla/client:local_client", "//tensorflow/compiler/xla/client:xla_builder", @@ -1451,9 +1382,6 @@ xla_test( xla_test( name = "log_test", srcs = ["log_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla/client:local_client", "//tensorflow/compiler/xla/client:xla_builder", @@ -1468,9 +1396,6 @@ xla_test( xla_test( name = "matrix_ops_simple_test", srcs = ["matrix_ops_simple_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:array2d", "//tensorflow/compiler/xla:literal", @@ -1497,6 +1422,10 @@ xla_test( xla_test( name = "prng_test", srcs = ["prng_test.cc"], + blacklisted_backends = [ + # TODO(b/122047800) support RNGs on the interpreter backend. + "interpreter", + ], deps = [ "//tensorflow/compiler/xla:literal", "//tensorflow/compiler/xla:shape_util", @@ -1517,9 +1446,6 @@ xla_test( name = "reshape_test", srcs = ["reshape_test.cc"], shard_count = 30, - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:array2d", "//tensorflow/compiler/xla:array4d", @@ -1545,9 +1471,6 @@ xla_test( xla_test( name = "reverse_test", srcs = ["reverse_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:array2d", "//tensorflow/compiler/xla:array4d", @@ -1566,9 +1489,6 @@ xla_test( xla_test( name = "vector_ops_simple_test", srcs = ["vector_ops_simple_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:array4d", "//tensorflow/compiler/xla:shape_util", @@ -1592,9 +1512,6 @@ xla_test( xla_test( name = "concat_test", srcs = ["concat_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:array2d", "//tensorflow/compiler/xla:array3d", @@ -1615,9 +1532,6 @@ xla_test( xla_test( name = "convert_test", srcs = ["convert_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:shape_util", "//tensorflow/compiler/xla:xla_data_proto", @@ -1637,6 +1551,10 @@ xla_test( xla_test( name = "all_reduce_test", srcs = ["all_reduce_test.cc"], + blacklisted_backends = [ + # All reduce is not supported on the interpreter backend. + "interpreter", + ], deps = [ "//tensorflow/compiler/xla:literal", "//tensorflow/compiler/xla:shape_util", @@ -1661,9 +1579,6 @@ xla_test( xla_test( name = "bitcast_convert_test", srcs = ["bitcast_convert_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:shape_util", "//tensorflow/compiler/xla:xla_data_proto", @@ -1703,9 +1618,6 @@ xla_test( xla_test( name = "floor_ceil_test", srcs = ["floor_ceil_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla/client:local_client", "//tensorflow/compiler/xla/client:xla_builder", @@ -1767,6 +1679,10 @@ xla_test( xla_test( name = "execution_profile_test", srcs = ["execution_profile_test.cc"], + blacklisted_backends = [ + # Execution profiles are not supported on the interpreter backend. + "interpreter", + ], deps = [ ":client_library_test_base", "//tensorflow/compiler/xla/client:global_data", @@ -1781,6 +1697,10 @@ xla_test( name = "execution_profile_test_with_xla_hlo_profile", srcs = ["execution_profile_test.cc"], args = ["--xla_hlo_profile"], + blacklisted_backends = [ + # Hlo profiles are not supported on the interpreter backend. + "interpreter", + ], deps = [ ":client_library_test_base", "//tensorflow/compiler/xla/client:global_data", @@ -1794,9 +1714,6 @@ xla_test( xla_test( name = "replay_test", srcs = ["replay_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:literal", "//tensorflow/compiler/xla:protobuf_util", @@ -1819,9 +1736,6 @@ xla_test( xla_test( name = "broadcast_test", srcs = ["broadcast_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:literal", "//tensorflow/compiler/xla:shape_util", @@ -1883,9 +1797,6 @@ xla_test( xla_test( name = "fusion_test", srcs = ["fusion_test.cc"], - tags = [ - "enable_for_xla_interpreter", - ], deps = [ "//tensorflow/compiler/xla:array2d", "//tensorflow/compiler/xla:literal", @@ -2003,6 +1914,10 @@ xla_test( xla_test( name = "outfeed_in_nested_computation_test", srcs = ["outfeed_in_nested_computation_test.cc"], + blacklisted_backends = [ + # Outfeed ops are not supported on the interpreter backend. + "interpreter", + ], deps = [ "//tensorflow/compiler/xla/tests:local_client_test_base", "//tensorflow/compiler/xla/tests:xla_internal_test_main", @@ -2179,7 +2094,6 @@ xla_test( srcs = ["iota_test.cc"], shard_count = 30, tags = [ - "enable_for_xla_interpreter", # Require optimized builds, iota_test_cpu is very slow in fastbuild. "optonly", ], diff --git a/tensorflow/compiler/xla/tests/array_elementwise_ops_test.cc b/tensorflow/compiler/xla/tests/array_elementwise_ops_test.cc index 915b456b52..e4cc8b4199 100644 --- a/tensorflow/compiler/xla/tests/array_elementwise_ops_test.cc +++ b/tensorflow/compiler/xla/tests/array_elementwise_ops_test.cc @@ -2047,6 +2047,19 @@ XLA_TEST_F(ArrayElementwiseOpTest, NonNanClampF32) { error_spec_); } +XLA_TEST_F(ArrayElementwiseOpTest, ClampF32) { + SetFastMathDisabled(true); + XlaBuilder builder(TestName()); + auto minimum = ConstantR1(&builder, {1.0f, -6.5f, 1.0f, 2.25f, NAN}); + auto argument = + ConstantR1(&builder, {2.0f, 10.0f, -5.0f, 1.0f, 10.0f}); + auto maximum = ConstantR1(&builder, {3.0f, 0.5f, 25.5f, NAN, 123.0f}); + Clamp(minimum, argument, maximum); + + ComputeAndCompareR1(&builder, {2.0f, 0.5f, 1.0f, NAN, NAN}, {}, + error_spec_); +} + XLA_TEST_F(ArrayElementwiseOpTest, ClampF32Scalar) { XlaBuilder builder(TestName()); auto minimum = ConstantR0(&builder, 0.0f); diff --git a/tensorflow/compiler/xla/tests/bfloat16_test.cc b/tensorflow/compiler/xla/tests/bfloat16_test.cc index e9728e636f..63e4811705 100644 --- a/tensorflow/compiler/xla/tests/bfloat16_test.cc +++ b/tensorflow/compiler/xla/tests/bfloat16_test.cc @@ -76,7 +76,9 @@ XLA_TEST_F(Bfloat16Test, NegateScalarF16) { error_spec_); } -XLA_TEST_F(Bfloat16Test, BatchNormTraining) { +// Disabled on interpreter since BatchNormExanper is not run by default on the +// intepreter backend. +XLA_TEST_F(Bfloat16Test, DISABLED_ON_INTERPRETER(BatchNormTraining)) { const int kFeatureIndex = 2; XlaBuilder builder(TestName()); @@ -110,7 +112,9 @@ XLA_TEST_F(Bfloat16Test, BatchNormTraining) { ComputeAndCompareTuple(&builder, expected, {}, ErrorSpec(0.01, 0.02)); } -XLA_TEST_F(Bfloat16Test, BatchNormGrad) { +// Disabled on interpreter since BatchNormExanper is not run by default on the +// intepreter backend. +XLA_TEST_F(Bfloat16Test, DISABLED_ON_INTERPRETER(BatchNormGrad)) { const int kFeatureIndex = 2; XlaBuilder builder(TestName()); diff --git a/tensorflow/compiler/xla/tests/client_test.cc b/tensorflow/compiler/xla/tests/client_test.cc index d5b3a4d14f..247328b730 100644 --- a/tensorflow/compiler/xla/tests/client_test.cc +++ b/tensorflow/compiler/xla/tests/client_test.cc @@ -109,7 +109,10 @@ XLA_TEST_F(ClientTest, ExecuteWithTupleLayout) { /*minor_to_major=*/{1, 0}))); } -XLA_TEST_F(ClientTest, DISABLED_ON_GPU(ExecuteParallel)) { +// Disabled for interpreter since ExecuteAsyncOnStream is not implemented on +// interpreter backend. +XLA_TEST_F(ClientTest, + DISABLED_ON_INTERPRETER(DISABLED_ON_GPU(ExecuteParallel))) { XlaComputation add_with_one_arg, mul_with_two_args, dot_with_one_arg; Shape shape = ShapeUtil::MakeShape(S32, {2, 2}); diff --git a/tensorflow/compiler/xla/tests/gather_operation_test.cc b/tensorflow/compiler/xla/tests/gather_operation_test.cc index daa89398a6..d65b67a535 100644 --- a/tensorflow/compiler/xla/tests/gather_operation_test.cc +++ b/tensorflow/compiler/xla/tests/gather_operation_test.cc @@ -600,7 +600,9 @@ ENTRY main { class GatherClientLibraryTest : public ClientLibraryTestBase {}; -XLA_TEST_F(GatherClientLibraryTest, DISABLED_ON_GPU(Basic)) { +// Disabled on interpreter since ExectuteAsyncOnStream is not supported. +XLA_TEST_F(GatherClientLibraryTest, + DISABLED_ON_INTERPRETER(DISABLED_ON_GPU(Basic))) { // We create this HLO, but using the XlaBuilder API. // // ENTRY main { diff --git a/tensorflow/compiler/xla/tests/local_client_execute_test.cc b/tensorflow/compiler/xla/tests/local_client_execute_test.cc index 6522c56325..96527886b7 100644 --- a/tensorflow/compiler/xla/tests/local_client_execute_test.cc +++ b/tensorflow/compiler/xla/tests/local_client_execute_test.cc @@ -842,7 +842,8 @@ XLA_TEST_F(LocalClientExecuteTest, ShapeBufferToLiteralConversion64bit) { LiteralUtil::CreateR0(123456789000LL)})); } -XLA_TEST_F(LocalClientExecuteTest, InfeedTest) { +// Disabled on interpreter backend since infeed HLO is unsupported. +XLA_TEST_F(LocalClientExecuteTest, DISABLED_ON_INTERPRETER(InfeedTest)) { XlaBuilder builder(TestName()); const Shape shape = ShapeUtil::MakeShape(F32, {3}); auto in = Infeed(&builder, shape); @@ -867,7 +868,8 @@ XLA_TEST_F(LocalClientExecuteTest, InfeedTest) { LiteralTestUtil::ExpectR1Equal({-4.0, 125.0, 45.0}, result); } -XLA_TEST_F(LocalClientExecuteTest, InfeedOutfeedTest) { +// Disabled on interpreter backend since infeed/outfeed HLOs are unsupported. +XLA_TEST_F(LocalClientExecuteTest, DISABLED_ON_INTERPRETER(InfeedOutfeedTest)) { XlaBuilder builder(TestName()); const Shape shape = ShapeUtil::MakeShape(F32, {3}); auto in = Infeed(&builder, shape); -- GitLab From 4435d81822bef10437b3ec7a73300db62a5a53ad Mon Sep 17 00:00:00 2001 From: Allen Lavoie Date: Wed, 2 Jan 2019 16:16:35 -0800 Subject: [PATCH 272/622] Throw a more helpful error message when trying to export a subclassed Model Previously it said to call _set_inputs. More directly people should just specify signatures=... PiperOrigin-RevId: 227599645 --- tensorflow/python/saved_model/save_test.py | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tensorflow/python/saved_model/save_test.py b/tensorflow/python/saved_model/save_test.py index 4573495998..e643ff3dbf 100644 --- a/tensorflow/python/saved_model/save_test.py +++ b/tensorflow/python/saved_model/save_test.py @@ -31,6 +31,7 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_spec from tensorflow.python.framework import test_util +from tensorflow.python.keras.engine import training from tensorflow.python.keras.layers import core from tensorflow.python.lib.io import file_io from tensorflow.python.ops import lookup_ops @@ -306,6 +307,30 @@ class SaveTest(test.TestCase): self.assertNotIn("T", complex_node.attr) self.assertNotIn("Tout", complex_node.attr) + def test_subclassed_no_signature(self): + + class Subclassed(training.Model): + + def call(self, inputs): + return inputs * 2. + + save_dir = os.path.join(self.get_temp_dir(), "saved_model") + model = Subclassed() + with self.assertRaisesRegexp( + ValueError, "no @tf.function-decorated methods"): + save.save(model, save_dir) + + traced_call = def_function.function( + model.call, + input_signature=(tensor_spec.TensorSpec( + (None, None), + dtype=dtypes.float32),)) + save.save(model, save_dir, traced_call) + self.assertAllClose({"output_0": [[8., 10.], [10., 12.]]}, + _import_and_infer( + save_dir, + {"inputs": [[4., 5.], [5., 6.]]})) + class AssetTests(test.TestCase): -- GitLab From da7b71f67147ff4795c5c0168d1f225ba2b4b522 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Wed, 2 Jan 2019 16:32:13 -0800 Subject: [PATCH 273/622] Automated rollback of commit 7d2be9eef5f4b74da5b5c6f473ea2e6ad57398dc PiperOrigin-RevId: 227601883 --- .../lite/delegates/nnapi/nnapi_delegate.cc | 161 ++- tensorflow/lite/nnapi/BUILD | 12 - tensorflow/lite/nnapi/NeuralNetworksShim.cc | 137 -- tensorflow/lite/nnapi/NeuralNetworksShim.h | 1205 +++++++++-------- tensorflow/lite/nnapi/nnapi_lib_test.cc | 90 -- tensorflow/lite/nnapi_delegate.cc | 135 +- 6 files changed, 809 insertions(+), 931 deletions(-) delete mode 100644 tensorflow/lite/nnapi/NeuralNetworksShim.cc delete mode 100644 tensorflow/lite/nnapi/nnapi_lib_test.cc diff --git a/tensorflow/lite/delegates/nnapi/nnapi_delegate.cc b/tensorflow/lite/delegates/nnapi/nnapi_delegate.cc index 9487d3331f..8af159e6fb 100644 --- a/tensorflow/lite/delegates/nnapi/nnapi_delegate.cc +++ b/tensorflow/lite/delegates/nnapi/nnapi_delegate.cc @@ -48,39 +48,57 @@ namespace { } while (0) namespace { +int32_t GetAndroidSdkVersion() { +#ifdef __ANDROID__ + const char* sdkProp = "ro.build.version.sdk"; + char sdkVersion[PROP_VALUE_MAX]; + int length = __system_property_get(sdkProp, sdkVersion); + if (length != 0) { + for (int i = 0; i < length; ++i) { + int digit = sdkVersion[i] - '0'; + if (digit < 0 || digit > 9) { + // Non-numeric SDK version, assume it's higher then expected; + return std::numeric_limits::max(); + } + } + return atoi(sdkVersion); + } +#endif // __ANDROID__ + return 0; +} + constexpr int32_t kMinSdkVersionForNNAPI = 27; constexpr int32_t kMinSdkVersionForNNAPI11 = 28; +static const int32_t kAndroidSdkVersion = GetAndroidSdkVersion(); + } // namespace // RAII NN API Model Destructor for use with std::unique_ptr struct NNFreeModel { void operator()(ANeuralNetworksModel* model) { - NnApiImplementation()->ANeuralNetworksModel_free(model); + ANeuralNetworksModel_free(model); } }; // RAII NN API Compilation Destructor for use with std::unique_ptr struct NNFreeCompilation { void operator()(ANeuralNetworksCompilation* model) { - NnApiImplementation()->ANeuralNetworksCompilation_free(model); + ANeuralNetworksCompilation_free(model); } }; // Manage NNAPI shared memory handle class NNMemory { public: + NNMemory(const char* name, size_t size) { #ifdef __ANDROID__ - NNMemory(const NnApi* nnapi, const char* name, size_t size) { - nnapi_ = nnapi; byte_size_ = size; - fd_ = nnapi_->ASharedMemory_create(name, size); + fd_ = ASharedMemory_create(name, size); data_ptr_ = reinterpret_cast( mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0)); - nnapi_->ANeuralNetworksMemory_createFromFd(size, PROT_READ | PROT_WRITE, - fd_, 0, &nn_memory_handle_); - } -#else - NNMemory(const NnApi* /*nnapi*/, const char* /*name*/, size_t /*size*/) {} + ANeuralNetworksMemory_createFromFd(size, PROT_READ | PROT_WRITE, fd_, 0, + &nn_memory_handle_); #endif + } ~NNMemory() { #ifdef __ANDROID__ @@ -88,7 +106,7 @@ class NNMemory { munmap(data_ptr_, byte_size_); } if (nn_memory_handle_) { - nnapi_->ANeuralNetworksMemory_free(nn_memory_handle_); + ANeuralNetworksMemory_free(nn_memory_handle_); } if (fd_ > 0) close(fd_); #endif @@ -99,7 +117,6 @@ class NNMemory { private: #ifdef __ANDROID__ - const NnApi* nnapi_; int fd_ = 0; size_t byte_size_ = 0; #endif @@ -149,10 +166,9 @@ class OperandMapping { // operands for both tensors and parameters, and TFLite separates the two. class NNAPIOpBuilder { public: - NNAPIOpBuilder(const NnApi* nnapi, TfLiteContext* context, - OperandMapping* tensor_mapping, ANeuralNetworksModel* nn_model) - : nnapi_(nnapi), - context_(context), + NNAPIOpBuilder(TfLiteContext* context, OperandMapping* tensor_mapping, + ANeuralNetworksModel* nn_model) + : context_(context), operand_mapping_(tensor_mapping), nn_model_(nn_model) {} @@ -208,8 +224,7 @@ class NNAPIOpBuilder { .dimensionCount = dimension_count, .dimensions = dims.data()}; RETURN_TFLITE_ERROR_IF_NN_ERROR( - context_, - nnapi_->ANeuralNetworksModel_addOperand(nn_model_, &operand_type)); + context_, ANeuralNetworksModel_addOperand(nn_model_, &operand_type)); int ann_operand = operand_mapping_->add_new_non_tensor_operand(); augmented_outputs_.push_back(ann_operand); return kTfLiteOk; @@ -226,8 +241,7 @@ class NNAPIOpBuilder { reinterpret_cast(tensor->dims->data), tensor->params.scale, tensor->params.zero_point}; RETURN_TFLITE_ERROR_IF_NN_ERROR( - context_, - nnapi_->ANeuralNetworksModel_addOperand(nn_model_, &operand_type)); + context_, ANeuralNetworksModel_addOperand(nn_model_, &operand_type)); augmented_outputs_.push_back(ann_index); *ann_tensor_index_out = ann_index; @@ -284,14 +298,13 @@ class NNAPIOpBuilder { nn_type, static_cast(tensor->dims->size), reinterpret_cast(tensor->dims->data), scale, zeroPoint}; RETURN_TFLITE_ERROR_IF_NN_ERROR( - context_, - nnapi_->ANeuralNetworksModel_addOperand(nn_model_, &operand_type)); + context_, ANeuralNetworksModel_addOperand(nn_model_, &operand_type)); if (tensor->allocation_type == kTfLiteMmapRo) { // TODO(b/80630405): Use NNAPIAllocation. RETURN_TFLITE_ERROR_IF_NN_ERROR( context_, - nnapi_->ANeuralNetworksModel_setOperandValue( + ANeuralNetworksModel_setOperandValue( nn_model_, ann_tensor_index, tensor->data.raw, tensor->bytes)); } @@ -304,7 +317,7 @@ class NNAPIOpBuilder { // Actually add a NN API operation RETURN_TFLITE_ERROR_IF_NN_ERROR( context_, - nnapi_->ANeuralNetworksModel_addOperation( + ANeuralNetworksModel_addOperation( nn_model_, type, static_cast(augmented_inputs_.size()), augmented_inputs_.data(), static_cast(augmented_outputs_.size()), @@ -319,12 +332,11 @@ class NNAPIOpBuilder { TfLiteStatus AddScalarOperand(T value, int32_t nn_type) { ANeuralNetworksOperandType operand_type{.type = nn_type}; RETURN_TFLITE_ERROR_IF_NN_ERROR( - context_, - nnapi_->ANeuralNetworksModel_addOperand(nn_model_, &operand_type)); + context_, ANeuralNetworksModel_addOperand(nn_model_, &operand_type)); int ann_operand = operand_mapping_->add_new_non_tensor_operand(); RETURN_TFLITE_ERROR_IF_NN_ERROR( - context_, nnapi_->ANeuralNetworksModel_setOperandValue( - nn_model_, ann_operand, &value, sizeof(T))); + context_, ANeuralNetworksModel_setOperandValue(nn_model_, ann_operand, + &value, sizeof(T))); augmented_inputs_.push_back(ann_operand); return kTfLiteOk; } @@ -335,19 +347,15 @@ class NNAPIOpBuilder { ANeuralNetworksOperandType operand_type{ .type = nn_type, .dimensionCount = 1, .dimensions = &num_values}; RETURN_TFLITE_ERROR_IF_NN_ERROR( - context_, - nnapi_->ANeuralNetworksModel_addOperand(nn_model_, &operand_type)); + context_, ANeuralNetworksModel_addOperand(nn_model_, &operand_type)); int ann_operand = operand_mapping_->add_new_non_tensor_operand(); RETURN_TFLITE_ERROR_IF_NN_ERROR( - context_, nnapi_->ANeuralNetworksModel_setOperandValue( + context_, ANeuralNetworksModel_setOperandValue( nn_model_, ann_operand, values, sizeof(T) * num_values)); augmented_inputs_.push_back(ann_operand); return kTfLiteOk; } - // Access to NNAPI. - const NnApi* const nnapi_; - // TfLiteContext for error handling. TfLiteContext* const context_; @@ -383,7 +391,7 @@ ANeuralNetworksOperationType BasicMappingFn( // The kernel that represents the node sub set of TF Lite being run on NN API. class NNAPIDelegateKernel { public: - NNAPIDelegateKernel() { nnapi_ = NnApiImplementation(); } + NNAPIDelegateKernel() = default; typedef ANeuralNetworksOperationType (*MappingFn)( const NNAPIOpMappingArgs& mapping_args); @@ -392,7 +400,7 @@ class NNAPIDelegateKernel { // when called. You can use this function to see if a node is supported // (i.e. that MappingFn is not nullptr). static MappingFn Map(TfLiteContext* context, int builtin_code, int version, - int android_sdk_version, TfLiteNode* node) { + TfLiteNode* node) { switch (builtin_code) { case kTfLiteBuiltinAdd: if (version == 1) { @@ -511,7 +519,7 @@ class NNAPIDelegateKernel { } break; case kTfLiteBuiltinSqueeze: - if (version == 1 && android_sdk_version >= kMinSdkVersionForNNAPI11) { + if (version == 1 && kAndroidSdkVersion >= kMinSdkVersionForNNAPI11) { return [](const NNAPIOpMappingArgs& mapping_args) -> ANeuralNetworksOperationType { auto builtin = reinterpret_cast( @@ -627,7 +635,7 @@ class NNAPIDelegateKernel { } break; case kTfLiteBuiltinSub: - if (version == 1 && android_sdk_version >= kMinSdkVersionForNNAPI11 && + if (version == 1 && kAndroidSdkVersion >= kMinSdkVersionForNNAPI11 && context->tensors[node->inputs->data[0]].type == kTfLiteFloat32) { // NNAPI only support float sub. return [](const NNAPIOpMappingArgs& mapping_args) @@ -640,7 +648,7 @@ class NNAPIDelegateKernel { } break; case kTfLiteBuiltinDiv: - if (version == 1 && android_sdk_version >= kMinSdkVersionForNNAPI11 && + if (version == 1 && kAndroidSdkVersion >= kMinSdkVersionForNNAPI11 && context->tensors[node->inputs->data[0]].type == kTfLiteFloat32) { // NNAPI only support float div. return [](const NNAPIOpMappingArgs& mapping_args) @@ -653,7 +661,7 @@ class NNAPIDelegateKernel { } break; case kTfLiteBuiltinPad: - if (version == 1 && android_sdk_version >= kMinSdkVersionForNNAPI11 && + if (version == 1 && kAndroidSdkVersion >= kMinSdkVersionForNNAPI11 && node->inputs->size == 2 && context->tensors[node->inputs->data[0]].type == kTfLiteFloat32) { // NNAPI does not support specifying the padding value. @@ -663,12 +671,12 @@ class NNAPIDelegateKernel { } break; case kTfLiteBuiltinSpaceToBatchNd: - if (version == 1 && android_sdk_version >= kMinSdkVersionForNNAPI11) { + if (version == 1 && kAndroidSdkVersion >= kMinSdkVersionForNNAPI11) { return BasicMappingFn; } break; case kTfLiteBuiltinStridedSlice: - if (version == 1 && android_sdk_version >= kMinSdkVersionForNNAPI11) { + if (version == 1 && kAndroidSdkVersion >= kMinSdkVersionForNNAPI11) { return [](const NNAPIOpMappingArgs& mapping_args) -> ANeuralNetworksOperationType { auto builtin = reinterpret_cast( @@ -686,7 +694,7 @@ class NNAPIDelegateKernel { // dimensions. // TODO(b/110888333): Support dynamically-sized tensors in delegates. if ((version == 1) && - (android_sdk_version >= kMinSdkVersionForNNAPI11) && + (kAndroidSdkVersion >= kMinSdkVersionForNNAPI11) && (node->inputs->size > 1) && (context->tensors[node->inputs->data[1]].allocation_type == kTfLiteMmapRo)) { @@ -784,7 +792,7 @@ class NNAPIDelegateKernel { break; case kTfLiteBuiltinMean: // NNAPI does not support generating a scalar as output for MEAN. - if (version == 1 && android_sdk_version >= kMinSdkVersionForNNAPI11 && + if (version == 1 && kAndroidSdkVersion >= kMinSdkVersionForNNAPI11 && context->tensors[node->inputs->data[0]].type == kTfLiteFloat32 && context->tensors[node->outputs->data[0]].dims->size > 0) { return [](const NNAPIOpMappingArgs& mapping_args) @@ -828,8 +836,8 @@ class NNAPIDelegateKernel { if (!nn_model_) { ANeuralNetworksModel* model; - RETURN_TFLITE_ERROR_IF_NN_ERROR( - context, nnapi_->ANeuralNetworksModel_create(&model)); + RETURN_TFLITE_ERROR_IF_NN_ERROR(context, + ANeuralNetworksModel_create(&model)); nn_model_.reset(model); TF_LITE_ENSURE_STATUS( @@ -839,10 +847,10 @@ class NNAPIDelegateKernel { if (!nn_compilation_) { ANeuralNetworksCompilation* compilation; RETURN_TFLITE_ERROR_IF_NN_ERROR( - context, nnapi_->ANeuralNetworksCompilation_create(nn_model_.get(), - &compilation)); + context, + ANeuralNetworksCompilation_create(nn_model_.get(), &compilation)); RETURN_TFLITE_ERROR_IF_NN_ERROR( - context, nnapi_->ANeuralNetworksCompilation_finish(compilation)); + context, ANeuralNetworksCompilation_finish(compilation)); nn_compilation_.reset(compilation); } return kTfLiteOk; @@ -851,8 +859,8 @@ class NNAPIDelegateKernel { TfLiteStatus Invoke(TfLiteContext* context, TfLiteNode* node) { ANeuralNetworksExecution* execution = nullptr; RETURN_TFLITE_ERROR_IF_NN_ERROR( - context, nnapi_->ANeuralNetworksExecution_create(nn_compilation_.get(), - &execution)); + context, + ANeuralNetworksExecution_create(nn_compilation_.get(), &execution)); // Set the input tensor buffers. Note: we access tflite tensors using // absolute indices but NN api indices inputs by relative indices. @@ -872,7 +880,7 @@ class NNAPIDelegateKernel { tensor->data.raw, tensor->bytes); RETURN_TFLITE_ERROR_IF_NN_ERROR( context, - nnapi_->ANeuralNetworksExecution_setInputFromMemory( + ANeuralNetworksExecution_setInputFromMemory( execution, relative_input_index, nullptr, nn_input_memory_->get_handle(), input_offset, tensor->bytes)); input_offset += tensor->bytes; @@ -887,7 +895,7 @@ class NNAPIDelegateKernel { TfLiteTensor* tensor = &context->tensors[output_index]; RETURN_TFLITE_ERROR_IF_NN_ERROR( context, - nnapi_->ANeuralNetworksExecution_setOutputFromMemory( + ANeuralNetworksExecution_setOutputFromMemory( execution, relative_output_index, nullptr, nn_output_memory_->get_handle(), output_offset, tensor->bytes)); output_offset += tensor->bytes; @@ -903,7 +911,7 @@ class NNAPIDelegateKernel { // reading and writing into the same buffer during a invocation. // TODO(110369471): using double shared buffer to minimize the copies. RETURN_TFLITE_ERROR_IF_NN_ERROR( - context, nnapi_->ANeuralNetworksExecution_setOutput( + context, ANeuralNetworksExecution_setOutput( execution, relative_output_index, nullptr, tensor->data.raw, tensor->bytes)); relative_output_index++; @@ -911,12 +919,10 @@ class NNAPIDelegateKernel { // Invoke ANN in blocking fashion. ANeuralNetworksEvent* event = nullptr; RETURN_TFLITE_ERROR_IF_NN_ERROR( - context, - nnapi_->ANeuralNetworksExecution_startCompute(execution, &event)); - RETURN_TFLITE_ERROR_IF_NN_ERROR(context, - nnapi_->ANeuralNetworksEvent_wait(event)); - nnapi_->ANeuralNetworksEvent_free(event); - nnapi_->ANeuralNetworksExecution_free(execution); + context, ANeuralNetworksExecution_startCompute(execution, &event)); + RETURN_TFLITE_ERROR_IF_NN_ERROR(context, ANeuralNetworksEvent_wait(event)); + ANeuralNetworksEvent_free(event); + ANeuralNetworksExecution_free(execution); // copy results from shared memory to the destination. output_offset = 0; @@ -931,8 +937,6 @@ class NNAPIDelegateKernel { } private: - // Access to NNApi. - const NnApi* nnapi_; // ANN API state. std::unique_ptr nn_model_; std::unique_ptr @@ -953,7 +957,7 @@ class NNAPIDelegateKernel { // The operand builder allows creating a single op. We create it at this // reduced power position rather than in the for loop to avoid reallocating // the vectors. - NNAPIOpBuilder builder(nnapi_, context, &operand_mapping_, nn_model_.get()); + NNAPIOpBuilder builder(context, &operand_mapping_, nn_model_.get()); // Add Tensors // allocate outside to avoid realloc for (auto node_index : nodes_) { @@ -976,10 +980,9 @@ class NNAPIDelegateKernel { } } // Get op type and operands - int nn_op_type = Map( - context, reg->builtin_code, reg->version, nnapi_->android_sdk_version, - node)({context, &builder, node, &model_state_outputs_, - &model_state_tfl_inputs_}); + int nn_op_type = Map(context, reg->builtin_code, reg->version, node)( + {context, &builder, node, &model_state_outputs_, + &model_state_tfl_inputs_}); // Map outputs to NN API tensor indices. for (auto output_index : TfLiteIntArrayView(node->outputs)) { TF_LITE_ENSURE_STATUS(builder.AddTensorOutput(output_index)); @@ -1025,27 +1028,25 @@ class NNAPIDelegateKernel { // Tell ANN to declare inputs/outputs RETURN_TFLITE_ERROR_IF_NN_ERROR( - context, nnapi_->ANeuralNetworksModel_identifyInputsAndOutputs( + context, ANeuralNetworksModel_identifyInputsAndOutputs( nn_model_.get(), inputs.size(), inputs.data(), outputs.size(), outputs.data())); // Set relaxed computation mode for fp32 if possible. - if (nnapi_->android_sdk_version >= kMinSdkVersionForNNAPI11) { + if (kAndroidSdkVersion >= kMinSdkVersionForNNAPI11) { RETURN_TFLITE_ERROR_IF_NN_ERROR( - context, - nnapi_->ANeuralNetworksModel_relaxComputationFloat32toFloat16( - nn_model_.get(), context->allow_fp32_relax_to_fp16)); + context, ANeuralNetworksModel_relaxComputationFloat32toFloat16( + nn_model_.get(), context->allow_fp32_relax_to_fp16)); } // Finalize the model RETURN_TFLITE_ERROR_IF_NN_ERROR( - context, nnapi_->ANeuralNetworksModel_finish(nn_model_.get())); + context, ANeuralNetworksModel_finish(nn_model_.get())); // Create shared memory pool for inputs and outputs. - nn_input_memory_.reset( - new NNMemory(nnapi_, "input_pool", total_input_byte_size)); + nn_input_memory_.reset(new NNMemory("input_pool", total_input_byte_size)); nn_output_memory_.reset( - new NNMemory(nnapi_, "output_pool", total_output_byte_size)); + new NNMemory("output_pool", total_output_byte_size)); return kTfLiteOk; } @@ -1061,9 +1062,7 @@ TfLiteDelegate* NnApiDelegate() { .Prepare = [](TfLiteContext* context, TfLiteDelegate* delegate) -> TfLiteStatus { // Do not check nodes_ if NN API is unavailable. - const NnApi* nnapi = NnApiImplementation(); - if (nnapi->android_sdk_version < kMinSdkVersionForNNAPI || - !nnapi->nnapi_exists) { + if (kAndroidSdkVersion < kMinSdkVersionForNNAPI || !NNAPIExists()) { return kTfLiteOk; } @@ -1076,7 +1075,6 @@ TfLiteDelegate* NnApiDelegate() { TfLiteIntArray* plan; TF_LITE_ENSURE_STATUS(context->GetExecutionPlan(context, &plan)); - int android_sdk_version = NnApiImplementation()->android_sdk_version; // Check for every node if it is supported // TODO(b/80625235): Fix this to do more careful checking of versioning. for (int node_index : TfLiteIntArrayView(plan)) { @@ -1085,8 +1083,7 @@ TfLiteDelegate* NnApiDelegate() { TF_LITE_ENSURE_STATUS(context->GetNodeAndRegistration( context, node_index, &node, ®istration)); if (NNAPIDelegateKernel::Map(context, registration->builtin_code, - registration->version, - android_sdk_version, node)) { + registration->version, node)) { supported_nodes.push_back(node_index); } } diff --git a/tensorflow/lite/nnapi/BUILD b/tensorflow/lite/nnapi/BUILD index 390c3730cb..467a2b7a7b 100644 --- a/tensorflow/lite/nnapi/BUILD +++ b/tensorflow/lite/nnapi/BUILD @@ -6,20 +6,8 @@ package(default_visibility = [ cc_library( name = "nnapi_lib", - srcs = [ - "NeuralNetworksShim.cc", - ], hdrs = [ "NeuralNetworksShim.h", ], linkopts = ["-ldl"], ) - -cc_test( - name = "nnapi_lib_test", - srcs = ["nnapi_lib_test.cc"], - deps = [ - "//tensorflow/lite/nnapi:nnapi_lib", - "@com_google_googletest//:gtest_main", - ], -) diff --git a/tensorflow/lite/nnapi/NeuralNetworksShim.cc b/tensorflow/lite/nnapi/NeuralNetworksShim.cc deleted file mode 100644 index c063525cae..0000000000 --- a/tensorflow/lite/nnapi/NeuralNetworksShim.cc +++ /dev/null @@ -1,137 +0,0 @@ -/* 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/lite/nnapi/NeuralNetworksShim.h" - -#include - -#ifdef __ANDROID__ -#include -#include -#include -#include -#endif - -#define NNAPI_LOG(format, ...) fprintf(stderr, format "\n", __VA_ARGS__); - -namespace { - -#ifdef __ANDROID__ -int32_t GetAndroidSdkVersion() { - const char* sdkProp = "ro.build.version.sdk"; - char sdkVersion[PROP_VALUE_MAX]; - int length = __system_property_get(sdkProp, sdkVersion); - if (length != 0) { - int32_t result = 0; - for (int i = 0; i < length; ++i) { - int digit = sdkVersion[i] - '0'; - if (digit < 0 || digit > 9) { - // Non-numeric SDK version, assume it's higher than expected; - return 0xffff; - } - result = result * 10 + digit; - } - return result; - } - return 0; -} - -void* LoadFunction(void* handle, const char* name) { - if (handle == nullptr) { - return nullptr; - } - void* fn = dlsym(handle, name); - if (fn == nullptr) { - NNAPI_LOG("nnapi error: unable to open function %s", name); - } - return fn; -} - -#define LOAD_FUNCTION(handle, name) \ - nnapi.name = reinterpret_cast(LoadFunction(handle, #name)); - -#else - -#define LOAD_FUNCTION(handle, name) nnapi.name = nullptr; - -#endif - -const NnApi LoadNnApi() { - NnApi nnapi = {}; - -#ifdef __ANDROID__ - void* libneuralnetworks = nullptr; - void* libandroid = nullptr; - nnapi.android_sdk_version = GetAndroidSdkVersion(); - if (nnapi.android_sdk_version < 27) { - NNAPI_LOG("nnapi error: requires android sdk version to be at least %d", - 27); - } else { - // TODO: change RTLD_LOCAL? Assumes there can be multiple instances of nn - // api RT - libneuralnetworks = dlopen("libneuralnetworks.so", RTLD_LAZY | RTLD_LOCAL); - if (libneuralnetworks == nullptr) { - NNAPI_LOG("nnapi error: unable to open library %s", - "libneuralnetworks.so"); - } - libandroid = dlopen("libandroid.so", RTLD_LAZY | RTLD_LOCAL); - if (libneuralnetworks == nullptr) { - NNAPI_LOG("nnapi error: unable to open library %s", "libandroid.so"); - } - } - nnapi.nnapi_exists = libneuralnetworks != nullptr; -#else - nnapi.nnapi_exists = false; - nnapi.android_sdk_version = 0; -#endif - - LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksMemory_createFromFd); - LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksMemory_free); - LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksModel_create); - LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksModel_free); - LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksModel_finish); - LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksModel_addOperand); - LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksModel_setOperandValue); - LOAD_FUNCTION(libneuralnetworks, - ANeuralNetworksModel_setOperandValueFromMemory); - LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksModel_addOperation); - LOAD_FUNCTION(libneuralnetworks, - ANeuralNetworksModel_identifyInputsAndOutputs); - LOAD_FUNCTION(libneuralnetworks, - ANeuralNetworksModel_relaxComputationFloat32toFloat16); - LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksCompilation_create); - LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksCompilation_free); - LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksCompilation_setPreference); - LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksCompilation_finish); - LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksExecution_create); - LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksExecution_free); - LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksExecution_setInput); - LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksExecution_setInputFromMemory); - LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksExecution_setOutput); - LOAD_FUNCTION(libneuralnetworks, - ANeuralNetworksExecution_setOutputFromMemory); - LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksExecution_startCompute); - LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksEvent_wait); - LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksEvent_free); - LOAD_FUNCTION(libandroid, ASharedMemory_create); - - return nnapi; -} - -} // namespace - -const NnApi* NnApiImplementation() { - static const NnApi nnapi = LoadNnApi(); - return &nnapi; -} diff --git a/tensorflow/lite/nnapi/NeuralNetworksShim.h b/tensorflow/lite/nnapi/NeuralNetworksShim.h index 3fd869e4af..c39502f4ac 100644 --- a/tensorflow/lite/nnapi/NeuralNetworksShim.h +++ b/tensorflow/lite/nnapi/NeuralNetworksShim.h @@ -15,10 +15,69 @@ limitations under the License. #ifndef TENSORFLOW_LITE_NNAPI_NEURALNETWORKSSHIM_H_ #define TENSORFLOW_LITE_NNAPI_NEURALNETWORKSSHIM_H_ +#include #include #include #include +// helpers + +#define NNAPI_LOG(format, ...) fprintf(stderr, format "\n", __VA_ARGS__); +#define LOAD_FUNCTION(name) \ + static name##_fn fn = reinterpret_cast(loadFunction(#name)); +#define EXECUTE_FUNCTION(...) \ + if (fn != nullptr) { \ + fn(__VA_ARGS__); \ + } +#define EXECUTE_FUNCTION_RETURN(...) return fn != nullptr ? fn(__VA_ARGS__) : 0; + +inline void* loadLibrary(const char* name) { + // TODO: change RTLD_LOCAL? Assumes there can be multiple instances of nn + // api RT + void* handle = nullptr; +#ifdef __ANDROID__ + handle = dlopen(name, RTLD_LAZY | RTLD_LOCAL); + if (handle == nullptr) { + NNAPI_LOG("nnapi error: unable to open library %s", name); + } +#endif + return handle; +} + +typedef int (*ASharedMemory_create_fn)(const char* name, size_t size); + +// ASharedMemory_create was added in Android 8.0, so safe to use with NNAPI +// which was added in 8.1. +inline int ASharedMemory_create(const char* name, size_t size) { + static void* handle = loadLibrary("libandroid.so"); + static ASharedMemory_create_fn fn = + handle != nullptr ? reinterpret_cast( + dlsym(handle, "ASharedMemory_create")) + : nullptr; + return fn(name, size); +} + +inline void* getLibraryHandle() { + static void* handle = loadLibrary("libneuralnetworks.so"); + return handle; +} + +inline void* loadFunction(const char* name) { + void* fn = nullptr; + if (getLibraryHandle() != nullptr) { + fn = dlsym(getLibraryHandle(), name); + } + if (fn == nullptr) { + NNAPI_LOG("nnapi error: unable to open function %s", name); + } + return fn; +} + +inline bool NNAPIExists() { + static bool nnapi_is_available = getLibraryHandle(); + return nnapi_is_available; +} + // NN api types based on NNAPI header file // https://developer.android.com/ndk/reference/group/neural-networks @@ -348,564 +407,606 @@ typedef int (*ANeuralNetworksEvent_wait_fn)(ANeuralNetworksEvent* event); typedef void (*ANeuralNetworksEvent_free_fn)(ANeuralNetworksEvent* event); -typedef int (*ASharedMemory_create_fn)(const char* name, size_t size); +/** + * Creates a shared memory object from a file descriptor. + * + * The shared memory is backed by a file descriptor via mmap. + * See {@link ANeuralNetworksMemory} for a description on how to use + * this shared memory. + * + * @param size The requested size in bytes. + * Must not be larger than the file size. + * @param prot The desired memory protection for the mapping. + * It is either PROT_NONE or the bitwise OR of one or + * more of the following flags: PROT_READ, PROT_WRITE. + * @param fd The requested file descriptor. + * The file descriptor has to be mmap-able. The file + * descriptor will be duplicated. + * @param offset The offset to the beginning of the file of the area to map. + * The offset has to be aligned to a page size. + * @param memory The memory object to be created. + * Set to NULL if unsuccessful. + * + * @return ANEURALNETWORKS_NO_ERROR if the request completed normally. + */ +inline int ANeuralNetworksMemory_createFromFd(size_t size, int protect, int fd, + size_t offset, + ANeuralNetworksMemory** memory) { + LOAD_FUNCTION(ANeuralNetworksMemory_createFromFd); + EXECUTE_FUNCTION_RETURN(size, protect, fd, offset, memory); +} -struct NnApi { - bool nnapi_exists; - int32_t android_sdk_version; - - /** - * Creates a shared memory object from a file descriptor. - * - * The shared memory is backed by a file descriptor via mmap. - * See {@link ANeuralNetworksMemory} for a description on how to use - * this shared memory. - * - * @param size The requested size in bytes. - * Must not be larger than the file size. - * @param prot The desired memory protection for the mapping. - * It is either PROT_NONE or the bitwise OR of one or - * more of the following flags: PROT_READ, PROT_WRITE. - * @param fd The requested file descriptor. - * The file descriptor has to be mmap-able. The file - * descriptor will be duplicated. - * @param offset The offset to the beginning of the file of the area to map. - * The offset has to be aligned to a page size. - * @param memory The memory object to be created. - * Set to NULL if unsuccessful. - * - * @return ANEURALNETWORKS_NO_ERROR if the request completed normally. - */ - int (*ANeuralNetworksMemory_createFromFd)(size_t size, int protect, int fd, - size_t offset, - ANeuralNetworksMemory** memory); - - /** - * Delete a memory object. - * - * Destroys the object used by the run time to keep track of the memory. - * This will free the underlying actual memory if no other code has open - * handles to this memory. - * - * @param memory The memory object to be freed. - */ - void (*ANeuralNetworksMemory_free)(ANeuralNetworksMemory* memory); - - /** - * Create an empty {@link ANeuralNetworksModel}. - * - *

    This only creates the object. Computation is performed once - * {@link ANeuralNetworksExecution_startCompute} is invoked. - * - * The model should be constructed with calls to - * {@link ANeuralNetworksModel_addOperation} and - * {@link ANeuralNetworksModel_addOperand} - * - *

    {@link ANeuralNetworksModel_finish} should be called once the model - * has been fully constructed.

    - * - *

    {@link ANeuralNetworksModel_free} should be called once the model - * is no longer needed.

    - * - * @param model The {@link ANeuralNetworksModel} to be created. - * Set to NULL if unsuccessful. - * - * @return ANEURALNETWORKS_NO_ERROR if successful. - */ - int (*ANeuralNetworksModel_create)(ANeuralNetworksModel** model); - - /** - * Destroy a model. - * - * The model need not have been finished by a call to - * {@link ANeuralNetworksModel_finish}. - * - * See {@link ANeuralNetworksModel} for information on multithreaded usage. - * - * @param model The model to be destroyed. Passing NULL is acceptable and - * results in no operation. - */ - void (*ANeuralNetworksModel_free)(ANeuralNetworksModel* model); - - /** - * Indicate that we have finished modifying a model. Required before - * calling {@link ANeuralNetworksCompilation_compile}. - * - * An application is responsible to make sure that no other thread uses - * the model at the same time. - * - * See {@link ANeuralNetworksModel} for information on multithreaded usage. - * - * @param model The model to be finished. - * - * @return ANEURALNETWORKS_NO_ERROR if successful. - */ - int (*ANeuralNetworksModel_finish)(ANeuralNetworksModel* model); - - /** - * Add an operand to a model. - * - * The order in which the operands are added is important. The first one added - * to a model will have the index value 0, the second 1, etc. These indexes - * are used as operand identifiers in - * {@link ANeuralNetworksModel_addOperation}, - * {@link ANeuralNetworksExecution_setInput}, - * {@link ANeuralNetworksExecution_setInputFromMemory}, - * {@link ANeuralNetworksExecution_setOutput}, - * {@link ANeuralNetworksExecution_setOutputFromMemory} and - * {@link ANeuralNetworksExecution_setOperandValue}. - * - * To build a model that can accommodate inputs of various sizes, as you may - * want to do for a CNN, set the size of the dimensions that will vary at run - * time to 0. If you do so, provide the full dimensions when calling - * {@link ANeuralNetworksExecution_setInput} or {@link - * ANeuralNetworksExecution_setInputFromMemory}. - * - * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has - * been called will return an error. - * - * See {@link ANeuralNetworksModel} for information on multithreaded usage. - * - * @param model The model to be modified. - * @param type The {@link ANeuralNetworksOperandType} that describes the shape - * of the operand. - * - * @return ANEURALNETWORKS_NO_ERROR if successful. - */ - int (*ANeuralNetworksModel_addOperand)( - ANeuralNetworksModel* model, const ANeuralNetworksOperandType* type); - - /** - * Sets an operand to a constant value. - * - * For scalar values, the content of buffer is copied into the model. - * - * For tensor values, a pointer to the buffer is stored within the model. - * The application is responsible for not changing the content of this region - * until all executions using this model have completed. As the data may - * be copied during processing, modifying the data after this call yields - * undefined results. - * - * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has - * been called will return an error. - * - * See {@link ANeuralNetworksModel} for information on multithreaded usage. - * - * @param model The model to be modified. - * @param index The index of the model operand we're setting. - * @param buffer A pointer to the data to use. - * @param length The size in bytes of the data value. - * - * @return ANEURALNETWORKS_NO_ERROR if successful. - */ - int (*ANeuralNetworksModel_setOperandValue)(ANeuralNetworksModel* model, - int32_t index, const void* buffer, - size_t length); - - /** - * Sets an operand to a value stored in a memory object. - * - * The content of the memory is not copied. A reference to that memory is - * stored inside the model. The application is responsible for not changing - * the content of the memory region until all executions using this model have - * completed. - * As the data may be copied during processing, modifying the data after this - * call yields undefined results. - * - * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has - * been called will return an error. - * - * See {@link ANeuralNetworksModel} for information on multithreaded usage. - * - * @param model The model to be modified. - * @param index The index of the model operand we're setting. - * @param buffer A pointer to the data to use. - * @param memory The memory containing the data. - * @param offset This specifies the location of the data within the memory. - * The offset is in bytes from the start of memory. - * @param length The size in bytes of the data value. - * - * @return ANEURALNETWORKS_NO_ERROR if successful. - */ - int (*ANeuralNetworksModel_setOperandValueFromMemory)( - ANeuralNetworksModel* model, int32_t index, - const ANeuralNetworksMemory* memory, size_t offset, size_t length); - - /** - * Add an operation to a model. - * - * @param model The model to be modified. - * @param type The type of the operation. - * @param inputCount The number of entries in the inputs array. - * @param inputs An array of indexes identifying each operand. - * @param outputCount The number of entries in the outputs array. - * @param outputs An array of indexes identifying each operand. - * - * The operands specified by inputs and outputs must have been - * previously added by calls to {@link ANeuralNetworksModel_addOperand}. - * - * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has - * been called will return an error. - * - * See {@link ANeuralNetworksModel} for information on multithreaded usage. - * - * @return ANEURALNETWORKS_NO_ERROR if successful. - */ - int (*ANeuralNetworksModel_addOperation)(ANeuralNetworksModel* model, - ANeuralNetworksOperationType type, - uint32_t inputCount, - const uint32_t* inputs, - uint32_t outputCount, - const uint32_t* outputs); - - /** - * Specifies which operands will be the model's inputs and outputs. - * - * An operand cannot be used for both input and output. Doing so will - * return an error. - * - * @param model The model to be modified. - * @param inputCount The number of entries in the inputs array. - * @param inputs An array of indexes identifying the input operands. - * @param outputCount The number of entries in the outputs array. - * @param outputs An array of indexes identifying the output operands. - * - * The operands specified by inputs and outputs must have been - * previously added by calls to {@link ANeuralNetworksModel_addOperand}. - * - * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has - * been called will return an error. - * - * See {@link ANeuralNetworksModel} for information on multithreaded usage. - * - */ - int (*ANeuralNetworksModel_identifyInputsAndOutputs)( - ANeuralNetworksModel* model, uint32_t inputCount, const uint32_t* inputs, - uint32_t outputCount, const uint32_t* outputs); - - /** - * Specifies whether {@link ANEURALNETWORKS_TENSOR_FLOAT32} is allowed to be - * calculated with range and/or precision as low as that of the - * IEEE 754 16-bit floating-point format. By default, - * {@link ANEURALNETWORKS_TENSOR_FLOAT32} must be calculated using at least - * the range and precision of the IEEE 754 32-bit floating-point format. - * - * @param model The model to be modified. - * @param allow 'true' indicates {@link ANEURALNETWORKS_TENSOR_FLOAT32} may be - * calculated with range and/or precision as low as that of the - * IEEE 754 16-bit floating point format. 'false' indicates - * {@link ANEURALNETWORKS_TENSOR_FLOAT32} must be calculated - * using at least the range and precision of the IEEE 754 32-bit - * floating point format. - * - * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has - * been called will return an error. - * - * Available since API level 28. - * - * See {@link ANeuralNetworksModel} for information on multithreaded usage. - */ - int (*ANeuralNetworksModel_relaxComputationFloat32toFloat16)( - ANeuralNetworksModel* model, bool allow); - - /** - * Create a {@link ANeuralNetworksCompilation} to compile the given model. - * This only creates the object. Compilation is only performed once - * {@link ANeuralNetworksCompilation_start} is invoked. - * - *

    The provided model must outlive the compilation.

    - * - * The model must already have been finished by a call to - * {@link ANeuralNetworksModel_finish}. - * - * See {@link ANeuralNetworksCompilation} for information on multithreaded - * usage. - * - * @param model The {@link ANeuralNetworksModel} to be compiled. - * @param compilation The newly created object or NULL if unsuccessful. - * - * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA - * if the model is invalid. - */ - int (*ANeuralNetworksCompilation_create)( - ANeuralNetworksModel* model, ANeuralNetworksCompilation** compilation); - - /** - * Destroy a compilation. - * - *

    If called on a compilation for which - * {@link ANeuralNetworksCompilation_start} has been called, the - * function will return immediately but will mark the compilation to be - * deleted once the compilation completes. The - * {@link ANeuralNetworksCompilation_wait} will return ERROR_DELETED. - * - * See {@link ANeuralNetworksCompilation} for information on multithreaded - * usage. - * - * @param compilation The compilation to be destroyed. Passing NULL is - * acceptable and results in no operation. - */ - void (*ANeuralNetworksCompilation_free)( - ANeuralNetworksCompilation* compilation); - - /** - * Sets the execution preference. - * - *

    Provides guidance to the runtime when trade-offs are possible.

    - * - * See {@link ANeuralNetworksCompilation} for information on multithreaded - * usage. - * - * @param compilation The compilation to be modified. - * @param preference Either {@link PREFER_LOW_POWER}, - * {@link PREFER_SINGLE_FAST_ANSWER}, or - * {@link PREFER_SUSTAINED_SPEED}. - * - * @return ANEURALNETWORKS_NO_ERROR if successful. - */ - int (*ANeuralNetworksCompilation_setPreference)( - ANeuralNetworksCompilation* compilation, int32_t preference); - - /** - * Waits until the compilation completes. - * - * More than one thread can wait on a compilation. When the compilation - * completes, all threads will be released. - * - * See {@link ANeuralNetworksCompilation} for information on multithreaded - * usage. - * - * @return ANEURALNETWORKS_NO_ERROR if the compilation completed normally. - */ - int (*ANeuralNetworksCompilation_finish)( - ANeuralNetworksCompilation* compilation); - - /** - * Create a {@link ANeuralNetworksExecution} to apply the given compilation. - * This only creates the object. Computation is only performed once - * {@link ANeuralNetworksExecution_startCompute} is invoked. - * - *

    The provided compilation must outlive the execution.

    - * - * See {@link ANeuralNetworksExecution} for information on multithreaded - * usage. - * - * @param compilation The {@link ANeuralNetworksCompilation} to be evaluated. - * @param execution The newly created object or NULL if unsuccessful. - * - * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA - * if the compilation is invalid. - */ - int (*ANeuralNetworksExecution_create)( - ANeuralNetworksCompilation* compilation, - ANeuralNetworksExecution** execution); - - /** - * Destroy an execution. - * - *

    If called on an execution for which - * {@link ANeuralNetworksExecution_startCompute} has been called, the - * function will return immediately but will mark the execution to be deleted - * once the computation completes. The {link ANeuralNetworksExecution_wait} - * will return ANEURALNETWORKS_ERROR_DELETED. - * - * See {@link ANeuralNetworksExecution} for information on multithreaded - * usage. - * - * @param execution The execution to be destroyed. Passing NULL is acceptable - * and results in no operation. - */ - void (*ANeuralNetworksExecution_free)(ANeuralNetworksExecution* execution); - - /** - * Associate a user buffer with an input of the model of the - * {@link ANeuralNetworksExecution}. - * - *

    The provided buffer must outlive the execution.

    - * - * See {@link ANeuralNetworksExecution} for information on multithreaded - * usage. - * - * @param execution The execution to be modified. - * @param index The index of the input argument we are setting. It is - * an index into the lists passed to - * {@link ANeuralNetworksModel_identifyInputsAndOutputs}. It is - * not the index associated with {@link - * ANeuralNetworksModel_addOperand}. - * @param type The type of the operand. This should be used to specify the - * dimensions that were set to 0 when the operand was added to the - * model. All other properties of the type must be the same as - * specified in the model. If the type is the same as specified - * when the model was built, NULL can be passed. - * @param buffer The buffer containing the data. - * @param length The length in bytes of the buffer. - * - * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA if - * the name is not recognized or the buffer is too small for the input. - */ - int (*ANeuralNetworksExecution_setInput)( - ANeuralNetworksExecution* execution, int32_t index, - const ANeuralNetworksOperandType* type, const void* buffer, - size_t length); - - /** - * Associate part of a memory object with an input of the model of the - * {@link ANeuralNetworksExecution}. - * - *

    The provided memory must outlive the execution.

    - * - * See {@link ANeuralNetworksExecution} for information on multithreaded - * usage. - * - * @param execution The execution to be modified. - * @param index The index of the input argument we are setting. It is - * an index into the lists passed to - * {@link ANeuralNetworksModel_identifyInputsAndOutputs}. It is - * not the index associated with {@link - * ANeuralNetworksModel_addOperand}. - * @param type The type of the operand. This can be used to specify the - * dimensions that were set to 0 when the operand was added to the - * model. All other values must be the same as specified in the - * model. If the type is the same as specified when the model - * was built, NULL can be passed. - * @param memory The memory containing the data. - * @param offset This specifies the location of the data within the memory. - * The offset is in bytes from the start of memory. - * @param length The size in bytes of the data value. - * - * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA if - * the name is not recognized or the buffer is too small for the input. - */ - int (*ANeuralNetworksExecution_setInputFromMemory)( - ANeuralNetworksExecution* execution, int32_t index, - const ANeuralNetworksOperandType* type, - const ANeuralNetworksMemory* memory, size_t offset, size_t length); - - /** - * Associate a user buffer with an output of the model of the - * {@link ANeuralNetworksExecution}. - * - *

    The provided buffer must outlive the execution.

    - * - * See {@link ANeuralNetworksExecution} for information on multithreaded - * usage. - * - * @param execution The execution to be modified. - * @param index The index of the output argument we are setting. It is - * an index into the lists passed to - * {@link ANeuralNetworksModel_identifyInputsAndOutputs}. It is - * not the index associated with {@link - * ANeuralNetworksModel_addOperand}. - * @param type The type of the operand. This can be used to specify the - * dimensions that were set to 0 when the operand was added to the - * model. All other values must be the same as specified in the - * model. If the type is the same as specified when the model - * was built, NULL can be passed. - * @param buffer The buffer where the data is to be written. - * @param length The length in bytes of the buffer. - * - * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA if - * the name is not recognized or the buffer is too small for the output. - */ - int (*ANeuralNetworksExecution_setOutput)( - ANeuralNetworksExecution* execution, int32_t index, - const ANeuralNetworksOperandType* type, void* buffer, size_t length); - - /** - * Associate part of a memory object with an output of the model of the - * {@link ANeuralNetworksExecution}. - * - *

    The provided memory must outlive the execution.

    - * - * See {@link ANeuralNetworksExecution} for information on multithreaded - * usage. - * - * @param execution The execution to be modified. - * @param index The index of the output argument we are setting. It is - * an index into the lists passed to - * {@link ANeuralNetworksModel_identifyInputsAndOutputs}. It is - * not the index associated with {@link - * ANeuralNetworksModel_addOperand}. - * @param type The type of the operand. This can be used to specify the - * dimensions that were set to 0 when the operand was added to the - * model. All other values must be the same as specified in the - * model. If the type is the same as specified when the model - * was built, NULL can be passed. - * @param memory The memory where the data is to be stored. - * @param offset This specifies the location of the data within the memory. - * The offset is in bytes from the start of memory. - * @param length The length in bytes of the data value. - * - * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA if - * the name is not recognized or the buffer is too small for the output. - */ - int (*ANeuralNetworksExecution_setOutputFromMemory)( - ANeuralNetworksExecution* execution, int32_t index, - const ANeuralNetworksOperandType* type, - const ANeuralNetworksMemory* memory, size_t offset, size_t length); - - /** - * Schedule evaluation of the execution. - * - *

    Schedules evaluation of the execution. Once the model has been - * applied and the outputs are ready to be consumed, the execution will be - * signaled. Use {@link ANeuralNetworksExecution_wait} to wait for that - * signal. - *

    - * - * Multiple executions can be scheduled and evaluated concurrently, and - * compilations can be performed concurrently with executions. The runtime - * makes no guarantee on the ordering of the completion of compilations and - * executions. If it's important to the application, the application should - * enforce the ordering by using {@link ANeuralNetworksCompilation_wait} and - * {@link ANeuralNetworksExecution_wait}. - * - * ANeuralNetworksExecution_wait must be called to recuperate the resources - * used by the execution. - * - * See {@link ANeuralNetworksExecution} for information on multithreaded - * usage. - * - * @param execution The execution to be scheduled and executed. - * - * @return ANEURALNETWORKS_NO_ERROR if successful. - */ - int (*ANeuralNetworksExecution_startCompute)( - ANeuralNetworksExecution* execution, ANeuralNetworksEvent** event); - - /** - * Waits until the execution completes. - * - * More than one thread can wait on an event. When the execution completes, - * all threads will be released. - * - * See {@link ANeuralNetworksExecution} for information on multithreaded - * usage. - * - * @return ANEURALNETWORKS_NO_ERROR if the execution completed normally. - */ - int (*ANeuralNetworksEvent_wait)(ANeuralNetworksEvent* event); +/** + * Delete a memory object. + * + * Destroys the object used by the run time to keep track of the memory. + * This will free the underlying actual memory if no other code has open + * handles to this memory. + * + * @param memory The memory object to be freed. + */ +inline void ANeuralNetworksMemory_free(ANeuralNetworksMemory* memory) { + LOAD_FUNCTION(ANeuralNetworksMemory_free); + EXECUTE_FUNCTION(memory); +} - /** - * Destroys the event. - * - * See {@link ANeuralNetworksExecution} for information on multithreaded - * usage. - */ - void (*ANeuralNetworksEvent_free)(ANeuralNetworksEvent* event); +/** + * Create an empty {@link ANeuralNetworksModel}. + * + *

    This only creates the object. Computation is performed once + * {@link ANeuralNetworksExecution_startCompute} is invoked. + * + * The model should be constructed with calls to + * {@link ANeuralNetworksModel_addOperation} and + * {@link ANeuralNetworksModel_addOperand} + * + *

    {@link ANeuralNetworksModel_finish} should be called once the model + * has been fully constructed.

    + * + *

    {@link ANeuralNetworksModel_free} should be called once the model + * is no longer needed.

    + * + * @param model The {@link ANeuralNetworksModel} to be created. + * Set to NULL if unsuccessful. + * + * @return ANEURALNETWORKS_NO_ERROR if successful. + */ +inline int ANeuralNetworksModel_create(ANeuralNetworksModel** model) { + LOAD_FUNCTION(ANeuralNetworksModel_create); + EXECUTE_FUNCTION_RETURN(model); +} - // ASharedMemory_create was added in Android 8.0, so safe to use with NNAPI - // which was added in 8.1. - int (*ASharedMemory_create)(const char* name, size_t size); +/** + * Destroy a model. + * + * The model need not have been finished by a call to + * {@link ANeuralNetworksModel_finish}. + * + * See {@link ANeuralNetworksModel} for information on multithreaded usage. + * + * @param model The model to be destroyed. Passing NULL is acceptable and + * results in no operation. + */ +inline void ANeuralNetworksModel_free(ANeuralNetworksModel* model) { + LOAD_FUNCTION(ANeuralNetworksModel_free); + EXECUTE_FUNCTION(model); +} - /**/ -}; +/** + * Indicate that we have finished modifying a model. Required before + * calling {@link ANeuralNetworksCompilation_compile}. + * + * An application is responsible to make sure that no other thread uses + * the model at the same time. + * + * See {@link ANeuralNetworksModel} for information on multithreaded usage. + * + * @param model The model to be finished. + * + * @return ANEURALNETWORKS_NO_ERROR if successful. + */ +inline int ANeuralNetworksModel_finish(ANeuralNetworksModel* model) { + LOAD_FUNCTION(ANeuralNetworksModel_finish); + EXECUTE_FUNCTION_RETURN(model); +} + +/** + * Add an operand to a model. + * + * The order in which the operands are added is important. The first one added + * to a model will have the index value 0, the second 1, etc. These indexes are + * used as operand identifiers in {@link ANeuralNetworksModel_addOperation}, + * {@link ANeuralNetworksExecution_setInput}, + * {@link ANeuralNetworksExecution_setInputFromMemory}, + * {@link ANeuralNetworksExecution_setOutput}, + * {@link ANeuralNetworksExecution_setOutputFromMemory} and + * {@link ANeuralNetworksExecution_setOperandValue}. + * + * To build a model that can accommodate inputs of various sizes, as you may + * want to do for a CNN, set the size of the dimensions that will vary at run + * time to 0. If you do so, provide the full dimensions when calling + * {@link ANeuralNetworksExecution_setInput} or {@link + * ANeuralNetworksExecution_setInputFromMemory}. + * + * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has + * been called will return an error. + * + * See {@link ANeuralNetworksModel} for information on multithreaded usage. + * + * @param model The model to be modified. + * @param type The {@link ANeuralNetworksOperandType} that describes the shape + * of the operand. + * + * @return ANEURALNETWORKS_NO_ERROR if successful. + */ +inline int ANeuralNetworksModel_addOperand( + ANeuralNetworksModel* model, const ANeuralNetworksOperandType* type) { + LOAD_FUNCTION(ANeuralNetworksModel_addOperand); + EXECUTE_FUNCTION_RETURN(model, type); +} + +/** + * Sets an operand to a constant value. + * + * For scalar values, the content of buffer is copied into the model. + * + * For tensor values, a pointer to the buffer is stored within the model. + * The application is responsible for not changing the content of this region + * until all executions using this model have completed. As the data may + * be copied during processing, modifying the data after this call yields + * undefined results. + * + * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has + * been called will return an error. + * + * See {@link ANeuralNetworksModel} for information on multithreaded usage. + * + * @param model The model to be modified. + * @param index The index of the model operand we're setting. + * @param buffer A pointer to the data to use. + * @param length The size in bytes of the data value. + * + * @return ANEURALNETWORKS_NO_ERROR if successful. + */ +inline int ANeuralNetworksModel_setOperandValue(ANeuralNetworksModel* model, + int32_t index, + const void* buffer, + size_t length) { + LOAD_FUNCTION(ANeuralNetworksModel_setOperandValue); + EXECUTE_FUNCTION_RETURN(model, index, buffer, length); +} /** - * Load the NNAPI implementation from the shared libraries. - * The NnApi structure is filled with all the pointers. If one function doesn't - * exist, a null pointer is stored. + * Sets an operand to a value stored in a memory object. + * + * The content of the memory is not copied. A reference to that memory is stored + * inside the model. The application is responsible for not changing the content + * of the memory region until all executions using this model have completed. + * As the data may be copied during processing, modifying the data after this + * call yields undefined results. + * + * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has + * been called will return an error. + * + * See {@link ANeuralNetworksModel} for information on multithreaded usage. + * + * @param model The model to be modified. + * @param index The index of the model operand we're setting. + * @param buffer A pointer to the data to use. + * @param memory The memory containing the data. + * @param offset This specifies the location of the data within the memory. + * The offset is in bytes from the start of memory. + * @param length The size in bytes of the data value. + * + * @return ANEURALNETWORKS_NO_ERROR if successful. */ -const NnApi* NnApiImplementation(); +inline int ANeuralNetworksModel_setOperandValueFromMemory( + ANeuralNetworksModel* model, int32_t index, + const ANeuralNetworksMemory* memory, size_t offset, size_t length) { + LOAD_FUNCTION(ANeuralNetworksModel_setOperandValueFromMemory); + EXECUTE_FUNCTION_RETURN(model, index, memory, offset, length); +} + +/** + * Add an operation to a model. + * + * @param model The model to be modified. + * @param type The type of the operation. + * @param inputCount The number of entries in the inputs array. + * @param inputs An array of indexes identifying each operand. + * @param outputCount The number of entries in the outputs array. + * @param outputs An array of indexes identifying each operand. + * + * The operands specified by inputs and outputs must have been + * previously added by calls to {@link ANeuralNetworksModel_addOperand}. + * + * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has + * been called will return an error. + * + * See {@link ANeuralNetworksModel} for information on multithreaded usage. + * + * @return ANEURALNETWORKS_NO_ERROR if successful. + */ +inline int ANeuralNetworksModel_addOperation(ANeuralNetworksModel* model, + ANeuralNetworksOperationType type, + uint32_t inputCount, + const uint32_t* inputs, + uint32_t outputCount, + const uint32_t* outputs) { + LOAD_FUNCTION(ANeuralNetworksModel_addOperation); + EXECUTE_FUNCTION_RETURN(model, type, inputCount, inputs, outputCount, + outputs); +} + +/** + * Specifies which operands will be the model's inputs and outputs. + * + * An operand cannot be used for both input and output. Doing so will + * return an error. + * + * @param model The model to be modified. + * @param inputCount The number of entries in the inputs array. + * @param inputs An array of indexes identifying the input operands. + * @param outputCount The number of entries in the outputs array. + * @param outputs An array of indexes identifying the output operands. + * + * The operands specified by inputs and outputs must have been + * previously added by calls to {@link ANeuralNetworksModel_addOperand}. + * + * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has + * been called will return an error. + * + * See {@link ANeuralNetworksModel} for information on multithreaded usage. + * + */ +inline int ANeuralNetworksModel_identifyInputsAndOutputs( + ANeuralNetworksModel* model, uint32_t inputCount, const uint32_t* inputs, + uint32_t outputCount, const uint32_t* outputs) { + LOAD_FUNCTION(ANeuralNetworksModel_identifyInputsAndOutputs); + EXECUTE_FUNCTION_RETURN(model, inputCount, inputs, outputCount, outputs); +} + +/** + * Specifies whether {@link ANEURALNETWORKS_TENSOR_FLOAT32} is allowed to be + * calculated with range and/or precision as low as that of the IEEE 754 16-bit + * floating-point format. By default, {@link ANEURALNETWORKS_TENSOR_FLOAT32} + * must be calculated using at least the range and precision of the IEEE 754 + * 32-bit floating-point format. + * + * @param model The model to be modified. + * @param allow 'true' indicates {@link ANEURALNETWORKS_TENSOR_FLOAT32} may be + * calculated with range and/or precision as low as that of the + * IEEE 754 16-bit floating point format. 'false' indicates + * {@link ANEURALNETWORKS_TENSOR_FLOAT32} must be calculated using + * at least the range and precision of the IEEE 754 32-bit floating + * point format. + * + * Attempting to modify a model once {@link ANeuralNetworksModel_finish} has + * been called will return an error. + * + * Available since API level 28. + * + * See {@link ANeuralNetworksModel} for information on multithreaded usage. + */ +inline int ANeuralNetworksModel_relaxComputationFloat32toFloat16( + ANeuralNetworksModel* model, bool allow) { + LOAD_FUNCTION(ANeuralNetworksModel_relaxComputationFloat32toFloat16); + EXECUTE_FUNCTION_RETURN(model, allow); +} + +/** + * Create a {@link ANeuralNetworksCompilation} to compile the given model. + * This only creates the object. Compilation is only performed once + * {@link ANeuralNetworksCompilation_start} is invoked. + * + *

    The provided model must outlive the compilation.

    + * + * The model must already have been finished by a call to + * {@link ANeuralNetworksModel_finish}. + * + * See {@link ANeuralNetworksCompilation} for information on multithreaded + * usage. + * + * @param model The {@link ANeuralNetworksModel} to be compiled. + * @param compilation The newly created object or NULL if unsuccessful. + * + * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA + * if the model is invalid. + */ +inline int ANeuralNetworksCompilation_create( + ANeuralNetworksModel* model, ANeuralNetworksCompilation** compilation) { + LOAD_FUNCTION(ANeuralNetworksCompilation_create); + EXECUTE_FUNCTION_RETURN(model, compilation); +} + +/** + * Destroy a compilation. + * + *

    If called on a compilation for which + * {@link ANeuralNetworksCompilation_start} has been called, the + * function will return immediately but will mark the compilation to be deleted + * once the compilation completes. The {@link ANeuralNetworksCompilation_wait} + * will return ERROR_DELETED. + * + * See {@link ANeuralNetworksCompilation} for information on multithreaded + * usage. + * + * @param compilation The compilation to be destroyed. Passing NULL is + * acceptable and results in no operation. + */ +inline void ANeuralNetworksCompilation_free( + ANeuralNetworksCompilation* compilation) { + LOAD_FUNCTION(ANeuralNetworksCompilation_free); + EXECUTE_FUNCTION(compilation); +} + +/** + * Sets the execution preference. + * + *

    Provides guidance to the runtime when trade-offs are possible.

    + * + * See {@link ANeuralNetworksCompilation} for information on multithreaded + * usage. + * + * @param compilation The compilation to be modified. + * @param preference Either {@link PREFER_LOW_POWER}, + * {@link PREFER_SINGLE_FAST_ANSWER}, or + * {@link PREFER_SUSTAINED_SPEED}. + * + * @return ANEURALNETWORKS_NO_ERROR if successful. + */ +inline int ANeuralNetworksCompilation_setPreference( + ANeuralNetworksCompilation* compilation, int32_t preference) { + LOAD_FUNCTION(ANeuralNetworksCompilation_setPreference); + EXECUTE_FUNCTION_RETURN(compilation, preference); +} + +/** + * Waits until the compilation completes. + * + * More than one thread can wait on a compilation. When the compilation + * completes, all threads will be released. + * + * See {@link ANeuralNetworksCompilation} for information on multithreaded + * usage. + * + * @return ANEURALNETWORKS_NO_ERROR if the compilation completed normally. + */ +inline int ANeuralNetworksCompilation_finish( + ANeuralNetworksCompilation* compilation) { + LOAD_FUNCTION(ANeuralNetworksCompilation_finish); + EXECUTE_FUNCTION_RETURN(compilation); +} +/** + * Create a {@link ANeuralNetworksExecution} to apply the given compilation. + * This only creates the object. Computation is only performed once + * {@link ANeuralNetworksExecution_startCompute} is invoked. + * + *

    The provided compilation must outlive the execution.

    + * + * See {@link ANeuralNetworksExecution} for information on multithreaded usage. + * + * @param compilation The {@link ANeuralNetworksCompilation} to be evaluated. + * @param execution The newly created object or NULL if unsuccessful. + * + * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA + * if the compilation is invalid. + */ +inline int ANeuralNetworksExecution_create( + ANeuralNetworksCompilation* compilation, + ANeuralNetworksExecution** execution) { + LOAD_FUNCTION(ANeuralNetworksExecution_create); + EXECUTE_FUNCTION_RETURN(compilation, execution); +} + +/** + * Destroy an execution. + * + *

    If called on an execution for which + * {@link ANeuralNetworksExecution_startCompute} has been called, the + * function will return immediately but will mark the execution to be deleted + * once the computation completes. The {link ANeuralNetworksExecution_wait} + * will return ANEURALNETWORKS_ERROR_DELETED. + * + * See {@link ANeuralNetworksExecution} for information on multithreaded usage. + * + * @param execution The execution to be destroyed. Passing NULL is acceptable + * and results in no operation. + */ +inline void ANeuralNetworksExecution_free(ANeuralNetworksExecution* execution) { + LOAD_FUNCTION(ANeuralNetworksExecution_free); + EXECUTE_FUNCTION(execution); +} + +/** + * Associate a user buffer with an input of the model of the + * {@link ANeuralNetworksExecution}. + * + *

    The provided buffer must outlive the execution.

    + * + * See {@link ANeuralNetworksExecution} for information on multithreaded usage. + * + * @param execution The execution to be modified. + * @param index The index of the input argument we are setting. It is + * an index into the lists passed to + * {@link ANeuralNetworksModel_identifyInputsAndOutputs}. It is not + * the index associated with {@link + * ANeuralNetworksModel_addOperand}. + * @param type The type of the operand. This should be used to specify the + * dimensions that were set to 0 when the operand was added to the + * model. All other properties of the type must be the same as + * specified in the model. If the type is the same as specified + * when the model was built, NULL can be passed. + * @param buffer The buffer containing the data. + * @param length The length in bytes of the buffer. + * + * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA if + * the name is not recognized or the buffer is too small for the input. + */ +inline int ANeuralNetworksExecution_setInput( + ANeuralNetworksExecution* execution, int32_t index, + const ANeuralNetworksOperandType* type, const void* buffer, size_t length) { + LOAD_FUNCTION(ANeuralNetworksExecution_setInput); + EXECUTE_FUNCTION_RETURN(execution, index, type, buffer, length); +} + +/** + * Associate part of a memory object with an input of the model of the + * {@link ANeuralNetworksExecution}. + * + *

    The provided memory must outlive the execution.

    + * + * See {@link ANeuralNetworksExecution} for information on multithreaded usage. + * + * @param execution The execution to be modified. + * @param index The index of the input argument we are setting. It is + * an index into the lists passed to + * {@link ANeuralNetworksModel_identifyInputsAndOutputs}. It is not + * the index associated with {@link + * ANeuralNetworksModel_addOperand}. + * @param type The type of the operand. This can be used to specify the + * dimensions that were set to 0 when the operand was added to the + * model. All other values must be the same as specified in the + * model. If the type is the same as specified when the model + * was built, NULL can be passed. + * @param memory The memory containing the data. + * @param offset This specifies the location of the data within the memory. + * The offset is in bytes from the start of memory. + * @param length The size in bytes of the data value. + * + * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA if + * the name is not recognized or the buffer is too small for the input. + */ +inline int ANeuralNetworksExecution_setInputFromMemory( + ANeuralNetworksExecution* execution, int32_t index, + const ANeuralNetworksOperandType* type, const ANeuralNetworksMemory* memory, + size_t offset, size_t length) { + LOAD_FUNCTION(ANeuralNetworksExecution_setInputFromMemory); + EXECUTE_FUNCTION_RETURN(execution, index, type, memory, offset, length); +} + +/** + * Associate a user buffer with an output of the model of the + * {@link ANeuralNetworksExecution}. + * + *

    The provided buffer must outlive the execution.

    + * + * See {@link ANeuralNetworksExecution} for information on multithreaded usage. + * + * @param execution The execution to be modified. + * @param index The index of the output argument we are setting. It is + * an index into the lists passed to + * {@link ANeuralNetworksModel_identifyInputsAndOutputs}. It is not + * the index associated with {@link + * ANeuralNetworksModel_addOperand}. + * @param type The type of the operand. This can be used to specify the + * dimensions that were set to 0 when the operand was added to the + * model. All other values must be the same as specified in the + * model. If the type is the same as specified when the model + * was built, NULL can be passed. + * @param buffer The buffer where the data is to be written. + * @param length The length in bytes of the buffer. + * + * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA if + * the name is not recognized or the buffer is too small for the output. + */ +inline int ANeuralNetworksExecution_setOutput( + ANeuralNetworksExecution* execution, int32_t index, + const ANeuralNetworksOperandType* type, void* buffer, size_t length) { + LOAD_FUNCTION(ANeuralNetworksExecution_setOutput); + EXECUTE_FUNCTION_RETURN(execution, index, type, buffer, length); +} + +/** + * Associate part of a memory object with an output of the model of the + * {@link ANeuralNetworksExecution}. + * + *

    The provided memory must outlive the execution.

    + * + * See {@link ANeuralNetworksExecution} for information on multithreaded usage. + * + * @param execution The execution to be modified. + * @param index The index of the output argument we are setting. It is + * an index into the lists passed to + * {@link ANeuralNetworksModel_identifyInputsAndOutputs}. It is not + * the index associated with {@link + * ANeuralNetworksModel_addOperand}. + * @param type The type of the operand. This can be used to specify the + * dimensions that were set to 0 when the operand was added to the + * model. All other values must be the same as specified in the + * model. If the type is the same as specified when the model + * was built, NULL can be passed. + * @param memory The memory where the data is to be stored. + * @param offset This specifies the location of the data within the memory. + * The offset is in bytes from the start of memory. + * @param length The length in bytes of the data value. + * + * @return ANEURALNETWORKS_NO_ERROR if successful, ANEURALNETWORKS_BAD_DATA if + * the name is not recognized or the buffer is too small for the output. + */ +inline int ANeuralNetworksExecution_setOutputFromMemory( + ANeuralNetworksExecution* execution, int32_t index, + const ANeuralNetworksOperandType* type, const ANeuralNetworksMemory* memory, + size_t offset, size_t length) { + LOAD_FUNCTION(ANeuralNetworksExecution_setOutputFromMemory); + EXECUTE_FUNCTION_RETURN(execution, index, type, memory, offset, length); +} + +/** + * Schedule evaluation of the execution. + * + *

    Schedules evaluation of the execution. Once the model has been + * applied and the outputs are ready to be consumed, the execution will be + * signaled. Use {@link ANeuralNetworksExecution_wait} to wait for that signal. + *

    + * + * Multiple executions can be scheduled and evaluated concurrently, and + * compilations can be performed concurrently with executions. The runtime makes + * no guarantee on the ordering of the completion of compilations and + * executions. If it's important to the application, the application should + * enforce the ordering by using {@link ANeuralNetworksCompilation_wait} and + * {@link ANeuralNetworksExecution_wait}. + * + * ANeuralNetworksExecution_wait must be called to recuperate the resources used + * by the execution. + * + * See {@link ANeuralNetworksExecution} for information on multithreaded usage. + * + * @param execution The execution to be scheduled and executed. + * + * @return ANEURALNETWORKS_NO_ERROR if successful. + */ +inline int ANeuralNetworksExecution_startCompute( + ANeuralNetworksExecution* execution, ANeuralNetworksEvent** event) { + LOAD_FUNCTION(ANeuralNetworksExecution_startCompute); + EXECUTE_FUNCTION_RETURN(execution, event); +} + +/** + * Waits until the execution completes. + * + * More than one thread can wait on an event. When the execution completes, + * all threads will be released. + * + * See {@link ANeuralNetworksExecution} for information on multithreaded usage. + * + * @return ANEURALNETWORKS_NO_ERROR if the execution completed normally. + */ +inline int ANeuralNetworksEvent_wait(ANeuralNetworksEvent* event) { + LOAD_FUNCTION(ANeuralNetworksEvent_wait); + EXECUTE_FUNCTION_RETURN(event); +} + +/** + * Destroys the event. + * + * See {@link ANeuralNetworksExecution} for information on multithreaded usage. + */ +inline void ANeuralNetworksEvent_free(ANeuralNetworksEvent* event) { + LOAD_FUNCTION(ANeuralNetworksEvent_free); + EXECUTE_FUNCTION(event); +} + +/**/ #endif // TENSORFLOW_LITE_NNAPI_NEURALNETWORKSSHIM_H_ diff --git a/tensorflow/lite/nnapi/nnapi_lib_test.cc b/tensorflow/lite/nnapi/nnapi_lib_test.cc deleted file mode 100644 index aaf0ffaa8e..0000000000 --- a/tensorflow/lite/nnapi/nnapi_lib_test.cc +++ /dev/null @@ -1,90 +0,0 @@ -/* Copyright 2018 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 -#include "tensorflow/lite/nnapi/NeuralNetworksShim.h" - -namespace { - -TEST(NnapiLibTest, NnApiImplementation) { - const NnApi* nnapi = NnApiImplementation(); - EXPECT_NE(nnapi, nullptr); -#ifdef __ANDROID__ - EXPECT_TRUE(nnapi->nnapi_exists); - EXPECT_GT(nnapi->android_sdk_version, 0); - EXPECT_NE(nnapi->ANeuralNetworksMemory_createFromFd, nullptr); - EXPECT_NE(nnapi->ANeuralNetworksMemory_free, nullptr); - EXPECT_NE(nnapi->ANeuralNetworksModel_create, nullptr); - EXPECT_NE(nnapi->ANeuralNetworksModel_free, nullptr); - EXPECT_NE(nnapi->ANeuralNetworksModel_finish, nullptr); - EXPECT_NE(nnapi->ANeuralNetworksModel_addOperand, nullptr); - EXPECT_NE(nnapi->ANeuralNetworksModel_setOperandValue, nullptr); - EXPECT_NE(nnapi->ANeuralNetworksModel_setOperandValueFromMemory, nullptr); - EXPECT_NE(nnapi->ANeuralNetworksModel_addOperation, nullptr); - EXPECT_NE(nnapi->ANeuralNetworksModel_identifyInputsAndOutputs, nullptr); - if (nnapi->android_sdk_version >= 28) { - // relaxComputationFloat32toFloat16 only available with Android 9.0 (P). - EXPECT_NE(nnapi->ANeuralNetworksModel_relaxComputationFloat32toFloat16, - nullptr); - } else { - EXPECT_EQ(nnapi->ANeuralNetworksModel_relaxComputationFloat32toFloat16, - nullptr); - } - EXPECT_NE(nnapi->ANeuralNetworksCompilation_create, nullptr); - EXPECT_NE(nnapi->ANeuralNetworksCompilation_free, nullptr); - EXPECT_NE(nnapi->ANeuralNetworksCompilation_setPreference, nullptr); - EXPECT_NE(nnapi->ANeuralNetworksCompilation_finish, nullptr); - EXPECT_NE(nnapi->ANeuralNetworksExecution_create, nullptr); - EXPECT_NE(nnapi->ANeuralNetworksExecution_free, nullptr); - EXPECT_NE(nnapi->ANeuralNetworksExecution_setInput, nullptr); - EXPECT_NE(nnapi->ANeuralNetworksExecution_setInputFromMemory, nullptr); - EXPECT_NE(nnapi->ANeuralNetworksExecution_setOutput, nullptr); - EXPECT_NE(nnapi->ANeuralNetworksExecution_setOutputFromMemory, nullptr); - EXPECT_NE(nnapi->ANeuralNetworksExecution_startCompute, nullptr); - EXPECT_NE(nnapi->ANeuralNetworksEvent_wait, nullptr); - EXPECT_NE(nnapi->ANeuralNetworksEvent_free, nullptr); - EXPECT_NE(nnapi->ASharedMemory_create, nullptr); -#else - EXPECT_FALSE(nnapi->nnapi_exists); - EXPECT_EQ(nnapi->android_sdk_version, 0); - EXPECT_EQ(nnapi->ANeuralNetworksMemory_createFromFd, nullptr); - EXPECT_EQ(nnapi->ANeuralNetworksMemory_free, nullptr); - EXPECT_EQ(nnapi->ANeuralNetworksModel_create, nullptr); - EXPECT_EQ(nnapi->ANeuralNetworksModel_free, nullptr); - EXPECT_EQ(nnapi->ANeuralNetworksModel_finish, nullptr); - EXPECT_EQ(nnapi->ANeuralNetworksModel_addOperand, nullptr); - EXPECT_EQ(nnapi->ANeuralNetworksModel_setOperandValue, nullptr); - EXPECT_EQ(nnapi->ANeuralNetworksModel_setOperandValueFromMemory, nullptr); - EXPECT_EQ(nnapi->ANeuralNetworksModel_addOperation, nullptr); - EXPECT_EQ(nnapi->ANeuralNetworksModel_identifyInputsAndOutputs, nullptr); - EXPECT_EQ(nnapi->ANeuralNetworksModel_relaxComputationFloat32toFloat16, - nullptr); - EXPECT_EQ(nnapi->ANeuralNetworksCompilation_create, nullptr); - EXPECT_EQ(nnapi->ANeuralNetworksCompilation_free, nullptr); - EXPECT_EQ(nnapi->ANeuralNetworksCompilation_setPreference, nullptr); - EXPECT_EQ(nnapi->ANeuralNetworksCompilation_finish, nullptr); - EXPECT_EQ(nnapi->ANeuralNetworksExecution_create, nullptr); - EXPECT_EQ(nnapi->ANeuralNetworksExecution_free, nullptr); - EXPECT_EQ(nnapi->ANeuralNetworksExecution_setInput, nullptr); - EXPECT_EQ(nnapi->ANeuralNetworksExecution_setInputFromMemory, nullptr); - EXPECT_EQ(nnapi->ANeuralNetworksExecution_setOutput, nullptr); - EXPECT_EQ(nnapi->ANeuralNetworksExecution_setOutputFromMemory, nullptr); - EXPECT_EQ(nnapi->ANeuralNetworksExecution_startCompute, nullptr); - EXPECT_EQ(nnapi->ANeuralNetworksEvent_wait, nullptr); - EXPECT_EQ(nnapi->ANeuralNetworksEvent_free, nullptr); - EXPECT_EQ(nnapi->ASharedMemory_create, nullptr); -#endif -} - -} // namespace diff --git a/tensorflow/lite/nnapi_delegate.cc b/tensorflow/lite/nnapi_delegate.cc index 44b9930acc..dc8e81cde7 100644 --- a/tensorflow/lite/nnapi_delegate.cc +++ b/tensorflow/lite/nnapi_delegate.cc @@ -84,27 +84,56 @@ void logError(const char* format, ...) { static const int64_t kOperandIdNotSet = -1; static const int64_t kOperandNotNeeded = -2; +namespace { + +int32_t GetAndroidSdkVersion() { +#ifdef __ANDROID__ + const char* sdkProp = "ro.build.version.sdk"; + char sdkVersion[PROP_VALUE_MAX]; + int length = __system_property_get(sdkProp, sdkVersion); + if (length != 0) { + for (int i = 0; i < length; ++i) { + int digit = sdkVersion[i] - '0'; + if (digit < 0 || digit > 9) { + // Non-numeric SDK version, assume it's higher then expected; + return 0xFFFF; + } + } + return atoi(sdkVersion); + } + FATAL("No %s prop", sdkProp); +#endif // __ANDROID__ + return 0; +} + +int32_t GetAndroidSdkVersionCached() { + static int32_t androidSdkVersion = GetAndroidSdkVersion(); + return androidSdkVersion; +} + +} // namespace + NNAPIAllocation::NNAPIAllocation(const char* filename, ErrorReporter* error_reporter) : MMAPAllocation(filename, error_reporter) { if (mmapped_buffer_ != MAP_FAILED) - CHECK_NN(NnApiImplementation()->ANeuralNetworksMemory_createFromFd( - buffer_size_bytes_, PROT_READ, mmap_fd_, 0, &handle_)); + CHECK_NN(ANeuralNetworksMemory_createFromFd(buffer_size_bytes_, PROT_READ, + mmap_fd_, 0, &handle_)); } NNAPIAllocation::~NNAPIAllocation() { if (handle_) { - NnApiImplementation()->ANeuralNetworksMemory_free(handle_); + ANeuralNetworksMemory_free(handle_); } } NNAPIDelegate::~NNAPIDelegate() { if (nn_compiled_model_) { - NnApiImplementation()->ANeuralNetworksCompilation_free(nn_compiled_model_); + ANeuralNetworksCompilation_free(nn_compiled_model_); nn_compiled_model_ = nullptr; } if (nn_model_) { - NnApiImplementation()->ANeuralNetworksModel_free(nn_model_); + ANeuralNetworksModel_free(nn_model_); nn_model_ = nullptr; // TODO(aselle): Is this thread-safe and callable multiple times? } @@ -116,7 +145,6 @@ TfLiteStatus addTensorOperands(tflite::Subgraph* subgraph, ANeuralNetworksModel* nn_model, uint32_t* no_of_operands_added, std::vector* nnapi_ids) { - const NnApi* nnapi = NnApiImplementation(); uint32_t next_id = 0; for (size_t i = 0; i < subgraph->tensors_size(); i++) { // Skip temporaries and RNN back-edges. @@ -170,24 +198,24 @@ TfLiteStatus addTensorOperands(tflite::Subgraph* subgraph, nn_type, static_cast(tensor->dims->size), reinterpret_cast(tensor->dims->data), scale, zeroPoint}; RETURN_ERROR_IF_NN_FAILED( - nnapi->ANeuralNetworksModel_addOperand(nn_model, &operand_type)); + ANeuralNetworksModel_addOperand(nn_model, &operand_type)); // TODO(aselle): Based on Michael's suggestion, limiting this to read // only memory if (tensor->allocation_type == kTfLiteMmapRo) { if (const NNAPIAllocation* alloc = dynamic_cast( static_cast(tensor->allocation))) { RETURN_ERROR_IF_NN_FAILED( - nnapi->ANeuralNetworksModel_setOperandValueFromMemory( + ANeuralNetworksModel_setOperandValueFromMemory( nn_model, next_id, alloc->memory(), alloc->offset(tensor->data.raw), tensor->bytes)); } else { - RETURN_ERROR_IF_NN_FAILED(nnapi->ANeuralNetworksModel_setOperandValue( + RETURN_ERROR_IF_NN_FAILED(ANeuralNetworksModel_setOperandValue( nn_model, next_id, tensor->data.raw, tensor->bytes)); } } else if (tensor->bytes == 0) { // These size 0 tensors are optional tensors reserved. - RETURN_ERROR_IF_NN_FAILED(nnapi->ANeuralNetworksModel_setOperandValue( - nn_model, next_id, nullptr, 0)); + RETURN_ERROR_IF_NN_FAILED( + ANeuralNetworksModel_setOperandValue(nn_model, next_id, nullptr, 0)); } ++next_id; @@ -216,7 +244,6 @@ TfLiteStatus AddOpsAndParams( uint32_t next_id, std::vector* model_state_inputs, std::vector* model_state_outputs, const std::vector& tensor_id_to_nnapi_id) { - const NnApi* nnapi = NnApiImplementation(); for (size_t i = 0; i < subgraph->nodes_size(); i++) { const auto* node_and_registration = subgraph->node_and_registration(i); const TfLiteNode& node = node_and_registration->first; @@ -231,21 +258,21 @@ TfLiteStatus AddOpsAndParams( MapAndAddTensorIds(node.outputs->data, node.outputs->size, &augmented_outputs, tensor_id_to_nnapi_id); - auto add_scalar_int32 = [nnapi, &nn_model, &augmented_inputs, + auto add_scalar_int32 = [&nn_model, &augmented_inputs, &next_id](int value) { ANeuralNetworksOperandType operand_type{.type = ANEURALNETWORKS_INT32}; - CHECK_NN(nnapi->ANeuralNetworksModel_addOperand(nn_model, &operand_type)) - CHECK_NN(nnapi->ANeuralNetworksModel_setOperandValue( - nn_model, next_id, &value, sizeof(int32_t))) + CHECK_NN(ANeuralNetworksModel_addOperand(nn_model, &operand_type)) + CHECK_NN(ANeuralNetworksModel_setOperandValue(nn_model, next_id, &value, + sizeof(int32_t))) augmented_inputs.push_back(next_id++); }; - auto add_scalar_float32 = [nnapi, &nn_model, &augmented_inputs, + auto add_scalar_float32 = [&nn_model, &augmented_inputs, &next_id](float value) { ANeuralNetworksOperandType operand_type{.type = ANEURALNETWORKS_FLOAT32}; - CHECK_NN(nnapi->ANeuralNetworksModel_addOperand(nn_model, &operand_type)) - CHECK_NN(nnapi->ANeuralNetworksModel_setOperandValue( - nn_model, next_id, &value, sizeof(float))) + CHECK_NN(ANeuralNetworksModel_addOperand(nn_model, &operand_type)) + CHECK_NN(ANeuralNetworksModel_setOperandValue(nn_model, next_id, &value, + sizeof(float))) augmented_inputs.push_back(next_id++); }; @@ -254,8 +281,8 @@ TfLiteStatus AddOpsAndParams( .type = ANEURALNETWORKS_TENSOR_INT32, .dimensionCount = 1, .dimensions = &num_values}; - CHECK_NN(nnapi->ANeuralNetworksModel_addOperand(nn_model, &operand_type)) - CHECK_NN(nnapi->ANeuralNetworksModel_setOperandValue( + CHECK_NN(ANeuralNetworksModel_addOperand(nn_model, &operand_type)) + CHECK_NN(ANeuralNetworksModel_setOperandValue( nn_model, next_id, values, sizeof(int32_t) * num_values)); augmented_inputs.push_back(next_id++); }; @@ -264,16 +291,15 @@ TfLiteStatus AddOpsAndParams( // For each state_out tensor, a corresponding state_in operand needs to be // created for NNAPI. auto duplicate_state_tensor_float32 = - [nnapi, subgraph, &nn_model, &next_id, &augmented_inputs, - &model_state_inputs, &model_state_outputs](int tensor_id) { + [subgraph, &nn_model, &next_id, &augmented_inputs, &model_state_inputs, + &model_state_outputs](int tensor_id) { const TfLiteTensor* tensor = subgraph->tensor(tensor_id); ANeuralNetworksOperandType operand_type{ ANEURALNETWORKS_TENSOR_FLOAT32, static_cast(tensor->dims->size), reinterpret_cast(tensor->dims->data), tensor->params.scale, tensor->params.zero_point}; - CHECK_NN( - nnapi->ANeuralNetworksModel_addOperand(nn_model, &operand_type)); + CHECK_NN(ANeuralNetworksModel_addOperand(nn_model, &operand_type)); augmented_inputs.push_back(next_id); model_state_inputs->push_back(next_id); model_state_outputs->push_back(tensor_id); @@ -362,7 +388,7 @@ TfLiteStatus AddOpsAndParams( }; // LSTM in NNAPI requires scratch tensor as an output operand. - auto add_lstm_scratch_tensor_float32 = [nnapi, subgraph, &node, &nn_model, + auto add_lstm_scratch_tensor_float32 = [subgraph, &node, &nn_model, &next_id, &augmented_outputs]() { if (node.temporaries->size == 0) return; int scratch_buffer_index = node.temporaries->data[0]; @@ -372,7 +398,7 @@ TfLiteStatus AddOpsAndParams( static_cast(tensor->dims->size), reinterpret_cast(tensor->dims->data), tensor->params.scale, tensor->params.zero_point}; - CHECK_NN(nnapi->ANeuralNetworksModel_addOperand(nn_model, &operand_type)); + CHECK_NN(ANeuralNetworksModel_addOperand(nn_model, &operand_type)); augmented_outputs.insert(augmented_outputs.begin(), next_id++); }; @@ -401,16 +427,15 @@ TfLiteStatus AddOpsAndParams( }; // Handle optional input tensors. - auto add_optional_tensors = [nnapi, &nn_model, &augmented_inputs, + auto add_optional_tensors = [&nn_model, &augmented_inputs, &next_id](int nn_type) { for (size_t idx = 0; idx < augmented_inputs.size(); idx++) { if (augmented_inputs[idx] == kOptionalTensor) { const std::vector dim = {0, 0}; ANeuralNetworksOperandType operand_type{nn_type, 2, dim.data(), 0, 0}; - CHECK_NN( - nnapi->ANeuralNetworksModel_addOperand(nn_model, &operand_type)) - CHECK_NN(nnapi->ANeuralNetworksModel_setOperandValue( - nn_model, next_id, nullptr, 0)) + CHECK_NN(ANeuralNetworksModel_addOperand(nn_model, &operand_type)) + CHECK_NN(ANeuralNetworksModel_setOperandValue(nn_model, next_id, + nullptr, 0)) augmented_inputs[idx] = next_id++; } } @@ -671,13 +696,13 @@ TfLiteStatus AddOpsAndParams( break; } - if (nnapi_version == 11 && nnapi->android_sdk_version < 28) { + if (nnapi_version == 11 && GetAndroidSdkVersionCached() < 28) { logError("Op %d needs NNAPI1.1", builtin); return kTfLiteError; } // Add the operation. - RETURN_ERROR_IF_NN_FAILED(nnapi->ANeuralNetworksModel_addOperation( + RETURN_ERROR_IF_NN_FAILED(ANeuralNetworksModel_addOperation( nn_model, nn_op_type, static_cast(augmented_inputs.size()), augmented_inputs.data(), static_cast(augmented_outputs.size()), @@ -689,10 +714,9 @@ TfLiteStatus AddOpsAndParams( TfLiteStatus NNAPIDelegate::BuildGraph(Subgraph* subgraph) { if (nn_model_ && nn_compiled_model_) return model_status_; - const NnApi* nnapi = NnApiImplementation(); // TODO(aselle): This is not correct. need to handle resize invalidation. if (!nn_model_) { - CHECK_NN(nnapi->ANeuralNetworksModel_create(&nn_model_)); + CHECK_NN(ANeuralNetworksModel_create(&nn_model_)); // Find which tensors should be added to NNAPI. TFLite has temporaries // and RNN back-edges which are are not valid for NNAPI. We look through all @@ -739,22 +763,21 @@ TfLiteStatus NNAPIDelegate::BuildGraph(Subgraph* subgraph) { model_states_outputs_.size(), &augmented_outputs, tensor_id_to_nnapi_id); - CHECK_NN(nnapi->ANeuralNetworksModel_identifyInputsAndOutputs( + CHECK_NN(ANeuralNetworksModel_identifyInputsAndOutputs( nn_model_, static_cast(augmented_inputs.size()), reinterpret_cast(augmented_inputs.data()), static_cast(augmented_outputs.size()), reinterpret_cast(augmented_outputs.data()))); - if (nnapi->android_sdk_version >= 28) { - CHECK_NN(nnapi->ANeuralNetworksModel_relaxComputationFloat32toFloat16( + if (GetAndroidSdkVersionCached() >= 28) { + CHECK_NN(ANeuralNetworksModel_relaxComputationFloat32toFloat16( nn_model_, subgraph->GetAllowFp16PrecisionForFp32())); } - CHECK_NN(nnapi->ANeuralNetworksModel_finish(nn_model_)); + CHECK_NN(ANeuralNetworksModel_finish(nn_model_)); } if (!nn_compiled_model_) { - CHECK_NN(nnapi->ANeuralNetworksCompilation_create(nn_model_, - &nn_compiled_model_)); - CHECK_NN(nnapi->ANeuralNetworksCompilation_finish(nn_compiled_model_)); + CHECK_NN(ANeuralNetworksCompilation_create(nn_model_, &nn_compiled_model_)); + CHECK_NN(ANeuralNetworksCompilation_finish(nn_compiled_model_)); } return kTfLiteOk; } @@ -770,10 +793,8 @@ TfLiteStatus NNAPIDelegate::Invoke(Subgraph* subgraph) { return model_status_; } - const NnApi* nnapi = NnApiImplementation(); ANeuralNetworksExecution* execution = nullptr; - CHECK_NN( - nnapi->ANeuralNetworksExecution_create(nn_compiled_model_, &execution)); + CHECK_NN(ANeuralNetworksExecution_create(nn_compiled_model_, &execution)); // Currently perform deep copy of input buffer for (size_t i = 0; i < subgraph->inputs().size(); i++) { @@ -781,7 +802,7 @@ TfLiteStatus NNAPIDelegate::Invoke(Subgraph* subgraph) { // TODO(aselle): Is this what we want or do we want input instead? // TODO(aselle): This should be called setInputValue maybe to be cons. TfLiteTensor* tensor = subgraph->tensor(input); - CHECK_NN(nnapi->ANeuralNetworksExecution_setInput( + CHECK_NN(ANeuralNetworksExecution_setInput( execution, i, nullptr, tensor->data.raw, tensor->bytes)); } @@ -789,7 +810,7 @@ TfLiteStatus NNAPIDelegate::Invoke(Subgraph* subgraph) { for (size_t i = 0; i < subgraph->outputs().size(); i++) { int output = subgraph->outputs()[i]; TfLiteTensor* tensor = subgraph->tensor(output); - CHECK_NN(nnapi->ANeuralNetworksExecution_setOutput( + CHECK_NN(ANeuralNetworksExecution_setOutput( execution, i, nullptr, tensor->data.raw, tensor->bytes)); } @@ -801,21 +822,21 @@ TfLiteStatus NNAPIDelegate::Invoke(Subgraph* subgraph) { // Here we are using a deep copy for state_in tensors so that we are not // reading and writing into the same buffer during a invocation. // TODO(miaowang): using double shared buffer to minimize the copies. - CHECK_NN(nnapi->ANeuralNetworksExecution_setInput( + CHECK_NN(ANeuralNetworksExecution_setInput( execution, i + subgraph->inputs().size(), nullptr, tensor->data.raw, tensor->bytes)); // Tell NNAPI where to output the state_out. - CHECK_NN(nnapi->ANeuralNetworksExecution_setOutput( + CHECK_NN(ANeuralNetworksExecution_setOutput( execution, i + subgraph->outputs().size(), nullptr, tensor->data.raw, tensor->bytes)); } // Currently use blocking compute. ANeuralNetworksEvent* event = nullptr; - CHECK_NN(nnapi->ANeuralNetworksExecution_startCompute(execution, &event)); - CHECK_NN(nnapi->ANeuralNetworksEvent_wait(event)); - nnapi->ANeuralNetworksEvent_free(event); - nnapi->ANeuralNetworksExecution_free(execution); + CHECK_NN(ANeuralNetworksExecution_startCompute(execution, &event)); + CHECK_NN(ANeuralNetworksEvent_wait(event)); + ANeuralNetworksEvent_free(event); + ANeuralNetworksExecution_free(execution); #if 0 printf("From the NN API:\n"); @@ -833,8 +854,6 @@ TfLiteStatus NNAPIDelegate::Invoke(Subgraph* subgraph) { return kTfLiteOk; } -bool NNAPIDelegate::IsSupported() { - return NnApiImplementation()->nnapi_exists; -} +bool NNAPIDelegate::IsSupported() { return NNAPIExists(); } } // namespace tflite -- GitLab From a460c9525d416e442c245c87ccb6da9032cc5fd6 Mon Sep 17 00:00:00 2001 From: Scott Zhu Date: Wed, 2 Jan 2019 16:58:33 -0800 Subject: [PATCH 274/622] Fix masking generate logic in sequence model. sequence model was incorrectly using the layer output instead of input to generate mask, and compute_mask of the layer usually does not have shape check. PiperOrigin-RevId: 227605298 --- tensorflow/python/keras/engine/sequential.py | 9 +- tensorflow/python/keras/layers/gru_test.py | 112 ++++++++-------- tensorflow/python/keras/layers/lstm_test.py | 8 +- .../python/keras/layers/simplernn_test.py | 8 +- .../python/keras/layers/unified_gru_test.py | 64 +++++---- .../python/keras/layers/unified_lstm_test.py | 125 +++++++++--------- 6 files changed, 153 insertions(+), 173 deletions(-) diff --git a/tensorflow/python/keras/engine/sequential.py b/tensorflow/python/keras/engine/sequential.py index dffd3c8a69..3627091198 100644 --- a/tensorflow/python/keras/engine/sequential.py +++ b/tensorflow/python/keras/engine/sequential.py @@ -262,16 +262,17 @@ class Sequential(Model): with ops.name_scope(layer._name_scope()): layer._maybe_build(x) layer.built = True + if layer.supports_masking: + mask = layer.compute_mask(x, mask) + else: + mask = None + if context.executing_eagerly(): x = layer(x, **kwargs) elif layer.dynamic: x = layer._symbolic_call(x) else: x = layer.call(x, **kwargs) - if layer.supports_masking: - mask = layer.compute_mask(x, mask) - else: - mask = None if not context.executing_eagerly(): x._keras_mask = mask return x, mask diff --git a/tensorflow/python/keras/layers/gru_test.py b/tensorflow/python/keras/layers/gru_test.py index 52a7944d60..e2b65de661 100644 --- a/tensorflow/python/keras/layers/gru_test.py +++ b/tensorflow/python/keras/layers/gru_test.py @@ -117,63 +117,6 @@ class GRULayerTest(keras_parameterized.TestCase): run_eagerly=testing_utils.should_run_eagerly()) model.fit(inputs, targets, epochs=1, batch_size=2, verbose=1) - -@tf_test_util.run_all_in_graph_and_eager_modes -class GRULayerGenericTest(test.TestCase): - - def test_constraints_GRU(self): - embedding_dim = 4 - layer_class = keras.layers.GRU - k_constraint = keras.constraints.max_norm(0.01) - r_constraint = keras.constraints.max_norm(0.01) - b_constraint = keras.constraints.max_norm(0.01) - layer = layer_class( - 5, - return_sequences=False, - weights=None, - input_shape=(None, embedding_dim), - kernel_constraint=k_constraint, - recurrent_constraint=r_constraint, - bias_constraint=b_constraint) - layer.build((None, None, embedding_dim)) - self.assertEqual(layer.cell.kernel.constraint, k_constraint) - self.assertEqual(layer.cell.recurrent_kernel.constraint, r_constraint) - self.assertEqual(layer.cell.bias.constraint, b_constraint) - - def test_from_config_GRU(self): - layer_class = keras.layers.GRU - for stateful in (False, True): - l1 = layer_class(units=1, stateful=stateful) - l2 = layer_class.from_config(l1.get_config()) - assert l1.get_config() == l2.get_config() - - def test_regularizers_GRU(self): - embedding_dim = 4 - layer_class = keras.layers.GRU - layer = layer_class( - 5, - return_sequences=False, - weights=None, - input_shape=(None, embedding_dim), - kernel_regularizer=keras.regularizers.l1(0.01), - recurrent_regularizer=keras.regularizers.l1(0.01), - bias_regularizer='l2', - activity_regularizer='l1') - layer.build((None, None, 2)) - self.assertEqual(len(layer.losses), 3) - - x = keras.backend.variable(np.ones((2, 3, 2))) - layer(x) - if context.executing_eagerly(): - self.assertEqual(len(layer.losses), 4) - else: - self.assertEqual(len(layer.get_losses_for(x)), 1) - - -class GRULayerV1OnlyTest(test.TestCase): - - @tf_test_util.run_v1_only('b/120941292') - @tf_test_util.run_in_graph_and_eager_modes def test_statefulness_GRU(self): num_samples = 2 timesteps = 3 @@ -192,7 +135,8 @@ class GRULayerV1OnlyTest(test.TestCase): layer = layer_class( units, return_sequences=False, stateful=True, weights=None) model.add(layer) - model.compile(optimizer='sgd', loss='mse') + model.compile(optimizer='sgd', loss='mse', + run_eagerly=testing_utils.should_run_eagerly()) out1 = model.predict(np.ones((num_samples, timesteps))) self.assertEqual(out1.shape, (num_samples, units)) @@ -237,5 +181,57 @@ class GRULayerV1OnlyTest(test.TestCase): np.testing.assert_allclose(out7, out6, atol=1e-5) +@tf_test_util.run_all_in_graph_and_eager_modes +class GRULayerGenericTest(test.TestCase): + + def test_constraints_GRU(self): + embedding_dim = 4 + layer_class = keras.layers.GRU + k_constraint = keras.constraints.max_norm(0.01) + r_constraint = keras.constraints.max_norm(0.01) + b_constraint = keras.constraints.max_norm(0.01) + layer = layer_class( + 5, + return_sequences=False, + weights=None, + input_shape=(None, embedding_dim), + kernel_constraint=k_constraint, + recurrent_constraint=r_constraint, + bias_constraint=b_constraint) + layer.build((None, None, embedding_dim)) + self.assertEqual(layer.cell.kernel.constraint, k_constraint) + self.assertEqual(layer.cell.recurrent_kernel.constraint, r_constraint) + self.assertEqual(layer.cell.bias.constraint, b_constraint) + + def test_from_config_GRU(self): + layer_class = keras.layers.GRU + for stateful in (False, True): + l1 = layer_class(units=1, stateful=stateful) + l2 = layer_class.from_config(l1.get_config()) + assert l1.get_config() == l2.get_config() + + def test_regularizers_GRU(self): + embedding_dim = 4 + layer_class = keras.layers.GRU + layer = layer_class( + 5, + return_sequences=False, + weights=None, + input_shape=(None, embedding_dim), + kernel_regularizer=keras.regularizers.l1(0.01), + recurrent_regularizer=keras.regularizers.l1(0.01), + bias_regularizer='l2', + activity_regularizer='l1') + layer.build((None, None, 2)) + self.assertEqual(len(layer.losses), 3) + + x = keras.backend.variable(np.ones((2, 3, 2))) + layer(x) + if context.executing_eagerly(): + self.assertEqual(len(layer.losses), 4) + else: + self.assertEqual(len(layer.get_losses_for(x)), 1) + + if __name__ == '__main__': test.main() diff --git a/tensorflow/python/keras/layers/lstm_test.py b/tensorflow/python/keras/layers/lstm_test.py index e8c55438c9..38c0177e39 100644 --- a/tensorflow/python/keras/layers/lstm_test.py +++ b/tensorflow/python/keras/layers/lstm_test.py @@ -23,7 +23,6 @@ import numpy as np from tensorflow.python import keras from tensorflow.python.eager import context -from tensorflow.python.framework import test_util from tensorflow.python.keras import keras_parameterized from tensorflow.python.keras import testing_utils from tensorflow.python.platform import test @@ -340,11 +339,6 @@ class LSTMLayerTest(keras_parameterized.TestCase): else: self.assertEqual(len(layer.get_losses_for(x)), 1) - -class LSTMLayerV1OnlyTest(test.TestCase): - - @test_util.run_v1_only('b/120941292') - @test_util.run_in_graph_and_eager_modes def test_statefulness_LSTM(self): num_samples = 2 timesteps = 3 @@ -363,7 +357,7 @@ class LSTMLayerV1OnlyTest(test.TestCase): units, return_sequences=False, stateful=True, weights=None) model.add(layer) model.compile(optimizer=gradient_descent.GradientDescentOptimizer(0.01), - loss='mse') + loss='mse', run_eagerly=testing_utils.should_run_eagerly()) out1 = model.predict(np.ones((num_samples, timesteps))) self.assertEqual(out1.shape, (num_samples, units)) diff --git a/tensorflow/python/keras/layers/simplernn_test.py b/tensorflow/python/keras/layers/simplernn_test.py index 390ae789e1..0e599dade9 100644 --- a/tensorflow/python/keras/layers/simplernn_test.py +++ b/tensorflow/python/keras/layers/simplernn_test.py @@ -22,7 +22,6 @@ import numpy as np from tensorflow.python import keras from tensorflow.python.eager import context -from tensorflow.python.framework import test_util from tensorflow.python.keras import keras_parameterized from tensorflow.python.keras import testing_utils from tensorflow.python.platform import test @@ -141,11 +140,6 @@ class SimpleRNNLayerTest(keras_parameterized.TestCase): else: self.assertEqual(len(layer.get_losses_for(x)), 1) - -class SimpleRNNLayerV1OnlyTest(test.TestCase): - - @test_util.run_v1_only('b/120941292') - @test_util.run_in_graph_and_eager_modes def test_statefulness_SimpleRNN(self): num_samples = 2 timesteps = 3 @@ -164,7 +158,7 @@ class SimpleRNNLayerV1OnlyTest(test.TestCase): units, return_sequences=False, stateful=True, weights=None) model.add(layer) model.compile(optimizer=gradient_descent.GradientDescentOptimizer(0.01), - loss='mse') + loss='mse', run_eagerly=testing_utils.should_run_eagerly()) out1 = model.predict(np.ones((num_samples, timesteps))) self.assertEqual(out1.shape, (num_samples, units)) diff --git a/tensorflow/python/keras/layers/unified_gru_test.py b/tensorflow/python/keras/layers/unified_gru_test.py index 244ffdb8b6..11322764ac 100644 --- a/tensorflow/python/keras/layers/unified_gru_test.py +++ b/tensorflow/python/keras/layers/unified_gru_test.py @@ -402,39 +402,6 @@ class UnifiedGRUTest(keras_parameterized.TestCase): else: self.assertEqual(len(layer.get_losses_for(x)), 1) - -class GRULayerGradientTapeTest(test.TestCase): - - @test_util.run_in_graph_and_eager_modes(config=_config) - def test_in_tape(self): - if not context.executing_eagerly(): - self.skipTest('bloo') - time_steps = 10 - embedding_size = 11 - gru_unit_size = 12 - - gru = keras.layers.UnifiedGRU(gru_unit_size, - return_sequences=True, - return_state=True, - recurrent_activation='sigmoid', - recurrent_initializer='glorot_uniform') - - x = random_ops.random_uniform([1, time_steps, embedding_size]) - y = random_ops.random_uniform([1, gru_unit_size]) - - with backprop.GradientTape() as tape: - hidden_state = array_ops.zeros([1, gru_unit_size], dtype=dtypes.float32) - _, state = gru(x, initial_state=hidden_state) - - loss = math_ops.reduce_mean(math_ops.square(state - y)) - - tape.gradient(loss, gru.variables) - - -class GRULayerV1OnlyTest(test.TestCase, parameterized.TestCase): - - @test_util.run_v1_only('b/120941292') - @test_util.run_in_graph_and_eager_modes(config=_config) def test_statefulness_GRU(self): num_samples = 2 timesteps = 3 @@ -452,7 +419,8 @@ class GRULayerV1OnlyTest(test.TestCase, parameterized.TestCase): layer = layer_class( units, return_sequences=False, stateful=True, weights=None) model.add(layer) - model.compile(optimizer='sgd', loss='mse') + model.compile(optimizer=gradient_descent.GradientDescentOptimizer(0.01), + loss='mse', run_eagerly=testing_utils.should_run_eagerly()) out1 = model.predict(np.ones((num_samples, timesteps))) self.assertEqual(out1.shape, (num_samples, units)) @@ -497,6 +465,34 @@ class GRULayerV1OnlyTest(test.TestCase, parameterized.TestCase): np.testing.assert_allclose(out7, out6, atol=1e-5) +class GRULayerGradientTapeTest(test.TestCase): + + @test_util.run_in_graph_and_eager_modes(config=_config) + def test_in_tape(self): + if not context.executing_eagerly(): + self.skipTest('bloo') + time_steps = 10 + embedding_size = 11 + gru_unit_size = 12 + + gru = keras.layers.UnifiedGRU(gru_unit_size, + return_sequences=True, + return_state=True, + recurrent_activation='sigmoid', + recurrent_initializer='glorot_uniform') + + x = random_ops.random_uniform([1, time_steps, embedding_size]) + y = random_ops.random_uniform([1, gru_unit_size]) + + with backprop.GradientTape() as tape: + hidden_state = array_ops.zeros([1, gru_unit_size], dtype=dtypes.float32) + _, state = gru(x, initial_state=hidden_state) + + loss = math_ops.reduce_mean(math_ops.square(state - y)) + + tape.gradient(loss, gru.variables) + + class GRULayerGraphOnlyTest(test.TestCase): # Need session for test diff --git a/tensorflow/python/keras/layers/unified_lstm_test.py b/tensorflow/python/keras/layers/unified_lstm_test.py index 811b8d128e..375894b166 100644 --- a/tensorflow/python/keras/layers/unified_lstm_test.py +++ b/tensorflow/python/keras/layers/unified_lstm_test.py @@ -571,6 +571,68 @@ class UnifiedLSTMTest(keras_parameterized.TestCase): else: self.assertEqual(len(layer.get_losses_for(x)), 1) + def test_statefulness_LSTM(self): + num_samples = 2 + timesteps = 3 + embedding_dim = 4 + units = 2 + layer_class = keras.layers.UnifiedLSTM + model = keras.models.Sequential() + model.add( + keras.layers.Embedding( + 4, + embedding_dim, + mask_zero=True, + input_length=timesteps, + batch_input_shape=(num_samples, timesteps))) + layer = layer_class( + units, return_sequences=False, stateful=True, weights=None) + model.add(layer) + model.compile(optimizer=gradient_descent.GradientDescentOptimizer(0.01), + loss='mse', run_eagerly=testing_utils.should_run_eagerly()) + out1 = model.predict(np.ones((num_samples, timesteps))) + self.assertEqual(out1.shape, (num_samples, units)) + + # train once so that the states change + model.train_on_batch( + np.ones((num_samples, timesteps)), np.ones((num_samples, units))) + out2 = model.predict(np.ones((num_samples, timesteps))) + + # if the state is not reset, output should be different + self.assertNotEqual(out1.max(), out2.max()) + + # check that output changes after states are reset + # (even though the model itself didn't change) + layer.reset_states() + out3 = model.predict(np.ones((num_samples, timesteps))) + self.assertNotEqual(out2.max(), out3.max()) + + # check that container-level reset_states() works + model.reset_states() + out4 = model.predict(np.ones((num_samples, timesteps))) + self.assertAllClose(out3, out4, atol=1e-5) + + # check that the call to `predict` updated the states + out5 = model.predict(np.ones((num_samples, timesteps))) + self.assertNotEqual(out4.max(), out5.max()) + + # Check masking + layer.reset_states() + + left_padded_input = np.ones((num_samples, timesteps)) + left_padded_input[0, :1] = 0 + left_padded_input[1, :2] = 0 + out6 = model.predict(left_padded_input) + + layer.reset_states() + + right_padded_input = np.ones((num_samples, timesteps)) + right_padded_input[0, -1:] = 0 + right_padded_input[1, -2:] = 0 + out7 = model.predict(right_padded_input) + + self.assertAllClose(out7, out6, atol=1e-5) + class LSTMLayerGraphOnlyTest(test.TestCase): @@ -697,69 +759,6 @@ class LSTMLayerV1OnlyTest(test.TestCase, parameterized.TestCase): }, input_shape=(num_samples, timesteps, embedding_dim)) - @test_util.run_in_graph_and_eager_modes(config=_config) - def test_statefulness_LSTM(self): - num_samples = 2 - timesteps = 3 - embedding_dim = 4 - units = 2 - layer_class = keras.layers.UnifiedLSTM - model = keras.models.Sequential() - model.add( - keras.layers.Embedding( - 4, - embedding_dim, - mask_zero=True, - input_length=timesteps, - batch_input_shape=(num_samples, timesteps))) - layer = layer_class( - units, return_sequences=False, stateful=True, weights=None) - model.add(layer) - model.compile( - optimizer=gradient_descent.GradientDescentOptimizer(0.01), loss='mse') - out1 = model.predict(np.ones((num_samples, timesteps))) - self.assertEqual(out1.shape, (num_samples, units)) - - # train once so that the states change - model.train_on_batch( - np.ones((num_samples, timesteps)), np.ones((num_samples, units))) - out2 = model.predict(np.ones((num_samples, timesteps))) - - # if the state is not reset, output should be different - self.assertNotEqual(out1.max(), out2.max()) - - # check that output changes after states are reset - # (even though the model itself didn't change) - layer.reset_states() - out3 = model.predict(np.ones((num_samples, timesteps))) - self.assertNotEqual(out2.max(), out3.max()) - - # check that container-level reset_states() works - model.reset_states() - out4 = model.predict(np.ones((num_samples, timesteps))) - self.assertAllClose(out3, out4, atol=1e-5) - - # check that the call to `predict` updated the states - out5 = model.predict(np.ones((num_samples, timesteps))) - self.assertNotEqual(out4.max(), out5.max()) - - # Check masking - layer.reset_states() - - left_padded_input = np.ones((num_samples, timesteps)) - left_padded_input[0, :1] = 0 - left_padded_input[1, :2] = 0 - out6 = model.predict(left_padded_input) - - layer.reset_states() - - right_padded_input = np.ones((num_samples, timesteps)) - right_padded_input[0, -1:] = 0 - right_padded_input[1, -2:] = 0 - out7 = model.predict(right_padded_input) - - self.assertAllClose(out7, out6, atol=1e-5) - class UnifiedLSTMPerformanceTest(test.Benchmark): -- GitLab From 563ff22e7f6c9431072d2d9af6e876b3b4843920 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Wed, 2 Jan 2019 17:06:04 -0800 Subject: [PATCH 275/622] Fix bug where sample weights were being ignored in the tf.distribute.Strategy / numpy input case. PiperOrigin-RevId: 227606473 --- tensorflow/python/keras/engine/training.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tensorflow/python/keras/engine/training.py b/tensorflow/python/keras/engine/training.py index 1dc3b81cfc..8263467c37 100644 --- a/tensorflow/python/keras/engine/training.py +++ b/tensorflow/python/keras/engine/training.py @@ -2191,7 +2191,6 @@ class Model(Network): else: x = dataset_ops.Dataset.from_tensor_slices((var_x, var_y)) - x = dataset_ops.Dataset.from_tensor_slices((var_x, var_y)) if shuffle: # 1024 is a good buffer size since it is much larger than the average # batch size provided by the user and provides sufficient randomness. -- GitLab From b38e065943271461bd9afce7ae8f1bd00c3d6bf9 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Wed, 2 Jan 2019 17:34:44 -0800 Subject: [PATCH 276/622] use packages@tensorflow.org as author email for python package PiperOrigin-RevId: 227609414 --- tensorflow/lite/tools/pip_package/setup.py | 2 +- tensorflow/tools/pip_package/setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tensorflow/lite/tools/pip_package/setup.py b/tensorflow/lite/tools/pip_package/setup.py index 64d62ee1f2..82f9ac5b8f 100644 --- a/tensorflow/lite/tools/pip_package/setup.py +++ b/tensorflow/lite/tools/pip_package/setup.py @@ -136,7 +136,7 @@ setup( long_description='\n'.join(DOCLINES[2:]), url='https://www.tensorflow.org/lite/', author='Google Inc.', - author_email='opensource@google.com', + author_email='packages@tensorflow.org', license='Apache 2.0', include_package_data=True, keywords='tflite tensorflow tensor machine learning', diff --git a/tensorflow/tools/pip_package/setup.py b/tensorflow/tools/pip_package/setup.py index 3927540cc7..9567e0aff7 100644 --- a/tensorflow/tools/pip_package/setup.py +++ b/tensorflow/tools/pip_package/setup.py @@ -248,7 +248,7 @@ setup( url='https://www.tensorflow.org/', download_url='https://github.com/tensorflow/tensorflow/tags', author='Google Inc.', - author_email='opensource@google.com', + author_email='packages@tensorflow.org', # Contained modules and scripts. packages=find_packages(), entry_points={ -- GitLab From 894278658bf6da29831371670862688f46153be0 Mon Sep 17 00:00:00 2001 From: Pavithra Vijay Date: Wed, 2 Jan 2019 17:56:16 -0800 Subject: [PATCH 277/622] Add top k categorical accuracy and sparse top k categorical accuracy v2 metrics. PiperOrigin-RevId: 227611439 --- tensorflow/python/keras/metrics.py | 78 +++++++++++++++++++++++++ tensorflow/python/keras/metrics_test.py | 76 ++++++++++++++++++++++++ 2 files changed, 154 insertions(+) diff --git a/tensorflow/python/keras/metrics.py b/tensorflow/python/keras/metrics.py index 6b81151d5c..73c116a745 100644 --- a/tensorflow/python/keras/metrics.py +++ b/tensorflow/python/keras/metrics.py @@ -590,6 +590,84 @@ class SparseCategoricalAccuracy(MeanMetricWrapper): return super(SparseCategoricalAccuracy, cls).from_config(config) +class TopKCategoricalAccuracy(MeanMetricWrapper): + """Computes how often targets are in the top `K` predictions. + + Usage: + + ```python + m = tf.keras.metrics.TopKCategoricalAccuracy() + m.update_state([[0, 0, 1], [0, 1, 0]], [[0.1, 0.9, 0.8], [0.05, 0.95, 0]]) + print('Final result: ', m.result().numpy()) # Final result: 1.0 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile('sgd', metrics=[tf.keras.metrics.TopKCategoricalAccuracy()]) + ``` + """ + + def __init__(self, k=5, name='top_k_categorical_accuracy', dtype=None): + """Creates a `TopKCategoricalAccuracy` instance. + + Args: + k: (Optional) Number of top elements to look at for computing accuracy. + Defaults to 5. + name: (Optional) string name of the metric instance. + dtype: (Optional) data type of the metric result. + """ + super(TopKCategoricalAccuracy, self).__init__( + top_k_categorical_accuracy, name, dtype=dtype, k=k) + + @classmethod + def from_config(cls, config): + if 'fn' in config: + config.pop('fn') + return super(TopKCategoricalAccuracy, cls).from_config(config) + + +class SparseTopKCategoricalAccuracy(MeanMetricWrapper): + """Computes how often integer targets are in the top `K` predictions. + + Usage: + + ```python + m = tf.keras.metrics.SparseTopKCategoricalAccuracy() + m.update_state([2, 1], [[0.1, 0.9, 0.8], [0.05, 0.95, 0]]) + print('Final result: ', m.result().numpy()) # Final result: 1.0 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile( + 'sgd', + metrics=[tf.keras.metrics.SparseTopKCategoricalAccuracy()]) + ``` + """ + + def __init__(self, k=5, name='sparse_top_k_categorical_accuracy', dtype=None): + """Creates a `SparseTopKCategoricalAccuracy` instance. + + Args: + k: (Optional) Number of top elements to look at for computing accuracy. + Defaults to 5. + name: (Optional) string name of the metric instance. + dtype: (Optional) data type of the metric result. + """ + super(SparseTopKCategoricalAccuracy, self).__init__( + sparse_top_k_categorical_accuracy, name, dtype=dtype, k=k) + + @classmethod + def from_config(cls, config): + if 'fn' in config: + config.pop('fn') + return super(SparseTopKCategoricalAccuracy, cls).from_config(config) + + class _ConfusionMatrixConditionCount(Metric): """Calculates the number of the given confusion matrix condition.""" diff --git a/tensorflow/python/keras/metrics_test.py b/tensorflow/python/keras/metrics_test.py index 8f8befd413..fb635e7055 100644 --- a/tensorflow/python/keras/metrics_test.py +++ b/tensorflow/python/keras/metrics_test.py @@ -1391,6 +1391,82 @@ class RootMeanSquaredErrorTest(test.TestCase): self.assertAllClose(math.sqrt(13), self.evaluate(result), atol=1e-3) +@test_util.run_all_in_graph_and_eager_modes +class TopKCategoricalAccuracyTest(test.TestCase): + + def test_config(self): + a_obj = metrics.TopKCategoricalAccuracy(name='topkca', dtype=dtypes.int32) + self.assertEqual(a_obj.name, 'topkca') + self.assertEqual(a_obj._dtype, dtypes.int32) + + a_obj2 = metrics.TopKCategoricalAccuracy.from_config(a_obj.get_config()) + self.assertEqual(a_obj2.name, 'topkca') + self.assertEqual(a_obj2._dtype, dtypes.int32) + + def test_correctness(self): + a_obj = metrics.TopKCategoricalAccuracy() + self.evaluate(variables.variables_initializer(a_obj.variables)) + y_true = constant_op.constant([[0, 0, 1], [0, 1, 0]]) + y_pred = constant_op.constant([[0.1, 0.9, 0.8], [0.05, 0.95, 0]]) + + result = a_obj(y_true, y_pred) + self.assertEqual(1, self.evaluate(result)) # both the samples match + + # With `k` < 5. + a_obj = metrics.TopKCategoricalAccuracy(k=1) + self.evaluate(variables.variables_initializer(a_obj.variables)) + result = a_obj(y_true, y_pred) + self.assertEqual(0.5, self.evaluate(result)) # only sample #2 matches + + # With `k` > 5. + y_true = constant_op.constant([[0, 0, 1, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0]]) + y_pred = constant_op.constant([[0.5, 0.9, 0.1, 0.7, 0.6, 0.5, 0.4], + [0.05, 0.95, 0, 0, 0, 0, 0]]) + a_obj = metrics.TopKCategoricalAccuracy(k=6) + self.evaluate(variables.variables_initializer(a_obj.variables)) + result = a_obj(y_true, y_pred) + self.assertEqual(0.5, self.evaluate(result)) # only 1 sample matches. + + +@test_util.run_all_in_graph_and_eager_modes +class SparseTopKCategoricalAccuracyTest(test.TestCase): + + def test_config(self): + a_obj = metrics.SparseTopKCategoricalAccuracy( + name='stopkca', dtype=dtypes.int32) + self.assertEqual(a_obj.name, 'stopkca') + self.assertEqual(a_obj._dtype, dtypes.int32) + + a_obj2 = metrics.SparseTopKCategoricalAccuracy.from_config( + a_obj.get_config()) + self.assertEqual(a_obj2.name, 'stopkca') + self.assertEqual(a_obj2._dtype, dtypes.int32) + + def test_correctness(self): + a_obj = metrics.SparseTopKCategoricalAccuracy() + self.evaluate(variables.variables_initializer(a_obj.variables)) + y_true = constant_op.constant([2, 1]) + y_pred = constant_op.constant([[0.1, 0.9, 0.8], [0.05, 0.95, 0]]) + + result = a_obj(y_true, y_pred) + self.assertEqual(1, self.evaluate(result)) # both the samples match + + # With `k` < 5. + a_obj = metrics.SparseTopKCategoricalAccuracy(k=1) + self.evaluate(variables.variables_initializer(a_obj.variables)) + result = a_obj(y_true, y_pred) + self.assertEqual(0.5, self.evaluate(result)) # only sample #2 matches + + # With `k` > 5. + y_pred = constant_op.constant([[0.5, 0.9, 0.1, 0.7, 0.6, 0.5, 0.4], + [0.05, 0.95, 0, 0, 0, 0, 0]]) + a_obj = metrics.SparseTopKCategoricalAccuracy(k=6) + self.evaluate(variables.variables_initializer(a_obj.variables)) + result = a_obj(y_true, y_pred) + self.assertEqual(0.5, self.evaluate(result)) # only 1 sample matches. + + def _get_model(compile_metrics): model_layers = [ layers.Dense(3, activation='relu', kernel_initializer='ones'), -- GitLab From a8bf983c60f3684220996cd518d2b86e31dd6e1e Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Wed, 2 Jan 2019 21:49:05 -0800 Subject: [PATCH 278/622] use packages@tensorflow.org as author email for python package PiperOrigin-RevId: 227630041 --- tensorflow/contrib/tpu/profiler/pip_package/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/contrib/tpu/profiler/pip_package/setup.py b/tensorflow/contrib/tpu/profiler/pip_package/setup.py index f27ae38e04..807cf26fe9 100644 --- a/tensorflow/contrib/tpu/profiler/pip_package/setup.py +++ b/tensorflow/contrib/tpu/profiler/pip_package/setup.py @@ -33,7 +33,7 @@ setup( long_description='Tools for capture TPU profile', url='https://www.tensorflow.org/tfrc/', author='Google Inc.', - author_email='opensource@google.com', + author_email='packages@tensorflow.org', packages=['cloud_tpu_profiler'], package_data={ 'cloud_tpu_profiler': ['data/*'], -- GitLab From 7e71784d707cee4e14689b55a59bb1e23ed1a3f5 Mon Sep 17 00:00:00 2001 From: Heungsub Lee Date: Thu, 3 Jan 2019 17:12:21 +0900 Subject: [PATCH 279/622] Fix quotation typo around "task" --- tensorflow/contrib/distribute/python/mirrored_strategy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/contrib/distribute/python/mirrored_strategy.py b/tensorflow/contrib/distribute/python/mirrored_strategy.py index db8fd98307..e635190b50 100644 --- a/tensorflow/contrib/distribute/python/mirrored_strategy.py +++ b/tensorflow/contrib/distribute/python/mirrored_strategy.py @@ -48,7 +48,7 @@ class MirroredStrategy(distribute_lib.DistributionStrategy): distributed environment. There are several important concepts for distributed TensorFlow, e.g. - `client`, `job`, 'task', `cluster`, `in-graph replication` and + `client`, `job`, `task`, `cluster`, `in-graph replication` and 'synchronous training' and they have already been defined in the [TensorFlow's documentation](https://www.tensorflow.org/deploy/distributed). The distribution strategy inherits these concepts as well and in addition to -- GitLab From df0bd2904e2ace75c53e0e7663a2f9cff23f261d Mon Sep 17 00:00:00 2001 From: Kay Zhu Date: Thu, 3 Jan 2019 00:30:28 -0800 Subject: [PATCH 280/622] [XLA] Support RNG in HloEvaluator. PiperOrigin-RevId: 227642508 --- .../xla/service/hlo_constant_folding.cc | 5 +- .../compiler/xla/service/hlo_evaluator.cc | 8 ++ .../compiler/xla/service/hlo_evaluator.h | 5 + .../xla/service/hlo_evaluator_typed_visitor.h | 99 ++++++++++++++++++- tensorflow/compiler/xla/tests/BUILD | 4 - tensorflow/compiler/xla/tests/prng_test.cc | 41 +++++++- 6 files changed, 153 insertions(+), 9 deletions(-) diff --git a/tensorflow/compiler/xla/service/hlo_constant_folding.cc b/tensorflow/compiler/xla/service/hlo_constant_folding.cc index c58d00ff50..799cc9fd91 100644 --- a/tensorflow/compiler/xla/service/hlo_constant_folding.cc +++ b/tensorflow/compiler/xla/service/hlo_constant_folding.cc @@ -52,7 +52,7 @@ StatusOr HloConstantFolding::Run(HloModule* module) { computation->root_instruction() != instruction) { continue; } - // Skip Constant, Parameter, Tuple, AfterAll operation. + // Skip Constant, Parameter, Tuple, AfterAll, Rng operations. // Tuple constants are not directly supported by any backends, hence // folding Tuple is not useful and would in fact be expanded back into // kTuple by Algebraic Simplifier. @@ -62,7 +62,8 @@ StatusOr HloConstantFolding::Run(HloModule* module) { if (instruction->opcode() == HloOpcode::kParameter || instruction->opcode() == HloOpcode::kConstant || instruction->opcode() == HloOpcode::kTuple || - instruction->opcode() == HloOpcode::kAfterAll) { + instruction->opcode() == HloOpcode::kAfterAll || + instruction->opcode() == HloOpcode::kRng) { continue; } diff --git a/tensorflow/compiler/xla/service/hlo_evaluator.cc b/tensorflow/compiler/xla/service/hlo_evaluator.cc index 4f5484293c..e897d09e1b 100644 --- a/tensorflow/compiler/xla/service/hlo_evaluator.cc +++ b/tensorflow/compiler/xla/service/hlo_evaluator.cc @@ -233,6 +233,14 @@ StatusOr HloEvaluator::Evaluate( for (const auto& literal_ptr : arg_literals) { arg_literals_.push_back(&*literal_ptr); } + if (computation.parent()->config().seed()) { + seed_ = computation.parent()->config().seed(); + } else { + std::random_device rd; + seed_ = rd(); + } + + engine_ = std::minstd_rand0(seed_); TF_RETURN_IF_ERROR(computation.Accept(this)); return GetEvaluatedLiteralFor(computation.root_instruction()).Clone(); diff --git a/tensorflow/compiler/xla/service/hlo_evaluator.h b/tensorflow/compiler/xla/service/hlo_evaluator.h index d8e3019576..4fc5d10aed 100644 --- a/tensorflow/compiler/xla/service/hlo_evaluator.h +++ b/tensorflow/compiler/xla/service/hlo_evaluator.h @@ -290,6 +290,11 @@ class HloEvaluator : public DfsHloVisitorWithDefault { // Max loop iterations to execute with no maximum if negative. int64 max_loop_iterations_; + // Module-level seed handle. + uint64 seed_; + // RNG engine. + std::minstd_rand0 engine_; + TF_DISALLOW_COPY_AND_ASSIGN(HloEvaluator); }; diff --git a/tensorflow/compiler/xla/service/hlo_evaluator_typed_visitor.h b/tensorflow/compiler/xla/service/hlo_evaluator_typed_visitor.h index b1bbf49d4a..cf3509d0aa 100644 --- a/tensorflow/compiler/xla/service/hlo_evaluator_typed_visitor.h +++ b/tensorflow/compiler/xla/service/hlo_evaluator_typed_visitor.h @@ -2653,7 +2653,7 @@ class HloEvaluatorTypedVisitor : public DfsHloVisitorWithDefault { template ::value>::type* = nullptr> Status HandleReducePrecision(HloInstruction* reduce_precision) { - return InvalidArgument("Double not supported for reduce precision"); + return InvalidArgument("Double is not supported for reduce precision"); } template < @@ -2719,6 +2719,103 @@ class HloEvaluatorTypedVisitor : public DfsHloVisitorWithDefault { return HandleIota(iota); } + template ::value || + std::is_floating_point::value)>::type* = nullptr> + Status HandleRng(HloInstruction* random) { + return UnsupportedTypeError(random); + } + template ::value)>::type* = nullptr> + Status HandleRng(HloInstruction* random) { + RandomDistribution distribution = random->random_distribution(); + const auto result_shape = random->shape(); + Literal result(result_shape); + + switch (distribution) { + case RNG_UNIFORM: { + const Literal& low = + parent_->GetEvaluatedLiteralFor(random->operand(0)); + const Literal& high = + parent_->GetEvaluatedLiteralFor(random->operand(1)); + + std::uniform_real_distribution generator( + low.Get({}), high.Get({})); + + TF_RETURN_IF_ERROR( + result.Populate([&](absl::Span /*indexes*/) { + return generator(parent_->engine_); + })); + break; + } + case RNG_NORMAL: { + const Literal& mean = + parent_->GetEvaluatedLiteralFor(random->operand(0)); + const Literal& stddev = + parent_->GetEvaluatedLiteralFor(random->operand(1)); + + std::normal_distribution generator(mean.Get({}), + stddev.Get({})); + + TF_RETURN_IF_ERROR( + result.Populate([&](absl::Span /*indexes*/) { + return generator(parent_->engine_); + })); + break; + } + default: + return UnimplementedStrCat("The distribution ", + RandomDistribution_Name(distribution), + " is not implemented."); + } + parent_->evaluated_[random] = std::move(result); + return Status::OK(); + } + template ::value)>::type* = + nullptr> + Status HandleRng(HloInstruction* random) { + RandomDistribution distribution = random->random_distribution(); + const auto result_shape = random->shape(); + Literal result(result_shape); + + switch (distribution) { + case RNG_UNIFORM: { + const Literal& low = + parent_->GetEvaluatedLiteralFor(random->operand(0)); + const Literal& high = + parent_->GetEvaluatedLiteralFor(random->operand(1)); + + // Note std::uniform_int_distribution assumes interval is closed, i.e., + // [low, high], but we want [low, high) instead. Hence high-1 is used as + // the upper range. + std::uniform_int_distribution generator( + low.Get({}), high.Get({}) - 1); + + TF_RETURN_IF_ERROR( + result.Populate([&](absl::Span /*indexes*/) { + return static_cast(generator(parent_->engine_)); + })); + break; + } + case RNG_NORMAL: { + return Unimplemented( + "Normal distribution is not supported for integral types."); + } + default: + return UnimplementedStrCat("The distribution ", + RandomDistribution_Name(distribution), + " is not implemented."); + } + parent_->evaluated_[random] = std::move(result); + return Status::OK(); + } + Status HandleRng(HloInstruction* random) override { + return HandleRng(random); + } + private: // Creates a vector of multipliers which can be used to create a linear index // into shape. diff --git a/tensorflow/compiler/xla/tests/BUILD b/tensorflow/compiler/xla/tests/BUILD index 578e716b59..2c11ba4283 100644 --- a/tensorflow/compiler/xla/tests/BUILD +++ b/tensorflow/compiler/xla/tests/BUILD @@ -1422,10 +1422,6 @@ xla_test( xla_test( name = "prng_test", srcs = ["prng_test.cc"], - blacklisted_backends = [ - # TODO(b/122047800) support RNGs on the interpreter backend. - "interpreter", - ], deps = [ "//tensorflow/compiler/xla:literal", "//tensorflow/compiler/xla:shape_util", diff --git a/tensorflow/compiler/xla/tests/prng_test.cc b/tensorflow/compiler/xla/tests/prng_test.cc index 8f2c26f0ee..e49bcf26bd 100644 --- a/tensorflow/compiler/xla/tests/prng_test.cc +++ b/tensorflow/compiler/xla/tests/prng_test.cc @@ -80,7 +80,9 @@ XLA_TEST_F(PrngTest, LargeU01) { UniformTest(0, 1, {0x100, 0x100}); } XLA_TEST_F(PrngTest, TwelveValuesU524) { UniformTest(5, 24, {12}); } // TODO(b/71543667): Fix Rng ops on LLVM backends. -XLA_TEST_F(PrngTest, DISABLED_ON_GPU(DISABLED_ON_CPU(ScalarBF16Tests))) { +// TODO(b/122047800): Interpreter does not support BF16 for RNG ops. +XLA_TEST_F(PrngTest, DISABLED_ON_INTERPRETER( + DISABLED_ON_GPU(DISABLED_ON_CPU(ScalarBF16Tests)))) { for (int64 seed = 0; seed < 100; ++seed) { // The largest negative number smaller than zero in bf16 that's not // denormalized. @@ -103,7 +105,9 @@ XLA_TEST_F(PrngTest, DISABLED_ON_GPU(DISABLED_ON_CPU(ScalarBF16Tests))) { } // TODO(b/71543667): Fix Rng ops on LLVM backends. -XLA_TEST_F(PrngTest, DISABLED_ON_GPU(DISABLED_ON_CPU(ScalarBF16CountTests))) { +// TODO(b/122047800): Interpreter does not support BF16 for RNG ops. +XLA_TEST_F(PrngTest, DISABLED_ON_INTERPRETER(DISABLED_ON_GPU( + DISABLED_ON_CPU(ScalarBF16CountTests)))) { // There are 3 BF16 values in the range of [32.25, 33): 32.25, 32.5, 32.75, // they should get similar counts. bfloat16 low = static_cast(32.25); @@ -276,6 +280,39 @@ XLA_TEST_F(PrngTest, PassInGlobalRngSeed) { EXPECT_FALSE(LiteralTestUtil::Equal(result5, result6)); } +// This test verifies that the two RNG instructions with the same parameters in +// the same HloComputation produces different values. +XLA_TEST_F(PrngTest, DifferentValuesForIdenticalRngNodesInSameComputation) { + // Build a U[0,1) computation. + auto build_computation = [this]() { + XlaBuilder builder(TestName()); + auto a = RngUniform(ConstantR0(&builder, 0), + ConstantR0(&builder, 100), + ShapeUtil::MakeShape(S32, {10})); + auto b = RngUniform(ConstantR0(&builder, 0), + ConstantR0(&builder, 100), + ShapeUtil::MakeShape(S32, {10})); + Tuple(&builder, {a, b}); + return builder.Build(); + }; + + ExecutionOptions execution_options = execution_options_; + execution_options.set_seed(42); + + Literal result_tuple; + { + TF_ASSERT_OK_AND_ASSIGN(auto computation, build_computation()); + TF_ASSERT_OK_AND_ASSIGN( + result_tuple, client_->ExecuteAndTransfer(computation, /*arguments=*/{}, + &execution_options)); + } + + auto results = result_tuple.DecomposeTuple(); + ASSERT_EQ(results.size(), 2); + + EXPECT_FALSE(LiteralTestUtil::Equal(results[0], results[1])); +} + XLA_TEST_F(PrngTest, TenValuesN01) { XlaBuilder builder(TestName()); RngNormal(ConstantR0(&builder, 0), ConstantR0(&builder, 1), -- GitLab From 37326d1497de6de3ea32a1b121f99b2e50ac2dbc Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Thu, 3 Jan 2019 01:02:58 -0800 Subject: [PATCH 281/622] compat: Update forward compatibility horizon to 2019-01-03 PiperOrigin-RevId: 227646086 --- tensorflow/python/compat/compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/python/compat/compat.py b/tensorflow/python/compat/compat.py index 949bb0dc0c..c60c42ee65 100644 --- a/tensorflow/python/compat/compat.py +++ b/tensorflow/python/compat/compat.py @@ -27,7 +27,7 @@ import datetime from tensorflow.python.util import tf_contextlib from tensorflow.python.util.tf_export import tf_export -_FORWARD_COMPATIBILITY_HORIZON = datetime.date(2019, 1, 2) +_FORWARD_COMPATIBILITY_HORIZON = datetime.date(2019, 1, 3) @tf_export("compat.forward_compatible") -- GitLab From 61fde9e1e3c5d995aa20f7bf2781ba60db5bf246 Mon Sep 17 00:00:00 2001 From: Heungsub Lee Date: Thu, 3 Jan 2019 18:26:13 +0900 Subject: [PATCH 282/622] Single quote to backtick for consistency --- tensorflow/contrib/distribute/python/mirrored_strategy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/contrib/distribute/python/mirrored_strategy.py b/tensorflow/contrib/distribute/python/mirrored_strategy.py index e635190b50..e3ab2bf19e 100644 --- a/tensorflow/contrib/distribute/python/mirrored_strategy.py +++ b/tensorflow/contrib/distribute/python/mirrored_strategy.py @@ -49,7 +49,7 @@ class MirroredStrategy(distribute_lib.DistributionStrategy): There are several important concepts for distributed TensorFlow, e.g. `client`, `job`, `task`, `cluster`, `in-graph replication` and - 'synchronous training' and they have already been defined in the + `synchronous training` and they have already been defined in the [TensorFlow's documentation](https://www.tensorflow.org/deploy/distributed). The distribution strategy inherits these concepts as well and in addition to that we also clarify several more concepts: -- GitLab From eb54349cb4274ab797917a9e0699decec0b9794c Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Thu, 3 Jan 2019 03:11:44 -0800 Subject: [PATCH 283/622] Remove NCCL from RBE dockers. NCCL is built from source now. PiperOrigin-RevId: 227661008 --- .../Dockerfile.rbe.cuda10.0-cudnn7-ubuntu14.04 | 9 --------- .../ci_build/Dockerfile.rbe.cuda9.0-cudnn7-ubuntu14.04 | 9 --------- tensorflow/tools/docker/Dockerfile.devel-gpu | 10 ---------- 3 files changed, 28 deletions(-) diff --git a/tensorflow/tools/ci_build/Dockerfile.rbe.cuda10.0-cudnn7-ubuntu14.04 b/tensorflow/tools/ci_build/Dockerfile.rbe.cuda10.0-cudnn7-ubuntu14.04 index 553fbb2860..d08d31d913 100644 --- a/tensorflow/tools/ci_build/Dockerfile.rbe.cuda10.0-cudnn7-ubuntu14.04 +++ b/tensorflow/tools/ci_build/Dockerfile.rbe.cuda10.0-cudnn7-ubuntu14.04 @@ -19,7 +19,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates ENV CUDA_VERSION 10.0.130 ENV CUDA_PKG_VERSION 10-0=$CUDA_VERSION-1 ENV CUDNN_VERSION 7.3.1.20 -ENV NCCL_VERSION 2.3.5 ENV TENSORRT_VERSION 5.0.2 ENV NVIDIA_DRIVER_CAPABILITIES compute,utility ENV NVIDIA_REQUIRE_CUDA "cuda>=10.0,driver>=410" @@ -48,26 +47,18 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libcudnn7=$CUDNN_VERSION-1+cuda10.0 \ libcudnn7=$CUDNN_VERSION-1+cuda10.0 \ libcudnn7-dev=$CUDNN_VERSION-1+cuda10.0 \ - libnccl2=$NCCL_VERSION-2+cuda10.0 \ - libnccl-dev=$NCCL_VERSION-2+cuda10.0 \ nvinfer-runtime-trt-repo-ubuntu1604-$TENSORRT_VERSION-ga-cuda10.0 && \ apt-get update && apt-get install -y --no-install-recommends \ libnvinfer5=$TENSORRT_VERSION-1+cuda10.0 \ libnvinfer-dev=$TENSORRT_VERSION-1+cuda10.0 && \ ln -s cuda-10.0 /usr/local/cuda && \ apt-mark hold libcudnn7 && \ - apt-mark hold libnccl2 && \ rm -rf /var/lib/apt/lists/* # TODO(b/110903506): Provide a link to the SONAME of libcuda.so. # https://github.com/NVIDIA/nvidia-docker/issues/775 RUN ln -s libcuda.so /usr/local/cuda/lib64/stubs/libcuda.so.1 -# TODO(klimek): Once the TODO in tensorflow's configure.py to correctly find -# libnccl is resolved, delete this block. -RUN ln -s /usr/lib/x86_64-linux-gnu/libnccl.so /usr/lib/libnccl.so \ - && ln -s /usr/lib/x86_64-linux-gnu/libnccl.so /usr/lib/libnccl.so.2 - # Install a newer version of libstdc++, as new clang versions do not work # with the stock ubuntu 14.04 libstdc++. RUN apt-get update && \ diff --git a/tensorflow/tools/ci_build/Dockerfile.rbe.cuda9.0-cudnn7-ubuntu14.04 b/tensorflow/tools/ci_build/Dockerfile.rbe.cuda9.0-cudnn7-ubuntu14.04 index 60a23e1edb..9d5d51447c 100644 --- a/tensorflow/tools/ci_build/Dockerfile.rbe.cuda9.0-cudnn7-ubuntu14.04 +++ b/tensorflow/tools/ci_build/Dockerfile.rbe.cuda9.0-cudnn7-ubuntu14.04 @@ -25,7 +25,6 @@ ENV PATH /usr/local/nvidia/bin:/usr/local/cuda/bin:${PATH} ENV NVIDIA_VISIBLE_DEVICES all ENV NVIDIA_DRIVER_CAPABILITIES compute,utility ENV NVIDIA_REQUIRE_CUDA "cuda>=9.0" -ENV NCCL_VERSION 2.2.13 ENV TENSORRT_VERSION 5.0.2 ENV CUDNN_VERSION 7.1.4.18 @@ -45,14 +44,12 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ cuda-cudart-$CUDA_PKG_VERSION \ cuda-libraries-$CUDA_PKG_VERSION \ cuda-cublas-9-0=9.0.176.4-1 \ - libnccl2=$NCCL_VERSION-1+cuda9.0 \ cuda-libraries-dev-$CUDA_PKG_VERSION \ cuda-nvml-dev-$CUDA_PKG_VERSION \ cuda-minimal-build-$CUDA_PKG_VERSION \ cuda-command-line-tools-$CUDA_PKG_VERSION \ cuda-core-9-0=9.0.176.3-1 \ cuda-cublas-dev-9-0=9.0.176.4-1 \ - libnccl-dev=$NCCL_VERSION-1+cuda9.0 \ libcudnn7-dev=$CUDNN_VERSION-1+cuda9.0 \ libcudnn7=$CUDNN_VERSION-1+cuda9.0 \ nvinfer-runtime-trt-repo-ubuntu1604-$TENSORRT_VERSION-ga-cuda9.0 && \ @@ -60,7 +57,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libnvinfer5=$TENSORRT_VERSION-1+cuda9.0 \ libnvinfer-dev=$TENSORRT_VERSION-1+cuda9.0 && \ ln -s cuda-9.0 /usr/local/cuda && \ - apt-mark hold libnccl2 && \ apt-mark hold libcudnn7 libcudnn7-dev && \ rm -rf /var/lib/apt/lists/* @@ -71,11 +67,6 @@ RUN echo "/usr/local/nvidia/lib" >> /etc/ld.so.conf.d/nvidia.conf && \ # https://github.com/NVIDIA/nvidia-docker/issues/775 RUN ln -s libcuda.so /usr/local/cuda/lib64/stubs/libcuda.so.1 -# TODO(klimek): Once the TODO in tensorflow's configure.py to correctly find -# libnccl is resolved, delete this block. -RUN ln -s /usr/lib/x86_64-linux-gnu/libnccl.so /usr/lib/libnccl.so \ - && ln -s /usr/lib/x86_64-linux-gnu/libnccl.so /usr/lib/libnccl.so.2 - # Install a newer version of libstdc++, as new clang versions do not work # with the stock ubuntu 14.04 libstdc++. RUN apt-get update && \ diff --git a/tensorflow/tools/docker/Dockerfile.devel-gpu b/tensorflow/tools/docker/Dockerfile.devel-gpu index 1ad359ddcc..e085ee7170 100644 --- a/tensorflow/tools/docker/Dockerfile.devel-gpu +++ b/tensorflow/tools/docker/Dockerfile.devel-gpu @@ -15,8 +15,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ git \ libcudnn7=7.2.1.38-1+cuda9.0 \ libcudnn7-dev=7.2.1.38-1+cuda9.0 \ - libnccl2=2.2.13-1+cuda9.0 \ - libnccl-dev=2.2.13-1+cuda9.0 \ libcurl3-dev \ libfreetype6-dev \ libhdf5-serial-dev \ @@ -41,11 +39,6 @@ RUN apt-get update && \ apt-get install libnvinfer4=4.1.2-1+cuda9.0 && \ apt-get install libnvinfer-dev=4.1.2-1+cuda9.0 -# Link NCCL libray and header where the build script expects them. -RUN mkdir /usr/local/cuda-9.0/lib && \ - ln -s /usr/lib/x86_64-linux-gnu/libnccl.so.2 /usr/local/cuda/lib/libnccl.so.2 && \ - ln -s /usr/include/nccl.h /usr/local/cuda/include/nccl.h - RUN curl -fSsL -O https://bootstrap.pypa.io/get-pip.py && \ python get-pip.py && \ rm get-pip.py @@ -111,9 +104,6 @@ ENV TF_CUDA_COMPUTE_CAPABILITIES=3.5,5.2,6.0,6.1,7.0 ENV TF_CUDA_VERSION=9.0 ENV TF_CUDNN_VERSION=7 -# NCCL 2.x -ENV TF_NCCL_VERSION=2 - RUN ln -s /usr/local/cuda/lib64/stubs/libcuda.so /usr/local/cuda/lib64/stubs/libcuda.so.1 && \ LD_LIBRARY_PATH=/usr/local/cuda/lib64/stubs:${LD_LIBRARY_PATH} \ tensorflow/tools/ci_build/builds/configured GPU \ -- GitLab From 31cf2b97f0f3b1a90d26a56016c4b984f62dea3e Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Thu, 3 Jan 2019 06:51:42 -0800 Subject: [PATCH 284/622] Add all unary operations to list of autograph-supported ops. PiperOrigin-RevId: 227681912 --- .../converters/logical_expressions.py | 44 ++++++------- .../converters/logical_expressions_test.py | 7 ++ .../python/autograph/operators/__init__.py | 2 + .../python/autograph/operators/logical.py | 65 +++++-------------- 4 files changed, 46 insertions(+), 72 deletions(-) diff --git a/tensorflow/python/autograph/converters/logical_expressions.py b/tensorflow/python/autograph/converters/logical_expressions.py index dfcaafdc9e..ea9740a22e 100644 --- a/tensorflow/python/autograph/converters/logical_expressions.py +++ b/tensorflow/python/autograph/converters/logical_expressions.py @@ -38,29 +38,29 @@ from tensorflow.python.autograph.pyct import templates SAFE_BOOLEAN_OPERAND = 'SAFE_BOOLEAN_OPERAND' +OP_MAPPING = { + gast.And: 'ag__.and_', + gast.Eq: 'ag__.eq', + gast.NotEq: 'ag__.not_eq', + gast.Lt: 'ag__.lt', + gast.LtE: 'ag__.lt_e', + gast.Gt: 'ag__.gt', + gast.GtE: 'ag__.gt_e', + gast.Is: 'ag__.is_', + gast.IsNot: 'ag__.is_not', + gast.In: 'ag__.in_', + gast.Not: 'ag__.not_', + gast.NotIn: 'ag__.not_in', + gast.Or: 'ag__.or_', + gast.UAdd: 'ag__.u_add', + gast.USub: 'ag__.u_sub', + gast.Invert: 'ag__.invert', +} + + class LogicalExpressionTransformer(converter.Base): """Converts logical expressions to corresponding TF calls.""" - def __init__(self, ctx): - super(LogicalExpressionTransformer, self).__init__(ctx) - # TODO(mdan): For completeness and consistency, overload everything. - self.op_mapping = { - gast.And: 'ag__.and_', - gast.Eq: 'ag__.eq', - gast.NotEq: 'ag__.not_eq', - gast.Lt: 'ag__.lt', - gast.LtE: 'ag__.lt_e', - gast.Gt: 'ag__.gt', - gast.GtE: 'ag__.gt_e', - gast.Is: 'ag__.is_', - gast.IsNot: 'ag__.is_not', - gast.In: 'ag__.in_', - gast.Not: 'ag__.not_', - gast.NotIn: 'ag__.not_in', - gast.Or: 'ag__.or_', - gast.USub: 'ag__.u_sub', - } - def _expect_simple_symbol(self, operand): if isinstance(operand, gast.Name): return @@ -74,11 +74,11 @@ class LogicalExpressionTransformer(converter.Base): def _has_matching_func(self, operator): op_type = type(operator) - return op_type in self.op_mapping + return op_type in OP_MAPPING def _matching_func(self, operator): op_type = type(operator) - return self.op_mapping[op_type] + return OP_MAPPING[op_type] def _as_function(self, func_name, args, args_as_lambda=False): if args_as_lambda: diff --git a/tensorflow/python/autograph/converters/logical_expressions_test.py b/tensorflow/python/autograph/converters/logical_expressions_test.py index 687412750e..67ccd1fb47 100644 --- a/tensorflow/python/autograph/converters/logical_expressions_test.py +++ b/tensorflow/python/autograph/converters/logical_expressions_test.py @@ -77,6 +77,13 @@ class LogicalExpressionTest(converter_testing.TestCase): with self.converted(test_fn, logical_expressions, {}) as result: self.assertTrue(result.test_fn('a', ('a',))) + def test_unary_ops(self): + def test_fn(a): + return ~a, -a, +a + + with self.converted(test_fn, logical_expressions, {}) as result: + self.assertEqual(result.test_fn(1), (-2, -1, 1)) + if __name__ == '__main__': test.main() diff --git a/tensorflow/python/autograph/operators/__init__.py b/tensorflow/python/autograph/operators/__init__.py index 7a580fe324..35f8028c29 100644 --- a/tensorflow/python/autograph/operators/__init__.py +++ b/tensorflow/python/autograph/operators/__init__.py @@ -52,6 +52,7 @@ from tensorflow.python.autograph.operators.logical import eq from tensorflow.python.autograph.operators.logical import gt from tensorflow.python.autograph.operators.logical import gt_e from tensorflow.python.autograph.operators.logical import in_ +from tensorflow.python.autograph.operators.logical import invert from tensorflow.python.autograph.operators.logical import is_ from tensorflow.python.autograph.operators.logical import is_not from tensorflow.python.autograph.operators.logical import lt @@ -60,6 +61,7 @@ from tensorflow.python.autograph.operators.logical import not_ from tensorflow.python.autograph.operators.logical import not_eq from tensorflow.python.autograph.operators.logical import not_in from tensorflow.python.autograph.operators.logical import or_ +from tensorflow.python.autograph.operators.logical import u_add from tensorflow.python.autograph.operators.logical import u_sub from tensorflow.python.autograph.operators.py_builtins import float_ from tensorflow.python.autograph.operators.py_builtins import int_ diff --git a/tensorflow/python/autograph/operators/logical.py b/tensorflow/python/autograph/operators/logical.py index 569db5b91b..dadb0daf1a 100644 --- a/tensorflow/python/autograph/operators/logical.py +++ b/tensorflow/python/autograph/operators/logical.py @@ -18,6 +18,8 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import operator + from tensorflow.python.framework import tensor_util from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import gen_math_ops @@ -35,7 +37,7 @@ def and_(a, b): a_val = a() if tensor_util.is_tensor(a_val): return _tf_lazy_and(a_val, b) - return _py_lazy_and(a_val, b) + return a_val and b() def _tf_lazy_and(cond, b): @@ -44,17 +46,12 @@ def _tf_lazy_and(cond, b): return control_flow_ops.cond(cond, b, lambda: cond) -def _py_lazy_and(cond, b): - """Lazy-eval equivalent of "and" in Python.""" - return cond and b() - - def or_(a, b): """Functional form of "or". Uses lazy evaluation semantics.""" a_val = a() if tensor_util.is_tensor(a_val): return _tf_lazy_or(a_val, b) - return _py_lazy_or(a_val, b) + return a_val or b() def _tf_lazy_or(cond, b): @@ -63,16 +60,11 @@ def _tf_lazy_or(cond, b): return control_flow_ops.cond(cond, lambda: cond, b) -def _py_lazy_or(cond, b): - """Lazy-eval equivalent of "or" in Python.""" - return cond or b() - - def eq(a, b): """Functional form of "equal".""" if tensor_util.is_tensor(a) or tensor_util.is_tensor(b): return _tf_equal(a, b) - return _py_equal(a, b) + return a == b def _tf_equal(a, b): @@ -80,11 +72,6 @@ def _tf_equal(a, b): return gen_math_ops.equal(a, b) -def _py_equal(a, b): - """Overload of "equal" that falls back to Python's default implementation.""" - return a == b - - def not_eq(a, b): """Functional form of "not-equal".""" return not_(eq(a, b)) @@ -92,25 +79,8 @@ def not_eq(a, b): # Default implementation for the remainings. - -def gt(a, b): - """Functional form of "less-than".""" - return a > b - - -def gt_e(a, b): - """Functional form of "less-than".""" - return a >= b - - -def is_(a, b): - """Functional form of "less-than".""" - return a is b - - -def is_not(a, b): - """Functional form of "less-than".""" - return a is not b +is_ = operator.is_ +is_not = operator.is_not def in_(a, b): @@ -119,21 +89,16 @@ def in_(a, b): return a in b -def lt(a, b): - """Functional form of "less-than".""" - return a < b - - -def lt_e(a, b): - """Functional form of "less-than".""" - return a <= b - - def not_in(a, b): """Functional form of "less-than".""" return a not in b +gt = operator.gt +gt_e = operator.ge +lt = operator.lt +lt_e = operator.le + -def u_sub(a): - """Functional form of "unary-sub".""" - return -a +u_add = operator.pos +u_sub = operator.neg +invert = operator.invert -- GitLab From dfad4e97b2d00df768e9e8df16179a6f7ceda43c Mon Sep 17 00:00:00 2001 From: James Keeling Date: Thu, 3 Jan 2019 09:08:14 -0800 Subject: [PATCH 285/622] Fix odd number of backticks in docstring. This was causing the documentation to render incorrectly. PiperOrigin-RevId: 227698008 --- tensorflow/python/ops/linalg/linear_operator_kronecker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/python/ops/linalg/linear_operator_kronecker.py b/tensorflow/python/ops/linalg/linear_operator_kronecker.py index f7e785caa5..005b9b429b 100644 --- a/tensorflow/python/ops/linalg/linear_operator_kronecker.py +++ b/tensorflow/python/ops/linalg/linear_operator_kronecker.py @@ -71,7 +71,7 @@ class LinearOperatorKronecker(linear_operator.LinearOperator): `op1 x op2 x .. opJ` (we omit parentheses as the Kronecker product is associative). - If `opj` has shape `batch_shape_j` + [M_j, N_j`, then the composed operator + If `opj` has shape `batch_shape_j + [M_j, N_j]`, then the composed operator will have shape equal to `broadcast_batch_shape + [prod M_j, prod N_j]`, where the product is over all operators. -- GitLab From d04b09d65653bb78ad3b14448bf9ef867d9041d4 Mon Sep 17 00:00:00 2001 From: Blake Hechtman Date: Thu, 3 Jan 2019 09:19:36 -0800 Subject: [PATCH 286/622] [XLA] Propagate the layout of transposes and reshapes that add one sized dimensions in a depth first order. PiperOrigin-RevId: 227699358 --- .../compiler/xla/service/layout_assignment.cc | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/tensorflow/compiler/xla/service/layout_assignment.cc b/tensorflow/compiler/xla/service/layout_assignment.cc index 1c4ac178f0..ebf7a88fae 100644 --- a/tensorflow/compiler/xla/service/layout_assignment.cc +++ b/tensorflow/compiler/xla/service/layout_assignment.cc @@ -1236,6 +1236,23 @@ Status LayoutAssignment::PropagateUseConstraintToDefs( }); } +namespace { +// A transpose or a reshape that only changes trivial dimensions have meaningful +// layouts that are valuable to propagate in a depthfirst manner to avoid +// unassinged layouts in the graph. +bool InstructionShouldPropagateDepthFirst(const HloInstruction& hlo) { + switch (hlo.opcode()) { + case HloOpcode::kReshape: + return std::get<0>(hlo.ReshapeMerelyInsertsOrDeletes1SizedDimensions()); + case HloOpcode::kTranspose: + return true; + default: + return false; + } +} + +} // namespace + Status LayoutAssignment::PropagateOperandConstraint( const OperandLayoutConstraint& operand_constraint, LayoutConstraints* constraints) { @@ -1370,7 +1387,7 @@ Status LayoutAssignment::PropagateOperandConstraint( TF_RETURN_IF_ERROR(constraints->SetBufferLayout( *layout, *buffer, /*mandatory=*/user->opcode() == HloOpcode::kReduce, - /*dfs=*/false)); + /*dfs=*/InstructionShouldPropagateDepthFirst(*user))); } } return Status::OK(); @@ -1420,11 +1437,9 @@ Status LayoutAssignment::PropagateBufferConstraintToOperands( ChooseOperandLayoutFromOutputLayout(buffer_constraint.layout(), instruction, operand_no); if (operand_layout != nullptr) { - // Do not propagate operand constraints of transposes and reshapes, it - // tends to create really bad layouts. TF_RETURN_IF_ERROR(constraints->SetArrayOperandLayout( *operand_layout, instruction, operand_no, /*mandatory=*/false, - /*dfs=*/false)); + /*dfs=*/InstructionShouldPropagateDepthFirst(*instruction))); } } else { VLOG(6) << "Operand already has a constraint " -- GitLab From a5b70b7cb46e18846ba8875a13da08e7444c5136 Mon Sep 17 00:00:00 2001 From: Allen Lavoie Date: Thu, 3 Jan 2019 09:20:12 -0800 Subject: [PATCH 287/622] Fix for an optimizer.get_config() exception when exporting a SavedModel Tests tf.saved_model.save() with Optimizerv2 instead of v1 PiperOrigin-RevId: 227699428 --- tensorflow/python/keras/backend.py | 5 +++++ tensorflow/python/saved_model/save_test.py | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/tensorflow/python/keras/backend.py b/tensorflow/python/keras/backend.py index 42d94e77a0..6693243a68 100644 --- a/tensorflow/python/keras/backend.py +++ b/tensorflow/python/keras/backend.py @@ -2794,6 +2794,11 @@ def get_value(x): """ if context.executing_eagerly(): return x.numpy() + elif not getattr(x, '_in_graph_mode', True): + # This is a variable which was created in an eager context, but is being + # evaluated from a Graph. + with context.eager_mode(): + return x.numpy() elif ops.inside_function(): raise RuntimeError('Cannot get value inside Tensorflow graph function.') return x.eval(session=get_session()) diff --git a/tensorflow/python/saved_model/save_test.py b/tensorflow/python/saved_model/save_test.py index e643ff3dbf..533b954190 100644 --- a/tensorflow/python/saved_model/save_test.py +++ b/tensorflow/python/saved_model/save_test.py @@ -41,7 +41,7 @@ from tensorflow.python.saved_model import loader from tensorflow.python.saved_model import save from tensorflow.python.saved_model import signature_constants from tensorflow.python.saved_model import tag_constants -from tensorflow.python.training import adam +from tensorflow.python.keras.optimizer_v2 import adam from tensorflow.python.training.checkpointable import tracking from tensorflow.python.training.checkpointable import util @@ -50,7 +50,7 @@ class _ModelWithOptimizer(util.Checkpoint): def __init__(self): self.dense = core.Dense(1) - self.optimizer = adam.AdamOptimizer(0.01) + self.optimizer = adam.Adam(0.01) @def_function.function( input_signature=(tensor_spec.TensorSpec([None, 2], dtypes.float32), @@ -401,7 +401,7 @@ class _ModelWithOptimizerUsingDefun(util.Checkpoint): def __init__(self): self.dense = core.Dense(1) - self.optimizer = adam.AdamOptimizer(0.01) + self.optimizer = adam.Adam(0.01) # Using defun due to control flow v2 cycles, b/121159261. def_function uses # conds to gate variable initialization and so triggers cond reference cycles, -- GitLab From 78acbc6ee7fa557504599d7847bcd96a9a977d40 Mon Sep 17 00:00:00 2001 From: Allen Lavoie Date: Thu, 3 Jan 2019 09:21:25 -0800 Subject: [PATCH 288/622] TFTS: Remove flaky assertions PiperOrigin-RevId: 227699591 --- tensorflow/contrib/timeseries/examples/predict_test.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tensorflow/contrib/timeseries/examples/predict_test.py b/tensorflow/contrib/timeseries/examples/predict_test.py index 678fd71cd8..b353f85cb5 100644 --- a/tensorflow/contrib/timeseries/examples/predict_test.py +++ b/tensorflow/contrib/timeseries/examples/predict_test.py @@ -43,10 +43,6 @@ class PeriodTrendExampleTest(test.TestCase): self.assertAllEqual([700], mean.shape) self.assertAllEqual([700], upper_limit.shape) self.assertAllEqual([700], lower_limit.shape) - # Check that variance hasn't blown up too much. This is a relatively good - # indication that training was successful. - self.assertLess(upper_limit[-1] - lower_limit[-1], - 1.5 * (upper_limit[0] - lower_limit[0])) def test_ar(self): (times, observed, all_times, mean, @@ -55,7 +51,6 @@ class PeriodTrendExampleTest(test.TestCase): self.assertAllEqual(all_times.shape, mean.shape) self.assertAllEqual(all_times.shape, upper_limit.shape) self.assertAllEqual(all_times.shape, lower_limit.shape) - self.assertLess((upper_limit - lower_limit).mean(), 4.) if __name__ == "__main__": -- GitLab From c2ec7217a8b122d6a561da5d7d96b94ede9b7684 Mon Sep 17 00:00:00 2001 From: Jianwei Xie Date: Thu, 3 Jan 2019 09:53:37 -0800 Subject: [PATCH 289/622] Add model saving in Resnet50 code. In addition, 1. Optimized the weights saving logic in Keras for TPU. 2. Fixed the bug in optimizer for TPU mirrored variable. PiperOrigin-RevId: 227704010 --- tensorflow/python/keras/engine/saving.py | 11 ++++++++++- tensorflow/python/keras/optimizer_v2/BUILD | 1 + tensorflow/python/keras/optimizer_v2/optimizer_v2.py | 4 +++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/tensorflow/python/keras/engine/saving.py b/tensorflow/python/keras/engine/saving.py index 91eba0acab..0604a389a9 100644 --- a/tensorflow/python/keras/engine/saving.py +++ b/tensorflow/python/keras/engine/saving.py @@ -25,6 +25,7 @@ import os import numpy as np from six.moves import zip # pylint: disable=redefined-builtin +from tensorflow.python.framework import ops from tensorflow.python.keras import backend as K from tensorflow.python.keras import optimizers from tensorflow.python.keras.utils import conv_utils @@ -736,9 +737,17 @@ def save_weights_to_hdf5_group(f, layers): f.attrs['backend'] = K.backend().encode('utf8') f.attrs['keras_version'] = str(keras_version).encode('utf8') + # On TPUs, modifying the graph between session.runs() triggers some expensive + # recompilation overhead. To avoid this, we build up the full set of tensors + # to save before fetching weights, thus only modifying the graph once. + layer_weights_dict = {} + for layer in layers: + layer_weights_dict[layer.name] = [ops.convert_to_tensor(w) + for w in layer.weights] + for layer in layers: g = f.create_group(layer.name) - symbolic_weights = layer.weights + symbolic_weights = layer_weights_dict[layer.name] weight_values = K.batch_get_value(symbolic_weights) weight_names = [] for i, (w, val) in enumerate(zip(symbolic_weights, weight_values)): diff --git a/tensorflow/python/keras/optimizer_v2/BUILD b/tensorflow/python/keras/optimizer_v2/BUILD index 05f6e5ac48..964cf5fcba 100644 --- a/tensorflow/python/keras/optimizer_v2/BUILD +++ b/tensorflow/python/keras/optimizer_v2/BUILD @@ -34,6 +34,7 @@ py_library( "//tensorflow/python:variable_scope", "//tensorflow/python:variables", "//tensorflow/python/distribute:reduce_util", + "//tensorflow/python/distribute:values", ], ) diff --git a/tensorflow/python/keras/optimizer_v2/optimizer_v2.py b/tensorflow/python/keras/optimizer_v2/optimizer_v2.py index b740b2f05b..98f87a41ae 100644 --- a/tensorflow/python/keras/optimizer_v2/optimizer_v2.py +++ b/tensorflow/python/keras/optimizer_v2/optimizer_v2.py @@ -28,6 +28,7 @@ import six from tensorflow.python.distribute import distribute_lib from tensorflow.python.distribute import distribution_strategy_context as distribute_ctx from tensorflow.python.distribute import reduce_util as ds_reduce_util +from tensorflow.python.distribute import values as distributed_values from tensorflow.python.eager import backprop from tensorflow.python.eager import context from tensorflow.python.framework import dtypes @@ -576,7 +577,8 @@ class OptimizerV2(checkpointable.CheckpointableBase): value = self._get_hyper(hyperparameter_name) if callable(value): return value() - if isinstance(value, (ops.Tensor, tf_variables.Variable)): + if isinstance(value, (ops.Tensor, tf_variables.Variable, + distributed_values.TPUMirroredVariable)): return backend.get_value(value) return value -- GitLab From 3e2408a52822439b04de0a0a070a200c8f15509b Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Thu, 3 Jan 2019 10:34:07 -0800 Subject: [PATCH 290/622] Modify NNAPI delegate SVDF test to use 0 bias. PiperOrigin-RevId: 227710985 --- tensorflow/lite/delegates/nnapi/nnapi_delegate_test.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tensorflow/lite/delegates/nnapi/nnapi_delegate_test.cc b/tensorflow/lite/delegates/nnapi/nnapi_delegate_test.cc index 2da14cc1de..5da052eb42 100644 --- a/tensorflow/lite/delegates/nnapi/nnapi_delegate_test.cc +++ b/tensorflow/lite/delegates/nnapi/nnapi_delegate_test.cc @@ -2003,7 +2003,9 @@ class BaseSVDFOpModel : public SingleOpModelWithNNAPI { input_ = AddInput(TensorType_FLOAT32); weights_feature_ = AddInput(weights_feature_type); weights_time_ = AddInput(weights_time_type); - bias_ = AddNullInput(); + // TODO(b/121383394) : figure out why optional bias causes TFLite segfault + // when using NNAPI delegate. + bias_ = AddInput(TensorType_FLOAT32); const int num_filters = units * rank; activation_state_ = AddInput( TensorData{TensorType_FLOAT32, {batches, memory_size * num_filters}}, @@ -2019,6 +2021,8 @@ class BaseSVDFOpModel : public SingleOpModelWithNNAPI { {units_}, // bias tensor {batches, memory_size * num_filters} // activation_state tensor }); + // TODO(b/121383394) : remove once the optional bias bug is fixed. + PopulateTensor(bias_, std::vector(units_)); } // Populates the weights_feature tensor. -- GitLab From 140f0a3cde3696ca5128322cffda178563c8d04f Mon Sep 17 00:00:00 2001 From: Sourabh Bajaj Date: Thu, 3 Jan 2019 10:35:02 -0800 Subject: [PATCH 291/622] Finish migration to the new scope based API for using distribution strategy. PiperOrigin-RevId: 227711127 --- tensorflow/contrib/distribute/README.md | 30 ++++++++----------- .../distribute/python/examples/keras_mnist.py | 19 ++++++------ 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/tensorflow/contrib/distribute/README.md b/tensorflow/contrib/distribute/README.md index 8a8dc159ad..dbcaf8185f 100644 --- a/tensorflow/contrib/distribute/README.md +++ b/tensorflow/contrib/distribute/README.md @@ -43,28 +43,19 @@ the workers. Let's see how to scale to multiple GPUs on one machine using `MirroredStrategy` with [tf.keras] (https://www.tensorflow.org/guide/keras). -Take a very simple model consisting of a single layer: +Let's define a simple input dataset for training this model. Note that currently we require using +[`tf.data.Dataset`](https://www.tensorflow.org/api_docs/python/tf/data/Dataset) +with `DistributionStrategy`. ```python import tensorflow as tf from tensorflow import keras -inputs = tf.keras.layers.Input(shape=(1,)) -predictions = tf.keras.layers.Dense(1)(inputs) -model = tf.keras.models.Model(inputs=inputs, outputs=predictions) -``` - -Let's also define a simple input dataset for training this model. Note that currently we require using -[`tf.data.Dataset`](https://www.tensorflow.org/api_docs/python/tf/data/Dataset) -with `DistributionStrategy`. - -```python features = tf.data.Dataset.from_tensors([1.]).repeat(10000).batch(10) labels = tf.data.Dataset.from_tensors([1.]).repeat(10000).batch(10) train_dataset = tf.data.Dataset.zip((features, labels)) ``` - To distribute this Keras model on multiple GPUs using `MirroredStrategy` we first instantiate a `MirroredStrategy` object. @@ -72,14 +63,17 @@ first instantiate a `MirroredStrategy` object. distribution = tf.contrib.distribute.MirroredStrategy() ``` -We then compile the Keras model and pass the `MirroredStrategy` object in the -`distribute` argument (apart from other usual arguments like `loss` and -`optimizer`). +Take a very simple model consisting of a single layer. We need to create and compile +the model under the distribution strategy scope. ```python -model.compile(loss='mean_squared_error', - optimizer=tf.train.GradientDescentOptimizer(learning_rate=0.2), - distribute=distribution) +with distribution.scope(): + inputs = tf.keras.layers.Input(shape=(1,)) + predictions = tf.keras.layers.Dense(1)(inputs) + model = tf.keras.models.Model(inputs=inputs, outputs=predictions) + + model.compile(loss='mean_squared_error', + optimizer=tf.train.GradientDescentOptimizer(learning_rate=0.2)) ``` To train the model we call Keras `fit` API using the input dataset that we diff --git a/tensorflow/contrib/distribute/python/examples/keras_mnist.py b/tensorflow/contrib/distribute/python/examples/keras_mnist.py index 60fda99664..1ce91ecaf2 100644 --- a/tensorflow/contrib/distribute/python/examples/keras_mnist.py +++ b/tensorflow/contrib/distribute/python/examples/keras_mnist.py @@ -109,22 +109,21 @@ def main(_): tf.enable_eager_execution() train_ds, eval_ds, input_shape = get_input_datasets() - model = get_model(input_shape) # Instantiate the MirroredStrategy object. If we don't specify `num_gpus` or # the `devices` argument then all the GPUs available on the machine are used. # TODO(priyag): Use `tf.distribute.MirroredStrategy` once available. strategy = mirrored_strategy.MirroredStrategy(['/gpu:0', '/cpu:0']) - optimizer = rmsprop.RMSProp(learning_rate=0.001) - - # Compile the model by passing the distribution strategy object to the - # `distribute` argument. `fit`, `evaluate` and `predict` will be distributed - # based on the strategy instantiated. - model.compile(loss=tf.keras.losses.categorical_crossentropy, - optimizer=optimizer, - metrics=['accuracy'], - distribute=strategy) + # Create and compile the model under Distribution strategy scope. + # `fit`, `evaluate` and `predict` will be distributed based on the strategy + # model was compiled with. + with strategy.scope(): + model = get_model(input_shape) + optimizer = rmsprop.RMSProp(learning_rate=0.001) + model.compile(loss=tf.keras.losses.categorical_crossentropy, + optimizer=optimizer, + metrics=['accuracy']) # Train the model with the train dataset. model.fit(x=train_ds, epochs=20, steps_per_epoch=468) -- GitLab From 64596a68235709e7f3246d0b120a4146521a998b Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Thu, 3 Jan 2019 10:37:35 -0800 Subject: [PATCH 292/622] No public changes. PiperOrigin-RevId: 227711548 --- tensorflow/lite/experimental/swift/BUILD | 100 +++++ tensorflow/lite/experimental/swift/LICENSE | 202 ++++++++++ tensorflow/lite/experimental/swift/README.md | 54 +++ .../swift/Sources/Interpreter.swift | 265 ++++++++++++++ .../swift/Sources/InterpreterError.swift | 99 +++++ .../swift/Sources/InterpreterOptions.swift | 29 ++ .../experimental/swift/Sources/Model.swift | 40 ++ .../Sources/QuantizationParameters.swift | 38 ++ .../experimental/swift/Sources/Tensor.swift | 138 +++++++ .../Configs/TensorFlowLite.tulsigen | 57 +++ .../project.tulsiconf | 14 + .../project.pbxproj | 345 ++++++++++++++++++ .../TensorFlowLiteApp/AppDelegate.swift | 24 ++ .../Array+TensorFlowLite.swift | 22 ++ .../AppIcon.appiconset/Contents.json | 98 +++++ .../Assets.xcassets/Contents.json | 6 + .../Base.lproj/LaunchScreen.storyboard | 44 +++ .../Base.lproj/Main.storyboard | 95 +++++ .../Data+TensorFlowLite.swift | 13 + .../TensorFlowLiteApp/Info.plist | 46 +++ .../TensorFlowLiteApp/ViewController.swift | 299 +++++++++++++++ .../swift/Tests/InterpreterOptionsTests.swift | 54 +++ .../swift/Tests/InterpreterTests.swift | 315 ++++++++++++++++ .../experimental/swift/Tests/ModelTests.swift | 59 +++ .../Tests/QuantizationParametersTests.swift | 43 +++ .../swift/Tests/TensorTests.swift | 83 +++++ .../tools/pip_package/pip_smoke_test.py | 36 +- 27 files changed, 2603 insertions(+), 15 deletions(-) create mode 100644 tensorflow/lite/experimental/swift/BUILD create mode 100644 tensorflow/lite/experimental/swift/LICENSE create mode 100644 tensorflow/lite/experimental/swift/README.md create mode 100644 tensorflow/lite/experimental/swift/Sources/Interpreter.swift create mode 100644 tensorflow/lite/experimental/swift/Sources/InterpreterError.swift create mode 100644 tensorflow/lite/experimental/swift/Sources/InterpreterOptions.swift create mode 100644 tensorflow/lite/experimental/swift/Sources/Model.swift create mode 100644 tensorflow/lite/experimental/swift/Sources/QuantizationParameters.swift create mode 100644 tensorflow/lite/experimental/swift/Sources/Tensor.swift create mode 100644 tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/Configs/TensorFlowLite.tulsigen create mode 100644 tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/project.tulsiconf create mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp.xcodeproj/project.pbxproj create mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/AppDelegate.swift create mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Array+TensorFlowLite.swift create mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/Contents.json create mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/LaunchScreen.storyboard create mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/Main.storyboard create mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Data+TensorFlowLite.swift create mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Info.plist create mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/ViewController.swift create mode 100644 tensorflow/lite/experimental/swift/Tests/InterpreterOptionsTests.swift create mode 100644 tensorflow/lite/experimental/swift/Tests/InterpreterTests.swift create mode 100644 tensorflow/lite/experimental/swift/Tests/ModelTests.swift create mode 100644 tensorflow/lite/experimental/swift/Tests/QuantizationParametersTests.swift create mode 100644 tensorflow/lite/experimental/swift/Tests/TensorTests.swift diff --git a/tensorflow/lite/experimental/swift/BUILD b/tensorflow/lite/experimental/swift/BUILD new file mode 100644 index 0000000000..3bd288a1e1 --- /dev/null +++ b/tensorflow/lite/experimental/swift/BUILD @@ -0,0 +1,100 @@ +# TensorFlow Lite for Swift. + +package(default_visibility = ["//visibility:private"]) + +licenses(["notice"]) # Apache 2.0 + +exports_files(["LICENSE"]) + +load("@build_bazel_rules_apple//apple:ios.bzl", "ios_application", "ios_unit_test") +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +MINIMUM_OS_VERSION = "9.0" + +SWIFT_COPTS = [ + "-wmo", +] + +swift_library( + name = "TensorFlowLite", + srcs = glob(["Sources/*.swift"]), + copts = SWIFT_COPTS, + module_name = "TensorFlowLite", + tags = ["manual"], + deps = [ + "//tensorflow/lite/experimental/c:c_api", + ], +) + +ios_unit_test( + name = "TensorFlowLiteTests", + size = "small", + minimum_os_version = MINIMUM_OS_VERSION, + tags = [ + "manual", + # DISABLED: Following sanitizer tests are not supported by iOS test targets. + "noasan", + "nomsan", + "notsan", + ], + deps = [":TensorFlowLiteTestsLib"], +) + +swift_library( + name = "TensorFlowLiteTestsLib", + testonly = 1, + srcs = glob(["Tests/*.swift"]), + copts = SWIFT_COPTS, + tags = ["manual"], + deps = [ + ":TensorFlowLite", + ":TestResources", + ], +) + +objc_library( + name = "TestResources", + resources = [ + "//tensorflow/lite:testdata/add.bin", + "//tensorflow/lite:testdata/add_quantized.bin", + "//tensorflow/lite:testdata/multi_add.bin", + ], + tags = ["manual"], +) + +ios_application( + name = "TensorFlowLiteApp", + app_icons = glob(["TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/AppIcon.appiconset/**"]), + bundle_id = "com.tensorflow.lite.swift.TensorFlowLite", + families = [ + "ipad", + "iphone", + ], + infoplists = ["TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Info.plist"], + launch_storyboard = "TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/LaunchScreen.storyboard", + minimum_os_version = MINIMUM_OS_VERSION, + sdk_frameworks = [ + "CoreGraphics", + ], + tags = ["manual"], + deps = [":TensorFlowLiteAppLib"], +) + +swift_library( + name = "TensorFlowLiteAppLib", + srcs = glob(["TestApps/TensorFlowLiteApp/TensorFlowLiteApp/*.swift"]), + tags = ["manual"], + deps = [ + ":TensorFlowLite", + ":TensorFlowLiteAppResources", + ], +) + +objc_library( + name = "TensorFlowLiteAppResources", + storyboards = glob([ + "TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/*.storyboard", + ]), + tags = ["manual"], + deps = [":TestResources"], +) diff --git a/tensorflow/lite/experimental/swift/LICENSE b/tensorflow/lite/experimental/swift/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/tensorflow/lite/experimental/swift/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/tensorflow/lite/experimental/swift/README.md b/tensorflow/lite/experimental/swift/README.md new file mode 100644 index 0000000000..deb16c1e91 --- /dev/null +++ b/tensorflow/lite/experimental/swift/README.md @@ -0,0 +1,54 @@ +# TensorFlow Lite for Swift + +[TensorFlow Lite](https://www.tensorflow.org/lite/) is TensorFlow's lightweight +solution for Swift developers. It enables low-latency inference of on-device +machine learning models with a small binary size and fast performance supporting +hardware acceleration. + +## Getting Started + +### Bazel + +In your `BUILD` file, add the `TensorFlowLite` dependency: + +```python +swift_library( + # ... + deps = [ + "//tensorflow/lite/swift:TensorFlowLite", + ], +) +``` + +In your Swift files, import the module: + +```swift +import TensorFlowLite +``` + +### Tulsi + +Open the `TensorFlowLite.tulsiproj` using the [TulsiApp](https://github.com/bazelbuild/tulsi) or by +running the [`generate_xcodeproj.sh`](https://github.com/bazelbuild/tulsi/blob/master/src/tools/generate_xcodeproj.sh) +script: + +```shell +generate_xcodeproj.sh --genconfig tensorflow/lite/swift/TensorFlowLite.tulsiproj:TensorFlowLite --outputfolder ~/path/to/generated/TensorFlowLite.xcodeproj +``` + +### CocoaPods + +Add the following to your `Podfile`: + +```ruby +use_frameworks! +pod 'TensorFlowLiteSwift' +``` + +Then, run `pod install`. + +In your Swift files, import the module: + +```swift +import TensorFlowLite +``` diff --git a/tensorflow/lite/experimental/swift/Sources/Interpreter.swift b/tensorflow/lite/experimental/swift/Sources/Interpreter.swift new file mode 100644 index 0000000000..a14b5966b1 --- /dev/null +++ b/tensorflow/lite/experimental/swift/Sources/Interpreter.swift @@ -0,0 +1,265 @@ +// Copyright 2018 Google Inc. 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. + +import Foundation +import TensorFlowLiteCAPI + +/// A TensorFlow Lite interpreter that performs inference from a given model. +public final class Interpreter { + + /// The `TFL_Interpreter` C pointer type represented as an `UnsafePointer`. + private typealias CInterpreter = OpaquePointer + + /// Total number of input tensors associated with the model. + public var inputTensorCount: Int { + return Int(TFL_InterpreterGetInputTensorCount(cInterpreter)) + } + + /// Total number of output tensors associated with the model. + public var outputTensorCount: Int { + return Int(TFL_InterpreterGetOutputTensorCount(cInterpreter)) + } + + /// The underlying `TFL_Interpreter` C pointer. + private var cInterpreter: CInterpreter? + + /// Creates a new model interpreter instance. + /// + /// - Parameters: + /// - modelPath: Local file path to a TensorFlow Lite model. + /// - options: Custom configurations for the interpreter. The default is `nil` indicating that + /// interpreter will determine the configuration options. + /// - Throws: An error if the model could not be loaded or the interpreter could not be created. + public init(modelPath: String, options: InterpreterOptions? = nil) throws { + guard let model = Model(filePath: modelPath) else { throw InterpreterError.failedToLoadModel } + + let cInterpreterOptions: OpaquePointer? = try options.map { options in + guard let cOptions = TFL_NewInterpreterOptions() else { + throw InterpreterError.failedToCreateInterpreter + } + if let threadCount = options.threadCount, threadCount > 0 { + TFL_InterpreterOptionsSetNumThreads(cOptions, Int32(threadCount)) + } + if options.isErrorLoggingEnabled { + TFL_InterpreterOptionsSetErrorReporter( + cOptions, + { (_, format, arguments) in + guard let cFormat = format, + let message = String(cFormat: cFormat, arguments: arguments) + else { + return + } + print(String(describing: InterpreterError.tensorFlowLiteError(message))) + }, + nil + ) + } + return cOptions + } + defer { TFL_DeleteInterpreterOptions(cInterpreterOptions) } + + guard let cInterpreter = TFL_NewInterpreter(model.cModel, cInterpreterOptions) else { + throw InterpreterError.failedToCreateInterpreter + } + self.cInterpreter = cInterpreter + } + + deinit { + TFL_DeleteInterpreter(cInterpreter) + } + + /// Invokes the interpreter to perform inference from the loaded graph. + /// + /// - Throws: An error if the model was not ready because tensors were not allocated. + public func invoke() throws { + guard TFL_InterpreterInvoke(cInterpreter) == kTfLiteOk else { + // TODO(b/117510052): Determine which error to throw. + throw InterpreterError.allocateTensorsRequired + } + } + + /// Returns the input tensor at the given index. + /// + /// - Parameters: + /// - index: The index for the input tensor. + /// - Throws: An error if the index is invalid or the tensors have not been allocated. + /// - Returns: The input tensor at the given index. + public func input(at index: Int) throws -> Tensor { + let maxIndex = inputTensorCount - 1 + guard case 0...maxIndex = index else { + throw InterpreterError.invalidTensorIndex(index: index, maxIndex: maxIndex) + } + guard let cTensor = TFL_InterpreterGetInputTensor(cInterpreter, Int32(index)), + let bytes = TFL_TensorData(cTensor), + let nameCString = TFL_TensorName(cTensor) + else { + throw InterpreterError.allocateTensorsRequired + } + guard let dataType = TensorDataType(type: TFL_TensorType(cTensor)) else { + throw InterpreterError.invalidTensorDataType + } + + let name = String(cString: nameCString) + let rank = TFL_TensorNumDims(cTensor) + let dimensions = (0.. Tensor { + let maxIndex = outputTensorCount - 1 + guard case 0...maxIndex = index else { + throw InterpreterError.invalidTensorIndex(index: index, maxIndex: maxIndex) + } + guard let cTensor = TFL_InterpreterGetOutputTensor(cInterpreter, Int32(index)), + let bytes = TFL_TensorData(cTensor), + let nameCString = TFL_TensorName(cTensor) + else { + // TODO(b/117510052): Determine which error to throw. + throw InterpreterError.invokeInterpreterRequired + } + guard let dataType = TensorDataType(type: TFL_TensorType(cTensor)) else { + throw InterpreterError.invalidTensorDataType + } + + let name = String(cString: nameCString) + let rank = TFL_TensorNumDims(cTensor) + let dimensions = (0.. Tensor { + let maxIndex = inputTensorCount - 1 + guard case 0...maxIndex = index else { + throw InterpreterError.invalidTensorIndex(index: index, maxIndex: maxIndex) + } + guard let cTensor = TFL_InterpreterGetInputTensor(cInterpreter, Int32(index)) else { + throw InterpreterError.allocateTensorsRequired + } + + let byteCount = TFL_TensorByteSize(cTensor) + guard data.count == byteCount else { + throw InterpreterError.invalidTensorDataCount(provided: data.count, required: byteCount) + } + + let status = data.withUnsafeBytes { TFL_TensorCopyFromBuffer(cTensor, $0, data.count) } + guard status == kTfLiteOk else { throw InterpreterError.failedToCopyDataToInputTensor } + return try input(at: index) + } + + /// Allocates memory for all input tensors based on their `TensorShape`s. + /// + /// - Note: This is a relatively expensive operation and should only be called after creating the + /// interpreter and/or resizing any input tensors. + /// - Throws: An error if memory could not be allocated for the input tensors. + public func allocateTensors() throws { + guard TFL_InterpreterAllocateTensors(cInterpreter) == kTfLiteOk else { + throw InterpreterError.failedToAllocateTensors + } + } +} + +// MARK: - Extensions + +extension String { + /// Returns a new `String` initialized by using the given format C array as a template into which + /// the remaining argument values are substituted according to the user’s default locale. + /// + /// - Note: Returns `nil` if a new `String` could not be constructed from the given values. + /// - Parameters: + /// - cFormat: The format C array as a template for substituting values. + /// - arguments: A C pointer to a `va_list` of arguments to substitute into `cFormat`. + init?(cFormat: UnsafePointer, arguments: CVaListPointer) { + var buffer: UnsafeMutablePointer? + guard vasprintf(&buffer, cFormat, arguments) != 0, let cString = buffer else { return nil } + self.init(validatingUTF8: cString) + } +} diff --git a/tensorflow/lite/experimental/swift/Sources/InterpreterError.swift b/tensorflow/lite/experimental/swift/Sources/InterpreterError.swift new file mode 100644 index 0000000000..5de58b997a --- /dev/null +++ b/tensorflow/lite/experimental/swift/Sources/InterpreterError.swift @@ -0,0 +1,99 @@ +// Copyright 2018 Google Inc. 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. + +import Foundation + +/// TensorFlow Lite interpreter errors. +public enum InterpreterError: Error { + case invalidTensorIndex(index: Int, maxIndex: Int) + case invalidTensorDataCount(provided: Int, required: Int) + case invalidTensorDataType + case failedToLoadModel + case failedToCreateInterpreter + case failedToResizeInputTensor(index: Int) + case failedToCopyDataToInputTensor + case failedToAllocateTensors + case allocateTensorsRequired + case invokeInterpreterRequired + case tensorFlowLiteError(String) +} + +// MARK: - Extensions + +extension InterpreterError: LocalizedError { + /// Localized description of the interpreter error. + public var errorDescription: String? { + switch self { + case .invalidTensorIndex(let index, let maxIndex): + return "Invalid tensor index \(index), max index is \(maxIndex)." + case .invalidTensorDataCount(let providedCount, let requiredCount): + return "Provided data count \(providedCount) must match the required count \(requiredCount)." + case .invalidTensorDataType: + return "Tensor data type is unsupported or could not be determined because of a model error." + case .failedToLoadModel: + return "Failed to load the given model." + case .failedToCreateInterpreter: + return "Failed to create the interpreter." + case .failedToResizeInputTensor(let index): + return "Failed to resize input tesnor at index \(index)." + case .failedToCopyDataToInputTensor: + return "Failed to copy data to input tensor." + case .failedToAllocateTensors: + return "Failed to allocate memory for input tensors." + case .allocateTensorsRequired: + return "Must call allocateTensors()." + case .invokeInterpreterRequired: + return "Must call invoke()." + case .tensorFlowLiteError(let message): + return "TensorFlow Lite Error: \(message)" + } + } +} + +extension InterpreterError: CustomStringConvertible { + /// Textual representation of the TensorFlow Lite interpreter error. + public var description: String { + return errorDescription ?? "Unknown error." + } +} + +#if swift(>=4.2) +extension InterpreterError: Equatable {} +#else +extension InterpreterError: Equatable { + public static func == (lhs: InterpreterError, rhs: InterpreterError) -> Bool { + switch (lhs, rhs) { + case (.invalidTensorDataType, .invalidTensorDataType), + (.failedToLoadModel, .failedToLoadModel), + (.failedToCreateInterpreter, .failedToCreateInterpreter), + (.failedToAllocateTensors, .failedToAllocateTensors), + (.allocateTensorsRequired, .allocateTensorsRequired), + (.invokeInterpreterRequired, .invokeInterpreterRequired): + return true + case (.invalidTensorIndex(let lhsIndex, let lhsMaxIndex), + .invalidTensorIndex(let rhsIndex, let rhsMaxIndex)): + return lhsIndex == rhsIndex && lhsMaxIndex == rhsMaxIndex + case (.invalidTensorDataCount(let lhsProvidedCount, let lhsRequiredCount), + .invalidTensorDataCount(let rhsProvidedCount, let rhsRequiredCount)): + return lhsProvidedCount == rhsProvidedCount && lhsRequiredCount == rhsRequiredCount + case (.failedToResizeInputTensor(let lhsIndex), .failedToResizeInputTensor(let rhsIndex)): + return lhsIndex == rhsIndex + case (.tensorFlowLiteError(let lhsMessage), .tensorFlowLiteError(let rhsMessage)): + return lhsMessage == rhsMessage + default: + return false + } + } +} +#endif // swift(>=4.2) diff --git a/tensorflow/lite/experimental/swift/Sources/InterpreterOptions.swift b/tensorflow/lite/experimental/swift/Sources/InterpreterOptions.swift new file mode 100644 index 0000000000..2365fd7ade --- /dev/null +++ b/tensorflow/lite/experimental/swift/Sources/InterpreterOptions.swift @@ -0,0 +1,29 @@ +// Copyright 2018 Google Inc. 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. + +import Foundation + +/// Custom configuration options for a TensorFlow Lite interpreter. +public struct InterpreterOptions: Equatable { + + /// Maximum number of CPU threads that the interpreter should run on. Default is `nil` which + /// indicates that the `Interpreter` will decide the number of threads to use. + public var threadCount: Int? = nil + + /// Whether error logging to the console is enabled. The default is `false`. + public var isErrorLoggingEnabled = false + + /// Creates a new instance of interpreter options. + public init() {} +} diff --git a/tensorflow/lite/experimental/swift/Sources/Model.swift b/tensorflow/lite/experimental/swift/Sources/Model.swift new file mode 100644 index 0000000000..e8c49ff1ae --- /dev/null +++ b/tensorflow/lite/experimental/swift/Sources/Model.swift @@ -0,0 +1,40 @@ +// Copyright 2018 Google Inc. 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. + +import Foundation +import TensorFlowLiteCAPI + +/// A TensorFlow Lite model used by the 'Interpreter` to perform inference. +final class Model { + + /// The `TFL_Model` C pointer type represented as an `UnsafePointer`. + typealias CModel = OpaquePointer + + /// The underlying `TFL_Model` C pointer. + let cModel: CModel? + + /// Creates a new model instance. + /// + /// - Precondition: Initialization can fail if the given `filePath` is invalid. + /// - Parameters: + /// - filePath: Local file path to a TensorFlow Lite model. + init?(filePath: String) { + guard !filePath.isEmpty, let cModel = TFL_NewModelFromFile(filePath) else { return nil } + self.cModel = cModel + } + + deinit { + TFL_DeleteModel(cModel) + } +} diff --git a/tensorflow/lite/experimental/swift/Sources/QuantizationParameters.swift b/tensorflow/lite/experimental/swift/Sources/QuantizationParameters.swift new file mode 100644 index 0000000000..f367875644 --- /dev/null +++ b/tensorflow/lite/experimental/swift/Sources/QuantizationParameters.swift @@ -0,0 +1,38 @@ +// Copyright 2018 Google Inc. 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. + +import Foundation + +/// Parameters that determine the mapping of quantized values to real values. Quantized values can +/// be mapped to float values using the following conversion: +/// `realValue = scale * (quantizedValue - zeroPoint)`. +public struct QuantizationParameters { + + /// Difference between real values corresponding to consecutive quantized values differing by 1. + /// For example, the range of quantized values for `UInt8` data type is [0, 255]. + public let scale: Float + + /// Quantized value that corresponds to the real 0 value. + public let zeroPoint: Int + + /// Creates a new quantization parameters instance. + /// + /// - Parameters: + /// - scale: Scale value for asymmetric quantization. + /// - zeroPoint: Zero point for asymmetric quantization. + init(scale: Float, zeroPoint: Int) { + self.scale = scale + self.zeroPoint = zeroPoint + } +} diff --git a/tensorflow/lite/experimental/swift/Sources/Tensor.swift b/tensorflow/lite/experimental/swift/Sources/Tensor.swift new file mode 100644 index 0000000000..b738d87549 --- /dev/null +++ b/tensorflow/lite/experimental/swift/Sources/Tensor.swift @@ -0,0 +1,138 @@ +// Copyright 2018 Google Inc. 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. + +import Foundation +import TensorFlowLiteCAPI + +/// An input or output tensor in a TensorFlow Lite graph. +public struct Tensor { + + /// Name of the tensor. + public let name: String + + /// Data type of the tensor. + public let dataType: TensorDataType + + /// Shape of the tensor. + public let shape: TensorShape + + /// Data in the input or output tensor. + public let data: Data + + /// Quantization parameters for the tensor if using a quantized model. + public let quantizationParameters: QuantizationParameters? + + /// Creates a new input or output tensor instance. + /// + /// - Parameters: + /// - name: Name of the tensor. + /// - dataType: Data type of the tensor. + /// - data: Data in the input tensor. + /// - quantizationParameters Quantization parameters for the tensor if using a quantized model. + /// The default is `nil`. + init( + name: String, + dataType: TensorDataType, + shape: TensorShape, + data: Data, + quantizationParameters: QuantizationParameters? = nil + ) { + self.name = name + self.dataType = dataType + self.shape = shape + self.data = data + self.quantizationParameters = quantizationParameters + } +} + +/// Supported TensorFlow Lite tensor data types. +public enum TensorDataType: Equatable { + /// 32-bit single precision floating point tensor data type. + case float32 + /// 8-bit unsigned integer tensor data type. + case uInt8 + /// 16-bit signed integer tensor data type. + case int16 + /// 32-bit signed integer tensor data type. + case int32 + /// 64-bit signed integer tensor data type. + case int64 + /// Boolean tensor data type. + case bool + + /// Creates a new tensor data type from the given `TFL_Type` or `nil` if the data type is + /// unsupported or could not be determined because there was an error. + /// + /// - Parameter type: A data type supported by a tensor. + init?(type: TFL_Type) { + switch type { + case kTfLiteFloat32: + self = .float32 + case kTfLiteUInt8: + self = .uInt8 + case kTfLiteInt16: + self = .int16 + case kTfLiteInt32: + self = .int32 + case kTfLiteInt64: + self = .int64 + case kTfLiteBool: + self = .bool + case kTfLiteNoType: + fallthrough + default: + return nil + } + } +} + +/// The shape of a TensorFlow Lite tensor. +public struct TensorShape { + + /// The number of dimensions of the tensor. + public let rank: Int + + /// Array of dimensions for the tensor. + public let dimensions: [Int] + + /// Array of `Int32` dimensions for the tensor. + var int32Dimensions: [Int32] { return dimensions.map(Int32.init) } + + /// Creates a new tensor shape instance with the given array of dimensions. + /// + /// - Parameters: + /// - dimensions: Dimensions for the tensor. + public init(_ dimensions: [Int]) { + self.rank = dimensions.count + self.dimensions = dimensions + } + + /// Creates a new tensor shape instance with the given elements representing the dimensions. + /// + /// - Parameters: + /// - elements: Dimensions for the tensor. + public init(_ elements: Int...) { + self.init(elements) + } +} + +extension TensorShape: ExpressibleByArrayLiteral { + /// Creates a new tensor shape instance with the given array literal representing the dimensions. + /// + /// - Parameters: + /// - arrayLiteral: Dimensions for the tensor. + public init(arrayLiteral: Int...) { + self.init(arrayLiteral) + } +} diff --git a/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/Configs/TensorFlowLite.tulsigen b/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/Configs/TensorFlowLite.tulsigen new file mode 100644 index 0000000000..4010fab49e --- /dev/null +++ b/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/Configs/TensorFlowLite.tulsigen @@ -0,0 +1,57 @@ +{ + "sourceFilters" : [ + "third_party/tensorflow/lite/experimental/c", + "third_party/tensorflow/lite/experimental/swift", + "third_party/tensorflow/lite/experimental/swift/Sources", + "third_party/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp", + "third_party/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj", + "third_party/tensorflow/lite/experimental/swift/Tests", + ], + "buildTargets" : [ + "//third_party/tensorflow/lite/experimental/swift:TensorFlowLite", + "//third_party/tensorflow/lite/experimental/swift:TensorFlowLiteApp", + "//third_party/tensorflow/lite/experimental/swift:TensorFlowLiteTests", + ], + "projectName" : "TensorFlowLite", + "optionSet" : { + "LaunchActionPreActionScript" : { + "p" : "$(inherited)" + }, + "BazelBuildStartupOptionsRelease" : { + "p" : "$(inherited)" + }, + "BazelBuildOptionsRelease" : { + "p" : "$(inherited)" + }, + "BazelBuildOptionsDebug" : { + "p" : "$(inherited)" + }, + "EnvironmentVariables" : { + "p" : "$(inherited)" + }, + "BuildActionPreActionScript" : { + "p" : "$(inherited)" + }, + "CommandlineArguments" : { + "p" : "$(inherited)" + }, + "TestActionPreActionScript" : { + "p" : "$(inherited)" + }, + "BazelBuildStartupOptionsDebug" : { + "p" : "$(inherited)" + }, + "BuildActionPostActionScript" : { + "p" : "$(inherited)" + }, + "TestActionPostActionScript" : { + "p" : "$(inherited)" + }, + "LaunchActionPostActionScript" : { + "p" : "$(inherited)" + } + }, + "additionalFilePaths" : [ + "third_party/tensorflow/lite/experimental/swift/BUILD" + ] +} diff --git a/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/project.tulsiconf b/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/project.tulsiconf new file mode 100644 index 0000000000..14cff94453 --- /dev/null +++ b/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/project.tulsiconf @@ -0,0 +1,14 @@ +{ + "configDefaults" : { + "optionSet" : { + "ProjectPrioritizesSwift" : { + "p" : "YES" + } + } + }, + "projectName" : "TensorFlowLite", + "packages" : [ + "third_party/tensorflow/lite/experimental/swift" + ], + "workspaceRoot" : "../../../../../.." +} diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp.xcodeproj/project.pbxproj b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..fbbf9a1de2 --- /dev/null +++ b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp.xcodeproj/project.pbxproj @@ -0,0 +1,345 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 4A7304B421500B8400C90B21 /* Data+TensorFlowLite.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A7304B321500B8300C90B21 /* Data+TensorFlowLite.swift */; }; + 4AA72B732146ED64006C3AEF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AA72B722146ED64006C3AEF /* AppDelegate.swift */; }; + 4AA72B752146ED64006C3AEF /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AA72B742146ED64006C3AEF /* ViewController.swift */; }; + 4AA72B782146ED64006C3AEF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4AA72B762146ED64006C3AEF /* Main.storyboard */; }; + 4AA72B7A2146ED66006C3AEF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4AA72B792146ED66006C3AEF /* Assets.xcassets */; }; + 4AA72B7D2146ED66006C3AEF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4AA72B7B2146ED66006C3AEF /* LaunchScreen.storyboard */; }; + 4ADDE0CE2176600E00FF07A2 /* Array+TensorFlowLite.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ADDE0CD2176600900FF07A2 /* Array+TensorFlowLite.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 4A7304B321500B8300C90B21 /* Data+TensorFlowLite.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+TensorFlowLite.swift"; sourceTree = ""; }; + 4AA72B6F2146ED64006C3AEF /* TensorFlowLiteApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TensorFlowLiteApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 4AA72B722146ED64006C3AEF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 4AA72B742146ED64006C3AEF /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 4AA72B772146ED64006C3AEF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 4AA72B792146ED66006C3AEF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 4AA72B7C2146ED66006C3AEF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 4AA72B7E2146ED66006C3AEF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 4ADDE0CD2176600900FF07A2 /* Array+TensorFlowLite.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+TensorFlowLite.swift"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 4AA72B6C2146ED64006C3AEF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 4AA72B662146ED64006C3AEF = { + isa = PBXGroup; + children = ( + 4AA72B712146ED64006C3AEF /* TensorFlowLiteApp */, + 4AA72B702146ED64006C3AEF /* Products */, + ); + sourceTree = ""; + }; + 4AA72B702146ED64006C3AEF /* Products */ = { + isa = PBXGroup; + children = ( + 4AA72B6F2146ED64006C3AEF /* TensorFlowLiteApp.app */, + ); + name = Products; + sourceTree = ""; + }; + 4AA72B712146ED64006C3AEF /* TensorFlowLiteApp */ = { + isa = PBXGroup; + children = ( + 4AA72B722146ED64006C3AEF /* AppDelegate.swift */, + 4ADDE0CD2176600900FF07A2 /* Array+TensorFlowLite.swift */, + 4A7304B321500B8300C90B21 /* Data+TensorFlowLite.swift */, + 4AA72B742146ED64006C3AEF /* ViewController.swift */, + 4AA72B762146ED64006C3AEF /* Main.storyboard */, + 4AA72B792146ED66006C3AEF /* Assets.xcassets */, + 4AA72B7B2146ED66006C3AEF /* LaunchScreen.storyboard */, + 4AA72B7E2146ED66006C3AEF /* Info.plist */, + ); + path = TensorFlowLiteApp; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 4AA72B6E2146ED64006C3AEF /* TensorFlowLiteApp */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4AA72B812146ED66006C3AEF /* Build configuration list for PBXNativeTarget "TensorFlowLiteApp" */; + buildPhases = ( + 4AA72B6B2146ED64006C3AEF /* Sources */, + 4AA72B6C2146ED64006C3AEF /* Frameworks */, + 4AA72B6D2146ED64006C3AEF /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = TensorFlowLiteApp; + productName = TensorFlowLiteApp; + productReference = 4AA72B6F2146ED64006C3AEF /* TensorFlowLiteApp.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 4AA72B672146ED64006C3AEF /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0940; + LastUpgradeCheck = 0940; + ORGANIZATIONNAME = Google; + TargetAttributes = { + 4AA72B6E2146ED64006C3AEF = { + CreatedOnToolsVersion = 9.4.1; + }; + }; + }; + buildConfigurationList = 4AA72B6A2146ED64006C3AEF /* Build configuration list for PBXProject "TensorFlowLiteApp" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 4AA72B662146ED64006C3AEF; + productRefGroup = 4AA72B702146ED64006C3AEF /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 4AA72B6E2146ED64006C3AEF /* TensorFlowLiteApp */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 4AA72B6D2146ED64006C3AEF /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4AA72B7D2146ED66006C3AEF /* LaunchScreen.storyboard in Resources */, + 4AA72B7A2146ED66006C3AEF /* Assets.xcassets in Resources */, + 4AA72B782146ED64006C3AEF /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 4AA72B6B2146ED64006C3AEF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4AA72B732146ED64006C3AEF /* AppDelegate.swift in Sources */, + 4ADDE0CE2176600E00FF07A2 /* Array+TensorFlowLite.swift in Sources */, + 4A7304B421500B8400C90B21 /* Data+TensorFlowLite.swift in Sources */, + 4AA72B752146ED64006C3AEF /* ViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 4AA72B762146ED64006C3AEF /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 4AA72B772146ED64006C3AEF /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 4AA72B7B2146ED66006C3AEF /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 4AA72B7C2146ED66006C3AEF /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 4AA72B7F2146ED66006C3AEF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.4; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 4AA72B802146ED66006C3AEF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.4; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 4AA72B822146ED66006C3AEF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = TensorFlowLiteApp/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.tensorflow.lite.swift.TensorFlowLite; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 4AA72B832146ED66006C3AEF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = TensorFlowLiteApp/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.tensorflow.lite.swift.TensorFlowLite; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 4AA72B6A2146ED64006C3AEF /* Build configuration list for PBXProject "TensorFlowLiteApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4AA72B7F2146ED66006C3AEF /* Debug */, + 4AA72B802146ED66006C3AEF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4AA72B812146ED66006C3AEF /* Build configuration list for PBXNativeTarget "TensorFlowLiteApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4AA72B822146ED66006C3AEF /* Debug */, + 4AA72B832146ED66006C3AEF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 4AA72B672146ED64006C3AEF /* Project object */; +} diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/AppDelegate.swift b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/AppDelegate.swift new file mode 100644 index 0000000000..ffa90a06ad --- /dev/null +++ b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/AppDelegate.swift @@ -0,0 +1,24 @@ +import UIKit + +@UIApplicationMain + +final class AppDelegate: UIResponder, UIApplicationDelegate { + + /// The main window of the app. + var window: UIWindow? + + func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil + ) -> Bool { + return true + } +} + +// MARK: - Extensions + +#if !swift(>=4.2) +extension UIApplication { + typealias LaunchOptionsKey = UIApplicationLaunchOptionsKey +} +#endif // !swift(>=4.2) diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Array+TensorFlowLite.swift b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Array+TensorFlowLite.swift new file mode 100644 index 0000000000..56df1ce659 --- /dev/null +++ b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Array+TensorFlowLite.swift @@ -0,0 +1,22 @@ +import Foundation + +extension Array { + /// Creates a new array from the bytes of the given unsafe data. + /// + /// - Warning: The array's `Element` type must be trivial in that it can be copied bit for bit + /// with no indirection or reference-counting operations; otherwise, copying the raw bytes in + /// the `unsafeData`'s buffer to a new array returns an unsafe copy. + /// - Note: Returns `nil` if `unsafeData.count` is not a multiple of + /// `MemoryLayout.stride`. + /// - Parameter unsafeData: The data containing the bytes to turn into an array. + init?(unsafeData: Data) { + guard unsafeData.count % MemoryLayout.stride == 0 else { return nil } + let elements = unsafeData.withUnsafeBytes { + UnsafeBufferPointer( + start: $0, + count: unsafeData.count / MemoryLayout.stride + ) + } + self.init(elements) + } +} diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..d8db8d65fd --- /dev/null +++ b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/Contents.json b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/Contents.json new file mode 100644 index 0000000000..da4a164c91 --- /dev/null +++ b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/LaunchScreen.storyboard b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000000..a07a1321be --- /dev/null +++ b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/Main.storyboard b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/Main.storyboard new file mode 100644 index 0000000000..d0e91d63c4 --- /dev/null +++ b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/Main.storyboard @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Data+TensorFlowLite.swift b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Data+TensorFlowLite.swift new file mode 100644 index 0000000000..bc8a70c848 --- /dev/null +++ b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Data+TensorFlowLite.swift @@ -0,0 +1,13 @@ +import Foundation + +extension Data { + /// Creates a new buffer by copying the buffer pointer of the given array. + /// + /// - Warning: The given array's element type `T` must be trivial in that it can be copied bit + /// for bit with no indirection or reference-counting operations; otherwise, reinterpreting + /// data from the resulting buffer has undefined behavior. + /// - Parameter array: An array with elements of type `T`. + init(copyingBufferOf array: [T]) { + self = array.withUnsafeBufferPointer(Data.init) + } +} diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Info.plist b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Info.plist new file mode 100644 index 0000000000..3ca3875f04 --- /dev/null +++ b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Info.plist @@ -0,0 +1,46 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 0.0.1 + LSRequiresIPhoneOS + + NSCameraUsageDescription + NSCameraUsageDescription + NSPhotoLibraryUsageDescription + Select a photo to detect objects in. + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + + + diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/ViewController.swift b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/ViewController.swift new file mode 100644 index 0000000000..73c74fd19c --- /dev/null +++ b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/ViewController.swift @@ -0,0 +1,299 @@ +import TensorFlowLite +import UIKit + +class ViewController: UIViewController { + + // MARK: - Properties + + /// TensorFlowLite interpreter object for performing inference from a given model. + private var interpreter: Interpreter? + + /// Serial dispatch queue for managing `Interpreter` calls. + private let interpreterQueue = DispatchQueue( + label: Constant.dispatchQueueLabel, + qos: .userInitiated + ) + + /// The currently selected model. + private var currentModel: Model { + guard let currentModel = Model(rawValue: modelControl.selectedSegmentIndex) else { + preconditionFailure("Invalid model for selected segment index.") + } + return currentModel + } + + /// A description of the current model. + private var modelDescription: String { + guard let interpreter = interpreter else { return "" } + let inputCount = interpreter.inputTensorCount + let outputCount = interpreter.outputTensorCount + let inputTensors = (0.. String = { + guard let results = [Float32](unsafeData: outputTensor.data) else { return "No results." } + return resultsText + results.description + } + self.updateResultsText(results()) + } catch let error { + self.updateResultsText( + "Failed to invoke the interpreter with error: \(error.localizedDescription)" + ) + return + } + } + } + + private func invokeAddQuantized() { + interpreterQueue.async { + guard let interpreter = self.interpreter else { + self.updateResultsText(Constant.nilInterpreterErrorMessage) + return + } + do { + try interpreter.resizeInput(at: 0, to: [2]) + try interpreter.allocateTensors() + let input: [UInt8] = [1, 3] + let resultsText = self.modelDescription + "\n\n" + + "Performing 2 add operations on quantized input \(input.description) equals: " + self.updateResultsText(resultsText) + let data = Data(input) + try interpreter.copy(data, toInputAt: 0) + try interpreter.invoke() + let outputTensor = try interpreter.output(at: 0) + let results: () -> String = { + guard let quantizationParameters = outputTensor.quantizationParameters else { + return "No results." + } + let quantizedResults = [UInt8](outputTensor.data) + let dequantizedResults = quantizedResults.map { + quantizationParameters.scale * Float(Int($0) - quantizationParameters.zeroPoint) + } + return resultsText + quantizedResults.description + + ", dequantized results: " + dequantizedResults.description + } + self.updateResultsText(results()) + } catch let error { + self.updateResultsText( + "Failed to invoke the interpreter with error: \(error.localizedDescription)" + ) + return + } + } + } + + private func invokeMultiAdd() { + interpreterQueue.async { + guard let interpreter = self.interpreter else { + self.updateResultsText(Constant.nilInterpreterErrorMessage) + return + } + do { + let shape = TensorShape(2) + try (0.. [Float32] in + let input = [Float32(index + 1), Float32(index + 2)] + let data = Data(copyingBufferOf: input) + try interpreter.copy(data, toInputAt: index) + return input + } + let resultsText = self.modelDescription + "\n\n" + + "Performing 3 add operations on inputs \(inputs.description) equals: " + self.updateResultsText(resultsText) + try interpreter.invoke() + let results = try (0.. [Float32] in + let tensor = try interpreter.output(at: index) + return [Float32](unsafeData: tensor.data) ?? [] + } + self.updateResultsText(resultsText + results.description) + } catch let error { + self.updateResultsText( + "Failed to invoke the interpreter with error: \(error.localizedDescription)" + ) + return + } + } + } + + private func updateResultsText(_ text: String? = nil) { + safeDispatchOnMain { self.resultsTextView.text = text } + } +} + +// MARK: - Constants + +private enum Constant { + static let dispatchQueueLabel = "TensorFlowLiteInterpreterQueue" + static let nilInterpreterErrorMessage = + "Failed to invoke the interpreter because the interpreter was nil." +} + +/// Models that can be loaded by the TensorFlow Lite `Interpreter`. +private enum Model: Int, CustomStringConvertible { + /// A float model that performs two add operations on one input tensor and returns the result in + /// one output tensor. + case add = 0 + /// A quantized model that performs two add operations on one input tensor and returns the result + /// in one output tensor. + case addQuantized = 1 + /// A float model that performs three add operations on four input tensors and returns the results + /// in 2 output tensors. + case multiAdd = 2 + + var fileInfo: (name: String, extension: String) { + switch self { + case .add: + return Add.fileInfo + case .addQuantized: + return AddQuantized.fileInfo + case .multiAdd: + return MultiAdd.fileInfo + } + } + + // MARK: - CustomStringConvertible + + var description: String { + switch self { + case .add: + return Add.name + case .addQuantized: + return AddQuantized.name + case .multiAdd: + return MultiAdd.name + } + } +} + +/// Values for the `Add` model. +private enum Add { + static let name = "Add" + static let fileInfo = (name: "add", extension: "bin") +} + +/// Values for the `AddQuantized` model. +private enum AddQuantized { + static let name = "AddQuantized" + static let fileInfo = (name: "add_quantized", extension: "bin") +} + +/// Values for the `MultiAdd` model. +private enum MultiAdd { + static let name = "MultiAdd" + static let fileInfo = (name: "multi_add", extension: "bin") +} + +// MARK: - Fileprivate + +/// Safely dispatches the given block on the main queue. If the current thread is `main`, the block +/// is executed synchronously; otherwise, the block is executed asynchronously on the main thread. +fileprivate func safeDispatchOnMain(_ block: @escaping () -> Void) { + if Thread.isMainThread { block(); return } + DispatchQueue.main.async { block() } +} diff --git a/tensorflow/lite/experimental/swift/Tests/InterpreterOptionsTests.swift b/tensorflow/lite/experimental/swift/Tests/InterpreterOptionsTests.swift new file mode 100644 index 0000000000..54b4f59b28 --- /dev/null +++ b/tensorflow/lite/experimental/swift/Tests/InterpreterOptionsTests.swift @@ -0,0 +1,54 @@ +// Copyright 2018 Google Inc. 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. + +@testable import TensorFlowLite +import XCTest + +class InterpreterOptionsTests: XCTestCase { + + func testInterpreterOptions_InitWithDefaultValues() { + let options = InterpreterOptions() + XCTAssertNil(options.threadCount) + XCTAssertFalse(options.isErrorLoggingEnabled) + } + + func testInterpreterOptions_InitWithCustomValues() { + var options = InterpreterOptions() + options.threadCount = 2 + XCTAssertEqual(options.threadCount, 2) + options.isErrorLoggingEnabled = true + XCTAssertTrue(options.isErrorLoggingEnabled) + } + + func testInterpreterOptions_Equatable() { + var options1 = InterpreterOptions() + var options2 = InterpreterOptions() + XCTAssertEqual(options1, options2) + + options1.threadCount = 2 + options2.threadCount = 2 + XCTAssertEqual(options1, options2) + + options2.threadCount = 3 + XCTAssertNotEqual(options1, options2) + options2.threadCount = 2 + + options1.isErrorLoggingEnabled = true + options2.isErrorLoggingEnabled = true + XCTAssertEqual(options1, options2) + + options2.isErrorLoggingEnabled = false + XCTAssertNotEqual(options1, options2) + } +} diff --git a/tensorflow/lite/experimental/swift/Tests/InterpreterTests.swift b/tensorflow/lite/experimental/swift/Tests/InterpreterTests.swift new file mode 100644 index 0000000000..e98da5f951 --- /dev/null +++ b/tensorflow/lite/experimental/swift/Tests/InterpreterTests.swift @@ -0,0 +1,315 @@ +// Copyright 2018 Google Inc. 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. + +@testable import TensorFlowLite +import XCTest + +class InterpreterTests: XCTestCase { + + var interpreter: Interpreter! + + override func setUp() { + super.setUp() + + interpreter = try! Interpreter(modelPath: AddModel.path) + } + + override func tearDown() { + interpreter = nil + + super.tearDown() + } + + func testInterpreter_InitWithModelPath() { + XCTAssertNoThrow(try Interpreter(modelPath: AddModel.path)) + } + + func testInterpreter_Init_ThrowsFailedToLoadModel() { + XCTAssertThrowsError(try Interpreter(modelPath: "/invalid/path")) { error in + self.assertEqualErrors(actual: error, expected: .failedToLoadModel) + } + } + + func testInterpreter_InitWithModelPathAndOptions() { + var options = InterpreterOptions() + options.threadCount = 2 + XCTAssertNoThrow(try Interpreter(modelPath: AddModel.path, options: options)) + } + + func testInterpreter_InputTensorCount() { + XCTAssertEqual(interpreter.inputTensorCount, AddModel.inputTensorCount) + } + + func testInterpreter_OutputTensorCount() { + XCTAssertEqual(interpreter.outputTensorCount, AddModel.outputTensorCount) + } + + func testInterpreter_Invoke() throws { + try interpreter.allocateTensors() + XCTAssertNoThrow(try interpreter.invoke()) + } + + func testInterpreter_Invoke_ThrowsAllocateTensorsRequired_ModelNotReady() { + XCTAssertThrowsError(try interpreter.invoke()) { error in + self.assertEqualErrors(actual: error, expected: .allocateTensorsRequired) + } + } + + func testInterpreter_InputTensorAtIndex() throws { + try setUpAddModelInputTensor() + let inputTensor = try interpreter.input(at: AddModel.validIndex) + XCTAssertEqual(inputTensor, AddModel.inputTensor) + } + + func testInterpreter_InputTensorAtIndex_QuantizedModel() throws { + interpreter = try Interpreter(modelPath: AddQuantizedModel.path) + try setUpAddQuantizedModelInputTensor() + let inputTensor = try interpreter.input(at: AddQuantizedModel.inputOutputIndex) + XCTAssertEqual(inputTensor, AddQuantizedModel.inputTensor) + } + + func testInterpreter_InputTensorAtIndex_ThrowsInvalidIndex() throws { + try interpreter.allocateTensors() + XCTAssertThrowsError(try interpreter.input(at: AddModel.invalidIndex)) { error in + let maxIndex = AddModel.inputTensorCount - 1 + self.assertEqualErrors( + actual: error, + expected: .invalidTensorIndex(index: AddModel.invalidIndex, maxIndex: maxIndex) + ) + } + } + + func testInterpreter_InputTensorAtIndex_ThrowsAllocateTensorsRequired() { + XCTAssertThrowsError(try interpreter.input(at: AddModel.validIndex)) { error in + self.assertEqualErrors(actual: error, expected: .allocateTensorsRequired) + } + } + + func testInterpreter_OutputTensorAtIndex() throws { + try setUpAddModelInputTensor() + try interpreter.invoke() + let outputTensor = try interpreter.output(at: AddModel.validIndex) + XCTAssertEqual(outputTensor, AddModel.outputTensor) + let expectedResults = [Float32](unsafeData: outputTensor.data) + XCTAssertEqual(expectedResults, AddModel.results) + } + + func testInterpreter_OutputTensorAtIndex_QuantizedModel() throws { + interpreter = try Interpreter(modelPath: AddQuantizedModel.path) + try setUpAddQuantizedModelInputTensor() + try interpreter.invoke() + let outputTensor = try interpreter.output(at: AddQuantizedModel.inputOutputIndex) + XCTAssertEqual(outputTensor, AddQuantizedModel.outputTensor) + let expectedResults = [UInt8](outputTensor.data) + XCTAssertEqual(expectedResults, AddQuantizedModel.results) + } + + func testInterpreter_OutputTensorAtIndex_ThrowsInvalidIndex() throws { + try interpreter.allocateTensors() + try interpreter.invoke() + XCTAssertThrowsError(try interpreter.output(at: AddModel.invalidIndex)) { error in + let maxIndex = AddModel.outputTensorCount - 1 + self.assertEqualErrors( + actual: error, + expected: .invalidTensorIndex(index: AddModel.invalidIndex, maxIndex: maxIndex) + ) + } + } + + func testInterpreter_OutputTensorAtIndex_ThrowsInvokeInterpreterRequired() { + XCTAssertThrowsError(try interpreter.output(at: AddModel.validIndex)) { error in + self.assertEqualErrors(actual: error, expected: .invokeInterpreterRequired) + } + } + + func testInterpreter_ResizeInputTensorAtIndexToShape() { + XCTAssertNoThrow(try interpreter.resizeInput(at: AddModel.validIndex, to: [2, 2, 3])) + XCTAssertNoThrow(try interpreter.allocateTensors()) + } + + func testInterpreter_ResizeInputTensorAtIndexToShape_ThrowsInvalidIndex() { + XCTAssertThrowsError(try interpreter.resizeInput( + at: AddModel.invalidIndex, + to: [2, 2, 3] + )) { error in + let maxIndex = AddModel.inputTensorCount - 1 + self.assertEqualErrors( + actual: error, + expected: .invalidTensorIndex(index: AddModel.invalidIndex, maxIndex: maxIndex) + ) + } + } + + func testInterpreter_CopyDataToInputTensorAtIndex() throws { + try interpreter.resizeInput(at: AddModel.validIndex, to: AddModel.shape) + try interpreter.allocateTensors() + let inputTensor = try interpreter.copy(AddModel.inputData, toInputAt: AddModel.validIndex) + XCTAssertEqual(inputTensor.data, AddModel.inputData) + } + + func testInterpreter_CopyDataToInputTensorAtIndex_ThrowsInvalidIndex() { + XCTAssertThrowsError(try interpreter.copy( + AddModel.inputData, + toInputAt: AddModel.invalidIndex + )) { error in + let maxIndex = AddModel.inputTensorCount - 1 + self.assertEqualErrors( + actual: error, + expected: .invalidTensorIndex(index: AddModel.invalidIndex, maxIndex: maxIndex) + ) + } + } + + func testInterpreter_CopyDataToInputTensorAtIndex_ThrowsInvalidDataCount() throws { + try interpreter.resizeInput(at: AddModel.validIndex, to: AddModel.shape) + try interpreter.allocateTensors() + let invalidData = Data(count: AddModel.dataCount - 1) + XCTAssertThrowsError(try interpreter.copy( + invalidData, + toInputAt: AddModel.validIndex + )) { error in + self.assertEqualErrors( + actual: error, + expected: .invalidTensorDataCount(provided: invalidData.count, required: AddModel.dataCount) + ) + } + } + + func testInterpreter_AllocateTensors() { + XCTAssertNoThrow(try interpreter.allocateTensors()) + } + + // MARK: - Private + + private func setUpAddModelInputTensor() throws { + precondition(interpreter != nil) + try interpreter.resizeInput(at: AddModel.validIndex, to: AddModel.shape) + try interpreter.allocateTensors() + try interpreter.copy(AddModel.inputData, toInputAt: AddModel.validIndex) + } + + private func setUpAddQuantizedModelInputTensor() throws { + precondition(interpreter != nil) + try interpreter.resizeInput(at: AddQuantizedModel.inputOutputIndex, to: AddQuantizedModel.shape) + try interpreter.allocateTensors() + try interpreter.copy(AddQuantizedModel.inputData, toInputAt: AddQuantizedModel.inputOutputIndex) + } + + private func assertEqualErrors(actual: Error, expected: InterpreterError) { + guard let actual = actual as? InterpreterError else { + XCTFail("Actual error should be of type InterpreterError.") + return + } + XCTAssertEqual(actual, expected) + } +} + +// MARK: - Constants + +/// Values for the `add.bin` model. +private enum AddModel { + static let info = (name: "add", extension: "bin") + static let inputTensorCount = 1 + static let outputTensorCount = 1 + static let invalidIndex = 1 + static let validIndex = 0 + static let shape: TensorShape = [2] + static let dataCount = inputData.count + static let inputData = Data(copyingBufferOf: [Float32(1.0), Float32(3.0)]) + static let outputData = Data(copyingBufferOf: [Float32(3.0), Float32(9.0)]) + static let results = [Float32(3.0), Float32(9.0)] + + static let inputTensor = Tensor( + name: "input", + dataType: .float32, + shape: shape, + data: inputData + ) + static let outputTensor = Tensor( + name: "output", + dataType: .float32, + shape: shape, + data: outputData + ) + + static var path: String = { + let bundle = Bundle(for: InterpreterTests.self) + guard let path = bundle.path(forResource: info.name, ofType: info.extension) else { return "" } + return path + }() +} + +/// Values for the `add_quantized.bin` model. +private enum AddQuantizedModel { + static let info = (name: "add_quantized", extension: "bin") + static let inputOutputIndex = 0 + static let shape: TensorShape = [2] + static let inputData = Data([1, 3]) + static let outputData = Data([3, 9]) + static let quantizationParameters = QuantizationParameters(scale: 0.003922, zeroPoint: 0) + static let results: [UInt8] = [3, 9] + + static let inputTensor = Tensor( + name: "input", + dataType: .uInt8, + shape: shape, + data: inputData, + quantizationParameters: quantizationParameters + ) + static let outputTensor = Tensor( + name: "output", + dataType: .uInt8, + shape: shape, + data: outputData, + quantizationParameters: quantizationParameters + ) + + static var path: String = { + let bundle = Bundle(for: InterpreterTests.self) + guard let path = bundle.path(forResource: info.name, ofType: info.extension) else { return "" } + return path + }() +} + +// MARK: - Extensions + +extension Array { + /// Creates a new array from the bytes of the given unsafe data. + /// + /// - Note: Returns `nil` if `unsafeData.count` is not a multiple of + /// `MemoryLayout.stride`. + /// - Parameter unsafeData: The data containing the bytes to turn into an array. + init?(unsafeData: Data) { + guard unsafeData.count % MemoryLayout.stride == 0 else { return nil } + let elements = unsafeData.withUnsafeBytes { + UnsafeBufferPointer( + start: $0, + count: unsafeData.count / MemoryLayout.stride + ) + } + self.init(elements) + } +} + +extension Data { + /// Creates a new buffer by copying the buffer pointer of the given array. + /// + /// - Warning: The given array's element type `T` must be trivial in that it can be copied bit + /// for bit with no indirection or reference-counting operations; otherwise, reinterpreting + /// data from the resulting buffer has undefined behavior. + /// - Parameter array: An array with elements of type `T`. + init(copyingBufferOf array: [T]) { + self = array.withUnsafeBufferPointer(Data.init) + } +} diff --git a/tensorflow/lite/experimental/swift/Tests/ModelTests.swift b/tensorflow/lite/experimental/swift/Tests/ModelTests.swift new file mode 100644 index 0000000000..025db18906 --- /dev/null +++ b/tensorflow/lite/experimental/swift/Tests/ModelTests.swift @@ -0,0 +1,59 @@ +// Copyright 2018 Google Inc. 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. + +@testable import TensorFlowLite +import XCTest + +class ModelTests: XCTestCase { + + var modelPath: String! + + override func setUp() { + super.setUp() + + let bundle = Bundle(for: type(of: self)) + guard let modelPath = bundle.path( + forResource: Constant.modelInfo.name, + ofType: Constant.modelInfo.extension) + else { + XCTFail("Failed to get the model file path.") + return + } + self.modelPath = modelPath + } + + override func tearDown() { + modelPath = nil + + super.tearDown() + } + + func testModel_InitWithFilePath() { + XCTAssertNotNil(Model(filePath: modelPath)) + } + + func testModel_InitWithEmptyFilePath_FailsInitialization() { + XCTAssertNil(Model(filePath: "")) + } + + func testModel_InitWithInvalidFilePath_FailsInitialization() { + XCTAssertNil(Model(filePath: "invalid/path")) + } +} + +// MARK: - Constants + +private enum Constant { + static let modelInfo = (name: "add", extension: "bin") +} diff --git a/tensorflow/lite/experimental/swift/Tests/QuantizationParametersTests.swift b/tensorflow/lite/experimental/swift/Tests/QuantizationParametersTests.swift new file mode 100644 index 0000000000..65648c2698 --- /dev/null +++ b/tensorflow/lite/experimental/swift/Tests/QuantizationParametersTests.swift @@ -0,0 +1,43 @@ +// Copyright 2018 Google Inc. 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. + +@testable import TensorFlowLite +import XCTest + +class QuantizationParametersTests: XCTestCase { + + func testQuantizationParameters_InitWithCustomValues() { + let parameters = QuantizationParameters(scale: 0.5, zeroPoint: 1) + XCTAssertEqual(parameters.scale, 0.5) + XCTAssertEqual(parameters.zeroPoint, 1) + } + + func testQuantizationParameters_Equatable() { + let parameters1 = QuantizationParameters(scale: 0.5, zeroPoint: 1) + let parameters2 = QuantizationParameters(scale: 0.5, zeroPoint: 1) + XCTAssertEqual(parameters1, parameters2) + + let parameters3 = QuantizationParameters(scale: 0.4, zeroPoint: 1) + XCTAssertNotEqual(parameters1, parameters3) + XCTAssertNotEqual(parameters2, parameters3) + } +} + +// MARK: - Extensions + +extension QuantizationParameters: Equatable { + public static func == (lhs: QuantizationParameters, rhs: QuantizationParameters) -> Bool { + return lhs.scale == rhs.scale && lhs.zeroPoint == rhs.zeroPoint + } +} diff --git a/tensorflow/lite/experimental/swift/Tests/TensorTests.swift b/tensorflow/lite/experimental/swift/Tests/TensorTests.swift new file mode 100644 index 0000000000..4540043a16 --- /dev/null +++ b/tensorflow/lite/experimental/swift/Tests/TensorTests.swift @@ -0,0 +1,83 @@ +// Copyright 2018 Google Inc. 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. + +@testable import TensorFlowLite +import XCTest + +class TensorTests: XCTestCase { + + // MARK: - Tensor + + func testTensor_Init() { + let name = "InputTensor" + let dataType: TensorDataType = .uInt8 + let shape = TensorShape(Constant.dimensions) + guard let data = name.data(using: .utf8) else { XCTFail("Data should not be nil."); return } + let quantizationParameters = QuantizationParameters(scale: 0.5, zeroPoint: 1) + let inputTensor = Tensor( + name: name, + dataType: dataType, + shape: shape, + data: data, + quantizationParameters: quantizationParameters + ) + XCTAssertEqual(inputTensor.name, name) + XCTAssertEqual(inputTensor.dataType, dataType) + XCTAssertEqual(inputTensor.shape, shape) + XCTAssertEqual(inputTensor.data, data) + XCTAssertEqual(inputTensor.quantizationParameters, quantizationParameters) + } + + // MARK: - TensorShape + + func testTensorShape_InitWithArray() { + let shape = TensorShape(Constant.dimensions) + XCTAssertEqual(shape.rank, Constant.dimensions.count) + XCTAssertEqual(shape.dimensions, Constant.dimensions) + } + + func testTensorShape_InitWithElements() { + let shape = TensorShape(2, 2, 3) + XCTAssertEqual(shape.rank, Constant.dimensions.count) + XCTAssertEqual(shape.dimensions, Constant.dimensions) + } + + func testTensorShape_InitWithArrayLiteral() { + let shape: TensorShape = [2, 2, 3] + XCTAssertEqual(shape.rank, Constant.dimensions.count) + XCTAssertEqual(shape.dimensions, Constant.dimensions) + } +} + +// MARK: - Constants + +private enum Constant { + /// Array of 2 arrays of 2 arrays of 3 numbers: [[[1, 1, 1], [2, 2, 2]], [[3, 3, 3], [4, 4, 4]]]. + static let dimensions = [2, 2, 3] +} + +// MARK: - Extensions + +extension TensorShape: Equatable { + public static func == (lhs: TensorShape, rhs: TensorShape) -> Bool { + return lhs.rank == rhs.rank && lhs.dimensions == rhs.dimensions + } +} + +extension Tensor: Equatable { + public static func == (lhs: Tensor, rhs: Tensor) -> Bool { + return lhs.name == rhs.name && lhs.dataType == rhs.dataType && lhs.shape == rhs.shape && + lhs.data == rhs.data && lhs.quantizationParameters == rhs.quantizationParameters + } +} diff --git a/tensorflow/tools/pip_package/pip_smoke_test.py b/tensorflow/tools/pip_package/pip_smoke_test.py index 51d010c9e1..952c71c615 100644 --- a/tensorflow/tools/pip_package/pip_smoke_test.py +++ b/tensorflow/tools/pip_package/pip_smoke_test.py @@ -30,14 +30,19 @@ os.chdir(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../.."))) PIP_PACKAGE_QUERY_EXPRESSION = ( "deps(//tensorflow/tools/pip_package:build_pip_package)") +# List of file paths containing BUILD files that should not be included for the +# pip smoke test. +BUILD_BLACKLIST = [ + "tensorflow/lite/examples/android", + "tensorflow/lite/experimental/swift", +] def GetBuild(dir_base): """Get the list of BUILD file all targets recursively startind at dir_base.""" items = [] for root, _, files in os.walk(dir_base): for name in files: - if (name == "BUILD" and - root.find("tensorflow/lite/examples/android") == -1): + if (name == "BUILD" and root not in BUILD_BLACKLIST): items.append("//" + root + ":all") return items @@ -67,9 +72,9 @@ def BuildPyTestDependencies(): PYTHON_TARGETS, PY_TEST_QUERY_EXPRESSION = BuildPyTestDependencies() -# Hard-coded blacklist of files if not included in pip package # TODO(amitpatankar): Clean up blacklist. -BLACKLIST = [ +# List of dependencies that should not included in the pip package. +DEPENDENCY_BLACKLIST = [ "//tensorflow/python:extra_py_tests_deps", "//tensorflow/cc/saved_model:saved_model_half_plus_two", "//tensorflow:no_tensorflow_py_deps", @@ -82,9 +87,7 @@ BLACKLIST = [ "//tensorflow/core/kernels/cloud:bigquery_reader_ops", "//tensorflow/python/feature_column:vocabulary_testdata", "//tensorflow/python:framework/test_file_system.so", - # contrib - "//tensorflow/contrib/session_bundle:session_bundle_half_plus_two", - "//tensorflow/contrib/keras:testing_utils", + # lite "//tensorflow/lite/experimental/examples/lstm:tflite_lstm", "//tensorflow/lite/experimental/examples/lstm:tflite_lstm.py", "//tensorflow/lite/experimental/examples/lstm:unidirectional_sequence_lstm_test", # pylint:disable=line-too-long @@ -93,6 +96,9 @@ BLACKLIST = [ "//tensorflow/lite/python:interpreter_test", "//tensorflow/lite/python:interpreter.py", "//tensorflow/lite/python:interpreter_test.py", + # contrib + "//tensorflow/contrib/session_bundle:session_bundle_half_plus_two", + "//tensorflow/contrib/keras:testing_utils", "//tensorflow/contrib/ffmpeg:test_data", "//tensorflow/contrib/fused_conv:fused_conv2d_bias_activation_op_test_base", "//tensorflow/contrib/hadoop:test_data", @@ -149,8 +155,8 @@ def main(): # File extensions and endings to ignore ignore_extensions = ["_test", "_test.py", "_test_gpu", "_test_gpu.py"] - ignored_files = 0 - blacklisted_files = len(BLACKLIST) + ignored_files_count = 0 + blacklisted_dependencies_count = len(DEPENDENCY_BLACKLIST) # Compare dependencies for dependency in tf_py_test_dependencies_list: if dependency and dependency.startswith("//tensorflow"): @@ -158,16 +164,16 @@ def main(): # Ignore extensions if any(dependency.endswith(ext) for ext in ignore_extensions): ignore = True - ignored_files += 1 + ignored_files_count += 1 - # Check if the dependency is in the pip package, the blacklist, or - # should be ignored because of its file extension + # Check if the dependency is in the pip package, the dependency blacklist, + # or should be ignored because of its file extension. if not (ignore or dependency in pip_package_dependencies_list or - dependency in BLACKLIST): + dependency in DEPENDENCY_BLACKLIST): missing_dependencies.append(dependency) - print("Ignored files: %d" % ignored_files) - print("Blacklisted files: %d" % blacklisted_files) + print("Ignored files count: %d" % ignored_files_count) + print("Blacklisted dependencies count: %d" % blacklisted_dependencies_count) if missing_dependencies: print("Missing the following dependencies from pip_packages:") for missing_dependency in missing_dependencies: -- GitLab From e5b4b8daf1db2f49173a0eace291172cc9b1e0d3 Mon Sep 17 00:00:00 2001 From: Ben Zinberg Date: Thu, 3 Jan 2019 10:37:47 -0800 Subject: [PATCH 293/622] Minor correction to the docs: `tf.Dimension(None)` does not `==` itself. PiperOrigin-RevId: 227711581 --- tensorflow/python/framework/tensor_shape.py | 49 +++++++++++---------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/tensorflow/python/framework/tensor_shape.py b/tensorflow/python/framework/tensor_shape.py index a4c626c64c..a7537bb5f1 100644 --- a/tensorflow/python/framework/tensor_shape.py +++ b/tensorflow/python/framework/tensor_shape.py @@ -271,10 +271,11 @@ class Dimension(object): Dimensions are combined as follows: ```python - tf.Dimension(n) .merge_with(tf.Dimension(n)) == tf.Dimension(n) - tf.Dimension(n) .merge_with(tf.Dimension(None)) == tf.Dimension(n) - tf.Dimension(None).merge_with(tf.Dimension(n)) == tf.Dimension(n) - tf.Dimension(None).merge_with(tf.Dimension(None)) == tf.Dimension(None) + tf.Dimension(n) .merge_with(tf.Dimension(n)) == tf.Dimension(n) + tf.Dimension(n) .merge_with(tf.Dimension(None)) == tf.Dimension(n) + tf.Dimension(None).merge_with(tf.Dimension(n)) == tf.Dimension(n) + # equivalent to tf.Dimension(None) + tf.Dimension(None).merge_with(tf.Dimension(None)) # raises ValueError for n != m tf.Dimension(n) .merge_with(tf.Dimension(m)) @@ -304,10 +305,10 @@ class Dimension(object): Dimensions are summed as follows: ```python - tf.Dimension(m) + tf.Dimension(n) == tf.Dimension(m + n) - tf.Dimension(m) + tf.Dimension(None) == tf.Dimension(None) - tf.Dimension(None) + tf.Dimension(n) == tf.Dimension(None) - tf.Dimension(None) + tf.Dimension(None) == tf.Dimension(None) + tf.Dimension(m) + tf.Dimension(n) == tf.Dimension(m + n) + tf.Dimension(m) + tf.Dimension(None) # equiv. to tf.Dimension(None) + tf.Dimension(None) + tf.Dimension(n) # equiv. to tf.Dimension(None) + tf.Dimension(None) + tf.Dimension(None) # equiv. to tf.Dimension(None) ``` Args: @@ -339,10 +340,10 @@ class Dimension(object): Dimensions are subtracted as follows: ```python - tf.Dimension(m) - tf.Dimension(n) == tf.Dimension(m - n) - tf.Dimension(m) - tf.Dimension(None) == tf.Dimension(None) - tf.Dimension(None) - tf.Dimension(n) == tf.Dimension(None) - tf.Dimension(None) - tf.Dimension(None) == tf.Dimension(None) + tf.Dimension(m) - tf.Dimension(n) == tf.Dimension(m - n) + tf.Dimension(m) - tf.Dimension(None) # equiv. to tf.Dimension(None) + tf.Dimension(None) - tf.Dimension(n) # equiv. to tf.Dimension(None) + tf.Dimension(None) - tf.Dimension(None) # equiv. to tf.Dimension(None) ``` Args: @@ -378,10 +379,10 @@ class Dimension(object): Dimensions are summed as follows: ```python - tf.Dimension(m) * tf.Dimension(n) == tf.Dimension(m * n) - tf.Dimension(m) * tf.Dimension(None) == tf.Dimension(None) - tf.Dimension(None) * tf.Dimension(n) == tf.Dimension(None) - tf.Dimension(None) * tf.Dimension(None) == tf.Dimension(None) + tf.Dimension(m) * tf.Dimension(n) == tf.Dimension(m * n) + tf.Dimension(m) * tf.Dimension(None) # equiv. to tf.Dimension(None) + tf.Dimension(None) * tf.Dimension(n) # equiv. to tf.Dimension(None) + tf.Dimension(None) * tf.Dimension(None) # equiv. to tf.Dimension(None) ``` Args: @@ -417,10 +418,10 @@ class Dimension(object): Dimensions are divided as follows: ```python - tf.Dimension(m) // tf.Dimension(n) == tf.Dimension(m // n) - tf.Dimension(m) // tf.Dimension(None) == tf.Dimension(None) - tf.Dimension(None) // tf.Dimension(n) == tf.Dimension(None) - tf.Dimension(None) // tf.Dimension(None) == tf.Dimension(None) + tf.Dimension(m) // tf.Dimension(n) == tf.Dimension(m // n) + tf.Dimension(m) // tf.Dimension(None) # equiv. to tf.Dimension(None) + tf.Dimension(None) // tf.Dimension(n) # equiv. to tf.Dimension(None) + tf.Dimension(None) // tf.Dimension(None) # equiv. to tf.Dimension(None) ``` Args: @@ -475,10 +476,10 @@ class Dimension(object): Dimension moduli are computed as follows: ```python - tf.Dimension(m) % tf.Dimension(n) == tf.Dimension(m % n) - tf.Dimension(m) % tf.Dimension(None) == tf.Dimension(None) - tf.Dimension(None) % tf.Dimension(n) == tf.Dimension(None) - tf.Dimension(None) % tf.Dimension(None) == tf.Dimension(None) + tf.Dimension(m) % tf.Dimension(n) == tf.Dimension(m % n) + tf.Dimension(m) % tf.Dimension(None) # equiv. to tf.Dimension(None) + tf.Dimension(None) % tf.Dimension(n) # equiv. to tf.Dimension(None) + tf.Dimension(None) % tf.Dimension(None) # equiv. to tf.Dimension(None) ``` Args: -- GitLab From 0d969989e0e4fbddb8c57eadf8f4c72d975fbd6b Mon Sep 17 00:00:00 2001 From: Gunhan Gulsoy Date: Thu, 3 Jan 2019 10:41:14 -0800 Subject: [PATCH 294/622] Automated rollback of commit 4d9173668fd3ba410532efd901467312b7418752 PiperOrigin-RevId: 227712180 --- tensorflow/cc/BUILD | 1 + tensorflow/cc/saved_model/BUILD | 2 + tensorflow/compiler/tf2xla/BUILD | 1 + tensorflow/core/BUILD | 3 +- tensorflow/core/kernels/BUILD | 83 +------------------------------- tensorflow/tensorflow.bzl | 2 + 6 files changed, 10 insertions(+), 82 deletions(-) diff --git a/tensorflow/cc/BUILD b/tensorflow/cc/BUILD index a09becc49b..cf6d6050fa 100644 --- a/tensorflow/cc/BUILD +++ b/tensorflow/cc/BUILD @@ -150,6 +150,7 @@ cc_library_with_android_deps( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", + "//tensorflow/core:ops", "//tensorflow/core:protos_all_cc", ], ) diff --git a/tensorflow/cc/saved_model/BUILD b/tensorflow/cc/saved_model/BUILD index 52345a376c..836678d364 100644 --- a/tensorflow/cc/saved_model/BUILD +++ b/tensorflow/cc/saved_model/BUILD @@ -81,6 +81,7 @@ cc_library( ] + if_not_mobile([ "//tensorflow/core:core_cpu", "//tensorflow/core:lib", + "//tensorflow/core:ops", "//tensorflow/core:protos_all_cc", "//tensorflow/core:tensorflow", ]) + if_android([ @@ -100,6 +101,7 @@ cc_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", + "//tensorflow/core:ops", "//tensorflow/core:protos_all_cc", "//tensorflow/core/util/tensor_bundle:naming", # mobile not supported yet diff --git a/tensorflow/compiler/tf2xla/BUILD b/tensorflow/compiler/tf2xla/BUILD index d8123e956f..dfb6b57161 100644 --- a/tensorflow/compiler/tf2xla/BUILD +++ b/tensorflow/compiler/tf2xla/BUILD @@ -281,6 +281,7 @@ tf_cc_test( "//tensorflow/core:protos_all_cc", "//tensorflow/core:test", "//tensorflow/core:test_main", + "//tensorflow/core:testlib", ], ) diff --git a/tensorflow/core/BUILD b/tensorflow/core/BUILD index 1afac057d9..f9552d5965 100644 --- a/tensorflow/core/BUILD +++ b/tensorflow/core/BUILD @@ -1526,6 +1526,7 @@ cc_library( ":framework_internal", ":lib", ":lib_internal", + ":ops", ":protos_all_cc", ":shape_inference_testutil", ":tensor_testutil", @@ -3072,6 +3073,7 @@ tf_cuda_library( ":lib", ":lib_internal", ":metrics", + ":ops", ":proto_text", ":protos_all_cc", "//tensorflow/core/debug:debug_graph_utils", @@ -3854,7 +3856,6 @@ tf_cc_test( "ops/cudnn_rnn_ops_test.cc", ], deps = [ - ":cudnn_rnn_ops", "//tensorflow/core", "//tensorflow/core:framework", "//tensorflow/core:lib", diff --git a/tensorflow/core/kernels/BUILD b/tensorflow/core/kernels/BUILD index c72d04d3af..fd1df63c95 100644 --- a/tensorflow/core/kernels/BUILD +++ b/tensorflow/core/kernels/BUILD @@ -157,7 +157,6 @@ tf_kernel_library( name = "collective_ops", prefix = "collective_ops", deps = [ - "//tensorflow/core:collective_ops_op_lib", "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:protos_all_cc", @@ -222,7 +221,6 @@ tf_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", - "//tensorflow/core:nn_ops_op_lib", "//third_party/eigen3", ], alwayslink = 1, @@ -313,7 +311,6 @@ tf_kernel_library( "//tensorflow/core/nccl:nccl_lib", "//tensorflow/core:framework", "//tensorflow/core:gpu_headers_lib", - "//tensorflow/core:nccl_ops_op_lib", ]), ) @@ -459,7 +456,6 @@ cc_library( name = "batch_kernels", srcs = ["batch_kernels.cc"], deps = [ - "//tensorflow/core:batch_ops_op_lib", "//tensorflow/core:framework_headers_lib", "//tensorflow/core:protos_all_cc", "//tensorflow/core/kernels:concat_lib_hdrs", @@ -686,7 +682,6 @@ ARRAY_DEPS = [ ":ops_util", ":transpose_functor", "//tensorflow/core:array_grad", - "//tensorflow/core:array_ops_op_lib", "//tensorflow/core:core_cpu", "//tensorflow/core:framework", "//tensorflow/core:lib", @@ -715,7 +710,6 @@ tf_kernel_library( deps = [ "//tensorflow/core:framework_headers_lib", "//tensorflow/core:lib", - "//tensorflow/core:set_ops_op_lib", "//third_party/eigen3", ], ) @@ -1079,7 +1073,6 @@ tf_kernel_library( srcs = ["ragged_gather_op.cc"], deps = [ "//tensorflow/core:framework", - "//tensorflow/core:ragged_array_ops_op_lib", ], ) @@ -1090,7 +1083,6 @@ tf_cc_test( deps = [ ":ragged_gather_op", "//tensorflow/core:framework", - "//tensorflow/core:ragged_array_ops_op_lib", "//tensorflow/core:test", "//tensorflow/core:test_main", "//tensorflow/core:testlib", @@ -1103,7 +1095,6 @@ tf_kernel_library( srcs = ["ragged_range_op.cc"], deps = [ "//tensorflow/core:framework", - "//tensorflow/core:ragged_math_ops_op_lib", ], ) @@ -1113,7 +1104,6 @@ tf_cc_test( deps = [ ":ragged_range_op", "//tensorflow/core:framework", - "//tensorflow/core:ragged_math_ops_op_lib", "//tensorflow/core:test", "//tensorflow/core:test_main", "//tensorflow/core:testlib", @@ -1126,7 +1116,6 @@ tf_kernel_library( srcs = ["ragged_tensor_to_sparse_kernel.cc"], deps = [ "//tensorflow/core:framework", - "//tensorflow/core:ragged_conversion_ops_op_lib", ], ) @@ -1138,7 +1127,6 @@ tf_cc_test( ":ragged_tensor_to_sparse_kernel", "//tensorflow/core:framework", "//tensorflow/core:lib", - "//tensorflow/core:ragged_conversion_ops_op_lib", "//tensorflow/core:test", "//tensorflow/core:test_main", "//tensorflow/core:testlib", @@ -1152,7 +1140,6 @@ tf_kernel_library( visibility = ["//visibility:public"], deps = [ ":gpu_util_hdrs", - "//tensorflow/core:cudnn_rnn_ops_op_lib", "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", @@ -1229,7 +1216,6 @@ tf_cuda_cc_test( "//tensorflow/core:core_cpu", "//tensorflow/core:framework", "//tensorflow/core:lib", - "//tensorflow/core:math_ops_op_lib", "//tensorflow/core:protos_all_cc", "//tensorflow/core:test", "//tensorflow/core:test_main", @@ -1754,7 +1740,6 @@ tf_kernel_library( prefix = "candidate_sampler_ops", deps = [ ":range_sampler", - "//tensorflow/core:candidate_sampling_ops_op_lib", "//tensorflow/core:framework", "//tensorflow/core:lib", ], @@ -1788,7 +1773,6 @@ tf_kernel_library( name = "control_flow_ops", prefix = "control_flow_ops", deps = [ - "//tensorflow/core:control_flow_ops_op_lib", "//tensorflow/core:framework", "//tensorflow/core:lib", ], @@ -1800,7 +1784,6 @@ tf_kernel_library( deps = [ ":bounds_check", ":ops_util", - "//tensorflow/core:ctc_ops_op_lib", "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core/util/ctc:ctc_beam_search_lib", @@ -1881,7 +1864,6 @@ DATA_FLOW_DEPS = [ ":typed_queue", "//third_party/eigen3", "//tensorflow/core:core_cpu", - "//tensorflow/core:data_flow_ops_op_lib", "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", @@ -1946,7 +1928,6 @@ tf_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", - "//tensorflow/core:scoped_allocator_ops_op_lib", ], ) @@ -1965,7 +1946,6 @@ tf_cuda_cc_test( "//tensorflow/core:core_cpu_internal", "//tensorflow/core:framework", "//tensorflow/core:lib", - "//tensorflow/core:math_ops_op_lib", "//tensorflow/core:proto_text", "//tensorflow/core:protos_all_cc", "//tensorflow/core:test", @@ -2013,7 +1993,6 @@ tf_kernel_library( DYNAMIC_DEPS = [ ":bounds_check", "//tensorflow/core:core_cpu", - "//tensorflow/core:data_flow_ops_op_lib", "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", @@ -2046,7 +2025,6 @@ LOOKUP_DEPS = [ "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", - "//tensorflow/core:lookup_ops_op_lib", ] tf_kernel_library( @@ -2075,7 +2053,6 @@ tf_kernel_library( deps = [ ":lookup_table_init_op", ":lookup_table_op", - "//tensorflow/core:checkpoint_ops_op_lib", "//tensorflow/core:framework", "//tensorflow/core:lib", "//third_party/eigen3", @@ -2086,7 +2063,6 @@ tf_kernel_library( name = "load_and_remap_matrix_op", srcs = ["load_and_remap_matrix_op.cc"], deps = [ - "//tensorflow/core:checkpoint_ops_op_lib", "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", @@ -2233,7 +2209,6 @@ tf_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", - "//tensorflow/core:resource_variable_ops_op_lib", "@com_google_absl//absl/strings", ], ) @@ -2250,7 +2225,6 @@ tf_kernel_library( ":concat_lib", "//tensorflow/core:framework", "//tensorflow/core:lib", - "//tensorflow/core:list_ops_op_lib", "//third_party/eigen3", ], ) @@ -2261,7 +2235,6 @@ tf_kernel_library( deps = [ "//tensorflow/core:framework", "//tensorflow/core:lib", - "//tensorflow/core:user_ops_op_lib", ], ) @@ -2284,7 +2257,6 @@ tf_kernel_library( "//tensorflow/core:core_cpu", "//tensorflow/core:core_cpu_internal", "//tensorflow/core:framework", - "//tensorflow/core:functional_ops_op_lib", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", "//third_party/eigen3", @@ -2297,7 +2269,6 @@ tf_kernel_library( deps = [ "//tensorflow/core:core_cpu_internal", "//tensorflow/core:framework", - "//tensorflow/core:functional_ops_op_lib", "//tensorflow/core:lib", "//tensorflow/core:protos_all_cc", "//tensorflow/core/grappler:grappler_item", @@ -2340,7 +2311,6 @@ IMAGE_DEPS = [ "//third_party/eigen3", "//tensorflow/core:framework", "//tensorflow/core:gif_internal", - "//tensorflow/core:image_ops_op_lib", "//tensorflow/core:jpeg_internal", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", @@ -2680,7 +2650,6 @@ cc_library( IO_DEPS = [ ":ops_util", "//tensorflow/core:framework", - "//tensorflow/core:io_ops_op_lib", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", "//tensorflow/core:protos_all_cc", @@ -2724,7 +2693,6 @@ SAVE_RESTORE_DEPS = [ ":bounds_check_lib", ":save_restore_tensor", "//tensorflow/core:framework", - "//tensorflow/core:io_ops_op_lib", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", "//tensorflow/core:protos_all_cc", @@ -2843,7 +2811,6 @@ LINALG_DEPS = [ "//third_party/eigen3", "//tensorflow/core:framework", "//tensorflow/core:lib", - "//tensorflow/core:linalg_ops_op_lib", ] + if_cuda([ ":cuda_solvers", ":transpose_functor", @@ -2987,7 +2954,6 @@ LOGGING_DEPS = [ "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", - "//tensorflow/core:logging_ops_op_lib", "//tensorflow/core:protos_all_cc", ] @@ -3059,7 +3025,6 @@ tf_kernel_library( ":bounds_check", "//tensorflow/core:framework", "//tensorflow/core:lib", - "//tensorflow/core:manip_ops_op_lib", "//third_party/eigen3", ], ) @@ -3091,7 +3056,6 @@ MATH_DEPS = [ "//tensorflow/core:lib", "//tensorflow/core:lib_internal", "//tensorflow/core:math_grad", - "//tensorflow/core:math_ops_op_lib", "//third_party/eigen3", ] @@ -3202,7 +3166,7 @@ tf_kernel_library( tf_kernel_library( name = "cwise_op", prefix = "cwise_op", - deps = MATH_DEPS + ["//tensorflow/core:bitwise_ops_op_lib"], + deps = MATH_DEPS, ) tf_kernel_library( @@ -3221,7 +3185,6 @@ tf_kernel_library( name = "fft_ops", prefix = "fft_ops", deps = MATH_DEPS + [ - "//tensorflow/core:spectral_ops_op_lib", ] + if_cuda([ "//tensorflow/core/platform/default/build_config:cufft_plugin", ]), @@ -3420,10 +3383,7 @@ tf_cuda_cc_test( ":quantized_ops", "//tensorflow/cc:cc_ops", "//tensorflow/cc:client_session", - "//tensorflow/core:array_ops_op_lib", "//tensorflow/core:framework", - "//tensorflow/core:math_ops_op_lib", - "//tensorflow/core:nn_ops_op_lib", "//tensorflow/core:protos_all_cc", "//tensorflow/core:test", "//tensorflow/core:test_main", @@ -3681,7 +3641,6 @@ tf_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", - "//tensorflow/core:nn_ops_op_lib", ] + select({ ":xsmm_convolutions": [ "@libxsmm_archive//:xsmm_avx", @@ -3703,7 +3662,6 @@ tf_kernel_library( "//tensorflow/core:core_cpu", "//tensorflow/core:framework", "//tensorflow/core:lib", - "//tensorflow/core:nn_ops_op_lib", ] + if_cuda([ "@cub_archive//:cub", "@local_config_cuda//cuda:cudnn_header", @@ -3723,7 +3681,6 @@ tf_kernel_library( "//tensorflow/core:core_cpu", "//tensorflow/core:framework", "//tensorflow/core:lib", - "//tensorflow/core:nn_ops_op_lib", ] + if_cuda([ "@local_config_cuda//cuda:cudnn_header", ]), @@ -3771,9 +3728,8 @@ NN_DEPS = [ "//tensorflow/core:lib", "//tensorflow/core:lib_internal", "//tensorflow/core:nn_grad", - "//tensorflow/core:nn_ops_op_lib", "//third_party/eigen3", -] + if_mkl(["//tensorflow/core:mkl_nn_ops_op_lib"]) +] tf_kernel_library( name = "batch_norm_op", @@ -3893,7 +3849,6 @@ tf_kernel_library( "//tensorflow/core:lib", "//tensorflow/core:lib_internal", "//tensorflow/core:nn_grad", - "//tensorflow/core:nn_ops_op_lib", ] + if_cuda(["@cub_archive//:cub"]), ) @@ -4001,7 +3956,6 @@ tf_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", - "//tensorflow/core:nn_ops_op_lib", "//tensorflow/core:stream_executor", "//third_party/eigen3", ], @@ -4045,7 +3999,6 @@ tf_kernel_library( "//tensorflow/core:core_cpu", "//tensorflow/core:framework", "//tensorflow/core:lib", - "//tensorflow/core:nn_ops_op_lib", "//third_party/eigen3", ], ) @@ -4127,7 +4080,6 @@ cc_library( PARSING_DEPS = [ "//tensorflow/core:framework", "//tensorflow/core:lib", - "//tensorflow/core:parsing_ops_op_lib", "//tensorflow/core:proto_text", "//tensorflow/core:protos_all_cc", ] @@ -4196,7 +4148,6 @@ RANDOM_OPS_DEPS = [ "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", - "//tensorflow/core:random_ops_op_lib", ] tf_kernel_library( @@ -4235,7 +4186,6 @@ tf_kernel_library( ":random_op", "//tensorflow/core:framework", "//tensorflow/core:lib", - "//tensorflow/core:stateless_random_ops_op_lib", ], ) @@ -4250,8 +4200,6 @@ cc_library( REQUIRED_DEPS = [ "//tensorflow/core:framework", "//tensorflow/core:lib", - "//tensorflow/core:no_op_op_lib", - "//tensorflow/core:sendrecv_ops_op_lib", ] tf_kernel_library( @@ -4312,7 +4260,6 @@ cc_library( SPARSE_DEPS = [ "//tensorflow/core:framework", "//tensorflow/core:lib", - "//tensorflow/core:sparse_ops_op_lib", ] tf_kernel_library( @@ -4557,7 +4504,6 @@ tf_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", - "//tensorflow/core:sdca_ops_op_lib", "//third_party/eigen3", "@farmhash_archive//:farmhash", ], @@ -4597,7 +4543,6 @@ STATE_DEPS = [ "//third_party/eigen3", "//tensorflow/core:framework", "//tensorflow/core:lib", - "//tensorflow/core:state_ops_op_lib", ] + if_sycl(["//tensorflow/core:sycl_runtime"]) tf_kernel_library( @@ -4735,7 +4680,6 @@ STRING_DEPS = [ "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", - "//tensorflow/core:string_ops_op_lib", ] tf_kernel_library( @@ -4886,7 +4830,6 @@ tf_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", - "//tensorflow/core:string_ops_op_lib", "//third_party/eigen3", "//third_party/icu/data:conversion_data", "@icu//:common", @@ -4908,7 +4851,6 @@ tf_kernel_library( ":variable_ops", "//tensorflow/core:framework", "//tensorflow/core:lib", - "//tensorflow/core:training_ops_op_lib", "//third_party/eigen3", ], ) @@ -4967,7 +4909,6 @@ tf_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", - "//tensorflow/core:random_ops_op_lib", ], ) @@ -4995,7 +4936,6 @@ tf_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", - "//tensorflow/core:random_ops_op_lib", ], ) @@ -5904,12 +5844,9 @@ tf_kernel_library( ":ops_util", ":pooling_ops", ":quantization_utils", - "//tensorflow/core:array_ops_op_lib", "//tensorflow/core:core_cpu", "//tensorflow/core:framework", "//tensorflow/core:lib", - "//tensorflow/core:math_ops_op_lib", - "//tensorflow/core:nn_ops_op_lib", "//third_party/eigen3", "@gemmlowp", ], @@ -6479,7 +6416,6 @@ tf_kernel_library( "//tensorflow/core:lib", "//tensorflow/core:lib_internal", "//tensorflow/core:protos_all_cc", - "//tensorflow/core:remote_fused_graph_ops_op_lib", ], ) @@ -6634,8 +6570,6 @@ tf_mkl_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", - "//tensorflow/core:mkl_nn_ops_op_lib", - "//tensorflow/core:nn_ops_op_lib", ] + mkl_deps(), ) @@ -6685,8 +6619,6 @@ tf_mkl_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", - "//tensorflow/core:mkl_nn_ops_op_lib", - "//tensorflow/core:nn_ops_op_lib", ] + mkl_deps(), ) @@ -6705,8 +6637,6 @@ tf_mkl_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", - "//tensorflow/core:mkl_nn_ops_op_lib", - "//tensorflow/core:nn_ops_op_lib", ] + mkl_deps(), ) @@ -6720,8 +6650,6 @@ tf_mkl_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", - "//tensorflow/core:mkl_nn_ops_op_lib", - "//tensorflow/core:nn_ops_op_lib", "//third_party/eigen3", ] + mkl_deps(), ) @@ -6736,8 +6664,6 @@ tf_mkl_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", - "//tensorflow/core:mkl_nn_ops_op_lib", - "//tensorflow/core:nn_ops_op_lib", "//third_party/eigen3", ] + mkl_deps(), ) @@ -6878,7 +6804,6 @@ tf_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:protos_all_cc", - "//tensorflow/core:summary_ops_op_lib", "//tensorflow/core/lib/db:sqlite", ] + if_not_v2([ "//tensorflow/contrib/tensorboard/db:schema", @@ -6893,7 +6818,6 @@ tf_kernel_library( "decode_proto_op.cc", ], deps = [ - "//tensorflow/core:decode_proto_ops_op_lib", "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core/util/proto:decode", @@ -6907,7 +6831,6 @@ tf_kernel_library( name = "encode_proto_op", srcs = ["encode_proto_op.cc"], deps = [ - "//tensorflow/core:encode_proto_ops_op_lib", "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core/util/proto:descriptors", @@ -6925,7 +6848,6 @@ tf_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", - "//tensorflow/core:rpc_ops_op_lib", "//tensorflow/core/util/rpc:call_container", "//tensorflow/core/util/rpc:rpc_factory", "//tensorflow/core/util/rpc:rpc_factory_registry", @@ -6938,7 +6860,6 @@ tf_kernel_library( srcs = ["unicode_script_op.cc"], deps = [ "//tensorflow/core:framework", - "//tensorflow/core:string_ops_op_lib", "@icu//:common", ], ) diff --git a/tensorflow/tensorflow.bzl b/tensorflow/tensorflow.bzl index 1024b686eb..b9069768e8 100644 --- a/tensorflow/tensorflow.bzl +++ b/tensorflow/tensorflow.bzl @@ -590,6 +590,7 @@ def tf_gen_op_wrappers_cc( clean_dep("//tensorflow/core:core_cpu"), clean_dep("//tensorflow/core:framework"), clean_dep("//tensorflow/core:lib"), + clean_dep("//tensorflow/core:ops"), clean_dep("//tensorflow/core:protos_all_cc"), ]) + if_android([ clean_dep("//tensorflow/core:android_tensorflow_lib"), @@ -606,6 +607,7 @@ def tf_gen_op_wrappers_cc( clean_dep("//tensorflow/core:core_cpu"), clean_dep("//tensorflow/core:framework"), clean_dep("//tensorflow/core:lib"), + clean_dep("//tensorflow/core:ops"), clean_dep("//tensorflow/core:protos_all_cc"), ]) + if_android([ clean_dep("//tensorflow/core:android_tensorflow_lib"), -- GitLab From f6fa3515db311b666dd4bd529f9b8b87575ce978 Mon Sep 17 00:00:00 2001 From: Allen Lavoie Date: Thu, 3 Jan 2019 10:44:43 -0800 Subject: [PATCH 295/622] Silence a noisy __del__ Popped up sometimes, looking something like: Exception ignored in: > Traceback (most recent call last): File "[...]tensorflow/python/client/session.py", line 738, in __del__ TypeError: 'NoneType' object is not callable Which I'm guessing is TF_DeleteSession being None. PiperOrigin-RevId: 227712751 --- tensorflow/python/client/session.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tensorflow/python/client/session.py b/tensorflow/python/client/session.py index 87a200ed33..b97eb884b3 100644 --- a/tensorflow/python/client/session.py +++ b/tensorflow/python/client/session.py @@ -736,10 +736,11 @@ class BaseSession(SessionInterface): if self._session is not None: try: tf_session.TF_DeleteSession(self._session) - except AttributeError: - # At shutdown, `c_api_util` or `tf_session` may have been garbage - # collected, causing the above method calls to fail. In this case, - # silently leak since the program is about to terminate anyway. + except (AttributeError, TypeError): + # At shutdown, `c_api_util`, `tf_session`, or + # `tf_session.TF_DeleteSession` may have been garbage collected, causing + # the above method calls to fail. In this case, silently leak since the + # program is about to terminate anyway. pass self._session = None -- GitLab From e518527f10048b10956c7192de1411c36a3a1938 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Thu, 3 Jan 2019 11:59:51 -0800 Subject: [PATCH 296/622] Spectral Normalization implementation in TFGAN PiperOrigin-RevId: 227725854 --- tensorflow/contrib/gan/BUILD | 43 +++ .../contrib/gan/python/features/__init__.py | 3 + .../features/python/spectral_normalization.py | 32 ++ .../python/spectral_normalization_impl.py | 315 ++++++++++++++++ .../python/spectral_normalization_test.py | 354 ++++++++++++++++++ 5 files changed, 747 insertions(+) create mode 100644 tensorflow/contrib/gan/python/features/python/spectral_normalization.py create mode 100644 tensorflow/contrib/gan/python/features/python/spectral_normalization_impl.py create mode 100644 tensorflow/contrib/gan/python/features/python/spectral_normalization_test.py diff --git a/tensorflow/contrib/gan/BUILD b/tensorflow/contrib/gan/BUILD index 97184dabb0..0626875b76 100644 --- a/tensorflow/contrib/gan/BUILD +++ b/tensorflow/contrib/gan/BUILD @@ -132,6 +132,7 @@ py_library( ":clip_weights", ":conditioning_utils", ":random_tensor_pool", + ":spectral_normalization", ":virtual_batchnorm", "//tensorflow/python:util", ], @@ -676,3 +677,45 @@ py_test( "//third_party/py/numpy", ], ) + +py_library( + name = "spectral_normalization", + srcs = [ + "python/features/python/spectral_normalization.py", + "python/features/python/spectral_normalization_impl.py", + ], + srcs_version = "PY2AND3", + deps = [ + "//tensorflow/python:array_ops", + "//tensorflow/python:dtypes", + "//tensorflow/python:framework_ops", + "//tensorflow/python:init_ops", + "//tensorflow/python:math_ops", + "//tensorflow/python:standard_ops", + "//tensorflow/python:variable_scope", + "//tensorflow/python/keras:engine", + ], +) + +py_test( + name = "spectral_normalization_test", + srcs = ["python/features/python/spectral_normalization_test.py"], + srcs_version = "PY2AND3", + deps = [ + ":spectral_normalization", + "//tensorflow/contrib/layers:layers_py", + "//tensorflow/contrib/slim", + "//tensorflow/python:array_ops", + "//tensorflow/python:client_testlib", + "//tensorflow/python:dtypes", + "//tensorflow/python:framework_ops", + "//tensorflow/python:init_ops", + "//tensorflow/python:layers", + "//tensorflow/python:linalg_ops", + "//tensorflow/python:math_ops", + "//tensorflow/python:variable_scope", + "//tensorflow/python:variables", + "//tensorflow/python/keras:layers", + "//third_party/py/numpy", + ], +) diff --git a/tensorflow/contrib/gan/python/features/__init__.py b/tensorflow/contrib/gan/python/features/__init__.py index 4816daf760..410c3a0205 100644 --- a/tensorflow/contrib/gan/python/features/__init__.py +++ b/tensorflow/contrib/gan/python/features/__init__.py @@ -27,11 +27,13 @@ from __future__ import print_function from tensorflow.contrib.gan.python.features.python import clip_weights from tensorflow.contrib.gan.python.features.python import conditioning_utils from tensorflow.contrib.gan.python.features.python import random_tensor_pool +from tensorflow.contrib.gan.python.features.python import spectral_normalization from tensorflow.contrib.gan.python.features.python import virtual_batchnorm from tensorflow.contrib.gan.python.features.python.clip_weights import * from tensorflow.contrib.gan.python.features.python.conditioning_utils import * from tensorflow.contrib.gan.python.features.python.random_tensor_pool import * +from tensorflow.contrib.gan.python.features.python.spectral_normalization import * from tensorflow.contrib.gan.python.features.python.virtual_batchnorm import * # pylint: enable=unused-import,wildcard-import @@ -40,5 +42,6 @@ from tensorflow.python.util.all_util import remove_undocumented _allowed_symbols = clip_weights.__all__ _allowed_symbols += conditioning_utils.__all__ _allowed_symbols += random_tensor_pool.__all__ +_allowed_symbols += spectral_normalization.__all__ _allowed_symbols += virtual_batchnorm.__all__ remove_undocumented(__name__, _allowed_symbols) diff --git a/tensorflow/contrib/gan/python/features/python/spectral_normalization.py b/tensorflow/contrib/gan/python/features/python/spectral_normalization.py new file mode 100644 index 0000000000..54d3d0a218 --- /dev/null +++ b/tensorflow/contrib/gan/python/features/python/spectral_normalization.py @@ -0,0 +1,32 @@ +# Copyright 2018 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. +# ============================================================================== +"""Keras-like layers and utilities that implement Spectral Normalization. + +Based on "Spectral Normalization for Generative Adversarial Networks" by Miyato, +et al in ICLR 2018. https://openreview.net/pdf?id=B1QRgziT- +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.contrib.gan.python.features.python import spectral_normalization_impl +# pylint: disable=wildcard-import +from tensorflow.contrib.gan.python.features.python.spectral_normalization_impl import * +# pylint: enable=wildcard-import +from tensorflow.python.util.all_util import remove_undocumented + +__all__ = spectral_normalization_impl.__all__ +remove_undocumented(__name__, __all__) diff --git a/tensorflow/contrib/gan/python/features/python/spectral_normalization_impl.py b/tensorflow/contrib/gan/python/features/python/spectral_normalization_impl.py new file mode 100644 index 0000000000..0cc653f0a7 --- /dev/null +++ b/tensorflow/contrib/gan/python/features/python/spectral_normalization_impl.py @@ -0,0 +1,315 @@ +# Copyright 2018 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. +# ============================================================================== +"""Keras-like layers and utilities that implement Spectral Normalization. + +Based on "Spectral Normalization for Generative Adversarial Networks" by Miyato, +et al in ICLR 2018. https://openreview.net/pdf?id=B1QRgziT- +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import contextlib +import numbers +import re + +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops +from tensorflow.python.keras.engine import base_layer_utils as keras_base_layer_utils +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import init_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import nn +from tensorflow.python.ops import variable_scope +from tensorflow.python.platform import tf_logging as logging + +__all__ = [ + 'compute_spectral_norm', 'spectral_normalize', 'spectral_norm_regularizer', + 'spectral_normalization_custom_getter', 'keras_spectral_normalization' +] + +# tf.bfloat16 should work, but tf.matmul converts those to tf.float32 which then +# can't directly be assigned back to the tf.bfloat16 variable. +_OK_DTYPES_FOR_SPECTRAL_NORM = (dtypes.float16, dtypes.float32, dtypes.float64) +_PERSISTED_U_VARIABLE_SUFFIX = 'spectral_norm_u' + + +def compute_spectral_norm(w_tensor, power_iteration_rounds=1, name=None): + """Estimates the largest singular value in the weight tensor. + + Args: + w_tensor: The weight matrix whose spectral norm should be computed. + power_iteration_rounds: The number of iterations of the power method to + perform. A higher number yeilds a better approximation. + name: An optional scope name. + + Returns: + The largest singular value (the spectral norm) of w. + """ + with variable_scope.variable_scope(name, 'spectral_norm'): + # The paper says to flatten convnet kernel weights from + # (C_out, C_in, KH, KW) to (C_out, C_in * KH * KW). But TensorFlow's Conv2D + # kernel weight shape is (KH, KW, C_in, C_out), so it should be reshaped to + # (KH * KW * C_in, C_out), and similarly for other layers that put output + # channels as last dimension. + # n.b. this means that w here is equivalent to w.T in the paper. + w = array_ops.reshape(w_tensor, (-1, w_tensor.get_shape()[-1])) + + # Persisted approximation of first left singular vector of matrix `w`. + u_var = variable_scope.get_variable( + _PERSISTED_U_VARIABLE_SUFFIX, + shape=(w.shape[0], 1), + dtype=w.dtype, + initializer=init_ops.random_normal_initializer(), + trainable=False) + u = u_var + + # Use power iteration method to approximate spectral norm. + for _ in range(power_iteration_rounds): + # `v` approximates the first right singular vector of matrix `w`. + v = nn.l2_normalize(math_ops.matmul(array_ops.transpose(w), u)) + u = nn.l2_normalize(math_ops.matmul(w, v)) + + # Update persisted approximation. + with ops.control_dependencies([u_var.assign(u, name='update_u')]): + u = array_ops.identity(u) + + u = array_ops.stop_gradient(u) + v = array_ops.stop_gradient(v) + + # Largest singular value of `w`. + spectral_norm = math_ops.matmul( + math_ops.matmul(array_ops.transpose(u), w), v) + spectral_norm.shape.assert_is_fully_defined() + spectral_norm.shape.assert_is_compatible_with([1, 1]) + + return spectral_norm[0][0] + + +def spectral_normalize(w, power_iteration_rounds=1, name=None): + """Normalizes a weight matrix by its spectral norm. + + Args: + w: The weight matrix to be normalized. + power_iteration_rounds: The number of iterations of the power method to + perform. A higher number yeilds a better approximation. + name: An optional scope name. + + Returns: + A normalized weight matrix tensor. + """ + with variable_scope.variable_scope(name, 'spectral_normalize'): + w_normalized = w / compute_spectral_norm( + w, power_iteration_rounds=power_iteration_rounds) + return array_ops.reshape(w_normalized, w.get_shape()) + + +def spectral_norm_regularizer(scale, power_iteration_rounds=1, scope=None): + """Returns a functions that can be used to apply spectral norm regularization. + + Small spectral norms enforce a small Lipschitz constant, which is necessary + for Wasserstein GANs. + + Args: + scale: A scalar multiplier. 0.0 disables the regularizer. + power_iteration_rounds: The number of iterations of the power method to + perform. A higher number yeilds a better approximation. + scope: An optional scope name. + + Returns: + A function with the signature `sn(weights)` that applies spectral norm + regularization. + + Raises: + ValueError: If scale is negative or if scale is not a float. + """ + if isinstance(scale, numbers.Integral): + raise ValueError('scale cannot be an integer: %s' % scale) + if isinstance(scale, numbers.Real): + if scale < 0.0: + raise ValueError( + 'Setting a scale less than 0 on a regularizer: %g' % scale) + if scale == 0.0: + logging.info('Scale of 0 disables regularizer.') + return lambda _: None + + def sn(weights, name=None): + """Applies spectral norm regularization to weights.""" + with ops.name_scope(scope, 'SpectralNormRegularizer', [weights]) as name: + scale_t = ops.convert_to_tensor( + scale, dtype=weights.dtype.base_dtype, name='scale') + return math_ops.multiply( + scale_t, + compute_spectral_norm( + weights, power_iteration_rounds=power_iteration_rounds), + name=name) + + return sn + + +def _default_name_filter(name): + """A filter function to identify common names of weight variables. + + Args: + name: The variable name. + + Returns: + Whether `name` is a standard name for a weight/kernel variables used in the + Keras, tf.layers, tf.contrib.layers or tf.contrib.slim libraries. + """ + match = re.match(r'(.*\/)?(depthwise_|pointwise_)?(weights|kernel)$', name) + return match is not None + + +def spectral_normalization_custom_getter(name_filter=_default_name_filter, + power_iteration_rounds=1): + """Custom getter that performs Spectral Normalization on a weight tensor. + + Specifically it divides the weight tensor by its largest singular value. This + is intended to stabilize GAN training, by making the discriminator satisfy a + local 1-Lipschitz constraint. + + Based on [Spectral Normalization for Generative Adversarial Networks][sn-gan]. + + [sn-gan]: https://openreview.net/forum?id=B1QRgziT- + + To reproduce an SN-GAN, apply this custom_getter to every weight tensor of + your discriminator. The last dimension of the weight tensor must be the number + of output channels. + + Apply this to layers by supplying this as the `custom_getter` of a + `tf.variable_scope`. For example: + + with tf.variable_scope('discriminator', + custom_getter=spectral_norm_getter()): + net = discriminator_fn(net) + + IMPORTANT: Keras does not respect the custom_getter supplied by the + VariableScope, so Keras users should use `keras_spectral_normalization` + instead of (or in addition to) this approach. + + It is important to carefully select to which weights you want to apply + Spectral Normalization. In general you want to normalize the kernels of + convolution and dense layers, but you do not want to normalize biases. You + also want to avoid normalizing batch normalization (and similar) variables, + but in general such layers play poorly with Spectral Normalization, since the + gamma can cancel out the normalization in other layers. By default we supply a + filter that matches the kernel variable names of the dense and convolution + layers of the tf.layers, tf.contrib.layers, tf.keras and tf.contrib.slim + libraries. If you are using anything else you'll need a custom `name_filter`. + + This custom getter internally creates a variable used to compute the spectral + norm by power iteration. It will update every time the variable is accessed, + which means the normalized discriminator weights may change slightly whilst + training the generator. Whilst unusual, this matches how the paper's authors + implement it, and in general additional rounds of power iteration can't hurt. + + Args: + name_filter: Optionally, a method that takes a Variable name as input and + returns whether this Variable should be normalized. + power_iteration_rounds: The number of iterations of the power method to + perform per step. A higher number yeilds a better approximation of the + true spectral norm. + + Returns: + A custom getter function that applies Spectral Normalization to all + Variables whose names match `name_filter`. + + Raises: + ValueError: If name_filter is not callable. + """ + if not callable(name_filter): + raise ValueError('name_filter must be callable') + + def _internal_getter(getter, name, *args, **kwargs): + """A custom getter function that applies Spectral Normalization. + + Args: + getter: The true getter to call. + name: Name of new/existing variable, in the same format as + tf.get_variable. + *args: Other positional arguments, in the same format as tf.get_variable. + **kwargs: Keyword arguments, in the same format as tf.get_variable. + + Returns: + The return value of `getter(name, *args, **kwargs)`, spectrally + normalized. + + Raises: + ValueError: If used incorrectly, or if `dtype` is not supported. + """ + if not name_filter(name): + return getter(name, *args, **kwargs) + + if name.endswith(_PERSISTED_U_VARIABLE_SUFFIX): + raise ValueError( + 'Cannot apply Spectral Normalization to internal variables created ' + 'for Spectral Normalization. Tried to normalized variable [%s]' % + name) + + if kwargs['dtype'] not in _OK_DTYPES_FOR_SPECTRAL_NORM: + raise ValueError('Disallowed data type {}'.format(kwargs['dtype'])) + + # This layer's weight Variable/PartitionedVariable. + w_tensor = getter(name, *args, **kwargs) + + if len(w_tensor.get_shape()) < 2: + raise ValueError( + 'Spectral norm can only be applied to multi-dimensional tensors') + + return spectral_normalize( + w_tensor, + power_iteration_rounds=power_iteration_rounds, + name=(name + '/spectral_normalize')) + + return _internal_getter + + +@contextlib.contextmanager +def keras_spectral_normalization(name_filter=_default_name_filter, + power_iteration_rounds=1): + """A context manager that enables Spectral Normalization for Keras. + + Keras doesn't respect the `custom_getter` in the VariableScope, so this is a + bit of a hack to make things work. + + Usage: + with keras_spectral_normalization(): + net = discriminator_fn(net) + + Args: + name_filter: Optionally, a method that takes a Variable name as input and + returns whether this Variable should be normalized. + power_iteration_rounds: The number of iterations of the power method to + perform per step. A higher number yeilds a better approximation of the + true spectral norm. + + Yields: + A context manager that wraps the standard Keras variable creation method + with the `spectral_normalization_custom_getter`. + """ + original_make_variable = keras_base_layer_utils.make_variable + sn_getter = spectral_normalization_custom_getter( + name_filter=name_filter, power_iteration_rounds=power_iteration_rounds) + + def make_variable_wrapper(name, *args, **kwargs): + return sn_getter(original_make_variable, name, *args, **kwargs) + + keras_base_layer_utils.make_variable = make_variable_wrapper + + yield + + keras_base_layer_utils.make_variable = original_make_variable diff --git a/tensorflow/contrib/gan/python/features/python/spectral_normalization_test.py b/tensorflow/contrib/gan/python/features/python/spectral_normalization_test.py new file mode 100644 index 0000000000..4ea21f70ec --- /dev/null +++ b/tensorflow/contrib/gan/python/features/python/spectral_normalization_test.py @@ -0,0 +1,354 @@ +# Copyright 2018 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 features.spectral_normalization.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +from tensorflow.contrib import slim +from tensorflow.contrib.gan.python.features.python import spectral_normalization_impl as spectral_normalization +from tensorflow.contrib.layers.python.layers import layers as contrib_layers +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops +from tensorflow.python.keras.layers import convolutional as keras_convolutional +from tensorflow.python.keras.layers import core as keras_core +from tensorflow.python.layers import convolutional as layers_convolutional +from tensorflow.python.layers import core as layers_core +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import init_ops +from tensorflow.python.ops import linalg_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import variable_scope +from tensorflow.python.ops import variables +from tensorflow.python.platform import test + + +class SpectralNormalizationTest(test.TestCase): + + def testComputeSpectralNorm(self): + weights = variable_scope.get_variable( + 'w', dtype=dtypes.float32, shape=[2, 3, 50, 100]) + weights = math_ops.multiply(weights, 10.0) + s = linalg_ops.svd( + array_ops.reshape(weights, [-1, weights.shape[-1]]), compute_uv=False) + true_sn = s[..., 0] + estimated_sn = spectral_normalization.compute_spectral_norm(weights) + + with self.cached_session() as sess: + sess.run(variables.global_variables_initializer()) + np_true_sn = sess.run(true_sn) + for i in range(50): + est = sess.run(estimated_sn) + if i < 1: + np_est_1 = est + if i < 4: + np_est_5 = est + if i < 9: + np_est_10 = est + np_est_50 = est + + # Check that the estimate improves with more iterations. + self.assertAlmostEqual(np_true_sn, np_est_50, 0) + self.assertGreater( + abs(np_true_sn - np_est_10), abs(np_true_sn - np_est_50)) + self.assertGreater( + abs(np_true_sn - np_est_5), abs(np_true_sn - np_est_10)) + self.assertGreater(abs(np_true_sn - np_est_1), abs(np_true_sn - np_est_5)) + + def testSpectralNormalize(self): + weights = variable_scope.get_variable( + 'w', dtype=dtypes.float32, shape=[2, 3, 50, 100]) + weights = math_ops.multiply(weights, 10.0) + normalized_weights = spectral_normalization.spectral_normalize( + weights, power_iteration_rounds=1) + + unnormalized_sigma = linalg_ops.svd( + array_ops.reshape(weights, [-1, weights.shape[-1]]), + compute_uv=False)[..., 0] + normalized_sigma = linalg_ops.svd( + array_ops.reshape(normalized_weights, [-1, weights.shape[-1]]), + compute_uv=False)[..., 0] + + with self.cached_session() as sess: + sess.run(variables.global_variables_initializer()) + s0 = sess.run(unnormalized_sigma) + + for i in range(50): + sigma = sess.run(normalized_sigma) + if i < 1: + s1 = sigma + if i < 5: + s5 = sigma + if i < 10: + s10 = sigma + s50 = sigma + + self.assertAlmostEqual(1., s50, 0) + self.assertGreater(abs(s10 - 1.), abs(s50 - 1.)) + self.assertGreater(abs(s5 - 1.), abs(s10 - 1.)) + self.assertGreater(abs(s1 - 1.), abs(s5 - 1.)) + self.assertGreater(abs(s0 - 1.), abs(s1 - 1.)) + + def _testLayerHelper(self, build_layer_fn, w_shape, b_shape, is_keras=False): + x = array_ops.placeholder(dtypes.float32, shape=[2, 10, 10, 3]) + + w_initial = np.random.randn(*w_shape) * 10 + w_initializer = init_ops.constant_initializer(w_initial) + b_initial = np.random.randn(*b_shape) + b_initializer = init_ops.constant_initializer(b_initial) + + if is_keras: + context_manager = spectral_normalization.keras_spectral_normalization() + else: + getter = spectral_normalization.spectral_normalization_custom_getter() + context_manager = variable_scope.variable_scope('', custom_getter=getter) + + with context_manager: + (net, + expected_normalized_vars, expected_not_normalized_vars) = build_layer_fn( + x, w_initializer, b_initializer) + + x_data = np.random.rand(*x.shape) + + with self.cached_session() as sess: + sess.run(variables.global_variables_initializer()) + + # Before running a forward pass we still expect the variables values to + # differ from the initial value because of the normalizer. + w_befores = [] + for name, var in expected_normalized_vars.items(): + w_before = sess.run(var) + w_befores.append(w_before) + self.assertFalse( + np.allclose(w_initial, w_before), + msg=('%s appears not to be normalized. Before: %s After: %s' % + (name, w_initial, w_before))) + + # Not true for the unnormalized variables. + for name, var in expected_not_normalized_vars.items(): + b_before = sess.run(var) + self.assertTrue( + np.allclose(b_initial, b_before), + msg=('%s appears to be unexpectedly normalized. ' + 'Before: %s After: %s' % (name, b_initial, b_before))) + + # Run a bunch of forward passes. + for _ in range(1000): + _ = sess.run(net, feed_dict={x: x_data}) + + # We expect this to have improved the estimate of the spectral norm, + # which should have changed the variable values and brought them close + # to the true Spectral Normalized values. + _, s, _ = np.linalg.svd(w_initial.reshape([-1, 3])) + exactly_normalized = w_initial / s[0] + for w_before, (name, var) in zip(w_befores, + expected_normalized_vars.items()): + w_after = sess.run(var) + self.assertFalse( + np.allclose(w_before, w_after, rtol=1e-8, atol=1e-8), + msg=('%s did not improve over many iterations. ' + 'Before: %s After: %s' % (name, w_before, w_after))) + self.assertAllClose( + exactly_normalized, + w_after, + rtol=1e-4, + atol=1e-4, + msg=('Estimate of spectral norm for %s was innacurate. ' + 'Normalized matrices do not match.' + 'Estimate: %s Actual: %s' % (name, w_after, + exactly_normalized))) + + def testConv2D_Layers(self): + + def build_layer_fn(x, w_initializer, b_initializer): + layer = layers_convolutional.Conv2D( + filters=3, + kernel_size=3, + padding='same', + kernel_initializer=w_initializer, + bias_initializer=b_initializer) + net = layer.apply(x) + expected_normalized_vars = {'tf.layers.Conv2d.kernel': layer.kernel} + expected_not_normalized_vars = {'tf.layers.Conv2d.bias': layer.bias} + + return net, expected_normalized_vars, expected_not_normalized_vars + + self._testLayerHelper(build_layer_fn, (3, 3, 3, 3), (3,)) + + def testConv2D_ContribLayers(self): + + def build_layer_fn(x, w_initializer, b_initializer): + var_collection = { + 'weights': ['CONTRIB_LAYERS_CONV2D_WEIGHTS'], + 'biases': ['CONTRIB_LAYERS_CONV2D_BIASES'] + } + net = contrib_layers.conv2d( + x, + 3, + 3, + weights_initializer=w_initializer, + biases_initializer=b_initializer, + variables_collections=var_collection) + weight_vars = ops.get_collection('CONTRIB_LAYERS_CONV2D_WEIGHTS') + self.assertEquals(1, len(weight_vars)) + bias_vars = ops.get_collection('CONTRIB_LAYERS_CONV2D_BIASES') + self.assertEquals(1, len(bias_vars)) + expected_normalized_vars = { + 'contrib.layers.conv2d.weights': weight_vars[0] + } + expected_not_normalized_vars = { + 'contrib.layers.conv2d.bias': bias_vars[0] + } + + return net, expected_normalized_vars, expected_not_normalized_vars + + self._testLayerHelper(build_layer_fn, (3, 3, 3, 3), (3,)) + + def testConv2D_Slim(self): + + def build_layer_fn(x, w_initializer, b_initializer): + var_collection = { + 'weights': ['SLIM_CONV2D_WEIGHTS'], + 'biases': ['SLIM_CONV2D_BIASES'] + } + net = slim.conv2d( + x, + 3, + 3, + weights_initializer=w_initializer, + biases_initializer=b_initializer, + variables_collections=var_collection) + weight_vars = ops.get_collection('SLIM_CONV2D_WEIGHTS') + self.assertEquals(1, len(weight_vars)) + bias_vars = ops.get_collection('SLIM_CONV2D_BIASES') + self.assertEquals(1, len(bias_vars)) + expected_normalized_vars = {'slim.conv2d.weights': weight_vars[0]} + expected_not_normalized_vars = {'slim.conv2d.bias': bias_vars[0]} + + return net, expected_normalized_vars, expected_not_normalized_vars + + self._testLayerHelper(build_layer_fn, (3, 3, 3, 3), (3,)) + + def testConv2D_Keras(self): + + def build_layer_fn(x, w_initializer, b_initializer): + layer = keras_convolutional.Conv2D( + filters=3, + kernel_size=3, + padding='same', + kernel_initializer=w_initializer, + bias_initializer=b_initializer) + net = layer.apply(x) + expected_normalized_vars = {'keras.layers.Conv2d.kernel': layer.kernel} + expected_not_normalized_vars = {'keras.layers.Conv2d.bias': layer.bias} + + return net, expected_normalized_vars, expected_not_normalized_vars + + self._testLayerHelper(build_layer_fn, (3, 3, 3, 3), (3,), is_keras=True) + + def testFC_Layers(self): + + def build_layer_fn(x, w_initializer, b_initializer): + x = layers_core.Flatten()(x) + layer = layers_core.Dense( + units=3, + kernel_initializer=w_initializer, + bias_initializer=b_initializer) + net = layer.apply(x) + expected_normalized_vars = {'tf.layers.Dense.kernel': layer.kernel} + expected_not_normalized_vars = {'tf.layers.Dense.bias': layer.bias} + + return net, expected_normalized_vars, expected_not_normalized_vars + + self._testLayerHelper(build_layer_fn, (300, 3), (3,)) + + def testFC_ContribLayers(self): + + def build_layer_fn(x, w_initializer, b_initializer): + var_collection = { + 'weights': ['CONTRIB_LAYERS_FC_WEIGHTS'], + 'biases': ['CONTRIB_LAYERS_FC_BIASES'] + } + x = contrib_layers.flatten(x) + net = contrib_layers.fully_connected( + x, + 3, + weights_initializer=w_initializer, + biases_initializer=b_initializer, + variables_collections=var_collection) + weight_vars = ops.get_collection('CONTRIB_LAYERS_FC_WEIGHTS') + self.assertEquals(1, len(weight_vars)) + bias_vars = ops.get_collection('CONTRIB_LAYERS_FC_BIASES') + self.assertEquals(1, len(bias_vars)) + expected_normalized_vars = { + 'contrib.layers.fully_connected.weights': weight_vars[0] + } + expected_not_normalized_vars = { + 'contrib.layers.fully_connected.bias': bias_vars[0] + } + + return net, expected_normalized_vars, expected_not_normalized_vars + + self._testLayerHelper(build_layer_fn, (300, 3), (3,)) + + def testFC_Slim(self): + + def build_layer_fn(x, w_initializer, b_initializer): + var_collection = { + 'weights': ['SLIM_FC_WEIGHTS'], + 'biases': ['SLIM_FC_BIASES'] + } + x = slim.flatten(x) + net = slim.fully_connected( + x, + 3, + weights_initializer=w_initializer, + biases_initializer=b_initializer, + variables_collections=var_collection) + weight_vars = ops.get_collection('SLIM_FC_WEIGHTS') + self.assertEquals(1, len(weight_vars)) + bias_vars = ops.get_collection('SLIM_FC_BIASES') + self.assertEquals(1, len(bias_vars)) + expected_normalized_vars = { + 'slim.fully_connected.weights': weight_vars[0] + } + expected_not_normalized_vars = {'slim.fully_connected.bias': bias_vars[0]} + + return net, expected_normalized_vars, expected_not_normalized_vars + + self._testLayerHelper(build_layer_fn, (300, 3), (3,)) + + def testFC_Keras(self): + + def build_layer_fn(x, w_initializer, b_initializer): + x = keras_core.Flatten()(x) + layer = keras_core.Dense( + units=3, + kernel_initializer=w_initializer, + bias_initializer=b_initializer) + net = layer.apply(x) + expected_normalized_vars = {'keras.layers.Dense.kernel': layer.kernel} + expected_not_normalized_vars = {'keras.layers.Dense.bias': layer.bias} + + return net, expected_normalized_vars, expected_not_normalized_vars + + self._testLayerHelper(build_layer_fn, (300, 3), (3,), is_keras=True) + + +if __name__ == '__main__': + test.main() -- GitLab From 41c30afbfb6a752f09f9b26db807b1a1ac494e8a Mon Sep 17 00:00:00 2001 From: Karim Nosir Date: Thu, 3 Jan 2019 12:07:50 -0800 Subject: [PATCH 297/622] Enable kernel tests for ios. PiperOrigin-RevId: 227727388 --- tensorflow/core/BUILD | 9 +- tensorflow/lite/kernels/BUILD | 163 ------------------ .../lite/kernels/batch_to_space_nd_test.cc | 2 + tensorflow/lite/kernels/pad_test.cc | 8 + tensorflow/lite/kernels/reshape_test.cc | 10 ++ .../lite/kernels/space_to_batch_nd_test.cc | 4 + .../lite/kernels/space_to_depth_test.cc | 2 + tensorflow/lite/kernels/strided_slice_test.cc | 2 + tensorflow/lite/kernels/transpose_test.cc | 4 + 9 files changed, 39 insertions(+), 165 deletions(-) diff --git a/tensorflow/core/BUILD b/tensorflow/core/BUILD index f9552d5965..6b32e1fead 100644 --- a/tensorflow/core/BUILD +++ b/tensorflow/core/BUILD @@ -2348,7 +2348,12 @@ cc_library( cc_library( name = "tflite_portable_logging", - srcs = [], + srcs = [ + ] + if_ios([ + "platform/default/logging.cc", + "platform/env_time.cc", + "platform/posix/env_time.cc", + ]), hdrs = [ "lib/bfloat16/bfloat16.h", "platform/default/integral_types.h", @@ -2357,7 +2362,7 @@ cc_library( "platform/macros.h", "platform/platform.h", "platform/types.h", - ] + if_windows(["platform/windows/integral_types.h"]), + ] + if_windows(["platform/windows/integral_types.h"]) + if_ios(["platform/env_time.h"]), copts = tf_copts(), linkopts = ["-ldl"], deps = [ diff --git a/tensorflow/lite/kernels/BUILD b/tensorflow/lite/kernels/BUILD index 5cc06c7a63..9abc33d297 100644 --- a/tensorflow/lite/kernels/BUILD +++ b/tensorflow/lite/kernels/BUILD @@ -25,9 +25,6 @@ tf_cc_test( name = "optional_tensor_test", size = "small", srcs = ["optional_tensor_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -122,9 +119,6 @@ tf_cc_test( name = "kernel_util_test", size = "small", srcs = ["kernel_util_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":kernel_util", "//tensorflow/lite/testing:util", @@ -136,9 +130,6 @@ tf_cc_test( name = "test_util_test", size = "small", srcs = ["test_util_test.cc"], - tags = [ - "tflite_not_portable_ios", # TODO(b/117786830) - ], deps = [ ":test_util", "//tensorflow/lite/testing:util", @@ -304,9 +295,6 @@ tf_cc_test( name = "audio_spectrogram_test", size = "small", srcs = ["audio_spectrogram_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -320,9 +308,6 @@ tf_cc_test( name = "mfcc_test", size = "small", srcs = ["mfcc_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -336,9 +321,6 @@ tf_cc_test( name = "detection_postprocess_test", size = "small", srcs = ["detection_postprocess_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -352,9 +334,6 @@ tf_cc_test( name = "relu1_test", size = "small", srcs = ["relu1_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -368,9 +347,6 @@ tf_cc_test( name = "sparse_output_fully_connected_test", size = "small", srcs = ["sparse_output_fully_connected_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -384,7 +360,6 @@ tf_cc_test( name = "activations_test", size = "small", srcs = ["activations_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -397,7 +372,6 @@ tf_cc_test( name = "add_test", size = "small", srcs = ["add_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -410,9 +384,6 @@ tf_cc_test( name = "arg_min_max_test", size = "small", srcs = ["arg_min_max_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -425,9 +396,6 @@ tf_cc_test( name = "div_test", size = "small", srcs = ["div_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -440,9 +408,6 @@ tf_cc_test( name = "sub_test", size = "small", srcs = ["sub_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -455,9 +420,6 @@ tf_cc_test( name = "transpose_test", size = "small", srcs = ["transpose_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -472,9 +434,6 @@ tf_cc_test( name = "space_to_batch_nd_test", size = "small", srcs = ["space_to_batch_nd_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -487,9 +446,6 @@ tf_cc_test( name = "batch_to_space_nd_test", size = "small", srcs = ["batch_to_space_nd_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -502,9 +458,6 @@ tf_cc_test( name = "cast_test", size = "small", srcs = ["cast_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -517,7 +470,6 @@ tf_cc_test( name = "concatenation_test", size = "small", srcs = ["concatenation_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -530,7 +482,6 @@ tf_cc_test( name = "conv_test", size = "small", srcs = ["conv_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -544,7 +495,6 @@ tf_cc_test( name = "depthwise_conv_test", size = "small", srcs = ["depthwise_conv_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -558,9 +508,6 @@ tf_cc_test( name = "dequantize_test", size = "small", srcs = ["dequantize_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -574,7 +521,6 @@ tf_cc_test( name = "basic_rnn_test", size = "small", srcs = ["basic_rnn_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -587,9 +533,6 @@ tf_cc_test( name = "bidirectional_sequence_lstm_test", size = "small", srcs = ["bidirectional_sequence_lstm_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -603,9 +546,6 @@ tf_cc_test( name = "floor_test", size = "small", srcs = ["floor_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -618,9 +558,6 @@ tf_cc_test( name = "elementwise_test", size = "small", srcs = ["elementwise_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -633,9 +570,6 @@ tf_cc_test( name = "unidirectional_sequence_lstm_test", size = "small", srcs = ["unidirectional_sequence_lstm_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -663,9 +597,6 @@ tf_cc_test( name = "unidirectional_sequence_rnn_test", size = "small", srcs = ["unidirectional_sequence_rnn_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -678,7 +609,6 @@ tf_cc_test( name = "l2norm_test", size = "small", srcs = ["l2norm_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -691,9 +621,6 @@ tf_cc_test( name = "exp_test", size = "small", srcs = ["exp_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -706,9 +633,6 @@ tf_cc_test( name = "fake_quant_test", size = "small", srcs = ["fake_quant_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -721,9 +645,6 @@ tf_cc_test( name = "maximum_minimum_test", size = "small", srcs = ["maximum_minimum_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -736,9 +657,6 @@ tf_cc_test( name = "reduce_test", size = "small", srcs = ["reduce_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -751,7 +669,6 @@ tf_cc_test( name = "mul_test", size = "small", srcs = ["mul_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -764,9 +681,6 @@ tf_cc_test( name = "pad_test", size = "small", srcs = ["pad_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -779,7 +693,6 @@ tf_cc_test( name = "reshape_test", size = "small", srcs = ["reshape_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -792,9 +705,6 @@ tf_cc_test( name = "gather_test", size = "small", srcs = ["gather_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -808,9 +718,6 @@ tf_cc_test( name = "topk_v2_test", size = "small", srcs = ["topk_v2_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -824,7 +731,6 @@ tf_cc_test( name = "resize_bilinear_test", size = "small", srcs = ["resize_bilinear_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -837,7 +743,6 @@ tf_cc_test( name = "resize_nearest_neighbor_test", size = "small", srcs = ["resize_nearest_neighbor_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -850,7 +755,6 @@ tf_cc_test( name = "svdf_test", size = "small", srcs = ["svdf_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -863,7 +767,6 @@ tf_cc_test( name = "embedding_lookup_test", size = "small", srcs = ["embedding_lookup_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -876,7 +779,6 @@ tf_cc_test( name = "embedding_lookup_sparse_test", size = "small", srcs = ["embedding_lookup_sparse_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -889,7 +791,6 @@ tf_cc_test( name = "fully_connected_test", size = "small", srcs = ["fully_connected_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -904,7 +805,6 @@ tf_cc_test( name = "local_response_norm_test", size = "small", srcs = ["local_response_norm_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -917,7 +817,6 @@ tf_cc_test( name = "pooling_test", size = "small", srcs = ["pooling_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -930,7 +829,6 @@ tf_cc_test( name = "softmax_test", size = "small", srcs = ["softmax_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -944,9 +842,6 @@ tf_cc_test( name = "log_softmax_test", size = "small", srcs = ["log_softmax_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -960,7 +855,6 @@ tf_cc_test( name = "lsh_projection_test", size = "small", srcs = ["lsh_projection_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -973,7 +867,6 @@ tf_cc_test( name = "hashtable_lookup_test", size = "small", srcs = ["hashtable_lookup_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -987,7 +880,6 @@ tf_cc_test( name = "layer_norm_lstm_test", size = "small", srcs = ["layer_norm_lstm_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -1001,7 +893,6 @@ tf_cc_test( name = "lstm_test", size = "small", srcs = ["lstm_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -1014,7 +905,6 @@ tf_cc_test( name = "skip_gram_test", size = "small", srcs = ["skip_gram_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -1028,7 +918,6 @@ tf_cc_test( name = "space_to_depth_test", size = "small", srcs = ["space_to_depth_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -1041,9 +930,6 @@ tf_cc_test( name = "split_test", size = "small", srcs = ["split_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -1056,9 +942,6 @@ tf_cc_test( name = "split_v_test", size = "small", srcs = ["split_v_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -1071,9 +954,6 @@ tf_cc_test( name = "squeeze_test", size = "small", srcs = ["squeeze_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -1086,9 +966,6 @@ tf_cc_test( name = "strided_slice_test", size = "small", srcs = ["strided_slice_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -1101,9 +978,6 @@ tf_cc_test( name = "tile_test", size = "small", srcs = ["tile_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -1119,9 +993,6 @@ tf_cc_test( srcs = [ "comparisons_test.cc", ], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -1134,9 +1005,6 @@ tf_cc_test( name = "neg_test", size = "small", srcs = ["neg_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -1151,9 +1019,6 @@ tf_cc_test( srcs = [ "select_test.cc", ], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -1168,9 +1033,6 @@ tf_cc_test( srcs = [ "slice_test.cc", ], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -1183,9 +1045,6 @@ tf_cc_test( name = "transpose_conv_test", size = "small", srcs = ["transpose_conv_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -1199,9 +1058,6 @@ tf_cc_test( name = "expand_dims_test", size = "small", srcs = ["expand_dims_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -1215,9 +1071,6 @@ tf_cc_test( name = "sparse_to_dense_test", size = "small", srcs = ["sparse_to_dense_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -1231,9 +1084,6 @@ tf_cc_test( name = "shape_test", size = "small", srcs = ["shape_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -1247,9 +1097,6 @@ tf_cc_test( name = "pow_test", size = "small", srcs = ["pow_test.cc"], - tags = [ - "tflite_not_portable_ios", - ], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -1263,7 +1110,6 @@ tf_cc_test( name = "pack_test", size = "small", srcs = ["pack_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -1277,7 +1123,6 @@ tf_cc_test( name = "one_hot_test", size = "small", srcs = ["one_hot_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -1290,7 +1135,6 @@ tf_cc_test( name = "logical_test", size = "small", srcs = ["logical_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -1304,7 +1148,6 @@ tf_cc_test( name = "unpack_test", size = "small", srcs = ["unpack_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:builtin_op_data", @@ -1318,7 +1161,6 @@ tf_cc_test( name = "floor_div_test", size = "small", srcs = ["floor_div_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:builtin_op_data", @@ -1332,7 +1174,6 @@ tf_cc_test( name = "zeros_like_test", size = "small", srcs = ["zeros_like_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:builtin_op_data", @@ -1346,7 +1187,6 @@ tf_cc_test( name = "floor_mod_test", size = "small", srcs = ["floor_mod_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:builtin_op_data", @@ -1360,7 +1200,6 @@ tf_cc_test( name = "range_test", size = "small", srcs = ["range_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:builtin_op_data", @@ -1374,7 +1213,6 @@ tf_cc_test( name = "squared_difference_test", size = "small", srcs = ["squared_difference_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", @@ -1387,7 +1225,6 @@ tf_cc_test( name = "fill_test", size = "small", srcs = ["fill_test.cc"], - tags = ["tflite_not_portable_ios"], deps = [ ":builtin_ops", "//tensorflow/lite:framework", diff --git a/tensorflow/lite/kernels/batch_to_space_nd_test.cc b/tensorflow/lite/kernels/batch_to_space_nd_test.cc index a3e06d4c89..f330895599 100644 --- a/tensorflow/lite/kernels/batch_to_space_nd_test.cc +++ b/tensorflow/lite/kernels/batch_to_space_nd_test.cc @@ -114,6 +114,7 @@ TEST(BatchToSpaceNDOpTest, SimpleDynamicTest) { 4, 8, 11, 15, 12, 16})); } +#ifdef GTEST_HAS_DEATH_TEST TEST(BatchToSpaceNDOpTest, InvalidShapeTest) { EXPECT_DEATH(BatchToSpaceNDOpConstModel({3, 2, 2, 1}, {2, 2}, {0, 0, 0, 0}), "Cannot allocate tensors"); @@ -131,6 +132,7 @@ TEST(BatchToSpaceNDOpTest, InvalidCropsDynamicTest) { m.SetCrops({0, 0, -1, 0}); EXPECT_DEATH(m.Invoke(), "crops.2. >= 0 was not true."); } +#endif } // namespace } // namespace tflite diff --git a/tensorflow/lite/kernels/pad_test.cc b/tensorflow/lite/kernels/pad_test.cc index 415a285c70..3caa4065dc 100644 --- a/tensorflow/lite/kernels/pad_test.cc +++ b/tensorflow/lite/kernels/pad_test.cc @@ -175,6 +175,7 @@ class PadOpDynamicModel : public PadOpModel { } }; +#ifdef GTEST_HAS_DEATH_TEST TEST(PadOpTest, TooManyDimensions) { EXPECT_DEATH( PadOpConstModel({TensorType_FLOAT32, {1, 2, 3, 4, 5, 6, 7, 8, 9}}, {9, 2}, @@ -195,6 +196,7 @@ TEST(PadOpTest, InvalidPadValue) { {0, 0, 1, -1, 2, -1, 0, 0}, {TensorType_FLOAT32}), "Pad value has to be greater than equal to 0."); } +#endif TEST(PadOpTest, SimpleConstTest) { // Padding is represented as four 2-D lists representing above padding and @@ -306,6 +308,7 @@ class QuantizedPadOpTest : public ::testing::Test { } }; +#ifdef GTEST_HAS_DEATH_TEST TEST_F(QuantizedPadOpTest, ZeroNotInQuantizationRange) { // The test_util and actual quantization code currently ensure that the range // must include zero, but if that ever changes, this test will catch it. @@ -314,6 +317,7 @@ TEST_F(QuantizedPadOpTest, ZeroNotInQuantizationRange) { {TensorType_UINT8, {}, 1.0, 2.0}), ".*Check failed: f_min <= 0.*"); } +#endif TEST_F(QuantizedPadOpTest, SimpleConstTest) { // Padding is represented as four 2-D lists representing above padding and @@ -371,6 +375,7 @@ TEST_F(QuantizedPadOpTest, AdvancedDynamicTest) { EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({1, 4, 7, 1})); } +#ifdef GTEST_HAS_DEATH_TEST TEST(PadV2OpTest, TooManyDimensions) { EXPECT_DEATH(PadV2OpConstModel( {TensorType_FLOAT32, {1, 2, 3, 4, 5, 6, 7, 8, 9}}, {9, 2}, @@ -392,6 +397,7 @@ TEST(PadV2OpTest, InvalidPadValue) { {TensorType_FLOAT32}), "Pad value has to be greater than equal to 0."); } +#endif TEST(PadV2OpTest, SimpleConstTest) { // Padding is represented as four 2-D lists representing above padding and @@ -495,6 +501,7 @@ class QuantizedPadV2OpTest : public ::testing::Test { } }; +#ifdef GTEST_HAS_DEATH_TEST TEST_F(QuantizedPadV2OpTest, ZeroNotInQuantizationRange) { // The test_util and actual quantization code currently ensure that the range // must include zero, but if that ever changes, this test will catch it. @@ -504,6 +511,7 @@ TEST_F(QuantizedPadV2OpTest, ZeroNotInQuantizationRange) { {TensorType_UINT8, {}, 1.0, 2.0}), ".*Check failed: f_min <= 0.*"); } +#endif TEST_F(QuantizedPadV2OpTest, SimpleConstTest) { // Padding is represented as four 2-D lists representing above padding and diff --git a/tensorflow/lite/kernels/reshape_test.cc b/tensorflow/lite/kernels/reshape_test.cc index 00bbbef57e..f98f3eb9ae 100644 --- a/tensorflow/lite/kernels/reshape_test.cc +++ b/tensorflow/lite/kernels/reshape_test.cc @@ -123,6 +123,7 @@ class ReshapeOpModel : public SingleOpModel { int output_; }; +#ifdef GTEST_HAS_DEATH_TEST TEST_P(ReshapeOpTest, MismatchedDimensions) { if (GetParam() == kAsTensor) { ReshapeOpModel m({1, 2, 4, 1}, {2}, {2, 1}, GetParam()); @@ -133,12 +134,15 @@ TEST_P(ReshapeOpTest, MismatchedDimensions) { "num_input_elements != num_output_elements"); } } +#endif TEST_P(ReshapeOpTest, TooManyDimensions) { if (GetParam() == kAsReshapeOption) { +#ifdef GTEST_HAS_DEATH_TEST EXPECT_DEATH(ReshapeOpModel({1, 1, 2, 1, 1, 1, 1, 1, 1}, {9}, {1, 1, 1, 1, 1, 1, 1, 1, 2}, GetParam()), "Found too many dimensions"); +#endif } else { ReshapeOpModel m({1, 1, 2, 1, 1, 1, 1, 1, 1}, {9}, {1, 1, 1, 1, 1, 1, 1, 1, 2}, GetParam()); @@ -150,6 +154,7 @@ TEST_P(ReshapeOpTest, TooManyDimensions) { } } +#ifdef GTEST_HAS_DEATH_TEST TEST_P(ReshapeOpTest, TooManySpecialDimensions) { if (GetParam() != kAsTensor) { EXPECT_DEATH( @@ -160,6 +165,7 @@ TEST_P(ReshapeOpTest, TooManySpecialDimensions) { EXPECT_DEATH(m.Invoke(), "stretch_dim != -1"); } } +#endif // Create the model with a 2x2 shape. Processing still works because the new // shape ends up being hardcoded as a flat vector. @@ -202,12 +208,16 @@ TEST_P(ReshapeOpTest, ScalarOutput) { // and output are scalars. TEST_P(ReshapeOpTest, LegacyScalarOutput) { if (GetParam() == kAsConstantTensor) { +#ifdef GTEST_HAS_DEATH_TEST EXPECT_DEATH(ReshapeOpModel({1}, {1}, {0}, GetParam()), "num_input_elements != num_output_elements"); +#endif } else if (GetParam() == kAsTensor) { +#ifdef GTEST_HAS_DEATH_TEST ReshapeOpModel m({1}, {1}, {0}, GetParam()); m.SetInput({3}); EXPECT_DEATH(m.Invoke(), "num_input_elements != num_output_elements"); +#endif } else { ReshapeOpModel m({1}, {1}, {0}, GetParam()); m.SetInput({3}); diff --git a/tensorflow/lite/kernels/space_to_batch_nd_test.cc b/tensorflow/lite/kernels/space_to_batch_nd_test.cc index 4d55ba56b7..c5d6e9a530 100644 --- a/tensorflow/lite/kernels/space_to_batch_nd_test.cc +++ b/tensorflow/lite/kernels/space_to_batch_nd_test.cc @@ -106,12 +106,14 @@ class SpaceToBatchNDOpDynamicModel : public SpaceToBatchNDOpModel { } }; +#ifdef GTEST_HAS_DEATH_TEST TEST(SpaceToBatchNDOpTest, InvalidShapeTest) { EXPECT_DEATH( SpaceToBatchNDOpConstModel({TensorType_FLOAT32, {1, 3, 3, 1}}, {2, 2}, {0, 0, 0, 0}, {TensorType_FLOAT32}), "Cannot allocate tensors"); } +#endif TEST(SpaceToBatchNDOpTest, SimpleConstTest) { SpaceToBatchNDOpConstModel m({TensorType_FLOAT32, {1, 4, 4, 1}}, {2, 2}, @@ -220,6 +222,7 @@ class QuantizedSpaceToBatchNDOpTest : public ::testing::Test { } }; +#ifdef GTEST_HAS_DEATH_TEST TEST_F(QuantizedSpaceToBatchNDOpTest, ZeroNotInQuantizationRange) { // The test_util and actual quantization code currently ensure that the range // must include zero, but if that ever changes, this test will catch it. @@ -228,6 +231,7 @@ TEST_F(QuantizedSpaceToBatchNDOpTest, ZeroNotInQuantizationRange) { {0, 0, 1, 1, 1, 1, 0, 0}, {TensorType_UINT8, {}, 1.0, 2.0}), ".*Check failed: f_min <= 0.*"); } +#endif TEST_F(QuantizedSpaceToBatchNDOpTest, SimplePaddingConstTest) { SpaceToBatchNDOpConstModel m({TensorType_UINT8, {1, 5, 2, 1}, -1.0, 1.0}, diff --git a/tensorflow/lite/kernels/space_to_depth_test.cc b/tensorflow/lite/kernels/space_to_depth_test.cc index 5744669b6d..3fa8d86348 100644 --- a/tensorflow/lite/kernels/space_to_depth_test.cc +++ b/tensorflow/lite/kernels/space_to_depth_test.cc @@ -50,10 +50,12 @@ class SpaceToDepthOpModel : public SingleOpModel { int output_; }; +#ifdef GTEST_HAS_DEATH_TEST TEST(SpaceToDepthOpModel, BadBlockSize) { EXPECT_DEATH(SpaceToDepthOpModel({TensorType_FLOAT32, {1, 2, 2, 1}}, 3), "Cannot allocate tensors"); } +#endif TEST(SpaceToDepthOpModel, Float32) { SpaceToDepthOpModel m({TensorType_FLOAT32, {1, 2, 2, 2}}, 2); diff --git a/tensorflow/lite/kernels/strided_slice_test.cc b/tensorflow/lite/kernels/strided_slice_test.cc index 122e01b99e..34875bf049 100644 --- a/tensorflow/lite/kernels/strided_slice_test.cc +++ b/tensorflow/lite/kernels/strided_slice_test.cc @@ -72,6 +72,7 @@ class StridedSliceOpModel : public SingleOpModel { int output_; }; +#ifdef GTEST_HAS_DEATH_TEST TEST(StridedSliceOpTest, UnsupportedInputSize) { EXPECT_DEATH( StridedSliceOpModel<>({2, 2, 2, 2, 2}, {5}, {5}, {5}, 0, 0, 0, 0, 0), @@ -84,6 +85,7 @@ TEST(StridedSliceOpTest, UnssupportedArgs) { EXPECT_DEATH(StridedSliceOpModel<>({3, 2}, {2}, {2}, {2}, 0, 0, 0, 1, 0), "new_axis_mask is not implemented yet."); } +#endif TEST(StridedSliceOpTest, In1D) { StridedSliceOpModel<> m({4}, {1}, {1}, {1}, 0, 0, 0, 0, 0); diff --git a/tensorflow/lite/kernels/transpose_test.cc b/tensorflow/lite/kernels/transpose_test.cc index 3ebaf3ca27..93df2c81db 100644 --- a/tensorflow/lite/kernels/transpose_test.cc +++ b/tensorflow/lite/kernels/transpose_test.cc @@ -184,6 +184,7 @@ class TransposeOpDynamicModel : public TransposeOpModel { } }; +#ifdef GTEST_HAS_DEATH_TEST TEST(TransposeTest, TestUnequalPermSize) { EXPECT_DEATH(TransposeOpConstModel({1, 3, 3, 1}, {2}, {2, 2}), "2 != 4"); } @@ -194,6 +195,7 @@ TEST(TransposeTest, TestPermOutOfBounds) { EXPECT_DEATH(TransposeOpConstModel({1, 3, 3, 1}, {4}, {0, 1, 2, 4}), "Transpose op permutations array is out of bounds."); } +#endif TEST(TransposeTest, Test1DInputConstTensor) { TransposeOpConstModel m({3}, {1}, {0}); @@ -252,10 +254,12 @@ TEST(TransposeTest, Test3DInputDynamicTensor) { 2, 6, 10, 14, 18, 22, 3, 7, 11, 15, 19, 23})); } +#ifdef GTEST_HAS_DEATH_TEST TEST(TransposeTest, Test5DInputTensor) { EXPECT_DEATH(TransposeOpConstModel({1, 2, 3, 4, 5}, {5}, {0, 1, 2, 3, 4}), "Transpose op only supports 1D-4D input arrays."); } +#endif TEST(TransposeTest, SimpleTestNoReorderConstTensor) { TransposeOpConstModel m({1, 2, 3, 1}, {4}, {0, 1, 2, 3}); -- GitLab From aa36aa0a912a4addfb387e5fc0f759db225d03cc Mon Sep 17 00:00:00 2001 From: Davide Libenzi Date: Thu, 3 Jan 2019 12:26:05 -0800 Subject: [PATCH 298/622] Allow XRT release operations to support vectors of keys. PiperOrigin-RevId: 227730040 --- .../compiler/xrt/kernels/xrt_compile_ops.cc | 15 ++- .../compiler/xrt/kernels/xrt_state_ops.h | 16 +-- .../compiler/xrt/ops/xrt_compile_ops.cc | 6 +- tensorflow/compiler/xrt/ops/xrt_state_ops.cc | 5 +- tensorflow/compiler/xrt/tests/raw_api_test.cc | 98 ++++++++++++++++++- 5 files changed, 117 insertions(+), 23 deletions(-) diff --git a/tensorflow/compiler/xrt/kernels/xrt_compile_ops.cc b/tensorflow/compiler/xrt/kernels/xrt_compile_ops.cc index 2ccdf0f02d..2ee1a6cd1a 100644 --- a/tensorflow/compiler/xrt/kernels/xrt_compile_ops.cc +++ b/tensorflow/compiler/xrt/kernels/xrt_compile_ops.cc @@ -215,11 +215,6 @@ XRTReleaseCompilationRefOp::~XRTReleaseCompilationRefOp() = default; void XRTReleaseCompilationRefOp::Compute(OpKernelContext* ctx) { VLOG(1) << "XRTReleaseCompilationRefOp::Compute"; - const Tensor& key_tensor = ctx->input(0); - OP_REQUIRES(ctx, TensorShapeUtils::IsScalar(key_tensor.shape()), - errors::Internal("computation key should be a string scalar")); - int64 uid = key_tensor.scalar()(); - ResourceMgr* rm; OP_REQUIRES_OK(ctx, XRTGenericDeviceAccessor::GetResourceManager(ctx, &rm)); @@ -230,9 +225,13 @@ void XRTReleaseCompilationRefOp::Compute(OpKernelContext* ctx) { kXRTCompilationCacheResourceName, &cache)); core::ScopedUnref cache_unref(cache); - OP_REQUIRES_OK(ctx, cache->Release(uid)); - - VLOG(2) << "Released computation handle " << uid; + const Tensor& keys_tensor = ctx->input(0); + auto flat_keys = keys_tensor.flat(); + for (int64 i = 0; i < flat_keys.size(); ++i) { + int64 key = flat_keys(i); + OP_REQUIRES_OK(ctx, cache->Release(key)); + VLOG(2) << "Released computation handle " << key; + } } } // namespace diff --git a/tensorflow/compiler/xrt/kernels/xrt_state_ops.h b/tensorflow/compiler/xrt/kernels/xrt_state_ops.h index 2e2f3ff116..c8def16bbc 100644 --- a/tensorflow/compiler/xrt/kernels/xrt_state_ops.h +++ b/tensorflow/compiler/xrt/kernels/xrt_state_ops.h @@ -453,17 +453,17 @@ class XRTReleaseAllocationOp : public OpKernel { void Compute(OpKernelContext* ctx) override { VLOG(1) << "XRTReleaseAllocationOp::Compute"; - const Tensor& allocation_handle = ctx->input(0); - OP_REQUIRES(ctx, TensorShapeUtils::IsScalar(allocation_handle.shape()), - errors::Internal("handle input should be an int64 scalar")); - int64 key = allocation_handle.scalar()(); - ResourceMgr* rm; OP_REQUIRES_OK(ctx, DeviceAccessor::GetResourceManager(ctx, &rm)); - OP_REQUIRES_OK(ctx, XRTTupleAllocation::DeleteFromResourceManager(rm, key)); - - VLOG(2) << "Released allocation handle " << key; + const Tensor& allocation_handle = ctx->input(0); + auto flat_keys = allocation_handle.flat(); + for (int64 i = 0; i < flat_keys.size(); ++i) { + int64 key = flat_keys(i); + OP_REQUIRES_OK(ctx, + XRTTupleAllocation::DeleteFromResourceManager(rm, key)); + VLOG(2) << "Released allocation handle " << key; + } } }; diff --git a/tensorflow/compiler/xrt/ops/xrt_compile_ops.cc b/tensorflow/compiler/xrt/ops/xrt_compile_ops.cc index 7b3b50c695..9dd964e546 100644 --- a/tensorflow/compiler/xrt/ops/xrt_compile_ops.cc +++ b/tensorflow/compiler/xrt/ops/xrt_compile_ops.cc @@ -44,10 +44,10 @@ REGISTER_OP("XRTReleaseCompilationHandle") .SetShapeFn(tensorflow::shape_inference::NoOutputs) .Doc( R"( -Discards a computation from the compilation cache. The handle cannot be -subsequently used. +Discards one or more computation handles from the compilation cache. +The handle(s) cannot be subsequently used. -'handle' is an id returned from a XRTCompile Op. +'handle' is an ID (or vector of IDs) returned from a XRTCompile Op. )"); } // namespace tensorflow diff --git a/tensorflow/compiler/xrt/ops/xrt_state_ops.cc b/tensorflow/compiler/xrt/ops/xrt_state_ops.cc index fe6bee0dac..db58f0797d 100644 --- a/tensorflow/compiler/xrt/ops/xrt_state_ops.cc +++ b/tensorflow/compiler/xrt/ops/xrt_state_ops.cc @@ -127,10 +127,11 @@ REGISTER_OP("XRTReleaseAllocationHandle") .SetShapeFn(tensorflow::shape_inference::NoOutputs) .Doc( R"( -Discards an allocation from device memory. The handle cannot be subsequently +Discards one or more device memory handles. The handle(s) cannot be subsequently used. -'handle' is the id returned from the Op that produced the on-device allocation. +'handle' is the ID (or a vector of IDs) returned from the Op that produced the +on-device allocation. )"); REGISTER_OP("XRTReleaseAllAllocations") diff --git a/tensorflow/compiler/xrt/tests/raw_api_test.cc b/tensorflow/compiler/xrt/tests/raw_api_test.cc index 5f8121703e..c8479cb778 100644 --- a/tensorflow/compiler/xrt/tests/raw_api_test.cc +++ b/tensorflow/compiler/xrt/tests/raw_api_test.cc @@ -258,8 +258,102 @@ TEST(RawApiTest, AllocAndRewrite) { EXPECT_TRUE(new_response.ParseFromString(outputs[0].scalar()())); EXPECT_TRUE(CompareLiteralProtos(new_literal, new_response)); - auto release = - ops::XRTReleaseAllocationHandle(root, Input(allocation_handle)); + Tensor release_tensor(DT_INT64, TensorShape({1})); + release_tensor.flat()(0) = allocation_handle; + + auto release = ops::XRTReleaseAllocationHandle(root, release_tensor); + TF_EXPECT_OK(session.Run(tensorflow::ClientSession::FeedType(), {}, {release}, + &outputs)); +} + +TEST(RawApiTest, AllocReleaseMany) { + xrt::XLAAllocation alloc1; + *alloc1.mutable_value() = + xla::LiteralUtil::CreateR2({{4, 5}, {6, 7}}).ToProto(); + xrt::XLAAllocation alloc2; + *alloc2.mutable_value() = + xla::LiteralUtil::CreateR2({{6, 7}, {4, 5}}).ToProto(); + + Scope root = Scope::NewRootScope().WithDevice(DeviceFromFlag()); + auto value1 = + ops::Const(root.WithDevice("/device:CPU:0"), alloc1.SerializeAsString()); + auto value2 = + ops::Const(root.WithDevice("/device:CPU:0"), alloc2.SerializeAsString()); + auto handle1 = ops::XRTAllocate(root, value1); + auto handle2 = ops::XRTAllocate(root, value2); + TF_ASSERT_OK(root.status()); + + tensorflow::ClientSession session(root); + std::vector outputs; + TF_EXPECT_OK(session.Run({handle1, handle2}, &outputs)); + EXPECT_EQ(outputs.size(), 2); + + int64 allocation_handle1 = outputs[0].scalar()(); + int64 allocation_handle2 = outputs[1].scalar()(); + + Tensor release_tensor(DT_INT64, TensorShape({2})); + release_tensor.flat()(0) = allocation_handle1; + release_tensor.flat()(1) = allocation_handle2; + + auto release = ops::XRTReleaseAllocationHandle(root, release_tensor); + outputs.clear(); + TF_EXPECT_OK(session.Run(tensorflow::ClientSession::FeedType(), {}, {release}, + &outputs)); +} + +TEST(RawApiTest, CompileAndReleaseMany) { + xrt::XLAComputation c1; + auto config1 = c1.mutable_config(); + auto shapes1 = config1->mutable_program_shape(); + *shapes1->add_parameters() = + xla::ShapeUtil::MakeShape(xla::F32, {2}).ToProto(); + *shapes1->add_parameters() = + xla::ShapeUtil::MakeShape(xla::F32, {2}).ToProto(); + *shapes1->mutable_result() = + xla::ShapeUtil::MakeShape(xla::F32, {2}).ToProto(); + StoreComputationSnapshot(AddAndScale(), c1.mutable_hlo_snapshot()); + + xrt::XLAComputation c2; + auto config2 = c2.mutable_config(); + auto shapes2 = config2->mutable_program_shape(); + *shapes2->add_parameters() = + xla::ShapeUtil::MakeShape(xla::F32, {2}).ToProto(); + *shapes2->add_parameters() = + xla::ShapeUtil::MakeShape(xla::F32, {2}).ToProto(); + *shapes2->mutable_result() = + xla::ShapeUtil::MakeTupleShape({xla::ShapeUtil::MakeShape(xla::F32, {2})}) + .ToProto(); + StoreComputationSnapshot(AddAndTuple(), c2.mutable_hlo_snapshot()); + + xrt::XRTExecutionConfig e; + e.set_release_input_handles(true); + e.set_release_compilation_handle(false); + + Scope root = Scope::NewRootScope().WithDevice(DeviceFromFlag()); + auto e_config = + ops::Const(root.WithDevice("/device:CPU:0"), e.SerializeAsString()); + auto computation1 = + ops::Const(root.WithDevice("/device:CPU:0"), c1.SerializeAsString()); + auto c_handle1 = ops::XRTCompile(root, computation1); + auto computation2 = + ops::Const(root.WithDevice("/device:CPU:0"), c2.SerializeAsString()); + auto c_handle2 = ops::XRTCompile(root, computation2); + TF_ASSERT_OK(root.status()); + + ClientSession session(root); + std::vector outputs; + TF_EXPECT_OK(session.Run({c_handle1.handle, c_handle2.handle}, &outputs)); + EXPECT_EQ(outputs.size(), 2); + + int64 compilation_handle1 = outputs[0].scalar()(); + int64 compilation_handle2 = outputs[1].scalar()(); + + Tensor release_tensor(DT_INT64, TensorShape({2})); + release_tensor.flat()(0) = compilation_handle1; + release_tensor.flat()(1) = compilation_handle2; + + auto release = ops::XRTReleaseCompilationHandle(root, release_tensor); + outputs.clear(); TF_EXPECT_OK(session.Run(tensorflow::ClientSession::FeedType(), {}, {release}, &outputs)); } -- GitLab From cb4f2d41f22b6df49c80555a911d7dc2f08b7b90 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Thu, 3 Jan 2019 12:27:49 -0800 Subject: [PATCH 299/622] Fix autograph detection of tensors that are wrapped in python structures. A dict with tensor values did not register as containing a Tensor, causing the python branch to be taken instead of the tf branch. PiperOrigin-RevId: 227730234 --- tensorflow/python/autograph/operators/BUILD | 1 + .../autograph/operators/control_flow.py | 4 ++- .../autograph/operators/control_flow_test.py | 30 +++++++++++++------ 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/tensorflow/python/autograph/operators/BUILD b/tensorflow/python/autograph/operators/BUILD index aedb901845..21a66c86b7 100644 --- a/tensorflow/python/autograph/operators/BUILD +++ b/tensorflow/python/autograph/operators/BUILD @@ -38,6 +38,7 @@ py_library( "//tensorflow/python:list_ops", "//tensorflow/python:tensor_array_ops", "//tensorflow/python:tensor_util", + "//tensorflow/python:util", "//tensorflow/python:variables", "//tensorflow/python/autograph/utils", "//tensorflow/python/data/ops:dataset_ops", diff --git a/tensorflow/python/autograph/operators/control_flow.py b/tensorflow/python/autograph/operators/control_flow.py index afa3787d42..035ea1bd92 100644 --- a/tensorflow/python/autograph/operators/control_flow.py +++ b/tensorflow/python/autograph/operators/control_flow.py @@ -23,6 +23,7 @@ from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import tensor_util from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import gen_math_ops +from tensorflow.python.util import nest def for_stmt(iter_, extra_test, body, init_state): @@ -160,7 +161,8 @@ def while_stmt(test, body, init_state, extra_deps, opts=None): # TODO(mdan): Consider adding a generic mechanism for dynamic dispatch. # That could be something as simple as a collection of dispatch rules, with # some prioritization. - if any(tensor_util.is_tensor(v) for v in init_state + extra_deps): + if any(tensor_util.is_tensor(v) + for v in nest.flatten(init_state + extra_deps)): return _tf_while_stmt(test, body, init_state, opts) else: return _py_while_stmt(test, body, init_state, opts) diff --git a/tensorflow/python/autograph/operators/control_flow_test.py b/tensorflow/python/autograph/operators/control_flow_test.py index 0a7d4b6402..f9e006f7ad 100644 --- a/tensorflow/python/autograph/operators/control_flow_test.py +++ b/tensorflow/python/autograph/operators/control_flow_test.py @@ -36,7 +36,7 @@ class ForLoopTest(test.TestCase): extra_test=lambda s: True, body=lambda i, s: (s + i,), init_state=(0,)) - with self.cached_session() as sess: + with self.cached_session(): self.assertEqual((10,), self.evaluate(s)) def test_python(self): @@ -55,7 +55,7 @@ class ForLoopTest(test.TestCase): extra_test=lambda s: True, body=lambda i, s: (s + i,), init_state=(0,)) - with self.cached_session() as sess: + with self.cached_session(): self.assertEqual((10,), self.evaluate(s)) @@ -65,18 +65,30 @@ class WhileLoopTest(test.TestCase): def test_tensor(self): n = constant_op.constant(5) results = control_flow.while_stmt( - test=lambda i, s: i < n, - body=lambda i, s: (i + 1, s + i,), + test=lambda i, sum: i < n, + body=lambda i, sum: (i + 1, sum + i,), init_state=(0, 0), extra_deps=(n,)) - with self.cached_session() as sess: + with self.cached_session(): self.assertEqual((5, 10), self.evaluate(results)) + @test_util.run_deprecated_v1 + def test_tensor_dict_state(self): + n = 5 + init_state = {'i': constant_op.constant(0), 'sum': constant_op.constant(0)} + results = control_flow.while_stmt( + test=lambda s: s['i'] < n, + body=lambda s: ({'i': s['i'] + 1, 'sum': s['sum'] + s['i']},), + init_state=(init_state,), + extra_deps=()) + with self.cached_session(): + self.assertEqual(({'i': 5, 'sum': 10},), self.evaluate(results)) + def test_python(self): n = 5 results = control_flow.while_stmt( - test=lambda i, s: i < n, - body=lambda i, s: (i + 1, s + i), + test=lambda i, sum: i < n, + body=lambda i, sum: (i + 1, sum + i), init_state=(0, 0), extra_deps=(n,)) self.assertEqual((5, 10), results) @@ -93,7 +105,7 @@ class IfStmtTest(test.TestCase): @test_util.run_deprecated_v1 def test_tensor(self): - with self.cached_session() as sess: + with self.cached_session(): t = self.single_return_if_stmt(constant_op.constant(True)) self.assertEqual(1, self.evaluate(t)) t = self.single_return_if_stmt(constant_op.constant(False)) @@ -105,7 +117,7 @@ class IfStmtTest(test.TestCase): @test_util.run_deprecated_v1 def test_tensor_multiple_returns(self): - with self.cached_session() as sess: + with self.cached_session(): t = self.multi_return_if_stmt(constant_op.constant(True)) self.assertAllEqual([1, 2], self.evaluate(t)) t = self.multi_return_if_stmt(constant_op.constant(False)) -- GitLab From 7e2d53c1c371f38c7f0ef13c1c06336b22a195c0 Mon Sep 17 00:00:00 2001 From: Shivani Agrawal Date: Thu, 3 Jan 2019 12:46:47 -0800 Subject: [PATCH 300/622] [tf.data] Adds the expected check for better debugging. PiperOrigin-RevId: 227733165 --- tensorflow/core/kernels/data/experimental/scan_dataset_op.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tensorflow/core/kernels/data/experimental/scan_dataset_op.cc b/tensorflow/core/kernels/data/experimental/scan_dataset_op.cc index 76ab33fe98..87263b5606 100644 --- a/tensorflow/core/kernels/data/experimental/scan_dataset_op.cc +++ b/tensorflow/core/kernels/data/experimental/scan_dataset_op.cc @@ -186,6 +186,8 @@ class ScanDatasetOp : public UnaryDatasetOpKernel { Status s = instantiated_captured_func_->Run(ctx, std::move(args), &state_and_output); + DCHECK(state_and_output.size() <= + dataset()->state_types_.size() + output_dtypes().size()); if (s.ok()) { state_.clear(); size_t i = 0; -- GitLab From f9d4786119cda668d9b05d4ae49f2a4fe8d5738f Mon Sep 17 00:00:00 2001 From: Andrew Selle Date: Thu, 3 Jan 2019 13:01:24 -0800 Subject: [PATCH 301/622] Make tests all have their own main so initialization works. PiperOrigin-RevId: 227735303 --- tensorflow/lite/toco/BUILD | 12 ++++++++---- tensorflow/lite/toco/import_tensorflow_test.cc | 7 +++++++ tensorflow/lite/toco/toco_convert_test.cc | 7 +++++++ tensorflow/lite/toco/toco_port_test.cc | 7 +++++++ tensorflow/lite/toco/tooling_util_test.cc | 9 ++++++++- 5 files changed, 37 insertions(+), 5 deletions(-) diff --git a/tensorflow/lite/toco/BUILD b/tensorflow/lite/toco/BUILD index 93d41fcae1..d77234c80a 100644 --- a/tensorflow/lite/toco/BUILD +++ b/tensorflow/lite/toco/BUILD @@ -348,7 +348,8 @@ tf_cc_test( "//tensorflow/core:lib", "//tensorflow/core:ops", "//tensorflow/core:protos_all_cc", - "@com_google_googletest//:gtest_main", + "//tensorflow/lite/testing:util", + "@com_google_googletest//:gtest", ], ) @@ -387,7 +388,8 @@ tf_cc_test( ":model", ":tooling_util", "//tensorflow/core:lib", - "@com_google_googletest//:gtest_main", + "//tensorflow/lite/testing:util", + "@com_google_googletest//:gtest", ], ) @@ -451,12 +453,13 @@ tf_cc_test( ":toco_port", ":toco_tooling", ":types_proto_cc", - "@com_google_googletest//:gtest_main", + "@com_google_googletest//:gtest", "@com_google_absl//absl/strings", "//tensorflow/core:lib", # We cannot embed the core:ops dependency directly into :toco_tooling as # it can conflict with downstream deps when toco is used as a library. "//tensorflow/core:ops", + "//tensorflow/lite/testing:util", ], ) @@ -468,6 +471,7 @@ tf_cc_test( ], deps = [ ":toco_port", - "@com_google_googletest//:gtest_main", + "//tensorflow/lite/testing:util", + "@com_google_googletest//:gtest", ], ) diff --git a/tensorflow/lite/toco/import_tensorflow_test.cc b/tensorflow/lite/toco/import_tensorflow_test.cc index 260704fd2a..70382f546b 100644 --- a/tensorflow/lite/toco/import_tensorflow_test.cc +++ b/tensorflow/lite/toco/import_tensorflow_test.cc @@ -23,6 +23,7 @@ limitations under the License. #include "tensorflow/core/framework/tensor.pb.h" #include "tensorflow/core/framework/tensor_shape.pb.h" #include "tensorflow/core/lib/core/status.h" +#include "tensorflow/lite/testing/util.h" namespace toco { @@ -564,3 +565,9 @@ TEST(ImportTest, UnsupportedOpWithMultipleOutputs) { } // namespace } // namespace toco + +int main(int argc, char** argv) { + ::tflite::LogToStderr(); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tensorflow/lite/toco/toco_convert_test.cc b/tensorflow/lite/toco/toco_convert_test.cc index c3c440db94..3730c53ae1 100644 --- a/tensorflow/lite/toco/toco_convert_test.cc +++ b/tensorflow/lite/toco/toco_convert_test.cc @@ -15,6 +15,7 @@ limitations under the License. #include "tensorflow/lite/toco/toco_convert.h" #include #include +#include "tensorflow/lite/testing/util.h" namespace toco { namespace { @@ -171,3 +172,9 @@ TEST(TocoTest, TransientStringTensors) { } // namespace } // namespace toco + +int main(int argc, char** argv) { + ::tflite::LogToStderr(); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tensorflow/lite/toco/toco_port_test.cc b/tensorflow/lite/toco/toco_port_test.cc index f5fbb4caeb..d80d423ed7 100644 --- a/tensorflow/lite/toco/toco_port_test.cc +++ b/tensorflow/lite/toco/toco_port_test.cc @@ -13,6 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ #include "tensorflow/lite/toco/toco_port.h" +#include "tensorflow/lite/testing/util.h" #include "tensorflow/lite/toco/toco_types.h" #include @@ -56,3 +57,9 @@ TEST(TocoPortTest, JoinPath) { } // namespace } // namespace port } // namespace toco + +int main(int argc, char** argv) { + ::tflite::LogToStderr(); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tensorflow/lite/toco/tooling_util_test.cc b/tensorflow/lite/toco/tooling_util_test.cc index 6f1c9c563a..e44b94b712 100644 --- a/tensorflow/lite/toco/tooling_util_test.cc +++ b/tensorflow/lite/toco/tooling_util_test.cc @@ -16,9 +16,10 @@ limitations under the License. #include #include +#include "tensorflow/core/lib/core/status.h" +#include "tensorflow/lite/testing/util.h" #include "tensorflow/lite/toco/model.h" #include "tensorflow/lite/toco/tooling_util.h" -#include "tensorflow/core/lib/core/status.h" namespace toco { @@ -203,3 +204,9 @@ TEST(FusedActivationTest, DefaultsToUnfused) { } } // namespace toco + +int main(int argc, char** argv) { + ::tflite::LogToStderr(); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} -- GitLab From 689db4ac81b13279677ceefd84700de3d0ec9925 Mon Sep 17 00:00:00 2001 From: Eugene Zhulenev Date: Thu, 3 Jan 2019 13:29:58 -0800 Subject: [PATCH 302/622] Replace SimpleGraphView with GraphTopologyView. + added separate enter/advance predicates for DFS traversal PiperOrigin-RevId: 227740260 --- tensorflow/core/grappler/optimizers/BUILD | 7 + .../optimizers/arithmetic_optimizer.cc | 47 ++--- .../grappler/optimizers/loop_optimizer.cc | 40 +++-- .../grappler/optimizers/memory_optimizer.cc | 67 ++++--- tensorflow/core/grappler/utils.cc | 169 ------------------ tensorflow/core/grappler/utils.h | 73 -------- tensorflow/core/grappler/utils/traversal.cc | 19 +- tensorflow/core/grappler/utils/traversal.h | 39 +++- .../core/grappler/utils/traversal_test.cc | 46 ++++- 9 files changed, 184 insertions(+), 323 deletions(-) diff --git a/tensorflow/core/grappler/optimizers/BUILD b/tensorflow/core/grappler/optimizers/BUILD index 512c3b07b4..e2b9de7cc5 100644 --- a/tensorflow/core/grappler/optimizers/BUILD +++ b/tensorflow/core/grappler/optimizers/BUILD @@ -254,12 +254,16 @@ cc_library( "//tensorflow/core:lib", "//tensorflow/core:lib_internal", "//tensorflow/core:protos_all_cc", + "//tensorflow/core/grappler:graph_topology_view", "//tensorflow/core/grappler:grappler_item", "//tensorflow/core/grappler:op_types", "//tensorflow/core/grappler:utils", "//tensorflow/core/grappler/costs:graph_properties", "//tensorflow/core/grappler/utils:symbolic_shapes", "//tensorflow/core/grappler/utils:topological_sort", + "//tensorflow/core/grappler/utils:traversal", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:flat_hash_set", ], ) @@ -411,6 +415,7 @@ cc_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:protos_all_cc", + "//tensorflow/core/grappler:graph_topology_view", "//tensorflow/core/grappler:grappler_item", "//tensorflow/core/grappler:mutable_graph_view", "//tensorflow/core/grappler:op_types", @@ -613,11 +618,13 @@ cc_library( "//tensorflow/core:lib", "//tensorflow/core:lib_internal", "//tensorflow/core:protos_all_cc", + "//tensorflow/core/grappler:graph_topology_view", "//tensorflow/core/grappler:grappler_item", "//tensorflow/core/grappler:mutable_graph_view", "//tensorflow/core/grappler:op_types", "//tensorflow/core/grappler/costs:graph_properties", "//tensorflow/core/grappler/utils:frame", + "//tensorflow/core/grappler/utils:traversal", "@com_google_absl//absl/container:flat_hash_set", ], ) diff --git a/tensorflow/core/grappler/optimizers/arithmetic_optimizer.cc b/tensorflow/core/grappler/optimizers/arithmetic_optimizer.cc index 94a87c3ff2..e85e0473b3 100644 --- a/tensorflow/core/grappler/optimizers/arithmetic_optimizer.cc +++ b/tensorflow/core/grappler/optimizers/arithmetic_optimizer.cc @@ -22,6 +22,8 @@ limitations under the License. #include #include +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" #include "tensorflow/core/framework/attr_value.pb.h" #include "tensorflow/core/framework/attr_value_util.h" #include "tensorflow/core/framework/node_def.pb.h" @@ -31,6 +33,7 @@ limitations under the License. #include "tensorflow/core/framework/tensor_shape.pb.h" #include "tensorflow/core/framework/types.h" #include "tensorflow/core/grappler/costs/graph_properties.h" +#include "tensorflow/core/grappler/graph_topology_view.h" #include "tensorflow/core/grappler/grappler_item.h" #include "tensorflow/core/grappler/op_types.h" #include "tensorflow/core/grappler/optimizers/constant_folding.h" @@ -38,6 +41,7 @@ limitations under the License. #include "tensorflow/core/grappler/utils.h" #include "tensorflow/core/grappler/utils/symbolic_shapes.h" #include "tensorflow/core/grappler/utils/topological_sort.h" +#include "tensorflow/core/grappler/utils/traversal.h" #include "tensorflow/core/lib/core/errors.h" #include "tensorflow/core/lib/core/stringpiece.h" #include "tensorflow/core/lib/hash/hash.h" @@ -3334,36 +3338,37 @@ bool ArithmeticOptimizer::CanDedup(const NodeDef& node) const { } void ArithmeticOptimizer::DedupComputations() { - bool stop = true; - SimpleGraphView graph_view; - if (!graph_view.Initialize(*optimized_graph_).ok()) { - LOG(WARNING) << "Failed to build SimpleGraphView."; + GraphTopologyView graph_view; + if (!graph_view.InitializeFromGraph(*optimized_graph_).ok()) { + LOG(WARNING) << "Failed to initialize GraphTopologyView."; return; } - std::set duplicates; + + const absl::flat_hash_set ops_to_traverse = { + "Identity", "IdentityN", "Reshape", "ExpandDims", + "Enter", "Switch", "Merge"}; + // Populate feed_inplace_op; - std::unordered_set feeds_inplace_op; - for (int i = 0; i < optimized_graph_->node_size(); ++i) { - const NodeDef& root = optimized_graph_->node(i); - if (feeds_inplace_op.find(&root) != feeds_inplace_op.end()) { - continue; - } + absl::flat_hash_set feeds_inplace_op; + + for (const NodeDef& root : optimized_graph_->node()) { + if (feeds_inplace_op.find(&root) != feeds_inplace_op.end()) continue; if (ModifiesInputsInPlace(root)) { - const std::unordered_set op_types_to_traverse = { - root.op(), "Identity", "IdentityN", "Reshape", - "ExpandDims", "Enter", "Switch", "Merge"}; + const auto is_continue_traversal = [&](const NodeDef* node) -> bool { + return node->op() == root.op() || ops_to_traverse.count(node->op()) > 0; + }; - graph_view.DepthFirstSearchWithCallback( - op_types_to_traverse, i, - [&](const NodeDef& node) { - feeds_inplace_op.insert(&node); - return false; - }, - SimpleGraphView::kFollowInputs /* search through inputs */); + DfsTraversal(graph_view, {&root}, TraversalDirection::kFollowInputs, + DfsPredicates::Advance(is_continue_traversal), + DfsCallbacks::PreOrder([&](const NodeDef* node) { + feeds_inplace_op.insert(node); + })); } } + bool stop = true; + std::set duplicates; do { stop = true; UniqueNodes nodes; diff --git a/tensorflow/core/grappler/optimizers/loop_optimizer.cc b/tensorflow/core/grappler/optimizers/loop_optimizer.cc index 3606473840..cf5e4db29f 100644 --- a/tensorflow/core/grappler/optimizers/loop_optimizer.cc +++ b/tensorflow/core/grappler/optimizers/loop_optimizer.cc @@ -30,12 +30,14 @@ limitations under the License. #include "tensorflow/core/framework/op.h" #include "tensorflow/core/framework/tensor.pb.h" #include "tensorflow/core/framework/types.h" +#include "tensorflow/core/grappler/graph_topology_view.h" #include "tensorflow/core/grappler/grappler_item.h" #include "tensorflow/core/grappler/mutable_graph_view.h" #include "tensorflow/core/grappler/op_types.h" #include "tensorflow/core/grappler/optimizers/constant_folding.h" #include "tensorflow/core/grappler/optimizers/evaluation_utils.h" #include "tensorflow/core/grappler/utils/frame.h" +#include "tensorflow/core/grappler/utils/traversal.h" #include "tensorflow/core/lib/core/errors.h" #include "tensorflow/core/lib/core/stringpiece.h" #include "tensorflow/core/lib/gtl/inlined_vector.h" @@ -451,16 +453,29 @@ Status LoopInvariantNodeMotionOptimizer::Optimize() { } std::vector GetStackPushNodesToConvert( - const SimpleGraphView& graph_view, + const GraphTopologyView& graph_view, const std::unordered_set& nodes_to_preserve, int stack_node_idx) { VLOG(1) << "Stack node: " << graph_view.graph()->node(stack_node_idx).name(); + const std::unordered_set op_types_to_traverse( {"Stack", "StackV2", "Enter", "RefEnter", "Switch", "RefSwitch", "Identity", "RefIdentity"}); + const auto is_op_to_traverse = [&](const NodeDef* node) -> bool { + return op_types_to_traverse.find(node->op()) != op_types_to_traverse.end(); + }; + std::vector nodes_to_convert; - std::set fanout; - graph_view.DepthFirstSearch(op_types_to_traverse, stack_node_idx, &fanout); - for (int fanout_idx : fanout) { + std::vector fanouts; + + DfsTraversal(graph_view, {graph_view.GetNode(stack_node_idx)}, + TraversalDirection::kFollowOutputs, + DfsPredicates::Advance(is_op_to_traverse), + DfsCallbacks::PreOrder([&](const NodeDef* node) { + const absl::optional idx = graph_view.GetNodeIndex(*node); + fanouts.push_back(idx.value()); + })); + + for (int fanout_idx : fanouts) { const NodeDef& fanout_node = graph_view.graph()->node(fanout_idx); VLOG(1) << "Fanout " << fanout_idx << " : " << fanout_node.name(); if (IsStackPushOp(fanout_node)) { @@ -468,13 +483,12 @@ std::vector GetStackPushNodesToConvert( // happen when the graph we have contains only the forward pass for a loop // (as when the forward and backward passes are split across different // functions). - if (graph_view.has_node(fanout_node.input(0))) { - const NodeDef* stack_node = - &graph_view.node(graph_view.index(fanout_node.input(0))); + if (graph_view.HasNode(fanout_node.input(0))) { + const NodeDef* stack_node = graph_view.GetNode(fanout_node.input(0)); while (stack_node->op() != "Stack" && stack_node->op() != "StackV2" && stack_node->input_size() > 0 && - graph_view.has_node(stack_node->input(0))) { - stack_node = &graph_view.node(graph_view.index(stack_node->input(0))); + graph_view.HasNode(stack_node->input(0))) { + stack_node = graph_view.GetNode(stack_node->input(0)); } if (nodes_to_preserve.find(stack_node->name()) == nodes_to_preserve.end()) { @@ -488,7 +502,7 @@ std::vector GetStackPushNodesToConvert( op_types_to_traverse.end()) { continue; } else if (!IsStackPopOp(fanout_node) || - (!graph_view.outputs(fanout_idx).empty() || + (!graph_view.GetFanout(fanout_idx).empty() || nodes_to_preserve.find(fanout_node.name()) != nodes_to_preserve.end())) { // The node is either a stack pop with consumers or something unexpected @@ -497,14 +511,16 @@ std::vector GetStackPushNodesToConvert( break; } } + return nodes_to_convert; } Status RemoveStackOps(const std::unordered_set& nodes_to_preserve, GraphDef* optimized_graph) { NodeMap node_map(optimized_graph); - SimpleGraphView graph_view; - TF_RETURN_IF_ERROR(graph_view.Initialize(*optimized_graph)); + GraphTopologyView graph_view; + TF_RETURN_IF_ERROR(graph_view.InitializeFromGraph(*optimized_graph)); + for (int node_idx = 0; node_idx < optimized_graph->node_size(); ++node_idx) { if (IsStackOp(optimized_graph->node(node_idx))) { for (int push_node_idx : GetStackPushNodesToConvert( diff --git a/tensorflow/core/grappler/optimizers/memory_optimizer.cc b/tensorflow/core/grappler/optimizers/memory_optimizer.cc index 6e33645537..b50d50f842 100644 --- a/tensorflow/core/grappler/optimizers/memory_optimizer.cc +++ b/tensorflow/core/grappler/optimizers/memory_optimizer.cc @@ -30,6 +30,7 @@ limitations under the License. #include "tensorflow/core/grappler/costs/graph_memory.h" #include "tensorflow/core/grappler/costs/graph_properties.h" #include "tensorflow/core/grappler/costs/utils.h" +#include "tensorflow/core/grappler/graph_topology_view.h" #include "tensorflow/core/grappler/grappler_item.h" #include "tensorflow/core/grappler/mutable_graph_view.h" #include "tensorflow/core/grappler/op_types.h" @@ -188,13 +189,14 @@ std::vector GetOpGroupsToRecompute( } } // Recompute only nodes which eventually feed into a target node. - connected_subgraph(node_map, - true, // Collect inputs - false, // Collect outputs - [&unpruned_recompute_nodes](const NodeDef& node) { - return unpruned_recompute_nodes.count(&node) != 0; - }, - ¤t_recomputation.recomputed_source_nodes); + connected_subgraph( + node_map, + true, // Collect inputs + false, // Collect outputs + [&unpruned_recompute_nodes](const NodeDef& node) { + return unpruned_recompute_nodes.count(&node) != 0; + }, + ¤t_recomputation.recomputed_source_nodes); if (current_recomputation.target_nodes.empty()) { continue; } @@ -1268,46 +1270,55 @@ Status RelaxAllocatorConstraints(GraphDef* optimized_graph) { return Status::OK(); } - std::unordered_set optimized_nodes; - SimpleGraphView graph_view; - TF_RETURN_IF_ERROR(graph_view.Initialize(*optimized_graph)); + GraphTopologyView graph_view; + TF_RETURN_IF_ERROR(graph_view.InitializeFromGraph(*optimized_graph)); + std::unordered_set optimized_nodes; + for (int i : assign_nodes) { - if (optimized_nodes.find(i) == optimized_nodes.end()) { - const NodeDef& assign_node = optimized_graph->node(i); - optimized_nodes.insert(i); - std::vector assign_nodes_in_fanout; - assign_nodes_in_fanout.push_back(i); - std::set transitive_fanout; - graph_view.DepthFirstSearch(std::unordered_set{}, i, - &transitive_fanout); + const NodeDef& assign_node = optimized_graph->node(i); + + if (optimized_nodes.find(&assign_node) == optimized_nodes.end()) { + std::vector assign_nodes_in_fanout; + optimized_nodes.insert(&assign_node); + assign_nodes_in_fanout.push_back(&assign_node); + + std::vector transitive_fanout; + DfsTraversal(graph_view, {graph_view.GetNode(i)}, + TraversalDirection::kFollowOutputs, + DfsCallbacks::PreOrder([&](const NodeDef* node) { + transitive_fanout.push_back(node); + })); + bool relax_constraint = true; // If all nodes in the transitive fanout are on the same device as the // assign node, there is no need to allocate the output in pinned memory. - for (int fanout : transitive_fanout) { - const NodeDef& fanout_node = optimized_graph->node(fanout); + for (const NodeDef* fanout_node : transitive_fanout) { + // const NodeDef& fanout_node = optimized_graph->node(fanout); if (relax_constraint && - (IsSend(fanout_node) || - CrossesTaskOrCpuGpuBoundary(fanout_node, assign_node))) { + (IsSend(*fanout_node) || + CrossesTaskOrCpuGpuBoundary(*fanout_node, assign_node))) { relax_constraint = false; break; } - if (optimized_nodes.find(fanout) == optimized_nodes.end() && - IsAssign(fanout_node)) { - assign_nodes_in_fanout.push_back(fanout); + if (optimized_nodes.find(fanout_node) == optimized_nodes.end() && + IsAssign(*fanout_node)) { + assign_nodes_in_fanout.push_back(fanout_node); } } if (relax_constraint) { - for (int assign_idx : assign_nodes_in_fanout) { + for (const NodeDef* assign_node_in_fanout : assign_nodes_in_fanout) { // If all devices match in fanout of node(i) then, by transitivity, // they must also match in the fanout of other assign nodes // in the fanout of node(i), so we can process them here, // and save computing their transitive fanout later. - optimized_nodes.insert(assign_idx); + optimized_nodes.insert(assign_node_in_fanout); // Set an attribute telling AssignOp to ignore allocator constraints. + const absl::optional assign_node_idx = + graph_view.GetNodeIndex(*assign_node_in_fanout); NodeDef* assign_node_to_relax = - optimized_graph->mutable_node(assign_idx); + optimized_graph->mutable_node(assign_node_idx.value()); (*assign_node_to_relax ->mutable_attr())["_grappler_relax_allocator_constraints"] .set_b(true); diff --git a/tensorflow/core/grappler/utils.cc b/tensorflow/core/grappler/utils.cc index d8fb88d95f..cc9a38b2d2 100644 --- a/tensorflow/core/grappler/utils.cc +++ b/tensorflow/core/grappler/utils.cc @@ -402,175 +402,6 @@ void EraseNodesFromGraph(const std::set& nodes_to_delete, EraseNodesFromGraphImpl(nodes_idx_to_delete, graph); } -Status SimpleGraphView::Initialize( - const GraphDef& graph, - const std::vector>* - extra_dependencies, - bool dedup_inputs, bool dedup_outputs) { - graph_ = &graph; - const int num_nodes = graph.node_size(); - inputs_.clear(); - inputs_.resize(num_nodes); - outputs_.clear(); - outputs_.resize(num_nodes); - name_to_index_.clear(); - name_to_index_.reserve(num_nodes); - index_to_name_.clear(); - index_to_name_.reserve(num_nodes); - - // Build map from name to index and vice versa. - for (int node_idx = 0; node_idx < num_nodes; ++node_idx) { - const NodeDef& node = graph.node(node_idx); - name_to_index_.emplace(node.name(), node_idx); - index_to_name_.push_back(node.name()); - } - - if (extra_dependencies) { - for (const auto& dep : *extra_dependencies) { - auto itr_src = name_to_index_.find(dep.first->name()); - if (itr_src == name_to_index_.end()) { - return errors::InvalidArgument("Non-existent src ", dep.first->name()); - } - auto itr_tgt = name_to_index_.find(dep.second->name()); - if (itr_tgt == name_to_index_.end()) { - return errors::InvalidArgument("Non-existent tgt ", dep.second->name()); - } - const int src_idx = itr_src->second; - const int tgt_idx = itr_tgt->second; - inputs_[tgt_idx].push_back(src_idx); - outputs_[src_idx].push_back(tgt_idx); - } - } - - // Build forward and reverse adjacency lists. - for (int node_idx = 0; node_idx < num_nodes; ++node_idx) { - const NodeDef& node = graph.node(node_idx); - inputs_[node_idx].reserve(node.input_size()); - for (const string& input : node.input()) { - auto it = name_to_index_.find(NodeName(input)); - if (it == name_to_index_.end()) { - return errors::InvalidArgument("Non-existent input ", input, - " for node ", node.name()); - } - const int input_idx = it->second; - inputs_[node_idx].push_back(input_idx); - outputs_[input_idx].push_back(node_idx); - } - if (dedup_inputs) { - // Dedup the input list while it's still hot in cache. - STLSortAndRemoveDuplicates(&inputs_[node_idx]); - } - } - - // Dedup outputs. - if (dedup_outputs) { - for (int node_idx = 0; node_idx < num_nodes; ++node_idx) { - STLSortAndRemoveDuplicates(&outputs_[node_idx]); - } - } - return Status::OK(); -} - -void SimpleGraphView::DepthFirstSearch( - const std::unordered_set& op_types_to_traverse, int root_node, - std::set* nodes_found, - SimpleGraphView::SearchDirection direction) const { - nodes_found->clear(); - const string& op_type = graph_->node(root_node).op(); - if (!op_types_to_traverse.empty() && - op_types_to_traverse.find(op_type) == op_types_to_traverse.end()) { - return; - } - std::vector stack; - stack.reserve(32); - stack.push_back(root_node); - - auto push_neighbors = [&stack, - &nodes_found](absl::Span neighbors) { - for (auto output_idx : neighbors) { - if (nodes_found->find(output_idx) == nodes_found->end()) { - stack.push_back(output_idx); - } - } - }; - - while (!stack.empty()) { - const int node_idx = stack.back(); - stack.pop_back(); - nodes_found->insert(node_idx); - const string& op_type = graph_->node(node_idx).op(); - if (op_types_to_traverse.empty() || - op_types_to_traverse.find(op_type) != op_types_to_traverse.end()) { - if (direction == kFollowOutputs) { - push_neighbors(this->outputs(node_idx)); - } else { - push_neighbors(this->inputs(node_idx)); - } - } - } -} - -void SimpleGraphView::DepthFirstSearchWithCallback( - const std::unordered_set& op_types_to_traverse, int node_idx, - SimpleGraphView::DFSCallback callback, - SimpleGraphView::SearchDirection direction) const { - std::set nodes_found; - nodes_found.clear(); - const string& op_type = graph_->node(node_idx).op(); - if (!op_types_to_traverse.empty() && - op_types_to_traverse.find(op_type) == op_types_to_traverse.end()) { - return; - } - std::vector stack; - stack.reserve(32); - stack.push_back(node_idx); - auto push_neighbors = [&stack, - &nodes_found](absl::Span neighbors) { - for (auto output_idx : neighbors) { - if (nodes_found.find(output_idx) == nodes_found.end()) { - stack.push_back(output_idx); - } - } - }; - while (!stack.empty()) { - const int node_idx = stack.back(); - stack.pop_back(); - if (callback(graph_->node(node_idx))) { - return; - } - nodes_found.insert(node_idx); - const string& op_type = graph_->node(node_idx).op(); - if (op_types_to_traverse.empty() || - op_types_to_traverse.find(op_type) != op_types_to_traverse.end()) { - if (direction == kFollowOutputs) { - push_neighbors(this->outputs(node_idx)); - } else { - push_neighbors(this->inputs(node_idx)); - } - } - } -} - -string SimpleGraphView::PrintToString() const { - string str; - for (int i = 0; i < num_nodes(); ++i) { - strings::StrAppend(&str, "Node ", i, "'", node_name(i), "'\n", "Inputs: ["); - for (int input : inputs(i)) { - strings::StrAppend(&str, input, " '", node_name(input), "', "); - } - strings::StrAppend(&str, "]\n", "Outputs: ["); - for (int j = 0; j < outputs(i).size(); ++j) { - const int output = outputs(i)[j]; - if (j > 0) { - strings::StrAppend(&str, ", "); - } - strings::StrAppend(&str, output, " '", node_name(output), "'"); - } - strings::StrAppend(&str, "]\n"); - } - return str; -} - #define HANDLE_CASE(DTYPE) \ case DTYPE: \ if (!SafeSetScalarTensorValue::Type>( \ diff --git a/tensorflow/core/grappler/utils.h b/tensorflow/core/grappler/utils.h index a9d6abd6db..1e820977ae 100644 --- a/tensorflow/core/grappler/utils.h +++ b/tensorflow/core/grappler/utils.h @@ -302,79 +302,6 @@ void EraseNodesFromGraph(std::vector&& nodes_to_delete, GraphDef* graph); void EraseNodesFromGraph(const std::set& nodes_to_delete, GraphDef* graph); -class SimpleGraphView { - public: - // Build a graph view for the specified graphdef. - Status Initialize(const GraphDef& graph) { - return Initialize(graph, nullptr, true, true); - } - // Build a graph view for the specified graphdef augmented with the additional - // edges specified in 'extra_dependencies' if any. Note that - // extra_dependencies can be null. - Status Initialize( - const GraphDef& graph, - const std::vector>* - extra_dependencies) { - return Initialize(graph, extra_dependencies, true, true); - } - Status Initialize( - const GraphDef& graph, - const std::vector>* - extra_dependencies, - bool dedup_inputs, bool dedup_outputs); - - const GraphDef* graph() const { return graph_; } - inline int num_nodes() const { return index_to_name_.size(); } - inline bool has_node(const string& node_name) const { - return name_to_index_.find(node_name) != name_to_index_.end(); - } - inline const int index(const string& node_name) const { - const auto& it = name_to_index_.find(node_name); - DCHECK(it != name_to_index_.end()); - return it == name_to_index_.end() ? -1 : it->second; - } - inline const NodeDef& node(int node_idx) const { - return graph_->node(node_idx); - } - inline const string& node_name(int node_idx) const { - return index_to_name_[node_idx]; - } - inline const gtl::InlinedVector& inputs(int node_idx) const { - return inputs_[node_idx]; - } - inline const gtl::InlinedVector& outputs(int node_idx) const { - return outputs_[node_idx]; - } - - enum SearchDirection { kFollowOutputs = 1, kFollowInputs = 2 }; - - // Traverse the graph starting at `node_idx`, collecting indices of nodes - // visited in nodes_found. If a node has an op in `op_types_to_traverse`, the - // walk continues to its children. It is assumed that *graph_ was not modified - // after the call to Initialize(). - // If `op_types_to_traverse` is empty the DFS will traverse any node type. - void DepthFirstSearch(const std::unordered_set& op_types_to_traverse, - int node_idx, std::set* nodes_found, - SearchDirection direction = kFollowOutputs) const; - - typedef std::function DFSCallback; - - // Like DepthFirstSearch, but invoke `callback` as each node is discovered. If - // `callback` returns true, the search is terminated early. - void DepthFirstSearchWithCallback( - const std::unordered_set& op_types_to_traverse, int node_idx, - DFSCallback callback, SearchDirection direction = kFollowOutputs) const; - - string PrintToString() const; - - private: - const GraphDef* graph_; // Not owned. - std::vector index_to_name_; - gtl::FlatMap name_to_index_; - std::vector> inputs_; - std::vector> outputs_; -}; - } // end namespace grappler } // end namespace tensorflow diff --git a/tensorflow/core/grappler/utils/traversal.cc b/tensorflow/core/grappler/utils/traversal.cc index 3231094fd4..c602e8c0e4 100644 --- a/tensorflow/core/grappler/utils/traversal.cc +++ b/tensorflow/core/grappler/utils/traversal.cc @@ -44,8 +44,8 @@ enum class NodeState { kNotVisited, kVisiting, kDone }; void DfsTraversal(const GraphTopologyView& graph_view, const absl::Span from, - TraversalDirection direction, - const std::function& should_visit, + const TraversalDirection direction, + const DfsPredicates& predicates, const DfsCallbacks& callbacks) { std::vector stack; stack.reserve(from.size()); @@ -66,8 +66,8 @@ void DfsTraversal(const GraphTopologyView& graph_view, NodeState& state = node_state[w.node]; if (state == NodeState::kDone) continue; - // Skip nodes that do not match predicate. - if (should_visit && !should_visit(graph_view.GetNode(w.node))) { + // Skip nodes that we should not enter. + if (predicates.enter && !predicates.enter(graph_view.GetNode(w.node))) { state = NodeState::kDone; continue; } @@ -98,6 +98,11 @@ void DfsTraversal(const GraphTopologyView& graph_view, // Enqueue the node again with the children_visited flag set to true. stack.emplace_back(w.node, true, w.src); + // Check if we can continue traversal from the current node. + if (predicates.advance && !predicates.advance(graph_view.GetNode(w.node))) { + continue; + } + // Now enqueue the fanin/fanout nodes. if (direction == TraversalDirection::kFollowInputs) { for (const int fanin : graph_view.GetFanin(w.node)) { @@ -111,14 +116,10 @@ void DfsTraversal(const GraphTopologyView& graph_view, } } -// Traverse the graph in DFS order in the given direction, starting from the -// list of nodes specified in the `from` argument. Call corresponding -// callbacks for each visited node. void DfsTraversal(const GraphTopologyView& graph_view, const absl::Span from, TraversalDirection direction, const DfsCallbacks& callbacks) { - const auto visit_all = [](const NodeDef*) { return true; }; - DfsTraversal(graph_view, from, direction, visit_all, callbacks); + DfsTraversal(graph_view, from, direction, {}, callbacks); } } // namespace grappler diff --git a/tensorflow/core/grappler/utils/traversal.h b/tensorflow/core/grappler/utils/traversal.h index ec6009096d..5c9dada493 100644 --- a/tensorflow/core/grappler/utils/traversal.h +++ b/tensorflow/core/grappler/utils/traversal.h @@ -33,6 +33,7 @@ enum class TraversalDirection { kFollowInputs, kFollowOutputs }; // corresponding back edges. Moreover, the pre and post order will assume that // these back edges will be cut. struct DfsCallbacks { + DfsCallbacks() = default; DfsCallbacks(std::function pre, std::function post, std::function back_edge) @@ -53,16 +54,40 @@ struct DfsCallbacks { std::function on_back_edge; }; +// Encapsulate DFS predicates for traversing the graph. +// +// The `enter` predicate decides if traversal should enter the node, and the +// `advance` predicate decides if the traversal should follow inputs/outputs +// from the node. +// +// If predicates are empty (default initialized), it's assumed that we can enter +// into any node and advance from any node respectively. +struct DfsPredicates { + DfsPredicates() = default; + DfsPredicates(std::function enter, + std::function advance) + : enter(std::move(enter)), advance(std::move(advance)) {} + + static DfsPredicates Enter(std::function enter) { + return DfsPredicates(std::move(enter), nullptr); + } + + static DfsPredicates Advance(std::function advance) { + return DfsPredicates(nullptr, std::move(advance)); + } + + std::function enter; + std::function advance; +}; + // Traverse the graph in DFS order in the given direction, starting from the -// list of nodes specified in the `from` argument. Use `should_visit` to decide -// if the node should be visited. This predicate also applied to the `from` -// nodes. Call corresponding callbacks for each visited node. If `should_visit` -// is empty (default initialized) it's assumed that traversal can visit all the -// nodes. +// list of nodes specified in the `from` argument. Use `predicates` to decide if +// traversal should enter/advance to/from the graph node. These predicates also +// applied to the `from` nodes. Call corresponding callbacks for each visited +// node. void DfsTraversal(const GraphTopologyView& graph_view, absl::Span from, - TraversalDirection direction, - const std::function& should_visit, + TraversalDirection direction, const DfsPredicates& predicates, const DfsCallbacks& callbacks); // Traverse the graph in DFS order in the given direction, starting from the diff --git a/tensorflow/core/grappler/utils/traversal_test.cc b/tensorflow/core/grappler/utils/traversal_test.cc index 5a68fa201e..7b36d328e9 100644 --- a/tensorflow/core/grappler/utils/traversal_test.cc +++ b/tensorflow/core/grappler/utils/traversal_test.cc @@ -159,7 +159,7 @@ TEST(TraversalTest, OutputDfsWithLoop) { EXPECT_EQ(back_edges, expected_edges); } -TEST(TraversalTest, DfsWithPredicate) { +TEST(TraversalTest, DfsWithEnterPredicate) { const string op = "OpIsNotImportantInThisTest"; GraphDef graph = ::tensorflow::test::function::GDef( // @@ -171,8 +171,8 @@ TEST(TraversalTest, DfsWithPredicate) { NDef("6", op, {"3", "5"}, {})}, // /*funcs=*/{}); - // Do not go through nodes '2' and '3'. - const auto predicate = [](const NodeDef* node) { + // Do not enter the nodes '2' and '3'. + const auto enter = [](const NodeDef* node) { return node->name() != "2" && node->name() != "3"; }; @@ -185,7 +185,8 @@ TEST(TraversalTest, DfsWithPredicate) { GraphTopologyView graph_view; TF_CHECK_OK(graph_view.InitializeFromGraph(graph)); DfsTraversal(graph_view, start_nodes, TraversalDirection::kFollowOutputs, - predicate, MkCallbacks(&pre_order, &post_order, &back_edges)); + DfsPredicates::Enter(enter), + MkCallbacks(&pre_order, &post_order, &back_edges)); const std::vector expected_pre = {"1", "4", "5", "6"}; const std::vector expected_post = {"6", "5", "4", "1"}; @@ -195,6 +196,43 @@ TEST(TraversalTest, DfsWithPredicate) { EXPECT_TRUE(back_edges.empty()); } +TEST(TraversalTest, DfsWithAdvancePredicate) { + const string op = "OpIsNotImportantInThisTest"; + + GraphDef graph = ::tensorflow::test::function::GDef( // + {NDef("1", op, {}, {}), // 2 -> 3 + NDef("2", op, {"1"}, {}), // 1 -> / \ -> 6 + NDef("3", op, {"2"}, {}), // \ / + NDef("4", op, {"1"}, {}), // 4 -> 5 + NDef("5", op, {"4"}, {}), // + NDef("6", op, {"3", "5"}, {})}, // + {} /* empty function library*/); + + // Do not advance from the nodes '2' and '3'. + const auto advance = [](const NodeDef* node) { + return node->name() != "2" && node->name() != "3"; + }; + + std::vector start_nodes = {&graph.node(0)}; + + std::vector pre_order; + std::vector post_order; + std::vector back_edges; + + GraphTopologyView graph_view; + TF_CHECK_OK(graph_view.InitializeFromGraph(graph)); + DfsTraversal(graph_view, start_nodes, TraversalDirection::kFollowOutputs, + DfsPredicates::Advance(advance), + MkCallbacks(&pre_order, &post_order, &back_edges)); + + const std::vector expected_pre = {"1", "4", "5", "6", "2"}; + const std::vector expected_post = {"6", "5", "4", "2", "1"}; + + EXPECT_EQ(pre_order, expected_pre); + EXPECT_EQ(post_order, expected_post); + EXPECT_TRUE(back_edges.empty()); +} + } // namespace } // namespace grappler } // namespace tensorflow -- GitLab From 75f12a50203ca31370c5edc02d650ee4a47fdf03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Schoentgen?= Date: Thu, 3 Jan 2019 22:36:43 +0100 Subject: [PATCH 303/622] Fix several DeprecationWarning: invlid escape sequence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mickaël Schoentgen --- .../contrib/kernel_methods/python/losses.py | 2 +- .../python/learn/learn_io/generator_io_test.py | 4 ++-- .../opt/python/training/adam_gs_optimizer.py | 2 +- tensorflow/contrib/optimizer_v2/adam.py | 2 +- tensorflow/python/client/session_test.py | 2 +- .../python/feature_column/feature_column_test.py | 12 ++++++------ .../feature_column/feature_column_v2_test.py | 16 ++++++++-------- .../python/kernel_tests/confusion_matrix_test.py | 4 ++-- tensorflow/python/training/adam.py | 2 +- tensorflow/tools/ci_build/copy_binary.py | 2 +- .../scripts_allreduce/k8s_generate_yaml_lib.py | 2 +- .../windows/msvc_wrapper_for_nvcc.py | 2 +- .../windows/msvc_wrapper_for_nvcc.py | 2 +- .../gcc-nvcc/windows/msvc_wrapper_for_nvcc.py | 2 +- 14 files changed, 28 insertions(+), 28 deletions(-) diff --git a/tensorflow/contrib/kernel_methods/python/losses.py b/tensorflow/contrib/kernel_methods/python/losses.py index 4ef0a66a52..294a7d69a7 100644 --- a/tensorflow/contrib/kernel_methods/python/losses.py +++ b/tensorflow/contrib/kernel_methods/python/losses.py @@ -34,7 +34,7 @@ def sparse_multiclass_hinge_loss( scope=None, loss_collection=ops.GraphKeys.LOSSES, reduction=losses.Reduction.SUM_BY_NONZERO_WEIGHTS): - """Adds Ops for computing the multiclass hinge loss. + r"""Adds Ops for computing the multiclass hinge loss. The implementation is based on the following paper: On the Algorithmic Implementation of Multiclass Kernel-based Vector Machines diff --git a/tensorflow/contrib/learn/python/learn/learn_io/generator_io_test.py b/tensorflow/contrib/learn/python/learn/learn_io/generator_io_test.py index 5e90d1fa20..318046733b 100644 --- a/tensorflow/contrib/learn/python/learn/learn_io/generator_io_test.py +++ b/tensorflow/contrib/learn/python/learn/learn_io/generator_io_test.py @@ -174,7 +174,7 @@ class GeneratorIoTest(test.TestCase): return np.arange(32, 36) with self.cached_session(): - with self.assertRaisesRegexp(TypeError, 'x\(\) must be generator'): + with self.assertRaisesRegexp(TypeError, r'x\(\) must be generator'): failing_input_fn = generator_io.generator_input_fn( generator, batch_size=2, shuffle=False, num_epochs=1) failing_input_fn() @@ -185,7 +185,7 @@ class GeneratorIoTest(test.TestCase): yield np.arange(32, 36) with self.cached_session(): - with self.assertRaisesRegexp(TypeError, 'x\(\) must yield dict'): + with self.assertRaisesRegexp(TypeError, r'x\(\) must yield dict'): failing_input_fn = generator_io.generator_input_fn( generator, batch_size=2, shuffle=False, num_epochs=1) failing_input_fn() diff --git a/tensorflow/contrib/opt/python/training/adam_gs_optimizer.py b/tensorflow/contrib/opt/python/training/adam_gs_optimizer.py index 3fb649ea82..855fbf58bf 100644 --- a/tensorflow/contrib/opt/python/training/adam_gs_optimizer.py +++ b/tensorflow/contrib/opt/python/training/adam_gs_optimizer.py @@ -41,7 +41,7 @@ class AdamGSOptimizer(optimizer.Optimizer): def __init__(self, global_step=0, learning_rate=0.001, beta1=0.9, beta2=0.999, epsilon=1e-8, use_locking=False, name="Adam"): - """Construct a new Adam optimizer. + r"""Construct a new Adam optimizer. Branched from tf.train.AdamOptimizer. The only difference is to pass global step for computing beta1 and beta2 accumulators, instead of having diff --git a/tensorflow/contrib/optimizer_v2/adam.py b/tensorflow/contrib/optimizer_v2/adam.py index 248ffb1f7e..1b7800f324 100644 --- a/tensorflow/contrib/optimizer_v2/adam.py +++ b/tensorflow/contrib/optimizer_v2/adam.py @@ -36,7 +36,7 @@ class AdamOptimizer(optimizer_v2.OptimizerV2): def __init__(self, learning_rate=0.001, beta1=0.9, beta2=0.999, epsilon=1e-8, use_locking=False, name="Adam"): - """Construct a new Adam optimizer. + r"""Construct a new Adam optimizer. Initialization: diff --git a/tensorflow/python/client/session_test.py b/tensorflow/python/client/session_test.py index c4a118a414..da6218663d 100644 --- a/tensorflow/python/client/session_test.py +++ b/tensorflow/python/client/session_test.py @@ -2036,7 +2036,7 @@ class SessionTest(test_util.TensorFlowTestCase): with self.cached_session() as sess: a = array_ops.placeholder(dtype=dtypes.string) with self.assertRaisesRegexp( - TypeError, 'Type of feed value 1 with type <(\w+) \'int\'> is not'): + TypeError, r'Type of feed value 1 with type <(\w+) \'int\'> is not'): sess.run(a, feed_dict={a: 1}) diff --git a/tensorflow/python/feature_column/feature_column_test.py b/tensorflow/python/feature_column/feature_column_test.py index daa0a3b3a4..0ded2bf8c9 100644 --- a/tensorflow/python/feature_column/feature_column_test.py +++ b/tensorflow/python/feature_column/feature_column_test.py @@ -1832,7 +1832,7 @@ class LinearModelTest(test.TestCase): } with self.assertRaisesRegexp( ValueError, - 'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string + r'Batch size \(first dimension\) of each feature must be same.'): fc.linear_model(features, [price1, price2]) def test_subset_of_static_batch_size_mismatch(self): @@ -1847,7 +1847,7 @@ class LinearModelTest(test.TestCase): } with self.assertRaisesRegexp( ValueError, - 'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string + r'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string fc.linear_model(features, [price1, price2, price3]) def test_runtime_batch_size_mismatch(self): @@ -2467,7 +2467,7 @@ class _LinearModelTest(test.TestCase): } with self.assertRaisesRegexp( ValueError, - 'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string + r'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string get_keras_linear_model_predictions(features, [price1, price2]) def test_subset_of_static_batch_size_mismatch(self): @@ -2482,7 +2482,7 @@ class _LinearModelTest(test.TestCase): } with self.assertRaisesRegexp( ValueError, - 'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string + r'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string get_keras_linear_model_predictions(features, [price1, price2, price3]) def test_runtime_batch_size_mismatch(self): @@ -2974,7 +2974,7 @@ class FunctionalInputLayerTest(test.TestCase): } with self.assertRaisesRegexp( ValueError, - 'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string + r'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string fc.input_layer(features, [price1, price2]) def test_subset_of_static_batch_size_mismatch(self): @@ -2989,7 +2989,7 @@ class FunctionalInputLayerTest(test.TestCase): } with self.assertRaisesRegexp( ValueError, - 'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string + r'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string fc.input_layer(features, [price1, price2, price3]) def test_runtime_batch_size_mismatch(self): diff --git a/tensorflow/python/feature_column/feature_column_v2_test.py b/tensorflow/python/feature_column/feature_column_v2_test.py index a247425369..2a864a0573 100644 --- a/tensorflow/python/feature_column/feature_column_v2_test.py +++ b/tensorflow/python/feature_column/feature_column_v2_test.py @@ -2052,7 +2052,7 @@ class LinearModelTest(test.TestCase): } with self.assertRaisesRegexp( ValueError, - 'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string + r'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string model = fc.LinearModel([price1, price2]) model(features) @@ -2068,7 +2068,7 @@ class LinearModelTest(test.TestCase): } with self.assertRaisesRegexp( ValueError, - 'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string + r'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string model = fc.LinearModel([price1, price2, price3]) model(features) @@ -2818,7 +2818,7 @@ class OldLinearModelTest(test.TestCase): } with self.assertRaisesRegexp( ValueError, - 'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string + r'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string fc_old.linear_model(features, [price1, price2]) def test_subset_of_static_batch_size_mismatch(self): @@ -2833,7 +2833,7 @@ class OldLinearModelTest(test.TestCase): } with self.assertRaisesRegexp( ValueError, - 'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string + r'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string fc_old.linear_model(features, [price1, price2, price3]) def test_runtime_batch_size_mismatch(self): @@ -3435,7 +3435,7 @@ class DenseFeaturesTest(test.TestCase): } with self.assertRaisesRegexp( ValueError, - 'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string + r'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string fc.DenseFeatures([price1, price2])(features) def test_subset_of_static_batch_size_mismatch(self): @@ -3450,7 +3450,7 @@ class DenseFeaturesTest(test.TestCase): } with self.assertRaisesRegexp( ValueError, - 'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string + r'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string fc.DenseFeatures([price1, price2, price3])(features) def test_runtime_batch_size_mismatch(self): @@ -4141,7 +4141,7 @@ class FunctionalInputLayerTest(test.TestCase): } with self.assertRaisesRegexp( ValueError, - 'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string + r'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string fc_old.input_layer(features, [price1, price2]) def test_subset_of_static_batch_size_mismatch(self): @@ -4156,7 +4156,7 @@ class FunctionalInputLayerTest(test.TestCase): } with self.assertRaisesRegexp( ValueError, - 'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string + r'Batch size \(first dimension\) of each feature must be same.'): # pylint: disable=anomalous-backslash-in-string fc_old.input_layer(features, [price1, price2, price3]) def test_runtime_batch_size_mismatch(self): diff --git a/tensorflow/python/kernel_tests/confusion_matrix_test.py b/tensorflow/python/kernel_tests/confusion_matrix_test.py index ae13c8e32e..6670f0326c 100644 --- a/tensorflow/python/kernel_tests/confusion_matrix_test.py +++ b/tensorflow/python/kernel_tests/confusion_matrix_test.py @@ -472,7 +472,7 @@ class RemoveSqueezableDimensionsTest(test.TestCase): } with self.assertRaisesRegexp( errors_impl.InvalidArgumentError, - "Can not squeeze dim\[2\]"): + r"Can not squeeze dim\[2\]"): dynamic_labels.eval(feed_dict=feed_dict) self.assertAllEqual( prediction_values, dynamic_predictions.eval(feed_dict=feed_dict)) @@ -500,7 +500,7 @@ class RemoveSqueezableDimensionsTest(test.TestCase): label_values, dynamic_labels.eval(feed_dict=feed_dict)) with self.assertRaisesRegexp( errors_impl.InvalidArgumentError, - "Can not squeeze dim\[2\]"): + r"Can not squeeze dim\[2\]"): dynamic_predictions.eval(feed_dict=feed_dict) diff --git a/tensorflow/python/training/adam.py b/tensorflow/python/training/adam.py index 0c701f4712..c204a5c1ee 100644 --- a/tensorflow/python/training/adam.py +++ b/tensorflow/python/training/adam.py @@ -39,7 +39,7 @@ class AdamOptimizer(optimizer.Optimizer): def __init__(self, learning_rate=0.001, beta1=0.9, beta2=0.999, epsilon=1e-8, use_locking=False, name="Adam"): - """Construct a new Adam optimizer. + r"""Construct a new Adam optimizer. Initialization: diff --git a/tensorflow/tools/ci_build/copy_binary.py b/tensorflow/tools/ci_build/copy_binary.py index 148526492d..40a7443745 100755 --- a/tensorflow/tools/ci_build/copy_binary.py +++ b/tensorflow/tools/ci_build/copy_binary.py @@ -33,7 +33,7 @@ import tempfile import zipfile TF_NIGHTLY_REGEX = (r"(.+)tf_nightly(|_gpu)-(\d\.[\d]{1,2}" - "\.\d.dev[\d]{0,8})-(.+)\.whl") + r"\.\d.dev[\d]{0,8})-(.+)\.whl") BINARY_STRING_TEMPLATE = "%s-%s-%s.whl" diff --git a/tensorflow/tools/dist_test/scripts_allreduce/k8s_generate_yaml_lib.py b/tensorflow/tools/dist_test/scripts_allreduce/k8s_generate_yaml_lib.py index c570d1a9f8..038a712d53 100644 --- a/tensorflow/tools/dist_test/scripts_allreduce/k8s_generate_yaml_lib.py +++ b/tensorflow/tools/dist_test/scripts_allreduce/k8s_generate_yaml_lib.py @@ -195,7 +195,7 @@ def generate_RSA(bits=2048, exponent=65537): def get_change_ssh_port(use_hostnet, port): if use_hostnet == 1: - return "sed -i '/Port 22/c\Port {}' /etc/ssh/sshd_config".format(port) + return r"sed -i '/Port 22/c\Port {}' /etc/ssh/sshd_config".format(port) return '' diff --git a/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda10.0/windows/msvc_wrapper_for_nvcc.py b/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda10.0/windows/msvc_wrapper_for_nvcc.py index 00483951af..2ea20b8b53 100755 --- a/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda10.0/windows/msvc_wrapper_for_nvcc.py +++ b/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda10.0/windows/msvc_wrapper_for_nvcc.py @@ -104,7 +104,7 @@ def InvokeNvcc(argv, log=False): """ src_files = [f for f in argv if - re.search('\.cpp$|\.cc$|\.c$|\.cxx$|\.C$', f)] + re.search(r'\.cpp$|\.cc$|\.c$|\.cxx$|\.C$', f)] if len(src_files) == 0: raise Error('No source files found for cuda compilation.') diff --git a/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/windows/msvc_wrapper_for_nvcc.py b/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/windows/msvc_wrapper_for_nvcc.py index 859b3196d5..2d0898e9cb 100755 --- a/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/windows/msvc_wrapper_for_nvcc.py +++ b/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/windows/msvc_wrapper_for_nvcc.py @@ -104,7 +104,7 @@ def InvokeNvcc(argv, log=False): """ src_files = [f for f in argv if - re.search('\.cpp$|\.cc$|\.c$|\.cxx$|\.C$', f)] + re.search(r'\.cpp$|\.cc$|\.c$|\.cxx$|\.C$', f)] if len(src_files) == 0: raise Error('No source files found for cuda compilation.') diff --git a/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc/windows/msvc_wrapper_for_nvcc.py b/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc/windows/msvc_wrapper_for_nvcc.py index 859b3196d5..2d0898e9cb 100755 --- a/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc/windows/msvc_wrapper_for_nvcc.py +++ b/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc/windows/msvc_wrapper_for_nvcc.py @@ -104,7 +104,7 @@ def InvokeNvcc(argv, log=False): """ src_files = [f for f in argv if - re.search('\.cpp$|\.cc$|\.c$|\.cxx$|\.C$', f)] + re.search(r'\.cpp$|\.cc$|\.c$|\.cxx$|\.C$', f)] if len(src_files) == 0: raise Error('No source files found for cuda compilation.') -- GitLab From 4ee52f8b0751004e76164464938cafed9f1f3623 Mon Sep 17 00:00:00 2001 From: Sanjoy Das Date: Thu, 3 Jan 2019 13:33:29 -0800 Subject: [PATCH 304/622] [TF:XLA] Bump open source llvm revision to r350327 PiperOrigin-RevId: 227740939 --- tensorflow/workspace.bzl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tensorflow/workspace.bzl b/tensorflow/workspace.bzl index 11ce55feda..7792824dbf 100755 --- a/tensorflow/workspace.bzl +++ b/tensorflow/workspace.bzl @@ -498,11 +498,11 @@ def tf_workspace(path_prefix = "", tf_repo_name = ""): tf_http_archive( name = "llvm", build_file = clean_dep("//third_party/llvm:llvm.autogenerated.BUILD"), - sha256 = "65a1aeb29e5940f9f480a41e904659d944e738458afd139caa7bde14bd6aab8a", - strip_prefix = "llvm-331ffd31b3dd49b3f02a27556938b836b679f564", + sha256 = "3a1c8a817d2d9a85b85e1ad776a90faa51f475bb924fbf70383892bb1faceac9", + strip_prefix = "llvm-6da47bee848304d215e3ca08b2a4a4dc9c954310", urls = [ - "https://mirror.bazel.build/github.com/llvm-mirror/llvm/archive/331ffd31b3dd49b3f02a27556938b836b679f564.tar.gz", - "https://github.com/llvm-mirror/llvm/archive/331ffd31b3dd49b3f02a27556938b836b679f564.tar.gz", + "https://mirror.bazel.build/github.com/llvm-mirror/llvm/archive/6da47bee848304d215e3ca08b2a4a4dc9c954310.tar.gz", + "https://github.com/llvm-mirror/llvm/archive/6da47bee848304d215e3ca08b2a4a4dc9c954310.tar.gz", ], ) -- GitLab From 6f4410f580ff48078f33c343b72d742095535fae Mon Sep 17 00:00:00 2001 From: Andrew Selle Date: Thu, 3 Jan 2019 14:05:20 -0800 Subject: [PATCH 305/622] Disable on mac a flaky test. PiperOrigin-RevId: 227746737 --- tensorflow/lite/kernels/internal/BUILD | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tensorflow/lite/kernels/internal/BUILD b/tensorflow/lite/kernels/internal/BUILD index 5eb8a353b7..92b4705908 100644 --- a/tensorflow/lite/kernels/internal/BUILD +++ b/tensorflow/lite/kernels/internal/BUILD @@ -654,6 +654,8 @@ cc_test( "logsoftmax_quantized_test.cc", ], tags = [ + # TODO(b/122242739): Reenable after fixing the flakiness? + "nomac", "tflite_not_portable", ], deps = [ -- GitLab From 4dc0ae2baf454ba3d3ee531d889079b0ff05bc95 Mon Sep 17 00:00:00 2001 From: Blake Hechtman Date: Thu, 3 Jan 2019 14:13:01 -0800 Subject: [PATCH 306/622] Fix typo. PiperOrigin-RevId: 227748088 --- tensorflow/compiler/xla/service/layout_assignment.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/compiler/xla/service/layout_assignment.cc b/tensorflow/compiler/xla/service/layout_assignment.cc index ebf7a88fae..09246db68e 100644 --- a/tensorflow/compiler/xla/service/layout_assignment.cc +++ b/tensorflow/compiler/xla/service/layout_assignment.cc @@ -1239,7 +1239,7 @@ Status LayoutAssignment::PropagateUseConstraintToDefs( namespace { // A transpose or a reshape that only changes trivial dimensions have meaningful // layouts that are valuable to propagate in a depthfirst manner to avoid -// unassinged layouts in the graph. +// unassigned layouts in the graph. bool InstructionShouldPropagateDepthFirst(const HloInstruction& hlo) { switch (hlo.opcode()) { case HloOpcode::kReshape: -- GitLab From 7c9323bedc48c98be3c07b72ec1d6f4dccdefb35 Mon Sep 17 00:00:00 2001 From: Penporn Koanantakool Date: Thu, 3 Jan 2019 14:22:17 -0800 Subject: [PATCH 307/622] Turn on MKL-DNN contraction kernels by default. To disable them, build with --define=tensorflow_mkldnn_contraction_kernel=0. Make TestFoldFusedBatchNormsWithConcat use ExpectClose which is more suitable for floating-point comparison than ExpectNear. PiperOrigin-RevId: 227749705 --- .bazelrc | 3 --- tensorflow/tools/graph_transforms/fold_old_batch_norms_test.cc | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.bazelrc b/.bazelrc index ceba7bfdba..cd7e13ddfc 100644 --- a/.bazelrc +++ b/.bazelrc @@ -93,9 +93,6 @@ build --define=PREFIX=/usr build --define=LIBDIR=$(PREFIX)/lib build --define=INCLUDEDIR=$(PREFIX)/include -# Disable MKL-DNN contraction kernels by default. -build --define=tensorflow_mkldnn_contraction_kernel=0 - # Default options should come above this line # Options from ./configure diff --git a/tensorflow/tools/graph_transforms/fold_old_batch_norms_test.cc b/tensorflow/tools/graph_transforms/fold_old_batch_norms_test.cc index 435f46c107..6c7174926d 100644 --- a/tensorflow/tools/graph_transforms/fold_old_batch_norms_test.cc +++ b/tensorflow/tools/graph_transforms/fold_old_batch_norms_test.cc @@ -291,7 +291,7 @@ class FoldOldBatchNormsTest : public ::testing::Test { std::vector fused_outputs; TF_ASSERT_OK(fused_session->Run({}, {"output"}, {}, &fused_outputs)); - test::ExpectTensorNear(original_outputs[0], fused_outputs[0], 1e-5); + test::ExpectClose(original_outputs[0], fused_outputs[0]); for (const NodeDef& node : fused_graph_def.node()) { EXPECT_NE("FusedBatchNorm", node.op()); -- GitLab From 9d78259894c0b533ac6454c9eee27f8eb8bef638 Mon Sep 17 00:00:00 2001 From: Skye Wanderman-Milne Date: Thu, 3 Jan 2019 14:29:35 -0800 Subject: [PATCH 308/622] Add "element_shape" attr to TensorListConcat and use it in _GraphTensorArrayV2. When using while_v2, this change prevents errors like this: All except the first dimension must be fully defined when concating an empty tensor list. element_shape: Previously, TensorListConcat op could only determine the element size from the TensorList object at runtime. In the case of a while loop that executes zero times, it wouldn't be able to determine the size since no element was ever seen by the runtime. However, we may have determined the element size during graph construction, so we use the attribute to tell the runtime what the element size is. Note that the original TensorArray implementation already does this. PiperOrigin-RevId: 227750868 --- tensorflow/core/kernels/list_kernels.h | 44 +++++++++++++------ tensorflow/core/ops/list_ops.cc | 14 ++++-- tensorflow/python/ops/list_ops.py | 6 ++- .../ops/parallel_for/control_flow_ops_test.py | 5 --- tensorflow/python/ops/tensor_array_ops.py | 12 +++-- 5 files changed, 53 insertions(+), 28 deletions(-) diff --git a/tensorflow/core/kernels/list_kernels.h b/tensorflow/core/kernels/list_kernels.h index 686679474c..99b4f42084 100644 --- a/tensorflow/core/kernels/list_kernels.h +++ b/tensorflow/core/kernels/list_kernels.h @@ -158,6 +158,17 @@ class TensorListConcat : public OpKernel { std::vector::ConstMatrix>>; explicit TensorListConcat(OpKernelConstruction* c) : OpKernel(c) { OP_REQUIRES_OK(c, c->GetAttr("element_dtype", &element_dtype_)); + // TODO(skyewm): the HasAttr check can be removed once the + // element_shape_except_first_dim attr has been checked in for 2 weeks + // (around 1/14/2019). + if (c->HasAttr("element_shape")) { + PartialTensorShape element_shape; + OP_REQUIRES_OK(c, c->GetAttr("element_shape", &element_shape)); + if (!element_shape.unknown_rank()) { + element_shape_except_first_dim_ = PartialTensorShape( + gtl::ArraySlice(element_shape.dim_sizes()).subspan(1)); + } + } } ~TensorListConcat() {} @@ -178,29 +189,33 @@ class TensorListConcat : public OpKernel { " but list elements ", DataTypeString(tensor_list->element_dtype))); // If the TensorList is empty, its element_shape must be fully defined // except for the first dimension. - PartialTensorShape shape_except_first_dim; - if (!tensor_list->element_shape.unknown_rank()) { - OP_REQUIRES(c, tensor_list->element_shape.dims() >= 1, - errors::InvalidArgument( - "Concat requires elements to be at least vectors, ", - "found scalars instead.")); - shape_except_first_dim = PartialTensorShape( - gtl::ArraySlice(tensor_list->element_shape.dim_sizes()) - .subspan(1)); + if (!element_shape_except_first_dim_.IsFullyDefined()) { + if (!tensor_list->element_shape.unknown_rank()) { + OP_REQUIRES(c, tensor_list->element_shape.dims() >= 1, + errors::InvalidArgument( + "Concat requires elements to be at least vectors, ", + "found scalars instead.")); + PartialTensorShape shape_except_first_dim( + gtl::ArraySlice(tensor_list->element_shape.dim_sizes()) + .subspan(1)); + PartialTensorShape tmp = element_shape_except_first_dim_; + OP_REQUIRES_OK(c, tmp.MergeWith(shape_except_first_dim, + &element_shape_except_first_dim_)); + } } OP_REQUIRES(c, !tensor_list->tensors.empty() || - shape_except_first_dim.IsFullyDefined(), + element_shape_except_first_dim_.IsFullyDefined(), errors::InvalidArgument( "All except the first dimension must be fully defined ", "when concating an empty tensor list. element_shape: ", tensor_list->element_shape.DebugString())); // 1. Compute the shape of the output tensor. - // If `shape_except_first_dim` is fully-defined we just prepend the leading - // dim to it. Otherwise we use the shape of the first element tensor and - // check to make sure shapes of all tensors are compatible. + // If `element_shape_except_first_dim_` is fully-defined we just prepend the + // leading dim to it. Otherwise we use the shape of the first element tensor + // and check to make sure shapes of all tensors are compatible. TensorShape output_shape; - if (!shape_except_first_dim.AsTensorShape(&output_shape)) { + if (!element_shape_except_first_dim_.AsTensorShape(&output_shape)) { const Tensor& element_tensor = tensor_list->tensors[0]; OP_REQUIRES( c, TensorShapeUtils::IsVectorOrHigher(element_tensor.shape()), @@ -268,6 +283,7 @@ class TensorListConcat : public OpKernel { private: DataType element_dtype_; + PartialTensorShape element_shape_except_first_dim_; }; template diff --git a/tensorflow/core/ops/list_ops.cc b/tensorflow/core/ops/list_ops.cc index 5722170071..cbc9c7a2f4 100644 --- a/tensorflow/core/ops/list_ops.cc +++ b/tensorflow/core/ops/list_ops.cc @@ -212,10 +212,16 @@ REGISTER_OP("TensorListConcat") .Output("tensor: element_dtype") .Output("lengths: int64") .Attr("element_dtype: type") + .Attr("element_shape: shape = { unknown_rank: true }") .SetShapeFn([](shape_inference::InferenceContext* c) { DataType element_dtype; TF_RETURN_IF_ERROR(c->GetAttr("element_dtype", &element_dtype)); - shape_inference::ShapeHandle element_shape = c->UnknownShape(); + PartialTensorShape raw_element_shape; + TF_RETURN_IF_ERROR(c->GetAttr("element_shape", &raw_element_shape)); + shape_inference::ShapeHandle element_shape; + TF_RETURN_IF_ERROR(c->MakeShapeFromPartialTensorShape(raw_element_shape, + &element_shape)); + auto* handle_data = c->input_handle_shapes_and_types(0); if (handle_data != nullptr && handle_data->size() != 1) { return errors::InvalidArgument( @@ -231,10 +237,10 @@ REGISTER_OP("TensorListConcat") DataTypeString(list_shape_type.dtype), " but expected type ", DataTypeString(element_dtype)); } - shape_inference::ShapeHandle ignored; + shape_inference::ShapeHandle merged; TF_RETURN_IF_ERROR( - c->Merge(element_shape, list_shape_type.shape, &ignored)); - element_shape = list_shape_type.shape; + c->Merge(element_shape, list_shape_type.shape, &merged)); + element_shape = merged; } if (c->RankKnown(element_shape)) { shape_inference::ShapeHandle result; diff --git a/tensorflow/python/ops/list_ops.py b/tensorflow/python/ops/list_ops.py index 3eac97655a..7bb9ca89e0 100644 --- a/tensorflow/python/ops/list_ops.py +++ b/tensorflow/python/ops/list_ops.py @@ -71,11 +71,13 @@ def tensor_list_from_tensor(tensor, element_shape, name=None): name=name) -def tensor_list_concat(input_handle, element_dtype, name=None): +def tensor_list_concat(input_handle, element_dtype, element_shape=None, + name=None): # Ignore the lengths output of TensorListConcat. It is only used during # gradient computation. return gen_list_ops.tensor_list_concat( - input_handle=input_handle, element_dtype=element_dtype, name=name)[0] + input_handle=input_handle, element_dtype=element_dtype, + element_shape=element_shape, name=name)[0] def tensor_list_split(tensor, element_shape, lengths, name=None): diff --git a/tensorflow/python/ops/parallel_for/control_flow_ops_test.py b/tensorflow/python/ops/parallel_for/control_flow_ops_test.py index 8acb0d839c..933bddd8cc 100644 --- a/tensorflow/python/ops/parallel_for/control_flow_ops_test.py +++ b/tensorflow/python/ops/parallel_for/control_flow_ops_test.py @@ -27,7 +27,6 @@ from tensorflow.core.example import example_pb2 from tensorflow.core.example import feature_pb2 from tensorflow.python.client import session from tensorflow.python.eager import backprop -from tensorflow.python.eager import context from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops @@ -105,10 +104,6 @@ class PForTest(test.TestCase): flags.FLAGS.op_conversion_fallback_to_while_loop = False def test_parallel_iterations(self): - # TODO(b/121334512): Remove this check once this passes in Eager mode. - if context.executing_eagerly(): - return - for parallel_iterations in [2, 3, 8, 10]: x = random_ops.random_uniform([8, 3]) diff --git a/tensorflow/python/ops/tensor_array_ops.py b/tensorflow/python/ops/tensor_array_ops.py index 9b8bd2c8d3..90a8b0af46 100644 --- a/tensorflow/python/ops/tensor_array_ops.py +++ b/tensorflow/python/ops/tensor_array_ops.py @@ -588,10 +588,16 @@ class _GraphTensorArrayV2(object): def concat(self, name=None): """See TensorArray.""" - value = list_ops.tensor_list_concat( - input_handle=self._flow, element_dtype=self._dtype, name=name) if self._element_shape and self._element_shape[0].dims is not None: - value.set_shape([None] + self._element_shape[0].dims[1:]) + element_shape = [None] + self._element_shape[0].dims[1:] + else: + element_shape = None + + value = list_ops.tensor_list_concat( + input_handle=self._flow, + element_dtype=self._dtype, + element_shape=element_shape, + name=name) return value @tf_should_use.should_use_result -- GitLab From 000dc6e0face09fd2a2886b611e6d1d3d7776e5d Mon Sep 17 00:00:00 2001 From: Jian Li Date: Thu, 3 Jan 2019 14:36:59 -0800 Subject: [PATCH 309/622] Update svdf test case. PiperOrigin-RevId: 227752127 --- tensorflow/lite/kernels/svdf_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/lite/kernels/svdf_test.cc b/tensorflow/lite/kernels/svdf_test.cc index fc2bb49223..c420260bf5 100644 --- a/tensorflow/lite/kernels/svdf_test.cc +++ b/tensorflow/lite/kernels/svdf_test.cc @@ -209,7 +209,7 @@ class HybridSVDFOpModel : public BaseSVDFOpModel { tensor_type_ = tensor_type; } - void SetWeights(int weights_idx, std::vector f) { + void SetWeights(int weights_idx, const std::vector& f) { if (tensor_type_ == TensorType_UINT8) { SymmetricQuantizeAndPopulate(weights_idx, f); } else { -- GitLab From 7d57d32e440bc4f1654fb217fd701531a82c3587 Mon Sep 17 00:00:00 2001 From: Tim Shen Date: Thu, 3 Jan 2019 14:45:53 -0800 Subject: [PATCH 310/622] Fix dso_loader.cc's includes. PiperOrigin-RevId: 227753675 --- tensorflow/contrib/mpi_collectives/BUILD | 2 +- tensorflow/stream_executor/platform/default/dso_loader.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tensorflow/contrib/mpi_collectives/BUILD b/tensorflow/contrib/mpi_collectives/BUILD index ecac06354d..d943ae6880 100644 --- a/tensorflow/contrib/mpi_collectives/BUILD +++ b/tensorflow/contrib/mpi_collectives/BUILD @@ -52,7 +52,7 @@ tf_custom_op_library( deps = [ ":mpi_defines", ":mpi_message_proto_cc", - "//tensorflow/stream_executor:stream_executor_headers_lib", + "//tensorflow/core:stream_executor_headers_lib", "//third_party/mpi", ], ) diff --git a/tensorflow/stream_executor/platform/default/dso_loader.cc b/tensorflow/stream_executor/platform/default/dso_loader.cc index 0f0bce3253..668eeee3f3 100644 --- a/tensorflow/stream_executor/platform/default/dso_loader.cc +++ b/tensorflow/stream_executor/platform/default/dso_loader.cc @@ -28,7 +28,7 @@ limitations under the License. #include "tensorflow/stream_executor/lib/path.h" #include "tensorflow/stream_executor/lib/str_util.h" #include "tensorflow/stream_executor/lib/stringprintf.h" -#include "tensorflow/stream_executor/platform/dso_loader.h" +#include "tensorflow/stream_executor/platform/default/dso_loader.h" #include "tensorflow/stream_executor/platform/logging.h" #include "tensorflow/stream_executor/platform/port.h" -- GitLab From 1051c377051b2ee24a495318737358d9ccf7280f Mon Sep 17 00:00:00 2001 From: Trevor Morris Date: Wed, 2 Jan 2019 14:34:42 -0800 Subject: [PATCH 311/622] Copy ben's changes --- .../contrib/tensorrt/convert/convert_nodes.cc | 25 ++- .../contrib/tensorrt/test/conv2d_test.py | 172 ++++++++++++++++++ 2 files changed, 194 insertions(+), 3 deletions(-) create mode 100644 tensorflow/contrib/tensorrt/test/conv2d_test.py diff --git a/tensorflow/contrib/tensorrt/convert/convert_nodes.cc b/tensorflow/contrib/tensorrt/convert/convert_nodes.cc index 2a402fe699..84d7f1aee5 100644 --- a/tensorflow/contrib/tensorrt/convert/convert_nodes.cc +++ b/tensorflow/contrib/tensorrt/convert/convert_nodes.cc @@ -62,14 +62,14 @@ limitations under the License. #define TFTRT_RETURN_ERROR_IF_FALSE(status, node) \ do { \ - if (status == false) { \ + if ((status) == false) { \ TFTRT_INTERNAL_ERROR_AT_NODE(node); \ } \ } while (0) #define TFTRT_RETURN_ERROR_IF_NULLPTR(ptr, node) \ do { \ - if (ptr == nullptr) { \ + if ((ptr) == nullptr) { \ TFTRT_INTERNAL_ERROR_AT_NODE(node); \ } \ } while (0) @@ -1577,12 +1577,14 @@ tensorflow::Status ConvertConv2DHelper(OpConverterParams* params, int group) { const nvinfer1::ITensor* tensor = inputs.at(0).tensor(); TFAttrs attrs(node_def); + int c_index = 1; int h_index = 2; int w_index = 3; auto data_format = attrs.get("data_format"); if (data_format == "NHWC") { TF_RETURN_IF_ERROR(params->converter->TransposeTensor( const_cast(tensor), {0, 3, 1, 2}, &tensor)); + c_index = 3; h_index = 1; w_index = 2; // TODO(jie): transpose it @@ -1618,14 +1620,30 @@ tensorflow::Status ConvertConv2DHelper(OpConverterParams* params, int group) { << tf_stride[3]; const nvinfer1::DimsHW stride(tf_stride[h_index], tf_stride[w_index]); + auto tf_dilations = attrs.get>("dilations"); + if ((int)tf_dilations.size() != 4) { + return tensorflow::errors::InvalidArgument( + "Convolution dilations field must specify 4 dimensions " + + node_def.name()); + } + if (tf_dilations[0] != 1 || tf_dilations[c_index] != 1) { + return tensorflow::errors::Unimplemented( + "Dilation rate must be 1 for batch and channel dimensions, at ", + node_def.name()); + } + nvinfer1::DimsHW dilation(tf_dilations[h_index], tf_dilations[w_index]); + std::vector> padding; // TODO(jie): padding. if (attrs.get("padding") == "SAME") { // This is NCHW tensor with no batch dimension. // 1 -> h // 2 -> w + nvinfer1::DimsHW effective_kernel_size = kernel_size; + effective_kernel_size.h() += (kernel_size.h() - 1) * (dilation.h() - 1); + effective_kernel_size.w() += (kernel_size.w() - 1) * (dilation.w() - 1); padding = CreateSamePadding( - stride, kernel_size, + stride, effective_kernel_size, {static_cast(tensor_dim.d[1]), static_cast(tensor_dim.d[2])}); } else { padding = {{0, 0}, {0, 0}}; @@ -1659,6 +1677,7 @@ tensorflow::Status ConvertConv2DHelper(OpConverterParams* params, int group) { layer->setPadding({padding[0].first, padding[1].first}); layer->setName(node_def.name().c_str()); layer->setNbGroups(num_groups); + layer->setDilation(dilation); const nvinfer1::ITensor* output_tensor = layer->getOutput(0); VLOG(2) << "TENSOR out: " << DebugString(output_tensor->getDimensions()); VLOG(2) << "data_format: " << data_format; diff --git a/tensorflow/contrib/tensorrt/test/conv2d_test.py b/tensorflow/contrib/tensorrt/test/conv2d_test.py new file mode 100644 index 0000000000..bcf20fc504 --- /dev/null +++ b/tensorflow/contrib/tensorrt/test/conv2d_test.py @@ -0,0 +1,172 @@ +# Copyright 2018 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. +# ============================================================================== +"""Model script to test TF-TensorRT integration.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +from tensorflow.contrib.tensorrt.test import tf_trt_integration_test_base as trt_test +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import gen_array_ops +from tensorflow.python.ops import gen_nn_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import nn_ops +from tensorflow.python.platform import test + + +def conv2d_layer(inputs, filters, kernel_size, strides=(1, 1), padding='valid', + data_format='channels_last', dilation_rate=(1, 1), name=None): + dtype = inputs.dtype + c_axis = -1 if data_format == 'channels_last' else 1 + nchan = inputs.shape[c_axis] + weights_shape = (kernel_size[0], kernel_size[1], nchan, filters) + weights = constant_op.constant(np.random.randn(*weights_shape), dtype=dtype) + padding = padding.upper() + if data_format == 'channels_last': + strides = [1] + list(strides) + [1] + dilations = [1] + list(dilation_rate) + [1] + data_format = 'NHWC' + else: + strides = [1, 1] + list(strides) + dilations = [1, 1] + list(dilation_rate) + data_format = 'NCHW' + return gen_nn_ops.conv2d(inputs, weights, strides=strides, padding=padding, + dilations=dilations, data_format=data_format) + +def div_round_up(n, d): + return (n - 1) // d + 1 + +class Conv2DNCHWTest(trt_test.TfTrtIntegrationTestBase): + + def GetParams(self): + """Testing conversion of Conv2D (data_format=NCHW) in TF-TRT conversion.""" + np.random.seed(1234) + dtype = dtypes.float32 + input_name = "input" + n, c, h, w = 13, 3, 7, 11 + num_filters = 5 + input_dims = [n, c, h, w] + output_name = "output" + g = ops.Graph() + with g.as_default(): + inp = array_ops.placeholder( + dtype=dtype, shape=[None] + input_dims[1:], name=input_name) + with g.device("/GPU:0"): + results = [] + for kernel_size in [(3, 3), (3, 2)]: + for dilation_rate in [(1, 1), (2, 3)]: + result = conv2d_layer(inp, num_filters, kernel_size, + dilation_rate=dilation_rate, padding='same', + data_format='channels_first') + results.append(result) + output = sum(results) + output = array_ops.identity(output, name=output_name) + return trt_test.TfTrtIntegrationTestParams( + gdef=g.as_graph_def(), + input_names=[input_name], + input_dims=[input_dims], + output_names=[output_name], + expected_output_dims=[(n, num_filters, h, w)]) + + def ExpectedEnginesToBuild(self, run_params): + """Return the expected engines to build.""" + return ["my_trt_op_0"] + + +class Conv2DStridedNCHWTest(trt_test.TfTrtIntegrationTestBase): + + def GetParams(self): + """Testing conversion of strided Conv2D (data_format=NCHW) in TF-TRT + conversion.""" + np.random.seed(1234) + dtype = dtypes.float32 + input_name = "input" + n, c, h, w = 13, 3, 7, 11 + num_filters = 5 + input_dims = [n, c, h, w] + output_name = "output" + g = ops.Graph() + with g.as_default(): + inp = array_ops.placeholder( + dtype=dtype, shape=[None] + input_dims[1:], name=input_name) + with g.device("/GPU:0"): + output = inp + output = conv2d_layer(output, num_filters, (3, 2), strides=(2, 2), + padding='same', data_format='channels_first') + h = div_round_up(h, 2) + w = div_round_up(w, 2) + output = conv2d_layer(output, num_filters, (3, 3), strides=(2, 2), + dilation_rate=(2, 3), padding='same', + data_format='channels_first') + h = div_round_up(h, 2) + w = div_round_up(w, 2) + output = array_ops.identity(output, name=output_name) + return trt_test.TfTrtIntegrationTestParams( + gdef=g.as_graph_def(), + input_names=[input_name], + input_dims=[input_dims], + output_names=[output_name], + expected_output_dims=[(n, num_filters, h, w)]) + + def ExpectedEnginesToBuild(self, run_params): + """Return the expected engines to build.""" + return ["my_trt_op_0"] + + +class Conv2DNHWCTest(trt_test.TfTrtIntegrationTestBase): + + def GetParams(self): + """Testing conversion of Conv2D (data_format=NHWC) in TF-TRT conversion.""" + np.random.seed(1234) + dtype = dtypes.float32 + input_name = "input" + n, h, w, c = 13, 7, 11, 3 + num_filters = 5 + input_dims = [n, h, w, c] + output_name = "output" + g = ops.Graph() + with g.as_default(): + inp = array_ops.placeholder( + dtype=dtype, shape=[None] + input_dims[1:], name=input_name) + with g.device("/GPU:0"): + results = [] + for kernel_size in [(3, 3), (3, 2)]: + for dilation_rate in [(1, 1), (2, 3)]: + result = conv2d_layer(inp, num_filters, kernel_size, + dilation_rate=dilation_rate, padding='same', + data_format='channels_last') + results.append(result) + output = sum(results) + output = array_ops.identity(output, name=output_name) + return trt_test.TfTrtIntegrationTestParams( + gdef=g.as_graph_def(), + input_names=[input_name], + input_dims=[input_dims], + output_names=[output_name], + expected_output_dims=[(n, h, w, num_filters)]) + + def ExpectedEnginesToBuild(self, run_params): + """Return the expected engines to build.""" + return ["my_trt_op_0"] + + +if __name__ == "__main__": + test.main() -- GitLab From db95d28671c790de369a84c891f9bc971663e0e4 Mon Sep 17 00:00:00 2001 From: Trevor Morris Date: Wed, 2 Jan 2019 15:12:40 -0800 Subject: [PATCH 312/622] Tidy up Conv2D converter --- tensorflow/contrib/tensorrt/BUILD | 1 + .../contrib/tensorrt/convert/convert_nodes.cc | 94 ++++++++----------- .../contrib/tensorrt/test/conv2d_test.py | 6 +- 3 files changed, 43 insertions(+), 58 deletions(-) diff --git a/tensorflow/contrib/tensorrt/BUILD b/tensorflow/contrib/tensorrt/BUILD index 089c9ad97c..c17dea02f1 100644 --- a/tensorflow/contrib/tensorrt/BUILD +++ b/tensorflow/contrib/tensorrt/BUILD @@ -491,6 +491,7 @@ cuda_py_tests( "test/binary_tensor_weight_broadcast_test.py", "test/concatenation_test.py", "test/const_broadcast_test.py", + "test/conv2d_test.py", "test/identity_output_test.py", "test/manual_test.py", "test/memory_alignment_test.py", diff --git a/tensorflow/contrib/tensorrt/convert/convert_nodes.cc b/tensorflow/contrib/tensorrt/convert/convert_nodes.cc index 84d7f1aee5..614cc47130 100644 --- a/tensorflow/contrib/tensorrt/convert/convert_nodes.cc +++ b/tensorflow/contrib/tensorrt/convert/convert_nodes.cc @@ -62,14 +62,14 @@ limitations under the License. #define TFTRT_RETURN_ERROR_IF_FALSE(status, node) \ do { \ - if ((status) == false) { \ + if ((status) == false) { \ TFTRT_INTERNAL_ERROR_AT_NODE(node); \ } \ } while (0) #define TFTRT_RETURN_ERROR_IF_NULLPTR(ptr, node) \ do { \ - if ((ptr) == nullptr) { \ + if ((ptr) == nullptr) { \ TFTRT_INTERNAL_ERROR_AT_NODE(node); \ } \ } while (0) @@ -1567,41 +1567,56 @@ tensorflow::Status ConvertConv2DHelper(OpConverterParams* params, int group) { node_def.name()); } TRT_ShapedWeights weights_rsck = inputs.at(1).weights(); - VLOG(2) << "weight shape: " << weights_rsck.DebugString(); if (weights_rsck.shape_.nbDims != 4) { return tensorflow::errors::Internal( "Conv2D expects kernel of dimension 4, at: " + node_def.name()); } + TFAttrs attrs(node_def); + auto data_format = attrs.get("data_format"); + int c_index = (data_format == "NHWC") ? 3 : 1; + int h_index = (data_format == "NHWC") ? 1 : 2; + int w_index = (data_format == "NHWC") ? 2 : 3; + auto tf_dilations = attrs.get>("dilations"); + if (tf_dilations.size() != 4) { + return tensorflow::errors::InvalidArgument( + "Convolution dilations field must specify 4 dimensions, at ", + node_def.name()); + } + if (tf_dilations[0] != 1 || tf_dilations[c_index] != 1) { + return tensorflow::errors::Unimplemented( + "Dilation rate must be 1 for batch and channel dimensions, at ", + node_def.name()); + } + nvinfer1::DimsHW dilation(tf_dilations[h_index], tf_dilations[w_index]); + const auto tf_stride = attrs.get>("strides"); + if (tf_stride[0] != 1 || tf_stride[c_index] != 1) { + return tensorflow::errors::Unimplemented( + "Stride must be 1 for batch and channel dimensions, at ", + node_def.name()); + } + const nvinfer1::DimsHW stride(tf_stride[h_index], tf_stride[w_index]); if (params->validation_only) return tensorflow::Status::OK(); const nvinfer1::ITensor* tensor = inputs.at(0).tensor(); - TFAttrs attrs(node_def); - int c_index = 1; - int h_index = 2; - int w_index = 3; - auto data_format = attrs.get("data_format"); - if (data_format == "NHWC") { + // Transpose to NCHW (NCHW is required for IConvLayer). + const bool need_transpose = (data_format == "NHWC"); + if (need_transpose) { TF_RETURN_IF_ERROR(params->converter->TransposeTensor( const_cast(tensor), {0, 3, 1, 2}, &tensor)); - c_index = 3; - h_index = 1; - w_index = 2; - // TODO(jie): transpose it } - - // tensor after transpose (NCHW) + // Dimensions of transposed tensor. const auto tensor_dim = tensor->getDimensions(); + // This is a depthwise convolution when num_groups is 0. Otherwise, num_groups + // will be 1. int num_groups = group; - if (num_groups == 0) num_groups = tensor_dim.d[0]; // depthwise convolution - VLOG(2) << "groups count: " << num_groups; + if (num_groups == 0) num_groups = tensor_dim.d[0]; if (params->converter->precision_mode() == FP16MODE) { weights_rsck = ConvertFP32ToFP16(params->weight_store, inputs.at(1).weights()); } - TRT_ShapedWeights weights = params->weight_store->GetTempWeights(weights_rsck); ReorderRSCKToKCRS(weights_rsck, &weights, num_groups); @@ -1610,35 +1625,10 @@ tensorflow::Status ConvertConv2DHelper(OpConverterParams* params, int group) { nvinfer1::DimsHW kernel_size; kernel_size.h() = weights.shape_.d[2]; kernel_size.w() = weights.shape_.d[3]; - VLOG(2) << "RSCK: " << weights.DebugString(); - VLOG(2) << "kernel size: " << kernel_size.h() << ", " << kernel_size.w(); - - // TODO(jie): stride. (NHWC/NCHW) - const auto tf_stride = attrs.get>("strides"); - VLOG(2) << "h_INDEX" << h_index << ", w_index " << w_index; - VLOG(2) << "stride: " << tf_stride[0] << tf_stride[1] << tf_stride[2] - << tf_stride[3]; - const nvinfer1::DimsHW stride(tf_stride[h_index], tf_stride[w_index]); - - auto tf_dilations = attrs.get>("dilations"); - if ((int)tf_dilations.size() != 4) { - return tensorflow::errors::InvalidArgument( - "Convolution dilations field must specify 4 dimensions " + - node_def.name()); - } - if (tf_dilations[0] != 1 || tf_dilations[c_index] != 1) { - return tensorflow::errors::Unimplemented( - "Dilation rate must be 1 for batch and channel dimensions, at ", - node_def.name()); - } - nvinfer1::DimsHW dilation(tf_dilations[h_index], tf_dilations[w_index]); + // Add padding. std::vector> padding; - // TODO(jie): padding. if (attrs.get("padding") == "SAME") { - // This is NCHW tensor with no batch dimension. - // 1 -> h - // 2 -> w nvinfer1::DimsHW effective_kernel_size = kernel_size; effective_kernel_size.h() += (kernel_size.h() - 1) * (dilation.h() - 1); effective_kernel_size.w() += (kernel_size.w() - 1) * (dilation.w() - 1); @@ -1648,13 +1638,9 @@ tensorflow::Status ConvertConv2DHelper(OpConverterParams* params, int group) { } else { padding = {{0, 0}, {0, 0}}; } - if (padding[0].first != padding[0].second || padding[1].first != padding[1].second) { - // TODO(jie): handle asymmetric padding - VLOG(2) << "Padding!!!: " << padding[0].first << padding[0].second - << padding[1].first << padding[1].second; - VLOG(2) << "TENSOR before: " << DebugString(tensor->getDimensions()); + // Handle asymmetric padding. auto pad_layer = params->converter->network()->addPadding( *const_cast(tensor), nvinfer1::DimsHW(padding[0].first, padding[1].first), @@ -1664,25 +1650,23 @@ tensorflow::Status ConvertConv2DHelper(OpConverterParams* params, int group) { const_cast(tensor), pad_layer->getOutput(0)); padding = {{0, 0}, {0, 0}}; tensor = pad_layer->getOutput(0); - VLOG(2) << "TENSOR after: " << DebugString(tensor->getDimensions()); } + // Add convolution. nvinfer1::IConvolutionLayer* layer = params->converter->network()->addConvolution( *const_cast(tensor), noutput, kernel_size, weights.GetTrtWeights(), biases.GetTrtWeights()); TFTRT_RETURN_ERROR_IF_NULLPTR(layer, node_def.name()); - layer->setStride(stride); layer->setPadding({padding[0].first, padding[1].first}); layer->setName(node_def.name().c_str()); layer->setNbGroups(num_groups); layer->setDilation(dilation); const nvinfer1::ITensor* output_tensor = layer->getOutput(0); - VLOG(2) << "TENSOR out: " << DebugString(output_tensor->getDimensions()); - VLOG(2) << "data_format: " << data_format; - if (data_format == "NHWC") { - // TODO(jie): transpose it back! + + // Restore transpose. + if (need_transpose) { TF_RETURN_IF_ERROR(params->converter->TransposeTensor( const_cast(output_tensor), {0, 2, 3, 1}, &output_tensor)); diff --git a/tensorflow/contrib/tensorrt/test/conv2d_test.py b/tensorflow/contrib/tensorrt/test/conv2d_test.py index bcf20fc504..c5228dea33 100644 --- a/tensorflow/contrib/tensorrt/test/conv2d_test.py +++ b/tensorflow/contrib/tensorrt/test/conv2d_test.py @@ -88,7 +88,7 @@ class Conv2DNCHWTest(trt_test.TfTrtIntegrationTestBase): def ExpectedEnginesToBuild(self, run_params): """Return the expected engines to build.""" - return ["my_trt_op_0"] + return ["TRTEngineOp_0"] class Conv2DStridedNCHWTest(trt_test.TfTrtIntegrationTestBase): @@ -128,7 +128,7 @@ class Conv2DStridedNCHWTest(trt_test.TfTrtIntegrationTestBase): def ExpectedEnginesToBuild(self, run_params): """Return the expected engines to build.""" - return ["my_trt_op_0"] + return ["TRTEngineOp_0"] class Conv2DNHWCTest(trt_test.TfTrtIntegrationTestBase): @@ -165,7 +165,7 @@ class Conv2DNHWCTest(trt_test.TfTrtIntegrationTestBase): def ExpectedEnginesToBuild(self, run_params): """Return the expected engines to build.""" - return ["my_trt_op_0"] + return ["TRTEngineOp_0"] if __name__ == "__main__": -- GitLab From a42746772faea0ab58d421dc68220ebc98c053bc Mon Sep 17 00:00:00 2001 From: Trevor Morris Date: Wed, 2 Jan 2019 16:13:58 -0800 Subject: [PATCH 313/622] Add unit test for Conv2D validator --- .../contrib/tensorrt/convert/convert_nodes.cc | 23 ++-- .../tensorrt/convert/convert_nodes_test.cc | 102 ++++++++++++++++++ 2 files changed, 118 insertions(+), 7 deletions(-) diff --git a/tensorflow/contrib/tensorrt/convert/convert_nodes.cc b/tensorflow/contrib/tensorrt/convert/convert_nodes.cc index 614cc47130..34ba2aeaa4 100644 --- a/tensorflow/contrib/tensorrt/convert/convert_nodes.cc +++ b/tensorflow/contrib/tensorrt/convert/convert_nodes.cc @@ -1556,6 +1556,11 @@ enum class ConvolutionType { DEFAULT, DEPTHWISE_CONV }; tensorflow::Status ConvertConv2DHelper(OpConverterParams* params, int group) { const auto& inputs = params->inputs; const auto& node_def = params->node_def; + if (inputs.size() != 2) { + return tensorflow::errors::InvalidArgument("Two inputs are expected for ", + node_def.op(), ", at ", + node_def.name()); + } if (inputs.at(0).is_weights()) { return tensorflow::errors::Unimplemented( node_def.op(), " is only implemented for tensors, not weights, at ", @@ -1568,8 +1573,8 @@ tensorflow::Status ConvertConv2DHelper(OpConverterParams* params, int group) { } TRT_ShapedWeights weights_rsck = inputs.at(1).weights(); if (weights_rsck.shape_.nbDims != 4) { - return tensorflow::errors::Internal( - "Conv2D expects kernel of dimension 4, at: " + node_def.name()); + return tensorflow::errors::InvalidArgument( + "Conv2D expects kernel of dimension 4, at " + node_def.name()); } TFAttrs attrs(node_def); auto data_format = attrs.get("data_format"); @@ -1587,8 +1592,13 @@ tensorflow::Status ConvertConv2DHelper(OpConverterParams* params, int group) { "Dilation rate must be 1 for batch and channel dimensions, at ", node_def.name()); } - nvinfer1::DimsHW dilation(tf_dilations[h_index], tf_dilations[w_index]); + const nvinfer1::DimsHW dilation(tf_dilations[h_index], tf_dilations[w_index]); const auto tf_stride = attrs.get>("strides"); + if (tf_stride.size() != 4) { + return tensorflow::errors::InvalidArgument( + "Convolution strides field must specify 4 dimensions, at ", + node_def.name()); + } if (tf_stride[0] != 1 || tf_stride[c_index] != 1) { return tensorflow::errors::Unimplemented( "Stride must be 1 for batch and channel dimensions, at ", @@ -1608,10 +1618,9 @@ tensorflow::Status ConvertConv2DHelper(OpConverterParams* params, int group) { // Dimensions of transposed tensor. const auto tensor_dim = tensor->getDimensions(); - // This is a depthwise convolution when num_groups is 0. Otherwise, num_groups - // will be 1. - int num_groups = group; - if (num_groups == 0) num_groups = tensor_dim.d[0]; + // For depthwise convolution, group will be 0 so set num_groups to size of + // input's channel dim. For a non-depthwise conv, num_groups will be 1. + const int num_groups = (group == 0) ? tensor_dim.d[0] : group; if (params->converter->precision_mode() == FP16MODE) { weights_rsck = diff --git a/tensorflow/contrib/tensorrt/convert/convert_nodes_test.cc b/tensorflow/contrib/tensorrt/convert/convert_nodes_test.cc index a2ddfbffa5..cb73d2e07b 100644 --- a/tensorflow/contrib/tensorrt/convert/convert_nodes_test.cc +++ b/tensorflow/contrib/tensorrt/convert/convert_nodes_test.cc @@ -2378,6 +2378,8 @@ TEST_F(OpConverterTest, ConvertStridedSlice) { }; { + // Input is weights, should fail. + Reset(); NodeDef node_def = get_strided_slice_nodedef(); AddTestWeights("input", {1, 2, 3}, {1, 2, 3, 4, 5, 6}); AddTestWeights("begin", {4}, {0, 0, 0, 0}); @@ -2619,6 +2621,106 @@ TEST_F(OpConverterTest, ConvertStridedSlice) { } } +TEST_F(OpConverterTest, ConvertConv2D) { + { + // Input list is empty, should fail. + NodeDef node_def = MakeNodeDef("my_conv2d", "Conv2D", {}); + RunValidationAndConversion( + node_def, error::INVALID_ARGUMENT, + "Two inputs are expected for Conv2D, at my_conv2d"); + } + + // Get nodedef for Conv2D layer. + auto get_conv2d_nodedef = []( + std::vector strides = {1, 1, 1, 1}, string padding = "SAME", + string data_format = "NCHW", + std::vector dilations = {1, 1, 1, 1}) -> NodeDef { + Scope s = Scope::NewRootScope(); + auto input = ops::Placeholder(s.WithOpName("input"), DT_FLOAT); + auto filter = ops::Placeholder(s.WithOpName("weights"), DT_FLOAT); + ops::Conv2D::Attrs attrs = + ops::Conv2D::Attrs().DataFormat(data_format).Dilations(dilations); + auto conv2d = ops::Conv2D(s.WithOpName("my_conv2d"), input, filter, strides, + padding, attrs); + return conv2d.operation.node()->def(); + }; + + { + // Input is weights, should fail. + Reset(); + NodeDef node_def = get_conv2d_nodedef(); + AddTestWeights("input", {1, 2, 3}, {1, 2, 3, 4, 5, 6}); + AddTestWeights("weights", {3, 3, 1, 1}, {1, 2, 3, 4, 5, 6, 7, 8, 9}); + RunValidationAndConversion( + node_def, error::UNIMPLEMENTED, + "Conv2D is only implemented for tensors, not weights, at my_conv2d"); + } + { + // Filter is tensor, should fail. + Reset(); + NodeDef node_def = get_conv2d_nodedef(); + AddTestTensor("input", {1, 2, 3}); + AddTestTensor("weights", {3, 3, 1, 1}); + RunValidationAndConversion( + node_def, error::UNIMPLEMENTED, + "Kernel for Conv2D must be constant weights, at my_conv2d"); + } + { + // Filter is not 4D, should fail. + Reset(); + NodeDef node_def = get_conv2d_nodedef(); + AddTestTensor("input", {1, 2, 3}); + AddTestWeights("weights", {3, 3, 1}, {1, 2, 3, 4, 5, 6, 7, 8, 9}); + RunValidationAndConversion( + node_def, error::INVALID_ARGUMENT, + "Conv2D expects kernel of dimension 4, at my_conv2d"); + } + { + // Dilations is not 4D, should fail. + Reset(); + NodeDef node_def = + get_conv2d_nodedef({1, 1, 1, 1}, "SAME", "NCHW", {1, 1, 1}); + AddTestTensor("input", {1, 2, 3}); + AddTestWeights("weights", {3, 3, 1, 1}, {1, 2, 3, 4, 5, 6, 7, 8, 9}); + RunValidationAndConversion( + node_def, error::INVALID_ARGUMENT, + "Convolution dilations field must specify 4 dimensions, at my_conv2d"); + } + { + // Dilation value is not 1 for channel, should fail. + Reset(); + NodeDef node_def = + get_conv2d_nodedef({1, 1, 1, 1}, "SAME", "NCHW", {1, 2, 1, 1}); + AddTestTensor("input", {1, 2, 3}); + AddTestWeights("weights", {3, 3, 1, 1}, {1, 2, 3, 4, 5, 6, 7, 8, 9}); + RunValidationAndConversion(node_def, error::UNIMPLEMENTED, + "Dilation rate must be 1 for batch and channel " + "dimensions, at my_conv2d"); + } + { + // Strides is not 4D, should fail. + Reset(); + NodeDef node_def = + get_conv2d_nodedef({1, 1, 1}, "SAME", "NCHW", {1, 1, 1, 1}); + AddTestTensor("input", {1, 2, 3}); + AddTestWeights("weights", {3, 3, 1, 1}, {1, 2, 3, 4, 5, 6, 7, 8, 9}); + RunValidationAndConversion( + node_def, error::INVALID_ARGUMENT, + "Convolution strides field must specify 4 dimensions, at my_conv2d"); + } + { + // Stride value is not 1 for channel, should fail. + Reset(); + NodeDef node_def = + get_conv2d_nodedef({1, 2, 1, 1}, "SAME", "NCHW", {1, 1, 1, 1}); + AddTestTensor("input", {1, 2, 3}); + AddTestWeights("weights", {3, 3, 1, 1}, {1, 2, 3, 4, 5, 6, 7, 8, 9}); + RunValidationAndConversion( + node_def, error::UNIMPLEMENTED, + "Stride must be 1 for batch and channel dimensions, at my_conv2d"); + } +} + } // namespace convert } // namespace tensorrt } // namespace tensorflow -- GitLab From 560e4dd67fa8f9b0b99b940e35d039cf6c40194a Mon Sep 17 00:00:00 2001 From: Trevor Morris Date: Wed, 2 Jan 2019 16:20:44 -0800 Subject: [PATCH 314/622] Remove unused imports --- tensorflow/contrib/tensorrt/test/conv2d_test.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tensorflow/contrib/tensorrt/test/conv2d_test.py b/tensorflow/contrib/tensorrt/test/conv2d_test.py index c5228dea33..f7d9ca6826 100644 --- a/tensorflow/contrib/tensorrt/test/conv2d_test.py +++ b/tensorflow/contrib/tensorrt/test/conv2d_test.py @@ -25,10 +25,7 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.ops import array_ops -from tensorflow.python.ops import gen_array_ops from tensorflow.python.ops import gen_nn_ops -from tensorflow.python.ops import math_ops -from tensorflow.python.ops import nn_ops from tensorflow.python.platform import test -- GitLab From cf471f0c15b0c9c78c44a238fe60fe1e31289503 Mon Sep 17 00:00:00 2001 From: Trevor Morris Date: Thu, 3 Jan 2019 14:56:44 -0800 Subject: [PATCH 315/622] Add positive test cases for Conv2D. Reuse build_graph in conv2d_test. --- .../contrib/tensorrt/convert/convert_nodes.cc | 5 +- .../tensorrt/convert/convert_nodes_test.cc | 134 ++++++++++++++++++ .../contrib/tensorrt/test/conv2d_test.py | 106 ++++++-------- 3 files changed, 183 insertions(+), 62 deletions(-) diff --git a/tensorflow/contrib/tensorrt/convert/convert_nodes.cc b/tensorflow/contrib/tensorrt/convert/convert_nodes.cc index 34ba2aeaa4..179d631a3e 100644 --- a/tensorflow/contrib/tensorrt/convert/convert_nodes.cc +++ b/tensorflow/contrib/tensorrt/convert/convert_nodes.cc @@ -62,14 +62,14 @@ limitations under the License. #define TFTRT_RETURN_ERROR_IF_FALSE(status, node) \ do { \ - if ((status) == false) { \ + if (status == false) { \ TFTRT_INTERNAL_ERROR_AT_NODE(node); \ } \ } while (0) #define TFTRT_RETURN_ERROR_IF_NULLPTR(ptr, node) \ do { \ - if ((ptr) == nullptr) { \ + if (ptr == nullptr) { \ TFTRT_INTERNAL_ERROR_AT_NODE(node); \ } \ } while (0) @@ -1593,6 +1593,7 @@ tensorflow::Status ConvertConv2DHelper(OpConverterParams* params, int group) { node_def.name()); } const nvinfer1::DimsHW dilation(tf_dilations[h_index], tf_dilations[w_index]); + const auto tf_stride = attrs.get>("strides"); if (tf_stride.size() != 4) { return tensorflow::errors::InvalidArgument( diff --git a/tensorflow/contrib/tensorrt/convert/convert_nodes_test.cc b/tensorflow/contrib/tensorrt/convert/convert_nodes_test.cc index cb73d2e07b..1e49026a34 100644 --- a/tensorflow/contrib/tensorrt/convert/convert_nodes_test.cc +++ b/tensorflow/contrib/tensorrt/convert/convert_nodes_test.cc @@ -2697,6 +2697,17 @@ TEST_F(OpConverterTest, ConvertConv2D) { "Dilation rate must be 1 for batch and channel " "dimensions, at my_conv2d"); } + { + // Dilation value is not 1 for channel (NHWC), should fail. + Reset(); + NodeDef node_def = + get_conv2d_nodedef({1, 1, 1, 1}, "SAME", "NHWC", {1, 1, 1, 2}); + AddTestTensor("input", {2, 3, 1}); + AddTestWeights("weights", {3, 3, 1, 1}, {1, 2, 3, 4, 5, 6, 7, 8, 9}); + RunValidationAndConversion(node_def, error::UNIMPLEMENTED, + "Dilation rate must be 1 for batch and channel " + "dimensions, at my_conv2d"); + } { // Strides is not 4D, should fail. Reset(); @@ -2719,6 +2730,129 @@ TEST_F(OpConverterTest, ConvertConv2D) { node_def, error::UNIMPLEMENTED, "Stride must be 1 for batch and channel dimensions, at my_conv2d"); } + + struct TestParams { + TestParams(const std::vector& input_dims, + const std::vector& input, + const std::vector& filter_dims, + const std::vector& filter, + const std::vector& strides, const string& padding, + const string& data_format, const std::vector& dilations, + const std::vector& expected_output_dims, + const std::vector& expected_output) + : input_dims(input_dims), + input(input), + filter_dims(filter_dims), + filter(filter), + strides(strides), + padding(padding), + data_format(data_format), + dilations(dilations), + expected_output_dims(expected_output_dims), + expected_output(expected_output) {} + + std::vector input_dims; + std::vector input; + std::vector filter_dims; + std::vector filter; + std::vector strides; + string padding; + string data_format; + std::vector dilations; + std::vector expected_output_dims; + std::vector expected_output; + }; + + // Ok. + const int kConv2DOKCases = 6; + TestParams ok_params[kConv2DOKCases] = { + // Basic + TestParams{/*input_dims=*/{1, 2, 3}, + /*input=*/{0, 1, 2, 3, 3, 4}, + /*filter_dims=*/{1, 2, 1, 1}, + /*filter=*/{-1, 1}, + /*strides=*/{1, 1, 1, 1}, + /*padding=*/"VALID", + /*data_format=*/"NCHW", + /*dilations=*/{1, 1, 1, 1}, + /*expected_output_dims=*/{1, 2, 2}, + /*expected_output=*/{1, 1, 0, 1}}, + // SAME padding (Asymmetric) + TestParams{/*input_dims=*/{1, 2, 3}, + /*input=*/{0, 1, 2, 3, 3, 4}, + /*filter_dims=*/{1, 2, 1, 1}, + /*filter=*/{-1, 1}, + /*strides=*/{1, 1, 1, 1}, + /*padding=*/"SAME", + /*data_format=*/"NCHW", + /*dilations=*/{1, 1, 1, 1}, + /*expected_output_dims=*/{1, 2, 3}, + /*expected_output=*/{1, 1, -2, 0, 1, -4}}, + // SAME padding (Symmetric) + TestParams{/*input_dims=*/{1, 2, 3}, + /*input=*/{0, 1, 2, 3, 3, 4}, + /*filter_dims=*/{1, 3, 1, 1}, + /*filter=*/{-1, 0, 1}, + /*strides=*/{1, 1, 1, 1}, + /*padding=*/"SAME", + /*data_format=*/"NCHW", + /*dilations=*/{1, 1, 1, 1}, + /*expected_output_dims=*/{1, 2, 3}, + /*expected_output=*/{1, 2, -1, 3, 1, -3}}, + // NHWC + TestParams{/*input_dims=*/{2, 3, 1}, + /*input=*/{0, 1, 2, 3, 3, 4}, + /*filter_dims=*/{1, 2, 1, 1}, + /*filter=*/{-1, 1}, + /*strides=*/{1, 1, 1, 1}, + /*padding=*/"VALID", + /*data_format=*/"NHWC", + /*dilations=*/{1, 1, 1, 1}, + /*expected_output_dims=*/{2, 2, 1}, + /*expected_output=*/{1, 1, 0, 1}}, + // Dilated + TestParams{/*input_dims=*/{1, 2, 3}, + /*input=*/{0, 1, 2, 3, 3, 4}, + /*filter_dims=*/{1, 2, 1, 1}, + /*filter=*/{-1, 1}, + /*strides=*/{1, 1, 1, 1}, + /*padding=*/"VALID", + /*data_format=*/"NCHW", + /*dilations=*/{1, 1, 1, 2}, + /*expected_output_dims=*/{1, 2, 1}, + /*expected_output=*/{2, 1}}, + // Strided + TestParams{/*input_dims=*/{1, 2, 4}, + /*input=*/{0, 1, 2, 2, 3, 4, 4, 7}, + /*filter_dims=*/{1, 2, 1, 1}, + /*filter=*/{-1, 1}, + /*strides=*/{1, 1, 1, 2}, + /*padding=*/"VALID", + /*data_format=*/"NCHW", + /*dilations=*/{1, 1, 1, 1}, + /*expected_output_dims=*/{1, 2, 2}, + /*expected_output=*/{1, 0, 1, 3}}, + }; + + for (int i = 0; i < kConv2DOKCases; i++) { + Reset(); + NodeDef node_def = + get_conv2d_nodedef(ok_params[i].strides, ok_params[i].padding, + ok_params[i].data_format, ok_params[i].dilations); + AddTestTensor("input", ok_params[i].input_dims); + AddTestWeights("weights", ok_params[i].filter_dims, + ok_params[i].filter); + RunValidationAndConversion(node_def); + TRT_TensorOrWeights output; + TF_EXPECT_OK(GetTensorOrWeights("my_conv2d", &output)); + EXPECT_TRUE(output.is_tensor()); + ExpectTrtDimsEqualsArray(ok_params[i].expected_output_dims, + output.tensor()->getDimensions()); + std::vector output_data(ok_params[i].expected_output.size()); + BuildAndRun({{"input", ok_params[i].input}}, "my_conv2d", + &output_data); + EXPECT_THAT(output_data, ElementsAreArray(ok_params[i].expected_output)); + } } } // namespace convert diff --git a/tensorflow/contrib/tensorrt/test/conv2d_test.py b/tensorflow/contrib/tensorrt/test/conv2d_test.py index f7d9ca6826..68b6534823 100644 --- a/tensorflow/contrib/tensorrt/test/conv2d_test.py +++ b/tensorflow/contrib/tensorrt/test/conv2d_test.py @@ -51,37 +51,60 @@ def conv2d_layer(inputs, filters, kernel_size, strides=(1, 1), padding='valid', def div_round_up(n, d): return (n - 1) // d + 1 +def build_graph(input_dims, dtype, num_filters, data_format, kernel_sizes, + dilation_rates, padding='same'): + g = ops.Graph() + with g.as_default(): + inp = array_ops.placeholder( + dtype=dtype, shape=[None] + input_dims[1:], name="input") + with g.device("/GPU:0"): + results = [] + for kernel_size in kernel_sizes: + for dilation_rate in dilation_rates: + result = conv2d_layer(inp, num_filters, kernel_size, (1, 1), + padding, data_format, dilation_rate) + results.append(result) + output = sum(results) + output = array_ops.identity(output, name="output") + return g + + class Conv2DNCHWTest(trt_test.TfTrtIntegrationTestBase): def GetParams(self): """Testing conversion of Conv2D (data_format=NCHW) in TF-TRT conversion.""" np.random.seed(1234) - dtype = dtypes.float32 - input_name = "input" - n, c, h, w = 13, 3, 7, 11 - num_filters = 5 - input_dims = [n, c, h, w] - output_name = "output" - g = ops.Graph() - with g.as_default(): - inp = array_ops.placeholder( - dtype=dtype, shape=[None] + input_dims[1:], name=input_name) - with g.device("/GPU:0"): - results = [] - for kernel_size in [(3, 3), (3, 2)]: - for dilation_rate in [(1, 1), (2, 3)]: - result = conv2d_layer(inp, num_filters, kernel_size, - dilation_rate=dilation_rate, padding='same', - data_format='channels_first') - results.append(result) - output = sum(results) - output = array_ops.identity(output, name=output_name) + input_dims = [13, 3, 7, 11] + g = build_graph(input_dims=input_dims, dtype=dtypes.float32, num_filters=5, + data_format="channels_first", kernel_sizes=[(3, 3), (3, 2)], + dilation_rates=[(1, 1), (2, 3)]) return trt_test.TfTrtIntegrationTestParams( gdef=g.as_graph_def(), - input_names=[input_name], + input_names=["input"], input_dims=[input_dims], - output_names=[output_name], - expected_output_dims=[(n, num_filters, h, w)]) + output_names=["output"], + expected_output_dims=[(13, 5, 7, 11)]) + + def ExpectedEnginesToBuild(self, run_params): + """Return the expected engines to build.""" + return ["TRTEngineOp_0"] + + +class Conv2DNHWCTest(trt_test.TfTrtIntegrationTestBase): + + def GetParams(self): + """Testing conversion of Conv2D (data_format=NCHW) in TF-TRT conversion.""" + np.random.seed(1234) + input_dims = [13, 7, 11, 3] + g = build_graph(input_dims=input_dims, dtype=dtypes.float32, num_filters=5, + data_format="channels_last", kernel_sizes=[(3, 3), (3, 2)], + dilation_rates=[(1, 1), (2, 3)]) + return trt_test.TfTrtIntegrationTestParams( + gdef=g.as_graph_def(), + input_names=["input"], + input_dims=[input_dims], + output_names=["output"], + expected_output_dims=[(13, 7, 11, 5)]) def ExpectedEnginesToBuild(self, run_params): """Return the expected engines to build.""" @@ -128,42 +151,5 @@ class Conv2DStridedNCHWTest(trt_test.TfTrtIntegrationTestBase): return ["TRTEngineOp_0"] -class Conv2DNHWCTest(trt_test.TfTrtIntegrationTestBase): - - def GetParams(self): - """Testing conversion of Conv2D (data_format=NHWC) in TF-TRT conversion.""" - np.random.seed(1234) - dtype = dtypes.float32 - input_name = "input" - n, h, w, c = 13, 7, 11, 3 - num_filters = 5 - input_dims = [n, h, w, c] - output_name = "output" - g = ops.Graph() - with g.as_default(): - inp = array_ops.placeholder( - dtype=dtype, shape=[None] + input_dims[1:], name=input_name) - with g.device("/GPU:0"): - results = [] - for kernel_size in [(3, 3), (3, 2)]: - for dilation_rate in [(1, 1), (2, 3)]: - result = conv2d_layer(inp, num_filters, kernel_size, - dilation_rate=dilation_rate, padding='same', - data_format='channels_last') - results.append(result) - output = sum(results) - output = array_ops.identity(output, name=output_name) - return trt_test.TfTrtIntegrationTestParams( - gdef=g.as_graph_def(), - input_names=[input_name], - input_dims=[input_dims], - output_names=[output_name], - expected_output_dims=[(n, h, w, num_filters)]) - - def ExpectedEnginesToBuild(self, run_params): - """Return the expected engines to build.""" - return ["TRTEngineOp_0"] - - if __name__ == "__main__": test.main() -- GitLab From 2ae28cb35f8060a1e7168b216adb13ce665cb349 Mon Sep 17 00:00:00 2001 From: Pavithra Vijay Date: Thu, 3 Jan 2019 14:57:47 -0800 Subject: [PATCH 316/622] Add v2 log loss. PiperOrigin-RevId: 227755606 --- tensorflow/python/keras/losses.py | 46 +++++++++++++ tensorflow/python/keras/losses_test.py | 91 ++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) diff --git a/tensorflow/python/keras/losses.py b/tensorflow/python/keras/losses.py index d05dfb03f1..e6db795bb8 100644 --- a/tensorflow/python/keras/losses.py +++ b/tensorflow/python/keras/losses.py @@ -500,6 +500,46 @@ class CategoricalHinge(Loss): return categorical_hinge(y_true, y_pred) +class LogLoss(Loss): + """Computes the log loss between `y_true` and `y_pred`. + + logloss = -y log(p) - (1-y) log(1-p) + + Usage: + + ```python + l = tf.losses.LogLoss() + loss = l([0., 1., 1.], [1., 0., 1.]) + print('Loss: ', loss.numpy()) # Loss: 10.745 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile('sgd', loss=tf.losses.LogLoss()) + ``` + + Args: + epsilon: A small increment to add to avoid taking a log of zero. + reduction: Type of `tf.losses.Reduction` to apply to loss. Default value is + `SUM_OVER_BATCH_SIZE`. + name: Optional name for the op. + """ + + def __init__(self, + epsilon=1e-7, + reduction=losses_impl.ReductionV2.SUM_OVER_BATCH_SIZE, + name=None): + super(LogLoss, self).__init__(reduction=reduction, name=name) + self.epsilon = epsilon + + def call(self, y_true, y_pred): + y_pred = ops.convert_to_tensor(y_pred) + y_true = math_ops.cast(y_true, y_pred.dtype) + return logloss(y_true, y_pred, epsilon=self.epsilon) + + @keras_export('keras.metrics.mean_squared_error', 'keras.metrics.mse', 'keras.metrics.MSE', @@ -562,6 +602,12 @@ def categorical_hinge(y_true, y_pred): return math_ops.maximum(0., neg - pos + 1.) +def logloss(y_true, y_pred, epsilon=1e-7): + losses = math_ops.multiply(y_true, math_ops.log(y_pred + epsilon)) + losses += math_ops.multiply((1 - y_true), math_ops.log(1 - y_pred + epsilon)) + return K.mean(-losses, axis=-1) + + @keras_export('keras.losses.logcosh') def logcosh(y_true, y_pred): """Logarithm of the hyperbolic cosine of the prediction error. diff --git a/tensorflow/python/keras/losses_test.py b/tensorflow/python/keras/losses_test.py index 19ed7c8ed9..76fadc4a24 100644 --- a/tensorflow/python/keras/losses_test.py +++ b/tensorflow/python/keras/losses_test.py @@ -1003,5 +1003,96 @@ class CategoricalHingeTest(test.TestCase): self.assertAlmostEqual(self.evaluate(loss), 0., 3) +@test_util.run_all_in_graph_and_eager_modes +class LogLossTest(test.TestCase): + + def setup(self): + # TODO(psv): Change to setUp() after b/122319309 is fixed. + y_pred = np.asarray([.9, .2, .2, .8, .4, .6]).reshape((2, 3)) + y_true = np.asarray([1., 0., 1., 1., 0., 0.]).reshape((2, 3)) + epsilon = 1e-7 # to avoid log 0 + + self.batch_size = 6 + self.expected_losses = np.multiply(y_true, np.log(y_pred + epsilon)) + self.expected_losses += np.multiply(1 - y_true, + np.log(1 - y_pred + epsilon)) + self.expected_losses = -self.expected_losses + + self.y_pred = constant_op.constant(y_pred) + self.y_true = constant_op.constant(y_true) + + def test_config(self): + log_loss_obj = keras.losses.LogLoss( + reduction=losses_impl.ReductionV2.SUM, name='log') + self.assertEqual(log_loss_obj.name, 'log') + self.assertEqual(log_loss_obj.reduction, losses_impl.ReductionV2.SUM) + + def test_all_correct(self): + self.setup() + log_loss_obj = keras.losses.LogLoss() + loss = log_loss_obj(self.y_true, self.y_true) + self.assertAlmostEqual(self.evaluate(loss), 0.0, 3) + + def test_unweighted(self): + self.setup() + log_loss_obj = keras.losses.LogLoss() + loss = log_loss_obj(self.y_true, self.y_pred) + actual_loss = np.sum(self.expected_losses) / self.batch_size + self.assertAlmostEqual(self.evaluate(loss), actual_loss, 3) + + def test_scalar_weighted(self): + self.setup() + log_loss_obj = keras.losses.LogLoss() + sample_weight = 2.3 + loss = log_loss_obj(self.y_true, self.y_pred, sample_weight=sample_weight) + actual_loss = sample_weight * np.sum(self.expected_losses) / self.batch_size + self.assertAlmostEqual(self.evaluate(loss), actual_loss, 3) + + # Verify we get the same output when the same input is given + loss_2 = log_loss_obj(self.y_true, self.y_pred, sample_weight=sample_weight) + self.assertAlmostEqual(self.evaluate(loss), self.evaluate(loss_2), 3) + + def test_sample_weighted(self): + self.setup() + log_loss_obj = keras.losses.LogLoss() + sample_weight = constant_op.constant((1.2, 3.4), shape=(2, 1)) + + loss = log_loss_obj(self.y_true, self.y_pred, sample_weight=sample_weight) + actual_loss = np.multiply( + self.expected_losses, + np.asarray([1.2, 1.2, 1.2, 3.4, 3.4, 3.4]).reshape((2, 3))) + actual_loss = np.sum(actual_loss) / self.batch_size + self.assertAlmostEqual(self.evaluate(loss), actual_loss, 3) + + def test_timestep_weighted(self): + log_loss_obj = keras.losses.LogLoss() + + y_pred = np.asarray([.9, .2, .2, .8, .4, .6]).reshape((2, 3, 1)) + y_true = np.asarray([1., 0., 1., 1., 0., 0.]).reshape((2, 3, 1)) + epsilon = 1e-7 # to avoid log 0 + batch_size = 6 + + expected_losses = np.multiply(y_true, np.log(y_pred + epsilon)) + expected_losses += np.multiply(1 - y_true, np.log(1 - y_pred + epsilon)) + + y_pred = constant_op.constant(y_pred) + y_true = constant_op.constant(y_true) + sample_weight = np.array([3, 6, 5, 0, 4, 2]).reshape((2, 3, 1)) + loss = log_loss_obj( + y_true, + y_pred, + sample_weight=constant_op.constant(sample_weight, shape=(2, 3))) + actual_loss = np.multiply(-expected_losses, sample_weight) + actual_loss = np.sum(actual_loss) / batch_size + self.assertAlmostEqual(self.evaluate(loss), actual_loss, 3) + + def test_zero_weighted(self): + self.setup() + log_loss_obj = keras.losses.LogLoss() + sample_weight = 0 + loss = log_loss_obj(self.y_true, self.y_pred, sample_weight=sample_weight) + self.assertAlmostEqual(self.evaluate(loss), 0., 3) + + if __name__ == '__main__': test.main() -- GitLab From 76df46cc71916d7027334b26cc2b390e4c432251 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Thu, 3 Jan 2019 15:05:44 -0800 Subject: [PATCH 317/622] Automated rollback of commit 4d9173668fd3ba410532efd901467312b7418752 PiperOrigin-RevId: 227757156 --- tensorflow/cc/BUILD | 1 - tensorflow/cc/saved_model/BUILD | 2 - tensorflow/compiler/tf2xla/BUILD | 1 - tensorflow/core/BUILD | 3 +- tensorflow/core/kernels/BUILD | 83 +++++++++++++++++++++++++++++++- tensorflow/tensorflow.bzl | 2 - 6 files changed, 82 insertions(+), 10 deletions(-) diff --git a/tensorflow/cc/BUILD b/tensorflow/cc/BUILD index cf6d6050fa..a09becc49b 100644 --- a/tensorflow/cc/BUILD +++ b/tensorflow/cc/BUILD @@ -150,7 +150,6 @@ cc_library_with_android_deps( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", - "//tensorflow/core:ops", "//tensorflow/core:protos_all_cc", ], ) diff --git a/tensorflow/cc/saved_model/BUILD b/tensorflow/cc/saved_model/BUILD index 836678d364..52345a376c 100644 --- a/tensorflow/cc/saved_model/BUILD +++ b/tensorflow/cc/saved_model/BUILD @@ -81,7 +81,6 @@ cc_library( ] + if_not_mobile([ "//tensorflow/core:core_cpu", "//tensorflow/core:lib", - "//tensorflow/core:ops", "//tensorflow/core:protos_all_cc", "//tensorflow/core:tensorflow", ]) + if_android([ @@ -101,7 +100,6 @@ cc_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", - "//tensorflow/core:ops", "//tensorflow/core:protos_all_cc", "//tensorflow/core/util/tensor_bundle:naming", # mobile not supported yet diff --git a/tensorflow/compiler/tf2xla/BUILD b/tensorflow/compiler/tf2xla/BUILD index dfb6b57161..d8123e956f 100644 --- a/tensorflow/compiler/tf2xla/BUILD +++ b/tensorflow/compiler/tf2xla/BUILD @@ -281,7 +281,6 @@ tf_cc_test( "//tensorflow/core:protos_all_cc", "//tensorflow/core:test", "//tensorflow/core:test_main", - "//tensorflow/core:testlib", ], ) diff --git a/tensorflow/core/BUILD b/tensorflow/core/BUILD index 6b32e1fead..7c9decbd09 100644 --- a/tensorflow/core/BUILD +++ b/tensorflow/core/BUILD @@ -1526,7 +1526,6 @@ cc_library( ":framework_internal", ":lib", ":lib_internal", - ":ops", ":protos_all_cc", ":shape_inference_testutil", ":tensor_testutil", @@ -3078,7 +3077,6 @@ tf_cuda_library( ":lib", ":lib_internal", ":metrics", - ":ops", ":proto_text", ":protos_all_cc", "//tensorflow/core/debug:debug_graph_utils", @@ -3861,6 +3859,7 @@ tf_cc_test( "ops/cudnn_rnn_ops_test.cc", ], deps = [ + ":cudnn_rnn_ops", "//tensorflow/core", "//tensorflow/core:framework", "//tensorflow/core:lib", diff --git a/tensorflow/core/kernels/BUILD b/tensorflow/core/kernels/BUILD index fd1df63c95..c72d04d3af 100644 --- a/tensorflow/core/kernels/BUILD +++ b/tensorflow/core/kernels/BUILD @@ -157,6 +157,7 @@ tf_kernel_library( name = "collective_ops", prefix = "collective_ops", deps = [ + "//tensorflow/core:collective_ops_op_lib", "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:protos_all_cc", @@ -221,6 +222,7 @@ tf_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", + "//tensorflow/core:nn_ops_op_lib", "//third_party/eigen3", ], alwayslink = 1, @@ -311,6 +313,7 @@ tf_kernel_library( "//tensorflow/core/nccl:nccl_lib", "//tensorflow/core:framework", "//tensorflow/core:gpu_headers_lib", + "//tensorflow/core:nccl_ops_op_lib", ]), ) @@ -456,6 +459,7 @@ cc_library( name = "batch_kernels", srcs = ["batch_kernels.cc"], deps = [ + "//tensorflow/core:batch_ops_op_lib", "//tensorflow/core:framework_headers_lib", "//tensorflow/core:protos_all_cc", "//tensorflow/core/kernels:concat_lib_hdrs", @@ -682,6 +686,7 @@ ARRAY_DEPS = [ ":ops_util", ":transpose_functor", "//tensorflow/core:array_grad", + "//tensorflow/core:array_ops_op_lib", "//tensorflow/core:core_cpu", "//tensorflow/core:framework", "//tensorflow/core:lib", @@ -710,6 +715,7 @@ tf_kernel_library( deps = [ "//tensorflow/core:framework_headers_lib", "//tensorflow/core:lib", + "//tensorflow/core:set_ops_op_lib", "//third_party/eigen3", ], ) @@ -1073,6 +1079,7 @@ tf_kernel_library( srcs = ["ragged_gather_op.cc"], deps = [ "//tensorflow/core:framework", + "//tensorflow/core:ragged_array_ops_op_lib", ], ) @@ -1083,6 +1090,7 @@ tf_cc_test( deps = [ ":ragged_gather_op", "//tensorflow/core:framework", + "//tensorflow/core:ragged_array_ops_op_lib", "//tensorflow/core:test", "//tensorflow/core:test_main", "//tensorflow/core:testlib", @@ -1095,6 +1103,7 @@ tf_kernel_library( srcs = ["ragged_range_op.cc"], deps = [ "//tensorflow/core:framework", + "//tensorflow/core:ragged_math_ops_op_lib", ], ) @@ -1104,6 +1113,7 @@ tf_cc_test( deps = [ ":ragged_range_op", "//tensorflow/core:framework", + "//tensorflow/core:ragged_math_ops_op_lib", "//tensorflow/core:test", "//tensorflow/core:test_main", "//tensorflow/core:testlib", @@ -1116,6 +1126,7 @@ tf_kernel_library( srcs = ["ragged_tensor_to_sparse_kernel.cc"], deps = [ "//tensorflow/core:framework", + "//tensorflow/core:ragged_conversion_ops_op_lib", ], ) @@ -1127,6 +1138,7 @@ tf_cc_test( ":ragged_tensor_to_sparse_kernel", "//tensorflow/core:framework", "//tensorflow/core:lib", + "//tensorflow/core:ragged_conversion_ops_op_lib", "//tensorflow/core:test", "//tensorflow/core:test_main", "//tensorflow/core:testlib", @@ -1140,6 +1152,7 @@ tf_kernel_library( visibility = ["//visibility:public"], deps = [ ":gpu_util_hdrs", + "//tensorflow/core:cudnn_rnn_ops_op_lib", "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", @@ -1216,6 +1229,7 @@ tf_cuda_cc_test( "//tensorflow/core:core_cpu", "//tensorflow/core:framework", "//tensorflow/core:lib", + "//tensorflow/core:math_ops_op_lib", "//tensorflow/core:protos_all_cc", "//tensorflow/core:test", "//tensorflow/core:test_main", @@ -1740,6 +1754,7 @@ tf_kernel_library( prefix = "candidate_sampler_ops", deps = [ ":range_sampler", + "//tensorflow/core:candidate_sampling_ops_op_lib", "//tensorflow/core:framework", "//tensorflow/core:lib", ], @@ -1773,6 +1788,7 @@ tf_kernel_library( name = "control_flow_ops", prefix = "control_flow_ops", deps = [ + "//tensorflow/core:control_flow_ops_op_lib", "//tensorflow/core:framework", "//tensorflow/core:lib", ], @@ -1784,6 +1800,7 @@ tf_kernel_library( deps = [ ":bounds_check", ":ops_util", + "//tensorflow/core:ctc_ops_op_lib", "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core/util/ctc:ctc_beam_search_lib", @@ -1864,6 +1881,7 @@ DATA_FLOW_DEPS = [ ":typed_queue", "//third_party/eigen3", "//tensorflow/core:core_cpu", + "//tensorflow/core:data_flow_ops_op_lib", "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", @@ -1928,6 +1946,7 @@ tf_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", + "//tensorflow/core:scoped_allocator_ops_op_lib", ], ) @@ -1946,6 +1965,7 @@ tf_cuda_cc_test( "//tensorflow/core:core_cpu_internal", "//tensorflow/core:framework", "//tensorflow/core:lib", + "//tensorflow/core:math_ops_op_lib", "//tensorflow/core:proto_text", "//tensorflow/core:protos_all_cc", "//tensorflow/core:test", @@ -1993,6 +2013,7 @@ tf_kernel_library( DYNAMIC_DEPS = [ ":bounds_check", "//tensorflow/core:core_cpu", + "//tensorflow/core:data_flow_ops_op_lib", "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", @@ -2025,6 +2046,7 @@ LOOKUP_DEPS = [ "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", + "//tensorflow/core:lookup_ops_op_lib", ] tf_kernel_library( @@ -2053,6 +2075,7 @@ tf_kernel_library( deps = [ ":lookup_table_init_op", ":lookup_table_op", + "//tensorflow/core:checkpoint_ops_op_lib", "//tensorflow/core:framework", "//tensorflow/core:lib", "//third_party/eigen3", @@ -2063,6 +2086,7 @@ tf_kernel_library( name = "load_and_remap_matrix_op", srcs = ["load_and_remap_matrix_op.cc"], deps = [ + "//tensorflow/core:checkpoint_ops_op_lib", "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", @@ -2209,6 +2233,7 @@ tf_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", + "//tensorflow/core:resource_variable_ops_op_lib", "@com_google_absl//absl/strings", ], ) @@ -2225,6 +2250,7 @@ tf_kernel_library( ":concat_lib", "//tensorflow/core:framework", "//tensorflow/core:lib", + "//tensorflow/core:list_ops_op_lib", "//third_party/eigen3", ], ) @@ -2235,6 +2261,7 @@ tf_kernel_library( deps = [ "//tensorflow/core:framework", "//tensorflow/core:lib", + "//tensorflow/core:user_ops_op_lib", ], ) @@ -2257,6 +2284,7 @@ tf_kernel_library( "//tensorflow/core:core_cpu", "//tensorflow/core:core_cpu_internal", "//tensorflow/core:framework", + "//tensorflow/core:functional_ops_op_lib", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", "//third_party/eigen3", @@ -2269,6 +2297,7 @@ tf_kernel_library( deps = [ "//tensorflow/core:core_cpu_internal", "//tensorflow/core:framework", + "//tensorflow/core:functional_ops_op_lib", "//tensorflow/core:lib", "//tensorflow/core:protos_all_cc", "//tensorflow/core/grappler:grappler_item", @@ -2311,6 +2340,7 @@ IMAGE_DEPS = [ "//third_party/eigen3", "//tensorflow/core:framework", "//tensorflow/core:gif_internal", + "//tensorflow/core:image_ops_op_lib", "//tensorflow/core:jpeg_internal", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", @@ -2650,6 +2680,7 @@ cc_library( IO_DEPS = [ ":ops_util", "//tensorflow/core:framework", + "//tensorflow/core:io_ops_op_lib", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", "//tensorflow/core:protos_all_cc", @@ -2693,6 +2724,7 @@ SAVE_RESTORE_DEPS = [ ":bounds_check_lib", ":save_restore_tensor", "//tensorflow/core:framework", + "//tensorflow/core:io_ops_op_lib", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", "//tensorflow/core:protos_all_cc", @@ -2811,6 +2843,7 @@ LINALG_DEPS = [ "//third_party/eigen3", "//tensorflow/core:framework", "//tensorflow/core:lib", + "//tensorflow/core:linalg_ops_op_lib", ] + if_cuda([ ":cuda_solvers", ":transpose_functor", @@ -2954,6 +2987,7 @@ LOGGING_DEPS = [ "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", + "//tensorflow/core:logging_ops_op_lib", "//tensorflow/core:protos_all_cc", ] @@ -3025,6 +3059,7 @@ tf_kernel_library( ":bounds_check", "//tensorflow/core:framework", "//tensorflow/core:lib", + "//tensorflow/core:manip_ops_op_lib", "//third_party/eigen3", ], ) @@ -3056,6 +3091,7 @@ MATH_DEPS = [ "//tensorflow/core:lib", "//tensorflow/core:lib_internal", "//tensorflow/core:math_grad", + "//tensorflow/core:math_ops_op_lib", "//third_party/eigen3", ] @@ -3166,7 +3202,7 @@ tf_kernel_library( tf_kernel_library( name = "cwise_op", prefix = "cwise_op", - deps = MATH_DEPS, + deps = MATH_DEPS + ["//tensorflow/core:bitwise_ops_op_lib"], ) tf_kernel_library( @@ -3185,6 +3221,7 @@ tf_kernel_library( name = "fft_ops", prefix = "fft_ops", deps = MATH_DEPS + [ + "//tensorflow/core:spectral_ops_op_lib", ] + if_cuda([ "//tensorflow/core/platform/default/build_config:cufft_plugin", ]), @@ -3383,7 +3420,10 @@ tf_cuda_cc_test( ":quantized_ops", "//tensorflow/cc:cc_ops", "//tensorflow/cc:client_session", + "//tensorflow/core:array_ops_op_lib", "//tensorflow/core:framework", + "//tensorflow/core:math_ops_op_lib", + "//tensorflow/core:nn_ops_op_lib", "//tensorflow/core:protos_all_cc", "//tensorflow/core:test", "//tensorflow/core:test_main", @@ -3641,6 +3681,7 @@ tf_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", + "//tensorflow/core:nn_ops_op_lib", ] + select({ ":xsmm_convolutions": [ "@libxsmm_archive//:xsmm_avx", @@ -3662,6 +3703,7 @@ tf_kernel_library( "//tensorflow/core:core_cpu", "//tensorflow/core:framework", "//tensorflow/core:lib", + "//tensorflow/core:nn_ops_op_lib", ] + if_cuda([ "@cub_archive//:cub", "@local_config_cuda//cuda:cudnn_header", @@ -3681,6 +3723,7 @@ tf_kernel_library( "//tensorflow/core:core_cpu", "//tensorflow/core:framework", "//tensorflow/core:lib", + "//tensorflow/core:nn_ops_op_lib", ] + if_cuda([ "@local_config_cuda//cuda:cudnn_header", ]), @@ -3728,8 +3771,9 @@ NN_DEPS = [ "//tensorflow/core:lib", "//tensorflow/core:lib_internal", "//tensorflow/core:nn_grad", + "//tensorflow/core:nn_ops_op_lib", "//third_party/eigen3", -] +] + if_mkl(["//tensorflow/core:mkl_nn_ops_op_lib"]) tf_kernel_library( name = "batch_norm_op", @@ -3849,6 +3893,7 @@ tf_kernel_library( "//tensorflow/core:lib", "//tensorflow/core:lib_internal", "//tensorflow/core:nn_grad", + "//tensorflow/core:nn_ops_op_lib", ] + if_cuda(["@cub_archive//:cub"]), ) @@ -3956,6 +4001,7 @@ tf_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", + "//tensorflow/core:nn_ops_op_lib", "//tensorflow/core:stream_executor", "//third_party/eigen3", ], @@ -3999,6 +4045,7 @@ tf_kernel_library( "//tensorflow/core:core_cpu", "//tensorflow/core:framework", "//tensorflow/core:lib", + "//tensorflow/core:nn_ops_op_lib", "//third_party/eigen3", ], ) @@ -4080,6 +4127,7 @@ cc_library( PARSING_DEPS = [ "//tensorflow/core:framework", "//tensorflow/core:lib", + "//tensorflow/core:parsing_ops_op_lib", "//tensorflow/core:proto_text", "//tensorflow/core:protos_all_cc", ] @@ -4148,6 +4196,7 @@ RANDOM_OPS_DEPS = [ "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", + "//tensorflow/core:random_ops_op_lib", ] tf_kernel_library( @@ -4186,6 +4235,7 @@ tf_kernel_library( ":random_op", "//tensorflow/core:framework", "//tensorflow/core:lib", + "//tensorflow/core:stateless_random_ops_op_lib", ], ) @@ -4200,6 +4250,8 @@ cc_library( REQUIRED_DEPS = [ "//tensorflow/core:framework", "//tensorflow/core:lib", + "//tensorflow/core:no_op_op_lib", + "//tensorflow/core:sendrecv_ops_op_lib", ] tf_kernel_library( @@ -4260,6 +4312,7 @@ cc_library( SPARSE_DEPS = [ "//tensorflow/core:framework", "//tensorflow/core:lib", + "//tensorflow/core:sparse_ops_op_lib", ] tf_kernel_library( @@ -4504,6 +4557,7 @@ tf_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", + "//tensorflow/core:sdca_ops_op_lib", "//third_party/eigen3", "@farmhash_archive//:farmhash", ], @@ -4543,6 +4597,7 @@ STATE_DEPS = [ "//third_party/eigen3", "//tensorflow/core:framework", "//tensorflow/core:lib", + "//tensorflow/core:state_ops_op_lib", ] + if_sycl(["//tensorflow/core:sycl_runtime"]) tf_kernel_library( @@ -4680,6 +4735,7 @@ STRING_DEPS = [ "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", + "//tensorflow/core:string_ops_op_lib", ] tf_kernel_library( @@ -4830,6 +4886,7 @@ tf_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", + "//tensorflow/core:string_ops_op_lib", "//third_party/eigen3", "//third_party/icu/data:conversion_data", "@icu//:common", @@ -4851,6 +4908,7 @@ tf_kernel_library( ":variable_ops", "//tensorflow/core:framework", "//tensorflow/core:lib", + "//tensorflow/core:training_ops_op_lib", "//third_party/eigen3", ], ) @@ -4909,6 +4967,7 @@ tf_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", + "//tensorflow/core:random_ops_op_lib", ], ) @@ -4936,6 +4995,7 @@ tf_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", + "//tensorflow/core:random_ops_op_lib", ], ) @@ -5844,9 +5904,12 @@ tf_kernel_library( ":ops_util", ":pooling_ops", ":quantization_utils", + "//tensorflow/core:array_ops_op_lib", "//tensorflow/core:core_cpu", "//tensorflow/core:framework", "//tensorflow/core:lib", + "//tensorflow/core:math_ops_op_lib", + "//tensorflow/core:nn_ops_op_lib", "//third_party/eigen3", "@gemmlowp", ], @@ -6416,6 +6479,7 @@ tf_kernel_library( "//tensorflow/core:lib", "//tensorflow/core:lib_internal", "//tensorflow/core:protos_all_cc", + "//tensorflow/core:remote_fused_graph_ops_op_lib", ], ) @@ -6570,6 +6634,8 @@ tf_mkl_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", + "//tensorflow/core:mkl_nn_ops_op_lib", + "//tensorflow/core:nn_ops_op_lib", ] + mkl_deps(), ) @@ -6619,6 +6685,8 @@ tf_mkl_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", + "//tensorflow/core:mkl_nn_ops_op_lib", + "//tensorflow/core:nn_ops_op_lib", ] + mkl_deps(), ) @@ -6637,6 +6705,8 @@ tf_mkl_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", + "//tensorflow/core:mkl_nn_ops_op_lib", + "//tensorflow/core:nn_ops_op_lib", ] + mkl_deps(), ) @@ -6650,6 +6720,8 @@ tf_mkl_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", + "//tensorflow/core:mkl_nn_ops_op_lib", + "//tensorflow/core:nn_ops_op_lib", "//third_party/eigen3", ] + mkl_deps(), ) @@ -6664,6 +6736,8 @@ tf_mkl_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", + "//tensorflow/core:mkl_nn_ops_op_lib", + "//tensorflow/core:nn_ops_op_lib", "//third_party/eigen3", ] + mkl_deps(), ) @@ -6804,6 +6878,7 @@ tf_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:protos_all_cc", + "//tensorflow/core:summary_ops_op_lib", "//tensorflow/core/lib/db:sqlite", ] + if_not_v2([ "//tensorflow/contrib/tensorboard/db:schema", @@ -6818,6 +6893,7 @@ tf_kernel_library( "decode_proto_op.cc", ], deps = [ + "//tensorflow/core:decode_proto_ops_op_lib", "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core/util/proto:decode", @@ -6831,6 +6907,7 @@ tf_kernel_library( name = "encode_proto_op", srcs = ["encode_proto_op.cc"], deps = [ + "//tensorflow/core:encode_proto_ops_op_lib", "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core/util/proto:descriptors", @@ -6848,6 +6925,7 @@ tf_kernel_library( "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", + "//tensorflow/core:rpc_ops_op_lib", "//tensorflow/core/util/rpc:call_container", "//tensorflow/core/util/rpc:rpc_factory", "//tensorflow/core/util/rpc:rpc_factory_registry", @@ -6860,6 +6938,7 @@ tf_kernel_library( srcs = ["unicode_script_op.cc"], deps = [ "//tensorflow/core:framework", + "//tensorflow/core:string_ops_op_lib", "@icu//:common", ], ) diff --git a/tensorflow/tensorflow.bzl b/tensorflow/tensorflow.bzl index b9069768e8..1024b686eb 100644 --- a/tensorflow/tensorflow.bzl +++ b/tensorflow/tensorflow.bzl @@ -590,7 +590,6 @@ def tf_gen_op_wrappers_cc( clean_dep("//tensorflow/core:core_cpu"), clean_dep("//tensorflow/core:framework"), clean_dep("//tensorflow/core:lib"), - clean_dep("//tensorflow/core:ops"), clean_dep("//tensorflow/core:protos_all_cc"), ]) + if_android([ clean_dep("//tensorflow/core:android_tensorflow_lib"), @@ -607,7 +606,6 @@ def tf_gen_op_wrappers_cc( clean_dep("//tensorflow/core:core_cpu"), clean_dep("//tensorflow/core:framework"), clean_dep("//tensorflow/core:lib"), - clean_dep("//tensorflow/core:ops"), clean_dep("//tensorflow/core:protos_all_cc"), ]) + if_android([ clean_dep("//tensorflow/core:android_tensorflow_lib"), -- GitLab From 8f1e280350d1a0a6b4134327ecd029195438e767 Mon Sep 17 00:00:00 2001 From: Tim Shen Date: Thu, 3 Jan 2019 15:08:10 -0800 Subject: [PATCH 318/622] Add a clang-specific warning -Wmismatched-tags. PiperOrigin-RevId: 227757550 --- tensorflow/stream_executor/cuda/cuda_dnn.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tensorflow/stream_executor/cuda/cuda_dnn.cc b/tensorflow/stream_executor/cuda/cuda_dnn.cc index 7c05701895..01ff248150 100644 --- a/tensorflow/stream_executor/cuda/cuda_dnn.cc +++ b/tensorflow/stream_executor/cuda/cuda_dnn.cc @@ -51,6 +51,12 @@ limitations under the License. #include "absl/strings/string_view.h" // clang-format on +#pragma clang diagnostic push + +// Make sure that Eigen::half forward declaration in dnn.h matches the +// declaration in Eigen. +#pragma clang diagnostic warning "-Wmismatched-tags" + namespace stream_executor { namespace cuda { @@ -4531,5 +4537,7 @@ void initialize_cudnn() { } // namespace stream_executor +#pragma clang diagnostic pop + REGISTER_MODULE_INITIALIZER(register_cudnn, { stream_executor::initialize_cudnn(); }); -- GitLab From 2c65e8b01301d3d001952b3fc92d4a06c1f08bf5 Mon Sep 17 00:00:00 2001 From: Sourabh Bajaj Date: Thu, 3 Jan 2019 15:23:43 -0800 Subject: [PATCH 319/622] Throw an error when running loop inside scope PiperOrigin-RevId: 227760023 --- .../contrib/distribute/python/keras_test.py | 19 +++++++++++++++++-- .../engine/distributed_training_utils.py | 11 +++++++++++ .../keras/engine/training_distributed.py | 6 ++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/tensorflow/contrib/distribute/python/keras_test.py b/tensorflow/contrib/distribute/python/keras_test.py index ba5a5e6400..4cfcc0375f 100644 --- a/tensorflow/contrib/distribute/python/keras_test.py +++ b/tensorflow/contrib/distribute/python/keras_test.py @@ -1161,8 +1161,8 @@ class TestDistributionStrategyWithNormalizationLayer( np.testing.assert_allclose(out.std(), 1.0, atol=1e-1) -class TestDistributionStrategyVariableValidation(test.TestCase, - parameterized.TestCase): +class TestDistributionStrategyValidation(test.TestCase, + parameterized.TestCase): @combinations.generate(all_strategy_combinations_minus_default()) def test_layer_outside_scope(self, distribution): @@ -1192,6 +1192,21 @@ class TestDistributionStrategyVariableValidation(test.TestCase, metrics = ['mae', keras.metrics.CategoricalAccuracy()] model.compile(optimizer, loss, metrics=metrics) + @combinations.generate(all_strategy_combinations_minus_default()) + def test_loop_in_scope(self, distribution): + with self.cached_session(): + with self.assertRaisesRegexp( + RuntimeError, 'should not be run inside the distribution strategy'): + with distribution.scope(): + x = keras.layers.Input(shape=(3,), name='input') + y = keras.layers.Dense(4, name='dense')(x) + model = keras.Model(x, y) + optimizer = gradient_descent.GradientDescentOptimizer(0.001) + loss = 'mse' + model.compile(optimizer, loss) + input_array = np.zeros((3, 3), dtype=np.float32) + model.predict(input_array) + if __name__ == '__main__': test.main() diff --git a/tensorflow/python/keras/engine/distributed_training_utils.py b/tensorflow/python/keras/engine/distributed_training_utils.py index 231c59ba93..9e3bdd26cb 100644 --- a/tensorflow/python/keras/engine/distributed_training_utils.py +++ b/tensorflow/python/keras/engine/distributed_training_utils.py @@ -38,10 +38,21 @@ from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import variables from tensorflow.python.platform import tf_logging as logging +from tensorflow.python.training import distribution_strategy_context from tensorflow.python.training.mode_keys import ModeKeys from tensorflow.python.util import nest +def validate_not_in_strategy_scope(): + """Validate fit/eval/predict are not running in DS scope.""" + if distribution_strategy_context.has_distribution_strategy(): + if distribution_strategy_context.in_cross_replica_context(): + raise RuntimeError( + 'Fit/Eval/Predict should not be run inside the distribution strategy ' + 'scope. Only model creation and compilation should be in ' + 'distribution strategy scope.') + + def set_weights(distribution_strategy, dist_model, weights): """Sets the weights of the replicated models. diff --git a/tensorflow/python/keras/engine/training_distributed.py b/tensorflow/python/keras/engine/training_distributed.py index 0bd79f2b47..9f63f7bada 100644 --- a/tensorflow/python/keras/engine/training_distributed.py +++ b/tensorflow/python/keras/engine/training_distributed.py @@ -53,6 +53,8 @@ def fit_distributed(model, steps_per_epoch=None, validation_steps=None): """Fit loop for Distribution Strategies.""" + # TODO(b/122314600): Remove the scope validate. + distributed_training_utils.validate_not_in_strategy_scope() distributed_training_utils.validate_callbacks(callbacks, model.optimizer) distributed_training_utils.validate_inputs( x, y, model._distribution_strategy) @@ -136,6 +138,8 @@ def evaluate_distributed(model, steps=None, callbacks=None): """Evaluate loop for Distribution Strategies.""" + # TODO(b/122314600): Remove the scope validate. + distributed_training_utils.validate_not_in_strategy_scope() distributed_training_utils.validate_inputs(x, y, model._distribution_strategy) first_x_value = nest.flatten(x)[0] if isinstance(first_x_value, np.ndarray): @@ -171,6 +175,8 @@ def predict_distributed(model, steps=None, callbacks=None): """Predict loop for Distribution Strategies.""" + # TODO(b/122314600): Remove the scope validate. + distributed_training_utils.validate_not_in_strategy_scope() distributed_training_utils.validate_inputs( x, None, model._distribution_strategy) first_x_value = nest.flatten(x)[0] -- GitLab From 6932e795621a76a7e520bee529d915265b40e9bd Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Thu, 3 Jan 2019 15:26:41 -0800 Subject: [PATCH 320/622] In tf.name_scope, throw an exception when the `default_name` arg is passed in but not a string and warn the user that they likely meant to pass it in as the `values` kwarg. PiperOrigin-RevId: 227760445 --- tensorflow/python/framework/ops.py | 8 ++++++++ tensorflow/python/framework/ops_test.py | 3 +++ 2 files changed, 11 insertions(+) diff --git a/tensorflow/python/framework/ops.py b/tensorflow/python/framework/ops.py index 5dc8e418a2..079fad4210 100644 --- a/tensorflow/python/framework/ops.py +++ b/tensorflow/python/framework/ops.py @@ -6029,7 +6029,15 @@ class name_scope(object): # pylint: disable=invalid-name name: The name argument that is passed to the op function. default_name: The default name to use if the `name` argument is `None`. values: The list of `Tensor` arguments that are passed to the op function. + + Raises: + TypeError: if `default_name` is passed in but not a string. """ + if not (default_name is None or isinstance(default_name, six.string_types)): + raise TypeError( + "`default_name` type (%s) is not a string type. You likely meant to " + "pass this into the `values` kwarg." + % type(default_name)) self._name = default_name if name is None else name self._default_name = default_name self._values = values diff --git a/tensorflow/python/framework/ops_test.py b/tensorflow/python/framework/ops_test.py index 2d7ee1a99e..58d311fe4e 100644 --- a/tensorflow/python/framework/ops_test.py +++ b/tensorflow/python/framework/ops_test.py @@ -2052,6 +2052,9 @@ class OpScopeTest(test_util.TensorFlowTestCase): with ops.name_scope(None, default_scope_name, [a, b]) as scope: self.assertEqual("%s/" % default_scope_name, scope) self.assertEqual(g0, ops.get_default_graph()) + with self.assertRaises(TypeError): + with ops.name_scope(scope_name, [a, b]): + pass def _testGraphElements(self, graph_elements): scope_name = "my_scope" -- GitLab From 88c658a1bc737385e2edcbb7068406321151d875 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Thu, 3 Jan 2019 16:18:43 -0800 Subject: [PATCH 321/622] Update ops-related pbtxt files. PiperOrigin-RevId: 227768824 --- .../core/ops/compat/ops_history.v1.pbtxt | 28 +++++++++++++++++++ tensorflow/core/ops/ops.pbtxt | 9 ++++++ 2 files changed, 37 insertions(+) diff --git a/tensorflow/core/ops/compat/ops_history.v1.pbtxt b/tensorflow/core/ops/compat/ops_history.v1.pbtxt index d27f7a5eac..28d085b2d2 100644 --- a/tensorflow/core/ops/compat/ops_history.v1.pbtxt +++ b/tensorflow/core/ops/compat/ops_history.v1.pbtxt @@ -76625,6 +76625,34 @@ op { type: "type" } } +op { + name: "TensorListConcat" + input_arg { + name: "input_handle" + type: DT_VARIANT + } + output_arg { + name: "tensor" + type_attr: "element_dtype" + } + output_arg { + name: "lengths" + type: DT_INT64 + } + attr { + name: "element_dtype" + type: "type" + } + attr { + name: "element_shape" + type: "shape" + default_value { + shape { + unknown_rank: true + } + } + } +} op { name: "TensorListConcatLists" input_arg { diff --git a/tensorflow/core/ops/ops.pbtxt b/tensorflow/core/ops/ops.pbtxt index 412b4160d9..b4767d35d2 100644 --- a/tensorflow/core/ops/ops.pbtxt +++ b/tensorflow/core/ops/ops.pbtxt @@ -36756,6 +36756,15 @@ op { name: "element_dtype" type: "type" } + attr { + name: "element_shape" + type: "shape" + default_value { + shape { + unknown_rank: true + } + } + } } op { name: "TensorListConcatLists" -- GitLab From be57fb3a05ccfe01089fc2926e885c35ad5bda00 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Thu, 3 Jan 2019 16:34:45 -0800 Subject: [PATCH 322/622] Small refactoring in preparation for a larger change. PiperOrigin-RevId: 227771218 --- tensorflow/lite/delegates/flex/kernel.cc | 116 +++++++++++------- tensorflow/lite/delegates/flex/kernel_test.cc | 5 +- 2 files changed, 70 insertions(+), 51 deletions(-) diff --git a/tensorflow/lite/delegates/flex/kernel.cc b/tensorflow/lite/delegates/flex/kernel.cc index d6e12ef650..250fc31043 100644 --- a/tensorflow/lite/delegates/flex/kernel.cc +++ b/tensorflow/lite/delegates/flex/kernel.cc @@ -52,11 +52,11 @@ namespace flex { namespace kernel { // Controls the lifetime of tensor handles in a vector. -class VectorOfHandles { +class OpOutputs { public: - explicit VectorOfHandles(int num_elements) : vector_(num_elements, nullptr) {} + explicit OpOutputs(int num_elements) : vector_(num_elements, nullptr) {} - ~VectorOfHandles() { + ~OpOutputs() { for (auto* handle : vector_) { if (handle) handle->Unref(); } @@ -72,6 +72,21 @@ class VectorOfHandles { tensorflow::gtl::InlinedVector vector_; }; +// A single node within the larger 'op'. Note that this kernel executes many +// TensorFlow ops within a single TF Lite op. +struct OpNode { + // The name of the TensorFlow op to execute. + string name; + // Index of this node into TF Lite's operator list. + int index; + // The corresponding NodeDef, containing the attributes for the op. + tensorflow::NodeDef nodedef; + // List of inputs, as TF Lite tensor indices. + std::vector inputs; + // List of outputs, as TF Lite tensor indices. + std::vector outputs; +}; + // Executes the TensorFlow op given by 'op_name', with the attributes specified // in 'nodedef'. Inputs and outputs are given as indices into the 'buffer_map'. tensorflow::Status ExecuteFlexOp(tensorflow::EagerContext* eager_context, @@ -114,7 +129,7 @@ tensorflow::Status ExecuteFlexOp(tensorflow::EagerContext* eager_context, } int num_retvals = outputs.size(); - VectorOfHandles retvals(num_retvals); + OpOutputs retvals(num_retvals); TF_RETURN_WITH_CONTEXT_IF_ERROR( EagerExecute(&op, retvals.GetVector(), &num_retvals), " (while executing '", op_name, "' via Eager)"); @@ -133,22 +148,38 @@ tensorflow::Status ExecuteFlexOp(tensorflow::EagerContext* eager_context, return tensorflow::Status::OK(); } -// A single node within the larger 'op'. Note that this kernel executes many -// TensorFlow ops within a single TF Lite op. -struct OpNode { - // The name of the TensorFlow op to execute. - string name; - // Index of this node into TF Lite's operator list. - int index; - // The corresponding NodeDef, containing the attributes for the op. - tensorflow::NodeDef nodedef; - // List of inputs, as TF Lite tensor indices. - std::vector inputs; - // List of outputs, as TF Lite tensor indices. - std::vector outputs; -}; +tensorflow::Status InitializeNodeDef(const void* custom_initial_data, + int custom_initial_data_size, + OpNode* node_data) { + if (!custom_initial_data) { + return tensorflow::errors::Internal( + "Cannot convert empty data into a valid NodeDef"); + } + // The flexbuffer contains a vector where the first elements is the + // op name and the second is a serialized NodeDef. + const flexbuffers::Vector& v = + flexbuffers::GetRoot( + reinterpret_cast(custom_initial_data), + custom_initial_data_size) + .AsVector(); + + node_data->name = v[0].AsString().str(); + if (!node_data->nodedef.ParseFromString(v[1].AsString().str())) { + node_data->nodedef.Clear(); + return tensorflow::errors::Internal( + "Failed to parse data into a valid NodeDef"); + } + + // Fill NodeDef with defaults if it's a valid op. + const tensorflow::OpRegistrationData* op_reg_data; + TF_RETURN_IF_ERROR(tensorflow::OpRegistry::Global()->LookUp( + node_data->nodedef.op(), &op_reg_data)); + AddDefaultsToNodeDef(op_reg_data->op_def, &node_data->nodedef); -// The Larger 'op', which contains all the nodes in a supported subgraph. + return tensorflow::Status::OK(); +} + +// The larger 'op', which contains all the nodes in a supported subgraph. struct OpData { tensorflow::EagerContext* eager_context; BufferMap* buffer_map; @@ -182,6 +213,7 @@ void* Init(TfLiteContext* context, const char* buffer, size_t length) { } CHECK(params->nodes_to_replace); + tensorflow::Status status; for (auto node_index : TfLiteIntArrayView(params->nodes_to_replace)) { TfLiteNode* node; TfLiteRegistration* reg; @@ -192,29 +224,10 @@ void* Init(TfLiteContext* context, const char* buffer, size_t length) { node_data.index = node_index; node_data.name = ""; - if (node->custom_initial_data) { - // The flexbuffer contains a vector where the first elements is the - // op name and the second is a serialized NodeDef. - const flexbuffers::Vector& v = - flexbuffers::GetRoot( - reinterpret_cast(node->custom_initial_data), - node->custom_initial_data_size) - .AsVector(); - - node_data.name = v[0].AsString().str(); - if (!node_data.nodedef.ParseFromString(v[1].AsString().str())) { - // We will just leave the nodedef empty and error out in Eval(). - node_data.nodedef.Clear(); - } - } - // Fill NodeDef with defaults if it's a valid op. - const tensorflow::OpRegistrationData* op_reg_data; - auto tf_status = tensorflow::OpRegistry::Global()->LookUp( - node_data.nodedef.op(), &op_reg_data); - if (tf_status.ok()) { - AddDefaultsToNodeDef(op_reg_data->op_def, &node_data.nodedef); - } + status = InitializeNodeDef(node->custom_initial_data, + node->custom_initial_data_size, &node_data); + if (!status.ok()) break; for (auto input_index : TfLiteIntArrayView(node->inputs)) { node_data.inputs.push_back(input_index); @@ -224,6 +237,13 @@ void* Init(TfLiteContext* context, const char* buffer, size_t length) { } } + if (ConvertStatus(context, status) != kTfLiteOk) { + // We can't return an error from this function but ConvertStatus will + // report them and we will stop processing in Prepare() if anything went + // wrong. + return op_data; + } + return op_data; } @@ -263,6 +283,12 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { } for (const auto& node_data : op_data->nodes) { + if (node_data.nodedef.op().empty()) { + context->ReportError(context, "Invalid NodeDef in Flex op '%s'", + node_data.name.c_str()); + return kTfLiteError; + } + for (int tensor_index : node_data.inputs) { ++tensor_ref_count[tensor_index]; } @@ -303,12 +329,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { for (const auto& node_data : op_data->nodes) { SCOPED_TAGGED_OPERATOR_PROFILE( reinterpret_cast(context->profiler), - node_data.name.c_str(), node_data.index); - if (node_data.nodedef.op().empty()) { - context->ReportError(context, "Invalid NodeDef in Flex op '%s'", - node_data.name.c_str()); - return kTfLiteError; - } + node_data->name.c_str(), node_data->index); + auto status = ExecuteFlexOp(eager_context, buffer_map, node_data.name, node_data.nodedef, node_data.inputs, node_data.outputs); diff --git a/tensorflow/lite/delegates/flex/kernel_test.cc b/tensorflow/lite/delegates/flex/kernel_test.cc index cc5c8b32a0..647c199d4f 100644 --- a/tensorflow/lite/delegates/flex/kernel_test.cc +++ b/tensorflow/lite/delegates/flex/kernel_test.cc @@ -166,10 +166,7 @@ TEST_F(KernelTest, WrongSetOfNodes) { return GenericPrepare(context, delegate, {0, 1}); }); - SetShape(0, {2, 2, 1}); - SetValues(0, {1.1f, 2.2f, 3.3f, 4.4f}); - - ASSERT_FALSE(Invoke()); + ASSERT_NE(interpreter_->AllocateTensors(), kTfLiteOk); ASSERT_THAT(error_reporter().error_messages(), ContainsRegex("Invalid NodeDef in Flex op")); } -- GitLab From 03258acbf396a1241dee813e86848dc768394afd Mon Sep 17 00:00:00 2001 From: Jiri Simsa Date: Thu, 3 Jan 2019 17:15:41 -0800 Subject: [PATCH 323/622] [tf.data] Replacing active / total parallel calls metric with thread utilization metric. PiperOrigin-RevId: 227776910 --- .../experimental/map_and_batch_dataset_op.cc | 22 ++++++++----------- .../data/parallel_interleave_dataset_op.cc | 19 +++++++--------- .../kernels/data/parallel_map_iterator.cc | 19 +++++++--------- .../kernel_tests/stats_dataset_test_base.py | 4 +--- 4 files changed, 26 insertions(+), 38 deletions(-) diff --git a/tensorflow/core/kernels/data/experimental/map_and_batch_dataset_op.cc b/tensorflow/core/kernels/data/experimental/map_and_batch_dataset_op.cc index ef75c84456..a6348e4647 100644 --- a/tensorflow/core/kernels/data/experimental/map_and_batch_dataset_op.cc +++ b/tensorflow/core/kernels/data/experimental/map_and_batch_dataset_op.cc @@ -259,7 +259,7 @@ class MapAndBatchDatasetOp : public UnaryDatasetOpKernel { params.dataset->batch_size_)) { std::vector components = str_util::Split(params.prefix, "::", str_util::SkipEmpty()); - prefix_end_ = components.back(); + key_prefix_ = components.back(); } ~Iterator() override { @@ -397,8 +397,9 @@ class MapAndBatchDatasetOp : public UnaryDatasetOpKernel { const auto& stats_aggregator = ctx->stats_aggregator(); if (stats_aggregator) { stats_aggregator->AddScalar( - strings::StrCat(prefix_end_, "::active_parallel_calls"), - static_cast(num_calls_)); + strings::StrCat(key_prefix_, "::thread_utilization"), + static_cast(num_calls_) / + static_cast(num_parallel_calls_->value)); } cond_var_->notify_all(); } @@ -637,18 +638,13 @@ class MapAndBatchDatasetOp : public UnaryDatasetOpKernel { num_calls_++; } } - const std::shared_ptr& stats_aggregator = - ctx->stats_aggregator(); + const auto& stats_aggregator = ctx->stats_aggregator(); if (stats_aggregator) { mutex_lock l(*mu_); - // TODO(shivaniagrawal): add `parallel_calls_utilization` in the - // monitoring code or as histogram at fixed time intervals. stats_aggregator->AddScalar( - strings::StrCat(prefix_end_, "::active_parallel_calls"), - static_cast(num_calls_)); - stats_aggregator->AddScalar( - strings::StrCat(prefix_end_, "::num_parallel_calls"), - static_cast(num_parallel_calls_->value)); + strings::StrCat(key_prefix_, "::thread_utilization"), + static_cast(num_calls_) / + static_cast(num_parallel_calls_->value)); } for (const auto& call : new_calls) { CallFunction(ctx, call.first, call.second); @@ -803,7 +799,7 @@ class MapAndBatchDatasetOp : public UnaryDatasetOpKernel { int64 waiting_ GUARDED_BY(*mu_) = 0; // Identifies the maximum number of batch results to store. int64 max_batch_results_ GUARDED_BY(*mu_); - string prefix_end_; + string key_prefix_; std::unique_ptr instantiated_captured_func_; }; diff --git a/tensorflow/core/kernels/data/parallel_interleave_dataset_op.cc b/tensorflow/core/kernels/data/parallel_interleave_dataset_op.cc index 74791669ce..315f96d4b5 100644 --- a/tensorflow/core/kernels/data/parallel_interleave_dataset_op.cc +++ b/tensorflow/core/kernels/data/parallel_interleave_dataset_op.cc @@ -209,7 +209,7 @@ class ParallelInterleaveDatasetOp : public UnaryDatasetOpKernel { false /* low_latency_hint */)) { std::vector components = str_util::Split(params.prefix, "::", str_util::SkipEmpty()); - prefix_end_ = components.back(); + key_prefix_ = components.back(); } ~ParallelInterleaveIterator() override { @@ -430,8 +430,9 @@ class ParallelInterleaveDatasetOp : public UnaryDatasetOpKernel { const auto& stats_aggregator = ctx->stats_aggregator(); if (stats_aggregator) { stats_aggregator->AddScalar( - strings::StrCat(prefix_end_, "::active_parallel_calls"), - static_cast(num_calls_)); + strings::StrCat(key_prefix_, "::thread_utilization"), + static_cast(num_calls_) / + static_cast(num_parallel_calls_->value)); } cond_var_->notify_all(); } @@ -515,14 +516,10 @@ class ParallelInterleaveDatasetOp : public UnaryDatasetOpKernel { } const auto& stats_aggregator = ctx->stats_aggregator(); if (stats_aggregator) { - // TODO(shivaniagrawal): add `parallel_calls_utilization` in the - // monitoring code or as histogram at fixed time intervals. stats_aggregator->AddScalar( - strings::StrCat(prefix_end_, "::active_parallel_calls"), - static_cast(num_calls_)); - stats_aggregator->AddScalar( - strings::StrCat(prefix_end_, "::num_parallel_calls"), - static_cast(num_parallel_calls_->value)); + strings::StrCat(key_prefix_, "::thread_utilization"), + static_cast(num_calls_) / + static_cast(num_parallel_calls_->value)); } cond_var_->notify_all(); } @@ -693,7 +690,7 @@ class ParallelInterleaveDatasetOp : public UnaryDatasetOpKernel { // Identifies whether background activity should be cancelled. bool cancelled_ GUARDED_BY(*mu_) = false; - string prefix_end_; + string key_prefix_; std::unique_ptr instantiated_captured_func_; }; diff --git a/tensorflow/core/kernels/data/parallel_map_iterator.cc b/tensorflow/core/kernels/data/parallel_map_iterator.cc index b62e7059ba..9a4ce981d0 100644 --- a/tensorflow/core/kernels/data/parallel_map_iterator.cc +++ b/tensorflow/core/kernels/data/parallel_map_iterator.cc @@ -60,7 +60,7 @@ class ParallelMapIterator : public DatasetBaseIterator { preserve_cardinality_(params.preserve_cardinality) { std::vector components = str_util::Split(base_params.prefix, "::", str_util::SkipEmpty()); - prefix_end_ = components.back(); + key_prefix_ = components.back(); } ~ParallelMapIterator() override { @@ -207,8 +207,9 @@ class ParallelMapIterator : public DatasetBaseIterator { const auto& stats_aggregator = ctx->stats_aggregator(); if (stats_aggregator) { stats_aggregator->AddScalar( - strings::StrCat(prefix_end_, "::active_parallel_calls"), - static_cast(num_calls_)); + strings::StrCat(key_prefix_, "::thread_utilization"), + static_cast(num_calls_) / + static_cast(num_parallel_calls_->value)); } RecordBufferEnqueue(ctx.get(), result->return_values); result->notification.Notify(); @@ -300,14 +301,10 @@ class ParallelMapIterator : public DatasetBaseIterator { } const auto& stats_aggregator = ctx->stats_aggregator(); if (stats_aggregator) { - // TODO(shivaniagrawal): add `parallel_calls_utilization` in the - // monitoring code or as histogram at fixed time intervals. - stats_aggregator->AddScalar( - strings::StrCat(prefix_end_, "::active_parallel_calls"), - static_cast(num_calls_)); stats_aggregator->AddScalar( - strings::StrCat(prefix_end_, "::num_parallel_calls"), - static_cast(num_parallel_calls_->value)); + strings::StrCat(key_prefix_, "::thread_utilization"), + static_cast(num_calls_) / + static_cast(num_parallel_calls_->value)); } cond_var_->notify_all(); } @@ -403,7 +400,7 @@ class ParallelMapIterator : public DatasetBaseIterator { GUARDED_BY(*mu_); std::unique_ptr runner_thread_ GUARDED_BY(*mu_); bool cancelled_ GUARDED_BY(*mu_) = false; - string prefix_end_; + string key_prefix_; }; } // namespace diff --git a/tensorflow/python/data/experimental/kernel_tests/stats_dataset_test_base.py b/tensorflow/python/data/experimental/kernel_tests/stats_dataset_test_base.py index b80aab994e..f5a15f4c84 100644 --- a/tensorflow/python/data/experimental/kernel_tests/stats_dataset_test_base.py +++ b/tensorflow/python/data/experimental/kernel_tests/stats_dataset_test_base.py @@ -104,9 +104,7 @@ class StatsDatasetTestBase(test_base.DatasetTestBase): self._assertSummaryHasCountMoreOrEqualGeneralisedTag( summary_str, "::execution_time", float(i + 1)) self._assertSummaryContains(summary_str, - dataset_name + "::num_parallel_calls") - self._assertSummaryContains(summary_str, - dataset_name + "::active_parallel_calls") + dataset_name + "::thread_utilization") with self.assertRaises(errors.OutOfRangeError): self.evaluate(next_element()) if function_processing_time: -- GitLab From 8cfb8d2c11c93e36d7db2635518427260fcc4d41 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Thu, 3 Jan 2019 17:16:06 -0800 Subject: [PATCH 324/622] Turn OpNode into a proper class, in preparation for a larger change. PiperOrigin-RevId: 227776959 --- tensorflow/lite/delegates/flex/kernel.cc | 118 +++++++++++++---------- 1 file changed, 68 insertions(+), 50 deletions(-) diff --git a/tensorflow/lite/delegates/flex/kernel.cc b/tensorflow/lite/delegates/flex/kernel.cc index 250fc31043..7d18c39098 100644 --- a/tensorflow/lite/delegates/flex/kernel.cc +++ b/tensorflow/lite/delegates/flex/kernel.cc @@ -74,17 +74,66 @@ class OpOutputs { // A single node within the larger 'op'. Note that this kernel executes many // TensorFlow ops within a single TF Lite op. -struct OpNode { +class OpNode { + public: + OpNode() {} + ~OpNode() {} + + const string& name() const { return name_; } + void set_name(const string& name) { name_ = name; } + + int index() const { return index_; } + void set_index(int index) { index_ = index; } + + const tensorflow::NodeDef& nodedef() const { return nodedef_; } + + const std::vector& inputs() const { return inputs_; } + std::vector* mutable_inputs() { return &inputs_; } + + const std::vector& outputs() const { return outputs_; } + std::vector* mutable_outputs() { return &outputs_; } + + tensorflow::Status InitializeNodeDef(const void* custom_initial_data, + int custom_initial_data_size) { + if (!custom_initial_data) { + return tensorflow::errors::Internal( + "Cannot convert empty data into a valid NodeDef"); + } + // The flexbuffer contains a vector where the first elements is the + // op name and the second is a serialized NodeDef. + const flexbuffers::Vector& v = + flexbuffers::GetRoot( + reinterpret_cast(custom_initial_data), + custom_initial_data_size) + .AsVector(); + + name_ = v[0].AsString().str(); + if (!nodedef_.ParseFromString(v[1].AsString().str())) { + nodedef_.Clear(); + return tensorflow::errors::Internal( + "Failed to parse data into a valid NodeDef"); + } + + // Fill NodeDef with defaults if it's a valid op. + const tensorflow::OpRegistrationData* op_reg_data; + TF_RETURN_IF_ERROR( + tensorflow::OpRegistry::Global()->LookUp(nodedef_.op(), &op_reg_data)); + AddDefaultsToNodeDef(op_reg_data->op_def, &nodedef_); + + return tensorflow::Status::OK(); + } + + private: // The name of the TensorFlow op to execute. - string name; + string name_; // Index of this node into TF Lite's operator list. - int index; + int index_; // The corresponding NodeDef, containing the attributes for the op. - tensorflow::NodeDef nodedef; + tensorflow::NodeDef nodedef_; // List of inputs, as TF Lite tensor indices. - std::vector inputs; + std::vector inputs_; // List of outputs, as TF Lite tensor indices. - std::vector outputs; + std::vector outputs_; }; // Executes the TensorFlow op given by 'op_name', with the attributes specified @@ -148,37 +197,6 @@ tensorflow::Status ExecuteFlexOp(tensorflow::EagerContext* eager_context, return tensorflow::Status::OK(); } -tensorflow::Status InitializeNodeDef(const void* custom_initial_data, - int custom_initial_data_size, - OpNode* node_data) { - if (!custom_initial_data) { - return tensorflow::errors::Internal( - "Cannot convert empty data into a valid NodeDef"); - } - // The flexbuffer contains a vector where the first elements is the - // op name and the second is a serialized NodeDef. - const flexbuffers::Vector& v = - flexbuffers::GetRoot( - reinterpret_cast(custom_initial_data), - custom_initial_data_size) - .AsVector(); - - node_data->name = v[0].AsString().str(); - if (!node_data->nodedef.ParseFromString(v[1].AsString().str())) { - node_data->nodedef.Clear(); - return tensorflow::errors::Internal( - "Failed to parse data into a valid NodeDef"); - } - - // Fill NodeDef with defaults if it's a valid op. - const tensorflow::OpRegistrationData* op_reg_data; - TF_RETURN_IF_ERROR(tensorflow::OpRegistry::Global()->LookUp( - node_data->nodedef.op(), &op_reg_data)); - AddDefaultsToNodeDef(op_reg_data->op_def, &node_data->nodedef); - - return tensorflow::Status::OK(); -} - // The larger 'op', which contains all the nodes in a supported subgraph. struct OpData { tensorflow::EagerContext* eager_context; @@ -222,18 +240,18 @@ void* Init(TfLiteContext* context, const char* buffer, size_t length) { op_data->nodes.push_back(OpNode()); OpNode& node_data = op_data->nodes.back(); - node_data.index = node_index; - node_data.name = ""; + node_data.set_index(node_index); + node_data.set_name(""); - status = InitializeNodeDef(node->custom_initial_data, - node->custom_initial_data_size, &node_data); + status = node_data.InitializeNodeDef(node->custom_initial_data, + node->custom_initial_data_size); if (!status.ok()) break; for (auto input_index : TfLiteIntArrayView(node->inputs)) { - node_data.inputs.push_back(input_index); + node_data.mutable_inputs()->push_back(input_index); } for (auto output_index : TfLiteIntArrayView(node->outputs)) { - node_data.outputs.push_back(output_index); + node_data.mutable_outputs()->push_back(output_index); } } @@ -283,13 +301,13 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { } for (const auto& node_data : op_data->nodes) { - if (node_data.nodedef.op().empty()) { + if (node_data.nodedef().op().empty()) { context->ReportError(context, "Invalid NodeDef in Flex op '%s'", - node_data.name.c_str()); + node_data.name().c_str()); return kTfLiteError; } - for (int tensor_index : node_data.inputs) { + for (int tensor_index : node_data.inputs()) { ++tensor_ref_count[tensor_index]; } } @@ -329,11 +347,11 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { for (const auto& node_data : op_data->nodes) { SCOPED_TAGGED_OPERATOR_PROFILE( reinterpret_cast(context->profiler), - node_data->name.c_str(), node_data->index); + node_data->name().c_str(), node_data->index()); - auto status = - ExecuteFlexOp(eager_context, buffer_map, node_data.name, - node_data.nodedef, node_data.inputs, node_data.outputs); + auto status = ExecuteFlexOp(eager_context, buffer_map, node_data.name(), + node_data.nodedef(), node_data.inputs(), + node_data.outputs()); TF_LITE_ENSURE_OK(context, ConvertStatus(context, status)); } -- GitLab From 1bad2986b24e520b6b1558d5970b4b25abff0ab3 Mon Sep 17 00:00:00 2001 From: Tong Shen Date: Thu, 3 Jan 2019 17:22:28 -0800 Subject: [PATCH 325/622] Support outside compilation in TPUPartitionedCall. PiperOrigin-RevId: 227777744 --- tensorflow/compiler/tf2xla/BUILD | 1 + .../compiler/tf2xla/side_effect_util.cc | 39 +++++++++++++++++++ tensorflow/compiler/tf2xla/side_effect_util.h | 4 ++ 3 files changed, 44 insertions(+) diff --git a/tensorflow/compiler/tf2xla/BUILD b/tensorflow/compiler/tf2xla/BUILD index d8123e956f..0366ec45fb 100644 --- a/tensorflow/compiler/tf2xla/BUILD +++ b/tensorflow/compiler/tf2xla/BUILD @@ -669,6 +669,7 @@ cc_library( name = "side_effect_util", srcs = ["side_effect_util.cc"], hdrs = ["side_effect_util.h"], + visibility = [":friends"], deps = [ "//tensorflow/core:core_cpu", "@com_google_absl//absl/strings", diff --git a/tensorflow/compiler/tf2xla/side_effect_util.cc b/tensorflow/compiler/tf2xla/side_effect_util.cc index b62f8e9115..a9144cfc4f 100644 --- a/tensorflow/compiler/tf2xla/side_effect_util.cc +++ b/tensorflow/compiler/tf2xla/side_effect_util.cc @@ -26,6 +26,45 @@ const char kXlaTokenArgNodeName[] = "_xla_token_arg_node"; const char kXlaHasHostTransferAttrName[] = "_xla_has_host_transfer"; +Status SetDeviceOrdinalAttributeForNode(Node* node, int device_ordinal) { + if (!HasNodeAttr(node->def(), kXlaHasHostTransferAttrName)) { + return errors::InvalidArgument("Node ", node->DebugString(), + " does not have attribute ", + kXlaHasHostTransferAttrName); + } + + if (node->type_string() == "_XlaRecvAtHost" || + node->type_string() == "_XlaSendFromHost") { + node->ClearAttr("device_ordinal"); + node->AddAttr("device_ordinal", device_ordinal); + } else if (node->type_string() == "If") { + AttrValue device_ordinal_value; + device_ordinal_value.set_i(device_ordinal); + for (const string& attr_name : + std::vector{"then_branch", "else_branch"}) { + NameAttrList branch_func; + TF_RETURN_IF_ERROR(GetNodeAttr(node->attrs(), attr_name, &branch_func)); + (*branch_func.mutable_attr())["device_ordinal"] = device_ordinal_value; + node->ClearAttr(attr_name); + node->AddAttr(attr_name, branch_func); + } + } else if (node->type_string() == "While") { + AttrValue device_ordinal_value; + device_ordinal_value.set_i(device_ordinal); + for (const string& attr_name : std::vector{"cond", "body"}) { + NameAttrList branch_func; + TF_RETURN_IF_ERROR(GetNodeAttr(node->attrs(), attr_name, &branch_func)); + (*branch_func.mutable_attr())["device_ordinal"] = device_ordinal_value; + node->ClearAttr(attr_name); + node->AddAttr(attr_name, branch_func); + } + } else { + return errors::Internal("Unknown node type to set 'device_ordinal': ", + node->DebugString()); + } + return Status::OK(); +} + std::set CalculateTokenInputsForOutputToken(const Graph& g) { std::set results; Node* first_side_effecting_node_on_path = nullptr; diff --git a/tensorflow/compiler/tf2xla/side_effect_util.h b/tensorflow/compiler/tf2xla/side_effect_util.h index 7081b362c3..75e1f253fb 100644 --- a/tensorflow/compiler/tf2xla/side_effect_util.h +++ b/tensorflow/compiler/tf2xla/side_effect_util.h @@ -38,6 +38,10 @@ extern const char kXlaTokenArgNodeName[]; // This node have XlaRecvAtHost/XlaSendFromHost in its associated functions. extern const char kXlaHasHostTransferAttrName[]; +// Sets device ordinal attribute for nodes with attribute +// `kXlaHasHostTransferAttrName`. +Status SetDeviceOrdinalAttributeForNode(Node* node, int device_ordinal); + // Calculates side-effect dependencies for the graph's token output. // Returns a set of node names representing these dependencies. std::set CalculateTokenInputsForOutputToken(const Graph& g); -- GitLab From 7f9d4f0b93f256440662fa12e5508b552537803a Mon Sep 17 00:00:00 2001 From: Eugene Zhulenev Date: Thu, 3 Jan 2019 17:31:51 -0800 Subject: [PATCH 326/622] Enable Tensorflow custom contraction kernel support in XLA single threaded matmul. PiperOrigin-RevId: 227778830 --- tensorflow/compiler/xla/service/cpu/BUILD | 1 + .../xla/service/cpu/runtime_single_threaded_matmul.cc | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/tensorflow/compiler/xla/service/cpu/BUILD b/tensorflow/compiler/xla/service/cpu/BUILD index f49b5110be..a4ca772c7c 100644 --- a/tensorflow/compiler/xla/service/cpu/BUILD +++ b/tensorflow/compiler/xla/service/cpu/BUILD @@ -631,6 +631,7 @@ cc_library( deps = [ ":runtime_matvec", "//tensorflow/core:framework_lite", + "//tensorflow/core/kernels:eigen_contraction_kernel", "//third_party/eigen3", ], ) diff --git a/tensorflow/compiler/xla/service/cpu/runtime_single_threaded_matmul.cc b/tensorflow/compiler/xla/service/cpu/runtime_single_threaded_matmul.cc index 1ed743afc3..1f7204e67a 100644 --- a/tensorflow/compiler/xla/service/cpu/runtime_single_threaded_matmul.cc +++ b/tensorflow/compiler/xla/service/cpu/runtime_single_threaded_matmul.cc @@ -20,6 +20,10 @@ limitations under the License. #include "tensorflow/core/platform/dynamic_annotations.h" #include "tensorflow/core/platform/types.h" +#if defined(TENSORFLOW_USE_CUSTOM_CONTRACTION_KERNEL) +#include "tensorflow/core/kernels/eigen_contraction_kernel.h" +#endif + using tensorflow::int32; using tensorflow::int64; -- GitLab From 846dba8e81fde8a47196c9b04d31f847e5cc336f Mon Sep 17 00:00:00 2001 From: Gunhan Gulsoy Date: Thu, 3 Jan 2019 18:05:00 -0800 Subject: [PATCH 327/622] Automated rollback of commit 64596a68235709e7f3246d0b120a4146521a998b PiperOrigin-RevId: 227782472 --- tensorflow/lite/experimental/swift/BUILD | 100 ----- tensorflow/lite/experimental/swift/LICENSE | 202 ---------- tensorflow/lite/experimental/swift/README.md | 54 --- .../swift/Sources/Interpreter.swift | 265 -------------- .../swift/Sources/InterpreterError.swift | 99 ----- .../swift/Sources/InterpreterOptions.swift | 29 -- .../experimental/swift/Sources/Model.swift | 40 -- .../Sources/QuantizationParameters.swift | 38 -- .../experimental/swift/Sources/Tensor.swift | 138 ------- .../Configs/TensorFlowLite.tulsigen | 57 --- .../project.tulsiconf | 14 - .../project.pbxproj | 345 ------------------ .../TensorFlowLiteApp/AppDelegate.swift | 24 -- .../Array+TensorFlowLite.swift | 22 -- .../AppIcon.appiconset/Contents.json | 98 ----- .../Assets.xcassets/Contents.json | 6 - .../Base.lproj/LaunchScreen.storyboard | 44 --- .../Base.lproj/Main.storyboard | 95 ----- .../Data+TensorFlowLite.swift | 13 - .../TensorFlowLiteApp/Info.plist | 46 --- .../TensorFlowLiteApp/ViewController.swift | 299 --------------- .../swift/Tests/InterpreterOptionsTests.swift | 54 --- .../swift/Tests/InterpreterTests.swift | 315 ---------------- .../experimental/swift/Tests/ModelTests.swift | 59 --- .../Tests/QuantizationParametersTests.swift | 43 --- .../swift/Tests/TensorTests.swift | 83 ----- .../tools/pip_package/pip_smoke_test.py | 36 +- 27 files changed, 15 insertions(+), 2603 deletions(-) delete mode 100644 tensorflow/lite/experimental/swift/BUILD delete mode 100644 tensorflow/lite/experimental/swift/LICENSE delete mode 100644 tensorflow/lite/experimental/swift/README.md delete mode 100644 tensorflow/lite/experimental/swift/Sources/Interpreter.swift delete mode 100644 tensorflow/lite/experimental/swift/Sources/InterpreterError.swift delete mode 100644 tensorflow/lite/experimental/swift/Sources/InterpreterOptions.swift delete mode 100644 tensorflow/lite/experimental/swift/Sources/Model.swift delete mode 100644 tensorflow/lite/experimental/swift/Sources/QuantizationParameters.swift delete mode 100644 tensorflow/lite/experimental/swift/Sources/Tensor.swift delete mode 100644 tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/Configs/TensorFlowLite.tulsigen delete mode 100644 tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/project.tulsiconf delete mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp.xcodeproj/project.pbxproj delete mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/AppDelegate.swift delete mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Array+TensorFlowLite.swift delete mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/Contents.json delete mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/LaunchScreen.storyboard delete mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/Main.storyboard delete mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Data+TensorFlowLite.swift delete mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Info.plist delete mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/ViewController.swift delete mode 100644 tensorflow/lite/experimental/swift/Tests/InterpreterOptionsTests.swift delete mode 100644 tensorflow/lite/experimental/swift/Tests/InterpreterTests.swift delete mode 100644 tensorflow/lite/experimental/swift/Tests/ModelTests.swift delete mode 100644 tensorflow/lite/experimental/swift/Tests/QuantizationParametersTests.swift delete mode 100644 tensorflow/lite/experimental/swift/Tests/TensorTests.swift diff --git a/tensorflow/lite/experimental/swift/BUILD b/tensorflow/lite/experimental/swift/BUILD deleted file mode 100644 index 3bd288a1e1..0000000000 --- a/tensorflow/lite/experimental/swift/BUILD +++ /dev/null @@ -1,100 +0,0 @@ -# TensorFlow Lite for Swift. - -package(default_visibility = ["//visibility:private"]) - -licenses(["notice"]) # Apache 2.0 - -exports_files(["LICENSE"]) - -load("@build_bazel_rules_apple//apple:ios.bzl", "ios_application", "ios_unit_test") -load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") - -MINIMUM_OS_VERSION = "9.0" - -SWIFT_COPTS = [ - "-wmo", -] - -swift_library( - name = "TensorFlowLite", - srcs = glob(["Sources/*.swift"]), - copts = SWIFT_COPTS, - module_name = "TensorFlowLite", - tags = ["manual"], - deps = [ - "//tensorflow/lite/experimental/c:c_api", - ], -) - -ios_unit_test( - name = "TensorFlowLiteTests", - size = "small", - minimum_os_version = MINIMUM_OS_VERSION, - tags = [ - "manual", - # DISABLED: Following sanitizer tests are not supported by iOS test targets. - "noasan", - "nomsan", - "notsan", - ], - deps = [":TensorFlowLiteTestsLib"], -) - -swift_library( - name = "TensorFlowLiteTestsLib", - testonly = 1, - srcs = glob(["Tests/*.swift"]), - copts = SWIFT_COPTS, - tags = ["manual"], - deps = [ - ":TensorFlowLite", - ":TestResources", - ], -) - -objc_library( - name = "TestResources", - resources = [ - "//tensorflow/lite:testdata/add.bin", - "//tensorflow/lite:testdata/add_quantized.bin", - "//tensorflow/lite:testdata/multi_add.bin", - ], - tags = ["manual"], -) - -ios_application( - name = "TensorFlowLiteApp", - app_icons = glob(["TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/AppIcon.appiconset/**"]), - bundle_id = "com.tensorflow.lite.swift.TensorFlowLite", - families = [ - "ipad", - "iphone", - ], - infoplists = ["TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Info.plist"], - launch_storyboard = "TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/LaunchScreen.storyboard", - minimum_os_version = MINIMUM_OS_VERSION, - sdk_frameworks = [ - "CoreGraphics", - ], - tags = ["manual"], - deps = [":TensorFlowLiteAppLib"], -) - -swift_library( - name = "TensorFlowLiteAppLib", - srcs = glob(["TestApps/TensorFlowLiteApp/TensorFlowLiteApp/*.swift"]), - tags = ["manual"], - deps = [ - ":TensorFlowLite", - ":TensorFlowLiteAppResources", - ], -) - -objc_library( - name = "TensorFlowLiteAppResources", - storyboards = glob([ - "TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/*.storyboard", - ]), - tags = ["manual"], - deps = [":TestResources"], -) diff --git a/tensorflow/lite/experimental/swift/LICENSE b/tensorflow/lite/experimental/swift/LICENSE deleted file mode 100644 index d645695673..0000000000 --- a/tensorflow/lite/experimental/swift/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/tensorflow/lite/experimental/swift/README.md b/tensorflow/lite/experimental/swift/README.md deleted file mode 100644 index deb16c1e91..0000000000 --- a/tensorflow/lite/experimental/swift/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# TensorFlow Lite for Swift - -[TensorFlow Lite](https://www.tensorflow.org/lite/) is TensorFlow's lightweight -solution for Swift developers. It enables low-latency inference of on-device -machine learning models with a small binary size and fast performance supporting -hardware acceleration. - -## Getting Started - -### Bazel - -In your `BUILD` file, add the `TensorFlowLite` dependency: - -```python -swift_library( - # ... - deps = [ - "//tensorflow/lite/swift:TensorFlowLite", - ], -) -``` - -In your Swift files, import the module: - -```swift -import TensorFlowLite -``` - -### Tulsi - -Open the `TensorFlowLite.tulsiproj` using the [TulsiApp](https://github.com/bazelbuild/tulsi) or by -running the [`generate_xcodeproj.sh`](https://github.com/bazelbuild/tulsi/blob/master/src/tools/generate_xcodeproj.sh) -script: - -```shell -generate_xcodeproj.sh --genconfig tensorflow/lite/swift/TensorFlowLite.tulsiproj:TensorFlowLite --outputfolder ~/path/to/generated/TensorFlowLite.xcodeproj -``` - -### CocoaPods - -Add the following to your `Podfile`: - -```ruby -use_frameworks! -pod 'TensorFlowLiteSwift' -``` - -Then, run `pod install`. - -In your Swift files, import the module: - -```swift -import TensorFlowLite -``` diff --git a/tensorflow/lite/experimental/swift/Sources/Interpreter.swift b/tensorflow/lite/experimental/swift/Sources/Interpreter.swift deleted file mode 100644 index a14b5966b1..0000000000 --- a/tensorflow/lite/experimental/swift/Sources/Interpreter.swift +++ /dev/null @@ -1,265 +0,0 @@ -// Copyright 2018 Google Inc. 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. - -import Foundation -import TensorFlowLiteCAPI - -/// A TensorFlow Lite interpreter that performs inference from a given model. -public final class Interpreter { - - /// The `TFL_Interpreter` C pointer type represented as an `UnsafePointer`. - private typealias CInterpreter = OpaquePointer - - /// Total number of input tensors associated with the model. - public var inputTensorCount: Int { - return Int(TFL_InterpreterGetInputTensorCount(cInterpreter)) - } - - /// Total number of output tensors associated with the model. - public var outputTensorCount: Int { - return Int(TFL_InterpreterGetOutputTensorCount(cInterpreter)) - } - - /// The underlying `TFL_Interpreter` C pointer. - private var cInterpreter: CInterpreter? - - /// Creates a new model interpreter instance. - /// - /// - Parameters: - /// - modelPath: Local file path to a TensorFlow Lite model. - /// - options: Custom configurations for the interpreter. The default is `nil` indicating that - /// interpreter will determine the configuration options. - /// - Throws: An error if the model could not be loaded or the interpreter could not be created. - public init(modelPath: String, options: InterpreterOptions? = nil) throws { - guard let model = Model(filePath: modelPath) else { throw InterpreterError.failedToLoadModel } - - let cInterpreterOptions: OpaquePointer? = try options.map { options in - guard let cOptions = TFL_NewInterpreterOptions() else { - throw InterpreterError.failedToCreateInterpreter - } - if let threadCount = options.threadCount, threadCount > 0 { - TFL_InterpreterOptionsSetNumThreads(cOptions, Int32(threadCount)) - } - if options.isErrorLoggingEnabled { - TFL_InterpreterOptionsSetErrorReporter( - cOptions, - { (_, format, arguments) in - guard let cFormat = format, - let message = String(cFormat: cFormat, arguments: arguments) - else { - return - } - print(String(describing: InterpreterError.tensorFlowLiteError(message))) - }, - nil - ) - } - return cOptions - } - defer { TFL_DeleteInterpreterOptions(cInterpreterOptions) } - - guard let cInterpreter = TFL_NewInterpreter(model.cModel, cInterpreterOptions) else { - throw InterpreterError.failedToCreateInterpreter - } - self.cInterpreter = cInterpreter - } - - deinit { - TFL_DeleteInterpreter(cInterpreter) - } - - /// Invokes the interpreter to perform inference from the loaded graph. - /// - /// - Throws: An error if the model was not ready because tensors were not allocated. - public func invoke() throws { - guard TFL_InterpreterInvoke(cInterpreter) == kTfLiteOk else { - // TODO(b/117510052): Determine which error to throw. - throw InterpreterError.allocateTensorsRequired - } - } - - /// Returns the input tensor at the given index. - /// - /// - Parameters: - /// - index: The index for the input tensor. - /// - Throws: An error if the index is invalid or the tensors have not been allocated. - /// - Returns: The input tensor at the given index. - public func input(at index: Int) throws -> Tensor { - let maxIndex = inputTensorCount - 1 - guard case 0...maxIndex = index else { - throw InterpreterError.invalidTensorIndex(index: index, maxIndex: maxIndex) - } - guard let cTensor = TFL_InterpreterGetInputTensor(cInterpreter, Int32(index)), - let bytes = TFL_TensorData(cTensor), - let nameCString = TFL_TensorName(cTensor) - else { - throw InterpreterError.allocateTensorsRequired - } - guard let dataType = TensorDataType(type: TFL_TensorType(cTensor)) else { - throw InterpreterError.invalidTensorDataType - } - - let name = String(cString: nameCString) - let rank = TFL_TensorNumDims(cTensor) - let dimensions = (0.. Tensor { - let maxIndex = outputTensorCount - 1 - guard case 0...maxIndex = index else { - throw InterpreterError.invalidTensorIndex(index: index, maxIndex: maxIndex) - } - guard let cTensor = TFL_InterpreterGetOutputTensor(cInterpreter, Int32(index)), - let bytes = TFL_TensorData(cTensor), - let nameCString = TFL_TensorName(cTensor) - else { - // TODO(b/117510052): Determine which error to throw. - throw InterpreterError.invokeInterpreterRequired - } - guard let dataType = TensorDataType(type: TFL_TensorType(cTensor)) else { - throw InterpreterError.invalidTensorDataType - } - - let name = String(cString: nameCString) - let rank = TFL_TensorNumDims(cTensor) - let dimensions = (0.. Tensor { - let maxIndex = inputTensorCount - 1 - guard case 0...maxIndex = index else { - throw InterpreterError.invalidTensorIndex(index: index, maxIndex: maxIndex) - } - guard let cTensor = TFL_InterpreterGetInputTensor(cInterpreter, Int32(index)) else { - throw InterpreterError.allocateTensorsRequired - } - - let byteCount = TFL_TensorByteSize(cTensor) - guard data.count == byteCount else { - throw InterpreterError.invalidTensorDataCount(provided: data.count, required: byteCount) - } - - let status = data.withUnsafeBytes { TFL_TensorCopyFromBuffer(cTensor, $0, data.count) } - guard status == kTfLiteOk else { throw InterpreterError.failedToCopyDataToInputTensor } - return try input(at: index) - } - - /// Allocates memory for all input tensors based on their `TensorShape`s. - /// - /// - Note: This is a relatively expensive operation and should only be called after creating the - /// interpreter and/or resizing any input tensors. - /// - Throws: An error if memory could not be allocated for the input tensors. - public func allocateTensors() throws { - guard TFL_InterpreterAllocateTensors(cInterpreter) == kTfLiteOk else { - throw InterpreterError.failedToAllocateTensors - } - } -} - -// MARK: - Extensions - -extension String { - /// Returns a new `String` initialized by using the given format C array as a template into which - /// the remaining argument values are substituted according to the user’s default locale. - /// - /// - Note: Returns `nil` if a new `String` could not be constructed from the given values. - /// - Parameters: - /// - cFormat: The format C array as a template for substituting values. - /// - arguments: A C pointer to a `va_list` of arguments to substitute into `cFormat`. - init?(cFormat: UnsafePointer, arguments: CVaListPointer) { - var buffer: UnsafeMutablePointer? - guard vasprintf(&buffer, cFormat, arguments) != 0, let cString = buffer else { return nil } - self.init(validatingUTF8: cString) - } -} diff --git a/tensorflow/lite/experimental/swift/Sources/InterpreterError.swift b/tensorflow/lite/experimental/swift/Sources/InterpreterError.swift deleted file mode 100644 index 5de58b997a..0000000000 --- a/tensorflow/lite/experimental/swift/Sources/InterpreterError.swift +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2018 Google Inc. 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. - -import Foundation - -/// TensorFlow Lite interpreter errors. -public enum InterpreterError: Error { - case invalidTensorIndex(index: Int, maxIndex: Int) - case invalidTensorDataCount(provided: Int, required: Int) - case invalidTensorDataType - case failedToLoadModel - case failedToCreateInterpreter - case failedToResizeInputTensor(index: Int) - case failedToCopyDataToInputTensor - case failedToAllocateTensors - case allocateTensorsRequired - case invokeInterpreterRequired - case tensorFlowLiteError(String) -} - -// MARK: - Extensions - -extension InterpreterError: LocalizedError { - /// Localized description of the interpreter error. - public var errorDescription: String? { - switch self { - case .invalidTensorIndex(let index, let maxIndex): - return "Invalid tensor index \(index), max index is \(maxIndex)." - case .invalidTensorDataCount(let providedCount, let requiredCount): - return "Provided data count \(providedCount) must match the required count \(requiredCount)." - case .invalidTensorDataType: - return "Tensor data type is unsupported or could not be determined because of a model error." - case .failedToLoadModel: - return "Failed to load the given model." - case .failedToCreateInterpreter: - return "Failed to create the interpreter." - case .failedToResizeInputTensor(let index): - return "Failed to resize input tesnor at index \(index)." - case .failedToCopyDataToInputTensor: - return "Failed to copy data to input tensor." - case .failedToAllocateTensors: - return "Failed to allocate memory for input tensors." - case .allocateTensorsRequired: - return "Must call allocateTensors()." - case .invokeInterpreterRequired: - return "Must call invoke()." - case .tensorFlowLiteError(let message): - return "TensorFlow Lite Error: \(message)" - } - } -} - -extension InterpreterError: CustomStringConvertible { - /// Textual representation of the TensorFlow Lite interpreter error. - public var description: String { - return errorDescription ?? "Unknown error." - } -} - -#if swift(>=4.2) -extension InterpreterError: Equatable {} -#else -extension InterpreterError: Equatable { - public static func == (lhs: InterpreterError, rhs: InterpreterError) -> Bool { - switch (lhs, rhs) { - case (.invalidTensorDataType, .invalidTensorDataType), - (.failedToLoadModel, .failedToLoadModel), - (.failedToCreateInterpreter, .failedToCreateInterpreter), - (.failedToAllocateTensors, .failedToAllocateTensors), - (.allocateTensorsRequired, .allocateTensorsRequired), - (.invokeInterpreterRequired, .invokeInterpreterRequired): - return true - case (.invalidTensorIndex(let lhsIndex, let lhsMaxIndex), - .invalidTensorIndex(let rhsIndex, let rhsMaxIndex)): - return lhsIndex == rhsIndex && lhsMaxIndex == rhsMaxIndex - case (.invalidTensorDataCount(let lhsProvidedCount, let lhsRequiredCount), - .invalidTensorDataCount(let rhsProvidedCount, let rhsRequiredCount)): - return lhsProvidedCount == rhsProvidedCount && lhsRequiredCount == rhsRequiredCount - case (.failedToResizeInputTensor(let lhsIndex), .failedToResizeInputTensor(let rhsIndex)): - return lhsIndex == rhsIndex - case (.tensorFlowLiteError(let lhsMessage), .tensorFlowLiteError(let rhsMessage)): - return lhsMessage == rhsMessage - default: - return false - } - } -} -#endif // swift(>=4.2) diff --git a/tensorflow/lite/experimental/swift/Sources/InterpreterOptions.swift b/tensorflow/lite/experimental/swift/Sources/InterpreterOptions.swift deleted file mode 100644 index 2365fd7ade..0000000000 --- a/tensorflow/lite/experimental/swift/Sources/InterpreterOptions.swift +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2018 Google Inc. 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. - -import Foundation - -/// Custom configuration options for a TensorFlow Lite interpreter. -public struct InterpreterOptions: Equatable { - - /// Maximum number of CPU threads that the interpreter should run on. Default is `nil` which - /// indicates that the `Interpreter` will decide the number of threads to use. - public var threadCount: Int? = nil - - /// Whether error logging to the console is enabled. The default is `false`. - public var isErrorLoggingEnabled = false - - /// Creates a new instance of interpreter options. - public init() {} -} diff --git a/tensorflow/lite/experimental/swift/Sources/Model.swift b/tensorflow/lite/experimental/swift/Sources/Model.swift deleted file mode 100644 index e8c49ff1ae..0000000000 --- a/tensorflow/lite/experimental/swift/Sources/Model.swift +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2018 Google Inc. 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. - -import Foundation -import TensorFlowLiteCAPI - -/// A TensorFlow Lite model used by the 'Interpreter` to perform inference. -final class Model { - - /// The `TFL_Model` C pointer type represented as an `UnsafePointer`. - typealias CModel = OpaquePointer - - /// The underlying `TFL_Model` C pointer. - let cModel: CModel? - - /// Creates a new model instance. - /// - /// - Precondition: Initialization can fail if the given `filePath` is invalid. - /// - Parameters: - /// - filePath: Local file path to a TensorFlow Lite model. - init?(filePath: String) { - guard !filePath.isEmpty, let cModel = TFL_NewModelFromFile(filePath) else { return nil } - self.cModel = cModel - } - - deinit { - TFL_DeleteModel(cModel) - } -} diff --git a/tensorflow/lite/experimental/swift/Sources/QuantizationParameters.swift b/tensorflow/lite/experimental/swift/Sources/QuantizationParameters.swift deleted file mode 100644 index f367875644..0000000000 --- a/tensorflow/lite/experimental/swift/Sources/QuantizationParameters.swift +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2018 Google Inc. 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. - -import Foundation - -/// Parameters that determine the mapping of quantized values to real values. Quantized values can -/// be mapped to float values using the following conversion: -/// `realValue = scale * (quantizedValue - zeroPoint)`. -public struct QuantizationParameters { - - /// Difference between real values corresponding to consecutive quantized values differing by 1. - /// For example, the range of quantized values for `UInt8` data type is [0, 255]. - public let scale: Float - - /// Quantized value that corresponds to the real 0 value. - public let zeroPoint: Int - - /// Creates a new quantization parameters instance. - /// - /// - Parameters: - /// - scale: Scale value for asymmetric quantization. - /// - zeroPoint: Zero point for asymmetric quantization. - init(scale: Float, zeroPoint: Int) { - self.scale = scale - self.zeroPoint = zeroPoint - } -} diff --git a/tensorflow/lite/experimental/swift/Sources/Tensor.swift b/tensorflow/lite/experimental/swift/Sources/Tensor.swift deleted file mode 100644 index b738d87549..0000000000 --- a/tensorflow/lite/experimental/swift/Sources/Tensor.swift +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2018 Google Inc. 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. - -import Foundation -import TensorFlowLiteCAPI - -/// An input or output tensor in a TensorFlow Lite graph. -public struct Tensor { - - /// Name of the tensor. - public let name: String - - /// Data type of the tensor. - public let dataType: TensorDataType - - /// Shape of the tensor. - public let shape: TensorShape - - /// Data in the input or output tensor. - public let data: Data - - /// Quantization parameters for the tensor if using a quantized model. - public let quantizationParameters: QuantizationParameters? - - /// Creates a new input or output tensor instance. - /// - /// - Parameters: - /// - name: Name of the tensor. - /// - dataType: Data type of the tensor. - /// - data: Data in the input tensor. - /// - quantizationParameters Quantization parameters for the tensor if using a quantized model. - /// The default is `nil`. - init( - name: String, - dataType: TensorDataType, - shape: TensorShape, - data: Data, - quantizationParameters: QuantizationParameters? = nil - ) { - self.name = name - self.dataType = dataType - self.shape = shape - self.data = data - self.quantizationParameters = quantizationParameters - } -} - -/// Supported TensorFlow Lite tensor data types. -public enum TensorDataType: Equatable { - /// 32-bit single precision floating point tensor data type. - case float32 - /// 8-bit unsigned integer tensor data type. - case uInt8 - /// 16-bit signed integer tensor data type. - case int16 - /// 32-bit signed integer tensor data type. - case int32 - /// 64-bit signed integer tensor data type. - case int64 - /// Boolean tensor data type. - case bool - - /// Creates a new tensor data type from the given `TFL_Type` or `nil` if the data type is - /// unsupported or could not be determined because there was an error. - /// - /// - Parameter type: A data type supported by a tensor. - init?(type: TFL_Type) { - switch type { - case kTfLiteFloat32: - self = .float32 - case kTfLiteUInt8: - self = .uInt8 - case kTfLiteInt16: - self = .int16 - case kTfLiteInt32: - self = .int32 - case kTfLiteInt64: - self = .int64 - case kTfLiteBool: - self = .bool - case kTfLiteNoType: - fallthrough - default: - return nil - } - } -} - -/// The shape of a TensorFlow Lite tensor. -public struct TensorShape { - - /// The number of dimensions of the tensor. - public let rank: Int - - /// Array of dimensions for the tensor. - public let dimensions: [Int] - - /// Array of `Int32` dimensions for the tensor. - var int32Dimensions: [Int32] { return dimensions.map(Int32.init) } - - /// Creates a new tensor shape instance with the given array of dimensions. - /// - /// - Parameters: - /// - dimensions: Dimensions for the tensor. - public init(_ dimensions: [Int]) { - self.rank = dimensions.count - self.dimensions = dimensions - } - - /// Creates a new tensor shape instance with the given elements representing the dimensions. - /// - /// - Parameters: - /// - elements: Dimensions for the tensor. - public init(_ elements: Int...) { - self.init(elements) - } -} - -extension TensorShape: ExpressibleByArrayLiteral { - /// Creates a new tensor shape instance with the given array literal representing the dimensions. - /// - /// - Parameters: - /// - arrayLiteral: Dimensions for the tensor. - public init(arrayLiteral: Int...) { - self.init(arrayLiteral) - } -} diff --git a/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/Configs/TensorFlowLite.tulsigen b/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/Configs/TensorFlowLite.tulsigen deleted file mode 100644 index 4010fab49e..0000000000 --- a/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/Configs/TensorFlowLite.tulsigen +++ /dev/null @@ -1,57 +0,0 @@ -{ - "sourceFilters" : [ - "third_party/tensorflow/lite/experimental/c", - "third_party/tensorflow/lite/experimental/swift", - "third_party/tensorflow/lite/experimental/swift/Sources", - "third_party/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp", - "third_party/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj", - "third_party/tensorflow/lite/experimental/swift/Tests", - ], - "buildTargets" : [ - "//third_party/tensorflow/lite/experimental/swift:TensorFlowLite", - "//third_party/tensorflow/lite/experimental/swift:TensorFlowLiteApp", - "//third_party/tensorflow/lite/experimental/swift:TensorFlowLiteTests", - ], - "projectName" : "TensorFlowLite", - "optionSet" : { - "LaunchActionPreActionScript" : { - "p" : "$(inherited)" - }, - "BazelBuildStartupOptionsRelease" : { - "p" : "$(inherited)" - }, - "BazelBuildOptionsRelease" : { - "p" : "$(inherited)" - }, - "BazelBuildOptionsDebug" : { - "p" : "$(inherited)" - }, - "EnvironmentVariables" : { - "p" : "$(inherited)" - }, - "BuildActionPreActionScript" : { - "p" : "$(inherited)" - }, - "CommandlineArguments" : { - "p" : "$(inherited)" - }, - "TestActionPreActionScript" : { - "p" : "$(inherited)" - }, - "BazelBuildStartupOptionsDebug" : { - "p" : "$(inherited)" - }, - "BuildActionPostActionScript" : { - "p" : "$(inherited)" - }, - "TestActionPostActionScript" : { - "p" : "$(inherited)" - }, - "LaunchActionPostActionScript" : { - "p" : "$(inherited)" - } - }, - "additionalFilePaths" : [ - "third_party/tensorflow/lite/experimental/swift/BUILD" - ] -} diff --git a/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/project.tulsiconf b/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/project.tulsiconf deleted file mode 100644 index 14cff94453..0000000000 --- a/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/project.tulsiconf +++ /dev/null @@ -1,14 +0,0 @@ -{ - "configDefaults" : { - "optionSet" : { - "ProjectPrioritizesSwift" : { - "p" : "YES" - } - } - }, - "projectName" : "TensorFlowLite", - "packages" : [ - "third_party/tensorflow/lite/experimental/swift" - ], - "workspaceRoot" : "../../../../../.." -} diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp.xcodeproj/project.pbxproj b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp.xcodeproj/project.pbxproj deleted file mode 100644 index fbbf9a1de2..0000000000 --- a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp.xcodeproj/project.pbxproj +++ /dev/null @@ -1,345 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 50; - objects = { - -/* Begin PBXBuildFile section */ - 4A7304B421500B8400C90B21 /* Data+TensorFlowLite.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A7304B321500B8300C90B21 /* Data+TensorFlowLite.swift */; }; - 4AA72B732146ED64006C3AEF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AA72B722146ED64006C3AEF /* AppDelegate.swift */; }; - 4AA72B752146ED64006C3AEF /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AA72B742146ED64006C3AEF /* ViewController.swift */; }; - 4AA72B782146ED64006C3AEF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4AA72B762146ED64006C3AEF /* Main.storyboard */; }; - 4AA72B7A2146ED66006C3AEF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4AA72B792146ED66006C3AEF /* Assets.xcassets */; }; - 4AA72B7D2146ED66006C3AEF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4AA72B7B2146ED66006C3AEF /* LaunchScreen.storyboard */; }; - 4ADDE0CE2176600E00FF07A2 /* Array+TensorFlowLite.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ADDE0CD2176600900FF07A2 /* Array+TensorFlowLite.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 4A7304B321500B8300C90B21 /* Data+TensorFlowLite.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+TensorFlowLite.swift"; sourceTree = ""; }; - 4AA72B6F2146ED64006C3AEF /* TensorFlowLiteApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TensorFlowLiteApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 4AA72B722146ED64006C3AEF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 4AA72B742146ED64006C3AEF /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; - 4AA72B772146ED64006C3AEF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 4AA72B792146ED66006C3AEF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 4AA72B7C2146ED66006C3AEF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 4AA72B7E2146ED66006C3AEF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 4ADDE0CD2176600900FF07A2 /* Array+TensorFlowLite.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+TensorFlowLite.swift"; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 4AA72B6C2146ED64006C3AEF /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 4AA72B662146ED64006C3AEF = { - isa = PBXGroup; - children = ( - 4AA72B712146ED64006C3AEF /* TensorFlowLiteApp */, - 4AA72B702146ED64006C3AEF /* Products */, - ); - sourceTree = ""; - }; - 4AA72B702146ED64006C3AEF /* Products */ = { - isa = PBXGroup; - children = ( - 4AA72B6F2146ED64006C3AEF /* TensorFlowLiteApp.app */, - ); - name = Products; - sourceTree = ""; - }; - 4AA72B712146ED64006C3AEF /* TensorFlowLiteApp */ = { - isa = PBXGroup; - children = ( - 4AA72B722146ED64006C3AEF /* AppDelegate.swift */, - 4ADDE0CD2176600900FF07A2 /* Array+TensorFlowLite.swift */, - 4A7304B321500B8300C90B21 /* Data+TensorFlowLite.swift */, - 4AA72B742146ED64006C3AEF /* ViewController.swift */, - 4AA72B762146ED64006C3AEF /* Main.storyboard */, - 4AA72B792146ED66006C3AEF /* Assets.xcassets */, - 4AA72B7B2146ED66006C3AEF /* LaunchScreen.storyboard */, - 4AA72B7E2146ED66006C3AEF /* Info.plist */, - ); - path = TensorFlowLiteApp; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 4AA72B6E2146ED64006C3AEF /* TensorFlowLiteApp */ = { - isa = PBXNativeTarget; - buildConfigurationList = 4AA72B812146ED66006C3AEF /* Build configuration list for PBXNativeTarget "TensorFlowLiteApp" */; - buildPhases = ( - 4AA72B6B2146ED64006C3AEF /* Sources */, - 4AA72B6C2146ED64006C3AEF /* Frameworks */, - 4AA72B6D2146ED64006C3AEF /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = TensorFlowLiteApp; - productName = TensorFlowLiteApp; - productReference = 4AA72B6F2146ED64006C3AEF /* TensorFlowLiteApp.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 4AA72B672146ED64006C3AEF /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0940; - LastUpgradeCheck = 0940; - ORGANIZATIONNAME = Google; - TargetAttributes = { - 4AA72B6E2146ED64006C3AEF = { - CreatedOnToolsVersion = 9.4.1; - }; - }; - }; - buildConfigurationList = 4AA72B6A2146ED64006C3AEF /* Build configuration list for PBXProject "TensorFlowLiteApp" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 4AA72B662146ED64006C3AEF; - productRefGroup = 4AA72B702146ED64006C3AEF /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 4AA72B6E2146ED64006C3AEF /* TensorFlowLiteApp */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 4AA72B6D2146ED64006C3AEF /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4AA72B7D2146ED66006C3AEF /* LaunchScreen.storyboard in Resources */, - 4AA72B7A2146ED66006C3AEF /* Assets.xcassets in Resources */, - 4AA72B782146ED64006C3AEF /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 4AA72B6B2146ED64006C3AEF /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4AA72B732146ED64006C3AEF /* AppDelegate.swift in Sources */, - 4ADDE0CE2176600E00FF07A2 /* Array+TensorFlowLite.swift in Sources */, - 4A7304B421500B8400C90B21 /* Data+TensorFlowLite.swift in Sources */, - 4AA72B752146ED64006C3AEF /* ViewController.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 4AA72B762146ED64006C3AEF /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 4AA72B772146ED64006C3AEF /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 4AA72B7B2146ED66006C3AEF /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 4AA72B7C2146ED66006C3AEF /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 4AA72B7F2146ED66006C3AEF /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.4; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - 4AA72B802146ED66006C3AEF /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.4; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 4AA72B822146ED66006C3AEF /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = TensorFlowLiteApp/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.tensorflow.lite.swift.TensorFlowLite; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 4AA72B832146ED66006C3AEF /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = TensorFlowLiteApp/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.tensorflow.lite.swift.TensorFlowLite; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 4AA72B6A2146ED64006C3AEF /* Build configuration list for PBXProject "TensorFlowLiteApp" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4AA72B7F2146ED66006C3AEF /* Debug */, - 4AA72B802146ED66006C3AEF /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4AA72B812146ED66006C3AEF /* Build configuration list for PBXNativeTarget "TensorFlowLiteApp" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4AA72B822146ED66006C3AEF /* Debug */, - 4AA72B832146ED66006C3AEF /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 4AA72B672146ED64006C3AEF /* Project object */; -} diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/AppDelegate.swift b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/AppDelegate.swift deleted file mode 100644 index ffa90a06ad..0000000000 --- a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/AppDelegate.swift +++ /dev/null @@ -1,24 +0,0 @@ -import UIKit - -@UIApplicationMain - -final class AppDelegate: UIResponder, UIApplicationDelegate { - - /// The main window of the app. - var window: UIWindow? - - func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil - ) -> Bool { - return true - } -} - -// MARK: - Extensions - -#if !swift(>=4.2) -extension UIApplication { - typealias LaunchOptionsKey = UIApplicationLaunchOptionsKey -} -#endif // !swift(>=4.2) diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Array+TensorFlowLite.swift b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Array+TensorFlowLite.swift deleted file mode 100644 index 56df1ce659..0000000000 --- a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Array+TensorFlowLite.swift +++ /dev/null @@ -1,22 +0,0 @@ -import Foundation - -extension Array { - /// Creates a new array from the bytes of the given unsafe data. - /// - /// - Warning: The array's `Element` type must be trivial in that it can be copied bit for bit - /// with no indirection or reference-counting operations; otherwise, copying the raw bytes in - /// the `unsafeData`'s buffer to a new array returns an unsafe copy. - /// - Note: Returns `nil` if `unsafeData.count` is not a multiple of - /// `MemoryLayout.stride`. - /// - Parameter unsafeData: The data containing the bytes to turn into an array. - init?(unsafeData: Data) { - guard unsafeData.count % MemoryLayout.stride == 0 else { return nil } - let elements = unsafeData.withUnsafeBytes { - UnsafeBufferPointer( - start: $0, - count: unsafeData.count / MemoryLayout.stride - ) - } - self.init(elements) - } -} diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index d8db8d65fd..0000000000 --- a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "size" : "20x20", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "20x20", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "3x" - }, - { - "idiom" : "ipad", - "size" : "20x20", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "20x20", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "29x29", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "40x40", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "76x76", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "76x76", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "83.5x83.5", - "scale" : "2x" - }, - { - "idiom" : "ios-marketing", - "size" : "1024x1024", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/Contents.json b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/Contents.json deleted file mode 100644 index da4a164c91..0000000000 --- a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/LaunchScreen.storyboard b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index a07a1321be..0000000000 --- a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/Main.storyboard b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/Main.storyboard deleted file mode 100644 index d0e91d63c4..0000000000 --- a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/Main.storyboard +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Data+TensorFlowLite.swift b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Data+TensorFlowLite.swift deleted file mode 100644 index bc8a70c848..0000000000 --- a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Data+TensorFlowLite.swift +++ /dev/null @@ -1,13 +0,0 @@ -import Foundation - -extension Data { - /// Creates a new buffer by copying the buffer pointer of the given array. - /// - /// - Warning: The given array's element type `T` must be trivial in that it can be copied bit - /// for bit with no indirection or reference-counting operations; otherwise, reinterpreting - /// data from the resulting buffer has undefined behavior. - /// - Parameter array: An array with elements of type `T`. - init(copyingBufferOf array: [T]) { - self = array.withUnsafeBufferPointer(Data.init) - } -} diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Info.plist b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Info.plist deleted file mode 100644 index 3ca3875f04..0000000000 --- a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Info.plist +++ /dev/null @@ -1,46 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleVersion - 0.0.1 - LSRequiresIPhoneOS - - NSCameraUsageDescription - NSCameraUsageDescription - NSPhotoLibraryUsageDescription - Select a photo to detect objects in. - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - - - diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/ViewController.swift b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/ViewController.swift deleted file mode 100644 index 73c74fd19c..0000000000 --- a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/ViewController.swift +++ /dev/null @@ -1,299 +0,0 @@ -import TensorFlowLite -import UIKit - -class ViewController: UIViewController { - - // MARK: - Properties - - /// TensorFlowLite interpreter object for performing inference from a given model. - private var interpreter: Interpreter? - - /// Serial dispatch queue for managing `Interpreter` calls. - private let interpreterQueue = DispatchQueue( - label: Constant.dispatchQueueLabel, - qos: .userInitiated - ) - - /// The currently selected model. - private var currentModel: Model { - guard let currentModel = Model(rawValue: modelControl.selectedSegmentIndex) else { - preconditionFailure("Invalid model for selected segment index.") - } - return currentModel - } - - /// A description of the current model. - private var modelDescription: String { - guard let interpreter = interpreter else { return "" } - let inputCount = interpreter.inputTensorCount - let outputCount = interpreter.outputTensorCount - let inputTensors = (0.. String = { - guard let results = [Float32](unsafeData: outputTensor.data) else { return "No results." } - return resultsText + results.description - } - self.updateResultsText(results()) - } catch let error { - self.updateResultsText( - "Failed to invoke the interpreter with error: \(error.localizedDescription)" - ) - return - } - } - } - - private func invokeAddQuantized() { - interpreterQueue.async { - guard let interpreter = self.interpreter else { - self.updateResultsText(Constant.nilInterpreterErrorMessage) - return - } - do { - try interpreter.resizeInput(at: 0, to: [2]) - try interpreter.allocateTensors() - let input: [UInt8] = [1, 3] - let resultsText = self.modelDescription + "\n\n" + - "Performing 2 add operations on quantized input \(input.description) equals: " - self.updateResultsText(resultsText) - let data = Data(input) - try interpreter.copy(data, toInputAt: 0) - try interpreter.invoke() - let outputTensor = try interpreter.output(at: 0) - let results: () -> String = { - guard let quantizationParameters = outputTensor.quantizationParameters else { - return "No results." - } - let quantizedResults = [UInt8](outputTensor.data) - let dequantizedResults = quantizedResults.map { - quantizationParameters.scale * Float(Int($0) - quantizationParameters.zeroPoint) - } - return resultsText + quantizedResults.description + - ", dequantized results: " + dequantizedResults.description - } - self.updateResultsText(results()) - } catch let error { - self.updateResultsText( - "Failed to invoke the interpreter with error: \(error.localizedDescription)" - ) - return - } - } - } - - private func invokeMultiAdd() { - interpreterQueue.async { - guard let interpreter = self.interpreter else { - self.updateResultsText(Constant.nilInterpreterErrorMessage) - return - } - do { - let shape = TensorShape(2) - try (0.. [Float32] in - let input = [Float32(index + 1), Float32(index + 2)] - let data = Data(copyingBufferOf: input) - try interpreter.copy(data, toInputAt: index) - return input - } - let resultsText = self.modelDescription + "\n\n" + - "Performing 3 add operations on inputs \(inputs.description) equals: " - self.updateResultsText(resultsText) - try interpreter.invoke() - let results = try (0.. [Float32] in - let tensor = try interpreter.output(at: index) - return [Float32](unsafeData: tensor.data) ?? [] - } - self.updateResultsText(resultsText + results.description) - } catch let error { - self.updateResultsText( - "Failed to invoke the interpreter with error: \(error.localizedDescription)" - ) - return - } - } - } - - private func updateResultsText(_ text: String? = nil) { - safeDispatchOnMain { self.resultsTextView.text = text } - } -} - -// MARK: - Constants - -private enum Constant { - static let dispatchQueueLabel = "TensorFlowLiteInterpreterQueue" - static let nilInterpreterErrorMessage = - "Failed to invoke the interpreter because the interpreter was nil." -} - -/// Models that can be loaded by the TensorFlow Lite `Interpreter`. -private enum Model: Int, CustomStringConvertible { - /// A float model that performs two add operations on one input tensor and returns the result in - /// one output tensor. - case add = 0 - /// A quantized model that performs two add operations on one input tensor and returns the result - /// in one output tensor. - case addQuantized = 1 - /// A float model that performs three add operations on four input tensors and returns the results - /// in 2 output tensors. - case multiAdd = 2 - - var fileInfo: (name: String, extension: String) { - switch self { - case .add: - return Add.fileInfo - case .addQuantized: - return AddQuantized.fileInfo - case .multiAdd: - return MultiAdd.fileInfo - } - } - - // MARK: - CustomStringConvertible - - var description: String { - switch self { - case .add: - return Add.name - case .addQuantized: - return AddQuantized.name - case .multiAdd: - return MultiAdd.name - } - } -} - -/// Values for the `Add` model. -private enum Add { - static let name = "Add" - static let fileInfo = (name: "add", extension: "bin") -} - -/// Values for the `AddQuantized` model. -private enum AddQuantized { - static let name = "AddQuantized" - static let fileInfo = (name: "add_quantized", extension: "bin") -} - -/// Values for the `MultiAdd` model. -private enum MultiAdd { - static let name = "MultiAdd" - static let fileInfo = (name: "multi_add", extension: "bin") -} - -// MARK: - Fileprivate - -/// Safely dispatches the given block on the main queue. If the current thread is `main`, the block -/// is executed synchronously; otherwise, the block is executed asynchronously on the main thread. -fileprivate func safeDispatchOnMain(_ block: @escaping () -> Void) { - if Thread.isMainThread { block(); return } - DispatchQueue.main.async { block() } -} diff --git a/tensorflow/lite/experimental/swift/Tests/InterpreterOptionsTests.swift b/tensorflow/lite/experimental/swift/Tests/InterpreterOptionsTests.swift deleted file mode 100644 index 54b4f59b28..0000000000 --- a/tensorflow/lite/experimental/swift/Tests/InterpreterOptionsTests.swift +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2018 Google Inc. 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. - -@testable import TensorFlowLite -import XCTest - -class InterpreterOptionsTests: XCTestCase { - - func testInterpreterOptions_InitWithDefaultValues() { - let options = InterpreterOptions() - XCTAssertNil(options.threadCount) - XCTAssertFalse(options.isErrorLoggingEnabled) - } - - func testInterpreterOptions_InitWithCustomValues() { - var options = InterpreterOptions() - options.threadCount = 2 - XCTAssertEqual(options.threadCount, 2) - options.isErrorLoggingEnabled = true - XCTAssertTrue(options.isErrorLoggingEnabled) - } - - func testInterpreterOptions_Equatable() { - var options1 = InterpreterOptions() - var options2 = InterpreterOptions() - XCTAssertEqual(options1, options2) - - options1.threadCount = 2 - options2.threadCount = 2 - XCTAssertEqual(options1, options2) - - options2.threadCount = 3 - XCTAssertNotEqual(options1, options2) - options2.threadCount = 2 - - options1.isErrorLoggingEnabled = true - options2.isErrorLoggingEnabled = true - XCTAssertEqual(options1, options2) - - options2.isErrorLoggingEnabled = false - XCTAssertNotEqual(options1, options2) - } -} diff --git a/tensorflow/lite/experimental/swift/Tests/InterpreterTests.swift b/tensorflow/lite/experimental/swift/Tests/InterpreterTests.swift deleted file mode 100644 index e98da5f951..0000000000 --- a/tensorflow/lite/experimental/swift/Tests/InterpreterTests.swift +++ /dev/null @@ -1,315 +0,0 @@ -// Copyright 2018 Google Inc. 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. - -@testable import TensorFlowLite -import XCTest - -class InterpreterTests: XCTestCase { - - var interpreter: Interpreter! - - override func setUp() { - super.setUp() - - interpreter = try! Interpreter(modelPath: AddModel.path) - } - - override func tearDown() { - interpreter = nil - - super.tearDown() - } - - func testInterpreter_InitWithModelPath() { - XCTAssertNoThrow(try Interpreter(modelPath: AddModel.path)) - } - - func testInterpreter_Init_ThrowsFailedToLoadModel() { - XCTAssertThrowsError(try Interpreter(modelPath: "/invalid/path")) { error in - self.assertEqualErrors(actual: error, expected: .failedToLoadModel) - } - } - - func testInterpreter_InitWithModelPathAndOptions() { - var options = InterpreterOptions() - options.threadCount = 2 - XCTAssertNoThrow(try Interpreter(modelPath: AddModel.path, options: options)) - } - - func testInterpreter_InputTensorCount() { - XCTAssertEqual(interpreter.inputTensorCount, AddModel.inputTensorCount) - } - - func testInterpreter_OutputTensorCount() { - XCTAssertEqual(interpreter.outputTensorCount, AddModel.outputTensorCount) - } - - func testInterpreter_Invoke() throws { - try interpreter.allocateTensors() - XCTAssertNoThrow(try interpreter.invoke()) - } - - func testInterpreter_Invoke_ThrowsAllocateTensorsRequired_ModelNotReady() { - XCTAssertThrowsError(try interpreter.invoke()) { error in - self.assertEqualErrors(actual: error, expected: .allocateTensorsRequired) - } - } - - func testInterpreter_InputTensorAtIndex() throws { - try setUpAddModelInputTensor() - let inputTensor = try interpreter.input(at: AddModel.validIndex) - XCTAssertEqual(inputTensor, AddModel.inputTensor) - } - - func testInterpreter_InputTensorAtIndex_QuantizedModel() throws { - interpreter = try Interpreter(modelPath: AddQuantizedModel.path) - try setUpAddQuantizedModelInputTensor() - let inputTensor = try interpreter.input(at: AddQuantizedModel.inputOutputIndex) - XCTAssertEqual(inputTensor, AddQuantizedModel.inputTensor) - } - - func testInterpreter_InputTensorAtIndex_ThrowsInvalidIndex() throws { - try interpreter.allocateTensors() - XCTAssertThrowsError(try interpreter.input(at: AddModel.invalidIndex)) { error in - let maxIndex = AddModel.inputTensorCount - 1 - self.assertEqualErrors( - actual: error, - expected: .invalidTensorIndex(index: AddModel.invalidIndex, maxIndex: maxIndex) - ) - } - } - - func testInterpreter_InputTensorAtIndex_ThrowsAllocateTensorsRequired() { - XCTAssertThrowsError(try interpreter.input(at: AddModel.validIndex)) { error in - self.assertEqualErrors(actual: error, expected: .allocateTensorsRequired) - } - } - - func testInterpreter_OutputTensorAtIndex() throws { - try setUpAddModelInputTensor() - try interpreter.invoke() - let outputTensor = try interpreter.output(at: AddModel.validIndex) - XCTAssertEqual(outputTensor, AddModel.outputTensor) - let expectedResults = [Float32](unsafeData: outputTensor.data) - XCTAssertEqual(expectedResults, AddModel.results) - } - - func testInterpreter_OutputTensorAtIndex_QuantizedModel() throws { - interpreter = try Interpreter(modelPath: AddQuantizedModel.path) - try setUpAddQuantizedModelInputTensor() - try interpreter.invoke() - let outputTensor = try interpreter.output(at: AddQuantizedModel.inputOutputIndex) - XCTAssertEqual(outputTensor, AddQuantizedModel.outputTensor) - let expectedResults = [UInt8](outputTensor.data) - XCTAssertEqual(expectedResults, AddQuantizedModel.results) - } - - func testInterpreter_OutputTensorAtIndex_ThrowsInvalidIndex() throws { - try interpreter.allocateTensors() - try interpreter.invoke() - XCTAssertThrowsError(try interpreter.output(at: AddModel.invalidIndex)) { error in - let maxIndex = AddModel.outputTensorCount - 1 - self.assertEqualErrors( - actual: error, - expected: .invalidTensorIndex(index: AddModel.invalidIndex, maxIndex: maxIndex) - ) - } - } - - func testInterpreter_OutputTensorAtIndex_ThrowsInvokeInterpreterRequired() { - XCTAssertThrowsError(try interpreter.output(at: AddModel.validIndex)) { error in - self.assertEqualErrors(actual: error, expected: .invokeInterpreterRequired) - } - } - - func testInterpreter_ResizeInputTensorAtIndexToShape() { - XCTAssertNoThrow(try interpreter.resizeInput(at: AddModel.validIndex, to: [2, 2, 3])) - XCTAssertNoThrow(try interpreter.allocateTensors()) - } - - func testInterpreter_ResizeInputTensorAtIndexToShape_ThrowsInvalidIndex() { - XCTAssertThrowsError(try interpreter.resizeInput( - at: AddModel.invalidIndex, - to: [2, 2, 3] - )) { error in - let maxIndex = AddModel.inputTensorCount - 1 - self.assertEqualErrors( - actual: error, - expected: .invalidTensorIndex(index: AddModel.invalidIndex, maxIndex: maxIndex) - ) - } - } - - func testInterpreter_CopyDataToInputTensorAtIndex() throws { - try interpreter.resizeInput(at: AddModel.validIndex, to: AddModel.shape) - try interpreter.allocateTensors() - let inputTensor = try interpreter.copy(AddModel.inputData, toInputAt: AddModel.validIndex) - XCTAssertEqual(inputTensor.data, AddModel.inputData) - } - - func testInterpreter_CopyDataToInputTensorAtIndex_ThrowsInvalidIndex() { - XCTAssertThrowsError(try interpreter.copy( - AddModel.inputData, - toInputAt: AddModel.invalidIndex - )) { error in - let maxIndex = AddModel.inputTensorCount - 1 - self.assertEqualErrors( - actual: error, - expected: .invalidTensorIndex(index: AddModel.invalidIndex, maxIndex: maxIndex) - ) - } - } - - func testInterpreter_CopyDataToInputTensorAtIndex_ThrowsInvalidDataCount() throws { - try interpreter.resizeInput(at: AddModel.validIndex, to: AddModel.shape) - try interpreter.allocateTensors() - let invalidData = Data(count: AddModel.dataCount - 1) - XCTAssertThrowsError(try interpreter.copy( - invalidData, - toInputAt: AddModel.validIndex - )) { error in - self.assertEqualErrors( - actual: error, - expected: .invalidTensorDataCount(provided: invalidData.count, required: AddModel.dataCount) - ) - } - } - - func testInterpreter_AllocateTensors() { - XCTAssertNoThrow(try interpreter.allocateTensors()) - } - - // MARK: - Private - - private func setUpAddModelInputTensor() throws { - precondition(interpreter != nil) - try interpreter.resizeInput(at: AddModel.validIndex, to: AddModel.shape) - try interpreter.allocateTensors() - try interpreter.copy(AddModel.inputData, toInputAt: AddModel.validIndex) - } - - private func setUpAddQuantizedModelInputTensor() throws { - precondition(interpreter != nil) - try interpreter.resizeInput(at: AddQuantizedModel.inputOutputIndex, to: AddQuantizedModel.shape) - try interpreter.allocateTensors() - try interpreter.copy(AddQuantizedModel.inputData, toInputAt: AddQuantizedModel.inputOutputIndex) - } - - private func assertEqualErrors(actual: Error, expected: InterpreterError) { - guard let actual = actual as? InterpreterError else { - XCTFail("Actual error should be of type InterpreterError.") - return - } - XCTAssertEqual(actual, expected) - } -} - -// MARK: - Constants - -/// Values for the `add.bin` model. -private enum AddModel { - static let info = (name: "add", extension: "bin") - static let inputTensorCount = 1 - static let outputTensorCount = 1 - static let invalidIndex = 1 - static let validIndex = 0 - static let shape: TensorShape = [2] - static let dataCount = inputData.count - static let inputData = Data(copyingBufferOf: [Float32(1.0), Float32(3.0)]) - static let outputData = Data(copyingBufferOf: [Float32(3.0), Float32(9.0)]) - static let results = [Float32(3.0), Float32(9.0)] - - static let inputTensor = Tensor( - name: "input", - dataType: .float32, - shape: shape, - data: inputData - ) - static let outputTensor = Tensor( - name: "output", - dataType: .float32, - shape: shape, - data: outputData - ) - - static var path: String = { - let bundle = Bundle(for: InterpreterTests.self) - guard let path = bundle.path(forResource: info.name, ofType: info.extension) else { return "" } - return path - }() -} - -/// Values for the `add_quantized.bin` model. -private enum AddQuantizedModel { - static let info = (name: "add_quantized", extension: "bin") - static let inputOutputIndex = 0 - static let shape: TensorShape = [2] - static let inputData = Data([1, 3]) - static let outputData = Data([3, 9]) - static let quantizationParameters = QuantizationParameters(scale: 0.003922, zeroPoint: 0) - static let results: [UInt8] = [3, 9] - - static let inputTensor = Tensor( - name: "input", - dataType: .uInt8, - shape: shape, - data: inputData, - quantizationParameters: quantizationParameters - ) - static let outputTensor = Tensor( - name: "output", - dataType: .uInt8, - shape: shape, - data: outputData, - quantizationParameters: quantizationParameters - ) - - static var path: String = { - let bundle = Bundle(for: InterpreterTests.self) - guard let path = bundle.path(forResource: info.name, ofType: info.extension) else { return "" } - return path - }() -} - -// MARK: - Extensions - -extension Array { - /// Creates a new array from the bytes of the given unsafe data. - /// - /// - Note: Returns `nil` if `unsafeData.count` is not a multiple of - /// `MemoryLayout.stride`. - /// - Parameter unsafeData: The data containing the bytes to turn into an array. - init?(unsafeData: Data) { - guard unsafeData.count % MemoryLayout.stride == 0 else { return nil } - let elements = unsafeData.withUnsafeBytes { - UnsafeBufferPointer( - start: $0, - count: unsafeData.count / MemoryLayout.stride - ) - } - self.init(elements) - } -} - -extension Data { - /// Creates a new buffer by copying the buffer pointer of the given array. - /// - /// - Warning: The given array's element type `T` must be trivial in that it can be copied bit - /// for bit with no indirection or reference-counting operations; otherwise, reinterpreting - /// data from the resulting buffer has undefined behavior. - /// - Parameter array: An array with elements of type `T`. - init(copyingBufferOf array: [T]) { - self = array.withUnsafeBufferPointer(Data.init) - } -} diff --git a/tensorflow/lite/experimental/swift/Tests/ModelTests.swift b/tensorflow/lite/experimental/swift/Tests/ModelTests.swift deleted file mode 100644 index 025db18906..0000000000 --- a/tensorflow/lite/experimental/swift/Tests/ModelTests.swift +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2018 Google Inc. 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. - -@testable import TensorFlowLite -import XCTest - -class ModelTests: XCTestCase { - - var modelPath: String! - - override func setUp() { - super.setUp() - - let bundle = Bundle(for: type(of: self)) - guard let modelPath = bundle.path( - forResource: Constant.modelInfo.name, - ofType: Constant.modelInfo.extension) - else { - XCTFail("Failed to get the model file path.") - return - } - self.modelPath = modelPath - } - - override func tearDown() { - modelPath = nil - - super.tearDown() - } - - func testModel_InitWithFilePath() { - XCTAssertNotNil(Model(filePath: modelPath)) - } - - func testModel_InitWithEmptyFilePath_FailsInitialization() { - XCTAssertNil(Model(filePath: "")) - } - - func testModel_InitWithInvalidFilePath_FailsInitialization() { - XCTAssertNil(Model(filePath: "invalid/path")) - } -} - -// MARK: - Constants - -private enum Constant { - static let modelInfo = (name: "add", extension: "bin") -} diff --git a/tensorflow/lite/experimental/swift/Tests/QuantizationParametersTests.swift b/tensorflow/lite/experimental/swift/Tests/QuantizationParametersTests.swift deleted file mode 100644 index 65648c2698..0000000000 --- a/tensorflow/lite/experimental/swift/Tests/QuantizationParametersTests.swift +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2018 Google Inc. 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. - -@testable import TensorFlowLite -import XCTest - -class QuantizationParametersTests: XCTestCase { - - func testQuantizationParameters_InitWithCustomValues() { - let parameters = QuantizationParameters(scale: 0.5, zeroPoint: 1) - XCTAssertEqual(parameters.scale, 0.5) - XCTAssertEqual(parameters.zeroPoint, 1) - } - - func testQuantizationParameters_Equatable() { - let parameters1 = QuantizationParameters(scale: 0.5, zeroPoint: 1) - let parameters2 = QuantizationParameters(scale: 0.5, zeroPoint: 1) - XCTAssertEqual(parameters1, parameters2) - - let parameters3 = QuantizationParameters(scale: 0.4, zeroPoint: 1) - XCTAssertNotEqual(parameters1, parameters3) - XCTAssertNotEqual(parameters2, parameters3) - } -} - -// MARK: - Extensions - -extension QuantizationParameters: Equatable { - public static func == (lhs: QuantizationParameters, rhs: QuantizationParameters) -> Bool { - return lhs.scale == rhs.scale && lhs.zeroPoint == rhs.zeroPoint - } -} diff --git a/tensorflow/lite/experimental/swift/Tests/TensorTests.swift b/tensorflow/lite/experimental/swift/Tests/TensorTests.swift deleted file mode 100644 index 4540043a16..0000000000 --- a/tensorflow/lite/experimental/swift/Tests/TensorTests.swift +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2018 Google Inc. 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. - -@testable import TensorFlowLite -import XCTest - -class TensorTests: XCTestCase { - - // MARK: - Tensor - - func testTensor_Init() { - let name = "InputTensor" - let dataType: TensorDataType = .uInt8 - let shape = TensorShape(Constant.dimensions) - guard let data = name.data(using: .utf8) else { XCTFail("Data should not be nil."); return } - let quantizationParameters = QuantizationParameters(scale: 0.5, zeroPoint: 1) - let inputTensor = Tensor( - name: name, - dataType: dataType, - shape: shape, - data: data, - quantizationParameters: quantizationParameters - ) - XCTAssertEqual(inputTensor.name, name) - XCTAssertEqual(inputTensor.dataType, dataType) - XCTAssertEqual(inputTensor.shape, shape) - XCTAssertEqual(inputTensor.data, data) - XCTAssertEqual(inputTensor.quantizationParameters, quantizationParameters) - } - - // MARK: - TensorShape - - func testTensorShape_InitWithArray() { - let shape = TensorShape(Constant.dimensions) - XCTAssertEqual(shape.rank, Constant.dimensions.count) - XCTAssertEqual(shape.dimensions, Constant.dimensions) - } - - func testTensorShape_InitWithElements() { - let shape = TensorShape(2, 2, 3) - XCTAssertEqual(shape.rank, Constant.dimensions.count) - XCTAssertEqual(shape.dimensions, Constant.dimensions) - } - - func testTensorShape_InitWithArrayLiteral() { - let shape: TensorShape = [2, 2, 3] - XCTAssertEqual(shape.rank, Constant.dimensions.count) - XCTAssertEqual(shape.dimensions, Constant.dimensions) - } -} - -// MARK: - Constants - -private enum Constant { - /// Array of 2 arrays of 2 arrays of 3 numbers: [[[1, 1, 1], [2, 2, 2]], [[3, 3, 3], [4, 4, 4]]]. - static let dimensions = [2, 2, 3] -} - -// MARK: - Extensions - -extension TensorShape: Equatable { - public static func == (lhs: TensorShape, rhs: TensorShape) -> Bool { - return lhs.rank == rhs.rank && lhs.dimensions == rhs.dimensions - } -} - -extension Tensor: Equatable { - public static func == (lhs: Tensor, rhs: Tensor) -> Bool { - return lhs.name == rhs.name && lhs.dataType == rhs.dataType && lhs.shape == rhs.shape && - lhs.data == rhs.data && lhs.quantizationParameters == rhs.quantizationParameters - } -} diff --git a/tensorflow/tools/pip_package/pip_smoke_test.py b/tensorflow/tools/pip_package/pip_smoke_test.py index 952c71c615..51d010c9e1 100644 --- a/tensorflow/tools/pip_package/pip_smoke_test.py +++ b/tensorflow/tools/pip_package/pip_smoke_test.py @@ -30,19 +30,14 @@ os.chdir(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../.."))) PIP_PACKAGE_QUERY_EXPRESSION = ( "deps(//tensorflow/tools/pip_package:build_pip_package)") -# List of file paths containing BUILD files that should not be included for the -# pip smoke test. -BUILD_BLACKLIST = [ - "tensorflow/lite/examples/android", - "tensorflow/lite/experimental/swift", -] def GetBuild(dir_base): """Get the list of BUILD file all targets recursively startind at dir_base.""" items = [] for root, _, files in os.walk(dir_base): for name in files: - if (name == "BUILD" and root not in BUILD_BLACKLIST): + if (name == "BUILD" and + root.find("tensorflow/lite/examples/android") == -1): items.append("//" + root + ":all") return items @@ -72,9 +67,9 @@ def BuildPyTestDependencies(): PYTHON_TARGETS, PY_TEST_QUERY_EXPRESSION = BuildPyTestDependencies() +# Hard-coded blacklist of files if not included in pip package # TODO(amitpatankar): Clean up blacklist. -# List of dependencies that should not included in the pip package. -DEPENDENCY_BLACKLIST = [ +BLACKLIST = [ "//tensorflow/python:extra_py_tests_deps", "//tensorflow/cc/saved_model:saved_model_half_plus_two", "//tensorflow:no_tensorflow_py_deps", @@ -87,7 +82,9 @@ DEPENDENCY_BLACKLIST = [ "//tensorflow/core/kernels/cloud:bigquery_reader_ops", "//tensorflow/python/feature_column:vocabulary_testdata", "//tensorflow/python:framework/test_file_system.so", - # lite + # contrib + "//tensorflow/contrib/session_bundle:session_bundle_half_plus_two", + "//tensorflow/contrib/keras:testing_utils", "//tensorflow/lite/experimental/examples/lstm:tflite_lstm", "//tensorflow/lite/experimental/examples/lstm:tflite_lstm.py", "//tensorflow/lite/experimental/examples/lstm:unidirectional_sequence_lstm_test", # pylint:disable=line-too-long @@ -96,9 +93,6 @@ DEPENDENCY_BLACKLIST = [ "//tensorflow/lite/python:interpreter_test", "//tensorflow/lite/python:interpreter.py", "//tensorflow/lite/python:interpreter_test.py", - # contrib - "//tensorflow/contrib/session_bundle:session_bundle_half_plus_two", - "//tensorflow/contrib/keras:testing_utils", "//tensorflow/contrib/ffmpeg:test_data", "//tensorflow/contrib/fused_conv:fused_conv2d_bias_activation_op_test_base", "//tensorflow/contrib/hadoop:test_data", @@ -155,8 +149,8 @@ def main(): # File extensions and endings to ignore ignore_extensions = ["_test", "_test.py", "_test_gpu", "_test_gpu.py"] - ignored_files_count = 0 - blacklisted_dependencies_count = len(DEPENDENCY_BLACKLIST) + ignored_files = 0 + blacklisted_files = len(BLACKLIST) # Compare dependencies for dependency in tf_py_test_dependencies_list: if dependency and dependency.startswith("//tensorflow"): @@ -164,16 +158,16 @@ def main(): # Ignore extensions if any(dependency.endswith(ext) for ext in ignore_extensions): ignore = True - ignored_files_count += 1 + ignored_files += 1 - # Check if the dependency is in the pip package, the dependency blacklist, - # or should be ignored because of its file extension. + # Check if the dependency is in the pip package, the blacklist, or + # should be ignored because of its file extension if not (ignore or dependency in pip_package_dependencies_list or - dependency in DEPENDENCY_BLACKLIST): + dependency in BLACKLIST): missing_dependencies.append(dependency) - print("Ignored files count: %d" % ignored_files_count) - print("Blacklisted dependencies count: %d" % blacklisted_dependencies_count) + print("Ignored files: %d" % ignored_files) + print("Blacklisted files: %d" % blacklisted_files) if missing_dependencies: print("Missing the following dependencies from pip_packages:") for missing_dependency in missing_dependencies: -- GitLab From 2e55da567448374f9f7e2498c1e338d07baf812f Mon Sep 17 00:00:00 2001 From: Andy Ly Date: Thu, 3 Jan 2019 18:15:14 -0800 Subject: [PATCH 328/622] [Grappler] MutableGraphView dedup control dependencies on init and on mutations. Update max regular output port on mutations. PiperOrigin-RevId: 227783394 --- tensorflow/core/grappler/graph_view.h | 5 +- .../core/grappler/mutable_graph_view.cc | 275 ++++++++---- tensorflow/core/grappler/mutable_graph_view.h | 72 ++-- .../core/grappler/mutable_graph_view_test.cc | 395 ++++++++++++++---- .../optimizers/data/graph_test_utils.cc | 4 +- .../optimizers/data/latency_all_edges_test.cc | 4 +- 6 files changed, 552 insertions(+), 203 deletions(-) diff --git a/tensorflow/core/grappler/graph_view.h b/tensorflow/core/grappler/graph_view.h index 16156d0f20..a17d17524a 100644 --- a/tensorflow/core/grappler/graph_view.h +++ b/tensorflow/core/grappler/graph_view.h @@ -172,9 +172,8 @@ class GraphViewInternal { if (fanin.index() < -1) { return false; } - string fanin_string = TensorIdToString(fanin); - for (int i = 0; i < node.input_size(); ++i) { - if (node.input(i) == fanin_string) { + for (const string& input : node.input()) { + if (ParseTensorName(input) == fanin) { return true; } } diff --git a/tensorflow/core/grappler/mutable_graph_view.cc b/tensorflow/core/grappler/mutable_graph_view.cc index ca4d5255c0..5df8bd8113 100644 --- a/tensorflow/core/grappler/mutable_graph_view.cc +++ b/tensorflow/core/grappler/mutable_graph_view.cc @@ -39,8 +39,103 @@ bool IsTensorIdPortValid(const TensorId& tensor_id) { return tensor_id.index() >= Graph::kControlSlot; } +// Determines if node is an Identity where it's first regular input is a Switch +// node. +bool IsIdentityConsumingSwitch(const MutableGraphView& graph, + const NodeDef& node) { + if ((IsIdentity(node) || IsIdentityNSingleInput(node)) && + node.input_size() > 0) { + TensorId tensor_id = ParseTensorName(node.input(0)); + if (tensor_id.index() == Graph::kControlSlot) return false; + NodeDef* input_node = graph.GetNode(tensor_id.node()); + return IsSwitch(*input_node); + } + return false; +} + +// Determines if node input can be deduped by regular inputs when used as a +// control dependency. Specifically, if a node is an Identity that leads to a +// Switch node, when used as a control dependency, that control dependency +// should not be deduped even though the same node is used as a regular input. +bool CanDedupControlWithRegularInput(const MutableGraphView& graph, + const NodeDef& control_node) { + return !IsIdentityConsumingSwitch(graph, control_node); +} + +// Determines if node input can be deduped by regular inputs when used as a +// control dependency. Specifically, if a node is an Identity that leads to a +// Switch node, when used as a control dependency, that control dependency +// should not be deduped even though the same node is used as a regular input. +bool CanDedupControlWithRegularInput(const MutableGraphView& graph, + absl::string_view control_node_name) { + NodeDef* control_node = graph.GetNode(control_node_name); + return CanDedupControlWithRegularInput(graph, *control_node); +} + } // namespace +void MutableGraphView::AddAndDedupFanouts(NodeDef* node) { + absl::flat_hash_set fanins; + absl::flat_hash_set controlling_fanins; + int pos = 0; + const int last_idx = node->input_size() - 1; + int last_pos = last_idx; + while (pos <= last_pos) { + TensorId tensor_id = ParseTensorName(node->input(pos)); + absl::string_view input_node_name = tensor_id.node(); + bool is_control_input = IsControlInput(tensor_id); + bool can_dedup_control_with_regular_input = + CanDedupControlWithRegularInput(*this, input_node_name); + bool can_dedup_control = + is_control_input && (can_dedup_control_with_regular_input || + (!can_dedup_control_with_regular_input && + controlling_fanins.contains(input_node_name))); + if (!gtl::InsertIfNotPresent(&fanins, input_node_name) && + can_dedup_control) { + node->mutable_input()->SwapElements(pos, last_pos--); + } else { + OutputPort output(nodes()[input_node_name], tensor_id.index()); + + if (is_control_input) { + fanouts()[output].emplace(node, Graph::kControlSlot); + } else { + max_regular_output_port()[output.node] = + std::max(max_regular_output_port()[output.node], output.port_id); + fanouts()[output].emplace(node, pos); + } + ++pos; + } + if (is_control_input) { + controlling_fanins.insert(input_node_name); + } + } + + if (last_pos < last_idx) { + node->mutable_input()->DeleteSubrange(last_pos + 1, last_idx - last_pos); + } +} + +void MutableGraphView::UpdateMaxRegularOutputPortForRemovedFanin( + const OutputPort& fanin, + const absl::flat_hash_set& fanin_fanouts) { + int max_port = max_regular_output_port()[fanin.node]; + if (!fanin_fanouts.empty() || max_port != fanin.port_id) { + return; + } + bool updated_max_port = false; + for (int i = fanin.port_id - 1; i >= 0; --i) { + OutputPort fanin_port(fanin.node, i); + if (!fanouts()[fanin_port].empty()) { + max_regular_output_port()[fanin.node] = i; + updated_max_port = true; + break; + } + } + if (!updated_max_port) { + max_regular_output_port().erase(fanin.node); + } +} + const absl::flat_hash_set& MutableGraphView::GetFanout(const GraphView::OutputPort& port) const { return GetFanout(MutableGraphView::OutputPort(const_cast(port.node), @@ -65,16 +160,16 @@ NodeDef* MutableGraphView::AddNode(NodeDef&& node) { AddUniqueNodeOrDie(node_in_graph); - AddFanouts(node_in_graph); + AddAndDedupFanouts(node_in_graph); return node_in_graph; } -void MutableGraphView::UpdateFanouts(absl::string_view from_node, +bool MutableGraphView::UpdateFanouts(absl::string_view from_node, absl::string_view to_node) { NodeDef* from_node_ptr = GetNode(from_node); NodeDef* to_node_ptr = GetNode(to_node); if (from_node_ptr && to_node_ptr) { - UpdateFanouts(from_node_ptr, to_node_ptr); + return UpdateFanoutsInternal(from_node_ptr, to_node_ptr); } else if (!from_node_ptr) { LOG(WARNING) << absl::Substitute( "Can't update fanouts from '$0' to '$1', from node was not found.", @@ -84,9 +179,11 @@ void MutableGraphView::UpdateFanouts(absl::string_view from_node, "Can't update fanouts from '$0' to '$1', to node was not found.", from_node, to_node); } + return false; } -void MutableGraphView::UpdateFanouts(NodeDef* from_node, NodeDef* to_node) { +bool MutableGraphView::UpdateFanoutsInternal(NodeDef* from_node, + NodeDef* to_node) { VLOG(2) << absl::Substitute("Update fanouts from '$0' to '$1'.", from_node->name(), to_node->name()); @@ -112,6 +209,7 @@ void MutableGraphView::UpdateFanouts(NodeDef* from_node, NodeDef* to_node) { // input to some other node. int keep_max_regular_output_port = -1; + bool modified = false; for (const Edge& edge : regular_edges) { const OutputPort output_port = edge.src; const InputPort input_port = edge.dst; @@ -120,7 +218,7 @@ void MutableGraphView::UpdateFanouts(NodeDef* from_node, NodeDef* to_node) { // AddAndUpdateFanoutsWithoutSelfLoops test for an example). if (input_port.node == to_node) { keep_max_regular_output_port = - std::max(keep_max_regular_output_port, input_port.port_id); + std::max(keep_max_regular_output_port, output_port.port_id); continue; } @@ -135,6 +233,11 @@ void MutableGraphView::UpdateFanouts(NodeDef* from_node, NodeDef* to_node) { remove_edge(output_port, input_port); // Add an edge between the `to_node` and new fanout node. add_edge(OutputPort(to_node, output_port.port_id), input_port); + // Dedup control dependency. + if (CanDedupControlWithRegularInput(*this, *to_node)) { + RemoveControllingFaninInternal(input_port.node, to_node); + } + modified = true; } // For the control fanouts we do not know the input index in a NodeDef, @@ -142,7 +245,6 @@ void MutableGraphView::UpdateFanouts(NodeDef* from_node, NodeDef* to_node) { auto control_fanouts = GetFanout(GraphView::OutputPort(from_node, Graph::kControlSlot)); - if (control_fanouts.empty()) return; const string from_control_input = absl::StrCat("^", from_node->name()); const string to_control_input = absl::StrCat("^", to_node->name()); @@ -151,20 +253,9 @@ void MutableGraphView::UpdateFanouts(NodeDef* from_node, NodeDef* to_node) { // Node can't be control dependency of itself. if (control_port.node == to_node) continue; - // Find and update input corresponding to control dependency. NodeDef* node = control_port.node; - for (int i = node->input_size() - 1; i >= 0; --i) { - const string& input = node->input(i); - if (!IsControlInput(input)) break; // we reached regular inputs - if (input == from_control_input) { - node->set_input(i, to_control_input); - } - } - - // Remove old edge between the `from_node` and the fanout node. - remove_edge(OutputPort(from_node, Graph::kControlSlot), control_port); - // Add an edge between the `to_node` and new fanout node. - add_edge(OutputPort(to_node, Graph::kControlSlot), control_port); + modified |= RemoveControllingFaninInternal(node, from_node); + modified |= AddFaninInternal(node, {to_node, Graph::kControlSlot}); } // Because we update all regular fanouts of `from_node`, we can just copy @@ -177,21 +268,33 @@ void MutableGraphView::UpdateFanouts(NodeDef* from_node, NodeDef* to_node) { } else { max_regular_output_port().erase(from_node); } + + return modified; } bool MutableGraphView::AddFaninInternal(NodeDef* node, const OutputPort& fanin) { int num_non_controlling_fanins = NumFanins(*node, /*include_controlling_nodes=*/false); + bool input_is_control = fanin.port_id == Graph::kControlSlot; + bool can_dedup_control_with_regular_input = + CanDedupControlWithRegularInput(*this, *fanin.node); + // Don't add duplicate control dependencies. + if (input_is_control) { + const int start = + can_dedup_control_with_regular_input ? 0 : num_non_controlling_fanins; + for (int i = start; i < node->input_size(); ++i) { + if (ParseTensorName(node->input(i)).node() == fanin.node->name()) { + return false; + } + } + } + InputPort input; input.node = node; - input.port_id = fanin.port_id == Graph::kControlSlot - ? Graph::kControlSlot - : num_non_controlling_fanins; + input.port_id = + input_is_control ? Graph::kControlSlot : num_non_controlling_fanins; - if (!gtl::InsertIfNotPresent(&fanouts()[fanin], input)) { - return false; - } node->add_input(TensorIdToString({fanin.node->name(), fanin.port_id})); if (fanin.port_id > Graph::kControlSlot) { int node_input_size = node->input_size() - 1; @@ -202,6 +305,17 @@ bool MutableGraphView::AddFaninInternal(NodeDef* node, num_non_controlling_fanins); } } + + fanouts()[fanin].insert(input); + if (max_regular_output_port()[fanin.node] < fanin.port_id) { + max_regular_output_port()[fanin.node] = fanin.port_id; + } + + // Dedup control dependencies. + if (!input_is_control && can_dedup_control_with_regular_input) { + RemoveControllingFaninInternal(node, fanin.node); + } + return true; } @@ -244,16 +358,18 @@ bool MutableGraphView::RemoveFanins(NodeDef* node, input.port_id = tensor_id.index() == Graph::kControlSlot ? Graph::kControlSlot : i; + auto& fanouts_set = fanouts()[fanin]; if (remove_fanin) { - fanouts()[fanin].erase(input); + fanouts_set.erase(input); } else { // Shift inputs to be retained. if (tensor_id.index() > Graph::kControlSlot) { - fanouts()[fanin].erase(input); - fanouts()[fanin].insert(InputPort(node, i)); + fanouts_set.erase(input); + fanouts_set.insert(InputPort(node, i)); } mutable_inputs->SwapElements(i, curr_pos++); } + UpdateMaxRegularOutputPortForRemovedFanin(fanin, fanouts_set); modified = true; } else { @@ -314,15 +430,12 @@ bool MutableGraphView::UpdateFanin(absl::string_view node_name, return false; } - bool is_from_fanin_control = from_fanin.index() == Graph::kControlSlot; - bool is_to_fanin_control = to_fanin.index() == Graph::kControlSlot; // When replacing a non control dependency fanin with a control dependency, or // vice versa, remove and add, so ports can be updated properly in fanout(s). - if (is_from_fanin_control || is_to_fanin_control) { + if (from_fanin.index() == Graph::kControlSlot || + to_fanin.index() == Graph::kControlSlot) { bool modified = RemoveFanins(node, {from_fanin}); - if (!HasFanin(*node, to_fanin)) { - modified |= AddFaninInternal(node, to_fanin); - } + modified |= AddFaninInternal(node, to_fanin); return modified; } @@ -336,79 +449,47 @@ bool MutableGraphView::UpdateFanin(absl::string_view node_name, string to_fanin_string = TensorIdToString(to_fanin); int num_inputs = node->input_size(); bool modified = false; + absl::flat_hash_set* from_fanin_port_fanouts = nullptr; + absl::flat_hash_set* to_fanin_port_fanouts = nullptr; for (int i = 0; i < num_inputs; ++i) { if (ParseTensorName(node->input(i)) == from_fanin) { - OutputPort from_fanin_port(from_fanin_node, from_fanin.index()); InputPort old_input; old_input.node = node; old_input.port_id = from_fanin.index() == Graph::kControlSlot ? Graph::kControlSlot : i; - fanouts()[from_fanin_port].erase(old_input); + if (from_fanin_port_fanouts == nullptr) { + OutputPort from_fanin_port(from_fanin_node, from_fanin.index()); + from_fanin_port_fanouts = &fanouts()[from_fanin_port]; + } + from_fanin_port_fanouts->erase(old_input); - OutputPort to_fanin_port(to_fanin_node, to_fanin.index()); InputPort new_input; new_input.node = node; new_input.port_id = to_fanin.index() == Graph::kControlSlot ? Graph::kControlSlot : i; - fanouts()[to_fanin_port].insert(new_input); + if (to_fanin_port_fanouts == nullptr) { + OutputPort to_fanin_port(to_fanin_node, to_fanin.index()); + to_fanin_port_fanouts = &fanouts()[to_fanin_port]; + } + to_fanin_port_fanouts->insert(new_input); node->set_input(i, to_fanin_string); modified = true; } } - return modified; -} - -bool MutableGraphView::DedupControllingFanins(NodeDef* node) { - absl::flat_hash_set fanins; - absl::flat_hash_set removed_fanins; - int pos = 0; - const int last_idx = node->input_size() - 1; - int last_pos = last_idx; - while (pos <= last_pos) { - const string& input = node->input(pos); - TensorId tensor_id = ParseTensorName(input); - if (!gtl::InsertIfNotPresent(&fanins, tensor_id.node()) && - IsControlInput(tensor_id)) { - node->mutable_input()->SwapElements(pos, last_pos--); - removed_fanins.insert(input); - } else { - ++pos; + // Dedup control dependencies and update max regular output ports. + if (modified) { + UpdateMaxRegularOutputPortForRemovedFanin( + {from_fanin_node, from_fanin.index()}, *from_fanin_port_fanouts); + if (max_regular_output_port()[to_fanin_node] < to_fanin.index()) { + max_regular_output_port()[to_fanin_node] = to_fanin.index(); } - } - - if (last_pos < last_idx) { - absl::flat_hash_set retained_fanins( - node->input().begin(), node->input().begin() + last_pos + 1); - for (const auto& removed : removed_fanins) { - if (!retained_fanins.contains(removed)) { - OutputPort fanin(nodes()[ParseTensorName(removed).node()], - Graph::kControlSlot); - fanouts()[fanin].erase({node, Graph::kControlSlot}); - } + if (CanDedupControlWithRegularInput(*this, *to_fanin_node)) { + RemoveControllingFaninInternal(node, to_fanin_node); } - node->mutable_input()->DeleteSubrange(last_pos + 1, last_idx - last_pos); - return true; } - return false; -} - -bool MutableGraphView::DedupControllingFanins(absl::string_view node_name) { - NodeDef* node = GetNode(node_name); - if (node == nullptr) { - return false; - } - return DedupControllingFanins(node); -} - -bool MutableGraphView::DedupControllingFanins() { - const int num_nodes = graph()->node_size(); - bool modified = false; - for (int i = 0; i < num_nodes; ++i) { - modified |= DedupControllingFanins(graph()->mutable_node(i)); - } return modified; } @@ -463,6 +544,24 @@ bool MutableGraphView::AddControllingFanin(absl::string_view node_name, } } +bool MutableGraphView::RemoveControllingFaninInternal(NodeDef* node, + NodeDef* fanin_node) { + for (int i = node->input_size() - 1; i >= 0; --i) { + TensorId tensor_id = ParseTensorName(node->input(i)); + if (tensor_id.index() > Graph::kControlSlot) { + break; + } + if (tensor_id.node() == fanin_node->name()) { + fanouts()[{fanin_node, Graph::kControlSlot}].erase( + {node, Graph::kControlSlot}); + node->mutable_input()->SwapElements(i, node->input_size() - 1); + node->mutable_input()->RemoveLast(); + return true; + } + } + return false; +} + void MutableGraphView::DeleteNodes(const std::set& nodes_to_delete) { for (const string& node_name_to_delete : nodes_to_delete) RemoveFaninsInternal(nodes().at(node_name_to_delete), @@ -488,7 +587,9 @@ void MutableGraphView::RemoveFaninsInternal(NodeDef* deleted_node, else input.port_id = i; - fanouts()[fanin].erase(input); + auto& fanouts_set = fanouts()[fanin]; + fanouts_set.erase(input); + UpdateMaxRegularOutputPortForRemovedFanin(fanin, fanouts_set); } } diff --git a/tensorflow/core/grappler/mutable_graph_view.h b/tensorflow/core/grappler/mutable_graph_view.h index f7c2a1118e..93f0958d5e 100644 --- a/tensorflow/core/grappler/mutable_graph_view.h +++ b/tensorflow/core/grappler/mutable_graph_view.h @@ -24,8 +24,10 @@ limitations under the License. #include "absl/types/span.h" #include "tensorflow/core/framework/graph.pb.h" #include "tensorflow/core/framework/node_def.pb.h" +#include "tensorflow/core/graph/graph.h" #include "tensorflow/core/graph/tensor_id.h" #include "tensorflow/core/grappler/graph_view.h" +#include "tensorflow/core/grappler/op_types.h" #include "tensorflow/core/platform/types.h" namespace tensorflow { @@ -41,7 +43,7 @@ class MutableGraphView : public internal::GraphViewInternal { public: explicit MutableGraphView(GraphDef* graph) : GraphViewInternal(graph) { for (NodeDef& node : *graph->mutable_node()) AddUniqueNodeOrDie(&node); - for (NodeDef& node : *graph->mutable_node()) AddFanouts(&node); + for (NodeDef& node : *graph->mutable_node()) AddAndDedupFanouts(&node); } // Lookup fanouts/fanins using immutable ports. @@ -63,16 +65,20 @@ class MutableGraphView : public internal::GraphViewInternal { // Updates all fanouts (input ports fetching output tensors) from `from_node` // to the `to_node`, including control dependencies. // - // Example: We have 2 nodes that use `bar` node output tensors as inputs: - // 1. foo1(bar:0, bar:1, other:0, ^bar) + // Example: We have 3 nodes that use `bar` node output tensors as inputs: + // 1. foo1(bar:0, bar:1, other:0) // 2. foo2(bar:1, other:1) + // 3. foo3(other:2, ^bar) // // After calling ForwardOutputs(bar, new_bar): - // 1. foo1(new_bar:0, new_bar:1, other:0, ^new_bar) + // 1. foo1(new_bar:0, new_bar:1, other:0) // 2. foo2(new_bar:1, other:1) - void UpdateFanouts(absl::string_view from_node, absl::string_view to_node); + // 3. foo3(other:2, ^new_bar) + // + // This will return true iff the nodes are modified. + bool UpdateFanouts(absl::string_view from_node, absl::string_view to_node); - // Add fanin to node `node_name`. If the node or fanin do not exist in the + // Adds fanin to node `node_name`. If the node or fanin do not exist in the // graph, nothing will be modified in the graph. If fanin is a control // dependency, existing control dependencies will be checked first before // adding. Otherwise fanin will be added after existing non control dependency @@ -82,7 +88,7 @@ class MutableGraphView : public internal::GraphViewInternal { // already exists, the node will not be modified. bool AddFanin(absl::string_view node_name, const TensorId& fanin); - // Remove fanin from node `node_name`. If the node or fanin do not exist in + // Removes fanin from node `node_name`. If the node or fanin do not exist in // the graph, nothing will be modified in the graph. If there are multiple // inputs that match the fanin, all of them will be removed. // @@ -90,32 +96,20 @@ class MutableGraphView : public internal::GraphViewInternal { // fanin, the node will not be modified. bool RemoveFanin(absl::string_view node_name, const TensorId& fanin); - // Remove all fanins from node `node_name`. Control dependencies will be + // Removes all fanins from node `node_name`. Control dependencies will be // retained if keep_controlling_fanins is true. // // This will return true iff the node is modified. bool RemoveAllFanins(absl::string_view node_name, bool keep_controlling_fanins); - // Replace all fanins `from_fanin` with `to_fanin` in node `node_name`. If + // Replaces all fanins `from_fanin` with `to_fanin` in node `node_name`. If // the fanins or node do not exist, nothing will be modified in the graph. // // This will return true iff the node is modified. bool UpdateFanin(absl::string_view node_name, const TensorId& from_fanin, const TensorId& to_fanin); - // Removes redundant control fanins from node `node_name`. - // - // This will return true iff the node is modified. - // TODO(lyandy): Measure performance of deduping on every AddFanin compared to - // deduping once at the end. - bool DedupControllingFanins(absl::string_view node_name); - - // Removes redundant control fanins from all nodes in the graph. - // - // This will return true iff the node is modified. - bool DedupControllingFanins(); - // Adds a control dependency to the target node named `node_name`. // // Case 1: If the fanin is not a Switch node, the control dependency is simply @@ -140,27 +134,42 @@ class MutableGraphView : public internal::GraphViewInternal { void DeleteNodes(const std::set& nodes_to_delete); private: + // Adds fanouts for fanins of node to graph, while deduping control + // dependencies from existing control dependencies and regular fanins. Note, + // node inputs will be mutated if control dependencies can be deduped. + void AddAndDedupFanouts(NodeDef* node); + + // Finds next output port smaller than fanin.port_id and update. The + // max_regular_output_port is only updated if fanin.port_id is the same as the + // current max_regular_output_port and if the fanouts set is empty. If there + // are no regular outputs, max_regular_output_port will be erased. + void UpdateMaxRegularOutputPortForRemovedFanin( + const OutputPort& fanin, + const absl::flat_hash_set& fanin_fanouts); + // Updates all fanouts (input ports fetching output tensors) from `from_node` // to the `to_node`, including control dependencies. // - // Example: We have 2 nodes that use `bar` node output tensors as inputs: - // 1. foo1(bar:0, bar:1, other:0, ^bar) + // Example: We have 3 nodes that use `bar` node output tensors as inputs: + // 1. foo1(bar:0, bar:1, other:0) // 2. foo2(bar:1, other:1) + // 3. foo3(other:2, ^bar) // // After calling ForwardOutputs(bar, new_bar): - // 1. foo1(new_bar:0, new_bar:1, other:0, ^new_bar) + // 1. foo1(new_bar:0, new_bar:1, other:0) // 2. foo2(new_bar:1, other:1) + // 3. foo3(other:2, ^new_bar) // // IMPORTANT: If `from_node` or `to_node` is not in the underlying graph, the // behavior is undefined. - void UpdateFanouts(NodeDef* from_node, NodeDef* to_node); + bool UpdateFanoutsInternal(NodeDef* from_node, NodeDef* to_node); // Removes fanins of the deleted node from internal state. Control // dependencies are retained iff keep_controlling_fanins is true. void RemoveFaninsInternal(NodeDef* deleted_node, bool keep_controlling_fanins); - // Add fanin to node. If fanin is a control dependency, existing control + // Adds fanin to node. If fanin is a control dependency, existing control // dependencies will be checked first before adding. Otherwise fanin will be // added after existing non control dependency inputs. // @@ -168,7 +177,7 @@ class MutableGraphView : public internal::GraphViewInternal { // already exists, the node will not be modified. bool AddFaninInternal(NodeDef* node, const OutputPort& fanin); - // Add fanin to node. If the node or fanin do not exist in the graph, nothing + // Adds fanin to node. If the node or fanin do not exist in the graph, nothing // will be modified in the graph. If fanin is a control dependency, existing // control dependencies will be checked first before adding. Otherwise fanin // will be added after existing non control dependency inputs. @@ -178,10 +187,15 @@ class MutableGraphView : public internal::GraphViewInternal { bool AddFaninInternal(NodeDef* node, const TensorId& fanin); // Removes any fanin in node that matches to a fanin in fanins. + // + // This will return true iff the node is modified. bool RemoveFanins(NodeDef* node, absl::Span fanins); - // Removes redundant control fanins from node. - bool DedupControllingFanins(NodeDef* node); + // Removes controlling fanin `fanin_node` from node if such controlling fanin + // exists. + // + // This will return true iff the node is modified. + bool RemoveControllingFaninInternal(NodeDef* node, NodeDef* fanin_node); }; } // end namespace grappler diff --git a/tensorflow/core/grappler/mutable_graph_view_test.cc b/tensorflow/core/grappler/mutable_graph_view_test.cc index cdc212f6f9..526ca85ec0 100644 --- a/tensorflow/core/grappler/mutable_graph_view_test.cc +++ b/tensorflow/core/grappler/mutable_graph_view_test.cc @@ -35,7 +35,8 @@ TEST(MutableGraphViewTest, AddAndUpdateFanouts) { {NDef("bar", "NotImportant", {}, {}), NDef("other", "NotImportant", {}, {}), NDef("foo_1", "NotImportant", {"bar", "other", "bar:1", "^bar"}), - NDef("foo_2", "NotImportant", {"other:1", "bar:2", "^bar"})}, + NDef("foo_2", "NotImportant", {"other:1", "bar:2", "^bar"}), + NDef("foo_3", "NotImportant", {"other:2", "^bar"})}, /*funcs=*/{}); MutableGraphView graph(&graph_def); @@ -43,7 +44,56 @@ TEST(MutableGraphViewTest, AddAndUpdateFanouts) { NodeDef* new_bar = graph.AddNode(NDef("new_bar", "NotImportant", {}, {})); NodeDef* bar = graph.GetNode("bar"); - graph.UpdateFanouts(bar->name(), new_bar->name()); + EXPECT_TRUE(graph.UpdateFanouts(bar->name(), new_bar->name())); + + // Fanout nodes must have their inputs updated. + NodeDef* foo_1 = graph.GetNode("foo_1"); + ASSERT_NE(foo_1, nullptr); + ASSERT_EQ(foo_1->input_size(), 3); + EXPECT_EQ(foo_1->input(0), "new_bar"); + EXPECT_EQ(foo_1->input(1), "other"); + EXPECT_EQ(foo_1->input(2), "new_bar:1"); + + NodeDef* foo_2 = graph.GetNode("foo_2"); + ASSERT_NE(foo_2, nullptr); + ASSERT_EQ(foo_2->input_size(), 2); + EXPECT_EQ(foo_2->input(0), "other:1"); + EXPECT_EQ(foo_2->input(1), "new_bar:2"); + + NodeDef* foo_3 = graph.GetNode("foo_3"); + ASSERT_NE(foo_3, nullptr); + ASSERT_EQ(foo_3->input_size(), 2); + EXPECT_EQ(foo_3->input(0), "other:2"); + EXPECT_EQ(foo_3->input(1), "^new_bar"); + + // And fanouts mapping must be also updated for both nodes. + bool include_control_fanouts = true; + auto old_node_fanouts = graph.GetFanouts(*bar, include_control_fanouts); + auto new_node_fanouts = graph.GetFanouts(*new_bar, include_control_fanouts); + + EXPECT_TRUE(old_node_fanouts.empty()); + + EXPECT_EQ(new_node_fanouts.size(), 4); + EXPECT_EQ(new_node_fanouts.count(MutableGraphView::InputPort(foo_1, 0)), 1); + EXPECT_EQ(new_node_fanouts.count(MutableGraphView::InputPort(foo_1, 2)), 1); + EXPECT_EQ(new_node_fanouts.count(MutableGraphView::InputPort(foo_2, 1)), 1); + EXPECT_EQ(new_node_fanouts.count(MutableGraphView::InputPort(foo_3, -1)), 1); +} + +TEST(MutableGraphViewTest, AddAndUpdateFanoutsKeepControls) { + GraphDef graph_def = test::function::GDef( + {NDef("bar_1", "Switch", {}, {}), NDef("bar_2", "Identity", {"bar_1:1"}), + NDef("other", "NotImportant", {}, {}), + NDef("foo_1", "NotImportant", {"bar_2", "other", "bar_2:1", "^bar_2"}), + NDef("foo_2", "NotImportant", {"other:1", "bar_2:2", "^bar_2"})}, + /*funcs=*/{}); + + MutableGraphView graph(&graph_def); + + NodeDef* new_bar = graph.AddNode(NDef("new_bar", "Identity", {"bar_1:2"})); + NodeDef* bar_2 = graph.GetNode("bar_2"); + + EXPECT_TRUE(graph.UpdateFanouts(bar_2->name(), new_bar->name())); // Fanout nodes must have their inputs updated. NodeDef* foo_1 = graph.GetNode("foo_1"); @@ -63,7 +113,7 @@ TEST(MutableGraphViewTest, AddAndUpdateFanouts) { // And fanouts mapping must be also updated for both nodes. bool include_control_fanouts = true; - auto old_node_fanouts = graph.GetFanouts(*bar, include_control_fanouts); + auto old_node_fanouts = graph.GetFanouts(*bar_2, include_control_fanouts); auto new_node_fanouts = graph.GetFanouts(*new_bar, include_control_fanouts); EXPECT_TRUE(old_node_fanouts.empty()); @@ -78,7 +128,8 @@ TEST(MutableGraphViewTest, AddAndUpdateFanoutsWithoutSelfLoops) { // Actual node.op() is not important in this test. GraphDef graph_def = test::function::GDef({NDef("bar", "NotImportant", {}, {}), - NDef("foo", "NotImportant", {"bar", "^bar"})}, + NDef("foo_1", "NotImportant", {"bar", "^bar"}), + NDef("foo_2", "NotImportant", {"^bar"})}, /*funcs=*/{}); MutableGraphView graph(&graph_def); @@ -87,14 +138,18 @@ TEST(MutableGraphViewTest, AddAndUpdateFanoutsWithoutSelfLoops) { NodeDef* new_bar = graph.AddNode(NDef("new_bar", "NewBar", {"bar"}, {})); NodeDef* bar = graph.GetNode("bar"); - graph.UpdateFanouts("bar", new_bar->name()); + EXPECT_TRUE(graph.UpdateFanouts("bar", new_bar->name())); // Foo node must read from `new_bar`. - NodeDef* foo = graph.GetNode("foo"); - ASSERT_NE(foo, nullptr); - ASSERT_EQ(foo->input_size(), 2); - EXPECT_EQ(foo->input(0), "new_bar"); - EXPECT_EQ(foo->input(1), "^new_bar"); + NodeDef* foo_1 = graph.GetNode("foo_1"); + ASSERT_NE(foo_1, nullptr); + ASSERT_EQ(foo_1->input_size(), 1); + EXPECT_EQ(foo_1->input(0), "new_bar"); + + NodeDef* foo_2 = graph.GetNode("foo_2"); + ASSERT_NE(foo_2, nullptr); + ASSERT_EQ(foo_2->input_size(), 1); + EXPECT_EQ(foo_2->input(0), "^new_bar"); // And the `new_bar` should read from the original `bar`. ASSERT_EQ(new_bar->input_size(), 1); @@ -109,8 +164,8 @@ TEST(MutableGraphViewTest, AddAndUpdateFanoutsWithoutSelfLoops) { EXPECT_EQ(bar_fanouts.count(MutableGraphView::InputPort(new_bar, 0)), 1); EXPECT_EQ(new_bar_fanouts.size(), 2); - EXPECT_EQ(new_bar_fanouts.count(MutableGraphView::InputPort(foo, 0)), 1); - EXPECT_EQ(new_bar_fanouts.count(MutableGraphView::InputPort(foo, -1)), 1); + EXPECT_EQ(new_bar_fanouts.count(MutableGraphView::InputPort(foo_1, 0)), 1); + EXPECT_EQ(new_bar_fanouts.count(MutableGraphView::InputPort(foo_2, -1)), 1); } GraphDef SimpleMutateFaninGraph() { @@ -176,7 +231,7 @@ TEST(MutableGraphViewTest, AddFanin) { expected_node = NDef("", "", {"b", "a:1", "a:1", "b:2"}); TestAddFanin("foo_3", {"b", 2}, /*modified=*/true, &expected_node); // Add input to node with 1 input multiple controls. - expected_node = NDef("", "", {"b", "a", "^c", "^a"}); + expected_node = NDef("", "", {"b", "a", "^c"}); TestAddFanin("foo_2", {"a", 0}, /*modified=*/true, &expected_node); // Add input to node with multiple inputs and controls. expected_node = NDef("", "", {"a", "b:2", "b:2", "a:1", "^d", "^c"}); @@ -201,8 +256,8 @@ TEST(MutableGraphViewTest, AddFanin) { TestAddFanin("foo_2", {"d", Graph::kControlSlot}, /*modified=*/true, &expected_node); // Add control to node with multiple input multiple controls. - expected_node = NDef("", "", {"a", "b:2", "b:2", "^c", "^d", "^a"}); - TestAddFanin("foo_4", {"a", Graph::kControlSlot}, /*modified=*/true, + expected_node = NDef("", "", {"a", "b:2", "b:2", "^c", "^d"}); + TestAddFanin("foo_4", {"a", Graph::kControlSlot}, /*modified=*/false, &expected_node); // Add control to node with 0 inputs 0 controls. expected_node = NDef("", "", {"^a"}); @@ -431,7 +486,7 @@ TEST(MutableGraphViewTest, UpdateFanin) { TestUpdateFanin("foo_4", {"d", Graph::kControlSlot}, {"d", 1}, /*modified=*/true, &expected_node); // Update fanin from control to control. - expected_node = NDef("", "", {"a", "b:2", "b:2", "^d", "^b"}); + expected_node = NDef("", "", {"a", "b:2", "b:2", "^d"}); TestUpdateFanin("foo_4", {"c", Graph::kControlSlot}, {"b", Graph::kControlSlot}, /*modified=*/true, &expected_node); @@ -463,85 +518,266 @@ TEST(MutableGraphViewTest, UpdateFanin) { /*modified=*/false, /*expected_node=*/nullptr); } -GraphDef SimpleDuplicateControllingFaninsGraph() { +TEST(MutableGraphViewTest, DedupControllingFaninsOnGraphInit) { + // Actual node.op() is not important in this test. + GraphDef graph_def = test::function::GDef( + { + NDef("a", "NotImportant", {}, {}), + NDef("b", "NotImportant", {}, {}), + NDef("c", "Switch", {}, {}), + NDef("d", "Identity", {"c:1"}), + NDef("foo_1", "IdentityN", {"a", "b:1", "^b"}), + NDef("foo_2", "IdentityN", {"a", "^b", "^b"}), + NDef("foo_3", "IdentityN", {"a", "b:1", "^b", "^b"}), + NDef("foo_4", "IdentityN", {"a:2", "b:1", "^b", "^b", "^a", "^a"}), + NDef("foo_5", "NotImportant", {"a:2", "b:1", "^b", "^b", "^a", "^a"}), + NDef("foo_6", "Identity", {"d", "^d"}), + NDef("foo_7", "NotImportant", + {"a:3", "b:2", "d", "^d", "^d", "^a", "^b", "^a", "^b"}), + }, + /*funcs=*/{}); + + MutableGraphView graph(&graph_def); + + EXPECT_EQ(graph.graph()->node_size(), 11); + NodeDef* a = graph.GetNode("a"); + ASSERT_NE(a, nullptr); + ASSERT_EQ(a->input_size(), 0); + NodeDef* b = graph.GetNode("b"); + ASSERT_NE(b, nullptr); + ASSERT_EQ(b->input_size(), 0); + NodeDef* c = graph.GetNode("c"); + ASSERT_NE(c, nullptr); + ASSERT_EQ(c->input_size(), 0); + NodeDef* d = graph.GetNode("d"); + ASSERT_NE(d, nullptr); + ASSERT_EQ(d->input_size(), 1); + EXPECT_EQ(d->input(0), "c:1"); + NodeDef* foo_1 = graph.GetNode("foo_1"); + ASSERT_NE(foo_1, nullptr); + ASSERT_EQ(foo_1->input_size(), 2); + EXPECT_EQ(foo_1->input(0), "a"); + EXPECT_EQ(foo_1->input(1), "b:1"); + NodeDef* foo_2 = graph.GetNode("foo_2"); + ASSERT_NE(foo_2, nullptr); + ASSERT_EQ(foo_2->input_size(), 2); + EXPECT_EQ(foo_2->input(0), "a"); + EXPECT_EQ(foo_2->input(1), "^b"); + NodeDef* foo_3 = graph.GetNode("foo_3"); + ASSERT_NE(foo_3, nullptr); + ASSERT_EQ(foo_3->input_size(), 2); + EXPECT_EQ(foo_3->input(0), "a"); + EXPECT_EQ(foo_3->input(1), "b:1"); + NodeDef* foo_4 = graph.GetNode("foo_4"); + ASSERT_NE(foo_4, nullptr); + ASSERT_EQ(foo_4->input_size(), 2); + EXPECT_EQ(foo_4->input(0), "a:2"); + EXPECT_EQ(foo_4->input(1), "b:1"); + NodeDef* foo_5 = graph.GetNode("foo_5"); + ASSERT_NE(foo_5, nullptr); + ASSERT_EQ(foo_5->input_size(), 2); + EXPECT_EQ(foo_5->input(0), "a:2"); + EXPECT_EQ(foo_5->input(1), "b:1"); + NodeDef* foo_6 = graph.GetNode("foo_6"); + ASSERT_NE(foo_6, nullptr); + ASSERT_EQ(foo_6->input_size(), 2); + EXPECT_EQ(foo_6->input(0), "d"); + EXPECT_EQ(foo_6->input(1), "^d"); + NodeDef* foo_7 = graph.GetNode("foo_7"); + ASSERT_NE(foo_7, nullptr); + ASSERT_EQ(foo_7->input_size(), 4); + EXPECT_EQ(foo_7->input(0), "a:3"); + EXPECT_EQ(foo_7->input(1), "b:2"); + EXPECT_EQ(foo_7->input(2), "d"); + EXPECT_EQ(foo_7->input(3), "^d"); +} + +TEST(MutableGraphViewTest, DedupControllingFaninsOnAddFanin) { + // Actual node.op() is not important in this test. + GraphDef graph_def = test::function::GDef( + {NDef("a", "NotImportant", {}, {}), NDef("b", "NotImportant", {"^a"}), + NDef("c", "NotImportant", {"a:1"})}, + /*funcs=*/{}); + + MutableGraphView graph(&graph_def); + + EXPECT_TRUE(graph.AddFanin("b", {"a", 2})); + NodeDef* b = graph.GetNode("b"); + ASSERT_NE(b, nullptr); + ASSERT_EQ(b->input_size(), 1); + EXPECT_EQ(b->input(0), "a:2"); + + EXPECT_FALSE(graph.AddFanin("c", {"a", Graph::kControlSlot})); + NodeDef* c = graph.GetNode("c"); + ASSERT_NE(c, nullptr); + ASSERT_EQ(c->input_size(), 1); + EXPECT_EQ(c->input(0), "a:1"); +} + +TEST(MutableGraphViewTest, NoDedupControlFlowControllingFaninsOnAddFanin) { + GraphDef graph_def = test::function::GDef( + {NDef("a", "Switch", {}, {}), NDef("b", "Identity", {"a:1"}), + NDef("c", "", {}, {}), NDef("d", "", {}, {})}, + /*funcs=*/{}); + + MutableGraphView graph(&graph_def); + + EXPECT_TRUE(graph.AddFanin("c", {"b", 2})); + NodeDef* c = graph.GetNode("c"); + ASSERT_NE(c, nullptr); + ASSERT_EQ(c->input_size(), 1); + EXPECT_EQ(c->input(0), "b:2"); + EXPECT_TRUE(graph.AddFanin("c", {"b", Graph::kControlSlot})); + ASSERT_EQ(c->input_size(), 2); + EXPECT_EQ(c->input(0), "b:2"); + EXPECT_EQ(c->input(1), "^b"); + EXPECT_FALSE(graph.AddFanin("c", {"b", Graph::kControlSlot})); + ASSERT_EQ(c->input_size(), 2); + EXPECT_EQ(c->input(0), "b:2"); + EXPECT_EQ(c->input(1), "^b"); + + EXPECT_TRUE(graph.AddFanin("d", {"b", Graph::kControlSlot})); + NodeDef* d = graph.GetNode("d"); + ASSERT_NE(d, nullptr); + ASSERT_EQ(d->input_size(), 1); + EXPECT_EQ(d->input(0), "^b"); + EXPECT_FALSE(graph.AddFanin("d", {"b", Graph::kControlSlot})); + ASSERT_EQ(d->input_size(), 1); + EXPECT_EQ(d->input(0), "^b"); + EXPECT_TRUE(graph.AddFanin("d", {"b", 3})); + ASSERT_EQ(d->input_size(), 2); + EXPECT_EQ(d->input(0), "b:3"); + EXPECT_EQ(d->input(1), "^b"); +} + +TEST(MutableGraphViewTest, DedupControllingFaninsOnUpdateFanin) { // Actual node.op() is not important in this test. GraphDef graph_def = test::function::GDef( {NDef("a", "NotImportant", {}, {}), NDef("b", "NotImportant", {}, {}), - NDef("foo_1", "NotImportant", {"a", "b:1", "^b"}), - NDef("foo_2", "NotImportant", {"a", "^b", "^b"}), - NDef("foo_3", "NotImportant", {"a", "b:1", "^b", "^b"}), - NDef("foo_4", "NotImportant", {"a:2", "b:1", "^b", "^b", "^a", "^a"})}, + NDef("c", "NotImportant", {"a:1", "^b"})}, /*funcs=*/{}); - return graph_def; + + MutableGraphView graph(&graph_def); + + EXPECT_TRUE(graph.UpdateFanin("c", {"a", 1}, {"b", 2})); + NodeDef* c = graph.GetNode("c"); + ASSERT_NE(c, nullptr); + ASSERT_EQ(c->input_size(), 1); + EXPECT_EQ(c->input(0), "b:2"); } -void CheckDedupControllingFaninsForNode(MutableGraphView* graph, - absl::string_view node_name, - const NodeDef* expected_node) { - // Deduping again should result in no change. - EXPECT_FALSE(graph->DedupControllingFanins(node_name)); - NodeDef* node = graph->GetNode(node_name); - ASSERT_NE(node, nullptr); - ASSERT_EQ(node->input_size(), expected_node->input_size()); - CompareNodeInputs(*graph, expected_node, node); - for (int i = 0; i < node->input_size(); ++i) { - TensorId tensor_id = ParseTensorName(node->input(i)); - if (tensor_id.index() > Graph::kControlSlot) { - CheckFanout(*graph, {tensor_id.node(), Graph::kControlSlot}, node_name); - } - } +TEST(MutableGraphViewTest, NoDedupControlFlowControllingFaninsOnUpdateFanin) { + GraphDef graph_def = test::function::GDef( + {NDef("a", "Switch", {}, {}), NDef("b", "Identity", {"a:1"}), + NDef("c", "Identity", {"a:2"}), NDef("d", "NotImportant", {"c", "^b"}), + NDef("e", "NotImportant", {"b", "^c"})}, + /*funcs=*/{}); + + MutableGraphView graph(&graph_def); + + EXPECT_TRUE(graph.UpdateFanin("d", {"b", Graph::kControlSlot}, + {"c", Graph::kControlSlot})); + NodeDef* d = graph.GetNode("d"); + ASSERT_NE(d, nullptr); + ASSERT_EQ(d->input_size(), 2); + EXPECT_EQ(d->input(0), "c"); + EXPECT_EQ(d->input(1), "^c"); + + EXPECT_TRUE(graph.UpdateFanin("e", {"b", 0}, {"c", 3})); + NodeDef* e = graph.GetNode("e"); + ASSERT_NE(e, nullptr); + ASSERT_EQ(e->input_size(), 2); + EXPECT_EQ(e->input(0), "c:3"); + EXPECT_EQ(e->input(1), "^c"); + + EXPECT_TRUE(graph.UpdateFanin("e", {"c", 3}, {"c", Graph::kControlSlot})); + ASSERT_NE(e, nullptr); + ASSERT_EQ(e->input_size(), 1); + EXPECT_EQ(e->input(0), "^c"); +} + +TEST(MutableGraphViewTest, UpdateMaxRegularOutputPortOnAddFanin) { + // Actual node.op() is not important in this test. + GraphDef graph_def = test::function::GDef( + {NDef("a", "NotImportant", {}, {}), NDef("b", "NotImportant", {"a:1"}), + NDef("c", "NotImportant", {"^b"})}, + /*funcs=*/{}); + + MutableGraphView graph(&graph_def); + + EXPECT_TRUE(graph.AddFanin("c", {"a", 3})); + NodeDef* a = graph.GetNode("a"); + ASSERT_NE(a, nullptr); + auto fanouts = graph.GetFanouts(*a, /*include_controlled_nodes=*/true); + EXPECT_EQ(fanouts.size(), 2); + NodeDef* b = graph.GetNode("b"); + ASSERT_NE(b, nullptr); + EXPECT_EQ(fanouts.count(MutableGraphView::InputPort(b, 0)), 1); + NodeDef* c = graph.GetNode("c"); + ASSERT_NE(c, nullptr); + EXPECT_EQ(fanouts.count(MutableGraphView::InputPort(c, 0)), 1); } -void TestDedupControllingFaninsForNode(MutableGraphView* graph, - absl::string_view node_name, - const NodeDef* expected_node) { - EXPECT_TRUE(graph->DedupControllingFanins(node_name)); - CheckDedupControllingFaninsForNode(graph, node_name, expected_node); +TEST(MutableGraphViewTest, UpdateMaxRegularOutputPortOnRemoveFanin) { + // Actual node.op() is not important in this test. + GraphDef graph_def = test::function::GDef( + {NDef("a", "NotImportant", {}, {}), NDef("b", "NotImportant", {"a:1"}), + NDef("c", "NotImportant", {"a:2"})}, + /*funcs=*/{}); + + MutableGraphView graph(&graph_def); + + EXPECT_TRUE(graph.RemoveFanin("c", {"a", 2})); + NodeDef* a = graph.GetNode("a"); + ASSERT_NE(a, nullptr); + auto fanouts = graph.GetFanouts(*a, /*include_controlled_nodes=*/true); + EXPECT_EQ(fanouts.size(), 1); + NodeDef* b = graph.GetNode("b"); + ASSERT_NE(b, nullptr); + EXPECT_EQ(fanouts.count(MutableGraphView::InputPort(b, 0)), 1); } -TEST(MutableGraphViewTest, DedupControllingFaninsForNode) { - GraphDef graph_def = SimpleDuplicateControllingFaninsGraph(); +TEST(MutableGraphViewTest, KeepMaxRegularOutputPortOnRemoveFanin) { + // Actual node.op() is not important in this test. + GraphDef graph_def = test::function::GDef( + {NDef("a", "NotImportant", {}, {}), NDef("b", "NotImportant", {"a:1"}), + NDef("c", "NotImportant", {"a:2"})}, + /*funcs=*/{}); MutableGraphView graph(&graph_def); - NodeDef expected_node; - // Remove redundant control dependency '^b'. - expected_node = NDef("", "", {"a", "b:1"}); - TestDedupControllingFaninsForNode(&graph, "foo_1", &expected_node); - // Remove extra control dependency '^b'. - expected_node = NDef("", "", {"a", "^b"}); - TestDedupControllingFaninsForNode(&graph, "foo_2", &expected_node); - // Remove redundant and extra control dependencies '^b'. - expected_node = NDef("", "", {"a", "b:1"}); - TestDedupControllingFaninsForNode(&graph, "foo_3", &expected_node); - // Remove multiple redundant control dependencies. - expected_node = NDef("", "", {"a:2", "b:1"}); - TestDedupControllingFaninsForNode(&graph, "foo_4", &expected_node); - // Missing node. - EXPECT_FALSE(graph.DedupControllingFanins("missing")); + EXPECT_TRUE(graph.RemoveFanin("b", {"a", 1})); + NodeDef* a = graph.GetNode("a"); + ASSERT_NE(a, nullptr); + auto fanouts = graph.GetFanouts(*a, /*include_controlled_nodes=*/true); + EXPECT_EQ(fanouts.size(), 1); + NodeDef* c = graph.GetNode("c"); + ASSERT_NE(c, nullptr); + EXPECT_EQ(fanouts.count(MutableGraphView::InputPort(c, 0)), 1); } -TEST(MutableGraphViewTest, DedupControllingFaninsForGraph) { - GraphDef graph_def = SimpleDuplicateControllingFaninsGraph(); +TEST(MutableGraphViewTest, UpdateMaxRegularOutputPortOnUpdateFanin) { + // Actual node.op() is not important in this test. + GraphDef graph_def = test::function::GDef( + {NDef("a", "NotImportant", {}, {}), NDef("b", "NotImportant", {"a:1"}), + NDef("c", "NotImportant", {"a:2"})}, + /*funcs=*/{}); MutableGraphView graph(&graph_def); - EXPECT_TRUE(graph.DedupControllingFanins()); - // Deduping again should result in no change. - EXPECT_FALSE(graph.DedupControllingFanins()); - NodeDef expected_node; - // Remove redundant control dependency '^b'. - expected_node = NDef("", "", {"a", "b:1"}); - CheckDedupControllingFaninsForNode(&graph, "foo_1", &expected_node); - // Remove extra control dependency '^b'. - expected_node = NDef("", "", {"a", "^b"}); - CheckDedupControllingFaninsForNode(&graph, "foo_2", &expected_node); - // Remove redundant and extra control dependencies '^b'. - expected_node = NDef("", "", {"a", "b:1"}); - CheckDedupControllingFaninsForNode(&graph, "foo_3", &expected_node); - // Remove multiple redundant control dependencies. - expected_node = NDef("", "", {"a:2", "b:1"}); - CheckDedupControllingFaninsForNode(&graph, "foo_4", &expected_node); + EXPECT_TRUE(graph.UpdateFanin("c", {"a", 2}, {"b", 3})); + NodeDef* a = graph.GetNode("a"); + ASSERT_NE(a, nullptr); + auto a_fanouts = graph.GetFanouts(*a, /*include_controlled_nodes=*/true); + EXPECT_EQ(a_fanouts.size(), 1); + NodeDef* b = graph.GetNode("b"); + ASSERT_NE(b, nullptr); + EXPECT_EQ(a_fanouts.count(MutableGraphView::InputPort(b, 0)), 1); + auto b_fanouts = graph.GetFanouts(*b, /*include_controlled_nodes=*/true); + EXPECT_EQ(b_fanouts.size(), 1); + NodeDef* c = graph.GetNode("c"); + ASSERT_NE(c, nullptr); + EXPECT_EQ(b_fanouts.count(MutableGraphView::InputPort(c, 0)), 1); } TEST(MutableGraphViewTest, AddControllingFaninMissing) { @@ -655,7 +891,7 @@ TEST(MutableGraphViewTest, AddControllingFaninSwitchWithNoExistingIdentity) { TEST(MutableGraphViewTest, AddControllingFaninSwitchWithExistingAddedIdentity) { GraphDef graph_def = test::function::GDef( {NDef("a", "NotImportant", {}, {}), NDef("switch", "Switch", {}, {}), - NDef("ConstantFoldingCtrl/switch_0", "Identity", {}, {})}, + NDef("ConstantFoldingCtrl/switch_0", "Identity", {"switch"})}, /*funcs=*/{}); MutableGraphView graph(&graph_def); @@ -694,9 +930,8 @@ TEST(MutableGraphViewTest, DeleteNodes) { auto bar_fanouts = graph.GetFanouts(*bar, include_control_fanouts); auto other_fanouts = graph.GetFanouts(*other, include_control_fanouts); - EXPECT_EQ(bar_fanouts.size(), 2); + EXPECT_EQ(bar_fanouts.size(), 1); EXPECT_EQ(bar_fanouts.count(MutableGraphView::InputPort(foo_2, 1)), 1); - EXPECT_EQ(bar_fanouts.count(MutableGraphView::InputPort(foo_2, -1)), 1); EXPECT_EQ(other_fanouts.size(), 1); EXPECT_EQ(other_fanouts.count(MutableGraphView::InputPort(foo_2, 0)), 1); diff --git a/tensorflow/core/grappler/optimizers/data/graph_test_utils.cc b/tensorflow/core/grappler/optimizers/data/graph_test_utils.cc index 9d8b388a3a..202dfb5ac8 100644 --- a/tensorflow/core/grappler/optimizers/data/graph_test_utils.cc +++ b/tensorflow/core/grappler/optimizers/data/graph_test_utils.cc @@ -42,7 +42,7 @@ NodeDef MakeMapAndBatchNode(StringPiece name, StringPiece input_node_name, StringPiece function_name) { return test::function::NDef( name, "ExperimentalMapAndBatchDataset", - {string(input_node_name), "", string(batch_size_node_name), + {string(input_node_name), string(batch_size_node_name), string(num_parallel_calls_node_name), string(drop_remainder_node_name)}, {{"f", FunctionDefHelper::FunctionRef(string(function_name))}, {"Targuments", {}}, @@ -68,7 +68,7 @@ NodeDef MakeParallelInterleaveNode(StringPiece name, StringPiece function_name, bool sloppy) { return test::function::NDef( name, "ParallelInterleaveDatasetV2", - {string(input_node_name), "", string(cycle_length_node_name), + {string(input_node_name), string(cycle_length_node_name), string(block_length_node_name), string(num_parallel_calls_node_name)}, { {"f", FunctionDefHelper::FunctionRef(string(function_name))}, diff --git a/tensorflow/core/grappler/optimizers/data/latency_all_edges_test.cc b/tensorflow/core/grappler/optimizers/data/latency_all_edges_test.cc index d428d04a66..426c1dca5b 100644 --- a/tensorflow/core/grappler/optimizers/data/latency_all_edges_test.cc +++ b/tensorflow/core/grappler/optimizers/data/latency_all_edges_test.cc @@ -30,9 +30,9 @@ TEST(LatencyAllEdgesTest, AddLatenciesAfterTensorMapPrefetch) { using test::function::NDef; GrapplerItem item; NodeDef component_node = - NDef("component_nodes", "Const", {}, {{"value", 1}, {"dtype", DT_INT32}}); + NDef("component_node", "Const", {}, {{"value", 1}, {"dtype", DT_INT32}}); NodeDef from_tensor_node = - NDef("from_tensor_nodes", "TensorDataset", {"component_nodes"}, + NDef("from_tensor_node", "TensorDataset", {"component_node"}, {{"Toutput_types", {}}, {"output_shapes", {}}}); NodeDef captured_input_node = NDef("captured_input_node", "Const", {}, -- GitLab From db48d17b671623a24dcee7e90f8b18d87f51d009 Mon Sep 17 00:00:00 2001 From: Michael Kuperstein Date: Thu, 3 Jan 2019 18:17:13 -0800 Subject: [PATCH 329/622] [XLA] Make the verifier check bitcasts don't change the element type This is what BitcastConvert is for. PiperOrigin-RevId: 227783600 --- .../compiler/xla/service/hlo_verifier.cc | 8 ++++++ .../compiler/xla/service/hlo_verifier_test.cc | 18 +++++++++++++ .../while_loop_invariant_code_motion_test.cc | 27 ++++++++++--------- 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/tensorflow/compiler/xla/service/hlo_verifier.cc b/tensorflow/compiler/xla/service/hlo_verifier.cc index 14e29533c2..9274d42280 100644 --- a/tensorflow/compiler/xla/service/hlo_verifier.cc +++ b/tensorflow/compiler/xla/service/hlo_verifier.cc @@ -387,6 +387,14 @@ Status ShapeVerifier::HandleReduce(HloInstruction* reduce) { Status ShapeVerifier::HandleBitcast(HloInstruction* bitcast) { TF_RETURN_IF_ERROR(CheckOperandCount(bitcast, 1)); + // Bitcasts are not allowed to change the element type. + if (bitcast->operand(0)->shape().element_type() != + bitcast->shape().element_type()) { + return InternalError( + "Bitcast can not change the element type from %s to %s", + PrimitiveType_Name(bitcast->operand(0)->shape().element_type()), + PrimitiveType_Name(bitcast->shape().element_type())); + } return Status::OK(); } diff --git a/tensorflow/compiler/xla/service/hlo_verifier_test.cc b/tensorflow/compiler/xla/service/hlo_verifier_test.cc index 91f247a9bb..9b2d884f79 100644 --- a/tensorflow/compiler/xla/service/hlo_verifier_test.cc +++ b/tensorflow/compiler/xla/service/hlo_verifier_test.cc @@ -480,5 +480,23 @@ TEST_F(HloVerifierTestLayoutSensitive, ConcatWithLayoutChangeNotAllowed) { EXPECT_THAT(status.error_message(), HasSubstr("Instruction shouldn't change layouts")); } + +TEST_F(HloVerifierTest, BitcastCanNotChangeElementType) { + const char* const hlo_string = R"( + HloModule Module + + ENTRY BitcastCanNotChangeElementType { + constant.0 = f32[2] constant({0.0, 0.0}) + ROOT bitcast = s32[2] bitcast(constant.0) + } + )"; + TF_ASSERT_OK_AND_ASSIGN(auto module, ParseHloString(hlo_string)); + + auto status = verifier().Run(module.get()).status(); + ASSERT_FALSE(status.ok()); + EXPECT_THAT(status.error_message(), + HasSubstr("Bitcast can not change the element type")); +} + } // namespace } // namespace xla diff --git a/tensorflow/compiler/xla/service/while_loop_invariant_code_motion_test.cc b/tensorflow/compiler/xla/service/while_loop_invariant_code_motion_test.cc index 8e7c4bc882..3587c016b4 100644 --- a/tensorflow/compiler/xla/service/while_loop_invariant_code_motion_test.cc +++ b/tensorflow/compiler/xla/service/while_loop_invariant_code_motion_test.cc @@ -299,7 +299,7 @@ TEST_F(WhileLoopInvariantCodeMotionTest, DontHoistBitcastAlone) { // bitcast either. auto m = CreateNewVerifiedModule(); auto scalar_s32 = ShapeUtil::MakeShape(S32, {}); - auto scalar_f32 = ShapeUtil::MakeShape(F32, {}); + auto effective_scalar_s32 = ShapeUtil::MakeShape(S32, {1}); auto token_shape = ShapeUtil::MakeTokenShape(); Shape while_shape = ShapeUtil::MakeTupleShape({scalar_s32, scalar_s32, token_shape}); @@ -314,10 +314,12 @@ TEST_F(WhileLoopInvariantCodeMotionTest, DontHoistBitcastAlone) { HloInstruction::CreateGetTupleElement(scalar_s32, param, 1)); HloInstruction* in_token = builder.AddInstruction( HloInstruction::CreateGetTupleElement(token_shape, param, 2)); - HloInstruction* bitcast_inst = builder.AddInstruction( - HloInstruction::CreateUnary(scalar_f32, HloOpcode::kBitcast, gte_0)); - HloInstruction* out_token = builder.AddInstruction( - HloInstruction::CreateOutfeed(scalar_f32, bitcast_inst, in_token, "")); + HloInstruction* bitcast_inst = + builder.AddInstruction(HloInstruction::CreateUnary( + effective_scalar_s32, HloOpcode::kBitcast, gte_0)); + HloInstruction* out_token = + builder.AddInstruction(HloInstruction::CreateOutfeed( + effective_scalar_s32, bitcast_inst, in_token, "")); builder.AddInstruction( HloInstruction::CreateTuple({gte_0, gte_1, out_token})); @@ -352,9 +354,9 @@ TEST_F(WhileLoopInvariantCodeMotionTest, HoistBitcastIfNeeded) { // The bitcast's user can be hoisted, so hoist the bitcast too. auto m = CreateNewVerifiedModule(); auto scalar_s32 = ShapeUtil::MakeShape(S32, {}); - auto scalar_f32 = ShapeUtil::MakeShape(F32, {}); - Shape while_shape = - ShapeUtil::MakeTupleShape({scalar_s32, scalar_f32, scalar_f32}); + auto effective_scalar_s32 = ShapeUtil::MakeShape(S32, {1}); + Shape while_shape = ShapeUtil::MakeTupleShape( + {scalar_s32, effective_scalar_s32, effective_scalar_s32}); HloComputation* while_body = [&]() { HloComputation::Builder builder(TestName() + ".while_body"); @@ -363,12 +365,13 @@ TEST_F(WhileLoopInvariantCodeMotionTest, HoistBitcastIfNeeded) { HloInstruction* gte_0 = builder.AddInstruction( HloInstruction::CreateGetTupleElement(scalar_s32, param, 0)); HloInstruction* gte_1 = builder.AddInstruction( - HloInstruction::CreateGetTupleElement(scalar_f32, param, 1)); - HloInstruction* bitcast_inst = builder.AddInstruction( - HloInstruction::CreateUnary(scalar_f32, HloOpcode::kBitcast, gte_0)); + HloInstruction::CreateGetTupleElement(effective_scalar_s32, param, 1)); + HloInstruction* bitcast_inst = + builder.AddInstruction(HloInstruction::CreateUnary( + effective_scalar_s32, HloOpcode::kBitcast, gte_0)); HloInstruction* add_inst = builder.AddInstruction(HloInstruction::CreateBinary( - scalar_f32, HloOpcode::kAdd, bitcast_inst, gte_1)); + effective_scalar_s32, HloOpcode::kAdd, bitcast_inst, gte_1)); builder.AddInstruction( HloInstruction::CreateTuple({gte_0, gte_1, add_inst})); -- GitLab From 328ca94210ba39e13b51228200c8d2da27246f7f Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Thu, 3 Jan 2019 18:19:05 -0800 Subject: [PATCH 330/622] Refactor ExecuteFlexOp into smaller methods of OpNode. PiperOrigin-RevId: 227783768 --- tensorflow/lite/delegates/flex/kernel.cc | 145 +++++++++++++---------- 1 file changed, 85 insertions(+), 60 deletions(-) diff --git a/tensorflow/lite/delegates/flex/kernel.cc b/tensorflow/lite/delegates/flex/kernel.cc index 7d18c39098..3012241f4f 100644 --- a/tensorflow/lite/delegates/flex/kernel.cc +++ b/tensorflow/lite/delegates/flex/kernel.cc @@ -88,10 +88,18 @@ class OpNode { const tensorflow::NodeDef& nodedef() const { return nodedef_; } const std::vector& inputs() const { return inputs_; } - std::vector* mutable_inputs() { return &inputs_; } + void InitializeInputs(const TfLiteIntArray* inputs) { + for (int index : TfLiteIntArrayView(inputs)) { + inputs_.push_back(index); + } + } const std::vector& outputs() const { return outputs_; } - std::vector* mutable_outputs() { return &outputs_; } + void InitializeOutputs(const TfLiteIntArray* outputs) { + for (int index : TfLiteIntArrayView(outputs)) { + outputs_.push_back(index); + } + } tensorflow::Status InitializeNodeDef(const void* custom_initial_data, int custom_initial_data_size) { @@ -123,6 +131,66 @@ class OpNode { return tensorflow::Status::OK(); } + // Build thew new EagerOperation. In case of error, the returned 'op' is + // guaranteed to be 'nullptr'. + tensorflow::Status BuildEagerOp( + tensorflow::EagerContext* eager_context, + std::unique_ptr* op) { + op->reset(); + + const tensorflow::AttrTypeMap* attr_types; + bool is_function = false; + TF_RETURN_WITH_CONTEXT_IF_ERROR( + tensorflow::AttrTypeMapForOp(name_.c_str(), &attr_types, &is_function), + " (while processing attributes of '", name_, "')"); + if (is_function) { + return tensorflow::errors::NotFound( + "Operation '", name_, + "' is not registered. (while processing attributes of '", name_, + "')"); + } + + op->reset(new tensorflow::EagerOperation(eager_context, name_.c_str(), + /*is_function=*/false, + attr_types)); + for (const auto& attr : nodedef_.attr()) { + (*op)->MutableAttrs()->Set(attr.first, attr.second); + } + + return tensorflow::Status::OK(); + } + + tensorflow::Status BuildEagerInputs(BufferMap* buffer_map, + tensorflow::EagerOperation* op) { + for (int input_index : inputs_) { + if (!buffer_map->HasTensor(input_index)) { + return tensorflow::errors::Internal( + "Cannot read from invalid tensor index ", input_index); + } + auto* handle = new tensorflow::TensorHandle( + buffer_map->GetTensor(input_index), nullptr, nullptr, nullptr); + op->AddInput(handle); + handle->Unref(); + + if (buffer_map->IsForwardable(input_index)) { + // Take it out of the map, so Eager/TF can reuse the buffer for an + // output tensor of the op. + buffer_map->RemoveTensor(input_index); + } + } + return tensorflow::Status::OK(); + } + + tensorflow::Status PersistEagerOutputs(BufferMap* buffer_map, + OpOutputs* retvals) { + for (int i = 0; i < outputs_.size(); ++i) { + const tensorflow::Tensor* tensor = nullptr; + TF_RETURN_IF_ERROR(retvals->GetHandle(i)->Tensor(&tensor)); + buffer_map->SetFromTensorFlow(outputs_[i], *tensor); + } + return tensorflow::Status::OK(); + } + private: // The name of the TensorFlow op to execute. string name_; @@ -139,60 +207,23 @@ class OpNode { // Executes the TensorFlow op given by 'op_name', with the attributes specified // in 'nodedef'. Inputs and outputs are given as indices into the 'buffer_map'. tensorflow::Status ExecuteFlexOp(tensorflow::EagerContext* eager_context, - BufferMap* buffer_map, const string& op_name, - const tensorflow::NodeDef& nodedef, - const std::vector& inputs, - const std::vector& outputs) { - const tensorflow::AttrTypeMap* attr_types; - bool is_function = false; - TF_RETURN_WITH_CONTEXT_IF_ERROR( - tensorflow::AttrTypeMapForOp(op_name.c_str(), &attr_types, &is_function), - " (while processing attributes of '", op_name, "')"); - if (is_function) { - return tensorflow::errors::NotFound( - "Operation '", op_name, - "' is not registered. (while processing attributes of '", op_name, - "')"); - } - tensorflow::EagerOperation op(eager_context, op_name.c_str(), - /*is_function=*/false, attr_types); - for (const auto& attr : nodedef.attr()) { - op.MutableAttrs()->Set(attr.first, attr.second); - } + BufferMap* buffer_map, OpNode* node_data) { + std::unique_ptr op; + TF_RETURN_IF_ERROR(node_data->BuildEagerOp(eager_context, &op)); + TF_RETURN_IF_ERROR(node_data->BuildEagerInputs(buffer_map, op.get())); - for (int input_index : inputs) { - if (!buffer_map->HasTensor(input_index)) { - return tensorflow::errors::Internal( - "Cannot read from invalid tensor index ", input_index); - } - auto* handle = new tensorflow::TensorHandle( - buffer_map->GetTensor(input_index), nullptr, nullptr, nullptr); - op.AddInput(handle); - handle->Unref(); - - if (buffer_map->IsForwardable(input_index)) { - // Take it out of the map, so Eager/TF can reuse the buffer for an output - // tensor of the op. - buffer_map->RemoveTensor(input_index); - } - } - - int num_retvals = outputs.size(); + int num_retvals = node_data->outputs().size(); OpOutputs retvals(num_retvals); TF_RETURN_WITH_CONTEXT_IF_ERROR( - EagerExecute(&op, retvals.GetVector(), &num_retvals), - " (while executing '", op_name, "' via Eager)"); + EagerExecute(op.get(), retvals.GetVector(), &num_retvals), + " (while executing '", node_data->name(), "' via Eager)"); - if (num_retvals != outputs.size()) { + if (num_retvals != node_data->outputs().size()) { return tensorflow::errors::Internal( "Unexpected number of outputs from EagerExecute"); } - for (int i = 0; i < num_retvals; ++i) { - const tensorflow::Tensor* tensor = nullptr; - TF_RETURN_IF_ERROR(retvals.GetHandle(i)->Tensor(&tensor)); - buffer_map->SetFromTensorFlow(outputs[i], *tensor); - } + TF_RETURN_IF_ERROR(node_data->PersistEagerOutputs(buffer_map, &retvals)); return tensorflow::Status::OK(); } @@ -247,12 +278,8 @@ void* Init(TfLiteContext* context, const char* buffer, size_t length) { node->custom_initial_data_size); if (!status.ok()) break; - for (auto input_index : TfLiteIntArrayView(node->inputs)) { - node_data.mutable_inputs()->push_back(input_index); - } - for (auto output_index : TfLiteIntArrayView(node->outputs)) { - node_data.mutable_outputs()->push_back(output_index); - } + node_data.InitializeInputs(node->inputs); + node_data.InitializeOutputs(node->outputs); } if (ConvertStatus(context, status) != kTfLiteOk) { @@ -325,7 +352,7 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { } TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { - const auto* op_data = reinterpret_cast(node->user_data); + auto* op_data = reinterpret_cast(node->user_data); BufferMap* buffer_map = op_data->buffer_map; tensorflow::EagerContext* eager_context = op_data->eager_context; @@ -344,14 +371,12 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { } // Execute the TensorFlow Ops sequentially. - for (const auto& node_data : op_data->nodes) { + for (OpNode& node_data : op_data->nodes) { SCOPED_TAGGED_OPERATOR_PROFILE( reinterpret_cast(context->profiler), - node_data->name().c_str(), node_data->index()); + node_data.name().c_str(), node_data.index()); - auto status = ExecuteFlexOp(eager_context, buffer_map, node_data.name(), - node_data.nodedef(), node_data.inputs(), - node_data.outputs()); + auto status = ExecuteFlexOp(eager_context, buffer_map, &node_data); TF_LITE_ENSURE_OK(context, ConvertStatus(context, status)); } -- GitLab From d0ce85e772fa17afcc7914c63bcab1b90df6664e Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Thu, 3 Jan 2019 18:26:08 -0800 Subject: [PATCH 331/622] Internal change. PiperOrigin-RevId: 227784390 --- tensorflow/compiler/xla/service/BUILD | 1 + 1 file changed, 1 insertion(+) diff --git a/tensorflow/compiler/xla/service/BUILD b/tensorflow/compiler/xla/service/BUILD index eed958dc6b..b84792cfc3 100644 --- a/tensorflow/compiler/xla/service/BUILD +++ b/tensorflow/compiler/xla/service/BUILD @@ -3576,6 +3576,7 @@ cc_library( tf_cc_test( name = "indexed_array_analysis_test", srcs = ["indexed_array_analysis_test.cc"], + extra_copts = ["-Wno-string-plus-int"], deps = [ ":hlo_matchers", ":indexed_array_analysis", -- GitLab From a2f7f39d982682fa8de050e001522581570c510f Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Thu, 3 Jan 2019 19:50:14 -0800 Subject: [PATCH 332/622] Add support non-stacking(cross-links) but connected to other bidi-lstm ops case, tensorflow equivalent: tf.nn.static_bidirectional_rnn PiperOrigin-RevId: 227791656 --- .../kernels/bidirectional_sequence_lstm.cc | 54 +++-- .../bidirectional_sequence_lstm_test.cc | 202 +++++++++++++++++- 2 files changed, 233 insertions(+), 23 deletions(-) diff --git a/tensorflow/lite/kernels/bidirectional_sequence_lstm.cc b/tensorflow/lite/kernels/bidirectional_sequence_lstm.cc index 1ddfe7201e..31c6e3f44c 100644 --- a/tensorflow/lite/kernels/bidirectional_sequence_lstm.cc +++ b/tensorflow/lite/kernels/bidirectional_sequence_lstm.cc @@ -105,7 +105,10 @@ constexpr int kBwInputActivationStateTensor = 37; // Cell state tensors of size {n_batch, n_cell} constexpr int kBwInputCellStateTensor = 38; -// Auxiliary input and weights when stacking. +// Used as auxiliary input and weights when stacking for +// tf.contrib.rnn.stack_bidirectional_rnn case (with cross links); Used as input +// to the backward cell when stacking for tf.nn.static_bidirectional_rnn case +// (without cross links). constexpr int kAuxInputTensor = 39; // Optional // Forward weights. constexpr int kFwAuxInputToInputWeightsTensor = 40; // Optional @@ -459,8 +462,8 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { const TfLiteTensor* bw_aux_input_to_output_weights = GetOptionalInputTensor(context, node, kBwAuxInputToOutputWeightsTensor); - const bool aux_inputs_all_or_none = - ((aux_input != nullptr) && (fw_aux_input_to_cell_weights != nullptr) && + const bool aux_inputs_weights_all_or_none = + ((fw_aux_input_to_cell_weights != nullptr) && (fw_aux_input_to_forget_weights != nullptr) && (fw_aux_input_to_output_weights != nullptr) && (bw_aux_input_to_cell_weights != nullptr) && @@ -472,8 +475,9 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { (bw_aux_input_to_cell_weights == nullptr) && (bw_aux_input_to_forget_weights == nullptr) && (bw_aux_input_to_output_weights == nullptr)); - TF_LITE_ENSURE(context, aux_inputs_all_or_none); - const bool has_aux_input = (aux_input != nullptr); + TF_LITE_ENSURE(context, aux_inputs_weights_all_or_none); + + const bool has_aux_input = (fw_aux_input_to_forget_weights != nullptr); if (has_aux_input) { // Check that aux_input has the same dimensions (except last) as the input. @@ -870,6 +874,9 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { const TfLiteTensor* bw_aux_input_to_output_weights = GetOptionalInputTensor(context, node, kBwAuxInputToOutputWeightsTensor); + const bool has_previous_bw_output = (aux_input != nullptr); + const bool use_aux_input = (fw_aux_input_to_forget_weights != nullptr); + // Populate a TfLiteLSTMParams struct for the evaluation functions. TfLiteLSTMParams lstm_params = {params->activation, params->cell_clip, params->proj_clip, kTfLiteLSTMFullKernel}; @@ -879,6 +886,26 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { const auto actual_bw_output = params->merge_outputs ? fw_output : bw_output; const bool time_major = params->time_major; + + // We want to cover the following cases: + // + // If not stacking (not connected after other bidi lstms): + // both fw & bw will just use `input`; aux_input will be null. + // + // If stacking with cross_links, TensorFlow equivalent + // (tf.contrib.rnn.stack_bidirectional_rnn): + // both fw & bw will use `input`, but aux_input will be none null. + // Note, this time, whether connected after other bidi lstms both works. + // + // If stacking without cross_links, but connected after other bidi lstms, + // TensorFlow equivalent (tf.nn.static_bidirectional_rnn): + // fw will use `input`, bw will use aux_input, and the `real aux_input` + // will be null. + + const bool non_stacking_mode = !use_aux_input && has_previous_bw_output; + const TfLiteTensor* bw_input = non_stacking_mode ? aux_input : input; + const TfLiteTensor* real_aux_input = non_stacking_mode ? nullptr : aux_input; + switch (fw_input_to_output_weights->type) { case kTfLiteFloat32: { TfLiteStatus fw_pass_status = lstm_eval::EvalFloat( @@ -891,7 +918,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { /*input_layer_norm_coefficients=*/nullptr, /*forget_layer_norm_coefficients=*/nullptr, /*cell_layer_norm_coefficients=*/nullptr, - /*output_layer_norm_coefficients=*/nullptr, aux_input, + /*output_layer_norm_coefficients=*/nullptr, real_aux_input, fw_aux_input_to_input_weights, fw_aux_input_to_forget_weights, fw_aux_input_to_cell_weights, fw_aux_input_to_output_weights, fw_input_gate_bias, fw_forget_gate_bias, fw_cell_bias, @@ -902,7 +929,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { TF_LITE_ENSURE_OK(context, fw_pass_status); TfLiteStatus bw_pass_status = lstm_eval::EvalFloat( - input, bw_input_to_input_weights, bw_input_to_forget_weights, + bw_input, bw_input_to_input_weights, bw_input_to_forget_weights, bw_input_to_cell_weights, bw_input_to_output_weights, bw_recurrent_to_input_weights, bw_recurrent_to_forget_weights, bw_recurrent_to_cell_weights, bw_recurrent_to_output_weights, @@ -911,7 +938,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { /*input_layer_norm_coefficients=*/nullptr, /*forget_layer_norm_coefficients=*/nullptr, /*cell_layer_norm_coefficients=*/nullptr, - /*output_layer_norm_coefficients=*/nullptr, aux_input, + /*output_layer_norm_coefficients=*/nullptr, real_aux_input, bw_aux_input_to_input_weights, bw_aux_input_to_forget_weights, bw_aux_input_to_cell_weights, bw_aux_input_to_output_weights, bw_input_gate_bias, bw_forget_gate_bias, bw_cell_bias, @@ -942,9 +969,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { TfLiteTensor* recovered_cell_weights = GetTemporary(context, node, kRecoveredCellWeights); TfLiteTensor* aux_input_quantized = - (aux_input == nullptr) - ? nullptr - : GetTemporary(context, node, kAuxInputQuantized); + use_aux_input ? GetTemporary(context, node, kAuxInputQuantized) + : nullptr; TfLiteStatus fw_pass_status = lstm_eval::EvalHybrid( input, fw_input_to_input_weights, fw_input_to_forget_weights, @@ -956,7 +982,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { /*input_layer_norm_coefficients=*/nullptr, /*forget_layer_norm_coefficients=*/nullptr, /*cell_layer_norm_coefficients=*/nullptr, - /*output_layer_norm_coefficients=*/nullptr, aux_input, + /*output_layer_norm_coefficients=*/nullptr, real_aux_input, fw_aux_input_to_input_weights, fw_aux_input_to_forget_weights, fw_aux_input_to_cell_weights, fw_aux_input_to_output_weights, fw_input_gate_bias, fw_forget_gate_bias, fw_cell_bias, @@ -970,7 +996,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { TF_LITE_ENSURE_OK(context, fw_pass_status); TfLiteStatus bw_pass_status = lstm_eval::EvalHybrid( - input, bw_input_to_input_weights, bw_input_to_forget_weights, + bw_input, bw_input_to_input_weights, bw_input_to_forget_weights, bw_input_to_cell_weights, bw_input_to_output_weights, bw_recurrent_to_input_weights, bw_recurrent_to_forget_weights, bw_recurrent_to_cell_weights, bw_recurrent_to_output_weights, @@ -979,7 +1005,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { /*input_layer_norm_coefficients=*/nullptr, /*forget_layer_norm_coefficients=*/nullptr, /*cell_layer_norm_coefficients=*/nullptr, - /*output_layer_norm_coefficients=*/nullptr, aux_input, + /*output_layer_norm_coefficients=*/nullptr, real_aux_input, bw_aux_input_to_input_weights, bw_aux_input_to_forget_weights, bw_aux_input_to_cell_weights, bw_aux_input_to_output_weights, bw_input_gate_bias, bw_forget_gate_bias, bw_cell_bias, diff --git a/tensorflow/lite/kernels/bidirectional_sequence_lstm_test.cc b/tensorflow/lite/kernels/bidirectional_sequence_lstm_test.cc index f5df6d15af..59ea47a2a2 100644 --- a/tensorflow/lite/kernels/bidirectional_sequence_lstm_test.cc +++ b/tensorflow/lite/kernels/bidirectional_sequence_lstm_test.cc @@ -38,7 +38,7 @@ class BidirectionalLSTMOpModel : public SingleOpModel { int sequence_length, bool use_cifg, bool use_peephole, bool use_projection_weights, bool use_projection_bias, bool merge_outputs, - float cell_clip, float proj_clip, + bool use_aux_input, float cell_clip, float proj_clip, bool quantize_weights, bool time_major, const std::vector>& input_shapes) : n_batch_(n_batch), @@ -185,7 +185,11 @@ class BidirectionalLSTMOpModel : public SingleOpModel { bw_output_ = AddOutput(TensorType_FLOAT32); } - aux_input_ = AddNullInput(); + if (use_aux_input) { + aux_input_ = AddInput(TensorType_FLOAT32); + } else { + aux_input_ = AddNullInput(); + } fw_aux_input_to_input_weights_ = AddNullInput(); fw_aux_input_to_forget_weights_ = AddNullInput(); fw_aux_input_to_cell_weights_ = AddNullInput(); @@ -302,6 +306,10 @@ class BidirectionalLSTMOpModel : public SingleOpModel { PopulateTensor(input_, offset, begin, end); } + void SetAuxInput(int offset, float* begin, float* end) { + PopulateTensor(aux_input_, offset, begin, end); + } + std::vector GetFwOutput() { return ExtractVector(fw_output_); } std::vector GetBwOutput() { return ExtractVector(bw_output_); } @@ -406,7 +414,8 @@ TEST_P(LSTMOpTest, BlackBoxTestNoCifgNoPeepholeNoProjectionNoClipping) { BidirectionalLSTMOpModel lstm( n_batch, n_input, n_cell, n_output, sequence_length, /*use_cifg=*/false, /*use_peephole=*/false, /*use_projection_weights=*/false, - /*use_projection_bias=*/false, /*merge_outputs=*/false, /*cell_clip=*/0.0, + /*use_projection_bias=*/false, /*merge_outputs=*/false, + /*use_aux_input=*/false, /*cell_clip=*/0.0, /*proj_clip=*/0.0, quantize_weights, /*time_major=*/true, { {sequence_length, n_batch, n_input}, // input tensor @@ -570,7 +579,8 @@ TEST_P(LSTMOpTest, BlackBoxTestMergedOutput) { BidirectionalLSTMOpModel lstm( n_batch, n_input, n_cell, n_output, sequence_length, /*use_cifg=*/false, /*use_peephole=*/false, /*use_projection_weights=*/false, - /*use_projection_bias=*/false, /*merge_outputs=*/true, /*cell_clip=*/0.0, + /*use_projection_bias=*/false, /*merge_outputs=*/true, + /*use_aux_input=*/false, /*cell_clip=*/0.0, /*proj_clip=*/0.0, quantize_weights, /*time_major=*/true, { {sequence_length, n_batch, n_input}, // input tensor @@ -733,7 +743,8 @@ TEST(LSTMOpTest, BlackBoxTestNoCifgNoPeepholeNoProjectionNoClippingReverse) { BidirectionalLSTMOpModel lstm( n_batch, n_input, n_cell, n_output, sequence_length, /*use_cifg=*/false, /*use_peephole=*/false, /*use_projection_weights=*/false, - /*use_projection_bias=*/false, /*merge_outputs=*/false, /*cell_clip=*/0.0, + /*use_projection_bias=*/false, /*merge_outputs=*/false, + /*use_aux_input=*/false, /*cell_clip=*/0.0, /*proj_clip=*/0.0, /*quantize_weights=*/false, /*time_major=*/true, { {sequence_length, n_batch, n_input}, // input tensor @@ -895,7 +906,8 @@ TEST(LSTMOpTest, BlackBoxTestWithCifgWithPeepholeNoProjectionNoClipping) { BidirectionalLSTMOpModel lstm( n_batch, n_input, n_cell, n_output, sequence_length, /*use_cifg=*/true, /*use_peephole=*/true, /*use_projection_weights=*/false, - /*use_projection_bias=*/false, /*merge_outputs=*/false, /*cell_clip=*/0.0, + /*use_projection_bias=*/false, /*merge_outputs=*/false, + /*use_aux_input=*/false, /*cell_clip=*/0.0, /*proj_clip=*/0.0, /*quantize_weights=*/false, /*time_major=*/true, { {sequence_length, n_batch, n_input}, // input tensor @@ -1047,7 +1059,8 @@ TEST(LSTMOpTest, BidirectionalLSTMOpModel lstm( n_batch, n_input, n_cell, n_output, sequence_length, /*use_cifg=*/true, /*use_peephole=*/true, /*use_projection_weights=*/false, - /*use_projection_bias=*/false, /*merge_outputs=*/false, /*cell_clip=*/0.0, + /*use_projection_bias=*/false, /*merge_outputs=*/false, + /*use_aux_input=*/false, /*cell_clip=*/0.0, /*proj_clip=*/0.0, /*quantize_weights=*/false, /*time_major=*/true, { {sequence_length, n_batch, n_input}, // input tensor @@ -1199,7 +1212,8 @@ TEST(LSTMOpTest, BlackBoxTestWithPeepholeWithProjectionNoClipping) { BidirectionalLSTMOpModel lstm( n_batch, n_input, n_cell, n_output, sequence_length, /*use_cifg=*/false, /*use_peephole=*/true, /*use_projection_weights=*/true, - /*use_projection_bias=*/false, /*merge_outputs=*/false, /*cell_clip=*/0.0, + /*use_projection_bias=*/false, /*merge_outputs=*/false, + /*use_aux_input=*/false, /*cell_clip=*/0.0, /*proj_clip=*/0.0, /*quantize_weights=*/false, /*time_major=*/true, { {sequence_length, n_batch, n_input}, // input tensor @@ -1903,7 +1917,8 @@ TEST(LSTMOpTest, BlackBoxTestWithPeepholeWithProjectionNoClippingBatchMajor) { BidirectionalLSTMOpModel lstm( n_batch, n_input, n_cell, n_output, sequence_length, /*use_cifg=*/false, /*use_peephole=*/true, /*use_projection_weights=*/true, - /*use_projection_bias=*/false, /*merge_outputs=*/false, /*cell_clip=*/0.0, + /*use_projection_bias=*/false, /*merge_outputs=*/false, + /*use_aux_input=*/false, /*cell_clip=*/0.0, /*proj_clip=*/0.0, /*quantize_weights=*/false, /*time_major=*/false, { {n_batch, sequence_length, n_input}, // input tensor @@ -2590,6 +2605,175 @@ TEST(LSTMOpTest, BlackBoxTestWithPeepholeWithProjectionNoClippingBatchMajor) { EXPECT_THAT(combined, ElementsAreArray(ArrayFloatNear(expected))); } +// Same as the no cifg no peephole no projection no clipping test, but have an +// aux input (without aux input weights), this is the case when stacking but no +// cross-links. +TEST_P(LSTMOpTest, BlackBoxTestWithAuxInput) { + const int n_batch = 1; + const int n_input = 2; + // n_cell and n_output have the same size when there is no projection. + const int n_cell = 4; + const int n_output = 4; + const int sequence_length = 3; + const bool quantize_weights = GetParam(); + + BidirectionalLSTMOpModel lstm( + n_batch, n_input, n_cell, n_output, sequence_length, /*use_cifg=*/false, + /*use_peephole=*/false, /*use_projection_weights=*/false, + /*use_projection_bias=*/false, /*merge_outputs=*/false, + /*use_aux_input=*/true, /*cell_clip=*/0.0, + /*proj_clip=*/0.0, quantize_weights, /*time_major=*/true, + { + {sequence_length, n_batch, n_input}, // input tensor + + // Forward cell + {n_cell, n_input}, // input_to_input_weight tensor + {n_cell, n_input}, // input_to_forget_weight tensor + {n_cell, n_input}, // input_to_cell_weight tensor + {n_cell, n_input}, // input_to_output_weight tensor + + {n_cell, n_output}, // recurrent_to_input_weight tensor + {n_cell, n_output}, // recurrent_to_forget_weight tensor + {n_cell, n_output}, // recurrent_to_cell_weight tensor + {n_cell, n_output}, // recurrent_to_output_weight tensor + + {0}, // cell_to_input_weight tensor + {0}, // cell_to_forget_weight tensor + {0}, // cell_to_output_weight tensor + + {n_cell}, // input_gate_bias tensor + {n_cell}, // forget_gate_bias tensor + {n_cell}, // cell_bias tensor + {n_cell}, // output_gate_bias tensor + + {0, 0}, // projection_weight tensor + {0}, // projection_bias tensor + + // Backward cell + {n_cell, n_input}, // input_to_input_weight tensor + {n_cell, n_input}, // input_to_forget_weight tensor + {n_cell, n_input}, // input_to_cell_weight tensor + {n_cell, n_input}, // input_to_output_weight tensor + + {n_cell, n_output}, // recurrent_to_input_weight tensor + {n_cell, n_output}, // recurrent_to_forget_weight tensor + {n_cell, n_output}, // recurrent_to_cell_weight tensor + {n_cell, n_output}, // recurrent_to_output_weight tensor + + {0}, // cell_to_input_weight tensor + {0}, // cell_to_forget_weight tensor + {0}, // cell_to_output_weight tensor + + {n_cell}, // input_gate_bias tensor + {n_cell}, // forget_gate_bias tensor + {n_cell}, // cell_bias tensor + {n_cell}, // output_gate_bias tensor + + {0, 0}, // projection_weight tensor + {0}, // projection_bias tensor + + {n_batch, n_output}, // activation_state tensor + {n_batch, n_cell}, // cell_state tensor + + {n_batch, n_output}, // activation_state tensor + {n_batch, n_cell}, // cell_state tensor + + // TODO(b/121134029): Update tests so tensor shapes after state tensor + // are used. They are currently ignored by test_util. + {sequence_length, n_batch, n_input}, // aux_input tensor + {n_cell, 0}, // aux_fw_input_to_input tensor + {n_cell, 0}, // aux_fw_input_to_forget tensor + {n_cell, 0}, // aux_fw_input_to_cell tensor + {n_cell, 0}, // aux_fw_input_to_output tensor + {n_cell, 0}, // aux_bw_input_to_input tensor + {n_cell, 0}, // aux_bw_input_to_forget tensor + {n_cell, 0}, // aux_bw_input_to_cell tensor + {n_cell, 0}, // aux_bw_input_to_output tensor + }); + + lstm.SetInputToInputWeights({-0.45018822, -0.02338299, -0.0870589, + -0.34550029, 0.04266912, -0.15680569, + -0.34856534, 0.43890524}); + + lstm.SetInputToCellWeights({-0.50013041, 0.1370284, 0.11810488, 0.2013163, + -0.20583314, 0.44344562, 0.22077113, + -0.29909778}); + + lstm.SetInputToForgetWeights({0.09701663, 0.20334584, -0.50592935, + -0.31343272, -0.40032279, 0.44781327, + 0.01387155, -0.35593212}); + + lstm.SetInputToOutputWeights({-0.25065863, -0.28290087, 0.04613829, + 0.40525138, 0.44272184, 0.03897077, -0.1556896, + 0.19487578}); + + lstm.SetInputGateBias({0., 0., 0., 0.}); + + lstm.SetCellBias({0., 0., 0., 0.}); + + lstm.SetForgetGateBias({1., 1., 1., 1.}); + + lstm.SetOutputGateBias({0., 0., 0., 0.}); + + lstm.SetRecurrentToInputWeights( + {-0.0063535, -0.2042388, 0.31454784, -0.35746509, 0.28902304, 0.08183324, + -0.16555229, 0.02286911, -0.13566875, 0.03034258, 0.48091322, + -0.12528998, 0.24077177, -0.51332325, -0.33502164, 0.10629296}); + + lstm.SetRecurrentToCellWeights( + {-0.3407414, 0.24443203, -0.2078532, 0.26320225, 0.05695659, -0.00123841, + -0.4744786, -0.35869038, -0.06418842, -0.13502428, -0.501764, 0.22830659, + -0.46367589, 0.26016325, -0.03894562, -0.16368064}); + + lstm.SetRecurrentToForgetWeights( + {-0.48684245, -0.06655136, 0.42224967, 0.2112639, 0.27654213, 0.20864892, + -0.07646349, 0.45877004, 0.00141793, -0.14609534, 0.36447752, 0.09196436, + 0.28053468, 0.01560611, -0.20127171, -0.01140004}); + + lstm.SetRecurrentToOutputWeights( + {0.43385774, -0.17194885, 0.2718237, 0.09215671, 0.24107647, -0.39835793, + 0.18212086, 0.01301402, 0.48572797, -0.50656658, 0.20047462, -0.20607421, + -0.51818722, -0.15390486, 0.0468148, 0.39922136}); + + // Input should have n_input * sequence_length many values. + static float lstm_input[] = {2., 3., 3., 4., 1., 1.}; + static float lstm_fw_golden_output[] = { + -0.02973187, 0.1229473, 0.20885126, -0.15358765, + -0.03716109, 0.12507336, 0.41193449, -0.20860538, + -0.15053082, 0.09120187, 0.24278517, -0.12222792}; + static float lstm_bw_golden_output[] = { + -0.0806187, 0.139077, 0.400476, -0.197842, -0.0332076, 0.123838, + 0.309777, -0.17621, -0.0490733, 0.0739237, 0.067706, -0.0208124}; + + float* batch0_start = lstm_input; + float* batch0_end = batch0_start + lstm.num_inputs() * lstm.sequence_length(); + + lstm.SetInput(0, batch0_start, batch0_end); + // Aux input and input are the same, so we should observe the same outputs + // as there's no aux input. + lstm.SetAuxInput(0, batch0_start, batch0_end); + + lstm.Invoke(); + + float* fw_golden_start = lstm_fw_golden_output; + float* fw_golden_end = + fw_golden_start + lstm.num_fw_outputs() * lstm.sequence_length(); + std::vector fw_expected; + fw_expected.insert(fw_expected.end(), fw_golden_start, fw_golden_end); + EXPECT_THAT(lstm.GetFwOutput(), + ElementsAreArray( + ArrayFloatNear(fw_expected, quantize_weights ? 1e-2 : 1e-5))); + + float* bw_golden_start = lstm_bw_golden_output; + float* bw_golden_end = + bw_golden_start + lstm.num_bw_outputs() * lstm.sequence_length(); + std::vector bw_expected; + bw_expected.insert(bw_expected.end(), bw_golden_start, bw_golden_end); + EXPECT_THAT(lstm.GetBwOutput(), + ElementsAreArray( + ArrayFloatNear(bw_expected, quantize_weights ? 1e-2 : 1e-5))); +} + } // namespace } // namespace tflite -- GitLab From f332be115267be60ed7e16fed661555c15f7d990 Mon Sep 17 00:00:00 2001 From: Pariksheet Pinjari Date: Fri, 4 Jan 2019 12:35:09 +0530 Subject: [PATCH 333/622] Update api_def_RegexReplace.pbtxt Documentation issue fixed --- tensorflow/core/api_def/base_api/api_def_RegexReplace.pbtxt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/core/api_def/base_api/api_def_RegexReplace.pbtxt b/tensorflow/core/api_def/base_api/api_def_RegexReplace.pbtxt index 70ad521926..2cc1a55676 100644 --- a/tensorflow/core/api_def/base_api/api_def_RegexReplace.pbtxt +++ b/tensorflow/core/api_def/base_api/api_def_RegexReplace.pbtxt @@ -10,7 +10,7 @@ op { } in_arg { name: "rewrite" - description: "The rewrite to be applied to the matched expresion." + description: "The rewrite to be applied to the matched expression." } out_arg { name: "output" -- GitLab From 72f0dadfffd8c2f635ec58ded5c38154644c6f3f Mon Sep 17 00:00:00 2001 From: Gunhan Gulsoy Date: Thu, 3 Jan 2019 23:49:44 -0800 Subject: [PATCH 334/622] Automated rollback of commit 7d57d32e440bc4f1654fb217fd701531a82c3587 PiperOrigin-RevId: 227809861 --- tensorflow/contrib/mpi_collectives/BUILD | 2 +- tensorflow/stream_executor/platform/default/dso_loader.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tensorflow/contrib/mpi_collectives/BUILD b/tensorflow/contrib/mpi_collectives/BUILD index d943ae6880..ecac06354d 100644 --- a/tensorflow/contrib/mpi_collectives/BUILD +++ b/tensorflow/contrib/mpi_collectives/BUILD @@ -52,7 +52,7 @@ tf_custom_op_library( deps = [ ":mpi_defines", ":mpi_message_proto_cc", - "//tensorflow/core:stream_executor_headers_lib", + "//tensorflow/stream_executor:stream_executor_headers_lib", "//third_party/mpi", ], ) diff --git a/tensorflow/stream_executor/platform/default/dso_loader.cc b/tensorflow/stream_executor/platform/default/dso_loader.cc index 668eeee3f3..0f0bce3253 100644 --- a/tensorflow/stream_executor/platform/default/dso_loader.cc +++ b/tensorflow/stream_executor/platform/default/dso_loader.cc @@ -28,7 +28,7 @@ limitations under the License. #include "tensorflow/stream_executor/lib/path.h" #include "tensorflow/stream_executor/lib/str_util.h" #include "tensorflow/stream_executor/lib/stringprintf.h" -#include "tensorflow/stream_executor/platform/default/dso_loader.h" +#include "tensorflow/stream_executor/platform/dso_loader.h" #include "tensorflow/stream_executor/platform/logging.h" #include "tensorflow/stream_executor/platform/port.h" -- GitLab From 6965d80c135a7c0008d2e17ce74529ab5798a5e4 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Fri, 4 Jan 2019 01:02:44 -0800 Subject: [PATCH 335/622] compat: Update forward compatibility horizon to 2019-01-04 PiperOrigin-RevId: 227817111 --- tensorflow/python/compat/compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/python/compat/compat.py b/tensorflow/python/compat/compat.py index c60c42ee65..66c4fa4893 100644 --- a/tensorflow/python/compat/compat.py +++ b/tensorflow/python/compat/compat.py @@ -27,7 +27,7 @@ import datetime from tensorflow.python.util import tf_contextlib from tensorflow.python.util.tf_export import tf_export -_FORWARD_COMPATIBILITY_HORIZON = datetime.date(2019, 1, 3) +_FORWARD_COMPATIBILITY_HORIZON = datetime.date(2019, 1, 4) @tf_export("compat.forward_compatible") -- GitLab From 8e4ca33c8b5861526bcdc43eb1af6d73e1670e92 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Fri, 4 Jan 2019 04:10:23 -0800 Subject: [PATCH 336/622] Correctly handle S32 and U32 in the AR-CRS combiner Some of the transforms we are doing are only valid on floating point types so we have to condition them on the element type of the operations. PiperOrigin-RevId: 227836047 --- tensorflow/compiler/xla/service/BUILD | 1 + .../compiler/xla/service/ar_crs_combiner.cc | 24 +++++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/tensorflow/compiler/xla/service/BUILD b/tensorflow/compiler/xla/service/BUILD index b84792cfc3..755c477a12 100644 --- a/tensorflow/compiler/xla/service/BUILD +++ b/tensorflow/compiler/xla/service/BUILD @@ -3678,6 +3678,7 @@ cc_library( ":pattern_matcher", "//tensorflow/compiler/xla:literal", "//tensorflow/compiler/xla:literal_util", + "//tensorflow/compiler/xla:shape_util", "//tensorflow/compiler/xla:status_macros", "//tensorflow/compiler/xla:statusor", "//tensorflow/compiler/xla:types", diff --git a/tensorflow/compiler/xla/service/ar_crs_combiner.cc b/tensorflow/compiler/xla/service/ar_crs_combiner.cc index 47d2c7e357..4a227d3b5c 100644 --- a/tensorflow/compiler/xla/service/ar_crs_combiner.cc +++ b/tensorflow/compiler/xla/service/ar_crs_combiner.cc @@ -26,6 +26,7 @@ limitations under the License. #include "tensorflow/compiler/xla/service/hlo_instruction.h" #include "tensorflow/compiler/xla/service/hlo_opcode.h" #include "tensorflow/compiler/xla/service/pattern_matcher.h" +#include "tensorflow/compiler/xla/shape_util.h" #include "tensorflow/compiler/xla/status_macros.h" #include "tensorflow/compiler/xla/types.h" #include "tensorflow/compiler/xla/xla_data.pb.h" @@ -44,11 +45,24 @@ bool MatchesArCrsPattern(HloInstruction* instruction) { if (instruction->user_count() != 1) { return false; } - auto opcode = instruction->opcode(); - return opcode == HloOpcode::kBitcast || opcode == HloOpcode::kTranspose || - opcode == HloOpcode::kReshape || opcode == HloOpcode::kConvert || - opcode == HloOpcode::kAdd || opcode == HloOpcode::kSubtract || - opcode == HloOpcode::kMultiply; + switch (instruction->opcode()) { + case HloOpcode::kBitcast: + case HloOpcode::kTranspose: + case HloOpcode::kReshape: + return true; + case HloOpcode::kConvert: + // Can be moved across if both input and output is either float or + // integer (e.g. S32<->U32 or F32<->BF16) + return ShapeUtil::ElementIsFloating(instruction->shape()) == + ShapeUtil::ElementIsFloating(instruction->operand(0)->shape()); + case HloOpcode::kAdd: + case HloOpcode::kSubtract: + case HloOpcode::kMultiply: + // Only supported for floating point operands. + return ShapeUtil::ElementIsFloating(instruction->shape()); + default: + return false; + } }; auto computation_is_addition = [](HloComputation* c) { -- GitLab From 0bc20afeec2b06437a7196b0d191d61f181cd81a Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Fri, 4 Jan 2019 07:27:10 -0800 Subject: [PATCH 337/622] Update RBE CPU Dockerfile with instructions on how to build/push. PiperOrigin-RevId: 227853285 --- tensorflow/tools/ci_build/Dockerfile.rbe.cpu | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tensorflow/tools/ci_build/Dockerfile.rbe.cpu b/tensorflow/tools/ci_build/Dockerfile.rbe.cpu index 7e5860aeec..500fb6e0b3 100644 --- a/tensorflow/tools/ci_build/Dockerfile.rbe.cpu +++ b/tensorflow/tools/ci_build/Dockerfile.rbe.cpu @@ -1,3 +1,8 @@ +# To push a new version, run: +# $ docker build -f Dockerfile.rbe.cpu \ +# --tag "gcr.io/tensorflow-testing/nosla-ubuntu16.04" . +# $ docker push gcr.io/tensorflow-testing/nosla-ubuntu16.04 + FROM launcher.gcr.io/google/rbe-ubuntu16-04:r327695 LABEL maintainer="Yu Yi " -- GitLab From bd2f78fd205faa739c657d26156bd27e02223df0 Mon Sep 17 00:00:00 2001 From: Scott Zhu Date: Fri, 4 Jan 2019 08:31:47 -0800 Subject: [PATCH 338/622] Make contrib.seq2seq more compatible with tf 2.0. (1/6) 1. Add a new loss object which wrap around the existing loss function, which use can use it keras model. 2. Slightly update the code for readability and reduce duplication. 3. Update the test case to be eager compatible. PiperOrigin-RevId: 227860442 --- .../seq2seq/python/kernel_tests/loss_test.py | 290 ++++++++++++++---- tensorflow/contrib/seq2seq/python/ops/loss.py | 109 +++++-- 2 files changed, 315 insertions(+), 84 deletions(-) diff --git a/tensorflow/contrib/seq2seq/python/kernel_tests/loss_test.py b/tensorflow/contrib/seq2seq/python/kernel_tests/loss_test.py index 5aa32b532f..41b2a53ca5 100644 --- a/tensorflow/contrib/seq2seq/python/kernel_tests/loss_test.py +++ b/tensorflow/contrib/seq2seq/python/kernel_tests/loss_test.py @@ -14,80 +14,254 @@ # ============================================================================== """Tests for contrib.seq2seq.python.seq2seq.loss_ops.""" -# pylint: disable=unused-import,g-bad-import-order from __future__ import absolute_import from __future__ import division from __future__ import print_function -# pylint: enable=unused-import import numpy as np from tensorflow.contrib.seq2seq.python.ops import loss from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops -from tensorflow.python.ops import init_ops -from tensorflow.python.ops import variable_scope from tensorflow.python.platform import test +@test_util.run_all_in_graph_and_eager_modes class LossTest(test.TestCase): + def setUp(self): + self.batch_size = 2 + self.sequence_length = 3 + self.number_of_classes = 5 + logits = [ + constant_op.constant(i + 0.5, shape=[self.batch_size, + self.number_of_classes]) + for i in range(self.sequence_length) + ] + self.logits = array_ops.stack(logits, axis=1) + targets = [ + constant_op.constant(i, dtypes.int32, shape=[self.batch_size]) + for i in range(self.sequence_length) + ] + self.targets = array_ops.stack(targets, axis=1) + weights = [ + constant_op.constant(1.0, shape=[self.batch_size]) + for _ in range(self.sequence_length) + ] + self.weights = array_ops.stack(weights, axis=1) + # expected_loss = sparse_softmax_cross_entropy_with_logits(targets, logits) + # where targets = [0, 1, 2], and logits = [[0.5] * 5, [1.5] * 5, [2.5] * 5] + self.expected_loss = 1.60944 + def testSequenceLoss(self): - with self.session(use_gpu=True) as sess: - with variable_scope.variable_scope( - 'root', initializer=init_ops.constant_initializer(0.5)): - batch_size = 2 - sequence_length = 3 - number_of_classes = 5 - logits = [ - constant_op.constant( - i + 0.5, shape=[batch_size, number_of_classes]) - for i in range(sequence_length) - ] - logits = array_ops.stack(logits, axis=1) - targets = [ - constant_op.constant( - i, dtypes.int32, shape=[batch_size]) - for i in range(sequence_length) - ] - targets = array_ops.stack(targets, axis=1) - weights = [ - constant_op.constant( - 1.0, shape=[batch_size]) for i in range(sequence_length) - ] - weights = array_ops.stack(weights, axis=1) - - average_loss_per_example = loss.sequence_loss( - logits, targets, weights, - average_across_timesteps=True, - average_across_batch=True) - res = sess.run(average_loss_per_example) - self.assertAllClose(1.60944, res) - - average_loss_per_sequence = loss.sequence_loss( - logits, targets, weights, - average_across_timesteps=False, - average_across_batch=True) - res = sess.run(average_loss_per_sequence) - compare_per_sequence = np.ones((sequence_length)) * 1.60944 - self.assertAllClose(compare_per_sequence, res) - - average_loss_per_batch = loss.sequence_loss( - logits, targets, weights, - average_across_timesteps=True, - average_across_batch=False) - res = sess.run(average_loss_per_batch) - compare_per_batch = np.ones((batch_size)) * 1.60944 - self.assertAllClose(compare_per_batch, res) - - total_loss = loss.sequence_loss( - logits, targets, weights, - average_across_timesteps=False, - average_across_batch=False) - res = sess.run(total_loss) - compare_total = np.ones((batch_size, sequence_length)) * 1.60944 - self.assertAllClose(compare_total, res) + with self.test_session(use_gpu=True): + average_loss_per_example = loss.sequence_loss( + self.logits, self.targets, self.weights, + average_across_timesteps=True, + average_across_batch=True) + res = self.evaluate(average_loss_per_example) + self.assertAllClose(self.expected_loss, res) + + average_loss_per_sequence = loss.sequence_loss( + self.logits, self.targets, self.weights, + average_across_timesteps=False, + average_across_batch=True) + res = self.evaluate(average_loss_per_sequence) + compare_per_sequence = np.full((self.sequence_length), self.expected_loss) + self.assertAllClose(compare_per_sequence, res) + + average_loss_per_batch = loss.sequence_loss( + self.logits, self.targets, self.weights, + average_across_timesteps=True, + average_across_batch=False) + res = self.evaluate(average_loss_per_batch) + compare_per_batch = np.full((self.batch_size), self.expected_loss) + self.assertAllClose(compare_per_batch, res) + + total_loss = loss.sequence_loss( + self.logits, self.targets, self.weights, + average_across_timesteps=False, + average_across_batch=False) + res = self.evaluate(total_loss) + compare_total = np.full((self.batch_size, self.sequence_length), + self.expected_loss) + self.assertAllClose(compare_total, res) + + def testSequenceLossClass(self): + with self.test_session(use_gpu=True): + seq_loss = loss.SequenceLoss(average_across_timesteps=True, + average_across_batch=True, + sum_over_timesteps=False, + sum_over_batch=False) + average_loss_per_example = seq_loss( + self.targets, self.logits, self.weights) + res = self.evaluate(average_loss_per_example) + self.assertAllClose(self.expected_loss, res) + + seq_loss = loss.SequenceLoss(average_across_timesteps=False, + average_across_batch=True, + sum_over_timesteps=False, + sum_over_batch=False) + average_loss_per_sequence = seq_loss( + self.targets, self.logits, self.weights) + res = self.evaluate(average_loss_per_sequence) + compare_per_sequence = np.full((self.sequence_length), self.expected_loss) + self.assertAllClose(compare_per_sequence, res) + + seq_loss = loss.SequenceLoss(average_across_timesteps=True, + average_across_batch=False, + sum_over_timesteps=False, + sum_over_batch=False) + average_loss_per_batch = seq_loss( + self.targets, self.logits, self.weights) + res = self.evaluate(average_loss_per_batch) + compare_per_batch = np.full((self.batch_size), self.expected_loss) + self.assertAllClose(compare_per_batch, res) + + seq_loss = loss.SequenceLoss(average_across_timesteps=False, + average_across_batch=False, + sum_over_timesteps=False, + sum_over_batch=False) + total_loss = seq_loss( + self.targets, self.logits, self.weights) + res = self.evaluate(total_loss) + compare_total = np.full((self.batch_size, self.sequence_length), + self.expected_loss) + self.assertAllClose(compare_total, res) + + def testSumReduction(self): + with self.test_session(use_gpu=True): + seq_loss = loss.SequenceLoss(average_across_timesteps=False, + average_across_batch=False, + sum_over_timesteps=True, + sum_over_batch=True) + average_loss_per_example = seq_loss( + self.targets, self.logits, self.weights) + res = self.evaluate(average_loss_per_example) + self.assertAllClose(self.expected_loss, res) + + seq_loss = loss.SequenceLoss(average_across_timesteps=False, + average_across_batch=False, + sum_over_timesteps=False, + sum_over_batch=True) + average_loss_per_sequence = seq_loss( + self.targets, self.logits, self.weights) + res = self.evaluate(average_loss_per_sequence) + compare_per_sequence = np.full((self.sequence_length), self.expected_loss) + self.assertAllClose(compare_per_sequence, res) + + seq_loss = loss.SequenceLoss(average_across_timesteps=False, + average_across_batch=False, + sum_over_timesteps=True, + sum_over_batch=False) + average_loss_per_batch = seq_loss( + self.targets, self.logits, self.weights) + res = self.evaluate(average_loss_per_batch) + compare_per_batch = np.full((self.batch_size), self.expected_loss) + self.assertAllClose(compare_per_batch, res) + + seq_loss = loss.SequenceLoss(average_across_timesteps=False, + average_across_batch=False, + sum_over_timesteps=False, + sum_over_batch=False) + total_loss = seq_loss( + self.targets, self.logits, self.weights) + res = self.evaluate(total_loss) + compare_total = np.full((self.batch_size, self.sequence_length), + self.expected_loss) + self.assertAllClose(compare_total, res) + + def testWeightedSumReduction(self): + weights = [ + constant_op.constant(1.0, shape=[self.batch_size]) + for _ in range(self.sequence_length) + ] + # Make the last element in the sequence to have zero weights. + weights[-1] = constant_op.constant(0.0, shape=[self.batch_size]) + self.weights = array_ops.stack(weights, axis=1) + with self.test_session(use_gpu=True): + seq_loss = loss.SequenceLoss(average_across_timesteps=False, + average_across_batch=False, + sum_over_timesteps=True, + sum_over_batch=True) + average_loss_per_example = seq_loss( + self.targets, self.logits, self.weights) + res = self.evaluate(average_loss_per_example) + self.assertAllClose(self.expected_loss, res) + + seq_loss = loss.SequenceLoss(average_across_timesteps=False, + average_across_batch=False, + sum_over_timesteps=False, + sum_over_batch=True) + average_loss_per_sequence = seq_loss( + self.targets, self.logits, self.weights) + res = self.evaluate(average_loss_per_sequence) + compare_per_sequence = np.full((self.sequence_length), self.expected_loss) + # The last element in every sequence are zeros, which will be filtered. + compare_per_sequence[-1] = 0. + self.assertAllClose(compare_per_sequence, res) + + seq_loss = loss.SequenceLoss(average_across_timesteps=False, + average_across_batch=False, + sum_over_timesteps=True, + sum_over_batch=False) + average_loss_per_batch = seq_loss(self.targets, self.logits, self.weights) + res = self.evaluate(average_loss_per_batch) + compare_per_batch = np.full((self.batch_size), self.expected_loss) + self.assertAllClose(compare_per_batch, res) + + seq_loss = loss.SequenceLoss(average_across_timesteps=False, + average_across_batch=False, + sum_over_timesteps=False, + sum_over_batch=False) + total_loss = seq_loss(self.targets, self.logits, self.weights) + res = self.evaluate(total_loss) + compare_total = np.full((self.batch_size, self.sequence_length), + self.expected_loss) + # The last element in every sequence are zeros, which will be filtered. + compare_total[:, -1] = 0 + self.assertAllClose(compare_total, res) + + def testZeroWeights(self): + weights = [ + constant_op.constant(0.0, shape=[self.batch_size]) + for _ in range(self.sequence_length) + ] + weights = array_ops.stack(weights, axis=1) + with self.test_session(use_gpu=True): + average_loss_per_example = loss.sequence_loss( + self.logits, self.targets, weights, + average_across_timesteps=True, + average_across_batch=True) + res = self.evaluate(average_loss_per_example) + self.assertAllClose(0.0, res) + + average_loss_per_sequence = loss.sequence_loss( + self.logits, self.targets, weights, + average_across_timesteps=False, + average_across_batch=True) + res = self.evaluate(average_loss_per_sequence) + compare_per_sequence = np.zeros((self.sequence_length)) + self.assertAllClose(compare_per_sequence, res) + + average_loss_per_batch = loss.sequence_loss( + self.logits, self.targets, weights, + average_across_timesteps=True, + average_across_batch=False) + res = self.evaluate(average_loss_per_batch) + compare_per_batch = np.zeros((self.batch_size)) + self.assertAllClose(compare_per_batch, res) + + total_loss = loss.sequence_loss( + self.logits, self.targets, weights, + average_across_timesteps=False, + average_across_batch=False) + res = self.evaluate(total_loss) + compare_total = np.zeros((self.batch_size, self.sequence_length)) + self.assertAllClose(compare_total, res) + if __name__ == '__main__': test.main() diff --git a/tensorflow/contrib/seq2seq/python/ops/loss.py b/tensorflow/contrib/seq2seq/python/ops/loss.py index 39a6d2f58b..0fbfd61870 100644 --- a/tensorflow/contrib/seq2seq/python/ops/loss.py +++ b/tensorflow/contrib/seq2seq/python/ops/loss.py @@ -20,11 +20,12 @@ from __future__ import division from __future__ import print_function from tensorflow.python.framework import ops +from tensorflow.python.keras.losses import Loss from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import nn_ops -__all__ = ["sequence_loss"] +__all__ = ["sequence_loss", "SequenceLoss"] def sequence_loss(logits, @@ -32,16 +33,26 @@ def sequence_loss(logits, weights, average_across_timesteps=True, average_across_batch=True, + sum_over_timesteps=False, + sum_over_batch=False, softmax_loss_function=None, name=None): """Weighted cross-entropy loss for a sequence of logits. - Depending on the values of `average_across_timesteps` and - `average_across_batch`, the return Tensor will have rank 0, 1, or 2 as these - arguments reduce the cross-entropy at each target, which has shape - `[batch_size, sequence_length]`, over their respective dimensions. For - example, if `average_across_timesteps` is `True` and `average_across_batch` - is `False`, then the return Tensor will have shape `[batch_size]`. + Depending on the values of `average_across_timesteps` / `sum_over_timesteps` + and `average_across_batch` / `sum_over_batch`, the return Tensor will have + rank 0, 1, or 2 as these arguments reduce the cross-entropy at each target, + which has shape `[batch_size, sequence_length]`, over their respective + dimensions. For example, if `average_across_timesteps` is `True` and + `average_across_batch` is `False`, then the return Tensor will have shape + `[batch_size]`. + + Note that `average_across_timesteps` and `sum_over_timesteps` cannot be True + at same time. Same for `average_across_batch` and `sum_over_batch`. + + The recommended loss reduction in tf 2.0 has been changed to sum_over, instead + of weighted average. User are recommend to use `sum_over_timesteps` and + `sum_over_batch` for reduction. Args: logits: A Tensor of shape @@ -58,6 +69,12 @@ def sequence_loss(logits, dimension and divide the cost by the total label weight across timesteps. average_across_batch: If set, sum the cost across the batch dimension and divide the returned cost by the batch size. + sum_over_timesteps: If set, sum the cost across the sequence dimension and + divide the size of the sequence. Note that any element with 0 weights will + be excluded from size calculation. + sum_over_batch: if set, sum the cost across the batch dimension and divide + the total cost by the batch size. Not that any element with 0 weights will + be excluded from size calculation. softmax_loss_function: Function (labels, logits) -> loss-batch to be used instead of the standard softmax (the default if this is None). **Note that to avoid confusion, it is required for the function to accept @@ -78,11 +95,15 @@ def sequence_loss(logits, raise ValueError("Logits must be a " "[batch_size x sequence_length x logits] tensor") if len(targets.get_shape()) != 2: - raise ValueError("Targets must be a [batch_size x sequence_length] " - "tensor") + raise ValueError("Targets must be a [batch_size x sequence_length] tensor") if len(weights.get_shape()) != 2: - raise ValueError("Weights must be a [batch_size x sequence_length] " - "tensor") + raise ValueError("Weights must be a [batch_size x sequence_length] tensor") + if average_across_timesteps and sum_over_timesteps: + raise ValueError("average_across_timesteps and sum_over_timesteps cannot " + "be set to True at same time.") + if average_across_batch and sum_over_batch: + raise ValueError("average_across_batch and sum_over_batch cannot be set " + "to True at same time.") with ops.name_scope(name, "sequence_loss", [logits, targets, weights]): num_classes = array_ops.shape(logits)[2] logits_flat = array_ops.reshape(logits, [-1, num_classes]) @@ -96,20 +117,56 @@ def sequence_loss(logits, if average_across_timesteps and average_across_batch: crossent = math_ops.reduce_sum(crossent) total_size = math_ops.reduce_sum(weights) - total_size += 1e-12 # to avoid division by 0 for all-0 weights - crossent /= total_size + crossent = math_ops.div_no_nan(crossent, total_size) + elif sum_over_timesteps and sum_over_batch: + crossent = math_ops.reduce_sum(crossent) + total_count = math_ops.cast(math_ops.count_nonzero(weights), + crossent.dtype) + crossent = math_ops.div_no_nan(crossent, total_count) else: - batch_size = array_ops.shape(logits)[0] - sequence_length = array_ops.shape(logits)[1] - crossent = array_ops.reshape(crossent, [batch_size, sequence_length]) - if average_across_timesteps and not average_across_batch: - crossent = math_ops.reduce_sum(crossent, axis=[1]) - total_size = math_ops.reduce_sum(weights, axis=[1]) - total_size += 1e-12 # to avoid division by 0 for all-0 weights - crossent /= total_size - if not average_across_timesteps and average_across_batch: - crossent = math_ops.reduce_sum(crossent, axis=[0]) - total_size = math_ops.reduce_sum(weights, axis=[0]) - total_size += 1e-12 # to avoid division by 0 for all-0 weights - crossent /= total_size + crossent = array_ops.reshape(crossent, array_ops.shape(logits)[0:2]) + if average_across_timesteps or average_across_batch: + reduce_axis = [0] if average_across_batch else [1] + crossent = math_ops.reduce_sum(crossent, axis=reduce_axis) + total_size = math_ops.reduce_sum(weights, axis=reduce_axis) + crossent = math_ops.div_no_nan(crossent, total_size) + elif sum_over_timesteps or sum_over_batch: + reduce_axis = [0] if sum_over_batch else [1] + crossent = math_ops.reduce_sum(crossent, axis=reduce_axis) + total_count = math_ops.cast( + math_ops.count_nonzero(weights, axis=reduce_axis), + dtype=crossent.dtype) + crossent = math_ops.div_no_nan(crossent, total_count) return crossent + + +class SequenceLoss(Loss): + """Weighted cross-entropy loss for a sequence of logits.""" + + def __init__(self, + average_across_timesteps=False, + average_across_batch=False, + sum_over_timesteps=True, + sum_over_batch=True, + softmax_loss_function=None, + name=None): + super(SequenceLoss, self).__init__(name=name) + self.average_across_timesteps = average_across_timesteps + self.average_across_batch = average_across_batch + self.sum_over_timesteps = sum_over_timesteps + self.sum_over_batch = sum_over_batch + self.softmax_loss_function = softmax_loss_function + + def __call__(self, y_true, y_pred, sample_weight=None): + """Override the parent __call__ to have a customized reduce behavior.""" + return sequence_loss(y_pred, y_true, sample_weight, + average_across_timesteps=self.average_across_timesteps, + average_across_batch=self.average_across_batch, + sum_over_timesteps=self.sum_over_timesteps, + sum_over_batch=self.sum_over_batch, + softmax_loss_function=self.softmax_loss_function, + name=self.name) + + def call(self, y_true, y_pred): + # Skip this method since the __call__ contains real implementation. + pass -- GitLab From 533d43705aa018d3bd07dc448ad63625776be63c Mon Sep 17 00:00:00 2001 From: "Joshua V. Dillon" Date: Fri, 4 Jan 2019 09:13:05 -0800 Subject: [PATCH 339/622] Remove tf.contrib.distributions seq2seq dependecy. PiperOrigin-RevId: 227865533 --- tensorflow/contrib/seq2seq/BUILD | 2 - .../python/kernel_tests/basic_decoder_test.py | 6 +- .../contrib/seq2seq/python/ops/helper.py | 86 ++++++++++++++++--- 3 files changed, 76 insertions(+), 18 deletions(-) diff --git a/tensorflow/contrib/seq2seq/BUILD b/tensorflow/contrib/seq2seq/BUILD index 18b56cd219..89176180ae 100644 --- a/tensorflow/contrib/seq2seq/BUILD +++ b/tensorflow/contrib/seq2seq/BUILD @@ -33,7 +33,6 @@ tf_custom_op_py_library( srcs_version = "PY2AND3", deps = [ ":beam_search_ops", - "//tensorflow/contrib/distributions:distributions_py", "//tensorflow/contrib/layers:layers_py", "//tensorflow/contrib/rnn:rnn_py", "//tensorflow/contrib/util:util_py", @@ -59,7 +58,6 @@ tf_custom_op_py_library( "//tensorflow/python:tensor_util", "//tensorflow/python:util", "//tensorflow/python:variable_scope", - "//tensorflow/python/ops/distributions", "//third_party/py/numpy", "@six_archive//:six", ], diff --git a/tensorflow/contrib/seq2seq/python/kernel_tests/basic_decoder_test.py b/tensorflow/contrib/seq2seq/python/kernel_tests/basic_decoder_test.py index b7f9f3fb09..abcf71c61b 100644 --- a/tensorflow/contrib/seq2seq/python/kernel_tests/basic_decoder_test.py +++ b/tensorflow/contrib/seq2seq/python/kernel_tests/basic_decoder_test.py @@ -34,8 +34,6 @@ from tensorflow.python.ops import math_ops from tensorflow.python.ops import rnn_cell from tensorflow.python.ops import variables from tensorflow.python.ops import variable_scope -from tensorflow.python.ops.distributions import bernoulli -from tensorflow.python.ops.distributions import categorical from tensorflow.python.platform import test # pylint: enable=g-import-not-at-top @@ -517,7 +515,7 @@ class BasicDecoderTest(test.TestCase): vocabulary_size) # The sample function samples categorically from the logits. - sample_fn = lambda x: categorical.Categorical(logits=x).sample() + sample_fn = lambda x: helper_py.categorical_sample(logits=x) # The next inputs are a one-hot encoding of the sampled labels. next_inputs_fn = ( lambda x: array_ops.one_hot(x, vocabulary_size, dtype=dtypes.float32)) @@ -599,7 +597,7 @@ class BasicDecoderTest(test.TestCase): # The sample function samples independent bernoullis from the logits. sample_fn = ( - lambda x: bernoulli.Bernoulli(logits=x, dtype=dtypes.bool).sample()) + lambda x: helper_py.bernoulli_sample(logits=x, dtype=dtypes.bool)) # The next inputs are a one-hot encoding of the sampled labels. next_inputs_fn = math_ops.to_float end_fn = lambda sample_ids: sample_ids[:, end_token] diff --git a/tensorflow/contrib/seq2seq/python/ops/helper.py b/tensorflow/contrib/seq2seq/python/ops/helper.py index 3245cc5e72..033c2eb080 100644 --- a/tensorflow/contrib/seq2seq/python/ops/helper.py +++ b/tensorflow/contrib/seq2seq/python/ops/helper.py @@ -32,9 +32,8 @@ from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import embedding_ops from tensorflow.python.ops import gen_array_ops from tensorflow.python.ops import math_ops +from tensorflow.python.ops import random_ops from tensorflow.python.ops import tensor_array_ops -from tensorflow.python.ops.distributions import bernoulli -from tensorflow.python.ops.distributions import categorical from tensorflow.python.util import nest __all__ = [ @@ -51,6 +50,68 @@ __all__ = [ _transpose_batch_time = decoder._transpose_batch_time # pylint: disable=protected-access +# The following sample functions (_call_sampler, bernoulli_sample, +# categorical_sample) mimic TensorFlow Probability distribution semantics. + + +def _call_sampler(sample_n_fn, sample_shape, name=None): + """Reshapes vector of samples.""" + with ops.name_scope(name, "call_sampler", values=[sample_shape]): + sample_shape = ops.convert_to_tensor( + sample_shape, dtype=dtypes.int32, name="sample_shape") + # Ensure sample_shape is a vector (vs just a scalar). + pad = math_ops.cast(math_ops.equal(array_ops.rank(sample_shape), 0), + dtypes.int32) + sample_shape = array_ops.reshape( + sample_shape, + array_ops.pad(array_ops.shape(sample_shape), + paddings=[[pad, 0]], + constant_values=1)) + samples = sample_n_fn(math_ops.reduce_prod(sample_shape)) + batch_event_shape = array_ops.shape(samples)[1:] + final_shape = array_ops.concat([sample_shape, batch_event_shape], 0) + return array_ops.reshape(samples, final_shape) + + +def bernoulli_sample(probs=None, logits=None, dtype=dtypes.int32, + sample_shape=(), seed=None): + """Samples from Bernoulli distribution.""" + if probs is None: + probs = math_ops.sigmoid(logits, name="probs") + else: + probs = ops.convert_to_tensor(probs, name="probs") + batch_shape_tensor = array_ops.shape(probs) + def _sample_n(n): + """Sample vector of Bernoullis.""" + new_shape = array_ops.concat([[n], batch_shape_tensor], 0) + uniform = random_ops.random_uniform( + new_shape, seed=seed, dtype=probs.dtype) + return math_ops.cast(math_ops.less(uniform, probs), dtype) + return _call_sampler(_sample_n, sample_shape) + + +def categorical_sample(logits, dtype=dtypes.int32, + sample_shape=(), seed=None): + """Samples from categorical distribution.""" + logits = ops.convert_to_tensor(logits, name="logits") + event_size = array_ops.shape(logits)[-1] + batch_shape_tensor = array_ops.shape(logits)[:-1] + def _sample_n(n): + """Sample vector of categoricals.""" + if logits.shape.ndims == 2: + logits_2d = logits + else: + logits_2d = array_ops.reshape(logits, [-1, event_size]) + sample_dtype = dtypes.int64 if logits.dtype.size > 4 else dtypes.int32 + draws = random_ops.multinomial( + logits_2d, n, seed=seed, output_dtype=sample_dtype) + draws = array_ops.reshape( + array_ops.transpose(draws), + array_ops.concat([[n], batch_shape_tensor], 0)) + return math_ops.cast(draws, dtype) + return _call_sampler(_sample_n, sample_shape) + + def _unstack_ta(inp): return tensor_array_ops.TensorArray( dtype=inp.dtype, size=array_ops.shape(inp)[0], @@ -307,14 +368,14 @@ class ScheduledEmbeddingTrainingHelper(TrainingHelper): with ops.name_scope(name, "ScheduledEmbeddingTrainingHelperSample", [time, outputs, state]): # Return -1s where we did not sample, and sample_ids elsewhere - select_sampler = bernoulli.Bernoulli( - probs=self._sampling_probability, dtype=dtypes.bool) - select_sample = select_sampler.sample( - sample_shape=self.batch_size, seed=self._scheduling_seed) - sample_id_sampler = categorical.Categorical(logits=outputs) + select_sample = bernoulli_sample( + probs=self._sampling_probability, + dtype=dtypes.bool, + sample_shape=self.batch_size, + seed=self._scheduling_seed) return array_ops.where( select_sample, - sample_id_sampler.sample(seed=self._seed), + categorical_sample(logits=outputs, seed=self._seed), gen_array_ops.fill([self.batch_size], -1)) def next_inputs(self, time, outputs, state, sample_ids, name=None): @@ -425,8 +486,10 @@ class ScheduledOutputTrainingHelper(TrainingHelper): def sample(self, time, outputs, state, name=None): with ops.name_scope(name, "ScheduledOutputTrainingHelperSample", [time, outputs, state]): - sampler = bernoulli.Bernoulli(probs=self._sampling_probability) - return sampler.sample(sample_shape=self.batch_size, seed=self._seed) + return bernoulli_sample( + probs=self._sampling_probability, + sample_shape=self.batch_size, + seed=self._seed) def next_inputs(self, time, outputs, state, sample_ids, name=None): with ops.name_scope(name, "ScheduledOutputTrainingHelperNextInputs", @@ -610,8 +673,7 @@ class SampleEmbeddingHelper(GreedyEmbeddingHelper): else: logits = outputs / self._softmax_temperature - sample_id_sampler = categorical.Categorical(logits=logits) - sample_ids = sample_id_sampler.sample(seed=self._seed) + sample_ids = categorical_sample(logits=logits, seed=self._seed) return sample_ids -- GitLab From 586c507c879c816eb001bfb32a14deb7053640a6 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Fri, 4 Jan 2019 09:58:03 -0800 Subject: [PATCH 340/622] [tfgan] Adds GANEstimator wrapper that allows backpropagation to input latent space. PiperOrigin-RevId: 227871387 --- tensorflow/contrib/gan/BUILD | 36 +++ .../contrib/gan/python/estimator/__init__.py | 5 +- .../estimator/python/latent_gan_estimator.py | 28 +++ .../python/latent_gan_estimator_impl.py | 205 ++++++++++++++++++ .../python/latent_gan_estimator_test.py | 119 ++++++++++ 5 files changed, 392 insertions(+), 1 deletion(-) create mode 100644 tensorflow/contrib/gan/python/estimator/python/latent_gan_estimator.py create mode 100644 tensorflow/contrib/gan/python/estimator/python/latent_gan_estimator_impl.py create mode 100644 tensorflow/contrib/gan/python/estimator/python/latent_gan_estimator_test.py diff --git a/tensorflow/contrib/gan/BUILD b/tensorflow/contrib/gan/BUILD index 0626875b76..08df38ad27 100644 --- a/tensorflow/contrib/gan/BUILD +++ b/tensorflow/contrib/gan/BUILD @@ -107,6 +107,7 @@ py_library( deps = [ ":gan_estimator", ":head", + ":latent_gan_estimator", ":stargan_estimator", ":tpu_gan_estimator", "//tensorflow/python:util", @@ -643,6 +644,41 @@ py_test( ], ) +py_library( + name = "latent_gan_estimator", + srcs = [ + "python/estimator/python/latent_gan_estimator.py", + "python/estimator/python/latent_gan_estimator_impl.py", + ], + srcs_version = "PY2AND3", + deps = [ + ":train", + "//tensorflow/python:clip_ops", + "//tensorflow/python:gradients", + "//tensorflow/python:random_ops", + "//tensorflow/python:summary", + "//tensorflow/python:training_util", + "//tensorflow/python:variable_scope", + "//tensorflow/python/estimator:estimator_py", + ], +) + +py_test( + name = "latent_gan_estimator_test", + srcs = [ + "python/estimator/python/latent_gan_estimator_test.py", + ], + srcs_version = "PY2AND3", + deps = [ + ":latent_gan_estimator", + "//tensorflow/python:array_ops", + "//tensorflow/python:training", + "//tensorflow/python:variable_scope", + "//tensorflow/python/estimator:run_config", + "//tensorflow/python/ops/losses", + ], +) + py_library( name = "sliced_wasserstein", srcs = [ diff --git a/tensorflow/contrib/gan/python/estimator/__init__.py b/tensorflow/contrib/gan/python/estimator/__init__.py index 03a639165b..430266555b 100644 --- a/tensorflow/contrib/gan/python/estimator/__init__.py +++ b/tensorflow/contrib/gan/python/estimator/__init__.py @@ -26,11 +26,13 @@ from __future__ import print_function # pylint: disable=unused-import,wildcard-import from tensorflow.contrib.gan.python.estimator.python import gan_estimator from tensorflow.contrib.gan.python.estimator.python import head +from tensorflow.contrib.gan.python.estimator.python import latent_gan_estimator from tensorflow.contrib.gan.python.estimator.python import stargan_estimator from tensorflow.contrib.gan.python.estimator.python import tpu_gan_estimator from tensorflow.contrib.gan.python.estimator.python.gan_estimator import * from tensorflow.contrib.gan.python.estimator.python.head import * +from tensorflow.contrib.gan.python.estimator.python.latent_gan_estimator import * from tensorflow.contrib.gan.python.estimator.python.stargan_estimator import * from tensorflow.contrib.gan.python.estimator.python.tpu_gan_estimator import * # pylint: enable=unused-import,wildcard-import @@ -41,7 +43,8 @@ _allowed_symbols = ([ 'gan_estimator', 'stargan_estimator', 'tpu_gan_estimator', + 'latent_gan_estimator', 'head', ] + gan_estimator.__all__ + stargan_estimator.__all__ + head.__all__ + - tpu_gan_estimator.__all__) + tpu_gan_estimator.__all__ + latent_gan_estimator.__all__) remove_undocumented(__name__, _allowed_symbols) diff --git a/tensorflow/contrib/gan/python/estimator/python/latent_gan_estimator.py b/tensorflow/contrib/gan/python/estimator/python/latent_gan_estimator.py new file mode 100644 index 0000000000..4e164e2416 --- /dev/null +++ b/tensorflow/contrib/gan/python/estimator/python/latent_gan_estimator.py @@ -0,0 +1,28 @@ +# 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. +# ============================================================================== +"""`tf.Learn` components for `Train Input Estimator`.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.contrib.gan.python.estimator.python import latent_gan_estimator_impl +# pylint: disable=wildcard-import +from tensorflow.contrib.gan.python.estimator.python.latent_gan_estimator_impl import * +# pylint: enable=wildcard-import +from tensorflow.python.util.all_util import remove_undocumented + +__all__ = latent_gan_estimator_impl.__all__ +remove_undocumented(__name__, __all__) diff --git a/tensorflow/contrib/gan/python/estimator/python/latent_gan_estimator_impl.py b/tensorflow/contrib/gan/python/estimator/python/latent_gan_estimator_impl.py new file mode 100644 index 0000000000..f5afc77319 --- /dev/null +++ b/tensorflow/contrib/gan/python/estimator/python/latent_gan_estimator_impl.py @@ -0,0 +1,205 @@ +# 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. +# ============================================================================== +"""Implements an estimator wrapper that allows training the input latent space. + +This file implements a latent gan estimator that wraps around a previously +trained GAN. The latent gan estimator trains a single variable z, representing +the hidden latent distribution that is the 'noise' input to the GAN. By training +z, the inpainting estimator can move around the latent z space towards +minimizing a specific loss function. + +The latent gan estimator has a few key differences from a normal estimator. + +First: the variables in the estimator should not be saved, as we are not +updating the original GAN and are only adding a new z variable that is meant +to be different for each run. In order to do distributed training using +train_and_evaluate, the Tensorflow RunConfig is expected to save checkpoints +by having either save_checkpoints_steps or save_checkpoints_secs saved. +To avoid this conflict, we purposely set the save_checkpoints_steps value in +the RunConfig to be one step more than the total number of steps that the +inpainter estimator will run. + +Second: we need to specify warm start settings, as we are reloading the +GAN model into a different graph (specifically, one with a new z variable). +The warm start settings defined below reload all GAN variables and ignore the +new z variable (and the optimizer). + +Usage: + + def _generator(net, mode): + ... + + def _discriminator(net, condition, mode): + ... + + def _loss(gan_model, features, labels, add_summaries): + ... + + def optimizer(): + ... + + params = {} + config = tf.estimator.RunConfig() + tmp_dir = path/to/output/storage + + estimator = latent_gan_estimator.get_latent_gan_estimator( + _generator, _discriminator, _loss, optimizer, params, config, tmp_dir) + + def input_fn(): + ... + + estimator.train(input_fn=input_fn) + +See latent_gan_estimator_test.py or tensorflow_models/gan/face_inpainting for +further examples. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import functools +from tensorflow.contrib.gan.python import train as tfgan_train +from tensorflow.python.estimator import estimator +from tensorflow.python.estimator import model_fn as model_fn_lib +from tensorflow.python.ops import clip_ops +from tensorflow.python.ops import gradients_impl +from tensorflow.python.ops import random_ops +from tensorflow.python.ops import variable_scope +from tensorflow.python.summary import summary +from tensorflow.python.training import training_util + + +INPUT_NAME = 'new_var_z_input' # The name for the new z space input variable. +OPTIMIZER_NAME = 'latent_gan_optimizer' # The name for the new optimizer vars. + +__all__ = [ + 'get_latent_gan_estimator', +] + + +def _get_latent_gan_model_fn(generator_fn, discriminator_fn, loss_fn, + optimizer): + """Sets up a model function that wraps around a given GAN.""" + def model_fn(features, labels, mode, params): + """Model function defining an inpainting estimator.""" + batch_size = params['batch_size'] + z_shape = [batch_size] + params['z_shape'] + add_summaries = params['add_summaries'] + input_clip = params['input_clip'] + + z = variable_scope.get_variable( + name=INPUT_NAME, initializer=random_ops.truncated_normal(z_shape), + constraint=lambda x: clip_ops.clip_by_value(x, -input_clip, input_clip)) + + generator = functools.partial(generator_fn, mode=mode) + discriminator = functools.partial(discriminator_fn, mode=mode) + gan_model = tfgan_train.gan_model(generator_fn=generator, + discriminator_fn=discriminator, + real_data=labels, + generator_inputs=z, + check_shapes=False) + + loss = loss_fn(gan_model, features, labels, add_summaries) + + # Use a variable scope to make sure that estimator variables dont cause + # save/load problems when restoring from ckpts. + with variable_scope.variable_scope(OPTIMIZER_NAME): + opt = optimizer(learning_rate=params['learning_rate'], + **params['opt_kwargs']) + train_op = opt.minimize( + loss=loss, global_step=training_util.get_or_create_global_step(), + var_list=[z]) + + if add_summaries: + z_grads = gradients_impl.gradients(loss, z) + summary.scalar('z_loss/z_grads', clip_ops.global_norm(z_grads)) + summary.scalar('z_loss/loss', loss) + + return model_fn_lib.EstimatorSpec(mode=mode, + predictions=gan_model.generated_data, + loss=loss, + train_op=train_op) + return model_fn + + +def get_latent_gan_estimator(generator_fn, discriminator_fn, loss_fn, + optimizer, params, config, ckpt_dir, + warmstart_options=True): + """Gets an estimator that passes gradients to the input. + + This function takes in a generator and adds a trainable z variable that is + used as input to this generator_fn. The generator itself is treated as a black + box through which gradients can pass through without updating any weights. The + result is a trainable way to traverse the GAN latent space. The loss_fn is + used to actually train the z variable. The generator_fn and discriminator_fn + should be previously trained by the tfgan library (on reload, the variables + are expected to follow the tfgan format. It may be possible to use the + latent gan estimator with entirely custom GANs that do not use the tfgan + library as long as the appropriate variables are wired properly). + + Args: + generator_fn: a function defining a Tensorflow graph for a GAN generator. + The weights defined in this graph should already be defined in the given + checkpoint location. Should have 'mode' as an argument. + discriminator_fn: a function defining a Tensorflow graph for a GAN + discriminator. Should have 'mode' as an argument. + loss_fn: a function defining a Tensorflow graph for a GAN loss. Takes in a + GANModel tuple, features, labels, and add_summaries as inputs. + optimizer: a tf.Optimizer or a function that returns a tf.Optimizer with no + inputs. + params: An object containing the following parameters: + - batch_size: an int indicating the size of the training batch. + - z_shape: the desired shape of the input z values (not counting batch). + - learning_rate: a scalar or function defining a learning rate applied to + optimizer. + - input_clip: the amount to clip the x training variable by. + - add_summaries: whether or not to add summaries. + - opt_kwargs: optimizer kwargs. + config: tf.RunConfig. Should point model to output dir and should indicate + whether to save checkpoints (to avoid saving checkpoints, set + save_checkpoints_steps to a number larger than the number of train steps). + The model_dir field in the RunConfig should point to a directory WITHOUT + any saved checkpoints. + ckpt_dir: the directory where the model checkpoints live. The checkpoint is + used to warm start the underlying GAN. This should NOT be the same as + config.model_dir. + warmstart_options: boolean, None, or a WarmStartSettings object. If set to + True, uses a default WarmStartSettings object. If set to False or None, + does not use warm start. If using a custom WarmStartSettings object, make + sure that new variables are properly accounted for when reloading the + underlying GAN. Defaults to True. + Returns: + An estimator spec defining a GAN input training estimator. + """ + model_fn = _get_latent_gan_model_fn(generator_fn, discriminator_fn, + loss_fn, optimizer) + + if isinstance(warmstart_options, estimator.WarmStartSettings): + ws = warmstart_options + elif warmstart_options: + # Default WarmStart loads all variable names except INPUT_NAME and + # OPTIMIZER_NAME. + var_regex = '^(?!.*(%s|%s).*)' % (INPUT_NAME, OPTIMIZER_NAME) + ws = estimator.WarmStartSettings(ckpt_to_initialize_from=ckpt_dir, + vars_to_warm_start=var_regex) + else: + ws = None + + if 'opt_kwargs' not in params: + params['opt_kwargs'] = {} + + return estimator.Estimator(model_fn=model_fn, config=config, params=params, + warm_start_from=ws) diff --git a/tensorflow/contrib/gan/python/estimator/python/latent_gan_estimator_test.py b/tensorflow/contrib/gan/python/estimator/python/latent_gan_estimator_test.py new file mode 100644 index 0000000000..ac139e532e --- /dev/null +++ b/tensorflow/contrib/gan/python/estimator/python/latent_gan_estimator_test.py @@ -0,0 +1,119 @@ +# 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. +# ============================================================================== +"""Tests for latent_gan_estimator. + +See g3.tp.tensorflow.contrib.gan.python.estimator.python.latent_gan_estimator. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import tempfile +import numpy as np +from tensorflow.contrib.gan.python.estimator.python import latent_gan_estimator +from tensorflow.python.estimator import run_config as run_config +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import variable_scope +from tensorflow.python.ops.losses import losses +from tensorflow.python.platform import test +from tensorflow.python.training import training + + +class TrainInputEstimatorTest(test.TestCase): + + def test_get_input_training_estimator(self): + """Integration test to make sure the input_training_estimator works.""" + + # Create dummy test input tensors. + true_features = np.reshape(np.random.uniform(size=100), (10, 10)) + true_labels = np.reshape(np.random.uniform(size=100), (5, 20)) + expected_z_output = [[1, -1], [-1, 1]] + + # Fill out required parameters randomly, includes optimizer kwargs. + params = { + 'batch_size': 2, + 'z_shape': [2], + 'learning_rate': 1.0, + 'input_clip': 1.0, + 'add_summaries': False, + 'opt_kwargs': { + 'beta1': 0.1 + } + } + + input_z_shape = [params['batch_size']] + params['z_shape'] + + # Create dummy model functions that represent an underlying GANEstimator and + # the input training wrapper. Make sure that everything is wired up + # correctly in the internals of each dummy function. + def _generator(net, mode): + """The generator function will get the newly created z variable.""" + del mode + self.assertSequenceEqual(net.shape, input_z_shape) + gen_dummy_var = variable_scope.get_variable( + name='generator_dummy_variable', + initializer=array_ops.ones(input_z_shape)) + return net * gen_dummy_var + + def _discriminator(net, condition, mode): + """The discriminator function will get either the z variable or labels.""" + del condition, mode + try: + self.assertSequenceEqual(net.shape, true_labels.shape) + except AssertionError: + self.assertSequenceEqual(net.shape, input_z_shape) + return net + + def _loss(gan_model, features, labels, _): + """Make sure that features and labels are passed in from input.""" + self.assertTrue(np.array_equal(features, true_features)) + self.assertTrue(np.array_equal(labels, true_labels)) + return losses.absolute_difference(expected_z_output, + gan_model.generated_data) + + optimizer = training.AdamOptimizer + + # We are not loading checkpoints, so set the corresponding directory to a + # dummy directories. + tmp_dir = tempfile.mkdtemp() + config = run_config.RunConfig(model_dir=tmp_dir, + save_summary_steps=None, + save_checkpoints_steps=1, + save_checkpoints_secs=None) + + # Get the estimator. Disable warm start so that there is no attempted + # checkpoint reloading. + estimator = latent_gan_estimator.get_latent_gan_estimator( + _generator, _discriminator, _loss, optimizer, params, config, tmp_dir, + warmstart_options=None) + + # Train for a few steps. + def dummy_input(): + return true_features, true_labels + estimator.train(input_fn=dummy_input, steps=10) + + # Make sure the generator variables did not change, but the z variables did + # change. + self.assertTrue(np.array_equal( + estimator.get_variable_value('Generator/generator_dummy_variable'), + np.ones(input_z_shape))) + self.assertTrue(np.array_equal( + estimator.get_variable_value('new_var_z_input'), + expected_z_output)) + + +if __name__ == '__main__': + test.main() -- GitLab From df01a441b477b20346c5cbf8248018798b75ff0f Mon Sep 17 00:00:00 2001 From: Sergei Lebedev Date: Fri, 4 Jan 2019 10:04:36 -0800 Subject: [PATCH 341/622] Decoupled ResourceVariable from RefVariable * RefVariable._try_guard_against_uninitialized_dependencies and related methods are now functions used by both variables subclasses * Removed implicit chaining of ResourceVariable ->Tensor convertor with that of RefVariable. Previously, ResourceVariable._dense_var_to_tensor would return NotImplemented if called with a different dtype. This effectively delegated the conversion to RefVariable._TensorConversionFunction which allowed compatible but not exactly matching dtypes. The change makes this behavior explicit in ResourceVariable._dense_var_to_tensor. PiperOrigin-RevId: 227872550 --- .../feature_column/feature_column_v2_test.py | 8 +- .../python/ops/resource_variable_ops.py | 109 ++++-- tensorflow/python/ops/variables.py | 316 +++++++++--------- 3 files changed, 251 insertions(+), 182 deletions(-) diff --git a/tensorflow/python/feature_column/feature_column_v2_test.py b/tensorflow/python/feature_column/feature_column_v2_test.py index a247425369..253e4c322b 100644 --- a/tensorflow/python/feature_column/feature_column_v2_test.py +++ b/tensorflow/python/feature_column/feature_column_v2_test.py @@ -2015,7 +2015,7 @@ class LinearModelTest(test.TestCase): } model(features) for var in model.variables: - self.assertTrue(isinstance(var, variables_lib.RefVariable)) + self.assertIsInstance(var, variables_lib.VariableV1) variable_names = [var.name for var in model.variables] self.assertItemsEqual([ 'linear_model/dense_feature_bucketized/weights:0', @@ -4010,7 +4010,7 @@ class FunctionalInputLayerTest(test.TestCase): self.assertEqual(0, len(cols_to_vars[dense_feature_bucketized])) self.assertEqual(1, len(cols_to_vars[some_embedding_column])) self.assertIsInstance(cols_to_vars[some_embedding_column][0], - variables_lib.Variable) + variables_lib.VariableV1) self.assertAllEqual(cols_to_vars[some_embedding_column][0].shape, [5, 10]) @test_util.run_deprecated_v1 @@ -6839,7 +6839,7 @@ class EmbeddingColumnTest(test.TestCase): self.assertItemsEqual(('dense_features/aaa_embedding/embedding_weights:0',), tuple([v.name for v in global_vars])) for v in global_vars: - self.assertTrue(isinstance(v, variables_lib.RefVariable)) + self.assertIsInstance(v, variables_lib.Variable) trainable_vars = ops.get_collection(ops.GraphKeys.TRAINABLE_VARIABLES) self.assertItemsEqual(('dense_features/aaa_embedding/embedding_weights:0',), tuple([v.name for v in trainable_vars])) @@ -7732,7 +7732,7 @@ class SharedEmbeddingColumnTest(test.TestCase): ['aaa_bbb_shared_embedding:0', 'ccc_ddd_shared_embedding:0'], tuple([v.name for v in global_vars])) for v in global_vars: - self.assertTrue(isinstance(v, variables_lib.RefVariable)) + self.assertIsInstance(v, variables_lib.Variable) trainable_vars = ops.get_collection(ops.GraphKeys.TRAINABLE_VARIABLES) if trainable: self.assertItemsEqual( diff --git a/tensorflow/python/ops/resource_variable_ops.py b/tensorflow/python/ops/resource_variable_ops.py index 3e26463ddd..ede4bc0078 100644 --- a/tensorflow/python/ops/resource_variable_ops.py +++ b/tensorflow/python/ops/resource_variable_ops.py @@ -36,6 +36,7 @@ from tensorflow.python.ops import gen_array_ops from tensorflow.python.ops import gen_resource_variable_ops from tensorflow.python.ops import gen_state_ops from tensorflow.python.ops import math_ops +from tensorflow.python.ops import state_ops from tensorflow.python.ops import variables # go/tf-wildcard-import # pylint: disable=wildcard-import @@ -160,8 +161,7 @@ def shape_safe_assign_variable_handle(handle, shape, value, name=None): name=name) -# TODO(apassos) make this be variables.Variable -class ResourceVariable(variables.RefVariable): +class ResourceVariable(variables.VariableV1): """Variable based on resource handles. See the [Variables How To](https://tensorflow.org/guide/variables) @@ -297,6 +297,15 @@ class ResourceVariable(variables.RefVariable): dtype=dtype, constraint=constraint) + def __repr__(self): + if context.executing_eagerly() and not self._in_graph_mode: + return "" % ( + self.name, self.get_shape(), self.dtype.name, + ops.numpy_text(self.read_value(), is_repr=True)) + else: + return "" % ( + self.name, self.get_shape(), self.dtype.name) + # pylint: disable=unused-argument def _init_from_args(self, initial_value=None, @@ -437,12 +446,15 @@ class ResourceVariable(variables.RefVariable): gen_resource_variable_ops.var_is_initialized_op(self._handle)) if initial_value is not None: with ops.name_scope("Assign") as n, ops.colocate_with(self._handle): + # pylint: disable=protected-access self._initializer_op = ( gen_resource_variable_ops.assign_variable_op( self._handle, - self._try_guard_against_uninitialized_dependencies( + variables._try_guard_against_uninitialized_dependencies( + name, initial_value), name=n)) + # pylint: enable=protected-access with ops.name_scope("Read"), ops.colocate_with(self._handle): # Manually assign reads to the handle's device to avoid log # messages. @@ -683,6 +695,10 @@ class ResourceVariable(variables.RefVariable): """The op for this variable.""" return self._handle.op + @property + def trainable(self): + return self._trainable + def eval(self, session=None): """Evaluates and returns the value of this variable.""" if context.executing_eagerly(): @@ -818,10 +834,6 @@ class ResourceVariable(variables.RefVariable): return ResourceVariable( variable_def=variable_def, import_scope=import_scope) - def _ref(self): - """Unsupported.""" - raise NotImplementedError("ResourceVariable does not implement _ref()") - def set_shape(self, shape): """Unsupported.""" raise NotImplementedError("ResourceVariable does not implement set_shape()") @@ -994,6 +1006,55 @@ class ResourceVariable(variables.RefVariable): self.handle, sparse_delta.indices, ops.convert_to_tensor(sparse_delta.values, self.dtype), name=name)) + def batch_scatter_update(self, sparse_delta, use_locking=False, name=None): + """Assigns `IndexedSlices` to this variable batch-wise. + + Analogous to `batch_gather`. This assumes that this variable and the + sparse_delta IndexedSlices have a series of leading dimensions that are the + same for all of them, and the updates are performed on the last dimension of + indices. In other words, the dimensions should be the following: + + `num_prefix_dims = sparse_delta.indices.ndims - 1` + `batch_dim = num_prefix_dims + 1` + `sparse_delta.updates.shape = sparse_delta.indices.shape + var.shape[ + batch_dim:]` + + where + + `sparse_delta.updates.shape[:num_prefix_dims]` + `== sparse_delta.indices.shape[:num_prefix_dims]` + `== var.shape[:num_prefix_dims]` + + And the operation performed can be expressed as: + + `var[i_1, ..., i_n, + sparse_delta.indices[i_1, ..., i_n, j]] = sparse_delta.updates[ + i_1, ..., i_n, j]` + + When sparse_delta.indices is a 1D tensor, this operation is equivalent to + `scatter_update`. + + To avoid this operation one can looping over the first `ndims` of the + variable and using `scatter_update` on the subtensors that result of slicing + the first dimension. This is a valid option for `ndims = 1`, but less + efficient than this implementation. + + Args: + sparse_delta: `IndexedSlices` to be assigned to this variable. + use_locking: If `True`, use locking during the operation. + name: the name of the operation. + + Returns: + A `Tensor` that will hold the new value of this variable after + the scattered subtraction has completed. + + Raises: + ValueError: if `sparse_delta` is not an `IndexedSlices`. + """ + return self._lazy_read(state_ops.batch_scatter_update( + self, sparse_delta.indices, sparse_delta.values, + use_locking=use_locking, name=name)) + def scatter_nd_sub(self, indices, updates, name=None): """Applies sparse subtraction to individual values or slices in a Variable. @@ -1178,8 +1239,10 @@ class ResourceVariable(variables.RefVariable): def _dense_var_to_tensor(self, dtype=None, name=None, as_ref=False): del name - if dtype is not None and dtype != self.dtype: - return NotImplemented + if dtype is not None and not dtype.is_compatible_with(self.dtype): + raise ValueError( + "Incompatible type conversion requested to type {!r} for variable " + "of type {!r}".format(dtype.name, self.dtype.name)) if as_ref: return self.read_value().op.inputs[0] else: @@ -1231,6 +1294,12 @@ def _dense_var_to_tensor(var, dtype=None, name=None, as_ref=False): return var._dense_var_to_tensor(dtype=dtype, name=name, as_ref=as_ref) # pylint: disable=protected-access +# Register a conversion function which reads the value of the variable, +# allowing instances of the class to be used as tensors. +ops.register_tensor_conversion_function(ResourceVariable, _dense_var_to_tensor) +ops.register_dense_tensor_like_type(ResourceVariable) + + class _UnreadVariable(ResourceVariable): """Represents a future for a read of a variable. @@ -1291,7 +1360,7 @@ class _UnreadVariable(ResourceVariable): """The op for this variable.""" return self._parent_op -ops.register_tensor_conversion_function(_UnreadVariable, _dense_var_to_tensor) + ops.register_dense_tensor_like_type(_UnreadVariable) @@ -1390,29 +1459,15 @@ class _MixedPrecisionVariable(ResourceVariable): def _dense_var_to_tensor(self, dtype=None, name=None, as_ref=False): del name - dtype = dtype or self.read_dtype - if dtype != self.read_dtype or as_ref: + if (dtype is not None and + not dtype.is_compatible_with(self.read_dtype) or as_ref): return NotImplemented - else: - res = self.value() - return res + return self.value() def _should_act_as_resource_variable(self): """To pass resource_variable_ops.is_resource_variable check.""" pass -# Register a conversion function which reads the value of the variable, -# allowing instances of the class to be used as tensors. - -# Note: registering for Variable after ResourceVariable because inheritance will -# otherwise lead to the wrong behavior. -ops.register_tensor_conversion_function(ResourceVariable, _dense_var_to_tensor) -ops.register_tensor_conversion_function( - variables.Variable, variables.Variable._TensorConversionFunction) # pylint: disable=protected-access - -# pylint: disable=protected-access -ops.register_dense_tensor_like_type(ResourceVariable) - @ops.RegisterGradient("ReadVariableOp") def _ReadGrad(_, grad): diff --git a/tensorflow/python/ops/variables.py b/tensorflow/python/ops/variables.py index 657f64deaa..a4967e1c0f 100644 --- a/tensorflow/python/ops/variables.py +++ b/tensorflow/python/ops/variables.py @@ -59,21 +59,6 @@ def _make_getter(captured_getter, captured_previous): return getter -def _has_cycle(op, path): - """Detect cycles in the dependencies of `initial_value`.""" - if op.name in path: - return True - path.add(op.name) - for op_input in op.inputs: - if _has_cycle(op_input.op, path): - return True - for op_control_input in op.control_inputs: - if _has_cycle(op_control_input, path): - return True - path.remove(op.name) - return False - - @tf_export("VariableSynchronization") class VariableSynchronization(enum.Enum): """Indicates when a distributed variable will be synced. @@ -1041,6 +1026,10 @@ class Variable(six.with_metaclass(VariableMetaclass, """Alias of `Variable.shape`.""" return self.shape + def _gather_saveables_for_checkpoint(self): + """For implementing `Checkpointable`. This object is saveable on its own.""" + return {checkpointable.VARIABLE_VALUE_KEY: self} + def to_proto(self, export_scope=None): """Converts a `Variable` to a `VariableDef` protocol buffer. @@ -1144,6 +1133,9 @@ class Variable(six.with_metaclass(VariableMetaclass, return None +Variable._OverloadAllOperators() # pylint: disable=protected-access + + @tf_export(v1=["Variable"]) class VariableV1(Variable): """See the [Variables Guide](https://tensorflow.org/guide/variables). @@ -1582,7 +1574,8 @@ class RefVariable(VariableV1): # using their initialized_value() method. self._initializer_op = state_ops.assign( self._variable, - self._try_guard_against_uninitialized_dependencies( + _try_guard_against_uninitialized_dependencies( + name, self._initial_value), validate_shape=validate_shape).op @@ -2161,134 +2154,6 @@ class RefVariable(VariableV1): else: return v.value() - def _gather_saveables_for_checkpoint(self): - """For implementing `Checkpointable`. This object is saveable on its own.""" - return {checkpointable.VARIABLE_VALUE_KEY: self} - - def _try_guard_against_uninitialized_dependencies(self, initial_value): - """Attempt to guard against dependencies on uninitialized variables. - - Replace references to variables in `initial_value` with references to the - variable's initialized values. The initialized values are essentially - conditional TensorFlow graphs that return a variable's value if it is - initialized or its `initial_value` if it hasn't been initialized. This - replacement is done on a best effort basis: - - - If the `initial_value` graph contains cycles, we don't do any - replacements for that graph. - - If the variables that `initial_value` depends on are not present in the - `GLOBAL_VARIABLES` or `LOCAL_VARIABLES` we don't replace them. - - In these cases, it is up to the caller to ensure that the `initial_value` - graph uses initialized variables or that they guard access to variables - using their `initialized_value` method. - - Args: - initial_value: `Tensor`. The initial value. - Returns: - A `Tensor` suitable to initialize a variable. - Raises: - TypeError: If `initial_value` is not a `Tensor`. - """ - if not isinstance(initial_value, ops.Tensor): - raise TypeError("initial_value needs to be a Tensor: %s" % initial_value) - - # Don't modify initial_value if it contains any cyclic dependencies. - if _has_cycle(initial_value.op, path=set()): - return initial_value - - return self._safe_initial_value_from_tensor(initial_value, op_cache={}) - - def _safe_initial_value_from_tensor(self, tensor, op_cache): - """Replace dependencies on variables with their initialized values. - - Args: - tensor: A `Tensor`. The tensor to replace. - op_cache: A dict mapping operation names to `Operation`s. Used to memoize - the results so as to avoid creating redundant operations. - Returns: - A `Tensor` compatible with `tensor`. Any inputs that lead to variable - values will be replaced with a corresponding graph that uses the - variable's initialized values. This is done on a best-effort basis. If no - modifications need to be made then `tensor` will be returned unchanged. - """ - op = tensor.op - new_op = op_cache.get(op.name) - if new_op is None: - new_op = self._safe_initial_value_from_op(op, op_cache) - op_cache[op.name] = new_op - return new_op.outputs[tensor.value_index] - - def _safe_initial_value_from_op(self, op, op_cache): - """Replace dependencies on variables with their initialized values. - - Args: - op: An `Operation`. The operation to replace. - op_cache: A dict mapping operation names to `Operation`s. Used to memoize - the results so as to avoid creating redundant operations. - Returns: - An `Operation` compatible with `op`. Any inputs that lead to variable - values will be replaced with a corresponding graph that uses the - variable's initialized values. This is done on a best-effort basis. If no - modifications need to be made then `op` will be returned unchanged. - """ - op_type = op.node_def.op - if op_type in ("IsVariableInitialized", "VarIsInitializedOp", - "ReadVariableOp"): - return op - - # Attempt to find the initialized_value of any variable reference / handles. - # TODO(b/70206927): Fix handling of ResourceVariables. - if op_type in ("Variable", "VariableV2", "VarHandleOp"): - initialized_value = self._find_initialized_value_for_variable(op) - return op if initialized_value is None else initialized_value.op - - # Recursively build initializer expressions for inputs. - modified = False - new_op_inputs = [] - for op_input in op.inputs: - new_op_input = self._safe_initial_value_from_tensor(op_input, op_cache) - new_op_inputs.append(new_op_input) - modified = modified or (new_op_input != op_input) - - # If at least one input was modified, replace the op. - if modified: - new_op_type = op_type - if new_op_type == "RefSwitch": - new_op_type = "Switch" - new_op_name = op.node_def.name + "_" + self.name - new_op_name = new_op_name.replace(":", "_") - return self.graph.create_op( - new_op_type, new_op_inputs, - op._output_types, # pylint: disable=protected-access - name=new_op_name, attrs=op.node_def.attr) - - return op - - def _find_initialized_value_for_variable(self, variable_op): - """Find the initialized value for a variable op. - - To do so, lookup the variable op in the variables collection. - - Args: - variable_op: A variable `Operation`. - Returns: - A `Tensor` representing the initialized value for the variable or `None` - if the initialized value could not be found. - """ - try: - var_names = [variable_op.node_def.name, variable_op.node_def.name + ":0"] - for collection_name in (ops.GraphKeys.GLOBAL_VARIABLES, - ops.GraphKeys.LOCAL_VARIABLES): - for var in self.graph.get_collection(collection_name): - if var.name in var_names: - return var.initialized_value() - except AttributeError: - # Return None when an incomplete user-defined variable type was put in - # the collection. - return None - return None - # NOTE(mrry): This enables the Variable's overloaded "right" binary # operators to run when the left operand is an ndarray, because it # accords the Variable class higher priority than an ndarray, or a @@ -2441,6 +2306,151 @@ class RefVariable(VariableV1): return self._save_slice_info +def _try_guard_against_uninitialized_dependencies(name, initial_value): + """Attempt to guard against dependencies on uninitialized variables. + + Replace references to variables in `initial_value` with references to the + variable's initialized values. The initialized values are essentially + conditional TensorFlow graphs that return a variable's value if it is + initialized or its `initial_value` if it hasn't been initialized. This + replacement is done on a best effort basis: + + - If the `initial_value` graph contains cycles, we don't do any + replacements for that graph. + - If the variables that `initial_value` depends on are not present in the + `GLOBAL_VARIABLES` or `LOCAL_VARIABLES` we don't replace them. + + In these cases, it is up to the caller to ensure that the `initial_value` + graph uses initialized variables or that they guard access to variables + using their `initialized_value` method. + + Args: + name: Variable name. + initial_value: `Tensor`. The initial value. + Returns: + A `Tensor` suitable to initialize a variable. + Raises: + TypeError: If `initial_value` is not a `Tensor`. + """ + if not isinstance(initial_value, ops.Tensor): + raise TypeError("initial_value needs to be a Tensor: %s" % initial_value) + + # Don't modify initial_value if it contains any cyclic dependencies. + if _has_cycle(initial_value.op, path=set()): + return initial_value + return _safe_initial_value_from_tensor(name, initial_value, op_cache={}) + + +def _has_cycle(op, path): + """Detect cycles in the dependencies of `initial_value`.""" + if op.name in path: + return True + path.add(op.name) + for op_input in op.inputs: + if _has_cycle(op_input.op, path): + return True + for op_control_input in op.control_inputs: + if _has_cycle(op_control_input, path): + return True + path.remove(op.name) + return False + + +def _safe_initial_value_from_tensor(name, tensor, op_cache): + """Replace dependencies on variables with their initialized values. + + Args: + name: Variable name. + tensor: A `Tensor`. The tensor to replace. + op_cache: A dict mapping operation names to `Operation`s. Used to memoize + the results so as to avoid creating redundant operations. + Returns: + A `Tensor` compatible with `tensor`. Any inputs that lead to variable + values will be replaced with a corresponding graph that uses the + variable's initialized values. This is done on a best-effort basis. If no + modifications need to be made then `tensor` will be returned unchanged. + """ + op = tensor.op + new_op = op_cache.get(op.name) + if new_op is None: + new_op = _safe_initial_value_from_op(name, op, op_cache) + op_cache[op.name] = new_op + return new_op.outputs[tensor.value_index] + + +def _safe_initial_value_from_op(name, op, op_cache): + """Replace dependencies on variables with their initialized values. + + Args: + name: Variable name. + op: An `Operation`. The operation to replace. + op_cache: A dict mapping operation names to `Operation`s. Used to memoize + the results so as to avoid creating redundant operations. + Returns: + An `Operation` compatible with `op`. Any inputs that lead to variable + values will be replaced with a corresponding graph that uses the + variable's initialized values. This is done on a best-effort basis. If no + modifications need to be made then `op` will be returned unchanged. + """ + op_type = op.node_def.op + if op_type in ("IsVariableInitialized", "VarIsInitializedOp", + "ReadVariableOp"): + return op + + # Attempt to find the initialized_value of any variable reference / handles. + # TODO(b/70206927): Fix handling of ResourceVariables. + if op_type in ("Variable", "VariableV2", "VarHandleOp"): + initialized_value = _find_initialized_value_for_variable(op) + return op if initialized_value is None else initialized_value.op + + # Recursively build initializer expressions for inputs. + modified = False + new_op_inputs = [] + for op_input in op.inputs: + new_op_input = _safe_initial_value_from_tensor(name, op_input, op_cache) + new_op_inputs.append(new_op_input) + modified = modified or (new_op_input != op_input) + + # If at least one input was modified, replace the op. + if modified: + new_op_type = op_type + if new_op_type == "RefSwitch": + new_op_type = "Switch" + new_op_name = op.node_def.name + "_" + name + new_op_name = new_op_name.replace(":", "_") + return op.graph.create_op( + new_op_type, new_op_inputs, + op._output_types, # pylint: disable=protected-access + name=new_op_name, attrs=op.node_def.attr) + + return op + + +def _find_initialized_value_for_variable(variable_op): + """Find the initialized value for a variable op. + + To do so, lookup the variable op in the variables collection. + + Args: + variable_op: A variable `Operation`. + Returns: + A `Tensor` representing the initialized value for the variable or `None` + if the initialized value could not be found. + """ + try: + var_names = [variable_op.node_def.name, variable_op.node_def.name + ":0"] + for collection_name in (ops.GraphKeys.GLOBAL_VARIABLES, + ops.GraphKeys.LOCAL_VARIABLES): + for var in variable_op.graph.get_collection(collection_name): + if var.name in var_names: + return var.initialized_value() + except AttributeError: + # Return None when an incomplete user-defined variable type was put in + # the collection. + return None + return None + + class PartitionedVariable(object): """A container for partitioned `Variable` objects. @@ -2659,6 +2669,15 @@ class PartitionedVariable(object): return assign_list return [assign.op for assign in assign_list] + +# Register a conversion function which reads the value of the variable, +# allowing instances of the class to be used as tensors. +ops.register_tensor_conversion_function( + RefVariable, + RefVariable._TensorConversionFunction) # pylint: disable=protected-access +ops.register_dense_tensor_like_type(RefVariable) + + @tf_export(v1=["global_variables"]) def global_variables(scope=None): """Returns global variables. @@ -2982,12 +3001,7 @@ def report_uninitialized_variables(var_list=None, # uninitialized variables. return array_ops.boolean_mask(variable_names_tensor, variables_mask) -# pylint: disable=protected-access -Variable._OverloadAllOperators() ops.register_tensor_conversion_function( - PartitionedVariable, PartitionedVariable._TensorConversionFunction) -# pylint: enable=protected-access - - -ops.register_dense_tensor_like_type(Variable) + PartitionedVariable, + PartitionedVariable._TensorConversionFunction) # pylint: disable=protected-access -- GitLab From 2a3d6401a3f21e4c9d043d90da48444d6e24e057 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Fri, 4 Jan 2019 10:09:12 -0800 Subject: [PATCH 342/622] Remove NHWC format restriction while using batch_group_count for convolutions of depthwise backprop filters PiperOrigin-RevId: 227873314 --- tensorflow/compiler/tf2xla/kernels/conv_op_helpers.cc | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tensorflow/compiler/tf2xla/kernels/conv_op_helpers.cc b/tensorflow/compiler/tf2xla/kernels/conv_op_helpers.cc index b0bc764030..6b049f653c 100644 --- a/tensorflow/compiler/tf2xla/kernels/conv_op_helpers.cc +++ b/tensorflow/compiler/tf2xla/kernels/conv_op_helpers.cc @@ -428,11 +428,8 @@ xla::StatusOr MakeXlaBackpropFilterConvOp( int n_dim = GetTensorBatchDimIndex(num_dims, attrs.data_format); int c_dim = GetTensorFeatureDimIndex(num_dims, attrs.data_format); - // The conversion logic below assumes that the data format is NHWC, so we also - // check that here. bool use_batch_group_count = - filter_tensor_shape.dim_size(num_dims - 1) == 1 && attrs.depthwise && - attrs.data_format == FORMAT_NHWC; + filter_tensor_shape.dim_size(num_dims - 1) == 1 && attrs.depthwise; std::vector> padding(attrs.num_spatial_dims); std::vector rhs_dilation(attrs.num_spatial_dims); -- GitLab From 07816cc1e13c02d7ad579d1279a5fca883126c35 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Fri, 4 Jan 2019 10:09:49 -0800 Subject: [PATCH 343/622] Fix markdown rendering. PiperOrigin-RevId: 227873426 --- tensorflow/python/ops/variables.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tensorflow/python/ops/variables.py b/tensorflow/python/ops/variables.py index a4967e1c0f..f9fc72a6da 100644 --- a/tensorflow/python/ops/variables.py +++ b/tensorflow/python/ops/variables.py @@ -306,6 +306,7 @@ class Variable(six.with_metaclass(VariableMetaclass, Here replacing adding `use_resource=True` when constructing the variable will fix any nondeterminism issues: + ``` v = tf.Variable(True, use_resource=True) tf.cond(v, lambda: v.assign(False), my_false_fn) -- GitLab From daa75aff18fd42598d1fb68f13da13042e886c07 Mon Sep 17 00:00:00 2001 From: Saurabh Saxena Date: Fri, 4 Jan 2019 10:17:31 -0800 Subject: [PATCH 344/622] Allows TensorListScatter to scatter at non-contiguous indices to make it consistent with TensorArray.scatter. PiperOrigin-RevId: 227874653 --- tensorflow/core/kernels/list_kernels.h | 31 ++++++++++---- .../python/kernel_tests/list_ops_test.py | 41 +++++++++++++++++++ .../kernel_tests/tensor_array_ops_test.py | 5 +-- tensorflow/python/ops/list_ops.py | 14 +++++-- 4 files changed, 77 insertions(+), 14 deletions(-) diff --git a/tensorflow/core/kernels/list_kernels.h b/tensorflow/core/kernels/list_kernels.h index 99b4f42084..559e14b03c 100644 --- a/tensorflow/core/kernels/list_kernels.h +++ b/tensorflow/core/kernels/list_kernels.h @@ -521,14 +521,31 @@ class TensorListScatter : public OpKernel { "Specified a list with shape ", element_shape.DebugString(), " from a tensor with shape ", output_shape.DebugString())); output_list.element_shape = element_shape; - output_list.tensors.reserve(indices.NumElements()); + + OP_REQUIRES(c, indices.NumElements() == input_tensor.shape().dim_size(0), + errors::InvalidArgument( + "Invalid number of rows in input tensor. Expected: ", + indices.NumElements(), + " Actual: ", input_tensor.shape().dim_size(0))); + + // Validate indices and resize output_list.tensors to fit the highest index. + { + size_t list_size = 0; + for (int index = 0; index < indices.NumElements(); ++index) { + const int i = indices.flat()(index); + OP_REQUIRES(c, i >= 0, + errors::InvalidArgument( + "Indices in TensorListScatter must all be positive.")); + if (i >= list_size) { + list_size = i + 1; + } + } + output_list.tensors.resize(list_size, Tensor(DT_INVALID)); + } + for (int index = 0; index < indices.NumElements(); ++index) { const int i = indices.flat()(index); - OP_REQUIRES(c, i < input_tensor.shape().dim_size(0), - errors::InvalidArgument( - "Trying to scatter index ", i, " from tensor with ", - input_tensor.shape().dim_size(0), " rows.")); - Tensor tmp = input_tensor.Slice(i, i + 1); + Tensor tmp = input_tensor.Slice(index, index + 1); TensorShape tmp_shape = tmp.shape(); tmp_shape.RemoveDim(0); OP_REQUIRES(c, tmp.CopyFrom(tmp, tmp_shape), @@ -541,7 +558,7 @@ class TensorListScatter : public OpKernel { // many small ondes. aligned.flat().device(c->eigen_device()) = tmp.unaligned_flat(); - output_list.tensors.push_back(aligned); + std::swap(output_list.tensors[i], aligned); } output_tensor->scalar()() = std::move(output_list); } diff --git a/tensorflow/python/kernel_tests/list_ops_test.py b/tensorflow/python/kernel_tests/list_ops_test.py index 054ac36755..fc0e270667 100644 --- a/tensorflow/python/kernel_tests/list_ops_test.py +++ b/tensorflow/python/kernel_tests/list_ops_test.py @@ -290,6 +290,47 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): t = list_ops.tensor_list_gather(l, [], element_dtype=dtypes.float32) self.evaluate(t) + def testGatherGradWithNonContiguousIndices(self): + with backprop.GradientTape(persistent=True) as tape: + t = constant_op.constant([1.0, 2.0, 3.0]) + l = list_ops.tensor_list_from_tensor(t, element_shape=[]) + c = constant_op.constant(5.0) + tape.watch(c) + l = list_ops.tensor_list_set_item(l, 1, c) + t = list_ops.tensor_list_gather(l, [1], element_dtype=dtypes.float32) + self.assertAllEqual(self.evaluate(t), [5.0]) + s = t[0] * t[0] + dt = tape.gradient(s, c) + self.assertAllEqual(self.evaluate(dt), 10.0) + dl = tape.gradient(t, l) + dl_length = list_ops.tensor_list_length(dl) + self.assertAllEqual(self.evaluate(dl_length), 3) + + def testScatterOutputListSize(self): + c0 = constant_op.constant([1.0, 2.0]) + l = list_ops.tensor_list_scatter( + c0, [1, 3], ops.convert_to_tensor([], dtype=dtypes.int32)) + # TensorListScatter should return a list with size largest index + 1. + self.assertEqual(self.evaluate(list_ops.tensor_list_length(l)), 4) + + def testScatterWithInvalidRowsInInputTensorFails(self): + c0 = constant_op.constant([1.0, 2.0]) + with self.assertRaisesRegexp( + errors.InvalidArgumentError, + "Invalid number of rows in input tensor. Expected: 3 Actual: 2"): + l = list_ops.tensor_list_scatter( + c0, [1, 0, 2], ops.convert_to_tensor([], dtype=dtypes.int32)) + self.evaluate(l) + + def testScatterWithNegativeIndicesFails(self): + c0 = constant_op.constant([1.0, 2.0]) + with self.assertRaisesRegexp( + errors.InvalidArgumentError, + "Indices in TensorListScatter must all be positive."): + l = list_ops.tensor_list_scatter( + c0, [-1, -2], ops.convert_to_tensor([], dtype=dtypes.int32)) + self.evaluate(l) + def testScatterGrad(self): with backprop.GradientTape() as tape: c0 = constant_op.constant([1.0, 2.0]) diff --git a/tensorflow/python/kernel_tests/tensor_array_ops_test.py b/tensorflow/python/kernel_tests/tensor_array_ops_test.py index 9fb5791048..303e9a006a 100644 --- a/tensorflow/python/kernel_tests/tensor_array_ops_test.py +++ b/tensorflow/python/kernel_tests/tensor_array_ops_test.py @@ -1359,7 +1359,6 @@ class TensorArrayTest(test.TestCase): def testSkipEagerTensorArrayEvalEmptyWithDefault(self): self._testTensorArrayEvalEmptyWithDefault() - @test_util.disable_control_flow_v2("b/117943286") @test_util.run_v1_only("b/117943489") def testSkipEagerTensorArrayScatterReadAndGradients(self): with self.session(use_gpu=True) as session: @@ -1387,8 +1386,8 @@ class TensorArrayTest(test.TestCase): self.assertAllEqual([10.0, -10.0], read_vals[1]) self.assertAllEqual([[2.0, 3.0], [4.0, 5.0]], grad_vals[0]) - @test_util.disable_control_flow_v2("b/117943286") - @test_util.run_v1_only("b/117943286") + @test_util.disable_control_flow_v2("b/118890905") + @test_util.run_v1_only("b/118890905") def testTensorArrayWriteGatherAndGradients(self): with self.session(use_gpu=True) as session: ta = tensor_array_ops.TensorArray( diff --git a/tensorflow/python/ops/list_ops.py b/tensorflow/python/ops/list_ops.py index 7bb9ca89e0..df928ea85d 100644 --- a/tensorflow/python/ops/list_ops.py +++ b/tensorflow/python/ops/list_ops.py @@ -200,10 +200,16 @@ def _TensorListResizeGrad(op, dlist): @ops.RegisterGradient("TensorListGather") def _TensorListGatherGrad(op, dtensor): - _, indices = op.inputs - return gen_list_ops.tensor_list_scatter( - tensor=dtensor, indices=indices, - element_shape=ops.convert_to_tensor(-1, dtype=dtypes.int32)), None + input_list, indices = op.inputs + dlist = gen_list_ops.tensor_list_scatter( + tensor=dtensor, + indices=indices, + element_shape=ops.convert_to_tensor(-1, dtype=dtypes.int32)) + # TensorListScatter returns a list with size `max(indices) + 1` + # so we manually resize it to match the size of the input list. + input_list_size = gen_list_ops.tensor_list_length(input_list) + dlist = gen_list_ops.tensor_list_resize(dlist, input_list_size) + return dlist, None @ops.RegisterGradient("TensorListScatter") -- GitLab From d183818d4d5e85a8f469402d273f8f03fa21f4b6 Mon Sep 17 00:00:00 2001 From: Scott Zhu Date: Fri, 4 Jan 2019 10:24:02 -0800 Subject: [PATCH 345/622] Update the math formula for LogLoss to match the parameters. PiperOrigin-RevId: 227875597 --- tensorflow/python/keras/losses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/python/keras/losses.py b/tensorflow/python/keras/losses.py index e6db795bb8..6a428b28a9 100644 --- a/tensorflow/python/keras/losses.py +++ b/tensorflow/python/keras/losses.py @@ -503,7 +503,7 @@ class CategoricalHinge(Loss): class LogLoss(Loss): """Computes the log loss between `y_true` and `y_pred`. - logloss = -y log(p) - (1-y) log(1-p) + logloss = - y_true * log(y_pred) - (1 - y_true) * log(1 - y_pred) Usage: -- GitLab From 59d5fbcc1a5e677387aac2bbf919faddf0e43377 Mon Sep 17 00:00:00 2001 From: Gunhan Gulsoy Date: Fri, 4 Jan 2019 10:36:25 -0800 Subject: [PATCH 346/622] Automated rollback of commit 64596a68235709e7f3246d0b120a4146521a998b PiperOrigin-RevId: 227877471 --- tensorflow/lite/experimental/swift/BUILD | 100 +++++ tensorflow/lite/experimental/swift/LICENSE | 202 ++++++++++ tensorflow/lite/experimental/swift/README.md | 54 +++ .../swift/Sources/Interpreter.swift | 265 ++++++++++++++ .../swift/Sources/InterpreterError.swift | 99 +++++ .../swift/Sources/InterpreterOptions.swift | 29 ++ .../experimental/swift/Sources/Model.swift | 40 ++ .../Sources/QuantizationParameters.swift | 38 ++ .../experimental/swift/Sources/Tensor.swift | 138 +++++++ .../Configs/TensorFlowLite.tulsigen | 57 +++ .../project.tulsiconf | 14 + .../project.pbxproj | 345 ++++++++++++++++++ .../TensorFlowLiteApp/AppDelegate.swift | 24 ++ .../Array+TensorFlowLite.swift | 22 ++ .../AppIcon.appiconset/Contents.json | 98 +++++ .../Assets.xcassets/Contents.json | 6 + .../Base.lproj/LaunchScreen.storyboard | 44 +++ .../Base.lproj/Main.storyboard | 95 +++++ .../Data+TensorFlowLite.swift | 13 + .../TensorFlowLiteApp/Info.plist | 46 +++ .../TensorFlowLiteApp/ViewController.swift | 299 +++++++++++++++ .../swift/Tests/InterpreterOptionsTests.swift | 54 +++ .../swift/Tests/InterpreterTests.swift | 315 ++++++++++++++++ .../experimental/swift/Tests/ModelTests.swift | 59 +++ .../Tests/QuantizationParametersTests.swift | 43 +++ .../swift/Tests/TensorTests.swift | 83 +++++ .../tools/pip_package/pip_smoke_test.py | 36 +- 27 files changed, 2603 insertions(+), 15 deletions(-) create mode 100644 tensorflow/lite/experimental/swift/BUILD create mode 100644 tensorflow/lite/experimental/swift/LICENSE create mode 100644 tensorflow/lite/experimental/swift/README.md create mode 100644 tensorflow/lite/experimental/swift/Sources/Interpreter.swift create mode 100644 tensorflow/lite/experimental/swift/Sources/InterpreterError.swift create mode 100644 tensorflow/lite/experimental/swift/Sources/InterpreterOptions.swift create mode 100644 tensorflow/lite/experimental/swift/Sources/Model.swift create mode 100644 tensorflow/lite/experimental/swift/Sources/QuantizationParameters.swift create mode 100644 tensorflow/lite/experimental/swift/Sources/Tensor.swift create mode 100644 tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/Configs/TensorFlowLite.tulsigen create mode 100644 tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/project.tulsiconf create mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp.xcodeproj/project.pbxproj create mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/AppDelegate.swift create mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Array+TensorFlowLite.swift create mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/Contents.json create mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/LaunchScreen.storyboard create mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/Main.storyboard create mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Data+TensorFlowLite.swift create mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Info.plist create mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/ViewController.swift create mode 100644 tensorflow/lite/experimental/swift/Tests/InterpreterOptionsTests.swift create mode 100644 tensorflow/lite/experimental/swift/Tests/InterpreterTests.swift create mode 100644 tensorflow/lite/experimental/swift/Tests/ModelTests.swift create mode 100644 tensorflow/lite/experimental/swift/Tests/QuantizationParametersTests.swift create mode 100644 tensorflow/lite/experimental/swift/Tests/TensorTests.swift diff --git a/tensorflow/lite/experimental/swift/BUILD b/tensorflow/lite/experimental/swift/BUILD new file mode 100644 index 0000000000..3bd288a1e1 --- /dev/null +++ b/tensorflow/lite/experimental/swift/BUILD @@ -0,0 +1,100 @@ +# TensorFlow Lite for Swift. + +package(default_visibility = ["//visibility:private"]) + +licenses(["notice"]) # Apache 2.0 + +exports_files(["LICENSE"]) + +load("@build_bazel_rules_apple//apple:ios.bzl", "ios_application", "ios_unit_test") +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +MINIMUM_OS_VERSION = "9.0" + +SWIFT_COPTS = [ + "-wmo", +] + +swift_library( + name = "TensorFlowLite", + srcs = glob(["Sources/*.swift"]), + copts = SWIFT_COPTS, + module_name = "TensorFlowLite", + tags = ["manual"], + deps = [ + "//tensorflow/lite/experimental/c:c_api", + ], +) + +ios_unit_test( + name = "TensorFlowLiteTests", + size = "small", + minimum_os_version = MINIMUM_OS_VERSION, + tags = [ + "manual", + # DISABLED: Following sanitizer tests are not supported by iOS test targets. + "noasan", + "nomsan", + "notsan", + ], + deps = [":TensorFlowLiteTestsLib"], +) + +swift_library( + name = "TensorFlowLiteTestsLib", + testonly = 1, + srcs = glob(["Tests/*.swift"]), + copts = SWIFT_COPTS, + tags = ["manual"], + deps = [ + ":TensorFlowLite", + ":TestResources", + ], +) + +objc_library( + name = "TestResources", + resources = [ + "//tensorflow/lite:testdata/add.bin", + "//tensorflow/lite:testdata/add_quantized.bin", + "//tensorflow/lite:testdata/multi_add.bin", + ], + tags = ["manual"], +) + +ios_application( + name = "TensorFlowLiteApp", + app_icons = glob(["TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/AppIcon.appiconset/**"]), + bundle_id = "com.tensorflow.lite.swift.TensorFlowLite", + families = [ + "ipad", + "iphone", + ], + infoplists = ["TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Info.plist"], + launch_storyboard = "TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/LaunchScreen.storyboard", + minimum_os_version = MINIMUM_OS_VERSION, + sdk_frameworks = [ + "CoreGraphics", + ], + tags = ["manual"], + deps = [":TensorFlowLiteAppLib"], +) + +swift_library( + name = "TensorFlowLiteAppLib", + srcs = glob(["TestApps/TensorFlowLiteApp/TensorFlowLiteApp/*.swift"]), + tags = ["manual"], + deps = [ + ":TensorFlowLite", + ":TensorFlowLiteAppResources", + ], +) + +objc_library( + name = "TensorFlowLiteAppResources", + storyboards = glob([ + "TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/*.storyboard", + ]), + tags = ["manual"], + deps = [":TestResources"], +) diff --git a/tensorflow/lite/experimental/swift/LICENSE b/tensorflow/lite/experimental/swift/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/tensorflow/lite/experimental/swift/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/tensorflow/lite/experimental/swift/README.md b/tensorflow/lite/experimental/swift/README.md new file mode 100644 index 0000000000..deb16c1e91 --- /dev/null +++ b/tensorflow/lite/experimental/swift/README.md @@ -0,0 +1,54 @@ +# TensorFlow Lite for Swift + +[TensorFlow Lite](https://www.tensorflow.org/lite/) is TensorFlow's lightweight +solution for Swift developers. It enables low-latency inference of on-device +machine learning models with a small binary size and fast performance supporting +hardware acceleration. + +## Getting Started + +### Bazel + +In your `BUILD` file, add the `TensorFlowLite` dependency: + +```python +swift_library( + # ... + deps = [ + "//tensorflow/lite/swift:TensorFlowLite", + ], +) +``` + +In your Swift files, import the module: + +```swift +import TensorFlowLite +``` + +### Tulsi + +Open the `TensorFlowLite.tulsiproj` using the [TulsiApp](https://github.com/bazelbuild/tulsi) or by +running the [`generate_xcodeproj.sh`](https://github.com/bazelbuild/tulsi/blob/master/src/tools/generate_xcodeproj.sh) +script: + +```shell +generate_xcodeproj.sh --genconfig tensorflow/lite/swift/TensorFlowLite.tulsiproj:TensorFlowLite --outputfolder ~/path/to/generated/TensorFlowLite.xcodeproj +``` + +### CocoaPods + +Add the following to your `Podfile`: + +```ruby +use_frameworks! +pod 'TensorFlowLiteSwift' +``` + +Then, run `pod install`. + +In your Swift files, import the module: + +```swift +import TensorFlowLite +``` diff --git a/tensorflow/lite/experimental/swift/Sources/Interpreter.swift b/tensorflow/lite/experimental/swift/Sources/Interpreter.swift new file mode 100644 index 0000000000..a14b5966b1 --- /dev/null +++ b/tensorflow/lite/experimental/swift/Sources/Interpreter.swift @@ -0,0 +1,265 @@ +// Copyright 2018 Google Inc. 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. + +import Foundation +import TensorFlowLiteCAPI + +/// A TensorFlow Lite interpreter that performs inference from a given model. +public final class Interpreter { + + /// The `TFL_Interpreter` C pointer type represented as an `UnsafePointer`. + private typealias CInterpreter = OpaquePointer + + /// Total number of input tensors associated with the model. + public var inputTensorCount: Int { + return Int(TFL_InterpreterGetInputTensorCount(cInterpreter)) + } + + /// Total number of output tensors associated with the model. + public var outputTensorCount: Int { + return Int(TFL_InterpreterGetOutputTensorCount(cInterpreter)) + } + + /// The underlying `TFL_Interpreter` C pointer. + private var cInterpreter: CInterpreter? + + /// Creates a new model interpreter instance. + /// + /// - Parameters: + /// - modelPath: Local file path to a TensorFlow Lite model. + /// - options: Custom configurations for the interpreter. The default is `nil` indicating that + /// interpreter will determine the configuration options. + /// - Throws: An error if the model could not be loaded or the interpreter could not be created. + public init(modelPath: String, options: InterpreterOptions? = nil) throws { + guard let model = Model(filePath: modelPath) else { throw InterpreterError.failedToLoadModel } + + let cInterpreterOptions: OpaquePointer? = try options.map { options in + guard let cOptions = TFL_NewInterpreterOptions() else { + throw InterpreterError.failedToCreateInterpreter + } + if let threadCount = options.threadCount, threadCount > 0 { + TFL_InterpreterOptionsSetNumThreads(cOptions, Int32(threadCount)) + } + if options.isErrorLoggingEnabled { + TFL_InterpreterOptionsSetErrorReporter( + cOptions, + { (_, format, arguments) in + guard let cFormat = format, + let message = String(cFormat: cFormat, arguments: arguments) + else { + return + } + print(String(describing: InterpreterError.tensorFlowLiteError(message))) + }, + nil + ) + } + return cOptions + } + defer { TFL_DeleteInterpreterOptions(cInterpreterOptions) } + + guard let cInterpreter = TFL_NewInterpreter(model.cModel, cInterpreterOptions) else { + throw InterpreterError.failedToCreateInterpreter + } + self.cInterpreter = cInterpreter + } + + deinit { + TFL_DeleteInterpreter(cInterpreter) + } + + /// Invokes the interpreter to perform inference from the loaded graph. + /// + /// - Throws: An error if the model was not ready because tensors were not allocated. + public func invoke() throws { + guard TFL_InterpreterInvoke(cInterpreter) == kTfLiteOk else { + // TODO(b/117510052): Determine which error to throw. + throw InterpreterError.allocateTensorsRequired + } + } + + /// Returns the input tensor at the given index. + /// + /// - Parameters: + /// - index: The index for the input tensor. + /// - Throws: An error if the index is invalid or the tensors have not been allocated. + /// - Returns: The input tensor at the given index. + public func input(at index: Int) throws -> Tensor { + let maxIndex = inputTensorCount - 1 + guard case 0...maxIndex = index else { + throw InterpreterError.invalidTensorIndex(index: index, maxIndex: maxIndex) + } + guard let cTensor = TFL_InterpreterGetInputTensor(cInterpreter, Int32(index)), + let bytes = TFL_TensorData(cTensor), + let nameCString = TFL_TensorName(cTensor) + else { + throw InterpreterError.allocateTensorsRequired + } + guard let dataType = TensorDataType(type: TFL_TensorType(cTensor)) else { + throw InterpreterError.invalidTensorDataType + } + + let name = String(cString: nameCString) + let rank = TFL_TensorNumDims(cTensor) + let dimensions = (0.. Tensor { + let maxIndex = outputTensorCount - 1 + guard case 0...maxIndex = index else { + throw InterpreterError.invalidTensorIndex(index: index, maxIndex: maxIndex) + } + guard let cTensor = TFL_InterpreterGetOutputTensor(cInterpreter, Int32(index)), + let bytes = TFL_TensorData(cTensor), + let nameCString = TFL_TensorName(cTensor) + else { + // TODO(b/117510052): Determine which error to throw. + throw InterpreterError.invokeInterpreterRequired + } + guard let dataType = TensorDataType(type: TFL_TensorType(cTensor)) else { + throw InterpreterError.invalidTensorDataType + } + + let name = String(cString: nameCString) + let rank = TFL_TensorNumDims(cTensor) + let dimensions = (0.. Tensor { + let maxIndex = inputTensorCount - 1 + guard case 0...maxIndex = index else { + throw InterpreterError.invalidTensorIndex(index: index, maxIndex: maxIndex) + } + guard let cTensor = TFL_InterpreterGetInputTensor(cInterpreter, Int32(index)) else { + throw InterpreterError.allocateTensorsRequired + } + + let byteCount = TFL_TensorByteSize(cTensor) + guard data.count == byteCount else { + throw InterpreterError.invalidTensorDataCount(provided: data.count, required: byteCount) + } + + let status = data.withUnsafeBytes { TFL_TensorCopyFromBuffer(cTensor, $0, data.count) } + guard status == kTfLiteOk else { throw InterpreterError.failedToCopyDataToInputTensor } + return try input(at: index) + } + + /// Allocates memory for all input tensors based on their `TensorShape`s. + /// + /// - Note: This is a relatively expensive operation and should only be called after creating the + /// interpreter and/or resizing any input tensors. + /// - Throws: An error if memory could not be allocated for the input tensors. + public func allocateTensors() throws { + guard TFL_InterpreterAllocateTensors(cInterpreter) == kTfLiteOk else { + throw InterpreterError.failedToAllocateTensors + } + } +} + +// MARK: - Extensions + +extension String { + /// Returns a new `String` initialized by using the given format C array as a template into which + /// the remaining argument values are substituted according to the user’s default locale. + /// + /// - Note: Returns `nil` if a new `String` could not be constructed from the given values. + /// - Parameters: + /// - cFormat: The format C array as a template for substituting values. + /// - arguments: A C pointer to a `va_list` of arguments to substitute into `cFormat`. + init?(cFormat: UnsafePointer, arguments: CVaListPointer) { + var buffer: UnsafeMutablePointer? + guard vasprintf(&buffer, cFormat, arguments) != 0, let cString = buffer else { return nil } + self.init(validatingUTF8: cString) + } +} diff --git a/tensorflow/lite/experimental/swift/Sources/InterpreterError.swift b/tensorflow/lite/experimental/swift/Sources/InterpreterError.swift new file mode 100644 index 0000000000..5de58b997a --- /dev/null +++ b/tensorflow/lite/experimental/swift/Sources/InterpreterError.swift @@ -0,0 +1,99 @@ +// Copyright 2018 Google Inc. 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. + +import Foundation + +/// TensorFlow Lite interpreter errors. +public enum InterpreterError: Error { + case invalidTensorIndex(index: Int, maxIndex: Int) + case invalidTensorDataCount(provided: Int, required: Int) + case invalidTensorDataType + case failedToLoadModel + case failedToCreateInterpreter + case failedToResizeInputTensor(index: Int) + case failedToCopyDataToInputTensor + case failedToAllocateTensors + case allocateTensorsRequired + case invokeInterpreterRequired + case tensorFlowLiteError(String) +} + +// MARK: - Extensions + +extension InterpreterError: LocalizedError { + /// Localized description of the interpreter error. + public var errorDescription: String? { + switch self { + case .invalidTensorIndex(let index, let maxIndex): + return "Invalid tensor index \(index), max index is \(maxIndex)." + case .invalidTensorDataCount(let providedCount, let requiredCount): + return "Provided data count \(providedCount) must match the required count \(requiredCount)." + case .invalidTensorDataType: + return "Tensor data type is unsupported or could not be determined because of a model error." + case .failedToLoadModel: + return "Failed to load the given model." + case .failedToCreateInterpreter: + return "Failed to create the interpreter." + case .failedToResizeInputTensor(let index): + return "Failed to resize input tesnor at index \(index)." + case .failedToCopyDataToInputTensor: + return "Failed to copy data to input tensor." + case .failedToAllocateTensors: + return "Failed to allocate memory for input tensors." + case .allocateTensorsRequired: + return "Must call allocateTensors()." + case .invokeInterpreterRequired: + return "Must call invoke()." + case .tensorFlowLiteError(let message): + return "TensorFlow Lite Error: \(message)" + } + } +} + +extension InterpreterError: CustomStringConvertible { + /// Textual representation of the TensorFlow Lite interpreter error. + public var description: String { + return errorDescription ?? "Unknown error." + } +} + +#if swift(>=4.2) +extension InterpreterError: Equatable {} +#else +extension InterpreterError: Equatable { + public static func == (lhs: InterpreterError, rhs: InterpreterError) -> Bool { + switch (lhs, rhs) { + case (.invalidTensorDataType, .invalidTensorDataType), + (.failedToLoadModel, .failedToLoadModel), + (.failedToCreateInterpreter, .failedToCreateInterpreter), + (.failedToAllocateTensors, .failedToAllocateTensors), + (.allocateTensorsRequired, .allocateTensorsRequired), + (.invokeInterpreterRequired, .invokeInterpreterRequired): + return true + case (.invalidTensorIndex(let lhsIndex, let lhsMaxIndex), + .invalidTensorIndex(let rhsIndex, let rhsMaxIndex)): + return lhsIndex == rhsIndex && lhsMaxIndex == rhsMaxIndex + case (.invalidTensorDataCount(let lhsProvidedCount, let lhsRequiredCount), + .invalidTensorDataCount(let rhsProvidedCount, let rhsRequiredCount)): + return lhsProvidedCount == rhsProvidedCount && lhsRequiredCount == rhsRequiredCount + case (.failedToResizeInputTensor(let lhsIndex), .failedToResizeInputTensor(let rhsIndex)): + return lhsIndex == rhsIndex + case (.tensorFlowLiteError(let lhsMessage), .tensorFlowLiteError(let rhsMessage)): + return lhsMessage == rhsMessage + default: + return false + } + } +} +#endif // swift(>=4.2) diff --git a/tensorflow/lite/experimental/swift/Sources/InterpreterOptions.swift b/tensorflow/lite/experimental/swift/Sources/InterpreterOptions.swift new file mode 100644 index 0000000000..2365fd7ade --- /dev/null +++ b/tensorflow/lite/experimental/swift/Sources/InterpreterOptions.swift @@ -0,0 +1,29 @@ +// Copyright 2018 Google Inc. 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. + +import Foundation + +/// Custom configuration options for a TensorFlow Lite interpreter. +public struct InterpreterOptions: Equatable { + + /// Maximum number of CPU threads that the interpreter should run on. Default is `nil` which + /// indicates that the `Interpreter` will decide the number of threads to use. + public var threadCount: Int? = nil + + /// Whether error logging to the console is enabled. The default is `false`. + public var isErrorLoggingEnabled = false + + /// Creates a new instance of interpreter options. + public init() {} +} diff --git a/tensorflow/lite/experimental/swift/Sources/Model.swift b/tensorflow/lite/experimental/swift/Sources/Model.swift new file mode 100644 index 0000000000..e8c49ff1ae --- /dev/null +++ b/tensorflow/lite/experimental/swift/Sources/Model.swift @@ -0,0 +1,40 @@ +// Copyright 2018 Google Inc. 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. + +import Foundation +import TensorFlowLiteCAPI + +/// A TensorFlow Lite model used by the 'Interpreter` to perform inference. +final class Model { + + /// The `TFL_Model` C pointer type represented as an `UnsafePointer`. + typealias CModel = OpaquePointer + + /// The underlying `TFL_Model` C pointer. + let cModel: CModel? + + /// Creates a new model instance. + /// + /// - Precondition: Initialization can fail if the given `filePath` is invalid. + /// - Parameters: + /// - filePath: Local file path to a TensorFlow Lite model. + init?(filePath: String) { + guard !filePath.isEmpty, let cModel = TFL_NewModelFromFile(filePath) else { return nil } + self.cModel = cModel + } + + deinit { + TFL_DeleteModel(cModel) + } +} diff --git a/tensorflow/lite/experimental/swift/Sources/QuantizationParameters.swift b/tensorflow/lite/experimental/swift/Sources/QuantizationParameters.swift new file mode 100644 index 0000000000..f367875644 --- /dev/null +++ b/tensorflow/lite/experimental/swift/Sources/QuantizationParameters.swift @@ -0,0 +1,38 @@ +// Copyright 2018 Google Inc. 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. + +import Foundation + +/// Parameters that determine the mapping of quantized values to real values. Quantized values can +/// be mapped to float values using the following conversion: +/// `realValue = scale * (quantizedValue - zeroPoint)`. +public struct QuantizationParameters { + + /// Difference between real values corresponding to consecutive quantized values differing by 1. + /// For example, the range of quantized values for `UInt8` data type is [0, 255]. + public let scale: Float + + /// Quantized value that corresponds to the real 0 value. + public let zeroPoint: Int + + /// Creates a new quantization parameters instance. + /// + /// - Parameters: + /// - scale: Scale value for asymmetric quantization. + /// - zeroPoint: Zero point for asymmetric quantization. + init(scale: Float, zeroPoint: Int) { + self.scale = scale + self.zeroPoint = zeroPoint + } +} diff --git a/tensorflow/lite/experimental/swift/Sources/Tensor.swift b/tensorflow/lite/experimental/swift/Sources/Tensor.swift new file mode 100644 index 0000000000..b738d87549 --- /dev/null +++ b/tensorflow/lite/experimental/swift/Sources/Tensor.swift @@ -0,0 +1,138 @@ +// Copyright 2018 Google Inc. 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. + +import Foundation +import TensorFlowLiteCAPI + +/// An input or output tensor in a TensorFlow Lite graph. +public struct Tensor { + + /// Name of the tensor. + public let name: String + + /// Data type of the tensor. + public let dataType: TensorDataType + + /// Shape of the tensor. + public let shape: TensorShape + + /// Data in the input or output tensor. + public let data: Data + + /// Quantization parameters for the tensor if using a quantized model. + public let quantizationParameters: QuantizationParameters? + + /// Creates a new input or output tensor instance. + /// + /// - Parameters: + /// - name: Name of the tensor. + /// - dataType: Data type of the tensor. + /// - data: Data in the input tensor. + /// - quantizationParameters Quantization parameters for the tensor if using a quantized model. + /// The default is `nil`. + init( + name: String, + dataType: TensorDataType, + shape: TensorShape, + data: Data, + quantizationParameters: QuantizationParameters? = nil + ) { + self.name = name + self.dataType = dataType + self.shape = shape + self.data = data + self.quantizationParameters = quantizationParameters + } +} + +/// Supported TensorFlow Lite tensor data types. +public enum TensorDataType: Equatable { + /// 32-bit single precision floating point tensor data type. + case float32 + /// 8-bit unsigned integer tensor data type. + case uInt8 + /// 16-bit signed integer tensor data type. + case int16 + /// 32-bit signed integer tensor data type. + case int32 + /// 64-bit signed integer tensor data type. + case int64 + /// Boolean tensor data type. + case bool + + /// Creates a new tensor data type from the given `TFL_Type` or `nil` if the data type is + /// unsupported or could not be determined because there was an error. + /// + /// - Parameter type: A data type supported by a tensor. + init?(type: TFL_Type) { + switch type { + case kTfLiteFloat32: + self = .float32 + case kTfLiteUInt8: + self = .uInt8 + case kTfLiteInt16: + self = .int16 + case kTfLiteInt32: + self = .int32 + case kTfLiteInt64: + self = .int64 + case kTfLiteBool: + self = .bool + case kTfLiteNoType: + fallthrough + default: + return nil + } + } +} + +/// The shape of a TensorFlow Lite tensor. +public struct TensorShape { + + /// The number of dimensions of the tensor. + public let rank: Int + + /// Array of dimensions for the tensor. + public let dimensions: [Int] + + /// Array of `Int32` dimensions for the tensor. + var int32Dimensions: [Int32] { return dimensions.map(Int32.init) } + + /// Creates a new tensor shape instance with the given array of dimensions. + /// + /// - Parameters: + /// - dimensions: Dimensions for the tensor. + public init(_ dimensions: [Int]) { + self.rank = dimensions.count + self.dimensions = dimensions + } + + /// Creates a new tensor shape instance with the given elements representing the dimensions. + /// + /// - Parameters: + /// - elements: Dimensions for the tensor. + public init(_ elements: Int...) { + self.init(elements) + } +} + +extension TensorShape: ExpressibleByArrayLiteral { + /// Creates a new tensor shape instance with the given array literal representing the dimensions. + /// + /// - Parameters: + /// - arrayLiteral: Dimensions for the tensor. + public init(arrayLiteral: Int...) { + self.init(arrayLiteral) + } +} diff --git a/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/Configs/TensorFlowLite.tulsigen b/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/Configs/TensorFlowLite.tulsigen new file mode 100644 index 0000000000..4010fab49e --- /dev/null +++ b/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/Configs/TensorFlowLite.tulsigen @@ -0,0 +1,57 @@ +{ + "sourceFilters" : [ + "third_party/tensorflow/lite/experimental/c", + "third_party/tensorflow/lite/experimental/swift", + "third_party/tensorflow/lite/experimental/swift/Sources", + "third_party/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp", + "third_party/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj", + "third_party/tensorflow/lite/experimental/swift/Tests", + ], + "buildTargets" : [ + "//third_party/tensorflow/lite/experimental/swift:TensorFlowLite", + "//third_party/tensorflow/lite/experimental/swift:TensorFlowLiteApp", + "//third_party/tensorflow/lite/experimental/swift:TensorFlowLiteTests", + ], + "projectName" : "TensorFlowLite", + "optionSet" : { + "LaunchActionPreActionScript" : { + "p" : "$(inherited)" + }, + "BazelBuildStartupOptionsRelease" : { + "p" : "$(inherited)" + }, + "BazelBuildOptionsRelease" : { + "p" : "$(inherited)" + }, + "BazelBuildOptionsDebug" : { + "p" : "$(inherited)" + }, + "EnvironmentVariables" : { + "p" : "$(inherited)" + }, + "BuildActionPreActionScript" : { + "p" : "$(inherited)" + }, + "CommandlineArguments" : { + "p" : "$(inherited)" + }, + "TestActionPreActionScript" : { + "p" : "$(inherited)" + }, + "BazelBuildStartupOptionsDebug" : { + "p" : "$(inherited)" + }, + "BuildActionPostActionScript" : { + "p" : "$(inherited)" + }, + "TestActionPostActionScript" : { + "p" : "$(inherited)" + }, + "LaunchActionPostActionScript" : { + "p" : "$(inherited)" + } + }, + "additionalFilePaths" : [ + "third_party/tensorflow/lite/experimental/swift/BUILD" + ] +} diff --git a/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/project.tulsiconf b/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/project.tulsiconf new file mode 100644 index 0000000000..14cff94453 --- /dev/null +++ b/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/project.tulsiconf @@ -0,0 +1,14 @@ +{ + "configDefaults" : { + "optionSet" : { + "ProjectPrioritizesSwift" : { + "p" : "YES" + } + } + }, + "projectName" : "TensorFlowLite", + "packages" : [ + "third_party/tensorflow/lite/experimental/swift" + ], + "workspaceRoot" : "../../../../../.." +} diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp.xcodeproj/project.pbxproj b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..fbbf9a1de2 --- /dev/null +++ b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp.xcodeproj/project.pbxproj @@ -0,0 +1,345 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 4A7304B421500B8400C90B21 /* Data+TensorFlowLite.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A7304B321500B8300C90B21 /* Data+TensorFlowLite.swift */; }; + 4AA72B732146ED64006C3AEF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AA72B722146ED64006C3AEF /* AppDelegate.swift */; }; + 4AA72B752146ED64006C3AEF /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AA72B742146ED64006C3AEF /* ViewController.swift */; }; + 4AA72B782146ED64006C3AEF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4AA72B762146ED64006C3AEF /* Main.storyboard */; }; + 4AA72B7A2146ED66006C3AEF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4AA72B792146ED66006C3AEF /* Assets.xcassets */; }; + 4AA72B7D2146ED66006C3AEF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4AA72B7B2146ED66006C3AEF /* LaunchScreen.storyboard */; }; + 4ADDE0CE2176600E00FF07A2 /* Array+TensorFlowLite.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ADDE0CD2176600900FF07A2 /* Array+TensorFlowLite.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 4A7304B321500B8300C90B21 /* Data+TensorFlowLite.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+TensorFlowLite.swift"; sourceTree = ""; }; + 4AA72B6F2146ED64006C3AEF /* TensorFlowLiteApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TensorFlowLiteApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 4AA72B722146ED64006C3AEF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 4AA72B742146ED64006C3AEF /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 4AA72B772146ED64006C3AEF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 4AA72B792146ED66006C3AEF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 4AA72B7C2146ED66006C3AEF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 4AA72B7E2146ED66006C3AEF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 4ADDE0CD2176600900FF07A2 /* Array+TensorFlowLite.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+TensorFlowLite.swift"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 4AA72B6C2146ED64006C3AEF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 4AA72B662146ED64006C3AEF = { + isa = PBXGroup; + children = ( + 4AA72B712146ED64006C3AEF /* TensorFlowLiteApp */, + 4AA72B702146ED64006C3AEF /* Products */, + ); + sourceTree = ""; + }; + 4AA72B702146ED64006C3AEF /* Products */ = { + isa = PBXGroup; + children = ( + 4AA72B6F2146ED64006C3AEF /* TensorFlowLiteApp.app */, + ); + name = Products; + sourceTree = ""; + }; + 4AA72B712146ED64006C3AEF /* TensorFlowLiteApp */ = { + isa = PBXGroup; + children = ( + 4AA72B722146ED64006C3AEF /* AppDelegate.swift */, + 4ADDE0CD2176600900FF07A2 /* Array+TensorFlowLite.swift */, + 4A7304B321500B8300C90B21 /* Data+TensorFlowLite.swift */, + 4AA72B742146ED64006C3AEF /* ViewController.swift */, + 4AA72B762146ED64006C3AEF /* Main.storyboard */, + 4AA72B792146ED66006C3AEF /* Assets.xcassets */, + 4AA72B7B2146ED66006C3AEF /* LaunchScreen.storyboard */, + 4AA72B7E2146ED66006C3AEF /* Info.plist */, + ); + path = TensorFlowLiteApp; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 4AA72B6E2146ED64006C3AEF /* TensorFlowLiteApp */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4AA72B812146ED66006C3AEF /* Build configuration list for PBXNativeTarget "TensorFlowLiteApp" */; + buildPhases = ( + 4AA72B6B2146ED64006C3AEF /* Sources */, + 4AA72B6C2146ED64006C3AEF /* Frameworks */, + 4AA72B6D2146ED64006C3AEF /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = TensorFlowLiteApp; + productName = TensorFlowLiteApp; + productReference = 4AA72B6F2146ED64006C3AEF /* TensorFlowLiteApp.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 4AA72B672146ED64006C3AEF /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0940; + LastUpgradeCheck = 0940; + ORGANIZATIONNAME = Google; + TargetAttributes = { + 4AA72B6E2146ED64006C3AEF = { + CreatedOnToolsVersion = 9.4.1; + }; + }; + }; + buildConfigurationList = 4AA72B6A2146ED64006C3AEF /* Build configuration list for PBXProject "TensorFlowLiteApp" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 4AA72B662146ED64006C3AEF; + productRefGroup = 4AA72B702146ED64006C3AEF /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 4AA72B6E2146ED64006C3AEF /* TensorFlowLiteApp */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 4AA72B6D2146ED64006C3AEF /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4AA72B7D2146ED66006C3AEF /* LaunchScreen.storyboard in Resources */, + 4AA72B7A2146ED66006C3AEF /* Assets.xcassets in Resources */, + 4AA72B782146ED64006C3AEF /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 4AA72B6B2146ED64006C3AEF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4AA72B732146ED64006C3AEF /* AppDelegate.swift in Sources */, + 4ADDE0CE2176600E00FF07A2 /* Array+TensorFlowLite.swift in Sources */, + 4A7304B421500B8400C90B21 /* Data+TensorFlowLite.swift in Sources */, + 4AA72B752146ED64006C3AEF /* ViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 4AA72B762146ED64006C3AEF /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 4AA72B772146ED64006C3AEF /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 4AA72B7B2146ED66006C3AEF /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 4AA72B7C2146ED66006C3AEF /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 4AA72B7F2146ED66006C3AEF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.4; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 4AA72B802146ED66006C3AEF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.4; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 4AA72B822146ED66006C3AEF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = TensorFlowLiteApp/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.tensorflow.lite.swift.TensorFlowLite; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 4AA72B832146ED66006C3AEF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = TensorFlowLiteApp/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.tensorflow.lite.swift.TensorFlowLite; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 4AA72B6A2146ED64006C3AEF /* Build configuration list for PBXProject "TensorFlowLiteApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4AA72B7F2146ED66006C3AEF /* Debug */, + 4AA72B802146ED66006C3AEF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4AA72B812146ED66006C3AEF /* Build configuration list for PBXNativeTarget "TensorFlowLiteApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4AA72B822146ED66006C3AEF /* Debug */, + 4AA72B832146ED66006C3AEF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 4AA72B672146ED64006C3AEF /* Project object */; +} diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/AppDelegate.swift b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/AppDelegate.swift new file mode 100644 index 0000000000..ffa90a06ad --- /dev/null +++ b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/AppDelegate.swift @@ -0,0 +1,24 @@ +import UIKit + +@UIApplicationMain + +final class AppDelegate: UIResponder, UIApplicationDelegate { + + /// The main window of the app. + var window: UIWindow? + + func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil + ) -> Bool { + return true + } +} + +// MARK: - Extensions + +#if !swift(>=4.2) +extension UIApplication { + typealias LaunchOptionsKey = UIApplicationLaunchOptionsKey +} +#endif // !swift(>=4.2) diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Array+TensorFlowLite.swift b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Array+TensorFlowLite.swift new file mode 100644 index 0000000000..56df1ce659 --- /dev/null +++ b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Array+TensorFlowLite.swift @@ -0,0 +1,22 @@ +import Foundation + +extension Array { + /// Creates a new array from the bytes of the given unsafe data. + /// + /// - Warning: The array's `Element` type must be trivial in that it can be copied bit for bit + /// with no indirection or reference-counting operations; otherwise, copying the raw bytes in + /// the `unsafeData`'s buffer to a new array returns an unsafe copy. + /// - Note: Returns `nil` if `unsafeData.count` is not a multiple of + /// `MemoryLayout.stride`. + /// - Parameter unsafeData: The data containing the bytes to turn into an array. + init?(unsafeData: Data) { + guard unsafeData.count % MemoryLayout.stride == 0 else { return nil } + let elements = unsafeData.withUnsafeBytes { + UnsafeBufferPointer( + start: $0, + count: unsafeData.count / MemoryLayout.stride + ) + } + self.init(elements) + } +} diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..d8db8d65fd --- /dev/null +++ b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/Contents.json b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/Contents.json new file mode 100644 index 0000000000..da4a164c91 --- /dev/null +++ b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/LaunchScreen.storyboard b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000000..a07a1321be --- /dev/null +++ b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/Main.storyboard b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/Main.storyboard new file mode 100644 index 0000000000..d0e91d63c4 --- /dev/null +++ b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/Main.storyboard @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Data+TensorFlowLite.swift b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Data+TensorFlowLite.swift new file mode 100644 index 0000000000..bc8a70c848 --- /dev/null +++ b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Data+TensorFlowLite.swift @@ -0,0 +1,13 @@ +import Foundation + +extension Data { + /// Creates a new buffer by copying the buffer pointer of the given array. + /// + /// - Warning: The given array's element type `T` must be trivial in that it can be copied bit + /// for bit with no indirection or reference-counting operations; otherwise, reinterpreting + /// data from the resulting buffer has undefined behavior. + /// - Parameter array: An array with elements of type `T`. + init(copyingBufferOf array: [T]) { + self = array.withUnsafeBufferPointer(Data.init) + } +} diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Info.plist b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Info.plist new file mode 100644 index 0000000000..3ca3875f04 --- /dev/null +++ b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Info.plist @@ -0,0 +1,46 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 0.0.1 + LSRequiresIPhoneOS + + NSCameraUsageDescription + NSCameraUsageDescription + NSPhotoLibraryUsageDescription + Select a photo to detect objects in. + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + + + diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/ViewController.swift b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/ViewController.swift new file mode 100644 index 0000000000..73c74fd19c --- /dev/null +++ b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/ViewController.swift @@ -0,0 +1,299 @@ +import TensorFlowLite +import UIKit + +class ViewController: UIViewController { + + // MARK: - Properties + + /// TensorFlowLite interpreter object for performing inference from a given model. + private var interpreter: Interpreter? + + /// Serial dispatch queue for managing `Interpreter` calls. + private let interpreterQueue = DispatchQueue( + label: Constant.dispatchQueueLabel, + qos: .userInitiated + ) + + /// The currently selected model. + private var currentModel: Model { + guard let currentModel = Model(rawValue: modelControl.selectedSegmentIndex) else { + preconditionFailure("Invalid model for selected segment index.") + } + return currentModel + } + + /// A description of the current model. + private var modelDescription: String { + guard let interpreter = interpreter else { return "" } + let inputCount = interpreter.inputTensorCount + let outputCount = interpreter.outputTensorCount + let inputTensors = (0.. String = { + guard let results = [Float32](unsafeData: outputTensor.data) else { return "No results." } + return resultsText + results.description + } + self.updateResultsText(results()) + } catch let error { + self.updateResultsText( + "Failed to invoke the interpreter with error: \(error.localizedDescription)" + ) + return + } + } + } + + private func invokeAddQuantized() { + interpreterQueue.async { + guard let interpreter = self.interpreter else { + self.updateResultsText(Constant.nilInterpreterErrorMessage) + return + } + do { + try interpreter.resizeInput(at: 0, to: [2]) + try interpreter.allocateTensors() + let input: [UInt8] = [1, 3] + let resultsText = self.modelDescription + "\n\n" + + "Performing 2 add operations on quantized input \(input.description) equals: " + self.updateResultsText(resultsText) + let data = Data(input) + try interpreter.copy(data, toInputAt: 0) + try interpreter.invoke() + let outputTensor = try interpreter.output(at: 0) + let results: () -> String = { + guard let quantizationParameters = outputTensor.quantizationParameters else { + return "No results." + } + let quantizedResults = [UInt8](outputTensor.data) + let dequantizedResults = quantizedResults.map { + quantizationParameters.scale * Float(Int($0) - quantizationParameters.zeroPoint) + } + return resultsText + quantizedResults.description + + ", dequantized results: " + dequantizedResults.description + } + self.updateResultsText(results()) + } catch let error { + self.updateResultsText( + "Failed to invoke the interpreter with error: \(error.localizedDescription)" + ) + return + } + } + } + + private func invokeMultiAdd() { + interpreterQueue.async { + guard let interpreter = self.interpreter else { + self.updateResultsText(Constant.nilInterpreterErrorMessage) + return + } + do { + let shape = TensorShape(2) + try (0.. [Float32] in + let input = [Float32(index + 1), Float32(index + 2)] + let data = Data(copyingBufferOf: input) + try interpreter.copy(data, toInputAt: index) + return input + } + let resultsText = self.modelDescription + "\n\n" + + "Performing 3 add operations on inputs \(inputs.description) equals: " + self.updateResultsText(resultsText) + try interpreter.invoke() + let results = try (0.. [Float32] in + let tensor = try interpreter.output(at: index) + return [Float32](unsafeData: tensor.data) ?? [] + } + self.updateResultsText(resultsText + results.description) + } catch let error { + self.updateResultsText( + "Failed to invoke the interpreter with error: \(error.localizedDescription)" + ) + return + } + } + } + + private func updateResultsText(_ text: String? = nil) { + safeDispatchOnMain { self.resultsTextView.text = text } + } +} + +// MARK: - Constants + +private enum Constant { + static let dispatchQueueLabel = "TensorFlowLiteInterpreterQueue" + static let nilInterpreterErrorMessage = + "Failed to invoke the interpreter because the interpreter was nil." +} + +/// Models that can be loaded by the TensorFlow Lite `Interpreter`. +private enum Model: Int, CustomStringConvertible { + /// A float model that performs two add operations on one input tensor and returns the result in + /// one output tensor. + case add = 0 + /// A quantized model that performs two add operations on one input tensor and returns the result + /// in one output tensor. + case addQuantized = 1 + /// A float model that performs three add operations on four input tensors and returns the results + /// in 2 output tensors. + case multiAdd = 2 + + var fileInfo: (name: String, extension: String) { + switch self { + case .add: + return Add.fileInfo + case .addQuantized: + return AddQuantized.fileInfo + case .multiAdd: + return MultiAdd.fileInfo + } + } + + // MARK: - CustomStringConvertible + + var description: String { + switch self { + case .add: + return Add.name + case .addQuantized: + return AddQuantized.name + case .multiAdd: + return MultiAdd.name + } + } +} + +/// Values for the `Add` model. +private enum Add { + static let name = "Add" + static let fileInfo = (name: "add", extension: "bin") +} + +/// Values for the `AddQuantized` model. +private enum AddQuantized { + static let name = "AddQuantized" + static let fileInfo = (name: "add_quantized", extension: "bin") +} + +/// Values for the `MultiAdd` model. +private enum MultiAdd { + static let name = "MultiAdd" + static let fileInfo = (name: "multi_add", extension: "bin") +} + +// MARK: - Fileprivate + +/// Safely dispatches the given block on the main queue. If the current thread is `main`, the block +/// is executed synchronously; otherwise, the block is executed asynchronously on the main thread. +fileprivate func safeDispatchOnMain(_ block: @escaping () -> Void) { + if Thread.isMainThread { block(); return } + DispatchQueue.main.async { block() } +} diff --git a/tensorflow/lite/experimental/swift/Tests/InterpreterOptionsTests.swift b/tensorflow/lite/experimental/swift/Tests/InterpreterOptionsTests.swift new file mode 100644 index 0000000000..54b4f59b28 --- /dev/null +++ b/tensorflow/lite/experimental/swift/Tests/InterpreterOptionsTests.swift @@ -0,0 +1,54 @@ +// Copyright 2018 Google Inc. 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. + +@testable import TensorFlowLite +import XCTest + +class InterpreterOptionsTests: XCTestCase { + + func testInterpreterOptions_InitWithDefaultValues() { + let options = InterpreterOptions() + XCTAssertNil(options.threadCount) + XCTAssertFalse(options.isErrorLoggingEnabled) + } + + func testInterpreterOptions_InitWithCustomValues() { + var options = InterpreterOptions() + options.threadCount = 2 + XCTAssertEqual(options.threadCount, 2) + options.isErrorLoggingEnabled = true + XCTAssertTrue(options.isErrorLoggingEnabled) + } + + func testInterpreterOptions_Equatable() { + var options1 = InterpreterOptions() + var options2 = InterpreterOptions() + XCTAssertEqual(options1, options2) + + options1.threadCount = 2 + options2.threadCount = 2 + XCTAssertEqual(options1, options2) + + options2.threadCount = 3 + XCTAssertNotEqual(options1, options2) + options2.threadCount = 2 + + options1.isErrorLoggingEnabled = true + options2.isErrorLoggingEnabled = true + XCTAssertEqual(options1, options2) + + options2.isErrorLoggingEnabled = false + XCTAssertNotEqual(options1, options2) + } +} diff --git a/tensorflow/lite/experimental/swift/Tests/InterpreterTests.swift b/tensorflow/lite/experimental/swift/Tests/InterpreterTests.swift new file mode 100644 index 0000000000..e98da5f951 --- /dev/null +++ b/tensorflow/lite/experimental/swift/Tests/InterpreterTests.swift @@ -0,0 +1,315 @@ +// Copyright 2018 Google Inc. 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. + +@testable import TensorFlowLite +import XCTest + +class InterpreterTests: XCTestCase { + + var interpreter: Interpreter! + + override func setUp() { + super.setUp() + + interpreter = try! Interpreter(modelPath: AddModel.path) + } + + override func tearDown() { + interpreter = nil + + super.tearDown() + } + + func testInterpreter_InitWithModelPath() { + XCTAssertNoThrow(try Interpreter(modelPath: AddModel.path)) + } + + func testInterpreter_Init_ThrowsFailedToLoadModel() { + XCTAssertThrowsError(try Interpreter(modelPath: "/invalid/path")) { error in + self.assertEqualErrors(actual: error, expected: .failedToLoadModel) + } + } + + func testInterpreter_InitWithModelPathAndOptions() { + var options = InterpreterOptions() + options.threadCount = 2 + XCTAssertNoThrow(try Interpreter(modelPath: AddModel.path, options: options)) + } + + func testInterpreter_InputTensorCount() { + XCTAssertEqual(interpreter.inputTensorCount, AddModel.inputTensorCount) + } + + func testInterpreter_OutputTensorCount() { + XCTAssertEqual(interpreter.outputTensorCount, AddModel.outputTensorCount) + } + + func testInterpreter_Invoke() throws { + try interpreter.allocateTensors() + XCTAssertNoThrow(try interpreter.invoke()) + } + + func testInterpreter_Invoke_ThrowsAllocateTensorsRequired_ModelNotReady() { + XCTAssertThrowsError(try interpreter.invoke()) { error in + self.assertEqualErrors(actual: error, expected: .allocateTensorsRequired) + } + } + + func testInterpreter_InputTensorAtIndex() throws { + try setUpAddModelInputTensor() + let inputTensor = try interpreter.input(at: AddModel.validIndex) + XCTAssertEqual(inputTensor, AddModel.inputTensor) + } + + func testInterpreter_InputTensorAtIndex_QuantizedModel() throws { + interpreter = try Interpreter(modelPath: AddQuantizedModel.path) + try setUpAddQuantizedModelInputTensor() + let inputTensor = try interpreter.input(at: AddQuantizedModel.inputOutputIndex) + XCTAssertEqual(inputTensor, AddQuantizedModel.inputTensor) + } + + func testInterpreter_InputTensorAtIndex_ThrowsInvalidIndex() throws { + try interpreter.allocateTensors() + XCTAssertThrowsError(try interpreter.input(at: AddModel.invalidIndex)) { error in + let maxIndex = AddModel.inputTensorCount - 1 + self.assertEqualErrors( + actual: error, + expected: .invalidTensorIndex(index: AddModel.invalidIndex, maxIndex: maxIndex) + ) + } + } + + func testInterpreter_InputTensorAtIndex_ThrowsAllocateTensorsRequired() { + XCTAssertThrowsError(try interpreter.input(at: AddModel.validIndex)) { error in + self.assertEqualErrors(actual: error, expected: .allocateTensorsRequired) + } + } + + func testInterpreter_OutputTensorAtIndex() throws { + try setUpAddModelInputTensor() + try interpreter.invoke() + let outputTensor = try interpreter.output(at: AddModel.validIndex) + XCTAssertEqual(outputTensor, AddModel.outputTensor) + let expectedResults = [Float32](unsafeData: outputTensor.data) + XCTAssertEqual(expectedResults, AddModel.results) + } + + func testInterpreter_OutputTensorAtIndex_QuantizedModel() throws { + interpreter = try Interpreter(modelPath: AddQuantizedModel.path) + try setUpAddQuantizedModelInputTensor() + try interpreter.invoke() + let outputTensor = try interpreter.output(at: AddQuantizedModel.inputOutputIndex) + XCTAssertEqual(outputTensor, AddQuantizedModel.outputTensor) + let expectedResults = [UInt8](outputTensor.data) + XCTAssertEqual(expectedResults, AddQuantizedModel.results) + } + + func testInterpreter_OutputTensorAtIndex_ThrowsInvalidIndex() throws { + try interpreter.allocateTensors() + try interpreter.invoke() + XCTAssertThrowsError(try interpreter.output(at: AddModel.invalidIndex)) { error in + let maxIndex = AddModel.outputTensorCount - 1 + self.assertEqualErrors( + actual: error, + expected: .invalidTensorIndex(index: AddModel.invalidIndex, maxIndex: maxIndex) + ) + } + } + + func testInterpreter_OutputTensorAtIndex_ThrowsInvokeInterpreterRequired() { + XCTAssertThrowsError(try interpreter.output(at: AddModel.validIndex)) { error in + self.assertEqualErrors(actual: error, expected: .invokeInterpreterRequired) + } + } + + func testInterpreter_ResizeInputTensorAtIndexToShape() { + XCTAssertNoThrow(try interpreter.resizeInput(at: AddModel.validIndex, to: [2, 2, 3])) + XCTAssertNoThrow(try interpreter.allocateTensors()) + } + + func testInterpreter_ResizeInputTensorAtIndexToShape_ThrowsInvalidIndex() { + XCTAssertThrowsError(try interpreter.resizeInput( + at: AddModel.invalidIndex, + to: [2, 2, 3] + )) { error in + let maxIndex = AddModel.inputTensorCount - 1 + self.assertEqualErrors( + actual: error, + expected: .invalidTensorIndex(index: AddModel.invalidIndex, maxIndex: maxIndex) + ) + } + } + + func testInterpreter_CopyDataToInputTensorAtIndex() throws { + try interpreter.resizeInput(at: AddModel.validIndex, to: AddModel.shape) + try interpreter.allocateTensors() + let inputTensor = try interpreter.copy(AddModel.inputData, toInputAt: AddModel.validIndex) + XCTAssertEqual(inputTensor.data, AddModel.inputData) + } + + func testInterpreter_CopyDataToInputTensorAtIndex_ThrowsInvalidIndex() { + XCTAssertThrowsError(try interpreter.copy( + AddModel.inputData, + toInputAt: AddModel.invalidIndex + )) { error in + let maxIndex = AddModel.inputTensorCount - 1 + self.assertEqualErrors( + actual: error, + expected: .invalidTensorIndex(index: AddModel.invalidIndex, maxIndex: maxIndex) + ) + } + } + + func testInterpreter_CopyDataToInputTensorAtIndex_ThrowsInvalidDataCount() throws { + try interpreter.resizeInput(at: AddModel.validIndex, to: AddModel.shape) + try interpreter.allocateTensors() + let invalidData = Data(count: AddModel.dataCount - 1) + XCTAssertThrowsError(try interpreter.copy( + invalidData, + toInputAt: AddModel.validIndex + )) { error in + self.assertEqualErrors( + actual: error, + expected: .invalidTensorDataCount(provided: invalidData.count, required: AddModel.dataCount) + ) + } + } + + func testInterpreter_AllocateTensors() { + XCTAssertNoThrow(try interpreter.allocateTensors()) + } + + // MARK: - Private + + private func setUpAddModelInputTensor() throws { + precondition(interpreter != nil) + try interpreter.resizeInput(at: AddModel.validIndex, to: AddModel.shape) + try interpreter.allocateTensors() + try interpreter.copy(AddModel.inputData, toInputAt: AddModel.validIndex) + } + + private func setUpAddQuantizedModelInputTensor() throws { + precondition(interpreter != nil) + try interpreter.resizeInput(at: AddQuantizedModel.inputOutputIndex, to: AddQuantizedModel.shape) + try interpreter.allocateTensors() + try interpreter.copy(AddQuantizedModel.inputData, toInputAt: AddQuantizedModel.inputOutputIndex) + } + + private func assertEqualErrors(actual: Error, expected: InterpreterError) { + guard let actual = actual as? InterpreterError else { + XCTFail("Actual error should be of type InterpreterError.") + return + } + XCTAssertEqual(actual, expected) + } +} + +// MARK: - Constants + +/// Values for the `add.bin` model. +private enum AddModel { + static let info = (name: "add", extension: "bin") + static let inputTensorCount = 1 + static let outputTensorCount = 1 + static let invalidIndex = 1 + static let validIndex = 0 + static let shape: TensorShape = [2] + static let dataCount = inputData.count + static let inputData = Data(copyingBufferOf: [Float32(1.0), Float32(3.0)]) + static let outputData = Data(copyingBufferOf: [Float32(3.0), Float32(9.0)]) + static let results = [Float32(3.0), Float32(9.0)] + + static let inputTensor = Tensor( + name: "input", + dataType: .float32, + shape: shape, + data: inputData + ) + static let outputTensor = Tensor( + name: "output", + dataType: .float32, + shape: shape, + data: outputData + ) + + static var path: String = { + let bundle = Bundle(for: InterpreterTests.self) + guard let path = bundle.path(forResource: info.name, ofType: info.extension) else { return "" } + return path + }() +} + +/// Values for the `add_quantized.bin` model. +private enum AddQuantizedModel { + static let info = (name: "add_quantized", extension: "bin") + static let inputOutputIndex = 0 + static let shape: TensorShape = [2] + static let inputData = Data([1, 3]) + static let outputData = Data([3, 9]) + static let quantizationParameters = QuantizationParameters(scale: 0.003922, zeroPoint: 0) + static let results: [UInt8] = [3, 9] + + static let inputTensor = Tensor( + name: "input", + dataType: .uInt8, + shape: shape, + data: inputData, + quantizationParameters: quantizationParameters + ) + static let outputTensor = Tensor( + name: "output", + dataType: .uInt8, + shape: shape, + data: outputData, + quantizationParameters: quantizationParameters + ) + + static var path: String = { + let bundle = Bundle(for: InterpreterTests.self) + guard let path = bundle.path(forResource: info.name, ofType: info.extension) else { return "" } + return path + }() +} + +// MARK: - Extensions + +extension Array { + /// Creates a new array from the bytes of the given unsafe data. + /// + /// - Note: Returns `nil` if `unsafeData.count` is not a multiple of + /// `MemoryLayout.stride`. + /// - Parameter unsafeData: The data containing the bytes to turn into an array. + init?(unsafeData: Data) { + guard unsafeData.count % MemoryLayout.stride == 0 else { return nil } + let elements = unsafeData.withUnsafeBytes { + UnsafeBufferPointer( + start: $0, + count: unsafeData.count / MemoryLayout.stride + ) + } + self.init(elements) + } +} + +extension Data { + /// Creates a new buffer by copying the buffer pointer of the given array. + /// + /// - Warning: The given array's element type `T` must be trivial in that it can be copied bit + /// for bit with no indirection or reference-counting operations; otherwise, reinterpreting + /// data from the resulting buffer has undefined behavior. + /// - Parameter array: An array with elements of type `T`. + init(copyingBufferOf array: [T]) { + self = array.withUnsafeBufferPointer(Data.init) + } +} diff --git a/tensorflow/lite/experimental/swift/Tests/ModelTests.swift b/tensorflow/lite/experimental/swift/Tests/ModelTests.swift new file mode 100644 index 0000000000..025db18906 --- /dev/null +++ b/tensorflow/lite/experimental/swift/Tests/ModelTests.swift @@ -0,0 +1,59 @@ +// Copyright 2018 Google Inc. 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. + +@testable import TensorFlowLite +import XCTest + +class ModelTests: XCTestCase { + + var modelPath: String! + + override func setUp() { + super.setUp() + + let bundle = Bundle(for: type(of: self)) + guard let modelPath = bundle.path( + forResource: Constant.modelInfo.name, + ofType: Constant.modelInfo.extension) + else { + XCTFail("Failed to get the model file path.") + return + } + self.modelPath = modelPath + } + + override func tearDown() { + modelPath = nil + + super.tearDown() + } + + func testModel_InitWithFilePath() { + XCTAssertNotNil(Model(filePath: modelPath)) + } + + func testModel_InitWithEmptyFilePath_FailsInitialization() { + XCTAssertNil(Model(filePath: "")) + } + + func testModel_InitWithInvalidFilePath_FailsInitialization() { + XCTAssertNil(Model(filePath: "invalid/path")) + } +} + +// MARK: - Constants + +private enum Constant { + static let modelInfo = (name: "add", extension: "bin") +} diff --git a/tensorflow/lite/experimental/swift/Tests/QuantizationParametersTests.swift b/tensorflow/lite/experimental/swift/Tests/QuantizationParametersTests.swift new file mode 100644 index 0000000000..65648c2698 --- /dev/null +++ b/tensorflow/lite/experimental/swift/Tests/QuantizationParametersTests.swift @@ -0,0 +1,43 @@ +// Copyright 2018 Google Inc. 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. + +@testable import TensorFlowLite +import XCTest + +class QuantizationParametersTests: XCTestCase { + + func testQuantizationParameters_InitWithCustomValues() { + let parameters = QuantizationParameters(scale: 0.5, zeroPoint: 1) + XCTAssertEqual(parameters.scale, 0.5) + XCTAssertEqual(parameters.zeroPoint, 1) + } + + func testQuantizationParameters_Equatable() { + let parameters1 = QuantizationParameters(scale: 0.5, zeroPoint: 1) + let parameters2 = QuantizationParameters(scale: 0.5, zeroPoint: 1) + XCTAssertEqual(parameters1, parameters2) + + let parameters3 = QuantizationParameters(scale: 0.4, zeroPoint: 1) + XCTAssertNotEqual(parameters1, parameters3) + XCTAssertNotEqual(parameters2, parameters3) + } +} + +// MARK: - Extensions + +extension QuantizationParameters: Equatable { + public static func == (lhs: QuantizationParameters, rhs: QuantizationParameters) -> Bool { + return lhs.scale == rhs.scale && lhs.zeroPoint == rhs.zeroPoint + } +} diff --git a/tensorflow/lite/experimental/swift/Tests/TensorTests.swift b/tensorflow/lite/experimental/swift/Tests/TensorTests.swift new file mode 100644 index 0000000000..4540043a16 --- /dev/null +++ b/tensorflow/lite/experimental/swift/Tests/TensorTests.swift @@ -0,0 +1,83 @@ +// Copyright 2018 Google Inc. 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. + +@testable import TensorFlowLite +import XCTest + +class TensorTests: XCTestCase { + + // MARK: - Tensor + + func testTensor_Init() { + let name = "InputTensor" + let dataType: TensorDataType = .uInt8 + let shape = TensorShape(Constant.dimensions) + guard let data = name.data(using: .utf8) else { XCTFail("Data should not be nil."); return } + let quantizationParameters = QuantizationParameters(scale: 0.5, zeroPoint: 1) + let inputTensor = Tensor( + name: name, + dataType: dataType, + shape: shape, + data: data, + quantizationParameters: quantizationParameters + ) + XCTAssertEqual(inputTensor.name, name) + XCTAssertEqual(inputTensor.dataType, dataType) + XCTAssertEqual(inputTensor.shape, shape) + XCTAssertEqual(inputTensor.data, data) + XCTAssertEqual(inputTensor.quantizationParameters, quantizationParameters) + } + + // MARK: - TensorShape + + func testTensorShape_InitWithArray() { + let shape = TensorShape(Constant.dimensions) + XCTAssertEqual(shape.rank, Constant.dimensions.count) + XCTAssertEqual(shape.dimensions, Constant.dimensions) + } + + func testTensorShape_InitWithElements() { + let shape = TensorShape(2, 2, 3) + XCTAssertEqual(shape.rank, Constant.dimensions.count) + XCTAssertEqual(shape.dimensions, Constant.dimensions) + } + + func testTensorShape_InitWithArrayLiteral() { + let shape: TensorShape = [2, 2, 3] + XCTAssertEqual(shape.rank, Constant.dimensions.count) + XCTAssertEqual(shape.dimensions, Constant.dimensions) + } +} + +// MARK: - Constants + +private enum Constant { + /// Array of 2 arrays of 2 arrays of 3 numbers: [[[1, 1, 1], [2, 2, 2]], [[3, 3, 3], [4, 4, 4]]]. + static let dimensions = [2, 2, 3] +} + +// MARK: - Extensions + +extension TensorShape: Equatable { + public static func == (lhs: TensorShape, rhs: TensorShape) -> Bool { + return lhs.rank == rhs.rank && lhs.dimensions == rhs.dimensions + } +} + +extension Tensor: Equatable { + public static func == (lhs: Tensor, rhs: Tensor) -> Bool { + return lhs.name == rhs.name && lhs.dataType == rhs.dataType && lhs.shape == rhs.shape && + lhs.data == rhs.data && lhs.quantizationParameters == rhs.quantizationParameters + } +} diff --git a/tensorflow/tools/pip_package/pip_smoke_test.py b/tensorflow/tools/pip_package/pip_smoke_test.py index 51d010c9e1..952c71c615 100644 --- a/tensorflow/tools/pip_package/pip_smoke_test.py +++ b/tensorflow/tools/pip_package/pip_smoke_test.py @@ -30,14 +30,19 @@ os.chdir(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../.."))) PIP_PACKAGE_QUERY_EXPRESSION = ( "deps(//tensorflow/tools/pip_package:build_pip_package)") +# List of file paths containing BUILD files that should not be included for the +# pip smoke test. +BUILD_BLACKLIST = [ + "tensorflow/lite/examples/android", + "tensorflow/lite/experimental/swift", +] def GetBuild(dir_base): """Get the list of BUILD file all targets recursively startind at dir_base.""" items = [] for root, _, files in os.walk(dir_base): for name in files: - if (name == "BUILD" and - root.find("tensorflow/lite/examples/android") == -1): + if (name == "BUILD" and root not in BUILD_BLACKLIST): items.append("//" + root + ":all") return items @@ -67,9 +72,9 @@ def BuildPyTestDependencies(): PYTHON_TARGETS, PY_TEST_QUERY_EXPRESSION = BuildPyTestDependencies() -# Hard-coded blacklist of files if not included in pip package # TODO(amitpatankar): Clean up blacklist. -BLACKLIST = [ +# List of dependencies that should not included in the pip package. +DEPENDENCY_BLACKLIST = [ "//tensorflow/python:extra_py_tests_deps", "//tensorflow/cc/saved_model:saved_model_half_plus_two", "//tensorflow:no_tensorflow_py_deps", @@ -82,9 +87,7 @@ BLACKLIST = [ "//tensorflow/core/kernels/cloud:bigquery_reader_ops", "//tensorflow/python/feature_column:vocabulary_testdata", "//tensorflow/python:framework/test_file_system.so", - # contrib - "//tensorflow/contrib/session_bundle:session_bundle_half_plus_two", - "//tensorflow/contrib/keras:testing_utils", + # lite "//tensorflow/lite/experimental/examples/lstm:tflite_lstm", "//tensorflow/lite/experimental/examples/lstm:tflite_lstm.py", "//tensorflow/lite/experimental/examples/lstm:unidirectional_sequence_lstm_test", # pylint:disable=line-too-long @@ -93,6 +96,9 @@ BLACKLIST = [ "//tensorflow/lite/python:interpreter_test", "//tensorflow/lite/python:interpreter.py", "//tensorflow/lite/python:interpreter_test.py", + # contrib + "//tensorflow/contrib/session_bundle:session_bundle_half_plus_two", + "//tensorflow/contrib/keras:testing_utils", "//tensorflow/contrib/ffmpeg:test_data", "//tensorflow/contrib/fused_conv:fused_conv2d_bias_activation_op_test_base", "//tensorflow/contrib/hadoop:test_data", @@ -149,8 +155,8 @@ def main(): # File extensions and endings to ignore ignore_extensions = ["_test", "_test.py", "_test_gpu", "_test_gpu.py"] - ignored_files = 0 - blacklisted_files = len(BLACKLIST) + ignored_files_count = 0 + blacklisted_dependencies_count = len(DEPENDENCY_BLACKLIST) # Compare dependencies for dependency in tf_py_test_dependencies_list: if dependency and dependency.startswith("//tensorflow"): @@ -158,16 +164,16 @@ def main(): # Ignore extensions if any(dependency.endswith(ext) for ext in ignore_extensions): ignore = True - ignored_files += 1 + ignored_files_count += 1 - # Check if the dependency is in the pip package, the blacklist, or - # should be ignored because of its file extension + # Check if the dependency is in the pip package, the dependency blacklist, + # or should be ignored because of its file extension. if not (ignore or dependency in pip_package_dependencies_list or - dependency in BLACKLIST): + dependency in DEPENDENCY_BLACKLIST): missing_dependencies.append(dependency) - print("Ignored files: %d" % ignored_files) - print("Blacklisted files: %d" % blacklisted_files) + print("Ignored files count: %d" % ignored_files_count) + print("Blacklisted dependencies count: %d" % blacklisted_dependencies_count) if missing_dependencies: print("Missing the following dependencies from pip_packages:") for missing_dependency in missing_dependencies: -- GitLab From d28fddf859f0d9093a1279ceda52ebfb2d915788 Mon Sep 17 00:00:00 2001 From: Pavithra Vijay Date: Fri, 4 Jan 2019 10:57:17 -0800 Subject: [PATCH 347/622] Fix some metric doc strings. PiperOrigin-RevId: 227880840 --- tensorflow/python/keras/metrics.py | 42 +++++++++++++++--------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/tensorflow/python/keras/metrics.py b/tensorflow/python/keras/metrics.py index 73c116a745..7f13cc46e3 100644 --- a/tensorflow/python/keras/metrics.py +++ b/tensorflow/python/keras/metrics.py @@ -1382,8 +1382,8 @@ class MeanAbsoluteError(MeanMetricWrapper): Usage: ```python - mae = tf.metrics.MeanAbsoluteError() - mae.update_state([0., 0., 1., 1.], [1., 1., 1., 0.]) + m = tf.metrics.MeanAbsoluteError() + m.update_state([0., 0., 1., 1.], [1., 1., 1., 0.]) print('Final result: ', m.result().numpy()) # Final result: 0.75 ``` @@ -1391,7 +1391,7 @@ class MeanAbsoluteError(MeanMetricWrapper): ```python model = keras.models.Model(inputs, outputs) - model.compile('sgd', loss=tf.keras.losses.MeanAbsoluteError()) + model.compile('sgd', metrics=[tf.keras.metrics.MeanAbsoluteError()]) ``` """ @@ -1416,8 +1416,8 @@ class MeanAbsolutePercentageError(MeanMetricWrapper): Usage: ```python - mape = tf.keras.losses.MeanAbsolutePercentageError() - mape.update_state([0., 0., 1., 1.], [1., 1., 1., 0.]) + m = tf.keras.metrics.MeanAbsolutePercentageError() + m.update_state([0., 0., 1., 1.], [1., 1., 1., 0.]) print('Final result: ', m.result().numpy()) # Final result: 5e+08 ``` @@ -1425,7 +1425,7 @@ class MeanAbsolutePercentageError(MeanMetricWrapper): ```python model = keras.models.Model(inputs, outputs) - model.compile('sgd', loss=tf.keras.losses.MeanAbsolutePercentageError()) + model.compile('sgd', metrics=[tf.keras.metrics.MeanAbsolutePercentageError()]) ``` """ @@ -1450,8 +1450,8 @@ class MeanSquaredError(MeanMetricWrapper): Usage: ```python - mape = tf.keras.losses.MeanSquaredError() - mape.update_state([0., 0., 1., 1.], [1., 1., 1., 0.]) + m = tf.keras.metrics.MeanSquaredError() + m.update_state([0., 0., 1., 1.], [1., 1., 1., 0.]) print('Final result: ', m.result().numpy()) # Final result: 0.75 ``` @@ -1459,7 +1459,7 @@ class MeanSquaredError(MeanMetricWrapper): ```python model = keras.models.Model(inputs, outputs) - model.compile('sgd', loss=tf.keras.losses.MeanSquaredError()) + model.compile('sgd', metrics=[tf.keras.metrics.MeanSquaredError()]) ``` """ @@ -1484,8 +1484,8 @@ class MeanSquaredLogarithmicError(MeanMetricWrapper): Usage: ```python - msle = tf.keras.losses.MeanSquaredLogarithmicError() - msle.update_state([0., 0., 1., 1.], [1., 1., 1., 0.]) + m = tf.keras.metrics.MeanSquaredLogarithmicError() + m.update_state([0., 0., 1., 1.], [1., 1., 1., 0.]) print('Final result: ', m.result().numpy()) # Final result: 0.36034 ``` @@ -1493,7 +1493,7 @@ class MeanSquaredLogarithmicError(MeanMetricWrapper): ```python model = keras.models.Model(inputs, outputs) - model.compile('sgd', loss=tf.keras.losses.MeanSquaredLogarithmicError()) + model.compile('sgd', metrics=[tf.keras.metrics.MeanSquaredLogarithmicError()]) ``` """ @@ -1518,8 +1518,8 @@ class Hinge(MeanMetricWrapper): Usage: ```python - h = tf.keras.metrics.Hinge() - h.update_state([0., 1., 1.], [1., 0., 1.]) + m = tf.keras.metrics.Hinge() + m.update_state([0., 1., 1.], [1., 0., 1.]) print('Final result: ', m.result().numpy()) # Final result: 0.66 ``` @@ -1527,7 +1527,7 @@ class Hinge(MeanMetricWrapper): ```python model = keras.models.Model(inputs, outputs) - model.compile('sgd', loss=tf.keras.metrics.Hinge()) + model.compile('sgd', metrics=[tf.keras.metrics.Hinge()]) ``` """ @@ -1551,8 +1551,8 @@ class SquaredHinge(MeanMetricWrapper): Usage: ```python - h = tf.keras.metrics.SquaredHinge() - h.update_state([0., 1., 1.], [1., 0., 1.]) + m = tf.keras.metrics.SquaredHinge() + m.update_state([0., 1., 1.], [1., 0., 1.]) print('Final result: ', m.result().numpy()) # Final result: 0.66 ``` @@ -1560,7 +1560,7 @@ class SquaredHinge(MeanMetricWrapper): ```python model = keras.models.Model(inputs, outputs) - model.compile('sgd', loss=tf.keras.metrics.SquaredHinge()) + model.compile('sgd', metrics=[tf.keras.metrics.SquaredHinge()]) ``` """ @@ -1584,8 +1584,8 @@ class CategoricalHinge(MeanMetricWrapper): Usage: ```python - h = tf.keras.metrics.CategoricalHinge() - h.update_state([0., 1., 1.], [1., 0., 1.]) + m = tf.keras.metrics.CategoricalHinge() + m.update_state([0., 1., 1.], [1., 0., 1.]) print('Final result: ', m.result().numpy()) # Final result: 1.0 ``` @@ -1593,7 +1593,7 @@ class CategoricalHinge(MeanMetricWrapper): ```python model = keras.models.Model(inputs, outputs) - model.compile('sgd', loss=tf.keras.metrics.CategoricalHinge()) + model.compile('sgd', metrics=[tf.keras.metrics.CategoricalHinge()]) ``` """ -- GitLab From 49d70dff868661bc7aa999227dad64bde2538899 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Fri, 4 Jan 2019 10:58:35 -0800 Subject: [PATCH 348/622] Make tf.where documentation clearer. PiperOrigin-RevId: 227881065 --- tensorflow/python/ops/array_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/python/ops/array_ops.py b/tensorflow/python/ops/array_ops.py index 07a7e180fb..81558db04f 100644 --- a/tensorflow/python/ops/array_ops.py +++ b/tensorflow/python/ops/array_ops.py @@ -3191,7 +3191,7 @@ def where(condition, x=None, y=None, name=None): Returns: A `Tensor` with the same type and shape as `x`, `y` if they are non-None. - A `Tensor` with shape `(num_true, dim_size(condition))`. + Otherwise, a `Tensor` with shape `(num_true, rank(condition))`. Raises: ValueError: When exactly one of `x` or `y` is non-None. -- GitLab From 9d72bb94c054b9d3a00f39e6a99c02995b207ae6 Mon Sep 17 00:00:00 2001 From: Bixia Zheng Date: Fri, 4 Jan 2019 11:04:06 -0800 Subject: [PATCH 349/622] [XLA] The HLO evaluator should preserve the layouts for instructions inside a fusion. When evaluating a fusion, the HLO evaluator makes a copy of the fusion and resets the layout of each instruction in the copy to default layout before evaluating the copy of the fusion. In doing that, it changes the layout of the fusion intputs and outputs and causes incorrect results. This change fixes the evaluator to not overwrite the layouts for the instructions inside a fusion. Add test cases for fusion inputs, single output fusion and multiple output fusion. PiperOrigin-RevId: 227882142 --- .../compiler/xla/service/hlo_evaluator.cc | 4 +- .../xla/service/hlo_evaluator_test.cc | 66 +++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/tensorflow/compiler/xla/service/hlo_evaluator.cc b/tensorflow/compiler/xla/service/hlo_evaluator.cc index e897d09e1b..9754b8906b 100644 --- a/tensorflow/compiler/xla/service/hlo_evaluator.cc +++ b/tensorflow/compiler/xla/service/hlo_evaluator.cc @@ -1097,7 +1097,9 @@ Status HloEvaluator::HandleFusion(HloInstruction* fusion) { fusion->fused_instructions_computation()->Clone( /*suffix=*/"clone_with_layout", &context); for (auto* instruction : cloned_fused_computation->instructions()) { - LayoutUtil::SetToDefaultLayout(instruction->mutable_shape()); + if (!LayoutUtil::HasLayout(instruction->shape())) { + LayoutUtil::SetToDefaultLayout(instruction->mutable_shape()); + } } auto readded_computation = empty_hlo_module.AddEntryComputation(std::move(cloned_fused_computation)); diff --git a/tensorflow/compiler/xla/service/hlo_evaluator_test.cc b/tensorflow/compiler/xla/service/hlo_evaluator_test.cc index 0298da2174..8056193d99 100644 --- a/tensorflow/compiler/xla/service/hlo_evaluator_test.cc +++ b/tensorflow/compiler/xla/service/hlo_evaluator_test.cc @@ -2924,5 +2924,71 @@ ENTRY main { "Expected 1 argument, but got 2."); } +TEST_F(HloEvaluatorTest, PreserveFusionInputLayout) { + constexpr absl::string_view hlo_text = R"( + HloModule FusionInputLayout + + fused_computation { + param_0 = f32[20,20]{0,1} parameter(0) + ROOT bitcast = f32[20,20]{1,0} bitcast(param_0) + } + + ENTRY kernel_entry { + parameter.0 = f32[20,20]{0,1} parameter(0) + ROOT fusion = f32[20,20]{1,0} fusion(parameter.0), + kind=kLoop, calls=fused_computation + })"; + + TF_ASSERT_OK_AND_ASSIGN(m_, ParseAndReturnVerifiedModule(hlo_text)); + auto args = MakeFakeArguments(m_.get()).ConsumeValueOrDie(); + Literal actual = Evaluate({&args[0]}); + EXPECT_TRUE(absl::c_equal(args[0].data(), actual.data())); +} + +TEST_F(HloEvaluatorTest, PreserveFusionOutputLayout) { + constexpr absl::string_view hlo_text = R"( + HloModule FusionOutputLayout + + fused_computation { + param_0 = f32[20,20]{1,0} parameter(0) + ROOT bitcast = f32[20,20]{0,1} bitcast(param_0) + } + + ENTRY kernel_entry { + parameter.0 = f32[20,20]{1,0} parameter(0) + ROOT fusion = f32[20,20]{0,1} fusion(parameter.0), + kind=kLoop, calls=fused_computation + })"; + + TF_ASSERT_OK_AND_ASSIGN(m_, ParseAndReturnVerifiedModule(hlo_text)); + auto args = MakeFakeArguments(m_.get()).ConsumeValueOrDie(); + Literal actual = Evaluate({&args[0]}); + EXPECT_TRUE(absl::c_equal(args[0].data(), actual.data())); +} + +TEST_F(HloEvaluatorTest, PreserveMOFusionOutputLayout) { + constexpr absl::string_view hlo_text = R"( + HloModule MOFusionOutputLayout + + fused_computation { + param_0 = f32[20,20]{1,0} parameter(0) + bitcast = f32[20,20]{0,1} bitcast(param_0) + ROOT tuple = (f32[20,20]{0,1}) tuple(bitcast) + } + + ENTRY kernel_entry { + parameter.0 = f32[20,20]{1,0} parameter(0) + ROOT fusion = (f32[20,20]{0,1}) fusion(parameter.0), + kind=kLoop, calls=fused_computation + })"; + + TF_ASSERT_OK_AND_ASSIGN(m_, ParseAndReturnVerifiedModule(hlo_text)); + auto args = MakeFakeArguments(m_.get()).ConsumeValueOrDie(); + Literal actual_tuple = Evaluate({&args[0]}); + std::vector actual_literals = actual_tuple.DecomposeTuple(); + EXPECT_TRUE( + absl::c_equal(args[0].data(), actual_literals[0].data())); +} + } // namespace } // namespace xla -- GitLab From d76c2d0a570495854617c318a2708636f5530f27 Mon Sep 17 00:00:00 2001 From: Sourabh Bajaj Date: Fri, 4 Jan 2019 11:10:12 -0800 Subject: [PATCH 350/622] Improve the error message for model fit/eval/predict in scope PiperOrigin-RevId: 227883249 --- tensorflow/contrib/distribute/python/keras_test.py | 2 +- .../python/keras/engine/distributed_training_utils.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tensorflow/contrib/distribute/python/keras_test.py b/tensorflow/contrib/distribute/python/keras_test.py index 4cfcc0375f..1cb5fa30a3 100644 --- a/tensorflow/contrib/distribute/python/keras_test.py +++ b/tensorflow/contrib/distribute/python/keras_test.py @@ -1196,7 +1196,7 @@ class TestDistributionStrategyValidation(test.TestCase, def test_loop_in_scope(self, distribution): with self.cached_session(): with self.assertRaisesRegexp( - RuntimeError, 'should not be run inside the distribution strategy'): + RuntimeError, 'should not be run inside the tf.distribute.Strategy'): with distribution.scope(): x = keras.layers.Input(shape=(3,), name='input') y = keras.layers.Dense(4, name='dense')(x) diff --git a/tensorflow/python/keras/engine/distributed_training_utils.py b/tensorflow/python/keras/engine/distributed_training_utils.py index 9e3bdd26cb..1678d84307 100644 --- a/tensorflow/python/keras/engine/distributed_training_utils.py +++ b/tensorflow/python/keras/engine/distributed_training_utils.py @@ -48,9 +48,9 @@ def validate_not_in_strategy_scope(): if distribution_strategy_context.has_distribution_strategy(): if distribution_strategy_context.in_cross_replica_context(): raise RuntimeError( - 'Fit/Eval/Predict should not be run inside the distribution strategy ' - 'scope. Only model creation and compilation should be in ' - 'distribution strategy scope.') + 'Fit/Eval/Predict should not be run inside the tf.distribute.Strategy' + ' scope. Only model creation and compilation should be in ' + 'tf.distribute.Strategy scope.') def set_weights(distribution_strategy, dist_model, weights): -- GitLab From 735d26a821d9f69f58ad12ea841da9bc8c5a65ee Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Fri, 4 Jan 2019 11:18:24 -0800 Subject: [PATCH 351/622] Make session_handle of DirectSession an unique identifier and accessible from OptimizationPassRegistry::PRE_PLACEMENT pass. PiperOrigin-RevId: 227884596 --- tensorflow/core/common_runtime/direct_session.cc | 11 +++++++---- tensorflow/core/common_runtime/direct_session.h | 1 + tensorflow/core/common_runtime/executor.cc | 3 +++ tensorflow/core/common_runtime/executor.h | 2 ++ .../core/common_runtime/graph_execution_state.cc | 3 +++ .../core/common_runtime/graph_execution_state.h | 4 ++++ .../core/common_runtime/optimization_registry.h | 1 + tensorflow/core/framework/op_kernel.h | 6 ++++++ 8 files changed, 27 insertions(+), 4 deletions(-) diff --git a/tensorflow/core/common_runtime/direct_session.cc b/tensorflow/core/common_runtime/direct_session.cc index 51b2c68c76..36f1a92aab 100644 --- a/tensorflow/core/common_runtime/direct_session.cc +++ b/tensorflow/core/common_runtime/direct_session.cc @@ -59,6 +59,7 @@ limitations under the License. #include "tensorflow/core/lib/gtl/array_slice.h" #include "tensorflow/core/lib/gtl/stl_util.h" #include "tensorflow/core/lib/monitoring/counter.h" +#include "tensorflow/core/lib/random/random.h" #include "tensorflow/core/lib/strings/numbers.h" #include "tensorflow/core/lib/strings/str_util.h" #include "tensorflow/core/lib/strings/strcat.h" @@ -303,10 +304,8 @@ DirectSession::DirectSession(const SessionOptions& options, if (!status.ok()) { LOG(ERROR) << status.error_message(); } - // NOTE(mrry): We do not need to use a unique string for the session - // handle, because DirectSession owns its devices. This may change - // in future versions. - session_handle_ = "direct"; + session_handle_ = + strings::StrCat("direct", strings::FpToString(random::New64())); int devices_added = 0; if (options.config.log_device_placement()) { const string mapping_str = device_mgr_->DeviceMappingString(); @@ -371,6 +370,7 @@ Status DirectSession::MaybeInitializeExecutionState( GraphExecutionStateOptions options; options.device_set = &device_set_; options.session_options = &options_; + options.session_handle = session_handle_; // TODO(mrry,suharshs): We explicitly copy `graph` so that // `MakeForBaseGraph()` can take ownership of its // contents. Previously this happened implicitly in calls to the @@ -533,6 +533,7 @@ Status DirectSession::RunInternal(int64 step_id, const RunOptions& run_options, CancellationManager step_cancellation_manager; args.cancellation_manager = &step_cancellation_manager; args.session_state = &session_state_; + args.session_handle = session_handle_; args.tensor_store = &run_state.tensor_store; args.step_container = &run_state.step_container; args.sync_on_finish = sync_on_finish_; @@ -888,6 +889,7 @@ Status DirectSession::PRunSetup(const std::vector& input_names, SchedClosure(pool, std::move(c)); }; args.session_state = &session_state_; + args.session_handle = session_handle_; args.tensor_store = &run_state->tensor_store; args.step_container = &run_state->step_container; if (LogMemory::IsEnabled()) { @@ -1465,6 +1467,7 @@ Status DirectSession::CreateGraphs( prune_options.device_set = &device_set_; prune_options.session_options = &options_; prune_options.stateful_placements = stateful_placements_; + prune_options.session_handle = session_handle_; TF_RETURN_IF_ERROR(GraphExecutionState::MakeForPrunedGraph( execution_state_->original_graph_def().library(), prune_options, execution_state_->original_graph_def(), subgraph_options, diff --git a/tensorflow/core/common_runtime/direct_session.h b/tensorflow/core/common_runtime/direct_session.h index 6754e9cfb7..bcac341544 100644 --- a/tensorflow/core/common_runtime/direct_session.h +++ b/tensorflow/core/common_runtime/direct_session.h @@ -317,6 +317,7 @@ class DirectSession : public Session { std::vector devices_; // not owned DeviceSet device_set_; + // Unique session identifier. string session_handle_; mutex graph_state_lock_; bool graph_created_ GUARDED_BY(graph_state_lock_) = false; diff --git a/tensorflow/core/common_runtime/executor.cc b/tensorflow/core/common_runtime/executor.cc index df2ee6c618..07c8c4a5d4 100644 --- a/tensorflow/core/common_runtime/executor.cc +++ b/tensorflow/core/common_runtime/executor.cc @@ -1244,6 +1244,7 @@ class ExecutorState { Rendezvous* rendezvous_; CollectiveExecutor* collective_executor_ = nullptr; SessionState* session_state_; + string session_handle_; TensorStore* tensor_store_; // Step-local container. ScopedStepContainer* step_container_; @@ -1371,6 +1372,7 @@ ExecutorState::ExecutorState(const Executor::Args& args, ExecutorImpl* impl) rendezvous_(args.rendezvous), collective_executor_(args.collective_executor), session_state_(args.session_state), + session_handle_(args.session_handle), tensor_store_(args.tensor_store), step_container_(args.step_container), stats_collector_(args.stats_collector), @@ -1616,6 +1618,7 @@ void ExecutorState::Process(TaggedNode tagged_node, int64 scheduled_nsec) { params.rendezvous = rendezvous_; params.collective_executor = collective_executor_; params.session_state = session_state_; + params.session_handle = session_handle_; params.tensor_store = tensor_store_; params.cancellation_manager = cancellation_manager_; params.call_frame = call_frame_; diff --git a/tensorflow/core/common_runtime/executor.h b/tensorflow/core/common_runtime/executor.h index 02930168a4..4be60c6771 100644 --- a/tensorflow/core/common_runtime/executor.h +++ b/tensorflow/core/common_runtime/executor.h @@ -88,6 +88,8 @@ class Executor { CallFrameInterface* call_frame = nullptr; CancellationManager* cancellation_manager = nullptr; SessionState* session_state = nullptr; + // Unique session identifier. Can be empty. + string session_handle; TensorStore* tensor_store = nullptr; ScopedStepContainer* step_container = nullptr; CollectiveExecutor* collective_executor = nullptr; diff --git a/tensorflow/core/common_runtime/graph_execution_state.cc b/tensorflow/core/common_runtime/graph_execution_state.cc index 04d658f047..9ecbc34f5f 100644 --- a/tensorflow/core/common_runtime/graph_execution_state.cc +++ b/tensorflow/core/common_runtime/graph_execution_state.cc @@ -59,6 +59,7 @@ GraphExecutionState::GraphExecutionState( : stateful_placements_(options.stateful_placements), device_set_(options.device_set), session_options_(options.session_options), + session_handle_(options.session_handle), flib_def_(new FunctionLibraryDefinition(OpRegistry::Global(), graph_def->library())), graph_(nullptr) { @@ -198,6 +199,7 @@ Status GraphExecutionState::Extend( GraphExecutionStateOptions combined_options; combined_options.device_set = device_set_; combined_options.session_options = session_options_; + combined_options.session_handle = session_handle_; combined_options.stateful_placements = stateful_placements_; // NOTE(mrry): `gdef` is no longer valid after the constructor @@ -558,6 +560,7 @@ Status GraphExecutionState::InitBaseGraph(const BuildGraphOptions& options) { RestoreStatefulNodes(new_graph.get()); GraphOptimizationPassOptions optimization_options; + optimization_options.session_handle = session_handle_; optimization_options.session_options = session_options_; optimization_options.graph = &new_graph; optimization_options.flib_def = flib_def_.get(); diff --git a/tensorflow/core/common_runtime/graph_execution_state.h b/tensorflow/core/common_runtime/graph_execution_state.h index 9cabe478a6..56315bb1ef 100644 --- a/tensorflow/core/common_runtime/graph_execution_state.h +++ b/tensorflow/core/common_runtime/graph_execution_state.h @@ -41,6 +41,8 @@ struct RewriteGraphMetadata; struct GraphExecutionStateOptions { const DeviceSet* device_set = nullptr; const SessionOptions* session_options = nullptr; + // Unique session identifier. Can be empty. + string session_handle; // A map from node name to device name, representing the unchangeable // placement of stateful nodes. std::unordered_map stateful_placements; @@ -192,6 +194,8 @@ class GraphExecutionState { GraphDef original_graph_def_; // Immutable after ctor. const DeviceSet* device_set_; // Not owned const SessionOptions* session_options_; // Not owned + // Unique session identifier. Can be empty. + string session_handle_; // Map from name to Node for the full graph in placed_. NodeNameToCostIdMap node_name_to_cost_id_map_; diff --git a/tensorflow/core/common_runtime/optimization_registry.h b/tensorflow/core/common_runtime/optimization_registry.h index 6fcd2afd27..19a76529bb 100644 --- a/tensorflow/core/common_runtime/optimization_registry.h +++ b/tensorflow/core/common_runtime/optimization_registry.h @@ -35,6 +35,7 @@ struct SessionOptions; // as a key into a state dictionary if it wants to keep state across // calls. struct GraphOptimizationPassOptions { + // Filled in by DirectSession for PRE_PLACEMENT optimizations. Can be empty. string session_handle; const SessionOptions* session_options = nullptr; const CostModel* cost_model = nullptr; diff --git a/tensorflow/core/framework/op_kernel.h b/tensorflow/core/framework/op_kernel.h index bccb2bf3c7..aa07cbd380 100644 --- a/tensorflow/core/framework/op_kernel.h +++ b/tensorflow/core/framework/op_kernel.h @@ -606,6 +606,9 @@ class OpKernelContext { // The session state for this op. SessionState* session_state = nullptr; + // Unique session identifier. Can be empty. + string session_handle; + // The tensor store for this op. TensorStore* tensor_store = nullptr; @@ -1034,6 +1037,9 @@ class OpKernelContext { // An op kernel can access the session state it belongs to. SessionState* session_state() const { return params_->session_state; } + // Unique identifier of the session it belongs to. Can be empty. + string session_handle() const { return params_->session_handle; } + // An op kernel can access the tensor store of the run it belongs to. TensorStore* tensor_store() const { return params_->tensor_store; } -- GitLab From 8c22259497e9914c37a7adcd33aebaf754473a02 Mon Sep 17 00:00:00 2001 From: Laurent Le Brun Date: Fri, 4 Jan 2019 20:37:31 +0100 Subject: [PATCH 352/622] Update dependency on rules_closure This makes this command succeed: bazel build //tensorflow/tools/pip_package:build_pip_package --incompatible_disallow_data_transition This is needed for a future change in Bazel. --- WORKSPACE | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index 2277e83a3f..1c59686f16 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -4,11 +4,11 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive", "http_file" http_archive( name = "io_bazel_rules_closure", - sha256 = "a38539c5b5c358548e75b44141b4ab637bba7c4dc02b46b1f62a96d6433f56ae", - strip_prefix = "rules_closure-dbb96841cc0a5fb2664c37822803b06dab20c7d1", + sha256 = "43c9b882fa921923bcba764453f4058d102bece35a37c9f6383c713004aacff1", + strip_prefix = "rules_closure-9889e2348259a5aad7e805547c1a0cf311cfcd91", urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/rules_closure/archive/dbb96841cc0a5fb2664c37822803b06dab20c7d1.tar.gz", - "https://github.com/bazelbuild/rules_closure/archive/dbb96841cc0a5fb2664c37822803b06dab20c7d1.tar.gz", # 2018-04-13 + "https://mirror.bazel.build/github.com/bazelbuild/rules_closure/archive/9889e2348259a5aad7e805547c1a0cf311cfcd91.tar.gz", + "https://github.com/bazelbuild/rules_closure/archive/9889e2348259a5aad7e805547c1a0cf311cfcd91.tar.gz", # 2018-12-21 ], ) -- GitLab From 3e37a58b6615582cca0006be22d95edf4945f078 Mon Sep 17 00:00:00 2001 From: Pete Warden Date: Fri, 4 Jan 2019 11:44:16 -0800 Subject: [PATCH 353/622] Update documentation for Apollo3 board PiperOrigin-RevId: 227889248 --- tensorflow/lite/experimental/micro/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tensorflow/lite/experimental/micro/README.md b/tensorflow/lite/experimental/micro/README.md index dab7ae80f3..464c7b6ad7 100644 --- a/tensorflow/lite/experimental/micro/README.md +++ b/tensorflow/lite/experimental/micro/README.md @@ -187,7 +187,7 @@ You should see the following log with the magic string `~~~ALL TEST PASSED~~~`: 02:25:22.4253 [DEBUG] uart0: [+0.16ms host +0s virt 0.28s virt from start] Progam has exited with code:0x00000000 ``` -## Building for Apollo3 +## Building for Ambiq Micro Apollo3Blue EVB Follow these steps to get the pushbutton yes/no example working on Apollo 3: @@ -201,7 +201,8 @@ Follow these steps to get the pushbutton yes/no example working on Apollo 3: tensorflow/lite/experimental/micro/tools/make/Makefile TARGET=apollo3evb pushbutton_cmsis_speech_test_bin 4. Install [Segger JLink tools](https://www.segger.com/downloads/jlink/) -5. Connect the Apollo3 EVB (with mic shield) to the computer and power it on +5. Connect the Apollo3 EVB (with mic shield in slot 3 of Microbus Shield board) + to the computer and power it on. 6. Start the GDB server in a new terminal with the following command: JLinkGDBServer -select USB -device AMA3B1KK-KBR -endian little -if SWD -speed 1000 -noir -noLocalhostOnly -- GitLab From 37afc40b6f33073193114c081bcda939289deca0 Mon Sep 17 00:00:00 2001 From: Tim Shen Date: Fri, 4 Jan 2019 11:57:01 -0800 Subject: [PATCH 354/622] Roll-forward with fix: Fix dso_loader.cc's includes. tf_custom_op_library already adds core:stream_executor_headers_lib as a dep, so we must not add a duplicated dep here. PiperOrigin-RevId: 227891141 --- tensorflow/contrib/mpi_collectives/BUILD | 1 - tensorflow/stream_executor/platform/default/dso_loader.cc | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tensorflow/contrib/mpi_collectives/BUILD b/tensorflow/contrib/mpi_collectives/BUILD index ecac06354d..a7be92a35e 100644 --- a/tensorflow/contrib/mpi_collectives/BUILD +++ b/tensorflow/contrib/mpi_collectives/BUILD @@ -52,7 +52,6 @@ tf_custom_op_library( deps = [ ":mpi_defines", ":mpi_message_proto_cc", - "//tensorflow/stream_executor:stream_executor_headers_lib", "//third_party/mpi", ], ) diff --git a/tensorflow/stream_executor/platform/default/dso_loader.cc b/tensorflow/stream_executor/platform/default/dso_loader.cc index 0f0bce3253..668eeee3f3 100644 --- a/tensorflow/stream_executor/platform/default/dso_loader.cc +++ b/tensorflow/stream_executor/platform/default/dso_loader.cc @@ -28,7 +28,7 @@ limitations under the License. #include "tensorflow/stream_executor/lib/path.h" #include "tensorflow/stream_executor/lib/str_util.h" #include "tensorflow/stream_executor/lib/stringprintf.h" -#include "tensorflow/stream_executor/platform/dso_loader.h" +#include "tensorflow/stream_executor/platform/default/dso_loader.h" #include "tensorflow/stream_executor/platform/logging.h" #include "tensorflow/stream_executor/platform/port.h" -- GitLab From 45b2b27f6f30ec981300fb6987736f14caa45f29 Mon Sep 17 00:00:00 2001 From: Yuxin Wu Date: Fri, 4 Jan 2019 12:17:10 -0800 Subject: [PATCH 355/622] improve docs of depthwise conv --- tensorflow/python/ops/nn_impl.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tensorflow/python/ops/nn_impl.py b/tensorflow/python/ops/nn_impl.py index ec5ea44526..7abfde5149 100644 --- a/tensorflow/python/ops/nn_impl.py +++ b/tensorflow/python/ops/nn_impl.py @@ -467,7 +467,7 @@ def depthwise_conv2d(input, to `channel_multiplier` channels for each), then concatenates the results together. The output has `in_channels * channel_multiplier` channels. - In detail, + In detail, with the default NHWC format, output[b, i, j, k * channel_multiplier + q] = sum_{di, dj} filter[di, dj, k, q] * input[b, strides[1] * i + rate[0] * di, @@ -540,7 +540,7 @@ def depthwise_conv2d_v2(input, to `channel_multiplier` channels for each), then concatenates the results together. The output has `in_channels * channel_multiplier` channels. - In detail, + In detail, with the default NHWC format, output[b, i, j, k * channel_multiplier + q] = sum_{di, dj} filter[di, dj, k, q] * input[b, strides[1] * i + rate[0] * di, @@ -599,7 +599,7 @@ def separable_conv2d(input, between dimensions `[1, 2]` and `3`, not spatial separability between dimensions `1` and `2`. - In detail, + In detail, with the default NHWC format, output[b, i, j, k] = sum_{di, dj, q, r} input[b, strides[1] * i + di, strides[2] * j + dj, q] * @@ -699,7 +699,7 @@ def separable_conv2d_v2( between dimensions `[1, 2]` and `3`, not spatial separability between dimensions `1` and `2`. - In detail, + In detail, with the default NHWC format, output[b, i, j, k] = sum_{di, dj, q, r} input[b, strides[1] * i + di, strides[2] * j + dj, q] * -- GitLab From f1d4d18a622a3bb030ebc92b60763fe35a755bd5 Mon Sep 17 00:00:00 2001 From: Pavithra Vijay Date: Fri, 4 Jan 2019 12:17:54 -0800 Subject: [PATCH 356/622] Add logcosh v2 loss and metric. PiperOrigin-RevId: 227894520 --- tensorflow/python/keras/losses.py | 27 +++++++++ tensorflow/python/keras/losses_test.py | 81 +++++++++++++++++++++++++ tensorflow/python/keras/metrics.py | 31 ++++++++++ tensorflow/python/keras/metrics_test.py | 43 +++++++++++++ 4 files changed, 182 insertions(+) diff --git a/tensorflow/python/keras/losses.py b/tensorflow/python/keras/losses.py index 6a428b28a9..1ff564054b 100644 --- a/tensorflow/python/keras/losses.py +++ b/tensorflow/python/keras/losses.py @@ -540,6 +540,33 @@ class LogLoss(Loss): return logloss(y_true, y_pred, epsilon=self.epsilon) +class Logcosh(Loss): + """Computes the logarithm of the hyperbolic cosine of the prediction error. + + logcosh = log((exp(x) + exp(-x))/2) where x is the error `y_pred` - `y_true`. + + Usage: + + ```python + l = tf.losses.Logcosh() + loss = l([0., 1., 1.], [1., 0., 1.]) + print('Loss: ', loss.numpy()) # Loss: 0.289 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile('sgd', loss=tf.losses.Logcosh()) + ``` + """ + + def call(self, y_true, y_pred): + y_pred = ops.convert_to_tensor(y_pred) + y_true = math_ops.cast(y_true, y_pred.dtype) + return logcosh(y_true, y_pred) + + @keras_export('keras.metrics.mean_squared_error', 'keras.metrics.mse', 'keras.metrics.MSE', diff --git a/tensorflow/python/keras/losses_test.py b/tensorflow/python/keras/losses_test.py index 76fadc4a24..aa00ba5a91 100644 --- a/tensorflow/python/keras/losses_test.py +++ b/tensorflow/python/keras/losses_test.py @@ -1094,5 +1094,86 @@ class LogLossTest(test.TestCase): self.assertAlmostEqual(self.evaluate(loss), 0., 3) +@test_util.run_all_in_graph_and_eager_modes +class LogcoshTest(test.TestCase): + + def setup(self): + y_pred = np.asarray([1, 9, 2, -5, -2, 6]).reshape((2, 3)) + y_true = np.asarray([4, 8, 12, 8, 1, 3]).reshape((2, 3)) + + self.batch_size = 6 + error = y_pred - y_true + self.expected_losses = np.log((np.exp(error) + np.exp(-error)) / 2) + + self.y_pred = constant_op.constant(y_pred, dtype=dtypes.float32) + self.y_true = constant_op.constant(y_true) + + def test_config(self): + logcosh_obj = keras.losses.Logcosh( + reduction=losses_impl.ReductionV2.SUM, name='logcosh_loss') + self.assertEqual(logcosh_obj.name, 'logcosh_loss') + self.assertEqual(logcosh_obj.reduction, losses_impl.ReductionV2.SUM) + + def test_unweighted(self): + self.setup() + logcosh_obj = keras.losses.Logcosh() + + loss = logcosh_obj(self.y_true, self.y_pred) + expected_loss = np.sum(self.expected_losses) / self.batch_size + self.assertAlmostEqual(self.evaluate(loss), expected_loss, 3) + + def test_scalar_weighted(self): + self.setup() + logcosh_obj = keras.losses.Logcosh() + sample_weight = 2.3 + + loss = logcosh_obj(self.y_true, self.y_pred, sample_weight=sample_weight) + expected_loss = sample_weight * np.sum( + self.expected_losses) / self.batch_size + self.assertAlmostEqual(self.evaluate(loss), expected_loss, 3) + + # Verify we get the same output when the same input is given + loss_2 = logcosh_obj(self.y_true, self.y_pred, sample_weight=sample_weight) + self.assertAlmostEqual(self.evaluate(loss), self.evaluate(loss_2), 3) + + def test_sample_weighted(self): + self.setup() + logcosh_obj = keras.losses.Logcosh() + + sample_weight = constant_op.constant([1.2, 3.4], shape=(2, 1)) + loss = logcosh_obj(self.y_true, self.y_pred, sample_weight=sample_weight) + + expected_loss = np.multiply( + self.expected_losses, + np.asarray([1.2, 1.2, 1.2, 3.4, 3.4, 3.4]).reshape((2, 3))) + expected_loss = np.sum(expected_loss) / self.batch_size + self.assertAlmostEqual(self.evaluate(loss), expected_loss, 3) + + def test_timestep_weighted(self): + self.setup() + logcosh_obj = keras.losses.Logcosh() + y_true = np.asarray([1, 9, 2, -5, -2, 6]).reshape(2, 3, 1) + y_pred = np.asarray([4, 8, 12, 8, 1, 3]).reshape(2, 3, 1) + error = y_pred - y_true + expected_losses = np.log((np.exp(error) + np.exp(-error)) / 2) + sample_weight = np.array([3, 6, 5, 0, 4, 2]).reshape((2, 3, 1)) + + y_pred = constant_op.constant(y_pred, dtype=dtypes.float32) + y_true = constant_op.constant(y_true) + loss = logcosh_obj( + y_true, + y_pred, + sample_weight=constant_op.constant(sample_weight, shape=(2, 3))) + expected_loss = np.sum(expected_losses * sample_weight) / self.batch_size + self.assertAlmostEqual(self.evaluate(loss), expected_loss, 3) + + def test_zero_weighted(self): + self.setup() + logcosh_obj = keras.losses.Logcosh() + sample_weight = 0 + loss = logcosh_obj(self.y_true, self.y_pred, sample_weight=sample_weight) + self.assertAlmostEqual(self.evaluate(loss), 0., 3) + + if __name__ == '__main__': test.main() diff --git a/tensorflow/python/keras/metrics.py b/tensorflow/python/keras/metrics.py index 7f13cc46e3..2de0bc474c 100644 --- a/tensorflow/python/keras/metrics.py +++ b/tensorflow/python/keras/metrics.py @@ -1654,6 +1654,37 @@ class RootMeanSquaredError(Mean): return math_ops.sqrt(math_ops.div_no_nan(self.total, self.count)) +class Logcosh(MeanMetricWrapper): + """Computes the logarithm of the hyperbolic cosine of the prediction error. + + logcosh = log((exp(x) + exp(-x))/2) where x is the error `y_pred` - `y_true`. + + Usage: + + ```python + m = tf.keras.metrics.Logcosh() + m.update_state([0., 1., 1.], [1., 0., 1.]) + print('Final result: ', m.result().numpy()) # Final result: 0.289 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile('sgd', metrics=[tf.keras.metrics.Logcosh()]) + ``` + """ + + def __init__(self, name='logcosh', dtype=None): + super(Logcosh, self).__init__(logcosh, name, dtype=dtype) + + @classmethod + def from_config(cls, config): + if 'fn' in config: + config.pop('fn') + return super(Logcosh, cls).from_config(config) + + def accuracy(y_true, y_pred): y_pred.get_shape().assert_is_compatible_with(y_true.get_shape()) if y_true.dtype != y_pred.dtype: diff --git a/tensorflow/python/keras/metrics_test.py b/tensorflow/python/keras/metrics_test.py index fb635e7055..a9d88789ed 100644 --- a/tensorflow/python/keras/metrics_test.py +++ b/tensorflow/python/keras/metrics_test.py @@ -1467,6 +1467,49 @@ class SparseTopKCategoricalAccuracyTest(test.TestCase): self.assertEqual(0.5, self.evaluate(result)) # only 1 sample matches. +@test_util.run_all_in_graph_and_eager_modes +class LogcoshTest(test.TestCase): + + def setup(self): + y_pred = np.asarray([1, 9, 2, -5, -2, 6]).reshape((2, 3)) + y_true = np.asarray([4, 8, 12, 8, 1, 3]).reshape((2, 3)) + + self.batch_size = 6 + error = y_pred - y_true + self.expected_results = np.log((np.exp(error) + np.exp(-error)) / 2) + + self.y_pred = constant_op.constant(y_pred, dtype=dtypes.float32) + self.y_true = constant_op.constant(y_true) + + def test_config(self): + logcosh_obj = metrics.Logcosh(name='logcosh', dtype=dtypes.int32) + self.assertEqual(logcosh_obj.name, 'logcosh') + self.assertEqual(logcosh_obj._dtype, dtypes.int32) + + def test_unweighted(self): + self.setup() + logcosh_obj = metrics.Logcosh() + self.evaluate(variables.variables_initializer(logcosh_obj.variables)) + + update_op = logcosh_obj.update_state(self.y_true, self.y_pred) + self.evaluate(update_op) + result = logcosh_obj.result() + expected_result = np.sum(self.expected_results) / self.batch_size + self.assertAllClose(result, expected_result, atol=1e-3) + + def test_weighted(self): + self.setup() + logcosh_obj = metrics.Logcosh() + self.evaluate(variables.variables_initializer(logcosh_obj.variables)) + sample_weight = constant_op.constant([1.2, 3.4], shape=(2, 1)) + result = logcosh_obj(self.y_true, self.y_pred, sample_weight=sample_weight) + + sample_weight = np.asarray([1.2, 1.2, 1.2, 3.4, 3.4, 3.4]).reshape((2, 3)) + expected_result = np.multiply(self.expected_results, sample_weight) + expected_result = np.sum(expected_result) / np.sum(sample_weight) + self.assertAllClose(self.evaluate(result), expected_result, atol=1e-3) + + def _get_model(compile_metrics): model_layers = [ layers.Dense(3, activation='relu', kernel_initializer='ones'), -- GitLab From f9bd1568aa6f0e0b8c40c10d73caeca80c3c19ab Mon Sep 17 00:00:00 2001 From: Justin Lebar Date: Fri, 4 Jan 2019 12:18:26 -0800 Subject: [PATCH 357/622] [XLA] Use absl::flat_hash_{map,set}::contains() instead of count(). contains() is a much better description of what we're trying to do! Also change a few std containers over to absl containers so we can advantage of this. PiperOrigin-RevId: 227894609 --- tensorflow/compiler/xla/BUILD | 1 + tensorflow/compiler/xla/client/xla_builder.cc | 8 +++-- tensorflow/compiler/xla/client/xla_builder.h | 3 +- tensorflow/compiler/xla/reference_util.cc | 25 ++++++++------ tensorflow/compiler/xla/service/BUILD | 9 +++++ .../xla/service/algebraic_simplifier.cc | 5 +-- tensorflow/compiler/xla/service/backend.cc | 8 ++--- tensorflow/compiler/xla/service/backend.h | 4 ++- .../compiler/xla/service/buffer_assignment.cc | 34 +++++++------------ .../compiler/xla/service/buffer_assignment.h | 7 ++-- .../xla/service/buffer_assignment_test.cc | 7 ++-- .../compiler/xla/service/channel_tracker.cc | 4 +-- .../compiler/xla/service/channel_tracker.h | 4 ++- .../xla/service/cpu/cpu_layout_assignment.cc | 3 +- .../compiler/xla/service/cpu/ir_emitter.cc | 12 +++---- .../compiler/xla/service/cpu/ir_emitter.h | 2 +- tensorflow/compiler/xla/service/gpu/BUILD | 7 +++- .../xla/service/gpu/buffer_allocations.cc | 11 +++--- .../xla/service/gpu/buffer_allocations.h | 4 ++- .../xla/service/gpu/hlo_to_ir_bindings.cc | 7 ++-- .../xla/service/gpu/hlo_to_ir_bindings.h | 6 ++-- .../xla/service/gpu/multi_output_fusion.cc | 2 +- .../xla/service/gpu/stream_assignment.cc | 9 ++--- .../xla/service/gpu/thunk_schedule.cc | 16 +++++---- .../compiler/xla/service/gpu/thunk_schedule.h | 12 ++++--- .../compiler/xla/service/heap_simulator.cc | 22 ++++++------ .../compiler/xla/service/hlo_computation.cc | 15 ++++---- .../xla/service/hlo_computation_test.cc | 5 +-- .../xla/service/hlo_dataflow_analysis.cc | 2 +- tensorflow/compiler/xla/service/hlo_dce.cc | 5 +-- .../compiler/xla/service/hlo_graph_dumper.cc | 16 ++++----- .../xla/service/hlo_instruction_test.cc | 33 +++++++++--------- .../xla/service/hlo_liveness_analysis.cc | 5 +-- tensorflow/compiler/xla/service/hlo_module.cc | 29 +++++++--------- .../xla/service/hlo_module_group_metadata.cc | 4 +-- .../xla/service/hlo_module_group_metadata.h | 2 +- .../compiler/xla/service/hlo_ordering.cc | 2 +- .../compiler/xla/service/hlo_pass_pipeline.cc | 2 +- .../compiler/xla/service/hlo_schedule.cc | 10 +++--- .../compiler/xla/service/hlo_schedule.h | 2 +- .../compiler/xla/service/hlo_sharding.cc | 5 +-- .../xla/service/hlo_sharding_metadata.cc | 4 +-- .../xla/service/indexed_array_analysis.cc | 6 ++-- .../compiler/xla/service/layout_assignment.cc | 2 +- .../compiler/xla/service/layout_assignment.h | 4 +-- tensorflow/compiler/xla/service/llvm_ir/BUILD | 1 + .../xla/service/llvm_ir/alias_analysis.h | 7 ++-- .../xla/service/llvm_ir/fused_ir_emitter.cc | 2 +- .../xla/service/llvm_ir/fused_ir_emitter.h | 6 ++-- .../xla/service/multi_output_fusion.cc | 2 +- .../xla/service/tuple_points_to_analysis.cc | 8 ++--- .../while_loop_invariant_code_motion.cc | 4 +-- .../xla/service/while_loop_simplifier.cc | 2 +- 53 files changed, 225 insertions(+), 192 deletions(-) diff --git a/tensorflow/compiler/xla/BUILD b/tensorflow/compiler/xla/BUILD index 722d137668..8a0e2db2b5 100644 --- a/tensorflow/compiler/xla/BUILD +++ b/tensorflow/compiler/xla/BUILD @@ -741,6 +741,7 @@ cc_library( "//tensorflow/compiler/xla/service:hlo_evaluator", "//tensorflow/compiler/xla/service:shape_inference", "//tensorflow/core:lib", + "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/memory", "@com_google_absl//absl/types:span", ], diff --git a/tensorflow/compiler/xla/client/xla_builder.cc b/tensorflow/compiler/xla/client/xla_builder.cc index 4b752def1a..433033cae6 100644 --- a/tensorflow/compiler/xla/client/xla_builder.cc +++ b/tensorflow/compiler/xla/client/xla_builder.cc @@ -22,6 +22,8 @@ limitations under the License. #include #include "absl/algorithm/container.h" +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" #include "absl/memory/memory.h" #include "absl/strings/match.h" #include "absl/strings/str_cat.h" @@ -193,9 +195,9 @@ StatusOr XlaBuilder::GetProgramShape(XlaOp root) const { } void XlaBuilder::IsConstantVisitor(const int64 op_handle, - std::set* visited, + absl::flat_hash_set* visited, bool* is_constant) const { - if (visited->count(op_handle) != 0 || !*is_constant) { + if (visited->contains(op_handle) || !*is_constant) { return; } @@ -2415,7 +2417,7 @@ StatusOr XlaBuilder::IsConstant(const XlaOp& operand) const { TF_RETURN_IF_ERROR(LookUpInstruction(operand).status()); bool is_constant = true; - std::set visited; + absl::flat_hash_set visited; IsConstantVisitor(operand.handle(), &visited, &is_constant); return is_constant; } diff --git a/tensorflow/compiler/xla/client/xla_builder.h b/tensorflow/compiler/xla/client/xla_builder.h index 68ddf2cdd8..ebef8e0879 100644 --- a/tensorflow/compiler/xla/client/xla_builder.h +++ b/tensorflow/compiler/xla/client/xla_builder.h @@ -727,7 +727,8 @@ class XlaBuilder { // operation such as `RngNormal` or `Infeed`. The visitor walks the // computation starting at a given operation and sets is_constant to false iff // a parameter or stateful operation is encountered. - void IsConstantVisitor(const int64 op_handle, std::set* visited, + void IsConstantVisitor(const int64 op_handle, + absl::flat_hash_set* visited, bool* is_constant) const; // Checks bounds for convolution parameters. diff --git a/tensorflow/compiler/xla/reference_util.cc b/tensorflow/compiler/xla/reference_util.cc index 033f6c9b0f..08b78ee244 100644 --- a/tensorflow/compiler/xla/reference_util.cc +++ b/tensorflow/compiler/xla/reference_util.cc @@ -18,6 +18,7 @@ limitations under the License. #include #include +#include "absl/container/flat_hash_set.h" #include "absl/memory/memory.h" #include "tensorflow/compiler/xla/client/xla_builder.h" #include "tensorflow/compiler/xla/literal_util.h" @@ -605,24 +606,26 @@ ReferenceUtil::ReduceToRowArray2D( const std::function& reduce_function) { std::vector result; CHECK_EQ(dims.size(), 3); - const std::set dim_set(dims.begin(), dims.end()); + const absl::flat_hash_set dim_set(dims.begin(), dims.end()); CHECK_EQ(dim_set.size(), 3); - for (int64 a0 = 0; a0 == 0 || (!dim_set.count(0) && a0 < array.n1()); ++a0) { - for (int64 a1 = 0; a1 == 0 || (!dim_set.count(1) && a1 < array.n2()); + for (int64 a0 = 0; a0 == 0 || (!dim_set.contains(0) && a0 < array.n1()); + ++a0) { + for (int64 a1 = 0; a1 == 0 || (!dim_set.contains(1) && a1 < array.n2()); ++a1) { - for (int64 a2 = 0; a2 == 0 || (!dim_set.count(2) && a2 < array.n3()); + for (int64 a2 = 0; a2 == 0 || (!dim_set.contains(2) && a2 < array.n3()); ++a2) { - for (int64 a3 = 0; a3 == 0 || (!dim_set.count(3) && a3 < array.n4()); + for (int64 a3 = 0; a3 == 0 || (!dim_set.contains(3) && a3 < array.n4()); ++a3) { float accumulator = init; - for (int64 i0 = 0; i0 == 0 || (dim_set.count(0) && i0 < array.n1()); - ++i0) { - for (int64 i1 = 0; i1 == 0 || (dim_set.count(1) && i1 < array.n2()); - ++i1) { + for (int64 i0 = 0; + i0 == 0 || (dim_set.contains(0) && i0 < array.n1()); ++i0) { + for (int64 i1 = 0; + i1 == 0 || (dim_set.contains(1) && i1 < array.n2()); ++i1) { for (int64 i2 = 0; - i2 == 0 || (dim_set.count(2) && i2 < array.n3()); ++i2) { + i2 == 0 || (dim_set.contains(2) && i2 < array.n3()); ++i2) { for (int64 i3 = 0; - i3 == 0 || (dim_set.count(3) && i3 < array.n4()); ++i3) { + i3 == 0 || (dim_set.contains(3) && i3 < array.n4()); + ++i3) { // Handle zero-sized arrays. if (array.n1() > 0 && array.n2() > 0 && array.n3() > 0 && array.n4() > 0) { diff --git a/tensorflow/compiler/xla/service/BUILD b/tensorflow/compiler/xla/service/BUILD index 755c477a12..85a21a8518 100644 --- a/tensorflow/compiler/xla/service/BUILD +++ b/tensorflow/compiler/xla/service/BUILD @@ -515,6 +515,7 @@ tf_cc_test( "//tensorflow/compiler/xla:window_util", "//tensorflow/compiler/xla/tests:hlo_test_base", "//tensorflow/compiler/xla/tests:xla_internal_test_main", + "@com_google_absl//absl/container:flat_hash_map", ], ) @@ -677,6 +678,7 @@ cc_library( "//tensorflow/core:lib", "//tensorflow/core:stream_executor_no_cuda", "//third_party/eigen3", + "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/memory", "@com_google_absl//absl/strings", "@com_google_absl//absl/types:span", @@ -1002,6 +1004,7 @@ cc_library( "//tensorflow/compiler/xla:xla_data_proto", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", + "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/memory", "@com_google_absl//absl/strings", "@com_google_absl//absl/types:span", @@ -1136,6 +1139,7 @@ tf_cc_test( "//tensorflow/compiler/xla/tests:xla_internal_test_main", "//tensorflow/core:lib", "//tensorflow/core:test", + "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/memory", ], ) @@ -1580,6 +1584,7 @@ cc_library( "//tensorflow/core:lib", "@com_google_absl//absl/algorithm:container", "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/container:inlined_vector", "@com_google_absl//absl/memory", "@com_google_absl//absl/strings", @@ -2116,6 +2121,7 @@ tf_cc_test( "//tensorflow/compiler/xla:test_helpers", "//tensorflow/compiler/xla/tests:hlo_test_base", "//tensorflow/compiler/xla/tests:xla_internal_test_main", + "@com_google_absl//absl/container:flat_hash_set", ], ) @@ -2288,6 +2294,7 @@ cc_library( "//tensorflow/compiler/xla:types", "//tensorflow/compiler/xla:util", "//tensorflow/core:lib", + "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/memory", "@com_google_absl//absl/strings", ], @@ -2548,6 +2555,7 @@ cc_library( "//tensorflow/compiler/xla:types", "//tensorflow/compiler/xla:util", "//tensorflow/core:lib", + "@com_google_absl//absl/container:flat_hash_set", ], ) @@ -3188,6 +3196,7 @@ cc_library( "//tensorflow/core:lib", "//tensorflow/core:lib_internal", "//tensorflow/core:regexp_internal", + "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", "@com_google_absl//absl/types:optional", diff --git a/tensorflow/compiler/xla/service/algebraic_simplifier.cc b/tensorflow/compiler/xla/service/algebraic_simplifier.cc index c573b60161..3d269dc04f 100644 --- a/tensorflow/compiler/xla/service/algebraic_simplifier.cc +++ b/tensorflow/compiler/xla/service/algebraic_simplifier.cc @@ -27,6 +27,7 @@ limitations under the License. #include "absl/algorithm/container.h" #include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" #include "absl/container/inlined_vector.h" #include "absl/memory/memory.h" #include "absl/strings/str_cat.h" @@ -2539,7 +2540,7 @@ Status AlgebraicSimplifierVisitor::HandleReduce(HloInstruction* reduce) { } if (can_move_reshape_into_reduce) { changed_ = true; - std::unordered_set dimensions_not_to_reduce; + absl::flat_hash_set dimensions_not_to_reduce; for (auto dim_pair : unmodified_dims) { if (arg_dim_in_output[dim_pair.second]) { dimensions_not_to_reduce.insert(dim_pair.first); @@ -2547,7 +2548,7 @@ Status AlgebraicSimplifierVisitor::HandleReduce(HloInstruction* reduce) { } std::vector new_reduce_dimensions; for (int64 i = 0; i < arg->operand(0)->shape().rank(); ++i) { - if (dimensions_not_to_reduce.count(i) == 0) { + if (!dimensions_not_to_reduce.contains(i)) { new_reduce_dimensions.push_back(i); } } diff --git a/tensorflow/compiler/xla/service/backend.cc b/tensorflow/compiler/xla/service/backend.cc index 2cf24a9dd5..215e8ced4b 100644 --- a/tensorflow/compiler/xla/service/backend.cc +++ b/tensorflow/compiler/xla/service/backend.cc @@ -115,12 +115,10 @@ StatusOr Backend::BorrowStream(int device_ordinal) { StatusOr Backend::BorrowStream(se::StreamExecutor* executor) { tensorflow::mutex_lock l(mu_); - if (0 == stream_pools_.count(executor)) { - stream_pools_.emplace(std::piecewise_construct, - std::forward_as_tuple(executor), - std::forward_as_tuple()); + if (!stream_pools_.contains(executor)) { + stream_pools_.emplace(executor, absl::make_unique()); } - return stream_pools_.at(executor).BorrowStream(executor); + return stream_pools_.at(executor)->BorrowStream(executor); } Backend::Backend(se::Platform* platform, Compiler* compiler, diff --git a/tensorflow/compiler/xla/service/backend.h b/tensorflow/compiler/xla/service/backend.h index 7ca993fb26..c35f033dc0 100644 --- a/tensorflow/compiler/xla/service/backend.h +++ b/tensorflow/compiler/xla/service/backend.h @@ -22,6 +22,7 @@ limitations under the License. #include #include +#include "absl/container/flat_hash_map.h" #include "absl/strings/str_cat.h" #include "absl/types/span.h" #include "tensorflow/compiler/xla/service/compiler.h" @@ -175,7 +176,8 @@ class Backend { tensorflow::mutex mu_; // Mapping from stream executor to stream pools, used by `BorrowStream` above. - std::map stream_pools_ GUARDED_BY(mu_); + absl::flat_hash_map> + stream_pools_ GUARDED_BY(mu_); // The default memory allocator to use. std::unique_ptr memory_allocator_; diff --git a/tensorflow/compiler/xla/service/buffer_assignment.cc b/tensorflow/compiler/xla/service/buffer_assignment.cc index 6f4c1104f3..c997d594c4 100644 --- a/tensorflow/compiler/xla/service/buffer_assignment.cc +++ b/tensorflow/compiler/xla/service/buffer_assignment.cc @@ -138,8 +138,8 @@ Status GatherComputationsByAllocationType( worklist.pop_front(); const HloComputation* computation = worklist_front.first; bool is_thread_local = worklist_front.second; - bool in_thread_local_set = thread_local_set.count(computation) > 0; - bool in_global_set = global_set.count(computation) > 0; + bool in_thread_local_set = thread_local_set.contains(computation); + bool in_global_set = global_set.contains(computation); // If the computation has already been added to the respective set, then // nothing to do. @@ -207,9 +207,9 @@ Status GatherComputationsByAllocationType( // Add the computations to the vectors in post order. for (auto* computation : module->MakeComputationPostOrder()) { - if (thread_local_set.count(computation) > 0) { + if (thread_local_set.contains(computation)) { thread_local_computations->push_back(computation); - } else if (global_set.count(computation) > 0) { + } else if (global_set.contains(computation)) { global_computations->push_back(computation); } // If the computation is not reachable from the entry computation, then it @@ -219,13 +219,6 @@ Status GatherComputationsByAllocationType( return Status::OK(); } -size_t BufferAllocation::Slice::Hasher::operator()(Slice s) const { - uint64 h = std::hash()(s.index()); - h = tensorflow::Hash64Combine(h, std::hash()(s.offset())); - h = tensorflow::Hash64Combine(h, std::hash()(s.size())); - return h; -} - string BufferAllocation::Slice::ToString() const { return absl::StrCat("{index:", index(), ", offset:", offset_, ", size:", size_, "}"); @@ -240,7 +233,7 @@ BufferAllocation::Slice BufferAllocation::GetSlice( void BufferAllocation::AddAssignment(const LogicalBuffer& buffer, int64 offset, int64 size) { VLOG(4) << "Trying to add " << buffer << " to allocation #" << index(); - CHECK(assigned_buffers_.count(&buffer) == 0) + CHECK(!assigned_buffers_.contains(&buffer)) << "LogicalBuffer " << buffer << " already assigned to allocation " << index_; CHECK_LE(offset, size_) << "LogicalBuffer " << buffer @@ -346,7 +339,7 @@ const PointsToSet& BufferAssignment::GetPointsToSet( bool BufferAssignment::HasAllocation(const LogicalBuffer& buffer) const { TF_CHECK_OK(points_to_analysis().VerifyBuffer(buffer)); - return allocation_index_for_buffer_.count(&buffer) > 0; + return allocation_index_for_buffer_.contains(&buffer); } const BufferAllocation& BufferAssignment::GetAssignedAllocation( @@ -401,7 +394,7 @@ bool BufferAssignment::HasAllocationAt(const HloInstruction* instruction, const ShapeIndex& index) const { for (const LogicalBuffer* buffer : GetPointsToSet(instruction).element(index)) { - if (allocation_index_for_buffer_.count(buffer) > 0) { + if (allocation_index_for_buffer_.contains(buffer)) { return true; } } @@ -459,8 +452,7 @@ bool BufferAssignment::SharesSliceAtIndex( bool BufferAssignment::HaveDisjointSlices(const HloInstruction* hlo_a, const HloInstruction* hlo_b) const { - using SliceSet = - flat_hash_set; + using SliceSet = flat_hash_set; // Gets the slices all of instr's subshapes. If any subshape doesn't have an // assigned slice, returns the empty set. auto collect_slices = [&](const HloInstruction* instr) -> SliceSet { @@ -519,7 +511,7 @@ BufferAllocation* BufferAssignment::NewAllocation(const LogicalBuffer& buffer, void BufferAssignment::AddAssignment(BufferAllocation* allocation, const LogicalBuffer& buffer, int64 offset, int64 size) { - CHECK_EQ(0, allocation_index_for_buffer_.count(&buffer)) + CHECK(!allocation_index_for_buffer_.contains(&buffer)) << "LogicalBuffer " << buffer << " already has an allocation."; CHECK(allocation->is_reusable() || allocation->assigned_buffers().empty()) << "Non-reusable allocation already assigned a buffer: " @@ -988,7 +980,7 @@ Status BufferAssigner::AssignBuffersForComputation( std::vector allocation_indices; for (const LogicalBuffer* buffer : sorted_buffers) { VLOG(3) << "Assigning allocation to: " << *buffer; - if (colocated_buffers.count(buffer) > 0) { + if (colocated_buffers.contains(buffer)) { // Colocated buffers are currently assigned in an earlier pass. VLOG(3) << "Skipping colocated buffer: " << *buffer; continue; @@ -1056,7 +1048,7 @@ Status BufferAssigner::AssignBuffersForComputation( assignment->GetAllSlices(operand, /*index=*/{})) { BufferAllocation* allocation = assignment->GetMutableAllocation(operand_slice.index()); - if (colocated_allocations.count(allocation->index()) == 0) { + if (!colocated_allocations.contains(allocation->index())) { // TODO(b/32491382) Colocated buffers are currently assigned in an // earlier pass, and so can break the "increasing allocation size" // invariant in this function (causing this CHECK to fail). However, @@ -1087,7 +1079,7 @@ Status BufferAssigner::AssignBuffersForComputation( // Instructions are iterated in increasing buffer size, so any // previously create allocation must be large enough to hold this // instruction's output (with the exception of colocated buffers). - if (colocated_allocations.count(allocation->index()) == 0) { + if (!colocated_allocations.contains(allocation->index())) { // TODO(b/32491382) Colocated buffers are currently assigned in an // earlier pass, and so can break the "increasing allocation size" // invariant in this function (causing this CHECK to fail). However, @@ -1376,7 +1368,7 @@ void BufferAssigner::AddSetToColocatedBufferSets( std::vector overlap_set_indices; for (size_t index = 0; index < colocated_buffer_sets->size(); ++index) { for (const LogicalBuffer* buffer : colocated_set) { - if ((*colocated_buffer_sets)[index].count(buffer) > 0) { + if ((*colocated_buffer_sets)[index].contains(buffer)) { VLOG(5) << "Found overlap with existing set on buffer " << buffer->ToString() << "\n" << ColocatedBufferSetsToString((*colocated_buffer_sets)[index], diff --git a/tensorflow/compiler/xla/service/buffer_assignment.h b/tensorflow/compiler/xla/service/buffer_assignment.h index 0a9fdede80..4baab9b6ad 100644 --- a/tensorflow/compiler/xla/service/buffer_assignment.h +++ b/tensorflow/compiler/xla/service/buffer_assignment.h @@ -186,9 +186,10 @@ class BufferAllocation { end > other.offset_; } - struct Hasher { - size_t operator()(Slice s) const; - }; + template + friend H AbslHashValue(H h, const Slice& s) { + return H::combine(std::move(h), s.index(), s.offset(), s.size()); + } string ToString() const; diff --git a/tensorflow/compiler/xla/service/buffer_assignment_test.cc b/tensorflow/compiler/xla/service/buffer_assignment_test.cc index 8f482e6ba8..370fad7ffb 100644 --- a/tensorflow/compiler/xla/service/buffer_assignment_test.cc +++ b/tensorflow/compiler/xla/service/buffer_assignment_test.cc @@ -21,6 +21,7 @@ limitations under the License. #include #include +#include "absl/container/flat_hash_set.h" #include "absl/memory/memory.h" #include "tensorflow/compiler/xla/literal.h" #include "tensorflow/compiler/xla/service/buffer_value.h" @@ -309,7 +310,7 @@ class BufferAssignmentTest : public HloTestBase { static bool BuffersDistinct(const std::vector& a, const std::vector& b, const BufferAssignment& assignment) { - std::set a_slices; + absl::flat_hash_set a_slices; for (const HloInstruction* instruction : a) { if (assignment.HasTopLevelAllocation(instruction)) { a_slices.insert( @@ -319,8 +320,8 @@ static bool BuffersDistinct(const std::vector& a, for (const HloInstruction* instruction : b) { if (assignment.HasTopLevelAllocation(instruction)) { - if (a_slices.count(assignment.GetUniqueTopLevelSlice(instruction) - .ConsumeValueOrDie())) { + if (a_slices.contains(assignment.GetUniqueTopLevelSlice(instruction) + .ConsumeValueOrDie())) { return false; } } diff --git a/tensorflow/compiler/xla/service/channel_tracker.cc b/tensorflow/compiler/xla/service/channel_tracker.cc index 3c2d1ae6d8..b517495f2e 100644 --- a/tensorflow/compiler/xla/service/channel_tracker.cc +++ b/tensorflow/compiler/xla/service/channel_tracker.cc @@ -72,7 +72,7 @@ ChannelHandle ChannelTracker::AllocateHandle(ChannelHandle::ChannelType type) { } Status ChannelTracker::RegisterSendInternal(const ChannelHandle& handle) { - if (opaque_to_channel_.count(handle.handle()) == 0) { + if (!opaque_to_channel_.contains(handle.handle())) { return NotFound("channel handle not found: %d", handle.handle()); } Channel& channel = opaque_to_channel_[handle.handle()]; @@ -94,7 +94,7 @@ Status ChannelTracker::RegisterSendInternal(const ChannelHandle& handle) { } Status ChannelTracker::RegisterRecvInternal(const ChannelHandle& handle) { - if (opaque_to_channel_.count(handle.handle()) == 0) { + if (!opaque_to_channel_.contains(handle.handle())) { return NotFound("channel handle not found: %d", handle.handle()); } Channel& channel = opaque_to_channel_[handle.handle()]; diff --git a/tensorflow/compiler/xla/service/channel_tracker.h b/tensorflow/compiler/xla/service/channel_tracker.h index 52037bf9b5..89e17eba36 100644 --- a/tensorflow/compiler/xla/service/channel_tracker.h +++ b/tensorflow/compiler/xla/service/channel_tracker.h @@ -18,6 +18,7 @@ limitations under the License. #include +#include "absl/container/flat_hash_map.h" #include "absl/types/span.h" #include "tensorflow/compiler/xla/service/hlo_module.h" #include "tensorflow/compiler/xla/status.h" @@ -83,7 +84,8 @@ class ChannelTracker { // Mapping from ChannelHandle value to the corresponding registered // Channel object. - std::map opaque_to_channel_ GUARDED_BY(channel_mutex_); + absl::flat_hash_map opaque_to_channel_ + GUARDED_BY(channel_mutex_); TF_DISALLOW_COPY_AND_ASSIGN(ChannelTracker); }; diff --git a/tensorflow/compiler/xla/service/cpu/cpu_layout_assignment.cc b/tensorflow/compiler/xla/service/cpu/cpu_layout_assignment.cc index 1cb154e9ca..ff11409482 100644 --- a/tensorflow/compiler/xla/service/cpu/cpu_layout_assignment.cc +++ b/tensorflow/compiler/xla/service/cpu/cpu_layout_assignment.cc @@ -46,8 +46,7 @@ static bool ShouldMakeAllUsersColMajor(const HloInstruction* instruction) { for (auto* user : instruction->users()) { optional operand_idx = ProfitableToMakeDotOperandColumnMajor(*user); if (!operand_idx || user->operand(*operand_idx) != instruction || - std::count(user->operands().begin(), user->operands().end(), - instruction) != 1) { + absl::c_count(user->operands(), instruction) != 1) { return false; } } diff --git a/tensorflow/compiler/xla/service/cpu/ir_emitter.cc b/tensorflow/compiler/xla/service/cpu/ir_emitter.cc index 169d628923..e4760aedf1 100644 --- a/tensorflow/compiler/xla/service/cpu/ir_emitter.cc +++ b/tensorflow/compiler/xla/service/cpu/ir_emitter.cc @@ -24,11 +24,9 @@ limitations under the License. #include #include +// IWYU pragma: no_include "llvm/IR/Intrinsics.gen.inc" #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" -#include "tensorflow/core/lib/math/math_util.h" -#include "tensorflow/core/platform/logging.h" -// IWYU pragma: no_include "llvm/IR/Intrinsics.gen.inc" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/types/span.h" @@ -70,6 +68,8 @@ limitations under the License. #include "tensorflow/compiler/xla/window_util.h" #include "tensorflow/core/lib/core/bits.h" #include "tensorflow/core/lib/core/errors.h" +#include "tensorflow/core/lib/math/math_util.h" +#include "tensorflow/core/platform/logging.h" namespace xla { @@ -1399,7 +1399,7 @@ static bool ReductionPreservesLayout(const HloInstruction& reduce) { int64 delta = 0; for (int64 i = 0; i < operand_shape.dimensions_size(); i++) { - if (reduced_dims.count(i)) { + if (reduced_dims.contains(i)) { delta++; } else { InsertOrDie(&unreduced_dim_map, i, i - delta); @@ -1412,7 +1412,7 @@ static bool ReductionPreservesLayout(const HloInstruction& reduce) { for (int64 operand_dim_idx = 0; operand_dim_idx < operand_shape.dimensions_size(); operand_dim_idx++) { int64 operand_dim = operand_shape.layout().minor_to_major(operand_dim_idx); - if (!reduced_dims.count(operand_dim)) { + if (!reduced_dims.contains(operand_dim)) { if (FindOrDie(unreduced_dim_map, operand_dim) != result_shape.layout().minor_to_major(result_dim_idx++)) { return false; @@ -1990,7 +1990,7 @@ Status IrEmitter::HandleSlice(HloInstruction* slice) { // The memcpy will copy elements that are logically this shape (allowed to be // scalar). const Shape logical_element_shape = ShapeUtil::FilterDimensions( - [&inner_dims](int64 dim) -> bool { return inner_dims.count(dim); }, + [&inner_dims](int64 dim) { return inner_dims.contains(dim); }, operand->shape()); const int64 primitive_elements_per_logical_element = diff --git a/tensorflow/compiler/xla/service/cpu/ir_emitter.h b/tensorflow/compiler/xla/service/cpu/ir_emitter.h index db76de4bb2..a6fb11dcbf 100644 --- a/tensorflow/compiler/xla/service/cpu/ir_emitter.h +++ b/tensorflow/compiler/xla/service/cpu/ir_emitter.h @@ -448,7 +448,7 @@ class IrEmitter : public DfsHloVisitorWithDefault, computation_to_profile_idx_; // Maps HLOs to Values emitted for them. - std::unordered_map emitted_value_; + absl::flat_hash_map emitted_value_; llvm_ir::AliasAnalysis alias_analysis_; diff --git a/tensorflow/compiler/xla/service/gpu/BUILD b/tensorflow/compiler/xla/service/gpu/BUILD index 6c23f921f4..d10a11d096 100644 --- a/tensorflow/compiler/xla/service/gpu/BUILD +++ b/tensorflow/compiler/xla/service/gpu/BUILD @@ -94,8 +94,8 @@ cc_library( "//tensorflow/compiler/xla:util", "//tensorflow/compiler/xla/service:hlo", "//tensorflow/compiler/xla/service:hlo_reachability", - "//tensorflow/core:lib", "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/memory", ], ) @@ -135,6 +135,8 @@ cc_library( "//tensorflow/compiler/xla/service/llvm_ir:llvm_util", "//tensorflow/compiler/xla/service/llvm_ir:tuple_ops", "//tensorflow/core:lib", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/strings", "@com_google_absl//absl/types:span", "@llvm//:core", @@ -263,7 +265,9 @@ cc_library( "//tensorflow/compiler/xla/service:buffer_assignment", "//tensorflow/compiler/xla/service:device_memory_allocator", "//tensorflow/core:lib", + "//tensorflow/core:lib_internal", "//tensorflow/core:stream_executor_no_cuda", + "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/memory", "@com_google_absl//absl/types:span", ], @@ -362,6 +366,7 @@ cc_library( "//tensorflow/core/platform/default/build_config:stream_executor_cuda", # build_cleaner: keep "//tensorflow/stream_executor", "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/memory", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", diff --git a/tensorflow/compiler/xla/service/gpu/buffer_allocations.cc b/tensorflow/compiler/xla/service/gpu/buffer_allocations.cc index 528209abc7..eb59ee5a1d 100644 --- a/tensorflow/compiler/xla/service/gpu/buffer_allocations.cc +++ b/tensorflow/compiler/xla/service/gpu/buffer_allocations.cc @@ -24,6 +24,7 @@ limitations under the License. #include "tensorflow/compiler/xla/types.h" #include "tensorflow/compiler/xla/util.h" #include "tensorflow/core/lib/core/errors.h" +#include "tensorflow/core/lib/gtl/map_util.h" #include "tensorflow/core/lib/strings/numbers.h" #include "tensorflow/core/platform/logging.h" #include "tensorflow/core/platform/types.h" @@ -57,16 +58,16 @@ StatusOr> BufferAllocations::Builder::Build( // If buffer #i's address is already registered (e.g. external arguments or // result buffers), use that registered buffer. - if (registered_buffers_.count(i)) { - se::DeviceMemoryBase address = FindOrDie(registered_buffers_, i); - if (reinterpret_cast(address.opaque()) % expected_alignment != + if (se::DeviceMemoryBase* address = + tensorflow::gtl::FindOrNull(registered_buffers_, i)) { + if (reinterpret_cast(address->opaque()) % expected_alignment != 0) { return InternalError( "Address of registered buffer %d must be a multiple of %x, but " "was %p", - i, kEntryParameterAlignBytes, address.opaque()); + i, kEntryParameterAlignBytes, address->opaque()); } - buffer_allocations->SetBuffer(i, FindOrDie(registered_buffers_, i)); + buffer_allocations->SetBuffer(i, *address); continue; } diff --git a/tensorflow/compiler/xla/service/gpu/buffer_allocations.h b/tensorflow/compiler/xla/service/gpu/buffer_allocations.h index 14186b8faa..9413ac2cff 100644 --- a/tensorflow/compiler/xla/service/gpu/buffer_allocations.h +++ b/tensorflow/compiler/xla/service/gpu/buffer_allocations.h @@ -20,6 +20,7 @@ limitations under the License. #include #include +#include "absl/container/flat_hash_map.h" #include "absl/types/span.h" #include "tensorflow/compiler/xla/service/buffer_assignment.h" #include "tensorflow/compiler/xla/service/device_memory_allocator.h" @@ -52,7 +53,8 @@ class BufferAllocations { DeviceMemoryAllocator* memory_allocator); private: - std::map registered_buffers_; + absl::flat_hash_map + registered_buffers_; }; ~BufferAllocations(); diff --git a/tensorflow/compiler/xla/service/gpu/hlo_to_ir_bindings.cc b/tensorflow/compiler/xla/service/gpu/hlo_to_ir_bindings.cc index 9634c92786..69aaaceca1 100644 --- a/tensorflow/compiler/xla/service/gpu/hlo_to_ir_bindings.cc +++ b/tensorflow/compiler/xla/service/gpu/hlo_to_ir_bindings.cc @@ -15,6 +15,7 @@ limitations under the License. #include "tensorflow/compiler/xla/service/gpu/hlo_to_ir_bindings.h" +#include "absl/container/flat_hash_set.h" #include "absl/strings/str_cat.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Function.h" @@ -45,10 +46,10 @@ void HloToIrBindings::EmitBasePointersForHlos( // An HLO can have duplicated operands. This data structure remembers which // operand HLOs are already bound to avoid rebinding the same HLO. - std::set already_bound_for_this_function; + absl::flat_hash_set already_bound_for_this_function; auto arg_iter = function->arg_begin(); for (const HloInstruction* io_hlo : io_hlos) { - if (!already_bound_for_this_function.count(io_hlo)) { + if (!already_bound_for_this_function.contains(io_hlo)) { if (!is_nested_ && io_hlo->opcode() == HloOpcode::kGetTupleElement) { BindHloToIrValue(*io_hlo, EmitGetTupleElement(io_hlo, &*arg_iter)); } else { @@ -63,7 +64,7 @@ void HloToIrBindings::EmitBasePointersForHlos( temp_buffer_base_->setName("temp_buffer"); for (const HloInstruction* non_io_hlo : non_io_hlos) { - if (already_bound_for_this_function.count(non_io_hlo)) { + if (already_bound_for_this_function.contains(non_io_hlo)) { continue; } already_bound_for_this_function.insert(non_io_hlo); diff --git a/tensorflow/compiler/xla/service/gpu/hlo_to_ir_bindings.h b/tensorflow/compiler/xla/service/gpu/hlo_to_ir_bindings.h index c0edae530c..f57b594e9c 100644 --- a/tensorflow/compiler/xla/service/gpu/hlo_to_ir_bindings.h +++ b/tensorflow/compiler/xla/service/gpu/hlo_to_ir_bindings.h @@ -18,6 +18,7 @@ limitations under the License. #include +#include "absl/container/flat_hash_map.h" #include "absl/types/span.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Value.h" @@ -61,7 +62,7 @@ class HloToIrBindings { // Returns whether `hlo` is bound to an LLVM IR value. bool BoundToIrValue(const HloInstruction& hlo) const { - return base_ptrs_.count(&hlo); + return base_ptrs_.contains(&hlo); } llvm::Value* GetTempBufferBase() const { return temp_buffer_base_; } @@ -110,7 +111,8 @@ class HloToIrBindings { // For an instruction that generates multiple outputs, the root will be a // tuple shape. The IrArray for each element output is stored in the subnode // in the ShapeTree. - std::unordered_map> base_ptrs_; + absl::flat_hash_map> + base_ptrs_; // The address of the memory block that contains all temporary buffers. llvm::Value* temp_buffer_base_ = nullptr; diff --git a/tensorflow/compiler/xla/service/gpu/multi_output_fusion.cc b/tensorflow/compiler/xla/service/gpu/multi_output_fusion.cc index 01fddcede6..02e1207f37 100644 --- a/tensorflow/compiler/xla/service/gpu/multi_output_fusion.cc +++ b/tensorflow/compiler/xla/service/gpu/multi_output_fusion.cc @@ -67,7 +67,7 @@ int64 GpuMultiOutputFusion::GetProfit(HloInstruction* instr1, } int64 profit = 0; for (auto instr : instr2->operands()) { - if (!IsProfitableOperand(instr) || in_list.count(instr) == 0) { + if (!IsProfitableOperand(instr) || !in_list.contains(instr)) { continue; } profit += ShapeUtil::ByteSizeOf(instr->shape()); diff --git a/tensorflow/compiler/xla/service/gpu/stream_assignment.cc b/tensorflow/compiler/xla/service/gpu/stream_assignment.cc index 4775baf44a..1dedbd3bef 100644 --- a/tensorflow/compiler/xla/service/gpu/stream_assignment.cc +++ b/tensorflow/compiler/xla/service/gpu/stream_assignment.cc @@ -15,6 +15,7 @@ limitations under the License. #include "tensorflow/compiler/xla/service/gpu/stream_assignment.h" +#include "absl/container/flat_hash_set.h" #include "absl/memory/memory.h" #include "tensorflow/compiler/xla/map_util.h" #include "tensorflow/compiler/xla/service/gpu/ir_emission_utils.h" @@ -25,7 +26,7 @@ namespace xla { namespace gpu { bool StreamAssignment::HasStreamAssigned(const HloInstruction& hlo) const { - return hlo_to_stream_number_.count(&hlo); + return hlo_to_stream_number_.contains(&hlo); } int StreamAssignment::StreamNumberForHlo(const HloInstruction& hlo) const { @@ -98,10 +99,10 @@ int ComputeStreamToAssign( // greedy approach. First, we compute as forbidden_stream_numbers the // streams assigned to GEMMs that are concurrent with `hlo`. Then, we assign // `hlo` a different stream. - std::set forbidden_stream_numbers; + absl::flat_hash_set forbidden_stream_numbers; for (const auto* seen_gemm : seen_gemms) { int stream_num = stream_assignment.StreamNumberForHlo(*seen_gemm); - if (!forbidden_stream_numbers.count(stream_num) && + if (!forbidden_stream_numbers.contains(stream_num) && CanRunConcurrently(*seen_gemm, hlo, reachability)) { forbidden_stream_numbers.insert(stream_num); } @@ -109,7 +110,7 @@ int ComputeStreamToAssign( for (int stream_num = 0; stream_num < stream_assignment.StreamCount(); ++stream_num) { - if (!forbidden_stream_numbers.count(stream_num)) { + if (!forbidden_stream_numbers.contains(stream_num)) { return stream_num; } } diff --git a/tensorflow/compiler/xla/service/gpu/thunk_schedule.cc b/tensorflow/compiler/xla/service/gpu/thunk_schedule.cc index 6b2d76764a..25bad67bab 100644 --- a/tensorflow/compiler/xla/service/gpu/thunk_schedule.cc +++ b/tensorflow/compiler/xla/service/gpu/thunk_schedule.cc @@ -14,17 +14,19 @@ limitations under the License. ==============================================================================*/ #include "tensorflow/compiler/xla/service/gpu/thunk_schedule.h" +#include "absl/container/flat_hash_map.h" #include "tensorflow/compiler/xla/array2d.h" #include "tensorflow/compiler/xla/map_util.h" #include "tensorflow/compiler/xla/types.h" +#include "tensorflow/core/lib/gtl/map_util.h" namespace xla { namespace gpu { void ThunkSchedule::AddDependenciesOnTransitiveOperands( const Thunk& thunk, const HloInstruction& operand, - const std::unordered_map& hlo_to_thunk) { - if (hlo_to_thunk.count(&operand)) { + const absl::flat_hash_map& hlo_to_thunk) { + if (hlo_to_thunk.contains(&operand)) { // If `operand` is mapped to a thunk, adds `operand` to `thunk`'s dependency // list if `operand` is assigned to a different stream. As an optimization, // we skip `operand`'s operands because `operand` depends on them already. @@ -48,14 +50,14 @@ ThunkSchedule::ThunkSchedule( const std::vector& hlo_total_order) : thunks_(std::move(thunks)), stream_assignment_(std::move(stream_assignment)) { - std::unordered_map hlo_to_thunk; + absl::flat_hash_map hlo_to_thunk; for (const auto& thunk : *thunks_) { InsertOrDie(&hlo_to_thunk, thunk->hlo_instruction(), thunk.get()); } for (HloInstruction* hlo : hlo_total_order) { - if (hlo_to_thunk.count(hlo)) { - thunk_total_order_.push_back(FindOrDie(hlo_to_thunk, hlo)); + if (Thunk** thunk = tensorflow::gtl::FindOrNull(hlo_to_thunk, hlo)) { + thunk_total_order_.push_back(*thunk); } } @@ -106,7 +108,7 @@ void ThunkSchedule::RemoveRedundantDependencyEdges() { // redundant dependency edge. Array2D last_dependency(stream_count, stream_count, -1); for (const Thunk* dst : thunk_total_order_) { - if (!depends_on_.count(dst)) { + if (!depends_on_.contains(dst)) { continue; } @@ -134,7 +136,7 @@ void ThunkSchedule::RemoveRedundantDependencyEdges() { const std::list& ThunkSchedule::DependsOn( const Thunk* thunk) const { - if (depends_on_.count(thunk)) { + if (depends_on_.contains(thunk)) { return FindOrDie(depends_on_, thunk); } else { return empty_thunk_list_; diff --git a/tensorflow/compiler/xla/service/gpu/thunk_schedule.h b/tensorflow/compiler/xla/service/gpu/thunk_schedule.h index 43b628a1ba..549378debd 100644 --- a/tensorflow/compiler/xla/service/gpu/thunk_schedule.h +++ b/tensorflow/compiler/xla/service/gpu/thunk_schedule.h @@ -21,6 +21,8 @@ limitations under the License. #include #include +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" #include "tensorflow/compiler/xla/service/gpu/stream_assignment.h" #include "tensorflow/compiler/xla/service/gpu/thunk.h" #include "tensorflow/compiler/xla/service/hlo_instruction.h" @@ -54,7 +56,9 @@ class ThunkSchedule { // Thunks that `thunk` depends on. const std::list& DependsOn(const Thunk* thunk) const; // Whether `thunk` is depended by another thunk. - bool Depended(const Thunk* thunk) const { return depended_by_.count(thunk); } + bool Depended(const Thunk* thunk) const { + return depended_by_.contains(thunk); + } // Delegates to StreamAssignment. int StreamCount() const { return stream_assignment_->StreamCount(); } @@ -75,13 +79,13 @@ class ThunkSchedule { // thunk.hlo_instruction(). void AddDependenciesOnTransitiveOperands( const Thunk& thunk, const HloInstruction& operand, - const std::unordered_map& hlo_to_thunk); + const absl::flat_hash_map& hlo_to_thunk); std::unique_ptr thunks_; std::vector thunk_total_order_; - std::unordered_map> depends_on_; - std::set depended_by_; + absl::flat_hash_map> depends_on_; + absl::flat_hash_set depended_by_; std::list empty_thunk_list_; std::unique_ptr stream_assignment_; diff --git a/tensorflow/compiler/xla/service/heap_simulator.cc b/tensorflow/compiler/xla/service/heap_simulator.cc index 9220865867..70ae7078ae 100644 --- a/tensorflow/compiler/xla/service/heap_simulator.cc +++ b/tensorflow/compiler/xla/service/heap_simulator.cc @@ -199,7 +199,7 @@ Status HeapSimulator::RunComputation( // If the buffer has no users and isn't an entry parameter or output, it // must be a dead value. - if (live_buffers.count(buffer) == 0) { + if (!live_buffers.contains(buffer)) { dead_buffers_to_free.push_back(buffer); } } @@ -253,7 +253,7 @@ Status HeapSimulator::RunComputation( bool shared = false; if (options_.may_reuse_operand_buffers) { for (const BufferValue* operand_buffer : operand_buffers_to_free) { - if (reused_buffers.count(operand_buffer) != 0) { + if (reused_buffers.contains(operand_buffer)) { continue; } if (buffer->instruction()->IsUserOf(operand_buffer->instruction()) && @@ -374,15 +374,15 @@ bool HeapSimulator::IgnoreBuffer(const BufferValue* buffer) const { return true; } return options_.buffers_to_assign != nullptr && - options_.buffers_to_assign->count(buffer) == 0; + !options_.buffers_to_assign->contains(buffer); } // Alloc always calls the underlying heap algorithm. void HeapSimulator::Alloc(const BufferValue* buffer, const HloInstruction* instruction) { - CHECK(allocated_buffers_.count(buffer) == 0) + CHECK(!allocated_buffers_.contains(buffer)) << "Alloc called on allocated buffer: " << *buffer; - CHECK(freed_buffers_.count(buffer) == 0) + CHECK(!freed_buffers_.contains(buffer)) << "Alloc called on freed buffer: " << *buffer; allocated_buffers_.insert(buffer); @@ -411,9 +411,9 @@ void HeapSimulator::Free(const BufferValue* buffer, buffer = group->canonical; } - CHECK(allocated_buffers_.count(buffer) > 0) + CHECK(allocated_buffers_.contains(buffer)) << "Free called on non-allocated buffer: " << *buffer; - CHECK(freed_buffers_.count(buffer) == 0) + CHECK(!freed_buffers_.contains(buffer)) << "Free called on freed buffer: " << *buffer; freed_buffers_.insert(buffer); @@ -433,11 +433,11 @@ void HeapSimulator::ShareBuffer(const BufferValue* buffer, const HloInstruction* instruction) { CHECK_LE(size_fn_(*buffer), size_fn_(*shared)) << "ShareBuffer oversized buffer" << *buffer << " shared: " << *shared; - CHECK(allocated_buffers_.count(buffer) == 0) + CHECK(!allocated_buffers_.contains(buffer)) << "ShareBuffer called on allocated buffer: " << *buffer; - CHECK(freed_buffers_.count(buffer) == 0) + CHECK(!freed_buffers_.contains(buffer)) << "ShareBuffer called on freed buffer: " << *buffer; - CHECK(freed_buffers_.count(shared) == 0) + CHECK(!freed_buffers_.contains(shared)) << "ShareBuffer called on freed shared buffer: " << *shared; const BufferValue* canonical = nullptr; @@ -452,7 +452,7 @@ void HeapSimulator::ShareBuffer(const BufferValue* buffer, } else { // The 'shared' buffer doesn't have a group; it must be the canonical. Add // both 'buffer' and 'shared' to a new group. - CHECK(allocated_buffers_.count(shared) > 0) + CHECK(allocated_buffers_.contains(shared)) << "ShareBuffer called on non-allocated shared buffer: " << *shared; auto group = std::make_shared(); canonical = shared; diff --git a/tensorflow/compiler/xla/service/hlo_computation.cc b/tensorflow/compiler/xla/service/hlo_computation.cc index 52ca67afb8..10f0cf3a34 100644 --- a/tensorflow/compiler/xla/service/hlo_computation.cc +++ b/tensorflow/compiler/xla/service/hlo_computation.cc @@ -207,14 +207,14 @@ Status HloComputation::RemoveInstructionAndUnusedOperands( TF_RET_CHECK(instruction->user_count() == 0); TF_RET_CHECK(IsRemovable(instruction)) << "Cannot remove instruction: " << instruction->ToString(); - std::unordered_set removed; + absl::flat_hash_set removed; std::queue worklist; worklist.push(instruction); while (!worklist.empty()) { HloInstruction* item = worklist.front(); worklist.pop(); - if (removed.count(item) != 0 || item->user_count() != 0 || + if (removed.contains(item) || item->user_count() != 0 || item == root_instruction() || !IsRemovable(item) || (item->HasSideEffect() && item != instruction)) { continue; @@ -694,13 +694,14 @@ bool HloComputation::operator==(const HloComputation& other) const { if (this == &other) { return true; } - std::set> visited; + absl::flat_hash_set> + visited; std::function eq = [&visited, &eq](const HloInstruction* a, const HloInstruction* b) { // If are visited but not identical, the recursion should have // been aborted. So, if are visited at this point, they must be // identical. - if (visited.count(std::make_pair(a, b)) > 0) { + if (visited.contains(std::make_pair(a, b))) { return true; } visited.emplace(a, b); @@ -803,13 +804,13 @@ Status HloComputation::AcceptOrdered( << root->ToString(); } TF_RET_CHECK(order.size() == instruction_count()); - std::unordered_set visited; + absl::flat_hash_set visited; for (const HloInstruction* instruction : order) { VLOG(3) << "Visiting ordered: " << instruction->ToString(); - TF_RET_CHECK(instruction_iterators_.count(instruction) == 1) + TF_RET_CHECK(instruction_iterators_.contains(instruction)) << "Instruction " << instruction->name() << " is not in computation " << name(); - TF_RET_CHECK(visited.count(instruction) == 0) + TF_RET_CHECK(!visited.contains(instruction)) << "Instruction " << instruction->name() << " appears more than once in order"; HloInstruction* mutable_instruction = diff --git a/tensorflow/compiler/xla/service/hlo_computation_test.cc b/tensorflow/compiler/xla/service/hlo_computation_test.cc index 0361c87428..216d56b868 100644 --- a/tensorflow/compiler/xla/service/hlo_computation_test.cc +++ b/tensorflow/compiler/xla/service/hlo_computation_test.cc @@ -17,6 +17,7 @@ limitations under the License. #include +#include "absl/container/flat_hash_set.h" #include "tensorflow/compiler/xla/literal.h" #include "tensorflow/compiler/xla/service/dfs_hlo_visitor_with_default.h" #include "tensorflow/compiler/xla/service/hlo_instruction.h" @@ -226,7 +227,7 @@ TEST_F(HloComputationTest, VisitWithMultipleRoots) { : computation_(computation) {} Status DefaultAction(HloInstruction* hlo_instruction) override { - EXPECT_EQ(0, visited_set_.count(hlo_instruction)); + EXPECT_FALSE(visited_set_.contains(hlo_instruction)); visited_set_.insert(hlo_instruction); last_visited_ = hlo_instruction; return Status::OK(); @@ -239,7 +240,7 @@ TEST_F(HloComputationTest, VisitWithMultipleRoots) { } HloComputation* computation_; - std::set visited_set_; + absl::flat_hash_set visited_set_; int64 finish_visit_calls_ = 0; HloInstruction* last_visited_ = nullptr; }; diff --git a/tensorflow/compiler/xla/service/hlo_dataflow_analysis.cc b/tensorflow/compiler/xla/service/hlo_dataflow_analysis.cc index 1924204df0..0db452b85f 100644 --- a/tensorflow/compiler/xla/service/hlo_dataflow_analysis.cc +++ b/tensorflow/compiler/xla/service/hlo_dataflow_analysis.cc @@ -107,7 +107,7 @@ bool HloDataflowAnalysis::AreTransitiveUsesElementwiseOrTuple( return false; } } - if (!visited.count(user)) { + if (!visited.contains(user)) { stack.push_back(user); } } diff --git a/tensorflow/compiler/xla/service/hlo_dce.cc b/tensorflow/compiler/xla/service/hlo_dce.cc index 7d35e251ca..a5a11f09cf 100644 --- a/tensorflow/compiler/xla/service/hlo_dce.cc +++ b/tensorflow/compiler/xla/service/hlo_dce.cc @@ -20,6 +20,7 @@ limitations under the License. #include #include +#include "absl/container/flat_hash_set.h" #include "tensorflow/compiler/xla/service/hlo_computation.h" #include "tensorflow/compiler/xla/service/hlo_instruction.h" #include "tensorflow/compiler/xla/service/hlo_module.h" @@ -65,7 +66,7 @@ StatusOr HloDCE::Run(HloModule* module) { // Now DCE HloComputations. First, collect the computations that are // referenced by some remaining instruction. - std::unordered_set live_computations; + absl::flat_hash_set live_computations; if (HloComputation* entry_computation = module->entry_computation()) { live_computations.insert(entry_computation); } @@ -79,7 +80,7 @@ StatusOr HloDCE::Run(HloModule* module) { // Remove dead computations. for (auto* computation : module->MakeComputationPostOrder()) { - if (live_computations.count(computation) == 0) { + if (!live_computations.contains(computation)) { TF_RETURN_IF_ERROR(module->RemoveEmbeddedComputation(computation)); changed = true; } diff --git a/tensorflow/compiler/xla/service/hlo_graph_dumper.cc b/tensorflow/compiler/xla/service/hlo_graph_dumper.cc index c6eaead8dd..22bcb2030b 100644 --- a/tensorflow/compiler/xla/service/hlo_graph_dumper.cc +++ b/tensorflow/compiler/xla/service/hlo_graph_dumper.cc @@ -24,9 +24,9 @@ limitations under the License. #include #include #include -#include #include +#include "absl/container/flat_hash_map.h" #include "absl/strings/match.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" @@ -380,7 +380,7 @@ class HloDotDumper { // Each HloInstruction dumped gets a monotically-increasing node ID. This // must start at 1, because that's where graphviz's accounting starts. int64 next_node_id_ = 1; - std::unordered_map node_ids_; + absl::flat_hash_map node_ids_; // The "root" tag doesn't have an associated HloInstruction pointer, so we // need to store it outside the map. @@ -397,7 +397,7 @@ class HloDotDumper { // Each HloComputation that's emitted gets a monotonically-increasing ID. int64 next_cluster_id_ = 1; - std::unordered_map cluster_ids_; + absl::flat_hash_map cluster_ids_; // Edges to print from Footer(). Edges come at the end because graphviz is // unhappy if an edge from a subcomputation to a node in the outer computation @@ -407,7 +407,7 @@ class HloDotDumper { // When coloring by sharding information, we track the sharding string // representation to color association, by round-robin the color schemes. - std::unordered_map + absl::flat_hash_map sharding_colors_; int64 next_shard_color_ = 0; }; @@ -1286,7 +1286,7 @@ NodeFilter MakeNodeRadiusAroundFilter(const HloInstruction* root, int64 radius) { // First, find the neighborhood of nodes with distance from root <= radius. // These nodes are our initial set of "normal" nodes. - std::unordered_map nodes; + absl::flat_hash_map nodes; std::deque> worklist; worklist.push_back({root, 0}); while (!worklist.empty()) { @@ -1307,7 +1307,7 @@ NodeFilter MakeNodeRadiusAroundFilter(const HloInstruction* root, // are not interesting to the graph at hand. if (instr == root || instr->opcode() != HloOpcode::kTuple) { for (const HloInstruction* operand : instr->operands()) { - if (!nodes.count(operand)) { + if (!nodes.contains(operand)) { worklist.push_back({operand, depth + 1}); } } @@ -1335,7 +1335,7 @@ NodeFilter MakeNodeRadiusAroundFilter(const HloInstruction* root, continue; } for (const HloInstruction* user : instr->users()) { - if (!nodes.count(user)) { + if (!nodes.contains(user)) { worklist.push_back({user, depth + 1}); } } @@ -1344,7 +1344,7 @@ NodeFilter MakeNodeRadiusAroundFilter(const HloInstruction* root, auto is_displayed = [&](const HloInstruction* instr) { // Constants are displayed inline with their users; they're never omitted. // Nodes in subcomputations are always shown. - return nodes.count(instr) > 0 || instr->opcode() == HloOpcode::kConstant || + return nodes.contains(instr) || instr->opcode() == HloOpcode::kConstant || instr->parent() != root->parent(); }; diff --git a/tensorflow/compiler/xla/service/hlo_instruction_test.cc b/tensorflow/compiler/xla/service/hlo_instruction_test.cc index 8048e332cb..35f031f29a 100644 --- a/tensorflow/compiler/xla/service/hlo_instruction_test.cc +++ b/tensorflow/compiler/xla/service/hlo_instruction_test.cc @@ -20,6 +20,7 @@ limitations under the License. #include #include +#include "absl/container/flat_hash_map.h" #include "tensorflow/compiler/xla/literal.h" #include "tensorflow/compiler/xla/protobuf_util.h" #include "tensorflow/compiler/xla/service/dfs_hlo_visitor_with_default.h" @@ -55,13 +56,13 @@ class OpAndUserCollectingVisitor : public DfsHloVisitorWithDefault { } Status HandleParameter(HloInstruction* parameter) override { - EXPECT_EQ(0, count_.count(parameter)); + EXPECT_FALSE(count_.contains(parameter)); count_[parameter] = GetCountsForNode(parameter); return Status::OK(); } Status HandleConstant(HloInstruction* constant) override { - EXPECT_EQ(0, count_.count(constant)); + EXPECT_FALSE(count_.contains(constant)); count_[constant] = GetCountsForNode(constant); return Status::OK(); } @@ -69,25 +70,25 @@ class OpAndUserCollectingVisitor : public DfsHloVisitorWithDefault { Status HandleAdd(HloInstruction* add) override { auto lhs = add->operand(0); auto rhs = add->operand(1); - EXPECT_EQ(0, count_.count(add)); - EXPECT_GT(count_.count(lhs), 0); - EXPECT_GT(count_.count(rhs), 0); + EXPECT_FALSE(count_.contains(add)); + EXPECT_TRUE(count_.contains(lhs)); + EXPECT_TRUE(count_.contains(rhs)); count_[add] = GetCountsForNode(add); return Status::OK(); } Status HandleNegate(HloInstruction* negate) override { auto operand = negate->operand(0); - EXPECT_EQ(0, count_.count(negate)); - EXPECT_GT(count_.count(operand), 0); + EXPECT_FALSE(count_.contains(negate)); + EXPECT_TRUE(count_.contains(operand)); count_[negate] = GetCountsForNode(negate); return Status::OK(); } Status HandleMap(HloInstruction* map) override { - EXPECT_EQ(0, count_.count(map)); + EXPECT_FALSE(count_.contains(map)); for (HloInstruction* arg : map->operands()) { - EXPECT_GT(count_.count(arg), 0); + EXPECT_TRUE(count_.contains(arg)); } count_[map] = GetCountsForNode(map); return Status::OK(); @@ -96,9 +97,9 @@ class OpAndUserCollectingVisitor : public DfsHloVisitorWithDefault { Status HandleReduce(HloInstruction* reduce) override { auto arg = reduce->operand(0); auto init_value = reduce->operand(1); - EXPECT_EQ(0, count_.count(reduce)); - EXPECT_GT(count_.count(arg), 0); - EXPECT_GT(count_.count(init_value), 0); + EXPECT_FALSE(count_.contains(reduce)); + EXPECT_TRUE(count_.contains(arg)); + EXPECT_TRUE(count_.contains(init_value)); count_[reduce] = GetCountsForNode(reduce); return Status::OK(); } @@ -128,7 +129,7 @@ class OpAndUserCollectingVisitor : public DfsHloVisitorWithDefault { } // Counters for HLOs. Maps HLO to a NumOpsAndUsers. - std::unordered_map count_; + absl::flat_hash_map count_; }; TEST_F(HloInstructionTest, BasicProperties) { @@ -137,7 +138,7 @@ TEST_F(HloInstructionTest, BasicProperties) { EXPECT_EQ(HloOpcode::kParameter, parameter->opcode()); EXPECT_TRUE(ShapeUtil::IsScalarWithElementType(parameter->shape(), F32)); EXPECT_FALSE(ShapeUtil::IsScalarWithElementType(parameter->shape(), S32)); - EXPECT_EQ(0, parameter->operand_count()); + EXPECT_FALSE(parameter->operand_count()); } TEST_F(HloInstructionTest, UserWithTwoOperands) { @@ -981,9 +982,9 @@ TEST_F(HloInstructionTest, FunctionVisitor) { module->AddEntryComputation(builder.Build()); int visit_num = 0; - std::unordered_map visit_order; + absl::flat_hash_map visit_order; EXPECT_IS_OK(add->Accept([&visit_num, &visit_order](HloInstruction* inst) { - EXPECT_EQ(0, visit_order.count(inst)); + EXPECT_FALSE(visit_order.contains(inst)); visit_order[inst] = visit_num; visit_num++; return Status::OK(); diff --git a/tensorflow/compiler/xla/service/hlo_liveness_analysis.cc b/tensorflow/compiler/xla/service/hlo_liveness_analysis.cc index 5bf055f3c0..e14bcfa7f6 100644 --- a/tensorflow/compiler/xla/service/hlo_liveness_analysis.cc +++ b/tensorflow/compiler/xla/service/hlo_liveness_analysis.cc @@ -17,6 +17,7 @@ limitations under the License. #include +#include "absl/container/flat_hash_set.h" #include "absl/memory/memory.h" #include "absl/strings/str_cat.h" #include "tensorflow/compiler/xla/map_util.h" @@ -36,11 +37,11 @@ namespace xla { namespace { using Worklist = std::deque; -using Workset = std::unordered_set; +using Workset = absl::flat_hash_set; void AddToWorklist(const HloInstruction* instruction, Worklist* worklist, Workset* workset) { - if (workset->count(instruction) == 0) { + if (!workset->contains(instruction)) { worklist->push_back(instruction); workset->insert(instruction); VLOG(3) << "ADD instruction: " << instruction->name(); diff --git a/tensorflow/compiler/xla/service/hlo_module.cc b/tensorflow/compiler/xla/service/hlo_module.cc index fe8371384c..ed78189fed 100644 --- a/tensorflow/compiler/xla/service/hlo_module.cc +++ b/tensorflow/compiler/xla/service/hlo_module.cc @@ -392,15 +392,12 @@ namespace { // Returns whether `hlo` is used outside the given subcomputation. // `instructions_in_subcomputation` is the instruction set of the given // subcomputation. -bool IsUsedOutsideSubcomputation( - const HloInstruction& hlo, - const std::unordered_set& instructions_in_subcomputation) { - for (HloInstruction* user : hlo.users()) { - if (!instructions_in_subcomputation.count(user)) { - return true; - } - } - return false; +bool IsUsedOutsideSubcomputation(const HloInstruction& hlo, + const absl::flat_hash_set& + instructions_in_subcomputation) { + return absl::c_any_of(hlo.users(), [&](HloInstruction* user) { + return !instructions_in_subcomputation.contains(user); + }); } } // anonymous namespace @@ -411,9 +408,9 @@ HloInstruction* HloModule::OutlineExpressionFromComputation( // A map from original instructions to their counterparts in the new outlined // function. - std::unordered_map outlined_instructions; + absl::flat_hash_map outlined_instructions; // A set that contains all instructions to be outlined. - std::unordered_set instruction_set_to_outline( + absl::flat_hash_set instruction_set_to_outline( instructions_to_outline.begin(), instructions_to_outline.end()); std::vector arguments; std::vector outputs; @@ -502,7 +499,7 @@ std::vector HloModule::MakeComputationPostOrder() const { // First determine all root computations by building a set of nonroot // computations (computations which are called by an instruction in the // module). - std::set nonroot_computations; + absl::flat_hash_set nonroot_computations; for (auto& computation : computations_) { for (auto* instruction : computation->instructions()) { for (HloComputation* called_computation : @@ -515,19 +512,19 @@ std::vector HloModule::MakeComputationPostOrder() const { // Keep track of computations which have already been added to the post // order. This prevents duplication as an embedded computation may be called // from two different root computations. - std::set added_computations; + absl::flat_hash_set added_computations; std::vector post_order; for (auto& computation : computations_) { - if (nonroot_computations.count(computation.get()) == 0) { + if (!nonroot_computations.contains(computation.get())) { for (HloComputation* embedded_computation : computation->MakeEmbeddedComputationsList()) { - if (added_computations.count(embedded_computation) == 0) { + if (!added_computations.contains(embedded_computation)) { post_order.push_back(embedded_computation); added_computations.insert(embedded_computation); } } // Root computations should only be encountered once. - CHECK_EQ(0, added_computations.count(computation.get())); + CHECK(!added_computations.contains(computation.get())); post_order.push_back(computation.get()); added_computations.insert(computation.get()); } diff --git a/tensorflow/compiler/xla/service/hlo_module_group_metadata.cc b/tensorflow/compiler/xla/service/hlo_module_group_metadata.cc index 80f8ca2226..47734bc55c 100644 --- a/tensorflow/compiler/xla/service/hlo_module_group_metadata.cc +++ b/tensorflow/compiler/xla/service/hlo_module_group_metadata.cc @@ -199,7 +199,7 @@ bool HloModuleGroupMetadata::IsChannelInstruction( } bool HloModuleGroupMetadata::IsCompanionInstruction(HloInstruction* hlo) const { - return companion_set_index_.count(hlo) > 0; + return companion_set_index_.contains(hlo); } bool HloModuleGroupMetadata::InstructionCommunicates( @@ -510,7 +510,7 @@ Status HloModuleGroupMetadata::CheckCommunicatingInstruction( HloComputation* computation = instruction->parent(); const HloModule* module = computation->parent(); if (module->entry_computation() == computation || - tracked_instructions_.count(computation) > 0) { + tracked_instructions_.contains(computation)) { return Status::OK(); } return FailedPrecondition("channel is used in disallowed computation"); diff --git a/tensorflow/compiler/xla/service/hlo_module_group_metadata.h b/tensorflow/compiler/xla/service/hlo_module_group_metadata.h index 5cd0e38f38..3ed95c1050 100644 --- a/tensorflow/compiler/xla/service/hlo_module_group_metadata.h +++ b/tensorflow/compiler/xla/service/hlo_module_group_metadata.h @@ -178,7 +178,7 @@ class HloModuleGroupMetadata { // Precondition: IsCompanionWhile(instruction) is true. const std::vector& Companions( const HloInstruction* instruction) const { - CHECK_EQ(companion_set_index_.count(instruction), 1); + CHECK(companion_set_index_.contains(instruction)); return companion_set(companion_set_index_.at(instruction)); } diff --git a/tensorflow/compiler/xla/service/hlo_ordering.cc b/tensorflow/compiler/xla/service/hlo_ordering.cc index ca6a154809..0cec61c257 100644 --- a/tensorflow/compiler/xla/service/hlo_ordering.cc +++ b/tensorflow/compiler/xla/service/hlo_ordering.cc @@ -367,7 +367,7 @@ bool SequentialHloOrdering::ExecutesBeforeInSameComputation( const HloInstruction* a, const HloInstruction* b) const { CHECK_EQ(a->parent(), b->parent()); // If either instruction is not in the order, then 'a' and 'b' are unordered. - if (order_position_.count(a) == 0 || order_position_.count(b) == 0) { + if (!order_position_.contains(a) || !order_position_.contains(b)) { return false; } return order_position_.at(a) < order_position_.at(b); diff --git a/tensorflow/compiler/xla/service/hlo_pass_pipeline.cc b/tensorflow/compiler/xla/service/hlo_pass_pipeline.cc index 33ce7e23a8..ae8c08cf1d 100644 --- a/tensorflow/compiler/xla/service/hlo_pass_pipeline.cc +++ b/tensorflow/compiler/xla/service/hlo_pass_pipeline.cc @@ -89,7 +89,7 @@ std::vector HloPassPipeline::GetEnabledPasses( std::vector enabled_passes; for (auto& pass : passes_) { - if (disabled_pass_names.count(string(pass->name())) == 0) { + if (!disabled_pass_names.contains(pass->name())) { enabled_passes.push_back(pass.get()); } } diff --git a/tensorflow/compiler/xla/service/hlo_schedule.cc b/tensorflow/compiler/xla/service/hlo_schedule.cc index 8f6eb974c5..e75373501c 100644 --- a/tensorflow/compiler/xla/service/hlo_schedule.cc +++ b/tensorflow/compiler/xla/service/hlo_schedule.cc @@ -140,7 +140,7 @@ Status HloSchedule::UpdateComputationSchedule( std::queue worklist; for (HloInstruction* instruction : computation->instructions()) { - if (ids_in_schedule.count(instruction->unique_id()) == 0) { + if (!ids_in_schedule.contains(instruction->unique_id())) { // This is a newly added instruction which is not in the schedule. if (instruction->operands().empty()) { worklist.push(instruction); @@ -204,7 +204,7 @@ Status HloSchedule::Update() { std::vector nonfusion_computations = module_->MakeNonfusionComputations(); for (const HloComputation* computation : nonfusion_computations) { - TF_RET_CHECK(sequences_.count(computation->unique_id()) == 1) + TF_RET_CHECK(sequences_.contains(computation->unique_id())) << "Computation " << computation->name() << " not in HloSchedule."; } if (sequences_.size() > nonfusion_computations.size()) { @@ -215,7 +215,7 @@ Status HloSchedule::Update() { nonfusion_computations_ids.insert(computation->unique_id()); } for (auto it = sequences_.begin(); it != sequences_.end();) { - if (nonfusion_computations_ids.count(it->first) == 0) { + if (!nonfusion_computations_ids.contains(it->first)) { sequences_.erase(it++); } else { ++it; @@ -244,7 +244,7 @@ Status HloSchedule::Verify() const { << "Schedule has " << sequences_.size() << " sequences, but module has " << nonfusion_computations.size() << " non-fusion computations"; for (const HloComputation* computation : nonfusion_computations) { - TF_RET_CHECK(sequences_.count(computation->unique_id()) == 1) + TF_RET_CHECK(sequences_.contains(computation->unique_id())) << "Computation " << computation->name() << " missing from HLO schedule."; } @@ -268,7 +268,7 @@ Status HloSchedule::Verify() const { << instruction_position.size() << " instructions, expected " << computation->instruction_count(); for (const HloInstruction* instruction : computation->instructions()) { - TF_RET_CHECK(instruction_position.count(instruction) == 1) + TF_RET_CHECK(instruction_position.contains(instruction)) << "Instruction " << instruction->name() << " is not in schedule"; } diff --git a/tensorflow/compiler/xla/service/hlo_schedule.h b/tensorflow/compiler/xla/service/hlo_schedule.h index 486ddbf499..a5f54ae2c3 100644 --- a/tensorflow/compiler/xla/service/hlo_schedule.h +++ b/tensorflow/compiler/xla/service/hlo_schedule.h @@ -110,7 +110,7 @@ class HloSchedule { // Returns true if the schedule has a sequence for the given computation. bool is_computation_scheduled(const HloComputation* computation) const { - return sequences_.count(computation->unique_id()) == 1; + return sequences_.contains(computation->unique_id()); } // Updates the schedule such that it is (again) a valid schedule for the diff --git a/tensorflow/compiler/xla/service/hlo_sharding.cc b/tensorflow/compiler/xla/service/hlo_sharding.cc index bdd6ec1169..6c9c32f179 100644 --- a/tensorflow/compiler/xla/service/hlo_sharding.cc +++ b/tensorflow/compiler/xla/service/hlo_sharding.cc @@ -15,6 +15,7 @@ limitations under the License. #include "tensorflow/compiler/xla/service/hlo_sharding.h" +#include "absl/container/flat_hash_set.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_join.h" #include "tensorflow/compiler/xla/overflow_util.h" @@ -316,7 +317,7 @@ Status HloSharding::ValidateNonTuple(const Shape& shape, // All tile assignments must be less than the number of available cores and // unique. Status status = Status::OK(); - std::set seen_cores; + absl::flat_hash_set seen_cores; tile_assignment_.Each( [&](absl::Span indices, int32 core) { // Don't overwrite a bad status, so we report the first error. @@ -324,7 +325,7 @@ Status HloSharding::ValidateNonTuple(const Shape& shape, if (core >= num_devices) { status = tensorflow::errors::InvalidArgument(StrCat( "core ", core, " > ", num_devices, " in tile assignment")); - } else if (seen_cores.count(core) != 0) { + } else if (seen_cores.contains(core)) { status = tensorflow::errors::InvalidArgument( StrCat("core ", core, " is not unique in tile assignment")); } diff --git a/tensorflow/compiler/xla/service/hlo_sharding_metadata.cc b/tensorflow/compiler/xla/service/hlo_sharding_metadata.cc index b414d2a663..094d98bc6e 100644 --- a/tensorflow/compiler/xla/service/hlo_sharding_metadata.cc +++ b/tensorflow/compiler/xla/service/hlo_sharding_metadata.cc @@ -99,7 +99,7 @@ std::vector LocatePassThroughDomainLinks( << "Instruction is not a kDomain: " << instruction->ToString(); for (HloInstruction* user : instruction->users()) { if (user->opcode() == HloOpcode::kDomain && - domain.exit_domains.count(user) != 0) { + domain.exit_domains.contains(user)) { pass_through.emplace_back(user, instruction); VLOG(2) << "Found passthrough domain link:"; VLOG(2) << " " << user->ToString(); @@ -253,7 +253,7 @@ StatusOr ApplyShardingFromUsers(HloInstruction* instruction, instruction->shape(), HloSharding::AssignDevice(kUnassignedDevice)); for (HloInstruction* user : instruction->users()) { if (user->opcode() == HloOpcode::kDomain && - domain.exit_domains.count(user) > 0) { + domain.exit_domains.contains(user)) { // If a user is a domain and it is registered in the domain exits, then // the instruction sharding is taken directly from the domain, and no // further users need to be visited. diff --git a/tensorflow/compiler/xla/service/indexed_array_analysis.cc b/tensorflow/compiler/xla/service/indexed_array_analysis.cc index a41cf714c5..76bf48870d 100644 --- a/tensorflow/compiler/xla/service/indexed_array_analysis.cc +++ b/tensorflow/compiler/xla/service/indexed_array_analysis.cc @@ -103,7 +103,7 @@ Status IndexedArrayAnalysis::TraverseAndPopulateCache( do { const HloInstruction* instr = stack.back(); - if (cache_.count(instr)) { + if (cache_.contains(instr)) { stack.pop_back(); continue; } @@ -111,9 +111,9 @@ Status IndexedArrayAnalysis::TraverseAndPopulateCache( switch (FindOrDie(dfs_state_map, instr)) { case kDiscovered: { for (const HloInstruction* operand : instr->operands()) { - if (!cache_.count(operand)) { + if (!cache_.contains(operand)) { stack.push_back(operand); - CHECK(!dfs_state_map.count(operand) || + CHECK(!dfs_state_map.contains(operand) || dfs_state_map[operand] == kDiscovered); dfs_state_map[operand] = kDiscovered; } diff --git a/tensorflow/compiler/xla/service/layout_assignment.cc b/tensorflow/compiler/xla/service/layout_assignment.cc index 09246db68e..406cd0a219 100644 --- a/tensorflow/compiler/xla/service/layout_assignment.cc +++ b/tensorflow/compiler/xla/service/layout_assignment.cc @@ -2135,7 +2135,7 @@ Status LayoutAssignment::ClearPreviousPassSideEffects(HloModule* module) { for (HloInstruction* instruction : computation->MakeInstructionPostOrder()) { if (instruction->opcode() == HloOpcode::kCopy && - added_copies_.count(instruction) > 0) { + added_copies_.contains(instruction)) { VLOG(5) << "Removing added copy: " << instruction->ToString(); TF_RETURN_IF_ERROR( instruction->ReplaceAllUsesWith(instruction->mutable_operand(0))); diff --git a/tensorflow/compiler/xla/service/layout_assignment.h b/tensorflow/compiler/xla/service/layout_assignment.h index 3b081de3c7..5701cb5b02 100644 --- a/tensorflow/compiler/xla/service/layout_assignment.h +++ b/tensorflow/compiler/xla/service/layout_assignment.h @@ -243,7 +243,7 @@ class ChannelLayoutConstraints { // Returns true if channel_id has a layout constraint. bool IsChannelConstrained(int64 channel_id) const { - return constraints_.count(channel_id) > 0; + return constraints_.contains(channel_id); } // Given `shape`, apply the layout for `channel_id`. `channel_id` must already @@ -276,7 +276,7 @@ class ChannelLayoutConstraints { } private: - std::unordered_map constraints_; + absl::flat_hash_map constraints_; }; // HLO pass which assigns layouts to all instructions in the HLO module while diff --git a/tensorflow/compiler/xla/service/llvm_ir/BUILD b/tensorflow/compiler/xla/service/llvm_ir/BUILD index 728a66b388..54e8fe1947 100644 --- a/tensorflow/compiler/xla/service/llvm_ir/BUILD +++ b/tensorflow/compiler/xla/service/llvm_ir/BUILD @@ -169,6 +169,7 @@ cc_library( "//tensorflow/compiler/xla/service:elemental_ir_emitter", "//tensorflow/compiler/xla/service:hlo", "//tensorflow/core:lib", + "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/types:optional", "@com_google_absl//absl/types:span", "@llvm//:core", diff --git a/tensorflow/compiler/xla/service/llvm_ir/alias_analysis.h b/tensorflow/compiler/xla/service/llvm_ir/alias_analysis.h index 2b46b3c396..12e2f449e2 100644 --- a/tensorflow/compiler/xla/service/llvm_ir/alias_analysis.h +++ b/tensorflow/compiler/xla/service/llvm_ir/alias_analysis.h @@ -76,15 +76,12 @@ class AliasAnalysis { // A map from a buffer slice to metadata corresponding to its alias.scope // metadata. The index kParameterAliasSet is used to hold aliasing // information for parameters. - absl::flat_hash_map + absl::flat_hash_map alias_scope_metadata_; // A map from a buffer slice to metadata corresponding to its noalias // metadata. - absl::flat_hash_map - noalias_metadata_; + absl::flat_hash_map noalias_metadata_; }; } // namespace llvm_ir diff --git a/tensorflow/compiler/xla/service/llvm_ir/fused_ir_emitter.cc b/tensorflow/compiler/xla/service/llvm_ir/fused_ir_emitter.cc index 03a475b40b..e440f05e2b 100644 --- a/tensorflow/compiler/xla/service/llvm_ir/fused_ir_emitter.cc +++ b/tensorflow/compiler/xla/service/llvm_ir/fused_ir_emitter.cc @@ -35,7 +35,7 @@ using llvm_ir::IrArray; Status FusedIrEmitter::DefaultAction(HloInstruction* hlo) { indexed_generators_[hlo] = [=](const IrArray::Index& index) -> StatusOr { - if (generated_value_cache_[hlo].count(index.multidim()) > 0) { + if (generated_value_cache_[hlo].contains(index.multidim())) { llvm::Value* generated_value = generated_value_cache_[hlo][index.multidim()]; llvm::BasicBlock* generated_value_bb = nullptr; diff --git a/tensorflow/compiler/xla/service/llvm_ir/fused_ir_emitter.h b/tensorflow/compiler/xla/service/llvm_ir/fused_ir_emitter.h index 1b9c61f670..e6d52a580c 100644 --- a/tensorflow/compiler/xla/service/llvm_ir/fused_ir_emitter.h +++ b/tensorflow/compiler/xla/service/llvm_ir/fused_ir_emitter.h @@ -19,6 +19,7 @@ limitations under the License. #include #include +#include "absl/container/flat_hash_map.h" #include "absl/types/optional.h" #include "absl/types/span.h" #include "llvm/IR/IRBuilder.h" @@ -134,8 +135,9 @@ class FusedIrEmitter : public DfsHloVisitorWithDefault { // Cache of generated values, lest we regenerate an element of a node with // multiple outgoing edges - std::unordered_map, llvm::Value*>> + absl::flat_hash_map< + const HloInstruction*, + absl::flat_hash_map, llvm::Value*>> generated_value_cache_; }; diff --git a/tensorflow/compiler/xla/service/multi_output_fusion.cc b/tensorflow/compiler/xla/service/multi_output_fusion.cc index 9ccdd7d8d8..53d52d9a3d 100644 --- a/tensorflow/compiler/xla/service/multi_output_fusion.cc +++ b/tensorflow/compiler/xla/service/multi_output_fusion.cc @@ -198,7 +198,7 @@ void MultiOutputFusion::Update(HloInstruction* instr1, HloInstruction* instr2) { if (instr == fusion || is_fused(instr) || is_connected(fusion, instr)) { continue; } - if (in_list.count(instr) > 0) { + if (in_list.contains(instr)) { continue; } int64 profit = GetProfit(instr, fusion); diff --git a/tensorflow/compiler/xla/service/tuple_points_to_analysis.cc b/tensorflow/compiler/xla/service/tuple_points_to_analysis.cc index b1f0672c60..dec719b04a 100644 --- a/tensorflow/compiler/xla/service/tuple_points_to_analysis.cc +++ b/tensorflow/compiler/xla/service/tuple_points_to_analysis.cc @@ -19,6 +19,7 @@ limitations under the License. #include #include +#include "absl/container/flat_hash_set.h" #include "absl/memory/memory.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" @@ -55,11 +56,10 @@ bool PointsToSet::IsAmbiguous() const { bool PointsToSet::IsDistinct() const { bool distinct = true; - std::set all_points_to; - ForEachElement([&distinct, &all_points_to](const ShapeIndex& /*index*/, - const BufferList& points_to) { + absl::flat_hash_set all_points_to; + ForEachElement([&](const ShapeIndex& /*index*/, const BufferList& points_to) { for (auto& buffer : points_to) { - if (all_points_to.count(buffer) != 0) { + if (all_points_to.contains(buffer)) { distinct = false; } all_points_to.insert(buffer); diff --git a/tensorflow/compiler/xla/service/while_loop_invariant_code_motion.cc b/tensorflow/compiler/xla/service/while_loop_invariant_code_motion.cc index a1c627a319..69cc8feb3f 100644 --- a/tensorflow/compiler/xla/service/while_loop_invariant_code_motion.cc +++ b/tensorflow/compiler/xla/service/while_loop_invariant_code_motion.cc @@ -89,7 +89,7 @@ static void CreateLoopInvariantCopy( HloInstruction* next_operand = frame->instruction->mutable_operand(frame->operand_index++); - if (hoisted_instructions->count(next_operand) || + if (hoisted_instructions->contains(next_operand) || next_operand == while_body_param) { continue; } @@ -241,7 +241,7 @@ WhileLoopInvariantCodeMotion::TryHoistingInvariantInstructionsFromWhileBody( auto is_invariant = [&](HloInstruction* op) { return hoisted_instructions.find(op) != hoisted_instructions.end() || - unhoisted_invariant_instructions.count(op) || + unhoisted_invariant_instructions.contains(op) || op->opcode() == HloOpcode::kConstant; }; diff --git a/tensorflow/compiler/xla/service/while_loop_simplifier.cc b/tensorflow/compiler/xla/service/while_loop_simplifier.cc index 772faa25e2..de884b037d 100644 --- a/tensorflow/compiler/xla/service/while_loop_simplifier.cc +++ b/tensorflow/compiler/xla/service/while_loop_simplifier.cc @@ -127,7 +127,7 @@ static StatusOr TryRemoveDeadWhileParams(HloInstruction* while_op) { // through to the while body's root, count that element as "used", since // removing that element would be observable. for (int64 i = 0; i < while_body_root->operand_count(); ++i) { - if (used_tuple_indices.count(i)) { + if (used_tuple_indices.contains(i)) { continue; } -- GitLab From b4813a0cff9f7ad1278aee376a5302f31c4558a2 Mon Sep 17 00:00:00 2001 From: Justin Lebar Date: Fri, 4 Jan 2019 12:29:13 -0800 Subject: [PATCH 358/622] [XLA] Use absl::c_foo rather than std::foo. No functional change. PiperOrigin-RevId: 227896034 --- tensorflow/compiler/xla/BUILD | 1 + .../compiler/xla/client/lib/sorting_test.cc | 2 +- tensorflow/compiler/xla/layout_util.cc | 6 +- .../compiler/xla/metric_table_report.cc | 4 +- tensorflow/compiler/xla/service/BUILD | 1 + .../xla/service/algebraic_simplifier.cc | 7 +- .../compiler/xla/service/buffer_assignment.cc | 85 +++++++++---------- .../compiler/xla/service/call_graph_test.cc | 2 +- .../xla/service/computation_layout.cc | 4 +- .../compiler/xla/service/copy_insertion.cc | 18 ++-- .../compiler/xla/service/cpu/disassembler.cc | 21 +++-- .../compiler/xla/service/cpu/ir_emitter.cc | 12 +-- .../xla/service/gpu/ir_emission_utils.cc | 15 ++-- .../xla/service/gpu/ir_emitter_unnested.cc | 8 +- .../compiler/xla/service/heap_simulator.cc | 41 +++++---- .../xla/service/hlo_alias_analysis.cc | 30 +++---- tensorflow/compiler/xla/service/hlo_buffer.cc | 2 +- .../compiler/xla/service/hlo_computation.cc | 12 ++- .../xla/service/hlo_dataflow_analysis.cc | 36 +++----- .../compiler/xla/service/hlo_dce_test.cc | 4 +- .../compiler/xla/service/hlo_domain_map.cc | 8 +- .../compiler/xla/service/hlo_evaluator.cc | 3 +- .../xla/service/hlo_evaluator_typed_visitor.h | 3 +- .../compiler/xla/service/hlo_graph_dumper.cc | 42 +++++---- .../compiler/xla/service/hlo_instruction.cc | 37 ++++---- .../compiler/xla/service/hlo_instructions.cc | 18 ++-- tensorflow/compiler/xla/service/hlo_module.cc | 18 ++-- .../xla/service/hlo_module_dce_test.cc | 4 +- tensorflow/compiler/xla/service/hlo_parser.cc | 2 +- .../xla/service/hlo_profile_printer.cc | 10 +-- .../compiler/xla/service/hlo_reachability.cc | 2 +- .../xla/service/hlo_rematerialization.cc | 25 +++--- .../compiler/xla/service/hlo_sharding.cc | 9 +- .../compiler/xla/service/hlo_sharding.h | 9 +- .../xla/service/hlo_tfgraph_builder.cc | 3 +- tensorflow/compiler/xla/service/hlo_value.cc | 2 +- .../service/human_readable_profile_builder.cc | 6 +- .../xla/service/instruction_fusion.cc | 30 +++---- .../compiler/xla/service/layout_assignment.cc | 9 +- .../xla/service/llvm_ir/alias_analysis.cc | 4 +- .../compiler/xla/service/name_uniquer.cc | 2 +- .../compiler/xla/service/platform_util.cc | 4 +- .../compiler/xla/service/shape_inference.cc | 22 +++-- .../compiler/xla/service/transpose_folding.cc | 6 +- .../xla/service/tuple_points_to_analysis.cc | 26 +++--- .../service/tuple_points_to_analysis_test.cc | 5 +- .../xla/service/while_loop_simplifier.cc | 5 +- .../xla/service/while_loop_simplifier_test.cc | 11 ++- tensorflow/compiler/xla/shape.cc | 3 +- tensorflow/compiler/xla/shape_util.cc | 3 +- tensorflow/compiler/xla/sparse_index_array.h | 2 +- .../compiler/xla/tests/convolution_test.cc | 4 +- tensorflow/compiler/xla/util.cc | 2 +- tensorflow/compiler/xla/util.h | 3 +- tensorflow/compiler/xla/window_util.cc | 32 ++++--- 55 files changed, 305 insertions(+), 380 deletions(-) diff --git a/tensorflow/compiler/xla/BUILD b/tensorflow/compiler/xla/BUILD index 8a0e2db2b5..45623c2718 100644 --- a/tensorflow/compiler/xla/BUILD +++ b/tensorflow/compiler/xla/BUILD @@ -717,6 +717,7 @@ cc_library( ":types", ":xla_data_proto", "//tensorflow/core:lib", + "@com_google_absl//absl/algorithm:container", "@com_google_absl//absl/strings", "@com_google_absl//absl/types:span", ], diff --git a/tensorflow/compiler/xla/client/lib/sorting_test.cc b/tensorflow/compiler/xla/client/lib/sorting_test.cc index 27ff36c749..0fbd138aca 100644 --- a/tensorflow/compiler/xla/client/lib/sorting_test.cc +++ b/tensorflow/compiler/xla/client/lib/sorting_test.cc @@ -77,7 +77,7 @@ XLA_TEST_F(SortingTest, TopKFullSort) { auto x = ConstantR1(&builder, inputs); xla::GetTupleElement(xla::TopK(x, kSize), 0); - std::sort(inputs.begin(), inputs.end(), std::greater()); + absl::c_sort(inputs, std::greater()); ComputeAndCompareR1(&builder, inputs, {}); } diff --git a/tensorflow/compiler/xla/layout_util.cc b/tensorflow/compiler/xla/layout_util.cc index 42649d6692..2fe9b56c6b 100644 --- a/tensorflow/compiler/xla/layout_util.cc +++ b/tensorflow/compiler/xla/layout_util.cc @@ -290,8 +290,8 @@ Layout CreateDefaultLayoutForRank(int64 rank) { /* static */ bool LayoutUtil::HasLayout(const Shape& shape) { if (shape.IsTuple()) { // Tuple shape: all subshapes must have a layout. - return std::all_of(shape.tuple_shapes().begin(), shape.tuple_shapes().end(), - [](const Shape& s) { return HasLayout(s); }); + return absl::c_all_of(shape.tuple_shapes(), + [](const Shape& s) { return HasLayout(s); }); } else if (!shape.IsArray()) { // Opaque, token types etc. ignore layout. return true; @@ -424,7 +424,7 @@ Status LayoutUtil::CopyLayoutBetweenShapes(const Shape& src, Shape* dst) { positions_in_layout.push_back( PositionInContainer(layout.minor_to_major(), dim)); } - std::sort(positions_in_layout.begin(), positions_in_layout.end()); + absl::c_sort(positions_in_layout); for (size_t i = 1; i < positions_in_layout.size(); ++i) { if (1 != positions_in_layout[i] - positions_in_layout[i - 1]) { return false; diff --git a/tensorflow/compiler/xla/metric_table_report.cc b/tensorflow/compiler/xla/metric_table_report.cc index 4eab4fa429..ad1699a1ae 100644 --- a/tensorflow/compiler/xla/metric_table_report.cc +++ b/tensorflow/compiler/xla/metric_table_report.cc @@ -55,7 +55,7 @@ string MetricTableReport::MakeReport(double expected_metric_sum) { const auto metric_greater = [](const Entry& a, const Entry& b) { return a.metric > b.metric; }; - std::sort(entries_.begin(), entries_.end(), metric_greater); + absl::c_sort(entries_, metric_greater); // Create the report AppendLine(); @@ -117,7 +117,7 @@ std::vector MetricTableReport::MakeCategories( auto metric_sum_greater = [](const Category& a, const Category& b) { return a.metric_sum > b.metric_sum; }; - std::sort(categories.begin(), categories.end(), metric_sum_greater); + absl::c_sort(categories, metric_sum_greater); return categories; } diff --git a/tensorflow/compiler/xla/service/BUILD b/tensorflow/compiler/xla/service/BUILD index 85a21a8518..3c18c450e6 100644 --- a/tensorflow/compiler/xla/service/BUILD +++ b/tensorflow/compiler/xla/service/BUILD @@ -3414,6 +3414,7 @@ cc_library( ":hlo_profile_printer_data", ":human_readable_profile_builder", "//tensorflow/compiler/xla:types", + "@com_google_absl//absl/algorithm:container", "@com_google_absl//absl/strings", ], ) diff --git a/tensorflow/compiler/xla/service/algebraic_simplifier.cc b/tensorflow/compiler/xla/service/algebraic_simplifier.cc index 3d269dc04f..003cb7e791 100644 --- a/tensorflow/compiler/xla/service/algebraic_simplifier.cc +++ b/tensorflow/compiler/xla/service/algebraic_simplifier.cc @@ -2216,8 +2216,7 @@ Status AlgebraicSimplifierVisitor::HandleReverse(HloInstruction* reverse) { auto dim_is_one = [&](int64 i) -> bool { return reverse->shape().dimensions(i) == 1; }; - if (std::all_of(reverse->dimensions().begin(), reverse->dimensions().end(), - dim_is_one)) { + if (absl::c_all_of(reverse->dimensions(), dim_is_one)) { return ReplaceInstruction(reverse, reverse->mutable_operand(0)); } return Status::OK(); @@ -2492,9 +2491,9 @@ Status AlgebraicSimplifierVisitor::HandleReduce(HloInstruction* reduce) { // Create a new reduce with the combined reduction dimensions of both // reduces. std::vector arg_dims = arg->dimensions(); - std::sort(arg_dims.begin(), arg_dims.end()); + absl::c_sort(arg_dims); std::vector reduce_dims = reduce->dimensions(); - std::sort(reduce_dims.begin(), reduce_dims.end()); + absl::c_sort(reduce_dims); // Transform reduce_dims to the same rank as the operand of the operand. for (int64 arg_dim : arg_dims) { for (int64& dim : reduce_dims) { diff --git a/tensorflow/compiler/xla/service/buffer_assignment.cc b/tensorflow/compiler/xla/service/buffer_assignment.cc index c997d594c4..c3285f516b 100644 --- a/tensorflow/compiler/xla/service/buffer_assignment.cc +++ b/tensorflow/compiler/xla/service/buffer_assignment.cc @@ -86,10 +86,9 @@ std::vector ColorInterferenceGraph( // first, but it would be good to investigate other ordering heuristics too. std::vector nodes(node_count); std::iota(nodes.begin(), nodes.end(), 0); - std::sort(nodes.begin(), nodes.end(), - [&interference_map](const int64 i, const int64 j) { - return interference_map[i].size() > interference_map[j].size(); - }); + absl::c_sort(nodes, [&interference_map](const int64 i, const int64 j) { + return interference_map[i].size() > interference_map[j].size(); + }); const int64 kColorUnassigned = -1; std::vector assigned_colors(node_count, kColorUnassigned); @@ -272,11 +271,12 @@ BufferAllocationProto BufferAllocation::ToProto() const { proto_assigned->set_offset(buffer_offset_size.second.offset); proto_assigned->set_size(buffer_offset_size.second.size); } - std::sort(proto.mutable_assigned()->begin(), proto.mutable_assigned()->end(), - [](const BufferAllocationProto::Assigned& assign1, - const BufferAllocationProto::Assigned& assign2) { - return assign1.logical_buffer_id() < assign2.logical_buffer_id(); - }); + absl::c_sort(*proto.mutable_assigned(), + [](const BufferAllocationProto::Assigned& assign1, + const BufferAllocationProto::Assigned& assign2) { + return assign1.logical_buffer_id() < + assign2.logical_buffer_id(); + }); return proto; } @@ -308,10 +308,10 @@ string BufferAllocation::ToString() const { for (const auto& buffer_offset_size : assigned_buffers_) { sorted_buffers.push_back(buffer_offset_size.first); } - std::sort(sorted_buffers.begin(), sorted_buffers.end(), - [](const LogicalBuffer* a, const LogicalBuffer* b) { - return a->id() < b->id(); - }); + absl::c_sort(sorted_buffers, + [](const LogicalBuffer* a, const LogicalBuffer* b) { + return a->id() < b->id(); + }); for (const LogicalBuffer* buffer : sorted_buffers) { const OffsetSize& offset_size = FindOrDie(assigned_buffers_, buffer); StrAppend(&output, absl::StrFormat( @@ -479,10 +479,9 @@ bool BufferAssignment::HaveDisjointSlices(const HloInstruction* hlo_a, // didn't return the empty set) for both HLOs, and the two resulting sets of // slices are disjoint. return !slices_a.empty() && !slices_b.empty() && - std::none_of(slices_a.begin(), slices_a.end(), - [&](const BufferAllocation::Slice& slice) { - return slices_b.count(slice) > 0; - }); + absl::c_none_of(slices_a, [&](const BufferAllocation::Slice& slice) { + return slices_b.contains(slice); + }); } StatusOr @@ -952,28 +951,28 @@ Status BufferAssigner::AssignBuffersForComputation( // operands (assuming operands are the same/larger size) enabling the // important reuse case where an elementwise instruction reuses one of its // operand's buffer. This improves locality. - std::sort(sorted_buffers.begin(), sorted_buffers.end(), - [has_sequential_order, &liveness, &post_order_position, assignment]( - const LogicalBuffer* a, const LogicalBuffer* b) { - // Primary sort is by decreasing buffer size. - const int64 a_size = assignment->buffer_size_(*a); - const int64 b_size = assignment->buffer_size_(*b); - if (a_size != b_size) { - return a_size > b_size; // use ">" for decreasing size. - } - // Otherwise live out buffers come before others, if the - // instructions are sequentially ordered. - if (has_sequential_order) { - const bool a_live_out = liveness.MaybeLiveOut(*a); - const bool b_live_out = liveness.MaybeLiveOut(*b); - if (a_live_out != b_live_out) { - return a_live_out; - } - } - // Final tiebreaker is in instruction post order. - return post_order_position.at(a->instruction()) < - post_order_position.at(b->instruction()); - }); + absl::c_sort(sorted_buffers, + [has_sequential_order, &liveness, &post_order_position, + assignment](const LogicalBuffer* a, const LogicalBuffer* b) { + // Primary sort is by decreasing buffer size. + const int64 a_size = assignment->buffer_size_(*a); + const int64 b_size = assignment->buffer_size_(*b); + if (a_size != b_size) { + return a_size > b_size; // use ">" for decreasing size. + } + // Otherwise live out buffers come before others, if the + // instructions are sequentially ordered. + if (has_sequential_order) { + const bool a_live_out = liveness.MaybeLiveOut(*a); + const bool b_live_out = liveness.MaybeLiveOut(*b); + if (a_live_out != b_live_out) { + return a_live_out; + } + } + // Final tiebreaker is in instruction post order. + return post_order_position.at(a->instruction()) < + post_order_position.at(b->instruction()); + }); // BufferAllocations are necessarily created in decreasing size order. Keep // indices of previously created BufferAllocations in allocation_indices. @@ -1305,10 +1304,10 @@ std::vector ComputePeakMemoryLogicalBuffers( live_buffers.end()); // Stabily sort the live buffers. - std::sort(live_buffers_vector.begin(), live_buffers_vector.end(), - [](const LogicalBuffer* a, const LogicalBuffer* b) { - return a->id() < b->id(); - }); + absl::c_sort(live_buffers_vector, + [](const LogicalBuffer* a, const LogicalBuffer* b) { + return a->id() < b->id(); + }); return live_buffers_vector; } diff --git a/tensorflow/compiler/xla/service/call_graph_test.cc b/tensorflow/compiler/xla/service/call_graph_test.cc index 7924134200..5de724f892 100644 --- a/tensorflow/compiler/xla/service/call_graph_test.cc +++ b/tensorflow/compiler/xla/service/call_graph_test.cc @@ -384,7 +384,7 @@ TEST_F(CallGraphTest, ComplexGraph) { // Verify visitation order of some computations in the graph. auto index_of = [&visited](const HloComputation* comp) { - auto it = std::find(visited.begin(), visited.end(), comp); + auto it = absl::c_find(visited, comp); EXPECT_NE(it, visited.end()); return std::distance(visited.begin(), it); }; diff --git a/tensorflow/compiler/xla/service/computation_layout.cc b/tensorflow/compiler/xla/service/computation_layout.cc index efc893818d..92d1ca4ba5 100644 --- a/tensorflow/compiler/xla/service/computation_layout.cc +++ b/tensorflow/compiler/xla/service/computation_layout.cc @@ -42,8 +42,8 @@ void ComputationLayout::SetToDefaultLayout() { } bool ComputationLayout::LayoutIsSet() const { - return std::all_of(parameter_layouts_.begin(), parameter_layouts_.end(), - [](const ShapeLayout& s) { return s.LayoutIsSet(); }) && + return absl::c_all_of(parameter_layouts_, + [](const ShapeLayout& s) { return s.LayoutIsSet(); }) && result_layout_.LayoutIsSet(); } diff --git a/tensorflow/compiler/xla/service/copy_insertion.cc b/tensorflow/compiler/xla/service/copy_insertion.cc index e0165d557d..2b837901f0 100644 --- a/tensorflow/compiler/xla/service/copy_insertion.cc +++ b/tensorflow/compiler/xla/service/copy_insertion.cc @@ -539,10 +539,9 @@ class CopyRemover { } std::vector values = buffer.values(); - std::sort(values.begin(), values.end(), - [this](const HloValue* a, const HloValue* b) { - return ordering_.IsDefinedBefore(*a, *b); - }); + absl::c_sort(values, [this](const HloValue* a, const HloValue* b) { + return ordering_.IsDefinedBefore(*a, *b); + }); // Create a list containing all of the values in the buffer. AddValueList(values, &value_to_node); @@ -842,12 +841,11 @@ class CopyRemover { copy_value_node->next->prev = operand_node; // Patch up uses. Remove use of copy from operand_node uses. - auto it = - std::find_if(operand_node->uses.begin(), operand_node->uses.end(), - [copy_value_node](const HloUse* use) { - return use->instruction == - copy_value_node->value->defining_instruction(); - }); + auto it = absl::c_find_if( + operand_node->uses, [copy_value_node](const HloUse* use) { + return use->instruction == + copy_value_node->value->defining_instruction(); + }); CHECK(it != operand_node->uses.end()); operand_node->uses.erase(it); diff --git a/tensorflow/compiler/xla/service/cpu/disassembler.cc b/tensorflow/compiler/xla/service/cpu/disassembler.cc index 3ae64142cd..c3c6847b7b 100644 --- a/tensorflow/compiler/xla/service/cpu/disassembler.cc +++ b/tensorflow/compiler/xla/service/cpu/disassembler.cc @@ -77,17 +77,16 @@ StatusOr Disassembler::DisassembleObjectFile( } // Sort the symbols in increasing address order. - std::sort( - symbols.begin(), symbols.end(), - [](const llvm::object::SymbolRef& a, const llvm::object::SymbolRef& b) { - // getAddress returns a Expected object. Assert there is no error - // before extracting the address. - llvm::Expected a_address_or_error = a.getAddress(); - CHECK(a_address_or_error); - llvm::Expected b_address_or_error = b.getAddress(); - CHECK(b_address_or_error); - return a_address_or_error.get() < b_address_or_error.get(); - }); + absl::c_sort(symbols, [](const llvm::object::SymbolRef& a, + const llvm::object::SymbolRef& b) { + // getAddress returns a Expected object. Assert there is no error + // before extracting the address. + llvm::Expected a_address_or_error = a.getAddress(); + CHECK(a_address_or_error); + llvm::Expected b_address_or_error = b.getAddress(); + CHECK(b_address_or_error); + return a_address_or_error.get() < b_address_or_error.get(); + }); // Construct ArrayRef pointing to section contents. llvm::StringRef section_content_string; diff --git a/tensorflow/compiler/xla/service/cpu/ir_emitter.cc b/tensorflow/compiler/xla/service/cpu/ir_emitter.cc index e4760aedf1..b352848824 100644 --- a/tensorflow/compiler/xla/service/cpu/ir_emitter.cc +++ b/tensorflow/compiler/xla/service/cpu/ir_emitter.cc @@ -1709,10 +1709,8 @@ StatusOr IrEmitter::EmitVectorizedReduce( vectorization_factor_in_bytes / ShapeUtil::ByteSizeOfPrimitiveType(reduce->shape().element_type()); - bool is_reduction_over_minor_dimension = - std::find(dimensions.begin(), dimensions.end(), - LayoutUtil::Minor(arg->shape().layout(), 0)) != - dimensions.end(); + bool is_reduction_over_minor_dimension = absl::c_linear_search( + dimensions, LayoutUtil::Minor(arg->shape().layout(), 0)); unsigned element_alignment = tensorflow::MathUtil::GCD( ShapeUtil::ByteSizeOfPrimitiveType(reduce->shape().element_type()), @@ -2401,8 +2399,7 @@ StatusOr IrEmitter::EmitFastConcatenate( int64 concat_dim = concatenate->dimensions(0); const Layout& output_layout = output_shape.layout(); auto output_min2maj = LayoutUtil::MinorToMajor(output_layout); - auto concat_dim_layout_itr = - std::find(output_min2maj.begin(), output_min2maj.end(), concat_dim); + auto concat_dim_layout_itr = absl::c_find(output_min2maj, concat_dim); std::vector inner_dims(output_min2maj.begin(), concat_dim_layout_itr); std::vector outer_dims(std::next(concat_dim_layout_itr), @@ -2956,8 +2953,7 @@ Status IrEmitter::ElementTypesSameAndSupported( TF_RET_CHECK(!operands.empty()); PrimitiveType primitive_type = operands[0]->shape().element_type(); - if (std::find(supported_types.begin(), supported_types.end(), - primitive_type) == supported_types.end()) { + if (!absl::c_linear_search(supported_types, primitive_type)) { return Unimplemented("unsupported operand type %s in op %s", PrimitiveType_Name(primitive_type), HloOpcodeString(instruction.opcode())); diff --git a/tensorflow/compiler/xla/service/gpu/ir_emission_utils.cc b/tensorflow/compiler/xla/service/gpu/ir_emission_utils.cc index 5d25a032a9..caccb18899 100644 --- a/tensorflow/compiler/xla/service/gpu/ir_emission_utils.cc +++ b/tensorflow/compiler/xla/service/gpu/ir_emission_utils.cc @@ -154,20 +154,17 @@ bool IsReductionToVector(const HloInstruction& reduce) { const HloInstruction* input = reduce.operand(0); std::vector dims_to_keep; for (int64 dim = 0; dim < input->shape().dimensions().size(); ++dim) { - if (!std::count(reduce.dimensions().begin(), reduce.dimensions().end(), - dim)) { + if (!absl::c_linear_search(reduce.dimensions(), dim)) { dims_to_keep.push_back(dim); } } return LayoutUtil::AreDimensionsConsecutive(input->shape().layout(), dims_to_keep) && - ShapeUtil::Equal(reduce.shape(), ShapeUtil::FilterDimensions( - [&dims_to_keep](int64 dim) { - return std::count( - dims_to_keep.begin(), - dims_to_keep.end(), dim); - }, - input->shape())); + ShapeUtil::Equal( + reduce.shape(), + ShapeUtil::FilterDimensions( + [&](int64 dim) { return absl::c_count(dims_to_keep, dim); }, + input->shape())); } // This emits a device-side call to diff --git a/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.cc b/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.cc index 063d493d90..923b3d5945 100644 --- a/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.cc +++ b/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.cc @@ -1506,10 +1506,10 @@ std::unique_ptr IrEmitterUnnested::BuildKernelThunk( return !allocation->is_constant(); }); - std::sort(non_constant_buffers.begin(), non_constant_buffers.end(), - [](const BufferAllocation* a, const BufferAllocation* b) { - return a->index() < b->index(); - }); + absl::c_sort(non_constant_buffers, + [](const BufferAllocation* a, const BufferAllocation* b) { + return a->index() < b->index(); + }); llvm::Function* kernel = BuildKernelPrototype(*inst, non_constant_buffers); diff --git a/tensorflow/compiler/xla/service/heap_simulator.cc b/tensorflow/compiler/xla/service/heap_simulator.cc index 70ae7078ae..4fca981c6a 100644 --- a/tensorflow/compiler/xla/service/heap_simulator.cc +++ b/tensorflow/compiler/xla/service/heap_simulator.cc @@ -225,10 +225,10 @@ Status HeapSimulator::RunComputation( } } // Sort to get a deterministic iteration order. - std::sort(operand_buffers_to_free.begin(), operand_buffers_to_free.end(), - [](const BufferValue* x, const BufferValue* y) { - return x->id() < y->id(); - }); + absl::c_sort(operand_buffers_to_free, + [](const BufferValue* x, const BufferValue* y) { + return x->id() < y->id(); + }); // Allocate buffers defined by this instruction. This is the latest point // that we can allocate; right before the buffer is first used. This must @@ -335,10 +335,9 @@ Status HeapSimulator::RunComputation( to_free.push_back(buffer); } - std::sort(to_free.begin(), to_free.end(), - [](const BufferValue* x, const BufferValue* y) { - return x->id() < y->id(); - }); + absl::c_sort(to_free, [](const BufferValue* x, const BufferValue* y) { + return x->id() < y->id(); + }); for (const BufferValue* buffer : to_free) { VLOG(3) << "Freeing pending: " << buffer->ToString(); Free(buffer, root); @@ -596,7 +595,7 @@ void DecreasingSizeRunsHeap::CallAndDrainRun() { } // Call ops in the run sorted by decreasing size, breaking ties by buffer id. - std::sort(run_.begin(), run_.end(), [](const Op& a, const Op& b) { + absl::c_sort(run_, [](const Op& a, const Op& b) { if (a.size != b.size) { return a.size > b.size; } @@ -866,23 +865,23 @@ HeapSimulator::Result GlobalDecreasingSizeBestFitHeap::Finish() { for (auto& entry : buffer_intervals_) { sorted_buffer_intervals.push_back(entry.second); } - std::sort(sorted_buffer_intervals.begin(), sorted_buffer_intervals.end(), - [](const BufferInterval& x, const BufferInterval& y) { - if (x.size != y.size) { - return x.size > y.size; - } - if (x.end - x.start != y.end - y.start) { - return x.end - x.start > y.end - y.start; - } - return x.buffer->id() < y.buffer->id(); - }); + absl::c_sort(sorted_buffer_intervals, + [](const BufferInterval& x, const BufferInterval& y) { + if (x.size != y.size) { + return x.size > y.size; + } + if (x.end - x.start != y.end - y.start) { + return x.end - x.start > y.end - y.start; + } + return x.buffer->id() < y.buffer->id(); + }); BufferIntervalTree interval_tree(sorted_buffer_intervals.size()); for (auto& buffer_interval : sorted_buffer_intervals) { auto chunks_overlapping_in_time = interval_tree.ChunksOverlappingInTime( buffer_interval.start, buffer_interval.end); - std::sort( - chunks_overlapping_in_time.begin(), chunks_overlapping_in_time.end(), + absl::c_sort( + chunks_overlapping_in_time, [](const Chunk& x, const Chunk& y) { return x.offset < y.offset; }); // Find the minimum free chunk that can hold this buffer. diff --git a/tensorflow/compiler/xla/service/hlo_alias_analysis.cc b/tensorflow/compiler/xla/service/hlo_alias_analysis.cc index 68094d8907..4c46ea595e 100644 --- a/tensorflow/compiler/xla/service/hlo_alias_analysis.cc +++ b/tensorflow/compiler/xla/service/hlo_alias_analysis.cc @@ -117,7 +117,7 @@ class BufferValueMap { for (const auto& pair : buffers_) { buffer_numbers.push_back(pair.first); } - std::sort(buffer_numbers.begin(), buffer_numbers.end()); + absl::c_sort(buffer_numbers); return buffer_numbers; } @@ -319,7 +319,7 @@ class BufferValueMap { ComputeWhileAliasedBuffers(value, &aliased_buffers); ComputeConditionalAliasedBuffers(value, &aliased_buffers); // Uniquify aliased buffers. - std::sort(aliased_buffers.begin(), aliased_buffers.end()); + absl::c_sort(aliased_buffers); aliased_buffers.erase( std::unique(aliased_buffers.begin(), aliased_buffers.end()), aliased_buffers.end()); @@ -367,7 +367,7 @@ std::vector HloAliasAnalysis::ComputeBuffersAt( } // Sort and uniquify vector before returning. - std::sort(buffers.begin(), buffers.end(), HloBuffer::IdLessThan); + absl::c_sort(buffers, HloBuffer::IdLessThan); buffers.erase(std::unique(buffers.begin(), buffers.end()), buffers.end()); return buffers; @@ -430,8 +430,7 @@ Status HloAliasAnalysis::Verify() const { for (const auto& pair : value_to_buffer_) { const HloValue* value = pair.first; const HloBuffer& buffer = *pair.second; - TF_RET_CHECK(std::find(buffer.values().begin(), buffer.values().end(), - value) != buffer.values().end()); + TF_RET_CHECK(absl::c_linear_search(buffer.values(), value)); } for (HloBuffer::Id id = 0; id < buffers_.size(); ++id) { @@ -515,7 +514,7 @@ StatusOr> HloAliasAnalysis::Run( auto& value_set = buffer_map.GetValuesInBuffer(buffer_number); std::vector sorted_values(value_set.begin(), value_set.end()); - std::sort(sorted_values.begin(), sorted_values.end(), HloValue::IdLessThan); + absl::c_sort(sorted_values, HloValue::IdLessThan); alias_analysis->buffers_.emplace_back(next_id++, sorted_values); for (const HloValue* value : sorted_values) { alias_analysis->value_to_buffer_[value] = @@ -547,16 +546,15 @@ bool HloAliasAnalysis::HasLiveRangeInterference( // tie-break using value ID. The tie-break is necessary because we need a // strict weak order for std::sort. std::vector values = buffer.values(); - std::sort(values.begin(), values.end(), - [&ordering](const HloValue* a, const HloValue* b) { - if (ordering.IsDefinedBefore(*a, *b)) { - return true; - } else if (ordering.IsDefinedBefore(*b, *a)) { - return false; - } else { - return a->id() < b->id(); - } - }); + absl::c_sort(values, [&ordering](const HloValue* a, const HloValue* b) { + if (ordering.IsDefinedBefore(*a, *b)) { + return true; + } else if (ordering.IsDefinedBefore(*b, *a)) { + return false; + } else { + return a->id() < b->id(); + } + }); // Walk through the ordered vector of values. First verify that the values // are totally ordered with respect to 'ordering', then check that no diff --git a/tensorflow/compiler/xla/service/hlo_buffer.cc b/tensorflow/compiler/xla/service/hlo_buffer.cc index 9c3aa0e64d..32e48651b3 100644 --- a/tensorflow/compiler/xla/service/hlo_buffer.cc +++ b/tensorflow/compiler/xla/service/hlo_buffer.cc @@ -49,7 +49,7 @@ std::vector HloBuffer::ComputePositions() const { value->positions().end()); } // Remove duplicates and sort positions. - std::sort(positions.begin(), positions.end()); + absl::c_sort(positions); positions.erase(std::unique(positions.begin(), positions.end()), positions.end()); return positions; diff --git a/tensorflow/compiler/xla/service/hlo_computation.cc b/tensorflow/compiler/xla/service/hlo_computation.cc index 10f0cf3a34..9e15722633 100644 --- a/tensorflow/compiler/xla/service/hlo_computation.cc +++ b/tensorflow/compiler/xla/service/hlo_computation.cc @@ -531,11 +531,10 @@ HloComputation::CreateFromProto( HloInstruction* root = instruction_map.at(proto.root_id()); // Sort the instructions in the proto id's order. - std::sort(instructions.begin(), instructions.end(), - [&](const std::unique_ptr& a, - const std::unique_ptr& b) { - return to_proto_id[a.get()] < to_proto_id[b.get()]; - }); + absl::c_sort(instructions, [&](const std::unique_ptr& a, + const std::unique_ptr& b) { + return to_proto_id[a.get()] < to_proto_id[b.get()]; + }); TF_RETURN_IF_ERROR([&]() -> Status { std::vector parameters_seen(parameter_count); @@ -800,8 +799,7 @@ Status HloComputation::AcceptOrdered( absl::Span order) const { VLOG(3) << "Accepting visitor with order."; for (HloInstruction* root : CollectUnreachableRoots()) { - TF_RET_CHECK(std::find(order.begin(), order.end(), root) != order.end()) - << root->ToString(); + TF_RET_CHECK(absl::c_linear_search(order, root)) << root->ToString(); } TF_RET_CHECK(order.size() == instruction_count()); absl::flat_hash_set visited; diff --git a/tensorflow/compiler/xla/service/hlo_dataflow_analysis.cc b/tensorflow/compiler/xla/service/hlo_dataflow_analysis.cc index 0db452b85f..3144a84805 100644 --- a/tensorflow/compiler/xla/service/hlo_dataflow_analysis.cc +++ b/tensorflow/compiler/xla/service/hlo_dataflow_analysis.cc @@ -256,7 +256,7 @@ bool HloDataflowAnalysis::Phi( input_value_ids.push_back(value->id()); } } - std::sort(input_value_ids.begin(), input_value_ids.end()); + absl::c_sort(input_value_ids); input_value_ids.erase( std::unique(input_value_ids.begin(), input_value_ids.end()), input_value_ids.end()); @@ -271,8 +271,7 @@ bool HloDataflowAnalysis::Phi( if (current_value_defined_here) { VLOG(5) << "current_value_defined_here: " << current_value->ToString(); CHECK(current_value->is_phi()); - auto it = std::find(input_value_ids.begin(), input_value_ids.end(), - current_value->id()); + auto it = absl::c_find(input_value_ids, current_value->id()); if (it != input_value_ids.end()) { input_value_ids.erase(it); } @@ -921,8 +920,7 @@ StatusOr> HloDataflowAnalysis::Run( for (auto& pair : dataflow_analysis->values_) { dataflow_analysis->values_vector_.push_back(&pair.second); } - std::sort(dataflow_analysis->values_vector_.begin(), - dataflow_analysis->values_vector_.end(), HloValue::IdLessThan); + absl::c_sort(dataflow_analysis->values_vector_, HloValue::IdLessThan); TF_DCHECK_OK(dataflow_analysis->Verify()); @@ -937,9 +935,7 @@ Status HloDataflowAnalysis::Verify() const { for (const HloValue* value : values()) { for (const HloPosition& position : value->positions()) { const HloValueSet& value_set = GetValueSet(position); - TF_RET_CHECK(std::find(value_set.values().begin(), - value_set.values().end(), - value) != value_set.values().end()) + TF_RET_CHECK(absl::c_linear_search(value_set.values(), value)) << "Value set at position " << position << " does not contain value " << value->ToShortString(); } @@ -954,9 +950,7 @@ Status HloDataflowAnalysis::Verify() const { const HloValueSet& value_set = pair.second; const HloPosition position{instruction, index}; for (const HloValue* value : value_set.values()) { - TF_RET_CHECK(std::find(value->positions().begin(), - value->positions().end(), - position) != value->positions().end()) + TF_RET_CHECK(absl::c_linear_search(value->positions(), position)) << "Value set at position " << position << " unexpectedly contains value " << value->ToShortString(); } @@ -1041,11 +1035,10 @@ bool HloDataflowAnalysis::CanShareOperandBufferWithUser( // Check if one operand of kAdd fused root is kDot or kConvolution. auto* add = user->fused_expression_root(); auto add_operand_it = - std::find_if(add->operands().begin(), add->operands().end(), - [&](HloInstruction* operand) { - return operand->opcode() == HloOpcode::kConvolution || - operand->opcode() == HloOpcode::kDot; - }); + absl::c_find_if(add->operands(), [&](HloInstruction* operand) { + return operand->opcode() == HloOpcode::kConvolution || + operand->opcode() == HloOpcode::kDot; + }); if (add_operand_it == add->operands().end()) { return false; } @@ -1100,16 +1093,15 @@ bool HloDataflowAnalysis::CanShareOperandBufferWithUser( // *) The root instruction of the called computation is element-wise on // 'operand'. const bool found_caller_use = - std::find_if(uses.begin(), uses.end(), [user](const HloUse& use) { + absl::c_find_if(uses, [user](const HloUse& use) { return use.instruction == user; }) != uses.end(); auto* callee_root = user->to_apply()->root_instruction(); const bool found_elementwise_callee_use = - std::find_if( - uses.begin(), uses.end(), [callee_root](const HloUse& use) { - return use.instruction == callee_root && - callee_root->IsElementwiseOnOperand(use.operand_number); - }) != uses.end(); + absl::c_find_if(uses, [callee_root](const HloUse& use) { + return use.instruction == callee_root && + callee_root->IsElementwiseOnOperand(use.operand_number); + }) != uses.end(); return uses.size() == 2 && found_caller_use && found_elementwise_callee_use; } diff --git a/tensorflow/compiler/xla/service/hlo_dce_test.cc b/tensorflow/compiler/xla/service/hlo_dce_test.cc index 1fa4259a3e..b5d72b386f 100644 --- a/tensorflow/compiler/xla/service/hlo_dce_test.cc +++ b/tensorflow/compiler/xla/service/hlo_dce_test.cc @@ -43,9 +43,7 @@ class HloDceTest : public HloTestBase { // Returns whether the given instruction exists in the given computation. bool HasInstruction(const HloComputation& computation, const HloInstruction* instruction) { - return std::find(computation.instructions().begin(), - computation.instructions().end(), - instruction) != computation.instructions().end(); + return absl::c_linear_search(computation.instructions(), instruction); } }; diff --git a/tensorflow/compiler/xla/service/hlo_domain_map.cc b/tensorflow/compiler/xla/service/hlo_domain_map.cc index c6d02f9f67..7cdb7f6bdf 100644 --- a/tensorflow/compiler/xla/service/hlo_domain_map.cc +++ b/tensorflow/compiler/xla/service/hlo_domain_map.cc @@ -230,10 +230,10 @@ HloDomainMap::MakeNonDomainInstructions( } } // sort instructions according to instructions_order - std::sort(instructions.begin(), instructions.end(), - [&instructions_order](HloInstruction* a, HloInstruction* b) { - return instructions_order.at(a) < instructions_order.at(b); - }); + absl::c_sort(instructions, + [&instructions_order](HloInstruction* a, HloInstruction* b) { + return instructions_order.at(a) < instructions_order.at(b); + }); return instructions; } diff --git a/tensorflow/compiler/xla/service/hlo_evaluator.cc b/tensorflow/compiler/xla/service/hlo_evaluator.cc index 9754b8906b..3a97b56c66 100644 --- a/tensorflow/compiler/xla/service/hlo_evaluator.cc +++ b/tensorflow/compiler/xla/service/hlo_evaluator.cc @@ -1248,8 +1248,7 @@ StatusOr EvaluateSortInternal(HloInstruction* sort, // Extract a slice from the keys and values literals that correspond to // exactly the row in dimension 'sort_dim'. std::vector limit_indices(indices.begin(), indices.end()); - std::for_each(limit_indices.begin(), limit_indices.end(), - [](int64& index) { ++index; }); + absl::c_for_each(limit_indices, [](int64& index) { ++index; }); limit_indices[sort_dim] = sort_dim_elements; TF_ASSIGN_OR_RETURN(auto keys_to_sort, keys_literal.Slice(indices, limit_indices) diff --git a/tensorflow/compiler/xla/service/hlo_evaluator_typed_visitor.h b/tensorflow/compiler/xla/service/hlo_evaluator_typed_visitor.h index cf3509d0aa..cc5f6cc682 100644 --- a/tensorflow/compiler/xla/service/hlo_evaluator_typed_visitor.h +++ b/tensorflow/compiler/xla/service/hlo_evaluator_typed_visitor.h @@ -1673,8 +1673,7 @@ class HloEvaluatorTypedVisitor : public DfsHloVisitorWithDefault { // Extract a slice from the literal that corresponds to exactly the // row in dimension 'sort_dim'. std::vector limit_indices(indices.begin(), indices.end()); - std::for_each(limit_indices.begin(), limit_indices.end(), - [](int64& index) { ++index; }); + absl::c_for_each(limit_indices, [](int64& index) { ++index; }); limit_indices[sort_dim] = sort_dim_elements; TF_ASSIGN_OR_RETURN(auto row_to_sort, keys_literal.Slice(indices, limit_indices) diff --git a/tensorflow/compiler/xla/service/hlo_graph_dumper.cc b/tensorflow/compiler/xla/service/hlo_graph_dumper.cc index 22bcb2030b..4c7f5e9e7d 100644 --- a/tensorflow/compiler/xla/service/hlo_graph_dumper.cc +++ b/tensorflow/compiler/xla/service/hlo_graph_dumper.cc @@ -561,8 +561,8 @@ bool HloDotDumper::ShouldShowSubcomputation(const HloComputation* subcomp) { } // Show the subcomputation if we're showing any of its members. - return std::any_of( - subcomp->instructions().begin(), subcomp->instructions().end(), + return absl::c_any_of( + subcomp->instructions(), [&](const HloInstruction* instr) { return filter_.Show(instr); }); } @@ -735,15 +735,14 @@ bool HloDotDumper::ShouldMergeIntoUsers(const HloInstruction* instr) const { const int kMinUsersToOmit = 3; return instr->opcode() == HloOpcode::kParameter && instr->shape().IsTuple() && !instr->IsFused() && - std::count_if(instr->users().begin(), instr->users().end(), - [&](const HloInstruction* user) { - return filter_.Show(user); - }) > kMinUsersToOmit && - std::all_of(instr->users().begin(), instr->users().end(), - [&](const HloInstruction* user) { - return !filter_.Show(user) || - user->opcode() == HloOpcode::kGetTupleElement; - }); + absl::c_count_if(instr->users(), + [&](const HloInstruction* user) { + return filter_.Show(user); + }) > kMinUsersToOmit && + absl::c_all_of(instr->users(), [&](const HloInstruction* user) { + return !filter_.Show(user) || + user->opcode() == HloOpcode::kGetTupleElement; + }); } string HloDotDumper::DumpInstruction(const HloInstruction* instr) { @@ -900,12 +899,11 @@ ColorScheme HloDotDumper::GetInstructionColor(const HloInstruction* instr) { // the same color as a parameter. Unless the merged-in parameter is a // parameter to a fusion node that is bound to a constant -- these aren't // "real" parameters from the user's perspective. - if (std::any_of(instr->operands().begin(), instr->operands().end(), - [&](const HloInstruction* operand) { - return operand->opcode() == HloOpcode::kParameter && - ShouldMergeIntoUsers(operand) && - TryGetFusionParameterConstant(operand) == nullptr; - })) { + if (absl::c_any_of(instr->operands(), [&](const HloInstruction* operand) { + return operand->opcode() == HloOpcode::kParameter && + ShouldMergeIntoUsers(operand) && + TryGetFusionParameterConstant(operand) == nullptr; + })) { return parameter_color; } @@ -1355,12 +1353,11 @@ NodeFilter MakeNodeRadiusAroundFilter(const HloInstruction* root, NodeFilterResult& filter_result = kv.second; const auto& operands = instr->operands(); - if (std::any_of(operands.begin(), operands.end(), is_displayed) && - !std::all_of(operands.begin(), operands.end(), is_displayed)) { + if (absl::c_any_of(operands, is_displayed) && + !absl::c_all_of(operands, is_displayed)) { // Mark nodes with some operands omitted appropriately. filter_result = kSomeOperandsOmitted; - } else if (!operands.empty() && - std::none_of(operands.begin(), operands.end(), is_displayed)) { + } else if (!operands.empty() && absl::c_none_of(operands, is_displayed)) { // Mark nodes with *all* operands omitted appropriately. filter_result = kOmitNodeOperands; } @@ -1368,8 +1365,7 @@ NodeFilter MakeNodeRadiusAroundFilter(const HloInstruction* root, // Promote nodes with type kSomeUsersOmitted to kNormalNode if all of their // users made it into the graph. if (filter_result == kSomeUsersOmitted && - std::all_of(instr->users().begin(), instr->users().end(), - is_displayed)) { + absl::c_all_of(instr->users(), is_displayed)) { filter_result = kNormalNode; } } diff --git a/tensorflow/compiler/xla/service/hlo_instruction.cc b/tensorflow/compiler/xla/service/hlo_instruction.cc index 66bc73740f..029d170317 100644 --- a/tensorflow/compiler/xla/service/hlo_instruction.cc +++ b/tensorflow/compiler/xla/service/hlo_instruction.cc @@ -83,15 +83,14 @@ StatusOr> HloInstruction::CreateFromProto( return computation_map.at(proto.called_computation_ids(index)); }; - TF_RET_CHECK(std::all_of( - proto.operand_ids().begin(), proto.operand_ids().end(), - [&instruction_map](int64 id) { return instruction_map.contains(id); })) + TF_RET_CHECK( + absl::c_all_of(proto.operand_ids(), + [&](int64 id) { return instruction_map.contains(id); })) << proto.name() << " instruction contains invalid operand id(s)"; - TF_RET_CHECK(std::all_of( - proto.called_computation_ids().begin(), - proto.called_computation_ids().end(), - [&computation_map](int64 id) { return computation_map.contains(id); })) + TF_RET_CHECK( + absl::c_all_of(proto.called_computation_ids(), + [&](int64 id) { return computation_map.contains(id); })) << proto.name() << " instruction references invalid computation id(s)"; Shape shape(proto.shape()); @@ -1599,12 +1598,10 @@ HloInstruction::InstructionVector HloInstruction::unique_operands() const { Status HloInstruction::AddControlDependencyTo(HloInstruction* instruction) { TF_RET_CHECK(instruction->parent() == parent()); - if (std::find(control_successors_.begin(), control_successors_.end(), - instruction) == control_successors_.end()) { + if (!absl::c_linear_search(control_successors_, instruction)) { control_successors_.push_back(instruction); - TF_RET_CHECK(std::find(instruction->control_predecessors_.begin(), - instruction->control_predecessors_.end(), - this) == instruction->control_predecessors_.end()); + TF_RET_CHECK( + !absl::c_linear_search(instruction->control_predecessors_, this)); instruction->control_predecessors_.push_back(this); } return Status::OK(); @@ -1853,7 +1850,7 @@ void HloInstruction::RemoveUser(HloInstruction* user) { user_set_.erase(set_it); // This is linear in the number of the users, but a vector provides a stable // iteration order and much faster traversal. - auto vec_it = std::find(users_.begin(), users_.end(), user); + auto vec_it = absl::c_find(users_, user); CHECK(vec_it != users_.end()); users_.erase(vec_it); } @@ -1871,8 +1868,7 @@ Status HloInstruction::ReplaceUseWith(HloInstruction* user, RemoveUser(user); - TF_RET_CHECK( - std::count(user->operands_.begin(), user->operands_.end(), this) >= 0); + TF_RET_CHECK(absl::c_count(user->operands_, this) >= 0); std::replace(user->operands_.begin(), user->operands_.end(), this, new_producer); new_producer->AddUser(user); @@ -1907,8 +1903,7 @@ Status HloInstruction::ReplaceOperandWithDifferentShape( VLOG(3) << "Replacing operand " << operand_num << " of " << name() << " with " << new_operand->name() << ", was " << old_operand->name(); - if (std::find(operands_.begin(), operands_.end(), old_operand) == - operands_.end()) { + if (!absl::c_linear_search(operands_, old_operand)) { old_operand->RemoveUser(this); } new_operand->AddUser(this); @@ -2945,10 +2940,10 @@ StatusOr StringToFusionKind( string PaddingConfigToString(const PaddingConfig& padding) { bool has_interior_padding = - std::any_of(padding.dimensions().begin(), padding.dimensions().end(), - [](const PaddingConfig::PaddingConfigDimension& dim) { - return dim.interior_padding() != 0; - }); + absl::c_any_of(padding.dimensions(), + [](const PaddingConfig::PaddingConfigDimension& dim) { + return dim.interior_padding() != 0; + }); return StrJoin( padding.dimensions(), "x", [&](string* out, const PaddingConfig::PaddingConfigDimension& dim) { diff --git a/tensorflow/compiler/xla/service/hlo_instructions.cc b/tensorflow/compiler/xla/service/hlo_instructions.cc index 7170dd7d81..c24bf41ff8 100644 --- a/tensorflow/compiler/xla/service/hlo_instructions.cc +++ b/tensorflow/compiler/xla/service/hlo_instructions.cc @@ -42,11 +42,9 @@ using absl::StrJoin; bool IsInstructionElementwiseOnOperand(const HloInstruction* instruction, const HloInstruction* operand) { std::vector operand_indices = instruction->OperandIndices(operand); - return std::all_of( - operand_indices.begin(), operand_indices.end(), - [instruction](int64 operand_index) { - return instruction->IsElementwiseOnOperand(operand_index); - }); + return absl::c_all_of(operand_indices, [instruction](int64 operand_index) { + return instruction->IsElementwiseOnOperand(operand_index); + }); } string PrecisionConfigToString(const PrecisionConfig& precision_config) { @@ -814,8 +812,7 @@ std::vector HloSliceInstruction::ExtraAttributesToStringImpl( std::vector bounds; bounds.reserve(slice_starts_.size()); const bool omit_stride = - std::all_of(slice_strides_.begin(), slice_strides_.end(), - [](int64 stride) { return stride == 1; }); + absl::c_all_of(slice_strides_, [](int64 stride) { return stride == 1; }); for (int i = 0; i < slice_starts_.size(); ++i) { string stride_str = omit_stride ? "" : StrCat(":", slice_strides_[i]); bounds.push_back( @@ -1051,8 +1048,7 @@ HloInstruction* HloFusionInstruction::AddFusionOperand( void HloFusionInstruction::MergeFusionInstruction( HloFusionInstruction* instruction_to_merge) { - CHECK(std::find(operands().begin(), operands().end(), instruction_to_merge) != - operands().end()); + CHECK(absl::c_linear_search(operands(), instruction_to_merge)); // Clone the instruction from which to merge fused instructions. std::unique_ptr cloned = instruction_to_merge->Clone(); HloFusionInstruction* cloned_fusion = @@ -1219,8 +1215,8 @@ HloInstruction* HloFusionInstruction::CloneAndFuseInternal( // corresponding fused parameter instruction. Renumber parameters as // necessary to make parameter numbers consistent with their index in the // fused_parameter_ vector. - bool in_operand_list = std::find(operands().begin(), operands().end(), - instruction_to_fuse) != operands().end(); + bool in_operand_list = + absl::c_linear_search(operands(), instruction_to_fuse); CHECK(add_output || in_operand_list); if (instruction_to_fuse->opcode() == HloOpcode::kTuple) { // We assume all uses of a kTuple operation are GTE ops, not another diff --git a/tensorflow/compiler/xla/service/hlo_module.cc b/tensorflow/compiler/xla/service/hlo_module.cc index ed78189fed..258f918f47 100644 --- a/tensorflow/compiler/xla/service/hlo_module.cc +++ b/tensorflow/compiler/xla/service/hlo_module.cc @@ -107,11 +107,10 @@ HloComputation* HloModule::AddEntryComputation( } Status HloModule::RemoveEmbeddedComputation(HloComputation* to_remove) { - auto it = - std::find_if(computations_.begin(), computations_.end(), - [&to_remove](const std::unique_ptr& comp) { - return comp.get() == to_remove; - }); + auto it = absl::c_find_if( + computations_, [&to_remove](const std::unique_ptr& comp) { + return comp.get() == to_remove; + }); TF_RET_CHECK(it->get() == to_remove); computations_.erase(it); return Status::OK(); @@ -304,11 +303,10 @@ StatusOr> HloModule::CreateFromProto( auto module = absl::make_unique(proto.name(), module_config); // Sort the computations in the proto id's order. - std::sort(computations.begin(), computations.end(), - [&](const std::unique_ptr& a, - const std::unique_ptr& b) { - return to_proto_id[a.get()] < to_proto_id[b.get()]; - }); + absl::c_sort(computations, [&](const std::unique_ptr& a, + const std::unique_ptr& b) { + return to_proto_id[a.get()] < to_proto_id[b.get()]; + }); // Add sorted computations to the module. for (auto& computation : computations) { diff --git a/tensorflow/compiler/xla/service/hlo_module_dce_test.cc b/tensorflow/compiler/xla/service/hlo_module_dce_test.cc index e535b7d749..f6e2866204 100644 --- a/tensorflow/compiler/xla/service/hlo_module_dce_test.cc +++ b/tensorflow/compiler/xla/service/hlo_module_dce_test.cc @@ -38,9 +38,7 @@ class HloModuleDceTest : public HloTestBase { // Returns whether the given instruction exists in the given computation. bool HasInstruction(const HloComputation& computation, const HloInstruction* instruction) { - return std::find(computation.instructions().begin(), - computation.instructions().end(), - instruction) != computation.instructions().end(); + return absl::c_linear_search(computation.instructions(), instruction); } // Returns whether the while instruction with name 'while_name' in diff --git a/tensorflow/compiler/xla/service/hlo_parser.cc b/tensorflow/compiler/xla/service/hlo_parser.cc index 730d39500b..638396308c 100644 --- a/tensorflow/compiler/xla/service/hlo_parser.cc +++ b/tensorflow/compiler/xla/service/hlo_parser.cc @@ -2746,7 +2746,7 @@ bool HloParser::ParseConvolutionDimensionNumbers( } auto is_unique = [](string str) -> bool { - std::sort(str.begin(), str.end()); + absl::c_sort(str); return std::unique(str.begin(), str.end()) == str.end(); }; diff --git a/tensorflow/compiler/xla/service/hlo_profile_printer.cc b/tensorflow/compiler/xla/service/hlo_profile_printer.cc index 5eb707a957..9cc202aa9f 100644 --- a/tensorflow/compiler/xla/service/hlo_profile_printer.cc +++ b/tensorflow/compiler/xla/service/hlo_profile_printer.cc @@ -15,6 +15,7 @@ limitations under the License. #include "tensorflow/compiler/xla/service/hlo_profile_printer.h" +#include "absl/algorithm/container.h" #include "absl/strings/str_cat.h" #include "tensorflow/compiler/xla/service/human_readable_profile_builder.h" @@ -34,11 +35,10 @@ string PrintHloProfile(const HloProfilePrinterData& hlo_profile_printer_data, for (const HloComputationInfo& computation_info : hlo_profile_printer_data.computation_infos()) { const auto& instruction_infos = computation_info.instruction_infos(); - bool any_instruction_profiled = - std::any_of(instruction_infos.begin(), instruction_infos.end(), - [&](const HloInstructionInfo& instruction_info) { - return counters[instruction_info.profile_index()] != 0; - }); + bool any_instruction_profiled = absl::c_any_of( + instruction_infos, [&](const HloInstructionInfo& instruction_info) { + return counters[instruction_info.profile_index()] != 0; + }); if (!any_instruction_profiled) { continue; diff --git a/tensorflow/compiler/xla/service/hlo_reachability.cc b/tensorflow/compiler/xla/service/hlo_reachability.cc index edaa4c59e2..0fced7f15b 100644 --- a/tensorflow/compiler/xla/service/hlo_reachability.cc +++ b/tensorflow/compiler/xla/service/hlo_reachability.cc @@ -49,7 +49,7 @@ void HloReachabilityMap::SetReachabilityToUnionHelper( absl::Span inputs, const HloInstruction* instruction, BitVector* bit_vector) { // If instruction is part of inputs, don't reset the bit_vector. - if (std::find(inputs.begin(), inputs.end(), instruction) == inputs.end()) { + if (!absl::c_linear_search(inputs, instruction)) { bit_vector->SetToZero(); } bit_vector->Set(GetIndex(instruction)); diff --git a/tensorflow/compiler/xla/service/hlo_rematerialization.cc b/tensorflow/compiler/xla/service/hlo_rematerialization.cc index ac74e2432f..9ca14ca18a 100644 --- a/tensorflow/compiler/xla/service/hlo_rematerialization.cc +++ b/tensorflow/compiler/xla/service/hlo_rematerialization.cc @@ -235,8 +235,7 @@ class InstructionList { } // Now scan forwards until we find one of the before_instructions. - while (std::find(before_instructions.begin(), before_instructions.end(), - min_position_item) == before_instructions.end()) { + while (!absl::c_linear_search(before_instructions, min_position_item)) { min_position_item = min_position_item->next; } return InsertBefore(to_insert, min_position_item); @@ -302,7 +301,7 @@ ItemList GetUsers(const InstructionList& instruction_list, // A buffer may be used by the instruction via more than one alias. For // example, a buffer which appears in more than one element of a tuple. Item* user_item = instruction_list.GetItem(user); - if (std::find(users.begin(), users.end(), user_item) == users.end()) { + if (!absl::c_linear_search(users, user_item)) { users.push_back(user_item); } } @@ -456,8 +455,7 @@ class MemoryUsageTracker { return false; } const BufferIdList& in_progress_uses = in_progress_item_->buffers_used; - return std::find(in_progress_uses.begin(), in_progress_uses.end(), - buffer_id) != in_progress_uses.end(); + return absl::c_linear_search(in_progress_uses, buffer_id); } // Returns whether the given instruction is live at the current program @@ -535,8 +533,7 @@ MemoryUsageTracker::MemoryUsageTracker( bool unused; for (Item* user_item : GetUsers(instruction_list_, logical_buffer, points_to_analysis, &unused)) { - if (std::find(buffer->users.begin(), buffer->users.end(), - user_item) == buffer->users.end()) { + if (!absl::c_linear_search(buffer->users, user_item)) { buffer->users.push_back(user_item); buffer->unfinished_user_count++; user_item->buffers_used.push_back(buffer->id); @@ -784,8 +781,7 @@ bool MemoryUsageTracker::Check() const { for (const Buffer& buffer : buffers_) { if (buffer.defining_instruction->instruction == instruction) { - CHECK(std::find(defined_buffers.begin(), defined_buffers.end(), - buffer.id) != defined_buffers.end()) + CHECK(absl::c_linear_search(defined_buffers, buffer.id)) << "Instruction " << instruction->name() << " defined buffers is missing: " << buffer.ToString(); } @@ -808,8 +804,7 @@ bool MemoryUsageTracker::Check() const { int64 unfinished_uses = 0; for (Item* user : buffer.users) { const BufferIdList& used_buffers = user->buffers_used; - CHECK(std::find(used_buffers.begin(), used_buffers.end(), buffer.id) != - used_buffers.end()) + CHECK(absl::c_linear_search(used_buffers, buffer.id)) << "Instruction " << user->instruction->name() << " used buffers is missing " << buffer.ToString(); if (!IsFinished(user)) { @@ -836,10 +831,10 @@ int64 RematerializationCost(const HloInstruction* instruction, // If none of the users of 'instruction' have been placed in the sequence (as // tracked by memory_tracker), then rematerialization of 'instruction' is a // zero-cost move of 'instruction' in the sequence. - if (!std::any_of(instruction->users().begin(), instruction->users().end(), - [&memory_tracker](const HloInstruction* inst) { - return memory_tracker.IsPlaced(inst); - })) { + if (!absl::c_any_of(instruction->users(), + [&memory_tracker](const HloInstruction* inst) { + return memory_tracker.IsPlaced(inst); + })) { return 0; } diff --git a/tensorflow/compiler/xla/service/hlo_sharding.cc b/tensorflow/compiler/xla/service/hlo_sharding.cc index 6c9c32f179..37cc146bd7 100644 --- a/tensorflow/compiler/xla/service/hlo_sharding.cc +++ b/tensorflow/compiler/xla/service/hlo_sharding.cc @@ -107,13 +107,12 @@ string HloSharding::ToString() const { bool HloSharding::UsesDevice(int64 device) const { if (IsTuple()) { - return std::any_of( - tuple_elements_.begin(), tuple_elements_.end(), - [&](const HloSharding& s) { return s.UsesDevice(device); }); + return absl::c_any_of(tuple_elements_, [&](const HloSharding& s) { + return s.UsesDevice(device); + }); } const auto& devices = tile_assignment_; - return replicated_ || - std::find(devices.begin(), devices.end(), device) != devices.end(); + return replicated_ || absl::c_linear_search(devices, device); } std::map HloSharding::UsedDevices(int64* count) const { diff --git a/tensorflow/compiler/xla/service/hlo_sharding.h b/tensorflow/compiler/xla/service/hlo_sharding.h index 9775505f86..5789ae0998 100644 --- a/tensorflow/compiler/xla/service/hlo_sharding.h +++ b/tensorflow/compiler/xla/service/hlo_sharding.h @@ -101,8 +101,8 @@ class HloSharding { if (!IsTuple()) { return replicated_; } - return std::all_of(tuple_elements_.begin(), tuple_elements_.end(), - [](const HloSharding& s) { return s.IsReplicated(); }); + return absl::c_all_of( + tuple_elements_, [](const HloSharding& s) { return s.IsReplicated(); }); } // Returns true if the tile size is the same as the input size. @@ -110,8 +110,9 @@ class HloSharding { if (!IsTuple()) { return maximal_; } - return std::all_of(tuple_elements_.begin(), tuple_elements_.end(), - [](const HloSharding& s) { return s.IsTileMaximal(); }); + return absl::c_all_of(tuple_elements_, [](const HloSharding& s) { + return s.IsTileMaximal(); + }); } // Returns true if the sharding defines an operation on the given device. diff --git a/tensorflow/compiler/xla/service/hlo_tfgraph_builder.cc b/tensorflow/compiler/xla/service/hlo_tfgraph_builder.cc index 4ea39a7628..c1f69db74e 100644 --- a/tensorflow/compiler/xla/service/hlo_tfgraph_builder.cc +++ b/tensorflow/compiler/xla/service/hlo_tfgraph_builder.cc @@ -61,8 +61,7 @@ void CleanNodeName(string* name) { name->erase(std::remove(name->begin(), name->end(), '%'), name->end()); const string chars_to_replace = "<>[]"; auto pred = [&](char c) { - return std::find(chars_to_replace.begin(), chars_to_replace.end(), c) != - chars_to_replace.end(); + return absl::c_linear_search(chars_to_replace, c); }; std::replace_if(name->begin(), name->end(), pred, '_'); } diff --git a/tensorflow/compiler/xla/service/hlo_value.cc b/tensorflow/compiler/xla/service/hlo_value.cc index d409df06be..218b33b2ac 100644 --- a/tensorflow/compiler/xla/service/hlo_value.cc +++ b/tensorflow/compiler/xla/service/hlo_value.cc @@ -209,7 +209,7 @@ std::ostream& operator<<(std::ostream& out, const HloValue& value) { } void HloValueSet::SortAndUniquifyValues() { - std::sort(values_.begin(), values_.end(), HloValue::IdLessThan); + absl::c_sort(values_, HloValue::IdLessThan); values_.erase(std::unique(values_.begin(), values_.end(), HloValue::IdEqual), values_.end()); } diff --git a/tensorflow/compiler/xla/service/human_readable_profile_builder.cc b/tensorflow/compiler/xla/service/human_readable_profile_builder.cc index 90904ac001..88fc62bd1e 100644 --- a/tensorflow/compiler/xla/service/human_readable_profile_builder.cc +++ b/tensorflow/compiler/xla/service/human_readable_profile_builder.cc @@ -128,9 +128,9 @@ string HumanReadableProfileBuilder::ToString() const { // Sort ops in decreasing order of cycles, and print them. std::vector sorted_ops(op_infos_); - std::sort( - sorted_ops.begin(), sorted_ops.end(), - [](const OpInfo& a, const OpInfo& b) { return a.cycles > b.cycles; }); + absl::c_sort(sorted_ops, [](const OpInfo& a, const OpInfo& b) { + return a.cycles > b.cycles; + }); for (const auto& op : sorted_ops) { print_op(op); } diff --git a/tensorflow/compiler/xla/service/instruction_fusion.cc b/tensorflow/compiler/xla/service/instruction_fusion.cc index f6f25c4413..b97060535d 100644 --- a/tensorflow/compiler/xla/service/instruction_fusion.cc +++ b/tensorflow/compiler/xla/service/instruction_fusion.cc @@ -178,19 +178,18 @@ bool InstructionFusion::EffectivelyAtMostUnary(HloInstruction* hlo) { output_rank = std::max(output_rank, ShapeUtil::TrueRank(subshape)); } }); - return std::count_if(hlo->operands().begin(), hlo->operands().end(), - [output_rank](HloInstruction* operand) { - if (operand->opcode() == HloOpcode::kBroadcast || - operand->opcode() == HloOpcode::kIota) { - return false; - } - if (operand->opcode() == HloOpcode::kConstant && - ShapeUtil::IsEffectiveScalar(operand->shape())) { - return false; - } - return ShapeUtil::TrueRank(operand->shape()) >= - output_rank; - }) <= 1; + return absl::c_count_if( + hlo->operands(), [output_rank](HloInstruction* operand) { + if (operand->opcode() == HloOpcode::kBroadcast || + operand->opcode() == HloOpcode::kIota) { + return false; + } + if (operand->opcode() == HloOpcode::kConstant && + ShapeUtil::IsEffectiveScalar(operand->shape())) { + return false; + } + return ShapeUtil::TrueRank(operand->shape()) >= output_rank; + }) <= 1; } bool InstructionFusion::CanFuseOnAllPaths( @@ -409,9 +408,8 @@ class ReversePostOrderFusionQueue : public FusionQueue { } sorted_operand_numbers.push_back(i); } - std::sort( - sorted_operand_numbers.begin(), sorted_operand_numbers.end(), - [&](int64 i, int64 j) { + absl::c_sort( + sorted_operand_numbers, [&](int64 i, int64 j) { // Instructions with higher priority in the queue come first. return ( FindOrDie(post_order_index_, instruction->mutable_operand(i)) > diff --git a/tensorflow/compiler/xla/service/layout_assignment.cc b/tensorflow/compiler/xla/service/layout_assignment.cc index 406cd0a219..10ff7bb6d4 100644 --- a/tensorflow/compiler/xla/service/layout_assignment.cc +++ b/tensorflow/compiler/xla/service/layout_assignment.cc @@ -147,12 +147,9 @@ bool LayoutConstraints::OperandBufferForwarded( PointsToSet::BufferSet* output_buffers = GetBufferSet(instruction); PointsToSet::BufferSet* operand_buffers = GetBufferSet(instruction->operand(operand_no)); - for (const LogicalBuffer* output_buffer : *output_buffers) { - if (operand_buffers->count(output_buffer) > 0) { - return true; - } - } - return false; + return absl::c_any_of(*output_buffers, [&](const LogicalBuffer* b) { + return operand_buffers->count(b) > 0; + }); } Status LayoutConstraints::SetBufferLayout(const Layout& layout, diff --git a/tensorflow/compiler/xla/service/llvm_ir/alias_analysis.cc b/tensorflow/compiler/xla/service/llvm_ir/alias_analysis.cc index 643ecd0fba..ce3d922ca7 100644 --- a/tensorflow/compiler/xla/service/llvm_ir/alias_analysis.cc +++ b/tensorflow/compiler/xla/service/llvm_ir/alias_analysis.cc @@ -81,9 +81,7 @@ void AliasAnalysis::AddAliasingInformationToIrArray(const HloInstruction& hlo, if (hlo.opcode() == HloOpcode::kParameter) { const std::vector& parameter_instructions = module_.entry_computation()->parameter_instructions(); - if (std::find(parameter_instructions.begin(), - parameter_instructions.end(), - &hlo) != parameter_instructions.end()) { + if (absl::c_linear_search(parameter_instructions, &hlo)) { array->MarkInvariantOverWholeProgram(context_); } } diff --git a/tensorflow/compiler/xla/service/name_uniquer.cc b/tensorflow/compiler/xla/service/name_uniquer.cc index daa718879d..f6feed2993 100644 --- a/tensorflow/compiler/xla/service/name_uniquer.cc +++ b/tensorflow/compiler/xla/service/name_uniquer.cc @@ -34,7 +34,7 @@ bool IsAllowed(char character) { } // namespace NameUniquer::NameUniquer(const string& separator) { - CHECK(std::all_of(separator.begin(), separator.end(), IsAllowed)) + CHECK(absl::c_all_of(separator, IsAllowed)) << "separator should comprises allowed characters only"; separator_ = separator; } diff --git a/tensorflow/compiler/xla/service/platform_util.cc b/tensorflow/compiler/xla/service/platform_util.cc index 896b73cda4..0491f641fc 100644 --- a/tensorflow/compiler/xla/service/platform_util.cc +++ b/tensorflow/compiler/xla/service/platform_util.cc @@ -260,8 +260,8 @@ PlatformUtil::GetStreamExecutors( // Block here in thread_pool destructor until all devices are initialized. } VLOG(1) << "Device initialization complete"; - if (std::all_of(stream_executors.begin(), stream_executors.end(), - [](se::StreamExecutor* s) { return s == nullptr; })) { + if (absl::c_all_of(stream_executors, + [](se::StreamExecutor* s) { return s == nullptr; })) { return InternalError("no supported devices found for platform %s", platform->Name()); } diff --git a/tensorflow/compiler/xla/service/shape_inference.cc b/tensorflow/compiler/xla/service/shape_inference.cc index 4a2dd09742..d711c8ef0d 100644 --- a/tensorflow/compiler/xla/service/shape_inference.cc +++ b/tensorflow/compiler/xla/service/shape_inference.cc @@ -534,9 +534,8 @@ Status ValidateDotDimensionNumbers( absl::Span contracting_dims, absl::Span batch_dims) -> bool { auto in_range = [&rank](int64 i) -> bool { return 0 <= i && i < rank; }; - return std::all_of(contracting_dims.begin(), contracting_dims.end(), - in_range) && - std::all_of(batch_dims.begin(), batch_dims.end(), in_range); + return absl::c_all_of(contracting_dims, in_range) && + absl::c_all_of(batch_dims, in_range); }; absl::Span lhs_contracting_dimensions = @@ -563,9 +562,8 @@ Status ValidateDotDimensionNumbers( auto is_unique = [&dim_set](int64 i) -> bool { return dim_set.insert(i).second; }; - return std::all_of(contracting_dims.begin(), contracting_dims.end(), - is_unique) && - std::all_of(batch_dims.begin(), batch_dims.end(), is_unique); + return absl::c_all_of(contracting_dims, is_unique) && + absl::c_all_of(batch_dims, is_unique); }; if (!dims_unique(lhs_contracting_dimensions, lhs_batch_dimensions) || @@ -1589,29 +1587,29 @@ ShapeInference::InferDegenerateDimensionBroadcastShape(HloOpcode operation, input_dnums[1] = dnums.input_feature_dimension(); std::copy(dnums.input_spatial_dimensions().begin(), dnums.input_spatial_dimensions().end(), input_dnums.begin() + 2); - std::sort(input_dnums.begin(), input_dnums.end()); + absl::c_sort(input_dnums); std::vector window_dnums(num_dims); window_dnums[0] = dnums.kernel_input_feature_dimension(); window_dnums[1] = dnums.kernel_output_feature_dimension(); std::copy(dnums.kernel_spatial_dimensions().begin(), dnums.kernel_spatial_dimensions().end(), window_dnums.begin() + 2); - std::sort(window_dnums.begin(), window_dnums.end()); + absl::c_sort(window_dnums); std::vector output_dnums(num_dims); output_dnums[0] = dnums.output_batch_dimension(); output_dnums[1] = dnums.output_feature_dimension(); std::copy(dnums.output_spatial_dimensions().begin(), dnums.output_spatial_dimensions().end(), output_dnums.begin() + 2); - std::sort(output_dnums.begin(), output_dnums.end()); + absl::c_sort(output_dnums); std::vector expected_dnums(num_dims); std::iota(expected_dnums.begin(), expected_dnums.end(), 0); const auto in_range = [num_dims](int64 i) { return 0 <= i && i < num_dims; }; - if (!std::all_of(input_dnums.begin(), input_dnums.end(), in_range) || - !std::all_of(window_dnums.begin(), window_dnums.end(), in_range) || - !std::all_of(output_dnums.begin(), output_dnums.end(), in_range)) { + if (!absl::c_all_of(input_dnums, in_range) || + !absl::c_all_of(window_dnums, in_range) || + !absl::c_all_of(output_dnums, in_range)) { return InvalidArgument( "A dimension number is out of range in convolution: %s.", dnums.DebugString()); diff --git a/tensorflow/compiler/xla/service/transpose_folding.cc b/tensorflow/compiler/xla/service/transpose_folding.cc index 15eb46bac0..a95ca2bf2a 100644 --- a/tensorflow/compiler/xla/service/transpose_folding.cc +++ b/tensorflow/compiler/xla/service/transpose_folding.cc @@ -130,8 +130,7 @@ bool FoldTransposeIntoConvolution(InstructionOperandsPair pair) { HloInstruction* new_lhs; const int64 kLhsIdx = 0; - if (std::find(operand_indices.begin(), operand_indices.end(), kLhsIdx) != - operand_indices.end()) { + if (absl::c_linear_search(operand_indices, kLhsIdx)) { HloInstruction& transpose = *convolution.mutable_operand(kLhsIdx); const auto& transpose_dimensions = transpose.dimensions(); HloInstruction& transpose_operand = *transpose.mutable_operand(0); @@ -154,8 +153,7 @@ bool FoldTransposeIntoConvolution(InstructionOperandsPair pair) { HloInstruction* new_rhs; const int64 kRhsIdx = 1; - if (std::find(operand_indices.begin(), operand_indices.end(), kRhsIdx) != - operand_indices.end()) { + if (absl::c_linear_search(operand_indices, kRhsIdx)) { HloInstruction& transpose = *convolution.mutable_operand(kRhsIdx); const auto& transpose_dimensions = transpose.dimensions(); HloInstruction& transpose_operand = *transpose.mutable_operand(0); diff --git a/tensorflow/compiler/xla/service/tuple_points_to_analysis.cc b/tensorflow/compiler/xla/service/tuple_points_to_analysis.cc index dec719b04a..5e505aaf02 100644 --- a/tensorflow/compiler/xla/service/tuple_points_to_analysis.cc +++ b/tensorflow/compiler/xla/service/tuple_points_to_analysis.cc @@ -87,9 +87,7 @@ bool PointsToSet::ContainsBuffer(const LogicalBuffer& buffer) const { bool found = false; ForEachElement([&found, &buffer](const ShapeIndex& /*index*/, const BufferList& pointed_to_buffers) { - if (!found && - std::find(pointed_to_buffers.begin(), pointed_to_buffers.end(), - &buffer) != pointed_to_buffers.end()) { + if (!found && absl::c_linear_search(pointed_to_buffers, &buffer)) { found = true; } }); @@ -99,8 +97,7 @@ bool PointsToSet::ContainsBuffer(const LogicalBuffer& buffer) const { bool PointsToSet::ContainsBufferAtIndex(const LogicalBuffer& buffer, const ShapeIndex& index) const { const auto& pointed_to_buffers = element(index); - return std::find(pointed_to_buffers.begin(), pointed_to_buffers.end(), - &buffer) != pointed_to_buffers.end(); + return absl::c_linear_search(pointed_to_buffers, &buffer); } void PointsToSet::AddPointedToBuffer(const LogicalBuffer& buffer, @@ -604,9 +601,8 @@ bool TuplePointsToAnalysis::DoesNotUseOperandBuffer( } else if (user->opcode() == HloOpcode::kFusion && user->fusion_kind() == HloInstruction::FusionKind::kLoop) { // Find fusion parameter associated with 'operand'. - auto it = std::find_if( - user->fused_parameters().begin(), user->fused_parameters().end(), - [=](HloInstruction* fused_param) { + auto it = absl::c_find_if( + user->fused_parameters(), [&](HloInstruction* fused_param) { return user->operand(fused_param->parameter_number()) == operand; }); CHECK(it != user->fused_parameters().end()); @@ -672,9 +668,8 @@ bool TuplePointsToAnalysis::HasUniqueFusedUseOfOperandAt( } // Find fusion parameter associated with 'operand'. const auto& fused_params = fusion->fused_parameters(); - auto fused_param_it = std::find_if( - fused_params.begin(), fused_params.end(), - [&](HloInstruction* fused_param) { + auto fused_param_it = + absl::c_find_if(fused_params, [&](HloInstruction* fused_param) { return fusion->operand(fused_param->parameter_number()) == operand; }); if (fused_param_it == fused_params.end()) { @@ -743,11 +738,10 @@ bool TuplePointsToAnalysis::CanShareOperandBufferWithUser( // Check if one operand of kAdd fused root is kDot or kConvolution. auto* add = user->fused_expression_root(); auto add_operand_it = - std::find_if(add->operands().begin(), add->operands().end(), - [&](HloInstruction* operand) { - return operand->opcode() == HloOpcode::kConvolution || - operand->opcode() == HloOpcode::kDot; - }); + absl::c_find_if(add->operands(), [&](HloInstruction* operand) { + return operand->opcode() == HloOpcode::kConvolution || + operand->opcode() == HloOpcode::kDot; + }); if (add_operand_it == add->operands().end()) { return false; } diff --git a/tensorflow/compiler/xla/service/tuple_points_to_analysis_test.cc b/tensorflow/compiler/xla/service/tuple_points_to_analysis_test.cc index d8875ca747..7599e1e6ad 100644 --- a/tensorflow/compiler/xla/service/tuple_points_to_analysis_test.cc +++ b/tensorflow/compiler/xla/service/tuple_points_to_analysis_test.cc @@ -721,9 +721,8 @@ class FusionPointsToAnalysisTest : public TuplePointsToAnalysisTest { // to fusion 'operand'. HloInstruction* GetFusionParameterForOperand(HloInstruction* fusion, HloInstruction* operand) { - auto it = std::find_if( - fusion->fused_instructions().begin(), - fusion->fused_instructions().end(), [=](const HloInstruction* fused) { + auto it = absl::c_find_if( + fusion->fused_instructions(), [&](const HloInstruction* fused) { return fused->opcode() == HloOpcode::kParameter && fusion->operand(fused->parameter_number()) == operand; }); diff --git a/tensorflow/compiler/xla/service/while_loop_simplifier.cc b/tensorflow/compiler/xla/service/while_loop_simplifier.cc index de884b037d..09d5409571 100644 --- a/tensorflow/compiler/xla/service/while_loop_simplifier.cc +++ b/tensorflow/compiler/xla/service/while_loop_simplifier.cc @@ -109,8 +109,7 @@ static StatusOr TryRemoveDeadWhileParams(HloInstruction* while_op) { // operand appears in, but it may appear more than once! if (user->user_count() == 1 && user->users().front() == while_body_root && while_body_root->operand_index(user) == user->tuple_index() && - std::count(while_body_root->operands().begin(), - while_body_root->operands().end(), user) == 1) { + absl::c_count(while_body_root->operands(), user) == 1) { continue; } @@ -158,7 +157,7 @@ static StatusOr TryRemoveDeadWhileParams(HloInstruction* while_op) { // Build up maps from the old/new to the new/old tuple indices. std::vector new_to_old_tuple_idx(used_tuple_indices.begin(), used_tuple_indices.end()); - std::sort(new_to_old_tuple_idx.begin(), new_to_old_tuple_idx.end()); + absl::c_sort(new_to_old_tuple_idx); absl::flat_hash_map old_to_new_tuple_idx; for (int64 new_idx = 0; new_idx < new_to_old_tuple_idx.size(); ++new_idx) { diff --git a/tensorflow/compiler/xla/service/while_loop_simplifier_test.cc b/tensorflow/compiler/xla/service/while_loop_simplifier_test.cc index 3713989ca2..ecca76b1e8 100644 --- a/tensorflow/compiler/xla/service/while_loop_simplifier_test.cc +++ b/tensorflow/compiler/xla/service/while_loop_simplifier_test.cc @@ -407,13 +407,12 @@ TEST_F(WhileLoopSimplifierTest, RemoveUnusedLoopOperands) { // The original while instruction is still left in the module as a dead // instruction, find a while instruction with a different name as the new // while instruction. + const auto& instrs = m->entry_computation()->instructions(); HloInstruction* new_while_op = - *std::find_if(m->entry_computation()->instructions().begin(), - m->entry_computation()->instructions().end(), - [&](const HloInstruction* instr) { - return (instr->opcode() == HloOpcode::kWhile && - instr->name() != "while"); - }); + *absl::c_find_if(instrs, [&](const HloInstruction* instr) { + return (instr->opcode() == HloOpcode::kWhile && + instr->name() != "while"); + }); auto scalar_s32 = ShapeUtil::MakeShape(S32, {}); EXPECT_TRUE( diff --git a/tensorflow/compiler/xla/shape.cc b/tensorflow/compiler/xla/shape.cc index f05d816268..ea4f722ef4 100644 --- a/tensorflow/compiler/xla/shape.cc +++ b/tensorflow/compiler/xla/shape.cc @@ -87,8 +87,7 @@ bool Shape::is_static() const { } } } - return !std::any_of(dynamic_dimensions_.begin(), dynamic_dimensions_.end(), - [](bool b) { return b; }); + return !absl::c_any_of(dynamic_dimensions_, [](bool b) { return b; }); } void Shape::DeleteDimension(int64 dim_to_delete) { diff --git a/tensorflow/compiler/xla/shape_util.cc b/tensorflow/compiler/xla/shape_util.cc index 7f68c0ae1f..4726ee6a5b 100644 --- a/tensorflow/compiler/xla/shape_util.cc +++ b/tensorflow/compiler/xla/shape_util.cc @@ -405,8 +405,7 @@ ShapeUtil::MakeShapeWithDescendingLayoutAndSamePhysicalLayout( } /* static */ bool ShapeUtil::IsNestedTuple(const Shape& shape) { - return IsTuple(shape) && std::any_of(shape.tuple_shapes().begin(), - shape.tuple_shapes().end(), IsTuple); + return IsTuple(shape) && absl::c_any_of(shape.tuple_shapes(), IsTuple); } /* static */ bool ShapeUtil::IsEmptyTuple(const Shape& shape) { diff --git a/tensorflow/compiler/xla/sparse_index_array.h b/tensorflow/compiler/xla/sparse_index_array.h index a96d483462..0c25355467 100644 --- a/tensorflow/compiler/xla/sparse_index_array.h +++ b/tensorflow/compiler/xla/sparse_index_array.h @@ -135,7 +135,7 @@ void SparseIndexArray::SortWithValues(absl::Span values) { auto sort_order_less = [this](int64 lhs, int64 rhs) { return IndexUtil::CompareIndices(At(lhs), At(rhs)) < 0; }; - std::sort(sort_order.begin(), sort_order.end(), sort_order_less); + absl::c_sort(sort_order, sort_order_less); // Reorder the array elements according to sort_order. Work through the array // and follow cycles so we can do the reorder in-place. diff --git a/tensorflow/compiler/xla/tests/convolution_test.cc b/tensorflow/compiler/xla/tests/convolution_test.cc index 2496938912..9db9f2563b 100644 --- a/tensorflow/compiler/xla/tests/convolution_test.cc +++ b/tensorflow/compiler/xla/tests/convolution_test.cc @@ -467,8 +467,8 @@ XLA_TEST_F(ConvolutionTest, Convolve3D_1x4x2x3x3_2x2x2x3x3_Valid) { // servers. The error message is missing the operator ++. template void iota_int_init_value(std::vector& values, int init_value) { - std::for_each(values.begin(), values.end(), - [&](T& value) { value = static_cast(init_value++); }); + absl::c_for_each(values, + [&](T& value) { value = static_cast(init_value++); }); } template diff --git a/tensorflow/compiler/xla/util.cc b/tensorflow/compiler/xla/util.cc index 68cab7387c..34b73b5206 100644 --- a/tensorflow/compiler/xla/util.cc +++ b/tensorflow/compiler/xla/util.cc @@ -86,7 +86,7 @@ bool IsPermutation(absl::Span permutation, int64 rank) { CHECK_LT(index, rank); output[index] = 0; } - return std::find(output.begin(), output.end(), -1) == output.end(); + return !absl::c_linear_search(output, -1); } std::vector InversePermutation( diff --git a/tensorflow/compiler/xla/util.h b/tensorflow/compiler/xla/util.h index 6722641e9d..f2fd17dc99 100644 --- a/tensorflow/compiler/xla/util.h +++ b/tensorflow/compiler/xla/util.h @@ -324,8 +324,7 @@ bool IsIdentityPermutation(absl::Span permutation); template int64 PositionInContainer(const Container& container, int64 value) { - return std::distance(container.begin(), - std::find(container.begin(), container.end(), value)); + return std::distance(container.begin(), absl::c_find(container, value)); } // Formats the container as a comma-separated string. StrAppend must support diff --git a/tensorflow/compiler/xla/window_util.cc b/tensorflow/compiler/xla/window_util.cc index 51c73b3d17..e001cc35f9 100644 --- a/tensorflow/compiler/xla/window_util.cc +++ b/tensorflow/compiler/xla/window_util.cc @@ -17,6 +17,7 @@ limitations under the License. #include +#include "absl/algorithm/container.h" #include "absl/strings/str_cat.h" #include "tensorflow/compiler/xla/types.h" #include "tensorflow/compiler/xla/xla_data.pb.h" @@ -137,25 +138,23 @@ bool HasPadding(const Window& window) { } bool HasSymmetricPadding(const Window& window) { - return std::all_of(window.dimensions().begin(), window.dimensions().end(), - [](const WindowDimension& dim) { - return dim.padding_low() == dim.padding_high(); - }); + return absl::c_all_of(window.dimensions(), [](const WindowDimension& dim) { + return dim.padding_low() == dim.padding_high(); + }); } bool HasSymmetricPadding(const PaddingConfig& padding_config) { - return std::all_of(padding_config.dimensions().begin(), - padding_config.dimensions().end(), - [](const PaddingConfig::PaddingConfigDimension& dim) { - return dim.edge_padding_low() == dim.edge_padding_high(); - }); + return absl::c_all_of(padding_config.dimensions(), + [](const PaddingConfig::PaddingConfigDimension& dim) { + return dim.edge_padding_low() == + dim.edge_padding_high(); + }); } bool HasNegativePadding(const Window& window) { - return std::any_of(window.dimensions().begin(), window.dimensions().end(), - [](const WindowDimension& dim) { - return dim.padding_low() < 0 || dim.padding_high() < 0; - }); + return absl::c_any_of(window.dimensions(), [](const WindowDimension& dim) { + return dim.padding_low() < 0 || dim.padding_high() < 0; + }); } bool HasBaseDilation(const Window& window) { @@ -190,10 +189,9 @@ bool AllOrNoneReversed(const Window& window) { return true; } bool reversed = window.dimensions()[0].window_reversal(); - return std::all_of(window.dimensions().begin(), window.dimensions().end(), - [&](const WindowDimension& dim) { - return dim.window_reversal() == reversed; - }); + return absl::c_all_of(window.dimensions(), [&](const WindowDimension& dim) { + return dim.window_reversal() == reversed; + }); } bool HasDilation(const Window& window) { -- GitLab From e2071f6a0502a5bc81abd0cb533f3b52ba0e7855 Mon Sep 17 00:00:00 2001 From: Pavithra Vijay Date: Fri, 4 Jan 2019 12:59:45 -0800 Subject: [PATCH 359/622] Add poisson v2 loss and metric. PiperOrigin-RevId: 227900433 --- tensorflow/python/keras/losses.py | 47 +++++++++----- tensorflow/python/keras/losses_test.py | 81 +++++++++++++++++++++++++ tensorflow/python/keras/metrics.py | 31 ++++++++++ tensorflow/python/keras/metrics_test.py | 46 ++++++++++++++ 4 files changed, 189 insertions(+), 16 deletions(-) diff --git a/tensorflow/python/keras/losses.py b/tensorflow/python/keras/losses.py index 1ff564054b..46e729c197 100644 --- a/tensorflow/python/keras/losses.py +++ b/tensorflow/python/keras/losses.py @@ -519,25 +519,39 @@ class LogLoss(Loss): model = keras.models.Model(inputs, outputs) model.compile('sgd', loss=tf.losses.LogLoss()) ``` - - Args: - epsilon: A small increment to add to avoid taking a log of zero. - reduction: Type of `tf.losses.Reduction` to apply to loss. Default value is - `SUM_OVER_BATCH_SIZE`. - name: Optional name for the op. """ - def __init__(self, - epsilon=1e-7, - reduction=losses_impl.ReductionV2.SUM_OVER_BATCH_SIZE, - name=None): - super(LogLoss, self).__init__(reduction=reduction, name=name) - self.epsilon = epsilon + def call(self, y_true, y_pred): + y_pred = ops.convert_to_tensor(y_pred) + y_true = math_ops.cast(y_true, y_pred.dtype) + return logloss(y_true, y_pred) + + +class Poisson(Loss): + """Computes the poisson loss between `y_true` and `y_pred`. + + loss = y_pred - y_true * log(y_pred) + + Usage: + + ```python + p = tf.losses.Poisson() + loss = p([1, 9, 2], [4, 8, 12]) + print('Loss: ', loss.numpy()) # Loss: -4.63 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile('sgd', loss=tf.losses.Poisson()) + ``` + """ def call(self, y_true, y_pred): y_pred = ops.convert_to_tensor(y_pred) y_true = math_ops.cast(y_true, y_pred.dtype) - return logloss(y_true, y_pred, epsilon=self.epsilon) + return poisson(y_true, y_pred) class Logcosh(Loss): @@ -629,9 +643,10 @@ def categorical_hinge(y_true, y_pred): return math_ops.maximum(0., neg - pos + 1.) -def logloss(y_true, y_pred, epsilon=1e-7): - losses = math_ops.multiply(y_true, math_ops.log(y_pred + epsilon)) - losses += math_ops.multiply((1 - y_true), math_ops.log(1 - y_pred + epsilon)) +def logloss(y_true, y_pred): + losses = math_ops.multiply(y_true, math_ops.log(y_pred + K.epsilon())) + losses += math_ops.multiply((1 - y_true), + math_ops.log(1 - y_pred + K.epsilon())) return K.mean(-losses, axis=-1) diff --git a/tensorflow/python/keras/losses_test.py b/tensorflow/python/keras/losses_test.py index aa00ba5a91..f34a6caa18 100644 --- a/tensorflow/python/keras/losses_test.py +++ b/tensorflow/python/keras/losses_test.py @@ -1175,5 +1175,86 @@ class LogcoshTest(test.TestCase): self.assertAlmostEqual(self.evaluate(loss), 0., 3) +@test_util.run_all_in_graph_and_eager_modes +class PoissonTest(test.TestCase): + + def setup(self): + self.np_y_pred = np.asarray([1, 9, 2, 5, 2, 6]).reshape((2, 3)) + self.np_y_true = np.asarray([4, 8, 12, 8, 1, 3]).reshape((2, 3)) + + self.batch_size = 6 + self.expected_losses = self.np_y_pred - np.multiply(self.np_y_true, + np.log(self.np_y_pred)) + + self.y_pred = constant_op.constant(self.np_y_pred, dtype=dtypes.float32) + self.y_true = constant_op.constant(self.np_y_true) + + def test_config(self): + poisson_obj = keras.losses.Poisson( + reduction=losses_impl.ReductionV2.SUM, name='poisson') + self.assertEqual(poisson_obj.name, 'poisson') + self.assertEqual(poisson_obj.reduction, losses_impl.ReductionV2.SUM) + + def test_unweighted(self): + self.setup() + poisson_obj = keras.losses.Poisson() + + loss = poisson_obj(self.y_true, self.y_pred) + expected_loss = np.sum(self.expected_losses) / self.batch_size + self.assertAlmostEqual(self.evaluate(loss), expected_loss, 3) + + def test_scalar_weighted(self): + self.setup() + poisson_obj = keras.losses.Poisson() + sample_weight = 2.3 + loss = poisson_obj(self.y_true, self.y_pred, sample_weight=sample_weight) + + expected_loss = sample_weight * np.sum( + self.expected_losses) / self.batch_size + self.assertAlmostEqual(self.evaluate(loss), expected_loss, 3) + self.assertAlmostEqual(self.evaluate(loss), expected_loss, 3) + + # Verify we get the same output when the same input is given + loss_2 = poisson_obj(self.y_true, self.y_pred, sample_weight=sample_weight) + self.assertAlmostEqual(self.evaluate(loss), self.evaluate(loss_2), 3) + + def test_sample_weighted(self): + self.setup() + poisson_obj = keras.losses.Poisson() + + sample_weight = constant_op.constant([1.2, 3.4], shape=(2, 1)) + loss = poisson_obj(self.y_true, self.y_pred, sample_weight=sample_weight) + + expected_loss = np.multiply( + self.expected_losses, + np.asarray([1.2, 1.2, 1.2, 3.4, 3.4, 3.4]).reshape((2, 3))) + expected_loss = np.sum(expected_loss) / self.batch_size + self.assertAlmostEqual(self.evaluate(loss), expected_loss, 3) + + def test_timestep_weighted(self): + self.setup() + poisson_obj = keras.losses.Poisson() + y_true = self.np_y_true.reshape(2, 3, 1) + y_pred = self.np_y_pred.reshape(2, 3, 1) + sample_weight = np.asarray([3, 6, 5, 0, 4, 2]).reshape(2, 3, 1) + expected_losses = y_pred - np.multiply(y_true, np.log(y_pred)) + + y_pred = constant_op.constant(y_pred, dtype=dtypes.float32) + y_true = constant_op.constant(y_true) + + loss = poisson_obj( + y_true, + y_pred, + sample_weight=constant_op.constant(sample_weight, shape=(2, 3))) + expected_loss = np.sum(expected_losses * sample_weight) / self.batch_size + self.assertAlmostEqual(self.evaluate(loss), expected_loss, 3) + + def test_zero_weighted(self): + self.setup() + poisson_obj = keras.losses.Poisson() + loss = poisson_obj(self.y_true, self.y_pred, sample_weight=0) + self.assertAlmostEqual(self.evaluate(loss), 0., 3) + + if __name__ == '__main__': test.main() diff --git a/tensorflow/python/keras/metrics.py b/tensorflow/python/keras/metrics.py index 2de0bc474c..71568e3fc5 100644 --- a/tensorflow/python/keras/metrics.py +++ b/tensorflow/python/keras/metrics.py @@ -1685,6 +1685,37 @@ class Logcosh(MeanMetricWrapper): return super(Logcosh, cls).from_config(config) +class Poisson(MeanMetricWrapper): + """Computes the poisson metric between `y_true` and `y_pred`. + + metric = y_pred - y_true * log(y_pred) + + Usage: + + ```python + m = tf.keras.metrics.Poisson() + m.update_state([1, 9, 2], [4, 8, 12]) + print('Final result: ', m.result().numpy()) # Final result: -4.63 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile('sgd', metrics=[tf.keras.metrics.Poisson()]) + ``` + """ + + def __init__(self, name='poisson', dtype=None): + super(Poisson, self).__init__(poisson, name, dtype=dtype) + + @classmethod + def from_config(cls, config): + if 'fn' in config: + config.pop('fn') + return super(Poisson, cls).from_config(config) + + def accuracy(y_true, y_pred): y_pred.get_shape().assert_is_compatible_with(y_true.get_shape()) if y_true.dtype != y_pred.dtype: diff --git a/tensorflow/python/keras/metrics_test.py b/tensorflow/python/keras/metrics_test.py index a9d88789ed..88b763d569 100644 --- a/tensorflow/python/keras/metrics_test.py +++ b/tensorflow/python/keras/metrics_test.py @@ -1510,6 +1510,52 @@ class LogcoshTest(test.TestCase): self.assertAllClose(self.evaluate(result), expected_result, atol=1e-3) +@test_util.run_all_in_graph_and_eager_modes +class PoissonTest(test.TestCase): + + def setup(self): + y_pred = np.asarray([1, 9, 2, 5, 2, 6]).reshape((2, 3)) + y_true = np.asarray([4, 8, 12, 8, 1, 3]).reshape((2, 3)) + + self.batch_size = 6 + self.expected_results = y_pred - np.multiply(y_true, np.log(y_pred)) + + self.y_pred = constant_op.constant(y_pred, dtype=dtypes.float32) + self.y_true = constant_op.constant(y_true) + + def test_config(self): + poisson_obj = metrics.Poisson(name='poisson', dtype=dtypes.int32) + self.assertEqual(poisson_obj.name, 'poisson') + self.assertEqual(poisson_obj._dtype, dtypes.int32) + + poisson_obj2 = metrics.Poisson.from_config(poisson_obj.get_config()) + self.assertEqual(poisson_obj2.name, 'poisson') + self.assertEqual(poisson_obj2._dtype, dtypes.int32) + + def test_unweighted(self): + self.setup() + poisson_obj = metrics.Poisson() + self.evaluate(variables.variables_initializer(poisson_obj.variables)) + + update_op = poisson_obj.update_state(self.y_true, self.y_pred) + self.evaluate(update_op) + result = poisson_obj.result() + expected_result = np.sum(self.expected_results) / self.batch_size + self.assertAllClose(result, expected_result, atol=1e-3) + + def test_weighted(self): + self.setup() + poisson_obj = metrics.Poisson() + self.evaluate(variables.variables_initializer(poisson_obj.variables)) + sample_weight = constant_op.constant([1.2, 3.4], shape=(2, 1)) + + result = poisson_obj(self.y_true, self.y_pred, sample_weight=sample_weight) + sample_weight = np.asarray([1.2, 1.2, 1.2, 3.4, 3.4, 3.4]).reshape((2, 3)) + expected_result = np.multiply(self.expected_results, sample_weight) + expected_result = np.sum(expected_result) / np.sum(sample_weight) + self.assertAllClose(self.evaluate(result), expected_result, atol=1e-3) + + def _get_model(compile_metrics): model_layers = [ layers.Dense(3, activation='relu', kernel_initializer='ones'), -- GitLab From d198ec5f99258f443af8e16825ae9c77e26c5db6 Mon Sep 17 00:00:00 2001 From: Yuefeng Zhou Date: Fri, 4 Jan 2019 13:11:46 -0800 Subject: [PATCH 360/622] Move ParameterServerStrategy to core. PiperOrigin-RevId: 227902368 --- tensorflow/contrib/distribute/python/BUILD | 15 +- .../python/parameter_server_strategy.py | 467 +--------------- .../python/parameter_server_strategy_test.py | 339 +++++++---- tensorflow/python/distribute/BUILD | 22 + .../distribute/parameter_server_strategy.py | 528 ++++++++++++++++++ 5 files changed, 813 insertions(+), 558 deletions(-) create mode 100644 tensorflow/python/distribute/parameter_server_strategy.py diff --git a/tensorflow/contrib/distribute/python/BUILD b/tensorflow/contrib/distribute/python/BUILD index 78ad3214fb..de6b6f1f84 100644 --- a/tensorflow/contrib/distribute/python/BUILD +++ b/tensorflow/contrib/distribute/python/BUILD @@ -80,19 +80,10 @@ py_library( srcs = ["parameter_server_strategy.py"], visibility = ["//tensorflow:internal"], deps = [ - ":mirrored_strategy", - "//tensorflow/core:protos_all_py", - "//tensorflow/python:array_ops", - "//tensorflow/python:framework_ops", - "//tensorflow/python:resource_variable_ops", - "//tensorflow/python:training", - "//tensorflow/python:util", - "//tensorflow/python/distribute:cross_device_ops", - "//tensorflow/python/distribute:input_lib", - "//tensorflow/python/distribute:multi_worker_util", - "//tensorflow/python/distribute:reduce_util", + "//tensorflow/python/distribute:distribute_lib", + "//tensorflow/python/distribute:parameter_server_strategy", "//tensorflow/python/distribute:values", - "//tensorflow/python/eager:context", + "//tensorflow/python/distribute/cluster_resolver:cluster_resolver_lib", ], ) diff --git a/tensorflow/contrib/distribute/python/parameter_server_strategy.py b/tensorflow/contrib/distribute/python/parameter_server_strategy.py index 461e1bca21..bb0b8eb992 100644 --- a/tensorflow/contrib/distribute/python/parameter_server_strategy.py +++ b/tensorflow/contrib/distribute/python/parameter_server_strategy.py @@ -18,35 +18,24 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -import copy - -from tensorflow.contrib.distribute.python import mirrored_strategy -from tensorflow.python.distribute import cross_device_ops as cross_device_ops_lib -from tensorflow.python.distribute import device_util from tensorflow.python.distribute import distribute_lib from tensorflow.python.distribute import input_lib -from tensorflow.python.distribute import multi_worker_util -from tensorflow.python.distribute import values -from tensorflow.python.eager import context -from tensorflow.python.framework import device as tf_device -from tensorflow.python.framework import ops -from tensorflow.python.ops import array_ops -from tensorflow.python.ops import resource_variable_ops -from tensorflow.python.ops import variable_scope as vs -from tensorflow.python.platform import tf_logging as logging -from tensorflow.python.training import device_setter -from tensorflow.python.util import nest +from tensorflow.python.distribute import parameter_server_strategy +from tensorflow.python.distribute.cluster_resolver import SimpleClusterResolver +from tensorflow.python.distribute.cluster_resolver import TFConfigClusterResolver + +# pylint: disable=protected-access,invalid-name,line-too-long +CoreParameterServerStrategy = parameter_server_strategy.ParameterServerStrategy +CoreParameterServerExtended = parameter_server_strategy.ParameterServerStrategyExtended -_LOCAL_CPU = "/device:CPU:0" -_LOCAL_GPU_0 = "/device:GPU:0" +# pylint: enable=protected-access,invalid-name,line-too-long -# TODO(yuefengz): maybe cache variables on local CPU. -# TODO(yuefengz): we may want to set session options to disallow communication -# between workers. class ParameterServerStrategy(distribute_lib.DistributionStrategy): """A parameter server DistributionStrategy. + *** contrib version *** + This strategy class works for both local training and between-graph replicated training for multiple workers. If `cluster_spec` is specified, either passed in to __init__() method or parsed from the @@ -101,434 +90,24 @@ class ParameterServerStrategy(distribute_lib.DistributionStrategy): ParameterServerExtended(self, num_gpus_per_worker)) -class ParameterServerExtended(distribute_lib.DistributionStrategyExtended): +class ParameterServerExtended(CoreParameterServerExtended): """Implementation of ParameterServerStrategy.""" def __init__(self, container_strategy, num_gpus_per_worker): - super(ParameterServerExtended, self).__init__(container_strategy) - self._num_gpus_per_worker = num_gpus_per_worker - self._initialize_local(num_gpus_per_worker) - - # We typically don't need to do all-reduce in this strategy. - self._cross_device_ops = ( - cross_device_ops_lib.ReductionToOneDeviceCrossDeviceOps( - reduce_to_device=_LOCAL_CPU)) - - def _initialize_multi_worker(self, num_gpus_per_worker, cluster_spec, - task_type, task_id): - """Initialize devices for multiple workers. - - It creates variable devices and compute devices. Variables and operations - will be assigned to them respectively. We have one compute device per - replica. The variable device is a device function or device string. The - default variable device assigns variables to parameter servers in a - round-robin fashion. - - Args: - num_gpus_per_worker: number of local GPUs or GPUs per worker. - cluster_spec: a dict, ClusterDef or ClusterSpec object specifying the - cluster configurations. - task_type: the current task type. - task_id: the current task id. - - Raises: - ValueError: if the cluster_spec doesn't have ps jobs. - """ - assert cluster_spec - if not task_type or task_id is None: - raise ValueError("When `cluster_spec` is given, you must also specify " - "`task_type` and `task_id`") - cluster_spec = multi_worker_util.normalize_cluster_spec(cluster_spec) - - worker_device = "/job:%s/task:%d" % (self._task_type, self._task_id) - - # Define compute devices which is a list of device strings and one for each - # replica. When there are GPUs, replicate operations on these GPUs. - # Otherwise, place operations on CPU. - if num_gpus_per_worker > 0: - compute_devices = tuple( - "%s/device:GPU:%d" % (worker_device, i) - for i in range(num_gpus_per_worker) - ) - else: - compute_devices = (worker_device,) - - self._device_map = values.ReplicaDeviceMap(compute_devices) - self._input_workers = input_lib.InputWorkers( - self._device_map, [(worker_device, compute_devices)]) - - # In distributed mode, place variables on ps jobs in a round-robin fashion. - # Note that devices returned from `replica_device_setter` are not - # canonical and therefore we don't canonicalize all variable devices to - # make them consistent. - # TODO(yuefengz): support passing a strategy object to control variable - # assignment. - # TODO(yuefengz): merge the logic of replica_device_setter into this - # class. - num_ps_replicas = len(cluster_spec.as_dict().get("ps", [])) - if num_ps_replicas == 0: - raise ValueError("The cluster spec needs to have `ps` jobs.") - self._variable_device = device_setter.replica_device_setter( - ps_tasks=num_ps_replicas, - worker_device=worker_device, - merge_devices=True, - cluster=cluster_spec) - - # The `_parameter_devices` is needed for the `parameter_devices` property - # and is a list of all variable devices. Here parameter devices are all - # tasks of the "ps" job. - self._parameter_devices = tuple(map("/job:ps/task:{}".format, - range(num_ps_replicas))) - - # Add a default device so that ops without specified devices will not end up - # on other workers. - self._default_device = worker_device - - self._is_chief = multi_worker_util.is_chief(cluster_spec, task_type, - task_id) - self._cluster_spec = cluster_spec - self._task_type = task_type - self._task_id = task_id - - logging.info( - "Multi-worker ParameterServerStrategy with " - "cluster_spec = %r, task_type = %r, task_id = %r, " - "num_ps_replicas = %r, is_chief = %r, device_map = %r, " - "variable_device = %r", cluster_spec.as_dict(), task_type, task_id, - num_ps_replicas, self._is_chief, self._device_map, - self._variable_device) - - def _initialize_local(self, num_gpus_per_worker): - """Initialize internal devices for local training.""" - worker_device = device_util.canonicalize("/device:CPU:0") - # Define compute devices which is a list of device strings and one for each - # replica. When there are GPUs, replicate operations on these GPUs. - # Otherwise, place operations on CPU. - if num_gpus_per_worker > 0: - compute_devices = tuple( - map("/device:GPU:{}".format, range(num_gpus_per_worker))) - else: - compute_devices = (_LOCAL_CPU,) - - self._device_map = values.ReplicaDeviceMap(compute_devices) - self._input_workers = input_lib.InputWorkers( - self._device_map, [(worker_device, compute_devices)]) - - # If there is only one GPU, put everything on that GPU. Otherwise, place - # variables on CPU. - if num_gpus_per_worker == 1: - assert len(compute_devices) == 1 - self._variable_device = _LOCAL_GPU_0 - self._parameter_devices = (_LOCAL_GPU_0,) - else: - self._variable_device = _LOCAL_CPU - self._parameter_devices = (_LOCAL_CPU,) - - self._is_chief = True - self._cluster_spec = None - self._task_type = None - self._task_id = None - - logging.info( - "ParameterServerStrategy with compute_devices = %r, " - "variable_device = %r", compute_devices, self._variable_device) - - def _validate_colocate_with_variable(self, colocate_with_variable): - values.validate_colocate(colocate_with_variable, self) - - def _distribute_dataset(self, dataset_fn): - """Distributes the dataset to each local GPU.""" - return input_lib.PerReplicaDataset( - self._call_dataset_fn(dataset_fn), self._input_workers, 0, - prefetch_on_device=True) + # Use TFConfigClusterResolver to parse TF_CONFIG. We don't want to change + # the constructor's interface to allow customized cluster resolver. Use + # SimpleClusterResolver to override num_accelerators. + tfconfig = TFConfigClusterResolver() + cluster_resolver = SimpleClusterResolver( + cluster_spec=tfconfig.cluster_spec(), + task_type=tfconfig.task_type, + task_index=tfconfig.task_index, + num_accelerators=num_gpus_per_worker) + super(ParameterServerExtended, self).__init__( + container_strategy, cluster_resolver=cluster_resolver) def _make_dataset_iterator(self, dataset): - return input_lib.DatasetIterator(dataset, self._input_workers, - self._num_replicas_in_sync) - - def _make_input_fn_iterator( - self, - input_fn, - replication_mode=distribute_lib.InputReplicationMode.PER_WORKER): - """Distributes the dataset to each local GPU.""" - if self._cluster_spec: - input_pipeline_id = multi_worker_util.id_in_cluster( - self._cluster_spec, self._task_type, self._task_id) - num_input_pipelines = multi_worker_util.worker_count( - self._cluster_spec, self._task_type) - else: - input_pipeline_id = 0 - num_input_pipelines = 1 - input_context = distribute_lib.InputContext( - num_input_pipelines=num_input_pipelines, - input_pipeline_id=input_pipeline_id, - num_replicas_in_sync=self._num_replicas_in_sync) - return input_lib.InputFunctionIterator( - input_fn, self._input_workers, [input_context]) - - def _broadcast_to(self, tensor, destinations): - # This is both a fast path for Python constants, and a way to delay - # converting Python values to a tensor until we know what type it - # should be converted to. Otherwise we have trouble with: - # global_step.assign_add(1) - # since the `1` gets broadcast as an int32 but global_step is int64. - if isinstance(tensor, (float, int)): - return tensor - if not cross_device_ops_lib.check_destinations(destinations): - # TODO(josh11b): Use current logical device instead of 0 here. - destinations = values.LogicalDeviceSpec( - device_map=self._device_map, logical_device=0) - return self._cross_device_ops.broadcast(tensor, destinations) - - def _allow_variable_partition(self): - return not context.executing_eagerly() - - # TODO(yuefengz): not all ops in device_setter.STANDARD_PS_OPS will go through - # this creator, such as "MutableHashTable". - def _create_variable(self, next_creator, *args, **kwargs): - if self._num_replicas_in_sync > 1: - aggregation = kwargs.pop("aggregation", vs.VariableAggregation.NONE) - if aggregation not in ( - vs.VariableAggregation.NONE, - vs.VariableAggregation.SUM, - vs.VariableAggregation.MEAN, - vs.VariableAggregation.ONLY_FIRST_REPLICA - ): - raise ValueError("Invalid variable aggregation mode: " + aggregation + - " for variable: " + kwargs["name"]) - - def var_creator(*args, **kwargs): - """Create an AggregatingVariable and fix up collections.""" - # Record what collections this variable should be added to. - collections = kwargs.pop("collections", None) - if collections is None: - collections = [ops.GraphKeys.GLOBAL_VARIABLES] - kwargs["collections"] = [] - - # Create and wrap the variable. - v = next_creator(*args, **kwargs) - wrapped = values.AggregatingVariable( - self._container_strategy(), v, aggregation) - - # Add the wrapped variable to the requested collections. - # The handling of eager mode and the global step matches - # ResourceVariable._init_from_args(). - if not context.executing_eagerly(): - g = ops.get_default_graph() - # If "trainable" is True, next_creator() will add the contained - # variable to the TRAINABLE_VARIABLES collection, so we manually - # remove it and replace with the wrapper. We can't set "trainable" - # to False for next_creator() since that causes functions like - # implicit_gradients to skip those variables. - if kwargs.get("trainable", True): - collections.append(ops.GraphKeys.TRAINABLE_VARIABLES) - l = g.get_collection_ref(ops.GraphKeys.TRAINABLE_VARIABLES) - l.remove(v) - g.add_to_collections(collections, wrapped) - elif ops.GraphKeys.GLOBAL_STEP in collections: - ops.add_to_collections(ops.GraphKeys.GLOBAL_STEP, wrapped) - - return wrapped - else: - var_creator = next_creator - - if "colocate_with" in kwargs: - with ops.device(None): - with ops.colocate_with(kwargs["colocate_with"]): - return var_creator(*args, **kwargs) - - with ops.colocate_with(None, ignore_existing=True): - with ops.device(self._variable_device): - return var_creator(*args, **kwargs) - - def _call_for_each_replica(self, fn, args, kwargs): - # pylint: disable=protected-access - return mirrored_strategy._call_for_each_replica( - self._container_strategy(), self._device_map, fn, args, kwargs) - - def _verify_destinations_not_different_worker(self, destinations): - if not self._cluster_spec: - return - if destinations is None: - return - for d in cross_device_ops_lib.get_devices_from(destinations): - d_spec = tf_device.DeviceSpec.from_string(d) - if d_spec.job == self._task_type and d_spec.task != self._task_id: - raise ValueError( - "Cannot reduce to another worker: %r, current worker is %r" % - (d, self._input_workers.worker_devices[0])) - - def _reduce_to(self, reduce_op, value, destinations): - self._verify_destinations_not_different_worker(destinations) - if not isinstance(value, values.DistributedValues): - # pylint: disable=protected-access - return cross_device_ops_lib.reduce_non_distributed_value( - reduce_op, self._device_map, value, destinations) - return self._cross_device_ops.reduce( - reduce_op, value, destinations=destinations) - - def _batch_reduce_to(self, reduce_op, value_destination_pairs): - for _, destinations in value_destination_pairs: - self._verify_destinations_not_different_worker(destinations) - return self._cross_device_ops.batch_reduce(reduce_op, - value_destination_pairs) - - def _select_single_value(self, structured): - """Select any single values in `structured`.""" - - def _select_fn(x): # pylint: disable=g-missing-docstring - if isinstance(x, values.Mirrored): - if len(x.devices) == 1: - return x.primary - else: - raise ValueError( - "You cannot update variable with a Mirrored object with multiple " - "components %r when using ParameterServerStrategy. You must " - "specify a single value or a Mirrored with a single value." % x) - elif isinstance(x, values.PerReplica): - raise ValueError( - "You cannot update variable with a PerReplica object %r when using " - "ParameterServerStrategy. You must specify a single value or a " - "Mirrored with a single value" % x) - else: - return x - - return nest.map_structure(_select_fn, structured) - - def _update(self, var, fn, args, kwargs, group): - if isinstance(var, values.AggregatingVariable): - var = var.get() - if not isinstance(var, resource_variable_ops.ResourceVariable): - raise ValueError( - "You can not update `var` %r. It must be a Variable." % var) - with ops.colocate_with(var), distribute_lib.UpdateContext(var.device): - result = fn(var, *self._select_single_value(args), - **self._select_single_value(kwargs)) - if group: - return result - else: - return nest.map_structure(self._unwrap, result) - - # TODO(yuefengz): does it need to call _select_single_value? - def _update_non_slot(self, colocate_with, fn, args, kwargs, group): - with ops.device( - colocate_with.device), distribute_lib.UpdateContext(colocate_with): - result = fn(*args, **kwargs) - if group: - return result - else: - return nest.map_structure(self._unwrap, result) - - def _unwrap(self, val): - if isinstance(val, values.DistributedValues): - return val.values - return (val,) - - def value_container(self, val): - if (hasattr(val, "_aggregating_container") and - not isinstance(val, values.AggregatingVariable)): - wrapper = val._aggregating_container() # pylint: disable=protected-access - if wrapper is not None: - return wrapper - return val - - def read_var(self, var): - # No need to distinguish between normal variables and replica-local - # variables. - return array_ops.identity(var) - - def _configure(self, - session_config=None, - cluster_spec=None, - task_type=None, - task_id=None): - """Configures the strategy class. - - The strategy object will be re-initialized if `cluster_spec` is given but - was not passed in the constructor. - - Args: - session_config: not used currently. - cluster_spec: a dict, ClusterDef or ClusterSpec object specifying the - cluster configurations. - task_type: the current task type. - task_id: the current task id. - - Raises: - ValueError: if `cluster_spec` is given but `task_type` or `task_id` is - not. - """ - if not self._cluster_spec and cluster_spec: - # If a `cluster_spec` is already passed in, do nothing here. - # TODO(yuefengz): check `cluster_spec` is the same if this object has - # already been initialized with a `cluster_spec`. - if task_type is None or task_id is None: - raise ValueError("When `cluster_spec` is given, must also specify " - "`task_type` and `task_id`.") - self._cluster_spec = multi_worker_util.normalize_cluster_spec( - cluster_spec) - self._task_type = task_type - self._task_id = task_id - self._initialize_multi_worker(self._num_gpus_per_worker, - self._cluster_spec, task_type, task_id) - - if session_config: - session_config.CopyFrom(self._update_config_proto(session_config)) - - def _update_config_proto(self, config_proto): - updated_config = copy.deepcopy(config_proto) - if not self._cluster_spec: - updated_config.isolate_session_state = True - return updated_config - - updated_config.isolate_session_state = False - - assert self._task_type - assert self._task_id is not None - - # The device filters prevent communication between workers. - if self._task_type not in ["chief", "worker"]: - return updated_config - del updated_config.device_filters[:] - updated_config.device_filters.extend( - ["/job:%s/task:%d" % (self._task_type, self._task_id), "/job:ps"]) - return updated_config - - @property - def _num_replicas_in_sync(self): - return self._device_map.num_replicas_in_graph - - @property - def worker_devices(self): - return self._device_map.all_devices - - @property - def worker_devices_by_replica(self): - return self._device_map.devices_by_replica - - @property - def parameter_devices(self): - return self._parameter_devices - - def non_slot_devices(self, var_list): - return min(var_list, key=lambda x: x.name) - - @property - def experimental_between_graph(self): - # TODO(yuefengz): Should this return False in the local case? - return True - - @property - def experimental_should_init(self): - return self._is_chief - - @property - def should_checkpoint(self): - return self._is_chief - - @property - def should_save_summary(self): - return self._is_chief + return input_lib.DatasetIterator(dataset, self._input_workers) # TODO(priyag): Delete this once all strategies use global batch size. @property diff --git a/tensorflow/contrib/distribute/python/parameter_server_strategy_test.py b/tensorflow/contrib/distribute/python/parameter_server_strategy_test.py index ce7065f220..d8c6c50db2 100644 --- a/tensorflow/contrib/distribute/python/parameter_server_strategy_test.py +++ b/tensorflow/contrib/distribute/python/parameter_server_strategy_test.py @@ -29,10 +29,13 @@ from tensorflow.contrib.distribute.python import strategy_test_lib from tensorflow.core.protobuf import config_pb2 from tensorflow.python.data.ops import dataset_ops from tensorflow.python.distribute import device_util +from tensorflow.python.distribute import distribute_lib from tensorflow.python.distribute import distribution_strategy_context as ds_context from tensorflow.python.distribute import multi_worker_util +from tensorflow.python.distribute import parameter_server_strategy as core_parameter_server_strategy from tensorflow.python.distribute import reduce_util from tensorflow.python.distribute import values +from tensorflow.python.distribute.cluster_resolver import SimpleClusterResolver from tensorflow.python.eager import backprop from tensorflow.python.eager import context from tensorflow.python.estimator import run_config @@ -50,6 +53,7 @@ from tensorflow.python.ops import variable_scope from tensorflow.python.ops import variables from tensorflow.python.platform import test from tensorflow.python.training import training_util +from tensorflow.python.training.server_lib import ClusterSpec CHIEF = run_config.TaskType.CHIEF WORKER = run_config.TaskType.WORKER @@ -63,6 +67,57 @@ def _get_replica_id_integer(): return replica_id +class MockCoreParameterServerStrategy(distribute_lib.DistributionStrategy): + """Mock the strategy to allow cluster resolver as an argument.""" + + def __init__(self, cluster_resolver): + super(MockCoreParameterServerStrategy, self).__init__( + core_parameter_server_strategy.ParameterServerStrategyExtended( + self, cluster_resolver=cluster_resolver)) + + +def create_test_objects(cluster_spec=None, + task_type=None, + task_id=None, + num_gpus=None, + sess_config=None, + use_core_strategy=False): + sess_config = sess_config or config_pb2.ConfigProto() + if num_gpus is None: + num_gpus = context.num_gpus() + if use_core_strategy: + if cluster_spec and task_type and task_id is not None: + cluster_resolver = SimpleClusterResolver( + cluster_spec=multi_worker_util.normalize_cluster_spec(cluster_spec), + task_type=task_type, + task_index=task_id, + num_accelerators=num_gpus) + target = 'grpc://' + cluster_spec[WORKER][task_id] + else: + cluster_resolver = SimpleClusterResolver( + ClusterSpec({}), num_accelerators=num_gpus) + target = '' + + distribution = MockCoreParameterServerStrategy(cluster_resolver) + sess_config = copy.deepcopy(sess_config) + sess_config = distribution.update_config_proto(sess_config) + else: + distribution = parameter_server_strategy.ParameterServerStrategy( + num_gpus_per_worker=num_gpus) + if task_type: + sess_config = copy.deepcopy(sess_config) + distribution.configure( + session_config=sess_config, + cluster_spec=cluster_spec, + task_type=task_type, + task_id=task_id) + target = 'grpc://' + cluster_spec[WORKER][task_id] + else: + target = '' + + return distribution, target, sess_config + + class ParameterServerStrategyTestBase( multi_worker_test_base.MultiWorkerTestBase): @@ -76,24 +131,27 @@ class ParameterServerStrategyTestBase( self._sess_config = config_pb2.ConfigProto(allow_soft_placement=True) super(ParameterServerStrategyTestBase, self).setUp() - def _get_test_objects(self, task_type, task_id, num_gpus): - distribution = parameter_server_strategy.ParameterServerStrategy( - num_gpus_per_worker=num_gpus) - if not task_type: - return distribution, '', self._sess_config - - sess_config = copy.deepcopy(self._sess_config) - distribution.configure( - session_config=sess_config, + def _get_test_objects(self, + task_type, + task_id, + num_gpus, + use_core_strategy=False): + return create_test_objects( cluster_spec=self._cluster_spec, task_type=task_type, - task_id=task_id) - return (distribution, 'grpc://' + self._cluster_spec[WORKER][task_id], - sess_config) - - def _test_device_assignment_distributed(self, task_type, task_id, num_gpus): + task_id=task_id, + num_gpus=num_gpus, + sess_config=self._sess_config, + use_core_strategy=use_core_strategy) + + def _test_device_assignment_distributed(self, + task_type, + task_id, + num_gpus, + use_core_strategy=False): worker_device = '/job:%s/replica:0/task:%d' % (task_type, task_id) - d, _, sess_config = self._get_test_objects(task_type, task_id, num_gpus) + d, _, sess_config = self._get_test_objects( + task_type, task_id, num_gpus, use_core_strategy=use_core_strategy) with ops.Graph().as_default(), \ self.cached_session(target=self._default_target, config=sess_config) as sess, \ @@ -191,8 +249,9 @@ class ParameterServerStrategyTestBase( self.assertEqual(f_val, 46.0) def _test_device_assignment_distributed_enable_partitioner( - self, task_type, task_id, num_gpus): - d, _, sess_config = self._get_test_objects(task_type, task_id, num_gpus) + self, task_type, task_id, num_gpus, use_core_strategy=False): + d, _, sess_config = self._get_test_objects( + task_type, task_id, num_gpus, use_core_strategy=use_core_strategy) num_shards = len(d.parameter_devices) partitioner = partitioned_variables.fixed_size_partitioner(num_shards) with ops.Graph().as_default(), \ @@ -340,9 +399,13 @@ class ParameterServerStrategyTestBase( self.assertEqual(z_val, 43.0) self.assertEqual(f_val, 46.0) - def _test_simple_increment(self, task_type, task_id, num_gpus): + def _test_simple_increment(self, + task_type, + task_id, + num_gpus, + use_core_strategy=False): d, master_target, sess_config = self._get_test_objects( - task_type, task_id, num_gpus) + task_type, task_id, num_gpus, use_core_strategy=use_core_strategy) if d.extended._cluster_spec: num_workers = len(d.extended._cluster_spec.as_dict().get(WORKER)) if 'chief' in d.extended._cluster_spec.as_dict(): @@ -410,9 +473,13 @@ class ParameterServerStrategyTestBase( y_val == 20.0 + 1.0 * num_workers * d.num_replicas_in_sync and z_val == 30.0 + 1.0 * num_workers) - def _test_minimize_loss_graph(self, task_type, task_id, num_gpus): + def _test_minimize_loss_graph(self, + task_type, + task_id, + num_gpus, + use_core_strategy=False): d, master_target, sess_config = self._get_test_objects( - task_type, task_id, num_gpus) + task_type, task_id, num_gpus, use_core_strategy=use_core_strategy) if task_type: # Multi-worker assert hasattr(d.extended, '_cluster_spec') and d.extended._cluster_spec @@ -498,10 +565,15 @@ class ParameterServerStrategyTestBase( self.assertLess(error_after, error_before) return error_after < error_before - def _test_input_fn_iterator(self, task_type, task_id, num_gpus, input_fn, - expected_values): + def _test_input_fn_iterator(self, + task_type, + task_id, + num_gpus, + input_fn, + expected_values, + use_core_strategy=False): distribution, master_target, config = self._get_test_objects( - task_type, task_id, num_gpus) + task_type, task_id, num_gpus, use_core_strategy=use_core_strategy) devices = distribution.extended.worker_devices with ops.Graph().as_default(), \ @@ -541,66 +613,93 @@ class ParameterServerStrategyTest(ParameterServerStrategyTestBase, num_workers=3, num_ps=2) cls._default_target = 'grpc://' + cls._cluster_spec[WORKER][0] - def test_num_replicas_in_sync(self): - distribution = parameter_server_strategy.ParameterServerStrategy( - num_gpus_per_worker=2) + @combinations.generate( + combinations.combine(mode=['graph'], use_core_strategy=[True, False])) + def test_num_replicas_in_sync(self, use_core_strategy): + strategy, _, _ = create_test_objects( + num_gpus=2, use_core_strategy=use_core_strategy) # All the devices on a given worker are in sync which in this case is the # number of gpus on each worker. - self.assertEqual(2, distribution.num_replicas_in_sync) + self.assertEqual(2, strategy.num_replicas_in_sync) - def testDeviceAssignmentLocalCPU(self): - distribution = parameter_server_strategy.ParameterServerStrategy( - num_gpus_per_worker=0) + @combinations.generate( + combinations.combine(mode=['graph'], use_core_strategy=[True, False])) + def testDeviceAssignmentLocalCPU(self, use_core_strategy): + strategy, _, _ = create_test_objects( + num_gpus=0, use_core_strategy=use_core_strategy) self._test_device_assignment_local( - distribution, compute_device='CPU', variable_device='CPU', num_gpus=0) + strategy, compute_device='CPU', variable_device='CPU', num_gpus=0) - def testDeviceAssignmentLocalOneGPU(self): - distribution = parameter_server_strategy.ParameterServerStrategy( - num_gpus_per_worker=1) + @combinations.generate( + combinations.combine(mode=['graph'], use_core_strategy=[True, False])) + def testDeviceAssignmentLocalOneGPU(self, use_core_strategy): + strategy, _, _ = create_test_objects( + num_gpus=1, use_core_strategy=use_core_strategy) self._test_device_assignment_local( - distribution, compute_device='GPU', variable_device='GPU', num_gpus=1) + strategy, compute_device='GPU', variable_device='GPU', num_gpus=1) - def testDeviceAssignmentLocalTwoGPUs(self): - distribution = parameter_server_strategy.ParameterServerStrategy( - num_gpus_per_worker=2) + @combinations.generate( + combinations.combine(mode=['graph'], use_core_strategy=[True, False])) + def testDeviceAssignmentLocalTwoGPUs(self, use_core_strategy): + strategy, _, _ = create_test_objects( + num_gpus=2, use_core_strategy=use_core_strategy) self._test_device_assignment_local( - distribution, compute_device='GPU', variable_device='CPU', num_gpus=2) + strategy, compute_device='GPU', variable_device='CPU', num_gpus=2) @combinations.generate( - combinations.combine(mode=['graph'], num_gpus=[0, 1, 2])) - def testDeviceAssignmentDistributed(self, num_gpus): - self._test_device_assignment_distributed('worker', 1, num_gpus) + combinations.combine( + mode=['graph'], num_gpus=[0, 1, 2], use_core_strategy=[True, False])) + def testDeviceAssignmentDistributed(self, num_gpus, use_core_strategy): + self._test_device_assignment_distributed( + 'worker', 1, num_gpus, use_core_strategy=use_core_strategy) @combinations.generate( - combinations.combine(mode=['graph'], num_gpus=[0, 1, 2])) - def testDeviceAssignmentDistributedEnablePartitioner(self, num_gpus): + combinations.combine( + mode=['graph'], num_gpus=[0, 1, 2], use_core_strategy=[True, False])) + def testDeviceAssignmentDistributedEnablePartitioner(self, num_gpus, + use_core_strategy): self._test_device_assignment_distributed_enable_partitioner( - 'worker', 1, num_gpus) + 'worker', 1, num_gpus, use_core_strategy=use_core_strategy) - def testSimpleBetweenGraph(self): - self._run_between_graph_clients(self._test_simple_increment, - self._cluster_spec, context.num_gpus()) + @combinations.generate( + combinations.combine(mode=['graph'], use_core_strategy=[True, False])) + def testSimpleBetweenGraph(self, use_core_strategy): + self._run_between_graph_clients( + self._test_simple_increment, + self._cluster_spec, + context.num_gpus(), + use_core_strategy=use_core_strategy) @combinations.generate( - combinations.combine(mode=['graph'], num_gpus=[0, 1, 2])) - def testLocalSimpleIncrement(self, num_gpus): - self._test_simple_increment(None, 0, num_gpus) + combinations.combine( + mode=['graph'], num_gpus=[0, 1, 2], use_core_strategy=[True, False])) + def testLocalSimpleIncrement(self, num_gpus, use_core_strategy): + self._test_simple_increment(None, 0, num_gpus, use_core_strategy) @combinations.generate( - combinations.combine(mode=['graph'], num_gpus=[0, 1, 2])) - def testMinimizeLossGraphDistributed(self, num_gpus): - self._run_between_graph_clients(self._test_minimize_loss_graph, - self._cluster_spec, num_gpus) + combinations.combine( + mode=['graph'], num_gpus=[0, 1, 2], use_core_strategy=[True, False])) + def testMinimizeLossGraphDistributed(self, num_gpus, use_core_strategy): + self._run_between_graph_clients( + self._test_minimize_loss_graph, + self._cluster_spec, + num_gpus, + use_core_strategy=use_core_strategy) @combinations.generate( - combinations.combine(mode=['graph'], num_gpus=[0, 1, 2])) - def testMinimizeLossGraphLocal(self, num_gpus): - self._test_minimize_loss_graph(None, None, num_gpus) + combinations.combine( + mode=['graph'], num_gpus=[0, 1, 2], use_core_strategy=[True, False])) + def testMinimizeLossGraphLocal(self, num_gpus, use_core_strategy): + self._test_minimize_loss_graph(None, None, num_gpus, use_core_strategy) # TODO(priyag): Refactor this and other multi worker tests. @combinations.generate( - combinations.combine(mode=['graph'], num_gpus=[1, 2], required_gpus=1)) - def testMakeInputFnIteratorDistributed(self, num_gpus): + combinations.combine( + mode=['graph'], + num_gpus=[1, 2], + required_gpus=1, + use_core_strategy=[True, False])) + def testMakeInputFnIteratorDistributed(self, num_gpus, use_core_strategy): if context.num_gpus() < num_gpus: self.skipTest('Not enough GPUs') dataset_fn = lambda: dataset_ops.Dataset.range(100) @@ -612,12 +711,21 @@ class ParameterServerStrategyTest(ParameterServerStrategyTestBase, expected_num_replicas_in_sync=num_gpus, expected_num_input_pipelines=3, expected_input_pipeline_id=1) # because task_id = 1 - self._test_input_fn_iterator('worker', 1, num_gpus, - input_fn, expected_values) + self._test_input_fn_iterator( + 'worker', + 1, + num_gpus, + input_fn, + expected_values, + use_core_strategy=use_core_strategy) @combinations.generate( - combinations.combine(mode=['graph'], num_gpus=[1, 2], required_gpus=1)) - def testMakeInputFnIteratorLocal(self, num_gpus): + combinations.combine( + mode=['graph'], + num_gpus=[1, 2], + required_gpus=1, + use_core_strategy=[True, False])) + def testMakeInputFnIteratorLocal(self, num_gpus, use_core_strategy): if context.num_gpus() < num_gpus: self.skipTest('Not enough GPUs') dataset_fn = lambda: dataset_ops.Dataset.range(100) @@ -629,23 +737,31 @@ class ParameterServerStrategyTest(ParameterServerStrategyTestBase, expected_num_replicas_in_sync=num_gpus, expected_num_input_pipelines=1, expected_input_pipeline_id=0) # only one worker and pipeline for local. - self._test_input_fn_iterator(None, None, num_gpus, - input_fn, expected_values) + self._test_input_fn_iterator( + None, + None, + num_gpus, + input_fn, + expected_values, + use_core_strategy=use_core_strategy) - def testGlobalStepUpdate(self): - strategy = parameter_server_strategy.ParameterServerStrategy( - num_gpus_per_worker=context.num_gpus()) + @combinations.generate( + combinations.combine(mode=['graph'], use_core_strategy=[True, False])) + def testGlobalStepUpdate(self, use_core_strategy): + strategy, _, _ = create_test_objects(use_core_strategy=use_core_strategy) self._test_global_step_update(strategy) - def testUpdateConfigProtoMultiWorker(self): - distribution = parameter_server_strategy.ParameterServerStrategy( - num_gpus_per_worker=2) - distribution.configure( + @combinations.generate( + combinations.combine(mode=['graph'], use_core_strategy=[True, False])) + def testUpdateConfigProtoMultiWorker(self, use_core_strategy): + strategy, _, _ = create_test_objects( + num_gpus=2, use_core_strategy=use_core_strategy) + strategy.configure( cluster_spec=self._cluster_spec, task_type='worker', task_id=1) config_proto = config_pb2.ConfigProto(device_filters=['to_be_overridden']) - new_config = distribution.update_config_proto(config_proto) + new_config = strategy.update_config_proto(config_proto) # Verify device filters. self.assertEqual(['/job:worker/task:1', '/job:ps'], @@ -654,12 +770,14 @@ class ParameterServerStrategyTest(ParameterServerStrategyTestBase, # Verify isolate_session_state self.assertFalse(new_config.isolate_session_state) - def testUpdateConfigProtoLocal(self): - distribution = parameter_server_strategy.ParameterServerStrategy( - num_gpus_per_worker=2) + @combinations.generate( + combinations.combine(mode=['graph'], use_core_strategy=[True, False])) + def testUpdateConfigProtoLocal(self, use_core_strategy): + strategy, _, _ = create_test_objects( + num_gpus=2, use_core_strategy=use_core_strategy) config_proto = config_pb2.ConfigProto() - new_config = distribution.update_config_proto(config_proto) + new_config = strategy.update_config_proto(config_proto) # Verify isolate_session_state self.assertTrue(new_config.isolate_session_state) @@ -674,20 +792,31 @@ class ParameterServerStrategyWithChiefTest(ParameterServerStrategyTestBase, num_workers=3, num_ps=2, has_chief=True) cls._default_target = 'grpc://' + cls._cluster_spec[CHIEF][0] - def testSimpleBetweenGraph(self): - self._run_between_graph_clients(self._test_simple_increment, - self._cluster_spec, context.num_gpus()) + @combinations.generate( + combinations.combine(mode=['graph'], use_core_strategy=[True, False])) + def testSimpleBetweenGraph(self, use_core_strategy): + self._run_between_graph_clients( + self._test_simple_increment, + self._cluster_spec, + context.num_gpus(), + use_core_strategy=use_core_strategy) @combinations.generate( - combinations.combine(mode=['graph'], num_gpus=[0, 1, 2])) - def testMinimizeLossGraph(self, num_gpus): - self._run_between_graph_clients(self._test_minimize_loss_graph, - self._cluster_spec, num_gpus) + combinations.combine( + mode=['graph'], num_gpus=[0, 1, 2], use_core_strategy=[True, False])) + def testMinimizeLossGraph(self, num_gpus, use_core_strategy): + self._run_between_graph_clients( + self._test_minimize_loss_graph, + self._cluster_spec, + num_gpus, + use_core_strategy=use_core_strategy) - def testGlobalStepIsWrappedOnTwoGPUs(self): - distribution = parameter_server_strategy.ParameterServerStrategy( - num_gpus_per_worker=2) - with ops.Graph().as_default(), distribution.scope(): + @combinations.generate( + combinations.combine(mode=['graph'], use_core_strategy=[True, False])) + def testGlobalStepIsWrappedOnTwoGPUs(self, use_core_strategy): + strategy, _, _ = create_test_objects( + num_gpus=2, use_core_strategy=use_core_strategy) + with ops.Graph().as_default(), strategy.scope(): created_step = training_util.create_global_step() get_step = training_util.get_global_step() self.assertEqual(created_step, get_step, @@ -696,12 +825,14 @@ class ParameterServerStrategyWithChiefTest(ParameterServerStrategyTestBase, id(get_step), get_step.__class__.__name__))) self.assertIs(values.AggregatingVariable, type(created_step)) self.assertIs(values.AggregatingVariable, type(get_step)) - self.assertIs(distribution, created_step.distribute_strategy) + self.assertIs(strategy, created_step.distribute_strategy) - def testGlobalStepIsNotWrappedOnOneGPU(self): - distribution = parameter_server_strategy.ParameterServerStrategy( - num_gpus_per_worker=1) - with ops.Graph().as_default(), distribution.scope(): + @combinations.generate( + combinations.combine(mode=['graph'], use_core_strategy=[True, False])) + def testGlobalStepIsNotWrappedOnOneGPU(self, use_core_strategy): + strategy, _, _ = create_test_objects( + num_gpus=1, use_core_strategy=use_core_strategy) + with ops.Graph().as_default(), strategy.scope(): created_step = training_util.create_global_step() get_step = training_util.get_global_step() self.assertEqual(created_step, get_step, @@ -710,20 +841,24 @@ class ParameterServerStrategyWithChiefTest(ParameterServerStrategyTestBase, id(get_step), get_step.__class__.__name__))) self.assertIs(resource_variable_ops.ResourceVariable, type(created_step)) self.assertIs(resource_variable_ops.ResourceVariable, type(get_step)) - self.assertIs(distribution, created_step.distribute_strategy) + self.assertIs(strategy, created_step.distribute_strategy) + + @combinations.generate( + combinations.combine(mode=['graph'], use_core_strategy=[True, False])) + def testValueContainer(self, use_core_strategy): + strategy, _, _ = create_test_objects( + num_gpus=2, use_core_strategy=use_core_strategy) + with ops.Graph().as_default(), strategy.scope(): - def testValueContainer(self): - distribution = parameter_server_strategy.ParameterServerStrategy( - num_gpus_per_worker=2) - with ops.Graph().as_default(), distribution.scope(): def f(): with backprop.GradientTape() as tape: v = variable_scope.get_variable('v', initializer=10.0) _ = v * v v, = tape.watched_variables() - w = distribution.extended.value_container(v) + w = strategy.extended.value_container(v) self.assertIs(values.AggregatingVariable, type(w)) - distribution.extended.call_for_each_replica(f) + + strategy.extended.call_for_each_replica(f) if __name__ == '__main__': diff --git a/tensorflow/python/distribute/BUILD b/tensorflow/python/distribute/BUILD index 987fb00454..faaa61934a 100644 --- a/tensorflow/python/distribute/BUILD +++ b/tensorflow/python/distribute/BUILD @@ -242,6 +242,28 @@ py_library( ], ) +py_library( + name = "parameter_server_strategy", + srcs = ["parameter_server_strategy.py"], + visibility = ["//tensorflow:internal"], + deps = [ + ":input_lib", + ":mirrored_strategy", + "//tensorflow/core:protos_all_py", + "//tensorflow/python:array_ops", + "//tensorflow/python:framework_ops", + "//tensorflow/python:resource_variable_ops", + "//tensorflow/python:training", + "//tensorflow/python:util", + "//tensorflow/python/distribute:cross_device_ops", + "//tensorflow/python/distribute:multi_worker_util", + "//tensorflow/python/distribute:reduce_util", + "//tensorflow/python/distribute:values", + "//tensorflow/python/distribute/cluster_resolver:cluster_resolver_lib", + "//tensorflow/python/eager:context", + ], +) + py_library( name = "multi_worker_util", srcs = [ diff --git a/tensorflow/python/distribute/parameter_server_strategy.py b/tensorflow/python/distribute/parameter_server_strategy.py new file mode 100644 index 0000000000..71fbffdc0d --- /dev/null +++ b/tensorflow/python/distribute/parameter_server_strategy.py @@ -0,0 +1,528 @@ +# Copyright 2018 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. +# ============================================================================== +"""Classes implementing a multi-worker ps DistributionStrategy.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import copy + +from tensorflow.contrib.distribute.python import mirrored_strategy +from tensorflow.python.distribute import cross_device_ops as cross_device_ops_lib +from tensorflow.python.distribute import device_util +from tensorflow.python.distribute import distribute_lib +from tensorflow.python.distribute import input_lib +from tensorflow.python.distribute import multi_worker_util +from tensorflow.python.distribute import values +from tensorflow.python.distribute.cluster_resolver import SimpleClusterResolver +from tensorflow.python.distribute.cluster_resolver import TFConfigClusterResolver +from tensorflow.python.eager import context +from tensorflow.python.framework import device as tf_device +from tensorflow.python.framework import ops +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import resource_variable_ops +from tensorflow.python.ops import variable_scope as vs +from tensorflow.python.platform import tf_logging as logging +from tensorflow.python.training import device_setter +from tensorflow.python.util import nest + +_LOCAL_CPU = "/device:CPU:0" +_LOCAL_GPU_0 = "/device:GPU:0" + + +# TODO(yuefengz): maybe cache variables on local CPU. +class ParameterServerStrategy(distribute_lib.DistributionStrategy): + """A parameter server DistributionStrategy. + + This strategy class works for both local training and between-graph replicated + training for multiple workers. It uses `TFConfigClusterResolver` to detect + configurations for multi-worker training. In multi-worker training mode, i.e. + `TFConfigClusterResolver` has detected 'TF_CONFIG' environment variable and + 'TF_CONFIG' has a cluster spec, variables and updates to those variables are + assigned to parameter servers and other operations are assigned to workers. + In local training mode, variables are assigned to local CPU or the only GPU. + When each worker has more than one GPU, operations will be replicated on these + GPUs. In both cases, operations are replicated but variables are not and these + workers share a common view for which paramater server a variable is assigned + to. + + This class assumes between-graph replication will be used and works on a graph + for a particular worker. Note that each graph and worker is independent. + This means that while each worker will synchronously compute a single gradient + update across all GPUs, updates between workers proceed asynchronously. + Operations that occur only on the first replica (such as incrementing the + global step), will occur on the first replica *of every worker*. + + It is expected to call `call_for_each_replica(fn, ...)` for any + operations which potentially can be replicated across replicas (i.e. multiple + GPUs) even if there is only CPU or one GPU. When defining the `fn`, extra + caution needs to be taken: + + 1) It is generally not recommended to open a device scope under the strategy's + scope. A device scope (i.e. calling `tf.device`) will be merged with or + override the device for operations but will not change the device for + variables. + + 2) It is also not recommended to open a colocation scope (i.e. calling + `tf.colocate_with`) under the strategy's scope. For colocating variables, + use `distribution.colocate_vars_with` instead. Colocation of ops will possibly + create conflicts of device assignment. + """ + + def __init__(self): + """Initializes this strategy with default TFConfigClusterResolver.""" + super(ParameterServerStrategy, self).__init__( + ParameterServerStrategyExtended(self)) + + +class ParameterServerStrategyExtended( + distribute_lib.DistributionStrategyExtended): + """Implementation of ParameterServerStrategy.""" + + def __init__(self, + container_strategy, + cluster_resolver=TFConfigClusterResolver()): + super(ParameterServerStrategyExtended, self).__init__(container_strategy) + self._initialize_strategy(cluster_resolver) + + # We typically don't need to do all-reduce in this strategy. + self._cross_device_ops = ( + cross_device_ops_lib.ReductionToOneDeviceCrossDeviceOps( + reduce_to_device=_LOCAL_CPU)) + + def _initialize_strategy(self, cluster_resolver): + if cluster_resolver.cluster_spec().as_dict(): + self._initialize_multi_worker(cluster_resolver) + else: + self._initialize_local(cluster_resolver) + # Save the num_gpus_per_worker for configure method. + self._num_gpus_per_worker = cluster_resolver.num_accelerators() + + def _initialize_multi_worker(self, cluster_resolver): + """Initialize devices for multiple workers. + + It creates variable devices and compute devices. Variables and operations + will be assigned to them respectively. We have one compute device per + replica. The variable device is a device function or device string. The + default variable device assigns variables to parameter servers in a + round-robin fashion. + + Args: + cluster_resolver: a descendant of `ClusterResolver` object. + + Raises: + ValueError: if the cluster doesn't have ps jobs. + """ + num_gpus = cluster_resolver.num_accelerators() + cluster_spec = cluster_resolver.cluster_spec() + task_type = cluster_resolver.task_type + task_id = cluster_resolver.task_index + if not task_type or task_id is None: + raise ValueError("When `cluster_spec` is given, you must also specify " + "`task_type` and `task_id`") + cluster_spec = multi_worker_util.normalize_cluster_spec(cluster_spec) + assert cluster_spec.as_dict() + + worker_device = "/job:%s/task:%d" % (task_type, task_id) + + # Define compute devices which is a list of device strings and one for each + # replica. When there are GPUs, replicate operations on these GPUs. + # Otherwise, place operations on CPU. + if num_gpus > 0: + compute_devices = tuple( + "%s/device:GPU:%d" % (worker_device, i) for i in range(num_gpus)) + else: + compute_devices = (worker_device,) + + self._device_map = values.ReplicaDeviceMap(compute_devices) + self._input_workers = input_lib.InputWorkers( + self._device_map, [(worker_device, compute_devices)]) + + # In distributed mode, place variables on ps jobs in a round-robin fashion. + # Note that devices returned from `replica_device_setter` are not + # canonical and therefore we don't canonicalize all variable devices to + # make them consistent. + # TODO(yuefengz): support passing a strategy object to control variable + # assignment. + # TODO(yuefengz): merge the logic of replica_device_setter into this + # class. + num_ps_replicas = len(cluster_spec.as_dict().get("ps", [])) + if num_ps_replicas == 0: + raise ValueError("The cluster spec needs to have `ps` jobs.") + self._variable_device = device_setter.replica_device_setter( + ps_tasks=num_ps_replicas, + worker_device=worker_device, + merge_devices=True, + cluster=cluster_spec) + + # The `_parameter_devices` is needed for the `parameter_devices` property + # and is a list of all variable devices. Here parameter devices are all + # tasks of the "ps" job. + self._parameter_devices = tuple(map("/job:ps/task:{}".format, + range(num_ps_replicas))) + + # Add a default device so that ops without specified devices will not end up + # on other workers. + self._default_device = worker_device + + self._is_chief = multi_worker_util.is_chief(cluster_spec, task_type, + task_id) + self._cluster_spec = cluster_spec + self._task_type = task_type + self._task_id = task_id + + logging.info( + "Multi-worker ParameterServerStrategy with " + "cluster_spec = %r, task_type = %r, task_id = %r, " + "num_ps_replicas = %r, is_chief = %r, device_map = %r, " + "variable_device = %r", cluster_spec.as_dict(), task_type, task_id, + num_ps_replicas, self._is_chief, self._device_map, + self._variable_device) + + def _initialize_local(self, cluster_resolver): + """Initialize internal devices for local training.""" + worker_device = device_util.canonicalize("/device:CPU:0") + num_gpus = cluster_resolver.num_accelerators() + # Define compute devices which is a list of device strings and one for each + # replica. When there are GPUs, replicate operations on these GPUs. + # Otherwise, place operations on CPU. + if num_gpus > 0: + compute_devices = tuple(map("/device:GPU:{}".format, range(num_gpus))) + else: + compute_devices = (_LOCAL_CPU,) + + self._device_map = values.ReplicaDeviceMap(compute_devices) + self._input_workers = input_lib.InputWorkers( + self._device_map, [(worker_device, compute_devices)]) + + # If there is only one GPU, put everything on that GPU. Otherwise, place + # variables on CPU. + if num_gpus == 1: + assert len(compute_devices) == 1 + self._variable_device = _LOCAL_GPU_0 + self._parameter_devices = (_LOCAL_GPU_0,) + else: + self._variable_device = _LOCAL_CPU + self._parameter_devices = (_LOCAL_CPU,) + + self._is_chief = True + self._cluster_spec = None + self._task_type = None + self._task_id = None + + logging.info( + "ParameterServerStrategy with compute_devices = %r, " + "variable_device = %r", compute_devices, self._variable_device) + + def _validate_colocate_with_variable(self, colocate_with_variable): + values.validate_colocate(colocate_with_variable, self) + + def _distribute_dataset(self, dataset_fn): + """Distributes the dataset to each local GPU.""" + return input_lib.PerReplicaDataset( + self._call_dataset_fn(dataset_fn), + self._input_workers, + 0, + prefetch_on_device=True) + + def _make_dataset_iterator(self, dataset): + return input_lib.DatasetIterator(dataset, self._input_workers, + self._num_replicas_in_sync) + + def _make_input_fn_iterator( + self, + input_fn, + replication_mode=distribute_lib.InputReplicationMode.PER_WORKER): + """Distributes the dataset to each local GPU.""" + if self._cluster_spec: + input_pipeline_id = multi_worker_util.id_in_cluster( + self._cluster_spec, self._task_type, self._task_id) + num_input_pipelines = multi_worker_util.worker_count( + self._cluster_spec, self._task_type) + else: + input_pipeline_id = 0 + num_input_pipelines = 1 + input_context = distribute_lib.InputContext( + num_input_pipelines=num_input_pipelines, + input_pipeline_id=input_pipeline_id, + num_replicas_in_sync=self._num_replicas_in_sync) + return input_lib.InputFunctionIterator(input_fn, self._input_workers, + [input_context]) + + def _broadcast_to(self, tensor, destinations): + # This is both a fast path for Python constants, and a way to delay + # converting Python values to a tensor until we know what type it + # should be converted to. Otherwise we have trouble with: + # global_step.assign_add(1) + # since the `1` gets broadcast as an int32 but global_step is int64. + if isinstance(tensor, (float, int)): + return tensor + if not cross_device_ops_lib.check_destinations(destinations): + # TODO(josh11b): Use current logical device instead of 0 here. + destinations = values.LogicalDeviceSpec( + device_map=self._device_map, logical_device=0) + return self._cross_device_ops.broadcast(tensor, destinations) + + def _allow_variable_partition(self): + return not context.executing_eagerly() + + # TODO(yuefengz): not all ops in device_setter.STANDARD_PS_OPS will go through + # this creator, such as "MutableHashTable". + def _create_variable(self, next_creator, *args, **kwargs): + if self._num_replicas_in_sync > 1: + aggregation = kwargs.pop("aggregation", vs.VariableAggregation.NONE) + if aggregation not in ( + vs.VariableAggregation.NONE, + vs.VariableAggregation.SUM, + vs.VariableAggregation.MEAN, + vs.VariableAggregation.ONLY_FIRST_REPLICA + ): + raise ValueError("Invalid variable aggregation mode: " + aggregation + + " for variable: " + kwargs["name"]) + + def var_creator(*args, **kwargs): + """Create an AggregatingVariable and fix up collections.""" + # Record what collections this variable should be added to. + collections = kwargs.pop("collections", None) + if collections is None: + collections = [ops.GraphKeys.GLOBAL_VARIABLES] + kwargs["collections"] = [] + + # Create and wrap the variable. + v = next_creator(*args, **kwargs) + wrapped = values.AggregatingVariable( + self._container_strategy(), v, aggregation) + + # Add the wrapped variable to the requested collections. + # The handling of eager mode and the global step matches + # ResourceVariable._init_from_args(). + if not context.executing_eagerly(): + g = ops.get_default_graph() + # If "trainable" is True, next_creator() will add the contained + # variable to the TRAINABLE_VARIABLES collection, so we manually + # remove it and replace with the wrapper. We can't set "trainable" + # to False for next_creator() since that causes functions like + # implicit_gradients to skip those variables. + if kwargs.get("trainable", True): + collections.append(ops.GraphKeys.TRAINABLE_VARIABLES) + l = g.get_collection_ref(ops.GraphKeys.TRAINABLE_VARIABLES) + l.remove(v) + g.add_to_collections(collections, wrapped) + elif ops.GraphKeys.GLOBAL_STEP in collections: + ops.add_to_collections(ops.GraphKeys.GLOBAL_STEP, wrapped) + + return wrapped + else: + var_creator = next_creator + + if "colocate_with" in kwargs: + with ops.device(None): + with ops.colocate_with(kwargs["colocate_with"]): + return var_creator(*args, **kwargs) + + with ops.colocate_with(None, ignore_existing=True): + with ops.device(self._variable_device): + return var_creator(*args, **kwargs) + + def _call_for_each_replica(self, fn, args, kwargs): + # pylint: disable=protected-access + return mirrored_strategy._call_for_each_replica( + self._container_strategy(), self._device_map, fn, args, kwargs) + + def _verify_destinations_not_different_worker(self, destinations): + if not self._cluster_spec: + return + if destinations is None: + return + for d in cross_device_ops_lib.get_devices_from(destinations): + d_spec = tf_device.DeviceSpec.from_string(d) + if d_spec.job == self._task_type and d_spec.task != self._task_id: + raise ValueError( + "Cannot reduce to another worker: %r, current worker is %r" % + (d, self._input_workers.worker_devices[0])) + + def _reduce_to(self, reduce_op, value, destinations): + self._verify_destinations_not_different_worker(destinations) + if not isinstance(value, values.DistributedValues): + # pylint: disable=protected-access + return cross_device_ops_lib.reduce_non_distributed_value( + reduce_op, self._device_map, value, destinations) + return self._cross_device_ops.reduce( + reduce_op, value, destinations=destinations) + + def _batch_reduce_to(self, reduce_op, value_destination_pairs): + for _, destinations in value_destination_pairs: + self._verify_destinations_not_different_worker(destinations) + return self._cross_device_ops.batch_reduce(reduce_op, + value_destination_pairs) + + def _select_single_value(self, structured): + """Select any single values in `structured`.""" + + def _select_fn(x): # pylint: disable=g-missing-docstring + if isinstance(x, values.Mirrored): + if len(x.devices) == 1: + return x.primary + else: + raise ValueError( + "You cannot update variable with a Mirrored object with multiple " + "components %r when using ParameterServerStrategy. You must " + "specify a single value or a Mirrored with a single value." % x) + elif isinstance(x, values.PerReplica): + raise ValueError( + "You cannot update variable with a PerReplica object %r when using " + "ParameterServerStrategy. You must specify a single value or a " + "Mirrored with a single value" % x) + else: + return x + + return nest.map_structure(_select_fn, structured) + + def _update(self, var, fn, args, kwargs, group): + if isinstance(var, values.AggregatingVariable): + var = var.get() + if not isinstance(var, resource_variable_ops.ResourceVariable): + raise ValueError( + "You can not update `var` %r. It must be a Variable." % var) + with ops.colocate_with(var), distribute_lib.UpdateContext(var.device): + result = fn(var, *self._select_single_value(args), + **self._select_single_value(kwargs)) + if group: + return result + else: + return nest.map_structure(self._unwrap, result) + + # TODO(yuefengz): does it need to call _select_single_value? + def _update_non_slot(self, colocate_with, fn, args, kwargs, group): + with ops.device( + colocate_with.device), distribute_lib.UpdateContext(colocate_with): + result = fn(*args, **kwargs) + if group: + return result + else: + return nest.map_structure(self._unwrap, result) + + def _unwrap(self, val): + if isinstance(val, values.DistributedValues): + return val.values + return (val,) + + def value_container(self, val): + if (hasattr(val, "_aggregating_container") and + not isinstance(val, values.AggregatingVariable)): + wrapper = val._aggregating_container() # pylint: disable=protected-access + if wrapper is not None: + return wrapper + return val + + def read_var(self, var): + # No need to distinguish between normal variables and replica-local + # variables. + return array_ops.identity(var) + + def _configure(self, + session_config=None, + cluster_spec=None, + task_type=None, + task_id=None): + """Configures the strategy class. + + The strategy object will be re-initialized if `cluster_spec` is given but + was not passed in the constructor. + + Args: + session_config: not used currently. + cluster_spec: a dict, ClusterDef or ClusterSpec object specifying the + cluster configurations. + task_type: the current task type. + task_id: the current task id. + + Raises: + ValueError: if `cluster_spec` is given but `task_type` or `task_id` is + not. + """ + if cluster_spec: + # Use the num_gpus_per_worker recorded in constructor since _configure + # doesn't take num_gpus. + cluster_resolver = SimpleClusterResolver( + cluster_spec=multi_worker_util.normalize_cluster_spec(cluster_spec), + task_type=task_type, + task_index=task_id, + num_accelerators=self._num_gpus_per_worker) + self._initialize_multi_worker(cluster_resolver) + + if session_config: + session_config.CopyFrom(self._update_config_proto(session_config)) + + def _update_config_proto(self, config_proto): + updated_config = copy.deepcopy(config_proto) + if not self._cluster_spec: + updated_config.isolate_session_state = True + return updated_config + + updated_config.isolate_session_state = False + + assert self._task_type + assert self._task_id is not None + + # The device filters prevent communication between workers. + if self._task_type not in ["chief", "worker"]: + return updated_config + del updated_config.device_filters[:] + updated_config.device_filters.extend( + ["/job:%s/task:%d" % (self._task_type, self._task_id), "/job:ps"]) + return updated_config + + @property + def _num_replicas_in_sync(self): + return self._device_map.num_replicas_in_graph + + @property + def worker_devices(self): + return self._device_map.all_devices + + @property + def worker_devices_by_replica(self): + return self._device_map.devices_by_replica + + @property + def parameter_devices(self): + return self._parameter_devices + + def non_slot_devices(self, var_list): + return min(var_list, key=lambda x: x.name) + + @property + def experimental_between_graph(self): + # TODO(yuefengz): Should this return False in the local case? + return True + + @property + def experimental_should_init(self): + return self._is_chief + + @property + def should_checkpoint(self): + return self._is_chief + + @property + def should_save_summary(self): + return self._is_chief + + # TODO(priyag): Delete this once all strategies use global batch size. + @property + def _global_batch_size(self): + return True -- GitLab From cea2e9c4fab95890873114bf60a5f19facca0c2f Mon Sep 17 00:00:00 2001 From: Sergei Lebedev Date: Fri, 4 Jan 2019 13:17:37 -0800 Subject: [PATCH 361/622] Removed set_shape overrides from ResourceVariable subclasses. These are essentially no-ops and allow for reads returning tensors inconsistent with variable shape: >>> import tensorflow as tf >>> tf.enable_eager_execution() >>> v = tf.Variable(0) >>> t = v.assign(42) >>> t.set_shape((100500, )) >>> t.shape (100500, ) >>> tf.convert_to_tensor(t) PiperOrigin-RevId: 227903155 --- tensorflow/python/ops/resource_variable_ops.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tensorflow/python/ops/resource_variable_ops.py b/tensorflow/python/ops/resource_variable_ops.py index ede4bc0078..d2e5dd9cfe 100644 --- a/tensorflow/python/ops/resource_variable_ops.py +++ b/tensorflow/python/ops/resource_variable_ops.py @@ -1351,10 +1351,6 @@ class _UnreadVariable(ResourceVariable): return gen_resource_variable_ops.read_variable_op(self._handle, self._dtype) - def set_shape(self, shape): - self._shape = shape - self._cached_shape_as_list = None - @property def op(self): """The op for this variable.""" @@ -1443,10 +1439,6 @@ class _MixedPrecisionVariable(ResourceVariable): else: return res - def set_shape(self, shape): - self._shape = shape - self._cached_shape_as_list = None - @property def op(self): """The op for this variable.""" -- GitLab From 4614b04cc076c543501124670da8b8da417cbf9f Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Fri, 4 Jan 2019 13:49:49 -0800 Subject: [PATCH 362/622] One more refactoring of kernel.cc. This introduces OpInputs to match OpOutputs, which is slightly enhanced. PiperOrigin-RevId: 227908106 --- tensorflow/lite/delegates/flex/kernel.cc | 127 ++++++++++++++--------- 1 file changed, 80 insertions(+), 47 deletions(-) diff --git a/tensorflow/lite/delegates/flex/kernel.cc b/tensorflow/lite/delegates/flex/kernel.cc index 3012241f4f..0005b6e5b0 100644 --- a/tensorflow/lite/delegates/flex/kernel.cc +++ b/tensorflow/lite/delegates/flex/kernel.cc @@ -51,24 +51,57 @@ namespace tflite { namespace flex { namespace kernel { -// Controls the lifetime of tensor handles in a vector. +// A list of inputs of a given node of the TensorFlow/Eager graph. +class OpInputs { + public: + explicit OpInputs(const TfLiteIntArray* indexes) { + for (int index : TfLiteIntArrayView(indexes)) { + inputs_.push_back(index); + } + } + ~OpInputs() {} + + int Size() const { return inputs_.size(); } + + int TfLiteIndex(int i) const { return inputs_[i]; } + + private: + std::vector inputs_; +}; + +// A list of outputs of a given node of the TensorFlow/Eager graph, along with +// the actual outputs of the EagerOperation. class OpOutputs { public: - explicit OpOutputs(int num_elements) : vector_(num_elements, nullptr) {} + explicit OpOutputs(const TfLiteIntArray* indexes) { + for (int index : TfLiteIntArrayView(indexes)) { + outputs_.push_back(index); + } + vector_.resize(outputs_.size()); + } + ~OpOutputs() { ResetTensorHandles(); } + + int Size() const { return outputs_.size(); } + + int TfLiteIndex(int i) const { return outputs_[i]; } - ~OpOutputs() { - for (auto* handle : vector_) { - if (handle) handle->Unref(); + // Carefully unreference all the handles in the eager output vector. + void ResetTensorHandles() { + for (int i = 0; i < vector_.size(); ++i) { + if (vector_[i]) { + vector_[i]->Unref(); + vector_[i] = nullptr; + } } } - tensorflow::gtl::InlinedVector* GetVector() { + tensorflow::gtl::InlinedVector* + GetTensorHandles() { return &vector_; } - tensorflow::TensorHandle* GetHandle(int index) { return vector_[index]; } - private: + std::vector outputs_; tensorflow::gtl::InlinedVector vector_; }; @@ -76,7 +109,8 @@ class OpOutputs { // TensorFlow ops within a single TF Lite op. class OpNode { public: - OpNode() {} + OpNode(const TfLiteIntArray* inputs, const TfLiteIntArray* outputs) + : inputs_(inputs), outputs_(outputs) {} ~OpNode() {} const string& name() const { return name_; } @@ -87,19 +121,14 @@ class OpNode { const tensorflow::NodeDef& nodedef() const { return nodedef_; } - const std::vector& inputs() const { return inputs_; } - void InitializeInputs(const TfLiteIntArray* inputs) { - for (int index : TfLiteIntArrayView(inputs)) { - inputs_.push_back(index); - } - } + const OpInputs& inputs() const { return inputs_; } + OpInputs* mutable_inputs() { return &inputs_; } - const std::vector& outputs() const { return outputs_; } - void InitializeOutputs(const TfLiteIntArray* outputs) { - for (int index : TfLiteIntArrayView(outputs)) { - outputs_.push_back(index); - } - } + const OpOutputs& outputs() const { return outputs_; } + OpOutputs* mutable_outputs() { return &outputs_; } + + int NumInputs() const { return inputs_.Size(); } + int NumOutputs() const { return outputs_.Size(); } tensorflow::Status InitializeNodeDef(const void* custom_initial_data, int custom_initial_data_size) { @@ -162,7 +191,8 @@ class OpNode { tensorflow::Status BuildEagerInputs(BufferMap* buffer_map, tensorflow::EagerOperation* op) { - for (int input_index : inputs_) { + for (int i = 0; i < inputs_.Size(); ++i) { + int input_index = inputs_.TfLiteIndex(i); if (!buffer_map->HasTensor(input_index)) { return tensorflow::errors::Internal( "Cannot read from invalid tensor index ", input_index); @@ -181,17 +211,21 @@ class OpNode { return tensorflow::Status::OK(); } - tensorflow::Status PersistEagerOutputs(BufferMap* buffer_map, - OpOutputs* retvals) { - for (int i = 0; i < outputs_.size(); ++i) { + tensorflow::Status PersistEagerOutputs(BufferMap* buffer_map) { + auto* handles = outputs_.GetTensorHandles(); + for (int i = 0; i < outputs_.Size(); ++i) { const tensorflow::Tensor* tensor = nullptr; - TF_RETURN_IF_ERROR(retvals->GetHandle(i)->Tensor(&tensor)); - buffer_map->SetFromTensorFlow(outputs_[i], *tensor); + TF_RETURN_IF_ERROR(handles->at(i)->Tensor(&tensor)); + buffer_map->SetFromTensorFlow(outputs_.TfLiteIndex(i), *tensor); } + outputs_.ResetTensorHandles(); return tensorflow::Status::OK(); } private: + OpNode(const OpNode&) = delete; + OpNode& operator=(const OpNode&) = delete; + // The name of the TensorFlow op to execute. string name_; // Index of this node into TF Lite's operator list. @@ -199,9 +233,9 @@ class OpNode { // The corresponding NodeDef, containing the attributes for the op. tensorflow::NodeDef nodedef_; // List of inputs, as TF Lite tensor indices. - std::vector inputs_; + OpInputs inputs_; // List of outputs, as TF Lite tensor indices. - std::vector outputs_; + OpOutputs outputs_; }; // Executes the TensorFlow op given by 'op_name', with the attributes specified @@ -212,18 +246,18 @@ tensorflow::Status ExecuteFlexOp(tensorflow::EagerContext* eager_context, TF_RETURN_IF_ERROR(node_data->BuildEagerOp(eager_context, &op)); TF_RETURN_IF_ERROR(node_data->BuildEagerInputs(buffer_map, op.get())); - int num_retvals = node_data->outputs().size(); - OpOutputs retvals(num_retvals); + int num_retvals = node_data->NumOutputs(); TF_RETURN_WITH_CONTEXT_IF_ERROR( - EagerExecute(op.get(), retvals.GetVector(), &num_retvals), + EagerExecute(op.get(), node_data->mutable_outputs()->GetTensorHandles(), + &num_retvals), " (while executing '", node_data->name(), "' via Eager)"); - if (num_retvals != node_data->outputs().size()) { + if (num_retvals != node_data->NumOutputs()) { return tensorflow::errors::Internal( "Unexpected number of outputs from EagerExecute"); } - TF_RETURN_IF_ERROR(node_data->PersistEagerOutputs(buffer_map, &retvals)); + TF_RETURN_IF_ERROR(node_data->PersistEagerOutputs(buffer_map)); return tensorflow::Status::OK(); } @@ -232,7 +266,7 @@ tensorflow::Status ExecuteFlexOp(tensorflow::EagerContext* eager_context, struct OpData { tensorflow::EagerContext* eager_context; BufferMap* buffer_map; - std::vector nodes; + std::vector> nodes; std::vector subgraph_inputs; std::vector subgraph_outputs; }; @@ -261,6 +295,8 @@ void* Init(TfLiteContext* context, const char* buffer, size_t length) { op_data->subgraph_inputs.push_back(tensor_index); } + op_data->nodes.reserve(params->nodes_to_replace->size); + CHECK(params->nodes_to_replace); tensorflow::Status status; for (auto node_index : TfLiteIntArrayView(params->nodes_to_replace)) { @@ -268,8 +304,8 @@ void* Init(TfLiteContext* context, const char* buffer, size_t length) { TfLiteRegistration* reg; context->GetNodeAndRegistration(context, node_index, &node, ®); - op_data->nodes.push_back(OpNode()); - OpNode& node_data = op_data->nodes.back(); + op_data->nodes.emplace_back(new OpNode(node->inputs, node->outputs)); + OpNode& node_data = *op_data->nodes.back(); node_data.set_index(node_index); node_data.set_name(""); @@ -277,9 +313,6 @@ void* Init(TfLiteContext* context, const char* buffer, size_t length) { status = node_data.InitializeNodeDef(node->custom_initial_data, node->custom_initial_data_size); if (!status.ok()) break; - - node_data.InitializeInputs(node->inputs); - node_data.InitializeOutputs(node->outputs); } if (ConvertStatus(context, status) != kTfLiteOk) { @@ -328,14 +361,14 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { } for (const auto& node_data : op_data->nodes) { - if (node_data.nodedef().op().empty()) { + if (node_data->nodedef().op().empty()) { context->ReportError(context, "Invalid NodeDef in Flex op '%s'", - node_data.name().c_str()); + node_data->name().c_str()); return kTfLiteError; } - for (int tensor_index : node_data.inputs()) { - ++tensor_ref_count[tensor_index]; + for (int i = 0; i < node_data->inputs().Size(); ++i) { + ++tensor_ref_count[node_data->inputs().TfLiteIndex(i)]; } } @@ -371,12 +404,12 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { } // Execute the TensorFlow Ops sequentially. - for (OpNode& node_data : op_data->nodes) { + for (auto& node_data : op_data->nodes) { SCOPED_TAGGED_OPERATOR_PROFILE( reinterpret_cast(context->profiler), - node_data.name().c_str(), node_data.index()); + node_data->name().c_str(), node_data->index()); - auto status = ExecuteFlexOp(eager_context, buffer_map, &node_data); + auto status = ExecuteFlexOp(eager_context, buffer_map, node_data.get()); TF_LITE_ENSURE_OK(context, ConvertStatus(context, status)); } -- GitLab From 962d8821ebb23e918bae3680bab5a7f6df32cd7f Mon Sep 17 00:00:00 2001 From: Michael Kuperstein Date: Fri, 4 Jan 2019 13:52:30 -0800 Subject: [PATCH 363/622] [XLA] Adapt HLO pipeline to scalar-index DS and DUS This and adapts HLO to work with the scalar DynamicSlice / DynamicUpdateSlice form. This means it also effectively switches the backends to use the new form. Also a bunch of test churn - tests that don't run optimizations (and, hence, don't trigger the splitter pass) needed to be converted. PiperOrigin-RevId: 227908542 --- .../xla/service/algebraic_simplifier.cc | 27 ++++----- .../xla/service/algebraic_simplifier_test.cc | 26 +++++---- .../xla/service/buffer_assignment_test.cc | 6 +- tensorflow/compiler/xla/service/cpu/BUILD | 18 +++--- .../compiler/xla/service/cpu/cpu_compiler.cc | 2 + tensorflow/compiler/xla/service/gpu/BUILD | 12 ++-- .../service/gpu/multi_output_fusion_test.cc | 5 +- .../xla/service/gpu/nvptx_compiler.cc | 2 + .../xla/service/hlo_creation_utils.cc | 37 ++++++++++-- .../xla/service/hlo_evaluator_typed_visitor.h | 22 +------- .../compiler/xla/service/hlo_verifier.cc | 19 +------ .../compiler/xla/service/hlo_verifier_test.cc | 5 +- .../compiler/xla/service/interpreter/BUILD | 9 +-- .../xla/service/interpreter/compiler.cc | 2 + .../xla/service/layout_assignment_test.cc | 7 ++- .../compiler/xla/service/pattern_matcher.h | 2 +- .../compiler/xla/service/shape_inference.cc | 10 ++-- .../compiler/xla/service/shape_inference.h | 4 +- tensorflow/compiler/xla/shape_util.cc | 1 + tensorflow/compiler/xla/tests/BUILD | 1 + tensorflow/compiler/xla/tests/test_utils.cc | 52 +++++++---------- .../compiler/xla/tests/test_utils_test.cc | 56 ++++++++++--------- 22 files changed, 162 insertions(+), 163 deletions(-) diff --git a/tensorflow/compiler/xla/service/algebraic_simplifier.cc b/tensorflow/compiler/xla/service/algebraic_simplifier.cc index 003cb7e791..5ac746c9f3 100644 --- a/tensorflow/compiler/xla/service/algebraic_simplifier.cc +++ b/tensorflow/compiler/xla/service/algebraic_simplifier.cc @@ -1380,6 +1380,9 @@ StatusOr AlgebraicSimplifierVisitor::OptimizeDotOfGather( // => output dimensions: DS ({M x N}, {0, start}, {M, 1}) => {M x 1}. bool lhs_is_dynamic_slice = lhs->opcode() == HloOpcode::kDynamicSlice; + HloDynamicSliceInstruction* dynamic_slice = + lhs_is_dynamic_slice ? Cast(lhs) + : Cast(rhs); // ctA: HloInstruction* left_operand = @@ -1397,8 +1400,6 @@ StatusOr AlgebraicSimplifierVisitor::OptimizeDotOfGather( HloInstruction::CreateDot(memoized_shape, left_operand, right_operand, dnums, dot->precision_config())); // Get pair {start, 0} or {0, start}. - HloInstruction* original_start_indices = - lhs_is_dynamic_slice ? lhs->mutable_operand(1) : rhs->mutable_operand(1); // Position of start: int index_of_non_zero_start = lhs_is_dynamic_slice ? 1 - lhs_contracting_dimension @@ -1407,23 +1408,19 @@ StatusOr AlgebraicSimplifierVisitor::OptimizeDotOfGather( int index_of_zero_start = 1 - index_of_non_zero_start; // Slice out start and 0 components and reorder if necessary. - auto indices_type = original_start_indices->shape().element_type(); + auto indices_type = dynamic_slice->operand(1)->shape().element_type(); Shape s_shape = ShapeUtil::MakeShape(indices_type, {1}); Shape d_shape = ShapeUtil::MakeShape(indices_type, {2}); HloInstruction* non_zero_start = - computation_->AddInstruction(HloInstruction::CreateSlice( - s_shape, original_start_indices, {index_of_non_zero_start}, - {index_of_non_zero_start + 1}, {1})); + dynamic_slice->mutable_operand(1 + index_of_non_zero_start); HloInstruction* zero_start = - computation_->AddInstruction(HloInstruction::CreateSlice( - s_shape, original_start_indices, {index_of_zero_start}, - {index_of_zero_start + 1}, {1})); - HloInstruction* new_start_indices = - lhs_is_dynamic_slice - ? computation_->AddInstruction(HloInstruction::CreateConcatenate( - d_shape, {non_zero_start, zero_start}, 0)) - : computation_->AddInstruction(HloInstruction::CreateConcatenate( - d_shape, {zero_start, non_zero_start}, 0)); + dynamic_slice->mutable_operand(1 + index_of_zero_start); + std::vector new_start_indices; + if (lhs_is_dynamic_slice) { + new_start_indices = {non_zero_start, zero_start}; + } else { + new_start_indices = {zero_start, non_zero_start}; + } // Build DynamicSlice(ctA x ctB). const int new_slice_m = lhs_is_dynamic_slice ? 1 : m; diff --git a/tensorflow/compiler/xla/service/algebraic_simplifier_test.cc b/tensorflow/compiler/xla/service/algebraic_simplifier_test.cc index 211c5bf05a..e39321f1f3 100644 --- a/tensorflow/compiler/xla/service/algebraic_simplifier_test.cc +++ b/tensorflow/compiler/xla/service/algebraic_simplifier_test.cc @@ -4535,14 +4535,17 @@ TEST_P(DotOfGatherSimplificationTest, ConstantRHS) { int32 start_row = (spec.lcd == 0) ? 0 : spec.s; int32 start_col = (spec.lcd == 0) ? spec.s : 0; - const auto start_indices = + std::vector start_indices = { builder.AddInstruction(HloInstruction::CreateConstant( - LiteralUtil::CreateR1({start_row, start_col}))); + LiteralUtil::CreateR0(start_row))), + builder.AddInstruction(HloInstruction::CreateConstant( + LiteralUtil::CreateR0(start_col)))}; int64 slice_row_size = (spec.lcd == 0) ? spec.k : 1; int64 slice_col_size = (spec.lcd == 0) ? 1 : spec.k; - Shape ds_shape = ShapeUtil::MakeShape(F32, {slice_row_size, slice_col_size}); + std::vector slice_sizes = {slice_row_size, slice_col_size}; + Shape ds_shape = ShapeUtil::MakeShape(F32, slice_sizes); auto* ds = builder.AddInstruction(HloInstruction::CreateDynamicSlice( - ds_shape, lhs, start_indices, {slice_row_size, slice_col_size})); + ds_shape, lhs, start_indices, slice_sizes)); int64 rhs_rows = (spec.rcd == 0) ? spec.k : spec.n; int64 rhs_cols = (spec.rcd == 0) ? spec.n : spec.k; @@ -4575,7 +4578,7 @@ TEST_P(DotOfGatherSimplificationTest, ConstantRHS) { } else { EXPECT_THAT(computation->root_instruction(), GmockMatch(m::DynamicSlice(m::Dot(m::Constant(), m::Constant()), - m::Concatenate()))); + m::Constant(), m::Constant()))); } } @@ -4613,14 +4616,17 @@ TEST_P(DotOfGatherSimplificationTest, ConstantLHS) { int32 start_row = (spec.rcd == 0) ? 0 : spec.s; int32 start_col = (spec.rcd == 0) ? spec.s : 0; - const auto start_indices = + std::vector start_indices = { + builder.AddInstruction(HloInstruction::CreateConstant( + LiteralUtil::CreateR0(start_row))), builder.AddInstruction(HloInstruction::CreateConstant( - LiteralUtil::CreateR1({start_row, start_col}))); + LiteralUtil::CreateR0(start_col)))}; int64 slice_row_size = (spec.rcd == 0) ? spec.k : 1; int64 slice_col_size = (spec.rcd == 0) ? 1 : spec.k; - Shape ds_shape = ShapeUtil::MakeShape(F32, {slice_row_size, slice_col_size}); + std::vector slice_sizes = {slice_row_size, slice_col_size}; + Shape ds_shape = ShapeUtil::MakeShape(F32, slice_sizes); auto* ds = builder.AddInstruction(HloInstruction::CreateDynamicSlice( - ds_shape, rhs, start_indices, {slice_row_size, slice_col_size})); + ds_shape, rhs, start_indices, slice_sizes)); DotDimensionNumbers dot_dnums; dot_dnums.add_lhs_contracting_dimensions(spec.lcd); @@ -4645,7 +4651,7 @@ TEST_P(DotOfGatherSimplificationTest, ConstantLHS) { } else { EXPECT_THAT(computation->root_instruction(), GmockMatch(m::DynamicSlice(m::Dot(m::Constant(), m::Constant()), - m::Concatenate()))); + m::Constant(), m::Constant()))); } } diff --git a/tensorflow/compiler/xla/service/buffer_assignment_test.cc b/tensorflow/compiler/xla/service/buffer_assignment_test.cc index 370fad7ffb..1b4e93a2f3 100644 --- a/tensorflow/compiler/xla/service/buffer_assignment_test.cc +++ b/tensorflow/compiler/xla/service/buffer_assignment_test.cc @@ -2486,9 +2486,9 @@ while_body { get-tuple-element.3 = s32[] get-tuple-element(state), index=0 constant.2 = s32[] constant(128) add.5 = s32[] add(get-tuple-element.3, constant.2) - constant.3 = s32[3]{0} constant({0, 0, 0}) - dynamic-update-slice.5 = f32[1280,1,128]{2,1,0} dynamic-update-slice(get-tuple-element.4, broadcast.6, constant.3) - dynamic-update-slice.9 = f32[1280,1,128]{2,1,0} dynamic-update-slice(dynamic-update-slice.5, broadcast.6, constant.3) + constant.3 = s32[] constant(0) + dynamic-update-slice.5 = f32[1280,1,128]{2,1,0} dynamic-update-slice(get-tuple-element.4, broadcast.6, constant.3, constant.3, constant.3) + dynamic-update-slice.9 = f32[1280,1,128]{2,1,0} dynamic-update-slice(dynamic-update-slice.5, broadcast.6, constant.3, constant.3, constant.3) ROOT tuple.85 = (s32[], s32[], s32[2]{0}, f32[1280,1,128]{2,1,0}) tuple(add.5, dynamic-update-slice.9) } diff --git a/tensorflow/compiler/xla/service/cpu/BUILD b/tensorflow/compiler/xla/service/cpu/BUILD index a4ca772c7c..5c72e1ff71 100644 --- a/tensorflow/compiler/xla/service/cpu/BUILD +++ b/tensorflow/compiler/xla/service/cpu/BUILD @@ -1,6 +1,14 @@ # Description: # LLVM-based CPU backend for XLA. +load("//tensorflow/compiler/xla:xla.bzl", "ORC_JIT_MEMORY_MAPPER_TARGETS") +load( + "//third_party/mkl:build_defs.bzl", + "mkl_deps", +) +load("//tensorflow:tensorflow.bzl", "tf_cc_binary", "tf_cc_test") +load(":build_defs.bzl", "runtime_copts") + licenses(["notice"]) # Apache 2.0 package( @@ -14,15 +22,6 @@ package_group( ], ) -load(":build_defs.bzl", "runtime_copts") -load("//tensorflow:tensorflow.bzl", "tf_cc_test") -load("//tensorflow:tensorflow.bzl", "tf_cc_binary") -load("//tensorflow/compiler/xla:xla.bzl", "ORC_JIT_MEMORY_MAPPER_TARGETS") -load( - "//third_party/mkl:build_defs.bzl", - "mkl_deps", -) - # Filegroup used to collect source files for dependency checking. filegroup( name = "c_srcs", @@ -114,6 +113,7 @@ cc_library( "//tensorflow/compiler/xla/service:conditional_simplifier", "//tensorflow/compiler/xla/service:convolution_group_converter", "//tensorflow/compiler/xla/service:dot_decomposer", + "//tensorflow/compiler/xla/service:dynamic_index_splitter", "//tensorflow/compiler/xla/service:executable", "//tensorflow/compiler/xla/service:flatten_call_graph", "//tensorflow/compiler/xla/service:hlo", diff --git a/tensorflow/compiler/xla/service/cpu/cpu_compiler.cc b/tensorflow/compiler/xla/service/cpu/cpu_compiler.cc index dbab839aee..91c64e45b9 100644 --- a/tensorflow/compiler/xla/service/cpu/cpu_compiler.cc +++ b/tensorflow/compiler/xla/service/cpu/cpu_compiler.cc @@ -69,6 +69,7 @@ limitations under the License. #include "tensorflow/compiler/xla/service/cpu/simple_orc_jit.h" #include "tensorflow/compiler/xla/service/dfs_hlo_visitor_with_default.h" #include "tensorflow/compiler/xla/service/dot_decomposer.h" +#include "tensorflow/compiler/xla/service/dynamic_index_splitter.h" #include "tensorflow/compiler/xla/service/flatten_call_graph.h" #include "tensorflow/compiler/xla/service/hlo.pb.h" #include "tensorflow/compiler/xla/service/hlo_computation.h" @@ -244,6 +245,7 @@ Status CpuCompiler::RunHloPassesThroughLayoutAssn( HloPassPipeline pipeline("HLO passes through layout assignment"); pipeline.AddInvariantChecker(/*layout_sensitive=*/false, /*allow_mixed_precision=*/false); + pipeline.AddPass(); pipeline.AddPass(); ReducePrecisionInsertion::AddPasses( diff --git a/tensorflow/compiler/xla/service/gpu/BUILD b/tensorflow/compiler/xla/service/gpu/BUILD index d10a11d096..5e81281bae 100644 --- a/tensorflow/compiler/xla/service/gpu/BUILD +++ b/tensorflow/compiler/xla/service/gpu/BUILD @@ -3,6 +3,11 @@ load("//tensorflow/compiler/xla/tests:build_defs.bzl", "xla_test") load("//tensorflow/compiler/xla:xla.bzl", "xla_proto_library") +load( + "//tensorflow/core:platform/default/build_config_root.bzl", + "tf_cuda_tests_tags", +) +load("//tensorflow:tensorflow.bzl", "tf_cc_test") licenses(["notice"]) # Apache 2.0 @@ -24,12 +29,6 @@ filegroup( ]), ) -load("//tensorflow:tensorflow.bzl", "tf_cc_test") -load( - "//tensorflow/core:platform/default/build_config_root.bzl", - "tf_cuda_tests_tags", -) - xla_proto_library( name = "backend_configs", srcs = ["backend_configs.proto"], @@ -700,6 +699,7 @@ cc_library( "//tensorflow/compiler/xla/service:call_inliner", "//tensorflow/compiler/xla/service:conditional_simplifier", "//tensorflow/compiler/xla/service:convolution_group_converter", + "//tensorflow/compiler/xla/service:dynamic_index_splitter", "//tensorflow/compiler/xla/service:executable", "//tensorflow/compiler/xla/service:flatten_call_graph", "//tensorflow/compiler/xla/service:hlo", diff --git a/tensorflow/compiler/xla/service/gpu/multi_output_fusion_test.cc b/tensorflow/compiler/xla/service/gpu/multi_output_fusion_test.cc index d16c87ba5c..40b87b16a1 100644 --- a/tensorflow/compiler/xla/service/gpu/multi_output_fusion_test.cc +++ b/tensorflow/compiler/xla/service/gpu/multi_output_fusion_test.cc @@ -628,8 +628,7 @@ TEST_F(MultiOutputFusionTest, MultiOutputFusionDUS) { p.1 = s32[1]{0} parameter(1) p.2 = f16[1,96,1024]{2,1,0} parameter(2) c.0 = s32[] constant(0) - pad = s32[3]{0} pad(p.1, c.0), padding=0_2 - ROOT %dynamic-update-slice = f16[50,96,1024]{2,1,0} dynamic-update-slice(p.0, p.2, pad) + ROOT %dynamic-update-slice = f16[50,96,1024]{2,1,0} dynamic-update-slice(p.0, p.2, p.1, c.0, c.0) } fusion.2 { @@ -638,7 +637,7 @@ TEST_F(MultiOutputFusionTest, MultiOutputFusionDUS) { p.2 = f16[1,96,1024]{2,1,0} parameter(2) c.0 = s32[] constant(0) pad = s32[3]{0} pad(p.1, c.0), padding=0_2 - ROOT %dynamic-update-slice = f16[50,96,1024]{2,1,0} dynamic-update-slice(p.0, p.2, pad) + ROOT %dynamic-update-slice = f16[50,96,1024]{2,1,0} dynamic-update-slice(p.0, p.2, p.1, c.0, c.0) } ENTRY entry { diff --git a/tensorflow/compiler/xla/service/gpu/nvptx_compiler.cc b/tensorflow/compiler/xla/service/gpu/nvptx_compiler.cc index cd369d5598..9be7609508 100644 --- a/tensorflow/compiler/xla/service/gpu/nvptx_compiler.cc +++ b/tensorflow/compiler/xla/service/gpu/nvptx_compiler.cc @@ -37,6 +37,7 @@ limitations under the License. #include "tensorflow/compiler/xla/service/call_inliner.h" #include "tensorflow/compiler/xla/service/conditional_simplifier.h" #include "tensorflow/compiler/xla/service/convolution_group_converter.h" +#include "tensorflow/compiler/xla/service/dynamic_index_splitter.h" #include "tensorflow/compiler/xla/service/flatten_call_graph.h" #include "tensorflow/compiler/xla/service/gpu/cudnn_batchnorm_rewriter.h" #include "tensorflow/compiler/xla/service/gpu/cudnn_conv_algorithm_picker.h" @@ -152,6 +153,7 @@ Status OptimizeHloModule(HloModule* hlo_module, se::StreamExecutor* stream_exec, HloPassPipeline pipeline("optimization"); pipeline.AddInvariantChecker(/*layout_sensitive=*/false, /*allow_mixed_precision=*/false); + pipeline.AddPass(); pipeline.AddPass(); ReducePrecisionInsertion::AddPasses( &pipeline, hlo_module->config().debug_options(), diff --git a/tensorflow/compiler/xla/service/hlo_creation_utils.cc b/tensorflow/compiler/xla/service/hlo_creation_utils.cc index 8cea95a73e..bb5d21c654 100644 --- a/tensorflow/compiler/xla/service/hlo_creation_utils.cc +++ b/tensorflow/compiler/xla/service/hlo_creation_utils.cc @@ -19,6 +19,7 @@ limitations under the License. #include "absl/strings/str_cat.h" #include "tensorflow/compiler/xla/literal.h" #include "tensorflow/compiler/xla/literal_util.h" +#include "tensorflow/compiler/xla/service/hlo_instruction.h" #include "tensorflow/compiler/xla/service/hlo_module.h" #include "tensorflow/compiler/xla/service/shape_inference.h" #include "tensorflow/compiler/xla/util.h" @@ -105,12 +106,26 @@ StatusOr MakeDynamicSliceHlo( absl::Span slice_sizes) { HloComputation* computation = operand->parent(); CHECK_EQ(computation, start_indices->parent()); + int64 rank = start_indices->shape().dimensions(0); + std::vector scalar_start_indices; + for (int i = 0; i < rank; ++i) { + // TODO(b/118437727): Update callers to provide scalars directly. + auto slice = computation->AddInstruction(HloInstruction::CreateSlice( + ShapeUtil::MakeShape(start_indices->shape().element_type(), {1}), + start_indices, {i}, {i + 1}, {1})); + scalar_start_indices.push_back( + computation->AddInstruction(HloInstruction::CreateReshape( + ShapeUtil::MakeShape(start_indices->shape().element_type(), {}), + slice))); + } + std::vector scalar_start_indices_shapes( + rank, ShapeUtil::MakeShape(start_indices->shape().element_type(), {})); TF_ASSIGN_OR_RETURN( Shape dynamic_slice_shape, ShapeInference::InferDynamicSliceShape( - operand->shape(), {start_indices->shape()}, slice_sizes)); + operand->shape(), scalar_start_indices_shapes, slice_sizes)); return computation->AddInstruction(HloInstruction::CreateDynamicSlice( - dynamic_slice_shape, operand, start_indices, slice_sizes)); + dynamic_slice_shape, operand, scalar_start_indices, slice_sizes)); } StatusOr MakeDynamicUpdateSliceHlo( @@ -119,12 +134,26 @@ StatusOr MakeDynamicUpdateSliceHlo( HloComputation* computation = operand->parent(); CHECK_EQ(computation, update->parent()); CHECK_EQ(computation, start_indices->parent()); + int64 rank = start_indices->shape().dimensions(0); + std::vector scalar_start_indices; + for (int i = 0; i < rank; ++i) { + // TODO(b/118437727): Update callers to provide scalars directly. + auto slice = computation->AddInstruction(HloInstruction::CreateSlice( + ShapeUtil::MakeShape(start_indices->shape().element_type(), {1}), + start_indices, {i}, {i + 1}, {1})); + scalar_start_indices.push_back( + computation->AddInstruction(HloInstruction::CreateReshape( + ShapeUtil::MakeShape(start_indices->shape().element_type(), {}), + slice))); + } + std::vector scalar_start_indices_shapes( + rank, ShapeUtil::MakeShape(start_indices->shape().element_type(), {})); TF_ASSIGN_OR_RETURN( Shape dynamic_update_slice_shape, ShapeInference::InferDynamicUpdateSliceShape( - operand->shape(), update->shape(), {start_indices->shape()})); + operand->shape(), update->shape(), scalar_start_indices_shapes)); return computation->AddInstruction(HloInstruction::CreateDynamicUpdateSlice( - dynamic_update_slice_shape, operand, update, start_indices)); + dynamic_update_slice_shape, operand, update, scalar_start_indices)); } StatusOr MakeBroadcastHlo( diff --git a/tensorflow/compiler/xla/service/hlo_evaluator_typed_visitor.h b/tensorflow/compiler/xla/service/hlo_evaluator_typed_visitor.h index cc5f6cc682..698b177310 100644 --- a/tensorflow/compiler/xla/service/hlo_evaluator_typed_visitor.h +++ b/tensorflow/compiler/xla/service/hlo_evaluator_typed_visitor.h @@ -1410,22 +1410,12 @@ class HloEvaluatorTypedVisitor : public DfsHloVisitorWithDefault { auto operand = dynamic_slice->operand(0); auto start_indices = dynamic_slice->operand(1); auto result_shape = dynamic_slice->shape(); - // TODO(b/118437727): Remove all of this nonsense. - // We may get an instruction without a parent module. In this case, assume - // scalar indices are not allowed. - bool allow_scalar_index = false; - if (dynamic_slice->GetModule() != nullptr) { - allow_scalar_index = dynamic_slice->GetModule() - ->config() - .debug_options() - .xla_allow_scalar_index_dynamic_ops(); - } TF_ASSIGN_OR_RETURN( auto inferred_return_shape, ShapeInference::InferDynamicSliceShape( operand->shape(), Cast(dynamic_slice)->index_shapes(), - dynamic_slice->dynamic_slice_sizes(), allow_scalar_index)); + dynamic_slice->dynamic_slice_sizes())); TF_RET_CHECK(ShapeUtil::Compatible(result_shape, inferred_return_shape)) << "return shape is set to: " << ShapeUtil::HumanString(result_shape) << " but is inferred to be: " @@ -1483,20 +1473,12 @@ class HloEvaluatorTypedVisitor : public DfsHloVisitorWithDefault { auto update = dynamic_update_slice->operand(1); auto start_indices = dynamic_update_slice->operand(2); auto result_shape = dynamic_update_slice->shape(); - bool allow_scalar_index = false; - if (dynamic_update_slice->GetModule() != nullptr) { - allow_scalar_index = dynamic_update_slice->GetModule() - ->config() - .debug_options() - .xla_allow_scalar_index_dynamic_ops(); - } TF_ASSIGN_OR_RETURN( auto inferred_return_shape, ShapeInference::InferDynamicUpdateSliceShape( operand->shape(), update->shape(), Cast(dynamic_update_slice) - ->index_shapes(), - allow_scalar_index)); + ->index_shapes())); TF_RET_CHECK(ShapeUtil::Compatible(result_shape, inferred_return_shape)) << "return shape is set to: " << ShapeUtil::HumanString(result_shape) << " but is inferred to be: " diff --git a/tensorflow/compiler/xla/service/hlo_verifier.cc b/tensorflow/compiler/xla/service/hlo_verifier.cc index 9274d42280..2c69c27ce5 100644 --- a/tensorflow/compiler/xla/service/hlo_verifier.cc +++ b/tensorflow/compiler/xla/service/hlo_verifier.cc @@ -504,38 +504,23 @@ Status ShapeVerifier::HandleSlice(HloInstruction* slice) { } Status ShapeVerifier::HandleDynamicSlice(HloInstruction* dynamic_slice) { - const DebugOptions& debug_options = - dynamic_slice->GetModule()->config().debug_options(); - const bool allow_scalar_indices = - debug_options.xla_allow_scalar_index_dynamic_ops(); - if (!allow_scalar_indices) { - TF_RETURN_IF_ERROR(CheckOperandCount(dynamic_slice, 2)); - } return CheckShape( dynamic_slice, ShapeInference::InferDynamicSliceShape( dynamic_slice->operand(0)->shape(), Cast(dynamic_slice)->index_shapes(), - dynamic_slice->dynamic_slice_sizes(), allow_scalar_indices)); + dynamic_slice->dynamic_slice_sizes())); } Status ShapeVerifier::HandleDynamicUpdateSlice( HloInstruction* dynamic_update_slice) { - const DebugOptions& debug_options = - dynamic_update_slice->GetModule()->config().debug_options(); - const bool allow_scalar_indices = - debug_options.xla_allow_scalar_index_dynamic_ops(); - if (!allow_scalar_indices) { - TF_RETURN_IF_ERROR(CheckOperandCount(dynamic_update_slice, 3)); - } return CheckShape( dynamic_update_slice, ShapeInference::InferDynamicUpdateSliceShape( dynamic_update_slice->operand(0)->shape(), dynamic_update_slice->operand(1)->shape(), Cast(dynamic_update_slice) - ->index_shapes(), - allow_scalar_indices)); + ->index_shapes())); } Status ShapeVerifier::HandleTuple(HloInstruction* tuple) { diff --git a/tensorflow/compiler/xla/service/hlo_verifier_test.cc b/tensorflow/compiler/xla/service/hlo_verifier_test.cc index 9b2d884f79..d27c62a879 100644 --- a/tensorflow/compiler/xla/service/hlo_verifier_test.cc +++ b/tensorflow/compiler/xla/service/hlo_verifier_test.cc @@ -450,8 +450,9 @@ TEST_F(HloVerifierTestLayoutSensitive, SliceWithLayoutChangeNotAllowed) { HloModule SliceWithLayoutChange ENTRY SliceWithLayoutChange { par0 = f32[4,5]{0,1} parameter(0) - par1 = s32[2] parameter(1) - ROOT dslice0 = f32[3,4]{1,0} dynamic-slice(par0, par1), + par1 = s32[] parameter(1) + par2 = s32[] parameter(2) + ROOT dslice0 = f32[3,4]{1,0} dynamic-slice(par0, par1, par2), dynamic_slice_sizes={3,4} } )"; diff --git a/tensorflow/compiler/xla/service/interpreter/BUILD b/tensorflow/compiler/xla/service/interpreter/BUILD index 8999f38ccd..2e1d2cfbda 100644 --- a/tensorflow/compiler/xla/service/interpreter/BUILD +++ b/tensorflow/compiler/xla/service/interpreter/BUILD @@ -1,12 +1,12 @@ -licenses(["notice"]) # Apache 2.0 - -package(default_visibility = ["//visibility:public"]) - load( "//tensorflow/core:platform/default/build_config_root.bzl", "if_static", ) +licenses(["notice"]) # Apache 2.0 + +package(default_visibility = ["//visibility:public"]) + cc_library( name = "interpreter_transfer_manager", srcs = ["interpreter_transfer_manager.cc"], @@ -35,6 +35,7 @@ cc_library( "//tensorflow/compiler/xla/service:batchnorm_expander", "//tensorflow/compiler/xla/service:compiler", "//tensorflow/compiler/xla/service:computation_placer", + "//tensorflow/compiler/xla/service:dynamic_index_splitter", "//tensorflow/compiler/xla/service:executable", "//tensorflow/compiler/xla/service:flatten_call_graph", "//tensorflow/compiler/xla/service:hlo", diff --git a/tensorflow/compiler/xla/service/interpreter/compiler.cc b/tensorflow/compiler/xla/service/interpreter/compiler.cc index 69f611a094..4818b2dae0 100644 --- a/tensorflow/compiler/xla/service/interpreter/compiler.cc +++ b/tensorflow/compiler/xla/service/interpreter/compiler.cc @@ -21,6 +21,7 @@ limitations under the License. #include "absl/memory/memory.h" #include "tensorflow/compiler/xla/service/algebraic_simplifier.h" #include "tensorflow/compiler/xla/service/computation_placer.h" +#include "tensorflow/compiler/xla/service/dynamic_index_splitter.h" #include "tensorflow/compiler/xla/service/flatten_call_graph.h" #include "tensorflow/compiler/xla/service/hlo_constant_folding.h" #include "tensorflow/compiler/xla/service/hlo_cse.h" @@ -44,6 +45,7 @@ namespace interpreter { Status InterpreterCompiler::RunHloOptimization(HloModule* hlo_module) { HloPassPipeline pipeline("Interpreter"); + pipeline.AddPass(); pipeline.AddPass( hlo_module->mutable_entry_computation_layout(), LayoutAssignment::InstructionCanChangeLayout); diff --git a/tensorflow/compiler/xla/service/layout_assignment_test.cc b/tensorflow/compiler/xla/service/layout_assignment_test.cc index 387b385157..c8cf3c47d3 100644 --- a/tensorflow/compiler/xla/service/layout_assignment_test.cc +++ b/tensorflow/compiler/xla/service/layout_assignment_test.cc @@ -960,8 +960,9 @@ TEST_F(LayoutAssignmentTest, CopyDSliceOperandToAvoidImplicitLayoutChange) { ENTRY CopyDSliceOperandToAvoidImplicitLayoutChange { par0 = f32[3,4]{1,0} parameter(0) par1 = f32[4,5]{0,1} parameter(1) - par2 = s32[2] parameter(2) - dslice0 = f32[3,4] dynamic-slice(par1, par2), dynamic_slice_sizes={3,4} + par2 = s32[] parameter(2) + par3 = s32[] parameter(3) + dslice0 = f32[3,4] dynamic-slice(par1, par2, par3), dynamic_slice_sizes={3,4} ROOT add0 = f32[3,4]{1,0} add(par0,dslice0) } )"; @@ -982,7 +983,7 @@ TEST_F(LayoutAssignmentTest, CopyDSliceOperandToAvoidImplicitLayoutChange) { m::Parameter(), m::DynamicSlice( m::Copy(m::Parameter(1)).WithShapeEqualTo(&shape_copy), - m::Parameter(2))))); + m::Parameter(2), m::Parameter(3))))); } TEST_F(LayoutAssignmentTest, CopyConcatOperandToAvoidImplicitLayoutChange) { diff --git a/tensorflow/compiler/xla/service/pattern_matcher.h b/tensorflow/compiler/xla/service/pattern_matcher.h index 802b2c0c85..9e3d106021 100644 --- a/tensorflow/compiler/xla/service/pattern_matcher.h +++ b/tensorflow/compiler/xla/service/pattern_matcher.h @@ -2118,7 +2118,6 @@ XLA_BINOP_PATTERN(Divide) XLA_BINOP_PATTERN(Complex) XLA_BINOP_PATTERN(Convolution) XLA_BINOP_PATTERN(Dot) -XLA_BINOP_PATTERN(DynamicSlice) XLA_COMMUTATIVE_BINOP_PATTERN(Eq) XLA_BINOP_PATTERN(Gather) XLA_BINOP_PATTERN(Ge) @@ -2235,6 +2234,7 @@ inline auto WithOperands(Matcher&& m, int64 operand_num, FirstArg&& first_arg, XLA_VARIADIC_OP_PATTERN(AfterAll); XLA_VARIADIC_OP_PATTERN(Concatenate); XLA_VARIADIC_OP_PATTERN(CustomCall); +XLA_VARIADIC_OP_PATTERN(DynamicSlice) XLA_VARIADIC_OP_PATTERN(Map) XLA_VARIADIC_OP_PATTERN(Reduce); XLA_VARIADIC_OP_PATTERN(Sort); diff --git a/tensorflow/compiler/xla/service/shape_inference.cc b/tensorflow/compiler/xla/service/shape_inference.cc index d711c8ef0d..b729dd7660 100644 --- a/tensorflow/compiler/xla/service/shape_inference.cc +++ b/tensorflow/compiler/xla/service/shape_inference.cc @@ -2099,15 +2099,15 @@ ShapeInference::InferDegenerateDimensionBroadcastShape(HloOpcode operation, } const Shape& start_indices_shape = start_index_shapes[0]; - TF_RETURN_IF_ERROR( - ExpectArray(start_indices_shape, "start indices of dynamic slice")); - VLOG(2) << StrFormat( "slicing shape %s at dynamic start_indices %s with slice_sizes={%s}", ShapeUtil::HumanString(operand_shape), ShapeUtil::HumanString(start_indices_shape), StrJoin(slice_sizes, ", ")); + TF_RETURN_IF_ERROR( + ExpectArray(start_indices_shape, "start indices of dynamic slice")); + if (start_indices_shape.rank() != 1) { return InvalidArgument( "Dynamic slice start indices of rank %d must be rank1.", @@ -2151,7 +2151,7 @@ ShapeInference::InferDegenerateDimensionBroadcastShape(HloOpcode operation, "Dynamic slice start indices must be of integral type."); } for (const Shape& index_shape : start_index_shapes) { - if (!ShapeUtil::Equal(first_index_shape, index_shape)) { + if (!ShapeUtil::Compatible(first_index_shape, index_shape)) { return InvalidArgument( "Dynamic slice start indices must all have the same shape, got " "mismatching indices with shapes %s and %s.", @@ -2258,7 +2258,7 @@ ShapeInference::InferDegenerateDimensionBroadcastShape(HloOpcode operation, "Dynamic update slice start indices must be of integral type."); } for (const Shape& index_shape : start_index_shapes) { - if (!ShapeUtil::Equal(first_index_shape, index_shape)) { + if (!ShapeUtil::Compatible(first_index_shape, index_shape)) { return InvalidArgument( "Dynamic update slice start indices must all have the same " "shape, got mismatching indices with shapes %s and %s.", diff --git a/tensorflow/compiler/xla/service/shape_inference.h b/tensorflow/compiler/xla/service/shape_inference.h index e440e364c8..7d39ef38e0 100644 --- a/tensorflow/compiler/xla/service/shape_inference.h +++ b/tensorflow/compiler/xla/service/shape_inference.h @@ -177,14 +177,14 @@ class ShapeInference { // in 'slice_sizes', with dynamic start indices shape 'start_indices_shape'. static StatusOr InferDynamicSliceShape( const Shape& operand_shape, absl::Span start_index_shapes, - absl::Span slice_sizes, bool allow_scalar_indices = false); + absl::Span slice_sizes, bool allow_scalar_indices = true); // Infers the shape produced by a dynamic update slice operation based // on the shape of operand and update. static StatusOr InferDynamicUpdateSliceShape( const Shape& operand_shape, const Shape& update_shape, absl::Span start_index_shapes, - bool allow_scalar_indices = false); + bool allow_scalar_indices = true); // Infers the shape produced by doing a compile-time-constant indexing into // the given input shape. This is essential for operations on tuples, because diff --git a/tensorflow/compiler/xla/shape_util.cc b/tensorflow/compiler/xla/shape_util.cc index 4726ee6a5b..7f359311c3 100644 --- a/tensorflow/compiler/xla/shape_util.cc +++ b/tensorflow/compiler/xla/shape_util.cc @@ -112,6 +112,7 @@ bool CompareShapes(const Shape& lhs, const Shape& rhs, bool compare_layouts, if (compare_layouts) { if (lhs.layout().format() != rhs.layout().format()) { + VLOG(3) << "CompareShapes: lhs layout format != rhs layout format"; return false; } if (LayoutUtil::IsDenseArray(lhs)) { diff --git a/tensorflow/compiler/xla/tests/BUILD b/tensorflow/compiler/xla/tests/BUILD index 2c11ba4283..9433264917 100644 --- a/tensorflow/compiler/xla/tests/BUILD +++ b/tensorflow/compiler/xla/tests/BUILD @@ -71,6 +71,7 @@ cc_library( "//tensorflow/compiler/xla:shape_util", "//tensorflow/compiler/xla:xla_data_proto", "//tensorflow/compiler/xla/service:hlo", + "//tensorflow/compiler/xla/service:hlo_casting_utils", "//tensorflow/compiler/xla/service:hlo_dataflow_analysis", "//tensorflow/compiler/xla/service:hlo_verifier", "//tensorflow/compiler/xla/service:transfer_manager", diff --git a/tensorflow/compiler/xla/tests/test_utils.cc b/tensorflow/compiler/xla/tests/test_utils.cc index 18df41c117..95c89b0ba6 100644 --- a/tensorflow/compiler/xla/tests/test_utils.cc +++ b/tensorflow/compiler/xla/tests/test_utils.cc @@ -19,6 +19,7 @@ limitations under the License. #include "absl/memory/memory.h" #include "tensorflow/compiler/xla/literal_util.h" #include "tensorflow/compiler/xla/primitive_util.h" +#include "tensorflow/compiler/xla/service/hlo_casting_utils.h" #include "tensorflow/compiler/xla/service/hlo_dataflow_analysis.h" #include "tensorflow/compiler/xla/service/hlo_verifier.h" #include "tensorflow/compiler/xla/service/transfer_manager.h" @@ -274,16 +275,9 @@ bool NeedsInitValue(const HloUse& use) { // Generate random values that are constrained to the input_shape minus the // output_shape so as not to produce wrapping slices, for instance. -Literal MakeRandomIndex(absl::Span index_space, - std::minstd_rand0* engine) { - std::vector start_indices(index_space.size()); - if (engine != nullptr) { - for (int i = 0; i < index_space.size(); ++i) { - std::uniform_int_distribution generator(0, index_space[i]); - start_indices[i] = generator(*engine); - } - } - return LiteralUtil::CreateR1(start_indices); +Literal MakeRandomIndex(int64 index_bound, std::minstd_rand0* engine) { + std::uniform_int_distribution generator(0, index_bound); + return LiteralUtil::CreateR0(generator(*engine)); } // Use dataflow analysis on each parameter to see if there are uses that would @@ -300,8 +294,8 @@ std::vector FindConstrainedUses( HloInstruction* instruction = use.instruction; const HloOpcode opcode = instruction->opcode(); const int64 op_num = use.operand_number; - if ((opcode == HloOpcode::kDynamicSlice && op_num == 1) || - (opcode == HloOpcode::kDynamicUpdateSlice && op_num == 2)) { + if ((opcode == HloOpcode::kDynamicSlice && op_num >= 1) || + (opcode == HloOpcode::kDynamicUpdateSlice && op_num >= 2)) { constrained_uses.push_back(instruction); } else if (opcode == HloOpcode::kFusion) { const HloInstruction* const to_analyze = @@ -336,7 +330,7 @@ std::vector FindConstrainedUses( StatusOr CreateLiteralForConstrainedUses( const absl::Span constrained_uses, const HloInstruction& param, std::minstd_rand0* engine) { - std::vector index_space; + int64 index_bound = INT64_MAX; bool no_duplicates = false; bool needs_constant = false; ConstantType constant_type = ConstantType::kUnknown; @@ -348,19 +342,16 @@ StatusOr CreateLiteralForConstrainedUses( const Shape& slice_shape = use->opcode() == HloOpcode::kDynamicSlice ? use->shape() : use->operand(1)->shape(); - const int64 rank = indexed_shape.rank(); - if (!index_space.empty()) { - TF_RET_CHECK(rank == index_space.size()); - for (int64 i = 0; i < rank; ++i) { - index_space[i] = std::min( - index_space[i], ShapeUtil::GetDimension(indexed_shape, i) - - ShapeUtil::GetDimension(slice_shape, i)); - } - } else { - index_space.resize(rank); - for (int64 i = 0; i < rank; ++i) { - index_space[i] = ShapeUtil::GetDimension(indexed_shape, i) - - ShapeUtil::GetDimension(slice_shape, i); + const int64 first_index = + Cast(use)->first_index_operand_number(); + for (int64 operand = first_index; operand < use->operand_count(); + ++operand) { + if (use->operand(operand) == ¶m) { + index_bound = std::min( + index_bound, + ShapeUtil::GetDimension(indexed_shape, operand - first_index) - + ShapeUtil::GetDimension(slice_shape, + operand - first_index)); } } break; @@ -388,16 +379,13 @@ StatusOr CreateLiteralForConstrainedUses( } int constraint_count = 0; constraint_count += no_duplicates ? 1 : 0; - constraint_count += !index_space.empty() ? 1 : 0; + constraint_count += (index_bound != INT64_MAX) ? 1 : 0; constraint_count += needs_constant ? 1 : 0; if (constraint_count > 1) { return Unimplemented("Conflicting operand generation constraints."); } - if (!index_space.empty()) { - // constrained_uses looks through bitcasts, so param and indexed_space may - // not have the same shape. (For example, param might be an R0 while - // indexed_space might have size 1.) - return MakeRandomIndex(index_space, engine) + if (index_bound != INT64_MAX) { + return MakeRandomIndex(index_bound, engine) .Reshape(param.shape().dimensions()); } else if (needs_constant) { switch (constant_type) { diff --git a/tensorflow/compiler/xla/tests/test_utils_test.cc b/tensorflow/compiler/xla/tests/test_utils_test.cc index 4b4f164d1b..591d6c1922 100644 --- a/tensorflow/compiler/xla/tests/test_utils_test.cc +++ b/tensorflow/compiler/xla/tests/test_utils_test.cc @@ -79,25 +79,26 @@ XLA_TEST_F(TestUtilsTest, MultipleIndexSpacesForDynamicSlices) { R"(HloModule index_space_module ENTRY IndexSpace { - index_param = s32[3]{0} parameter(0) - array_param.1 = f32[123,4,789]{0,1,2} parameter(1) - array_param.2 = f32[3,3000,5]{0,1,2} parameter(2) - dynamic-slice.1 = f32[1,2,3] dynamic-slice(array_param.1, index_param), dynamic_slice_sizes={1,2,3} - ROOT dynamic-slice.2 = f32[3,2,2] dynamic-slice(array_param.2, index_param), dynamic_slice_sizes={3,2,2} + index_param.0 = s32[] parameter(0) + index_param.1 = s32[] parameter(1) + index_param.2 = s32[] parameter(2) + array_param.1 = f32[123,4,789]{0,1,2} parameter(3) + array_param.2 = f32[3,3000,5]{0,1,2} parameter(4) + dynamic-slice.1 = f32[1,2,3] dynamic-slice(array_param.1, index_param.0, index_param.1, index_param.2), dynamic_slice_sizes={1,2,3} + ROOT dynamic-slice.2 = f32[3,2,2] dynamic-slice(array_param.2, index_param.0, index_param.1, index_param.2), dynamic_slice_sizes={3,2,2} })") .ValueOrDie(); TF_ASSERT_OK_AND_ASSIGN(std::vector args, MakeFakeArguments(module.get())); - ASSERT_EQ(args.size(), 3); - const Literal& index_arg = args[0]; + ASSERT_EQ(args.size(), 5); - EXPECT_EQ(index_arg.Get({0}), 0); + EXPECT_EQ(args[0].Get({}), 0); - EXPECT_GE(index_arg.Get({1}), 0); - EXPECT_LE(index_arg.Get({1}), 2); + EXPECT_GE(args[1].Get({}), 0); + EXPECT_LE(args[0].Get({}), 2); - EXPECT_GE(index_arg.Get({2}), 0); - EXPECT_LE(index_arg.Get({2}), 3); + EXPECT_GE(args[2].Get({}), 0); + EXPECT_LE(args[2].Get({}), 3); } XLA_TEST_F(TestUtilsTest, MultipleIndexSpacesForDynamicUpdateSlices) { @@ -105,28 +106,29 @@ XLA_TEST_F(TestUtilsTest, MultipleIndexSpacesForDynamicUpdateSlices) { R"(HloModule index_space_module ENTRY IndexSpace { - index_param = s32[3]{0} parameter(0) - array_param.1 = f32[123,4,789]{0,1,2} parameter(1) - array_param.2 = f32[3,3000,5]{0,1,2} parameter(2) - update_param.1 = f32[1,2,3]{0,1,2} parameter(3) - update_param.2 = f32[3,2,2]{0,1,2} parameter(4) - - dynamic-update-slice.1 = f32[123,4,789] dynamic-update-slice(array_param.1, update_param.1, index_param) - ROOT dynamic-update-slice.2 = f32[3,3000,5] dynamic-update-slice(array_param.2, update_param.2, index_param) + index_param.0 = s32[] parameter(0) + index_param.1 = s32[] parameter(1) + index_param.2 = s32[] parameter(2) + array_param.1 = f32[123,4,789]{0,1,2} parameter(3) + array_param.2 = f32[3,3000,5]{0,1,2} parameter(4) + update_param.1 = f32[1,2,3]{0,1,2} parameter(5) + update_param.2 = f32[3,2,2]{0,1,2} parameter(6) + + dynamic-update-slice.1 = f32[123,4,789] dynamic-update-slice(array_param.1, update_param.1, index_param.0, index_param.1, index_param.2) + ROOT dynamic-update-slice.2 = f32[3,3000,5] dynamic-update-slice(array_param.2, update_param.2, index_param.0, index_param.1, index_param.2) })") .ValueOrDie(); TF_ASSERT_OK_AND_ASSIGN(std::vector args, MakeFakeArguments(module.get())); - ASSERT_EQ(args.size(), 5); - const Literal& index_arg = args[0]; + ASSERT_EQ(args.size(), 7); - EXPECT_EQ(index_arg.Get({0}), 0); + EXPECT_EQ(args[0].Get({}), 0); - EXPECT_GE(index_arg.Get({1}), 0); - EXPECT_LE(index_arg.Get({1}), 2); + EXPECT_GE(args[1].Get({}), 0); + EXPECT_LE(args[0].Get({}), 2); - EXPECT_GE(index_arg.Get({2}), 0); - EXPECT_LE(index_arg.Get({2}), 3); + EXPECT_GE(args[2].Get({}), 0); + EXPECT_LE(args[2].Get({}), 3); } XLA_TEST_F(TestUtilsTest, NoDuplicatesFloats) { -- GitLab From efe565bc0981e80a52a97f3961cfba3e87023b42 Mon Sep 17 00:00:00 2001 From: Allen Lavoie Date: Fri, 4 Jan 2019 14:08:26 -0800 Subject: [PATCH 364/622] Make the initial tf.train.Checkpoint.restore() read Tensors in a batch Should be faster when reading from distributed file systems. Does not affect cases where restore-on-create is necessary, but as long as variable objects have been created and tracked before restore() their reads should be batched together. PiperOrigin-RevId: 227911381 --- .../python/training/checkpointable/util.py | 22 +++------ .../training/saving/functional_saver.py | 45 +++++++++++-------- 2 files changed, 31 insertions(+), 36 deletions(-) diff --git a/tensorflow/python/training/checkpointable/util.py b/tensorflow/python/training/checkpointable/util.py index c890a7f440..a45263f5c6 100644 --- a/tensorflow/python/training/checkpointable/util.py +++ b/tensorflow/python/training/checkpointable/util.py @@ -176,25 +176,13 @@ class _CheckpointRestoreCoordinator(object): raise AssertionError( ("Saveable keys changed when validating. Got back %s, was " "expecting %s") % (tensor_saveables.keys(), validated_names)) - for saveable in validated_saveables: - if saveable.device: - device = saveable_object_util.set_cpu0(saveable.device) - else: - device = None - with ops.device(device): - tensors = [] - for spec in saveable.specs: - tensors.append( - io_ops.restore_v2( - self.save_path_tensor, - [spec.name], - [spec.slice_spec], - [spec.dtype])[0]) - restore_op = saveable.restore(tensors, restored_shapes=None) - if not context.executing_eagerly(): + new_restore_ops = functional_saver.restore_from_saveable_objects( + self.save_path_tensor, validated_saveables) + if not context.executing_eagerly(): + restore_ops.extend(new_restore_ops) + for saveable, restore_op in zip(validated_saveables, new_restore_ops): assert saveable.name not in self.restore_ops_by_name self.restore_ops_by_name[saveable.name] = restore_op - restore_ops.append(restore_op) return restore_ops diff --git a/tensorflow/python/training/saving/functional_saver.py b/tensorflow/python/training/saving/functional_saver.py index 51f618ddd3..4ff2742c2f 100644 --- a/tensorflow/python/training/saving/functional_saver.py +++ b/tensorflow/python/training/saving/functional_saver.py @@ -107,25 +107,32 @@ class Saver(object): A scalar string Tensor containing `file_prefix` with control dependencies on the restore ops. """ - restore_specs = [] - tensor_structure = [] - for saveable in self._saveable_objects: - saveable_tensor_structure = [] - tensor_structure.append(saveable_tensor_structure) - for spec in saveable.specs: - saveable_tensor_structure.append(spec.name) - restore_specs.append((spec.name, spec.slice_spec, spec.dtype)) - tensor_names, tensor_slices, tensor_dtypes = zip(*restore_specs) - with ops.device("cpu:0"): - restored_tensors = io_ops.restore_v2( - file_prefix, tensor_names, tensor_slices, tensor_dtypes) - structured_restored_tensors = nest.pack_sequence_as( - tensor_structure, restored_tensors) - restore_ops = [] - for saveable, restored_tensors in zip(self._saveable_objects, - structured_restored_tensors): - restore_ops.append(saveable.restore(restored_tensors, - restored_shapes=None)) + restore_ops = restore_from_saveable_objects( + file_prefix, self._saveable_objects) with ops.device("cpu:0"): with ops.control_dependencies(restore_ops): return array_ops.identity(file_prefix) + + +def restore_from_saveable_objects(file_prefix, saveable_objects): + """Reads from a checkpoint and returns restore ops for `saveable_objects`s.""" + restore_specs = [] + tensor_structure = [] + for saveable in saveable_objects: + saveable_tensor_structure = [] + tensor_structure.append(saveable_tensor_structure) + for spec in saveable.specs: + saveable_tensor_structure.append(spec.name) + restore_specs.append((spec.name, spec.slice_spec, spec.dtype)) + tensor_names, tensor_slices, tensor_dtypes = zip(*restore_specs) + with ops.device("cpu:0"): + restored_tensors = io_ops.restore_v2( + file_prefix, tensor_names, tensor_slices, tensor_dtypes) + structured_restored_tensors = nest.pack_sequence_as( + tensor_structure, restored_tensors) + restore_ops = [] + for saveable, restored_tensors in zip(saveable_objects, + structured_restored_tensors): + restore_ops.append(saveable.restore(restored_tensors, + restored_shapes=None)) + return restore_ops -- GitLab From 41e333f0193f89bc889a31f2a32b4119261aa222 Mon Sep 17 00:00:00 2001 From: Jiri Simsa Date: Fri, 4 Jan 2019 14:10:40 -0800 Subject: [PATCH 365/622] [tf.data] Adding prefetching of input iterators for parallel interleave. PiperOrigin-RevId: 227911701 --- .../data/parallel_interleave_dataset_op.cc | 752 +++++++++++------- .../data/kernel_tests/interleave_test.py | 123 +-- 2 files changed, 511 insertions(+), 364 deletions(-) diff --git a/tensorflow/core/kernels/data/parallel_interleave_dataset_op.cc b/tensorflow/core/kernels/data/parallel_interleave_dataset_op.cc index 315f96d4b5..dc2663d1e0 100644 --- a/tensorflow/core/kernels/data/parallel_interleave_dataset_op.cc +++ b/tensorflow/core/kernels/data/parallel_interleave_dataset_op.cc @@ -14,6 +14,7 @@ limitations under the License. ==============================================================================*/ #include #include +#include #include #include "tensorflow/core/common_runtime/function.h" @@ -21,6 +22,7 @@ limitations under the License. #include "tensorflow/core/framework/partial_tensor_shape.h" #include "tensorflow/core/framework/stats_aggregator.h" #include "tensorflow/core/framework/tensor.h" +#include "tensorflow/core/framework/types.h" #include "tensorflow/core/kernels/data/captured_function.h" #include "tensorflow/core/kernels/data/dataset_utils.h" #include "tensorflow/core/lib/core/threadpool.h" @@ -43,12 +45,7 @@ namespace { // // Furthermore, this class favors modularity over extended functionality. In // particular, it refrains from implementing configurable buffering of output -// elements and prefetching of input iterators, relying on other parts of -// tf.data to provide this functionality if necessary. -// -// The above design choices were made with automated optimizations in mind, -// isolating the degree of parallelism as the single tunable knob of this -// implementation. +// elements and prefetching of input iterators. class ParallelInterleaveDatasetOp : public UnaryDatasetOpKernel { public: explicit ParallelInterleaveDatasetOp(OpKernelConstruction* ctx) @@ -237,27 +234,20 @@ class ParallelInterleaveDatasetOp : public UnaryDatasetOpKernel { Status GetNextInternal(IteratorContext* ctx, std::vector* out_tensors, bool* end_of_sequence) override { - std::shared_ptr result; - do { - result.reset(); - { - mutex_lock l(*mu_); - EnsureRunnerThreadStarted(ctx); - while (ShouldWait(&result)) { - RecordStop(ctx); - cond_var_->wait(l); - RecordStart(ctx); - } - if (!result) { - *end_of_sequence = true; - return Status::OK(); - } + std::shared_ptr result; + { + mutex_lock l(*mu_); + EnsureThreadsStarted(ctx); + while (!Consume(&result)) { + RecordStop(ctx); + cond_var_->wait(l); + RecordStart(ctx); } - RecordStop(ctx); - result->notification.WaitForNotification(); - RecordStart(ctx); - } while (result->skip); - + } + if (!result) { + *end_of_sequence = true; + return Status::OK(); + } if (result->status.ok()) { *out_tensors = std::move(result->return_values); RecordBufferDequeue(ctx, *out_tensors); @@ -281,37 +271,22 @@ class ParallelInterleaveDatasetOp : public UnaryDatasetOpKernel { while (num_calls_ > 0) { cond_var_->wait(l); } - CHECK_EQ(num_calls_, 0); + DCHECK_EQ(num_calls_, 0); TF_RETURN_IF_ERROR(SaveInput(writer, input_impl_)); - TF_RETURN_IF_ERROR(writer->WriteScalar( - full_name("invocation_results.size"), invocation_results_.size())); - for (size_t i = 0; i < invocation_results_.size(); i++) { - std::shared_ptr result = invocation_results_[i]; - TF_RETURN_IF_ERROR(WriteStatusLocked(writer, i, result->status)); - TF_RETURN_IF_ERROR(writer->WriteScalar( - full_name(strings::StrCat("invocation_results[", i, "].size")), - result->return_values.size())); - for (size_t j = 0; j < result->return_values.size(); j++) { - TF_RETURN_IF_ERROR(writer->WriteTensor( - full_name( - strings::StrCat("invocation_results[", i, "][", j, "]")), - result->return_values[j])); - } - if (result->skip) { - TF_RETURN_IF_ERROR(writer->WriteScalar( - full_name(strings::StrCat("invocation_results[", i, "].skip")), - "")); - } - } + TF_RETURN_IF_ERROR( + writer->WriteScalar(full_name("block_index"), block_index_)); TF_RETURN_IF_ERROR( writer->WriteScalar(full_name("cycle_index"), cycle_index_)); if (end_of_input_) { TF_RETURN_IF_ERROR( writer->WriteScalar(full_name("end_of_input"), "")); } + TF_RETURN_IF_ERROR(writer->WriteScalar(full_name("element_id_counter"), + element_id_counter_)); TF_RETURN_IF_ERROR( writer->WriteScalar(full_name("num_open"), num_open_)); TF_RETURN_IF_ERROR(WriteCurrentElements(writer)); + TF_RETURN_IF_ERROR(WriteFutureElements(writer)); return Status::OK(); } @@ -319,114 +294,268 @@ class ParallelInterleaveDatasetOp : public UnaryDatasetOpKernel { IteratorStateReader* reader) override { mutex_lock l(*mu_); TF_RETURN_IF_ERROR(RestoreInput(ctx, reader, input_impl_)); - int64 invocation_results_size; - TF_RETURN_IF_ERROR(reader->ReadScalar( - full_name("invocation_results.size"), &invocation_results_size)); - for (size_t i = 0; i < invocation_results_size; i++) { - std::shared_ptr result(new InvocationResult()); - invocation_results_.push_back(result); - TF_RETURN_IF_ERROR(ReadStatusLocked(reader, i, &result->status)); - size_t num_return_values; - { - int64 size; - TF_RETURN_IF_ERROR(reader->ReadScalar( - full_name(strings::StrCat("invocation_results[", i, "].size")), - &size)); - num_return_values = static_cast(size); - if (num_return_values != size) { - return errors::InvalidArgument(strings::StrCat( - full_name( - strings::StrCat("invocation_results[", i, "].size")), - ": ", size, " is not a valid value of type size_t.")); - } - } - result->return_values.reserve(num_return_values); - for (size_t j = 0; j < num_return_values; j++) { - result->return_values.emplace_back(); - TF_RETURN_IF_ERROR( - reader->ReadTensor(full_name(strings::StrCat( - "invocation_results[", i, "][", j, "]")), - &result->return_values.back())); - } - result->skip = reader->Contains( - full_name(strings::StrCat("invocation_results[", i, "].skip"))); - result->notification.Notify(); - } + TF_RETURN_IF_ERROR( + reader->ReadScalar(full_name("block_index"), &block_index_)); TF_RETURN_IF_ERROR( reader->ReadScalar(full_name("cycle_index"), &cycle_index_)); + TF_RETURN_IF_ERROR(reader->ReadScalar(full_name("element_id_counter"), + &element_id_counter_)); if (reader->Contains(full_name("end_of_input"))) end_of_input_ = true; TF_RETURN_IF_ERROR( reader->ReadScalar(full_name("num_open"), &num_open_)); TF_RETURN_IF_ERROR(ReadCurrentElements(ctx, reader)); + TF_RETURN_IF_ERROR(ReadFutureElements(ctx, reader)); return Status::OK(); } private: + // Represents the result of fetching an element from a dataset. + struct Result { + Status status; + std::vector return_values; + // Indicates whether the result is ready to be consumed. + bool is_ready = false; + }; + + // The interleave transformation repeatedly inputs elements, applies the + // user-provided function to transform the input elements to datasets, and + // interleaves the elements of these datasets as its output. + // + // This structure represents an input element and derived state. struct Element { + // Unique identifier, needed to support checkpointing. + int64 id; + // The actual input element. + std::vector inputs; + // Iterator created from the input element. std::unique_ptr iterator; - std::vector inputs; // inputs for creating the iterator - bool in_use; + mutex mu; + // Buffer for storing the outputs of `iterator`. + std::deque> results GUARDED_BY(mu); + // Indicates whether the element is used by a worker thread. + bool in_use = false; }; - struct InvocationResult { - Notification notification; // used for coordination with the consumer - Status status; // the invocation status - std::vector return_values; // the invocation result values - bool skip; // if set the result should be skipped - }; + // Advances the position in the interleave cycle to the next cycle + // element. + void AdvanceToNextInCycle() EXCLUSIVE_LOCKS_REQUIRED(*mu_) { + block_index_ = 0; + cycle_index_ = (cycle_index_ + 1) % dataset()->cycle_length_; + } - void EnsureRunnerThreadStarted(IteratorContext* ctx) + // Advances the position in the interleave cycle by one. + void AdvancePosition() EXCLUSIVE_LOCKS_REQUIRED(*mu_) { + ++block_index_; + if (block_index_ == dataset()->block_length_) { + AdvanceToNextInCycle(); + } + } + + // Consumes a result (if available), returning an indication of whether + // a result is available. If `true` is returned, `result` either + // points to a valid result or is null if end of input has been reached. + bool Consume(std::shared_ptr* result) EXCLUSIVE_LOCKS_REQUIRED(*mu_) { - if (!runner_thread_) { - std::shared_ptr new_ctx(new IteratorContext(*ctx)); - runner_thread_.reset(ctx->env()->StartThread( - {}, "tf_data_parallel_interleave_runner", - [this, new_ctx]() { RunnerThread(new_ctx); })); + if (!sloppy_) { + return ConsumeHelper(result); + } + // If we are allowed to be sloppy (i.e. return results out of order), + // try to find an element in the cycle that has a result available. + for (int i = 0; i < dataset()->cycle_length_; ++i) { + if (ConsumeHelper(result)) { + return true; + } + AdvanceToNextInCycle(); + } + return false; + } + + bool ConsumeHelper(std::shared_ptr* result) + EXCLUSIVE_LOCKS_REQUIRED(*mu_) { + while (true) { + std::shared_ptr element = current_elements_[cycle_index_]; + if (element) { + mutex_lock l(element->mu); + if (!element->results.empty()) { + if (element->results.front()->is_ready) { + // We found a result. + std::swap(*result, element->results.front()); + element->results.pop_front(); + AdvancePosition(); + cond_var_->notify_all(); + return true; + } else { + // Wait for the result to become ready. + return false; + } + } else if (!element->iterator) { + // We reached the end of input for this element. Reset + // it and move on to the next cycle element. + current_elements_[cycle_index_].reset(); + AdvanceToNextInCycle(); + cond_var_->notify_all(); + continue; + } else { + // Wait for the iterator to produce a result. + return false; + } + } else { + if (!future_elements_.empty() || !end_of_input_) { + // Wait for an element to be created. + return false; + } + // No new elements will be created; try to find a + // non-empty element in the cycle. + for (int i = 0; i < dataset()->cycle_length_; ++i) { + AdvanceToNextInCycle(); + if (current_elements_[cycle_index_]) { + break; + } + } + if (current_elements_[cycle_index_]) { + continue; + } + // End of input has been reached. + return true; + } } } - // Fetches up to `results.size()` outputs from the cycle element at - // position `cycle_index`. + // Manages current cycle elements, creating new iterators as needed and + // asynchronously fetching results from existing iterators. // - // If end of input is encountered, the `skip` field of the invocation - // result is used to identify results that should be skipped. - void FetchOutputs( - const std::shared_ptr& ctx, IteratorBase* iterator, - int64 cycle_index, - const std::vector>& results) - LOCKS_EXCLUDED(*mu_) { + // This method runs in the `current_elements_manager_` background thread. + void CurrentElementsManager(const std::shared_ptr& ctx) { RecordStart(ctx.get()); auto cleanup = gtl::MakeCleanup([this, ctx] { RecordStop(ctx.get()); }); - bool end_of_input = false; - for (auto& result : results) { - if (!end_of_input) { - result->status = iterator->GetNext( - ctx.get(), &result->return_values, &end_of_input); + auto busy = [this]() EXCLUSIVE_LOCKS_REQUIRED(*mu_) -> bool { + const bool has_more_elements = + !future_elements_.empty() || !end_of_input_; + const int block_length = dataset()->block_length_; + bool all_elements_busy = true; + for (auto& element : current_elements_) { + if (!element) { + if (has_more_elements) { + all_elements_busy = false; + break; + } + } else { + mutex_lock l(element->mu); + if (!element->in_use && element->iterator && + element->results.size() < block_length) { + all_elements_busy = false; + break; + } + } } - if (end_of_input) { - result->skip = true; + return all_elements_busy || num_calls_ >= num_parallel_calls_->value; + }; + while (true) { + mutex_lock l(*mu_); + + // Wait until this thread is cancelled, the end of input has been + // reached. + while (!cancelled_ && (!end_of_input_ || num_open_ > 0) && busy()) { + RecordStop(ctx.get()); + cond_var_->wait(l); + RecordStart(ctx.get()); } - RecordBufferEnqueue(ctx.get(), result->return_values); - { - mutex_lock l(*mu_); - result->notification.Notify(); - cond_var_->notify_all(); + + if (cancelled_ || + (future_elements_.empty() && end_of_input_ && num_open_ == 0)) { + return; + } + + for (int i = 0; i < dataset()->cycle_length_; ++i) { + int idx = (cycle_index_ + i) % dataset()->cycle_length_; + if (!current_elements_[idx]) { + if (!future_elements_.empty()) { + current_elements_[idx] = std::move(future_elements_.back()); + future_elements_.pop_back(); + } else { + current_elements_[idx] = MakeElement(ctx); + if (!current_elements_[idx]) { + continue; + } + } + } + std::shared_ptr element = current_elements_[idx]; + if (!element->in_use && element->iterator) { + int64 num_results; + { + mutex_lock l(element->mu); + num_results = + dataset()->block_length_ - element->results.size(); + } + if (num_results > 0) { + num_calls_++; + element->in_use = true; + thread_pool_->Schedule( + std::bind(&ParallelInterleaveIterator::FetchResults, this, + ctx, std::move(element), num_results)); + } + } + } + const auto& stats_aggregator = ctx->stats_aggregator(); + if (stats_aggregator) { + stats_aggregator->AddScalar( + strings::StrCat(key_prefix_, "::thread_utilization"), + static_cast(num_calls_) / + static_cast(num_parallel_calls_->value)); } - if (!result->status.ok()) { + cond_var_->notify_all(); + } + } + + void EnsureThreadsStarted(IteratorContext* ctx) + EXCLUSIVE_LOCKS_REQUIRED(*mu_) { + if (!current_elements_manager_) { + auto new_ctx = std::make_shared(*ctx); + current_elements_manager_ = + WrapUnique(ctx->env()->StartThread( + {}, "tf_data_parallel_interleave_current", + [this, new_ctx]() { CurrentElementsManager(new_ctx); })); + } + if (!future_elements_manager_) { + auto new_ctx = std::make_shared(*ctx); + future_elements_manager_ = WrapUnique(ctx->env()->StartThread( + {}, "tf_data_parallel_interleave_future", + [this, new_ctx]() { FutureElementsManager(new_ctx); })); + } + } + + // Fetches up to `dataset()->block_length_` results from `element`. + void FetchResults(const std::shared_ptr& ctx, + const std::shared_ptr& element, + int64 num_results) LOCKS_EXCLUDED(*mu_) { + RecordStart(ctx.get()); + auto cleanup = gtl::MakeCleanup([this, ctx] { RecordStop(ctx.get()); }); + bool end_of_input = false; + for (int64 i = 0; i < num_results; ++i) { + auto result = std::make_shared(); + result->status = element->iterator->GetNext( + ctx.get(), &result->return_values, &end_of_input); + if (end_of_input) { break; } + RecordBufferEnqueue(ctx.get(), result->return_values); + mutex_lock l(*mu_); + mutex_lock l2(element->mu); + element->results.push_back(result); + result->is_ready = true; + cond_var_->notify_all(); } mutex_lock l(*mu_); - current_elements_[cycle_index].in_use = false; + // Release the ownership of the cycle element iterator. + element->in_use = false; if (end_of_input) { - // Release the ownership of the cycle element iterator, closing the - // iterator if end of input was encountered. - current_elements_[cycle_index].iterator.reset(); - current_elements_[cycle_index].inputs.clear(); - num_open_--; + // Close the iterator if end of input was encountered. + element->iterator.reset(); + element->inputs.clear(); + --num_open_; } - num_calls_--; + --num_calls_; const auto& stats_aggregator = ctx->stats_aggregator(); if (stats_aggregator) { stats_aggregator->AddScalar( @@ -437,82 +566,47 @@ class ParallelInterleaveDatasetOp : public UnaryDatasetOpKernel { cond_var_->notify_all(); } - // Method responsible for 1) creating iterators out of input elements, 2) - // determining the order in which elements are fetched from the iterators, - // and 3) scheduling the fetching of the elements to a threadpool. + // Manages futures cycle elements, creating new iterators as needed and + // asynchronously fetching results from existing iterators. // - // This method runs in the `runner_thread` background thread. - void RunnerThread(const std::shared_ptr& ctx) { + // This method runs in the `future_elements_manager_` background thread. + void FutureElementsManager(const std::shared_ptr& ctx) { RecordStart(ctx.get()); auto cleanup = gtl::MakeCleanup([this, ctx] { RecordStop(ctx.get()); }); auto busy = [this]() EXCLUSIVE_LOCKS_REQUIRED(*mu_) -> bool { - return current_elements_[cycle_index_].in_use || - num_calls_ >= num_parallel_calls_->value || - invocation_results_.size() >= - dataset()->cycle_length_ * dataset()->block_length_; + return num_calls_ >= num_parallel_calls_->value || + future_elements_.size() >= dataset()->cycle_length_; }; while (true) { mutex_lock l(*mu_); + // Wait until this thread is cancelled, the end of input has been // reached, or the cycle element at the `cycle_index_` position is - // not in use and there is space in the `invocation_results_` queue. - while (!cancelled_ && (!end_of_input_ || num_open_ > 0) && busy()) { + // not in use. + while (!cancelled_ && !end_of_input_ && busy()) { RecordStop(ctx.get()); cond_var_->wait(l); RecordStart(ctx.get()); } - if (cancelled_ || (end_of_input_ && num_open_ == 0)) { + if (cancelled_ || end_of_input_) { return; } - while ((!end_of_input_ || num_open_ > 0) && !busy()) { - if (!current_elements_[cycle_index_].iterator) { - // Try to create a new iterator from the next input element. - Status status = input_impl_->GetNext( - ctx.get(), ¤t_elements_[cycle_index_].inputs, - &end_of_input_); - if (!status.ok()) { - invocation_results_.emplace_back(new InvocationResult()); - std::shared_ptr& result = - invocation_results_.back(); - result->status.Update(status); - result->notification.Notify(); - break; - } - if (!end_of_input_) { - Status status = MakeIteratorFromInputElement( - ctx.get(), current_elements_[cycle_index_].inputs, - cycle_index_, *instantiated_captured_func_, prefix(), - ¤t_elements_[cycle_index_].iterator); - if (!status.ok()) { - invocation_results_.emplace_back(new InvocationResult()); - std::shared_ptr& result = - invocation_results_.back(); - result->status.Update(status); - result->notification.Notify(); - break; - } - ++num_open_; - } + while (!end_of_input_ && !busy()) { + std::shared_ptr element = MakeElement(ctx); + if (!element) { + break; } - if (current_elements_[cycle_index_].iterator) { - // Pre-allocate invocation results for outputs to be fetched - // and then fetch the outputs asynchronously. - std::vector> results; - results.reserve(dataset()->block_length_); - for (int i = 0; i < dataset()->block_length_; ++i) { - invocation_results_.emplace_back(new InvocationResult()); - results.push_back(invocation_results_.back()); - } - num_calls_++; - current_elements_[cycle_index_].in_use = true; - thread_pool_->Schedule( - std::bind(&ParallelInterleaveIterator::FetchOutputs, this, - ctx, current_elements_[cycle_index_].iterator.get(), - cycle_index_, std::move(results))); + future_elements_.push_front(element); + if (!element->iterator) { + continue; } - cycle_index_ = (cycle_index_ + 1) % dataset()->cycle_length_; + ++num_calls_; + element->in_use = true; + thread_pool_->Schedule( + std::bind(&ParallelInterleaveIterator::FetchResults, this, ctx, + std::move(element), dataset()->block_length_)); } const auto& stats_aggregator = ctx->stats_aggregator(); if (stats_aggregator) { @@ -525,56 +619,66 @@ class ParallelInterleaveDatasetOp : public UnaryDatasetOpKernel { } } - // Determines whether the caller needs to wait for a result. Upon - // returning false, `result` will either be NULL if end of input has been - // reached or point to the result. - bool ShouldWait(std::shared_ptr* result) + // Creates a new element. + std::shared_ptr MakeElement( + const std::shared_ptr& ctx) EXCLUSIVE_LOCKS_REQUIRED(*mu_) { - if (sloppy_) { - for (auto it = invocation_results_.begin(); - it != invocation_results_.end(); ++it) { - if ((*it)->notification.HasBeenNotified()) { - std::swap(*result, *it); - invocation_results_.erase(it); - cond_var_->notify_all(); - return false; - } + auto element = std::make_shared(); + element->id = element_id_counter_++; + Status status = + input_impl_->GetNext(ctx.get(), &element->inputs, &end_of_input_); + if (!status.ok()) { + auto result = std::make_shared(); + result->is_ready = true; + result->status = status; + mutex_lock l(element->mu); + element->results.push_back(std::move(result)); + return element; + } + if (!end_of_input_) { + Status status = MakeIteratorFromInputElement( + ctx.get(), element->inputs, element->id, + *instantiated_captured_func_, prefix(), &element->iterator); + if (!status.ok()) { + auto result = std::make_shared(); + result->is_ready = true; + result->status = status; + mutex_lock l(element->mu); + element->results.push_back(std::move(result)); + return element; } - return !invocation_results_.empty() || - (!end_of_input_ || num_open_ > 0); + ++num_open_; } else { - if (!invocation_results_.empty()) { - std::swap(*result, invocation_results_.front()); - invocation_results_.pop_front(); - cond_var_->notify_all(); - return false; - } - return (!end_of_input_ || num_open_ > 0); + element.reset(); } + return element; } - Status WriteStatusLocked(IteratorStateWriter* writer, size_t index, + Status WriteStatusLocked(IteratorStateWriter* writer, + const string& key_prefix, size_t idx, const Status& status) EXCLUSIVE_LOCKS_REQUIRED(*mu_) { TF_RETURN_IF_ERROR(writer->WriteScalar( - CodeKey(index), static_cast(status.code()))); + CodeKey(key_prefix, idx), static_cast(status.code()))); if (!status.ok()) { - TF_RETURN_IF_ERROR(writer->WriteScalar(ErrorMessageKey(index), - status.error_message())); + TF_RETURN_IF_ERROR(writer->WriteScalar( + ErrorMessageKey(key_prefix, idx), status.error_message())); } return Status::OK(); } - Status ReadStatusLocked(IteratorStateReader* reader, size_t index, + Status ReadStatusLocked(IteratorStateReader* reader, + const string& key_prefix, size_t idx, Status* status) EXCLUSIVE_LOCKS_REQUIRED(*mu_) { int64 code_int; - TF_RETURN_IF_ERROR(reader->ReadScalar(CodeKey(index), &code_int)); + TF_RETURN_IF_ERROR( + reader->ReadScalar(CodeKey(key_prefix, idx), &code_int)); error::Code code = static_cast(code_int); if (code != error::Code::OK) { string error_message; - TF_RETURN_IF_ERROR( - reader->ReadScalar(ErrorMessageKey(index), &error_message)); + TF_RETURN_IF_ERROR(reader->ReadScalar( + ErrorMessageKey(key_prefix, idx), &error_message)); *status = Status(code, error_message); } else { *status = Status::OK(); @@ -582,64 +686,178 @@ class ParallelInterleaveDatasetOp : public UnaryDatasetOpKernel { return Status::OK(); } - string CodeKey(size_t index) { + string CodeKey(const string& key_prefix, size_t idx) { return full_name( - strings::StrCat("invocation_results[", index, "].code")); + strings::StrCat(key_prefix, ".results[", idx, "].code")); } - string ErrorMessageKey(size_t index) { + string ErrorMessageKey(const string& key_prefix, size_t idx) { return full_name( - strings::StrCat("invocation_results[", index, "].error_message")); + strings::StrCat(key_prefix, ".results[", idx, "].error_message")); + } + + Status WriteElement(std::shared_ptr element, int idx, + const string& key_prefix, IteratorStateWriter* writer) + EXCLUSIVE_LOCKS_REQUIRED(*mu_) { + if (element->iterator) { + TF_RETURN_IF_ERROR(SaveInput(writer, element->iterator)); + TF_RETURN_IF_ERROR(writer->WriteScalar( + full_name(strings::StrCat(key_prefix, "[", idx, "].id")), + element->id)); + TF_RETURN_IF_ERROR(writer->WriteScalar( + full_name(strings::StrCat(key_prefix, "[", idx, "].inputs.size")), + element->inputs.size())); + for (int i = 0; i < element->inputs.size(); i++) { + TF_RETURN_IF_ERROR(writer->WriteTensor( + full_name( + strings::StrCat(key_prefix, "[", idx, "].inputs[", i, "]")), + element->inputs[i])); + } + } + mutex_lock l(element->mu); + TF_RETURN_IF_ERROR(writer->WriteScalar( + full_name(strings::StrCat(key_prefix, "[", idx, "].results.size")), + element->results.size())); + for (size_t i = 0; i < element->results.size(); i++) { + std::shared_ptr result = element->results[i]; + TF_RETURN_IF_ERROR(WriteStatusLocked( + writer, strings::StrCat(key_prefix, "[", idx, "]"), i, + result->status)); + TF_RETURN_IF_ERROR(writer->WriteScalar( + full_name(strings::StrCat(key_prefix, "[", idx, "].results[", i, + "].size")), + result->return_values.size())); + for (size_t j = 0; j < result->return_values.size(); j++) { + TF_RETURN_IF_ERROR(writer->WriteTensor( + full_name(strings::StrCat(key_prefix, "[", idx, "].results[", i, + "][", j, "]")), + result->return_values[j])); + } + if (result->is_ready) { + TF_RETURN_IF_ERROR(writer->WriteScalar( + full_name(strings::StrCat(key_prefix, "[", idx, "].results[", i, + "].is_ready")), + "")); + } + } + return Status::OK(); } Status WriteCurrentElements(IteratorStateWriter* writer) EXCLUSIVE_LOCKS_REQUIRED(*mu_) { + TF_RETURN_IF_ERROR(writer->WriteScalar( + full_name("current_elements.size"), current_elements_.size())); for (int idx = 0; idx < current_elements_.size(); idx++) { - if (current_elements_[idx].iterator) { - TF_RETURN_IF_ERROR( - SaveInput(writer, current_elements_[idx].iterator)); - TF_RETURN_IF_ERROR(writer->WriteScalar( - full_name( - strings::StrCat("current_elements[", idx, "].inputs.size")), - current_elements_[idx].inputs.size())); - for (int i = 0; i < current_elements_[idx].inputs.size(); i++) { - TF_RETURN_IF_ERROR(writer->WriteTensor( - full_name(strings::StrCat("current_elements[", idx, - "].inputs[", i, "]")), - current_elements_[idx].inputs[i])); - } + if (current_elements_[idx]) { + TF_RETURN_IF_ERROR(WriteElement(current_elements_[idx], idx, + "current_elements", writer)); } } return Status::OK(); } + Status WriteFutureElements(IteratorStateWriter* writer) + EXCLUSIVE_LOCKS_REQUIRED(*mu_) { + TF_RETURN_IF_ERROR(writer->WriteScalar( + full_name("future_elements.size"), future_elements_.size())); + for (int idx = 0; idx < future_elements_.size(); idx++) { + if (future_elements_[idx]) { + TF_RETURN_IF_ERROR(WriteElement(future_elements_[idx], idx, + "future_elements", writer)); + } + } + return Status::OK(); + } + + Status ReadElement(IteratorContext* ctx, IteratorStateReader* reader, + int idx, const string& key_prefix, + std::shared_ptr* out) + EXCLUSIVE_LOCKS_REQUIRED(*mu_) { + if (!reader->Contains(full_name( + strings::StrCat(key_prefix, "[", idx, "].results.size")))) { + return Status::OK(); + } + auto element = std::make_shared(); + mutex_lock l(element->mu); + int64 results_size; + TF_RETURN_IF_ERROR(reader->ReadScalar( + full_name(strings::StrCat(key_prefix, "[", idx, "].results.size")), + &results_size)); + element->results.resize(results_size); + for (size_t i = 0; i < results_size; i++) { + auto result = std::make_shared(); + TF_RETURN_IF_ERROR(ReadStatusLocked( + reader, strings::StrCat(key_prefix, "[", idx, "]"), i, + &result->status)); + int64 num_return_values; + TF_RETURN_IF_ERROR(reader->ReadScalar( + full_name(strings::StrCat(key_prefix, "[", idx, "].results[", i, + "].size")), + &num_return_values)); + result->return_values.reserve(num_return_values); + for (size_t j = 0; j < num_return_values; j++) { + result->return_values.emplace_back(); + TF_RETURN_IF_ERROR(reader->ReadTensor( + full_name(strings::StrCat(key_prefix, "[", idx, "].results[", i, + "][", j, "]")), + &result->return_values.back())); + } + result->is_ready = reader->Contains(full_name(strings::StrCat( + key_prefix, "[", idx, "].results[", i, "].is_ready"))); + element->results[i] = std::move(result); + } + if (!reader->Contains(full_name( + strings::StrCat(key_prefix, "[", idx, "].inputs.size")))) { + element->iterator.reset(); + *out = std::move(element); + return Status::OK(); + } + int64 inputs_size; + TF_RETURN_IF_ERROR(reader->ReadScalar( + full_name(strings::StrCat(key_prefix, "[", idx, "].inputs.size")), + &inputs_size)); + element->inputs.resize(inputs_size); + for (int i = 0; i < inputs_size; i++) { + TF_RETURN_IF_ERROR(reader->ReadTensor( + full_name( + strings::StrCat(key_prefix, "[", idx, "].inputs[", i, "]")), + &element->inputs[i])); + } + TF_RETURN_IF_ERROR(reader->ReadScalar( + full_name(strings::StrCat(key_prefix, "[", idx, "].id")), + &element->id)); + TF_RETURN_IF_ERROR(MakeIteratorFromInputElement( + ctx, element->inputs, element->id, + *instantiated_captured_func_.get(), prefix(), &element->iterator)); + TF_RETURN_IF_ERROR(RestoreInput(ctx, reader, element->iterator)); + *out = std::move(element); + return Status::OK(); + } + Status ReadCurrentElements(IteratorContext* ctx, IteratorStateReader* reader) EXCLUSIVE_LOCKS_REQUIRED(*mu_) { + int64 size; + TF_RETURN_IF_ERROR( + reader->ReadScalar(full_name("current_elements.size"), &size)); + DCHECK_EQ(current_elements_.size(), size); for (int idx = 0; idx < current_elements_.size(); idx++) { - if (reader->Contains(full_name(strings::StrCat( - "current_elements[", idx, "].inputs.size")))) { - int64 inputs_size; - TF_RETURN_IF_ERROR(reader->ReadScalar( - full_name( - strings::StrCat("current_elements[", idx, "].inputs.size")), - &inputs_size)); - current_elements_[idx].inputs.resize(inputs_size); - for (int i = 0; i < inputs_size; i++) { - TF_RETURN_IF_ERROR(reader->ReadTensor( - full_name(strings::StrCat("current_elements[", idx, - "].inputs[", i, "]")), - ¤t_elements_[idx].inputs[i])); - } - TF_RETURN_IF_ERROR(MakeIteratorFromInputElement( - ctx, current_elements_[idx].inputs, idx, - *instantiated_captured_func_.get(), prefix(), - ¤t_elements_[idx].iterator)); - TF_RETURN_IF_ERROR( - RestoreInput(ctx, reader, current_elements_[idx].iterator)); - } else { - current_elements_[idx].iterator.reset(); - } + TF_RETURN_IF_ERROR(ReadElement(ctx, reader, idx, "current_elements", + ¤t_elements_[idx])); + } + return Status::OK(); + } + + Status ReadFutureElements(IteratorContext* ctx, + IteratorStateReader* reader) + EXCLUSIVE_LOCKS_REQUIRED(*mu_) { + int64 size; + TF_RETURN_IF_ERROR( + reader->ReadScalar(full_name("future_elements.size"), &size)); + future_elements_.resize(size); + for (int idx = 0; idx < future_elements_.size(); idx++) { + TF_RETURN_IF_ERROR(ReadElement(ctx, reader, idx, "future_elements", + &future_elements_[idx])); } return Status::OK(); } @@ -648,12 +866,11 @@ class ParallelInterleaveDatasetOp : public UnaryDatasetOpKernel { // the worker threads. const std::shared_ptr mu_; - // Used for coordination between the main thread, the runner thread, and - // the worker threads. In particular, the runner thread should only - // schedule new calls when the number of in-flight calls is less than the - // user specified level of parallelism, there are slots available in the - // `invocation_results_` buffer, the current cycle element is not in use, - // and there are elements left to be fetched. + // Used for coordination between the main thread, the manager threads, and + // the threadpool threads. In particular, the managers thread should only + // schedule new calls into the threadpool when the number of in-flight + // calls is less than the user specified level of parallelism and there + // are slots available in the element `results` buffer. const std::shared_ptr cond_var_; // Identifies the maximum number of parallel calls. @@ -665,18 +882,17 @@ class ParallelInterleaveDatasetOp : public UnaryDatasetOpKernel { // Iterator for input elements. std::unique_ptr input_impl_ GUARDED_BY(*mu_); - // Identifies current cycle element. - int64 cycle_index_ = 0; + // Identifies position in the interleave cycle. + int64 block_index_ GUARDED_BY(*mu_) = 0; + int64 cycle_index_ GUARDED_BY(*mu_) = 0; - // Iterators for the current cycle elements. Concurrent access is - // protected by `element_in_use_`. - std::vector current_elements_ GUARDED_BY(*mu_); + // Elements of the current interleave cycle. + std::vector> current_elements_ GUARDED_BY(*mu_); - // Buffer for storing the invocation results. - std::deque> invocation_results_ - GUARDED_BY(*mu_); + // Elements to be used in the interleave cycle in the future. + std::deque> future_elements_ GUARDED_BY(*mu_); - // Identifies whether end of input has been reached. + // Identifies whether the global end of input has been reached. bool end_of_input_ GUARDED_BY(*mu_) = false; // Identifies the number of open iterators. @@ -686,9 +902,11 @@ class ParallelInterleaveDatasetOp : public UnaryDatasetOpKernel { int64 num_calls_ GUARDED_BY(*mu_) = 0; std::unique_ptr thread_pool_; - std::unique_ptr runner_thread_ GUARDED_BY(*mu_); + std::unique_ptr current_elements_manager_ GUARDED_BY(*mu_); + std::unique_ptr future_elements_manager_ GUARDED_BY(*mu_); + int64 element_id_counter_ GUARDED_BY(*mu_) = 0; - // Identifies whether background activity should be cancelled. + // Identifies whether background threads should be cancelled. bool cancelled_ GUARDED_BY(*mu_) = false; string key_prefix_; std::unique_ptr instantiated_captured_func_; diff --git a/tensorflow/python/data/kernel_tests/interleave_test.py b/tensorflow/python/data/kernel_tests/interleave_test.py index 4fb61b2daf..4b427ff5a4 100644 --- a/tensorflow/python/data/kernel_tests/interleave_test.py +++ b/tensorflow/python/data/kernel_tests/interleave_test.py @@ -17,19 +17,15 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -import threading - from absl.testing import parameterized import numpy as np -from tensorflow.python.data.experimental.ops import threading_options from tensorflow.python.data.kernel_tests import test_base from tensorflow.python.data.ops import dataset_ops from tensorflow.python.framework import errors from tensorflow.python.framework import sparse_tensor from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops -from tensorflow.python.ops import script_ops from tensorflow.python.ops import sparse_ops from tensorflow.python.platform import test @@ -78,47 +74,6 @@ def _interleave(lists, cycle_length, block_length): break -def _make_coordinated_sloppy_dataset(input_values, cycle_length, block_length, - num_parallel_calls): - """Produces a dataset iterator and events to control the order of elements. - - Args: - input_values: the values to generate lists to interleave from - cycle_length: the length of the interleave cycle - block_length: the length of the interleave block - num_parallel_calls: the degree of interleave parallelism - - Returns: - A dataset iterator (represented as `get_next` op) and events that can be - used to control the order of output elements. - """ - - # Set up threading events used to sequence when items are produced that - # are subsequently interleaved. These events allow us to deterministically - # simulate slowdowns and force sloppiness. - coordination_events = {i: threading.Event() for i in input_values} - - def map_py_fn(x): - coordination_events[x].wait() - coordination_events[x].clear() - return x * x - - def map_fn(x): - return script_ops.py_func(map_py_fn, [x], x.dtype) - - def interleave_fn(x): - dataset = dataset_ops.Dataset.from_tensors(x) - dataset = dataset.repeat(x) - return dataset.map(map_fn) - - options = dataset_ops.Options() - options.experimental_deterministic = False - dataset = dataset_ops.Dataset.from_tensor_slices(input_values).repeat( - 2).interleave(interleave_fn, cycle_length, block_length, - num_parallel_calls).with_options(options) - return dataset, coordination_events - - def _repeat(values, count): """Produces a list of lists suitable for testing interleave. @@ -252,63 +207,37 @@ class InterleaveTest(test_base.DatasetTestBase, parameterized.TestCase): self.evaluate(get_next()) @parameterized.named_parameters( - ("1", np.int64([4, 5, 6]), 2, 1, 1), - ("2", np.int64([4, 5, 6]), 2, 1, 2), - ("3", np.int64([4, 5, 6]), 2, 3, 1), - ("4", np.int64([4, 5, 6]), 2, 3, 2), - ("5", np.int64([4, 5, 6]), 3, 2, 1), - ("6", np.int64([4, 5, 6]), 3, 2, 2), - ("7", np.int64([4, 5, 6]), 3, 2, 3), - ("8", np.int64([4, 0, 6]), 2, 3, 1), - ("9", np.int64([4, 0, 6]), 2, 3, 2), + ("1", np.int64([4, 5, 6]), 1, 3, 1), + ("2", np.int64([4, 5, 6]), 2, 1, 1), + ("3", np.int64([4, 5, 6]), 2, 1, 2), + ("4", np.int64([4, 5, 6]), 2, 3, 1), + ("5", np.int64([4, 5, 6]), 2, 3, 2), + ("6", np.int64([4, 5, 6]), 7, 2, 1), + ("7", np.int64([4, 5, 6]), 7, 2, 3), + ("8", np.int64([4, 5, 6]), 7, 2, 5), + ("9", np.int64([4, 5, 6]), 7, 2, 7), + ("10", np.int64([4, 0, 6]), 2, 3, 1), + ("11", np.int64([4, 0, 6]), 2, 3, 2), ) - def testSloppyInterleaveInOrder(self, input_values, cycle_length, + def testSloppyInterleaveDataset(self, input_values, cycle_length, block_length, num_parallel_calls): - dataset, coordination_events = _make_coordinated_sloppy_dataset( - input_values, cycle_length, block_length, num_parallel_calls) - options = dataset_ops.Options() - options.experimental_threading = threading_options.ThreadingOptions() - options.experimental_threading.private_threadpool_size = ( - num_parallel_calls + 1) - dataset = dataset.with_options(options) - - get_next = self.getNext(dataset, requires_initialization=True) - for expected_element in _interleave( - _repeat(input_values, 2), cycle_length, block_length): - coordination_events[expected_element].set() - self.assertEqual(expected_element * expected_element, - self.evaluate(get_next())) - with self.assertRaises(errors.OutOfRangeError): - self.evaluate(get_next()) - - @parameterized.named_parameters( - ("1", np.int64([4, 5, 6]), 2, 1, 2), - ("2", np.int64([4, 5, 6]), 2, 3, 2), - ("3", np.int64([4, 5, 6]), 3, 2, 3), - ("4", np.int64([4, 0, 6]), 2, 3, 2), - ) - def testSloppyInterleaveOutOfOrder(self, input_values, cycle_length, - block_length, num_parallel_calls): - dataset, coordination_events = _make_coordinated_sloppy_dataset( - input_values, cycle_length, block_length, num_parallel_calls) + count = 2 + dataset = dataset_ops.Dataset.from_tensor_slices(input_values).repeat( + count).interleave( + lambda x: dataset_ops.Dataset.from_tensors(x).repeat(x), + cycle_length, block_length, num_parallel_calls) options = dataset_ops.Options() - options.experimental_threading = threading_options.ThreadingOptions() - options.experimental_threading.private_threadpool_size = ( - num_parallel_calls + 1) + options.experimental_deterministic = False dataset = dataset.with_options(options) - get_next = self.getNext(dataset, requires_initialization=True) - elements = [ - x for x in _interleave( - _repeat(input_values, 2), cycle_length, block_length) + expected_output = [ + element for element in _interleave( + _repeat(input_values, count), cycle_length, block_length) ] - for i in [1, 4, 7]: - elements[i], elements[i + 1] = elements[i + 1], elements[i] - - for element in elements: - coordination_events[element].set() - self.assertEqual(element * element, self.evaluate(get_next())) - with self.assertRaises(errors.OutOfRangeError): - self.evaluate(get_next()) + get_next = self.getNext(dataset) + actual_output = [] + for _ in range(len(expected_output)): + actual_output.append(self.evaluate(get_next())) + self.assertAllEqual(expected_output.sort(), actual_output.sort()) if __name__ == "__main__": -- GitLab From dba2134638812e6375a26012a485b55a24c45a19 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Fri, 4 Jan 2019 14:26:00 -0800 Subject: [PATCH 366/622] Switch RBE image location for CUDA 9 to tensorflow-testing. PiperOrigin-RevId: 227914044 --- .../tools/ci_build/Dockerfile.rbe.cuda9.0-cudnn7-ubuntu14.04 | 4 ++-- third_party/toolchains/BUILD | 2 +- third_party/toolchains/preconfig/generate/containers.bzl | 2 +- third_party/toolchains/preconfig/generate/workspace.bzl | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tensorflow/tools/ci_build/Dockerfile.rbe.cuda9.0-cudnn7-ubuntu14.04 b/tensorflow/tools/ci_build/Dockerfile.rbe.cuda9.0-cudnn7-ubuntu14.04 index 9d5d51447c..4ce4214065 100644 --- a/tensorflow/tools/ci_build/Dockerfile.rbe.cuda9.0-cudnn7-ubuntu14.04 +++ b/tensorflow/tools/ci_build/Dockerfile.rbe.cuda9.0-cudnn7-ubuntu14.04 @@ -1,7 +1,7 @@ # To push a new version, run: # $ docker build -f Dockerfile.rbe.cuda9.0-cudnn7-ubuntu14.04 \ -# --tag "gcr.io/asci-toolchain/nosla-cuda9.0-cudnn7-ubuntu14.04" . -# $ docker push gcr.io/asci-toolchain/nosla-cuda9.0-cudnn7-ubuntu14.04 +# --tag "gcr.io/tensorflow-testing/nosla-cuda9.0-cudnn7-ubuntu14.04" . +# $ docker push gcr.io/tensorflow-testing/nosla-cuda9.0-cudnn7-ubuntu14.04 # # TODO(klimek): Include clang in this image so we can also target clang # builds. diff --git a/third_party/toolchains/BUILD b/third_party/toolchains/BUILD index 28f1e6979c..f2248341c4 100644 --- a/third_party/toolchains/BUILD +++ b/third_party/toolchains/BUILD @@ -32,7 +32,7 @@ platform( remote_execution_properties = """ properties: { name: "container-image" - value:"docker://gcr.io/asci-toolchain/nosla-cuda9.0-cudnn7-ubuntu14.04@%s" + value:"docker://gcr.io/tensorflow-testing/nosla-cuda9.0-cudnn7-ubuntu14.04@%s" }""" % container_digests["cuda9.0-cudnn7-ubuntu14.04"], ) diff --git a/third_party/toolchains/preconfig/generate/containers.bzl b/third_party/toolchains/preconfig/generate/containers.bzl index 32d5f29b52..67e43485ed 100644 --- a/third_party/toolchains/preconfig/generate/containers.bzl +++ b/third_party/toolchains/preconfig/generate/containers.bzl @@ -1,4 +1,4 @@ container_digests = { - "cuda9.0-cudnn7-ubuntu14.04": "sha256:c43ed5341dd765042e0bbd1bf50fadeedd649d1e0c34d81999cb6ce30916cb95", + "cuda9.0-cudnn7-ubuntu14.04": "sha256:006a76ee1838122ff7f21ebac85f24c1ef350d4dd79b3ceff0e4fe649ed90d33", "cuda10.0-cudnn7-ubuntu14.04": "sha256:e36f05f1ff39e39ddf07122e37f2b1895948bb6f7acc3db37a3c496be5e66228", } diff --git a/third_party/toolchains/preconfig/generate/workspace.bzl b/third_party/toolchains/preconfig/generate/workspace.bzl index a3268585e1..eb74022c24 100644 --- a/third_party/toolchains/preconfig/generate/workspace.bzl +++ b/third_party/toolchains/preconfig/generate/workspace.bzl @@ -11,7 +11,7 @@ def _remote_config_workspace(): container_pull( name = "cuda9.0-cudnn7-ubuntu14.04", registry = "gcr.io", - repository = "asci-toolchain/nosla-cuda9.0-cudnn7-ubuntu14.04", + repository = "tensorflow-testing/nosla-cuda9.0-cudnn7-ubuntu14.04", digest = container_digests["cuda9.0-cudnn7-ubuntu14.04"], ) -- GitLab From 2f246ef177ad3cb8aba3b4de0a628822cbeab71d Mon Sep 17 00:00:00 2001 From: Gunhan Gulsoy Date: Fri, 4 Jan 2019 14:27:22 -0800 Subject: [PATCH 367/622] Upgrade ci docker images to cuda 10. These really should be gone already... PiperOrigin-RevId: 227914258 --- tensorflow/tools/ci_build/Dockerfile.gpu | 4 ++-- tensorflow/tools/ci_build/Dockerfile.rbe.gpu | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tensorflow/tools/ci_build/Dockerfile.gpu b/tensorflow/tools/ci_build/Dockerfile.gpu index a4cad4b6c6..ff1fefe892 100644 --- a/tensorflow/tools/ci_build/Dockerfile.gpu +++ b/tensorflow/tools/ci_build/Dockerfile.gpu @@ -1,4 +1,4 @@ -FROM nvidia/cuda:9.0-cudnn7-devel-ubuntu16.04 +FROM nvidia/cuda:10.0-cudnn7-devel-ubuntu16.04 LABEL maintainer="Jan Prach " @@ -24,7 +24,7 @@ COPY install/.bazelrc /etc/bazel.bazelrc ENV LD_LIBRARY_PATH /usr/local/cuda/extras/CUPTI/lib64:$LD_LIBRARY_PATH # Link NCCL libray and header where the build script expects them. -RUN mkdir /usr/local/cuda-9.0/lib && \ +RUN mkdir /usr/local/cuda/lib && \ ln -s /usr/lib/x86_64-linux-gnu/libnccl.so.2 /usr/local/cuda/lib/libnccl.so.2 && \ ln -s /usr/include/nccl.h /usr/local/cuda/include/nccl.h diff --git a/tensorflow/tools/ci_build/Dockerfile.rbe.gpu b/tensorflow/tools/ci_build/Dockerfile.rbe.gpu index b656205836..c4912a65b6 100644 --- a/tensorflow/tools/ci_build/Dockerfile.rbe.gpu +++ b/tensorflow/tools/ci_build/Dockerfile.rbe.gpu @@ -1,4 +1,4 @@ -FROM nvidia/cuda:9.0-cudnn7-devel-ubuntu16.04 +FROM nvidia/cuda:10.0-cudnn7-devel-ubuntu16.04 LABEL maintainer="Nick Lopez " -- GitLab From 7cc1ff9072ce4c2c7f36b7011e24486071ad13ee Mon Sep 17 00:00:00 2001 From: Allen Lavoie Date: Fri, 4 Jan 2019 14:35:17 -0800 Subject: [PATCH 368/622] Better exception for tf.constant(tf.constant(x)) PiperOrigin-RevId: 227915529 --- tensorflow/python/framework/constant_op.py | 4 ++++ tensorflow/python/kernel_tests/constant_op_test.py | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/tensorflow/python/framework/constant_op.py b/tensorflow/python/framework/constant_op.py index ade0797dcd..62a25f3fea 100644 --- a/tensorflow/python/framework/constant_op.py +++ b/tensorflow/python/framework/constant_op.py @@ -277,6 +277,10 @@ def _constant_impl( (num_t, shape, shape.num_elements())) g = ops.get_default_graph() tensor_value = attr_value_pb2.AttrValue() + if tensor_util.is_tensor(value): + raise ValueError( + ("Cannot create a tf.constant from symbolic Tensor %s. Did you mean " + "tf.convert_to_tensor?") % (value,)) tensor_value.tensor.CopyFrom( tensor_util.make_tensor_proto( value, dtype=dtype, shape=shape, verify_shape=verify_shape, diff --git a/tensorflow/python/kernel_tests/constant_op_test.py b/tensorflow/python/kernel_tests/constant_op_test.py index 583082c2aa..2b743ed4c4 100644 --- a/tensorflow/python/kernel_tests/constant_op_test.py +++ b/tensorflow/python/kernel_tests/constant_op_test.py @@ -24,6 +24,7 @@ from google.protobuf import text_format from tensorflow.core.framework import graph_pb2 from tensorflow.core.framework import tensor_pb2 +from tensorflow.python.eager import def_function from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes as dtypes_lib from tensorflow.python.framework import errors_impl @@ -630,6 +631,16 @@ class OnesTest(test.TestCase): self.assertEqual([2, 3], z.get_shape()) self.assertAllEqual(z.eval(), np.ones([2, 3])) + @test_util.run_in_graph_and_eager_modes + def testIteratedConstantSensibleException(self): + + @def_function.function + def constant_on_constant(x): + return constant_op.constant(x) + + with self.assertRaisesRegexp(ValueError, "convert_to_tensor"): + constant_on_constant(constant_op.constant(2)) + class OnesLikeTest(test.TestCase): -- GitLab From 73b6e92b4b3faf0c20687af3ba63989fc5234f13 Mon Sep 17 00:00:00 2001 From: Sourabh Bajaj Date: Fri, 4 Jan 2019 14:35:23 -0800 Subject: [PATCH 369/622] Remove usage of the initialize and finalize api PiperOrigin-RevId: 227915547 --- .../distribute/python/metrics_v1_test.py | 3 --- .../distribute/python/minimize_loss_test.py | 19 +------------------ .../contrib/distribute/python/step_fn_test.py | 4 ---- 3 files changed, 1 insertion(+), 25 deletions(-) diff --git a/tensorflow/contrib/distribute/python/metrics_v1_test.py b/tensorflow/contrib/distribute/python/metrics_v1_test.py index 32a0d19943..7472f6dde4 100644 --- a/tensorflow/contrib/distribute/python/metrics_v1_test.py +++ b/tensorflow/contrib/distribute/python/metrics_v1_test.py @@ -122,7 +122,6 @@ class MetricsV1Test(test.TestCase, parameterized.TestCase): batches_per_update = distribution.num_replicas_in_sync self.evaluate(iterator.initializer) - self.evaluate(distribution.initialize()) self.evaluate(variables.local_variables_initializer()) batches_consumed = 0 @@ -136,8 +135,6 @@ class MetricsV1Test(test.TestCase, parameterized.TestCase): if batches_consumed >= 4: # Consume 4 input batches in total. break - self.evaluate(distribution.finalize()) - @combinations.generate(all_combinations() + tpu_combinations()) def testMean(self, distribution): def _dataset_fn(): diff --git a/tensorflow/contrib/distribute/python/minimize_loss_test.py b/tensorflow/contrib/distribute/python/minimize_loss_test.py index 824c4b0937..b0e24a53f6 100644 --- a/tensorflow/contrib/distribute/python/minimize_loss_test.py +++ b/tensorflow/contrib/distribute/python/minimize_loss_test.py @@ -75,7 +75,6 @@ class MinimizeLossStepTest(test.TestCase, parameterized.TestCase): return distribution.run_steps_on_dataset( step_fn, iterator, iterations=2).run_op - self.evaluate(distribution.initialize()) if not context.executing_eagerly(): with self.cached_session() as sess: run_step = sess.make_callable(run_step()) @@ -84,12 +83,9 @@ class MinimizeLossStepTest(test.TestCase, parameterized.TestCase): weights, biases = [], [] for _ in range(5): run_step() - weights.append(self.evaluate(layer.kernel)) biases.append(self.evaluate(layer.bias)) - self.evaluate(distribution.finalize()) - error = abs(numpy.add(numpy.squeeze(weights), numpy.squeeze(biases)) - 1) is_not_increasing = all(y <= x for x, y in zip(error, error[1:])) self.assertTrue(is_not_increasing) @@ -152,7 +148,7 @@ class MinimizeLossStepTest(test.TestCase, parameterized.TestCase): # `distribution.scope`. with variable_scope.variable_creator_scope( appending_creator), distribution.scope(): - model_fn, dataset_fn, layer = minimize_loss_example( + model_fn, dataset_fn, _ = minimize_loss_example( optimizer_fn, use_bias=True, use_callable_loss=True, @@ -169,16 +165,12 @@ class MinimizeLossStepTest(test.TestCase, parameterized.TestCase): return distribution.run_steps_on_dataset( step_fn, iterator, iterations=1).run_op - self.evaluate(distribution.initialize()) if not context.executing_eagerly(): with self.cached_session() as sess: run_step = sess.make_callable(run_step()) self.evaluate(variables_lib.global_variables_initializer()) - run_step() - self.evaluate(distribution.finalize()) - def get_expected_variables(optimizer_fn, num_parameter_devices): variables_map = { "GradientDescent": ["dense/kernel", "dense/bias"], @@ -241,7 +233,6 @@ class MinimizeLossStepTest(test.TestCase, parameterized.TestCase): return distribution.run_steps_on_dataset( step_fn, iterator, iterations=1).run_op - self.evaluate(distribution.initialize()) if not context.executing_eagerly(): with self.cached_session() as sess: run_step = sess.make_callable(run_step()) @@ -267,8 +258,6 @@ class MinimizeLossStepTest(test.TestCase, parameterized.TestCase): expected_moving_mean - averaged_batch_mean(i)) * (1.0 - momentum)) self.assertNear(expected_moving_means[i], moving_means[i], 0.0001) - self.evaluate(distribution.finalize()) - @combinations.generate( combinations.times( combinations.combine( @@ -335,7 +324,6 @@ class MinimizeLossStepTest(test.TestCase, parameterized.TestCase): return distribution.run_steps_on_dataset( step_fn, iterator, iterations=1).run_op - self.evaluate(distribution.initialize()) if not context.executing_eagerly(): with self.cached_session() as sess: run_step = sess.make_callable(run_step()) @@ -370,8 +358,6 @@ class MinimizeLossStepTest(test.TestCase, parameterized.TestCase): # One of the mean loss reductions. self.assertNear(weight, 2 + 10.6, 0.0001) - self.evaluate(distribution.finalize()) - @combinations.generate( combinations.times( combinations.distributions_and_v1_optimizers(), @@ -458,7 +444,6 @@ class MinimizeLossStepTest(test.TestCase, parameterized.TestCase): reduced=False, distribution=distribution) return (ctx.run_op, ctx.last_step_outputs["replica_loss_reduced"]) - self.evaluate(distribution.initialize()) if not context.executing_eagerly(): with self.cached_session() as sess: run_step = sess.make_callable(run_step()) @@ -471,8 +456,6 @@ class MinimizeLossStepTest(test.TestCase, parameterized.TestCase): weights.append(self.evaluate(layer.kernel)) biases.append(self.evaluate(layer.bias)) - self.evaluate(distribution.finalize()) - loss_is_not_increasing = all(y <= x for x, y in zip(losses, losses[1:])) self.assertTrue(loss_is_not_increasing) diff --git a/tensorflow/contrib/distribute/python/step_fn_test.py b/tensorflow/contrib/distribute/python/step_fn_test.py index 1ff9b9ceec..a77d6d0bec 100644 --- a/tensorflow/contrib/distribute/python/step_fn_test.py +++ b/tensorflow/contrib/distribute/python/step_fn_test.py @@ -45,7 +45,6 @@ class SingleLossStepTest(test.TestCase, parameterized.TestCase): single_loss_step, layer = single_loss_example( optimizer_fn, distribution, use_bias=True, iterations_per_step=2) - self.evaluate(distribution.initialize()) if context.executing_eagerly(): run_step = single_loss_step else: @@ -57,12 +56,9 @@ class SingleLossStepTest(test.TestCase, parameterized.TestCase): weights, biases = [], [] for _ in range(5): run_step() - weights.append(self.evaluate(layer.kernel)) biases.append(self.evaluate(layer.bias)) - self.evaluate(distribution.finalize()) - error = abs(numpy.add(numpy.squeeze(weights), numpy.squeeze(biases)) - 1) is_not_increasing = all(y <= x for x, y in zip(error, error[1:])) self.assertTrue(is_not_increasing) -- GitLab From 1dee496b6addb67c269151d4a96b81608b1eb463 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Fri, 4 Jan 2019 14:43:02 -0800 Subject: [PATCH 370/622] TOCO's default_range parameters are floats, not integers. PiperOrigin-RevId: 227916837 --- tensorflow/lite/python/tflite_convert.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tensorflow/lite/python/tflite_convert.py b/tensorflow/lite/python/tflite_convert.py index 341b539bea..401a592273 100644 --- a/tensorflow/lite/python/tflite_convert.py +++ b/tensorflow/lite/python/tflite_convert.py @@ -343,13 +343,13 @@ def run_main(_): "floats. Used for quantized input tensors. (default None)")) parser.add_argument( "--default_ranges_min", - type=int, + type=float, help=("Default value for min bound of min/max range values used for all " "arrays without a specified range, Intended for experimenting with " "quantization via \"dummy quantization\". (default None)")) parser.add_argument( "--default_ranges_max", - type=int, + type=float, help=("Default value for max bound of min/max range values used for all " "arrays without a specified range, Intended for experimenting with " "quantization via \"dummy quantization\". (default None)")) -- GitLab From 58edbcb97b90596ee5f4cd907dfa7dd748c1843a Mon Sep 17 00:00:00 2001 From: Andrew Selle Date: Fri, 4 Jan 2019 15:03:28 -0800 Subject: [PATCH 371/622] Internal change. PiperOrigin-RevId: 227920137 --- tensorflow/tools/ci_build/builds/run_pip_tests.sh | 3 ++- tensorflow/tools/ci_build/ci_parameterized_build.sh | 9 ++++++--- tensorflow/tools/ci_build/osx/cpu/run_contrib.sh | 1 + tensorflow/tools/ci_build/osx/cpu/run_py2_cc_core.sh | 1 + 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/tensorflow/tools/ci_build/builds/run_pip_tests.sh b/tensorflow/tools/ci_build/builds/run_pip_tests.sh index 7d5cf3f843..a095633a22 100755 --- a/tensorflow/tools/ci_build/builds/run_pip_tests.sh +++ b/tensorflow/tools/ci_build/builds/run_pip_tests.sh @@ -88,7 +88,8 @@ if [[ ${IS_GPU} == "1" ]]; then PIP_TEST_FILTER_TAG="-no_gpu,-no_pip_gpu,${PIP_TEST_FILTER_TAG}" fi if [[ ${IS_MAC} == "1" ]]; then - PIP_TEST_FILTER_TAG="-nomac,${PIP_TEST_FILTER_TAG}" + # TODO(b/122370901): Fix nomac, no_mac inconsistency. + PIP_TEST_FILTER_TAG="-nomac,-no_mac,${PIP_TEST_FILTER_TAG}" fi # Bazel flags we need for all tests: diff --git a/tensorflow/tools/ci_build/ci_parameterized_build.sh b/tensorflow/tools/ci_build/ci_parameterized_build.sh index 435ec7ca68..41b9f241d5 100755 --- a/tensorflow/tools/ci_build/ci_parameterized_build.sh +++ b/tensorflow/tools/ci_build/ci_parameterized_build.sh @@ -398,7 +398,8 @@ if [[ "${TF_BUILD_APPEND_ARGUMENTS}" == *"--test_tag_filters="* ]]; then NEW_ITEM="${NEW_ITEM},-benchmark-test" fi if [[ ${IS_MAC} == "1" ]] && [[ ${NEW_ITEM} != *"nomac"* ]]; then - NEW_ITEM="${NEW_ITEM},-nomac" + # TODO(b/122370901): Fix nomac, no_mac inconsistency. + NEW_ITEM="${NEW_ITEM},-nomac,-no_mac" fi EXTRA_ARGS="${EXTRA_ARGS} ${NEW_ITEM}" else @@ -408,11 +409,13 @@ if [[ "${TF_BUILD_APPEND_ARGUMENTS}" == *"--test_tag_filters="* ]]; then else EXTRA_ARGS="${EXTRA_ARGS} ${TF_BUILD_APPEND_ARGUMENTS} --test_tag_filters=-no_oss,-oss_serial,-benchmark-test" if [[ ${IS_MAC} == "1" ]]; then - EXTRA_ARGS="${EXTRA_ARGS},-nomac" + # TODO(b/122370901): Fix nomac, no_mac inconsistency. + EXTRA_ARGS="${EXTRA_ARGS},-nomac,-no_mac" fi EXTRA_ARGS="${EXTRA_ARGS} --build_tag_filters=-no_oss,-oss_serial,-benchmark-test" if [[ ${IS_MAC} == "1" ]]; then - EXTRA_ARGS="${EXTRA_ARGS},-nomac" + # TODO(b/122370901): Fix nomac, no_mac inconsistency. + EXTRA_ARGS="${EXTRA_ARGS},-nomac,-no_mac" fi fi diff --git a/tensorflow/tools/ci_build/osx/cpu/run_contrib.sh b/tensorflow/tools/ci_build/osx/cpu/run_contrib.sh index 3efd994d78..1184d4acec 100755 --- a/tensorflow/tools/ci_build/osx/cpu/run_contrib.sh +++ b/tensorflow/tools/ci_build/osx/cpu/run_contrib.sh @@ -31,6 +31,7 @@ export CC_OPT_FLAGS='-mavx' export PYTHON_BIN_PATH=$(which python2) yes "" | $PYTHON_BIN_PATH configure.py which bazel +# TODO(b/122370901): Fix nomac, no_mac inconsistency. bazel test --test_tag_filters=-no_oss,-gpu,-benchmark-test,-nomac,-no_mac \ --test_timeout 300,450,1200,3600 \ --test_size_filters=small,medium --config=opt \ diff --git a/tensorflow/tools/ci_build/osx/cpu/run_py2_cc_core.sh b/tensorflow/tools/ci_build/osx/cpu/run_py2_cc_core.sh index adee0d3171..d39340b1d8 100755 --- a/tensorflow/tools/ci_build/osx/cpu/run_py2_cc_core.sh +++ b/tensorflow/tools/ci_build/osx/cpu/run_py2_cc_core.sh @@ -32,6 +32,7 @@ export CC_OPT_FLAGS='-mavx' export PYTHON_BIN_PATH=$(which python2) yes "" | $PYTHON_BIN_PATH configure.py which bazel +# TODO(b/122370901): Fix nomac, no_mac inconsistency. bazel test --test_tag_filters=-no_oss,-gpu,-benchmark-test,-nomac,-no_mac \ --test_timeout 300,450,1200,3600 --config=opt \ --announce_rc \ -- GitLab From 07777005d9b3feff4c8d74348739ad17de510131 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Fri, 4 Jan 2019 15:07:06 -0800 Subject: [PATCH 372/622] Implement memory prefetching for PresizedCuckooMap PiperOrigin-RevId: 227920727 --- tensorflow/core/BUILD | 1 + tensorflow/core/util/presized_cuckoo_map.h | 10 ++++ .../core/util/presized_cuckoo_map_test.cc | 49 ++++++++++++++++++- 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/tensorflow/core/BUILD b/tensorflow/core/BUILD index 7c9decbd09..460353df41 100644 --- a/tensorflow/core/BUILD +++ b/tensorflow/core/BUILD @@ -3813,6 +3813,7 @@ tf_cc_tests( "//tensorflow/core/kernels:ops_util", "//third_party/eigen3", "@com_google_absl//absl/base", + "@com_google_absl//absl/time", ], ) diff --git a/tensorflow/core/util/presized_cuckoo_map.h b/tensorflow/core/util/presized_cuckoo_map.h index f88ad2faaf..1cdde34562 100644 --- a/tensorflow/core/util/presized_cuckoo_map.h +++ b/tensorflow/core/util/presized_cuckoo_map.h @@ -20,6 +20,7 @@ limitations under the License. #include #include "tensorflow/core/framework/types.h" #include "tensorflow/core/platform/macros.h" +#include "tensorflow/core/platform/prefetch.h" namespace tensorflow { @@ -132,6 +133,15 @@ class PresizedCuckooMap { FindInBucket(k, fast_map_to_buckets(h2(tk)), out); } + // Prefetch memory associated with the key k into cache levels specified by + // hint. + template + void PrefetchKey(const key_type k) const { + const uint64 tk = key_transform(k); + port::prefetch(&buckets_[fast_map_to_buckets(tk)].keys); + port::prefetch(&buckets_[fast_map_to_buckets(h2(tk))].keys); + } + int64 MemoryUsed() const { return sizeof(PresizedCuckooMap) + sizeof(CuckooPathQueue); } diff --git a/tensorflow/core/util/presized_cuckoo_map_test.cc b/tensorflow/core/util/presized_cuckoo_map_test.cc index f2be1e8a2f..8dbb2ec065 100644 --- a/tensorflow/core/util/presized_cuckoo_map_test.cc +++ b/tensorflow/core/util/presized_cuckoo_map_test.cc @@ -13,12 +13,14 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "tensorflow/core/util/presized_cuckoo_map.h" #include + +#include "absl/time/clock.h" #include "tensorflow/core/platform/env.h" #include "tensorflow/core/platform/fingerprint.h" #include "tensorflow/core/platform/test.h" #include "tensorflow/core/platform/test_benchmark.h" +#include "tensorflow/core/util/presized_cuckoo_map.h" namespace tensorflow { namespace { @@ -50,6 +52,51 @@ TEST(PresizedCuckooMapTest, Basic) { EXPECT_EQ(out, 2); } +TEST(PresizedCuckooMapTest, Prefetch) { + { + PresizedCuckooMap pscm(2); + EXPECT_TRUE(pscm.InsertUnique(1, 2)); + // Works for both present and absent keys. + pscm.PrefetchKey(1); + pscm.PrefetchKey(2); + } + + // Do not run in debug mode, when prefetch is not implemented, or when + // sanitizers are enabled. +#if defined(NDEBUG) && defined(__GNUC__) && !defined(ADDRESS_SANITIZER) && \ + !defined(MEMORY_SANITIZER) && !defined(THREAD_SANITIZER) && \ + !defined(UNDEFINED_BEHAVIOR_SANITIZER) + const auto now = [] { return absl::Now(); }; + + // Make size enough to not fit in L2 cache (16.7 Mb) + static constexpr int size = 1 << 22; + PresizedCuckooMap pscm(size); + for (int i = 0; i < size; ++i) { + pscm.InsertUnique(i, i); + } + + absl::Duration no_prefetch, prefetch; + int64 out; + for (int iter = 0; iter < 10; ++iter) { + auto time = now(); + for (int i = 0; i < size; ++i) { + testing::DoNotOptimize(pscm.Find(i, &out)); + } + no_prefetch += now() - time; + + time = now(); + for (int i = 0; i < size; ++i) { + pscm.PrefetchKey(i + 20); + testing::DoNotOptimize(pscm.Find(i, &out)); + } + prefetch += now() - time; + } + + // no_prefetch is at least 30% slower. + EXPECT_GE(1.0 * no_prefetch / prefetch, 1.3); +#endif +} + TEST(PresizedCuckooMapTest, TooManyItems) { static constexpr int kTableSize = 1000; PresizedCuckooMap pscm(kTableSize); -- GitLab From 620fa70346ef6cd2c7f13c9986625850d523cf0b Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Fri, 4 Jan 2019 15:23:23 -0800 Subject: [PATCH 373/622] Go: Update generated wrapper functions for TensorFlow ops. PiperOrigin-RevId: 227923029 --- tensorflow/go/op/wrappers.go | 1844 ++++++++++++++++++++++++++++++++-- 1 file changed, 1782 insertions(+), 62 deletions(-) diff --git a/tensorflow/go/op/wrappers.go b/tensorflow/go/op/wrappers.go index 6fe61f512f..208c15d75a 100644 --- a/tensorflow/go/op/wrappers.go +++ b/tensorflow/go/op/wrappers.go @@ -327,6 +327,192 @@ func FakeQuantWithMinMaxArgs(scope *Scope, inputs tf.Output, optional ...FakeQua return op.Output(0) } +// Subtracts sparse `updates` from an existing tensor according to `indices`. +// +// This operation creates a new tensor by subtracting sparse `updates` from the +// passed in `tensor`. +// This operation is very similar to `tf.scatter_nd_sub`, except that the updates +// are subtracted from an existing tensor (as opposed to a variable). If the memory +// for the existing tensor cannot be re-used, a copy is made and updated. +// +// `indices` is an integer tensor containing indices into a new tensor of shape +// `shape`. The last dimension of `indices` can be at most the rank of `shape`: +// +// indices.shape[-1] <= shape.rank +// +// The last dimension of `indices` corresponds to indices into elements +// (if `indices.shape[-1] = shape.rank`) or slices +// (if `indices.shape[-1] < shape.rank`) along dimension `indices.shape[-1]` of +// `shape`. `updates` is a tensor with shape +// +// indices.shape[:-1] + shape[indices.shape[-1]:] +// +// The simplest form of tensor_scatter_sub is to subtract individual elements +// from a tensor by index. For example, say we want to insert 4 scattered elements +// in a rank-1 tensor with 8 elements. +// +// In Python, this scatter subtract operation would look like this: +// +// ```python +// indices = tf.constant([[4], [3], [1], [7]]) +// updates = tf.constant([9, 10, 11, 12]) +// tensor = tf.ones([8], dtype=tf.int32) +// updated = tf.tensor_scatter_sub(tensor, indices, updates) +// with tf.Session() as sess: +// print(sess.run(scatter)) +// ``` +// +// The resulting tensor would look like this: +// +// [1, -10, 1, -9, -8, 1, 1, -11] +// +// We can also, insert entire slices of a higher rank tensor all at once. For +// example, if we wanted to insert two slices in the first dimension of a +// rank-3 tensor with two matrices of new values. +// +// In Python, this scatter add operation would look like this: +// +// ```python +// indices = tf.constant([[0], [2]]) +// updates = tf.constant([[[5, 5, 5, 5], [6, 6, 6, 6], +// [7, 7, 7, 7], [8, 8, 8, 8]], +// [[5, 5, 5, 5], [6, 6, 6, 6], +// [7, 7, 7, 7], [8, 8, 8, 8]]]) +// tensor = tf.ones([4, 4, 4]) +// updated = tf.tensor_scatter_sub(tensor, indices, updates) +// with tf.Session() as sess: +// print(sess.run(scatter)) +// ``` +// +// The resulting tensor would look like this: +// +// [[[-4, -4, -4, -4], [-5, -5, -5, -5], [-6, -6, -6, -6], [-7, -7, -7, -7]], +// [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]], +// [[-4, -4, -4, -4], [-5, -5, -5, -5], [-6, -6, -6, -6], [-7, -7, -7, -7]], +// [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]] +// +// Note that on CPU, if an out of bound index is found, an error is returned. +// On GPU, if an out of bound index is found, the index is ignored. +// +// Arguments: +// tensor: Tensor to copy/update. +// indices: Index tensor. +// updates: Updates to scatter into output. +// +// Returns A new tensor copied from tensor and updates subtracted according to the indices. +func TensorScatterSub(scope *Scope, tensor tf.Output, indices tf.Output, updates tf.Output) (output tf.Output) { + if scope.Err() != nil { + return + } + opspec := tf.OpSpec{ + Type: "TensorScatterSub", + Input: []tf.Input{ + tensor, indices, updates, + }, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + +// Scatter `updates` into an existing tensor according to `indices`. +// +// This operation creates a new tensor by applying sparse `updates` to the passed +// in `tensor`. +// This operation is very similar to `tf.scatter_nd`, except that the updates are +// scattered onto an existing tensor (as opposed to a zero-tensor). If the memory +// for the existing tensor cannot be re-used, a copy is made and updated. +// +// If `indices` contains duplicates, then their updates are accumulated (summed). +// +// **WARNING**: The order in which updates are applied is nondeterministic, so the +// output will be nondeterministic if `indices` contains duplicates -- because +// of some numerical approximation issues, numbers summed in different order +// may yield different results. +// +// `indices` is an integer tensor containing indices into a new tensor of shape +// `shape`. The last dimension of `indices` can be at most the rank of `shape`: +// +// indices.shape[-1] <= shape.rank +// +// The last dimension of `indices` corresponds to indices into elements +// (if `indices.shape[-1] = shape.rank`) or slices +// (if `indices.shape[-1] < shape.rank`) along dimension `indices.shape[-1]` of +// `shape`. `updates` is a tensor with shape +// +// indices.shape[:-1] + shape[indices.shape[-1]:] +// +// The simplest form of scatter is to insert individual elements in a tensor by +// index. For example, say we want to insert 4 scattered elements in a rank-1 +// tensor with 8 elements. +// +//
    +// +//
    +// +// In Python, this scatter operation would look like this: +// +// ```python +// indices = tf.constant([[4], [3], [1], [7]]) +// updates = tf.constant([9, 10, 11, 12]) +// tensor = tf.ones([8], dtype=tf.int32) +// updated = tf.tensor_scatter_update(tensor, indices, updates) +// with tf.Session() as sess: +// print(sess.run(scatter)) +// ``` +// +// The resulting tensor would look like this: +// +// [1, 11, 1, 10, 9, 1, 1, 12] +// +// We can also, insert entire slices of a higher rank tensor all at once. For +// example, if we wanted to insert two slices in the first dimension of a +// rank-3 tensor with two matrices of new values. +// +// In Python, this scatter operation would look like this: +// +// ```python +// indices = tf.constant([[0], [2]]) +// updates = tf.constant([[[5, 5, 5, 5], [6, 6, 6, 6], +// [7, 7, 7, 7], [8, 8, 8, 8]], +// [[5, 5, 5, 5], [6, 6, 6, 6], +// [7, 7, 7, 7], [8, 8, 8, 8]]]) +// tensor = tf.ones([4, 4, 4]) +// updated = tf.tensor_scatter_update(tensor, indices, updates) +// with tf.Session() as sess: +// print(sess.run(scatter)) +// ``` +// +// The resulting tensor would look like this: +// +// [[[5, 5, 5, 5], [6, 6, 6, 6], [7, 7, 7, 7], [8, 8, 8, 8]], +// [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]], +// [[5, 5, 5, 5], [6, 6, 6, 6], [7, 7, 7, 7], [8, 8, 8, 8]], +// [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]] +// +// Note that on CPU, if an out of bound index is found, an error is returned. +// On GPU, if an out of bound index is found, the index is ignored. +// +// Arguments: +// tensor: Tensor to copy/update. +// indices: Index tensor. +// updates: Updates to scatter into output. +// +// Returns A new tensor with the given shape and updates applied according +// to the indices. +func TensorScatterUpdate(scope *Scope, tensor tf.Output, indices tf.Output, updates tf.Output) (output tf.Output) { + if scope.Err() != nil { + return + } + opspec := tf.OpSpec{ + Type: "TensorScatterUpdate", + Input: []tf.Input{ + tensor, indices, updates, + }, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // Scatter `updates` into a new tensor according to `indices`. // // Creates a new tensor by applying sparse `updates` to individual values or @@ -334,6 +520,10 @@ func FakeQuantWithMinMaxArgs(scope *Scope, inputs tf.Output, optional ...FakeQua // the given `shape` according to indices. This operator is the inverse of the // `tf.gather_nd` operator which extracts values or slices from a given tensor. // +// This operation is similar to tensor_scatter_add, except that the tensor is +// zero-initialized. Calling `tf.scatter_nd(indices, values, shape)` is identical +// to `tensor_scatter_add(tf.zeros(shape, values.dtype), indices, values)` +// // If `indices` contains duplicates, then their updates are accumulated (summed). // // **WARNING**: The order in which updates are applied is nondeterministic, so the @@ -464,6 +654,15 @@ func QuantizeAndDequantizeV2RangeGiven(value bool) QuantizeAndDequantizeV2Attr { } // QuantizeAndDequantizeV2RoundMode sets the optional round_mode attribute to value. +// +// value: The 'round_mode' attribute controls which rounding tie-breaking algorithm is +// used when rounding float values to their quantized equivalents. The following +// rounding modes are currently supported: +// +// * HALF_TO_EVEN: this is the default round_mode. +// * HALF_UP: round towards positive. In this mode 7.5 rounds up to 8 and -7.5 +// rounds up to -7. +// // If not specified, defaults to "HALF_TO_EVEN" func QuantizeAndDequantizeV2RoundMode(value string) QuantizeAndDequantizeV2Attr { return func(m optionalAttr) { @@ -523,7 +722,7 @@ func QuantizeAndDequantizeV2RoundMode(value string) QuantizeAndDequantizeV2Attr // // output = round(clamp(value, input_min, input_max) * scale_factor) / scale_factor. // -// The above round function uses half to even rounding. +// The above round function rounds the value based on the given round_mode. // // // Arguments: @@ -3422,11 +3621,11 @@ func PopulationCount(scope *Scope, x tf.Output) (y tf.Output) { // bucketized values for a single feature. // // Arguments: -// float_values: float; List of Rank 2 Tensor each containing float values for a single feature. +// float_values: float; List of Rank 1 Tensor each containing float values for a single feature. // bucket_boundaries: float; List of Rank 1 Tensors each containing the bucket boundaries for a single // feature. // -// Returns int; List of Rank 2 Tensors each containing the bucketized values for a single feature. +// Returns int; List of Rank 1 Tensors each containing the bucketized values for a single feature. func BoostedTreesBucketize(scope *Scope, float_values []tf.Output, bucket_boundaries []tf.Output) (buckets []tf.Output) { if scope.Err() != nil { return @@ -3497,15 +3696,16 @@ func BoostedTreesQuantileStreamResourceFlush(scope *Scope, quantile_stream_resou // Makes the summary of quantiles for the batch. // -// An op that takes a list of tensors and outputs the quantile summaries for each tensor. +// An op that takes a list of tensors (one tensor per feature) and outputs the +// quantile summaries for each tensor. // // Arguments: -// float_values: float; List of Rank 2 Tensors each containing values for a single feature. +// float_values: float; List of Rank 1 Tensors each containing values for a single feature. // example_weights: float; Rank 1 Tensor with weights per instance. // epsilon: float; The required maximum approximation error. // -// Returns float; List of Rank 2 Tensors each containing the quantile summary (value, weight, -// min_rank, max_rank) of a single feature. +// Returns float; List of Rank 2 Tensors each containing the quantile summary +// (value, weight, min_rank, max_rank) of a single feature. func BoostedTreesMakeQuantileSummaries(scope *Scope, float_values []tf.Output, example_weights tf.Output, epsilon tf.Output) (summaries []tf.Output) { if scope.Err() != nil { return @@ -3806,6 +4006,70 @@ func BoostedTreesEnsembleResourceHandleOp(scope *Scope, optional ...BoostedTrees return op.Output(0) } +// Output the logits for the given input data +// +// Arguments: +// tree_handle: Handle to the tree resource. +// dense_features: Rank 2 dense features tensor. +// logits_dimension: Scalar, dimension of the logits. +// +// Returns The logits predictions from the tree for each instance in the batch. +func TensorForestTreePredict(scope *Scope, tree_handle tf.Output, dense_features tf.Output, logits_dimension int64) (logits tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{"logits_dimension": logits_dimension} + opspec := tf.OpSpec{ + Type: "TensorForestTreePredict", + Input: []tf.Input{ + tree_handle, dense_features, + }, + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + +// Get the number of nodes in a tree +// +// Arguments: +// tree_handle: Handle to the tree resource. +// +// Returns The size of the tree. +func TensorForestTreeSize(scope *Scope, tree_handle tf.Output) (tree_size tf.Output) { + if scope.Err() != nil { + return + } + opspec := tf.OpSpec{ + Type: "TensorForestTreeSize", + Input: []tf.Input{ + tree_handle, + }, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + +// Creates a tree resource and returns a handle to it. +// +// Arguments: +// tree_handle: Handle to the tree resource to be created. +// tree_config: Serialized proto string of the boosted_trees.Tree. +// +// Returns the created operation. +func TensorForestCreateTreeVariable(scope *Scope, tree_handle tf.Output, tree_config tf.Output) (o *tf.Operation) { + if scope.Err() != nil { + return + } + opspec := tf.OpSpec{ + Type: "TensorForestCreateTreeVariable", + Input: []tf.Input{ + tree_handle, tree_config, + }, + } + return scope.AddOperation(opspec) +} + // ComputeAccidentalHitsAttr is an optional argument to ComputeAccidentalHits. type ComputeAccidentalHitsAttr func(optionalAttr) @@ -4829,6 +5093,119 @@ func CudnnRNNParamsToCanonical(scope *Scope, num_layers tf.Output, num_units tf. return weights, biases } +// CudnnRNNBackpropV3Attr is an optional argument to CudnnRNNBackpropV3. +type CudnnRNNBackpropV3Attr func(optionalAttr) + +// CudnnRNNBackpropV3RnnMode sets the optional rnn_mode attribute to value. +// If not specified, defaults to "lstm" +func CudnnRNNBackpropV3RnnMode(value string) CudnnRNNBackpropV3Attr { + return func(m optionalAttr) { + m["rnn_mode"] = value + } +} + +// CudnnRNNBackpropV3InputMode sets the optional input_mode attribute to value. +// If not specified, defaults to "linear_input" +func CudnnRNNBackpropV3InputMode(value string) CudnnRNNBackpropV3Attr { + return func(m optionalAttr) { + m["input_mode"] = value + } +} + +// CudnnRNNBackpropV3Direction sets the optional direction attribute to value. +// If not specified, defaults to "unidirectional" +func CudnnRNNBackpropV3Direction(value string) CudnnRNNBackpropV3Attr { + return func(m optionalAttr) { + m["direction"] = value + } +} + +// CudnnRNNBackpropV3Dropout sets the optional dropout attribute to value. +// If not specified, defaults to 0 +func CudnnRNNBackpropV3Dropout(value float32) CudnnRNNBackpropV3Attr { + return func(m optionalAttr) { + m["dropout"] = value + } +} + +// CudnnRNNBackpropV3Seed sets the optional seed attribute to value. +// If not specified, defaults to 0 +func CudnnRNNBackpropV3Seed(value int64) CudnnRNNBackpropV3Attr { + return func(m optionalAttr) { + m["seed"] = value + } +} + +// CudnnRNNBackpropV3Seed2 sets the optional seed2 attribute to value. +// If not specified, defaults to 0 +func CudnnRNNBackpropV3Seed2(value int64) CudnnRNNBackpropV3Attr { + return func(m optionalAttr) { + m["seed2"] = value + } +} + +// Backprop step of CudnnRNNV3. +// +// Compute the backprop of both data and weights in a RNN. Takes an extra +// "sequence_lengths" input than CudnnRNNBackprop. +// +// rnn_mode: Indicates the type of the RNN model. +// input_mode: Indicates whether there is a linear projection between the input and +// the actual computation before the first layer. 'skip_input' is only allowed +// when input_size == num_units; 'auto_select' implies 'skip_input' when +// input_size == num_units; otherwise, it implies 'linear_input'. +// direction: Indicates whether a bidirectional model will be used. Should be +// "unidirectional" or "bidirectional". +// dropout: Dropout probability. When set to 0., dropout is disabled. +// seed: The 1st part of a seed to initialize dropout. +// seed2: The 2nd part of a seed to initialize dropout. +// input: A 3-D tensor with the shape of [seq_length, batch_size, input_size]. +// input_h: A 3-D tensor with the shape of [num_layer * dir, batch_size, +// num_units]. +// input_c: For LSTM, a 3-D tensor with the shape of +// [num_layer * dir, batch, num_units]. For other models, it is ignored. +// params: A 1-D tensor that contains the weights and biases in an opaque layout. +// The size must be created through CudnnRNNParamsSize, and initialized +// separately. Note that they might not be compatible across different +// generations. So it is a good idea to save and restore +// sequence_lengths: a vector of lengths of each input sequence. +// output: A 3-D tensor with the shape of [seq_length, batch_size, +// dir * num_units]. +// output_h: The same shape has input_h. +// output_c: The same shape as input_c for LSTM. An empty tensor for other models. +// output_backprop: A 3-D tensor with the same shape as output in the forward pass. +// output_h_backprop: A 3-D tensor with the same shape as output_h in the forward +// pass. +// output_c_backprop: A 3-D tensor with the same shape as output_c in the forward +// pass. +// reserve_space: The same reserve_space produced in the forward operation. +// input_backprop: The backprop to input in the forward pass. Has the same shape +// as input. +// input_h_backprop: The backprop to input_h in the forward pass. Has the same +// shape as input_h. +// input_c_backprop: The backprop to input_c in the forward pass. Has the same +// shape as input_c. +// params_backprop: The backprop to the params buffer in the forward pass. Has the +// same shape as params. +func CudnnRNNBackpropV3(scope *Scope, input tf.Output, input_h tf.Output, input_c tf.Output, params tf.Output, sequence_lengths tf.Output, output tf.Output, output_h tf.Output, output_c tf.Output, output_backprop tf.Output, output_h_backprop tf.Output, output_c_backprop tf.Output, reserve_space tf.Output, host_reserved tf.Output, optional ...CudnnRNNBackpropV3Attr) (input_backprop tf.Output, input_h_backprop tf.Output, input_c_backprop tf.Output, params_backprop tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{} + for _, a := range optional { + a(attrs) + } + opspec := tf.OpSpec{ + Type: "CudnnRNNBackpropV3", + Input: []tf.Input{ + input, input_h, input_c, params, sequence_lengths, output, output_h, output_c, output_backprop, output_h_backprop, output_c_backprop, reserve_space, host_reserved, + }, + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0), op.Output(1), op.Output(2), op.Output(3) +} + // CudnnRNNBackpropV2Attr is an optional argument to CudnnRNNBackpropV2. type CudnnRNNBackpropV2Attr func(optionalAttr) @@ -6697,6 +7074,34 @@ func Sign(scope *Scope, x tf.Output) (y tf.Output) { return op.Output(0) } +// Creates a dataset that passes a sliding window over `input_dataset`. +// +// Arguments: +// +// window_size: A scalar representing the number of elements in the +// sliding window. +// window_shift: A scalar representing the steps moving the sliding window +// forward in one iteration. It must be positive. +// window_stride: A scalar representing the stride of the input elements of the sliding window. +// It must be positive. +// +// +func ExperimentalSlidingWindowDataset(scope *Scope, input_dataset tf.Output, window_size tf.Output, window_shift tf.Output, window_stride tf.Output, output_types []tf.DataType, output_shapes []tf.Shape) (handle tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{"output_types": output_types, "output_shapes": output_shapes} + opspec := tf.OpSpec{ + Type: "ExperimentalSlidingWindowDataset", + Input: []tf.Input{ + input_dataset, window_size, window_shift, window_stride, + }, + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // Returns which elements of x are finite. // // @compatibility(numpy) @@ -7608,6 +8013,33 @@ func Inv(scope *Scope, x tf.Output) (y tf.Output) { return op.Output(0) } +// Creates a dataset that batches input elements into a SparseTensor. +// +// Arguments: +// input_dataset: A handle to an input dataset. Must have a single component. +// batch_size: A scalar representing the number of elements to accumulate in a +// batch. +// row_shape: A vector representing the dense shape of each row in the produced +// SparseTensor. The shape may be partially specified, using `-1` to indicate +// that a particular dimension should use the maximum size of all batch elements. +// +// +func ExperimentalDenseToSparseBatchDataset(scope *Scope, input_dataset tf.Output, batch_size tf.Output, row_shape tf.Output, output_types []tf.DataType, output_shapes []tf.Shape) (handle tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{"output_types": output_types, "output_shapes": output_shapes} + opspec := tf.OpSpec{ + Type: "ExperimentalDenseToSparseBatchDataset", + Input: []tf.Input{ + input_dataset, batch_size, row_shape, + }, + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // ComplexAbsAttr is an optional argument to ComplexAbs. type ComplexAbsAttr func(optionalAttr) @@ -8442,6 +8874,26 @@ func ResourceSparseApplyAdadelta(scope *Scope, var_ tf.Output, accum tf.Output, return scope.AddOperation(opspec) } +// Checks whether a tree has been initialized. +// +// Arguments: +// tree_handle: Handle to the tree. +// +// Returns Whether the tree is initialized. +func TensorForestTreeIsInitializedOp(scope *Scope, tree_handle tf.Output) (is_initialized tf.Output) { + if scope.Err() != nil { + return + } + opspec := tf.OpSpec{ + Type: "TensorForestTreeIsInitializedOp", + Input: []tf.Input{ + tree_handle, + }, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // Gets next element for the provided shard number. // // Arguments: @@ -10977,7 +11429,6 @@ func OneHotAxis(value int64) OneHotAttr { // ========= // // Suppose that -// // ``` // indices = [0, 2, -1, 1] // depth = 3 @@ -10987,16 +11438,15 @@ func OneHotAxis(value int64) OneHotAttr { // ``` // // Then output is `[4 x 3]`: -// -// ```output = -// [5.0 0.0 0.0] // one_hot(0) -// [0.0 0.0 5.0] // one_hot(2) -// [0.0 0.0 0.0] // one_hot(-1) -// [0.0 5.0 0.0] // one_hot(1) -// ``` +// ``` +// output = +// [5.0 0.0 0.0] // one_hot(0) +// [0.0 0.0 5.0] // one_hot(2) +// [0.0 0.0 0.0] // one_hot(-1) +// [0.0 5.0 0.0] // one_hot(1) +// ``` // // Suppose that -// // ``` // indices = [0, 2, -1, 1] // depth = 3 @@ -11006,19 +11456,19 @@ func OneHotAxis(value int64) OneHotAttr { // ``` // // Then output is `[3 x 4]`: +// ``` +// output = +// [0.0 3.0 3.0 3.0] +// [3.0 3.0 3.0 0.0] +// [3.0 3.0 3.0 3.0] +// [3.0 0.0 3.0 3.0] +// // ^ one_hot(0) +// // ^ one_hot(2) +// // ^ one_hot(-1) +// // ^ one_hot(1) +// ``` // -// ```output = -// [0.0 3.0 3.0 3.0] -// [3.0 3.0 3.0 0.0] -// [3.0 3.0 3.0 3.0] -// [3.0 0.0 3.0 3.0] -// // ^ one_hot(0) -// // ^ one_hot(2) -// // ^ one_hot(-1) -// // ^ one_hot(1) -// ``` // Suppose that -// // ``` // indices = [[0, 2], [1, -1]] // depth = 3 @@ -11028,15 +11478,16 @@ func OneHotAxis(value int64) OneHotAttr { // ``` // // Then output is `[2 x 2 x 3]`: -// -// ```output = -// [ -// [1.0, 0.0, 0.0] // one_hot(0) -// [0.0, 0.0, 1.0] // one_hot(2) -// ][ -// [0.0, 1.0, 0.0] // one_hot(1) -// [0.0, 0.0, 0.0] // one_hot(-1) -// ]``` +// ``` +// output = +// [ +// [1.0, 0.0, 0.0] // one_hot(0) +// [0.0, 0.0, 1.0] // one_hot(2) +// ][ +// [0.0, 1.0, 0.0] // one_hot(1) +// [0.0, 0.0, 0.0] // one_hot(-1) +// ] +// ``` // // Arguments: // indices: A tensor of indices. @@ -11594,8 +12045,8 @@ func ResizeBicubic(scope *Scope, images tf.Output, size tf.Output, optional ...R // Arguments: // params_nested_splits: The `nested_row_splits` tensors that define the row-partitioning for the // `params` RaggedTensor input. -// params_dense_values: The `inner_values` for the `params` RaggedTensor. There was a terminology change -// at the python level from dense_values to inner_values, so dense_values is the +// params_dense_values: The `flat_values` for the `params` RaggedTensor. There was a terminology change +// at the python level from dense_values to flat_values, so dense_values is the // deprecated name. // indices: Indices in the outermost dimension of `params` of the values that should be // gathered. @@ -11604,7 +12055,7 @@ func ResizeBicubic(scope *Scope, images tf.Output, size tf.Output, optional ...R // `indices.shape.ndims + params.ragged_rank - 1`. // // Returns The `nested_row_splits` tensors that define the row-partitioning for the -// returned RaggedTensor.The `inner_values` for the returned RaggedTensor. +// returned RaggedTensor.The `flat_values` for the returned RaggedTensor. func RaggedGather(scope *Scope, params_nested_splits []tf.Output, params_dense_values tf.Output, indices tf.Output, OUTPUT_RAGGED_RANK int64) (output_nested_splits []tf.Output, output_dense_values tf.Output) { if scope.Err() != nil { return @@ -11685,7 +12136,7 @@ func NonMaxSuppressionV2(scope *Scope, boxes tf.Output, scores tf.Output, max_ou // // Arguments: // rt_nested_splits: The `row_splits` for the `RaggedTensor`. -// rt_dense_values: The `inner_values` for the `RaggedTensor`. +// rt_dense_values: The `flat_values` for the `RaggedTensor`. // // Returns The indices for the `SparseTensor`.The values of the `SparseTensor`.`sparse_dense_shape` is a tight bounding box of the input `RaggedTensor`. func RaggedTensorToSparse(scope *Scope, rt_nested_splits []tf.Output, rt_dense_values tf.Output) (sparse_indices tf.Output, sparse_values tf.Output, sparse_dense_shape tf.Output) { @@ -12936,9 +13387,7 @@ func ResourceScatterNdAddUseLocking(value bool) ResourceScatterNdAddAttr { } } -// Adds sparse `updates` to individual values or slices within a given -// -// variable according to `indices`. +// Applies sparse addition to individual values or slices in a Variable. // // `ref` is a `Tensor` with rank `P` and `indices` is a `Tensor` of rank `Q`. // @@ -12952,24 +13401,24 @@ func ResourceScatterNdAddUseLocking(value bool) ResourceScatterNdAddAttr { // `updates` is `Tensor` of rank `Q-1+P-K` with shape: // // ``` -// [d_0, ..., d_{Q-2}, ref.shape[K], ..., ref.shape[P-1]]. +// [d_0, ..., d_{Q-2}, ref.shape[K], ..., ref.shape[P-1]] // ``` // -// For example, say we want to update 4 scattered elements to a rank-1 tensor to -// 8 elements. In Python, that update would look like this: +// For example, say we want to add 4 scattered elements to a rank-1 tensor to +// 8 elements. In Python, that addition would look like this: // // ```python -// ref = tf.Variable([1, 2, 3, 4, 5, 6, 7, 8], use_resource=True) -// indices = tf.constant([[4], [3], [1] ,[7]]) -// updates = tf.constant([9, 10, 11, 12]) -// update = tf.scatter_nd_add(ref, indices, updates) -// with tf.Session() as sess: -// print sess.run(update) +// ref = tf.Variable([1, 2, 3, 4, 5, 6, 7, 8], use_resource=True) +// indices = tf.constant([[4], [3], [1], [7]]) +// updates = tf.constant([9, 10, 11, 12]) +// add = tf.scatter_nd_add(ref, indices, updates) +// with tf.Session() as sess: +// print sess.run(add) // ``` // // The resulting update to ref would look like this: // -// [1, 12, 3, 14, 14, 6, 7, 20] +// [1, 13, 3, 14, 14, 6, 7, 20] // // See `tf.scatter_nd` for more details about how to make updates to // slices. @@ -13274,6 +13723,91 @@ func StatelessRandomNormal(scope *Scope, shape tf.Output, seed tf.Output, option return op.Output(0) } +// UnicodeDecodeAttr is an optional argument to UnicodeDecode. +type UnicodeDecodeAttr func(optionalAttr) + +// UnicodeDecodeErrors sets the optional errors attribute to value. +// +// value: Error handling policy when there is invalid formatting found in the input. +// The value of 'strict' will cause the operation to produce a InvalidArgument +// error on any invalid input formatting. A value of 'replace' (the default) will +// cause the operation to replace any invalid formatting in the input with the +// `replacement_char` codepoint. A value of 'ignore' will cause the operation to +// skip any invalid formatting in the input and produce no corresponding output +// character. +// If not specified, defaults to "replace" +func UnicodeDecodeErrors(value string) UnicodeDecodeAttr { + return func(m optionalAttr) { + m["errors"] = value + } +} + +// UnicodeDecodeReplacementChar sets the optional replacement_char attribute to value. +// +// value: The replacement character codepoint to be used in place of any invalid +// formatting in the input when `errors='replace'`. Any valid unicode codepoint may +// be used. The default value is the default unicode replacement character is +// 0xFFFD or U+65533.) +// If not specified, defaults to 65533 +func UnicodeDecodeReplacementChar(value int64) UnicodeDecodeAttr { + return func(m optionalAttr) { + m["replacement_char"] = value + } +} + +// UnicodeDecodeReplaceControlCharacters sets the optional replace_control_characters attribute to value. +// +// value: Whether to replace the C0 control characters (00-1F) with the +// `replacement_char`. Default is false. +// If not specified, defaults to false +func UnicodeDecodeReplaceControlCharacters(value bool) UnicodeDecodeAttr { + return func(m optionalAttr) { + m["replace_control_characters"] = value + } +} + +// Decodes each string in `input` into a sequence of Unicode code points. +// +// The character codepoints for all strings are returned using a single vector +// `char_values`, with strings expanded to characters in row-major order. +// +// The `row_splits` tensor indicates where the codepoints for +// each input string begin and end within the `char_values` tensor. +// In particular, the values for the `i`th +// string (in row-major order) are stored in the slice +// `[row_splits[i]:row_splits[i+1]]`. Thus: +// +// * `char_values[row_splits[i]+j]` is the Unicode codepoint for the `j`th +// character in the `i`th string (in row-major order). +// * `row_splits[i+1] - row_splits[i]` is the number of characters in the `i`th +// string (in row-major order). +// +// Arguments: +// input: The text to be decoded. Can have any shape. Note that the output is flattened +// to a vector of char values. +// input_encoding: Text encoding of the input strings. This is any of the encodings supported +// by ICU ucnv algorithmic converters. Examples: `"UTF-16", "US ASCII", "UTF-8"`. +// +// Returns A 1D int32 tensor containing the row splits.A 1D int32 Tensor containing the decoded codepoints. +func UnicodeDecode(scope *Scope, input tf.Output, input_encoding string, optional ...UnicodeDecodeAttr) (row_splits tf.Output, char_values tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{"input_encoding": input_encoding} + for _, a := range optional { + a(attrs) + } + opspec := tf.OpSpec{ + Type: "UnicodeDecode", + Input: []tf.Input{ + input, + }, + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0), op.Output(1) +} + // Adds up a SparseTensor and a dense Tensor, using these special rules: // // (1) Broadcasts the dense side to have the same shape as the sparse side, if @@ -13322,6 +13856,84 @@ func Erfc(scope *Scope, x tf.Output) (y tf.Output) { return op.Output(0) } +// UnicodeEncodeAttr is an optional argument to UnicodeEncode. +type UnicodeEncodeAttr func(optionalAttr) + +// UnicodeEncodeErrors sets the optional errors attribute to value. +// +// value: Error handling policy when there is invalid formatting found in the input. +// The value of 'strict' will cause the operation to produce a InvalidArgument +// error on any invalid input formatting. A value of 'replace' (the default) will +// cause the operation to replace any invalid formatting in the input with the +// `replacement_char` codepoint. A value of 'ignore' will cause the operation to +// skip any invalid formatting in the input and produce no corresponding output +// character. +// If not specified, defaults to "replace" +func UnicodeEncodeErrors(value string) UnicodeEncodeAttr { + return func(m optionalAttr) { + m["errors"] = value + } +} + +// UnicodeEncodeReplacementChar sets the optional replacement_char attribute to value. +// +// value: The replacement character codepoint to be used in place of any invalid +// formatting in the input when `errors='replace'`. Any valid unicode codepoint may +// be used. The default value is the default unicode replacement character is +// 0xFFFD (U+65533). +// If not specified, defaults to 65533 +func UnicodeEncodeReplacementChar(value int64) UnicodeEncodeAttr { + return func(m optionalAttr) { + m["replacement_char"] = value + } +} + +// Encode a tensor of ints into unicode strings. +// +// Returns a vector of strings, where `output[i]` is constructed by encoding the +// Unicode codepoints in `input_values[input_splits[i]:input_splits[i+1]]` +// using `output_encoding`. +// +// --- +// +// Example: +// +// ``` +// input_values = [72, 101, 108, 108, 111, 87, 111, 114, 108, 100] +// input_splits = [0, 5, 10] +// output_encoding = 'UTF-8' +// +// output = ['Hello', 'World'] +// ``` +// +// Arguments: +// input_values: A 1D tensor containing the unicode codepoints that should be encoded. +// input_splits: A 1D tensor specifying how the unicode codepoints should be split into strings. +// In particular, `output[i]` is constructed by encoding the codepoints in the +// slice `input_values[input_splits[i]:input_splits[i+1]]`. +// output_encoding: Unicode encoding of the output strings. Valid encodings are: `"UTF-8", +// "UTF-16-BE", and "UTF-32-BE"`. +// +// Returns The 1-D Tensor of strings encoded from the provided unicode codepoints. +func UnicodeEncode(scope *Scope, input_values tf.Output, input_splits tf.Output, output_encoding string, optional ...UnicodeEncodeAttr) (output tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{"output_encoding": output_encoding} + for _, a := range optional { + a(attrs) + } + opspec := tf.OpSpec{ + Type: "UnicodeEncode", + Input: []tf.Input{ + input_values, input_splits, + }, + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // Returns the number of tensors in the input tensor list. // // input_handle: the input list @@ -14644,6 +15256,117 @@ func MultiDeviceIteratorToStringHandle(scope *Scope, multi_device_iterator tf.Ou return op.Output(0) } +// CudnnRNNV3Attr is an optional argument to CudnnRNNV3. +type CudnnRNNV3Attr func(optionalAttr) + +// CudnnRNNV3RnnMode sets the optional rnn_mode attribute to value. +// If not specified, defaults to "lstm" +func CudnnRNNV3RnnMode(value string) CudnnRNNV3Attr { + return func(m optionalAttr) { + m["rnn_mode"] = value + } +} + +// CudnnRNNV3InputMode sets the optional input_mode attribute to value. +// If not specified, defaults to "linear_input" +func CudnnRNNV3InputMode(value string) CudnnRNNV3Attr { + return func(m optionalAttr) { + m["input_mode"] = value + } +} + +// CudnnRNNV3Direction sets the optional direction attribute to value. +// If not specified, defaults to "unidirectional" +func CudnnRNNV3Direction(value string) CudnnRNNV3Attr { + return func(m optionalAttr) { + m["direction"] = value + } +} + +// CudnnRNNV3Dropout sets the optional dropout attribute to value. +// If not specified, defaults to 0 +func CudnnRNNV3Dropout(value float32) CudnnRNNV3Attr { + return func(m optionalAttr) { + m["dropout"] = value + } +} + +// CudnnRNNV3Seed sets the optional seed attribute to value. +// If not specified, defaults to 0 +func CudnnRNNV3Seed(value int64) CudnnRNNV3Attr { + return func(m optionalAttr) { + m["seed"] = value + } +} + +// CudnnRNNV3Seed2 sets the optional seed2 attribute to value. +// If not specified, defaults to 0 +func CudnnRNNV3Seed2(value int64) CudnnRNNV3Attr { + return func(m optionalAttr) { + m["seed2"] = value + } +} + +// CudnnRNNV3IsTraining sets the optional is_training attribute to value. +// If not specified, defaults to true +func CudnnRNNV3IsTraining(value bool) CudnnRNNV3Attr { + return func(m optionalAttr) { + m["is_training"] = value + } +} + +// A RNN backed by cuDNN. +// +// Computes the RNN from the input and initial states, with respect to the params +// buffer. Accepts one extra input "sequence_lengths" than CudnnRNN. +// +// rnn_mode: Indicates the type of the RNN model. +// input_mode: Indicates whether there is a linear projection between the input and +// the actual computation before the first layer. 'skip_input' is only allowed +// when input_size == num_units; 'auto_select' implies 'skip_input' when +// input_size == num_units; otherwise, it implies 'linear_input'. +// direction: Indicates whether a bidirectional model will be used. Should be +// "unidirectional" or "bidirectional". +// dropout: Dropout probability. When set to 0., dropout is disabled. +// seed: The 1st part of a seed to initialize dropout. +// seed2: The 2nd part of a seed to initialize dropout. +// input: A 3-D tensor with the shape of [seq_length, batch_size, input_size]. +// input_h: A 3-D tensor with the shape of [num_layer * dir, batch_size, +// num_units]. +// input_c: For LSTM, a 3-D tensor with the shape of +// [num_layer * dir, batch, num_units]. For other models, it is ignored. +// params: A 1-D tensor that contains the weights and biases in an opaque layout. +// The size must be created through CudnnRNNParamsSize, and initialized +// separately. Note that they might not be compatible across different +// generations. So it is a good idea to save and restore +// sequence_lengths: a vector of lengths of each input sequence. +// output: A 3-D tensor with the shape of [seq_length, batch_size, +// dir * num_units]. +// output_h: The same shape has input_h. +// output_c: The same shape as input_c for LSTM. An empty tensor for other models. +// is_training: Indicates whether this operation is used for inferenece or +// training. +// reserve_space: An opaque tensor that can be used in backprop calculation. It +// is only produced if is_training is true. +func CudnnRNNV3(scope *Scope, input tf.Output, input_h tf.Output, input_c tf.Output, params tf.Output, sequence_lengths tf.Output, optional ...CudnnRNNV3Attr) (output tf.Output, output_h tf.Output, output_c tf.Output, reserve_space tf.Output, host_reserved tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{} + for _, a := range optional { + a(attrs) + } + opspec := tf.OpSpec{ + Type: "CudnnRNNV3", + Input: []tf.Input{ + input, input_h, input_c, params, sequence_lengths, + }, + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0), op.Output(1), op.Output(2), op.Output(3), op.Output(4) +} + // Applies softmax to a batched N-D `SparseTensor`. // // The inputs represent an N-D SparseTensor with logical shape `[..., B, C]` @@ -16873,6 +17596,23 @@ func SparseReduceSum(scope *Scope, input_indices tf.Output, input_values tf.Outp return op.Output(0) } +// Records the latency of producing `input_dataset` elements in a StatsAggregator. +func ExperimentalLatencyStatsDataset(scope *Scope, input_dataset tf.Output, tag tf.Output, output_types []tf.DataType, output_shapes []tf.Shape) (handle tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{"output_types": output_types, "output_shapes": output_shapes} + opspec := tf.OpSpec{ + Type: "ExperimentalLatencyStatsDataset", + Input: []tf.Input{ + input_dataset, tag, + }, + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // SparseTensorDenseMatMulAttr is an optional argument to SparseTensorDenseMatMul. type SparseTensorDenseMatMulAttr func(optionalAttr) @@ -17555,6 +18295,69 @@ func Timestamp(scope *Scope) (ts tf.Output) { return op.Output(0) } +// ResourceSparseApplyKerasMomentumAttr is an optional argument to ResourceSparseApplyKerasMomentum. +type ResourceSparseApplyKerasMomentumAttr func(optionalAttr) + +// ResourceSparseApplyKerasMomentumUseLocking sets the optional use_locking attribute to value. +// +// value: If `True`, updating of the var and accum tensors will be protected +// by a lock; otherwise the behavior is undefined, but may exhibit less +// contention. +// If not specified, defaults to false +func ResourceSparseApplyKerasMomentumUseLocking(value bool) ResourceSparseApplyKerasMomentumAttr { + return func(m optionalAttr) { + m["use_locking"] = value + } +} + +// ResourceSparseApplyKerasMomentumUseNesterov sets the optional use_nesterov attribute to value. +// +// value: If `True`, the tensor passed to compute grad will be +// var + momentum * accum, so in the end, the var you get is actually +// var + momentum * accum. +// If not specified, defaults to false +func ResourceSparseApplyKerasMomentumUseNesterov(value bool) ResourceSparseApplyKerasMomentumAttr { + return func(m optionalAttr) { + m["use_nesterov"] = value + } +} + +// Update relevant entries in '*var' and '*accum' according to the momentum scheme. +// +// Set use_nesterov = True if you want to use Nesterov momentum. +// +// That is for rows we have grad for, we update var and accum as follows: +// +// accum = accum * momentum - lr * grad +// var += accum +// +// Arguments: +// var_: Should be from a Variable(). +// accum: Should be from a Variable(). +// lr: Learning rate. Must be a scalar. +// grad: The gradient. +// indices: A vector of indices into the first dimension of var and accum. +// momentum: Momentum. Must be a scalar. +// +// Returns the created operation. +func ResourceSparseApplyKerasMomentum(scope *Scope, var_ tf.Output, accum tf.Output, lr tf.Output, grad tf.Output, indices tf.Output, momentum tf.Output, optional ...ResourceSparseApplyKerasMomentumAttr) (o *tf.Operation) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{} + for _, a := range optional { + a(attrs) + } + opspec := tf.OpSpec{ + Type: "ResourceSparseApplyKerasMomentum", + Input: []tf.Input{ + var_, accum, lr, grad, indices, momentum, + }, + Attrs: attrs, + } + return scope.AddOperation(opspec) +} + // VariableShapeAttr is an optional argument to VariableShape. type VariableShapeAttr func(optionalAttr) @@ -18293,6 +19096,96 @@ func DivNoNan(scope *Scope, x tf.Output, y tf.Output) (z tf.Output) { return op.Output(0) } +// UnicodeDecodeWithOffsetsAttr is an optional argument to UnicodeDecodeWithOffsets. +type UnicodeDecodeWithOffsetsAttr func(optionalAttr) + +// UnicodeDecodeWithOffsetsErrors sets the optional errors attribute to value. +// +// value: Error handling policy when there is invalid formatting found in the input. +// The value of 'strict' will cause the operation to produce a InvalidArgument +// error on any invalid input formatting. A value of 'replace' (the default) will +// cause the operation to replace any invalid formatting in the input with the +// `replacement_char` codepoint. A value of 'ignore' will cause the operation to +// skip any invalid formatting in the input and produce no corresponding output +// character. +// If not specified, defaults to "replace" +func UnicodeDecodeWithOffsetsErrors(value string) UnicodeDecodeWithOffsetsAttr { + return func(m optionalAttr) { + m["errors"] = value + } +} + +// UnicodeDecodeWithOffsetsReplacementChar sets the optional replacement_char attribute to value. +// +// value: The replacement character codepoint to be used in place of any invalid +// formatting in the input when `errors='replace'`. Any valid unicode codepoint may +// be used. The default value is the default unicode replacement character is +// 0xFFFD or U+65533.) +// If not specified, defaults to 65533 +func UnicodeDecodeWithOffsetsReplacementChar(value int64) UnicodeDecodeWithOffsetsAttr { + return func(m optionalAttr) { + m["replacement_char"] = value + } +} + +// UnicodeDecodeWithOffsetsReplaceControlCharacters sets the optional replace_control_characters attribute to value. +// +// value: Whether to replace the C0 control characters (00-1F) with the +// `replacement_char`. Default is false. +// If not specified, defaults to false +func UnicodeDecodeWithOffsetsReplaceControlCharacters(value bool) UnicodeDecodeWithOffsetsAttr { + return func(m optionalAttr) { + m["replace_control_characters"] = value + } +} + +// Decodes each string in `input` into a sequence of Unicode code points. +// +// The character codepoints for all strings are returned using a single vector +// `char_values`, with strings expanded to characters in row-major order. +// Similarly, the character start byte offsets are returned using a single vector +// `char_to_byte_starts`, with strings expanded in row-major order. +// +// The `row_splits` tensor indicates where the codepoints and start offsets for +// each input string begin and end within the `char_values` and +// `char_to_byte_starts` tensors. In particular, the values for the `i`th +// string (in row-major order) are stored in the slice +// `[row_splits[i]:row_splits[i+1]]`. Thus: +// +// * `char_values[row_splits[i]+j]` is the Unicode codepoint for the `j`th +// character in the `i`th string (in row-major order). +// * `char_to_bytes_starts[row_splits[i]+j]` is the start byte offset for the `j`th +// character in the `i`th string (in row-major order). +// * `row_splits[i+1] - row_splits[i]` is the number of characters in the `i`th +// string (in row-major order). +// +// Arguments: +// input: The text to be decoded. Can have any shape. Note that the output is flattened +// to a vector of char values. +// input_encoding: Text encoding of the input strings. This is any of the encodings supported +// by ICU ucnv algorithmic converters. Examples: `"UTF-16", "US ASCII", "UTF-8"`. +// +// Returns A 1D int32 tensor containing the row splits.A 1D int32 Tensor containing the decoded codepoints.A 1D int32 Tensor containing the byte index in the input string where each +// character in `char_values` starts. +func UnicodeDecodeWithOffsets(scope *Scope, input tf.Output, input_encoding string, optional ...UnicodeDecodeWithOffsetsAttr) (row_splits tf.Output, char_values tf.Output, char_to_byte_starts tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{"input_encoding": input_encoding} + for _, a := range optional { + a(attrs) + } + opspec := tf.OpSpec{ + Type: "UnicodeDecodeWithOffsets", + Input: []tf.Input{ + input, + }, + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0), op.Output(1), op.Output(2) +} + // Returns x - y element-wise. // // *NOTE*: `Subtract` supports broadcasting. More about broadcasting @@ -20606,15 +21499,62 @@ func Zeta(scope *Scope, x tf.Output, q tf.Output) (z tf.Output) { return op.Output(0) } +// Returns the cardinality of `input_dataset`. +// +// Returns the cardinality of `input_dataset`. +// +// Arguments: +// input_dataset: A variant tensor representing the dataset to return cardinality for. +// +// Returns The cardinality of `input_dataset`. Named constants are used to represent +// infinite and unknown cardinality. +func ExperimentalDatasetCardinality(scope *Scope, input_dataset tf.Output) (cardinality tf.Output) { + if scope.Err() != nil { + return + } + opspec := tf.OpSpec{ + Type: "ExperimentalDatasetCardinality", + Input: []tf.Input{ + input_dataset, + }, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + +// Creates a dataset that executes a SQL query and emits rows of the result set. +// +// Arguments: +// driver_name: The database type. Currently, the only supported type is 'sqlite'. +// data_source_name: A connection string to connect to the database. +// query: A SQL query to execute. +// +// +func ExperimentalSqlDataset(scope *Scope, driver_name tf.Output, data_source_name tf.Output, query tf.Output, output_types []tf.DataType, output_shapes []tf.Shape) (handle tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{"output_types": output_types, "output_shapes": output_shapes} + opspec := tf.OpSpec{ + Type: "ExperimentalSqlDataset", + Input: []tf.Input{ + driver_name, data_source_name, query, + }, + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // Inverse fast Fourier transform. // // Computes the inverse 1-dimensional discrete Fourier transform over the // inner-most dimension of `input`. // // Arguments: -// input: A complex64 tensor. +// input: A complex tensor. // -// Returns A complex64 tensor of the same shape as `input`. The inner-most +// Returns A complex tensor of the same shape as `input`. The inner-most // dimension of `input` is replaced with its inverse 1D Fourier transform. // // @compatibility(numpy) @@ -20640,9 +21580,9 @@ func IFFT(scope *Scope, input tf.Output) (output tf.Output) { // 2 dimensions of `input`. // // Arguments: -// input: A complex64 tensor. +// input: A complex tensor. // -// Returns A complex64 tensor of the same shape as `input`. The inner-most 2 +// Returns A complex tensor of the same shape as `input`. The inner-most 2 // dimensions of `input` are replaced with their 2D Fourier transform. // // @compatibility(numpy) @@ -20668,9 +21608,9 @@ func FFT2D(scope *Scope, input tf.Output) (output tf.Output) { // inner-most 2 dimensions of `input`. // // Arguments: -// input: A complex64 tensor. +// input: A complex tensor. // -// Returns A complex64 tensor of the same shape as `input`. The inner-most 2 +// Returns A complex tensor of the same shape as `input`. The inner-most 2 // dimensions of `input` are replaced with their inverse 2D Fourier transform. // // @compatibility(numpy) @@ -21010,6 +21950,44 @@ func ReaderNumRecordsProducedV2(scope *Scope, reader_handle tf.Output) (records_ return op.Output(0) } +// TensorListConcatAttr is an optional argument to TensorListConcat. +type TensorListConcatAttr func(optionalAttr) + +// TensorListConcatElementShape sets the optional element_shape attribute to value. +// If not specified, defaults to +func TensorListConcatElementShape(value tf.Shape) TensorListConcatAttr { + return func(m optionalAttr) { + m["element_shape"] = value + } +} + +// Concats all tensors in the list along the 0th dimension. +// +// Requires that all tensors have the same shape except the first dimension. +// +// input_handle: The input list. +// tensor: The concated result. +// lengths: Output tensor containing sizes of the 0th dimension of tensors in the list, used for computing the gradient. +// +func TensorListConcat(scope *Scope, input_handle tf.Output, element_dtype tf.DataType, optional ...TensorListConcatAttr) (tensor tf.Output, lengths tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{"element_dtype": element_dtype} + for _, a := range optional { + a(attrs) + } + opspec := tf.OpSpec{ + Type: "TensorListConcat", + Input: []tf.Input{ + input_handle, + }, + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0), op.Output(1) +} + // Returns the set of files matching one or more glob patterns. // // Note that this routine only supports wildcard characters in the @@ -22051,6 +23029,61 @@ func SparseSparseMinimum(scope *Scope, a_indices tf.Output, a_values tf.Output, return op.Output(0), op.Output(1) } +// ResourceApplyAdamWithAmsgradAttr is an optional argument to ResourceApplyAdamWithAmsgrad. +type ResourceApplyAdamWithAmsgradAttr func(optionalAttr) + +// ResourceApplyAdamWithAmsgradUseLocking sets the optional use_locking attribute to value. +// +// value: If `True`, updating of the var, m, and v tensors will be protected +// by a lock; otherwise the behavior is undefined, but may exhibit less +// contention. +// If not specified, defaults to false +func ResourceApplyAdamWithAmsgradUseLocking(value bool) ResourceApplyAdamWithAmsgradAttr { + return func(m optionalAttr) { + m["use_locking"] = value + } +} + +// Update '*var' according to the Adam algorithm. +// +// $$lr_t := \text{learning\_rate} * \sqrt{1 - beta_2^t} / (1 - beta_1^t)$$ +// $$m_t := beta_1 * m_{t-1} + (1 - beta_1) * g$$ +// $$v_t := beta_2 * v_{t-1} + (1 - beta_2) * g * g$$ +// $$vhat_t := max{vhat_{t-1}, v_t}$$ +// $$variable := variable - lr_t * m_t / (\sqrt{vhat_t} + \epsilon)$$ +// +// Arguments: +// var_: Should be from a Variable(). +// m: Should be from a Variable(). +// v: Should be from a Variable(). +// vhat: Should be from a Variable(). +// beta1_power: Must be a scalar. +// beta2_power: Must be a scalar. +// lr: Scaling factor. Must be a scalar. +// beta1: Momentum factor. Must be a scalar. +// beta2: Momentum factor. Must be a scalar. +// epsilon: Ridge term. Must be a scalar. +// grad: The gradient. +// +// Returns the created operation. +func ResourceApplyAdamWithAmsgrad(scope *Scope, var_ tf.Output, m tf.Output, v tf.Output, vhat tf.Output, beta1_power tf.Output, beta2_power tf.Output, lr tf.Output, beta1 tf.Output, beta2 tf.Output, epsilon tf.Output, grad tf.Output, optional ...ResourceApplyAdamWithAmsgradAttr) (o *tf.Operation) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{} + for _, a := range optional { + a(attrs) + } + opspec := tf.OpSpec{ + Type: "ResourceApplyAdamWithAmsgrad", + Input: []tf.Input{ + var_, m, v, vhat, beta1_power, beta2_power, lr, beta1, beta2, epsilon, grad, + }, + Attrs: attrs, + } + return scope.AddOperation(opspec) +} + // MapUnstageNoKeyAttr is an optional argument to MapUnstageNoKey. type MapUnstageNoKeyAttr func(optionalAttr) @@ -22452,6 +23485,93 @@ func SetSize(scope *Scope, set_indices tf.Output, set_values tf.Output, set_shap return op.Output(0) } +// Adds sparse `updates` to an existing tensor according to `indices`. +// +// This operation creates a new tensor by adding sparse `updates` to the passed +// in `tensor`. +// This operation is very similar to `tf.scatter_nd_add`, except that the updates +// are added onto an existing tensor (as opposed to a variable). If the memory +// for the existing tensor cannot be re-used, a copy is made and updated. +// +// `indices` is an integer tensor containing indices into a new tensor of shape +// `shape`. The last dimension of `indices` can be at most the rank of `shape`: +// +// indices.shape[-1] <= shape.rank +// +// The last dimension of `indices` corresponds to indices into elements +// (if `indices.shape[-1] = shape.rank`) or slices +// (if `indices.shape[-1] < shape.rank`) along dimension `indices.shape[-1]` of +// `shape`. `updates` is a tensor with shape +// +// indices.shape[:-1] + shape[indices.shape[-1]:] +// +// The simplest form of tensor_scatter_add is to add individual elements to a +// tensor by index. For example, say we want to add 4 elements in a rank-1 +// tensor with 8 elements. +// +// In Python, this scatter add operation would look like this: +// +// ```python +// indices = tf.constant([[4], [3], [1], [7]]) +// updates = tf.constant([9, 10, 11, 12]) +// tensor = tf.ones([8], dtype=tf.int32) +// updated = tf.tensor_scatter_add(tensor, indices, updates) +// with tf.Session() as sess: +// print(sess.run(scatter)) +// ``` +// +// The resulting tensor would look like this: +// +// [1, 12, 1, 11, 10, 1, 1, 13] +// +// We can also, insert entire slices of a higher rank tensor all at once. For +// example, if we wanted to insert two slices in the first dimension of a +// rank-3 tensor with two matrices of new values. +// +// In Python, this scatter add operation would look like this: +// +// ```python +// indices = tf.constant([[0], [2]]) +// updates = tf.constant([[[5, 5, 5, 5], [6, 6, 6, 6], +// [7, 7, 7, 7], [8, 8, 8, 8]], +// [[5, 5, 5, 5], [6, 6, 6, 6], +// [7, 7, 7, 7], [8, 8, 8, 8]]]) +// tensor = tf.ones([4, 4, 4]) +// updated = tf.tensor_scatter_add(tensor, indices, updates) +// with tf.Session() as sess: +// print(sess.run(scatter)) +// ``` +// +// The resulting tensor would look like this: +// +// [[[6, 6, 6, 6], [7, 7, 7, 7], [8, 8, 8, 8], [9, 9, 9, 9]], +// [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]], +// [[6, 6, 6, 6], [7, 7, 7, 7], [8, 8, 8, 8], [9, 9, 9, 9]], +// [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]] +// +// Note that on CPU, if an out of bound index is found, an error is returned. +// On GPU, if an out of bound index is found, the index is ignored. +// +// Arguments: +// tensor: Tensor to copy/update. +// indices: Index tensor. +// updates: Updates to scatter into output. +// +// Returns A new tensor copied from tensor and updates added according to the indices. +func TensorScatterAdd(scope *Scope, tensor tf.Output, indices tf.Output, updates tf.Output) (output tf.Output) { + if scope.Err() != nil { + return + } + opspec := tf.OpSpec{ + Type: "TensorScatterAdd", + Input: []tf.Input{ + tensor, indices, updates, + }, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // Computes the sign and the log of the absolute value of the determinant of // // one or more square matrices. @@ -22676,6 +23796,84 @@ func QuantizedResizeBilinear(scope *Scope, images tf.Output, size tf.Output, min return op.Output(0), op.Output(1), op.Output(2) } +// Creates a dataset that uses a custom thread pool to compute `input_dataset`. +// +// Arguments: +// +// num_threads: Identifies the number of threads to use for the private threadpool. +// +// +func ExperimentalPrivateThreadPoolDataset(scope *Scope, input_dataset tf.Output, num_threads tf.Output, output_types []tf.DataType, output_shapes []tf.Shape) (handle tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{"output_types": output_types, "output_shapes": output_shapes} + opspec := tf.OpSpec{ + Type: "ExperimentalPrivateThreadPoolDataset", + Input: []tf.Input{ + input_dataset, num_threads, + }, + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + +// ExperimentalParseExampleDatasetAttr is an optional argument to ExperimentalParseExampleDataset. +type ExperimentalParseExampleDatasetAttr func(optionalAttr) + +// ExperimentalParseExampleDatasetSloppy sets the optional sloppy attribute to value. +// If not specified, defaults to false +func ExperimentalParseExampleDatasetSloppy(value bool) ExperimentalParseExampleDatasetAttr { + return func(m optionalAttr) { + m["sloppy"] = value + } +} + +// Transforms `input_dataset` containing `Example` protos as vectors of DT_STRING into a dataset of `Tensor` or `SparseTensor` objects representing the parsed features. +// +// Arguments: +// +// +// dense_defaults: A dict mapping string keys to `Tensor`s. +// The keys of the dict must match the dense_keys of the feature. +// sparse_keys: A list of string keys in the examples features. +// The results for these keys will be returned as `SparseTensor` objects. +// dense_keys: A list of Ndense string Tensors (scalars). +// The keys expected in the Examples features associated with dense values. +// sparse_types: A list of `DTypes` of the same length as `sparse_keys`. +// Only `tf.float32` (`FloatList`), `tf.int64` (`Int64List`), +// and `tf.string` (`BytesList`) are supported. +// dense_shapes: List of tuples with the same length as `dense_keys`. +// The shape of the data for each dense feature referenced by `dense_keys`. +// Required for any input tensors identified by `dense_keys`. Must be +// either fully defined, or may contain an unknown first dimension. +// An unknown first dimension means the feature is treated as having +// a variable number of blocks, and the output shape along this dimension +// is considered unknown at graph build time. Padding is applied for +// minibatch elements smaller than the maximum number of blocks for the +// given feature along this dimension. +// output_types: The type list for the return values. +// output_shapes: The list of shapes being produced. +func ExperimentalParseExampleDataset(scope *Scope, input_dataset tf.Output, num_parallel_calls tf.Output, dense_defaults []tf.Output, sparse_keys []string, dense_keys []string, sparse_types []tf.DataType, dense_shapes []tf.Shape, output_types []tf.DataType, output_shapes []tf.Shape, optional ...ExperimentalParseExampleDatasetAttr) (handle tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{"sparse_keys": sparse_keys, "dense_keys": dense_keys, "sparse_types": sparse_types, "dense_shapes": dense_shapes, "output_types": output_types, "output_shapes": output_shapes} + for _, a := range optional { + a(attrs) + } + opspec := tf.OpSpec{ + Type: "ExperimentalParseExampleDataset", + Input: []tf.Input{ + input_dataset, num_parallel_calls, tf.OutputList(dense_defaults), + }, + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // SdcaOptimizerAttr is an optional argument to SdcaOptimizer. type SdcaOptimizerAttr func(optionalAttr) @@ -23420,6 +24618,26 @@ func MatMul(scope *Scope, a tf.Output, b tf.Output, optional ...MatMulAttr) (pro return op.Output(0) } +// Serializes the tree handle to a proto +// +// Arguments: +// tree_handle: Handle to the tree resource to be serialized. +// +// Returns Serialied proto string of the tree resource. +func TensorForestTreeSerialize(scope *Scope, tree_handle tf.Output) (tree_config tf.Output) { + if scope.Err() != nil { + return + } + opspec := tf.OpSpec{ + Type: "TensorForestTreeSerialize", + Input: []tf.Input{ + tree_handle, + }, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // SparseMatMulAttr is an optional argument to SparseMatMul. type SparseMatMulAttr func(optionalAttr) @@ -26575,6 +27793,28 @@ func Invert(scope *Scope, x tf.Output) (y tf.Output) { return op.Output(0) } +// Deserialize bucket boundaries and ready flag into current QuantileAccumulator. +// +// An op that deserializes bucket boundaries and are boundaries ready flag into current QuantileAccumulator. +// +// Arguments: +// quantile_stream_resource_handle: resource handle referring to a QuantileStreamResource. +// bucket_boundaries: float; List of Rank 1 Tensors each containing the bucket boundaries for a feature. +// +// Returns the created operation. +func BoostedTreesQuantileStreamResourceDeserialize(scope *Scope, quantile_stream_resource_handle tf.Output, bucket_boundaries []tf.Output) (o *tf.Operation) { + if scope.Err() != nil { + return + } + opspec := tf.OpSpec{ + Type: "BoostedTreesQuantileStreamResourceDeserialize", + Input: []tf.Input{ + quantile_stream_resource_handle, tf.OutputList(bucket_boundaries), + }, + } + return scope.AddOperation(opspec) +} + // Inverse 3D fast Fourier transform. // // Computes the inverse 3-dimensional discrete Fourier transform over the @@ -27102,6 +28342,29 @@ func AudioSummaryV2(scope *Scope, tag tf.Output, tensor tf.Output, sample_rate t return op.Output(0) } +// Splits a tensor into a list. +// +// list[i] corresponds to lengths[i] tensors from the input tensor. +// The tensor must have rank at least 1 and contain exactly sum(lengths) elements. +// +// tensor: The input tensor. +// element_shape: A shape compatible with that of elements in the tensor. +// lengths: Vector of sizes of the 0th dimension of tensors in the list. +// output_handle: The list. +func TensorListSplit(scope *Scope, tensor tf.Output, element_shape tf.Output, lengths tf.Output) (output_handle tf.Output) { + if scope.Err() != nil { + return + } + opspec := tf.OpSpec{ + Type: "TensorListSplit", + Input: []tf.Input{ + tensor, element_shape, lengths, + }, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // AvgPoolAttr is an optional argument to AvgPool. type AvgPoolAttr func(optionalAttr) @@ -27222,6 +28485,26 @@ func TensorListGetItem(scope *Scope, input_handle tf.Output, index tf.Output, el return op.Output(0) } +// Resizes the list. +// +// +// input_handle: the input list +// size: size of the output list +// +func TensorListResize(scope *Scope, input_handle tf.Output, size tf.Output) (output_handle tf.Output) { + if scope.Err() != nil { + return + } + opspec := tf.OpSpec{ + Type: "TensorListResize", + Input: []tf.Input{ + input_handle, size, + }, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // Returns a diagonal tensor with a given diagonal values. // // Given a `diagonal`, this operation returns a tensor with the `diagonal` and @@ -27412,7 +28695,7 @@ func TensorListScatter(scope *Scope, tensor tf.Output, indices tf.Output, elemen // limits: The limits of each range. // deltas: The deltas of each range. // -// Returns The `row_splits` for the returned `RaggedTensor`.The `inner_values` for the returned `RaggedTensor`. +// Returns The `row_splits` for the returned `RaggedTensor`.The `flat_values` for the returned `RaggedTensor`. func RaggedRange(scope *Scope, starts tf.Output, limits tf.Output, deltas tf.Output) (rt_nested_splits tf.Output, rt_dense_values tf.Output) { if scope.Err() != nil { return @@ -27744,6 +29027,66 @@ func AdjustSaturation(scope *Scope, images tf.Output, scale tf.Output) (output t return op.Output(0) } +// ResourceApplyKerasMomentumAttr is an optional argument to ResourceApplyKerasMomentum. +type ResourceApplyKerasMomentumAttr func(optionalAttr) + +// ResourceApplyKerasMomentumUseLocking sets the optional use_locking attribute to value. +// +// value: If `True`, updating of the var and accum tensors will be protected +// by a lock; otherwise the behavior is undefined, but may exhibit less +// contention. +// If not specified, defaults to false +func ResourceApplyKerasMomentumUseLocking(value bool) ResourceApplyKerasMomentumAttr { + return func(m optionalAttr) { + m["use_locking"] = value + } +} + +// ResourceApplyKerasMomentumUseNesterov sets the optional use_nesterov attribute to value. +// +// value: If `True`, the tensor passed to compute grad will be +// var + momentum * accum, so in the end, the var you get is actually +// var + momentum * accum. +// If not specified, defaults to false +func ResourceApplyKerasMomentumUseNesterov(value bool) ResourceApplyKerasMomentumAttr { + return func(m optionalAttr) { + m["use_nesterov"] = value + } +} + +// Update '*var' according to the momentum scheme. Set use_nesterov = True if you +// +// want to use Nesterov momentum. +// +// accum = accum * momentum - lr * grad +// var += accum +// +// Arguments: +// var_: Should be from a Variable(). +// accum: Should be from a Variable(). +// lr: Scaling factor. Must be a scalar. +// grad: The gradient. +// momentum: Momentum. Must be a scalar. +// +// Returns the created operation. +func ResourceApplyKerasMomentum(scope *Scope, var_ tf.Output, accum tf.Output, lr tf.Output, grad tf.Output, momentum tf.Output, optional ...ResourceApplyKerasMomentumAttr) (o *tf.Operation) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{} + for _, a := range optional { + a(attrs) + } + opspec := tf.OpSpec{ + Type: "ResourceApplyKerasMomentum", + Input: []tf.Input{ + var_, accum, lr, grad, momentum, + }, + Attrs: attrs, + } + return scope.AddOperation(opspec) +} + // MatrixSolveAttr is an optional argument to MatrixSolve. type MatrixSolveAttr func(optionalAttr) @@ -27813,6 +29156,70 @@ func DatasetToGraph(scope *Scope, input_dataset tf.Output) (graph tf.Output) { return op.Output(0) } +// LuAttr is an optional argument to Lu. +type LuAttr func(optionalAttr) + +// LuOutputIdxType sets the optional output_idx_type attribute to value. +// If not specified, defaults to DT_INT32 +func LuOutputIdxType(value tf.DataType) LuAttr { + return func(m optionalAttr) { + m["output_idx_type"] = value + } +} + +// Computes the LU decomposition of one or more square matrices. +// +// The input is a tensor of shape `[..., M, M]` whose inner-most 2 dimensions +// form square matrices. +// +// The input has to be invertible. +// +// The output consists of two tensors LU and P containing the LU decomposition +// of all input submatrices `[..., :, :]`. LU encodes the lower triangular and +// upper triangular factors. +// +// For each input submatrix of shape `[M, M]`, L is a lower triangular matrix of +// shape `[M, M]` with unit diagonal whose entries correspond to the strictly lower +// triangular part of LU. U is a upper triangular matrix of shape `[M, M]` whose +// entries correspond to the upper triangular part, including the diagonal, of LU. +// +// P represents a permutation matrix encoded as a list of indices each between `0` +// and `M-1`, inclusive. If P_mat denotes the permutation matrix corresponding to +// P, then the L, U and P satisfies P_mat * input = L * U. +// +// Arguments: +// input: A tensor of shape `[..., M, M]` whose inner-most 2 dimensions form matrices of +// size `[M, M]`. +// +// Returns A tensor of shape `[..., M, M]` whose strictly lower triangular part denotes the +// lower triangular factor `L` with unit diagonal, and whose upper triangular part +// denotes the upper triangular factor `U`.Permutation of the rows encoded as a list of indices in `0..M-1`. Shape is +// `[..., M]`. +// @compatibility(scipy) +// Similar to `scipy.linalg.lu`, except the triangular factors `L` and `U` are +// packed into a single tensor, the permutation is applied to `input` instead of +// the right hand side and the permutation `P` is returned as a list of indices +// instead of a permutation matrix. +// @end_compatibility +func Lu(scope *Scope, input tf.Output, optional ...LuAttr) (lu tf.Output, p tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{} + for _, a := range optional { + a(attrs) + } + opspec := tf.OpSpec{ + Type: "Lu", + Input: []tf.Input{ + input, + }, + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0), op.Output(1) +} + // Computes the matrix square root of one or more square matrices: // // matmul(sqrtm(A), sqrtm(A)) = A @@ -29948,6 +31355,43 @@ func Iterator(scope *Scope, shared_name string, container string, output_types [ return op.Output(0) } +// TensorForestTreeResourceHandleOpAttr is an optional argument to TensorForestTreeResourceHandleOp. +type TensorForestTreeResourceHandleOpAttr func(optionalAttr) + +// TensorForestTreeResourceHandleOpContainer sets the optional container attribute to value. +// If not specified, defaults to "" +func TensorForestTreeResourceHandleOpContainer(value string) TensorForestTreeResourceHandleOpAttr { + return func(m optionalAttr) { + m["container"] = value + } +} + +// TensorForestTreeResourceHandleOpSharedName sets the optional shared_name attribute to value. +// If not specified, defaults to "" +func TensorForestTreeResourceHandleOpSharedName(value string) TensorForestTreeResourceHandleOpAttr { + return func(m optionalAttr) { + m["shared_name"] = value + } +} + +// Creates a handle to a TensorForestTreeResource +func TensorForestTreeResourceHandleOp(scope *Scope, optional ...TensorForestTreeResourceHandleOpAttr) (resource tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{} + for _, a := range optional { + a(attrs) + } + opspec := tf.OpSpec{ + Type: "TensorForestTreeResourceHandleOp", + + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // CropAndResizeGradImageAttr is an optional argument to CropAndResizeGradImage. type CropAndResizeGradImageAttr func(optionalAttr) @@ -30312,6 +31756,29 @@ func FakeParam(scope *Scope, dtype tf.DataType, shape tf.Shape) (output tf.Outpu return op.Output(0) } +// Returns the next representable value of `x1` in the direction of `x2`, element-wise. +// +// This operation returns the same result as the C++ std::nextafter function. +// +// It can also return a subnormal number. +// +// @compatibility(cpp) +// Equivalent to C++ std::nextafter function. +// @end_compatibility +func NextAfter(scope *Scope, x1 tf.Output, x2 tf.Output) (output tf.Output) { + if scope.Err() != nil { + return + } + opspec := tf.OpSpec{ + Type: "NextAfter", + Input: []tf.Input{ + x1, x2, + }, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // Computes the gradient for the inverse of `x` wrt its input. // // Specifically, `grad = -dy * y*y`, where `y = 1/x`, and `dy` @@ -30461,6 +31928,71 @@ func BoostedTreesQuantileStreamResourceAddSummaries(scope *Scope, quantile_strea return scope.AddOperation(opspec) } +// Creates a Dataset that returns pseudorandom numbers. +// +// Arguments: +// seed: A scalar seed for the random number generator. If either seed or +// seed2 is set to be non-zero, the random number generator is seeded +// by the given seed. Otherwise, a random seed is used. +// seed2: A second scalar seed to avoid seed collision. +// +// +func ExperimentalRandomDataset(scope *Scope, seed tf.Output, seed2 tf.Output, output_types []tf.DataType, output_shapes []tf.Shape) (handle tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{"output_types": output_types, "output_shapes": output_shapes} + opspec := tf.OpSpec{ + Type: "ExperimentalRandomDataset", + Input: []tf.Input{ + seed, seed2, + }, + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + +// A dataset that splits the elements of its input into multiple elements. +func ExperimentalUnbatchDataset(scope *Scope, input_dataset tf.Output, output_types []tf.DataType, output_shapes []tf.Shape) (handle tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{"output_types": output_types, "output_shapes": output_shapes} + opspec := tf.OpSpec{ + Type: "ExperimentalUnbatchDataset", + Input: []tf.Input{ + input_dataset, + }, + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + +// Creates a dataset that overrides the maximum intra-op parallelism. +// +// Arguments: +// +// max_intra_op_parallelism: Identifies the maximum intra-op parallelism to use. +// +// +func ExperimentalMaxIntraOpParallelismDataset(scope *Scope, input_dataset tf.Output, max_intra_op_parallelism tf.Output, output_types []tf.DataType, output_shapes []tf.Shape) (handle tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{"output_types": output_types, "output_shapes": output_shapes} + opspec := tf.OpSpec{ + Type: "ExperimentalMaxIntraOpParallelismDataset", + Input: []tf.Input{ + input_dataset, max_intra_op_parallelism, + }, + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // StringSplitV2Attr is an optional argument to StringSplitV2. type StringSplitV2Attr func(optionalAttr) @@ -30823,6 +32355,83 @@ func DeserializeIterator(scope *Scope, resource_handle tf.Output, serialized tf. return scope.AddOperation(opspec) } +// ResourceScatterNdSubAttr is an optional argument to ResourceScatterNdSub. +type ResourceScatterNdSubAttr func(optionalAttr) + +// ResourceScatterNdSubUseLocking sets the optional use_locking attribute to value. +// +// value: An optional bool. Defaults to True. If True, the assignment will +// be protected by a lock; otherwise the behavior is undefined, +// but may exhibit less contention. +// If not specified, defaults to true +func ResourceScatterNdSubUseLocking(value bool) ResourceScatterNdSubAttr { + return func(m optionalAttr) { + m["use_locking"] = value + } +} + +// Applies sparse subtraction to individual values or slices in a Variable. +// +// `ref` is a `Tensor` with rank `P` and `indices` is a `Tensor` of rank `Q`. +// +// `indices` must be integer tensor, containing indices into `ref`. +// It must be shape `[d_0, ..., d_{Q-2}, K]` where `0 < K <= P`. +// +// The innermost dimension of `indices` (with length `K`) corresponds to +// indices into elements (if `K = P`) or slices (if `K < P`) along the `K`th +// dimension of `ref`. +// +// `updates` is `Tensor` of rank `Q-1+P-K` with shape: +// +// ``` +// [d_0, ..., d_{Q-2}, ref.shape[K], ..., ref.shape[P-1]] +// ``` +// +// For example, say we want to subtract 4 scattered elements from a rank-1 tensor +// with 8 elements. In Python, that subtraction would look like this: +// +// ```python +// ref = tf.Variable([1, 2, 3, 4, 5, 6, 7, 8], use_resource=True) +// indices = tf.constant([[4], [3], [1], [7]]) +// updates = tf.constant([9, 10, 11, 12]) +// sub = tf.scatter_nd_sub(ref, indices, updates) +// with tf.Session() as sess: +// print sess.run(sub) +// ``` +// +// The resulting update to ref would look like this: +// +// [1, -9, 3, -6, -4, 6, 7, -4] +// +// See `tf.scatter_nd` for more details about how to make updates to +// slices. +// +// Arguments: +// ref: A resource handle. Must be from a VarHandleOp. +// indices: A Tensor. Must be one of the following types: int32, int64. +// A tensor of indices into ref. +// updates: A Tensor. Must have the same type as ref. A tensor of +// values to add to ref. +// +// Returns the created operation. +func ResourceScatterNdSub(scope *Scope, ref tf.Output, indices tf.Output, updates tf.Output, optional ...ResourceScatterNdSubAttr) (o *tf.Operation) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{} + for _, a := range optional { + a(attrs) + } + opspec := tf.OpSpec{ + Type: "ResourceScatterNdSub", + Input: []tf.Input{ + ref, indices, updates, + }, + Attrs: attrs, + } + return scope.AddOperation(opspec) +} + // TensorArrayConcatV2Attr is an optional argument to TensorArrayConcatV2. type TensorArrayConcatV2Attr func(optionalAttr) @@ -31032,6 +32641,43 @@ func TFRecordDataset(scope *Scope, filenames tf.Output, compression_type tf.Outp return op.Output(0) } +// ExperimentalStatsAggregatorHandleAttr is an optional argument to ExperimentalStatsAggregatorHandle. +type ExperimentalStatsAggregatorHandleAttr func(optionalAttr) + +// ExperimentalStatsAggregatorHandleContainer sets the optional container attribute to value. +// If not specified, defaults to "" +func ExperimentalStatsAggregatorHandleContainer(value string) ExperimentalStatsAggregatorHandleAttr { + return func(m optionalAttr) { + m["container"] = value + } +} + +// ExperimentalStatsAggregatorHandleSharedName sets the optional shared_name attribute to value. +// If not specified, defaults to "" +func ExperimentalStatsAggregatorHandleSharedName(value string) ExperimentalStatsAggregatorHandleAttr { + return func(m optionalAttr) { + m["shared_name"] = value + } +} + +// Creates a statistics manager resource. +func ExperimentalStatsAggregatorHandle(scope *Scope, optional ...ExperimentalStatsAggregatorHandleAttr) (handle tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{} + for _, a := range optional { + a(attrs) + } + opspec := tf.OpSpec{ + Type: "ExperimentalStatsAggregatorHandle", + + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // A container for an iterator resource. // // Returns A handle to the iterator that can be passed to a "MakeIterator" or @@ -31157,6 +32803,21 @@ func BatchToSpace(scope *Scope, input tf.Output, crops tf.Output, block_size int return op.Output(0) } +// Produces a summary of any statistics recorded by the given statistics manager. +func ExperimentalStatsAggregatorSummary(scope *Scope, iterator tf.Output) (summary tf.Output) { + if scope.Err() != nil { + return + } + opspec := tf.OpSpec{ + Type: "ExperimentalStatsAggregatorSummary", + Input: []tf.Input{ + iterator, + }, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // Makes a new iterator from the given `dataset` and stores it in `iterator`. // // This operation may be executed multiple times. Each execution will reset the @@ -31488,6 +33149,26 @@ func FIFOQueueV2(scope *Scope, component_types []tf.DataType, optional ...FIFOQu return op.Output(0) } +// Deserializes a proto into the tree handle +// +// Arguments: +// tree_handle: Handle to the tree resource to be restored. +// tree_config: Serialied proto string of the boosted_trees.Tree proto. +// +// Returns the created operation. +func TensorForestTreeDeserialize(scope *Scope, tree_handle tf.Output, tree_config tf.Output) (o *tf.Operation) { + if scope.Err() != nil { + return + } + opspec := tf.OpSpec{ + Type: "TensorForestTreeDeserialize", + Input: []tf.Input{ + tree_handle, tree_config, + }, + } + return scope.AddOperation(opspec) +} + // Constructs an Optional variant from a tuple of tensors. func OptionalFromValue(scope *Scope, components []tf.Output) (optional tf.Output) { if scope.Err() != nil { @@ -31705,9 +33386,9 @@ func IteratorGetNextAsOptional(scope *Scope, iterator tf.Output, output_types [] // dimension of `input`. // // Arguments: -// input: A complex64 tensor. +// input: A complex tensor. // -// Returns A complex64 tensor of the same shape as `input`. The inner-most +// Returns A complex tensor of the same shape as `input`. The inner-most // dimension of `input` is replaced with its 1D Fourier transform. // // @compatibility(numpy) @@ -32418,6 +34099,28 @@ func Merge(scope *Scope, inputs []tf.Output) (output tf.Output, value_index tf.O return op.Output(0), op.Output(1) } +// Writes the given dataset to the given file using the TFRecord format. +// +// Arguments: +// input_dataset: A variant tensor representing the dataset to write. +// filename: A scalar string tensor representing the filename to use. +// compression_type: A scalar string tensor containing either (i) the empty string (no +// compression), (ii) "ZLIB", or (iii) "GZIP". +// +// Returns the created operation. +func ExperimentalDatasetToTFRecord(scope *Scope, input_dataset tf.Output, filename tf.Output, compression_type tf.Output) (o *tf.Operation) { + if scope.Err() != nil { + return + } + opspec := tf.OpSpec{ + Type: "ExperimentalDatasetToTFRecord", + Input: []tf.Input{ + input_dataset, filename, compression_type, + }, + } + return scope.AddOperation(opspec) +} + // QueueCloseV2Attr is an optional argument to QueueCloseV2. type QueueCloseV2Attr func(optionalAttr) @@ -32754,6 +34457,23 @@ func Rpc(scope *Scope, address tf.Output, method tf.Output, request tf.Output, o return op.Output(0) } +// Records the bytes size of each element of `input_dataset` in a StatsAggregator. +func ExperimentalBytesProducedStatsDataset(scope *Scope, input_dataset tf.Output, tag tf.Output, output_types []tf.DataType, output_shapes []tf.Shape) (handle tf.Output) { + if scope.Err() != nil { + return + } + attrs := map[string]interface{}{"output_types": output_types, "output_shapes": output_shapes} + opspec := tf.OpSpec{ + Type: "ExperimentalBytesProducedStatsDataset", + Input: []tf.Input{ + input_dataset, tag, + }, + Attrs: attrs, + } + op := scope.AddOperation(opspec) + return op.Output(0) +} + // StackPushV2Attr is an optional argument to StackPushV2. type StackPushV2Attr func(optionalAttr) -- GitLab From 55fda7fb66f1774ac5271e523259faa7d2a44d0c Mon Sep 17 00:00:00 2001 From: Davide Libenzi Date: Fri, 4 Jan 2019 15:31:48 -0800 Subject: [PATCH 374/622] Allow XLA input/output buffer aliasing to work via XRT. PiperOrigin-RevId: 227924255 --- .../xla/service/cpu/cpu_executable.cc | 29 +++- .../xla/service/gpu/gpu_executable.cc | 26 +++- tensorflow/compiler/xrt/kernels/BUILD | 1 + .../compiler/xrt/kernels/xrt_execute_op.cc | 17 +++ tensorflow/compiler/xrt/tests/raw_api_test.cc | 127 +++++++++++++++++- tensorflow/compiler/xrt/xrt_state.cc | 43 +++++- tensorflow/compiler/xrt/xrt_state.h | 23 +++- 7 files changed, 249 insertions(+), 17 deletions(-) diff --git a/tensorflow/compiler/xla/service/cpu/cpu_executable.cc b/tensorflow/compiler/xla/service/cpu/cpu_executable.cc index 412c2715b9..23d0af3423 100644 --- a/tensorflow/compiler/xla/service/cpu/cpu_executable.cc +++ b/tensorflow/compiler/xla/service/cpu/cpu_executable.cc @@ -213,6 +213,8 @@ StatusOr CpuExecutable::CreateResultShapedBuffer( /*on_host_shape=*/result_shape(), /*on_device_shape=*/result_shape(), run_options->allocator(), stream->parent()->device_ordinal()); + const HloInputOutputAliasConfig& input_output_alias = + module().input_output_alias_config(); // Move OwningDeviceMemory values which contain the array(s) of the result // into the respective location in ScopedShapedBuffer which is returned to the @@ -232,12 +234,31 @@ StatusOr CpuExecutable::CreateResultShapedBuffer( TF_ASSIGN_OR_RETURN( const BufferAllocation::Slice slice, this->assignment_->GetUniqueSlice(src, buffer_source->index())); - CHECK(!slice.allocation()->is_entry_computation_parameter()); - const BufferAllocation::Index buffer_index = slice.index(); OwningDeviceMemory& buffer = buffers[buffer_index]; - CHECK(!buffer.is_null() || buffer.size() == 0); - *device_memory = buffer.Forget(); + if (!slice.allocation()->is_entry_computation_parameter()) { + // If the buffer coming out of the result is from a parameter, the + // owning buffer will be null, and that means the caller aliased some + // parameter buffer to an output one (via the + // HloInputOutputAliasConfig API). If that is the case, the caller + // will receive a partially complete scoped shaped buffer, which they + // will have to fill up on return. Unfortunately the interface to the + // execute APIs are ShapedBuffer pointer based, which assumes caller + // ownership, and hence a buffer coming from there cannot be part of + // the new ScopedShapedBuffer we create for the result (which assumes + // ownership). + *device_memory = buffer.Forget(); + } else { + auto output_alias = input_output_alias.GetAliasedOutput( + slice.allocation()->parameter_number(), + slice.allocation()->param_shape_index()); + CHECK(output_alias) + << "Ouput buffer is coming from parameter " + << slice.allocation()->parameter_number() << " at index " + << slice.allocation()->param_shape_index() + << ", but no alias exists"; + CHECK_EQ(*output_alias, index); + } return Status::OK(); })); return std::move(result_buffer); diff --git a/tensorflow/compiler/xla/service/gpu/gpu_executable.cc b/tensorflow/compiler/xla/service/gpu/gpu_executable.cc index 128cecd765..434060ad89 100644 --- a/tensorflow/compiler/xla/service/gpu/gpu_executable.cc +++ b/tensorflow/compiler/xla/service/gpu/gpu_executable.cc @@ -310,12 +310,34 @@ StatusOr GpuExecutable::ExecuteOnStream( TF_ASSIGN_OR_RETURN( const BufferAllocation::Slice slice, this->assignment_->GetUniqueSlice(src_hlo, sources[0]->index())); - CHECK(!slice.allocation()->is_entry_computation_parameter()); se::DeviceMemoryBase src_base = buffer_allocations->GetDeviceAddress(slice.index()); CHECK(!src_base.is_null() || src_base.size() == 0); - *device_memory = src_base; + if (!slice.allocation()->is_entry_computation_parameter()) { + // If the buffer coming out of the result is from a parameter, it + // means the caller aliased some parameter buffer to an output one + // (via the HloInputOutputAliasConfig API). If that is the case, the + // caller will receive a partially complete scoped shaped buffer, + // which they will have to fill up on return. + // Unfortunately the interface to the execute APIs are ShapedBuffer + // pointer based, which assumes caller ownership, and hence a buffer + // coming from there cannot be part of the new ScopedShapedBuffer we + // create for the result (which assumes ownership). + *device_memory = src_base; + } else { + const HloInputOutputAliasConfig& input_output_alias = + module().input_output_alias_config(); + auto output_alias = input_output_alias.GetAliasedOutput( + slice.allocation()->parameter_number(), + slice.allocation()->param_shape_index()); + CHECK(output_alias) + << "Ouput buffer is coming from parameter " + << slice.allocation()->parameter_number() << " at index " + << slice.allocation()->param_shape_index() + << ", but no alias exists"; + CHECK_EQ(*output_alias, index); + } buffers_in_result.insert(src_base); return Status::OK(); })); diff --git a/tensorflow/compiler/xrt/kernels/BUILD b/tensorflow/compiler/xrt/kernels/BUILD index 67f475846e..c44769dfe3 100644 --- a/tensorflow/compiler/xrt/kernels/BUILD +++ b/tensorflow/compiler/xrt/kernels/BUILD @@ -55,6 +55,7 @@ cc_library( "//tensorflow/compiler/xla/client:xla_computation", "//tensorflow/compiler/xla/service:compiler", "//tensorflow/compiler/xla/service:computation_placer", + "//tensorflow/compiler/xla/service:hlo", "//tensorflow/compiler/xrt:xrt_proto", "//tensorflow/compiler/xrt:xrt_utils", "//tensorflow/core:core_cpu_internal", diff --git a/tensorflow/compiler/xrt/kernels/xrt_execute_op.cc b/tensorflow/compiler/xrt/kernels/xrt_execute_op.cc index 7f0ac123a5..7544541ca4 100644 --- a/tensorflow/compiler/xrt/kernels/xrt_execute_op.cc +++ b/tensorflow/compiler/xrt/kernels/xrt_execute_op.cc @@ -19,6 +19,7 @@ limitations under the License. #include "tensorflow/compiler/tf2xla/xla_op_registry.h" #include "tensorflow/compiler/xla/service/computation_placer.h" +#include "tensorflow/compiler/xla/service/hlo_input_output_alias_config.h" #include "tensorflow/compiler/xla/shape_util.h" #include "tensorflow/compiler/xla/status_macros.h" #include "tensorflow/compiler/xla/statusor.h" @@ -228,6 +229,22 @@ Status XRTExecuteOp::DoWork(OpKernelContext* context) { TF_RETURN_IF_ERROR(XRTTupleAllocation::CreateFromBuffer( shaped_buffer, device_ref.backend(), device_ref.device_ordinal(), &output_tuple)); + + // The ScopedShapedBuffer returned by the executable Run() API, in case of + // input/output buffer aliasing, might have holes in it, which need to be + // filled using the proper input tuples buffers which are the source of + // aliasing. + const xla::HloInputOutputAliasConfig& input_output_alias = + executable->executable()->module().input_output_alias_config(); + auto alias_function = [&](const xla::ShapeIndex& output_index, + int64 param_number, + const xla::ShapeIndex& param_index) -> Status { + TF_RET_CHECK(param_number < input_tuples.size()); + return output_tuple->AliasBufferFrom(*input_tuples[param_number], + param_index, output_index); + }; + TF_RETURN_IF_ERROR(input_output_alias.ForEachAliasWithStatus(alias_function)); + if (config_proto.return_exploded_tuple() && output_tuple->on_device_shape().IsTuple()) { int64 tuple_element_count = diff --git a/tensorflow/compiler/xrt/tests/raw_api_test.cc b/tensorflow/compiler/xrt/tests/raw_api_test.cc index c8479cb778..f81faf0e61 100644 --- a/tensorflow/compiler/xrt/tests/raw_api_test.cc +++ b/tensorflow/compiler/xrt/tests/raw_api_test.cc @@ -96,14 +96,21 @@ xla::LiteralProto FloatMatrix( return array.ToProto(); } +xla::Literal ReadOutputLiteral(const std::vector& outputs, size_t idx) { + xla::LiteralProto response; + CHECK(response.ParseFromString(outputs[idx].scalar()())); + return xla::Literal::CreateFromProto(response).ValueOrDie(); +} + bool CompareLiteralProtos(const xla::LiteralProto& a, const xla::LiteralProto& b) { auto l_a = xla::Literal::CreateFromProto(a).ValueOrDie(); auto l_b = xla::Literal::CreateFromProto(b).ValueOrDie(); bool equal = l_a == l_b; if (!equal) { - LOG(INFO) << "LiteralProtos don't match: " << a.DebugString() - << " != " << b.DebugString(); + LOG(INFO) << "LiteralProtos don't match:\n" + << a.DebugString() << "\n!=\n" + << b.DebugString(); } return equal; } @@ -113,8 +120,19 @@ bool CompareLiteralToLiteralProto(const xla::Literal& a, auto l_b = xla::Literal::CreateFromProto(b).ValueOrDie(); bool equal = a == l_b; if (!equal) { - LOG(INFO) << "Literal and LiteralProto don't match " - << a.ToProto().DebugString() << " != " << b.DebugString(); + LOG(INFO) << "Literal and LiteralProto don't match:\n" + << a.ToProto().DebugString() << "\n!=\n" + << b.DebugString(); + } + return equal; +} + +bool CompareLiterals(const xla::Literal& a, const xla::Literal& b) { + bool equal = a == b; + if (!equal) { + LOG(INFO) << "Literals don't match:\n" + << a.ToProto().DebugString() << "\n!=\n" + << b.ToProto().DebugString(); } return equal; } @@ -939,6 +957,107 @@ TEST(RawApiTest, LeakCompilationReference) { TF_EXPECT_OK(session.Run({c_handle.handle}, &outputs)); } +TEST(RawApiTest, CompileAndExecuteWithReusedBuffers) { + xla::Shape element_shape = xla::ShapeUtil::MakeShape(xla::F32, {2}); + xla::Shape shape = + xla::ShapeUtil::MakeTupleShape({element_shape, element_shape}); + xla::Shape return_shape = xla::ShapeUtil::MakeTupleShape( + {element_shape, element_shape, element_shape, element_shape}); + xla::XlaBuilder builder("ReuseBuffer"); + auto param = xla::Parameter(&builder, 0, shape, "param"); + auto p0 = xla::GetTupleElement(param, 0); + auto p1 = xla::GetTupleElement(param, 1); + auto add = xla::Add(p0, p1); + auto sub = xla::Sub(p0, p1); + xla::Tuple(&builder, {add, sub, p0, p1}); + + // Flip the tuple literals in the input handle. + builder.SetUpAlias({1}, 0, {0}); + builder.SetUpAlias({0}, 0, {1}); + + auto computation = builder.Build().ValueOrDie(); + + auto literal0 = xla::LiteralUtil::CreateR1({1.0f, 2.0f}); + auto literal1 = xla::LiteralUtil::CreateR1({5.0f, 9.0f}); + auto literal = xla::LiteralUtil::MakeTuple({&literal0, &literal1}); + + xrt::XLAAllocation param_alloc; + *param_alloc.mutable_value() = literal.ToProto(); + + xrt::XLAComputation c; + auto config = c.mutable_config(); + auto shapes = config->mutable_program_shape(); + *shapes->add_parameters() = shape.ToProto(); + *shapes->mutable_result() = return_shape.ToProto(); + StoreComputationSnapshot(computation, c.mutable_hlo_snapshot()); + + xrt::XRTExecutionConfig e; + e.set_release_input_handles(false); + e.set_release_compilation_handle(true); + + Scope root = Scope::NewRootScope().WithDevice(DeviceFromFlag()); + ClientSession session(root); + auto e_config = + ops::Const(root.WithDevice("/device:CPU:0"), e.SerializeAsString()); + auto c_data = + ops::Const(root.WithDevice("/device:CPU:0"), c.SerializeAsString()); + auto c_handle = ops::XRTCompile(root, c_data); + auto param_value = ops::Const(root.WithDevice("/device:CPU:0"), + param_alloc.SerializeAsString()); + auto param_handle = ops::XRTAllocate(root, param_value); + TF_ASSERT_OK(root.status()); + + std::vector outputs; + TF_EXPECT_OK(session.Run({param_handle}, &outputs)); + + int64 alloc_handle = outputs[0].scalar()(); + + // Note that we release the result handle immediately, but since we aliased + // the output buffers onto the input allocation ones (held in alloc_handle), + // we can fetch the result from there. + auto result = + ops::XRTExecute(root, c_handle.handle, e_config, {Input(alloc_handle)}); + auto read_back = ops::XRTReadLiteral(root, result); + auto release = ops::XRTReleaseAllocationHandle( + root.WithControlDependencies(read_back), result); + TF_ASSERT_OK(root.status()); + + outputs.clear(); + TF_EXPECT_OK(session.Run(tensorflow::ClientSession::FeedType(), {read_back}, + {release}, &outputs)); + + xla::Literal exec_literal = ReadOutputLiteral(outputs, 0); + auto exec_literal_parts = exec_literal.DecomposeTuple(); + ASSERT_EQ(exec_literal_parts.size(), 4); + + EXPECT_TRUE(CompareLiterals(exec_literal_parts[2], literal0)); + EXPECT_TRUE(CompareLiterals(exec_literal_parts[3], literal1)); + + // Now we read back the original input handle values, which at this point + // should contain the result of the XLA computation. + auto read_handle = ops::XRTReadLiteral(root, Input(alloc_handle)); + TF_ASSERT_OK(root.status()); + auto release_handle = ops::XRTReleaseAllocationHandle( + root.WithControlDependencies(read_handle), Input(alloc_handle)); + TF_ASSERT_OK(root.status()); + + outputs.clear(); + TF_EXPECT_OK(session.Run(tensorflow::ClientSession::FeedType(), {read_handle}, + {release_handle}, &outputs)); + + xla::Literal return_literal = ReadOutputLiteral(outputs, 0); + + auto expected_literal0 = xla::LiteralUtil::CreateR1({6.0f, 11.0f}); + auto expected_literal1 = xla::LiteralUtil::CreateR1({-4.0f, -7.0f}); + // The first element of the computation returned tuple would be the add + // (expected_literal0), but since we flipped the buffers, the sub + // (expected_literal1) should come first. + auto expected_literal = + xla::LiteralUtil::MakeTuple({&expected_literal1, &expected_literal0}); + + EXPECT_TRUE(CompareLiterals(return_literal, expected_literal)); +} + TEST(RawApiTest, CompileAndExecuteWithS64Argument) { xrt::XLAAllocation p0; *p0.mutable_value() = xla::LiteralUtil::CreateR0(11031965).ToProto(); diff --git a/tensorflow/compiler/xrt/xrt_state.cc b/tensorflow/compiler/xrt/xrt_state.cc index 343460ff10..13c275aaca 100644 --- a/tensorflow/compiler/xrt/xrt_state.cc +++ b/tensorflow/compiler/xrt/xrt_state.cc @@ -133,7 +133,8 @@ Status AllocateScopedShapedBuffer( XRTBufferAllocation::XRTBufferAllocation(const se::DeviceMemoryBase& allocation, int device_ordinal, xla::DeviceMemoryAllocator* allocator) - : allocation_(allocation), + : size_(allocation.size()), + allocation_(allocation), device_ordinal_(device_ordinal), allocator_(allocator) { if (VLOG_IS_ON(2)) { @@ -223,8 +224,19 @@ Status XRTTupleAllocation::ToLiteral(xla::Backend* backend, int device_ordinal, xla::Literal* literal) { auto transfer_manager = backend->transfer_manager(); TF_ASSIGN_OR_RETURN(auto stream, backend->BorrowStream(device_ordinal)); + + // Validate the allocation buffers as if nulls gets to + // TransferLiteralFromDevice() a CHECK is issued. + xla::ShapedBuffer shaped_buffer = ToShapedBuffer(); + for (auto& index_buffer : shaped_buffer.buffers()) { + if (index_buffer.second.is_null()) { + return errors::InvalidArgument("Literal buffer at index ", + index_buffer.first.ToString(), + " has been released"); + } + } TF_ASSIGN_OR_RETURN(*literal, transfer_manager->TransferLiteralFromDevice( - stream.get(), ToShapedBuffer())); + stream.get(), shaped_buffer)); return Status::OK(); } @@ -505,11 +517,34 @@ xla::ShapedBuffer XRTTupleAllocation::ToShapedBuffer() { return shaped_buffer; } +Status XRTTupleAllocation::AliasBufferFrom(const XRTTupleAllocation& source, + const xla::ShapeIndex& source_index, + const xla::ShapeIndex& dest_index) { + XRTBufferAllocation* source_buffer = source.buffers_.element(source_index); + XRTBufferAllocation* dest_buffer = buffers_.element(dest_index); + // We allow the destination size being zero, because there are cases where we + // are coming in later filling in null/uninitialized device buffers. + // In all other cases, the size of the new buffer must match. + if (source_buffer->size() != dest_buffer->size() && + dest_buffer->size() != 0) { + return errors::InvalidArgument( + "Source buffer at index ", source_index.ToString(), + " does not match the size of destination buffer at index ", + dest_index.ToString(), ": ", source_buffer->size(), " vs ", + dest_buffer->size()); + } + *buffers_.mutable_element(dest_index) = source_buffer; + source_buffer->Ref(); + dest_buffer->Unref(); + return Status::OK(); +} + xla::ShapeTree -XRTTupleAllocation::ToDeviceMemoryTree(bool release) { +XRTTupleAllocation::ToDeviceMemoryTree( + const std::function& release_checker) { xla::ShapeTree shaped_tree(on_device_shape()); for (const auto& buffer : buffers_) { - if (!release) { + if (!release_checker(buffer.first)) { *shaped_tree.mutable_element(buffer.first) = buffer.second->allocation(); } else { *shaped_tree.mutable_element(buffer.first) = xla::OwningDeviceMemory( diff --git a/tensorflow/compiler/xrt/xrt_state.h b/tensorflow/compiler/xrt/xrt_state.h index 3e3d502412..ac4be3a064 100644 --- a/tensorflow/compiler/xrt/xrt_state.h +++ b/tensorflow/compiler/xrt/xrt_state.h @@ -18,6 +18,7 @@ limitations under the License. #ifndef TENSORFLOW_COMPILER_XRT_XRT_STATE_H_ #define TENSORFLOW_COMPILER_XRT_XRT_STATE_H_ +#include #include #include #include @@ -58,7 +59,14 @@ class XRTBufferAllocation : public core::RefCounted { // freed when the reference count drops to zero. void DiscardAllocation(); + // Returns the expected size of the allocation. Since DiscardAllocation() will + // set allocation_ to {null,0}, and since later we might want to replace the + // discarded buffer with a new one, we need to be able to verify the size + // compatibility. + uint64 size() const { return size_; } + private: + uint64 size_ = 0; se::DeviceMemoryBase allocation_; int device_ordinal_; xla::DeviceMemoryAllocator* allocator_; @@ -168,9 +176,18 @@ class XRTTupleAllocation : public ResourceBase { // the same shape as on_host_shape. xla::ShapedBuffer ToShapedBuffer(); - // Returns the device memory tree of this allocation. If 'release' is set, the - // ownership of the device memory is transferred to the result. - xla::ShapeTree ToDeviceMemoryTree(bool release); + // Aliases the source buffer at source_index into the current tuple allocation + // dest_index. + Status AliasBufferFrom(const XRTTupleAllocation& source, + const xla::ShapeIndex& source_index, + const xla::ShapeIndex& dest_index); + + // Returns the device memory tree of this allocation. If the release_checker + // function returns true for a given index, the ownership of the device memory + // at that index is transferred to the result. Every attempt to read the value + // at that index will fail. + xla::ShapeTree ToDeviceMemoryTree( + const std::function& release_checker); string DebugString() override { return "XLA allocation handle"; } -- GitLab From 02bfac424857fa45621efab5908a31ffd4ce799b Mon Sep 17 00:00:00 2001 From: Pavithra Vijay Date: Fri, 4 Jan 2019 15:32:34 -0800 Subject: [PATCH 375/622] Add kullback leibler divergence v2 loss and metric. PiperOrigin-RevId: 227924402 --- tensorflow/python/keras/losses.py | 27 +++++++++ tensorflow/python/keras/losses_test.py | 80 +++++++++++++++++++++++++ tensorflow/python/keras/metrics.py | 32 ++++++++++ tensorflow/python/keras/metrics_test.py | 47 +++++++++++++++ 4 files changed, 186 insertions(+) diff --git a/tensorflow/python/keras/losses.py b/tensorflow/python/keras/losses.py index 46e729c197..de1605a09c 100644 --- a/tensorflow/python/keras/losses.py +++ b/tensorflow/python/keras/losses.py @@ -581,6 +581,33 @@ class Logcosh(Loss): return logcosh(y_true, y_pred) +class KullbackLeiblerDivergence(Loss): + """Computes kullback leibler divergence loss between `y_true` and `y_pred`. + + loss = y_true * log(y_true / y_pred) + + Usage: + + ```python + k = tf.losses.KullbackLeiblerDivergence() + loss = k([.4, .9, .2], [.5, .8, .12]) + print('Loss: ', loss.numpy()) # Loss: -0.043 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile('sgd', loss=tf.losses.KullbackLeiblerDivergence()) + ``` + """ + + def call(self, y_true, y_pred): + y_pred = ops.convert_to_tensor(y_pred) + y_true = math_ops.cast(y_true, y_pred.dtype) + return kullback_leibler_divergence(y_true, y_pred) + + @keras_export('keras.metrics.mean_squared_error', 'keras.metrics.mse', 'keras.metrics.MSE', diff --git a/tensorflow/python/keras/losses_test.py b/tensorflow/python/keras/losses_test.py index f34a6caa18..caa4ff4c2e 100644 --- a/tensorflow/python/keras/losses_test.py +++ b/tensorflow/python/keras/losses_test.py @@ -1256,5 +1256,85 @@ class PoissonTest(test.TestCase): self.assertAlmostEqual(self.evaluate(loss), 0., 3) +@test_util.run_all_in_graph_and_eager_modes +class KullbackLeiblerDivergenceTest(test.TestCase): + + def setup(self): + self.np_y_pred = np.asarray([.4, .9, .12, .36, .3, .4]).reshape((2, 3)) + self.np_y_true = np.asarray([.5, .8, .12, .7, .43, .8]).reshape((2, 3)) + + self.batch_size = 2 + self.expected_losses = np.multiply(self.np_y_true, + np.log(self.np_y_true / self.np_y_pred)) + + self.y_pred = constant_op.constant(self.np_y_pred, dtype=dtypes.float32) + self.y_true = constant_op.constant(self.np_y_true) + + def test_config(self): + k_obj = keras.losses.KullbackLeiblerDivergence( + reduction=losses_impl.ReductionV2.SUM, name='kld') + self.assertEqual(k_obj.name, 'kld') + self.assertEqual(k_obj.reduction, losses_impl.ReductionV2.SUM) + + def test_unweighted(self): + self.setup() + k_obj = keras.losses.KullbackLeiblerDivergence() + + loss = k_obj(self.y_true, self.y_pred) + expected_loss = np.sum(self.expected_losses) / self.batch_size + self.assertAlmostEqual(self.evaluate(loss), expected_loss, 3) + + def test_scalar_weighted(self): + self.setup() + k_obj = keras.losses.KullbackLeiblerDivergence() + sample_weight = 2.3 + + loss = k_obj(self.y_true, self.y_pred, sample_weight=sample_weight) + expected_loss = sample_weight * np.sum( + self.expected_losses) / self.batch_size + self.assertAlmostEqual(self.evaluate(loss), expected_loss, 3) + + # Verify we get the same output when the same input is given + loss_2 = k_obj(self.y_true, self.y_pred, sample_weight=sample_weight) + self.assertAlmostEqual(self.evaluate(loss), self.evaluate(loss_2), 3) + + def test_sample_weighted(self): + self.setup() + k_obj = keras.losses.KullbackLeiblerDivergence() + sample_weight = constant_op.constant([1.2, 3.4], shape=(2, 1)) + loss = k_obj(self.y_true, self.y_pred, sample_weight=sample_weight) + + expected_loss = np.multiply( + self.expected_losses, + np.asarray([1.2, 1.2, 1.2, 3.4, 3.4, 3.4]).reshape(2, 3)) + expected_loss = np.sum(expected_loss) / self.batch_size + self.assertAlmostEqual(self.evaluate(loss), expected_loss, 3) + + def test_timestep_weighted(self): + self.setup() + k_obj = keras.losses.KullbackLeiblerDivergence() + y_true = self.np_y_true.reshape(2, 3, 1) + y_pred = self.np_y_pred.reshape(2, 3, 1) + sample_weight = np.asarray([3, 6, 5, 0, 4, 2]).reshape(2, 3) + expected_losses = np.sum( + np.multiply(y_true, np.log(y_true / y_pred)), axis=-1) + + y_pred = constant_op.constant(y_pred, dtype=dtypes.float32) + y_true = constant_op.constant(y_true) + loss = k_obj( + y_true, y_pred, sample_weight=constant_op.constant(sample_weight)) + + num_timesteps = 3 + expected_loss = np.sum(expected_losses * sample_weight) / ( + self.batch_size * num_timesteps) + self.assertAlmostEqual(self.evaluate(loss), expected_loss, 3) + + def test_zero_weighted(self): + self.setup() + k_obj = keras.losses.KullbackLeiblerDivergence() + loss = k_obj(self.y_true, self.y_pred, sample_weight=0) + self.assertAlmostEqual(self.evaluate(loss), 0., 3) + + if __name__ == '__main__': test.main() diff --git a/tensorflow/python/keras/metrics.py b/tensorflow/python/keras/metrics.py index 71568e3fc5..9a2e057780 100644 --- a/tensorflow/python/keras/metrics.py +++ b/tensorflow/python/keras/metrics.py @@ -1716,6 +1716,38 @@ class Poisson(MeanMetricWrapper): return super(Poisson, cls).from_config(config) +class KullbackLeiblerDivergence(MeanMetricWrapper): + """Computes kullback leibler divergence metric between `y_true` and `y_pred`. + + metric = y_true * log(y_true / y_pred) + + Usage: + + ```python + m = tf.keras.metrics.KullbackLeiblerDivergence() + m.update_state([.4, .9, .2], [.5, .8, .12]) + print('Final result: ', m.result().numpy()) # Final result: -0.043 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile('sgd', metrics=[tf.keras.metrics.KullbackLeiblerDivergence()]) + ``` + """ + + def __init__(self, name='kullback_leibler_divergence', dtype=None): + super(KullbackLeiblerDivergence, self).__init__( + kullback_leibler_divergence, name, dtype=dtype) + + @classmethod + def from_config(cls, config): + if 'fn' in config: + config.pop('fn') + return super(KullbackLeiblerDivergence, cls).from_config(config) + + def accuracy(y_true, y_pred): y_pred.get_shape().assert_is_compatible_with(y_true.get_shape()) if y_true.dtype != y_pred.dtype: diff --git a/tensorflow/python/keras/metrics_test.py b/tensorflow/python/keras/metrics_test.py index 88b763d569..a02b95bb46 100644 --- a/tensorflow/python/keras/metrics_test.py +++ b/tensorflow/python/keras/metrics_test.py @@ -1556,6 +1556,53 @@ class PoissonTest(test.TestCase): self.assertAllClose(self.evaluate(result), expected_result, atol=1e-3) +@test_util.run_all_in_graph_and_eager_modes +class KullbackLeiblerDivergenceTest(test.TestCase): + + def setup(self): + y_pred = np.asarray([.4, .9, .12, .36, .3, .4]).reshape((2, 3)) + y_true = np.asarray([.5, .8, .12, .7, .43, .8]).reshape((2, 3)) + + self.batch_size = 2 + self.expected_results = np.multiply(y_true, np.log(y_true / y_pred)) + + self.y_pred = constant_op.constant(y_pred, dtype=dtypes.float32) + self.y_true = constant_op.constant(y_true) + + def test_config(self): + k_obj = metrics.KullbackLeiblerDivergence(name='kld', dtype=dtypes.int32) + self.assertEqual(k_obj.name, 'kld') + self.assertEqual(k_obj._dtype, dtypes.int32) + + k_obj2 = metrics.KullbackLeiblerDivergence.from_config(k_obj.get_config()) + self.assertEqual(k_obj2.name, 'kld') + self.assertEqual(k_obj2._dtype, dtypes.int32) + + def test_unweighted(self): + self.setup() + k_obj = metrics.KullbackLeiblerDivergence() + self.evaluate(variables.variables_initializer(k_obj.variables)) + + update_op = k_obj.update_state(self.y_true, self.y_pred) + self.evaluate(update_op) + result = k_obj.result() + expected_result = np.sum(self.expected_results) / self.batch_size + self.assertAllClose(result, expected_result, atol=1e-3) + + def test_weighted(self): + self.setup() + k_obj = metrics.KullbackLeiblerDivergence() + self.evaluate(variables.variables_initializer(k_obj.variables)) + + sample_weight = constant_op.constant([1.2, 3.4], shape=(2, 1)) + result = k_obj(self.y_true, self.y_pred, sample_weight=sample_weight) + + sample_weight = np.asarray([1.2, 1.2, 1.2, 3.4, 3.4, 3.4]).reshape((2, 3)) + expected_result = np.multiply(self.expected_results, sample_weight) + expected_result = np.sum(expected_result) / (1.2 + 3.4) + self.assertAllClose(self.evaluate(result), expected_result, atol=1e-3) + + def _get_model(compile_metrics): model_layers = [ layers.Dense(3, activation='relu', kernel_initializer='ones'), -- GitLab From 984ace61cdef46145a63081151c7eeb8609c4133 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Fri, 4 Jan 2019 15:45:38 -0800 Subject: [PATCH 376/622] Report the CUcontext in the cuda_driver log spew, not the CudaContext pointer. CUDA tools (like nvprof, cuda-memcheck) report the CUcontext, so this makes it possible to match them up during debugging. PiperOrigin-RevId: 227926402 --- .../stream_executor/cuda/cuda_driver.cc | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/tensorflow/stream_executor/cuda/cuda_driver.cc b/tensorflow/stream_executor/cuda/cuda_driver.cc index b34d1f722e..ca7a717bdb 100644 --- a/tensorflow/stream_executor/cuda/cuda_driver.cc +++ b/tensorflow/stream_executor/cuda/cuda_driver.cc @@ -431,7 +431,8 @@ bool DeviceOptionsToContextFlags(const DeviceOptions &device_options, *context = CreatedContexts::Add(new_context); CHECK(*context != nullptr) << "success in this call must entail non-null result"; - VLOG(2) << "created or reused context " << context << " for this thread"; + VLOG(2) << "created or reused context " << new_context + << " for this thread"; return port::Status::OK(); } @@ -769,13 +770,13 @@ CUDADriver::ContextGetSharedMemConfig(CudaContext* context) { ScopedActivateContext activated{context}; CUresult res = cuStreamCreate(out, 0); if (res != CUDA_SUCCESS) { - LOG(ERROR) << "could not allocate CUDA stream for context " << context - << ": " << ToString(res); + LOG(ERROR) << "could not allocate CUDA stream for context " + << context->context() << ": " << ToString(res); return false; } VLOG(2) << "successfully created stream " << *out << " for context " - << context << " on thread"; + << context->context() << " on thread"; return true; } @@ -788,11 +789,11 @@ CUDADriver::ContextGetSharedMemConfig(CudaContext* context) { ScopedActivateContext activated{context}; CUresult res = cuStreamDestroy(*stream); if (res != CUDA_SUCCESS) { - LOG(ERROR) << "failed to destroy CUDA stream for context " << context - << ": " << ToString(res); + LOG(ERROR) << "failed to destroy CUDA stream for context " + << context->context() << ": " << ToString(res); } else { VLOG(2) << "successfully destroyed stream " << *stream << " for context " - << context; + << context->context(); *stream = nullptr; } } @@ -809,8 +810,8 @@ CUDADriver::ContextGetSharedMemConfig(CudaContext* context) { return nullptr; } void *ptr = reinterpret_cast(result); - VLOG(2) << "allocated " << ptr << " for context " << context << " of " - << bytes << " bytes"; + VLOG(2) << "allocated " << ptr << " for context " << context->context() + << " of " << bytes << " bytes"; return ptr; } @@ -823,7 +824,8 @@ CUDADriver::ContextGetSharedMemConfig(CudaContext* context) { LOG(ERROR) << "failed to free device memory at " << location << "; result: " << ToString(res); } else { - VLOG(2) << "deallocated " << location << " for context " << context; + VLOG(2) << "deallocated " << location << " for context " + << context->context(); } } @@ -839,8 +841,8 @@ CUDADriver::ContextGetSharedMemConfig(CudaContext* context) { return nullptr; } void *ptr = reinterpret_cast(result); - VLOG(2) << "allocated " << ptr << " for context " << context << " of " - << bytes << " bytes in unified memory"; + VLOG(2) << "allocated " << ptr << " for context " << context->context() + << " of " << bytes << " bytes in unified memory"; return ptr; } @@ -854,7 +856,7 @@ CUDADriver::ContextGetSharedMemConfig(CudaContext* context) { << "; result: " << ToString(res); } else { VLOG(2) << "deallocated unified memory at " << location << " for context " - << context; + << context->context(); } } -- GitLab From 57d501422c16dd73b391d19bcc1e203942cc6b24 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Fri, 4 Jan 2019 15:55:26 -0800 Subject: [PATCH 377/622] Add Keras LSTM model to correctness test. PiperOrigin-RevId: 227927675 --- tensorflow/contrib/distribute/python/BUILD | 3 +- .../contrib/distribute/python/combinations.py | 15 ++- .../python/keras_correctness_test.py | 100 +++++++++++++++--- 3 files changed, 99 insertions(+), 19 deletions(-) diff --git a/tensorflow/contrib/distribute/python/BUILD b/tensorflow/contrib/distribute/python/BUILD index de6b6f1f84..c620a0448e 100644 --- a/tensorflow/contrib/distribute/python/BUILD +++ b/tensorflow/contrib/distribute/python/BUILD @@ -670,11 +670,12 @@ py_library( cuda_py_test( name = "keras_correctness_test", + size = "medium", srcs = ["keras_correctness_test.py"], additional_deps = [ ":keras_correctness_test_lib", ], - shard_count = 16, + shard_count = 20, tags = [ "multi_and_single_gpu", "no_oss", # TODO(b/117919883): Fix python error. diff --git a/tensorflow/contrib/distribute/python/combinations.py b/tensorflow/contrib/distribute/python/combinations.py index f6c4291659..8db0e9c316 100644 --- a/tensorflow/contrib/distribute/python/combinations.py +++ b/tensorflow/contrib/distribute/python/combinations.py @@ -321,11 +321,12 @@ class NamedDistribution(object): return self._required_tpu -def _get_tpu_strategy_creator(steps_per_run): +def _get_tpu_strategy_creator(steps_per_run, **kwargs): def _create_tpu_strategy(): resolver = cluster_resolver.TPUClusterResolver("") tpu_lib.initialize_tpu_system(resolver) - strategy = tpu_lib.TPUStrategy(resolver, steps_per_run=steps_per_run) + strategy = tpu_lib.TPUStrategy(resolver, + steps_per_run=steps_per_run, **kwargs) return strategy return _create_tpu_strategy @@ -344,7 +345,15 @@ tpu_strategy = NamedDistribution( tpu_strategy_one_step = NamedDistribution( "TPUOneStep", _get_tpu_strategy_creator(steps_per_run=1), required_tpu=True) - +# TODO(b/122327153): Remove below two NamedDistributions. +tpu_strategy_loop_on_device = NamedDistribution( + "TPULoopOnDevice", _get_tpu_strategy_creator( + steps_per_run=2, _disable_training_loop_on_host=True), + required_tpu=True) +tpu_strategy_one_step_loop_on_device = NamedDistribution( + "TPUOneStepLoopOnDevice", _get_tpu_strategy_creator( + steps_per_run=1, _disable_training_loop_on_host=True), + required_tpu=True) mirrored_strategy_with_one_cpu = NamedDistribution( "Mirrored1CPU", lambda: mirrored_lib.MirroredStrategy(["/cpu:0"])) diff --git a/tensorflow/contrib/distribute/python/keras_correctness_test.py b/tensorflow/contrib/distribute/python/keras_correctness_test.py index 3abdee2c0e..1c4519ef71 100644 --- a/tensorflow/contrib/distribute/python/keras_correctness_test.py +++ b/tensorflow/contrib/distribute/python/keras_correctness_test.py @@ -62,24 +62,41 @@ def all_strategy_combinations_with_graph_mode(): return combinations.combine(distribution=all_strategies, mode=['graph']) +def tpu_strategies_with_host_training_loop_disabled(): + strategies = [s for s in all_strategies if not s.required_tpu] + strategies.append(combinations.tpu_strategy_loop_on_device) + strategies.append(combinations.tpu_strategy_one_step_loop_on_device) + return strategies + + def strategy_and_input_combinations(): def cnn_model_with_batch_norm(**kwargs): return _create_cnn_model(with_batch_norm=True, **kwargs) - return ( - combinations.times( - combinations.combine(distribution=all_strategies), - combinations.combine(mode=['graph', 'eager'], - use_numpy=[True, False], - use_validation_data=[True, False]), - combinations.combine(model_with_data=[ - ModelWithData('dnn', _create_dnn_model, _dnn_training_data), - ModelWithData('cnn', _create_cnn_model, _cnn_training_data), - ModelWithData('cnn_batch_norm', - cnn_model_with_batch_norm, - _cnn_training_data, - with_batch_norm=True), - ]))) + combinations_without_embedding_model = combinations.times( + combinations.combine( + distribution=tpu_strategies_with_host_training_loop_disabled()), + combinations.combine(mode=['graph', 'eager'], + use_numpy=[True, False], + use_validation_data=[True, False]), + combinations.combine(model_with_data= + [ModelWithData('lstm', + _create_lstm_model, + _lstm_training_data)])) + + combinations_with_embedding_model = combinations.times( + combinations.combine(distribution=all_strategies), + combinations.combine(mode=['graph', 'eager'], + use_numpy=[True, False], + use_validation_data=[True, False]), + combinations.combine(model_with_data=[ + ModelWithData('dnn', _create_dnn_model, _dnn_training_data), + ModelWithData('cnn', _create_cnn_model, _cnn_training_data), + ModelWithData('cnn_batch_norm', cnn_model_with_batch_norm, + _cnn_training_data, with_batch_norm=True),])) + + return (combinations_with_embedding_model + + combinations_without_embedding_model) class MaybeDistributionScope(object): @@ -191,6 +208,59 @@ def _create_cnn_model(initial_weights=None, distribution=None, return model +def _lstm_training_data(count=_GLOBAL_BATCH_SIZE * _EVAL_STEPS, + min_words=10, + max_words=20, + max_word_id=99, + num_classes=2): + distribution = [] + for _ in range(num_classes): + dist = np.abs(np.random.randn(max_word_id)) + dist /= np.sum(dist) + distribution.append(dist) + + features = [] + labels = [] + for _ in range(count): + label = np.random.randint(0, num_classes, size=1)[0] + num_words = np.random.randint(min_words, max_words, size=1)[0] + word_ids = np.random.choice( + max_word_id, size=num_words, replace=True, p=distribution[label]) + word_ids = word_ids + labels.append(label) + features.append(word_ids) + + features = keras.preprocessing.sequence.pad_sequences( + features, maxlen=max_words) + x_train = np.asarray(features, dtype=np.float32) + y_train = np.asarray(labels, dtype=np.int32).reshape((count, 1)) + x_predict = x_train + return x_train, y_train, x_predict + + +def _create_lstm_model(max_words=20, + initial_weights=None, + distribution=None): + with MaybeDistributionScope(distribution): + word_ids = keras.layers.Input( + shape=(max_words,), dtype=np.int32, name='words') + word_embed = keras.layers.Embedding(input_dim=100, output_dim=10)(word_ids) + lstm_embed = keras.layers.LSTM( + units=8, return_sequences=False)(word_embed) + + preds = keras.layers.Dense(2, activation='softmax')(lstm_embed) + model = keras.Model(inputs=[word_ids], outputs=[preds]) + + if initial_weights: + model.set_weights(initial_weights) + + model.compile( + optimizer=gradient_descent.GradientDescentOptimizer(learning_rate=0.1), + loss='sparse_categorical_crossentropy', + metrics=['sparse_categorical_accuracy']) + return model + + def batch_wrapper(dataset, batch_size, distribution, repeat=None): if repeat: dataset = dataset.repeat(repeat) @@ -243,7 +313,7 @@ def get_correctness_test_inputs(use_numpy, use_validation_data, } else: if len(x_train) < _GLOBAL_BATCH_SIZE * _EVAL_STEPS: - # Currently, we cannot detech the size of a dataset. So, the eval steps is + # Currently, we cannot detect the size of a dataset. So, the eval steps is # hard coded. raise ValueError('x_train must have at least ' '_GLOBAL_BATCH_SIZE * _EVAL_STEPS samples') -- GitLab From 571d0114eda553e2d1b5c9c71f77c2211b5914e3 Mon Sep 17 00:00:00 2001 From: Zhenyu Tan Date: Fri, 4 Jan 2019 16:10:25 -0800 Subject: [PATCH 378/622] Internal Cleanup. PiperOrigin-RevId: 227929845 --- .../keras/optimizer_v2/optimizer_v2_test.py | 18 ------------------ tensorflow/python/keras/optimizers.py | 5 +++-- 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/tensorflow/python/keras/optimizer_v2/optimizer_v2_test.py b/tensorflow/python/keras/optimizer_v2/optimizer_v2_test.py index 290bdcad14..58c8b1f343 100644 --- a/tensorflow/python/keras/optimizer_v2/optimizer_v2_test.py +++ b/tensorflow/python/keras/optimizer_v2/optimizer_v2_test.py @@ -27,7 +27,6 @@ import numpy as np from tensorflow.python import keras from tensorflow.python.eager import context from tensorflow.python.eager import def_function -from tensorflow.python.eager import function from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops @@ -262,23 +261,6 @@ class OptimizerTest(test.TestCase): self.evaluate(sgd.iterations.initializer) self.assertEqual(0, self.evaluate(sgd.iterations)) - @test_util.run_in_graph_and_eager_modes - def testSerializationWithinDefun(self): - with self.cached_session(): - sgd = gradient_descent.SGD(3.0) - var0 = resource_variable_ops.ResourceVariable([1.0, 2.0], - dtype=dtypes.float32) - loss = lambda: 3 * var0 - sgd.minimize(loss, [var0]) - - def serialize(): - config = sgd.get_config() - gradient_descent.SGD.from_config(config) - - compiled_serialize = function.defun(serialize) - with self.assertRaisesRegexp(RuntimeError, 'inside Tensorflow graph'): - compiled_serialize() - @test_util.run_in_graph_and_eager_modes def testConfig(self): with self.cached_session(): diff --git a/tensorflow/python/keras/optimizers.py b/tensorflow/python/keras/optimizers.py index 0ab52fd0a0..12168e5574 100644 --- a/tensorflow/python/keras/optimizers.py +++ b/tensorflow/python/keras/optimizers.py @@ -575,7 +575,7 @@ class Adamax(Optimizer): def get_updates(self, loss, params): grads = self.get_gradients(loss, params) - self.updates = [state_ops.assign_add(self.iterations, 1)] + self.updates = [] lr = self.lr if self.initial_decay > 0: @@ -583,7 +583,8 @@ class Adamax(Optimizer): 1. / (1. + self.decay * math_ops.cast(self.iterations, K.dtype(self.decay)))) - t = math_ops.cast(self.iterations, K.floatx()) + 1 + with ops.control_dependencies([state_ops.assign_add(self.iterations, 1)]): + t = math_ops.cast(self.iterations, K.floatx()) lr_t = lr / (1. - math_ops.pow(self.beta_1, t)) shapes = [K.int_shape(p) for p in params] -- GitLab From abdbe6ee3d81d22d70e0181cea2a3bb261f7c09f Mon Sep 17 00:00:00 2001 From: Gaurav Jain Date: Fri, 4 Jan 2019 16:31:04 -0800 Subject: [PATCH 379/622] Add anonymous shared name GUID to avoid kernel cache Fixes #19671 We generate unique shared names for variables in eager mode to avoid unintended sharing. This results in a memory leak due to the kernel cache expanding with each new shared_name encountered. To avoid this we reserve a shared name to send in with the op. When the handle creation code sees this shared name, it generates a unique name internally and returns a fresh handle each time. PiperOrigin-RevId: 227932460 --- tensorflow/core/framework/resource_handle.cc | 3 ++ tensorflow/core/framework/resource_handle.h | 4 +++ tensorflow/core/framework/resource_mgr.cc | 12 ++++++- tensorflow/core/framework/resource_mgr.h | 33 ++++++++++++------- tensorflow/python/eager/context.py | 21 ++++++++++++ tensorflow/python/eager/def_function.py | 9 ++--- tensorflow/python/eager/memory_test.py | 11 +++++++ .../python/ops/resource_variable_ops.py | 6 ++-- 8 files changed, 81 insertions(+), 18 deletions(-) diff --git a/tensorflow/core/framework/resource_handle.cc b/tensorflow/core/framework/resource_handle.cc index fc3a329b3b..a3c48d63d4 100644 --- a/tensorflow/core/framework/resource_handle.cc +++ b/tensorflow/core/framework/resource_handle.cc @@ -19,6 +19,9 @@ limitations under the License. namespace tensorflow { +const char ResourceHandle::ANONYMOUS_NAME[] = + "cd2c89b7-88b7-44c8-ad83-06c2a9158347"; + ResourceHandle::ResourceHandle() {} ResourceHandle::ResourceHandle(const ResourceHandleProto& proto) { diff --git a/tensorflow/core/framework/resource_handle.h b/tensorflow/core/framework/resource_handle.h index db213669a3..c3beffb8ac 100644 --- a/tensorflow/core/framework/resource_handle.h +++ b/tensorflow/core/framework/resource_handle.h @@ -67,6 +67,10 @@ class ResourceHandle { string DebugString() const; + // GUID for anonymous resources. Resources with this shared_name will have + // their shared_name replaced with a GUID at creation time + static const char ANONYMOUS_NAME[]; + public: string device_; string container_; diff --git a/tensorflow/core/framework/resource_mgr.cc b/tensorflow/core/framework/resource_mgr.cc index 9f3204ab96..6a94ff6642 100644 --- a/tensorflow/core/framework/resource_mgr.cc +++ b/tensorflow/core/framework/resource_mgr.cc @@ -13,6 +13,8 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ +#include + #include "tensorflow/core/framework/resource_mgr.h" #include "tensorflow/core/framework/device_attributes.pb.h" @@ -26,6 +28,10 @@ limitations under the License. #include "tensorflow/core/platform/demangle.h" namespace tensorflow { + +// Used to generate unique names for anonymous variables +static std::atomic current_id_; + ResourceHandle MakeResourceHandle(OpKernelContext* ctx, const string& container, const string& name, const TypeIndex& type_index) { @@ -38,7 +44,11 @@ ResourceHandle MakeResourceHandle(OpKernelContext* ctx, const string& container, actual_container = ctx->resource_manager()->default_container(); } result.set_container(actual_container); - result.set_name(name); + if (name == ResourceHandle::ANONYMOUS_NAME) { + result.set_name(strings::StrCat("_AnonymousVar", current_id_.fetch_add(1))); + } else { + result.set_name(name); + } result.set_hash_code(type_index.hash_code()); result.set_maybe_type_name(type_index.name()); return result; diff --git a/tensorflow/core/framework/resource_mgr.h b/tensorflow/core/framework/resource_mgr.h index 3195cd2e9d..8a7c25da92 100644 --- a/tensorflow/core/framework/resource_mgr.h +++ b/tensorflow/core/framework/resource_mgr.h @@ -619,20 +619,31 @@ ResourceHandleOp::ResourceHandleOp(OpKernelConstruction* context) template void ResourceHandleOp::Compute(OpKernelContext* ctx) { - if (!initialized_.load()) { - mutex_lock ml(mutex_); - // Checking again to see if another thread has initialized the resource. + if (name_ == ResourceHandle::ANONYMOUS_NAME) { + AllocatorAttributes attr; + attr.set_on_host(true); + Tensor handle; + OP_REQUIRES_OK( + ctx, ctx->allocate_temp(DT_RESOURCE, TensorShape({}), &handle, attr)); + handle.scalar()() = + MakeResourceHandle(ctx, container_, name_); + ctx->set_output(0, handle); + } else { if (!initialized_.load()) { - AllocatorAttributes attr; - attr.set_on_host(true); - OP_REQUIRES_OK(ctx, ctx->allocate_temp(DT_RESOURCE, TensorShape({}), - &resource_, attr)); - resource_.scalar()() = - MakeResourceHandle(ctx, container_, name_); - initialized_.store(true); + mutex_lock ml(mutex_); + // Checking again to see if another thread has initialized the resource. + if (!initialized_.load()) { + AllocatorAttributes attr; + attr.set_on_host(true); + OP_REQUIRES_OK(ctx, ctx->allocate_temp(DT_RESOURCE, TensorShape({}), + &resource_, attr)); + resource_.scalar()() = + MakeResourceHandle(ctx, container_, name_); + initialized_.store(true); + } } + ctx->set_output(0, resource_); } - ctx->set_output(0, resource_); } template diff --git a/tensorflow/python/eager/context.py b/tensorflow/python/eager/context.py index cd43dc7ab2..faaf742c26 100644 --- a/tensorflow/python/eager/context.py +++ b/tensorflow/python/eager/context.py @@ -788,6 +788,27 @@ def in_eager_mode(): return executing_eagerly() +def shared_name(name=None): + """Returns the anonymous shared name GUID if no shared name is specified. + + In eager mode we need to use a unique shared name to avoid spurious sharing + issues. The runtime generates a unique name on our behalf when the reserved + GUID is used as a shared name. + + Args: + name: Optional shared name + + Returns: + Eager compatible shared name. + """ + if name or not executing_eagerly(): + return name + + # Ensure a unique name when eager execution is enabled to avoid spurious + # sharing issues. + return "cd2c89b7-88b7-44c8-ad83-06c2a9158347" + + def graph_mode(): """Context-manager to disable eager execution for the current thread.""" return context()._mode(GRAPH_MODE) # pylint: disable=protected-access diff --git a/tensorflow/python/eager/def_function.py b/tensorflow/python/eager/def_function.py index ebc47d1566..aa4f20df49 100644 --- a/tensorflow/python/eager/def_function.py +++ b/tensorflow/python/eager/def_function.py @@ -130,8 +130,9 @@ class UnliftedInitializerVariable(resource_variable_ops.ResourceVariable): if init_from_fn else [initial_value]) as name: # pylint: disable=protected-access with ops.init_scope(): - shared_name = ops._name_from_scope_name(name) - shared_name = "%s_%d" % (shared_name, ops.uid()) + handle_name = ops._name_from_scope_name(name) + unique_id = "%s_%d" % (handle_name, ops.uid()) + shared_name = context.shared_name(unique_id) with ops.name_scope("Initializer"), ops.device(None): initial_value = ops.convert_to_tensor( initial_value() if init_from_fn else initial_value, @@ -144,8 +145,8 @@ class UnliftedInitializerVariable(resource_variable_ops.ResourceVariable): name=name, graph_mode=self._in_graph_mode) self._shape = initial_value.shape - self._unique_id = shared_name - self._handle_name = shared_name + ":0" + self._unique_id = unique_id + self._handle_name = handle_name + ":0" self._dtype = initial_value.dtype.base_dtype self._constraint = constraint assert initial_value is not None diff --git a/tensorflow/python/eager/memory_test.py b/tensorflow/python/eager/memory_test.py index 5e4516239c..9d29180379 100644 --- a/tensorflow/python/eager/memory_test.py +++ b/tensorflow/python/eager/memory_test.py @@ -33,6 +33,7 @@ from tensorflow.python.eager import context from tensorflow.python.eager import test from tensorflow.python.framework import dtypes from tensorflow.python.ops import array_ops +from tensorflow.python.ops.variables import Variable # memory_profiler might not be available in the OSS version of TensorFlow. try: @@ -81,6 +82,16 @@ class MemoryTest(test.TestCase): "Maximum allowed increase: %f") % (initial, increase, increase_threshold_absolute_mb) + def testMemoryLeakAnonymousVariable(self): + if memory_profiler is None: + self.skipTest("memory_profiler required to run this test") + + def f(): + inputs = Variable(array_ops.zeros([32, 100], dtypes.float32)) + del inputs + + self.assertNotIncreasingMemory(f, num_iters=10000) + def testMemoryLeakInSimpleModelForwardOnly(self): if memory_profiler is None: self.skipTest("memory_profiler required to run this test") diff --git a/tensorflow/python/ops/resource_variable_ops.py b/tensorflow/python/ops/resource_variable_ops.py index d2e5dd9cfe..1d98fb2c89 100644 --- a/tensorflow/python/ops/resource_variable_ops.py +++ b/tensorflow/python/ops/resource_variable_ops.py @@ -403,10 +403,12 @@ class ResourceVariable(variables.VariableV1): handle_name = ops._name_from_scope_name(name) if self._in_graph_mode: shared_name = handle_name + unique_id = shared_name else: # When in eager mode use a uid for the shared_name, to prevent # accidental sharing. - shared_name = "%s_%d" % (handle_name, ops.uid()) + unique_id = "%s_%d" % (handle_name, ops.uid()) + shared_name = context.shared_name() # Use attr_scope and device(None) to simulate the behavior of # colocate_with when the variable we want to colocate with doesn't # yet exist. @@ -434,7 +436,7 @@ class ResourceVariable(variables.VariableV1): "variable inside a loop or conditional, use a lambda as the " "initializer." % name) # pylint: enable=protected-access - self._unique_id = shared_name + self._unique_id = unique_id self._initial_value = initial_value if self._in_graph_mode else None self._handle_name = handle_name + ":0" self._dtype = initial_value.dtype.base_dtype -- GitLab From 84bddea0109598e47fbe76ba1d443fdcb34c9bf4 Mon Sep 17 00:00:00 2001 From: Paul Donnelly Date: Fri, 4 Jan 2019 16:50:13 -0800 Subject: [PATCH 380/622] Improve Maxpool op to use cuDNN 7.3 improvements to cudnnPoolingForward() PiperOrigin-RevId: 227934668 --- tensorflow/core/kernels/maxpooling_op.cc | 8 ++ .../core/kernels/maxpooling_op_gpu.cu.cc | 1 - tensorflow/core/kernels/pooling_ops_common.cc | 83 +++++++++++++++---- tensorflow/stream_executor/cuda/cuda_dnn.cc | 25 ++++++ tensorflow/stream_executor/cuda/cuda_dnn.h | 8 ++ tensorflow/stream_executor/dnn.h | 11 +++ tensorflow/stream_executor/stream.cc | 22 +++++ tensorflow/stream_executor/stream.h | 7 ++ 8 files changed, 149 insertions(+), 16 deletions(-) diff --git a/tensorflow/core/kernels/maxpooling_op.cc b/tensorflow/core/kernels/maxpooling_op.cc index 507fc99837..ab235843f7 100644 --- a/tensorflow/core/kernels/maxpooling_op.cc +++ b/tensorflow/core/kernels/maxpooling_op.cc @@ -41,6 +41,7 @@ limitations under the License. #include "tensorflow/core/util/use_cudnn.h" #if GOOGLE_CUDA +#include "cuda/include/cudnn.h" #include "tensorflow/core/kernels/maxpooling_op_gpu.h" #include "tensorflow/core/kernels/pooling_ops_common_gpu.h" #include "tensorflow/core/platform/stream_executor.h" @@ -1134,11 +1135,18 @@ class MaxPoolingNoMaskOp : public OpKernel { errors::InvalidArgument( "qint8 should be used with data_format NCHW_VECT_C.")); +#if CUDNN_VERSION >= 7300 + if (use_dnn_) { + DnnPoolingOp::Compute(context, se::dnn::PoolingMode::kMaximum, ksize_, + stride_, padding_, data_format_, tensor_in, + out_shape, propagate_nans_); +#else // These is_int8x4 checks avoid linker errors for missing qint8 kernels. if (!is_int8x4 && use_dnn_ && data_format_ == FORMAT_NCHW) { DnnPoolingOp::Compute(context, se::dnn::PoolingMode::kMaximum, ksize_, stride_, padding_, data_format_, tensor_in, out_shape, propagate_nans_); +#endif } else { Tensor* output = nullptr; OP_REQUIRES_OK(context, context->allocate_output(0, out_shape, &output)); diff --git a/tensorflow/core/kernels/maxpooling_op_gpu.cu.cc b/tensorflow/core/kernels/maxpooling_op_gpu.cu.cc index 56d0340547..f28811ffa4 100644 --- a/tensorflow/core/kernels/maxpooling_op_gpu.cu.cc +++ b/tensorflow/core/kernels/maxpooling_op_gpu.cu.cc @@ -390,7 +390,6 @@ bool MaxPoolForwardNoMask_NCHW_VECT_C::operator()( 0, d.stream()>>>(output_size, bottom_data, height, width, channels, pooled_height, pooled_width, kernel_h, kernel_w, stride_h, stride_w, pad_t, pad_l, top_data); - d.synchronize(); return d.ok(); } diff --git a/tensorflow/core/kernels/pooling_ops_common.cc b/tensorflow/core/kernels/pooling_ops_common.cc index e583f7feb4..69122f467c 100644 --- a/tensorflow/core/kernels/pooling_ops_common.cc +++ b/tensorflow/core/kernels/pooling_ops_common.cc @@ -21,6 +21,7 @@ limitations under the License. #include "tensorflow/core/framework/tensor.h" #if GOOGLE_CUDA +#include "cuda/include/cudnn.h" #include "tensorflow/core/kernels/conv_2d.h" #include "tensorflow/core/kernels/pooling_ops_common_gpu.h" #include "tensorflow/core/platform/stream_executor.h" @@ -28,6 +29,20 @@ limitations under the License. namespace tensorflow { +namespace { + +template +struct RawType { + using type = T; +}; + +template <> +struct RawType { + using type = int8; +}; + +} // namespace + PoolParameters::PoolParameters(OpKernelContext* context, const std::vector& ksize, const std::vector& stride, @@ -156,7 +171,10 @@ void DnnPoolingOp::Compute(OpKernelContext* context, return; } - /// For now, cudnn does not support NHWC format, so we need to convert it + int batch_size = params.tensor_in_batch; + int depth = params.depth; +#if CUDNN_VERSION < 7300 + /// Earlier versions do not support NHWC format, so we need to convert it /// to NCHW before calling cudnn. We need to get rid of this once it is done Tensor transformed_input; if (data_format == FORMAT_NHWC) { @@ -181,7 +199,31 @@ void DnnPoolingOp::Compute(OpKernelContext* context, } else { transformed_output = *tensor_out; } - + se::dnn::DataLayout data_layout = se::dnn::DataLayout::kBatchDepthYX; +#else + auto& transformed_input = tensor_in; + auto& transformed_output = *tensor_out; + se::dnn::DataLayout data_layout; + switch (data_format) { + case FORMAT_NHWC: + data_layout = se::dnn::DataLayout::kBatchYXDepth; + break; + case FORMAT_NCHW: + data_layout = se::dnn::DataLayout::kBatchDepthYX; + break; + case FORMAT_NCHW_VECT_C: + // NCHW_VECT_C is not supported by cudnnPoolingForward(), but can be + // emulated via NHWC. + data_layout = se::dnn::DataLayout::kBatchYXDepth; + batch_size *= depth; + depth = 4; + break; + default: + OP_REQUIRES(context, false, + errors::InvalidArgument("Unsupported format: ", + ToString(data_format))); + } +#endif /// Get ready to call cudnn se::dnn::PoolingDescriptor pooling_desc; pooling_desc.set_pooling_mode(pooling_mode) @@ -194,23 +236,27 @@ void DnnPoolingOp::Compute(OpKernelContext* context, .set_propagate_nans(propagate_nans); se::dnn::BatchDescriptor input_desc; - input_desc.set_count(params.tensor_in_batch) + input_desc.set_count(batch_size) .set_height(params.tensor_in_rows) .set_width(params.tensor_in_cols) - .set_feature_map_count(params.depth) - .set_layout(se::dnn::DataLayout::kBatchDepthYX); + .set_feature_map_count(depth) + .set_layout(data_layout); se::dnn::BatchDescriptor output_desc; - output_desc.set_count(params.tensor_in_batch) + output_desc.set_count(batch_size) .set_height(params.out_height) .set_width(params.out_width) - .set_feature_map_count(params.depth) - .set_layout(se::dnn::DataLayout::kBatchDepthYX); + .set_feature_map_count(depth) + .set_layout(data_layout); + + auto input_data = + AsDeviceMemory(reinterpret_cast::type*>( + transformed_input.template flat().data()), + transformed_input.template flat().size()); - auto input_data = AsDeviceMemory(transformed_input.template flat().data(), - transformed_input.template flat().size()); auto output_data = - AsDeviceMemory(transformed_output.template flat().data(), + AsDeviceMemory(reinterpret_cast::type*>( + transformed_output.template flat().data()), transformed_output.template flat().size()); auto* stream = context->op_device_context()->stream(); @@ -222,15 +268,17 @@ void DnnPoolingOp::Compute(OpKernelContext* context, .ok(); OP_REQUIRES(context, status, errors::Internal("cudnn PoolForward launch failed")); - +#if CUDNN_VERSION < 7300 if (data_format == FORMAT_NHWC) { /// Transform the output data from NCHW back to NHWC auto toConstTensor = [](const Tensor& x) -> const Tensor { return x; }; - functor::NCHWToNHWC()( + using RT = typename RawType::type; + functor::NCHWToNHWC()( context->eigen_device(), - toConstTensor(transformed_output).template tensor(), - tensor_out->tensor()); + toConstTensor(transformed_output).template tensor(), + tensor_out->tensor()); } +#endif } template @@ -388,6 +436,11 @@ void DnnPoolingGradOp::Compute( template class DnnPoolingOp; \ template class DnnPoolingGradOp; TF_CALL_GPU_NUMBER_TYPES(DEFINE_DNN_OPS) + +#if CUDNN_VERSION >= 7300 +template class DnnPoolingOp; +#endif + #undef DEFINE_DNN_OPS #endif // GOOGLE_CUDA diff --git a/tensorflow/stream_executor/cuda/cuda_dnn.cc b/tensorflow/stream_executor/cuda/cuda_dnn.cc index 01ff248150..a34aa9354d 100644 --- a/tensorflow/stream_executor/cuda/cuda_dnn.cc +++ b/tensorflow/stream_executor/cuda/cuda_dnn.cc @@ -4207,6 +4207,31 @@ bool CudnnSupport::DoPoolForward( return IsStatusOk(status, /*report_error=*/true); } +bool CudnnSupport::DoPoolForward( + Stream* stream, const dnn::PoolingDescriptor& pooling_dimensions, + const dnn::BatchDescriptor& input_dimensions, + const DeviceMemory& input_data, + const dnn::BatchDescriptor& output_dimensions, + DeviceMemory* output_data, ScratchAllocator* workspace_allocator) { + // Alpha is the scaling factor for input. + float alpha = 1.0; + // Beta is the scaling factor for output. + float beta = 0.0; + + CudnnTensorDescriptor src_desc(input_dimensions, CUDNN_DATA_INT8); + CudnnTensorDescriptor dest_desc(output_dimensions, CUDNN_DATA_INT8); + CudnnPoolingDescriptor pooling_desc(pooling_dimensions); + + auto cudnn = cudnn_->GetHandle(parent_, stream); + auto status = [&] { + RETURN_IF_CUDNN_ERROR(cudnnPoolingForward( + cudnn.handle(), pooling_desc.handle(), &alpha, src_desc.handle(), + input_data.opaque(), &beta, dest_desc.handle(), output_data->opaque())); + return port::Status::OK(); + }(); + return IsStatusOk(status, /*report_error=*/true); +} + bool CudnnSupport::DoPoolBackward( Stream* stream, const dnn::PoolingDescriptor& pooling_dimensions, const dnn::BatchDescriptor& input_dimensions, diff --git a/tensorflow/stream_executor/cuda/cuda_dnn.h b/tensorflow/stream_executor/cuda/cuda_dnn.h index 044ed54514..4cce3c5626 100644 --- a/tensorflow/stream_executor/cuda/cuda_dnn.h +++ b/tensorflow/stream_executor/cuda/cuda_dnn.h @@ -540,6 +540,14 @@ class CudnnSupport : public dnn::DnnSupport { DeviceMemory* output_data, ScratchAllocator* workspace_allocator) override; + bool DoPoolForward(Stream* stream, + const dnn::PoolingDescriptor& pooling_dimensions, + const dnn::BatchDescriptor& input_dimensions, + const DeviceMemory& input_data, + const dnn::BatchDescriptor& output_dimensions, + DeviceMemory* output_data, + ScratchAllocator* workspace_allocator) override; + bool DoPoolBackward(Stream* stream, const dnn::PoolingDescriptor& pooling_dimensions, const dnn::BatchDescriptor& input_dimensions, diff --git a/tensorflow/stream_executor/dnn.h b/tensorflow/stream_executor/dnn.h index 1001824ed5..ff7b02cf6b 100644 --- a/tensorflow/stream_executor/dnn.h +++ b/tensorflow/stream_executor/dnn.h @@ -1617,6 +1617,17 @@ class DnnSupport { return false; } + virtual bool DoPoolForward(Stream* stream, + const dnn::PoolingDescriptor& pooling_dimensions, + const dnn::BatchDescriptor& input_dimensions, + const DeviceMemory& input_data, + const dnn::BatchDescriptor& output_dimensions, + DeviceMemory* output_data, + ScratchAllocator* workspace_allocator) { + LOG(FATAL) << "DoPoolForward not implemented for int8."; + return false; + } + // Performs differentiation of the pooling operation. virtual bool DoPoolBackward(Stream* stream, const dnn::PoolingDescriptor& pooling_dimensions, diff --git a/tensorflow/stream_executor/stream.cc b/tensorflow/stream_executor/stream.cc index 3edc66cde8..1b14e4c339 100644 --- a/tensorflow/stream_executor/stream.cc +++ b/tensorflow/stream_executor/stream.cc @@ -1490,6 +1490,28 @@ Stream &Stream::ThenPoolForward( return *this; } +Stream &Stream::ThenPoolForward( + const dnn::PoolingDescriptor &pooling_dimensions, + const dnn::BatchDescriptor &input_dimensions, + const DeviceMemory &input_data, + const dnn::BatchDescriptor &output_dimensions, + DeviceMemory *output_data, ScratchAllocator *workspace_allocator) { + VLOG_CALL(PARAM(pooling_dimensions), PARAM(input_dimensions), + PARAM(input_data), PARAM(output_dimensions), PARAM(output_data), + PARAM(workspace_allocator)); + + if (ok()) { + if (dnn::DnnSupport *dnn = parent_->AsDnn()) { + CheckError(dnn->DoPoolForward(this, pooling_dimensions, input_dimensions, + input_data, output_dimensions, output_data, + workspace_allocator)); + } else { + SetErrorAndLogNoDnnSupport(); + } + } + return *this; +} + Stream &Stream::ThenPoolBackward( const dnn::PoolingDescriptor &pooling_dimensions, const dnn::BatchDescriptor &input_dimensions, diff --git a/tensorflow/stream_executor/stream.h b/tensorflow/stream_executor/stream.h index 0fc90cf83d..d5e2fdf58d 100644 --- a/tensorflow/stream_executor/stream.h +++ b/tensorflow/stream_executor/stream.h @@ -650,6 +650,13 @@ class Stream { DeviceMemory *output_data, ScratchAllocator *workspace_allocator = nullptr); + Stream &ThenPoolForward(const dnn::PoolingDescriptor &pooling_dimensions, + const dnn::BatchDescriptor &input_dimensions, + const DeviceMemory &input_data, + const dnn::BatchDescriptor &output_dimensions, + DeviceMemory *output_data, + ScratchAllocator *workspace_allocator = nullptr); + Stream &ThenPoolBackward(const dnn::PoolingDescriptor &pooling_dimensions, const dnn::BatchDescriptor &input_dimensions, const DeviceMemory &input_data, -- GitLab From 75f2e9c266ec68d202a11ea5514898ccd1735d86 Mon Sep 17 00:00:00 2001 From: Allen Lavoie Date: Fri, 4 Jan 2019 16:51:01 -0800 Subject: [PATCH 381/622] Automated rollback of commit 7cc1ff9072ce4c2c7f36b7011e24486071ad13ee PiperOrigin-RevId: 227934767 --- tensorflow/python/framework/constant_op.py | 4 ---- tensorflow/python/kernel_tests/constant_op_test.py | 11 ----------- 2 files changed, 15 deletions(-) diff --git a/tensorflow/python/framework/constant_op.py b/tensorflow/python/framework/constant_op.py index 62a25f3fea..ade0797dcd 100644 --- a/tensorflow/python/framework/constant_op.py +++ b/tensorflow/python/framework/constant_op.py @@ -277,10 +277,6 @@ def _constant_impl( (num_t, shape, shape.num_elements())) g = ops.get_default_graph() tensor_value = attr_value_pb2.AttrValue() - if tensor_util.is_tensor(value): - raise ValueError( - ("Cannot create a tf.constant from symbolic Tensor %s. Did you mean " - "tf.convert_to_tensor?") % (value,)) tensor_value.tensor.CopyFrom( tensor_util.make_tensor_proto( value, dtype=dtype, shape=shape, verify_shape=verify_shape, diff --git a/tensorflow/python/kernel_tests/constant_op_test.py b/tensorflow/python/kernel_tests/constant_op_test.py index 2b743ed4c4..583082c2aa 100644 --- a/tensorflow/python/kernel_tests/constant_op_test.py +++ b/tensorflow/python/kernel_tests/constant_op_test.py @@ -24,7 +24,6 @@ from google.protobuf import text_format from tensorflow.core.framework import graph_pb2 from tensorflow.core.framework import tensor_pb2 -from tensorflow.python.eager import def_function from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes as dtypes_lib from tensorflow.python.framework import errors_impl @@ -631,16 +630,6 @@ class OnesTest(test.TestCase): self.assertEqual([2, 3], z.get_shape()) self.assertAllEqual(z.eval(), np.ones([2, 3])) - @test_util.run_in_graph_and_eager_modes - def testIteratedConstantSensibleException(self): - - @def_function.function - def constant_on_constant(x): - return constant_op.constant(x) - - with self.assertRaisesRegexp(ValueError, "convert_to_tensor"): - constant_on_constant(constant_op.constant(2)) - class OnesLikeTest(test.TestCase): -- GitLab From fe66882827806db63761b593c70c43340f40750e Mon Sep 17 00:00:00 2001 From: Martin Wicke Date: Fri, 4 Jan 2019 16:51:20 -0800 Subject: [PATCH 382/622] Refactor compatibility upgrade tool to use pasta (an AST based refactoring tool) instead of line based edits. This allows for some simplification (e.g., we can now remove arguments with a dict entry) and for much more powerful transformations. This passes all tests with no destructive test changes (though there are cosmetic test changes). Also adds transformations for tf.to_dtype -> tf.cast(..., dtype=...). PiperOrigin-RevId: 227934801 --- tensorflow/tools/compatibility/BUILD | 12 +- tensorflow/tools/compatibility/ast_edits.py | 668 +++++++++--------- .../tools/compatibility/ast_edits_test.py | 27 +- tensorflow/tools/compatibility/tf_upgrade.py | 26 +- .../tools/compatibility/tf_upgrade_test.py | 2 +- .../tools/compatibility/tf_upgrade_v2.py | 288 +++++--- .../tools/compatibility/tf_upgrade_v2_test.py | 105 +-- tensorflow/tools/pip_package/BUILD | 1 + tensorflow/workspace.bzl | 2 + third_party/pasta/BUILD | 1 + third_party/pasta/BUILD.bazel | 29 + third_party/pasta/BUILD.system | 13 + third_party/pasta/workspace.bzl | 16 + 13 files changed, 687 insertions(+), 503 deletions(-) create mode 100644 third_party/pasta/BUILD create mode 100644 third_party/pasta/BUILD.bazel create mode 100644 third_party/pasta/BUILD.system create mode 100644 third_party/pasta/workspace.bzl diff --git a/tensorflow/tools/compatibility/BUILD b/tensorflow/tools/compatibility/BUILD index a9902d77f5..452e6db941 100644 --- a/tensorflow/tools/compatibility/BUILD +++ b/tensorflow/tools/compatibility/BUILD @@ -1,17 +1,21 @@ -licenses(["notice"]) # Apache 2.0 - -package(default_visibility = ["//tensorflow:internal"]) - load( "//tensorflow:tensorflow.bzl", "tf_copts", # @unused "tf_cc_test", # @unused ) +licenses(["notice"]) # Apache 2.0 + +package(default_visibility = ["//tensorflow:internal"]) + py_library( name = "ast_edits", srcs = ["ast_edits.py"], srcs_version = "PY2AND3", + deps = [ + "@pasta", + "@six_archive//:six", + ], ) py_test( diff --git a/tensorflow/tools/compatibility/ast_edits.py b/tensorflow/tools/compatibility/ast_edits.py index 9106ec97c8..0ec6c2ac8c 100644 --- a/tensorflow/tools/compatibility/ast_edits.py +++ b/tensorflow/tools/compatibility/ast_edits.py @@ -19,7 +19,6 @@ from __future__ import division from __future__ import print_function import ast -import collections import os import re import shutil @@ -27,6 +26,9 @@ import sys import tempfile import traceback +import pasta +import six + # Some regular expressions we will need for parsing FIND_OPEN = re.compile(r"^\s*(\[).*$") FIND_STRING_CHARS = re.compile(r"['\"]") @@ -44,264 +46,294 @@ class APIChangeSpec(object): notifications) * `function_reorders`: maps functions whose argument order has changed to the list of arguments in the new order - * `function_handle`: maps function names to custom handlers for the function * `function_warnings`: maps full names of functions to warnings that will be printed out if the function is used. (e.g. tf.nn.convolution()) - * `unrestricted_function_warnings`: maps names of functions to warnings that - will be printed out when the function is used (e.g. foo.convolution()). - * `function_keyword_additions`: maps function names to a map of arg->value - names that should be passed to the function. + * `function_transformers`: maps function names to custom handlers For an example, see `TFAPIChangeSpec`. """ -class _FileEditTuple( - collections.namedtuple("_FileEditTuple", - ["comment", "line", "start", "old", "new"])): - """Each edit that is recorded by a _FileEditRecorder. +class _PastaEditVisitor(ast.NodeVisitor): + """AST Visitor that processes function calls. - Fields: - comment: A description of the edit and why it was made. - line: The line number in the file where the edit occurs (1-indexed). - start: The column number in the file where the edit occurs (0-indexed). - old: text string to remove (this must match what was in file). - new: text string to add in place of `old`. + Updates function calls from old API version to new API version using a given + change spec. """ - __slots__ = () + def __init__(self, api_change_spec): + self._api_change_spec = api_change_spec + self._log = [] # Holds 3-tuples: line, col, msg. + self._errors = [] # Same structure as _log. + self._stack = [] # Allow easy access to parents. + # Overridden to maintain a stack of nodes to allow for parent access + def visit(self, node): + self._stack.append(node) + super(_PastaEditVisitor, self).visit(node) + self._stack.pop() -class _FileEditRecorder(object): - """Record changes that need to be done to the file.""" + @property + def errors(self): + return self._errors - def __init__(self, filename): - # all edits are lists of chars - self._filename = filename + @property + def log(self): + return self._log - self._line_to_edit = collections.defaultdict(list) - self._errors = [] + def _format_log(self, log): + text = "" + for log_entry in log: + text += "Line %d:%d: %s\n" % log_entry + return text - def process(self, text): - """Process a list of strings, each corresponding to the recorded changes. + def log_text(self): + return self._format_log(self.log) - Args: - text: A list of lines of text (assumed to contain newlines) - Returns: - A tuple of the modified text and a textual description of what is done. - Raises: - ValueError: if substitution source location does not have expected text. - """ + def add_log(self, lineno, col, msg): + self._log.append((lineno, col, msg)) + print("Line %d:%d: %s" % (lineno, col, msg)) - change_report = "" - - # Iterate of each line - for line, edits in self._line_to_edit.items(): - offset = 0 - # sort by column so that edits are processed in order in order to make - # indexing adjustments cumulative for changes that change the string - # length - edits.sort(key=lambda x: x.start) - - # Extract each line to a list of characters, because mutable lists - # are editable, unlike immutable strings. - char_array = list(text[line - 1]) - - # Record a description of the change - change_report += "%r Line %d\n" % (self._filename, line) - change_report += "-" * 80 + "\n\n" - for e in edits: - change_report += "%s\n" % e.comment - change_report += "\n Old: %s" % (text[line - 1]) - - # Make underscore buffers for underlining where in the line the edit was - change_list = [" "] * len(text[line - 1]) - change_list_new = [" "] * len(text[line - 1]) - - # Iterate for each edit - for e in edits: - # Create effective start, end by accounting for change in length due - # to previous edits - start_eff = e.start + offset - end_eff = start_eff + len(e.old) - - # Make sure the edit is changing what it should be changing - old_actual = "".join(char_array[start_eff:end_eff]) - if old_actual != e.old: - raise ValueError("Expected text %r but got %r" % - ("".join(e.old), "".join(old_actual))) - # Make the edit - char_array[start_eff:end_eff] = list(e.new) - - # Create the underline highlighting of the before and after - change_list[e.start:e.start + len(e.old)] = "~" * len(e.old) - change_list_new[start_eff:end_eff] = "~" * len(e.new) - - # Keep track of how to generate effective ranges - offset += len(e.new) - len(e.old) - - # Finish the report comment - change_report += " %s\n" % "".join(change_list) - text[line - 1] = "".join(char_array) - change_report += " New: %s" % (text[line - 1]) - change_report += " %s\n\n" % "".join(change_list_new) - return "".join(text), change_report, self._errors - - def add(self, comment, line, start, old, new, error=None): - """Add a new change that is needed. + def add_error(self, lineno, col, msg): + # All errors are also added to the regular log. + self.add_log(lineno, col, msg) + self._errors.append((lineno, col, msg)) - Args: - comment: A description of what was changed - line: Line number (1 indexed) - start: Column offset (0 indexed) - old: old text - new: new text - error: this "edit" is something that cannot be fixed automatically - Returns: - None - """ - - self._line_to_edit[line].append( - _FileEditTuple(comment, line, start, old, new)) - if error: - self._errors.append("%s:%d: %s" % (self._filename, line, error)) - - -class _ASTCallVisitor(ast.NodeVisitor): - """AST Visitor that processes function calls. + def add_logs(self, logs): + """Record a log and print it. - Updates function calls from old API version to new API version using a given - change spec. - """ - - def __init__(self, filename, lines, api_change_spec): - self._filename = filename - self._file_edit = _FileEditRecorder(filename) - self._lines = lines - self._api_change_spec = api_change_spec + The log should be a tuple (lineno, col_offset, msg), which will be printed + and then recorded. It is part of the log available in the self.log property. - def process(self, lines): - return self._file_edit.process(lines) - - def generic_visit(self, node): - ast.NodeVisitor.generic_visit(self, node) - - def _rename_functions(self, node, full_name): - symbol_renames = self._api_change_spec.symbol_renames - try: - new_name = symbol_renames[full_name] - self._file_edit.add("Renamed function %r to %r" % (full_name, new_name), - node.lineno, node.col_offset, full_name, new_name) - except KeyError: - pass - - def _print_warning_for_function(self, node, full_name): - function_warnings = self._api_change_spec.function_warnings - try: - warning_message = function_warnings[full_name] - warning_message = warning_message.replace("", full_name) - self._file_edit.add(warning_message, - node.lineno, node.col_offset, full_name, full_name, - error="%s requires manual check." % full_name) - except KeyError: - pass + Args: + logs: The log to add. Must be a tuple (lineno, col_offset, msg). + """ + self._log.extend(logs) + for log in logs: + print("Line %d:%d: %s" % log) - def _print_warning_for_function_unrestricted(self, node): - """Print a warning when specific functions are called. + def add_errors(self, errors): + """Record an error and print it. - The function _print_warning_for_function matches the full name of the called - function, e.g., tf.foo.bar(). This function matches the function name that - is called, as long as the function is an attribute. For example, - `tf.foo.bar()` and `foo.bar()` are matched, but not `bar()`. + The error must be a tuple (lineno, col_offset, msg), which will be printed + and then recorded as both a log and an error. It is therefore part of the + log available in the self.log as well as the self.errors property. Args: - node: ast.Call object + errors: The log to add. Must be a tuple (lineno, col_offset, msg). """ - function_warnings = getattr( - self._api_change_spec, "unrestricted_function_warnings", {}) - if isinstance(node.func, ast.Attribute): - function_name = node.func.attr - try: - warning_message = function_warnings[function_name] - self._file_edit.add(warning_message, - node.lineno, node.col_offset, "", "", - error="%s requires manual check." % function_name) - except KeyError: - pass - - def _get_attribute_full_path(self, node): - """Traverse an attribute to generate a full name e.g. tf.foo.bar. + self.add_logs(errors) + self._errors.extend(errors) + + def _get_applicable_entries(self, transformer_field, full_name, name): + """Get all list entries indexed by name that apply to full_name or name.""" + # Transformers are indexed to full name, name, or no name + # as a performance optimization. + function_transformers = getattr(self._api_change_spec, + transformer_field, {}) + + glob_name = "*." + name if name else None + transformers = [] + if full_name in function_transformers: + transformers.append(function_transformers[full_name]) + if glob_name in function_transformers: + transformers.append(function_transformers[glob_name]) + if "*" in function_transformers: + transformers.append(function_transformers["*"]) + return transformers + + def _get_applicable_dict(self, transformer_field, full_name, name): + """Get all dict entries indexed by name that apply to full_name or name.""" + # Transformers are indexed to full name, name, or no name + # as a performance optimization. + function_transformers = getattr(self._api_change_spec, + transformer_field, {}) + + glob_name = "*." + name if name else None + transformers = function_transformers.get("*", {}).copy() + transformers.update(function_transformers.get(glob_name, {})) + transformers.update(function_transformers.get(full_name, {})) + return transformers + + def _get_full_name(self, node): + """Traverse an Attribute node to generate a full name, e.g., "tf.foo.bar". + + This is the inverse of _full_name_node. Args: node: A Node of type Attribute. Returns: - a '.'-delimited full-name or None if the tree was not a simple form. + a '.'-delimited full-name or None if node was not Attribute or Name. i.e. `foo()+b).bar` returns None, while `a.b.c` would return "a.b.c". """ curr = node items = [] while not isinstance(curr, ast.Name): if not isinstance(curr, ast.Attribute): - return None, None + return None items.append(curr.attr) curr = curr.value items.append(curr.id) - return ".".join(reversed(items)), items[0] + return ".".join(reversed(items)) - def _find_true_position(self, node): - """Return correct line number and column offset for a given node. + def _full_name_node(self, name, ctx=ast.Load()): + """Make an Attribute or Name node for name. - This is necessary mainly because ListComp's location reporting reports - the next token after the list comprehension list opening. + Translate a qualified name into nested Attribute nodes (and a Name node). + + Args: + name: The name to translate to a node. + ctx: What context this name is used in. Defaults to Load() Returns: - lineno, offset for the given node + A Name or Attribute node. + """ + names = name.split(".") + names.reverse() + node = ast.Name(id=names.pop(), ctx=ast.Load()) + while names: + node = ast.Attribute(value=node, attr=names.pop(), ctx=ast.Load()) + + # Change outermost ctx to the one given to us (inner ones should be Load). + node.ctx = ctx + return node + + def _maybe_add_warning(self, node, full_name): + """Adds an error to be printed about full_name at node.""" + function_warnings = self._api_change_spec.function_warnings + if full_name in function_warnings: + warning_message = function_warnings[full_name] + warning_message = warning_message.replace("", full_name) + self.add_error(node.lineno, node.col_offset, + "%s requires manual check: %s." % (full_name, + warning_message)) + return True + else: + return False + + def _maybe_add_call_warning(self, node, full_name, name): + """Print a warning when specific functions are called. + + The function _print_warning_for_function matches the full name of the called + function, e.g., tf.foo.bar(). This function matches the function name that + is called, as long as the function is an attribute. For example, + `tf.foo.bar()` and `foo.bar()` are matched, but not `bar()`. Args: - node: Node for which we wish to know the lineno and col_offset + node: ast.Call object + full_name: The precomputed full name of the callable, if one exists, None + otherwise. + name: The precomputed name of the callable, if one exists, None otherwise. + + Returns: + Whether an error was recorded. """ - if isinstance(node, ast.ListComp): - # Strangely, ast.ListComp returns the col_offset of the first token - # after the '[' token which appears to be a bug. Workaround by - # explicitly finding the real start of the list comprehension. - line = node.lineno - col = node.col_offset - # loop over lines - while 1: - # Reverse the text to and regular expression search for whitespace - text = self._lines[line - 1] - reversed_preceding_text = text[:col][::-1] - # First find if a [ can be found with only whitespace between it and - # col. - m = FIND_OPEN.match(reversed_preceding_text) - if m: - new_col_offset = col - m.start(1) - 1 - return line, new_col_offset + # Only look for *.-warnings here, the other will be handled by the Attribute + # visitor. Also, do not warn for bare functions, only if the call func is + # an attribute. + warned = False + if isinstance(node.func, ast.Attribute): + warned = self._maybe_add_warning(node, "*." + name) + + # All arg warnings are handled here, since only we have the args + arg_warnings = self._get_applicable_dict("function_arg_warnings", + full_name, name) + + used_args = [kw.arg for kw in node.keywords] + for arg, warning in arg_warnings.items(): + if arg in used_args: + warned = True + warning_message = warning.replace("", full_name or name) + self.add_error(node.lineno, node.col_offset, + "%s called with %s argument requires manual check: %s." % + (full_name or name, arg, warning_message)) + + return warned + + def _maybe_rename(self, parent, node, full_name): + """Replace node (Attribute or Name) with a node representing full_name.""" + new_name = self._api_change_spec.symbol_renames.get(full_name, None) + if new_name: + self.add_log(node.lineno, node.col_offset, + "Renamed %r to %r" % (full_name, new_name)) + new_node = self._full_name_node(new_name, node.ctx) + ast.copy_location(new_node, node) + pasta.ast_utils.replace_child(parent, node, new_node) + return True + else: + return False + + def _maybe_change_to_function_call(self, parent, node, full_name): + """Wraps node (typically, an Attribute or Expr) in a Call.""" + if full_name in self._api_change_spec.change_to_function: + if not isinstance(parent, ast.Call): + # ast.Call's constructor is really picky about how many arguments it + # wants, and also, it changed between Py2 and Py3. + if six.PY2: + new_node = ast.Call(node, [], [], None, None) else: - if (reversed_preceding_text == "" or - reversed_preceding_text.isspace()): - line = line - 1 - prev_line = self._lines[line - 1] - # TODO(aselle): - # this is poor comment detection, but it is good enough for - # cases where the comment does not contain string literal starting/ - # ending characters. If ast gave us start and end locations of the - # ast nodes rather than just start, we could use string literal - # node ranges to filter out spurious #'s that appear in string - # literals. - comment_start = prev_line.find("#") - if comment_start == -1: - col = len(prev_line) - 1 - elif FIND_STRING_CHARS.search(prev_line[comment_start:]) is None: - col = comment_start - else: - return None, None - else: - return None, None - # Most other nodes return proper locations (with notably does not), but - # it is not possible to use that in an argument. - return node.lineno, node.col_offset + new_node = ast.Call(node, [], []) + pasta.ast_utils.replace_child(parent, node, new_node) + ast.copy_location(new_node, node) + self.add_log(node.lineno, node.col_offset, + "Changed %r to a function call" % full_name) + return True + return False + + def _maybe_add_arg_names(self, node, full_name): + """Make args into keyword args if function called full_name requires it.""" + function_reorders = self._api_change_spec.function_reorders + + if full_name in function_reorders: + reordered = function_reorders[full_name] + new_keywords = [] + for idx, arg in enumerate(node.args): + keyword_arg = reordered[idx] + new_keywords.append(ast.keyword(arg=keyword_arg, value=arg)) + + if new_keywords: + self.add_log(node.lineno, node.col_offset, + "Added keywords to args of function %r" % full_name) + node.args = [] + node.keywords = new_keywords + (node.keywords or []) + return True + return False + + def _maybe_modify_args(self, node, full_name, name): + """Rename keyword args if the function called full_name requires it.""" + renamed_keywords = self._get_applicable_dict("function_keyword_renames", + full_name, name) + + if not renamed_keywords: + return False + + modified = False + new_keywords = [] + for keyword in node.keywords: + argkey = keyword.arg + if argkey in renamed_keywords: + modified = True + if renamed_keywords[argkey] is None: + lineno = getattr(keyword, "lineno", node.lineno) + col_offset = getattr(keyword, "col_offset", node.col_offset) + self.add_log(lineno, col_offset, + "Removed argument %s for function %s" % ( + argkey, full_name or name)) + else: + keyword.arg = renamed_keywords[argkey] + lineno = getattr(keyword, "lineno", node.lineno) + col_offset = getattr(keyword, "col_offset", node.col_offset) + self.add_log(lineno, col_offset, + "Renamed keyword argument for %s from %s to %s" % ( + full_name, argkey, renamed_keywords[argkey])) + new_keywords.append(keyword) + else: + new_keywords.append(keyword) + + if modified: + node.keywords = new_keywords + return modified def visit_Call(self, node): # pylint: disable=invalid-name """Handle visiting a call node in the AST. @@ -309,104 +341,74 @@ class _ASTCallVisitor(ast.NodeVisitor): Args: node: Current Node """ - self._print_warning_for_function_unrestricted(node) - - # Find a simple attribute name path e.g. "tf.foo.bar" - full_name, name = self._get_attribute_full_path(node.func) - - # Make sure the func is marked as being part of a call - node.func.is_function_for_call = True + assert self._stack[-1] is node + # Get the name for this call, so we can index stuff with it. + full_name = self._get_full_name(node.func) if full_name: - # Call special handlers - function_handles = self._api_change_spec.function_handle - glob_name = "*.{}".format(name) - if glob_name in function_handles: - function_handles[glob_name](self._file_edit, node, self._lines) - if full_name in function_handles: - function_handles[full_name](self._file_edit, node, self._lines) - - # Examine any non-keyword argument and make it into a keyword argument - # if reordering required. - function_reorders = self._api_change_spec.function_reorders - function_keyword_renames = ( - self._api_change_spec.function_keyword_renames) - - if full_name in function_reorders: - reordered = function_reorders[full_name] - for idx, arg in enumerate(node.args): - lineno, col_offset = self._find_true_position(arg) - if lineno is None or col_offset is None: - self._file_edit.add( - "Failed to add keyword %r to reordered function %r" % - (reordered[idx], full_name), - arg.lineno, - arg.col_offset, - "", - "", - error="A necessary keyword argument failed to be inserted.") - else: - keyword_arg = reordered[idx] - if (full_name in function_keyword_renames and - keyword_arg in function_keyword_renames[full_name]): - keyword_arg = function_keyword_renames[full_name][keyword_arg] - self._file_edit.add("Added keyword %r to reordered function %r" % - (reordered[idx], full_name), lineno, col_offset, - "", keyword_arg + "=") - - # Examine each keyword argument and convert it to the final renamed form - renamed_keywords = ({} if full_name not in function_keyword_renames else - function_keyword_renames[full_name]) - for keyword in node.keywords: - argkey = keyword.arg - argval = keyword.value - - if argkey in renamed_keywords: - argval_lineno, argval_col_offset = self._find_true_position(argval) - if argval_lineno is not None and argval_col_offset is not None: - # TODO(aselle): We should scan backward to find the start of the - # keyword key. Unfortunately ast does not give you the location of - # keyword keys, so we are forced to infer it from the keyword arg - # value. - key_start = argval_col_offset - len(argkey) - 1 - key_end = key_start + len(argkey) + 1 - if (self._lines[argval_lineno - 1][key_start:key_end] == argkey + - "="): - self._file_edit.add("Renamed keyword argument from %r to %r" % - (argkey, - renamed_keywords[argkey]), argval_lineno, - argval_col_offset - len(argkey) - 1, - argkey + "=", renamed_keywords[argkey] + "=") - continue - self._file_edit.add( - "Failed to rename keyword argument from %r to %r" % - (argkey, renamed_keywords[argkey]), - argval.lineno, - argval.col_offset - len(argkey) - 1, - "", - "", - error="Failed to find keyword lexographically. Fix manually.") - - ast.NodeVisitor.generic_visit(self, node) + name = full_name.split(".")[-1] + elif isinstance(node.func, ast.Name): + name = node.func.id + elif isinstance(node.func, ast.Attribute): + name = node.func.attr + else: + name = None + + # Call standard transformers for this node. + # Make sure warnings come first, since args or names triggering warnings + # may be removed by the other transformations. + self._maybe_add_call_warning(node, full_name, name) + # Make all args into kwargs + self._maybe_add_arg_names(node, full_name) + # Argument name changes or deletions + self._maybe_modify_args(node, full_name, name) + + # Call transformers. These have the ability to modify the node, and if they + # do, will return the new node they created (or the same node if they just + # changed it). The are given the parent, but we will take care of + # integrating their changes into the parent if they return a new node. + # + # These are matched on the old name, since renaming is performed by the + # Attribute visitor, which happens later. + transformers = self._get_applicable_entries("function_transformers", + full_name, name) + + parent = self._stack[-2] + + for transformer in transformers: + logs = [] + errors = [] + new_node = transformer(parent, node, full_name, name, logs, errors) + self.add_logs(logs) + self.add_errors(errors) + if new_node: + if new_node is not node: + pasta.ast_utils.replace_child(parent, node, new_node) + node = new_node + self._stack[-1] = node + + self.generic_visit(node) def visit_Attribute(self, node): # pylint: disable=invalid-name - """Handle bare Attributes i.e. [tf.foo, tf.bar]. + """Handle bare Attributes i.e. [tf.foo, tf.bar].""" + assert self._stack[-1] is node - Args: - node: Node that is of type ast.Attribute - """ - full_name, _ = self._get_attribute_full_path(node) + full_name = self._get_full_name(node) if full_name: + parent = self._stack[-2] + # Make sure the warning comes first, otherwise the name may have changed - self._print_warning_for_function(node, full_name) - self._rename_functions(node, full_name) - if full_name in self._api_change_spec.change_to_function: - if not hasattr(node, "is_function_for_call"): - new_text = full_name + "()" - self._file_edit.add("Changed %r to %r" % (full_name, new_text), - node.lineno, node.col_offset, full_name, new_text) + self._maybe_add_warning(node, full_name) - ast.NodeVisitor.generic_visit(self, node) + # Once we did a modification, node is invalid and not worth inspecting + # further. Also, we only perform modifications for simple nodes, so + # There'd be no point in descending further. + if self._maybe_rename(parent, node, full_name): + return + if self._maybe_change_to_function_call(parent, node, full_name): + return + + self.generic_visit(node) class ASTCodeUpgrader(object): @@ -429,16 +431,42 @@ class ASTCodeUpgrader(object): """ # Write to a temporary file, just in case we are doing an implace modify. + # pylint: disable=g-backslash-continuation with open(in_filename, "r") as in_file, \ tempfile.NamedTemporaryFile("w", delete=False) as temp_file: ret = self.process_opened_file(in_filename, in_file, out_filename, temp_file) + # pylint: enable=g-backslash-continuation shutil.move(temp_file.name, out_filename) return ret - # Broad exceptions are required here because ast throws whatever it wants. - # pylint: disable=broad-except + def _format_errors(self, errors, in_filename): + return ["%s:%d:%d: %s" % ((in_filename,) + error) for error in errors] + + def update_string_pasta(self, text, in_filename): + """Updates a file using pasta.""" + try: + t = pasta.parse(text) + except (SyntaxError, ValueError, TypeError): + log = "Failed to parse.\n\n" + traceback.format_exc() + return 0, "", log, [] + + visitor = _PastaEditVisitor(self._api_change_spec) + visitor.visit(t) + + errors = self._format_errors(visitor.errors, in_filename) + return 1, pasta.dump(t), visitor.log_text(), errors + + def _format_log(self, log, in_filename, out_filename): + text = "-" * 80 + "\n" + text += "Processing file %r\n outputting to %r\n" % (in_filename, + out_filename) + text += "-" * 80 + "\n\n" + text += log + text += "-" * 80 + "\n\n" + return text + def process_opened_file(self, in_filename, in_file, out_filename, out_file): """Process the given python file for incompatible changes. @@ -453,30 +481,16 @@ class ASTCodeUpgrader(object): Returns: A tuple representing number of files processed, log of actions, errors """ - process_errors = [] - text = "-" * 80 + "\n" - text += "Processing file %r\n outputting to %r\n" % (in_filename, - out_filename) - text += "-" * 80 + "\n\n" - - parsed_ast = None lines = in_file.readlines() - try: - parsed_ast = ast.parse("".join(lines)) - except Exception: - text += "Failed to parse %r\n\n" % in_filename - text += traceback.format_exc() - if parsed_ast: - visitor = _ASTCallVisitor(in_filename, lines, self._api_change_spec) - visitor.visit(parsed_ast) - out_text, new_text, process_errors = visitor.process(lines) - text += new_text - if out_file: - out_file.write(out_text) - text += "\n" - return 1, text, process_errors - - # pylint: enable=broad-except + processed_file, new_file_content, log, process_errors = ( + self.update_string_pasta("".join(lines), in_filename)) + + if out_file and processed_file: + out_file.write(new_file_content) + + return (processed_file, + self._format_log(log, in_filename, out_filename), + process_errors) def process_tree(self, root_directory, output_root_directory, copy_other_files): diff --git a/tensorflow/tools/compatibility/ast_edits_test.py b/tensorflow/tools/compatibility/ast_edits_test.py index 99f20a026f..b2e9f67638 100644 --- a/tensorflow/tools/compatibility/ast_edits_test.py +++ b/tensorflow/tools/compatibility/ast_edits_test.py @@ -39,6 +39,8 @@ following new APIs: from __future__ import absolute_import from __future__ import division from __future__ import print_function +import ast +import pasta import six from tensorflow.python.framework import test_util from tensorflow.python.platform import test as test_lib @@ -54,7 +56,6 @@ class NoUpdateSpec(ast_edits.APIChangeSpec): self.function_keyword_renames = {} self.symbol_renames = {} self.function_warnings = {} - self.unrestricted_function_warnings = {} self.change_to_function = {} @@ -401,7 +402,8 @@ class TestAstEdits(test_util.TensorFlowTestCase): def __init__(self): NoUpdateSpec.__init__(self) - self.unrestricted_function_warnings = {"foo": "not good"} + self.function_warnings = {"*.foo": "not good"} + texts = ["object.foo()", "get_object().foo()", "get_object().foo()", "object.foo().bar()"] for text in texts: @@ -416,5 +418,26 @@ class TestAstEdits(test_util.TensorFlowTestCase): self.assertNotIn("not good", report) +class ManualEditsTest(test_util.TensorFlowTestCase): + + def disabled_test_arg_order(self): + """Tests that generated arg order is sane.""" + text = "f(a)" + t = pasta.parse(text) + node = pasta.ast_utils.find_nodes_by_type(t, (ast.Call,))[0] + arg = ast.keyword(arg="b", value=ast.Num(n=0)) + node.keywords.append(arg) + + # This is only needed in Python3, and I think it's a bug (but maybe in ast). + arg.value.lineno = 0 + arg.value.col_offset = 0 + + # pasta.dump should never put kwargs before args, even if the col_offset is + # messed up. + # This fails if run with python3, but works find for python2. + # In python3, the dump yields "f(b=0, a)". + self.assertEqual(pasta.dump(t), "f(a, b=0)") + + if __name__ == "__main__": test_lib.main() diff --git a/tensorflow/tools/compatibility/tf_upgrade.py b/tensorflow/tools/compatibility/tf_upgrade.py index 68d2c02570..241b08510f 100644 --- a/tensorflow/tools/compatibility/tf_upgrade.py +++ b/tensorflow/tools/compatibility/tf_upgrade.py @@ -175,27 +175,13 @@ class TFAPIChangeSpec(ast_edits.APIChangeSpec): "tf.op_scope": ["values", "name", "default_name"], } - # Specially handled functions. - self.function_handle = {"tf.reverse": self._reverse_handler} - # Warnings that should be printed if corresponding functions are used. - self.function_warnings = {} - - @staticmethod - def _reverse_handler(file_edit_recorder, node, lines): - del lines - # TODO(aselle): Could check for a literal list of bools and try to convert - # them to indices. - comment = ("ERROR: tf.reverse has had its argument semantics changed " - "significantly the converter cannot detect this reliably, so " - "you need to inspect this usage manually.\n") - file_edit_recorder.add( - comment, - node.lineno, - node.col_offset, - "tf.reverse", - "tf.reverse", - error="tf.reverse requires manual check.") + self.function_warnings = { + "tf.reverse": + "ERROR: tf.reverse has had its argument semantics changed " + "significantly. The converter cannot detect this reliably, so " + "you need to inspect this usage manually.\n", + } if __name__ == "__main__": diff --git a/tensorflow/tools/compatibility/tf_upgrade_test.py b/tensorflow/tools/compatibility/tf_upgrade_test.py index 66325ea2ad..cf05575a9d 100644 --- a/tensorflow/tools/compatibility/tf_upgrade_test.py +++ b/tensorflow/tools/compatibility/tf_upgrade_test.py @@ -112,7 +112,7 @@ class TestUpgrade(test_util.TensorFlowTestCase): text = "tf.reverse(a, b)\n" _, unused_report, errors, new_text = self._upgrade(text) self.assertEqual(new_text, new_text) - self.assertEqual(errors, ["test.py:1: tf.reverse requires manual check."]) + self.assertIn("tf.reverse requires manual check", errors[0]) def testListComprehension(self): def _test(input, output): # pylint: disable=redefined-builtin diff --git a/tensorflow/tools/compatibility/tf_upgrade_v2.py b/tensorflow/tools/compatibility/tf_upgrade_v2.py index 82839afc3d..9d9c5878f7 100644 --- a/tensorflow/tools/compatibility/tf_upgrade_v2.py +++ b/tensorflow/tools/compatibility/tf_upgrade_v2.py @@ -18,7 +18,9 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -import re +import ast + +import pasta from tensorflow.tools.compatibility import ast_edits from tensorflow.tools.compatibility import renames_v2 @@ -31,7 +33,22 @@ class TFAPIChangeSpec(ast_edits.APIChangeSpec): def __init__(self): # Maps from a function name to a dictionary that describes how to # map from an old argument keyword to the new argument keyword. + # If the new argument is None, it will be removed. + # Only keyword args are handled, so make sure to also put any function in + # function_reorders to ensure that all args are made into keywords first. self.function_keyword_renames = { + "tf.gradients": { + "colocate_gradients_with_ops": None, + }, + "tf.hessians": { + "colocate_gradients_with_ops": None, + }, + "*.minimize": { + "colocate_gradients_with_ops": None, + }, + "*.compute_gradients": { + "colocate_gradients_with_ops": None, + }, "tf.argmin": { "dimension": "axis", }, @@ -672,14 +689,31 @@ class TFAPIChangeSpec(ast_edits.APIChangeSpec): # positional arguments yourself, this could do the wrong thing. self.function_reorders = reorders_v2.reorders - # Specially handled functions. - self.function_handle = { - "tf.batch_gather": self._batch_gather_handler, - "tf.nn.dropout": self._dropout_handler, - "tf.gradients": self._colocate_handler("tf.gradients"), - "*.minimize": self._colocate_handler("Optimizer.minimize"), - "*.compute_gradients": - self._colocate_handler("Optimizer.compute_gradients"), + # Specially handled functions (pasta version) + # Each transformer is a callable which will be called with the arguments + # transformer(parent, node, full_name, name, logs, errors) + # Where logs and errors are lists to which (line, col, msg) tuples can be + # appended, full_name is the FQN of the function called (or None if that is + # unknown), name is the name of the function called (or None is that is + # unknown). node is an ast.Call node representing this function call, and + # parent is its parent in the AST. + # The function may modify node (but not parent), and must return + # - none, if nothing was modified + # - node, if node was modified in place (make sure to use + # pasta.ast_utils.replace_child to swap out children, otherwise formatting + # may get messy) + # - a replacement for node, if the whole call node was replaced. The caller + # will take care of changing parent. + self.function_transformers = { + "tf.nn.dropout": self._dropout_transformer, + "tf.batch_gather": self._batch_gather_transformer, + "tf.to_bfloat16": self._cast_transformer, + "tf.to_complex128": self._cast_transformer, + "tf.to_complex64": self._cast_transformer, + "tf.to_double": self._cast_transformer, + "tf.to_float": self._cast_transformer, + "tf.to_int32": self._cast_transformer, + "tf.to_int64": self._cast_transformer, } decay_function_comment = ( @@ -748,9 +782,45 @@ class TFAPIChangeSpec(ast_edits.APIChangeSpec): "compat.v1 for backward compatibility. Please update these calls to " "the TF 2.0 versions.") + export_saved_model_renamed = ( + "(Manual edit required) Please rename the method export_savedmodel() " + "to export_saved_model(). Two things to note:\n\t(1) The argument " + "strip_default_attributes has been removed. The function will always " + "strip the default attributes from ops. If this breaks your code, " + "please switch to tf.compat.v1.estimator.Estimator.\n\t(2) This change " + "only effects core estimator. If you are using " + "tf.contrib.learn.Estimator, please switch to using core estimator.") + + make_initializable_iterator_deprecation = ( + "(Manual edit required) The " + "`tf.data.Dataset.make_initializable_iterator()` method has been " + "removed. If you are using the Estimator API, you can return a dataset " + "directly from your input functions without creating an iterator. " + "As a last resort, please replace calls to that method on `dataset` " + "with a call to " + "`tf.compat.v1.data.make_initializable_iterator(dataset)`.") + + make_one_shot_iterator_deprecation = ( + "(Manual edit required) The " + "`tf.data.Dataset.make_one_shot_iterator()` method has been " + "removed. If you are using eager execution, you can iterate over " + "`dataset` using a Python `for` loop. If you are using the Estimator " + "API, you can return a dataset directly from your input functions " + "without creating an iterator. As a last resort, please replace calls " + "to that method on `dataset` with a call to " + "`tf.compat.v1.data.make_one_shot_iterator(dataset)`.") + # Function warnings. placeholder inside warnings will be # replaced by function name. + # You can use *. to add items which do not check the FQN, and apply to e.g., + # methods. self.function_warnings = { + "*.export_savedmodel": + export_saved_model_renamed, + "*.make_initializable_iterator": + make_initializable_iterator_deprecation, + "*.make_one_shot_iterator": + make_one_shot_iterator_deprecation, "tf.assert_greater": assert_return_type_comment, "tf.assert_equal": @@ -834,11 +904,6 @@ class TFAPIChangeSpec(ast_edits.APIChangeSpec): default_loss_reduction_changed, "tf.estimator.BaselineRegressor": default_loss_reduction_changed, - "tf.hessians": - "tf.hessians no longer takes " - "'colocate_gradients_with_ops' argument. Also, " - "arguments have been reordered so that 'name' is the " - "last argument.", "tf.nn.conv1d": "WARNING: use_cudnn_on_gpu argument has been removed and \"value\"" " was renamed to \"input\"", @@ -1064,111 +1129,118 @@ class TFAPIChangeSpec(ast_edits.APIChangeSpec): metrics_comment, } + # Warnings that are emitted only if a specific arg is found. + self.function_arg_warnings = { + "tf.gradients": { + "colocate_gradients_with_ops": + "tf.gradients no longer takes " + "'colocate_gradients_with_ops' argument, it behaves as if it " + "was set to True.", + }, + "*.minimize": { + "colocate_gradients_with_ops": + "Optimizer.minimize no longer takes " + "'colocate_gradients_with_ops' argument, it behaves as if it " + "was set to True.", + }, + "*.compute_gradients": { + "colocate_gradients_with_ops": + "Optimizer.compute_gradients no " + "longer takes 'colocate_gradients_with_ops' argument, it " + "behaves as if it was set to True.", + }, + } + self.symbol_renames = { name: new_name for name, new_name in self.symbol_renames.items() } - export_saved_model_renamed = ( - "(Manual edit required) Please rename the method export_savedmodel() " - "to export_saved_model(). Two things to note:\n\t(1) The argument " - "strip_default_attributes has been removed. The function will always " - "strip the default attributes from ops. If this breaks your code, " - "please switch to tf.compat.v1.estimator.Estimator.\n\t(2) This change " - "only effects core estimator. If you are using " - "tf.contrib.learn.Estimator, please switch to using core estimator.") - - make_initializable_iterator_deprecation = ( - "(Manual edit required) The " - "`tf.data.Dataset.make_initializable_iterator()` method has been " - "removed. If you are using the Estimator API, you can return a dataset " - "directly from your input functions without creating an iterator. " - "As a last resort, please replace calls to that method on `dataset` " - "with a call to " - "`tf.compat.v1.data.make_initializable_iterator(dataset)`.") - - make_one_shot_iterator_deprecation = ( - "(Manual edit required) The " - "`tf.data.Dataset.make_one_shot_iterator()` method has been " - "removed. If you are using eager execution, you can iterate over " - "`dataset` using a Python `for` loop. If you are using the Estimator " - "API, you can return a dataset directly from your input functions " - "without creating an iterator. As a last resort, please replace calls " - "to that method on `dataset` with a call to " - "`tf.compat.v1.data.make_one_shot_iterator(dataset)`.") + @staticmethod + def _dropout_transformer(parent, node, full_name, name, logs, errors): + def _replace_keep_prob_node(parent, old_value): + """Replaces old_value with 1-(old_value).""" + one = ast.Num(n=1) + one.lineno = 0 + one.col_offset = 0 + new_value = ast.BinOp(left=one, op=ast.Sub(), + right=old_value) + # This copies the prefix and suffix on old_value to new_value. + pasta.ast_utils.replace_child(parent, old_value, new_value) + ast.copy_location(new_value, old_value) + # Put parentheses around keep_prob.value (and remove the old prefix/ + # suffix, they should only be around new_value). + pasta.base.formatting.set(old_value, "prefix", "(") + pasta.base.formatting.set(old_value, "suffix", ")") - # Specify warnings for functions that aren't restricted to the tf.x.y.z - # format. This should only be used for methods with unique names, e.g. - # export_savedmodel, which is only defined in Estimator objects. - self.unrestricted_function_warnings = { - "export_savedmodel": export_saved_model_renamed, - "make_initializable_iterator": make_initializable_iterator_deprecation, - "make_one_shot_iterator": make_one_shot_iterator_deprecation, - } + # Check if we have a keep_prob keyword arg + for keep_prob in node.keywords: + if keep_prob.arg == "keep_prob": + logs.append((node.lineno, node.col_offset, + "Changing keep_prob arg of tf.nn.dropout to rate, and " + "recomputing value. Please check this transformation.\n")) + keep_prob.arg = "rate" + _replace_keep_prob_node(keep_prob, keep_prob.value) + return node - @staticmethod - def _dropout_handler(file_edit_recorder, node, lines): - del lines + # Maybe it was a positional arg if len(node.args) < 2: - comment = ("ERROR: tf.nn.dropout did not take arguments, so automatic " - "transformation was disabled. tf.nn.dropout has changed " - "the semantics of the second argument.") - file_edit_recorder.add( - comment, - node.lineno, - node.col_offset, - "tf.nn.dropout", - "tf.nn.dropout", - error="tf.nn.dropout requires manual check.") + errors.append((node.lineno, node.col_offset, + "ERROR: tf.nn.dropout called without arguments, so " + "automatic fix was disabled. tf.nn.dropout has changed " + "the semantics of the second argument.")) else: - comment = ("WARNING: tf.nn.dropout has changed the semantics of the " - "second argument. Please check the transformation.\n") - file_edit_recorder.add( - comment, - node.args[1].lineno, - node.args[1].col_offset, - "", - "1 - ") + _replace_keep_prob_node(node, node.args[1]) + logs.append((node.lineno, node.col_offset, + "Changing keep_prob arg of tf.nn.dropout to rate, and " + "recomputing value.\n")) + errors.append((node.lineno, node.col_offset, + "WARNING: tf.nn.dropout has changed the semantics of the " + "second argument. Please check the applied transformation." + )) + return node @staticmethod - def _colocate_handler(name): - def _helper(file_edit_recorder, node, lines): - """Handler for updating colocate arguments.""" - del lines - for keyword in node.keywords: - if keyword.arg == "colocate_gradients_with_ops": - # TODO(jhseu): Since ast_edit.py does string replacement, there's no - # straightforward way to remove the argument. Try to fix before 2.0 is - # final. - comment = ("For tf.gradients and tf.Optimizer.minimize, " - "colocate_gradients_with_op has been removed and now " - "defaults to True.") - file_edit_recorder.add( - comment, - node.lineno, - node.col_offset, - "", - "", - error="{} requires manual check.".format(name)) - return _helper + def _cast_transformer(parent, node, full_name, name, logs, errors): + """Transforms to_int and to_float to cast(..., dtype=...).""" - @staticmethod - def _batch_gather_handler(file_edit_recorder, node, lines): - lineno = node.lineno - column = node.col_offset + # Find out the dtype to cast to from the function name + dtype_str = name[3:] + new_arg = ast.keyword(arg="dtype", + value=ast.Attribute(value=ast.Name(id="tf", + ctx=ast.Load()), + attr=dtype_str, ctx=ast.Load())) + + # Python3 ast requires the args for the Attribute, but codegen will mess up + # the arg order if we just set them to 0. + new_arg.value.lineno = node.lineno + new_arg.value.col_offset = node.col_offset+100 - # Find the position to add the batch_dims argument. We add it as the - # first argument, since that's easiest. This is safe because we included - # batch_gather in self.reordered_function_names, so it will have all - # of its arguments changed to keyword arguments. - m = re.match(r"tf\s*\.\s*batch_gather\s*\(", lines[lineno - 1][column:]) - if m is not None: - file_edit_recorder.add( - "Added keyword argument 'batch_dims=-1' to 'tf.batch_gather'", - lineno, column + m.end(), "", "batch_dims=-1, ") + node.keywords.append(new_arg) + if isinstance(node.func, ast.Attribute): + node.func.attr = "cast" else: - file_edit_recorder.add( - "Unable to add keyword argument 'batch_dims=-1' to 'tf.batch_gather'", - lineno, column, "", "", - error="Unable to add keyword argument batch_dims=-1 to " - "tf.batch_gather; please add it manually.") + assert isinstance(node.func, ast.Name) + node.func.id = "cast" + + logs.append((node.lineno, node.col_offset, + "Changed %s call to tf.cast(..., dtype=tf.%s)." % (full_name, + dtype_str))) + return node + + @staticmethod + def _batch_gather_transformer(parent, node, full_name, name, logs, errors): + # Check if the call already has a batch_dims argument + if any([kw.arg == "batch_dims" for kw in node.keywords]): + logs.append((node.lineno, node.col_offset, "tf.batch_gather already has " + "batch_dims argument. Neat.")) + return None + + minus_one = ast.Num(n=-1) + minus_one.lineno = 0 + minus_one.col_offset = 0 + new_arg = ast.keyword("batch_dims", minus_one) + node.keywords.append(new_arg) + logs.append((node.lineno, node.col_offset, + "Added keyword argument batch_dims=-1 to tf.batch_gather.")) + return node diff --git a/tensorflow/tools/compatibility/tf_upgrade_v2_test.py b/tensorflow/tools/compatibility/tf_upgrade_v2_test.py index 54120cfdd7..d8bc0ba75a 100644 --- a/tensorflow/tools/compatibility/tf_upgrade_v2_test.py +++ b/tensorflow/tools/compatibility/tf_upgrade_v2_test.py @@ -239,8 +239,8 @@ class TestUpgrade(test_util.TensorFlowTestCase): } function_warnings = ( tf_upgrade_v2.TFAPIChangeSpec().function_warnings) - function_handles = ( - tf_upgrade_v2.TFAPIChangeSpec().function_handle) + function_transformers = ( + tf_upgrade_v2.TFAPIChangeSpec().function_transformers) keyword_renames = ( tf_upgrade_v2.TFAPIChangeSpec().function_keyword_renames) @@ -255,7 +255,7 @@ class TestUpgrade(test_util.TensorFlowTestCase): for name in names_v1: tf_name = "tf.%s" % name - if tf_name in function_warnings or tf_name in function_handles: + if tf_name in function_warnings or tf_name in function_transformers: continue # These require manual change if tf_name in v1_name_exceptions: continue @@ -362,15 +362,14 @@ bazel-bin/tensorflow/tools/compatibility/update/generate_v2_reorders_map text = "%s(a, b)\n" % decay _, report, errors, _ = self._upgrade(text) - self.assertEqual(errors, ["test.py:1: %s requires manual check." % decay]) + self.assertIn("%s requires manual check" % decay, errors[0]) self.assertIn("%s has been changed" % decay, report) def testPiecewiseDecay(self): text = "tf.train.piecewise_constant_decay(a, b)\n" _, report, errors, _ = self._upgrade(text) - self.assertEqual( - errors, - ["test.py:1: tf.train.piecewise_constant_decay requires manual check."]) + self.assertIn("tf.train.piecewise_constant_decay requires manual check", + errors[0]) self.assertIn("tf.train.piecewise_constant_decay has been changed", report) def testMetrics(self): @@ -414,7 +413,7 @@ bazel-bin/tensorflow/tools/compatibility/update/generate_v2_reorders_map text = ns + "(a, b)" _, report, errors, new_text = self._upgrade(text) self.assertEqual("tf.compat.v1.metrics." + m + "(a, b)", new_text) - self.assertEqual(errors, ["test.py:1: %s requires manual check." % ns]) + self.assertIn("test.py:1:0: %s requires manual check" % ns, errors[0]) self.assertIn( "WARNING: tf.metrics have been converted to object oriented" " versions in TF 2.0 and after. The metric function calls have been " @@ -445,7 +444,7 @@ bazel-bin/tensorflow/tools/compatibility/update/generate_v2_reorders_map text = ns + "(a, b)" _, report, errors, new_text = self._upgrade(text) self.assertEqual("tf.compat.v1.losses." + l + "(a, b)", new_text) - self.assertEqual(errors, ["test.py:1: %s requires manual check." % ns]) + self.assertIn("test.py:1:0: %s requires manual check" % ns, errors[0]) self.assertIn( "WARNING: tf.losses have been converted to object oriented" " versions in TF 2.0 and after. The loss function calls have been " @@ -463,7 +462,7 @@ bazel-bin/tensorflow/tools/compatibility/update/generate_v2_reorders_map text = ns + "(a, b)" _, report, errors, new_text = self._upgrade(text) self.assertEqual(text, new_text) - self.assertEqual(errors, ["test.py:1: %s requires manual check." % ns]) + self.assertIn("%s requires manual check" % ns, errors[0]) self.assertIn("loss_reduction has been changed", report) def testDropout(self): @@ -471,15 +470,40 @@ bazel-bin/tensorflow/tools/compatibility/update/generate_v2_reorders_map _, unused_report, unused_errors, new_text = self._upgrade(text) self.assertEqual( new_text, - "tf.nn.dropout(x, 1 - keep_prob, name=\"foo\")\n", + "tf.nn.dropout(x, 1 - (keep_prob), name=\"foo\")\n", + ) + + text = "tf.nn.dropout(x, keep_prob=.4, name=\"foo\")\n" + _, unused_report, unused_errors, new_text = self._upgrade(text) + self.assertEqual( + new_text, + "tf.nn.dropout(x, rate=1 - (.4), name=\"foo\")\n", + ) + + text = ( + "tf.nn.dropout(x, # Stuff before\n" + " keep_prob=.4, # Stuff after\n" + " name=\"foo\")\n" + ) + _, unused_report, unused_errors, new_text = self._upgrade(text) + self.assertEqual( + new_text, + "tf.nn.dropout(x, # Stuff before\n" + " rate=1 - (.4), # Stuff after\n" + " name=\"foo\")\n", ) text = "tf.nn.dropout(x)\n" _, unused_report, errors, new_text = self._upgrade(text) self.assertEqual(new_text, text) + self.assertIn("tf.nn.dropout called without arguments", errors[0]) + + def testDropoutExpr(self): + text = "tf.nn.dropout(x, 1 - func(3 + 4.), name=\"foo\")\n" + _, unused_report, unused_errors, new_text = self._upgrade(text) self.assertEqual( - errors, - ["test.py:1: tf.nn.dropout requires manual check."] + new_text, + "tf.nn.dropout(x, 1 - (1 - func(3 + 4.)), name=\"foo\")\n", ) def testCountNonZeroChanges(self): @@ -543,9 +567,11 @@ bazel-bin/tensorflow/tools/compatibility/update/generate_v2_reorders_map text = "tf.gradients(a, colocate_gradients_with_ops=False)\n" _, unused_report, errors, new_text = self._upgrade(text) - self.assertEqual(text, new_text) - self.assertEqual(errors, ["test.py:1: tf.gradients requires manual check."]) + self.assertEqual("tf.gradients(a)\n", new_text) + self.assertIn("tf.gradients", errors[0]) + self.assertIn("requires manual check", errors[0]) + def testColocateGradientsWithOpsMinimize(self): text = "optimizer.minimize(a, foo=False)\n" _, unused_report, errors, new_text = self._upgrade(text) self.assertEqual(text, new_text) @@ -553,10 +579,11 @@ bazel-bin/tensorflow/tools/compatibility/update/generate_v2_reorders_map text = "optimizer.minimize(a, colocate_gradients_with_ops=False)\n" _, unused_report, errors, new_text = self._upgrade(text) - self.assertEqual(text, new_text) - self.assertEqual(errors, - ["test.py:1: Optimizer.minimize requires manual check."]) + self.assertEqual("optimizer.minimize(a)\n", new_text) + self.assertIn("requires manual check", errors[0]) + self.assertIn("minimize", errors[0]) + def testColocateGradientsWithOpsComputeGradients(self): text = "optimizer.compute_gradients(a, foo=False)\n" _, unused_report, errors, new_text = self._upgrade(text) self.assertEqual(text, new_text) @@ -564,10 +591,9 @@ bazel-bin/tensorflow/tools/compatibility/update/generate_v2_reorders_map text = "optimizer.compute_gradients(a, colocate_gradients_with_ops=False)\n" _, unused_report, errors, new_text = self._upgrade(text) - self.assertEqual(text, new_text) - self.assertEqual(errors, - ["test.py:1: Optimizer.compute_gradients " - "requires manual check."]) + self.assertEqual("optimizer.compute_gradients(a)\n", new_text) + self.assertIn("requires manual check", errors[0]) + self.assertIn("compute_gradients", errors[0]) def testExportSavedModelRename(self): text = "self.est.export_savedmodel(path)" @@ -673,7 +699,7 @@ bazel-bin/tensorflow/tools/compatibility/update/generate_v2_reorders_map _, report, errors, new_text = self._upgrade(text) self.assertEqual(new_text, expected_text) self.assertIn( - "tf.nn.softmax_cross_entropy_with_logits requires manual check.", + "tf.nn.softmax_cross_entropy_with_logits requires manual check", errors[0]) self.assertIn( "tf.nn.softmax_cross_entropy_with_logits behavior has changed. ", @@ -817,27 +843,24 @@ tf.print('abc') def testBatchGather(self): text = "tf.batch_gather(foo, bar)" - expected_text = "tf.gather(batch_dims=-1, params=foo, indices=bar)" + expected_text1 = "tf.gather(params=foo, indices=bar, batch_dims=-1)" + expected_text2 = "tf.gather(batch_dims=-1, params=foo, indices=bar)" _, unused_report, unused_errors, new_text = self._upgrade(text) - self.assertEqual(new_text, expected_text) + self.assertIn(new_text, [expected_text1, expected_text2]) text = "tf.batch_gather(params=foo, indices=bar)" - expected_text = "tf.gather(batch_dims=-1, params=foo, indices=bar)" - _, unused_report, unused_errors, new_text = self._upgrade(text) - self.assertEqual(new_text, expected_text) - - text = "tf.batch_gather ( foo, bar)" - expected_text = "tf.gather (batch_dims=-1, params=foo, indices=bar)" - _, unused_report, unused_errors, new_text = self._upgrade(text) - self.assertEqual(new_text, expected_text) - - text = "(tf.batch_gather\n(foo, bar))" - expected_text = "(tf.gather\n(params=foo, indices=bar))" - expected_errors = ["test.py:1: Unable to add keyword argument batch_dims=-1" - " to tf.batch_gather; please add it manually."] - _, unused_report, errors, new_text = self._upgrade(text) - self.assertEqual(errors, expected_errors) - self.assertEqual(new_text, expected_text) + expected_text1 = "tf.gather(params=foo, indices=bar, batch_dims=-1)" + expected_text2 = "tf.gather(batch_dims=-1, params=foo, indices=bar)" + _, unused_report, unused_errors, new_text = self._upgrade(text) + self.assertIn(new_text, [expected_text1, expected_text2]) + + def testCast(self): + for dtype in ["int32", "int64", "float", "double", + "complex64", "complex128", "bfloat16"]: + text = "tf.to_%s(x, name='test')" % dtype + expected_text = "tf.cast(x, name='test', dtype=tf.%s)" % dtype + _, unused_report, unused_errors, new_text = self._upgrade(text) + self.assertEqual(expected_text, new_text) class TestUpgradeFiles(test_util.TensorFlowTestCase): diff --git a/tensorflow/tools/pip_package/BUILD b/tensorflow/tools/pip_package/BUILD index 93a85763f5..c51b45a49c 100644 --- a/tensorflow/tools/pip_package/BUILD +++ b/tensorflow/tools/pip_package/BUILD @@ -169,6 +169,7 @@ filegroup( "@local_config_sycl//sycl:LICENSE.text", "@nasm//:LICENSE", "@nsync//:LICENSE", + "@pasta//:LICENSE", "@pcre//:LICENCE", "@png_archive//:LICENSE", "@protobuf_archive//:LICENSE", diff --git a/tensorflow/workspace.bzl b/tensorflow/workspace.bzl index 729eb8069f..b43312abb2 100755 --- a/tensorflow/workspace.bzl +++ b/tensorflow/workspace.bzl @@ -29,6 +29,7 @@ load("//third_party/jpeg:workspace.bzl", jpeg = "repo") load("//third_party/nasm:workspace.bzl", nasm = "repo") load("//third_party/kissfft:workspace.bzl", kissfft = "repo") load("//third_party/keras_applications_archive:workspace.bzl", keras_applications = "repo") +load("//third_party/pasta:workspace.bzl", pasta = "repo") def initialize_third_party(): """ Load third party repositories. See above load() statements. """ @@ -41,6 +42,7 @@ def initialize_third_party(): kissfft() jpeg() nasm() + pasta() # Sanitize a dependency so that it works correctly from code that includes # TensorFlow as a submodule. diff --git a/third_party/pasta/BUILD b/third_party/pasta/BUILD new file mode 100644 index 0000000000..9bd256a579 --- /dev/null +++ b/third_party/pasta/BUILD @@ -0,0 +1 @@ +# Empty BUILD file to force build system to see this directory at all. diff --git a/third_party/pasta/BUILD.bazel b/third_party/pasta/BUILD.bazel new file mode 100644 index 0000000000..6be33511e4 --- /dev/null +++ b/third_party/pasta/BUILD.bazel @@ -0,0 +1,29 @@ +# Description: +# AST-based python refactoring. + +licenses(["notice"]) # Apache2 + +exports_files(["LICENSE"]) + +py_library( + name = "pasta", + srcs = [ + "__init__.py", + "augment/__init__.py", + "augment/errors.py", + "augment/import_utils.py", + "augment/inline.py", + "augment/rename.py", + "base/__init__.py", + "base/annotate.py", + "base/ast_constants.py", + "base/ast_utils.py", + "base/codegen.py", + "base/formatting.py", + "base/scope.py", + "base/test_utils.py", + "base/token_generator.py", + ], + srcs_version = "PY2AND3", + visibility = ["//visibility:public"], +) diff --git a/third_party/pasta/BUILD.system b/third_party/pasta/BUILD.system new file mode 100644 index 0000000000..6adc953c5a --- /dev/null +++ b/third_party/pasta/BUILD.system @@ -0,0 +1,13 @@ +# Description: Pasta, AST based python refactoring. + +licenses(["notice"]) # Apache2 + +filegroup( + name = "LICENSE", + visibility = ["//visibility:public"], +) + +py_library( + name = "pasta", + visibility = ["//visibility:public"], +) diff --git a/third_party/pasta/workspace.bzl b/third_party/pasta/workspace.bzl new file mode 100644 index 0000000000..063bdb7b98 --- /dev/null +++ b/third_party/pasta/workspace.bzl @@ -0,0 +1,16 @@ +"""Loads pasta python package.""" + +load("//third_party:repo.bzl", "third_party_http_archive") + +def repo(): + third_party_http_archive( + name = "pasta", + urls = [ + "https://mirror.bazel.build/github.com/google/pasta/archive/c3d72cdee6fc806251949e912510444d58d7413c.tar.gz", + "https://github.com/google/pasta/archive/c3d72cdee6fc806251949e912510444d58d7413c.tar.gz", + ], + strip_prefix = "pasta-c3d72cdee6fc806251949e912510444d58d7413c/pasta", + sha256 = "b5905f9cecc4b28363c563f3c4cb0545288bd35f7cc72c55066e97e53befc084", + build_file = "//third_party/pasta:BUILD.bazel", + system_build_file = "//third_party/pasta:BUILD.system", + ) -- GitLab From 4b90409c6b9e11d0bf0517a8fc49f9826167cb0a Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Fri, 4 Jan 2019 17:04:27 -0800 Subject: [PATCH 383/622] Several fixes/improvements to wrap_function: 1. Makes gradient tape work on captures post-pruning 2. Allows users to prune with wrapped_func.inputs even when the func includes captures 3. Makes sure to write variables to collections for better legacy compatibility in compat.v1 (currently if the outer init_scope is eager, variables won't be written to the global_variables / trainable_variables collections) PiperOrigin-RevId: 227936352 --- tensorflow/python/eager/wrap_function.py | 23 +++++++++- tensorflow/python/eager/wrap_function_test.py | 44 +++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/tensorflow/python/eager/wrap_function.py b/tensorflow/python/eager/wrap_function.py index 0930b6116d..2c65e97871 100644 --- a/tensorflow/python/eager/wrap_function.py +++ b/tensorflow/python/eager/wrap_function.py @@ -38,8 +38,20 @@ class VariableHolder(object): self._variables = [] def variable_creator_scope(self, next_creator, **kwargs): + """Creates variables & adds them to collections to match legacy code.""" v = next_creator(**kwargs) self._variables.append(v) + + collections = kwargs.get("collections") + trainable = v.trainable + + if collections is None: + collections = [ops.GraphKeys.GLOBAL_VARIABLES] + if trainable and ops.GraphKeys.TRAINABLE_VARIABLES not in collections: + collections = list(collections) + [ops.GraphKeys.TRAINABLE_VARIABLES] + + ops.add_to_collections(collections, v) + return v def __call__(self, *args, **kwargs): @@ -61,6 +73,13 @@ class WrappedFunction(function.Function): for f in flat_feeds: if not isinstance(f, ops.Tensor): raise ValueError("Feeds must be tensors.") + + # Ignoring all feeds that are captures allows prune to be called + # using wrapped_func.inputs even when it uses variables + internal_captures = self.graph.internal_captures + flat_feeds = [f for f in flat_feeds + if f not in internal_captures] + tensor_fetches = [] operation_fetches = [] for f in flat_fetches: @@ -87,7 +106,7 @@ class WrappedFunction(function.Function): sink_tensor = control_flow_ops.no_op() lift_map = lift_to_graph.lift_to_graph( sink_tensor, pruned_graph, - sources=flat_feeds + self.graph.internal_captures) + sources=flat_feeds + internal_captures) for original_fetch, identity_fetch in zip( tensor_fetches, identity_fetches): lift_map[original_fetch] = lift_map[identity_fetch] @@ -98,6 +117,8 @@ class WrappedFunction(function.Function): pruned_graph.inputs.extend(lift_map[x] for x in flat_feeds) pruned_graph.inputs.extend(pruned_graph.captures.values()) + pruned_graph.variables = self.graph.variables + def _structured_output_mapping(fetched): lifted = lift_map[fetched] if isinstance(lifted, ops.Operation): diff --git a/tensorflow/python/eager/wrap_function_test.py b/tensorflow/python/eager/wrap_function_test.py index 65dd73aafc..17e204b4d8 100644 --- a/tensorflow/python/eager/wrap_function_test.py +++ b/tensorflow/python/eager/wrap_function_test.py @@ -18,6 +18,7 @@ from __future__ import division from __future__ import print_function +from tensorflow.python.eager import backprop from tensorflow.python.eager import wrap_function from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes @@ -90,11 +91,54 @@ class WrapFunctionTest(test.TestCase): f_wrapped = wrap_function.wrap_function(f, []) self.assertAllEqual(6.0, f_wrapped()) + + # Test pruning directly on the inputs + pruned = f_wrapped.prune( + feeds=f_wrapped.inputs, + fetches=f_wrapped.graph.get_tensor_by_name('fetch:0')) + self.assertAllEqual(6.0, pruned()) + + # Test pruning with no inputs pruned = f_wrapped.prune( feeds=(), fetches=f_wrapped.graph.get_tensor_by_name('fetch:0')) self.assertAllEqual(6.0, pruned()) + def testGradientsOfPrune(self): + + v1 = variables.Variable(2.) + v2_holder = [] + + def f(z): + v2 = variables.Variable(3.) + v2_holder.append(v2) + return array_ops.identity(v1 * v2 * z, 'fetch') + + f_wrapped = wrap_function.wrap_function( + f, [tensor_spec.TensorSpec((), dtype=dtypes.float32)]) + + x = constant_op.constant(1.) + with backprop.GradientTape() as tape: + tape.watch(x) + out = f_wrapped(x) + grads = tape.gradient(out, [x, v1, v2_holder[0]]) + + self.assertAllEqual(6.0, out) + self.assertAllEqual([6.0, 3.0, 2.0], grads) + + pruned = f_wrapped.prune( + feeds=f_wrapped.inputs, + fetches=f_wrapped.graph.get_tensor_by_name('fetch:0')) + + x = constant_op.constant(1.) + with backprop.GradientTape() as tape: + tape.watch(x) + out = pruned(x) + grads = tape.gradient(out, [x, v1, v2_holder[0]]) + + self.assertAllEqual(6.0, out) + self.assertAllEqual([6.0, 3.0, 2.0], grads) + def testPruneOperations(self): v = variables.Variable(0) -- GitLab From 542e30bab143034047ee18a0b060648be78df58c Mon Sep 17 00:00:00 2001 From: Jared Duke Date: Fri, 4 Jan 2019 17:11:01 -0800 Subject: [PATCH 384/622] Enable nnapi_delegate_test in oss builds PiperOrigin-RevId: 227937061 --- tensorflow/lite/delegates/nnapi/BUILD | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tensorflow/lite/delegates/nnapi/BUILD b/tensorflow/lite/delegates/nnapi/BUILD index fd954ba222..dda3c02567 100644 --- a/tensorflow/lite/delegates/nnapi/BUILD +++ b/tensorflow/lite/delegates/nnapi/BUILD @@ -3,6 +3,7 @@ package(default_visibility = [ ]) load("//tensorflow:tensorflow.bzl", "tf_cc_test") +load("//tensorflow/lite:special_rules.bzl", "tflite_portable_test_suite") licenses(["notice"]) # Apache 2.0 @@ -23,7 +24,7 @@ tf_cc_test( name = "nnapi_delegate_test", size = "small", srcs = ["nnapi_delegate_test.cc"], - tags = ["no_oss"], + tags = ["tflite_not_portable_ios"], deps = [ ":nnapi_delegate", "//tensorflow/lite:framework", @@ -32,3 +33,5 @@ tf_cc_test( "@com_google_googletest//:gtest", ], ) + +tflite_portable_test_suite() -- GitLab From 774ede5790c6cf11da05ca1082e9870db7b843f9 Mon Sep 17 00:00:00 2001 From: Karmel Allison Date: Fri, 4 Jan 2019 17:23:06 -0800 Subject: [PATCH 385/622] Consolidated duplicate tests for model cloning and added Keras test decorators where appropriate. Updated models_tests to run in v1/v2. This required several other changes: * Added a testing utility to get v2 optimizers * Ensured that subclassed models cleared key compilation attrs upon clone_reset (_is_compiled; optimizer) * Modified train/eval loops to check self._is_compiled rather than the existence of other attrs created during compiling, as these other attrs would not be reset during cloning. PiperOrigin-RevId: 227938218 --- tensorflow/python/keras/BUILD | 7 +- tensorflow/python/keras/engine/training.py | 4 +- tensorflow/python/keras/models.py | 44 +- tensorflow/python/keras/models_test.py | 734 ++++++++------------- tensorflow/python/keras/testing_utils.py | 56 +- 5 files changed, 375 insertions(+), 470 deletions(-) diff --git a/tensorflow/python/keras/BUILD b/tensorflow/python/keras/BUILD index 914568f652..3ffff61c2b 100755 --- a/tensorflow/python/keras/BUILD +++ b/tensorflow/python/keras/BUILD @@ -1,5 +1,7 @@ # Description: # Contains the Keras API (internal TensorFlow version). +load("//tensorflow:tensorflow.bzl", "tf_py_test") +load("//tensorflow:tensorflow.bzl", "cuda_py_test") licenses(["notice"]) # Apache 2.0 @@ -7,9 +9,6 @@ package(default_visibility = ["//visibility:public"]) exports_files(["LICENSE"]) -load("//tensorflow:tensorflow.bzl", "tf_py_test") -load("//tensorflow:tensorflow.bzl", "cuda_py_test") - config_setting( name = "empty_condition", values = {"define": "UNUSED=unused"}, @@ -1022,7 +1021,7 @@ tf_py_test( "//tensorflow/python:client_testlib", "//tensorflow/python:training", ], - shard_count = 2, + shard_count = 8, tags = ["notsan"], # b/67509773 ) diff --git a/tensorflow/python/keras/engine/training.py b/tensorflow/python/keras/engine/training.py index 8263467c37..bd06751428 100644 --- a/tensorflow/python/keras/engine/training.py +++ b/tensorflow/python/keras/engine/training.py @@ -1991,7 +1991,7 @@ class Model(Network): ' without calling `model.compile` after ?', 1) def _make_train_function_helper(self, fn_name, outputs, metric_updates=None): - if not hasattr(self, fn_name): + if not self._is_compiled: raise RuntimeError('You must compile your model before using it.') self._check_trainable_weights_consistency() if getattr(self, fn_name) is None: @@ -2040,7 +2040,7 @@ class Model(Network): '_fit_function', [self.total_loss] + metrics_tensors) def _make_test_function_helper(self, fn_name, outputs, metric_updates=None): - if not hasattr(self, fn_name): + if not self._is_compiled: raise RuntimeError('You must compile your model before using it.') if getattr(self, fn_name) is None: inputs = (self._feed_inputs + diff --git a/tensorflow/python/keras/models.py b/tensorflow/python/keras/models.py index f36c89041c..851de6e71f 100644 --- a/tensorflow/python/keras/models.py +++ b/tensorflow/python/keras/models.py @@ -281,8 +281,6 @@ def clone_model(model, input_tensors=None): # "Clone" a subclassed model by reseting all of the attributes. - - def _in_place_subclassed_model_reset(model): """Substitute for model cloning that works for subclassed models. @@ -382,11 +380,30 @@ def _in_place_subclassed_model_reset(model): for name in attributes_to_cache: attributes_cache[name] = getattr(model, name) model._original_attributes_cache = attributes_cache - # Reset built state + _reset_build_compile_trackers(model) + model._setattr_tracking = setattr_tracking + + +def _reset_build_compile_trackers(model): + """Reset state trackers for model. + + Note that we do not actually zero out attributes such as optimizer, + but instead rely on the expectation that all of the attrs will be + over-written on calling build/compile/etc. This is somewhat fragile, + insofar as we check elsewhere for the presence of these attributes as + evidence of having been built/compiled/etc. Pending a better way to do this, + we reset key attributes here to allow building and compiling. + + Args: + model: the model that is being reset + """ + # Reset build state model.built = False model.inputs = None model.outputs = None - model._setattr_tracking = setattr_tracking + # Reset compile state + model._is_compiled = False # pylint:disable=protected-access + model.optimizer = None def in_place_subclassed_model_state_restoration(model): @@ -418,9 +435,7 @@ def in_place_subclassed_model_state_restoration(model): model._setattr_tracking = setattr_tracking else: # Restore to the state of a never-called model. - model.built = False - model.inputs = None - model.outputs = None + _reset_build_compile_trackers(model) def clone_and_build_model( @@ -462,7 +477,10 @@ def clone_and_build_model( - cloning a subclassed model with `in_place_reset` set to False. - compiling the clone when the original model has not been compiled. """ - if compile_clone and not model.optimizer: + # Grab optimizer now, as we reset-in-place for subclassed models, but + # want to maintain access to the original optimizer. + orig_optimizer = model.optimizer + if compile_clone and not orig_optimizer: raise ValueError( 'Error when cloning model: compile_clone was set to True, but the ' 'original model has not been compiled.') @@ -498,14 +516,14 @@ def clone_and_build_model( input_tensors = input_tensors[0] clone._set_inputs(input_tensors) - if compile_clone and model.optimizer: - if isinstance(model.optimizer, optimizers.TFOptimizer): + if compile_clone: + if isinstance(orig_optimizer, optimizers.TFOptimizer): optimizer = optimizers.TFOptimizer( - model.optimizer.optimizer, optimizer_iterations) + orig_optimizer.optimizer, optimizer_iterations) K.track_tf_optimizer(optimizer) else: - optimizer_config = model.optimizer.get_config() - optimizer = model.optimizer.__class__.from_config(optimizer_config) + optimizer_config = orig_optimizer.get_config() + optimizer = orig_optimizer.__class__.from_config(optimizer_config) if optimizer_iterations is not None: optimizer.iterations = optimizer_iterations diff --git a/tensorflow/python/keras/models_test.py b/tensorflow/python/keras/models_test.py index 0a5f9a7bea..3eab10f624 100644 --- a/tensorflow/python/keras/models_test.py +++ b/tensorflow/python/keras/models_test.py @@ -19,18 +19,21 @@ from __future__ import division from __future__ import print_function import copy +import functools import os +from absl.testing import parameterized import numpy as np from tensorflow.python import keras from tensorflow.python.eager import context from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops -from tensorflow.python.framework import test_util from tensorflow.python.keras import backend as K +from tensorflow.python.keras import keras_parameterized from tensorflow.python.keras import metrics from tensorflow.python.keras import models +from tensorflow.python.keras import testing_utils from tensorflow.python.ops import array_ops from tensorflow.python.ops import random_ops from tensorflow.python.ops import resource_variable_ops @@ -52,158 +55,181 @@ class TestModel(keras.Model): return self.layer1(x) -def sequential_model(add_input_layer, include_input_shape=True): - model = keras.models.Sequential() +def _get_layers(input_shape=(4,), add_input_layer=False): if add_input_layer: - model.add(keras.layers.InputLayer(input_shape=(4,))) - model.add(keras.layers.Dense(4)) - elif include_input_shape: - model.add(keras.layers.Dense(4, input_shape=(4,))) + model_layers = [keras.layers.InputLayer(input_shape=input_shape), + keras.layers.Dense(4)] + elif input_shape: + model_layers = [keras.layers.Dense(4, input_shape=input_shape)] else: - model.add(keras.layers.Dense(4)) - model.add(keras.layers.BatchNormalization()) - model.add(keras.layers.Dropout(0.5)) - model.add(keras.layers.Dense(4)) - return model - - -class TestModelCloning(test.TestCase): - - @test_util.run_v1_only('b/120545219') - def test_clone_sequential_model(self): - with self.cached_session(): - val_a = np.random.random((10, 4)) - val_out = np.random.random((10, 4)) - - model = sequential_model(False) - - # Everything should work in a new session. - keras.backend.clear_session() - - with self.cached_session(): - # With placeholder creation - new_model = keras.models.clone_model(model) + model_layers = [keras.layers.Dense(4)] + + model_layers += [ + keras.layers.BatchNormalization(), + keras.layers.Dropout(0.5), + keras.layers.Dense(4)] + + return model_layers + + +def _get_model(input_shape=(4,)): + model_layers = _get_layers(input_shape=None, add_input_layer=False) + return testing_utils.get_model_from_layers( + model_layers, input_shape=input_shape) + + +class TestModelCloning(keras_parameterized.TestCase): + + @keras_parameterized.run_all_keras_modes + @parameterized.named_parameters([ + {'testcase_name': 'has_input_layer', + 'input_shape': (4,), + 'add_input_layer': True, + 'share_weights': False}, + {'testcase_name': 'no_input_layer', + 'input_shape': None, + 'add_input_layer': False, + 'share_weights': False}, + {'testcase_name': 'has_input_layer_share_weights', + 'input_shape': (4,), + 'add_input_layer': True, + 'share_weights': True}, + {'testcase_name': 'no_input_layer_share_weights', + 'input_shape': None, + 'add_input_layer': False, + 'share_weights': True}, + ]) + def test_clone_sequential_model( + self, input_shape, add_input_layer, share_weights): + + if share_weights: + clone_fn = functools.partial( + keras.models._clone_sequential_model, share_weights=True) + else: + clone_fn = keras.models.clone_model + + val_a = np.random.random((10, 4)) + model = models.Sequential(_get_layers(input_shape, add_input_layer)) + # Sanity check + self.assertEqual( + isinstance(model._layers[0], keras.layers.InputLayer), + add_input_layer) + self.assertEqual(model._is_graph_network, add_input_layer) + + # With placeholder creation -- clone model should have an InputLayer + # if the original model has one. + new_model = clone_fn(model) + self.assertEqual( + isinstance(new_model._layers[0], keras.layers.InputLayer), + add_input_layer) + self.assertEqual(new_model._is_graph_network, model._is_graph_network) + if input_shape: # update ops from batch norm needs to be included self.assertEqual(len(new_model.get_updates_for(new_model.inputs)), 2) - new_model.compile('rmsprop', 'mse') - new_model.train_on_batch(val_a, val_out) - - # On top of new tensor - input_a = keras.Input(shape=(4,)) - new_model = keras.models.clone_model(model, input_tensors=input_a) - self.assertEqual(len(new_model.get_updates_for(new_model.inputs)), 2) - new_model.compile('rmsprop', 'mse') - new_model.train_on_batch(val_a, val_out) - # On top of new, non-Keras tensor + # On top of new tensor -- clone model should always have an InputLayer. + input_a = keras.Input(shape=(4,)) + new_model = clone_fn(model, input_tensors=input_a) + self.assertIsInstance(new_model._layers[0], keras.layers.InputLayer) + self.assertTrue(new_model._is_graph_network) + + # On top of new, non-Keras tensor -- clone model should always have an + # InputLayer. + if not context.executing_eagerly(): + # TODO(b/121277734):Skip Eager contexts, as Input() layers raise an error + # saying they should not be used with EagerTensors input_a = keras.backend.variable(val_a) - new_model = keras.models.clone_model(model, input_tensors=input_a) - self.assertEqual(len(new_model.get_updates_for(new_model.inputs)), 2) - new_model.compile('rmsprop', 'mse') - new_model.train_on_batch(None, val_out) - - @test_util.run_v1_only('b/120545219') - def test_clone_sequential_model_input_layer(self): - - def test_input_layer(include_inputs): - with self.cached_session(): - val_a = np.random.random((10, 4)) - model = sequential_model(include_inputs, include_inputs) - # Sanity check - self.assertEqual( - isinstance(model._layers[0], keras.layers.InputLayer), - include_inputs) - self.assertEqual(model._is_graph_network, include_inputs) - - keras.backend.clear_session() - with self.cached_session(): - # With placeholder creation -- clone model should have an InputLayer - # if the original model has one. - new_model = keras.models.clone_model(model) - self.assertEqual( - isinstance(new_model._layers[0], keras.layers.InputLayer), - include_inputs) - self.assertEqual(new_model._is_graph_network, model._is_graph_network) - - # On top of new tensor -- clone model should always have an InputLayer. - input_a = keras.Input(shape=(4,)) - new_model = keras.models.clone_model(model, input_tensors=input_a) - self.assertIsInstance(new_model._layers[0], keras.layers.InputLayer) - self.assertTrue(new_model._is_graph_network) - - # On top of new, non-Keras tensor -- clone model should always have an - # InputLayer. - input_a = keras.backend.variable(val_a) - new_model = keras.models.clone_model(model, input_tensors=input_a) - self.assertIsInstance(new_model._layers[0], keras.layers.InputLayer) - self.assertTrue(new_model._is_graph_network) - - test_input_layer(True) - test_input_layer(False) - - @test_util.run_v1_only('b/120545219') - def test_clone_functional_model(self): - with self.cached_session(): - val_a = np.random.random((10, 4)) - val_b = np.random.random((10, 4)) - val_out = np.random.random((10, 4)) - - input_a = keras.Input(shape=(4,)) - input_b = keras.Input(shape=(4,)) - dense_1 = keras.layers.Dense(4,) - dense_2 = keras.layers.Dense(4,) - - x_a = dense_1(input_a) - x_a = keras.layers.Dropout(0.5)(x_a) - x_a = keras.layers.BatchNormalization()(x_a) - x_b = dense_1(input_b) - x_a = dense_2(x_a) - outputs = keras.layers.add([x_a, x_b]) - model = keras.models.Model([input_a, input_b], outputs) - - # Everything should work in a new session. - keras.backend.clear_session() - - with self.cached_session(): - # With placeholder creation - new_model = keras.models.clone_model(model) - self.assertEqual(len(new_model.get_updates_for(new_model.inputs)), 2) - new_model.compile('rmsprop', 'mse') - new_model.train_on_batch([val_a, val_b], val_out) - - # On top of new tensors - input_a = keras.Input(shape=(4,), name='a') - input_b = keras.Input(shape=(4,), name='b') - new_model = keras.models.clone_model( - model, input_tensors=[input_a, input_b]) - self.assertEqual(len(new_model.get_updates_for(new_model.inputs)), 2) - new_model.compile('rmsprop', 'mse') - new_model.train_on_batch([val_a, val_b], val_out) - - # On top of new, non-Keras tensors + new_model = clone_fn(model, input_tensors=input_a) + self.assertIsInstance(new_model._layers[0], keras.layers.InputLayer) + self.assertTrue(new_model._is_graph_network) + + @keras_parameterized.run_all_keras_modes + @parameterized.named_parameters([ + {'testcase_name': 'clone_weights', 'share_weights': False}, + {'testcase_name': 'share_weights', 'share_weights': True}, + ]) + def test_clone_functional_model(self, share_weights): + if share_weights: + clone_fn = functools.partial( + keras.models._clone_functional_model, share_weights=True) + else: + clone_fn = keras.models.clone_model + + val_a = np.random.random((10, 4)) + val_b = np.random.random((10, 4)) + val_out = np.random.random((10, 4)) + + input_a = keras.Input(shape=(4,)) + input_b = keras.Input(shape=(4,)) + dense_1 = keras.layers.Dense(4,) + dense_2 = keras.layers.Dense(4,) + + x_a = dense_1(input_a) + x_a = keras.layers.Dropout(0.5)(x_a) + x_a = keras.layers.BatchNormalization()(x_a) + x_b = dense_1(input_b) + x_a = dense_2(x_a) + outputs = keras.layers.add([x_a, x_b]) + model = keras.models.Model([input_a, input_b], outputs) + + # With placeholder creation + new_model = clone_fn(model) + self.assertEqual(len(new_model.get_updates_for(new_model.inputs)), 2) + new_model.compile( + testing_utils.get_v2_optimizer('rmsprop'), 'mse', + run_eagerly=testing_utils.should_run_eagerly()) + new_model.train_on_batch([val_a, val_b], val_out) + + # On top of new tensors + input_a = keras.Input(shape=(4,), name='a') + input_b = keras.Input(shape=(4,), name='b') + new_model = keras.models.clone_model( + model, input_tensors=[input_a, input_b]) + self.assertEqual(len(new_model.get_updates_for(new_model.inputs)), 2) + new_model.compile( + testing_utils.get_v2_optimizer('rmsprop'), 'mse', + run_eagerly=testing_utils.should_run_eagerly()) + new_model.train_on_batch([val_a, val_b], val_out) + + # On top of new, non-Keras tensors + if not context.executing_eagerly(): + # TODO(b/121277734):Skip Eager contexts, as Input() layers raise an error + # saying they should not be used with EagerTensors input_a = keras.backend.variable(val_a) input_b = keras.backend.variable(val_b) - new_model = keras.models.clone_model( - model, input_tensors=[input_a, input_b]) + new_model = clone_fn(model, input_tensors=[input_a, input_b]) self.assertEqual(len(new_model.get_updates_for(new_model.inputs)), 2) - new_model.compile('rmsprop', 'mse') + new_model.compile( + testing_utils.get_v2_optimizer('rmsprop'), 'mse', + run_eagerly=testing_utils.should_run_eagerly()) new_model.train_on_batch(None, val_out) - @test_util.run_in_graph_and_eager_modes - def test_clone_functional_model_with_masking(self): - with self.cached_session(): - x = np.array([[[1], [1]], [[0], [0]]]) - inputs = keras.Input((2, 1)) - outputs = keras.layers.Masking(mask_value=0)(inputs) - outputs = keras.layers.TimeDistributed( - keras.layers.Dense(1, kernel_initializer='one'))(outputs) - model = keras.Model(inputs, outputs) - - model = keras.models.clone_model(model) - model.compile(loss='mse', optimizer=adam.AdamOptimizer(0.01)) - y = np.array([[[1], [1]], [[1], [1]]]) - loss = model.train_on_batch(x, y) - self.assertEqual(float(loss), 0.) + @keras_parameterized.run_all_keras_modes + @parameterized.named_parameters([ + {'testcase_name': 'clone_weights', 'share_weights': False}, + {'testcase_name': 'share_weights', 'share_weights': True}, + ]) + def test_clone_functional_with_masking(self, share_weights): + if share_weights: + clone_fn = functools.partial( + keras.models._clone_functional_model, share_weights=True) + else: + clone_fn = keras.models.clone_model + + x = np.array([[[1.], [1.]], [[0.], [0.]]]) + inputs = keras.Input((2, 1)) + outputs = keras.layers.Masking(mask_value=0)(inputs) + outputs = keras.layers.TimeDistributed( + keras.layers.Dense(1, kernel_initializer='one'))(outputs) + model = keras.Model(inputs, outputs) + + model = clone_fn(model) + model.compile( + loss='mse', optimizer=testing_utils.get_v2_optimizer('adam'), + run_eagerly=testing_utils.should_run_eagerly()) + y = np.array([[[1], [1]], [[1], [1]]]) + loss = model.train_on_batch(x, y) + self.assertEqual(float(loss), 0.) def test_model_cloning_invalid_use_cases(self): seq_model = keras.models.Sequential() @@ -249,168 +275,23 @@ class TestModelCloning(test.TestCase): self.assertFalse(has_placeholder) -class TestModelCloningLayerPreserveWeights(test.TestCase): - - @test_util.run_deprecated_v1 - def test_clone_sequential_model(self): - with self.cached_session(): - val_a = np.random.random((10, 4)) - val_out = np.random.random((10, 4)) - - model = sequential_model(False) - - # Everything should work in a new session. - keras.backend.clear_session() - - with self.cached_session(): - # With placeholder creation - new_model = keras.models._clone_sequential_model( - model, share_weights=True) - # update ops from batch norm needs to be included - self.assertEqual(len(new_model.get_updates_for(new_model.inputs)), 2) - new_model.compile('rmsprop', 'mse') - new_model.train_on_batch(val_a, val_out) - - # On top of new tensor - input_a = keras.Input(shape=(4,)) - new_model = keras.models._clone_sequential_model( - model, input_tensors=input_a, share_weights=True) - self.assertEqual(len(new_model.get_updates_for(new_model.inputs)), 2) - new_model.compile('rmsprop', 'mse') - new_model.train_on_batch(val_a, val_out) - - # On top of new, non-Keras tensor - input_a = keras.backend.variable(val_a) - new_model = keras.models._clone_sequential_model( - model, input_tensors=input_a, share_weights=True) - self.assertEqual(len(new_model.get_updates_for(new_model.inputs)), 2) - new_model.compile('rmsprop', 'mse') - new_model.train_on_batch(None, val_out) - - @test_util.run_deprecated_v1 - def test_clone_sequential_model_input_layer(self): - - @test_util.run_deprecated_v1 - def test_input_layer(include_inputs): - with self.cached_session(): - val_a = np.random.random((10, 4)) - model = sequential_model(include_inputs, include_inputs) - # Sanity check - self.assertEqual( - isinstance(model._layers[0], keras.layers.InputLayer), - include_inputs) - self.assertEqual(model._is_graph_network, include_inputs) - - keras.backend.clear_session() - with self.cached_session(): - # With placeholder creation -- clone model should have an InputLayer - # if the original model has one. - new_model = keras.models._clone_sequential_model( - model, share_weights=True) - self.assertEqual( - isinstance(new_model._layers[0], keras.layers.InputLayer), - include_inputs) - self.assertEqual(new_model._is_graph_network, model._is_graph_network) - - # On top of new tensor -- clone model should always have an InputLayer. - input_a = keras.Input(shape=(4,)) - new_model = keras.models._clone_sequential_model( - model, input_tensors=input_a, share_weights=True) - self.assertIsInstance(new_model._layers[0], keras.layers.InputLayer) - self.assertTrue(new_model._is_graph_network) - - # On top of new, non-Keras tensor -- clone model should always have an - # InputLayer. - input_a = keras.backend.variable(val_a) - new_model = keras.models._clone_sequential_model( - model, input_tensors=input_a, share_weights=True) - self.assertIsInstance(new_model._layers[0], keras.layers.InputLayer) - self.assertTrue(new_model._is_graph_network) - - test_input_layer(True) - test_input_layer(False) - - @test_util.run_deprecated_v1 - def test_clone_functional_model(self): - with self.cached_session(): - val_a = np.random.random((10, 4)) - val_b = np.random.random((10, 4)) - val_out = np.random.random((10, 4)) - - input_a = keras.Input(shape=(4,)) - input_b = keras.Input(shape=(4,)) - dense_1 = keras.layers.Dense(4,) - dense_2 = keras.layers.Dense(4,) - - x_a = dense_1(input_a) - x_a = keras.layers.Dropout(0.5)(x_a) - x_a = keras.layers.BatchNormalization()(x_a) - x_b = dense_1(input_b) - x_a = dense_2(x_a) - outputs = keras.layers.add([x_a, x_b]) - model = keras.models.Model([input_a, input_b], outputs) - - # Everything should work in a new session. - keras.backend.clear_session() - - with self.cached_session(): - # With placeholder creation - new_model = keras.models._clone_functional_model( - model, share_weights=True) - self.assertEqual(len(new_model.get_updates_for(new_model.inputs)), 2) - new_model.compile('rmsprop', 'mse') - new_model.train_on_batch([val_a, val_b], val_out) - - # On top of new tensors - input_a = keras.Input(shape=(4,), name='a') - input_b = keras.Input(shape=(4,), name='b') - new_model = keras.models._clone_functional_model( - model, input_tensors=[input_a, input_b], share_weights=True) - self.assertEqual(len(new_model.get_updates_for(new_model.inputs)), 2) - new_model.compile('rmsprop', 'mse') - new_model.train_on_batch([val_a, val_b], val_out) - - # On top of new, non-Keras tensors - input_a = keras.backend.variable(val_a) - input_b = keras.backend.variable(val_b) - new_model = keras.models._clone_functional_model( - model, input_tensors=[input_a, input_b], share_weights=True) - self.assertEqual(len(new_model.get_updates_for(new_model.inputs)), 2) - new_model.compile('rmsprop', 'mse') - new_model.train_on_batch(None, val_out) - - @test_util.run_in_graph_and_eager_modes - def test_clone_functional_model_with_masking(self): - with self.cached_session(): - x = np.array([[[1], [1]], [[0], [0]]]) - inputs = keras.Input((2, 1)) - outputs = keras.layers.Masking(mask_value=0)(inputs) - outputs = keras.layers.TimeDistributed( - keras.layers.Dense(1, kernel_initializer='one'))(outputs) - model = keras.Model(inputs, outputs) - - model = keras.models._clone_functional_model( - model, share_weights=True) - model.compile(loss='mse', optimizer=adam.AdamOptimizer(0.01)) - y = np.array([[[1], [1]], [[1], [1]]]) - loss = model.train_on_batch(x, y) - self.assertEqual(float(loss), 0.) - - def _has_placeholder(graph): ops_types = [op.type for op in graph.get_operations()] return any('Placeholder' in s for s in ops_types) -class CheckpointingTests(test.TestCase): +@keras_parameterized.run_with_all_model_types +@keras_parameterized.run_all_keras_modes +class CheckpointingTests(keras_parameterized.TestCase): - @test_util.run_in_graph_and_eager_modes def test_optimizer_dependency(self): - model = keras.models.Sequential() - model.add(keras.layers.Dense(1, input_shape=(4,))) - opt = adam.AdamOptimizer(0.01) - model.compile(optimizer=opt, loss='mse') - model.fit(x=np.array([[1., 2., 3., 4.]]), y=[1.], epochs=2) + model = _get_model() + opt = adam.AdamOptimizer(.01) + model.compile( + optimizer=opt, loss='mse', + run_eagerly=testing_utils.should_run_eagerly()) + + model.fit(x=np.array([[1., 2., 3., 4.]]), y=np.array([1.]), epochs=2) save_prefix = os.path.join(self.get_temp_dir(), 'ckpt') beta1_power, _ = opt._get_beta_accumulators() self.evaluate(beta1_power.assign(12.)) @@ -420,7 +301,8 @@ class CheckpointingTests(test.TestCase): self.assertEqual(12., self.evaluate(beta1_power)) -class TestModelBackend(test.TestCase): +@keras_parameterized.run_all_keras_modes +class TestModelBackend(keras_parameterized.TestCase): def test_model_backend_float64_use_cases(self): # Test case for GitHub issue 19318 @@ -430,7 +312,9 @@ class TestModelBackend(test.TestCase): x = keras.Input((5,)) y = keras.layers.Dense(1)(x) model = keras.models.Model(x, y) - model.compile('rmsprop', 'mse') + model.compile( + testing_utils.get_v2_optimizer('rmsprop'), 'mse', + run_eagerly=testing_utils.should_run_eagerly()) keras.backend.set_floatx(floatx) @@ -465,48 +349,46 @@ class TestModelDeepCopy(test.TestCase): model_copy.get_weights()[0])) -@test_util.run_v1_only('b/120545219') -class TestCloneAndBuildModel(test.TestCase): +@keras_parameterized.run_all_keras_modes +class TestCloneAndBuildModel(keras_parameterized.TestCase): + @keras_parameterized.run_with_all_model_types def test_clone_and_build_non_compiled_model(self): - with self.cached_session(): - inp = np.random.random((10, 4)) - out = np.random.random((10, 4)) + inp = np.random.random((10, 4)) + out = np.random.random((10, 4)) - model = keras.models.Sequential() - model.add(keras.layers.Dense(4, input_shape=(4,))) - model.add(keras.layers.BatchNormalization()) - model.add(keras.layers.Dropout(0.5)) - model.add(keras.layers.Dense(4)) - - # Everything should work in a new session. - keras.backend.clear_session() - - with self.cached_session(): - with self.assertRaisesRegexp(ValueError, 'has not been compiled'): - models.clone_and_build_model(model, compile_clone=True) - - # With placeholder creation - new_model = models.clone_and_build_model(model, compile_clone=False) - with self.assertRaisesRegexp(RuntimeError, 'must compile'): - new_model.evaluate(inp, out) - with self.assertRaisesRegexp(RuntimeError, 'must compile'): - new_model.train_on_batch(inp, out) - new_model.compile('rmsprop', 'mse') - new_model.train_on_batch(inp, out) + model = _get_model() + + with self.assertRaisesRegexp(ValueError, 'has not been compiled'): + models.clone_and_build_model(model, compile_clone=True) - # Create new tensors for inputs and targets - input_a = keras.Input(shape=(4,)) - target_a = keras.Input(shape=(4,)) - new_model = models.clone_and_build_model(model, input_tensors=input_a, - target_tensors=[target_a], - compile_clone=False) - with self.assertRaisesRegexp(RuntimeError, 'must compile'): - new_model.evaluate(inp, out) - with self.assertRaisesRegexp(RuntimeError, 'must compile'): - new_model.train_on_batch(inp, out) - new_model.compile('rmsprop', 'mse') + is_subclassed = (testing_utils.get_model_type() == 'subclass') + # With placeholder creation + new_model = models.clone_and_build_model( + model, compile_clone=False, in_place_reset=is_subclassed) + with self.assertRaisesRegexp(RuntimeError, 'must compile'): + new_model.evaluate(inp, out) + with self.assertRaisesRegexp(RuntimeError, 'must compile'): + new_model.train_on_batch(inp, out) + new_model.compile( + testing_utils.get_v2_optimizer('rmsprop'), 'mse', + run_eagerly=testing_utils.should_run_eagerly()) + new_model.train_on_batch(inp, out) + + # Create new tensors for inputs and targets + input_a = keras.Input(shape=(4,)) + target_a = keras.Input(shape=(4,)) + new_model = models.clone_and_build_model( + model, input_tensors=input_a, target_tensors=[target_a], + compile_clone=False, in_place_reset=is_subclassed) + with self.assertRaisesRegexp(RuntimeError, 'must compile'): + new_model.evaluate(inp, out) + with self.assertRaisesRegexp(RuntimeError, 'must compile'): new_model.train_on_batch(inp, out) + new_model.compile( + testing_utils.get_v2_optimizer('rmsprop'), 'mse', + run_eagerly=testing_utils.should_run_eagerly()) + new_model.train_on_batch(inp, out) def _assert_same_compile_params(self, model): """Assert that two models have the same compile parameters.""" @@ -519,134 +401,88 @@ class TestCloneAndBuildModel(test.TestCase): self.assertEqual(['acc', metrics.categorical_accuracy], model._compile_metrics) - def _clone_and_build_test_helper(self, model, is_subclassed=False): + def _clone_and_build_test_helper(self, model, model_type): inp = np.random.random((10, 4)) out = np.random.random((10, 4)) - # Everything should work in a new session. - keras.backend.clear_session() - - with self.cached_session(): - # With placeholder creation - new_model = models.clone_and_build_model( - model, compile_clone=True, in_place_reset=is_subclassed) + is_subclassed = (model_type == 'subclass') + + # With placeholder creation + new_model = models.clone_and_build_model( + model, compile_clone=True, in_place_reset=is_subclassed) + + self._assert_same_compile_params(new_model) + new_model.train_on_batch(inp, out) + new_model.evaluate(inp, out) + + # Create new tensors for inputs and targets + input_a = keras.Input(shape=(4,), name='a') + new_model = models.clone_and_build_model( + model, input_tensors=input_a, compile_clone=True, + in_place_reset=is_subclassed) + self._assert_same_compile_params(new_model) + new_model.train_on_batch(inp, out) + new_model.evaluate(inp, out) + + target_a = keras.Input(shape=(4,), name='b') + new_model = models.clone_and_build_model( + model, input_tensors=input_a, target_tensors=[target_a], + compile_clone=True, in_place_reset=is_subclassed) + self._assert_same_compile_params(new_model) + new_model.train_on_batch(inp, out) + new_model.evaluate(inp, out) + + @keras_parameterized.run_with_all_model_types + def test_clone_and_build_compiled(self): + model = _get_model() + model.compile( + testing_utils.get_v2_optimizer('rmsprop'), 'mse', + metrics=['acc', metrics.categorical_accuracy], + run_eagerly=testing_utils.should_run_eagerly()) + + self._clone_and_build_test_helper(model, testing_utils.get_model_type()) + + def test_clone_and_build_sequential_without_inputs_defined(self): + model = models.Sequential(_get_layers(input_shape=None)) + model.compile( + testing_utils.get_v2_optimizer('rmsprop'), + 'mse', metrics=['acc', metrics.categorical_accuracy], + run_eagerly=testing_utils.should_run_eagerly()) + self._clone_and_build_test_helper(model, 'sequential') - self._assert_same_compile_params(new_model) - new_model.train_on_batch(inp, out) - new_model.evaluate(inp, out) - - # Create new tensors for inputs and targets - input_a = keras.Input(shape=(4,), name='a') - new_model = models.clone_and_build_model( - model, input_tensors=input_a, compile_clone=True, - in_place_reset=is_subclassed) - self._assert_same_compile_params(new_model) - new_model.train_on_batch(inp, out) - new_model.evaluate(inp, out) - - target_a = keras.Input(shape=(4,), name='b') - new_model = models.clone_and_build_model( - model, input_tensors=input_a, target_tensors=[target_a], - compile_clone=True, in_place_reset=is_subclassed) - self._assert_same_compile_params(new_model) - new_model.train_on_batch(inp, out) - new_model.evaluate(inp, out) - - def test_clone_and_build_compiled_sequential_model(self): - with self.cached_session(): - model = keras.models.Sequential() - model.add(keras.layers.Dense(4, input_shape=(4,))) - model.add(keras.layers.BatchNormalization()) - model.add(keras.layers.Dropout(0.5)) - model.add(keras.layers.Dense(4)) - model.compile('rmsprop', 'mse', - metrics=['acc', metrics.categorical_accuracy]) - - self._clone_and_build_test_helper(model) - - def test_clone_and_build_functional_model(self): - with self.cached_session(): - input_a = keras.Input(shape=(4,)) - dense_1 = keras.layers.Dense(4,) - dense_2 = keras.layers.Dense(4,) - - x_a = dense_1(input_a) - x_a = keras.layers.Dropout(0.5)(x_a) - x_a = keras.layers.BatchNormalization()(x_a) - x_a = dense_2(x_a) - model = keras.models.Model(input_a, x_a) - model.compile('rmsprop', 'mse', - metrics=['acc', metrics.categorical_accuracy]) - - self._clone_and_build_test_helper(model) - - def test_clone_and_build_subclassed_model(self): - class SubclassedModel(keras.Model): - - def __init__(self): - super(SubclassedModel, self).__init__() - self.layer1 = keras.layers.Dense(4) - self.layer2 = keras.layers.Dense(4) - - def call(self, inp): - out = self.layer1(inp) - out = keras.layers.BatchNormalization()(out) - out = keras.layers.Dropout(0.5)(out) - out = self.layer2(out) - return out - - with self.cached_session(): - model = SubclassedModel() - model.compile('rmsprop', 'mse', - metrics=['acc', metrics.categorical_accuracy]) - self._clone_and_build_test_helper(model, True) + inp = np.random.random((10, 4)) + out = np.random.random((10, 4)) + model.train_on_batch(inp, out) + self._clone_and_build_test_helper(model, 'sequential') def assert_optimizer_iterations_increases(self, optimizer): - with self.cached_session(): - input_a = keras.Input(shape=(4,)) - dense_1 = keras.layers.Dense(4,) - dense_2 = keras.layers.Dense(4,) - - x_a = dense_1(input_a) - x_a = keras.layers.Dropout(0.5)(x_a) - x_a = keras.layers.BatchNormalization()(x_a) - x_a = dense_2(x_a) - model = keras.models.Model(input_a, x_a) - model.compile(optimizer, 'mse', - metrics=['acc', metrics.categorical_accuracy]) + model = _get_model() + model.compile( + optimizer, 'mse', metrics=['acc', metrics.categorical_accuracy], + run_eagerly=testing_utils.should_run_eagerly()) - global_step = keras.backend.variable(123, dtype=dtypes.int64) - clone_model = models.clone_and_build_model( - model, compile_clone=True, optimizer_iterations=global_step) + global_step = keras.backend.variable(123, dtype=dtypes.int64) + clone_model = models.clone_and_build_model( + model, compile_clone=True, optimizer_iterations=global_step, + in_place_reset=(testing_utils.get_model_type() == 'subclass')) - inp = np.random.random((10, 4)) - out = np.random.random((10, 4)) - clone_model.train_on_batch(inp, out) + inp = np.random.random((10, 4)) + out = np.random.random((10, 4)) + clone_model.train_on_batch(inp, out) - self.assertEqual(K.eval(global_step), 124) + self.assertEqual(K.eval(global_step), 124) + @keras_parameterized.run_with_all_model_types def test_replace_tf_optimizer_iterations_variable(self): self.assert_optimizer_iterations_increases(adam.AdamOptimizer(0.01)) + @keras_parameterized.run_with_all_model_types def test_replace_keras_optimizer_iterations_variable(self): - self.assert_optimizer_iterations_increases('adam') + if testing_utils.should_run_eagerly(): + # This needs to be updated to run with v2 optimizers. + self.skipTest('b/120991591') - def test_replace_keras_optimizer_v2_iterations_variable(self): - self.assert_optimizer_iterations_increases( - keras.optimizer_v2.adam.Adam(0.01)) - - def test_clone_and_build_sequential_model_without_inputs_defined(self): - with self.cached_session(): - model = sequential_model(False, False) - model.compile('rmsprop', 'mse', - metrics=['acc', metrics.categorical_accuracy]) - self._clone_and_build_test_helper(model, False) - - with self.cached_session(): - inp = np.random.random((10, 4)) - out = np.random.random((10, 4)) - model.train_on_batch(inp, out) - self._clone_and_build_test_helper(model, False) + self.assert_optimizer_iterations_increases('adam') if __name__ == '__main__': diff --git a/tensorflow/python/keras/testing_utils.py b/tensorflow/python/keras/testing_utils.py index fd062b0ab3..6448f87d25 100644 --- a/tensorflow/python/keras/testing_utils.py +++ b/tensorflow/python/keras/testing_utils.py @@ -25,6 +25,13 @@ import numpy as np from tensorflow.python import keras from tensorflow.python.eager import context from tensorflow.python.framework import tensor_shape +from tensorflow.python.keras.optimizer_v2 import adadelta as adadelta_v2 +from tensorflow.python.keras.optimizer_v2 import adagrad as adagrad_v2 +from tensorflow.python.keras.optimizer_v2 import adam as adam_v2 +from tensorflow.python.keras.optimizer_v2 import adamax as adamax_v2 +from tensorflow.python.keras.optimizer_v2 import gradient_descent as gradient_descent_v2 +from tensorflow.python.keras.optimizer_v2 import nadam as nadam_v2 +from tensorflow.python.keras.optimizer_v2 import rmsprop as rmsprop_v2 from tensorflow.python.training.rmsprop import RMSPropOptimizer from tensorflow.python.util import tf_contextlib from tensorflow.python.util import tf_inspect @@ -355,11 +362,20 @@ class _SubclassModel(keras.Model): def __init__(self, layers): super(_SubclassModel, self).__init__() - self.all_layers = layers + # Note that clone and build doesn't support lists of layers in subclassed + # models. Adding each layer directly here. + for i, layer in enumerate(layers): + setattr(self, self._layer_name_for_i(i), layer) + + self.num_layers = len(layers) + + def _layer_name_for_i(self, i): + return 'layer{}'.format(i) def call(self, inputs, **kwargs): x = inputs - for layer in self.all_layers: + for i in range(self.num_layers): + layer = getattr(self, self._layer_name_for_i(i)) x = layer(x) return x @@ -626,3 +642,39 @@ def get_multi_io_model( return keras.Model(inputs, outputs) raise ValueError('Unknown model type {}'.format(model_type)) + + +_V2_OPTIMIZER_MAP = { + 'adadelta': adadelta_v2.Adadelta, + 'adagrad': adagrad_v2.Adagrad, + 'adam': adam_v2.Adam, + 'adamax': adamax_v2.Adamax, + 'nadam': nadam_v2.Nadam, + 'rmsprop': rmsprop_v2.RMSprop, + 'sgd': gradient_descent_v2.SGD +} + + +def get_v2_optimizer(name, **kwargs): + """Get the v2 optimizer requested. + + This is only necessary until v2 are the default, as we are testing in Eager, + and Eager + v1 optimizers fail tests. When we are in v2, the strings alone + should be sufficient, and this mapping can theoretically be removed. + + Args: + name: string name of Keras v2 optimizer. + **kwargs: any kwargs to pass to the optimizer constructor. + + Returns: + Initialized Keras v2 optimizer. + + Raises: + ValueError: if an unknown name was passed. + """ + try: + return _V2_OPTIMIZER_MAP[name](**kwargs) + except KeyError: + raise ValueError( + 'Could not find requested v2 optimizer: {}\nValid choices: {}'.format( + name, list(_V2_OPTIMIZER_MAP.keys()))) -- GitLab From 17bf2b33a90017c8536ef4a242bb60636bfc5e8a Mon Sep 17 00:00:00 2001 From: Russell Power Date: Fri, 4 Jan 2019 17:45:29 -0800 Subject: [PATCH 386/622] Graph construction: skip testing for duplicate edges during source/sink setup. When fixing up source/sink edges, we are only adding missing edges. Skip the test for duplicate edges in this case, which avoids a linear traversal over sink edges. PiperOrigin-RevId: 227940303 --- tensorflow/core/graph/algorithm.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tensorflow/core/graph/algorithm.cc b/tensorflow/core/graph/algorithm.cc index 9b4200e0b4..25bd3516a1 100644 --- a/tensorflow/core/graph/algorithm.cc +++ b/tensorflow/core/graph/algorithm.cc @@ -222,11 +222,12 @@ bool FixupSourceAndSinkEdges(Graph* g) { bool changed = false; for (Node* n : g->nodes()) { if (!n->IsSource() && n->in_edges().empty()) { - g->AddControlEdge(g->source_node(), n); + g->AddControlEdge(g->source_node(), n, + true /* skip test for duplicates */); changed = true; } if (!n->IsSink() && n->out_edges().empty()) { - g->AddControlEdge(n, g->sink_node()); + g->AddControlEdge(n, g->sink_node(), true /* skip test for duplicates */); changed = true; } } -- GitLab From 1814e22d3cf78fd6e5b172c6de1a44cd6abc25f3 Mon Sep 17 00:00:00 2001 From: Jared Duke Date: Fri, 4 Jan 2019 17:45:29 -0800 Subject: [PATCH 387/622] Fix some empty struct build warnings in TFLite PiperOrigin-RevId: 227940304 --- tensorflow/lite/c/builtin_op_data.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tensorflow/lite/c/builtin_op_data.h b/tensorflow/lite/c/builtin_op_data.h index 46ac656ecf..332c2db145 100644 --- a/tensorflow/lite/c/builtin_op_data.h +++ b/tensorflow/lite/c/builtin_op_data.h @@ -25,6 +25,11 @@ extern "C" { // TODO(aselle): Consider using "if this then that" for testing. +// Useful placeholder to put in otherwise empty structs to avoid size warnings. +typedef struct { + char dummy; +} EmptyStructPlaceholder; + // IMPORTANT: All new members of structs must be added at the end to ensure // backwards compatibility. @@ -152,9 +157,11 @@ typedef struct { } TfLiteAddParams; typedef struct { + EmptyStructPlaceholder placeholder; } TfLiteSpaceToBatchNDParams; typedef struct { + EmptyStructPlaceholder placeholder; } TfLiteBatchToSpaceNDParams; typedef struct { @@ -230,9 +237,11 @@ typedef struct { } TfLiteResizeNearestNeighborParams; typedef struct { + EmptyStructPlaceholder placeholder; } TfLitePadParams; typedef struct { + EmptyStructPlaceholder placeholder; } TfLitePadV2Params; typedef struct { @@ -272,6 +281,7 @@ typedef struct { } TfLiteGatherParams; typedef struct { + EmptyStructPlaceholder placeholder; } TfLiteTransposeParams; typedef struct { -- GitLab From 447189935cbc1ab3aeb2ac67f81f03d1a188e548 Mon Sep 17 00:00:00 2001 From: Guangda Lai Date: Fri, 4 Jan 2019 18:40:27 -0800 Subject: [PATCH 388/622] Make TRT tests respect RewriterConfig set by individual test case everywhere. PiperOrigin-RevId: 227944638 --- .../test/tf_trt_integration_test_base.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tensorflow/contrib/tensorrt/test/tf_trt_integration_test_base.py b/tensorflow/contrib/tensorrt/test/tf_trt_integration_test_base.py index 495a9391a1..671abba6a6 100644 --- a/tensorflow/contrib/tensorrt/test/tf_trt_integration_test_base.py +++ b/tensorflow/contrib/tensorrt/test/tf_trt_integration_test_base.py @@ -239,8 +239,8 @@ class TfTrtIntegrationTestBase(test_util.TensorFlowTestCase): def _GetConfigProto(self, run_params, graph_state): """Get config proto based on specific settings.""" + conversion_params = self.GetConversionParams(run_params) if graph_state != GraphState.ORIGINAL and run_params.use_optimizer: - conversion_params = self.GetConversionParams(run_params) rewriter_cfg = trt_convert.get_tensorrt_rewriter_config( conversion_params.rewriter_config, conversion_params.max_batch_size, conversion_params.max_workspace_size_bytes, @@ -254,6 +254,9 @@ class TfTrtIntegrationTestBase(test_util.TensorFlowTestCase): graph_options = config_pb2.GraphOptions(rewrite_options=rewriter_cfg) else: graph_options = config_pb2.GraphOptions() + if conversion_params.rewriter_config is not None: + graph_options.rewrite_options.CopyFrom( + conversion_params.rewriter_config) config = config_pb2.ConfigProto( gpu_options=self._GetGPUOptions(), graph_options=graph_options) @@ -321,16 +324,13 @@ class TfTrtIntegrationTestBase(test_util.TensorFlowTestCase): return self._RunGraph( run_params, gdef, input_data, config, GraphState.CALIBRATE, num_runs=5) - def _GetTrtGraphDef(self, run_params, gdef): + def _GetTrtGraphDef(self, run_params, graph_state, gdef): """Return trt converted graphdef.""" params = self._GetParamsCached() conversion_params = self.GetConversionParams(run_params) logging.info(conversion_params) - config_for_trt = config_pb2.ConfigProto(gpu_options=self._GetGPUOptions()) - if conversion_params.rewriter_config is not None: - config_for_trt.graph_options.rewrite_options.CopyFrom( - conversion_params.rewriter_config) + config_for_trt = self._GetConfigProto(run_params, graph_state) return trt_convert.create_inference_graph( input_graph_def=gdef, outputs=params.input_names + params.output_names, @@ -506,7 +506,8 @@ class TfTrtIntegrationTestBase(test_util.TensorFlowTestCase): result = self._RunCalibration(run_params, input_gdef, input_data, calib_config) else: - calib_gdef = self._GetTrtGraphDef(run_params, input_gdef) + calib_gdef = self._GetTrtGraphDef(run_params, GraphState.CALIBRATE, + input_gdef) self._VerifyGraphDef(run_params, calib_gdef, GraphState.CALIBRATE) result = self._RunCalibration(run_params, calib_gdef, input_data, calib_config) @@ -527,7 +528,8 @@ class TfTrtIntegrationTestBase(test_util.TensorFlowTestCase): logging.info("Running final inference graph, config:\n%s", str(infer_config)) if not run_params.use_optimizer: - infer_gdef = self._GetTrtGraphDef(run_params, infer_gdef) + infer_gdef = self._GetTrtGraphDef(run_params, GraphState.INFERENCE, + infer_gdef) self._VerifyGraphDef(run_params, infer_gdef, GraphState.INFERENCE) result = self._RunGraph(run_params, infer_gdef, input_data, infer_config, -- GitLab From d93409b9dfc3bfaacfa452fab603a089931b3335 Mon Sep 17 00:00:00 2001 From: Davide Libenzi Date: Fri, 4 Jan 2019 19:00:39 -0800 Subject: [PATCH 389/622] Automated rollback of commit 55fda7fb66f1774ac5271e523259faa7d2a44d0c PiperOrigin-RevId: 227945898 --- .../xla/service/cpu/cpu_executable.cc | 29 +--- .../xla/service/gpu/gpu_executable.cc | 26 +--- tensorflow/compiler/xrt/kernels/BUILD | 1 - .../compiler/xrt/kernels/xrt_execute_op.cc | 17 --- tensorflow/compiler/xrt/tests/raw_api_test.cc | 127 +----------------- tensorflow/compiler/xrt/xrt_state.cc | 43 +----- tensorflow/compiler/xrt/xrt_state.h | 23 +--- 7 files changed, 17 insertions(+), 249 deletions(-) diff --git a/tensorflow/compiler/xla/service/cpu/cpu_executable.cc b/tensorflow/compiler/xla/service/cpu/cpu_executable.cc index 23d0af3423..412c2715b9 100644 --- a/tensorflow/compiler/xla/service/cpu/cpu_executable.cc +++ b/tensorflow/compiler/xla/service/cpu/cpu_executable.cc @@ -213,8 +213,6 @@ StatusOr CpuExecutable::CreateResultShapedBuffer( /*on_host_shape=*/result_shape(), /*on_device_shape=*/result_shape(), run_options->allocator(), stream->parent()->device_ordinal()); - const HloInputOutputAliasConfig& input_output_alias = - module().input_output_alias_config(); // Move OwningDeviceMemory values which contain the array(s) of the result // into the respective location in ScopedShapedBuffer which is returned to the @@ -234,31 +232,12 @@ StatusOr CpuExecutable::CreateResultShapedBuffer( TF_ASSIGN_OR_RETURN( const BufferAllocation::Slice slice, this->assignment_->GetUniqueSlice(src, buffer_source->index())); + CHECK(!slice.allocation()->is_entry_computation_parameter()); + const BufferAllocation::Index buffer_index = slice.index(); OwningDeviceMemory& buffer = buffers[buffer_index]; - if (!slice.allocation()->is_entry_computation_parameter()) { - // If the buffer coming out of the result is from a parameter, the - // owning buffer will be null, and that means the caller aliased some - // parameter buffer to an output one (via the - // HloInputOutputAliasConfig API). If that is the case, the caller - // will receive a partially complete scoped shaped buffer, which they - // will have to fill up on return. Unfortunately the interface to the - // execute APIs are ShapedBuffer pointer based, which assumes caller - // ownership, and hence a buffer coming from there cannot be part of - // the new ScopedShapedBuffer we create for the result (which assumes - // ownership). - *device_memory = buffer.Forget(); - } else { - auto output_alias = input_output_alias.GetAliasedOutput( - slice.allocation()->parameter_number(), - slice.allocation()->param_shape_index()); - CHECK(output_alias) - << "Ouput buffer is coming from parameter " - << slice.allocation()->parameter_number() << " at index " - << slice.allocation()->param_shape_index() - << ", but no alias exists"; - CHECK_EQ(*output_alias, index); - } + CHECK(!buffer.is_null() || buffer.size() == 0); + *device_memory = buffer.Forget(); return Status::OK(); })); return std::move(result_buffer); diff --git a/tensorflow/compiler/xla/service/gpu/gpu_executable.cc b/tensorflow/compiler/xla/service/gpu/gpu_executable.cc index 434060ad89..128cecd765 100644 --- a/tensorflow/compiler/xla/service/gpu/gpu_executable.cc +++ b/tensorflow/compiler/xla/service/gpu/gpu_executable.cc @@ -310,34 +310,12 @@ StatusOr GpuExecutable::ExecuteOnStream( TF_ASSIGN_OR_RETURN( const BufferAllocation::Slice slice, this->assignment_->GetUniqueSlice(src_hlo, sources[0]->index())); + CHECK(!slice.allocation()->is_entry_computation_parameter()); se::DeviceMemoryBase src_base = buffer_allocations->GetDeviceAddress(slice.index()); CHECK(!src_base.is_null() || src_base.size() == 0); - if (!slice.allocation()->is_entry_computation_parameter()) { - // If the buffer coming out of the result is from a parameter, it - // means the caller aliased some parameter buffer to an output one - // (via the HloInputOutputAliasConfig API). If that is the case, the - // caller will receive a partially complete scoped shaped buffer, - // which they will have to fill up on return. - // Unfortunately the interface to the execute APIs are ShapedBuffer - // pointer based, which assumes caller ownership, and hence a buffer - // coming from there cannot be part of the new ScopedShapedBuffer we - // create for the result (which assumes ownership). - *device_memory = src_base; - } else { - const HloInputOutputAliasConfig& input_output_alias = - module().input_output_alias_config(); - auto output_alias = input_output_alias.GetAliasedOutput( - slice.allocation()->parameter_number(), - slice.allocation()->param_shape_index()); - CHECK(output_alias) - << "Ouput buffer is coming from parameter " - << slice.allocation()->parameter_number() << " at index " - << slice.allocation()->param_shape_index() - << ", but no alias exists"; - CHECK_EQ(*output_alias, index); - } + *device_memory = src_base; buffers_in_result.insert(src_base); return Status::OK(); })); diff --git a/tensorflow/compiler/xrt/kernels/BUILD b/tensorflow/compiler/xrt/kernels/BUILD index c44769dfe3..67f475846e 100644 --- a/tensorflow/compiler/xrt/kernels/BUILD +++ b/tensorflow/compiler/xrt/kernels/BUILD @@ -55,7 +55,6 @@ cc_library( "//tensorflow/compiler/xla/client:xla_computation", "//tensorflow/compiler/xla/service:compiler", "//tensorflow/compiler/xla/service:computation_placer", - "//tensorflow/compiler/xla/service:hlo", "//tensorflow/compiler/xrt:xrt_proto", "//tensorflow/compiler/xrt:xrt_utils", "//tensorflow/core:core_cpu_internal", diff --git a/tensorflow/compiler/xrt/kernels/xrt_execute_op.cc b/tensorflow/compiler/xrt/kernels/xrt_execute_op.cc index 7544541ca4..7f0ac123a5 100644 --- a/tensorflow/compiler/xrt/kernels/xrt_execute_op.cc +++ b/tensorflow/compiler/xrt/kernels/xrt_execute_op.cc @@ -19,7 +19,6 @@ limitations under the License. #include "tensorflow/compiler/tf2xla/xla_op_registry.h" #include "tensorflow/compiler/xla/service/computation_placer.h" -#include "tensorflow/compiler/xla/service/hlo_input_output_alias_config.h" #include "tensorflow/compiler/xla/shape_util.h" #include "tensorflow/compiler/xla/status_macros.h" #include "tensorflow/compiler/xla/statusor.h" @@ -229,22 +228,6 @@ Status XRTExecuteOp::DoWork(OpKernelContext* context) { TF_RETURN_IF_ERROR(XRTTupleAllocation::CreateFromBuffer( shaped_buffer, device_ref.backend(), device_ref.device_ordinal(), &output_tuple)); - - // The ScopedShapedBuffer returned by the executable Run() API, in case of - // input/output buffer aliasing, might have holes in it, which need to be - // filled using the proper input tuples buffers which are the source of - // aliasing. - const xla::HloInputOutputAliasConfig& input_output_alias = - executable->executable()->module().input_output_alias_config(); - auto alias_function = [&](const xla::ShapeIndex& output_index, - int64 param_number, - const xla::ShapeIndex& param_index) -> Status { - TF_RET_CHECK(param_number < input_tuples.size()); - return output_tuple->AliasBufferFrom(*input_tuples[param_number], - param_index, output_index); - }; - TF_RETURN_IF_ERROR(input_output_alias.ForEachAliasWithStatus(alias_function)); - if (config_proto.return_exploded_tuple() && output_tuple->on_device_shape().IsTuple()) { int64 tuple_element_count = diff --git a/tensorflow/compiler/xrt/tests/raw_api_test.cc b/tensorflow/compiler/xrt/tests/raw_api_test.cc index f81faf0e61..c8479cb778 100644 --- a/tensorflow/compiler/xrt/tests/raw_api_test.cc +++ b/tensorflow/compiler/xrt/tests/raw_api_test.cc @@ -96,21 +96,14 @@ xla::LiteralProto FloatMatrix( return array.ToProto(); } -xla::Literal ReadOutputLiteral(const std::vector& outputs, size_t idx) { - xla::LiteralProto response; - CHECK(response.ParseFromString(outputs[idx].scalar()())); - return xla::Literal::CreateFromProto(response).ValueOrDie(); -} - bool CompareLiteralProtos(const xla::LiteralProto& a, const xla::LiteralProto& b) { auto l_a = xla::Literal::CreateFromProto(a).ValueOrDie(); auto l_b = xla::Literal::CreateFromProto(b).ValueOrDie(); bool equal = l_a == l_b; if (!equal) { - LOG(INFO) << "LiteralProtos don't match:\n" - << a.DebugString() << "\n!=\n" - << b.DebugString(); + LOG(INFO) << "LiteralProtos don't match: " << a.DebugString() + << " != " << b.DebugString(); } return equal; } @@ -120,19 +113,8 @@ bool CompareLiteralToLiteralProto(const xla::Literal& a, auto l_b = xla::Literal::CreateFromProto(b).ValueOrDie(); bool equal = a == l_b; if (!equal) { - LOG(INFO) << "Literal and LiteralProto don't match:\n" - << a.ToProto().DebugString() << "\n!=\n" - << b.DebugString(); - } - return equal; -} - -bool CompareLiterals(const xla::Literal& a, const xla::Literal& b) { - bool equal = a == b; - if (!equal) { - LOG(INFO) << "Literals don't match:\n" - << a.ToProto().DebugString() << "\n!=\n" - << b.ToProto().DebugString(); + LOG(INFO) << "Literal and LiteralProto don't match " + << a.ToProto().DebugString() << " != " << b.DebugString(); } return equal; } @@ -957,107 +939,6 @@ TEST(RawApiTest, LeakCompilationReference) { TF_EXPECT_OK(session.Run({c_handle.handle}, &outputs)); } -TEST(RawApiTest, CompileAndExecuteWithReusedBuffers) { - xla::Shape element_shape = xla::ShapeUtil::MakeShape(xla::F32, {2}); - xla::Shape shape = - xla::ShapeUtil::MakeTupleShape({element_shape, element_shape}); - xla::Shape return_shape = xla::ShapeUtil::MakeTupleShape( - {element_shape, element_shape, element_shape, element_shape}); - xla::XlaBuilder builder("ReuseBuffer"); - auto param = xla::Parameter(&builder, 0, shape, "param"); - auto p0 = xla::GetTupleElement(param, 0); - auto p1 = xla::GetTupleElement(param, 1); - auto add = xla::Add(p0, p1); - auto sub = xla::Sub(p0, p1); - xla::Tuple(&builder, {add, sub, p0, p1}); - - // Flip the tuple literals in the input handle. - builder.SetUpAlias({1}, 0, {0}); - builder.SetUpAlias({0}, 0, {1}); - - auto computation = builder.Build().ValueOrDie(); - - auto literal0 = xla::LiteralUtil::CreateR1({1.0f, 2.0f}); - auto literal1 = xla::LiteralUtil::CreateR1({5.0f, 9.0f}); - auto literal = xla::LiteralUtil::MakeTuple({&literal0, &literal1}); - - xrt::XLAAllocation param_alloc; - *param_alloc.mutable_value() = literal.ToProto(); - - xrt::XLAComputation c; - auto config = c.mutable_config(); - auto shapes = config->mutable_program_shape(); - *shapes->add_parameters() = shape.ToProto(); - *shapes->mutable_result() = return_shape.ToProto(); - StoreComputationSnapshot(computation, c.mutable_hlo_snapshot()); - - xrt::XRTExecutionConfig e; - e.set_release_input_handles(false); - e.set_release_compilation_handle(true); - - Scope root = Scope::NewRootScope().WithDevice(DeviceFromFlag()); - ClientSession session(root); - auto e_config = - ops::Const(root.WithDevice("/device:CPU:0"), e.SerializeAsString()); - auto c_data = - ops::Const(root.WithDevice("/device:CPU:0"), c.SerializeAsString()); - auto c_handle = ops::XRTCompile(root, c_data); - auto param_value = ops::Const(root.WithDevice("/device:CPU:0"), - param_alloc.SerializeAsString()); - auto param_handle = ops::XRTAllocate(root, param_value); - TF_ASSERT_OK(root.status()); - - std::vector outputs; - TF_EXPECT_OK(session.Run({param_handle}, &outputs)); - - int64 alloc_handle = outputs[0].scalar()(); - - // Note that we release the result handle immediately, but since we aliased - // the output buffers onto the input allocation ones (held in alloc_handle), - // we can fetch the result from there. - auto result = - ops::XRTExecute(root, c_handle.handle, e_config, {Input(alloc_handle)}); - auto read_back = ops::XRTReadLiteral(root, result); - auto release = ops::XRTReleaseAllocationHandle( - root.WithControlDependencies(read_back), result); - TF_ASSERT_OK(root.status()); - - outputs.clear(); - TF_EXPECT_OK(session.Run(tensorflow::ClientSession::FeedType(), {read_back}, - {release}, &outputs)); - - xla::Literal exec_literal = ReadOutputLiteral(outputs, 0); - auto exec_literal_parts = exec_literal.DecomposeTuple(); - ASSERT_EQ(exec_literal_parts.size(), 4); - - EXPECT_TRUE(CompareLiterals(exec_literal_parts[2], literal0)); - EXPECT_TRUE(CompareLiterals(exec_literal_parts[3], literal1)); - - // Now we read back the original input handle values, which at this point - // should contain the result of the XLA computation. - auto read_handle = ops::XRTReadLiteral(root, Input(alloc_handle)); - TF_ASSERT_OK(root.status()); - auto release_handle = ops::XRTReleaseAllocationHandle( - root.WithControlDependencies(read_handle), Input(alloc_handle)); - TF_ASSERT_OK(root.status()); - - outputs.clear(); - TF_EXPECT_OK(session.Run(tensorflow::ClientSession::FeedType(), {read_handle}, - {release_handle}, &outputs)); - - xla::Literal return_literal = ReadOutputLiteral(outputs, 0); - - auto expected_literal0 = xla::LiteralUtil::CreateR1({6.0f, 11.0f}); - auto expected_literal1 = xla::LiteralUtil::CreateR1({-4.0f, -7.0f}); - // The first element of the computation returned tuple would be the add - // (expected_literal0), but since we flipped the buffers, the sub - // (expected_literal1) should come first. - auto expected_literal = - xla::LiteralUtil::MakeTuple({&expected_literal1, &expected_literal0}); - - EXPECT_TRUE(CompareLiterals(return_literal, expected_literal)); -} - TEST(RawApiTest, CompileAndExecuteWithS64Argument) { xrt::XLAAllocation p0; *p0.mutable_value() = xla::LiteralUtil::CreateR0(11031965).ToProto(); diff --git a/tensorflow/compiler/xrt/xrt_state.cc b/tensorflow/compiler/xrt/xrt_state.cc index 13c275aaca..343460ff10 100644 --- a/tensorflow/compiler/xrt/xrt_state.cc +++ b/tensorflow/compiler/xrt/xrt_state.cc @@ -133,8 +133,7 @@ Status AllocateScopedShapedBuffer( XRTBufferAllocation::XRTBufferAllocation(const se::DeviceMemoryBase& allocation, int device_ordinal, xla::DeviceMemoryAllocator* allocator) - : size_(allocation.size()), - allocation_(allocation), + : allocation_(allocation), device_ordinal_(device_ordinal), allocator_(allocator) { if (VLOG_IS_ON(2)) { @@ -224,19 +223,8 @@ Status XRTTupleAllocation::ToLiteral(xla::Backend* backend, int device_ordinal, xla::Literal* literal) { auto transfer_manager = backend->transfer_manager(); TF_ASSIGN_OR_RETURN(auto stream, backend->BorrowStream(device_ordinal)); - - // Validate the allocation buffers as if nulls gets to - // TransferLiteralFromDevice() a CHECK is issued. - xla::ShapedBuffer shaped_buffer = ToShapedBuffer(); - for (auto& index_buffer : shaped_buffer.buffers()) { - if (index_buffer.second.is_null()) { - return errors::InvalidArgument("Literal buffer at index ", - index_buffer.first.ToString(), - " has been released"); - } - } TF_ASSIGN_OR_RETURN(*literal, transfer_manager->TransferLiteralFromDevice( - stream.get(), shaped_buffer)); + stream.get(), ToShapedBuffer())); return Status::OK(); } @@ -517,34 +505,11 @@ xla::ShapedBuffer XRTTupleAllocation::ToShapedBuffer() { return shaped_buffer; } -Status XRTTupleAllocation::AliasBufferFrom(const XRTTupleAllocation& source, - const xla::ShapeIndex& source_index, - const xla::ShapeIndex& dest_index) { - XRTBufferAllocation* source_buffer = source.buffers_.element(source_index); - XRTBufferAllocation* dest_buffer = buffers_.element(dest_index); - // We allow the destination size being zero, because there are cases where we - // are coming in later filling in null/uninitialized device buffers. - // In all other cases, the size of the new buffer must match. - if (source_buffer->size() != dest_buffer->size() && - dest_buffer->size() != 0) { - return errors::InvalidArgument( - "Source buffer at index ", source_index.ToString(), - " does not match the size of destination buffer at index ", - dest_index.ToString(), ": ", source_buffer->size(), " vs ", - dest_buffer->size()); - } - *buffers_.mutable_element(dest_index) = source_buffer; - source_buffer->Ref(); - dest_buffer->Unref(); - return Status::OK(); -} - xla::ShapeTree -XRTTupleAllocation::ToDeviceMemoryTree( - const std::function& release_checker) { +XRTTupleAllocation::ToDeviceMemoryTree(bool release) { xla::ShapeTree shaped_tree(on_device_shape()); for (const auto& buffer : buffers_) { - if (!release_checker(buffer.first)) { + if (!release) { *shaped_tree.mutable_element(buffer.first) = buffer.second->allocation(); } else { *shaped_tree.mutable_element(buffer.first) = xla::OwningDeviceMemory( diff --git a/tensorflow/compiler/xrt/xrt_state.h b/tensorflow/compiler/xrt/xrt_state.h index ac4be3a064..3e3d502412 100644 --- a/tensorflow/compiler/xrt/xrt_state.h +++ b/tensorflow/compiler/xrt/xrt_state.h @@ -18,7 +18,6 @@ limitations under the License. #ifndef TENSORFLOW_COMPILER_XRT_XRT_STATE_H_ #define TENSORFLOW_COMPILER_XRT_XRT_STATE_H_ -#include #include #include #include @@ -59,14 +58,7 @@ class XRTBufferAllocation : public core::RefCounted { // freed when the reference count drops to zero. void DiscardAllocation(); - // Returns the expected size of the allocation. Since DiscardAllocation() will - // set allocation_ to {null,0}, and since later we might want to replace the - // discarded buffer with a new one, we need to be able to verify the size - // compatibility. - uint64 size() const { return size_; } - private: - uint64 size_ = 0; se::DeviceMemoryBase allocation_; int device_ordinal_; xla::DeviceMemoryAllocator* allocator_; @@ -176,18 +168,9 @@ class XRTTupleAllocation : public ResourceBase { // the same shape as on_host_shape. xla::ShapedBuffer ToShapedBuffer(); - // Aliases the source buffer at source_index into the current tuple allocation - // dest_index. - Status AliasBufferFrom(const XRTTupleAllocation& source, - const xla::ShapeIndex& source_index, - const xla::ShapeIndex& dest_index); - - // Returns the device memory tree of this allocation. If the release_checker - // function returns true for a given index, the ownership of the device memory - // at that index is transferred to the result. Every attempt to read the value - // at that index will fail. - xla::ShapeTree ToDeviceMemoryTree( - const std::function& release_checker); + // Returns the device memory tree of this allocation. If 'release' is set, the + // ownership of the device memory is transferred to the result. + xla::ShapeTree ToDeviceMemoryTree(bool release); string DebugString() override { return "XLA allocation handle"; } -- GitLab From 47c68e37dee827d9c3fe95f39e5fb49d07a573a5 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Fri, 4 Jan 2019 19:47:17 -0800 Subject: [PATCH 390/622] Automated rollback of commit 57d501422c16dd73b391d19bcc1e203942cc6b24 PiperOrigin-RevId: 227949022 --- tensorflow/contrib/distribute/python/BUILD | 3 +- .../contrib/distribute/python/combinations.py | 15 +-- .../python/keras_correctness_test.py | 100 +++--------------- 3 files changed, 19 insertions(+), 99 deletions(-) diff --git a/tensorflow/contrib/distribute/python/BUILD b/tensorflow/contrib/distribute/python/BUILD index c620a0448e..de6b6f1f84 100644 --- a/tensorflow/contrib/distribute/python/BUILD +++ b/tensorflow/contrib/distribute/python/BUILD @@ -670,12 +670,11 @@ py_library( cuda_py_test( name = "keras_correctness_test", - size = "medium", srcs = ["keras_correctness_test.py"], additional_deps = [ ":keras_correctness_test_lib", ], - shard_count = 20, + shard_count = 16, tags = [ "multi_and_single_gpu", "no_oss", # TODO(b/117919883): Fix python error. diff --git a/tensorflow/contrib/distribute/python/combinations.py b/tensorflow/contrib/distribute/python/combinations.py index 8db0e9c316..f6c4291659 100644 --- a/tensorflow/contrib/distribute/python/combinations.py +++ b/tensorflow/contrib/distribute/python/combinations.py @@ -321,12 +321,11 @@ class NamedDistribution(object): return self._required_tpu -def _get_tpu_strategy_creator(steps_per_run, **kwargs): +def _get_tpu_strategy_creator(steps_per_run): def _create_tpu_strategy(): resolver = cluster_resolver.TPUClusterResolver("") tpu_lib.initialize_tpu_system(resolver) - strategy = tpu_lib.TPUStrategy(resolver, - steps_per_run=steps_per_run, **kwargs) + strategy = tpu_lib.TPUStrategy(resolver, steps_per_run=steps_per_run) return strategy return _create_tpu_strategy @@ -345,15 +344,7 @@ tpu_strategy = NamedDistribution( tpu_strategy_one_step = NamedDistribution( "TPUOneStep", _get_tpu_strategy_creator(steps_per_run=1), required_tpu=True) -# TODO(b/122327153): Remove below two NamedDistributions. -tpu_strategy_loop_on_device = NamedDistribution( - "TPULoopOnDevice", _get_tpu_strategy_creator( - steps_per_run=2, _disable_training_loop_on_host=True), - required_tpu=True) -tpu_strategy_one_step_loop_on_device = NamedDistribution( - "TPUOneStepLoopOnDevice", _get_tpu_strategy_creator( - steps_per_run=1, _disable_training_loop_on_host=True), - required_tpu=True) + mirrored_strategy_with_one_cpu = NamedDistribution( "Mirrored1CPU", lambda: mirrored_lib.MirroredStrategy(["/cpu:0"])) diff --git a/tensorflow/contrib/distribute/python/keras_correctness_test.py b/tensorflow/contrib/distribute/python/keras_correctness_test.py index 1c4519ef71..3abdee2c0e 100644 --- a/tensorflow/contrib/distribute/python/keras_correctness_test.py +++ b/tensorflow/contrib/distribute/python/keras_correctness_test.py @@ -62,41 +62,24 @@ def all_strategy_combinations_with_graph_mode(): return combinations.combine(distribution=all_strategies, mode=['graph']) -def tpu_strategies_with_host_training_loop_disabled(): - strategies = [s for s in all_strategies if not s.required_tpu] - strategies.append(combinations.tpu_strategy_loop_on_device) - strategies.append(combinations.tpu_strategy_one_step_loop_on_device) - return strategies - - def strategy_and_input_combinations(): def cnn_model_with_batch_norm(**kwargs): return _create_cnn_model(with_batch_norm=True, **kwargs) - combinations_without_embedding_model = combinations.times( - combinations.combine( - distribution=tpu_strategies_with_host_training_loop_disabled()), - combinations.combine(mode=['graph', 'eager'], - use_numpy=[True, False], - use_validation_data=[True, False]), - combinations.combine(model_with_data= - [ModelWithData('lstm', - _create_lstm_model, - _lstm_training_data)])) - - combinations_with_embedding_model = combinations.times( - combinations.combine(distribution=all_strategies), - combinations.combine(mode=['graph', 'eager'], - use_numpy=[True, False], - use_validation_data=[True, False]), - combinations.combine(model_with_data=[ - ModelWithData('dnn', _create_dnn_model, _dnn_training_data), - ModelWithData('cnn', _create_cnn_model, _cnn_training_data), - ModelWithData('cnn_batch_norm', cnn_model_with_batch_norm, - _cnn_training_data, with_batch_norm=True),])) - - return (combinations_with_embedding_model + - combinations_without_embedding_model) + return ( + combinations.times( + combinations.combine(distribution=all_strategies), + combinations.combine(mode=['graph', 'eager'], + use_numpy=[True, False], + use_validation_data=[True, False]), + combinations.combine(model_with_data=[ + ModelWithData('dnn', _create_dnn_model, _dnn_training_data), + ModelWithData('cnn', _create_cnn_model, _cnn_training_data), + ModelWithData('cnn_batch_norm', + cnn_model_with_batch_norm, + _cnn_training_data, + with_batch_norm=True), + ]))) class MaybeDistributionScope(object): @@ -208,59 +191,6 @@ def _create_cnn_model(initial_weights=None, distribution=None, return model -def _lstm_training_data(count=_GLOBAL_BATCH_SIZE * _EVAL_STEPS, - min_words=10, - max_words=20, - max_word_id=99, - num_classes=2): - distribution = [] - for _ in range(num_classes): - dist = np.abs(np.random.randn(max_word_id)) - dist /= np.sum(dist) - distribution.append(dist) - - features = [] - labels = [] - for _ in range(count): - label = np.random.randint(0, num_classes, size=1)[0] - num_words = np.random.randint(min_words, max_words, size=1)[0] - word_ids = np.random.choice( - max_word_id, size=num_words, replace=True, p=distribution[label]) - word_ids = word_ids - labels.append(label) - features.append(word_ids) - - features = keras.preprocessing.sequence.pad_sequences( - features, maxlen=max_words) - x_train = np.asarray(features, dtype=np.float32) - y_train = np.asarray(labels, dtype=np.int32).reshape((count, 1)) - x_predict = x_train - return x_train, y_train, x_predict - - -def _create_lstm_model(max_words=20, - initial_weights=None, - distribution=None): - with MaybeDistributionScope(distribution): - word_ids = keras.layers.Input( - shape=(max_words,), dtype=np.int32, name='words') - word_embed = keras.layers.Embedding(input_dim=100, output_dim=10)(word_ids) - lstm_embed = keras.layers.LSTM( - units=8, return_sequences=False)(word_embed) - - preds = keras.layers.Dense(2, activation='softmax')(lstm_embed) - model = keras.Model(inputs=[word_ids], outputs=[preds]) - - if initial_weights: - model.set_weights(initial_weights) - - model.compile( - optimizer=gradient_descent.GradientDescentOptimizer(learning_rate=0.1), - loss='sparse_categorical_crossentropy', - metrics=['sparse_categorical_accuracy']) - return model - - def batch_wrapper(dataset, batch_size, distribution, repeat=None): if repeat: dataset = dataset.repeat(repeat) @@ -313,7 +243,7 @@ def get_correctness_test_inputs(use_numpy, use_validation_data, } else: if len(x_train) < _GLOBAL_BATCH_SIZE * _EVAL_STEPS: - # Currently, we cannot detect the size of a dataset. So, the eval steps is + # Currently, we cannot detech the size of a dataset. So, the eval steps is # hard coded. raise ValueError('x_train must have at least ' '_GLOBAL_BATCH_SIZE * _EVAL_STEPS samples') -- GitLab From 5633ba3fc829a31c297a412929f5953e8c7a1f2f Mon Sep 17 00:00:00 2001 From: Jason Zaman Date: Wed, 2 Jan 2019 18:29:06 +0800 Subject: [PATCH 391/622] cloud oauth_client: update for OpenSSL 1.1.0 compatibility EVP_MD_CTX_cleanup was removed in OpenSSL 1.1.0. There is a call to EVP_MD_CTX_destroy right after and _destroy will call _cleanup if required. EVP_MD_CTX_destroy exists in OpenSSL 1.0, 1.1 and BoringSSL so removing the call to _cleanup works everywhere. Signed-off-by: Jason Zaman --- tensorflow/core/platform/cloud/oauth_client.cc | 6 +++++- tensorflow/core/platform/cloud/oauth_client_test.cc | 1 - 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tensorflow/core/platform/cloud/oauth_client.cc b/tensorflow/core/platform/cloud/oauth_client.cc index 9b85cae9b9..a8657359a3 100644 --- a/tensorflow/core/platform/cloud/oauth_client.cc +++ b/tensorflow/core/platform/cloud/oauth_client.cc @@ -95,6 +95,11 @@ Status CreateSignature(RSA* private_key, StringPiece to_sign, if (!md) { return errors::Internal("Could not get a sha256 encryptor."); } + + // EVP_MD_CTX_destroy is renamed to EVP_MD_CTX_free in OpenSSL 1.1.0 but + // the old name is still retained as a compatibility macro. + // Keep this around until support is dropped for OpenSSL 1.0 + // https://www.openssl.org/news/cl110.txt std::unique_ptr> md_ctx( EVP_MD_CTX_create(), [](EVP_MD_CTX* ptr) { EVP_MD_CTX_destroy(ptr); }); if (!md_ctx) { @@ -119,7 +124,6 @@ Status CreateSignature(RSA* private_key, StringPiece to_sign, if (EVP_DigestSignFinal(md_ctx.get(), sig.get(), &sig_len) != 1) { return errors::Internal("DigestFinal (signature compute) failed."); } - EVP_MD_CTX_cleanup(md_ctx.get()); return Base64Encode(StringPiece(reinterpret_cast(sig.get()), sig_len), signature); } diff --git a/tensorflow/core/platform/cloud/oauth_client_test.cc b/tensorflow/core/platform/cloud/oauth_client_test.cc index 1cd0641cd3..ce3b9d79c8 100644 --- a/tensorflow/core/platform/cloud/oauth_client_test.cc +++ b/tensorflow/core/platform/cloud/oauth_client_test.cc @@ -166,7 +166,6 @@ TEST(OAuthClientTest, GetTokenFromServiceAccountJson) { const_cast( reinterpret_cast(signature.data())), signature.size())); - EVP_MD_CTX_cleanup(md_ctx); // Free all the crypto-related resources. EVP_PKEY_free(key); -- GitLab From 13fc6cfefb8204d5144f4c94b4b8cb50f69e8e8c Mon Sep 17 00:00:00 2001 From: Russell Power Date: Fri, 4 Jan 2019 21:25:17 -0800 Subject: [PATCH 392/622] Grappler micro-optimizations for virtual scheduler. * Only collect additional memory information when VLOG(1) is active. * Use const reference to avoid copy. PiperOrigin-RevId: 227954880 --- .../core/grappler/costs/virtual_scheduler.cc | 26 ++++++++++++------- .../core/grappler/costs/virtual_scheduler.h | 3 +++ .../grappler/costs/virtual_scheduler_test.cc | 4 ++- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/tensorflow/core/grappler/costs/virtual_scheduler.cc b/tensorflow/core/grappler/costs/virtual_scheduler.cc index ae5200b359..7071c679dc 100644 --- a/tensorflow/core/grappler/costs/virtual_scheduler.cc +++ b/tensorflow/core/grappler/costs/virtual_scheduler.cc @@ -319,6 +319,7 @@ VirtualScheduler::VirtualScheduler(const GrapplerItem* grappler_item, placer_(cluster) { graph_costs_.num_ops_total = 0; initialized_ = false; + track_mem_usage_snapshot_ = VLOG_IS_ON(1); } VirtualScheduler::VirtualScheduler(const bool use_static_shapes, @@ -331,6 +332,7 @@ VirtualScheduler::VirtualScheduler(const bool use_static_shapes, placer_(cluster) { graph_costs_.num_ops_total = 0; initialized_ = false; + track_mem_usage_snapshot_ = VLOG_IS_ON(1); } Status VirtualScheduler::Init(const GrapplerItem* item) { @@ -562,7 +564,7 @@ void VirtualScheduler::MaybeUpdateInputOutput(const NodeDef* node) { inputs.push_back(control_message); outputs.push_back(control_message); } else { - auto output_properties = + const auto& output_properties = graph_properties_->GetOutputProperties(NodeName(input_source_name)); // Like with HasInputProperties, if a node does not have output // properties, it's likely it was pruned during the shape inference run. @@ -778,13 +780,16 @@ bool VirtualScheduler::MarkCurrNodeExecuted(const Costs& node_costs) { auto& op_cost = FindOrCreateZero(op_name, &op_to_cost_); op_cost = CombineCosts(op_cost, node_costs); - // Also keep track of op counts and costs per op (with their shapes). - OpContext op_context = GetCurrNode(); - string node_description = GetOpDescription(op_context.op_info); - op_counts_[node_description] += 1; - op_costs_[node_description] = - std::make_pair(node_costs.execution_time.asMicroSeconds().count(), - !node_costs.inaccurate); + if (VLOG_IS_ON(2)) { + // Also keep track of op counts and costs per op (with their shapes). + OpContext op_context = GetCurrNode(); + + string node_description = GetOpDescription(op_context.op_info); + op_counts_[node_description] += 1; + op_costs_[node_description] = + std::make_pair(node_costs.execution_time.asMicroSeconds().count(), + !node_costs.inaccurate); + } // Update node and device states. auto& node_state = node_map_[node]; @@ -868,7 +873,10 @@ bool VirtualScheduler::MarkCurrNodeExecuted(const Costs& node_costs) { // check max memory usage. if (device.memory_usage > device.max_memory_usage) { device.max_memory_usage = device.memory_usage; - device.mem_usage_snapshot_at_peak = device.nodes_in_memory; + + if (track_mem_usage_snapshot_) { + device.mem_usage_snapshot_at_peak = device.nodes_in_memory; + } } } diff --git a/tensorflow/core/grappler/costs/virtual_scheduler.h b/tensorflow/core/grappler/costs/virtual_scheduler.h index 6a835f32d1..f500e4ab51 100644 --- a/tensorflow/core/grappler/costs/virtual_scheduler.h +++ b/tensorflow/core/grappler/costs/virtual_scheduler.h @@ -305,6 +305,8 @@ class VirtualScheduler { return &node_map_; } + void enable_mem_usage_tracking() { track_mem_usage_snapshot_ = true; } + private: // Constants. const string kAttrInputSrc = "input_source_"; @@ -356,6 +358,7 @@ class VirtualScheduler { const GrapplerItem* grappler_item_; // Not owned. bool use_static_shapes_; bool initialized_; + bool track_mem_usage_snapshot_; VirtualPlacer placer_; // owned. }; diff --git a/tensorflow/core/grappler/costs/virtual_scheduler_test.cc b/tensorflow/core/grappler/costs/virtual_scheduler_test.cc index 0a695458e1..c5651715f4 100644 --- a/tensorflow/core/grappler/costs/virtual_scheduler_test.cc +++ b/tensorflow/core/grappler/costs/virtual_scheduler_test.cc @@ -31,7 +31,9 @@ namespace grappler { class TestVirtualScheduler : public VirtualScheduler { public: TestVirtualScheduler(const bool use_static_shapes, Cluster* cluster) - : VirtualScheduler(use_static_shapes, cluster, &ready_node_manager_) {} + : VirtualScheduler(use_static_shapes, cluster, &ready_node_manager_) { + enable_mem_usage_tracking(); + } FRIEND_TEST(VirtualSchedulerTest, MemoryUsage); FRIEND_TEST(VirtualSchedulerTest, ControlDependency); -- GitLab From 20dc97f0f11534adca40d66e5268eb74e012e254 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Fri, 4 Jan 2019 21:37:11 -0800 Subject: [PATCH 393/622] export find_all_hinted_output_nodes for Ophint. PiperOrigin-RevId: 227955652 --- tensorflow/lite/python/convert_test.py | 23 ++++++++++++++++++ tensorflow/lite/python/op_hint.py | 32 +++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/tensorflow/lite/python/convert_test.py b/tensorflow/lite/python/convert_test.py index cf49ee2b47..a29d431322 100644 --- a/tensorflow/lite/python/convert_test.py +++ b/tensorflow/lite/python/convert_test.py @@ -29,6 +29,7 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import test_util from tensorflow.python.framework.graph_util_impl import _bfs_for_reachable_nodes from tensorflow.python.framework.graph_util_impl import _extract_graph_summary +from tensorflow.python.framework.graph_util_impl import _node_name from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops from tensorflow.python.platform import test @@ -389,6 +390,28 @@ class ConvertTestOpHint(test_util.TensorFlowTestCase): with self.assertRaises(ValueError): convert.convert_dtype_to_tflite_type(dtypes.bool) + def testFindHintedOutputNodes(self): + """Test if all hinted output nodes are correctly found.""" + + def _build_ophinted_op(name, input1, input2): + custom_op = op_hint.OpHint(name) + input1 = custom_op.add_input(input1) + input2 = custom_op.add_input(input2) + output = math_ops.mul(input1, input2) + return custom_op.add_output(output) + + output_1 = _build_ophinted_op("custom_op_1", array_ops.constant([1.]), + array_ops.constant([2.])) + output_2 = _build_ophinted_op("custom_op_2", array_ops.constant([3.]), + array_ops.constant([4.])) + with self.cached_session() as sess: + hinted_outputs_nodes = op_hint.find_all_hinted_output_nodes(sess) + expected_hinted_output_nodes = [ + _node_name(output_1.name), + _node_name(output_2.name) + ] + self.assertCountEqual(hinted_outputs_nodes, expected_hinted_output_nodes) + if __name__ == "__main__": test.main() diff --git a/tensorflow/lite/python/op_hint.py b/tensorflow/lite/python/op_hint.py index 8d7f9316bf..6ec050171f 100644 --- a/tensorflow/lite/python/op_hint.py +++ b/tensorflow/lite/python/op_hint.py @@ -964,6 +964,35 @@ def _convert_op_hints_to_stubs_helper( return curr_graph_def +def find_all_hinted_output_nodes(session=None, graph_def=None): + """Find all Ophints output nodes in the graph. + + This is used to get all the output nodes those are ophinted, it is important + for operation like convert_variables_to_constants keep all ophints structure. + Note: only one of session or graph_def should be used, not both. + + Args: + session: A TensorFlow session that contains the graph to convert. + graph_def: A graph def that we should convert. + + Returns: + A list of OpHints output nodes. + Raises: + ValueError: If both session and graph_def are provided. + """ + if session is not None and graph_def is not None: + raise ValueError("Provide only one of session and graph_def.") + hinted_outputs_nodes = [] + if session is not None: + hints = _find_all_hints_in_graph_def(session.graph_def) + elif graph_def is not None: + hints = _find_all_hints_in_graph_def(graph_def) + for hint in _six.itervalues(hints): + _, ouput_nodes = hint.flattened_inputs_and_outputs() + hinted_outputs_nodes.extend(ouput_nodes) + return hinted_outputs_nodes + + def convert_op_hints_to_stubs(session=None, graph_def=None, write_callback=lambda graph_def, comments: None): @@ -996,6 +1025,7 @@ def convert_op_hints_to_stubs(session=None, _allowed_symbols = [ - "OpHint", "convert_op_hints_to_stubs", "convert_op_hints_to_stubs_new" + "OpHint", "convert_op_hints_to_stubs", "convert_op_hints_to_stubs_new", + "find_all_hinted_output_nodes" ] remove_undocumented(__name__, _allowed_symbols) -- GitLab From 058bb7c3512455d2d83d2094a6f6e60b0ca6ee05 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Fri, 4 Jan 2019 22:49:14 -0800 Subject: [PATCH 394/622] Rollback flaky test PiperOrigin-RevId: 227959516 --- tensorflow/core/BUILD | 1 - .../core/util/presized_cuckoo_map_test.cc | 48 ++----------------- 2 files changed, 5 insertions(+), 44 deletions(-) diff --git a/tensorflow/core/BUILD b/tensorflow/core/BUILD index 460353df41..7c9decbd09 100644 --- a/tensorflow/core/BUILD +++ b/tensorflow/core/BUILD @@ -3813,7 +3813,6 @@ tf_cc_tests( "//tensorflow/core/kernels:ops_util", "//third_party/eigen3", "@com_google_absl//absl/base", - "@com_google_absl//absl/time", ], ) diff --git a/tensorflow/core/util/presized_cuckoo_map_test.cc b/tensorflow/core/util/presized_cuckoo_map_test.cc index 8dbb2ec065..f2c7904b00 100644 --- a/tensorflow/core/util/presized_cuckoo_map_test.cc +++ b/tensorflow/core/util/presized_cuckoo_map_test.cc @@ -15,7 +15,6 @@ limitations under the License. #include -#include "absl/time/clock.h" #include "tensorflow/core/platform/env.h" #include "tensorflow/core/platform/fingerprint.h" #include "tensorflow/core/platform/test.h" @@ -53,48 +52,11 @@ TEST(PresizedCuckooMapTest, Basic) { } TEST(PresizedCuckooMapTest, Prefetch) { - { - PresizedCuckooMap pscm(2); - EXPECT_TRUE(pscm.InsertUnique(1, 2)); - // Works for both present and absent keys. - pscm.PrefetchKey(1); - pscm.PrefetchKey(2); - } - - // Do not run in debug mode, when prefetch is not implemented, or when - // sanitizers are enabled. -#if defined(NDEBUG) && defined(__GNUC__) && !defined(ADDRESS_SANITIZER) && \ - !defined(MEMORY_SANITIZER) && !defined(THREAD_SANITIZER) && \ - !defined(UNDEFINED_BEHAVIOR_SANITIZER) - const auto now = [] { return absl::Now(); }; - - // Make size enough to not fit in L2 cache (16.7 Mb) - static constexpr int size = 1 << 22; - PresizedCuckooMap pscm(size); - for (int i = 0; i < size; ++i) { - pscm.InsertUnique(i, i); - } - - absl::Duration no_prefetch, prefetch; - int64 out; - for (int iter = 0; iter < 10; ++iter) { - auto time = now(); - for (int i = 0; i < size; ++i) { - testing::DoNotOptimize(pscm.Find(i, &out)); - } - no_prefetch += now() - time; - - time = now(); - for (int i = 0; i < size; ++i) { - pscm.PrefetchKey(i + 20); - testing::DoNotOptimize(pscm.Find(i, &out)); - } - prefetch += now() - time; - } - - // no_prefetch is at least 30% slower. - EXPECT_GE(1.0 * no_prefetch / prefetch, 1.3); -#endif + PresizedCuckooMap pscm(2); + EXPECT_TRUE(pscm.InsertUnique(1, 2)); + // Works for both present and absent keys. + pscm.PrefetchKey(1); + pscm.PrefetchKey(2); } TEST(PresizedCuckooMapTest, TooManyItems) { -- GitLab From 9e8962b78b3c514cb1163c01fa7ab767592f952f Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Sat, 5 Jan 2019 01:03:08 -0800 Subject: [PATCH 395/622] compat: Update forward compatibility horizon to 2019-01-05 PiperOrigin-RevId: 227969226 --- tensorflow/python/compat/compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/python/compat/compat.py b/tensorflow/python/compat/compat.py index 66c4fa4893..3b808d71a5 100644 --- a/tensorflow/python/compat/compat.py +++ b/tensorflow/python/compat/compat.py @@ -27,7 +27,7 @@ import datetime from tensorflow.python.util import tf_contextlib from tensorflow.python.util.tf_export import tf_export -_FORWARD_COMPATIBILITY_HORIZON = datetime.date(2019, 1, 4) +_FORWARD_COMPATIBILITY_HORIZON = datetime.date(2019, 1, 5) @tf_export("compat.forward_compatible") -- GitLab From f69eca8cef2e8536364bb5a6e5f966a5e071812f Mon Sep 17 00:00:00 2001 From: Skye Wanderman-Milne Date: Sat, 5 Jan 2019 13:29:23 -0800 Subject: [PATCH 396/622] Enable test that works with control flow v2 now. PiperOrigin-RevId: 228006449 --- tensorflow/python/kernel_tests/tensor_array_ops_test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tensorflow/python/kernel_tests/tensor_array_ops_test.py b/tensorflow/python/kernel_tests/tensor_array_ops_test.py index 303e9a006a..3f1bd60d1a 100644 --- a/tensorflow/python/kernel_tests/tensor_array_ops_test.py +++ b/tensorflow/python/kernel_tests/tensor_array_ops_test.py @@ -1005,7 +1005,6 @@ class TensorArrayTest(test.TestCase): self._testWhileLoopWritePackGradients( dynamic_size=True, dtype=dtypes.float32) - @test_util.disable_control_flow_v2("b/119323158") def testGradSerialTwoLoops(self): with self.session(use_gpu=True): def loop(x): -- GitLab From 933109e06a36d1e85daa59214ee37f0e3d170cc8 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Sun, 6 Jan 2019 01:03:37 -0800 Subject: [PATCH 397/622] compat: Update forward compatibility horizon to 2019-01-06 PiperOrigin-RevId: 228038143 --- tensorflow/python/compat/compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/python/compat/compat.py b/tensorflow/python/compat/compat.py index 3b808d71a5..b779c80aa7 100644 --- a/tensorflow/python/compat/compat.py +++ b/tensorflow/python/compat/compat.py @@ -27,7 +27,7 @@ import datetime from tensorflow.python.util import tf_contextlib from tensorflow.python.util.tf_export import tf_export -_FORWARD_COMPATIBILITY_HORIZON = datetime.date(2019, 1, 5) +_FORWARD_COMPATIBILITY_HORIZON = datetime.date(2019, 1, 6) @tf_export("compat.forward_compatible") -- GitLab From a0804de99207eb89b38a315b7baa0eb2976931c7 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Sun, 6 Jan 2019 03:06:52 -0800 Subject: [PATCH 398/622] Internal change PiperOrigin-RevId: 228045421 --- tensorflow/contrib/BUILD | 1 - tensorflow/contrib/__init__.py | 1 - 2 files changed, 2 deletions(-) diff --git a/tensorflow/contrib/BUILD b/tensorflow/contrib/BUILD index 832db0f4ab..307bb6eca3 100644 --- a/tensorflow/contrib/BUILD +++ b/tensorflow/contrib/BUILD @@ -63,7 +63,6 @@ py_library( "//tensorflow/contrib/libsvm", "//tensorflow/contrib/linear_optimizer:sdca_estimator_py", "//tensorflow/contrib/linear_optimizer:sdca_ops_py", - "//tensorflow/contrib/lite/python:lite", "//tensorflow/contrib/lookup:lookup_py", "//tensorflow/contrib/losses:losses_py", "//tensorflow/contrib/losses:metric_learning_py", diff --git a/tensorflow/contrib/__init__.py b/tensorflow/contrib/__init__.py index 4f1a2a5693..af59686120 100644 --- a/tensorflow/contrib/__init__.py +++ b/tensorflow/contrib/__init__.py @@ -91,7 +91,6 @@ from tensorflow.contrib import tpu from tensorflow.contrib import training from tensorflow.contrib import util from tensorflow.contrib.eager.python import tfe as eager -from tensorflow.contrib.lite.python import lite from tensorflow.contrib.optimizer_v2 import optimizer_v2_symbols as optimizer_v2 from tensorflow.contrib.receptive_field import receptive_field_api as receptive_field from tensorflow.contrib.recurrent.python import recurrent_api as recurrent -- GitLab From c67e5c39503c4ca2f1b383acfe55cd4fd8fba37d Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Sun, 6 Jan 2019 03:15:34 -0800 Subject: [PATCH 399/622] Extend config generation to include a default CPU configuration and provide a platform definition for this configuration. We create a new platform instead of changing the old one so we can transition without the need to change all users atomically. PiperOrigin-RevId: 228045771 --- third_party/toolchains/BUILD | 28 ++++++++-- .../toolchains/preconfig/generate/BUILD | 6 ++ .../preconfig/generate/containers.bzl | 1 + .../preconfig/generate/generate.bzl | 55 ++++++++++++------- .../toolchains/preconfig/generate/generate.sh | 31 ++++++++--- .../preconfig/generate/workspace.bzl | 7 +++ 6 files changed, 96 insertions(+), 32 deletions(-) diff --git a/third_party/toolchains/BUILD b/third_party/toolchains/BUILD index f2248341c4..6ed6e5c367 100644 --- a/third_party/toolchains/BUILD +++ b/third_party/toolchains/BUILD @@ -4,10 +4,9 @@ package(default_visibility = ["//visibility:public"]) load("//third_party/toolchains/preconfig/generate:containers.bzl", "container_digests") -# Platform for use with remote execution with -# custom container based off RBE Ubuntu16_04 -# http://gcr.io/cloud-marketplace/google/rbe-ubuntu16-04 -# Built with //tensorflow/tools/ci_build/Dockerfile.rbe.cpu +# TODO(b/122347293): This is the RBE config based on the CPU configuration / image provided +# in the asci-toolchain setup. Delete this once we switched CPU remote builds to the +# new platform below. platform( name = "rbe_ubuntu16_04-tf", constraint_values = [ @@ -23,6 +22,26 @@ platform( }""", ) +# Remote build platforms. +# Each of the platform rules here provide a platform definition that is bound to a docker image. +# The result of the skylark configuration is checked into +# //tensorflow/third_party/toolchains/preconfig. + +# Built with //tensorflow/tools/ci_build/Dockerfile.rbe.cpu. +platform( + name = "rbe_ubuntu16.04", + constraint_values = [ + "@bazel_tools//platforms:x86_64", + "@bazel_tools//platforms:linux", + ], + remote_execution_properties = """ + properties: { + name: "container-image" + value:"docker://gcr.io/tensorflow-testing/nosla-ubuntu16.04@%s" + }""" % container_digests["ubuntu16.04"], +) + +# Built with //tensorflow/tools/ci_build/Dockerfile.rbe.cuda9.0-cudnn7-ubuntu14.04. platform( name = "rbe_cuda9.0-cudnn7-ubuntu14.04", constraint_values = [ @@ -36,6 +55,7 @@ platform( }""" % container_digests["cuda9.0-cudnn7-ubuntu14.04"], ) +# Built with //tensorflow/tools/ci_build/Dockerfile.rbe.cuda10.0-cudnn7-ubuntu14.04. platform( name = "rbe_cuda10.0-cudnn7-ubuntu14.04", constraint_values = [ diff --git a/third_party/toolchains/preconfig/generate/BUILD b/third_party/toolchains/preconfig/generate/BUILD index b4c98dc94d..ad79255251 100644 --- a/third_party/toolchains/preconfig/generate/BUILD +++ b/third_party/toolchains/preconfig/generate/BUILD @@ -2,6 +2,12 @@ licenses(["restricted"]) load(":generate.bzl", "tensorflow_rbe_config") +tensorflow_rbe_config( + name = "ubuntu16.04-py3-clang", + compiler = "clang", + python_version = "3", +) + tensorflow_rbe_config( name = "ubuntu14.04-py3-gcc-cuda9.0-cudnn7-tensorrt5", compiler = "gcc", diff --git a/third_party/toolchains/preconfig/generate/containers.bzl b/third_party/toolchains/preconfig/generate/containers.bzl index 67e43485ed..428208523b 100644 --- a/third_party/toolchains/preconfig/generate/containers.bzl +++ b/third_party/toolchains/preconfig/generate/containers.bzl @@ -1,4 +1,5 @@ container_digests = { + "ubuntu16.04": "sha256:d0d98c53111c3ec071aa81632a2b0d6f210e5c2411c5172e31f99002125ec4de", "cuda9.0-cudnn7-ubuntu14.04": "sha256:006a76ee1838122ff7f21ebac85f24c1ef350d4dd79b3ceff0e4fe649ed90d33", "cuda10.0-cudnn7-ubuntu14.04": "sha256:e36f05f1ff39e39ddf07122e37f2b1895948bb6f7acc3db37a3c496be5e66228", } diff --git a/third_party/toolchains/preconfig/generate/generate.bzl b/third_party/toolchains/preconfig/generate/generate.bzl index 75deea41b8..fc485d43d2 100644 --- a/third_party/toolchains/preconfig/generate/generate.bzl +++ b/third_party/toolchains/preconfig/generate/generate.bzl @@ -3,30 +3,38 @@ load( "docker_toolchain_autoconfig", ) -def _tensorflow_rbe_config(name, cuda_version, cudnn_version, python_version, compiler, tensorrt_version): - docker_toolchain_autoconfig( - name = name, - base = "@cuda%s-cudnn%s-ubuntu14.04//image" % (cuda_version, cudnn_version), - bazel_version = "0.19.2", +def _tensorflow_rbe_config(name, compiler, python_version, cuda_version = None, cudnn_version = None, tensorrt_version = None): + base = "@ubuntu16.04//image" + config_repos = [ + "local_config_python", + "local_config_cc", + ] + env = { + "ABI_VERSION": "gcc", + "ABI_LIBC_VERSION": "glibc_2.19", + "BAZEL_COMPILER": compiler, + "BAZEL_HOST_SYSTEM": "i686-unknown-linux-gnu", + "BAZEL_TARGET_LIBC": "glibc_2.19", + "BAZEL_TARGET_CPU": "k8", + "BAZEL_TARGET_SYSTEM": "x86_64-unknown-linux-gnu", + "CC_TOOLCHAIN_NAME": "linux_gnu_x86", + "CC": compiler, + "PYTHON_BIN_PATH": "/usr/bin/python%s" % python_version, + "CLEAR_CACHE": "1", + } + + if cuda_version != None: + base = "@cuda%s-cudnn%s-ubuntu14.04//image" % (cuda_version, cudnn_version) + # The cuda toolchain currently contains its own C++ toolchain definition, + # so we do not fetch local_config_cc. config_repos = [ - "local_config_cuda", "local_config_python", + "local_config_cuda", "local_config_tensorrt", - ], - env = { - "ABI_VERSION": "gcc", - "ABI_LIBC_VERSION": "glibc_2.19", - "BAZEL_COMPILER": compiler, - "BAZEL_HOST_SYSTEM": "i686-unknown-linux-gnu", - "BAZEL_TARGET_LIBC": "glibc_2.19", - "BAZEL_TARGET_CPU": "k8", - "BAZEL_TARGET_SYSTEM": "x86_64-unknown-linux-gnu", - "CC_TOOLCHAIN_NAME": "linux_gnu_x86", - "CC": compiler, - "PYTHON_BIN_PATH": "/usr/bin/python%s" % python_version, + ] + env.update({ "TF_NEED_CUDA": "1", "TF_CUDA_CLANG": "1" if compiler == "clang" else "0", - "CLEAR_CACHE": "1", "TF_CUDA_COMPUTE_CAPABILITIES": "3.0", "TF_ENABLE_XLA": "1", "TF_CUDNN_VERSION": cudnn_version, @@ -35,7 +43,14 @@ def _tensorflow_rbe_config(name, cuda_version, cudnn_version, python_version, co "TF_NEED_TENSORRT" : "1", "TF_TENSORRT_VERSION": tensorrt_version, "TENSORRT_INSTALL_PATH": "/usr/lib/x86_64-linux-gnu", - }, + }) + + docker_toolchain_autoconfig( + name = name, + base = base, + bazel_version = "0.21.0", + config_repos = config_repos, + env = env, mount_project = "$(mount_project)", tags = ["manual"], incompatible_changes_off = True, diff --git a/third_party/toolchains/preconfig/generate/generate.sh b/third_party/toolchains/preconfig/generate/generate.sh index 8e3a1e6ada..c05a4de6fb 100755 --- a/third_party/toolchains/preconfig/generate/generate.sh +++ b/third_party/toolchains/preconfig/generate/generate.sh @@ -37,8 +37,16 @@ TENSORRT_VERSION="${PLATFORM[5]}" # TODO(klimek): Put this into the name. -if [[ "${COMPILER}" == "gcc" ]]; then - COMPILER="gcc-nvcc-${CUDA_VERSION}" +if [[ -n "${CUDA_VERSION}" ]]; then + if [[ "${COMPILER}" == "gcc" ]]; then + COMPILER="gcc-nvcc-${CUDA_VERSION}" + fi + # Currently we create a special toolchain for clang when compiling with + # cuda enabled. We can get rid of this once the default toolchain bazel + # provides supports cuda. + if [[ "${COMPILER}" == "clang" ]]; then + COMPILER="cuda-clang" + fi fi echo "OS: ${OS}" @@ -52,6 +60,8 @@ bazel build --define=mount_project="${PWD}" "${PKG}/generate:${TARGET}" cd "${TEMPDIR}" tar xvf "${ROOT}/bazel-bin/${PKG}/generate/${TARGET}_outputs.tar" +# TODO(klimek): The skylark config rules should copy the files instead of +# creating aliases. # Other than in @local_config_tensorrt, the header files in the remote config # repo are not relative to the repository root. Add a dummy include_prefix to # make them available as virtual includes. @@ -74,14 +84,19 @@ mkdir "${OS}" # Python: mv local_config_python "${OS}/${PY_VERSION}" -# Compiler: -mv local_config_cuda/crosstool "${OS}/${COMPILER}" +if [[ -n "${CUDA_VERSION}" ]]; then + # Compiler: + mv local_config_cuda/crosstool "${OS}/${COMPILER}" -# CUDA: -mv local_config_cuda "${OS}/${CUDA_VERSION}-${CUDNN_VERSION}" + # CUDA: + mv local_config_cuda "${OS}/${CUDA_VERSION}-${CUDNN_VERSION}" -# TensorRT: -mv local_config_tensorrt "${OS}/${TENSORRT_VERSION}" + # TensorRT: + mv local_config_tensorrt "${OS}/${TENSORRT_VERSION}" +else + # Compiler: + mv local_config_cc "${OS}/${COMPILER}" +fi # Cleanup for copybara. find "${OS}" -name 'BUILD' -o -name '*.bzl' |xargs buildifier diff --git a/third_party/toolchains/preconfig/generate/workspace.bzl b/third_party/toolchains/preconfig/generate/workspace.bzl index eb74022c24..0495173786 100644 --- a/third_party/toolchains/preconfig/generate/workspace.bzl +++ b/third_party/toolchains/preconfig/generate/workspace.bzl @@ -8,6 +8,13 @@ load(":containers.bzl", "container_digests") def _remote_config_workspace(): container_repositories() + container_pull( + name = "ubuntu16.04", + registry = "gcr.io", + repository = "tensorflow-testing/nosla-ubuntu16.04", + digest = container_digests["ubuntu16.04"], + ) + container_pull( name = "cuda9.0-cudnn7-ubuntu14.04", registry = "gcr.io", -- GitLab From 92985e67564c119203f0aaca94b5a334d87c35e4 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Sun, 6 Jan 2019 04:03:21 -0800 Subject: [PATCH 400/622] Add clang and python3 configurations for ubuntu 16.04. PiperOrigin-RevId: 228048061 --- tensorflow/opensource_only.files | 3 + .../preconfig/ubuntu16.04/clang/BUILD | 111 ++ .../preconfig/ubuntu16.04/clang/CROSSTOOL | 1209 +++++++++++++++++ .../preconfig/ubuntu16.04/clang/WORKSPACE | 2 + .../preconfig/ubuntu16.04/clang/cc_wrapper.sh | 25 + .../ubuntu16.04/clang/dummy_toolchain.bzl | 23 + .../ubuntu16.04/clang/tools/cpp/empty.cc | 1 + .../preconfig/ubuntu16.04/py3/BUILD | 205 +++ .../preconfig/ubuntu16.04/py3/WORKSPACE | 2 + 9 files changed, 1581 insertions(+) create mode 100755 third_party/toolchains/preconfig/ubuntu16.04/clang/BUILD create mode 100755 third_party/toolchains/preconfig/ubuntu16.04/clang/CROSSTOOL create mode 100644 third_party/toolchains/preconfig/ubuntu16.04/clang/WORKSPACE create mode 100755 third_party/toolchains/preconfig/ubuntu16.04/clang/cc_wrapper.sh create mode 100755 third_party/toolchains/preconfig/ubuntu16.04/clang/dummy_toolchain.bzl create mode 100755 third_party/toolchains/preconfig/ubuntu16.04/clang/tools/cpp/empty.cc create mode 100755 third_party/toolchains/preconfig/ubuntu16.04/py3/BUILD create mode 100644 third_party/toolchains/preconfig/ubuntu16.04/py3/WORKSPACE diff --git a/tensorflow/opensource_only.files b/tensorflow/opensource_only.files index 1054c285d6..e00d063cfe 100644 --- a/tensorflow/opensource_only.files +++ b/tensorflow/opensource_only.files @@ -53,6 +53,9 @@ tensorflow/third_party/toolchains/preconfig/generate/containers.bzl tensorflow/third_party/toolchains/preconfig/generate/generate.bzl tensorflow/third_party/toolchains/preconfig/generate/archives.bzl tensorflow/third_party/toolchains/preconfig/generate/BUILD +tensorflow/third_party/toolchains/preconfig/ubuntu16.04/clang/BUILD +tensorflow/third_party/toolchains/preconfig/ubuntu16.04/clang/dummy_toolchain.bzl +tensorflow/third_party/toolchains/preconfig/ubuntu16.04/py3/BUILD tensorflow/third_party/toolchains/preconfig/win_1803/bazel_018/BUILD tensorflow/third_party/toolchains/preconfig/win_1803/bazel_018/dummy_toolchain.bzl tensorflow/third_party/toolchains/preconfig/win_1803/py36/BUILD diff --git a/third_party/toolchains/preconfig/ubuntu16.04/clang/BUILD b/third_party/toolchains/preconfig/ubuntu16.04/clang/BUILD new file mode 100755 index 0000000000..5a0c52f66a --- /dev/null +++ b/third_party/toolchains/preconfig/ubuntu16.04/clang/BUILD @@ -0,0 +1,111 @@ +# Copyright 2016 The Bazel 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. + +# This becomes the BUILD file for @local_config_cc// under non-FreeBSD unixes. + +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) # Apache 2.0 + +cc_library( + name = "malloc", +) + +cc_library( + name = "stl", +) + +filegroup( + name = "empty", + srcs = [], +) + +filegroup( + name = "cc_wrapper", + srcs = ["cc_wrapper.sh"], +) + +filegroup( + name = "compiler_deps", + srcs = glob(["extra_tools/**"]) + [":empty"], +) + +# This is the entry point for --crosstool_top. Toolchains are found +# by lopping off the name of --crosstool_top and searching for +# the "${CPU}" entry in the toolchains attribute. +cc_toolchain_suite( + name = "toolchain", + toolchains = { + "k8|clang": ":cc-compiler-k8", + "k8": ":cc-compiler-k8", + "armeabi-v7a|compiler": ":cc-compiler-armeabi-v7a", + "armeabi-v7a": ":cc-compiler-armeabi-v7a", + }, +) + +cc_toolchain( + name = "cc-compiler-k8", + all_files = ":compiler_deps", + compiler_files = ":compiler_deps", + cpu = "k8", + dwp_files = ":empty", + dynamic_runtime_libs = [":empty"], + linker_files = ":compiler_deps", + objcopy_files = ":empty", + static_runtime_libs = [":empty"], + strip_files = ":empty", + supports_param_files = 1, + toolchain_identifier = "linux_gnu_x86", +) + +toolchain( + name = "cc-toolchain-k8", + exec_compatible_with = [ + # TODO(katre): add autodiscovered constraints for host CPU and OS. + ], + target_compatible_with = [ + # TODO(katre): add autodiscovered constraints for host CPU and OS. + ], + toolchain = ":cc-compiler-k8", + toolchain_type = "@bazel_tools//tools/cpp:toolchain_type", +) + +# Android tooling requires a default toolchain for the armeabi-v7a cpu. +cc_toolchain( + name = "cc-compiler-armeabi-v7a", + all_files = ":empty", + compiler_files = ":empty", + cpu = "local", + dwp_files = ":empty", + dynamic_runtime_libs = [":empty"], + linker_files = ":empty", + objcopy_files = ":empty", + static_runtime_libs = [":empty"], + strip_files = ":empty", + supports_param_files = 1, + toolchain_identifier = "stub_armeabi-v7a", +) + +toolchain( + name = "cc-toolchain-armeabi-v7a", + exec_compatible_with = [ + # TODO(katre): add autodiscovered constraints for host CPU and OS. + ], + target_compatible_with = [ + "@bazel_tools//platforms:arm", + "@bazel_tools//platforms:android", + ], + toolchain = ":cc-compiler-armabi-v7a", + toolchain_type = "@bazel_tools//tools/cpp:toolchain_type", +) diff --git a/third_party/toolchains/preconfig/ubuntu16.04/clang/CROSSTOOL b/third_party/toolchains/preconfig/ubuntu16.04/clang/CROSSTOOL new file mode 100755 index 0000000000..48f82eb35d --- /dev/null +++ b/third_party/toolchains/preconfig/ubuntu16.04/clang/CROSSTOOL @@ -0,0 +1,1209 @@ +# Copyright 2016 The Bazel 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. + +major_version: "local" +minor_version: "" + +# Android tooling requires a default toolchain for the armeabi-v7a cpu. +toolchain { + abi_version: "armeabi-v7a" + abi_libc_version: "armeabi-v7a" + builtin_sysroot: "" + compiler: "compiler" + host_system_name: "armeabi-v7a" + needsPic: true + supports_gold_linker: false + supports_incremental_linker: false + supports_fission: false + supports_interface_shared_objects: false + supports_normalizing_ar: false + supports_start_end_lib: false + target_libc: "armeabi-v7a" + target_cpu: "armeabi-v7a" + target_system_name: "armeabi-v7a" + toolchain_identifier: "stub_armeabi-v7a" + + tool_path { name: "ar" path: "/bin/false" } + tool_path { name: "compat-ld" path: "/bin/false" } + tool_path { name: "cpp" path: "/bin/false" } + tool_path { name: "dwp" path: "/bin/false" } + tool_path { name: "gcc" path: "/bin/false" } + tool_path { name: "gcov" path: "/bin/false" } + tool_path { name: "ld" path: "/bin/false" } + + tool_path { name: "nm" path: "/bin/false" } + tool_path { name: "objcopy" path: "/bin/false" } + tool_path { name: "objdump" path: "/bin/false" } + tool_path { name: "strip" path: "/bin/false" } + linking_mode_flags { mode: DYNAMIC } +} + +toolchain { + toolchain_identifier: "linux_gnu_x86" + abi_version: "gcc" + abi_libc_version: "glibc_2.19" + builtin_sysroot: "" + compiler: "clang" + host_system_name: "i686-unknown-linux-gnu" + needsPic: true + supports_gold_linker: true + supports_incremental_linker: false + supports_fission: false + supports_interface_shared_objects: false + supports_normalizing_ar: false + supports_start_end_lib: true + target_libc: "glibc_2.19" + target_cpu: "k8" + target_system_name: "x86_64-unknown-linux-gnu" + cxx_flag: "-std=c++0x" + linker_flag: "-fuse-ld=gold" + linker_flag: "-Wl,-no-as-needed" + linker_flag: "-Wl,-z,relro,-z,now" + linker_flag: "-B/usr/local/bin" + linker_flag: "-lstdc++" + linker_flag: "-lm" + cxx_builtin_include_directory: "/usr/local/include" + cxx_builtin_include_directory: "/usr/local/lib/clang/7.0.0/include" + cxx_builtin_include_directory: "/usr/include/x86_64-linux-gnu" + cxx_builtin_include_directory: "/usr/include" + cxx_builtin_include_directory: "/usr/include/c++/4.9" + cxx_builtin_include_directory: "/usr/include/x86_64-linux-gnu/c++/4.9" + cxx_builtin_include_directory: "/usr/include/c++/4.9/backward" + objcopy_embed_flag: "-I" + objcopy_embed_flag: "binary" + unfiltered_cxx_flag: "-no-canonical-prefixes" + unfiltered_cxx_flag: "-Wno-builtin-macro-redefined" + unfiltered_cxx_flag: "-D__DATE__=\"redacted\"" + unfiltered_cxx_flag: "-D__TIMESTAMP__=\"redacted\"" + unfiltered_cxx_flag: "-D__TIME__=\"redacted\"" + compiler_flag: "-U_FORTIFY_SOURCE" + compiler_flag: "-fstack-protector" + compiler_flag: "-Wall" + compiler_flag: "-Wthread-safety" + compiler_flag: "-Wself-assign" + compiler_flag: "-fcolor-diagnostics" + compiler_flag: "-fno-omit-frame-pointer" + tool_path {name: "ar" path: "/usr/bin/ar" } + tool_path {name: "ld" path: "/usr/bin/ld" } + tool_path {name: "cpp" path: "/usr/bin/cpp" } + tool_path {name: "gcc" path: "/usr/local/bin/clang" } + tool_path {name: "dwp" path: "/usr/bin/dwp" } + tool_path {name: "gcov" path: "None" } + tool_path {name: "nm" path: "/usr/bin/nm" } + tool_path {name: "objcopy" path: "/usr/bin/objcopy" } + tool_path {name: "objdump" path: "/usr/bin/objdump" } + tool_path {name: "strip" path: "/usr/bin/strip" } + + compilation_mode_flags { + mode: DBG + compiler_flag: "-g" + } + compilation_mode_flags { + mode: OPT + compiler_flag: "-g0" + compiler_flag: "-O2" + compiler_flag: "-D_FORTIFY_SOURCE=1" + compiler_flag: "-DNDEBUG" + compiler_flag: "-ffunction-sections" + compiler_flag: "-fdata-sections" + linker_flag: "-Wl,--gc-sections" + } + linking_mode_flags { mode: DYNAMIC } + + + feature { + name: 'coverage' + provides: 'profile' + flag_set { + action: 'preprocess-assemble' + action: 'c-compile' + action: 'c++-compile' + action: 'c++-header-parsing' + action: 'c++-module-compile' + flag_group { + flag: '--coverage' + } + } + flag_set { + action: 'c++-link-dynamic-library' + action: 'c++-link-nodeps-dynamic-library' + action: 'c++-link-executable' + flag_group { + flag: '--coverage' + } + } + } + + + feature { + name: 'fdo_optimize' + provides: 'profile' + flag_set { + action: 'c-compile' + action: 'c++-compile' + expand_if_all_available: 'fdo_profile_path' + flag_group { + flag: '-fprofile-use=%{fdo_profile_path}' + flag: '-fprofile-correction', + } + } + } +} + +toolchain { + toolchain_identifier: "msys_x64_mingw" + abi_version: "local" + abi_libc_version: "local" + builtin_sysroot: "" + compiler: "mingw-gcc" + host_system_name: "local" + needsPic: false + target_libc: "mingw" + target_cpu: "x64_windows" + target_system_name: "local" + + artifact_name_pattern { + category_name: 'executable' + prefix: '' + extension: '.exe' + } + + + + linking_mode_flags { mode: DYNAMIC } +} + +toolchain { + toolchain_identifier: "msvc_x64" + host_system_name: "local" + target_system_name: "local" + + abi_version: "local" + abi_libc_version: "local" + target_cpu: "x64_windows" + compiler: "msvc-cl" + target_libc: "msvcrt" + default_python_version: "python2.7" + + + + tool_path { + name: "ar" + path: "" + } + tool_path { + name: "ml" + path: "" + } + tool_path { + name: "cpp" + path: "" + } + tool_path { + name: "gcc" + path: "" + } + tool_path { + name: "gcov" + path: "wrapper/bin/msvc_nop.bat" + } + tool_path { + name: "ld" + path: "" + } + tool_path { + name: "nm" + path: "wrapper/bin/msvc_nop.bat" + } + tool_path { + name: "objcopy" + path: "wrapper/bin/msvc_nop.bat" + } + tool_path { + name: "objdump" + path: "wrapper/bin/msvc_nop.bat" + } + tool_path { + name: "strip" + path: "wrapper/bin/msvc_nop.bat" + } + supports_gold_linker: false + supports_start_end_lib: false + supports_interface_shared_objects: true + supports_incremental_linker: false + supports_normalizing_ar: true + needsPic: false + + # TODO(pcloudy): Review those flags below, they should be defined by cl.exe + compiler_flag: "/DCOMPILER_MSVC" + + # Don't define min/max macros in windows.h. + compiler_flag: "/DNOMINMAX" + + # Platform defines. + compiler_flag: "/D_WIN32_WINNT=0x0601" + # Turn off warning messages. + compiler_flag: "/D_CRT_SECURE_NO_DEPRECATE" + compiler_flag: "/D_CRT_SECURE_NO_WARNINGS" + + # Useful options to have on for compilation. + # Increase the capacity of object files to 2^32 sections. + compiler_flag: "/bigobj" + # Allocate 500MB for precomputed headers. + compiler_flag: "/Zm500" + # Catch C++ exceptions only and tell the compiler to assume that functions declared + # as extern "C" never throw a C++ exception. + compiler_flag: "/EHsc" + + # Globally disabled warnings. + # Don't warn about elements of array being be default initialized. + compiler_flag: "/wd4351" + # Don't warn about no matching delete found. + compiler_flag: "/wd4291" + # Don't warn about diamond inheritance patterns. + compiler_flag: "/wd4250" + # Don't warn about insecure functions (e.g. non _s functions). + compiler_flag: "/wd4996" + + linker_flag: "/MACHINE:X64" + + feature { + name: "no_legacy_features" + } + + artifact_name_pattern { + category_name: 'object_file' + prefix: '' + extension: '.obj' + } + + artifact_name_pattern { + category_name: 'static_library' + prefix: '' + extension: '.lib' + } + + artifact_name_pattern { + category_name: 'alwayslink_static_library' + prefix: '' + extension: '.lo.lib' + } + + artifact_name_pattern { + category_name: 'executable' + prefix: '' + extension: '.exe' + } + + artifact_name_pattern { + category_name: 'dynamic_library' + prefix: '' + extension: '.dll' + } + + artifact_name_pattern { + category_name: 'interface_library' + prefix: '' + extension: '.if.lib' + } + + # Suppress startup banner. + feature { + name: "nologo" + flag_set { + action: "c-compile" + action: "c++-compile" + action: "c++-module-compile" + action: "c++-module-codegen" + action: "c++-header-parsing" + action: "assemble" + action: "preprocess-assemble" + action: "c++-link-executable" + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + action: "c++-link-static-library" + flag_group { + flag: "/nologo" + } + } + } + + feature { + name: 'has_configured_linker_path' + } + + # This feature indicates strip is not supported, building stripped binary will just result a copy of orignial binary + feature { + name: 'no_stripping' + } + + # This feature indicates this is a toolchain targeting Windows. + feature { + name: 'targets_windows' + implies: 'copy_dynamic_libraries_to_binary' + enabled: true + } + + feature { + name: 'copy_dynamic_libraries_to_binary' + } + + action_config { + config_name: 'assemble' + action_name: 'assemble' + tool { + tool_path: '' + } + implies: 'compiler_input_flags' + implies: 'compiler_output_flags' + implies: 'nologo' + implies: 'msvc_env' + implies: 'sysroot' + } + + action_config { + config_name: 'preprocess-assemble' + action_name: 'preprocess-assemble' + tool { + tool_path: '' + } + implies: 'compiler_input_flags' + implies: 'compiler_output_flags' + implies: 'nologo' + implies: 'msvc_env' + implies: 'sysroot' + } + + action_config { + config_name: 'c-compile' + action_name: 'c-compile' + tool { + tool_path: '' + } + implies: 'compiler_input_flags' + implies: 'compiler_output_flags' + implies: 'legacy_compile_flags' + implies: 'nologo' + implies: 'msvc_env' + implies: 'parse_showincludes' + implies: 'user_compile_flags' + implies: 'sysroot' + implies: 'unfiltered_compile_flags' + } + + action_config { + config_name: 'c++-compile' + action_name: 'c++-compile' + tool { + tool_path: '' + } + implies: 'compiler_input_flags' + implies: 'compiler_output_flags' + implies: 'legacy_compile_flags' + implies: 'nologo' + implies: 'msvc_env' + implies: 'parse_showincludes' + implies: 'user_compile_flags' + implies: 'sysroot' + implies: 'unfiltered_compile_flags' + } + + action_config { + config_name: 'c++-link-executable' + action_name: 'c++-link-executable' + tool { + tool_path: '' + } + implies: 'nologo' + implies: 'linkstamps' + implies: 'output_execpath_flags' + implies: 'input_param_flags' + implies: 'user_link_flags' + implies: 'legacy_link_flags' + implies: 'linker_subsystem_flag' + implies: 'linker_param_file' + implies: 'msvc_env' + implies: 'no_stripping' + } + + action_config { + config_name: 'c++-link-dynamic-library' + action_name: 'c++-link-dynamic-library' + tool { + tool_path: '' + } + implies: 'nologo' + implies: 'shared_flag' + implies: 'linkstamps' + implies: 'output_execpath_flags' + implies: 'input_param_flags' + implies: 'user_link_flags' + implies: 'legacy_link_flags' + implies: 'linker_subsystem_flag' + implies: 'linker_param_file' + implies: 'msvc_env' + implies: 'no_stripping' + implies: 'has_configured_linker_path' + implies: 'def_file' + } + + action_config { + config_name: 'c++-link-nodeps-dynamic-library' + action_name: 'c++-link-nodeps-dynamic-library' + tool { + tool_path: '' + } + implies: 'nologo' + implies: 'shared_flag' + implies: 'linkstamps' + implies: 'output_execpath_flags' + implies: 'input_param_flags' + implies: 'user_link_flags' + implies: 'legacy_link_flags' + implies: 'linker_subsystem_flag' + implies: 'linker_param_file' + implies: 'msvc_env' + implies: 'no_stripping' + implies: 'has_configured_linker_path' + implies: 'def_file' + } + + action_config { + config_name: 'c++-link-static-library' + action_name: 'c++-link-static-library' + tool { + tool_path: '' + } + implies: 'nologo' + implies: 'archiver_flags' + implies: 'input_param_flags' + implies: 'linker_param_file' + implies: 'msvc_env' + } + + # TODO(b/65151735): Remove legacy_compile_flags feature when legacy fields are + # not used in this crosstool + feature { + name: 'legacy_compile_flags' + flag_set { + expand_if_all_available: 'legacy_compile_flags' + action: 'preprocess-assemble' + action: 'c-compile' + action: 'c++-compile' + action: 'c++-header-parsing' + action: 'c++-module-compile' + action: 'c++-module-codegen' + flag_group { + iterate_over: 'legacy_compile_flags' + flag: '%{legacy_compile_flags}' + } + } + } + + feature { + name: "msvc_env" + env_set { + action: "c-compile" + action: "c++-compile" + action: "c++-module-compile" + action: "c++-module-codegen" + action: "c++-header-parsing" + action: "assemble" + action: "preprocess-assemble" + action: "c++-link-executable" + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + action: "c++-link-static-library" + env_entry { + key: "PATH" + value: "" + } + env_entry { + key: "TMP" + value: "" + } + env_entry { + key: "TEMP" + value: "" + } + } + implies: 'msvc_compile_env' + implies: 'msvc_link_env' + } + + feature { + name: "msvc_compile_env" + env_set { + action: "c-compile" + action: "c++-compile" + action: "c++-module-compile" + action: "c++-module-codegen" + action: "c++-header-parsing" + action: "assemble" + action: "preprocess-assemble" + env_entry { + key: "INCLUDE" + value: "" + } + } + } + + feature { + name: "msvc_link_env" + env_set { + action: "c++-link-executable" + action: "c++-link-dynamic-library" + action: "c++-link-nodeps-dynamic-library" + action: "c++-link-static-library" + env_entry { + key: "LIB" + value: "" + } + } + } + + feature { + name: 'include_paths' + flag_set { + action: "assemble" + action: 'preprocess-assemble' + action: 'c-compile' + action: 'c++-compile' + action: 'c++-header-parsing' + action: 'c++-module-compile' + flag_group { + iterate_over: 'quote_include_paths' + flag: '/I%{quote_include_paths}' + } + flag_group { + iterate_over: 'include_paths' + flag: '/I%{include_paths}' + } + flag_group { + iterate_over: 'system_include_paths' + flag: '/I%{system_include_paths}' + } + } + } + + feature { + name: "preprocessor_defines" + flag_set { + action: "assemble" + action: "preprocess-assemble" + action: "c-compile" + action: "c++-compile" + action: "c++-header-parsing" + action: "c++-module-compile" + flag_group { + flag: "/D%{preprocessor_defines}" + iterate_over: "preprocessor_defines" + } + } + } + + # Tell Bazel to parse the output of /showIncludes + feature { + name: 'parse_showincludes' + flag_set { + action: 'preprocess-assemble' + action: 'c-compile' + action: 'c++-compile' + action: 'c++-module-compile' + action: 'c++-header-parsing' + flag_group { + flag: "/showIncludes" + } + } + } + + + feature { + name: 'generate_pdb_file' + requires: { + feature: 'dbg' + } + requires: { + feature: 'fastbuild' + } + } + + feature { + name: 'shared_flag' + flag_set { + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: '/DLL' + } + } + } + + feature { + name: 'linkstamps' + flag_set { + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + expand_if_all_available: 'linkstamp_paths' + flag_group { + iterate_over: 'linkstamp_paths' + flag: '%{linkstamp_paths}' + } + } + } + + feature { + name: 'output_execpath_flags' + flag_set { + expand_if_all_available: 'output_execpath' + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: '/OUT:%{output_execpath}' + } + } + } + + feature { + name: 'archiver_flags' + flag_set { + expand_if_all_available: 'output_execpath' + action: 'c++-link-static-library' + flag_group { + flag: '/OUT:%{output_execpath}' + } + } + } + + feature { + name: 'input_param_flags' + flag_set { + expand_if_all_available: 'interface_library_output_path' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "/IMPLIB:%{interface_library_output_path}" + } + } + flag_set { + expand_if_all_available: 'libopts' + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + iterate_over: 'libopts' + flag: '%{libopts}' + } + } + flag_set { + expand_if_all_available: 'libraries_to_link' + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + action: 'c++-link-static-library' + flag_group { + iterate_over: 'libraries_to_link' + flag_group { + expand_if_equal: { + variable: 'libraries_to_link.type' + value: 'object_file_group' + } + iterate_over: 'libraries_to_link.object_files' + flag_group { + flag: '%{libraries_to_link.object_files}' + } + } + flag_group { + expand_if_equal: { + variable: 'libraries_to_link.type' + value: 'object_file' + } + flag_group { + flag: '%{libraries_to_link.name}' + } + } + flag_group { + expand_if_equal: { + variable: 'libraries_to_link.type' + value: 'interface_library' + } + flag_group { + flag: '%{libraries_to_link.name}' + } + } + flag_group { + expand_if_equal: { + variable: 'libraries_to_link.type' + value: 'static_library' + } + flag_group { + expand_if_false: 'libraries_to_link.is_whole_archive' + flag: '%{libraries_to_link.name}' + } + flag_group { + expand_if_true: 'libraries_to_link.is_whole_archive' + flag: '/WHOLEARCHIVE:%{libraries_to_link.name}' + } + } + } + } + } + + # Since this feature is declared earlier in the CROSSTOOL than + # "user_link_flags", this feature will be applied prior to it anwyhere they + # are both implied. And since "user_link_flags" contains the linkopts from + # the build rule, this allows the user to override the /SUBSYSTEM in the BUILD + # file. + feature { + name: 'linker_subsystem_flag' + flag_set { + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: '/SUBSYSTEM:CONSOLE' + } + } + } + + # The "user_link_flags" contains user-defined linkopts (from build rules) + # so it should be defined after features that declare user-overridable flags. + # For example the "linker_subsystem_flag" defines a default "/SUBSYSTEM" flag + # but we want to let the user override it, therefore "link_flag_subsystem" is + # defined earlier in the CROSSTOOL file than "user_link_flags". + feature { + name: 'user_link_flags' + flag_set { + expand_if_all_available: 'user_link_flags' + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + iterate_over: 'user_link_flags' + flag: '%{user_link_flags}' + } + } + } + feature { + name: 'legacy_link_flags' + flag_set { + expand_if_all_available: 'legacy_link_flags' + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + iterate_over: 'legacy_link_flags' + flag: '%{legacy_link_flags}' + } + } + } + + feature { + name: 'linker_param_file' + flag_set { + expand_if_all_available: 'linker_param_file' + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + action: 'c++-link-static-library' + flag_group { + flag: '@%{linker_param_file}' + } + } + } + + feature { + name: 'static_link_msvcrt' + } + + feature { + name: 'static_link_msvcrt_no_debug' + flag_set { + action: 'c-compile' + action: 'c++-compile' + flag_group { + flag: "/MT" + } + } + flag_set { + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "/DEFAULTLIB:libcmt.lib" + } + } + requires: { feature: 'fastbuild'} + requires: { feature: 'opt'} + } + + feature { + name: 'dynamic_link_msvcrt_no_debug' + flag_set { + action: 'c-compile' + action: 'c++-compile' + flag_group { + flag: "/MD" + } + } + flag_set { + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "/DEFAULTLIB:msvcrt.lib" + } + } + requires: { feature: 'fastbuild'} + requires: { feature: 'opt'} + } + + feature { + name: 'static_link_msvcrt_debug' + flag_set { + action: 'c-compile' + action: 'c++-compile' + flag_group { + flag: "/MTd" + } + } + flag_set { + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "/DEFAULTLIB:libcmtd.lib" + } + } + requires: { feature: 'dbg'} + } + + feature { + name: 'dynamic_link_msvcrt_debug' + flag_set { + action: 'c-compile' + action: 'c++-compile' + flag_group { + flag: "/MDd" + } + } + flag_set { + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "/DEFAULTLIB:msvcrtd.lib" + } + } + requires: { feature: 'dbg'} + } + + feature { + name: 'dbg' + flag_set { + action: 'c-compile' + action: 'c++-compile' + flag_group { + flag: "/Od" + flag: "/Z7" + } + } + flag_set { + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "" + flag: "/INCREMENTAL:NO" + } + } + implies: 'generate_pdb_file' + } + + feature { + name: 'fastbuild' + flag_set { + action: 'c-compile' + action: 'c++-compile' + flag_group { + flag: "/Od" + flag: "/Z7" + } + } + flag_set { + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "" + flag: "/INCREMENTAL:NO" + } + } + implies: 'generate_pdb_file' + } + + feature { + name: 'opt' + flag_set { + action: 'c-compile' + action: 'c++-compile' + flag_group { + flag: "/O2" # Implies /Og /Oi /Ot /Oy /Ob2 /Gs /GF /Gy + } + } + implies: 'frame_pointer' + } + + # Keep stack frames for debugging, even in opt mode. + # Must come after /O1, /O2 and /Ox. + feature { + name: "frame_pointer" + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + flag: "/Oy-" + } + } + } + + # Remove assert/DCHECKs in opt mode. + # You can have them back with --features=-disable_assertions. + feature { + name: 'disable_assertions' + enabled: true + flag_set { + action: 'c-compile' + action: 'c++-compile' + with_feature: { + feature: 'opt' + } + flag_group { + flag: "/DNDEBUG" + } + } + } + + feature { + name: "determinism" + enabled: true + flag_set { + action: "c-compile" + action: "c++-compile" + flag_group { + # Make C++ compilation deterministic. Use linkstamping instead of these + # compiler symbols. + # TODO: detect clang on Windows and use "-Wno-builtin-macro-redefined" + flag: "/wd4117" # Trying to define or undefine a predefined macro + flag: "-D__DATE__=\"redacted\"" + flag: "-D__TIMESTAMP__=\"redacted\"" + flag: "-D__TIME__=\"redacted\"" + } + } + } + + feature { + name: 'treat_warnings_as_errors' + flag_set { + action: 'c-compile' + action: 'c++-compile' + flag_group { + flag: "/WX" + } + } + } + + # Trade slower build time for smaller binary + feature { + name: 'smaller_binary' + enabled: true + flag_set { + action: 'c-compile' + action: 'c++-compile' + with_feature: { + feature: 'opt' + } + flag_group { + flag: "/Gy" # Enable function-level linking (-ffunction-sections) + flag: "/Gw" # Optimize global data (-fdata-sections) + } + } + flag_set { + action: 'c++-link-executable' + action: 'c++-link-dynamic-library', + action: 'c++-link-nodeps-dynamic-library' + with_feature: { + feature: 'opt' + } + flag_group { + flag: '/OPT:ICF' # Fold identical functions + flag: '/OPT:REF' # Eliminate unreferenced functions and data + } + } + } + + # Suppress warnings that most users do not care + feature { + name: 'ignore_noisy_warnings' + enabled: true + flag_set { + action: 'c++-link-static-library' + flag_group { + # Suppress 'object file does not define any public symbols' warning + flag: '/ignore:4221' + } + } + } + + feature { + name: 'user_compile_flags' + flag_set { + expand_if_all_available: 'user_compile_flags' + action: 'preprocess-assemble' + action: 'c-compile' + action: 'c++-compile' + action: 'c++-header-parsing' + action: 'c++-module-compile' + action: 'c++-module-codegen' + flag_group { + iterate_over: 'user_compile_flags' + flag: '%{user_compile_flags}' + } + } + } + + feature { + name: 'sysroot' + flag_set { + expand_if_all_available: 'sysroot' + action: 'assemble' + action: 'preprocess-assemble' + action: 'c-compile' + action: 'c++-compile' + action: 'c++-header-parsing' + action: 'c++-module-compile' + action: 'c++-module-codegen' + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + iterate_over: 'sysroot' + flag: '--sysroot=%{sysroot}' + } + } + } + + feature { + name: 'unfiltered_compile_flags' + flag_set { + expand_if_all_available: 'unfiltered_compile_flags' + action: 'preprocess-assemble' + action: 'c-compile' + action: 'c++-compile' + action: 'c++-header-parsing' + action: 'c++-module-compile' + action: 'c++-module-codegen' + flag_group { + iterate_over: 'unfiltered_compile_flags' + flag: '%{unfiltered_compile_flags}' + } + } + } + + feature { + name: 'compiler_output_flags' + flag_set { + action: 'assemble' + flag_group { + expand_if_all_available: 'output_file' + expand_if_none_available: 'output_assembly_file' + expand_if_none_available: 'output_preprocess_file' + flag: '/Fo%{output_file}' + flag: '/Zi' + } + } + flag_set { + action: 'preprocess-assemble' + action: 'c-compile' + action: 'c++-compile' + action: 'c++-header-parsing' + action: 'c++-module-compile' + action: 'c++-module-codegen' + flag_group { + expand_if_all_available: 'output_file' + expand_if_none_available: 'output_assembly_file' + expand_if_none_available: 'output_preprocess_file' + flag: '/Fo%{output_file}' + } + flag_group { + expand_if_all_available: 'output_file' + expand_if_all_available: 'output_assembly_file' + flag: '/Fa%{output_file}' + } + flag_group { + expand_if_all_available: 'output_file' + expand_if_all_available: 'output_preprocess_file' + flag: '/P' + flag: '/Fi%{output_file}' + } + } + } + + feature { + name: 'compiler_input_flags' + flag_set { + action: 'assemble' + action: 'preprocess-assemble' + action: 'c-compile' + action: 'c++-compile' + action: 'c++-header-parsing' + action: 'c++-module-compile' + action: 'c++-module-codegen' + flag_group { + expand_if_all_available: 'source_file' + flag: '/c' + flag: '%{source_file}' + } + } + } + + feature { + name : 'def_file', + flag_set { + expand_if_all_available: 'def_file_path' + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + action: "c++-link-nodeps-dynamic-library" + flag_group { + flag: "/DEF:%{def_file_path}" + # We can specify a different DLL name in DEF file, /ignore:4070 suppresses + # the warning message about DLL name doesn't match the default one. + # See https://msdn.microsoft.com/en-us/library/sfkk2fz7.aspx + flag: "/ignore:4070" + } + } + } + + feature { + name: 'windows_export_all_symbols' + } + + feature { + name: 'no_windows_export_all_symbols' + } + + linking_mode_flags { mode: DYNAMIC } +} diff --git a/third_party/toolchains/preconfig/ubuntu16.04/clang/WORKSPACE b/third_party/toolchains/preconfig/ubuntu16.04/clang/WORKSPACE new file mode 100644 index 0000000000..bc05b4c36f --- /dev/null +++ b/third_party/toolchains/preconfig/ubuntu16.04/clang/WORKSPACE @@ -0,0 +1,2 @@ +# DO NOT EDIT: automatically generated WORKSPACE file for cc_autoconf rule +workspace(name = "local_config_cc") diff --git a/third_party/toolchains/preconfig/ubuntu16.04/clang/cc_wrapper.sh b/third_party/toolchains/preconfig/ubuntu16.04/clang/cc_wrapper.sh new file mode 100755 index 0000000000..42a751dccf --- /dev/null +++ b/third_party/toolchains/preconfig/ubuntu16.04/clang/cc_wrapper.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# +# Copyright 2015 The Bazel 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. +# +# Ship the environment to the C++ action +# +set -eu + +# Set-up the environment + + +# Call the C++ compiler +/usr/local/bin/clang "$@" diff --git a/third_party/toolchains/preconfig/ubuntu16.04/clang/dummy_toolchain.bzl b/third_party/toolchains/preconfig/ubuntu16.04/clang/dummy_toolchain.bzl new file mode 100755 index 0000000000..45c0285d23 --- /dev/null +++ b/third_party/toolchains/preconfig/ubuntu16.04/clang/dummy_toolchain.bzl @@ -0,0 +1,23 @@ +# pylint: disable=g-bad-file-header +# Copyright 2017 The Bazel 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. + +"""Skylark rule that stubs a toolchain.""" + +def _dummy_toolchain_impl(ctx): + ctx = ctx # unused argument + toolchain = platform_common.ToolchainInfo() + return [toolchain] + +dummy_toolchain = rule(_dummy_toolchain_impl, attrs = {}) diff --git a/third_party/toolchains/preconfig/ubuntu16.04/clang/tools/cpp/empty.cc b/third_party/toolchains/preconfig/ubuntu16.04/clang/tools/cpp/empty.cc new file mode 100755 index 0000000000..c272dabaeb --- /dev/null +++ b/third_party/toolchains/preconfig/ubuntu16.04/clang/tools/cpp/empty.cc @@ -0,0 +1 @@ +int main() {} \ No newline at end of file diff --git a/third_party/toolchains/preconfig/ubuntu16.04/py3/BUILD b/third_party/toolchains/preconfig/ubuntu16.04/py3/BUILD new file mode 100755 index 0000000000..77eaa4d512 --- /dev/null +++ b/third_party/toolchains/preconfig/ubuntu16.04/py3/BUILD @@ -0,0 +1,205 @@ +licenses(["restricted"]) + +package(default_visibility = ["//visibility:public"]) + +# To build Python C/C++ extension on Windows, we need to link to python import library pythonXY.lib +# See https://docs.python.org/3/extending/windows.html +cc_import( + name = "python_lib", + interface_library = select({ + ":windows": ":python_import_lib", + # A placeholder for Unix platforms which makes --no_build happy. + "//conditions:default": "not-existing.lib", + }), + system_provided = 1, +) + +cc_library( + name = "python_headers", + hdrs = [":python_include"], + includes = ["python_include"], + deps = select({ + ":windows": [":python_lib"], + "//conditions:default": [], + }), +) + +cc_library( + name = "numpy_headers", + hdrs = [":numpy_include"], + includes = ["numpy_include"], +) + +config_setting( + name = "windows", + values = {"cpu": "x64_windows"}, + visibility = ["//visibility:public"], +) + +genrule( + name = "python_include", + outs = [ + "python_include/Python-ast.h", + "python_include/Python.h", + "python_include/abstract.h", + "python_include/accu.h", + "python_include/asdl.h", + "python_include/ast.h", + "python_include/bitset.h", + "python_include/bltinmodule.h", + "python_include/boolobject.h", + "python_include/bytearrayobject.h", + "python_include/bytes_methods.h", + "python_include/bytesobject.h", + "python_include/cellobject.h", + "python_include/ceval.h", + "python_include/classobject.h", + "python_include/code.h", + "python_include/codecs.h", + "python_include/compile.h", + "python_include/complexobject.h", + "python_include/datetime.h", + "python_include/descrobject.h", + "python_include/dictobject.h", + "python_include/dtoa.h", + "python_include/dynamic_annotations.h", + "python_include/enumobject.h", + "python_include/errcode.h", + "python_include/eval.h", + "python_include/fileobject.h", + "python_include/fileutils.h", + "python_include/floatobject.h", + "python_include/frameobject.h", + "python_include/funcobject.h", + "python_include/genobject.h", + "python_include/graminit.h", + "python_include/grammar.h", + "python_include/import.h", + "python_include/intrcheck.h", + "python_include/iterobject.h", + "python_include/listobject.h", + "python_include/longintrepr.h", + "python_include/longobject.h", + "python_include/marshal.h", + "python_include/memoryobject.h", + "python_include/metagrammar.h", + "python_include/methodobject.h", + "python_include/modsupport.h", + "python_include/moduleobject.h", + "python_include/namespaceobject.h", + "python_include/node.h", + "python_include/numpy/__multiarray_api.h", + "python_include/numpy/__ufunc_api.h", + "python_include/numpy/_neighborhood_iterator_imp.h", + "python_include/numpy/_numpyconfig.h", + "python_include/numpy/arrayobject.h", + "python_include/numpy/arrayscalars.h", + "python_include/numpy/halffloat.h", + "python_include/numpy/multiarray_api.txt", + "python_include/numpy/ndarrayobject.h", + "python_include/numpy/ndarraytypes.h", + "python_include/numpy/noprefix.h", + "python_include/numpy/npy_1_7_deprecated_api.h", + "python_include/numpy/npy_3kcompat.h", + "python_include/numpy/npy_common.h", + "python_include/numpy/npy_cpu.h", + "python_include/numpy/npy_endian.h", + "python_include/numpy/npy_interrupt.h", + "python_include/numpy/npy_math.h", + "python_include/numpy/npy_no_deprecated_api.h", + "python_include/numpy/npy_os.h", + "python_include/numpy/numpyconfig.h", + "python_include/numpy/old_defines.h", + "python_include/numpy/oldnumeric.h", + "python_include/numpy/ufunc_api.txt", + "python_include/numpy/ufuncobject.h", + "python_include/numpy/utils.h", + "python_include/object.h", + "python_include/objimpl.h", + "python_include/odictobject.h", + "python_include/opcode.h", + "python_include/osdefs.h", + "python_include/parsetok.h", + "python_include/patchlevel.h", + "python_include/pgen.h", + "python_include/pgenheaders.h", + "python_include/py_curses.h", + "python_include/pyarena.h", + "python_include/pyatomic.h", + "python_include/pycapsule.h", + "python_include/pyconfig.h", + "python_include/pyctype.h", + "python_include/pydebug.h", + "python_include/pyerrors.h", + "python_include/pyexpat.h", + "python_include/pyfpe.h", + "python_include/pygetopt.h", + "python_include/pyhash.h", + "python_include/pylifecycle.h", + "python_include/pymacconfig.h", + "python_include/pymacro.h", + "python_include/pymath.h", + "python_include/pymem.h", + "python_include/pyport.h", + "python_include/pystate.h", + "python_include/pystrcmp.h", + "python_include/pystrhex.h", + "python_include/pystrtod.h", + "python_include/pythonrun.h", + "python_include/pythread.h", + "python_include/pytime.h", + "python_include/rangeobject.h", + "python_include/setobject.h", + "python_include/sliceobject.h", + "python_include/structmember.h", + "python_include/structseq.h", + "python_include/symtable.h", + "python_include/sysmodule.h", + "python_include/token.h", + "python_include/traceback.h", + "python_include/tupleobject.h", + "python_include/typeslots.h", + "python_include/ucnhash.h", + "python_include/unicodeobject.h", + "python_include/warnings.h", + "python_include/weakrefobject.h", + ], + cmd = """ +cp -f "/usr/include/python3.5m/Python-ast.h" "$(@D)/python_include/Python-ast.h" && cp -f "/usr/include/python3.5m/Python.h" "$(@D)/python_include/Python.h" && cp -f "/usr/include/python3.5m/abstract.h" "$(@D)/python_include/abstract.h" && cp -f "/usr/include/python3.5m/accu.h" "$(@D)/python_include/accu.h" && cp -f "/usr/include/python3.5m/asdl.h" "$(@D)/python_include/asdl.h" && cp -f "/usr/include/python3.5m/ast.h" "$(@D)/python_include/ast.h" && cp -f "/usr/include/python3.5m/bitset.h" "$(@D)/python_include/bitset.h" && cp -f "/usr/include/python3.5m/bltinmodule.h" "$(@D)/python_include/bltinmodule.h" && cp -f "/usr/include/python3.5m/boolobject.h" "$(@D)/python_include/boolobject.h" && cp -f "/usr/include/python3.5m/bytearrayobject.h" "$(@D)/python_include/bytearrayobject.h" && cp -f "/usr/include/python3.5m/bytes_methods.h" "$(@D)/python_include/bytes_methods.h" && cp -f "/usr/include/python3.5m/bytesobject.h" "$(@D)/python_include/bytesobject.h" && cp -f "/usr/include/python3.5m/cellobject.h" "$(@D)/python_include/cellobject.h" && cp -f "/usr/include/python3.5m/ceval.h" "$(@D)/python_include/ceval.h" && cp -f "/usr/include/python3.5m/classobject.h" "$(@D)/python_include/classobject.h" && cp -f "/usr/include/python3.5m/code.h" "$(@D)/python_include/code.h" && cp -f "/usr/include/python3.5m/codecs.h" "$(@D)/python_include/codecs.h" && cp -f "/usr/include/python3.5m/compile.h" "$(@D)/python_include/compile.h" && cp -f "/usr/include/python3.5m/complexobject.h" "$(@D)/python_include/complexobject.h" && cp -f "/usr/include/python3.5m/datetime.h" "$(@D)/python_include/datetime.h" && cp -f "/usr/include/python3.5m/descrobject.h" "$(@D)/python_include/descrobject.h" && cp -f "/usr/include/python3.5m/dictobject.h" "$(@D)/python_include/dictobject.h" && cp -f "/usr/include/python3.5m/dtoa.h" "$(@D)/python_include/dtoa.h" && cp -f "/usr/include/python3.5m/dynamic_annotations.h" "$(@D)/python_include/dynamic_annotations.h" && cp -f "/usr/include/python3.5m/enumobject.h" "$(@D)/python_include/enumobject.h" && cp -f "/usr/include/python3.5m/errcode.h" "$(@D)/python_include/errcode.h" && cp -f "/usr/include/python3.5m/eval.h" "$(@D)/python_include/eval.h" && cp -f "/usr/include/python3.5m/fileobject.h" "$(@D)/python_include/fileobject.h" && cp -f "/usr/include/python3.5m/fileutils.h" "$(@D)/python_include/fileutils.h" && cp -f "/usr/include/python3.5m/floatobject.h" "$(@D)/python_include/floatobject.h" && cp -f "/usr/include/python3.5m/frameobject.h" "$(@D)/python_include/frameobject.h" && cp -f "/usr/include/python3.5m/funcobject.h" "$(@D)/python_include/funcobject.h" && cp -f "/usr/include/python3.5m/genobject.h" "$(@D)/python_include/genobject.h" && cp -f "/usr/include/python3.5m/graminit.h" "$(@D)/python_include/graminit.h" && cp -f "/usr/include/python3.5m/grammar.h" "$(@D)/python_include/grammar.h" && cp -f "/usr/include/python3.5m/import.h" "$(@D)/python_include/import.h" && cp -f "/usr/include/python3.5m/intrcheck.h" "$(@D)/python_include/intrcheck.h" && cp -f "/usr/include/python3.5m/iterobject.h" "$(@D)/python_include/iterobject.h" && cp -f "/usr/include/python3.5m/listobject.h" "$(@D)/python_include/listobject.h" && cp -f "/usr/include/python3.5m/longintrepr.h" "$(@D)/python_include/longintrepr.h" && cp -f "/usr/include/python3.5m/longobject.h" "$(@D)/python_include/longobject.h" && cp -f "/usr/include/python3.5m/marshal.h" "$(@D)/python_include/marshal.h" && cp -f "/usr/include/python3.5m/memoryobject.h" "$(@D)/python_include/memoryobject.h" && cp -f "/usr/include/python3.5m/metagrammar.h" "$(@D)/python_include/metagrammar.h" && cp -f "/usr/include/python3.5m/methodobject.h" "$(@D)/python_include/methodobject.h" && cp -f "/usr/include/python3.5m/modsupport.h" "$(@D)/python_include/modsupport.h" && cp -f "/usr/include/python3.5m/moduleobject.h" "$(@D)/python_include/moduleobject.h" && cp -f "/usr/include/python3.5m/namespaceobject.h" "$(@D)/python_include/namespaceobject.h" && cp -f "/usr/include/python3.5m/node.h" "$(@D)/python_include/node.h" && cp -f "/usr/include/python3.5m/numpy/__multiarray_api.h" "$(@D)/python_include/numpy/__multiarray_api.h" && cp -f "/usr/include/python3.5m/numpy/__ufunc_api.h" "$(@D)/python_include/numpy/__ufunc_api.h" && cp -f "/usr/include/python3.5m/numpy/_neighborhood_iterator_imp.h" "$(@D)/python_include/numpy/_neighborhood_iterator_imp.h" && cp -f "/usr/include/python3.5m/numpy/_numpyconfig.h" "$(@D)/python_include/numpy/_numpyconfig.h" && cp -f "/usr/include/python3.5m/numpy/arrayobject.h" "$(@D)/python_include/numpy/arrayobject.h" && cp -f "/usr/include/python3.5m/numpy/arrayscalars.h" "$(@D)/python_include/numpy/arrayscalars.h" && cp -f "/usr/include/python3.5m/numpy/halffloat.h" "$(@D)/python_include/numpy/halffloat.h" && cp -f "/usr/include/python3.5m/numpy/multiarray_api.txt" "$(@D)/python_include/numpy/multiarray_api.txt" && cp -f "/usr/include/python3.5m/numpy/ndarrayobject.h" "$(@D)/python_include/numpy/ndarrayobject.h" && cp -f "/usr/include/python3.5m/numpy/ndarraytypes.h" "$(@D)/python_include/numpy/ndarraytypes.h" && cp -f "/usr/include/python3.5m/numpy/noprefix.h" "$(@D)/python_include/numpy/noprefix.h" && cp -f "/usr/include/python3.5m/numpy/npy_1_7_deprecated_api.h" "$(@D)/python_include/numpy/npy_1_7_deprecated_api.h" && cp -f "/usr/include/python3.5m/numpy/npy_3kcompat.h" "$(@D)/python_include/numpy/npy_3kcompat.h" && cp -f "/usr/include/python3.5m/numpy/npy_common.h" "$(@D)/python_include/numpy/npy_common.h" && cp -f "/usr/include/python3.5m/numpy/npy_cpu.h" "$(@D)/python_include/numpy/npy_cpu.h" && cp -f "/usr/include/python3.5m/numpy/npy_endian.h" "$(@D)/python_include/numpy/npy_endian.h" && cp -f "/usr/include/python3.5m/numpy/npy_interrupt.h" "$(@D)/python_include/numpy/npy_interrupt.h" && cp -f "/usr/include/python3.5m/numpy/npy_math.h" "$(@D)/python_include/numpy/npy_math.h" && cp -f "/usr/include/python3.5m/numpy/npy_no_deprecated_api.h" "$(@D)/python_include/numpy/npy_no_deprecated_api.h" && cp -f "/usr/include/python3.5m/numpy/npy_os.h" "$(@D)/python_include/numpy/npy_os.h" && cp -f "/usr/include/python3.5m/numpy/numpyconfig.h" "$(@D)/python_include/numpy/numpyconfig.h" && cp -f "/usr/include/python3.5m/numpy/old_defines.h" "$(@D)/python_include/numpy/old_defines.h" && cp -f "/usr/include/python3.5m/numpy/oldnumeric.h" "$(@D)/python_include/numpy/oldnumeric.h" && cp -f "/usr/include/python3.5m/numpy/ufunc_api.txt" "$(@D)/python_include/numpy/ufunc_api.txt" && cp -f "/usr/include/python3.5m/numpy/ufuncobject.h" "$(@D)/python_include/numpy/ufuncobject.h" && cp -f "/usr/include/python3.5m/numpy/utils.h" "$(@D)/python_include/numpy/utils.h" && cp -f "/usr/include/python3.5m/object.h" "$(@D)/python_include/object.h" && cp -f "/usr/include/python3.5m/objimpl.h" "$(@D)/python_include/objimpl.h" && cp -f "/usr/include/python3.5m/odictobject.h" "$(@D)/python_include/odictobject.h" && cp -f "/usr/include/python3.5m/opcode.h" "$(@D)/python_include/opcode.h" && cp -f "/usr/include/python3.5m/osdefs.h" "$(@D)/python_include/osdefs.h" && cp -f "/usr/include/python3.5m/parsetok.h" "$(@D)/python_include/parsetok.h" && cp -f "/usr/include/python3.5m/patchlevel.h" "$(@D)/python_include/patchlevel.h" && cp -f "/usr/include/python3.5m/pgen.h" "$(@D)/python_include/pgen.h" && cp -f "/usr/include/python3.5m/pgenheaders.h" "$(@D)/python_include/pgenheaders.h" && cp -f "/usr/include/python3.5m/py_curses.h" "$(@D)/python_include/py_curses.h" && cp -f "/usr/include/python3.5m/pyarena.h" "$(@D)/python_include/pyarena.h" && cp -f "/usr/include/python3.5m/pyatomic.h" "$(@D)/python_include/pyatomic.h" && cp -f "/usr/include/python3.5m/pycapsule.h" "$(@D)/python_include/pycapsule.h" && cp -f "/usr/include/python3.5m/pyconfig.h" "$(@D)/python_include/pyconfig.h" && cp -f "/usr/include/python3.5m/pyctype.h" "$(@D)/python_include/pyctype.h" && cp -f "/usr/include/python3.5m/pydebug.h" "$(@D)/python_include/pydebug.h" && cp -f "/usr/include/python3.5m/pyerrors.h" "$(@D)/python_include/pyerrors.h" && cp -f "/usr/include/python3.5m/pyexpat.h" "$(@D)/python_include/pyexpat.h" && cp -f "/usr/include/python3.5m/pyfpe.h" "$(@D)/python_include/pyfpe.h" && cp -f "/usr/include/python3.5m/pygetopt.h" "$(@D)/python_include/pygetopt.h" && cp -f "/usr/include/python3.5m/pyhash.h" "$(@D)/python_include/pyhash.h" && cp -f "/usr/include/python3.5m/pylifecycle.h" "$(@D)/python_include/pylifecycle.h" && cp -f "/usr/include/python3.5m/pymacconfig.h" "$(@D)/python_include/pymacconfig.h" && cp -f "/usr/include/python3.5m/pymacro.h" "$(@D)/python_include/pymacro.h" && cp -f "/usr/include/python3.5m/pymath.h" "$(@D)/python_include/pymath.h" && cp -f "/usr/include/python3.5m/pymem.h" "$(@D)/python_include/pymem.h" && cp -f "/usr/include/python3.5m/pyport.h" "$(@D)/python_include/pyport.h" && cp -f "/usr/include/python3.5m/pystate.h" "$(@D)/python_include/pystate.h" && cp -f "/usr/include/python3.5m/pystrcmp.h" "$(@D)/python_include/pystrcmp.h" && cp -f "/usr/include/python3.5m/pystrhex.h" "$(@D)/python_include/pystrhex.h" && cp -f "/usr/include/python3.5m/pystrtod.h" "$(@D)/python_include/pystrtod.h" && cp -f "/usr/include/python3.5m/pythonrun.h" "$(@D)/python_include/pythonrun.h" && cp -f "/usr/include/python3.5m/pythread.h" "$(@D)/python_include/pythread.h" && cp -f "/usr/include/python3.5m/pytime.h" "$(@D)/python_include/pytime.h" && cp -f "/usr/include/python3.5m/rangeobject.h" "$(@D)/python_include/rangeobject.h" && cp -f "/usr/include/python3.5m/setobject.h" "$(@D)/python_include/setobject.h" && cp -f "/usr/include/python3.5m/sliceobject.h" "$(@D)/python_include/sliceobject.h" && cp -f "/usr/include/python3.5m/structmember.h" "$(@D)/python_include/structmember.h" && cp -f "/usr/include/python3.5m/structseq.h" "$(@D)/python_include/structseq.h" && cp -f "/usr/include/python3.5m/symtable.h" "$(@D)/python_include/symtable.h" && cp -f "/usr/include/python3.5m/sysmodule.h" "$(@D)/python_include/sysmodule.h" && cp -f "/usr/include/python3.5m/token.h" "$(@D)/python_include/token.h" && cp -f "/usr/include/python3.5m/traceback.h" "$(@D)/python_include/traceback.h" && cp -f "/usr/include/python3.5m/tupleobject.h" "$(@D)/python_include/tupleobject.h" && cp -f "/usr/include/python3.5m/typeslots.h" "$(@D)/python_include/typeslots.h" && cp -f "/usr/include/python3.5m/ucnhash.h" "$(@D)/python_include/ucnhash.h" && cp -f "/usr/include/python3.5m/unicodeobject.h" "$(@D)/python_include/unicodeobject.h" && cp -f "/usr/include/python3.5m/warnings.h" "$(@D)/python_include/warnings.h" && cp -f "/usr/include/python3.5m/weakrefobject.h" "$(@D)/python_include/weakrefobject.h" + """, +) + +genrule( + name = "numpy_include", + outs = [ + "numpy_include/numpy/__multiarray_api.h", + "numpy_include/numpy/__ufunc_api.h", + "numpy_include/numpy/_neighborhood_iterator_imp.h", + "numpy_include/numpy/_numpyconfig.h", + "numpy_include/numpy/arrayobject.h", + "numpy_include/numpy/arrayscalars.h", + "numpy_include/numpy/halffloat.h", + "numpy_include/numpy/multiarray_api.txt", + "numpy_include/numpy/ndarrayobject.h", + "numpy_include/numpy/ndarraytypes.h", + "numpy_include/numpy/noprefix.h", + "numpy_include/numpy/npy_1_7_deprecated_api.h", + "numpy_include/numpy/npy_3kcompat.h", + "numpy_include/numpy/npy_common.h", + "numpy_include/numpy/npy_cpu.h", + "numpy_include/numpy/npy_endian.h", + "numpy_include/numpy/npy_interrupt.h", + "numpy_include/numpy/npy_math.h", + "numpy_include/numpy/npy_no_deprecated_api.h", + "numpy_include/numpy/npy_os.h", + "numpy_include/numpy/numpyconfig.h", + "numpy_include/numpy/old_defines.h", + "numpy_include/numpy/oldnumeric.h", + "numpy_include/numpy/ufunc_api.txt", + "numpy_include/numpy/ufuncobject.h", + "numpy_include/numpy/utils.h", + ], + cmd = """ +cp -f "/usr/lib/python3/dist-packages/numpy/core/include/numpy/__multiarray_api.h" "$(@D)/numpy_include/numpy/__multiarray_api.h" && cp -f "/usr/lib/python3/dist-packages/numpy/core/include/numpy/__ufunc_api.h" "$(@D)/numpy_include/numpy/__ufunc_api.h" && cp -f "/usr/lib/python3/dist-packages/numpy/core/include/numpy/_neighborhood_iterator_imp.h" "$(@D)/numpy_include/numpy/_neighborhood_iterator_imp.h" && cp -f "/usr/lib/python3/dist-packages/numpy/core/include/numpy/_numpyconfig.h" "$(@D)/numpy_include/numpy/_numpyconfig.h" && cp -f "/usr/lib/python3/dist-packages/numpy/core/include/numpy/arrayobject.h" "$(@D)/numpy_include/numpy/arrayobject.h" && cp -f "/usr/lib/python3/dist-packages/numpy/core/include/numpy/arrayscalars.h" "$(@D)/numpy_include/numpy/arrayscalars.h" && cp -f "/usr/lib/python3/dist-packages/numpy/core/include/numpy/halffloat.h" "$(@D)/numpy_include/numpy/halffloat.h" && cp -f "/usr/lib/python3/dist-packages/numpy/core/include/numpy/multiarray_api.txt" "$(@D)/numpy_include/numpy/multiarray_api.txt" && cp -f "/usr/lib/python3/dist-packages/numpy/core/include/numpy/ndarrayobject.h" "$(@D)/numpy_include/numpy/ndarrayobject.h" && cp -f "/usr/lib/python3/dist-packages/numpy/core/include/numpy/ndarraytypes.h" "$(@D)/numpy_include/numpy/ndarraytypes.h" && cp -f "/usr/lib/python3/dist-packages/numpy/core/include/numpy/noprefix.h" "$(@D)/numpy_include/numpy/noprefix.h" && cp -f "/usr/lib/python3/dist-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h" "$(@D)/numpy_include/numpy/npy_1_7_deprecated_api.h" && cp -f "/usr/lib/python3/dist-packages/numpy/core/include/numpy/npy_3kcompat.h" "$(@D)/numpy_include/numpy/npy_3kcompat.h" && cp -f "/usr/lib/python3/dist-packages/numpy/core/include/numpy/npy_common.h" "$(@D)/numpy_include/numpy/npy_common.h" && cp -f "/usr/lib/python3/dist-packages/numpy/core/include/numpy/npy_cpu.h" "$(@D)/numpy_include/numpy/npy_cpu.h" && cp -f "/usr/lib/python3/dist-packages/numpy/core/include/numpy/npy_endian.h" "$(@D)/numpy_include/numpy/npy_endian.h" && cp -f "/usr/lib/python3/dist-packages/numpy/core/include/numpy/npy_interrupt.h" "$(@D)/numpy_include/numpy/npy_interrupt.h" && cp -f "/usr/lib/python3/dist-packages/numpy/core/include/numpy/npy_math.h" "$(@D)/numpy_include/numpy/npy_math.h" && cp -f "/usr/lib/python3/dist-packages/numpy/core/include/numpy/npy_no_deprecated_api.h" "$(@D)/numpy_include/numpy/npy_no_deprecated_api.h" && cp -f "/usr/lib/python3/dist-packages/numpy/core/include/numpy/npy_os.h" "$(@D)/numpy_include/numpy/npy_os.h" && cp -f "/usr/lib/python3/dist-packages/numpy/core/include/numpy/numpyconfig.h" "$(@D)/numpy_include/numpy/numpyconfig.h" && cp -f "/usr/lib/python3/dist-packages/numpy/core/include/numpy/old_defines.h" "$(@D)/numpy_include/numpy/old_defines.h" && cp -f "/usr/lib/python3/dist-packages/numpy/core/include/numpy/oldnumeric.h" "$(@D)/numpy_include/numpy/oldnumeric.h" && cp -f "/usr/lib/python3/dist-packages/numpy/core/include/numpy/ufunc_api.txt" "$(@D)/numpy_include/numpy/ufunc_api.txt" && cp -f "/usr/lib/python3/dist-packages/numpy/core/include/numpy/ufuncobject.h" "$(@D)/numpy_include/numpy/ufuncobject.h" && cp -f "/usr/lib/python3/dist-packages/numpy/core/include/numpy/utils.h" "$(@D)/numpy_include/numpy/utils.h" + """, +) diff --git a/third_party/toolchains/preconfig/ubuntu16.04/py3/WORKSPACE b/third_party/toolchains/preconfig/ubuntu16.04/py3/WORKSPACE new file mode 100644 index 0000000000..1d298fefa3 --- /dev/null +++ b/third_party/toolchains/preconfig/ubuntu16.04/py3/WORKSPACE @@ -0,0 +1,2 @@ +# DO NOT EDIT: automatically generated WORKSPACE file for python_configure rule +workspace(name = "local_config_python") -- GitLab From 1c7de3473c6b9eb6c5b3bd63ee75c203f7a5296c Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Sun, 6 Jan 2019 13:52:15 -0800 Subject: [PATCH 401/622] Fix typo; remove extra space. PiperOrigin-RevId: 228076497 --- tensorflow/contrib/training/python/training/training.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/contrib/training/python/training/training.py b/tensorflow/contrib/training/python/training/training.py index c272a2ac14..fc6e38ab4a 100644 --- a/tensorflow/contrib/training/python/training/training.py +++ b/tensorflow/contrib/training/python/training/training.py @@ -419,7 +419,7 @@ def create_train_op(total_loss, update_ops = set(update_ops) if not global_update_ops.issubset(update_ops): logging.warning('update_ops in create_train_op does not contain all the ' - ' update_ops in GraphKeys.UPDATE_OPS') + 'update_ops in GraphKeys.UPDATE_OPS') # Make sure update_ops are computed before total_loss. if update_ops: -- GitLab From ebd5477f13c8d9585788b9721f821095fca7352e Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Sun, 6 Jan 2019 16:21:00 -0800 Subject: [PATCH 402/622] Make TF_DeleteServer check for __ANDROID__, too. This should avoid a Wdelete-incomplete warning. PiperOrigin-RevId: 228084360 --- tensorflow/c/c_api.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tensorflow/c/c_api.cc b/tensorflow/c/c_api.cc index 9580215a31..9f2f83920c 100644 --- a/tensorflow/c/c_api.cc +++ b/tensorflow/c/c_api.cc @@ -2881,6 +2881,9 @@ const char* TF_ServerTarget(TF_Server* server) { #endif } -void TF_DeleteServer(TF_Server* server) { delete server; } - +void TF_DeleteServer(TF_Server* server) { +#ifndef __ANDROID__ + delete server; +#endif +} } // end extern "C" -- GitLab From 38c91321421694432117b077294df43aa31d1193 Mon Sep 17 00:00:00 2001 From: Bixia Zheng Date: Sun, 6 Jan 2019 16:30:18 -0800 Subject: [PATCH 403/622] [XLA:GPU] Selectively exclude 0-2-1 transposed input parameters from the shared memory transpose implementation. Previously, we either use the shared memory tranpose implementation for all the 0-2-1 tranposed input parameters in a fusion or not to use the shared memory tranpose implement for any input parameters, if the kernel contains instructions such as kReverse and kGather which can make the shared memory tranpose implemetation unsafe. There are two problems in such an approach. First, the set of instructions that can make the shared memory tranpose implementation unsafe is more than the aforementioned two instructions. Second, even though a fusion contains such instructions, it may still be safe to use shared memory tranpose to implement some, if not all, 0-2-1 tranposed input parameters that are not involved in the computation of such instructions. This change adds an analysis to inspect the transitive users of each 0-2-1 tranposed input parameter to decide whether it is safe to use the shared memory tranpose implementation for the parameter. Add two test cases. PiperOrigin-RevId: 228084822 --- .../xla/service/gpu/ir_emitter_unnested.cc | 118 +++++++++++++++--- .../gpu/tests/gpu_kernel_tiling_test.cc | 78 ++++++++++-- 2 files changed, 170 insertions(+), 26 deletions(-) diff --git a/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.cc b/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.cc index 923b3d5945..f190af921e 100644 --- a/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.cc +++ b/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.cc @@ -2810,7 +2810,8 @@ void IrEmitterUnnested::EmitBlock(const TileGenerator& emit_one_tile, // unnested_hlo: The unnested hlo instruction for which the kernel is generated. // Currently, these hlo instructions are supported: kLoop fusion, kCopy. // tiled_param_ids: The IDs for the parameters that are 0-2-1 transpose of -// other tensors with the same dimensions and need to be tiled and tranposed. +// other tensors with the same dimensions and are safe to be tranposed via +// the shared memory tranpose implementation. // mapping_scheme: The tiling scheme to use. // kernel_generator: Contains function objects for code generation, such as // element generator, block prologue and epilogue generators. @@ -2989,7 +2990,10 @@ LaunchDimensions IrEmitterUnnested::EmitKernel( // Emits a kernel for the given hlo instruction using a tiled 0-2-1 transpose // algorithm to improve the memory access patterns for the input parameters -// with a shape that is a 0-2-1 transpose of the output tensor shape. +// with a shape that is a 0-2-1 transpose of the output tensor shape. The caller +// is responsible for making sure that it is safe to apply the shared memory +// tranpose on the input parameters. +// // // For the purpose of tiling, the output tensors have a logical shape of three // components 0-2-1 while the relevant input parameters have a logical shape @@ -3040,26 +3044,99 @@ LaunchDimensions IrEmitterUnnested::EmitHlo021Tile( } namespace { -// Returns true to indicate it is safe to use the tile based shared memory -// transpose implementation to implement the kernel for the instruction. +// A recursive function to inspect the users of a parameter to determine +// whether it's safe for a parameter to participate in a shared-memory +// transpose. // -// An instruction is not safe for such an implementation if it can change the -// element order of a tensor without changing the dimension of the tensor, and -// the instruction has a corresponding elemental_ir_emitter. -bool IsInstructionSafeForTileBasedTranspose(const HloInstruction* hlo) { - auto is_safe_for_tile_based_transpose = [&](const HloInstruction* instr) { - HloOpcode opcode = instr->opcode(); - CHECK_NE(opcode, HloOpcode::kFusion); - return (opcode != HloOpcode::kReverse && opcode != HloOpcode::kGather); - }; +// Consider a fusion parameter P for which we might want to use a shmem +// transpose. If we do, we use a GPU thread block to preload a tile of P with +// indices [z, y..y+31, x..x+31] to compute an output tile with the same indices +// cooperatively, where z, y, x are the indices for the normalized input/output +// tensor (see the document for FindTranspose021 for the definition of +// normalized tensor for 0-2-1 transpose). This shmem transpose implementation +// requires that the computation of the output tile only read elements within +// the preload tile. If this is not true, we can't use a shmem transpose for P. +// +// If the computation of output element [z, y, x] only requires the element of +// P with the same indices, the shmem tranpose implementation can be applied +// to P safely. This is a sufficient but not necessary condition. We check all +// the transitive users of P to see if we can find a user that may cause an +// exception to the situation. If such a user is not found, we conclude that P +// is safe for shmem transpose. +// +// This is trivially true for elementwise operations and some "data-movement" +// ops like kTuple. However, it's not true for operations that can change the +// dimensions of the inputs (e.g. pad, slice) and bitcast operation. +// For example: +// +// fused_computation { +// param_0 = f32[64,64]{1,0} parameter(0) +// ROOT bitcast = f32[64,64]{0,1} bitcast(param_0) +// } +// The output element at logical address [0, 63] depends on the input element +// at logical address [63, 0], which would not be within the shared-memory +// block. +// +// TODO(bixia): In order to extend this for kInput fusion, that is reduction +// with tranpose, we only need to end the use-chain checking with the input of +// a reduce operations. In this case, the above description on "output" apply +// to the result of such a use-chain, which provides the input to the reduce +// operation. +bool IsInstructionSafeForShmemTranspose(const HloInstruction* hlo) { + if (hlo->IsElementwise()) { + return absl::c_all_of(hlo->users(), [&](const HloInstruction* user) { + return IsInstructionSafeForShmemTranspose(user); + }); + } - if (hlo->opcode() == HloOpcode::kFusion) { - return absl::c_all_of(hlo->fused_instructions_computation()->instructions(), - is_safe_for_tile_based_transpose); + switch (hlo->opcode()) { + // Non-elementwise instructions that don't cause the shmem transpose + // to be unsafe, including the instructions that don't currently fuse. + case HloOpcode::kGetDimensionSize: + // The result of the operation doesn't rely on the content of the + // tensor. As such, there is no need to further inspect its users. + return true; + case HloOpcode::kGetTupleElement: + case HloOpcode::kMap: + case HloOpcode::kParameter: + case HloOpcode::kTuple: + case HloOpcode::kTupleSelect: + return absl::c_all_of(hlo->users(), [&](const HloInstruction* user) { + return IsInstructionSafeForShmemTranspose(user); + }); + + default: + return false; } +} - return is_safe_for_tile_based_transpose(hlo); +// Given a group of input parameters that are 0-2-1 tranpose of the outputs of +// a fusion kernel, returns the input parameters that are safe for the shared +// memory tranpose implementation. +// +// When a tile based shared memory transpose is used to implement an input with +// 0-2-1 transpose, we preload a tile of the input elements +// [z, y..y+31, x..x+31] to compute the output tile elements of the same +// indices. Preloading the input tile this way is only safe when the computation +// of the output tile elements do not need any input element outside the +// preloaded tile. We inspect all the transitive users of the input parameter +// up to the fusion root instruction to see if we can find any instruction +// that can make preloading the input tile unsafe. +std::vector FilterInputsForShmemTranspose(const HloInstruction* fusion, + std::vector input_ids) { + std::vector filtered_input_ids; + for (int64 i = 0; i < input_ids.size(); ++i) { + const HloInstruction* input = fusion->fused_parameter(input_ids[i]); + if (IsInstructionSafeForShmemTranspose(input)) { + filtered_input_ids.push_back(input_ids[i]); + } else { + VLOG(10) << "Input not safe for shmem transpose " << input->ToString() + << "\n"; + } + } + return filtered_input_ids; } + } // namespace bool IrEmitterUnnested::CheckAndEmitHloWithTile021(HloInstruction* hlo) { @@ -3106,8 +3183,11 @@ bool IrEmitterUnnested::CheckAndEmitHloWithTile021(HloInstruction* hlo) { return false; } - if (!IsInstructionSafeForTileBasedTranspose(hlo)) { - return false; + if (opcode == HloOpcode::kFusion) { + params_012 = FilterInputsForShmemTranspose(hlo, params_012); + if (params_012.empty()) { + return false; + } } // Each of our shared memory tiles has 32*33 elements (so ~4kb, if the diff --git a/tensorflow/compiler/xla/service/gpu/tests/gpu_kernel_tiling_test.cc b/tensorflow/compiler/xla/service/gpu/tests/gpu_kernel_tiling_test.cc index a302b582ed..8004ebdc8e 100644 --- a/tensorflow/compiler/xla/service/gpu/tests/gpu_kernel_tiling_test.cc +++ b/tensorflow/compiler/xla/service/gpu/tests/gpu_kernel_tiling_test.cc @@ -65,7 +65,7 @@ TEST_F(GpuKernelTilingTest, UnnestedTransposeWithProperDimensionsTiled) { CompileAndVerifyIr(std::move(hlo_module), R"( ; CHECK-LABEL: define void @copy -; CHECK: tail call void @llvm.nvvm.barrier0() +; CHECK: call void @llvm.nvvm.barrier0() ; CHECK: } )", /*match_optimized_ir=*/true); @@ -91,7 +91,7 @@ TEST_F(GpuKernelTilingTest, UnnestedTransposeWithSmallDimensionsNotTiled) { CompileAndVerifyIr(std::move(hlo_module), R"( ; CHECK-LABEL: define void @copy -; CHECK-NOT: tail call void @llvm.nvvm.barrier0() +; CHECK-NOT: call void @llvm.nvvm.barrier0() ; CHECK: } )", /*match_optimized_ir=*/true); @@ -118,7 +118,7 @@ TEST_F(GpuKernelTilingTest, SimpleFusionWithTransposeTiled) { CompileAndVerifyIr(std::move(hlo_module), R"( ; CHECK-LABEL: define void @fusion -; CHECK: tail call void @llvm.nvvm.barrier0() +; CHECK: call void @llvm.nvvm.barrier0() ; CHECK: } )", /*match_optimized_ir=*/true); @@ -152,7 +152,7 @@ TEST_F(GpuKernelTilingTest, MultipleOutputFusionWithOnePossibleTransposeTiled) { CompileAndVerifyIr(std::move(hlo_module), R"( ; CHECK-LABEL: define void @fusion -; CHECK: tail call void @llvm.nvvm.barrier0() +; CHECK: call void @llvm.nvvm.barrier0() ; CHECK: } )", /*match_optimized_ir=*/true); @@ -187,13 +187,13 @@ TEST_F(GpuKernelTilingTest, CompileAndVerifyIr(std::move(hlo_module), R"( ; CHECK-LABEL: define void @fusion -; CHECK-NOT: tail call void @llvm.nvvm.barrier0() +; CHECK-NOT: call void @llvm.nvvm.barrier0() ; CHECK: } )", /*match_optimized_ir=*/true); } -TEST_F(GpuKernelTilingTest, FusionTransposeWithReverseNotTiled) { +TEST_F(GpuKernelTilingTest, TransposedInputWithUserReverseNotTiled) { const char *const kHloString = R"( HloModule FusionTransposeWithReverseNotTiled fused_computation.1 { @@ -214,12 +214,76 @@ TEST_F(GpuKernelTilingTest, FusionTransposeWithReverseNotTiled) { CompileAndVerifyIr(std::move(hlo_module), R"( ; CHECK-LABEL: define void @fusion -; CHECK-NOT: tail call void @llvm.nvvm.barrier0() +; CHECK-NOT: call void @llvm.nvvm.barrier0() ; CHECK: } )", /*match_optimized_ir=*/true); } +TEST_F(GpuKernelTilingTest, TransposedInputWithUserBitcastNotTiled) { + const char *const kHloString = R"( + HloModule TransposedInputWithUserBitcast + + fused_computation { + param_0 = f32[20,20]{1,0} parameter(0) + ROOT bitcast = f32[20,20]{0,1} bitcast(param_0) + } + + ENTRY kernel_entry { + parameter.0 = f32[20,20]{1,0} parameter(0) + ROOT fusion = f32[20,20]{0,1} fusion(parameter.0), + kind=kLoop, calls=fused_computation + })"; + + // Check that a call to llvm.nvvm.barrier0 is not generated. + auto hlo_module = + ParseHloString(kHloString, ConfigWithoutLayoutAssignment()).ValueOrDie(); + CompileAndVerifyIr(std::move(hlo_module), + R"( +; CHECK-LABEL: define void @fusion +; CHECK-NOT: call void @llvm.nvvm.barrier0() +; CHECK: } +)", + /*match_optimized_ir=*/true); + + // Check that the kernel runs correctly. + EXPECT_TRUE(RunAndCompareNoHloPasses(kHloString, ErrorSpec{0.0})); +} + +TEST_F(GpuKernelTilingTest, TransposedInputWithoutUnsafeUseTiled) { + const char *const kHloString = R"( + HloModule TwoTransposedInputs + + fused_computation { + param_0 = f32[64,64]{1,0} parameter(0) + param_1 = f32[64,64]{1,0} parameter(1) + bitcast = f32[64,64]{0,1} bitcast(param_0) + copy = f32[64,64]{0,1} copy(param_1) + ROOT tuple = (f32[64,64]{0,1}, f32[64,64]{0,1}) tuple(bitcast, copy) + } + + ENTRY kernel_entry { + parameter.0 = f32[64,64]{1,0} parameter(0) + parameter.1 = f32[64,64]{1,0} parameter(1) + ROOT fusion = (f32[64,64]{0,1}, f32[64,64]{0,1}) + fusion(parameter.0, parameter.1), + kind=kLoop, calls=fused_computation + })"; + + // Check that a call to llvm.nvvm.barrier0 is not generated. + auto hlo_module = + ParseHloString(kHloString, ConfigWithoutLayoutAssignment()).ValueOrDie(); + CompileAndVerifyIr(std::move(hlo_module), + R"( +; CHECK-LABEL: define void @fusion +; CHECK: call void @llvm.nvvm.barrier0() +; CHECK: } +)", + /*match_optimized_ir=*/true); + // Check that the kernel runs correctly. + EXPECT_TRUE(RunAndCompareNoHloPasses(kHloString, ErrorSpec{0.0})); +} + } // namespace } // namespace gpu } // namespace xla -- GitLab From 187a8cba72aae56ed8dfbe5c2d5779664b7683f2 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Sun, 6 Jan 2019 19:22:26 -0800 Subject: [PATCH 404/622] Change TensorFlow's resize_uninitialized wrapper to call libc++'s __resize_default_init extension when available. PiperOrigin-RevId: 228095086 --- tensorflow/core/BUILD | 1 + tensorflow/core/lib/gtl/stl_util.h | 37 +++++++++++++++++++++++++----- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/tensorflow/core/BUILD b/tensorflow/core/BUILD index 7c9decbd09..5e6c8d4dd8 100644 --- a/tensorflow/core/BUILD +++ b/tensorflow/core/BUILD @@ -2193,6 +2193,7 @@ cc_library( ], }), deps = tf_additional_lib_deps() + [ + "@com_google_absl//absl/meta:type_traits", "@com_google_absl//absl/strings", "//third_party/eigen3", "@com_google_absl//absl/base:core_headers", diff --git a/tensorflow/core/lib/gtl/stl_util.h b/tensorflow/core/lib/gtl/stl_util.h index ffeca4e88a..853a290bf6 100644 --- a/tensorflow/core/lib/gtl/stl_util.h +++ b/tensorflow/core/lib/gtl/stl_util.h @@ -23,9 +23,12 @@ limitations under the License. #include #include #include +#include #include #include +#include "absl/meta/type_traits.h" + namespace tensorflow { namespace gtl { @@ -48,16 +51,38 @@ inline const T* vector_as_array(const std::vector* v) { return v->data(); } +namespace gtl_internal { + +// HasMember is true_type or false_type, depending on whether or not +// T has a __resize_default_init member. Resize will call the +// __resize_default_init member if it exists, and will call the resize +// member otherwise. +template +struct ResizeUninitializedTraits { + using HasMember = std::false_type; + static void Resize(string_type* s, size_t new_size) { s->resize(new_size); } +}; + +// __resize_default_init is provided by libc++ >= 8.0 and by Google's internal +// ::string implementation. +template +struct ResizeUninitializedTraits< + string_type, absl::void_t() + .__resize_default_init(237))> > { + using HasMember = std::true_type; + static void Resize(string_type* s, size_t new_size) { + s->__resize_default_init(new_size); + } +}; + +} // namespace gtl_internal + // Like str->resize(new_size), except any new characters added to "*str" as a // result of resizing may be left uninitialized, rather than being filled with // '0' bytes. Typically used when code is then going to overwrite the backing -// store of the string with known data. Uses a Google extension to ::string. +// store of the string with known data. inline void STLStringResizeUninitialized(string* s, size_t new_size) { -#if __google_stl_resize_uninitialized_string - s->resize_uninitialized(new_size); -#else - s->resize(new_size); -#endif + gtl_internal::ResizeUninitializedTraits::Resize(s, new_size); } // Calls delete (non-array version) on the SECOND item (pointer) in each pair in -- GitLab From b8f2207ead5c6ce6e7e98479a88a08e4977aa83d Mon Sep 17 00:00:00 2001 From: Tom Hennigan Date: Sun, 6 Jan 2019 23:46:22 -0800 Subject: [PATCH 405/622] Increase length of stack trace for original variable definition. PiperOrigin-RevId: 228112246 --- tensorflow/python/ops/variable_scope.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tensorflow/python/ops/variable_scope.py b/tensorflow/python/ops/variable_scope.py index 64cdde3d85..35c00778ae 100644 --- a/tensorflow/python/ops/variable_scope.py +++ b/tensorflow/python/ops/variable_scope.py @@ -842,8 +842,11 @@ class _VariableStore(object): if isinstance(var, resource_variable_ops.ResourceVariable): raise ValueError(err_msg) tb = var.op.traceback[::-1] - # Throw away internal tf entries and only take a few lines. - tb = [x for x in tb if "tensorflow/python" not in x[0]][:3] + # Throw away internal tf entries and only take a few lines. In some + # cases the traceback can be longer (e.g. if someone uses factory + # functions to create variables) so we take more than needed in the + # default case. + tb = [x for x in tb if "tensorflow/python" not in x[0]][:5] raise ValueError("%s Originally defined at:\n\n%s" % (err_msg, "".join( traceback.format_list(tb)))) found_var = self._vars[name] -- GitLab From 57d450732007e33db361ad8f16a1e8655a55dc39 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Mon, 7 Jan 2019 00:42:55 -0800 Subject: [PATCH 406/622] Internal change. PiperOrigin-RevId: 228117862 --- tensorflow/contrib/gan/BUILD | 1 - 1 file changed, 1 deletion(-) diff --git a/tensorflow/contrib/gan/BUILD b/tensorflow/contrib/gan/BUILD index 08df38ad27..db0868fb2c 100644 --- a/tensorflow/contrib/gan/BUILD +++ b/tensorflow/contrib/gan/BUILD @@ -2,7 +2,6 @@ load("//tensorflow:tensorflow.bzl", "py_test") package(default_visibility = [ - "//learning/brain/contrib/tfgan:__subpackages__", "//tensorflow:__subpackages__", ]) -- GitLab From 134b9e580f639005f8e5986c53884183d6d9bde0 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Mon, 7 Jan 2019 01:03:09 -0800 Subject: [PATCH 407/622] compat: Update forward compatibility horizon to 2019-01-07 PiperOrigin-RevId: 228120298 --- tensorflow/python/compat/compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/python/compat/compat.py b/tensorflow/python/compat/compat.py index b779c80aa7..dbad494e40 100644 --- a/tensorflow/python/compat/compat.py +++ b/tensorflow/python/compat/compat.py @@ -27,7 +27,7 @@ import datetime from tensorflow.python.util import tf_contextlib from tensorflow.python.util.tf_export import tf_export -_FORWARD_COMPATIBILITY_HORIZON = datetime.date(2019, 1, 6) +_FORWARD_COMPATIBILITY_HORIZON = datetime.date(2019, 1, 7) @tf_export("compat.forward_compatible") -- GitLab From 56ab6a2a7b224cb4a30e545035639ab84d82c58f Mon Sep 17 00:00:00 2001 From: Tom Hennigan Date: Mon, 7 Jan 2019 01:31:17 -0800 Subject: [PATCH 408/622] Fix Template.variables for templates with empty name. PiperOrigin-RevId: 228123554 --- .../python/kernel_tests/template_test.py | 15 ++++++++++ tensorflow/python/ops/template.py | 30 ++++--------------- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/tensorflow/python/kernel_tests/template_test.py b/tensorflow/python/kernel_tests/template_test.py index 3b2a56bd1f..f587a7ec43 100644 --- a/tensorflow/python/kernel_tests/template_test.py +++ b/tensorflow/python/kernel_tests/template_test.py @@ -160,6 +160,21 @@ class TemplateTest(test.TestCase): self.assertEqual(1, len(result)) self.assertNotEqual(len(first), len(result)) + @test_util.run_in_graph_and_eager_modes + def test_template_with_empty_name(self): + tpl = template.make_template("", variable_scoped_function) + with variable_scope.variable_scope("outer"): + x = variable_scope.get_variable("x", []) + v = tpl() + self.assertEqual("outer/", tpl.variable_scope_name) + self.assertEqual("outer//dummy:0", v.name) + if context.executing_eagerly(): + # In eager mode `x` is not visible to the template since the template does + # not rely on global collections. + self.assertEqual([v], tpl.variables) + else: + self.assertEqual([x, v], tpl.variables) + @test_util.run_in_graph_and_eager_modes def test_template_with_name(self): tmpl1 = template.make_template("s1", variable_scoped_function) diff --git a/tensorflow/python/ops/template.py b/tensorflow/python/ops/template.py index 7c2d3be338..4eaa16a22c 100644 --- a/tensorflow/python/ops/template.py +++ b/tensorflow/python/ops/template.py @@ -387,8 +387,11 @@ class Template(checkpointable.CheckpointableBase): """Returns the variable scope name created by this Template.""" if self._variable_scope: name = self._variable_scope.name - # To prevent partial matches on the scope_name, we add '/' at the end. - return name if name[-1] == "/" else name + "/" + if not name or name[-1] == "/": + return name + else: + # To prevent partial matches on the scope_name, we add '/' at the end. + return name + "/" @property def variables(self): @@ -646,29 +649,6 @@ class EagerTemplate(Template): with self._template_store.as_default(): return self._call_func(args, kwargs) - @property - def name(self): - """Returns the name given to this Template.""" - return self._name - - @property - def func(self): - """Returns the func given to this Template.""" - return self._func - - @property - def variable_scope(self): - """Returns the variable scope object created by this Template.""" - return self._variable_scope - - @property - def variable_scope_name(self): - """Returns the variable scope name created by this Template.""" - if self._variable_scope: - name = self._variable_scope.name - # To prevent partial matches on the scope_name, we add '/' at the end. - return name if name[-1] == "/" else name + "/" - @property def variables(self): """Returns the list of variables created by the Template.""" -- GitLab From 48d0e4d813b914fc480c51de5c48903b9bcc4cc7 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Mon, 7 Jan 2019 01:46:24 -0800 Subject: [PATCH 409/622] Fixing pasta by not stripping the subdirectory. PiperOrigin-RevId: 228125275 --- third_party/pasta/BUILD.bazel | 5 +++-- third_party/pasta/build_defs.bzl | 12 ++++++++++++ third_party/pasta/workspace.bzl | 2 +- 3 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 third_party/pasta/build_defs.bzl diff --git a/third_party/pasta/BUILD.bazel b/third_party/pasta/BUILD.bazel index 6be33511e4..ade681b606 100644 --- a/third_party/pasta/BUILD.bazel +++ b/third_party/pasta/BUILD.bazel @@ -1,5 +1,6 @@ # Description: # AST-based python refactoring. +load("@//third_party/pasta:build_defs.bzl", "copy_srcs") licenses(["notice"]) # Apache2 @@ -7,7 +8,7 @@ exports_files(["LICENSE"]) py_library( name = "pasta", - srcs = [ + srcs = copy_srcs([ "__init__.py", "augment/__init__.py", "augment/errors.py", @@ -23,7 +24,7 @@ py_library( "base/scope.py", "base/test_utils.py", "base/token_generator.py", - ], + ]), srcs_version = "PY2AND3", visibility = ["//visibility:public"], ) diff --git a/third_party/pasta/build_defs.bzl b/third_party/pasta/build_defs.bzl new file mode 100644 index 0000000000..0a5316de40 --- /dev/null +++ b/third_party/pasta/build_defs.bzl @@ -0,0 +1,12 @@ +"""Skylark makros for building pasta.""" + +def copy_srcs(srcs): + """Copies srcs from 'pasta' to parent directory.""" + for src in srcs: + native.genrule( + name = src.replace(".", "_"), + srcs = ["pasta/" + src], + outs = [src], + cmd = "mkdir -p $$(dirname $@); cp $< $@", + ) + return srcs diff --git a/third_party/pasta/workspace.bzl b/third_party/pasta/workspace.bzl index 063bdb7b98..e46cc4a45e 100644 --- a/third_party/pasta/workspace.bzl +++ b/third_party/pasta/workspace.bzl @@ -9,7 +9,7 @@ def repo(): "https://mirror.bazel.build/github.com/google/pasta/archive/c3d72cdee6fc806251949e912510444d58d7413c.tar.gz", "https://github.com/google/pasta/archive/c3d72cdee6fc806251949e912510444d58d7413c.tar.gz", ], - strip_prefix = "pasta-c3d72cdee6fc806251949e912510444d58d7413c/pasta", + strip_prefix = "pasta-c3d72cdee6fc806251949e912510444d58d7413c", sha256 = "b5905f9cecc4b28363c563f3c4cb0545288bd35f7cc72c55066e97e53befc084", build_file = "//third_party/pasta:BUILD.bazel", system_build_file = "//third_party/pasta:BUILD.system", -- GitLab From f64f7f787d3596cb7c9228f131f06c159a0ec188 Mon Sep 17 00:00:00 2001 From: Tamara Norman Date: Mon, 7 Jan 2019 02:38:36 -0800 Subject: [PATCH 410/622] Added option in conversion script to convert an entire directory in place when --inplace=True given as an argument PiperOrigin-RevId: 228130782 --- tensorflow/tools/compatibility/ast_edits.py | 34 ++++++++++++++++++- .../tools/compatibility/tf_upgrade_v2_main.py | 10 +++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/tensorflow/tools/compatibility/ast_edits.py b/tensorflow/tools/compatibility/ast_edits.py index 0ec6c2ac8c..859fd95314 100644 --- a/tensorflow/tools/compatibility/ast_edits.py +++ b/tensorflow/tools/compatibility/ast_edits.py @@ -493,7 +493,7 @@ class ASTCodeUpgrader(object): process_errors) def process_tree(self, root_directory, output_root_directory, - copy_other_files): + copy_other_files, in_place): """Processes upgrades on an entire tree of python files in place. Note that only Python files. If you have custom code in other languages, @@ -503,11 +503,20 @@ class ASTCodeUpgrader(object): root_directory: Directory to walk and process. output_root_directory: Directory to use as base. copy_other_files: Copy files that are not touched by this converter. + in_place: Allow the conversion of an entire directory in place. Returns: A tuple of files processed, the report string ofr all files, and errors """ + if output_root_directory == root_directory: + if in_place: + return self.process_tree_inplace(root_directory) + else: + print("In order to copy a directory in place the `--inplace` input " + "arg must be set to `True`.") + sys.exit(1) + # make sure output directory doesn't exist if output_root_directory and os.path.exists(output_root_directory): print("Output directory %r must not already exist." % @@ -564,3 +573,26 @@ class ASTCodeUpgrader(object): os.makedirs(output_directory) shutil.copy(input_path, output_path) return file_count, report, tree_errors + + def process_tree_inplace(self, root_directory): + """Process a directory of python files in place.""" + files_to_process = [] + for dir_name, _, file_list in os.walk(root_directory): + py_files = [os.path.join(dir_name, + f) for f in file_list if f.endswith(".py")] + files_to_process += py_files + + file_count = 0 + tree_errors = [] + report = "" + report += ("=" * 80) + "\n" + report += "Input tree: %r\n" % root_directory + report += ("=" * 80) + "\n" + + for path in files_to_process: + file_count += 1 + _, l_report, l_errors = self.process_file(path, path) + tree_errors += l_errors + report += l_report + + return file_count, report, tree_errors diff --git a/tensorflow/tools/compatibility/tf_upgrade_v2_main.py b/tensorflow/tools/compatibility/tf_upgrade_v2_main.py index 543d078642..870bc6f216 100644 --- a/tensorflow/tools/compatibility/tf_upgrade_v2_main.py +++ b/tensorflow/tools/compatibility/tf_upgrade_v2_main.py @@ -59,6 +59,14 @@ Simple usage: "copy the other files."), type=bool, default=True) + parser.add_argument( + "--inplace", + dest="in_place", + help=("If converting a whole tree of files, whether to " + "allow the conversion to be performed on the " + "files in the input tree."), + type=bool, + default=False) parser.add_argument( "--reportfile", dest="report_filename", @@ -86,7 +94,7 @@ Simple usage: "--outtree= argument is required when converting a " "file tree.") files_processed, report_text, errors = upgrade.process_tree( - args.input_tree, args.output_tree, args.copy_other_files) + args.input_tree, args.output_tree, args.copy_other_files, args.in_place) else: parser.print_help() if report_text: -- GitLab From 2703d8e84aced78a05ec7ad1ea0e5f4d0fe70f9f Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Mon, 7 Jan 2019 07:03:10 -0800 Subject: [PATCH 411/622] Change //tensorflow/compiler/tf2xla/python:xla to a tf_custom_op_py_library Previously it was a simple py_library what didn't linked in the op registration code causing errors when no other dependency pulled in the actual implementations. PiperOrigin-RevId: 228158673 --- tensorflow/compiler/tf2xla/ops/BUILD | 17 ++++++++++++++++- tensorflow/compiler/tf2xla/python/BUILD | 7 ++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/tensorflow/compiler/tf2xla/ops/BUILD b/tensorflow/compiler/tf2xla/ops/BUILD index 4dce0a2102..7140b6a122 100644 --- a/tensorflow/compiler/tf2xla/ops/BUILD +++ b/tensorflow/compiler/tf2xla/ops/BUILD @@ -4,7 +4,11 @@ package( licenses(["notice"]) # Apache 2.0 -load("//tensorflow:tensorflow.bzl", "tf_gen_op_wrapper_py") +load( + "//tensorflow:tensorflow.bzl", + "tf_custom_op_library", + "tf_gen_op_wrapper_py", +) cc_library( name = "xla_ops", @@ -24,3 +28,14 @@ tf_gen_op_wrapper_py( ":xla_ops", ], ) + +tf_custom_op_library( + name = "_xla_ops.so", + srcs = [ + "xla_ops.cc", + ], + deps = [ + "//tensorflow/core:lib", + "@com_google_absl//absl/algorithm:container", + ], +) diff --git a/tensorflow/compiler/tf2xla/python/BUILD b/tensorflow/compiler/tf2xla/python/BUILD index fef97b98c3..9abdb04d77 100644 --- a/tensorflow/compiler/tf2xla/python/BUILD +++ b/tensorflow/compiler/tf2xla/python/BUILD @@ -15,6 +15,7 @@ load( "//tensorflow/core:platform/default/build_config.bzl", "tf_py_clif_cc", ) +load("//tensorflow:tensorflow.bzl", "tf_custom_op_py_library") tf_py_clif_cc( name = "xla_op_registry", @@ -27,9 +28,13 @@ tf_py_clif_cc( ], ) -py_library( +tf_custom_op_py_library( name = "xla", srcs = ["xla.py"], + dso = ["//tensorflow/compiler/tf2xla/ops:_xla_ops.so"], + kernels = [ + "//tensorflow/compiler/tf2xla/ops:xla_ops", + ], deps = [ "//tensorflow/compiler/tf2xla/ops:gen_xla_ops", "//tensorflow/compiler/xla:xla_data_proto_py", -- GitLab From 36c3466356546f09198ce01982e6049dbf1a1afe Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Mon, 7 Jan 2019 10:31:04 -0800 Subject: [PATCH 412/622] Merged commit includes the following changes: 228190834 by A. Unique TensorFlower: Internal change -- 228187607 by A. Unique TensorFlower: Split depthwise conv GPU code into multiple files This file was a bottleneck during compilation, often taking many minutes to compile. In local testing this change reduces the wall-clock build time for the depthwise conv GPU kernels from 163s to 105s. -- 228181904 by A. Unique TensorFlower: Fix py2 and py3 of gen_git_version.py to fix 1.13 release. Byteify function to handle py2 and py3. -- 228161227 by A. Unique TensorFlower: Create Tensorflow version of LLVM's opt. This is useful for iterating on individual passes in the OptimizationPassRegistry. This is run with a command of the form optimization_pass_runner --input_file_path=/tmp/input.pbtxt --output_file_path=/tmp/output.pbtxt --optimization_pass=NameOfGraphOptimizationPass -- PiperOrigin-RevId: 228190834 --- .../common_runtime/optimization_registry.h | 4 + tensorflow/core/kernels/BUILD | 10 +- ...v_op_gpu.cu.cc => depthwise_conv_op_gpu.h} | 25 +- .../depthwise_conv_op_gpu_double.cu.cc | 30 ++ .../kernels/depthwise_conv_op_gpu_float.cu.cc | 30 ++ .../kernels/depthwise_conv_op_gpu_half.cu.cc | 30 ++ tensorflow/opensource_only.files | 416 +++++++++--------- tensorflow/tools/git/gen_git_source.py | 4 +- tensorflow/tools/optimization/BUILD | 52 +++ .../gpu_optimization_pass_runner_main.cc | 60 +++ .../optimization/optimization_pass_runner.cc | 167 +++++++ .../optimization/optimization_pass_runner.h | 61 +++ 12 files changed, 661 insertions(+), 228 deletions(-) rename tensorflow/core/kernels/{depthwise_conv_op_gpu.cu.cc => depthwise_conv_op_gpu.h} (98%) create mode 100644 tensorflow/core/kernels/depthwise_conv_op_gpu_double.cu.cc create mode 100644 tensorflow/core/kernels/depthwise_conv_op_gpu_float.cu.cc create mode 100644 tensorflow/core/kernels/depthwise_conv_op_gpu_half.cu.cc create mode 100644 tensorflow/tools/optimization/BUILD create mode 100644 tensorflow/tools/optimization/gpu_optimization_pass_runner_main.cc create mode 100644 tensorflow/tools/optimization/optimization_pass_runner.cc create mode 100644 tensorflow/tools/optimization/optimization_pass_runner.h diff --git a/tensorflow/core/common_runtime/optimization_registry.h b/tensorflow/core/common_runtime/optimization_registry.h index 19a76529bb..0e31f389aa 100644 --- a/tensorflow/core/common_runtime/optimization_registry.h +++ b/tensorflow/core/common_runtime/optimization_registry.h @@ -95,6 +95,10 @@ class OptimizationPassRegistry { void Register(Grouping grouping, int phase, std::unique_ptr pass); + const std::map& groups() { + return groups_; + } + // Run all passes in grouping, ordered by phase, with the same // options. Status RunGrouping(Grouping grouping, diff --git a/tensorflow/core/kernels/BUILD b/tensorflow/core/kernels/BUILD index c72d04d3af..3891caf5d3 100644 --- a/tensorflow/core/kernels/BUILD +++ b/tensorflow/core/kernels/BUILD @@ -3695,7 +3695,15 @@ tf_kernel_library( tf_kernel_library( name = "depthwise_conv_op", - prefix = "depthwise_conv_op", + srcs = ["depthwise_conv_op.cc"], + hdrs = ["depthwise_conv_op.h"], + gpu_srcs = [ + "depthwise_conv_op.h", + "depthwise_conv_op_gpu.h", + "depthwise_conv_op_gpu_double.cu.cc", + "depthwise_conv_op_gpu_float.cu.cc", + "depthwise_conv_op_gpu_half.cu.cc", + ], deps = [ ":bounds_check", ":conv_ops", diff --git a/tensorflow/core/kernels/depthwise_conv_op_gpu.cu.cc b/tensorflow/core/kernels/depthwise_conv_op_gpu.h similarity index 98% rename from tensorflow/core/kernels/depthwise_conv_op_gpu.cu.cc rename to tensorflow/core/kernels/depthwise_conv_op_gpu.h index e811968d27..098853e684 100644 --- a/tensorflow/core/kernels/depthwise_conv_op_gpu.cu.cc +++ b/tensorflow/core/kernels/depthwise_conv_op_gpu.h @@ -13,6 +13,9 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ +#ifndef TENSORFLOW_CORE_KERNELS_DEPTHWISE_CONV_OP_GPU_H_ +#define TENSORFLOW_CORE_KERNELS_DEPTHWISE_CONV_OP_GPU_H_ + #if GOOGLE_CUDA #define EIGEN_USE_GPU @@ -38,7 +41,7 @@ using Eigen::GpuDevice; // Returns whether depthwise convolution forward or backward input pass can be // performed using the faster ('Small') variant of the kernel. -EIGEN_DEVICE_FUNC bool CanLaunchDepthwiseConv2dGPUSmall( +inline EIGEN_DEVICE_FUNC bool CanLaunchDepthwiseConv2dGPUSmall( const DepthwiseArgs& args) { return args.depth_multiplier == 1 && args.stride == 1 && args.in_rows <= 32 && args.in_cols <= 32 && args.in_rows == args.out_rows && @@ -51,7 +54,7 @@ EIGEN_DEVICE_FUNC bool CanLaunchDepthwiseConv2dGPUSmall( // Returns whether depthwise convolution backward filter pass can be performed // using the faster ('Small') variant of the kernel. -EIGEN_DEVICE_FUNC bool CanLaunchDepthwiseConv2dBackpropFilterGPUSmall( +inline EIGEN_DEVICE_FUNC bool CanLaunchDepthwiseConv2dBackpropFilterGPUSmall( const DepthwiseArgs& args, const int block_height) { return args.depth_multiplier == 1 && args.stride == 1 && args.in_rows <= 32 && args.in_cols <= 32 && args.in_rows == args.out_rows && @@ -652,13 +655,12 @@ struct PseudoHalfType { }; } // namespace detail -namespace { // Maps to float if T is __half, and to T otherwise. template using PseudoHalfType = typename detail::PseudoHalfType::Type; // Returns whether the context's GPU supports efficient fp16 math. -bool HasFastHalfMath(OpKernelContext* ctx) { +inline bool HasFastHalfMath(OpKernelContext* ctx) { int major, minor; ctx->op_device_context() ->stream() @@ -669,7 +671,6 @@ bool HasFastHalfMath(OpKernelContext* ctx) { // GPUs before sm_53 don't support fp16 math, and sm_61's fp16 math is slow. return cuda_arch >= 530 && cuda_arch != 610; } -} // namespace template ::operator()(OpKernelContext* ctx, } } -template struct LaunchDepthwiseConvOp; -template struct LaunchDepthwiseConvOp; -template struct LaunchDepthwiseConvOp; - // A Cuda kernel to compute the depthwise convolution backprop w.r.t. input. template @@ -1030,10 +1027,6 @@ void LaunchDepthwiseConvBackpropInputOp::operator()( } } -template struct LaunchDepthwiseConvBackpropInputOp; -template struct LaunchDepthwiseConvBackpropInputOp; -template struct LaunchDepthwiseConvBackpropInputOp; - // A Cuda kernel to compute the depthwise convolution backprop w.r.t. filter. template @@ -1803,9 +1796,7 @@ void LaunchDepthwiseConvBackpropFilterOp::operator()( ctx, args, out_backprop, input, filter_backprop, data_format)); } } - -template struct LaunchDepthwiseConvBackpropFilterOp; -template struct LaunchDepthwiseConvBackpropFilterOp; -template struct LaunchDepthwiseConvBackpropFilterOp; } // namespace tensorflow #endif // GOOGLE_CUDA + +#endif // TENSORFLOW_CORE_KERNELS_DEPTHWISE_CONV_OP_GPU_H_ diff --git a/tensorflow/core/kernels/depthwise_conv_op_gpu_double.cu.cc b/tensorflow/core/kernels/depthwise_conv_op_gpu_double.cu.cc new file mode 100644 index 0000000000..073e7cf269 --- /dev/null +++ b/tensorflow/core/kernels/depthwise_conv_op_gpu_double.cu.cc @@ -0,0 +1,30 @@ +/* Copyright 2018 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. +==============================================================================*/ + +#if GOOGLE_CUDA +#define EIGEN_USE_GPU + +#include "tensorflow/core/kernels/depthwise_conv_op.h" +#include "tensorflow/core/kernels/depthwise_conv_op_gpu.h" + +namespace tensorflow { +using Eigen::GpuDevice; + +template struct LaunchDepthwiseConvOp; +template struct LaunchDepthwiseConvBackpropInputOp; +template struct LaunchDepthwiseConvBackpropFilterOp; +} // namespace tensorflow + +#endif // GOOGLE_CUDA diff --git a/tensorflow/core/kernels/depthwise_conv_op_gpu_float.cu.cc b/tensorflow/core/kernels/depthwise_conv_op_gpu_float.cu.cc new file mode 100644 index 0000000000..4b0e15e476 --- /dev/null +++ b/tensorflow/core/kernels/depthwise_conv_op_gpu_float.cu.cc @@ -0,0 +1,30 @@ +/* Copyright 2018 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. +==============================================================================*/ + +#if GOOGLE_CUDA +#define EIGEN_USE_GPU + +#include "tensorflow/core/kernels/depthwise_conv_op.h" +#include "tensorflow/core/kernels/depthwise_conv_op_gpu.h" + +namespace tensorflow { +using Eigen::GpuDevice; + +template struct LaunchDepthwiseConvOp; +template struct LaunchDepthwiseConvBackpropInputOp; +template struct LaunchDepthwiseConvBackpropFilterOp; +} // namespace tensorflow + +#endif // GOOGLE_CUDA diff --git a/tensorflow/core/kernels/depthwise_conv_op_gpu_half.cu.cc b/tensorflow/core/kernels/depthwise_conv_op_gpu_half.cu.cc new file mode 100644 index 0000000000..2db9fa4dff --- /dev/null +++ b/tensorflow/core/kernels/depthwise_conv_op_gpu_half.cu.cc @@ -0,0 +1,30 @@ +/* Copyright 2018 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. +==============================================================================*/ + +#if GOOGLE_CUDA +#define EIGEN_USE_GPU + +#include "tensorflow/core/kernels/depthwise_conv_op.h" +#include "tensorflow/core/kernels/depthwise_conv_op_gpu.h" + +namespace tensorflow { +using Eigen::GpuDevice; + +template struct LaunchDepthwiseConvOp; +template struct LaunchDepthwiseConvBackpropInputOp; +template struct LaunchDepthwiseConvBackpropFilterOp; +} // namespace tensorflow + +#endif // GOOGLE_CUDA diff --git a/tensorflow/opensource_only.files b/tensorflow/opensource_only.files index e00d063cfe..40f87d6c36 100644 --- a/tensorflow/opensource_only.files +++ b/tensorflow/opensource_only.files @@ -1,248 +1,248 @@ -tensorflow/contrib/tpu/profiler/pip_package/BUILD -tensorflow/contrib/tpu/profiler/pip_package/setup.py -tensorflow/contrib/tpu/profiler/pip_package/README -tensorflow/contrib/tpu/profiler/pip_package/build_pip_package.sh -tensorflow/contrib/tpu/profiler/pip_package/cloud_tpu_profiler/main.py -tensorflow/contrib/tpu/profiler/pip_package/cloud_tpu_profiler/__init__.py -tensorflow/contrib/mpi/BUILD -tensorflow/tools/ci_build/remote/BUILD -tensorflow/tools/pip_package/README -tensorflow/tools/pip_package/MANIFEST.in -tensorflow/tools/pip_package/simple_console.py -tensorflow/tools/pip_package/build_pip_package.sh -tensorflow/tools/pip_package/check_load_py_test.py -tensorflow/tools/pip_package/pip_smoke_test.py -tensorflow/tools/pip_package/simple_console_for_windows.py -tensorflow/tools/pip_package/setup.py -tensorflow/tools/pip_package/BUILD -tensorflow/tools/lib_package/concat_licenses.sh -tensorflow/tools/lib_package/libtensorflow_test.c -tensorflow/tools/lib_package/LibTensorFlowTest.java -tensorflow/tools/lib_package/BUILD -tensorflow/tools/lib_package/libtensorflow_test.sh -tensorflow/tools/lib_package/README.md -tensorflow/tools/lib_package/libtensorflow_java_test.sh -tensorflow/tools/def_file_filter/def_file_filter_configure.bzl -tensorflow/tools/def_file_filter/BUILD -tensorflow/tools/def_file_filter/BUILD.tpl -tensorflow/tools/def_file_filter/def_file_filter.py.tpl -tensorflow/third_party/mkl/MKL_LICENSE -tensorflow/third_party/mkl/LICENSE -tensorflow/third_party/mkl/BUILD -tensorflow/third_party/mkl/mkl.BUILD -tensorflow/third_party/mkl/build_defs.bzl -tensorflow/third_party/backports_weakref.BUILD -tensorflow/third_party/toolchains/clang6/BUILD +tensorflow/api_template_v1.__init__.py +tensorflow/compat_template_v1.__init__.py +tensorflow/stream_executor/BUILD +tensorflow/third_party/curl.BUILD +tensorflow/third_party/snappy.BUILD +tensorflow/third_party/tflite_mobilenet_float.BUILD +tensorflow/third_party/libxsmm.BUILD +tensorflow/third_party/cub.BUILD +tensorflow/third_party/repo.bzl +tensorflow/third_party/systemlibs/curl.BUILD +tensorflow/third_party/systemlibs/snappy.BUILD +tensorflow/third_party/systemlibs/protobuf.BUILD +tensorflow/third_party/systemlibs/absl_py.BUILD +tensorflow/third_party/systemlibs/google_cloud_cpp.google.cloud.bigtable.BUILD +tensorflow/third_party/systemlibs/astor.BUILD +tensorflow/third_party/systemlibs/absl_py.absl.flags.BUILD +tensorflow/third_party/systemlibs/png.BUILD +tensorflow/third_party/systemlibs/BUILD.tpl +tensorflow/third_party/systemlibs/boringssl.BUILD +tensorflow/third_party/systemlibs/absl_py.absl.testing.BUILD +tensorflow/third_party/systemlibs/swig.BUILD +tensorflow/third_party/systemlibs/double_conversion.BUILD +tensorflow/third_party/systemlibs/gif.BUILD +tensorflow/third_party/systemlibs/cython.BUILD +tensorflow/third_party/systemlibs/google_cloud_cpp.BUILD +tensorflow/third_party/systemlibs/zlib.BUILD +tensorflow/third_party/systemlibs/grpc.BUILD +tensorflow/third_party/systemlibs/re2.BUILD +tensorflow/third_party/systemlibs/build_defs.bzl.tpl +tensorflow/third_party/systemlibs/protobuf.bzl +tensorflow/third_party/systemlibs/gast.BUILD +tensorflow/third_party/systemlibs/syslibs_configure.bzl +tensorflow/third_party/systemlibs/nsync.BUILD +tensorflow/third_party/systemlibs/jsoncpp.BUILD +tensorflow/third_party/systemlibs/six.BUILD +tensorflow/third_party/systemlibs/lmdb.BUILD +tensorflow/third_party/systemlibs/BUILD +tensorflow/third_party/systemlibs/googleapis.BUILD +tensorflow/third_party/systemlibs/sqlite.BUILD +tensorflow/third_party/systemlibs/termcolor.BUILD +tensorflow/third_party/systemlibs/pcre.BUILD +tensorflow/third_party/tflite_mobilenet_quant.BUILD +tensorflow/third_party/common.bzl +tensorflow/third_party/boringssl/BUILD +tensorflow/third_party/kafka/config.patch +tensorflow/third_party/kafka/BUILD +tensorflow/third_party/android/android_configure.bzl +tensorflow/third_party/android/android_configure.BUILD.tpl +tensorflow/third_party/android/android.bzl.tpl +tensorflow/third_party/android/BUILD +tensorflow/third_party/astor.BUILD +tensorflow/third_party/icu/udata.patch +tensorflow/third_party/tflite_mobilenet.BUILD +tensorflow/third_party/png.BUILD +tensorflow/third_party/mpi/.gitignore +tensorflow/third_party/mpi/BUILD +tensorflow/third_party/nanopb.BUILD +tensorflow/third_party/arm_neon_2_x86_sse.BUILD +tensorflow/third_party/linenoise.BUILD +tensorflow/third_party/llvm/llvm.autogenerated.BUILD +tensorflow/third_party/llvm/llvm.bzl +tensorflow/third_party/llvm/BUILD +tensorflow/third_party/llvm/expand_cmake_vars.py +tensorflow/third_party/swig.BUILD +tensorflow/third_party/png_fix_rpi.patch +tensorflow/third_party/double_conversion.BUILD +tensorflow/third_party/gif.BUILD +tensorflow/third_party/grpc/BUILD +tensorflow/third_party/cython.BUILD +tensorflow/third_party/eigen.BUILD +tensorflow/third_party/codegen.BUILD +tensorflow/third_party/zlib.BUILD +tensorflow/third_party/tensorrt/BUILD.tpl +tensorflow/third_party/tensorrt/tensorrt_configure.bzl +tensorflow/third_party/tensorrt/build_defs.bzl.tpl +tensorflow/third_party/tensorrt/remote.BUILD.tpl +tensorflow/third_party/tensorrt/LICENSE +tensorflow/third_party/tensorrt/BUILD +tensorflow/third_party/tflite_ovic_testdata.BUILD +tensorflow/third_party/fft2d/fft.h +tensorflow/third_party/fft2d/fft2d.BUILD +tensorflow/third_party/fft2d/LICENSE +tensorflow/third_party/fft2d/BUILD tensorflow/third_party/toolchains/clang6/README.md tensorflow/third_party/toolchains/clang6/repo.bzl -tensorflow/third_party/toolchains/clang6/CROSSTOOL.tpl tensorflow/third_party/toolchains/clang6/clang.BUILD -tensorflow/third_party/toolchains/preconfig/ubuntu14.04/tensorrt5/BUILD +tensorflow/third_party/toolchains/clang6/CROSSTOOL.tpl +tensorflow/third_party/toolchains/clang6/BUILD +tensorflow/third_party/toolchains/preconfig/generate/archives.bzl +tensorflow/third_party/toolchains/preconfig/generate/workspace.bzl +tensorflow/third_party/toolchains/preconfig/generate/generate.bzl +tensorflow/third_party/toolchains/preconfig/generate/containers.bzl +tensorflow/third_party/toolchains/preconfig/generate/BUILD tensorflow/third_party/toolchains/preconfig/ubuntu14.04/tensorrt5/build_defs.bzl +tensorflow/third_party/toolchains/preconfig/ubuntu14.04/tensorrt5/BUILD tensorflow/third_party/toolchains/preconfig/ubuntu14.04/py3/BUILD -tensorflow/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/BUILD tensorflow/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc/BUILD +tensorflow/third_party/toolchains/preconfig/ubuntu14.04/cuda9.0-cudnn7/cuda/build_defs.bzl +tensorflow/third_party/toolchains/preconfig/ubuntu14.04/cuda9.0-cudnn7/cuda/BUILD tensorflow/third_party/toolchains/preconfig/ubuntu14.04/cuda10.0-cudnn7/cuda/build_defs.bzl tensorflow/third_party/toolchains/preconfig/ubuntu14.04/cuda10.0-cudnn7/cuda/BUILD +tensorflow/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/BUILD tensorflow/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda10.0/BUILD -tensorflow/third_party/toolchains/preconfig/ubuntu14.04/cuda9.0-cudnn7/cuda/build_defs.bzl -tensorflow/third_party/toolchains/preconfig/ubuntu14.04/cuda9.0-cudnn7/cuda/BUILD tensorflow/third_party/toolchains/preconfig/ubuntu14.04/nccl2/BUILD -tensorflow/third_party/toolchains/preconfig/generate/workspace.bzl -tensorflow/third_party/toolchains/preconfig/generate/containers.bzl -tensorflow/third_party/toolchains/preconfig/generate/generate.bzl -tensorflow/third_party/toolchains/preconfig/generate/archives.bzl -tensorflow/third_party/toolchains/preconfig/generate/BUILD -tensorflow/third_party/toolchains/preconfig/ubuntu16.04/clang/BUILD -tensorflow/third_party/toolchains/preconfig/ubuntu16.04/clang/dummy_toolchain.bzl -tensorflow/third_party/toolchains/preconfig/ubuntu16.04/py3/BUILD -tensorflow/third_party/toolchains/preconfig/win_1803/bazel_018/BUILD -tensorflow/third_party/toolchains/preconfig/win_1803/bazel_018/dummy_toolchain.bzl tensorflow/third_party/toolchains/preconfig/win_1803/py36/BUILD tensorflow/third_party/toolchains/preconfig/win_1803/BUILD -tensorflow/third_party/toolchains/gpus/cuda/build_defs.bzl -tensorflow/third_party/toolchains/gpus/cuda/BUILD -tensorflow/third_party/toolchains/gpus/cuda/cuda/cuda_config.h -tensorflow/third_party/toolchains/gpus/crosstool/BUILD -tensorflow/third_party/toolchains/gpus/crosstool/CROSSTOOL -tensorflow/third_party/toolchains/gpus/py/BUILD -tensorflow/third_party/toolchains/cpus/arm/arm_compiler_configure.bzl +tensorflow/third_party/toolchains/preconfig/win_1803/bazel_018/dummy_toolchain.bzl +tensorflow/third_party/toolchains/preconfig/win_1803/bazel_018/BUILD +tensorflow/third_party/toolchains/preconfig/ubuntu16.04/py3/BUILD +tensorflow/third_party/toolchains/preconfig/ubuntu16.04/clang/dummy_toolchain.bzl +tensorflow/third_party/toolchains/preconfig/ubuntu16.04/clang/BUILD +tensorflow/third_party/toolchains/cpus/py3/BUILD tensorflow/third_party/toolchains/cpus/arm/CROSSTOOL.tpl tensorflow/third_party/toolchains/cpus/arm/BUILD -tensorflow/third_party/toolchains/cpus/py3/BUILD +tensorflow/third_party/toolchains/cpus/arm/arm_compiler_configure.bzl tensorflow/third_party/toolchains/cpus/py/BUILD +tensorflow/third_party/toolchains/gpus/crosstool/CROSSTOOL +tensorflow/third_party/toolchains/gpus/crosstool/BUILD +tensorflow/third_party/toolchains/gpus/cuda/build_defs.bzl +tensorflow/third_party/toolchains/gpus/cuda/cuda/cuda_config.h +tensorflow/third_party/toolchains/gpus/cuda/BUILD +tensorflow/third_party/toolchains/gpus/py/BUILD tensorflow/third_party/toolchains/BUILD -tensorflow/third_party/gpus/BUILD +tensorflow/third_party/nccl/archive.BUILD +tensorflow/third_party/nccl/nccl_configure.bzl +tensorflow/third_party/nccl/system.BUILD.tpl +tensorflow/third_party/nccl/build_defs.bzl.tpl +tensorflow/third_party/nccl/LICENSE +tensorflow/third_party/nccl/BUILD +tensorflow/third_party/farmhash.BUILD +tensorflow/third_party/sycl/crosstool/BUILD +tensorflow/third_party/backports_weakref.BUILD +tensorflow/third_party/gast.BUILD +tensorflow/third_party/clang_toolchain/cc_configure_clang.bzl +tensorflow/third_party/clang_toolchain/download_clang.bzl +tensorflow/third_party/clang_toolchain/BUILD +tensorflow/third_party/mpi_collectives/BUILD +tensorflow/third_party/jsoncpp.BUILD +tensorflow/third_party/mkl_dnn/mkldnn.BUILD +tensorflow/third_party/mkl_dnn/LICENSE +tensorflow/third_party/python_runtime/BUILD +tensorflow/third_party/ngraph/ngraph.BUILD +tensorflow/third_party/ngraph/nlohmann_json.BUILD +tensorflow/third_party/ngraph/build_defs.bzl +tensorflow/third_party/ngraph/ngraph_tf.BUILD +tensorflow/third_party/ngraph/NGRAPH_LICENSE +tensorflow/third_party/ngraph/tbb.BUILD +tensorflow/third_party/ngraph/LICENSE +tensorflow/third_party/ngraph/BUILD +tensorflow/third_party/gpus/crosstool/windows/msvc_wrapper_for_nvcc.py.tpl +tensorflow/third_party/gpus/crosstool/CROSSTOOL.tpl +tensorflow/third_party/gpus/crosstool/BUILD.tpl +tensorflow/third_party/gpus/crosstool/remote.BUILD.tpl tensorflow/third_party/gpus/crosstool/clang/bin/crosstool_wrapper_driver_rocm.tpl tensorflow/third_party/gpus/crosstool/clang/bin/crosstool_wrapper_driver_is_not_gcc.tpl -tensorflow/third_party/gpus/crosstool/CROSSTOOL.tpl tensorflow/third_party/gpus/crosstool/CROSSTOOL_hipcc.tpl tensorflow/third_party/gpus/crosstool/LICENSE -tensorflow/third_party/gpus/crosstool/remote.BUILD.tpl -tensorflow/third_party/gpus/crosstool/windows/msvc_wrapper_for_nvcc.py.tpl -tensorflow/third_party/gpus/crosstool/BUILD.tpl tensorflow/third_party/gpus/crosstool/BUILD -tensorflow/third_party/gpus/cuda/LICENSE +tensorflow/third_party/gpus/rocm/BUILD.tpl +tensorflow/third_party/gpus/rocm/rocm_config.h.tpl +tensorflow/third_party/gpus/rocm/build_defs.bzl.tpl +tensorflow/third_party/gpus/rocm/BUILD tensorflow/third_party/gpus/cuda/BUILD.tpl +tensorflow/third_party/gpus/cuda/build_defs.bzl.tpl +tensorflow/third_party/gpus/cuda/remote.BUILD.tpl tensorflow/third_party/gpus/cuda/BUILD.windows.tpl tensorflow/third_party/gpus/cuda/cuda_config.h.tpl -tensorflow/third_party/gpus/cuda/remote.BUILD.tpl +tensorflow/third_party/gpus/cuda/LICENSE tensorflow/third_party/gpus/cuda/BUILD -tensorflow/third_party/gpus/cuda/build_defs.bzl.tpl -tensorflow/third_party/gpus/rocm/rocm_config.h.tpl -tensorflow/third_party/gpus/rocm/BUILD -tensorflow/third_party/gpus/rocm/BUILD.tpl -tensorflow/third_party/gpus/rocm/build_defs.bzl.tpl tensorflow/third_party/gpus/cuda_configure.bzl tensorflow/third_party/gpus/rocm_configure.bzl -tensorflow/third_party/snappy.BUILD -tensorflow/third_party/cython.BUILD -tensorflow/third_party/farmhash.BUILD -tensorflow/third_party/eigen3/Eigen/Cholesky -tensorflow/third_party/eigen3/Eigen/QR -tensorflow/third_party/eigen3/Eigen/LU +tensorflow/third_party/gpus/BUILD +tensorflow/third_party/py/python_configure.bzl +tensorflow/third_party/py/BUILD.tpl +tensorflow/third_party/py/numpy/BUILD +tensorflow/third_party/py/remote.BUILD.tpl +tensorflow/third_party/py/BUILD +tensorflow/third_party/mkl/build_defs.bzl +tensorflow/third_party/mkl/mkl.BUILD +tensorflow/third_party/mkl/MKL_LICENSE +tensorflow/third_party/mkl/LICENSE +tensorflow/third_party/mkl/BUILD +tensorflow/third_party/com_google_absl.BUILD +tensorflow/third_party/six.BUILD +tensorflow/third_party/lmdb.BUILD +tensorflow/third_party/BUILD +tensorflow/third_party/googleapis.BUILD +tensorflow/third_party/sqlite.BUILD +tensorflow/third_party/termcolor.BUILD +tensorflow/third_party/protobuf/BUILD +tensorflow/third_party/pcre.BUILD +tensorflow/third_party/git/BUILD.tpl +tensorflow/third_party/git/git_configure.bzl +tensorflow/third_party/git/BUILD +tensorflow/third_party/pprof.BUILD +tensorflow/third_party/tflite_smartreply.BUILD tensorflow/third_party/eigen3/Eigen/Core -tensorflow/third_party/eigen3/Eigen/SVD +tensorflow/third_party/eigen3/Eigen/Cholesky tensorflow/third_party/eigen3/Eigen/Eigenvalues -tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/FixedPoint +tensorflow/third_party/eigen3/Eigen/SVD +tensorflow/third_party/eigen3/Eigen/LU +tensorflow/third_party/eigen3/Eigen/QR +tensorflow/third_party/eigen3/unsupported/Eigen/MatrixFunctions tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/Tensor -tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/src/FixedPoint/PacketMathAVX512.h +tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/ThreadPool tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/src/FixedPoint/TypeCastingAVX512.h -tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/src/FixedPoint/PacketMathAVX2.h tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/src/FixedPoint/MatMatProduct.h -tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/src/FixedPoint/FixedPointTypes.h -tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/src/FixedPoint/MatVecProduct.h tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/src/FixedPoint/MatMatProductNEON.h +tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/src/FixedPoint/PacketMathAVX512.h +tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/src/FixedPoint/MatVecProduct.h tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/src/FixedPoint/TypeCastingAVX2.h +tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/src/FixedPoint/FixedPointTypes.h tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/src/FixedPoint/MatMatProductAVX2.h -tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/ThreadPool +tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/src/FixedPoint/PacketMathAVX2.h +tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/FixedPoint tensorflow/third_party/eigen3/unsupported/Eigen/SpecialFunctions -tensorflow/third_party/eigen3/unsupported/Eigen/MatrixFunctions tensorflow/third_party/eigen3/LICENSE tensorflow/third_party/eigen3/BUILD -tensorflow/third_party/systemlibs/build_defs.bzl.tpl -tensorflow/third_party/systemlibs/absl_py.BUILD -tensorflow/third_party/systemlibs/curl.BUILD -tensorflow/third_party/systemlibs/termcolor.BUILD -tensorflow/third_party/systemlibs/absl_py.absl.flags.BUILD -tensorflow/third_party/systemlibs/grpc.BUILD -tensorflow/third_party/systemlibs/swig.BUILD -tensorflow/third_party/systemlibs/protobuf.bzl -tensorflow/third_party/systemlibs/protobuf.BUILD -tensorflow/third_party/systemlibs/BUILD -tensorflow/third_party/systemlibs/google_cloud_cpp.BUILD -tensorflow/third_party/systemlibs/astor.BUILD -tensorflow/third_party/systemlibs/six.BUILD -tensorflow/third_party/systemlibs/absl_py.absl.testing.BUILD -tensorflow/third_party/systemlibs/boringssl.BUILD -tensorflow/third_party/systemlibs/nsync.BUILD -tensorflow/third_party/systemlibs/google_cloud_cpp.google.cloud.bigtable.BUILD -tensorflow/third_party/systemlibs/gif.BUILD -tensorflow/third_party/systemlibs/pcre.BUILD -tensorflow/third_party/systemlibs/BUILD.tpl -tensorflow/third_party/systemlibs/snappy.BUILD -tensorflow/third_party/systemlibs/gast.BUILD -tensorflow/third_party/systemlibs/cython.BUILD -tensorflow/third_party/systemlibs/double_conversion.BUILD -tensorflow/third_party/systemlibs/zlib.BUILD -tensorflow/third_party/systemlibs/jsoncpp.BUILD -tensorflow/third_party/systemlibs/re2.BUILD -tensorflow/third_party/systemlibs/lmdb.BUILD -tensorflow/third_party/systemlibs/googleapis.BUILD -tensorflow/third_party/systemlibs/png.BUILD -tensorflow/third_party/systemlibs/syslibs_configure.bzl -tensorflow/third_party/systemlibs/sqlite.BUILD -tensorflow/third_party/python_runtime/BUILD -tensorflow/third_party/sycl/crosstool/BUILD -tensorflow/third_party/ngraph/LICENSE -tensorflow/third_party/ngraph/tbb.BUILD -tensorflow/third_party/ngraph/BUILD -tensorflow/third_party/ngraph/ngraph.BUILD -tensorflow/third_party/ngraph/build_defs.bzl -tensorflow/third_party/ngraph/NGRAPH_LICENSE -tensorflow/third_party/ngraph/ngraph_tf.BUILD -tensorflow/third_party/ngraph/nlohmann_json.BUILD -tensorflow/third_party/clang_toolchain/download_clang.bzl -tensorflow/third_party/clang_toolchain/BUILD -tensorflow/third_party/clang_toolchain/cc_configure_clang.bzl -tensorflow/third_party/gast.BUILD -tensorflow/third_party/llvm/BUILD -tensorflow/third_party/llvm/expand_cmake_vars.py -tensorflow/third_party/llvm/llvm.autogenerated.BUILD -tensorflow/third_party/llvm/llvm.bzl -tensorflow/third_party/icu/udata.patch -tensorflow/third_party/nccl/archive.BUILD -tensorflow/third_party/nccl/LICENSE -tensorflow/third_party/nccl/system.BUILD.tpl -tensorflow/third_party/nccl/nccl_configure.bzl -tensorflow/third_party/nccl/build_defs.bzl.tpl -tensorflow/third_party/nccl/BUILD -tensorflow/third_party/fft2d/BUILD -tensorflow/third_party/fft2d/fft.h -tensorflow/third_party/fft2d/LICENSE -tensorflow/third_party/fft2d/fft2d.BUILD -tensorflow/third_party/boringssl/BUILD -tensorflow/third_party/mpi/.gitignore -tensorflow/third_party/mpi/BUILD -tensorflow/third_party/tensorrt/LICENSE -tensorflow/third_party/tensorrt/BUILD -tensorflow/third_party/tensorrt/build_defs.bzl.tpl -tensorflow/third_party/tensorrt/BUILD.tpl -tensorflow/third_party/tensorrt/tensorrt_configure.bzl -tensorflow/third_party/tensorrt/remote.BUILD.tpl -tensorflow/third_party/kafka/config.patch -tensorflow/third_party/kafka/BUILD -tensorflow/third_party/android/BUILD -tensorflow/third_party/android/android.bzl.tpl -tensorflow/third_party/android/android_configure.bzl -tensorflow/third_party/android/android_configure.BUILD.tpl -tensorflow/third_party/tflite_smartreply.BUILD -tensorflow/third_party/mkl_dnn/LICENSE -tensorflow/third_party/mkl_dnn/mkldnn.BUILD -tensorflow/third_party/pcre.BUILD -tensorflow/third_party/linenoise.BUILD -tensorflow/third_party/sqlite.BUILD -tensorflow/third_party/common.bzl -tensorflow/third_party/com_google_absl.BUILD -tensorflow/third_party/pprof.BUILD -tensorflow/third_party/BUILD -tensorflow/third_party/tflite_mobilenet_quant.BUILD -tensorflow/third_party/lmdb.BUILD -tensorflow/third_party/git/BUILD.tpl -tensorflow/third_party/git/BUILD -tensorflow/third_party/git/git_configure.bzl -tensorflow/third_party/protobuf/BUILD -tensorflow/third_party/tflite_mobilenet.BUILD -tensorflow/third_party/py/BUILD -tensorflow/third_party/py/BUILD.tpl -tensorflow/third_party/py/remote.BUILD.tpl -tensorflow/third_party/py/numpy/BUILD -tensorflow/third_party/py/python_configure.bzl -tensorflow/third_party/termcolor.BUILD -tensorflow/third_party/png_fix_rpi.patch -tensorflow/third_party/swig.BUILD -tensorflow/third_party/astor.BUILD -tensorflow/third_party/grpc/BUILD -tensorflow/third_party/curl.BUILD -tensorflow/third_party/arm_neon_2_x86_sse.BUILD -tensorflow/third_party/png.BUILD -tensorflow/third_party/googleapis.BUILD -tensorflow/third_party/mpi_collectives/BUILD -tensorflow/third_party/nanopb.BUILD -tensorflow/third_party/gif.BUILD -tensorflow/third_party/double_conversion.BUILD -tensorflow/third_party/six.BUILD -tensorflow/third_party/tflite_mobilenet_float.BUILD -tensorflow/third_party/repo.bzl -tensorflow/third_party/codegen.BUILD -tensorflow/third_party/cub.BUILD -tensorflow/third_party/jsoncpp.BUILD -tensorflow/third_party/tflite_ovic_testdata.BUILD -tensorflow/third_party/libxsmm.BUILD -tensorflow/third_party/zlib.BUILD -tensorflow/third_party/eigen.BUILD -tensorflow/stream_executor/BUILD -tensorflow/api_template_v1.__init__.py -tensorflow/compat_template_v1.__init__.py +tensorflow/tools/pip_package/simple_console.py +tensorflow/tools/pip_package/setup.py +tensorflow/tools/pip_package/MANIFEST.in +tensorflow/tools/pip_package/README +tensorflow/tools/pip_package/check_load_py_test.py +tensorflow/tools/pip_package/pip_smoke_test.py +tensorflow/tools/pip_package/BUILD +tensorflow/tools/pip_package/build_pip_package.sh +tensorflow/tools/pip_package/simple_console_for_windows.py +tensorflow/tools/lib_package/LibTensorFlowTest.java +tensorflow/tools/lib_package/README.md +tensorflow/tools/lib_package/libtensorflow_test.sh +tensorflow/tools/lib_package/libtensorflow_java_test.sh +tensorflow/tools/lib_package/libtensorflow_test.c +tensorflow/tools/lib_package/concat_licenses.sh +tensorflow/tools/lib_package/BUILD +tensorflow/tools/ci_build/remote/BUILD +tensorflow/tools/def_file_filter/BUILD.tpl +tensorflow/tools/def_file_filter/def_file_filter_configure.bzl +tensorflow/tools/def_file_filter/def_file_filter.py.tpl +tensorflow/tools/def_file_filter/BUILD +tensorflow/contrib/mpi/BUILD +tensorflow/contrib/tpu/profiler/pip_package/cloud_tpu_profiler/main.py +tensorflow/contrib/tpu/profiler/pip_package/cloud_tpu_profiler/__init__.py +tensorflow/contrib/tpu/profiler/pip_package/setup.py +tensorflow/contrib/tpu/profiler/pip_package/README +tensorflow/contrib/tpu/profiler/pip_package/BUILD +tensorflow/contrib/tpu/profiler/pip_package/build_pip_package.sh tensorflow/api_template.__init__.py tensorflow/__init__.py \ No newline at end of file diff --git a/tensorflow/tools/git/gen_git_source.py b/tensorflow/tools/git/gen_git_source.py index 645d817d9f..0c37508650 100755 --- a/tensorflow/tools/git/gen_git_source.py +++ b/tensorflow/tools/git/gen_git_source.py @@ -174,8 +174,8 @@ def get_git_version(git_base_path, git_tag_override): # There might be "-" in the tag name. But we can be sure that the final # two "-" are those inserted by the git describe command. abbrev_commit = split_val[-1] - val = bytes( - version_separator.join([git_tag_override, "0", abbrev_commit])) + val = version_separator.join( + [bytes(git_tag_override), b"0", abbrev_commit]) return val if val else unknown_label except (subprocess.CalledProcessError, OSError): return unknown_label diff --git a/tensorflow/tools/optimization/BUILD b/tensorflow/tools/optimization/BUILD new file mode 100644 index 0000000000..aa6c850b0b --- /dev/null +++ b/tensorflow/tools/optimization/BUILD @@ -0,0 +1,52 @@ +# Description: +# Utilities that perform useful transformations on graphs + +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) # Apache 2.0 + +load( + "//tensorflow:tensorflow.bzl", + "tf_cc_binary", + "tf_cuda_library", +) + +exports_files(["LICENSE"]) + +tf_cuda_library( + name = "optimization_pass_runner_lib", + srcs = ["optimization_pass_runner.cc"], + hdrs = ["optimization_pass_runner.h"], + deps = [ + "//tensorflow/contrib:contrib_ops_op_lib", + "//tensorflow/core:core_cpu", + "//tensorflow/core:core_cpu_base", + "//tensorflow/core:framework", + "//tensorflow/core:framework_internal", + "//tensorflow/core:framework_lite", + "//tensorflow/core:lib", + "//tensorflow/core:protos_all_cc", + "//tensorflow/core:tensorflow", + ], +) + +tf_cc_binary( + name = "gpu_optimization_pass_runner", + srcs = ["gpu_optimization_pass_runner_main.cc"], + deps = [ + ":optimization_pass_runner_lib", + "//tensorflow/compiler/jit:xla_cpu_jit", + "//tensorflow/compiler/jit:xla_gpu_jit", + "//tensorflow/compiler/tf2xla:xla_compiler", + "//tensorflow/contrib:contrib_ops_op_lib", + "//tensorflow/core:core_cpu", + "//tensorflow/core:core_cpu_base", + "//tensorflow/core:framework", + "//tensorflow/core:framework_internal", + "//tensorflow/core:framework_lite", + "//tensorflow/core:lib", + "//tensorflow/core:protos_all_cc", + "//tensorflow/core:tensorflow", + "@com_google_absl//absl/strings", + ], +) diff --git a/tensorflow/tools/optimization/gpu_optimization_pass_runner_main.cc b/tensorflow/tools/optimization/gpu_optimization_pass_runner_main.cc new file mode 100644 index 0000000000..0d9f26cd5a --- /dev/null +++ b/tensorflow/tools/optimization/gpu_optimization_pass_runner_main.cc @@ -0,0 +1,60 @@ +/* Copyright 2018 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. +==============================================================================*/ +// This file creates a binary that can run any registered optimization pass. +// ./xla_gpu_opt --input_file_path=/tmp/input.pbtxt +// --output_file_path=/tmp/output.pbtxt +// --optimization_pass=NameOfGraphOptimizationPass + +#include "absl/strings/str_cat.h" +#include "tensorflow/compiler/tf2xla/xla_op_registry.h" +#include "tensorflow/core/framework/types.h" +#include "tensorflow/core/lib/core/status.h" +#include "tensorflow/core/protobuf/config.pb.h" +#include "tensorflow/tools/optimization/optimization_pass_runner.h" + +int main(int argc, char** argv) { + tensorflow::OptimizationPassRunner runner; + // Add fake devices for CPU, GPU, and XLA to ensure we have all devices we + // need. + // Most machines in our servers currently use 8 gpus. There is nothing special + // about this number and it can be decreased or increased to test other + // configurations. + int num_gpus_per_machine = 8; + for (int i = 0; i < num_gpus_per_machine; i++) { + TF_CHECK_OK(runner.AddDevice( + absl::StrCat("/job:localhost/replica:0/task:0/device:CPU:", i), + tensorflow::DEVICE_CPU)); + TF_CHECK_OK(runner.AddDevice( + absl::StrCat("/job:localhost/replica:0/task:0/device:GPU:", i), + tensorflow::DEVICE_GPU)); + TF_CHECK_OK(runner.AddDevice( + absl::StrCat("/job:localhost/replica:0/task:0/device:XLA_CPU:", i), + tensorflow::DEVICE_XLA_CPU)); + TF_CHECK_OK(runner.AddDevice( + absl::StrCat("/job:localhost/replica:0/task:0/device:XLA_GPU:", i), + tensorflow::DEVICE_XLA_GPU)); + TF_CHECK_OK(runner.AddDevice( + absl::StrCat("/job:localhost/replica:0/task:0/device:CPU_XLA_JIT:", i), + tensorflow::DEVICE_CPU_XLA_JIT)); + TF_CHECK_OK(runner.AddDevice( + absl::StrCat("/job:localhost/replica:0/task:0/device:GPU_XLA_JIT:", i), + tensorflow::DEVICE_GPU_XLA_JIT)); + } + // This binary is used to test TF:XLA behavior, so turn on auto_jit. + TF_CHECK_OK(runner.SetJitLevel(tensorflow::OptimizerOptions::GlobalJitLevel:: + OptimizerOptions_GlobalJitLevel_ON_2)); + // Run the actual "main" function. + TF_CHECK_OK(runner.RunMain(argc, argv)); +} diff --git a/tensorflow/tools/optimization/optimization_pass_runner.cc b/tensorflow/tools/optimization/optimization_pass_runner.cc new file mode 100644 index 0000000000..231ff08381 --- /dev/null +++ b/tensorflow/tools/optimization/optimization_pass_runner.cc @@ -0,0 +1,167 @@ +/* Copyright 2018 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. +==============================================================================*/ +// This file creates a library that can run any registered optimization pass. +// The binary that uses this will be run in a form similar to: +// ./optimization_pass_runner --input_file_path=/tmp/input.pbtxt +// --output_file_path=/tmp/output.pbtxt +// --optimization_pass=NameOfGraphOptimizationPass +#include "tensorflow/tools/optimization/optimization_pass_runner.h" + +#include +#include +#include + +#include "tensorflow/core/common_runtime/device.h" +#include "tensorflow/core/common_runtime/device_set.h" +#include "tensorflow/core/common_runtime/optimization_registry.h" +#include "tensorflow/core/framework/allocator.h" +#include "tensorflow/core/framework/device_attributes.pb.h" +#include "tensorflow/core/framework/function.h" +#include "tensorflow/core/framework/function.pb.h" +#include "tensorflow/core/framework/graph.pb.h" +#include "tensorflow/core/framework/op.h" +#include "tensorflow/core/framework/types.h" +#include "tensorflow/core/graph/graph.h" +#include "tensorflow/core/graph/graph_constructor.h" +#include "tensorflow/core/lib/core/errors.h" +#include "tensorflow/core/lib/core/status.h" +#include "tensorflow/core/platform/env.h" +#include "tensorflow/core/platform/init_main.h" +#include "tensorflow/core/protobuf/config.pb.h" +#include "tensorflow/core/public/session_options.h" +#include "tensorflow/core/util/command_line_flags.h" + +namespace tensorflow { + +namespace { +// A fake device used to populate a DeviceSet. +class FakeDevice : public Device { + private: + explicit FakeDevice(const DeviceAttributes& device_attributes) + : Device(nullptr, device_attributes) {} + + public: + Status Sync() override; + static std::unique_ptr Make(const string& name, const string& type); +}; + +Status FakeDevice::Sync() { + return errors::Unimplemented("FakeDevice::Sync()"); +} + +std::unique_ptr FakeDevice::Make(const string& name, + const string& type) { + DeviceAttributes device_attributes; + device_attributes.set_name(name); + device_attributes.set_device_type(DeviceType(type).type()); + return std::unique_ptr(new FakeDevice(device_attributes)); +} +} // namespace + +Status OptimizationPassRunner::RunMain(int argc, char** argv) { + string input_file_path; + string output_file_path; + string optimization_pass; + + const std::vector flag_list = { + Flag("input_file_path", &input_file_path, "Location of the input graph."), + Flag("output_file_path", &output_file_path, + "Location to write the resulting graph."), + // For now only a single optimization pass can be run. + Flag("optimization_pass", &optimization_pass, + "Which optimization pass to run."), + }; + if (!Flags::Parse(&argc, argv, flag_list)) { + return errors::FailedPrecondition("Invalid flags passed"); + } + port::InitMain(argv[0], &argc, &argv); + + if (input_file_path.empty()) { + return errors::FailedPrecondition("input_file_path is a required flag."); + } + if (output_file_path.empty()) { + return errors::FailedPrecondition("output_file_path is a required flag."); + } + if (optimization_pass.empty()) { + return errors::FailedPrecondition("optimization_pass is a required flag."); + } + + // Turn on XLA Auto-Jit. + auto session_options = absl::make_unique(); + session_options->config.mutable_graph_options() + ->mutable_optimizer_options() + ->set_global_jit_level(jit_level_); + FunctionDefLibrary flib; + std::unique_ptr graph = absl::make_unique(OpRegistry::Global()); + + GraphOptimizationPassOptions options; + options.session_options = session_options.release(); + options.graph = &graph; + options.flib_def = + new FunctionLibraryDefinition((*options.graph)->op_registry(), flib); + + // Grab the data + GraphDef graphdef; + GraphConstructorOptions graph_opts; + graph_opts.expect_device_spec = true; + graph_opts.allow_internal_ops = true; + TF_RETURN_IF_ERROR(ReadTextProto(Env::Default(), input_file_path, &graphdef)); + TF_RETURN_IF_ERROR( + ConvertGraphDefToGraph(graph_opts, graphdef, options.graph->get())); + + // Add all devices that were previously configured with AddDevice. + DeviceSet device_set; + for (auto& device : devices_) { + device_set.AddDevice(device.get()); + } + options.device_set = &device_set; + + Status result = errors::NotFound( + "An OptimizationPass was not found with the desired name."); + + // Run the optimization pass specified by the command line flag. + for (const auto& groups_and_passes : + OptimizationPassRegistry::Global()->groups()) { + for (const auto& phase_and_passes : groups_and_passes.second) { + for (const auto& pass : phase_and_passes.second) { + if (pass->name() == optimization_pass) { + result = pass->Run(options); + } + } + } + } + + TF_RETURN_IF_ERROR(result); + + // Write out the result. + options.graph->get()->ToGraphDef(&graphdef); + TF_RETURN_IF_ERROR( + WriteTextProto(Env::Default(), output_file_path, graphdef)); + return Status::OK(); +} + +Status OptimizationPassRunner::SetJitLevel( + OptimizerOptions::GlobalJitLevel jit_level) { + jit_level_ = jit_level; + return Status::OK(); +} + +Status OptimizationPassRunner::AddDevice(const string& name, + const string& type) { + devices_.push_back(FakeDevice::Make(name, type)); + return Status::OK(); +} + +} // namespace tensorflow diff --git a/tensorflow/tools/optimization/optimization_pass_runner.h b/tensorflow/tools/optimization/optimization_pass_runner.h new file mode 100644 index 0000000000..3b26f64bcf --- /dev/null +++ b/tensorflow/tools/optimization/optimization_pass_runner.h @@ -0,0 +1,61 @@ +/* Copyright 2018 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_TOOLS_OPTIMIZATION_OPTIMIZATION_PASS_RUNNER_H_ +#define TENSORFLOW_TOOLS_OPTIMIZATION_OPTIMIZATION_PASS_RUNNER_H_ + +#include +#include +#include + +#include "tensorflow/core/common_runtime/device.h" +#include "tensorflow/core/common_runtime/optimization_registry.h" +#include "tensorflow/core/lib/core/status.h" +#include "tensorflow/core/protobuf/config.pb.h" + +namespace tensorflow { + +// OptimizationPassRunner can be initialized, populated with devices, then run +// to test individual Tensorflow Optimization passes. +class OptimizationPassRunner { + public: + explicit OptimizationPassRunner() + : jit_level_(OptimizerOptions::GlobalJitLevel:: + OptimizerOptions_GlobalJitLevel_DEFAULT) {} + + // Add a fake device to the (initially empty) DeviceSet used for optimization. + // Names are of the form: "/job:localhost/replica:0/task:0/device:CPU:0" + Status AddDevice(const string& name, const string& type); + + // Increasing the Jit level will cause XLA to compile parts of the tensorflow + // graph that it is able to. + Status SetJitLevel(OptimizerOptions::GlobalJitLevel jit_level); + + // This can be called after adding devices and setting the jit level to parse + // command line flags and run the specified job. All 3 flags are required: + // input_file_path, output_file_path, optimization_pass. + // + // If this library becomes heavily used, the caller should be responsible for + // parsing any command line flags desired rather than this Method handling the + // work of a main() function. + Status RunMain(int argc, char** argv); + + private: + OptimizerOptions::GlobalJitLevel jit_level_; + std::vector> devices_; +}; + +} // namespace tensorflow + +#endif // TENSORFLOW_TOOLS_OPTIMIZATION_OPTIMIZATION_PASS_RUNNER_H_ -- GitLab From 044b34b2819ec45ffc34c7b59e921ad4d8aaec9a Mon Sep 17 00:00:00 2001 From: Penporn Koanantakool Date: Mon, 7 Jan 2019 11:13:45 -0800 Subject: [PATCH 413/622] Disable MKL-DNN contraction kernels in Eigen when building with config=mkl to avoid duplicate MKL-DNN symbols. (config=mkl links to OpenMP MKL-DNN while tensorflow_mkldnn_contraction_kernel links to serial MKL-DNN.) PiperOrigin-RevId: 228199406 --- .bazelrc | 2 + tensorflow/opensource_only.files | 416 +++++++++++++++---------------- 2 files changed, 210 insertions(+), 208 deletions(-) diff --git a/.bazelrc b/.bazelrc index cd7e13ddfc..d5432aeb17 100644 --- a/.bazelrc +++ b/.bazelrc @@ -25,12 +25,14 @@ build --define framework_shared_object=true # If you would like to use a local MKL instead of downloading, please set the # environment variable "TF_MKL_ROOT" every time before build. build:mkl --define=build_with_mkl=true --define=enable_mkl=true +build:mkl --define=tensorflow_mkldnn_contraction_kernel=0 build:mkl -c opt # This config option is used to enable MKL-DNN open source library only, # without depending on MKL binary version. build:mkl_open_source_only --define=build_with_mkl_dnn_only=true build:mkl_open_source_only --define=build_with_mkl=true --define=enable_mkl=true +build:mkl_open_source_only --define=tensorflow_mkldnn_contraction_kernel=0 build:download_clang --crosstool_top=@local_config_download_clang//:toolchain build:download_clang --define=using_clang=true diff --git a/tensorflow/opensource_only.files b/tensorflow/opensource_only.files index 40f87d6c36..e00d063cfe 100644 --- a/tensorflow/opensource_only.files +++ b/tensorflow/opensource_only.files @@ -1,248 +1,248 @@ -tensorflow/api_template_v1.__init__.py -tensorflow/compat_template_v1.__init__.py -tensorflow/stream_executor/BUILD -tensorflow/third_party/curl.BUILD -tensorflow/third_party/snappy.BUILD -tensorflow/third_party/tflite_mobilenet_float.BUILD -tensorflow/third_party/libxsmm.BUILD -tensorflow/third_party/cub.BUILD -tensorflow/third_party/repo.bzl -tensorflow/third_party/systemlibs/curl.BUILD -tensorflow/third_party/systemlibs/snappy.BUILD -tensorflow/third_party/systemlibs/protobuf.BUILD -tensorflow/third_party/systemlibs/absl_py.BUILD -tensorflow/third_party/systemlibs/google_cloud_cpp.google.cloud.bigtable.BUILD -tensorflow/third_party/systemlibs/astor.BUILD -tensorflow/third_party/systemlibs/absl_py.absl.flags.BUILD -tensorflow/third_party/systemlibs/png.BUILD -tensorflow/third_party/systemlibs/BUILD.tpl -tensorflow/third_party/systemlibs/boringssl.BUILD -tensorflow/third_party/systemlibs/absl_py.absl.testing.BUILD -tensorflow/third_party/systemlibs/swig.BUILD -tensorflow/third_party/systemlibs/double_conversion.BUILD -tensorflow/third_party/systemlibs/gif.BUILD -tensorflow/third_party/systemlibs/cython.BUILD -tensorflow/third_party/systemlibs/google_cloud_cpp.BUILD -tensorflow/third_party/systemlibs/zlib.BUILD -tensorflow/third_party/systemlibs/grpc.BUILD -tensorflow/third_party/systemlibs/re2.BUILD -tensorflow/third_party/systemlibs/build_defs.bzl.tpl -tensorflow/third_party/systemlibs/protobuf.bzl -tensorflow/third_party/systemlibs/gast.BUILD -tensorflow/third_party/systemlibs/syslibs_configure.bzl -tensorflow/third_party/systemlibs/nsync.BUILD -tensorflow/third_party/systemlibs/jsoncpp.BUILD -tensorflow/third_party/systemlibs/six.BUILD -tensorflow/third_party/systemlibs/lmdb.BUILD -tensorflow/third_party/systemlibs/BUILD -tensorflow/third_party/systemlibs/googleapis.BUILD -tensorflow/third_party/systemlibs/sqlite.BUILD -tensorflow/third_party/systemlibs/termcolor.BUILD -tensorflow/third_party/systemlibs/pcre.BUILD -tensorflow/third_party/tflite_mobilenet_quant.BUILD -tensorflow/third_party/common.bzl -tensorflow/third_party/boringssl/BUILD -tensorflow/third_party/kafka/config.patch -tensorflow/third_party/kafka/BUILD -tensorflow/third_party/android/android_configure.bzl -tensorflow/third_party/android/android_configure.BUILD.tpl -tensorflow/third_party/android/android.bzl.tpl -tensorflow/third_party/android/BUILD -tensorflow/third_party/astor.BUILD -tensorflow/third_party/icu/udata.patch -tensorflow/third_party/tflite_mobilenet.BUILD -tensorflow/third_party/png.BUILD -tensorflow/third_party/mpi/.gitignore -tensorflow/third_party/mpi/BUILD -tensorflow/third_party/nanopb.BUILD -tensorflow/third_party/arm_neon_2_x86_sse.BUILD -tensorflow/third_party/linenoise.BUILD -tensorflow/third_party/llvm/llvm.autogenerated.BUILD -tensorflow/third_party/llvm/llvm.bzl -tensorflow/third_party/llvm/BUILD -tensorflow/third_party/llvm/expand_cmake_vars.py -tensorflow/third_party/swig.BUILD -tensorflow/third_party/png_fix_rpi.patch -tensorflow/third_party/double_conversion.BUILD -tensorflow/third_party/gif.BUILD -tensorflow/third_party/grpc/BUILD -tensorflow/third_party/cython.BUILD -tensorflow/third_party/eigen.BUILD -tensorflow/third_party/codegen.BUILD -tensorflow/third_party/zlib.BUILD -tensorflow/third_party/tensorrt/BUILD.tpl -tensorflow/third_party/tensorrt/tensorrt_configure.bzl -tensorflow/third_party/tensorrt/build_defs.bzl.tpl -tensorflow/third_party/tensorrt/remote.BUILD.tpl -tensorflow/third_party/tensorrt/LICENSE -tensorflow/third_party/tensorrt/BUILD -tensorflow/third_party/tflite_ovic_testdata.BUILD -tensorflow/third_party/fft2d/fft.h -tensorflow/third_party/fft2d/fft2d.BUILD -tensorflow/third_party/fft2d/LICENSE -tensorflow/third_party/fft2d/BUILD +tensorflow/contrib/tpu/profiler/pip_package/BUILD +tensorflow/contrib/tpu/profiler/pip_package/setup.py +tensorflow/contrib/tpu/profiler/pip_package/README +tensorflow/contrib/tpu/profiler/pip_package/build_pip_package.sh +tensorflow/contrib/tpu/profiler/pip_package/cloud_tpu_profiler/main.py +tensorflow/contrib/tpu/profiler/pip_package/cloud_tpu_profiler/__init__.py +tensorflow/contrib/mpi/BUILD +tensorflow/tools/ci_build/remote/BUILD +tensorflow/tools/pip_package/README +tensorflow/tools/pip_package/MANIFEST.in +tensorflow/tools/pip_package/simple_console.py +tensorflow/tools/pip_package/build_pip_package.sh +tensorflow/tools/pip_package/check_load_py_test.py +tensorflow/tools/pip_package/pip_smoke_test.py +tensorflow/tools/pip_package/simple_console_for_windows.py +tensorflow/tools/pip_package/setup.py +tensorflow/tools/pip_package/BUILD +tensorflow/tools/lib_package/concat_licenses.sh +tensorflow/tools/lib_package/libtensorflow_test.c +tensorflow/tools/lib_package/LibTensorFlowTest.java +tensorflow/tools/lib_package/BUILD +tensorflow/tools/lib_package/libtensorflow_test.sh +tensorflow/tools/lib_package/README.md +tensorflow/tools/lib_package/libtensorflow_java_test.sh +tensorflow/tools/def_file_filter/def_file_filter_configure.bzl +tensorflow/tools/def_file_filter/BUILD +tensorflow/tools/def_file_filter/BUILD.tpl +tensorflow/tools/def_file_filter/def_file_filter.py.tpl +tensorflow/third_party/mkl/MKL_LICENSE +tensorflow/third_party/mkl/LICENSE +tensorflow/third_party/mkl/BUILD +tensorflow/third_party/mkl/mkl.BUILD +tensorflow/third_party/mkl/build_defs.bzl +tensorflow/third_party/backports_weakref.BUILD +tensorflow/third_party/toolchains/clang6/BUILD tensorflow/third_party/toolchains/clang6/README.md tensorflow/third_party/toolchains/clang6/repo.bzl -tensorflow/third_party/toolchains/clang6/clang.BUILD tensorflow/third_party/toolchains/clang6/CROSSTOOL.tpl -tensorflow/third_party/toolchains/clang6/BUILD -tensorflow/third_party/toolchains/preconfig/generate/archives.bzl -tensorflow/third_party/toolchains/preconfig/generate/workspace.bzl -tensorflow/third_party/toolchains/preconfig/generate/generate.bzl -tensorflow/third_party/toolchains/preconfig/generate/containers.bzl -tensorflow/third_party/toolchains/preconfig/generate/BUILD -tensorflow/third_party/toolchains/preconfig/ubuntu14.04/tensorrt5/build_defs.bzl +tensorflow/third_party/toolchains/clang6/clang.BUILD tensorflow/third_party/toolchains/preconfig/ubuntu14.04/tensorrt5/BUILD +tensorflow/third_party/toolchains/preconfig/ubuntu14.04/tensorrt5/build_defs.bzl tensorflow/third_party/toolchains/preconfig/ubuntu14.04/py3/BUILD +tensorflow/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/BUILD tensorflow/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc/BUILD -tensorflow/third_party/toolchains/preconfig/ubuntu14.04/cuda9.0-cudnn7/cuda/build_defs.bzl -tensorflow/third_party/toolchains/preconfig/ubuntu14.04/cuda9.0-cudnn7/cuda/BUILD tensorflow/third_party/toolchains/preconfig/ubuntu14.04/cuda10.0-cudnn7/cuda/build_defs.bzl tensorflow/third_party/toolchains/preconfig/ubuntu14.04/cuda10.0-cudnn7/cuda/BUILD -tensorflow/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda9.0/BUILD tensorflow/third_party/toolchains/preconfig/ubuntu14.04/gcc-nvcc-cuda10.0/BUILD +tensorflow/third_party/toolchains/preconfig/ubuntu14.04/cuda9.0-cudnn7/cuda/build_defs.bzl +tensorflow/third_party/toolchains/preconfig/ubuntu14.04/cuda9.0-cudnn7/cuda/BUILD tensorflow/third_party/toolchains/preconfig/ubuntu14.04/nccl2/BUILD +tensorflow/third_party/toolchains/preconfig/generate/workspace.bzl +tensorflow/third_party/toolchains/preconfig/generate/containers.bzl +tensorflow/third_party/toolchains/preconfig/generate/generate.bzl +tensorflow/third_party/toolchains/preconfig/generate/archives.bzl +tensorflow/third_party/toolchains/preconfig/generate/BUILD +tensorflow/third_party/toolchains/preconfig/ubuntu16.04/clang/BUILD +tensorflow/third_party/toolchains/preconfig/ubuntu16.04/clang/dummy_toolchain.bzl +tensorflow/third_party/toolchains/preconfig/ubuntu16.04/py3/BUILD +tensorflow/third_party/toolchains/preconfig/win_1803/bazel_018/BUILD +tensorflow/third_party/toolchains/preconfig/win_1803/bazel_018/dummy_toolchain.bzl tensorflow/third_party/toolchains/preconfig/win_1803/py36/BUILD tensorflow/third_party/toolchains/preconfig/win_1803/BUILD -tensorflow/third_party/toolchains/preconfig/win_1803/bazel_018/dummy_toolchain.bzl -tensorflow/third_party/toolchains/preconfig/win_1803/bazel_018/BUILD -tensorflow/third_party/toolchains/preconfig/ubuntu16.04/py3/BUILD -tensorflow/third_party/toolchains/preconfig/ubuntu16.04/clang/dummy_toolchain.bzl -tensorflow/third_party/toolchains/preconfig/ubuntu16.04/clang/BUILD -tensorflow/third_party/toolchains/cpus/py3/BUILD -tensorflow/third_party/toolchains/cpus/arm/CROSSTOOL.tpl -tensorflow/third_party/toolchains/cpus/arm/BUILD -tensorflow/third_party/toolchains/cpus/arm/arm_compiler_configure.bzl -tensorflow/third_party/toolchains/cpus/py/BUILD -tensorflow/third_party/toolchains/gpus/crosstool/CROSSTOOL -tensorflow/third_party/toolchains/gpus/crosstool/BUILD tensorflow/third_party/toolchains/gpus/cuda/build_defs.bzl -tensorflow/third_party/toolchains/gpus/cuda/cuda/cuda_config.h tensorflow/third_party/toolchains/gpus/cuda/BUILD +tensorflow/third_party/toolchains/gpus/cuda/cuda/cuda_config.h +tensorflow/third_party/toolchains/gpus/crosstool/BUILD +tensorflow/third_party/toolchains/gpus/crosstool/CROSSTOOL tensorflow/third_party/toolchains/gpus/py/BUILD +tensorflow/third_party/toolchains/cpus/arm/arm_compiler_configure.bzl +tensorflow/third_party/toolchains/cpus/arm/CROSSTOOL.tpl +tensorflow/third_party/toolchains/cpus/arm/BUILD +tensorflow/third_party/toolchains/cpus/py3/BUILD +tensorflow/third_party/toolchains/cpus/py/BUILD tensorflow/third_party/toolchains/BUILD -tensorflow/third_party/nccl/archive.BUILD -tensorflow/third_party/nccl/nccl_configure.bzl -tensorflow/third_party/nccl/system.BUILD.tpl -tensorflow/third_party/nccl/build_defs.bzl.tpl -tensorflow/third_party/nccl/LICENSE -tensorflow/third_party/nccl/BUILD -tensorflow/third_party/farmhash.BUILD -tensorflow/third_party/sycl/crosstool/BUILD -tensorflow/third_party/backports_weakref.BUILD -tensorflow/third_party/gast.BUILD -tensorflow/third_party/clang_toolchain/cc_configure_clang.bzl -tensorflow/third_party/clang_toolchain/download_clang.bzl -tensorflow/third_party/clang_toolchain/BUILD -tensorflow/third_party/mpi_collectives/BUILD -tensorflow/third_party/jsoncpp.BUILD -tensorflow/third_party/mkl_dnn/mkldnn.BUILD -tensorflow/third_party/mkl_dnn/LICENSE -tensorflow/third_party/python_runtime/BUILD -tensorflow/third_party/ngraph/ngraph.BUILD -tensorflow/third_party/ngraph/nlohmann_json.BUILD -tensorflow/third_party/ngraph/build_defs.bzl -tensorflow/third_party/ngraph/ngraph_tf.BUILD -tensorflow/third_party/ngraph/NGRAPH_LICENSE -tensorflow/third_party/ngraph/tbb.BUILD -tensorflow/third_party/ngraph/LICENSE -tensorflow/third_party/ngraph/BUILD -tensorflow/third_party/gpus/crosstool/windows/msvc_wrapper_for_nvcc.py.tpl -tensorflow/third_party/gpus/crosstool/CROSSTOOL.tpl -tensorflow/third_party/gpus/crosstool/BUILD.tpl -tensorflow/third_party/gpus/crosstool/remote.BUILD.tpl +tensorflow/third_party/gpus/BUILD tensorflow/third_party/gpus/crosstool/clang/bin/crosstool_wrapper_driver_rocm.tpl tensorflow/third_party/gpus/crosstool/clang/bin/crosstool_wrapper_driver_is_not_gcc.tpl +tensorflow/third_party/gpus/crosstool/CROSSTOOL.tpl tensorflow/third_party/gpus/crosstool/CROSSTOOL_hipcc.tpl tensorflow/third_party/gpus/crosstool/LICENSE +tensorflow/third_party/gpus/crosstool/remote.BUILD.tpl +tensorflow/third_party/gpus/crosstool/windows/msvc_wrapper_for_nvcc.py.tpl +tensorflow/third_party/gpus/crosstool/BUILD.tpl tensorflow/third_party/gpus/crosstool/BUILD -tensorflow/third_party/gpus/rocm/BUILD.tpl -tensorflow/third_party/gpus/rocm/rocm_config.h.tpl -tensorflow/third_party/gpus/rocm/build_defs.bzl.tpl -tensorflow/third_party/gpus/rocm/BUILD +tensorflow/third_party/gpus/cuda/LICENSE tensorflow/third_party/gpus/cuda/BUILD.tpl -tensorflow/third_party/gpus/cuda/build_defs.bzl.tpl -tensorflow/third_party/gpus/cuda/remote.BUILD.tpl tensorflow/third_party/gpus/cuda/BUILD.windows.tpl tensorflow/third_party/gpus/cuda/cuda_config.h.tpl -tensorflow/third_party/gpus/cuda/LICENSE +tensorflow/third_party/gpus/cuda/remote.BUILD.tpl tensorflow/third_party/gpus/cuda/BUILD +tensorflow/third_party/gpus/cuda/build_defs.bzl.tpl +tensorflow/third_party/gpus/rocm/rocm_config.h.tpl +tensorflow/third_party/gpus/rocm/BUILD +tensorflow/third_party/gpus/rocm/BUILD.tpl +tensorflow/third_party/gpus/rocm/build_defs.bzl.tpl tensorflow/third_party/gpus/cuda_configure.bzl tensorflow/third_party/gpus/rocm_configure.bzl -tensorflow/third_party/gpus/BUILD -tensorflow/third_party/py/python_configure.bzl -tensorflow/third_party/py/BUILD.tpl -tensorflow/third_party/py/numpy/BUILD -tensorflow/third_party/py/remote.BUILD.tpl -tensorflow/third_party/py/BUILD -tensorflow/third_party/mkl/build_defs.bzl -tensorflow/third_party/mkl/mkl.BUILD -tensorflow/third_party/mkl/MKL_LICENSE -tensorflow/third_party/mkl/LICENSE -tensorflow/third_party/mkl/BUILD -tensorflow/third_party/com_google_absl.BUILD -tensorflow/third_party/six.BUILD -tensorflow/third_party/lmdb.BUILD -tensorflow/third_party/BUILD -tensorflow/third_party/googleapis.BUILD -tensorflow/third_party/sqlite.BUILD -tensorflow/third_party/termcolor.BUILD -tensorflow/third_party/protobuf/BUILD -tensorflow/third_party/pcre.BUILD -tensorflow/third_party/git/BUILD.tpl -tensorflow/third_party/git/git_configure.bzl -tensorflow/third_party/git/BUILD -tensorflow/third_party/pprof.BUILD -tensorflow/third_party/tflite_smartreply.BUILD -tensorflow/third_party/eigen3/Eigen/Core +tensorflow/third_party/snappy.BUILD +tensorflow/third_party/cython.BUILD +tensorflow/third_party/farmhash.BUILD tensorflow/third_party/eigen3/Eigen/Cholesky -tensorflow/third_party/eigen3/Eigen/Eigenvalues -tensorflow/third_party/eigen3/Eigen/SVD -tensorflow/third_party/eigen3/Eigen/LU tensorflow/third_party/eigen3/Eigen/QR -tensorflow/third_party/eigen3/unsupported/Eigen/MatrixFunctions +tensorflow/third_party/eigen3/Eigen/LU +tensorflow/third_party/eigen3/Eigen/Core +tensorflow/third_party/eigen3/Eigen/SVD +tensorflow/third_party/eigen3/Eigen/Eigenvalues +tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/FixedPoint tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/Tensor -tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/ThreadPool +tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/src/FixedPoint/PacketMathAVX512.h tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/src/FixedPoint/TypeCastingAVX512.h +tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/src/FixedPoint/PacketMathAVX2.h tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/src/FixedPoint/MatMatProduct.h -tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/src/FixedPoint/MatMatProductNEON.h -tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/src/FixedPoint/PacketMathAVX512.h +tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/src/FixedPoint/FixedPointTypes.h tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/src/FixedPoint/MatVecProduct.h +tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/src/FixedPoint/MatMatProductNEON.h tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/src/FixedPoint/TypeCastingAVX2.h -tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/src/FixedPoint/FixedPointTypes.h tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/src/FixedPoint/MatMatProductAVX2.h -tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/src/FixedPoint/PacketMathAVX2.h -tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/FixedPoint +tensorflow/third_party/eigen3/unsupported/Eigen/CXX11/ThreadPool tensorflow/third_party/eigen3/unsupported/Eigen/SpecialFunctions +tensorflow/third_party/eigen3/unsupported/Eigen/MatrixFunctions tensorflow/third_party/eigen3/LICENSE tensorflow/third_party/eigen3/BUILD -tensorflow/tools/pip_package/simple_console.py -tensorflow/tools/pip_package/setup.py -tensorflow/tools/pip_package/MANIFEST.in -tensorflow/tools/pip_package/README -tensorflow/tools/pip_package/check_load_py_test.py -tensorflow/tools/pip_package/pip_smoke_test.py -tensorflow/tools/pip_package/BUILD -tensorflow/tools/pip_package/build_pip_package.sh -tensorflow/tools/pip_package/simple_console_for_windows.py -tensorflow/tools/lib_package/LibTensorFlowTest.java -tensorflow/tools/lib_package/README.md -tensorflow/tools/lib_package/libtensorflow_test.sh -tensorflow/tools/lib_package/libtensorflow_java_test.sh -tensorflow/tools/lib_package/libtensorflow_test.c -tensorflow/tools/lib_package/concat_licenses.sh -tensorflow/tools/lib_package/BUILD -tensorflow/tools/ci_build/remote/BUILD -tensorflow/tools/def_file_filter/BUILD.tpl -tensorflow/tools/def_file_filter/def_file_filter_configure.bzl -tensorflow/tools/def_file_filter/def_file_filter.py.tpl -tensorflow/tools/def_file_filter/BUILD -tensorflow/contrib/mpi/BUILD -tensorflow/contrib/tpu/profiler/pip_package/cloud_tpu_profiler/main.py -tensorflow/contrib/tpu/profiler/pip_package/cloud_tpu_profiler/__init__.py -tensorflow/contrib/tpu/profiler/pip_package/setup.py -tensorflow/contrib/tpu/profiler/pip_package/README -tensorflow/contrib/tpu/profiler/pip_package/BUILD -tensorflow/contrib/tpu/profiler/pip_package/build_pip_package.sh +tensorflow/third_party/systemlibs/build_defs.bzl.tpl +tensorflow/third_party/systemlibs/absl_py.BUILD +tensorflow/third_party/systemlibs/curl.BUILD +tensorflow/third_party/systemlibs/termcolor.BUILD +tensorflow/third_party/systemlibs/absl_py.absl.flags.BUILD +tensorflow/third_party/systemlibs/grpc.BUILD +tensorflow/third_party/systemlibs/swig.BUILD +tensorflow/third_party/systemlibs/protobuf.bzl +tensorflow/third_party/systemlibs/protobuf.BUILD +tensorflow/third_party/systemlibs/BUILD +tensorflow/third_party/systemlibs/google_cloud_cpp.BUILD +tensorflow/third_party/systemlibs/astor.BUILD +tensorflow/third_party/systemlibs/six.BUILD +tensorflow/third_party/systemlibs/absl_py.absl.testing.BUILD +tensorflow/third_party/systemlibs/boringssl.BUILD +tensorflow/third_party/systemlibs/nsync.BUILD +tensorflow/third_party/systemlibs/google_cloud_cpp.google.cloud.bigtable.BUILD +tensorflow/third_party/systemlibs/gif.BUILD +tensorflow/third_party/systemlibs/pcre.BUILD +tensorflow/third_party/systemlibs/BUILD.tpl +tensorflow/third_party/systemlibs/snappy.BUILD +tensorflow/third_party/systemlibs/gast.BUILD +tensorflow/third_party/systemlibs/cython.BUILD +tensorflow/third_party/systemlibs/double_conversion.BUILD +tensorflow/third_party/systemlibs/zlib.BUILD +tensorflow/third_party/systemlibs/jsoncpp.BUILD +tensorflow/third_party/systemlibs/re2.BUILD +tensorflow/third_party/systemlibs/lmdb.BUILD +tensorflow/third_party/systemlibs/googleapis.BUILD +tensorflow/third_party/systemlibs/png.BUILD +tensorflow/third_party/systemlibs/syslibs_configure.bzl +tensorflow/third_party/systemlibs/sqlite.BUILD +tensorflow/third_party/python_runtime/BUILD +tensorflow/third_party/sycl/crosstool/BUILD +tensorflow/third_party/ngraph/LICENSE +tensorflow/third_party/ngraph/tbb.BUILD +tensorflow/third_party/ngraph/BUILD +tensorflow/third_party/ngraph/ngraph.BUILD +tensorflow/third_party/ngraph/build_defs.bzl +tensorflow/third_party/ngraph/NGRAPH_LICENSE +tensorflow/third_party/ngraph/ngraph_tf.BUILD +tensorflow/third_party/ngraph/nlohmann_json.BUILD +tensorflow/third_party/clang_toolchain/download_clang.bzl +tensorflow/third_party/clang_toolchain/BUILD +tensorflow/third_party/clang_toolchain/cc_configure_clang.bzl +tensorflow/third_party/gast.BUILD +tensorflow/third_party/llvm/BUILD +tensorflow/third_party/llvm/expand_cmake_vars.py +tensorflow/third_party/llvm/llvm.autogenerated.BUILD +tensorflow/third_party/llvm/llvm.bzl +tensorflow/third_party/icu/udata.patch +tensorflow/third_party/nccl/archive.BUILD +tensorflow/third_party/nccl/LICENSE +tensorflow/third_party/nccl/system.BUILD.tpl +tensorflow/third_party/nccl/nccl_configure.bzl +tensorflow/third_party/nccl/build_defs.bzl.tpl +tensorflow/third_party/nccl/BUILD +tensorflow/third_party/fft2d/BUILD +tensorflow/third_party/fft2d/fft.h +tensorflow/third_party/fft2d/LICENSE +tensorflow/third_party/fft2d/fft2d.BUILD +tensorflow/third_party/boringssl/BUILD +tensorflow/third_party/mpi/.gitignore +tensorflow/third_party/mpi/BUILD +tensorflow/third_party/tensorrt/LICENSE +tensorflow/third_party/tensorrt/BUILD +tensorflow/third_party/tensorrt/build_defs.bzl.tpl +tensorflow/third_party/tensorrt/BUILD.tpl +tensorflow/third_party/tensorrt/tensorrt_configure.bzl +tensorflow/third_party/tensorrt/remote.BUILD.tpl +tensorflow/third_party/kafka/config.patch +tensorflow/third_party/kafka/BUILD +tensorflow/third_party/android/BUILD +tensorflow/third_party/android/android.bzl.tpl +tensorflow/third_party/android/android_configure.bzl +tensorflow/third_party/android/android_configure.BUILD.tpl +tensorflow/third_party/tflite_smartreply.BUILD +tensorflow/third_party/mkl_dnn/LICENSE +tensorflow/third_party/mkl_dnn/mkldnn.BUILD +tensorflow/third_party/pcre.BUILD +tensorflow/third_party/linenoise.BUILD +tensorflow/third_party/sqlite.BUILD +tensorflow/third_party/common.bzl +tensorflow/third_party/com_google_absl.BUILD +tensorflow/third_party/pprof.BUILD +tensorflow/third_party/BUILD +tensorflow/third_party/tflite_mobilenet_quant.BUILD +tensorflow/third_party/lmdb.BUILD +tensorflow/third_party/git/BUILD.tpl +tensorflow/third_party/git/BUILD +tensorflow/third_party/git/git_configure.bzl +tensorflow/third_party/protobuf/BUILD +tensorflow/third_party/tflite_mobilenet.BUILD +tensorflow/third_party/py/BUILD +tensorflow/third_party/py/BUILD.tpl +tensorflow/third_party/py/remote.BUILD.tpl +tensorflow/third_party/py/numpy/BUILD +tensorflow/third_party/py/python_configure.bzl +tensorflow/third_party/termcolor.BUILD +tensorflow/third_party/png_fix_rpi.patch +tensorflow/third_party/swig.BUILD +tensorflow/third_party/astor.BUILD +tensorflow/third_party/grpc/BUILD +tensorflow/third_party/curl.BUILD +tensorflow/third_party/arm_neon_2_x86_sse.BUILD +tensorflow/third_party/png.BUILD +tensorflow/third_party/googleapis.BUILD +tensorflow/third_party/mpi_collectives/BUILD +tensorflow/third_party/nanopb.BUILD +tensorflow/third_party/gif.BUILD +tensorflow/third_party/double_conversion.BUILD +tensorflow/third_party/six.BUILD +tensorflow/third_party/tflite_mobilenet_float.BUILD +tensorflow/third_party/repo.bzl +tensorflow/third_party/codegen.BUILD +tensorflow/third_party/cub.BUILD +tensorflow/third_party/jsoncpp.BUILD +tensorflow/third_party/tflite_ovic_testdata.BUILD +tensorflow/third_party/libxsmm.BUILD +tensorflow/third_party/zlib.BUILD +tensorflow/third_party/eigen.BUILD +tensorflow/stream_executor/BUILD +tensorflow/api_template_v1.__init__.py +tensorflow/compat_template_v1.__init__.py tensorflow/api_template.__init__.py tensorflow/__init__.py \ No newline at end of file -- GitLab From 68340a644560241ca7098bb2d358a3cb6fb42a4d Mon Sep 17 00:00:00 2001 From: Sourabh Bajaj Date: Mon, 7 Jan 2019 11:20:59 -0800 Subject: [PATCH 414/622] Move the fit-scope check to only error our when using numpy PiperOrigin-RevId: 228200832 --- .../python/keras/engine/training_distributed.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tensorflow/python/keras/engine/training_distributed.py b/tensorflow/python/keras/engine/training_distributed.py index 9f63f7bada..92a46e399a 100644 --- a/tensorflow/python/keras/engine/training_distributed.py +++ b/tensorflow/python/keras/engine/training_distributed.py @@ -53,14 +53,14 @@ def fit_distributed(model, steps_per_epoch=None, validation_steps=None): """Fit loop for Distribution Strategies.""" - # TODO(b/122314600): Remove the scope validate. - distributed_training_utils.validate_not_in_strategy_scope() distributed_training_utils.validate_callbacks(callbacks, model.optimizer) distributed_training_utils.validate_inputs( x, y, model._distribution_strategy) first_x_value = nest.flatten(x)[0] if isinstance(first_x_value, np.ndarray): + # TODO(b/122314600): Remove the scope validate. + distributed_training_utils.validate_not_in_strategy_scope() steps_per_epoch, batch_size = ( distributed_training_utils.get_input_params( model._distribution_strategy, first_x_value, steps_per_epoch, @@ -138,11 +138,11 @@ def evaluate_distributed(model, steps=None, callbacks=None): """Evaluate loop for Distribution Strategies.""" - # TODO(b/122314600): Remove the scope validate. - distributed_training_utils.validate_not_in_strategy_scope() distributed_training_utils.validate_inputs(x, y, model._distribution_strategy) first_x_value = nest.flatten(x)[0] if isinstance(first_x_value, np.ndarray): + # TODO(b/122314600): Remove the scope validate. + distributed_training_utils.validate_not_in_strategy_scope() steps, batch_size = distributed_training_utils.get_input_params( model._distribution_strategy, first_x_value, steps, batch_size) batch_size = model._validate_or_infer_batch_size(batch_size, steps, x) @@ -175,12 +175,12 @@ def predict_distributed(model, steps=None, callbacks=None): """Predict loop for Distribution Strategies.""" - # TODO(b/122314600): Remove the scope validate. - distributed_training_utils.validate_not_in_strategy_scope() distributed_training_utils.validate_inputs( x, None, model._distribution_strategy) first_x_value = nest.flatten(x)[0] if isinstance(first_x_value, np.ndarray): + # TODO(b/122314600): Remove the scope validate. + distributed_training_utils.validate_not_in_strategy_scope() steps, batch_size = distributed_training_utils.get_input_params( model._distribution_strategy, first_x_value, steps, batch_size) batch_size = model._validate_or_infer_batch_size(batch_size, steps, x) -- GitLab From 2c0a8c16477999cc791705112f501d4530649938 Mon Sep 17 00:00:00 2001 From: Karim Nosir Date: Mon, 7 Jan 2019 11:29:07 -0800 Subject: [PATCH 415/622] Add Unique Op implementation PiperOrigin-RevId: 228202673 --- tensorflow/lite/build_def.bzl | 1 + tensorflow/lite/kernels/BUILD | 12 ++ tensorflow/lite/kernels/register.cc | 2 + tensorflow/lite/kernels/unique.cc | 164 ++++++++++++++++++ tensorflow/lite/kernels/unique_test.cc | 103 +++++++++++ tensorflow/lite/testing/generate_examples.py | 49 ++++++ .../propagate_array_data_types.cc | 8 + .../propagate_fixed_sizes.cc | 17 ++ tensorflow/lite/toco/import_tensorflow.cc | 113 ++++++------ tensorflow/lite/toco/model.h | 14 +- tensorflow/lite/toco/tflite/operator.cc | 28 +++ tensorflow/lite/toco/tflite/operator_test.cc | 9 + tensorflow/lite/toco/tooling_util.cc | 1 + 13 files changed, 467 insertions(+), 54 deletions(-) create mode 100644 tensorflow/lite/kernels/unique.cc create mode 100644 tensorflow/lite/kernels/unique_test.cc diff --git a/tensorflow/lite/build_def.bzl b/tensorflow/lite/build_def.bzl index c17eddf47b..343ec60f2a 100644 --- a/tensorflow/lite/build_def.bzl +++ b/tensorflow/lite/build_def.bzl @@ -311,6 +311,7 @@ def generated_test_models(): "topk", "transpose", "transpose_conv", + "unique", "unpack", "unroll_batch_matmul", "where", diff --git a/tensorflow/lite/kernels/BUILD b/tensorflow/lite/kernels/BUILD index 9abc33d297..6809a4a3d0 100644 --- a/tensorflow/lite/kernels/BUILD +++ b/tensorflow/lite/kernels/BUILD @@ -221,6 +221,7 @@ cc_library( "transpose_conv.cc", "unidirectional_sequence_lstm.cc", "unidirectional_sequence_rnn.cc", + "unique.cc", "unpack.cc", "zeros_like.cc", ], @@ -1233,6 +1234,17 @@ tf_cc_test( ], ) +tf_cc_test( + name = "unique_test", + srcs = ["unique_test.cc"], + deps = [ + ":builtin_ops", + ":test_util", + "//tensorflow/lite:framework", + "@com_google_googletest//:gtest_main", + ], +) + filegroup( name = "all_files", srcs = glob( diff --git a/tensorflow/lite/kernels/register.cc b/tensorflow/lite/kernels/register.cc index 47296fa105..f17f39fc2b 100644 --- a/tensorflow/lite/kernels/register.cc +++ b/tensorflow/lite/kernels/register.cc @@ -129,6 +129,7 @@ TfLiteRegistration* Register_LEAKY_RELU(); TfLiteRegistration* Register_SQUARED_DIFFERENCE(); TfLiteRegistration* Register_FILL(); TfLiteRegistration* Register_MIRROR_PAD(); +TfLiteRegistration* Register_UNIQUE(); TfLiteStatus UnsupportedTensorFlowOp(TfLiteContext* context, TfLiteNode* node) { context->ReportError( @@ -284,6 +285,7 @@ BuiltinOpResolver::BuiltinOpResolver() { AddBuiltin(BuiltinOperator_SQUARED_DIFFERENCE, Register_SQUARED_DIFFERENCE()); AddBuiltin(BuiltinOperator_FILL, Register_FILL()); AddBuiltin(BuiltinOperator_MIRROR_PAD, Register_MIRROR_PAD()); + AddBuiltin(BuiltinOperator_UNIQUE, Register_UNIQUE()); // TODO(andrewharp, ahentz): Move these somewhere more appropriate so that // custom ops aren't always included by default. diff --git a/tensorflow/lite/kernels/unique.cc b/tensorflow/lite/kernels/unique.cc new file mode 100644 index 0000000000..80c033aa5c --- /dev/null +++ b/tensorflow/lite/kernels/unique.cc @@ -0,0 +1,164 @@ +/* Copyright 2018 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 + +#include "tensorflow/lite/c/builtin_op_data.h" +#include "tensorflow/lite/c/c_api_internal.h" +#include "tensorflow/lite/kernels/internal/reference/reference_ops.h" +#include "tensorflow/lite/kernels/internal/tensor.h" +#include "tensorflow/lite/kernels/kernel_util.h" + +namespace tflite { +namespace ops { +namespace builtin { +namespace unique { + +void* Init(TfLiteContext* context, const char* buffer, size_t length) { + return nullptr; +} + +void Free(TfLiteContext* context, void* buffer) {} + +TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { + static const int kOutputUniqueTensor = 0; + static const int kOutputIndexTensor = 1; + + TF_LITE_ENSURE_EQ(context, NumInputs(node), 1); + TF_LITE_ENSURE_EQ(context, NumOutputs(node), 2); + const TfLiteTensor* input = GetInput(context, node, 0); + TfLiteTensor* output_unique_tensor = + GetOutput(context, node, kOutputUniqueTensor); + TfLiteTensor* output_index_tensor = + GetOutput(context, node, kOutputIndexTensor); + + // The op only supports 1D input. + TF_LITE_ENSURE_EQ(context, NumDimensions(input), 1); + TfLiteIntArray* output_index_shape = TfLiteIntArrayCopy(input->dims); + // The unique values are determined during evaluation, so we don't know yet + // the size of the output tensor. + SetTensorToDynamic(output_unique_tensor); + return context->ResizeTensor(context, output_index_tensor, + output_index_shape); +} + +namespace { + +// Actual evaluation for the unique op. +template +TfLiteStatus EvalImpl(TfLiteContext* context, const TfLiteTensor* input, + TfLiteNode* node) { + // Map from value, to index in the unique elements vector. + // Note that we prefer to use map than unordered_map as it showed less + // increase in the binary size. + std::map unique_values; + TfLiteTensor* output_indexes = GetOutput(context, node, 1); + I* indexes = GetTensorData(output_indexes); + const T* data = GetTensorData(input); + const int num_elements = NumElements(input); + + for (int i = 0; i < num_elements; ++i) { + const auto element_it = unique_values.find(data[i]); + if (element_it != unique_values.end()) { + indexes[i] = element_it->second; + } else { + const int unique_index = unique_values.size(); + unique_values[data[i]] = unique_index; + indexes[i] = unique_index; + } + } + // Allocate output tensor. + TfLiteTensor* unique_output = GetOutput(context, node, 0); + std::unique_ptr shape( + TfLiteIntArrayCreate(NumDimensions(input)), TfLiteIntArrayFree); + shape->data[0] = unique_values.size(); + TF_LITE_ENSURE_STATUS( + context->ResizeTensor(context, unique_output, shape.release())); + // Set the values in the output tensor. + T* output_unique_values = GetTensorData(unique_output); + for (int i = 0; i < unique_values.size(); ++i) { + output_unique_values[i] = data[indexes[i]]; + } + return kTfLiteOk; +} + +template +TfLiteStatus EvalImpl(TfLiteContext* context, const TfLiteTensor* input, + TfLiteNode* node) { + auto* params = reinterpret_cast(node->builtin_data); + if (params == nullptr) { + context->ReportError(context, "Null params passed"); + return kTfLiteError; + } + switch (params->index_out_type) { + case kTfLiteInt32: + return EvalImpl(context, input, node); + case kTfLiteInt64: + return EvalImpl(context, input, node); + default: + context->ReportError( + context, + "Unique index output array can only be Int32 or In64, requested: ", + TfLiteTypeGetName(params->index_out_type)); + } + return kTfLiteError; +} + +} // namespace + +TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { + const TfLiteTensor* input = GetInput(context, node, 0); + TfLiteTensor* output_index_tensor = GetOutput(context, node, 1); + TF_LITE_ENSURE_EQ(context, NumElements(output_index_tensor), + NumElements(input)); + + switch (input->type) { + case kTfLiteInt8: + TF_LITE_ENSURE_STATUS(EvalImpl(context, input, node)); + break; + case kTfLiteInt16: + TF_LITE_ENSURE_STATUS(EvalImpl(context, input, node)); + break; + case kTfLiteInt32: + TF_LITE_ENSURE_STATUS(EvalImpl(context, input, node)); + break; + case kTfLiteInt64: + TF_LITE_ENSURE_STATUS(EvalImpl(context, input, node)); + break; + case kTfLiteFloat32: + TF_LITE_ENSURE_STATUS(EvalImpl(context, input, node)); + break; + case kTfLiteUInt8: + TF_LITE_ENSURE_STATUS(EvalImpl(context, input, node)); + break; + default: + context->ReportError(context, "Currently Unique doesn't support type: %s", + TfLiteTypeGetName(input->type)); + return kTfLiteError; + } + return kTfLiteOk; +} + +} // namespace unique + +TfLiteRegistration* Register_UNIQUE() { + static TfLiteRegistration r = {unique::Init, unique::Free, unique::Prepare, + unique::Eval}; + return &r; +} + +} // namespace builtin +} // namespace ops +} // namespace tflite diff --git a/tensorflow/lite/kernels/unique_test.cc b/tensorflow/lite/kernels/unique_test.cc new file mode 100644 index 0000000000..1df5e6b796 --- /dev/null +++ b/tensorflow/lite/kernels/unique_test.cc @@ -0,0 +1,103 @@ +/* Copyright 2018 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 +#include "tensorflow/lite/interpreter.h" +#include "tensorflow/lite/kernels/register.h" +#include "tensorflow/lite/kernels/test_util.h" +#include "tensorflow/lite/model.h" + +namespace tflite { +namespace { + +using ::testing::ElementsAreArray; + +template +class UniqueOpModel : public SingleOpModel { + public: + UniqueOpModel(const TensorData& input, TensorType input_type, + TensorType index_out_type) { + input_id_ = AddInput(input); + output_id_ = AddOutput(input_type); + output_index_id_ = AddOutput(index_out_type); + SetBuiltinOp(BuiltinOperator_UNIQUE, BuiltinOptions_UniqueOptions, + CreateUniqueOptions(builder_, index_out_type).Union()); + BuildInterpreter({GetShape(input_id_)}); + } + + int input_tensor_id() { return input_id_; } + + std::vector GetOutput() { return ExtractVector(output_id_); } + std::vector GetIndexesOutput() { + return ExtractVector(output_index_id_); + } + + protected: + int input_id_; + int output_id_; + int output_index_id_; +}; + +TEST(UniqueOpModelTest, OneElement) { + UniqueOpModel model({TensorType_FLOAT32, {1}}, + TensorType_FLOAT32, TensorType_INT32); + model.PopulateTensor(model.input_tensor_id(), {5}); + model.Invoke(); + EXPECT_THAT(model.GetOutput(), ElementsAreArray({5})); + EXPECT_THAT(model.GetIndexesOutput(), ElementsAreArray({0})); +} + +TEST(UniqueOpModelTest, MultipleElements_AllUnique) { + UniqueOpModel model({TensorType_FLOAT32, {8}}, + TensorType_FLOAT32, TensorType_INT32); + model.PopulateTensor(model.input_tensor_id(), + {5, 2, 3, 51, 6, 72, 7, 8}); + model.Invoke(); + EXPECT_THAT(model.GetOutput(), ElementsAreArray({5, 2, 3, 51, 6, 72, 7, 8})); + EXPECT_THAT(model.GetIndexesOutput(), + ElementsAreArray({0, 1, 2, 3, 4, 5, 6, 7})); +} + +TEST(UniqueOpModelTest, MultipleElements_AllDuplicates) { + UniqueOpModel model({TensorType_FLOAT32, {7}}, + TensorType_FLOAT32, TensorType_INT32); + model.PopulateTensor(model.input_tensor_id(), {5, 5, 5, 5, 5, 5, 5}); + model.Invoke(); + EXPECT_THAT(model.GetOutput(), ElementsAreArray({5})); + EXPECT_THAT(model.GetIndexesOutput(), + ElementsAreArray({0, 0, 0, 0, 0, 0, 0})); +} + +TEST(UniqueOpModelTest, MultipleElements_SomeDuplicates) { + UniqueOpModel model({TensorType_FLOAT32, {7}}, + TensorType_FLOAT32, TensorType_INT32); + model.PopulateTensor(model.input_tensor_id(), {2, 3, 5, 7, 2, 7, 3}); + model.Invoke(); + EXPECT_THAT(model.GetOutput(), ElementsAreArray({2, 3, 5, 7})); + EXPECT_THAT(model.GetIndexesOutput(), + ElementsAreArray({0, 1, 2, 3, 0, 3, 1})); +} + +TEST(UniqueOpModelTest, MultipleElements_SomeDuplicates_IndexInt64) { + UniqueOpModel model({TensorType_FLOAT32, {7}}, + TensorType_FLOAT32, TensorType_INT64); + model.PopulateTensor(model.input_tensor_id(), {2, 3, 5, 7, 2, 7, 3}); + model.Invoke(); + EXPECT_THAT(model.GetOutput(), ElementsAreArray({2, 3, 5, 7})); + EXPECT_THAT(model.GetIndexesOutput(), + ElementsAreArray({0, 1, 2, 3, 0, 3, 1})); +} + +} // namespace +} // namespace tflite diff --git a/tensorflow/lite/testing/generate_examples.py b/tensorflow/lite/testing/generate_examples.py index dd7b3d0745..87e7d7eb02 100644 --- a/tensorflow/lite/testing/generate_examples.py +++ b/tensorflow/lite/testing/generate_examples.py @@ -3749,6 +3749,55 @@ def make_placeholder_with_default_tests(zip_path): expected_tf_success=3) +def make_unique_tests(zip_path): + """Make a set of tests for Unique op.""" + + test_parameters = [ + { + "input_shape": [[1]], + "index_type": [tf.int32, tf.int64, None], + "input_values": [3] + }, + { + "input_shape": [[5]], + "index_type": [tf.int32, tf.int64], + "input_values": [[3, 2, 1, 2, 3]] + }, + { + "input_shape": [[7]], + "index_type": [tf.int32, tf.int64], + "input_values": [[1, 1, 1, 1, 1, 1, 1]] + }, + { + "input_shape": [[5]], + "index_type": [tf.int32, tf.int64], + "input_values": [[3, 2, 1, 0, -1]] + }] + + def build_graph(parameters): + """Build the graph for the test case.""" + + input_tensor = tf.placeholder( + dtype=tf.int32, name="input", shape=parameters["input_shape"]) + if parameters["index_type"] is None: + output = tf.unique(input_tensor) + else: + output = tf.unique(input_tensor, parameters["index_type"]) + + return [input_tensor], output + + def build_inputs(parameters, sess, inputs, outputs): + input_values = [create_tensor_data(tf.int32, parameters["input_shape"])] + return input_values, sess.run( + outputs, feed_dict=dict(zip(inputs, input_values))) + + make_zip_of_tests( + zip_path, + test_parameters, + build_graph, + build_inputs, + expected_tf_success=9) + # Toco binary path provided by the generate rule. bin_path = None diff --git a/tensorflow/lite/toco/graph_transformations/propagate_array_data_types.cc b/tensorflow/lite/toco/graph_transformations/propagate_array_data_types.cc index cbae6610d7..6d9aad66b6 100644 --- a/tensorflow/lite/toco/graph_transformations/propagate_array_data_types.cc +++ b/tensorflow/lite/toco/graph_transformations/propagate_array_data_types.cc @@ -252,6 +252,14 @@ void SetDataTypeForAllOutputs(Model* model, Operator* op, SetDataTypeForAllOutputs(model, op, data_type); break; } + case OperatorType::kUnique: { + CHECK_EQ(op->outputs.size(), 2); + const UniqueOperator* unique_op = static_cast(op); + const ArrayDataType data_type = model->GetArray(op->inputs[0]).data_type; + model->GetArray(op->outputs[0]).data_type = data_type; + model->GetArray(op->outputs[1]).data_type = unique_op->idx_out_type; + break; + } default: { // These operators produce outputs with the same type as their 1st input CHECK_GT(op->inputs.size(), 0); diff --git a/tensorflow/lite/toco/graph_transformations/propagate_fixed_sizes.cc b/tensorflow/lite/toco/graph_transformations/propagate_fixed_sizes.cc index 0e653f08a0..5185afd22e 100644 --- a/tensorflow/lite/toco/graph_transformations/propagate_fixed_sizes.cc +++ b/tensorflow/lite/toco/graph_transformations/propagate_fixed_sizes.cc @@ -1828,6 +1828,20 @@ void ProcessMirrorPadOperator(Model* model, MirrorPadOperator* op) { output_array.copy_shape(output_shape); } +void ProcessUniqueOperator(Model* model, UniqueOperator* op) { + const auto& input_array = model->GetArray(op->inputs[0]); + // We have 2 outputs, the shape of the index tensor, is the same size + // as the input array. The unique values tensor, is unknown until runtime. + CHECK_EQ(op->outputs.size(), 2); + auto& idx_output_array = model->GetArray(op->outputs[1]); + + // Yield until input dims have been resolved, or output already computed + if (!input_array.has_shape() || idx_output_array.has_shape()) { + return; + } + idx_output_array.copy_shape(input_array.shape()); +} + } // namespace ::tensorflow::Status PropagateFixedSizes::Run(Model* model, @@ -2103,6 +2117,9 @@ void ProcessMirrorPadOperator(Model* model, MirrorPadOperator* op) { case OperatorType::kMirrorPad: ProcessMirrorPadOperator(model, static_cast(op)); break; + case OperatorType::kUnique: + ProcessUniqueOperator(model, static_cast(op)); + break; default: // Unimplemented, another graph transformation should drop it. LOG(FATAL) << "Unhandled operator type " << OperatorTypeName(op->type); diff --git a/tensorflow/lite/toco/import_tensorflow.cc b/tensorflow/lite/toco/import_tensorflow.cc index e1f7eb82ee..86e04b2393 100644 --- a/tensorflow/lite/toco/import_tensorflow.cc +++ b/tensorflow/lite/toco/import_tensorflow.cc @@ -1190,7 +1190,7 @@ enum FlexSupport { kFlexOk, kFlexNotOk }; // taken from the given NodeDef, and its number must match NumInputs, unless // kAnyNumInputs is passed in. If kFlexOk is passed in the resulting operator // will be eligible for being exported as a flex op. -template +template tensorflow::Status ConvertSimpleOperatorGeneric( const NodeDef& node, const TensorFlowImportFlags& tf_import_flags, Model* model) { @@ -1203,6 +1203,11 @@ tensorflow::Status ConvertSimpleOperatorGeneric( op->inputs.push_back(node.input(i)); } op->outputs.push_back(node.name()); + if (NumOutputs > 1) { + for (int i = 1; i < NumOutputs; ++i) { + op->outputs.push_back(node.name() + ":" + std::to_string(i)); + } + } if (flex == kFlexOk) { RetainTensorFlowNodeDef(node, op); @@ -1213,20 +1218,20 @@ tensorflow::Status ConvertSimpleOperatorGeneric( } // Convert a simple operator which is not valid as a flex op. -template +template tensorflow::Status ConvertSimpleOperator( const NodeDef& node, const TensorFlowImportFlags& tf_import_flags, Model* model) { - return ConvertSimpleOperatorGeneric( + return ConvertSimpleOperatorGeneric( node, tf_import_flags, model); } // Convert a simple operator which is valid as a flex op. -template +template tensorflow::Status ConvertSimpleOperatorFlexOk( const NodeDef& node, const TensorFlowImportFlags& tf_import_flags, Model* model) { - return ConvertSimpleOperatorGeneric( + return ConvertSimpleOperatorGeneric( node, tf_import_flags, model); } @@ -2333,14 +2338,15 @@ ConverterMapType GetTensorFlowNodeConverterMapForFlex() { ConverterMapType GetTensorFlowNodeConverterMap() { return std::unordered_map({ - {"Abs", ConvertSimpleOperator}, - {"Add", ConvertSimpleOperator}, - {"AddN", ConvertSimpleOperatorFlexOk}, - {"All", ConvertSimpleOperator}, + {"Abs", ConvertSimpleOperator}, + {"Add", ConvertSimpleOperator}, + {"AddN", ConvertSimpleOperatorFlexOk}, + {"All", ConvertSimpleOperator}, {"Any", ConvertReduceOperator}, {"ArgMax", ConvertArgMaxOperator}, {"ArgMin", ConvertArgMinOperator}, - {"Assert", ConvertSimpleOperator}, + {"Assert", + ConvertSimpleOperator}, {"AvgPool", ConvertAvgPoolOperator}, {"BatchMatMul", ConvertBatchMatMulOperator}, {"BatchNormWithGlobalNormalization", @@ -2357,98 +2363,99 @@ ConverterMapType GetTensorFlowNodeConverterMap() { {"CTCBeamSearchDecoder", ConvertCTCBeamSearchDecoderOperator}, {"DepthToSpace", ConvertDepthToSpaceOperator}, {"DepthwiseConv2dNative", ConvertDepthwiseConvOperator}, - {"Div", ConvertSimpleOperator}, + {"Div", ConvertSimpleOperator}, {"DynamicPartition", ConvertDynamicPartitionOperator}, {"DynamicStitch", ConvertDynamicStitchOperator}, - {"Equal", ConvertSimpleOperator}, - {"Exp", ConvertSimpleOperator}, - {"ExpandDims", ConvertSimpleOperator}, + {"Equal", ConvertSimpleOperator}, + {"Exp", ConvertSimpleOperator}, + {"ExpandDims", ConvertSimpleOperator}, {"FakeQuantWithMinMaxArgs", ConvertFakeQuantWithMinMaxArgs}, {"FakeQuantWithMinMaxVars", ConvertFakeQuantWithMinMaxVars}, - {"Fill", ConvertSimpleOperator}, + {"Fill", ConvertSimpleOperator}, {"Floor", ConvertFloorOperator}, - {"FloorDiv", ConvertSimpleOperator}, - {"FloorMod", ConvertSimpleOperator}, + {"FloorDiv", ConvertSimpleOperator}, + {"FloorMod", ConvertSimpleOperator}, {"FusedBatchNorm", ConvertFusedBatchNormOperator}, {"Gather", ConvertGatherOperator}, {"GatherV2", ConvertGatherOperator}, - {"Greater", ConvertSimpleOperator}, + {"Greater", ConvertSimpleOperator}, {"GreaterEqual", - ConvertSimpleOperator}, + ConvertSimpleOperator}, {"Identity", ConvertIdentityOperator}, {"LRN", ConvertLRNOperator}, {"LeakyRelu", ConvertLeakyReluOperator}, {"LegacyFedInput", ConvertPlaceholderOperator}, - {"Less", ConvertSimpleOperator}, - {"LessEqual", ConvertSimpleOperator}, - {"Log", ConvertSimpleOperator}, - {"LogicalAnd", ConvertSimpleOperator}, - {"LogicalOr", ConvertSimpleOperator}, - {"LogicalNot", ConvertSimpleOperator}, - {"LogSoftmax", ConvertSimpleOperator}, + {"Less", ConvertSimpleOperator}, + {"LessEqual", ConvertSimpleOperator}, + {"Log", ConvertSimpleOperator}, + {"LogicalAnd", ConvertSimpleOperator}, + {"LogicalOr", ConvertSimpleOperator}, + {"LogicalNot", ConvertSimpleOperator}, + {"LogSoftmax", ConvertSimpleOperator}, {"MatMul", ConvertMatMulOperator}, {"Max", ConvertReduceOperator}, {"MaxPool", ConvertMaxPoolOperator}, - {"Maximum", ConvertSimpleOperator}, + {"Maximum", ConvertSimpleOperator}, {"Mean", ConvertReduceOperator}, - {"Merge", ConvertSimpleOperator}, + {"Merge", ConvertSimpleOperator}, {"Min", ConvertReduceOperator}, - {"Minimum", ConvertSimpleOperator}, - {"Mul", ConvertSimpleOperator}, - {"Neg", ConvertSimpleOperator}, + {"Minimum", ConvertSimpleOperator}, + {"Mul", ConvertSimpleOperator}, + {"Neg", ConvertSimpleOperator}, {"NextIteration", ConvertOperatorSpecialCasedAsRNNBackEdge}, {"NoOp", ConvertNoOpOperator}, - {"NotEqual", ConvertSimpleOperator}, + {"NotEqual", ConvertSimpleOperator}, {"OneHot", ConvertOneHotOperator}, {"Pack", ConvertPackOperator}, - {"Pad", ConvertSimpleOperator}, - {"PadV2", ConvertSimpleOperator}, + {"Pad", ConvertSimpleOperator}, + {"PadV2", ConvertSimpleOperator}, {"ParallelDynamicStitch", ConvertDynamicStitchOperator}, {"Placeholder", ConvertPlaceholderOperator}, {"PlaceholderWithDefault", ConvertIdentityOperator}, - {"Pow", ConvertSimpleOperator}, + {"Pow", ConvertSimpleOperator}, {"Prod", ConvertReduceOperator}, {"RandomUniform", ConvertRandomUniform}, {"Range", ConvertRangeOperator}, - {"Rank", ConvertSimpleOperator}, - {"RealDiv", ConvertSimpleOperator}, - {"Relu", ConvertSimpleOperator}, - {"Relu6", ConvertSimpleOperator}, - {"Reshape", ConvertSimpleOperator}, + {"Rank", ConvertSimpleOperator}, + {"RealDiv", ConvertSimpleOperator}, + {"Relu", ConvertSimpleOperator}, + {"Relu6", ConvertSimpleOperator}, + {"Reshape", ConvertSimpleOperator}, {"ResizeBilinear", ConvertResizeBilinearOperator}, {"ResizeNearestNeighbor", ConvertResizeNearestNeighborOperator}, - {"Rsqrt", ConvertSimpleOperator}, - {"Select", ConvertSimpleOperator}, + {"Rsqrt", ConvertSimpleOperator}, + {"Select", ConvertSimpleOperator}, {"Shape", ConvertShapeOperator}, - {"Sigmoid", ConvertSimpleOperator}, - {"Sin", ConvertSimpleOperator}, - {"Slice", ConvertSimpleOperator}, + {"Sigmoid", ConvertSimpleOperator}, + {"Sin", ConvertSimpleOperator}, + {"Slice", ConvertSimpleOperator}, {"Softmax", ConvertSoftmaxOperator}, {"SpaceToBatchND", ConvertSpaceToBatchNDOperator}, {"SpaceToDepth", ConvertSpaceToDepthOperator}, {"SparseToDense", ConvertSparseToDenseOperator}, {"Split", ConvertSplitOperator}, {"SplitV", ConvertSplitVOperator}, - {"Sqrt", ConvertSimpleOperator}, - {"Square", ConvertSimpleOperator}, + {"Sqrt", ConvertSimpleOperator}, + {"Square", ConvertSimpleOperator}, {"SquaredDifference", - ConvertSimpleOperator}, + ConvertSimpleOperator}, {"Squeeze", ConvertSqueezeOperator}, {"StopGradient", ConvertIdentityOperator}, {"StridedSlice", ConvertStridedSliceOperator}, - {"Sub", ConvertSimpleOperator}, + {"Sub", ConvertSimpleOperator}, {"Sum", ConvertReduceOperator}, {"Svdf", ConvertSvdfOperator}, {"Switch", ConvertSwitchOperator}, - {"Tanh", ConvertSimpleOperator}, - {"Tile", ConvertSimpleOperator}, + {"Tanh", ConvertSimpleOperator}, + {"Tile", ConvertSimpleOperator}, {"TopK", ConvertTopKV2Operator}, {"TopKV2", ConvertTopKV2Operator}, - {"Transpose", ConvertSimpleOperator}, + {"Transpose", ConvertSimpleOperator}, {"Unpack", ConvertUnpackOperator}, - {"ZerosLike", ConvertSimpleOperator}, + {"ZerosLike", ConvertSimpleOperator}, {"UnidirectionalSequenceLstm", ConvertUnidirectionalSequenceLstm}, {"MirrorPad", ConvertMirrorPadOperator}, + {"Unique", ConvertSimpleOperator}, }); } diff --git a/tensorflow/lite/toco/model.h b/tensorflow/lite/toco/model.h index e71d36583e..bfa86c8059 100644 --- a/tensorflow/lite/toco/model.h +++ b/tensorflow/lite/toco/model.h @@ -157,7 +157,8 @@ enum class OperatorType : uint8 { kResizeNearestNeighbor, kLeakyRelu, kAbs, - kMirrorPad + kMirrorPad, + kUnique }; // Helper to deal with TensorFlow arrays using a different ordering of @@ -1953,6 +1954,17 @@ struct MirrorPadOperator : Operator { MirrorPadMode mode; }; +// Unique Operator: +// +// Inputs: +// inputs[0]: required: the input array +// +// TensorFlow equivalent: Unique +struct UniqueOperator : Operator { + UniqueOperator() : Operator(OperatorType::kUnique) {} + ArrayDataType idx_out_type = ArrayDataType::kInt32; +}; + // Alloc's are used for transient arrays only. An Alloc specifies which interval // of the "transient_data" workspace buffer passed to inference functions, is to // be used for the transient array at hand. The 'start' and 'end' values are diff --git a/tensorflow/lite/toco/tflite/operator.cc b/tensorflow/lite/toco/tflite/operator.cc index d3fce6893f..8eb4d321ef 100644 --- a/tensorflow/lite/toco/tflite/operator.cc +++ b/tensorflow/lite/toco/tflite/operator.cc @@ -21,6 +21,7 @@ limitations under the License. #include "tensorflow/core/util/ptr_util.h" // TODO(ycling): Consider refactoring to extract the LSTM definition out of // graph_transformation module. +#include "tensorflow/lite/schema/schema_generated.h" #include "tensorflow/lite/toco/graph_transformations/lstm_utils.h" #include "tensorflow/lite/toco/model.h" #include "tensorflow/lite/toco/tflite/builtin_operator.h" @@ -1478,6 +1479,31 @@ class MirrorPad : MirrorPadMode::kSymmetric; } + int GetVersion(const OperatorSignature& op) const override { return 1; } +}; + +class Unique : public BuiltinOperator { + public: + using BuiltinOperator::BuiltinOperator; + flatbuffers::Offset WriteOptions( + const TocoOperator& op, + flatbuffers::FlatBufferBuilder* builder) const override { + const UniqueOperator& unique_op = static_cast(op); + return ::tflite::CreateUniqueOptions( + *builder, unique_op.idx_out_type == toco::ArrayDataType::kInt64 + ? ::tflite::TensorType::TensorType_INT64 + : ::tflite::TensorType_INT32); + } + void ReadOptions(const TfLiteOptions& options, + TocoOperator* op) const override { + UniqueOperator* unique_op = static_cast(op); + unique_op->idx_out_type = + options.idx_out_type() == ::tflite::TensorType_INT64 + ? toco::ArrayDataType::kInt64 + : toco::ArrayDataType::kInt32; + } + int GetVersion(const OperatorSignature& op_signature) const override { return 1; } @@ -1819,6 +1845,8 @@ std::vector> BuildOperatorList( OperatorType::kSquaredDifference)); ops.push_back(MakeUnique(::tflite::BuiltinOperator_MIRROR_PAD, OperatorType::kMirrorPad)); + ops.push_back(MakeUnique(::tflite::BuiltinOperator_UNIQUE, + OperatorType::kUnique)); // Custom Operators. ops.push_back( diff --git a/tensorflow/lite/toco/tflite/operator_test.cc b/tensorflow/lite/toco/tflite/operator_test.cc index 849eace8cc..215eda82f6 100644 --- a/tensorflow/lite/toco/tflite/operator_test.cc +++ b/tensorflow/lite/toco/tflite/operator_test.cc @@ -629,6 +629,15 @@ TEST_F(OperatorTest, BuiltinMirrorPad) { EXPECT_EQ(op.mode, output_toco_op->mode); } +TEST_F(OperatorTest, BuiltinUnique) { + UniqueOperator op; + op.idx_out_type = ArrayDataType::kInt64; + auto output_toco_op = + SerializeAndDeserialize(GetOperator("UNIQUE", OperatorType::kUnique), op); + ASSERT_NE(nullptr, output_toco_op.get()); + EXPECT_EQ(output_toco_op->idx_out_type, op.idx_out_type); +} + } // namespace } // namespace tflite diff --git a/tensorflow/lite/toco/tooling_util.cc b/tensorflow/lite/toco/tooling_util.cc index af4cd386a2..2396de1a3d 100644 --- a/tensorflow/lite/toco/tooling_util.cc +++ b/tensorflow/lite/toco/tooling_util.cc @@ -416,6 +416,7 @@ const char* OperatorTypeName(OperatorType type) { HANDLE_OPERATORTYPENAME_CASE(LeakyRelu) HANDLE_OPERATORTYPENAME_CASE(SquaredDifference) HANDLE_OPERATORTYPENAME_CASE(MirrorPad) + HANDLE_OPERATORTYPENAME_CASE(Unique) default: LOG(FATAL) << "Unhandled op type"; #undef HANDLE_OPERATORTYPENAME_CASE -- GitLab From b2db5d4089f6cebfabd461401eeac4ed8a976fe1 Mon Sep 17 00:00:00 2001 From: Gaurav Jain Date: Mon, 7 Jan 2019 11:30:46 -0800 Subject: [PATCH 416/622] Fix windows build PiperOrigin-RevId: 228203086 --- tensorflow/core/framework/resource_handle.cc | 3 --- tensorflow/core/framework/resource_handle.h | 3 ++- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tensorflow/core/framework/resource_handle.cc b/tensorflow/core/framework/resource_handle.cc index a3c48d63d4..fc3a329b3b 100644 --- a/tensorflow/core/framework/resource_handle.cc +++ b/tensorflow/core/framework/resource_handle.cc @@ -19,9 +19,6 @@ limitations under the License. namespace tensorflow { -const char ResourceHandle::ANONYMOUS_NAME[] = - "cd2c89b7-88b7-44c8-ad83-06c2a9158347"; - ResourceHandle::ResourceHandle() {} ResourceHandle::ResourceHandle(const ResourceHandleProto& proto) { diff --git a/tensorflow/core/framework/resource_handle.h b/tensorflow/core/framework/resource_handle.h index c3beffb8ac..d1f6771bf3 100644 --- a/tensorflow/core/framework/resource_handle.h +++ b/tensorflow/core/framework/resource_handle.h @@ -69,7 +69,8 @@ class ResourceHandle { // GUID for anonymous resources. Resources with this shared_name will have // their shared_name replaced with a GUID at creation time - static const char ANONYMOUS_NAME[]; + static constexpr const char* ANONYMOUS_NAME = + "cd2c89b7-88b7-44c8-ad83-06c2a9158347"; public: string device_; -- GitLab From 2325650d59511ad1e19e0102cf920b48cca0bfc5 Mon Sep 17 00:00:00 2001 From: Jian Li Date: Mon, 7 Jan 2019 11:31:30 -0800 Subject: [PATCH 417/622] Remove false comments in portable kernel. PiperOrigin-RevId: 228203258 --- .../lite/kernels/internal/reference/portable_tensor_utils.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/tensorflow/lite/kernels/internal/reference/portable_tensor_utils.cc b/tensorflow/lite/kernels/internal/reference/portable_tensor_utils.cc index d692063a96..1acf0caad0 100644 --- a/tensorflow/lite/kernels/internal/reference/portable_tensor_utils.cc +++ b/tensorflow/lite/kernels/internal/reference/portable_tensor_utils.cc @@ -101,7 +101,6 @@ void PortableMatrixBatchVectorMultiplyAccumulate( __builtin_prefetch(row_ptr, 0 /* prefetch for read */, 3 /* temporal locality */); #endif - // For every block of 16 8-bit elements (128-bit register) from each row. for (col = 0; col < m_cols; ++col, ++row_ptr) { dotprod += (*row_ptr) * (vectors[col]); } // for col -- GitLab From e740529d3f09071ef0691a8af24dc566f96cbbec Mon Sep 17 00:00:00 2001 From: Nick Felt Date: Mon, 7 Jan 2019 11:36:12 -0800 Subject: [PATCH 418/622] Ensure tf.linspace(start, stop, num)[-1] == stop The contract of tf.linspace says "A sequence of num evenly-spaced values are generated beginning at start. If num > 1, the values in the sequence increase by stop - start / num - 1, so that the last one is exactly stop." But the existing logic computes the last value as `start + (num - 1) * ((start - stop) / (num - 1))`. This exactly equals `stop` for real numbers, but not for floating point numbers. In particular, for start = 0.0, stop = 1.0, num = 42, this reduces to (1.0/41.0)*41.0, and for IEEE 754 32-bit floats, that resolves to ~0.99999994, not 1.0 (binary values 0x3F7FFFFF vs 0x3F800000). After this change, `tf.linspace(start, stop, num)[-1] == stop` holds for all values assuming num > 1, because we just set it directly to `stop`. This matches what numpy does here: https://github.com/numpy/numpy/blob/v1.15.0/numpy/core/function_base.py#L144-L145 PiperOrigin-RevId: 228204335 --- tensorflow/core/kernels/sequence_ops.cc | 9 ++++---- tensorflow/core/kernels/sequence_ops_test.cc | 21 +++++++++++++++++++ .../python/kernel_tests/init_ops_test.py | 16 ++++++++++++++ 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/tensorflow/core/kernels/sequence_ops.cc b/tensorflow/core/kernels/sequence_ops.cc index 9db0bd4d98..21c3b89f54 100644 --- a/tensorflow/core/kernels/sequence_ops.cc +++ b/tensorflow/core/kernels/sequence_ops.cc @@ -143,11 +143,12 @@ class LinSpaceOp : public OpKernel { OP_REQUIRES_OK(context, context->allocate_output(0, TensorShape({num}), &out)); auto flat = out->flat(); - if (num == 1) { - flat(0) = start; - } else { + flat(0) = start; + if (num > 1) { const T step = (stop - start) / (num - 1); - for (Tnum i = 0; i < num; ++i) flat(i) = start + step * i; + for (Tnum i = 1; i < num - 1; ++i) flat(i) = start + step * i; + // Ensure final value == stop; float arithmetic won't guarantee this. + flat(num - 1) = stop; } } }; diff --git a/tensorflow/core/kernels/sequence_ops_test.cc b/tensorflow/core/kernels/sequence_ops_test.cc index 5f0e0a69a8..2247c44750 100644 --- a/tensorflow/core/kernels/sequence_ops_test.cc +++ b/tensorflow/core/kernels/sequence_ops_test.cc @@ -114,6 +114,27 @@ TEST_F(LinSpaceOpTest, Simple_D32) { test::ExpectTensorEqual(expected, *GetOutput(0)); } +TEST_F(LinSpaceOpTest, Exact_Endpoints) { + MakeOp(DT_FLOAT, DT_INT32); + + // Feed and run. The particular values 0., 1., and 42 are chosen to test that + // the last value is not calculated via an intermediate delta as (1./41)*41, + // because for IEEE 32-bit floats that returns 0.99999994 != 1.0. + AddInputFromArray(TensorShape({}), {0.0}); + AddInputFromArray(TensorShape({}), {1.0}); + AddInputFromArray(TensorShape({}), {42}); + TF_ASSERT_OK(RunOpKernel()); + + // Check the output + Tensor output = *GetOutput(0); + float expected_start = 0.0; + float start = output.flat()(0); + EXPECT_EQ(expected_start, start) << expected_start << " vs. " << start; + float expected_stop = 1.0; + float stop = output.flat()(output.NumElements() - 1); + EXPECT_EQ(expected_stop, stop) << expected_stop << " vs. " << stop; +} + TEST_F(LinSpaceOpTest, Single_D64) { MakeOp(DT_FLOAT, DT_INT64); diff --git a/tensorflow/python/kernel_tests/init_ops_test.py b/tensorflow/python/kernel_tests/init_ops_test.py index 09b9944baa..4b9681afd2 100644 --- a/tensorflow/python/kernel_tests/init_ops_test.py +++ b/tensorflow/python/kernel_tests/init_ops_test.py @@ -592,6 +592,22 @@ class LinSpaceTest(test.TestCase): self.assertArrayNear(self._LinSpace(5., 5., 3), np.array([5.] * 3), 1e-5) self.assertArrayNear(self._LinSpace(5., 5., 4), np.array([5.] * 4), 1e-5) + def testEndpointsAreExact(self): + for self.force_gpu in self._gpu_modes(): + # Test some cases that produce last values not equal to "stop" when + # computed via start + (num - 1) * ((stop - start) / (num - 1)), since + # float arithmetic will introduce error through precision loss. + self.assertAllEqual( + self._LinSpace(0., 1., 42)[[0, -1]], np.array([0., 1.], np.float32)) + self.assertAllEqual( + self._LinSpace(-1., 0., 42)[[0, -1]], np.array([-1., 0.], np.float32)) + self.assertAllEqual( + self._LinSpace(.1, .2, 4)[[0, -1]], np.array([.1, .2], np.float32)) + # Check a case for float64 error too. + self.assertAllEqual( + self._LinSpace(np.array(0., np.float64), .1, 12)[[0, -1]], + np.array([0., .1], np.float64)) + class DeviceTest(test.TestCase): -- GitLab From 5167dd3495de7e857db8a2b578a08db8139c7d29 Mon Sep 17 00:00:00 2001 From: Allen Lavoie Date: Mon, 7 Jan 2019 11:53:00 -0800 Subject: [PATCH 419/622] Remove a remove_checkpoint deprecation warning showing up in a 2.x API Inlines the only useful bits PiperOrigin-RevId: 228207585 --- tensorflow/python/training/checkpoint_management.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tensorflow/python/training/checkpoint_management.py b/tensorflow/python/training/checkpoint_management.py index a7ad1f70e5..21fa6b3b5d 100644 --- a/tensorflow/python/training/checkpoint_management.py +++ b/tensorflow/python/training/checkpoint_management.py @@ -621,7 +621,8 @@ class CheckpointManager(object): >= self._last_preserved_timestamp)): self._last_preserved_timestamp = timestamp continue - remove_checkpoint(filename) + _delete_file_if_exists(filename + ".index") + _delete_file_if_exists(filename + ".data-?????-of-?????") def _record_state(self): """Saves the `CheckpointManager`'s state in `directory`.""" -- GitLab From f75b3374770d75b2f5b412ba4b32b497bec8b6aa Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Mon, 7 Jan 2019 12:05:42 -0800 Subject: [PATCH 420/622] fix spacing PiperOrigin-RevId: 228210397 --- tensorflow/python/grappler/cluster.i | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/python/grappler/cluster.i b/tensorflow/python/grappler/cluster.i index 87795ffcfb..b0c1f71a85 100644 --- a/tensorflow/python/grappler/cluster.i +++ b/tensorflow/python/grappler/cluster.i @@ -132,7 +132,7 @@ struct GCluster { static GCluster TF_NewCluster(bool allow_soft_placement, bool disable_detailed_stats, TF_Status* out_status) { - int num_cpu_cores = tensorflow::grappler::GetNumAvailableLogicalCPUCores(); + int num_cpu_cores = tensorflow::grappler::GetNumAvailableLogicalCPUCores(); int num_gpus = tensorflow::grappler::GetNumAvailableGPUs(); int timeout_s = 60 * 10; tensorflow::grappler::Cluster* cluster_ = -- GitLab From 27822e43c671c7705b2be24891cc40ad628d1c91 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Mon, 7 Jan 2019 12:12:45 -0800 Subject: [PATCH 421/622] fix typos PiperOrigin-RevId: 228211568 --- tensorflow/core/grappler/clusters/virtual_cluster.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tensorflow/core/grappler/clusters/virtual_cluster.cc b/tensorflow/core/grappler/clusters/virtual_cluster.cc index dbd8f26c28..e80d6b54d9 100644 --- a/tensorflow/core/grappler/clusters/virtual_cluster.cc +++ b/tensorflow/core/grappler/clusters/virtual_cluster.cc @@ -67,8 +67,8 @@ Status VirtualCluster::Run(const GraphDef& graph, const std::vector& fetch, RunMetadata* metadata) { // Initialize a virtual scheduler to process the graph. Make sure to use - // static shape inference to prevent the schedulrer from calling the Run - // method on the cluster, and create an infinite loop. + // static shape inference to prevent the scheduler from calling the Run + // method on the cluster and creating an infinite loop. GrapplerItem item; item.graph = graph; item.feed = feed; -- GitLab From 7ba8db889667a757339714253bb70f77bca5b2f0 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Mon, 7 Jan 2019 12:18:06 -0800 Subject: [PATCH 422/622] Add missing include for `std::fegetround()` PiperOrigin-RevId: 228212438 --- tensorflow/core/platform/setround.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tensorflow/core/platform/setround.cc b/tensorflow/core/platform/setround.cc index 592626bfa1..5573b2fc93 100644 --- a/tensorflow/core/platform/setround.cc +++ b/tensorflow/core/platform/setround.cc @@ -15,6 +15,8 @@ limitations under the License. #include "tensorflow/core/platform/setround.h" +#include // NOLINT + namespace tensorflow { namespace port { -- GitLab From 7236c9bb7612fb1a0212dd81f934e0e68fddc0a2 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Mon, 7 Jan 2019 12:18:25 -0800 Subject: [PATCH 423/622] Fix issue with passing EagerTensors to fit when run_eagerly=False PiperOrigin-RevId: 228212493 --- .../python/keras/engine/training_arrays.py | 4 ++ .../keras/engine/training_eager_test.py | 39 +++++++++++++------ .../python/keras/engine/training_utils.py | 19 +++++++++ 3 files changed, 51 insertions(+), 11 deletions(-) diff --git a/tensorflow/python/keras/engine/training_arrays.py b/tensorflow/python/keras/engine/training_arrays.py index 97025a9e18..9887fe34a8 100644 --- a/tensorflow/python/keras/engine/training_arrays.py +++ b/tensorflow/python/keras/engine/training_arrays.py @@ -200,6 +200,10 @@ def model_iteration(model, use_steps = steps_per_epoch is not None do_validation = val_inputs is not None + # Convert Eager Tensors to NumPy arrays to support batching/shuffling. + inputs, targets, sample_weights = training_utils. \ + convert_eager_tensors_to_numpy((inputs, targets, sample_weights)) + # Prepare input data. ins = _prepare_feed_values(model, inputs, targets, sample_weights, mode) num_samples_or_steps = _get_num_samples_or_steps(ins, batch_size, diff --git a/tensorflow/python/keras/engine/training_eager_test.py b/tensorflow/python/keras/engine/training_eager_test.py index 27eaea23ba..a6e2c24ec2 100644 --- a/tensorflow/python/keras/engine/training_eager_test.py +++ b/tensorflow/python/keras/engine/training_eager_test.py @@ -28,13 +28,20 @@ from tensorflow.python.keras import keras_parameterized from tensorflow.python.keras import metrics as metrics_module from tensorflow.python.keras import testing_utils from tensorflow.python.keras.optimizer_v2 import rmsprop +from tensorflow.python.ops import array_ops from tensorflow.python.platform import test class TrainingTest(keras_parameterized.TestCase): @keras_parameterized.run_with_all_model_types(exclude_models='sequential') + @keras_parameterized.run_all_keras_modes def test_model_methods_with_eager_tensors_multi_io(self): + if not context.executing_eagerly(): + # Only test V2 Function and V2 Eager modes, as V1 Graph mode with + # symbolic tensors has different requirements. + return + input_a = keras.layers.Input(shape=(3,), name='input_a') input_b = keras.layers.Input(shape=(3,), name='input_b') @@ -53,13 +60,13 @@ class TrainingTest(keras_parameterized.TestCase): loss, metrics=metrics, loss_weights=loss_weights, - run_eagerly=True, + run_eagerly=testing_utils.should_run_eagerly(), sample_weight_mode=None) - input_a = keras.backend.zeros(shape=(10, 3)) - input_b = keras.backend.zeros(shape=(10, 3)) - target_a = keras.backend.zeros(shape=(10, 4)) - target_b = keras.backend.zeros(shape=(10, 4)) + input_a = array_ops.zeros(shape=(10, 3)) + input_b = array_ops.zeros(shape=(10, 3)) + target_a = array_ops.zeros(shape=(10, 4)) + target_b = array_ops.zeros(shape=(10, 4)) model.fit( [input_a, input_b], [target_a, target_b], @@ -107,16 +114,26 @@ class TrainingTest(keras_parameterized.TestCase): model.test_on_batch([input_a, input_b], [target_a, target_b]) @keras_parameterized.run_with_all_model_types + @keras_parameterized.run_all_keras_modes def test_model_methods_with_eager_tensors_single_io(self): + if not context.executing_eagerly(): + # Only test V2 Function and V2 Eager modes, as V1 Graph mode with + # symbolic tensors has different requirements. + return + model = testing_utils.get_small_mlp(10, 4, 3) optimizer = rmsprop.RMSprop(learning_rate=0.001) loss = 'mse' metrics = ['mae', metrics_module.CategoricalAccuracy()] - model.compile(optimizer, loss, metrics=metrics, run_eagerly=True) + model.compile( + optimizer, + loss, + metrics=metrics, + run_eagerly=testing_utils.should_run_eagerly()) - inputs = keras.backend.zeros(shape=(10, 3)) - targets = keras.backend.zeros(shape=(10, 4)) + inputs = array_ops.zeros(shape=(10, 3)) + targets = array_ops.zeros(shape=(10, 4)) model.fit(inputs, targets, epochs=1, batch_size=2, verbose=0) model.fit(inputs, targets, epochs=1, batch_size=3, verbose=0, shuffle=False) @@ -134,8 +151,8 @@ class TrainingTest(keras_parameterized.TestCase): loss='mse', run_eagerly=True) - x = keras.backend.zeros(shape=(10, 3)) - y = keras.backend.zeros(shape=(10, 4)) + x = array_ops.zeros(shape=(10, 3)) + y = array_ops.zeros(shape=(10, 4)) dataset = dataset_ops.Dataset.from_tensor_slices((x, y)).repeat(10).batch(5) iterator = dataset_ops.make_one_shot_iterator(dataset) validation_dataset = dataset_ops.Dataset.from_tensor_slices( @@ -146,7 +163,7 @@ class TrainingTest(keras_parameterized.TestCase): ValueError, r'specify .* `steps_per_epoch`'): model.fit(iterator, epochs=1, verbose=0) if not context.executing_eagerly(): - # In eager execution, `keras.backend.zeros` returns value tensors + # In eager execution, `array_ops.zeros` returns value tensors # which can be used for validation without a `validation_steps` argument. with self.assertRaisesRegexp( ValueError, r'provide either `batch_size` or `validation_steps`'): diff --git a/tensorflow/python/keras/engine/training_utils.py b/tensorflow/python/keras/engine/training_utils.py index d07e3cc4f7..5edf1de1b0 100644 --- a/tensorflow/python/keras/engine/training_utils.py +++ b/tensorflow/python/keras/engine/training_utils.py @@ -1402,3 +1402,22 @@ def set_run_eagerly_for_dict_structure(model, x): if isinstance(item, dict): model.run_eagerly = True return + + +def convert_eager_tensors_to_numpy(structure): + """Convert every EagerTensor in `structure` to NumPy. + + Arguments: + structure: An arbitrary structure of elements to be converted to NumPy + arrays. + + Returns: + An identical structure with EagerTensors converted to NumPy arrays. + """ + + def _convert(element): + if isinstance(element, ops.EagerTensor): + return element.numpy() + return element + + return nest.map_structure(_convert, structure) -- GitLab From e1a553ba1b10a6f39f01b364d7107d1c77a97776 Mon Sep 17 00:00:00 2001 From: Pavithra Vijay Date: Mon, 7 Jan 2019 12:18:27 -0800 Subject: [PATCH 424/622] Add v2 huber loss. PiperOrigin-RevId: 228212499 --- tensorflow/python/keras/losses.py | 80 ++++++++++++++++++++ tensorflow/python/keras/losses_test.py | 101 +++++++++++++++++++++++++ 2 files changed, 181 insertions(+) diff --git a/tensorflow/python/keras/losses.py b/tensorflow/python/keras/losses.py index de1605a09c..5f9278823c 100644 --- a/tensorflow/python/keras/losses.py +++ b/tensorflow/python/keras/losses.py @@ -608,6 +608,53 @@ class KullbackLeiblerDivergence(Loss): return kullback_leibler_divergence(y_true, y_pred) +class HuberLoss(Loss): + """Computes the huber loss between `y_true` and `y_pred`. + + For each value x in `error=y_true-y_pred`, the following is calculated: + + ``` + 0.5 * x^2 if |x| <= d + 0.5 * d^2 + d * (|x| - d) if |x| > d + ``` + where d is `delta`. See: https://en.wikipedia.org/wiki/Huber_loss + + Usage: + + ```python + l = tf.losses.HuberLoss() + loss = l([0., 1., 1.], [1., 0., 1.]) + print('Loss: ', loss.numpy()) # Loss: 0.333 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile('sgd', loss=tf.losses.HuberLoss()) + ``` + + Args: + delta: A float, the point where the huber loss function changes from a + quadratic to linear. + reduction: Type of `tf.losses.Reduction` to apply to loss. Default value is + `SUM_OVER_BATCH_SIZE`. + name: Optional name for the op. + """ + + def __init__(self, + delta=1.0, + reduction=losses_impl.ReductionV2.SUM_OVER_BATCH_SIZE, + name=None): + super(HuberLoss, self).__init__(reduction=reduction, name=name) + self.delta = delta + + def call(self, y_true, y_pred): + y_pred = ops.convert_to_tensor(y_pred) + y_true = math_ops.cast(y_true, y_pred.dtype) + return huber_loss(y_true, y_pred, delta=self.delta) + + @keras_export('keras.metrics.mean_squared_error', 'keras.metrics.mse', 'keras.metrics.MSE', @@ -677,6 +724,39 @@ def logloss(y_true, y_pred): return K.mean(-losses, axis=-1) +def huber_loss(y_true, y_pred, delta=1.0): + """Computes huber loss value. + + For each value x in `error=y_true-y_pred`, the following is calculated: + + ``` + 0.5 * x^2 if |x| <= d + 0.5 * d^2 + d * (|x| - d) if |x| > d + ``` + where d is `delta`. See: https://en.wikipedia.org/wiki/Huber_loss + + Args: + y_true: tensor of true targets. + y_pred: tensor of predicted targets. + delta: A float, the point where the huber loss function changes from a + quadratic to linear. + + Returns: + Tensor with one scalar loss entry per sample. + """ + y_pred = math_ops.cast(y_pred, dtype=K.floatx()) + y_true = math_ops.cast(y_true, dtype=K.floatx()) + error = math_ops.subtract(y_pred, y_true) + abs_error = math_ops.abs(error) + quadratic = math_ops.minimum(abs_error, delta) + linear = math_ops.subtract(abs_error, quadratic) + return math_ops.add( + math_ops.multiply( + ops.convert_to_tensor(0.5, dtype=quadratic.dtype), + math_ops.multiply(quadratic, quadratic)), + math_ops.multiply(delta, linear)) + + @keras_export('keras.losses.logcosh') def logcosh(y_true, y_pred): """Logarithm of the hyperbolic cosine of the prediction error. diff --git a/tensorflow/python/keras/losses_test.py b/tensorflow/python/keras/losses_test.py index caa4ff4c2e..5d6cd1be55 100644 --- a/tensorflow/python/keras/losses_test.py +++ b/tensorflow/python/keras/losses_test.py @@ -1336,5 +1336,106 @@ class KullbackLeiblerDivergenceTest(test.TestCase): self.assertAlmostEqual(self.evaluate(loss), 0., 3) +@test_util.run_all_in_graph_and_eager_modes +class HuberLossTest(test.TestCase): + + def huber_loss(self, y_true, y_pred, delta=1.0): + error = y_pred - y_true + abs_error = np.abs(error) + + quadratic = np.minimum(abs_error, delta) + linear = np.subtract(abs_error, quadratic) + return np.add( + np.multiply(0.5, np.multiply(quadratic, quadratic)), + np.multiply(delta, linear)) + + def setup(self, delta=1.0): + self.np_y_pred = np.asarray([.9, .2, .2, .8, .4, .6]).reshape((2, 3)) + self.np_y_true = np.asarray([1., 0., 1., 1., 0., 0.]).reshape((2, 3)) + + self.batch_size = 6 + self.expected_losses = self.huber_loss(self.np_y_true, self.np_y_pred, + delta) + + self.y_pred = constant_op.constant(self.np_y_pred) + self.y_true = constant_op.constant(self.np_y_true) + + def test_config(self): + h_obj = keras.losses.HuberLoss( + reduction=losses_impl.ReductionV2.SUM, name='huber') + self.assertEqual(h_obj.name, 'huber') + self.assertEqual(h_obj.reduction, losses_impl.ReductionV2.SUM) + + def test_all_correct(self): + self.setup() + h_obj = keras.losses.HuberLoss() + loss = h_obj(self.y_true, self.y_true) + self.assertAlmostEqual(self.evaluate(loss), 0.0, 3) + + def test_unweighted(self): + self.setup() + h_obj = keras.losses.HuberLoss() + loss = h_obj(self.y_true, self.y_pred) + actual_loss = np.sum(self.expected_losses) / self.batch_size + self.assertAlmostEqual(self.evaluate(loss), actual_loss, 3) + + def test_scalar_weighted(self): + self.setup() + h_obj = keras.losses.HuberLoss() + sample_weight = 2.3 + loss = h_obj(self.y_true, self.y_pred, sample_weight=sample_weight) + actual_loss = sample_weight * np.sum(self.expected_losses) / self.batch_size + self.assertAlmostEqual(self.evaluate(loss), actual_loss, 3) + + # Verify we get the same output when the same input is given + loss_2 = h_obj(self.y_true, self.y_pred, sample_weight=sample_weight) + self.assertAlmostEqual(self.evaluate(loss), self.evaluate(loss_2), 3) + + def test_sample_weighted(self): + self.setup() + h_obj = keras.losses.HuberLoss() + sample_weight = constant_op.constant((1.2, 3.4), shape=(2, 1)) + + loss = h_obj(self.y_true, self.y_pred, sample_weight=sample_weight) + actual_loss = np.multiply( + self.expected_losses, + np.asarray([1.2, 1.2, 1.2, 3.4, 3.4, 3.4]).reshape((2, 3))) + actual_loss = np.sum(actual_loss) / self.batch_size + self.assertAlmostEqual(self.evaluate(loss), actual_loss, 3) + + def test_timestep_weighted(self): + self.setup() + h_obj = keras.losses.HuberLoss() + y_pred = self.np_y_pred.reshape((2, 3, 1)) + y_true = self.np_y_true.reshape((2, 3, 1)) + expected_losses = self.huber_loss(y_true, y_pred) + + y_pred = constant_op.constant(y_pred) + y_true = constant_op.constant(y_true) + sample_weight = np.array([3, 6, 5, 0, 4, 2]).reshape((2, 3, 1)) + loss = h_obj( + y_true, + y_pred, + sample_weight=constant_op.constant(sample_weight, shape=(2, 3))) + actual_loss = np.multiply(expected_losses, sample_weight) + actual_loss = np.sum(actual_loss) / self.batch_size + self.assertAlmostEqual(self.evaluate(loss), actual_loss, 3) + + def test_zero_weighted(self): + self.setup() + h_obj = keras.losses.HuberLoss() + sample_weight = 0 + loss = h_obj(self.y_true, self.y_pred, sample_weight=sample_weight) + self.assertAlmostEqual(self.evaluate(loss), 0., 3) + + def test_non_default_delta(self): + self.setup(delta=0.8) + h_obj = keras.losses.HuberLoss(delta=0.8) + sample_weight = 2.3 + loss = h_obj(self.y_true, self.y_pred, sample_weight=sample_weight) + actual_loss = sample_weight * np.sum(self.expected_losses) / self.batch_size + self.assertAlmostEqual(self.evaluate(loss), actual_loss, 3) + + if __name__ == '__main__': test.main() -- GitLab From 7ba04bba51ac7500afd5fe0d8e2f883082cdd5fb Mon Sep 17 00:00:00 2001 From: Bixia Zheng Date: Mon, 7 Jan 2019 12:22:34 -0800 Subject: [PATCH 425/622] [XLA:GPU] Enhance column reduction implementation. With KernelMappingScheme, a thread processes n elements, where n=tile_size_x/num_thread_x. Previously, we only support n>1 for transpose and row reduction. This change extends the kernel mapping scheme and code generation to allow each thread to compute n partial results for column reduction as follows: .Add dilated_x to KernelMappingScheme, to indicate whether the multiple elements processed by the same thread are contiguous or dilated. Dilated_x=true is what we currently use for transpose while dilated_x=false is used to support the vectorization of column reduction. .Extend the stack storages that store the partial result address and current output linear index address for each output tensor from a scalar to an array of n elements. .Add curr_iter_index_x to kernelCodegenInfo, to indicate which output element that the compiler is currently generating code for. This information is used to locate the partial result address and output linear index address for the element. .Modify the code generation to use n=2 for column reduction. PiperOrigin-RevId: 228213135 --- .../xla/service/gpu/ir_emitter_unnested.cc | 330 +++++++++++------- .../xla/service/gpu/ir_emitter_unnested.h | 20 +- .../compiler/xla/service/llvm_ir/ir_array.h | 2 + .../xla/service/llvm_ir/kernel_tiling.cc | 3 +- .../xla/service/llvm_ir/kernel_tiling.h | 22 +- 5 files changed, 248 insertions(+), 129 deletions(-) diff --git a/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.cc b/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.cc index f190af921e..2bc4912155 100644 --- a/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.cc +++ b/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.cc @@ -88,6 +88,9 @@ namespace xla { namespace gpu { using llvm_ir::KernelMappingScheme; +using EmitElementFunction = + std::function; namespace { @@ -2133,53 +2136,86 @@ int IrEmitterUnnested::ConstructInputReducedShapeAndCastInputIrArrayToShape( namespace { -void EmitFullElementalTile( - const KernelMappingScheme* mapping_scheme, - const IrArray::Index& tile_origin_index, const string& loop_name, - KernelSupportLibrary* ksl, llvm::IRBuilder<>* builder, llvm::Value* y, - llvm::Value* x, llvm::Type* index_ty, - const std::function& emit_elem_function) { +std::tuple GetStartOffsetAndStepForX( + int64 tile_size_x, int64 num_threads_x, + const KernelMappingScheme* mapping_scheme, llvm::IRBuilder<>* builder, + llvm::Value* x, llvm::Type* index_ty) { + llvm::Value* start_offset_x; + int64 step_x; + if (mapping_scheme->DilatedX()) { + start_offset_x = x; + step_x = num_threads_x; + } else { + start_offset_x = builder->CreateMul( + x, llvm::ConstantInt::get(index_ty, tile_size_x / num_threads_x)); + step_x = 1; + } + return std::make_tuple(start_offset_x, step_x); +} + +void EmitFullElementalTile(const KernelMappingScheme* mapping_scheme, + const IrArray::Index& tile_origin_index, + const string& loop_name, KernelSupportLibrary* ksl, + llvm::IRBuilder<>* builder, llvm::Value* y, + llvm::Value* x, llvm::Type* index_ty, + const EmitElementFunction& emit_elem_function) { int64 num_threads_x = mapping_scheme->GetNumberOfThreadsForDimensionX(); int64 num_threads_y = mapping_scheme->GetNumberOfThreadsForDimensionY(); int64 tile_size_x = mapping_scheme->GetTileSizeForDimensionX(); int64 tile_size_y = mapping_scheme->GetTileSizeForDimensionY(); + + llvm::Value* start_offset_x; + int64 step_x; + std::tie(start_offset_x, step_x) = GetStartOffsetAndStepForX( + tile_size_x, num_threads_x, mapping_scheme, builder, x, index_ty); + IrArray::Index source_idx = + tile_origin_index.AddOffsetToDim(y, KernelMappingScheme::DimY, builder) + .AddOffsetToDim(start_offset_x, KernelMappingScheme::DimX, builder); ksl->For(loop_name + "_y", /*start=*/llvm::ConstantInt::get(index_ty, 0), /*end=*/llvm::ConstantInt::get(index_ty, tile_size_y), /*step=*/llvm::ConstantInt::get(index_ty, num_threads_y), [&](llvm::Value* y_indvar) { - IrArray::Index source_idx_y = tile_origin_index.AddOffsetToDim( + IrArray::Index source_idx_y = source_idx.AddOffsetToDim( y_indvar, KernelMappingScheme::DimY, builder); llvm::Value* y_loc = builder->CreateAdd(y_indvar, y); - for (int64 j = 0; j < tile_size_x; j += num_threads_x) { - IrArray::Index source_idx = source_idx_y.AddOffsetToDim( - llvm::ConstantInt::get(index_ty, j), + + for (int64 j = 0; j < tile_size_x / num_threads_x; j++) { + IrArray::Index source_idx_y_x = source_idx_y.AddOffsetToDim( + llvm::ConstantInt::get(index_ty, j * step_x), KernelMappingScheme::DimX, builder); - llvm::Value* x_loc = - builder->CreateAdd(llvm::ConstantInt::get(index_ty, j), x); - emit_elem_function(source_idx, y_loc, x_loc); + llvm::Value* x_loc = builder->CreateAdd( + llvm::ConstantInt::get(index_ty, j * step_x), + start_offset_x); + emit_elem_function(source_idx_y_x, y_loc, x_loc, j); } }); } -void EmitPartialElementalTile( - const KernelMappingScheme* mapping_scheme, - const IrArray::Index& tile_origin_index, const string& loop_name, - KernelSupportLibrary* ksl, llvm::IRBuilder<>* builder, llvm::Value* y, - llvm::Value* x, llvm::Value* tile_height, llvm::Value* tile_width, - llvm::Type* index_ty, - const std::function& emit_elem_function) { +void EmitPartialElementalTile(const KernelMappingScheme* mapping_scheme, + const IrArray::Index& tile_origin_index, + const string& loop_name, + KernelSupportLibrary* ksl, + llvm::IRBuilder<>* builder, llvm::Value* y, + llvm::Value* x, llvm::Value* tile_height, + llvm::Value* tile_width, llvm::Type* index_ty, + const EmitElementFunction& emit_elem_function) { int64 num_threads_x = mapping_scheme->GetNumberOfThreadsForDimensionX(); int64 num_threads_y = mapping_scheme->GetNumberOfThreadsForDimensionY(); int64 tile_size_x = mapping_scheme->GetTileSizeForDimensionX(); - for (int64 j = 0; j < tile_size_x; j += num_threads_x) { - IrArray::Index source_idx = - tile_origin_index.AddOffsetToDim(llvm::ConstantInt::get(index_ty, j), - KernelMappingScheme::DimX, builder); - llvm::Value* x_loc = - builder->CreateAdd(llvm::ConstantInt::get(index_ty, j), x); + llvm::Value* start_offset_x; + int64 step_x; + std::tie(start_offset_x, step_x) = GetStartOffsetAndStepForX( + tile_size_x, num_threads_x, mapping_scheme, builder, x, index_ty); + IrArray::Index source_idx = + tile_origin_index.AddOffsetToDim(y, KernelMappingScheme::DimY, builder) + .AddOffsetToDim(start_offset_x, KernelMappingScheme::DimX, builder); + for (int64 j = 0; j < tile_size_x / num_threads_x; j++) { + IrArray::Index source_idx_x = + source_idx.AddOffsetToDim(llvm::ConstantInt::get(index_ty, j * step_x), + KernelMappingScheme::DimX, builder); + llvm::Value* x_loc = builder->CreateAdd( + llvm::ConstantInt::get(index_ty, j * step_x), start_offset_x); ksl->If( loop_name + "_x_in_tile", builder->CreateICmpULT(x_loc, tile_width), @@ -2199,14 +2235,13 @@ void EmitPartialElementalTile( /*step=*/llvm::ConstantInt::get(index_ty, num_threads_y), [&](llvm::Value* y_indvar) { llvm::Value* y_loc = builder->CreateAdd(y_indvar, y); - ksl->If( - loop_name + "_y_in_tile", - builder->CreateICmpULT(y_loc, tile_height), [&] { - emit_elem_function( - source_idx.AddOffsetToDim( - y_indvar, KernelMappingScheme::DimY, builder), - y_loc, x_loc); - }); + ksl->If(loop_name + "_y_in_tile", + builder->CreateICmpULT(y_loc, tile_height), [&] { + emit_elem_function( + source_idx_x.AddOffsetToDim( + y_indvar, KernelMappingScheme::DimY, builder), + y_loc, x_loc, j); + }); }); }); } @@ -2225,8 +2260,7 @@ void EmitTiledElementalCodeWithBoundsCheck( const IrArray::Index& tile_origin_index, const string& loop_name, KernelSupportLibrary* ksl, llvm::IRBuilder<>* builder, llvm::Value* y, llvm::Value* x, llvm::Value* tile_height, llvm::Value* tile_width, - const std::function& emit_elem_function) { + const EmitElementFunction& emit_elem_function) { int64 tile_size_x = mapping_scheme->GetTileSizeForDimensionX(); int64 tile_size_y = mapping_scheme->GetTileSizeForDimensionY(); llvm::Type* index_ty = tile_width->getType(); @@ -2262,7 +2296,7 @@ void EmitTiledElementalCodeWithBoundsCheck( void IrEmitterUnnested::EmitTileElementForCopy( HloInstruction* hlo, const llvm_ir::IrArray::Index& index, const KernelCodegenInfo* kernel_info, llvm::Value* y_loc, - llvm::Value* x_loc) { + llvm::Value* x_loc, int64 /*x_iter_num*/) { llvm_ir::TiledParameterInfo* tiled_param_info = kernel_info->GetTiledParameterInfo(); // TODO(jlebar): Add AA metadata to this load. @@ -2292,7 +2326,7 @@ void IrEmitterUnnested::EmitTileElementForCopy( void IrEmitterUnnested::EmitTileElementForFusion( HloInstruction* hlo, const llvm_ir::IrArray::Index& index, const KernelCodegenInfo* kernel_info, llvm::Value* y_loc, - llvm::Value* x_loc) { + llvm::Value* x_loc, int64 /*x_iter_num*/) { llvm_ir::TiledParameterInfo* tiled_param_info = kernel_info->GetTiledParameterInfo(); std::vector output_arrays = ConstructIrArrayForOutputs(*hlo); @@ -2393,6 +2427,23 @@ class ReductionCodegenInfo : public IrEmitterUnnested::KernelCodegenInfo { : llvm_ir::KernelMappingScheme::DimX; } + int GetNumberOfPartialResults() const { + if (IsRowReduction()) { + return 1; + } + int64 num_thread = mapping_scheme_->GetNumberOfThreadsForDimensionX(); + int64 tile_size = mapping_scheme_->GetTileSizeForDimensionX(); + CHECK_EQ(tile_size % num_thread, 0); + return tile_size / num_thread; + } + + int GetPartialResultIndex(int64 x_iter_num) const { + if (IsRowReduction()) { + return 0; + } + return x_iter_num; + } + private: AddressVector partial_result_addresses_; AddressVector reduction_input_addresses_; @@ -2452,10 +2503,11 @@ void IrEmitterUnnested::EmitPrologueForOneReduction( llvm::AllocaInst* reduction_input_address = Alloca(element_type); reduction_input_addresses->push_back(reduction_input_address); + int num_partial_results = reduction_info->GetNumberOfPartialResults(); AddressVector* partial_result_addresses = reduction_info->GetMutablePartialResultAddresses(); llvm::AllocaInst* partial_result_address = - Alloca(element_type, /*ArraySize=*/nullptr, + Alloca(element_type, /*ArraySize=*/b_.getInt32(num_partial_results), "partial_reduction_result." + llvm::Twine(reduce_idx)); partial_result_addresses->push_back(partial_result_address); @@ -2478,7 +2530,9 @@ void IrEmitterUnnested::EmitPrologueForOneReduction( .EmitReadArrayElement(IrArray::Index(b_.getInt32Ty()), &b_); } - Store(init_ir_value, partial_result_address); + for (int i = 0; i < num_partial_results; ++i) { + Store(init_ir_value, InBoundsGEP(partial_result_address, {b_.getInt32(i)})); + } } void IrEmitterUnnested::EmitPrologueForReduction( @@ -2516,10 +2570,14 @@ void IrEmitterUnnested::EmitPrologueForReduction( std::move(output_shape_index)); } - // Allocate stack storage to store the current output linear index and record - // the address of the storage. + int num_partial_results = reduction_info->GetNumberOfPartialResults(); + + // Allocate stack storage to store the linear indices for the current output, + // and record the address of the storage. reduction_info->SetCurrentOutputLinearIndexAddress( - Alloca(reduction_info->GetIndexType())); + Alloca(reduction_info->GetIndexType(), + /*ArraySize=*/b_.getInt32(num_partial_results), + "current_output_linear_index_address")); if (!reduction_info->IsRowReduction()) { llvm::Type* bool_ty = b_.getInt1Ty(); @@ -2589,36 +2647,45 @@ void IrEmitterUnnested::EmitEpilogueForReduction( llvm_ir::SetToFirstInsertPoint(if_output_inbound_data.true_block, &b_); } + int num_partial_results = reduction_info->GetNumberOfPartialResults(); + // Emit an atomic operation that accumulates the partial reduction to the // output element. For row reduction, this is only for lane 0 due to the // if-statement emitted above. for (int i = 0; i != num_reduces; ++i) { - IrArray::Index element_index( - /*linear=*/Load(reduction_info->GetCurrentOutputLinearIndexAddress(), - "output_linear_addr"), - ShapeUtil::GetSubshape(unnested_hlo->shape(), - reduction_output_shape_indices[i]), - &b_); - llvm::Value* output_address = - GetIrArray(*unnested_hlo, *unnested_hlo, - reduction_output_shape_indices[i]) - .EmitArrayElementAddress(element_index, &b_, - "output_element_address"); - // Do not emit atomic operations if each element in the reduction result is - // computed by one block, that is the dimension being reduced has only one - // block. - const llvm_ir::KernelMappingScheme* mapping_scheme = - reduction_info->GetKernelMappingScheme(); - if (mapping_scheme->GetTileBlockSizeForDimension( - llvm_ir::KernelMappingScheme::DimZ) == 1 && - mapping_scheme->GetTileBlockSizeForDimension( - reduction_info->GetReducedDimensionEnum()) == 1) { - TF_CHECK_OK(EmitCallToNestedComputation( - *reducers[i], {output_address, partial_result_addresses[i]}, - output_address)); - } else { - TF_CHECK_OK(EmitAtomicOperationForNestedComputation( - *reducers[i], output_address, partial_result_addresses[i])); + for (int j = 0; j < num_partial_results; ++j) { + IrArray::Index element_index( + /*linear=*/Load( + InBoundsGEP(reduction_info->GetCurrentOutputLinearIndexAddress(), + {b_.getInt32(j)}), + "output_linear_addr"), + ShapeUtil::GetSubshape(unnested_hlo->shape(), + reduction_output_shape_indices[i]), + &b_); + llvm::Value* output_address = + GetIrArray(*unnested_hlo, *unnested_hlo, + reduction_output_shape_indices[i]) + .EmitArrayElementAddress(element_index, &b_, + "output_element_address"); + // Do not emit atomic operations if each element in the reduction result + // is computed by one block, that is the dimension being reduced has only + // one block. + const llvm_ir::KernelMappingScheme* mapping_scheme = + reduction_info->GetKernelMappingScheme(); + if (mapping_scheme->GetTileBlockSizeForDimension( + llvm_ir::KernelMappingScheme::DimZ) == 1 && + mapping_scheme->GetTileBlockSizeForDimension( + reduction_info->GetReducedDimensionEnum()) == 1) { + TF_CHECK_OK(EmitCallToNestedComputation( + *reducers[i], + {output_address, + InBoundsGEP(partial_result_addresses[i], {b_.getInt32(j)})}, + output_address)); + } else { + TF_CHECK_OK(EmitAtomicOperationForNestedComputation( + *reducers[i], output_address, + InBoundsGEP(partial_result_addresses[i], {b_.getInt32(j)}))); + } } } } @@ -2626,7 +2693,7 @@ void IrEmitterUnnested::EmitEpilogueForReduction( void IrEmitterUnnested::EmitTileElementForReduction( HloInstruction* unnested_hlo, const llvm_ir::IrArray::Index& index, const KernelCodegenInfo* kernel_info, llvm::Value* y_loc, - llvm::Value* x_loc) { + llvm::Value* x_loc, int64 x_iter_num) { VLOG(10) << "Emit tile element for reduce " << unnested_hlo->ToString(); HloInstruction* reduce_or_tuple = unnested_hlo->opcode() == HloOpcode::kFusion ? unnested_hlo->fused_expression_root() @@ -2639,8 +2706,11 @@ void IrEmitterUnnested::EmitTileElementForReduction( // Record the linear address for the current reduction. const ReductionCodegenInfo* reduction_info = dynamic_cast(kernel_info); + int partial_result_index = reduction_info->IsRowReduction() ? 0 : x_iter_num; + Store(index[reduction_info->GetKeptDimensionEnum()], - reduction_info->GetCurrentOutputLinearIndexAddress()); + InBoundsGEP(reduction_info->GetCurrentOutputLinearIndexAddress(), + {b_.getInt32(partial_result_index)})); if (!reduction_info->IsRowReduction()) { llvm::Type* bool_ty = b_.getInt1Ty(); llvm::AllocaInst* output_inbound_addr = @@ -2687,6 +2757,13 @@ void IrEmitterUnnested::EmitTileElementForReduction( reduction_info->GetKernelMappingScheme()->GetUnnormalizedIndex( index, GetFirstReduceInstruction(output_instructions)->operand(0)->shape()); + int num_partial_results = reduction_info->GetNumberOfPartialResults(); + if (num_partial_results > 1) { + // Clear the linear index field of the IrArray::Index to enable the use of + // GetElementPointer with array types. This enables the vectorization of + // the computation for different partial results. + input_index.ClearLinearIndex(); + } absl::Span partial_reduction_result_addresses = reduction_info->GetPartialResultAddresses(); absl::Span reduction_input_addresses = @@ -2699,10 +2776,12 @@ void IrEmitterUnnested::EmitTileElementForReduction( for (int i = 0; i != reducers.size(); ++i) { llvm::Value* const input_ir_value = input_gens[i](input_index).ValueOrDie(); Store(input_ir_value, reduction_input_addresses[i]); + llvm::Value* partial_result_address = + InBoundsGEP(partial_reduction_result_addresses[i], + {b_.getInt32(partial_result_index)}); TF_CHECK_OK(EmitCallToNestedComputation( - *reducers[i], - {partial_reduction_result_addresses[i], reduction_input_addresses[i]}, - partial_reduction_result_addresses[i])); + *reducers[i], {partial_result_address, reduction_input_addresses[i]}, + partial_result_address)); } // Emit code to generate the output for the non-reduction instructions in the @@ -2713,8 +2792,8 @@ void IrEmitterUnnested::EmitTileElementForReduction( // Emits a kernel for the hlo instruction using the given tiling scheme. void IrEmitterUnnested::EmitBlock(const TileGenerator& emit_one_tile, - const KernelCodegenInfo* kernel_info, - KernelSupportLibrary& ksl, + KernelCodegenInfo* kernel_info, + KernelSupportLibrary* ksl, llvm::Type* index_ty) { KernelMappingScheme* mapping_scheme = kernel_info->GetKernelMappingScheme(); absl::Span dims_in_tile = mapping_scheme->GetDimensionsInTiles(); @@ -2747,15 +2826,14 @@ void IrEmitterUnnested::EmitBlock(const TileGenerator& emit_one_tile, llvm::Value* num_tiles_in_block = Select(ICmpEQ(last_block_for_dim, block_id_for_dim), last_block_size_for_dim, block_size_for_dim); - - ksl.For(loop_name, - /*start=*/index_typed_constant(0), - /*end=*/num_tiles_in_block, - /*step=*/1, [&](llvm::Value* block_dim_induction_var) { - IrArray::Index tile_index = starting_tile.AddOffsetToDim( - block_dim_induction_var, dim_id, &b_); - emit_next_block_dim(tile_index); - }); + ksl->For(loop_name, + /*start=*/index_typed_constant(0), + /*end=*/num_tiles_in_block, + /*step=*/1, [&](llvm::Value* block_dim_induction_var) { + IrArray::Index tile_index = starting_tile.AddOffsetToDim( + block_dim_induction_var, dim_id, &b_); + emit_next_block_dim(tile_index); + }); } }; @@ -2899,8 +2977,7 @@ LaunchDimensions IrEmitterUnnested::EmitKernel( auto emit_tiled_elemental_code_with_bounds_check = [&](const IrArray::Index& index, const string& loop_name, llvm::Value* tile_height, llvm::Value* tile_width, - const std::function& emit_elem_function) { + const EmitElementFunction& emit_elem_function) { EmitTiledElementalCodeWithBoundsCheck(mapping_scheme, index, loop_name, &ksl, &b_, y, x, tile_height, tile_width, emit_elem_function); @@ -2913,10 +2990,6 @@ LaunchDimensions IrEmitterUnnested::EmitKernel( const IrArray::Index input_tile_origin( Permute({0, 2, 1}, output_tile_origin.multidim())); - const IrArray::Index input_index = - input_tile_origin.AddOffsetToDim(x, KernelMappingScheme::DimX, &b_) - .AddOffsetToDim(y, KernelMappingScheme::DimY, &b_); - // If shared memory transpose is needed, wait for all threads to reach this // point, lest we copy a value from tile to output before the other thread // copies it from input to tile. This is `__syncthreads` in CUDA. @@ -2926,9 +2999,10 @@ LaunchDimensions IrEmitterUnnested::EmitKernel( // Note that tile_width and tile_height are flipped here because we are // reading a transposed tile. emit_tiled_elemental_code_with_bounds_check( - input_index, "input", output_tile_bounds[2], output_tile_bounds[1], + input_tile_origin, "input", output_tile_bounds[2], + output_tile_bounds[1], [&](const IrArray::Index& index, llvm::Value* y_loc, - llvm::Value* x_loc) { + llvm::Value* x_loc, int64 /*x_iter_num*/) { for (int64 id : tiled_param_ids) { IrArray& input_in_logical_shape = param_in_reduced_shape_arrays[id]; @@ -2948,18 +3022,15 @@ LaunchDimensions IrEmitterUnnested::EmitKernel( llvm_ir::TiledParameterInfo tiled_param_info(param_shmem_buffers, y, x); kernel_info->SetTiledParamInfo(&tiled_param_info); - const IrArray::Index output_index = - output_tile_origin.AddOffsetToDim(x, KernelMappingScheme::DimX, &b_) - .AddOffsetToDim(y, KernelMappingScheme::DimY, &b_); - // Write to output[index] by emitting code like normal, except that values // for the tiled parameters are read from the shmem buffers. emit_tiled_elemental_code_with_bounds_check( - output_index, "output", output_tile_bounds[1], output_tile_bounds[2], - [&](const IrArray::Index& index, llvm::Value* y_loc, - llvm::Value* x_loc) { - kernel_generator.GetTileElementGenerator()(unnested_hlo, index, - kernel_info, y_loc, x_loc); + output_tile_origin, "output", output_tile_bounds[1], + output_tile_bounds[2], + [&](const IrArray::Index& index, llvm::Value* y_loc, llvm::Value* x_loc, + int64 x_iter_num) { + kernel_generator.GetTileElementGenerator()( + unnested_hlo, index, kernel_info, y_loc, x_loc, x_iter_num); }); // If a tile block contains multiple tiles and shared memory buffers are @@ -2977,7 +3048,7 @@ LaunchDimensions IrEmitterUnnested::EmitKernel( block_prologue_generator(unnested_hlo, kernel_info); } - EmitBlock(std::move(emit_one_tile), kernel_info, ksl, index_ty); + EmitBlock(std::move(emit_one_tile), kernel_info, &ksl, index_ty); const BlockEpilogueGenerator& block_epilogue_generator = kernel_generator.GetBlockEpilogueGenerator(); @@ -3026,17 +3097,19 @@ LaunchDimensions IrEmitterUnnested::EmitHlo021Tile( element_generator = [&](HloInstruction* hlo, const llvm_ir::IrArray::Index& index, const KernelCodegenInfo* kernel_info, - llvm::Value* y_loc, llvm::Value* x_loc) { - EmitTileElementForCopy(hlo, index, kernel_info, y_loc, x_loc); + llvm::Value* y_loc, llvm::Value* x_loc, + int64 x_iter_num) { + EmitTileElementForCopy(hlo, index, kernel_info, y_loc, x_loc, x_iter_num); }; } else { DCHECK_EQ(hlo->opcode(), HloOpcode::kFusion); - element_generator = [&](HloInstruction* hlo, - const llvm_ir::IrArray::Index& index, - const KernelCodegenInfo* kernel_info, - llvm::Value* y_loc, llvm::Value* x_loc) { - EmitTileElementForFusion(hlo, index, kernel_info, y_loc, x_loc); - }; + element_generator = + [&](HloInstruction* hlo, const llvm_ir::IrArray::Index& index, + const KernelCodegenInfo* kernel_info, llvm::Value* y_loc, + llvm::Value* x_loc, int64 x_iter_num) { + EmitTileElementForFusion(hlo, index, kernel_info, y_loc, x_loc, + x_iter_num); + }; } KernelCodegenInfo kernel_info(&mapping_scheme); KernelCodeGenerator kernel_generator(std::move(element_generator)); @@ -3351,6 +3424,7 @@ IrEmitterUnnested::ComputeMappingSchemeAndReductionKind( std::tie(num_reduced_major, num_kept, num_reduced_minor) = GetReductionToVectorDimensions(input_shape, first_reduce->dimensions()); CHECK_EQ(num_output_elems, num_kept); + bool dilated_x = true; if (num_kept == 1) { // Scalar reduction is a special row reduction with depth = height = 1. @@ -3363,15 +3437,31 @@ IrEmitterUnnested::ComputeMappingSchemeAndReductionKind( height = num_reduced_major; width = num_kept; is_row_reduction = false; + // Assume unrolling is beneficial only when we can vectorize the loads + // of small data types. + auto is_unrolling_beneficial = [&] { + // TODO(b/122468062): Need further investigate to see whether we can + // remove the constraint on IsPowerOfTwo. + return IsPowerOfTwo(static_cast(num_kept)) && + primitive_util::BitWidth( + first_reduce->operand(0)->shape().element_type()) <= 16; + }; // Column reduction without transpose doesn't require communication among // threads processing elements in the same tile. The current implementation - // only support the use of on hardware thread block to process one block of - // tiles in the KernelMappingScheme. We try to maximize the values of + // only support the use of one hardware thread block to process one block of + // tiles in the KernelMappingScheme. We try to use one thread to compute + // the partial results for two tensor elements and to maximize the values of // num_threads_x and tile_size_x to allow a bigger hardware thread block. int64 hw_threads_per_block_limit = ThreadsPerBlockLimit(ir_emitter_context_->device_description()); - tile_size_x = std::min(hw_threads_per_block_limit, num_kept); - num_threads_x = tile_size_x; + if (is_unrolling_beneficial()) { + tile_size_x = std::min(2 * hw_threads_per_block_limit, num_kept); + num_threads_x = tile_size_x / 2; + dilated_x = false; + } else { + tile_size_x = std::min(hw_threads_per_block_limit, num_kept); + num_threads_x = tile_size_x; + } int64 kNumElementsPerPartialSum = 128; tile_size_y = kNumElementsPerPartialSum; } else { @@ -3400,6 +3490,7 @@ IrEmitterUnnested::ComputeMappingSchemeAndReductionKind( llvm_ir::KernelMappingScheme mapping_scheme( dims_in_elem, tile_size_y, tile_size_x, req_block_sizes, num_threads_y, num_threads_x, &b_); + mapping_scheme.SetDilatedX(dilated_x); return std::make_tuple(mapping_scheme, is_row_reduction); } @@ -3454,8 +3545,9 @@ Status IrEmitterUnnested::EmitReductionToVector(HloInstruction* unnested_hlo) { /*tile_element_generator=*/ [&](HloInstruction* hlo, const llvm_ir::IrArray::Index& index, const KernelCodegenInfo* kernel_info, llvm::Value* y_loc, - llvm::Value* x_loc) { - EmitTileElementForReduction(hlo, index, kernel_info, y_loc, x_loc); + llvm::Value* x_loc, int64 x_iter_num) { + EmitTileElementForReduction(hlo, index, kernel_info, y_loc, x_loc, + x_iter_num); }, /*block_prologue_generator=*/ [&](HloInstruction* hlo, KernelCodegenInfo* kernel_info) { diff --git a/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.h b/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.h index d217ee36cf..eb0ea90e14 100644 --- a/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.h +++ b/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.h @@ -76,7 +76,6 @@ class IrEmitterUnnested : public IrEmitter { void SetLaneId(llvm::Value* v) { lane_id_ = v; } void SetIndexType(llvm::Type* t) { index_ty_ = t; } void SetTiledParamInfo(llvm_ir::TiledParameterInfo* tiled_param_info) { - CHECK_EQ(tiled_param_info_, nullptr); tiled_param_info_ = tiled_param_info; } @@ -89,7 +88,7 @@ class IrEmitterUnnested : public IrEmitter { } llvm::Type* GetIndexType() const { return index_ty_; } - private: + protected: llvm_ir::KernelMappingScheme* mapping_scheme_; llvm_ir::TiledParameterInfo* tiled_param_info_; llvm::Value* lane_id_; @@ -109,10 +108,12 @@ class IrEmitterUnnested : public IrEmitter { // y_loc: The y coordinate within a tile. // x_loc: The x coordinate within a tile. // kernel_info: Other information to support the kernel code generation. + // x_iter_num: When a thread process N elements in the X dimension, x_iter_num + // has a value of 0..N-1 to identify the element being process. using TileElementGenerator = std::function; + llvm::Value* x_loc, int64 x_iter_num)>; // KernelCodeGenerator records the code generator objects that generate code // for tile elements or tile block prologue/epilogue. @@ -243,26 +244,29 @@ class IrEmitterUnnested : public IrEmitter { const KernelCodeGenerator& kernel_generator, KernelCodegenInfo* kernel_info); void EmitBlock(const TileGenerator& emit_one_tile, - const KernelCodegenInfo* kernel_info, - KernelSupportLibrary& ksl, llvm::Type* index_ty); + KernelCodegenInfo* kernel_info, KernelSupportLibrary* ksl, + llvm::Type* index_ty); // Emits code to process a tensor element in a tile for the given kCopy HLO // that performs a 0-2-1 transpose. void EmitTileElementForCopy(HloInstruction* hlo, const llvm_ir::IrArray::Index& index, const KernelCodegenInfo* kernel_info, - llvm::Value* y_loc, llvm::Value* x_loc); + llvm::Value* y_loc, llvm::Value* x_loc, + int64 x_iter_num); // Emits code to process a tensor element in a tile for the given kLoop fusion // HLO containing parameters that are 0-2-1 transpose of its outputs. void EmitTileElementForFusion(HloInstruction* hlo, const llvm_ir::IrArray::Index& index, const KernelCodegenInfo* kernel_info, - llvm::Value* y_loc, llvm::Value* x_loc); + llvm::Value* y_loc, llvm::Value* x_loc, + int64 x_iter_num); // Emits code to process a tensor element in a tile for the given input hlo // that is either a unnested kReduce or a kInput fusion. void EmitTileElementForReduction(HloInstruction* unnested_hlo, const llvm_ir::IrArray::Index& index, const KernelCodegenInfo* kernel_info, - llvm::Value* y_loc, llvm::Value* x_loc); + llvm::Value* y_loc, llvm::Value* x_loc, + int64 x_iter_num); // Prepares for the code generation for a tile block of a reduction kernel. void EmitPrologueForReduction(HloInstruction* unnested_hlo, KernelCodegenInfo* kernel_info); diff --git a/tensorflow/compiler/xla/service/llvm_ir/ir_array.h b/tensorflow/compiler/xla/service/llvm_ir/ir_array.h index d6d84994ee..a483f7051f 100644 --- a/tensorflow/compiler/xla/service/llvm_ir/ir_array.h +++ b/tensorflow/compiler/xla/service/llvm_ir/ir_array.h @@ -189,6 +189,8 @@ class IrArray { return llvm::ConstantInt::get(index_type_, c); } + void ClearLinearIndex() { linear_ = nullptr; } + private: // Changing the multi-dimensional index invalidates the linear index. std::vector& mutable_multidim() { diff --git a/tensorflow/compiler/xla/service/llvm_ir/kernel_tiling.cc b/tensorflow/compiler/xla/service/llvm_ir/kernel_tiling.cc index cebbc42901..cd8dd72cd7 100644 --- a/tensorflow/compiler/xla/service/llvm_ir/kernel_tiling.cc +++ b/tensorflow/compiler/xla/service/llvm_ir/kernel_tiling.cc @@ -123,7 +123,8 @@ KernelMappingScheme::KernelMappingScheme( dims_in_elems_(dims_in_elems.begin(), dims_in_elems.end()), tile_sizes_{1, tile_size_y, tile_size_x}, num_threads_x_(num_threads_x), - num_threads_y_(num_threads_y) { + num_threads_y_(num_threads_y), + dilated_x_(true) { DCHECK_EQ(dims_in_elems_.size(), 3); DCHECK_EQ(req_block_sizes.size(), 3); diff --git a/tensorflow/compiler/xla/service/llvm_ir/kernel_tiling.h b/tensorflow/compiler/xla/service/llvm_ir/kernel_tiling.h index fb633b12e6..f802cc27d5 100644 --- a/tensorflow/compiler/xla/service/llvm_ir/kernel_tiling.h +++ b/tensorflow/compiler/xla/service/llvm_ir/kernel_tiling.h @@ -117,7 +117,10 @@ class KernelMappingScheme { int64 GetNumberOfTilesInOneBlock() const { return absl::c_accumulate(block_sizes_, 1, std::multiplies()); } - + int64 GetNumberOfTilesInOneBlockForDimension(int d) const { + DCHECK(d >= DimZ && d <= DimX); + return block_sizes_[d]; + } int64 GetNumberOfBlocks() const { return absl::c_accumulate(dims_in_blocks_, 1, std::multiplies()); } @@ -147,6 +150,16 @@ class KernelMappingScheme { GetNumberOfThreadsForDimensionY(); } + bool DilatedX() const { return dilated_x_; } + void SetDilatedX(bool v) { + dilated_x_ = v; + if (!dilated_x_) { + // dilated_x_=false is for the purpose of vectorization, which requires + // GetTileSizeForDimension(DimX) to be a multiplier of num_threads_x_. + CHECK_EQ(GetTileSizeForDimension(DimX) % num_threads_x_, 0); + } + } + IrArray::Index EmitBlockIndex(llvm::Type* index_ty); // Returns the index for the first tile in the block with the given block // index. @@ -186,6 +199,13 @@ class KernelMappingScheme { int64 num_threads_x_; // Number of threads used to process elements in the Y direction of a tile. int64 num_threads_y_; + + // When num_threads_x threads process a total of tile_size_x elements in the + // X dimension of a tile, each threads process n=tile_size_x/num_threads_x + // elements. When dilated_x=false, the n elements processed by a thread are + // contiguous. On the other hand, when dilated_x=true the n elements are + // dilated by a factor of num_threads_x. + bool dilated_x_; }; // A class to represent information for tiled parameters to support IR emission -- GitLab From 021a8fe8a70b880031958f596b6c96e819e14386 Mon Sep 17 00:00:00 2001 From: Nick Felt Date: Mon, 7 Jan 2019 12:23:54 -0800 Subject: [PATCH 426/622] Move contrib/tensorboard/db to new core/summary directory PiperOrigin-RevId: 228213314 --- tensorflow/contrib/cmake/tf_core_framework.cmake | 12 ++++++------ tensorflow/core/kernels/BUILD | 10 ++++------ tensorflow/core/kernels/summary_kernels.cc | 6 +++--- .../{contrib/tensorboard/db => core/summary}/BUILD | 2 +- .../tensorboard/db => core/summary}/loader.cc | 4 ++-- .../tensorboard/db => core/summary}/schema.cc | 2 +- .../tensorboard/db => core/summary}/schema.h | 6 +++--- .../tensorboard/db => core/summary}/schema_test.cc | 2 +- .../db => core/summary}/summary_converter.cc | 2 +- .../db => core/summary}/summary_converter.h | 6 +++--- .../db => core/summary}/summary_db_writer.cc | 4 ++-- .../db => core/summary}/summary_db_writer.h | 6 +++--- .../db => core/summary}/summary_db_writer_test.cc | 4 ++-- .../db => core/summary}/summary_file_writer.cc | 4 ++-- .../db => core/summary}/summary_file_writer.h | 6 +++--- .../db => core/summary}/summary_file_writer_test.cc | 2 +- .../tensorboard/db => core/summary}/vacuum.cc | 0 17 files changed, 38 insertions(+), 40 deletions(-) rename tensorflow/{contrib/tensorboard/db => core/summary}/BUILD (98%) rename tensorflow/{contrib/tensorboard/db => core/summary}/loader.cc (97%) rename tensorflow/{contrib/tensorboard/db => core/summary}/schema.cc (99%) rename tensorflow/{contrib/tensorboard/db => core/summary}/schema.h (87%) rename tensorflow/{contrib/tensorboard/db => core/summary}/schema_test.cc (95%) rename tensorflow/{contrib/tensorboard/db => core/summary}/summary_converter.cc (99%) rename tensorflow/{contrib/tensorboard/db => core/summary}/summary_converter.h (89%) rename tensorflow/{contrib/tensorboard/db => core/summary}/summary_db_writer.cc (99%) rename tensorflow/{contrib/tensorboard/db => core/summary}/summary_db_writer.h (89%) rename tensorflow/{contrib/tensorboard/db => core/summary}/summary_db_writer_test.cc (99%) rename tensorflow/{contrib/tensorboard/db => core/summary}/summary_file_writer.cc (98%) rename tensorflow/{contrib/tensorboard/db => core/summary}/summary_file_writer.h (89%) rename tensorflow/{contrib/tensorboard/db => core/summary}/summary_file_writer_test.cc (99%) rename tensorflow/{contrib/tensorboard/db => core/summary}/vacuum.cc (100%) diff --git a/tensorflow/contrib/cmake/tf_core_framework.cmake b/tensorflow/contrib/cmake/tf_core_framework.cmake index d7b2a1339e..9ae5831f47 100644 --- a/tensorflow/contrib/cmake/tf_core_framework.cmake +++ b/tensorflow/contrib/cmake/tf_core_framework.cmake @@ -302,8 +302,8 @@ file(GLOB_RECURSE tf_core_framework_srcs "${tensorflow_source_dir}/tensorflow/core/common_runtime/session.cc" "${tensorflow_source_dir}/tensorflow/core/common_runtime/session_factory.cc" "${tensorflow_source_dir}/tensorflow/core/common_runtime/session_options.cc" - "${tensorflow_source_dir}/tensorflow/contrib/tensorboard/db/*.cc" - "${tensorflow_source_dir}/tensorflow/contrib/tensorboard/db/*.h" + "${tensorflow_source_dir}/tensorflow/core/summary/*.cc" + "${tensorflow_source_dir}/tensorflow/core/summary/*.h" "${tensorflow_source_dir}/public/*.h" ) @@ -317,14 +317,14 @@ file(GLOB_RECURSE tf_core_framework_exclude_srcs "${tensorflow_source_dir}/tensorflow/core/util/*test*.h" "${tensorflow_source_dir}/tensorflow/core/util/*test*.cc" "${tensorflow_source_dir}/tensorflow/core/util/*main.cc" - "${tensorflow_source_dir}/tensorflow/contrib/tensorboard/db/*test*.cc" - "${tensorflow_source_dir}/tensorflow/contrib/tensorboard/db/loader.cc" - "${tensorflow_source_dir}/tensorflow/contrib/tensorboard/db/vacuum.cc" + "${tensorflow_source_dir}/tensorflow/core/summary/*test*.cc" + "${tensorflow_source_dir}/tensorflow/core/summary/loader.cc" + "${tensorflow_source_dir}/tensorflow/core/summary/vacuum.cc" ) # TODO(jart): Why doesn't this work? # set_source_files_properties( -# ${tensorflow_source_dir}/tensorflow/contrib/tensorboard/db/snapfn.cc +# ${tensorflow_source_dir}/tensorflow/core/lib/db/snapfn.cc # PROPERTIES COMPILE_FLAGS -DSQLITE_OMIT_LOAD_EXTENSION) list(REMOVE_ITEM tf_core_framework_srcs ${tf_core_framework_exclude_srcs}) diff --git a/tensorflow/core/kernels/BUILD b/tensorflow/core/kernels/BUILD index 3891caf5d3..136902e249 100644 --- a/tensorflow/core/kernels/BUILD +++ b/tensorflow/core/kernels/BUILD @@ -31,7 +31,6 @@ load( "//tensorflow:tensorflow.bzl", "cc_header_only_library", "if_android", - "if_not_v2", "if_not_windows", "tf_cc_binary", "tf_cc_test", @@ -6888,11 +6887,10 @@ tf_kernel_library( "//tensorflow/core:protos_all_cc", "//tensorflow/core:summary_ops_op_lib", "//tensorflow/core/lib/db:sqlite", - ] + if_not_v2([ - "//tensorflow/contrib/tensorboard/db:schema", - "//tensorflow/contrib/tensorboard/db:summary_db_writer", - "//tensorflow/contrib/tensorboard/db:summary_file_writer", - ]), + "//tensorflow/core/summary:schema", + "//tensorflow/core/summary:summary_db_writer", + "//tensorflow/core/summary:summary_file_writer", + ], ) tf_kernel_library( diff --git a/tensorflow/core/kernels/summary_kernels.cc b/tensorflow/core/kernels/summary_kernels.cc index b287f0cc2f..5e3465d1dd 100644 --- a/tensorflow/core/kernels/summary_kernels.cc +++ b/tensorflow/core/kernels/summary_kernels.cc @@ -13,14 +13,14 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "tensorflow/contrib/tensorboard/db/schema.h" -#include "tensorflow/contrib/tensorboard/db/summary_db_writer.h" -#include "tensorflow/contrib/tensorboard/db/summary_file_writer.h" #include "tensorflow/core/framework/graph.pb.h" #include "tensorflow/core/framework/op_kernel.h" #include "tensorflow/core/framework/resource_mgr.h" #include "tensorflow/core/lib/db/sqlite.h" #include "tensorflow/core/platform/protobuf.h" +#include "tensorflow/core/summary/schema.h" +#include "tensorflow/core/summary/summary_db_writer.h" +#include "tensorflow/core/summary/summary_file_writer.h" #include "tensorflow/core/util/event.pb.h" namespace tensorflow { diff --git a/tensorflow/contrib/tensorboard/db/BUILD b/tensorflow/core/summary/BUILD similarity index 98% rename from tensorflow/contrib/tensorboard/db/BUILD rename to tensorflow/core/summary/BUILD index 6507546ee9..a89175cdb1 100644 --- a/tensorflow/contrib/tensorboard/db/BUILD +++ b/tensorflow/core/summary/BUILD @@ -1,5 +1,5 @@ # Description: -# TensorBoard database code. +# C++ implementation code for the summary writing APIs. package(default_visibility = ["//tensorflow:internal"]) diff --git a/tensorflow/contrib/tensorboard/db/loader.cc b/tensorflow/core/summary/loader.cc similarity index 97% rename from tensorflow/contrib/tensorboard/db/loader.cc rename to tensorflow/core/summary/loader.cc index 6439328022..68535feacf 100644 --- a/tensorflow/contrib/tensorboard/db/loader.cc +++ b/tensorflow/core/summary/loader.cc @@ -15,8 +15,8 @@ limitations under the License. #include #include -#include "tensorflow/contrib/tensorboard/db/schema.h" -#include "tensorflow/contrib/tensorboard/db/summary_db_writer.h" +#include "tensorflow/core/summary/schema.h" +#include "tensorflow/core/summary/summary_db_writer.h" #include "tensorflow/core/lib/db/sqlite.h" #include "tensorflow/core/lib/io/record_reader.h" #include "tensorflow/core/platform/init_main.h" diff --git a/tensorflow/contrib/tensorboard/db/schema.cc b/tensorflow/core/summary/schema.cc similarity index 99% rename from tensorflow/contrib/tensorboard/db/schema.cc rename to tensorflow/core/summary/schema.cc index 3c7bc87e4a..822e2fa3bf 100644 --- a/tensorflow/contrib/tensorboard/db/schema.cc +++ b/tensorflow/core/summary/schema.cc @@ -12,7 +12,7 @@ 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/contrib/tensorboard/db/schema.h" +#include "tensorflow/core/summary/schema.h" #include "tensorflow/core/lib/core/errors.h" diff --git a/tensorflow/contrib/tensorboard/db/schema.h b/tensorflow/core/summary/schema.h similarity index 87% rename from tensorflow/contrib/tensorboard/db/schema.h rename to tensorflow/core/summary/schema.h index 3da4504225..6305f8eabd 100644 --- a/tensorflow/contrib/tensorboard/db/schema.h +++ b/tensorflow/core/summary/schema.h @@ -12,8 +12,8 @@ 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_CONTRIB_TENSORBOARD_DB_SCHEMA_H_ -#define TENSORFLOW_CONTRIB_TENSORBOARD_DB_SCHEMA_H_ +#ifndef TENSORFLOW_CORE_SUMMARY_SCHEMA_H_ +#define TENSORFLOW_CORE_SUMMARY_SCHEMA_H_ #include "tensorflow/core/lib/core/status.h" #include "tensorflow/core/lib/db/sqlite.h" @@ -30,4 +30,4 @@ Status SetupTensorboardSqliteDb(Sqlite* db); } // namespace tensorflow -#endif // TENSORFLOW_CONTRIB_TENSORBOARD_DB_SCHEMA_H_ +#endif // TENSORFLOW_CORE_SUMMARY_SCHEMA_H_ diff --git a/tensorflow/contrib/tensorboard/db/schema_test.cc b/tensorflow/core/summary/schema_test.cc similarity index 95% rename from tensorflow/contrib/tensorboard/db/schema_test.cc rename to tensorflow/core/summary/schema_test.cc index 4d3f2880bd..fa21b45b62 100644 --- a/tensorflow/contrib/tensorboard/db/schema_test.cc +++ b/tensorflow/core/summary/schema_test.cc @@ -12,7 +12,7 @@ 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/contrib/tensorboard/db/schema.h" +#include "tensorflow/core/summary/schema.h" #include diff --git a/tensorflow/contrib/tensorboard/db/summary_converter.cc b/tensorflow/core/summary/summary_converter.cc similarity index 99% rename from tensorflow/contrib/tensorboard/db/summary_converter.cc rename to tensorflow/core/summary/summary_converter.cc index 93c1183072..e6e34e9602 100644 --- a/tensorflow/contrib/tensorboard/db/summary_converter.cc +++ b/tensorflow/core/summary/summary_converter.cc @@ -12,7 +12,7 @@ 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/contrib/tensorboard/db/summary_converter.h" +#include "tensorflow/core/summary/summary_converter.h" #include "tensorflow/core/framework/register_types.h" #include "tensorflow/core/framework/summary.pb.h" diff --git a/tensorflow/contrib/tensorboard/db/summary_converter.h b/tensorflow/core/summary/summary_converter.h similarity index 89% rename from tensorflow/contrib/tensorboard/db/summary_converter.h rename to tensorflow/core/summary/summary_converter.h index 329c7f9f2f..dc005d2604 100644 --- a/tensorflow/contrib/tensorboard/db/summary_converter.h +++ b/tensorflow/core/summary/summary_converter.h @@ -12,8 +12,8 @@ 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_CONTRIB_TENSORBOARD_DB_SUMMARY_CONVERTER_H_ -#define TENSORFLOW_CONTRIB_TENSORBOARD_DB_SUMMARY_CONVERTER_H_ +#ifndef TENSORFLOW_CORE_SUMMARY_SUMMARY_CONVERTER_H_ +#define TENSORFLOW_CORE_SUMMARY_SUMMARY_CONVERTER_H_ #include "tensorflow/core/framework/summary.pb.h" #include "tensorflow/core/framework/tensor.h" @@ -35,4 +35,4 @@ Status AddTensorAsAudioToSummary(const Tensor& tensor, const string& tag, } // namespace tensorflow -#endif // TENSORFLOW_CONTRIB_TENSORBOARD_DB_SUMMARY_CONVERTER_H_ +#endif // TENSORFLOW_CORE_SUMMARY_SUMMARY_CONVERTER_H_ diff --git a/tensorflow/contrib/tensorboard/db/summary_db_writer.cc b/tensorflow/core/summary/summary_db_writer.cc similarity index 99% rename from tensorflow/contrib/tensorboard/db/summary_db_writer.cc rename to tensorflow/core/summary/summary_db_writer.cc index cfdc884277..7a5d796821 100644 --- a/tensorflow/contrib/tensorboard/db/summary_db_writer.cc +++ b/tensorflow/core/summary/summary_db_writer.cc @@ -12,11 +12,11 @@ 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/contrib/tensorboard/db/summary_db_writer.h" +#include "tensorflow/core/summary/summary_db_writer.h" #include -#include "tensorflow/contrib/tensorboard/db/summary_converter.h" +#include "tensorflow/core/summary/summary_converter.h" #include "tensorflow/core/framework/graph.pb.h" #include "tensorflow/core/framework/node_def.pb.h" #include "tensorflow/core/framework/register_types.h" diff --git a/tensorflow/contrib/tensorboard/db/summary_db_writer.h b/tensorflow/core/summary/summary_db_writer.h similarity index 89% rename from tensorflow/contrib/tensorboard/db/summary_db_writer.h rename to tensorflow/core/summary/summary_db_writer.h index 746da1533b..5669afe7f6 100644 --- a/tensorflow/contrib/tensorboard/db/summary_db_writer.h +++ b/tensorflow/core/summary/summary_db_writer.h @@ -12,8 +12,8 @@ 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_CONTRIB_TENSORBOARD_DB_SUMMARY_DB_WRITER_H_ -#define TENSORFLOW_CONTRIB_TENSORBOARD_DB_SUMMARY_DB_WRITER_H_ +#ifndef TENSORFLOW_CORE_SUMMARY_SUMMARY_DB_WRITER_H_ +#define TENSORFLOW_CORE_SUMMARY_SUMMARY_DB_WRITER_H_ #include "tensorflow/core/kernels/summary_interface.h" #include "tensorflow/core/lib/core/status.h" @@ -39,4 +39,4 @@ Status CreateSummaryDbWriter(Sqlite* db, const string& experiment_name, } // namespace tensorflow -#endif // TENSORFLOW_CONTRIB_TENSORBOARD_DB_SUMMARY_DB_WRITER_H_ +#endif // TENSORFLOW_CORE_SUMMARY_SUMMARY_DB_WRITER_H_ diff --git a/tensorflow/contrib/tensorboard/db/summary_db_writer_test.cc b/tensorflow/core/summary/summary_db_writer_test.cc similarity index 99% rename from tensorflow/contrib/tensorboard/db/summary_db_writer_test.cc rename to tensorflow/core/summary/summary_db_writer_test.cc index 2e8d4109dd..c4e9ddea2c 100644 --- a/tensorflow/contrib/tensorboard/db/summary_db_writer_test.cc +++ b/tensorflow/core/summary/summary_db_writer_test.cc @@ -12,9 +12,9 @@ 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/contrib/tensorboard/db/summary_db_writer.h" +#include "tensorflow/core/summary/summary_db_writer.h" -#include "tensorflow/contrib/tensorboard/db/schema.h" +#include "tensorflow/core/summary/schema.h" #include "tensorflow/core/framework/function.pb.h" #include "tensorflow/core/framework/graph.pb.h" #include "tensorflow/core/framework/node_def.pb.h" diff --git a/tensorflow/contrib/tensorboard/db/summary_file_writer.cc b/tensorflow/core/summary/summary_file_writer.cc similarity index 98% rename from tensorflow/contrib/tensorboard/db/summary_file_writer.cc rename to tensorflow/core/summary/summary_file_writer.cc index 22b6f09d0c..593ccdd684 100644 --- a/tensorflow/contrib/tensorboard/db/summary_file_writer.cc +++ b/tensorflow/core/summary/summary_file_writer.cc @@ -12,9 +12,9 @@ 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/contrib/tensorboard/db/summary_file_writer.h" +#include "tensorflow/core/summary/summary_file_writer.h" -#include "tensorflow/contrib/tensorboard/db/summary_converter.h" +#include "tensorflow/core/summary/summary_converter.h" #include "tensorflow/core/framework/graph.pb.h" #include "tensorflow/core/framework/op_kernel.h" #include "tensorflow/core/framework/resource_mgr.h" diff --git a/tensorflow/contrib/tensorboard/db/summary_file_writer.h b/tensorflow/core/summary/summary_file_writer.h similarity index 89% rename from tensorflow/contrib/tensorboard/db/summary_file_writer.h rename to tensorflow/core/summary/summary_file_writer.h index 73b0a5542b..7d964516da 100644 --- a/tensorflow/contrib/tensorboard/db/summary_file_writer.h +++ b/tensorflow/core/summary/summary_file_writer.h @@ -12,8 +12,8 @@ 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_CONTRIB_TENSORBOARD_DB_SUMMARY_FILE_WRITER_H_ -#define TENSORFLOW_CONTRIB_TENSORBOARD_DB_SUMMARY_FILE_WRITER_H_ +#ifndef TENSORFLOW_CORE_SUMMARY_SUMMARY_FILE_WRITER_H_ +#define TENSORFLOW_CORE_SUMMARY_SUMMARY_FILE_WRITER_H_ #include "tensorflow/core/kernels/summary_interface.h" #include "tensorflow/core/lib/core/status.h" @@ -40,4 +40,4 @@ Status CreateSummaryFileWriter(int max_queue, int flush_millis, } // namespace tensorflow -#endif // TENSORFLOW_CONTRIB_TENSORBOARD_DB_SUMMARY_FILE_WRITER_H_ +#endif // TENSORFLOW_CORE_SUMMARY_SUMMARY_FILE_WRITER_H_ diff --git a/tensorflow/contrib/tensorboard/db/summary_file_writer_test.cc b/tensorflow/core/summary/summary_file_writer_test.cc similarity index 99% rename from tensorflow/contrib/tensorboard/db/summary_file_writer_test.cc rename to tensorflow/core/summary/summary_file_writer_test.cc index ffbfb9533e..d3b19c3abd 100644 --- a/tensorflow/contrib/tensorboard/db/summary_file_writer_test.cc +++ b/tensorflow/core/summary/summary_file_writer_test.cc @@ -12,7 +12,7 @@ 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/contrib/tensorboard/db/summary_file_writer.h" +#include "tensorflow/core/summary/summary_file_writer.h" #include "tensorflow/core/framework/summary.pb.h" #include "tensorflow/core/framework/tensor.pb.h" diff --git a/tensorflow/contrib/tensorboard/db/vacuum.cc b/tensorflow/core/summary/vacuum.cc similarity index 100% rename from tensorflow/contrib/tensorboard/db/vacuum.cc rename to tensorflow/core/summary/vacuum.cc -- GitLab From bd2610f638bd35dca30bb00a6c16da775b3bfcdf Mon Sep 17 00:00:00 2001 From: Eugene Zhulenev Date: Mon, 7 Jan 2019 12:31:43 -0800 Subject: [PATCH 427/622] [Grappler] Remapper for GPU Conv2d+BiasAdd+Activation PiperOrigin-RevId: 228214620 --- tensorflow/core/grappler/BUILD | 1 + tensorflow/core/grappler/graph_view.cc | 23 +- tensorflow/core/grappler/graph_view.h | 10 +- tensorflow/core/grappler/optimizers/BUILD | 2 + .../core/grappler/optimizers/remapper.cc | 384 ++++++++++++------ tensorflow/core/grappler/utils.cc | 18 +- tensorflow/core/grappler/utils.h | 3 + tensorflow/core/grappler/utils/BUILD | 1 + .../core/grappler/utils/grappler_test.cc | 27 ++ .../core/grappler/utils/grappler_test.h | 16 +- tensorflow/core/kernels/conv_ops_test.cc | 20 + 11 files changed, 353 insertions(+), 152 deletions(-) diff --git a/tensorflow/core/grappler/BUILD b/tensorflow/core/grappler/BUILD index 4d0f02f4d6..55d642612b 100644 --- a/tensorflow/core/grappler/BUILD +++ b/tensorflow/core/grappler/BUILD @@ -27,6 +27,7 @@ cc_library( "//tensorflow/core:lib", "//tensorflow/core:lib_internal", "//tensorflow/core:protos_all_cc", + "@com_google_absl//absl/strings", "@com_google_absl//absl/types:span", ], ) diff --git a/tensorflow/core/grappler/graph_view.cc b/tensorflow/core/grappler/graph_view.cc index ba9d2eb321..be9b9c36c7 100644 --- a/tensorflow/core/grappler/graph_view.cc +++ b/tensorflow/core/grappler/graph_view.cc @@ -66,28 +66,27 @@ int OpInputPortIdToArgId(const NodeDef& node, const OpDef& op, int port_id) { bool HasSingleFanoutNode(const GraphView& graph_view, const NodeDef* node, int port) { const auto output = GraphView::OutputPort(node, port); - const auto fanout = graph_view.GetFanout(output); - return fanout.size() <= 1; + return graph_view.GetFanout(output).size() <= 1; } bool HasFanouts(const GraphView& graph_view, const NodeDef* node, int port) { const auto output = GraphView::OutputPort(node, port); - const auto fanout = graph_view.GetFanout(output); - return !fanout.empty(); + return !graph_view.GetFanout(output).empty(); } -bool NoControlFanin(const GraphView& graph_view, const NodeDef* node) { - const auto control_port = GraphView::InputPort(node, -1); - return graph_view.GetFanin(control_port).empty(); +bool HasControlFanin(const GraphView& graph_view, const NodeDef* node) { + const auto control_port = GraphView::InputPort(node, Graph::kControlSlot); + return !graph_view.GetFanin(control_port).empty(); } -bool NoControlFanout(const GraphView& graph_view, const NodeDef* node) { - const auto control_port = GraphView::OutputPort(node, -1); - return graph_view.GetFanout(control_port).empty(); +bool HasControlFanout(const GraphView& graph_view, const NodeDef* node) { + const auto control_port = GraphView::OutputPort(node, Graph::kControlSlot); + return !graph_view.GetFanout(control_port).empty(); } -bool NoControlFaninOrFanout(const GraphView& graph_view, const NodeDef* node) { - return NoControlFanin(graph_view, node) && NoControlFanout(graph_view, node); +bool HasControlFaninOrFanout(const GraphView& graph_view, const NodeDef* node) { + return HasControlFanin(graph_view, node) || + HasControlFanout(graph_view, node); } } // end namespace grappler diff --git a/tensorflow/core/grappler/graph_view.h b/tensorflow/core/grappler/graph_view.h index a17d17524a..dc4ab93894 100644 --- a/tensorflow/core/grappler/graph_view.h +++ b/tensorflow/core/grappler/graph_view.h @@ -369,10 +369,12 @@ bool HasSingleFanoutNode(const GraphView& graph_view, const NodeDef* node, // Returns true if node has at least one fanout node at given output port. bool HasFanouts(const GraphView& graph_view, const NodeDef* node, int port = 0); - -bool NoControlFanin(const GraphView& graph_view, const NodeDef* node); -bool NoControlFanout(const GraphView& graph_view, const NodeDef* node); -bool NoControlFaninOrFanout(const GraphView& graph_view, const NodeDef* node); +// Returns true if the node has at least one input control dependency. +bool HasControlFanin(const GraphView& graph_view, const NodeDef* node); +// Returns true if the node has at least one output control dependency. +bool HasControlFanout(const GraphView& graph_view, const NodeDef* node); +// Returns true if the node has at least one input or output control dependency. +bool HasControlFaninOrFanout(const GraphView& graph_view, const NodeDef* node); } // end namespace grappler } // end namespace tensorflow diff --git a/tensorflow/core/grappler/optimizers/BUILD b/tensorflow/core/grappler/optimizers/BUILD index e2b9de7cc5..7e29cee86a 100644 --- a/tensorflow/core/grappler/optimizers/BUILD +++ b/tensorflow/core/grappler/optimizers/BUILD @@ -700,6 +700,7 @@ cc_library( "//tensorflow/core/grappler:op_types", "//tensorflow/core/grappler:utils", "//tensorflow/core/grappler/costs:graph_properties", + "//tensorflow/core/grappler/utils:symbolic_shapes", "//tensorflow/core/grappler/utils:topological_sort", "@com_google_absl//absl/container:flat_hash_set", ], @@ -711,6 +712,7 @@ tf_cuda_cc_test( deps = [ ":remapper", "//tensorflow/cc:cc_ops", + "//tensorflow/core:framework", "//tensorflow/core:protos_all_cc", "//tensorflow/core:test", "//tensorflow/core:test_main", diff --git a/tensorflow/core/grappler/optimizers/remapper.cc b/tensorflow/core/grappler/optimizers/remapper.cc index f0c81f29e6..0869e3b49b 100644 --- a/tensorflow/core/grappler/optimizers/remapper.cc +++ b/tensorflow/core/grappler/optimizers/remapper.cc @@ -23,6 +23,7 @@ limitations under the License. #include "tensorflow/core/grappler/op_types.h" #include "tensorflow/core/grappler/optimizers/constant_folding.h" #include "tensorflow/core/grappler/utils.h" +#include "tensorflow/core/grappler/utils/symbolic_shapes.h" #include "tensorflow/core/grappler/utils/topological_sort.h" #include "tensorflow/core/platform/logging.h" @@ -60,17 +61,30 @@ struct RemapperContext { // FusedBatchNorm that can be replaced with a cheaper set of primitives. struct FusedBatchNorm { + FusedBatchNorm() = default; + explicit FusedBatchNorm(const NodeDef* fused_batch_norm) + : fused_batch_norm(fused_batch_norm) {} + const NodeDef* fused_batch_norm = nullptr; }; // Conv2D node followed by a BiasAdd. struct Conv2DWithBiasAdd { + Conv2DWithBiasAdd() = default; + Conv2DWithBiasAdd(const NodeDef* conv2d, const NodeDef* bias_add) + : conv2d(conv2d), bias_add(bias_add) {} + const NodeDef* conv2d = nullptr; const NodeDef* bias_add = nullptr; }; // Conv2D node followed by a BiasAdd and Relu. struct Conv2DWithBiasAddAndRelu { + Conv2DWithBiasAddAndRelu() = default; + Conv2DWithBiasAddAndRelu(const NodeDef* conv2d, const NodeDef* bias_add, + const NodeDef* relu) + : conv2d(conv2d), bias_add(bias_add), relu(relu) {} + const NodeDef* conv2d = nullptr; const NodeDef* bias_add = nullptr; const NodeDef* relu = nullptr; @@ -78,6 +92,11 @@ struct Conv2DWithBiasAddAndRelu { // Conv2D node followed by a Squeeze and BiasAdd. struct Conv2DWithSqueezeAndBiasAdd { + Conv2DWithSqueezeAndBiasAdd() = default; + Conv2DWithSqueezeAndBiasAdd(const NodeDef* conv2d, const NodeDef* squeeze, + const NodeDef* bias_add) + : conv2d(conv2d), squeeze(squeeze), bias_add(bias_add) {} + const NodeDef* conv2d = nullptr; const NodeDef* squeeze = nullptr; const NodeDef* bias_add = nullptr; @@ -85,6 +104,11 @@ struct Conv2DWithSqueezeAndBiasAdd { // Conv2D node followed by a FusedBatchNorm. struct Conv2DWithBatchNorm { + Conv2DWithBatchNorm() = default; + Conv2DWithBatchNorm(const NodeDef* conv2d, const NodeDef* fused_batch_norm, + float epsilon = 0.0) + : conv2d(conv2d), fused_batch_norm(fused_batch_norm), epsilon(epsilon) {} + const NodeDef* conv2d = nullptr; const NodeDef* fused_batch_norm = nullptr; float epsilon = 0.0; @@ -92,16 +116,23 @@ struct Conv2DWithBatchNorm { // Conv2D node followed by a FusedBatchNorm and Relu. struct Conv2DWithBatchNormAndRelu { + Conv2DWithBatchNormAndRelu() = default; + Conv2DWithBatchNormAndRelu(const NodeDef* conv2d, + const NodeDef* fused_batch_norm, + const NodeDef* relu, float epsilon = 0.0) + : conv2d(conv2d), + fused_batch_norm(fused_batch_norm), + relu(relu), + epsilon(epsilon) {} + const NodeDef* conv2d = nullptr; const NodeDef* fused_batch_norm = nullptr; const NodeDef* relu = nullptr; float epsilon = 0.0; }; -bool IsFloatOrDoubleDataType(const NodeDef* node, - const string& type_attr = "T") { - DataType dtype = GetDataTypeFromAttr(*node, type_attr); - return dtype == DT_FLOAT || dtype == DT_DOUBLE; +bool IsInPreserveSet(const RemapperContext& ctx, const NodeDef* node) { + return ctx.nodes_to_preserve.count(node->name()) > 0; } bool HaveSameDataType(const NodeDef* lhs, const NodeDef* rhs, @@ -119,91 +150,165 @@ bool HasDataType(const NodeDef* node, const DataType& expected, return dtype == expected; } -bool IsInPreserveSet(const RemapperContext& ctx, const NodeDef* node) { - return ctx.nodes_to_preserve.count(node->name()) > 0; +bool IsCpuCompatibleDataType(const NodeDef* node, + const string& type_attr = "T") { + DataType dtype = GetDataTypeFromAttr(*node, type_attr); + return dtype == DT_FLOAT || dtype == DT_DOUBLE; +} + +bool IsGpuCompatibleDataType(const NodeDef* node, + const string& type_attr = "T") { + DataType dtype = GetDataTypeFromAttr(*node, type_attr); + return dtype == DT_FLOAT; +} + +bool IsCpuCompatibleDataFormat(const NodeDef* conv2d) { + DCHECK(IsConv2D(*conv2d)) << "Expected Conv2D op"; + const string& data_format = conv2d->attr().at(kDataFormat).s(); + return data_format == "NHWC"; +} + +bool IsGpuCompatibleDataFormat(const NodeDef* conv2d) { + DCHECK(IsConv2D(*conv2d)) << "Expected Conv2D op"; + const string& data_format = conv2d->attr().at(kDataFormat).s(); + return data_format == "NHWC" || data_format == "NCHW"; } -bool FindConv2DWithBias(const RemapperContext& ctx, const NodeDef* node, - Conv2DWithBiasAdd* matched) { +bool IsCpuCompatibleConv2D(const NodeDef* conv2d) { + DCHECK(IsConv2D(*conv2d)) << "Expected Conv2D op"; + return NodeIsOnCpu(conv2d) && IsCpuCompatibleDataType(conv2d) && + IsCpuCompatibleDataFormat(conv2d); +} + +bool IsGpuCompatibleConv2D(const NodeDef* conv2d) { + DCHECK(IsConv2D(*conv2d)) << "Expected Conv2D op"; + return NodeIsOnGpu(conv2d) && IsGpuCompatibleDataType(conv2d) && + IsGpuCompatibleDataFormat(conv2d); +} + +// Checks if we can rewrite a pattern to the `_FusedConv2D` on CPU device. +template +bool IsCpuCompatible(const Pattern& matched) { + return IsCpuCompatibleConv2D(matched.conv2d); +} + +// Checks if we can rewrite a pattern to the `_FusedConv2D` on GPU device. +bool IsGpuCompatible(const RemapperContext& ctx, + const Conv2DWithBiasAddAndRelu& matched) { + const std::vector& input_props = + ctx.graph_properties.GetInputProperties(matched.conv2d->name()); + const TensorShapeProto& filter_shape = + input_props.size() >= 2 ? input_props[1].shape() : TensorShapeProto(); + + // FusedConv2D on GPU with 1x1 convolution is marginally faster than + // in-graph computation in micro benchmarks (see kernels/conv_ops_test.cc), + // and significantly slower in large scale benchmarks. + bool is_spatial_conv = Rank(filter_shape) == 4 && // + IsKnown(filter_shape.dim(1)) && // + IsKnown(filter_shape.dim(2)) && // + filter_shape.dim(1).size() != 1 && // + filter_shape.dim(2).size() != 1; + + return is_spatial_conv && IsGpuCompatibleConv2D(matched.conv2d); +} +bool IsGpuCompatible(const RemapperContext& ctx, + const Conv2DWithBiasAdd& matched) { + return false; +} +bool IsGpuCompatible(const RemapperContext& ctx, + const Conv2DWithSqueezeAndBiasAdd& matched) { + return false; +} + +// Returns true if the given pattern is supported on the assigned device. +template +bool IsDeviceCompatible(const RemapperContext& ctx, Pattern& matched) { + return IsCpuCompatible(matched) || IsGpuCompatible(ctx, matched); +} + +bool FindConv2DWithBias(const RemapperContext& ctx, const NodeDef* bias_add, + Conv2DWithBiasAdd* matched, + bool check_device_compatible = true) { if (!EigenSupportsContractionOutputKernel()) return false; // Root of the pattern must be a BiasAdd. - if (!node) return false; - if (!IsBiasAdd(*node)) return false; - if (!NodeIsOnCpu(node)) return false; - if (!IsFloatOrDoubleDataType(node)) return false; - if (!NoControlFaninOrFanout(ctx.graph_view, node)) return false; + if (bias_add == nullptr || !IsBiasAdd(*bias_add) || + HasControlFaninOrFanout(ctx.graph_view, bias_add)) + return false; - // Input to the BiasAdd must be a Conv2D in NHWC format. - const auto input_port = GraphView::InputPort(node, 0); + // Input to the BiasAdd must be a Conv2D. + const auto input_port = GraphView::InputPort(bias_add, 0); const auto conv2d = ctx.graph_view.GetRegularFanin(input_port); - if (!conv2d.node) return false; - if (!IsConv2D(*conv2d.node)) return false; - if (conv2d.node->attr().at(kDataFormat).s() != "NHWC") return false; - if (!NodeIsOnCpu(conv2d.node)) return false; - if (!HaveSameDataType(node, conv2d.node)) return false; - if (!NoControlFaninOrFanout(ctx.graph_view, conv2d.node)) return false; - if (!HasSingleFanoutNode(ctx.graph_view, conv2d.node)) return false; - if (IsInPreserveSet(ctx, conv2d.node)) return false; + + if (!conv2d.node || !IsConv2D(*conv2d.node) || + !HaveSameDataType(bias_add, conv2d.node) || + HasControlFaninOrFanout(ctx.graph_view, conv2d.node) || + !HasSingleFanoutNode(ctx.graph_view, conv2d.node) || + IsInPreserveSet(ctx, conv2d.node)) + return false; + + // Check that data type and data format are supported on assigned device. + const Conv2DWithBiasAdd pattern{conv2d.node, bias_add}; + if (check_device_compatible && !IsDeviceCompatible(ctx, pattern)) { + return false; + } // We successfully found a Conv2D+BiasAdd pattern. - matched->conv2d = conv2d.node; - matched->bias_add = node; + *matched = pattern; return true; } -bool FindConv2DWithBiasAndRelu(const RemapperContext& ctx, const NodeDef* node, +bool FindConv2DWithBiasAndRelu(const RemapperContext& ctx, const NodeDef* relu, Conv2DWithBiasAddAndRelu* matched) { if (!EigenSupportsContractionOutputKernel()) return false; // Root of the pattern must be a Relu. - if (!node) return false; - if (!IsRelu(*node)) return false; - if (!NodeIsOnCpu(node)) return false; - if (!IsFloatOrDoubleDataType(node)) return false; - if (!NoControlFaninOrFanout(ctx.graph_view, node)) return false; + if (!relu || !IsRelu(*relu) || HasControlFaninOrFanout(ctx.graph_view, relu)) + return false; // And input to Relu must match Conv2DWithBiasAdd pattern. - const auto input_port = GraphView::InputPort(node, 0); + const auto input_port = GraphView::InputPort(relu, 0); const auto bias_add = ctx.graph_view.GetRegularFanin(input_port); Conv2DWithBiasAdd base; - if (!FindConv2DWithBias(ctx, bias_add.node, &base)) return false; - if (!HasSingleFanoutNode(ctx.graph_view, base.bias_add)) return false; - if (!HaveSameDataType(node, base.bias_add)) return false; - if (IsInPreserveSet(ctx, base.bias_add)) return false; + if (!FindConv2DWithBias(ctx, bias_add.node, &base, + /*check_device_compatible=*/false) || + !HasSingleFanoutNode(ctx.graph_view, base.bias_add) || + !HaveSameDataType(relu, base.bias_add) || + IsInPreserveSet(ctx, base.bias_add)) + return false; + + // Check that data type and data format are supported on assigned device. + const Conv2DWithBiasAddAndRelu pattern{base.conv2d, base.bias_add, relu}; + if (!IsDeviceCompatible(ctx, pattern)) return false; // We successfully found a Conv2D+BiasAdd+Relu pattern. - matched->conv2d = base.conv2d; - matched->bias_add = base.bias_add; - matched->relu = node; + *matched = pattern; return true; } bool FindConv2DWithSqueezeAndBias(const RemapperContext& ctx, - const NodeDef* node, + const NodeDef* bias_add, Conv2DWithSqueezeAndBiasAdd* matched) { if (!EigenSupportsContractionOutputKernel()) return false; // Root of the pattern must be a BiasAdd. - if (node == nullptr) return false; - if (node->op() != "BiasAdd") return false; - if (!NodeIsOnCpu(node)) return false; - if (!IsFloatOrDoubleDataType(node)) return false; - if (!NoControlFaninOrFanout(ctx.graph_view, node)) return false; + if (!bias_add || !IsBiasAdd(*bias_add) || + HasControlFaninOrFanout(ctx.graph_view, bias_add)) + return false; // Input to the BiasAdd must be a Squeeze. - const auto bias_input_port = GraphView::InputPort(node, 0); + const auto bias_input_port = GraphView::InputPort(bias_add, 0); const auto squeeze = ctx.graph_view.GetRegularFanin(bias_input_port); - if (squeeze.node == nullptr) return false; - if (squeeze.node->op() != "Squeeze") return false; - if (!NodeIsOnCpu(squeeze.node)) return false; - if (!HaveSameDataType(node, squeeze.node, "T")) return false; - if (!NoControlFaninOrFanout(ctx.graph_view, squeeze.node)) return false; - if (!HasSingleFanoutNode(ctx.graph_view, squeeze.node)) return false; - if (IsInPreserveSet(ctx, squeeze.node)) return false; + + if (!squeeze.node || !IsSqueeze(*squeeze.node) || + !HaveSameDataType(bias_add, squeeze.node, "T") || + HasControlFaninOrFanout(ctx.graph_view, squeeze.node) || + !HasSingleFanoutNode(ctx.graph_view, squeeze.node) || + IsInPreserveSet(ctx, squeeze.node)) + return false; // Squeeze must not squeeze output channel dimension. std::vector dims; @@ -212,67 +317,72 @@ bool FindConv2DWithSqueezeAndBias(const RemapperContext& ctx, if (dim == 3) return false; } - // Input to the Squeeze must be a Conv2D in NHWC format. + // Input to the Squeeze must be a Conv2D. const auto squeeze_input_port = GraphView::InputPort(squeeze.node, 0); const auto conv2d = ctx.graph_view.GetRegularFanin(squeeze_input_port); - if (conv2d.node == nullptr) return false; - if (conv2d.node->op() != "Conv2D") return false; - if (conv2d.node->attr().at("data_format").s() != "NHWC") return false; - if (!NodeIsOnCpu(conv2d.node)) return false; - if (!HaveSameDataType(node, conv2d.node, "T")) return false; - if (!NoControlFaninOrFanout(ctx.graph_view, conv2d.node)) return false; - if (!HasSingleFanoutNode(ctx.graph_view, conv2d.node)) return false; - if (IsInPreserveSet(ctx, conv2d.node)) return false; + + if (!conv2d.node || !IsConv2D(*conv2d.node) || + !HaveSameDataType(bias_add, conv2d.node, "T") || + HasControlFaninOrFanout(ctx.graph_view, conv2d.node) || + !HasSingleFanoutNode(ctx.graph_view, conv2d.node) || + IsInPreserveSet(ctx, conv2d.node)) + return false; + + // Check that data type and data format are supported on assigned device. + const Conv2DWithSqueezeAndBiasAdd pattern{conv2d.node, squeeze.node, + bias_add}; + if (!IsDeviceCompatible(ctx, pattern)) return false; // We successfully found a Conv2D+Squeeze+BiasAdd pattern. - matched->conv2d = conv2d.node; - matched->squeeze = squeeze.node; - matched->bias_add = node; + *matched = pattern; return true; } -bool FindConv2DWithBatchNorm(const RemapperContext& ctx, const NodeDef* node, +bool FindConv2DWithBatchNorm(const RemapperContext& ctx, + const NodeDef* batch_norm, Conv2DWithBatchNorm* matched) { if (!EigenSupportsContractionOutputKernel()) return false; // Root of the pattern must be a FusedBatchNorm or a FusedBatchNormV2. - if (node == nullptr) return false; - if (!IsFusedBatchNorm(*node)) return false; - if (!NodeIsOnCpu(node)) return false; - if (!HasDataType(node, DT_FLOAT)) return false; + if (!batch_norm || !IsFusedBatchNorm(*batch_norm)) return false; // V2 has a separate data type for the scale/offset/mean/variance inputs. - if (node->op() == "FusedBatchNormV2" && !HasDataType(node, DT_FLOAT, "U")) + if (batch_norm->op() == "FusedBatchNormV2" && + !HasDataType(batch_norm, DT_FLOAT, "U")) return false; // Check that batch normalization is in inference mode. - const auto& attr = node->attr(); + const auto& attr = batch_norm->attr(); if (attr.count(kIsTraining) > 0 && attr.at(kIsTraining).b()) return false; // Check that only 0th output is consumed by other nodes. - if (!NoControlFaninOrFanout(ctx.graph_view, node)) return false; - if (HasFanouts(ctx.graph_view, node, 1)) return false; // batch_mean - if (HasFanouts(ctx.graph_view, node, 2)) return false; // batch_variance - if (HasFanouts(ctx.graph_view, node, 3)) return false; // reserve_space_1 - if (HasFanouts(ctx.graph_view, node, 4)) return false; // reserve_space_2 + if (HasControlFaninOrFanout(ctx.graph_view, batch_norm) || + HasFanouts(ctx.graph_view, batch_norm, 1) || // batch_mean + HasFanouts(ctx.graph_view, batch_norm, 2) || // batch_variance + HasFanouts(ctx.graph_view, batch_norm, 3) || // reserve_space_1 + HasFanouts(ctx.graph_view, batch_norm, 4)) // reserve_space_2 + return false; - // Input to the FusedBatchNorm must be a Conv2D in NHWC format. - const auto input_port = GraphView::InputPort(node, 0); + // Input to the FusedBatchNorm must be a Conv2D. + const auto input_port = GraphView::InputPort(batch_norm, 0); const auto conv2d = ctx.graph_view.GetRegularFanin(input_port); - if (conv2d.node == nullptr) return false; - if (!IsConv2D(*conv2d.node)) return false; - if (conv2d.node->attr().at(kDataFormat).s() != "NHWC") return false; - if (!NodeIsOnCpu(conv2d.node)) return false; - if (!HaveSameDataType(node, conv2d.node)) return false; - if (!NoControlFaninOrFanout(ctx.graph_view, conv2d.node)) return false; - if (!HasSingleFanoutNode(ctx.graph_view, conv2d.node)) return false; - if (IsInPreserveSet(ctx, conv2d.node)) return false; + + if (!conv2d.node || !IsConv2D(*conv2d.node) || // + !NodeIsOnCpu(conv2d.node) || // + !HaveSameDataType(batch_norm, conv2d.node) || // + !IsCpuCompatibleDataType(conv2d.node) || // + !IsCpuCompatibleDataFormat(conv2d.node) || // + HasControlFaninOrFanout(ctx.graph_view, conv2d.node) || // + !HasSingleFanoutNode(ctx.graph_view, conv2d.node) || // + IsInPreserveSet(ctx, conv2d.node)) + return false; // We successfully found a Conv2D+FusedBatchNorm pattern. matched->conv2d = conv2d.node; - matched->fused_batch_norm = node; - if (!GetNodeAttr(*node, "epsilon", &matched->epsilon).ok()) return false; + matched->fused_batch_norm = batch_norm; + if (!GetNodeAttr(*batch_norm, "epsilon", &matched->epsilon).ok()) + return false; return true; } @@ -283,21 +393,19 @@ bool FindConv2DWithBatchNormAndRelu(const RemapperContext& ctx, if (!EigenSupportsContractionOutputKernel()) return false; // Root of the pattern must be a Relu. - if (node == nullptr) return false; - if (!IsRelu(*node)) return false; - if (!NodeIsOnCpu(node)) return false; - if (!IsFloatOrDoubleDataType(node)) return false; - if (!NoControlFaninOrFanout(ctx.graph_view, node)) return false; + if (!node || !IsRelu(*node) || HasControlFaninOrFanout(ctx.graph_view, node)) + return false; // And input to Relu must match Conv2DWithBatchNorm pattern. const auto input_port = GraphView::InputPort(node, 0); const auto batch_norm = ctx.graph_view.GetRegularFanin(input_port); Conv2DWithBatchNorm base; - if (!FindConv2DWithBatchNorm(ctx, batch_norm.node, &base)) return false; - if (!HasSingleFanoutNode(ctx.graph_view, base.fused_batch_norm)) return false; - if (!HaveSameDataType(node, base.fused_batch_norm)) return false; - if (IsInPreserveSet(ctx, base.fused_batch_norm)) return false; + if (!FindConv2DWithBatchNorm(ctx, batch_norm.node, &base) || + !HasSingleFanoutNode(ctx.graph_view, base.fused_batch_norm) || + !HaveSameDataType(node, base.fused_batch_norm) || + IsInPreserveSet(ctx, base.fused_batch_norm)) + return false; // We successfully found a Conv2D+FusedBatchNorm+Relu pattern. matched->conv2d = base.conv2d; @@ -355,9 +463,7 @@ bool FindFusedBatchNorm(const RemapperContext& ctx, const NodeDef* node, return true; } -void CopyConv2DAttributes(const NodeDef* conv2d, NodeDef* fused_conv2d, - const std::vector& fused_ops = {}, - int num_args = 1, float epsilon = 0.0) { +void CopyConv2DAttributes(const NodeDef* conv2d, NodeDef* fused_conv2d) { auto* attr = fused_conv2d->mutable_attr(); auto src_attr = conv2d->attr(); @@ -367,53 +473,65 @@ void CopyConv2DAttributes(const NodeDef* conv2d, NodeDef* fused_conv2d, (*attr)["dilations"] = src_attr.at("dilations"); (*attr)["data_format"] = src_attr.at("data_format"); (*attr)["use_cudnn_on_gpu"] = src_attr.at("use_cudnn_on_gpu"); +} - auto* fused_ops_attr = (*attr)["fused_ops"].mutable_list(); - for (const string& fused_op : fused_ops) { - fused_ops_attr->add_s(fused_op); - } - +void SetFusedConv2DAttributes( + NodeDef* fused_conv2d, const absl::Span fused_ops, + int num_args = 1, float epsilon = 0.0) { + auto* attr = fused_conv2d->mutable_attr(); + SetAttrValue(fused_ops, &(*attr)["fused_ops"]); SetAttrValue(num_args, &(*attr)["num_args"]); - // Required only for FusedBatchNorm. - SetAttrValue(epsilon, &(*attr)["epsilon"]); + SetAttrValue(epsilon, &(*attr)["epsilon"]); // required only for BatchNorm } void AddFusedConv2DNode( - const Conv2DWithBiasAdd& matched, GraphDef* optimized_graph, + const RemapperContext& ctx, const Conv2DWithBiasAdd& matched, + GraphDef* optimized_graph, absl::flat_hash_set* invalidated_nodes) { - VLOG(2) << "Fuse Conv2D with BiasAdd: bias_add=" << matched.bias_add->name() + DCHECK(IsDeviceCompatible(ctx, matched)) + << "Unsupported fused Conv2D pattern"; + + VLOG(2) << "Fuse Conv2D with BiasAdd: " + << " bias_add=" << matched.bias_add->name() << " conv2d=" << matched.conv2d->name(); NodeDef* fused_conv2d = optimized_graph->add_node(); - fused_conv2d->set_name(matched.bias_add->name()); fused_conv2d->set_op(kFusedConv2D); - fused_conv2d->set_device(matched.bias_add->device()); + fused_conv2d->set_name(matched.bias_add->name()); + fused_conv2d->set_device(matched.conv2d->device()); fused_conv2d->add_input(matched.conv2d->input(0)); // 0: input fused_conv2d->add_input(matched.conv2d->input(1)); // 1: filter fused_conv2d->add_input(matched.bias_add->input(1)); // 2: bias - CopyConv2DAttributes(matched.conv2d, fused_conv2d, {"BiasAdd"}); + CopyConv2DAttributes(matched.conv2d, fused_conv2d); + SetFusedConv2DAttributes(fused_conv2d, {"BiasAdd"}); invalidated_nodes->insert(matched.bias_add); invalidated_nodes->insert(matched.conv2d); } void AddFusedConv2DNode( - const Conv2DWithBiasAddAndRelu& matched, GraphDef* optimized_graph, + const RemapperContext& ctx, const Conv2DWithBiasAddAndRelu& matched, + GraphDef* optimized_graph, absl::flat_hash_set* invalidated_nodes) { - VLOG(2) << "Fuse Conv2D with BiasAdd and Relu: relu=" << matched.relu->name() + DCHECK(IsDeviceCompatible(ctx, matched)) + << "Unsupported fused Conv2D pattern"; + + VLOG(2) << "Fuse Conv2D with BiasAdd and Relu: " + << " relu=" << matched.relu->name() << " bias_add=" << matched.bias_add->name() << " conv2d=" << matched.conv2d->name(); NodeDef* fused_conv2d = optimized_graph->add_node(); fused_conv2d->set_name(matched.relu->name()); fused_conv2d->set_op(kFusedConv2D); - fused_conv2d->set_device(matched.relu->device()); + fused_conv2d->set_device(matched.conv2d->device()); fused_conv2d->add_input(matched.conv2d->input(0)); // 0: input fused_conv2d->add_input(matched.conv2d->input(1)); // 1: filter fused_conv2d->add_input(matched.bias_add->input(1)); // 2: bias - CopyConv2DAttributes(matched.conv2d, fused_conv2d, {"BiasAdd", "Relu"}); + CopyConv2DAttributes(matched.conv2d, fused_conv2d); + SetFusedConv2DAttributes(fused_conv2d, {"BiasAdd", "Relu"}); invalidated_nodes->insert(matched.relu); invalidated_nodes->insert(matched.bias_add); @@ -421,8 +539,12 @@ void AddFusedConv2DNode( } void AddFusedConv2DNode( - const Conv2DWithSqueezeAndBiasAdd& matched, GraphDef* optimized_graph, + const RemapperContext& ctx, const Conv2DWithSqueezeAndBiasAdd& matched, + GraphDef* optimized_graph, absl::flat_hash_set* invalidated_nodes) { + DCHECK(IsDeviceCompatible(ctx, matched)) + << "Unsupported fused Conv2D pattern"; + VLOG(2) << "Fuse Conv2D with Squeeze and BiasAdd: " << " bias_add=" << matched.bias_add->name() << " squeeze=" << matched.squeeze->name() @@ -432,13 +554,14 @@ void AddFusedConv2DNode( // has single consumer (only the squeeze node). NodeDef* fused_conv2d = optimized_graph->add_node(); fused_conv2d->set_name(matched.conv2d->name()); - fused_conv2d->set_op("_FusedConv2D"); + fused_conv2d->set_op(kFusedConv2D); fused_conv2d->set_device(matched.conv2d->device()); fused_conv2d->add_input(matched.conv2d->input(0)); // 0: input fused_conv2d->add_input(matched.conv2d->input(1)); // 1: filter fused_conv2d->add_input(matched.bias_add->input(1)); // 2: bias - CopyConv2DAttributes(matched.conv2d, fused_conv2d, {"BiasAdd"}); + CopyConv2DAttributes(matched.conv2d, fused_conv2d); + SetFusedConv2DAttributes(fused_conv2d, {"BiasAdd"}); // Replace BiasAdd node with a Squeeze. NodeDef* remapped_squeeze = optimized_graph->add_node(); @@ -461,7 +584,7 @@ void AddFusedConv2DNode( NodeDef* fused_conv2d = optimized_graph->add_node(); fused_conv2d->set_name(matched.fused_batch_norm->name()); fused_conv2d->set_op(kFusedConv2D); - fused_conv2d->set_device(matched.fused_batch_norm->device()); + fused_conv2d->set_device(matched.conv2d->device()); fused_conv2d->add_input(matched.conv2d->input(0)); // 0: input fused_conv2d->add_input(matched.conv2d->input(1)); // 1: filter fused_conv2d->add_input(matched.fused_batch_norm->input(1)); // 2: scale @@ -469,8 +592,9 @@ void AddFusedConv2DNode( fused_conv2d->add_input(matched.fused_batch_norm->input(3)); // 4: mean fused_conv2d->add_input(matched.fused_batch_norm->input(4)); // 5: variance - CopyConv2DAttributes(matched.conv2d, fused_conv2d, {"FusedBatchNorm"}, - /*num_args*/ 4, /*epsilon*/ matched.epsilon); + CopyConv2DAttributes(matched.conv2d, fused_conv2d); + SetFusedConv2DAttributes(fused_conv2d, {"FusedBatchNorm"}, + /*num_args=*/4, /*epsilon=*/matched.epsilon); invalidated_nodes->insert(matched.fused_batch_norm); invalidated_nodes->insert(matched.conv2d); @@ -487,7 +611,7 @@ void AddFusedConv2DNode( NodeDef* fused_conv2d = optimized_graph->add_node(); fused_conv2d->set_name(matched.relu->name()); fused_conv2d->set_op(kFusedConv2D); - fused_conv2d->set_device(matched.fused_batch_norm->device()); + fused_conv2d->set_device(matched.conv2d->device()); fused_conv2d->add_input(matched.conv2d->input(0)); // 0: input fused_conv2d->add_input(matched.conv2d->input(1)); // 1: filter fused_conv2d->add_input(matched.fused_batch_norm->input(1)); // 2: scale @@ -495,8 +619,9 @@ void AddFusedConv2DNode( fused_conv2d->add_input(matched.fused_batch_norm->input(3)); // 4: mean fused_conv2d->add_input(matched.fused_batch_norm->input(4)); // 5: variance - CopyConv2DAttributes(matched.conv2d, fused_conv2d, {"FusedBatchNorm", "Relu"}, - /*num_args*/ 4, /*epsilon*/ matched.epsilon); + CopyConv2DAttributes(matched.conv2d, fused_conv2d); + SetFusedConv2DAttributes(fused_conv2d, {"FusedBatchNorm", "Relu"}, + /*num_args=*/4, /*epsilon=*/matched.epsilon); invalidated_nodes->insert(matched.relu); invalidated_nodes->insert(matched.fused_batch_norm); @@ -680,13 +805,14 @@ Status Remapper::Optimize(Cluster* /*cluster*/, const GrapplerItem& item, // Remap Conv2D+BiasAdd into the _FusedConv2D. if (FindConv2DWithBias(ctx, &node, &conv2d_with_bias)) { - AddFusedConv2DNode(conv2d_with_bias, optimized_graph, &invalidated_nodes); + AddFusedConv2DNode(ctx, conv2d_with_bias, optimized_graph, + &invalidated_nodes); continue; } // Remap Conv2D+BiasAdd+Relu into the _FusedConv2D. if (FindConv2DWithBiasAndRelu(ctx, &node, &conv2d_with_bias_and_relu)) { - AddFusedConv2DNode(conv2d_with_bias_and_relu, optimized_graph, + AddFusedConv2DNode(ctx, conv2d_with_bias_and_relu, optimized_graph, &invalidated_nodes); continue; } @@ -694,7 +820,7 @@ Status Remapper::Optimize(Cluster* /*cluster*/, const GrapplerItem& item, // Remap Conv2D+Squeeze+BiasAdd into the _FusedConv2D+Squeeze. if (FindConv2DWithSqueezeAndBias(ctx, &node, &conv2d_with_squeeze_and_bias)) { - AddFusedConv2DNode(conv2d_with_squeeze_and_bias, optimized_graph, + AddFusedConv2DNode(ctx, conv2d_with_squeeze_and_bias, optimized_graph, &invalidated_nodes); continue; } diff --git a/tensorflow/core/grappler/utils.cc b/tensorflow/core/grappler/utils.cc index cc9a38b2d2..375c3e56c8 100644 --- a/tensorflow/core/grappler/utils.cc +++ b/tensorflow/core/grappler/utils.cc @@ -20,6 +20,8 @@ limitations under the License. #include #include +#include "absl/strings/match.h" +#include "absl/strings/str_cat.h" #include "tensorflow/core/framework/attr_value.pb.h" #include "tensorflow/core/framework/function.h" #include "tensorflow/core/framework/node_def_util.h" @@ -166,10 +168,10 @@ string AddPrefixToNodeName(const string& name, const string& prefix, const string& delimiter) { if (!name.empty()) { if (name[0] == '^') { - return strings::StrCat("^", prefix, delimiter, name.substr(1)); + return absl::StrCat("^", prefix, delimiter, name.substr(1)); } } - return strings::StrCat(prefix, delimiter, name); + return absl::StrCat(prefix, delimiter, name); } string AddPrefixToNodeName(const string& name, const string& prefix) { @@ -193,20 +195,26 @@ bool ExecuteWithTimeout(std::function fn, const int64 timeout_in_ms, } string AsControlDependency(const NodeDef& node) { - return strings::StrCat("^", node.name()); + return absl::StrCat("^", node.name()); } string AsControlDependency(const string& node_name) { CHECK(!node_name.empty()); return (!node_name.empty() && node_name[0] == '^') ? node_name - : strings::StrCat("^", node_name); + : absl::StrCat("^", node_name); } bool NodeIsOnCpu(const NodeDef* node) { string task, device; return DeviceNameUtils::SplitDeviceName(node->device(), &task, &device) && - str_util::StartsWith(device, DEVICE_CPU); + absl::StartsWith(device, DEVICE_CPU); +} + +bool NodeIsOnGpu(const NodeDef* node) { + string task, device; + return DeviceNameUtils::SplitDeviceName(node->device(), &task, &device) && + absl::StartsWith(device, DEVICE_GPU); } int NumOutputs(const NodeDef& node, GraphDef* graph) { diff --git a/tensorflow/core/grappler/utils.h b/tensorflow/core/grappler/utils.h index 1e820977ae..9053ae4c07 100644 --- a/tensorflow/core/grappler/utils.h +++ b/tensorflow/core/grappler/utils.h @@ -242,6 +242,9 @@ string AsControlDependency(const string& node); // Returns true if the node is assigned to run on CPU device. bool NodeIsOnCpu(const NodeDef* node); +// Returns true if the node is assigned to run on GPU device. +bool NodeIsOnGpu(const NodeDef* node); + // Returns the number of outputs of a node according to its OpDef. Note that // some of the outputs may be unconnected. int NumOutputs(const NodeDef& node, GraphDef* graph); diff --git a/tensorflow/core/grappler/utils/BUILD b/tensorflow/core/grappler/utils/BUILD index 89417f85c2..ff15541cdc 100644 --- a/tensorflow/core/grappler/utils/BUILD +++ b/tensorflow/core/grappler/utils/BUILD @@ -143,6 +143,7 @@ cc_library( "//tensorflow/core:test", "//tensorflow/core/grappler:grappler_item", "//tensorflow/core/grappler:utils", + "@com_google_absl//absl/algorithm:container", ], ) diff --git a/tensorflow/core/grappler/utils/grappler_test.cc b/tensorflow/core/grappler/utils/grappler_test.cc index 576494cad5..5ef1cf444b 100644 --- a/tensorflow/core/grappler/utils/grappler_test.cc +++ b/tensorflow/core/grappler/utils/grappler_test.cc @@ -15,6 +15,8 @@ limitations under the License. #include "tensorflow/core/grappler/utils/grappler_test.h" #include +#include "absl/algorithm/container.h" +#include "tensorflow/core/framework/attr_value_util.h" #include "tensorflow/core/grappler/utils.h" #include "tensorflow/core/lib/core/status.h" #include "tensorflow/core/protobuf/rewriter_config.pb.h" @@ -125,6 +127,31 @@ void GrapplerTest::CompareGraphs(GraphDef want, GraphDef got) const { } } +void GrapplerTest::CompareNodes(const NodeDef& want, const NodeDef& got) const { + EXPECT_EQ(want.name(), got.name()); + EXPECT_EQ(want.op(), got.op()); + + std::vector want_inputs(want.input().begin(), want.input().end()); + std::vector got_inputs(got.input().begin(), got.input().end()); + EXPECT_EQ(want_inputs, got_inputs); + + const auto attr_name = [](const std::pair& attr) { + return attr.first; + }; + + std::vector want_attrs; + std::vector got_attrs; + absl::c_transform(want.attr(), std::back_inserter(want_attrs), attr_name); + absl::c_transform(got.attr(), std::back_inserter(got_attrs), attr_name); + absl::c_sort(want_attrs); + absl::c_sort(got_attrs); + EXPECT_EQ(want_attrs, got_attrs); + + for (const string& attr : want_attrs) { + EXPECT_TRUE(AreAttrValuesEqual(want.attr().at(attr), got.attr().at(attr))); + } +} + bool GrapplerTest::IsNodesDirectlyConnected(const NodeMap& node_map, const string& src, const string& dst, int position) { diff --git a/tensorflow/core/grappler/utils/grappler_test.h b/tensorflow/core/grappler/utils/grappler_test.h index 0cfd740dcb..20fd04c1c6 100644 --- a/tensorflow/core/grappler/utils/grappler_test.h +++ b/tensorflow/core/grappler/utils/grappler_test.h @@ -49,13 +49,25 @@ class GrapplerTest : public ::testing::Test { const std::vector>& attributes, GraphDef* graph) const; + // Checks if two graphs are equal. Both graphs must have the same set of nodes + // with the same inputs and attributes. Nodes can be in different order. + // + // NOTE: This function uses EXPECT/ASSERT macros to check node properties + // equality, and adds all failuires to the current test. void CompareGraphs(GraphDef want, GraphDef got) const; - // Check if node 'src' is directly connected to the input($position) of 'dst'. + // Checks if two nodes have the same name, op, inputs and attributes. + // + // NOTE: This function uses EXPECT/ASSERT macros to check node properties + // equality, and adds all failuires to the current test. + void CompareNodes(const NodeDef& want, const NodeDef& got) const; + + // Checks if node 'src' is directly connected to the input($position) of + // 'dst'. bool IsNodesDirectlyConnected(const NodeMap& node_map, const string& src, const string& dst, int position = 0); - // Count nodes of the given op-type in a graph. + // Counts nodes of the given op-type in a graph. int CountOpNodes(const GraphDef& graph, const string& op); // Get a random tensor with given shape. diff --git a/tensorflow/core/kernels/conv_ops_test.cc b/tensorflow/core/kernels/conv_ops_test.cc index 3e59219f8f..fc93915e16 100644 --- a/tensorflow/core/kernels/conv_ops_test.cc +++ b/tensorflow/core/kernels/conv_ops_test.cc @@ -1497,6 +1497,26 @@ BM_FusedConv2DWithBatchNormAndRelu(32, 32, 32, 128, 3, 3, 1024, cpu, "3x3 /b 32"); #if GOOGLE_CUDA +// -------------------------------------------------------------------------- // +// 1x1 Convolution +// -------------------------------------------------------------------------- // + +BM_Conv2D(8, 32, 32, 128, 1, 1, 1024, gpu, "1x1 /b 8"); +BM_Conv2D(16, 32, 32, 128, 1, 1, 1024, gpu, "1x1 /b 16"); +BM_Conv2D(32, 32, 32, 128, 1, 1, 1024, gpu, "1x1 /b 32"); + +BM_Conv2DWithBiasAndRelu(8, 32, 32, 128, 1, 1, 1024, gpu, "1x1 /b 8"); +BM_Conv2DWithBiasAndRelu(16, 32, 32, 128, 1, 1, 1024, gpu, "1x1 /b 16"); +BM_Conv2DWithBiasAndRelu(32, 32, 32, 128, 1, 1, 1024, gpu, "1x1 /b 32"); + +BM_FusedConv2DWithBiasAndRelu(8, 32, 32, 128, 1, 1, 1024, gpu, "1x1 /b 8"); +BM_FusedConv2DWithBiasAndRelu(16, 32, 32, 128, 1, 1, 1024, gpu, "1x1 /b 16"); +BM_FusedConv2DWithBiasAndRelu(32, 32, 32, 128, 1, 1, 1024, gpu, "1x1 /b 32"); + +// -------------------------------------------------------------------------- // +// 3x3 Convolution +// -------------------------------------------------------------------------- // + BM_Conv2D(8, 32, 32, 128, 3, 3, 1024, gpu, "3x3 /b 8"); BM_Conv2D(16, 32, 32, 128, 3, 3, 1024, gpu, "3x3 /b 16"); BM_Conv2D(32, 32, 32, 128, 3, 3, 1024, gpu, "3x3 /b 32"); -- GitLab From c7c4a42c4372ca560ea415fe3a798e18286cedec Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Mon, 7 Jan 2019 12:36:38 -0800 Subject: [PATCH 428/622] Fix an error in keras input_layer.Input() dtype type checking. PiperOrigin-RevId: 228215444 --- tensorflow/python/keras/engine/input_layer.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tensorflow/python/keras/engine/input_layer.py b/tensorflow/python/keras/engine/input_layer.py index bc2cf2fb6e..c6dcedfce2 100644 --- a/tensorflow/python/keras/engine/input_layer.py +++ b/tensorflow/python/keras/engine/input_layer.py @@ -77,8 +77,9 @@ class InputLayer(base_layer.Layer): dtype = backend.floatx() else: dtype = backend.dtype(input_tensor) - elif input_tensor and input_tensor.dtype != dtype: - raise ValueError('`input_tensor.dtype` differs from `dtype`.') + elif input_tensor is not None and input_tensor.dtype != dtype: + raise ValueError('`input_tensor.dtype` differs from `dtype`: %s vs. %s' % + (input_tensor.dtype, dtype)) super(InputLayer, self).__init__(dtype=dtype, name=name) self.built = True self.sparse = sparse -- GitLab From e7f7f01352be4054a7fb2a872cf2ef335292600c Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Mon, 7 Jan 2019 12:48:37 -0800 Subject: [PATCH 429/622] Prevent the interpreter from continuing execution after an error occurred. PiperOrigin-RevId: 228217675 --- tensorflow/lite/core/subgraph.cc | 6 +++--- tensorflow/lite/interpreter.cc | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/tensorflow/lite/core/subgraph.cc b/tensorflow/lite/core/subgraph.cc index 75212cac22..4be80d143e 100644 --- a/tensorflow/lite/core/subgraph.cc +++ b/tensorflow/lite/core/subgraph.cc @@ -670,7 +670,7 @@ TfLiteStatus Subgraph::Invoke() { TfLiteTensor* tensor = &tensors_[tensor_index]; if (tensor->delegate && tensor->delegate != node.delegate && tensor->data_is_stale) { - EnsureTensorDataIsReadable(tensor_index); + TF_LITE_ENSURE_STATUS(EnsureTensorDataIsReadable(tensor_index)); } } @@ -683,8 +683,8 @@ TfLiteStatus Subgraph::Invoke() { EnsureTensorsVectorCapacity(); tensor_resized_since_op_invoke_ = false; if (OpInvoke(registration, &node) == kTfLiteError) { - status = ReportOpError(context_, node, registration, node_index, - "failed to invoke"); + return ReportOpError(context_, node, registration, node_index, + "failed to invoke"); } // Force execution prep for downstream ops if the latest op triggered the diff --git a/tensorflow/lite/interpreter.cc b/tensorflow/lite/interpreter.cc index 2abe062ec6..60fa2130fa 100644 --- a/tensorflow/lite/interpreter.cc +++ b/tensorflow/lite/interpreter.cc @@ -102,15 +102,16 @@ TfLiteStatus Interpreter::ResizeInputTensor(int tensor_index, } TfLiteStatus Interpreter::Invoke() { - TfLiteStatus status = primary_subgraph().Invoke(); + TF_LITE_ENSURE_STATUS(primary_subgraph().Invoke()); if (!allow_buffer_handle_output_) { for (int tensor_index : outputs()) { - primary_subgraph().EnsureTensorDataIsReadable(tensor_index); + TF_LITE_ENSURE_STATUS( + primary_subgraph().EnsureTensorDataIsReadable(tensor_index)); } } - return status; + return kTfLiteOk; } TfLiteStatus Interpreter::AddTensors(int tensors_to_add, -- GitLab From 95d708d7b9e3f22c09ca77004e68f2cadb295226 Mon Sep 17 00:00:00 2001 From: Shanqing Cai Date: Mon, 7 Jan 2019 13:12:01 -0800 Subject: [PATCH 430/622] tf.keras: Remove unnecessary backend check in PReLU.call() PiperOrigin-RevId: 228222042 --- tensorflow/python/keras/layers/advanced_activations.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/tensorflow/python/keras/layers/advanced_activations.py b/tensorflow/python/keras/layers/advanced_activations.py index be1039a2ac..5095287430 100644 --- a/tensorflow/python/keras/layers/advanced_activations.py +++ b/tensorflow/python/keras/layers/advanced_activations.py @@ -121,11 +121,9 @@ class PReLU(Layer): @tf_utils.shape_type_conversion def build(self, input_shape): param_shape = list(input_shape[1:]) - self.param_broadcast = [False] * len(param_shape) if self.shared_axes is not None: for i in self.shared_axes: param_shape[i - 1] = 1 - self.param_broadcast[i - 1] = True self.alpha = self.add_weight( shape=param_shape, name='alpha', @@ -143,12 +141,7 @@ class PReLU(Layer): def call(self, inputs, mask=None): pos = K.relu(inputs) - if K.backend() == 'theano': - neg = ( - K.pattern_broadcast(self.alpha, self.param_broadcast) * - (inputs - math_ops.abs(inputs)) * 0.5) - else: - neg = -self.alpha * K.relu(-inputs) + neg = -self.alpha * K.relu(-inputs) return pos + neg def get_config(self): -- GitLab From 38491a84a9e38357e400457dbbe408b66e786672 Mon Sep 17 00:00:00 2001 From: Pooya Davoodi Date: Mon, 7 Jan 2019 14:21:21 -0800 Subject: [PATCH 431/622] TFTRT: Expand lambda and inline ifs to functions with if and else --- .../contrib/tensorrt/python/trt_convert.py | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/tensorflow/contrib/tensorrt/python/trt_convert.py b/tensorflow/contrib/tensorrt/python/trt_convert.py index 3d2b6b499d..f5d624ef64 100644 --- a/tensorflow/contrib/tensorrt/python/trt_convert.py +++ b/tensorflow/contrib/tensorrt/python/trt_convert.py @@ -45,15 +45,33 @@ from tensorflow.python.saved_model import loader_impl from tensorflow.python.saved_model import tag_constants from tensorflow.python.training import saver -if _six.PY2: - _to_bytes = lambda s: s.encode("utf-8", errors="surrogateescape") \ - if isinstance(s, unicode) else s - _to_string = lambda s: s.decode("utf-8") if isinstance(s, str) else s -else: - _to_bytes = lambda s: s.encode("utf-8", errors="surrogateescape") \ - if isinstance(s, str) else s - _to_string = lambda s: s.decode("utf-8") if isinstance(s, bytes) else s - +def _to_bytes(s): + """Returns encoded of s if s is a sequence of chars otherwise returns s. + """ + if _six.PY2: + if isinstance(s, unicode): + return s.encode("utf-8", errors="surrogateescape") + else: + return s + else: + if isinstance(s, str): + return s.encode("utf-8", errors="surrogateescape") + else: + return s + +def _to_string(s): + """Returns decoded of s if s is a sequence of bytes otherwise returns s. + """ + if _six.PY2: + if isinstance(s, str): + return s.decode("utf-8") + else: + return s + else: + if isinstance(s, bytes): + return s.decode("utf-8") + else: + return s class TrtPrecisionMode(object): FP32 = "FP32" -- GitLab From 46f030b6e275bd9950da91e480bda5f2fa44ad80 Mon Sep 17 00:00:00 2001 From: Michael Case Date: Mon, 7 Jan 2019 13:26:16 -0800 Subject: [PATCH 432/622] Clean up example/learn/BUILD a bit Some of the data deps are targets that no longer exist. Pretty sure should be cleaned up. PiperOrigin-RevId: 228224240 --- tensorflow/examples/learn/BUILD | 9 --------- 1 file changed, 9 deletions(-) diff --git a/tensorflow/examples/learn/BUILD b/tensorflow/examples/learn/BUILD index d6ec1f393b..a22d55e5af 100644 --- a/tensorflow/examples/learn/BUILD +++ b/tensorflow/examples/learn/BUILD @@ -28,17 +28,8 @@ sh_test( size = "large", srcs = ["examples_test.sh"], data = [ - ":boston", - ":iris", ":iris_custom_decay_dnn", ":iris_custom_model", - ":iris_run_config", - ":random_forest_mnist", - ":resnet", - ":text_classification", - ":text_classification_character_cnn", - ":text_classification_character_rnn", - ":text_classification_cnn", ], tags = [ "manual", -- GitLab From d2dd3bd43ebbfd2d14f2f7e658b5b5aae201607e Mon Sep 17 00:00:00 2001 From: Skye Wanderman-Milne Date: Mon, 7 Jan 2019 13:35:32 -0800 Subject: [PATCH 433/622] while_v2 fixes for better TPU integration. PiperOrigin-RevId: 228226029 --- tensorflow/python/ops/while_v2.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tensorflow/python/ops/while_v2.py b/tensorflow/python/ops/while_v2.py index aaed86c1ca..f9fea0e5bd 100644 --- a/tensorflow/python/ops/while_v2.py +++ b/tensorflow/python/ops/while_v2.py @@ -115,12 +115,15 @@ def while_loop(cond, loop_counter < maximum_iterations, cond(*_pack_sequence_as(orig_loop_vars, args))) + # NOTE(skyewm): we set read_only_collections=False for compatibility with + # TPUEstimator. cond_graph = func_graph_module.func_graph_from_py_func( cond_name, wrapped_cond, loop_vars, {}, signature=_build_signature(loop_vars, shape_invariants), - func_graph=util.WhileCondFuncGraph(cond_name), + func_graph=util.WhileCondFuncGraph(cond_name, + read_only_collections=False), add_control_dependencies=add_control_dependencies) # Add external_captures of cond to the list of loop vars. @@ -171,7 +174,8 @@ def while_loop(cond, wrapped_body, loop_vars, {}, signature=_build_signature(loop_vars, shape_invariants), - func_graph=util.WhileBodyFuncGraph(body_name), + func_graph=util.WhileBodyFuncGraph(body_name, + read_only_collections=False), add_control_dependencies=add_control_dependencies) # Add external captures of body to the list of loop vars. # Note that external tensors will be treated as loop invariants, i.e., -- GitLab From 879af3c38d2405db3ff4c96d6104cdf82a1ab368 Mon Sep 17 00:00:00 2001 From: Frank Chen Date: Mon, 7 Jan 2019 13:38:36 -0800 Subject: [PATCH 434/622] Change dataset rewriting in distribution strategy to avoid a unbatch().batch() call. PiperOrigin-RevId: 228226620 --- .../python/data/experimental/ops/batching.py | 8 ++++++-- tensorflow/python/distribute/input_lib.py | 20 ++++++++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/tensorflow/python/data/experimental/ops/batching.py b/tensorflow/python/data/experimental/ops/batching.py index f0cf7f0a99..ff8a9182c3 100644 --- a/tensorflow/python/data/experimental/ops/batching.py +++ b/tensorflow/python/data/experimental/ops/batching.py @@ -553,8 +553,12 @@ class _MapAndBatchDataset(dataset_ops.UnaryDataset): """See `Dataset.map()` for details.""" self._input_dataset = input_dataset - self._map_func = dataset_ops.StructuredFunctionWrapper( - map_func, "tf.data.experimental.map_and_batch()", dataset=input_dataset) + if isinstance(map_func, dataset_ops.StructuredFunctionWrapper): + self._map_func = map_func + else: + self._map_func = dataset_ops.StructuredFunctionWrapper( + map_func, "tf.data.experimental.map_and_batch()", + dataset=input_dataset) self._batch_size_t = ops.convert_to_tensor( batch_size, dtype=dtypes.int64, name="batch_size") self._num_parallel_calls_t = ops.convert_to_tensor( diff --git a/tensorflow/python/distribute/input_lib.py b/tensorflow/python/distribute/input_lib.py index cbe6518e5c..022595146d 100644 --- a/tensorflow/python/distribute/input_lib.py +++ b/tensorflow/python/distribute/input_lib.py @@ -574,12 +574,19 @@ def _split_dataset_batch(dataset, split_batch_by): "The batch operations can be followed by a prefetch.") batched_dataset = _get_batch_dataset(dataset) + prev_dataset = batched_dataset._input_dataset + + num_parallel_calls = None + map_func = None + if isinstance(batched_dataset, dataset_ops.BatchDataset): batch_size = batched_dataset._batch_size drop_remainder = batched_dataset._drop_remainder elif isinstance(batched_dataset, batching._MapAndBatchDataset): batch_size = batched_dataset._batch_size_t drop_remainder = batched_dataset._drop_remainder_t + num_parallel_calls = batched_dataset._num_parallel_calls_t + map_func = batched_dataset._map_func prefetch_buffer = None if isinstance(dataset, dataset_ops.PrefetchDataset): @@ -587,7 +594,6 @@ def _split_dataset_batch(dataset, split_batch_by): elif (isinstance(dataset, dataset_ops.DatasetV1Adapter) and isinstance(dataset._dataset, dataset_ops.PrefetchDataset)): prefetch_buffer = dataset._dataset._buffer_size - # pylint: enable=protected-access if tensor_util.is_tensor(batch_size): batch_size = tensor_util.constant_value(batch_size) @@ -595,14 +601,22 @@ def _split_dataset_batch(dataset, split_batch_by): if tensor_util.is_tensor(drop_remainder): drop_remainder = tensor_util.constant_value(drop_remainder) + if num_parallel_calls is not None and tensor_util.is_tensor(drop_remainder): + num_parallel_calls = tensor_util.constant_value(num_parallel_calls) + if batch_size % split_batch_by: raise ValueError( "Batch size %s cannot be sharded evenly across replicas %s" % ( batch_size, split_batch_by)) new_batch_size = batch_size // split_batch_by - dataset = dataset.apply(batching.unbatch()) - dataset = dataset.batch(new_batch_size, drop_remainder=drop_remainder) + if isinstance(batched_dataset, dataset_ops.BatchDataset): + dataset = prev_dataset.batch(new_batch_size, drop_remainder=drop_remainder) + elif isinstance(batched_dataset, batching._MapAndBatchDataset): + dataset = prev_dataset.apply(batching.map_and_batch( + map_func, new_batch_size, num_parallel_calls, drop_remainder)) + # pylint: enable=protected-access + if prefetch_buffer is not None: dataset = dataset.prefetch(prefetch_buffer) return dataset -- GitLab From 7bc843afd6696fc802fa44f65f0fdf67b0bc56e6 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Mon, 7 Jan 2019 13:45:54 -0800 Subject: [PATCH 435/622] Unittests to demonstrate three bugs in flex mode: - forwardability is a property of a subgraph, not of the global buffer map - input tensors should never be forwardable, even when they come from another subgraph. That's because they might be outputs of the model, or inputs into another subgraph. - input tensors shouldn't be removed at the end, because they might be used by another subgraph. PiperOrigin-RevId: 228228094 --- tensorflow/lite/delegates/flex/kernel_test.cc | 175 +++++++++++++++++- tensorflow/lite/delegates/flex/test_util.cc | 5 + tensorflow/lite/delegates/flex/test_util.h | 3 + 3 files changed, 179 insertions(+), 4 deletions(-) diff --git a/tensorflow/lite/delegates/flex/kernel_test.cc b/tensorflow/lite/delegates/flex/kernel_test.cc index 647c199d4f..9de3ca67de 100644 --- a/tensorflow/lite/delegates/flex/kernel_test.cc +++ b/tensorflow/lite/delegates/flex/kernel_test.cc @@ -36,13 +36,38 @@ TfLiteStatus GenericPrepare(TfLiteContext* context, TfLiteDelegate* delegate, return kTfLiteOk; } +// There is no easy way to pass a parameter into the TfLiteDelegate's +// 'prepare' function, so we keep a global map for testing purpused. +// To avoid collisions use: GetPrepareFunction<__LINE__>(). +std::map>* GetGlobalOpLists() { + static auto* op_list = new std::map>; + return op_list; +} + class KernelTest : public testing::FlexModelTest { public: + static constexpr int kOnes = 1; // This is the index of a tensor of 1's. + static constexpr int kTwos = 2; // This is the index of a tensor of 2's. + static constexpr int kMaxTensors = 30; + + static void SetUpTestSuite() { GetGlobalOpLists()->clear(); } + KernelTest() { CHECK(delegate_data_.Prepare(tensorflow::SessionOptions{}).ok()); interpreter_.reset(new Interpreter(&error_reporter_)); } + typedef TfLiteStatus (*PrepareFunction)(TfLiteContext* context, + TfLiteDelegate* delegate); + + template + PrepareFunction GetPrepareFunction() { + GetGlobalOpLists()->insert({KEY, tf_ops_}); + return [](TfLiteContext* context, TfLiteDelegate* delegate) { + return GenericPrepare(context, delegate, GetGlobalOpLists()->at(KEY)); + }; + } + template void ConfigureDelegate(T prepare_function) { delegate_.data_ = &delegate_data_; @@ -54,9 +79,13 @@ class KernelTest : public testing::FlexModelTest { TfLiteBufferHandle buffer_handle, TfLiteTensor* output) { auto* delegate_data = reinterpret_cast(delegate->data_); - tensorflow::StringPiece values = delegate_data->GetBufferMap(context) - ->GetTensor(buffer_handle) - .tensor_data(); + auto* buffer_map = delegate_data->GetBufferMap(context); + if (!buffer_map->HasTensor(buffer_handle)) { + context->ReportError(context, "Tensor '%d' not found", buffer_handle); + return kTfLiteError; + } + tensorflow::StringPiece values = + buffer_map->GetTensor(buffer_handle).tensor_data(); memcpy(output->data.raw, values.data(), values.size()); return kTfLiteOk; }; @@ -225,7 +254,7 @@ TEST_F(KernelTest, SplitGraph) { AddTfOp(testing::kAdd, {9, 16}, {17}); // => 16 ConfigureDelegate([](TfLiteContext* context, TfLiteDelegate* delegate) { - // All ops by #3 are TF ops, handled by the delegate. However, because #4 + // All ops but #3 are TF ops, handled by the delegate. However, because #4 // depends on the non-TF op, two subgraphs are necessary: // TF subgraph 1: 0, 1, 2, 6, 7, 8, 9 // TF Lite Op: 3 @@ -260,6 +289,144 @@ TEST_F(KernelTest, SplitGraph) { ASSERT_THAT(GetValues(17), ElementsAre(18.0f)); } +class MultipleSubgraphsTest : public KernelTest { + public: + static constexpr int kInput = 0; + + void PrepareInterpreter(PrepareFunction prepare, + const std::vector& input) { + ConfigureDelegate(prepare); + + SetShape(kOnes, {3}); + SetValues(kOnes, {1.0f, 1.0f, 1.0f}); + SetShape(kTwos, {3}); + SetValues(kTwos, {2.0f, 2.0f, 2.0f}); + + SetValues(kInput, input); + } + + std::vector Apply(const std::vector& input, + std::function function) { + std::vector result; + for (float f : input) { + result.push_back(function(f)); + } + return result; + } +}; + +TEST_F(MultipleSubgraphsTest, ForwardabilityIsLocal) { + AddTensors(kMaxTensors, {kInput, kOnes, kTwos}, {12}, kTfLiteFloat32, {3}); + + // Only TF tensors can be forwarded, so we build a small first graph + // to produce tensor #10. Here #10 is forwardable, because it is only + // used once, as an output. + AddTfOp(testing::kAdd, {0, kOnes}, {3}); + AddTfOp(testing::kAdd, {0, kOnes}, {10}); + + // The second TF graph, separated from the former by a TF Lite + // multiplication, will consume tensor #10, which is not forwardable here + // since it is used by more than one op. The existing code will forward the + // tensor anyway, because it was deemed to be forwardable by the previous + // subgraph. + AddTfLiteMulOp({3, kTwos}, {4}); + AddTfOp(testing::kAdd, {10, 4}, {11}); + AddTfOp(testing::kAdd, {11, 10}, {7}); + + // And a simple TF Lite op trying to access tensor #10, which was removed + // from the buffer map. It will cause Invoke() to fail. + AddTfLiteMulOp({10, 7}, {12}); + + auto input = {3.0f, 4.0f, 5.0f}; + PrepareInterpreter(GetPrepareFunction<__LINE__>(), input); + + ASSERT_FALSE(Invoke()); + ASSERT_THAT(error_reporter().error_messages(), + ContainsRegex("Cannot read from invalid tensor index 10")); + + // TODO(b/122457581): expected result for this test: + // ASSERT_THAT(GetValues(12), ElementsAreArray(Apply(input, [](float in) { + // return (4 * in + 4) * (in + 1); + // }))); +} + +// Subgraphs should not remove input tensors from the buffer_map, since +// they could be necessary for downstream graphs. +TEST_F(MultipleSubgraphsTest, DoNotRemoveInputTensors) { + AddTensors(kMaxTensors, {kInput, kOnes, kTwos}, {12}, kTfLiteFloat32, {3}); + + // Only TF tensors can be removed, so we build a small first graph + // to produce tensor #10. We make sure it is used by more than one + // op, so it is not forwardable here. + AddTfOp(testing::kAdd, {0, kOnes}, {3}); + AddTfOp(testing::kAdd, {0, kOnes}, {10}); + AddTfOp(testing::kAdd, {10, kOnes}, {15}); + AddTfOp(testing::kAdd, {10, kOnes}, {16}); + + // The second TF graph, separated from the former by a TF Lite + // multiplication, will consume tensor #10. The existing code will remove + // from the buffer_map all tensors that are not outputs, so #10 will + // disappear. Note that we are using #10 in two ops, so it is not forwardable + // either. + AddTfLiteMulOp({3, kTwos}, {4}); + AddTfOp(testing::kAdd, {10, 4}, {11}); + AddTfOp(testing::kAdd, {10, 11}, {7}); + + // And a simple TF Lite op trying to access tensor #10, which was removed + // from the buffer map. It will cause Invoke() to fail. + AddTfLiteMulOp({10, 7}, {12}); + + auto input = {3.0f, 4.0f, 5.0f}; + PrepareInterpreter(GetPrepareFunction<__LINE__>(), input); + + ASSERT_FALSE(Invoke()); + ASSERT_THAT(error_reporter().error_messages(), + ContainsRegex("Tensor '10' not found")); + + // TODO(b/122457581): expected result for this test: + // ASSERT_THAT(GetValues(12), ElementsAreArray(Apply(input, [](float in) { + // return (4 * in + 4) * (in + 1); + // }))); +} + +// A tensor is deemed forwardable but it happens to be the input to +// more than one subgraph. It should not be forwarded, otherwise its +// contents will be overwritten. +TEST_F(MultipleSubgraphsTest, DoNotForwardInputTensors) { + AddTensors(kMaxTensors, {kInput, kOnes, kTwos}, {12}, kTfLiteFloat32, {3}); + + // Only TF tensors can be forwarded, so we build a small first graph + // to produce tensor #10. + AddTfOp(testing::kAdd, {0, kOnes}, {3}); + AddTfOp(testing::kAdd, {0, kOnes}, {10}); + + // The second TF graph, separated from the former by a TF Lite + // multiplication, will consume tensor #10 and will think it is forwardable + // because it is used by a single op. However, the subgraph doesn't have + // enough information to make that judgment, as the input tensor could be + // used by another graph further downstream. The existing code will forward + // the tensor and remove it from the buffer_map, causing a failure later. + AddTfLiteMulOp({3, kTwos}, {4}); + AddTfOp(testing::kAdd, {10, 4}, {11}); + AddTfOp(testing::kAdd, {11, 4}, {7}); + + // And a simple TF Lite op trying to access tensor #10, which was removed + // from the buffer map. It will cause Invoke() to fail. + AddTfLiteMulOp({10, 7}, {12}); + + auto input = {3.0f, 4.0f, 5.0f}; + PrepareInterpreter(GetPrepareFunction<__LINE__>(), input); + + ASSERT_FALSE(Invoke()); + ASSERT_THAT(error_reporter().error_messages(), + ContainsRegex("Tensor '10' not found")); + + // TODO(b/122457581): expected result for this test: + // ASSERT_THAT(GetValues(12), ElementsAreArray(Apply(input, [](float in) { + // return (5 * in + 5) * (in + 1); + // }))); +} + } // namespace } // namespace flex } // namespace tflite diff --git a/tensorflow/lite/delegates/flex/test_util.cc b/tensorflow/lite/delegates/flex/test_util.cc index aa24675a7b..a67aeef231 100644 --- a/tensorflow/lite/delegates/flex/test_util.cc +++ b/tensorflow/lite/delegates/flex/test_util.cc @@ -90,6 +90,8 @@ void FlexModelTest::AddTensors(int num_tensors, const std::vector& inputs, void FlexModelTest::AddTfLiteMulOp(const std::vector& inputs, const std::vector& outputs) { + ++next_op_index_; + static TfLiteRegistration reg = {nullptr, nullptr, nullptr, nullptr}; reg.builtin_code = BuiltinOperator_MUL; reg.prepare = [](TfLiteContext* context, TfLiteNode* node) { @@ -114,6 +116,9 @@ void FlexModelTest::AddTfLiteMulOp(const std::vector& inputs, void FlexModelTest::AddTfOp(TfOpType op, const std::vector& inputs, const std::vector& outputs) { + tf_ops_.push_back(next_op_index_); + ++next_op_index_; + auto attr = [](const string& key, const string& value) { return " attr{ key: '" + key + "' value {" + value + "}}"; }; diff --git a/tensorflow/lite/delegates/flex/test_util.h b/tensorflow/lite/delegates/flex/test_util.h index 2cc2dc30e9..1913a406e8 100644 --- a/tensorflow/lite/delegates/flex/test_util.h +++ b/tensorflow/lite/delegates/flex/test_util.h @@ -103,6 +103,7 @@ class FlexModelTest : public ::testing::Test { protected: std::unique_ptr interpreter_; TestErrorReporter error_reporter_; + std::vector tf_ops_; private: // Helper method to add a TensorFlow op. tflite_names needs to start with @@ -112,6 +113,8 @@ class FlexModelTest : public ::testing::Test { const std::vector& outputs); std::vector> flexbuffers_; + + int next_op_index_ = 0; }; } // namespace testing -- GitLab From 2d8379badceae7beae681eb2321ed575ba8ec718 Mon Sep 17 00:00:00 2001 From: Sanjoy Das Date: Mon, 7 Jan 2019 14:17:08 -0800 Subject: [PATCH 436/622] [TF:XLA] Bump open source llvm revision to r350438 PiperOrigin-RevId: 228234219 --- tensorflow/workspace.bzl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tensorflow/workspace.bzl b/tensorflow/workspace.bzl index b43312abb2..834ec28447 100755 --- a/tensorflow/workspace.bzl +++ b/tensorflow/workspace.bzl @@ -500,11 +500,11 @@ def tf_workspace(path_prefix = "", tf_repo_name = ""): tf_http_archive( name = "llvm", build_file = clean_dep("//third_party/llvm:llvm.autogenerated.BUILD"), - sha256 = "3a1c8a817d2d9a85b85e1ad776a90faa51f475bb924fbf70383892bb1faceac9", - strip_prefix = "llvm-6da47bee848304d215e3ca08b2a4a4dc9c954310", + sha256 = "20c0ebd54f433d68652a54e328d70b0ec7cf37ce734645a0259cf904d14c2c53", + strip_prefix = "llvm-0e2aef924e02d00a4d3454d63c1921a243d316c6", urls = [ - "https://mirror.bazel.build/github.com/llvm-mirror/llvm/archive/6da47bee848304d215e3ca08b2a4a4dc9c954310.tar.gz", - "https://github.com/llvm-mirror/llvm/archive/6da47bee848304d215e3ca08b2a4a4dc9c954310.tar.gz", + "https://mirror.bazel.build/github.com/llvm-mirror/llvm/archive/0e2aef924e02d00a4d3454d63c1921a243d316c6.tar.gz", + "https://github.com/llvm-mirror/llvm/archive/0e2aef924e02d00a4d3454d63c1921a243d316c6.tar.gz", ], ) -- GitLab From 30e5f93366b7ef7f24f7f267d2ad1d75afe4583b Mon Sep 17 00:00:00 2001 From: Sanjoy Das Date: Mon, 7 Jan 2019 14:24:44 -0800 Subject: [PATCH 437/622] [XLA:CPU] Split out the logic to emit tiled GEMM/GEMV into a separate file; NFC PiperOrigin-RevId: 228235618 --- tensorflow/compiler/xla/service/cpu/BUILD | 16 + .../xla/service/cpu/dot_op_emitter.cc | 993 +--------------- .../xla/service/cpu/tiled_dot_emitter.cc | 1014 +++++++++++++++++ .../xla/service/cpu/tiled_dot_emitter.h | 55 + 4 files changed, 1107 insertions(+), 971 deletions(-) create mode 100644 tensorflow/compiler/xla/service/cpu/tiled_dot_emitter.cc create mode 100644 tensorflow/compiler/xla/service/cpu/tiled_dot_emitter.h diff --git a/tensorflow/compiler/xla/service/cpu/BUILD b/tensorflow/compiler/xla/service/cpu/BUILD index 5c72e1ff71..86c5f71096 100644 --- a/tensorflow/compiler/xla/service/cpu/BUILD +++ b/tensorflow/compiler/xla/service/cpu/BUILD @@ -364,6 +364,21 @@ cc_library( ], ) +cc_library( + name = "tiled_dot_emitter", + srcs = ["tiled_dot_emitter.cc"], + hdrs = ["tiled_dot_emitter.h"], + deps = [ + ":vector_support_library", + "//tensorflow/compiler/xla:xla_data_proto", + "//tensorflow/compiler/xla/service:hlo", + "//tensorflow/compiler/xla/service/llvm_ir:kernel_support_library", + "//tensorflow/compiler/xla/service/llvm_ir:llvm_util", + "//tensorflow/core:lib", + "@llvm//:core", + ], +) + cc_library( name = "dot_op_emitter", srcs = ["dot_op_emitter.cc"], @@ -373,6 +388,7 @@ cc_library( ":cpu_runtime", ":ir_emission_utils", ":target_machine_features", + ":tiled_dot_emitter", ":vector_support_library", "//tensorflow/compiler/xla:shape_util", "//tensorflow/compiler/xla:status_macros", diff --git a/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc b/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc index 1525a33af7..bdd688c2b9 100644 --- a/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc +++ b/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc @@ -26,6 +26,7 @@ limitations under the License. #include "tensorflow/compiler/xla/service/cpu/cpu_runtime.h" #include "tensorflow/compiler/xla/service/cpu/ir_emission_utils.h" #include "tensorflow/compiler/xla/service/cpu/target_machine_features.h" +#include "tensorflow/compiler/xla/service/cpu/tiled_dot_emitter.h" #include "tensorflow/compiler/xla/service/cpu/vector_support_library.h" #include "tensorflow/compiler/xla/service/hlo_module.h" #include "tensorflow/compiler/xla/service/llvm_ir/kernel_support_library.h" @@ -41,930 +42,6 @@ namespace xla { using llvm_ir::SetToFirstInsertPoint; namespace cpu { - -namespace { -// Provides tiled access to an in-memory rank 2 array. -class MemoryTile { - public: - // Constructs a MemoryTile that can operate on tiles consisting of - // `tile_size_along_major_dim` vectors from the matrix `matrix`, starting at - // `major_dim_offset` in the major dimension. The tile size along the minor - // dimension is the vector size, and that is implicitly determined by `vsl`. - MemoryTile(VectorSupportLibrary* vsl, llvm::IRBuilder<>* b, - llvm::Value* matrix, int64 matrix_size_along_minor_dim, - llvm::Value* major_dim_offset, int64 tile_size_along_major_dim) - : vsl_(vsl), b_(b) { - pointers_.reserve(tile_size_along_major_dim); - for (int64 i = 0; i < tile_size_along_major_dim; i++) { - llvm::Value* total_offset = - b->CreateMul(b->getInt64(matrix_size_along_minor_dim), - b->CreateAdd(b->getInt64(i), major_dim_offset)); - pointers_.push_back(vsl_->ComputeOffsetPointer(matrix, total_offset)); - } - } - - // Load a tile consisting of `tile_size_along_major_dim` vectors from position - // {major: `major_dim_offset`, minor: `minor_dim_offset`}. - // - // Note: `major_dim_offset` is a parameter to the constructor. - std::vector LoadTile(llvm::Value* minor_dim_offset) const { - std::vector result; - result.reserve(pointers_.size()); - for (const auto& pointer : pointers_) { - result.push_back(vsl_->LoadVector(pointer, minor_dim_offset)); - } - return result; - } - - // Stores `tile` to position {major: `major_dim_offset`, minor: - // `minor_dim_offset`}. - // - // Note: `major_dim_offset` is a parameter to the constructor. - void StoreTile(absl::Span tile, - llvm::Value* minor_dim_offset) const { - CHECK_EQ(tile.size(), pointers_.size()); - for (int64 i = 0; i < pointers_.size(); i++) { - vsl_->StoreVector(tile[i], pointers_[i], minor_dim_offset); - } - } - - // Loads a tile of size [`tile_size_along_major_dim`, - // `tile_size_along_middle_dim`] from position {major: `major_dim_offset`, - // minor: `minor_dim_offset`} and then broadcasts each element into a vector - // of size vsl_.vector_size(). The (i,j)'th element of the return value is - // the (i,j)'th element in the tile broadcasted into an LLVM vector. - // - // Note: `major_dim_offset` is a parameter to the constructor. - std::vector> LoadBroadcastTile( - llvm::Value* minor_dim_offset, int64 tile_size_along_middle_dim) const { - std::vector> result; - result.resize(pointers_.size()); - for (int64 i = 0; i < pointers_.size(); i++) { - for (int64 j = 0; j < tile_size_along_middle_dim; j++) { - result[i].push_back(vsl_->LoadBroadcast( - pointers_[i], b_->CreateAdd(minor_dim_offset, b_->getInt64(j)))); - } - } - return result; - } - - private: - VectorSupportLibrary* vsl_; - llvm::IRBuilder<>* b_; - std::vector pointers_; -}; - -// The base class for the classes representing the GEMV emitter configurations. -// -// The IR emitted (modulo the LLVM values representing the input and output -// buffers) by the row major and column major GEMV emitters should be a function -// of their configuration. This is important because their configuration is -// used as a key to cache the generated IR. -class GemvConfig { - public: - // Mixin for convenience. - template - struct User { - public: - PrimitiveType scalar_type() const { - return derived().config().scalar_type(); - } - int64 tile_rows() const { return derived().config().tile_rows(); } - int64 tile_cols() const { return derived().config().tile_cols(); } - int64 m() const { return derived().config().m(); } - int64 k() const { return derived().config().k(); } - int64 has_addend() const { return derived().config().has_addend(); } - - private: - const T& derived() const { return *static_cast(this); } - }; - - PrimitiveType scalar_type() const { return scalar_type_; } - int64 tile_rows() const { return tile_rows_; } - int64 tile_cols() const { return tile_cols_; } - int64 m() const { return m_; } - int64 k() const { return k_; } - bool has_addend() const { return has_addend_; } - - string GetCacheKey() const { - return absl::StrCat(name_, "_", PrimitiveType_Name(scalar_type()), "_", - tile_rows(), "_", tile_cols(), "_", m(), "_", k(), - has_addend() ? "_with_addend" : ""); - } - - protected: - explicit GemvConfig(string name, PrimitiveType scalar_type, int64 tile_rows, - int64 tile_cols, int64 m, int64 k, bool has_addend) - : name_(std::move(name)), - scalar_type_(scalar_type), - tile_rows_(tile_rows), - tile_cols_(tile_cols), - m_(m), - k_(k), - has_addend_(has_addend) {} - - private: - string name_; - PrimitiveType scalar_type_; - int64 tile_rows_; - int64 tile_cols_; - int64 m_; - int64 k_; - bool has_addend_; -}; - -// Computes a dot product between "[M,K]{0,1} lhs" with a [K,1] vector (the -// layout of the vector does not matter). This implementation uses a tiling -// scheme to improve performance. -// -// We logically separate the LHS matrix into four segments: -// -// +----------------------+---+ -// | | | -// | | | -// | A | B | -// | | | -// | | | -// | | | -// +----------------------+---+ -// | C | D | -// +----------------------+---+ -// -// where A is the largest submatrix of the LHS that can be evenly dividied into -// tiles. For each tile in A, assuming tile_rows_ == tile_cols_ == 4, we have: -// -// +---+---+---+---+ +--+--+--+--+ -// |M00|M10|M20|M30| |V0|V1|V2|V3| -// +---+---+---+---+ +--+--+--+--+ -// |M01|M11|M21|M31| and |V0|V1|V2|V3| -// +---+---+---+---+ +--+--+--+--+ -// |M02|M12|M22|M32| |V0|V1|V2|V3| -// +---+---+---+---+ +--+--+--+--+ -// |M03|M13|M23|M33| |V0|V1|V2|V3| -// +---+---+---+---+ +--+--+--+--+ -// -// (Legend: rows are horizontal and columns are vertical; and each column is one -// llvm::Value of a vector type) -// -// where: -// -// a. The left tile is from the column major left matrix. -// b. The right tile is an elementwise broadcast of a [V0, V1, V2, V3] -// vector loaded from the RHS vector. -// -// As we iterate through the column dimension, we compute the change to the -// result vector by an elementwise multiplication between the two tiles above -// followed by a reduction along the major dimension: -// -// +-----------------------------------+ -// | M00*V0 + M10*V1 + M20*V2 + M30*V3 | -// +-----------------------------------+ -// | M01*V0 + M11*V1 + M21*V2 + M31*V3 | -// Result[R:R+4] += +-----------------------------------+ -// | M02*V0 + M12*V1 + M22*V2 + M32*V3 | -// +-----------------------------------+ -// | M03*V0 + M13*V1 + M23*V2 + M33*V3 | -// +-----------------------------------+ -// -// Where R is the starting row for the tile. -// -// We have an inner epilogue loop to deal with the "C" submatrix and an outer -// epilogue loop to deal with the B,D submarix. -// -// TODO(sanjoy): We should investigate if using gather loads and scatter stores -// can be used here have the same inner loop for both column-major and row-major -// matrix-vector products. -class ColumnMajorMatrixVectorProductEmitter - : public GemvConfig::User { - public: - class Config : public GemvConfig { - public: - explicit Config(PrimitiveType scalar_type, int64 tile_rows, int64 tile_cols, - int64 m, int64 k, bool has_addend) - : GemvConfig(/*name=*/"col_major_gemv", scalar_type, - /*tile_rows=*/tile_rows, /*tile_cols=*/tile_cols, /*m=*/m, - /*k=*/k, /*has_addend=*/has_addend) {} - }; - - ColumnMajorMatrixVectorProductEmitter(const Config& config, llvm::Value* lhs, - llvm::Value* rhs, llvm::Value* addend, - llvm::Value* result, - llvm::IRBuilder<>* b) - : config_(config), - lhs_(lhs), - rhs_(rhs), - addend_(addend), - result_(result), - b_(b), - ksl_(b_), - vsl_(config.scalar_type(), /*vector_size=*/config.tile_rows(), b_, "") { - CHECK(tile_rows() > 0 && IsPowerOfTwo(static_cast(tile_rows()))); - CHECK(!has_addend() || addend != nullptr); - } - - void Emit(); - - const Config& config() const { return config_; } - - private: - void EmitOuterLoopBody(llvm::Value* column, int64 column_count, - bool is_first_column); - - MemoryTile GetLhsMemoryTile(llvm::Value* column_start, int64 column_count) { - return MemoryTile(&vsl_, b_, /*matrix=*/lhs_, - /*matrix_size_along_minor_dim=*/m(), - /*major_dim_offset=*/column_start, - /*tile_size_along_major_dim=*/column_count); - } - - // Load a tile of values from the RHS. For the RHS a "tile" is a contiguous - // sequence of `count` values, each one broadcasted to the vector width. - std::vector LoadRhsTile(llvm::Value* offset, int64 count) { - llvm::Value* base_pointer = vsl_.ComputeOffsetPointer(rhs_, offset); - std::vector result; - result.reserve(count); - for (int64 i = 0; i < count; i++) { - result.push_back(vsl_.LoadBroadcast(base_pointer, i)); - } - return result; - } - - void EmitInnerLoopTiled(MemoryTile* lhs_memory_tile, - const std::vector& rhs_tile, - int64 columns, bool is_first_column); - - void EmitInnerLoopEpilogue(llvm::Value* current_tile_col, int64 columns, - bool is_first_tiled_column); - - Config config_; - llvm::Value* lhs_; - llvm::Value* rhs_; - llvm::Value* addend_; - llvm::Value* result_; - llvm::IRBuilder<>* b_; - KernelSupportLibrary ksl_; - VectorSupportLibrary vsl_; -}; - -void ColumnMajorMatrixVectorProductEmitter::EmitOuterLoopBody( - llvm::Value* column, int64 column_count, bool is_first_column) { - MemoryTile lhs_memory_tile = GetLhsMemoryTile(/*column_start=*/column, - /*column_count=*/column_count); - - std::vector rhs_tile = - LoadRhsTile(column, /*count=*/column_count); - EmitInnerLoopTiled(&lhs_memory_tile, rhs_tile, - /*columns=*/column_count, is_first_column); - EmitInnerLoopEpilogue(column, /*columns=*/column_count, is_first_column); -} - -void ColumnMajorMatrixVectorProductEmitter::Emit() { - // See the comment on the class declaration for the algorithm used here. - int64 column_remainder = k() % tile_cols(); - int64 column_limit = k() - column_remainder; - - ksl_.For("dot.outer.tiled", - /*start=*/0, /*end=*/column_limit, /*step=*/tile_cols(), - [&](llvm::Value* column, bool is_first_column) { - EmitOuterLoopBody(column, tile_cols(), is_first_column); - }); - - if (column_remainder != 0) { - EmitOuterLoopBody(b_->getInt64(column_limit), column_remainder, - column_limit == 0); - } -} - -void ColumnMajorMatrixVectorProductEmitter::EmitInnerLoopTiled( - MemoryTile* lhs_memory_tile, const std::vector& rhs_tile, - int64 columns, bool is_first_column) { - int64 row_limit = m() - (m() % tile_rows()); - - ksl_.For( - "dot.inner.tiled", /*start=*/0, /*end=*/row_limit, - /*step=*/tile_rows(), [&](llvm::Value* row) { - std::vector lhs_tile = - lhs_memory_tile->LoadTile(/*minor_dim_offset=*/row); - llvm::Value* accumulator = - is_first_column ? (addend_ ? vsl_.LoadVector(addend_, row) - : vsl_.GetZeroVector()) - : vsl_.LoadVector(result_, row); - for (int i = 0; i < columns; i++) { - accumulator = vsl_.MulAdd(lhs_tile[i], rhs_tile[i], accumulator); - } - vsl_.StoreVector(accumulator, result_, row); - }); -} - -void ColumnMajorMatrixVectorProductEmitter::EmitInnerLoopEpilogue( - llvm::Value* current_tile_col, int64 columns, bool is_first_tiled_column) { - int64 row_start = m() - (m() % tile_rows()); - if (row_start == m()) { - return; - } - - llvm::Value* columns_llvm = b_->getInt64(columns); - - // for (col = current_tile_col; col < (columns + current_tile_col); col++) - // for (row = row_start, row < m_; row++) { - // result[row] += lhs[row, col] * rhs[col] - // // Also take into account that if col is 0 then result[row] is not - // // initialized. - // } - - ksl_.For( - "dot.inner.epilg.outer", /*start=*/current_tile_col, - /*end=*/b_->CreateAdd(columns_llvm, current_tile_col), - /*step=*/1, /*peel_first_iteration=*/false, - [&](llvm::Value* col, llvm::Value* is_first_scalar_col) { - llvm::Value* rhs_element = vsl_.LoadScalar(rhs_, col); - llvm::Value* total_offset = b_->CreateMul(col, b_->getInt64(m())); - llvm::Value* lhs_base_pointer = - vsl_.ComputeOffsetPointer(lhs_, total_offset); - ksl_.For( - "dot.inner.epilg.inner", /*start=*/row_start, /*end=*/m(), - /*step=*/1, [&](llvm::Value* scalar_row) { - llvm::Value* product = vsl_.Mul( - vsl_.LoadScalar(lhs_base_pointer, scalar_row), rhs_element); - llvm::Value* setting_result_first_time = b_->CreateAnd( - is_first_scalar_col, b_->getInt1(is_first_tiled_column)); - ksl_.If( - setting_result_first_time, - /*true_block_generator=*/ - [&]() { - if (addend_) { - vsl_.StoreScalar( - vsl_.Add(vsl_.LoadScalar(addend_, scalar_row), - product), - result_, scalar_row); - } else { - vsl_.StoreScalar(product, result_, scalar_row); - } - }, - /*false_block_generator=*/ - [&]() { - vsl_.StoreScalar( - vsl_.Add(vsl_.LoadScalar(result_, scalar_row), product), - result_, scalar_row); - }); - }); - }); -} - -// Computes a dot product between "[M,K]{1,0} lhs" with a [K,1] vector (the -// layout of the vector does not matter). This implementation uses a tiling -// scheme to improve performance. -// -// We logically separate the LHS matrix into four segments: -// -// +----------------------+---+ -// | | | -// | | | -// | A | B | -// | | | -// | | | -// | | | -// +----------------------+---+ -// | C | D | -// +----------------------+---+ -// -// where A is the largest submatrix of the LHS that can be evenly dividied into -// tiles. For each tile in A, assuming tile_rows_ == tile_cols_ == 4, we have: -// -// +---+---+---+---+ -// |M00|M10|M20|M30| -// +---+---+---+---+ +--+--+--+--+ -// |M01|M11|M21|M31| and |V0|V1|V2|V3| -// +---+---+---+---+ +--+--+--+--+ -// |M02|M12|M22|M32| -// +---+---+---+---+ -// |M03|M13|M23|M33| -// +---+---+---+---+ -// -// (Legend: rows are horizontal and columns are vertical; and each row is one -// llvm::Value of a vector type) -// -// where: -// -// a. The left tile is loaded from the row major left matrix. -// b. The right vector is loaded from the RHS vector. -// -// We keep 4 vector accumulators accumulating the following four vector -// expressions as we iterate over the row dimension: -// -// +------+------+------+------+ -// |M0I*V0|M1I*V1|M2I*V2|M3I*V3| for I in [0,4) -// +------+------+------+------+ -// -// In the end we do a horizontal reduction over these 4 vector accumulators to -// get 4 values in the result vector. -// -// We have an inner epilogue loop to deal with the "B" sub-matrix and an outer -// epilogue loop to deal with the C,D submatrix. -class RowMajorMatrixVectorProductEmitter - : public GemvConfig::User { - public: - class Config : public GemvConfig { - public: - explicit Config(PrimitiveType scalar_type, int64 tile_rows, int64 tile_cols, - int64 m, int64 k, bool has_addend) - : GemvConfig(/*name=*/"row_major_gemv", scalar_type, - /*tile_rows=*/tile_rows, /*tile_cols=*/tile_cols, /*m=*/m, - /*k=*/k, /*has_addend=*/has_addend) {} - }; - - RowMajorMatrixVectorProductEmitter(const Config& config, llvm::Value* lhs, - llvm::Value* rhs, llvm::Value* addend, - llvm::Value* result, llvm::IRBuilder<>* b) - : config_(config), - lhs_(lhs), - rhs_(rhs), - addend_(addend), - result_(result), - b_(b), - ksl_(b_), - vsl_(scalar_type(), /*vector_size=*/tile_cols(), b_, "") { - CHECK(tile_cols() > 0 && IsPowerOfTwo(static_cast(tile_cols()))); - CHECK(!has_addend() || addend != nullptr); - } - - void Emit(); - - const Config& config() const { return config_; } - - private: - MemoryTile GetLhsMemoryTile(llvm::Value* row_start, int64 row_count) { - return MemoryTile(&vsl_, b_, /*matrix=*/lhs_, - /*matrix_size_along_minor_dim=*/k(), - /*major_dim_offset=*/row_start, - /*tile_size_along_major_dim=*/row_count); - } - - void EmitOuterLoopBody(llvm::Value* row, int64 row_count); - - void EmitInnerLoopTiled(MemoryTile* lhs_memory_tile, int64 rows, - std::vector* vector_accumulators); - - void EmitInnerLoopEpilogue(llvm::Value* current_tile_row, int64 rows, - std::vector* scalar_accumulators); - - Config config_; - llvm::Value* lhs_; - llvm::Value* rhs_; - llvm::Value* addend_; - llvm::Value* result_; - llvm::IRBuilder<>* b_; - KernelSupportLibrary ksl_; - VectorSupportLibrary vsl_; -}; - -void RowMajorMatrixVectorProductEmitter::EmitOuterLoopBody(llvm::Value* row, - int64 row_count) { - MemoryTile lhs_memory_tile = GetLhsMemoryTile(/*row_start=*/row, - /*row_count=*/row_count); - std::vector vector_accumulators; - std::vector scalar_accumulators; - for (int i = 0; i < row_count; i++) { - vector_accumulators.emplace_back(&vsl_, vsl_.GetZeroVector()); - scalar_accumulators.emplace_back(&vsl_, vsl_.GetZeroScalar()); - } - EmitInnerLoopTiled(&lhs_memory_tile, /*rows=*/row_count, - &vector_accumulators); - EmitInnerLoopEpilogue(/*current_tile_row=*/row, /*rows=*/row_count, - &scalar_accumulators); - - std::vector accumulator_values; - std::transform( - vector_accumulators.begin(), vector_accumulators.end(), - std::back_inserter(accumulator_values), - [](const VectorVariable& vector_var) { return vector_var.Get(); }); - - std::vector horizontal_sums; - if (row_count == vsl_.vector_size()) { - if (addend_) { - horizontal_sums = vsl_.ComputeHorizontalSums( - std::move(accumulator_values), vsl_.LoadVector(addend_, row)); - } else { - horizontal_sums = - vsl_.ComputeHorizontalSums(std::move(accumulator_values)); - } - } else { - horizontal_sums = vsl_.ComputeHorizontalSums(std::move(accumulator_values)); - } - - for (int i = 0; i < row_count; i++) { - llvm::Value* result_value = - vsl_.Add(horizontal_sums[i], scalar_accumulators[i].Get()); - llvm::Value* offset = b_->CreateAdd(b_->getInt64(i), row); - if (addend_ && row_count != vsl_.vector_size()) { - result_value = vsl_.Add(vsl_.LoadScalar(addend_, offset), result_value); - } - vsl_.StoreScalar(result_value, result_, offset); - } -} - -void RowMajorMatrixVectorProductEmitter::Emit() { - // See the comment on the class declaration for the algorithm used here. - int64 row_remainder = m() % tile_rows(); - int64 row_limit = m() - row_remainder; - - ksl_.For("dot.outer.tiled", - /*start=*/0, /*end=*/row_limit, /*step=*/tile_rows(), - [&](llvm::Value* row) { EmitOuterLoopBody(row, tile_rows()); }); - - if (row_remainder != 0) { - EmitOuterLoopBody(b_->getInt64(row_limit), row_remainder); - } -} - -void RowMajorMatrixVectorProductEmitter::EmitInnerLoopTiled( - MemoryTile* lhs_memory_tile, int64 rows, - std::vector* vector_accumulators) { - int64 column_limit = k() - (k() % tile_cols()); - - ksl_.For("dot.inner.tiled", /*start=*/0, /*end=*/column_limit, - /*step=*/tile_cols(), [&](llvm::Value* col) { - std::vector lhs_tile = - lhs_memory_tile->LoadTile(/*minor_dim_offset=*/col); - llvm::Value* rhs_value = vsl_.LoadVector(rhs_, col); - for (int i = 0; i < rows; i++) { - llvm::Value* old_sum = (*vector_accumulators)[i].Get(); - (*vector_accumulators)[i].Set( - vsl_.Add(old_sum, vsl_.Mul(rhs_value, lhs_tile[i]))); - } - }); -} - -void RowMajorMatrixVectorProductEmitter::EmitInnerLoopEpilogue( - llvm::Value* current_tile_row, int64 rows, - std::vector* scalar_accumulators) { - int64 column_start = k() - (k() % tile_cols()); - if (column_start == k()) { - return; - } - - for (int r = 0; r < rows; r++) { - llvm::Value* total_offset = b_->CreateMul( - b_->CreateAdd(b_->getInt64(r), current_tile_row), b_->getInt64(k())); - llvm::Value* lhs_base_pointer = - vsl_.ComputeOffsetPointer(lhs_, total_offset); - ksl_.For( - "dot.inner.epilg.inner", /*start=*/column_start, /*end=*/k(), - /*step=*/1, [&](llvm::Value* scalar_col) { - llvm::Value* product = - vsl_.Mul(vsl_.LoadScalar(lhs_base_pointer, scalar_col), - vsl_.LoadScalar(rhs_, scalar_col)); - llvm::Value* old_value = (*scalar_accumulators)[r].Get(); - (*scalar_accumulators)[r].Set(vsl_.Add(old_value, product)); - }); - } -} - -// This class implements a tiled matrix multiplication algorithm, intended for -// multiplying small matrices that don't need cache tiling. -// -// In the future this can be used as the innermost GEBP loop in a GEMM kernel as -// described in "Goto, Kazushige, and Robert A. Geijn. "Anatomy of -// high-performance matrix multiplication." ACM Transactions on Mathematical -// Software (TOMS) 34.3 (2008): 12.". -// -// This only supports canonical dot operations (i.e. where the lhs contraction -// dimension is 1 and the rhs contraction dimension is 0) over row major -// matrices. -class TiledSmallGemmEmitter { - public: - // Describe the dimensions of the kernel. - class Dimensions { - public: - explicit Dimensions(int64 m, int64 k, int64 n) : m_(m), k_(k), n_(n) {} - - int64 m() const { return m_; } - int64 k() const { return k_; } - int64 n() const { return n_; } - - string ToString() const { return absl::StrCat(m(), "x", k(), "x", n()); } - - private: - const int64 m_; - const int64 k_; - const int64 n_; - }; - - // Represents the configuration of the emitter. The LLVM IR emitted by the - // emitter, modulo the LLVM values holding the input and output buffers, must - // be a function of the instance of `Config` passed to it. - // - // `dims` holds the matrix multiplication dimensions. - // - // `max_vectorization_width` is the maximum vector width (i.e. the width of - // the largest vector register we will use). This can be larger than the - // largest vector register supported by the machine -- LLVM will legalize - // these large vector widths into legally sized vectors. - // - // `max_vector_count` is the maximum number of vectors of size - // `max_vectorization_width` that we will attempt to process at once. - // - // `min_vectorization_width` is the smallest vector width the emitter will use - // -- below that it will devolve to using a scalar loop. - // - // The innermost reduction loop executes the matrix multiply in tiles of size - // [`tile_size_m`, `tile_size_k`] from the LHS and [`tile_size_k`, - // ] in the RHS. - class Config { - public: - explicit Config(PrimitiveType scalar_type, Dimensions dims, - int64 max_vectorization_width, int64 max_vector_count, - int64 min_vectorization_width, int64 tile_size_m, - int64 tile_size_k) - : scalar_type_(scalar_type), - dims_(dims), - max_vectorization_width_(max_vectorization_width), - max_vector_count_(max_vector_count), - min_vectorization_width_(min_vectorization_width), - tile_size_m_(tile_size_m), - tile_size_k_(tile_size_k) {} - - string GetCacheKey() const { - return absl::StrCat("gemm_", PrimitiveType_Name(scalar_type()), "_", - dims().ToString(), "_", max_vectorization_width(), - "_", min_vectorization_width(), "_", tile_size_m(), - "_", tile_size_k()); - } - - PrimitiveType scalar_type() const { return scalar_type_; } - Dimensions dims() const { return dims_; } - int64 max_vectorization_width() const { return max_vectorization_width_; } - int64 max_vector_count() const { return max_vector_count_; } - int64 min_vectorization_width() const { return min_vectorization_width_; } - - int64 tile_size_m() const { return tile_size_m_; } - int64 tile_size_k() const { return tile_size_k_; } - - private: - PrimitiveType scalar_type_; - Dimensions dims_; - int64 max_vectorization_width_; - int64 max_vector_count_; - int64 min_vectorization_width_; - int64 tile_size_m_; - int64 tile_size_k_; - }; - - // Creates an instance of TiledSmallGemmEmitter that matrix-multiplies - // `lhs` with `rhs` and stores the result in `result`. - explicit TiledSmallGemmEmitter(Config config, llvm::Value* lhs, - llvm::Value* rhs, llvm::Value* result, - llvm::IRBuilder<>* b) - : lhs_(lhs), - rhs_(rhs), - result_(result), - config_(config), - b_(b), - ksl_(b_) { - CHECK(max_vectorization_width() > 0 && - IsPowerOfTwo(static_cast(max_vectorization_width()))); - CHECK_GT(max_vector_count(), 0); - CHECK(min_vectorization_width() > 0 && - IsPowerOfTwo(static_cast(min_vectorization_width()))); - CHECK_GE(max_vectorization_width(), min_vectorization_width()); - CHECK_GT(tile_size_k(), 0); - } - - void Emit(); - - private: - // The HandleResiduesOnX helpers split the iteration space for dimension X - // into a multiple of the tile size on dimension X and an epilogue. These - // helpers ultimately call into `EmitTiledGemm` for emitting the - // tiled GEMM kernel. - - void HandleResiduesOnN(); - void HandleResiduesOnK(VectorSupportLibrary* vsl, llvm::Value* n_start, - llvm::Value* n_end); - void HandleResiduesOnM(VectorSupportLibrary* vsl, int64 tile_size_k, - llvm::Value* k_start, llvm::Value* k_end, - llvm::Value* n_start, llvm::Value* n_end); - - // This emits a tiled GEMM kernel. For a detailed description see the comment - // on the implementation. - void EmitTiledGemm(VectorSupportLibrary* vsl, int64 tile_size_k, - llvm::Value* k_start, llvm::Value* k_end, - llvm::Value* n_start, llvm::Value* n_end, - int64 tile_size_m, llvm::Value* m_start, - llvm::Value* m_end); - - llvm::Value* GetInt64(int64 value) { return b_->getInt64(value); } - - Config config() const { return config_; } - Dimensions dims() const { return config().dims(); } - - int64 max_vectorization_width() const { - return config().max_vectorization_width(); - } - int64 max_vector_count() const { return config().max_vector_count(); } - int64 min_vectorization_width() const { - return config().min_vectorization_width(); - } - int64 tile_size_m() const { return config().tile_size_m(); } - int64 tile_size_k() const { return config().tile_size_k(); } - PrimitiveType scalar_type() const { return config().scalar_type(); } - - llvm::Value* lhs_; - llvm::Value* rhs_; - llvm::Value* result_; - Config config_; - - llvm::IRBuilder<>* b_; - KernelSupportLibrary ksl_; -}; - -void TiledSmallGemmEmitter::Emit() { HandleResiduesOnN(); } - -void TiledSmallGemmEmitter::HandleResiduesOnN() { - // We can only iterate the `n` dimension for an extent that is divisible by - // the vectorization width. So we emit an outer loop that first processes the - // largest extent in `n` that is divisible by max_vectorization_width, then - // the largest remaining extent that is divisible by max_vectorization_width / - // 2 etc. - - int64 current_vectorization_width = - max_vector_count() * max_vectorization_width(); - int64 current_vector_count = max_vector_count(); - - int64 n_start = 0; - while (n_start != dims().n() && - current_vectorization_width >= min_vectorization_width()) { - int64 n_end = dims().n() - (dims().n() % current_vectorization_width); - if (n_start != n_end) { - VectorSupportLibrary vsl(scalar_type(), current_vectorization_width, b_, - "gemm"); - HandleResiduesOnK(&vsl, GetInt64(n_start), GetInt64(n_end)); - n_start = n_end; - } - if (current_vector_count == 1) { - current_vectorization_width /= 2; - } else { - current_vector_count--; - current_vectorization_width = - current_vector_count * max_vectorization_width(); - } - } - - if (n_start != dims().n()) { - VectorSupportLibrary vsl(scalar_type(), 1, b_, "gemm"); - ksl_.For("epi.n", n_start, dims().n(), 1, [&](llvm::Value* n_i) { - llvm::Value* n_i_next = b_->CreateAdd(n_i, b_->getInt64(1)); - HandleResiduesOnK(&vsl, n_i, n_i_next); - }); - } -} - -void TiledSmallGemmEmitter::HandleResiduesOnK(VectorSupportLibrary* vsl, - llvm::Value* n_start, - llvm::Value* n_end) { - int64 k_start = 0; - int64 k_end = dims().k() - (dims().k() % tile_size_k()); - if (k_end != k_start) { - HandleResiduesOnM(vsl, tile_size_k(), GetInt64(k_start), GetInt64(k_end), - n_start, n_end); - k_start = k_end; - } - - if (k_start != dims().k()) { - HandleResiduesOnM(vsl, dims().k() - k_start, GetInt64(k_start), - GetInt64(dims().k()), n_start, n_end); - } -} - -void TiledSmallGemmEmitter::HandleResiduesOnM( - VectorSupportLibrary* vsl, int64 tile_size_k, llvm::Value* k_start, - llvm::Value* k_end, llvm::Value* n_start, llvm::Value* n_end) { - const int64 m_end = dims().m() - dims().m() % tile_size_m(); - EmitTiledGemm(vsl, tile_size_k, k_start, k_end, n_start, n_end, tile_size_m(), - GetInt64(0), GetInt64(m_end)); - - if (m_end != dims().m()) { - EmitTiledGemm(vsl, tile_size_k, k_start, k_end, n_start, n_end, - dims().m() - m_end, GetInt64(m_end), GetInt64(dims().m())); - } -} - -// The loop structure is: -// -// Iterate over dimension M as m: -// Iterate over dimension N as n: -// Iterate over dimension K as k: -// OutputTile[m,n] += Dot(LhsTile[m,k], RhsTile[k,n]) -// -// I.e. a just a tiled version of a "naive" GEMM. -// -// The tiling scheme is as follows: -// -// Let the LHS be: -// -// +----+----+----+ -// | a0 | b0 | c0 | . -// +----+----+----+ . -// | a1 | b1 | c1 | . -// +----+----+----+ -// .. .. -// -// and the RHS be: -// -// +----+----+----+----+ -// | p0 | p1 | p2 | p3 | . -// +----+----+----+----+ . -// | q0 | q1 | q2 | q3 | . -// +----+----+----+----+ -// | r0 | r1 | r2 | r3 | . -// +----+----+----+----+ . -// ...... ...... -// -// and let tile_size_m=2, tile_size_k=3 and the vector width (implicitly denoted -// by `vsl`) be 4. Then we want to matrix multiply this tile to get a [2,4] -// matrix that we can increment the result matrix by. -// -// First broadcast the rows row in LHS to 3 vectors of width 4, giving us a rank -// 3 array, L, of dimension [2,3,4]: -// -// L[0,_,_] * L[1,_,_] -// * -// +----+----+----+----+ * +----+----+----+----+ -// | a0 | a0 | a0 | a0 | * | a1 | a1 | a1 | a1 | -// +----+----+----+----+ * +----+----+----+----+ -// | b0 | b0 | b0 | b0 | * | b1 | b1 | b1 | b1 | -// +----+----+----+----+ * +----+----+----+----+ -// | c0 | c0 | c0 | c0 | * | c1 | c1 | c1 | c1 | -// +----+----+----+----+ * +----+----+----+----+ -// -// -// Then we FMA L[0,_,_] with the RHS to get the first row of the result and -// L[1,_,_] with the RHS to get the second row of the result. For example, -// L[0,_,_] is computed as: -// -// +----+----+----+----+ +----+----+----+----+ -// | a0 | a0 | a0 | a0 | * | p0 | p1 | p2 | p3 | + -// +----+----+----+----+ +----+----+----+----+ -// -// +----+----+----+----+ +----+----+----+----+ -// | b0 | b0 | b0 | b0 | * | q0 | q1 | q2 | q3 | + -// +----+----+----+----+ +----+----+----+----+ -// -// +----+----+----+----+ +----+----+----+----+ -// | c0 | c0 | c0 | c0 | * | r0 | r1 | r2 | r3 | -// +----+----+----+----+ +----+----+----+----+ -// -// to get: -// -// +-------------------+-------------------+-------------------+--------- -// | a0*p0+b0*q0+c0*r0 | a0*p1+b0*q1+c0*r1 | a0*p2+b0*q2+c0*r2 | ... -// +-------------------+-------------------+-------------------+--------- -void TiledSmallGemmEmitter::EmitTiledGemm( - VectorSupportLibrary* vsl, int64 tile_size_k, llvm::Value* k_start, - llvm::Value* k_end, llvm::Value* n_start, llvm::Value* n_end, - int64 tile_size_m, llvm::Value* m_start, llvm::Value* m_end) { - ksl_.For( - "dot.m", m_start, m_end, tile_size_m, [&](llvm::Value* m_i) { - MemoryTile result_memory_tile( - vsl, b_, /*matrix=*/result_, - /*matrix_size_along_minor_dim=*/dims().n(), - /*major_dim_offset=*/m_i, - /*tile_size_along_major_dim=*/tile_size_m); - MemoryTile lhs_memory_tile(vsl, b_, /*matrix=*/lhs_, - /*matrix_size_along_minor_dim=*/dims().k(), - /*major_dim_offset=*/m_i, - /*tile_size_along_major_dim=*/tile_size_m); - ksl_.For( - "dot.n", n_start, n_end, vsl->vector_size(), [&](llvm::Value* n_i) { - TileVariable result_tile_var(vsl, - result_memory_tile.LoadTile(n_i)); - ksl_.For( - "dot.k", k_start, k_end, tile_size_k, [&](llvm::Value* k_i) { - MemoryTile rhs_memory_tile(vsl, b_, rhs_, dims().n(), k_i, - tile_size_k); - std::vector> lhs_tile = - lhs_memory_tile.LoadBroadcastTile(k_i, tile_size_k); - std::vector rhs_tile = - rhs_memory_tile.LoadTile(n_i); - std::vector result_tile = - result_tile_var.Get(); - for (int64 r_m_i = 0; r_m_i < tile_size_m; r_m_i++) { - for (int64 r_k_i = 0; r_k_i < tile_size_k; r_k_i++) { - result_tile[r_m_i] = - vsl->MulAdd(lhs_tile[r_m_i][r_k_i], rhs_tile[r_k_i], - result_tile[r_m_i]); - } - } - result_tile_var.Set(result_tile); - }); - - result_memory_tile.StoreTile(result_tile_var.Get(), n_i); - }); - }); -} - -} // namespace - DotOpEmitter::DotOpEmitter(const HloInstruction& dot, const llvm_ir::IrArray& target_array, const llvm_ir::IrArray& lhs_array, @@ -1062,32 +139,21 @@ bool DotOpEmitter::EmitSmallGemmIfProfitable( std::tie(tile_size_m, tile_size_k, tile_size_n_in_vector_width) = GetGemmTileSize(); - TiledSmallGemmEmitter::Config config( - /*scalar_type=*/primitive_type, - TiledSmallGemmEmitter::Dimensions{/*m=*/m, /*k=*/k, /*n=*/n}, - /*max_vectorization_width=*/max_target_vector_width, - /*max_vector_count=*/tile_size_n_in_vector_width, - /*min_vectorization_width=*/std::min(4, max_target_vector_width), - /*tile_size_m=*/tile_size_m, /*tile_size_k=*/tile_size_k); - - VLOG(2) << "Emitting GEMM kernel in LLVM IR with config " - << config.GetCacheKey(); - const bool enable_fast_math = hlo_module_config_.debug_options().xla_cpu_enable_fast_math(); const bool optimize_for_size = options::OptimizeForSizeRequested(hlo_module_config_); - KernelSupportLibrary::EmitAndCallOutlinedKernel( + EmitSmallGemm( + /*scalar_type=*/primitive_type, + /*m=*/m, /*k=*/k, /*n=*/n, + /*max_vectorization_width=*/max_target_vector_width, + /*max_vector_count=*/tile_size_n_in_vector_width, + /*min_vectorization_width=*/std::min(4, max_target_vector_width), + /*tile_size_m=*/tile_size_m, /*tile_size_k=*/tile_size_k, /*lhs=*/lhs, + /*rhs=*/rhs, /*result=*/target, b_, /*enable_fast_math=*/enable_fast_math, - /*optimize_for_size=*/optimize_for_size, b_, config.GetCacheKey(), lhs, - rhs, target, - [this, config](llvm::Value* lhs, llvm::Value* rhs, llvm::Value* target) { - TiledSmallGemmEmitter small_gemm_emitter(config, /*lhs=*/lhs, - /*rhs=*/rhs, - /*result=*/target, b_); - small_gemm_emitter.Emit(); - }); + /*optimize_for_size=*/optimize_for_size); return true; } @@ -1177,41 +243,26 @@ bool DotOpEmitter::EmitLlvmIrDotIfProfitable() { if (is_column_major_matrix_vector) { VLOG(2) << "Emitting column major matrix-vector multiply with m = " << m << " and k = " << k; - ColumnMajorMatrixVectorProductEmitter::Config config( + EmitColumnMajorGemv( /*scalar_type=*/primitive_type, /*tile_rows=*/vector_register_element_size, /*tile_cols=*/tiling_factor, - /*m=*/m, /*k=*/k, /*has_addend=*/addend_array_ != nullptr); - - KernelSupportLibrary::EmitAndCallOutlinedKernel( + /*m=*/m, /*k=*/k, /*lhs=*/lhs_op, /*rhs=*/rhs_op, + /*addend=*/addend_array_ ? addend_array_->GetBasePointer() : nullptr, + /*result=*/result_op, b_, /*enable_fast_math=*/enable_fast_math, - /*optimize_for_size=*/optimize_for_size, b_, config.GetCacheKey(), - lhs_op, rhs_op, - addend_array_ ? addend_array_->GetBasePointer() : nullptr, result_op, - [this, config](llvm::Value* lhs_op, llvm::Value* rhs_op, - llvm::Value* addend_op, llvm::Value* result_op) { - ColumnMajorMatrixVectorProductEmitter emitter( - config, lhs_op, rhs_op, addend_op, result_op, b_); - emitter.Emit(); - }); + /*optimize_for_size=*/optimize_for_size); } else { VLOG(2) << "Emitting row major matrix-vector multiply with m = " << m << " and k = " << k; - RowMajorMatrixVectorProductEmitter::Config config( + EmitRowMajorGemv( /*scalar_type=*/primitive_type, - /*tile_rows=*/tiling_factor, /*tile_cols=*/vector_register_element_size, - /*m=*/m, /*k=*/k, /*has_addend=*/addend_array_ != nullptr); - - KernelSupportLibrary::EmitAndCallOutlinedKernel( + /*tile_rows=*/tiling_factor, + /*tile_cols=*/vector_register_element_size, + /*m=*/m, /*k=*/k, /*lhs=*/lhs_op, /*rhs=*/rhs_op, + /*addend=*/addend_array_ ? addend_array_->GetBasePointer() : nullptr, + /*result=*/result_op, b_, /*enable_fast_math=*/enable_fast_math, - /*optimize_for_size=*/optimize_for_size, b_, config.GetCacheKey(), - lhs_op, rhs_op, - addend_array_ ? addend_array_->GetBasePointer() : nullptr, result_op, - [this, config](llvm::Value* lhs_op, llvm::Value* rhs_op, - llvm::Value* addend_op, llvm::Value* result_op) { - RowMajorMatrixVectorProductEmitter emitter(config, lhs_op, rhs_op, - addend_op, result_op, b_); - emitter.Emit(); - }); + /*optimize_for_size=*/optimize_for_size); } return true; diff --git a/tensorflow/compiler/xla/service/cpu/tiled_dot_emitter.cc b/tensorflow/compiler/xla/service/cpu/tiled_dot_emitter.cc new file mode 100644 index 0000000000..eb6c44b70a --- /dev/null +++ b/tensorflow/compiler/xla/service/cpu/tiled_dot_emitter.cc @@ -0,0 +1,1014 @@ +/* Copyright 2018 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/compiler/xla/service/cpu/tiled_dot_emitter.h" + +#include "tensorflow/compiler/xla/service/cpu/vector_support_library.h" +#include "tensorflow/compiler/xla/service/hlo_module.h" +#include "tensorflow/compiler/xla/service/llvm_ir/kernel_support_library.h" +#include "tensorflow/compiler/xla/service/llvm_ir/llvm_util.h" + +namespace xla { +namespace cpu { +namespace { + +using tensorflow::int64; + +// Provides tiled access to an in-memory rank 2 array. +class MemoryTile { + public: + // Constructs a MemoryTile that can operate on tiles consisting of + // `tile_size_along_major_dim` vectors from the matrix `matrix`, starting at + // `major_dim_offset` in the major dimension. The tile size along the minor + // dimension is the vector size, and that is implicitly determined by `vsl`. + MemoryTile(VectorSupportLibrary* vsl, llvm::IRBuilder<>* b, + llvm::Value* matrix, int64 matrix_size_along_minor_dim, + llvm::Value* major_dim_offset, int64 tile_size_along_major_dim) + : vsl_(vsl), b_(b) { + pointers_.reserve(tile_size_along_major_dim); + for (int64 i = 0; i < tile_size_along_major_dim; i++) { + llvm::Value* total_offset = + b->CreateMul(b->getInt64(matrix_size_along_minor_dim), + b->CreateAdd(b->getInt64(i), major_dim_offset)); + pointers_.push_back(vsl_->ComputeOffsetPointer(matrix, total_offset)); + } + } + + // Load a tile consisting of `tile_size_along_major_dim` vectors from position + // {major: `major_dim_offset`, minor: `minor_dim_offset`}. + // + // Note: `major_dim_offset` is a parameter to the constructor. + std::vector LoadTile(llvm::Value* minor_dim_offset) const { + std::vector result; + result.reserve(pointers_.size()); + for (const auto& pointer : pointers_) { + result.push_back(vsl_->LoadVector(pointer, minor_dim_offset)); + } + return result; + } + + // Stores `tile` to position {major: `major_dim_offset`, minor: + // `minor_dim_offset`}. + // + // Note: `major_dim_offset` is a parameter to the constructor. + void StoreTile(absl::Span tile, + llvm::Value* minor_dim_offset) const { + CHECK_EQ(tile.size(), pointers_.size()); + for (int64 i = 0; i < pointers_.size(); i++) { + vsl_->StoreVector(tile[i], pointers_[i], minor_dim_offset); + } + } + + // Loads a tile of size [`tile_size_along_major_dim`, + // `tile_size_along_middle_dim`] from position {major: `major_dim_offset`, + // minor: `minor_dim_offset`} and then broadcasts each element into a vector + // of size vsl_.vector_size(). The (i,j)'th element of the return value is + // the (i,j)'th element in the tile broadcasted into an LLVM vector. + // + // Note: `major_dim_offset` is a parameter to the constructor. + std::vector> LoadBroadcastTile( + llvm::Value* minor_dim_offset, int64 tile_size_along_middle_dim) const { + std::vector> result; + result.resize(pointers_.size()); + for (int64 i = 0; i < pointers_.size(); i++) { + for (int64 j = 0; j < tile_size_along_middle_dim; j++) { + result[i].push_back(vsl_->LoadBroadcast( + pointers_[i], b_->CreateAdd(minor_dim_offset, b_->getInt64(j)))); + } + } + return result; + } + + private: + VectorSupportLibrary* vsl_; + llvm::IRBuilder<>* b_; + std::vector pointers_; +}; + +// The base class for the classes representing the GEMV emitter configurations. +// +// The IR emitted (modulo the LLVM values representing the input and output +// buffers) by the row major and column major GEMV emitters should be a function +// of their configuration. This is important because their configuration is +// used as a key to cache the generated IR. +class GemvConfig { + public: + // Mixin for convenience. + template + struct User { + public: + PrimitiveType scalar_type() const { + return derived().config().scalar_type(); + } + int64 tile_rows() const { return derived().config().tile_rows(); } + int64 tile_cols() const { return derived().config().tile_cols(); } + int64 m() const { return derived().config().m(); } + int64 k() const { return derived().config().k(); } + int64 has_addend() const { return derived().config().has_addend(); } + + private: + const T& derived() const { return *static_cast(this); } + }; + + PrimitiveType scalar_type() const { return scalar_type_; } + int64 tile_rows() const { return tile_rows_; } + int64 tile_cols() const { return tile_cols_; } + int64 m() const { return m_; } + int64 k() const { return k_; } + bool has_addend() const { return has_addend_; } + + string GetCacheKey() const { + return absl::StrCat(name_, "_", PrimitiveType_Name(scalar_type()), "_", + tile_rows(), "_", tile_cols(), "_", m(), "_", k(), + has_addend() ? "_with_addend" : ""); + } + + protected: + explicit GemvConfig(string name, PrimitiveType scalar_type, int64 tile_rows, + int64 tile_cols, int64 m, int64 k, bool has_addend) + : name_(std::move(name)), + scalar_type_(scalar_type), + tile_rows_(tile_rows), + tile_cols_(tile_cols), + m_(m), + k_(k), + has_addend_(has_addend) {} + + private: + string name_; + PrimitiveType scalar_type_; + int64 tile_rows_; + int64 tile_cols_; + int64 m_; + int64 k_; + bool has_addend_; +}; + +// Computes a dot product between "[M,K]{0,1} lhs" with a [K,1] vector (the +// layout of the vector does not matter). This implementation uses a tiling +// scheme to improve performance. +// +// We logically separate the LHS matrix into four segments: +// +// +----------------------+---+ +// | | | +// | | | +// | A | B | +// | | | +// | | | +// | | | +// +----------------------+---+ +// | C | D | +// +----------------------+---+ +// +// where A is the largest submatrix of the LHS that can be evenly dividied into +// tiles. For each tile in A, assuming tile_rows_ == tile_cols_ == 4, we have: +// +// +---+---+---+---+ +--+--+--+--+ +// |M00|M10|M20|M30| |V0|V1|V2|V3| +// +---+---+---+---+ +--+--+--+--+ +// |M01|M11|M21|M31| and |V0|V1|V2|V3| +// +---+---+---+---+ +--+--+--+--+ +// |M02|M12|M22|M32| |V0|V1|V2|V3| +// +---+---+---+---+ +--+--+--+--+ +// |M03|M13|M23|M33| |V0|V1|V2|V3| +// +---+---+---+---+ +--+--+--+--+ +// +// (Legend: rows are horizontal and columns are vertical; and each column is one +// llvm::Value of a vector type) +// +// where: +// +// a. The left tile is from the column major left matrix. +// b. The right tile is an elementwise broadcast of a [V0, V1, V2, V3] +// vector loaded from the RHS vector. +// +// As we iterate through the column dimension, we compute the change to the +// result vector by an elementwise multiplication between the two tiles above +// followed by a reduction along the major dimension: +// +// +-----------------------------------+ +// | M00*V0 + M10*V1 + M20*V2 + M30*V3 | +// +-----------------------------------+ +// | M01*V0 + M11*V1 + M21*V2 + M31*V3 | +// Result[R:R+4] += +-----------------------------------+ +// | M02*V0 + M12*V1 + M22*V2 + M32*V3 | +// +-----------------------------------+ +// | M03*V0 + M13*V1 + M23*V2 + M33*V3 | +// +-----------------------------------+ +// +// Where R is the starting row for the tile. +// +// We have an inner epilogue loop to deal with the "C" submatrix and an outer +// epilogue loop to deal with the B,D submarix. +// +// TODO(sanjoy): We should investigate if using gather loads and scatter stores +// can be used here have the same inner loop for both column-major and row-major +// matrix-vector products. +class ColumnMajorMatrixVectorProductEmitter + : public GemvConfig::User { + public: + class Config : public GemvConfig { + public: + explicit Config(PrimitiveType scalar_type, int64 tile_rows, int64 tile_cols, + int64 m, int64 k, bool has_addend) + : GemvConfig(/*name=*/"col_major_gemv", scalar_type, + /*tile_rows=*/tile_rows, /*tile_cols=*/tile_cols, /*m=*/m, + /*k=*/k, /*has_addend=*/has_addend) {} + }; + + ColumnMajorMatrixVectorProductEmitter(const Config& config, llvm::Value* lhs, + llvm::Value* rhs, llvm::Value* addend, + llvm::Value* result, + llvm::IRBuilder<>* b) + : config_(config), + lhs_(lhs), + rhs_(rhs), + addend_(addend), + result_(result), + b_(b), + ksl_(b_), + vsl_(config.scalar_type(), /*vector_size=*/config.tile_rows(), b_, "") { + CHECK(tile_rows() > 0 && IsPowerOfTwo(static_cast(tile_rows()))); + CHECK(!has_addend() || addend != nullptr); + } + + void Emit(); + + const Config& config() const { return config_; } + + private: + void EmitOuterLoopBody(llvm::Value* column, int64 column_count, + bool is_first_column); + + MemoryTile GetLhsMemoryTile(llvm::Value* column_start, int64 column_count) { + return MemoryTile(&vsl_, b_, /*matrix=*/lhs_, + /*matrix_size_along_minor_dim=*/m(), + /*major_dim_offset=*/column_start, + /*tile_size_along_major_dim=*/column_count); + } + + // Load a tile of values from the RHS. For the RHS a "tile" is a contiguous + // sequence of `count` values, each one broadcasted to the vector width. + std::vector LoadRhsTile(llvm::Value* offset, int64 count) { + llvm::Value* base_pointer = vsl_.ComputeOffsetPointer(rhs_, offset); + std::vector result; + result.reserve(count); + for (int64 i = 0; i < count; i++) { + result.push_back(vsl_.LoadBroadcast(base_pointer, i)); + } + return result; + } + + void EmitInnerLoopTiled(MemoryTile* lhs_memory_tile, + const std::vector& rhs_tile, + int64 columns, bool is_first_column); + + void EmitInnerLoopEpilogue(llvm::Value* current_tile_col, int64 columns, + bool is_first_tiled_column); + + Config config_; + llvm::Value* lhs_; + llvm::Value* rhs_; + llvm::Value* addend_; + llvm::Value* result_; + llvm::IRBuilder<>* b_; + KernelSupportLibrary ksl_; + VectorSupportLibrary vsl_; +}; + +void ColumnMajorMatrixVectorProductEmitter::EmitOuterLoopBody( + llvm::Value* column, int64 column_count, bool is_first_column) { + MemoryTile lhs_memory_tile = GetLhsMemoryTile(/*column_start=*/column, + /*column_count=*/column_count); + + std::vector rhs_tile = + LoadRhsTile(column, /*count=*/column_count); + EmitInnerLoopTiled(&lhs_memory_tile, rhs_tile, + /*columns=*/column_count, is_first_column); + EmitInnerLoopEpilogue(column, /*columns=*/column_count, is_first_column); +} + +void ColumnMajorMatrixVectorProductEmitter::Emit() { + // See the comment on the class declaration for the algorithm used here. + int64 column_remainder = k() % tile_cols(); + int64 column_limit = k() - column_remainder; + + ksl_.For("dot.outer.tiled", + /*start=*/0, /*end=*/column_limit, /*step=*/tile_cols(), + [&](llvm::Value* column, bool is_first_column) { + EmitOuterLoopBody(column, tile_cols(), is_first_column); + }); + + if (column_remainder != 0) { + EmitOuterLoopBody(b_->getInt64(column_limit), column_remainder, + column_limit == 0); + } +} + +void ColumnMajorMatrixVectorProductEmitter::EmitInnerLoopTiled( + MemoryTile* lhs_memory_tile, const std::vector& rhs_tile, + int64 columns, bool is_first_column) { + int64 row_limit = m() - (m() % tile_rows()); + + ksl_.For("dot.inner.tiled", /*start=*/0, /*end=*/row_limit, + /*step=*/tile_rows(), [&](llvm::Value* row) { + std::vector lhs_tile = + lhs_memory_tile->LoadTile(/*minor_dim_offset=*/row); + llvm::Value* accumulator = + is_first_column ? (addend_ ? vsl_.LoadVector(addend_, row) + : vsl_.GetZeroVector()) + : vsl_.LoadVector(result_, row); + for (int i = 0; i < columns; i++) { + accumulator = vsl_.MulAdd(lhs_tile[i], rhs_tile[i], accumulator); + } + vsl_.StoreVector(accumulator, result_, row); + }); +} + +void ColumnMajorMatrixVectorProductEmitter::EmitInnerLoopEpilogue( + llvm::Value* current_tile_col, int64 columns, bool is_first_tiled_column) { + int64 row_start = m() - (m() % tile_rows()); + if (row_start == m()) { + return; + } + + llvm::Value* columns_llvm = b_->getInt64(columns); + + // for (col = current_tile_col; col < (columns + current_tile_col); col++) + // for (row = row_start, row < m_; row++) { + // result[row] += lhs[row, col] * rhs[col] + // // Also take into account that if col is 0 then result[row] is not + // // initialized. + // } + + ksl_.For( + "dot.inner.epilg.outer", /*start=*/current_tile_col, + /*end=*/b_->CreateAdd(columns_llvm, current_tile_col), + /*step=*/1, /*peel_first_iteration=*/false, + [&](llvm::Value* col, llvm::Value* is_first_scalar_col) { + llvm::Value* rhs_element = vsl_.LoadScalar(rhs_, col); + llvm::Value* total_offset = b_->CreateMul(col, b_->getInt64(m())); + llvm::Value* lhs_base_pointer = + vsl_.ComputeOffsetPointer(lhs_, total_offset); + ksl_.For( + "dot.inner.epilg.inner", /*start=*/row_start, /*end=*/m(), + /*step=*/1, [&](llvm::Value* scalar_row) { + llvm::Value* product = vsl_.Mul( + vsl_.LoadScalar(lhs_base_pointer, scalar_row), rhs_element); + llvm::Value* setting_result_first_time = b_->CreateAnd( + is_first_scalar_col, b_->getInt1(is_first_tiled_column)); + ksl_.If( + setting_result_first_time, + /*true_block_generator=*/ + [&]() { + if (addend_) { + vsl_.StoreScalar( + vsl_.Add(vsl_.LoadScalar(addend_, scalar_row), + product), + result_, scalar_row); + } else { + vsl_.StoreScalar(product, result_, scalar_row); + } + }, + /*false_block_generator=*/ + [&]() { + vsl_.StoreScalar( + vsl_.Add(vsl_.LoadScalar(result_, scalar_row), product), + result_, scalar_row); + }); + }); + }); +} + +// Computes a dot product between "[M,K]{1,0} lhs" with a [K,1] vector (the +// layout of the vector does not matter). This implementation uses a tiling +// scheme to improve performance. +// +// We logically separate the LHS matrix into four segments: +// +// +----------------------+---+ +// | | | +// | | | +// | A | B | +// | | | +// | | | +// | | | +// +----------------------+---+ +// | C | D | +// +----------------------+---+ +// +// where A is the largest submatrix of the LHS that can be evenly dividied into +// tiles. For each tile in A, assuming tile_rows_ == tile_cols_ == 4, we have: +// +// +---+---+---+---+ +// |M00|M10|M20|M30| +// +---+---+---+---+ +--+--+--+--+ +// |M01|M11|M21|M31| and |V0|V1|V2|V3| +// +---+---+---+---+ +--+--+--+--+ +// |M02|M12|M22|M32| +// +---+---+---+---+ +// |M03|M13|M23|M33| +// +---+---+---+---+ +// +// (Legend: rows are horizontal and columns are vertical; and each row is one +// llvm::Value of a vector type) +// +// where: +// +// a. The left tile is loaded from the row major left matrix. +// b. The right vector is loaded from the RHS vector. +// +// We keep 4 vector accumulators accumulating the following four vector +// expressions as we iterate over the row dimension: +// +// +------+------+------+------+ +// |M0I*V0|M1I*V1|M2I*V2|M3I*V3| for I in [0,4) +// +------+------+------+------+ +// +// In the end we do a horizontal reduction over these 4 vector accumulators to +// get 4 values in the result vector. +// +// We have an inner epilogue loop to deal with the "B" sub-matrix and an outer +// epilogue loop to deal with the C,D submatrix. +class RowMajorMatrixVectorProductEmitter + : public GemvConfig::User { + public: + class Config : public GemvConfig { + public: + explicit Config(PrimitiveType scalar_type, int64 tile_rows, int64 tile_cols, + int64 m, int64 k, bool has_addend) + : GemvConfig(/*name=*/"row_major_gemv", scalar_type, + /*tile_rows=*/tile_rows, /*tile_cols=*/tile_cols, /*m=*/m, + /*k=*/k, /*has_addend=*/has_addend) {} + }; + + RowMajorMatrixVectorProductEmitter(const Config& config, llvm::Value* lhs, + llvm::Value* rhs, llvm::Value* addend, + llvm::Value* result, llvm::IRBuilder<>* b) + : config_(config), + lhs_(lhs), + rhs_(rhs), + addend_(addend), + result_(result), + b_(b), + ksl_(b_), + vsl_(scalar_type(), /*vector_size=*/tile_cols(), b_, "") { + CHECK(tile_cols() > 0 && IsPowerOfTwo(static_cast(tile_cols()))); + CHECK(!has_addend() || addend != nullptr); + } + + void Emit(); + + const Config& config() const { return config_; } + + private: + MemoryTile GetLhsMemoryTile(llvm::Value* row_start, int64 row_count) { + return MemoryTile(&vsl_, b_, /*matrix=*/lhs_, + /*matrix_size_along_minor_dim=*/k(), + /*major_dim_offset=*/row_start, + /*tile_size_along_major_dim=*/row_count); + } + + void EmitOuterLoopBody(llvm::Value* row, int64 row_count); + + void EmitInnerLoopTiled(MemoryTile* lhs_memory_tile, int64 rows, + std::vector* vector_accumulators); + + void EmitInnerLoopEpilogue(llvm::Value* current_tile_row, int64 rows, + std::vector* scalar_accumulators); + + Config config_; + llvm::Value* lhs_; + llvm::Value* rhs_; + llvm::Value* addend_; + llvm::Value* result_; + llvm::IRBuilder<>* b_; + KernelSupportLibrary ksl_; + VectorSupportLibrary vsl_; +}; + +void RowMajorMatrixVectorProductEmitter::EmitOuterLoopBody(llvm::Value* row, + int64 row_count) { + MemoryTile lhs_memory_tile = GetLhsMemoryTile(/*row_start=*/row, + /*row_count=*/row_count); + std::vector vector_accumulators; + std::vector scalar_accumulators; + for (int i = 0; i < row_count; i++) { + vector_accumulators.emplace_back(&vsl_, vsl_.GetZeroVector()); + scalar_accumulators.emplace_back(&vsl_, vsl_.GetZeroScalar()); + } + EmitInnerLoopTiled(&lhs_memory_tile, /*rows=*/row_count, + &vector_accumulators); + EmitInnerLoopEpilogue(/*current_tile_row=*/row, /*rows=*/row_count, + &scalar_accumulators); + + std::vector accumulator_values; + std::transform( + vector_accumulators.begin(), vector_accumulators.end(), + std::back_inserter(accumulator_values), + [](const VectorVariable& vector_var) { return vector_var.Get(); }); + + std::vector horizontal_sums; + if (row_count == vsl_.vector_size()) { + if (addend_) { + horizontal_sums = vsl_.ComputeHorizontalSums( + std::move(accumulator_values), vsl_.LoadVector(addend_, row)); + } else { + horizontal_sums = + vsl_.ComputeHorizontalSums(std::move(accumulator_values)); + } + } else { + horizontal_sums = vsl_.ComputeHorizontalSums(std::move(accumulator_values)); + } + + for (int i = 0; i < row_count; i++) { + llvm::Value* result_value = + vsl_.Add(horizontal_sums[i], scalar_accumulators[i].Get()); + llvm::Value* offset = b_->CreateAdd(b_->getInt64(i), row); + if (addend_ && row_count != vsl_.vector_size()) { + result_value = vsl_.Add(vsl_.LoadScalar(addend_, offset), result_value); + } + vsl_.StoreScalar(result_value, result_, offset); + } +} + +void RowMajorMatrixVectorProductEmitter::Emit() { + // See the comment on the class declaration for the algorithm used here. + int64 row_remainder = m() % tile_rows(); + int64 row_limit = m() - row_remainder; + + ksl_.For("dot.outer.tiled", + /*start=*/0, /*end=*/row_limit, /*step=*/tile_rows(), + [&](llvm::Value* row) { EmitOuterLoopBody(row, tile_rows()); }); + + if (row_remainder != 0) { + EmitOuterLoopBody(b_->getInt64(row_limit), row_remainder); + } +} + +void RowMajorMatrixVectorProductEmitter::EmitInnerLoopTiled( + MemoryTile* lhs_memory_tile, int64 rows, + std::vector* vector_accumulators) { + int64 column_limit = k() - (k() % tile_cols()); + + ksl_.For("dot.inner.tiled", /*start=*/0, /*end=*/column_limit, + /*step=*/tile_cols(), [&](llvm::Value* col) { + std::vector lhs_tile = + lhs_memory_tile->LoadTile(/*minor_dim_offset=*/col); + llvm::Value* rhs_value = vsl_.LoadVector(rhs_, col); + for (int i = 0; i < rows; i++) { + llvm::Value* old_sum = (*vector_accumulators)[i].Get(); + (*vector_accumulators)[i].Set( + vsl_.Add(old_sum, vsl_.Mul(rhs_value, lhs_tile[i]))); + } + }); +} + +void RowMajorMatrixVectorProductEmitter::EmitInnerLoopEpilogue( + llvm::Value* current_tile_row, int64 rows, + std::vector* scalar_accumulators) { + int64 column_start = k() - (k() % tile_cols()); + if (column_start == k()) { + return; + } + + for (int r = 0; r < rows; r++) { + llvm::Value* total_offset = b_->CreateMul( + b_->CreateAdd(b_->getInt64(r), current_tile_row), b_->getInt64(k())); + llvm::Value* lhs_base_pointer = + vsl_.ComputeOffsetPointer(lhs_, total_offset); + ksl_.For("dot.inner.epilg.inner", /*start=*/column_start, /*end=*/k(), + /*step=*/1, [&](llvm::Value* scalar_col) { + llvm::Value* product = + vsl_.Mul(vsl_.LoadScalar(lhs_base_pointer, scalar_col), + vsl_.LoadScalar(rhs_, scalar_col)); + llvm::Value* old_value = (*scalar_accumulators)[r].Get(); + (*scalar_accumulators)[r].Set(vsl_.Add(old_value, product)); + }); + } +} + +// This class implements a tiled matrix multiplication algorithm, intended for +// multiplying small matrices that don't need cache tiling. +// +// In the future this can be used as the innermost GEBP loop in a GEMM kernel as +// described in "Goto, Kazushige, and Robert A. Geijn. "Anatomy of +// high-performance matrix multiplication." ACM Transactions on Mathematical +// Software (TOMS) 34.3 (2008): 12.". +// +// This only supports canonical dot operations (i.e. where the lhs contraction +// dimension is 1 and the rhs contraction dimension is 0) over row major +// matrices. +class TiledSmallGemmEmitter { + public: + // Describe the dimensions of the kernel. + class Dimensions { + public: + explicit Dimensions(int64 m, int64 k, int64 n) : m_(m), k_(k), n_(n) {} + + int64 m() const { return m_; } + int64 k() const { return k_; } + int64 n() const { return n_; } + + string ToString() const { return absl::StrCat(m(), "x", k(), "x", n()); } + + private: + const int64 m_; + const int64 k_; + const int64 n_; + }; + + // Represents the configuration of the emitter. The LLVM IR emitted by the + // emitter, modulo the LLVM values holding the input and output buffers, must + // be a function of the instance of `Config` passed to it. + // + // `dims` holds the matrix multiplication dimensions. + // + // `max_vectorization_width` is the maximum vector width (i.e. the width of + // the largest vector register we will use). This can be larger than the + // largest vector register supported by the machine -- LLVM will legalize + // these large vector widths into legally sized vectors. + // + // `max_vector_count` is the maximum number of vectors of size + // `max_vectorization_width` that we will attempt to process at once. + // + // `min_vectorization_width` is the smallest vector width the emitter will use + // -- below that it will devolve to using a scalar loop. + // + // The innermost reduction loop executes the matrix multiply in tiles of size + // [`tile_size_m`, `tile_size_k`] from the LHS and [`tile_size_k`, + // ] in the RHS. + class Config { + public: + explicit Config(PrimitiveType scalar_type, Dimensions dims, + int64 max_vectorization_width, int64 max_vector_count, + int64 min_vectorization_width, int64 tile_size_m, + int64 tile_size_k) + : scalar_type_(scalar_type), + dims_(dims), + max_vectorization_width_(max_vectorization_width), + max_vector_count_(max_vector_count), + min_vectorization_width_(min_vectorization_width), + tile_size_m_(tile_size_m), + tile_size_k_(tile_size_k) {} + + string GetCacheKey() const { + return absl::StrCat("gemm_", PrimitiveType_Name(scalar_type()), "_", + dims().ToString(), "_", max_vectorization_width(), + "_", min_vectorization_width(), "_", tile_size_m(), + "_", tile_size_k()); + } + + PrimitiveType scalar_type() const { return scalar_type_; } + Dimensions dims() const { return dims_; } + int64 max_vectorization_width() const { return max_vectorization_width_; } + int64 max_vector_count() const { return max_vector_count_; } + int64 min_vectorization_width() const { return min_vectorization_width_; } + + int64 tile_size_m() const { return tile_size_m_; } + int64 tile_size_k() const { return tile_size_k_; } + + private: + PrimitiveType scalar_type_; + Dimensions dims_; + int64 max_vectorization_width_; + int64 max_vector_count_; + int64 min_vectorization_width_; + int64 tile_size_m_; + int64 tile_size_k_; + }; + + // Creates an instance of TiledSmallGemmEmitter that matrix-multiplies + // `lhs` with `rhs` and stores the result in `result`. + explicit TiledSmallGemmEmitter(Config config, llvm::Value* lhs, + llvm::Value* rhs, llvm::Value* result, + llvm::IRBuilder<>* b) + : lhs_(lhs), + rhs_(rhs), + result_(result), + config_(config), + b_(b), + ksl_(b_) { + CHECK(max_vectorization_width() > 0 && + IsPowerOfTwo(static_cast(max_vectorization_width()))); + CHECK_GT(max_vector_count(), 0); + CHECK(min_vectorization_width() > 0 && + IsPowerOfTwo(static_cast(min_vectorization_width()))); + CHECK_GE(max_vectorization_width(), min_vectorization_width()); + CHECK_GT(tile_size_k(), 0); + } + + void Emit(); + + private: + // The HandleResiduesOnX helpers split the iteration space for dimension X + // into a multiple of the tile size on dimension X and an epilogue. These + // helpers ultimately call into `EmitTiledGemm` for emitting the + // tiled GEMM kernel. + + void HandleResiduesOnN(); + void HandleResiduesOnK(VectorSupportLibrary* vsl, llvm::Value* n_start, + llvm::Value* n_end); + void HandleResiduesOnM(VectorSupportLibrary* vsl, int64 tile_size_k, + llvm::Value* k_start, llvm::Value* k_end, + llvm::Value* n_start, llvm::Value* n_end); + + // This emits a tiled GEMM kernel. For a detailed description see the comment + // on the implementation. + void EmitTiledGemm(VectorSupportLibrary* vsl, int64 tile_size_k, + llvm::Value* k_start, llvm::Value* k_end, + llvm::Value* n_start, llvm::Value* n_end, + int64 tile_size_m, llvm::Value* m_start, + llvm::Value* m_end); + + llvm::Value* GetInt64(int64 value) { return b_->getInt64(value); } + + Config config() const { return config_; } + Dimensions dims() const { return config().dims(); } + + int64 max_vectorization_width() const { + return config().max_vectorization_width(); + } + int64 max_vector_count() const { return config().max_vector_count(); } + int64 min_vectorization_width() const { + return config().min_vectorization_width(); + } + int64 tile_size_m() const { return config().tile_size_m(); } + int64 tile_size_k() const { return config().tile_size_k(); } + PrimitiveType scalar_type() const { return config().scalar_type(); } + + llvm::Value* lhs_; + llvm::Value* rhs_; + llvm::Value* result_; + Config config_; + + llvm::IRBuilder<>* b_; + KernelSupportLibrary ksl_; +}; + +void TiledSmallGemmEmitter::Emit() { HandleResiduesOnN(); } + +void TiledSmallGemmEmitter::HandleResiduesOnN() { + // We can only iterate the `n` dimension for an extent that is divisible by + // the vectorization width. So we emit an outer loop that first processes the + // largest extent in `n` that is divisible by max_vectorization_width, then + // the largest remaining extent that is divisible by max_vectorization_width / + // 2 etc. + + int64 current_vectorization_width = + max_vector_count() * max_vectorization_width(); + int64 current_vector_count = max_vector_count(); + + int64 n_start = 0; + while (n_start != dims().n() && + current_vectorization_width >= min_vectorization_width()) { + int64 n_end = dims().n() - (dims().n() % current_vectorization_width); + if (n_start != n_end) { + VectorSupportLibrary vsl(scalar_type(), current_vectorization_width, b_, + "gemm"); + HandleResiduesOnK(&vsl, GetInt64(n_start), GetInt64(n_end)); + n_start = n_end; + } + if (current_vector_count == 1) { + current_vectorization_width /= 2; + } else { + current_vector_count--; + current_vectorization_width = + current_vector_count * max_vectorization_width(); + } + } + + if (n_start != dims().n()) { + VectorSupportLibrary vsl(scalar_type(), 1, b_, "gemm"); + ksl_.For("epi.n", n_start, dims().n(), 1, [&](llvm::Value* n_i) { + llvm::Value* n_i_next = b_->CreateAdd(n_i, b_->getInt64(1)); + HandleResiduesOnK(&vsl, n_i, n_i_next); + }); + } +} + +void TiledSmallGemmEmitter::HandleResiduesOnK(VectorSupportLibrary* vsl, + llvm::Value* n_start, + llvm::Value* n_end) { + int64 k_start = 0; + int64 k_end = dims().k() - (dims().k() % tile_size_k()); + if (k_end != k_start) { + HandleResiduesOnM(vsl, tile_size_k(), GetInt64(k_start), GetInt64(k_end), + n_start, n_end); + k_start = k_end; + } + + if (k_start != dims().k()) { + HandleResiduesOnM(vsl, dims().k() - k_start, GetInt64(k_start), + GetInt64(dims().k()), n_start, n_end); + } +} + +void TiledSmallGemmEmitter::HandleResiduesOnM( + VectorSupportLibrary* vsl, int64 tile_size_k, llvm::Value* k_start, + llvm::Value* k_end, llvm::Value* n_start, llvm::Value* n_end) { + const int64 m_end = dims().m() - dims().m() % tile_size_m(); + EmitTiledGemm(vsl, tile_size_k, k_start, k_end, n_start, n_end, tile_size_m(), + GetInt64(0), GetInt64(m_end)); + + if (m_end != dims().m()) { + EmitTiledGemm(vsl, tile_size_k, k_start, k_end, n_start, n_end, + dims().m() - m_end, GetInt64(m_end), GetInt64(dims().m())); + } +} + +// The loop structure is: +// +// Iterate over dimension M as m: +// Iterate over dimension N as n: +// Iterate over dimension K as k: +// OutputTile[m,n] += Dot(LhsTile[m,k], RhsTile[k,n]) +// +// I.e. a just a tiled version of a "naive" GEMM. +// +// The tiling scheme is as follows: +// +// Let the LHS be: +// +// +----+----+----+ +// | a0 | b0 | c0 | . +// +----+----+----+ . +// | a1 | b1 | c1 | . +// +----+----+----+ +// .. .. +// +// and the RHS be: +// +// +----+----+----+----+ +// | p0 | p1 | p2 | p3 | . +// +----+----+----+----+ . +// | q0 | q1 | q2 | q3 | . +// +----+----+----+----+ +// | r0 | r1 | r2 | r3 | . +// +----+----+----+----+ . +// ...... ...... +// +// and let tile_size_m=2, tile_size_k=3 and the vector width (implicitly denoted +// by `vsl`) be 4. Then we want to matrix multiply this tile to get a [2,4] +// matrix that we can increment the result matrix by. +// +// First broadcast the rows row in LHS to 3 vectors of width 4, giving us a rank +// 3 array, L, of dimension [2,3,4]: +// +// L[0,_,_] * L[1,_,_] +// * +// +----+----+----+----+ * +----+----+----+----+ +// | a0 | a0 | a0 | a0 | * | a1 | a1 | a1 | a1 | +// +----+----+----+----+ * +----+----+----+----+ +// | b0 | b0 | b0 | b0 | * | b1 | b1 | b1 | b1 | +// +----+----+----+----+ * +----+----+----+----+ +// | c0 | c0 | c0 | c0 | * | c1 | c1 | c1 | c1 | +// +----+----+----+----+ * +----+----+----+----+ +// +// +// Then we FMA L[0,_,_] with the RHS to get the first row of the result and +// L[1,_,_] with the RHS to get the second row of the result. For example, +// L[0,_,_] is computed as: +// +// +----+----+----+----+ +----+----+----+----+ +// | a0 | a0 | a0 | a0 | * | p0 | p1 | p2 | p3 | + +// +----+----+----+----+ +----+----+----+----+ +// +// +----+----+----+----+ +----+----+----+----+ +// | b0 | b0 | b0 | b0 | * | q0 | q1 | q2 | q3 | + +// +----+----+----+----+ +----+----+----+----+ +// +// +----+----+----+----+ +----+----+----+----+ +// | c0 | c0 | c0 | c0 | * | r0 | r1 | r2 | r3 | +// +----+----+----+----+ +----+----+----+----+ +// +// to get: +// +// +-------------------+-------------------+-------------------+--------- +// | a0*p0+b0*q0+c0*r0 | a0*p1+b0*q1+c0*r1 | a0*p2+b0*q2+c0*r2 | ... +// +-------------------+-------------------+-------------------+--------- +void TiledSmallGemmEmitter::EmitTiledGemm( + VectorSupportLibrary* vsl, int64 tile_size_k, llvm::Value* k_start, + llvm::Value* k_end, llvm::Value* n_start, llvm::Value* n_end, + int64 tile_size_m, llvm::Value* m_start, llvm::Value* m_end) { + ksl_.For("dot.m", m_start, m_end, tile_size_m, [&](llvm::Value* m_i) { + MemoryTile result_memory_tile(vsl, b_, /*matrix=*/result_, + /*matrix_size_along_minor_dim=*/dims().n(), + /*major_dim_offset=*/m_i, + /*tile_size_along_major_dim=*/tile_size_m); + MemoryTile lhs_memory_tile(vsl, b_, /*matrix=*/lhs_, + /*matrix_size_along_minor_dim=*/dims().k(), + /*major_dim_offset=*/m_i, + /*tile_size_along_major_dim=*/tile_size_m); + ksl_.For( + "dot.n", n_start, n_end, vsl->vector_size(), [&](llvm::Value* n_i) { + TileVariable result_tile_var(vsl, result_memory_tile.LoadTile(n_i)); + ksl_.For("dot.k", k_start, k_end, tile_size_k, [&](llvm::Value* k_i) { + MemoryTile rhs_memory_tile(vsl, b_, rhs_, dims().n(), k_i, + tile_size_k); + std::vector> lhs_tile = + lhs_memory_tile.LoadBroadcastTile(k_i, tile_size_k); + std::vector rhs_tile = rhs_memory_tile.LoadTile(n_i); + std::vector result_tile = result_tile_var.Get(); + for (int64 r_m_i = 0; r_m_i < tile_size_m; r_m_i++) { + for (int64 r_k_i = 0; r_k_i < tile_size_k; r_k_i++) { + result_tile[r_m_i] = + vsl->MulAdd(lhs_tile[r_m_i][r_k_i], rhs_tile[r_k_i], + result_tile[r_m_i]); + } + } + result_tile_var.Set(result_tile); + }); + + result_memory_tile.StoreTile(result_tile_var.Get(), n_i); + }); + }); +} + +} // namespace + +void EmitRowMajorGemv(PrimitiveType scalar_type, int64 tile_rows, + int64 tile_cols, int64 m, int64 k, llvm::Value* lhs, + llvm::Value* rhs, llvm::Value* addend, + llvm::Value* result, llvm::IRBuilder<>* b, + bool enable_fast_math, bool optimize_for_size) { + RowMajorMatrixVectorProductEmitter::Config config( + /*scalar_type=*/scalar_type, + /*tile_rows=*/tile_rows, /*tile_cols=*/tile_cols, + /*m=*/m, /*k=*/k, /*has_addend=*/addend != nullptr); + + KernelSupportLibrary::EmitAndCallOutlinedKernel( + /*enable_fast_math=*/enable_fast_math, + /*optimize_for_size=*/optimize_for_size, b, config.GetCacheKey(), lhs, + rhs, addend, result, + [&](llvm::Value* lhs, llvm::Value* rhs, llvm::Value* addend, + llvm::Value* result) { + RowMajorMatrixVectorProductEmitter emitter(config, lhs, rhs, addend, + result, b); + emitter.Emit(); + }); +} + +void EmitColumnMajorGemv(PrimitiveType scalar_type, int64 tile_rows, + int64 tile_cols, int64 m, int64 k, llvm::Value* lhs, + llvm::Value* rhs, llvm::Value* addend, + llvm::Value* result, llvm::IRBuilder<>* b, + bool enable_fast_math, bool optimize_for_size) { + ColumnMajorMatrixVectorProductEmitter::Config config( + /*scalar_type=*/scalar_type, + /*tile_rows=*/tile_rows, /*tile_cols=*/tile_cols, + /*m=*/m, /*k=*/k, /*has_addend=*/addend != nullptr); + + KernelSupportLibrary::EmitAndCallOutlinedKernel( + /*enable_fast_math=*/enable_fast_math, + /*optimize_for_size=*/optimize_for_size, b, config.GetCacheKey(), lhs, + rhs, addend, result, + [&](llvm::Value* lhs, llvm::Value* rhs, llvm::Value* addend, + llvm::Value* result) { + ColumnMajorMatrixVectorProductEmitter emitter(config, lhs, rhs, addend, + result, b); + emitter.Emit(); + }); +} + +void EmitSmallGemm(PrimitiveType scalar_type, int64 m, int64 k, int64 n, + int64 max_vectorization_width, int64 max_vector_count, + int64 min_vectorization_width, int64 tile_size_m, + int64 tile_size_k, llvm::Value* lhs, llvm::Value* rhs, + llvm::Value* result, llvm::IRBuilder<>* b, + bool enable_fast_math, bool optimize_for_size) { + TiledSmallGemmEmitter::Config config( + /*scalar_type=*/scalar_type, + TiledSmallGemmEmitter::Dimensions{/*m=*/m, /*k=*/k, /*n=*/n}, + /*max_vectorization_width=*/max_vectorization_width, + /*max_vector_count=*/max_vector_count, + /*min_vectorization_width=*/min_vectorization_width, + /*tile_size_m=*/tile_size_m, /*tile_size_k=*/tile_size_k); + + KernelSupportLibrary::EmitAndCallOutlinedKernel( + /*enable_fast_math=*/enable_fast_math, + /*optimize_for_size=*/optimize_for_size, b, config.GetCacheKey(), lhs, + rhs, result, + [&](llvm::Value* lhs, llvm::Value* rhs, llvm::Value* result) { + TiledSmallGemmEmitter small_gemm_emitter(config, /*lhs=*/lhs, + /*rhs=*/rhs, + /*result=*/result, b); + small_gemm_emitter.Emit(); + }); +} + +} // namespace cpu +} // namespace xla diff --git a/tensorflow/compiler/xla/service/cpu/tiled_dot_emitter.h b/tensorflow/compiler/xla/service/cpu/tiled_dot_emitter.h new file mode 100644 index 0000000000..0a82326cc3 --- /dev/null +++ b/tensorflow/compiler/xla/service/cpu/tiled_dot_emitter.h @@ -0,0 +1,55 @@ +/* Copyright 2018 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_COMPILER_XLA_SERVICE_CPU_TILED_DOT_EMITTER_H_ +#define TENSORFLOW_COMPILER_XLA_SERVICE_CPU_TILED_DOT_EMITTER_H_ + +#include "llvm/IR/IRBuilder.h" +#include "tensorflow/compiler/xla/xla_data.pb.h" +#include "tensorflow/core/platform/types.h" + +namespace xla { +namespace cpu { + +// These routines emit LLVM IR implementing tiled GEMM and GEMV routines. + +void EmitRowMajorGemv(PrimitiveType scalar_type, tensorflow::int64 tile_rows, + tensorflow::int64 tile_cols, tensorflow::int64 m, + tensorflow::int64 k, llvm::Value* lhs, llvm::Value* rhs, + llvm::Value* addend, llvm::Value* result, + llvm::IRBuilder<>* b, bool enable_fast_math, + bool optimize_for_size); + +void EmitColumnMajorGemv(PrimitiveType scalar_type, tensorflow::int64 tile_rows, + tensorflow::int64 tile_cols, tensorflow::int64 m, + tensorflow::int64 k, llvm::Value* lhs, + llvm::Value* rhs, llvm::Value* addend, + llvm::Value* result, llvm::IRBuilder<>* b, + bool enable_fast_math, bool optimize_for_size); + +void EmitSmallGemm(PrimitiveType scalar_type, tensorflow::int64 m, + tensorflow::int64 k, tensorflow::int64 n, + tensorflow::int64 max_vectorization_width, + tensorflow::int64 max_vector_count, + tensorflow::int64 min_vectorization_width, + tensorflow::int64 tile_size_m, tensorflow::int64 tile_size_k, + llvm::Value* lhs, llvm::Value* rhs, llvm::Value* result, + llvm::IRBuilder<>* b, bool enable_fast_math, + bool optimize_for_size); + +} // namespace cpu +} // namespace xla + +#endif // TENSORFLOW_COMPILER_XLA_SERVICE_CPU_TILED_DOT_EMITTER_H_ -- GitLab From 0ebce89605326c602b3dd4d8cfe3f3a29284b390 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Mon, 7 Jan 2019 14:39:51 -0800 Subject: [PATCH 438/622] Optimize test combinations to prevent timeout during testing. PiperOrigin-RevId: 228238454 --- tensorflow/contrib/distribute/python/BUILD | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tensorflow/contrib/distribute/python/BUILD b/tensorflow/contrib/distribute/python/BUILD index de6b6f1f84..7ee12812af 100644 --- a/tensorflow/contrib/distribute/python/BUILD +++ b/tensorflow/contrib/distribute/python/BUILD @@ -674,7 +674,9 @@ cuda_py_test( additional_deps = [ ":keras_correctness_test_lib", ], - shard_count = 16, + # Shard count is set to an odd number to distribute tasks across + # shards more evenly. + shard_count = 19, tags = [ "multi_and_single_gpu", "no_oss", # TODO(b/117919883): Fix python error. -- GitLab From 67a7f04d010c50fb3d6d1bde7793d3f8d350decf Mon Sep 17 00:00:00 2001 From: Russell Power Date: Mon, 7 Jan 2019 14:43:32 -0800 Subject: [PATCH 439/622] Graph partitioning: use a sensible hash function for device memory info. PiperOrigin-RevId: 228239154 --- tensorflow/core/BUILD | 2 ++ tensorflow/core/graph/graph_partition.cc | 44 +++++++++--------------- 2 files changed, 18 insertions(+), 28 deletions(-) diff --git a/tensorflow/core/BUILD b/tensorflow/core/BUILD index 5e6c8d4dd8..e6af9211b5 100644 --- a/tensorflow/core/BUILD +++ b/tensorflow/core/BUILD @@ -1750,6 +1750,7 @@ cc_library( cc_library( name = "mobile_additional_lib_deps", deps = tf_additional_lib_deps() + [ + "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/strings", ], @@ -2817,6 +2818,7 @@ tf_cuda_library( ":proto_text", ":protos_all_cc", "//third_party/eigen3", + "@com_google_absl//absl/container:flat_hash_map", ], ) diff --git a/tensorflow/core/graph/graph_partition.cc b/tensorflow/core/graph/graph_partition.cc index f213eb7c10..be0cac50a1 100644 --- a/tensorflow/core/graph/graph_partition.cc +++ b/tensorflow/core/graph/graph_partition.cc @@ -22,6 +22,7 @@ limitations under the License. #include #include +#include "absl/container/flat_hash_map.h" #include "tensorflow/core/framework/function.h" #include "tensorflow/core/framework/memory_types.h" #include "tensorflow/core/framework/node_def_builder.h" @@ -58,29 +59,24 @@ struct DupRecvKey { int src_output_slot; // Edge's src node output slot GraphDef* dst_graph; // Edge's dst node is in this subgraph bool recv_output_on_host; // The output of recv is on host -}; -struct DupRecvKeyHash { - size_t operator()(const DupRecvKey& k) const { - size_t h = Hash64(reinterpret_cast(&k.src_node_id), - sizeof(k.src_node_id), k.src_output_slot); - h = Hash64(reinterpret_cast(&k.dst_graph), sizeof(k.dst_graph), - h); - h = Hash64(reinterpret_cast(&k.recv_output_on_host), - sizeof(k.recv_output_on_host), h); - return h; + template + friend H AbslHashValue(H h, const DupRecvKey& c) { + return H::combine(std::move(h), c.src_node_id, c.src_output_slot, + reinterpret_cast(c.dst_graph), + c.recv_output_on_host); } -}; -struct DupRecvKeyEq { - bool operator()(const DupRecvKey& x, const DupRecvKey& y) const { - return (x.src_node_id == y.src_node_id) && - (x.src_output_slot == y.src_output_slot) && - (x.dst_graph == y.dst_graph) && - (x.recv_output_on_host == y.recv_output_on_host); - } + friend bool operator==(const DupRecvKey& x, const DupRecvKey& y); }; +bool operator==(const DupRecvKey& x, const DupRecvKey& y) { + return (x.src_node_id == y.src_node_id) && + (x.src_output_slot == y.src_output_slot) && + (x.dst_graph == y.dst_graph) && + (x.recv_output_on_host == y.recv_output_on_host); +} + // struct used to store the recvs, so that start times can be properly updated struct RecvInfo { NodeDef* recv; @@ -88,19 +84,11 @@ struct RecvInfo { int64 start_time; }; -typedef std::unordered_map - DupRecvTable; +typedef absl::flat_hash_map DupRecvTable; -struct PairIntHash { - public: - std::size_t operator()(const std::pair& x) const { - return std::hash()(x.first) ^ std::hash()(x.second); - } -}; // A map used to store memory types for the inputs/outputs of every node. // The key is a pair of ints consisting of a node id and input/output index. -typedef std::unordered_map, MemoryType, PairIntHash> - MemoryTypeMap; +typedef absl::flat_hash_map, MemoryType> MemoryTypeMap; // We collect the following information about the graph before performing // graph partitioning. -- GitLab From 106b3c183546d2f4bde8c209ef9d01ba0fb2904b Mon Sep 17 00:00:00 2001 From: Jiri Simsa Date: Mon, 7 Jan 2019 14:55:46 -0800 Subject: [PATCH 440/622] [tf.data] Implementation of a "standalone" C++ API that encapsulates TensorFlow runtime internals. The new API facilitates efficient execution of a dataset input pipeline (represented as a GraphDef) in C++. PiperOrigin-RevId: 228241484 --- tensorflow/core/common_runtime/data/BUILD | 35 ++++ .../core/common_runtime/data/standalone.cc | 128 ++++++++++++ .../core/common_runtime/data/standalone.h | 122 ++++++++++++ .../common_runtime/data/standalone_test.cc | 188 ++++++++++++++++++ 4 files changed, 473 insertions(+) create mode 100644 tensorflow/core/common_runtime/data/BUILD create mode 100644 tensorflow/core/common_runtime/data/standalone.cc create mode 100644 tensorflow/core/common_runtime/data/standalone.h create mode 100644 tensorflow/core/common_runtime/data/standalone_test.cc diff --git a/tensorflow/core/common_runtime/data/BUILD b/tensorflow/core/common_runtime/data/BUILD new file mode 100644 index 0000000000..124862dbb7 --- /dev/null +++ b/tensorflow/core/common_runtime/data/BUILD @@ -0,0 +1,35 @@ +package( + default_visibility = [ + "//tensorflow:internal", + "//tensorflow_models:__subpackages__", + ], +) + +licenses(["notice"]) # Apache 2.0 + +load("//tensorflow:tensorflow.bzl", "tf_cc_test") +load("//tensorflow/core:platform/default/build_config.bzl", "tf_protos_all") + +cc_library( + name = "standalone", + srcs = ["standalone.cc"], + hdrs = ["standalone.h"], + visibility = ["//visibility:public"], + deps = [ + "//tensorflow/core:core_cpu_internal", + "//tensorflow/core:framework", + "//tensorflow/core:lib", + "//tensorflow/core:session_options", + ], +) + +tf_cc_test( + name = "standalone_test", + srcs = ["standalone_test.cc"], + deps = [ + ":standalone", + "//tensorflow/core:all_kernels", + "//tensorflow/core:test", + "//tensorflow/core:test_main", + ] + tf_protos_all(), +) diff --git a/tensorflow/core/common_runtime/data/standalone.cc b/tensorflow/core/common_runtime/data/standalone.cc new file mode 100644 index 0000000000..b05bff566f --- /dev/null +++ b/tensorflow/core/common_runtime/data/standalone.cc @@ -0,0 +1,128 @@ +/* Copyright 2018 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/common_runtime/data/standalone.h" + +#include + +#include "tensorflow/core/common_runtime/device_factory.h" +#include "tensorflow/core/common_runtime/device_mgr.h" +#include "tensorflow/core/common_runtime/function.h" +#include "tensorflow/core/common_runtime/graph_runner.h" +#include "tensorflow/core/common_runtime/process_util.h" +#include "tensorflow/core/framework/dataset.h" +#include "tensorflow/core/graph/graph.h" +#include "tensorflow/core/graph/graph_constructor.h" +#include "tensorflow/core/public/version.h" +#include "tensorflow/core/util/ptr_util.h" + +namespace tensorflow { +namespace data { +namespace standalone { + +Status Iterator::GetNext(std::vector* outputs, bool* end_of_input) { + return iterator_->GetNext(ctx_.get(), outputs, end_of_input); +} + +Iterator::Iterator(IteratorBase* iterator, IteratorContext* ctx) + : iterator_(iterator), ctx_(ctx) {} + +Status Dataset::FromGraph(Params params, const GraphDef& graph_def, + const string& fetch_node, + std::unique_ptr* result) { + Graph graph(OpRegistry::Global()); + TF_RETURN_IF_ERROR(ImportGraphDef({}, graph_def, &graph, nullptr)); + + // Instantiate enough of the TensorFlow runtime to run `graph` on a single CPU + // device. + std::unique_ptr device_mgr = MakeUnique( + DeviceFactory::NewDevice("CPU", params.session_options, "")); + Device* device = device_mgr->ListDevices()[0]; + // Clone the `FunctionLibraryDefinition` to extend its lifetime extends beyond + // the lifetime of `graph`. + std::unique_ptr flib_def = + MakeUnique(graph.flib_def()); + std::unique_ptr pflr = + MakeUnique( + device_mgr.get(), Env::Default(), TF_GRAPH_DEF_VERSION, + flib_def.get(), OptimizerOptions{}, nullptr /* parent */); + + // Run graph up to `output_node` and extract the `DatasetBase` stored in the + // DT_VARIANT output tensor. + data::DatasetBase* dataset; + { + std::vector outputs; + GraphRunner graph_runner(device); + TF_RETURN_IF_ERROR(graph_runner.Run(&graph, pflr->GetFLR("/device:CPU:0"), + {}, {fetch_node}, &outputs)); + TF_RETURN_IF_ERROR(GetDatasetFromVariantTensor(outputs[0], &dataset)); + // NOTE(mrry): The dataset is currently owned by `outputs[0]`, so acquire an + // additional reference. + dataset->Ref(); + } + + std::unique_ptr pool( + NewThreadPoolFromSessionOptions(params.session_options)); + *result = + WrapUnique(new Dataset(dataset, device_mgr.release(), pflr.release(), + flib_def.release(), pool.release())); + return Status::OK(); +} // static + +Status Dataset::MakeIterator(std::unique_ptr* result) { + // Create an `IteratorContext`, which bundles together the necessary runtime + // support to create and get elements from an iterator. + std::unique_ptr ctx; + { + // NOTE(mrry): In the current API, an `IteratorContext` is always initially + // created from an `OpKernelContext*`, so we need to create a fake + // `OpKernelContext` with the appropriate subset of parameters. + OpKernelContext::Params op_params; + op_params.function_library = pflr_->GetFLR("/device:CPU:0"); + op_params.device = device_mgr_->ListDevices()[0]; + op_params.runner = &runner_; + OpKernelContext op_ctx(&op_params, 0); + IteratorContext::Params params(&op_ctx); + params.function_handle_cache = function_handle_cache_.get(); + ctx = MakeUnique(std::move(params)); + } + + // Create the iterator from the dataset. + std::unique_ptr iterator; + TF_RETURN_IF_ERROR(dataset_->MakeIterator(ctx.get(), "iterator", &iterator)); + + *result = WrapUnique(new Iterator(iterator.release(), ctx.release())); + + return Status::OK(); +} + +Dataset::Dataset(DatasetBase* dataset, DeviceMgr* device_mgr, + ProcessFunctionLibraryRuntime* pflr, + FunctionLibraryDefinition* flib_def, thread::ThreadPool* pool) + : dataset_(dataset), + device_mgr_(device_mgr), + flib_def_(flib_def), + pflr_(pflr), + pool_(pool) { + runner_ = [this](std::function c) { pool_->Schedule(std::move(c)); }; + function_handle_cache_ = + MakeUnique(pflr_->GetFLR("/device:CPU:0")); +} + +Dataset::~Dataset() { dataset_->Unref(); } + +} // namespace standalone +} // namespace data +} // namespace tensorflow diff --git a/tensorflow/core/common_runtime/data/standalone.h b/tensorflow/core/common_runtime/data/standalone.h new file mode 100644 index 0000000000..ecea5ba21d --- /dev/null +++ b/tensorflow/core/common_runtime/data/standalone.h @@ -0,0 +1,122 @@ +/* Copyright 2018 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_COMMON_RUNTIME_DATA_STANDALONE_H_ +#define TENSORFLOW_CORE_COMMON_RUNTIME_DATA_STANDALONE_H_ + +#include +#include "tensorflow/core/common_runtime/device_mgr.h" +#include "tensorflow/core/framework/dataset.h" +#include "tensorflow/core/framework/function_handle_cache.h" +#include "tensorflow/core/lib/core/threadpool.h" +#include "tensorflow/core/public/session_options.h" + +namespace tensorflow { +namespace data { +namespace standalone { + +// The purpose of the API in this file is to facilitate standalone execution of +// a tf.data input pipeline graph. +// +// The API exposes two abstractions -- a `Dataset` and an `Iterator` -- which +// encapsulate TensorFlow runtime. +// +// The `Dataset` abstraction represents an input pipeline as a collection +// of data sources and a logical plan of transformations that operate over the +// data. +// +// The `Iterator` abstraction represents an execution of an input pipeline that +// can be used to enumerate its elements. +// +// Example usage: +// +// // Create a `Dataset` by running the `graph_def` graph and fetching the +// // output of the `fetch_node` node. +// tensorflow::data:standalone::Dataset::Params params; +// std::unique_ptr dataset; +// Status s = tensorflow::data::standalone::Dataset::FromGraph( +// params, graph_def, fetch_node, &dataset); +// if (!s.ok()) { /* error handling */ } +// +// std::unique_ptr iterator; +// s = dataset->MakeIterator(&iterator); +// if (!s.ok()) { /* error handling */ } +// +// bool end_of_input = false; +// while (!end_of_input) { +// std::vector outputs; +// s = iterator->GetNext(&outputs, &end_of_input); +// if (!s.ok()) { /* error handling */ } +// if (!end_of_input) { /* output handling */ } +// } + +class Dataset; + +// Represents an execution of an input pipeline that can be used to enumerate +// its elements. +class Iterator { + public: + // Returns the next element of the input pipeline (if there is one) and an + // indication of whether the end of the input pipeline has been reached. + Status GetNext(std::vector* outputs, bool* end_of_input); + + private: + friend class Dataset; + + Iterator(IteratorBase* iterator, IteratorContext* ctx); + + std::unique_ptr iterator_; + std::unique_ptr ctx_; +}; + +// Represents an input pipeline as a collection of data sources and a logical +// plan of transformations that operate over the data. +class Dataset { + public: + // Parameters for `Dataset` creation (e.g. TensorFlow runtime configuration). + struct Params { + SessionOptions session_options; + }; + + // Creates a new `Dataset` instance by running the TensorFlow graph `graph` + // and fetching the output of the `fetch_node` node. + static Status FromGraph(Params params, const GraphDef& graph_def, + const string& fetch_node, + std::unique_ptr* result); + + ~Dataset(); + + // Creates an iterator for this dataset. + Status MakeIterator(std::unique_ptr* result); + + private: + Dataset(DatasetBase* dataset, DeviceMgr* device_mgr, + ProcessFunctionLibraryRuntime* pflr, + FunctionLibraryDefinition* flib_def, thread::ThreadPool* pool); + + DatasetBase* dataset_; // owned + std::unique_ptr device_mgr_; + std::unique_ptr flib_def_; + std::unique_ptr pflr_; + std::unique_ptr pool_; + std::unique_ptr function_handle_cache_; + std::function)> runner_; +}; + +} // namespace standalone +} // namespace data +} // namespace tensorflow + +#endif // TENSORFLOW_CORE_COMMON_RUNTIME_DATA_STANDALONE_H_ diff --git a/tensorflow/core/common_runtime/data/standalone_test.cc b/tensorflow/core/common_runtime/data/standalone_test.cc new file mode 100644 index 0000000000..7e7a7a9b61 --- /dev/null +++ b/tensorflow/core/common_runtime/data/standalone_test.cc @@ -0,0 +1,188 @@ +/* Copyright 2018 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 +#include + +#include "tensorflow/core/common_runtime/data/standalone.h" +#include "tensorflow/core/framework/graph.pb.h" +#include "tensorflow/core/lib/core/status_test_util.h" +#include "tensorflow/core/platform/test.h" + +namespace tensorflow { +namespace data { +namespace standalone { +namespace { + +constexpr const char* const kGraphProto = R"proto( + node { + name: "Const/_0" + op: "Const" + attr { + key: "dtype" + value { type: DT_INT64 } + } + attr { + key: "value" + value { + tensor { + dtype: DT_INT64 + tensor_shape {} + int64_val: 0 + } + } + } + } + node { + name: "Const/_1" + op: "Const" + attr { + key: "dtype" + value { type: DT_INT64 } + } + attr { + key: "value" + value { + tensor { + dtype: DT_INT64 + tensor_shape {} + int64_val: 10 + } + } + } + } + node { + name: "Const/_2" + op: "Const" + attr { + key: "dtype" + value { type: DT_INT64 } + } + attr { + key: "value" + value { + tensor { + dtype: DT_INT64 + tensor_shape {} + int64_val: 1 + } + } + } + } + node { + name: "RangeDataset/_3" + op: "RangeDataset" + input: "Const/_0" + input: "Const/_1" + input: "Const/_2" + attr { + key: "output_shapes" + value { list { shape { unknown_rank: true } } } + } + attr { + key: "output_types" + value { list { type: DT_INT64 } } + } + } + node { + name: "MapDataset/_4" + op: "MapDataset" + input: "RangeDataset/_3" + attr { + key: "Targuments" + value { list {} } + } + attr { + key: "f" + value { func { name: "Dataset_map__10" } } + } + attr { + key: "output_shapes" + value { list { shape {} } } + } + attr { + key: "output_types" + value { list { type: DT_INT64 } } + } + attr { + key: "preserve_cardinality" + value { b: false } + } + attr { + key: "use_inter_op_parallelism" + value { b: true } + } + } + library { + function { + signature { + name: "Dataset_map__10" + input_arg { name: "arg0" type: DT_INT64 } + output_arg { name: "mul" type: DT_INT64 } + description: "Wrapper for passing nested structures to and from tf.data functions." + } + node_def { + name: "mul_0" + op: "Mul" + input: "arg0" + input: "arg0" + attr { + key: "T" + value { type: DT_INT64 } + } + } + ret { key: "mul" value: "mul_0:z:0" } + } + } + versions { producer: 27 min_consumer: 12 } +)proto"; + +TEST(Scalar, Standalone) { + GraphDef graph_def; + protobuf::TextFormat::ParseFromString(kGraphProto, &graph_def); + struct TestCase { + string fetch_node; + std::vector expected_outputs; + }; + auto test_cases = { + TestCase{"RangeDataset/_3", {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}, + TestCase{"MapDataset/_4", {0, 1, 4, 9, 16, 25, 36, 49, 64, 81}}, + }; + for (auto test_case : test_cases) { + std::unique_ptr dataset; + auto s = Dataset::FromGraph({}, graph_def, test_case.fetch_node, &dataset); + TF_EXPECT_OK(s); + std::unique_ptr iterator; + s = dataset->MakeIterator(&iterator); + TF_EXPECT_OK(s); + bool end_of_input = false; + for (int num_outputs = 0; !end_of_input; ++num_outputs) { + std::vector outputs; + s = iterator->GetNext(&outputs, &end_of_input); + TF_EXPECT_OK(s); + if (!end_of_input) { + EXPECT_EQ(outputs[0].scalar()(), + test_case.expected_outputs[num_outputs]); + } else { + EXPECT_EQ(test_case.expected_outputs.size(), num_outputs); + } + } + } +} + +} // namespace +} // namespace standalone +} // namespace data +} // namespace tensorflow -- GitLab From 17efadd4556c242efaa68b30134d3e79d2412922 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Mon, 7 Jan 2019 14:57:23 -0800 Subject: [PATCH 441/622] Internal copybara changes. PiperOrigin-RevId: 228241768 --- tensorflow/lite/experimental/swift/BUILD | 1 + .../Configs/TensorFlowLite.tulsigen | 20 +++++++++---------- .../project.tulsiconf | 4 ++-- .../Base.lproj/Main.storyboard | 6 +++--- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/tensorflow/lite/experimental/swift/BUILD b/tensorflow/lite/experimental/swift/BUILD index 3bd288a1e1..53bcb0ecbd 100644 --- a/tensorflow/lite/experimental/swift/BUILD +++ b/tensorflow/lite/experimental/swift/BUILD @@ -83,6 +83,7 @@ ios_application( swift_library( name = "TensorFlowLiteAppLib", srcs = glob(["TestApps/TensorFlowLiteApp/TensorFlowLiteApp/*.swift"]), + module_name = "TensorFlowLiteAppLib", tags = ["manual"], deps = [ ":TensorFlowLite", diff --git a/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/Configs/TensorFlowLite.tulsigen b/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/Configs/TensorFlowLite.tulsigen index 4010fab49e..16bc6cbfe8 100644 --- a/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/Configs/TensorFlowLite.tulsigen +++ b/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/Configs/TensorFlowLite.tulsigen @@ -1,16 +1,16 @@ { "sourceFilters" : [ - "third_party/tensorflow/lite/experimental/c", - "third_party/tensorflow/lite/experimental/swift", - "third_party/tensorflow/lite/experimental/swift/Sources", - "third_party/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp", - "third_party/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj", - "third_party/tensorflow/lite/experimental/swift/Tests", + "tensorflow/lite/experimental/c", + "tensorflow/lite/experimental/swift", + "tensorflow/lite/experimental/swift/Sources", + "tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp", + "tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj", + "tensorflow/lite/experimental/swift/Tests", ], "buildTargets" : [ - "//third_party/tensorflow/lite/experimental/swift:TensorFlowLite", - "//third_party/tensorflow/lite/experimental/swift:TensorFlowLiteApp", - "//third_party/tensorflow/lite/experimental/swift:TensorFlowLiteTests", + "//tensorflow/lite/experimental/swift:TensorFlowLite", + "//tensorflow/lite/experimental/swift:TensorFlowLiteApp", + "//tensorflow/lite/experimental/swift:TensorFlowLiteTests", ], "projectName" : "TensorFlowLite", "optionSet" : { @@ -52,6 +52,6 @@ } }, "additionalFilePaths" : [ - "third_party/tensorflow/lite/experimental/swift/BUILD" + "tensorflow/lite/experimental/swift/BUILD" ] } diff --git a/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/project.tulsiconf b/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/project.tulsiconf index 14cff94453..82ac8aa381 100644 --- a/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/project.tulsiconf +++ b/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/project.tulsiconf @@ -8,7 +8,7 @@ }, "projectName" : "TensorFlowLite", "packages" : [ - "third_party/tensorflow/lite/experimental/swift" + "tensorflow/lite/experimental/swift" ], - "workspaceRoot" : "../../../../../.." + "workspaceRoot" : "../../../../.." } diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/Main.storyboard b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/Main.storyboard index d0e91d63c4..10cae6e855 100644 --- a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/Main.storyboard +++ b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/Main.storyboard @@ -1,17 +1,17 @@ - + - + - + -- GitLab From a0dfedcca7c766404735720b9d675e1a04f40350 Mon Sep 17 00:00:00 2001 From: Yunxing Dai Date: Mon, 7 Jan 2019 15:02:16 -0800 Subject: [PATCH 442/622] [XLA] Use DynamicDimensionInference in HloEvaluator - Use DynamicDimensionInference in HloEvaluator so that it can correctly evaluate GetDimensionSize. - Change ComputeConstant to first calculate DynamicDimensionInference and pass it into evaluator. PiperOrigin-RevId: 228242693 --- tensorflow/compiler/xla/service/BUILD | 2 + .../compiler/xla/service/hlo_evaluator.cc | 34 ++++++++++++++++ .../compiler/xla/service/hlo_evaluator.h | 12 ++++++ .../xla/service/hlo_evaluator_test.cc | 40 +++++++++++++++++-- tensorflow/compiler/xla/service/service.cc | 5 +++ 5 files changed, 89 insertions(+), 4 deletions(-) diff --git a/tensorflow/compiler/xla/service/BUILD b/tensorflow/compiler/xla/service/BUILD index 3c18c450e6..9bc6218f75 100644 --- a/tensorflow/compiler/xla/service/BUILD +++ b/tensorflow/compiler/xla/service/BUILD @@ -236,6 +236,7 @@ cc_library( ], hdrs = ["hlo_evaluator.h"], deps = [ + ":dynamic_dimension_inference", ":hlo", ":hlo_casting_utils", ":hlo_query", @@ -697,6 +698,7 @@ cc_library( ":compiler", ":computation_layout", ":device_memory_allocator", + ":dynamic_dimension_inference", ":executable", ":execution_tracker", ":hlo", diff --git a/tensorflow/compiler/xla/service/hlo_evaluator.cc b/tensorflow/compiler/xla/service/hlo_evaluator.cc index 3a97b56c66..b8d95fb244 100644 --- a/tensorflow/compiler/xla/service/hlo_evaluator.cc +++ b/tensorflow/compiler/xla/service/hlo_evaluator.cc @@ -361,6 +361,31 @@ Status HloEvaluator::HandleBitcast(HloInstruction* bitcast) { return Status::OK(); } +Status HloEvaluator::HandleGetDimensionSize( + HloInstruction* get_dimension_size) { + HloInstruction* operand = get_dimension_size->mutable_operand(0); + int64 dim = get_dimension_size->dimension(); + if (dynamic_dimension_inference_ == nullptr) { + return InvalidArgument( + "Evaluator cannot evaluate get_dimension_size without " + "set_dynamic_dimension_inference."); + } + HloInstruction* dynamic_size = + dynamic_dimension_inference_->GetDynamicSize(operand, {}, dim); + if (dynamic_size != nullptr) { + evaluated_[get_dimension_size] = + GetEvaluatedLiteralFor(dynamic_size).Clone(); + return Status::OK(); + } + + const Shape& shape = get_dimension_size->operand(0)->shape(); + Literal output(ShapeUtil::MakeShape(U32, {})); + output.PopulateWithValue( + static_cast(shape.dimensions(get_dimension_size->dimension()))); + evaluated_[get_dimension_size] = std::move(output); + return Status::OK(); +} + Status HloEvaluator::HandleParameter(HloInstruction* parameter) { CHECK_LT(parameter->parameter_number(), arg_literals_.size()); const Literal* input_literal = arg_literals_[parameter->parameter_number()]; @@ -1080,6 +1105,8 @@ Status HloEvaluator::HandleCall(HloInstruction* call) { } HloEvaluator embedded_evaluator; + embedded_evaluator.set_dynamic_dimension_inference( + dynamic_dimension_inference_); Literal result = embedded_evaluator.Evaluate(*computation, arg_literals) .ConsumeValueOrDie(); @@ -1113,6 +1140,8 @@ Status HloEvaluator::HandleFusion(HloInstruction* fusion) { } HloEvaluator embedded_evaluator; + embedded_evaluator.set_dynamic_dimension_inference( + dynamic_dimension_inference_); Literal result = embedded_evaluator.Evaluate(*readded_computation, arg_literals) .ConsumeValueOrDie(); @@ -1132,6 +1161,8 @@ Status HloEvaluator::HandleConditional(HloInstruction* conditional) { auto* false_computation = conditional->false_computation(); HloEvaluator embedded_evaluator; + embedded_evaluator.set_dynamic_dimension_inference( + dynamic_dimension_inference_); Literal result; if (pred.Get({})) { result = @@ -1186,7 +1217,10 @@ Status HloEvaluator::HandleWhile(HloInstruction* while_hlo) { bool keep_going = true; int64 iteration_count = 0; HloEvaluator cond_evaluator(max_loop_iterations_); + cond_evaluator.set_dynamic_dimension_inference(dynamic_dimension_inference_); HloEvaluator loop_body_evaluator(max_loop_iterations_); + loop_body_evaluator.set_dynamic_dimension_inference( + dynamic_dimension_inference_); while (keep_going) { if (max_loop_iterations_ >= 0 && iteration_count++ > max_loop_iterations_) { return InvalidArgument("Loop %s exceeded loop iteration limit (%d).", diff --git a/tensorflow/compiler/xla/service/hlo_evaluator.h b/tensorflow/compiler/xla/service/hlo_evaluator.h index 4fc5d10aed..604b861913 100644 --- a/tensorflow/compiler/xla/service/hlo_evaluator.h +++ b/tensorflow/compiler/xla/service/hlo_evaluator.h @@ -23,6 +23,7 @@ limitations under the License. #include "absl/types/span.h" #include "tensorflow/compiler/xla/array2d.h" #include "tensorflow/compiler/xla/service/dfs_hlo_visitor_with_default.h" +#include "tensorflow/compiler/xla/service/dynamic_dimension_inference.h" #include "tensorflow/compiler/xla/service/hlo_computation.h" #include "tensorflow/compiler/xla/service/hlo_instruction.h" #include "tensorflow/compiler/xla/service/hlo_module.h" @@ -123,6 +124,11 @@ class HloEvaluator : public DfsHloVisitorWithDefault { const PrecisionConfig& precision_config, const Literal& lhs, const Literal& rhs); + void set_dynamic_dimension_inference( + DynamicDimensionInference* dynamic_dimension_inference) { + dynamic_dimension_inference_ = dynamic_dimension_inference; + } + // Enable the fast path for certain operations like dot or convolution. void set_use_fast_path(bool value) { use_fast_path_ = value; } @@ -161,6 +167,8 @@ class HloEvaluator : public DfsHloVisitorWithDefault { // Status HandleBitcast(HloInstruction* bitcast) override; + Status HandleGetDimensionSize(HloInstruction* get_dimension_size) override; + Status HandleParameter(HloInstruction* parameter) override; Status HandleConstant(HloInstruction* constant) override; @@ -295,6 +303,10 @@ class HloEvaluator : public DfsHloVisitorWithDefault { // RNG engine. std::minstd_rand0 engine_; + // DynamicDimensionInference is used to evaluate GetDimensionSize, which + // returns the dynamic dimension size of its operand. + DynamicDimensionInference* dynamic_dimension_inference_; + TF_DISALLOW_COPY_AND_ASSIGN(HloEvaluator); }; diff --git a/tensorflow/compiler/xla/service/hlo_evaluator_test.cc b/tensorflow/compiler/xla/service/hlo_evaluator_test.cc index 8056193d99..319f0db97a 100644 --- a/tensorflow/compiler/xla/service/hlo_evaluator_test.cc +++ b/tensorflow/compiler/xla/service/hlo_evaluator_test.cc @@ -62,8 +62,7 @@ class HloEvaluatorTest : public HloTestBase { if (use_bfloat16_) { HloElementTypeConverter(F32, BF16).Run(m_.get()).ValueOrDie(); } - return HloEvaluator() - .Evaluate(*m_->entry_computation(), arg_literals) + return evaluator_.Evaluate(*m_->entry_computation(), arg_literals) .ConsumeValueOrDie(); } @@ -75,8 +74,7 @@ class HloEvaluatorTest : public HloTestBase { if (use_bfloat16_) { HloElementTypeConverter(F32, BF16).Run(m_.get()).ValueOrDie(); } - return HloEvaluator() - .Evaluate(*module->entry_computation(), arg_literals) + return evaluator_.Evaluate(*module->entry_computation(), arg_literals) .ConsumeValueOrDie(); } @@ -115,6 +113,7 @@ class HloEvaluatorTest : public HloTestBase { protected: explicit HloEvaluatorTest(bool use_bfloat16) : use_bfloat16_(use_bfloat16) {} + HloEvaluator evaluator_; const bool use_bfloat16_; std::unique_ptr m_ = CreateNewVerifiedModule(); @@ -2874,6 +2873,39 @@ ENTRY main { static_cast(pow31 * pow31)); } +TEST_F(HloEvaluatorTest, GetDimensionSize) { + constexpr absl::string_view hlo_text = R"( +HloModule Test + +ENTRY main { + size = u32[] parameter(0) + + data = s32[4] parameter(1) + + sum = s32[4] add(data, data) + + ROOT dynamic_size = u32[] get-dimension-size(sum), dimensions={0} +} +)"; + TF_ASSERT_OK_AND_ASSIGN(m_, ParseAndReturnVerifiedModule(hlo_text)); + + // Set up dynamic parameter binding. + TF_CHECK_OK(m_->dynamic_parameter_binding().Bind( + DynamicParameterBinding::DynamicParameter{0, {}}, + DynamicParameterBinding::DynamicDimension{1, {}, 0})); + + TF_ASSERT_OK_AND_ASSIGN(DynamicDimensionInference dynamic_dimension_inference, + DynamicDimensionInference::Run(m_.get())); + + evaluator_.set_dynamic_dimension_inference(&dynamic_dimension_inference); + Literal size_arg = LiteralUtil::CreateR0(3); + Literal data_arg = LiteralUtil::CreateR1({1, 2, 3, 4}); + + Literal actual = Evaluate({&size_arg, &data_arg}); + + EXPECT_EQ(actual.GetFirstElement(), static_cast(3)); +} + // Check that we get a useful error if we pass inputs of the wrong shape. TEST_F(HloEvaluatorTest, EvaluateWithWrongInputShapes) { constexpr absl::string_view hlo_text = R"( diff --git a/tensorflow/compiler/xla/service/service.cc b/tensorflow/compiler/xla/service/service.cc index efefe9003f..4a8d272261 100644 --- a/tensorflow/compiler/xla/service/service.cc +++ b/tensorflow/compiler/xla/service/service.cc @@ -29,6 +29,7 @@ limitations under the License. #include "tensorflow/compiler/xla/service/compiler.h" #include "tensorflow/compiler/xla/service/computation_layout.h" #include "tensorflow/compiler/xla/service/device_memory_allocator.h" +#include "tensorflow/compiler/xla/service/dynamic_dimension_inference.h" #include "tensorflow/compiler/xla/service/executable.h" #include "tensorflow/compiler/xla/service/hlo_computation.h" #include "tensorflow/compiler/xla/service/hlo_cost_analysis.h" @@ -1097,7 +1098,11 @@ Status Service::ComputeConstantGraph(const ComputeConstantGraphRequest* arg, TF_ASSIGN_OR_RETURN(std::unique_ptr module, CreateModuleFromProto(arg->computation(), config)); + TF_ASSIGN_OR_RETURN(DynamicDimensionInference dynamic_dimension_inference, + DynamicDimensionInference::Run(module.get())); + HloEvaluator evaluator; + evaluator.set_dynamic_dimension_inference(&dynamic_dimension_inference); TF_ASSIGN_OR_RETURN(auto result_literal, evaluator.Evaluate(*module, {})); // Since the result layout is non-effective to the Evaluator results, explicit -- GitLab From b94da8cca6fc657a77ab920e7feeb05cd57f37d9 Mon Sep 17 00:00:00 2001 From: Sanjoy Das Date: Mon, 7 Jan 2019 15:04:18 -0800 Subject: [PATCH 443/622] Print a more ergonomic error message when the CPU JIT is not linked in PiperOrigin-RevId: 228243176 --- tensorflow/compiler/xla/service/compiler.cc | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tensorflow/compiler/xla/service/compiler.cc b/tensorflow/compiler/xla/service/compiler.cc index 8f08c24490..653f4555a7 100644 --- a/tensorflow/compiler/xla/service/compiler.cc +++ b/tensorflow/compiler/xla/service/compiler.cc @@ -98,10 +98,17 @@ Compiler::GetPlatformCompilers() { auto* factories = GetPlatformCompilerFactories(); auto it = factories->find(platform->id()); if (it == factories->end()) { + string hint; + if (platform->Name() == "Host") { + hint = " (hint: try linking in tensorflow/compiler/jit:xla_cpu_jit)"; + } else if (platform->Name() == "CUDA") { + hint = " (hint: try linking in tensorflow/compiler/jit:xla_gpu_jit)"; + } + return NotFound( "could not find registered compiler for platform %s -- check " - "target linkage", - platform->Name()); + "target linkage%s", + platform->Name(), hint); } // And then we invoke the factory, placing the result into the mapping. -- GitLab From 35938dbf10a7695ace5fa1707b5e6ccdd22869a0 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Mon, 7 Jan 2019 15:34:00 -0800 Subject: [PATCH 444/622] Fixes for flex issues with multiple subgraphs. PiperOrigin-RevId: 228248671 --- tensorflow/lite/delegates/flex/buffer_map.h | 3 ++ .../lite/delegates/flex/buffer_map_test.cc | 2 + tensorflow/lite/delegates/flex/kernel.cc | 11 +++++- tensorflow/lite/delegates/flex/kernel_test.cc | 37 +++++++------------ 4 files changed, 27 insertions(+), 26 deletions(-) diff --git a/tensorflow/lite/delegates/flex/buffer_map.h b/tensorflow/lite/delegates/flex/buffer_map.h index 6c1df4c836..d4724a011d 100644 --- a/tensorflow/lite/delegates/flex/buffer_map.h +++ b/tensorflow/lite/delegates/flex/buffer_map.h @@ -66,6 +66,9 @@ class BufferMap { // be use by TF's forwarding optimizations. void SetForwardable(int tensor_index) { forwardable_.insert(tensor_index); } + // Removes all information about which tensors are forwardable. + void ClearForwardable() { forwardable_.clear(); } + // Returns true if this tensor has been explicitly marks as forwardable by // a call to SetForwardable(). bool IsForwardable(int tensor_index) const { diff --git a/tensorflow/lite/delegates/flex/buffer_map_test.cc b/tensorflow/lite/delegates/flex/buffer_map_test.cc index 2148bfe8e2..7b4acbd69d 100644 --- a/tensorflow/lite/delegates/flex/buffer_map_test.cc +++ b/tensorflow/lite/delegates/flex/buffer_map_test.cc @@ -278,6 +278,8 @@ TEST(BufferMapTest, Forwardable) { EXPECT_FALSE(buffer_map.IsForwardable(0)); buffer_map.SetForwardable(0); EXPECT_TRUE(buffer_map.IsForwardable(0)); + buffer_map.ClearForwardable(); + EXPECT_FALSE(buffer_map.IsForwardable(0)); } } // namespace diff --git a/tensorflow/lite/delegates/flex/kernel.cc b/tensorflow/lite/delegates/flex/kernel.cc index 0005b6e5b0..84745ba9cb 100644 --- a/tensorflow/lite/delegates/flex/kernel.cc +++ b/tensorflow/lite/delegates/flex/kernel.cc @@ -350,7 +350,11 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { buffer_map->SetFromTfLite(tensor_index, tensor); } } - ++tensor_ref_count[tensor_index]; + + // Input tensors should never be forwarded so we increment their ref counts + // twice: once for this graph and another for the possibility of them being + // used by another subgraph, or being an output of the full graph. + tensor_ref_count[tensor_index] += 2; } // All output tensors are allocated by TensorFlow/Eager, so we @@ -372,6 +376,7 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { } } + buffer_map->ClearForwardable(); for (const auto& x : tensor_ref_count) { if (x.second == 1) { // This tensor is referenced once by a single op. We can allow the TF @@ -429,10 +434,12 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { } // We don't need to keep track of internal TF tensors any longer, so take - // them out of the buffer_map, but make sure we keep all the one we might + // them out of the buffer_map, but make sure we keep all the ones we might // need for other subgraphs, or as final output of inference. const auto& outputs = op_data->subgraph_outputs; std::set keep(outputs.begin(), outputs.end()); + const auto& inputs = op_data->subgraph_inputs; + keep.insert(inputs.begin(), inputs.end()); buffer_map->RemoveTensorsNotInSet(keep); return kTfLiteOk; diff --git a/tensorflow/lite/delegates/flex/kernel_test.cc b/tensorflow/lite/delegates/flex/kernel_test.cc index 9de3ca67de..cef8017e6e 100644 --- a/tensorflow/lite/delegates/flex/kernel_test.cc +++ b/tensorflow/lite/delegates/flex/kernel_test.cc @@ -25,6 +25,7 @@ namespace { using ::testing::ContainsRegex; using ::testing::ElementsAre; +using ::testing::ElementsAreArray; TfLiteStatus GenericPrepare(TfLiteContext* context, TfLiteDelegate* delegate, const std::vector& supported_nodes) { @@ -340,14 +341,10 @@ TEST_F(MultipleSubgraphsTest, ForwardabilityIsLocal) { auto input = {3.0f, 4.0f, 5.0f}; PrepareInterpreter(GetPrepareFunction<__LINE__>(), input); - ASSERT_FALSE(Invoke()); - ASSERT_THAT(error_reporter().error_messages(), - ContainsRegex("Cannot read from invalid tensor index 10")); - - // TODO(b/122457581): expected result for this test: - // ASSERT_THAT(GetValues(12), ElementsAreArray(Apply(input, [](float in) { - // return (4 * in + 4) * (in + 1); - // }))); + ASSERT_TRUE(Invoke()); + ASSERT_THAT(GetValues(12), ElementsAreArray(Apply(input, [](float in) { + return (4 * in + 4) * (in + 1); + }))); } // Subgraphs should not remove input tensors from the buffer_map, since @@ -379,14 +376,10 @@ TEST_F(MultipleSubgraphsTest, DoNotRemoveInputTensors) { auto input = {3.0f, 4.0f, 5.0f}; PrepareInterpreter(GetPrepareFunction<__LINE__>(), input); - ASSERT_FALSE(Invoke()); - ASSERT_THAT(error_reporter().error_messages(), - ContainsRegex("Tensor '10' not found")); - - // TODO(b/122457581): expected result for this test: - // ASSERT_THAT(GetValues(12), ElementsAreArray(Apply(input, [](float in) { - // return (4 * in + 4) * (in + 1); - // }))); + ASSERT_TRUE(Invoke()); + ASSERT_THAT(GetValues(12), ElementsAreArray(Apply(input, [](float in) { + return (4 * in + 4) * (in + 1); + }))); } // A tensor is deemed forwardable but it happens to be the input to @@ -417,14 +410,10 @@ TEST_F(MultipleSubgraphsTest, DoNotForwardInputTensors) { auto input = {3.0f, 4.0f, 5.0f}; PrepareInterpreter(GetPrepareFunction<__LINE__>(), input); - ASSERT_FALSE(Invoke()); - ASSERT_THAT(error_reporter().error_messages(), - ContainsRegex("Tensor '10' not found")); - - // TODO(b/122457581): expected result for this test: - // ASSERT_THAT(GetValues(12), ElementsAreArray(Apply(input, [](float in) { - // return (5 * in + 5) * (in + 1); - // }))); + ASSERT_TRUE(Invoke()); + ASSERT_THAT(GetValues(12), ElementsAreArray(Apply(input, [](float in) { + return (5 * in + 5) * (in + 1); + }))); } } // namespace -- GitLab From 6d26622733a3f55861c135b857b2cbce981b2467 Mon Sep 17 00:00:00 2001 From: Karim Nosir Date: Mon, 7 Jan 2019 15:34:34 -0800 Subject: [PATCH 445/622] Fix some tests failures. PiperOrigin-RevId: 228248776 --- tensorflow/lite/kernels/BUILD | 2 ++ tensorflow/lite/kernels/dequantize_test.cc | 1 + tensorflow/lite/kernels/internal/BUILD | 4 ++++ tensorflow/lite/kernels/sparse_output_fully_connected_test.cc | 1 + 4 files changed, 8 insertions(+) diff --git a/tensorflow/lite/kernels/BUILD b/tensorflow/lite/kernels/BUILD index 6809a4a3d0..50deac9bb6 100644 --- a/tensorflow/lite/kernels/BUILD +++ b/tensorflow/lite/kernels/BUILD @@ -352,6 +352,7 @@ tf_cc_test( ":builtin_ops", "//tensorflow/lite:framework", "//tensorflow/lite/kernels:test_util", + "//tensorflow/lite/kernels/internal:types", "@com_google_googletest//:gtest", "@flatbuffers", ], @@ -513,6 +514,7 @@ tf_cc_test( ":builtin_ops", "//tensorflow/lite:framework", "//tensorflow/lite/kernels:test_util", + "//tensorflow/lite/kernels/internal:types", "@com_google_absl//absl/memory", "@com_google_googletest//:gtest", ], diff --git a/tensorflow/lite/kernels/dequantize_test.cc b/tensorflow/lite/kernels/dequantize_test.cc index bb5f1e74a8..6343745eef 100644 --- a/tensorflow/lite/kernels/dequantize_test.cc +++ b/tensorflow/lite/kernels/dequantize_test.cc @@ -14,6 +14,7 @@ limitations under the License. ==============================================================================*/ #include #include "tensorflow/lite/interpreter.h" +#include "tensorflow/lite/kernels/internal/types.h" #include "tensorflow/lite/kernels/register.h" #include "tensorflow/lite/kernels/test_util.h" #include "tensorflow/lite/model.h" diff --git a/tensorflow/lite/kernels/internal/BUILD b/tensorflow/lite/kernels/internal/BUILD index 92b4705908..885f650dd5 100644 --- a/tensorflow/lite/kernels/internal/BUILD +++ b/tensorflow/lite/kernels/internal/BUILD @@ -545,6 +545,10 @@ cc_library( name = "test_util", srcs = ["test_util.cc"], hdrs = ["test_util.h"], + linkopts = select({ + "//tensorflow:windows": [], + "//conditions:default": ["-lm"], + }), deps = [ ":types", "//tensorflow/lite:string", diff --git a/tensorflow/lite/kernels/sparse_output_fully_connected_test.cc b/tensorflow/lite/kernels/sparse_output_fully_connected_test.cc index fb71cb7f7b..8152ae6685 100644 --- a/tensorflow/lite/kernels/sparse_output_fully_connected_test.cc +++ b/tensorflow/lite/kernels/sparse_output_fully_connected_test.cc @@ -19,6 +19,7 @@ limitations under the License. #include #include "flatbuffers/flexbuffers.h" // TF:flatbuffers +#include "tensorflow/lite/kernels/internal/types.h" #include "tensorflow/lite/kernels/register.h" #include "tensorflow/lite/kernels/test_util.h" -- GitLab From 3b74e1c16ab280a4e0cbd5138a0c73710cb9804c Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Mon, 7 Jan 2019 15:46:39 -0800 Subject: [PATCH 446/622] Clarified that variables_to_restore function chooses whether to use a moving average version based on provided parameters rather than based on the existence of a given name. PiperOrigin-RevId: 228250786 --- tensorflow/python/training/moving_averages.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tensorflow/python/training/moving_averages.py b/tensorflow/python/training/moving_averages.py index 72670f0ca3..4b267dfb98 100644 --- a/tensorflow/python/training/moving_averages.py +++ b/tensorflow/python/training/moving_averages.py @@ -505,13 +505,13 @@ class ExponentialMovingAverage(object): ``` Args: moving_avg_variables: a list of variables that require to use of the - moving variable name to be restored. If None, it will default to + moving average variable name to be restored. If None, it will default to variables.moving_average_variables() + variables.trainable_variables() Returns: - A map from restore_names to variables. The restore_name can be the - moving_average version of the variable name if it exist, or the original - variable name. + A map from restore_names to variables. The restore_name is either the + original or the moving average version of the variable name, depending + on whether the variable name is in the `moving_avg_variables`. """ name_map = {} if moving_avg_variables is None: -- GitLab From 0de09568916f31d77ef0093285a058e901195919 Mon Sep 17 00:00:00 2001 From: Peter Ma Date: Mon, 7 Jan 2019 16:09:22 -0800 Subject: [PATCH 447/622] Replace op_features with op_info to correctly reflect the data type, and some minor fix on function argument names. PiperOrigin-RevId: 228254456 --- .../grappler/costs/op_level_cost_estimator.cc | 246 +++++++++--------- .../grappler/costs/op_level_cost_estimator.h | 34 ++- .../costs/op_level_cost_estimator_test.cc | 17 +- 3 files changed, 143 insertions(+), 154 deletions(-) diff --git a/tensorflow/core/grappler/costs/op_level_cost_estimator.cc b/tensorflow/core/grappler/costs/op_level_cost_estimator.cc index 55eb391d2b..96bac8d0cb 100644 --- a/tensorflow/core/grappler/costs/op_level_cost_estimator.cc +++ b/tensorflow/core/grappler/costs/op_level_cost_estimator.cc @@ -72,25 +72,25 @@ static const Costs::Duration kMinComputeTime(1); namespace { -string GetDataFormat(const OpInfo& op_features) { +string GetDataFormat(const OpInfo& op_info) { string data_format = "NHWC"; // Default format. - if (op_features.attr().find("data_format") != op_features.attr().end()) { - data_format = op_features.attr().at("data_format").s(); + if (op_info.attr().find("data_format") != op_info.attr().end()) { + data_format = op_info.attr().at("data_format").s(); } return data_format; } -string GetFilterFormat(const OpInfo& op_features) { +string GetFilterFormat(const OpInfo& op_info) { string filter_format = "HWIO"; // Default format. - if (op_features.attr().find("filter_format") != op_features.attr().end()) { - filter_format = op_features.attr().at("filter_format").s(); + if (op_info.attr().find("filter_format") != op_info.attr().end()) { + filter_format = op_info.attr().at("filter_format").s(); } return filter_format; } -Padding GetPadding(const OpInfo& op_features) { - if (op_features.attr().find("padding") != op_features.attr().end() && - op_features.attr().at("padding").s() == "VALID") { +Padding GetPadding(const OpInfo& op_info) { + if (op_info.attr().find("padding") != op_info.attr().end() && + op_info.attr().at("padding").s() == "VALID") { return Padding::VALID; } return Padding::SAME; // Default padding. @@ -107,11 +107,11 @@ bool IsTraining(const OpInfo& op_info) { // TODO(dyoon): support non-4D tensors in the c ost functions of convolution // related ops (Conv, Pool, BatchNorm, and their backprops) and the related // helper functions. -std::vector GetStrides(const OpInfo& op_features) { - if (op_features.attr().find("strides") != op_features.attr().end()) { - const auto strides = op_features.attr().at("strides").list().i(); - CHECK(strides.size() == 4) << "Attr strides is not a length-4 vector: " - << op_features.DebugString(); +std::vector GetStrides(const OpInfo& op_info) { + if (op_info.attr().find("strides") != op_info.attr().end()) { + const auto strides = op_info.attr().at("strides").list().i(); + CHECK(strides.size() == 4) + << "Attr strides is not a length-4 vector: " << op_info.DebugString(); return {strides[0], strides[1], strides[2], strides[3]}; } return {1, 1, 1, 1}; @@ -359,21 +359,21 @@ OpLevelCostEstimator::OpLevelCostEstimator() { } Costs OpLevelCostEstimator::PredictCosts(const OpContext& op_context) const { - const auto& op_features = op_context.op_info; - auto it = device_cost_impl_.find(op_features.op()); + const auto& op_info = op_context.op_info; + auto it = device_cost_impl_.find(op_info.op()); if (it == device_cost_impl_.end()) { - if (elementwise_ops_.find(op_features.op()) != elementwise_ops_.end()) { + if (elementwise_ops_.find(op_info.op()) != elementwise_ops_.end()) { return PredictCwiseOp(op_context); } - VLOG(1) << "Missing accurate estimator for op: " << op_features.op(); + VLOG(1) << "Missing accurate estimator for op: " << op_info.op(); return PredictCostOfAnUnknownOp(op_context); } std::function estimator = it->second; Costs costs = estimator(op_context); - VLOG(1) << "Operation " << op_features.op() << " takes " + VLOG(1) << "Operation " << op_info.op() << " takes " << costs.execution_time.count() << " ns."; return costs; } @@ -430,39 +430,38 @@ DeviceInfo OpLevelCostEstimator::GetDeviceInfo( } Costs OpLevelCostEstimator::PredictCwiseOp(const OpContext& op_context) const { - const auto& op_features = op_context.op_info; + const auto& op_info = op_context.op_info; bool found_unknown_shapes = false; // For unary or binary element-wise operations, op count is the element count // of any input. We use the count for the largest input here to be more robust // in case that the shape is unknown or partially known for other input. - int64 op_count = - CalculateLargestInputCount(op_features, &found_unknown_shapes); + int64 op_count = CalculateLargestInputCount(op_info, &found_unknown_shapes); // If output shape is available, try use the element count calcuated from // that. - if (op_features.outputs_size() > 0) { - op_count = - std::max(op_count, CalculateTensorElementCount(op_features.outputs(0), - &found_unknown_shapes)); + if (op_info.outputs_size() > 0) { + op_count = std::max( + op_count, + CalculateTensorElementCount(op_info.outputs(0), &found_unknown_shapes)); } // For binary ops, calculate the output shape possibly resulting from // broadcasting. - if (op_features.inputs_size() >= 2) { - op_count = std::max(op_count, - CwiseOutputElementCount(op_features.inputs(0).shape(), - op_features.inputs(1).shape())); + if (op_info.inputs_size() >= 2) { + op_count = + std::max(op_count, CwiseOutputElementCount(op_info.inputs(0).shape(), + op_info.inputs(1).shape())); } int op_cost = 1; bool is_known_elementwise_op = false; - auto it = elementwise_ops_.find(op_features.op()); + auto it = elementwise_ops_.find(op_info.op()); if (it != elementwise_ops_.end()) { op_cost = it->second; is_known_elementwise_op = true; } else { - LOG(WARNING) << "Not a cwise op: " << op_features.op(); + LOG(WARNING) << "Not a cwise op: " << op_info.op(); } - Costs costs = PredictOpCountBasedCost(op_count * op_cost, op_features); + Costs costs = PredictOpCountBasedCost(op_count * op_cost, op_info); if (found_unknown_shapes || !is_known_elementwise_op) { costs.inaccurate = true; } @@ -542,17 +541,17 @@ Costs OpLevelCostEstimator::PredictOpCountBasedCost( } int64 OpLevelCostEstimator::CountConv2DOperations( - const OpInfo& op_features, bool* found_unknown_shapes) const { - return CountConv2DOperations(op_features, nullptr, found_unknown_shapes); + const OpInfo& op_info, bool* found_unknown_shapes) const { + return CountConv2DOperations(op_info, nullptr, found_unknown_shapes); } // Helper to translate the positional arguments into named fields. OpLevelCostEstimator::ConvolutionDimensions OpLevelCostEstimator::ConvolutionDimensionsFromInputs( const TensorShapeProto& original_image_shape, - const TensorShapeProto& original_filter_shape, const OpInfo& op_features, + const TensorShapeProto& original_filter_shape, const OpInfo& op_info, bool* found_unknown_shapes) { - VLOG(2) << "op features: " << op_features.DebugString(); + VLOG(2) << "op features: " << op_info.DebugString(); VLOG(2) << "Original image shape: " << original_image_shape.DebugString(); VLOG(2) << "Original filter shape: " << original_filter_shape.DebugString(); auto image_shape = @@ -563,7 +562,7 @@ OpLevelCostEstimator::ConvolutionDimensionsFromInputs( VLOG(2) << "Filter shape: " << filter_shape.DebugString(); int x_index, y_index, channel_index; - const string& data_format = GetDataFormat(op_features); + const string& data_format = GetDataFormat(op_info); if (data_format == "NCHW") { x_index = 2; y_index = 3; @@ -574,7 +573,7 @@ OpLevelCostEstimator::ConvolutionDimensionsFromInputs( y_index = 2; channel_index = 3; } - const string& filter_format = GetFilterFormat(op_features); + const string& filter_format = GetFilterFormat(op_info); int filter_x_index, filter_y_index, in_channel_index, out_channel_index; if (filter_format == "HWIO") { filter_x_index = 0; @@ -594,8 +593,8 @@ OpLevelCostEstimator::ConvolutionDimensionsFromInputs( int64 iz = image_shape.dim(channel_index).size(); int64 kx = filter_shape.dim(filter_x_index).size(); int64 ky = filter_shape.dim(filter_y_index).size(); - std::vector strides = GetStrides(op_features); - const auto padding = GetPadding(op_features); + std::vector strides = GetStrides(op_info); + const auto padding = GetPadding(op_info); int64 sx = strides[x_index]; int64 sy = strides[y_index]; int64 ox = GetOutputSize(ix, kx, sx, padding); @@ -623,14 +622,13 @@ OpLevelCostEstimator::ConvolutionDimensionsFromInputs( } int64 OpLevelCostEstimator::CountConv2DOperations( - const OpInfo& op_features, ConvolutionDimensions* conv_info, + const OpInfo& op_info, ConvolutionDimensions* conv_info, bool* found_unknown_shapes) const { - DCHECK(op_features.op() == kConv2d || - op_features.op() == kDepthwiseConv2dNative) + DCHECK(op_info.op() == kConv2d || op_info.op() == kDepthwiseConv2dNative) << "Invalid Operation: not Conv2D nor DepthwiseConv2dNative"; ConvolutionDimensions conv_dims = ConvolutionDimensionsFromInputs( - op_features.inputs(0).shape(), op_features.inputs(1).shape(), op_features, + op_info.inputs(0).shape(), op_info.inputs(1).shape(), op_info, found_unknown_shapes); // in DepthwiseConv2dNative conv_dims.oz is actually the channel depth @@ -641,7 +639,7 @@ int64 OpLevelCostEstimator::CountConv2DOperations( int64 ops = conv_dims.batch; ops *= conv_dims.ox * conv_dims.oy; ops *= conv_dims.kx * conv_dims.ky; - if (op_features.op() == kConv2d) { + if (op_info.op() == kConv2d) { ops *= conv_dims.iz * conv_dims.oz; } else { // To ensure output tensor dims to be correct for DepthwiseConv2DNative, @@ -658,32 +656,32 @@ int64 OpLevelCostEstimator::CountConv2DOperations( } int64 OpLevelCostEstimator::CountMatMulOperations( - const OpInfo& op_features, bool* found_unknown_shapes) const { - return CountMatMulOperations(op_features, nullptr, found_unknown_shapes); + const OpInfo& op_info, bool* found_unknown_shapes) const { + return CountMatMulOperations(op_info, nullptr, found_unknown_shapes); } // TODO(nishantpatil): Create separate estimator for Sparse Matmul int64 OpLevelCostEstimator::CountMatMulOperations( - const OpInfo& op_features, MatMulDimensions* mat_mul, + const OpInfo& op_info, MatMulDimensions* mat_mul, bool* found_unknown_shapes) const { double ops = 0; - if (op_features.inputs_size() < 2) { - LOG(ERROR) << "Need 2 inputs but got " << op_features.inputs_size(); + if (op_info.inputs_size() < 2) { + LOG(ERROR) << "Need 2 inputs but got " << op_info.inputs_size(); // TODO(pcma): Try to separate invalid inputs from unknown shapes *found_unknown_shapes = true; return 0; } - auto& a_matrix = op_features.inputs(0); - auto& b_matrix = op_features.inputs(1); + auto& a_matrix = op_info.inputs(0); + auto& b_matrix = op_info.inputs(1); bool transpose_a = false; bool transpose_b = false; double m_dim, n_dim, k_dim, k_dim_b = 0; - for (const auto& item : op_features.attr()) { + for (const auto& item : op_info.attr()) { VLOG(1) << "Key:" << item.first << " Value:" << SummarizeAttrValue(item.second); if (item.first == "transpose_a" && item.second.b() == true) @@ -735,23 +733,23 @@ int64 OpLevelCostEstimator::CountMatMulOperations( } int64 OpLevelCostEstimator::CountBatchMatMulOperations( - const OpInfo& op_features, bool* found_unknown_shapes) const { - if (op_features.op() != kBatchMatMul) { - LOG(ERROR) << "Invalid Operation: " << op_features.op(); + const OpInfo& op_info, bool* found_unknown_shapes) const { + if (op_info.op() != kBatchMatMul) { + LOG(ERROR) << "Invalid Operation: " << op_info.op(); // TODO(pcma): Try to separate invalid inputs from unknown shapes *found_unknown_shapes = true; return 0; } - if (op_features.inputs_size() != 2) { - LOG(ERROR) << "Expected 2 inputs but got " << op_features.inputs_size(); + if (op_info.inputs_size() != 2) { + LOG(ERROR) << "Expected 2 inputs but got " << op_info.inputs_size(); // TODO(pcma): Try to separate invalid inputs from unknown shapes *found_unknown_shapes = true; return 0; } double ops = 0; - const auto& a_input = op_features.inputs(0); - const auto& b_input = op_features.inputs(1); + const auto& a_input = op_info.inputs(0); + const auto& b_input = op_info.inputs(1); // BatchMatMul requires inputs of at least matrix shape (rank 2). // The two most minor dimensions of each input are matrices that @@ -801,24 +799,24 @@ int64 OpLevelCostEstimator::CountBatchMatMulOperations( // Build the MatMul. Note that values are ignored here since we are just // counting ops (e.g. only shapes matter). - OpInfo matmul_op_features; - matmul_op_features.set_op("MatMul"); + OpInfo matmul_op_info; + matmul_op_info.set_op("MatMul"); AttrValue transpose_a; transpose_a.set_b(false); - if (op_features.attr().find("adj_x") != op_features.attr().end()) { - transpose_a.set_b(op_features.attr().at("adj_x").b()); + if (op_info.attr().find("adj_x") != op_info.attr().end()) { + transpose_a.set_b(op_info.attr().at("adj_x").b()); } - (*matmul_op_features.mutable_attr())["transpose_a"] = transpose_a; + (*matmul_op_info.mutable_attr())["transpose_a"] = transpose_a; AttrValue transpose_b; transpose_b.set_b(false); - if (op_features.attr().find("adj_y") != op_features.attr().end()) { - transpose_b.set_b(op_features.attr().at("adj_y").b()); + if (op_info.attr().find("adj_y") != op_info.attr().end()) { + transpose_b.set_b(op_info.attr().at("adj_y").b()); } - (*matmul_op_features.mutable_attr())["transpose_b"] = transpose_b; + (*matmul_op_info.mutable_attr())["transpose_b"] = transpose_b; - OpInfo::TensorProperties* a_matrix = matmul_op_features.add_inputs(); + OpInfo::TensorProperties* a_matrix = matmul_op_info.add_inputs(); a_matrix->set_dtype(a_input.dtype()); TensorShapeProto* a_matrix_shape = a_matrix->mutable_shape(); for (int i = std::max(0, a_input_shape.dim_size() - matrix_rank); @@ -826,7 +824,7 @@ int64 OpLevelCostEstimator::CountBatchMatMulOperations( *(a_matrix_shape->add_dim()) = a_input_shape.dim(i); } - OpInfo::TensorProperties* b_matrix = matmul_op_features.add_inputs(); + OpInfo::TensorProperties* b_matrix = matmul_op_info.add_inputs(); b_matrix->set_dtype(b_input.dtype()); TensorShapeProto* b_matrix_shape = b_matrix->mutable_shape(); for (int i = std::max(0, b_input_shape.dim_size() - matrix_rank); @@ -836,7 +834,7 @@ int64 OpLevelCostEstimator::CountBatchMatMulOperations( for (int i = 0; i < num_matmuls; ++i) { bool matmul_unknown_shapes = false; - ops += CountMatMulOperations(matmul_op_features, &matmul_unknown_shapes); + ops += CountMatMulOperations(matmul_op_info, &matmul_unknown_shapes); *found_unknown_shapes |= matmul_unknown_shapes; } return ops; @@ -894,16 +892,16 @@ bool GetTensorShapeProtoFromTensorProto(const TensorProto& tensor_proto, // TODO(cliffy): Dedup this method and CountConv2DBackpropFilterOperations. int64 OpLevelCostEstimator::CountConv2DBackpropInputOperations( - const OpInfo& op_features, ConvolutionDimensions* returned_conv_dims, + const OpInfo& op_info, ConvolutionDimensions* returned_conv_dims, bool* found_unknown_shapes) const { int64 ops = 0; - DCHECK(op_features.op() == kConv2dBackpropInput || - op_features.op() == kDepthwiseConv2dNativeBackpropInput) + DCHECK(op_info.op() == kConv2dBackpropInput || + op_info.op() == kDepthwiseConv2dNativeBackpropInput) << "Invalid Operation: not kConv2dBackpropInput nor" "kDepthwiseConv2dNativeBackpropInput"; - if (op_features.inputs_size() < 2) { + if (op_info.inputs_size() < 2) { // TODO(pcma): Try to separate invalid inputs from unknown shapes *found_unknown_shapes = true; return ops; @@ -911,12 +909,12 @@ int64 OpLevelCostEstimator::CountConv2DBackpropInputOperations( TensorShapeProto input_shape; bool shape_found = false; - if (op_features.inputs(0).has_value()) { - const TensorProto& value = op_features.inputs(0).value(); + if (op_info.inputs(0).has_value()) { + const TensorProto& value = op_info.inputs(0).value(); shape_found = GetTensorShapeProtoFromTensorProto(value, &input_shape); } - if (!shape_found && op_features.outputs_size() == 1) { - input_shape = op_features.outputs(0).shape(); + if (!shape_found && op_info.outputs_size() == 1) { + input_shape = op_info.outputs(0).shape(); shape_found = true; } if (!shape_found) { @@ -929,13 +927,12 @@ int64 OpLevelCostEstimator::CountConv2DBackpropInputOperations( } ConvolutionDimensions conv_dims = ConvolutionDimensionsFromInputs( - input_shape, op_features.inputs(1).shape(), op_features, - found_unknown_shapes); + input_shape, op_info.inputs(1).shape(), op_info, found_unknown_shapes); ops = conv_dims.batch; ops *= conv_dims.ox * conv_dims.oy; ops *= conv_dims.kx * conv_dims.ky; - if (op_features.op() == kConv2dBackpropInput) { + if (op_info.op() == kConv2dBackpropInput) { ops *= conv_dims.iz * conv_dims.oz; } else { // conv_dims always use forward path definition regardless @@ -944,7 +941,7 @@ int64 OpLevelCostEstimator::CountConv2DBackpropInputOperations( } ops *= kOpsPerMac; - VLOG(1) << "Operations for" << op_features.op() << " " << ops; + VLOG(1) << "Operations for" << op_info.op() << " " << ops; if (returned_conv_dims != nullptr) { *returned_conv_dims = conv_dims; @@ -953,23 +950,23 @@ int64 OpLevelCostEstimator::CountConv2DBackpropInputOperations( } int64 OpLevelCostEstimator::CountConv2DBackpropFilterOperations( - const OpInfo& op_features, ConvolutionDimensions* returned_conv_dims, + const OpInfo& op_info, ConvolutionDimensions* returned_conv_dims, bool* found_unknown_shapes) const { int64 ops = 0; - DCHECK(op_features.op() == kConv2dBackpropFilter || - op_features.op() == kDepthwiseConv2dNativeBackpropFilter) + DCHECK(op_info.op() == kConv2dBackpropFilter || + op_info.op() == kDepthwiseConv2dNativeBackpropFilter) << "Invalid Operation: not kConv2dBackpropFilter nor" "kDepthwiseConv2dNativeBackpropFilter"; TensorShapeProto filter_shape; bool shape_found = false; - if (op_features.inputs_size() >= 2 && op_features.inputs(1).has_value()) { - const TensorProto& value = op_features.inputs(1).value(); + if (op_info.inputs_size() >= 2 && op_info.inputs(1).has_value()) { + const TensorProto& value = op_info.inputs(1).value(); shape_found = GetTensorShapeProtoFromTensorProto(value, &filter_shape); } - if (!shape_found && op_features.outputs_size() == 1) { - filter_shape = op_features.outputs(0).shape(); + if (!shape_found && op_info.outputs_size() == 1) { + filter_shape = op_info.outputs(0).shape(); shape_found = true; } if (!shape_found) { @@ -981,19 +978,18 @@ int64 OpLevelCostEstimator::CountConv2DBackpropFilterOperations( *found_unknown_shapes = true; } - if (op_features.inputs_size() < 1) { + if (op_info.inputs_size() < 1) { // TODO(pcma): Try to separate invalid inputs from unknown shapes *found_unknown_shapes = true; return ops; } ConvolutionDimensions conv_dims = ConvolutionDimensionsFromInputs( - op_features.inputs(0).shape(), filter_shape, op_features, - found_unknown_shapes); + op_info.inputs(0).shape(), filter_shape, op_info, found_unknown_shapes); ops = conv_dims.batch; ops *= conv_dims.ox * conv_dims.oy; ops *= conv_dims.kx * conv_dims.ky; - if (op_features.op() == kConv2dBackpropFilter) { + if (op_info.op() == kConv2dBackpropFilter) { ops *= conv_dims.iz * conv_dims.oz; } else { // conv_dims always use forward path definition regardless @@ -1001,7 +997,7 @@ int64 OpLevelCostEstimator::CountConv2DBackpropFilterOperations( ops *= conv_dims.oz; } ops *= kOpsPerMac; - VLOG(1) << "Operations for" << op_features.op() << " " << ops; + VLOG(1) << "Operations for" << op_info.op() << " " << ops; if (returned_conv_dims != nullptr) { *returned_conv_dims = conv_dims; @@ -1032,9 +1028,9 @@ int64 OpLevelCostEstimator::CalculateTensorSize( } int64 OpLevelCostEstimator::CalculateInputSize( - const OpInfo& op_features, bool* found_unknown_shapes) const { + const OpInfo& op_info, bool* found_unknown_shapes) const { int64 total_input_size = 0; - for (auto& input : op_features.inputs()) { + for (auto& input : op_info.inputs()) { int64 input_size = CalculateTensorSize(input, found_unknown_shapes); total_input_size += input_size; VLOG(1) << "Input Size: " << input_size @@ -1044,9 +1040,9 @@ int64 OpLevelCostEstimator::CalculateInputSize( } int64 OpLevelCostEstimator::CalculateLargestInputCount( - const OpInfo& op_features, bool* found_unknown_shapes) const { + const OpInfo& op_info, bool* found_unknown_shapes) const { int64 largest_input_count = 0; - for (auto& input : op_features.inputs()) { + for (auto& input : op_info.inputs()) { int64 input_count = CalculateTensorElementCount(input, found_unknown_shapes); if (input_count > largest_input_count) { @@ -1059,10 +1055,10 @@ int64 OpLevelCostEstimator::CalculateLargestInputCount( } int64 OpLevelCostEstimator::CalculateOutputSize( - const OpInfo& op_features, bool* found_unknown_shapes) const { + const OpInfo& op_info, bool* found_unknown_shapes) const { int64 total_output_size = 0; // use float as default for calculations - for (const auto& output : op_features.outputs()) { + for (const auto& output : op_info.outputs()) { DataType dt = output.dtype(); const auto& original_output_shape = output.shape(); int64 output_size = DataTypeSize(BaseType(dt)); @@ -1080,10 +1076,10 @@ int64 OpLevelCostEstimator::CalculateOutputSize( } Costs OpLevelCostEstimator::PredictConv2D(const OpContext& op_context) const { - const auto& op_features = op_context.op_info; + const auto& op_info = op_context.op_info; bool found_unknown_shapes = false; auto costs = PredictOpCountBasedCost( - CountConv2DOperations(op_features, &found_unknown_shapes), op_features); + CountConv2DOperations(op_info, &found_unknown_shapes), op_info); costs.inaccurate = found_unknown_shapes; costs.num_ops_with_unknown_shapes = found_unknown_shapes; return costs; @@ -1091,12 +1087,12 @@ Costs OpLevelCostEstimator::PredictConv2D(const OpContext& op_context) const { Costs OpLevelCostEstimator::PredictConv2DBackpropInput( const OpContext& op_context) const { - const auto& op_features = op_context.op_info; + const auto& op_info = op_context.op_info; bool found_unknown_shapes = false; auto costs = PredictOpCountBasedCost(CountConv2DBackpropInputOperations( - op_features, nullptr, &found_unknown_shapes), - op_features); + op_info, nullptr, &found_unknown_shapes), + op_info); costs.inaccurate = found_unknown_shapes; costs.num_ops_with_unknown_shapes = found_unknown_shapes; return costs; @@ -1104,12 +1100,12 @@ Costs OpLevelCostEstimator::PredictConv2DBackpropInput( Costs OpLevelCostEstimator::PredictConv2DBackpropFilter( const OpContext& op_context) const { - const auto& op_features = op_context.op_info; + const auto& op_info = op_context.op_info; bool found_unknown_shapes = false; auto costs = PredictOpCountBasedCost(CountConv2DBackpropFilterOperations( - op_features, nullptr, &found_unknown_shapes), - op_features); + op_info, nullptr, &found_unknown_shapes), + op_info); costs.inaccurate = found_unknown_shapes; costs.num_ops_with_unknown_shapes = found_unknown_shapes; return costs; @@ -1204,26 +1200,26 @@ Costs OpLevelCostEstimator::PredictFusedConv2DBiasActivation( } Costs OpLevelCostEstimator::PredictMatMul(const OpContext& op_context) const { - const auto& op_features = op_context.op_info; + const auto& op_info = op_context.op_info; bool found_unknown_shapes = false; auto costs = PredictOpCountBasedCost( - CountMatMulOperations(op_features, &found_unknown_shapes), op_features); + CountMatMulOperations(op_info, &found_unknown_shapes), op_info); costs.inaccurate = found_unknown_shapes; costs.num_ops_with_unknown_shapes = found_unknown_shapes; return costs; } Costs OpLevelCostEstimator::PredictNoOp(const OpContext& op_context) const { - const auto& op_features = op_context.op_info; - VLOG(1) << "Op:" << op_features.op() << " Execution Time 0 (ns)"; + const auto& op_info = op_context.op_info; + VLOG(1) << "Op:" << op_info.op() << " Execution Time 0 (ns)"; return Costs::ZeroCosts(); } Costs OpLevelCostEstimator::PredictIdentity(const OpContext& op_context) const { - const auto& op_features = op_context.op_info; - VLOG(1) << "Op:" << op_features.op() << " Execution Time 0 (ns)"; + const auto& op_info = op_context.op_info; + VLOG(1) << "Op:" << op_info.op() << " Execution Time 0 (ns)"; Costs result = Costs::ZeroCosts(); - result.max_memory = CalculateOutputSize(op_features, &result.inaccurate); + result.max_memory = CalculateOutputSize(op_info, &result.inaccurate); result.num_ops_with_unknown_shapes = result.inaccurate; // Assign the minimum amount of time we can represent to the identity op since // it tends to be really cheap. @@ -1233,11 +1229,10 @@ Costs OpLevelCostEstimator::PredictIdentity(const OpContext& op_context) const { } Costs OpLevelCostEstimator::PredictVariable(const OpContext& op_context) const { - const auto& op_features = op_context.op_info; - VLOG(1) << "Op:" << op_features.op() << " Execution Time 0 (ns)"; + const auto& op_info = op_context.op_info; + VLOG(1) << "Op:" << op_info.op() << " Execution Time 0 (ns)"; Costs result = Costs::ZeroCosts(); - result.persistent_memory = - CalculateOutputSize(op_features, &result.inaccurate); + result.persistent_memory = CalculateOutputSize(op_info, &result.inaccurate); result.num_ops_with_unknown_shapes = result.inaccurate; result.compute_time = kMinComputeTime; @@ -1247,20 +1242,19 @@ Costs OpLevelCostEstimator::PredictVariable(const OpContext& op_context) const { Costs OpLevelCostEstimator::PredictBatchMatMul( const OpContext& op_context) const { - const auto& op_features = op_context.op_info; + const auto& op_info = op_context.op_info; bool found_unknown_shapes = false; Costs costs = PredictOpCountBasedCost( - CountBatchMatMulOperations(op_features, &found_unknown_shapes), - op_features); + CountBatchMatMulOperations(op_info, &found_unknown_shapes), op_info); costs.inaccurate = found_unknown_shapes; costs.num_ops_with_unknown_shapes = found_unknown_shapes; return costs; } Costs OpLevelCostEstimator::PredictMetadata(const OpContext& op_context) const { - const auto& op_features = op_context.op_info; + const auto& op_info = op_context.op_info; Costs costs = Costs::ZeroCosts(); - costs.max_memory = CalculateOutputSize(op_features, &costs.inaccurate); + costs.max_memory = CalculateOutputSize(op_info, &costs.inaccurate); costs.num_ops_with_unknown_shapes = costs.inaccurate; // Metadata operations are so cheap we assume they take the minimum amount of // time we can represent (1 ns). diff --git a/tensorflow/core/grappler/costs/op_level_cost_estimator.h b/tensorflow/core/grappler/costs/op_level_cost_estimator.h index 84dd9213f7..f8ba8c6637 100644 --- a/tensorflow/core/grappler/costs/op_level_cost_estimator.h +++ b/tensorflow/core/grappler/costs/op_level_cost_estimator.h @@ -16,10 +16,6 @@ limitations under the License. #ifndef TENSORFLOW_CORE_GRAPPLER_COSTS_OP_LEVEL_COST_ESTIMATOR_H_ #define TENSORFLOW_CORE_GRAPPLER_COSTS_OP_LEVEL_COST_ESTIMATOR_H_ -#include -#include -#include - #include "tensorflow/core/grappler/costs/cost_estimator.h" #include "tensorflow/core/grappler/costs/op_context.h" #include "tensorflow/core/grappler/costs/op_performance_data.pb.h" @@ -79,24 +75,23 @@ class OpLevelCostEstimator { int64 sy; // Stride y. Padding padding; // SAME or VALID. }; - int64 CountConv2DOperations(const OpInfo& op_features, + int64 CountConv2DOperations(const OpInfo& op_info, bool* found_unknown_shapes) const; - int64 CountConv2DOperations(const OpInfo& op_features, + int64 CountConv2DOperations(const OpInfo& op_info, ConvolutionDimensions* conv_info, bool* found_unknown_shapes) const; - int64 CountMatMulOperations(const OpInfo& op_features, + int64 CountMatMulOperations(const OpInfo& op_info, bool* found_unknown_shapes) const; - int64 CountMatMulOperations(const OpInfo& op_features, - MatMulDimensions* mat_mul, + int64 CountMatMulOperations(const OpInfo& op_info, MatMulDimensions* mat_mul, bool* found_unknown_shapes) const; - int64 CountBatchMatMulOperations(const OpInfo& op_features, + int64 CountBatchMatMulOperations(const OpInfo& op_info, bool* found_unknown_shapes) const; - int64 CountConv2DBackpropInputOperations(const OpInfo& op_features, - ConvolutionDimensions* conv_info, - bool* found_unknown_shapes) const; - int64 CountConv2DBackpropFilterOperations(const OpInfo& op_features, - ConvolutionDimensions* conv_info, - bool* found_unknown_shapes) const; + int64 CountConv2DBackpropInputOperations( + const OpInfo& op_info, ConvolutionDimensions* returned_conv_dims, + bool* found_unknown_shapes) const; + int64 CountConv2DBackpropFilterOperations( + const OpInfo& op_info, ConvolutionDimensions* returned_conv_dims, + bool* found_unknown_shapes) const; // Calculate the element count of an input/output tensor. int64 CalculateTensorElementCount(const OpInfo::TensorProperties& tensor, @@ -108,17 +103,17 @@ class OpLevelCostEstimator { // Calculate the element count of the largest // input of specified TensorFlow op. - int64 CalculateLargestInputCount(const OpInfo& op_features, + int64 CalculateLargestInputCount(const OpInfo& op_info, bool* found_unknown_shapes) const; // Calculate the total size in bytes of the all // the inputs of specified TensorFlow op. - int64 CalculateInputSize(const OpInfo& op_features, + int64 CalculateInputSize(const OpInfo& op_info, bool* found_unknown_shapes) const; // Calculate the total size in bytes of the all // the outputs of specified TensorFlow op. - int64 CalculateOutputSize(const OpInfo& op_features, + int64 CalculateOutputSize(const OpInfo& op_info, bool* found_unknown_shapes) const; // This family of routines predicts the costs to @@ -205,4 +200,5 @@ class OpLevelCostEstimator { } // end namespace grappler } // end namespace tensorflow + #endif // TENSORFLOW_CORE_GRAPPLER_COSTS_OP_LEVEL_COST_ESTIMATOR_H_ diff --git a/tensorflow/core/grappler/costs/op_level_cost_estimator_test.cc b/tensorflow/core/grappler/costs/op_level_cost_estimator_test.cc index 9a59877ac5..6a9bf13b93 100644 --- a/tensorflow/core/grappler/costs/op_level_cost_estimator_test.cc +++ b/tensorflow/core/grappler/costs/op_level_cost_estimator_test.cc @@ -29,8 +29,8 @@ namespace grappler { namespace { // Wrangles the minimum number of proto fields to set up a matrix. -void DescribeMatrix(int rows, int columns, OpInfo* op_features) { - auto input = op_features->add_inputs(); +void DescribeMatrix(int rows, int columns, OpInfo* op_info) { + auto input = op_info->add_inputs(); auto shape = input->mutable_shape(); auto shape_rows = shape->add_dim(); shape_rows->set_size(rows); @@ -39,8 +39,8 @@ void DescribeMatrix(int rows, int columns, OpInfo* op_features) { input->set_dtype(DT_FLOAT); } -void SetCpuDevice(OpInfo* op_features) { - auto device = op_features->mutable_device(); +void SetCpuDevice(OpInfo* op_info) { + auto device = op_info->mutable_device(); device->set_type("CPU"); device->set_num_cores(10); device->set_bandwidth(10000000); // 10000000 KB/s = 10 GB/s @@ -413,15 +413,14 @@ class OpLevelCostEstimatorTest : public ::testing::Test { return estimator_.PredictCosts(op_context); } - int64 CountMatMulOperations(const OpInfo& op_features, + int64 CountMatMulOperations(const OpInfo& op_info, bool* found_unknown_shapes) const { - return estimator_.CountMatMulOperations(op_features, found_unknown_shapes); + return estimator_.CountMatMulOperations(op_info, found_unknown_shapes); } - int64 CountBatchMatMulOperations(const OpInfo& op_features, + int64 CountBatchMatMulOperations(const OpInfo& op_info, bool* found_unknown_shapes) const { - return estimator_.CountBatchMatMulOperations(op_features, - found_unknown_shapes); + return estimator_.CountBatchMatMulOperations(op_info, found_unknown_shapes); } void SetComputeMemoryOverlap(bool value) { -- GitLab From 7cadc29b011e2dd73f70034f280a13931c73900e Mon Sep 17 00:00:00 2001 From: Doe Hyun Yoon Date: Mon, 7 Jan 2019 16:10:33 -0800 Subject: [PATCH 448/622] Add PredictCostsAndReturnRunMetadata method to CostEstimator. PiperOrigin-RevId: 228254629 --- tensorflow/core/grappler/costs/BUILD | 1 + .../costs/analytical_cost_estimator.cc | 37 +++++++++++-------- .../costs/analytical_cost_estimator.h | 14 +++---- .../core/grappler/costs/cost_estimator.h | 14 +++++++ 4 files changed, 42 insertions(+), 24 deletions(-) diff --git a/tensorflow/core/grappler/costs/BUILD b/tensorflow/core/grappler/costs/BUILD index 15dc7074b9..92294379b5 100644 --- a/tensorflow/core/grappler/costs/BUILD +++ b/tensorflow/core/grappler/costs/BUILD @@ -171,6 +171,7 @@ cc_library( visibility = ["//visibility:public"], deps = [ "//tensorflow/core:lib", + "//tensorflow/core:protos_all_cc", ], ) diff --git a/tensorflow/core/grappler/costs/analytical_cost_estimator.cc b/tensorflow/core/grappler/costs/analytical_cost_estimator.cc index b7804ffaa5..eac6a0de3f 100644 --- a/tensorflow/core/grappler/costs/analytical_cost_estimator.cc +++ b/tensorflow/core/grappler/costs/analytical_cost_estimator.cc @@ -104,17 +104,15 @@ AnalyticalCostEstimator::AnalyticalCostEstimator(Cluster* cluster, bool use_static_shapes) : AnalyticalCostEstimator( cluster, absl::make_unique(), - ReadyNodeManagerFactory("FirstReady"), use_static_shapes, nullptr) {} + ReadyNodeManagerFactory("FirstReady"), use_static_shapes) {} AnalyticalCostEstimator::AnalyticalCostEstimator( Cluster* cluster, std::unique_ptr node_estimator, - std::unique_ptr node_manager, bool use_static_shapes, - RunMetadata* run_metadata) + std::unique_ptr node_manager, bool use_static_shapes) : cluster_(cluster), node_estimator_(std::move(node_estimator)), node_manager_(std::move(node_manager)), - use_static_shapes_(use_static_shapes), - run_metadata_(run_metadata) { + use_static_shapes_(use_static_shapes) { scheduler_ = absl::make_unique(use_static_shapes_, cluster_, node_manager_.get()); } @@ -128,6 +126,18 @@ Status AnalyticalCostEstimator::Initialize(const GrapplerItem& item) { Status AnalyticalCostEstimator::PredictCosts(const GraphDef& optimized_graph, CostGraphDef* cost_graph, Costs* costs) const { + RunMetadata run_metadata; + auto s = + PredictCostsAndReturnRunMetadata(optimized_graph, &run_metadata, costs); + if (s.ok() && cost_graph) { + cost_graph->Swap(run_metadata.mutable_cost_graph()); + } + return s; +} + +Status AnalyticalCostEstimator::PredictCostsAndReturnRunMetadata( + const GraphDef& optimized_graph, RunMetadata* run_metadata, + Costs* costs) const { GrapplerItem item = item_; item.graph = optimized_graph; @@ -138,7 +148,9 @@ Status AnalyticalCostEstimator::PredictCosts(const GraphDef& optimized_graph, } gtl::FlatMap name_to_cost_node; - if (cost_graph) { + CostGraphDef* cost_graph = nullptr; + if (run_metadata) { + cost_graph = run_metadata->mutable_cost_graph(); // TODO(pcma): Clear nodes in cost_graph after we make sure we always pass // in an empty cost_graph (a non-empty but incomplete cost_graph will cause // problems, e.g., no node_id in cost_graph) @@ -179,18 +191,13 @@ Status AnalyticalCostEstimator::PredictCosts(const GraphDef& optimized_graph, } } - *costs = scheduler_->Summary(run_metadata_); - // run_metadata_ gets step_stats and parition_graphs from Summary. - // Note that cost_graph could already point to the cost_graph field of - // run_metadata_, since both are set by the caller. - if (run_metadata_ && cost_graph && - run_metadata_->mutable_cost_graph() != cost_graph) - *run_metadata_->mutable_cost_graph() = *cost_graph; + // run_metadata gets step_stats and partition_graphs from Summary. + *costs = scheduler_->Summary(run_metadata); if (VLOG_IS_ON(1)) { bool verbose = VLOG_IS_ON(2); - if (run_metadata_) { - VLOG(1) << GetStatsStringFromRunMetadata(*run_metadata_, verbose); + if (run_metadata) { + VLOG(1) << GetStatsStringFromRunMetadata(*run_metadata, verbose); } else { RunMetadata run_metadata; scheduler_->GenerateRunMetadata(&run_metadata); diff --git a/tensorflow/core/grappler/costs/analytical_cost_estimator.h b/tensorflow/core/grappler/costs/analytical_cost_estimator.h index 2629672459..ade61f4051 100644 --- a/tensorflow/core/grappler/costs/analytical_cost_estimator.h +++ b/tensorflow/core/grappler/costs/analytical_cost_estimator.h @@ -39,16 +39,10 @@ class AnalyticalCostEstimator : public CostEstimator { public: // Does not take ownership of cluster. AnalyticalCostEstimator(Cluster* cluster, bool use_static_shapes); - // Does not take ownership of cluster or run_metadata - // - // When metadata is provided, step_stats and partition_graphs fields will - // always be filled during PredictCosts, and the cost_graph field of metadata - // will be filled only when cost_graph is not nullptr when invoking - // PredictCosts. AnalyticalCostEstimator(Cluster* cluster, std::unique_ptr node_estimator, std::unique_ptr node_manager, - bool use_static_shapes, RunMetadata* run_metadata); + bool use_static_shapes); ~AnalyticalCostEstimator() override {} // Initializes the estimator for the specified grappler item. @@ -61,6 +55,10 @@ class AnalyticalCostEstimator : public CostEstimator { Status PredictCosts(const GraphDef& optimized_graph, CostGraphDef* cost_graph, Costs* cost) const override; + Status PredictCostsAndReturnRunMetadata(const GraphDef& optimized_graph, + RunMetadata* run_metadata, + Costs* cost) const override; + const VirtualScheduler* GetScheduler() const { return scheduler_.get(); } private: @@ -70,8 +68,6 @@ class AnalyticalCostEstimator : public CostEstimator { std::unique_ptr node_manager_; bool use_static_shapes_; std::unique_ptr scheduler_; - - RunMetadata* run_metadata_; }; } // end namespace grappler diff --git a/tensorflow/core/grappler/costs/cost_estimator.h b/tensorflow/core/grappler/costs/cost_estimator.h index e3b3a36b09..725d81a881 100644 --- a/tensorflow/core/grappler/costs/cost_estimator.h +++ b/tensorflow/core/grappler/costs/cost_estimator.h @@ -20,6 +20,7 @@ limitations under the License. #include #include #include "tensorflow/core/lib/core/status.h" +#include "tensorflow/core/protobuf/config.pb.h" namespace tensorflow { class GraphDef; @@ -223,6 +224,19 @@ class CostEstimator { // not. virtual Status PredictCosts(const GraphDef& optimized_graph, CostGraphDef* cost_graph, Costs* cost) const = 0; + + // TODO(dyoon): Delete PredictCosts() with CostGraphDef as RunMetadata is a + // superset of CostGraphDef. + // Same method, but returns RunMetadata. + virtual Status PredictCostsAndReturnRunMetadata( + const GraphDef& optimized_graph, RunMetadata* run_metadata, + Costs* cost) const { + CostGraphDef* cost_graph = nullptr; + if (run_metadata) { + cost_graph = run_metadata->mutable_cost_graph(); + } + return PredictCosts(optimized_graph, cost_graph, cost); + } }; } // end namespace grappler -- GitLab From acfc65f0110e272abc8bd1abce867f2f534ba263 Mon Sep 17 00:00:00 2001 From: Yunxing Dai Date: Mon, 7 Jan 2019 16:11:20 -0800 Subject: [PATCH 449/622] [TF2XLA] Use dynamic dimension in SizeOp. This is needed if the number of elements in a tensor can be dynamic. PiperOrigin-RevId: 228254723 --- tensorflow/compiler/tf2xla/kernels/shape_op.cc | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/tensorflow/compiler/tf2xla/kernels/shape_op.cc b/tensorflow/compiler/tf2xla/kernels/shape_op.cc index 12830816ec..85b0367f73 100644 --- a/tensorflow/compiler/tf2xla/kernels/shape_op.cc +++ b/tensorflow/compiler/tf2xla/kernels/shape_op.cc @@ -20,6 +20,7 @@ limitations under the License. #include "tensorflow/compiler/tf2xla/xla_helpers.h" #include "tensorflow/compiler/tf2xla/xla_op_kernel.h" #include "tensorflow/compiler/tf2xla/xla_op_registry.h" +#include "tensorflow/compiler/xla/client/lib/constants.h" #include "tensorflow/compiler/xla/client/xla_builder.h" #include "tensorflow/core/framework/kernel_def_builder.h" #include "tensorflow/core/framework/tensor_shape.h" @@ -91,14 +92,20 @@ class SizeOp : public XlaOpKernel { void Compile(XlaOpKernelContext* ctx) override { const TensorShape input_shape = ctx->InputShape(0); - const int64 size = input_shape.num_elements(); - OP_REQUIRES(ctx, FastBoundsCheck(size, std::numeric_limits::max()), + OP_REQUIRES(ctx, + FastBoundsCheck(input_shape.num_elements(), + std::numeric_limits::max()), errors::InvalidArgument("Size does not work for tensors > " "int32 max.")); Tensor size_constant(DT_INT32, TensorShape({})); - size_constant.scalar()() = static_cast(size); - - ctx->SetConstantOutput(0, size_constant); + const int rank = input_shape.dims(); + xla::XlaBuilder* builder = ctx->builder(); + auto size = xla::One(builder, xla::U32); + for (int64 i = 0; i < rank; ++i) { + size = xla::Mul(size, xla::GetDimensionSize(ctx->Input(0), i)); + } + size = xla::ConvertElementType(size, xla::S32); + ctx->SetOutput(0, size); } }; -- GitLab From ec569e692435fb68191553fd7ffa5b633fdf1ef2 Mon Sep 17 00:00:00 2001 From: Andy Ly Date: Mon, 7 Jan 2019 16:20:48 -0800 Subject: [PATCH 450/622] [Grappler] Replace NodeMap with MutableGraphView in DependencyOptimizer. PiperOrigin-RevId: 228256183 --- tensorflow/core/grappler/optimizers/BUILD | 4 + .../optimizers/dependency_optimizer.cc | 456 +++++++----------- .../optimizers/dependency_optimizer.h | 24 +- .../optimizers/dependency_optimizer_test.cc | 9 +- 4 files changed, 202 insertions(+), 291 deletions(-) diff --git a/tensorflow/core/grappler/optimizers/BUILD b/tensorflow/core/grappler/optimizers/BUILD index 7e29cee86a..5a328a91c5 100644 --- a/tensorflow/core/grappler/optimizers/BUILD +++ b/tensorflow/core/grappler/optimizers/BUILD @@ -303,14 +303,18 @@ cc_library( ":constant_folding", ":graph_optimizer", "//tensorflow/core:framework", + "//tensorflow/core:graph", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", "//tensorflow/core:protos_all_cc", "//tensorflow/core/grappler:grappler_item", + "//tensorflow/core/grappler:mutable_graph_view", "//tensorflow/core/grappler:op_types", "//tensorflow/core/grappler:utils", "//tensorflow/core/grappler/costs:graph_properties", "//tensorflow/core/grappler/utils:topological_sort", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:flat_hash_set", ], ) diff --git a/tensorflow/core/grappler/optimizers/dependency_optimizer.cc b/tensorflow/core/grappler/optimizers/dependency_optimizer.cc index 7fee3ae9d5..9bceb6b906 100644 --- a/tensorflow/core/grappler/optimizers/dependency_optimizer.cc +++ b/tensorflow/core/grappler/optimizers/dependency_optimizer.cc @@ -18,10 +18,14 @@ limitations under the License. #include #include +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" #include "tensorflow/core/framework/node_def.pb.h" #include "tensorflow/core/framework/op.h" +#include "tensorflow/core/graph/tensor_id.h" #include "tensorflow/core/grappler/costs/graph_properties.h" #include "tensorflow/core/grappler/grappler_item.h" +#include "tensorflow/core/grappler/mutable_graph_view.h" #include "tensorflow/core/grappler/op_types.h" #include "tensorflow/core/grappler/optimizers/constant_folding.h" #include "tensorflow/core/grappler/utils.h" @@ -38,20 +42,15 @@ namespace grappler { namespace { -bool RemoveInput(NodeDef* node, const string& input, NodeMap* node_map) { - bool removed_input = false; - int pos = 0; - while (pos < node->input_size()) { - if (node->input(pos) == input) { - node->mutable_input()->SwapElements(pos, node->input_size() - 1); - node->mutable_input()->RemoveLast(); - node_map->RemoveOutput(NodeName(input), node->name()); - removed_input = true; - } else { - ++pos; - } +// Builds a map from the &graph->node(i) to i. +absl::flat_hash_map BuildNodeToIdx(const GraphDef& graph) { + // Set up &node -> index map. + absl::flat_hash_map node_to_idx; + for (int i = 0; i < graph.node_size(); ++i) { + const NodeDef& node = graph.node(i); + node_to_idx[&node] = i; } - return removed_input; + return node_to_idx; } } // namespace @@ -68,7 +67,10 @@ bool DependencyOptimizer::SafeToRemoveIdentity(const NodeDef& node) const { // The output values of this node may be needed. return false; } - const NodeDef* input = node_map_->GetNode(NodeName(node.input(0))); + + MutableGraphView::OutputPort port = graph_view_->GetRegularFanin( + MutableGraphView::InputPort(const_cast(&node), 0)); + NodeDef* input = port.node; CHECK(input != nullptr) << "node = " << node.name() << " input = " << node.input(0); // Don't remove Identity nodes corresponding to Variable reads or following @@ -79,20 +81,23 @@ bool DependencyOptimizer::SafeToRemoveIdentity(const NodeDef& node) const { // Don't turn Identity nodes following Switch into NoOp or remove them // if it requires anchoring a control dependencies the Switch node, which // is not valid. - if (str_util::StartsWith(node.name(), kConstantFoldingCtrl)) { - // TODO(rmlarsen): Try to remove this artificial contraint. + MutableGraphView::OutputPort control_port(const_cast(&node), + Graph::kControlSlot); + if (!graph_view_->GetFanout(control_port).empty()) { return false; } } - for (auto consumer : node_map_->GetOutputs(node.name())) { - if (node.input_size() > 1 && IsMerge(*consumer)) { + bool node_has_multiple_inputs = + graph_view_->NumFanins(node, /*include_controlling_nodes=*/true) > 1; + for (auto consumer : + graph_view_->GetFanouts(node, /*include_controlled_nodes=*/true)) { + if (node_has_multiple_inputs && IsMerge(*consumer.node)) { return false; } if (IsSwitch(*input)) { - for (const string& consumer_input : consumer->input()) { - if (consumer_input == AsControlDependency(node.name())) { - return false; - } + if (graph_view_->HasFanin(*consumer.node, + {node.name(), Graph::kControlSlot})) { + return false; } } } @@ -126,7 +131,7 @@ bool DependencyOptimizer::SafeToConvertToNoOp(const NodeDef& node) const { if (!SafeToRemoveIdentity(node)) { return false; } - if (NumNonControlOutputs(node, *node_map_) > 0) { + if (graph_view_->NumFanouts(node, /*include_controlled_nodes=*/false) > 0) { // The output values of this node may be needed. return false; } @@ -134,61 +139,56 @@ bool DependencyOptimizer::SafeToConvertToNoOp(const NodeDef& node) const { } int DependencyOptimizer::NumEdgesIfBypassed( - const NodeDef& node, const std::vector& output_nodes) const { + const NodeDef& node, int num_fanins, + const absl::flat_hash_set& fanout_edges) const { const bool is_multi_input_identity_n = IsIdentityN(node) && !IsIdentityNSingleInput(node); - const int num_outputs = output_nodes.size(); - const int num_inputs = node.input_size(); + const int num_fanouts = fanout_edges.size(); if (is_multi_input_identity_n) { // multi-input identity_n with input/output control dependencies will likely // increase number of edges after optimization. int num_edges_if_bypassed(0); - for (string input_node_name : node.input()) { - if (IsControlInput(input_node_name)) { - num_edges_if_bypassed += num_outputs; + int num_non_controlling_fanins = + graph_view_->NumFanins(node, /*include_controlling_nodes=*/false); + num_edges_if_bypassed += num_non_controlling_fanins; + num_edges_if_bypassed += + (num_fanins - num_non_controlling_fanins) * num_fanouts; + + for (const auto& fanout : fanout_edges) { + if (fanout.dst.port_id == Graph::kControlSlot) { + num_edges_if_bypassed += num_fanins; } else { ++num_edges_if_bypassed; } } - - for (auto consumer : output_nodes) { - for (int j = 0; j < consumer->input_size(); ++j) { - const TensorId consumer_input = ParseTensorName(consumer->input(j)); - if (consumer_input.node() == node.name()) { - if (IsControlInput(consumer_input)) { - num_edges_if_bypassed += num_inputs; - } else { - ++num_edges_if_bypassed; - } - } - } - } return num_edges_if_bypassed; } else { - return num_inputs * num_outputs; + return num_fanins * num_fanouts; } } bool DependencyOptimizer::BypassingNodeIsBeneficial( - const NodeDef& node, const std::vector& input_nodes, - const std::vector& output_nodes) const { + const NodeDef& node, + const absl::flat_hash_set& fanins, + const absl::flat_hash_set& fanout_edges) const { const bool is_identity = IsIdentity(node) || IsIdentityNSingleInput(node); const bool is_multi_input_identity_n = IsIdentityN(node) && !IsIdentityNSingleInput(node); - const int num_outputs = output_nodes.size(); - const int num_inputs = node.input_size(); + const int num_outputs = fanout_edges.size(); + const int num_inputs = fanins.size(); - if (NumEdgesIfBypassed(node, output_nodes) > num_inputs + num_outputs) { + if (NumEdgesIfBypassed(node, num_inputs, fanout_edges) > + num_inputs + num_outputs) { return false; } // Make sure that we don't increase the number of edges that cross // device boundaries. if ((num_inputs == 1 && num_outputs > 1 && - input_nodes[0]->device() != node.device()) || + fanins.begin()->node->device() != node.device()) || (num_inputs > 1 && num_outputs == 1 && - output_nodes[0]->device() != node.device())) { + fanout_edges.begin()->dst.node->device() != node.device())) { return false; } @@ -197,12 +197,12 @@ bool DependencyOptimizer::BypassingNodeIsBeneficial( // cost before and after. const string& node_dev = node.device(); int num_cross_in = 0; - for (NodeDef* input_node : input_nodes) { - num_cross_in += static_cast(input_node->device() != node_dev); + for (const auto& fanin : fanins) { + num_cross_in += static_cast(fanin.node->device() != node_dev); } int num_cross_out = 0; - for (NodeDef* output_node : output_nodes) { - num_cross_out += static_cast(output_node->device() != node_dev); + for (const auto& fanout : fanout_edges) { + num_cross_out += static_cast(fanout.dst.node->device() != node_dev); } if ((is_identity || is_multi_input_identity_n) && num_cross_in > 0 && @@ -216,10 +216,10 @@ bool DependencyOptimizer::BypassingNodeIsBeneficial( // Make sure we do not increase the number of device crossings. const int num_cross_before = num_cross_in + num_cross_out; int num_cross_after = 0; - for (NodeDef* input_node : input_nodes) { - for (NodeDef* output_node : output_nodes) { + for (const auto& fanin : fanins) { + for (const auto& fanout : fanout_edges) { num_cross_after += - static_cast(input_node->device() != output_node->device()); + static_cast(fanin.node->device() != fanout.dst.node->device()); } } if (num_cross_after > num_cross_before) { @@ -228,45 +228,32 @@ bool DependencyOptimizer::BypassingNodeIsBeneficial( return true; } -void DependencyOptimizer::OptimizeNode(int node_idx, - SetVector* nodes_to_simplify, - std::set* nodes_to_delete) { - NodeDef* node = optimized_graph_->mutable_node(node_idx); +void DependencyOptimizer::OptimizeNode(NodeDef* node, + SetVector* nodes_to_simplify, + std::set* nodes_to_delete) { + const string node_name = node->name(); const bool is_noop = IsNoOp(*node); const bool is_identity = IsIdentity(*node) || IsIdentityNSingleInput(*node); const bool is_multi_input_identity = IsIdentityN(*node) && !IsIdentityNSingleInput(*node); - const string node_name = node->name(); // Constant nodes with no input control dependency are always executed early, // so we can prune all their output control dependencies. - if (IsConstant(*node) && node->input_size() == 0) { - const std::set output_nodes = node_map_->GetOutputs(node_name); - for (NodeDef* fanout : output_nodes) { - bool optimize_fanout = false; - bool data_connection = false; - for (int i = fanout->input_size() - 1; i >= 0; --i) { - const TensorId input_tensor = ParseTensorName(fanout->input(i)); - if (input_tensor.node() == node_name) { - if (input_tensor.index() < 0) { - fanout->mutable_input()->SwapElements(i, fanout->input_size() - 1); - fanout->mutable_input()->RemoveLast(); - optimize_fanout = true; - } else { - data_connection = true; - } - } - } - if (optimize_fanout) { - nodes_to_simplify->PushBack(node_to_idx_[fanout]); - if (!data_connection) { - node_map_->RemoveOutput(node_name, fanout->name()); - } + if (IsConstant(*node) && + graph_view_->NumFanins(*node, /*include_controlling_nodes=*/true) == 0) { + for (const auto& fanout : + graph_view_->GetFanouts(*node, /*include_controlled_nodes=*/true)) { + if (graph_view_->RemoveFanin(fanout.node->name(), + {node_name, Graph::kControlSlot})) { + nodes_to_simplify->PushBack(fanout.node); } } - if (node_map_->GetOutputs(node_name).empty() && fetch_nodes_known_ && + + if (graph_view_->NumFanouts(*node, /*include_controlled_nodes=*/true) == + 0 && + fetch_nodes_known_ && nodes_to_preserve_.find(node_name) == nodes_to_preserve_.end()) { // Mark the node for deletion. - nodes_to_delete->insert(node_to_idx_[node]); + nodes_to_delete->insert(node->name()); } return; } @@ -277,33 +264,23 @@ void DependencyOptimizer::OptimizeNode(int node_idx, << ") with NoOp."; // The outputs of this node are not consumed. Replace its inputs with // control dependencies and replace the op itself with the NoOp op. - std::unordered_set ctrl_inputs; - int pos = 0; - while (pos < node->input_size()) { - const string old_input = node->input(pos); - if (IsControlInput(old_input)) { - if (!ctrl_inputs.insert(old_input).second) { - // We found a duplicate control input. Remove it. - node->mutable_input()->SwapElements(pos, node->input_size() - 1); - node->mutable_input()->RemoveLast(); - } else { - ++pos; - } - continue; + int num_non_controlling_fanins = + graph_view_->NumFanins(*node, /*include_controlling_nodes=*/false); + if (num_non_controlling_fanins > 0) { + absl::flat_hash_set non_controlling_fanins( + node->input().begin(), + node->input().begin() + num_non_controlling_fanins); + graph_view_->RemoveAllFanins(node_name, + /*keep_controlling_fanins=*/true); + for (const string& fanin : non_controlling_fanins) { + TensorId tensor_id = ParseTensorName(fanin); + graph_view_->AddControllingFanin(node_name, tensor_id); + nodes_to_simplify->PushBack(graph_view_->GetNode(tensor_id.node())); } - // Replace a normal input with a control input. - const string ctrl_input = ConstantFolding::AddControlDependency( - old_input, optimized_graph_, node_map_.get()); - ctrl_inputs.insert(ctrl_input); - node->set_input(pos, ctrl_input); - node_map_->UpdateInput(node_name, old_input, ctrl_input); - const NodeDef* old_input_node = node_map_->GetNode(old_input); - nodes_to_simplify->PushBack(node_to_idx_[old_input_node]); - ++pos; } node->set_op("NoOp"); node->clear_attr(); - nodes_to_simplify->PushBack(node_to_idx_[node]); + nodes_to_simplify->PushBack(node); return; } @@ -357,110 +334,80 @@ void DependencyOptimizer::OptimizeNode(int node_idx, if (is_noop || ((is_identity || is_multi_input_identity) && SafeToRemoveIdentity(*node))) { - const auto& output_node_set = node_map_->GetOutputs(node_name); - const std::vector output_nodes(output_node_set.begin(), - output_node_set.end()); - const int num_inputs = node->input_size(); - std::vector input_nodes; - for (int i = 0; i < num_inputs; ++i) { - NodeDef* input_node = node_map_->GetNode(node->input(i)); - if (input_node == nullptr) { - LOG(ERROR) << "Invalid input " << node->input(i); - return; - } - input_nodes.push_back(input_node); - } + auto fanins = + graph_view_->GetFanins(*node, /*include_controlling_nodes=*/true); + auto fanout_edges = + graph_view_->GetFanoutEdges(*node, /*include_controlled_edges=*/true); - if (!BypassingNodeIsBeneficial(*node, input_nodes, output_nodes)) { + if (!BypassingNodeIsBeneficial(*node, fanins, fanout_edges)) { return; } + int num_non_controlling_fanins = + graph_view_->NumFanins(*node, /*include_controlling_nodes=*/false); VLOG(1) << "***** Rerouting input around\n" << node->DebugString(); // Now remove the node and re-wire its inputs to its outputs. - for (auto consumer : output_nodes) { + for (auto fanout_edge : fanout_edges) { bool updated_consumer = false; + NodeDef* consumer = fanout_edge.dst.node; VLOG(1) << "consumer before:\n" << consumer->DebugString(); - for (int i = 0; i < num_inputs; ++i) { - const NodeDef* input = input_nodes[i]; - // Forward dependency from input to consumer if it doesn't already - // depend on it. - if ((is_identity && i == 0) || - (is_multi_input_identity && !IsControlInput(node->input(i)))) { - // Replace regular input from Identity node. - string new_input; - const string& input_to_forward = node->input(i); - CHECK(!IsControlInput(input_to_forward)); - for (int j = 0; j < consumer->input_size(); ++j) { - const TensorId old_input = ParseTensorName(consumer->input(j)); - if (old_input.node() == node_name) { - if (old_input.index() == i) { - // Regular input - new_input = input_to_forward; - node_map_->UpdateInput(consumer->name(), old_input.ToString(), - new_input); - consumer->set_input(j, new_input); - } else if (old_input.index() == -1) { - // Control dependency - new_input = AsControlDependency(NodeName(input_to_forward)); - node_map_->UpdateInput(consumer->name(), old_input.ToString(), - new_input); - consumer->set_input(j, new_input); - } + if (fanout_edge.src.port_id == Graph::kControlSlot) { + updated_consumer = graph_view_->RemoveFanin( + consumer->name(), {node_name, Graph::kControlSlot}); + if (updated_consumer) { + // Add all non controlling fanins of node as controlling fanins to + // consumer. + for (int i = 0; i < num_non_controlling_fanins; ++i) { + TensorId input = ParseTensorName(node->input(i)); + if (graph_view_->AddControllingFanin(consumer->name(), input)) { + nodes_to_simplify->PushBack(graph_view_->GetNode(input.node())); } } - updated_consumer = true; - } else { - // Forward dependency from input to consumer if it doesn't already - // depend on it. - if (node_map_->GetOutputs(input->name()).count(consumer) == 0) { - consumer->add_input(AsControlDependency(input->name())); - node_map_->AddOutput(input->name(), consumer->name()); - nodes_to_simplify->PushBack(node_to_idx_[input]); - updated_consumer = true; - } } + } else { + updated_consumer = graph_view_->UpdateFanin( + consumer->name(), {node_name, fanout_edge.src.port_id}, + ParseTensorName(node->input(fanout_edge.src.port_id))); } - // Remove dependency on node from consumer. - updated_consumer |= RemoveInput(consumer, AsControlDependency(node_name), - node_map_.get()); if (updated_consumer) { - nodes_to_simplify->PushBack(node_to_idx_[consumer]); + // Forward all controlling fanins of node to consumer. + for (int i = num_non_controlling_fanins; i < node->input_size(); ++i) { + TensorId input = ParseTensorName(node->input(i)); + if (graph_view_->AddFanin(consumer->name(), input)) { + nodes_to_simplify->PushBack(graph_view_->GetNode(input.node())); + } + } + nodes_to_simplify->PushBack(consumer); } VLOG(1) << "consumer after:\n" << consumer->DebugString(); } - node_map_->RemoveOutputs(node_name); if (fetch_nodes_known_ && nodes_to_preserve_.find(node_name) == nodes_to_preserve_.end()) { // Mark the node for deletion. - nodes_to_delete->insert(node_idx); + nodes_to_delete->insert(node_name); // Disconnect the node from its inputs to enable further optimizations. - node_map_->RemoveInputs(node_name); - node->clear_input(); + graph_view_->RemoveAllFanins(node_name, + /*keep_controlling_fanins=*/false); } } } -void DependencyOptimizer::CleanControlInputs() { - for (int i = 0; i < optimized_graph_->node_size(); ++i) { - DedupControlInputs(optimized_graph_->mutable_node(i)); - } -} - Status DependencyOptimizer::OptimizeDependencies() { - SetVector nodes_to_simplify; - std::set nodes_to_delete; - for (int i = 0; i < optimized_graph_->node_size(); ++i) { - const NodeDef& node = optimized_graph_->node(i); - if (IsNoOp(node) || IsIdentity(node) || IsIdentityN(node) || - IsConstant(node) || SafeToConvertToNoOp(node)) { - nodes_to_simplify.PushBack(i); + SetVector nodes_to_simplify; + std::set nodes_to_delete; + for (int i = 0; i < graph_view_->graph()->node_size(); ++i) { + NodeDef* node = graph_view_->graph()->mutable_node(i); + if (IsNoOp(*node) || IsIdentity(*node) || IsIdentityN(*node) || + IsConstant(*node) || SafeToConvertToNoOp(*node)) { + nodes_to_simplify.PushBack(node); } } while (!nodes_to_simplify.Empty()) { - int node_to_simplify = nodes_to_simplify.PopBack(); + NodeDef* node_to_simplify = nodes_to_simplify.PopBack(); // Discard nodes that were marked for deletion already. - while (nodes_to_delete.find(node_to_simplify) != nodes_to_delete.end()) { + while (nodes_to_delete.find(node_to_simplify->name()) != + nodes_to_delete.end()) { node_to_simplify = nodes_to_simplify.PopBack(); } OptimizeNode(node_to_simplify, &nodes_to_simplify, &nodes_to_delete); @@ -468,17 +415,17 @@ Status DependencyOptimizer::OptimizeDependencies() { if (fetch_nodes_known_) { VLOG(1) << "Deleted " << nodes_to_delete.size() << " out of " - << optimized_graph_->node_size() << " nodes."; - EraseNodesFromGraph(nodes_to_delete, optimized_graph_); - node_map_.reset(new NodeMap(optimized_graph_)); - BuildNodeToIdx(); + << graph_view_->graph()->node_size() << " nodes."; + graph_view_->DeleteNodes(nodes_to_delete); } return Status::OK(); } Status DependencyOptimizer::TransitiveReduction() { // PRECONDITION: optimized_graph_ must be sorted topologically. - const int num_nodes = optimized_graph_->node_size(); + GraphDef* graph = graph_view_->graph(); + auto node_to_idx = BuildNodeToIdx(*graph); + const int num_nodes = graph->node_size(); // Set up a compressed version of the graph to save a constant factor in the // expensive algorithm below. Also cache the set of control outputs and the // highest index of a target of any control output from each node. @@ -487,20 +434,20 @@ Status DependencyOptimizer::TransitiveReduction() { std::vector, 2>> control_outputs( num_nodes); for (int node_idx = 0; node_idx < num_nodes; ++node_idx) { - const NodeDef& node = optimized_graph_->node(node_idx); + const NodeDef& node = graph->node(node_idx); if (ModifiesFrameInfo(node) || !HasOpDef(node)) { // Ignore function nodes and nodes that modify frame info. continue; } for (int input_slot = 0; input_slot < node.input_size(); ++input_slot) { const string& input = node.input(input_slot); - const NodeDef* input_node = node_map_->GetNode(input); + const NodeDef* input_node = graph_view_->GetNode(NodeName(input)); if (ModifiesFrameInfo(*input_node) || IsMerge(*input_node)) { // Ignore edges from nodes that modify frame info and from Merge nodes, // because we cannot know which of it's input paths executes. continue; } - const int input_node_idx = node_to_idx_[input_node]; + const int input_node_idx = node_to_idx[input_node]; inputs[node_idx].push_back(input_node_idx); if (IsControlInput(input)) { ++num_controls; @@ -566,16 +513,12 @@ Status DependencyOptimizer::TransitiveReduction() { for (const auto& it : control_edges_to_remove) { const int target = it.first; - NodeDef* target_node = optimized_graph_->mutable_node(target); + const NodeDef& target_node = graph->node(target); + const string target_node_name = target_node.name(); for (const InputSlotAndSource& slot_and_source : it.second) { const int input_slot = slot_and_source.first; - const int source = slot_and_source.second; - const NodeDef& source_node = optimized_graph_->node(source); - CHECK_LT(input_slot, target_node->input_size()); - target_node->mutable_input()->SwapElements(input_slot, - target_node->input_size() - 1); - node_map_->RemoveOutput(source_node.name(), target_node->name()); - target_node->mutable_input()->RemoveLast(); + const TensorId tensor_id = ParseTensorName(target_node.input(input_slot)); + graph_view_->RemoveFanin(target_node_name, tensor_id); ++num_controls_removed; } } @@ -584,26 +527,17 @@ Status DependencyOptimizer::TransitiveReduction() { return Status::OK(); } -void DependencyOptimizer::BuildNodeToIdx() { - // Set up &node -> index map. - node_to_idx_.clear(); - for (int i = 0; i < optimized_graph_->node_size(); ++i) { - const NodeDef& node = optimized_graph_->node(i); - node_to_idx_[&node] = i; - } -} - // Suppose there are cross-device control inputs to node C from multiple nodes // that are located on another device, e.g., we have control edges: // A->C, B->C // where A and B are on device X and C is on device Y. // We can reduce cross-device communication by introducing an intermediate // NoOp node C' on device X and rewriting the control edges to: -// A->C', B->C', C' -> C +// A->C', B->C', C'->C void DependencyOptimizer::GroupCrossDeviceControlEdges() { - const int num_nodes = optimized_graph_->node_size(); + const int num_nodes = graph_view_->graph()->node_size(); for (int i = 0; i < num_nodes; ++i) { - NodeDef* node = optimized_graph_->mutable_node(i); + NodeDef* node = graph_view_->graph()->mutable_node(i); if (node->device().empty()) continue; // Creates new noop nodes for devices on which multiple control inputs are @@ -614,66 +548,50 @@ void DependencyOptimizer::GroupCrossDeviceControlEdges() { // that device. std::map noops; int num_noops = 0; - for (int j = 0; j < node->input_size(); ++j) { - if (IsControlInput(node->input(j))) { - const NodeDef* input = node_map_->GetNode(node->input(j)); - if (input != nullptr && !input->device().empty() && - input->device() != node->device()) { - auto emplace_result = noops.emplace(input->device(), nullptr); - if (!emplace_result.second && - emplace_result.first->second == nullptr) { - // This is the second cross-device control input from the same - // device. Creates an intermediate noop node on that device. - string group_name; - NodeDef* noop; - // Creates a fresh node name; there may be conflicting names from - // a previous iteration of the optimizer. - do { - group_name = AddPrefixToNodeName( - node->name(), - strings::StrCat("GroupCrossDeviceControlEdges_", num_noops)); - noop = node_map_->GetNode(group_name); - ++num_noops; - } while (noop != nullptr); - noop = optimized_graph_->add_node(); - noop->set_name(group_name); - noop->set_device(input->device()); - noop->set_op("NoOp"); - node_map_->AddNode(noop->name(), noop); - emplace_result.first->second = noop; - } + auto controlling_fanins = graph_view_->GetFanin( + MutableGraphView::InputPort(node, Graph::kControlSlot)); + for (const auto& controlling_fanin : controlling_fanins) { + const NodeDef* fanin_node = controlling_fanin.node; + if (!fanin_node->device().empty() && + fanin_node->device() != node->device()) { + auto emplace_result = noops.emplace(fanin_node->device(), nullptr); + if (!emplace_result.second && emplace_result.first->second == nullptr) { + // This is the second cross-device control input from the same + // device. Creates an intermediate noop node on that device. + string group_name; + NodeDef* noop; + // Creates a fresh node name; there may be conflicting names from + // a previous iteration of the optimizer. + do { + group_name = AddPrefixToNodeName( + node->name(), + strings::StrCat("GroupCrossDeviceControlEdges_", num_noops)); + noop = graph_view_->GetNode(group_name); + ++num_noops; + } while (noop != nullptr); + NodeDef new_node; + new_node.set_name(group_name); + new_node.set_device(fanin_node->device()); + new_node.set_op("NoOp"); + emplace_result.first->second = + graph_view_->AddNode(std::move(new_node)); } } } // Reroute existing control edges to go via the newly introduced NoOp nodes. - int pos = 0; - while (pos < node->input_size()) { - const string& input_name = node->input(pos); - if (IsControlInput(input_name)) { - NodeDef* input = node_map_->GetNode(input_name); - if (input == nullptr) { - ++pos; - } else { - auto it = noops.find(input->device()); - if (it == noops.end() || it->second == nullptr) { - ++pos; - } else { - node->mutable_input()->SwapElements(pos, node->input_size() - 1); - node->mutable_input()->RemoveLast(); - it->second->add_input(AsControlDependency(*input)); - node_map_->UpdateOutput(input_name, node->name(), - it->second->name()); - } - } - } else { - ++pos; + for (const auto& controlling_fanin : controlling_fanins) { + auto it = noops.find(controlling_fanin.node->device()); + if (it != noops.end() && it->second != nullptr) { + TensorId tensor_id(controlling_fanin.node->name(), Graph::kControlSlot); + graph_view_->RemoveFanin(node->name(), tensor_id); + graph_view_->AddFanin(it->second->name(), tensor_id); } } for (const auto& entry : noops) { if (entry.second) { - node->add_input(AsControlDependency(*entry.second)); - node_map_->AddOutput(entry.second->name(), node->name()); + graph_view_->AddFanin(node->name(), + {entry.second->name(), Graph::kControlSlot}); } } } @@ -681,22 +599,17 @@ void DependencyOptimizer::GroupCrossDeviceControlEdges() { Status DependencyOptimizer::Optimize(Cluster* cluster, const GrapplerItem& item, GraphDef* optimized_graph) { - optimized_graph_ = optimized_graph; - *optimized_graph_ = item.graph; + *optimized_graph = item.graph; nodes_to_preserve_ = item.NodesToPreserve(); fetch_nodes_known_ = !item.fetch.empty(); - CleanControlInputs(); + graph_view_.reset(new MutableGraphView(optimized_graph)); const int num_iterations = 2; for (int iteration = 0; iteration < num_iterations; ++iteration) { GRAPPLER_RETURN_IF_DEADLINE_EXCEEDED(); Status topo_sort_status; // Perform topological sort to prepare the graph for transitive reduction. - topo_sort_status = TopologicalSort(optimized_graph_); - // Set up index-based graph datastructures to speed up analysis steps below. - node_map_.reset(new NodeMap(optimized_graph_)); - BuildNodeToIdx(); - + topo_sort_status = TopologicalSort(optimized_graph); if (topo_sort_status.ok()) { // Remove redundant control dependencies. TF_RETURN_IF_ERROR(TransitiveReduction()); @@ -709,9 +622,6 @@ Status DependencyOptimizer::Optimize(Cluster* cluster, const GrapplerItem& item, // nodes. TF_RETURN_IF_ERROR(OptimizeDependencies()); - // Dedup control inputs. - CleanControlInputs(); - GroupCrossDeviceControlEdges(); } diff --git a/tensorflow/core/grappler/optimizers/dependency_optimizer.h b/tensorflow/core/grappler/optimizers/dependency_optimizer.h index 7b032673fb..eb7fc89080 100644 --- a/tensorflow/core/grappler/optimizers/dependency_optimizer.h +++ b/tensorflow/core/grappler/optimizers/dependency_optimizer.h @@ -17,6 +17,8 @@ limitations under the License. #define TENSORFLOW_CORE_GRAPPLER_OPTIMIZERS_DEPENDENCY_OPTIMIZER_H_ #include + +#include "tensorflow/core/grappler/mutable_graph_view.h" #include "tensorflow/core/grappler/optimizers/graph_optimizer.h" #include "tensorflow/core/grappler/utils.h" #include "tensorflow/core/protobuf/rewriter_config.pb.h" @@ -46,24 +48,22 @@ class DependencyOptimizer : public GraphOptimizer { // Returns true if bypassing node does not increase the number of edges or // number of edges crossing a device boundary. bool BypassingNodeIsBeneficial( - const NodeDef& node, const std::vector& input_nodes, - const std::vector& output_nodes) const; - int NumEdgesIfBypassed(const NodeDef& node, - const std::vector& output_nodes) const; + const NodeDef& node, + const absl::flat_hash_set& fanins, + const absl::flat_hash_set& fanout_edges) const; + int NumEdgesIfBypassed( + const NodeDef& node, int num_fanins, + const absl::flat_hash_set& fanout_edges) const; // Returns true if node is not an Identity node or if it is an Identity // that is safe to remove. bool SafeToRemoveIdentity(const NodeDef& node) const; // Returns true if it is safe to convert node to NoOp. bool SafeToConvertToNoOp(const NodeDef& node) const; - // Removes all duplicate control dependencies. - void CleanControlInputs(); - // Builds a map from the &optimized_graph_->node(i) to i. - void BuildNodeToIdx(); // Tries to optimize the node with the given index, possibly additional // optimizations by inserting nodes in nodes_to_simplify, and pruning nodes by // inserting them in nodes_to_delete. - void OptimizeNode(int node_idx, SetVector* nodes_to_simplify, - std::set* nodes_to_delete); + void OptimizeNode(NodeDef* node, SetVector* nodes_to_simplify, + std::set* nodes_to_delete); // Eliminates redundant control dependencies by computing the transitive // reduction of the graph. Status TransitiveReduction(); @@ -76,9 +76,7 @@ class DependencyOptimizer : public GraphOptimizer { RewriterConfig::Toggle opt_level_; bool fetch_nodes_known_; std::unordered_set nodes_to_preserve_; - std::unique_ptr node_map_; - std::unordered_map node_to_idx_; - GraphDef* optimized_graph_; // Not owned. + std::unique_ptr graph_view_; }; } // end namespace grappler diff --git a/tensorflow/core/grappler/optimizers/dependency_optimizer_test.cc b/tensorflow/core/grappler/optimizers/dependency_optimizer_test.cc index 8d70d9d5c7..673fc987a8 100644 --- a/tensorflow/core/grappler/optimizers/dependency_optimizer_test.cc +++ b/tensorflow/core/grappler/optimizers/dependency_optimizer_test.cc @@ -91,7 +91,7 @@ TEST_F(DependencyOptimizerTest, DependenciesDrivenByConstants) { // The 'z' node should have been optimized away leaving only 5 nodes. EXPECT_EQ(5, output.node_size()); - for (const NodeDef& node : item.graph.node()) { + for (const NodeDef& node : output.node()) { if (node.name() == "id1" || node.name() == "id2") { EXPECT_EQ(1, node.input_size()); EXPECT_EQ("add", node.input(0)); @@ -125,8 +125,8 @@ TEST_F(DependencyOptimizerTest, ChangeToNoop) { EXPECT_EQ(item.graph.node_size(), output.node_size()); int found = 0; - for (int i = 0; i < item.graph.node_size(); ++i) { - const NodeDef& node = item.graph.node(i); + for (int i = 0; i < output.node_size(); ++i) { + const NodeDef& node = output.node(i); // "add" should get turned into a NoOp and removed. EXPECT_NE("add", node.name()); if (node.name() == "id1") { @@ -164,7 +164,6 @@ TEST_F(DependencyOptimizerTest, ChangeToNoop_RepeatedInput) { item.graph.Swap(&output); status = optimizer.Optimize(nullptr, item, &output); TF_EXPECT_OK(status); - LOG(INFO) << output.DebugString(); EXPECT_EQ(item.graph.node_size(), output.node_size()); int found = 0; @@ -748,7 +747,7 @@ TEST_F(DependencyOptimizerTest, Identity_DeviceCrossing_ConsumerOnSameDevice) { GraphDef output; Status status = optimizer.Optimize(nullptr, item, &output); TF_EXPECT_OK(status); - LOG(INFO) << output.DebugString(); + EXPECT_EQ(3, output.node_size()); for (const auto& node : output.node()) { EXPECT_NE("x_on_2", node.name()); -- GitLab From fed73306b650d0b5c99689e8b142a610fd8a3488 Mon Sep 17 00:00:00 2001 From: Jian Li Date: Mon, 7 Jan 2019 16:32:46 -0800 Subject: [PATCH 451/622] Update unidirectional rnn test case. PiperOrigin-RevId: 228257798 --- tensorflow/lite/kernels/unidirectional_sequence_rnn_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/lite/kernels/unidirectional_sequence_rnn_test.cc b/tensorflow/lite/kernels/unidirectional_sequence_rnn_test.cc index 494c449589..de1f7818bd 100644 --- a/tensorflow/lite/kernels/unidirectional_sequence_rnn_test.cc +++ b/tensorflow/lite/kernels/unidirectional_sequence_rnn_test.cc @@ -255,7 +255,7 @@ class HybridUnidirectionalRNNOpModel : public UnidirectionalRNNOpModel { tensor_type_ = tensor_type; } - void SetWeights(int weights_idx, std::vector f) { + void SetWeights(int weights_idx, const std::vector& f) { if (tensor_type_ == TensorType_UINT8) { SymmetricQuantizeAndPopulate(weights_idx, f); } else { -- GitLab From b9762fed18234b2d87efc9786384e467008fc674 Mon Sep 17 00:00:00 2001 From: Eugene Zhulenev Date: Mon, 7 Jan 2019 16:50:53 -0800 Subject: [PATCH 452/622] Add benchmarks for pack_lhs PiperOrigin-RevId: 228260001 --- tensorflow/core/kernels/BUILD | 1 + .../eigen_spatial_convolutions_test.cc | 226 +++++++++++++++++- 2 files changed, 214 insertions(+), 13 deletions(-) diff --git a/tensorflow/core/kernels/BUILD b/tensorflow/core/kernels/BUILD index 136902e249..701bda7ef6 100644 --- a/tensorflow/core/kernels/BUILD +++ b/tensorflow/core/kernels/BUILD @@ -2511,6 +2511,7 @@ tf_cc_tests( ":eigen_helpers", "//tensorflow/core:test", "//tensorflow/core:test_main", + "@com_google_absl//absl/strings", ], ) diff --git a/tensorflow/core/kernels/eigen_spatial_convolutions_test.cc b/tensorflow/core/kernels/eigen_spatial_convolutions_test.cc index 22f71d6260..03002adec4 100644 --- a/tensorflow/core/kernels/eigen_spatial_convolutions_test.cc +++ b/tensorflow/core/kernels/eigen_spatial_convolutions_test.cc @@ -14,6 +14,8 @@ limitations under the License. ==============================================================================*/ #include "tensorflow/core/kernels/eigen_spatial_convolutions.h" + +#include "absl/strings/str_cat.h" #include "tensorflow/core/kernels/eigen_cuboid_convolution.h" #include "tensorflow/core/platform/test.h" #include "tensorflow/core/platform/test_benchmark.h" @@ -1540,22 +1542,187 @@ static void PackRhsHelper(int iters, pack_rhs(packed.data() + packed_offset, sub_mapper, depth, cols); } tensorflow::testing::StopTiming(); + tensorflow::testing::SetLabel( + absl::StrCat("patch: ", patch_rows, "x", patch_cols, " D", patch_depth, + "; num_patches=", num_patches, " patch_size=", patch_size, + " num_inputs=", num_inputs)); +} + +static void PackLhsHelper(int iters, + /* Input dimensions: */ + int input_depth, + /* Filter (kernel) dimensions: */ + int filter_count, int filter_cols, int filter_rows, + /* Block dimensions: */ + Index block_rows, Index block_cols) { + // Set random seed for benchmark repeatability. + srand(12345); + + eigen_assert(block_rows <= filter_count); + eigen_assert(block_cols <= input_depth * filter_rows * filter_cols); + + tensorflow::testing::UseRealTime(); + tensorflow::testing::StopTiming(); + + using Dimensions = Eigen::DSizes; + + // Default Eigen::Tensor layout is column major, so we configure dimensions + // starting from the inner most (`filter count` aka `kernel filers`). + Dimensions filter_dims(filter_count, filter_rows, filter_cols, input_depth); + + static const int packet_size = Eigen::internal::packet_traits::size; + + // We are going to reshape filter into 2D tensor. + using NewDimension = Eigen::DSizes; + + // Contraction dimensions. + using nocontract_t = Eigen::array; + using contract_t = Eigen::array; + + // Input to the ReshapeOp. It is the tensorflow TTypes::Tensor + // with ColMajor layout, instead of RowMajor. But that doesn't make any + // difference, because TensorContraction swaps LHS with RHS for row major + // inputs, and contraction mapper always works with column major data. + using ArgType = TensorMap, Eigen::Aligned>; + + using Evaluator = + TensorEvaluator, + Eigen::DefaultDevice>; + + using InputMapper = Eigen::internal::TensorContractionInputMapper< + float, Index, Eigen::internal::Lhs, Evaluator, // + nocontract_t, contract_t, // + packet_size, // + /*inner_dim_contiguous*/ true, // + /*inner_dim_reordered*/ false, // + /*Alignment*/ 0>; + + using SubMapper = Eigen::internal::TensorContractionSubMapper< + float, Index, Eigen::internal::Lhs, Evaluator, // + nocontract_t, contract_t, // + packet_size, // + /*inner_dim_contiguous*/ true, // + /*inner_dim_reordered*/ false, // + /*Alignment*/ 0>; + +#if defined(TENSORFLOW_USE_MKLDNN_CONTRACTION_KERNEL) + using PackLhsImpl = Eigen::internal::mkldnn_gemm_pack; +#else + using PackLhsImpl = + Eigen::internal::gemm_pack_lhs; +#endif + + Eigen::DefaultDevice device; - std::ostringstream stringStream; - stringStream << "patch: " << patch_rows << "x" << patch_cols << " D" - << patch_depth << "; num_patches=" << num_patches - << " patch_size=" << patch_size << " num_inputs=" << num_inputs; - tensorflow::testing::SetLabel(stringStream.str()); + // We will reshape kernel into 2D tensor. + NewDimension reshape_dims; + reshape_dims[0] = filter_count; + reshape_dims[1] = input_depth * filter_rows * filter_cols; + + // We are going to contract along the 'in_depth * filter_rows * filter_cols`. + nocontract_t nocontract_dim = {0}; + contract_t contract_dim = {1}; + + // These values computed using the algorithm in TensorContraction.h, with + // 'nocontract_dim' and 'contract_dim' values specified above. + nocontract_t nocontract_strides = {1}; + contract_t contract_strides = {filter_count}; + nocontract_t i_strides = {1}; + contract_t k_strides = {1}; + + // We use tensor of the same dimensions to store packed data. + Tensor packed(filter_dims); + + // We generate multiple filter tensors, around 512mb in total size to measure + // realistic workload when input data in not in L1-L3 cache. + size_t input_bytes = filter_dims.TotalSize() * sizeof(float); + size_t mem_size_bytes = 1024 * 1024 * 512; + size_t num_filters = + std::max(static_cast(1), mem_size_bytes / input_bytes); + + std::vector> filters; + std::vector evaluators; + std::vector input_mappers; + + for (int i = 0; i < num_filters; ++i) { + filters.emplace_back(filter_dims); + filters[i].setRandom(); + + ArgType tensor_map(filters[i].data(), filter_dims); + + const auto reshape_op = + TensorReshapingOp(tensor_map, reshape_dims); + + evaluators.emplace_back(reshape_op, device); + + input_mappers.emplace_back(evaluators[i], nocontract_strides, i_strides, + contract_strides, k_strides); + } + + PackLhsImpl pack_lhs; + + const Index packed_total_size = filter_dims.TotalSize(); + + // Round up row/col/memory offsets to make them multiple of packet size. + const auto round_up = [](const Index idx) { + return (idx / packet_size) * packet_size; + }; + + // Block rows is in the [0, filter_count) range. + // Block cols is in the [0, filter_rows * filter_cols * input_depth) range. + + const Index max_row = filter_count; + const Index max_col = filter_rows * filter_cols * input_depth; + + tensorflow::testing::StartTiming(); + for (int i = 0; i < iters; ++i) { + int filter_idx = + num_filters == 1 ? 1 : internal::random(0, num_filters - 1); + + Index row_offset = round_up(internal::random(0, max_row - 10)); + Index col_offset = round_up(internal::random(0, max_col - 10)); + + Index rows = std::min(block_rows, max_row - row_offset); + Index cols = std::min(block_cols, max_col - col_offset); + + // Write packed data to random memory location to emulate cold caches. + Index packed_offset = round_up( + internal::random(0, packed_total_size - rows * cols - 1)); + + SubMapper sub_mapper = + input_mappers[filter_idx].getSubMapper(row_offset, col_offset); + + // NOTE: Eigen gemm_pack_lhs accepts contraction depth (k-th dimension) as a + // first argument (aka block cols). MKL-DNN pack is generic for lhs and rhs + // and accepts block rows and cols in the same order for lhs and rhs. +#if defined(TENSORFLOW_USE_MKLDNN_CONTRACTION_KERNEL) + pack_lhs(packed.data() + packed_offset, sub_mapper, rows, cols); +#else + pack_lhs(packed.data() + packed_offset, sub_mapper, cols, rows); +#endif + } + tensorflow::testing::StopTiming(); + tensorflow::testing::SetLabel(absl::StrCat( + "filter: count=", filter_count, " dims=", filter_rows, "x", filter_cols, + "; input: depth=", input_depth, "; num_filers=", num_filters)); } // -------------------------------------------------------------------------- // -// Macro argumentnames: +// Pack RHS +// +// Macro argument names: // N: batch size // H: height // W: width // C: input channels // FC: filter channles // FH: filter height +// FW: filter width // SH: stride in height dimensions // SW: stride in width dimensions // BR: block rows @@ -1563,16 +1730,16 @@ static void PackRhsHelper(int iters, #define BM_CONCAT(a, b) a##b -#define BM_NAME(prefix, N, H, W, C, FC, FH, FW, SH, SW, BR, BC) \ +#define BM_RHS_NAME(prefix, N, H, W, C, FC, FH, FW, SH, SW, BR, BC) \ BM_CONCAT(BM_##prefix##_##N##_##H##x##W##_IC##C##_FC##FC##_##FH##x##FW, \ _s##SH##x##SW##_B##BR##x##BC) -#define BM_PackRhs(N, H, W, C, FC, FH, FW, SH, SW, BR, BC) \ - static void BM_NAME(PackRhs, N, H, W, C, FC, FH, FW, SH, SW, BR, \ - BC)(int iters) { \ - PackRhsHelper(iters, N, H, W, C, FC, FH, FW, SH, SW, BR, BC); \ - } \ - BENCHMARK(BM_NAME(PackRhs, N, H, W, C, FC, FH, FW, SH, SW, BR, BC)) +#define BM_PackRhs(N, H, W, C, FC, FH, FW, SH, SW, BR, BC) \ + static void BM_RHS_NAME(PackRhs, N, H, W, C, FC, FH, FW, SH, SW, BR, \ + BC)(int iters) { \ + PackRhsHelper(iters, N, H, W, C, FC, FH, FW, SH, SW, BR, BC); \ + } \ + BENCHMARK(BM_RHS_NAME(PackRhs, N, H, W, C, FC, FH, FW, SH, SW, BR, BC)) // Number of input channel (input depth) it equal to the number of patch // channels (patch depth). @@ -1645,4 +1812,37 @@ BM_PackRhs(/*batch*/ 32, // /*filter*/ 3, 3, // /*stride*/ 2, 2, // /*block*/ 36, 432); + +// -------------------------------------------------------------------------- // +// Pack LHS +// +// Macro argument names: +// C: input channels +// FC: filter channels +// FH: filter height +// FW: filter width +// BR: block rows +// BC: block cols + +#define BM_LHS_NAME(prefix, C, FC, FH, FW, BR, BC) \ + BM_CONCAT(BM_##prefix##_##C##_FC##FC##_##FH##x##FW, _B##BR##x##BC) + +#define BM_PackLhs(C, FC, FH, FW, BR, BC) \ + static void BM_LHS_NAME(PackLhs, C, FC, FH, FW, BR, BC)(int iters) { \ + PackLhsHelper(iters, C, FC, FH, FW, BR, BC); \ + } \ + BENCHMARK(BM_LHS_NAME(PackLhs, C, FC, FH, FW, BR, BC)) + +// Number of input channel (input depth) it equal to the number of patch +// channels (patch depth). + +BM_PackLhs(/*input channels*/ 128, // + /*filter channels*/ 1024, // + /*filter dims*/ 3, 3, // + /*block*/ 256, 56); + +BM_PackLhs(/*input channels*/ 128, // + /*filter channels*/ 1024, // + /*filter dims*/ 3, 3, // + /*block*/ 56, 256); } // namespace Eigen -- GitLab From a253b9eab582307315567ba36d2b25033ad29b8b Mon Sep 17 00:00:00 2001 From: Anna R Date: Mon, 7 Jan 2019 16:54:22 -0800 Subject: [PATCH 453/622] Change tf_upgrade_v2.py script to wrap labels argument with tf.stop_gradients in tf.softmax_cross_entropy_with_logits call. PiperOrigin-RevId: 228260462 --- tensorflow/tools/compatibility/BUILD | 1 + tensorflow/tools/compatibility/reorders_v2.py | 1 + .../compatibility/testdata/test_file_v1_12.py | 9 ++++ .../tools/compatibility/tf_upgrade_v2.py | 45 +++++++++++++++++-- .../tools/compatibility/tf_upgrade_v2_test.py | 28 +++++++----- 5 files changed, 69 insertions(+), 15 deletions(-) diff --git a/tensorflow/tools/compatibility/BUILD b/tensorflow/tools/compatibility/BUILD index 452e6db941..31dbc02963 100644 --- a/tensorflow/tools/compatibility/BUILD +++ b/tensorflow/tools/compatibility/BUILD @@ -69,6 +69,7 @@ py_library( ":ast_edits", ":renames_v2", ":reorders_v2", + "@six_archive//:six", ], ) diff --git a/tensorflow/tools/compatibility/reorders_v2.py b/tensorflow/tools/compatibility/reorders_v2.py index 3f05aea6ca..5c11388516 100644 --- a/tensorflow/tools/compatibility/reorders_v2.py +++ b/tensorflow/tools/compatibility/reorders_v2.py @@ -65,6 +65,7 @@ reorders = { 'tf.nn.moments': ['x', 'axes', 'shift', 'name', 'keep_dims'], 'tf.nn.pool': ['input', 'window_shape', 'pooling_type', 'padding', 'dilation_rate', 'strides', 'name', 'data_format'], 'tf.nn.separable_conv2d': ['input', 'depthwise_filter', 'pointwise_filter', 'strides', 'padding', 'rate', 'name', 'data_format'], + 'tf.nn.softmax_cross_entropy_with_logits': ['_sentinel', 'labels', 'logits', 'dim', 'name'], 'tf.nn.space_to_batch': ['input', 'paddings', 'block_size', 'name'], 'tf.nn.space_to_depth': ['input', 'block_size', 'name', 'data_format'], 'tf.nn.weighted_moments': ['x', 'axes', 'frequency_weights', 'name', 'keep_dims'], diff --git a/tensorflow/tools/compatibility/testdata/test_file_v1_12.py b/tensorflow/tools/compatibility/testdata/test_file_v1_12.py index 5ce4dd49ad..2663762aa7 100644 --- a/tensorflow/tools/compatibility/testdata/test_file_v1_12.py +++ b/tensorflow/tools/compatibility/testdata/test_file_v1_12.py @@ -70,6 +70,15 @@ class TestUpgrade(test_util.TensorFlowTestCase): [0], tf.argmin([[1, 3, 2]], name='abc', dimension=1)) + @test_util.run_v1_only("b/120545219") + def testSoftmaxCrossEntropyWithLogits(self): + out = tf.nn.softmax_cross_entropy_with_logits( + logits=[0.1, 0.8], labels=[0, 1]) + self.assertAllClose(out, 0.40318608) + out = tf.nn.softmax_cross_entropy_with_logits_v2( + logits=[0.1, 0.8], labels=[0, 1]) + self.assertAllClose(out, 0.40318608) + if __name__ == "__main__": test_lib.main() diff --git a/tensorflow/tools/compatibility/tf_upgrade_v2.py b/tensorflow/tools/compatibility/tf_upgrade_v2.py index 9d9c5878f7..2dbbe27598 100644 --- a/tensorflow/tools/compatibility/tf_upgrade_v2.py +++ b/tensorflow/tools/compatibility/tf_upgrade_v2.py @@ -21,6 +21,7 @@ from __future__ import print_function import ast import pasta +import six from tensorflow.tools.compatibility import ast_edits from tensorflow.tools.compatibility import renames_v2 @@ -94,6 +95,10 @@ class TFAPIChangeSpec(ast_edits.APIChangeSpec): "tf.convert_to_tensor": { "preferred_dtype": "dtype_hint" }, + "tf.nn.softmax_cross_entropy_with_logits": { + "dim": "axis", + "_sentinel": None, + }, "tf.nn.softmax_cross_entropy_with_logits_v2": { "dim": "axis" }, @@ -682,6 +687,10 @@ class TFAPIChangeSpec(ast_edits.APIChangeSpec): "tf.norm", "tf.reverse_sequence", "tf.sparse_split", + # tf.nn.softmax_cross_entropy_with_logits *must* be called with + # keyword arguments. Add keyword arguments in rare case when they + # are not specified. + "tf.nn.softmax_cross_entropy_with_logits", } # Functions that were reordered should be changed to the new keyword args @@ -714,6 +723,8 @@ class TFAPIChangeSpec(ast_edits.APIChangeSpec): "tf.to_float": self._cast_transformer, "tf.to_int32": self._cast_transformer, "tf.to_int64": self._cast_transformer, + "tf.nn.softmax_cross_entropy_with_logits": + self._softmax_cross_entropy_with_logits_transformer, } decay_function_comment = ( @@ -950,10 +961,6 @@ class TFAPIChangeSpec(ast_edits.APIChangeSpec): "'deterministic' arguments. Now it takes a single 'seed' arg. If " "'seed' is zero, the execution is random and deterministic " "otherwise", - "tf.nn.softmax_cross_entropy_with_logits": - "tf.nn.softmax_cross_entropy_with_logits behavior has changed. " - "'labels' needs to be wrapped with tf.stop_gradient to keep the " - "old behavior. Also, 'dim' argument has been renamed to 'axis'.", "tf.test.assert_equal_graph_def": "tf.assert_equal_graph_def no longer takes 'checkpoint_v2' " "argument. 'checkpoint_v2' now defaults to True.", @@ -1228,6 +1235,36 @@ class TFAPIChangeSpec(ast_edits.APIChangeSpec): dtype_str))) return node + @staticmethod + def _softmax_cross_entropy_with_logits_transformer( + parent, node, full_name, name, logs, errors): + def _wrap_label(parent, old_value): + """Wrap labels with tf.stop_gradient.""" + if six.PY3: + new_value = ast.Call( + ast.Name(id="tf.stop_gradient", ctx=ast.Load()), + [old_value], []) + else: + new_value = ast.Call( + ast.Name(id="tf.stop_gradient", ctx=ast.Load()), + [old_value], [], None, None) + + # This copies the prefix and suffix on old_value to new_value. + pasta.ast_utils.replace_child(parent, old_value, new_value) + ast.copy_location(new_value, old_value) + + # Check if we have a labels keyword arg + for karg in node.keywords: + if karg.arg == "labels": + logs.append((node.lineno, node.col_offset, + "Changing labels arg of " + "tf.nn.softmax_cross_entropy_with_logits to " + "tf.stop_gradient(labels). Please check this " + "transformation.\n")) + _wrap_label(karg, karg.value) + return node + return node + @staticmethod def _batch_gather_transformer(parent, node, full_name, name, logs, errors): # Check if the call already has a batch_dims argument diff --git a/tensorflow/tools/compatibility/tf_upgrade_v2_test.py b/tensorflow/tools/compatibility/tf_upgrade_v2_test.py index d8bc0ba75a..80d86d7a2b 100644 --- a/tensorflow/tools/compatibility/tf_upgrade_v2_test.py +++ b/tensorflow/tools/compatibility/tf_upgrade_v2_test.py @@ -684,26 +684,32 @@ bazel-bin/tensorflow/tools/compatibility/update/generate_v2_reorders_map self.assertEqual(new_text, expected_text) def testSoftMaxCrossEntropyWithLogitsV2(self): - text = "tf.nn.softmax_cross_entropy_with_logits_v2(labels, logits, dim=2)" + text = ( + "tf.nn.softmax_cross_entropy_with_logits_v2(" + "labels=labels, logits=logits, dim=2)") expected_text = ( - "tf.nn.softmax_cross_entropy_with_logits(labels, logits, axis=2)") + "tf.nn.softmax_cross_entropy_with_logits(" + "labels=labels, logits=logits, axis=2)") _, unused_report, errors, new_text = self._upgrade(text) self.assertEqual(new_text, expected_text) self.assertFalse(errors) def testSoftMaxCrossEntropyWithLogits(self): - text = "tf.nn.softmax_cross_entropy_with_logits(labels, logits, dim=2)" + text = ("tf.nn.softmax_cross_entropy_with_logits(" + "labels=labels, logits=logits, dim=2)") expected_text = ( - "tf.nn.softmax_cross_entropy_with_logits(labels, logits, dim=2)") - _, report, errors, new_text = self._upgrade(text) + "tf.nn.softmax_cross_entropy_with_logits(" + "labels=tf.stop_gradient(labels), logits=logits, axis=2)") + _, unused_report, unused_errors, new_text = self._upgrade(text) self.assertEqual(new_text, expected_text) - self.assertIn( - "tf.nn.softmax_cross_entropy_with_logits requires manual check", - errors[0]) - self.assertIn( - "tf.nn.softmax_cross_entropy_with_logits behavior has changed. ", - report) + + text = ("tf.nn.softmax_cross_entropy_with_logits(" + "labels=foo(bar))") + expected_text = ("tf.nn.softmax_cross_entropy_with_logits(" + "labels=tf.stop_gradient(foo(bar)))") + _, unused_report, unused_errors, new_text = self._upgrade(text) + self.assertEqual(expected_text, new_text) def testSparseMatmul(self): text = ("tf.sparse_matmul(a, b, c, d, e, f, g)\n") -- GitLab From 09713e439363d763ca7c12d0c279b8d55d5b6053 Mon Sep 17 00:00:00 2001 From: Davide Libenzi Date: Mon, 7 Jan 2019 17:10:18 -0800 Subject: [PATCH 454/622] We should be using on host shape as the device one can have tuples in place of complex or S64 types. PiperOrigin-RevId: 228262394 --- tensorflow/compiler/xrt/tests/raw_api_test.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/tensorflow/compiler/xrt/tests/raw_api_test.cc b/tensorflow/compiler/xrt/tests/raw_api_test.cc index c8479cb778..be0c4b9392 100644 --- a/tensorflow/compiler/xrt/tests/raw_api_test.cc +++ b/tensorflow/compiler/xrt/tests/raw_api_test.cc @@ -956,6 +956,7 @@ TEST(RawApiTest, CompileAndExecuteWithS64Argument) { xrt::XRTExecutionConfig e; e.set_release_input_handles(true); e.set_release_compilation_handle(true); + e.set_return_exploded_tuple(true); Scope root = Scope::NewRootScope().WithDevice(DeviceFromFlag()); auto e_config = -- GitLab From fbbecc81be3b1eab0c15020ce3b0566e60e25c7f Mon Sep 17 00:00:00 2001 From: Yunxing Dai Date: Mon, 7 Jan 2019 17:48:53 -0800 Subject: [PATCH 455/622] Dynamic Padder - Change HloGetDimensionSizeRewriter to use DynamicDimensionInference, when trying to get the size of a dynamic dimension, GetDimensionSize is replaced by the hlo instruction the represents the dynamic size instead. - Implements Dynamic Padder, which uses DynamicDimensionInference to analyze the graph and inserts certain operations to make sure the result is the same as if there is no padding in the bounded shapes. PiperOrigin-RevId: 228266743 --- tensorflow/compiler/xla/service/BUILD | 41 +++++ .../compiler/xla/service/dynamic_padder.cc | 161 ++++++++++++++++++ .../compiler/xla/service/dynamic_padder.h | 44 +++++ .../xla/service/dynamic_padder_test.cc | 152 +++++++++++++++++ .../hlo_get_dimension_size_rewriter.cc | 26 ++- .../service/hlo_get_dimension_size_rewriter.h | 4 +- 6 files changed, 421 insertions(+), 7 deletions(-) create mode 100644 tensorflow/compiler/xla/service/dynamic_padder.cc create mode 100644 tensorflow/compiler/xla/service/dynamic_padder.h create mode 100644 tensorflow/compiler/xla/service/dynamic_padder_test.cc diff --git a/tensorflow/compiler/xla/service/BUILD b/tensorflow/compiler/xla/service/BUILD index 9bc6218f75..ec63ae1ac6 100644 --- a/tensorflow/compiler/xla/service/BUILD +++ b/tensorflow/compiler/xla/service/BUILD @@ -1938,6 +1938,46 @@ cc_library( ], ) +cc_library( + name = "dynamic_padder", + srcs = ["dynamic_padder.cc"], + hdrs = ["dynamic_padder.h"], + deps = [ + ":dynamic_dimension_inference", + ":hlo_dce", + ":hlo_pass", + "//tensorflow/compiler/xla:literal", + "//tensorflow/compiler/xla:literal_util", + "//tensorflow/compiler/xla:shape_util", + "//tensorflow/compiler/xla:status_macros", + "//tensorflow/compiler/xla:util", + "//tensorflow/core:lib", + "@com_google_absl//absl/algorithm:container", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:flat_hash_set", + ], +) + +tf_cc_test( + name = "dynamic_padder_test", + srcs = ["dynamic_padder_test.cc"], + deps = [ + ":dynamic_padder", + "//tensorflow/compiler/xla:debug_options_flags", + "//tensorflow/compiler/xla:literal", + "//tensorflow/compiler/xla:shape_util", + "//tensorflow/compiler/xla:test", + "//tensorflow/compiler/xla:test_helpers", + "//tensorflow/compiler/xla:xla_data_proto", + "//tensorflow/compiler/xla/client:xla_builder", + "//tensorflow/compiler/xla/service:hlo", + "//tensorflow/compiler/xla/service:hlo_matchers", + "//tensorflow/compiler/xla/service:hlo_runner", + "//tensorflow/compiler/xla/tests:hlo_test_base", + "//tensorflow/core:test", + ], +) + tf_cc_test( name = "dynamic_dimension_inference_test", srcs = ["dynamic_dimension_inference_test.cc"], @@ -2981,6 +3021,7 @@ cc_library( srcs = ["hlo_get_dimension_size_rewriter.cc"], hdrs = ["hlo_get_dimension_size_rewriter.h"], deps = [ + ":dynamic_dimension_inference", ":hlo", ":hlo_pass", ":shape_inference", diff --git a/tensorflow/compiler/xla/service/dynamic_padder.cc b/tensorflow/compiler/xla/service/dynamic_padder.cc new file mode 100644 index 0000000000..4db280f817 --- /dev/null +++ b/tensorflow/compiler/xla/service/dynamic_padder.cc @@ -0,0 +1,161 @@ +/* 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/compiler/xla/service/dynamic_padder.h" + +#include +#include + +#include "absl/algorithm/container.h" +#include "absl/container/flat_hash_map.h" + +#include "absl/container/flat_hash_set.h" +#include "tensorflow/compiler/xla/literal.h" +#include "tensorflow/compiler/xla/literal_util.h" +#include "tensorflow/compiler/xla/service/dynamic_dimension_inference.h" +#include "tensorflow/compiler/xla/service/hlo_dce.h" +#include "tensorflow/compiler/xla/shape_util.h" +#include "tensorflow/compiler/xla/status_macros.h" +#include "tensorflow/compiler/xla/util.h" + +#include "tensorflow/core/lib/core/errors.h" + +namespace xla { + +namespace { + +// ChooseIdentityValue looks at the instruction and returns a identity value +// which, when padded, doesn't change the result of the instruction. +// +// nullopt is returned if padding doesn't need to be reset. +StatusOr ChooseIdentityValue(HloInstruction* inst) { + HloComputation* comp = inst->parent(); + // Padding on elementwise operation doesn't affect the result of the effective + // data. + if (inst->IsElementwise()) { + return nullptr; + } + + switch (inst->opcode()) { + case HloOpcode::kReduce: + case HloOpcode::kReduceWindow: { + // Because of the way we do reduce, we already require the `init` operand + // of hlo reduce instruction to be identity value. Here we reuse the + // operand. + return inst->mutable_operand(1); + } + + case HloOpcode::kConvolution: + case HloOpcode::kDot: { + // Use 0 as padding value for convolution and dot. + PrimitiveType ptype = inst->shape().element_type(); + return comp->AddInstruction( + HloInstruction::CreateConstant(LiteralUtil::Zero(ptype))); + } + + case HloOpcode::kPad: { + return inst->mutable_operand(1); + } + case HloOpcode::kParameter: + case HloOpcode::kGetDimensionSize: + case HloOpcode::kReshape: + case HloOpcode::kTuple: + case HloOpcode::kAllReduce: + case HloOpcode::kBroadcast: + return nullptr; + default: + return UnimplementedStrCat("Unimplimented padding for instruction: ", + inst->ToString()); + } +} + +} // namespace + +StatusOr DynamicPadder::Run(HloModule* module) { + bool changed = false; + VLOG(2) << "Pre DynamicPadder HLO:"; + XLA_VLOG_LINES(2, module->ToString()); + TF_ASSIGN_OR_RETURN(DynamicDimensionInference dynamic_dimension_inference, + DynamicDimensionInference::Run(module)); + + for (HloComputation* computation : module->computations()) { + for (HloInstruction* inst : computation->instructions()) { + for (int64 operand_num = 0; operand_num < inst->operand_count(); + ++operand_num) { + HloInstruction* operand = inst->mutable_operand(operand_num); + if (!operand->shape().IsArray()) { + continue; + } + for (int64 dim = 0; dim < operand->shape().rank(); ++dim) { + HloInstruction* dynamic_size = + dynamic_dimension_inference.GetDynamicSize(operand, {}, dim); + if (dynamic_size == nullptr) { + continue; + } + VLOG(1) << "Has dynamic dimension of operand" << operand_num << " @" + << dim; + TF_ASSIGN_OR_RETURN(HloInstruction * identity_value, + ChooseIdentityValue(inst)); + if (identity_value == nullptr) { + continue; + } + + // For each dimension, first generates a mask representing the + // effective area of data and padded area of data using iota and + // dynamic_size. For example, given a dimension of 7 elements and 5 + // effective elements: + // + // iota = [0, 1, 2, 3, 4, 5, 6] + // broadcast_dynamic_size = [5, 5, 5, 5, 5, 5, 5] + // mask = lt(iota, broadcast_dynamic_size) = [t, t, t, t, t, f, f] + // + // Once the mask is generated, the input data is then padded using the + // mask and pad value. + // + const Shape mask_shape = + ShapeUtil::ChangeElementType(operand->shape(), xla::U32); + const Shape pred_shape = + ShapeUtil::ChangeElementType(operand->shape(), xla::PRED); + HloInstruction* iota = computation->AddInstruction( + HloInstruction::CreateIota(mask_shape, dim)); + + HloInstruction* broadcasted_effective_size = + computation->AddInstruction(HloInstruction::CreateBroadcast( + mask_shape, dynamic_size, {})); + HloInstruction* pred = computation->AddInstruction( + HloInstruction::CreateBinary(pred_shape, HloOpcode::kLt, iota, + broadcasted_effective_size)); + + HloInstruction* broadcasted_identity_value = + computation->AddInstruction(HloInstruction::CreateBroadcast( + operand->shape(), identity_value, {})); + HloInstruction* padded = + computation->AddInstruction(HloInstruction::CreateTernary( + operand->shape(), HloOpcode::kSelect, pred, operand, + broadcasted_identity_value)); + TF_RETURN_IF_ERROR(inst->ReplaceOperandWith(operand_num, padded)); + operand = inst->mutable_operand(operand_num); + changed = true; + } + } + } + } + HloDCE dce; + TF_ASSIGN_OR_RETURN(changed, dce.Run(module)); + VLOG(2) << "Post DynamicPadder HLO:"; + XLA_VLOG_LINES(2, module->ToString()); + return changed; +} + +} // namespace xla diff --git a/tensorflow/compiler/xla/service/dynamic_padder.h b/tensorflow/compiler/xla/service/dynamic_padder.h new file mode 100644 index 0000000000..509269f7f5 --- /dev/null +++ b/tensorflow/compiler/xla/service/dynamic_padder.h @@ -0,0 +1,44 @@ +/* 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. +==============================================================================*/ + +#ifndef TENSORFLOW_COMPILER_XLA_SERVICE_DYNAMIC_PADDER_H_ +#define TENSORFLOW_COMPILER_XLA_SERVICE_DYNAMIC_PADDER_H_ + +#include "tensorflow/compiler/xla/service/dynamic_dimension_inference.h" +#include "tensorflow/compiler/xla/service/hlo_pass_interface.h" + +namespace xla { + +// With bounded shapes, only part of the shape contains effective data and the +// rest contains padded data, whose value can be anything depending on the +// source of the data. When a bounded shape is directly consumed by an +// instruction that collapses dimensions (reduce for example), the padding data +// would affect result of the instruction. +// +// DynamicPadder uses DynamicDimensionInference to detect bounded shapes in a +// hlo module, it then inserts certain instructions to reset the padding into an +// identity value so that in doesn't affect the result of subsequent +// instruction. For example, it'd reset the padding to 0 before a bounded shape +// is consumed by a reduce-sum. +class DynamicPadder : public HloModulePass { + public: + absl::string_view name() const override { return "dynamic_padder"; } + + StatusOr Run(HloModule* module) override; +}; + +} // namespace xla + +#endif // TENSORFLOW_COMPILER_XLA_SERVICE_DYNAMIC_PADDER_H_ diff --git a/tensorflow/compiler/xla/service/dynamic_padder_test.cc b/tensorflow/compiler/xla/service/dynamic_padder_test.cc new file mode 100644 index 0000000000..55a11286e4 --- /dev/null +++ b/tensorflow/compiler/xla/service/dynamic_padder_test.cc @@ -0,0 +1,152 @@ +/* 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/compiler/xla/service/dynamic_padder.h" + +#include "tensorflow/compiler/xla/client/xla_builder.h" +#include "tensorflow/compiler/xla/literal.h" +#include "tensorflow/compiler/xla/service/hlo_computation.h" +#include "tensorflow/compiler/xla/service/hlo_instruction.h" +#include "tensorflow/compiler/xla/service/hlo_matchers.h" +#include "tensorflow/compiler/xla/service/hlo_module.h" +#include "tensorflow/compiler/xla/service/hlo_opcode.h" +#include "tensorflow/compiler/xla/service/hlo_runner.h" +#include "tensorflow/compiler/xla/shape_util.h" +#include "tensorflow/compiler/xla/test.h" +#include "tensorflow/compiler/xla/test_helpers.h" +#include "tensorflow/compiler/xla/tests/hlo_test_base.h" +#include "tensorflow/compiler/xla/xla_data.pb.h" +#include "tensorflow/core/lib/core/status_test_util.h" +#include "tensorflow/core/platform/test_benchmark.h" + +namespace op = xla::testing::opcode_matchers; + +namespace xla { +namespace { + +class DynamicPadderTest : public HloTestBase { + protected: + DynamicPadderTest() : HloTestBase() { module_ = CreateNewVerifiedModule(); } + + StatusOr RunPadder() { + hlo_graph_dumper::MaybeDumpHloModule(*module_, "Before padder"); + + DynamicPadder padder; + + return padder.Run(module_.get()); + } + + void ExpectPadded(const HloInstruction* inst) { + EXPECT_THAT(inst, + op::Select(op::Lt(op::Iota(), op::Broadcast(op::Parameter())), + ::testing::_, op::Broadcast())); + } + + HloComputation* GetScalarAddComputation() { + auto embedded_builder = HloComputation::Builder("add"); + auto lhs = embedded_builder.AddInstruction(HloInstruction::CreateParameter( + 0, ShapeUtil::MakeShape(F32, {}), "lhs")); + auto rhs = embedded_builder.AddInstruction(HloInstruction::CreateParameter( + 1, ShapeUtil::MakeShape(F32, {}), "rhs")); + embedded_builder.AddInstruction( + HloInstruction::CreateBinary(lhs->shape(), HloOpcode::kAdd, lhs, rhs)); + return module_->AddEmbeddedComputation(embedded_builder.Build()); + } + + std::unique_ptr module_; + const Shape scalar_shape_ = ShapeUtil::MakeShape(U32, {}); +}; + +TEST_F(DynamicPadderTest, ReduceTest) { + auto builder = HloComputation::Builder(TestName()); + auto input_shape = ShapeUtil::MakeShape(F32, {1, 2, 2}); + auto reduce_shape = ShapeUtil::MakeShape(F32, {2}); + + auto data_param = builder.AddInstruction( + HloInstruction::CreateParameter(0, input_shape, "data_param")); + builder.AddInstruction( + HloInstruction::CreateParameter(1, scalar_shape_, "size_param")); + + auto negate = builder.AddInstruction( + HloInstruction::CreateUnary(input_shape, HloOpcode::kNegate, data_param)); + + auto init = builder.AddInstruction( + HloInstruction::CreateConstant(LiteralUtil::CreateR0(0.0))); + + auto reduce = builder.AddInstruction(HloInstruction::CreateReduce( + reduce_shape, negate, init, {0, 2}, GetScalarAddComputation())); + + module_->AddEntryComputation(builder.Build()); + + // Set up dynamic parameter binding. + TF_CHECK_OK(module_->dynamic_parameter_binding().Bind( + DynamicParameterBinding::DynamicParameter{1, {}}, + DynamicParameterBinding::DynamicDimension{0, {}, 1})); + + TF_ASSERT_OK(RunPadder().status()); + + ExpectPadded(reduce->operand(0)); +} + +TEST_F(DynamicPadderTest, ConvolutionTest) { + auto builder = HloComputation::Builder(TestName()); + constexpr int xdim = 3; + constexpr int ydim = 2; + constexpr int zdim = 1; + auto xy_shape = ShapeUtil::MakeShape(F32, {xdim, ydim}); + auto yz_shape = ShapeUtil::MakeShape(F32, {ydim, zdim}); + auto zx_shape = ShapeUtil::MakeShape(F32, {zdim, xdim}); + + auto* a_param = builder.AddInstruction(HloInstruction::CreateParameter( + /*parameter_number=*/0, xy_shape, "A")); + auto* b_param = builder.AddInstruction(HloInstruction::CreateParameter( + /*parameter_number=*/1, yz_shape, "B")); + builder.AddInstruction(HloInstruction::CreateParameter( + /*parameter_number=*/2, scalar_shape_, "size_param")); + + auto dnums = XlaBuilder::CreateDefaultConvDimensionNumbers(0); + + dnums.set_kernel_input_feature_dimension(0); + dnums.set_kernel_output_feature_dimension(1); + dnums.set_input_batch_dimension(0); + dnums.set_output_batch_dimension(1); + dnums.set_output_feature_dimension(0); + + Window window; + + auto* conv = builder.AddInstruction(HloInstruction::CreateConvolve( + zx_shape, a_param, b_param, /*feature_group_count=*/1, + /*batch_group_count=*/1, window, dnums, + HloTestBase::DefaultPrecisionConfig(2))); + + module_->AddEntryComputation(builder.Build()); + + // Set up dynamic parameter binding for non-contracting dimension. + TF_CHECK_OK(module_->dynamic_parameter_binding().Bind( + DynamicParameterBinding::DynamicParameter{2, {}}, + DynamicParameterBinding::DynamicDimension{0, {}, 0})); + + // Set up binding for contracting dimensions. + TF_CHECK_OK(module_->dynamic_parameter_binding().Bind( + DynamicParameterBinding::DynamicParameter{2, {}}, + DynamicParameterBinding::DynamicDimension{0, {}, 1})); + + TF_ASSERT_OK(RunPadder().status()); + + ExpectPadded(conv->operand(0)); +} + +} // namespace +} // namespace xla diff --git a/tensorflow/compiler/xla/service/hlo_get_dimension_size_rewriter.cc b/tensorflow/compiler/xla/service/hlo_get_dimension_size_rewriter.cc index c919dbd82d..862b202971 100644 --- a/tensorflow/compiler/xla/service/hlo_get_dimension_size_rewriter.cc +++ b/tensorflow/compiler/xla/service/hlo_get_dimension_size_rewriter.cc @@ -17,6 +17,7 @@ limitations under the License. #include "absl/algorithm/container.h" #include "tensorflow/compiler/xla/literal_util.h" +#include "tensorflow/compiler/xla/service/dynamic_dimension_inference.h" #include "tensorflow/compiler/xla/service/hlo_instruction.h" #include "tensorflow/compiler/xla/service/hlo_opcode.h" #include "tensorflow/compiler/xla/service/shape_inference.h" @@ -25,7 +26,9 @@ namespace xla { namespace { -StatusOr ReplaceGetSize(HloInstruction* instr) { +StatusOr ReplaceGetSize( + HloInstruction* instr, + const DynamicDimensionInference* dynamic_dimension_inference) { if (instr->opcode() != HloOpcode::kGetDimensionSize) { return false; } @@ -36,10 +39,18 @@ StatusOr ReplaceGetSize(HloInstruction* instr) { instr->operand(0)->shape(), instr->dimension())); TF_RET_CHECK(ShapeUtil::Equal(instr->shape(), legal_shape)); TF_RET_CHECK(ShapeUtil::HasPrimitiveType(instr->shape(), U32)); - uint32 size = instr->operand(0)->shape().dimensions(instr->dimension()); - HloInstruction* new_instr = computation->AddInstruction( - HloInstruction::CreateConstant(LiteralUtil::CreateR0(size))); - TF_RETURN_IF_ERROR(instr->ReplaceAllUsesWith(new_instr)); + HloInstruction* operand = instr->mutable_operand(0); + int64 dim = instr->dimension(); + HloInstruction* dynamic_size = + dynamic_dimension_inference->GetDynamicSize(operand, {}, dim); + if (dynamic_size != nullptr) { + TF_RETURN_IF_ERROR(instr->ReplaceAllUsesWith(dynamic_size)); + } else { + uint32 size = instr->operand(0)->shape().dimensions(dim); + HloInstruction* new_instr = computation->AddInstruction( + HloInstruction::CreateConstant(LiteralUtil::CreateR0(size))); + TF_RETURN_IF_ERROR(instr->ReplaceAllUsesWith(new_instr)); + } return true; } @@ -48,10 +59,13 @@ StatusOr ReplaceGetSize(HloInstruction* instr) { StatusOr HloGetDimensionSizeRewriter::Run(HloModule* module) { bool changed = false; HloProto proto; + TF_ASSIGN_OR_RETURN(DynamicDimensionInference inference, + DynamicDimensionInference::Run(module)); *proto.mutable_hlo_module() = module->ToProto(); for (auto* computation : module->computations()) { for (auto instruction : computation->instructions()) { - TF_ASSIGN_OR_RETURN(bool replaced, ReplaceGetSize(instruction)); + TF_ASSIGN_OR_RETURN(bool replaced, + ReplaceGetSize(instruction, &inference)); changed = changed || replaced; } } diff --git a/tensorflow/compiler/xla/service/hlo_get_dimension_size_rewriter.h b/tensorflow/compiler/xla/service/hlo_get_dimension_size_rewriter.h index 30f44c23a8..9aa79fe66b 100644 --- a/tensorflow/compiler/xla/service/hlo_get_dimension_size_rewriter.h +++ b/tensorflow/compiler/xla/service/hlo_get_dimension_size_rewriter.h @@ -21,7 +21,9 @@ limitations under the License. namespace xla { -// Pass to replace a kGetDimensionSize instruction with a constant instruction. +// Pass to replace a kGetDimensionSize instruction with a hlo instruction +// representing the dynamic size if the dimension is dynamic, otherwise a +// constant instruction representing the static size. class HloGetDimensionSizeRewriter : public HloModulePass { public: absl::string_view name() const override { -- GitLab From 5ed2c498eddc0b95ae43d6f4c39e29011cd97694 Mon Sep 17 00:00:00 2001 From: Justin Lebar Date: Mon, 7 Jan 2019 18:49:39 -0800 Subject: [PATCH 456/622] [XLA] Add testcase for ptxas bug. This ptxas bug caused the wrong output on this testcase. We never received a clear explanation of what was going on from nvidia, so I can't say more than "it used to not work, now it does work." PiperOrigin-RevId: 228271902 --- tensorflow/compiler/xla/tests/BUILD | 15 ++++ .../compiler/xla/tests/ptxas_bug_120501638.cc | 82 +++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 tensorflow/compiler/xla/tests/ptxas_bug_120501638.cc diff --git a/tensorflow/compiler/xla/tests/BUILD b/tensorflow/compiler/xla/tests/BUILD index 9433264917..106b3fd6c9 100644 --- a/tensorflow/compiler/xla/tests/BUILD +++ b/tensorflow/compiler/xla/tests/BUILD @@ -2118,3 +2118,18 @@ tf_cc_test( "@com_google_absl//absl/synchronization", ], ) + +xla_test( + name = "ptxas_bug_120501638", + srcs = ["ptxas_bug_120501638.cc"], + tags = [ + # Disabled in OSS until nvidia publicly releases a fixed ptxas. + "no_oss", + ], + deps = [ + ":hlo_test_base", + ":xla_internal_test_main", # fixdeps: keep + "//tensorflow/compiler/xla:debug_options_flags", + "//tensorflow/compiler/xla:test", + ], +) diff --git a/tensorflow/compiler/xla/tests/ptxas_bug_120501638.cc b/tensorflow/compiler/xla/tests/ptxas_bug_120501638.cc new file mode 100644 index 0000000000..0e5d7db97e --- /dev/null +++ b/tensorflow/compiler/xla/tests/ptxas_bug_120501638.cc @@ -0,0 +1,82 @@ +/* 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/compiler/xla/debug_options_flags.h" +#include "tensorflow/compiler/xla/test.h" +#include "tensorflow/compiler/xla/tests/hlo_test_base.h" +#include "tensorflow/compiler/xla/tests/test_macros.h" + +namespace xla { +namespace { + +class PtxasBugTest : public HloTestBase {}; + +// Checks for a bug in ptxas, tracked as Google bug 120501638, and nvidia bug +// 2459377. We never received an explanation of what exactly was going wrong +// here in ptxas. Known-bad in ptxas 10.0.145, known-good in ptxas 10.0.249. +TEST_F(PtxasBugTest, DoIt) { + const char* const kModuleStr = R"( +HloModule test + +add_F32.14 { + lhs.15 = f32[] parameter(0) + rhs.16 = f32[] parameter(1) + ROOT add.17 = f32[] add(lhs.15, rhs.16) +} + +ENTRY testcase { + arg0.1 = f32[2,5,2]{2,1,0} parameter(0) + reshape.2 = f32[2,5,2]{2,1,0} reshape(arg0.1) + constant.3 = f32[] constant(0) + pad.4 = f32[2,6,2]{2,1,0} pad(reshape.2, constant.3), padding=0_0x0_1x0_0 + reshape.5 = f32[2,3,2,2]{3,2,1,0} reshape(pad.4) + transpose.6 = f32[2,2,3,2]{3,0,2,1} transpose(reshape.5), dimensions={2,0,1,3} + reshape.7 = f32[4,3,2]{2,1,0} reshape(transpose.6) + reshape.8 = f32[4,1,3,2]{3,2,1,0} reshape(reshape.7) + transpose.9 = f32[4,2,1,3]{1,3,2,0} transpose(reshape.8), dimensions={0,3,1,2} + convert.10 = f32[4,2,1,3]{1,3,2,0} convert(transpose.9) + constant.12 = f32[] constant(0) + pad.13 = f32[4,2,1,3]{3,2,1,0} pad(convert.10, constant.12), padding=0_0x0_0x0_0x0_0 + constant.11 = f32[] constant(0) + reduce-window.18 = f32[4,2,1,3]{3,2,1,0} reduce-window(pad.13, constant.11), + window={size=1x1x1x1}, to_apply=add_F32.14 + constant.19 = f32[] constant(1) + broadcast.20 = f32[4,2,1,3]{3,2,1,0} broadcast(constant.19), dimensions={} + divide.21 = f32[4,2,1,3]{3,2,1,0} divide(reduce-window.18, broadcast.20) + convert.22 = f32[4,2,1,3]{3,2,1,0} convert(divide.21) + transpose.23 = f32[4,1,3,2]{2,1,3,0} transpose(convert.22), dimensions={0,2,3,1} + reshape.24 = f32[4,3,2]{2,1,0} reshape(transpose.23) + reshape.25 = f32[2,2,3,2]{3,2,1,0} reshape(reshape.24) + transpose.26 = f32[2,3,2,2]{3,1,0,2} transpose(reshape.25), dimensions={1,2,0,3} + reshape.27 = f32[2,6,2]{2,1,0} reshape(transpose.26) + slice.28 = f32[2,5,2]{2,1,0} slice(reshape.27), slice={[0:2], [0:5], [0:2]} + reshape.29 = f32[2,5,2]{2,1,0} reshape(slice.28) + tuple.30 = (f32[2,5,2]{2,1,0}) tuple(reshape.29) + ROOT get-tuple-element.31 = f32[2,5,2]{2,1,0} get-tuple-element(tuple.30), index=0 +})"; + + // Create a module with the true-default flags, not the default-for-testing + // flags. In particular, true-default flags enable unrolling, whereas for + // testing we disable unrolling, and this bug doesn't trigger without + // unrolling. + HloModuleConfig config; + config.set_debug_options(DefaultDebugOptionsIgnoringFlags()); + TF_ASSERT_OK_AND_ASSIGN(auto module, + ParseAndReturnVerifiedModule(kModuleStr, config)); + EXPECT_TRUE(RunAndCompare(std::move(module), ErrorSpec{0.01, 0.01})); +} + +} // anonymous namespace +} // namespace xla -- GitLab From 9b884d8392706a82af674adbbff862ca2de593a6 Mon Sep 17 00:00:00 2001 From: Justin Lebar Date: Mon, 7 Jan 2019 19:02:54 -0800 Subject: [PATCH 457/622] [XLA] Don't constant-fold instructions which transitively contain kRng or kAfterAll. Constant-folding avoids folding kRng/kAfterAll instructions, but it also needs to avoid folding instructions which *may contain* kRng/kAfterAll, because e.g. folding a call that contains an rng is morally equivalent to folding an rng. PiperOrigin-RevId: 228272774 --- .../xla/service/hlo_constant_folding.cc | 66 +++++++++++++++---- .../xla/service/hlo_constant_folding_test.cc | 46 +++++++++++++ 2 files changed, 98 insertions(+), 14 deletions(-) diff --git a/tensorflow/compiler/xla/service/hlo_constant_folding.cc b/tensorflow/compiler/xla/service/hlo_constant_folding.cc index 799cc9fd91..e7ed858e8c 100644 --- a/tensorflow/compiler/xla/service/hlo_constant_folding.cc +++ b/tensorflow/compiler/xla/service/hlo_constant_folding.cc @@ -35,6 +35,34 @@ limitations under the License. namespace xla { +// Checks whether instr is or transitively contains an instruction that we +// shouldn't fold. +// +// Specifically, we don't fold kRng or kAfterAll instructions: +// +// - kRng is already marked as side-effecting and so is skipped elsewhere, but +// we check for it here. Even kRng weren't side-effecting and took an +// explicit seed, we *still* wouldn't want to constant-fold it, because the +// evaluator's handling of rng is not guaranteed to be identical to any +// particular backend's rng. +// +// - kAfterAll needs to be skipped because a kAfterAll op with no args can +// currently materialize a token "out of thin air". TODO(b/110532604): +// Remove this check once AfterAll requires at least one operand, in which +// case constant folding will be impossible. +static bool IsOrContainsIllegalInstr(const HloInstruction* instr) { + if (instr->opcode() == HloOpcode::kAfterAll || + instr->opcode() == HloOpcode::kRng) { + return true; + } + for (const HloComputation* c : instr->called_computations()) { + if (absl::c_any_of(c->instructions(), IsOrContainsIllegalInstr)) { + return true; + } + } + return false; +} + StatusOr HloConstantFolding::Run(HloModule* module) { // Limit the constant folding to 0 iterations to skip folding loops. This // retains the behavior from before while loop support in HloEvaluator and may @@ -52,26 +80,24 @@ StatusOr HloConstantFolding::Run(HloModule* module) { computation->root_instruction() != instruction) { continue; } - // Skip Constant, Parameter, Tuple, AfterAll, Rng operations. - // Tuple constants are not directly supported by any backends, hence - // folding Tuple is not useful and would in fact be expanded back into - // kTuple by Algebraic Simplifier. - // TODO(b/110532604): Enable AfterAll once AfterAll requires at least one - // operand in which case constant folding will be impossible and this - // special case is not necessary. - if (instruction->opcode() == HloOpcode::kParameter || - instruction->opcode() == HloOpcode::kConstant || - instruction->opcode() == HloOpcode::kTuple || - instruction->opcode() == HloOpcode::kAfterAll || - instruction->opcode() == HloOpcode::kRng) { - continue; - } // Skip instructions with non-constant operands. if (!hlo_query::AllOperandsAreConstants(*instruction)) { continue; } + // Don't fold Constant, Parameter, and Tuple instructions. Tuple + // constants are not directly supported by any backends, hence folding + // Tuple is not useful and would in fact be expanded back into kTuple by + // Algebraic Simplifier. + // + // (We do allow folding subcomputations that contain these instructions.) + if (instruction->opcode() == HloOpcode::kParameter || + instruction->opcode() == HloOpcode::kConstant || + instruction->opcode() == HloOpcode::kTuple) { + continue; + } + // Broadcasts dramatically increase the size of constants, which is often // detrimental to performance and memory capacity, so do not fold // broadcasts. @@ -80,6 +106,18 @@ StatusOr HloConstantFolding::Run(HloModule* module) { continue; } + // Check for instructions that we can't fold even if they appear inside of + // a subcomputation (e.g. a kCall). + if (IsOrContainsIllegalInstr(instruction)) { + continue; + } + + // Don't constant-fold side-effecting instructions or instructions which + // contain side-effecting instructions. + if (instruction->HasSideEffect()) { + continue; + } + // Don't constant fold unless it's a net positive or the output is small. if (instruction->shape().IsArray()) { int64 elements_in_removed_operands = 0; diff --git a/tensorflow/compiler/xla/service/hlo_constant_folding_test.cc b/tensorflow/compiler/xla/service/hlo_constant_folding_test.cc index 92b748d813..4bdc980c9a 100644 --- a/tensorflow/compiler/xla/service/hlo_constant_folding_test.cc +++ b/tensorflow/compiler/xla/service/hlo_constant_folding_test.cc @@ -268,5 +268,51 @@ TEST_F(HloConstantFoldingTest, DoesNotFoldLargePad) { GmockMatch(m::Pad(m::Constant(), m::Constant()))); } +TEST_F(HloConstantFoldingTest, DontFoldSubcomputationContainingAfterAll) { + const char* const kModuleStr = R"( + HloModule test + + Fn { + tok = token[] after-all() + ROOT root = f32[10] iota(), iota_dimension=0 + } + + ENTRY entry { + ROOT call = f32[10] call(), to_apply=Fn + })"; + TF_ASSERT_OK_AND_ASSIGN(auto module, + ParseAndReturnVerifiedModule(kModuleStr)); + HloConstantFolding constant_folding; + TF_ASSERT_OK_AND_ASSIGN(bool result, + RunHloPass(&constant_folding, module.get())); + EXPECT_FALSE(result); +} + +TEST_F(HloConstantFoldingTest, + DontFoldSubcomputationTransitivelyContainingRng) { + const char* const kModuleStr = R"( + HloModule test + + InnerFn { + c0 = f32[] constant(0) + c1 = f32[] constant(1) + ROOT rng = f32[10] rng(c0, c1), distribution=rng_uniform + } + + Fn { + ROOT fusion = f32[10] fusion(), kind=kLoop, calls=InnerFn + } + + ENTRY entry { + ROOT call = f32[10] call(), to_apply=Fn + })"; + TF_ASSERT_OK_AND_ASSIGN(auto module, + ParseAndReturnVerifiedModule(kModuleStr)); + HloConstantFolding constant_folding; + TF_ASSERT_OK_AND_ASSIGN(bool result, + RunHloPass(&constant_folding, module.get())); + EXPECT_FALSE(result); +} + } // namespace } // namespace xla -- GitLab From 5265604a31dcad7e37a326b1f7d5f1ff006968fb Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Mon, 7 Jan 2019 19:17:05 -0800 Subject: [PATCH 458/622] [convergence_tools]: Moving the norm, nan, and max computations inside of TPU. PiperOrigin-RevId: 228273700 --- .../contrib/tpu/python/tpu/tensor_tracer.py | 124 ++++++++++++------ 1 file changed, 83 insertions(+), 41 deletions(-) diff --git a/tensorflow/contrib/tpu/python/tpu/tensor_tracer.py b/tensorflow/contrib/tpu/python/tpu/tensor_tracer.py index 66689cde2f..c7e29860e5 100644 --- a/tensorflow/contrib/tpu/python/tpu/tensor_tracer.py +++ b/tensorflow/contrib/tpu/python/tpu/tensor_tracer.py @@ -377,10 +377,7 @@ class TensorTracer(object): return True # Reasons for not including following op types: # Assign: cause incorrect result with CPU tracing. - # others: compilation problems. - # TODO(deveci): Check if 'Pack', 'Shape', 'Reshape', 'ArgMin', 'ArgMax' - # are still unsafe now that we have handled int64 tensors. - if op.type in ['Assign', 'Pack', 'Shape', 'Reshape', 'ArgMin', 'ArgMax']: + if op.type in ['Assign']: return True return False @@ -471,7 +468,7 @@ class TensorTracer(object): temporarily_marked_ops, sorted_ops) # pylint: disable=protected-access for ctrl_output_op in op._control_outputs: - # pylint: enable=protected-access + # pylint: enable=protected-access visit(ctrl_output_op, cycle, permanently_marked_ops, temporarily_marked_ops, sorted_ops) temporarily_marked_ops.remove(op) @@ -737,6 +734,59 @@ class TensorTracer(object): self._write_report('%d "%s"\n'%(i, l[i].name)) self._write_report('%s %s\n'%(_MARKER_SECTION_END, _SECTION_NAME_GRAPH)) + def _preprocess_traced_tensor(self, tensor): + """Computes NAN/Norm/Max on TPUs before sending to CPU. + + Args: + tensor: The tensor to be traced. + Returns: + A tensor that should be input to the trace_function. + Raises: + RuntimeError: If the trace mode is invalid. + """ + + def _detect_nan_inf(tensor): + """Trace function for detecting any NaN/Inf in the tensor.""" + + if tensor.dtype.is_floating: + output_tensor = math_ops.reduce_any( + gen_math_ops.logical_or( + gen_math_ops.is_nan(tensor), gen_math_ops.is_inf(tensor))) + else: + output_tensor = constant_op.constant(False) + # The shape has to be 1. Set it if it does not have the information. + output_tensor = array_ops.reshape(output_tensor, [1]) + return output_tensor + + def _show_norm(tensor): + tensor = math_ops.cast(tensor, dtypes.float32) + output_tensor = linalg_ops.norm(tensor) + # The shape has to be 1. Set it if it does not have the information. + output_tensor = array_ops.reshape(output_tensor, [1]) + return output_tensor + + def _show_max_abs(tensor): + tensor = math_ops.cast(tensor, dtypes.float32) + output_tensor = math_ops.reduce_max(math_ops.abs(tensor)) + zero = constant_op.constant(0, dtypes.float32) + output_tensor = gen_math_ops.maximum(zero, output_tensor) + # The shape has to be 1. Set it if it does not have the information. + output_tensor = array_ops.reshape(output_tensor, [1]) + return output_tensor + + if self._trace_mode == _TRACE_MODE_NAN_INF: + return _detect_nan_inf(tensor) + if self._trace_mode == _TRACE_MODE_PART_TENSOR: + return tensor + if self._trace_mode == _TRACE_MODE_FULL_TENSOR: + return tensor + if self._trace_mode == _TRACE_MODE_NORM: + return _show_norm(tensor) + if self._trace_mode == _TRACE_MODE_MAX_ABS: + return _show_max_abs(tensor) + raise RuntimeError( + 'Tensor trace fun for %s is not yet implemented' % self._trace_mode) + def _make_tensor_trace_fun(self, tensor_name): """Makes the tensor tracing function called by outside compilation. @@ -787,29 +837,6 @@ class TensorTracer(object): with ops.control_dependencies([print_op]): return array_ops.identity(tensor).op - def _detect_nan_inf(tensor): - """Trace function for detecting any NaN/Inf in the tensor.""" - - if tensor.dtype.is_floating: - output_tensor = math_ops.reduce_any( - gen_math_ops.logical_or(gen_math_ops.is_nan(tensor), - gen_math_ops.is_inf(tensor))) - else: - output_tensor = constant_op.constant(False) - - return _print_tensor(tensor_name, -1, tensor, output_tensor) - - def _show_norm(tensor): - tensor = math_ops.cast(tensor, dtypes.float64) - output_tensor = linalg_ops.norm(tensor) - return _print_tensor(tensor_name, -1, tensor, output_tensor) - - def _show_max_abs(tensor): - output_tensor = math_ops.cast(math_ops.reduce_max(math_ops.abs(tensor)), - dtypes.float64) - zero = constant_op.constant(0, dtypes.float64) - output_tensor = gen_math_ops.maximum(zero, output_tensor) - return _print_tensor(tensor_name, -1, tensor, output_tensor) def _show_part_tensor(tensor): """Trace function for printing part of the tensor.""" @@ -822,16 +849,17 @@ class TensorTracer(object): return _print_tensor(tensor_name, -1, tensor, tensor) - if self._trace_mode == _TRACE_MODE_NAN_INF: - return _detect_nan_inf if self._trace_mode == _TRACE_MODE_PART_TENSOR: return _show_part_tensor - if self._trace_mode == _TRACE_MODE_FULL_TENSOR: + # The input tensor has a shape of "[1]" for _TRACE_MODE_NAN_INF, + # _TRACE_MODE_NORM, and _TRACE_MODE_MAX_ABS, as related computations are + # performed within TPUs and only their results are transferred to CPU. + # Simply, print the full tensor for these trace modes. + if self._trace_mode in [ + _TRACE_MODE_NAN_INF, _TRACE_MODE_NORM, _TRACE_MODE_FULL_TENSOR, + _TRACE_MODE_MAX_ABS + ]: return _show_full_tensor - if self._trace_mode == _TRACE_MODE_NORM: - return _show_norm - if self._trace_mode == _TRACE_MODE_MAX_ABS: - return _show_max_abs raise RuntimeError('Tensor trace fun for %s is not yet implemented' %self._trace_mode) @@ -889,9 +917,18 @@ class TensorTracer(object): op_id, _REASON_USER_EXCLUDED) return True if not out_tensor.get_shape().is_fully_defined(): - self._instrument_records[out_tensor.name] = TensorTracer.reason( - op_id, _REASON_DYNAMIC_SHAPE) - return True + # If trace mode is nan-inf, norm or max, then the tensor will be reduced + # to a scalar before the outside compilation call. + if self._trace_mode in [ + _TRACE_MODE_NAN_INF, _TRACE_MODE_NORM, _TRACE_MODE_MAX_ABS + ]: + self._instrument_records[out_tensor.name] = TensorTracer.reason( + op_id, _REASON_TENSOR_GET_TRACED) + return False + else: + self._instrument_records[out_tensor.name] = TensorTracer.reason( + op_id, _REASON_DYNAMIC_SHAPE) + return True rank = len(out_tensor.shape) if rank < 1: # scalar @@ -1000,11 +1037,15 @@ class TensorTracer(object): if self._skip_tensor(op_id, out_tensor, user_included, user_excluded): continue + # Create the list of consumers before calling _preprocess_traced_tensor. + # Otherwise, adding control input below, will introduce a cycle in the + # graph. consumers = out_tensor.consumers() tensor_name = out_tensor.name - out_tensor = _cast_unsupported_dtypes(out_tensor) + processed_out_tensor = self._preprocess_traced_tensor(out_tensor) + processed_out_tensor = _cast_unsupported_dtypes(processed_out_tensor) trace_op = tpu.outside_compilation( - self._make_tensor_trace_fun(tensor_name), out_tensor) + self._make_tensor_trace_fun(tensor_name), processed_out_tensor) if consumers: for consumer_op in consumers: # pylint: disable=protected-access @@ -1050,8 +1091,9 @@ class TensorTracer(object): if self._skip_tensor(op_id, out_tensor, user_included, user_excluded): continue + processed_out_tensor = self._preprocess_traced_tensor(out_tensor) trace_fun = self._make_tensor_trace_fun(out_tensor.name) - trace_call = (trace_fun, [out_tensor]) + trace_call = (trace_fun, [processed_out_tensor]) trace_call_key = 'tensor_tracing_cpu-%s:%d'%(op.name, i) tracing_calls[trace_call_key] = trace_call self._post_tracing(succeed, sorted_or_cycle) -- GitLab From d95400ecd815c493e4241a96bea1c483387d0059 Mon Sep 17 00:00:00 2001 From: Pooya Davoodi Date: Mon, 7 Jan 2019 19:42:00 -0800 Subject: [PATCH 459/622] TFTRT: use six.text_type and six.binary_type --- .../contrib/tensorrt/python/trt_convert.py | 24 +++++-------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/tensorflow/contrib/tensorrt/python/trt_convert.py b/tensorflow/contrib/tensorrt/python/trt_convert.py index f5d624ef64..ac39a570d8 100644 --- a/tensorflow/contrib/tensorrt/python/trt_convert.py +++ b/tensorflow/contrib/tensorrt/python/trt_convert.py @@ -48,30 +48,18 @@ from tensorflow.python.training import saver def _to_bytes(s): """Returns encoded of s if s is a sequence of chars otherwise returns s. """ - if _six.PY2: - if isinstance(s, unicode): - return s.encode("utf-8", errors="surrogateescape") - else: - return s + if isinstance(s, _six.text_type): + return s.encode("utf-8", errors="surrogateescape") else: - if isinstance(s, str): - return s.encode("utf-8", errors="surrogateescape") - else: - return s + return s def _to_string(s): """Returns decoded of s if s is a sequence of bytes otherwise returns s. """ - if _six.PY2: - if isinstance(s, str): - return s.decode("utf-8") - else: - return s + if isinstance(s, _six.binary_type): + return s.decode("utf-8") else: - if isinstance(s, bytes): - return s.decode("utf-8") - else: - return s + return s class TrtPrecisionMode(object): FP32 = "FP32" -- GitLab From 4cff2f464cf195fedd8165a11319e8379746456f Mon Sep 17 00:00:00 2001 From: Sanjoy Das Date: Mon, 7 Jan 2019 19:59:50 -0800 Subject: [PATCH 460/622] Organize the dot op emitter a bit better; NFC This CL pays down some technical debt around dot_op_emitter's organization. It - Extracts out a GetDotImplementationStrategy that decides the lowering strategy for a dot operation. Earlier this logic was scattered through the lowering code. - Removes calls to PotentiallyImplementedAsEigenDot. This is an implementation detail for dot_op_emitter and exposing it like this has aged poorly in the face of the various tiled LLVM IR lowerings we now have. Instead, we expose two more specific hooks: DotOperandsAndResultMustHaveRowMajorLayout and DotImplementationCanHandleTranspose. - Renames xla_enable_experimental_llvm_ir_gemm to xla_force_enable_experimental_llvm_ir_gemm since its behavior is that it enables the tiled LLVM IR GEMM even when it does not look profitable. Note: In parallel_task_assignment the call to PotentiallyImplementedAsEigenDot was redundant since we already check for non-loop fusions and dots. PiperOrigin-RevId: 228276282 --- tensorflow/compiler/xla/service/cpu/BUILD | 5 +- .../compiler/xla/service/cpu/cpu_compiler.cc | 3 +- .../cpu/cpu_eigen_tensor_alignment_test.cc | 16 +- .../xla/service/cpu/cpu_layout_assignment.cc | 64 ++-- .../compiler/xla/service/cpu/cpu_options.cc | 8 +- .../compiler/xla/service/cpu/cpu_options.h | 2 +- .../xla/service/cpu/dot_op_emitter.cc | 301 ++++++++++-------- .../compiler/xla/service/cpu/dot_op_emitter.h | 40 ++- .../xla/service/cpu/dot_op_emitter_internal.h | 88 +++++ .../service/cpu/parallel_task_assignment.cc | 2 - 10 files changed, 318 insertions(+), 211 deletions(-) create mode 100644 tensorflow/compiler/xla/service/cpu/dot_op_emitter_internal.h diff --git a/tensorflow/compiler/xla/service/cpu/BUILD b/tensorflow/compiler/xla/service/cpu/BUILD index 86c5f71096..1eb42cf5ad 100644 --- a/tensorflow/compiler/xla/service/cpu/BUILD +++ b/tensorflow/compiler/xla/service/cpu/BUILD @@ -382,7 +382,10 @@ cc_library( cc_library( name = "dot_op_emitter", srcs = ["dot_op_emitter.cc"], - hdrs = ["dot_op_emitter.h"], + hdrs = [ + "dot_op_emitter.h", + "dot_op_emitter_internal.h", + ], deps = [ ":cpu_options", ":cpu_runtime", diff --git a/tensorflow/compiler/xla/service/cpu/cpu_compiler.cc b/tensorflow/compiler/xla/service/cpu/cpu_compiler.cc index 91c64e45b9..a6d92ce10e 100644 --- a/tensorflow/compiler/xla/service/cpu/cpu_compiler.cc +++ b/tensorflow/compiler/xla/service/cpu/cpu_compiler.cc @@ -304,7 +304,8 @@ Status CpuCompiler::RunHloPassesThroughLayoutAssn( pipeline.AddPass( [&](const HloInstruction& dot, const TransposeFolding::OperandIndices& candidate_operands) { - return PotentiallyImplementedAsEigenDot(dot, *target_machine_features) + return DotImplementationCanHandleTranspose(dot, + *target_machine_features) ? candidate_operands : TransposeFolding::OperandIndices{}; }, diff --git a/tensorflow/compiler/xla/service/cpu/cpu_eigen_tensor_alignment_test.cc b/tensorflow/compiler/xla/service/cpu/cpu_eigen_tensor_alignment_test.cc index 8727c72b6e..823bdf259c 100644 --- a/tensorflow/compiler/xla/service/cpu/cpu_eigen_tensor_alignment_test.cc +++ b/tensorflow/compiler/xla/service/cpu/cpu_eigen_tensor_alignment_test.cc @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "tensorflow/compiler/xla/service/cpu/dot_op_emitter.h" +#include "tensorflow/compiler/xla/service/cpu/dot_op_emitter_internal.h" #include "tensorflow/compiler/xla/service/cpu/ir_emission_utils.h" #include "tensorflow/compiler/xla/service/cpu/target_machine_features_fake.h" #include "tensorflow/compiler/xla/service/hlo_parser.h" @@ -23,6 +23,10 @@ namespace xla { namespace cpu { namespace { +using internal::DotImplementationStrategy; +using internal::DotInfo; +using internal::GetDotImplementationStrategy; + // Test that we don't call into Eigen with tensors too small to be aligned // reliably. @@ -47,16 +51,18 @@ ENTRY DotOperation { TargetMachineFeaturesWithFakeAlignmentLogic target_machine_with_no_alignment( [](int64 size) { return 1; }); - EXPECT_FALSE( - PotentiallyImplementedAsEigenDot(*dot, target_machine_with_no_alignment)); + EXPECT_EQ(GetDotImplementationStrategy(HloModuleConfig{}, DotInfo(*dot), + target_machine_with_no_alignment), + DotImplementationStrategy::kNaiveLlvmIr); TargetMachineFeaturesWithFakeAlignmentLogic target_machine_with_full_alignment([](int64 size) { return TargetMachineFeatures::kEigenExpectedTensorAlignment; }); - EXPECT_TRUE(PotentiallyImplementedAsEigenDot( - *dot, target_machine_with_full_alignment)); + EXPECT_NE(GetDotImplementationStrategy(HloModuleConfig{}, DotInfo(*dot), + target_machine_with_full_alignment), + DotImplementationStrategy::kNaiveLlvmIr); } TEST_F(CpuEigenTensorAlignmentTest, EigenConvAlignment) { diff --git a/tensorflow/compiler/xla/service/cpu/cpu_layout_assignment.cc b/tensorflow/compiler/xla/service/cpu/cpu_layout_assignment.cc index ff11409482..95b8025f87 100644 --- a/tensorflow/compiler/xla/service/cpu/cpu_layout_assignment.cc +++ b/tensorflow/compiler/xla/service/cpu/cpu_layout_assignment.cc @@ -93,60 +93,38 @@ static Shape ColMajorShape(const Shape& old_shape) { return new_shape; } +static bool OperandsAndResultMustHaveRowMajorLayout( + const HloInstruction& instr, + const TargetMachineFeatures& target_machine_features) { + if (instr.opcode() == HloOpcode::kConvolution) { + return PotentiallyImplementedAsEigenConvolution(instr, + target_machine_features); + } else if (instr.opcode() == HloOpcode::kDot) { + return DotOperandsAndResultMustHaveRowMajorLayout(instr, + target_machine_features); + } + return false; +} + Status CpuLayoutAssignment::AddBackendConstraints( LayoutConstraints* constraints) { ShouldMakeOperandColMajorCache cache; const HloComputation* computation = constraints->computation(); for (auto* instruction : computation->instructions()) { - if (instruction->opcode() == HloOpcode::kConvolution && - PotentiallyImplementedAsEigenConvolution(*instruction, - target_machine_features_)) { - const HloInstruction* convolution = instruction; - const HloInstruction* lhs_instruction = convolution->operand(0); - const HloInstruction* rhs_instruction = convolution->operand(1); - - // In order to implement `convolution` with Eigen convolution, the layouts - // of the input, filter, and output need to be row-major. - // - // These constraints are not hard constraints. Ideally, we should decide - // which layouts to choose according to some cost model. - Shape output_shape(RowMajorShape(convolution->shape())); - Shape input_shape(RowMajorShape(lhs_instruction->shape())); - Shape filter_shape(RowMajorShape(rhs_instruction->shape())); - - // Set layouts of the instructions' shapes. - TF_RETURN_IF_ERROR( - constraints->SetOperandLayout(input_shape, convolution, 0)); - TF_RETURN_IF_ERROR( - constraints->SetOperandLayout(filter_shape, convolution, 1)); - TF_RETURN_IF_ERROR( - constraints->SetInstructionLayout(output_shape, convolution)); + if (OperandsAndResultMustHaveRowMajorLayout(*instruction, + target_machine_features_)) { + TF_RETURN_IF_ERROR(constraints->SetInstructionLayout( + RowMajorShape(instruction->shape()), instruction)); + for (int i = 0; i < instruction->operand_count(); i++) { + TF_RETURN_IF_ERROR(constraints->SetOperandLayout( + RowMajorShape(instruction->operand(i)->shape()), instruction, i)); + } } else if (optional op_idx = ShouldMakeOperandColumnMajor(&cache, *instruction)) { const HloInstruction* op = instruction->operand(*op_idx); TF_RETURN_IF_ERROR(constraints->SetOperandLayout( ColMajorShape(op->shape()), instruction, *op_idx)); - } else if (PotentiallyImplementedAsEigenDot(*instruction, - target_machine_features_)) { - const HloInstruction* dot = instruction; - // In order to implement `dot` with Eigen dot, the layouts of the lhs, - // rhs, and output need to be row-major. - // - // These constraints are not hard constraints. Ideally, we should decide - // which layouts to choose according to some cost model. - Shape output_shape(RowMajorShape(dot->shape())); - - const HloInstruction* lhs_instruction = dot->operand(0); - Shape lhs_shape(RowMajorShape(lhs_instruction->shape())); - TF_RETURN_IF_ERROR(constraints->SetOperandLayout(lhs_shape, dot, 0)); - - const HloInstruction* rhs_instruction = dot->operand(1); - Shape rhs_shape(RowMajorShape(rhs_instruction->shape())); - TF_RETURN_IF_ERROR(constraints->SetOperandLayout(rhs_shape, dot, 1)); - - // Set layouts of the instructions' shapes. - TF_RETURN_IF_ERROR(constraints->SetInstructionLayout(output_shape, dot)); } else { for (int64 operand_no = 0; operand_no < instruction->operand_count(); ++operand_no) { diff --git a/tensorflow/compiler/xla/service/cpu/cpu_options.cc b/tensorflow/compiler/xla/service/cpu/cpu_options.cc index 92debb83e3..ff654c83d6 100644 --- a/tensorflow/compiler/xla/service/cpu/cpu_options.cc +++ b/tensorflow/compiler/xla/service/cpu/cpu_options.cc @@ -23,8 +23,8 @@ namespace { const char* const kXlaOptimizeForSizeCpuOption = "xla_cpu_optimize_for_size"; const char* const kLlvmIrDotTilingFactor = "xla_llvm_dot_tiling_factor"; -const char* const kXlaEnableExperimentalLlvmIrGemm = - "xla_enable_experimental_llvm_ir_gemm"; +const char* const kXlaForceEnableExperimentalLlvmIrGemm = + "xla_force_enable_experimental_llvm_ir_gemm"; const char* const kLlvmIrGemmTileSize = "xla_llvm_ir_gemm_tile_size"; } // namespace @@ -57,10 +57,10 @@ absl::optional LlvmIrGemvTilingFactor(const HloModuleConfig& config) { return absl::nullopt; } -bool EnableExperimentalLlvmIrGemm(const HloModuleConfig& config) { +bool ForceEnableExperimentalLlvmIrGemm(const HloModuleConfig& config) { const auto& extra_options_map = config.debug_options().xla_backend_extra_options(); - return extra_options_map.count(kXlaEnableExperimentalLlvmIrGemm) > 0; + return extra_options_map.count(kXlaForceEnableExperimentalLlvmIrGemm) > 0; } static absl::string_view RemoveSuffix(absl::string_view str, diff --git a/tensorflow/compiler/xla/service/cpu/cpu_options.h b/tensorflow/compiler/xla/service/cpu/cpu_options.h index 47c7eb13b6..99e6702d14 100644 --- a/tensorflow/compiler/xla/service/cpu/cpu_options.h +++ b/tensorflow/compiler/xla/service/cpu/cpu_options.h @@ -26,7 +26,7 @@ namespace options { bool OptimizeForSizeRequested(const HloModuleConfig& config); bool VectorizedReduceDisabled(const HloModuleConfig& config); -bool EnableExperimentalLlvmIrGemm(const HloModuleConfig& config); +bool ForceEnableExperimentalLlvmIrGemm(const HloModuleConfig& config); absl::optional LlvmIrGemvTilingFactor(const HloModuleConfig& config); absl::optional> LlvmIrGemmTileSize( const HloModuleConfig& config); diff --git a/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc b/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc index bdd688c2b9..70d8fee265 100644 --- a/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc +++ b/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc @@ -14,6 +14,7 @@ limitations under the License. ==============================================================================*/ #include "tensorflow/compiler/xla/service/cpu/dot_op_emitter.h" +#include "tensorflow/compiler/xla/service/cpu/dot_op_emitter_internal.h" #include #include @@ -42,6 +43,18 @@ namespace xla { using llvm_ir::SetToFirstInsertPoint; namespace cpu { + +using internal::DotImplementationStrategy; +using internal::DotInfo; +using internal::GetDotImplementationStrategy; + +namespace { +// Returns true if we should call into multi-threaded Eigen routines. +bool ShouldUseMultiThreadedEigen(const HloModuleConfig& config) { + return config.debug_options().xla_cpu_multi_thread_eigen(); +} +} // namespace + DotOpEmitter::DotOpEmitter(const HloInstruction& dot, const llvm_ir::IrArray& target_array, const llvm_ir::IrArray& lhs_array, @@ -76,43 +89,9 @@ DotOpEmitter::DotOpEmitter(const HloInstruction& dot, return dot_emitter.Emit(); } -bool DotOpEmitter::EmitSmallGemmIfProfitable( - const DotOpEmitter::MatMultDims& mat_mult_dims) { - if (ShouldUseMultiThreadedEigen()) { - return false; - } - - if (!EnableExperimentalLlvmIrGemm()) { - // TODO(sanjoy): We should make these numbers micro-arch specific. - bool small_gemm = mat_mult_dims.k <= 128 && - ((mat_mult_dims.m <= 32 && mat_mult_dims.n <= 128) || - (mat_mult_dims.m <= 128 && mat_mult_dims.n <= 32)); - if (!small_gemm) { - return false; - } - } - - if (mat_mult_dims.lhs_non_canonical || mat_mult_dims.rhs_non_canonical) { - return false; - } - +void DotOpEmitter::EmitTiledLlvmIrGemm() { PrimitiveType primitive_type = dot_.shape().element_type(); - - switch (primitive_type) { - default: - return false; - - case F32: - case F64: - case S32: - case S64: - break; - } - - if (!(mat_mult_dims.lhs_column_major == mat_mult_dims.rhs_column_major && - mat_mult_dims.rhs_column_major == mat_mult_dims.target_column_major)) { - return false; - } + MatMultDims mat_mult_dims = GetMatMultDims(); llvm::Value* lhs = lhs_array_.GetBasePointer(); llvm::Value* rhs = rhs_array_.GetBasePointer(); @@ -154,21 +133,13 @@ bool DotOpEmitter::EmitSmallGemmIfProfitable( /*rhs=*/rhs, /*result=*/target, b_, /*enable_fast_math=*/enable_fast_math, /*optimize_for_size=*/optimize_for_size); - - return true; } -bool DotOpEmitter::EmitLlvmIrDotIfProfitable() { - if (dot_.shape().dimensions_size() != 2) { - return false; - } - +void DotOpEmitter::EmitTiledLlvmIrGemv() { PrimitiveType primitive_type = dot_.shape().element_type(); - if (!primitive_util::IsFloatingPointType(primitive_type) && - !primitive_util::IsIntegralType(primitive_type)) { - return false; - } + CHECK(primitive_util::IsFloatingPointType(primitive_type) || + primitive_util::IsIntegralType(primitive_type)); MatMultDims mat_mult_dims = GetMatMultDims(); bool is_column_major_matrix_vector = false; @@ -209,9 +180,7 @@ bool DotOpEmitter::EmitLlvmIrDotIfProfitable() { } } - if (!is_column_major_matrix_vector && !is_row_major_matrix_vector) { - return EmitSmallGemmIfProfitable(mat_mult_dims); - } + CHECK(is_column_major_matrix_vector || is_row_major_matrix_vector); int64 tiling_factor = GetGemvTilingFactor(); CHECK_GT(tiling_factor, 0); @@ -264,8 +233,6 @@ bool DotOpEmitter::EmitLlvmIrDotIfProfitable() { /*enable_fast_math=*/enable_fast_math, /*optimize_for_size=*/optimize_for_size); } - - return true; } Status DotOpEmitter::Emit() { @@ -306,27 +273,41 @@ Status DotOpEmitter::Emit() { return EmitScalarDot(); } - if (EmitLlvmIrDotIfProfitable()) { - return Status::OK(); + switch (GetDotImplementationStrategy(hlo_module_config_, DotInfo(dot_), + target_machine_features_)) { + case DotImplementationStrategy::kNaiveLlvmIr: + EmitNaiveLlvmIrGemm(); + return Status::OK(); + + case DotImplementationStrategy::kTiledLlvmIrGemv: + EmitTiledLlvmIrGemv(); + return Status::OK(); + + case DotImplementationStrategy::kTiledLlvmIrGemm: + EmitTiledLlvmIrGemm(); + return Status::OK(); + + case DotImplementationStrategy::kEigen: + return EmitCallToRuntime(); } +} +void DotOpEmitter::EmitNaiveLlvmIrGemm() { CHECK_EQ(addend_array_, nullptr); - if (PotentiallyImplementedAsEigenDot(dot_, target_machine_features_)) { - return EmitCallToRuntime(); - } + const Shape& lhs_shape = lhs_array_.GetShape(); + const Shape& rhs_shape = rhs_array_.GetShape(); + const DotDimensionNumbers& dim_nums = dot_.dot_dimension_numbers(); // Reduce along dimension 0 of the LHS and 1 of the RHS. Vectors are a special // case where the reduction dimension is 0 for both LHS and RHS. This results // in a vector dot product producing a scalar. - int64 lhs_reduction_dimension = - dot_.dot_dimension_numbers().lhs_contracting_dimensions(0); - int64 rhs_reduction_dimension = - dot_.dot_dimension_numbers().rhs_contracting_dimensions(0); + int64 lhs_reduction_dimension = dim_nums.lhs_contracting_dimensions(0); + int64 rhs_reduction_dimension = dim_nums.rhs_contracting_dimensions(0); // Verify the reduction dimension in the two operands are the same size. - TF_RET_CHECK(lhs_shape.dimensions(lhs_reduction_dimension) == - rhs_shape.dimensions(rhs_reduction_dimension)); + CHECK_EQ(lhs_shape.dimensions(lhs_reduction_dimension), + rhs_shape.dimensions(rhs_reduction_dimension)); bool lhs_reduction_along_minor_dimension = lhs_reduction_dimension == LayoutUtil::Minor(lhs_shape.layout(), 0); @@ -441,8 +422,6 @@ Status DotOpEmitter::Emit() { // Set the IR builder insert point to the exit basic block of the outer most // loop. b_->SetInsertPoint(loop_nest.GetOuterLoopExitBasicBlock()); - - return Status::OK(); } Status DotOpEmitter::EmitScalarDot() { @@ -489,7 +468,7 @@ Status DotOpEmitter::EmitCallToRuntime() { // The two transpose_... parameters are actually booleans, but we use int32 // to avoid target-dependent calling convention details. - bool multi_threaded = ShouldUseMultiThreadedEigen(); + bool multi_threaded = ShouldUseMultiThreadedEigen(hlo_module_config_); bool use_mkl_dnn = hlo_module_config_.debug_options().xla_cpu_use_mkl_dnn(); PrimitiveType type = target_array_.GetShape().element_type(); llvm::Type* float_type; @@ -600,12 +579,52 @@ DotOpEmitter::MatMultDims DotOpEmitter::GetMatMultDims() const { LayoutUtil::Minor(target_array_.GetShape().layout(), 0) == 0}; } +// For vector-matrix dot products, it is always profitable to make the Rhs +// column major. +absl::optional ProfitableToMakeDotOperandColumnMajor( + const HloInstruction& hlo) { + if (hlo.opcode() == HloOpcode::kDot && hlo.shape().dimensions_size() == 2 && + hlo.shape().dimensions(0) == 1) { + if (hlo.dot_dimension_numbers().rhs_contracting_dimensions(0) == 0) { + return 1; + } + return {}; + } + + if (hlo.opcode() == HloOpcode::kFusion && + hlo.fusion_kind() == HloInstruction::FusionKind::kOutput) { + auto* fusion_root = + hlo.fused_instructions_computation()->root_instruction(); + if (fusion_root->opcode() != HloOpcode::kAdd) { + return {}; + } + + for (auto* fusion_root_op : fusion_root->operands()) { + if (fusion_root_op->opcode() != HloOpcode::kDot) { + continue; + } + if (auto operand_num = + ProfitableToMakeDotOperandColumnMajor(*fusion_root_op)) { + auto* operand = fusion_root_op->operand(*operand_num); + if (operand->opcode() == HloOpcode::kParameter && + operand->user_count() == 1) { + return operand->parameter_number(); + } + } + } + } + + return {}; +} + +namespace internal { +namespace { // Return whether the given shape is rank 2. -static bool IsRank2(const Shape& shape) { return shape.rank() == 2; } +bool IsRank2(const Shape& shape) { return shape.rank() == 2; } // In a gemm operation where output = lhs * rhs, check whether the given shapes // are valid for the operation. -static bool AreValidGemmShapes( +bool AreAlignedGemmShapes( const Shape& lhs_shape, const Shape& rhs_shape, const Shape& output_shape, const TargetMachineFeatures& target_machine_features) { // The inputs and the output must @@ -634,88 +653,106 @@ static bool AreValidGemmShapes( return true; } -bool PotentiallyImplementedAsEigenDot( - const HloInstruction& hlo, - const TargetMachineFeatures& target_machine_features) { - // For certain types of Dot, we can call Eigen - if (hlo.opcode() == HloOpcode::kDot) { - const Shape& lhs_shape = hlo.operand(0)->shape(); - const Shape& rhs_shape = hlo.operand(1)->shape(); +bool IsAlignedGemm(const DotInfo& dot_info, + const TargetMachineFeatures& target_machine_features) { + if (ShapeUtil::IsZeroElementArray(dot_info.lhs_shape) || + ShapeUtil::IsZeroElementArray(dot_info.rhs_shape)) { + return false; + } - if (ShapeUtil::IsZeroElementArray(lhs_shape) || - ShapeUtil::IsZeroElementArray(rhs_shape)) { - return false; - } + return AreAlignedGemmShapes(dot_info.lhs_shape, dot_info.rhs_shape, + dot_info.result_shape, target_machine_features); +} - if (ProfitableToImplementDotInTiledLlvmIr(hlo)) { - return false; - } +bool CanEmitTiledLlvmIrGemm( + const HloModuleConfig& config, const DotInfo& dot_info, + const TargetMachineFeatures& target_machine_features) { + CHECK(IsAlignedGemm(dot_info, target_machine_features)); - // If gemm can accept the operand shapes, use it rather than a custom - // kernel. - if (AreValidGemmShapes(lhs_shape, rhs_shape, hlo.shape(), - target_machine_features)) { - const DotDimensionNumbers& dim_numbers = hlo.dot_dimension_numbers(); - // The size of the reduction dimension should match. The shape inference - // guarantees this invariant, so the check here is for programming - // errors. - CHECK_EQ(lhs_shape.dimensions(dim_numbers.lhs_contracting_dimensions(0)), - rhs_shape.dimensions(dim_numbers.rhs_contracting_dimensions(0))); - return true; - } + if (ShouldUseMultiThreadedEigen(config)) { + return false; } - return false; -} + int m = dot_info.result_shape.dimensions(0); + int k = dot_info.lhs_shape.dimensions( + dot_info.dim_nums.lhs_contracting_dimensions(0)); + int n = dot_info.result_shape.dimensions(1); -// For vector-matrix dot products, it is always profitable to make the Rhs -// column major. -absl::optional ProfitableToMakeDotOperandColumnMajor( - const HloInstruction& hlo) { - if (hlo.opcode() == HloOpcode::kDot && hlo.shape().dimensions_size() == 2 && - hlo.shape().dimensions(0) == 1) { - if (hlo.dot_dimension_numbers().rhs_contracting_dimensions(0) == 0) { - return 1; + if (!options::ForceEnableExperimentalLlvmIrGemm(config)) { + // TODO(sanjoy): We should make these numbers micro-arch specific. + bool small_gemm = + k <= 128 && ((m <= 32 && n <= 128) || (m <= 128 && n <= 32)); + if (!small_gemm) { + return false; } - return {}; } - if (hlo.opcode() == HloOpcode::kFusion && - hlo.fusion_kind() == HloInstruction::FusionKind::kOutput) { - auto* fusion_root = - hlo.fused_instructions_computation()->root_instruction(); - if (fusion_root->opcode() != HloOpcode::kAdd) { - return {}; - } + bool lhs_non_canonical = dot_info.dim_nums.lhs_contracting_dimensions(0) == 0; + bool rhs_non_canonical = dot_info.dim_nums.rhs_contracting_dimensions(0) == 1; - for (auto* fusion_root_op : fusion_root->operands()) { - if (fusion_root_op->opcode() != HloOpcode::kDot) { - continue; - } - if (auto operand_num = - ProfitableToMakeDotOperandColumnMajor(*fusion_root_op)) { - auto* operand = fusion_root_op->operand(*operand_num); - if (operand->opcode() == HloOpcode::kParameter && - operand->user_count() == 1) { - return operand->parameter_number(); - } - } - } + if (lhs_non_canonical || rhs_non_canonical) { + return false; } - return {}; + if (dot_info.result_shape.element_type() == F16) { + // TODO(sanjoy): This is probably easy to fix, but I want to keep the CL + // adding this comment NFC. + return false; + } + + return true; } +} // namespace -bool ProfitableToImplementDotInTiledLlvmIr(const HloInstruction& dot) { +DotImplementationStrategy GetDotImplementationStrategy( + const HloModuleConfig& config, const DotInfo& dot_info, + const TargetMachineFeatures& target_machine_features) { + PrimitiveType element_type = dot_info.result_shape.element_type(); // Any Matrix-Vector product of floating point or integral type, or // a transpose-dot fusion of the same can be lowered to a tiled LLVM // IR implementation. - const Shape& shape = dot.shape(); - return shape.dimensions_size() == 2 && - (shape.dimensions(0) == 1 || shape.dimensions(1) == 1) && - (primitive_util::IsFloatingPointType(shape.element_type()) || - primitive_util::IsIntegralType(shape.element_type())); + if (dot_info.result_shape.dimensions_size() == 2 && + (dot_info.result_shape.dimensions(0) == 1 || + dot_info.result_shape.dimensions(1) == 1) && + (primitive_util::IsFloatingPointType(element_type) || + primitive_util::IsIntegralType(element_type))) { + return DotImplementationStrategy::kTiledLlvmIrGemv; + } + + if (IsAlignedGemm(dot_info, target_machine_features)) { + return CanEmitTiledLlvmIrGemm(config, dot_info, target_machine_features) + ? DotImplementationStrategy::kTiledLlvmIrGemm + : DotImplementationStrategy::kEigen; + } + + return DotImplementationStrategy::kNaiveLlvmIr; } +} // namespace internal +bool DotImplementationCanHandleTranspose( + const HloInstruction& dot_instr, + const TargetMachineFeatures& target_machine_features) { + DotImplementationStrategy impl_strategy = + GetDotImplementationStrategy(dot_instr.parent()->parent()->config(), + DotInfo(dot_instr), target_machine_features); + + // TODO(sanjoy): This is not quite right, it should be `impl_strategy == + // kEigen || impl_strategy == kTiledLlvmIrGemv || impl_strategy == + // kNaiveLlvmIr` but I'll fix this in a later CL in the interest of keeping + // the CL adding this comment NFC. + return impl_strategy == DotImplementationStrategy::kTiledLlvmIrGemm || + impl_strategy == DotImplementationStrategy::kEigen; +} + +bool DotOperandsAndResultMustHaveRowMajorLayout( + const HloInstruction& dot_instr, + const TargetMachineFeatures& target_machine_features) { + DotImplementationStrategy impl_strategy = + GetDotImplementationStrategy(dot_instr.parent()->parent()->config(), + DotInfo(dot_instr), target_machine_features); + + return impl_strategy == DotImplementationStrategy::kTiledLlvmIrGemm || + impl_strategy == DotImplementationStrategy::kEigen; +} } // namespace cpu } // namespace xla diff --git a/tensorflow/compiler/xla/service/cpu/dot_op_emitter.h b/tensorflow/compiler/xla/service/cpu/dot_op_emitter.h index 4c2041b556..eaa226a4c2 100644 --- a/tensorflow/compiler/xla/service/cpu/dot_op_emitter.h +++ b/tensorflow/compiler/xla/service/cpu/dot_op_emitter.h @@ -30,9 +30,16 @@ limitations under the License. namespace xla { namespace cpu { +// Returns true if the two operands and the output of `dot_instr` must have row +// major layout. +bool DotOperandsAndResultMustHaveRowMajorLayout( + const HloInstruction& dot_instr, + const TargetMachineFeatures& target_machine_features); -bool PotentiallyImplementedAsEigenDot( - const HloInstruction& hlo, +// Returns true our lowering strategy for `dot_instr` can fold in transposes to +// the either of the inputs. +bool DotImplementationCanHandleTranspose( + const HloInstruction& dot_instr, const TargetMachineFeatures& target_machine_features); // Returns the index for an operand to `hlo` that should ideally be column @@ -41,10 +48,6 @@ bool PotentiallyImplementedAsEigenDot( absl::optional ProfitableToMakeDotOperandColumnMajor( const HloInstruction& hlo); -// Returns true to indicate that we can generate a tiled LLVM IR implementation -// for |dot|. -bool ProfitableToImplementDotInTiledLlvmIr(const HloInstruction& dot); - // Helper class for emitting LLVM IR to perform the dot operation. class DotOpEmitter { public: @@ -81,10 +84,6 @@ class DotOpEmitter { // LHS and RHS) and store the results in the target. Status EmitScalarDot(); - // Emit an LLVM IR implementation of the dot operation if we can. Returns - // true if an LLVM IR implementation was emitted. - bool EmitLlvmIrDotIfProfitable(); - // Emits a call to the CPU runtime to perform the matrix multiply. Status EmitCallToRuntime(); @@ -121,7 +120,15 @@ class DotOpEmitter { // of rank 2 as well). MatMultDims GetMatMultDims() const; - bool EmitSmallGemmIfProfitable(const MatMultDims& mat_mult_dims); + // Lowers the dot operation as a tiled Matrix*Vector loop. + void EmitTiledLlvmIrGemv(); + + // Lowers the dot operation as a tiled Matrix*Matrix loop. + void EmitTiledLlvmIrGemm(); + + // Lowers the dot operation as a naive nested loop that computes the result + // one element at a time. + void EmitNaiveLlvmIrGemm(); // When doing a tiled GEMV in LLVM IR, a "tile" consists of this many vector // registers. @@ -142,17 +149,6 @@ class DotOpEmitter { .value_or(kDefaultTileSize); } - // Returns true if we should use an experimental implementation of GEMM - // (general matrix matrix multiplication) if possible. - bool EnableExperimentalLlvmIrGemm() const { - return options::EnableExperimentalLlvmIrGemm(hlo_module_config_); - } - - // Returns true if we should call into multi-threaded Eigen routines. - bool ShouldUseMultiThreadedEigen() { - return hlo_module_config_.debug_options().xla_cpu_multi_thread_eigen(); - } - const HloInstruction& dot_; const llvm_ir::IrArray& target_array_; const llvm_ir::IrArray& lhs_array_; diff --git a/tensorflow/compiler/xla/service/cpu/dot_op_emitter_internal.h b/tensorflow/compiler/xla/service/cpu/dot_op_emitter_internal.h new file mode 100644 index 0000000000..cc28918ed6 --- /dev/null +++ b/tensorflow/compiler/xla/service/cpu/dot_op_emitter_internal.h @@ -0,0 +1,88 @@ +/* Copyright 2018 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_COMPILER_XLA_SERVICE_CPU_DOT_OP_EMITTER_INTERNAL_H_ +#define TENSORFLOW_COMPILER_XLA_SERVICE_CPU_DOT_OP_EMITTER_INTERNAL_H_ + +#include "tensorflow/compiler/xla/service/cpu/target_machine_features.h" +#include "tensorflow/compiler/xla/service/hlo_instruction.h" +#include "tensorflow/compiler/xla/service/hlo_module.h" + +// ----------------------------------------------------------------------------- +// INTERNAL HEADER. +// +// This file exposes internal implementation details from dot_op_emitter.cc for +// unit tests. Please do not depend on this! +// +// ----------------------------------------------------------------------------- + +namespace xla { +namespace cpu { +namespace internal { + +// Represents a dot operation. We use this in lieu of an `HloInstruction` +// because we want to be able to create this for the "inner" dot operation in a +// batch dot, for which there is no separate HLO instruction. +struct DotInfo { + Shape lhs_shape; + Shape rhs_shape; + Shape result_shape; + DotDimensionNumbers dim_nums; + + explicit DotInfo(const HloInstruction& instr) { + CHECK_EQ(instr.opcode(), HloOpcode::kDot); + lhs_shape = instr.operand(0)->shape(); + rhs_shape = instr.operand(1)->shape(); + result_shape = instr.shape(); + dim_nums = instr.dot_dimension_numbers(); + } +}; + +// Dictates how a dot operation is implemented. +enum class DotImplementationStrategy { + // The dot operation is lowered into LLVM IR that implements a naive nested + // loop that computes the result one element at a time. This is our + // "fallback"; we don't really want this to kick in for any non-trival dot + // operation. + kNaiveLlvmIr, + + // The dot operation is lowered into LLVM IR that implements a tiled + // Matrix*Vector operation. This strategy also allows fusing in a bias add + // into the dot. The matrix can be row major or column major, both are + // supported. + kTiledLlvmIrGemv, + + // The dot operation is lowered into LLVM IR that implemetns a tiled + // Matrix*Matrix operation. No fusions are supported. The two inputs + // and the output have to be row major. + kTiledLlvmIrGemm, + + // The dot operation is lowered into a call into an Eigen routine. No fusions + // are supported today. The two inputs and the output have to be row major. + // However, we do allow transposing either the LHS or the RHS as part of the + // GEMM -- we expose this flexibility as flexibility in the contraction + // dimensions, but we can also see this as flexibility in the input layouts. + kEigen, +}; + +// Returns the implementation strategy for a dot with the configuration +// `dot_info`. +DotImplementationStrategy GetDotImplementationStrategy( + const HloModuleConfig& config, const DotInfo& dot_info, + const TargetMachineFeatures& target_machine_features); +} // namespace internal +} // namespace cpu +} // namespace xla + +#endif // TENSORFLOW_COMPILER_XLA_SERVICE_CPU_DOT_OP_EMITTER_INTERNAL_H_ diff --git a/tensorflow/compiler/xla/service/cpu/parallel_task_assignment.cc b/tensorflow/compiler/xla/service/cpu/parallel_task_assignment.cc index 3b423f6391..6121d1ca9a 100644 --- a/tensorflow/compiler/xla/service/cpu/parallel_task_assignment.cc +++ b/tensorflow/compiler/xla/service/cpu/parallel_task_assignment.cc @@ -146,8 +146,6 @@ int64 ParallelTaskAssignment::GetTargetParallelTaskCount( (opcode == HloOpcode::kConvolution && PotentiallyImplementedAsEigenConvolution(*instruction, target_machine_features_)) || - PotentiallyImplementedAsEigenDot(*instruction, - target_machine_features_) || (opcode == HloOpcode::kFusion && instruction->fusion_kind() != HloInstruction::FusionKind::kLoop) || instruction->shape().IsTuple()) { -- GitLab From 8c91712adfb586da675aeaa59730fd4c6e8c5ded Mon Sep 17 00:00:00 2001 From: Siju Date: Tue, 8 Jan 2019 10:02:21 +0530 Subject: [PATCH 461/622] LOG the result in case of error. --- tensorflow/core/common_runtime/gpu/gpu_debug_allocator.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tensorflow/core/common_runtime/gpu/gpu_debug_allocator.cc b/tensorflow/core/common_runtime/gpu/gpu_debug_allocator.cc index 77737a76b6..2879b00f55 100644 --- a/tensorflow/core/common_runtime/gpu/gpu_debug_allocator.cc +++ b/tensorflow/core/common_runtime/gpu/gpu_debug_allocator.cc @@ -46,7 +46,7 @@ bool CheckMask(se::StreamExecutor* exec, void* ptr, int64* mask) { Status result = exec->SynchronousMemcpyD2H(gpu_ptr, MASK_BYTES, tmp); if (!result.ok()) { - LOG(FATAL) << "Could not copy debug mask"; + LOG(FATAL) << "Could not copy debug mask, " << result.error_message(); } bool ok = true; @@ -66,7 +66,7 @@ void InitMask(se::StreamExecutor* exec, void* ptr, int64* mask) { se::DeviceMemory gpu_ptr{se::DeviceMemoryBase{ptr, MASK_BYTES}}; Status result = exec->SynchronousMemcpyH2D(mask, MASK_BYTES, &gpu_ptr); if (!result.ok()) { - LOG(FATAL) << "Could not copy debug mask"; + LOG(FATAL) << "Could not copy debug mask, " << result.error_message(); } } @@ -176,7 +176,7 @@ void* GPUNanResetAllocator::AllocateRaw(size_t alignment, size_t num_bytes) { Status result = stream_exec_->SynchronousMemcpyH2D(&nans[0], req_size, &nan_ptr); if (!result.ok()) { - LOG(ERROR) << "Could not initialize to NaNs"; + LOG(ERROR) << "Could not initialize to NaNs, " << result.error_message(); } return allocated_ptr; @@ -192,7 +192,7 @@ void GPUNanResetAllocator::DeallocateRaw(void* ptr) { Status result = stream_exec_->SynchronousMemcpyH2D(&nans[0], req_size, &nan_ptr); if (!result.ok()) { - LOG(ERROR) << "Could not initialize to NaNs"; + LOG(ERROR) << "Could not initialize to NaNs, " << result.error_message(); } } -- GitLab From 246780be77509fa4bea195d05fa73faa99467c58 Mon Sep 17 00:00:00 2001 From: Sanjoy Das Date: Mon, 7 Jan 2019 21:32:32 -0800 Subject: [PATCH 462/622] Hide the DotOpEmitter class and make it use a DotInfo instead of a HloInstr; NFC - Hiding it in the .cc is good encapsulation. - Making it depend on a DotInfo as opposed to an HLO instruction will make implementing batch dot (in terms of non-batch dot) easier. PiperOrigin-RevId: 228281578 --- .../xla/service/cpu/dot_op_emitter.cc | 159 ++++++++++++++---- .../compiler/xla/service/cpu/dot_op_emitter.h | 130 ++------------ .../compiler/xla/service/cpu/ir_emitter.cc | 16 +- 3 files changed, 156 insertions(+), 149 deletions(-) diff --git a/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc b/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc index 70d8fee265..e8a84ebe6b 100644 --- a/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc +++ b/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc @@ -53,9 +53,107 @@ namespace { bool ShouldUseMultiThreadedEigen(const HloModuleConfig& config) { return config.debug_options().xla_cpu_multi_thread_eigen(); } + +// Helper class for emitting LLVM IR to perform the dot operation. +class DotOpEmitter { + public: + explicit DotOpEmitter(DotInfo dot_info, string dot_hlo_name, + const llvm_ir::IrArray& target_array, + const llvm_ir::IrArray& lhs_array, + const llvm_ir::IrArray& rhs_array, + const llvm_ir::IrArray* addend_array, + llvm::Value* executable_run_options_value, + llvm::IRBuilder<>* b, + const HloModuleConfig& hlo_module_config, + const TargetMachineFeatures& target_machine_features); + + // Emits the IR to perform the dot operation. + Status Emit(); + + private: + // Emits instructions to perform a scalar dot product (a multiply of the + // LHS and RHS) and store the results in the target. + Status EmitScalarDot(); + + // Emits a call to the CPU runtime to perform the matrix multiply. + Status EmitCallToRuntime(); + + // Represents the dimensions of a matrix-matrix multiply operation. + struct MatMultDims { + // The number of rows in the LHS. + int64 m; + + // The number of columns in the LHS, which is also must be equal to the + // number of rows in the RHS. + int64 k; + + // The number of columns on the RHS. + int64 n; + + // True if the LHS matrix is column major. + bool lhs_column_major; + + // True if the LHS contraction dimension is not 1. + bool lhs_non_canonical; + + // True if the RHS matrix is column major. + bool rhs_column_major; + + // True if the RHS contraction dimension is not 0. + bool rhs_non_canonical; + + // True if the result matrix is column major. + bool target_column_major; + }; + + // Get the MatMultDims instance for the dot product this DotOpEmitter + // represents. Precondition: the dot is of rank 2 (and thus its operands are + // of rank 2 as well). + MatMultDims GetMatMultDims() const; + + // Lowers the dot operation as a tiled Matrix*Vector loop. + void EmitTiledLlvmIrGemv(); + + // Lowers the dot operation as a tiled Matrix*Matrix loop. + void EmitTiledLlvmIrGemm(); + + // Lowers the dot operation as a naive nested loop that computes the result + // one element at a time. + void EmitNaiveLlvmIrGemm(); + + // When doing a tiled GEMV in LLVM IR, a "tile" consists of this many vector + // registers. + int64 GetGemvTilingFactor() const { + const int64 kDefaultTilingFactor = 8; + return options::LlvmIrGemvTilingFactor(hlo_module_config_) + .value_or(kDefaultTilingFactor); + } + + std::tuple GetGemmTileSize() const { + // Tuned for broadwell - Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60GHz + // + // TODO(b/80093688): Tune for other architectures and centralize this + // information in one place. + const std::tuple kDefaultTileSize = + std::tuple(11, 9, 1); + return options::LlvmIrGemmTileSize(hlo_module_config_) + .value_or(kDefaultTileSize); + } + + DotInfo dot_info_; + string dot_hlo_name_; + const llvm_ir::IrArray& target_array_; + const llvm_ir::IrArray& lhs_array_; + const llvm_ir::IrArray& rhs_array_; + const llvm_ir::IrArray* addend_array_; + llvm::Value* executable_run_options_value_; + llvm::IRBuilder<>* b_; + const HloModuleConfig& hlo_module_config_; + const TargetMachineFeatures& target_machine_features_; +}; } // namespace -DotOpEmitter::DotOpEmitter(const HloInstruction& dot, +DotOpEmitter::DotOpEmitter(DotInfo dot_info, string dot_hlo_name, const llvm_ir::IrArray& target_array, const llvm_ir::IrArray& lhs_array, const llvm_ir::IrArray& rhs_array, @@ -64,7 +162,8 @@ DotOpEmitter::DotOpEmitter(const HloInstruction& dot, llvm::IRBuilder<>* b, const HloModuleConfig& hlo_module_config, const TargetMachineFeatures& target_machine_features) - : dot_(dot), + : dot_info_(std::move(dot_info)), + dot_hlo_name_(std::move(dot_hlo_name)), target_array_(target_array), lhs_array_(lhs_array), rhs_array_(rhs_array), @@ -74,23 +173,8 @@ DotOpEmitter::DotOpEmitter(const HloInstruction& dot, hlo_module_config_(hlo_module_config), target_machine_features_(target_machine_features) {} -/* static */ Status DotOpEmitter::EmitDotOperation( - const HloInstruction& dot, const llvm_ir::IrArray& target_array, - const llvm_ir::IrArray& lhs_array, const llvm_ir::IrArray& rhs_array, - const llvm_ir::IrArray* addend_array, - llvm::Value* executable_run_options_value, llvm::IRBuilder<>* b, - const HloModuleConfig& hlo_module_config, - const TargetMachineFeatures& target_machine_features) { - PrimitiveType type = target_array.GetShape().element_type(); - TF_RET_CHECK(F16 == type || F32 == type || F64 == type || C64 == type); - DotOpEmitter dot_emitter(dot, target_array, lhs_array, rhs_array, - addend_array, executable_run_options_value, b, - hlo_module_config, target_machine_features); - return dot_emitter.Emit(); -} - void DotOpEmitter::EmitTiledLlvmIrGemm() { - PrimitiveType primitive_type = dot_.shape().element_type(); + PrimitiveType primitive_type = dot_info_.result_shape.element_type(); MatMultDims mat_mult_dims = GetMatMultDims(); llvm::Value* lhs = lhs_array_.GetBasePointer(); @@ -136,7 +220,7 @@ void DotOpEmitter::EmitTiledLlvmIrGemm() { } void DotOpEmitter::EmitTiledLlvmIrGemv() { - PrimitiveType primitive_type = dot_.shape().element_type(); + PrimitiveType primitive_type = dot_info_.result_shape.element_type(); CHECK(primitive_util::IsFloatingPointType(primitive_type) || primitive_util::IsIntegralType(primitive_type)); @@ -258,11 +342,6 @@ Status DotOpEmitter::Emit() { // which performs the sum-of-products (the reduction loop) before storing // the result in the output buffer. - // This routine assumes that the dot operation is not in a parallelized - // enclosing computation. - CHECK( - dot_.parent()->root_instruction()->outer_dimension_partitions().empty()); - const Shape& lhs_shape = lhs_array_.GetShape(); const Shape& rhs_shape = rhs_array_.GetShape(); @@ -273,7 +352,7 @@ Status DotOpEmitter::Emit() { return EmitScalarDot(); } - switch (GetDotImplementationStrategy(hlo_module_config_, DotInfo(dot_), + switch (GetDotImplementationStrategy(hlo_module_config_, dot_info_, target_machine_features_)) { case DotImplementationStrategy::kNaiveLlvmIr: EmitNaiveLlvmIrGemm(); @@ -297,7 +376,7 @@ void DotOpEmitter::EmitNaiveLlvmIrGemm() { const Shape& lhs_shape = lhs_array_.GetShape(); const Shape& rhs_shape = rhs_array_.GetShape(); - const DotDimensionNumbers& dim_nums = dot_.dot_dimension_numbers(); + const DotDimensionNumbers& dim_nums = dot_info_.dim_nums; // Reduce along dimension 0 of the LHS and 1 of the RHS. Vectors are a special // case where the reduction dimension is 0 for both LHS and RHS. This results @@ -317,7 +396,7 @@ void DotOpEmitter::EmitNaiveLlvmIrGemm() { // Create loop nests which loop through the LHS operand dimensions and the RHS // operand dimensions. The reduction dimension of the LHS and RHS are handled // in a separate innermost loop which performs the sum of products. - llvm_ir::ForLoopNest loop_nest(llvm_ir::IrName(&dot_), b_); + llvm_ir::ForLoopNest loop_nest(llvm_ir::IrName(dot_hlo_name_), b_); llvm_ir::IrArray::Index lhs_index = loop_nest.EmitOperandArrayLoopNest( lhs_array_, /*dimension_to_skip=*/lhs_reduction_dimension, "lhs"); llvm_ir::IrArray::Index rhs_index = loop_nest.EmitOperandArrayLoopNest( @@ -561,11 +640,11 @@ Status DotOpEmitter::EmitCallToRuntime() { } DotOpEmitter::MatMultDims DotOpEmitter::GetMatMultDims() const { - CHECK_EQ(dot_.shape().dimensions_size(), 2); + CHECK_EQ(dot_info_.result_shape.dimensions_size(), 2); const Shape& lhs_shape = lhs_array_.GetShape(); const Shape& rhs_shape = rhs_array_.GetShape(); - const DotDimensionNumbers& dim_nums = dot_.dot_dimension_numbers(); + const DotDimensionNumbers& dim_nums = dot_info_.dim_nums; return { /*m=*/lhs_shape.dimensions(1 - dim_nums.lhs_contracting_dimensions(0)), @@ -754,5 +833,27 @@ bool DotOperandsAndResultMustHaveRowMajorLayout( return impl_strategy == DotImplementationStrategy::kTiledLlvmIrGemm || impl_strategy == DotImplementationStrategy::kEigen; } + +Status EmitDotOperation(const HloInstruction& dot, + const llvm_ir::IrArray& target_array, + const llvm_ir::IrArray& lhs_array, + const llvm_ir::IrArray& rhs_array, + const llvm_ir::IrArray* addend_array, + llvm::Value* executable_run_options_value, + llvm::IRBuilder<>* b, + const HloModuleConfig& hlo_module_config, + const TargetMachineFeatures& target_machine_features) { + // This routine assumes that the dot operation is not in a parallelized + // enclosing computation. + CHECK(dot.parent()->root_instruction()->outer_dimension_partitions().empty()); + + PrimitiveType type = target_array.GetShape().element_type(); + TF_RET_CHECK(F16 == type || F32 == type || F64 == type || C64 == type); + DotOpEmitter dot_emitter(DotInfo(dot), dot.name(), target_array, lhs_array, + rhs_array, addend_array, + executable_run_options_value, b, hlo_module_config, + target_machine_features); + return dot_emitter.Emit(); +} } // namespace cpu } // namespace xla diff --git a/tensorflow/compiler/xla/service/cpu/dot_op_emitter.h b/tensorflow/compiler/xla/service/cpu/dot_op_emitter.h index eaa226a4c2..105bd3005c 100644 --- a/tensorflow/compiler/xla/service/cpu/dot_op_emitter.h +++ b/tensorflow/compiler/xla/service/cpu/dot_op_emitter.h @@ -48,118 +48,24 @@ bool DotImplementationCanHandleTranspose( absl::optional ProfitableToMakeDotOperandColumnMajor( const HloInstruction& hlo); -// Helper class for emitting LLVM IR to perform the dot operation. -class DotOpEmitter { - public: - // Emit LLVM IR to perform the dot operation on lhs_array and rhs_array and - // place the result in target_array. IR is emitted at current insert point of - // the builder. Upon completion of the method, the insert point is set to the - // end of all instructions emitted for this operation. - // - // If `addend_array` is not nullptr then it must be an array of the same - // dimensions as the result, and the result is computed as `addend_array` + - // dot(`lhs_array`, `rhs_array`). A non-null `addend_array` is only supported - // for Matrix-vector products. - static Status EmitDotOperation( - const HloInstruction& dot, const llvm_ir::IrArray& target_array, - const llvm_ir::IrArray& lhs_array, const llvm_ir::IrArray& rhs_array, - const llvm_ir::IrArray* addend_array, - llvm::Value* executable_run_options_value, llvm::IRBuilder<>* b, - const HloModuleConfig& hlo_module_config, - const TargetMachineFeatures& target_machine_features); - - private: - DotOpEmitter(const HloInstruction& dot, const llvm_ir::IrArray& target_array, - const llvm_ir::IrArray& lhs_array, - const llvm_ir::IrArray& rhs_array, - const llvm_ir::IrArray* addend_array, - llvm::Value* executable_run_options_value, llvm::IRBuilder<>* b, - const HloModuleConfig& hlo_module_config, - const TargetMachineFeatures& target_machine_features); - - // Emits the IR to perform the dot operation. - Status Emit(); - - // Emits instructions to perform a scalar dot product (a multiply of the - // LHS and RHS) and store the results in the target. - Status EmitScalarDot(); - - // Emits a call to the CPU runtime to perform the matrix multiply. - Status EmitCallToRuntime(); - - // Represents the dimensions of a matrix-matrix multiply operation. - struct MatMultDims { - // The number of rows in the LHS. - int64 m; - - // The number of columns in the LHS, which is also must be equal to the - // number of rows in the RHS. - int64 k; - - // The number of columns on the RHS. - int64 n; - - // True if the LHS matrix is column major. - bool lhs_column_major; - - // True if the LHS contraction dimension is not 1. - bool lhs_non_canonical; - - // True if the RHS matrix is column major. - bool rhs_column_major; - - // True if the RHS contraction dimension is not 0. - bool rhs_non_canonical; - - // True if the result matrix is column major. - bool target_column_major; - }; - - // Get the MatMultDims instance for the dot product this DotOpEmitter - // represents. Precondition: the dot is of rank 2 (and thus its operands are - // of rank 2 as well). - MatMultDims GetMatMultDims() const; - - // Lowers the dot operation as a tiled Matrix*Vector loop. - void EmitTiledLlvmIrGemv(); - - // Lowers the dot operation as a tiled Matrix*Matrix loop. - void EmitTiledLlvmIrGemm(); - - // Lowers the dot operation as a naive nested loop that computes the result - // one element at a time. - void EmitNaiveLlvmIrGemm(); - - // When doing a tiled GEMV in LLVM IR, a "tile" consists of this many vector - // registers. - int64 GetGemvTilingFactor() const { - const int64 kDefaultTilingFactor = 8; - return options::LlvmIrGemvTilingFactor(hlo_module_config_) - .value_or(kDefaultTilingFactor); - } - - std::tuple GetGemmTileSize() const { - // Tuned for broadwell - Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60GHz - // - // TODO(b/80093688): Tune for other architectures and centralize this - // information in one place. - const std::tuple kDefaultTileSize = - std::tuple(11, 9, 1); - return options::LlvmIrGemmTileSize(hlo_module_config_) - .value_or(kDefaultTileSize); - } - - const HloInstruction& dot_; - const llvm_ir::IrArray& target_array_; - const llvm_ir::IrArray& lhs_array_; - const llvm_ir::IrArray& rhs_array_; - const llvm_ir::IrArray* addend_array_; - llvm::Value* executable_run_options_value_; - llvm::IRBuilder<>* b_; - const HloModuleConfig& hlo_module_config_; - const TargetMachineFeatures& target_machine_features_; -}; - +// Emit LLVM IR to perform the dot operation on lhs_array and rhs_array and +// place the result in target_array. IR is emitted at current insert point of +// the builder. Upon completion of the method, the insert point is set to the +// end of all instructions emitted for this operation. +// +// If `addend_array` is not nullptr then it must be an array of the same +// dimensions as the result, and the result is computed as `addend_array` + +// dot(`lhs_array`, `rhs_array`). A non-null `addend_array` is only supported +// for Matrix-vector products. +Status EmitDotOperation(const HloInstruction& dot, + const llvm_ir::IrArray& target_array, + const llvm_ir::IrArray& lhs_array, + const llvm_ir::IrArray& rhs_array, + const llvm_ir::IrArray* addend_array, + llvm::Value* executable_run_options_value, + llvm::IRBuilder<>* b, + const HloModuleConfig& hlo_module_config, + const TargetMachineFeatures& target_machine_features); } // namespace cpu } // namespace xla diff --git a/tensorflow/compiler/xla/service/cpu/ir_emitter.cc b/tensorflow/compiler/xla/service/cpu/ir_emitter.cc index b352848824..91e3693354 100644 --- a/tensorflow/compiler/xla/service/cpu/ir_emitter.cc +++ b/tensorflow/compiler/xla/service/cpu/ir_emitter.cc @@ -970,10 +970,10 @@ Status IrEmitter::HandleDot(HloInstruction* dot) { << llvm_ir::DumpToString(*target_array.GetBasePointer()); // Dot operation is complicated so we delegate to a helper class. - return DotOpEmitter::EmitDotOperation( - *dot, target_array, lhs_array, rhs_array, /*addend_array=*/nullptr, - GetExecutableRunOptionsArgument(), &b_, hlo_module_config_, - target_machine_features_); + return EmitDotOperation(*dot, target_array, lhs_array, rhs_array, + /*addend_array=*/nullptr, + GetExecutableRunOptionsArgument(), &b_, + hlo_module_config_, target_machine_features_); } StatusOr IrEmitter::EmitTargetElementLoopBodyForConvolution( @@ -2203,10 +2203,10 @@ Status IrEmitter::HandleFusion(HloInstruction* fusion) { llvm_ir::IrArray addend_array( GetIrArrayFor(fusion->operand(addend_param_number))); - TF_RETURN_IF_ERROR(DotOpEmitter::EmitDotOperation( - *dot, target_array, lhs_array, rhs_array, &addend_array, - GetExecutableRunOptionsArgument(), &b_, hlo_module_config_, - target_machine_features_)); + TF_RETURN_IF_ERROR( + EmitDotOperation(*dot, target_array, lhs_array, rhs_array, + &addend_array, GetExecutableRunOptionsArgument(), &b_, + hlo_module_config_, target_machine_features_)); return Status::OK(); } else { return Unimplemented("Fusion kind not implemented on CPU"); -- GitLab From 2ca0bf7cf4e16cbed6affd7dd844fea4ed3a62df Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Mon, 7 Jan 2019 22:40:38 -0800 Subject: [PATCH 463/622] This CL checks StepStats, records the output slot for each Switch Op execution, and adds the output slot vector to GraphDef Switch node. When VirtualScheduler schedules the Switch Op, it will take the actual output branch. PiperOrigin-RevId: 228285499 --- .../core/grappler/costs/virtual_scheduler.cc | 113 +++- .../core/grappler/costs/virtual_scheduler.h | 12 + .../grappler/costs/virtual_scheduler_test.cc | 516 ++++++++++++++++++ 3 files changed, 623 insertions(+), 18 deletions(-) diff --git a/tensorflow/core/grappler/costs/virtual_scheduler.cc b/tensorflow/core/grappler/costs/virtual_scheduler.cc index 7071c679dc..bb8425a5a1 100644 --- a/tensorflow/core/grappler/costs/virtual_scheduler.cc +++ b/tensorflow/core/grappler/costs/virtual_scheduler.cc @@ -15,8 +15,6 @@ limitations under the License. #include "tensorflow/core/grappler/costs/virtual_scheduler.h" -#include - #include "tensorflow/core/framework/allocation_description.pb.h" #include "tensorflow/core/framework/attr_value.pb.h" #include "tensorflow/core/framework/node_def.pb.h" @@ -38,6 +36,12 @@ namespace tensorflow { namespace grappler { namespace { +// Optional attribute name for Switch op as a vector of int that tells +// which branch the Switch output is taken on every round of execution. +// We use this side information, if provided, for scheduling ops after Switch +// correctly (e.g., While loop). +constexpr char kOutputSlots[] = "_output_slot_vector"; + Costs CombineCosts(const Costs& left, const Costs& right) { CHECK_NE(left.max_memory, kMemoryUnknown); CHECK_NE(left.max_per_op_buffers, kMemoryUnknown); @@ -402,6 +406,8 @@ Status VirtualScheduler::Init() { name_to_node[node->name()] = node; } + // Traverse the graph to check if the graph is annotated with Switch outputs. + // Also record _Send nodes. // TODO(dyoon): Instead of identifying _Send node here manually, add _Send // to _Recv as control dependency when creating GrapplerItem. std::unordered_map name_to_send; @@ -410,6 +416,11 @@ Status VirtualScheduler::Init() { const auto& attr = node.attr(); name_to_send[attr.at("tensor_name").s()] = &node; } + + if (IsSwitch(node)) { + const auto& attr = node.attr(); + if (attr.count(kOutputSlots) > 0) switch_outputs_annotated_ = true; + } } // To reuse _Recv ops. @@ -771,6 +782,82 @@ Costs& VirtualScheduler::FindOrCreateZero(const string& op_name, return it->second; } +// Check Switch outputs in updated MetaGraphDef, add corresponding nodes to +// ready queue. +// Fallback to add all outputs if fail to find the actual output. +bool VirtualScheduler::AddSwitchOutputsToReadyQueue( + const NodeDef* node, int curr_iter, const Costs::Duration& curr_time) { + if (node->attr().count(kOutputSlots) == 0) return false; + + auto& node_state = node_map_[node]; + const auto& slot_vector = node->attr().at(kOutputSlots); + if (slot_vector.list().i_size() <= curr_iter) { + // Sometimes we encounter infinite loop. Fall back to add all outputs. + return false; + } + + int slot = slot_vector.list().i(curr_iter); + for (const auto& port_num_output_pair : node_state.outputs) { + if (port_num_output_pair.first != slot) continue; + + for (auto* output_node : port_num_output_pair.second) { + auto& output_state = node_map_[output_node]; + output_state.num_inputs_ready++; + // Execute a node as soon as all its inputs are ready. Merge nodes + // are special since they run as soon as one of their inputs becomes + // available. + if (output_state.num_inputs_ready == output_state.inputs.size() || + IsMerge(*output_node)) { + // This output node is now ready. + output_state.time_ready = curr_time; + ready_nodes_->AddNode(output_node); + VLOG(3) << "Node " << node->name() << " iter " << curr_iter << "/" + << slot_vector.list().i_size() << " Add Switch output " << slot + << ": " << output_node->name(); + } + } + return true; + } + + return false; +} + +void VirtualScheduler::AddOutputNodesToReadyQueue( + const NodeDef* node, const Costs::Duration& curr_time) { + auto& node_state = node_map_[node]; + int curr_iter = node_state.num_executed_times; + ++node_state.num_executed_times; + + if (switch_outputs_annotated_) { + // If the graph is annotated with StepStats, reset num_inputs_ready so we + // can schedule the node multiple times. + node_state.num_inputs_ready = 0; + + // For Switch node, get output branch from updated MetaGraphDef. + if (IsSwitch(*node) && + AddSwitchOutputsToReadyQueue(node, curr_iter, curr_time)) + return; + } + + // Increment num_inputs_ready of the output nodes and maybe add to ready + // nodes. + for (const auto& port_num_output_pair : node_state.outputs) { + for (auto* output_node : port_num_output_pair.second) { + auto& output_state = node_map_[output_node]; + output_state.num_inputs_ready++; + // Execute a node as soon as all its inputs are ready. Merge nodes are + // special since they run as soon as one of their inputs becomes + // available. + if (output_state.num_inputs_ready == output_state.inputs.size() || + IsMerge(*output_node)) { + // This output node is now ready. + output_state.time_ready = curr_time; + ready_nodes_->AddNode(output_node); + } + } + } +} + bool VirtualScheduler::MarkCurrNodeExecuted(const Costs& node_costs) { // Update graph_costs_ and per-op costs. graph_costs_ = CombineCosts(graph_costs_, node_costs); @@ -798,6 +885,10 @@ bool VirtualScheduler::MarkCurrNodeExecuted(const Costs& node_costs) { // Node is scheduled when the device is available AND all the inputs are // ready; hence, time_scheduled is time_ready if time_ready > device curr // time. + // TODO(andiryxu): Current node_state result only records the last execution. + // With annotated MetaGraph we can schedule a node for multiple times. + // Refine NodeState structure accordingly, e.g. record time_scheduled in a + // vector. node_state.time_scheduled = std::max(device.GetCurrTime(), node_state.time_ready); // Override device curr time with the time_scheduled. @@ -831,22 +922,8 @@ bool VirtualScheduler::MarkCurrNodeExecuted(const Costs& node_costs) { << ", scheduled: " << node_state.time_scheduled.count() << ", finished: " << node_state.time_finished.count(); - // Increment num_inputs_ready of the output nodes and maybe add to ready nodes - for (const auto& port_num_output_pair : node_state.outputs) { - for (auto* output_node : port_num_output_pair.second) { - auto& output_state = node_map_[output_node]; - output_state.num_inputs_ready++; - // Execute a node as soon as all its inputs are ready. Merge nodes are - // special since they run as soon as one of their inputs becomes - // available. - if (output_state.num_inputs_ready == output_state.inputs.size() || - IsMerge(*output_node)) { - // This output node is now ready. - output_state.time_ready = curr_time; - ready_nodes_->AddNode(output_node); - } - } - } + // Check outputs, add ready nodes to queue. + AddOutputNodesToReadyQueue(node, curr_time); // Increment num_outputs_executed of the input nodes and maybe update memory. for (const auto& input_port : node_state.inputs) { diff --git a/tensorflow/core/grappler/costs/virtual_scheduler.h b/tensorflow/core/grappler/costs/virtual_scheduler.h index f500e4ab51..ffc5e9187c 100644 --- a/tensorflow/core/grappler/costs/virtual_scheduler.h +++ b/tensorflow/core/grappler/costs/virtual_scheduler.h @@ -70,11 +70,15 @@ struct NodeState { // Each output port uses up memory space from time_scheduled to its // time_no_references. + // How many times this node has been executed, e.g. in a while loop. + int num_executed_times; + NodeState() { num_inputs_ready = 0; time_ready = Costs::Duration::max(); time_scheduled = Costs::Duration::max(); time_finished = Costs::Duration::max(); + num_executed_times = 0; // Note that num_outputs_executed and time_no_references are not initialized // here, since we don't know the size (i.e., # outputs for this node). } @@ -330,6 +334,10 @@ class VirtualScheduler { std::map* op_cost); float Round2(const float x) const; bool IsPersistentNode(const NodeDef* node) const; + bool AddSwitchOutputsToReadyQueue(const NodeDef* node, int curr_iter, + const Costs::Duration& curr_time); + void AddOutputNodesToReadyQueue(const NodeDef* node, + const Costs::Duration& curr_time); // Scheduler states: ReadyNodeManager* ready_nodes_; // Not owned. @@ -360,6 +368,10 @@ class VirtualScheduler { bool initialized_; bool track_mem_usage_snapshot_; + // Whether the input graph includes Switch nodes annotated with output slots + // information. + bool switch_outputs_annotated_ = false; + VirtualPlacer placer_; // owned. }; diff --git a/tensorflow/core/grappler/costs/virtual_scheduler_test.cc b/tensorflow/core/grappler/costs/virtual_scheduler_test.cc index c5651715f4..5e044b2037 100644 --- a/tensorflow/core/grappler/costs/virtual_scheduler_test.cc +++ b/tensorflow/core/grappler/costs/virtual_scheduler_test.cc @@ -869,6 +869,439 @@ versions { grappler_item_->fetch = {"while/Exit", "while/Exit_1"}; } + // A simple while loop strengthened with Switch outputs. + void CreateGrapplerItemWithLoopSwitchOutputs() { + // Test graph produced in python using: + /* + with tf.Graph().as_default(): + i0 = tf.constant(0) + m0 = tf.ones([2, 2]) + c = lambda i, m: i < 10 + b = lambda i, m: [i+1, tf.concat([m, m], axis=0)] + r = tf.while_loop( + c, b, loop_vars=[i0, m0], + shape_invariants=[i0.get_shape(), tf.TensorShape([None, 2])]) + with open('/tmp/graph.pbtxt', 'w') as f: + f.write(str(tf.get_default_graph().as_graph_def())) + */ + const string gdef_ascii = R"EOF( +node { + name: "Const" + op: "Const" + attr { + key: "dtype" + value { + type: DT_INT32 + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_INT32 + tensor_shape { + } + int_val: 0 + } + } + } +} +node { + name: "ones" + op: "Const" + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_FLOAT + tensor_shape { + dim { + size: 2 + } + dim { + size: 2 + } + } + float_val: 1.0 + } + } + } +} +node { + name: "while/Enter" + op: "Enter" + input: "Const" + attr { + key: "T" + value { + type: DT_INT32 + } + } + attr { + key: "frame_name" + value { + s: "while/while/" + } + } + attr { + key: "is_constant" + value { + b: false + } + } + attr { + key: "parallel_iterations" + value { + i: 10 + } + } +} +node { + name: "while/Enter_1" + op: "Enter" + input: "ones" + attr { + key: "T" + value { + type: DT_FLOAT + } + } + attr { + key: "frame_name" + value { + s: "while/while/" + } + } + attr { + key: "is_constant" + value { + b: false + } + } + attr { + key: "parallel_iterations" + value { + i: 10 + } + } +} +node { + name: "while/Merge" + op: "Merge" + input: "while/Enter" + input: "while/NextIteration" + attr { + key: "N" + value { + i: 2 + } + } + attr { + key: "T" + value { + type: DT_INT32 + } + } +} +node { + name: "while/Merge_1" + op: "Merge" + input: "while/Enter_1" + input: "while/NextIteration_1" + attr { + key: "N" + value { + i: 2 + } + } + attr { + key: "T" + value { + type: DT_FLOAT + } + } +} +node { + name: "while/Less/y" + op: "Const" + input: "^while/Merge" + attr { + key: "dtype" + value { + type: DT_INT32 + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_INT32 + tensor_shape { + } + int_val: 10 + } + } + } +} +node { + name: "while/Less" + op: "Less" + input: "while/Merge" + input: "while/Less/y" + attr { + key: "T" + value { + type: DT_INT32 + } + } +} +node { + name: "while/LoopCond" + op: "LoopCond" + input: "while/Less" +} +node { + name: "while/Switch" + op: "Switch" + input: "while/Merge" + input: "while/LoopCond" + attr { + key: "T" + value { + type: DT_INT32 + } + } + attr { + key: "_class" + value { + list { + s: "loc:@while/Merge" + } + } + } + attr { + key: "_output_slot_vector" + value { + list { + i: 1 + i: 1 + i: 1 + i: 1 + i: 1 + i: 1 + i: 1 + i: 1 + i: 1 + i: 1 + i: 0 + } + } + } +} +node { + name: "while/Switch_1" + op: "Switch" + input: "while/Merge_1" + input: "while/LoopCond" + attr { + key: "T" + value { + type: DT_FLOAT + } + } + attr { + key: "_class" + value { + list { + s: "loc:@while/Merge_1" + } + } + } + attr { + key: "_output_slot_vector" + value { + list { + i: 1 + i: 1 + i: 1 + i: 1 + i: 1 + i: 1 + i: 1 + i: 1 + i: 1 + i: 1 + i: 0 + } + } + } +} +node { + name: "while/Identity" + op: "Identity" + input: "while/Switch:1" + attr { + key: "T" + value { + type: DT_INT32 + } + } +} +node { + name: "while/Identity_1" + op: "Identity" + input: "while/Switch_1:1" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +} +node { + name: "while/add/y" + op: "Const" + input: "^while/Identity" + attr { + key: "dtype" + value { + type: DT_INT32 + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_INT32 + tensor_shape { + } + int_val: 1 + } + } + } +} +node { + name: "while/add" + op: "Add" + input: "while/Identity" + input: "while/add/y" + attr { + key: "T" + value { + type: DT_INT32 + } + } +} +node { + name: "while/concat/axis" + op: "Const" + input: "^while/Identity" + attr { + key: "dtype" + value { + type: DT_INT32 + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_INT32 + tensor_shape { + } + int_val: 0 + } + } + } +} +node { + name: "while/concat" + op: "ConcatV2" + input: "while/Identity_1" + input: "while/Identity_1" + input: "while/concat/axis" + attr { + key: "N" + value { + i: 2 + } + } + attr { + key: "T" + value { + type: DT_FLOAT + } + } + attr { + key: "Tidx" + value { + type: DT_INT32 + } + } +} +node { + name: "while/NextIteration" + op: "NextIteration" + input: "while/add" + attr { + key: "T" + value { + type: DT_INT32 + } + } +} +node { + name: "while/NextIteration_1" + op: "NextIteration" + input: "while/concat" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +} +node { + name: "while/Exit" + op: "Exit" + input: "while/Switch" + attr { + key: "T" + value { + type: DT_INT32 + } + } +} +node { + name: "while/Exit_1" + op: "Exit" + input: "while/Switch_1" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +} +versions { + producer: 21 +} + )EOF"; + + grappler_item_.reset(new GrapplerItem); + CHECK(protobuf::TextFormat::ParseFromString(gdef_ascii, + &grappler_item_->graph)); + grappler_item_->id = "test_graph"; + grappler_item_->fetch = {"while/Exit", "while/Exit_1"}; + } + + // Create a FusedBatchNorm op that has multiple output ports. void CreateGrapplerItemWithInterDeviceTransfers() { tensorflow::Scope s = tensorflow::Scope::NewRootScope().WithDevice(kCPU0); @@ -1942,6 +2375,89 @@ TEST_F(VirtualSchedulerTest, WhileLoop) { ValidateDependencyChain(start_times, {"while/Switch_1", "while/Exit_1"}); } +TEST_F(VirtualSchedulerTest, WhileLoopWithSwitchOutputs) { + // Init. + CreateGrapplerItemWithLoopSwitchOutputs(); + InitScheduler(); + + // Runs the scheduler. + RunScheduler(""); + + RunMetadata metadata; + scheduler_->Summary(&metadata); + + // Nodes in topological order: + // * const, ones + // * while/Enter, while/Enter_1 + // * while/Merge, while/Merge_1 + // * while/Less/y + // * while/Less + // * while/LoopCond + // * while/Switch, while/Switch_1 + // * while/Identity, while/Identity_1, while/Exit, while/Exit_1 + // * while/add/y, while/concat/axis + // * while/add, while/concat + // * while/NextIteration, while/NextIteration_1 + + int num_next_iteration = 0; + int num_next_iteration_1 = 0; + int num_exit = 0; + int num_exit_1 = 0; + int64 next_iter_start_micro; + int64 next_iter_1_start_micro; + int64 exit_start_micro; + int64 exit_1_start_micro; + + std::unordered_map start_times; + for (const auto& device_step_stats : metadata.step_stats().dev_stats()) { + for (const auto& stats : device_step_stats.node_stats()) { + start_times[stats.node_name()] = stats.all_start_micros(); + if (stats.node_name() == "while/NextIteration") { + ++num_next_iteration; + next_iter_start_micro = stats.all_start_micros(); + } else if (stats.node_name() == "while/NextIteration_1") { + ++num_next_iteration_1; + next_iter_1_start_micro = stats.all_start_micros(); + } else if (stats.node_name() == "while/Exit") { + ++num_exit; + exit_start_micro = stats.all_start_micros(); + } else if (stats.node_name() == "while/Exit_1") { + ++num_exit_1; + exit_1_start_micro = stats.all_start_micros(); + } + } + } + + // Makes sure we run the loop body for ten times. + EXPECT_EQ(10, num_next_iteration); + EXPECT_EQ(10, num_next_iteration_1); + EXPECT_EQ(1, num_exit); + EXPECT_EQ(1, num_exit_1); + + // Start times of while/NextIteration and while/NextIteration_1 should be + // different, so should be those of while/Exit and while/Exit_1. + EXPECT_NE(next_iter_start_micro, next_iter_1_start_micro); + EXPECT_NE(exit_start_micro, exit_1_start_micro); + + // Checks dependency among the nodes; no matter what scheduling mechanism we + // use, the scheduled ops should follow these dependency chains. + // We have to break the loop into two parts, identified by Switch outputs. + ValidateDependencyChain( + start_times, + {"Const", "while/Enter", "while/Merge", "while/Less/y", "while/Less", + "while/LoopCond", "while/Switch", "while/Exit"}); + ValidateDependencyChain(start_times, {"while/Identity", "while/add/y", + "while/add", "while/NextIteration"}); + ValidateDependencyChain( + start_times, {"ones", "while/Enter_1", "while/Merge_1", "while/Switch_1", + "while/Exit_1"}); + ValidateDependencyChain(start_times, {"while/Identity_1", "while/concat", + "while/NextIteration_1"}); + ValidateDependencyChain( + start_times, {"while/Identity", "while/concat/axis", "while/concat"}); + ValidateDependencyChain(start_times, {"while/Identity", "while/add"}); +} + TEST_F(VirtualSchedulerTest, InterDeviceTransfer) { // Init. CreateGrapplerItemWithInterDeviceTransfers(); -- GitLab From 94c604bdd72717b0c18a1648927529b27aeea320 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Tue, 8 Jan 2019 00:15:37 -0800 Subject: [PATCH 464/622] Documentation fix for the unsorted_segment_max op RELNOTES: n/a PiperOrigin-RevId: 228290885 --- .../core/api_def/base_api/api_def_UnsortedSegmentMax.pbtxt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tensorflow/core/api_def/base_api/api_def_UnsortedSegmentMax.pbtxt b/tensorflow/core/api_def/base_api/api_def_UnsortedSegmentMax.pbtxt index 7a60e4387a..ed4a2bd558 100644 --- a/tensorflow/core/api_def/base_api/api_def_UnsortedSegmentMax.pbtxt +++ b/tensorflow/core/api_def/base_api/api_def_UnsortedSegmentMax.pbtxt @@ -3,7 +3,8 @@ op { in_arg { name: "segment_ids" description: < Date: Tue, 8 Jan 2019 01:03:21 -0800 Subject: [PATCH 465/622] compat: Update forward compatibility horizon to 2019-01-08 PiperOrigin-RevId: 228294335 --- tensorflow/python/compat/compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/python/compat/compat.py b/tensorflow/python/compat/compat.py index dbad494e40..5008741d55 100644 --- a/tensorflow/python/compat/compat.py +++ b/tensorflow/python/compat/compat.py @@ -27,7 +27,7 @@ import datetime from tensorflow.python.util import tf_contextlib from tensorflow.python.util.tf_export import tf_export -_FORWARD_COMPATIBILITY_HORIZON = datetime.date(2019, 1, 7) +_FORWARD_COMPATIBILITY_HORIZON = datetime.date(2019, 1, 8) @tf_export("compat.forward_compatible") -- GitLab From 84cf1da2b9552f0af564519e8b200ac8859a17b4 Mon Sep 17 00:00:00 2001 From: James Keeling Date: Tue, 8 Jan 2019 01:34:31 -0800 Subject: [PATCH 466/622] Split tile functor GPU code into multiple files This file was a bottleneck during compilation, often taking many minutes to compile. In local testing this change reduces the wall-clock build time for the tile functor GPU kernels from 217s to 71s. PiperOrigin-RevId: 228296771 --- tensorflow/core/kernels/BUILD | 11 ++++++- ...e_functor_gpu.cu.cc => tile_functor_gpu.h} | 28 +++-------------- .../core/kernels/tile_functor_gpu_bool.cu.cc | 31 +++++++++++++++++++ .../kernels/tile_functor_gpu_complex128.cu.cc | 31 +++++++++++++++++++ .../kernels/tile_functor_gpu_complex64.cu.cc | 31 +++++++++++++++++++ .../kernels/tile_functor_gpu_double.cu.cc | 31 +++++++++++++++++++ .../core/kernels/tile_functor_gpu_float.cu.cc | 31 +++++++++++++++++++ .../core/kernels/tile_functor_gpu_half.cu.cc | 31 +++++++++++++++++++ .../core/kernels/tile_functor_gpu_int16.cu.cc | 31 +++++++++++++++++++ .../core/kernels/tile_functor_gpu_int32.cu.cc | 31 +++++++++++++++++++ .../core/kernels/tile_functor_gpu_int64.cu.cc | 31 +++++++++++++++++++ 11 files changed, 294 insertions(+), 24 deletions(-) rename tensorflow/core/kernels/{tile_functor_gpu.cu.cc => tile_functor_gpu.h} (85%) create mode 100644 tensorflow/core/kernels/tile_functor_gpu_bool.cu.cc create mode 100644 tensorflow/core/kernels/tile_functor_gpu_complex128.cu.cc create mode 100644 tensorflow/core/kernels/tile_functor_gpu_complex64.cu.cc create mode 100644 tensorflow/core/kernels/tile_functor_gpu_double.cu.cc create mode 100644 tensorflow/core/kernels/tile_functor_gpu_float.cu.cc create mode 100644 tensorflow/core/kernels/tile_functor_gpu_half.cu.cc create mode 100644 tensorflow/core/kernels/tile_functor_gpu_int16.cu.cc create mode 100644 tensorflow/core/kernels/tile_functor_gpu_int32.cu.cc create mode 100644 tensorflow/core/kernels/tile_functor_gpu_int64.cu.cc diff --git a/tensorflow/core/kernels/BUILD b/tensorflow/core/kernels/BUILD index 701bda7ef6..fee30fa6c3 100644 --- a/tensorflow/core/kernels/BUILD +++ b/tensorflow/core/kernels/BUILD @@ -1012,7 +1012,16 @@ tf_kernel_library( hdrs = ["tile_functor.h"], gpu_srcs = [ "tile_functor.h", - "tile_functor_gpu.cu.cc", + "tile_functor_gpu.h", + "tile_functor_gpu_bool.cu.cc", + "tile_functor_gpu_complex64.cu.cc", + "tile_functor_gpu_complex128.cu.cc", + "tile_functor_gpu_double.cu.cc", + "tile_functor_gpu_float.cu.cc", + "tile_functor_gpu_half.cu.cc", + "tile_functor_gpu_int16.cu.cc", + "tile_functor_gpu_int32.cu.cc", + "tile_functor_gpu_int64.cu.cc", ], prefix = "tile_ops", deps = ARRAY_DEPS, diff --git a/tensorflow/core/kernels/tile_functor_gpu.cu.cc b/tensorflow/core/kernels/tile_functor_gpu.h similarity index 85% rename from tensorflow/core/kernels/tile_functor_gpu.cu.cc rename to tensorflow/core/kernels/tile_functor_gpu.h index 84a5060fc3..0de32e730e 100644 --- a/tensorflow/core/kernels/tile_functor_gpu.cu.cc +++ b/tensorflow/core/kernels/tile_functor_gpu.h @@ -13,6 +13,9 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ +#ifndef TENSORFLOW_CORE_KERNELS_TILE_FUNCTOR_GPU_H_ +#define TENSORFLOW_CORE_KERNELS_TILE_FUNCTOR_GPU_H_ + #if GOOGLE_CUDA #define EIGEN_USE_GPU @@ -80,28 +83,7 @@ void TileSimple(const Device& d, Tensor* out, const Tensor& in) { } } // end namespace internal - -namespace functor { - -typedef Eigen::GpuDevice GPUDevice; - -// Register functors used for Tile functor. -#define DEFINE_TYPE(T) \ - template struct Tile; \ - template struct Tile; - -TF_CALL_bool(DEFINE_TYPE); -TF_CALL_int16(DEFINE_TYPE); -TF_CALL_int32(DEFINE_TYPE); -TF_CALL_int64(DEFINE_TYPE); -TF_CALL_float(DEFINE_TYPE); -TF_CALL_double(DEFINE_TYPE); -TF_CALL_half(DEFINE_TYPE); -TF_CALL_complex64(DEFINE_TYPE); -TF_CALL_complex128(DEFINE_TYPE); - -#undef DEFINE_TYPE - -} // end namespace functor } // namespace tensorflow #endif // GOOGLE_CUDA + +#endif // TENSORFLOW_CORE_KERNELS_TILE_FUNCTOR_GPU_H_ diff --git a/tensorflow/core/kernels/tile_functor_gpu_bool.cu.cc b/tensorflow/core/kernels/tile_functor_gpu_bool.cu.cc new file mode 100644 index 0000000000..c7a814c7a2 --- /dev/null +++ b/tensorflow/core/kernels/tile_functor_gpu_bool.cu.cc @@ -0,0 +1,31 @@ +/* Copyright 2018 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. +==============================================================================*/ + +#if GOOGLE_CUDA +#define EIGEN_USE_GPU + +#include "tensorflow/core/kernels/tile_functor.h" +#include "tensorflow/core/kernels/tile_functor_gpu.h" + +namespace tensorflow { +namespace functor { +using Eigen::GpuDevice; + +template struct Tile; +template struct Tile; +} // namespace functor +} // namespace tensorflow + +#endif // GOOGLE_CUDA diff --git a/tensorflow/core/kernels/tile_functor_gpu_complex128.cu.cc b/tensorflow/core/kernels/tile_functor_gpu_complex128.cu.cc new file mode 100644 index 0000000000..4dfa4bac1b --- /dev/null +++ b/tensorflow/core/kernels/tile_functor_gpu_complex128.cu.cc @@ -0,0 +1,31 @@ +/* Copyright 2018 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. +==============================================================================*/ + +#if GOOGLE_CUDA +#define EIGEN_USE_GPU + +#include "tensorflow/core/kernels/tile_functor.h" +#include "tensorflow/core/kernels/tile_functor_gpu.h" + +namespace tensorflow { +namespace functor { +using Eigen::GpuDevice; + +template struct Tile; +template struct Tile; +} // namespace functor +} // namespace tensorflow + +#endif // GOOGLE_CUDA diff --git a/tensorflow/core/kernels/tile_functor_gpu_complex64.cu.cc b/tensorflow/core/kernels/tile_functor_gpu_complex64.cu.cc new file mode 100644 index 0000000000..525ede938f --- /dev/null +++ b/tensorflow/core/kernels/tile_functor_gpu_complex64.cu.cc @@ -0,0 +1,31 @@ +/* Copyright 2018 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. +==============================================================================*/ + +#if GOOGLE_CUDA +#define EIGEN_USE_GPU + +#include "tensorflow/core/kernels/tile_functor.h" +#include "tensorflow/core/kernels/tile_functor_gpu.h" + +namespace tensorflow { +namespace functor { +using Eigen::GpuDevice; + +template struct Tile; +template struct Tile; +} // namespace functor +} // namespace tensorflow + +#endif // GOOGLE_CUDA diff --git a/tensorflow/core/kernels/tile_functor_gpu_double.cu.cc b/tensorflow/core/kernels/tile_functor_gpu_double.cu.cc new file mode 100644 index 0000000000..25e024083e --- /dev/null +++ b/tensorflow/core/kernels/tile_functor_gpu_double.cu.cc @@ -0,0 +1,31 @@ +/* Copyright 2018 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. +==============================================================================*/ + +#if GOOGLE_CUDA +#define EIGEN_USE_GPU + +#include "tensorflow/core/kernels/tile_functor.h" +#include "tensorflow/core/kernels/tile_functor_gpu.h" + +namespace tensorflow { +namespace functor { +using Eigen::GpuDevice; + +template struct Tile; +template struct Tile; +} // namespace functor +} // namespace tensorflow + +#endif // GOOGLE_CUDA diff --git a/tensorflow/core/kernels/tile_functor_gpu_float.cu.cc b/tensorflow/core/kernels/tile_functor_gpu_float.cu.cc new file mode 100644 index 0000000000..f0f31370e4 --- /dev/null +++ b/tensorflow/core/kernels/tile_functor_gpu_float.cu.cc @@ -0,0 +1,31 @@ +/* Copyright 2018 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. +==============================================================================*/ + +#if GOOGLE_CUDA +#define EIGEN_USE_GPU + +#include "tensorflow/core/kernels/tile_functor.h" +#include "tensorflow/core/kernels/tile_functor_gpu.h" + +namespace tensorflow { +namespace functor { +using Eigen::GpuDevice; + +template struct Tile; +template struct Tile; +} // namespace functor +} // namespace tensorflow + +#endif // GOOGLE_CUDA diff --git a/tensorflow/core/kernels/tile_functor_gpu_half.cu.cc b/tensorflow/core/kernels/tile_functor_gpu_half.cu.cc new file mode 100644 index 0000000000..6c3810a0bc --- /dev/null +++ b/tensorflow/core/kernels/tile_functor_gpu_half.cu.cc @@ -0,0 +1,31 @@ +/* Copyright 2018 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. +==============================================================================*/ + +#if GOOGLE_CUDA +#define EIGEN_USE_GPU + +#include "tensorflow/core/kernels/tile_functor.h" +#include "tensorflow/core/kernels/tile_functor_gpu.h" + +namespace tensorflow { +namespace functor { +using Eigen::GpuDevice; + +template struct Tile; +template struct Tile; +} // namespace functor +} // namespace tensorflow + +#endif // GOOGLE_CUDA diff --git a/tensorflow/core/kernels/tile_functor_gpu_int16.cu.cc b/tensorflow/core/kernels/tile_functor_gpu_int16.cu.cc new file mode 100644 index 0000000000..2280dcbc82 --- /dev/null +++ b/tensorflow/core/kernels/tile_functor_gpu_int16.cu.cc @@ -0,0 +1,31 @@ +/* Copyright 2018 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. +==============================================================================*/ + +#if GOOGLE_CUDA +#define EIGEN_USE_GPU + +#include "tensorflow/core/kernels/tile_functor.h" +#include "tensorflow/core/kernels/tile_functor_gpu.h" + +namespace tensorflow { +namespace functor { +using Eigen::GpuDevice; + +template struct Tile; +template struct Tile; +} // namespace functor +} // namespace tensorflow + +#endif // GOOGLE_CUDA diff --git a/tensorflow/core/kernels/tile_functor_gpu_int32.cu.cc b/tensorflow/core/kernels/tile_functor_gpu_int32.cu.cc new file mode 100644 index 0000000000..b05403bada --- /dev/null +++ b/tensorflow/core/kernels/tile_functor_gpu_int32.cu.cc @@ -0,0 +1,31 @@ +/* Copyright 2018 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. +==============================================================================*/ + +#if GOOGLE_CUDA +#define EIGEN_USE_GPU + +#include "tensorflow/core/kernels/tile_functor.h" +#include "tensorflow/core/kernels/tile_functor_gpu.h" + +namespace tensorflow { +namespace functor { +using Eigen::GpuDevice; + +template struct Tile; +template struct Tile; +} // namespace functor +} // namespace tensorflow + +#endif // GOOGLE_CUDA diff --git a/tensorflow/core/kernels/tile_functor_gpu_int64.cu.cc b/tensorflow/core/kernels/tile_functor_gpu_int64.cu.cc new file mode 100644 index 0000000000..2d83c6b3a1 --- /dev/null +++ b/tensorflow/core/kernels/tile_functor_gpu_int64.cu.cc @@ -0,0 +1,31 @@ +/* Copyright 2018 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. +==============================================================================*/ + +#if GOOGLE_CUDA +#define EIGEN_USE_GPU + +#include "tensorflow/core/kernels/tile_functor.h" +#include "tensorflow/core/kernels/tile_functor_gpu.h" + +namespace tensorflow { +namespace functor { +using Eigen::GpuDevice; + +template struct Tile; +template struct Tile; +} // namespace functor +} // namespace tensorflow + +#endif // GOOGLE_CUDA -- GitLab From f09a6224bac04fd8b45d72b5babee08212dcf836 Mon Sep 17 00:00:00 2001 From: Gaurav Jain Date: Tue, 8 Jan 2019 02:23:41 -0800 Subject: [PATCH 467/622] Make DebugString() in ResourceBase constant PiperOrigin-RevId: 228300469 --- .../compiler/jit/xla_compilation_cache.cc | 2 +- .../compiler/jit/xla_compilation_cache.h | 2 +- .../compiler/tf2xla/xla_compiler_test.cc | 2 +- tensorflow/compiler/tf2xla/xla_context.cc | 2 +- tensorflow/compiler/tf2xla/xla_context.h | 2 +- .../compiler/xrt/xrt_compilation_cache.cc | 4 +++- .../compiler/xrt/xrt_compilation_cache.h | 2 +- tensorflow/compiler/xrt/xrt_state.h | 2 +- .../contrib/bigtable/kernels/bigtable_lib.h | 4 ++-- .../kernels/stats_accumulator_ops.cc | 2 +- .../decision_tree_ensemble_resource.h | 2 +- .../resources/quantile_stream_resource.h | 2 +- .../kernels/v4/decision-tree-resource.h | 2 +- .../kernels/v4/fertile-stats-resource.h | 2 +- .../contrib/tensorrt/resources/trt_resources.h | 2 +- tensorflow/core/framework/lookup_interface.h | 2 +- tensorflow/core/framework/queue_interface.h | 4 ++-- tensorflow/core/framework/reader_interface.h | 2 +- tensorflow/core/framework/resource_mgr.h | 2 +- tensorflow/core/framework/resource_mgr_test.cc | 8 ++++---- .../core/framework/resource_op_kernel_test.cc | 2 +- tensorflow/core/framework/resource_var.h | 2 +- tensorflow/core/framework/stats_aggregator.h | 2 +- tensorflow/core/kernels/barrier_ops.cc | 2 +- tensorflow/core/kernels/batch_kernels.cc | 6 +++--- .../quantiles/quantile_stream_resource.h | 18 +++++++++--------- .../core/kernels/boosted_trees/resources.cc | 2 +- .../core/kernels/boosted_trees/resources.h | 2 +- .../kernels/conditional_accumulator_base.h | 2 +- tensorflow/core/kernels/conv_ops.h | 2 +- .../core/kernels/data/cache_dataset_ops.cc | 4 +++- .../data/experimental/indexed_dataset_op.cc | 2 +- .../data/experimental/threadpool_dataset_op.cc | 2 +- tensorflow/core/kernels/data/iterator_ops.cc | 2 +- .../kernels/data/multi_device_iterator_ops.cc | 2 +- .../core/kernels/data/shuffle_dataset_op.cc | 2 +- tensorflow/core/kernels/fifo_queue.h | 2 +- tensorflow/core/kernels/map_stage_op.cc | 2 +- tensorflow/core/kernels/meta_support.cc | 2 +- tensorflow/core/kernels/mutex_ops.cc | 4 +++- tensorflow/core/kernels/priority_queue.h | 2 +- .../core/kernels/random_shuffle_queue_op.cc | 2 +- .../core/kernels/sparse_tensors_map_ops.cc | 2 +- tensorflow/core/kernels/stack.cc | 2 +- tensorflow/core/kernels/stage_op.cc | 4 ++-- tensorflow/core/kernels/summary_op.cc | 4 +++- tensorflow/core/kernels/tensor_array.h | 4 ++-- .../core/kernels/tensor_forest/resources.h | 2 +- tensorflow/core/kernels/variable_ops.cc | 4 ++-- tensorflow/core/summary/summary_db_writer.cc | 2 +- tensorflow/core/summary/summary_file_writer.cc | 2 +- tensorflow/python/framework/test_ops.cc | 2 +- 52 files changed, 78 insertions(+), 70 deletions(-) diff --git a/tensorflow/compiler/jit/xla_compilation_cache.cc b/tensorflow/compiler/jit/xla_compilation_cache.cc index d5a1ab0850..bff4cc57ee 100644 --- a/tensorflow/compiler/jit/xla_compilation_cache.cc +++ b/tensorflow/compiler/jit/xla_compilation_cache.cc @@ -62,7 +62,7 @@ XlaCompilationCache::~XlaCompilationCache() { // about? } -string XlaCompilationCache::DebugString() { +string XlaCompilationCache::DebugString() const { return "XLA JIT compilation cache"; } diff --git a/tensorflow/compiler/jit/xla_compilation_cache.h b/tensorflow/compiler/jit/xla_compilation_cache.h index 846d0c963d..02aa8f8839 100644 --- a/tensorflow/compiler/jit/xla_compilation_cache.h +++ b/tensorflow/compiler/jit/xla_compilation_cache.h @@ -88,7 +88,7 @@ class XlaCompilationCache : public ResourceBase { xla::LocalClient* client() const { return client_; } const DeviceType& device_type() const { return device_type_; } - string DebugString() override; + string DebugString() const override; // Describes the types, shapes and any compile-time constant arguments // to a kernel. Key that uniquely identifies a compilation output. diff --git a/tensorflow/compiler/tf2xla/xla_compiler_test.cc b/tensorflow/compiler/tf2xla/xla_compiler_test.cc index 32a4301840..492010f731 100644 --- a/tensorflow/compiler/tf2xla/xla_compiler_test.cc +++ b/tensorflow/compiler/tf2xla/xla_compiler_test.cc @@ -82,7 +82,7 @@ namespace { // compiled kernels. class DummyResourceForTest : public ResourceBase { public: - string DebugString() override { return "dummy"; } + string DebugString() const override { return "dummy"; } void Increment() { ++value_; } int Get() { return value_; } diff --git a/tensorflow/compiler/tf2xla/xla_context.cc b/tensorflow/compiler/tf2xla/xla_context.cc index a69af70503..6139bf3cea 100644 --- a/tensorflow/compiler/tf2xla/xla_context.cc +++ b/tensorflow/compiler/tf2xla/xla_context.cc @@ -61,7 +61,7 @@ void XlaContext::set_args(std::vector args) { XlaContext::XlaContext(XlaCompiler* compiler, xla::XlaBuilder* builder) : compiler_(compiler), builder_(builder) {} -string XlaContext::DebugString() { return "XLA JIT context"; } +string XlaContext::DebugString() const { return "XLA JIT context"; } void XlaContext::SetRetval(int index, const XlaExpression& expression) { if (retvals_.size() <= index) { diff --git a/tensorflow/compiler/tf2xla/xla_context.h b/tensorflow/compiler/tf2xla/xla_context.h index 0767d1faac..eb4ad3fe6a 100644 --- a/tensorflow/compiler/tf2xla/xla_context.h +++ b/tensorflow/compiler/tf2xla/xla_context.h @@ -47,7 +47,7 @@ class XlaContext : public ResourceBase { XlaContext(XlaCompiler* compiler, xla::XlaBuilder* builder); // Virtual method defined by ResourceBase. - string DebugString() override; + string DebugString() const override; XlaCompiler* compiler() const { return compiler_; } diff --git a/tensorflow/compiler/xrt/xrt_compilation_cache.cc b/tensorflow/compiler/xrt/xrt_compilation_cache.cc index d1405eae46..8bf0f28d22 100644 --- a/tensorflow/compiler/xrt/xrt_compilation_cache.cc +++ b/tensorflow/compiler/xrt/xrt_compilation_cache.cc @@ -273,6 +273,8 @@ Status XRTCompilationCache::Lookup( return Status::OK(); } -string XRTCompilationCache::DebugString() { return "XRTCompilationCache"; } +string XRTCompilationCache::DebugString() const { + return "XRTCompilationCache"; +} } // namespace tensorflow diff --git a/tensorflow/compiler/xrt/xrt_compilation_cache.h b/tensorflow/compiler/xrt/xrt_compilation_cache.h index c43d0fc478..7398e847d8 100644 --- a/tensorflow/compiler/xrt/xrt_compilation_cache.h +++ b/tensorflow/compiler/xrt/xrt_compilation_cache.h @@ -118,7 +118,7 @@ class XRTCompilationCache : public ResourceBase { // EntryRef holding the program is returned in entry. Status Lookup(int64 uid, std::unique_ptr* entry); - string DebugString() override; + string DebugString() const override; private: // An entry in the compilation cache. The entry is deleted once it has been diff --git a/tensorflow/compiler/xrt/xrt_state.h b/tensorflow/compiler/xrt/xrt_state.h index 3e3d502412..4aac37737e 100644 --- a/tensorflow/compiler/xrt/xrt_state.h +++ b/tensorflow/compiler/xrt/xrt_state.h @@ -172,7 +172,7 @@ class XRTTupleAllocation : public ResourceBase { // ownership of the device memory is transferred to the result. xla::ShapeTree ToDeviceMemoryTree(bool release); - string DebugString() override { return "XLA allocation handle"; } + string DebugString() const override { return "XLA allocation handle"; } private: // Creates a new handle with (tuple) shape. diff --git a/tensorflow/contrib/bigtable/kernels/bigtable_lib.h b/tensorflow/contrib/bigtable/kernels/bigtable_lib.h index 4652021fec..e3b4535bac 100644 --- a/tensorflow/contrib/bigtable/kernels/bigtable_lib.h +++ b/tensorflow/contrib/bigtable/kernels/bigtable_lib.h @@ -42,7 +42,7 @@ class BigtableClientResource : public ResourceBase { return client_; } - string DebugString() override { + string DebugString() const override { return strings::StrCat("BigtableClientResource(project_id: ", project_id_, ", instance_id: ", instance_id_, ")"); } @@ -67,7 +67,7 @@ class BigtableTableResource : public ResourceBase { ::google::cloud::bigtable::noex::Table& table() { return table_; } - string DebugString() override { + string DebugString() const override { return strings::StrCat( "BigtableTableResource(client: ", client_->DebugString(), ", table: ", table_name_, ")"); diff --git a/tensorflow/contrib/boosted_trees/kernels/stats_accumulator_ops.cc b/tensorflow/contrib/boosted_trees/kernels/stats_accumulator_ops.cc index e446c411a8..6faf696301 100644 --- a/tensorflow/contrib/boosted_trees/kernels/stats_accumulator_ops.cc +++ b/tensorflow/contrib/boosted_trees/kernels/stats_accumulator_ops.cc @@ -96,7 +96,7 @@ class StatsAccumulatorResource : public boosted_trees::StampedResource { TensorShapeUtils::IsScalar(hessian_shape)); } - string DebugString() override { + string DebugString() const override { return strings::StrCat("StatsAccumulatorResource[size=", values_.size(), "]"); } diff --git a/tensorflow/contrib/boosted_trees/resources/decision_tree_ensemble_resource.h b/tensorflow/contrib/boosted_trees/resources/decision_tree_ensemble_resource.h index 94aeb2c7bb..0fe57c0a4e 100644 --- a/tensorflow/contrib/boosted_trees/resources/decision_tree_ensemble_resource.h +++ b/tensorflow/contrib/boosted_trees/resources/decision_tree_ensemble_resource.h @@ -34,7 +34,7 @@ class DecisionTreeEnsembleResource : public StampedResource { protobuf::Arena::CreateMessage< boosted_trees::trees::DecisionTreeEnsembleConfig>(&arena_)) {} - string DebugString() override { + string DebugString() const override { return strings::StrCat("GTFlowDecisionTreeEnsemble[size=", decision_tree_ensemble_->trees_size(), "]"); } diff --git a/tensorflow/contrib/boosted_trees/resources/quantile_stream_resource.h b/tensorflow/contrib/boosted_trees/resources/quantile_stream_resource.h index fdaaae7f47..574e3065e7 100644 --- a/tensorflow/contrib/boosted_trees/resources/quantile_stream_resource.h +++ b/tensorflow/contrib/boosted_trees/resources/quantile_stream_resource.h @@ -43,7 +43,7 @@ class QuantileStreamResource : public StampedResource { set_stamp(stamp_token); } - string DebugString() override { return "QuantileStreamResource"; } + string DebugString() const override { return "QuantileStreamResource"; } tensorflow::mutex* mutex() { return &mu_; } diff --git a/tensorflow/contrib/tensor_forest/kernels/v4/decision-tree-resource.h b/tensorflow/contrib/tensor_forest/kernels/v4/decision-tree-resource.h index d3edb43733..3100a5a0e5 100644 --- a/tensorflow/contrib/tensor_forest/kernels/v4/decision-tree-resource.h +++ b/tensorflow/contrib/tensor_forest/kernels/v4/decision-tree-resource.h @@ -32,7 +32,7 @@ class DecisionTreeResource : public ResourceBase { // Constructor. explicit DecisionTreeResource(const TensorForestParams& params); - string DebugString() override { + string DebugString() const override { return strings::StrCat("DecisionTree[size=", decision_tree_->decision_tree().nodes_size(), "]"); } diff --git a/tensorflow/contrib/tensor_forest/kernels/v4/fertile-stats-resource.h b/tensorflow/contrib/tensor_forest/kernels/v4/fertile-stats-resource.h index eea0be27ca..44f2b3f473 100644 --- a/tensorflow/contrib/tensor_forest/kernels/v4/fertile-stats-resource.h +++ b/tensorflow/contrib/tensor_forest/kernels/v4/fertile-stats-resource.h @@ -40,7 +40,7 @@ class FertileStatsResource : public ResourceBase { model_op_ = LeafModelOperatorFactory::CreateLeafModelOperator(params_); } - string DebugString() override { return "FertileStats"; } + string DebugString() const override { return "FertileStats"; } void ExtractFromProto(const FertileStats& stats); diff --git a/tensorflow/contrib/tensorrt/resources/trt_resources.h b/tensorflow/contrib/tensorrt/resources/trt_resources.h index aac9e5c7bd..8d877b392f 100644 --- a/tensorflow/contrib/tensorrt/resources/trt_resources.h +++ b/tensorflow/contrib/tensorrt/resources/trt_resources.h @@ -48,7 +48,7 @@ class TRTCalibrationResource : public tensorflow::ResourceBase { allocator_.reset(); } - string DebugString() override { + string DebugString() const override { std::stringstream oss; using std::dec; using std::endl; diff --git a/tensorflow/core/framework/lookup_interface.h b/tensorflow/core/framework/lookup_interface.h index d33945fd1b..7e5dbe5632 100644 --- a/tensorflow/core/framework/lookup_interface.h +++ b/tensorflow/core/framework/lookup_interface.h @@ -131,7 +131,7 @@ class LookupInterface : public ResourceBase { // - the default_value tensor shape matches the table's value shape. Status CheckFindArguments(const Tensor& keys, const Tensor& default_value); - string DebugString() override { + string DebugString() const override { return strings::StrCat("A lookup table of size: ", size()); } diff --git a/tensorflow/core/framework/queue_interface.h b/tensorflow/core/framework/queue_interface.h index 4ca4416c5a..9395cce164 100644 --- a/tensorflow/core/framework/queue_interface.h +++ b/tensorflow/core/framework/queue_interface.h @@ -85,11 +85,11 @@ class QueueInterface : public ResourceBase { virtual Status MatchesNodeDef(const NodeDef& node_def) = 0; // Returns the number of elements in the queue. - virtual int32 size() = 0; + virtual int32 size() const = 0; virtual const DataTypeVector& component_dtypes() const = 0; - string DebugString() override { + string DebugString() const override { return strings::StrCat("A Queue of size: ", size()); } diff --git a/tensorflow/core/framework/reader_interface.h b/tensorflow/core/framework/reader_interface.h index f894acbe1d..e47644cb8f 100644 --- a/tensorflow/core/framework/reader_interface.h +++ b/tensorflow/core/framework/reader_interface.h @@ -76,7 +76,7 @@ class ReaderInterface : public ResourceBase { // Note: Must Reset on error. virtual Status RestoreState(const string& state) = 0; - string DebugString() override { return "a reader"; } + string DebugString() const override { return "a reader"; } protected: virtual ~ReaderInterface() {} diff --git a/tensorflow/core/framework/resource_mgr.h b/tensorflow/core/framework/resource_mgr.h index 8a7c25da92..9c381e7d6b 100644 --- a/tensorflow/core/framework/resource_mgr.h +++ b/tensorflow/core/framework/resource_mgr.h @@ -77,7 +77,7 @@ namespace tensorflow { class ResourceBase : public core::RefCounted { public: // Returns a debug string for *this. - virtual string DebugString() = 0; + virtual string DebugString() const = 0; // Returns memory used by this resource. virtual int64 MemoryUsed() const { return 0; } diff --git a/tensorflow/core/framework/resource_mgr_test.cc b/tensorflow/core/framework/resource_mgr_test.cc index 7c7f0af0ce..1c785736e6 100644 --- a/tensorflow/core/framework/resource_mgr_test.cc +++ b/tensorflow/core/framework/resource_mgr_test.cc @@ -32,7 +32,7 @@ class Resource : public ResourceBase { explicit Resource(const string& label) : label_(label) {} ~Resource() override {} - string DebugString() override { return strings::StrCat("R/", label_); } + string DebugString() const override { return strings::StrCat("R/", label_); } private: string label_; @@ -43,7 +43,7 @@ class Other : public ResourceBase { explicit Other(const string& label) : label_(label) {} ~Other() override {} - string DebugString() override { return strings::StrCat("O/", label_); } + string DebugString() const override { return strings::StrCat("O/", label_); } private: string label_; @@ -245,7 +245,7 @@ class StubDevice : public DeviceBase { // Empty stub resource for testing resource handles. class StubResource : public ResourceBase { public: - string DebugString() override { return ""; } + string DebugString() const override { return ""; } int value_{0}; }; @@ -305,7 +305,7 @@ TEST(ResourceHandleTest, DifferentDevice) { // Other stub resource to test type-checking of resource handles. class OtherStubResource : public ResourceBase { public: - string DebugString() override { return ""; } + string DebugString() const override { return ""; } }; TEST(ResourceHandleTest, DifferentType) { diff --git a/tensorflow/core/framework/resource_op_kernel_test.cc b/tensorflow/core/framework/resource_op_kernel_test.cc index c1e503dc57..7a2a87045b 100644 --- a/tensorflow/core/framework/resource_op_kernel_test.cc +++ b/tensorflow/core/framework/resource_op_kernel_test.cc @@ -46,7 +46,7 @@ class StubDevice : public DeviceBase { // Stub resource for testing resource op kernel. class StubResource : public ResourceBase { public: - string DebugString() override { return ""; } + string DebugString() const override { return ""; } int code; }; diff --git a/tensorflow/core/framework/resource_var.h b/tensorflow/core/framework/resource_var.h index f5de5dba88..9387b6c23c 100644 --- a/tensorflow/core/framework/resource_var.h +++ b/tensorflow/core/framework/resource_var.h @@ -67,7 +67,7 @@ class Var : public ResourceBase { mutex* mu() { return &mu_; } Tensor* tensor() { return &tensor_; } - string DebugString() override { + string DebugString() const override { return strings::StrCat(DataTypeString(tensor_.dtype()), "/", tensor_.shape().DebugString()); } diff --git a/tensorflow/core/framework/stats_aggregator.h b/tensorflow/core/framework/stats_aggregator.h index af53ed0a3c..7c960840d7 100644 --- a/tensorflow/core/framework/stats_aggregator.h +++ b/tensorflow/core/framework/stats_aggregator.h @@ -83,7 +83,7 @@ class StatsAggregatorResource : public ResourceBase { return stats_aggregator_; } - string DebugString() { return "StatsAggregatorResource"; } + string DebugString() const override { return "StatsAggregatorResource"; } private: const std::shared_ptr stats_aggregator_; diff --git a/tensorflow/core/kernels/barrier_ops.cc b/tensorflow/core/kernels/barrier_ops.cc index aa91235822..d5bd36b4ce 100644 --- a/tensorflow/core/kernels/barrier_ops.cc +++ b/tensorflow/core/kernels/barrier_ops.cc @@ -300,7 +300,7 @@ class Barrier : public ResourceBase { ready_queue_->Unref(); } - string DebugString() override { return "A barrier"; } + string DebugString() const override { return "A barrier"; } protected: template diff --git a/tensorflow/core/kernels/batch_kernels.cc b/tensorflow/core/kernels/batch_kernels.cc index 35ddda0ec0..5ba461aa9d 100644 --- a/tensorflow/core/kernels/batch_kernels.cc +++ b/tensorflow/core/kernels/batch_kernels.cc @@ -233,7 +233,7 @@ class BatchResource : public ResourceBase { return Status::OK(); } - string DebugString() final { return "BatchResource"; } + string DebugString() const final { return "BatchResource"; } // Ingests data from one invocation of the batch op. The data is enqueued to // be combined with others into a batch, asynchronously. @@ -878,7 +878,7 @@ class UnbatchResource : public ResourceBase { timeout_enforcer_ = nullptr; } - string DebugString() final { return "UnbatchResource"; } + string DebugString() const final { return "UnbatchResource"; } Status Compute(OpKernelContext* context, AsyncOpKernel::DoneCallback done) { const Tensor& data_t = context->input(0); @@ -1094,7 +1094,7 @@ class UnbatchGradResource : public ResourceBase { public: UnbatchGradResource() {} - string DebugString() final { return "UnbatchGradResource"; } + string DebugString() const final { return "UnbatchGradResource"; } // Flushes the information for one batch, given its context and done // callback. Clears all information about it from the available_tensors_. diff --git a/tensorflow/core/kernels/boosted_trees/quantiles/quantile_stream_resource.h b/tensorflow/core/kernels/boosted_trees/quantiles/quantile_stream_resource.h index 1c31724272..965bf2c924 100644 --- a/tensorflow/core/kernels/boosted_trees/quantiles/quantile_stream_resource.h +++ b/tensorflow/core/kernels/boosted_trees/quantiles/quantile_stream_resource.h @@ -37,15 +37,15 @@ class BoostedTreesQuantileStreamResource : public ResourceBase { epsilon_(epsilon), num_streams_(num_streams), max_elements_(max_elements) { - streams_.reserve(num_streams_); - boundaries_.reserve(num_streams_); - for (int64 idx = 0; idx < num_streams; ++idx) { - streams_.push_back(QuantileStream(epsilon, max_elements)); - boundaries_.push_back(std::vector()); - } - } - - string DebugString() override { return "QuantileStreamResource"; } + streams_.reserve(num_streams_); + boundaries_.reserve(num_streams_); + for (int64 idx = 0; idx < num_streams; ++idx) { + streams_.push_back(QuantileStream(epsilon, max_elements)); + boundaries_.push_back(std::vector()); + } + } + + string DebugString() const override { return "QuantileStreamResource"; } tensorflow::mutex* mutex() { return &mu_; } diff --git a/tensorflow/core/kernels/boosted_trees/resources.cc b/tensorflow/core/kernels/boosted_trees/resources.cc index 2798722536..42df484881 100644 --- a/tensorflow/core/kernels/boosted_trees/resources.cc +++ b/tensorflow/core/kernels/boosted_trees/resources.cc @@ -31,7 +31,7 @@ BoostedTreesEnsembleResource::BoostedTreesEnsembleResource() protobuf::Arena::CreateMessage( &arena_)) {} -string BoostedTreesEnsembleResource::DebugString() { +string BoostedTreesEnsembleResource::DebugString() const { return strings::StrCat("TreeEnsemble[size=", tree_ensemble_->trees_size(), "]"); } diff --git a/tensorflow/core/kernels/boosted_trees/resources.h b/tensorflow/core/kernels/boosted_trees/resources.h index f961ed3814..3c7b2df9b0 100644 --- a/tensorflow/core/kernels/boosted_trees/resources.h +++ b/tensorflow/core/kernels/boosted_trees/resources.h @@ -48,7 +48,7 @@ class BoostedTreesEnsembleResource : public StampedResource { public: BoostedTreesEnsembleResource(); - string DebugString() override; + string DebugString() const override; bool InitFromSerialized(const string& serialized, const int64 stamp_token); diff --git a/tensorflow/core/kernels/conditional_accumulator_base.h b/tensorflow/core/kernels/conditional_accumulator_base.h index 4a5ec6f0fb..2618ffbb09 100644 --- a/tensorflow/core/kernels/conditional_accumulator_base.h +++ b/tensorflow/core/kernels/conditional_accumulator_base.h @@ -68,7 +68,7 @@ class ConditionalAccumulatorBase : public ResourceBase { const DataType& dtype() const { return dtype_; } - string DebugString() override { return "A conditional accumulator"; } + string DebugString() const override { return "A conditional accumulator"; } // SetGlobalStep is a modifier method for current_global_step. // It returns an InvalidArgument error if the new_global_step is less than diff --git a/tensorflow/core/kernels/conv_ops.h b/tensorflow/core/kernels/conv_ops.h index 7ec878e0b2..7ccbaf4bf2 100644 --- a/tensorflow/core/kernels/conv_ops.h +++ b/tensorflow/core/kernels/conv_ops.h @@ -63,7 +63,7 @@ struct Im2ColBufferResource : public ResourceBase { // the buffer memory held by this resource. mutex mu; T* data; - string DebugString() { return "Im2ColBufferResource"; } + string DebugString() const { return "Im2ColBufferResource"; } }; // Convolution parameters specified by Op attributes. diff --git a/tensorflow/core/kernels/data/cache_dataset_ops.cc b/tensorflow/core/kernels/data/cache_dataset_ops.cc index f00b38e732..535f49cff8 100644 --- a/tensorflow/core/kernels/data/cache_dataset_ops.cc +++ b/tensorflow/core/kernels/data/cache_dataset_ops.cc @@ -614,7 +614,9 @@ class CacheDatasetOp : public UnaryDatasetOpKernel { public: MemoryCache() = default; - string DebugString() override { return "CacheDataset::MemoryCache"; } + string DebugString() const override { + return "CacheDataset::MemoryCache"; + } // Marks the cache as completed. void Complete() { diff --git a/tensorflow/core/kernels/data/experimental/indexed_dataset_op.cc b/tensorflow/core/kernels/data/experimental/indexed_dataset_op.cc index a07eaebdf9..83eeed7892 100644 --- a/tensorflow/core/kernels/data/experimental/indexed_dataset_op.cc +++ b/tensorflow/core/kernels/data/experimental/indexed_dataset_op.cc @@ -106,7 +106,7 @@ class MaterializedDatasetResource : public ResourceBase { const std::vector& output_shapes) : output_dtypes_(output_dtypes), output_shapes_(output_shapes) {} - string DebugString() override { + string DebugString() const override { return "Materialized IndexedDataset resource"; } diff --git a/tensorflow/core/kernels/data/experimental/threadpool_dataset_op.cc b/tensorflow/core/kernels/data/experimental/threadpool_dataset_op.cc index 8ae45ed5c9..fab3cab7da 100644 --- a/tensorflow/core/kernels/data/experimental/threadpool_dataset_op.cc +++ b/tensorflow/core/kernels/data/experimental/threadpool_dataset_op.cc @@ -51,7 +51,7 @@ class ThreadPoolResource : public ResourceBase { int32 NumThreads() { return thread_pool_.NumThreads(); } - string DebugString() override { return "ThreadPoolResource"; } + string DebugString() const override { return "ThreadPoolResource"; } private: thread::ThreadPool thread_pool_; diff --git a/tensorflow/core/kernels/data/iterator_ops.cc b/tensorflow/core/kernels/data/iterator_ops.cc index 9f5881563b..81e26d35c0 100644 --- a/tensorflow/core/kernels/data/iterator_ops.cc +++ b/tensorflow/core/kernels/data/iterator_ops.cc @@ -231,7 +231,7 @@ class IteratorResource : public ResourceBase { return Status::OK(); } - string DebugString() override { return "Iterator resource"; } + string DebugString() const override { return "Iterator resource"; } const DataTypeVector& output_dtypes() const { return output_dtypes_; } diff --git a/tensorflow/core/kernels/data/multi_device_iterator_ops.cc b/tensorflow/core/kernels/data/multi_device_iterator_ops.cc index ba2125a66e..05528e0ee0 100644 --- a/tensorflow/core/kernels/data/multi_device_iterator_ops.cc +++ b/tensorflow/core/kernels/data/multi_device_iterator_ops.cc @@ -59,7 +59,7 @@ class MultiDeviceIterator : public ResourceBase { DCHECK(lib_ != nullptr); } - string DebugString() override { + string DebugString() const override { return strings::StrCat("MultiDeviceIterator for ", devices_.size(), " devices"); } diff --git a/tensorflow/core/kernels/data/shuffle_dataset_op.cc b/tensorflow/core/kernels/data/shuffle_dataset_op.cc index db0cc6fa4d..4c380c1fa2 100644 --- a/tensorflow/core/kernels/data/shuffle_dataset_op.cc +++ b/tensorflow/core/kernels/data/shuffle_dataset_op.cc @@ -412,7 +412,7 @@ class ShuffleDatasetOp : public ShuffleDatasetOpBase { parent_generator_(seed, seed2), generator_(&parent_generator_) {} - string DebugString() override { + string DebugString() const override { return "ReshufflingDataset::RandomSeedGenerator"; } diff --git a/tensorflow/core/kernels/fifo_queue.h b/tensorflow/core/kernels/fifo_queue.h index 697ee81c39..4d3a7c1971 100644 --- a/tensorflow/core/kernels/fifo_queue.h +++ b/tensorflow/core/kernels/fifo_queue.h @@ -49,7 +49,7 @@ class FIFOQueue : public TypedQueue > { CallbackWithTuple callback) override; Status MatchesNodeDef(const NodeDef& node_def) override; - int32 size() override { + int32 size() const override { mutex_lock lock(mu_); return queues_[0].size(); } diff --git a/tensorflow/core/kernels/map_stage_op.cc b/tensorflow/core/kernels/map_stage_op.cc index dd89597369..27a8696e54 100644 --- a/tensorflow/core/kernels/map_stage_op.cc +++ b/tensorflow/core/kernels/map_stage_op.cc @@ -480,7 +480,7 @@ class StagingMap : public ResourceBase { return map_.size(); } - string DebugString() override { return "StagingMap"; } + string DebugString() const override { return "StagingMap"; } }; template diff --git a/tensorflow/core/kernels/meta_support.cc b/tensorflow/core/kernels/meta_support.cc index 39e60c9fce..44f2997e18 100644 --- a/tensorflow/core/kernels/meta_support.cc +++ b/tensorflow/core/kernels/meta_support.cc @@ -54,7 +54,7 @@ class Scratch : public ResourceBase { uint8_t* buffer() { return scratch_32_aligned_; } - string DebugString() { return "MetaGemmScratchResource"; } + string DebugString() const override { return "MetaGemmScratchResource"; } private: std::unique_ptr scratch_; diff --git a/tensorflow/core/kernels/mutex_ops.cc b/tensorflow/core/kernels/mutex_ops.cc index ddb7a606c1..1603a2aa86 100644 --- a/tensorflow/core/kernels/mutex_ops.cc +++ b/tensorflow/core/kernels/mutex_ops.cc @@ -45,7 +45,9 @@ class Mutex : public ResourceBase { VLOG(2) << "Creating mutex with name " << name << ": " << this; } - string DebugString() override { return strings::StrCat("Mutex ", name_); } + string DebugString() const override { + return strings::StrCat("Mutex ", name_); + } class LockReleaser { public: diff --git a/tensorflow/core/kernels/priority_queue.h b/tensorflow/core/kernels/priority_queue.h index 8e69b5b699..a719c518c3 100644 --- a/tensorflow/core/kernels/priority_queue.h +++ b/tensorflow/core/kernels/priority_queue.h @@ -68,7 +68,7 @@ class PriorityQueue Status MatchesPriorityNodeDefTypes(const NodeDef& node_def) const; Status MatchesPriorityNodeDefShapes(const NodeDef& node_def) const; - int32 size() override { + int32 size() const override { mutex_lock lock(mu_); return queues_[0].size(); } diff --git a/tensorflow/core/kernels/random_shuffle_queue_op.cc b/tensorflow/core/kernels/random_shuffle_queue_op.cc index 31e8ce944f..02b9b022fd 100644 --- a/tensorflow/core/kernels/random_shuffle_queue_op.cc +++ b/tensorflow/core/kernels/random_shuffle_queue_op.cc @@ -59,7 +59,7 @@ class RandomShuffleQueue : public TypedQueue > { CallbackWithTuple callback) override; Status MatchesNodeDef(const NodeDef& node_def) override; - int32 size() override { + int32 size() const override { mutex_lock lock(mu_); return queues_[0].size(); } diff --git a/tensorflow/core/kernels/sparse_tensors_map_ops.cc b/tensorflow/core/kernels/sparse_tensors_map_ops.cc index 74fa3a15f0..939638b370 100644 --- a/tensorflow/core/kernels/sparse_tensors_map_ops.cc +++ b/tensorflow/core/kernels/sparse_tensors_map_ops.cc @@ -43,7 +43,7 @@ class SparseTensorsMap : public ResourceBase { public: explicit SparseTensorsMap(const string& name) : name_(name), counter_(0) {} - string DebugString() override { return "A SparseTensorsMap"; } + string DebugString() const override { return "A SparseTensorsMap"; } typedef struct { PersistentTensor indices; diff --git a/tensorflow/core/kernels/stack.cc b/tensorflow/core/kernels/stack.cc index 5c70a2d62d..2af6b4b814 100644 --- a/tensorflow/core/kernels/stack.cc +++ b/tensorflow/core/kernels/stack.cc @@ -96,7 +96,7 @@ class Stack : public ResourceBase { DataType ElemType() { return elem_type_; } - string DebugString() override { + string DebugString() const override { mutex_lock l(mu_); return strings::StrCat("Stack[", stack_name_, "]"); } diff --git a/tensorflow/core/kernels/stage_op.cc b/tensorflow/core/kernels/stage_op.cc index c91bdc43cf..65174e163c 100644 --- a/tensorflow/core/kernels/stage_op.cc +++ b/tensorflow/core/kernels/stage_op.cc @@ -132,7 +132,7 @@ class Buffer : public ResourceBase { notify_inserters_if_bounded(&lock); } - string DebugString() override { + string DebugString() const override { std::unique_lock lock(mu_); return strings::StrCat("Staging size: ", buf_.size()); } @@ -170,7 +170,7 @@ class Buffer : public ResourceBase { std::size_t capacity_; std::size_t memory_limit_; std::size_t current_bytes_; - std::mutex mu_; + mutable std::mutex mu_; std::condition_variable non_empty_cond_var_; std::condition_variable full_cond_var_; std::deque buf_; diff --git a/tensorflow/core/kernels/summary_op.cc b/tensorflow/core/kernels/summary_op.cc index 1f4e3418f4..1053aa7d53 100644 --- a/tensorflow/core/kernels/summary_op.cc +++ b/tensorflow/core/kernels/summary_op.cc @@ -124,7 +124,9 @@ TF_CALL_REAL_NUMBER_TYPES(REGISTER) struct HistogramResource : public ResourceBase { histogram::ThreadSafeHistogram histogram; - string DebugString() override { return "A histogram summary. Stats ..."; } + string DebugString() const override { + return "A histogram summary. Stats ..."; + } }; class SummaryMergeOp : public OpKernel { diff --git a/tensorflow/core/kernels/tensor_array.h b/tensorflow/core/kernels/tensor_array.h index 384a63e945..507ab459ca 100644 --- a/tensorflow/core/kernels/tensor_array.h +++ b/tensorflow/core/kernels/tensor_array.h @@ -261,7 +261,7 @@ class TensorArray : public ResourceBase { return Status::OK(); } - string DebugString() override { + string DebugString() const override { mutex_lock l(mu_); CHECK(!closed_); return strings::StrCat("TensorArray[", tensors_.size(), "]"); @@ -376,7 +376,7 @@ class TensorArray : public ResourceBase { const DataType dtype_; Tensor handle_; - mutex mu_; + mutable mutex mu_; // Marks that the tensor_array_ has been cleared. bool closed_ GUARDED_BY(mu_); diff --git a/tensorflow/core/kernels/tensor_forest/resources.h b/tensorflow/core/kernels/tensor_forest/resources.h index da258e5017..f0a78f9726 100644 --- a/tensorflow/core/kernels/tensor_forest/resources.h +++ b/tensorflow/core/kernels/tensor_forest/resources.h @@ -34,7 +34,7 @@ class TensorForestTreeResource : public ResourceBase { public: TensorForestTreeResource(); - string DebugString() override { + string DebugString() const override { return strings::StrCat("TensorForestTree[size=", get_size(), "]"); } diff --git a/tensorflow/core/kernels/variable_ops.cc b/tensorflow/core/kernels/variable_ops.cc index eadea18f76..00994bbe8e 100644 --- a/tensorflow/core/kernels/variable_ops.cc +++ b/tensorflow/core/kernels/variable_ops.cc @@ -35,7 +35,7 @@ class LegacyVar : public ResourceBase { mutex* mu() { return &mu_; } Tensor* tensor() { return &tensor_; } - string DebugString() override { + string DebugString() const override { return strings::StrCat(DataTypeString(tensor_.dtype()), "/", tensor_.shape().DebugString()); } @@ -116,7 +116,7 @@ class TemporaryVariableOp : public OpKernel { mutex mu; Tensor val; string name; - string DebugString() override { return name; } + string DebugString() const override { return name; } ~TmpVar() override { VLOG(3) << "TmpVar " << name << " deleted"; } }; diff --git a/tensorflow/core/summary/summary_db_writer.cc b/tensorflow/core/summary/summary_db_writer.cc index 7a5d796821..b203d439cc 100644 --- a/tensorflow/core/summary/summary_db_writer.cc +++ b/tensorflow/core/summary/summary_db_writer.cc @@ -972,7 +972,7 @@ class SummaryDbWriter : public SummaryWriterInterface { return MigrateEvent(std::move(e)); } - string DebugString() override { return "SummaryDbWriter"; } + string DebugString() const override { return "SummaryDbWriter"; } private: Status Write(int64 step, const Tensor& t, const string& tag, diff --git a/tensorflow/core/summary/summary_file_writer.cc b/tensorflow/core/summary/summary_file_writer.cc index 593ccdd684..711a7d3d10 100644 --- a/tensorflow/core/summary/summary_file_writer.cc +++ b/tensorflow/core/summary/summary_file_writer.cc @@ -148,7 +148,7 @@ class SummaryFileWriter : public SummaryWriterInterface { return Status::OK(); } - string DebugString() override { return "SummaryFileWriter"; } + string DebugString() const override { return "SummaryFileWriter"; } private: double GetWallTime() { diff --git a/tensorflow/python/framework/test_ops.cc b/tensorflow/python/framework/test_ops.cc index 99e184a8ac..1d0145f61c 100644 --- a/tensorflow/python/framework/test_ops.cc +++ b/tensorflow/python/framework/test_ops.cc @@ -157,7 +157,7 @@ REGISTER_KERNEL_BUILDER(Name("Old").Device(DEVICE_CPU), OldOp); // Stubbed-out resource to test resource handle ops. class StubResource : public ResourceBase { public: - string DebugString() override { return ""; } + string DebugString() const override { return ""; } }; REGISTER_RESOURCE_HANDLE_KERNEL(StubResource); -- GitLab From 8624a703ebd914e9d91bb7992570b52946fad970 Mon Sep 17 00:00:00 2001 From: Andrew Selle Date: Tue, 8 Jan 2019 02:47:46 -0800 Subject: [PATCH 468/622] Try to make resize bilinear test more deterministic. PiperOrigin-RevId: 228301953 --- tensorflow/lite/kernels/internal/resize_bilinear_test.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tensorflow/lite/kernels/internal/resize_bilinear_test.cc b/tensorflow/lite/kernels/internal/resize_bilinear_test.cc index 1c5ac1992f..4a19b69a7c 100644 --- a/tensorflow/lite/kernels/internal/resize_bilinear_test.cc +++ b/tensorflow/lite/kernels/internal/resize_bilinear_test.cc @@ -76,6 +76,7 @@ void TestOneResizeBilinear(int batch, int depth, int input_width, } TEST(ResizeBilinear, TestResizeBilinear8Bit) { + RandomEngine().seed(38291); const int kTestsToRun = 100 * 1000; for (int i = 0; i < kTestsToRun; i++) { const int batch = ExponentialRandomPositiveInt(0.9f, 3, 20); @@ -91,6 +92,7 @@ TEST(ResizeBilinear, TestResizeBilinear8Bit) { } TEST(ResizeBilinear2x2, TestResizeBilinear8Bit) { + RandomEngine().seed(38291); const int kTestsToRun = 100 * 1000; for (int i = 0; i < kTestsToRun; i++) { const int batch = ExponentialRandomPositiveInt(0.9f, 3, 20); @@ -106,6 +108,7 @@ TEST(ResizeBilinear2x2, TestResizeBilinear8Bit) { } TEST(ResizeBilinear, TestResizeBilinear) { + RandomEngine().seed(38291); const int kTestsToRun = 100 * 1000; for (int i = 0; i < kTestsToRun; i++) { const int batch = ExponentialRandomPositiveInt(0.9f, 3, 20); @@ -121,6 +124,7 @@ TEST(ResizeBilinear, TestResizeBilinear) { } TEST(ResizeBilinear2x2, TestResizeBilinear) { + RandomEngine().seed(38291); const int kTestsToRun = 100 * 1000; for (int i = 0; i < kTestsToRun; i++) { const int batch = ExponentialRandomPositiveInt(0.9f, 3, 20); -- GitLab From 07c6aa1a40f4515506d53262d7da2e8663c049cb Mon Sep 17 00:00:00 2001 From: Andrew Selle Date: Tue, 8 Jan 2019 02:48:31 -0800 Subject: [PATCH 469/622] Use int32_t rather than int32 for compatibility. PiperOrigin-RevId: 228302002 --- tensorflow/lite/kernels/sparse_output_fully_connected_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/lite/kernels/sparse_output_fully_connected_test.cc b/tensorflow/lite/kernels/sparse_output_fully_connected_test.cc index 8152ae6685..7d5fec192c 100644 --- a/tensorflow/lite/kernels/sparse_output_fully_connected_test.cc +++ b/tensorflow/lite/kernels/sparse_output_fully_connected_test.cc @@ -62,7 +62,7 @@ class BaseSparseOutputFullyConnectedOpModel : public SingleOpModel { PopulateTensor(input_, data); } - void SetLookup(const std::vector& f) { PopulateTensor(lookup_, f); } + void SetLookup(const std::vector& f) { PopulateTensor(lookup_, f); } void SetBias(const std::vector& f) { PopulateTensor(bias_, f); } -- GitLab From 81492c074aa134d8756b361557ec2dd9aad3cdc1 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Tue, 8 Jan 2019 02:50:54 -0800 Subject: [PATCH 470/622] Add DistStrat `ReplicaContext.all_reduce`. PiperOrigin-RevId: 228302155 --- .../collective_all_reduce_strategy_test.py | 44 ++++- .../python/mirrored_strategy_multigpu_test.py | 47 ++++- .../python/one_device_strategy_test.py | 22 ++- .../python/parameter_server_strategy_test.py | 38 +++- .../distribute/python/strategy_test_lib.py | 162 ++++++++++++++++++ .../python/distribute/distribute_lib.py | 54 ++++++ ...nsorflow.distribute.-replica-context.pbtxt | 4 + ...nsorflow.distribute.-replica-context.pbtxt | 4 + 8 files changed, 363 insertions(+), 12 deletions(-) diff --git a/tensorflow/contrib/distribute/python/collective_all_reduce_strategy_test.py b/tensorflow/contrib/distribute/python/collective_all_reduce_strategy_test.py index 0fb672dded..4c8c01a216 100644 --- a/tensorflow/contrib/distribute/python/collective_all_reduce_strategy_test.py +++ b/tensorflow/contrib/distribute/python/collective_all_reduce_strategy_test.py @@ -397,9 +397,11 @@ class DistributedCollectiveAllReduceStrategyTestWithChief( self._test_complex_model, self._cluster_spec, num_gpus=num_gpus) -class LocalCollectiveAllReduceStrategy(CollectiveAllReduceStrategyTestBase, - strategy_test_lib.DistributionTestBase, - parameterized.TestCase): +class LocalCollectiveAllReduceStrategy( + CollectiveAllReduceStrategyTestBase, + strategy_test_lib.DistributionTestBase, + strategy_test_lib.TwoDeviceDistributionTestBase, + parameterized.TestCase): def testMinimizeLossGraph(self, num_gpus=2): # Collective ops doesn't support strategy with one device. @@ -428,6 +430,42 @@ class LocalCollectiveAllReduceStrategy(CollectiveAllReduceStrategyTestBase, self._test_input_fn_iterator(None, None, num_gpus, input_fn, expected_values) + def testAllReduceSum(self): + if context.num_gpus() < 2: self.skipTest('Not enough GPUs') + distribution, target, config = self._get_test_object(None, None, num_gpus=2) + with self.cached_session(config=config, target=target): + self._test_all_reduce_sum(distribution) + + def testAllReduceSumGradients(self): + if context.num_gpus() < 2: self.skipTest('Not enough GPUs') + distribution, target, config = self._get_test_object(None, None, num_gpus=2) + with self.cached_session(config=config, target=target): + self._test_all_reduce_sum_gradients(distribution) + + def testAllReduceSumGradientTape(self): + if context.num_gpus() < 2: self.skipTest('Not enough GPUs') + distribution, target, config = self._get_test_object(None, None, num_gpus=2) + with self.cached_session(config=config, target=target): + self._test_all_reduce_sum_gradient_tape(distribution) + + def testAllReduceMean(self): + if context.num_gpus() < 2: self.skipTest('Not enough GPUs') + distribution, target, config = self._get_test_object(None, None, num_gpus=2) + with self.cached_session(config=config, target=target): + self._test_all_reduce_mean(distribution) + + def testAllReduceMeanGradients(self): + if context.num_gpus() < 2: self.skipTest('Not enough GPUs') + distribution, target, config = self._get_test_object(None, None, num_gpus=2) + with self.cached_session(config=config, target=target): + self._test_all_reduce_mean_gradients(distribution) + + def testAllReduceMeanGradientTape(self): + if context.num_gpus() < 2: self.skipTest('Not enough GPUs') + distribution, target, config = self._get_test_object(None, None, num_gpus=2) + with self.cached_session(config=config, target=target): + self._test_all_reduce_mean_gradient_tape(distribution) + if __name__ == '__main__': test.main() diff --git a/tensorflow/contrib/distribute/python/mirrored_strategy_multigpu_test.py b/tensorflow/contrib/distribute/python/mirrored_strategy_multigpu_test.py index 9cbc34412d..9821828b2d 100644 --- a/tensorflow/contrib/distribute/python/mirrored_strategy_multigpu_test.py +++ b/tensorflow/contrib/distribute/python/mirrored_strategy_multigpu_test.py @@ -66,8 +66,10 @@ GPU_TEST = "test_gpu" in sys.argv[0] combinations.core_mirrored_strategy_with_gpu_and_cpu, combinations.core_mirrored_strategy_with_two_gpus], mode=["graph", "eager"])) -class MirroredTwoDeviceDistributionTest(strategy_test_lib.DistributionTestBase, - parameterized.TestCase): +class MirroredTwoDeviceDistributionTest( + strategy_test_lib.DistributionTestBase, + strategy_test_lib.TwoDeviceDistributionTestBase, + parameterized.TestCase): def testMinimizeLoss(self, distribution): if context.executing_eagerly(): @@ -117,6 +119,24 @@ class MirroredTwoDeviceDistributionTest(strategy_test_lib.DistributionTestBase, def testGlobalStepUpdate(self, distribution): self._test_global_step_update(distribution) + def testAllReduceSum(self, distribution): + self._test_all_reduce_sum(distribution) + + def testAllReduceSumGradients(self, distribution): + self._test_all_reduce_sum_gradients(distribution) + + def testAllReduceSumGradientTape(self, distribution): + self._test_all_reduce_sum_gradient_tape(distribution) + + def testAllReduceMean(self, distribution): + self._test_all_reduce_mean(distribution) + + def testAllReduceMeanGradients(self, distribution): + self._test_all_reduce_mean_gradients(distribution) + + def testAllReduceMeanGradientTape(self, distribution): + self._test_all_reduce_mean_gradient_tape(distribution) + def one_device_combinations(): return combinations.combine( @@ -128,25 +148,42 @@ def one_device_combinations(): mode=["graph", "eager"]) +@combinations.generate(one_device_combinations()) class MirroredOneDeviceDistributionTest( strategy_test_lib.DistributionTestBase, + strategy_test_lib.OneDeviceDistributionTestBase, parameterized.TestCase): - @combinations.generate(one_device_combinations()) def testMinimizeLoss(self, distribution): if context.executing_eagerly(): self._test_minimize_loss_eager(distribution) else: self._test_minimize_loss_graph(distribution) - @combinations.generate(one_device_combinations()) def testReplicaId(self, distribution): self._test_replica_id(distribution) - @combinations.generate(one_device_combinations()) def testCallAndMergeExceptions(self, distribution): self._test_call_and_merge_exceptions(distribution) + def testAllReduceSum(self, distribution): + self._test_all_reduce_sum(distribution) + + def testAllReduceSumGradients(self, distribution): + self._test_all_reduce_sum_gradients(distribution) + + def testAllReduceSumGradientTape(self, distribution): + self._test_all_reduce_sum_gradient_tape(distribution) + + def testAllReduceMean(self, distribution): + self._test_all_reduce_mean(distribution) + + def testAllReduceMeanGradients(self, distribution): + self._test_all_reduce_mean_gradients(distribution) + + def testAllReduceMeanGradientTape(self, distribution): + self._test_all_reduce_mean_gradient_tape(distribution) + class MirroredStrategyVariableCreatorStackTest( test.TestCase, parameterized.TestCase): diff --git a/tensorflow/contrib/distribute/python/one_device_strategy_test.py b/tensorflow/contrib/distribute/python/one_device_strategy_test.py index d46cd6f529..2403dc8f12 100644 --- a/tensorflow/contrib/distribute/python/one_device_strategy_test.py +++ b/tensorflow/contrib/distribute/python/one_device_strategy_test.py @@ -25,7 +25,9 @@ from tensorflow.python.eager import test from tensorflow.python.framework import test_util -class OneDeviceStrategyTest(strategy_test_lib.DistributionTestBase): +class OneDeviceStrategyTest( + strategy_test_lib.DistributionTestBase, + strategy_test_lib.OneDeviceDistributionTestBase): def _get_distribution_strategy(self): return one_device_strategy.OneDeviceStrategy("/device:CPU:0") @@ -57,6 +59,24 @@ class OneDeviceStrategyTest(strategy_test_lib.DistributionTestBase): self._test_input_fn_iterator( iterator, d.extended.worker_devices, expected_values) + def testAllReduceSum(self): + self._test_all_reduce_sum(self._get_distribution_strategy()) + + def testAllReduceSumGradients(self): + self._test_all_reduce_sum_gradients(self._get_distribution_strategy()) + + def testAllReduceSumGradientTape(self): + self._test_all_reduce_sum_gradient_tape(self._get_distribution_strategy()) + + def testAllReduceMean(self): + self._test_all_reduce_mean(self._get_distribution_strategy()) + + def testAllReduceMeanGradients(self): + self._test_all_reduce_mean_gradients(self._get_distribution_strategy()) + + def testAllReduceMeanGradientTape(self): + self._test_all_reduce_mean_gradient_tape(self._get_distribution_strategy()) + if __name__ == "__main__": test.main() diff --git a/tensorflow/contrib/distribute/python/parameter_server_strategy_test.py b/tensorflow/contrib/distribute/python/parameter_server_strategy_test.py index d8c6c50db2..7836687e7d 100644 --- a/tensorflow/contrib/distribute/python/parameter_server_strategy_test.py +++ b/tensorflow/contrib/distribute/python/parameter_server_strategy_test.py @@ -603,9 +603,11 @@ class ParameterServerStrategyTestBase( self.assertEqual(expected_value, computed_value) -class ParameterServerStrategyTest(ParameterServerStrategyTestBase, - strategy_test_lib.DistributionTestBase, - parameterized.TestCase): +class ParameterServerStrategyTest( + ParameterServerStrategyTestBase, + strategy_test_lib.DistributionTestBase, + strategy_test_lib.TwoDeviceDistributionTestBase, + parameterized.TestCase): @classmethod def setUpClass(cls): @@ -782,6 +784,36 @@ class ParameterServerStrategyTest(ParameterServerStrategyTestBase, # Verify isolate_session_state self.assertTrue(new_config.isolate_session_state) + def testAllReduceSum(self): + distribution = parameter_server_strategy.ParameterServerStrategy( + num_gpus_per_worker=2) + self._test_all_reduce_sum(distribution) + + def testAllReduceSumGradients(self): + distribution = parameter_server_strategy.ParameterServerStrategy( + num_gpus_per_worker=2) + self._test_all_reduce_sum_gradients(distribution) + + def testAllReduceSumGradientTape(self): + distribution = parameter_server_strategy.ParameterServerStrategy( + num_gpus_per_worker=2) + self._test_all_reduce_sum_gradient_tape(distribution) + + def testAllReduceMean(self): + distribution = parameter_server_strategy.ParameterServerStrategy( + num_gpus_per_worker=2) + self._test_all_reduce_mean(distribution) + + def testAllReduceMeanGradients(self): + distribution = parameter_server_strategy.ParameterServerStrategy( + num_gpus_per_worker=2) + self._test_all_reduce_mean_gradients(distribution) + + def testAllReduceMeanGradientTape(self): + distribution = parameter_server_strategy.ParameterServerStrategy( + num_gpus_per_worker=2) + self._test_all_reduce_mean_gradient_tape(distribution) + class ParameterServerStrategyWithChiefTest(ParameterServerStrategyTestBase, parameterized.TestCase): diff --git a/tensorflow/contrib/distribute/python/strategy_test_lib.py b/tensorflow/contrib/distribute/python/strategy_test_lib.py index 6e5280e356..4fbd630cf7 100644 --- a/tensorflow/contrib/distribute/python/strategy_test_lib.py +++ b/tensorflow/contrib/distribute/python/strategy_test_lib.py @@ -19,6 +19,7 @@ from __future__ import division from __future__ import print_function from tensorflow.core.protobuf import config_pb2 +from tensorflow.python.data.ops import dataset_ops from tensorflow.python.distribute import distribution_strategy_context as ds_context from tensorflow.python.distribute import reduce_util from tensorflow.python.distribute import values @@ -31,6 +32,7 @@ from tensorflow.python.framework import errors from tensorflow.python.framework import ops from tensorflow.python.layers import core from tensorflow.python.ops import array_ops +from tensorflow.python.ops import gradients_impl from tensorflow.python.ops import init_ops from tensorflow.python.ops import variable_scope from tensorflow.python.ops import variables @@ -292,3 +294,163 @@ class DistributionTestBase(test.TestCase): global_step_tensors = strategy.unwrap(value) global_step_values = self.evaluate(global_step_tensors) self.assertEqual((1,) * len(global_step_tensors), global_step_values) + + +class OneDeviceDistributionTestBase(test.TestCase): + """Some tests that should work with any one-device DistributionStrategy.""" + + def _test_all_reduce_sum(self, strategy): + self._test_collective_comms( + strategy, _all_sum, inputs=(4., [42., 43.]), expected=(4., [42., 43.])) + + def _test_all_reduce_sum_gradients(self, strategy): + self._test_collective_comms_gradients( + strategy, _all_sum, inputs=[4.], expected_grads=[4.]) + + def _test_all_reduce_sum_gradient_tape(self, strategy): + self._test_collective_comms_gradient_tape( + strategy, _all_sum, inputs=[4.], expected_grads=[4.]) + + def _test_all_reduce_mean(self, strategy): + self._test_collective_comms( + strategy, _all_mean, inputs=(2., [21., 22.]), expected=(2., [21., 22.])) + + def _test_all_reduce_mean_gradients(self, strategy): + self._test_collective_comms_gradients( + strategy, _all_mean, inputs=[5.], expected_grads=[5.]) + + def _test_all_reduce_mean_gradient_tape(self, strategy): + self._test_collective_comms_gradient_tape( + strategy, _all_mean, inputs=[5.], expected_grads=[5.]) + + def _test_collective_comms(self, strategy, comm_fn, inputs, expected): + inputs = strategy.make_input_fn_iterator( + lambda _: dataset_ops.Dataset.from_tensors(inputs)) + + self.evaluate(inputs.initialize()) + outputs = self.evaluate( + list(map(strategy.unwrap, strategy.experimental_run(comm_fn, inputs)))) + self.assertAllEqual([expected[0]], outputs[0]) + self.assertAllEqual([expected[1]], outputs[1]) + + def _test_collective_comms_gradients( + self, strategy, comm_fn, inputs, expected_grads): + if context.executing_eagerly(): + self.skipTest("`tf.gradients` is not supported with eager execution.") + + def step(c): + x = constant_op.constant(42.) + y = comm_fn(x) * c + return gradients_impl.gradients(y, [x])[0] + + inputs = strategy.make_input_fn_iterator( + lambda _: dataset_ops.Dataset.from_tensors(inputs)) + + self.evaluate(inputs.initialize()) + self.assertAllEqual( + expected_grads, + self.evaluate(strategy.unwrap(strategy.experimental_run(step, inputs)))) + + def _test_collective_comms_gradient_tape( + self, strategy, comm_fn, inputs, expected_grads): + def step(c): + x = constant_op.constant(42.) + with backprop.GradientTape() as tape: + tape.watch(x) + y = comm_fn(x) * c + return tape.gradient(y, x) + + inputs = strategy.make_input_fn_iterator( + lambda _: dataset_ops.Dataset.from_tensors(inputs)) + + self.evaluate(inputs.initialize()) + self.assertAllEqual( + expected_grads, + self.evaluate(strategy.unwrap(strategy.experimental_run(step, inputs)))) + + +class TwoDeviceDistributionTestBase(test.TestCase): + """Some tests that should work with any two-device DistributionStrategy.""" + + def _test_all_reduce_sum(self, strategy): + self._test_collective_comms( + strategy, _all_sum, + inputs=([1., 3.], [[39., 2.], [3., 41.]]), + expected=(4., [42., 43.])) + + def _test_all_reduce_sum_gradients(self, strategy): + self._test_collective_comms_gradients( + strategy, _all_sum, inputs=[1., 3.], expected_grads=[4., 4.]) + + def _test_all_reduce_sum_gradient_tape(self, strategy): + self._test_collective_comms_gradient_tape( + strategy, _all_sum, inputs=[1., 3.], expected_grads=[4., 4.]) + + def _test_all_reduce_mean(self, strategy): + self._test_collective_comms( + strategy, _all_mean, + inputs=([1., 3.], [[39., 2.], [3., 41.]]), + expected=(2., [21., 21.5])) + + def _test_all_reduce_mean_gradients(self, strategy): + self._test_collective_comms_gradients( + strategy, _all_mean, inputs=[1., 3.], expected_grads=[2., 2.]) + + def _test_all_reduce_mean_gradient_tape(self, strategy): + self._test_collective_comms_gradient_tape( + strategy, _all_mean, inputs=[1., 3.], expected_grads=[2., 2.]) + + def _test_collective_comms(self, strategy, comm_fn, inputs, expected): + inputs = strategy.make_input_fn_iterator( + lambda _: dataset_ops.Dataset.from_tensor_slices(inputs)) + + self.evaluate(inputs.initialize()) + outputs = self.evaluate( + list(map(strategy.unwrap, strategy.experimental_run(comm_fn, inputs)))) + self.assertAllEqual([expected[0], expected[0]], outputs[0]) + self.assertAllEqual([expected[1], expected[1]], outputs[1]) + + def _test_collective_comms_gradients( + self, strategy, comm_fn, inputs, expected_grads): + if context.executing_eagerly(): + self.skipTest("`tf.gradients` is not supported with eager execution.") + + def step(c): + x = constant_op.constant(42.) + y = comm_fn(x) * c + return gradients_impl.gradients(y, [x])[0] + + inputs = strategy.make_input_fn_iterator( + lambda _: dataset_ops.Dataset.from_tensor_slices(inputs)) + + self.evaluate(inputs.initialize()) + self.assertAllEqual( + expected_grads, + self.evaluate(strategy.unwrap(strategy.experimental_run(step, inputs)))) + + def _test_collective_comms_gradient_tape( + self, strategy, comm_fn, inputs, expected_grads): + def step(c): + x = constant_op.constant(42.) + with backprop.GradientTape() as tape: + tape.watch(x) + y = comm_fn(x) * c + return tape.gradient(y, x) + + inputs = strategy.make_input_fn_iterator( + lambda _: dataset_ops.Dataset.from_tensor_slices(inputs)) + + self.evaluate(inputs.initialize()) + self.assertAllEqual( + expected_grads, + self.evaluate(strategy.unwrap(strategy.experimental_run(step, inputs)))) + + +def _all_sum(value): + ctx = ds_context.get_replica_context() + return ctx.all_reduce(reduce_util.ReduceOp.SUM, value) + + +def _all_mean(value): + ctx = ds_context.get_replica_context() + return ctx.all_reduce(reduce_util.ReduceOp.MEAN, value) diff --git a/tensorflow/python/distribute/distribute_lib.py b/tensorflow/python/distribute/distribute_lib.py index 76cbdd53d9..4ad8cc00b8 100644 --- a/tensorflow/python/distribute/distribute_lib.py +++ b/tensorflow/python/distribute/distribute_lib.py @@ -33,6 +33,7 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.ops import array_ops from tensorflow.python.ops import control_flow_ops +from tensorflow.python.ops import custom_gradient from tensorflow.python.ops import resource_variable_ops from tensorflow.python.ops import variable_scope from tensorflow.python.ops.losses import losses_impl @@ -1554,6 +1555,50 @@ class ReplicaContext(object): require_replica_context(self) return (device_util.current(),) + def all_reduce(self, reduce_op, value): + """All-reduces the given `Tensor` nest across replicas. + + If `all_reduce` is called in any replica, it must be called in all replicas. + The nested structure and `Tensor` shapes must be identical in all replicas. + + IMPORTANT: The ordering of communications must be identical in all replicas. + + Example with two replicas: + Replica 0 `value`: {'a': 1, 'b': [40, 1]} + Replica 1 `value`: {'a': 3, 'b': [ 2, 98]} + + If `reduce_op` == `SUM`: + Result (on all replicas): {'a': 4, 'b': [42, 99]} + + If `reduce_op` == `MEAN`: + Result (on all replicas): {'a': 2, 'b': [21, 49.5]} + + Args: + reduce_op: Reduction type, an instance of `tf.distribute.ReduceOp` enum. + value: The nested structure of `Tensor`s to all-reduced. + The structure must be compatible with `tf.nest`. + + Returns: + A `Tensor` nest with the reduced `value`s from each replica. + """ + def batch_all_reduce(strategy, *value_flat): + return strategy.extended.batch_reduce_to( + reduce_op, [(v, _batch_reduce_destination(v)) for v in value_flat]) + + if reduce_op in [reduce_util.ReduceOp.SUM, reduce_util.ReduceOp.MEAN]: + # TODO(cjfj): Work out why `batch_reduce` doesn't return the correct grad. + @custom_gradient.custom_gradient + def grad_wrapper(*xs): + ys = self.merge_call(batch_all_reduce, args=xs) + # The gradient of an all-sum is itself an all-sum (all-mean, likewise). + return ys, lambda *dy_s: self.all_reduce(reduce_op, dy_s) + return nest.pack_sequence_as(value, grad_wrapper(*nest.flatten(value))) + else: + # TODO(cjfj): Implement gradients for other reductions. + reduced = nest.pack_sequence_as( + value, self.merge_call(batch_all_reduce, args=nest.flatten(value))) + return nest.map_structure(array_ops.prevent_gradient, reduced) + # TODO(josh11b): Implement `start_all_reduce(method, t)` for efficient # all-reduce. It would return a function returning the result of reducing `t` # across all replicas. The caller would wait to call this function until they @@ -1564,6 +1609,15 @@ class ReplicaContext(object): # to that point that the first result is needed. Most likely this can be # implemented in terms of `merge_call()` and `batch_reduce_to()`. + +def _batch_reduce_destination(x): + """Returns the destinations for batch all-reduce.""" + if isinstance(x, ops.Tensor): # One device strategies. + return x.device + else: + return x + + # ------------------------------------------------------------------------------ diff --git a/tensorflow/tools/api/golden/v1/tensorflow.distribute.-replica-context.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.distribute.-replica-context.pbtxt index df707e8920..22f8160c96 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.distribute.-replica-context.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.distribute.-replica-context.pbtxt @@ -26,6 +26,10 @@ tf_class { name: "__init__" argspec: "args=[\'self\', \'strategy\', \'replica_id_in_sync_group\'], varargs=None, keywords=None, defaults=None" } + member_method { + name: "all_reduce" + argspec: "args=[\'self\', \'reduce_op\', \'value\'], varargs=None, keywords=None, defaults=None" + } member_method { name: "merge_call" argspec: "args=[\'self\', \'merge_fn\', \'args\', \'kwargs\'], varargs=None, keywords=None, defaults=[\'()\', \'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.distribute.-replica-context.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.distribute.-replica-context.pbtxt index df707e8920..22f8160c96 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.distribute.-replica-context.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.distribute.-replica-context.pbtxt @@ -26,6 +26,10 @@ tf_class { name: "__init__" argspec: "args=[\'self\', \'strategy\', \'replica_id_in_sync_group\'], varargs=None, keywords=None, defaults=None" } + member_method { + name: "all_reduce" + argspec: "args=[\'self\', \'reduce_op\', \'value\'], varargs=None, keywords=None, defaults=None" + } member_method { name: "merge_call" argspec: "args=[\'self\', \'merge_fn\', \'args\', \'kwargs\'], varargs=None, keywords=None, defaults=[\'()\', \'None\'], " -- GitLab From 0ef4b190443a2f7b3b2330f053df5503af6b0191 Mon Sep 17 00:00:00 2001 From: James Keeling Date: Tue, 8 Jan 2019 02:53:00 -0800 Subject: [PATCH 471/622] Split topk GPU code into multiple files This file was a bottleneck during compilation, often taking many minutes to compile. In local testing this change reduces the wall-clock build time for the topk GPU kernels from 155s to 48s. PiperOrigin-RevId: 228302319 --- tensorflow/core/kernels/BUILD | 16 ++++++++++- .../{topk_op_gpu.cu.cc => topk_op_gpu.h} | 12 +++----- .../core/kernels/topk_op_gpu_double.cu.cc | 28 +++++++++++++++++++ .../core/kernels/topk_op_gpu_float.cu.cc | 28 +++++++++++++++++++ .../core/kernels/topk_op_gpu_half.cu.cc | 28 +++++++++++++++++++ .../core/kernels/topk_op_gpu_int16.cu.cc | 28 +++++++++++++++++++ .../core/kernels/topk_op_gpu_int32.cu.cc | 28 +++++++++++++++++++ .../core/kernels/topk_op_gpu_int64.cu.cc | 28 +++++++++++++++++++ .../core/kernels/topk_op_gpu_int8.cu.cc | 28 +++++++++++++++++++ .../core/kernels/topk_op_gpu_uint16.cu.cc | 28 +++++++++++++++++++ .../core/kernels/topk_op_gpu_uint8.cu.cc | 28 +++++++++++++++++++ 11 files changed, 271 insertions(+), 9 deletions(-) rename tensorflow/core/kernels/{topk_op_gpu.cu.cc => topk_op_gpu.h} (98%) create mode 100644 tensorflow/core/kernels/topk_op_gpu_double.cu.cc create mode 100644 tensorflow/core/kernels/topk_op_gpu_float.cu.cc create mode 100644 tensorflow/core/kernels/topk_op_gpu_half.cu.cc create mode 100644 tensorflow/core/kernels/topk_op_gpu_int16.cu.cc create mode 100644 tensorflow/core/kernels/topk_op_gpu_int32.cu.cc create mode 100644 tensorflow/core/kernels/topk_op_gpu_int64.cu.cc create mode 100644 tensorflow/core/kernels/topk_op_gpu_int8.cu.cc create mode 100644 tensorflow/core/kernels/topk_op_gpu_uint16.cu.cc create mode 100644 tensorflow/core/kernels/topk_op_gpu_uint8.cu.cc diff --git a/tensorflow/core/kernels/BUILD b/tensorflow/core/kernels/BUILD index fee30fa6c3..3df63d34e3 100644 --- a/tensorflow/core/kernels/BUILD +++ b/tensorflow/core/kernels/BUILD @@ -3862,7 +3862,21 @@ tf_kernel_library( tf_kernel_library( name = "topk_op", - prefix = "topk_op", + srcs = ["topk_op.cc"], + hdrs = ["topk_op.h"], + gpu_srcs = [ + "topk_op.h", + "topk_op_gpu.h", + "topk_op_gpu_double.cu.cc", + "topk_op_gpu_float.cu.cc", + "topk_op_gpu_half.cu.cc", + "topk_op_gpu_int64.cu.cc", + "topk_op_gpu_int32.cu.cc", + "topk_op_gpu_int16.cu.cc", + "topk_op_gpu_uint16.cu.cc", + "topk_op_gpu_int8.cu.cc", + "topk_op_gpu_uint8.cu.cc", + ], deps = NN_DEPS + if_cuda(["@cub_archive//:cub"]), ) diff --git a/tensorflow/core/kernels/topk_op_gpu.cu.cc b/tensorflow/core/kernels/topk_op_gpu.h similarity index 98% rename from tensorflow/core/kernels/topk_op_gpu.cu.cc rename to tensorflow/core/kernels/topk_op_gpu.h index 2fbe1fe7cb..6f3bec20f6 100644 --- a/tensorflow/core/kernels/topk_op_gpu.cu.cc +++ b/tensorflow/core/kernels/topk_op_gpu.h @@ -12,6 +12,8 @@ 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_TOPK_OP_GPU_H_ +#define TENSORFLOW_CORE_KERNELS_TOPK_OP_GPU_H_ #if GOOGLE_CUDA @@ -561,14 +563,8 @@ struct TopKFunctor { }; } // end namespace functor - -#define INSTANTIATE_TEMPLATE(type) \ - template struct functor::TopKFunctor; - -TF_CALL_GPU_NUMBER_TYPES(INSTANTIATE_TEMPLATE); -TF_CALL_INTEGRAL_TYPES(INSTANTIATE_TEMPLATE); -#undef INSTANTIATE_TEMPLATE - } // namespace tensorflow #endif // GOOGLE_CUDA + +#endif // TENSORFLOW_CORE_KERNELS_TOPK_OP_GPU_H_ diff --git a/tensorflow/core/kernels/topk_op_gpu_double.cu.cc b/tensorflow/core/kernels/topk_op_gpu_double.cu.cc new file mode 100644 index 0000000000..8a5a7e71b1 --- /dev/null +++ b/tensorflow/core/kernels/topk_op_gpu_double.cu.cc @@ -0,0 +1,28 @@ +/* Copyright 2018 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. +==============================================================================*/ + +#if GOOGLE_CUDA +#define EIGEN_USE_GPU + +#include "tensorflow/core/kernels/topk_op.h" +#include "tensorflow/core/kernels/topk_op_gpu.h" + +namespace tensorflow { +using Eigen::GpuDevice; + +template struct functor::TopKFunctor; +} // namespace tensorflow + +#endif // GOOGLE_CUDA diff --git a/tensorflow/core/kernels/topk_op_gpu_float.cu.cc b/tensorflow/core/kernels/topk_op_gpu_float.cu.cc new file mode 100644 index 0000000000..0b69396bb1 --- /dev/null +++ b/tensorflow/core/kernels/topk_op_gpu_float.cu.cc @@ -0,0 +1,28 @@ +/* Copyright 2018 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. +==============================================================================*/ + +#if GOOGLE_CUDA +#define EIGEN_USE_GPU + +#include "tensorflow/core/kernels/topk_op.h" +#include "tensorflow/core/kernels/topk_op_gpu.h" + +namespace tensorflow { +using Eigen::GpuDevice; + +template struct functor::TopKFunctor; +} // namespace tensorflow + +#endif // GOOGLE_CUDA diff --git a/tensorflow/core/kernels/topk_op_gpu_half.cu.cc b/tensorflow/core/kernels/topk_op_gpu_half.cu.cc new file mode 100644 index 0000000000..e53586aeca --- /dev/null +++ b/tensorflow/core/kernels/topk_op_gpu_half.cu.cc @@ -0,0 +1,28 @@ +/* Copyright 2018 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. +==============================================================================*/ + +#if GOOGLE_CUDA +#define EIGEN_USE_GPU + +#include "tensorflow/core/kernels/topk_op.h" +#include "tensorflow/core/kernels/topk_op_gpu.h" + +namespace tensorflow { +using Eigen::GpuDevice; + +template struct functor::TopKFunctor; +} // namespace tensorflow + +#endif // GOOGLE_CUDA diff --git a/tensorflow/core/kernels/topk_op_gpu_int16.cu.cc b/tensorflow/core/kernels/topk_op_gpu_int16.cu.cc new file mode 100644 index 0000000000..5bd310523c --- /dev/null +++ b/tensorflow/core/kernels/topk_op_gpu_int16.cu.cc @@ -0,0 +1,28 @@ +/* Copyright 2018 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. +==============================================================================*/ + +#if GOOGLE_CUDA +#define EIGEN_USE_GPU + +#include "tensorflow/core/kernels/topk_op.h" +#include "tensorflow/core/kernels/topk_op_gpu.h" + +namespace tensorflow { +using Eigen::GpuDevice; + +template struct functor::TopKFunctor; +} // namespace tensorflow + +#endif // GOOGLE_CUDA diff --git a/tensorflow/core/kernels/topk_op_gpu_int32.cu.cc b/tensorflow/core/kernels/topk_op_gpu_int32.cu.cc new file mode 100644 index 0000000000..55b393a0c0 --- /dev/null +++ b/tensorflow/core/kernels/topk_op_gpu_int32.cu.cc @@ -0,0 +1,28 @@ +/* Copyright 2018 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. +==============================================================================*/ + +#if GOOGLE_CUDA +#define EIGEN_USE_GPU + +#include "tensorflow/core/kernels/topk_op.h" +#include "tensorflow/core/kernels/topk_op_gpu.h" + +namespace tensorflow { +using Eigen::GpuDevice; + +template struct functor::TopKFunctor; +} // namespace tensorflow + +#endif // GOOGLE_CUDA diff --git a/tensorflow/core/kernels/topk_op_gpu_int64.cu.cc b/tensorflow/core/kernels/topk_op_gpu_int64.cu.cc new file mode 100644 index 0000000000..3e4a775056 --- /dev/null +++ b/tensorflow/core/kernels/topk_op_gpu_int64.cu.cc @@ -0,0 +1,28 @@ +/* Copyright 2018 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. +==============================================================================*/ + +#if GOOGLE_CUDA +#define EIGEN_USE_GPU + +#include "tensorflow/core/kernels/topk_op.h" +#include "tensorflow/core/kernels/topk_op_gpu.h" + +namespace tensorflow { +using Eigen::GpuDevice; + +template struct functor::TopKFunctor; +} // namespace tensorflow + +#endif // GOOGLE_CUDA diff --git a/tensorflow/core/kernels/topk_op_gpu_int8.cu.cc b/tensorflow/core/kernels/topk_op_gpu_int8.cu.cc new file mode 100644 index 0000000000..ac73cd170b --- /dev/null +++ b/tensorflow/core/kernels/topk_op_gpu_int8.cu.cc @@ -0,0 +1,28 @@ +/* Copyright 2018 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. +==============================================================================*/ + +#if GOOGLE_CUDA +#define EIGEN_USE_GPU + +#include "tensorflow/core/kernels/topk_op.h" +#include "tensorflow/core/kernels/topk_op_gpu.h" + +namespace tensorflow { +using Eigen::GpuDevice; + +template struct functor::TopKFunctor; +} // namespace tensorflow + +#endif // GOOGLE_CUDA diff --git a/tensorflow/core/kernels/topk_op_gpu_uint16.cu.cc b/tensorflow/core/kernels/topk_op_gpu_uint16.cu.cc new file mode 100644 index 0000000000..8d5f8ceb06 --- /dev/null +++ b/tensorflow/core/kernels/topk_op_gpu_uint16.cu.cc @@ -0,0 +1,28 @@ +/* Copyright 2018 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. +==============================================================================*/ + +#if GOOGLE_CUDA +#define EIGEN_USE_GPU + +#include "tensorflow/core/kernels/topk_op.h" +#include "tensorflow/core/kernels/topk_op_gpu.h" + +namespace tensorflow { +using Eigen::GpuDevice; + +template struct functor::TopKFunctor; +} // namespace tensorflow + +#endif // GOOGLE_CUDA diff --git a/tensorflow/core/kernels/topk_op_gpu_uint8.cu.cc b/tensorflow/core/kernels/topk_op_gpu_uint8.cu.cc new file mode 100644 index 0000000000..fc1a8a2c8c --- /dev/null +++ b/tensorflow/core/kernels/topk_op_gpu_uint8.cu.cc @@ -0,0 +1,28 @@ +/* Copyright 2018 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. +==============================================================================*/ + +#if GOOGLE_CUDA +#define EIGEN_USE_GPU + +#include "tensorflow/core/kernels/topk_op.h" +#include "tensorflow/core/kernels/topk_op_gpu.h" + +namespace tensorflow { +using Eigen::GpuDevice; + +template struct functor::TopKFunctor; +} // namespace tensorflow + +#endif // GOOGLE_CUDA -- GitLab From 3436665db2438dd339a0edddd0d0a3d49b5b2424 Mon Sep 17 00:00:00 2001 From: James Keeling Date: Tue, 8 Jan 2019 03:28:39 -0800 Subject: [PATCH 472/622] Split scan ops GPU code into multiple files This file was a bottleneck during compilation, often taking many minutes to compile. In local testing this change reduces the wall-clock build time for the scan ops GPU kernels from 107s to 96s. PiperOrigin-RevId: 228304727 --- tensorflow/core/kernels/BUILD | 10 +++++- .../{scan_ops_gpu.cu.cc => scan_ops_gpu.h} | 16 +++------- .../core/kernels/scan_ops_gpu_double.cu.cc | 31 +++++++++++++++++++ .../core/kernels/scan_ops_gpu_float.cu.cc | 31 +++++++++++++++++++ .../core/kernels/scan_ops_gpu_half.cu.cc | 31 +++++++++++++++++++ 5 files changed, 107 insertions(+), 12 deletions(-) rename tensorflow/core/kernels/{scan_ops_gpu.cu.cc => scan_ops_gpu.h} (97%) create mode 100644 tensorflow/core/kernels/scan_ops_gpu_double.cu.cc create mode 100644 tensorflow/core/kernels/scan_ops_gpu_float.cu.cc create mode 100644 tensorflow/core/kernels/scan_ops_gpu_half.cu.cc diff --git a/tensorflow/core/kernels/BUILD b/tensorflow/core/kernels/BUILD index 3df63d34e3..b934c64bfc 100644 --- a/tensorflow/core/kernels/BUILD +++ b/tensorflow/core/kernels/BUILD @@ -3279,7 +3279,15 @@ tf_kernel_library( tf_kernel_library( name = "scan_ops", - prefix = "scan_ops", + srcs = ["scan_ops.cc"], + hdrs = ["scan_ops.h"], + gpu_srcs = [ + "scan_ops.h", + "scan_ops_gpu.h", + "scan_ops_gpu_double.cu.cc", + "scan_ops_gpu_float.cu.cc", + "scan_ops_gpu_half.cu.cc", + ], deps = MATH_DEPS + if_cuda(["@cub_archive//:cub"]), ) diff --git a/tensorflow/core/kernels/scan_ops_gpu.cu.cc b/tensorflow/core/kernels/scan_ops_gpu.h similarity index 97% rename from tensorflow/core/kernels/scan_ops_gpu.cu.cc rename to tensorflow/core/kernels/scan_ops_gpu.h index ed66c02dc5..976b221540 100644 --- a/tensorflow/core/kernels/scan_ops_gpu.cu.cc +++ b/tensorflow/core/kernels/scan_ops_gpu.h @@ -13,6 +13,9 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ +#ifndef TENSORFLOW_CORE_KERNELS_SCAN_OPS_GPU_H_ +#define TENSORFLOW_CORE_KERNELS_SCAN_OPS_GPU_H_ + #if GOOGLE_CUDA #define EIGEN_USE_GPU @@ -290,17 +293,8 @@ struct Scan, T> { }; } // namespace functor - -#define DEFINE(REDUCER, T) template struct functor::Scan; - -#define DEFINE_FOR_ALL_REDUCERS(T) \ - DEFINE(Eigen::internal::SumReducer, T); \ - DEFINE(Eigen::internal::ProdReducer, T); - -TF_CALL_GPU_NUMBER_TYPES(DEFINE_FOR_ALL_REDUCERS); -#undef DEFINE_FOR_ALL_REDUCERS -#undef DEFINE - } // end namespace tensorflow #endif // GOOGLE_CUDA + +#endif // TENSORFLOW_CORE_KERNELS_SCAN_OPS_GPU_H_ diff --git a/tensorflow/core/kernels/scan_ops_gpu_double.cu.cc b/tensorflow/core/kernels/scan_ops_gpu_double.cu.cc new file mode 100644 index 0000000000..adce37e473 --- /dev/null +++ b/tensorflow/core/kernels/scan_ops_gpu_double.cu.cc @@ -0,0 +1,31 @@ +/* Copyright 2018 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. +==============================================================================*/ + +#if GOOGLE_CUDA + +#define EIGEN_USE_GPU + +#include "tensorflow/core/kernels/scan_ops.h" +#include "tensorflow/core/kernels/scan_ops_gpu.h" + +namespace tensorflow { +using Eigen::GpuDevice; +template struct functor::Scan, + double>; +template struct functor::Scan, + double>; +} // namespace tensorflow + +#endif // GOOGLE_CUDA diff --git a/tensorflow/core/kernels/scan_ops_gpu_float.cu.cc b/tensorflow/core/kernels/scan_ops_gpu_float.cu.cc new file mode 100644 index 0000000000..b72415822d --- /dev/null +++ b/tensorflow/core/kernels/scan_ops_gpu_float.cu.cc @@ -0,0 +1,31 @@ +/* Copyright 2018 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. +==============================================================================*/ + +#if GOOGLE_CUDA + +#define EIGEN_USE_GPU + +#include "tensorflow/core/kernels/scan_ops.h" +#include "tensorflow/core/kernels/scan_ops_gpu.h" + +namespace tensorflow { +using Eigen::GpuDevice; +template struct functor::Scan, + float>; +template struct functor::Scan, + float>; +} // namespace tensorflow + +#endif // GOOGLE_CUDA diff --git a/tensorflow/core/kernels/scan_ops_gpu_half.cu.cc b/tensorflow/core/kernels/scan_ops_gpu_half.cu.cc new file mode 100644 index 0000000000..f9fb528be9 --- /dev/null +++ b/tensorflow/core/kernels/scan_ops_gpu_half.cu.cc @@ -0,0 +1,31 @@ +/* Copyright 2018 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. +==============================================================================*/ + +#if GOOGLE_CUDA + +#define EIGEN_USE_GPU + +#include "tensorflow/core/kernels/scan_ops.h" +#include "tensorflow/core/kernels/scan_ops_gpu.h" + +namespace tensorflow { +using Eigen::GpuDevice; +template struct functor::Scan< + GpuDevice, Eigen::internal::SumReducer, Eigen::half>; +template struct functor::Scan< + GpuDevice, Eigen::internal::ProdReducer, Eigen::half>; +} // namespace tensorflow + +#endif // GOOGLE_CUDA -- GitLab From 137c142341c9f10a1e105403fbfb1123a1802eb9 Mon Sep 17 00:00:00 2001 From: Andrew Selle Date: Tue, 8 Jan 2019 03:57:48 -0800 Subject: [PATCH 473/622] Make toco_port trivial initialize-able for test cases. Problem is toco_port indirects a way of initializing google. One impl has a static way of finding out if it is initialized (internal). One impl has a non-static way to find out. In the non-static way, test cases won't work since we initialize the tensorflow google emulation elsewhere (gtest override) PiperOrigin-RevId: 228306710 --- tensorflow/lite/toco/BUILD | 2 ++ tensorflow/lite/toco/import_tensorflow_test.cc | 2 ++ tensorflow/lite/toco/toco_convert_test.cc | 2 ++ tensorflow/lite/toco/toco_port.cc | 7 +++++++ tensorflow/lite/toco/toco_port.h | 4 ++++ tensorflow/lite/toco/toco_port_test.cc | 1 + tensorflow/lite/toco/tooling_util_test.cc | 2 ++ 7 files changed, 20 insertions(+) diff --git a/tensorflow/lite/toco/BUILD b/tensorflow/lite/toco/BUILD index d77234c80a..40bceedd6a 100644 --- a/tensorflow/lite/toco/BUILD +++ b/tensorflow/lite/toco/BUILD @@ -342,6 +342,7 @@ tf_cc_test( name = "import_tensorflow_test", srcs = ["import_tensorflow_test.cc"], deps = [ + ":toco_port", ":toco_tooling", "//tensorflow/core:framework", "//tensorflow/core:graph", @@ -386,6 +387,7 @@ tf_cc_test( srcs = ["tooling_util_test.cc"], deps = [ ":model", + ":toco_port", ":tooling_util", "//tensorflow/core:lib", "//tensorflow/lite/testing:util", diff --git a/tensorflow/lite/toco/import_tensorflow_test.cc b/tensorflow/lite/toco/import_tensorflow_test.cc index 70382f546b..de7f4cdb7e 100644 --- a/tensorflow/lite/toco/import_tensorflow_test.cc +++ b/tensorflow/lite/toco/import_tensorflow_test.cc @@ -13,6 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ #include "tensorflow/lite/toco/import_tensorflow.h" +#include "tensorflow/lite/toco/toco_port.h" #include #include @@ -569,5 +570,6 @@ TEST(ImportTest, UnsupportedOpWithMultipleOutputs) { int main(int argc, char** argv) { ::tflite::LogToStderr(); ::testing::InitGoogleTest(&argc, argv); + ::toco::port::InitGoogleWasDoneElsewhere(); return RUN_ALL_TESTS(); } diff --git a/tensorflow/lite/toco/toco_convert_test.cc b/tensorflow/lite/toco/toco_convert_test.cc index 3730c53ae1..739b924607 100644 --- a/tensorflow/lite/toco/toco_convert_test.cc +++ b/tensorflow/lite/toco/toco_convert_test.cc @@ -16,6 +16,7 @@ limitations under the License. #include #include #include "tensorflow/lite/testing/util.h" +#include "tensorflow/lite/toco/toco_port.h" namespace toco { namespace { @@ -176,5 +177,6 @@ TEST(TocoTest, TransientStringTensors) { int main(int argc, char** argv) { ::tflite::LogToStderr(); ::testing::InitGoogleTest(&argc, argv); + ::toco::port::InitGoogleWasDoneElsewhere(); return RUN_ALL_TESTS(); } diff --git a/tensorflow/lite/toco/toco_port.cc b/tensorflow/lite/toco/toco_port.cc index fb8c1b8337..b222032e61 100644 --- a/tensorflow/lite/toco/toco_port.cc +++ b/tensorflow/lite/toco/toco_port.cc @@ -57,6 +57,11 @@ void InitGoogle(const char* usage, int* argc, char*** argv, bool remove_flags) { ::InitGoogle(usage, argc, argv, remove_flags); } +void InitGoogleWasDoneElsewhere() { + // Nothing need be done since ::CheckInitGoogleIsDone() is aware of other + // possible initialization entry points. +} + void CheckInitGoogleIsDone(const char* message) { ::CheckInitGoogleIsDone(message); } @@ -152,6 +157,8 @@ constexpr int kFileWriteFlags = O_CREAT | O_WRONLY; static bool port_initialized = false; +void InitGoogleWasDoneElsewhere() { port_initialized = true; } + void InitGoogle(const char* usage, int* argc, char*** argv, bool remove_flags) { if (!port_initialized) { #if defined(PLATFORM_GOOGLE) diff --git a/tensorflow/lite/toco/toco_port.h b/tensorflow/lite/toco/toco_port.h index 2f39e3d6d5..231612ecd4 100644 --- a/tensorflow/lite/toco/toco_port.h +++ b/tensorflow/lite/toco/toco_port.h @@ -55,6 +55,10 @@ double round(double x); namespace toco { namespace port { +// Things like tests use other initialization routines that need control +// of flags. However, for testing we still want to use toco_port.h facilities. +// This function sets initialized flag trivially. +void InitGoogleWasDoneElsewhere(); void InitGoogle(const char* usage, int* argc, char*** argv, bool remove_flags); void CheckInitGoogleIsDone(const char* message); diff --git a/tensorflow/lite/toco/toco_port_test.cc b/tensorflow/lite/toco/toco_port_test.cc index d80d423ed7..997da58b8f 100644 --- a/tensorflow/lite/toco/toco_port_test.cc +++ b/tensorflow/lite/toco/toco_port_test.cc @@ -61,5 +61,6 @@ TEST(TocoPortTest, JoinPath) { int main(int argc, char** argv) { ::tflite::LogToStderr(); ::testing::InitGoogleTest(&argc, argv); + ::toco::port::InitGoogleWasDoneElsewhere(); return RUN_ALL_TESTS(); } diff --git a/tensorflow/lite/toco/tooling_util_test.cc b/tensorflow/lite/toco/tooling_util_test.cc index e44b94b712..faa6fe412e 100644 --- a/tensorflow/lite/toco/tooling_util_test.cc +++ b/tensorflow/lite/toco/tooling_util_test.cc @@ -19,6 +19,7 @@ limitations under the License. #include "tensorflow/core/lib/core/status.h" #include "tensorflow/lite/testing/util.h" #include "tensorflow/lite/toco/model.h" +#include "tensorflow/lite/toco/toco_port.h" #include "tensorflow/lite/toco/tooling_util.h" namespace toco { @@ -208,5 +209,6 @@ TEST(FusedActivationTest, DefaultsToUnfused) { int main(int argc, char** argv) { ::tflite::LogToStderr(); ::testing::InitGoogleTest(&argc, argv); + ::toco::port::InitGoogleWasDoneElsewhere(); return RUN_ALL_TESTS(); } -- GitLab From c503c6254608a622b06340f1f7ddd8f31565ab5e Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Tue, 8 Jan 2019 04:37:51 -0800 Subject: [PATCH 474/622] Small clean up in in saved_model/load_test.py Temporary `function` in checkpointable under test is not needed in most examples as `cycle` is passing signatures explicitly. PiperOrigin-RevId: 228309220 --- tensorflow/python/saved_model/load_test.py | 34 +++++++++------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/tensorflow/python/saved_model/load_test.py b/tensorflow/python/saved_model/load_test.py index 9eac3e6555..a1c81d3f14 100644 --- a/tensorflow/python/saved_model/load_test.py +++ b/tensorflow/python/saved_model/load_test.py @@ -42,9 +42,6 @@ class LoadTest(test.TestCase): def test_structure_import(self): root = tracking.Checkpointable() - root.f = def_function.function( - lambda x: 2. * x, - input_signature=[tensor_spec.TensorSpec(None, dtypes.float32)]) root.dep_one = tracking.Checkpointable() root.dep_two = tracking.Checkpointable() root.dep_two.dep = tracking.Checkpointable() @@ -52,19 +49,25 @@ class LoadTest(test.TestCase): imported = self.cycle(root) self.assertIs(imported.dep_three, imported.dep_two.dep) self.assertIsNot(imported.dep_one, imported.dep_two) - self.assertEqual(4., imported.f(constant_op.constant(2.)).numpy()) def test_variables(self): root = tracking.Checkpointable() root.v1 = variables.Variable(1.) root.v2 = variables.Variable(2.) - root.f = def_function.function( - lambda x: root.v2 * x, - input_signature=[tensor_spec.TensorSpec(None, dtypes.float32)]) imported = self.cycle(root) self.assertEquals(imported.v1.numpy(), 1.0) self.assertEquals(imported.v2.numpy(), 2.0) + + def test_capture_variables(self): + root = tracking.Checkpointable() + root.weights = variables.Variable(2.) + root.f = def_function.function( + lambda x: root.weights * x, + input_signature=[tensor_spec.TensorSpec(None, dtypes.float32)]) + imported = self.cycle(root) self.assertEqual(4., imported.f(constant_op.constant(2.)).numpy()) + imported.weights.assign(4.0) + self.assertEqual(8., imported.f(constant_op.constant(2.)).numpy()) def _make_asset(self, contents): filename = tempfile.mktemp(prefix=self.get_temp_dir()) @@ -72,19 +75,16 @@ class LoadTest(test.TestCase): f.write(contents) return filename - def test_assets_import(self): + def test_assets(self): file1 = self._make_asset("contents 1") file2 = self._make_asset("contents 2") root = tracking.Checkpointable() - root.f = def_function.function( - lambda x: 2. * x, - input_signature=[tensor_spec.TensorSpec(None, dtypes.float32)]) root.asset1 = tracking.TrackableAsset(file1) root.asset2 = tracking.TrackableAsset(file2) save_dir = os.path.join(self.get_temp_dir(), "save_dir") - save.save(root, save_dir) + save.save(root, save_dir, signatures={}) file_io.delete_file(file1) file_io.delete_file(file2) @@ -110,18 +110,12 @@ class LoadTest(test.TestCase): with open(imported_output, "r") as f: self.assertEquals("contents", f.read()) - def test_assets_dedup(self): + def test_dedup_assets(self): vocab = self._make_asset("contents") root = tracking.Checkpointable() - root.f = def_function.function( - lambda x: 2. * x, - input_signature=[tensor_spec.TensorSpec(None, dtypes.float32)]) - root.asset1 = tracking.TrackableAsset(vocab) root.asset2 = tracking.TrackableAsset(vocab) - imported = self.cycle(root) - self.assertEqual(imported.asset1.asset_path.numpy(), imported.asset2.asset_path.numpy()) @@ -154,7 +148,7 @@ class LoadTest(test.TestCase): imported = self.cycle(root) self.assertEqual(4., imported.f(constant_op.constant(2.0)).numpy()) - def test_nested_func(self): + def test_nested_functions(self): f = def_function.function( lambda x: x*2.0, input_signature=[tensor_spec.TensorSpec(None, dtypes.float32)]) -- GitLab From 114f0d81404e7f9a93297f11ebc2fbceee8dbf7c Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Tue, 8 Jan 2019 05:20:40 -0800 Subject: [PATCH 475/622] Add fine-tune capabilities to object based saved model save/load. - Keep "trainable" bit of variables. - Wire variables in restored FuncGraphs so they are automaticaly captured by GradientTapes. PiperOrigin-RevId: 228311974 --- tensorflow/python/eager/function.py | 2 +- tensorflow/python/saved_model/load.py | 8 +++++- tensorflow/python/saved_model/load_test.py | 25 +++++++++++++++++-- tensorflow/python/saved_model/save.py | 1 + .../saved_model/saved_object_graph.proto | 3 ++- 5 files changed, 34 insertions(+), 5 deletions(-) diff --git a/tensorflow/python/eager/function.py b/tensorflow/python/eager/function.py index 58d1f6b886..05fccfbcd9 100644 --- a/tensorflow/python/eager/function.py +++ b/tensorflow/python/eager/function.py @@ -105,7 +105,7 @@ def _parse_func_attrs(attributes): attrs[key] = attr_value_pb2.AttrValue(i=value) elif isinstance(value, float): attrs[key] = attr_value_pb2.AttrValue(f=value) - elif isinstance(value, (str, bytes)): + elif isinstance(value, (str, bytes, six.text_type)): attrs[key] = attr_value_pb2.AttrValue(s=compat.as_bytes(value)) else: raise ValueError("Unsupported attribute type for %s with type %s" % diff --git a/tensorflow/python/saved_model/load.py b/tensorflow/python/saved_model/load.py index 3e85fade50..1ee1b69b03 100644 --- a/tensorflow/python/saved_model/load.py +++ b/tensorflow/python/saved_model/load.py @@ -58,6 +58,11 @@ class _Loader(object): bound_inputs = [ self._get_tensor_from_node(node_id) for node_id in monomorphic_function.bound_inputs] + bound_variables = [ + self._nodes[node_id] + for node_id in monomorphic_function.bound_inputs + if self._proto.nodes[node_id].WhichOneof("kind") == "variable" + ] if name in seen_functions: if self._functions[name]._captured_inputs != bound_inputs: # pylint: disable=protected-access raise NotImplementedError( @@ -69,6 +74,7 @@ class _Loader(object): # concrete function, note that we did not modify the FuncGraph # itself. self._functions[name]._captured_inputs = bound_inputs # pylint: disable=protected-access + self._functions[name]._func_graph.variables = bound_variables # pylint: disable=protected-access def _get_tensor_from_node(self, node_id): obj = self._nodes[node_id] @@ -123,7 +129,7 @@ class _Loader(object): def _recreate_variable(self, proto): # TODO(andresp): Can we use the checkpointed value as initializer? dummy_value = init_ops.Zeros(dtype=proto.dtype)(shape=proto.shape) - return variables.Variable(dummy_value) + return variables.Variable(dummy_value, trainable=proto.trainable) def _load_saved_object_graph_proto(filename): diff --git a/tensorflow/python/saved_model/load_test.py b/tensorflow/python/saved_model/load_test.py index a1c81d3f14..35e384dbfc 100644 --- a/tensorflow/python/saved_model/load_test.py +++ b/tensorflow/python/saved_model/load_test.py @@ -21,6 +21,7 @@ from __future__ import print_function import os import tempfile +from tensorflow.python.eager import backprop from tensorflow.python.eager import def_function from tensorflow.python.eager import test from tensorflow.python.framework import constant_op @@ -52,11 +53,13 @@ class LoadTest(test.TestCase): def test_variables(self): root = tracking.Checkpointable() - root.v1 = variables.Variable(1.) - root.v2 = variables.Variable(2.) + root.v1 = variables.Variable(1., trainable=True) + root.v2 = variables.Variable(2., trainable=False) imported = self.cycle(root) self.assertEquals(imported.v1.numpy(), 1.0) + self.assertTrue(imported.v1.trainable) self.assertEquals(imported.v2.numpy(), 2.0) + self.assertFalse(imported.v2.trainable) def test_capture_variables(self): root = tracking.Checkpointable() @@ -248,6 +251,24 @@ class LoadTest(test.TestCase): self.cycle(m) self.assertEquals(4.0, m.f(constant_op.constant(2.0)).numpy()) + def test_basic_backprop(self): + weight = variables.Variable(1., trainable=True) + bias = variables.Variable(0., trainable=True) + g = def_function.function( + lambda x: x*weight + bias, + input_signature=[tensor_spec.TensorSpec(None, dtypes.float32)]) + + root = tracking.Checkpointable() + root.weight = weight + root.bias = bias + root.g = g + imported = self.cycle(root) + with backprop.GradientTape(watch_accessed_variables=True) as t: + x = constant_op.constant([3.5]) + loss = imported.g(x) + grad = t.gradient(loss, [imported.weight, imported.bias]) + self.assertAllClose(grad, [3.5, 1.0]) + if __name__ == "__main__": test.main() diff --git a/tensorflow/python/saved_model/save.py b/tensorflow/python/saved_model/save.py index a76b370565..d79b5fe3a6 100644 --- a/tensorflow/python/saved_model/save.py +++ b/tensorflow/python/saved_model/save.py @@ -610,6 +610,7 @@ def _write_object_proto(obj, proto, asset_file_def_index, node_ids): proto.asset.asset_file_def_index = asset_file_def_index[obj] elif resource_variable_ops.is_resource_variable(obj): proto.variable.SetInParent() + proto.variable.trainable = obj.trainable proto.variable.dtype = obj.dtype.as_datatype_enum proto.variable.shape.CopyFrom(obj.shape.as_proto()) elif isinstance(obj, def_function.PolymorphicFunction): diff --git a/tensorflow/python/saved_model/saved_object_graph.proto b/tensorflow/python/saved_model/saved_object_graph.proto index f46927d6e8..1e2514b7f7 100644 --- a/tensorflow/python/saved_model/saved_object_graph.proto +++ b/tensorflow/python/saved_model/saved_object_graph.proto @@ -104,6 +104,7 @@ message SavedMonomorphicFunction { message SavedVariable { DataType dtype = 1; TensorShapeProto shape = 2; + bool trainable = 3; - // TODO(andresp): Add "trainable" and save_slice_info_def. + // TODO(andresp): Add save_slice_info_def? } -- GitLab From 0f9478c3c2a71f26fd26083b2e2ed76fb69d1030 Mon Sep 17 00:00:00 2001 From: ktaebum Date: Tue, 8 Jan 2019 23:19:38 +0900 Subject: [PATCH 476/622] resolved incompatible dimension reshape before feed into final dense layer --- tensorflow/contrib/eager/python/examples/rnn_ptb/rnn_ptb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/contrib/eager/python/examples/rnn_ptb/rnn_ptb.py b/tensorflow/contrib/eager/python/examples/rnn_ptb/rnn_ptb.py index 15776c694e..9b5a2c947b 100644 --- a/tensorflow/contrib/eager/python/examples/rnn_ptb/rnn_ptb.py +++ b/tensorflow/contrib/eager/python/examples/rnn_ptb/rnn_ptb.py @@ -128,7 +128,7 @@ class PTBModel(tf.keras.Model): self.linear = layers.Dense( vocab_size, kernel_initializer=tf.random_uniform_initializer(-0.1, 0.1)) - self._output_shape = [-1, embedding_dim] + self._output_shape = [-1, hidden_dim] def call(self, input_seq, training): """Run the forward pass of PTBModel. -- GitLab From 7fa90cddf07c6aac532306dac15e02216a83b9fa Mon Sep 17 00:00:00 2001 From: Asim Shankar Date: Tue, 8 Jan 2019 07:29:42 -0800 Subject: [PATCH 477/622] [eager]: Disable benchmark that is currently timing out. This timeout seems to have started after https://github.com/tensorflow/tensorflow/commit/0445684a64d1bea8490a99eb9ce278176133df75 PiperOrigin-RevId: 228326334 --- tensorflow/python/eager/benchmarks_test.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tensorflow/python/eager/benchmarks_test.py b/tensorflow/python/eager/benchmarks_test.py index 31a7efca82..9191c8e689 100644 --- a/tensorflow/python/eager/benchmarks_test.py +++ b/tensorflow/python/eager/benchmarks_test.py @@ -867,6 +867,10 @@ class MicroBenchmarks(test.Benchmark): self._run(scan, 100) def benchmarkScanDefun(self): + if context.num_gpus(): + # TODO(b/122081934): Re-enable this after figuring out why this became + # really slow with control flow V2 + return elems = math_ops.range(1600) @function.defun -- GitLab From 107b7c4de6727ad02a0b4fc24aa1ac17bd697776 Mon Sep 17 00:00:00 2001 From: Dan Moldovan Date: Tue, 8 Jan 2019 11:37:37 -0500 Subject: [PATCH 478/622] Fixes for rnn_keras_estimator.ipynb * wrapping `range(batch_size)` into a `list` call so that it's convertible to `Tensor` in Python 3, where `range` alone returns just a generator * specifying a `dtype` argument to `LSTMBlockCell`'s constructor These should fix the breakages in #24701 --- .../autograph/examples/notebooks/rnn_keras_estimator.ipynb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tensorflow/contrib/autograph/examples/notebooks/rnn_keras_estimator.ipynb b/tensorflow/contrib/autograph/examples/notebooks/rnn_keras_estimator.ipynb index 44532cb078..831c613f2c 100644 --- a/tensorflow/contrib/autograph/examples/notebooks/rnn_keras_estimator.ipynb +++ b/tensorflow/contrib/autograph/examples/notebooks/rnn_keras_estimator.ipynb @@ -186,8 +186,8 @@ "\n", " def __init__(self):\n", " super(RnnColorbot, self).__init__()\n", - " self.lower_cell = tf.contrib.rnn.LSTMBlockCell(256)\n", - " self.upper_cell = tf.contrib.rnn.LSTMBlockCell(128)\n", + " self.lower_cell = tf.contrib.rnn.LSTMBlockCell(256, dtype=tf.float32)\n", + " self.upper_cell = tf.contrib.rnn.LSTMBlockCell(128, dtype=tf.float32)\n", " self.relu_layer = tf.layers.Dense(3, activation=tf.nn.relu)\n", "\n", " def _rnn_layer(self, chars, cell, batch_size, training):\n", @@ -241,7 +241,7 @@ " seq = self._rnn_layer(seq, self.upper_cell, batch_size, training)\n", "\n", " # Grab just the end-of-sequence from each output.\n", - " indices = (length - 1, range(batch_size))\n", + " indices = (length - 1, list(range(batch_size)))\n", " indices = tf.stack(indices, 1)\n", " sequence_ends = tf.gather_nd(seq, indices)\n", " return self.relu_layer(sequence_ends)\n", -- GitLab From 02d4333dfc5a9fe2c3929ba6288e9a679725da85 Mon Sep 17 00:00:00 2001 From: Andrew Selle Date: Tue, 8 Jan 2019 09:18:09 -0800 Subject: [PATCH 479/622] Give bytes constructor the appropriate utf-8 argument. PiperOrigin-RevId: 228342070 --- tensorflow/tools/git/gen_git_source.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tensorflow/tools/git/gen_git_source.py b/tensorflow/tools/git/gen_git_source.py index 0c37508650..c2449da923 100755 --- a/tensorflow/tools/git/gen_git_source.py +++ b/tensorflow/tools/git/gen_git_source.py @@ -29,8 +29,8 @@ from __future__ import print_function import argparse import json import os -import subprocess import shutil +import subprocess def parse_branch_ref(filename): @@ -175,7 +175,7 @@ def get_git_version(git_base_path, git_tag_override): # two "-" are those inserted by the git describe command. abbrev_commit = split_val[-1] val = version_separator.join( - [bytes(git_tag_override), b"0", abbrev_commit]) + [bytes(git_tag_override, "utf-8"), b"0", abbrev_commit]) return val if val else unknown_label except (subprocess.CalledProcessError, OSError): return unknown_label -- GitLab From 3ba26026545be700c4aed9bc609b25fad9c1531e Mon Sep 17 00:00:00 2001 From: Sergei Lebedev Date: Tue, 8 Jan 2019 09:25:59 -0800 Subject: [PATCH 480/622] Removed ResourceVariable._cached_shape_as_list _shape_as_list is no longer on the hotpath of EagerLinearRegressionBenchmark, the change does not seem to affect throughput estimates. Note that both estimates have high variance. Before: entry { name: "EagerLinearRegressionBenchmark.eager_train_cpu" iters: 2000 wall_time: 1.24479007721 extras { key: "examples_per_sec" value { double_value: 102828.583183 } } } After: entry { name: "EagerLinearRegressionBenchmark.eager_train_cpu" iters: 2000 wall_time: 1.25307798386 extras { key: "examples_per_sec" value { double_value: 102148.470924 } } } PiperOrigin-RevId: 228343127 --- tensorflow/python/ops/resource_variable_ops.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/tensorflow/python/ops/resource_variable_ops.py b/tensorflow/python/ops/resource_variable_ops.py index 1d98fb2c89..d27e38c735 100644 --- a/tensorflow/python/ops/resource_variable_ops.py +++ b/tensorflow/python/ops/resource_variable_ops.py @@ -504,7 +504,6 @@ class ResourceVariable(variables.VariableV1): # all in graph mode. self._handle_deleter = EagerResourceDeleter( handle=self._handle, handle_device=self._handle.device) - self._cached_shape_as_list = None def _init_from_proto(self, variable_def, import_scope=None): """Initializes from `VariableDef` proto.""" @@ -562,7 +561,6 @@ class ResourceVariable(variables.VariableV1): self._caching_device = None self._dtype = dtypes.as_dtype(self._handle.op.get_attr("dtype")) self._constraint = None - self._cached_shape_as_list = None @contextlib.contextmanager def _assign_dependencies(self): @@ -632,12 +630,9 @@ class ResourceVariable(variables.VariableV1): return self._distribute_strategy def _shape_as_list(self): - if self._cached_shape_as_list: - return self._cached_shape_as_list if self.shape.ndims is None: return None - self._cached_shape_as_list = [dim.value for dim in self.shape.dims] - return self._cached_shape_as_list + return [dim.value for dim in self.shape.dims] def _shape_tuple(self): shape = self._shape_as_list() -- GitLab From aa5e17a2dc94ba47d41a74021d6701a8c10de4a5 Mon Sep 17 00:00:00 2001 From: Jared Duke Date: Tue, 8 Jan 2019 09:37:02 -0800 Subject: [PATCH 481/622] Fix linkage for several lite/testing tests Use the tf_cc_test rule for targets that have TensorFlow deps. PiperOrigin-RevId: 228344897 --- tensorflow/lite/testing/BUILD | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tensorflow/lite/testing/BUILD b/tensorflow/lite/testing/BUILD index fa25cfaa69..19c950b4f8 100644 --- a/tensorflow/lite/testing/BUILD +++ b/tensorflow/lite/testing/BUILD @@ -257,7 +257,7 @@ cc_library( ], ) -cc_test( +tf_cc_test( name = "tf_driver_test", size = "small", srcs = ["tf_driver_test.cc"], @@ -286,7 +286,7 @@ cc_library( ], ) -cc_test( +tf_cc_test( name = "generate_testspec_test", size = "small", srcs = ["generate_testspec_test.cc"], -- GitLab From f0fba8b59b3e3fa6b068c0c2715104153983c554 Mon Sep 17 00:00:00 2001 From: Andrew Selle Date: Tue, 8 Jan 2019 09:54:11 -0800 Subject: [PATCH 482/622] Use correct _t integer types to fix mac build. PiperOrigin-RevId: 228347592 --- tensorflow/lite/kernels/dequantize_test.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tensorflow/lite/kernels/dequantize_test.cc b/tensorflow/lite/kernels/dequantize_test.cc index 6343745eef..be7caa3189 100644 --- a/tensorflow/lite/kernels/dequantize_test.cc +++ b/tensorflow/lite/kernels/dequantize_test.cc @@ -12,6 +12,8 @@ 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 + #include #include "tensorflow/lite/interpreter.h" #include "tensorflow/lite/kernels/internal/types.h" @@ -59,7 +61,7 @@ TEST(DequantizeOpTest, UINT8) { // [-63.5, 64] -> scale=0.5 zero_point=127 for UINT8 DequantizeOpModel m(TensorType_UINT8, {2, 5}, 0.5, 127); - m.SetInput({0, 1, 2, 3, 4, 251, 252, 253, 254, 255}); + m.SetInput({0, 1, 2, 3, 4, 251, 252, 253, 254, 255}); m.Invoke(); EXPECT_THAT(m.GetOutput(), ElementsAreArray(ArrayFloatNear( @@ -70,7 +72,7 @@ TEST(DequantizeOpTest, INT8) { // [-63.5, 64] -> scale=0.5, zero_point=1 for INT8 DequantizeOpModel m(TensorType_INT8, {2, 5}, 0.5, -1); - m.SetInput({-128, -127, -126, -125, -124, 123, 124, 125, 126, 127}); + m.SetInput({-128, -127, -126, -125, -124, 123, 124, 125, 126, 127}); m.Invoke(); EXPECT_THAT(m.GetOutput(), ElementsAreArray(ArrayFloatNear( -- GitLab From 0ad4e9ea2b41ad1d9c8c59844fd814b01820bab2 Mon Sep 17 00:00:00 2001 From: Jian Li Date: Tue, 8 Jan 2019 09:57:31 -0800 Subject: [PATCH 483/622] Update unidirectional lstm test case. PiperOrigin-RevId: 228348125 --- tensorflow/lite/kernels/unidirectional_sequence_lstm_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/lite/kernels/unidirectional_sequence_lstm_test.cc b/tensorflow/lite/kernels/unidirectional_sequence_lstm_test.cc index 274f907076..bc35d90773 100644 --- a/tensorflow/lite/kernels/unidirectional_sequence_lstm_test.cc +++ b/tensorflow/lite/kernels/unidirectional_sequence_lstm_test.cc @@ -252,7 +252,7 @@ class HybridUnidirectionalLSTMOpModel : public UnidirectionalLSTMOpModel { tensor_type_ = tensor_type; } - void SetWeights(int weights_idx, std::vector f) { + void SetWeights(int weights_idx, const std::vector& f) { if (tensor_type_ == TensorType_UINT8) { SymmetricQuantizeAndPopulate(weights_idx, f); } else { -- GitLab From 011a8094c182101a65fb84e98a60a152e0f58f0d Mon Sep 17 00:00:00 2001 From: Sergei Lebedev Date: Tue, 8 Jan 2019 10:06:30 -0800 Subject: [PATCH 484/622] Aligned ResourceVariable.__reduce__ with ResourceVariable.__deepcopy__ PiperOrigin-RevId: 228350078 --- .../kernel_tests/resource_variable_ops_test.py | 13 ++++++++++--- tensorflow/python/ops/resource_variable_ops.py | 14 ++++++++++++-- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/tensorflow/python/kernel_tests/resource_variable_ops_test.py b/tensorflow/python/kernel_tests/resource_variable_ops_test.py index 67fa350317..2594055119 100644 --- a/tensorflow/python/kernel_tests/resource_variable_ops_test.py +++ b/tensorflow/python/kernel_tests/resource_variable_ops_test.py @@ -286,12 +286,19 @@ class ResourceVariableOpsTest(test_util.TensorFlowTestCase): tmp_dir = self.get_temp_dir() fname = os.path.join(tmp_dir, "var.pickle") with open(fname, "wb") as f: - v = resource_variable_ops.ResourceVariable(10.0) + v = resource_variable_ops.ResourceVariable( + 10.0, + dtype=dtypes.float16, + name="v") pickle.dump(v, f) with open(fname, "rb") as f: - v = pickle.load(f) - self.assertAllEqual(v.numpy(), 10.0) + new_v = pickle.load(f) + self.assertEqual(new_v.name, v.name) + self.assertEqual(new_v.shape, v.shape) + self.assertEqual(new_v.dtype, v.dtype) + self.assertEqual(new_v.trainable, v.trainable) + self.assertAllEqual(new_v.numpy(), v.numpy()) @test_util.run_in_graph_and_eager_modes def testScatterDiv(self): diff --git a/tensorflow/python/ops/resource_variable_ops.py b/tensorflow/python/ops/resource_variable_ops.py index d27e38c735..2d46e21074 100644 --- a/tensorflow/python/ops/resource_variable_ops.py +++ b/tensorflow/python/ops/resource_variable_ops.py @@ -20,6 +20,7 @@ from __future__ import division from __future__ import print_function import contextlib +import functools from tensorflow.core.framework import attr_value_pb2 from tensorflow.core.framework import variable_pb2 @@ -595,7 +596,8 @@ class ResourceVariable(variables.VariableV1): trainable=self._trainable, constraint=self._constraint, dtype=self._dtype, - name=self._shared_name + "_copy") + name=self._shared_name + "_copy", + distribute_strategy=self.distribute_strategy) memo[self._unique_id] = copied_variable return copied_variable @@ -938,7 +940,15 @@ class ResourceVariable(variables.VariableV1): return assign_op def __reduce__(self): - return (ResourceVariable, (self.numpy(),)) + # The implementation mirrors that of __deepcopy__. + return functools.partial( + ResourceVariable, + initial_value=self.numpy(), + trainable=self.trainable, + name=self._shared_name, + dtype=self.dtype, + constraint=self.constraint, + distribute_strategy=self.distribute_strategy), () def scatter_sub(self, sparse_delta, use_locking=False, name=None): """Subtracts `IndexedSlices` from this variable. -- GitLab From 35641fa70182fb9ee4c7e015b4ed38d0148cf340 Mon Sep 17 00:00:00 2001 From: Francois Chollet Date: Tue, 8 Jan 2019 10:15:50 -0800 Subject: [PATCH 485/622] Make dataset instances passed to `fit`/etc propagate to the `model_iteration` loop. This is a necessary step in order to start resetting a dataset of known cardinality as part of the loop. PiperOrigin-RevId: 228351978 --- tensorflow/python/keras/engine/training.py | 185 ++++++++---------- .../python/keras/engine/training_arrays.py | 23 ++- .../keras/engine/training_dataset_test.py | 55 +++--- .../python/keras/engine/training_utils.py | 67 ++++++- 4 files changed, 192 insertions(+), 138 deletions(-) diff --git a/tensorflow/python/keras/engine/training.py b/tensorflow/python/keras/engine/training.py index bd06751428..1eda8cf797 100644 --- a/tensorflow/python/keras/engine/training.py +++ b/tensorflow/python/keras/engine/training.py @@ -19,14 +19,12 @@ from __future__ import division from __future__ import print_function import collections -import weakref import numpy as np from tensorflow.python import tf2 from tensorflow.python.data.ops import dataset_ops from tensorflow.python.data.ops import iterator_ops from tensorflow.python.eager import context -from tensorflow.python.framework import errors from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape from tensorflow.python.framework import tensor_util @@ -122,10 +120,6 @@ class Model(Network): def __init__(self, *args, **kwargs): super(Model, self).__init__(*args, **kwargs) - # Create a cache for iterator get_next op. - self._iterator_get_next = weakref.WeakKeyDictionary() - # Create a cache for dataset - uninitialized iterators - self._dataset_iterator_cache = weakref.WeakKeyDictionary() # initializing _distribution_strategy here since it is possible to call # predict on a model without compiling it. self._distribution_strategy = None @@ -1247,7 +1241,8 @@ class Model(Network): 'compiled with DistributionStrategy.') # Validate and standardize user data. x, y, sample_weights = self._standardize_user_data( - x, y, sample_weight=sample_weight, class_weight=class_weight) + x, y, sample_weight=sample_weight, class_weight=class_weight, + extract_tensors_from_dataset=True) if self.run_eagerly: outputs = training_eager.train_on_batch( @@ -1316,7 +1311,7 @@ class Model(Network): 'compiled with DistributionStrategy.') # Validate and standardize user data. x, y, sample_weights = self._standardize_user_data( - x, y, sample_weight=sample_weight) + x, y, sample_weight=sample_weight, extract_tensors_from_dataset=True) if self.run_eagerly: outputs = training_eager.test_on_batch( @@ -1359,7 +1354,8 @@ class Model(Network): raise NotImplementedError('`predict_on_batch` is not supported for ' 'models compiled with DistributionStrategy.') # Validate and standardize user data. - inputs, _, _ = self._standardize_user_data(x) + inputs, _, _ = self._standardize_user_data( + x, extract_tensors_from_dataset=True) if self.run_eagerly: if (isinstance(inputs, iterator_ops.EagerIterator) or (isinstance(inputs, dataset_ops.DatasetV2))): @@ -2103,13 +2099,6 @@ class Model(Network): self._make_predict_function() return self.predict_function - def _get_iterator_get_next_tensors(self, iterator): - get_next_op = self._iterator_get_next.get(iterator, None) - if get_next_op is None: - get_next_op = iterator.get_next() - self._iterator_get_next[iterator] = get_next_op - return get_next_op - def _distribution_standardize_user_data(self, x, y=None, @@ -2231,7 +2220,8 @@ class Model(Network): steps_name='steps', steps=None, validation_split=0, - shuffle=False): + shuffle=False, + extract_tensors_from_dataset=False): """Runs validation checks on input and target data passed by the user. Also standardizes the data to lists of arrays, in order. @@ -2275,6 +2265,10 @@ class Model(Network): validation_split: Float between 0 and 1. Fraction of the training data to be used as validation data. shuffle: Boolean whether to shuffle the training data before each epoch. + extract_tensors_from_dataset: Boolean. When `x` is a dataset instance, + this indicates whether to extract actual tensors from the dataset or + instead output the dataset instance itself. + Set to True when calling from `train_on_batch`/etc. Returns: A tuple of 3: inputs (arrays or dicts, depending on whether `x` was a dict @@ -2287,60 +2281,30 @@ class Model(Network): ValueError: In case of invalid user-provided data. RuntimeError: If the model was never compiled. """ - if isinstance(x, dataset_ops.DatasetV2): - if context.executing_eagerly(): - x = iter(x) - else: - if x in self._dataset_iterator_cache: - x = self._dataset_iterator_cache[x] - else: - iterator = dataset_ops.make_initializable_iterator(x) - self._dataset_iterator_cache[x] = iterator - x = iterator - K.get_session().run(x.initializer) + if isinstance(x, (dataset_ops.DatasetV2, dataset_ops.DatasetV1)): + # Graph mode dataset. We'll pass the dataset as-is (unless + # `extract_tensors_from_dataset` is True, in which case we extract + # the tensors from the dataset and we output them. + training_utils.validate_dataset_input(x, y, sample_weight, + validation_split) + is_dataset = True + if extract_tensors_from_dataset: + # We do this for `train_on_batch`/etc. + x, y, sample_weight = training_utils.extract_tensors_from_dataset(x) + elif isinstance(x, iterator_ops.Iterator): + # Graph mode iterator. We extract the symbolic tensors. + training_utils.validate_dataset_input(x, y, sample_weight, + validation_split) + iterator = x + x, y, sample_weight = training_utils.unpack_iterator_input(iterator) + is_dataset = True + else: + is_dataset = False # Validates `steps` argument based on x's type. if check_steps: training_utils.check_steps_argument(x, steps, steps_name) - is_x_eager_iterator = isinstance(x, iterator_ops.EagerIterator) - is_x_iterator = isinstance(x, iterator_ops.Iterator) - - # Validate user inputs when data is given as a dataset or dataset iterator. - if is_x_iterator or is_x_eager_iterator: - training_utils.validate_dataset_input(x, y, sample_weight, - validation_split) - - # For eager iterators, when we have to process multiple batches of samples, - # we will standardize the data when we actually loop over iterator and get - # the batches. For now, we just return the iterator as is. - if is_x_eager_iterator: - return x, y, sample_weight - - # If input data is a dataset iterator in graph mode or if it is an eager - # iterator and only one batch of samples is required, we fetch the data - # tensors from the iterator and then standardize them. - if is_x_iterator: - try: - next_element = self._get_iterator_get_next_tensors(x) - except errors.OutOfRangeError: - raise RuntimeError('Your dataset iterator ran out of data; ' - 'Make sure that your dataset can generate ' - 'required number of samples.') - - if isinstance(next_element, (list, tuple)): - if len(next_element) not in [2, 3]: - raise ValueError( - 'Please provide model inputs as a list or tuple of 2 or 3' - 'elements: (input, target) or (input, target, sample_weights)' - 'Received %s' % next_element) - if len(next_element) == 2: - x, y = next_element - else: - x, y, sample_weight = next_element - else: - x = next_element - # First, we build/compile the model on the fly if necessary. all_inputs = [] is_build_called = False @@ -2349,40 +2313,51 @@ class Model(Network): # rather than list inputs (e.g. FeatureColumn-based models). dict_inputs = False if not self.inputs: - # We need to use `x` to set the model inputs. - # We type-check that `x` and `y` are either single arrays + # We need to use `x_input` to set the model inputs. + + # If input data is a dataset iterator in graph mode or if it is an eager + # iterator and only one batch of samples is required, we fetch the data + # tensors from the iterator and then standardize them. + if isinstance(x, (dataset_ops.DatasetV2, dataset_ops.DatasetV1)): + x_input, y_input, _ = training_utils.extract_tensors_from_dataset(x) + else: + x_input = x + y_input = y + # We type-check that `x_input` and `y_input` are either single arrays # or lists of arrays. - if isinstance(x, (list, tuple)): + if isinstance(x_input, (list, tuple)): if not all(isinstance(v, np.ndarray) or - tensor_util.is_tensor(v) for v in x): + tensor_util.is_tensor(v) for v in x_input): raise ValueError('Please provide as model inputs either a single ' 'array or a list of arrays. You passed: x=' + str(x)) - all_inputs += list(x) - elif isinstance(x, dict): + all_inputs += list(x_input) + elif isinstance(x_input, dict): dict_inputs = True - keys = sorted(x.keys()) - all_inputs = [x[k] for k in keys] + keys = sorted(x_input.keys()) + all_inputs = [x_input[k] for k in keys] else: - if not isinstance(x, np.ndarray) and not tensor_util.is_tensor(x): + if (not isinstance(x_input, np.ndarray) and + not tensor_util.is_tensor(x_input)): raise ValueError('Please provide as model inputs either a single ' 'array or a list of arrays. You passed: x=' + str(x)) - all_inputs.append(x) + all_inputs.append(x_input) # Build the model using the retrieved inputs (value or symbolic). # If values or generated from a dataset, then in symbolic-mode # placeholders will be created to match the value shapes. is_build_called = True - if is_x_iterator: - cast_inputs = nest.map_structure(lambda v: v.shape, x) - elif training_utils.has_tensors(x): - cast_inputs = training_utils.cast_if_floating_dtype(x) + if is_dataset: + cast_inputs = nest.map_structure(lambda v: v.shape, x_input) + elif training_utils.has_tensors(x_input): + cast_inputs = training_utils.cast_if_floating_dtype(x_input) else: - cast_inputs = x + cast_inputs = x_input self._set_inputs(cast_inputs) else: + y_input = y dict_inputs = isinstance(self.inputs, dict) - if y is not None: + if y_input is not None: if not self.optimizer: raise RuntimeError('You must compile a model before ' 'training/testing. ' @@ -2390,23 +2365,24 @@ class Model(Network): if not self._is_compiled: # On-the-fly compilation of the model. # We need to use `y` to set the model targets. - if training_utils.has_tensors(y): - y = training_utils.cast_if_floating_dtype(y) - if isinstance(y, (list, tuple)): + if training_utils.has_tensors(y_input): + y_input = training_utils.cast_if_floating_dtype(y_input) + if isinstance(y_input, (list, tuple)): if not all(isinstance(v, np.ndarray) or - tensor_util.is_tensor(v) for v in y): + tensor_util.is_tensor(v) for v in y_input): raise ValueError('Please provide as model targets either a single ' 'array or a list of arrays. ' 'You passed: y=' + str(y)) - all_inputs += list(y) - elif isinstance(y, dict): - raise ValueError('Please do not pass a dictionary as model targets.') + all_inputs += list(y_input) + elif isinstance(y_input, dict): + raise ValueError('You cannot pass a dictionary as model targets.') else: - if not isinstance(y, np.ndarray) and not tensor_util.is_tensor(y): + if (not isinstance(y_input, np.ndarray) and + not tensor_util.is_tensor(y_input)): raise ValueError('Please provide as model targets either a single ' 'array or a list of arrays. ' 'You passed: y=' + str(y)) - all_inputs.append(y) + all_inputs.append(y_input) # Typecheck that all inputs are *either* value *or* symbolic. # TODO(fchollet): this check could be removed in Eager mode? @@ -2416,13 +2392,13 @@ class Model(Network): 'TensorFlow tensors. ' 'You passed: x=' + str(x) + '; y=' + str(y)) - if self.run_eagerly or is_x_iterator: + if is_dataset or context.executing_eagerly(): target_tensors = None else: # Handle target tensors if any passed. - if not isinstance(y, (list, tuple)): - y = [y] - target_tensors = [v for v in y if _is_symbolic_tensor(v)] + if not isinstance(y_input, (list, tuple)): + y_input = [y_input] + target_tensors = [v for v in y_input if _is_symbolic_tensor(v)] is_compile_called = True self.compile( optimizer=self.optimizer, @@ -2440,7 +2416,7 @@ class Model(Network): # Note: in this case, `any` and `all` are equivalent since we disallow # mixed symbolic/value inputs. if (not self.run_eagerly and is_build_called and is_compile_called and - not is_x_iterator and any(_is_symbolic_tensor(v) for v in all_inputs)): + not is_dataset and any(_is_symbolic_tensor(v) for v in all_inputs)): return [], [], [] # What follows is input validation and standardization to list format, @@ -2462,12 +2438,14 @@ class Model(Network): feed_input_shapes = self._feed_input_shapes # Standardize the inputs. - x = training_utils.standardize_input_data( - x, - feed_input_names, - feed_input_shapes, - check_batch_axis=False, # Don't enforce the batch size. - exception_prefix='input') + if not isinstance(x, (dataset_ops.DatasetV2, dataset_ops.DatasetV1)): + # TODO(fchollet): run static checks with dataset output shape(s). + x = training_utils.standardize_input_data( + x, + feed_input_names, + feed_input_shapes, + check_batch_axis=False, # Don't enforce the batch size. + exception_prefix='input') if y is not None: if not self._is_graph_network: @@ -2541,7 +2519,8 @@ class Model(Network): str(x[0].shape[0]) + ' samples') # If dictionary inputs were provided, we return a dictionary as well. - if dict_inputs: + if dict_inputs and not isinstance(x, (dataset_ops.DatasetV2, + dataset_ops.DatasetV1)): x = dict(zip(feed_input_names, x)) return x, y, sample_weights diff --git a/tensorflow/python/keras/engine/training_arrays.py b/tensorflow/python/keras/engine/training_arrays.py index 9887fe34a8..ab7d455bfa 100644 --- a/tensorflow/python/keras/engine/training_arrays.py +++ b/tensorflow/python/keras/engine/training_arrays.py @@ -23,6 +23,7 @@ import functools import numpy as np +from tensorflow.python.data.ops import dataset_ops from tensorflow.python.eager import context from tensorflow.python.framework import errors from tensorflow.python.keras import backend as K @@ -146,15 +147,15 @@ def model_iteration(model, Arguments: model: Keras Model instance. - inputs: Either a list of arrays or a dictionary. - targets: List of target arrays. + inputs: Either a list or dictionary of arrays, or a dataset instance. + targets: List/dictionary of input arrays. sample_weights: Optional list of sample weight arrays. batch_size: Integer batch size or None if unknown. epochs: Number of times to iterate over the data verbose: Verbosity mode, 0, 1 or 2 callbacks: List of callbacks to be called during training - val_inputs: List of input arrays. - val_targets: List of target arrays. + val_inputs: Either a list or dictionary of arrays, or a dataset instance. + val_targets: List/dictionary of target arrays. val_sample_weights: Optional list of sample weight arrays. shuffle: Whether to shuffle the data at the beginning of each epoch concatenation of list the display names of the outputs of `f` and the @@ -186,6 +187,20 @@ def model_iteration(model, if 'steps' in kwargs: steps_per_epoch = kwargs['steps'] + # In case we are passed datasets, we extract symbolic tensors from them. + if isinstance(inputs, (dataset_ops.DatasetV2, dataset_ops.DatasetV1)): + inputs, targets, sample_weights = model._standardize_user_data( + inputs, + steps_name='steps_per_epoch', + steps=steps_per_epoch, + extract_tensors_from_dataset=True) + if isinstance(val_inputs, (dataset_ops.DatasetV2, dataset_ops.DatasetV1)): + val_inputs, val_targets, val_sample_weights = model._standardize_user_data( + val_inputs, + steps_name='validation_steps', + steps=validation_steps, + extract_tensors_from_dataset=True) + _validate_arguments(steps_per_epoch, validation_steps, kwargs) if mode == ModeKeys.TRAIN: _print_train_info(inputs, val_inputs, steps_per_epoch, verbose) diff --git a/tensorflow/python/keras/engine/training_dataset_test.py b/tensorflow/python/keras/engine/training_dataset_test.py index 646ce31909..40d42c13f0 100644 --- a/tensorflow/python/keras/engine/training_dataset_test.py +++ b/tensorflow/python/keras/engine/training_dataset_test.py @@ -25,7 +25,6 @@ import numpy as np from tensorflow.python import keras from tensorflow.python.data.ops import dataset_ops from tensorflow.python.eager import context -from tensorflow.python.framework import ops from tensorflow.python.framework import test_util as tf_test_util from tensorflow.python.keras import keras_parameterized from tensorflow.python.keras import metrics as metrics_module @@ -99,29 +98,6 @@ class TestTrainingWithDatasetIterators(keras_parameterized.TestCase): 'the `steps` argument'): model.predict(iterator, verbose=0) - @keras_parameterized.run_with_all_model_types - @keras_parameterized.run_all_keras_modes - def test_get_next_op_created_once(self): - model = testing_utils.get_small_mlp(1, 4, input_dim=3) - optimizer = RMSPropOptimizer(learning_rate=0.001) - loss = 'mse' - metrics = ['mae'] - model.compile(optimizer, loss, metrics=metrics, - run_eagerly=testing_utils.should_run_eagerly()) - - inputs = np.zeros((10, 3), np.float32) - targets = np.zeros((10, 4), np.float32) - dataset = dataset_ops.Dataset.from_tensor_slices((inputs, targets)) - dataset = dataset.repeat(100) - dataset = dataset.batch(10) - iterator = dataset_ops.make_one_shot_iterator(dataset) - - model.fit(iterator, epochs=1, steps_per_epoch=2, verbose=1) - # Finalize graph to make sure we are not appending another iterator - # get_next op in the graph. - ops.get_default_graph().finalize() - model.fit(iterator, epochs=1, steps_per_epoch=2, verbose=1) - @keras_parameterized.run_with_all_model_types @keras_parameterized.run_all_keras_modes def test_iterators_running_out_of_data(self): @@ -172,9 +148,6 @@ class TestTrainingWithDataset(keras_parameterized.TestCase): # Call fit with validation data model.fit(dataset, epochs=1, steps_per_epoch=2, verbose=0, validation_data=dataset, validation_steps=2) - # Finalize the graph to make sure new ops aren't added when calling on the - # same dataset - ops.get_default_graph().finalize() model.fit(dataset, epochs=1, steps_per_epoch=2, verbose=0, validation_data=dataset, validation_steps=2) @@ -292,6 +265,34 @@ class TestTrainingWithDataset(keras_parameterized.TestCase): model.fit(dataset, epochs=1, steps_per_epoch=2, verbose=1) + @keras_parameterized.run_all_keras_modes + def test_dataset_fit_correctness(self): + + class SumLayer(keras.layers.Layer): + + def build(self, _): + self.w = self.add_weight('w', ()) + + def call(self, inputs): + return keras.backend.sum(inputs) + self.w * 0 + + model = keras.Sequential([SumLayer(input_shape=(2,))]) + model.compile(RMSPropOptimizer(learning_rate=0.001), + loss='mae', + run_eagerly=testing_utils.should_run_eagerly()) + + inputs = np.zeros((40, 2), dtype=np.float32) + inputs[10:20, :] = 2 + inputs[20:30, :] = 1 + inputs[30:, :] = 4 + targets = np.zeros((40, 1), dtype=np.float32) + dataset = dataset_ops.Dataset.from_tensor_slices((inputs, targets)) + dataset = dataset.batch(10) + history = model.fit(dataset, + epochs=2, steps_per_epoch=2, verbose=1, shuffle=False) + self.assertListEqual(history.history['loss'], + [inputs[:20].sum() / 2, inputs[20:].sum() / 2]) + @tf_test_util.run_deprecated_v1 def test_dataset_input_shape_validation(self): with self.cached_session(): diff --git a/tensorflow/python/keras/engine/training_utils.py b/tensorflow/python/keras/engine/training_utils.py index 5edf1de1b0..949f1e400d 100644 --- a/tensorflow/python/keras/engine/training_utils.py +++ b/tensorflow/python/keras/engine/training_utils.py @@ -31,6 +31,7 @@ from tensorflow.python.data.ops import readers from tensorflow.python.eager import context from tensorflow.python.eager import def_function from tensorflow.python.framework import constant_op +from tensorflow.python.framework import errors from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape from tensorflow.python.framework import tensor_spec @@ -1081,10 +1082,10 @@ def is_feature_layer(layer): def is_eager_dataset_or_iterator(data): - if context.executing_eagerly(): - if isinstance(data, (dataset_ops.DatasetV2, iterator_ops.EagerIterator)): - return True - return False + return context.executing_eagerly() and isinstance( + data, (dataset_ops.DatasetV1, + dataset_ops.DatasetV2, + iterator_ops.EagerIterator)) # pylint: disable=protected-access @@ -1199,6 +1200,64 @@ def assert_not_shuffled(dataset): raise ValueError('Could not assert that dataset is not shuffled.') +def is_dataset_or_iterator(data): + return isinstance(data, (dataset_ops.DatasetV1, + dataset_ops.DatasetV2, + iterator_ops.EagerIterator, + iterator_ops.Iterator)) + + +def extract_tensors_from_dataset(dataset): + """Extract a tuple of tensors `inputs, targets, sample_weight` from a dataset. + + Works only for graph mode. + + Arguments: + dataset: Dataset instance. + + Returns: + Tuple of tensors `x, y, weights`. `y` and `weights` entry may be None. + """ + iterator = dataset_ops.make_initializable_iterator(dataset) + K.get_session().run(iterator.initializer) + inputs, targets, sample_weight = unpack_iterator_input(iterator) + return inputs, targets, sample_weight + + +def unpack_iterator_input(iterator): + """Convert a dataset iterator to a tuple of tensors `x, y, sample_weights`. + + Arguments: + iterator: Instance of a dataset iterator. + + Returns: + Tuple of tensors `x, y, weights`. `y` and `weights` entry may be None. + """ + try: + next_element = iterator.get_next() + except errors.OutOfRangeError: + raise RuntimeError('Your dataset iterator ran out of data; ' + 'Make sure that your dataset can generate ' + 'required number of samples.') + + if isinstance(next_element, (list, tuple)): + if len(next_element) not in [2, 3]: + raise ValueError( + 'Please provide model inputs as a list or tuple of 2 or 3 ' + 'elements: (input, target) or (input, target, sample_weights) ' + 'Received %s' % next_element) + if len(next_element) == 2: + x, y = next_element + weights = None + else: + x, y, weights = next_element + else: + x = next_element + y = None + weights = None + return x, y, weights + + class ModelInputs(object): """Encapsulates model inputs. -- GitLab From e5319d26bb5b3fed5e3c9c865193db41f75cf958 Mon Sep 17 00:00:00 2001 From: Pavithra Vijay Date: Tue, 8 Jan 2019 10:17:23 -0800 Subject: [PATCH 486/622] Add mean relative error v2 metric. PiperOrigin-RevId: 228352265 --- tensorflow/python/keras/metrics.py | 80 +++++++++++++++++++++++++ tensorflow/python/keras/metrics_test.py | 56 +++++++++++++++++ 2 files changed, 136 insertions(+) diff --git a/tensorflow/python/keras/metrics.py b/tensorflow/python/keras/metrics.py index 9a2e057780..2c2700ffa3 100644 --- a/tensorflow/python/keras/metrics.py +++ b/tensorflow/python/keras/metrics.py @@ -51,6 +51,7 @@ from tensorflow.python.keras.utils.generic_utils import serialize_keras_object from tensorflow.python.keras.utils.generic_utils import to_list from tensorflow.python.keras.utils.losses_utils import squeeze_or_expand_dimensions from tensorflow.python.ops import array_ops +from tensorflow.python.ops import confusion_matrix from tensorflow.python.ops import init_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import nn @@ -346,6 +347,85 @@ class Mean(Metric): return math_ops.div_no_nan(self.total, self.count) +class MeanRelativeError(Mean): + """Computes the mean relative error by normalizing with the given values. + + This metric creates two local variables, `total` and `count` that are used to + compute the mean relative absolute error. This average is weighted by + `sample_weight`, and it is ultimately returned as `mean_relative_error`: + an idempotent operation that simply divides `total` by `count`. + + If `sample_weight` is `None`, weights default to 1. + Use `sample_weight` of 0 to mask values. + + Usage: + + ```python + m = tf.keras.metrics.MeanRelativeError(normalizer=[1, 3, 2, 3]) + m.update_state([1, 3, 2, 3], [2, 4, 6, 8]) + + # metric = mean(|y_pred - y_true| / normalizer) + # = mean([1, 1, 4, 5] / [1, 3, 2, 3]) = mean([1, 1/3, 2, 5/3]) + # = 5/4 = 1.25 + print('Final result: ', m.result().numpy()) # Final result: 1.25 + ``` + + Usage with tf.keras API: + + ```python + model = keras.models.Model(inputs, outputs) + model.compile( + 'sgd', + loss='mse', + metrics=[tf.keras.metrics.MeanRelativeError(normalizer=[1, 3])]) + ``` + """ + + def __init__(self, normalizer, name=None, dtype=None): + """Creates a `MeanRelativeError` instance. + + Args: + normalizer: The normalizer values with same shape as predictions. + name: (Optional) string name of the metric instance. + dtype: (Optional) data type of the metric result. + """ + super(MeanRelativeError, self).__init__(name=name, dtype=dtype) + normalizer = math_ops.cast(normalizer, self._dtype) + self.normalizer = normalizer + + def update_state(self, y_true, y_pred, sample_weight=None): + """Accumulates metric statistics. + + Args: + y_true: The ground truth values. + y_pred: The predicted values. + sample_weight: Optional weighting of each example. Defaults to 1. Can be a + `Tensor` whose rank is either 0, or the same rank as `y_true`, and must + be broadcastable to `y_true`. + + Returns: + Update op. + """ + y_true = math_ops.cast(y_true, self._dtype) + y_pred = math_ops.cast(y_pred, self._dtype) + y_pred, y_true, sample_weight = squeeze_or_expand_dimensions( + y_pred, y_true, sample_weight) + + y_pred, self.normalizer = confusion_matrix.remove_squeezable_dimensions( + y_pred, self.normalizer) + y_pred.shape.assert_is_compatible_with(y_pred.shape) + relative_errors = math_ops.div_no_nan( + math_ops.abs(y_true - y_pred), self.normalizer) + + return super(MeanRelativeError, self).update_state( + relative_errors, sample_weight=sample_weight) + + def get_config(self): + config = {'normalizer': self.normalizer} + base_config = super(MeanRelativeError, self).get_config() + return dict(list(base_config.items()) + list(config.items())) + + class MeanMetricWrapper(Mean): """Wraps a stateless metric function with the Mean metric.""" diff --git a/tensorflow/python/keras/metrics_test.py b/tensorflow/python/keras/metrics_test.py index a02b95bb46..4f8406f0e3 100644 --- a/tensorflow/python/keras/metrics_test.py +++ b/tensorflow/python/keras/metrics_test.py @@ -1603,6 +1603,62 @@ class KullbackLeiblerDivergenceTest(test.TestCase): self.assertAllClose(self.evaluate(result), expected_result, atol=1e-3) +@test_util.run_all_in_graph_and_eager_modes +class MeanRelativeErrorTest(test.TestCase): + + def test_config(self): + normalizer = constant_op.constant([1, 3], dtype=dtypes.float32) + mre_obj = metrics.MeanRelativeError(normalizer=normalizer, name='mre') + self.assertEqual(mre_obj.name, 'mre') + self.assertArrayNear(self.evaluate(mre_obj.normalizer), [1, 3], 1e-1) + + mre_obj2 = metrics.MeanRelativeError.from_config(mre_obj.get_config()) + self.assertEqual(mre_obj2.name, 'mre') + self.assertArrayNear(self.evaluate(mre_obj2.normalizer), [1, 3], 1e-1) + + def test_unweighted(self): + np_y_pred = np.asarray([2, 4, 6, 8], dtype=np.float32) + np_y_true = np.asarray([1, 3, 2, 3], dtype=np.float32) + expected_error = np.mean( + np.divide(np.absolute(np_y_pred - np_y_true), np_y_true)) + + y_pred = constant_op.constant(np_y_pred, shape=(1, 4), dtype=dtypes.float32) + y_true = constant_op.constant(np_y_true, shape=(1, 4)) + + mre_obj = metrics.MeanRelativeError(normalizer=y_true) + self.evaluate(variables.variables_initializer(mre_obj.variables)) + + result = mre_obj(y_true, y_pred) + self.assertAllClose(self.evaluate(result), expected_error, atol=1e-3) + + def test_weighted(self): + np_y_pred = np.asarray([2, 4, 6, 8], dtype=np.float32) + np_y_true = np.asarray([1, 3, 2, 3], dtype=np.float32) + sample_weight = np.asarray([0.2, 0.3, 0.5, 0], dtype=np.float32) + rel_errors = np.divide(np.absolute(np_y_pred - np_y_true), np_y_true) + expected_error = np.sum(rel_errors * sample_weight) + + y_pred = constant_op.constant(np_y_pred, dtype=dtypes.float32) + y_true = constant_op.constant(np_y_true) + + mre_obj = metrics.MeanRelativeError(normalizer=y_true) + self.evaluate(variables.variables_initializer(mre_obj.variables)) + + result = mre_obj( + y_true, y_pred, sample_weight=constant_op.constant(sample_weight)) + self.assertAllClose(self.evaluate(result), expected_error, atol=1e-3) + + def test_zero_normalizer(self): + y_pred = constant_op.constant([2, 4], dtype=dtypes.float32) + y_true = constant_op.constant([1, 3]) + + mre_obj = metrics.MeanRelativeError(normalizer=array_ops.zeros_like(y_true)) + self.evaluate(variables.variables_initializer(mre_obj.variables)) + + result = mre_obj(y_true, y_pred) + self.assertEqual(self.evaluate(result), 0) + + def _get_model(compile_metrics): model_layers = [ layers.Dense(3, activation='relu', kernel_initializer='ones'), -- GitLab From dff89fe60f5bbf1aa6c124fe295c9af3d9e3a868 Mon Sep 17 00:00:00 2001 From: Tong Shen Date: Tue, 8 Jan 2019 10:18:15 -0800 Subject: [PATCH 487/622] Support outside compilation in function call. PiperOrigin-RevId: 228352466 --- tensorflow/compiler/jit/BUILD | 1 + .../jit/encapsulate_subgraphs_pass_test.cc | 98 ++++++-- .../jit/extract_outside_compilation_pass.cc | 196 +++++++++++++--- .../jit/extract_outside_compilation_pass.h | 4 +- .../extract_outside_compilation_pass_test.cc | 216 +++++++++++++++++- tensorflow/compiler/tf2xla/graph_compiler.cc | 28 ++- .../compiler/tf2xla/side_effect_util.cc | 4 + 7 files changed, 475 insertions(+), 72 deletions(-) diff --git a/tensorflow/compiler/jit/BUILD b/tensorflow/compiler/jit/BUILD index b9a87ba296..ba9fa93654 100644 --- a/tensorflow/compiler/jit/BUILD +++ b/tensorflow/compiler/jit/BUILD @@ -634,6 +634,7 @@ tf_cc_test( "//tensorflow/core:framework_internal", "//tensorflow/core:lib", "//tensorflow/core:protos_all_cc", + "//tensorflow/core:session_options", "//tensorflow/core:test", "//tensorflow/core:test_main", "//tensorflow/core:testlib", diff --git a/tensorflow/compiler/jit/encapsulate_subgraphs_pass_test.cc b/tensorflow/compiler/jit/encapsulate_subgraphs_pass_test.cc index 8617beec00..1f8ec09e19 100644 --- a/tensorflow/compiler/jit/encapsulate_subgraphs_pass_test.cc +++ b/tensorflow/compiler/jit/encapsulate_subgraphs_pass_test.cc @@ -25,6 +25,8 @@ limitations under the License. #include "tensorflow/compiler/jit/encapsulate_util.h" #include "tensorflow/compiler/jit/extract_outside_compilation_pass.h" #include "tensorflow/compiler/tf2xla/side_effect_util.h" +#include "tensorflow/core/common_runtime/device_factory.h" +#include "tensorflow/core/common_runtime/function.h" #include "tensorflow/core/framework/function_testlib.h" #include "tensorflow/core/framework/graph_to_functiondef.h" #include "tensorflow/core/graph/graph_constructor.h" @@ -32,6 +34,8 @@ limitations under the License. #include "tensorflow/core/lib/core/errors.h" #include "tensorflow/core/lib/core/status_test_util.h" #include "tensorflow/core/platform/test.h" +#include "tensorflow/core/public/session_options.h" +#include "tensorflow/core/public/version.h" #include "tensorflow/core/util/equal_graph_def.h" namespace tensorflow { @@ -513,6 +517,18 @@ Status Encapsulate(GraphDef* graphdef, FunctionDefLibrary* library, s = PerformStaticShapeInferenceBeforeEncapsulation(graph.get()); if (!s.ok()) return s; + // Create FunctionLibraryRuntime. + SessionOptions session_options; + std::vector> devices; + TF_CHECK_OK(DeviceFactory::AddDevices( + session_options, "/job:localhost/replica:0/task:0", &devices)); + OptimizerOptions opts; + auto device_mgr = absl::make_unique(std::move(devices)); + auto pflr = absl::make_unique( + device_mgr.get(), Env::Default(), TF_GRAPH_DEF_VERSION, lib_def.get(), + opts, /*default_thread_pool=*/nullptr, /*cluster_flr=*/nullptr); + auto flr = pflr->GetFLR("/job:localhost/replica:0/task:0/cpu:0"); + std::unique_ptr graph_out; s = EncapsulateSubgraphsInFunctions( "_encapsulate", /*outside_compilation_attribute=*/"", *graph, @@ -538,7 +554,7 @@ Status Encapsulate(GraphDef* graphdef, FunctionDefLibrary* library, std::map{}}); } s = ExtractOutsideCompilation("_encapsulate", "_outside", clusters, - graph_out.get(), lib_def.get()); + graph_out.get(), flr, lib_def.get()); if (!s.ok()) return s; GraphDef graphdef_out; @@ -941,7 +957,9 @@ TEST(EncapsulateSubgraphsTest, OneFunctionOneOutside) { {"key", "host_compute_channel_F1_O1"}, {"shape_inference_graph", shape_inference_graph}, {"shapes", absl::Span({})}, - {"_outside_compilation_subgraph", "O1"}}, + {"_outside_compilation_subgraph", "O1"}, + {"_xla_token_input_nodes", + absl::Span({"_xla_token_arg_node"})}}, {"c"}}, }, {{"f_0_retval_retval", "F:o:0"}}); @@ -1101,7 +1119,9 @@ TEST(EncapsulateSubgraphsTest, OneFunctionTwoOutside) { {"key", "host_compute_channel_F1_O2"}, {"shape_inference_graph", shape_inference_graph2}, {"shapes", absl::Span({})}, - {"_outside_compilation_subgraph", "O2"}}, + {"_outside_compilation_subgraph", "O2"}, + {"_xla_token_input_nodes", + absl::Span({"_xla_token_arg_node"})}}, {"F"}}, {{"outside_compilation_O1_host_compute"}, "XlaHostCompute", @@ -1112,7 +1132,9 @@ TEST(EncapsulateSubgraphsTest, OneFunctionTwoOutside) { {"key", "host_compute_channel_F1_O1"}, {"shape_inference_graph", shape_inference_graph1}, {"shapes", absl::Span({})}, - {"_outside_compilation_subgraph", "O1"}}, + {"_outside_compilation_subgraph", "O1"}, + {"_xla_token_input_nodes", + absl::Span({"_xla_token_arg_node"})}}, {"D"}}, }, {{"g_0_retval_retval", "outside_compilation_O2_host_compute:outputs:0"}, @@ -1244,7 +1266,9 @@ TEST(EncapsulateSubgraphsTest, TwoFunctionsTwoOutside) { {"shape_inference_graph", NameAttrList()}, {"shapes", absl::Span({shape_proto_expected})}, - {"_outside_compilation_subgraph", "O1"}}, + {"_outside_compilation_subgraph", "O1"}, + {"_xla_token_input_nodes", + absl::Span({"_xla_token_arg_node"})}}, {"D"}}, }, {{"e_0_retval_retval", "outside_compilation_O1_host_compute:outputs:0"}, @@ -1269,7 +1293,9 @@ TEST(EncapsulateSubgraphsTest, TwoFunctionsTwoOutside) { {"shape_inference_graph", NameAttrList()}, {"shapes", absl::Span({shape_proto_expected})}, - {"_outside_compilation_subgraph", "O1"}}}, + {"_outside_compilation_subgraph", "O1"}, + {"_xla_token_input_nodes", + absl::Span({"_xla_token_arg_node"})}}}, }, {{"g_0_retval_retval", "G:o:0"}, {"i_0_retval_retval", "I:o:0"}}); @@ -1397,7 +1423,9 @@ TEST(EncapsulateSubgraphsTest, TwoFunctionsTwoOutsideDependencyFromOutside) { {"shape_inference_graph", NameAttrList()}, {"shapes", absl::Span({shape_proto_expected})}, - {"_outside_compilation_subgraph", "O1"}}, + {"_outside_compilation_subgraph", "O1"}, + {"_xla_token_input_nodes", + absl::Span({"_xla_token_arg_node"})}}, {"D"}}, }, {{"f_0_retval_retval", "F:o:0"}}); @@ -1419,7 +1447,9 @@ TEST(EncapsulateSubgraphsTest, TwoFunctionsTwoOutsideDependencyFromOutside) { {"shape_inference_graph", NameAttrList()}, {"shapes", absl::Span({shape_proto_expected})}, - {"_outside_compilation_subgraph", "O1"}}}, + {"_outside_compilation_subgraph", "O1"}, + {"_xla_token_input_nodes", + absl::Span({"_xla_token_arg_node"})}}}, }, {{"i_0_retval_retval", "I:o:0"}}); @@ -1527,7 +1557,9 @@ TEST(EncapsulateSubgraphsTest, OutsideCompilationNoInputs) { {"shape_inference_graph", NameAttrList()}, {"shapes", absl::Span({shape_proto_expected})}, - {"_outside_compilation_subgraph", "O1"}}}, + {"_outside_compilation_subgraph", "O1"}, + {"_xla_token_input_nodes", + absl::Span({"_xla_token_arg_node"})}}}, }, {{"f_0_retval_retval", "F:o:0"}}); @@ -1615,7 +1647,9 @@ TEST(EncapsulateSubgraphsTest, OutsideCompilationControlInput) { {"shape_inference_graph", NameAttrList()}, {"shapes", absl::Span({shape_proto_expected})}, - {"_outside_compilation_subgraph", "O1"}}, + {"_outside_compilation_subgraph", "O1"}, + {"_xla_token_input_nodes", + absl::Span({"_xla_token_arg_node"})}}, {"D"}}, }, {{"f_0_retval_retval", "F:o:0"}}); @@ -1716,7 +1750,9 @@ TEST(EncapsulateSubgraphsTest, OutsideCompilationNoOutputs) { {"key", "host_compute_channel_F1_O1"}, {"shape_inference_graph", shape_inference_graph}, {"shapes", absl::Span({})}, - {"_outside_compilation_subgraph", "O1"}}}, + {"_outside_compilation_subgraph", "O1"}, + {"_xla_token_input_nodes", + absl::Span({"_xla_token_arg_node"})}}}, }, {{"e_0_retval_retval", "outside_compilation_O1_host_compute:outputs:0"}, {"f_0_retval_retval", "F:o:0"}}); @@ -1821,7 +1857,9 @@ TEST(EncapsulateSubgraphsTest, OutsideCompilationControlOutput) { {"key", "host_compute_channel_F1_O1"}, {"shape_inference_graph", shape_inference_graph}, {"shapes", absl::Span({})}, - {"_outside_compilation_subgraph", "O1"}}}, + {"_outside_compilation_subgraph", "O1"}, + {"_xla_token_input_nodes", + absl::Span({"_xla_token_arg_node"})}}}, }, {{"e_0_retval_retval", "outside_compilation_O1_host_compute:outputs:0"}, {"f_0_retval_retval", "F:o:0"}}); @@ -1949,7 +1987,9 @@ TEST(EncapsulateSubgraphsTest, {"key", "host_compute_channel_F1_O1"}, {"shape_inference_graph", shape_inference_graph1}, {"shapes", absl::Span({})}, - {"_outside_compilation_subgraph", "O1"}}}, + {"_outside_compilation_subgraph", "O1"}, + {"_xla_token_input_nodes", + absl::Span({"_xla_token_arg_node"})}}}, {{"outside_compilation_O2_host_compute"}, "XlaHostCompute", {"F:o:0"}, @@ -1959,7 +1999,9 @@ TEST(EncapsulateSubgraphsTest, {"key", "host_compute_channel_F1_O2"}, {"shape_inference_graph", shape_inference_graph2}, {"shapes", absl::Span({})}, - {"_outside_compilation_subgraph", "O2"}}}, + {"_outside_compilation_subgraph", "O2"}, + {"_xla_token_input_nodes", + absl::Span({"_xla_token_arg_node"})}}}, }, {{"e_0_retval_retval", "outside_compilation_O1_host_compute:outputs:0"}, {"h_0_retval_retval", "H:o:0"}}); @@ -2082,7 +2124,9 @@ TEST(EncapsulateSubgraphsTest, {"key", "host_compute_channel_F1_O2"}, {"shape_inference_graph", NameAttrList()}, {"shapes", absl::Span({})}, - {"_outside_compilation_subgraph", "O2"}}}, + {"_outside_compilation_subgraph", "O2"}, + {"_xla_token_input_nodes", + absl::Span({"_xla_token_arg_node"})}}}, {{"outside_compilation_O1_host_compute"}, "XlaHostCompute", {"D:o:0"}, @@ -2092,7 +2136,9 @@ TEST(EncapsulateSubgraphsTest, {"key", "host_compute_channel_F1_O1"}, {"shape_inference_graph", shape_inference_graph}, {"shapes", absl::Span({})}, - {"_outside_compilation_subgraph", "O1"}}}, + {"_outside_compilation_subgraph", "O1"}, + {"_xla_token_input_nodes", + absl::Span({"_xla_token_arg_node"})}}}, }, {{"e_0_retval_retval", "outside_compilation_O1_host_compute:outputs:0"}, {"h_0_retval_retval", "H:o:0"}}); @@ -2214,7 +2260,9 @@ TEST(EncapsulateSubgraphsTest, OutsideCompilationClusterDependency) { {"key", "host_compute_channel_F1_O1"}, {"shape_inference_graph", shape_inference_graph}, {"shapes", absl::Span({})}, - {"_outside_compilation_subgraph", "O1"}}}, + {"_outside_compilation_subgraph", "O1"}, + {"_xla_token_input_nodes", + absl::Span({"_xla_token_arg_node"})}}}, {{"outside_compilation_O2_host_compute"}, "XlaHostCompute", {"D:o:0"}, @@ -2224,7 +2272,9 @@ TEST(EncapsulateSubgraphsTest, OutsideCompilationClusterDependency) { {"key", "host_compute_channel_F1_O2"}, {"shape_inference_graph", NameAttrList()}, {"shapes", absl::Span({})}, - {"_outside_compilation_subgraph", "O2"}}, + {"_outside_compilation_subgraph", "O2"}, + {"_xla_token_input_nodes", + absl::Span({"_xla_token_arg_node"})}}, {}}, {{"outside_compilation_O3_host_compute"}, "XlaHostCompute", @@ -2235,7 +2285,9 @@ TEST(EncapsulateSubgraphsTest, OutsideCompilationClusterDependency) { {"key", "host_compute_channel_F1_O3"}, {"shape_inference_graph", NameAttrList()}, {"shapes", absl::Span({})}, - {"_outside_compilation_subgraph", "O3"}}, + {"_outside_compilation_subgraph", "O3"}, + {"_xla_token_input_nodes", + absl::Span({"_xla_token_arg_node"})}}, {}}}, {{"e_0_retval_retval", "outside_compilation_O1_host_compute:outputs:0"}, {"h_0_retval_retval", "H:o:0"}}); @@ -2354,7 +2406,9 @@ TEST(EncapsulateSubgraphsTest, OutsideCompilationNoInputsOrOutputs) { {"key", "host_compute_channel_F1_O1"}, {"shape_inference_graph", shape_inference_graph}, {"shapes", absl::Span({})}, - {"_outside_compilation_subgraph", "O1"}}}, + {"_outside_compilation_subgraph", "O1"}, + {"_xla_token_input_nodes", + absl::Span({"_xla_token_arg_node"})}}}, }, {{"e_0_retval_retval", "outside_compilation_O1_host_compute:outputs:0"}, {"f_0_retval_retval", "F:o:0"}}); @@ -2465,7 +2519,9 @@ TEST(EncapsulateSubgraphsTest, OutsideCompilationShapeInference) { {"key", "host_compute_channel_F1_O1"}, {"shape_inference_graph", shape_inference_graph}, {"shapes", absl::Span({})}, - {"_outside_compilation_subgraph", "O1"}}, + {"_outside_compilation_subgraph", "O1"}, + {"_xla_token_input_nodes", + absl::Span({"_xla_token_arg_node"})}}, {"c"}}, }, {{"f_0_retval_retval", "F:o:0"}}); diff --git a/tensorflow/compiler/jit/extract_outside_compilation_pass.cc b/tensorflow/compiler/jit/extract_outside_compilation_pass.cc index 8b01768c49..2a770c527b 100644 --- a/tensorflow/compiler/jit/extract_outside_compilation_pass.cc +++ b/tensorflow/compiler/jit/extract_outside_compilation_pass.cc @@ -30,6 +30,7 @@ limitations under the License. #include "tensorflow/core/framework/tensor_shape.pb.h" #include "tensorflow/core/graph/algorithm.h" #include "tensorflow/core/lib/core/errors.h" +#include "tensorflow/core/lib/gtl/cleanup.h" namespace tensorflow { @@ -308,6 +309,10 @@ xla::StatusOr BuildXlaHostComputeNodeDef( host_compute_builder.Attr("tpu_core", core); } + // Set input tokens. + host_compute_builder.Attr(kXlaTokenInputNodesAttrName, + std::vector{kXlaTokenArgNodeName}); + // Populate inputs. std::vector input_dtypes; TF_RETURN_IF_ERROR(GetNodeAttr(call_node->attrs(), "Tinputs", &input_dtypes)); @@ -398,8 +403,8 @@ Status ReplaceOrRemoveOutsideCompilationCallNode( } // Resets "device_ordinal" attr to placeholder value for related nodes -// (XlaRecvAtHost nodes; XlaSendFromHost nodes; If nodes containing -// XlaRecvAtHost/XlaSendFromHost). +// (XlaRecvAtHost nodes; XlaSendFromHost nodes; If/While/FuncCall nodes +// containing XlaRecvAtHost/XlaSendFromHost). Status ResetDeviceOrdinalToPlaceholderValue(Graph* g) { AttrValue device_ordinal_value; device_ordinal_value.set_placeholder("device_ordinal"); @@ -429,6 +434,10 @@ Status ResetDeviceOrdinalToPlaceholderValue(Graph* g) { n->ClearAttr(attr_name); n->AddAttr(attr_name, branch_func); } + } else if (HasNodeAttr(n->def(), "device_ordinal")) { + // Function call node containing outside compilation. + n->ClearAttr("device_ordinal"); + n->AddAttr("device_ordinal", device_ordinal_value); } else { return errors::Internal("Unknown node marked with ", kXlaHasHostTransferAttrName, ": ", @@ -1217,20 +1226,129 @@ Status BuildHostGraphForWhileNode( return Status::OK(); } +// Builds host graph for func call nodes. +Status BuildHostGraphForFuncCallNode(const string& func_call_node_name, + const string& xla_cluster_name, + const string& func_call_host_func_name, + const string& host_graph_func_name, + FunctionLibraryDefinition* fld) { + Graph host_graph(fld); + AttrValue device_ordinal_value; + device_ordinal_value.set_placeholder("device_ordinal"); + + // Step 1: add key placeholder node. + TF_ASSIGN_OR_RETURN( + Node * key_placeholder, + AddHostComputeKeyPlaceholder(xla_cluster_name, &host_graph)); + + // Step 2: rewrite `host_func_name`, replace key placeholder with an _Arg + // node. + TF_RETURN_IF_ERROR(ReplaceKeyPlaceholderWithArgNode( + xla_cluster_name, func_call_host_func_name, fld)); + + // Step 3: build a function call node with `host_func_name`, with + // `key_placeholder` as input. + NodeDefBuilder call_builder(absl::StrCat("oc_call_", func_call_node_name), + func_call_host_func_name, fld); + call_builder.Input(key_placeholder->name(), 0, DT_STRING); + call_builder.Attr("device_ordinal", device_ordinal_value); + call_builder.Attr(kXlaHasHostTransferAttrName, true); + NodeDef call_def; + TF_RETURN_IF_ERROR(call_builder.Finalize(&call_def)); + Status s; + Node* call_node = host_graph.AddNode(call_def, &s); + TF_RETURN_IF_ERROR(s); + host_graph.AddEdge(key_placeholder, 0, call_node, 0); + + // Convert `host_graph` to function, and add a "device_ordinal" attr. + FunctionDef oc_host_graph_fdef; + TF_RETURN_IF_ERROR(GraphToFunctionDef(host_graph, host_graph_func_name, + &oc_host_graph_fdef)); + if (fld->Find(host_graph_func_name)) { + TF_RETURN_IF_ERROR( + fld->ReplaceFunction(host_graph_func_name, oc_host_graph_fdef)); + } else { + TF_RETURN_IF_ERROR(fld->AddFunctionDef(oc_host_graph_fdef)); + } + + return Status::OK(); +} + Status ExtractOutsideCompilationForNodesWithAssociatedFunctions( Graph* g, const string& xla_cluster_attr_name, const string& outside_compilation_attr_name, const string& xla_cluster_name, - const std::map& host_compute_core, + const std::map& host_compute_core, FunctionLibraryRuntime* flr, FunctionLibraryDefinition* fld, std::vector* host_graphs, std::vector* shape_inference_graphs, bool* has_outside_compilation) { - std::vector if_nodes, while_nodes; + std::vector if_nodes, while_nodes, func_call_nodes; for (Node* n : g->nodes()) { if (n->type_string() == "If") { if_nodes.push_back(n); } else if (n->type_string() == "While") { while_nodes.push_back(n); + } else if (fld->Contains(n->type_string())) { + func_call_nodes.push_back(n); + } else if (n->type_string() == FunctionLibraryDefinition::kGradientOp) { + // Only gradient for user-defined function should be considered as + // function call node. + NameAttrList original_func; + TF_RETURN_IF_ERROR(GetNodeAttr( + n->def(), FunctionLibraryDefinition::kFuncAttr, &original_func)); + if (fld->Contains(original_func.name())) { + func_call_nodes.push_back(n); + } + } + } + + for (Node* n : func_call_nodes) { + // Extract outside compilation for the function call. + bool func_has_outside_compilation = false; + NameAttrList func; + func.set_name(n->type_string()); + typedef protobuf::Map AttrMap; + *func.mutable_attr() = AttrMap(n->attrs().begin(), n->attrs().end()); + string new_func_name = absl::StrCat(n->name(), "_oc"); + string host_func_name = absl::StrCat("oc_func_call_host_", n->name()); + TF_RETURN_IF_ERROR(ExtractOutsideCompilationForFunction( + xla_cluster_attr_name, outside_compilation_attr_name, xla_cluster_name, + func, new_func_name, host_func_name, host_compute_core, flr, fld, + shape_inference_graphs, &func_has_outside_compilation)); + + // If the function call does not have outside compilation, nothing to do. + if (!func_has_outside_compilation) { + continue; } + + *has_outside_compilation = true; + + // Change `n` to call the new function directly. + NodeDefBuilder replace_builder(n->name(), new_func_name, fld); + for (const Edge* e : n->in_edges()) { + if (e->IsControlEdge()) { + continue; + } + replace_builder.Input(e->src()->name(), e->src_output(), + e->src()->output_type(e->src_output())); + } + for (const auto& attr : n->attrs()) { + replace_builder.Attr(attr.first, attr.second); + } + NodeDef replace_def; + TF_RETURN_IF_ERROR(replace_builder.Finalize(&replace_def)); + TF_ASSIGN_OR_RETURN(Node * replace, ReplaceNode(g, n, replace_def)); + replace->AddAttr(kXlaTokenInputNodesAttrName, + std::vector{kXlaTokenArgNodeName}); + + // Build host side graph for the function call. + string oc_host_graph_name = + absl::StrCat("oc_func_host_graph_", replace->name()); + TF_RETURN_IF_ERROR( + BuildHostGraphForFuncCallNode(replace->name(), xla_cluster_name, + host_func_name, oc_host_graph_name, fld)); + + // Record the host graph. + host_graphs->push_back(oc_host_graph_name); } for (Node* n : if_nodes) { @@ -1251,12 +1369,12 @@ Status ExtractOutsideCompilationForNodesWithAssociatedFunctions( TF_RETURN_IF_ERROR(ExtractOutsideCompilationForFunction( xla_cluster_attr_name, outside_compilation_attr_name, xla_cluster_name, then_branch, then_branch_xla_func_name, then_branch_host_func_name, - host_compute_core, fld, shape_inference_graphs, + host_compute_core, flr, fld, shape_inference_graphs, &then_branch_has_outside_compilation)); TF_RETURN_IF_ERROR(ExtractOutsideCompilationForFunction( xla_cluster_attr_name, outside_compilation_attr_name, xla_cluster_name, else_branch, else_branch_xla_func_name, else_branch_host_func_name, - host_compute_core, fld, shape_inference_graphs, + host_compute_core, flr, fld, shape_inference_graphs, &else_branch_has_outside_compilation)); // If then/else branch do not have outside compilation, nothing to do. @@ -1316,12 +1434,12 @@ Status ExtractOutsideCompilationForNodesWithAssociatedFunctions( body_xla_func_name = absl::StrCat(body.name(), "_oc"); TF_RETURN_IF_ERROR(ExtractOutsideCompilationForFunction( xla_cluster_attr_name, outside_compilation_attr_name, xla_cluster_name, - cond, cond_xla_func_name, cond_host_func_name, host_compute_core, fld, - shape_inference_graphs, &cond_has_outside_compilation)); + cond, cond_xla_func_name, cond_host_func_name, host_compute_core, flr, + fld, shape_inference_graphs, &cond_has_outside_compilation)); TF_RETURN_IF_ERROR(ExtractOutsideCompilationForFunction( xla_cluster_attr_name, outside_compilation_attr_name, xla_cluster_name, - body, body_xla_func_name, body_host_func_name, host_compute_core, fld, - shape_inference_graphs, &body_has_outside_compilation)); + body, body_xla_func_name, body_host_func_name, host_compute_core, flr, + fld, shape_inference_graphs, &body_has_outside_compilation)); // If cond/body do not have outside compilation, nothing to do. if (!cond_has_outside_compilation && !body_has_outside_compilation) { @@ -1469,17 +1587,27 @@ Status ExtractOutsideCompilationForFunction( const string& outside_compilation_attr_name, const string& xla_cluster_name, const NameAttrList& func_name_attrs, const string& new_func_name, const string& host_graph_func_name, - const std::map& host_compute_core, + const std::map& host_compute_core, FunctionLibraryRuntime* flr, FunctionLibraryDefinition* fld, std::vector* shape_inference_graphs, bool* has_outside_compilation) { + // Convert the function to graph. const string& func_name = func_name_attrs.name(); - const FunctionDef* fdef = fld->Find(func_name); - if (!fdef) { - return errors::Internal("Cannot find function ", func_name); - } + FunctionLibraryRuntime::Handle handle; + TF_RETURN_IF_ERROR( + flr->Instantiate(func_name, AttrSlice(&func_name_attrs.attr()), &handle)); + Status ret_status = Status::OK(); + auto cleanup_handle = gtl::MakeCleanup([&]() { + auto s = flr->ReleaseHandle(handle); + if (!s.ok()) { + ret_status.Update(s); + } + }); + const FunctionBody* fbody = flr->GetFunctionBody(handle); + + // Check if we have outside compilation nodes. *has_outside_compilation = false; - for (auto& node_def : fdef->node_def()) { - if (HasNodeAttr(node_def, outside_compilation_attr_name)) { + for (Node* n : fbody->graph->nodes()) { + if (HasNodeAttr(n->def(), outside_compilation_attr_name)) { *has_outside_compilation = true; break; } @@ -1487,16 +1615,6 @@ Status ExtractOutsideCompilationForFunction( // We cannot early return here, because we might have outside compilation in // If/While function body. - // Convert the function to graph. - FunctionBody* fbody = nullptr; - TF_RETURN_IF_ERROR(FunctionDefToBodyHelper( - *fld->Find(func_name), AttrSlice(&func_name_attrs.attr()), fld, - [&](const string& op, const OpDef** sig) { - return fld->LookUpOpDef(op, sig); - }, - &fbody)); - std::unique_ptr fbody_deleter(fbody); - // Preprocess edges between different outside compilations. They will be // restored in `ConstructHostGraph()`. TF_RETURN_IF_ERROR(PreprocessEdgesBetweenOutsideCompilations( @@ -1553,16 +1671,11 @@ Status ExtractOutsideCompilationForFunction( TF_RETURN_IF_ERROR(ReplaceOrRemoveOutsideCompilationCallNode( graph_out.get(), n, host_compute_core)); } - if (VLOG_IS_ON(4)) { - dump_graph::DumpGraphToFile( - absl::StrCat("extract_outside_compilation_for_func_after_", func_name), - *graph_out, fld); - } // Handle nodes with associated functions. TF_RETURN_IF_ERROR(ExtractOutsideCompilationForNodesWithAssociatedFunctions( graph_out.get(), xla_cluster_attr_name, outside_compilation_attr_name, - xla_cluster_name, host_compute_core, fld, + xla_cluster_name, host_compute_core, flr, fld, &outside_compilation_host_graphs, shape_inference_graphs, has_outside_compilation)); @@ -1580,20 +1693,31 @@ Status ExtractOutsideCompilationForFunction( FunctionDef updated_fdef; TF_RETURN_IF_ERROR( GraphToFunctionDef(*graph_out, new_func_name, &updated_fdef)); + const FunctionDef* original_fdef = fld->Find(func_name); + if (original_fdef) { + for (const auto& attr : original_fdef->attr()) { + (*updated_fdef.mutable_attr())[attr.first] = attr.second; + } + } if (fld->Find(new_func_name)) { TF_RETURN_IF_ERROR(fld->ReplaceFunction(new_func_name, updated_fdef)); } else { TF_RETURN_IF_ERROR(fld->AddFunctionDef(updated_fdef)); } + if (VLOG_IS_ON(4)) { + dump_graph::DumpGraphToFile( + absl::StrCat("extract_outside_compilation_for_func_after_", func_name), + *graph_out, fld); + } - return Status::OK(); + return ret_status; } Status ExtractOutsideCompilation( const string& xla_cluster_attr_name, const string& outside_compilation_attr_name, const std::unordered_map& clusters, Graph* g, - FunctionLibraryDefinition* fld) { + FunctionLibraryRuntime* flr, FunctionLibraryDefinition* fld) { if (VLOG_IS_ON(4)) { dump_graph::DumpGraphToFile("extract_outside_compilation_before", *g, fld); } @@ -1610,7 +1734,7 @@ Status ExtractOutsideCompilation( TF_RETURN_IF_ERROR(ExtractOutsideCompilationForFunction( xla_cluster_attr_name, outside_compilation_attr_name, xla_cluster_name, func_name_attrs, func_name_attrs.name(), host_graph_func_name, - host_compute_core, fld, &shape_inference_graphs, + host_compute_core, flr, fld, &shape_inference_graphs, &has_outside_compilation)); TF_RETURN_IF_ERROR( ExpandHostGraphIntoMainGraph(g, fld, host_graph_func_name, n)); diff --git a/tensorflow/compiler/jit/extract_outside_compilation_pass.h b/tensorflow/compiler/jit/extract_outside_compilation_pass.h index e07e7c5dd0..d64cc2a103 100644 --- a/tensorflow/compiler/jit/extract_outside_compilation_pass.h +++ b/tensorflow/compiler/jit/extract_outside_compilation_pass.h @@ -89,7 +89,7 @@ Status ExtractOutsideCompilationForFunction( const string& outside_compilation_attr_name, const string& xla_cluster_name, const NameAttrList& func_name_attrs, const string& new_func_name, const string& host_graph_func_name, - const std::map& host_compute_core, + const std::map& host_compute_core, FunctionLibraryRuntime* flr, FunctionLibraryDefinition* fld, std::vector* shape_inference_graphs, bool* has_outside_compilation); @@ -101,7 +101,7 @@ Status ExtractOutsideCompilation( const string& xla_cluster_attr_name, const string& outside_compilation_attr_name, const std::unordered_map& clusters, Graph* g, - FunctionLibraryDefinition* fld); + FunctionLibraryRuntime* flr, FunctionLibraryDefinition* fld); } // namespace tensorflow diff --git a/tensorflow/compiler/jit/extract_outside_compilation_pass_test.cc b/tensorflow/compiler/jit/extract_outside_compilation_pass_test.cc index e9a89e34e0..7c3a24feff 100644 --- a/tensorflow/compiler/jit/extract_outside_compilation_pass_test.cc +++ b/tensorflow/compiler/jit/extract_outside_compilation_pass_test.cc @@ -23,6 +23,7 @@ limitations under the License. #include "tensorflow/cc/ops/standard_ops.h" #include "tensorflow/compiler/jit/encapsulate_util.h" #include "tensorflow/compiler/xla/test.h" +#include "tensorflow/core/common_runtime/device_factory.h" #include "tensorflow/core/common_runtime/function.h" #include "tensorflow/core/framework/common_shape_fns.h" #include "tensorflow/core/framework/function.h" @@ -31,6 +32,8 @@ limitations under the License. #include "tensorflow/core/framework/tensor_shape.h" #include "tensorflow/core/framework/tensor_shape.pb.h" #include "tensorflow/core/platform/test.h" +#include "tensorflow/core/public/session_options.h" +#include "tensorflow/core/public/version.h" namespace tensorflow { @@ -222,7 +225,42 @@ TEST(RewriteOutsideCompilationSubgraphFnTest, ShapesInferred) { EXPECT_EQ(shapes[0].dim_size(), 1); } -TEST(ExtractOutsideCompilationForFunctionTest, Basic) { +class ExtractOutsideCompilationForFunctionTest : public ::testing::Test { + public: + void SetUp() override { + SessionOptions session_options; + std::vector> devices; + TF_CHECK_OK(DeviceFactory::AddDevices( + session_options, "/job:localhost/replica:0/task:0", &devices)); + device_mgr_ = absl::make_unique(std::move(devices)); + } + + Status ExtractOutsideCompilationTest( + const string &xla_cluster_attr_name, + const string &outside_compilation_attr_name, + const string &xla_cluster_name, const NameAttrList &func_name_attrs, + const string &new_func_name, const string &host_graph_func_name, + const std::map &host_compute_core, + FunctionLibraryDefinition *fld, + std::vector *shape_inference_graphs, + bool *has_outside_compilation) { + OptimizerOptions opts; + pflr_ = absl::make_unique( + device_mgr_.get(), Env::Default(), TF_GRAPH_DEF_VERSION, fld, opts, + /*default_thread_pool=*/nullptr, /*cluster_flr=*/nullptr); + auto flr = pflr_->GetFLR("/job:localhost/replica:0/task:0/cpu:0"); + return ExtractOutsideCompilationForFunction( + xla_cluster_attr_name, outside_compilation_attr_name, xla_cluster_name, + func_name_attrs, new_func_name, host_graph_func_name, host_compute_core, + flr, fld, shape_inference_graphs, has_outside_compilation); + } + + private: + std::unique_ptr device_mgr_; + std::unique_ptr pflr_; +}; + +TEST_F(ExtractOutsideCompilationForFunctionTest, Basic) { // Build the XLA computation func. // "const0" // "identity0" = "const0" (outside compilation cluster "0") @@ -256,7 +294,7 @@ TEST(ExtractOutsideCompilationForFunctionTest, Basic) { NameAttrList name_attrs; name_attrs.set_name("cluster"); *name_attrs.mutable_attr() = attrs; - TF_CHECK_OK(ExtractOutsideCompilationForFunction( + TF_CHECK_OK(ExtractOutsideCompilationTest( "_xla", "_oc", "cluster", name_attrs, "cluster_rewritten", "host_graph", host_compute_core, &fld, &shape_inference_graphs, &has_outside_compilation)); @@ -362,7 +400,7 @@ TEST(ExtractOutsideCompilationForFunctionTest, Basic) { } } -TEST(ExtractOutsideCompilationForFunctionTest, NoHostGraph) { +TEST_F(ExtractOutsideCompilationForFunctionTest, NoHostGraph) { // Build the XLA computation func. // "const0" FunctionDefLibrary fdl; @@ -384,7 +422,7 @@ TEST(ExtractOutsideCompilationForFunctionTest, NoHostGraph) { NameAttrList name_attrs; name_attrs.set_name("cluster"); *name_attrs.mutable_attr() = attrs; - TF_CHECK_OK(ExtractOutsideCompilationForFunction( + TF_CHECK_OK(ExtractOutsideCompilationTest( "_xla", "_oc", "cluster", name_attrs, "cluster_rewritten", "host_graph", host_compute_core, &fld, &shape_inference_graphs, &has_outside_compilation)); @@ -406,7 +444,7 @@ TEST(ExtractOutsideCompilationForFunctionTest, NoHostGraph) { EXPECT_EQ(host_graph->num_nodes(), 2); } -TEST(ExtractOutsideCompilationForFunctionTest, XlaHostComputeRemoved) { +TEST_F(ExtractOutsideCompilationForFunctionTest, XlaHostComputeRemoved) { // Build the XLA computation func. // "const0" // "const1" (outside compilation cluster "0") @@ -432,7 +470,7 @@ TEST(ExtractOutsideCompilationForFunctionTest, XlaHostComputeRemoved) { NameAttrList name_attrs; name_attrs.set_name("cluster"); *name_attrs.mutable_attr() = attrs; - TF_CHECK_OK(ExtractOutsideCompilationForFunction( + TF_CHECK_OK(ExtractOutsideCompilationTest( "_xla", "_oc", "cluster", name_attrs, "cluster_rewritten", "host_graph", host_compute_core, &fld, &shape_inference_graphs, &has_outside_compilation)); @@ -489,7 +527,7 @@ REGISTER_OP("XlaRecvFromHost") .Attr("key: string") .SetIsStateful(); -TEST(ExtractOutsideCompilationForFunctionTest, OutsideCompilationInIf) { +TEST_F(ExtractOutsideCompilationForFunctionTest, OutsideCompilationInIf) { // Build the XLA computation func. // "const0" (bool) // "const1" (int32) @@ -555,7 +593,7 @@ TEST(ExtractOutsideCompilationForFunctionTest, OutsideCompilationInIf) { NameAttrList name_attrs; name_attrs.set_name("cluster"); *name_attrs.mutable_attr() = attrs; - TF_CHECK_OK(ExtractOutsideCompilationForFunction( + TF_CHECK_OK(ExtractOutsideCompilationTest( "_xla", "_oc", "cluster", name_attrs, "cluster_rewritten", "host_graph", host_compute_core, &fld, &shape_inference_graphs, &has_outside_compilation)); @@ -651,7 +689,7 @@ TEST(ExtractOutsideCompilationForFunctionTest, OutsideCompilationInIf) { } } -TEST(ExtractOutsideCompilationForFunctionTest, OutsideCompilationInWhile) { +TEST_F(ExtractOutsideCompilationForFunctionTest, OutsideCompilationInWhile) { // Build the XLA computation func. // "const0" (bool) // "while0" (input = "const0", cond = "cond_fn", body = "body_fn") @@ -714,7 +752,7 @@ TEST(ExtractOutsideCompilationForFunctionTest, OutsideCompilationInWhile) { NameAttrList name_attrs; name_attrs.set_name("cluster"); *name_attrs.mutable_attr() = attrs; - TF_CHECK_OK(ExtractOutsideCompilationForFunction( + TF_CHECK_OK(ExtractOutsideCompilationTest( "_xla", "_oc", "cluster", name_attrs, "cluster_rewritten", "host_graph", host_compute_core, &fld, &shape_inference_graphs, &has_outside_compilation)); @@ -782,4 +820,162 @@ TEST(ExtractOutsideCompilationForFunctionTest, OutsideCompilationInWhile) { } } +TEST_F(ExtractOutsideCompilationForFunctionTest, OutsideCompilationInFunction) { + // Build the XLA computation func. + // "const0" (int32) + // "fn" (input = "const0") + FunctionDefLibrary fdl; + { + tensorflow::Scope s = tensorflow::Scope::NewRootScope(); + Output arg = ops::_Arg(s.WithOpName("arg"), DT_INT32, 0); + Output identity = ops::Identity(s.WithOpName("identity"), arg); + ops::_Retval retval(s.WithOpName("retval"), identity, 0); + std::unique_ptr g(new Graph(OpRegistry::Global())); + TF_CHECK_OK(s.ToGraph(g.get())); + auto node_name_image = g->BuildNodeNameIndex(); + node_name_image["identity"]->AddAttr("_oc", "0"); + PartialTensorShape shape({2}); + node_name_image["identity"]->AddAttr( + kXlaInferredShapesAttrName, std::vector{shape}); + + FunctionDef *true_fn_fdef = fdl.add_function(); + TF_CHECK_OK(GraphToFunctionDef(*g, "fn", true_fn_fdef)); + } + FunctionLibraryDefinition fld(OpRegistry::Global(), fdl); + { + std::unique_ptr g(new Graph(&fld)); + + tensorflow::TensorProto tensor_proto; + tensor_proto.set_dtype(tensorflow::DT_INT32); + tensorflow::TensorShapeProto shape; + shape.add_dim()->set_size(2); + *tensor_proto.mutable_tensor_shape() = shape; + for (int i = 0; i < 2; ++i) { + tensor_proto.add_int_val(1); + } + NodeDef const_def; + TF_CHECK_OK(NodeDefBuilder("const", "Const") + .Attr("dtype", DT_INT32) + .Attr("value", tensor_proto) + .Finalize(&const_def)); + Status s; + Node *const_node = g->AddNode(const_def, &s); + TF_CHECK_OK(s); + + NodeDef fn_def; + TF_CHECK_OK(NodeDefBuilder("fn", "fn", &fld) + .Input("const", 0, DT_INT32) + .Finalize(&fn_def)); + Node *fn_node = g->AddNode(fn_def, &s); + TF_CHECK_OK(s); + g->AddEdge(const_node, 0, fn_node, 0); + + NodeDef ret_def; + TF_CHECK_OK(NodeDefBuilder("ret", "_Retval") + .Attr("index", 0) + .Attr("T", DT_INT32) + .Input("fn", 0, DT_INT32) + .Finalize(&ret_def)); + Node *ret_node = g->AddNode(ret_def, &s); + TF_CHECK_OK(s); + g->AddEdge(fn_node, 0, ret_node, 0); + + FunctionDef *xla_fdef = fdl.add_function(); + TF_CHECK_OK(GraphToFunctionDef(*g, "cluster", xla_fdef)); + TF_CHECK_OK(fld.AddFunctionDef(*xla_fdef)); + } + + protobuf::Map attrs; + std::map host_compute_core; + std::vector shape_inference_graphs; + bool has_outside_compilation; + NameAttrList name_attrs; + name_attrs.set_name("cluster"); + *name_attrs.mutable_attr() = attrs; + TF_CHECK_OK(ExtractOutsideCompilationTest( + "_xla", "_oc", "cluster", name_attrs, "cluster_rewritten", "host_graph", + host_compute_core, &fld, &shape_inference_graphs, + &has_outside_compilation)); + + // Check host graph. + { + FunctionBody *host_fbody = nullptr; + AttrValue device_ordinal_temp_value; + device_ordinal_temp_value.set_i(0); + protobuf::Map host_func_attrs; + host_func_attrs["device_ordinal"] = device_ordinal_temp_value; + TF_CHECK_OK(FunctionDefToBodyHelper( + *fld.Find("host_graph"), AttrSlice(&host_func_attrs), &fld, + [&](const string &op, const OpDef **sig) { + return fld.LookUpOpDef(op, sig); + }, + &host_fbody)); + std::unique_ptr host_fbody_deleter(host_fbody); + Graph *host_graph = host_fbody->graph; + auto node_name_index = host_graph->BuildNodeNameIndex(); + + // Verify we have call node for outside compilation in `fn`. + Node *call_node = node_name_index["oc_call_fn"]; + EXPECT_NE(call_node, nullptr); + + FunctionBody *call_fbody = nullptr; + TF_CHECK_OK(FunctionDefToBodyHelper( + *fld.Find("oc_func_call_host_fn"), AttrSlice(&host_func_attrs), &fld, + [&](const string &op, const OpDef **sig) { + return fld.LookUpOpDef(op, sig); + }, + &call_fbody)); + std::unique_ptr call_fbody_deleter(call_fbody); + + // Verify we have _XlaRecvAtHost and _XlaSendFromHost nodes. + bool has_recv = false, has_send = false; + for (Node *n : call_fbody->graph->nodes()) { + if (n->type_string() == "_XlaRecvAtHost") { + has_recv = true; + } else if (n->type_string() == "_XlaSendFromHost") { + has_send = true; + } + } + EXPECT_TRUE(has_recv); + EXPECT_TRUE(has_send); + } + + // Check XLA graph. + { + FunctionBody *xla_fbody = nullptr; + TF_CHECK_OK(FunctionDefToBodyHelper( + *fld.Find("cluster_rewritten"), AttrSlice(), &fld, + [&](const string &op, const OpDef **sig) { + return fld.LookUpOpDef(op, sig); + }, + &xla_fbody)); + std::unique_ptr xla_fbody_deleter(xla_fbody); + Graph *xla_graph = xla_fbody->graph; + auto node_name_index = xla_graph->BuildNodeNameIndex(); + + // Check that we have call node. + Node *fn_node = node_name_index["fn"]; + EXPECT_NE(fn_node, nullptr); + EXPECT_EQ(fn_node->type_string(), "fn_oc"); + + FunctionBody *call_fbody = nullptr; + TF_CHECK_OK(FunctionDefToBodyHelper( + *fld.Find("fn_oc"), AttrSlice(), &fld, + [&](const string &op, const OpDef **sig) { + return fld.LookUpOpDef(op, sig); + }, + &call_fbody)); + std::unique_ptr call_fbody_deleter(call_fbody); + + // Verify we have XlaHostCompute nodes. + bool has_hc = false; + for (Node *n : call_fbody->graph->nodes()) { + if (n->type_string() == "XlaHostCompute") { + has_hc = true; + } + } + EXPECT_TRUE(has_hc); + } +} + } // namespace tensorflow diff --git a/tensorflow/compiler/tf2xla/graph_compiler.cc b/tensorflow/compiler/tf2xla/graph_compiler.cc index efb7574972..0c2bb02239 100644 --- a/tensorflow/compiler/tf2xla/graph_compiler.cc +++ b/tensorflow/compiler/tf2xla/graph_compiler.cc @@ -22,6 +22,7 @@ limitations under the License. #include "tensorflow/compiler/tf2xla/dump_graph.h" #include "tensorflow/compiler/tf2xla/literal_util.h" #include "tensorflow/compiler/tf2xla/shape_util.h" +#include "tensorflow/compiler/tf2xla/side_effect_util.h" #include "tensorflow/compiler/tf2xla/type_util.h" #include "tensorflow/compiler/tf2xla/xla_compiler.h" #include "tensorflow/compiler/tf2xla/xla_context.h" @@ -191,6 +192,9 @@ Status GraphCompiler::CompileFunctionalNode(Node* n, // into the functions. XlaOpKernelContext xla_op_context(op_context); + XlaContext& context = XlaContext::Get(op_context); + auto* b = context.builder(); + XlaCompiler* compiler = xla_op_context.compiler(); NameAttrList func; @@ -219,8 +223,12 @@ Status GraphCompiler::CompileFunctionalNode(Node* n, TF_RETURN_IF_ERROR( PrepareArguments(&xla_op_context, graph.get(), expressions, &arguments)); + bool add_token_input_output = + HasNodeAttr(n->def(), kXlaTokenInputNodesAttrName); + XlaCompiler::CompileOptions compile_options; compile_options.is_entry_computation = false; + compile_options.add_token_input_output = add_token_input_output; XlaCompiler::CompilationResult result; TF_RETURN_IF_ERROR( compiler->CompileFunction(compile_options, func, arguments, &result)); @@ -234,9 +242,19 @@ Status GraphCompiler::CompileFunctionalNode(Node* n, } handles.push_back(expressions[i]->handle()); } - - XlaContext& context = XlaContext::Get(op_context); - auto* b = context.builder(); + if (add_token_input_output) { + std::vector token_input_nodes; + TF_RETURN_IF_ERROR( + GetNodeAttr(n->def(), kXlaTokenInputNodesAttrName, &token_input_nodes)); + std::vector token_inputs; + for (const string& node_name : token_input_nodes) { + auto token_or = compiler->GetNodeToken(node_name); + TF_RETURN_IF_ERROR(token_or.status()); + token_inputs.push_back(token_or.ConsumeValueOrDie()); + } + xla::XlaOp token_input = xla::AfterAll(b, token_inputs); + handles.push_back(token_input); + } auto output_handle = xla::Call(b, *result.computation, handles); // The output handle of `Call` computation is a tuple type. Unzip it so @@ -251,6 +269,10 @@ Status GraphCompiler::CompileFunctionalNode(Node* n, ++computation_output; } } + if (add_token_input_output) { + TF_RETURN_IF_ERROR(compiler->SetNodeToken( + n->name(), xla::GetTupleElement(output_handle, computation_output))); + } return b->first_error(); } diff --git a/tensorflow/compiler/tf2xla/side_effect_util.cc b/tensorflow/compiler/tf2xla/side_effect_util.cc index a9144cfc4f..412f31adbb 100644 --- a/tensorflow/compiler/tf2xla/side_effect_util.cc +++ b/tensorflow/compiler/tf2xla/side_effect_util.cc @@ -58,6 +58,10 @@ Status SetDeviceOrdinalAttributeForNode(Node* node, int device_ordinal) { node->ClearAttr(attr_name); node->AddAttr(attr_name, branch_func); } + } else if (HasNodeAttr(node->def(), "device_ordinal")) { + // Function call node containing outside compilation. + node->ClearAttr("device_ordinal"); + node->AddAttr("device_ordinal", device_ordinal); } else { return errors::Internal("Unknown node type to set 'device_ordinal': ", node->DebugString()); -- GitLab From 1b2e900f3cadc1a61d0c0a65c239e53afba7a7ec Mon Sep 17 00:00:00 2001 From: Michael Kuperstein Date: Tue, 8 Jan 2019 10:19:40 -0800 Subject: [PATCH 488/622] [XLA] Start moving over the DynamicSlice / DynamicUpdateSlice API * Updated operation semantics document for new syntax. * Added XlaBuilder APIs. * Marked some of the old APIs as deprecated. * Moved over a couple of slice tests to the new API. Other uses in XLA tests and in TF2XLA will be moved over incrementally. PiperOrigin-RevId: 228352804 --- tensorflow/compiler/xla/client/xla_builder.cc | 62 +++++++++++++++++++ tensorflow/compiler/xla/client/xla_builder.h | 30 +++++++-- .../compiler/xla/g3doc/operation_semantics.md | 59 +++++++++--------- .../compiler/xla/service/hlo_instruction.h | 2 + 4 files changed, 121 insertions(+), 32 deletions(-) diff --git a/tensorflow/compiler/xla/client/xla_builder.cc b/tensorflow/compiler/xla/client/xla_builder.cc index 433033cae6..328abbc7b9 100644 --- a/tensorflow/compiler/xla/client/xla_builder.cc +++ b/tensorflow/compiler/xla/client/xla_builder.cc @@ -705,6 +705,34 @@ XlaOp XlaBuilder::DynamicSlice(const XlaOp& operand, const XlaOp& start_indices, }); } +XlaOp XlaBuilder::DynamicSlice(const XlaOp& operand, + absl::Span start_indices, + absl::Span slice_sizes) { + return ReportErrorOrReturn([&]() -> StatusOr { + HloInstructionProto instr; + + TF_ASSIGN_OR_RETURN(const Shape& operand_shape, GetShape(operand)); + std::vector start_indices_shape_ptrs; + TF_ASSIGN_OR_RETURN(const auto& start_indices_shapes, + GetOperandShapes(start_indices)); + absl::c_transform(start_indices_shapes, + std::back_inserter(start_indices_shape_ptrs), + [](const Shape& shape) { return &shape; }); + TF_ASSIGN_OR_RETURN(Shape shape, + ShapeInference::InferDynamicSliceShape( + operand_shape, start_indices_shapes, slice_sizes)); + *instr.mutable_shape() = shape.ToProto(); + + for (int64 size : slice_sizes) { + instr.add_dynamic_slice_sizes(size); + } + + std::vector operands = {operand}; + operands.insert(operands.end(), start_indices.begin(), start_indices.end()); + return AddInstruction(std::move(instr), HloOpcode::kDynamicSlice, operands); + }); +} + XlaOp XlaBuilder::DynamicUpdateSlice(const XlaOp& operand, const XlaOp& update, const XlaOp& start_indices) { return ReportErrorOrReturn([&]() -> StatusOr { @@ -724,6 +752,31 @@ XlaOp XlaBuilder::DynamicUpdateSlice(const XlaOp& operand, const XlaOp& update, }); } +XlaOp XlaBuilder::DynamicUpdateSlice(const XlaOp& operand, const XlaOp& update, + absl::Span start_indices) { + return ReportErrorOrReturn([&]() -> StatusOr { + HloInstructionProto instr; + + TF_ASSIGN_OR_RETURN(const Shape& operand_shape, GetShape(operand)); + TF_ASSIGN_OR_RETURN(const Shape& update_shape, GetShape(update)); + std::vector start_indices_shape_ptrs; + TF_ASSIGN_OR_RETURN(const auto& start_indices_shapes, + GetOperandShapes(start_indices)); + absl::c_transform(start_indices_shapes, + std::back_inserter(start_indices_shape_ptrs), + [](const Shape& shape) { return &shape; }); + TF_ASSIGN_OR_RETURN(Shape shape, + ShapeInference::InferDynamicUpdateSliceShape( + operand_shape, update_shape, start_indices_shapes)); + *instr.mutable_shape() = shape.ToProto(); + + std::vector operands = {operand, update}; + operands.insert(operands.end(), start_indices.begin(), start_indices.end()); + return AddInstruction(std::move(instr), HloOpcode::kDynamicUpdateSlice, + operands); + }); +} + XlaOp XlaBuilder::ConcatInDim(absl::Span operands, int64 dimension) { return ReportErrorOrReturn([&]() -> StatusOr { @@ -2751,12 +2804,21 @@ XlaOp DynamicSlice(const XlaOp& operand, const XlaOp& start_indices, absl::Span slice_sizes) { return operand.builder()->DynamicSlice(operand, start_indices, slice_sizes); } +XlaOp DynamicSlice(const XlaOp& operand, absl::Span start_indices, + absl::Span slice_sizes) { + return operand.builder()->DynamicSlice(operand, start_indices, slice_sizes); +} XlaOp DynamicUpdateSlice(const XlaOp& operand, const XlaOp& update, const XlaOp& start_indices) { return operand.builder()->DynamicUpdateSlice(operand, update, start_indices); } +XlaOp DynamicUpdateSlice(const XlaOp& operand, const XlaOp& update, + absl::Span start_indices) { + return operand.builder()->DynamicUpdateSlice(operand, update, start_indices); +} + XlaOp ConcatInDim(XlaBuilder* builder, absl::Span operands, int64 dimension) { return builder->ConcatInDim(operands, dimension); diff --git a/tensorflow/compiler/xla/client/xla_builder.h b/tensorflow/compiler/xla/client/xla_builder.h index ebef8e0879..8908d172fa 100644 --- a/tensorflow/compiler/xla/client/xla_builder.h +++ b/tensorflow/compiler/xla/client/xla_builder.h @@ -359,11 +359,18 @@ class XlaBuilder { XlaOp SliceInDim(const XlaOp& operand, int64 start_index, int64 limit_index, int64 stride, int64 dimno); + ABSL_DEPRECATED("Use span-of-indices form instead") XlaOp DynamicSlice(const XlaOp& operand, const XlaOp& start_indices, absl::Span slice_sizes); + XlaOp DynamicSlice(const XlaOp& operand, + absl::Span start_indices, + absl::Span slice_sizes); + ABSL_DEPRECATED("Use span-of-indices form instead") XlaOp DynamicUpdateSlice(const XlaOp& operand, const XlaOp& update, const XlaOp& start_indices); + XlaOp DynamicUpdateSlice(const XlaOp& operand, const XlaOp& update, + absl::Span start_indices); XlaOp ConcatInDim(absl::Span operands, int64 dimension); @@ -875,9 +882,14 @@ class XlaBuilder { friend XlaOp DynamicSlice(const XlaOp& operand, const XlaOp& start_indices, absl::Span slice_sizes); + friend XlaOp DynamicSlice(const XlaOp& operand, + absl::Span start_indices, + absl::Span slice_sizes); friend XlaOp DynamicUpdateSlice(const XlaOp& operand, const XlaOp& update, const XlaOp& start_indices); + friend XlaOp DynamicUpdateSlice(const XlaOp& operand, const XlaOp& update, + absl::Span start_indices); friend XlaOp ConcatInDim(XlaBuilder* builder, absl::Span operands, int64 dimension); @@ -1319,10 +1331,15 @@ XlaOp SliceInDim(const XlaOp& operand, int64 start_index, int64 limit_index, // The size of the slice in each dimension is passed in 'slice_sizes', // which specify the end point of exclusive slice intervals in each // dimension [start, start + size). -// The shape of 'start_indices' must be rank == 1, with dimension size -// equal to the rank of the 'operand'. +// The shape of each element of 'start_indices' must be scalar, with the span +// size equal to the rank of the 'operand'. All elements of 'start_indices' must +// have the same shape. // Slice index calculations are computed modulo input dimension sizes to // prevent dynamic start indices from generating out-of-bound array accesses. +XlaOp DynamicSlice(const XlaOp& operand, absl::Span start_indices, + absl::Span slice_sizes); + +ABSL_DEPRECATED("Use span-of-indices form instead") XlaOp DynamicSlice(const XlaOp& operand, const XlaOp& start_indices, absl::Span slice_sizes); @@ -1338,10 +1355,15 @@ XlaOp DynamicSlice(const XlaOp& operand, const XlaOp& start_indices, // [4 5 6] => DynamicUpdateslice(data, update, start) => [4 10 11] // [7 8 9] [7 8 9 ] // -// The shape of 'start_indices' must be rank == 1, with dimension size -// equal to the rank of the 'operand'. +// The shape of each element of 'start_indices' must be scalar, with the span +// size equal to the rank of the 'operand'. All elements of 'start_indices' must +// have the same shape. // Slice index calculations are computed modulo update dimension sizes to // prevent dynamic start indices from generating out-of-bound array accesses. +XlaOp DynamicUpdateSlice(const XlaOp& operand, const XlaOp& update, + absl::Span start_indices); + +ABSL_DEPRECATED("Use span-of-indices form instead") XlaOp DynamicUpdateSlice(const XlaOp& operand, const XlaOp& update, const XlaOp& start_indices); diff --git a/tensorflow/compiler/xla/g3doc/operation_semantics.md b/tensorflow/compiler/xla/g3doc/operation_semantics.md index 78d4617017..7377ed729b 100644 --- a/tensorflow/compiler/xla/g3doc/operation_semantics.md +++ b/tensorflow/compiler/xla/g3doc/operation_semantics.md @@ -944,21 +944,21 @@ dimension: [start, start + size). The shape of `start_indices` must be rank == `DynamicSlice(operand, start_indices, size_indices)` -| Arguments | Type | Semantics | -| --------------- | ------------------- | ----------------------------------- | -| `operand` | `XlaOp` | N dimensional array of type T | -| `start_indices` | `XlaOp` | Rank 1 array of N integers | -: : : containing the starting indices of : -: : : the slice for each dimension. Value : -: : : must be greater than or equal to : -: : : zero. : -| `size_indices` | `ArraySlice` | List of N integers containing the | -: : : slice size for each dimension. Each : -: : : value must be strictly greater than : -: : : zero, and start + size must be less : -: : : than or equal to the size of the : -: : : dimension to avoid wrapping modulo : -: : : dimension size. : +| Arguments | Type | Semantics | +| --------------- | --------------------- | ---------------------------------- | +| `operand` | `XlaOp` | N dimensional array of type T | +| `start_indices` | sequence of N `XlaOp` | List of N scalar integers | +: : : containing the starting indices of : +: : : the slice for each dimension. : +: : : Value must be greater than or : +: : : equal to zero. : +| `size_indices` | `ArraySlice` | List of N integers containing the | +: : : slice size for each dimension. : +: : : Each value must be strictly : +: : : greater than zero, and start + : +: : : size must be less than or equal to : +: : : the size of the dimension to avoid : +: : : wrapping modulo dimension size. : The effective slice indices are computed by applying the following transformation for each index `i` in `[1, N)` before performing the slice: @@ -1009,19 +1009,22 @@ the rank of `operand`. `DynamicUpdateSlice(operand, update, start_indices)` -| Arguments | Type | Semantics | -| --------------- | ------- | ------------------------------------------------ | -| `operand` | `XlaOp` | N dimensional array of type T | -| `update` | `XlaOp` | N dimensional array of type T containing the | -: : : slice update. Each dimension of update shape : -: : : must be strictly greater than zero, and start + : -: : : update must be less than or equal to the operand : -: : : size for each dimension to avoid generating : -: : : out-of-bounds update indices. : -| `start_indices` | `XlaOp` | Rank 1 array of N integers containing the | -: : : starting indices of the slice for each : -: : : dimension. Value must be greater than or equal : -: : : to zero. : +| Arguments | Type | Semantics | +| --------------- | --------------------- | ---------------------------------- | +| `operand` | `XlaOp` | N dimensional array of type T | +| `update` | `XlaOp` | N dimensional array of type T | +: : : containing the slice update. Each : +: : : dimension of update shape must be : +: : : strictly greater than zero, and : +: : : start + update must be less than : +: : : or equal to the operand size for : +: : : each dimension to avoid generating : +: : : out-of-bounds update indices. : +| `start_indices` | sequence of N `XlaOp` | List of N scalar integers | +: : : containing the starting indices of : +: : : the slice for each dimension. : +: : : Value must be greater than or : +: : : equal to zero. : The effective slice indices are computed by applying the following transformation for each index `i` in `[1, N)` before performing the slice: diff --git a/tensorflow/compiler/xla/service/hlo_instruction.h b/tensorflow/compiler/xla/service/hlo_instruction.h index 2827eed0df..789ace6a26 100644 --- a/tensorflow/compiler/xla/service/hlo_instruction.h +++ b/tensorflow/compiler/xla/service/hlo_instruction.h @@ -556,6 +556,7 @@ class HloInstruction { // Creates a slice instruction, where the first operand is sliced by // start indices specified in the second operand, and by size specified in // 'slice_sizes'. + ABSL_DEPRECATED("Use span-of-indices form instead") static std::unique_ptr CreateDynamicSlice( const Shape& shape, HloInstruction* operand, HloInstruction* start_indices, absl::Span slice_sizes); @@ -567,6 +568,7 @@ class HloInstruction { // Creates a dynamic update slice instruction, which updates a slice // of 'operand' with 'update' and 'start_indices'. + ABSL_DEPRECATED("Use span-of-indices form instead") static std::unique_ptr CreateDynamicUpdateSlice( const Shape& shape, HloInstruction* operand, HloInstruction* update, HloInstruction* start_indices); -- GitLab From 3b6f11930a3c6fe91c43fc92aeb303e8cefffc5d Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Tue, 8 Jan 2019 10:22:53 -0800 Subject: [PATCH 489/622] Automated rollback of commit ec569e692435fb68191553fd7ffa5b633fdf1ef2 PiperOrigin-RevId: 228353495 --- tensorflow/core/grappler/optimizers/BUILD | 4 - .../optimizers/dependency_optimizer.cc | 456 +++++++++++------- .../optimizers/dependency_optimizer.h | 24 +- .../optimizers/dependency_optimizer_test.cc | 9 +- 4 files changed, 291 insertions(+), 202 deletions(-) diff --git a/tensorflow/core/grappler/optimizers/BUILD b/tensorflow/core/grappler/optimizers/BUILD index 5a328a91c5..7e29cee86a 100644 --- a/tensorflow/core/grappler/optimizers/BUILD +++ b/tensorflow/core/grappler/optimizers/BUILD @@ -303,18 +303,14 @@ cc_library( ":constant_folding", ":graph_optimizer", "//tensorflow/core:framework", - "//tensorflow/core:graph", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", "//tensorflow/core:protos_all_cc", "//tensorflow/core/grappler:grappler_item", - "//tensorflow/core/grappler:mutable_graph_view", "//tensorflow/core/grappler:op_types", "//tensorflow/core/grappler:utils", "//tensorflow/core/grappler/costs:graph_properties", "//tensorflow/core/grappler/utils:topological_sort", - "@com_google_absl//absl/container:flat_hash_map", - "@com_google_absl//absl/container:flat_hash_set", ], ) diff --git a/tensorflow/core/grappler/optimizers/dependency_optimizer.cc b/tensorflow/core/grappler/optimizers/dependency_optimizer.cc index 9bceb6b906..7fee3ae9d5 100644 --- a/tensorflow/core/grappler/optimizers/dependency_optimizer.cc +++ b/tensorflow/core/grappler/optimizers/dependency_optimizer.cc @@ -18,14 +18,10 @@ limitations under the License. #include #include -#include "absl/container/flat_hash_map.h" -#include "absl/container/flat_hash_set.h" #include "tensorflow/core/framework/node_def.pb.h" #include "tensorflow/core/framework/op.h" -#include "tensorflow/core/graph/tensor_id.h" #include "tensorflow/core/grappler/costs/graph_properties.h" #include "tensorflow/core/grappler/grappler_item.h" -#include "tensorflow/core/grappler/mutable_graph_view.h" #include "tensorflow/core/grappler/op_types.h" #include "tensorflow/core/grappler/optimizers/constant_folding.h" #include "tensorflow/core/grappler/utils.h" @@ -42,15 +38,20 @@ namespace grappler { namespace { -// Builds a map from the &graph->node(i) to i. -absl::flat_hash_map BuildNodeToIdx(const GraphDef& graph) { - // Set up &node -> index map. - absl::flat_hash_map node_to_idx; - for (int i = 0; i < graph.node_size(); ++i) { - const NodeDef& node = graph.node(i); - node_to_idx[&node] = i; +bool RemoveInput(NodeDef* node, const string& input, NodeMap* node_map) { + bool removed_input = false; + int pos = 0; + while (pos < node->input_size()) { + if (node->input(pos) == input) { + node->mutable_input()->SwapElements(pos, node->input_size() - 1); + node->mutable_input()->RemoveLast(); + node_map->RemoveOutput(NodeName(input), node->name()); + removed_input = true; + } else { + ++pos; + } } - return node_to_idx; + return removed_input; } } // namespace @@ -67,10 +68,7 @@ bool DependencyOptimizer::SafeToRemoveIdentity(const NodeDef& node) const { // The output values of this node may be needed. return false; } - - MutableGraphView::OutputPort port = graph_view_->GetRegularFanin( - MutableGraphView::InputPort(const_cast(&node), 0)); - NodeDef* input = port.node; + const NodeDef* input = node_map_->GetNode(NodeName(node.input(0))); CHECK(input != nullptr) << "node = " << node.name() << " input = " << node.input(0); // Don't remove Identity nodes corresponding to Variable reads or following @@ -81,23 +79,20 @@ bool DependencyOptimizer::SafeToRemoveIdentity(const NodeDef& node) const { // Don't turn Identity nodes following Switch into NoOp or remove them // if it requires anchoring a control dependencies the Switch node, which // is not valid. - MutableGraphView::OutputPort control_port(const_cast(&node), - Graph::kControlSlot); - if (!graph_view_->GetFanout(control_port).empty()) { + if (str_util::StartsWith(node.name(), kConstantFoldingCtrl)) { + // TODO(rmlarsen): Try to remove this artificial contraint. return false; } } - bool node_has_multiple_inputs = - graph_view_->NumFanins(node, /*include_controlling_nodes=*/true) > 1; - for (auto consumer : - graph_view_->GetFanouts(node, /*include_controlled_nodes=*/true)) { - if (node_has_multiple_inputs && IsMerge(*consumer.node)) { + for (auto consumer : node_map_->GetOutputs(node.name())) { + if (node.input_size() > 1 && IsMerge(*consumer)) { return false; } if (IsSwitch(*input)) { - if (graph_view_->HasFanin(*consumer.node, - {node.name(), Graph::kControlSlot})) { - return false; + for (const string& consumer_input : consumer->input()) { + if (consumer_input == AsControlDependency(node.name())) { + return false; + } } } } @@ -131,7 +126,7 @@ bool DependencyOptimizer::SafeToConvertToNoOp(const NodeDef& node) const { if (!SafeToRemoveIdentity(node)) { return false; } - if (graph_view_->NumFanouts(node, /*include_controlled_nodes=*/false) > 0) { + if (NumNonControlOutputs(node, *node_map_) > 0) { // The output values of this node may be needed. return false; } @@ -139,56 +134,61 @@ bool DependencyOptimizer::SafeToConvertToNoOp(const NodeDef& node) const { } int DependencyOptimizer::NumEdgesIfBypassed( - const NodeDef& node, int num_fanins, - const absl::flat_hash_set& fanout_edges) const { + const NodeDef& node, const std::vector& output_nodes) const { const bool is_multi_input_identity_n = IsIdentityN(node) && !IsIdentityNSingleInput(node); - const int num_fanouts = fanout_edges.size(); + const int num_outputs = output_nodes.size(); + const int num_inputs = node.input_size(); if (is_multi_input_identity_n) { // multi-input identity_n with input/output control dependencies will likely // increase number of edges after optimization. int num_edges_if_bypassed(0); - int num_non_controlling_fanins = - graph_view_->NumFanins(node, /*include_controlling_nodes=*/false); - num_edges_if_bypassed += num_non_controlling_fanins; - num_edges_if_bypassed += - (num_fanins - num_non_controlling_fanins) * num_fanouts; - - for (const auto& fanout : fanout_edges) { - if (fanout.dst.port_id == Graph::kControlSlot) { - num_edges_if_bypassed += num_fanins; + for (string input_node_name : node.input()) { + if (IsControlInput(input_node_name)) { + num_edges_if_bypassed += num_outputs; } else { ++num_edges_if_bypassed; } } + + for (auto consumer : output_nodes) { + for (int j = 0; j < consumer->input_size(); ++j) { + const TensorId consumer_input = ParseTensorName(consumer->input(j)); + if (consumer_input.node() == node.name()) { + if (IsControlInput(consumer_input)) { + num_edges_if_bypassed += num_inputs; + } else { + ++num_edges_if_bypassed; + } + } + } + } return num_edges_if_bypassed; } else { - return num_fanins * num_fanouts; + return num_inputs * num_outputs; } } bool DependencyOptimizer::BypassingNodeIsBeneficial( - const NodeDef& node, - const absl::flat_hash_set& fanins, - const absl::flat_hash_set& fanout_edges) const { + const NodeDef& node, const std::vector& input_nodes, + const std::vector& output_nodes) const { const bool is_identity = IsIdentity(node) || IsIdentityNSingleInput(node); const bool is_multi_input_identity_n = IsIdentityN(node) && !IsIdentityNSingleInput(node); - const int num_outputs = fanout_edges.size(); - const int num_inputs = fanins.size(); + const int num_outputs = output_nodes.size(); + const int num_inputs = node.input_size(); - if (NumEdgesIfBypassed(node, num_inputs, fanout_edges) > - num_inputs + num_outputs) { + if (NumEdgesIfBypassed(node, output_nodes) > num_inputs + num_outputs) { return false; } // Make sure that we don't increase the number of edges that cross // device boundaries. if ((num_inputs == 1 && num_outputs > 1 && - fanins.begin()->node->device() != node.device()) || + input_nodes[0]->device() != node.device()) || (num_inputs > 1 && num_outputs == 1 && - fanout_edges.begin()->dst.node->device() != node.device())) { + output_nodes[0]->device() != node.device())) { return false; } @@ -197,12 +197,12 @@ bool DependencyOptimizer::BypassingNodeIsBeneficial( // cost before and after. const string& node_dev = node.device(); int num_cross_in = 0; - for (const auto& fanin : fanins) { - num_cross_in += static_cast(fanin.node->device() != node_dev); + for (NodeDef* input_node : input_nodes) { + num_cross_in += static_cast(input_node->device() != node_dev); } int num_cross_out = 0; - for (const auto& fanout : fanout_edges) { - num_cross_out += static_cast(fanout.dst.node->device() != node_dev); + for (NodeDef* output_node : output_nodes) { + num_cross_out += static_cast(output_node->device() != node_dev); } if ((is_identity || is_multi_input_identity_n) && num_cross_in > 0 && @@ -216,10 +216,10 @@ bool DependencyOptimizer::BypassingNodeIsBeneficial( // Make sure we do not increase the number of device crossings. const int num_cross_before = num_cross_in + num_cross_out; int num_cross_after = 0; - for (const auto& fanin : fanins) { - for (const auto& fanout : fanout_edges) { + for (NodeDef* input_node : input_nodes) { + for (NodeDef* output_node : output_nodes) { num_cross_after += - static_cast(fanin.node->device() != fanout.dst.node->device()); + static_cast(input_node->device() != output_node->device()); } } if (num_cross_after > num_cross_before) { @@ -228,32 +228,45 @@ bool DependencyOptimizer::BypassingNodeIsBeneficial( return true; } -void DependencyOptimizer::OptimizeNode(NodeDef* node, - SetVector* nodes_to_simplify, - std::set* nodes_to_delete) { - const string node_name = node->name(); +void DependencyOptimizer::OptimizeNode(int node_idx, + SetVector* nodes_to_simplify, + std::set* nodes_to_delete) { + NodeDef* node = optimized_graph_->mutable_node(node_idx); const bool is_noop = IsNoOp(*node); const bool is_identity = IsIdentity(*node) || IsIdentityNSingleInput(*node); const bool is_multi_input_identity = IsIdentityN(*node) && !IsIdentityNSingleInput(*node); + const string node_name = node->name(); // Constant nodes with no input control dependency are always executed early, // so we can prune all their output control dependencies. - if (IsConstant(*node) && - graph_view_->NumFanins(*node, /*include_controlling_nodes=*/true) == 0) { - for (const auto& fanout : - graph_view_->GetFanouts(*node, /*include_controlled_nodes=*/true)) { - if (graph_view_->RemoveFanin(fanout.node->name(), - {node_name, Graph::kControlSlot})) { - nodes_to_simplify->PushBack(fanout.node); + if (IsConstant(*node) && node->input_size() == 0) { + const std::set output_nodes = node_map_->GetOutputs(node_name); + for (NodeDef* fanout : output_nodes) { + bool optimize_fanout = false; + bool data_connection = false; + for (int i = fanout->input_size() - 1; i >= 0; --i) { + const TensorId input_tensor = ParseTensorName(fanout->input(i)); + if (input_tensor.node() == node_name) { + if (input_tensor.index() < 0) { + fanout->mutable_input()->SwapElements(i, fanout->input_size() - 1); + fanout->mutable_input()->RemoveLast(); + optimize_fanout = true; + } else { + data_connection = true; + } + } + } + if (optimize_fanout) { + nodes_to_simplify->PushBack(node_to_idx_[fanout]); + if (!data_connection) { + node_map_->RemoveOutput(node_name, fanout->name()); + } } } - - if (graph_view_->NumFanouts(*node, /*include_controlled_nodes=*/true) == - 0 && - fetch_nodes_known_ && + if (node_map_->GetOutputs(node_name).empty() && fetch_nodes_known_ && nodes_to_preserve_.find(node_name) == nodes_to_preserve_.end()) { // Mark the node for deletion. - nodes_to_delete->insert(node->name()); + nodes_to_delete->insert(node_to_idx_[node]); } return; } @@ -264,23 +277,33 @@ void DependencyOptimizer::OptimizeNode(NodeDef* node, << ") with NoOp."; // The outputs of this node are not consumed. Replace its inputs with // control dependencies and replace the op itself with the NoOp op. - int num_non_controlling_fanins = - graph_view_->NumFanins(*node, /*include_controlling_nodes=*/false); - if (num_non_controlling_fanins > 0) { - absl::flat_hash_set non_controlling_fanins( - node->input().begin(), - node->input().begin() + num_non_controlling_fanins); - graph_view_->RemoveAllFanins(node_name, - /*keep_controlling_fanins=*/true); - for (const string& fanin : non_controlling_fanins) { - TensorId tensor_id = ParseTensorName(fanin); - graph_view_->AddControllingFanin(node_name, tensor_id); - nodes_to_simplify->PushBack(graph_view_->GetNode(tensor_id.node())); + std::unordered_set ctrl_inputs; + int pos = 0; + while (pos < node->input_size()) { + const string old_input = node->input(pos); + if (IsControlInput(old_input)) { + if (!ctrl_inputs.insert(old_input).second) { + // We found a duplicate control input. Remove it. + node->mutable_input()->SwapElements(pos, node->input_size() - 1); + node->mutable_input()->RemoveLast(); + } else { + ++pos; + } + continue; } + // Replace a normal input with a control input. + const string ctrl_input = ConstantFolding::AddControlDependency( + old_input, optimized_graph_, node_map_.get()); + ctrl_inputs.insert(ctrl_input); + node->set_input(pos, ctrl_input); + node_map_->UpdateInput(node_name, old_input, ctrl_input); + const NodeDef* old_input_node = node_map_->GetNode(old_input); + nodes_to_simplify->PushBack(node_to_idx_[old_input_node]); + ++pos; } node->set_op("NoOp"); node->clear_attr(); - nodes_to_simplify->PushBack(node); + nodes_to_simplify->PushBack(node_to_idx_[node]); return; } @@ -334,80 +357,110 @@ void DependencyOptimizer::OptimizeNode(NodeDef* node, if (is_noop || ((is_identity || is_multi_input_identity) && SafeToRemoveIdentity(*node))) { - auto fanins = - graph_view_->GetFanins(*node, /*include_controlling_nodes=*/true); - auto fanout_edges = - graph_view_->GetFanoutEdges(*node, /*include_controlled_edges=*/true); + const auto& output_node_set = node_map_->GetOutputs(node_name); + const std::vector output_nodes(output_node_set.begin(), + output_node_set.end()); + const int num_inputs = node->input_size(); + std::vector input_nodes; + for (int i = 0; i < num_inputs; ++i) { + NodeDef* input_node = node_map_->GetNode(node->input(i)); + if (input_node == nullptr) { + LOG(ERROR) << "Invalid input " << node->input(i); + return; + } + input_nodes.push_back(input_node); + } - if (!BypassingNodeIsBeneficial(*node, fanins, fanout_edges)) { + if (!BypassingNodeIsBeneficial(*node, input_nodes, output_nodes)) { return; } - int num_non_controlling_fanins = - graph_view_->NumFanins(*node, /*include_controlling_nodes=*/false); VLOG(1) << "***** Rerouting input around\n" << node->DebugString(); // Now remove the node and re-wire its inputs to its outputs. - for (auto fanout_edge : fanout_edges) { + for (auto consumer : output_nodes) { bool updated_consumer = false; - NodeDef* consumer = fanout_edge.dst.node; VLOG(1) << "consumer before:\n" << consumer->DebugString(); - if (fanout_edge.src.port_id == Graph::kControlSlot) { - updated_consumer = graph_view_->RemoveFanin( - consumer->name(), {node_name, Graph::kControlSlot}); - if (updated_consumer) { - // Add all non controlling fanins of node as controlling fanins to - // consumer. - for (int i = 0; i < num_non_controlling_fanins; ++i) { - TensorId input = ParseTensorName(node->input(i)); - if (graph_view_->AddControllingFanin(consumer->name(), input)) { - nodes_to_simplify->PushBack(graph_view_->GetNode(input.node())); + for (int i = 0; i < num_inputs; ++i) { + const NodeDef* input = input_nodes[i]; + // Forward dependency from input to consumer if it doesn't already + // depend on it. + if ((is_identity && i == 0) || + (is_multi_input_identity && !IsControlInput(node->input(i)))) { + // Replace regular input from Identity node. + string new_input; + const string& input_to_forward = node->input(i); + CHECK(!IsControlInput(input_to_forward)); + for (int j = 0; j < consumer->input_size(); ++j) { + const TensorId old_input = ParseTensorName(consumer->input(j)); + if (old_input.node() == node_name) { + if (old_input.index() == i) { + // Regular input + new_input = input_to_forward; + node_map_->UpdateInput(consumer->name(), old_input.ToString(), + new_input); + consumer->set_input(j, new_input); + } else if (old_input.index() == -1) { + // Control dependency + new_input = AsControlDependency(NodeName(input_to_forward)); + node_map_->UpdateInput(consumer->name(), old_input.ToString(), + new_input); + consumer->set_input(j, new_input); + } } } + updated_consumer = true; + } else { + // Forward dependency from input to consumer if it doesn't already + // depend on it. + if (node_map_->GetOutputs(input->name()).count(consumer) == 0) { + consumer->add_input(AsControlDependency(input->name())); + node_map_->AddOutput(input->name(), consumer->name()); + nodes_to_simplify->PushBack(node_to_idx_[input]); + updated_consumer = true; + } } - } else { - updated_consumer = graph_view_->UpdateFanin( - consumer->name(), {node_name, fanout_edge.src.port_id}, - ParseTensorName(node->input(fanout_edge.src.port_id))); } + // Remove dependency on node from consumer. + updated_consumer |= RemoveInput(consumer, AsControlDependency(node_name), + node_map_.get()); if (updated_consumer) { - // Forward all controlling fanins of node to consumer. - for (int i = num_non_controlling_fanins; i < node->input_size(); ++i) { - TensorId input = ParseTensorName(node->input(i)); - if (graph_view_->AddFanin(consumer->name(), input)) { - nodes_to_simplify->PushBack(graph_view_->GetNode(input.node())); - } - } - nodes_to_simplify->PushBack(consumer); + nodes_to_simplify->PushBack(node_to_idx_[consumer]); } VLOG(1) << "consumer after:\n" << consumer->DebugString(); } + node_map_->RemoveOutputs(node_name); if (fetch_nodes_known_ && nodes_to_preserve_.find(node_name) == nodes_to_preserve_.end()) { // Mark the node for deletion. - nodes_to_delete->insert(node_name); + nodes_to_delete->insert(node_idx); // Disconnect the node from its inputs to enable further optimizations. - graph_view_->RemoveAllFanins(node_name, - /*keep_controlling_fanins=*/false); + node_map_->RemoveInputs(node_name); + node->clear_input(); } } } +void DependencyOptimizer::CleanControlInputs() { + for (int i = 0; i < optimized_graph_->node_size(); ++i) { + DedupControlInputs(optimized_graph_->mutable_node(i)); + } +} + Status DependencyOptimizer::OptimizeDependencies() { - SetVector nodes_to_simplify; - std::set nodes_to_delete; - for (int i = 0; i < graph_view_->graph()->node_size(); ++i) { - NodeDef* node = graph_view_->graph()->mutable_node(i); - if (IsNoOp(*node) || IsIdentity(*node) || IsIdentityN(*node) || - IsConstant(*node) || SafeToConvertToNoOp(*node)) { - nodes_to_simplify.PushBack(node); + SetVector nodes_to_simplify; + std::set nodes_to_delete; + for (int i = 0; i < optimized_graph_->node_size(); ++i) { + const NodeDef& node = optimized_graph_->node(i); + if (IsNoOp(node) || IsIdentity(node) || IsIdentityN(node) || + IsConstant(node) || SafeToConvertToNoOp(node)) { + nodes_to_simplify.PushBack(i); } } while (!nodes_to_simplify.Empty()) { - NodeDef* node_to_simplify = nodes_to_simplify.PopBack(); + int node_to_simplify = nodes_to_simplify.PopBack(); // Discard nodes that were marked for deletion already. - while (nodes_to_delete.find(node_to_simplify->name()) != - nodes_to_delete.end()) { + while (nodes_to_delete.find(node_to_simplify) != nodes_to_delete.end()) { node_to_simplify = nodes_to_simplify.PopBack(); } OptimizeNode(node_to_simplify, &nodes_to_simplify, &nodes_to_delete); @@ -415,17 +468,17 @@ Status DependencyOptimizer::OptimizeDependencies() { if (fetch_nodes_known_) { VLOG(1) << "Deleted " << nodes_to_delete.size() << " out of " - << graph_view_->graph()->node_size() << " nodes."; - graph_view_->DeleteNodes(nodes_to_delete); + << optimized_graph_->node_size() << " nodes."; + EraseNodesFromGraph(nodes_to_delete, optimized_graph_); + node_map_.reset(new NodeMap(optimized_graph_)); + BuildNodeToIdx(); } return Status::OK(); } Status DependencyOptimizer::TransitiveReduction() { // PRECONDITION: optimized_graph_ must be sorted topologically. - GraphDef* graph = graph_view_->graph(); - auto node_to_idx = BuildNodeToIdx(*graph); - const int num_nodes = graph->node_size(); + const int num_nodes = optimized_graph_->node_size(); // Set up a compressed version of the graph to save a constant factor in the // expensive algorithm below. Also cache the set of control outputs and the // highest index of a target of any control output from each node. @@ -434,20 +487,20 @@ Status DependencyOptimizer::TransitiveReduction() { std::vector, 2>> control_outputs( num_nodes); for (int node_idx = 0; node_idx < num_nodes; ++node_idx) { - const NodeDef& node = graph->node(node_idx); + const NodeDef& node = optimized_graph_->node(node_idx); if (ModifiesFrameInfo(node) || !HasOpDef(node)) { // Ignore function nodes and nodes that modify frame info. continue; } for (int input_slot = 0; input_slot < node.input_size(); ++input_slot) { const string& input = node.input(input_slot); - const NodeDef* input_node = graph_view_->GetNode(NodeName(input)); + const NodeDef* input_node = node_map_->GetNode(input); if (ModifiesFrameInfo(*input_node) || IsMerge(*input_node)) { // Ignore edges from nodes that modify frame info and from Merge nodes, // because we cannot know which of it's input paths executes. continue; } - const int input_node_idx = node_to_idx[input_node]; + const int input_node_idx = node_to_idx_[input_node]; inputs[node_idx].push_back(input_node_idx); if (IsControlInput(input)) { ++num_controls; @@ -513,12 +566,16 @@ Status DependencyOptimizer::TransitiveReduction() { for (const auto& it : control_edges_to_remove) { const int target = it.first; - const NodeDef& target_node = graph->node(target); - const string target_node_name = target_node.name(); + NodeDef* target_node = optimized_graph_->mutable_node(target); for (const InputSlotAndSource& slot_and_source : it.second) { const int input_slot = slot_and_source.first; - const TensorId tensor_id = ParseTensorName(target_node.input(input_slot)); - graph_view_->RemoveFanin(target_node_name, tensor_id); + const int source = slot_and_source.second; + const NodeDef& source_node = optimized_graph_->node(source); + CHECK_LT(input_slot, target_node->input_size()); + target_node->mutable_input()->SwapElements(input_slot, + target_node->input_size() - 1); + node_map_->RemoveOutput(source_node.name(), target_node->name()); + target_node->mutable_input()->RemoveLast(); ++num_controls_removed; } } @@ -527,17 +584,26 @@ Status DependencyOptimizer::TransitiveReduction() { return Status::OK(); } +void DependencyOptimizer::BuildNodeToIdx() { + // Set up &node -> index map. + node_to_idx_.clear(); + for (int i = 0; i < optimized_graph_->node_size(); ++i) { + const NodeDef& node = optimized_graph_->node(i); + node_to_idx_[&node] = i; + } +} + // Suppose there are cross-device control inputs to node C from multiple nodes // that are located on another device, e.g., we have control edges: // A->C, B->C // where A and B are on device X and C is on device Y. // We can reduce cross-device communication by introducing an intermediate // NoOp node C' on device X and rewriting the control edges to: -// A->C', B->C', C'->C +// A->C', B->C', C' -> C void DependencyOptimizer::GroupCrossDeviceControlEdges() { - const int num_nodes = graph_view_->graph()->node_size(); + const int num_nodes = optimized_graph_->node_size(); for (int i = 0; i < num_nodes; ++i) { - NodeDef* node = graph_view_->graph()->mutable_node(i); + NodeDef* node = optimized_graph_->mutable_node(i); if (node->device().empty()) continue; // Creates new noop nodes for devices on which multiple control inputs are @@ -548,50 +614,66 @@ void DependencyOptimizer::GroupCrossDeviceControlEdges() { // that device. std::map noops; int num_noops = 0; - auto controlling_fanins = graph_view_->GetFanin( - MutableGraphView::InputPort(node, Graph::kControlSlot)); - for (const auto& controlling_fanin : controlling_fanins) { - const NodeDef* fanin_node = controlling_fanin.node; - if (!fanin_node->device().empty() && - fanin_node->device() != node->device()) { - auto emplace_result = noops.emplace(fanin_node->device(), nullptr); - if (!emplace_result.second && emplace_result.first->second == nullptr) { - // This is the second cross-device control input from the same - // device. Creates an intermediate noop node on that device. - string group_name; - NodeDef* noop; - // Creates a fresh node name; there may be conflicting names from - // a previous iteration of the optimizer. - do { - group_name = AddPrefixToNodeName( - node->name(), - strings::StrCat("GroupCrossDeviceControlEdges_", num_noops)); - noop = graph_view_->GetNode(group_name); - ++num_noops; - } while (noop != nullptr); - NodeDef new_node; - new_node.set_name(group_name); - new_node.set_device(fanin_node->device()); - new_node.set_op("NoOp"); - emplace_result.first->second = - graph_view_->AddNode(std::move(new_node)); + for (int j = 0; j < node->input_size(); ++j) { + if (IsControlInput(node->input(j))) { + const NodeDef* input = node_map_->GetNode(node->input(j)); + if (input != nullptr && !input->device().empty() && + input->device() != node->device()) { + auto emplace_result = noops.emplace(input->device(), nullptr); + if (!emplace_result.second && + emplace_result.first->second == nullptr) { + // This is the second cross-device control input from the same + // device. Creates an intermediate noop node on that device. + string group_name; + NodeDef* noop; + // Creates a fresh node name; there may be conflicting names from + // a previous iteration of the optimizer. + do { + group_name = AddPrefixToNodeName( + node->name(), + strings::StrCat("GroupCrossDeviceControlEdges_", num_noops)); + noop = node_map_->GetNode(group_name); + ++num_noops; + } while (noop != nullptr); + noop = optimized_graph_->add_node(); + noop->set_name(group_name); + noop->set_device(input->device()); + noop->set_op("NoOp"); + node_map_->AddNode(noop->name(), noop); + emplace_result.first->second = noop; + } } } } // Reroute existing control edges to go via the newly introduced NoOp nodes. - for (const auto& controlling_fanin : controlling_fanins) { - auto it = noops.find(controlling_fanin.node->device()); - if (it != noops.end() && it->second != nullptr) { - TensorId tensor_id(controlling_fanin.node->name(), Graph::kControlSlot); - graph_view_->RemoveFanin(node->name(), tensor_id); - graph_view_->AddFanin(it->second->name(), tensor_id); + int pos = 0; + while (pos < node->input_size()) { + const string& input_name = node->input(pos); + if (IsControlInput(input_name)) { + NodeDef* input = node_map_->GetNode(input_name); + if (input == nullptr) { + ++pos; + } else { + auto it = noops.find(input->device()); + if (it == noops.end() || it->second == nullptr) { + ++pos; + } else { + node->mutable_input()->SwapElements(pos, node->input_size() - 1); + node->mutable_input()->RemoveLast(); + it->second->add_input(AsControlDependency(*input)); + node_map_->UpdateOutput(input_name, node->name(), + it->second->name()); + } + } + } else { + ++pos; } } for (const auto& entry : noops) { if (entry.second) { - graph_view_->AddFanin(node->name(), - {entry.second->name(), Graph::kControlSlot}); + node->add_input(AsControlDependency(*entry.second)); + node_map_->AddOutput(entry.second->name(), node->name()); } } } @@ -599,17 +681,22 @@ void DependencyOptimizer::GroupCrossDeviceControlEdges() { Status DependencyOptimizer::Optimize(Cluster* cluster, const GrapplerItem& item, GraphDef* optimized_graph) { - *optimized_graph = item.graph; + optimized_graph_ = optimized_graph; + *optimized_graph_ = item.graph; nodes_to_preserve_ = item.NodesToPreserve(); fetch_nodes_known_ = !item.fetch.empty(); - graph_view_.reset(new MutableGraphView(optimized_graph)); + CleanControlInputs(); const int num_iterations = 2; for (int iteration = 0; iteration < num_iterations; ++iteration) { GRAPPLER_RETURN_IF_DEADLINE_EXCEEDED(); Status topo_sort_status; // Perform topological sort to prepare the graph for transitive reduction. - topo_sort_status = TopologicalSort(optimized_graph); + topo_sort_status = TopologicalSort(optimized_graph_); + // Set up index-based graph datastructures to speed up analysis steps below. + node_map_.reset(new NodeMap(optimized_graph_)); + BuildNodeToIdx(); + if (topo_sort_status.ok()) { // Remove redundant control dependencies. TF_RETURN_IF_ERROR(TransitiveReduction()); @@ -622,6 +709,9 @@ Status DependencyOptimizer::Optimize(Cluster* cluster, const GrapplerItem& item, // nodes. TF_RETURN_IF_ERROR(OptimizeDependencies()); + // Dedup control inputs. + CleanControlInputs(); + GroupCrossDeviceControlEdges(); } diff --git a/tensorflow/core/grappler/optimizers/dependency_optimizer.h b/tensorflow/core/grappler/optimizers/dependency_optimizer.h index eb7fc89080..7b032673fb 100644 --- a/tensorflow/core/grappler/optimizers/dependency_optimizer.h +++ b/tensorflow/core/grappler/optimizers/dependency_optimizer.h @@ -17,8 +17,6 @@ limitations under the License. #define TENSORFLOW_CORE_GRAPPLER_OPTIMIZERS_DEPENDENCY_OPTIMIZER_H_ #include - -#include "tensorflow/core/grappler/mutable_graph_view.h" #include "tensorflow/core/grappler/optimizers/graph_optimizer.h" #include "tensorflow/core/grappler/utils.h" #include "tensorflow/core/protobuf/rewriter_config.pb.h" @@ -48,22 +46,24 @@ class DependencyOptimizer : public GraphOptimizer { // Returns true if bypassing node does not increase the number of edges or // number of edges crossing a device boundary. bool BypassingNodeIsBeneficial( - const NodeDef& node, - const absl::flat_hash_set& fanins, - const absl::flat_hash_set& fanout_edges) const; - int NumEdgesIfBypassed( - const NodeDef& node, int num_fanins, - const absl::flat_hash_set& fanout_edges) const; + const NodeDef& node, const std::vector& input_nodes, + const std::vector& output_nodes) const; + int NumEdgesIfBypassed(const NodeDef& node, + const std::vector& output_nodes) const; // Returns true if node is not an Identity node or if it is an Identity // that is safe to remove. bool SafeToRemoveIdentity(const NodeDef& node) const; // Returns true if it is safe to convert node to NoOp. bool SafeToConvertToNoOp(const NodeDef& node) const; + // Removes all duplicate control dependencies. + void CleanControlInputs(); + // Builds a map from the &optimized_graph_->node(i) to i. + void BuildNodeToIdx(); // Tries to optimize the node with the given index, possibly additional // optimizations by inserting nodes in nodes_to_simplify, and pruning nodes by // inserting them in nodes_to_delete. - void OptimizeNode(NodeDef* node, SetVector* nodes_to_simplify, - std::set* nodes_to_delete); + void OptimizeNode(int node_idx, SetVector* nodes_to_simplify, + std::set* nodes_to_delete); // Eliminates redundant control dependencies by computing the transitive // reduction of the graph. Status TransitiveReduction(); @@ -76,7 +76,9 @@ class DependencyOptimizer : public GraphOptimizer { RewriterConfig::Toggle opt_level_; bool fetch_nodes_known_; std::unordered_set nodes_to_preserve_; - std::unique_ptr graph_view_; + std::unique_ptr node_map_; + std::unordered_map node_to_idx_; + GraphDef* optimized_graph_; // Not owned. }; } // end namespace grappler diff --git a/tensorflow/core/grappler/optimizers/dependency_optimizer_test.cc b/tensorflow/core/grappler/optimizers/dependency_optimizer_test.cc index 673fc987a8..8d70d9d5c7 100644 --- a/tensorflow/core/grappler/optimizers/dependency_optimizer_test.cc +++ b/tensorflow/core/grappler/optimizers/dependency_optimizer_test.cc @@ -91,7 +91,7 @@ TEST_F(DependencyOptimizerTest, DependenciesDrivenByConstants) { // The 'z' node should have been optimized away leaving only 5 nodes. EXPECT_EQ(5, output.node_size()); - for (const NodeDef& node : output.node()) { + for (const NodeDef& node : item.graph.node()) { if (node.name() == "id1" || node.name() == "id2") { EXPECT_EQ(1, node.input_size()); EXPECT_EQ("add", node.input(0)); @@ -125,8 +125,8 @@ TEST_F(DependencyOptimizerTest, ChangeToNoop) { EXPECT_EQ(item.graph.node_size(), output.node_size()); int found = 0; - for (int i = 0; i < output.node_size(); ++i) { - const NodeDef& node = output.node(i); + for (int i = 0; i < item.graph.node_size(); ++i) { + const NodeDef& node = item.graph.node(i); // "add" should get turned into a NoOp and removed. EXPECT_NE("add", node.name()); if (node.name() == "id1") { @@ -164,6 +164,7 @@ TEST_F(DependencyOptimizerTest, ChangeToNoop_RepeatedInput) { item.graph.Swap(&output); status = optimizer.Optimize(nullptr, item, &output); TF_EXPECT_OK(status); + LOG(INFO) << output.DebugString(); EXPECT_EQ(item.graph.node_size(), output.node_size()); int found = 0; @@ -747,7 +748,7 @@ TEST_F(DependencyOptimizerTest, Identity_DeviceCrossing_ConsumerOnSameDevice) { GraphDef output; Status status = optimizer.Optimize(nullptr, item, &output); TF_EXPECT_OK(status); - + LOG(INFO) << output.DebugString(); EXPECT_EQ(3, output.node_size()); for (const auto& node : output.node()) { EXPECT_NE("x_on_2", node.name()); -- GitLab From 93f760a1d83404cadf2ce1bc8cd8268c47e2f4f8 Mon Sep 17 00:00:00 2001 From: Peter Hawkins Date: Tue, 8 Jan 2019 10:41:24 -0800 Subject: [PATCH 490/622] [XLA] Fix VLOG(0) usage in dynamic_dimension_inference that caused a lot of LOG spam. PiperOrigin-RevId: 228357053 --- tensorflow/compiler/xla/service/dynamic_dimension_inference.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/compiler/xla/service/dynamic_dimension_inference.cc b/tensorflow/compiler/xla/service/dynamic_dimension_inference.cc index f2b7e9a186..2b158d7a6e 100644 --- a/tensorflow/compiler/xla/service/dynamic_dimension_inference.cc +++ b/tensorflow/compiler/xla/service/dynamic_dimension_inference.cc @@ -433,7 +433,7 @@ Status DynamicDimensionInferenceVisitor::ForEachOperandDynamicDimension( /* static */ StatusOr DynamicDimensionInference::Run( HloModule* module) { - VLOG(0) << "Param Config " << module->dynamic_parameter_binding().ToString(); + VLOG(2) << "Param Config " << module->dynamic_parameter_binding().ToString(); DynamicDimensionInference inference(module); TF_RETURN_IF_ERROR(inference.AnalyzeDynamicDimensions()); return inference; -- GitLab From 2ce3e3758d0fc1eea31b3e16c94321198c121953 Mon Sep 17 00:00:00 2001 From: Pete Warden Date: Tue, 8 Jan 2019 10:47:29 -0800 Subject: [PATCH 491/622] Fix for Raspberry Pi build, removing default dependency on Intel MKL PiperOrigin-RevId: 228358313 --- tensorflow/tools/ci_build/pi/build_raspberry_pi.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tensorflow/tools/ci_build/pi/build_raspberry_pi.sh b/tensorflow/tools/ci_build/pi/build_raspberry_pi.sh index 864278c647..987f0769b2 100755 --- a/tensorflow/tools/ci_build/pi/build_raspberry_pi.sh +++ b/tensorflow/tools/ci_build/pi/build_raspberry_pi.sh @@ -107,6 +107,7 @@ bazel build -c opt ${PI_COPTS} \ --copt=-funsafe-math-optimizations --copt=-ftree-vectorize \ --copt=-fomit-frame-pointer --cpu=armeabi \ --crosstool_top=@local_config_arm_compiler//:toolchain \ + --define tensorflow_mkldnn_contraction_kernel=0 \ --verbose_failures \ //tensorflow:libtensorflow.so \ //tensorflow:libtensorflow_framework.so \ -- GitLab From dd355d3b10a73474fb34972ec0ec708868bd0a24 Mon Sep 17 00:00:00 2001 From: Saurabh Saxena Date: Tue, 8 Jan 2019 11:13:29 -0800 Subject: [PATCH 492/622] Fill zeros for DT_INVALID tensors in TensorListStack. PiperOrigin-RevId: 228363806 --- tensorflow/core/kernels/BUILD | 1 + tensorflow/core/kernels/fill_functor.cc | 5 ++ tensorflow/core/kernels/list_kernels.h | 71 ++++++++++++++----- .../python/kernel_tests/list_ops_test.py | 41 +++++++++++ .../kernel_tests/tensor_array_ops_test.py | 13 ++-- 5 files changed, 106 insertions(+), 25 deletions(-) diff --git a/tensorflow/core/kernels/BUILD b/tensorflow/core/kernels/BUILD index b934c64bfc..aaea09bdc4 100644 --- a/tensorflow/core/kernels/BUILD +++ b/tensorflow/core/kernels/BUILD @@ -2256,6 +2256,7 @@ tf_kernel_library( ], deps = [ ":concat_lib", + ":fill_functor", "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:list_ops_op_lib", diff --git a/tensorflow/core/kernels/fill_functor.cc b/tensorflow/core/kernels/fill_functor.cc index 7090417dfd..9c4c0487f0 100644 --- a/tensorflow/core/kernels/fill_functor.cc +++ b/tensorflow/core/kernels/fill_functor.cc @@ -51,6 +51,11 @@ DEFINE_SETZERO_CPU(uint16); DEFINE_SETZERO_CPU(int16); DEFINE_SETZERO_CPU(int32); DEFINE_SETZERO_CPU(int64); +DEFINE_SETZERO_CPU(quint8); +DEFINE_SETZERO_CPU(qint8); +DEFINE_SETZERO_CPU(quint16); +DEFINE_SETZERO_CPU(qint16); +DEFINE_SETZERO_CPU(qint32); DEFINE_SETZERO_CPU(complex64); DEFINE_SETZERO_CPU(complex128); DEFINE_SETZERO_CPU(Variant); diff --git a/tensorflow/core/kernels/list_kernels.h b/tensorflow/core/kernels/list_kernels.h index 559e14b03c..fd1be80f11 100644 --- a/tensorflow/core/kernels/list_kernels.h +++ b/tensorflow/core/kernels/list_kernels.h @@ -28,6 +28,7 @@ limitations under the License. #include "tensorflow/core/framework/variant.h" #include "tensorflow/core/framework/variant_op_registry.h" #include "tensorflow/core/kernels/concat_lib.h" +#include "tensorflow/core/kernels/fill_functor.h" #include "tensorflow/core/lib/core/coding.h" #include "tensorflow/core/lib/core/errors.h" #include "tensorflow/core/lib/gtl/array_slice.h" @@ -65,6 +66,15 @@ struct TensorList { Status TensorShapeFromTensor(const Tensor& t, PartialTensorShape* out); +// Allocates a Tensor of requested shape and dtype and fills it with zeros. +template +void BuildZerosTensor(OpKernelContext* c, DataType dtype, + const TensorShape& shape, Tensor* zeros) { + OP_REQUIRES_OK(c, c->allocate_temp(dtype, shape, zeros)); + functor::SetZeroFunctor f; + f(c->eigen_device(), zeros->flat()); +} + template class TensorListStack : public OpKernel { public: @@ -94,7 +104,7 @@ class TensorListStack : public OpKernel { !tensor_list->tensors.empty() || tensor_list->element_shape.IsFullyDefined(), errors::InvalidArgument("Tried to stack elements of a empty ", - "list with non-fully-defined shape: ", + "list with non-fully-defined element_shape: ", tensor_list->element_shape.DebugString())); if (num_elements_ != -1) { OP_REQUIRES(c, tensor_list->tensors.size() == num_elements_, @@ -106,34 +116,59 @@ class TensorListStack : public OpKernel { // Compute the shape of the output tensor. // If `element_shape` is fully-defined it gets used. It is assumed that all // element tensors have the same shape. - // If `element_shape` is not fully-defined the shape of the first element - // tensor is used and it is checked that all other tensors have the same - // shape. - TensorShape resulting_shape; - if (!tensor_list->element_shape.AsTensorShape(&resulting_shape)) { - const Tensor& t = tensor_list->tensors[0]; - resulting_shape = t.shape(); - for (int i = 1; i < tensor_list->tensors.size(); ++i) { + // If `element_shape` is not fully-defined the shape of the first + // initialized element tensor is used and it is checked that all other + // initialized tensors have the same shape. An error is thrown if the list + // only contains DT_INVALID type tensors. + TensorShape resulting_element_shape; + if (!tensor_list->element_shape.AsTensorShape(&resulting_element_shape)) { + bool resulting_element_shape_initialized = false; + for (int i = 0; i < tensor_list->tensors.size(); ++i) { const Tensor& t = tensor_list->tensors[i]; - OP_REQUIRES(c, t.shape() == resulting_shape, - errors::InvalidArgument( - "Tried to stack tensors with unequal shapes: ", - resulting_shape.DebugString(), " vs ", - t.shape().DebugString())); + if (!resulting_element_shape_initialized) { + if (t.dtype() == DT_INVALID) { + continue; + } + resulting_element_shape = t.shape(); + resulting_element_shape_initialized = true; + continue; + } + OP_REQUIRES( + c, t.dtype() == DT_INVALID || t.shape() == resulting_element_shape, + errors::InvalidArgument( + "Tried to stack tensors with unequal shapes: ", + resulting_element_shape.DebugString(), " vs ", + t.shape().DebugString())); } + OP_REQUIRES( + c, resulting_element_shape_initialized, + errors::InvalidArgument("Tried to stack list which only contains ", + "uninitialized tensors and has a ", + "non-fully-defined element_shape: ", + tensor_list->element_shape.DebugString())); } - resulting_shape.InsertDim(0, tensor_list->tensors.size()); + TensorShape output_tensor_shape = resulting_element_shape; + output_tensor_shape.InsertDim(0, tensor_list->tensors.size()); Tensor* output; - OP_REQUIRES_OK(c, c->allocate_output(0, resulting_shape, &output)); + OP_REQUIRES_OK(c, c->allocate_output(0, output_tensor_shape, &output)); if (output->NumElements() == 0) { return; } ConstMatrixVector inputs_flat; inputs_flat.reserve(tensor_list->tensors.size()); + Tensor zeros; + BuildZerosTensor(c, element_dtype_, resulting_element_shape, + &zeros); for (const auto& t : tensor_list->tensors) { - inputs_flat.emplace_back(new typename TTypes::ConstMatrix( - t.shaped({1, t.NumElements()}))); + if (t.dtype() != DT_INVALID) { + inputs_flat.emplace_back(new typename TTypes::ConstMatrix( + t.shaped({1, t.NumElements()}))); + } else { + inputs_flat.emplace_back(new typename TTypes::ConstMatrix( + const_cast(zeros).shaped( + {1, zeros.NumElements()}))); + } } auto output_flat = output->shaped({1, output->NumElements()}); diff --git a/tensorflow/python/kernel_tests/list_ops_test.py b/tensorflow/python/kernel_tests/list_ops_test.py index fc0e270667..ec6906f20c 100644 --- a/tensorflow/python/kernel_tests/list_ops_test.py +++ b/tensorflow/python/kernel_tests/list_ops_test.py @@ -189,6 +189,47 @@ class ListOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase): t = list_ops.tensor_list_stack(l, element_dtype=dtypes.float32) self.evaluate(t) + def _testStackWithUninitializedTensors(self): + l = list_ops.tensor_list_reserve( + element_dtype=dtypes.float32, element_shape=[], num_elements=3) + t = list_ops.tensor_list_stack(l, element_dtype=dtypes.float32) + self.assertAllEqual(self.evaluate(t), [0., 0., 0.]) + + def testStackWithUninitializedTensors(self): + self._testStackWithUninitializedTensors() + + def testStackWithUninitializedTensorsGpu(self): + if not context.num_gpus(): + return + with context.device("gpu:0"): + self._testStackWithUninitializedTensors() + + def _testStackWithUninitializedTensorsInferShape(self): + l = list_ops.tensor_list_reserve( + element_dtype=dtypes.float32, element_shape=None, num_elements=3) + l = list_ops.tensor_list_set_item(l, 1, [1., 2.]) + t = list_ops.tensor_list_stack(l, element_dtype=dtypes.float32) + self.assertAllEqual(self.evaluate(t), [[0., 0.], [1., 2.], [0., 0.]]) + + def testStackWithUninitializedTensorsInferShape(self): + self._testStackWithUninitializedTensorsInferShape() + + def testStackWithUninitializedTensorsInferShapeGpu(self): + if not context.num_gpus(): + return + with context.device("gpu:0"): + self._testStackWithUninitializedTensorsInferShape() + + def testStackReservedListWithNoElementsAndPartialElementShapeFails(self): + l = list_ops.tensor_list_reserve( + element_dtype=dtypes.float32, element_shape=None, num_elements=3) + with self.assertRaisesRegexp(errors.InvalidArgumentError, + "Tried to stack list which only contains " + "uninitialized tensors and has a " + "non-fully-defined element_shape: "): + t = list_ops.tensor_list_stack(l, element_dtype=dtypes.float32) + self.evaluate(t) + @parameterized.named_parameters(("NoMaxNumElements", None), ("WithMaxNumElements", 2)) def testGatherGrad(self, max_num_elements): diff --git a/tensorflow/python/kernel_tests/tensor_array_ops_test.py b/tensorflow/python/kernel_tests/tensor_array_ops_test.py index 3f1bd60d1a..e8af998a70 100644 --- a/tensorflow/python/kernel_tests/tensor_array_ops_test.py +++ b/tensorflow/python/kernel_tests/tensor_array_ops_test.py @@ -185,8 +185,8 @@ class TensorArrayTest(test.TestCase): self.assertAllEqual([[0.0, 0.0], [4.0, 5.0], [0.0, 0.0]], self.evaluate(ta.write(1, [[4.0, 5.0]]).concat())) - @test_util.disable_control_flow_v2("b/118890905") - @test_util.run_v1_only("b/118890905") + @test_util.disable_control_flow_v2("b/122324791") + @test_util.run_v1_only("b/122324791") def testTensorArrayReadOrPackNotAllValuesAvailableFillsZeros(self): self._testTensorArrayReadOrPackNotAllValuesAvailableFillsZeros() @@ -202,8 +202,8 @@ class TensorArrayTest(test.TestCase): self.assertAllEqual([[0.0, 0.0], [4.0, 5.0], [0.0, 0.0]], self.evaluate(ta.write(1, [[4.0, 5.0]]).concat())) - @test_util.disable_control_flow_v2("b/118890905") - @test_util.run_v1_only("b/118890905") + @test_util.disable_control_flow_v2("b/122324791") + @test_util.run_v1_only("b/122324791") def testTensorArrayReadOrPackNotAllValuesAvailableInferShapeFillsZeros(self): self._testTensorArrayReadOrPackNotAllValuesAvailableInferShapeFillsZeros() @@ -1321,8 +1321,8 @@ class TensorArrayTest(test.TestCase): with self.cached_session(use_gpu=True): ta = tensor_array_ops.TensorArray( dtype=dtypes.float32, size=0, dynamic_size=False, infer_shape=False) - v2_msg = ("Tried to stack elements of a empty list with " - "non-fully-defined shape") + v2_msg = ("Tried to stack elements of a empty list with non-fully-defined" + " element_shape") v1_msg = ( "TensorArray has size zero, but element shape is not " "fully defined. Currently only static shapes are supported when " @@ -1385,7 +1385,6 @@ class TensorArrayTest(test.TestCase): self.assertAllEqual([10.0, -10.0], read_vals[1]) self.assertAllEqual([[2.0, 3.0], [4.0, 5.0]], grad_vals[0]) - @test_util.disable_control_flow_v2("b/118890905") @test_util.run_v1_only("b/118890905") def testTensorArrayWriteGatherAndGradients(self): with self.session(use_gpu=True) as session: -- GitLab From 9c091ca7134fc51f49ea930aac7a48bbdf84e7dc Mon Sep 17 00:00:00 2001 From: Yunxing Dai Date: Tue, 8 Jan 2019 11:15:25 -0800 Subject: [PATCH 493/622] Automated rollback of commit acfc65f0110e272abc8bd1abce867f2f534ba263 PiperOrigin-RevId: 228364189 --- tensorflow/compiler/tf2xla/kernels/shape_op.cc | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/tensorflow/compiler/tf2xla/kernels/shape_op.cc b/tensorflow/compiler/tf2xla/kernels/shape_op.cc index 85b0367f73..12830816ec 100644 --- a/tensorflow/compiler/tf2xla/kernels/shape_op.cc +++ b/tensorflow/compiler/tf2xla/kernels/shape_op.cc @@ -20,7 +20,6 @@ limitations under the License. #include "tensorflow/compiler/tf2xla/xla_helpers.h" #include "tensorflow/compiler/tf2xla/xla_op_kernel.h" #include "tensorflow/compiler/tf2xla/xla_op_registry.h" -#include "tensorflow/compiler/xla/client/lib/constants.h" #include "tensorflow/compiler/xla/client/xla_builder.h" #include "tensorflow/core/framework/kernel_def_builder.h" #include "tensorflow/core/framework/tensor_shape.h" @@ -92,20 +91,14 @@ class SizeOp : public XlaOpKernel { void Compile(XlaOpKernelContext* ctx) override { const TensorShape input_shape = ctx->InputShape(0); - OP_REQUIRES(ctx, - FastBoundsCheck(input_shape.num_elements(), - std::numeric_limits::max()), + const int64 size = input_shape.num_elements(); + OP_REQUIRES(ctx, FastBoundsCheck(size, std::numeric_limits::max()), errors::InvalidArgument("Size does not work for tensors > " "int32 max.")); Tensor size_constant(DT_INT32, TensorShape({})); - const int rank = input_shape.dims(); - xla::XlaBuilder* builder = ctx->builder(); - auto size = xla::One(builder, xla::U32); - for (int64 i = 0; i < rank; ++i) { - size = xla::Mul(size, xla::GetDimensionSize(ctx->Input(0), i)); - } - size = xla::ConvertElementType(size, xla::S32); - ctx->SetOutput(0, size); + size_constant.scalar()() = static_cast(size); + + ctx->SetConstantOutput(0, size_constant); } }; -- GitLab From bdeda4c087a3515578e6287c647bc00fe5f35491 Mon Sep 17 00:00:00 2001 From: Zhenyu Tan Date: Tue, 8 Jan 2019 11:27:47 -0800 Subject: [PATCH 494/622] Internal Cleanup. PiperOrigin-RevId: 228366686 --- tensorflow/python/keras/BUILD | 2 +- tensorflow/python/keras/optimizers_test.py | 157 +++++++++++---------- 2 files changed, 81 insertions(+), 78 deletions(-) diff --git a/tensorflow/python/keras/BUILD b/tensorflow/python/keras/BUILD index 3ffff61c2b..9f8da126bf 100755 --- a/tensorflow/python/keras/BUILD +++ b/tensorflow/python/keras/BUILD @@ -377,7 +377,7 @@ tf_py_test( "//tensorflow/python:client_testlib", "//tensorflow/python:training", ], - shard_count = 2, + shard_count = 4, tags = ["notsan"], ) diff --git a/tensorflow/python/keras/optimizers_test.py b/tensorflow/python/keras/optimizers_test.py index 18a20567ce..de24bf0536 100644 --- a/tensorflow/python/keras/optimizers_test.py +++ b/tensorflow/python/keras/optimizers_test.py @@ -44,116 +44,119 @@ def _get_model(input_dim, num_hidden, output_dim): return model -def _test_optimizer(optimizer, target=0.75): - np.random.seed(1337) - (x_train, y_train), _ = testing_utils.get_test_data(train_samples=1000, - test_samples=200, - input_shape=(10,), - num_classes=2) - y_train = keras.utils.to_categorical(y_train) - model = _get_model(x_train.shape[1], 20, y_train.shape[1]) - model.compile(loss='categorical_crossentropy', - optimizer=optimizer, - metrics=['accuracy']) - np.testing.assert_equal(keras.backend.get_value(model.optimizer.iterations), - 0) - history = model.fit(x_train, y_train, epochs=2, batch_size=16, verbose=0) - np.testing.assert_equal(keras.backend.get_value(model.optimizer.iterations), - 126) # 63 steps per epoch - assert history.history['acc'][-1] >= target - config = keras.optimizers.serialize(optimizer) - optim = keras.optimizers.deserialize(config) - new_config = keras.optimizers.serialize(optim) - new_config['class_name'] = new_config['class_name'].lower() - new_config['config'].pop('name', None) - if 'amsgrad' not in config['config']: - new_config['config'].pop('amsgrad', None) - if 'decay' in new_config['config'] and 'schedule_decay' in config['config']: - new_config['config']['schedule_decay'] = new_config['config'].pop('decay') - if 'momentum' not in config['config']: - new_config['config'].pop('momentum', None) - if 'centered' not in config['config']: - new_config['config'].pop('centered', None) - assert config == new_config - - # Test constraints. - model = keras.models.Sequential() - dense = keras.layers.Dense(10, - input_shape=(x_train.shape[1],), - kernel_constraint=lambda x: 0. * x + 1., - bias_constraint=lambda x: 0. * x + 2., - activation='relu') - model.add(dense) - model.add(keras.layers.Dense(y_train.shape[1], activation='softmax')) - model.compile(loss='categorical_crossentropy', - optimizer=optimizer, - metrics=['accuracy']) - np.testing.assert_equal(keras.backend.get_value(model.optimizer.iterations), - 126) # Using same optimizer from before - model.train_on_batch(x_train[:10], y_train[:10]) - np.testing.assert_equal(keras.backend.get_value(model.optimizer.iterations), - 127) - kernel, bias = dense.get_weights() - np.testing.assert_allclose(kernel, 1., atol=1e-3) - np.testing.assert_allclose(bias, 2., atol=1e-3) - - class KerasOptimizersTest(test.TestCase): + def _test_optimizer(self, optimizer, target=0.75): + np.random.seed(1337) + (x_train, y_train), _ = testing_utils.get_test_data( + train_samples=1000, test_samples=200, input_shape=(10,), num_classes=2) + y_train = keras.utils.to_categorical(y_train) + model = _get_model(x_train.shape[1], 20, y_train.shape[1]) + model.compile( + loss='categorical_crossentropy', + optimizer=optimizer, + metrics=['accuracy']) + np.testing.assert_equal( + keras.backend.get_value(model.optimizer.iterations), 0) + history = model.fit(x_train, y_train, epochs=2, batch_size=16, verbose=0) + np.testing.assert_equal( + keras.backend.get_value(model.optimizer.iterations), + 126) # 63 steps per epoch + assert history.history['acc'][-1] >= target + config = keras.optimizers.serialize(optimizer) + optim = keras.optimizers.deserialize(config) + new_config = keras.optimizers.serialize(optim) + new_config['class_name'] = new_config['class_name'].lower() + new_config['config'].pop('name', None) + if 'amsgrad' not in config['config']: + new_config['config'].pop('amsgrad', None) + if 'decay' in new_config['config'] and 'schedule_decay' in config['config']: + new_config['config']['schedule_decay'] = new_config['config'].pop('decay') + if 'momentum' not in config['config']: + new_config['config'].pop('momentum', None) + if 'centered' not in config['config']: + new_config['config'].pop('centered', None) + self.assertDictEqual(config, new_config) + + # Test constraints. + model = keras.models.Sequential() + dense = keras.layers.Dense( + 10, + input_shape=(x_train.shape[1],), + kernel_constraint=lambda x: 0. * x + 1., + bias_constraint=lambda x: 0. * x + 2., + activation='relu') + model.add(dense) + model.add(keras.layers.Dense(y_train.shape[1], activation='softmax')) + model.compile( + loss='categorical_crossentropy', + optimizer=optimizer, + metrics=['accuracy']) + np.testing.assert_equal( + keras.backend.get_value(model.optimizer.iterations), + 126) # Using same optimizer from before + model.train_on_batch(x_train[:10], y_train[:10]) + np.testing.assert_equal( + keras.backend.get_value(model.optimizer.iterations), 127) + kernel, bias = dense.get_weights() + np.testing.assert_allclose(kernel, 1., atol=1e-3) + np.testing.assert_allclose(bias, 2., atol=1e-3) + def test_sgd(self): with self.cached_session(): - _test_optimizer(keras.optimizers.SGD(lr=0.01, - momentum=0.9, - nesterov=True)) + self._test_optimizer(keras.optimizers.SGD()) + + def test_momentum(self): + with self.cached_session(): + self._test_optimizer( + keras.optimizers.SGD(lr=0.01, momentum=0.9, nesterov=True)) def test_rmsprop(self): with self.cached_session(): - _test_optimizer(keras.optimizers.RMSprop()) - _test_optimizer(keras.optimizers.RMSprop(decay=1e-3)) + self._test_optimizer(keras.optimizers.RMSprop()) + self._test_optimizer(keras.optimizers.RMSprop(decay=1e-3)) def test_adagrad(self): with self.cached_session(): - _test_optimizer(keras.optimizers.Adagrad()) - _test_optimizer(keras.optimizers.Adagrad(decay=1e-3)) + self._test_optimizer(keras.optimizers.Adagrad()) + self._test_optimizer(keras.optimizers.Adagrad(decay=1e-3)) def test_adadelta(self): with self.cached_session(): - _test_optimizer(keras.optimizers.Adadelta(), target=0.6) + self._test_optimizer(keras.optimizers.Adadelta(), target=0.6) # Accuracy seems dependent on the initialization. Even adding tf.Print # nodes in the graph seemed to affect the initialization seed, and hence # the accuracy. - _test_optimizer(keras.optimizers.Adadelta(decay=1e-3), target=0.4) + self._test_optimizer(keras.optimizers.Adadelta(decay=1e-3), target=0.4) def test_adam(self): with self.cached_session(): - _test_optimizer(keras.optimizers.Adam()) + self._test_optimizer(keras.optimizers.Adam()) # Accuracy seems dependent on the seed initialization. # TODO(b/121051441): fix test flakiness. - _test_optimizer(keras.optimizers.Adam(decay=1e-3), target=0.73) - _test_optimizer(keras.optimizers.Adam(amsgrad=True)) + self._test_optimizer(keras.optimizers.Adam(decay=1e-3), target=0.73) + self._test_optimizer(keras.optimizers.Adam(amsgrad=True)) def test_adamax(self): with self.cached_session(): - _test_optimizer(keras.optimizers.Adamax()) - _test_optimizer(keras.optimizers.Adamax(decay=1e-3)) + self._test_optimizer(keras.optimizers.Adamax()) + self._test_optimizer(keras.optimizers.Adamax(decay=1e-3)) def test_nadam(self): with self.cached_session(): - _test_optimizer(keras.optimizers.Nadam()) + self._test_optimizer(keras.optimizers.Nadam()) def test_clipnorm(self): with self.cached_session(): - _test_optimizer(keras.optimizers.SGD(lr=0.01, - momentum=0.9, - clipnorm=0.5)) + self._test_optimizer( + keras.optimizers.SGD(lr=0.01, momentum=0.9, clipnorm=0.5)) def test_clipvalue(self): with self.cached_session(): - _test_optimizer(keras.optimizers.SGD(lr=0.01, - momentum=0.9, - clipvalue=0.5)) + self._test_optimizer( + keras.optimizers.SGD(lr=0.01, momentum=0.9, clipvalue=0.5)) - def test_tfoptimizer(self): + def test_tf_optimizer(self): optimizer = keras.optimizers.TFOptimizer(AdamOptimizer(0.01)) model = keras.models.Sequential() model.add(keras.layers.Dense( @@ -188,7 +191,7 @@ class KerasOptimizersTest(test.TestCase): self.assertIs(optimizer_weak(), None) @test_util.run_in_graph_and_eager_modes - def test_tfoptimizer_iterations(self): + def test_tf_optimizer_iterations(self): with self.cached_session(): optimizer = keras.optimizers.TFOptimizer(AdamOptimizer(0.01)) model = keras.models.Sequential() -- GitLab From 35279e5c61d3ff5882c31d3d291256321ba27267 Mon Sep 17 00:00:00 2001 From: Doe Hyun Yoon Date: Tue, 8 Jan 2019 11:37:17 -0800 Subject: [PATCH 495/622] Replace the references to PredictCosts() with PredictCostsAndReturnRunMetadata(). PiperOrigin-RevId: 228368607 --- .../core/grappler/costs/analytical_cost_estimator_test.cc | 5 +++-- tensorflow/python/grappler/cost_analyzer.cc | 8 ++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/tensorflow/core/grappler/costs/analytical_cost_estimator_test.cc b/tensorflow/core/grappler/costs/analytical_cost_estimator_test.cc index a9a1abfa98..8a56344378 100644 --- a/tensorflow/core/grappler/costs/analytical_cost_estimator_test.cc +++ b/tensorflow/core/grappler/costs/analytical_cost_estimator_test.cc @@ -98,9 +98,10 @@ TEST_F(AnalyticalCostEstimatorTest, SimpleTest) { AnalyticalCostEstimator estimator(cluster_.get(), true); TF_ASSERT_OK(estimator.Initialize(item)); - CostGraphDef cost_graph; + RunMetadata run_metadata; Costs summary; - TF_ASSERT_OK(estimator.PredictCosts(item.graph, &cost_graph, &summary)); + TF_ASSERT_OK(estimator.PredictCostsAndReturnRunMetadata( + item.graph, &run_metadata, &summary)); EXPECT_EQ(Costs::NanoSeconds(9151), summary.execution_time); // Note there are totally 17 nodes (RandomUniform creates 2 nodes), but diff --git a/tensorflow/python/grappler/cost_analyzer.cc b/tensorflow/python/grappler/cost_analyzer.cc index b474e19894..bb8c6d5b85 100644 --- a/tensorflow/python/grappler/cost_analyzer.cc +++ b/tensorflow/python/grappler/cost_analyzer.cc @@ -42,9 +42,13 @@ Status CostAnalyzer::GenerateReport(std::ostream& os, bool per_node_report, void CostAnalyzer::PredictCosts(CostEstimator* cost_estimator, CostGraphDef* cost_graph, int64* total_time) { TF_CHECK_OK(cost_estimator->Initialize(*item_)); + RunMetadata run_metadata; Costs costs; - const Status status = - cost_estimator->PredictCosts(item_->graph, cost_graph, &costs); + const Status status = cost_estimator->PredictCostsAndReturnRunMetadata( + item_->graph, &run_metadata, &costs); + if (cost_graph) { + cost_graph->Swap(run_metadata.mutable_cost_graph()); + } *total_time = costs.execution_time.count(); if (!status.ok()) { LOG(ERROR) << "Could not estimate the cost for item " << item_->id << ": " -- GitLab From a849894f02e41fdeb08f86aafad51a2f02b454b1 Mon Sep 17 00:00:00 2001 From: Karim Nosir Date: Tue, 8 Jan 2019 11:59:53 -0800 Subject: [PATCH 496/622] Reduce the number of tests inside unit-test and shard it to speed it up. PiperOrigin-RevId: 228373086 --- tensorflow/lite/kernels/internal/BUILD | 2 ++ .../lite/kernels/internal/logsoftmax_quantized_test.cc | 6 +++--- tensorflow/lite/kernels/internal/softmax_quantized_test.cc | 6 +++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/tensorflow/lite/kernels/internal/BUILD b/tensorflow/lite/kernels/internal/BUILD index 885f650dd5..74a11a72ef 100644 --- a/tensorflow/lite/kernels/internal/BUILD +++ b/tensorflow/lite/kernels/internal/BUILD @@ -641,6 +641,7 @@ cc_test( srcs = [ "softmax_quantized_test.cc", ], + shard_count = 3, deps = [ ":optimized_base", ":quantization_util", @@ -657,6 +658,7 @@ cc_test( srcs = [ "logsoftmax_quantized_test.cc", ], + shard_count = 3, tags = [ # TODO(b/122242739): Reenable after fixing the flakiness? "nomac", diff --git a/tensorflow/lite/kernels/internal/logsoftmax_quantized_test.cc b/tensorflow/lite/kernels/internal/logsoftmax_quantized_test.cc index 889a726f3a..945300dad1 100644 --- a/tensorflow/lite/kernels/internal/logsoftmax_quantized_test.cc +++ b/tensorflow/lite/kernels/internal/logsoftmax_quantized_test.cc @@ -225,7 +225,7 @@ bool TryOneSkyscraperLogSoftmax(bool small_depth) { } TEST(TestQuantizedLogSoftmax, UniformLogSoftmaxTests) { - const int kTestsToRun = 1000; + const int kTestsToRun = 100; for (int i = 0; i < kTestsToRun; i++) { while (!TryOneUniformLogSoftmax()) { } @@ -233,7 +233,7 @@ TEST(TestQuantizedLogSoftmax, UniformLogSoftmaxTests) { } TEST(TestQuantizedLogSoftmax, SkyscraperLogSoftmaxTests) { - const int kTestsToRun = 1000; + const int kTestsToRun = 100; for (int i = 0; i < kTestsToRun; i++) { while (!TryOneSkyscraperLogSoftmax(false)) { } @@ -241,7 +241,7 @@ TEST(TestQuantizedLogSoftmax, SkyscraperLogSoftmaxTests) { } TEST(TestQuantizedLogSoftmax, SmallSkyscraperLogSoftmaxTests) { - const int kTestsToRun = 1000; + const int kTestsToRun = 100; for (int i = 0; i < kTestsToRun; i++) { while (!TryOneSkyscraperLogSoftmax(true)) { } diff --git a/tensorflow/lite/kernels/internal/softmax_quantized_test.cc b/tensorflow/lite/kernels/internal/softmax_quantized_test.cc index 743ce0355c..8ac62d9af7 100644 --- a/tensorflow/lite/kernels/internal/softmax_quantized_test.cc +++ b/tensorflow/lite/kernels/internal/softmax_quantized_test.cc @@ -210,7 +210,7 @@ bool TryOneSkyscraperSoftmax(bool small_depth) { } TEST(TestQuantizedSoftmax, UniformSoftmaxTests) { - const int kTestsToRun = 1000; + const int kTestsToRun = 100; for (int i = 0; i < kTestsToRun; i++) { while (!TryOneUniformSoftmax()) { } @@ -218,7 +218,7 @@ TEST(TestQuantizedSoftmax, UniformSoftmaxTests) { } TEST(TestQuantizedSoftmax, SkyscraperSoftmaxTests) { - const int kTestsToRun = 1000; + const int kTestsToRun = 100; for (int i = 0; i < kTestsToRun; i++) { while (!TryOneSkyscraperSoftmax(false)) { } @@ -226,7 +226,7 @@ TEST(TestQuantizedSoftmax, SkyscraperSoftmaxTests) { } TEST(TestQuantizedSoftmax, SmallSkyscraperSoftmaxTests) { - const int kTestsToRun = 1000; + const int kTestsToRun = 100; for (int i = 0; i < kTestsToRun; i++) { while (!TryOneSkyscraperSoftmax(true)) { } -- GitLab From 68727289bca144c9d1a8f618d81474e4a7322256 Mon Sep 17 00:00:00 2001 From: Peter Hawkins Date: Tue, 8 Jan 2019 12:01:56 -0800 Subject: [PATCH 497/622] [XLA] Move ArgMin/ArgMax out of TF/XLA and into XLA client library. NFC intended. PiperOrigin-RevId: 228373518 --- .../compiler/tf2xla/kernels/categorical_op.cc | 4 +- .../compiler/tf2xla/kernels/index_ops.cc | 5 +- .../compiler/tf2xla/kernels/index_ops_cpu.cc | 4 +- tensorflow/compiler/tf2xla/xla_helpers.cc | 67 ------------------- tensorflow/compiler/tf2xla/xla_helpers.h | 10 --- tensorflow/compiler/xla/client/lib/BUILD | 15 +++++ .../compiler/xla/client/lib/arithmetic.cc | 60 +++++++++++++++++ .../compiler/xla/client/lib/arithmetic.h | 8 +++ .../xla/client/lib/arithmetic_test.cc | 67 +++++++++++++++++++ 9 files changed, 156 insertions(+), 84 deletions(-) create mode 100644 tensorflow/compiler/xla/client/lib/arithmetic_test.cc diff --git a/tensorflow/compiler/tf2xla/kernels/categorical_op.cc b/tensorflow/compiler/tf2xla/kernels/categorical_op.cc index 7199b9b6fe..c2b4c28d15 100644 --- a/tensorflow/compiler/tf2xla/kernels/categorical_op.cc +++ b/tensorflow/compiler/tf2xla/kernels/categorical_op.cc @@ -99,8 +99,8 @@ class CategoricalOp : public XlaOpKernel { xla::PrimitiveType xla_output_type; OP_REQUIRES_OK(ctx, DataTypeToPrimitiveType(output_type(0), &xla_output_type)); - xla::XlaOp argmax = XlaHelpers::ArgMax(softmax_entries, xla_output_type, - /*axis=*/class_dimension); + xla::XlaOp argmax = xla::ArgMax(softmax_entries, xla_output_type, + /*axis=*/class_dimension); if (num_samples == 1) { argmax = xla::Reshape(argmax, {batch_size, 1}); } diff --git a/tensorflow/compiler/tf2xla/kernels/index_ops.cc b/tensorflow/compiler/tf2xla/kernels/index_ops.cc index 843b6bb4e6..978e9480ea 100644 --- a/tensorflow/compiler/tf2xla/kernels/index_ops.cc +++ b/tensorflow/compiler/tf2xla/kernels/index_ops.cc @@ -18,7 +18,6 @@ limitations under the License. #include "tensorflow/compiler/tf2xla/kernels/index_ops.h" #include "tensorflow/compiler/tf2xla/type_util.h" -#include "tensorflow/compiler/tf2xla/xla_helpers.h" #include "tensorflow/compiler/tf2xla/xla_op_kernel.h" #include "tensorflow/compiler/tf2xla/xla_op_registry.h" #include "tensorflow/compiler/xla/client/lib/arithmetic.h" @@ -66,9 +65,9 @@ void XlaArgMinMaxOp::Compile(XlaOpKernelContext* ctx) { xla::XlaOp input = ctx->Input(0); xla::XlaOp output; if (is_min_) { - output = XlaHelpers::ArgMin(input, index_xla_type, axis); + output = xla::ArgMin(input, index_xla_type, axis); } else { - output = XlaHelpers::ArgMax(input, index_xla_type, axis); + output = xla::ArgMax(input, index_xla_type, axis); } ctx->SetOutput(0, output); diff --git a/tensorflow/compiler/tf2xla/kernels/index_ops_cpu.cc b/tensorflow/compiler/tf2xla/kernels/index_ops_cpu.cc index 3e7e8eae6e..30b993045c 100644 --- a/tensorflow/compiler/tf2xla/kernels/index_ops_cpu.cc +++ b/tensorflow/compiler/tf2xla/kernels/index_ops_cpu.cc @@ -16,9 +16,9 @@ limitations under the License. // Native XLA implementations of indexing ops. #include "tensorflow/compiler/tf2xla/type_util.h" -#include "tensorflow/compiler/tf2xla/xla_helpers.h" #include "tensorflow/compiler/tf2xla/xla_op_kernel.h" #include "tensorflow/compiler/tf2xla/xla_op_registry.h" +#include "tensorflow/compiler/xla/client/lib/arithmetic.h" #include "tensorflow/compiler/xla/literal_util.h" #include "tensorflow/core/framework/kernel_def_builder.h" #include "tensorflow/core/framework/op_kernel.h" @@ -74,7 +74,7 @@ class ArgMaxCustomCallOp : public XlaOpKernel { // shape isn't supported. if (!ctx->compiler()->options().allow_cpu_custom_calls || (input_dims != 1 && input_dims != 2)) { - xla::XlaOp output = XlaHelpers::ArgMax(ctx->Input(0), output_type, axis); + xla::XlaOp output = xla::ArgMax(ctx->Input(0), output_type, axis); ctx->SetOutput(0, output); return; } diff --git a/tensorflow/compiler/tf2xla/xla_helpers.cc b/tensorflow/compiler/tf2xla/xla_helpers.cc index 00035d24b7..04a5d93406 100644 --- a/tensorflow/compiler/tf2xla/xla_helpers.cc +++ b/tensorflow/compiler/tf2xla/xla_helpers.cc @@ -34,63 +34,6 @@ limitations under the License. namespace tensorflow { -namespace { - -xla::XlaOp ArgMinMax(xla::XlaOp input, xla::PrimitiveType output_type, int axis, - bool is_min) { - xla::XlaBuilder* builder = input.builder(); - return builder->ReportErrorOrReturn([&]() -> xla::StatusOr { - TF_ASSIGN_OR_RETURN(xla::Shape input_shape, builder->GetShape(input)); - xla::XlaOp init_value; - xla::XlaComputation reducer; - if (is_min) { - init_value = xla::MaxValue(builder, input_shape.element_type()); - reducer = - xla::CreateScalarMinComputation(input_shape.element_type(), builder); - } else { - init_value = xla::MinValue(builder, input_shape.element_type()); - reducer = - xla::CreateScalarMaxComputation(input_shape.element_type(), builder); - } - - xla::XlaOp input_max = xla::Reduce(input, init_value, reducer, - /*dimensions_to_reduce=*/{axis}); - std::vector broadcast_dims(input_shape.rank() - 1); - std::iota(broadcast_dims.begin(), broadcast_dims.begin() + axis, 0); - std::iota(broadcast_dims.begin() + axis, broadcast_dims.end(), axis + 1); - // Compute a mask that has 1s for elements equal to the maximum. - xla::XlaOp partial_mask = xla::ConvertElementType( - xla::Eq(input, input_max, broadcast_dims), output_type); - - // In order to make identity elements for a bitwise And, we: - // Left shift the 1 to the leftmost bit, yielding 0x10...0 - // Arithmetic right shift the 1 back to the rightmost bit, yielding - // 0xFF...F - int32 bits_in_type = - xla::ShapeUtil::ByteSizeOfPrimitiveType(output_type) * 8 - 1; - xla::XlaOp shift_amount = - xla::ConstantR0WithType(builder, output_type, bits_in_type); - xla::XlaOp full_mask = xla::ShiftRightArithmetic( - xla::ShiftLeft(partial_mask, shift_amount), shift_amount); - - // And with the vector [0, 1, 2, ...] to convert each 0xFF...F into its - // index. - - const int64 axis_size = xla::ShapeUtil::GetDimension(input_shape, axis); - xla::XlaOp iota = xla::Iota(builder, output_type, axis_size); - xla::XlaOp product = - xla::And(full_mask, iota, /*broadcast_dimensions=*/{axis}); - - // If there are multiple maximum elements, choose the one with the highest - // index. - return xla::Reduce(product, xla::MinValue(builder, output_type), - xla::CreateScalarMaxComputation(output_type, builder), - /*dimensions_to_reduce=*/{axis}); - }); -} - -} // namespace - xla::XlaOp XlaHelpers::Zero(xla::XlaBuilder* b, DataType data_type) { xla::PrimitiveType type; TF_CHECK_OK(DataTypeToPrimitiveType(data_type, &type)); @@ -148,16 +91,6 @@ static Tensor MakeLinspaceTensor(const TensorShape& shape, int64 depth) { return linspace; } -xla::XlaOp XlaHelpers::ArgMax(xla::XlaOp input, xla::PrimitiveType output_type, - int axis) { - return ArgMinMax(input, output_type, axis, /*is_min=*/false); -} - -xla::XlaOp XlaHelpers::ArgMin(xla::XlaOp input, xla::PrimitiveType output_type, - int axis) { - return ArgMinMax(input, output_type, axis, /*is_min=*/true); -} - Status XlaHelpers::OneHot(xla::XlaBuilder* builder, int64 depth, int axis, DataType index_type, const TensorShape& indices_shape, const xla::XlaOp& indices, const xla::XlaOp& on_value, diff --git a/tensorflow/compiler/tf2xla/xla_helpers.h b/tensorflow/compiler/tf2xla/xla_helpers.h index 4858dfee55..490923526b 100644 --- a/tensorflow/compiler/tf2xla/xla_helpers.h +++ b/tensorflow/compiler/tf2xla/xla_helpers.h @@ -53,16 +53,6 @@ class XlaHelpers { absl::Span shape, xla::Literal* output); - // Returns the argmax of `input` along `axis`. `output_type` is the type to - // use for the output. - static xla::XlaOp ArgMax(xla::XlaOp input, xla::PrimitiveType output_type, - int axis); - - // Returns the argmin of `input` along `axis`. `output_type` is the type to - // use for the output. - static xla::XlaOp ArgMin(xla::XlaOp input, xla::PrimitiveType output_type, - int axis); - // Converts `indices` into a one-hot representation. `depth` is the size // of the new axis to add. `axis` is the position at which to add the new // axis. `indices_shape` is the shape of `indices`. `on_value` and diff --git a/tensorflow/compiler/xla/client/lib/BUILD b/tensorflow/compiler/xla/client/lib/BUILD index d863ebfea1..df1ee330f1 100644 --- a/tensorflow/compiler/xla/client/lib/BUILD +++ b/tensorflow/compiler/xla/client/lib/BUILD @@ -34,6 +34,21 @@ cc_library( ], ) +xla_test( + name = "arithmetic_test", + srcs = ["arithmetic_test.cc"], + deps = [ + ":arithmetic", + "//tensorflow/compiler/xla:literal_util", + "//tensorflow/compiler/xla:test", + "//tensorflow/compiler/xla:types", + "//tensorflow/compiler/xla:xla_data_proto", + "//tensorflow/compiler/xla/client:xla_builder", + "//tensorflow/compiler/xla/tests:client_library_test_base", + "//tensorflow/compiler/xla/tests:xla_internal_test_main", + ], +) + cc_library( name = "cholesky", srcs = ["cholesky.cc"], diff --git a/tensorflow/compiler/xla/client/lib/arithmetic.cc b/tensorflow/compiler/xla/client/lib/arithmetic.cc index 33ff3971d7..3b875135af 100644 --- a/tensorflow/compiler/xla/client/lib/arithmetic.cc +++ b/tensorflow/compiler/xla/client/lib/arithmetic.cc @@ -123,4 +123,64 @@ XlaOp Any(XlaOp predicates) { }); } +namespace { + +XlaOp ArgMinMax(XlaOp input, PrimitiveType output_type, int axis, bool is_min) { + XlaBuilder* builder = input.builder(); + return builder->ReportErrorOrReturn([&]() -> StatusOr { + TF_ASSIGN_OR_RETURN(Shape input_shape, builder->GetShape(input)); + XlaOp init_value; + XlaComputation reducer; + if (is_min) { + init_value = MaxValue(builder, input_shape.element_type()); + reducer = CreateScalarMinComputation(input_shape.element_type(), builder); + } else { + init_value = MinValue(builder, input_shape.element_type()); + reducer = CreateScalarMaxComputation(input_shape.element_type(), builder); + } + + XlaOp input_max = Reduce(input, init_value, reducer, + /*dimensions_to_reduce=*/{axis}); + std::vector broadcast_dims(input_shape.rank() - 1); + std::iota(broadcast_dims.begin(), broadcast_dims.begin() + axis, 0); + std::iota(broadcast_dims.begin() + axis, broadcast_dims.end(), axis + 1); + // Compute a mask that has 1s for elements equal to the maximum. + XlaOp partial_mask = + ConvertElementType(Eq(input, input_max, broadcast_dims), output_type); + + // In order to make identity elements for a bitwise And, we: + // Left shift the 1 to the leftmost bit, yielding 0x10...0 + // Arithmetic right shift the 1 back to the rightmost bit, yielding + // 0xFF...F + int32 bits_in_type = + ShapeUtil::ByteSizeOfPrimitiveType(output_type) * 8 - 1; + XlaOp shift_amount = ConstantR0WithType(builder, output_type, bits_in_type); + XlaOp full_mask = ShiftRightArithmetic( + ShiftLeft(partial_mask, shift_amount), shift_amount); + + // And with the vector [0, 1, 2, ...] to convert each 0xFF...F into its + // index. + + const int64 axis_size = ShapeUtil::GetDimension(input_shape, axis); + XlaOp iota = Iota(builder, output_type, axis_size); + XlaOp product = And(full_mask, iota, /*broadcast_dimensions=*/{axis}); + + // If there are multiple maximum elements, choose the one with the highest + // index. + return Reduce(product, MinValue(builder, output_type), + CreateScalarMaxComputation(output_type, builder), + /*dimensions_to_reduce=*/{axis}); + }); +} + +} // namespace + +XlaOp ArgMax(XlaOp input, PrimitiveType output_type, int axis) { + return ArgMinMax(input, output_type, axis, /*is_min=*/false); +} + +XlaOp ArgMin(XlaOp input, PrimitiveType output_type, int axis) { + return ArgMinMax(input, output_type, axis, /*is_min=*/true); +} + } // namespace xla diff --git a/tensorflow/compiler/xla/client/lib/arithmetic.h b/tensorflow/compiler/xla/client/lib/arithmetic.h index 632e8cc8bc..d4a7812c44 100644 --- a/tensorflow/compiler/xla/client/lib/arithmetic.h +++ b/tensorflow/compiler/xla/client/lib/arithmetic.h @@ -57,6 +57,14 @@ XlaComputation CreateScalarOrComputation(PrimitiveType type, // Note: if predicates is zero-sized, Any() vacuously returns false. XlaOp Any(XlaOp predicates); +// Returns the argmax of `input` along `axis`. `output_type` is the type to +// use for the output. +XlaOp ArgMax(XlaOp input, PrimitiveType output_type, int axis); + +// Returns the argmin of `input` along `axis`. `output_type` is the type to +// use for the output. +XlaOp ArgMin(XlaOp input, PrimitiveType output_type, int axis); + } // namespace xla #endif // TENSORFLOW_COMPILER_XLA_CLIENT_LIB_ARITHMETIC_H_ diff --git a/tensorflow/compiler/xla/client/lib/arithmetic_test.cc b/tensorflow/compiler/xla/client/lib/arithmetic_test.cc new file mode 100644 index 0000000000..a13839f9db --- /dev/null +++ b/tensorflow/compiler/xla/client/lib/arithmetic_test.cc @@ -0,0 +1,67 @@ +/* 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/compiler/xla/client/lib/arithmetic.h" +#include "tensorflow/compiler/xla/client/xla_builder.h" +#include "tensorflow/compiler/xla/literal_util.h" +#include "tensorflow/compiler/xla/test.h" +#include "tensorflow/compiler/xla/tests/client_library_test_base.h" +#include "tensorflow/compiler/xla/tests/test_macros.h" +#include "tensorflow/compiler/xla/types.h" +#include "tensorflow/compiler/xla/xla_data.pb.h" + +namespace xla { +namespace { + +using ArithmeticTest = ClientLibraryTestBase; + +XLA_TEST_F(ArithmeticTest, ArgMinR2Axis0) { + XlaBuilder builder(TestName()); + auto x = ConstantR2(&builder, {{1, 7, 4}, {6, 3, 5}, {8, 3, 3}}); + ArgMin(x, S32, /*axis=*/0); + + std::vector expected = {0, 2, 2}; + ComputeAndCompareR1(&builder, expected, {}); +} + +XLA_TEST_F(ArithmeticTest, ArgMinR2Axis1) { + XlaBuilder builder(TestName()); + auto x = ConstantR2(&builder, {{1, 7, 4}, {6, 3, 5}, {8, 3, 3}}); + ArgMin(x, S32, /*axis=*/1); + + std::vector expected = {0, 1, 2}; + ComputeAndCompareR1(&builder, expected, {}); +} + +XLA_TEST_F(ArithmeticTest, ArgMaxR2Axis0) { + XlaBuilder builder(TestName()); + auto x = ConstantR2(&builder, {{1, 7, 4}, {6, 3, 5}, {8, 3, 3}}); + ArgMax(x, S32, /*axis=*/0); + + std::vector expected = {2, 0, 1}; + ComputeAndCompareR1(&builder, expected, {}); +} + +XLA_TEST_F(ArithmeticTest, ArgMaxR2Axis1) { + XlaBuilder builder(TestName()); + auto x = ConstantR2(&builder, {{1, 7, 4}, {6, 3, 5}, {8, 3, 3}}); + ArgMax(x, S32, /*axis=*/1); + + std::vector expected = {1, 0, 0}; + ComputeAndCompareR1(&builder, expected, {}); +} + +} // namespace +} // namespace xla -- GitLab From 989c8b40a9b3b7bd45aedef43315675b0d4baf8d Mon Sep 17 00:00:00 2001 From: Adam Roberts Date: Tue, 8 Jan 2019 12:03:30 -0800 Subject: [PATCH 498/622] Disable writing examples/sec and global_step/sec summaries on TPU if save_summary_steps is Falsey. PiperOrigin-RevId: 228373932 --- tensorflow/contrib/tpu/python/tpu/tpu_estimator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/contrib/tpu/python/tpu/tpu_estimator.py b/tensorflow/contrib/tpu/python/tpu/tpu_estimator.py index 98babbb543..2317300041 100644 --- a/tensorflow/contrib/tpu/python/tpu/tpu_estimator.py +++ b/tensorflow/contrib/tpu/python/tpu/tpu_estimator.py @@ -2654,7 +2654,7 @@ class TPUEstimator(estimator_lib.Estimator): if self._log_every_n_steps is not None: examples_hook = ExamplesPerSecondHook( ctx.global_batch_size, - output_dir=self.model_dir, + output_dir=self.model_dir if config.save_summary_steps else None, every_n_steps=self._log_every_n_steps) if ctx.is_running_on_cpu(is_export_mode=is_export_mode): -- GitLab From 67d8a0330b5bacc0bb45675b1e3f211bd930ec7f Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Tue, 8 Jan 2019 12:18:59 -0800 Subject: [PATCH 499/622] Fixing flaky test in quantile regression PiperOrigin-RevId: 228376536 --- .../estimator_batch/estimator_test.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tensorflow/contrib/boosted_trees/estimator_batch/estimator_test.py b/tensorflow/contrib/boosted_trees/estimator_batch/estimator_test.py index ee052ac603..47d910d42a 100644 --- a/tensorflow/contrib/boosted_trees/estimator_batch/estimator_test.py +++ b/tensorflow/contrib/boosted_trees/estimator_batch/estimator_test.py @@ -487,8 +487,8 @@ class BoostedTreeEstimatorTest(test_util.TensorFlowTestCase): self.assertTrue(frac_below_upper_0 <= 0.98) self.assertTrue(frac_below_upper_1 >= 0.92) self.assertTrue(frac_below_upper_1 <= 0.98) - self.assertTrue(frac_both_below_upper >= 0.92) - self.assertTrue(frac_both_below_upper <= 0.98) + self.assertTrue(frac_both_below_upper >= 0.91) + self.assertTrue(frac_both_below_upper <= 0.99) train_input_fn, test_input_fn, _ = _quantile_regression_input_fns( two_dimension=True) @@ -516,8 +516,8 @@ class BoostedTreeEstimatorTest(test_util.TensorFlowTestCase): self.assertTrue(frac_above_lower_0 <= 0.98) self.assertTrue(frac_above_lower_1 >= 0.92) self.assertTrue(frac_above_lower_1 <= 0.98) - self.assertTrue(frac_both_above_lower >= 0.92) - self.assertTrue(frac_both_above_lower <= 0.98) + self.assertTrue(frac_both_above_lower >= 0.91) + self.assertTrue(frac_both_above_lower <= 0.99) class CoreGradientBoostedDecisionTreeEstimators(test_util.TensorFlowTestCase): @@ -806,8 +806,8 @@ class CoreGradientBoostedDecisionTreeEstimators(test_util.TensorFlowTestCase): self.assertTrue(frac_below_upper_0 <= 0.98) self.assertTrue(frac_below_upper_1 >= 0.92) self.assertTrue(frac_below_upper_1 <= 0.98) - self.assertTrue(frac_both_below_upper >= 0.92) - self.assertTrue(frac_both_below_upper <= 0.98) + self.assertTrue(frac_both_below_upper >= 0.91) + self.assertTrue(frac_both_below_upper <= 0.99) train_input_fn, test_input_fn, _ = _quantile_regression_input_fns( two_dimension=True) @@ -835,8 +835,8 @@ class CoreGradientBoostedDecisionTreeEstimators(test_util.TensorFlowTestCase): self.assertTrue(frac_above_lower_0 <= 0.98) self.assertTrue(frac_above_lower_1 >= 0.92) self.assertTrue(frac_above_lower_1 <= 0.98) - self.assertTrue(frac_both_above_lower >= 0.92) - self.assertTrue(frac_both_above_lower <= 0.98) + self.assertTrue(frac_both_above_lower >= 0.91) + self.assertTrue(frac_both_above_lower <= 0.99) if __name__ == "__main__": -- GitLab From a3d639a5a46df8f3523bd514cade1faaf602b66d Mon Sep 17 00:00:00 2001 From: Blake Hechtman Date: Tue, 8 Jan 2019 12:22:54 -0800 Subject: [PATCH 500/622] [XLA] Canonicalize dot dimension numbers on CPU and GPU backends cases that previously failed shape inference. PiperOrigin-RevId: 228377214 --- tensorflow/compiler/xla/service/BUILD | 3 +- .../compiler/xla/service/dot_decomposer.cc | 174 +++++++++++++++++- tensorflow/compiler/xla/service/gpu/BUILD | 1 + .../xla/service/gpu/nvptx_compiler.cc | 2 + tensorflow/compiler/xla/tests/BUILD | 2 + .../compiler/xla/tests/dot_operation_test.cc | 34 ++++ 6 files changed, 208 insertions(+), 8 deletions(-) diff --git a/tensorflow/compiler/xla/service/BUILD b/tensorflow/compiler/xla/service/BUILD index ec63ae1ac6..728adb67a0 100644 --- a/tensorflow/compiler/xla/service/BUILD +++ b/tensorflow/compiler/xla/service/BUILD @@ -1873,8 +1873,9 @@ cc_library( "//tensorflow/compiler/xla:shape_util", "//tensorflow/compiler/xla:status_macros", "//tensorflow/compiler/xla:types", - "//tensorflow/compiler/xla:xla_data_proto", "//tensorflow/core:lib", + "@com_google_absl//absl/algorithm:container", + "@com_google_absl//absl/strings", ], ) diff --git a/tensorflow/compiler/xla/service/dot_decomposer.cc b/tensorflow/compiler/xla/service/dot_decomposer.cc index b2ba261790..855424067d 100644 --- a/tensorflow/compiler/xla/service/dot_decomposer.cc +++ b/tensorflow/compiler/xla/service/dot_decomposer.cc @@ -15,6 +15,8 @@ limitations under the License. #include "tensorflow/compiler/xla/service/dot_decomposer.h" +#include "absl/algorithm/container.h" +#include "absl/strings/str_join.h" #include "tensorflow/compiler/xla/service/hlo_computation.h" #include "tensorflow/compiler/xla/service/hlo_instruction.h" #include "tensorflow/compiler/xla/service/hlo_opcode.h" @@ -156,29 +158,187 @@ Status DecomposeBatchDot(HloInstruction* dot) { return computation->ReplaceInstruction(dot, new_dot); } +// Convert a dot into a canonical form where non-contracting and contracting +// dimensions are reshaped together and batch dimensions are the most major +// dimensions. The requires transposing and reshapes the lhs and rhs and +// reshaping the output batch to the original shape. +Status CanonicalizeDot(HloInstruction* original_dot) { + auto computation = original_dot->parent(); + const auto& original_dnums = original_dot->dot_dimension_numbers(); + const int64 num_batch_dims = original_dnums.lhs_batch_dimensions_size(); + const int64 num_contracting_dims = + original_dnums.lhs_contracting_dimensions_size(); + + const auto& lhs_shape = original_dot->operand(0)->shape(); + const int64 lhs_rank = lhs_shape.rank(); + const int64 num_lhs_non_contracting_dims = + lhs_rank - num_batch_dims - num_contracting_dims; + + std::vector lhs_non_contracting_dims; + lhs_non_contracting_dims.reserve(num_lhs_non_contracting_dims); + int64 lhs_contracting_size = 1; + int64 lhs_non_contracting_size = 1; + std::vector batch_dim_sizes; + batch_dim_sizes.reserve(num_batch_dims); + for (int64 i = 0; i < lhs_rank; ++i) { + if (absl::c_linear_search(original_dnums.lhs_contracting_dimensions(), i)) { + lhs_contracting_size *= lhs_shape.dimensions(i); + } else if (absl::c_linear_search(original_dnums.lhs_batch_dimensions(), + i)) { + batch_dim_sizes.push_back(lhs_shape.dimensions(i)); + } else { + lhs_non_contracting_dims.push_back(i); + lhs_non_contracting_size *= lhs_shape.dimensions(i); + } + } + // The canonical form of the lhs is + // [BatchDims, NonContractingDims, ContractingsDims] + std::vector lhs_transpose; + lhs_transpose.reserve(lhs_rank); + lhs_transpose.insert(lhs_transpose.end(), + original_dnums.lhs_batch_dimensions().begin(), + original_dnums.lhs_batch_dimensions().end()); + lhs_transpose.insert(lhs_transpose.end(), lhs_non_contracting_dims.begin(), + lhs_non_contracting_dims.end()); + lhs_transpose.insert(lhs_transpose.end(), + original_dnums.lhs_contracting_dimensions().begin(), + original_dnums.lhs_contracting_dimensions().end()); + HloInstruction* transposed_lhs = + computation->AddInstruction(HloInstruction::CreateTranspose( + ShapeUtil::PermuteDimensions(InversePermutation(lhs_transpose), + lhs_shape), + original_dot->mutable_operand(0), lhs_transpose)); + std::vector lhs_reshape_dims = batch_dim_sizes; + lhs_reshape_dims.push_back(lhs_non_contracting_size); + lhs_reshape_dims.push_back(lhs_contracting_size); + // Reshape the contracting and non-contracting dimensions together. + HloInstruction* reshaped_lhs = + computation->AddInstruction(HloInstruction::CreateReshape( + ShapeUtil::MakeShape(lhs_shape.element_type(), lhs_reshape_dims), + transposed_lhs)); + + const auto& rhs_shape = original_dot->operand(1)->shape(); + const int64 rhs_rank = rhs_shape.rank(); + const int64 num_rhs_non_contracting_dims = + rhs_rank - num_batch_dims - num_contracting_dims; + std::vector rhs_non_contracting_dims; + rhs_non_contracting_dims.reserve(num_rhs_non_contracting_dims); + int64 rhs_non_contracting_size = 1; + int64 rhs_contracting_size = 1; + for (int64 i = 0; i < rhs_rank; ++i) { + if (absl::c_linear_search(original_dnums.rhs_contracting_dimensions(), i)) { + rhs_contracting_size *= rhs_shape.dimensions(i); + } else if (!absl::c_linear_search(original_dnums.rhs_batch_dimensions(), + i)) { + rhs_non_contracting_dims.push_back(i); + rhs_non_contracting_size *= rhs_shape.dimensions(i); + } + } + + // The canonical form of the rhs is + // [BatchDims, ContractingsDims, NonContractingDims] + std::vector rhs_transpose; + rhs_transpose.reserve(rhs_rank); + rhs_transpose.insert(rhs_transpose.end(), + original_dnums.rhs_batch_dimensions().begin(), + original_dnums.rhs_batch_dimensions().end()); + rhs_transpose.insert(rhs_transpose.end(), + original_dnums.rhs_contracting_dimensions().begin(), + original_dnums.rhs_contracting_dimensions().end()); + rhs_transpose.insert(rhs_transpose.end(), rhs_non_contracting_dims.begin(), + rhs_non_contracting_dims.end()); + HloInstruction* transposed_rhs = + computation->AddInstruction(HloInstruction::CreateTranspose( + ShapeUtil::PermuteDimensions(InversePermutation(rhs_transpose), + rhs_shape), + original_dot->mutable_operand(1), rhs_transpose)); + + std::vector rhs_reshape_dims = batch_dim_sizes; + rhs_reshape_dims.push_back(rhs_contracting_size); + rhs_reshape_dims.push_back(rhs_non_contracting_size); + // Reshape the contracting and non-contracting dimensions together. + HloInstruction* reshaped_rhs = + computation->AddInstruction(HloInstruction::CreateReshape( + ShapeUtil::MakeShape(rhs_shape.element_type(), rhs_reshape_dims), + transposed_rhs)); + + std::vector dot_dims = batch_dim_sizes; + dot_dims.push_back(lhs_non_contracting_size); + dot_dims.push_back(rhs_non_contracting_size); + + DotDimensionNumbers dot_dnums; + for (int64 i = 0; i < num_batch_dims; ++i) { + dot_dnums.add_lhs_batch_dimensions(i); + dot_dnums.add_rhs_batch_dimensions(i); + } + dot_dnums.add_lhs_contracting_dimensions(num_batch_dims + 1); + dot_dnums.add_rhs_contracting_dimensions(num_batch_dims); + + HloInstruction* dot = computation->AddInstruction(HloInstruction::CreateDot( + ShapeUtil::MakeShape(original_dot->shape().element_type(), dot_dims), + reshaped_lhs, reshaped_rhs, dot_dnums, original_dot->precision_config())); + + return computation->ReplaceInstruction( + original_dot, computation->AddInstruction(HloInstruction::CreateReshape( + original_dot->shape(), dot))); +} + } // namespace StatusOr DotDecomposer::Run(HloModule* module) { XLA_VLOG_LINES(2, "DotDecomposer ENTRY\n" + module->ToString()); - // Gather all batch Dot operations. - std::vector batch_dots; + // Gather all Non-canonical Dot operations. + std::vector non_canonical_dots; for (auto* computation : module->MakeNonfusionComputations()) { for (auto* instruction : computation->instructions()) { if (instruction->opcode() != HloOpcode::kDot) { continue; } const DotDimensionNumbers& dnums = instruction->dot_dimension_numbers(); - if (dnums.lhs_batch_dimensions_size() > 0 && decompose_batch_dot_) { - batch_dots.push_back(instruction); + // A dot it not canonical if there are more than one contracting + // dimension. + if (dnums.lhs_contracting_dimensions_size() > 1) { + non_canonical_dots.push_back(instruction); + continue; + } + if (dnums.lhs_batch_dimensions().empty()) { + continue; + } + std::vector canonical_batch_dims( + dnums.lhs_batch_dimensions_size()); + absl::c_iota(canonical_batch_dims, 0); + if (!absl::c_equal(dnums.lhs_batch_dimensions(), canonical_batch_dims) || + !absl::c_equal(dnums.rhs_batch_dimensions(), canonical_batch_dims)) { + non_canonical_dots.push_back(instruction); } } } - // Decompose each batch Dot in 'batch_dots'. bool changed = false; - for (auto* dot : batch_dots) { - TF_RETURN_IF_ERROR(DecomposeBatchDot(dot)); + for (auto* dot : non_canonical_dots) { + TF_RETURN_IF_ERROR(CanonicalizeDot(dot)); changed = true; } + + if (decompose_batch_dot_) { + std::vector batch_dots; + for (auto* computation : module->MakeNonfusionComputations()) { + for (auto* instruction : computation->instructions()) { + if (instruction->opcode() != HloOpcode::kDot) { + continue; + } + const DotDimensionNumbers& dnums = instruction->dot_dimension_numbers(); + if (!dnums.lhs_batch_dimensions().empty()) { + batch_dots.push_back(instruction); + } + } + } + // Decompose each batch Dot in 'batch_dots'. + + for (auto* dot : batch_dots) { + TF_RETURN_IF_ERROR(DecomposeBatchDot(dot)); + changed = true; + } + } XLA_VLOG_LINES(2, "DotDecompose EXIT\n" + module->ToString()); return changed; } diff --git a/tensorflow/compiler/xla/service/gpu/BUILD b/tensorflow/compiler/xla/service/gpu/BUILD index 5e81281bae..30a8bda606 100644 --- a/tensorflow/compiler/xla/service/gpu/BUILD +++ b/tensorflow/compiler/xla/service/gpu/BUILD @@ -699,6 +699,7 @@ cc_library( "//tensorflow/compiler/xla/service:call_inliner", "//tensorflow/compiler/xla/service:conditional_simplifier", "//tensorflow/compiler/xla/service:convolution_group_converter", + "//tensorflow/compiler/xla/service:dot_decomposer", "//tensorflow/compiler/xla/service:dynamic_index_splitter", "//tensorflow/compiler/xla/service:executable", "//tensorflow/compiler/xla/service:flatten_call_graph", diff --git a/tensorflow/compiler/xla/service/gpu/nvptx_compiler.cc b/tensorflow/compiler/xla/service/gpu/nvptx_compiler.cc index 9be7609508..d1522280ba 100644 --- a/tensorflow/compiler/xla/service/gpu/nvptx_compiler.cc +++ b/tensorflow/compiler/xla/service/gpu/nvptx_compiler.cc @@ -37,6 +37,7 @@ limitations under the License. #include "tensorflow/compiler/xla/service/call_inliner.h" #include "tensorflow/compiler/xla/service/conditional_simplifier.h" #include "tensorflow/compiler/xla/service/convolution_group_converter.h" +#include "tensorflow/compiler/xla/service/dot_decomposer.h" #include "tensorflow/compiler/xla/service/dynamic_index_splitter.h" #include "tensorflow/compiler/xla/service/flatten_call_graph.h" #include "tensorflow/compiler/xla/service/gpu/cudnn_batchnorm_rewriter.h" @@ -165,6 +166,7 @@ Status OptimizeHloModule(HloModule* hlo_module, se::StreamExecutor* stream_exec, // We need a cost model for GPUs. Currently, do nothing. return false; }; + pipeline.AddPass(false); pipeline.AddPass( cost_model, /*convert_batch_groups_only=*/true); diff --git a/tensorflow/compiler/xla/tests/BUILD b/tensorflow/compiler/xla/tests/BUILD index 106b3fd6c9..0fd0fc108a 100644 --- a/tensorflow/compiler/xla/tests/BUILD +++ b/tensorflow/compiler/xla/tests/BUILD @@ -711,6 +711,7 @@ xla_test( "//tensorflow/compiler/xla/client:local_client", "//tensorflow/compiler/xla/client:xla_builder", "//tensorflow/compiler/xla/tests:client_library_test_base", + "//tensorflow/compiler/xla/tests:hlo_test_base", "//tensorflow/compiler/xla/tests:literal_test_util", "//tensorflow/compiler/xla/tests:test_utils", "//tensorflow/compiler/xla/tests:xla_internal_test_main", @@ -768,6 +769,7 @@ xla_test( "//tensorflow/compiler/xla/client:local_client", "//tensorflow/compiler/xla/client:xla_builder", "//tensorflow/compiler/xla/tests:client_library_test_base", + "//tensorflow/compiler/xla/tests:hlo_test_base", "//tensorflow/compiler/xla/tests:literal_test_util", "//tensorflow/compiler/xla/tests:test_utils", "//tensorflow/compiler/xla/tests:xla_internal_test_main", diff --git a/tensorflow/compiler/xla/tests/dot_operation_test.cc b/tensorflow/compiler/xla/tests/dot_operation_test.cc index c5d8b663f4..2e02968ac5 100644 --- a/tensorflow/compiler/xla/tests/dot_operation_test.cc +++ b/tensorflow/compiler/xla/tests/dot_operation_test.cc @@ -25,6 +25,7 @@ limitations under the License. #include "tensorflow/compiler/xla/reference_util.h" #include "tensorflow/compiler/xla/shape_util.h" #include "tensorflow/compiler/xla/tests/client_library_test_base.h" +#include "tensorflow/compiler/xla/tests/hlo_test_base.h" #include "tensorflow/compiler/xla/tests/literal_test_util.h" #include "tensorflow/compiler/xla/tests/test_macros.h" #include "tensorflow/compiler/xla/tests/test_utils.h" @@ -1147,5 +1148,38 @@ XLA_TEST_F(DotOperationTest, DotRank2AndRank2NonDefaultContractionDims) { ComputeAndCompareR2(&builder, expected, {}, error_spec_); } + +class DotOperationTextTest : public HloTestBase {}; + +XLA_TEST_F(DotOperationTextTest, DotReorderedDotDims) { + absl::string_view hlo_string = + R"( +HloModule ComplexDotMultipleNonContracting + +ENTRY %test { + %lhs = f32[7,17,10,13]{3,2,1,0} parameter(0) + %rhs = f32[7,9,10,13,6]{4,3,2,1,0} parameter(1) + ROOT %dot = f32[10,7,17,9,6]{4,3,2,1,0} dot(%lhs, %rhs), lhs_batch_dims={2,0}, rhs_batch_dims={2,0}, lhs_contracting_dims={3}, rhs_contracting_dims={3} +} +)"; + + EXPECT_TRUE(RunAndCompare(hlo_string, ErrorSpec{1e-3, 1e-3})); +} + +XLA_TEST_F(DotOperationTextTest, DotReorderedDotDimsAndMultipleContracting) { + absl::string_view hlo_string = + R"( +HloModule ComplexDotMultipleNonContracting + +ENTRY %test { + %lhs = f32[7,5,17,10,13]{4,3,2,1,0} parameter(0) + %rhs = f32[7,9,10,13,6,5]{5,4,3,2,1,0} parameter(1) + ROOT %dot = f32[10,7,17,9,6]{4,3,2,1,0} dot(%lhs, %rhs), lhs_batch_dims={3,0}, rhs_batch_dims={2,0}, lhs_contracting_dims={1,4}, rhs_contracting_dims={5,3} +} +)"; + + EXPECT_TRUE(RunAndCompare(hlo_string, ErrorSpec{1e-3, 1e-3})); +} + } // namespace } // namespace xla -- GitLab From 495b3eeef0386b1b89b7aa9df42f2cf438de6ebc Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Tue, 8 Jan 2019 12:55:56 -0800 Subject: [PATCH 501/622] Enable fast GPU code path for solving many small linear systems in matrix_solve. PiperOrigin-RevId: 228382682 --- tensorflow/core/kernels/cuda_solvers.cc | 12 +++++----- tensorflow/core/kernels/cuda_solvers.h | 5 ++-- tensorflow/core/kernels/matrix_solve_op.cc | 28 +++++++++++++--------- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/tensorflow/core/kernels/cuda_solvers.cc b/tensorflow/core/kernels/cuda_solvers.cc index a59baaa96f..39d0a998fd 100644 --- a/tensorflow/core/kernels/cuda_solvers.cc +++ b/tensorflow/core/kernels/cuda_solvers.cc @@ -692,8 +692,8 @@ static inline Status GetrsBatchedImpl( SolverFnT solver, CudaSolver* cuda_solver, OpKernelContext* context, cublasHandle_t cublas_handle, cublasOperation_t trans, int n, int nrhs, const Scalar* const host_a_dev_ptrs[], int lda, const int* dev_pivots, - const Scalar* const host_b_dev_ptrs[], int ldb, - DeviceLapackInfo* dev_lapack_info, int batch_size) { + const Scalar* const host_b_dev_ptrs[], int ldb, int* host_lapack_info, + int batch_size) { mutex_lock lock(handle_map_mutex); using CudaScalar = typename CUDAComplexT::type; ScratchSpace dev_a_dev_ptrs = @@ -714,7 +714,7 @@ static inline Status GetrsBatchedImpl( cublas_handle, trans, n, nrhs, reinterpret_cast(dev_a_dev_ptrs.data()), lda, dev_pivots, reinterpret_cast(dev_b_dev_ptrs.mutable_data()), - ldb, dev_lapack_info->mutable_data(), batch_size)); + ldb, host_lapack_info, batch_size)); return Status::OK(); } @@ -723,13 +723,13 @@ static inline Status GetrsBatchedImpl( Status CudaSolver::GetrsBatched( \ cublasOperation_t trans, int n, int nrhs, \ const Scalar* const host_a_dev_ptrs[], int lda, const int* dev_pivots, \ - const Scalar* const host_b_dev_ptrs[], int ldb, \ - DeviceLapackInfo* dev_lapack_info, int batch_size) { \ + const Scalar* const host_b_dev_ptrs[], int ldb, int* host_lapack_info, \ + int batch_size) { \ return GetrsBatchedImpl(reinterpret_cast( \ BLAS_SOLVER_FN(getrsBatched, type_prefix)), \ this, context_, cublas_handle_, trans, n, nrhs, \ host_a_dev_ptrs, lda, dev_pivots, host_b_dev_ptrs, \ - ldb, dev_lapack_info, batch_size); \ + ldb, host_lapack_info, batch_size); \ } TF_CALL_LAPACK_TYPES(GETRS_BATCHED_INSTANCE); diff --git a/tensorflow/core/kernels/cuda_solvers.h b/tensorflow/core/kernels/cuda_solvers.h index 2c30d036df..1fc344731c 100644 --- a/tensorflow/core/kernels/cuda_solvers.h +++ b/tensorflow/core/kernels/cuda_solvers.h @@ -235,13 +235,14 @@ class CudaSolver { int batch_size) TF_MUST_USE_RESULT; // Batched linear solver using LU factorization from getrfBatched. - // See: + // Notice that lapack_info is returned on the host, as opposed to + // most of the other functions that return it on the device. See: // http://docs.nvidia.com/cuda/cublas/index.html#cublas-lt-t-gt-getrsbatched template Status GetrsBatched(cublasOperation_t trans, int n, int nrhs, const Scalar* const dev_Aarray[], int lda, const int* devIpiv, const Scalar* const dev_Barray[], - int ldb, DeviceLapackInfo* dev_lapack_info, + int ldb, int* host_lapack_info, int batch_size) TF_MUST_USE_RESULT; // Computes matrix inverses for a batch of small matrices. Uses the outputs diff --git a/tensorflow/core/kernels/matrix_solve_op.cc b/tensorflow/core/kernels/matrix_solve_op.cc index 169f3dae76..f3919a16aa 100644 --- a/tensorflow/core/kernels/matrix_solve_op.cc +++ b/tensorflow/core/kernels/matrix_solve_op.cc @@ -214,9 +214,12 @@ class MatrixSolveOpGpu : public AsyncOpKernel { auto input_copy_ptrs = solver->GetScratchSpace( sizeof(Scalar*) * batch_size, "input_copt_ptrs", /* on_host */ true); - if (n / batch_size <= 128) { - // For small matrices or large batch sizes, we use the batched - // interface from cuBlas. + const int kMaxMatrixSizeToBatchSizeRatio = 128; + const bool use_batched_solver = + n <= kMaxMatrixSizeToBatchSizeRatio * batch_size; + if (use_batched_solver) { + // For small matrices or large batch sizes, we use the batched interface + // from cuBlas. const Scalar** input_copy_ptrs_base = reinterpret_cast(input_copy_ptrs.mutable_data()); for (int batch = 0; batch < batch_size; ++batch) { @@ -230,8 +233,8 @@ class MatrixSolveOpGpu : public AsyncOpKernel { &dev_info.back(), batch_size), done); } else { - // For small batch sizes we use the non-batched interface from cuSolver, - // which is much faster for large matrices. + // For small batch sizes or large matrices, we use the non-batched + // interface from cuSolver, which is much faster for large matrices. dev_info.push_back(solver->GetDeviceLapackInfo(batch_size, "getrf")); for (int batch = 0; batch < batch_size; ++batch) { OP_REQUIRES_OK_ASYNC( @@ -279,11 +282,7 @@ class MatrixSolveOpGpu : public AsyncOpKernel { /* on_host */ true); auto transposed_rhs_reshaped = transposed_rhs.template flat_inner_dims(); - // TODO(rmlarsen): Enable the following branch when I figure - // out why it causes a segfault. - if (false && n / batch_size <= 128) { - dev_info.push_back( - solver->GetDeviceLapackInfo(batch_size, "GetrsBatched")); + if (use_batched_solver) { const Scalar** input_copy_ptrs_base = reinterpret_cast(input_copy_ptr_array.mutable_data()); const Scalar** transposed_rhs_ptrs_base = @@ -293,13 +292,20 @@ class MatrixSolveOpGpu : public AsyncOpKernel { input_copy_ptrs_base[batch] = &input_copy_reshaped(batch, 0, 0); transposed_rhs_ptrs_base[batch] = &transposed_rhs_reshaped(batch, 0, 0); } + int host_info = 0; OP_REQUIRES_OK_ASYNC( context, solver->GetrsBatched(adjoint_ ? CUBLAS_OP_C : CUBLAS_OP_T, n, nrhs, input_copy_ptrs_base, n, pivots_mat.data(), - transposed_rhs_ptrs_base, n, &dev_info.back(), + transposed_rhs_ptrs_base, n, &host_info, batch_size), done); + OP_REQUIRES_ASYNC( + context, host_info == 0, + errors::InvalidArgument("The ", -host_info, + "'th argument to cublas*getrsBatched had " + "an illegal value."), + done); } else { dev_info.push_back(solver->GetDeviceLapackInfo(batch_size, "getrs")); for (int batch = 0; batch < batch_size; ++batch) { -- GitLab From 01dd371b8441aa96536732b804f1c5853fb8af6e Mon Sep 17 00:00:00 2001 From: Mark Daoust Date: Tue, 8 Jan 2019 13:29:19 -0800 Subject: [PATCH 502/622] remove JackOptions block. PiperOrigin-RevId: 228388690 --- tensorflow/lite/g3doc/demo_ios.md | 45 ++++++++++-------- .../g3doc/images/ios/bundle_identifier.png | Bin 0 -> 64902 bytes 2 files changed, 26 insertions(+), 19 deletions(-) create mode 100644 tensorflow/lite/g3doc/images/ios/bundle_identifier.png diff --git a/tensorflow/lite/g3doc/demo_ios.md b/tensorflow/lite/g3doc/demo_ios.md index fbf1dd6392..f4b481dc61 100644 --- a/tensorflow/lite/g3doc/demo_ios.md +++ b/tensorflow/lite/g3doc/demo_ios.md @@ -7,22 +7,23 @@ instructions walk you through building and running the demo on an iOS device. ## Prerequisites -* You must have [Xcode](https://developer.apple.com/xcode/) installed and have a - valid Apple Developer ID, and have an iOS device set up and linked to your - developer account with all of the appropriate certificates. For these - instructions, we assume that you have already been able to build and deploy an - app to an iOS device with your current developer environment. +* You must have [Xcode](https://developer.apple.com/xcode/) installed and have + a valid Apple Developer ID, and have an iOS device set up and linked to your + developer account with all of the appropriate certificates. For these + instructions, we assume that you have already been able to build and deploy + an app to an iOS device with your current developer environment. -* The demo app requires a camera and must be executed on a real iOS device. You - can build it and run with the iPhone Simulator but it won't have any camera - information to classify. +* The demo app requires a camera and must be executed on a real iOS device. + You can build it and run with the iPhone Simulator but it won't have any + camera information to classify. -* You don't need to build the entire TensorFlow library to run the demo, but you - will need to clone the TensorFlow repository if you haven't already: +* You don't need to build the entire TensorFlow library to run the demo, but + you will need to clone the TensorFlow repository if you haven't already: git clone https://github.com/tensorflow/tensorflow + cd tensorflow -* You'll also need the Xcode command-line tools: +* You'll also need the Xcode command-line tools: xcode-select --install @@ -31,35 +32,41 @@ instructions walk you through building and running the demo on an iOS device. ## Building the iOS Demo App -1. Install CocoaPods if you don't have it: +1. Install CocoaPods if you don't have it: sudo gem install cocoapods -2. Download the model files used by the demo app (this is done from inside the - cloned directory): +2. Download the model files used by the demo app (this is done from inside the + cloned directory): sh tensorflow/lite/examples/ios/download_models.sh -3. Install the pod to generate the workspace file: +3. Install the pod to generate the workspace file: cd tensorflow/lite/examples/ios/camera pod install If you have installed this pod before and that command doesn't work, try - pod update + pod repo update - At the end of this step you should have a file called + At the end of this step you should have a file called `tflite_camera_example.xcworkspace`. -4. Open the project in Xcode by typing this on the command line: +4. Open the project in Xcode by typing this on the command line: open tflite_camera_example.xcworkspace This launches Xcode if it isn't open already and opens the `tflite_camera_example` project. -5. Build and run the app in Xcode. +5. Under `Project navigator -> tflite_camera_example -> Targets -> + tflite_camera_example -> General` change the bundle identifier by + pre-pending your name: + + ![pre-pend your name to the bundle identifier](images/ios/bundle_identifier.png) + +6. Build and run the app in Xcode. Note that as mentioned earlier, you must already have a device set up and linked to your Apple Developer account in order to deploy the app on a diff --git a/tensorflow/lite/g3doc/images/ios/bundle_identifier.png b/tensorflow/lite/g3doc/images/ios/bundle_identifier.png new file mode 100644 index 0000000000000000000000000000000000000000..398763916b353e61f236392e2b8898aad2aafe8e GIT binary patch literal 64902 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 Ae*gdg literal 0 HcmV?d00001 -- GitLab From 530d1b267f124fdb7dd6ce65b94423b8801152cd Mon Sep 17 00:00:00 2001 From: Tim Shen Date: Tue, 8 Jan 2019 13:29:30 -0800 Subject: [PATCH 503/622] Add fine-grained BUILD files for stream executor. PiperOrigin-RevId: 228388744 --- tensorflow/compiler/xla/BUILD | 2 +- tensorflow/compiler/xla/service/cpu/BUILD | 1 + tensorflow/compiler/xla/service/gpu/BUILD | 1 + .../compiler/xla/service/interpreter/BUILD | 2 + tensorflow/compiler/xrt/kernels/BUILD | 2 +- tensorflow/core/kernels/BUILD | 3 + .../core/platform/default/build_config/BUILD | 39 +- tensorflow/opensource_only.files | 2 +- tensorflow/stream_executor/BUILD | 703 +++++++++++++++--- tensorflow/stream_executor/build_defs.bzl | 11 + tensorflow/stream_executor/cuda/BUILD | 365 +++++++++ tensorflow/stream_executor/host/BUILD | 108 +++ tensorflow/stream_executor/lib/BUILD | 62 ++ tensorflow/stream_executor/platform/BUILD | 47 ++ .../stream_executor/platform/default/BUILD | 25 + 15 files changed, 1277 insertions(+), 96 deletions(-) create mode 100644 tensorflow/stream_executor/build_defs.bzl create mode 100644 tensorflow/stream_executor/cuda/BUILD create mode 100644 tensorflow/stream_executor/host/BUILD create mode 100644 tensorflow/stream_executor/lib/BUILD create mode 100644 tensorflow/stream_executor/platform/BUILD create mode 100644 tensorflow/stream_executor/platform/default/BUILD diff --git a/tensorflow/compiler/xla/BUILD b/tensorflow/compiler/xla/BUILD index 45623c2718..aa43fc8dab 100644 --- a/tensorflow/compiler/xla/BUILD +++ b/tensorflow/compiler/xla/BUILD @@ -152,7 +152,7 @@ cc_library( ":status", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", - "//tensorflow/stream_executor", + "//tensorflow/stream_executor/lib", ], ) diff --git a/tensorflow/compiler/xla/service/cpu/BUILD b/tensorflow/compiler/xla/service/cpu/BUILD index 1eb42cf5ad..de62aa60aa 100644 --- a/tensorflow/compiler/xla/service/cpu/BUILD +++ b/tensorflow/compiler/xla/service/cpu/BUILD @@ -241,6 +241,7 @@ cc_library( "//tensorflow/compiler/xla/service:tuple_points_to_analysis", "//tensorflow/core:lib", "//tensorflow/core:stream_executor_no_cuda", + "//tensorflow/stream_executor/host:host_stream", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", "@com_google_absl//absl/types:span", diff --git a/tensorflow/compiler/xla/service/gpu/BUILD b/tensorflow/compiler/xla/service/gpu/BUILD index 30a8bda606..2be5d918ee 100644 --- a/tensorflow/compiler/xla/service/gpu/BUILD +++ b/tensorflow/compiler/xla/service/gpu/BUILD @@ -731,6 +731,7 @@ cc_library( "//tensorflow/core:lib_internal", "//tensorflow/core:regexp_internal", "//tensorflow/core:stream_executor_no_cuda", + "//tensorflow/stream_executor/cuda:cuda_diagnostics", "@com_google_absl//absl/container:node_hash_map", "@com_google_absl//absl/memory", "@com_google_absl//absl/strings", diff --git a/tensorflow/compiler/xla/service/interpreter/BUILD b/tensorflow/compiler/xla/service/interpreter/BUILD index 2e1d2cfbda..9b4cafa4b4 100644 --- a/tensorflow/compiler/xla/service/interpreter/BUILD +++ b/tensorflow/compiler/xla/service/interpreter/BUILD @@ -119,6 +119,8 @@ cc_library( "//tensorflow/compiler/xla:xla_data_proto", "//tensorflow/core:lib", "//tensorflow/core:stream_executor_headers_lib", + "//tensorflow/stream_executor/host:host_stream", + "//tensorflow/stream_executor/host:host_timer", "@com_google_absl//absl/types:span", ], ) diff --git a/tensorflow/compiler/xrt/kernels/BUILD b/tensorflow/compiler/xrt/kernels/BUILD index 67f475846e..78d093ec4a 100644 --- a/tensorflow/compiler/xrt/kernels/BUILD +++ b/tensorflow/compiler/xrt/kernels/BUILD @@ -62,7 +62,7 @@ cc_library( "//tensorflow/core:lib", "//tensorflow/core:lib_internal", "//tensorflow/core:protos_all_cc", - "//tensorflow/stream_executor:stream_executor_headers_lib", + "//tensorflow/stream_executor:stream_executor_headers", "@com_google_absl//absl/strings", ], alwayslink = 1, diff --git a/tensorflow/core/kernels/BUILD b/tensorflow/core/kernels/BUILD index aaea09bdc4..5794ba520e 100644 --- a/tensorflow/core/kernels/BUILD +++ b/tensorflow/core/kernels/BUILD @@ -2313,6 +2313,7 @@ tf_kernel_library( "//tensorflow/core/grappler/clusters:virtual_cluster", "//tensorflow/core/grappler/optimizers:meta_optimizer", "//tensorflow/core/grappler/utils:functions", + "//tensorflow/stream_executor:stream", ], ) @@ -3819,6 +3820,8 @@ tf_kernel_library( deps = NN_DEPS + if_cuda([ ":reduction_ops", "@cub_archive//:cub", + "//tensorflow/core:stream_executor", + "//tensorflow/stream_executor/cuda:cuda_stream", ]), ) diff --git a/tensorflow/core/platform/default/build_config/BUILD b/tensorflow/core/platform/default/build_config/BUILD index da1f66dc67..f5a194428c 100644 --- a/tensorflow/core/platform/default/build_config/BUILD +++ b/tensorflow/core/platform/default/build_config/BUILD @@ -32,15 +32,25 @@ cc_library( tf_cuda_library( name = "stream_executor", - deps = [ - "//tensorflow/stream_executor", + cuda_deps = [ + "//tensorflow/stream_executor/cuda:cuda_activation", ] + select({ - "//tensorflow:using_cuda_clang": ["//tensorflow/stream_executor:cuda_platform"], - "//tensorflow:using_cuda_nvcc": ["//tensorflow/stream_executor:cuda_platform"], + "//tensorflow:using_cuda_clang": ["//tensorflow/stream_executor/cuda:all_runtime"], + "//tensorflow:using_cuda_nvcc": ["//tensorflow/stream_executor/cuda:all_runtime"], "//tensorflow:using_cuda_clang_with_dynamic_build": [], "//tensorflow:using_cuda_nvcc_with_dynamic_build": [], "//conditions:default": [], - }) + select({ + }), + deps = [ + "//tensorflow/stream_executor", + "//tensorflow/stream_executor:dnn", + "//tensorflow/stream_executor:event", + "//tensorflow/stream_executor:multi_platform_manager", + "//tensorflow/stream_executor:scratch_allocator", + "//tensorflow/stream_executor/cuda:cuda_platform_id", + "//tensorflow/stream_executor/host:host_platform_id", + "//tensorflow/stream_executor/platform:dso_loader", + ] + select({ "@local_config_cuda//cuda:darwin": ["IOKit"], "//conditions:default": [], }), @@ -49,9 +59,10 @@ tf_cuda_library( cc_library( name = "stream_executor_cuda", deps = [ - "//tensorflow/stream_executor", + ":stream_executor_no_cuda", + ":cuda", ] + if_static( - ["//tensorflow/stream_executor:cuda_platform"], + ["//tensorflow/stream_executor/cuda:all_runtime"], ) + select({ "@local_config_cuda//cuda:darwin": ["IOKit"], "//conditions:default": [], @@ -62,23 +73,31 @@ cc_library( name = "stream_executor_no_cuda", deps = [ "//tensorflow/stream_executor", + "//tensorflow/stream_executor:dnn", + "//tensorflow/stream_executor:event", + "//tensorflow/stream_executor:multi_platform_manager", + "//tensorflow/stream_executor:scratch_allocator", + "//tensorflow/stream_executor/cuda:cuda_platform_id", + "//tensorflow/stream_executor/host:host_platform", + "//tensorflow/stream_executor/host:host_platform_id", + "//tensorflow/stream_executor/platform:dso_loader", ], ) # Dummy stream executor cuda plugins. cc_library( name = "cublas_plugin", - srcs = [], + deps = if_static(["//tensorflow/stream_executor/cuda:cublas_plugin"]), ) cc_library( name = "cufft_plugin", - srcs = [], + deps = if_static(["//tensorflow/stream_executor/cuda:cufft_plugin"]), ) cc_library( name = "cudnn_plugin", - srcs = [], + deps = if_static(["//tensorflow/stream_executor/cuda:cudnn_plugin"]), ) # OSX framework for device driver access diff --git a/tensorflow/opensource_only.files b/tensorflow/opensource_only.files index e00d063cfe..bade64dcf8 100644 --- a/tensorflow/opensource_only.files +++ b/tensorflow/opensource_only.files @@ -241,7 +241,7 @@ tensorflow/third_party/tflite_ovic_testdata.BUILD tensorflow/third_party/libxsmm.BUILD tensorflow/third_party/zlib.BUILD tensorflow/third_party/eigen.BUILD -tensorflow/stream_executor/BUILD +tensorflow/stream_executor/build_defs.bzl tensorflow/api_template_v1.__init__.py tensorflow/compat_template_v1.__init__.py tensorflow/api_template.__init__.py diff --git a/tensorflow/stream_executor/BUILD b/tensorflow/stream_executor/BUILD index 3c45bf314a..7cf7e75a55 100644 --- a/tensorflow/stream_executor/BUILD +++ b/tensorflow/stream_executor/BUILD @@ -1,126 +1,663 @@ -licenses(["restricted"]) +# GPU executor library for data-parallel kernel launches and cross-platform +# HPC-library APIs. +# +# Throughout this file, all targets are built with the standard crosstool and +# do not link against restricted binary blobs. -load("@local_config_cuda//cuda:build_defs.bzl", "if_cuda_is_configured") +licenses(["notice"]) # Apache 2.0 + +load("//tensorflow:tensorflow.bzl", "tf_cc_test") load("//tensorflow/core:platform/default/build_config.bzl", "tf_proto_library") load("//tensorflow/core:platform/default/build_config_root.bzl", "if_static") -load("//tensorflow:tensorflow.bzl", "cc_header_only_library") +load("//tensorflow/stream_executor:build_defs.bzl", "stream_executor_friends") + +package_group( + name = "friends", + packages = stream_executor_friends(), +) + +package( + default_visibility = [":friends"], +) + +# Filegroup used to collect source files for the dependency check. +filegroup( + name = "c_srcs", + data = glob([ + "**/*.cc", + "**/*.h", + ]), +) + +cc_library( + name = "launch_dim", + hdrs = [ + "gpu_launch_dim.h", + "launch_dim.h", + ], + deps = [ + "//tensorflow/stream_executor/platform", + "@com_google_absl//absl/strings", + ], +) + +cc_library( + name = "device_description", + srcs = ["device_description.cc"], + hdrs = ["device_description.h"], + deps = [ + ":launch_dim", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/strings", + ], +) + +cc_library( + name = "event", + srcs = [ + "blas.h", + "device_description.h", + "device_options.h", + "dnn.h", + "event.cc", + "fft.h", + "kernel_cache_config.h", + "launch_dim.h", + "plugin.h", + "plugin_registry.h", + "rng.h", + "shared_memory_config.h", + "stream_executor_pimpl.h", + "temporary_device_memory.h", + "temporary_memory_manager.h", + "trace_listener.h", + ], + hdrs = [ + "device_memory.h", + "event.h", + "kernel.h", + "kernel_spec.h", + "platform.h", + "stream.h", + "stream_executor_internal.h", + ], + deps = [ + ":dnn_proto_cc", + ":host_or_device_scalar", + ":stream_executor_headers", + "//tensorflow/core:lib", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:optional", + "@com_google_absl//absl/types:span", + ], +) + +cc_library( + name = "kernel", + srcs = [ + "dnn.h", + "fft.h", + "kernel.cc", + "plugin.h", + "rng.h", + "stream.h", + "stream_executor_pimpl.h", + "temporary_device_memory.h", + "temporary_memory_manager.h", + ], + hdrs = [ + "blas.h", + "device_description.h", + "device_options.h", + "event.h", + "kernel.h", + "kernel_spec.h", + "launch_dim.h", + "multi_platform_manager.h", + "platform.h", + "plugin_registry.h", + "shared_memory_config.h", + "stream_executor.h", + "stream_executor_internal.h", + "timer.h", + "trace_listener.h", + ], + deps = [ + ":device_memory", + ":dnn_proto_cc", + ":host_or_device_scalar", + ":kernel_cache_config", + ":stream_executor_headers", + "//tensorflow/core:lib", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:optional", + "@com_google_absl//absl/types:span", + ], +) -STREAM_EXECUTOR_HEADERS = glob([ - "*.h", - "cuda/*.h", - "host/*.h", - "lib/*.h", - "lib/gtl/*.h", - "platform/**/*.h", -]) +cc_library( + name = "kernel_spec", + srcs = ["kernel_spec.cc"], + hdrs = ["kernel_spec.h"], + deps = [ + "//tensorflow/stream_executor/platform", + "@com_google_absl//absl/strings", + ], +) + +cc_library( + name = "kernel_cache_config", + hdrs = ["kernel_cache_config.h"], +) + +cc_library( + name = "module_spec", + hdrs = ["module_spec.h"], + deps = [ + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + ], +) + +cc_library( + name = "shared_memory_config", + hdrs = ["shared_memory_config.h"], +) + +cc_library( + name = "stream_header", + hdrs = [ + "blas.h", + "device_memory.h", + "dnn.h", + "event.h", + "fft.h", + "gpu_launch_dim.h", + "kernel.h", + "kernel_cache_config.h", + "launch_dim.h", + "stream.h", + "temporary_device_memory.h", + "temporary_memory_manager.h", + ], + visibility = ["//visibility:public"], + deps = [ + ":dnn_proto_cc", + ":host_or_device_scalar", + ":stream_executor_headers", + "//tensorflow/core:lib", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:optional", + "@com_google_absl//absl/types:span", + ], +) + +# It implements :stream_header +cc_library( + name = "stream", + srcs = [ + "host_buffer.h", + "stream.cc", + ], + hdrs = ["stream.h"], + deps = [ + ":blas", + ":device_memory", + ":dnn", + ":event", + ":fft", + ":host_or_device_scalar", + ":kernel", + ":launch_dim", + ":platform", + ":rng", + ":stream_executor_headers", + ":stream_executor_internal", + ":stream_executor_pimpl", + ":temporary_memory_manager", + "//tensorflow/core:lib", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + "//third_party/eigen3", + "@com_google_absl//absl/strings", + ], +) + +cc_library( + name = "timer", + srcs = [ + "device_description.h", + "kernel_cache_config.h", + "timer.cc", + ], + hdrs = [ + "blas.h", + "kernel.h", + "stream.h", + "stream_executor.h", + "timer.h", + ], + deps = [ + ":host_or_device_scalar", + ":platform", + ":stream_executor_headers", + ":stream_executor_internal", + ":stream_executor_pimpl_header", + "//tensorflow/core:lib", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/strings", + ], +) + +cc_library( + name = "platform", + srcs = ["platform.cc"], + hdrs = ["platform.h"], + deps = [ + ":plugin", + ":stream_executor_headers", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + "@com_google_absl//absl/strings", + ], +) + +cc_library( + name = "rng", + srcs = ["rng.cc"], + hdrs = ["rng.h"], + deps = ["//tensorflow/stream_executor/platform"], +) + +cc_library( + name = "temporary_device_memory", + srcs = [ + "event.h", + "temporary_device_memory.cc", + "temporary_memory_manager.h", + ], + hdrs = ["temporary_device_memory.h"], + deps = [ + ":device_memory", + ":stream_header", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + ], +) + +cc_library( + name = "temporary_memory_manager", + srcs = ["temporary_memory_manager.cc"], + hdrs = ["temporary_memory_manager.h"], + deps = [ + ":device_memory", + ":stream_executor_pimpl_header", + ":stream_header", + ":temporary_device_memory", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + "@com_google_absl//absl/strings", + ], +) + +cc_library( + name = "fft", + hdrs = ["fft.h"], + deps = [ + "//tensorflow/stream_executor/platform", + ], +) + +cc_library( + name = "blas", + srcs = ["blas.cc"], + hdrs = ["blas.h"], + deps = [ + ":host_or_device_scalar", + ":stream_executor_headers", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + "@com_google_absl//absl/strings", + ], +) + +cc_library( + name = "device_memory", + hdrs = ["device_memory.h"], + deps = ["//tensorflow/stream_executor/platform"], +) + +cc_library( + name = "host_or_device_scalar", + hdrs = ["host_or_device_scalar.h"], + deps = [ + ":device_memory", + "//tensorflow/stream_executor/platform", + ], +) + +cc_library( + name = "device_options", + hdrs = ["device_options.h"], + deps = [ + "//tensorflow/stream_executor/platform", + ], +) + +cc_library( + name = "executor_cache", + srcs = [ + "device_description.h", + "device_memory.h", + "device_options.h", + "event.h", + "executor_cache.cc", + "launch_dim.h", + "plugin.h", + "plugin_registry.h", + "rng.h", + "stream_executor_pimpl.h", + "temporary_device_memory.h", + "temporary_memory_manager.h", + ], + hdrs = [ + "blas.h", + "dnn.h", + "executor_cache.h", + "fft.h", + "kernel.h", + "kernel_cache_config.h", + "kernel_spec.h", + "platform.h", + "shared_memory_config.h", + "stream.h", + "stream_executor_internal.h", + "trace_listener.h", + ], + deps = [ + ":dnn_proto_cc", + ":host_or_device_scalar", + ":stream_executor_headers", + "//tensorflow/core:lib", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:optional", + "@com_google_absl//absl/types:span", + ], +) + +cc_library( + name = "multi_platform_manager", + srcs = ["multi_platform_manager.cc"], + hdrs = ["multi_platform_manager.h"], + deps = [ + ":platform", + ":stream_executor_headers", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/synchronization", + ], +) + +cc_library( + name = "plugin", + srcs = ["plugin.cc"], + hdrs = ["plugin.h"], +) + +cc_library( + name = "plugin_registry", + srcs = ["plugin_registry.cc"], + hdrs = ["plugin_registry.h"], + deps = [ + ":blas", + ":dnn", + ":fft", + ":multi_platform_manager", + ":platform", + ":plugin", + ":stream_executor_headers", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + "@com_google_absl//absl/base:core_headers", + ], +) + +cc_library( + name = "scratch_allocator", + srcs = ["scratch_allocator.cc"], + hdrs = ["scratch_allocator.h"], + deps = [ + ":device_memory", + ":stream_header", + ":temporary_device_memory", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + ], +) + +cc_library( + name = "host_buffer", + hdrs = ["host_buffer.h"], + deps = [":dnn"], +) tf_proto_library( name = "dnn_proto", srcs = ["dnn.proto"], cc_api_version = 2, default_header = True, + provide_cc_alias = True, ) tf_proto_library( name = "logging_proto", srcs = ["logging.proto"], cc_api_version = 2, - default_header = True, protodeps = [":dnn_proto"], + provide_cc_alias = True, + visibility = [":friends"], ) cc_library( - name = "stream_executor_impl", - srcs = glob( - [ - "*.cc", - "host/*.cc", - "cuda/cuda_platform_id.cc", - "lib/*.cc", - "platform/default/*.cc", - ], - exclude = [ - "**/*_test.cc", - ], - ), - hdrs = STREAM_EXECUTOR_HEADERS, - linkopts = select({ - "//tensorflow:freebsd": [], - "//tensorflow:windows": [], - "//conditions:default": ["-ldl"], - }), - visibility = ["//visibility:public"], + name = "dnn", + srcs = ["dnn.cc"], + hdrs = ["dnn.h"], deps = [ - ":dnn_proto_cc_impl", - ":logging_proto_cc_impl", + ":device_memory", + ":dnn_proto_cc", + ":stream_executor_headers", "//tensorflow/core:lib", - "//tensorflow/core:logger", - "//tensorflow/core:ptr_util", - "@com_google_absl//absl/container:flat_hash_map", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", "@com_google_absl//absl/strings", - "@com_google_absl//absl/strings:str_format", - "@com_google_absl//absl/synchronization", - "@local_config_cuda//cuda:cuda_headers", + "@com_google_absl//absl/types:optional", + "@com_google_absl//absl/types:span", ], - alwayslink = 1, ) cc_library( - name = "stream_executor", - hdrs = STREAM_EXECUTOR_HEADERS, + name = "stream_executor_internal", + srcs = [ + "dnn.h", + "stream_executor_internal.cc", + ], + hdrs = [ + "shared_memory_config.h", + "stream_executor_internal.h", + ], + deps = [ + ":device_description", + ":device_memory", + ":device_options", + ":dnn_proto_cc", + ":kernel", + ":kernel_cache_config", + ":kernel_spec", + ":launch_dim", + ":plugin_registry", + ":stream_executor_headers", + "//tensorflow/core:lib", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + "@com_google_absl//absl/types:optional", + "@com_google_absl//absl/types:span", + ], +) + +cc_library( + name = "stream_executor_pimpl_header", + hdrs = [ + "device_description.h", + "dnn.h", + "kernel.h", + "kernel_cache_config.h", + "shared_memory_config.h", + "stream_executor_pimpl.h", + ], visibility = ["//visibility:public"], deps = [ ":dnn_proto_cc", - ":logging_proto_cc", + ":platform", + ":stream_executor_headers", + ":stream_executor_internal", "//tensorflow/core:lib", - "//tensorflow/core:ptr_util", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/strings", - "@local_config_cuda//cuda:cuda_headers", - ] + if_static([":stream_executor_impl"]), + "@com_google_absl//absl/types:optional", + "@com_google_absl//absl/types:span", + ], ) -cc_header_only_library( - name = "stream_executor_headers_lib", - visibility = ["//visibility:public"], +# It implements :stream_executor_pimpl_header +cc_library( + name = "stream_executor_pimpl", + srcs = ["stream_executor_pimpl.cc"], + hdrs = ["stream_executor_pimpl.h"], deps = [ - ":stream_executor", + ":blas", + ":executor_cache", + ":fft", + ":kernel", + ":platform", + ":rng", + ":stream_executor_headers", + ":stream_header", + ":timer", + "//tensorflow/core:lib_internal", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/strings", ], ) +# The stream_executor_headers target does not prescribe an implementation. +# +# TODO(b/25131218) this is OBSOLETE/DEPRECATED -- get rid of this target altogether cc_library( - name = "cuda_platform", - srcs = if_cuda_is_configured( - glob( - [ - "cuda/*.cc", - ], - exclude = [ - "cuda/*_test.cc", - "cuda/cuda_platform_id.cc", - ], - ), - ), - copts = select({ - "//tensorflow:windows": ["/DNOGDI"], - "//conditions:default": [], - }), - data = [ - "@local_config_cuda//cuda:cudnn", - ], - linkopts = select({ - "//tensorflow:freebsd": [], - "//tensorflow:windows": [], - "//conditions:default": ["-ldl"], - }), + name = "stream_executor_headers", + hdrs = [ + "blas.h", + "device_description.h", + "device_memory.h", + "device_options.h", + "dnn.h", + "event.h", + "executor_cache.h", + "fft.h", + "gpu_launch_dim.h", + "kernel.h", + "kernel_cache_config.h", + "kernel_spec.h", + "launch_dim.h", + "module_spec.h", + "multi_platform_manager.h", + "platform.h", + "plugin.h", + "plugin_registry.h", + "rng.h", + "shared_memory_config.h", + "stream.h", + "stream_executor.h", + "stream_executor_internal.h", + "stream_executor_pimpl.h", + "temporary_device_memory.h", + "temporary_memory_manager.h", + "timer.h", + "trace_listener.h", + ], visibility = ["//visibility:public"], deps = [ - ":stream_executor", + ":dnn_proto_cc", + ":host_or_device_scalar", "//tensorflow/core:lib", - "//tensorflow/core/kernels:ops_util", - "@local_config_cuda//cuda:cuda_headers", - ] + if_cuda_is_configured([ - "//tensorflow/core:cuda", - "@local_config_cuda//cuda:cuda_driver", - ]), - alwayslink = 1, + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:optional", + "@com_google_absl//absl/types:span", + ], +) + +cc_library( + name = "stream_executor", + hdrs = ["stream_executor.h"], + deps = [":stream_executor_headers"] + if_static([":stream_executor_impl"]), +) + +cc_library( + name = "stream_executor_impl", + deps = [ + ":device_description", + ":device_memory", + ":dnn_proto_cc", + ":dnn_proto_cc_impl", + ":event", + ":kernel", + ":launch_dim", + ":multi_platform_manager", + ":platform", + ":stream", + ":stream_executor_headers", + ":stream_executor_pimpl", + ":timer", + ], +) + +tf_cc_test( + name = "stream_test", + size = "small", + srcs = ["stream_test.cc"], + deps = [ + ":stream_executor", + "//tensorflow/core:test", + "//tensorflow/core:test_main", + "//tensorflow/stream_executor/host:host_platform", + ], +) + +alias( + name = "cuda_platform", + actual = "//tensorflow/stream_executor/cuda:all_runtime", ) diff --git a/tensorflow/stream_executor/build_defs.bzl b/tensorflow/stream_executor/build_defs.bzl new file mode 100644 index 0000000000..a7ddf5a0d7 --- /dev/null +++ b/tensorflow/stream_executor/build_defs.bzl @@ -0,0 +1,11 @@ +def stream_executor_friends(): + return ["//tensorflow/..."] + +def tf_additional_cuda_platform_deps(): + return [] + +def tf_additional_cuda_driver_deps(): + return ["@local_config_cuda//cuda:cuda_driver"] + +def tf_additional_cudnn_plugin_deps(): + return [] diff --git a/tensorflow/stream_executor/cuda/BUILD b/tensorflow/stream_executor/cuda/BUILD new file mode 100644 index 0000000000..58c9480884 --- /dev/null +++ b/tensorflow/stream_executor/cuda/BUILD @@ -0,0 +1,365 @@ +# Description: +# CUDA-platform specific StreamExecutor support code. + +licenses(["notice"]) # Apache 2.0 + +load("//tensorflow:tensorflow.bzl", "tf_cc_test") +load( + "//tensorflow/stream_executor:build_defs.bzl", + "stream_executor_friends", + "tf_additional_cuda_driver_deps", + "tf_additional_cuda_platform_deps", + "tf_additional_cudnn_plugin_deps", +) +load("//tensorflow:tensorflow.bzl", "tf_copts") +load("@local_config_cuda//cuda:build_defs.bzl", "if_cuda_is_configured") +load("//tensorflow/core:platform/default/build_config_root.bzl", "if_static") + +package_group( + name = "friends", + packages = stream_executor_friends(), +) + +package( + default_visibility = [":friends"], +) + +# Filegroup used to collect source files for the dependency check. +filegroup( + name = "c_srcs", + data = glob([ + "**/*.cc", + "**/*.h", + ]), +) + +cc_library( + name = "cuda_platform_id", + srcs = ["cuda_platform_id.cc"], + hdrs = ["cuda_platform_id.h"], + deps = ["//tensorflow/stream_executor:platform"], +) + +cc_library( + name = "cuda_platform", + srcs = ["cuda_platform.cc"], + hdrs = ["cuda_platform.h"], + visibility = ["//visibility:public"], + deps = [ + ":cuda_driver", + ":cuda_gpu_executor", + ":cuda_platform_id", + "//tensorflow/stream_executor", # buildcleaner: keep + "//tensorflow/stream_executor:executor_cache", + "//tensorflow/stream_executor:multi_platform_manager", + "//tensorflow/stream_executor:stream_executor_pimpl_header", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + ] + tf_additional_cuda_platform_deps(), + alwayslink = True, # Registers itself with the MultiPlatformManager. +) + +cc_library( + name = "cuda_diagnostics", + srcs = ["cuda_diagnostics.cc"], + hdrs = ["cuda_diagnostics.h"], + deps = [ + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + "@com_google_absl//absl/container:inlined_vector", + "@com_google_absl//absl/strings", + ], +) + +cc_library( + name = "cuda_driver", + srcs = ["cuda_driver.cc"], + hdrs = ["cuda_driver.h"], + deps = [ + ":cuda_diagnostics", + "@com_google_absl//absl/base", + "@com_google_absl//absl/container:inlined_vector", + "@com_google_absl//absl/strings", + "@local_config_cuda//cuda:cuda_headers", + "//tensorflow/stream_executor:device_options", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + ] + tf_additional_cuda_driver_deps(), +) + +# The activation library is tightly coupled to the executor library. +# TODO(leary) split up cuda_gpu_executor.cc so that this can stand alone. +cc_library( + name = "cuda_activation_header", + hdrs = ["cuda_activation.h"], + visibility = ["//visibility:public"], + deps = ["//tensorflow/stream_executor/platform"], +) + +cc_library( + name = "cuda_activation", + srcs = ["cuda_activation.cc"], + hdrs = ["cuda_activation.h"], + deps = [ + ":cuda_driver", + "//tensorflow/stream_executor", + "//tensorflow/stream_executor:stream_executor_internal", + "//tensorflow/stream_executor/platform", + "@local_config_cuda//cuda:cuda_headers", + ], +) + +cc_library( + name = "cuda_gpu_executor_header", + textual_hdrs = ["cuda_gpu_executor.h"], + visibility = ["//visibility:public"], + deps = [ + ":cuda_kernel", + "//tensorflow/stream_executor:event", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + ], +) + +cc_library( + name = "cublas_plugin", + srcs = ["cuda_blas.cc"], + hdrs = ["cuda_blas.h"], + visibility = ["//visibility:public"], + deps = [ + ":cuda_activation", + ":cuda_gpu_executor", + ":cuda_helpers", + ":cuda_platform_id", + ":cuda_stream", + ":cuda_timer", + "@com_google_absl//absl/strings", + "//third_party/eigen3", + "@local_config_cuda//cuda:cuda_headers", + "//tensorflow/core:lib_internal", + "//tensorflow/stream_executor", + "//tensorflow/stream_executor:event", + "//tensorflow/stream_executor:host_or_device_scalar", + "//tensorflow/stream_executor:plugin_registry", + "//tensorflow/stream_executor:scratch_allocator", + "//tensorflow/stream_executor:timer", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + "//tensorflow/stream_executor/platform:dso_loader", + ] + if_static( + [ + "@local_config_cuda//cuda:cublas", + "@local_config_cuda//cuda:cudart_static", + ], + ["@local_config_cuda//cuda:cudart"], + ), + alwayslink = True, +) + +cc_library( + name = "cufft_plugin", + srcs = ["cuda_fft.cc"], + hdrs = ["cuda_fft.h"], + visibility = ["//visibility:public"], + deps = [ + ":cuda_activation_header", + ":cuda_gpu_executor_header", + ":cuda_helpers", + ":cuda_platform_id", + ":cuda_stream", + "@local_config_cuda//cuda:cuda_headers", + "//tensorflow/stream_executor:event", + "//tensorflow/stream_executor:fft", + "//tensorflow/stream_executor:plugin_registry", + "//tensorflow/stream_executor:scratch_allocator", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + "//tensorflow/stream_executor/platform:dso_loader", + ] + if_static(["@local_config_cuda//cuda:cufft"]), + alwayslink = True, +) + +cc_library( + name = "cudnn_plugin", + srcs = ["cuda_dnn.cc"], + hdrs = ["cuda_dnn.h"], + copts = [ + # STREAM_EXECUTOR_CUDNN_WRAP would fail on Clang with the default + # setting of template depth 256 + "-ftemplate-depth-512", + ], + visibility = ["//visibility:public"], + deps = [ + ":cuda_activation", + ":cuda_diagnostics", + ":cuda_driver", + ":cuda_gpu_executor", + ":cuda_platform_id", + ":cuda_stream", + ":cuda_timer", + ":cudnn_version", + "@com_google_absl//absl/strings", + "//third_party/eigen3", + "@local_config_cuda//cuda:cuda_headers", + "//tensorflow/core:lib", + "//tensorflow/core:lib_internal", + "//tensorflow/core:logger", + "//tensorflow/stream_executor:dnn", + "//tensorflow/stream_executor:event", + "//tensorflow/stream_executor:logging_proto_cc", + "//tensorflow/stream_executor:plugin_registry", + "//tensorflow/stream_executor:scratch_allocator", + "//tensorflow/stream_executor:stream_executor_pimpl_header", + "//tensorflow/stream_executor:temporary_device_memory", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + "//tensorflow/stream_executor/platform:dso_loader", + ] + tf_additional_cudnn_plugin_deps() + if_static( + [ + "@local_config_cuda//cuda:cudnn", + "@local_config_cuda//cuda:cudart_static", + ], + ["@local_config_cuda//cuda:cudart"], + ), + alwayslink = True, +) + +cc_library( + name = "curand_plugin", + srcs = ["cuda_rng.cc"], + hdrs = ["cuda_rng.h"], + deps = [ + ":cuda_activation", + ":cuda_gpu_executor", + ":cuda_helpers", + ":cuda_platform_id", + ":cuda_stream", + "@local_config_cuda//cuda:cuda_headers", + "//tensorflow/stream_executor:event", + "//tensorflow/stream_executor:plugin_registry", + "//tensorflow/stream_executor:rng", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + "//tensorflow/stream_executor/platform:dso_loader", + ] + if_static(["@local_config_cuda//cuda:curand"]), + alwayslink = True, +) + +cc_library( + name = "cuda_kernel", + hdrs = ["cuda_kernel.h"], + deps = [ + ":cuda_driver", + "//tensorflow/stream_executor:event", + "//tensorflow/stream_executor:stream_executor_pimpl_header", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + "@local_config_cuda//cuda:cuda_headers", + ], +) + +# TODO(leary) we likely need to canonicalize/eliminate this. +cc_library( + name = "cuda_helpers", + textual_hdrs = ["cuda_helpers.h"], +) + +cc_library( + name = "cuda_event", + srcs = ["cuda_event.cc"], + hdrs = ["cuda_event.h"], + deps = [ + ":cuda_driver", + ":cuda_gpu_executor_header", + ":cuda_stream", + "//tensorflow/stream_executor:stream_executor_headers", + "//tensorflow/stream_executor/lib", + ], +) + +cc_library( + name = "cuda_stream", + srcs = ["cuda_stream.cc"], + hdrs = ["cuda_stream.h"], + deps = [ + ":cuda_driver", + ":cuda_gpu_executor_header", + "//tensorflow/stream_executor:stream_executor_headers", + "//tensorflow/stream_executor:stream_header", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + ], +) + +cc_library( + name = "cuda_timer", + srcs = ["cuda_timer.cc"], + hdrs = ["cuda_timer.h"], + deps = [ + ":cuda_driver", + ":cuda_gpu_executor_header", + ":cuda_stream", + "//tensorflow/stream_executor:stream_executor_headers", + "//tensorflow/stream_executor/lib", + ], +) + +# It implements :cuda_gpu_executor_header +cc_library( + name = "cuda_gpu_executor", + srcs = ["cuda_gpu_executor.cc"], + hdrs = ["cuda_gpu_executor.h"], + deps = [ + ":cuda_activation", + ":cuda_diagnostics", + ":cuda_driver", + ":cuda_event", + ":cuda_kernel", + ":cuda_platform_id", + ":cuda_stream", + ":cuda_timer", + "//tensorflow/stream_executor:event", + "//tensorflow/stream_executor:plugin_registry", + "//tensorflow/stream_executor:stream_executor_internal", + "//tensorflow/stream_executor:stream_executor_pimpl_header", + "//tensorflow/stream_executor:timer", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + "@com_google_absl//absl/strings", + ], + alwayslink = True, +) + +cc_library( + name = "cudnn_version", + srcs = ["cudnn_version.cc"], + hdrs = ["cudnn_version.h"], + deps = [ + "//tensorflow/core:lib", + ], +) + +tf_cc_test( + name = "cudnn_version_test", + srcs = ["cudnn_version_test.cc"], + deps = [ + ":cudnn_version", + "//tensorflow/core:test", + "//tensorflow/core:test_main", + ], +) + +cc_library( + name = "all_runtime", + copts = tf_copts(), + visibility = ["//visibility:public"], + deps = if_cuda_is_configured([ + ":cudnn_plugin", + ":cufft_plugin", + ":cublas_plugin", + ":curand_plugin", + ":cuda_driver", + ":cuda_platform", + ]), + alwayslink = 1, +) diff --git a/tensorflow/stream_executor/host/BUILD b/tensorflow/stream_executor/host/BUILD new file mode 100644 index 0000000000..59472b14c1 --- /dev/null +++ b/tensorflow/stream_executor/host/BUILD @@ -0,0 +1,108 @@ +# Description: +# Host-platform specific StreamExecutor support code. + +licenses(["notice"]) # Apache 2.0 + +load("//tensorflow/stream_executor:build_defs.bzl", "stream_executor_friends") + +package_group( + name = "friends", + packages = stream_executor_friends(), +) + +package(default_visibility = [":friends"]) + +# Filegroup used to collect source files for the dependency check. +filegroup( + name = "c_srcs", + data = glob([ + "**/*.cc", + "**/*.h", + ]), +) + +cc_library( + name = "host_platform_id", + srcs = [ + "host_platform_id.cc", + ], + hdrs = [ + "host_platform_id.h", + ], + deps = [ + "//tensorflow/stream_executor:platform", + ], +) + +cc_library( + name = "host_platform", + srcs = [ + "host_platform.cc", + ], + hdrs = [ + "host_platform.h", + ], + visibility = ["//visibility:public"], + deps = [ + ":host_gpu_executor", + ":host_platform_id", + "//tensorflow/stream_executor:executor_cache", + "//tensorflow/stream_executor:multi_platform_manager", + "//tensorflow/stream_executor:stream_executor_headers", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + ], + alwayslink = True, # Registers itself with the MultiPlatformManager. +) + +cc_library( + name = "host_stream", + srcs = [ + "host_stream.cc", + ], + hdrs = [ + "host_stream.h", + ], + deps = [ + "//tensorflow/stream_executor:kernel", + "//tensorflow/stream_executor/lib", + ], +) + +cc_library( + name = "host_timer", + srcs = [ + "host_timer.cc", + ], + hdrs = [ + "host_timer.h", + ], + deps = [ + "//tensorflow/stream_executor:stream_executor_internal", + "//tensorflow/stream_executor:timer", + "//tensorflow/stream_executor/platform", + ], +) + +# TODO(22689637): Rename this target. +cc_library( + name = "host_gpu_executor", + srcs = [ + "host_gpu_executor.cc", + ], + hdrs = [ + "host_gpu_executor.h", + ], + deps = [ + ":host_platform_id", + ":host_stream", + ":host_timer", + "//tensorflow/core:lib", + "//tensorflow/stream_executor:kernel", + "//tensorflow/stream_executor:rng", + "//tensorflow/stream_executor:stream_executor_internal", + "//tensorflow/stream_executor:timer", + "//tensorflow/stream_executor/lib", + ], + alwayslink = True, +) diff --git a/tensorflow/stream_executor/lib/BUILD b/tensorflow/stream_executor/lib/BUILD new file mode 100644 index 0000000000..133ff2b161 --- /dev/null +++ b/tensorflow/stream_executor/lib/BUILD @@ -0,0 +1,62 @@ +licenses(["notice"]) # Apache 2.0 + +load("//tensorflow:tensorflow.bzl", "tf_cc_test") +load("//tensorflow/stream_executor:build_defs.bzl", "stream_executor_friends") + +package_group( + name = "friends", + packages = stream_executor_friends(), +) + +package(default_visibility = [":friends"]) + +filegroup( + name = "c_srcs", + data = glob([ + "**/*.cc", + "**/*.h", + ]), +) + +cc_library( + name = "lib", + srcs = glob( + [ + "**/*.cc", + ], + exclude = [ + "**/*test*", + ], + ), + hdrs = glob(["**/*.h"]), + deps = [ + "//tensorflow/core:lib", + "//tensorflow/core:protos_all_cc", + "//tensorflow/core:ptr_util", + "//tensorflow/stream_executor/platform", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:span", + ], +) + +tf_cc_test( + name = "statusor_test", + size = "small", + srcs = ["statusor_test.cc"], + deps = [ + ":lib", + "//tensorflow/core:lib", + "//tensorflow/core:test", + "//tensorflow/core:test_main", + ], +) + +cc_library( + name = "utility_headers", + hdrs = [ + "ptr_util.h", + ], + deps = [ + "//tensorflow/core:ptr_util", + ], +) diff --git a/tensorflow/stream_executor/platform/BUILD b/tensorflow/stream_executor/platform/BUILD new file mode 100644 index 0000000000..702b2cdfe0 --- /dev/null +++ b/tensorflow/stream_executor/platform/BUILD @@ -0,0 +1,47 @@ +licenses(["notice"]) # Apache 2.0 + +load("//tensorflow/stream_executor:build_defs.bzl", "stream_executor_friends") +load("//tensorflow/core:platform/default/build_config.bzl", "tf_platform_hdrs") + +package_group( + name = "friends", + packages = stream_executor_friends(), +) + +package( + default_visibility = [":friends"], +) + +cc_library( + name = "platform", + textual_hdrs = [ + "logging.h", + "mutex.h", + "platform.h", + "port.h", + "thread_annotations.h", + "initialize.h", + ], + deps = [ + "//tensorflow/core:lib", + "//tensorflow/stream_executor/platform/default:platform", + "@com_google_absl//absl/strings", + ], +) + +cc_library( + name = "dso_loader", + hdrs = ["dso_loader.h"], + deps = [ + ":platform", + "//tensorflow/stream_executor/platform/default:dso_loader", + ], +) + +filegroup( + name = "c_srcs", + data = glob([ + "**/*.cc", + "**/*.h", + ]), +) diff --git a/tensorflow/stream_executor/platform/default/BUILD b/tensorflow/stream_executor/platform/default/BUILD new file mode 100644 index 0000000000..f1ae7d86ff --- /dev/null +++ b/tensorflow/stream_executor/platform/default/BUILD @@ -0,0 +1,25 @@ +licenses(["notice"]) # Apache 2.0 + +package(default_visibility = ["//tensorflow/stream_executor:__subpackages__"]) + +cc_library( + name = "platform", + textual_hdrs = [ + "initialize.h", + "mutex.h", + ], + deps = ["//tensorflow/core:lib"], +) + +cc_library( + name = "dso_loader", + srcs = ["dso_loader.cc"], + hdrs = ["dso_loader.h"], + deps = [ + "//tensorflow/stream_executor:platform", + "//tensorflow/stream_executor/lib", + "//tensorflow/stream_executor/platform", + "@com_google_absl//absl/strings", + "@local_config_cuda//cuda:cuda_headers", + ], +) -- GitLab From df17a7db51000ccf7c6b062e37b5c3acb2cd286b Mon Sep 17 00:00:00 2001 From: Pete Warden Date: Tue, 8 Jan 2019 13:42:38 -0800 Subject: [PATCH 504/622] MacOS support for simple speech example PiperOrigin-RevId: 228391393 --- .../micro/examples/micro_speech/BUILD | 24 --- .../micro/examples/micro_speech/Makefile.inc | 27 +--- .../examples/micro_speech/audio_provider.cc | 6 + .../examples/micro_speech/audio_provider.h | 10 ++ .../micro_speech/audio_provider_test.cc | 26 ++++ .../micro/examples/micro_speech/main.cc | 4 +- .../examples/micro_speech/model_settings.h | 1 + .../examples/micro_speech/osx/Makefile.inc | 6 + .../micro_speech/osx/audio_provider.cc | 139 ++++++++++++++++++ .../micro/examples/micro_speech/timer.cc | 22 --- .../micro/examples/micro_speech/timer.h | 31 ---- .../micro/examples/micro_speech/timer_test.cc | 49 ------ .../experimental/micro/tools/make/Makefile | 35 ++++- .../micro/tools/make/targets/osx_makefile.inc | 10 ++ 14 files changed, 241 insertions(+), 149 deletions(-) create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/osx/Makefile.inc create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/osx/audio_provider.cc delete mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/timer.cc delete mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/timer.h delete mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/timer_test.cc create mode 100644 tensorflow/lite/experimental/micro/tools/make/targets/osx_makefile.inc diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/BUILD b/tensorflow/lite/experimental/micro/examples/micro_speech/BUILD index 70eeac1458..702b893e6f 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/BUILD +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/BUILD @@ -196,29 +196,6 @@ tflite_micro_cc_test( ], ) -cc_library( - name = "timer", - srcs = [ - "timer.cc", - ], - hdrs = [ - "timer.h", - ], -) - -tflite_micro_cc_test( - name = "timer_test", - srcs = [ - "timer_test.cc", - ], - deps = [ - ":timer", - "//tensorflow/lite/c:c_api_internal", - "//tensorflow/lite/experimental/micro:micro_framework", - "//tensorflow/lite/experimental/micro/testing:micro_test", - ], -) - cc_library( name = "recognize_commands", srcs = [ @@ -259,7 +236,6 @@ cc_binary( ":model_settings", ":preprocessor_reference", ":recognize_commands", - ":timer", ":tiny_conv_model_data", "//tensorflow/lite:schema_fbs_version", "//tensorflow/lite/experimental/micro:micro_framework", diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/Makefile.inc b/tensorflow/lite/experimental/micro/examples/micro_speech/Makefile.inc index 5623eabdde..6f7563c02f 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/Makefile.inc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/Makefile.inc @@ -5,6 +5,7 @@ tensorflow/lite/experimental/micro/examples/micro_speech/micro_speech_test.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/tiny_conv_model_data.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/no_features_data.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/yes_features_data.cc +MICRO_SPEECH_TEST_SRCS := $(call specialize,$(MICRO_SPEECH_TEST_SRCS)) ALL_SRCS += $(MICRO_SPEECH_TEST_SRCS) MICRO_SPEECH_TEST_OBJS := $(addprefix $(OBJDIR), \ $(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(MICRO_SPEECH_TEST_SRCS)))) @@ -32,6 +33,7 @@ tensorflow/lite/experimental/micro/examples/micro_speech/yes_power_spectrum_data PREPROCESSOR_REFERENCE_TEST_SRCS = \ $(PREPROCESSOR_TEST_SHARED_SRCS) \ tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.cc +PREPROCESSOR_REFERENCE_TEST_SRCS := $(call specialize,$(PREPROCESSOR_REFERENCE_TEST_SRCS)) ALL_SRCS += $(PREPROCESSOR_REFERENCE_TEST_SRCS) PREPROCESSOR_REFERENCE_TEST_OBJS := $(addprefix $(OBJDIR), \ $(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(PREPROCESSOR_REFERENCE_TEST_SRCS)))) @@ -51,6 +53,7 @@ test_preprocessor_reference: $(PREPROCESSOR_REFERENCE_TEST_BINARY) PREPROCESSOR_FIXED_TEST_SRCS = \ $(PREPROCESSOR_TEST_SHARED_SRCS) \ tensorflow/lite/experimental/micro/examples/micro_speech/fixed_point/preprocessor.cc +PREPROCESSOR_FIXED_TEST_SRCS := $(call specialize,$(PREPROCESSOR_FIXED_TEST_SRCS)) ALL_SRCS += $(PREPROCESSOR_FIXED_TEST_SRCS) PREPROCESSOR_FIXED_TEST_OBJS := $(addprefix $(OBJDIR), \ $(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(PREPROCESSOR_FIXED_TEST_SRCS)))) @@ -71,6 +74,7 @@ AUDIO_PROVIDER_TEST_SRCS := \ tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider_test.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.cc +AUDIO_PROVIDER_TEST_SRCS := $(call specialize,$(AUDIO_PROVIDER_TEST_SRCS)) ALL_SRCS += $(AUDIO_PROVIDER_TEST_SRCS) AUDIO_PROVIDER_TEST_OBJS := $(addprefix $(OBJDIR), \ $(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(AUDIO_PROVIDER_TEST_SRCS)))) @@ -93,6 +97,7 @@ tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider.cc +FEATURE_PROVIDER_TEST_SRCS := $(call specialize,$(FEATURE_PROVIDER_TEST_SRCS)) ALL_SRCS += $(FEATURE_PROVIDER_TEST_SRCS) FEATURE_PROVIDER_TEST_OBJS := $(addprefix $(OBJDIR), \ $(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(FEATURE_PROVIDER_TEST_SRCS)))) @@ -108,30 +113,12 @@ feature_provider_test_bin: $(FEATURE_PROVIDER_TEST_BINARY).bin test_feature_provider: $(FEATURE_PROVIDER_TEST_BINARY) $(TEST_SCRIPT) $(FEATURE_PROVIDER_TEST_BINARY) '~~~ALL TESTS PASSED~~~' -# Tests the timer module. -TIMER_TEST_SRCS := \ -tensorflow/lite/experimental/micro/examples/micro_speech/timer_test.cc \ -tensorflow/lite/experimental/micro/examples/micro_speech/timer.cc -ALL_SRCS += $(TIMER_TEST_SRCS) -TIMER_TEST_OBJS := $(addprefix $(OBJDIR), \ -$(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(TIMER_TEST_SRCS)))) -TIMER_TEST_BINARY := $(BINDIR)timer_test -ALL_BINARIES += $(TIMER_TEST_BINARY) -$(TIMER_TEST_BINARY): $(TIMER_TEST_OBJS) $(MICROLITE_LIB_PATH) - @mkdir -p $(dir $@) - $(CXX) $(CXXFLAGS) $(INCLUDES) \ - -o $(TIMER_TEST_BINARY) $(TIMER_TEST_OBJS) \ - $(LIBFLAGS) $(MICROLITE_LIB_PATH) $(LDFLAGS) $(MICROLITE_LIBS) -timer_test: $(TIMER_TEST_BINARY) -timer_test_bin: $(TIMER_TEST_BINARY).bin -test_timer: $(TIMER_TEST_BINARY) - $(TEST_SCRIPT) $(TIMER_TEST_BINARY) '~~~ALL TESTS PASSED~~~' - # Tests the feature provider module. RECOGNIZE_COMMANDS_TEST_SRCS := \ tensorflow/lite/experimental/micro/examples/micro_speech/recognize_commands_test.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/recognize_commands.cc +RECOGNIZE_COMMANDS_TEST_SRCS := $(call specialize,$(RECOGNIZE_COMMANDS_TEST_SRCS)) ALL_SRCS += $(RECOGNIZE_COMMANDS_TEST_SRCS) RECOGNIZE_COMMANDS_TEST_OBJS := $(addprefix $(OBJDIR), \ $(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(RECOGNIZE_COMMANDS_TEST_SRCS)))) @@ -153,12 +140,12 @@ tensorflow/lite/experimental/micro/examples/micro_speech/main.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider.cc \ -tensorflow/lite/experimental/micro/examples/micro_speech/timer.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/no_features_data.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/yes_features_data.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/tiny_conv_model_data.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/recognize_commands.cc +MICRO_SPEECH_SRCS := $(call specialize,$(MICRO_SPEECH_SRCS)) ALL_SRCS += $(MICRO_SPEECH_SRCS) MICRO_SPEECH_OBJS := $(addprefix $(OBJDIR), \ $(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(MICRO_SPEECH_SRCS)))) diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.cc index c0365d5690..52db18e686 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.cc @@ -19,6 +19,7 @@ limitations under the License. namespace { int16_t g_dummy_audio_data[kMaxAudioSampleSize]; +int32_t g_latest_audio_timestamp = 0; } // namespace TfLiteStatus GetAudioSamples(tflite::ErrorReporter* error_reporter, @@ -31,3 +32,8 @@ TfLiteStatus GetAudioSamples(tflite::ErrorReporter* error_reporter, *audio_samples = g_dummy_audio_data; return kTfLiteOk; } + +int32_t LatestAudioTimestamp() { + g_latest_audio_timestamp += 100; + return g_latest_audio_timestamp; +} diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.h b/tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.h index 7e2442a5e8..b690673641 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.h +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.h @@ -33,4 +33,14 @@ TfLiteStatus GetAudioSamples(tflite::ErrorReporter* error_reporter, int start_ms, int duration_ms, int* audio_samples_size, int16_t** audio_samples); +// Returns the time that audio data was last captured in milliseconds. There's +// no contract about what time zero represents, the accuracy, or the granularity +// of the result. Subsequent calls will generally not return a lower value, but +// even that's not guaranteed if there's an overflow wraparound. +// The reference implementation of this function just returns a constantly +// incrementing value for each call, since it would need a non-portable platform +// call to access time information. For real applications, you'll need to write +// your own platform-specific implementation. +int32_t LatestAudioTimestamp(); + #endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_AUDIO_PROVIDER_H_ diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider_test.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider_test.cc index 5f7c7605f0..85fbbb80a6 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider_test.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider_test.cc @@ -14,6 +14,9 @@ limitations under the License. ==============================================================================*/ #include "tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.h" + +#include + #include "tensorflow/lite/c/c_api_internal.h" #include "tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h" #include "tensorflow/lite/experimental/micro/micro_error_reporter.h" @@ -41,4 +44,27 @@ TF_LITE_MICRO_TEST(TestAudioProvider) { } } +TF_LITE_MICRO_TEST(TestTimer) { + // Make sure that the technically-undefined overflow behavior we rely on below + // works on this platform. It's still not guaranteed, but at least this is a + // sanity check. Turn off when running with ASan, as it will complain about + // the following undefined behavior. +#ifndef ADDRESS_SANITIZER + int32_t overflow_value = std::numeric_limits::max(); + overflow_value += 1; + TF_LITE_MICRO_EXPECT_EQ(std::numeric_limits::min(), overflow_value); +#endif + + const int32_t first_time = LatestAudioTimestamp(); + const int32_t second_time = LatestAudioTimestamp(); + + // It's possible that the timer may have wrapped around from +BIG_NUM to + // -BIG_NUM between the first and second calls, since we're storing + // milliseconds in a 32-bit integer. It's not reasonable that the call itself + // would have taken more than 2^31 milliseconds though, so look at the + // difference and rely on integer overflow to ensure it's accurate. + const int32_t time_delta = (second_time - first_time); + TF_LITE_MICRO_EXPECT_LE(0, time_delta); +} + TF_LITE_MICRO_TESTS_END diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/main.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/main.cc index 515f82fcbc..3a9a5a4df1 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/main.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/main.cc @@ -13,10 +13,10 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ +#include "tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.h" #include "tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider.h" #include "tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h" #include "tensorflow/lite/experimental/micro/examples/micro_speech/recognize_commands.h" -#include "tensorflow/lite/experimental/micro/examples/micro_speech/timer.h" #include "tensorflow/lite/experimental/micro/examples/micro_speech/tiny_conv_model_data.h" #include "tensorflow/lite/experimental/micro/kernels/all_ops_resolver.h" #include "tensorflow/lite/experimental/micro/micro_error_reporter.h" @@ -76,7 +76,7 @@ int main(int argc, char* argv[]) { // Keep reading and analysing audio data in an infinite loop. while (true) { // Fetch the spectrogram for the current time. - const int32_t current_time = TimeInMilliseconds(); + const int32_t current_time = LatestAudioTimestamp(); int how_many_new_slices = 0; TfLiteStatus feature_status = feature_provider.PopulateFeatureData( error_reporter, previous_time, current_time, &how_many_new_slices); diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h b/tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h index 1d8f3123a5..f48252d14d 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h @@ -23,6 +23,7 @@ limitations under the License. // frequency information. This has to be a power of two, and since we're dealing // with 30ms of 16KHz inputs, which means 480 samples, this is the next value. constexpr int kMaxAudioSampleSize = 512; +constexpr int kAudioSampleFrequency = 16000; // All of these values are derived from the values used during model training, // if you change your model you'll need to update these constants. diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/osx/Makefile.inc b/tensorflow/lite/experimental/micro/examples/micro_speech/osx/Makefile.inc new file mode 100644 index 0000000000..89d107cfe0 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/osx/Makefile.inc @@ -0,0 +1,6 @@ +#Settings for Mac OS platforms. +ifeq ($(TARGET), osx) + MICROLITE_LIBS += \ + -framework Foundation \ + -framework AudioToolbox +endif diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/osx/audio_provider.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/osx/audio_provider.cc new file mode 100644 index 0000000000..892757e799 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/osx/audio_provider.cc @@ -0,0 +1,139 @@ +/* Copyright 2018 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/lite/experimental/micro/examples/micro_speech/audio_provider.h" + +#include + +#include "tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h" + +namespace { + +constexpr int kNumberRecordBuffers = 3; +bool g_is_audio_initialized = false; +constexpr int kAudioCaptureBufferSize = kAudioSampleFrequency * 0.5; +int16_t g_audio_capture_buffer[kAudioCaptureBufferSize]; +int16_t g_audio_output_buffer[kMaxAudioSampleSize]; +int32_t g_latest_audio_timestamp = 0; + +// Checks for MacOS errors, prints information and returns a TF Lite version. +#define RETURN_IF_OS_ERROR(error, error_reporter) \ + do { \ + if (error != noErr) { \ + error_reporter->Report("Error: %s:%d (%d)\n", __FILE__, __LINE__, \ + error); \ + return kTfLiteError; \ + } \ + } while (0); + +// Called when an audio input buffer has been filled. +void OnAudioBufferFilledCallback( + void* user_data, AudioQueueRef queue, AudioQueueBufferRef buffer, + const AudioTimeStamp* start_time, UInt32 num_packets, + const AudioStreamPacketDescription* packet_description) { + const int sample_size = buffer->mAudioDataByteSize / sizeof(float); + const int64_t sample_offset = start_time->mSampleTime; + const int32_t time_in_ms = + (sample_offset + sample_size) / (kAudioSampleFrequency / 1000); + const float* float_samples = static_cast(buffer->mAudioData); + for (int i = 0; i < sample_size; ++i) { + const int capture_index = (sample_offset + i) % kAudioCaptureBufferSize; + g_audio_capture_buffer[capture_index] = float_samples[i] * ((1 << 15) - 1); + } + // This is how we let the outside world know that new audio data has arrived. + g_latest_audio_timestamp = time_in_ms; + AudioQueueEnqueueBuffer(queue, buffer, 0, nullptr); +} + +// Set up everything we need to capture audio samples from the default recording +// device on MacOS. +TfLiteStatus InitAudioRecording(tflite::ErrorReporter* error_reporter) { + // Set up the format of the audio - single channel, 32-bit float at 16KHz. + AudioStreamBasicDescription recordFormat = {0}; + recordFormat.mSampleRate = kAudioSampleFrequency; + recordFormat.mFormatID = kAudioFormatLinearPCM; + recordFormat.mFormatFlags = + kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked; + recordFormat.mBitsPerChannel = 8 * sizeof(float); + recordFormat.mChannelsPerFrame = 1; + recordFormat.mBytesPerFrame = sizeof(float) * recordFormat.mChannelsPerFrame; + recordFormat.mFramesPerPacket = 1; + recordFormat.mBytesPerPacket = + recordFormat.mBytesPerFrame * recordFormat.mFramesPerPacket; + recordFormat.mReserved = 0; + + UInt32 propSize = sizeof(recordFormat); + RETURN_IF_OS_ERROR(AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, + NULL, &propSize, &recordFormat), + error_reporter); + + // Create a recording queue. + AudioQueueRef queue; + RETURN_IF_OS_ERROR( + AudioQueueNewInput(&recordFormat, OnAudioBufferFilledCallback, + error_reporter, nullptr, nullptr, 0, &queue), + error_reporter); + + // Set up the buffers we'll need. + int buffer_bytes = 512 * sizeof(float); + for (int i = 0; i < kNumberRecordBuffers; ++i) { + AudioQueueBufferRef buffer; + RETURN_IF_OS_ERROR(AudioQueueAllocateBuffer(queue, buffer_bytes, &buffer), + error_reporter); + RETURN_IF_OS_ERROR(AudioQueueEnqueueBuffer(queue, buffer, 0, nullptr), + error_reporter); + } + + // Start capturing audio. + RETURN_IF_OS_ERROR(AudioQueueStart(queue, nullptr), error_reporter); + + return kTfLiteOk; +} + +} // namespace + +TfLiteStatus GetAudioSamples(tflite::ErrorReporter* error_reporter, + int start_ms, int duration_ms, + int* audio_samples_size, int16_t** audio_samples) { + if (!g_is_audio_initialized) { + TfLiteStatus init_status = InitAudioRecording(error_reporter); + if (init_status != kTfLiteOk) { + return init_status; + } + for (int i = 0; i < kMaxAudioSampleSize; ++i) { + g_audio_output_buffer[i] = 0; + } + g_is_audio_initialized = true; + } + // This should only be called when the main thread notices that the latest + // audio sample data timestamp has changed, so that there's new data in the + // capture ring buffer. The ring buffer will eventually wrap around and + // overwrite the data, but the assumption is that the main thread is checking + // often enough and the buffer is large enough that this call will be made + // before that happens. + const int start_offset = start_ms * (kAudioSampleFrequency / 1000); + const int duration_sample_count = + duration_ms * (kAudioSampleFrequency / 1000); + for (int i = 0; i < duration_sample_count; ++i) { + const int capture_index = (start_offset + i) % kAudioCaptureBufferSize; + g_audio_output_buffer[i] = g_audio_capture_buffer[capture_index]; + } + + *audio_samples_size = kMaxAudioSampleSize; + *audio_samples = g_audio_output_buffer; + return kTfLiteOk; +} + +int32_t LatestAudioTimestamp() { return g_latest_audio_timestamp; } diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/timer.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/timer.cc deleted file mode 100644 index 6c96a61ab5..0000000000 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/timer.cc +++ /dev/null @@ -1,22 +0,0 @@ -/* Copyright 2018 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/lite/experimental/micro/examples/micro_speech/timer.h" - -int32_t TimeInMilliseconds() { - static int current_time = 0; - current_time += 100; - return current_time; -} diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/timer.h b/tensorflow/lite/experimental/micro/examples/micro_speech/timer.h deleted file mode 100644 index 162952844a..0000000000 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/timer.h +++ /dev/null @@ -1,31 +0,0 @@ -/* Copyright 2018 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_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_TIMER_H_ -#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_TIMER_H_ - -#include - -// Returns the time in milliseconds. There's no contract about what time zero -// represents, the accuracy, or the granularity of the result. Subsequent calls -// will generally not return a lower value, but even that's not guaranteed if -// there's an overflow wraparound. -// The reference implementation of this function just returns a constantly -// incrementing value for each call, since it would need a non-portable platform -// call to access time information. For real applications, you'll need to write -// your own platform-specific implementation. -int32_t TimeInMilliseconds(); - -#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_TIMER_H_ diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/timer_test.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/timer_test.cc deleted file mode 100644 index 0487a12b25..0000000000 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/timer_test.cc +++ /dev/null @@ -1,49 +0,0 @@ -/* Copyright 2018 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/lite/experimental/micro/examples/micro_speech/timer.h" - -#include - -#include "tensorflow/lite/c/c_api_internal.h" -#include "tensorflow/lite/experimental/micro/micro_error_reporter.h" -#include "tensorflow/lite/experimental/micro/testing/micro_test.h" - -TF_LITE_MICRO_TESTS_BEGIN - -TF_LITE_MICRO_TEST(TestTimer) { - // Make sure that the technically-undefined overflow behavior we rely on below - // works on this platform. It's still not guaranteed, but at least this is a - // sanity check. Turn off when running with ASan, as it will complain about - // the following undefined behavior. -#ifndef ADDRESS_SANITIZER - int32_t overflow_value = std::numeric_limits::max(); - overflow_value += 1; - TF_LITE_MICRO_EXPECT_EQ(std::numeric_limits::min(), overflow_value); -#endif - - const int32_t first_time = TimeInMilliseconds(); - const int32_t second_time = TimeInMilliseconds(); - - // It's possible that the timer may have wrapped around from +BIG_NUM to - // -BIG_NUM between the first and second calls, since we're storing - // milliseconds in a 32-bit integer. It's not reasonable that the call itself - // would have taken more than 2^31 milliseconds though, so look at the - // difference and rely on integer overflow to ensure it's accurate. - const int32_t time_delta = (second_time - first_time); - TF_LITE_MICRO_EXPECT_LE(0, time_delta); -} - -TF_LITE_MICRO_TESTS_END diff --git a/tensorflow/lite/experimental/micro/tools/make/Makefile b/tensorflow/lite/experimental/micro/tools/make/Makefile index 6a7c875888..f9a2dffae2 100644 --- a/tensorflow/lite/experimental/micro/tools/make/Makefile +++ b/tensorflow/lite/experimental/micro/tools/make/Makefile @@ -21,6 +21,38 @@ HOST_ARCH := $(shell if [[ $(shell uname -m) =~ i[345678]86 ]]; then echo x86_32 TARGET := $(HOST_OS) TARGET_ARCH := $(HOST_ARCH) +# Look for platform or target-specific implementation files to replace reference +# implementations with, given a tag. These are expected to occur in subfolders +# of a directory where a reference implementation exists, and have the same +# interface and header file. For example, +# tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.cc +# defines a module for supplying audio data, but since no platform or OS can be +# presumed, it just always returns zeroes for its samples. The MacOS-specific +# tensorflow/lite/experimental/micro/examples/micro_speech/osx/audio_provider.cc +# has an implementation that relies on CoreAudio, and there are equivalent +# versions for other operating systems. +# All lists of source files are put through this substitution process with the +# tags of their target OS and architecture, so that implementations can be added +# by simply placing them in the file tree, with no changes to the build files +# needed. +# One confusing thing about this implementation is that we're using wildcard to +# act as a 'does file exist?' function, rather than expanding an expression. +# Wildcard will return an empty string if given a plain file path with no actual +# wildcards, if the file doesn't exist, so taking the first word of the list +# between that and the reference path will pick the specialized one if it's +# available. +substitute_specialized_implementation = \ + $(firstword $(wildcard $(dir $(1))$(2)/$(notdir $(1))) $(wildcard $(1))) +substitute_specialized_implementations = \ + $(foreach source,$(1),$(call substitute_specialized_implementation,$(source),$(2))) +# Here we're first looking for specialized implementations in ref_dir/$(TARGET) +# and then ref_dir/$(TARGET_ARCH), before falling back to ref_dir's +# implementation. +# The argument to this function should be a list of space-separated file paths, +# with any wildcards already expanded. +specialize = \ + $(call substitute_specialized_implementations,$(call substitute_specialized_implementations,$(1),$(TARGET)),$(TARGET_ARCH)) + INCLUDES := \ -I. \ -I$(MAKEFILE_DIR)/../../../../../ \ @@ -67,6 +99,7 @@ tensorflow/lite/core/api/op_resolver.cc \ tensorflow/lite/kernels/kernel_util.cc \ tensorflow/lite/kernels/internal/quantization_util.cc MICROLITE_CC_SRCS := $(filter-out $(MICROLITE_TEST_SRCS), $(MICROLITE_CC_BASE_SRCS)) +MICROLITE_CC_SRCS := $(call specialize,$(MICROLITE_CC_SRCS)) # These target-specific makefiles should modify or replace options like # CXXFLAGS or LIBS to work for a specific targetted architecture. All logic @@ -115,7 +148,7 @@ $(OBJDIR)%.o: %.S $(CC) $(CCFLAGS) $(INCLUDES) -c $< -o $@ # The target that's compiled if there's no command-line arguments. -all: $(MICROLITE_LIB_PATH) $(MICRO_SPEECH_TEST_BINARY) $(PREPROCESSOR_TEST_BINARY) +all: $(MICROLITE_LIB_PATH) microlite: $(MICROLITE_LIB_PATH) diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/osx_makefile.inc b/tensorflow/lite/experimental/micro/tools/make/targets/osx_makefile.inc new file mode 100644 index 0000000000..0e8aad1993 --- /dev/null +++ b/tensorflow/lite/experimental/micro/tools/make/targets/osx_makefile.inc @@ -0,0 +1,10 @@ +#Settings for Mac OS platforms. +ifeq ($(TARGET), osx) + + PLATFORM_FLAGS = \ + -DTF_LITE_DISABLE_X86_NEON + + CXXFLAGS += $(PLATFORM_FLAGS) + CCFLAGS += $(PLATFORM_FLAGS) + +endif \ No newline at end of file -- GitLab From f9b6cccd90939bf9516d0ae0e3bbdc4a7c4f4a54 Mon Sep 17 00:00:00 2001 From: Jiri Simsa Date: Tue, 8 Jan 2019 13:50:00 -0800 Subject: [PATCH 505/622] [tf.data] Update `map` documentation -- calling out the fact that setting `num_parallel_calls` results in asynchronous computation. PiperOrigin-RevId: 228392788 --- tensorflow/python/data/ops/dataset_ops.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tensorflow/python/data/ops/dataset_ops.py b/tensorflow/python/data/ops/dataset_ops.py index 61210f3a7f..45e732be0d 100644 --- a/tensorflow/python/data/ops/dataset_ops.py +++ b/tensorflow/python/data/ops/dataset_ops.py @@ -970,8 +970,8 @@ class DatasetV2(object): shapes and types defined by `self.output_shapes` and `self.output_types`) to another nested structure of tensors. num_parallel_calls: (Optional.) A `tf.int32` scalar `tf.Tensor`, - representing the number elements to process in parallel. If not - specified, elements will be processed sequentially. If the value + representing the number elements to process asynchronously in parallel. + If not specified, elements will be processed sequentially. If the value `tf.data.experimental.AUTOTUNE` is used, then the number of parallel calls is set dynamically based on available CPU. -- GitLab From 4aaaee6112c40791e5dbc9730c2a06274ff35b3f Mon Sep 17 00:00:00 2001 From: Sanjoy Das Date: Tue, 8 Jan 2019 13:50:17 -0800 Subject: [PATCH 506/622] [TF:XLA] Bump open source llvm revision to r350570 PiperOrigin-RevId: 228392842 --- tensorflow/workspace.bzl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tensorflow/workspace.bzl b/tensorflow/workspace.bzl index 834ec28447..ae26ba6e68 100755 --- a/tensorflow/workspace.bzl +++ b/tensorflow/workspace.bzl @@ -500,11 +500,11 @@ def tf_workspace(path_prefix = "", tf_repo_name = ""): tf_http_archive( name = "llvm", build_file = clean_dep("//third_party/llvm:llvm.autogenerated.BUILD"), - sha256 = "20c0ebd54f433d68652a54e328d70b0ec7cf37ce734645a0259cf904d14c2c53", - strip_prefix = "llvm-0e2aef924e02d00a4d3454d63c1921a243d316c6", + sha256 = "83a4f199742f3d6892994dd6dc46d6a53019aedaa28590b460c120f3dfc7bc47", + strip_prefix = "llvm-671f057a2cf137914be6c786daf8000469adebab", urls = [ - "https://mirror.bazel.build/github.com/llvm-mirror/llvm/archive/0e2aef924e02d00a4d3454d63c1921a243d316c6.tar.gz", - "https://github.com/llvm-mirror/llvm/archive/0e2aef924e02d00a4d3454d63c1921a243d316c6.tar.gz", + "https://mirror.bazel.build/github.com/llvm-mirror/llvm/archive/671f057a2cf137914be6c786daf8000469adebab.tar.gz", + "https://github.com/llvm-mirror/llvm/archive/671f057a2cf137914be6c786daf8000469adebab.tar.gz", ], ) -- GitLab From c52d92f10021f8caa00ea271755d47a9c86259bd Mon Sep 17 00:00:00 2001 From: Jared Duke Date: Tue, 8 Jan 2019 14:06:17 -0800 Subject: [PATCH 507/622] Internal change PiperOrigin-RevId: 228396077 --- tensorflow/lite/experimental/swift/BUILD | 101 ----- tensorflow/lite/experimental/swift/LICENSE | 202 ---------- tensorflow/lite/experimental/swift/README.md | 54 --- .../swift/Sources/Interpreter.swift | 265 -------------- .../swift/Sources/InterpreterError.swift | 99 ----- .../swift/Sources/InterpreterOptions.swift | 29 -- .../experimental/swift/Sources/Model.swift | 40 -- .../Sources/QuantizationParameters.swift | 38 -- .../experimental/swift/Sources/Tensor.swift | 138 ------- .../Configs/TensorFlowLite.tulsigen | 57 --- .../project.tulsiconf | 14 - .../project.pbxproj | 345 ------------------ .../TensorFlowLiteApp/AppDelegate.swift | 24 -- .../Array+TensorFlowLite.swift | 22 -- .../AppIcon.appiconset/Contents.json | 98 ----- .../Assets.xcassets/Contents.json | 6 - .../Base.lproj/LaunchScreen.storyboard | 44 --- .../Base.lproj/Main.storyboard | 95 ----- .../Data+TensorFlowLite.swift | 13 - .../TensorFlowLiteApp/Info.plist | 46 --- .../TensorFlowLiteApp/ViewController.swift | 299 --------------- .../swift/Tests/InterpreterOptionsTests.swift | 54 --- .../swift/Tests/InterpreterTests.swift | 315 ---------------- .../experimental/swift/Tests/ModelTests.swift | 59 --- .../Tests/QuantizationParametersTests.swift | 43 --- .../swift/Tests/TensorTests.swift | 83 ----- 26 files changed, 2583 deletions(-) delete mode 100644 tensorflow/lite/experimental/swift/BUILD delete mode 100644 tensorflow/lite/experimental/swift/LICENSE delete mode 100644 tensorflow/lite/experimental/swift/README.md delete mode 100644 tensorflow/lite/experimental/swift/Sources/Interpreter.swift delete mode 100644 tensorflow/lite/experimental/swift/Sources/InterpreterError.swift delete mode 100644 tensorflow/lite/experimental/swift/Sources/InterpreterOptions.swift delete mode 100644 tensorflow/lite/experimental/swift/Sources/Model.swift delete mode 100644 tensorflow/lite/experimental/swift/Sources/QuantizationParameters.swift delete mode 100644 tensorflow/lite/experimental/swift/Sources/Tensor.swift delete mode 100644 tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/Configs/TensorFlowLite.tulsigen delete mode 100644 tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/project.tulsiconf delete mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp.xcodeproj/project.pbxproj delete mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/AppDelegate.swift delete mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Array+TensorFlowLite.swift delete mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/Contents.json delete mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/LaunchScreen.storyboard delete mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/Main.storyboard delete mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Data+TensorFlowLite.swift delete mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Info.plist delete mode 100644 tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/ViewController.swift delete mode 100644 tensorflow/lite/experimental/swift/Tests/InterpreterOptionsTests.swift delete mode 100644 tensorflow/lite/experimental/swift/Tests/InterpreterTests.swift delete mode 100644 tensorflow/lite/experimental/swift/Tests/ModelTests.swift delete mode 100644 tensorflow/lite/experimental/swift/Tests/QuantizationParametersTests.swift delete mode 100644 tensorflow/lite/experimental/swift/Tests/TensorTests.swift diff --git a/tensorflow/lite/experimental/swift/BUILD b/tensorflow/lite/experimental/swift/BUILD deleted file mode 100644 index 53bcb0ecbd..0000000000 --- a/tensorflow/lite/experimental/swift/BUILD +++ /dev/null @@ -1,101 +0,0 @@ -# TensorFlow Lite for Swift. - -package(default_visibility = ["//visibility:private"]) - -licenses(["notice"]) # Apache 2.0 - -exports_files(["LICENSE"]) - -load("@build_bazel_rules_apple//apple:ios.bzl", "ios_application", "ios_unit_test") -load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") - -MINIMUM_OS_VERSION = "9.0" - -SWIFT_COPTS = [ - "-wmo", -] - -swift_library( - name = "TensorFlowLite", - srcs = glob(["Sources/*.swift"]), - copts = SWIFT_COPTS, - module_name = "TensorFlowLite", - tags = ["manual"], - deps = [ - "//tensorflow/lite/experimental/c:c_api", - ], -) - -ios_unit_test( - name = "TensorFlowLiteTests", - size = "small", - minimum_os_version = MINIMUM_OS_VERSION, - tags = [ - "manual", - # DISABLED: Following sanitizer tests are not supported by iOS test targets. - "noasan", - "nomsan", - "notsan", - ], - deps = [":TensorFlowLiteTestsLib"], -) - -swift_library( - name = "TensorFlowLiteTestsLib", - testonly = 1, - srcs = glob(["Tests/*.swift"]), - copts = SWIFT_COPTS, - tags = ["manual"], - deps = [ - ":TensorFlowLite", - ":TestResources", - ], -) - -objc_library( - name = "TestResources", - resources = [ - "//tensorflow/lite:testdata/add.bin", - "//tensorflow/lite:testdata/add_quantized.bin", - "//tensorflow/lite:testdata/multi_add.bin", - ], - tags = ["manual"], -) - -ios_application( - name = "TensorFlowLiteApp", - app_icons = glob(["TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/AppIcon.appiconset/**"]), - bundle_id = "com.tensorflow.lite.swift.TensorFlowLite", - families = [ - "ipad", - "iphone", - ], - infoplists = ["TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Info.plist"], - launch_storyboard = "TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/LaunchScreen.storyboard", - minimum_os_version = MINIMUM_OS_VERSION, - sdk_frameworks = [ - "CoreGraphics", - ], - tags = ["manual"], - deps = [":TensorFlowLiteAppLib"], -) - -swift_library( - name = "TensorFlowLiteAppLib", - srcs = glob(["TestApps/TensorFlowLiteApp/TensorFlowLiteApp/*.swift"]), - module_name = "TensorFlowLiteAppLib", - tags = ["manual"], - deps = [ - ":TensorFlowLite", - ":TensorFlowLiteAppResources", - ], -) - -objc_library( - name = "TensorFlowLiteAppResources", - storyboards = glob([ - "TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/*.storyboard", - ]), - tags = ["manual"], - deps = [":TestResources"], -) diff --git a/tensorflow/lite/experimental/swift/LICENSE b/tensorflow/lite/experimental/swift/LICENSE deleted file mode 100644 index d645695673..0000000000 --- a/tensorflow/lite/experimental/swift/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/tensorflow/lite/experimental/swift/README.md b/tensorflow/lite/experimental/swift/README.md deleted file mode 100644 index deb16c1e91..0000000000 --- a/tensorflow/lite/experimental/swift/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# TensorFlow Lite for Swift - -[TensorFlow Lite](https://www.tensorflow.org/lite/) is TensorFlow's lightweight -solution for Swift developers. It enables low-latency inference of on-device -machine learning models with a small binary size and fast performance supporting -hardware acceleration. - -## Getting Started - -### Bazel - -In your `BUILD` file, add the `TensorFlowLite` dependency: - -```python -swift_library( - # ... - deps = [ - "//tensorflow/lite/swift:TensorFlowLite", - ], -) -``` - -In your Swift files, import the module: - -```swift -import TensorFlowLite -``` - -### Tulsi - -Open the `TensorFlowLite.tulsiproj` using the [TulsiApp](https://github.com/bazelbuild/tulsi) or by -running the [`generate_xcodeproj.sh`](https://github.com/bazelbuild/tulsi/blob/master/src/tools/generate_xcodeproj.sh) -script: - -```shell -generate_xcodeproj.sh --genconfig tensorflow/lite/swift/TensorFlowLite.tulsiproj:TensorFlowLite --outputfolder ~/path/to/generated/TensorFlowLite.xcodeproj -``` - -### CocoaPods - -Add the following to your `Podfile`: - -```ruby -use_frameworks! -pod 'TensorFlowLiteSwift' -``` - -Then, run `pod install`. - -In your Swift files, import the module: - -```swift -import TensorFlowLite -``` diff --git a/tensorflow/lite/experimental/swift/Sources/Interpreter.swift b/tensorflow/lite/experimental/swift/Sources/Interpreter.swift deleted file mode 100644 index a14b5966b1..0000000000 --- a/tensorflow/lite/experimental/swift/Sources/Interpreter.swift +++ /dev/null @@ -1,265 +0,0 @@ -// Copyright 2018 Google Inc. 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. - -import Foundation -import TensorFlowLiteCAPI - -/// A TensorFlow Lite interpreter that performs inference from a given model. -public final class Interpreter { - - /// The `TFL_Interpreter` C pointer type represented as an `UnsafePointer`. - private typealias CInterpreter = OpaquePointer - - /// Total number of input tensors associated with the model. - public var inputTensorCount: Int { - return Int(TFL_InterpreterGetInputTensorCount(cInterpreter)) - } - - /// Total number of output tensors associated with the model. - public var outputTensorCount: Int { - return Int(TFL_InterpreterGetOutputTensorCount(cInterpreter)) - } - - /// The underlying `TFL_Interpreter` C pointer. - private var cInterpreter: CInterpreter? - - /// Creates a new model interpreter instance. - /// - /// - Parameters: - /// - modelPath: Local file path to a TensorFlow Lite model. - /// - options: Custom configurations for the interpreter. The default is `nil` indicating that - /// interpreter will determine the configuration options. - /// - Throws: An error if the model could not be loaded or the interpreter could not be created. - public init(modelPath: String, options: InterpreterOptions? = nil) throws { - guard let model = Model(filePath: modelPath) else { throw InterpreterError.failedToLoadModel } - - let cInterpreterOptions: OpaquePointer? = try options.map { options in - guard let cOptions = TFL_NewInterpreterOptions() else { - throw InterpreterError.failedToCreateInterpreter - } - if let threadCount = options.threadCount, threadCount > 0 { - TFL_InterpreterOptionsSetNumThreads(cOptions, Int32(threadCount)) - } - if options.isErrorLoggingEnabled { - TFL_InterpreterOptionsSetErrorReporter( - cOptions, - { (_, format, arguments) in - guard let cFormat = format, - let message = String(cFormat: cFormat, arguments: arguments) - else { - return - } - print(String(describing: InterpreterError.tensorFlowLiteError(message))) - }, - nil - ) - } - return cOptions - } - defer { TFL_DeleteInterpreterOptions(cInterpreterOptions) } - - guard let cInterpreter = TFL_NewInterpreter(model.cModel, cInterpreterOptions) else { - throw InterpreterError.failedToCreateInterpreter - } - self.cInterpreter = cInterpreter - } - - deinit { - TFL_DeleteInterpreter(cInterpreter) - } - - /// Invokes the interpreter to perform inference from the loaded graph. - /// - /// - Throws: An error if the model was not ready because tensors were not allocated. - public func invoke() throws { - guard TFL_InterpreterInvoke(cInterpreter) == kTfLiteOk else { - // TODO(b/117510052): Determine which error to throw. - throw InterpreterError.allocateTensorsRequired - } - } - - /// Returns the input tensor at the given index. - /// - /// - Parameters: - /// - index: The index for the input tensor. - /// - Throws: An error if the index is invalid or the tensors have not been allocated. - /// - Returns: The input tensor at the given index. - public func input(at index: Int) throws -> Tensor { - let maxIndex = inputTensorCount - 1 - guard case 0...maxIndex = index else { - throw InterpreterError.invalidTensorIndex(index: index, maxIndex: maxIndex) - } - guard let cTensor = TFL_InterpreterGetInputTensor(cInterpreter, Int32(index)), - let bytes = TFL_TensorData(cTensor), - let nameCString = TFL_TensorName(cTensor) - else { - throw InterpreterError.allocateTensorsRequired - } - guard let dataType = TensorDataType(type: TFL_TensorType(cTensor)) else { - throw InterpreterError.invalidTensorDataType - } - - let name = String(cString: nameCString) - let rank = TFL_TensorNumDims(cTensor) - let dimensions = (0.. Tensor { - let maxIndex = outputTensorCount - 1 - guard case 0...maxIndex = index else { - throw InterpreterError.invalidTensorIndex(index: index, maxIndex: maxIndex) - } - guard let cTensor = TFL_InterpreterGetOutputTensor(cInterpreter, Int32(index)), - let bytes = TFL_TensorData(cTensor), - let nameCString = TFL_TensorName(cTensor) - else { - // TODO(b/117510052): Determine which error to throw. - throw InterpreterError.invokeInterpreterRequired - } - guard let dataType = TensorDataType(type: TFL_TensorType(cTensor)) else { - throw InterpreterError.invalidTensorDataType - } - - let name = String(cString: nameCString) - let rank = TFL_TensorNumDims(cTensor) - let dimensions = (0.. Tensor { - let maxIndex = inputTensorCount - 1 - guard case 0...maxIndex = index else { - throw InterpreterError.invalidTensorIndex(index: index, maxIndex: maxIndex) - } - guard let cTensor = TFL_InterpreterGetInputTensor(cInterpreter, Int32(index)) else { - throw InterpreterError.allocateTensorsRequired - } - - let byteCount = TFL_TensorByteSize(cTensor) - guard data.count == byteCount else { - throw InterpreterError.invalidTensorDataCount(provided: data.count, required: byteCount) - } - - let status = data.withUnsafeBytes { TFL_TensorCopyFromBuffer(cTensor, $0, data.count) } - guard status == kTfLiteOk else { throw InterpreterError.failedToCopyDataToInputTensor } - return try input(at: index) - } - - /// Allocates memory for all input tensors based on their `TensorShape`s. - /// - /// - Note: This is a relatively expensive operation and should only be called after creating the - /// interpreter and/or resizing any input tensors. - /// - Throws: An error if memory could not be allocated for the input tensors. - public func allocateTensors() throws { - guard TFL_InterpreterAllocateTensors(cInterpreter) == kTfLiteOk else { - throw InterpreterError.failedToAllocateTensors - } - } -} - -// MARK: - Extensions - -extension String { - /// Returns a new `String` initialized by using the given format C array as a template into which - /// the remaining argument values are substituted according to the user’s default locale. - /// - /// - Note: Returns `nil` if a new `String` could not be constructed from the given values. - /// - Parameters: - /// - cFormat: The format C array as a template for substituting values. - /// - arguments: A C pointer to a `va_list` of arguments to substitute into `cFormat`. - init?(cFormat: UnsafePointer, arguments: CVaListPointer) { - var buffer: UnsafeMutablePointer? - guard vasprintf(&buffer, cFormat, arguments) != 0, let cString = buffer else { return nil } - self.init(validatingUTF8: cString) - } -} diff --git a/tensorflow/lite/experimental/swift/Sources/InterpreterError.swift b/tensorflow/lite/experimental/swift/Sources/InterpreterError.swift deleted file mode 100644 index 5de58b997a..0000000000 --- a/tensorflow/lite/experimental/swift/Sources/InterpreterError.swift +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2018 Google Inc. 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. - -import Foundation - -/// TensorFlow Lite interpreter errors. -public enum InterpreterError: Error { - case invalidTensorIndex(index: Int, maxIndex: Int) - case invalidTensorDataCount(provided: Int, required: Int) - case invalidTensorDataType - case failedToLoadModel - case failedToCreateInterpreter - case failedToResizeInputTensor(index: Int) - case failedToCopyDataToInputTensor - case failedToAllocateTensors - case allocateTensorsRequired - case invokeInterpreterRequired - case tensorFlowLiteError(String) -} - -// MARK: - Extensions - -extension InterpreterError: LocalizedError { - /// Localized description of the interpreter error. - public var errorDescription: String? { - switch self { - case .invalidTensorIndex(let index, let maxIndex): - return "Invalid tensor index \(index), max index is \(maxIndex)." - case .invalidTensorDataCount(let providedCount, let requiredCount): - return "Provided data count \(providedCount) must match the required count \(requiredCount)." - case .invalidTensorDataType: - return "Tensor data type is unsupported or could not be determined because of a model error." - case .failedToLoadModel: - return "Failed to load the given model." - case .failedToCreateInterpreter: - return "Failed to create the interpreter." - case .failedToResizeInputTensor(let index): - return "Failed to resize input tesnor at index \(index)." - case .failedToCopyDataToInputTensor: - return "Failed to copy data to input tensor." - case .failedToAllocateTensors: - return "Failed to allocate memory for input tensors." - case .allocateTensorsRequired: - return "Must call allocateTensors()." - case .invokeInterpreterRequired: - return "Must call invoke()." - case .tensorFlowLiteError(let message): - return "TensorFlow Lite Error: \(message)" - } - } -} - -extension InterpreterError: CustomStringConvertible { - /// Textual representation of the TensorFlow Lite interpreter error. - public var description: String { - return errorDescription ?? "Unknown error." - } -} - -#if swift(>=4.2) -extension InterpreterError: Equatable {} -#else -extension InterpreterError: Equatable { - public static func == (lhs: InterpreterError, rhs: InterpreterError) -> Bool { - switch (lhs, rhs) { - case (.invalidTensorDataType, .invalidTensorDataType), - (.failedToLoadModel, .failedToLoadModel), - (.failedToCreateInterpreter, .failedToCreateInterpreter), - (.failedToAllocateTensors, .failedToAllocateTensors), - (.allocateTensorsRequired, .allocateTensorsRequired), - (.invokeInterpreterRequired, .invokeInterpreterRequired): - return true - case (.invalidTensorIndex(let lhsIndex, let lhsMaxIndex), - .invalidTensorIndex(let rhsIndex, let rhsMaxIndex)): - return lhsIndex == rhsIndex && lhsMaxIndex == rhsMaxIndex - case (.invalidTensorDataCount(let lhsProvidedCount, let lhsRequiredCount), - .invalidTensorDataCount(let rhsProvidedCount, let rhsRequiredCount)): - return lhsProvidedCount == rhsProvidedCount && lhsRequiredCount == rhsRequiredCount - case (.failedToResizeInputTensor(let lhsIndex), .failedToResizeInputTensor(let rhsIndex)): - return lhsIndex == rhsIndex - case (.tensorFlowLiteError(let lhsMessage), .tensorFlowLiteError(let rhsMessage)): - return lhsMessage == rhsMessage - default: - return false - } - } -} -#endif // swift(>=4.2) diff --git a/tensorflow/lite/experimental/swift/Sources/InterpreterOptions.swift b/tensorflow/lite/experimental/swift/Sources/InterpreterOptions.swift deleted file mode 100644 index 2365fd7ade..0000000000 --- a/tensorflow/lite/experimental/swift/Sources/InterpreterOptions.swift +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2018 Google Inc. 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. - -import Foundation - -/// Custom configuration options for a TensorFlow Lite interpreter. -public struct InterpreterOptions: Equatable { - - /// Maximum number of CPU threads that the interpreter should run on. Default is `nil` which - /// indicates that the `Interpreter` will decide the number of threads to use. - public var threadCount: Int? = nil - - /// Whether error logging to the console is enabled. The default is `false`. - public var isErrorLoggingEnabled = false - - /// Creates a new instance of interpreter options. - public init() {} -} diff --git a/tensorflow/lite/experimental/swift/Sources/Model.swift b/tensorflow/lite/experimental/swift/Sources/Model.swift deleted file mode 100644 index e8c49ff1ae..0000000000 --- a/tensorflow/lite/experimental/swift/Sources/Model.swift +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2018 Google Inc. 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. - -import Foundation -import TensorFlowLiteCAPI - -/// A TensorFlow Lite model used by the 'Interpreter` to perform inference. -final class Model { - - /// The `TFL_Model` C pointer type represented as an `UnsafePointer`. - typealias CModel = OpaquePointer - - /// The underlying `TFL_Model` C pointer. - let cModel: CModel? - - /// Creates a new model instance. - /// - /// - Precondition: Initialization can fail if the given `filePath` is invalid. - /// - Parameters: - /// - filePath: Local file path to a TensorFlow Lite model. - init?(filePath: String) { - guard !filePath.isEmpty, let cModel = TFL_NewModelFromFile(filePath) else { return nil } - self.cModel = cModel - } - - deinit { - TFL_DeleteModel(cModel) - } -} diff --git a/tensorflow/lite/experimental/swift/Sources/QuantizationParameters.swift b/tensorflow/lite/experimental/swift/Sources/QuantizationParameters.swift deleted file mode 100644 index f367875644..0000000000 --- a/tensorflow/lite/experimental/swift/Sources/QuantizationParameters.swift +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2018 Google Inc. 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. - -import Foundation - -/// Parameters that determine the mapping of quantized values to real values. Quantized values can -/// be mapped to float values using the following conversion: -/// `realValue = scale * (quantizedValue - zeroPoint)`. -public struct QuantizationParameters { - - /// Difference between real values corresponding to consecutive quantized values differing by 1. - /// For example, the range of quantized values for `UInt8` data type is [0, 255]. - public let scale: Float - - /// Quantized value that corresponds to the real 0 value. - public let zeroPoint: Int - - /// Creates a new quantization parameters instance. - /// - /// - Parameters: - /// - scale: Scale value for asymmetric quantization. - /// - zeroPoint: Zero point for asymmetric quantization. - init(scale: Float, zeroPoint: Int) { - self.scale = scale - self.zeroPoint = zeroPoint - } -} diff --git a/tensorflow/lite/experimental/swift/Sources/Tensor.swift b/tensorflow/lite/experimental/swift/Sources/Tensor.swift deleted file mode 100644 index b738d87549..0000000000 --- a/tensorflow/lite/experimental/swift/Sources/Tensor.swift +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2018 Google Inc. 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. - -import Foundation -import TensorFlowLiteCAPI - -/// An input or output tensor in a TensorFlow Lite graph. -public struct Tensor { - - /// Name of the tensor. - public let name: String - - /// Data type of the tensor. - public let dataType: TensorDataType - - /// Shape of the tensor. - public let shape: TensorShape - - /// Data in the input or output tensor. - public let data: Data - - /// Quantization parameters for the tensor if using a quantized model. - public let quantizationParameters: QuantizationParameters? - - /// Creates a new input or output tensor instance. - /// - /// - Parameters: - /// - name: Name of the tensor. - /// - dataType: Data type of the tensor. - /// - data: Data in the input tensor. - /// - quantizationParameters Quantization parameters for the tensor if using a quantized model. - /// The default is `nil`. - init( - name: String, - dataType: TensorDataType, - shape: TensorShape, - data: Data, - quantizationParameters: QuantizationParameters? = nil - ) { - self.name = name - self.dataType = dataType - self.shape = shape - self.data = data - self.quantizationParameters = quantizationParameters - } -} - -/// Supported TensorFlow Lite tensor data types. -public enum TensorDataType: Equatable { - /// 32-bit single precision floating point tensor data type. - case float32 - /// 8-bit unsigned integer tensor data type. - case uInt8 - /// 16-bit signed integer tensor data type. - case int16 - /// 32-bit signed integer tensor data type. - case int32 - /// 64-bit signed integer tensor data type. - case int64 - /// Boolean tensor data type. - case bool - - /// Creates a new tensor data type from the given `TFL_Type` or `nil` if the data type is - /// unsupported or could not be determined because there was an error. - /// - /// - Parameter type: A data type supported by a tensor. - init?(type: TFL_Type) { - switch type { - case kTfLiteFloat32: - self = .float32 - case kTfLiteUInt8: - self = .uInt8 - case kTfLiteInt16: - self = .int16 - case kTfLiteInt32: - self = .int32 - case kTfLiteInt64: - self = .int64 - case kTfLiteBool: - self = .bool - case kTfLiteNoType: - fallthrough - default: - return nil - } - } -} - -/// The shape of a TensorFlow Lite tensor. -public struct TensorShape { - - /// The number of dimensions of the tensor. - public let rank: Int - - /// Array of dimensions for the tensor. - public let dimensions: [Int] - - /// Array of `Int32` dimensions for the tensor. - var int32Dimensions: [Int32] { return dimensions.map(Int32.init) } - - /// Creates a new tensor shape instance with the given array of dimensions. - /// - /// - Parameters: - /// - dimensions: Dimensions for the tensor. - public init(_ dimensions: [Int]) { - self.rank = dimensions.count - self.dimensions = dimensions - } - - /// Creates a new tensor shape instance with the given elements representing the dimensions. - /// - /// - Parameters: - /// - elements: Dimensions for the tensor. - public init(_ elements: Int...) { - self.init(elements) - } -} - -extension TensorShape: ExpressibleByArrayLiteral { - /// Creates a new tensor shape instance with the given array literal representing the dimensions. - /// - /// - Parameters: - /// - arrayLiteral: Dimensions for the tensor. - public init(arrayLiteral: Int...) { - self.init(arrayLiteral) - } -} diff --git a/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/Configs/TensorFlowLite.tulsigen b/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/Configs/TensorFlowLite.tulsigen deleted file mode 100644 index 16bc6cbfe8..0000000000 --- a/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/Configs/TensorFlowLite.tulsigen +++ /dev/null @@ -1,57 +0,0 @@ -{ - "sourceFilters" : [ - "tensorflow/lite/experimental/c", - "tensorflow/lite/experimental/swift", - "tensorflow/lite/experimental/swift/Sources", - "tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp", - "tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj", - "tensorflow/lite/experimental/swift/Tests", - ], - "buildTargets" : [ - "//tensorflow/lite/experimental/swift:TensorFlowLite", - "//tensorflow/lite/experimental/swift:TensorFlowLiteApp", - "//tensorflow/lite/experimental/swift:TensorFlowLiteTests", - ], - "projectName" : "TensorFlowLite", - "optionSet" : { - "LaunchActionPreActionScript" : { - "p" : "$(inherited)" - }, - "BazelBuildStartupOptionsRelease" : { - "p" : "$(inherited)" - }, - "BazelBuildOptionsRelease" : { - "p" : "$(inherited)" - }, - "BazelBuildOptionsDebug" : { - "p" : "$(inherited)" - }, - "EnvironmentVariables" : { - "p" : "$(inherited)" - }, - "BuildActionPreActionScript" : { - "p" : "$(inherited)" - }, - "CommandlineArguments" : { - "p" : "$(inherited)" - }, - "TestActionPreActionScript" : { - "p" : "$(inherited)" - }, - "BazelBuildStartupOptionsDebug" : { - "p" : "$(inherited)" - }, - "BuildActionPostActionScript" : { - "p" : "$(inherited)" - }, - "TestActionPostActionScript" : { - "p" : "$(inherited)" - }, - "LaunchActionPostActionScript" : { - "p" : "$(inherited)" - } - }, - "additionalFilePaths" : [ - "tensorflow/lite/experimental/swift/BUILD" - ] -} diff --git a/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/project.tulsiconf b/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/project.tulsiconf deleted file mode 100644 index 82ac8aa381..0000000000 --- a/tensorflow/lite/experimental/swift/TensorFlowLite.tulsiproj/project.tulsiconf +++ /dev/null @@ -1,14 +0,0 @@ -{ - "configDefaults" : { - "optionSet" : { - "ProjectPrioritizesSwift" : { - "p" : "YES" - } - } - }, - "projectName" : "TensorFlowLite", - "packages" : [ - "tensorflow/lite/experimental/swift" - ], - "workspaceRoot" : "../../../../.." -} diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp.xcodeproj/project.pbxproj b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp.xcodeproj/project.pbxproj deleted file mode 100644 index fbbf9a1de2..0000000000 --- a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp.xcodeproj/project.pbxproj +++ /dev/null @@ -1,345 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 50; - objects = { - -/* Begin PBXBuildFile section */ - 4A7304B421500B8400C90B21 /* Data+TensorFlowLite.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A7304B321500B8300C90B21 /* Data+TensorFlowLite.swift */; }; - 4AA72B732146ED64006C3AEF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AA72B722146ED64006C3AEF /* AppDelegate.swift */; }; - 4AA72B752146ED64006C3AEF /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AA72B742146ED64006C3AEF /* ViewController.swift */; }; - 4AA72B782146ED64006C3AEF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4AA72B762146ED64006C3AEF /* Main.storyboard */; }; - 4AA72B7A2146ED66006C3AEF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4AA72B792146ED66006C3AEF /* Assets.xcassets */; }; - 4AA72B7D2146ED66006C3AEF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4AA72B7B2146ED66006C3AEF /* LaunchScreen.storyboard */; }; - 4ADDE0CE2176600E00FF07A2 /* Array+TensorFlowLite.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ADDE0CD2176600900FF07A2 /* Array+TensorFlowLite.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 4A7304B321500B8300C90B21 /* Data+TensorFlowLite.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+TensorFlowLite.swift"; sourceTree = ""; }; - 4AA72B6F2146ED64006C3AEF /* TensorFlowLiteApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TensorFlowLiteApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 4AA72B722146ED64006C3AEF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 4AA72B742146ED64006C3AEF /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; - 4AA72B772146ED64006C3AEF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 4AA72B792146ED66006C3AEF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 4AA72B7C2146ED66006C3AEF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 4AA72B7E2146ED66006C3AEF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 4ADDE0CD2176600900FF07A2 /* Array+TensorFlowLite.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+TensorFlowLite.swift"; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 4AA72B6C2146ED64006C3AEF /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 4AA72B662146ED64006C3AEF = { - isa = PBXGroup; - children = ( - 4AA72B712146ED64006C3AEF /* TensorFlowLiteApp */, - 4AA72B702146ED64006C3AEF /* Products */, - ); - sourceTree = ""; - }; - 4AA72B702146ED64006C3AEF /* Products */ = { - isa = PBXGroup; - children = ( - 4AA72B6F2146ED64006C3AEF /* TensorFlowLiteApp.app */, - ); - name = Products; - sourceTree = ""; - }; - 4AA72B712146ED64006C3AEF /* TensorFlowLiteApp */ = { - isa = PBXGroup; - children = ( - 4AA72B722146ED64006C3AEF /* AppDelegate.swift */, - 4ADDE0CD2176600900FF07A2 /* Array+TensorFlowLite.swift */, - 4A7304B321500B8300C90B21 /* Data+TensorFlowLite.swift */, - 4AA72B742146ED64006C3AEF /* ViewController.swift */, - 4AA72B762146ED64006C3AEF /* Main.storyboard */, - 4AA72B792146ED66006C3AEF /* Assets.xcassets */, - 4AA72B7B2146ED66006C3AEF /* LaunchScreen.storyboard */, - 4AA72B7E2146ED66006C3AEF /* Info.plist */, - ); - path = TensorFlowLiteApp; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 4AA72B6E2146ED64006C3AEF /* TensorFlowLiteApp */ = { - isa = PBXNativeTarget; - buildConfigurationList = 4AA72B812146ED66006C3AEF /* Build configuration list for PBXNativeTarget "TensorFlowLiteApp" */; - buildPhases = ( - 4AA72B6B2146ED64006C3AEF /* Sources */, - 4AA72B6C2146ED64006C3AEF /* Frameworks */, - 4AA72B6D2146ED64006C3AEF /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = TensorFlowLiteApp; - productName = TensorFlowLiteApp; - productReference = 4AA72B6F2146ED64006C3AEF /* TensorFlowLiteApp.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 4AA72B672146ED64006C3AEF /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0940; - LastUpgradeCheck = 0940; - ORGANIZATIONNAME = Google; - TargetAttributes = { - 4AA72B6E2146ED64006C3AEF = { - CreatedOnToolsVersion = 9.4.1; - }; - }; - }; - buildConfigurationList = 4AA72B6A2146ED64006C3AEF /* Build configuration list for PBXProject "TensorFlowLiteApp" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 4AA72B662146ED64006C3AEF; - productRefGroup = 4AA72B702146ED64006C3AEF /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 4AA72B6E2146ED64006C3AEF /* TensorFlowLiteApp */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 4AA72B6D2146ED64006C3AEF /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4AA72B7D2146ED66006C3AEF /* LaunchScreen.storyboard in Resources */, - 4AA72B7A2146ED66006C3AEF /* Assets.xcassets in Resources */, - 4AA72B782146ED64006C3AEF /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 4AA72B6B2146ED64006C3AEF /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4AA72B732146ED64006C3AEF /* AppDelegate.swift in Sources */, - 4ADDE0CE2176600E00FF07A2 /* Array+TensorFlowLite.swift in Sources */, - 4A7304B421500B8400C90B21 /* Data+TensorFlowLite.swift in Sources */, - 4AA72B752146ED64006C3AEF /* ViewController.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 4AA72B762146ED64006C3AEF /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 4AA72B772146ED64006C3AEF /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 4AA72B7B2146ED66006C3AEF /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 4AA72B7C2146ED66006C3AEF /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 4AA72B7F2146ED66006C3AEF /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.4; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - 4AA72B802146ED66006C3AEF /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.4; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 4AA72B822146ED66006C3AEF /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = TensorFlowLiteApp/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.tensorflow.lite.swift.TensorFlowLite; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 4AA72B832146ED66006C3AEF /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = TensorFlowLiteApp/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.tensorflow.lite.swift.TensorFlowLite; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 4AA72B6A2146ED64006C3AEF /* Build configuration list for PBXProject "TensorFlowLiteApp" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4AA72B7F2146ED66006C3AEF /* Debug */, - 4AA72B802146ED66006C3AEF /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4AA72B812146ED66006C3AEF /* Build configuration list for PBXNativeTarget "TensorFlowLiteApp" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4AA72B822146ED66006C3AEF /* Debug */, - 4AA72B832146ED66006C3AEF /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 4AA72B672146ED64006C3AEF /* Project object */; -} diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/AppDelegate.swift b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/AppDelegate.swift deleted file mode 100644 index ffa90a06ad..0000000000 --- a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/AppDelegate.swift +++ /dev/null @@ -1,24 +0,0 @@ -import UIKit - -@UIApplicationMain - -final class AppDelegate: UIResponder, UIApplicationDelegate { - - /// The main window of the app. - var window: UIWindow? - - func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil - ) -> Bool { - return true - } -} - -// MARK: - Extensions - -#if !swift(>=4.2) -extension UIApplication { - typealias LaunchOptionsKey = UIApplicationLaunchOptionsKey -} -#endif // !swift(>=4.2) diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Array+TensorFlowLite.swift b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Array+TensorFlowLite.swift deleted file mode 100644 index 56df1ce659..0000000000 --- a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Array+TensorFlowLite.swift +++ /dev/null @@ -1,22 +0,0 @@ -import Foundation - -extension Array { - /// Creates a new array from the bytes of the given unsafe data. - /// - /// - Warning: The array's `Element` type must be trivial in that it can be copied bit for bit - /// with no indirection or reference-counting operations; otherwise, copying the raw bytes in - /// the `unsafeData`'s buffer to a new array returns an unsafe copy. - /// - Note: Returns `nil` if `unsafeData.count` is not a multiple of - /// `MemoryLayout.stride`. - /// - Parameter unsafeData: The data containing the bytes to turn into an array. - init?(unsafeData: Data) { - guard unsafeData.count % MemoryLayout.stride == 0 else { return nil } - let elements = unsafeData.withUnsafeBytes { - UnsafeBufferPointer( - start: $0, - count: unsafeData.count / MemoryLayout.stride - ) - } - self.init(elements) - } -} diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index d8db8d65fd..0000000000 --- a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "size" : "20x20", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "20x20", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "3x" - }, - { - "idiom" : "ipad", - "size" : "20x20", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "20x20", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "29x29", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "40x40", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "76x76", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "76x76", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "83.5x83.5", - "scale" : "2x" - }, - { - "idiom" : "ios-marketing", - "size" : "1024x1024", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/Contents.json b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/Contents.json deleted file mode 100644 index da4a164c91..0000000000 --- a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/LaunchScreen.storyboard b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index a07a1321be..0000000000 --- a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/Main.storyboard b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/Main.storyboard deleted file mode 100644 index 10cae6e855..0000000000 --- a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Base.lproj/Main.storyboard +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Data+TensorFlowLite.swift b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Data+TensorFlowLite.swift deleted file mode 100644 index bc8a70c848..0000000000 --- a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Data+TensorFlowLite.swift +++ /dev/null @@ -1,13 +0,0 @@ -import Foundation - -extension Data { - /// Creates a new buffer by copying the buffer pointer of the given array. - /// - /// - Warning: The given array's element type `T` must be trivial in that it can be copied bit - /// for bit with no indirection or reference-counting operations; otherwise, reinterpreting - /// data from the resulting buffer has undefined behavior. - /// - Parameter array: An array with elements of type `T`. - init(copyingBufferOf array: [T]) { - self = array.withUnsafeBufferPointer(Data.init) - } -} diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Info.plist b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Info.plist deleted file mode 100644 index 3ca3875f04..0000000000 --- a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/Info.plist +++ /dev/null @@ -1,46 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleVersion - 0.0.1 - LSRequiresIPhoneOS - - NSCameraUsageDescription - NSCameraUsageDescription - NSPhotoLibraryUsageDescription - Select a photo to detect objects in. - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - - - diff --git a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/ViewController.swift b/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/ViewController.swift deleted file mode 100644 index 73c74fd19c..0000000000 --- a/tensorflow/lite/experimental/swift/TestApps/TensorFlowLiteApp/TensorFlowLiteApp/ViewController.swift +++ /dev/null @@ -1,299 +0,0 @@ -import TensorFlowLite -import UIKit - -class ViewController: UIViewController { - - // MARK: - Properties - - /// TensorFlowLite interpreter object for performing inference from a given model. - private var interpreter: Interpreter? - - /// Serial dispatch queue for managing `Interpreter` calls. - private let interpreterQueue = DispatchQueue( - label: Constant.dispatchQueueLabel, - qos: .userInitiated - ) - - /// The currently selected model. - private var currentModel: Model { - guard let currentModel = Model(rawValue: modelControl.selectedSegmentIndex) else { - preconditionFailure("Invalid model for selected segment index.") - } - return currentModel - } - - /// A description of the current model. - private var modelDescription: String { - guard let interpreter = interpreter else { return "" } - let inputCount = interpreter.inputTensorCount - let outputCount = interpreter.outputTensorCount - let inputTensors = (0.. String = { - guard let results = [Float32](unsafeData: outputTensor.data) else { return "No results." } - return resultsText + results.description - } - self.updateResultsText(results()) - } catch let error { - self.updateResultsText( - "Failed to invoke the interpreter with error: \(error.localizedDescription)" - ) - return - } - } - } - - private func invokeAddQuantized() { - interpreterQueue.async { - guard let interpreter = self.interpreter else { - self.updateResultsText(Constant.nilInterpreterErrorMessage) - return - } - do { - try interpreter.resizeInput(at: 0, to: [2]) - try interpreter.allocateTensors() - let input: [UInt8] = [1, 3] - let resultsText = self.modelDescription + "\n\n" + - "Performing 2 add operations on quantized input \(input.description) equals: " - self.updateResultsText(resultsText) - let data = Data(input) - try interpreter.copy(data, toInputAt: 0) - try interpreter.invoke() - let outputTensor = try interpreter.output(at: 0) - let results: () -> String = { - guard let quantizationParameters = outputTensor.quantizationParameters else { - return "No results." - } - let quantizedResults = [UInt8](outputTensor.data) - let dequantizedResults = quantizedResults.map { - quantizationParameters.scale * Float(Int($0) - quantizationParameters.zeroPoint) - } - return resultsText + quantizedResults.description + - ", dequantized results: " + dequantizedResults.description - } - self.updateResultsText(results()) - } catch let error { - self.updateResultsText( - "Failed to invoke the interpreter with error: \(error.localizedDescription)" - ) - return - } - } - } - - private func invokeMultiAdd() { - interpreterQueue.async { - guard let interpreter = self.interpreter else { - self.updateResultsText(Constant.nilInterpreterErrorMessage) - return - } - do { - let shape = TensorShape(2) - try (0.. [Float32] in - let input = [Float32(index + 1), Float32(index + 2)] - let data = Data(copyingBufferOf: input) - try interpreter.copy(data, toInputAt: index) - return input - } - let resultsText = self.modelDescription + "\n\n" + - "Performing 3 add operations on inputs \(inputs.description) equals: " - self.updateResultsText(resultsText) - try interpreter.invoke() - let results = try (0.. [Float32] in - let tensor = try interpreter.output(at: index) - return [Float32](unsafeData: tensor.data) ?? [] - } - self.updateResultsText(resultsText + results.description) - } catch let error { - self.updateResultsText( - "Failed to invoke the interpreter with error: \(error.localizedDescription)" - ) - return - } - } - } - - private func updateResultsText(_ text: String? = nil) { - safeDispatchOnMain { self.resultsTextView.text = text } - } -} - -// MARK: - Constants - -private enum Constant { - static let dispatchQueueLabel = "TensorFlowLiteInterpreterQueue" - static let nilInterpreterErrorMessage = - "Failed to invoke the interpreter because the interpreter was nil." -} - -/// Models that can be loaded by the TensorFlow Lite `Interpreter`. -private enum Model: Int, CustomStringConvertible { - /// A float model that performs two add operations on one input tensor and returns the result in - /// one output tensor. - case add = 0 - /// A quantized model that performs two add operations on one input tensor and returns the result - /// in one output tensor. - case addQuantized = 1 - /// A float model that performs three add operations on four input tensors and returns the results - /// in 2 output tensors. - case multiAdd = 2 - - var fileInfo: (name: String, extension: String) { - switch self { - case .add: - return Add.fileInfo - case .addQuantized: - return AddQuantized.fileInfo - case .multiAdd: - return MultiAdd.fileInfo - } - } - - // MARK: - CustomStringConvertible - - var description: String { - switch self { - case .add: - return Add.name - case .addQuantized: - return AddQuantized.name - case .multiAdd: - return MultiAdd.name - } - } -} - -/// Values for the `Add` model. -private enum Add { - static let name = "Add" - static let fileInfo = (name: "add", extension: "bin") -} - -/// Values for the `AddQuantized` model. -private enum AddQuantized { - static let name = "AddQuantized" - static let fileInfo = (name: "add_quantized", extension: "bin") -} - -/// Values for the `MultiAdd` model. -private enum MultiAdd { - static let name = "MultiAdd" - static let fileInfo = (name: "multi_add", extension: "bin") -} - -// MARK: - Fileprivate - -/// Safely dispatches the given block on the main queue. If the current thread is `main`, the block -/// is executed synchronously; otherwise, the block is executed asynchronously on the main thread. -fileprivate func safeDispatchOnMain(_ block: @escaping () -> Void) { - if Thread.isMainThread { block(); return } - DispatchQueue.main.async { block() } -} diff --git a/tensorflow/lite/experimental/swift/Tests/InterpreterOptionsTests.swift b/tensorflow/lite/experimental/swift/Tests/InterpreterOptionsTests.swift deleted file mode 100644 index 54b4f59b28..0000000000 --- a/tensorflow/lite/experimental/swift/Tests/InterpreterOptionsTests.swift +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2018 Google Inc. 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. - -@testable import TensorFlowLite -import XCTest - -class InterpreterOptionsTests: XCTestCase { - - func testInterpreterOptions_InitWithDefaultValues() { - let options = InterpreterOptions() - XCTAssertNil(options.threadCount) - XCTAssertFalse(options.isErrorLoggingEnabled) - } - - func testInterpreterOptions_InitWithCustomValues() { - var options = InterpreterOptions() - options.threadCount = 2 - XCTAssertEqual(options.threadCount, 2) - options.isErrorLoggingEnabled = true - XCTAssertTrue(options.isErrorLoggingEnabled) - } - - func testInterpreterOptions_Equatable() { - var options1 = InterpreterOptions() - var options2 = InterpreterOptions() - XCTAssertEqual(options1, options2) - - options1.threadCount = 2 - options2.threadCount = 2 - XCTAssertEqual(options1, options2) - - options2.threadCount = 3 - XCTAssertNotEqual(options1, options2) - options2.threadCount = 2 - - options1.isErrorLoggingEnabled = true - options2.isErrorLoggingEnabled = true - XCTAssertEqual(options1, options2) - - options2.isErrorLoggingEnabled = false - XCTAssertNotEqual(options1, options2) - } -} diff --git a/tensorflow/lite/experimental/swift/Tests/InterpreterTests.swift b/tensorflow/lite/experimental/swift/Tests/InterpreterTests.swift deleted file mode 100644 index e98da5f951..0000000000 --- a/tensorflow/lite/experimental/swift/Tests/InterpreterTests.swift +++ /dev/null @@ -1,315 +0,0 @@ -// Copyright 2018 Google Inc. 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. - -@testable import TensorFlowLite -import XCTest - -class InterpreterTests: XCTestCase { - - var interpreter: Interpreter! - - override func setUp() { - super.setUp() - - interpreter = try! Interpreter(modelPath: AddModel.path) - } - - override func tearDown() { - interpreter = nil - - super.tearDown() - } - - func testInterpreter_InitWithModelPath() { - XCTAssertNoThrow(try Interpreter(modelPath: AddModel.path)) - } - - func testInterpreter_Init_ThrowsFailedToLoadModel() { - XCTAssertThrowsError(try Interpreter(modelPath: "/invalid/path")) { error in - self.assertEqualErrors(actual: error, expected: .failedToLoadModel) - } - } - - func testInterpreter_InitWithModelPathAndOptions() { - var options = InterpreterOptions() - options.threadCount = 2 - XCTAssertNoThrow(try Interpreter(modelPath: AddModel.path, options: options)) - } - - func testInterpreter_InputTensorCount() { - XCTAssertEqual(interpreter.inputTensorCount, AddModel.inputTensorCount) - } - - func testInterpreter_OutputTensorCount() { - XCTAssertEqual(interpreter.outputTensorCount, AddModel.outputTensorCount) - } - - func testInterpreter_Invoke() throws { - try interpreter.allocateTensors() - XCTAssertNoThrow(try interpreter.invoke()) - } - - func testInterpreter_Invoke_ThrowsAllocateTensorsRequired_ModelNotReady() { - XCTAssertThrowsError(try interpreter.invoke()) { error in - self.assertEqualErrors(actual: error, expected: .allocateTensorsRequired) - } - } - - func testInterpreter_InputTensorAtIndex() throws { - try setUpAddModelInputTensor() - let inputTensor = try interpreter.input(at: AddModel.validIndex) - XCTAssertEqual(inputTensor, AddModel.inputTensor) - } - - func testInterpreter_InputTensorAtIndex_QuantizedModel() throws { - interpreter = try Interpreter(modelPath: AddQuantizedModel.path) - try setUpAddQuantizedModelInputTensor() - let inputTensor = try interpreter.input(at: AddQuantizedModel.inputOutputIndex) - XCTAssertEqual(inputTensor, AddQuantizedModel.inputTensor) - } - - func testInterpreter_InputTensorAtIndex_ThrowsInvalidIndex() throws { - try interpreter.allocateTensors() - XCTAssertThrowsError(try interpreter.input(at: AddModel.invalidIndex)) { error in - let maxIndex = AddModel.inputTensorCount - 1 - self.assertEqualErrors( - actual: error, - expected: .invalidTensorIndex(index: AddModel.invalidIndex, maxIndex: maxIndex) - ) - } - } - - func testInterpreter_InputTensorAtIndex_ThrowsAllocateTensorsRequired() { - XCTAssertThrowsError(try interpreter.input(at: AddModel.validIndex)) { error in - self.assertEqualErrors(actual: error, expected: .allocateTensorsRequired) - } - } - - func testInterpreter_OutputTensorAtIndex() throws { - try setUpAddModelInputTensor() - try interpreter.invoke() - let outputTensor = try interpreter.output(at: AddModel.validIndex) - XCTAssertEqual(outputTensor, AddModel.outputTensor) - let expectedResults = [Float32](unsafeData: outputTensor.data) - XCTAssertEqual(expectedResults, AddModel.results) - } - - func testInterpreter_OutputTensorAtIndex_QuantizedModel() throws { - interpreter = try Interpreter(modelPath: AddQuantizedModel.path) - try setUpAddQuantizedModelInputTensor() - try interpreter.invoke() - let outputTensor = try interpreter.output(at: AddQuantizedModel.inputOutputIndex) - XCTAssertEqual(outputTensor, AddQuantizedModel.outputTensor) - let expectedResults = [UInt8](outputTensor.data) - XCTAssertEqual(expectedResults, AddQuantizedModel.results) - } - - func testInterpreter_OutputTensorAtIndex_ThrowsInvalidIndex() throws { - try interpreter.allocateTensors() - try interpreter.invoke() - XCTAssertThrowsError(try interpreter.output(at: AddModel.invalidIndex)) { error in - let maxIndex = AddModel.outputTensorCount - 1 - self.assertEqualErrors( - actual: error, - expected: .invalidTensorIndex(index: AddModel.invalidIndex, maxIndex: maxIndex) - ) - } - } - - func testInterpreter_OutputTensorAtIndex_ThrowsInvokeInterpreterRequired() { - XCTAssertThrowsError(try interpreter.output(at: AddModel.validIndex)) { error in - self.assertEqualErrors(actual: error, expected: .invokeInterpreterRequired) - } - } - - func testInterpreter_ResizeInputTensorAtIndexToShape() { - XCTAssertNoThrow(try interpreter.resizeInput(at: AddModel.validIndex, to: [2, 2, 3])) - XCTAssertNoThrow(try interpreter.allocateTensors()) - } - - func testInterpreter_ResizeInputTensorAtIndexToShape_ThrowsInvalidIndex() { - XCTAssertThrowsError(try interpreter.resizeInput( - at: AddModel.invalidIndex, - to: [2, 2, 3] - )) { error in - let maxIndex = AddModel.inputTensorCount - 1 - self.assertEqualErrors( - actual: error, - expected: .invalidTensorIndex(index: AddModel.invalidIndex, maxIndex: maxIndex) - ) - } - } - - func testInterpreter_CopyDataToInputTensorAtIndex() throws { - try interpreter.resizeInput(at: AddModel.validIndex, to: AddModel.shape) - try interpreter.allocateTensors() - let inputTensor = try interpreter.copy(AddModel.inputData, toInputAt: AddModel.validIndex) - XCTAssertEqual(inputTensor.data, AddModel.inputData) - } - - func testInterpreter_CopyDataToInputTensorAtIndex_ThrowsInvalidIndex() { - XCTAssertThrowsError(try interpreter.copy( - AddModel.inputData, - toInputAt: AddModel.invalidIndex - )) { error in - let maxIndex = AddModel.inputTensorCount - 1 - self.assertEqualErrors( - actual: error, - expected: .invalidTensorIndex(index: AddModel.invalidIndex, maxIndex: maxIndex) - ) - } - } - - func testInterpreter_CopyDataToInputTensorAtIndex_ThrowsInvalidDataCount() throws { - try interpreter.resizeInput(at: AddModel.validIndex, to: AddModel.shape) - try interpreter.allocateTensors() - let invalidData = Data(count: AddModel.dataCount - 1) - XCTAssertThrowsError(try interpreter.copy( - invalidData, - toInputAt: AddModel.validIndex - )) { error in - self.assertEqualErrors( - actual: error, - expected: .invalidTensorDataCount(provided: invalidData.count, required: AddModel.dataCount) - ) - } - } - - func testInterpreter_AllocateTensors() { - XCTAssertNoThrow(try interpreter.allocateTensors()) - } - - // MARK: - Private - - private func setUpAddModelInputTensor() throws { - precondition(interpreter != nil) - try interpreter.resizeInput(at: AddModel.validIndex, to: AddModel.shape) - try interpreter.allocateTensors() - try interpreter.copy(AddModel.inputData, toInputAt: AddModel.validIndex) - } - - private func setUpAddQuantizedModelInputTensor() throws { - precondition(interpreter != nil) - try interpreter.resizeInput(at: AddQuantizedModel.inputOutputIndex, to: AddQuantizedModel.shape) - try interpreter.allocateTensors() - try interpreter.copy(AddQuantizedModel.inputData, toInputAt: AddQuantizedModel.inputOutputIndex) - } - - private func assertEqualErrors(actual: Error, expected: InterpreterError) { - guard let actual = actual as? InterpreterError else { - XCTFail("Actual error should be of type InterpreterError.") - return - } - XCTAssertEqual(actual, expected) - } -} - -// MARK: - Constants - -/// Values for the `add.bin` model. -private enum AddModel { - static let info = (name: "add", extension: "bin") - static let inputTensorCount = 1 - static let outputTensorCount = 1 - static let invalidIndex = 1 - static let validIndex = 0 - static let shape: TensorShape = [2] - static let dataCount = inputData.count - static let inputData = Data(copyingBufferOf: [Float32(1.0), Float32(3.0)]) - static let outputData = Data(copyingBufferOf: [Float32(3.0), Float32(9.0)]) - static let results = [Float32(3.0), Float32(9.0)] - - static let inputTensor = Tensor( - name: "input", - dataType: .float32, - shape: shape, - data: inputData - ) - static let outputTensor = Tensor( - name: "output", - dataType: .float32, - shape: shape, - data: outputData - ) - - static var path: String = { - let bundle = Bundle(for: InterpreterTests.self) - guard let path = bundle.path(forResource: info.name, ofType: info.extension) else { return "" } - return path - }() -} - -/// Values for the `add_quantized.bin` model. -private enum AddQuantizedModel { - static let info = (name: "add_quantized", extension: "bin") - static let inputOutputIndex = 0 - static let shape: TensorShape = [2] - static let inputData = Data([1, 3]) - static let outputData = Data([3, 9]) - static let quantizationParameters = QuantizationParameters(scale: 0.003922, zeroPoint: 0) - static let results: [UInt8] = [3, 9] - - static let inputTensor = Tensor( - name: "input", - dataType: .uInt8, - shape: shape, - data: inputData, - quantizationParameters: quantizationParameters - ) - static let outputTensor = Tensor( - name: "output", - dataType: .uInt8, - shape: shape, - data: outputData, - quantizationParameters: quantizationParameters - ) - - static var path: String = { - let bundle = Bundle(for: InterpreterTests.self) - guard let path = bundle.path(forResource: info.name, ofType: info.extension) else { return "" } - return path - }() -} - -// MARK: - Extensions - -extension Array { - /// Creates a new array from the bytes of the given unsafe data. - /// - /// - Note: Returns `nil` if `unsafeData.count` is not a multiple of - /// `MemoryLayout.stride`. - /// - Parameter unsafeData: The data containing the bytes to turn into an array. - init?(unsafeData: Data) { - guard unsafeData.count % MemoryLayout.stride == 0 else { return nil } - let elements = unsafeData.withUnsafeBytes { - UnsafeBufferPointer( - start: $0, - count: unsafeData.count / MemoryLayout.stride - ) - } - self.init(elements) - } -} - -extension Data { - /// Creates a new buffer by copying the buffer pointer of the given array. - /// - /// - Warning: The given array's element type `T` must be trivial in that it can be copied bit - /// for bit with no indirection or reference-counting operations; otherwise, reinterpreting - /// data from the resulting buffer has undefined behavior. - /// - Parameter array: An array with elements of type `T`. - init(copyingBufferOf array: [T]) { - self = array.withUnsafeBufferPointer(Data.init) - } -} diff --git a/tensorflow/lite/experimental/swift/Tests/ModelTests.swift b/tensorflow/lite/experimental/swift/Tests/ModelTests.swift deleted file mode 100644 index 025db18906..0000000000 --- a/tensorflow/lite/experimental/swift/Tests/ModelTests.swift +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2018 Google Inc. 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. - -@testable import TensorFlowLite -import XCTest - -class ModelTests: XCTestCase { - - var modelPath: String! - - override func setUp() { - super.setUp() - - let bundle = Bundle(for: type(of: self)) - guard let modelPath = bundle.path( - forResource: Constant.modelInfo.name, - ofType: Constant.modelInfo.extension) - else { - XCTFail("Failed to get the model file path.") - return - } - self.modelPath = modelPath - } - - override func tearDown() { - modelPath = nil - - super.tearDown() - } - - func testModel_InitWithFilePath() { - XCTAssertNotNil(Model(filePath: modelPath)) - } - - func testModel_InitWithEmptyFilePath_FailsInitialization() { - XCTAssertNil(Model(filePath: "")) - } - - func testModel_InitWithInvalidFilePath_FailsInitialization() { - XCTAssertNil(Model(filePath: "invalid/path")) - } -} - -// MARK: - Constants - -private enum Constant { - static let modelInfo = (name: "add", extension: "bin") -} diff --git a/tensorflow/lite/experimental/swift/Tests/QuantizationParametersTests.swift b/tensorflow/lite/experimental/swift/Tests/QuantizationParametersTests.swift deleted file mode 100644 index 65648c2698..0000000000 --- a/tensorflow/lite/experimental/swift/Tests/QuantizationParametersTests.swift +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2018 Google Inc. 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. - -@testable import TensorFlowLite -import XCTest - -class QuantizationParametersTests: XCTestCase { - - func testQuantizationParameters_InitWithCustomValues() { - let parameters = QuantizationParameters(scale: 0.5, zeroPoint: 1) - XCTAssertEqual(parameters.scale, 0.5) - XCTAssertEqual(parameters.zeroPoint, 1) - } - - func testQuantizationParameters_Equatable() { - let parameters1 = QuantizationParameters(scale: 0.5, zeroPoint: 1) - let parameters2 = QuantizationParameters(scale: 0.5, zeroPoint: 1) - XCTAssertEqual(parameters1, parameters2) - - let parameters3 = QuantizationParameters(scale: 0.4, zeroPoint: 1) - XCTAssertNotEqual(parameters1, parameters3) - XCTAssertNotEqual(parameters2, parameters3) - } -} - -// MARK: - Extensions - -extension QuantizationParameters: Equatable { - public static func == (lhs: QuantizationParameters, rhs: QuantizationParameters) -> Bool { - return lhs.scale == rhs.scale && lhs.zeroPoint == rhs.zeroPoint - } -} diff --git a/tensorflow/lite/experimental/swift/Tests/TensorTests.swift b/tensorflow/lite/experimental/swift/Tests/TensorTests.swift deleted file mode 100644 index 4540043a16..0000000000 --- a/tensorflow/lite/experimental/swift/Tests/TensorTests.swift +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2018 Google Inc. 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. - -@testable import TensorFlowLite -import XCTest - -class TensorTests: XCTestCase { - - // MARK: - Tensor - - func testTensor_Init() { - let name = "InputTensor" - let dataType: TensorDataType = .uInt8 - let shape = TensorShape(Constant.dimensions) - guard let data = name.data(using: .utf8) else { XCTFail("Data should not be nil."); return } - let quantizationParameters = QuantizationParameters(scale: 0.5, zeroPoint: 1) - let inputTensor = Tensor( - name: name, - dataType: dataType, - shape: shape, - data: data, - quantizationParameters: quantizationParameters - ) - XCTAssertEqual(inputTensor.name, name) - XCTAssertEqual(inputTensor.dataType, dataType) - XCTAssertEqual(inputTensor.shape, shape) - XCTAssertEqual(inputTensor.data, data) - XCTAssertEqual(inputTensor.quantizationParameters, quantizationParameters) - } - - // MARK: - TensorShape - - func testTensorShape_InitWithArray() { - let shape = TensorShape(Constant.dimensions) - XCTAssertEqual(shape.rank, Constant.dimensions.count) - XCTAssertEqual(shape.dimensions, Constant.dimensions) - } - - func testTensorShape_InitWithElements() { - let shape = TensorShape(2, 2, 3) - XCTAssertEqual(shape.rank, Constant.dimensions.count) - XCTAssertEqual(shape.dimensions, Constant.dimensions) - } - - func testTensorShape_InitWithArrayLiteral() { - let shape: TensorShape = [2, 2, 3] - XCTAssertEqual(shape.rank, Constant.dimensions.count) - XCTAssertEqual(shape.dimensions, Constant.dimensions) - } -} - -// MARK: - Constants - -private enum Constant { - /// Array of 2 arrays of 2 arrays of 3 numbers: [[[1, 1, 1], [2, 2, 2]], [[3, 3, 3], [4, 4, 4]]]. - static let dimensions = [2, 2, 3] -} - -// MARK: - Extensions - -extension TensorShape: Equatable { - public static func == (lhs: TensorShape, rhs: TensorShape) -> Bool { - return lhs.rank == rhs.rank && lhs.dimensions == rhs.dimensions - } -} - -extension Tensor: Equatable { - public static func == (lhs: Tensor, rhs: Tensor) -> Bool { - return lhs.name == rhs.name && lhs.dataType == rhs.dataType && lhs.shape == rhs.shape && - lhs.data == rhs.data && lhs.quantizationParameters == rhs.quantizationParameters - } -} -- GitLab From fd54d400ba60e57daf431afe209133226e03acfc Mon Sep 17 00:00:00 2001 From: Zhenyu Tan Date: Tue, 8 Jan 2019 14:10:55 -0800 Subject: [PATCH 508/622] Unify RMSProp w/o momentum. PiperOrigin-RevId: 228396933 --- tensorflow/python/keras/optimizer_v2/BUILD | 1 + .../keras/optimizer_v2/optimizer_v2_test.py | 2 +- .../python/keras/optimizer_v2/rmsprop.py | 155 ++++++++++++------ .../python/keras/optimizer_v2/rmsprop_test.py | 106 ++++++++---- 4 files changed, 181 insertions(+), 83 deletions(-) diff --git a/tensorflow/python/keras/optimizer_v2/BUILD b/tensorflow/python/keras/optimizer_v2/BUILD index 964cf5fcba..2b990c9334 100644 --- a/tensorflow/python/keras/optimizer_v2/BUILD +++ b/tensorflow/python/keras/optimizer_v2/BUILD @@ -217,4 +217,5 @@ cuda_py_test( "//tensorflow/python:state_ops", "//tensorflow/python:variables", ], + shard_count = 2, ) diff --git a/tensorflow/python/keras/optimizer_v2/optimizer_v2_test.py b/tensorflow/python/keras/optimizer_v2/optimizer_v2_test.py index 58c8b1f343..b41e4aa064 100644 --- a/tensorflow/python/keras/optimizer_v2/optimizer_v2_test.py +++ b/tensorflow/python/keras/optimizer_v2/optimizer_v2_test.py @@ -507,7 +507,7 @@ class OptimizersCompatibilityTest(test.TestCase, parameterized.TestCase): ('adadelta', 'adadelta', True), ('adagrad', 'adagrad', True), ('adam', 'adam', True), ('adamax', 'adamax', True), ('nadam', 'nadam', True), ('momentum', 'momentum', True), - ('sgd', 'sgd', False)) + ('rmsprop', 'rmsprop', True), ('sgd', 'sgd', False)) def testOptimizersCompatibility(self, opt_str, test_weights): np.random.seed(1331) with self.cached_session(): diff --git a/tensorflow/python/keras/optimizer_v2/rmsprop.py b/tensorflow/python/keras/optimizer_v2/rmsprop.py index 3f153c4a41..83b81cbe5d 100644 --- a/tensorflow/python/keras/optimizer_v2/rmsprop.py +++ b/tensorflow/python/keras/optimizer_v2/rmsprop.py @@ -17,8 +17,14 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import numpy as np + from tensorflow.python.framework import ops from tensorflow.python.keras.optimizer_v2 import optimizer_v2 +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import control_flow_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import state_ops from tensorflow.python.training import training_ops from tensorflow.python.util.tf_export import keras_export @@ -114,77 +120,122 @@ class RMSprop(optimizer_v2.OptimizerV2): def _create_slots(self, var_list): for var in var_list: self.add_slot(var, "rms") - self.add_slot(var, "momentum") - if self.centered: + if self._momentum: + for var in var_list: + self.add_slot(var, "momentum") + if self.centered: + for var in var_list: self.add_slot(var, "mg") def _resource_apply_dense(self, grad, var): var_dtype = var.dtype.base_dtype lr_t = self._decayed_lr(var_dtype) rms = self.get_slot(var, "rms") - mom = self.get_slot(var, "momentum") rho = self._get_hyper("rho", var_dtype) momentum = self._get_hyper("momentum", var_dtype) epsilon = self._get_hyper("epsilon", var_dtype) - if self.centered: - mg = self.get_slot(var, "mg") - return training_ops.resource_apply_centered_rms_prop( - var.handle, - mg.handle, - rms.handle, - mom.handle, - lr_t, - rho, - momentum, - epsilon, - grad, - use_locking=self._use_locking) + if self._momentum: + mom = self.get_slot(var, "momentum") + if self.centered: + mg = self.get_slot(var, "mg") + return training_ops.resource_apply_centered_rms_prop( + var.handle, + mg.handle, + rms.handle, + mom.handle, + lr_t, + rho, + momentum, + epsilon, + grad, + use_locking=self._use_locking) + else: + return training_ops.resource_apply_rms_prop( + var.handle, + rms.handle, + mom.handle, + lr_t, + rho, + momentum, + epsilon, + grad, + use_locking=self._use_locking) else: - return training_ops.resource_apply_rms_prop( - var.handle, - rms.handle, - mom.handle, - lr_t, - rho, - momentum, - epsilon, - grad, - use_locking=self._use_locking) + rms_t = rho * rms + (1. - rho) * math_ops.square(grad) + rms_t = state_ops.assign(rms, rms_t, use_locking=self._use_locking) + denom_t = rms_t + if self.centered: + mg = self.get_slot(var, "mg") + mg_t = rho * mg + (1. - rho) * grad + mg_t = state_ops.assign(mg, mg_t, use_locking=self._use_locking) + denom_t = rms_t - math_ops.square(mg_t) + var_t = var - lr_t * grad / (math_ops.sqrt(denom_t) + epsilon) + return state_ops.assign(var, var_t, use_locking=self._use_locking).op def _resource_apply_sparse(self, grad, var, indices): var_dtype = var.dtype.base_dtype lr_t = self._decayed_lr(var_dtype) rms = self.get_slot(var, "rms") - mom = self.get_slot(var, "momentum") rho = self._get_hyper("rho", var_dtype) momentum = self._get_hyper("momentum", var_dtype) epsilon = self._get_hyper("epsilon", var_dtype) - if self.centered: - mg = self.get_slot(var, "mg") - return training_ops.resource_sparse_apply_centered_rms_prop( - var.handle, - mg.handle, - rms.handle, - mom.handle, - lr_t, - rho, - momentum, - epsilon, - grad, - indices, - use_locking=self._use_locking) + if self._momentum: + mom = self.get_slot(var, "momentum") + if self.centered: + mg = self.get_slot(var, "mg") + return training_ops.resource_sparse_apply_centered_rms_prop( + var.handle, + mg.handle, + rms.handle, + mom.handle, + lr_t, + rho, + momentum, + epsilon, + grad, + indices, + use_locking=self._use_locking) + else: + return training_ops.resource_sparse_apply_rms_prop( + var.handle, + rms.handle, + mom.handle, + lr_t, + rho, + momentum, + epsilon, + grad, + indices, + use_locking=self._use_locking) else: - return training_ops.resource_sparse_apply_rms_prop( - var.handle, - rms.handle, - mom.handle, - lr_t, - rho, - momentum, - epsilon, - grad, - indices, - use_locking=self._use_locking) + rms_scaled_g_values = (grad * grad) * (1. - rho) + rms_t = state_ops.assign(rms, rms * rho, use_locking=self._use_locking) + with ops.control_dependencies([rms_t]): + rms_t = self._resource_scatter_add(rms, indices, rms_scaled_g_values) + rms_slice = array_ops.gather(rms_t, indices) + denom_slice = rms_slice + if self.centered: + mg = self.get_slot(var, "mg") + mg_scaled_g_values = grad * (1. - rho) + mg_t = state_ops.assign(mg, mg * rho, use_locking=self._use_locking) + with ops.control_dependencies([mg_t]): + mg_t = self._resource_scatter_add(mg, indices, mg_scaled_g_values) + mg_slice = array_ops.gather(mg_t, indices) + denom_slice = rms_slice - math_ops.square(mg_slice) + var_update = self._resource_scatter_add( + var, indices, -lr_t * grad / (math_ops.sqrt(denom_slice) + epsilon)) + if self.centered: + return control_flow_ops.group(*[var_update, rms_t, mg_t]) + return control_flow_ops.group(*[var_update, rms_t]) + + def set_weights(self, weights): + params = self.weights + # Override set_weights for backward compatibility of Keras V1 optimizer + # since it does not include iteration at head of the weight list. Set + # iteration to 0. + if len(params) == len(weights) + 1: + weights = [np.array(0)] + weights + super(RMSprop, self).set_weights(weights) def get_config(self): config = super(RMSprop, self).get_config() diff --git a/tensorflow/python/keras/optimizer_v2/rmsprop_test.py b/tensorflow/python/keras/optimizer_v2/rmsprop_test.py index 4d61cfbbc5..67662aae20 100644 --- a/tensorflow/python/keras/optimizer_v2/rmsprop_test.py +++ b/tensorflow/python/keras/optimizer_v2/rmsprop_test.py @@ -58,14 +58,18 @@ class RMSpropOptimizerTest(test.TestCase): def _rmsprop_update_numpy(self, var, g, mg, rms, mom, lr, rho, momentum, epsilon, centered): rms_t = rms * rho + (1 - rho) * g * g - denom_t = rms_t + epsilon if centered: mg_t = mg * rho + (1 - rho) * g - denom_t -= mg_t * mg_t + denom_t = rms_t - mg_t * mg_t else: mg_t = mg - mom_t = momentum * mom + lr * g / np.sqrt(denom_t, dtype=denom_t.dtype) - var_t = var - mom_t + denom_t = rms_t + if momentum > 0.: + mom_t = momentum * mom + lr * g / (np.sqrt(denom_t + epsilon)) + var_t = var - mom_t + else: + mom_t = mom + var_t = var - lr * g / (np.sqrt(denom_t) + epsilon) return var_t, mg_t, rms_t, mom_t def _sparse_rmsprop_update_numpy(self, var, gindexs, gvalues, mg, rms, mom, @@ -78,12 +82,18 @@ class RMSpropOptimizerTest(test.TestCase): gindex = gindexs[i] gvalue = gvalues[i] rms_t[gindex] = rms[gindex] * rho + (1 - rho) * gvalue * gvalue - denom_t = rms_t[gindex] + epsilon if centered: mg_t[gindex] = mg_t[gindex] * rho + (1 - rho) * gvalue - denom_t -= mg_t[gindex] * mg_t[gindex] - mom_t[gindex] = momentum * mom[gindex] + lr * gvalue / np.sqrt(denom_t) - var_t[gindex] = var[gindex] - mom_t[gindex] + denom_t = rms_t[gindex] - mg_t[gindex] * mg_t[gindex] + else: + denom_t = rms_t[gindex] + if momentum > 0.: + mom_t[gindex] = momentum * mom[gindex] + lr * gvalue / np.sqrt(denom_t + + epsilon) + var_t[gindex] = var[gindex] - mom_t[gindex] + else: + mom_t[gindex] = mom[gindex] + var_t[gindex] = var[gindex] - lr * gvalue / (np.sqrt(denom_t) + epsilon) return var_t, mg_t, rms_t, mom_t @test_util.run_deprecated_v1 @@ -117,14 +127,17 @@ class RMSpropOptimizerTest(test.TestCase): mg0 = None mg1 = None + if momentum > 0.: + mom0 = opt.get_slot(var0, "momentum") + mom1 = opt.get_slot(var1, "momentum") + else: + mom0 = None + mom1 = None + rms0 = opt.get_slot(var0, "rms") self.assertTrue(rms0 is not None) rms1 = opt.get_slot(var1, "rms") self.assertTrue(rms1 is not None) - mom0 = opt.get_slot(var0, "momentum") - self.assertTrue(mom0 is not None) - mom1 = opt.get_slot(var1, "momentum") - self.assertTrue(mom1 is not None) mg0_np = np.array([0.0, 0.0], dtype=dtype.as_numpy_dtype) mg1_np = np.array([0.0, 0.0], dtype=dtype.as_numpy_dtype) @@ -137,8 +150,8 @@ class RMSpropOptimizerTest(test.TestCase): self.assertAllClose([1.0, 2.0], self.evaluate(var0)) self.assertAllClose([3.0, 4.0], self.evaluate(var1)) - # Run 4 steps of RMSprop - for _ in range(1, 5): + # Run 3 steps of RMSprop + for _ in range(1, 4): self.evaluate(update) var0_np, mg0_np, rms0_np, mom0_np = self._rmsprop_update_numpy( @@ -152,10 +165,11 @@ class RMSpropOptimizerTest(test.TestCase): if centered: self.assertAllCloseAccordingToType(mg0_np, self.evaluate(mg0)) self.assertAllCloseAccordingToType(mg1_np, self.evaluate(mg1)) + if momentum > 0.: + self.assertAllCloseAccordingToType(mom0_np, self.evaluate(mom0)) + self.assertAllCloseAccordingToType(mom1_np, self.evaluate(mom1)) self.assertAllCloseAccordingToType(rms0_np, self.evaluate(rms0)) self.assertAllCloseAccordingToType(rms1_np, self.evaluate(rms1)) - self.assertAllCloseAccordingToType(mom0_np, self.evaluate(mom0)) - self.assertAllCloseAccordingToType(mom1_np, self.evaluate(mom1)) self.assertAllCloseAccordingToType(var0_np, self.evaluate(var0)) self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1)) @@ -191,10 +205,12 @@ class RMSpropOptimizerTest(test.TestCase): self.assertTrue(rms0 is not None) rms1 = opt.get_slot(var1, "rms") self.assertTrue(rms1 is not None) - mom0 = opt.get_slot(var0, "momentum") - self.assertTrue(mom0 is not None) - mom1 = opt.get_slot(var1, "momentum") - self.assertTrue(mom1 is not None) + if momentum > 0.: + mom0 = opt.get_slot(var0, "momentum") + mom1 = opt.get_slot(var1, "momentum") + else: + mom0 = None + mom1 = None mg0_np = np.array([0.0, 0.0]) mg1_np = np.array([0.0, 0.0]) @@ -222,8 +238,9 @@ class RMSpropOptimizerTest(test.TestCase): # Validate updated params self.assertAllCloseAccordingToType(rms0_np, self.evaluate(rms0)) self.assertAllCloseAccordingToType(rms1_np, self.evaluate(rms1)) - self.assertAllCloseAccordingToType(mom0_np, self.evaluate(mom0)) - self.assertAllCloseAccordingToType(mom1_np, self.evaluate(mom1)) + if momentum > 0.: + self.assertAllCloseAccordingToType(mom0_np, self.evaluate(mom0)) + self.assertAllCloseAccordingToType(mom1_np, self.evaluate(mom1)) self.assertAllCloseAccordingToType(var0_np, self.evaluate(var0)) self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1)) @@ -325,10 +342,12 @@ class RMSpropOptimizerTest(test.TestCase): self.assertTrue(rms0 is not None) rms1 = opt.get_slot(var1, "rms") self.assertTrue(rms1 is not None) - mom0 = opt.get_slot(var0, "momentum") - self.assertTrue(mom0 is not None) - mom1 = opt.get_slot(var1, "momentum") - self.assertTrue(mom1 is not None) + if momentum > 0.: + mom0 = opt.get_slot(var0, "momentum") + mom1 = opt.get_slot(var1, "momentum") + else: + mom0 = None + mom1 = None mg0_np = np.array([0.0, 0.0], dtype=dtype.as_numpy_dtype) mg1_np = np.array([0.0, 0.0], dtype=dtype.as_numpy_dtype) @@ -341,8 +360,8 @@ class RMSpropOptimizerTest(test.TestCase): self.assertAllClose([1.0, 2.0], self.evaluate(var0)) self.assertAllClose([3.0, 4.0], self.evaluate(var1)) - # Run 4 steps of RMSprop - for _ in range(1, 5): + # Run 3 steps of RMSprop + for _ in range(1, 4): self.evaluate(update) var0_np, mg0_np, rms0_np, mom0_np = self._sparse_rmsprop_update_numpy( @@ -358,8 +377,9 @@ class RMSpropOptimizerTest(test.TestCase): self.assertAllCloseAccordingToType(mg1_np, self.evaluate(mg1)) self.assertAllCloseAccordingToType(rms0_np, self.evaluate(rms0)) self.assertAllCloseAccordingToType(rms1_np, self.evaluate(rms1)) - self.assertAllCloseAccordingToType(mom0_np, self.evaluate(mom0)) - self.assertAllCloseAccordingToType(mom1_np, self.evaluate(mom1)) + if momentum > 0.: + self.assertAllCloseAccordingToType(mom0_np, self.evaluate(mom0)) + self.assertAllCloseAccordingToType(mom1_np, self.evaluate(mom1)) self.assertAllCloseAccordingToType(var0_np, self.evaluate(var0)) self.assertAllCloseAccordingToType(var1_np, self.evaluate(var1)) @@ -420,6 +440,32 @@ class RMSpropOptimizerTest(test.TestCase): opt_3 = rmsprop.RMSprop(learning_rate=0.1) self.assertEqual(opt_3.lr, 0.1) + def testSlotsUniqueEager(self): + with context.eager_mode(): + v1 = variables.Variable(1.) + v2 = variables.Variable(1.) + + opt = rmsprop.RMSprop(1., momentum=0., centered=False) + opt.minimize(lambda: v1 + v2, var_list=[v1, v2]) + # There should be iteration, and one unique slot variable for v1 and v2. + self.assertEqual(3, len(set(opt.variables()))) + self.assertEqual( + self.evaluate(opt.variables()[0]), self.evaluate(opt.iterations)) + + opt = rmsprop.RMSprop(learning_rate=1., momentum=0.2, centered=False) + opt.minimize(lambda: v1 + v2, var_list=[v1, v2]) + # There should be iteration, and two unique slot variables for v1 and v2. + self.assertEqual(5, len(set(opt.variables()))) + self.assertEqual( + self.evaluate(opt.variables()[0]), self.evaluate(opt.iterations)) + + opt = rmsprop.RMSprop(learning_rate=1., momentum=0.2, centered=True) + opt.minimize(lambda: v1 + v2, var_list=[v1, v2]) + # There should be iteration, and three unique slot variables for v1 and v2 + self.assertEqual(7, len(set(opt.variables()))) + self.assertEqual( + self.evaluate(opt.variables()[0]), self.evaluate(opt.iterations)) + if __name__ == "__main__": test.main() -- GitLab From 0ce305b6ce8560945be3a92eae1bcddd60af5e10 Mon Sep 17 00:00:00 2001 From: Peter Hawkins Date: Tue, 8 Jan 2019 14:53:40 -0800 Subject: [PATCH 509/622] [XLA:Python] Add Gather and Scatter to XLA Python bindings. Refactor handling of repeated fields in *DimensionNumbers SWIG rules. PiperOrigin-RevId: 228404715 --- .../xla/python/local_computation_builder.cc | 16 + .../xla/python/local_computation_builder.h | 9 + .../xla/python/local_computation_builder.i | 281 +++++++----------- tensorflow/compiler/xla/python/xla_client.py | 12 + .../compiler/xla/python/xla_client_test.py | 40 +++ 5 files changed, 183 insertions(+), 175 deletions(-) diff --git a/tensorflow/compiler/xla/python/local_computation_builder.cc b/tensorflow/compiler/xla/python/local_computation_builder.cc index b0f0e9e570..c153603105 100644 --- a/tensorflow/compiler/xla/python/local_computation_builder.cc +++ b/tensorflow/compiler/xla/python/local_computation_builder.cc @@ -927,6 +927,22 @@ LocalOp LocalComputationBuilder::TriangularSolve(const LocalOp& a, conjugate_a); } +LocalOp LocalComputationBuilder::Gather( + const LocalOp& input, const LocalOp& start_indices, + const GatherDimensionNumbers& dimension_numbers, + absl::Span slice_sizes) { + return xla::Gather(input.op(), start_indices.op(), dimension_numbers, + slice_sizes); +} + +LocalOp LocalComputationBuilder::Scatter( + const LocalOp& input, const LocalOp& scatter_indices, + const LocalOp& updates, const LocalComputation& update_computation, + const ScatterDimensionNumbers& dimension_numbers) { + return xla::Scatter(input.op(), scatter_indices.op(), updates.op(), + update_computation.computation(), dimension_numbers); +} + StatusOr LocalComputationBuilder::BuildConstantSubGraph( const LocalOp& operand) { TF_ASSIGN_OR_RETURN(XlaComputation computation, diff --git a/tensorflow/compiler/xla/python/local_computation_builder.h b/tensorflow/compiler/xla/python/local_computation_builder.h index 5e83415921..98759cf984 100644 --- a/tensorflow/compiler/xla/python/local_computation_builder.h +++ b/tensorflow/compiler/xla/python/local_computation_builder.h @@ -418,6 +418,15 @@ class LocalComputationBuilder { LocalOp TriangularSolve(const LocalOp& a, const LocalOp& b, bool left_side, bool lower, bool transpose_a, bool conjugate_a); + LocalOp Gather(const LocalOp& input, const LocalOp& start_indices, + const GatherDimensionNumbers& dimension_numbers, + absl::Span slice_sizes); + + LocalOp Scatter(const LocalOp& input, const LocalOp& scatter_indices, + const LocalOp& updates, + const LocalComputation& update_computation, + const ScatterDimensionNumbers& dimension_numbers); + StatusOr BuildConstantSubGraph(const LocalOp& operand); #define _FORWARD(method_name, return_sig, args_sig) \ diff --git a/tensorflow/compiler/xla/python/local_computation_builder.i b/tensorflow/compiler/xla/python/local_computation_builder.i index bf5d667c6a..cc96d84724 100644 --- a/tensorflow/compiler/xla/python/local_computation_builder.i +++ b/tensorflow/compiler/xla/python/local_computation_builder.i @@ -34,6 +34,8 @@ limitations under the License. // PaddingConfig proto <- corresponding Python proto // ConvolutionDimensionNumbers proto <- corresponding Python proto // DotDimensionNumbers proto <- corresponding Python proto +// GatherDimensionNumbers proto <- corresponding Python proto +// ScatterDimensionNumbers proto <- corresponding Python proto // // Arrows indicate whether a conversion only ever occurs in one // direction, or whether it is maintained bidirectionally. @@ -167,8 +169,41 @@ bool HandleStringAttribute(PyObject* o, return true; // Handled string attribute, ok! } +bool HandleRepeatedInt64Attribute( + PyObject* o, const char* attr_name, + tensorflow::protobuf::RepeatedField* field) { + PyObject* seq = PyObject_GetAttrString(o, attr_name); + if (!seq) { + return false; + } + + int length = PySequence_Size(seq); + if (length == -1) { + Py_DECREF(seq); + return false; + } + + for (int i = 0; i < length; ++i) { + PyObject* item = PySequence_GetItem(seq, i); + if (!item) { + Py_DECREF(seq); + return false; + } + const int64 dimension = numpy::PyIntOrPyLongToLong(item); + if (dimension == -1 && PyErr_Occurred()) { + Py_DECREF(item); + Py_DECREF(seq); + return false; + } + *field->Add() = dimension; + Py_DECREF(item); + } + Py_DECREF(seq); + return true; } -} + +} // namespace swig +} // namespace xla %} // Required to use PyArray_* functions. @@ -657,128 +692,27 @@ tensorflow::ImportNumpy(); %typemap(in) const DotDimensionNumbers& (DotDimensionNumbers dimension_numbers) { - int length; - - /* lhs_contracting_dimensions */ - PyObject* lhs_contracting_dimensions = PyObject_GetAttrString( - $input, "lhs_contracting_dimensions"); - if (!lhs_contracting_dimensions) { - SWIG_fail; - } - - length = PySequence_Size(lhs_contracting_dimensions); - if (length == -1) { - Py_DECREF(lhs_contracting_dimensions); - SWIG_fail; - } - - for (int i = 0; i < length; ++i) { - PyObject* item = PySequence_GetItem(lhs_contracting_dimensions, i); - if (!item) { - Py_DECREF(lhs_contracting_dimensions); - SWIG_fail; - } - const int64 dimension = numpy::PyIntOrPyLongToLong(item); - if (dimension == -1 && PyErr_Occurred()) { - Py_DECREF(item); - Py_DECREF(lhs_contracting_dimensions); - SWIG_fail; - } - dimension_numbers.add_lhs_contracting_dimensions(dimension); - Py_DECREF(item); - } - Py_DECREF(lhs_contracting_dimensions); - - /* rhs_contracting_dimensions */ - PyObject* rhs_contracting_dimensions = PyObject_GetAttrString( - $input, "rhs_contracting_dimensions"); - if (!lhs_contracting_dimensions) { + if (!HandleRepeatedInt64Attribute( + $input, "lhs_contracting_dimensions", + dimension_numbers.mutable_lhs_contracting_dimensions())) { SWIG_fail; } - - length = PySequence_Size(rhs_contracting_dimensions); - if (length == -1) { - Py_DECREF(rhs_contracting_dimensions); + if (!HandleRepeatedInt64Attribute( + $input, "rhs_contracting_dimensions", + dimension_numbers.mutable_rhs_contracting_dimensions())) { SWIG_fail; } - - for (int i = 0; i < length; ++i) { - PyObject* item = PySequence_GetItem(rhs_contracting_dimensions, i); - if (!item) { - Py_DECREF(rhs_contracting_dimensions); - SWIG_fail; - } - const int64 dimension = numpy::PyIntOrPyLongToLong(item); - if (dimension == -1 && PyErr_Occurred()) { - Py_DECREF(item); - Py_DECREF(rhs_contracting_dimensions); - SWIG_fail; - } - dimension_numbers.add_rhs_contracting_dimensions(dimension); - Py_DECREF(item); - } - Py_DECREF(rhs_contracting_dimensions); - - /* lhs_batch_dimensions */ - PyObject* lhs_batch_dimensions = PyObject_GetAttrString( - $input, "lhs_batch_dimensions"); - if (!lhs_batch_dimensions) { + if (!HandleRepeatedInt64Attribute( + $input, "lhs_batch_dimensions", + dimension_numbers.mutable_lhs_batch_dimensions())) { SWIG_fail; } - - length = PySequence_Size(lhs_batch_dimensions); - if (length == -1) { - Py_DECREF(lhs_batch_dimensions); - SWIG_fail; - } - - for (int i = 0; i < length; ++i) { - PyObject* item = PySequence_GetItem(lhs_batch_dimensions, i); - if (!item) { - Py_DECREF(lhs_batch_dimensions); - SWIG_fail; - } - const int64 dimension = numpy::PyIntOrPyLongToLong(item); - if (dimension == -1 && PyErr_Occurred()) { - Py_DECREF(item); - Py_DECREF(lhs_batch_dimensions); - SWIG_fail; - } - dimension_numbers.add_lhs_batch_dimensions(dimension); - Py_DECREF(item); - } - Py_DECREF(lhs_batch_dimensions); - - /* rhs_batch_dimensions */ - PyObject* rhs_batch_dimensions = PyObject_GetAttrString( - $input, "rhs_batch_dimensions"); - if (!rhs_batch_dimensions) { - SWIG_fail; - } - - length = PySequence_Size(rhs_batch_dimensions); - if (length == -1) { - Py_DECREF(rhs_batch_dimensions); + if (!HandleRepeatedInt64Attribute( + $input, "rhs_batch_dimensions", + dimension_numbers.mutable_rhs_batch_dimensions())) { SWIG_fail; } - for (int i = 0; i < length; ++i) { - PyObject* item = PySequence_GetItem(rhs_batch_dimensions, i); - if (!item) { - Py_DECREF(rhs_batch_dimensions); - SWIG_fail; - } - const int64 dimension = numpy::PyIntOrPyLongToLong(item); - if (dimension == -1 && PyErr_Occurred()) { - Py_DECREF(item); - Py_DECREF(rhs_batch_dimensions); - SWIG_fail; - } - dimension_numbers.add_rhs_batch_dimensions(dimension); - Py_DECREF(item); - } - Py_DECREF(rhs_batch_dimensions); - $1 = &dimension_numbers; } @@ -861,85 +795,80 @@ tensorflow::ImportNumpy(); dimension_numbers.set_kernel_input_feature_dimension(value); PyObject* o; - int length; - o = PyObject_GetAttrString($input, "input_spatial_dimensions"); - if (!o) { + if (!HandleRepeatedInt64Attribute( + $input, "input_spatial_dimensions", + dimension_numbers.mutable_input_spatial_dimensions())) { SWIG_fail; } - length = PySequence_Size(o); - if (length == -1) { - Py_DECREF(o); + if (!HandleRepeatedInt64Attribute( + $input, "kernel_spatial_dimensions", + dimension_numbers.mutable_kernel_spatial_dimensions())) { SWIG_fail; } - for (int i = 0; i < length; ++i) { - PyObject* item = PySequence_GetItem(o, i); - if (!item) { - Py_DECREF(o); - SWIG_fail; - } - const int64 dimension = numpy::PyIntOrPyLongToLong(item); - if (dimension == -1 && PyErr_Occurred()) { - Py_DECREF(item); - Py_DECREF(o); - SWIG_fail; - } - dimension_numbers.add_input_spatial_dimensions(dimension); - Py_DECREF(item); + if (!HandleRepeatedInt64Attribute( + $input, "output_spatial_dimensions", + dimension_numbers.mutable_output_spatial_dimensions())) { + SWIG_fail; } - Py_DECREF(o); - o = PyObject_GetAttrString($input, "kernel_spatial_dimensions"); - if (!o) { + $1 = &dimension_numbers; +} + +// GatherDimensionNumbers + +%typemap(in) const GatherDimensionNumbers& + (GatherDimensionNumbers dimension_numbers) { + if (!HandleRepeatedInt64Attribute( + $input, "offset_dims", + dimension_numbers.mutable_offset_dims())) { SWIG_fail; } - length = PySequence_Size(o); - if (length == -1) { - Py_DECREF(o); + if (!HandleRepeatedInt64Attribute( + $input, "collapsed_slice_dims", + dimension_numbers.mutable_collapsed_slice_dims())) { SWIG_fail; } - for (int i = 0; i < length; ++i) { - PyObject* item = PySequence_GetItem(o, i); - if (!item) { - Py_DECREF(o); - SWIG_fail; - } - const int64 dimension = numpy::PyIntOrPyLongToLong(item); - if (dimension == -1 && PyErr_Occurred()) { - Py_DECREF(item); - Py_DECREF(o); - SWIG_fail; - } - dimension_numbers.add_kernel_spatial_dimensions(dimension); - Py_DECREF(item); + if (!HandleRepeatedInt64Attribute( + $input, "start_index_map", + dimension_numbers.mutable_start_index_map())) { + SWIG_fail; } - Py_DECREF(o); - o = PyObject_GetAttrString($input, "output_spatial_dimensions"); - if (!o) { + int64 value; + if (!GetIntAttr($input, "index_vector_dim", &value)) { SWIG_fail; } - length = PySequence_Size(o); - if (length == -1) { - Py_DECREF(o); + dimension_numbers.set_index_vector_dim(value); + + $1 = &dimension_numbers; +} + +// ScatterDimensionNumbers + +%typemap(in) const ScatterDimensionNumbers& + (ScatterDimensionNumbers dimension_numbers) { + if (!HandleRepeatedInt64Attribute( + $input, "update_window_dims", + dimension_numbers.mutable_update_window_dims())) { SWIG_fail; } - for (int i = 0; i < length; ++i) { - PyObject* item = PySequence_GetItem(o, i); - if (!item) { - Py_DECREF(o); - SWIG_fail; - } - const int64 dimension = numpy::PyIntOrPyLongToLong(item); - if (dimension == -1 && PyErr_Occurred()) { - Py_DECREF(item); - Py_DECREF(o); - SWIG_fail; - } - dimension_numbers.add_output_spatial_dimensions(dimension); - Py_DECREF(item); + if (!HandleRepeatedInt64Attribute( + $input, "inserted_window_dims", + dimension_numbers.mutable_inserted_window_dims())) { + SWIG_fail; + } + if (!HandleRepeatedInt64Attribute( + $input, "scatter_dims_to_operand_dims", + dimension_numbers.mutable_scatter_dims_to_operand_dims())) { + SWIG_fail; + } + + int64 value; + if (!GetIntAttr($input, "index_vector_dim", &value)) { + SWIG_fail; } - Py_DECREF(o); + dimension_numbers.set_index_vector_dim(value); $1 = &dimension_numbers; } @@ -1151,6 +1080,8 @@ tensorflow::ImportNumpy(); %unignore xla::swig::LocalComputationBuilder::QR; %unignore xla::swig::LocalComputationBuilder::TriangularSolve; %unignore xla::swig::LocalComputationBuilder::CustomCall; +%unignore xla::swig::LocalComputationBuilder::Gather; +%unignore xla::swig::LocalComputationBuilder::Scatter; %unignore xla::swig::DeleteLocalComputation; %unignore xla::swig::DestructureLocalShapedBufferTuple; %unignore xla::swig::DestructureXrtAllocationTuple; diff --git a/tensorflow/compiler/xla/python/xla_client.py b/tensorflow/compiler/xla/python/xla_client.py index 378bbdcb17..4e71121c09 100644 --- a/tensorflow/compiler/xla/python/xla_client.py +++ b/tensorflow/compiler/xla/python/xla_client.py @@ -1477,6 +1477,18 @@ class ComputationBuilder(object): return self._client.TriangularSolve( a, b, left_side, lower, transpose_a, conjugate_a) + def Gather(self, a, start_indices, dimension_numbers, slice_sizes): + """Enqueues a Gather operation onto the computation.""" + return self._client.Gather(a, start_indices, dimension_numbers, + slice_sizes) + + def Scatter(self, a, scatter_indices, updates, update_computation, + dimension_numbers): + """Enqueues a Scatter operation onto the computation.""" + return self._client.Scatter( + a, scatter_indices, updates, update_computation.computation, + dimension_numbers,) + def _forward_methods_to_local_builder(): """Forward remaining ComputationBuilder methods to the C API. diff --git a/tensorflow/compiler/xla/python/xla_client_test.py b/tensorflow/compiler/xla/python/xla_client_test.py index 002a20e60a..874e087eb6 100644 --- a/tensorflow/compiler/xla/python/xla_client_test.py +++ b/tensorflow/compiler/xla/python/xla_client_test.py @@ -1129,6 +1129,21 @@ class SingleOpTest(LocalComputationTest): self.assertFalse(c.IsConstant(non_const_expr)) # self.assertTrue(c.IsConstant(c.Sub(c.Add(x, a), x))) # TODO(b/77245564) + def testGather(self): + a = np.arange(9).astype(np.int32).reshape((3, 3)) + indices = np.array([[[0, 2], [2, 1]], [[1, 2], [2, 0]]], dtype=np.int32) + dnums = xla_client.xla_data_pb2.GatherDimensionNumbers() + dnums.offset_dims.append(1) + dnums.offset_dims.append(2) + dnums.start_index_map.append(0) + dnums.start_index_map.append(1) + dnums.index_vector_dim = 2 + c = self._NewComputation() + c.Gather(c.Constant(a), c.Constant(indices), dnums, slice_sizes=[1, 1]) + g = self._Execute(c, ()) + expected = np.array([[[[2, 7]]], [[[5, 6]]]], dtype=np.int32) + np.testing.assert_allclose(g, expected, rtol=1e-4) + class EmbeddedComputationsTest(LocalComputationTest): """Tests for XLA graphs with embedded computations (such as maps).""" @@ -1186,6 +1201,14 @@ class EmbeddedComputationsTest(LocalComputationTest): c.Mul(c.ParameterFromNumpy(NumpyArrayF64(0)), c.ConstantF64Scalar(2.0)) return c.Build() + def _CreateBinaryAddS32Computation(self): + """Computation (s32, s32) -> s32 that adds its two parameters.""" + c = self._NewComputation("add_param0_by_param1") + c.Add( + c.ParameterFromNumpy(NumpyArrayS32(0)), + c.ParameterFromNumpy(NumpyArrayS32(0))) + return c.Build() + def _CreateBinaryAddF32Computation(self): """Computation (f32, f32) -> f32 that adds its two parameters.""" c = self._NewComputation("add_param0_by_param1") @@ -1568,6 +1591,23 @@ class EmbeddedComputationsTest(LocalComputationTest): execution.join() self.assertEqual(want, got) + def testScatter(self): + a = np.arange(9).astype(np.int32).reshape((3, 3)) + scatter_indices = np.array([0, 2], dtype=np.int32) + updates = np.array([[10, 20, 30], [70, 80, 90]], dtype=np.int32) + + dnums = xla_client.xla_data_pb2.ScatterDimensionNumbers() + dnums.update_window_dims.append(1) + dnums.inserted_window_dims.append(0) + dnums.scatter_dims_to_operand_dims.append(0) + dnums.index_vector_dim = 1 + + c = self._NewComputation() + c.Scatter(c.Constant(a), c.Constant(scatter_indices), c.Constant(updates), + self._CreateBinaryAddS32Computation(), dnums) + expected = np.array([[10, 21, 32], [3, 4, 5], [76, 87, 98]], dtype=np.int32) + self._ExecuteAndCompareClose(c, expected=expected) + class ErrorTest(LocalComputationTest): -- GitLab From 1ad09edf03a1c4b96eff12a7d6c765a430603b7d Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Tue, 8 Jan 2019 14:55:09 -0800 Subject: [PATCH 510/622] Use nsync, version 1.20.2, which has a bazel BUILD bug fix for aarch64 systems. It requires bazel version 0.16.0 or higher, but TensorFlow already relies on higher versions than that. PiperOrigin-RevId: 228404987 --- tensorflow/contrib/cmake/external/nsync.cmake | 2 +- tensorflow/workspace.bzl | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tensorflow/contrib/cmake/external/nsync.cmake b/tensorflow/contrib/cmake/external/nsync.cmake index 479609458c..b15143bfc1 100644 --- a/tensorflow/contrib/cmake/external/nsync.cmake +++ b/tensorflow/contrib/cmake/external/nsync.cmake @@ -16,7 +16,7 @@ include (ExternalProject) set(nsync_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/external/nsync/public) set(nsync_URL https://github.com/google/nsync) -set(nsync_TAG 1.20.1) +set(nsync_TAG 1.20.2) set(nsync_BUILD ${CMAKE_CURRENT_BINARY_DIR}/nsync/src/nsync) set(nsync_INSTALL ${CMAKE_CURRENT_BINARY_DIR}/nsync/install) diff --git a/tensorflow/workspace.bzl b/tensorflow/workspace.bzl index ae26ba6e68..5b0f6d4c84 100755 --- a/tensorflow/workspace.bzl +++ b/tensorflow/workspace.bzl @@ -395,12 +395,12 @@ def tf_workspace(path_prefix = "", tf_repo_name = ""): tf_http_archive( name = "nsync", - sha256 = "692f9b30e219f71a6371b98edd39cef3cbda35ac3abc4cd99ce19db430a5591a", - strip_prefix = "nsync-1.20.1", + sha256 = "704be7f58afa47b99476bbac7aafd1a9db4357cef519db361716f13538547ffd", + strip_prefix = "nsync-1.20.2", system_build_file = clean_dep("//third_party/systemlibs:nsync.BUILD"), urls = [ - "https://mirror.bazel.build/github.com/google/nsync/archive/1.20.1.tar.gz", - "https://github.com/google/nsync/archive/1.20.1.tar.gz", + "https://mirror.bazel.build/github.com/google/nsync/archive/1.20.2.tar.gz", + "https://github.com/google/nsync/archive/1.20.2.tar.gz", ], ) -- GitLab From 9f0ab258f7d715171f878080e6ed5c880aea004c Mon Sep 17 00:00:00 2001 From: Mihai Maruseac Date: Tue, 8 Jan 2019 15:17:41 -0800 Subject: [PATCH 511/622] Prevent OOM in decoding GIF images. PiperOrigin-RevId: 228409275 --- tensorflow/core/lib/gif/gif_io.cc | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tensorflow/core/lib/gif/gif_io.cc b/tensorflow/core/lib/gif/gif_io.cc index 9a5215320f..3fad8c8b14 100644 --- a/tensorflow/core/lib/gif/gif_io.cc +++ b/tensorflow/core/lib/gif/gif_io.cc @@ -82,9 +82,20 @@ uint8* Decode(const void* srcdata, int datasize, return nullptr; } + // Don't request more memory than needed for each frame, preventing OOM + int max_frame_width = 0; + int max_frame_height = 0; + for (int k = 0; k < gif_file->ImageCount; k++) { + SavedImage* si = &gif_file->SavedImages[k]; + if (max_frame_height < si->ImageDesc.Height) + max_frame_height = si->ImageDesc.Height; + if (max_frame_width < si->ImageDesc.Width) + max_frame_width = si->ImageDesc.Width; + } + const int num_frames = gif_file->ImageCount; - const int width = gif_file->SWidth; - const int height = gif_file->SHeight; + const int width = max_frame_width; // gif_file->SWidth; + const int height = max_frame_height; // gif_file->SHeight; const int channel = 3; uint8* const dstdata = allocate_output(num_frames, width, height, channel); -- GitLab From bef89aba9c73880ed0db8109e2d029b8e65cb367 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Tue, 8 Jan 2019 15:17:50 -0800 Subject: [PATCH 512/622] Improves Flex performance. - Avoid recreating the EagerOperation every time - Avoid buffer_map churn by keeping the inputs and outputs along with the EagerOperation in OpNode. PiperOrigin-RevId: 228409298 --- tensorflow/lite/delegates/flex/kernel.cc | 179 +++++++++++++----- tensorflow/lite/delegates/flex/kernel_test.cc | 7 +- 2 files changed, 138 insertions(+), 48 deletions(-) diff --git a/tensorflow/lite/delegates/flex/kernel.cc b/tensorflow/lite/delegates/flex/kernel.cc index 84745ba9cb..d2853cf9f8 100644 --- a/tensorflow/lite/delegates/flex/kernel.cc +++ b/tensorflow/lite/delegates/flex/kernel.cc @@ -51,6 +51,15 @@ namespace tflite { namespace flex { namespace kernel { +struct OpNode; + +// Represents the origin of a given tensor as a reference to the output +// of an upstream node. +struct TensorSource { + OpNode* node; + int node_output_index; +}; + // A list of inputs of a given node of the TensorFlow/Eager graph. class OpInputs { public: @@ -65,8 +74,26 @@ class OpInputs { int TfLiteIndex(int i) const { return inputs_[i]; } + // Given a map relating tensors to the node that originates them, populate a + // list of sources for the tensors in this class. + void InitializeTensorSources( + const std::map& tflite_tensor_sources) { + sources_.clear(); + for (int i : inputs_) { + auto it = tflite_tensor_sources.find(i); + if (it == tflite_tensor_sources.end()) { + sources_.push_back({nullptr, 0}); + } else { + sources_.push_back(it->second); + } + } + } + + TensorSource GetTensorSource(int i) const { return sources_[i]; } + private: std::vector inputs_; + std::vector sources_; }; // A list of outputs of a given node of the TensorFlow/Eager graph, along with @@ -81,6 +108,33 @@ class OpOutputs { } ~OpOutputs() { ResetTensorHandles(); } + // Stores information about which of the tensors in this class are also + // outputs of the sugbraph. + void InitializeGraphOutputs(const std::set& subgraph_outputs) { + subgraph_outputs_.clear(); + for (int i : outputs_) { + subgraph_outputs_.push_back(subgraph_outputs.count(i) > 0); + } + } + + // Returns true if the tensor given by index 'i' is an output of the entire + // subgraph. + bool IsSubgraphOutput(int i) const { return subgraph_outputs_[i]; } + + // Returns a handle to a given tensor and, optionally, remove it from the + // internal vector. + tensorflow::TensorHandle* GetHandle(int i, bool remove) { + auto* handle = vector_[i]; + if (!remove) { + handle->Ref(); + } else { + // Don't increase the ref-count. Instead, simply take it out of the + // vector. + vector_[i] = nullptr; + } + return handle; + } + int Size() const { return outputs_.size(); } int TfLiteIndex(int i) const { return outputs_[i]; } @@ -102,6 +156,7 @@ class OpOutputs { private: std::vector outputs_; + std::vector subgraph_outputs_; tensorflow::gtl::InlinedVector vector_; }; @@ -111,7 +166,9 @@ class OpNode { public: OpNode(const TfLiteIntArray* inputs, const TfLiteIntArray* outputs) : inputs_(inputs), outputs_(outputs) {} - ~OpNode() {} + ~OpNode() { + if (op_) ClearEagerInputs(); + } const string& name() const { return name_; } void set_name(const string& name) { name_ = name; } @@ -130,6 +187,8 @@ class OpNode { int NumInputs() const { return inputs_.Size(); } int NumOutputs() const { return outputs_.Size(); } + tensorflow::EagerOperation* op() { return op_.get(); } + tensorflow::Status InitializeNodeDef(const void* custom_initial_data, int custom_initial_data_size) { if (!custom_initial_data) { @@ -162,10 +221,8 @@ class OpNode { // Build thew new EagerOperation. In case of error, the returned 'op' is // guaranteed to be 'nullptr'. - tensorflow::Status BuildEagerOp( - tensorflow::EagerContext* eager_context, - std::unique_ptr* op) { - op->reset(); + tensorflow::Status BuildEagerOp(tensorflow::EagerContext* eager_context) { + op_.reset(); const tensorflow::AttrTypeMap* attr_types; bool is_function = false; @@ -179,33 +236,47 @@ class OpNode { "')"); } - op->reset(new tensorflow::EagerOperation(eager_context, name_.c_str(), + op_.reset(new tensorflow::EagerOperation(eager_context, name_.c_str(), /*is_function=*/false, attr_types)); + + op_->MutableAttrs()->NumInputs(inputs_.Size()); for (const auto& attr : nodedef_.attr()) { - (*op)->MutableAttrs()->Set(attr.first, attr.second); + op_->MutableAttrs()->Set(attr.first, attr.second); } return tensorflow::Status::OK(); } - tensorflow::Status BuildEagerInputs(BufferMap* buffer_map, - tensorflow::EagerOperation* op) { + void ClearEagerInputs() { + for (tensorflow::TensorHandle* h : *op_->MutableInputs()) { + if (h) h->Unref(); + } + op_->MutableInputs()->clear(); + } + + tensorflow::Status BuildEagerInputs(const BufferMap* buffer_map) { for (int i = 0; i < inputs_.Size(); ++i) { int input_index = inputs_.TfLiteIndex(i); - if (!buffer_map->HasTensor(input_index)) { - return tensorflow::errors::Internal( - "Cannot read from invalid tensor index ", input_index); - } - auto* handle = new tensorflow::TensorHandle( - buffer_map->GetTensor(input_index), nullptr, nullptr, nullptr); - op->AddInput(handle); - handle->Unref(); - - if (buffer_map->IsForwardable(input_index)) { - // Take it out of the map, so Eager/TF can reuse the buffer for an - // output tensor of the op. - buffer_map->RemoveTensor(input_index); + TensorSource s = inputs_.GetTensorSource(i); + if (!s.node) { + // This input is not produced by this Eager subgraph (it could be a TF + // Lite native buffer, or could be produced by a separater subgraph). We + // need to fetch it from the delegate's buffer_map. + if (!buffer_map->HasTensor(input_index)) { + return tensorflow::errors::Internal( + "Cannot read from invalid tensor index ", input_index); + } + auto* handle = new tensorflow::TensorHandle( + buffer_map->GetTensor(input_index), nullptr, nullptr, nullptr); + op_->MutableInputs()->push_back(handle); + } else { + // If this is a forwardable tensor, we will remove it from the previous + // op's list, giving TF the opportunity to reuse its buffer. + bool unref_handle = buffer_map->IsForwardable(input_index); + auto* handle = + s.node->outputs_.GetHandle(s.node_output_index, unref_handle); + op_->MutableInputs()->push_back(handle); } } return tensorflow::Status::OK(); @@ -214,11 +285,12 @@ class OpNode { tensorflow::Status PersistEagerOutputs(BufferMap* buffer_map) { auto* handles = outputs_.GetTensorHandles(); for (int i = 0; i < outputs_.Size(); ++i) { - const tensorflow::Tensor* tensor = nullptr; - TF_RETURN_IF_ERROR(handles->at(i)->Tensor(&tensor)); - buffer_map->SetFromTensorFlow(outputs_.TfLiteIndex(i), *tensor); + if (outputs_.IsSubgraphOutput(i)) { + const tensorflow::Tensor* tensor = nullptr; + TF_RETURN_IF_ERROR(handles->at(i)->Tensor(&tensor)); + buffer_map->SetFromTensorFlow(outputs_.TfLiteIndex(i), *tensor); + } } - outputs_.ResetTensorHandles(); return tensorflow::Status::OK(); } @@ -236,19 +308,23 @@ class OpNode { OpInputs inputs_; // List of outputs, as TF Lite tensor indices. OpOutputs outputs_; + + std::unique_ptr op_; }; // Executes the TensorFlow op given by 'op_name', with the attributes specified // in 'nodedef'. Inputs and outputs are given as indices into the 'buffer_map'. -tensorflow::Status ExecuteFlexOp(tensorflow::EagerContext* eager_context, - BufferMap* buffer_map, OpNode* node_data) { - std::unique_ptr op; - TF_RETURN_IF_ERROR(node_data->BuildEagerOp(eager_context, &op)); - TF_RETURN_IF_ERROR(node_data->BuildEagerInputs(buffer_map, op.get())); +tensorflow::Status ExecuteFlexOp(TfLiteContext* context, BufferMap* buffer_map, + OpNode* node_data) { + TF_RETURN_WITH_CONTEXT_IF_ERROR(node_data->BuildEagerInputs(buffer_map), + " (while executing '", node_data->name(), + "' via Eager)"); + node_data->mutable_outputs()->ResetTensorHandles(); int num_retvals = node_data->NumOutputs(); TF_RETURN_WITH_CONTEXT_IF_ERROR( - EagerExecute(op.get(), node_data->mutable_outputs()->GetTensorHandles(), + EagerExecute(node_data->op(), + node_data->mutable_outputs()->GetTensorHandles(), &num_retvals), " (while executing '", node_data->name(), "' via Eager)"); @@ -259,6 +335,8 @@ tensorflow::Status ExecuteFlexOp(tensorflow::EagerContext* eager_context, TF_RETURN_IF_ERROR(node_data->PersistEagerOutputs(buffer_map)); + node_data->ClearEagerInputs(); + return tensorflow::Status::OK(); } @@ -286,8 +364,10 @@ void* Init(TfLiteContext* context, const char* buffer, size_t length) { ->GetBufferMap(context); CHECK(params->output_tensors); + std::set output_set; for (auto tensor_index : TfLiteIntArrayView(params->output_tensors)) { op_data->subgraph_outputs.push_back(tensor_index); + output_set.insert(tensor_index); } CHECK(params->input_tensors); @@ -313,6 +393,8 @@ void* Init(TfLiteContext* context, const char* buffer, size_t length) { status = node_data.InitializeNodeDef(node->custom_initial_data, node->custom_initial_data_size); if (!status.ok()) break; + status = node_data.BuildEagerOp(op_data->eager_context); + if (!status.ok()) break; } if (ConvertStatus(context, status) != kTfLiteOk) { @@ -322,6 +404,26 @@ void* Init(TfLiteContext* context, const char* buffer, size_t length) { return op_data; } + // Given a TfLite tensor index, return the OpNode that produces it, + // along with it index into that OpNodes list of outputs. + std::map tflite_tensor_sources; + + // Find out how each tensor is produced. This does not account for + // tensors that are not produce by eager ops. + for (auto& node_data : op_data->nodes) { + node_data->mutable_outputs()->InitializeGraphOutputs(output_set); + for (int i = 0; i < node_data->outputs().Size(); ++i) { + int output_index = node_data->outputs().TfLiteIndex(i); + tflite_tensor_sources[output_index] = TensorSource{node_data.get(), i}; + } + } + + // For each node, resolve the inputs, so we can keep pointers to the nodes + // that produces them. + for (auto& node_data : op_data->nodes) { + node_data->mutable_inputs()->InitializeTensorSources(tflite_tensor_sources); + } + return op_data; } @@ -370,6 +472,7 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { node_data->name().c_str()); return kTfLiteError; } + TF_LITE_ENSURE(context, node_data->op()); for (int i = 0; i < node_data->inputs().Size(); ++i) { ++tensor_ref_count[node_data->inputs().TfLiteIndex(i)]; @@ -392,7 +495,6 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { auto* op_data = reinterpret_cast(node->user_data); BufferMap* buffer_map = op_data->buffer_map; - tensorflow::EagerContext* eager_context = op_data->eager_context; // Insert a tensor in the buffer map for all inputs that are not constant. // Constants were handled in Prepare() already. @@ -414,7 +516,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { reinterpret_cast(context->profiler), node_data->name().c_str(), node_data->index()); - auto status = ExecuteFlexOp(eager_context, buffer_map, node_data.get()); + auto status = ExecuteFlexOp(context, buffer_map, node_data.get()); TF_LITE_ENSURE_OK(context, ConvertStatus(context, status)); } @@ -433,15 +535,6 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { tensor->data_is_stale = true; } - // We don't need to keep track of internal TF tensors any longer, so take - // them out of the buffer_map, but make sure we keep all the ones we might - // need for other subgraphs, or as final output of inference. - const auto& outputs = op_data->subgraph_outputs; - std::set keep(outputs.begin(), outputs.end()); - const auto& inputs = op_data->subgraph_inputs; - keep.insert(inputs.begin(), inputs.end()); - buffer_map->RemoveTensorsNotInSet(keep); - return kTfLiteOk; } diff --git a/tensorflow/lite/delegates/flex/kernel_test.cc b/tensorflow/lite/delegates/flex/kernel_test.cc index cef8017e6e..5b3a6d1647 100644 --- a/tensorflow/lite/delegates/flex/kernel_test.cc +++ b/tensorflow/lite/delegates/flex/kernel_test.cc @@ -144,12 +144,9 @@ TEST_F(KernelTest, BadTensorFlowOp) { return GenericPrepare(context, delegate, {0}); }); - SetShape(0, {2, 2, 1}); - SetValues(0, {1.1f, 2.2f, 3.3f, 4.4f}); - - ASSERT_FALSE(Invoke()); + ASSERT_NE(interpreter_->AllocateTensors(), kTfLiteOk); ASSERT_THAT(error_reporter().error_messages(), - ContainsRegex("while processing attributes of 'NonExistentOp'")); + ContainsRegex("Op type not registered 'NonExistentOp'")); } TEST_F(KernelTest, BadNumberOfOutputs) { -- GitLab From 2c6316172f2c17929d34aa64df45f08210f08a83 Mon Sep 17 00:00:00 2001 From: Sourabh Bajaj Date: Tue, 8 Jan 2019 15:29:53 -0800 Subject: [PATCH 513/622] Add a new experimental initialize method only to TPU strategy PiperOrigin-RevId: 228411389 --- .../contrib/distribute/python/tpu_strategy.py | 8 ++++++++ tensorflow/python/training/session_manager.py | 15 +++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/tensorflow/contrib/distribute/python/tpu_strategy.py b/tensorflow/contrib/distribute/python/tpu_strategy.py index 9cb2e461ab..f498f58b25 100644 --- a/tensorflow/contrib/distribute/python/tpu_strategy.py +++ b/tensorflow/contrib/distribute/python/tpu_strategy.py @@ -455,6 +455,14 @@ class TPUExtended(distribute_lib.DistributionStrategyExtended): with _TPUReplicaContext(self._container_strategy()): return fn(*args, **kwargs) + def _experimental_initialize_system(self): + """Experimental method added to be used by Estimator. + + This is a private method only to be used by Estimator. Other frameworks + should directly be calling `tf.contrib.distribute.initialize_tpu_system` + """ + initialize_tpu_system(self._tpu_cluster_resolver) + def _initialize(self): if context.executing_eagerly(): # TODO(priyag): Add appopriate call here when eager is supported for TPUs. diff --git a/tensorflow/python/training/session_manager.py b/tensorflow/python/training/session_manager.py index 1ec9639177..cd22e7e7de 100644 --- a/tensorflow/python/training/session_manager.py +++ b/tensorflow/python/training/session_manager.py @@ -182,13 +182,16 @@ class SessionManager(object): set. """ self._target = master + + # This is required to so that we initialize the TPU device before + # restoring from checkpoint since we'll be placing variables on the device + # and TPUInitialize wipes out the memory of the device. + strategy = distribution_strategy_context.get_distribution_strategy() + if strategy and hasattr(strategy.extended, + "_experimental_initialize_system"): + strategy.extended._experimental_initialize_system() # pylint: disable=protected-access + sess = session.Session(self._target, graph=self._graph, config=config) - # TODO(jhseu): Delete once tpu.initialize_system() goes away. - initialize_ops = ( - distribution_strategy_context.get_distribution_strategy().initialize() - ) - if initialize_ops: - sess.run(initialize_ops) if checkpoint_dir and checkpoint_filename_with_path: raise ValueError("Can not provide both checkpoint_dir and " "checkpoint_filename_with_path.") -- GitLab From d4985a621d8e71b128d34fec2a0260109bb4fb7d Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Tue, 8 Jan 2019 15:32:32 -0800 Subject: [PATCH 514/622] This CL isolates collections between instances of wrap_function calls. PiperOrigin-RevId: 228411886 --- tensorflow/python/eager/wrap_function.py | 3 +- tensorflow/python/eager/wrap_function_test.py | 39 +++++++++++++++++++ tensorflow/python/framework/func_graph.py | 34 ++++++++++------ tensorflow/python/ops/cond_v2.py | 7 ++-- tensorflow/python/ops/while_v2.py | 12 +++--- 5 files changed, 74 insertions(+), 21 deletions(-) diff --git a/tensorflow/python/eager/wrap_function.py b/tensorflow/python/eager/wrap_function.py index 2c65e97871..6f978feb3c 100644 --- a/tensorflow/python/eager/wrap_function.py +++ b/tensorflow/python/eager/wrap_function.py @@ -197,6 +197,7 @@ def wrap_function(fn, signature, name=None): name, holder, args=None, kwargs=None, signature=signature, - add_control_dependencies=False), + add_control_dependencies=False, + collections={}), variable_holder=holder, signature=signature) diff --git a/tensorflow/python/eager/wrap_function_test.py b/tensorflow/python/eager/wrap_function_test.py index 17e204b4d8..a6e1931fcd 100644 --- a/tensorflow/python/eager/wrap_function_test.py +++ b/tensorflow/python/eager/wrap_function_test.py @@ -104,6 +104,45 @@ class WrapFunctionTest(test.TestCase): fetches=f_wrapped.graph.get_tensor_by_name('fetch:0')) self.assertAllEqual(6.0, pruned()) + def testCollectionsIsolation(self): + + v1 = variables.Variable(2.) + v2_holder = [] + def f(): + v2 = variables.Variable(3.) + v2_holder.append(v2) + ops.add_to_collection(ops.GraphKeys.LOSSES, v2 * constant_op.constant(3.)) + return array_ops.identity(v1 * v2 * constant_op.constant(1.), 'fetch') + + f_wrapped = wrap_function.wrap_function(f, []) + self.assertAllEqual(6.0, f_wrapped()) + self.assertEqual( + len(f_wrapped.graph.get_collection(ops.GraphKeys.LOSSES)), 1) + f_var_collection = f_wrapped.graph.get_collection( + ops.GraphKeys.TRAINABLE_VARIABLES) + self.assertEqual(len(f_var_collection), 1) + self.assertIs(f_var_collection[0], v2_holder[0]) + + v3_holder = [] + def g(): + v3 = variables.Variable(4.) + v3_holder.append(v3) + ops.add_to_collection(ops.GraphKeys.LOSSES, v3 * constant_op.constant(3.)) + return array_ops.identity(v1 * v3 * constant_op.constant(1.), 'fetch') + + g_wrapped = wrap_function.wrap_function(g, []) + self.assertAllEqual(8.0, g_wrapped()) + self.assertEqual( + len(g_wrapped.graph.get_collection(ops.GraphKeys.LOSSES)), 1) + g_var_collection = g_wrapped.graph.get_collection( + ops.GraphKeys.TRAINABLE_VARIABLES) + self.assertEqual(len(g_var_collection), 1) + self.assertIs(g_var_collection[0], v3_holder[0]) + + # Both have only one value, and their values aren't equal. So no sharing. + self.assertNotEqual(g_wrapped.graph.get_collection(ops.GraphKeys.LOSSES), + f_wrapped.graph.get_collection(ops.GraphKeys.LOSSES)) + def testGradientsOfPrune(self): v1 = variables.Variable(2.) diff --git a/tensorflow/python/framework/func_graph.py b/tensorflow/python/framework/func_graph.py index 9528a24b46..9603c1536d 100644 --- a/tensorflow/python/framework/func_graph.py +++ b/tensorflow/python/framework/func_graph.py @@ -18,7 +18,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -import collections +import collections as py_collections import weakref from tensorflow.core.framework import attr_value_pb2 @@ -80,7 +80,7 @@ class FuncGraph(ops.Graph): seed: The graph-level random seed. """ - def __init__(self, name, read_only_collections=True): + def __init__(self, name, collections=None): """Construct a new FuncGraph. The graph will inherit its graph key, collections, seed, and distribution @@ -88,8 +88,13 @@ class FuncGraph(ops.Graph): Args: name: the name of the function. - read_only_collections: whether to not write function graph collections - back to default graph. Defaults to True. + collections: a dictionary of collections this FuncGraph should start + with. If not specified (None), the FuncGraph will read (but not write + to) the outer graph's collections that are not whitelisted, and both + read and write to the outer graph's collections that are whitelisted. + The current whitelisted collections are the global variables, the + local variables, and the trainable variables. + Defaults to None. """ super(FuncGraph, self).__init__() @@ -97,10 +102,9 @@ class FuncGraph(ops.Graph): self.inputs = [] self.outputs = [] self.structured_outputs = None - self._read_only_collections = read_only_collections self._weak_variables = [] self.outer_graph = ops.get_default_graph() - self.captures = collections.OrderedDict() + self.captures = py_collections.OrderedDict() self._building_function = True # Map from resource tensor name to last op (in program order) which uses @@ -122,9 +126,7 @@ class FuncGraph(ops.Graph): # specialization (currently used in cond_v2), here and in the cache key. self._colocation_stack = graph._colocation_stack.copy() # pylint: disable=protected-access - if not self._read_only_collections: - self._collections = graph._collections # pylint: disable=protected-access - else: + if collections is None: for collection_name in graph.get_all_collection_keys(): if collection_name not in WHITELIST_COLLECTIONS: self._collections[collection_name] = graph.get_collection( @@ -132,6 +134,8 @@ class FuncGraph(ops.Graph): for collection_name in WHITELIST_COLLECTIONS: self._collections[collection_name] = graph.get_collection_ref( collection_name) + else: + self._collections = collections def as_default(self): outer_cm = super(FuncGraph, self).as_default() @@ -338,7 +342,8 @@ def func_graph_from_py_func(name, autograph=False, add_control_dependencies=True, arg_names=None, - op_return_value=None): + op_return_value=None, + collections=None): """Returns a `FuncGraph` generated from `python_func`. Args: @@ -365,6 +370,13 @@ def func_graph_from_py_func(name, op_return_value: Optional. A Tensor. If set and `python_func` returns Operations, those return values will be replaced with this value. If not set, returning an Operation triggers an error. + collections: a dictionary of collections this FuncGraph should start + with. If not specified (None), the FuncGraph will read (but not write to) + the outer graph's collections that are not whitelisted, and both + read and write to the outer graph's collections that are whitelisted. + The current whitelisted collections are the global variables, the + local variables, and the trainable variables. + Defaults to None. Returns: A FuncGraph. @@ -376,7 +388,7 @@ def func_graph_from_py_func(name, if op_return_value is not None: assert isinstance(op_return_value, ops.Tensor), op_return_value if func_graph is None: - func_graph = FuncGraph(name) + func_graph = FuncGraph(name, collections=collections) assert isinstance(func_graph, FuncGraph) if add_control_dependencies: control_manager = AutomaticControlDependencies diff --git a/tensorflow/python/ops/cond_v2.py b/tensorflow/python/ops/cond_v2.py index 7d09e32e24..5dca8e501b 100644 --- a/tensorflow/python/ops/cond_v2.py +++ b/tensorflow/python/ops/cond_v2.py @@ -68,14 +68,14 @@ def cond_v2(pred, true_fn, false_fn, name="cond"): true_name, true_fn, [], {}, func_graph=util.CondBranchFuncGraph( - true_name, read_only_collections=False), + true_name, collections=ops.get_default_graph()._collections), # pylint: disable=protected-access add_control_dependencies=add_control_dependencies, op_return_value=pred) false_graph = func_graph_module.func_graph_from_py_func( false_name, false_fn, [], {}, func_graph=util.CondBranchFuncGraph( - false_name, read_only_collections=False), + false_name, collections=ops.get_default_graph()._collections), # pylint: disable=protected-access add_control_dependencies=add_control_dependencies, op_return_value=pred) @@ -554,7 +554,8 @@ class _CondGradFuncGraph(util.CondBranchFuncGraph): """ def __init__(self, name, forward_graph): - super(_CondGradFuncGraph, self).__init__(name, read_only_collections=False) + super(_CondGradFuncGraph, self).__init__( + name, collections=ops.get_default_graph()._collections) # pylint: disable=protected-access self.if_op_needs_rewrite = False self._forward_graph = forward_graph # Maps from forward intermediate tensor -> the unwrapped captured diff --git a/tensorflow/python/ops/while_v2.py b/tensorflow/python/ops/while_v2.py index f9fea0e5bd..f5a51bb1bc 100644 --- a/tensorflow/python/ops/while_v2.py +++ b/tensorflow/python/ops/while_v2.py @@ -115,15 +115,15 @@ def while_loop(cond, loop_counter < maximum_iterations, cond(*_pack_sequence_as(orig_loop_vars, args))) - # NOTE(skyewm): we set read_only_collections=False for compatibility with - # TPUEstimator. + # NOTE(skyewm): we set collections to the outer graph's collections for + # compatibility with TPUEstimator. cond_graph = func_graph_module.func_graph_from_py_func( cond_name, wrapped_cond, loop_vars, {}, signature=_build_signature(loop_vars, shape_invariants), - func_graph=util.WhileCondFuncGraph(cond_name, - read_only_collections=False), + func_graph=util.WhileCondFuncGraph( + cond_name, collections=ops.get_default_graph()._collections), # pylint: disable=protected-access add_control_dependencies=add_control_dependencies) # Add external_captures of cond to the list of loop vars. @@ -174,8 +174,8 @@ def while_loop(cond, wrapped_body, loop_vars, {}, signature=_build_signature(loop_vars, shape_invariants), - func_graph=util.WhileBodyFuncGraph(body_name, - read_only_collections=False), + func_graph=util.WhileBodyFuncGraph( + body_name, collections=ops.get_default_graph()._collections), # pylint: disable=protected-access add_control_dependencies=add_control_dependencies) # Add external captures of body to the list of loop vars. # Note that external tensors will be treated as loop invariants, i.e., -- GitLab From f71e77471c140bac2f4dcb9a003139fb5c657302 Mon Sep 17 00:00:00 2001 From: Sanjoy Das Date: Tue, 8 Jan 2019 15:35:49 -0800 Subject: [PATCH 515/622] Don't check for alignment before calling into a tiled dot (Eigen or LLVM) The alignment restrictions on Eigen was lifted in cr/226091175. This CL lifts the alignment restriction on the tiled LLVM IR GEMM. PiperOrigin-RevId: 228412524 --- tensorflow/compiler/xla/service/cpu/BUILD | 2 - .../cpu/cpu_eigen_tensor_alignment_test.cc | 38 ------ .../xla/service/cpu/dot_op_emitter.cc | 117 ++++++++++++------ .../xla/service/cpu/dot_op_emitter_internal.h | 88 ------------- 4 files changed, 77 insertions(+), 168 deletions(-) delete mode 100644 tensorflow/compiler/xla/service/cpu/dot_op_emitter_internal.h diff --git a/tensorflow/compiler/xla/service/cpu/BUILD b/tensorflow/compiler/xla/service/cpu/BUILD index de62aa60aa..a197bdddc8 100644 --- a/tensorflow/compiler/xla/service/cpu/BUILD +++ b/tensorflow/compiler/xla/service/cpu/BUILD @@ -385,7 +385,6 @@ cc_library( srcs = ["dot_op_emitter.cc"], hdrs = [ "dot_op_emitter.h", - "dot_op_emitter_internal.h", ], deps = [ ":cpu_options", @@ -1029,7 +1028,6 @@ tf_cc_test( size = "small", srcs = ["cpu_eigen_tensor_alignment_test.cc"], deps = [ - ":dot_op_emitter", ":ir_emission_utils", ":target_machine_features_fake", "//tensorflow/compiler/xla:test", diff --git a/tensorflow/compiler/xla/service/cpu/cpu_eigen_tensor_alignment_test.cc b/tensorflow/compiler/xla/service/cpu/cpu_eigen_tensor_alignment_test.cc index 823bdf259c..485769a373 100644 --- a/tensorflow/compiler/xla/service/cpu/cpu_eigen_tensor_alignment_test.cc +++ b/tensorflow/compiler/xla/service/cpu/cpu_eigen_tensor_alignment_test.cc @@ -13,7 +13,6 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "tensorflow/compiler/xla/service/cpu/dot_op_emitter_internal.h" #include "tensorflow/compiler/xla/service/cpu/ir_emission_utils.h" #include "tensorflow/compiler/xla/service/cpu/target_machine_features_fake.h" #include "tensorflow/compiler/xla/service/hlo_parser.h" @@ -23,48 +22,11 @@ namespace xla { namespace cpu { namespace { -using internal::DotImplementationStrategy; -using internal::DotInfo; -using internal::GetDotImplementationStrategy; - // Test that we don't call into Eigen with tensors too small to be aligned // reliably. class CpuEigenTensorAlignmentTest : public ::testing::Test {}; -TEST_F(CpuEigenTensorAlignmentTest, EigenDotAlignment) { - string hlo_string = R"( -HloModule DotOperation - -ENTRY DotOperation { - arg0 = f32[5,256] parameter(0) - arg1 = f32[256,1024] parameter(1) - ROOT dot = f32[5,1024] dot(arg0, arg1), lhs_contracting_dims={1}, rhs_contracting_dims={0} -} -)"; - - TF_ASSERT_OK_AND_ASSIGN(std::unique_ptr module, - ParseHloString(hlo_string)); - - HloInstruction* dot = module->entry_computation()->root_instruction(); - - TargetMachineFeaturesWithFakeAlignmentLogic target_machine_with_no_alignment( - [](int64 size) { return 1; }); - - EXPECT_EQ(GetDotImplementationStrategy(HloModuleConfig{}, DotInfo(*dot), - target_machine_with_no_alignment), - DotImplementationStrategy::kNaiveLlvmIr); - - TargetMachineFeaturesWithFakeAlignmentLogic - target_machine_with_full_alignment([](int64 size) { - return TargetMachineFeatures::kEigenExpectedTensorAlignment; - }); - - EXPECT_NE(GetDotImplementationStrategy(HloModuleConfig{}, DotInfo(*dot), - target_machine_with_full_alignment), - DotImplementationStrategy::kNaiveLlvmIr); -} - TEST_F(CpuEigenTensorAlignmentTest, EigenConvAlignment) { string hlo_string = R"( HloModule ConvOperation diff --git a/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc b/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc index e8a84ebe6b..b018e0cd46 100644 --- a/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc +++ b/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc @@ -14,7 +14,6 @@ limitations under the License. ==============================================================================*/ #include "tensorflow/compiler/xla/service/cpu/dot_op_emitter.h" -#include "tensorflow/compiler/xla/service/cpu/dot_op_emitter_internal.h" #include #include @@ -43,17 +42,63 @@ namespace xla { using llvm_ir::SetToFirstInsertPoint; namespace cpu { - -using internal::DotImplementationStrategy; -using internal::DotInfo; -using internal::GetDotImplementationStrategy; - namespace { // Returns true if we should call into multi-threaded Eigen routines. bool ShouldUseMultiThreadedEigen(const HloModuleConfig& config) { return config.debug_options().xla_cpu_multi_thread_eigen(); } +// Represents a dot operation. We use this in lieu of an `HloInstruction` +// because we want to be able to create this for the "inner" dot operation in a +// batch dot, for which there is no separate HLO instruction. +struct DotInfo { + Shape lhs_shape; + Shape rhs_shape; + Shape result_shape; + DotDimensionNumbers dim_nums; + + explicit DotInfo(const HloInstruction& instr) { + CHECK_EQ(instr.opcode(), HloOpcode::kDot); + lhs_shape = instr.operand(0)->shape(); + rhs_shape = instr.operand(1)->shape(); + result_shape = instr.shape(); + dim_nums = instr.dot_dimension_numbers(); + } +}; + +// Dictates how a dot operation is implemented. +enum class DotImplementationStrategy { + // The dot operation is lowered into LLVM IR that implements a naive nested + // loop that computes the result one element at a time. This is our + // "fallback"; we don't really want this to kick in for any non-trival dot + // operation. + kNaiveLlvmIr, + + // The dot operation is lowered into LLVM IR that implements a tiled + // Matrix*Vector operation. This strategy also allows fusing in a bias add + // into the dot. The matrix can be row major or column major, both are + // supported. + kTiledLlvmIrGemv, + + // The dot operation is lowered into LLVM IR that implemetns a tiled + // Matrix*Matrix operation. No fusions are supported. The two inputs + // and the output have to be row major. + kTiledLlvmIrGemm, + + // The dot operation is lowered into a call into an Eigen routine. No fusions + // are supported today. The two inputs and the output have to be row major. + // However, we do allow transposing either the LHS or the RHS as part of the + // GEMM -- we expose this flexibility as flexibility in the contraction + // dimensions, but we can also see this as flexibility in the input layouts. + kEigen, +}; + +// Returns the implementation strategy for a dot with the configuration +// `dot_info`. +DotImplementationStrategy GetDotImplementationStrategy( + const HloModuleConfig& config, const DotInfo& dot_info, + const TargetMachineFeatures& target_machine_features); + // Helper class for emitting LLVM IR to perform the dot operation. class DotOpEmitter { public: @@ -190,9 +235,8 @@ void DotOpEmitter::EmitTiledLlvmIrGemm() { } int64 size_bytes = m * n * ShapeUtil::ByteSizeOfPrimitiveType(primitive_type); - b_->CreateMemSet( - target, b_->getInt8(0), size_bytes, - target_machine_features_.minimum_alignment_for_allocation(size_bytes)); + b_->CreateMemSet(target, b_->getInt8(0), /*Size=*/size_bytes, + /*Align=*/1); int64 max_target_vector_width = target_machine_features_.vector_register_num_elements( @@ -696,40 +740,34 @@ absl::optional ProfitableToMakeDotOperandColumnMajor( return {}; } -namespace internal { namespace { // Return whether the given shape is rank 2. bool IsRank2(const Shape& shape) { return shape.rank() == 2; } +bool IsSimpleLayout(const Layout& layout) { + return layout.tiles().empty() && layout.format() == DENSE; +} + // In a gemm operation where output = lhs * rhs, check whether the given shapes // are valid for the operation. -bool AreAlignedGemmShapes( - const Shape& lhs_shape, const Shape& rhs_shape, const Shape& output_shape, - const TargetMachineFeatures& target_machine_features) { - // The inputs and the output must - // 1) be matrices with no padding, and - // 2) have an allowed element type. - PrimitiveType output_primitive_type = output_shape.element_type(); - if (!(output_primitive_type == F64 || output_primitive_type == F32 || - output_primitive_type == F16)) { - return false; - } - - if (!(IsRank2(lhs_shape) && IsRank2(rhs_shape) && IsRank2(output_shape))) { - return false; - } - - auto is_aligned = [&](const Shape& shape) { - return GetMinimumAlignmentForArray(shape, target_machine_features) >= - TargetMachineFeatures::kEigenExpectedTensorAlignment; - }; - - if (!is_aligned(lhs_shape) || !is_aligned(rhs_shape) || - !is_aligned(output_shape)) { - return false; +bool AreGemmShapes(const Shape& lhs_shape, const Shape& rhs_shape, + const Shape& output_shape, + const TargetMachineFeatures& target_machine_features) { + CHECK(!lhs_shape.has_layout() || IsSimpleLayout(lhs_shape.layout())) + << lhs_shape.DebugString(); + CHECK(!rhs_shape.has_layout() || IsSimpleLayout(rhs_shape.layout())) + << rhs_shape.DebugString(); + CHECK(!output_shape.has_layout() || IsSimpleLayout(output_shape.layout())) + << output_shape.DebugString(); + + switch (output_shape.element_type()) { + case F64: + case F32: + case F16: + return IsRank2(lhs_shape) && IsRank2(rhs_shape) && IsRank2(output_shape); + default: + return false; } - - return true; } bool IsAlignedGemm(const DotInfo& dot_info, @@ -739,8 +777,8 @@ bool IsAlignedGemm(const DotInfo& dot_info, return false; } - return AreAlignedGemmShapes(dot_info.lhs_shape, dot_info.rhs_shape, - dot_info.result_shape, target_machine_features); + return AreGemmShapes(dot_info.lhs_shape, dot_info.rhs_shape, + dot_info.result_shape, target_machine_features); } bool CanEmitTiledLlvmIrGemm( @@ -781,7 +819,6 @@ bool CanEmitTiledLlvmIrGemm( return true; } -} // namespace DotImplementationStrategy GetDotImplementationStrategy( const HloModuleConfig& config, const DotInfo& dot_info, @@ -806,7 +843,7 @@ DotImplementationStrategy GetDotImplementationStrategy( return DotImplementationStrategy::kNaiveLlvmIr; } -} // namespace internal +} // namespace bool DotImplementationCanHandleTranspose( const HloInstruction& dot_instr, diff --git a/tensorflow/compiler/xla/service/cpu/dot_op_emitter_internal.h b/tensorflow/compiler/xla/service/cpu/dot_op_emitter_internal.h deleted file mode 100644 index cc28918ed6..0000000000 --- a/tensorflow/compiler/xla/service/cpu/dot_op_emitter_internal.h +++ /dev/null @@ -1,88 +0,0 @@ -/* Copyright 2018 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_COMPILER_XLA_SERVICE_CPU_DOT_OP_EMITTER_INTERNAL_H_ -#define TENSORFLOW_COMPILER_XLA_SERVICE_CPU_DOT_OP_EMITTER_INTERNAL_H_ - -#include "tensorflow/compiler/xla/service/cpu/target_machine_features.h" -#include "tensorflow/compiler/xla/service/hlo_instruction.h" -#include "tensorflow/compiler/xla/service/hlo_module.h" - -// ----------------------------------------------------------------------------- -// INTERNAL HEADER. -// -// This file exposes internal implementation details from dot_op_emitter.cc for -// unit tests. Please do not depend on this! -// -// ----------------------------------------------------------------------------- - -namespace xla { -namespace cpu { -namespace internal { - -// Represents a dot operation. We use this in lieu of an `HloInstruction` -// because we want to be able to create this for the "inner" dot operation in a -// batch dot, for which there is no separate HLO instruction. -struct DotInfo { - Shape lhs_shape; - Shape rhs_shape; - Shape result_shape; - DotDimensionNumbers dim_nums; - - explicit DotInfo(const HloInstruction& instr) { - CHECK_EQ(instr.opcode(), HloOpcode::kDot); - lhs_shape = instr.operand(0)->shape(); - rhs_shape = instr.operand(1)->shape(); - result_shape = instr.shape(); - dim_nums = instr.dot_dimension_numbers(); - } -}; - -// Dictates how a dot operation is implemented. -enum class DotImplementationStrategy { - // The dot operation is lowered into LLVM IR that implements a naive nested - // loop that computes the result one element at a time. This is our - // "fallback"; we don't really want this to kick in for any non-trival dot - // operation. - kNaiveLlvmIr, - - // The dot operation is lowered into LLVM IR that implements a tiled - // Matrix*Vector operation. This strategy also allows fusing in a bias add - // into the dot. The matrix can be row major or column major, both are - // supported. - kTiledLlvmIrGemv, - - // The dot operation is lowered into LLVM IR that implemetns a tiled - // Matrix*Matrix operation. No fusions are supported. The two inputs - // and the output have to be row major. - kTiledLlvmIrGemm, - - // The dot operation is lowered into a call into an Eigen routine. No fusions - // are supported today. The two inputs and the output have to be row major. - // However, we do allow transposing either the LHS or the RHS as part of the - // GEMM -- we expose this flexibility as flexibility in the contraction - // dimensions, but we can also see this as flexibility in the input layouts. - kEigen, -}; - -// Returns the implementation strategy for a dot with the configuration -// `dot_info`. -DotImplementationStrategy GetDotImplementationStrategy( - const HloModuleConfig& config, const DotInfo& dot_info, - const TargetMachineFeatures& target_machine_features); -} // namespace internal -} // namespace cpu -} // namespace xla - -#endif // TENSORFLOW_COMPILER_XLA_SERVICE_CPU_DOT_OP_EMITTER_INTERNAL_H_ -- GitLab From cb0ecda8555f827c4fa4d9c160c68ea028bdc305 Mon Sep 17 00:00:00 2001 From: Tim Shen Date: Tue, 8 Jan 2019 15:41:12 -0800 Subject: [PATCH 516/622] Unbreak windows builds. cuda_deps seems not to work well with select() on windows. PiperOrigin-RevId: 228413463 --- .../core/platform/default/build_config/BUILD | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/tensorflow/core/platform/default/build_config/BUILD b/tensorflow/core/platform/default/build_config/BUILD index f5a194428c..c0c1022c16 100644 --- a/tensorflow/core/platform/default/build_config/BUILD +++ b/tensorflow/core/platform/default/build_config/BUILD @@ -32,15 +32,7 @@ cc_library( tf_cuda_library( name = "stream_executor", - cuda_deps = [ - "//tensorflow/stream_executor/cuda:cuda_activation", - ] + select({ - "//tensorflow:using_cuda_clang": ["//tensorflow/stream_executor/cuda:all_runtime"], - "//tensorflow:using_cuda_nvcc": ["//tensorflow/stream_executor/cuda:all_runtime"], - "//tensorflow:using_cuda_clang_with_dynamic_build": [], - "//tensorflow:using_cuda_nvcc_with_dynamic_build": [], - "//conditions:default": [], - }), + cuda_deps = ["//tensorflow/stream_executor/cuda:cuda_activation"], deps = [ "//tensorflow/stream_executor", "//tensorflow/stream_executor:dnn", @@ -53,6 +45,12 @@ tf_cuda_library( ] + select({ "@local_config_cuda//cuda:darwin": ["IOKit"], "//conditions:default": [], + }) + select({ + "//tensorflow:using_cuda_clang": ["//tensorflow/stream_executor/cuda:all_runtime"], + "//tensorflow:using_cuda_nvcc": ["//tensorflow/stream_executor/cuda:all_runtime"], + "//tensorflow:using_cuda_clang_with_dynamic_build": [], + "//tensorflow:using_cuda_nvcc_with_dynamic_build": [], + "//conditions:default": [], }), ) -- GitLab From 29579a2e548800994f1acdf37fc2f8bf42b043ee Mon Sep 17 00:00:00 2001 From: Jared Duke Date: Tue, 8 Jan 2019 15:50:48 -0800 Subject: [PATCH 517/622] Fix tf_driver_test Use the proper path for the tested model. PiperOrigin-RevId: 228415121 --- tensorflow/lite/testing/tf_driver_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/lite/testing/tf_driver_test.cc b/tensorflow/lite/testing/tf_driver_test.cc index 363d162d56..e79704d616 100644 --- a/tensorflow/lite/testing/tf_driver_test.cc +++ b/tensorflow/lite/testing/tf_driver_test.cc @@ -93,7 +93,7 @@ TEST(TfDriverTest, SimpleTest) { {"1,8,8,3", "1,8,8,3", "1,8,8,3", "1,8,8,3"}, {"x", "y"})); runner->LoadModel( - "third_party/tensorflow/lite/testdata/multi_add.pb"); + "tensorflow/lite/testdata/multi_add.pb"); EXPECT_TRUE(runner->IsValid()) << runner->GetErrorMessage(); ASSERT_THAT(runner->GetInputs(), ElementsAre(0, 1, 2, 3)); -- GitLab From 0b53b291f31d9ac9d1bb9cffbc037fe978f593fd Mon Sep 17 00:00:00 2001 From: Reed Wanderman-Milne Date: Tue, 8 Jan 2019 15:57:52 -0800 Subject: [PATCH 518/622] Automated rollback of changelist 205730535 PiperOrigin-RevId: 228416180 --- tensorflow/core/BUILD | 11 ++++ .../core/common_runtime/gpu/gpu_device.cc | 51 ++++++++++++++++++- .../core/common_runtime/gpu/gpu_device.h | 11 ++++ .../gpu/gpu_device_kernel_check.cu.cc | 37 ++++++++++++++ .../gpu/gpu_device_kernel_check.h | 32 ++++++++++++ 5 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 tensorflow/core/common_runtime/gpu/gpu_device_kernel_check.cu.cc create mode 100644 tensorflow/core/common_runtime/gpu/gpu_device_kernel_check.h diff --git a/tensorflow/core/BUILD b/tensorflow/core/BUILD index e6af9211b5..6a842a432f 100644 --- a/tensorflow/core/BUILD +++ b/tensorflow/core/BUILD @@ -87,6 +87,7 @@ load( "tf_gen_op_libs", "tf_generate_proto_text_sources", "tf_genrule_cmd_append_to_srcs", + "tf_gpu_kernel_library", "tf_opts_nortti_if_android", "transitive_hdrs", ) @@ -3162,6 +3163,15 @@ cc_library( ], ) +tf_gpu_kernel_library( + name = "gpu_device_kernel_check", + srcs = ["common_runtime/gpu/gpu_device_kernel_check.cu.cc"], + hdrs = ["common_runtime/gpu/gpu_device_kernel_check.h"], + deps = [ + "//tensorflow/core:stream_executor", + ], +) + GPU_RUNTIME_HEADERS = [ "common_runtime/gpu/cuda_host_allocator.h", "common_runtime/gpu/gpu_bfc_allocator.h", @@ -3200,6 +3210,7 @@ tf_cuda_library( ":core_cpu_lib", ":framework", ":framework_internal", + ":gpu_device_kernel_check", ":gpu_id_impl", ":gpu_init_impl", ":gpu_lib", diff --git a/tensorflow/core/common_runtime/gpu/gpu_device.cc b/tensorflow/core/common_runtime/gpu/gpu_device.cc index 010fdff4e9..891b6e0e2a 100644 --- a/tensorflow/core/common_runtime/gpu/gpu_device.cc +++ b/tensorflow/core/common_runtime/gpu/gpu_device.cc @@ -31,6 +31,7 @@ limitations under the License. #include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor" #include "tensorflow/core/common_runtime/device_factory.h" +#include "tensorflow/core/common_runtime/gpu/gpu_device_kernel_check.h" #include "tensorflow/core/common_runtime/gpu/gpu_event_mgr.h" #include "tensorflow/core/common_runtime/gpu/gpu_id.h" #include "tensorflow/core/common_runtime/gpu/gpu_id_manager.h" @@ -388,7 +389,7 @@ Status BaseGPUDevice::Init(const SessionOptions& options) { } } - return Status::OK(); + return CheckGPU(); } bool BaseGPUDevice::RequiresRecordingAccessedTensors() const { @@ -907,6 +908,54 @@ Allocator* BaseGPUDevice::GetScopedAllocator(AllocatorAttributes attr, return gpu_allocator_; } +Status BaseGPUDevice::CheckGPU() { + se::Stream* stream = tensorflow_gpu_device_info()->stream; + TF_RETURN_IF_ERROR(stream->BlockHostUntilDone()); + Tensor device_tensor(gpu_allocator_, DT_FLOAT, {}); + if (!device_tensor.IsInitialized()) { + return errors::ResourceExhausted("Failed to allocate ", sizeof(float), + " bytes on the GPU for initialization " + "checks"); + } + float* val_dev = device_tensor.scalar().data(); + const cudaStream_t cu_stream = *reinterpret_cast( + stream->implementation()->GpuStreamMemberHack()); + { + se::cuda::ScopedActivateExecutorContext scoped_activation{stream->parent()}; + run_test_kernel(val_dev, cu_stream); + // We have to use the CUDA runtime function cudaPeekAtLastError here, + // because 'stream' does not provide a way to check if a kernel launch + // succeeds. Calling 'stream->BlockHostUntilDone()', which internally calls + // 'cuCtxSynchronize()', does not catch all kernel launch errors. + cudaError_t cuda_error = cudaPeekAtLastError(); + if (cuda_error == cudaSuccess) { + cuda_error = cudaDeviceSynchronize(); + } + TF_RETURN_IF_ERROR(CudaErrorToStatus(cuda_error, *stream)); + } + + float val_host = 0.; + stream->ThenMemcpy(&val_host, se::DeviceMemoryBase(val_dev, sizeof(float)), + sizeof(float)); + TF_RETURN_IF_ERROR(stream->BlockHostUntilDone()); + if (val_host != 12345.) { + return errors::Internal( + "GPU kernel for initialization returned wrong value: ", val_host); + } + return Status::OK(); +} + +Status BaseGPUDevice::CudaErrorToStatus(cudaError_t cuda_error, + const se::Stream& stream) { + if (cuda_error != cudaSuccess) { + return errors::Internal( + "Failed to run GPU kernel for the initialization check. Received " + "error ", + cudaGetErrorName(cuda_error), " after running GPU kernel."); + } + return Status::OK(); +} + const int BaseGPUDeviceFactory::InterconnectMap::kSameDeviceStrength = 1000; const int BaseGPUDeviceFactory::InterconnectMap::kStreamExecutorStrength = 1; diff --git a/tensorflow/core/common_runtime/gpu/gpu_device.h b/tensorflow/core/common_runtime/gpu/gpu_device.h index d002d02c51..86622dad49 100644 --- a/tensorflow/core/common_runtime/gpu/gpu_device.h +++ b/tensorflow/core/common_runtime/gpu/gpu_device.h @@ -26,6 +26,7 @@ limitations under the License. #include #include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor" +#include "cuda/include/cuda_runtime_api.h" #include "tensorflow/core/common_runtime/device_factory.h" #include "tensorflow/core/common_runtime/gpu/gpu_event_mgr.h" #include "tensorflow/core/common_runtime/gpu/gpu_id.h" @@ -121,6 +122,12 @@ class BaseGPUDevice : public LocalDevice { se::StreamExecutor* executor_; // not owned std::unique_ptr scoped_allocator_mgr_; + // Returns a Status corresponding to a cudaError_t. The CUDA error must have + // been obtained from a CUDA kernel launch used to check if the GPU is + // initialized properly. + virtual Status CudaErrorToStatus(cudaError_t cuda_error, + const se::Stream& stream); + private: struct StreamGroup { se::Stream* compute = nullptr; @@ -161,6 +168,10 @@ class BaseGPUDevice : public LocalDevice { Status MaybeCopyTensorToGPU(const AllocatorAttributes& alloc_attrs, const Tensor& from, Tensor* to, StatusCallback done); + + // Checks that the GPU is capable of doing work, by running a test kernel on + // it. + Status CheckGPU(); }; class BaseGPUDeviceFactory : public DeviceFactory { diff --git a/tensorflow/core/common_runtime/gpu/gpu_device_kernel_check.cu.cc b/tensorflow/core/common_runtime/gpu/gpu_device_kernel_check.cu.cc new file mode 100644 index 0000000000..017565195b --- /dev/null +++ b/tensorflow/core/common_runtime/gpu/gpu_device_kernel_check.cu.cc @@ -0,0 +1,37 @@ +/* Copyright 2018 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. +==============================================================================*/ + +#if GOOGLE_CUDA + +#include "tensorflow/core/common_runtime/gpu/gpu_device_kernel_check.h" +#include "tensorflow/stream_executor/cuda/cuda_activation.h" + +namespace { +__global__ void test_kernel(float* val) { + if (blockIdx.x == 0 && threadIdx.x == 0) { + (*val) = 12345.; + } +} +} // namespace + +namespace tensorflow { + +void run_test_kernel(float* val, cudaStream_t cu_stream) { + test_kernel<<<1, 1, 0, cu_stream>>>(val); +} + +} // namespace tensorflow + +#endif // GOOGLE_CUDA diff --git a/tensorflow/core/common_runtime/gpu/gpu_device_kernel_check.h b/tensorflow/core/common_runtime/gpu/gpu_device_kernel_check.h new file mode 100644 index 0000000000..064fb7a49f --- /dev/null +++ b/tensorflow/core/common_runtime/gpu/gpu_device_kernel_check.h @@ -0,0 +1,32 @@ +/* Copyright 2018 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_COMMON_RUNTIME_GPU_GPU_DEVICE_KERNEL_CHECK_H_ +#define TENSORFLOW_CORE_COMMON_RUNTIME_GPU_GPU_DEVICE_KERNEL_CHECK_H_ + +#if GOOGLE_CUDA + +#include "tensorflow/core/platform/stream_executor.h" + +namespace tensorflow { + +// Runs a GPU kernel to test that it functions correctly. Sets 'val' to 12345. +void run_test_kernel(float* val, cudaStream_t cu_stream); + +} // namespace tensorflow + +#endif // GOOGLE_CUDA + +#endif // TENSORFLOW_CORE_COMMON_RUNTIME_GPU_GPU_DEVICE_KERNEL_CHECK_H_ -- GitLab From 03ec3f7b11572b70ef7f33f2a9af9bebbb6ce473 Mon Sep 17 00:00:00 2001 From: Toby Boyd Date: Tue, 8 Jan 2019 16:24:00 -0800 Subject: [PATCH 519/622] Installs TensorRT --- tensorflow/tools/ci_build/Dockerfile.gpu | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tensorflow/tools/ci_build/Dockerfile.gpu b/tensorflow/tools/ci_build/Dockerfile.gpu index ff1fefe892..f5a28ff163 100644 --- a/tensorflow/tools/ci_build/Dockerfile.gpu +++ b/tensorflow/tools/ci_build/Dockerfile.gpu @@ -7,6 +7,12 @@ LABEL maintainer="Jan Prach " RUN cp -P /usr/include/cudnn.h /usr/local/cuda/include RUN cp -P /usr/lib/x86_64-linux-gnu/libcudnn* /usr/local/cuda/lib64 +# Installs TensorRT, which is not included in NVIDIA Docker containers. +RUN apt-get update \ + && apt-get install nvinfer-runtime-trt-repo-ubuntu1604-5.0.2-ga-cuda10.0 \ + && apt-get update \ + && apt-get install -y --no-install-recommends libnvinfer-dev=5.0.2-1+cuda10.0 + # Copy and run the install scripts. COPY install/*.sh /install/ ARG DEBIAN_FRONTEND=noninteractive -- GitLab From f3b8bf9aca77b6264d791425593bd8cd02aa8843 Mon Sep 17 00:00:00 2001 From: Doe Hyun Yoon Date: Tue, 8 Jan 2019 16:23:50 -0800 Subject: [PATCH 520/622] Add aggressive_shape_inference option to VirtualScheduler, and enable it in analytical_cost_estimator, but not in VirtualCluster::Run(). PiperOrigin-RevId: 228420860 --- .../core/grappler/clusters/virtual_cluster.cc | 6 ++++- .../costs/analytical_cost_estimator.cc | 6 +++-- .../costs/analytical_cost_estimator.h | 3 ++- .../core/grappler/costs/virtual_scheduler.cc | 27 +++---------------- .../core/grappler/costs/virtual_scheduler.h | 12 +++------ .../grappler/costs/virtual_scheduler_test.cc | 10 ++++--- 6 files changed, 25 insertions(+), 39 deletions(-) diff --git a/tensorflow/core/grappler/clusters/virtual_cluster.cc b/tensorflow/core/grappler/clusters/virtual_cluster.cc index e80d6b54d9..118f74e8b0 100644 --- a/tensorflow/core/grappler/clusters/virtual_cluster.cc +++ b/tensorflow/core/grappler/clusters/virtual_cluster.cc @@ -73,7 +73,11 @@ Status VirtualCluster::Run(const GraphDef& graph, item.graph = graph; item.feed = feed; item.fetch = fetch; - VirtualScheduler scheduler(true, this, node_manager_.get()); + // Note that we do not use aggressive shape inference to preserve unknown + // shapes from the input graph. + VirtualScheduler scheduler(/*use_static_shapes=*/true, + /*use_aggressive_shape_inference=*/false, this, + node_manager_.get()); TF_RETURN_IF_ERROR(scheduler.Init(&item)); if (metadata) { diff --git a/tensorflow/core/grappler/costs/analytical_cost_estimator.cc b/tensorflow/core/grappler/costs/analytical_cost_estimator.cc index eac6a0de3f..09cddad8ba 100644 --- a/tensorflow/core/grappler/costs/analytical_cost_estimator.cc +++ b/tensorflow/core/grappler/costs/analytical_cost_estimator.cc @@ -113,8 +113,10 @@ AnalyticalCostEstimator::AnalyticalCostEstimator( node_estimator_(std::move(node_estimator)), node_manager_(std::move(node_manager)), use_static_shapes_(use_static_shapes) { - scheduler_ = absl::make_unique(use_static_shapes_, cluster_, - node_manager_.get()); + // Use aggressive static shape inference to minimize unknown shapes. + scheduler_ = absl::make_unique( + use_static_shapes_, + /*use_aggressive_shape_inference=*/true, cluster_, node_manager_.get()); } Status AnalyticalCostEstimator::Initialize(const GrapplerItem& item) { diff --git a/tensorflow/core/grappler/costs/analytical_cost_estimator.h b/tensorflow/core/grappler/costs/analytical_cost_estimator.h index ade61f4051..6275c62198 100644 --- a/tensorflow/core/grappler/costs/analytical_cost_estimator.h +++ b/tensorflow/core/grappler/costs/analytical_cost_estimator.h @@ -34,7 +34,8 @@ class Cluster; struct GrapplerItem; // Estimate the cost of running a Grappler item based on the theoretical -// performance of the hardware that will run the model. +// performance of the hardware that will run the model. Note that this +// internally uses aggressive shape inference with static shape inference. class AnalyticalCostEstimator : public CostEstimator { public: // Does not take ownership of cluster. diff --git a/tensorflow/core/grappler/costs/virtual_scheduler.cc b/tensorflow/core/grappler/costs/virtual_scheduler.cc index bb8425a5a1..0aac0348b5 100644 --- a/tensorflow/core/grappler/costs/virtual_scheduler.cc +++ b/tensorflow/core/grappler/costs/virtual_scheduler.cc @@ -310,29 +310,15 @@ ReadyNodeManager* VirtualScheduler::ReadyNodeManagerFactory( LOG(FATAL) << "Not a valid ready node manager: " << ready_node_manager; } -VirtualScheduler::VirtualScheduler(const GrapplerItem* grappler_item, - const bool use_static_shapes, - Cluster* cluster, - ReadyNodeManager* ready_nodes) - : ready_nodes_(ready_nodes), - graph_costs_(Costs::ZeroCosts()), - graph_properties_(new GraphProperties(*grappler_item)), - cluster_(cluster), - grappler_item_(grappler_item), - use_static_shapes_(use_static_shapes), - placer_(cluster) { - graph_costs_.num_ops_total = 0; - initialized_ = false; - track_mem_usage_snapshot_ = VLOG_IS_ON(1); -} - VirtualScheduler::VirtualScheduler(const bool use_static_shapes, + const bool use_aggressive_shape_inference, Cluster* cluster, ReadyNodeManager* ready_nodes) : ready_nodes_(ready_nodes), graph_costs_(Costs::ZeroCosts()), cluster_(cluster), use_static_shapes_(use_static_shapes), + use_aggressive_shape_inference_(use_aggressive_shape_inference), placer_(cluster) { graph_costs_.num_ops_total = 0; initialized_ = false; @@ -343,12 +329,6 @@ Status VirtualScheduler::Init(const GrapplerItem* item) { grappler_item_ = item; graph_properties_ = absl::make_unique(*item); - return Init(); -} - -// TODO(pcma): Merge with Init(const GrapplerItem* item) when this -// deprecated API is deleted -Status VirtualScheduler::Init() { initialized_ = false; // Clear all internal states so that the VirtualScheduler is reusable for @@ -372,7 +352,8 @@ Status VirtualScheduler::Init() { // Construct graph properties. if (use_static_shapes_) { - TF_RETURN_IF_ERROR(graph_properties_->InferStatically(true)); + TF_RETURN_IF_ERROR(graph_properties_->InferStatically( + true, use_aggressive_shape_inference_)); } else { TF_RETURN_IF_ERROR(graph_properties_->InferDynamically(cluster_)); } diff --git a/tensorflow/core/grappler/costs/virtual_scheduler.h b/tensorflow/core/grappler/costs/virtual_scheduler.h index ffc5e9187c..d96371bcab 100644 --- a/tensorflow/core/grappler/costs/virtual_scheduler.h +++ b/tensorflow/core/grappler/costs/virtual_scheduler.h @@ -260,16 +260,9 @@ std::unique_ptr ReadyNodeManagerFactory( // dependencies, device, etc. class VirtualScheduler { public: - // TODO(pcma): Modify power_analyzer.cc to use new API's. - // DEPRECATED - VirtualScheduler(const GrapplerItem* grappler_item, - const bool use_static_shapes, Cluster* cluster, - ReadyNodeManager* ready_nodes); - // DEPRECATED - Status Init(); - // Does not take ownership of cluster or ready_nodes. - VirtualScheduler(bool use_static_shapes, Cluster* cluster, + VirtualScheduler(const bool use_static_shapes, + const bool use_aggressive_shape_inference, Cluster* cluster, ReadyNodeManager* ready_nodes); // Initializes the scheduler for the specific grappler item. // Should be called immediately after the c'tor or when the scheduler will be @@ -367,6 +360,7 @@ class VirtualScheduler { bool use_static_shapes_; bool initialized_; bool track_mem_usage_snapshot_; + const bool use_aggressive_shape_inference_; // Whether the input graph includes Switch nodes annotated with output slots // information. diff --git a/tensorflow/core/grappler/costs/virtual_scheduler_test.cc b/tensorflow/core/grappler/costs/virtual_scheduler_test.cc index 5e044b2037..128cb986f1 100644 --- a/tensorflow/core/grappler/costs/virtual_scheduler_test.cc +++ b/tensorflow/core/grappler/costs/virtual_scheduler_test.cc @@ -30,8 +30,11 @@ namespace grappler { // Class for testing virtual scheduler. class TestVirtualScheduler : public VirtualScheduler { public: - TestVirtualScheduler(const bool use_static_shapes, Cluster* cluster) - : VirtualScheduler(use_static_shapes, cluster, &ready_node_manager_) { + TestVirtualScheduler(const bool use_static_shapes, + const bool use_aggressive_shape_inference, + Cluster* cluster) + : VirtualScheduler(use_static_shapes, use_aggressive_shape_inference, + cluster, &ready_node_manager_) { enable_mem_usage_tracking(); } @@ -68,7 +71,8 @@ class VirtualSchedulerTest : public ::testing::Test { devices[kCPU1] = cpu_device; cluster_ = absl::make_unique(devices); scheduler_ = absl::make_unique( - /* use_static_shapes = */ true, cluster_.get()); + /*use_static_shapes=*/true, + /*use_aggressive_shape_inference=*/true, cluster_.get()); } NodeDef node1_, node2_, node3_, node4_, node5_, node6_; -- GitLab From 46137ebafbb56857622403115ba99051d896351d Mon Sep 17 00:00:00 2001 From: Martin Wicke Date: Tue, 8 Jan 2019 16:25:48 -0800 Subject: [PATCH 521/622] Remove disabled test (should live with pasta). PiperOrigin-RevId: 228421179 --- .../tools/compatibility/ast_edits_test.py | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/tensorflow/tools/compatibility/ast_edits_test.py b/tensorflow/tools/compatibility/ast_edits_test.py index b2e9f67638..4accd8fe29 100644 --- a/tensorflow/tools/compatibility/ast_edits_test.py +++ b/tensorflow/tools/compatibility/ast_edits_test.py @@ -39,8 +39,6 @@ following new APIs: from __future__ import absolute_import from __future__ import division from __future__ import print_function -import ast -import pasta import six from tensorflow.python.framework import test_util from tensorflow.python.platform import test as test_lib @@ -418,26 +416,5 @@ class TestAstEdits(test_util.TensorFlowTestCase): self.assertNotIn("not good", report) -class ManualEditsTest(test_util.TensorFlowTestCase): - - def disabled_test_arg_order(self): - """Tests that generated arg order is sane.""" - text = "f(a)" - t = pasta.parse(text) - node = pasta.ast_utils.find_nodes_by_type(t, (ast.Call,))[0] - arg = ast.keyword(arg="b", value=ast.Num(n=0)) - node.keywords.append(arg) - - # This is only needed in Python3, and I think it's a bug (but maybe in ast). - arg.value.lineno = 0 - arg.value.col_offset = 0 - - # pasta.dump should never put kwargs before args, even if the col_offset is - # messed up. - # This fails if run with python3, but works find for python2. - # In python3, the dump yields "f(b=0, a)". - self.assertEqual(pasta.dump(t), "f(a, b=0)") - - if __name__ == "__main__": test_lib.main() -- GitLab From 5439aec55231818d345767b5038410bf647844e2 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Tue, 8 Jan 2019 16:36:37 -0800 Subject: [PATCH 522/622] Internal change PiperOrigin-RevId: 228422956 --- .../g3doc/images/convert/sample_after.png | Bin 185267 -> 166567 bytes .../g3doc/images/convert/sample_before.png | Bin 155610 -> 136956 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/tensorflow/lite/g3doc/images/convert/sample_after.png b/tensorflow/lite/g3doc/images/convert/sample_after.png index 6c451f97903f7f70a9f28dee8abf6daeb7ec5693..db09d0a6ca70695205833acfd2bd8ac6682cb065 100644 GIT binary patch literal 166567 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%0D3xss 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 literal 185267 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 Y{-@dbB|mq!zXI_+UHx3vIVCg!0O{5<+5i9m diff --git a/tensorflow/lite/g3doc/images/convert/sample_before.png b/tensorflow/lite/g3doc/images/convert/sample_before.png index e5317ef295062e79c66430512ef1c45925858ce0..55440d324977f0ff5b795bc80898857918066e96 100644 GIT binary patch literal 136956 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

    747KGxnT6?`G~Iy0y5x zlHKx7UE1HXM@`b(7KG>U#(WHtHj_RY)z|W;TsVHI+U@Q6``7jD2dxL*nCu?4J+L|xV0~s9otb6o4IZ!jQ@$y*%Z;lRwwKp``1J6f0;{v$)|Bqd?7a;q z_gasQj7=Dt1drs50lld|_450&qApZu)cIQdvlcF~>4X-8W)g+a$^ zfwtTq?GgoTCiw87pw=qJvHVUU`|7Z@iz+`qGs(EnAZJ@u@a09IsGG|Zs}w#-qm&b0 zHzhGb^=`L$dKG+?bQhsZN1b6ZFUxm*MKjwbg^rOY`{m$4JZ4@%C~nSkEMLvWAg2J@r8X$ zn8SDUPI6okV%8Ed-Em$F+v({)PXym_TzQCZ*7WiT+f?UnbkDVkKkqr?uEKQA(A5?v zWz#08?>;(ZEu)rbfk3=j3!7`snYq)B-EmnFdeQNyk>U|G_K2b%XLe2$TPdhkC}%(a z%`O9-X}!f4l9p#GJo_F@u)zy{n_kMqIb+ve4VBoKi9Z~l~a?P~D*PUVJw>bb^X5N+MEIzjuY{_^YTu~%FUdbbw1~apj55$l-@-b3tlZby7BXO#VZQax%uXA`B$hPd(VUU zj~M%Cw870;(z{~x~o@3JqW`LFu=w}~JAKA1Dds&dYT4I8xgt*ib0 z?c<%|^B*7g+k?)>c(?n#9j|#jd)d1?p8x;-y}2=&UD>_w$-L@!pcVV;+V#I{9sGYN z>AI_<;DNc*KK|G*dCqpiAN{6z=ac>)ef{5eZO0Nc}HQgT-}dEAz@+Yo`uuzkIUCD@tUf&=l{Ro zKJ#p57Ct^UQQcoo^qE!qEH!@nKL*lfISM8wQ!Xv_HcmRiu_tbQm$FjP)R%LPdasDQ zdU7voP6boKEXnO_EsGAgABwWy{qO1C7Uf00a%<(CHX3xBt5s#(QJk}Bxs8q4^ZB|f za`sG4*P89~$7U+)^xQJh=H*r`iR*kHpPhT~^&^Kpcb@CKySsaO`uy5ypoNMhF9L3D z&#%9^{pW*SGks#VNs9}by!gYAx;eID@sj4%X&=6xIy@mSaPjBnkh8U&JByjrBdv0^ z;(aZB&U5MqY5m!wSa@D;-ufEx&}F*I4W(zAr7PP1lRIv`e%qr@ajV}ZKE1*pS)|Awmact?FMisMjeUF_Ta{+2ozFY>)5a*l&{xgVQljmd#| zr!9Er3+4)mvs%~l-z&+TK55b+Zv8zDOTDIQ1TJEEeSQ6YB}YL8|39%?bSIrTF5mxX z>H2H&Uv4k1xmRl-U;jrhQflkok^mNi#hMdNA73=L`u`8>-IKlU$}cVm6BlIl3cT>E zb?a`u^z*aY=THCJ#IAld+C(|`P4%AbCUb6GJ@;kLtSRe7j~}1^Zb@NxmW3_*k8h99 zOZ@#KVYc*#e$17ZvNcw3O^zn7Kcm~;C8m4o%F5u%=X1*?Y$^=CzPg%xxQ+MM2GAzw zJ!O9B|9tIgFD1QAlV2S6i(&F@?pq4R2FF~a(yJ@LjYylT8xJqY(2%iy-!=Uu_hrd_ zj}FZY`s5~cWDf5_w)1yHy?(yvS$6aP?EZiED=g}h_RR2TUUX4BcFM$`zdAgc_lUDZ zHSdUc+%#{6RQj_S`P}NIfBv7o&M=0YuPeen9ojj;_MOV4N#|rmHg4S6Q1GVggUU|J>XI2dGw%Nm zJazn*;oO@iCcl34@H2y_r)R}E`#DDWTv7$Q3*&7R`;XTitop7i<-A;ddV0(R(YT&@ z?`@q5Zf8t;=V$qQj`C}<;Kx7RjjL?tR_^?M@%YZoSscstqrMz0y}xm}Yjsz9{LSvx zSJ&6qf1Bp+JKJn&^!7X%%OV%~2qmTP39+g_viE&C6EH#Y-lwplkCDvMD_@Ew>NUq| zU*?>`qbSyKCQG?;d%NA|+9|s7PVZlp@%`gt&D-o)HQ`Vp->*{VTFct!p4GWvpego-*4ebe1A`&(Dm%*(>wOgIQj7Gz14NX@3d77 z4JR)1o&Bh2=M1A%u8$u-hUeau^SAKle|P-SbT@;H<`Z9FaeBrU<9V8oe^2CB_`&@B*z~i1xWfC()Azf5*>#df(xPp*y8hDa zoofo>x|hC*KQ}x7%#o?Kz2}#!8fG1nSX3|Ldj0yc3!8Gih4y(dMB5(ImGHe9VSPKj z{>Y7`>KwL4t(AebGp=su$p130A(gfA+gFV?`^)oUOrtIx`_Zv*zW(B;50=ePnf&1$ z*RHxxXDWYB=;Pl}u&s=LwOZX_=l*XGvhK)UH1XY4`r7UGw%p3MTd!-x?lMU}*0V7A zc;BxBLV`P)u5AACyf&2K%9{5}>Sr75nr7f>^&<5Omww%YFMRy+|7M&!7{ljvqp9X! ztduuDTc@6g)9jeCs&M|qk5@wC=1e^GrP;CIK>qB%YwYEY9AV;_m+?+1H(Ba$`Vzz9 z_z5vb-@J)6%3Trv<&boZe9RZVz5jz7%j2rMp2yEVB%Qi{ z>EX-wADy+i7B^#0&spjJ-t$kLuFT3{YMo`eyf1P0k#H;X`&%k{a^A`3-c0BiFk-QOE3KL^Qep0?@fcab%ZPiNk(XN{fz?&RfK_6PAltm5BkJI$Xi zl{`)AMD_Wt>eD%Ia9?`3boYn4uZr6K5fSY52|nBWLe70N-`l!;`-h*micGZT|6MiB zPD`r`bWqa6!|k7+PLF@3><#VucLYbq*;Bt zO=n*Icy``ouj_>`Wt5er=gdt1wdJv-%$Pa>eBa*wFU=H+DiXSJj!=!&ri0+Rbj_omNcw2y80-oQDBp(@j*V%-SvzK z*Jq`!xtp1Oq+@$Vlm^p$bJgP0=QJ<=uv-ApQhh1alcu!)DDSnFJf=3=6`a$#_Z2+ z#zV%^NBnN&s#)*5-?%$i=Apwho1G1{Gndcab&%ge(K|7Jj^6^E>E9pP2o~D>tKi+W?~4NW z+qpCD8y~+Pa^P^k@cxC#{dpNpc7hXUPp%UCe`Mx8hx@LH=TGh7FnJu~*6rKrc!;a@ zLxtw#jsWSu@*InooMp-}Sux||bK`bxWr=5{4p|Iu^q948K2izTxUGA4GViHrJc$+E z0_S3kv#ofO7Uj;_dF$YGzUV&IgNZ@9pWiu|p1z@|d7x8R|Kf`~PDqym`Z)t1-aZ?=BGekk+SQ%;6?uWoh}9C*jk_<&!=VxHhhuIamk z|G#qmGp$nb%=-KFy^Bk|KS}T74*K`aHUCk~7O{;D<>3sMe!b_LbYJj^c)w$6e8bL@S&vUYJh5vgPt7y?H5=b5KL6o(Cw8ahUFl_) zRP5OQ9hom-`OBW6#^6zIUiC)t)Y)I2{$>9ABI0-DuP;3b->#*dsoTpZ_%eFu{udlp zHr7qlo3eitTvC$Lo_=VLtX*Bz@9cMfRxrP}uQj)Azjyx1LV-(pD&FkJ zAKs7XKhW{;%GzzSS9<>vtbXRK=wI4qy23Bz?j-Sd1rId3m(?Ero?Wx1bH=$ZGUY+{ ze)ZiAeZRD1$1)!g!JUyWm))7}3F-=opSvt0mnCra{`tFe%r8A~*z|RK|J?m|V)Sl& z?SEEcrXj`K?jN@qDQZhFM z9YKM9T*LH<=OXu5&6u%ColTbr?*L`(tdr7b9&z|cY4N8R~MJOtXGkHdU^sTAAe5@Pj4{WTv^4*-yi>Z z{q$*jJp$+4*FV2yigS-Fath z?#@49%9NJpi>jAaPnz`M#^H0mKhvKrs(tUSDPMiB!Qt84;&XdS?k_SdsVb^`7WzXi zHOj_m`R()XFY-(%dBox=zE)?#&-(I7vp?4h7pNC1oh){gh&BBvE%z!S(s)}(#~mdT zo?1!Ocl;B0rmPh{HbLg)o{d+|&%S)=#KZF|N(7p(Pp;jI_KDwm*tRY5B*WTU6Z6N;p4u;+s}? zq5onv*9DCdB$w}s66Ge(4&3i8Jc`pL& zBDeIi@p^5@xLD?NYwxK`2g{3UF7CGZ8!tY$>))Z2a_fCno7m=@o3Zif)K9beBVC?^ zEu44s^hxmtU;kHmuyIW*GyJwNAj&mL=IdPTZ}WIxmi#rCx>rJE_gS}nHf2RK9OwO2 z>(%_-B%+g){@O~OohhM;>C10}R?p}^EqBG7UhfdNC1x7ANu&4wnr}^~mz1V{eIGMj ziRaq7=p%oAUaow*Fp4L7^8D*=-B+w)DtHom6$IFGQtSmD`O!N)hG)*Q9o+a}hZ|8VU}=NK{yW~Xm@OviM>_j%d=FE^ED+}feCd?WKopO^m_PTJmo_S0QH^-Py$%f8#~+v~d) z<=(E6NGq<|_scYU`QE1Qs|)`;EBkyncz(blPw#hczX_Eu{qHAKkaD;DP3FaUkNm#o zfF{dM{ZILyZ#VD9g|lyB>k4MvsmzxsJ;!Gm{b!FcL&TEbMr~`q=YIR}Nk+dmy7iLj zeqJZuqQr;n-*Ubc^89#zsz>on(@*23#DzhMS5D<_NSr=Bl)YK&*H?|Bhd53j%iS)z ztaATMo%5 z_}7~WUtZ3Y-XOZ(rH50%cfPOm%P1>tyZmD=4<(t8`z=!5l#+Lz@!8uCfg2sJoiT`> z@hem&$IC~pI;Sk>=%W+zc@MM#=4iQvDfIkc)SYp0wcy?RaSn^`eHUtNmshWIK3=Vr z!4c*1L~K#wi_PcT{#$zf@banWE3yVoqPuImrW4cH5; zRR8ktFKls{^Lv`u#JMP~rk8*3D0P{wKXmQlS^wmnm4*EjQ3mqvSi&6bF#+ngLl!@S0}dH*L6Ide`Nh@u}#YztGn8>K~Z?^ z*J`!tzDb){_1K%=@jw0^q9Ks{_(h)n{W)7c`e>^sTv!msvAoZLll?~V&6C@VIkw-F zyrDd2@p6XtUb(NbZ$3MgWQyk>iEueCu_k`fu{CSOEW2l3IVFGSgq-4pC&>@QckO!m zODN&(mGU1oTc^*>Zu{i-rQ2qnWzK8=Kb@TO^;I^!W|@Ch&-=Sr>I;iP32!Uj1({nf zN~9I5>B~AsZWio6o9)_em8uzc<`|#0$JVL$jqlDq^V-q=>M{Rik2dogWZrPI;;ou7 zbG_47^?S)VznPZguW?T}Qt&G5_E~rN@U(k(LMyJxJzTv^{zBOg@i%X8_4zrM2W#i* zdN%)e-rv+-`TFK}j-2%e?u0Hi2=7ZzFW`;$`8`wU-S?NqN-b)Z+k)2K^_Hosw~(Fx za*bk!;Kb;j#K>u-wmBXZDPgN@Hw(IFeCdt$ZFl6Gf7GzuF=orbl}{AdFa0~XuU)ET zSt^g}aTh5W-!{j)hd0*wq<&%K4*%48_{_tFY&&B%yk1gzS*GJY|GASZCm-kJ=Kg(s z{aoX8zQu06Nx#0lG|$*~X2;)NTP?2VmHgFr@(NlXs}#JO)6!jOvty(1I}`ny*oq&l zr*}P2_4NEPH?YO}|H)-L3#%2hv}F3NIb#AX)^FZ2W5R?3M>>UPS{5&}yXfk&j^V3L zoZr4P0nazznDgU&^Bn85kIvjobDylMkUT@b+-~xewRS;zAOFN=YJK-TTp}d*_4t!# zJpA^bTkPxCUiSPkf8RU*xS~Lfzps6{pPyM~d3f=aFeRn!3UfDC+%0HY)a{0^cZY<@wNDa_y(7x`^YI*;po_=4Oyl^fcu$3uT--fl*56rP z_fJl^<{$LtW4NUGyU@JyoTq^Wzo0S|IOOJN)xo+ zI6U5`<>0ibFKhPdpSZT9=Rnz?hzWDsnf7eIvd~}wPi1-bnPAs!@0o@TY__Id^aaO(%ZMa$;G9O`C&+&{R~CdZJW;*KU=hd z!BgFD!V}Bu|CY$4vPT{eR;d5G?#eg6#g`|aJ|8FHs_=QIoVn*aW5G+#L6d&>@4P(s zNT;`ro|00Lrc~R$mLvCT_RRgc`Sl{Fx3}Lsy|Fj3ChV@;B}aXKwThj~BQ?H1F~3pE z!}WB-!|#zdWZ4onzC2!X?~cp@S7&kEqMIBoH#&G&ek2r0wRc%mu3Xm~IXQU7w3@uX zsgGq!R=-pEvSNCA7>neMGbeNPny-{}8%$^Ptc&|+c%XKv@vlYma^5HXF@L${3!_9y z_QRINcFRvOcrHKQcV*tV=X*J*h9MbLhh?|g5&kLM-Ky>X=D zowLEaj5CcJ-%q@jEs|fZd(eEbeR6sMZ*sh(-`!JBBaBO!rinhed3|xl?0Wgy_&C4k z@2=kb_R-~=citGjj6u!{7OoT<{kZivR=qWg}HG`Pn^5_hV)dc z_v=4AWarEAS^m0xWlp*Dl(lkB`y$wLyJzV{PB~T|R(&w_(ymP>JB91E$$#1OdlAd_ z%+26A_J}1azh>@KW?-55yLa|;v1-LrOYV5e-+#yFbl`l0=1j+niOCoKKD=@&&!MOJ z+)hh<4W}GW_U(cR&(hAS*XHhDB${%eT>iqL4IiHguU&a1S7i1bzd30}!Mp4oiq3DZ z{CCk^a&r>b!uQ`Eu2p5U;;(4Z+NSlT_NVKvEt#%}vAE@#r_(|rdprTeGF6P^VwKd_&4f zrfHQ`2H!6q7ZCxSUGVY$QSpR#?-VYo%7h$p>y2USHe<7_GV);Pp1&=vWw)nvO~S1e z@g|GeHw3H{$a-paW5emwtC}5ygqZitdhm3)eazvc!asYCm(AUxknl0sNan7zMfJQ} zGb@*Eo_BYh!eO;#7qY{wG9^MZOYd3iIQ*n_SJ|{PUXRtcJ}?!ZxL!@!Y;D4&Y+H^s zzozu@-b<*z1Z_H{b-Rfys}p6PEi~Eq*+jNKGvjq??)B`>uVQ#3c~H;4@lC_zq*SfI zJy|iwjv9UASaU#%ZXZ*|dyPia)QT3VRii$7NIlr@%9{NU2@IDLP zA7Jt}jH?6=?k6FR?E|X4;V}pXc{uhI)eeD;y_rBpXJn>QdwEh10 zxf?G^a-vmxD-7mYRzV7QU*7jEW$9r7u8@>x24}QS#J|zFl zcSYWcw{IP<96vi#b-v%38P!#|-Y3G+;<~!T$&7GZ}ciq1yv$Mv==*#WR;>%M`&SULK5;+||jqR(m zgn6xV+WSJG+sl?-UJ(ii|B(AoX)|HdyoKv`&fIU**v=Gx>G@<)bC=!KHgceY^X~7f z&Ahm%)ou-FFX!ADU&NJ)ZdZQ%*0X<`>*`%EWS>2JTl)X{y;}B=$Zz#B_4AKD+`tlY z#pTW{=_^~#$m}ed);0B+{_U?nI?^tCoxLRI?(eT@wkF3yi~HX%cRbBR@O=4QVtj<}B#4g{~%9q-cu<^sI zxdKslnbh=ar6LR5e=A2v+XJ+826%|!OiwsYA0^3SHeVdSsf z^(*kU_=*?KK1Vm#r}SG!Ob9NyrDB_#wo`vzBqLxg5|Nl2% zwe9PlFZKslJUc1Zay_@P`U}S_bGs8Cru*o8t}@Vzaf%IBnBNT(>#_>p zs2eZ5OXkN%)z5A;+LwOb@sH^X7AcEal}~*Gj4m{o z8~>kqMU|O%%BF)KuWUZQLeFIyukO*8Unc+f`FFG8`7K*a*!bnnoSA71x_H6g<|B(* zFk^MfqT7v!Q;xnm_w0%!d)WVgKl(f7PE=NAo^JYrfBk%O-!iiwzmxyYK7Foi=Ev=` z3amFBeC3~5wcyFKv$HFIKAnE@%$X;k%LYd?>;i>dD4d9tMX=g;;#_w{l%N>u*Tku9azXU19XApzS?Tg9Pn(j+!q%Yv#;O# zO-fNwQOl1}TwMJ4g@w+U7ZxzSzP=u`6;i9K;{@-5_0NNzMCypmtN&M7_VyO5Lr92- zeBBR4z3JXZH5))XL1&xgR(!u(4%+Q>*RwD*Dl()t=0Rr@L5*ugCfhaH#ZI*Xk_;E@kx1d zV&azU>wfcDSRIOrjGWu~Qg3cb1+7PQm#+m$Pq^j8(iZjH zKC5k9^z&wFl1sSsPR`04i+2lCBDv3R-9BOJlSf~7#PrSVy(1O&<$pxl^0!Byp4y!k zu6un|X!p@>@z3Y1-y39HPyj7fDSLl!t$(xhk-Y83MNd4WOtV~ETv#|cIc@&^cnrC- z>iKM=m&M`ng9T{B@~kLl_U7p6J7` zx5sst%yRzq$*tZU5@ZN@07QS*H(L{6bKTFk z*Q_P|9`wwvO|RE`6)wN@-JJ95)>nUe8y^4RMsokX38fn%bZmaV+5G0_X7=u*PJe%W zm9($3@mn4|pYJ;8x<=5^eH%A!^qFfl_5HrzeO#hiD-1xDTm8Di$H&&hel>oXegENw zOM&L$-Vr`qw6jzG9-IAmQTO84cF#ZjY1H|f$oTzE@p<0(`4=t(fL0uyxBq|U(o*l8 zZ@1l^VPC(`PI29)B}-J~>i-m0eS32<*x&YO8?SW6tt~4d>u`Ur3SE6{+1J&Es=6uf z*DzMtzMH*B@zXc|J?Mrh0gIJhCC3Uk}i=F6$ju1}%-) z{ww{|PqmG&XSWwvzjfUuXtizYMTNtj|K&gP*G}6aa`x?)9kD7k|MS;mp1!%IuC%dk zUjFZIZ*OkPm5$w2ve3Dm545LRSl#c1@ZbkN1f1O$Wy))7IdqIa) zcY$_)|2WUW!m=UdV$8cWp-pe+mmixMY@%{>Br?~Lhlk6k-&b#YUjMYq@)y^_UVii(P$t^K0fVINNG z@4v9Xk@?x#*~|U=y)CM~X!P5B;@DgLeOdheI?y?=YQD2T>7_Mf;f@F2-Wn=vhOg6c zZoBJzwtb)Cik!Vlfq$+rSK1gUzQ5F)to7&ijM_&G)@tR?XCALM{IvFAYgP2B@b%~R zRDK36+yrf6ILvRKa%Dx}m36Vv^Hm(m%FO=%zW@KnkH`H#zwiHlRy@9@Fg9h`>O=52iY=yJ{-N6$!KG`ls`Y|`ZL z3hV1Lm1p0u+GwlXzBlAW^P~4$b8k;u8@(NLx29G3J0Bk(p3KZl^SA9C*I%c;y0X&6 z)wS@)hs13;HxJFu-?wsshEkA=$c*EEW7kd94!^X~+x*m4$#c4a{Hs{GR|r?vCA{xR zpJ9}#6uYZLbJOk0UtcmoE1;J7%sh0Ukr{Lmjh&sHxv%y5$jxoj+12}+&$dt!H&UEK85mgRbN&!@BOt37z52N6)R~ zzpy4TbeZ2jneL;D7R@m&e&!Qb^U-x%&do*Y(>gjl9?EKH2z6dhNVrq|{==yQ>Nh`S zS{qH?zB+Y1^Y4`|kL)wu*H_Ore*5|)SJ(c@vu1hS-&Z@+By$qzJR6aBb3#Sj#OmMA z&foX&Wc}CY*W~64M(vy#^+W2U#kQ$)gXJIeF4X`0rs1E~3D$RezsG?N)~NsYSzgrH z#f8hXIPmrYcKMnKmzH`L=WqYL@Uu4WAMbd* zZgN#`#{T1n@0Q>9UG6ux;$f?Jh=8D=rcl=xTZ2!bFBdj6x1JHFMQUiNjhsEGc;C=jvz+QX zz8+jTqqh0Vr|Bmj7fmqut9k0irAU){v7=q0!GU^7$NOYK2T_;4z81L9iB;6a#igrf z_M>;Z!d6ecdB5I$xmYPEH}~~C&iZ-!!It@H$Cs>g3ES}D@+DWUuIZ~LU-o=|Z?B|j z))Y_#Y3PXE;x(0;`Qc&3yxM1q+iEVgt+`^G_1Au4^)HEN^?T0OxAD%jtd$B|Ew{8n z^KculvXWBMak*-rqXL2xm7XuN;Hh2cIa&Sk`v2T_emzie(z>%SuAuSEW9yriPwV0; zH`?p(U&3wr@64;Kt3hqGj~_p_w6=n_7cSZm5du0a#pV8gfvHw|y(8Y9d&QYP*J?x4 z-TQWjTE5?39KJqI>%{yM=gz4a8BN-Lzi#&Lce_D%D+;zAU6l6u+1V{SmkTeQW#j(i zt^d6*a}`rxe}6Ogv(EZGUu>G$`LEskDE{l)TW>!Qzpjd&9ZkK?qRhjHp<68zRW2vAT8}36g25upZ&&yhfdGV&p*GT@No!_prGcZ zyjK&C#?Q5>oD`O8U34p^bJh30;~7TxO>V#YT4lK*!(g7Wcf_~HPN$~ppP!w-@8qki zt0OlgG=7`VZBhD41hmre(h|=tIX8nq2QVm|T_NP~@zK%9y-AW+yVEzmUj4h!a#LQ{ z+iyGOzM1(?92e?ab3>OC9*d&U5zy?jrum1 zCRJPA32olFdD-LFuYb2^g6gFSip~{39<~?mzWYk%z{cd`72j^A&$Oxh-YV7 z<$GWK*4AwI%gcNvjZ!+w1O!2sz5b~-tTWBEa$W24_V(6CjlbXTUz6D{XY19>#v8FE zLvZ56iRQWSKOQvmgAPd24qta9#VGSR_~P7&@xF7drcP9L-&FYc*z8Xm>p_Rwdrnqc z6SH$t*40%jok4zHbfWd${{MAKN=iT8*Z;2$&fR|d?Xz>%@0XOnzxU-qKnD*NPYPi@J(eB}jbH}wjm6Zbb} zUDaB&cyVWc|MHZRlU(zDPt%Y0dw*{)=p5~Z3m0Y;fI>1v^2z(Er&GgkY))7mHvFOxPQm2Yy!S$KchOyl%TNk_Rr)drgw=r{u9?oa(a zlE!VV+~S`e_uGrT`Yo_**|MPZakk6+=le}o^S!b`Nl7VPGL-43dR)ar))+nU_j|v` zExgyKKEGxXs9)n4=;HE(Nh)$m{Tt9Fad~%UJUu-D4(EI(gD0r<4>G(1n|YkB_m|)!wgs zE}M3K-q}s5rwydO?)7v59fY%Pr=)S(hf~_?K}R0_`SXYCP1U;_8=J*+qc-H-wbBY( zv!YEH6bn;cKYiFPuco2V@$vC-@e^N*t>%K9R`>VU#~+XTS04oL;eT3XRs4*n^!2r; zll|=$=HA}Mn)f%Z`mO1<{QKwDL~gzk0bYz!q^uLWYl>a%uMMTI!HszC=G(GY z*Vmulka)P_K_h$Kk6)lWkl(z?+4tv@w`m{fbb?ENou16vcrypI>>p&}lqoI8A3ywh zJ^uN--S783JALldDJ2t=DeL$DlWG8ULF)h2Bqb+bH2`hVZ*~)VI;;9|uQ}*e?wK=Z zzPY)%ea;-2OP4Nb1TH#a8vSgJWijZAhkg6@nPgw<`SGJ-na@n8)6;aXD!{dKb8{!3 zon?CD`0>e;Cp#x3D0olP*}3fW9Mf#EyzR5!yveEi^U?j>982f8xOq~}E-p{K5BRJzE|$&=Hz^MWo7X7l%1d*m2SOKPo6!S_kGvZtfQ~L zPMSNn_hwEQb7x0K44Z7*wCrVWy;7hnO|42_{dst?zOu3sG!{8ar~Kj~*ZPWUo9EA$ z=ePYLkbZ7XASkT`Moei^Qc`N-l{OO)7H&S9X8nEF?YC(U54B!TnR$7+zq-0QJHzCY zE(r;sJDU?99qIh>>(?sK5f=YI*1dkVe16@c=ctzWmkI;px}bL{8BT_v2MiQYvCK*`#sZ_w%!}o*o_#*6n_GC~NDctgBiw ze8)p$kNx`k`sB%z2h%niCLCb6wmyFS0u3dlPy87v8z)5XDNszlFBkmc+~M2xFAS`O zgm!%1c3VSEsOIXk-P!NszRB4fnae93WMi_V;B60^@?*PyGdemrN^=ASH4RFZ)hm9l zy;GiYG+)hXkI%xW_t(sXmuH75DwqowdtD2iogZ8;&$D%PVriPQSJ}cY@~lw}-q|2(;cgWPDSrsiQ;00^9M>xKF4+k?FFrJ-AfExJA(B1?!*g z-%NRX_{8IUB-Z{sJ8ye6U*`OO1#kCHG^v~ubT_N{Fl(Bz+ktM*W=@;PYNiZy)(bU;@;lRmjC{-1gX~@cxk&c zg6YypWsb%3f6D7CmlhiBd-$-Rfb+QhJU_jsnjdcjczxgbc+>O8clWU_|2cnsKRD%z zFzvK3(^YzVx^l<2cZc6H`G-lJYuUPFzkT-|<$(8P)pvUZ#d5N5je}R-XO! zwu`K$trBnXab|dC_0?d0?fIS4|1uvCXj>VhPq$jvw!X(kFj1 zW8sl;_E43M%jP`u?yt=M6844uB}ZPRADa63*qii2v*pfDOm|QGd)<1!mI(P2>&ZEXtDi_$rwR3uR%x5az>%~5@DY-6gqjAfJca{+a`znb>)%yqGCL1y2$mE^h(?ab#rZvR7$ z=c(q;jN&`e>4i0qa_0OH5V_0!|JnA$bN762?RNIOT_@A5-@vKA-16h6|Aj9;`${zB z9W`Gha&%T$Yl}rt-248!xrf6HCx4ka`yHh+5KeC;@ z;(TXE$Du_ca?`A)GJMFr*1A9|&uIG^^%mL}OhI=DBFU+&HWUzKWqHap*> z4vDMHmlxkZBpN6kFZ@K-YL@-sBk?Cn8!Rmtwjbv1Qz&kXHKW;kYgdMR1%anyp5f)_;&K0{XEa#r%m3S$m+LQ zRDb52e;G38au%dGuW4B6qoTMrU$y*i)ww@grp@WKHfXu$*Lpr_W6n&UV{!ZmOyOKR zGvoGH^Ut~a&*aiCK83Fix_OoPN_A=Nh7GH>(xpIdfv3;(Qq=`=Ih{n6X65~{PVEjm+we44(`kGaPN}RZ;n(;QnjB(?`za^Y+s(UuUTJn6kS; zW>4N_*X-;k4|dLXb#dWh?$x$yvBM*jT&&nowp{`5KW4#)Us8?dxKE-qqSx%k;*Gu9-#&2!JhAD(~q z`F6hAAK8=A-yKTO^Pl;j??A;94n3b1zDn+B7lmbFhEuv%1ZSEVca*g4MV`uX_!QqgM+@ikhr3)`=#E=g6n7QH&*)4OL^ zwz#IeEqzp9ZtWCxudn|2H3@#bM?ZcAP05NEUTn#6zsv4P%6+ew$DYZPXU)7A&56Ov0A^9X`=HCk4c=sOL?bz>v5jHcYJX~Cm=l{n!w}0=im-24q_4yZ98ehr2+jeVf z_Uh#B4v)sf4^?~PrY`*O>$-5i@U|jL{;As4Pv3C_uMgk89r`Oy-(UImz~1)CiYpRw6%QDp4V-g}9{5_f zXuqBB``YtHZRquvL7%O~MIOFqcWrnRa))!J_!$$?>#gf!OCCm62yH&M_xN14J7Ge~ zYCQKs?o`|UU$IR|Nr@G7gW}F0MqkeA1$vd4%QEU0EbKRZu&-!h$%%v^_ypH+HiZqq*I5rnjx*LFAnS5 zElMA;%C zsKtJ`oqx3iXt~$SbCDt>9NO86^Po@4kMk_%iCcetwULM8z z6XWFUyz%zi=W*FrRtRQaUl&^F;^MMSxL3!k@#*W=-pBi7&;Nb%{{8vO{`PARfzLea zdQ$zUQ~jK5SJ}>(lK1ywL7@{EA#(EiuF}`V|M$OMw_C5W6>>b#`;J z_xH^>n+7^*O+!aZ=hXd&MLR40|NUP1`K&qU4~Fn?(CfW@9*!;>F4EkmB07fTlF=s>uuT27|>0JtlVNB9yIeC zB^}}D64zhHrwFn_i9y|eUdo3D2Wvi`HQ$nd|KF}T#t{Oksi}tP=VZ)sZX9@Uu(`9d z^I+1(70qBf?#!9vr@nFH#vTd7rZ!&bOFN6xnV6XJe(h3GRTUH$cXxAROFrJWGLW*wUR>zR9rSt2`S<-|Vq`?M!&J=7!rDKB)=&j<_2=H+c5!{YJ)f+VOG$~z z*6izHf$Km+*`J=Co@t&hcXwB5@{gjMsIT8&e!q5l;9|GJ-*2~H2KnO8pO6`^ z-~9Oa*k``o+$~#7?p43H-BtE>73kj9LyPW2fNVH#_ghERdzy-s)vTbUUMn@sZ}iDp zcXW3zj@en%BX9q2S!E8mFX+W6Bros3zwR$+>h13C^6R{dxh49KgKo1tGsm)c$M<{H zlD1VQiHV7jJ3~)xojPmQsnh!V&%C+08FWkK+1cjTD?3>u{QUSnKRcWJ_0`oqUoLrr zP7#S%8wNRE?dhu`3mMS(Tk@G1hIa~&%a**k5%^qHOi5Ey6Xb;{Q$+67evjQ%{(c?A z3o){nm;0wbIM6snE41s~-QBPE9C6CIzAm=r``z+8MW=Op9l~R|GCTW*XV|`HN!Rzf|*x17RB#eOJYQc#~W$q+Nf@OU=(|waL=J zsT1ta9hrP|-q+U=yVd4~oiADbrqVrX?wc7O)n=dmw)A^$_}y`?th-ZvGUB_-qlG555G*d{+T*^^0n_SiNEj0oO=^_ z?9k-U$_1W3Ojmwi|J?Z8TXd%LA-8X^G8|xwD~3{(0@uRmwZX-^ln0)kQVcM?BbiTe8n!4-XGp7CvGz z&ARen?e=?z9u}2Mn8kmmP_*MENACZGJYk>k~L{j#&!d7E!`^!24(Sm1bOp6%>YQ?-rr@7cU% zQBo>$o)OyAq_3&jd9az?IO#~o>-yQE(`R$VRt5e%vHeW6#Ch4P(E==UZfulFZvUNO zQm^*%dC}ClvfRrze<~?FHQ~UiDP2cwuFiZEcVPGAa`ydm>}M`i>)-Q(;c3zLjW2eu zTynj>O?}o>`}&>pjMvQEK0)P=N6f~iPqs-rc2{t1PCL3$`KI$zBe!qr)jM{dSGaYR zN3rsBP~DBWz5d6tw%xM&;B?&JS?y2mxIHzt$KCq-whPCYcDd=K9y`UdXxFu)9dpli zf4}&qlkd6yFQrtAVuN{CR#nx{K`QCcy8FgUV5gCQap_kv73KJH&9GdTwI%QN ztiHLcH>$m}Hp*M+X=<;$?AAq_UvE5;tU?|y@BLknruyNYd3ENsg{HDr_u8v}%Y-eR z`e>e9sll|p{q9!x{Wb_1Ok3jF|7*v!e<$|o=r4JnH1WS|2#>)ZSKc`)Ya-k~vx(-P z-~-*5QIfS0)VT6$d|JALv!`Q8>%kL;u2x&j+|fG4%vV!3;YVylNm0scwo_#*I=<#y z*NT6wXT$!j_|#KYXJ4%;ADNR47tJz1Ui?7dr5`urzHQxYo0X#BiD%!ho4TqsnbBw81KX+IZnq2>g81G%bl5a;o^5sT_9^;u<^LW&yDuN07yO)0 zt1>uZ_hW@>^W0^wuciJxO?Y_I`NG!ItgDPWI!^eyw=aCEZ&}FPc;{O{@HGFQZHBYG zKVNz6dvNBT-79^5h!{`UV$6Ab?!ySi{SO2lJh_>#va{m6-2JVh+y@g19;P?;bn9Jg zR=)0?aZ>9_=bx`!Io8WJpPRS%MbUq=l?Qu{=N?a3<@xu-iQ=_lW;=OVM?o zy)AU6K{C_r-tteUPVDDvNas5?IqG%UsTcEZDk&8SUpleqkL7Cqm(i077*z9eJyh7v z_}&h_n8q-flOcXHr$La`9=myecYADozI@A+yV-{3T}xUN?e9pQ`?l@L+hW7*`=%-! zNHUu+DUtm^QCVGD_tZ~YwKl%peKN;>Ugz=3S*DX8S-dhzVlsH{pKkJ#NkAZkX??a{ z@pZXlos*yFeva6n-@Uo}=*umW6aT;UIl{#|iy`3ozF#cyI;(O-EM71$2pj^P3-By0 zNh8&6ckvVXro19|*0(xJN=3n-G3ZI^+MP>_pKpA8!|vJoy{+qdwF|@BkA<%#aM{geO9zJFoiR{iKnN|hfEO8wY1X~*U-GD-XA`M#cD zEc3vQt#qBswT*Arblh)$eL(Ws)o+J4OvY!o<&!e57u@MI{di{H$qx%N zzsTR3xgdVOB5Szft}xLY(^G#EXK(JyO~1&0`lHI!^q02;-mQ7@Y~)9QRN4C||p5PuLo<$D+E| zUluf6JvBA{$eV+S<;hzk^CE16gM%yo|NZXi<8$Slf|Anpxo%=lR~bjGiP4FUT(fPz z?5#Cnx9y|W9sT9+Z(@?P^(DiWj*dG=>aT|~fqEN|&cej~zU>R2<`-?sf2;rI^rxhQ zoyP1sj;b2*d$~fr*#E!XS@}jYCu(cgS@lCN|2n9!r|miUY*X#ayF1RLu07qi-}LX( zq79YDwpYyhw!m@5`dKk9E_qo}kyG}cbeei>`@`LD-KX6;eQ{52u$HLxN%i-APcF~r z|MQnGgIo1Lhbt{;QaGoO$`*tlG$a?dgRjhOD5re5>nDCc7WM zBv(iT@ys@D=$cqA9DDlEzq7|TO#WZtRPdPl;K|w*;wrTZOs(qd_&9c7UeZ978{{EjWOJ{!%Wb-g{6Z#y(d`$R8NntIZ1+uWV^eO34VGq$<7#%5lR@|nMB zjCNPGi!1)|hrAGW{_*dp%m=yDLr-?vT+nIy%e{Q=?0IXH&-^Q4YqQ#Fx%CJ8r(6Hz z_e!&GtU1Or?Y+mMu(i=QHY#&|e#>vPC0pd$n!h!luZpcQ(K=gx|IOn2(zP1trJM4P zTFts&yM3Q$C#dv1CHmR4;K2jFZ9ionKX@g%#wK7=-1%)v1?Nqs9dp0*cIvkRqvpTw zYdZcfK7VP^8u8D&Hu}unI@MfW@9>fD%P&{n(9pWg6Joaf!#_9uf2pg3xBEqkC9ue> z`Ta(j>0NE_ZtY(a-&A)LYWEv%IsJA+k??uDi>*O!>i>?{&AoSSmcPBy_N&}?;?-GZ zU3+kP;o}I2Z%n#U*AsrOGl;UiFq!?bx#5$&PnVQ+94bo?zU_7G!NC)^rWEhoSiR@@ zJm%W}i>vrReYQi3Vs^&pv#nnZhby~(av06;ro@%y(=T(VbhzJ%v2;( zS^uZ^f<_S}$^2M>2@kg9t3o?3> z@Kya#!h_kBr!A&ERd`)t6MyDj>%k|J=d0TKZ8CbZ-#1^=^K4XF-K`1B#^4W}5*fBPoyO+!Dc5r4kw#gNW#{(sxqvmmTVnA~h! z>XLbCI^W`wtpeZlW*uFWYQHJR)JEWP=cR*7SIU&kTED#Rc)xXBO7I^^o-@q<NEK<`o zm-A>wfa>I{-)7ypzJjNX>B_&GH*bOtjca6P&jL5R&OP{AwO7meVA+=!fw02-4nx0i z`{CA$W&buWwDn{6(}~b6c-#U~9Z%ktxOdHGzPUHv z>a8)8c3aMDEtWH_$@IazS+h?4e!t&8DQQs|o1oy%cO{M8G1YF|VY8nq=|}&39W5!f zF-o5!-%b0P{mEjn*>CgL%wfH~{&m{Mq~L7Ooa%?^5ul+hlym8yth#8~U37F)UvB!* zZHpsx#OC;^Cnzq-sazGl+GWGnPy5?bWbYr{bo906RjpZWsejkKHnZ3qbYn}B`9s~= z!C~i{!~_L@GWY7766My2o}9HUS?6D(_SC$5x68LzP1^aZ*o|-YF~j5cXZU{%no<5v z>AThX{hzQM z?!SM&yePbJa?|tscGBJUic5QE_@;fZ*kC;;#=tN#vg};-)yJ0B+1BT{C(XN@cl_Ot z*FVxZ*ZHhIm%DOF8)yZBifr4o)t4upyH~6B^MM_MQ}d_(mumTur}%eo{&+ItY*;jV zT-6HAb$7dG?z-A;eOe|pG%xwr_HG9I8ymCqZeLsXm?7@ntG`NfUrkZ?{6I_XoZXq+ ziH&FC3MMU&d+YY?*H53=J)K5VwI}bXx0moLu0JDeqVYKK?4DMYhwhKMM5nwLbiUs9 zuzueozwJ`~E-p{}Gg1QoDRNhoZ_0Qkv#;*GXwv@em;au8zCt#qX${N0-)Z`1t+p(4 zKK{EPfBxaFy_s+3ZRs&8U7T@ak2UX{<>zg^W<9X7v8)oQwGK&sI%}^*!KU}qf6jQ9 zadz%m>x)}UnA-hkuPBTbNp1L*TV+)BMPQfPJQck>ZqI0ODzuzi`UggAZe?Pw-{9yl;QMxJ54teCEH-t1eOPw!3-HZ|CnzSL%yiSSe{bC!AHcb1-Oe+y)e`D{leV8pK6p=f&EGuJqpQpx z-c0OoPFJ<`Q%>XAap&0P&tJ~|zg|3P>*wv4Z8k}TJn_zva@Nt*LzDvoYCV>4u`8 z=cjb$INW;0`)9*&CZ#P z&oyeEotx|Z|KDFuK0Y-ot64^=r$la)ezlObh)LKS`TxsS?>*AX5BDC;Vw__!M{JFX zVd4oHt3CfD5-T>u8Su9s=5x~y>z;U}`L{>;;h%x0d6y-u3egE&@M=r#@3Kda9)Ye& z+qiKfXuY^>tcy#Y#a*lA6F_?<-rm}(Y-To1P}!~EcJ6l2@FEiv6W5(xQo3O|)ArZB z6?%WH=v}6P*cvefb=_(A?EHLxP`L8&ECCIN>PCmTYAQT> z{CK8eGF#~CFjX_NX|qhTS3yQkug^dIH0jTekD#42zrMb{ywrO-Q>ESGvyX0UOa=|| zojKzJ+8@-;CmSLnC>R(qMf&}fmBFAD+n{BOcYgMyZC;nXD{j-KO*3q(&1ReBCY_j| z2wFY(RUCBloMuJY`+L4VK0Le2-!F^ap6BA`wo2prgws#0%HxvO$L*D{tFc(Pa3N%n z((B;1f`?AOzQ6B3o2G1KHS5EN0@0^hw>PD7UtaFN+^tvY$gyKZuh(veI=!|1^wUSp z{B{p|&F?8_X?5wv?h=rYSaIl&vwOdss-7O-?z`u96g~#6?}NJikhZPuTt(+LgVI+a zpyigU!`7DA-4#7?;zYu?H#c)`ZfdNg*-aC>PK|V!Ij((3YtlP<(k^+*0u|DJdywL)6+YFD^PiH~!b@ zJKGF&27A`^b!T@JK3);GcUKp9-C59=op{(Nw5wa!Gk_sJ;g z>G8E6cFf7q$-TWT6f~)o7?@|b+<*Qv(4w=``umr}?k?MsclXyaJAQp-<>tuEX&Xyl zhqduaAKU-$>-t5%|KtjS!hdHBXys@9|9?l0AHTdd+FZ&cqaZkU|LLcX+U4sKK0Z2X z^XY^#=;-qY4-!gwKoKnRbFZrRGy!4Z=94Ll-23G~tMY#4-rG^AY@BvR!N_RR#^mEc zTeCt-S-`4yifq4q_PBignF)%{D}tBveZN<&f3!+@I{Wk5-kB?ij zu6p(AgQqp7zdke57-Z*p`~P!_o}Loj=`d&7v|}G09 Date: Tue, 8 Jan 2019 16:45:50 -0800 Subject: [PATCH 523/622] [XLA] Use Iota in Cholesky rather than a std::iota constant. NFC intended, just a cleanup noticed in passing. PiperOrigin-RevId: 228424323 --- .../compiler/xla/client/lib/cholesky.cc | 39 +++++++++---------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/tensorflow/compiler/xla/client/lib/cholesky.cc b/tensorflow/compiler/xla/client/lib/cholesky.cc index e100d47922..414bd1494c 100644 --- a/tensorflow/compiler/xla/client/lib/cholesky.cc +++ b/tensorflow/compiler/xla/client/lib/cholesky.cc @@ -68,29 +68,26 @@ XlaOp CholeskyUnblocked(XlaOp a, PrecisionConfig::Precision precision) { auto body_fn = [&](XlaOp i, absl::Span loop_vars, XlaBuilder* body_builder) -> StatusOr> { - Shape col_shape; - Shape row_shape; - for (int64 d : major_dims) { - row_shape.add_dimensions(d); - col_shape.add_dimensions(d); - } - row_shape.add_dimensions(1); - row_shape.add_dimensions(n); - row_shape.set_element_type(a_shape.element_type()); - auto mask_zeros_row = Zeros(body_builder, row_shape); - - col_shape.add_dimensions(n); - col_shape.add_dimensions(1); - col_shape.set_element_type(a_shape.element_type()); - auto mask_zeros_col = Zeros(body_builder, col_shape); - - std::vector mask_vector(n); - std::iota(mask_vector.begin(), mask_vector.end(), 0); - auto mask_range = ConstantR1(body_builder, mask_vector); + std::vector row_shape_dims(major_dims.begin(), major_dims.end()); + std::vector col_shape_dims(major_dims.begin(), major_dims.end()); + row_shape_dims.push_back(1); + row_shape_dims.push_back(n); + auto mask_zeros_row = + Zeros(body_builder, + ShapeUtil::MakeShape(a_shape.element_type(), row_shape_dims)); + + col_shape_dims.push_back(n); + col_shape_dims.push_back(1); + auto mask_zeros_col = + Zeros(body_builder, + ShapeUtil::MakeShape(a_shape.element_type(), col_shape_dims)); + auto mask_range_row = - Broadcast(Reshape(mask_range, {0}, {1, n}), major_dims); + Iota(body_builder, ShapeUtil::MakeShape(S32, row_shape_dims), + /*iota_dimension=*/n_dims - 1); auto mask_range_col = - Broadcast(Reshape(mask_range, {0}, {n, 1}), major_dims); + Iota(body_builder, ShapeUtil::MakeShape(S32, col_shape_dims), + /*iota_dimension=*/n_dims - 2); auto body_a = loop_vars[0]; auto body_l = loop_vars[1]; -- GitLab From 2c7444faf3d23ddb959f887b5589ec53ca431e9d Mon Sep 17 00:00:00 2001 From: Michael Kuperstein Date: Tue, 8 Jan 2019 16:53:15 -0800 Subject: [PATCH 524/622] [TF:XLA] Move some bridge implementations over to the new DS/DUS API Move some bridge and xla client library op implementations over to the new span-of-scalars API for DynamicSlice and DynamicUpdateSlice. PiperOrigin-RevId: 228425409 --- .../compiler/tf2xla/kernels/image_ops.cc | 17 ++++--- .../compiler/tf2xla/kernels/stack_ops.cc | 12 ++--- tensorflow/compiler/xla/client/lib/slicing.cc | 49 ++++++++++--------- .../xla/client/lib/triangular_solve.cc | 16 +++--- 4 files changed, 49 insertions(+), 45 deletions(-) diff --git a/tensorflow/compiler/tf2xla/kernels/image_ops.cc b/tensorflow/compiler/tf2xla/kernels/image_ops.cc index 96ddd42e2a..a31b5a2cfd 100644 --- a/tensorflow/compiler/tf2xla/kernels/image_ops.cc +++ b/tensorflow/compiler/tf2xla/kernels/image_ops.cc @@ -13,6 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ +#include "absl/types/span.h" #include "tensorflow/compiler/tf2xla/kernels/gather_op_helpers.h" #include "tensorflow/compiler/tf2xla/lib/util.h" #include "tensorflow/compiler/tf2xla/xla_helpers.h" @@ -351,24 +352,26 @@ struct SuppressBodyFn { auto num_outputs_so_far = values[1]; auto iou_mask = values[2]; auto included_iou = values[3]; - auto zero_r1 = xla::ConstantR1(builder, {0}); + auto zero = xla::ConstantR0(builder, 0); // Determine if current elem is active using a slice. - auto row_idx_r1 = xla::Reshape(row_idx, {1}); - auto active_elem = xla::DynamicSlice(included_iou, row_idx_r1, {1}); + // TODO(b/118437727): The only reason we need an explicit vector is because + // some old GCCs can't deduce the right type for MakeConstSpan, and + // providing a single-value initializer list directly uses the wrong + // overload. Delete this once the deprecated overload is gone. + std::vector row_idx_vector = {row_idx}; + auto active_elem = xla::DynamicSlice(included_iou, row_idx_vector, {1}); active_elem = xla::Reshape(active_elem, {}); // Increment output count iff current elem is not suppressed. num_outputs_so_far = xla::Select( active_elem, num_outputs_so_far + xla::ConstantR0(builder, 1), num_outputs_so_far); // Slice out the row_idx. - auto starts = xla::ConcatInDim(builder, {row_idx_r1, zero_r1}, 0); - auto row_iou = xla::DynamicSlice(iou_mask, starts, {1, num_boxes}); + auto row_iou = xla::DynamicSlice(iou_mask, {row_idx, zero}, {1, num_boxes}); // Remove the diagonal from consideration. An elem cannot suppress // itself. - auto update_starts = xla::ConcatInDim(builder, {zero_r1, row_idx_r1}, 0); row_iou = xla::DynamicUpdateSlice( row_iou, xla::ConstantR2FromArray2D(builder, {{false}}), - update_starts); + {zero, row_idx}); // Create a suppression by inverting polarity. row_iou = xla::Reshape(row_iou, {num_boxes}); auto supp_mask = xla::Not(row_iou); diff --git a/tensorflow/compiler/tf2xla/kernels/stack_ops.cc b/tensorflow/compiler/tf2xla/kernels/stack_ops.cc index 02d71a3942..d0c5231e84 100644 --- a/tensorflow/compiler/tf2xla/kernels/stack_ops.cc +++ b/tensorflow/compiler/tf2xla/kernels/stack_ops.cc @@ -146,9 +146,9 @@ class StackPushOp : public XlaOpKernel { xla::XlaOp value = ctx->Input(1); // start_indices of the DynamicUpdateSlice are [index, 0, 0, ..., 0]. - auto start_indices = - xla::Pad(xla::Reshape(index, {1}), xla::ConstantR0(b, 0), - xla::MakeEdgePaddingConfig({{0, elem_shape.dims()}})); + std::vector start_indices(elem_shape.dims() + 1, + xla::ConstantR0(b, 0)); + start_indices[0] = index; TensorShape slice_shape = elem_shape; slice_shape.InsertDim(0, 1LL); @@ -202,9 +202,9 @@ class StackPopOp : public XlaOpKernel { OP_REQUIRES_OK(ctx, resource->SetValue(xla::Tuple(b, {ta, index}))); // start_indices of the DynamicSlice are [index, 0, 0, ..., 0]. - auto start_indices = - xla::Pad(xla::Reshape(index, {1}), xla::ConstantR0(b, 0), - xla::MakeEdgePaddingConfig({{0, stack_shape.dims() - 1}})); + std::vector start_indices(stack_shape.dims(), + xla::ConstantR0(b, 0)); + start_indices[0] = index; auto slice_shape = stack_shape.dim_sizes(); slice_shape[0] = 1LL; diff --git a/tensorflow/compiler/xla/client/lib/slicing.cc b/tensorflow/compiler/xla/client/lib/slicing.cc index 611fffba8d..77145ba7d4 100644 --- a/tensorflow/compiler/xla/client/lib/slicing.cc +++ b/tensorflow/compiler/xla/client/lib/slicing.cc @@ -14,6 +14,7 @@ limitations under the License. ==============================================================================*/ #include "tensorflow/compiler/xla/client/lib/slicing.h" +#include "tensorflow/compiler/xla/client/xla_builder.h" namespace xla { @@ -51,17 +52,17 @@ XlaOp SliceInMinorDims(XlaOp x, absl::Span start, XlaOp UpdateSlice(XlaOp x, XlaOp update, absl::Span start) { XlaBuilder* builder = x.builder(); return builder->ReportErrorOrReturn([&]() -> StatusOr { - // TODO(phawkins): make int64 work on all backends, remove the int32 cast. - std::vector start_as_int32(start.begin(), start.end()); - auto start_constant = ConstantR1(builder, start_as_int32); TF_ASSIGN_OR_RETURN(Shape shape, builder->GetShape(x)); const int64 n_dims = shape.rank(); - TF_ASSIGN_OR_RETURN(Shape start_constant_shape, - builder->GetShape(start_constant)); - const int64 start_length = - ShapeUtil::GetDimension(start_constant_shape, -1); - TF_RET_CHECK(start_length == n_dims); - return DynamicUpdateSlice(x, update, start_constant); + TF_RET_CHECK(start.size() == n_dims); + + // TODO(phawkins): make int64 work on all backends, remove the int32 cast. + std::vector start_as_int32(start.begin(), start.end()); + std::vector start_ops(start.size()); + for (int i = 0; i < start.size(); ++i) { + start_ops[i] = ConstantR0(builder, start_as_int32[i]); + } + return DynamicUpdateSlice(x, update, start_ops); }); } @@ -90,18 +91,17 @@ std::vector ConcatVectors(absl::Span xs, return output; } -XlaOp PrependZerosInMajorDims(XlaOp x, absl::Span starts) { +StatusOr> PrependZerosInMajorDims( + XlaOp x, absl::Span starts) { XlaBuilder* builder = x.builder(); - return builder->ReportErrorOrReturn([&]() -> StatusOr { - TF_ASSIGN_OR_RETURN(Shape shape, builder->GetShape(x)); - const int64 n_dims = shape.rank(); - auto zero = Reshape(ConstantR0(builder, 0), {1}); - std::vector padded_starts(n_dims, zero); - for (int i = 0; i < starts.size(); ++i) { - padded_starts[n_dims - starts.size() + i] = Reshape(starts[i], {1}); - } - return ConcatInDim(builder, padded_starts, 0); - }); + TF_ASSIGN_OR_RETURN(Shape shape, builder->GetShape(x)); + const int64 n_dims = shape.rank(); + auto zero = ConstantR0(builder, 0); + std::vector padded_starts(n_dims, zero); + for (int i = 0; i < starts.size(); ++i) { + padded_starts[n_dims - starts.size() + i] = starts[i]; + } + return padded_starts; } } // namespace @@ -119,7 +119,7 @@ XlaOp DynamicSliceInMinorDims(XlaOp x, absl::Span starts, .subspan( /*pos=*/0, /*len=*/n_dims - sizes.size()); - auto padded_starts = PrependZerosInMajorDims(x, starts); + TF_ASSIGN_OR_RETURN(auto padded_starts, PrependZerosInMajorDims(x, starts)); auto padded_sizes = ConcatVectors(major_dims, sizes); return DynamicSlice(x, padded_starts, padded_sizes); }); @@ -127,8 +127,11 @@ XlaOp DynamicSliceInMinorDims(XlaOp x, absl::Span starts, XlaOp DynamicUpdateSliceInMinorDims(XlaOp x, XlaOp update, absl::Span starts) { - auto padded_starts = PrependZerosInMajorDims(x, starts); - return DynamicUpdateSlice(x, update, padded_starts); + XlaBuilder* builder = x.builder(); + return builder->ReportErrorOrReturn([&]() -> StatusOr { + TF_ASSIGN_OR_RETURN(auto padded_starts, PrependZerosInMajorDims(x, starts)); + return DynamicUpdateSlice(x, update, padded_starts); + }); } } // namespace xla diff --git a/tensorflow/compiler/xla/client/lib/triangular_solve.cc b/tensorflow/compiler/xla/client/lib/triangular_solve.cc index 6061e64656..c2f31742e9 100644 --- a/tensorflow/compiler/xla/client/lib/triangular_solve.cc +++ b/tensorflow/compiler/xla/client/lib/triangular_solve.cc @@ -165,10 +165,10 @@ XlaOp InvertDiagonalBlocks(XlaOp diag_blocks, bool lower, bool transpose_a, // The first or last diagonal element should be set to 1 instead of -1 // though, since we never update it auto pos_one = Reshape(One(builder, shape.element_type()), {1, 1}); - auto start_index = (lower) ? 0 : block_size - 1; - auto output_block = DynamicUpdateSlice( - neg_identity, pos_one, - /*start_indices=*/ConstantR1(builder, 2, start_index)); + auto start_index = ConstantR0(builder, (lower) ? 0 : block_size - 1); + auto output_block = + DynamicUpdateSlice(neg_identity, pos_one, + /*start_indices=*/{start_index, start_index}); // Broadcast diag([1, -1, -1, ...]) to every block XlaOp output = Broadcast(output_block, @@ -211,12 +211,10 @@ XlaOp InvertDiagonalBlocks(XlaOp diag_blocks, bool lower, bool transpose_a, auto body_out = GetTupleElement(input_tuple, 1); auto body_input = GetTupleElement(input_tuple, 2); - auto zero = ConstantR1(bodyb.get(), 1, 0); + auto zero = ConstantR0(bodyb.get(), 0); auto j = (lower) ? i : ScalarLike(i, block_size - 1) - i; - auto start_indices = - ConcatInDim(bodyb.get(), {zero, Reshape(j, {1}), zero}, 0); auto input_row = - DynamicSlice(body_input, start_indices, + DynamicSlice(body_input, {zero, j, zero}, /*slice_sizes=*/{num_blocks, 1, block_size}); // We want -L21 L11^{-1} @@ -230,7 +228,7 @@ XlaOp InvertDiagonalBlocks(XlaOp diag_blocks, bool lower, bool transpose_a, precision_proto.add_operand_precision(precision); auto update = -DotGeneral(input_row, body_out, dnums, &precision_proto); - body_out = DynamicUpdateSlice(body_out, update, start_indices); + body_out = DynamicUpdateSlice(body_out, update, {zero, j, zero}); auto next_i = i + ScalarLike(i, 1); Tuple(bodyb.get(), {next_i, body_out, body_input}); -- GitLab From c842394896de32c4160fc0f5f94dabf06890476d Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Tue, 8 Jan 2019 16:57:19 -0800 Subject: [PATCH 525/622] Support not fully defined batch dimension in the inputs to group_norm. PiperOrigin-RevId: 228425992 --- .../layers/python/layers/normalization.py | 18 +++++++++++++----- .../layers/python/layers/normalization_test.py | 9 +++++++++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/tensorflow/contrib/layers/python/layers/normalization.py b/tensorflow/contrib/layers/python/layers/normalization.py index 11033a2e9c..76b03ff514 100644 --- a/tensorflow/contrib/layers/python/layers/normalization.py +++ b/tensorflow/contrib/layers/python/layers/normalization.py @@ -186,7 +186,7 @@ def group_norm(inputs, Args: inputs: A Tensor with at least 2 dimensions one which is channels. All - shape dimensions must be fully defined. + shape dimensions except for batch must be fully defined. groups: Integer. Divide the channels into this number of groups over which normalization statistics are computed. This number must be commensurate with the number of channels in `inputs`. @@ -249,13 +249,21 @@ def group_norm(inputs, """ # TODO(shlens): Support partially defined shapes for the inputs. inputs = ops.convert_to_tensor(inputs) - original_shape = inputs.shape if inputs.shape.ndims is None: raise ValueError('Inputs %s has undefined rank.' % inputs.name) if channels_axis > (inputs.shape.ndims - 1): raise ValueError('Axis is out of bounds.') + # Use dynamic shape for not fully defined dimensions in the inputs. + dyanmic_shape = array_ops.shape(inputs) + input_shape_list = [] + for i, dim in enumerate(inputs.shape): + if dim.value is None: + input_shape_list.append(dyanmic_shape[i]) + else: + input_shape_list.append(dim) + # Standardize the channels_axis to be positive and identify # of channels. if channels_axis < 0: channels_axis = inputs.shape.ndims + channels_axis @@ -289,8 +297,8 @@ def group_norm(inputs, # Determine axes before channels. Some examples of common image formats: # 'NCHW': before = [N], after = [HW] # 'NHWC': before = [NHW], after = [] - axes_before_channels = inputs.shape.as_list()[:channels_axis] - axes_after_channels = inputs.shape.as_list()[channels_axis+1:] + axes_before_channels = input_shape_list[:channels_axis] + axes_after_channels = input_shape_list[channels_axis+1:] # Manually broadcast the parameters to conform to the number of groups. params_shape_broadcast = ([1] * len(axes_before_channels) + @@ -369,7 +377,7 @@ def group_norm(inputs, outputs = inputs * gain + offset # Collapse the groups into the channel dimension. - outputs = array_ops.reshape(outputs, original_shape) + outputs = array_ops.reshape(outputs, input_shape_list) if activation_fn is not None: outputs = activation_fn(outputs) diff --git a/tensorflow/contrib/layers/python/layers/normalization_test.py b/tensorflow/contrib/layers/python/layers/normalization_test.py index c8d3c91b10..9a85084b23 100644 --- a/tensorflow/contrib/layers/python/layers/normalization_test.py +++ b/tensorflow/contrib/layers/python/layers/normalization_test.py @@ -221,6 +221,15 @@ class GroupNormTest(test.TestCase): normalization.group_norm(inputs, channels_axis=-1, reduction_axes=[-3, -2]) + def testParamsShapeNotFullyDefinedBatchAxis(self): + height, width, groups = 3, 3, 4 + inputs = array_ops.placeholder(dtypes.float32, + shape=(None, height, width, 2*groups)) + output = normalization.group_norm(inputs, channels_axis=-1, + reduction_axes=[-3, -2], groups=groups) + self.assertListEqual([None, height, width, 2 * groups], + output.shape.as_list()) + def testCreateOp(self): height, width, groups = 3, 3, 4 images = random_ops.random_uniform((5, height, width, 2*groups), seed=1) -- GitLab From dde706e1d3561656f0a6d842a79c50cfbe1ad508 Mon Sep 17 00:00:00 2001 From: Siju Date: Wed, 9 Jan 2019 06:48:03 +0530 Subject: [PATCH 526/622] Changed result.error_message() to result --- tensorflow/core/common_runtime/gpu/gpu_debug_allocator.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tensorflow/core/common_runtime/gpu/gpu_debug_allocator.cc b/tensorflow/core/common_runtime/gpu/gpu_debug_allocator.cc index 2879b00f55..f8c67b60c3 100644 --- a/tensorflow/core/common_runtime/gpu/gpu_debug_allocator.cc +++ b/tensorflow/core/common_runtime/gpu/gpu_debug_allocator.cc @@ -46,7 +46,7 @@ bool CheckMask(se::StreamExecutor* exec, void* ptr, int64* mask) { Status result = exec->SynchronousMemcpyD2H(gpu_ptr, MASK_BYTES, tmp); if (!result.ok()) { - LOG(FATAL) << "Could not copy debug mask, " << result.error_message(); + LOG(FATAL) << "Could not copy debug mask, " << result; } bool ok = true; @@ -66,7 +66,7 @@ void InitMask(se::StreamExecutor* exec, void* ptr, int64* mask) { se::DeviceMemory gpu_ptr{se::DeviceMemoryBase{ptr, MASK_BYTES}}; Status result = exec->SynchronousMemcpyH2D(mask, MASK_BYTES, &gpu_ptr); if (!result.ok()) { - LOG(FATAL) << "Could not copy debug mask, " << result.error_message(); + LOG(FATAL) << "Could not copy debug mask, " << result; } } @@ -176,7 +176,7 @@ void* GPUNanResetAllocator::AllocateRaw(size_t alignment, size_t num_bytes) { Status result = stream_exec_->SynchronousMemcpyH2D(&nans[0], req_size, &nan_ptr); if (!result.ok()) { - LOG(ERROR) << "Could not initialize to NaNs, " << result.error_message(); + LOG(ERROR) << "Could not initialize to NaNs, " << result; } return allocated_ptr; @@ -192,7 +192,7 @@ void GPUNanResetAllocator::DeallocateRaw(void* ptr) { Status result = stream_exec_->SynchronousMemcpyH2D(&nans[0], req_size, &nan_ptr); if (!result.ok()) { - LOG(ERROR) << "Could not initialize to NaNs, " << result.error_message(); + LOG(ERROR) << "Could not initialize to NaNs, " << result; } } -- GitLab From fa8ed584884fae406e2398718bbcd6e20498c7a2 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Tue, 8 Jan 2019 17:24:16 -0800 Subject: [PATCH 527/622] Mark certain tests in export_output_test as v1 only as export_output.as_signature_def does not support v2 due to its dependent method saved_model.build_tensor_info not working in eager. Also raise a runtime exception inside build_tensor_info when running in eager. PiperOrigin-RevId: 228429679 --- .../saved_model/model_utils/export_test.py | 255 +++++++++--------- tensorflow/python/saved_model/utils_impl.py | 6 + tensorflow/python/saved_model/utils_test.py | 7 + 3 files changed, 140 insertions(+), 128 deletions(-) diff --git a/tensorflow/python/saved_model/model_utils/export_test.py b/tensorflow/python/saved_model/model_utils/export_test.py index 776bfff886..ef512150a2 100644 --- a/tensorflow/python/saved_model/model_utils/export_test.py +++ b/tensorflow/python/saved_model/model_utils/export_test.py @@ -22,7 +22,6 @@ import os import tempfile import time -from tensorflow.python.eager import context from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops @@ -52,110 +51,110 @@ ops.register_tensor_conversion_function(LabeledTensorMock, class ExportTest(test_util.TensorFlowTestCase): + @test_util.deprecated_graph_mode_only def test_build_all_signature_defs_without_receiver_alternatives(self): - with context.graph_mode(): - receiver_tensor = array_ops.placeholder(dtypes.string) - output_1 = constant_op.constant([1.]) - output_2 = constant_op.constant(["2"]) - output_3 = constant_op.constant(["3"]) - export_outputs = { - signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY: - export_output.RegressionOutput(value=output_1), - "head-2": export_output.ClassificationOutput(classes=output_2), - "head-3": export_output.PredictOutput(outputs={ - "some_output_3": output_3 - }), - } - - signature_defs = export_utils.build_all_signature_defs( - receiver_tensor, export_outputs) - - expected_signature_defs = { - "serving_default": - signature_def_utils.regression_signature_def(receiver_tensor, - output_1), - "head-2": - signature_def_utils.classification_signature_def(receiver_tensor, - output_2, None), - "head-3": - signature_def_utils.predict_signature_def({ - "input": receiver_tensor - }, {"some_output_3": output_3}) - } - - self.assertDictEqual(expected_signature_defs, signature_defs) + receiver_tensor = array_ops.placeholder(dtypes.string) + output_1 = constant_op.constant([1.]) + output_2 = constant_op.constant(["2"]) + output_3 = constant_op.constant(["3"]) + export_outputs = { + signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY: + export_output.RegressionOutput(value=output_1), + "head-2": export_output.ClassificationOutput(classes=output_2), + "head-3": export_output.PredictOutput(outputs={ + "some_output_3": output_3 + }), + } + + signature_defs = export_utils.build_all_signature_defs( + receiver_tensor, export_outputs) + expected_signature_defs = { + "serving_default": + signature_def_utils.regression_signature_def(receiver_tensor, + output_1), + "head-2": + signature_def_utils.classification_signature_def(receiver_tensor, + output_2, None), + "head-3": + signature_def_utils.predict_signature_def({ + "input": receiver_tensor + }, {"some_output_3": output_3}) + } + + self.assertDictEqual(expected_signature_defs, signature_defs) + + @test_util.deprecated_graph_mode_only def test_build_all_signature_defs_with_dict_alternatives(self): - with context.graph_mode(): - receiver_tensor = array_ops.placeholder(dtypes.string) - receiver_tensors_alternative_1 = { - "foo": array_ops.placeholder(dtypes.int64), - "bar": array_ops.sparse_placeholder(dtypes.float32)} - receiver_tensors_alternatives = {"other": receiver_tensors_alternative_1} - output_1 = constant_op.constant([1.]) - output_2 = constant_op.constant(["2"]) - output_3 = constant_op.constant(["3"]) - export_outputs = { - signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY: - export_output.RegressionOutput(value=output_1), - "head-2": export_output.ClassificationOutput(classes=output_2), - "head-3": export_output.PredictOutput(outputs={ - "some_output_3": output_3 - }), - } - - signature_defs = export_utils.build_all_signature_defs( - receiver_tensor, export_outputs, receiver_tensors_alternatives) - - expected_signature_defs = { - "serving_default": - signature_def_utils.regression_signature_def( - receiver_tensor, - output_1), - "head-2": - signature_def_utils.classification_signature_def( - receiver_tensor, - output_2, None), - "head-3": - signature_def_utils.predict_signature_def( - {"input": receiver_tensor}, - {"some_output_3": output_3}), - "other:head-3": - signature_def_utils.predict_signature_def( - receiver_tensors_alternative_1, - {"some_output_3": output_3}) - - # Note that the alternatives 'other:serving_default' and - # 'other:head-2' are invalid, because regession and classification - # signatures must take a single string input. Here we verify that - # these invalid signatures are not included in the export_utils. - } - - self.assertDictEqual(expected_signature_defs, signature_defs) + receiver_tensor = array_ops.placeholder(dtypes.string) + receiver_tensors_alternative_1 = { + "foo": array_ops.placeholder(dtypes.int64), + "bar": array_ops.sparse_placeholder(dtypes.float32)} + receiver_tensors_alternatives = {"other": receiver_tensors_alternative_1} + output_1 = constant_op.constant([1.]) + output_2 = constant_op.constant(["2"]) + output_3 = constant_op.constant(["3"]) + export_outputs = { + signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY: + export_output.RegressionOutput(value=output_1), + "head-2": export_output.ClassificationOutput(classes=output_2), + "head-3": export_output.PredictOutput(outputs={ + "some_output_3": output_3 + }), + } + signature_defs = export_utils.build_all_signature_defs( + receiver_tensor, export_outputs, receiver_tensors_alternatives) + + expected_signature_defs = { + "serving_default": + signature_def_utils.regression_signature_def( + receiver_tensor, + output_1), + "head-2": + signature_def_utils.classification_signature_def( + receiver_tensor, + output_2, None), + "head-3": + signature_def_utils.predict_signature_def( + {"input": receiver_tensor}, + {"some_output_3": output_3}), + "other:head-3": + signature_def_utils.predict_signature_def( + receiver_tensors_alternative_1, + {"some_output_3": output_3}) + + # Note that the alternatives 'other:serving_default' and + # 'other:head-2' are invalid, because regession and classification + # signatures must take a single string input. Here we verify that + # these invalid signatures are not included in the export_utils. + } + + self.assertDictEqual(expected_signature_defs, signature_defs) + + @test_util.deprecated_graph_mode_only def test_build_all_signature_defs_with_single_alternatives(self): - with context.graph_mode(): - receiver_tensor = array_ops.placeholder(dtypes.string) - receiver_tensors_alternative_1 = array_ops.placeholder(dtypes.int64) - receiver_tensors_alternative_2 = array_ops.sparse_placeholder( - dtypes.float32) - # Note we are passing single Tensors as values of - # receiver_tensors_alternatives, where normally that is a dict. - # In this case a dict will be created using the default receiver tensor - # name "input". - receiver_tensors_alternatives = {"other1": receiver_tensors_alternative_1, - "other2": receiver_tensors_alternative_2} - output_1 = constant_op.constant([1.]) - output_2 = constant_op.constant(["2"]) - output_3 = constant_op.constant(["3"]) - export_outputs = { - signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY: - export_output.RegressionOutput(value=output_1), - "head-2": export_output.ClassificationOutput(classes=output_2), - "head-3": export_output.PredictOutput(outputs={ - "some_output_3": output_3 - }), - } + receiver_tensor = array_ops.placeholder(dtypes.string) + receiver_tensors_alternative_1 = array_ops.placeholder(dtypes.int64) + receiver_tensors_alternative_2 = array_ops.sparse_placeholder( + dtypes.float32) + # Note we are passing single Tensors as values of + # receiver_tensors_alternatives, where normally that is a dict. + # In this case a dict will be created using the default receiver tensor + # name "input". + receiver_tensors_alternatives = {"other1": receiver_tensors_alternative_1, + "other2": receiver_tensors_alternative_2} + output_1 = constant_op.constant([1.]) + output_2 = constant_op.constant(["2"]) + output_3 = constant_op.constant(["3"]) + export_outputs = { + signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY: + export_output.RegressionOutput(value=output_1), + "head-2": export_output.ClassificationOutput(classes=output_2), + "head-3": export_output.PredictOutput(outputs={ + "some_output_3": output_3 + }), + } signature_defs = export_utils.build_all_signature_defs( receiver_tensor, export_outputs, receiver_tensors_alternatives) @@ -222,35 +221,35 @@ class ExportTest(test_util.TensorFlowTestCase): self.assertTrue(int(time_1) < int(time_2)) self.assertTrue(int(time_2) < int(time_3)) + @test_util.deprecated_graph_mode_only def test_build_all_signature_defs_serving_only(self): - with context.graph_mode(): - receiver_tensor = {"input": array_ops.placeholder(dtypes.string)} - output_1 = constant_op.constant([1.]) - export_outputs = { - signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY: - export_output.PredictOutput(outputs=output_1), - "train": export_output.TrainOutput(loss=output_1), - } - - signature_defs = export_utils.build_all_signature_defs( - receiver_tensor, export_outputs) - - expected_signature_defs = { - "serving_default": signature_def_utils.predict_signature_def( - receiver_tensor, {"output": output_1}) - } - - self.assertDictEqual(expected_signature_defs, signature_defs) - - signature_defs = export_utils.build_all_signature_defs( - receiver_tensor, export_outputs, serving_only=False) - - expected_signature_defs.update({ - "train": signature_def_utils.supervised_train_signature_def( - receiver_tensor, loss={"loss": output_1}) - }) - - self.assertDictEqual(expected_signature_defs, signature_defs) + receiver_tensor = {"input": array_ops.placeholder(dtypes.string)} + output_1 = constant_op.constant([1.]) + export_outputs = { + signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY: + export_output.PredictOutput(outputs=output_1), + "train": export_output.TrainOutput(loss=output_1), + } + + signature_defs = export_utils.build_all_signature_defs( + receiver_tensor, export_outputs) + + expected_signature_defs = { + "serving_default": signature_def_utils.predict_signature_def( + receiver_tensor, {"output": output_1}) + } + + self.assertDictEqual(expected_signature_defs, signature_defs) + + signature_defs = export_utils.build_all_signature_defs( + receiver_tensor, export_outputs, serving_only=False) + + expected_signature_defs.update({ + "train": signature_def_utils.supervised_train_signature_def( + receiver_tensor, loss={"loss": output_1}) + }) + + self.assertDictEqual(expected_signature_defs, signature_defs) if __name__ == "__main__": diff --git a/tensorflow/python/saved_model/utils_impl.py b/tensorflow/python/saved_model/utils_impl.py index 5caabe59fe..d43cfdf2b3 100644 --- a/tensorflow/python/saved_model/utils_impl.py +++ b/tensorflow/python/saved_model/utils_impl.py @@ -22,6 +22,7 @@ import os from tensorflow.core.framework import types_pb2 from tensorflow.core.protobuf import meta_graph_pb2 +from tensorflow.python.eager import context from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import sparse_tensor @@ -53,7 +54,12 @@ def build_tensor_info(tensor): Returns: A TensorInfo protocol buffer constructed based on the supplied argument. + + Raises: + RuntimeError: If eager execution is enabled. """ + if context.executing_eagerly(): + raise RuntimeError("build_tensor_info is not supported in Eager mode.") tensor_info = meta_graph_pb2.TensorInfo( dtype=dtypes.as_dtype(tensor.dtype).as_datatype_enum, tensor_shape=tensor.get_shape().as_proto()) diff --git a/tensorflow/python/saved_model/utils_test.py b/tensorflow/python/saved_model/utils_test.py index 2afe8abfd6..1e12de91b8 100644 --- a/tensorflow/python/saved_model/utils_test.py +++ b/tensorflow/python/saved_model/utils_test.py @@ -19,6 +19,7 @@ from __future__ import division from __future__ import print_function from tensorflow.core.framework import types_pb2 +from tensorflow.python.eager import context from tensorflow.python.eager import function from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes @@ -81,6 +82,12 @@ class UtilsTest(test.TestCase): self.assertEqual(42, x_tensor_info.tensor_shape.dim[0].size) self.assertEqual(69, x_tensor_info.tensor_shape.dim[1].size) + def testBuildTensorInfoEager(self): + x = constant_op.constant(1, name="x") + with context.eager_mode(), self.assertRaisesRegexp( + RuntimeError, "build_tensor_info is not supported in Eager mode"): + utils.build_tensor_info(x) + @test_util.run_v1_only("b/120545219") def testGetTensorFromInfoDense(self): expected = array_ops.placeholder(dtypes.float32, 1, name="x") -- GitLab From f9c3fea70f7304c1ee96e8d293dd57623c06fe19 Mon Sep 17 00:00:00 2001 From: Davide Libenzi Date: Tue, 8 Jan 2019 17:24:41 -0800 Subject: [PATCH 528/622] The HLO input/ouput alias config can be setup by the user, expecting a must alias semantics, and opportunistically by the compiler. Since the upper layer of the code will perform aliasing actions on their own, based on the HLO input/output aliasing configuration, we need a way for the upper layer to distringuish whether a given aliasing is coming from the user, or the compiler. This CL adds a single field to the alias data, to differentiate between user and compiler/system aliases. PiperOrigin-RevId: 228429740 --- tensorflow/compiler/xla/client/xla_builder.cc | 5 +- .../compiler/xla/service/buffer_assignment.cc | 9 +- .../compiler/xla/service/copy_insertion.cc | 18 +-- .../xla/service/copy_insertion_test.cc | 21 ++-- tensorflow/compiler/xla/service/hlo.proto | 14 +++ .../xla/service/hlo_alias_analysis.cc | 9 +- .../xla/service/hlo_alias_analysis_test.cc | 24 ++-- .../service/hlo_input_output_alias_config.cc | 119 ++++++++++-------- .../service/hlo_input_output_alias_config.h | 51 ++++++-- .../hlo_input_output_alias_config_test.cc | 60 +++++---- 10 files changed, 209 insertions(+), 121 deletions(-) diff --git a/tensorflow/compiler/xla/client/xla_builder.cc b/tensorflow/compiler/xla/client/xla_builder.cc index 328abbc7b9..7a04c0ec69 100644 --- a/tensorflow/compiler/xla/client/xla_builder.cc +++ b/tensorflow/compiler/xla/client/xla_builder.cc @@ -350,8 +350,9 @@ StatusOr XlaBuilder::Build(int64 root_id) { alias.param_number, alias.param_index.ToString().c_str()); } - TF_RETURN_IF_ERROR(config.SetUpAlias(alias.output_index, alias.param_number, - alias.param_index)); + TF_RETURN_IF_ERROR(config.SetUpAlias( + alias.output_index, alias.param_number, alias.param_index, + HloInputOutputAliasConfig::AliasKind::kUserAlias)); } *module->mutable_input_output_alias() = config.ToProto(); return Status::OK(); diff --git a/tensorflow/compiler/xla/service/buffer_assignment.cc b/tensorflow/compiler/xla/service/buffer_assignment.cc index c3285f516b..d07615b828 100644 --- a/tensorflow/compiler/xla/service/buffer_assignment.cc +++ b/tensorflow/compiler/xla/service/buffer_assignment.cc @@ -1530,15 +1530,16 @@ void BufferAssigner::BuildColocatedBufferSets( VLOG(4) << "Input/Output Alias Config: "; VLOG(4) << module->input_output_alias_config(); module->input_output_alias_config().ForEachAlias( - [&](const ShapeIndex& output_index, int64 param_number, - const ShapeIndex& param_index) { + [&](const ShapeIndex& output_index, + const HloInputOutputAliasConfig::Alias& alias) { std::vector colocated_set; AddBufferToColocatedSet(module->entry_computation()->root_instruction(), output_index, points_to_analysis, &colocated_set); AddBufferToColocatedSet( - module->entry_computation()->parameter_instruction(param_number), - param_index, points_to_analysis, &colocated_set); + module->entry_computation()->parameter_instruction( + alias.parameter_number), + alias.parameter_index, points_to_analysis, &colocated_set); AddSetToColocatedBufferSets(colocated_set, colocated_buffer_sets); }); diff --git a/tensorflow/compiler/xla/service/copy_insertion.cc b/tensorflow/compiler/xla/service/copy_insertion.cc index 2b837901f0..5e26a63ceb 100644 --- a/tensorflow/compiler/xla/service/copy_insertion.cc +++ b/tensorflow/compiler/xla/service/copy_insertion.cc @@ -349,11 +349,12 @@ Status AddCopiesForAliasedInputOutputs(HloModule* module) { ShapeTree param_indices_to_copy(param->shape()); module->input_output_alias_config().ForEachAlias( - [&](const ShapeIndex& output_index, int64 param_number, - const ShapeIndex& param_index) { - if (param_number == param->parameter_number()) { + [&](const ShapeIndex& output_index, + const HloInputOutputAliasConfig::Alias& alias) { + if (alias.parameter_number == param->parameter_number()) { param_has_alias = true; - *(param_indices_to_copy.mutable_element(param_index)) = true; + *(param_indices_to_copy.mutable_element(alias.parameter_index)) = + true; *(output_indices_to_copy.mutable_element(output_index)) = true; } }); @@ -395,13 +396,14 @@ Status AddCopiesForAliasedInputOutputs(HloModule* module) { // Add control dependencies between the input/output copies. TF_RETURN_IF_ERROR(module->input_output_alias_config().ForEachAliasWithStatus( - [&](const ShapeIndex& output_index, int64 param_number, - const ShapeIndex& input_index) -> Status { - if (!copied_parameters[param_number]) { + [&](const ShapeIndex& output_index, + const HloInputOutputAliasConfig::Alias& alias) -> Status { + if (!copied_parameters[alias.parameter_number]) { return Status::OK(); } HloInstruction* from = - copied_parameters[param_number]->element(input_index); + copied_parameters[alias.parameter_number]->element( + alias.parameter_index); HloInstruction* to = output_copy_tree.element(output_index); TF_RET_CHECK(from != nullptr); diff --git a/tensorflow/compiler/xla/service/copy_insertion_test.cc b/tensorflow/compiler/xla/service/copy_insertion_test.cc index e4e9d7ba05..4d4074943e 100644 --- a/tensorflow/compiler/xla/service/copy_insertion_test.cc +++ b/tensorflow/compiler/xla/service/copy_insertion_test.cc @@ -1376,9 +1376,11 @@ TEST_F(CopyInsertionTest, CrossingParameters) { builder.AddInstruction(HloInstruction::CreateTuple({gte1, gte0})); module->AddEntryComputation(builder.Build()); ASSERT_IS_OK(module->input_output_alias_config().SetUpAlias( - /*output_index=*/{0}, /*param_number=*/0, /*param_index=*/{0})); + /*output_index=*/{0}, /*param_number=*/0, /*param_index=*/{0}, + /*kind=*/HloInputOutputAliasConfig::AliasKind::kUserAlias)); ASSERT_IS_OK(module->input_output_alias_config().SetUpAlias( - /*output_index=*/{1}, /*param_number=*/0, /*param_index=*/{1})); + /*output_index=*/{1}, /*param_number=*/0, /*param_index=*/{1}, + /*kind=*/HloInputOutputAliasConfig::AliasKind::kUserAlias)); InsertCopies(module.get()); EXPECT_EQ(CountCopies(*module), 4); @@ -1409,9 +1411,11 @@ TEST_F(CopyInsertionTest, ParametersAliasing) { builder.AddInstruction(HloInstruction::CreateTuple({gte0, gte1})); module->AddEntryComputation(builder.Build()); ASSERT_IS_OK(module->input_output_alias_config().SetUpAlias( - /*output_index=*/{0}, /*param_number=*/0, /*param_index=*/{0})); + /*output_index=*/{0}, /*param_number=*/0, /*param_index=*/{0}, + /*kind=*/HloInputOutputAliasConfig::AliasKind::kUserAlias)); ASSERT_IS_OK(module->input_output_alias_config().SetUpAlias( - /*output_index=*/{1}, /*param_number=*/0, /*param_index=*/{1})); + /*output_index=*/{1}, /*param_number=*/0, /*param_index=*/{1}, + /*kind=*/HloInputOutputAliasConfig::AliasKind::kUserAlias)); InsertCopies(module.get()); EXPECT_EQ(CountCopies(*module), 0); @@ -1475,7 +1479,8 @@ TEST_F(CopyInsertionTest, ParameterWithPartialAliasing) { builder.AddInstruction(HloInstruction::CreateTuple({gte0, gte1})); module->AddEntryComputation(builder.Build()); ASSERT_IS_OK(module->input_output_alias_config().SetUpAlias( - /*output_index=*/{0}, /*param_number=*/0, /*param_index=*/{0})); + /*output_index=*/{0}, /*param_number=*/0, /*param_index=*/{0}, + /*kind=*/HloInputOutputAliasConfig::AliasKind::kUserAlias)); InsertCopies(module.get()); EXPECT_THAT(module->entry_computation()->root_instruction(), @@ -1516,7 +1521,8 @@ TEST_F(CopyInsertionTest, ParameterAndParallelOpsWithPartialAliasing) { builder.AddInstruction(HloInstruction::CreateTuple({negate0, negate1})); module->AddEntryComputation(builder.Build()); ASSERT_IS_OK(module->input_output_alias_config().SetUpAlias( - /*output_index=*/{0}, /*param_number=*/0, /*param_index=*/{0})); + /*output_index=*/{0}, /*param_number=*/0, /*param_index=*/{0}, + /*kind=*/HloInputOutputAliasConfig::AliasKind::kUserAlias)); InsertCopies(module.get()); EXPECT_EQ(CountCopies(*module), 0); @@ -1557,7 +1563,8 @@ TEST_F(CopyInsertionTest, ParameterAndOpsWithPartialAliasing) { builder.AddInstruction(HloInstruction::CreateTuple({add, negate1})); module->AddEntryComputation(builder.Build()); ASSERT_IS_OK(module->input_output_alias_config().SetUpAlias( - /*output_index=*/{0}, /*param_number=*/0, /*param_index=*/{0})); + /*output_index=*/{0}, /*param_number=*/0, /*param_index=*/{0}, + /*kind=*/HloInputOutputAliasConfig::AliasKind::kUserAlias)); InsertCopies(module.get()); EXPECT_EQ(CountCopies(*module), 0); diff --git a/tensorflow/compiler/xla/service/hlo.proto b/tensorflow/compiler/xla/service/hlo.proto index 9b50f1ca5b..263b42a29d 100644 --- a/tensorflow/compiler/xla/service/hlo.proto +++ b/tensorflow/compiler/xla/service/hlo.proto @@ -229,6 +229,18 @@ message HloScheduleProto { } message HloInputOutputAliasProto { + enum Kind { + // Define a UNDEFINED_ALIAS equal to zero to get around the default-0 proto3 + // behavior and missing has_*() APIs. + UNDEFINED_ALIAS = 0; + // An alias setup by the user as must alias. A use setting USER_ALIAS is + // expecting the designed output to be dropped over the given input + // parameter number+index. + USER_ALIAS = 1; + // An alias setup by the compiler as part of its optimizations. + SYSTEM_ALIAS = 2; + } + // The following proto describes a pair of aliased an input // (described by parameter number and a ShapeIndex of the parameter) // and an output (described by a ShapeIndex of the root @@ -249,6 +261,8 @@ message HloInputOutputAliasProto { int64 parameter_number = 2; // ShapeIndex of the parameter instruction. repeated int64 parameter_shape_index = 3; + // The kind of alias to be setup. + Kind kind = 4; } repeated AliasEntryProto entries = 1; diff --git a/tensorflow/compiler/xla/service/hlo_alias_analysis.cc b/tensorflow/compiler/xla/service/hlo_alias_analysis.cc index 4c46ea595e..e511f1951c 100644 --- a/tensorflow/compiler/xla/service/hlo_alias_analysis.cc +++ b/tensorflow/compiler/xla/service/hlo_alias_analysis.cc @@ -176,13 +176,12 @@ class BufferValueMap { const HloValue& value, std::vector* aliased_buffers) { // Get parameter value from an aliased_input object. const auto get_parameter_value = - [this](const std::pair& aliased_input) + [this](const HloInputOutputAliasConfig::Alias& aliased_input) -> const HloValue& { - int64 param_number = aliased_input.first; - const ShapeIndex& param_index = aliased_input.second; return dataflow_.GetUniqueValueAt( - module_->entry_computation()->parameter_instruction(param_number), - param_index); + module_->entry_computation()->parameter_instruction( + aliased_input.parameter_number), + aliased_input.parameter_index); }; // If the value shows up in a root instruction, alias it with parameter diff --git a/tensorflow/compiler/xla/service/hlo_alias_analysis_test.cc b/tensorflow/compiler/xla/service/hlo_alias_analysis_test.cc index 7e6150e941..b6dbf07959 100644 --- a/tensorflow/compiler/xla/service/hlo_alias_analysis_test.cc +++ b/tensorflow/compiler/xla/service/hlo_alias_analysis_test.cc @@ -238,13 +238,16 @@ TEST_F(HloAliasAnalysisTest, ParametersWithAliasing) { builder.AddInstruction(HloInstruction::CreateTuple({negate0, negate1})); module_->AddEntryComputation(builder.Build()); TF_ASSERT_OK(module_->input_output_alias_config().SetUpAlias( - /*output_index=*/{0}, /*param_number=*/0, /*param_index=*/{0})); + /*output_index=*/{0}, /*param_number=*/0, /*param_index=*/{0}, + /*kind=*/HloInputOutputAliasConfig::AliasKind::kUserAlias)); TF_ASSERT_OK(module_->input_output_alias_config().SetUpAlias( - /*output_index=*/{1}, /*param_number=*/0, /*param_index=*/{1})); + /*output_index=*/{1}, /*param_number=*/0, /*param_index=*/{1}, + /*kind=*/HloInputOutputAliasConfig::AliasKind::kUserAlias)); // Cannot alias an output twice. ASSERT_IS_NOT_OK(module_->input_output_alias_config().SetUpAlias( - /*output_index=*/{1}, /*param_number=*/0, /*param_index=*/{0})); + /*output_index=*/{1}, /*param_number=*/0, /*param_index=*/{0}, + /*kind=*/HloInputOutputAliasConfig::AliasKind::kUserAlias)); const HloAliasAnalysis& analysis = RunAnalysis(); @@ -279,13 +282,16 @@ TEST_F(HloAliasAnalysisTest, ParametersWithCrossAliasing) { builder.AddInstruction(HloInstruction::CreateTuple({gte0, gte1})); module_->AddEntryComputation(builder.Build()); TF_ASSERT_OK(module_->input_output_alias_config().SetUpAlias( - /*output_index=*/{0}, /*param_number=*/0, /*param_index=*/{1})); + /*output_index=*/{0}, /*param_number=*/0, /*param_index=*/{1}, + /*kind=*/HloInputOutputAliasConfig::AliasKind::kUserAlias)); TF_ASSERT_OK(module_->input_output_alias_config().SetUpAlias( - /*output_index=*/{1}, /*param_number=*/0, /*param_index=*/{0})); + /*output_index=*/{1}, /*param_number=*/0, /*param_index=*/{0}, + /*kind=*/HloInputOutputAliasConfig::AliasKind::kUserAlias)); // Cannot alias an output twice. ASSERT_IS_NOT_OK(module_->input_output_alias_config().SetUpAlias( - /*output_index=*/{1}, /*param_number=*/0, /*param_index=*/{1})); + /*output_index=*/{1}, /*param_number=*/0, /*param_index=*/{1}, + /*kind=*/HloInputOutputAliasConfig::AliasKind::kUserAlias)); const HloAliasAnalysis& analysis = RunAnalysis(); @@ -365,9 +371,11 @@ TEST_F(HloAliasAnalysisTest, InputOutputAliasingWithWhile) { builder.AddInstruction(HloInstruction::CreateTuple({negate_1, negate_2})); module_->AddEntryComputation(builder.Build()); TF_ASSERT_OK(module_->input_output_alias_config().SetUpAlias( - /*output_index=*/{0}, /*param_number=*/0, /*param_index=*/{0})); + /*output_index=*/{0}, /*param_number=*/0, /*param_index=*/{0}, + /*kind=*/HloInputOutputAliasConfig::AliasKind::kUserAlias)); TF_ASSERT_OK(module_->input_output_alias_config().SetUpAlias( - /*output_index=*/{1}, /*param_number=*/0, /*param_index=*/{1})); + /*output_index=*/{1}, /*param_number=*/0, /*param_index=*/{1}, + /*kind=*/HloInputOutputAliasConfig::AliasKind::kUserAlias)); const HloAliasAnalysis& analysis = RunAnalysis(); diff --git a/tensorflow/compiler/xla/service/hlo_input_output_alias_config.cc b/tensorflow/compiler/xla/service/hlo_input_output_alias_config.cc index 70d4df5d1c..b01c00121b 100644 --- a/tensorflow/compiler/xla/service/hlo_input_output_alias_config.cc +++ b/tensorflow/compiler/xla/service/hlo_input_output_alias_config.cc @@ -20,28 +20,31 @@ namespace xla { bool HloInputOutputAliasConfig::OutputHasAlias( const ShapeIndex& output_index) const { - return aliased_output_indices_.count(output_index) > 0; + return alias_.element(output_index).has_value(); } Status HloInputOutputAliasConfig::SetUpAlias(const ShapeIndex& output_index, int64 param_number, - const ShapeIndex& param_index) { - TF_RET_CHECK(!OutputHasAlias(output_index)) - << "Output index " << output_index << " already has an alias setup"; + const ShapeIndex& param_index, + AliasKind kind) { + TF_RET_CHECK(kind == AliasKind::kUserAlias || kind == AliasKind::kSystemAlias) + << kind; TF_RET_CHECK(ShapeUtil::IndexIsValid(alias_.shape(), output_index)) << absl::StrCat("Tring to set up alias at ", output_index.ToString(), " which is an invalid index for shape ", ShapeUtil::HumanString(alias_.shape())); + TF_RET_CHECK(param_number >= 0) << param_number; + TF_RET_CHECK(!OutputHasAlias(output_index)) + << "Output index " << output_index << " already has an alias setup"; // Output can't be aliased with multiple parameters. TF_RET_CHECK(!alias_.element(output_index)) << absl::StrFormat( "Trying to set up output alias for param %lld at %s but failed: output " "index %s is already aliased with param %lld at %s", param_number, param_index.ToString(), output_index.ToString(), - alias_.element(output_index)->first, - alias_.element(output_index)->second.ToString()); + alias_.element(output_index)->parameter_number, + alias_.element(output_index)->parameter_index.ToString()); (*alias_.mutable_element(output_index)) = - std::make_pair(param_number, param_index); - aliased_output_indices_.insert(output_index); + Alias(kind, param_number, param_index); VLOG(4) << "Set up alias between output index " << output_index.ToString() << " and parameter " << param_index << " at index " << param_index.ToString(); @@ -51,15 +54,24 @@ Status HloInputOutputAliasConfig::SetUpAlias(const ShapeIndex& output_index, HloInputOutputAliasProto HloInputOutputAliasConfig::ToProto() const { HloInputOutputAliasProto result; alias_.ForEachElement( - [&](const ShapeIndex& index, - const absl::optional>& data) { + [&](const ShapeIndex& index, const absl::optional& data) { if (data) { HloInputOutputAliasProto::AliasEntryProto entry; + switch (data->kind) { + case AliasKind::kUserAlias: + entry.set_kind(HloInputOutputAliasProto::USER_ALIAS); + break; + case AliasKind::kSystemAlias: + entry.set_kind(HloInputOutputAliasProto::SYSTEM_ALIAS); + break; + default: + LOG(FATAL) << "Unknown alias kind " << data->kind; + } for (int64 i : index) { entry.add_output_shape_index(i); } - entry.set_parameter_number(data->first); - for (int64 i : data->second) { + entry.set_parameter_number(data->parameter_number); + for (int64 i : data->parameter_index) { entry.add_parameter_shape_index(i); } result.add_entries()->Swap(&entry); @@ -75,14 +87,18 @@ StatusOr HloInputOutputAliasConfig::CreateFromProto( proto.entries()) { ShapeIndex output_index(entry.output_shape_index().begin(), entry.output_shape_index().end()); - int64 param_number = entry.parameter_number(); ShapeIndex param_index(entry.parameter_shape_index().begin(), entry.parameter_shape_index().end()); + // Handle backward compatibility with existing protos, which only knew of + // system aliases. + AliasKind kind = AliasKind::kSystemAlias; + if (entry.kind() == HloInputOutputAliasProto::USER_ALIAS) { + kind = AliasKind::kUserAlias; + } TF_RETURN_IF_ERROR( - result.SetUpAlias(output_index, param_number, param_index)); + result.SetUpAlias(output_index, param_number, param_index, kind)); } - return result; } @@ -90,45 +106,44 @@ string HloInputOutputAliasConfig::ToString() const { std::vector pieces; pieces.push_back("HloInputOutputAliasConfig"); - ForEachAlias([&](const ShapeIndex& output_index, int64 param_number, - const ShapeIndex& param_index) { + ForEachAlias([&](const ShapeIndex& output_index, const Alias& alias) { + const char* kind = alias.kind == AliasKind::kUserAlias ? "USER" : "SYSTEM"; pieces.push_back(absl::StrFormat( - " OutputIndex %s is aliased with parameter %lld at %s:", - output_index.ToString(), param_number, param_index.ToString())); + " OutputIndex %s is aliased (kind=%s) with parameter %lld at %s:", + output_index.ToString(), kind, alias.parameter_number, + alias.parameter_index.ToString())); }); - return absl::StrJoin(pieces, "\n"); } -bool HloInputOutputAliasConfig::ParameterHasAlias( +HloInputOutputAliasConfig::AliasKind +HloInputOutputAliasConfig::ParameterAliasKind( int64 param_number, const ShapeIndex& param_index) const { - bool output = false; + AliasKind kind = AliasKind::kNoAlias; alias_.ForEachElement( - [&](const xla::ShapeIndex&, - absl::optional> alias) { - if (alias && alias->first == param_number && - alias->second == param_index) { - output = true; + [&](const xla::ShapeIndex&, absl::optional alias) { + if (alias && alias->parameter_number == param_number && + alias->parameter_index == param_index) { + kind = alias->kind; } }); - return output; + return kind; } absl::optional HloInputOutputAliasConfig::GetAliasedOutput( int64 param_number, const ShapeIndex& param_index) const { absl::optional output; alias_.ForEachElement( - [&](const xla::ShapeIndex& output_index, - absl::optional> alias) { - if (alias && alias->first == param_number && - alias->second == param_index) { + [&](const xla::ShapeIndex& output_index, absl::optional alias) { + if (alias && alias->parameter_number == param_number && + alias->parameter_index == param_index) { output = output_index; } }); return output; } -absl::optional> +absl::optional HloInputOutputAliasConfig::GetAliasedParameter( const ShapeIndex& output_index) const { CHECK(ShapeUtil::IndexIsValid(alias_.shape(), output_index)); @@ -137,10 +152,9 @@ HloInputOutputAliasConfig::GetAliasedParameter( void HloInputOutputAliasConfig::ForEachAlias(AliasFn fn) const { alias_.ForEachElement( - [&](const ShapeIndex& output_index, - absl::optional> aliased) { + [&](const ShapeIndex& output_index, absl::optional aliased) { if (aliased) { - fn(output_index, aliased->first, aliased->second); + fn(output_index, *aliased); } }); } @@ -148,10 +162,9 @@ void HloInputOutputAliasConfig::ForEachAlias(AliasFn fn) const { Status HloInputOutputAliasConfig::ForEachAliasWithStatus( AliasFnWithStatus fn) const { return alias_.ForEachElementWithStatus( - [&](const ShapeIndex& output_index, - absl::optional> aliased) { + [&](const ShapeIndex& output_index, absl::optional aliased) { if (aliased) { - TF_RETURN_IF_ERROR(fn(output_index, aliased->first, aliased->second)); + TF_RETURN_IF_ERROR(fn(output_index, *aliased)); } return Status::OK(); }); @@ -167,20 +180,19 @@ Status HloInputOutputAliasConfig::Verify( param_has_seen.emplace_back(param->shape()); } return ForEachAliasWithStatus([&](const ShapeIndex& output_index, - int64 param_number, - const ShapeIndex& param_index) -> Status { + const Alias& alias) -> Status { const HloInstruction* root = entry->root_instruction(); - TF_RET_CHECK(0 <= param_number); - TF_RET_CHECK(entry->num_parameters() > param_number); + TF_RET_CHECK(0 <= alias.parameter_number); + TF_RET_CHECK(entry->num_parameters() > alias.parameter_number); const Shape& param_shape = - entry->parameter_instruction(param_number)->shape(); + entry->parameter_instruction(alias.parameter_number)->shape(); const Shape& output_shape = root->shape(); - TF_RET_CHECK(ShapeUtil::IndexIsValid(param_shape, param_index)); + TF_RET_CHECK(ShapeUtil::IndexIsValid(param_shape, alias.parameter_index)); TF_RET_CHECK(ShapeUtil::IndexIsValid(output_shape, output_index)); const Shape& param_subshape = - ShapeUtil::GetSubshape(param_shape, param_index); + ShapeUtil::GetSubshape(param_shape, alias.parameter_index); const Shape& output_subshape = ShapeUtil::GetSubshape(output_shape, output_index); TF_RET_CHECK(LayoutUtil::IsDenseArray(param_subshape)); @@ -191,19 +203,20 @@ Status HloInputOutputAliasConfig::Verify( "Expected aliased input %lld at index %s and output at index %s to " "have the same size. Input sub-shape is %s with size %lld, output " "sub-shape is %s with size %lld", - param_number, param_index.ToString(), output_index.ToString(), + alias.parameter_number, alias.parameter_index.ToString(), + output_index.ToString(), ShapeUtil::HumanStringWithLayout(param_subshape), size_func(param_subshape), ShapeUtil::HumanStringWithLayout(output_subshape), size_func(output_subshape)); } - // Check each param_number and param_index pair only show up once. No - // input can be aliased with output buffers. - TF_RET_CHECK(param_has_seen[param_number].element(param_index) == false); - - *(param_has_seen[param_number].mutable_element(param_index)) = true; - + // Check each alias.parameter_number and alias.parameter_index pair only + // show up once. No input can be aliased with output buffers. + TF_RET_CHECK(param_has_seen[alias.parameter_number].element( + alias.parameter_index) == false); + *(param_has_seen[alias.parameter_number].mutable_element( + alias.parameter_index)) = true; return Status::OK(); }); } diff --git a/tensorflow/compiler/xla/service/hlo_input_output_alias_config.h b/tensorflow/compiler/xla/service/hlo_input_output_alias_config.h index 3967743d53..b0b71dece8 100644 --- a/tensorflow/compiler/xla/service/hlo_input_output_alias_config.h +++ b/tensorflow/compiler/xla/service/hlo_input_output_alias_config.h @@ -32,6 +32,28 @@ class HloModule; // parameter index in the entry computation. class HloInputOutputAliasConfig { public: + // The kind of aliases which can be set. A kUserAlias is one setup at + // compilation time by the user, and has to be respected. A kSystemAlias one + // might be setup by the compiler, if it decides it is convenient to do so. + enum AliasKind { + kNoAlias, + kUserAlias, + kSystemAlias, + }; + + // Defines the alias information for a given output buffer. A given output + // buffer shape index can refer only to one parameter+index. + struct Alias { + Alias(AliasKind kind, int64 parameter_number, ShapeIndex parameter_index) + : kind(kind), + parameter_number(parameter_number), + parameter_index(std::move(parameter_index)) {} + + AliasKind kind; + int64 parameter_number; + ShapeIndex parameter_index; + }; + HloInputOutputAliasConfig() = default; explicit HloInputOutputAliasConfig(Shape shape) : alias_(shape) {} @@ -41,12 +63,19 @@ class HloInputOutputAliasConfig { // Sets up alias config from `output_index` to `param_index` at // `param_number`. Status SetUpAlias(const ShapeIndex& output_index, int64 param_number, - const ShapeIndex& param_index); + const ShapeIndex& param_index, AliasKind kind); + + // Returns the kind of alias for the given parameter number and parameter + // index. If no alias exists, AliasKind::kNoAlias is returned. + AliasKind ParameterAliasKind(int64 param_number, + const ShapeIndex& param_index) const; // Returns true if the given parameter is aliased with one of the output // buffers. bool ParameterHasAlias(int64 param_number, - const ShapeIndex& param_index) const; + const ShapeIndex& param_index) const { + return ParameterAliasKind(param_number, param_index) != AliasKind::kNoAlias; + } // Checks whether the provided output index has already been aliased. bool OutputHasAlias(const ShapeIndex& output_index) const; @@ -67,19 +96,17 @@ class HloInputOutputAliasConfig { // Returns the number of parameter and index of the parameter buffer that the // given output buffer index is aliased with. A nullopt is returned if there // is no parameter is aliased with the specific output. - absl::optional> GetAliasedParameter( + absl::optional GetAliasedParameter( const ShapeIndex& output_index) const; using AliasFn = - std::function; + std::function; // Iterates through each aliased output and input. void ForEachAlias(AliasFn fn) const; using AliasFnWithStatus = - std::function; + std::function; // Verifies that the given config is valid for the given module. // Specifically, the config's input and output should be in-bound and size of @@ -94,12 +121,10 @@ class HloInputOutputAliasConfig { private: // A ShapeTree which indicates the list of buffers that's expected to be // aliased. The key on this shape tree represents the output index. The value - // is a pair of parameter number and index into the buffer. If the value is - // nullopt, it means there is no parameter aliasing for this output. - ShapeTree>> alias_; - - // The indices of the output which have been aliased. - absl::flat_hash_set aliased_output_indices_; + // is an Alias data structure which defines the input parameter coordinates. + // If the value is nullopt, it means there is no parameter aliasing for this + // output. + ShapeTree> alias_; }; std::ostream& operator<<(std::ostream& out, diff --git a/tensorflow/compiler/xla/service/hlo_input_output_alias_config_test.cc b/tensorflow/compiler/xla/service/hlo_input_output_alias_config_test.cc index aeb9b0fdc8..a46a107723 100644 --- a/tensorflow/compiler/xla/service/hlo_input_output_alias_config_test.cc +++ b/tensorflow/compiler/xla/service/hlo_input_output_alias_config_test.cc @@ -45,11 +45,12 @@ class HloInputOutputAliasConfigTest : public HloTestBase { EXPECT_TRUE(aliased_output); EXPECT_EQ(aliased_output.value(), output_index); - absl::optional> aliased_param = + absl::optional aliased_param = config.GetAliasedParameter(output_index); EXPECT_TRUE(aliased_param); - EXPECT_EQ(aliased_param.value(), std::make_pair(param_number, param_index)); + EXPECT_EQ(aliased_param->parameter_number, param_number); + EXPECT_EQ(aliased_param->parameter_index, param_index); } void expect_not_aliased(const ShapeIndex& output_index, int64 param_number, @@ -60,11 +61,12 @@ class HloInputOutputAliasConfigTest : public HloTestBase { EXPECT_FALSE(aliased_output && aliased_output == output_index); - absl::optional> aliased_param = + absl::optional aliased_param = config.GetAliasedParameter(output_index); - EXPECT_FALSE(aliased_param && aliased_param->first == param_number && - aliased_param->second == param_index); + EXPECT_FALSE(aliased_param && + aliased_param->parameter_number == param_number && + aliased_param->parameter_index == param_index); } }; @@ -84,8 +86,10 @@ ENTRY main { HloInputOutputAliasConfig config( module->entry_computation()->root_instruction()->shape()); - TF_ASSERT_OK(config.SetUpAlias(/*output_index=*/{0}, /*param_number=*/1, - /*param_index=*/{})); + TF_ASSERT_OK(config.SetUpAlias( + /*output_index=*/{0}, /*param_number=*/1, + /*param_index=*/{}, + /*kind=*/HloInputOutputAliasConfig::AliasKind::kUserAlias)); expect_aliased(/*output_index=*/{0}, /*param_number=*/1, /*param_index=*/{}, config); @@ -114,11 +118,15 @@ ENTRY main { HloInputOutputAliasConfig config( module->entry_computation()->root_instruction()->shape()); - TF_ASSERT_OK(config.SetUpAlias(/*output_index=*/{0}, /*param_number=*/0, - /*param_index=*/{0})); + TF_ASSERT_OK(config.SetUpAlias( + /*output_index=*/{0}, /*param_number=*/0, + /*param_index=*/{0}, + /*kind=*/HloInputOutputAliasConfig::AliasKind::kUserAlias)); - TF_ASSERT_OK(config.SetUpAlias(/*output_index=*/{1}, /*param_number=*/0, - /*param_index=*/{1})); + TF_ASSERT_OK(config.SetUpAlias( + /*output_index=*/{1}, /*param_number=*/0, + /*param_index=*/{1}, + /*kind=*/HloInputOutputAliasConfig::AliasKind::kUserAlias)); expect_aliased(/*output_index=*/{0}, /*param_number=*/0, /*param_index=*/{0}, config); @@ -149,11 +157,15 @@ ENTRY main { HloInputOutputAliasConfig config( module->entry_computation()->root_instruction()->shape()); - TF_ASSERT_OK(config.SetUpAlias(/*output_index=*/{0}, /*param_number=*/0, - /*param_index=*/{})); + TF_ASSERT_OK(config.SetUpAlias( + /*output_index=*/{0}, /*param_number=*/0, + /*param_index=*/{}, + /*kind=*/HloInputOutputAliasConfig::AliasKind::kUserAlias)); - TF_ASSERT_OK(config.SetUpAlias(/*output_index=*/{1}, /*param_number=*/0, - /*param_index=*/{})); + TF_ASSERT_OK(config.SetUpAlias( + /*output_index=*/{1}, /*param_number=*/0, + /*param_index=*/{}, + /*kind=*/HloInputOutputAliasConfig::AliasKind::kUserAlias)); ASSERT_IS_NOT_OK(config.Verify(*module, [](const Shape& shape) { return ShapeUtil::ByteSizeOf(shape); @@ -176,8 +188,10 @@ ENTRY main { HloInputOutputAliasConfig config( module->entry_computation()->root_instruction()->shape()); - TF_ASSERT_OK(config.SetUpAlias(/*output_index=*/{1}, /*param_number=*/0, - /*param_index=*/{})); + TF_ASSERT_OK(config.SetUpAlias( + /*output_index=*/{1}, /*param_number=*/0, + /*param_index=*/{}, + /*kind=*/HloInputOutputAliasConfig::AliasKind::kUserAlias)); ASSERT_IS_NOT_OK(config.Verify(*module, [](const Shape& shape) { return ShapeUtil::ByteSizeOf(shape); @@ -200,11 +214,15 @@ ENTRY main { HloInputOutputAliasConfig config( module->entry_computation()->root_instruction()->shape()); - TF_ASSERT_OK(config.SetUpAlias(/*output_index=*/{0}, /*param_number=*/0, - /*param_index=*/{})); + TF_ASSERT_OK(config.SetUpAlias( + /*output_index=*/{0}, /*param_number=*/0, + /*param_index=*/{}, + /*kind=*/HloInputOutputAliasConfig::AliasKind::kUserAlias)); - ASSERT_IS_NOT_OK(config.SetUpAlias(/*output_index=*/{0}, /*param_number=*/1, - /*param_index=*/{})); + ASSERT_IS_NOT_OK(config.SetUpAlias( + /*output_index=*/{0}, /*param_number=*/1, + /*param_index=*/{}, + /*kind=*/HloInputOutputAliasConfig::AliasKind::kUserAlias)); } } // namespace } // namespace xla -- GitLab From fb8a14fde29f2dc19331d73795c2ead0e8ef0e8e Mon Sep 17 00:00:00 2001 From: Yifei Feng Date: Tue, 8 Jan 2019 17:33:23 -0800 Subject: [PATCH 529/622] Remove cuda runtime dependencies from stream executor. PiperOrigin-RevId: 228430943 --- tensorflow/stream_executor/cuda/BUILD | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/tensorflow/stream_executor/cuda/BUILD b/tensorflow/stream_executor/cuda/BUILD index 58c9480884..87c8eae416 100644 --- a/tensorflow/stream_executor/cuda/BUILD +++ b/tensorflow/stream_executor/cuda/BUILD @@ -146,13 +146,7 @@ cc_library( "//tensorflow/stream_executor/lib", "//tensorflow/stream_executor/platform", "//tensorflow/stream_executor/platform:dso_loader", - ] + if_static( - [ - "@local_config_cuda//cuda:cublas", - "@local_config_cuda//cuda:cudart_static", - ], - ["@local_config_cuda//cuda:cudart"], - ), + ] + if_static(["@local_config_cuda//cuda:cublas"]), alwayslink = True, ) @@ -214,13 +208,7 @@ cc_library( "//tensorflow/stream_executor/lib", "//tensorflow/stream_executor/platform", "//tensorflow/stream_executor/platform:dso_loader", - ] + tf_additional_cudnn_plugin_deps() + if_static( - [ - "@local_config_cuda//cuda:cudnn", - "@local_config_cuda//cuda:cudart_static", - ], - ["@local_config_cuda//cuda:cudart"], - ), + ] + tf_additional_cudnn_plugin_deps() + if_static(["@local_config_cuda//cuda:cudnn"]), alwayslink = True, ) -- GitLab From c8191270e47ece2bba777c271428e205d34d0827 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Tue, 8 Jan 2019 18:41:13 -0800 Subject: [PATCH 530/622] Remove Identity nodes where all inputs and outputs are on the same device, but the identity is on a different device. PiperOrigin-RevId: 228438774 --- .../optimizers/dependency_optimizer.cc | 17 ++++++------ .../optimizers/dependency_optimizer_test.cc | 26 +++++++++++++++++++ 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/tensorflow/core/grappler/optimizers/dependency_optimizer.cc b/tensorflow/core/grappler/optimizers/dependency_optimizer.cc index 7fee3ae9d5..8b81cb2430 100644 --- a/tensorflow/core/grappler/optimizers/dependency_optimizer.cc +++ b/tensorflow/core/grappler/optimizers/dependency_optimizer.cc @@ -205,14 +205,6 @@ bool DependencyOptimizer::BypassingNodeIsBeneficial( num_cross_out += static_cast(output_node->device() != node_dev); } - if ((is_identity || is_multi_input_identity_n) && num_cross_in > 0 && - num_cross_out > 0) { - // This identity node follows a device crossing, so it might be - // following a _Recv node after partioning. Do not remove such nodes, - // unless they only have consumers on the same device as themselves. - return false; - } - // Make sure we do not increase the number of device crossings. const int num_cross_before = num_cross_in + num_cross_out; int num_cross_after = 0; @@ -225,6 +217,15 @@ bool DependencyOptimizer::BypassingNodeIsBeneficial( if (num_cross_after > num_cross_before) { return false; } + + if ((is_identity || is_multi_input_identity_n) && num_cross_in > 0 && + num_cross_out > 0 && num_cross_after > 0) { + // This identity node follows a device crossing, so it might be + // following a _Recv node after partioning. Do not remove such nodes, + // unless they only have consumers on the same device as themselves. + return false; + } + return true; } diff --git a/tensorflow/core/grappler/optimizers/dependency_optimizer_test.cc b/tensorflow/core/grappler/optimizers/dependency_optimizer_test.cc index 8d70d9d5c7..5883fcb926 100644 --- a/tensorflow/core/grappler/optimizers/dependency_optimizer_test.cc +++ b/tensorflow/core/grappler/optimizers/dependency_optimizer_test.cc @@ -356,6 +356,32 @@ TEST_F(DependencyOptimizerTest, RemoveIdentityOps_DeviceBoundaries) { VerifyGraphsEqual(item.graph, output, __FUNCTION__); } +TEST_F(DependencyOptimizerTest, RemoveIdentityOps_IdenticalDevices) { + tensorflow::Scope s = tensorflow::Scope::NewRootScope(); + Output x = ops::RandomUniform(s.WithOpName("x").WithDevice("/CPU:0"), {1, 2}, + DT_FLOAT); + auto id_a = ops::Identity(s.WithOpName("id_a").WithDevice("/CPU:1"), x); + Output id = + ops::Identity(s.WithControlDependencies(id_a).WithDevice("/CPU:0"), id_a); + + GrapplerItem item; + TF_CHECK_OK(s.ToGraphDef(&item.graph)); + item.fetch.push_back("Identity"); + + DependencyOptimizer optimizer; + GraphDef output; + Status status = optimizer.Optimize(nullptr, item, &output); + TF_EXPECT_OK(status); + + EXPECT_EQ(item.graph.node_size() - 1, output.node_size()); + for (const NodeDef& node : output.node()) { + EXPECT_NE(node.name(), "id_a"); + if (node.name() == "Identity") { + EXPECT_EQ(node.input(0), "x"); + } + } +} + TEST_F(DependencyOptimizerTest, RemoveNoOps_SingleInputOrOutput) { tensorflow::Scope s = tensorflow::Scope::NewRootScope(); Output x = ops::RandomUniform(s.WithOpName("x"), {1, 2}, DT_FLOAT); -- GitLab From ec81825aaf7e848d9f8ddffdf1e0d20aebe9172c Mon Sep 17 00:00:00 2001 From: Reed Wanderman-Milne Date: Tue, 8 Jan 2019 18:50:27 -0800 Subject: [PATCH 531/622] Add GPU explicit padding to tf.nn.conv2d. Benchmark results: All benchmark results were run on a Z840 with a Titan V, with internal TensorFlow. 1. Resnet50 Eager results The internal resnet50 Eager benchmarks were run, to ensure no regressions in Resnet50 in Eager mode that could have occurred due to the extra Python overhead this change adds. The benchmarks run are here: https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/eager/python/examples/resnet50/resnet50_test.py. Each row was run 150 times and the average was taken. Note none of these benchmarks use explicit padding. Numbers represent time, so lower is better. Benchmark Name After Before % diff apply_async_gpu_batch_64_channels_first 0.0726 0.0726 -0.06% apply_gpu_batch_64_channels_first 0.0725 0.0725 -0.06% apply_with_defun_gpu_batch_64_channels_first 0.0755 0.0756 0.07% train_async_gpu_batch_16_channels_first 0.0776 0.0778 0.27% train_async_gpu_batch_32_channels_first 0.1268 0.1271 0.23% train_dataset_gpu_batch_16_channels_first 0.1085 0.1094 0.77% train_dataset_gpu_batch_32_channels_first 0.1473 0.1477 0.28% train_dataset_with_defun_gpu_batch_16_channels_first 0.0800 0.0803 0.37% train_dataset_with_defun_gpu_batch_32_channels_first 0.1325 0.1326 0.09% train_gpu_batch_16_channels_first 0.0812 0.0813 0.18% train_gpu_batch_32_channels_first 0.1329 0.1325 -0.32% train_with_defun_gpu_batch_16_channels_first 0.0789 0.0791 0.26% train_with_defun_gpu_batch_32_channels_first 0.1325 0.1325 -0.02% There is minimal impact to Eager performance. 2. tf_cnn_benchmarks tf_cnn_benchmarks was run internally with the following flags: --batch_size=128 --model=resnet50 It was run 60 times with and without this change. With this change, tf_cnn_benchmarks had all instances of a tf.pad followed by Conv2D replaced with an explicitly padded Conv2d. It got 330.96 images/sec with this change and 330.80 without, and the difference is likely noise. Therefore, this change does not improve tf_cnn_benchmarks performance. 3. Conv2D benchmarks The added benchmarks to conv_ops_test.py were run with this change, each 400 times and the average was taken. They were not run without this change. The table groups the 8 benchmarks into 4 pairs, with each pair running two similar benchmarks, one with explicit padding, and one without explicit padding. Benchmark name Explicit Non-explicit % diff explicit/manual pad forward 0.001815 0.002006 10.56% explicit/manual pad backward 0.006261 0.006937 10.79% eager explicit/same pad 0.039320 0.038403 -2.33% graph explicit/same pad 0.037039 0.037034 -0.11% The first two rows show there is theoretical performance gains to using explicit padding over a manual tf.pad followed by the convolution. On Resnet50, we were not able to achieve this performance gain in practice, as tf_cnn_benchmarks saw no improvement. On models that use larger paddings than in Resnet50, the performance gain will less negligible. The last two rows compare explicit padding padding to the equivalent same padding, to see if explicit padding adds any overhead. In Graph mode, there is no overhead, but Eager mode has some overhead with explicit padding over SAME padding. PiperOrigin-RevId: 228439591 --- .../tf2xla/kernels/conv_op_helpers.cc | 9 +- .../api_def/base_api/api_def_Conv2D.pbtxt | 9 + .../api_def_Conv2DBackpropFilter.pbtxt | 9 + .../api_def_Conv2DBackpropInput.pbtxt | 9 + tensorflow/core/framework/common_shape_fns.cc | 84 +- tensorflow/core/framework/common_shape_fns.h | 48 +- .../core/framework/common_shape_fns_test.cc | 82 +- tensorflow/core/framework/ops_util.cc | 3 + .../large_function_graph.pbtxt | 7 + .../grappler/optimizers/layout_optimizer.cc | 23 + .../optimizers/layout_optimizer_test.cc | 64 +- .../core/kernels/conv_grad_filter_ops.cc | 134 +- .../core/kernels/conv_grad_input_ops.cc | 146 +- tensorflow/core/kernels/conv_grad_ops.cc | 51 +- tensorflow/core/kernels/conv_grad_ops.h | 20 +- tensorflow/core/kernels/conv_grad_ops_3d.cc | 21 +- tensorflow/core/kernels/conv_ops.cc | 223 +-- tensorflow/core/kernels/conv_ops.h | 13 +- .../core/kernels/depthwise_conv_grad_op.cc | 6 +- tensorflow/core/kernels/depthwise_conv_op.cc | 3 +- tensorflow/core/ops/nn_ops.cc | 11 +- tensorflow/core/util/padding.cc | 43 + tensorflow/core/util/padding.h | 19 +- tensorflow/core/util/tensor_format.h | 27 +- .../python/keras/layers/convolutional.py | 4 +- tensorflow/python/keras/utils/conv_utils.py | 4 +- .../python/kernel_tests/conv_ops_test.py | 1232 +++++++++++++++-- tensorflow/python/ops/nn_grad.py | 15 +- tensorflow/python/ops/nn_ops.py | 316 ++++- 29 files changed, 2218 insertions(+), 417 deletions(-) diff --git a/tensorflow/compiler/tf2xla/kernels/conv_op_helpers.cc b/tensorflow/compiler/tf2xla/kernels/conv_op_helpers.cc index 6b049f653c..5b4f863f74 100644 --- a/tensorflow/compiler/tf2xla/kernels/conv_op_helpers.cc +++ b/tensorflow/compiler/tf2xla/kernels/conv_op_helpers.cc @@ -212,8 +212,8 @@ Status ConvBackpropComputeDimensionsV2XlaShapes( XLAShapeToTensorShape(out_backprop_shape, &out_backprop_tensor_shape)); return ConvBackpropComputeDimensionsV2( label, num_spatial_dims, input_tensor_shape, filter_tensor_shape, - out_backprop_tensor_shape, dilations, strides, padding, data_format, - dims); + out_backprop_tensor_shape, dilations, strides, padding, + /*explicit_paddings=*/{}, data_format, dims); } } // anonymous namespace @@ -227,6 +227,11 @@ xla::StatusOr ConvOpAttrs::Create(int num_spatial_dims, TF_RETURN_IF_ERROR(ctx->GetAttr("dilations", &attrs.dilations)); TF_RETURN_IF_ERROR(ctx->GetAttr("strides", &attrs.strides)); TF_RETURN_IF_ERROR(ctx->GetAttr("padding", &attrs.padding)); + // TODO(reedwm): Support explicit padding. + if (attrs.padding == EXPLICIT) { + return errors::Unimplemented( + "XLA does not yet support Conv2D with explicit padding."); + } string data_format; TF_RETURN_IF_ERROR(ctx->GetAttr("data_format", &data_format)); diff --git a/tensorflow/core/api_def/base_api/api_def_Conv2D.pbtxt b/tensorflow/core/api_def/base_api/api_def_Conv2D.pbtxt index 070d6adb97..d0794de4ba 100644 --- a/tensorflow/core/api_def/base_api/api_def_Conv2D.pbtxt +++ b/tensorflow/core/api_def/base_api/api_def_Conv2D.pbtxt @@ -33,6 +33,15 @@ END name: "padding" description: < 0, but got ", stride); } @@ -137,6 +152,11 @@ Status GetWindowedOutputSizeFromDimsV2( // See also the parallel implementation in GetWindowedOutputSizeVerbose. switch (padding_type) { case Padding::VALID: + padding_before = padding_after = 0; + TF_FALLTHROUGH_INTENDED; + case Padding::EXPLICIT: + TF_RETURN_IF_ERROR( + c->Add(input_size, padding_before + padding_after, &input_size)); if (dilation_rate > 1) { DimensionHandle window_size; TF_RETURN_IF_ERROR( @@ -166,9 +186,18 @@ Status GetWindowedOutputSizeFromDims( shape_inference::DimensionHandle input_size, shape_inference::DimensionOrConstant filter_size, int64 stride, Padding padding_type, shape_inference::DimensionHandle* output_size) { + if (padding_type == Padding::EXPLICIT) { + return errors::Internal( + "GetWindowedOutputSizeFromDims does not handle EXPLICIT padding; call " + "GetWindowedOutputSizeFromDimsV2 instead"); + } return GetWindowedOutputSizeFromDimsV2(c, input_size, filter_size, /*dilation_rate=*/1, stride, - padding_type, output_size); + padding_type, + // Give dummy values of -1 to + // padding_before and padding_after, + // since explicit padding is not used. + -1, -1, output_size); } Status UnchangedShape(shape_inference::InferenceContext* c) { @@ -371,7 +400,10 @@ Status ShapeFromDimensions(DimensionHandle batch_dim, return tensorflow::Status::OK(); } -Status Conv2DShape(shape_inference::InferenceContext* c) { +namespace { + +Status Conv2DShapeImpl(shape_inference::InferenceContext* c, + bool supports_explicit_padding) { string data_format_str, filter_format_str; if (!c->GetAttr("data_format", &data_format_str).ok()) { data_format_str = "NHWC"; @@ -464,13 +496,30 @@ Status Conv2DShape(shape_inference::InferenceContext* c) { Padding padding; TF_RETURN_IF_ERROR(c->GetAttr("padding", &padding)); + std::vector explicit_paddings; + if (supports_explicit_padding) { + TF_RETURN_IF_ERROR(c->GetAttr("explicit_paddings", &explicit_paddings)); + TF_RETURN_IF_ERROR(CheckValidPadding(padding, explicit_paddings, + /*num_dims=*/4, data_format)); + } else { + DCHECK(padding != Padding::EXPLICIT); + } + DimensionHandle output_rows, output_cols; + int64 pad_rows_before = -1, pad_rows_after = -1; + int64 pad_cols_before = -1, pad_cols_after = -1; + if (padding == Padding::EXPLICIT) { + GetExplicitPaddingForDim(explicit_paddings, data_format, 'H', + &pad_rows_before, &pad_rows_after); + GetExplicitPaddingForDim(explicit_paddings, data_format, 'W', + &pad_cols_before, &pad_cols_after); + } TF_RETURN_IF_ERROR(GetWindowedOutputSizeFromDimsV2( c, input_spatial_dims[0], filter_rows_dim, dilation_rows, stride_rows, - padding, &output_rows)); + padding, pad_rows_before, pad_rows_after, &output_rows)); TF_RETURN_IF_ERROR(GetWindowedOutputSizeFromDimsV2( c, input_spatial_dims[1], filter_cols_dim, dilation_cols, stride_cols, - padding, &output_cols)); + padding, pad_cols_before, pad_cols_after, &output_cols)); ShapeHandle output_shape; TF_RETURN_IF_ERROR( @@ -480,6 +529,19 @@ Status Conv2DShape(shape_inference::InferenceContext* c) { return Status::OK(); } +} // namespace + +// Shape function for Conv2D-like operations that support explicit padding. +Status Conv2DShapeWithExplicitPadding(shape_inference::InferenceContext* c) { + return Conv2DShapeImpl(c, true); +} + +// Shape function for Conv2D-like operations that do not support explicit +// padding. +Status Conv2DShape(shape_inference::InferenceContext* c) { + return Conv2DShapeImpl(c, false); +} + // TODO(mjanusz): Unify all conv/pooling shape functions. Status Conv3DShape(shape_inference::InferenceContext* c) { ShapeHandle input_shape; @@ -551,13 +613,13 @@ Status Conv3DShape(shape_inference::InferenceContext* c) { TF_RETURN_IF_ERROR(GetWindowedOutputSizeFromDimsV2( c, in_planes_dim, filter_planes_dim, dilation_planes, stride_planes, - padding, &output_planes)); + padding, -1, -1, &output_planes)); TF_RETURN_IF_ERROR(GetWindowedOutputSizeFromDimsV2( - c, in_rows_dim, filter_rows_dim, dilation_rows, stride_rows, padding, - &output_rows)); + c, in_rows_dim, filter_rows_dim, dilation_rows, stride_rows, padding, -1, + -1, &output_rows)); TF_RETURN_IF_ERROR(GetWindowedOutputSizeFromDimsV2( - c, in_cols_dim, filter_cols_dim, dilation_cols, stride_cols, padding, - &output_cols)); + c, in_cols_dim, filter_cols_dim, dilation_cols, stride_cols, padding, -1, + -1, &output_cols)); ShapeHandle output_shape; if (data_format == "NCDHW") { diff --git a/tensorflow/core/framework/common_shape_fns.h b/tensorflow/core/framework/common_shape_fns.h index 362899b947..14b9688bdc 100644 --- a/tensorflow/core/framework/common_shape_fns.h +++ b/tensorflow/core/framework/common_shape_fns.h @@ -38,11 +38,12 @@ namespace tensorflow { // // Padding (P): the padding we apply to the input tensor along each // dimension. This is usually used to make sure that the spatial dimensions -// do not shrink when we progress with convolutions. Two types of padding are -// often used: +// do not shrink when we progress with convolutions. This function supports two +// types of padding. // SAME: the pad value is computed so that the output will have size H/S. // VALID: no padding is carried out. -// The padded area is zero-filled. +// If you want to use EXPLICIT padding, GetWindowedOutputSizeVerbose must be +// called instead. Note the padded area is zero-filled. // // The output dimensions for convolution and many other operations, when given // all the parameters above, are as follows: @@ -95,6 +96,9 @@ Status GetWindowedOutputSize(int64 input_size, int64 filter_size, int64 stride, // When the stride is 1, the expression simplifies to // H' = H-K'+1. // +// If you want to use EXPLICIT padding, GetWindowedOutputSizeVerboseV2 must be +// called instead +// // TODO(b/67112639): Merge V2 versions and the original versions eventually. Status GetWindowedOutputSizeV2(int64 input_size, int64 filter_size, int64 dilation_rate, int64 stride, @@ -102,9 +106,12 @@ Status GetWindowedOutputSizeV2(int64 input_size, int64 filter_size, int64* padding_size); // Returns the same output dimensions as in GetWindowedOutputSize, but returns -// verbose padding dimensions (before/after). Any excess padding -// (caused by an odd padding size value) is added to the 'padding_after' -// dimension. +// verbose padding dimensions (before/after), and EXPLICIT padding is supported. +// When padding_type is EXPLICIT, *padding_before and *padding_after must +// already point to initialized integers with the padding amounts. Otherwise, +// *padding_before and *padding_after are set by this function, and any +// excess padding (caused by an odd padding size value) is added to the +// 'padding_after' dimension. Status GetWindowedOutputSizeVerbose(int64 input_size, int64 filter_size, int64 stride, Padding padding_type, int64* output_size, int64* padding_before, @@ -122,7 +129,8 @@ Status GetWindowedOutputSizeVerboseV2(int64 input_size, int64 filter_size, // of the output tensor and padding to be applied to the input tensor at the // lower end of every dimension. Use for 3D convolutions, where the input data // is padded with zeros, as well as for 3D avg/max pooling, where the input data -// is padded with invalid values that are not considered for pooling. +// is padded with invalid values that are not considered for pooling. EXPLICIT +// padding is not supported. Status Get3dOutputSize(const std::array& input, const std::array& window, const std::array& strides, @@ -140,21 +148,23 @@ Status Get3dOutputSizeV2(const std::array& input, namespace shape_inference { -// Like GetWindowedOutputSize, but deals with DimensionHandles. +// Like GetWindowedOutputSize, but deals with DimensionHandles. Does not support +// EXPLICIT padding. Status GetWindowedOutputSizeFromDims(InferenceContext* c, DimensionHandle input_size, DimensionOrConstant filter_size, int64 stride, Padding padding_type, DimensionHandle* output_size); -// The V2 version computes the same outputs with arbitrary dilation_rate. For -// detailed equations, refer to the comments for GetWindowedOutputSizeV2(). -Status GetWindowedOutputSizeFromDimsV2(InferenceContext* c, - DimensionHandle input_size, - DimensionOrConstant filter_size, - int64 dilation_rate, int64 stride, - Padding padding_type, - DimensionHandle* output_size); +// The V2 version computes the same outputs with arbitrary dilation_rate, and +// supports EXPLICIT padding. For detailed equations, refer to the comments +// for GetWindowedOutputSizeV2(). The 'padding_before' and 'padding_after' +// parameters are only used if padding_type == EXPLICIT. +Status GetWindowedOutputSizeFromDimsV2( + InferenceContext* c, DimensionHandle input_size, + DimensionOrConstant filter_size, int64 dilation_rate, int64 stride, + Padding padding_type, int64 padding_before, int64 padding_after, + DimensionHandle* output_size); // Transfers shape of input(0) to output(0). Status UnchangedShape(shape_inference::InferenceContext* c); @@ -222,7 +232,11 @@ Status BiasAddShape(shape_inference::InferenceContext* c); // Shape function for BiasAddGrad-like operations. Status BiasAddGradShape(shape_inference::InferenceContext* c); -// Shape function for Conv2D-like operations. +// Shape function for Conv2D-like operations that support explicit padding. +Status Conv2DShapeWithExplicitPadding(shape_inference::InferenceContext* c); + +// Shape function for Conv2D-like operations that do not support explicit +// padding. Status Conv2DShape(shape_inference::InferenceContext* c); // Shape function for Conv3D-like operations. diff --git a/tensorflow/core/framework/common_shape_fns_test.cc b/tensorflow/core/framework/common_shape_fns_test.cc index 7c395679d3..b94925c04e 100644 --- a/tensorflow/core/framework/common_shape_fns_test.cc +++ b/tensorflow/core/framework/common_shape_fns_test.cc @@ -408,12 +408,14 @@ TEST(CommonShapeFnsTest, BiasAddGradShapeTest) { TEST(CommonShapeFnsTest, Conv2DShapeTest) { ShapeInferenceTestOp op("Conv2D"); auto set_op = [&op](const std::vector& strides, const string& padding, - const string& data_format, const string& filter_format) { + const string& data_format, const string& filter_format, + const std::vector& explicit_paddings = {}) { TF_CHECK_OK(NodeDefBuilder("test", "Conv2D") .Input("input", 0, DT_FLOAT) .Input("filter", 0, DT_FLOAT) .Attr("strides", strides) .Attr("padding", padding) + .Attr("explicit_paddings", explicit_paddings) .Attr("data_format", data_format) .Attr("filter_format", filter_format) .Finalize(&op.node_def)); @@ -536,19 +538,73 @@ TEST(CommonShapeFnsTest, Conv2DShapeTest) { INFER_OK(op, "[1,?,4,1];[?,?,?,?]", "[d0_0,?,2,d1_3]"); INFER_OK(op, "[1,4,?,1];[?,?,?,?]", "[d0_0,2,?,d1_3]"); INFER_OK(op, "[1,4,4,?];[?,?,?,?]", "[d0_0,2,2,d1_3]"); + + // Some tests for "EXPLICIT" padding + + // 4x4 input, 1x1 filter, 1x1 stride, [0, 2, 1, 4] padding + set_op({{1, 1, 1, 1}}, "EXPLICIT", "NHWC", "HWIO", {0, 0, 0, 2, 1, 4, 0, 0}); + INFER_OK(op, "[1,4,4,1];[1,1,1,1]", "[d0_0,6,9,d1_3]"); + + // 3x3 input, 2x2 filter, 1x1 stride, [1, 0, 1, 2] padding + set_op({{1, 1, 1, 1}}, "EXPLICIT", "NHWC", "HWIO", {0, 0, 1, 0, 1, 2, 0, 0}); + INFER_OK(op, "[1,3,3,1];[2,2,1,1]", "[d0_0,3,5,d1_3]"); + + // 4x4 input, 2x2 filter, 2x2 stride, [3, 2, 1, 0] padding + set_op({{1, 2, 2, 1}}, "EXPLICIT", "NHWC", "HWIO", {0, 0, 3, 2, 1, 0, 0, 0}); + INFER_OK(op, "[1,4,4,2];[2,2,2,3]", "[d0_0,4,2,d1_3]"); + + // 2x2 input, 2x1 filter, 1x2 stride, [1, 1, 2, 2] padding + set_op({{1, 1, 2, 1}}, "EXPLICIT", "NHWC", "HWIO", {0, 0, 1, 1, 2, 2, 0, 0}); + INFER_OK(op, "[1,2,2,1];[2,1,1,1]", "[d0_0,3,3,d1_3]"); + + // Unknown dims in the critical fields lead to partial inference. + INFER_OK(op, "[1,4,4,1];[2,1,1,1]", "[d0_0,5,4,d1_3]"); + INFER_OK(op, "[1,?,4,1];[2,1,1,1]", "[d0_0,?,4,d1_3]"); + INFER_OK(op, "[1,4,?,1];[2,1,1,1]", "[d0_0,5,?,d1_3]"); + INFER_OK(op, "[1,4,4,?];[2,1,1,1]", "[d0_0,5,4,d1_3]"); + INFER_OK(op, "[1,4,4,1];[?,1,1,1]", "[d0_0,?,4,d1_3]"); + INFER_OK(op, "[1,4,4,1];[2,?,1,1]", "[d0_0,5,?,d1_3]"); + + // Explicit padding errors + // Negative padding + set_op({{1, 1, 1, 1}}, "EXPLICIT", "NHWC", "HWIO", {0, 0, 0, -1, 0, 0, 0, 0}); + INFER_ERROR("must be nonnegative", op, "[1,2,2,1];[1,1,1,1]"); + + // Too little padding (7 explicit paddings instead of 8) + set_op({{1, 1, 1, 1}}, "EXPLICIT", "NHWC", "HWIO", {0, 0, 0, 0, 0, 0, 0}); + INFER_ERROR("must contain 8 values", op, "[1,2,2,1];[1,1,1,1]"); + + // Too much padding (9 explicit paddings instead of 8) + set_op({{1, 1, 1, 1}}, "EXPLICIT", "NHWC", "HWIO", + {0, 0, 0, 0, 0, 0, 0, 0, 0}); + INFER_ERROR("must contain 8 values", op, "[1,2,2,1];[1,1,1,1]"); + + // Padding in batch dimension + set_op({{1, 1, 1, 1}}, "EXPLICIT", "NHWC", "HWIO", {1, 0, 0, 0, 0, 0, 0, 0}); + INFER_ERROR("batch or depth dimensions", op, "[1,2,2,1];[1,1,1,1]"); + + // Padding in depth dimension + set_op({{1, 1, 1, 1}}, "EXPLICIT", "NHWC", "HWIO", {0, 0, 0, 0, 0, 0, 1, 0}); + INFER_ERROR("batch or depth dimensions", op, "[1,2,2,1];[1,1,1,1]"); + + // Padding explicit_paddings when padding is not EXPLICIT + set_op({{1, 1, 1, 1}}, "VALID", "NHWC", "HWIO", {0, 0, 0, 0, 0, 0, 0, 0}); + INFER_ERROR("must be empty", op, "[1,2,2,1];[1,1,1,1]"); } TEST(CommonShapeFnsTest, Conv2DDilatedShapeTest) { ShapeInferenceTestOp op("Conv2D"); auto set_op = [&op](const std::vector& dilations, const std::vector& strides, const string& padding, - const string& data_format) { + const string& data_format, + const std::vector& explicit_paddings = {}) { TF_CHECK_OK(NodeDefBuilder("test", "Conv2D") .Input("input", 0, DT_FLOAT) .Input("filter", 0, DT_FLOAT) .Attr("dilations", dilations) .Attr("strides", strides) .Attr("padding", padding) + .Attr("explicit_paddings", explicit_paddings) .Attr("data_format", data_format) .Finalize(&op.node_def)); }; @@ -628,6 +684,28 @@ TEST(CommonShapeFnsTest, Conv2DDilatedShapeTest) { // 4x4 input, 2x2 filter, 2x2 dilations, 1x1 stride set_op({{1, 2, 2, 1}}, {{1, 1, 1, 1}}, "SAME", "NHWC"); INFER_OK(op, "[1,4,4,1];[2,2,1,1]", "[d0_0,d0_1,d0_2,d1_3]"); + + // Some tests for "EXPLICIT" padding + + // 4x4 input, 1x1 filter, 2x1 dilations, 1x1 stride, [0, 2, 1, 4] padding + set_op({{1, 2, 1, 1}}, {{1, 1, 1, 1}}, "EXPLICIT", "NHWC", + {0, 0, 0, 2, 1, 4, 0, 0}); + INFER_OK(op, "[1,4,4,1];[1,1,1,1]", "[d0_0,6,9,d1_3]"); + + // 3x3 input, 2x2 filter, 2x2 dilations, 1x1 stride, [1, 0, 1, 2] padding + set_op({{1, 2, 2, 1}}, {{1, 1, 1, 1}}, "EXPLICIT", "NHWC", + {0, 0, 1, 0, 1, 2, 0, 0}); + INFER_OK(op, "[1,3,3,1];[2,2,1,1]", "[d0_0,2,4,d1_3]"); + + // 4x4 input, 2x2 filter, 1x2 dilations, 2x2 stride, [3, 2, 1, 0] padding + set_op({{1, 1, 2, 1}}, {{1, 2, 2, 1}}, "EXPLICIT", "NHWC", + {0, 0, 3, 2, 1, 0, 0, 0}); + INFER_OK(op, "[1,4,4,1];[2,2,1,1]", "[d0_0,4,2,d1_3]"); + + // 4x4 input, 2x2 filter, 2x2 dilations, 1x1 stride, [1, 1, 2, 2] padding + set_op({{1, 2, 2, 1}}, {{1, 1, 1, 1}}, "EXPLICIT", "NHWC", + {0, 0, 1, 1, 2, 2, 0, 0}); + INFER_OK(op, "[1,4,4,1];[2,2,1,1]", "[d0_0,4,6,d1_3]"); } TEST(CommonShapeFnsTest, Conv3DShapeTest) { diff --git a/tensorflow/core/framework/ops_util.cc b/tensorflow/core/framework/ops_util.cc index e8cf014ca0..4e603b9598 100644 --- a/tensorflow/core/framework/ops_util.cc +++ b/tensorflow/core/framework/ops_util.cc @@ -30,6 +30,9 @@ Eigen::PaddingType BrainPadding2EigenPadding(Padding padding) { return Eigen::PADDING_VALID; case Padding::SAME: return Eigen::PADDING_SAME; + case Padding::EXPLICIT: + LOG(FATAL) << "Eigen does not have explicit padding enum " // Crash OK + "value"; } return Eigen::PADDING_SAME; // Prevent compiler warning about missing return } diff --git a/tensorflow/core/grappler/costs/graph_properties_testdata/large_function_graph.pbtxt b/tensorflow/core/grappler/costs/graph_properties_testdata/large_function_graph.pbtxt index 415c347a1d..d4e23e901a 100644 --- a/tensorflow/core/grappler/costs/graph_properties_testdata/large_function_graph.pbtxt +++ b/tensorflow/core/grappler/costs/graph_properties_testdata/large_function_graph.pbtxt @@ -511,6 +511,13 @@ library { s: "VALID" } } + attr { + key: "explicit_paddings" + value { + list { + } + } + } attr { key: "strides" value { diff --git a/tensorflow/core/grappler/optimizers/layout_optimizer.cc b/tensorflow/core/grappler/optimizers/layout_optimizer.cc index 8f25a1c8c1..e9b706a583 100644 --- a/tensorflow/core/grappler/optimizers/layout_optimizer.cc +++ b/tensorflow/core/grappler/optimizers/layout_optimizer.cc @@ -503,6 +503,7 @@ class NodeProcessor : public GraphProcessor { UpdateAttrKSize(); UpdateAttrStrides(); UpdateAttrDilations(); + UpdateAttrExplicitPaddings(); UpdateAttrShape(); TF_RETURN_IF_ERROR(AddLayoutTransposeToInputs()); TF_RETURN_IF_ERROR(AddLayoutTransposeToOutputs()); @@ -753,6 +754,28 @@ class NodeProcessor : public GraphProcessor { } } + void UpdateAttrExplicitPaddings() { + if (node_->attr().find("explicit_paddings") != node_->attr().end()) { + auto list = node_->mutable_attr()->at("explicit_paddings").mutable_list(); + int size = list->i_size(); + if (size == 8) { + int64 height_before = list->i(2); + int64 height_after = list->i(3); + int64 width_before = list->i(4); + int64 width_after = list->i(5); + list->set_i(2, 0); + list->set_i(3, 0); + list->set_i(4, height_before); + list->set_i(5, height_after); + list->set_i(6, width_before); + list->set_i(7, width_after); + } else if (size != 0) { + LOG(ERROR) << "Cannot handle explicit_paddings attribute of size " + << size; + } + } + } + void UpdateAttrDataFormat() { if (node_->attr().find("data_format") != node_->attr().end()) { if (node_->attr().at("data_format").s().compare("NHWC") == 0) { diff --git a/tensorflow/core/grappler/optimizers/layout_optimizer_test.cc b/tensorflow/core/grappler/optimizers/layout_optimizer_test.cc index 20e47c1b26..eb2a8e87dd 100644 --- a/tensorflow/core/grappler/optimizers/layout_optimizer_test.cc +++ b/tensorflow/core/grappler/optimizers/layout_optimizer_test.cc @@ -15,6 +15,7 @@ limitations under the License. #include "tensorflow/core/grappler/optimizers/layout_optimizer.h" #include "tensorflow/cc/ops/standard_ops.h" +#include "tensorflow/core/framework/common_shape_fns.h" #include "tensorflow/core/framework/node_def.pb.h" #include "tensorflow/core/framework/tensor_testutil.h" #include "tensorflow/core/grappler/clusters/single_machine.h" @@ -80,8 +81,13 @@ class LayoutOptimizerTest : public GrapplerTest { Output filter = ops::Const(s->WithOpName("Filter"), Input::Initializer(filter_data)); + ops::Conv2D::Attrs attrs; + if (padding == "EXPLICIT") { + attrs = attrs.ExplicitPaddings({0, 0, 1, 2, 3, 4, 0, 0}); + } + Output conv = ops::Conv2D(s->WithOpName("Conv2D").WithDevice(device), input, - filter, {1, stride, stride, 1}, padding); + filter, {1, stride, stride, 1}, padding, attrs); return conv; } @@ -100,6 +106,28 @@ class LayoutOptimizerTest : public GrapplerTest { int input_depth = 3; int filter_count = 2; int stride = 1; + int dilation = dilated ? 2 : 1; + int64 padding_top = 1; + int64 padding_bottom = 2; + int64 padding_left = 3; + int64 padding_right = 4; + int64 output_height; + int64 output_width; + Padding padding_enum; + if (padding == "SAME") { + padding_enum = SAME; + } else if (padding == "VALID") { + padding_enum = VALID; + } else { + CHECK_EQ(padding, "EXPLICIT"); + padding_enum = EXPLICIT; + } + TF_CHECK_OK(GetWindowedOutputSizeVerboseV2( + input_height, filter_size, dilation, stride, padding_enum, + &output_height, &padding_top, &padding_bottom)); + TF_CHECK_OK(GetWindowedOutputSizeVerboseV2( + input_width, filter_size, dilation, stride, padding_enum, &output_width, + &padding_left, &padding_right)); TensorShape input_sizes_shape({4}); Tensor input_data(DT_INT32, input_sizes_shape); test::FillValues(&input_data, @@ -112,8 +140,6 @@ class LayoutOptimizerTest : public GrapplerTest { Output filter = ops::Variable(s->WithOpName("Filter"), filter_shape, DT_FLOAT); - int output_height = input_height; - int output_width = input_width; TensorShape output_shape( {batch_size, output_height, output_width, filter_count}); Tensor output_data(DT_FLOAT, output_shape); @@ -124,10 +150,21 @@ class LayoutOptimizerTest : public GrapplerTest { Output conv_backprop_input; Output input_sizes_i = ops::Identity(s->WithOpName("InputSizesIdentity"), input_sizes); - ops::Conv2DBackpropInput::Attrs attrs; - if (dilated) { - attrs = attrs.Dilations({1, 2, 2, 1}); + std::vector dilations{1, dilation, dilation, 1}; + std::vector explicit_paddings; + if (padding == "EXPLICIT") { + explicit_paddings = {0, + 0, + static_cast(padding_top), + static_cast(padding_bottom), + static_cast(padding_left), + static_cast(padding_right), + 0, + 0}; } + auto attrs = + ops::Conv2DBackpropInput::Attrs().Dilations(dilations).ExplicitPaddings( + explicit_paddings); if (const_input_size) { conv_backprop_input = ops::Conv2DBackpropInput( s->WithOpName("Conv2DBackpropInput"), input_sizes, filter, output, @@ -186,7 +223,7 @@ class LayoutOptimizerTest : public GrapplerTest { TEST_F(LayoutOptimizerTest, Conv2DBackpropInput) { tensorflow::Scope s = tensorflow::Scope::NewRootScope(); - auto conv = SimpleConv2DBackpropInput(&s, 7, 2, "SAME"); + auto conv = SimpleConv2DBackpropInput(&s, 7, 2, "EXPLICIT"); Output fetch = ops::Identity(s.WithOpName("Fetch"), {conv}); GrapplerItem item; TF_CHECK_OK(s.ToGraphDef(&item.graph)); @@ -306,6 +343,19 @@ TEST_F(LayoutOptimizerTest, NotEqualSizeWithValidPadding) { EXPECT_TRUE(node_map.GetNode("Conv2D-0-TransposeNHWCToNCHW-LayoutOptimizer")); } +TEST_F(LayoutOptimizerTest, ExplicitPadding) { + tensorflow::Scope s = tensorflow::Scope::NewRootScope(); + auto conv = SimpleConv2D(&s, 4, 2, "EXPLICIT"); + Output fetch = ops::Identity(s.WithOpName("Fetch"), {conv}); + GrapplerItem item; + TF_CHECK_OK(s.ToGraphDef(&item.graph)); + LayoutOptimizer optimizer; + GraphDef output; + Status status = optimizer.Optimize(virtual_cluster_.get(), item, &output); + NodeMap node_map(&output); + EXPECT_TRUE(node_map.GetNode("Conv2D-0-TransposeNHWCToNCHW-LayoutOptimizer")); +} + TEST_F(LayoutOptimizerTest, Pad) { tensorflow::Scope s = tensorflow::Scope::NewRootScope(); auto conv = SimpleConv2D(&s, 4, 2, "VALID"); diff --git a/tensorflow/core/kernels/conv_grad_filter_ops.cc b/tensorflow/core/kernels/conv_grad_filter_ops.cc index 4e3de33e83..0df05ceb02 100644 --- a/tensorflow/core/kernels/conv_grad_filter_ops.cc +++ b/tensorflow/core/kernels/conv_grad_filter_ops.cc @@ -102,6 +102,7 @@ struct LaunchConv2DBackpropFilterOp { const Tensor& out_backprop, const Tensor& input, int row_dilation, int col_dilation, int row_stride, int col_stride, const Padding& padding, + const std::vector& explicit_paddings, Tensor* filter_backprop, TensorFormat data_format) { const CPUDevice& d = ctx->eigen_device(); functor::SpatialConvolutionBackwardFilter()( @@ -204,6 +205,15 @@ class Conv2DFastBackpropFilterOp : public OpKernel { errors::InvalidArgument( "Row and column strides should be larger than 0.")); OP_REQUIRES_OK(context, context->GetAttr("padding", &padding_)); + OP_REQUIRES( + context, padding_ != Padding::EXPLICIT, + errors::Unimplemented("Current CPU implementation does not support " + "EXPLICIT padding yet.")); + std::vector explicit_paddings; + OP_REQUIRES_OK(context, + context->GetAttr("explicit_paddings", &explicit_paddings)); + OP_REQUIRES_OK(context, CheckValidPadding(padding_, explicit_paddings, + /*num_dims=*/4, data_format_)); OP_REQUIRES_OK(context, context->GetAttr("dilations", &dilations_)); OP_REQUIRES(context, dilations_.size() == 4, errors::InvalidArgument("Sliding window dilations field must " @@ -282,7 +292,8 @@ class Conv2DFastBackpropFilterOp : public OpKernel { LaunchConv2DBackpropFilterOp()( context, false, false, out_backprop, input, /*row_dilation=*/1, /*col_dilation=*/1, dims.spatial_dims[0].stride, - dims.spatial_dims[1].stride, padding_, filter_backprop, data_format_); + dims.spatial_dims[1].stride, padding_, /*explicit_paddings=*/{}, + filter_backprop, data_format_); } private: @@ -319,6 +330,15 @@ class Conv2DCustomBackpropFilterOp : public OpKernel { errors::InvalidArgument( "Row and column strides should be larger than 0.")); OP_REQUIRES_OK(context, context->GetAttr("padding", &padding_)); + OP_REQUIRES( + context, padding_ != Padding::EXPLICIT, + errors::Unimplemented("Current CPU implementation does not support " + "EXPLICIT padding yet.")); + std::vector explicit_paddings; + OP_REQUIRES_OK(context, + context->GetAttr("explicit_paddings", &explicit_paddings)); + OP_REQUIRES_OK(context, CheckValidPadding(padding_, explicit_paddings, + /*num_dims=*/4, data_format_)); OP_REQUIRES_OK(context, context->GetAttr("dilations", &dilations_)); OP_REQUIRES(context, dilations_.size() == 4, errors::InvalidArgument("Sliding window dilations field must " @@ -587,6 +607,10 @@ class Conv2DSlowBackpropFilterOp : public OpKernel { use_cudnn_ &= CanUseCudnn(); cudnn_use_autotune_ = CudnnUseAutotune(); OP_REQUIRES_OK(context, context->GetAttr("padding", &padding_)); + OP_REQUIRES_OK(context, + context->GetAttr("explicit_paddings", &explicit_paddings_)); + OP_REQUIRES_OK(context, CheckValidPadding(padding_, explicit_paddings_, + /*num_dims=*/4, data_format_)); } void Compute(OpKernelContext* context) override { @@ -626,13 +650,14 @@ class Conv2DSlowBackpropFilterOp : public OpKernel { launcher_(context, use_cudnn_, cudnn_use_autotune_, out_backprop, input, dilation_rows, dilation_cols, stride_rows, stride_cols, padding_, - filter_backprop, data_format_); + explicit_paddings_, filter_backprop, data_format_); } private: std::vector dilations_; std::vector strides_; Padding padding_; + std::vector explicit_paddings_; bool use_cudnn_; TensorFormat data_format_; LaunchConv2DBackpropFilterOp launcher_; @@ -646,7 +671,8 @@ void LaunchConv2DBackpropFilterOp::operator()( OpKernelContext* ctx, bool use_cudnn, bool cudnn_use_autotune, const Tensor& out_backprop, const Tensor& input, int row_dilation, int col_dilation, int row_stride, int col_stride, const Padding& padding, - Tensor* filter_backprop, TensorFormat data_format) { + const std::vector& explicit_paddings, Tensor* filter_backprop, + TensorFormat data_format) { using se::dnn::AlgorithmConfig; using se::dnn::AlgorithmDesc; using se::dnn::ProfileResult; @@ -661,35 +687,33 @@ void LaunchConv2DBackpropFilterOp::operator()( TensorShape filter_shape = filter_backprop->shape(); ConvBackpropDimensions dims; - OP_REQUIRES_OK(ctx, ConvBackpropComputeDimensionsV2( - "Conv2DSlowBackpropFilter", /*num_spatial_dims=*/2, - input.shape(), filter_shape, out_backprop.shape(), - dilations, strides, padding, data_format, &dims)); - - // TODO(yangzihao): The padding computations should be done in - // GetWindowedOutputSize() functions. - const int padding_rows = - (padding == VALID) - ? 0 - : std::max(0, (dims.spatial_dims[0].output_size - 1) * - dims.spatial_dims[0].stride + - (dims.spatial_dims[0].filter_size - 1) * - dims.spatial_dims[0].dilation + - 1 - dims.spatial_dims[0].input_size); - const int padding_cols = - (padding == VALID) - ? 0 - : std::max(0, (dims.spatial_dims[1].output_size - 1) * - dims.spatial_dims[1].stride + - (dims.spatial_dims[1].filter_size - 1) * - dims.spatial_dims[1].dilation + - 1 - dims.spatial_dims[1].input_size); - - // TODO(zhengxq): cuDNN only supports equal padding on both sides, so only - // calling it when that is true. Remove this check when (if?) cuDNN starts - // supporting different padding. - bool rows_odd = (padding_rows % 2 != 0); - bool cols_odd = (padding_cols % 2 != 0); + OP_REQUIRES_OK( + ctx, ConvBackpropComputeDimensionsV2( + "Conv2DSlowBackpropFilter", /*num_spatial_dims=*/2, + input.shape(), filter_shape, out_backprop.shape(), dilations, + strides, padding, explicit_paddings, data_format, &dims)); + + int64 padding_top = -1, padding_bottom = -1; + int64 padding_left = -1, padding_right = -1; + if (padding == EXPLICIT) { + GetExplicitPaddingForDim(explicit_paddings, data_format, 'H', &padding_top, + &padding_bottom); + GetExplicitPaddingForDim(explicit_paddings, data_format, 'W', &padding_left, + &padding_right); + } + int64 expected_out_rows, expected_out_cols; + // The function is guaranteed to succeed because we checked the output and + // padding was valid earlier. + TF_CHECK_OK(GetWindowedOutputSizeVerboseV2( + dims.spatial_dims[0].input_size, dims.spatial_dims[0].filter_size, + row_dilation, row_stride, padding, &expected_out_rows, &padding_top, + &padding_bottom)); + DCHECK_EQ(dims.spatial_dims[0].output_size, expected_out_rows); + TF_CHECK_OK(GetWindowedOutputSizeVerboseV2( + dims.spatial_dims[1].input_size, dims.spatial_dims[1].filter_size, + col_dilation, col_stride, padding, &expected_out_cols, &padding_left, + &padding_right)); + DCHECK_EQ(dims.spatial_dims[1].output_size, expected_out_cols); auto* stream = ctx->op_device_context()->stream(); OP_REQUIRES(ctx, stream, errors::Internal("No GPU stream available.")); @@ -711,7 +735,7 @@ void LaunchConv2DBackpropFilterOp::operator()( dims.spatial_dims[0].filter_size == 1 && dims.spatial_dims[1].filter_size == 1 && !is_grouped_convolution && dims.spatial_dims[0].stride == 1 && dims.spatial_dims[1].stride == 1 && - data_format == FORMAT_NHWC) { + data_format == FORMAT_NHWC && (padding == VALID || padding == SAME)) { const uint64 m = dims.in_depth; const uint64 k = dims.batch_size * dims.spatial_dims[0].input_size * dims.spatial_dims[1].input_size; @@ -779,31 +803,43 @@ void LaunchConv2DBackpropFilterOp::operator()( return; } + const int64 common_padding_rows = std::min(padding_top, padding_bottom); + const int64 common_padding_cols = std::min(padding_left, padding_right); Tensor compatible_input; - if (rows_odd || cols_odd) { - // If a padding dimension is odd, we have one more element on the right - // side or the bottom side. This is unsupported in cudnn. Therefore, - // we pad that extra element and make it compatible. + if (padding_top != padding_bottom || padding_left != padding_right) { + // Pad the input in the same way we did during the forward pass, so that + // cuDNN receives the same input during the backward pass function as it did + // during the forward pass function. + const int64 padding_rows_diff = std::abs(padding_bottom - padding_top); + const int64 padding_cols_diff = std::abs(padding_right - padding_left); + const int64 new_in_rows = + dims.spatial_dims[0].input_size + padding_rows_diff; + const int64 new_in_cols = + dims.spatial_dims[1].input_size + padding_cols_diff; + const int64 input_pad_top = padding_top - common_padding_rows; + const int64 input_pad_bottom = padding_bottom - common_padding_rows; + const int64 input_pad_left = padding_left - common_padding_cols; + const int64 input_pad_right = padding_right - common_padding_cols; OP_REQUIRES_OK( ctx, ctx->allocate_temp( DataTypeToEnum::value, - ShapeFromFormat(data_format, dims.batch_size, - dims.spatial_dims[0].input_size + rows_odd, - dims.spatial_dims[1].input_size + cols_odd, - dims.in_depth), + ShapeFromFormat(data_format, dims.batch_size, new_in_rows, + new_in_cols, dims.in_depth), &compatible_input)); functor::PadInput()( ctx->template eigen_device(), To32Bit(input.tensor()), - {{0, 0}}, {{rows_odd, cols_odd}}, + {{static_cast(input_pad_top), static_cast(input_pad_left)}}, + {{static_cast(input_pad_bottom), + static_cast(input_pad_right)}}, To32Bit(compatible_input.tensor()), data_format); } else { compatible_input = input; } - CHECK(padding_rows >= 0 && padding_cols >= 0) - << "Negative row or col paddings: (" << padding_rows << ", " - << padding_cols << ")"; + CHECK(common_padding_rows >= 0 && common_padding_cols >= 0) // Crash OK + << "Negative row or col paddings: (" << common_padding_rows << ", " + << common_padding_cols << ")"; se::dnn::BatchDescriptor input_desc; input_desc.set_count(dims.batch_size) .set_height(GetTensorDim(compatible_input, data_format, 'H')) @@ -826,8 +862,8 @@ void LaunchConv2DBackpropFilterOp::operator()( .set_horizontal_dilation_rate(dims.spatial_dims[1].dilation) .set_vertical_filter_stride(dims.spatial_dims[0].stride) .set_horizontal_filter_stride(dims.spatial_dims[1].stride) - .set_zero_padding_height(padding_rows / 2) - .set_zero_padding_width(padding_cols / 2) + .set_zero_padding_height(common_padding_rows) + .set_zero_padding_width(common_padding_cols) .set_group_count(dims.in_depth / filter_shape.dim_size(2)); // NOTE(zhengxq): @@ -922,8 +958,8 @@ void LaunchConv2DBackpropFilterOp::operator()( dims.spatial_dims[1].dilation}}, // dilation_cols {{dims.spatial_dims[0].stride, // stride_rows dims.spatial_dims[1].stride}}, // stride_cols - {{padding_rows, // padding_rows - padding_cols}}, // padding_cols + {{common_padding_rows, // padding_rows + common_padding_cols}}, // padding_cols dtype, // tensor datatype device_id, // device_id }; diff --git a/tensorflow/core/kernels/conv_grad_input_ops.cc b/tensorflow/core/kernels/conv_grad_input_ops.cc index 9f983ed816..74b97b9864 100644 --- a/tensorflow/core/kernels/conv_grad_input_ops.cc +++ b/tensorflow/core/kernels/conv_grad_input_ops.cc @@ -106,8 +106,9 @@ struct LaunchConv2DBackpropInputOp { void operator()(OpKernelContext* ctx, bool use_cudnn, bool cudnn_use_autotune, const Tensor& out_backprop, const Tensor& filter, int row_dilation, int col_dilation, int row_stride, - int col_stride, const Padding& padding, Tensor* in_backprop, - TensorFormat data_format) { + int col_stride, const Padding& padding, + const std::vector& explicit_paddings, + Tensor* in_backprop, TensorFormat data_format) { const CPUDevice& d = ctx->eigen_device(); functor::SpatialConvolutionBackwardInput()( d, in_backprop->tensor(), filter.tensor(), @@ -220,6 +221,15 @@ class Conv2DFastBackpropInputOp : public OpKernel { errors::InvalidArgument( "Current Eigen and libxsmm implementations do not " "yet support dilation rates larger than 1.")); + OP_REQUIRES( + context, padding_ != Padding::EXPLICIT, + errors::Unimplemented("Current CPU implementation does not support " + "EXPLICIT padding yet.")); + std::vector explicit_paddings; + OP_REQUIRES_OK(context, + context->GetAttr("explicit_paddings", &explicit_paddings)); + OP_REQUIRES_OK(context, CheckValidPadding(padding_, explicit_paddings, + /*num_dims=*/4, data_format_)); } void Compute(OpKernelContext* context) override { @@ -286,7 +296,8 @@ class Conv2DFastBackpropInputOp : public OpKernel { LaunchConv2DBackpropInputOp()( context, false, false, out_backprop, filter, /*row_dilation=*/1, /*col_dilation=*/1, dims.spatial_dims[0].stride, - dims.spatial_dims[1].stride, padding_, in_backprop, data_format_); + dims.spatial_dims[1].stride, padding_, /*explicit_paddings=*/{}, + in_backprop, data_format_); } private: @@ -336,6 +347,15 @@ class Conv2DCustomBackpropInputOp : public OpKernel { errors::InvalidArgument( "Current libxsmm and customized CPU implementations do " "not yet support dilation rates larger than 1.")); + OP_REQUIRES( + context, padding_ != Padding::EXPLICIT, + errors::Unimplemented("Current CPU implementation does not support " + "EXPLICIT padding yet.")); + std::vector explicit_paddings; + OP_REQUIRES_OK(context, + context->GetAttr("explicit_paddings", &explicit_paddings)); + OP_REQUIRES_OK(context, CheckValidPadding(padding_, explicit_paddings, + /*num_dims=*/4, data_format_)); } void Compute(OpKernelContext* context) override { @@ -661,6 +681,16 @@ class Conv2DSlowBackpropInputOp : public OpKernel { use_cudnn_ &= CanUseCudnn(); cudnn_use_autotune_ = CudnnUseAutotune(); OP_REQUIRES_OK(context, context->GetAttr("padding", &padding_)); + if (!std::is_same::value) { + OP_REQUIRES( + context, padding_ != Padding::EXPLICIT, + errors::Unimplemented("Current CPU implementation does not support " + "EXPLICIT padding yet.")); + } + OP_REQUIRES_OK(context, + context->GetAttr("explicit_paddings", &explicit_paddings_)); + OP_REQUIRES_OK(context, CheckValidPadding(padding_, explicit_paddings_, + /*num_dims=*/4, data_format_)); } void Compute(OpKernelContext* context) override { @@ -694,13 +724,14 @@ class Conv2DSlowBackpropInputOp : public OpKernel { launcher_(context, use_cudnn_, cudnn_use_autotune_, out_backprop, filter, dilation_rows, dilation_cols, stride_rows, stride_cols, padding_, - in_backprop, data_format_); + explicit_paddings_, in_backprop, data_format_); } private: std::vector dilations_; std::vector strides_; Padding padding_; + std::vector explicit_paddings_; bool use_cudnn_; TensorFormat data_format_; LaunchConv2DBackpropInputOp launcher_; @@ -714,7 +745,8 @@ void LaunchConv2DBackpropInputOp::operator()( OpKernelContext* ctx, bool use_cudnn, bool cudnn_use_autotune, const Tensor& out_backprop, const Tensor& filter, int row_dilation, int col_dilation, int row_stride, int col_stride, const Padding& padding, - Tensor* in_backprop, TensorFormat data_format) { + const std::vector& explicit_paddings, Tensor* in_backprop, + TensorFormat data_format) { using se::dnn::AlgorithmConfig; using se::dnn::AlgorithmDesc; using se::dnn::ProfileResult; @@ -731,35 +763,33 @@ void LaunchConv2DBackpropInputOp::operator()( const TensorShape& filter_shape = filter.shape(); ConvBackpropDimensions dims; - OP_REQUIRES_OK(ctx, ConvBackpropComputeDimensionsV2( - "Conv2DSlowBackpropInput", /*num_spatial_dims=*/2, - input_shape, filter_shape, out_backprop.shape(), - dilations, strides, padding, data_format, &dims)); - - // TODO(yangzihao): The padding computations should be done in - // GetWindowedOutputSize() functions. - const int padding_rows = - (padding == VALID) - ? 0 - : std::max(0, (dims.spatial_dims[0].output_size - 1) * - dims.spatial_dims[0].stride + - (dims.spatial_dims[0].filter_size - 1) * - dims.spatial_dims[0].dilation + - 1 - dims.spatial_dims[0].input_size); - const int padding_cols = - (padding == VALID) - ? 0 - : std::max(0, (dims.spatial_dims[1].output_size - 1) * - dims.spatial_dims[1].stride + - (dims.spatial_dims[1].filter_size - 1) * - dims.spatial_dims[1].dilation + - 1 - dims.spatial_dims[1].input_size); - - // TODO(keveman): cuDNN only supports equal padding on both sides, so only - // calling it when that is true. Remove this check when (if?) cuDNN starts - // supporting different padding. - bool rows_odd = (padding_rows % 2 != 0); - bool cols_odd = (padding_cols % 2 != 0); + OP_REQUIRES_OK( + ctx, ConvBackpropComputeDimensionsV2( + "Conv2DSlowBackpropInput", /*num_spatial_dims=*/2, input_shape, + filter_shape, out_backprop.shape(), dilations, strides, padding, + explicit_paddings, data_format, &dims)); + + int64 padding_top = -1, padding_bottom = -1; + int64 padding_left = -1, padding_right = -1; + if (padding == EXPLICIT) { + GetExplicitPaddingForDim(explicit_paddings, data_format, 'H', &padding_top, + &padding_bottom); + GetExplicitPaddingForDim(explicit_paddings, data_format, 'W', &padding_left, + &padding_right); + } + int64 expected_out_rows, expected_out_cols; + // The function is guaranteed to succeed because we checked the output and + // padding was valid earlier. + TF_CHECK_OK(GetWindowedOutputSizeVerboseV2( + dims.spatial_dims[0].input_size, dims.spatial_dims[0].filter_size, + row_dilation, row_stride, padding, &expected_out_rows, &padding_top, + &padding_bottom)); + DCHECK_EQ(dims.spatial_dims[0].output_size, expected_out_rows); + TF_CHECK_OK(GetWindowedOutputSizeVerboseV2( + dims.spatial_dims[1].input_size, dims.spatial_dims[1].filter_size, + col_dilation, col_stride, padding, &expected_out_cols, &padding_left, + &padding_right)); + DCHECK_EQ(dims.spatial_dims[1].output_size, expected_out_cols); auto* stream = ctx->op_device_context()->stream(); OP_REQUIRES(ctx, stream, errors::Internal("No GPU stream available.")); @@ -779,7 +809,7 @@ void LaunchConv2DBackpropInputOp::operator()( if (dims.spatial_dims[0].filter_size == 1 && dims.spatial_dims[1].filter_size == 1 && !is_grouped_convolution && dims.spatial_dims[0].stride == 1 && dims.spatial_dims[1].stride == 1 && - data_format == FORMAT_NHWC) { + data_format == FORMAT_NHWC && (padding == VALID || padding == SAME)) { // 1x1 filter, so call cublas directly. const uint64 m = dims.batch_size * dims.spatial_dims[0].input_size * dims.spatial_dims[1].input_size; @@ -841,22 +871,28 @@ void LaunchConv2DBackpropInputOp::operator()( return; } + const int64 common_padding_rows = std::min(padding_top, padding_bottom); + const int64 common_padding_cols = std::min(padding_left, padding_right); TensorShape compatible_input_shape; - if (rows_odd || cols_odd) { - // If a padding dimension is odd, we have one more element on the right - // side or the bottom side. This is unsupported in cudnn. Therefore, - // we pad that extra element and make it compatible. + if (padding_top != padding_bottom || padding_left != padding_right) { + // Pad the input in the same way we did during the forward pass, so that + // cuDNN receives the same input during the backward pass function as it did + // during the forward pass function. + const int64 padding_rows_diff = std::abs(padding_bottom - padding_top); + const int64 padding_cols_diff = std::abs(padding_right - padding_left); + const int64 new_in_rows = + dims.spatial_dims[0].input_size + padding_rows_diff; + const int64 new_in_cols = + dims.spatial_dims[1].input_size + padding_cols_diff; compatible_input_shape = ShapeFromFormat( - data_format, dims.batch_size, - dims.spatial_dims[0].input_size + rows_odd, - dims.spatial_dims[1].input_size + cols_odd, dims.in_depth); + data_format, dims.batch_size, new_in_rows, new_in_cols, dims.in_depth); } else { compatible_input_shape = input_shape; } - CHECK(padding_rows >= 0 && padding_cols >= 0) - << "Negative row or col paddings: (" << padding_rows << ", " - << padding_cols << ")"; + CHECK(common_padding_rows >= 0 && common_padding_cols >= 0) // Crash OK + << "Negative row or col paddings: (" << common_padding_rows << ", " + << common_padding_cols << ")"; se::dnn::BatchDescriptor input_desc; input_desc.set_count(dims.batch_size) .set_height(GetTensorDim(compatible_input_shape, data_format, 'H')) @@ -879,8 +915,8 @@ void LaunchConv2DBackpropInputOp::operator()( .set_horizontal_dilation_rate(dims.spatial_dims[1].dilation) .set_vertical_filter_stride(dims.spatial_dims[0].stride) .set_horizontal_filter_stride(dims.spatial_dims[1].stride) - .set_zero_padding_height(padding_rows / 2) - .set_zero_padding_width(padding_cols / 2) + .set_zero_padding_height(common_padding_rows) + .set_zero_padding_width(common_padding_cols) .set_group_count(dims.in_depth / filter_shape.dim_size(2)); // NOTE(keveman): @@ -971,8 +1007,8 @@ void LaunchConv2DBackpropInputOp::operator()( dims.spatial_dims[1].dilation}}, // dilation_cols {{dims.spatial_dims[0].stride, // stride_rows dims.spatial_dims[1].stride}}, // stride_cols - {{padding_rows, // padding_rows - padding_cols}}, // padding_cols + {{common_padding_rows, // padding_rows + common_padding_cols}}, // padding_cols dtype, // tensor data type device_id, // device_id }; @@ -1041,7 +1077,7 @@ void LaunchConv2DBackpropInputOp::operator()( return; } - if (rows_odd || cols_odd) { + if (padding_top != padding_bottom || padding_left != padding_right) { Tensor in_backprop_remove_padding; OP_REQUIRES_OK( ctx, ctx->allocate_temp( @@ -1053,12 +1089,18 @@ void LaunchConv2DBackpropInputOp::operator()( GetTensorDim(input_shape, data_format, 'C')), &in_backprop_remove_padding)); - // Remove the padding for odd rows or cols. + // Remove the padding that was added to the input shape above. + const int64 input_pad_top = padding_top - common_padding_rows; + const int64 input_pad_bottom = padding_bottom - common_padding_rows; + const int64 input_pad_left = padding_left - common_padding_cols; + const int64 input_pad_right = padding_right - common_padding_cols; functor::PadInput()( ctx->template eigen_device(), To32Bit(const_cast(pre_transformed_in_backprop) .tensor()), - {{0, 0}}, {{-rows_odd, -cols_odd}}, + {{static_cast(-input_pad_top), static_cast(-input_pad_left)}}, + {{static_cast(-input_pad_bottom), + static_cast(-input_pad_right)}}, To32Bit(in_backprop_remove_padding.tensor()), FORMAT_NCHW); pre_transformed_in_backprop = in_backprop_remove_padding; diff --git a/tensorflow/core/kernels/conv_grad_ops.cc b/tensorflow/core/kernels/conv_grad_ops.cc index 507720c998..0fd7550830 100644 --- a/tensorflow/core/kernels/conv_grad_ops.cc +++ b/tensorflow/core/kernels/conv_grad_ops.cc @@ -52,24 +52,23 @@ int ConvBackpropDimensions::SpatialPadding(const Padding& padding, 1 - input_size(dim))); } -// The V2 version computes windowed output size with arbitrary dilation_rate, -// while the original version only handles the cases where dilation_rates equal -// to 1. -Status ConvBackpropExtractAndVerifyDimensionV2( +namespace { + +Status ConvBackpropExtractAndVerifyDimension( StringPiece label, const TensorShape& input_shape, const TensorShape& filter_shape, const TensorShape& output_shape, const gtl::ArraySlice& dilations, const std::vector& strides, - Padding padding, int spatial_dim, int filter_spatial_dim, - ConvBackpropSpatialDimension* dim) { + Padding padding, int64 padding_before, int64 padding_after, int spatial_dim, + int filter_spatial_dim, ConvBackpropSpatialDimension* dim) { dim->input_size = input_shape.dim_size(spatial_dim); dim->filter_size = filter_shape.dim_size(filter_spatial_dim); dim->output_size = output_shape.dim_size(spatial_dim); dim->stride = strides[spatial_dim]; dim->dilation = dilations[spatial_dim]; - int64 out_size = 0, pad_size = 0; - TF_RETURN_IF_ERROR(GetWindowedOutputSizeV2(dim->input_size, dim->filter_size, - dim->dilation, dim->stride, - padding, &out_size, &pad_size)); + int64 out_size = 0; + TF_RETURN_IF_ERROR(GetWindowedOutputSizeVerboseV2( + dim->input_size, dim->filter_size, dim->dilation, dim->stride, padding, + &out_size, &padding_before, &padding_after)); if (dim->output_size != out_size) { return errors::InvalidArgument( label, ": Size of out_backprop doesn't match computed: ", "actual = ", @@ -79,10 +78,13 @@ Status ConvBackpropExtractAndVerifyDimensionV2( " stride: ", dim->stride, " dilation: ", dim->dilation); } + // TODO(reedwm): Correctly handle explicit padding here. The rest of the + // fields set on 'dim' are only used in XLA. TensorFlow ops do not yet support + // explicit padding for XLA. int64 effective_filter_size = (dim->filter_size - 1) * dim->dilation + 1; dim->expanded_output_size = (dim->output_size - 1) * dim->stride + 1; const auto padded_out_size = dim->input_size + effective_filter_size - 1; - dim->pad_before = effective_filter_size - 1 - pad_size; + dim->pad_before = effective_filter_size - 1 - padding_before; dim->pad_after = padded_out_size - dim->expanded_output_size - dim->pad_before; VLOG(2) << label << ": expanded_out = " << dim->expanded_output_size @@ -94,22 +96,14 @@ Status ConvBackpropExtractAndVerifyDimensionV2( return Status::OK(); } -Status ConvBackpropExtractAndVerifyDimension( - StringPiece label, const TensorShape& input_shape, - const TensorShape& filter_shape, const TensorShape& output_shape, - const std::vector& strides, Padding padding, int spatial_dim, - int filter_spatial_dim, ConvBackpropSpatialDimension* dim) { - static constexpr std::array one_dilations = {{1, 1, 1, 1, 1}}; - return ConvBackpropExtractAndVerifyDimensionV2( - label, input_shape, filter_shape, output_shape, one_dilations, strides, - padding, spatial_dim, filter_spatial_dim, dim); -} +} // namespace Status ConvBackpropComputeDimensionsV2( StringPiece label, int num_spatial_dims, const TensorShape& input_shape, const TensorShape& filter_shape, const TensorShape& out_backprop_shape, const gtl::ArraySlice& dilations, const std::vector& strides, - Padding padding, TensorFormat data_format, ConvBackpropDimensions* dims) { + Padding padding, const std::vector& explicit_paddings, + TensorFormat data_format, ConvBackpropDimensions* dims) { // The + 2 in the following line is for the batch and feature dimensions. const int num_dims = num_spatial_dims + 2; if (input_shape.dims() != num_dims) { @@ -152,9 +146,15 @@ Status ConvBackpropComputeDimensionsV2( dims->spatial_dims.resize(num_spatial_dims); for (int i = 0; i < num_spatial_dims; ++i) { int image_dim = GetTensorSpatialDimIndex(num_dims, data_format, i); - TF_RETURN_IF_ERROR(ConvBackpropExtractAndVerifyDimensionV2( + int64 padding_before = -1, padding_after = -1; + if (padding == EXPLICIT) { + padding_before = explicit_paddings[2 * image_dim]; + padding_after = explicit_paddings[2 * image_dim + 1]; + } + TF_RETURN_IF_ERROR(ConvBackpropExtractAndVerifyDimension( label, input_shape, filter_shape, out_backprop_shape, dilations, - strides, padding, image_dim, i, &dims->spatial_dims[i])); + strides, padding, padding_before, padding_after, image_dim, i, + &dims->spatial_dims[i])); } return Status::OK(); } @@ -169,7 +169,8 @@ Status ConvBackpropComputeDimensions(StringPiece label, int num_spatial_dims, static constexpr std::array one_dilations = {{1, 1, 1, 1, 1}}; return ConvBackpropComputeDimensionsV2( label, num_spatial_dims, input_shape, filter_shape, out_backprop_shape, - one_dilations, strides, padding, data_format, dims); + one_dilations, strides, padding, /*explicit_paddings=*/{}, data_format, + dims); } } // namespace tensorflow diff --git a/tensorflow/core/kernels/conv_grad_ops.h b/tensorflow/core/kernels/conv_grad_ops.h index 9551959463..c8e8cf28c5 100644 --- a/tensorflow/core/kernels/conv_grad_ops.h +++ b/tensorflow/core/kernels/conv_grad_ops.h @@ -176,8 +176,9 @@ struct LaunchConv2DBackpropInputOp { void operator()(OpKernelContext* ctx, bool use_cudnn, bool cudnn_use_autotune, const Tensor& out_backprop, const Tensor& filter, int row_dilation, int col_dilation, int row_stride, - int col_stride, const Padding& padding, Tensor* in_backprop, - TensorFormat data_format); + int col_stride, const Padding& padding, + const std::vector& explicit_paddings, + Tensor* in_backprop, TensorFormat data_format); }; template @@ -186,6 +187,7 @@ struct LaunchConv2DBackpropFilterOp { const Tensor& out_backprop, const Tensor& input, int row_dilation, int col_dilation, int row_stride, int col_stride, const Padding& padding, + const std::vector& explicit_paddings, Tensor* filter_backprop, TensorFormat data_format); }; @@ -195,7 +197,8 @@ struct LaunchConv2DBackpropInputOp { void operator()(OpKernelContext* ctx, bool use_cudnn, bool cudnn_use_autotune, const Tensor& input, const Tensor& filter, int row_dilation, int col_dilation, int row_stride, int col_stride, - const Padding& padding, Tensor* output, + const Padding& padding, + const std::vector& explicit_paddings, Tensor* output, TensorFormat data_format); }; @@ -205,6 +208,7 @@ struct LaunchConv2DBackpropFilterOp { const Tensor& out_backprop, const Tensor& input, int row_dilation, int col_dilation, int row_stride, int col_stride, const Padding& padding, + const std::vector& explicit_paddings, Tensor* filter_backprop, TensorFormat data_format); }; #endif // GOOGLE_CUDA @@ -217,6 +221,8 @@ struct ConvBackpropSpatialDimension { int64 output_size; int64 stride; int64 dilation; + + // The following fields are valid only if the padding is not EXPLICIT. int64 expanded_output_size; // Number of padding elements to be added before/after this dimension of @@ -248,7 +254,7 @@ struct ConvBackpropDimensions { // Common code between implementations of Conv?DBackpropInput and // Conv?DBackpropFilter. Verifies that the dimensions all match, and computes -// sizes/padding for the spatial dimensions. +// sizes/padding for the spatial dimensions. Does not support explicit padding. Status ConvBackpropComputeDimensions(StringPiece label, int num_spatial_dims, const TensorShape& input_shape, const TensorShape& filter_shape, @@ -257,13 +263,15 @@ Status ConvBackpropComputeDimensions(StringPiece label, int num_spatial_dims, Padding padding, TensorFormat data_format, ConvBackpropDimensions* dims); -// The V2 version computes the same outputs with arbitrary dilation rate. +// The V2 version computes the same outputs with arbitrary dilation rate and +// supports explicit padding. // TODO(b/67112639): Merge V2 versions and the original versions eventually. Status ConvBackpropComputeDimensionsV2( StringPiece label, int num_spatial_dims, const TensorShape& input_shape, const TensorShape& filter_shape, const TensorShape& out_backprop_shape, const gtl::ArraySlice& dilations, const std::vector& strides, - Padding padding, TensorFormat data_format, ConvBackpropDimensions* dims); + Padding padding, const std::vector& explicit_paddings, + TensorFormat data_format, ConvBackpropDimensions* dims); } // namespace tensorflow #endif // TENSORFLOW_CORE_KERNELS_CONV_GRAD_OPS_H_ diff --git a/tensorflow/core/kernels/conv_grad_ops_3d.cc b/tensorflow/core/kernels/conv_grad_ops_3d.cc index 562a9c8aed..ca46da6ba3 100644 --- a/tensorflow/core/kernels/conv_grad_ops_3d.cc +++ b/tensorflow/core/kernels/conv_grad_ops_3d.cc @@ -1152,11 +1152,11 @@ class Conv3DBackpropInputOp : public OpKernel { } ConvBackpropDimensions dims; - OP_REQUIRES_OK(context, - ConvBackpropComputeDimensionsV2( - "Conv3DBackpropInputOp", /*num_spatial_dims=*/3, - input_shape, filter_shape, out_backprop_shape, dilation_, - stride_, padding_, data_format_, &dims)); + OP_REQUIRES_OK(context, ConvBackpropComputeDimensionsV2( + "Conv3DBackpropInputOp", /*num_spatial_dims=*/3, + input_shape, filter_shape, out_backprop_shape, + dilation_, stride_, padding_, + /*explicit_paddings=*/{}, data_format_, &dims)); Tensor* in_backprop; OP_REQUIRES_OK(context, @@ -1537,11 +1537,12 @@ class Conv3DBackpropFilterOp : public OpKernel { } ConvBackpropDimensions dims; - OP_REQUIRES_OK(context, - ConvBackpropComputeDimensionsV2( - "Conv3DBackpropFilterOp", /*num_spatial_dims=*/3, - input_shape, filter_shape, out_backprop_shape, dilation_, - stride_, padding_, data_format_, &dims)); + OP_REQUIRES_OK( + context, + ConvBackpropComputeDimensionsV2( + "Conv3DBackpropFilterOp", /*num_spatial_dims=*/3, input_shape, + filter_shape, out_backprop_shape, dilation_, stride_, padding_, + /*explicit_paddings=*/{}, data_format_, &dims)); Tensor* filter_backprop; OP_REQUIRES_OK(context, diff --git a/tensorflow/core/kernels/conv_ops.cc b/tensorflow/core/kernels/conv_ops.cc index dfba15792d..a8138fd0a7 100644 --- a/tensorflow/core/kernels/conv_ops.cc +++ b/tensorflow/core/kernels/conv_ops.cc @@ -122,7 +122,8 @@ struct LaunchConv2DOp { void operator()(OpKernelContext* ctx, bool use_cudnn, bool cudnn_use_autotune, const Tensor& input, const Tensor& filter, int row_dilation, int col_dilation, int row_stride, int col_stride, - const Padding& padding, Tensor* output, + const Padding& padding, + const std::vector& explicit_paddings, Tensor* output, TensorFormat data_format) { if (data_format != FORMAT_NHWC) { ctx->SetStatus( @@ -130,6 +131,11 @@ struct LaunchConv2DOp { "NHWC tensor format for now.")); return; } + // TODO(reedwm): Enable explicit padding on the CPU. + OP_REQUIRES( + ctx, padding != Padding::EXPLICIT, + errors::Unimplemented("Generic conv implementation does not support " + "EXPLICIT padding yet.")); const int64 in_depth = GetTensorDim(input, data_format, 'C'); OP_REQUIRES(ctx, in_depth == filter.dim_size(2), errors::Unimplemented("Generic conv implementation does not " @@ -274,6 +280,10 @@ Status InitConv2DParameters(const OpKernelConstruction* context, TF_RETURN_IF_ERROR(context->GetAttr("dilations", ¶ms->dilations)); TF_RETURN_IF_ERROR(context->GetAttr("strides", ¶ms->strides)); TF_RETURN_IF_ERROR(context->GetAttr("padding", ¶ms->padding)); + if (context->HasAttr("explicit_paddings")) { + TF_RETURN_IF_ERROR( + context->GetAttr("explicit_paddings", ¶ms->explicit_paddings)); + } string data_format_string; TF_RETURN_IF_ERROR(context->GetAttr("data_format", &data_format_string)); TF_REQUIRES(FormatFromString(data_format_string, ¶ms->data_format), @@ -313,6 +323,10 @@ Status InitConv2DParameters(const OpKernelConstruction* context, dilation_h > 0 && dilation_w > 0, errors::InvalidArgument("Dilated rates should be larger than 0.")); + TF_RETURN_IF_ERROR(CheckValidPadding(params->padding, + params->explicit_paddings, + /*num_dims=*/4, data_format)); + return Status::OK(); } @@ -381,14 +395,22 @@ Status ComputeConv2DDimension(const Conv2DParameters& params, const int dilation_cols = GetTensorDim(params.dilations, params.data_format, 'W'); + int64 pad_rows_before, pad_rows_after, pad_cols_before, pad_cols_after; + if (params.padding == Padding::EXPLICIT) { + GetExplicitPaddingForDim(params.explicit_paddings, params.data_format, 'H', + &pad_rows_before, &pad_rows_after); + GetExplicitPaddingForDim(params.explicit_paddings, params.data_format, 'W', + &pad_cols_before, &pad_cols_after); + } + // Compute windowed output sizes for rows and columns. - int64 out_rows = 0, out_cols = 0, pad_rows = 0, pad_cols = 0; - TF_RETURN_IF_ERROR(GetWindowedOutputSizeV2( + int64 out_rows = 0, out_cols = 0; + TF_RETURN_IF_ERROR(GetWindowedOutputSizeVerboseV2( input_rows, filter_rows, dilation_rows, stride_rows, params.padding, - &out_rows, &pad_rows)); - TF_RETURN_IF_ERROR(GetWindowedOutputSizeV2( + &out_rows, &pad_rows_before, &pad_rows_after)); + TF_RETURN_IF_ERROR(GetWindowedOutputSizeVerboseV2( input_cols, filter_cols, dilation_cols, stride_cols, params.padding, - &out_cols, &pad_cols)); + &out_cols, &pad_cols_before, &pad_cols_after)); dimensions->batch = batch; dimensions->input_rows = input_rows; @@ -404,8 +426,10 @@ Status ComputeConv2DDimension(const Conv2DParameters& params, dimensions->dilation_cols = dilation_cols; dimensions->out_rows = out_rows; dimensions->out_cols = out_cols; - dimensions->pad_rows = pad_rows; - dimensions->pad_cols = pad_cols; + dimensions->pad_rows_before = pad_rows_before; + dimensions->pad_rows_after = pad_rows_after; + dimensions->pad_cols_before = pad_cols_before; + dimensions->pad_cols_after = pad_cols_after; return Status::OK(); } @@ -463,33 +487,35 @@ class Conv2DOp : public BinaryOp { } #ifdef TENSORFLOW_USE_LIBXSMM_CONVOLUTIONS - if (LaunchXsmmConvOp::Run( + if (params_.padding != EXPLICIT && + LaunchXsmmConvOp::Run( context, input, filter, dimensions.batch, dimensions.input_rows, dimensions.input_cols, dimensions.in_depth, dimensions.filter_rows, - dimensions.filter_cols, dimensions.pad_rows, dimensions.pad_cols, - dimensions.out_rows, dimensions.out_cols, dimensions.out_depth, - dimensions.dilation_rows, dimensions.dilation_cols, - dimensions.stride_rows, dimensions.stride_cols, output, - params_.data_format)) { + dimensions.filter_cols, dimensions.pad_rows_before, + dimensions.pad_cols_before, dimensions.out_rows, + dimensions.out_cols, dimensions.out_depth, dimensions.dilation_rows, + dimensions.dilation_cols, dimensions.stride_rows, + dimensions.stride_cols, output, params_.data_format)) { return; } #endif - if (LaunchDeepConvOp::Run( + if (params_.padding != EXPLICIT && + LaunchDeepConvOp::Run( context, input, filter, dimensions.batch, dimensions.input_rows, dimensions.input_cols, dimensions.in_depth, dimensions.filter_rows, - dimensions.filter_cols, dimensions.pad_rows, dimensions.pad_cols, - dimensions.out_rows, dimensions.out_cols, dimensions.out_depth, - dimensions.dilation_rows, dimensions.dilation_cols, - dimensions.stride_rows, dimensions.stride_cols, output, - params_.data_format)) { + dimensions.filter_cols, dimensions.pad_rows_before, + dimensions.pad_cols_before, dimensions.out_rows, + dimensions.out_cols, dimensions.out_depth, dimensions.dilation_rows, + dimensions.dilation_cols, dimensions.stride_rows, + dimensions.stride_cols, output, params_.data_format)) { return; } launcher_(context, use_cudnn_, cudnn_use_autotune_, input, filter, dimensions.dilation_rows, dimensions.dilation_cols, dimensions.stride_rows, dimensions.stride_cols, params_.padding, - output, params_.data_format); + params_.explicit_paddings, output, params_.data_format); } private: @@ -551,7 +577,8 @@ void LaunchConv2DOp::operator()( OpKernelContext* ctx, bool use_cudnn, bool cudnn_use_autotune, const Tensor& input_param, const Tensor& filter, int row_dilation, int col_dilation, int row_stride, int col_stride, const Padding& padding, - Tensor* output, TensorFormat data_format) { + const std::vector& explicit_paddings, Tensor* output, + TensorFormat data_format) { using se::dnn::AlgorithmConfig; using se::dnn::AlgorithmDesc; using se::dnn::ProfileResult; @@ -580,7 +607,8 @@ void LaunchConv2DOp::operator()( bool is_grouped_convolution = patch_depths != in_depths; if (patch_rows == 1 && patch_cols == 1 && !is_grouped_convolution && row_dilation == 1 && col_dilation == 1 && row_stride == 1 && - col_stride == 1 && data_format == FORMAT_NHWC) { + col_stride == 1 && data_format == FORMAT_NHWC && + (padding == VALID || padding == SAME)) { // 1x1 filter, so call cublas directly. const uint64 m = in_batch * in_rows * in_cols; const uint64 k = patch_depths; @@ -634,49 +662,78 @@ void LaunchConv2DOp::operator()( return; } - int padding_rows = 0; - int padding_cols = 0; const int64 out_batch = GetTensorDim(*output, data_format, 'N'); const int64 out_rows = GetTensorDim(*output, data_format, 'H'); const int64 out_cols = GetTensorDim(*output, data_format, 'W'); const int64 out_depths = GetTensorDim(*output, data_format, 'C'); - if (padding == SAME) { - // Total padding on rows and cols is - // Pr = (R' - 1) * S + (Kr - 1) * Dr + 1 - R - // Pc = (C' - 1) * S + (Kc - 1) * Dc + 1 - C - // where (R', C') are output dimensions, (R, C) are input dimensions, S - // is stride, (Dr, Dc) are dilations, (Kr, Kc) are filter dimensions. - // We pad Pr/2 on the left and Pr - Pr/2 on the right, Pc/2 on the top - // and Pc - Pc/2 on the bottom. When Pr or Pc is odd, this means - // we pad more on the right and bottom than on the top and left. - padding_rows = - std::max(0, (out_rows - 1) * row_stride + - (patch_rows - 1) * row_dilation + 1 - in_rows); - padding_cols = - std::max(0, (out_cols - 1) * col_stride + - (patch_cols - 1) * col_dilation + 1 - in_cols); - const bool rows_odd = (padding_rows % 2 != 0); - const bool cols_odd = (padding_cols % 2 != 0); - if (rows_odd || cols_odd) { - Tensor transformed_input; - int64 new_in_rows = in_rows + rows_odd; - int64 new_in_cols = in_cols + cols_odd; - OP_REQUIRES_OK( - ctx, - ctx->allocate_temp(DataTypeToEnum::value, - ShapeFromFormat(data_format, in_batch, new_in_rows, - new_in_cols, in_depths), - &transformed_input)); - - functor::PadInput()( - ctx->eigen_device(), To32Bit(input_param.tensor()), - {{0, 0}}, {{rows_odd, cols_odd}}, - To32Bit(transformed_input.tensor()), data_format); - - input = transformed_input; - in_rows = new_in_rows; - in_cols = new_in_cols; + int64 padding_top = -1, padding_bottom = -1; + int64 padding_left = -1, padding_right = -1; + if (padding == EXPLICIT) { + GetExplicitPaddingForDim(explicit_paddings, data_format, 'H', &padding_top, + &padding_bottom); + GetExplicitPaddingForDim(explicit_paddings, data_format, 'W', &padding_left, + &padding_right); + } + int64 out_rows_check, out_cols_check; + Status status = GetWindowedOutputSizeVerboseV2( + in_rows, patch_rows, row_dilation, row_stride, padding, &out_rows_check, + &padding_top, &padding_bottom); + // The status is guaranteed to be OK because we checked the output and padding + // was valid earlier. + TF_CHECK_OK(status); + DCHECK_EQ(out_rows, out_rows_check); + status = GetWindowedOutputSizeVerboseV2(in_cols, patch_cols, col_dilation, + col_stride, padding, &out_cols_check, + &padding_left, &padding_right); + TF_CHECK_OK(status); + DCHECK_EQ(out_cols, out_cols_check); + + const int64 common_padding_rows = std::min(padding_top, padding_bottom); + const int64 common_padding_cols = std::min(padding_left, padding_right); + if (padding_top != padding_bottom || padding_left != padding_right) { + // cuDNN only supports padding the same amount on the left and right sides, + // and on the top and bottom sides. So we manually create a new padded + // input tensor such that we can pass it to cuDNN. + + // TODO(reedwm): In some cases, we can avoid an allocation even if the two + // padding sides are different. For example, if the input is 2x2, the filter + // is 1x1, the stride is 2, and the padding is (1, 0, 1, 0), the result is + // equivalent to as if the padding is (1, 1, 1, 1). Changing the padding in + // such a way would allow us to avoid the allocation. + Tensor transformed_input; + const int64 padding_rows_diff = std::abs(padding_bottom - padding_top); + const int64 padding_cols_diff = std::abs(padding_right - padding_left); + const int64 new_in_rows = in_rows + padding_rows_diff; + const int64 new_in_cols = in_cols + padding_cols_diff; + OP_REQUIRES_OK(ctx, ctx->allocate_temp( + DataTypeToEnum::value, + ShapeFromFormat(data_format, in_batch, new_in_rows, + new_in_cols, in_depths), + &transformed_input)); + + const int64 input_pad_top = padding_top - common_padding_rows; + const int64 input_pad_bottom = padding_bottom - common_padding_rows; + const int64 input_pad_left = padding_left - common_padding_cols; + const int64 input_pad_right = padding_right - common_padding_cols; + bool in_bounds = + FastBoundsCheck(input_pad_top, std::numeric_limits::max()) && + FastBoundsCheck(input_pad_bottom, std::numeric_limits::max()) && + FastBoundsCheck(input_pad_left, std::numeric_limits::max()) && + FastBoundsCheck(input_pad_right, std::numeric_limits::max()); + if (!in_bounds) { + ctx->SetStatus(errors::InvalidArgument("Padding is too large.")); + return; } + functor::PadInput()( + ctx->eigen_device(), To32Bit(input_param.tensor()), + {{static_cast(input_pad_top), static_cast(input_pad_left)}}, + {{static_cast(input_pad_bottom), + static_cast(input_pad_right)}}, + To32Bit(transformed_input.tensor()), data_format); + + input = transformed_input; + in_rows = new_in_rows; + in_cols = new_in_cols; } if (data_format == FORMAT_NHWC) { @@ -698,9 +755,9 @@ void LaunchConv2DOp::operator()( } } - CHECK(padding_rows >= 0 && padding_cols >= 0) - << "Negative row or col paddings: (" << padding_rows << ", " - << padding_cols << ")"; + CHECK(common_padding_rows >= 0 && common_padding_cols >= 0) // Crash OK + << "Negative row or col paddings: (" << common_padding_rows << ", " + << common_padding_cols << ")"; se::dnn::BatchDescriptor input_desc; input_desc.set_count(in_batch) .set_feature_map_count(in_depths) @@ -723,8 +780,8 @@ void LaunchConv2DOp::operator()( .set_horizontal_dilation_rate(col_dilation) .set_vertical_filter_stride(row_stride) .set_horizontal_filter_stride(col_stride) - .set_zero_padding_height(padding_rows / 2) - .set_zero_padding_width(padding_cols / 2) + .set_zero_padding_height(common_padding_rows) + .set_zero_padding_width(common_padding_cols) .set_group_count(in_depths / patch_depths); Tensor transformed_filter; @@ -767,23 +824,23 @@ void LaunchConv2DOp::operator()( int device_id = stream->parent()->device_ordinal(); DataType dtype = input.dtype(); ConvParameters conv_parameters = { - in_batch, // batch - in_depths, // in_depths - {{in_rows, // in_rows - in_cols}}, // in_cols - FORMAT_NCHW, // compute_data_format - out_depths, // out_depths - {{patch_rows, // filter_rows - patch_cols, // filter_cols - patch_depths}}, // filter_depths - {{row_dilation, // dilation_rows - col_dilation}}, // dilation_cols - {{row_stride, // stride_rows - col_stride}}, // stride_cols - {{padding_rows, // padding_rows - padding_cols}}, // padding_cols - dtype, // tensor datatype - device_id, // device_id + in_batch, // batch + in_depths, // in_depths + {{in_rows, // in_rows + in_cols}}, // in_cols + FORMAT_NCHW, // compute_data_format + out_depths, // out_depths + {{patch_rows, // filter_rows + patch_cols, // filter_cols + patch_depths}}, // filter_depths + {{row_dilation, // dilation_rows + col_dilation}}, // dilation_cols + {{row_stride, // stride_rows + col_stride}}, // stride_cols + {{common_padding_rows, // padding_rows + common_padding_cols}}, // padding_cols + dtype, // tensor datatype + device_id, // device_id }; AlgorithmConfig algorithm_config; if (cudnn_use_autotune && diff --git a/tensorflow/core/kernels/conv_ops.h b/tensorflow/core/kernels/conv_ops.h index 7ccbaf4bf2..105a4b1b82 100644 --- a/tensorflow/core/kernels/conv_ops.h +++ b/tensorflow/core/kernels/conv_ops.h @@ -36,7 +36,8 @@ struct LaunchConv2DOp { void operator()(OpKernelContext* ctx, bool use_cudnn, bool cudnn_use_autotune, const Tensor& input, const Tensor& filter, int row_dilation, int col_dilation, int row_stride, int col_stride, - const Padding& padding, Tensor* output, + const Padding& padding, + const std::vector& explicit_paddings, Tensor* output, TensorFormat data_format); }; @@ -46,7 +47,8 @@ struct LaunchConv2DOp { void operator()(OpKernelContext* ctx, bool use_cudnn, bool cudnn_use_autotune, const Tensor& input, const Tensor& filter, int row_dilation, int col_dilation, int row_stride, int col_stride, - const Padding& padding, Tensor* output, + const Padding& padding, + const std::vector& explicit_paddings, Tensor* output, TensorFormat data_format); }; #endif // GOOGLE_CUDA @@ -72,6 +74,7 @@ struct Conv2DParameters { std::vector strides; Padding padding; TensorFormat data_format; + std::vector explicit_paddings; }; // Convolution dimensions inferred from parameters, input and filter tensors. @@ -94,8 +97,10 @@ struct Conv2DDimensions { int64 out_rows; int64 out_cols; - int64 pad_rows; - int64 pad_cols; + int64 pad_rows_before; + int64 pad_rows_after; + int64 pad_cols_before; + int64 pad_cols_after; }; // Initializes and validates Conv2D parameters configured by OpKernel diff --git a/tensorflow/core/kernels/depthwise_conv_grad_op.cc b/tensorflow/core/kernels/depthwise_conv_grad_op.cc index da3bdb475e..c152f2b7e4 100644 --- a/tensorflow/core/kernels/depthwise_conv_grad_op.cc +++ b/tensorflow/core/kernels/depthwise_conv_grad_op.cc @@ -633,7 +633,8 @@ class DepthwiseConv2dNativeBackpropInputOp : public OpKernel { // conv is supported. launcher_(context, use_cudnn_, cudnn_use_autotune_, out_backprop, reshaped_filter, /*row_dilation=*/1, /*col_dilation=*/1, - stride_, stride_, padding_, in_backprop, data_format_); + stride_, stride_, padding_, /*explicit_paddings=*/{}, + in_backprop, data_format_); return; } @@ -1115,7 +1116,8 @@ class DepthwiseConv2dNativeBackpropFilterOp : public OpKernel { // conv is supported. launcher_(context, use_cudnn_, cudnn_use_autotune_, out_backprop, input, /*row_dilation=*/1, /*col_dilation=*/1, stride_, stride_, - padding_, &reshaped_filter, data_format_); + padding_, /*explicit_paddings=*/{}, &reshaped_filter, + data_format_); return; } diff --git a/tensorflow/core/kernels/depthwise_conv_op.cc b/tensorflow/core/kernels/depthwise_conv_op.cc index f0902fdba6..dacd3cfea8 100644 --- a/tensorflow/core/kernels/depthwise_conv_op.cc +++ b/tensorflow/core/kernels/depthwise_conv_op.cc @@ -404,7 +404,8 @@ class DepthwiseConv2dNativeOp : public BinaryOp { // conv is supported. launcher_(context, use_cudnn_, cudnn_use_autotune_, input, reshaped_filter, /*row_dilation=*/1, /*col_dilation=*/1, - stride_, stride_, padding_, output, data_format_); + stride_, stride_, padding_, /*explicit_paddings=*/{}, output, + data_format_); return; } diff --git a/tensorflow/core/ops/nn_ops.cc b/tensorflow/core/ops/nn_ops.cc index 0e94259f75..0f4f725937 100644 --- a/tensorflow/core/ops/nn_ops.cc +++ b/tensorflow/core/ops/nn_ops.cc @@ -269,10 +269,11 @@ REGISTER_OP("Conv2D") .Attr("T: {half, bfloat16, float, double}") .Attr("strides: list(int)") .Attr("use_cudnn_on_gpu: bool = true") - .Attr(GetPaddingAttrString()) + .Attr(GetPaddingAttrStringWithExplicit()) + .Attr(GetExplicitPaddingsAttrString()) .Attr(GetConvnetDataFormatAttrString()) .Attr("dilations: list(int) = [1, 1, 1, 1]") - .SetShapeFn(shape_inference::Conv2DShape); + .SetShapeFn(shape_inference::Conv2DShapeWithExplicitPadding); REGISTER_OP("Conv2DBackpropInput") .Input("input_sizes: int32") @@ -282,7 +283,8 @@ REGISTER_OP("Conv2DBackpropInput") .Attr("T: {half, bfloat16, float, double}") .Attr("strides: list(int)") .Attr("use_cudnn_on_gpu: bool = true") - .Attr(GetPaddingAttrString()) + .Attr(GetPaddingAttrStringWithExplicit()) + .Attr(GetExplicitPaddingsAttrString()) .Attr(GetConvnetDataFormatAttrString()) .Attr("dilations: list(int) = [1, 1, 1, 1]") .SetShapeFn([](InferenceContext* c) { @@ -304,7 +306,8 @@ REGISTER_OP("Conv2DBackpropFilter") .Attr("T: {half, bfloat16, float, double}") .Attr("strides: list(int)") .Attr("use_cudnn_on_gpu: bool = true") - .Attr(GetPaddingAttrString()) + .Attr(GetPaddingAttrStringWithExplicit()) + .Attr(GetExplicitPaddingsAttrString()) .Attr(GetConvnetDataFormatAttrString()) .Attr("dilations: list(int) = [1, 1, 1, 1]") .SetShapeFn([](InferenceContext* c) { diff --git a/tensorflow/core/util/padding.cc b/tensorflow/core/util/padding.cc index 117de5ee4b..9e7fb8489e 100644 --- a/tensorflow/core/util/padding.cc +++ b/tensorflow/core/util/padding.cc @@ -29,12 +29,55 @@ Status GetNodeAttr(const NodeDef& node_def, StringPiece attr_name, *value = SAME; } else if (str_value == "VALID") { *value = VALID; + } else if (str_value == "EXPLICIT") { + *value = EXPLICIT; } else { return errors::NotFound(str_value, " is not an allowed padding type"); } return Status::OK(); } +Status CheckValidPadding(Padding padding_type, + const std::vector& explicit_paddings, + int num_dims, TensorFormat data_format) { + if (padding_type == Padding::EXPLICIT) { + if (explicit_paddings.size() != 2 * num_dims) { + return errors::InvalidArgument( + "explicit_paddings attribute must contain ", 2 * num_dims, + " values, but got: ", explicit_paddings.size()); + } + for (int64 padding_value : explicit_paddings) { + if (padding_value < 0) { + return errors::InvalidArgument( + "All elements of explicit_paddings must be nonnegative"); + } + } + const int32 batch_index = GetTensorBatchDimIndex(num_dims, data_format); + const int32 depth_index = GetTensorFeatureDimIndex(num_dims, data_format); + if (explicit_paddings[2 * batch_index] != 0 || + explicit_paddings[2 * batch_index + 1] != 0 || + explicit_paddings[2 * depth_index] != 0 || + explicit_paddings[2 * depth_index + 1] != 0) { + return errors::InvalidArgument( + "Nonzero explicit padding in the batch or depth dimensions is not " + "supported"); + } + } else if (!explicit_paddings.empty()) { + return errors::InvalidArgument( + "explicit_paddings attribute must be empty if the padding attribute is " + "not EXPLICIT"); + } + return Status::OK(); +} + string GetPaddingAttrString() { return "padding: {'SAME', 'VALID'}"; } +string GetPaddingAttrStringWithExplicit() { + return "padding: {'SAME', 'VALID', 'EXPLICIT'}"; +} + +string GetExplicitPaddingsAttrString() { + return "explicit_paddings: list(int) = []"; +} + } // end namespace tensorflow diff --git a/tensorflow/core/util/padding.h b/tensorflow/core/util/padding.h index 76f9b4dd9a..a1dd1c0bd9 100644 --- a/tensorflow/core/util/padding.h +++ b/tensorflow/core/util/padding.h @@ -20,8 +20,10 @@ limitations under the License. // kernels. #include +#include #include "tensorflow/core/lib/core/status.h" +#include "tensorflow/core/util/tensor_format.h" namespace tensorflow { @@ -34,16 +36,29 @@ class NodeDef; // VALID: No padding is carried out. // SAME: The pad value is computed so that the output will have the same // dimensions as the input. +// EXPLICIT: The user specifies the pad values in the explicit_padding +// attribute. // The padded area is zero-filled. enum Padding { - VALID = 1, // No padding. - SAME = 2, // Input and output layers have the same size. + VALID = 1, // No padding. + SAME = 2, // Input and output layers have the same size. + EXPLICIT = 3, // Padding is explicitly specified }; +// Returns an error if the padding attributes are invalid. +Status CheckValidPadding(Padding padding_type, + const std::vector& explicit_paddings, + int num_dims, TensorFormat data_format); + // Return the string containing the list of valid padding types, that can be // used as an Attr() in REGISTER_OP. string GetPaddingAttrString(); +// Like GetPaddingAttrString(), but also includes EXPLICIT. +string GetPaddingAttrStringWithExplicit(); + +string GetExplicitPaddingsAttrString(); + // Specialization to parse an attribute directly into a Padding enum. Status GetNodeAttr(const NodeDef& node_def, StringPiece attr_name, Padding* value); diff --git a/tensorflow/core/util/tensor_format.h b/tensorflow/core/util/tensor_format.h index a296fb447e..643e14e0b5 100644 --- a/tensorflow/core/util/tensor_format.h +++ b/tensorflow/core/util/tensor_format.h @@ -408,18 +408,24 @@ inline int32 GetTensorDimIndex(TensorFormat format, char dimension) { return GetTensorDimIndex<2>(format, dimension); } +inline int32 GetTensorDimIndex(TensorFormat format, char dimension, + int num_total_dims) { + int32 index = (GetTensorSpatialDims(num_total_dims, format) == 3) + ? GetTensorDimIndex<3>(format, dimension) + : GetTensorDimIndex<2>(format, dimension); + CHECK(index >= 0 && index < num_total_dims) // Crash OK. + << "Invalid index from the dimension: " << index << ", " << format << ", " + << dimension; + return index; +} + // Return the element from 'dimension_attributes' that corresponds to the // specified 'dimension' according to 'tensor_format'. template T GetTensorDim(gtl::ArraySlice dimension_attributes, TensorFormat tensor_format, char dimension) { int index = - (GetTensorSpatialDims(dimension_attributes.size(), tensor_format) == 3) - ? GetTensorDimIndex<3>(tensor_format, dimension) - : GetTensorDimIndex<2>(tensor_format, dimension); - CHECK(index >= 0 && index < dimension_attributes.size()) - << "Invalid index from the dimension: " << index << ", " << tensor_format - << ", " << dimension; + GetTensorDimIndex(tensor_format, dimension, dimension_attributes.size()); return dimension_attributes[index]; } @@ -476,6 +482,15 @@ inline int64 GetFilterDim(const Tensor& tensor, return GetFilterDim(tensor.shape(), filter_tensor_format, dimension); } +inline void GetExplicitPaddingForDim( + const std::vector& explicit_paddings, TensorFormat tensor_format, + char dimension, int64* padding_before, int64* padding_after) { + int index = + GetTensorDimIndex(tensor_format, dimension, explicit_paddings.size() / 2); + *padding_before = explicit_paddings[2 * index]; + *padding_after = explicit_paddings[2 * index + 1]; +} + // Return the string that specifies the data format for convnet operations. string GetConvnetDataFormatAttrString(); string GetConvnet3dDataFormatAttrString(); diff --git a/tensorflow/python/keras/layers/convolutional.py b/tensorflow/python/keras/layers/convolutional.py index 80ec55f410..30b919cc0a 100644 --- a/tensorflow/python/keras/layers/convolutional.py +++ b/tensorflow/python/keras/layers/convolutional.py @@ -180,12 +180,14 @@ class Conv(Layer): op_padding = 'valid' else: op_padding = self.padding + if not isinstance(op_padding, (list, tuple)): + op_padding = op_padding.upper() self._convolution_op = nn_ops.Convolution( input_shape, filter_shape=self.kernel.get_shape(), dilation_rate=self.dilation_rate, strides=self.strides, - padding=op_padding.upper(), + padding=op_padding, data_format=conv_utils.convert_data_format(self.data_format, self.rank + 2)) self.built = True diff --git a/tensorflow/python/keras/utils/conv_utils.py b/tensorflow/python/keras/utils/conv_utils.py index f486e631e5..ea7427f61a 100644 --- a/tensorflow/python/keras/utils/conv_utils.py +++ b/tensorflow/python/keras/utils/conv_utils.py @@ -194,9 +194,11 @@ def normalize_data_format(value): def normalize_padding(value): + if isinstance(value, (list, tuple)): + return value padding = value.lower() if padding not in {'valid', 'same', 'causal'}: - raise ValueError('The `padding` argument must be one of ' + raise ValueError('The `padding` argument must be a list/tuple or one of ' '"valid", "same" (or "causal", only for `Conv1D). ' 'Received: ' + str(padding)) return padding diff --git a/tensorflow/python/kernel_tests/conv_ops_test.py b/tensorflow/python/kernel_tests/conv_ops_test.py index 2f6f3bb383..7ff1a61e47 100644 --- a/tensorflow/python/kernel_tests/conv_ops_test.py +++ b/tensorflow/python/kernel_tests/conv_ops_test.py @@ -26,13 +26,18 @@ import numpy as np from six.moves import xrange # pylint: disable=redefined-builtin from tensorflow.contrib import layers +from tensorflow.core.protobuf import config_pb2 +from tensorflow.core.protobuf import rewriter_config_pb2 from tensorflow.python.client import session as session_lib +from tensorflow.python.eager import backprop +from tensorflow.python.eager import context from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors_impl from tensorflow.python.framework import ops from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops +from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import gradient_checker from tensorflow.python.ops import gradients_impl from tensorflow.python.ops import nn_impl @@ -165,6 +170,12 @@ class Conv2DTest(test.TestCase): # as we will be using its gradients as reference for fp16 gradients. return [dtypes.float32, dtypes.float16, dtypes.float64] + def _CreateNumpyTensor(self, shape): + total_size = 1 + for s in shape: + total_size *= s + return np.arange(1, total_size + 1, dtype=np.float32).reshape(shape) + def _SetupValuesForDevice(self, tensor_in_sizes, filter_in_sizes, dilations, strides, padding, data_format, dtype, use_gpu): """Verifies the output values of the convolution function. @@ -183,26 +194,22 @@ class Conv2DTest(test.TestCase): Returns: Symbolic tensor value that can be used to execute the computation """ - total_size_1 = 1 - total_size_2 = 1 - for s in tensor_in_sizes: - total_size_1 *= s - for s in filter_in_sizes: - total_size_2 *= s - # Initializes the input tensor with array containing incrementing - # numbers from 1. - x1 = [f * 1.0 for f in range(1, total_size_1 + 1)] - x2 = [f * 1.0 for f in range(1, total_size_2 + 1)] + x1 = self._CreateNumpyTensor(tensor_in_sizes) + x2 = self._CreateNumpyTensor(filter_in_sizes) with test_util.device(use_gpu): t1 = constant_op.constant(x1, shape=tensor_in_sizes, dtype=dtype) t2 = constant_op.constant(x2, shape=filter_in_sizes, dtype=dtype) strides = [1] + strides + [1] dilations = [1] + dilations + [1] + if isinstance(padding, (list, tuple)): + padding = [(0, 0)] + padding + [(0, 0)] if data_format == "NCHW": t1 = test_util.NHWCToNCHW(t1) strides = test_util.NHWCToNCHW(strides) dilations = test_util.NHWCToNCHW(dilations) + if isinstance(padding, (list, tuple)): + padding = test_util.NHWCToNCHW(padding) conv = nn_ops.conv2d( t1, t2, @@ -254,17 +261,8 @@ class Conv2DTest(test.TestCase): def _ComputeReferenceDilatedConv(self, tensor_in_sizes, filter_in_sizes, stride, dilation, padding, data_format, use_gpu): - total_size_1 = 1 - total_size_2 = 1 - for s in tensor_in_sizes: - total_size_1 *= s - for s in filter_in_sizes: - total_size_2 *= s - - # Initializes the input tensor with array containing incrementing - # numbers from 1. - x1 = [f * 1.0 for f in range(1, total_size_1 + 1)] - x2 = [f * 1.0 for f in range(1, total_size_2 + 1)] + x1 = self._CreateNumpyTensor(tensor_in_sizes) + x2 = self._CreateNumpyTensor(filter_in_sizes) with test_util.device(use_gpu): t1 = constant_op.constant(x1, shape=tensor_in_sizes) t2 = constant_op.constant(x2, shape=filter_in_sizes) @@ -312,16 +310,29 @@ class Conv2DTest(test.TestCase): expected_values = self.evaluate(expected_results) computed_values = self.evaluate(computed_results) for e_value, c_value in zip(expected_values, computed_values): - tf_logging.info("expected = ", e_value) - tf_logging.info("actual = ", c_value) + tf_logging.debug("expected = %s", e_value) + tf_logging.debug("actual = %s", c_value) self.assertAllClose( e_value.flatten(), c_value.flatten(), atol=tolerance, rtol=1e-4) - def _VerifyValues(self, tensor_in_sizes, filter_in_sizes, strides, padding, - expected): + def _VerifyValues(self, + tensor_in_sizes, + filter_in_sizes, + strides, + padding, + expected, + dilations=(1, 1), + gpu_only=False, + test_grappler_layout_optimizer=False, + tol=1e-5, + fp16_tol=1e-3): + if gpu_only and not test.is_gpu_available(cuda_only=True): + return tensors = [] - dilations = [1, 1] + dilations = list(dilations) for (data_format, use_gpu) in GetTestConfigs(): + if gpu_only and not use_gpu: + continue for dtype in self._DtypesToTest(use_gpu): result = self._SetupValuesForDevice( tensor_in_sizes, @@ -332,19 +343,71 @@ class Conv2DTest(test.TestCase): data_format, dtype, use_gpu=use_gpu) + if test_grappler_layout_optimizer and data_format == "NHWC" and use_gpu: + # Grappler's layout optimizer will not optimize a fetch node, so + # this identity allows Grappler to optimize the Conv2D node. + result = array_ops.identity(result) tensors.append(result) values = self.evaluate(tensors) for i in range(len(tensors)): conv = tensors[i] value = values[i] - tf_logging.info("expected = ", expected) - tf_logging.info("actual = ", value) - tol = 1e-5 - if value.dtype == np.float16: - tol = 1e-3 - self.assertAllClose(expected, np.ravel(value), atol=tol, rtol=tol) + tf_logging.debug("expected = %s", expected) + tf_logging.debug("actual = %s", value) + tol_to_use = fp16_tol if value.dtype == np.float16 else tol + self.assertAllClose(expected, np.ravel(value), atol=tol_to_use, + rtol=tol_to_use) self.assertShapeEqual(value, conv) + def _VerifyExplicitPaddings(self, + tensor_in_sizes, + filter_in_sizes, + strides, + padding, + dilations=(1, 1), + test_grappler_layout_optimizer=False, + tol=1e-5, + fp16_tol=1e-3): + """Verifies Conv2D with explicit padding generates correct values. + + It does this by comparing with Conv2D without explicit padding. This + function assumes Conv2D without explicit padding works correctly. + + Args: + tensor_in_sizes: Input tensor dimensions in [batch, input_rows, + input_cols, input_depth]. + filter_in_sizes: Filter tensor dimensions in [kernel_rows, kernel_cols, + input_depth, output_depth]. + strides: [row_stride, col_stride] for the convolution; + padding: Explicit padding amounts. + dilations: Dilation values + test_grappler_layout_optimizer: If True, allow the Grappler layout + optimizer to run, which turns NHWC Conv2Ds on the GPU to NCHW Conv2Ds. + tol: The absolute and relative tolerance for non-fp16 dtypes. + fp16_tol: The absolute and relative tolerance for fp16. + """ + input_tensor = self._CreateNumpyTensor(tensor_in_sizes) + filter_tensor = self._CreateNumpyTensor(filter_in_sizes) + input_tensor = array_ops.pad(input_tensor, [(0, 0)] + padding + [(0, 0)]) + dilations = list(dilations) + conv2d_result = nn_ops.conv2d( + input_tensor, + filter_tensor, [1] + list(strides) + [1], + "VALID", + dilations=[1] + dilations + [1]) + expected = list(self.evaluate(array_ops.reshape(conv2d_result, [-1]))) + self._VerifyValues( + tensor_in_sizes, + filter_in_sizes, + strides, + padding, + expected, + dilations, + gpu_only=True, + test_grappler_layout_optimizer=test_grappler_layout_optimizer, + tol=tol, + fp16_tol=fp16_tol) + @test_util.run_in_graph_and_eager_modes def testConv2D1x1Filter(self): expected_output = [ @@ -510,6 +573,126 @@ class Conv2DTest(test.TestCase): dilations=[2, 2], padding="VALID") + @test_util.run_in_graph_and_eager_modes() + def testConv2D0x0Padding(self): + self._VerifyExplicitPaddings( + tensor_in_sizes=[1, 2, 3, 3], + filter_in_sizes=[2, 2, 3, 3], + strides=[1, 1], + padding=[[0, 0], [0, 0]]) + + self._VerifyExplicitPaddings( + tensor_in_sizes=[3, 4, 3, 2], + filter_in_sizes=[1, 1, 2, 1], + strides=[2, 2], + padding=[[0, 0], [0, 0]]) + + @test_util.run_in_graph_and_eager_modes() + def testConv2D1x1Padding(self): + self._VerifyExplicitPaddings( + tensor_in_sizes=[1, 2, 3, 2], + filter_in_sizes=[2, 2, 2, 2], + strides=[1, 1], + padding=[[1, 1], [1, 1]]) + + self._VerifyExplicitPaddings( + tensor_in_sizes=[1, 2, 2, 1], + filter_in_sizes=[1, 1, 1, 2], + strides=[1, 1], + padding=[[1, 1], [1, 1]]) + + @test_util.run_in_graph_and_eager_modes() + def testConv2D2x2Padding(self): + self._VerifyExplicitPaddings( + tensor_in_sizes=[1, 2, 1, 2], + filter_in_sizes=[2, 1, 2, 1], + strides=[1, 1], + padding=[[2, 2], [2, 2]]) + + self._VerifyExplicitPaddings( + tensor_in_sizes=[1, 2, 1, 2], + filter_in_sizes=[1, 1, 2, 1], + strides=[2, 1], + padding=[[2, 2], [2, 2]]) + + @test_util.run_in_graph_and_eager_modes() + def testConv2DOnlyBottomPadding(self): + self._VerifyExplicitPaddings( + tensor_in_sizes=[1, 2, 3, 3], + filter_in_sizes=[2, 2, 3, 2], + strides=[1, 1], + padding=[[0, 3], [0, 0]], tol=2e-5) + + self._VerifyExplicitPaddings( + tensor_in_sizes=[2, 2, 4, 3], + filter_in_sizes=[1, 2, 3, 2], + strides=[2, 2], + padding=[[0, 3], [0, 0]]) + + @test_util.run_in_graph_and_eager_modes() + def testConv2DOnlyTopRightPadding(self): + self._VerifyExplicitPaddings( + tensor_in_sizes=[1, 2, 3, 3], + filter_in_sizes=[2, 2, 3, 2], + strides=[1, 1], + padding=[[1, 0], [0, 2]], + tol=5e-5) + + self._VerifyExplicitPaddings( + tensor_in_sizes=[1, 2, 4, 2], + filter_in_sizes=[2, 2, 2, 2], + strides=[1, 3], + padding=[[1, 0], [0, 2]]) + + @test_util.run_in_graph_and_eager_modes() + def testConv2DLotsPadding(self): + self._VerifyExplicitPaddings( + tensor_in_sizes=[1, 1, 1, 3], + filter_in_sizes=[2, 2, 3, 3], + strides=[1, 1], + padding=[[3, 4], [4, 2]]) + + self._VerifyExplicitPaddings( + tensor_in_sizes=[1, 2, 1, 1], + filter_in_sizes=[2, 2, 1, 3], + strides=[2, 1], + padding=[[3, 4], [4, 2]]) + + @test_util.run_in_graph_and_eager_modes() + def testConv2DExplicitPaddingWithDilations(self): + self._VerifyExplicitPaddings( + tensor_in_sizes=[1, 3, 2, 1], + filter_in_sizes=[1, 2, 1, 2], + strides=[1, 1], + padding=[[1, 0], [0, 1]], + dilations=[2, 1]) + + self._VerifyExplicitPaddings( + tensor_in_sizes=[1, 2, 3, 2], + filter_in_sizes=[3, 2, 2, 1], + strides=[1, 1], + padding=[[2, 1], [1, 2]], + dilations=[2, 3]) + + def testConv2DExplicitPaddingWithLayoutOptimizer(self): + # Test with Grappler's layout optimizer, to ensure the layout optimizer + # handles explicit padding correctly. + self._VerifyExplicitPaddings( + tensor_in_sizes=[1, 3, 2, 1], + filter_in_sizes=[1, 2, 1, 2], + strides=[1, 1], + padding=[[1, 0], [0, 1]], + dilations=[2, 1], + test_grappler_layout_optimizer=True) + + self._VerifyExplicitPaddings( + tensor_in_sizes=[1, 2, 3, 2], + filter_in_sizes=[3, 2, 2, 1], + strides=[1, 1], + padding=[[2, 1], [1, 2]], + dilations=[2, 3], + test_grappler_layout_optimizer=True) + # TODO(yzhwang): this currently fails. # self._VerifyValues(tensor_in_sizes=[1, 8, 8, 1], # filter_in_sizes=[2, 2, 1, 1], @@ -517,19 +700,22 @@ class Conv2DTest(test.TestCase): # expected=[72, 112, 392, 432]) # Testing for backprops - def _RunAndVerifyBackpropInput(self, input_sizes, filter_sizes, output_sizes, - strides, padding, expected, data_format, - use_gpu, err): - total_output_size = 1 - total_filter_size = 1 - for s in output_sizes: - total_output_size *= s - for s in filter_sizes: - total_filter_size *= s - # Initializes the input tensor with array containing incrementing - # numbers from 1. - x1 = [f * 1.0 for f in range(1, total_filter_size + 1)] - x2 = [f * 1.0 for f in range(1, total_output_size + 1)] + def _RunAndVerifyBackpropInput(self, + input_sizes, + filter_sizes, + output_sizes, + strides, + padding, + expected, + data_format, + use_gpu, + err, + dilations=(1, 1)): + if use_gpu and not test.is_gpu_available(cuda_only=True): + return + x1 = self._CreateNumpyTensor(filter_sizes) + x2 = self._CreateNumpyTensor(output_sizes) + dilations = list(dilations) with test_util.device(use_gpu): if data_format == "NCHW": input_sizes = test_util.NHWCToNCHW(input_sizes) @@ -537,18 +723,30 @@ class Conv2DTest(test.TestCase): t1 = constant_op.constant(x1, shape=filter_sizes) t2 = constant_op.constant(x2, shape=output_sizes) strides = [1] + strides + [1] + dilations = [1] + dilations + [1] + if isinstance(padding, (list, tuple)): + padding = [(0, 0)] + padding + [(0, 0)] if data_format == "NCHW": t2 = test_util.NHWCToNCHW(t2) strides = test_util.NHWCToNCHW(strides) + dilations = test_util.NHWCToNCHW(dilations) + if isinstance(padding, (list, tuple)): + padding = test_util.NHWCToNCHW((padding)) conv = nn_ops.conv2d_backprop_input( - t0, t1, t2, strides=strides, padding=padding, data_format=data_format) + t0, + t1, + t2, + strides=strides, + padding=padding, + data_format=data_format, + dilations=dilations) if data_format == "NCHW": conv = test_util.NCHWToNHWC(conv) # "values" consists of two tensors for two backprops value = self.evaluate(conv) self.assertShapeEqual(value, conv) - tf_logging.info("expected = ", expected) - tf_logging.info("actual = ", value) + tf_logging.debug("expected = %s", expected) + tf_logging.debug("actual = %s", value) self.assertArrayNear(expected, value.flatten(), err) def _CompareBackpropInput(self, input_sizes, filter_sizes, output_sizes, @@ -691,41 +889,51 @@ class Conv2DTest(test.TestCase): err=1e-5) # Testing for backprops - def _RunAndVerifyBackpropFilter(self, input_sizes, filter_sizes, output_sizes, - strides, padding, expected, data_format, - use_gpu): - total_input_size = 1 - total_output_size = 1 - for s in input_sizes: - total_input_size *= s - for s in output_sizes: - total_output_size *= s - # Initializes the input tensor with array containing incrementing - # numbers from 1. - x0 = [f * 1.0 for f in range(1, total_input_size + 1)] - x2 = [f * 1.0 for f in range(1, total_output_size + 1)] + def _RunAndVerifyBackpropFilter(self, + input_sizes, + filter_sizes, + output_sizes, + strides, + padding, + expected, + data_format, + use_gpu, + dilations=(1, 1), + err=1e-5): + x0 = self._CreateNumpyTensor(input_sizes) + x2 = self._CreateNumpyTensor(output_sizes) + dilations = list(dilations) + explicit_strides = [1] + strides + [1] + new_padding = padding + new_dilations = [1] + dilations + [1] + if isinstance(new_padding, (list, tuple)): + new_padding = [(0, 0)] + new_padding + [(0, 0)] + if data_format == "NCHW": + explicit_strides = test_util.NHWCToNCHW(explicit_strides) + new_dilations = test_util.NHWCToNCHW(new_dilations) + if isinstance(padding, (list, tuple)): + new_padding = test_util.NHWCToNCHW(new_padding) for dtype in self._DtypesToTest(use_gpu=use_gpu): with test_util.device(use_gpu): t0 = constant_op.constant(x0, shape=input_sizes, dtype=dtype) t1 = constant_op.constant(filter_sizes, shape=[len(filter_sizes)]) t2 = constant_op.constant(x2, shape=output_sizes, dtype=dtype) - explicit_strides = [1] + strides + [1] if data_format == "NCHW": t0 = test_util.NHWCToNCHW(t0) t2 = test_util.NHWCToNCHW(t2) - explicit_strides = test_util.NHWCToNCHW(explicit_strides) conv = nn_ops.conv2d_backprop_filter( t0, t1, t2, strides=explicit_strides, - padding=padding, + padding=new_padding, + dilations=new_dilations, data_format=data_format) value = self.evaluate(conv) self.assertShapeEqual(value, conv) - tf_logging.info("expected = ", expected) - tf_logging.info("actual = ", value) - self.assertArrayNear(expected, value.flatten(), 1e-5) + tf_logging.debug("expected = %s", expected) + tf_logging.debug("actual = %s", value) + self.assertArrayNear(expected, value.flatten(), err) def _CompareBackFilter(self, input_sizes, filter_sizes, output_sizes, conv_strides, padding): @@ -866,16 +1074,8 @@ class Conv2DTest(test.TestCase): def _RunAndVerifyBackpropInputDilation(self, input_sizes, filter_sizes, output_sizes, strides, dilations, padding, data_format, use_gpu, err): - total_input_size = 1 - total_filter_size = 1 - for s in input_sizes: - total_input_size *= s - for s in filter_sizes: - total_filter_size *= s - # Initializes the input tensor with array containing incrementing - # numbers from 1. - x1 = [f * 1.0 for f in range(1, total_input_size + 1)] - x2 = [f * 1.0 for f in range(1, total_filter_size + 1)] + x1 = self._CreateNumpyTensor(input_sizes) + x2 = self._CreateNumpyTensor(filter_sizes) default_dilations = (dilations[0] == 1 and dilations[1] == 1) if default_dilations or use_gpu: with self.cached_session(use_gpu=use_gpu) as sess: @@ -912,24 +1112,16 @@ class Conv2DTest(test.TestCase): value_2 = self.evaluate(conv_2) self.assertShapeEqual(value, conv) self.assertShapeEqual(value_2, conv_2) - tf_logging.info("expected = ", value_2) - tf_logging.info("actual = ", value) + tf_logging.debug("expected = %s", value_2) + tf_logging.debug("actual = %s", value) self.assertArrayNear(value_2.flatten(), value.flatten(), err) # Testing for backprops def _RunAndVerifyBackpropFilterDilation(self, input_sizes, filter_sizes, output_sizes, strides, dilations, padding, data_format, use_gpu, err): - total_input_size = 1 - total_filter_size = 1 - for s in input_sizes: - total_input_size *= s - for s in filter_sizes: - total_filter_size *= s - # Initializes the input tensor with array containing incrementing - # numbers from 1. - x1 = [f * 1.0 for f in range(1, total_input_size + 1)] - x2 = [f * 1.0 for f in range(1, total_filter_size + 1)] + x1 = self._CreateNumpyTensor(input_sizes) + x2 = self._CreateNumpyTensor(filter_sizes) default_dilations = (dilations[0] == 1 and dilations[1] == 1) if default_dilations or use_gpu: with self.cached_session(use_gpu=use_gpu) as sess: @@ -965,8 +1157,8 @@ class Conv2DTest(test.TestCase): value_2 = self.evaluate(conv_2) self.assertShapeEqual(value, conv) self.assertShapeEqual(value_2, conv_2) - tf_logging.info("expected = ", value_2) - tf_logging.info("actual = ", value) + tf_logging.debug("expected = %s", value_2) + tf_logging.debug("actual = %s", value) self.assertArrayNear(value_2.flatten(), value.flatten(), err) def testConv2D2x2Depth3ValidBackpropFilterStride1x1Dilation2x1(self): @@ -1111,20 +1303,347 @@ class Conv2DTest(test.TestCase): use_gpu=use_gpu, err=1e-5) + def _RunAndVerifyBackpropInputExplicitPadding(self, + input_sizes, + filter_sizes, + output_sizes, + strides, + padding, + data_format, + dilations=(1, 1), + err=2e-5): + x1 = self._CreateNumpyTensor(filter_sizes) + x2 = self._CreateNumpyTensor(output_sizes) + dilations = list(dilations) + padded_input_sizes = input_sizes[:] + padded_input_sizes[1] += padding[0][0] + padding[0][1] + padded_input_sizes[2] += padding[1][0] + padding[1][1] + c = nn_ops.conv2d_backprop_input( + padded_input_sizes, + x1, + x2, + strides=[1] + strides + [1], + padding="VALID", + dilations=[1] + dilations + [1]) + c = c[:, padding[0][0]:(c.shape[1] - padding[0][1]), padding[1][0]:( + c.shape[2] - padding[1][1]), :] + expected = list(self.evaluate(array_ops.reshape(c, [-1]))) + self._RunAndVerifyBackpropInput( + input_sizes, + filter_sizes, + output_sizes, + strides, + padding, + expected, + data_format, + use_gpu=True, + err=err, + dilations=dilations) + + @test_util.run_in_graph_and_eager_modes() + def testConv2D2x2Depth1Padding0x0BackpropInput(self): + if not test.is_gpu_available(cuda_only=True): + return + for (data_format, use_gpu) in GetTestConfigs(): + if use_gpu: + self._RunAndVerifyBackpropInputExplicitPadding( + input_sizes=[1, 2, 3, 1], + filter_sizes=[2, 2, 1, 1], + output_sizes=[1, 1, 2, 1], + strides=[1, 1], + padding=[[0, 0], [0, 0]], + data_format=data_format) + + self._RunAndVerifyBackpropInputExplicitPadding( + input_sizes=[1, 3, 4, 2], + filter_sizes=[2, 2, 2, 3], + output_sizes=[1, 1, 2, 3], + strides=[2, 2], + padding=[[0, 0], [0, 0]], + data_format=data_format) + + @test_util.run_in_graph_and_eager_modes() + def testConv2D2x2Depth1Padding1x1BackpropInput(self): + if not test.is_gpu_available(cuda_only=True): + return + + for (data_format, use_gpu) in GetTestConfigs(): + if use_gpu: + self._RunAndVerifyBackpropInputExplicitPadding( + input_sizes=[1, 2, 3, 1], + filter_sizes=[2, 2, 1, 2], + output_sizes=[1, 3, 4, 2], + strides=[1, 1], + padding=[[1, 1], [1, 1]], + data_format=data_format, err=1e-4) + + self._RunAndVerifyBackpropInputExplicitPadding( + input_sizes=[1, 2, 3, 2], + filter_sizes=[1, 1, 2, 1], + output_sizes=[1, 4, 3, 1], + strides=[1, 2], + padding=[[1, 1], [1, 1]], + data_format=data_format) + + self._RunAndVerifyBackpropInputExplicitPadding( + input_sizes=[1, 4, 3, 1], + filter_sizes=[2, 2, 1, 1], + output_sizes=[1, 4, 2, 1], + strides=[1, 2], + padding=[[1, 1], [1, 1]], + data_format=data_format, + dilations=[2, 2]) + + @test_util.run_in_graph_and_eager_modes() + def testConv2D2x2Depth1Padding2x2BackpropInput(self): + if not test.is_gpu_available(cuda_only=True): + return + + for (data_format, use_gpu) in GetTestConfigs(): + if use_gpu: + self._RunAndVerifyBackpropInputExplicitPadding( + input_sizes=[2, 3, 1, 1], + filter_sizes=[2, 1, 1, 1], + output_sizes=[2, 2, 5, 1], + strides=[3, 1], + padding=[[2, 2], [2, 2]], + data_format=data_format) + + self._RunAndVerifyBackpropInputExplicitPadding( + input_sizes=[1, 3, 6, 1], + filter_sizes=[3, 2, 1, 1], + output_sizes=[1, 3, 4, 1], + strides=[1, 2], + padding=[[2, 2], [2, 2]], + data_format=data_format, + dilations=[2, 3]) + + @test_util.run_in_graph_and_eager_modes() + def testConv2D2x2Depth1Padding_1_8_4_1_BackpropInput(self): + if not test.is_gpu_available(cuda_only=True): + return + for (data_format, use_gpu) in GetTestConfigs(): + if use_gpu: + self._RunAndVerifyBackpropInputExplicitPadding( + input_sizes=[1, 2, 3, 1], + filter_sizes=[2, 2, 1, 1], + output_sizes=[1, 10, 8, 1], + strides=[1, 1], + padding=[[1, 8], [4, 2]], + data_format=data_format, err=5e-5) + + self._RunAndVerifyBackpropInputExplicitPadding( + input_sizes=[1, 5, 3, 1], + filter_sizes=[3, 2, 1, 1], + output_sizes=[1, 4, 8, 1], + strides=[3, 1], + padding=[[1, 8], [4, 2]], + data_format=data_format) + + @test_util.run_in_graph_and_eager_modes() + def testConv2D2x2Depth1Padding_5_0_2_2_BackpropInput(self): + if not test.is_gpu_available(cuda_only=True): + return + for (data_format, use_gpu) in GetTestConfigs(): + if use_gpu: + self._RunAndVerifyBackpropInputExplicitPadding( + input_sizes=[1, 3, 3, 1], + filter_sizes=[2, 1, 1, 1], + output_sizes=[1, 7, 7, 1], + strides=[1, 1], + padding=[[5, 0], [2, 2]], + data_format=data_format, + err=5e-5) + + self._RunAndVerifyBackpropInputExplicitPadding( + input_sizes=[1, 4, 2, 1], + filter_sizes=[3, 3, 1, 1], + output_sizes=[1, 5, 2, 1], + strides=[1, 2], + padding=[[5, 0], [2, 2]], + data_format=data_format, + dilations=[2, 1]) + + def _RunAndVerifyBackpropFilterExplicitPadding(self, + input_sizes, + filter_sizes, + output_sizes, + strides, + padding, + data_format, + dilations=(1, 1), + err=1e-5): + x0 = self._CreateNumpyTensor(input_sizes) + x2 = self._CreateNumpyTensor(output_sizes) + dilations = list(dilations) + + x0 = np.pad(x0, [(0, 0)] + padding + [(0, 0)], "constant") + c = nn_ops.conv2d_backprop_filter( + x0, + filter_sizes, + x2, + strides=[1] + strides + [1], + padding="VALID", + dilations=[1] + dilations + [1]) + expected = list(self.evaluate(array_ops.reshape(c, [-1]))) + self._RunAndVerifyBackpropFilter( + input_sizes, + filter_sizes, + output_sizes, + strides, + padding, + expected, + data_format, + use_gpu=True, + dilations=dilations, + err=err) + + @test_util.run_in_graph_and_eager_modes() + def testConv2D2x2Depth1Padding0x0BackpropFilter(self): + if not test.is_gpu_available(cuda_only=True): + return + for (data_format, use_gpu) in GetTestConfigs(): + if use_gpu: + self._RunAndVerifyBackpropFilterExplicitPadding( + input_sizes=[1, 2, 3, 1], + filter_sizes=[2, 2, 1, 1], + output_sizes=[1, 1, 2, 1], + strides=[1, 1], + padding=[[0, 0], [0, 0]], + data_format=data_format) + + self._RunAndVerifyBackpropFilterExplicitPadding( + input_sizes=[1, 3, 4, 2], + filter_sizes=[2, 2, 2, 3], + output_sizes=[1, 1, 2, 3], + strides=[2, 2], + padding=[[0, 0], [0, 0]], + data_format=data_format) + + @test_util.run_in_graph_and_eager_modes() + def testConv2D2x2Depth1Padding1x1BackpropFilter(self): + if not test.is_gpu_available(cuda_only=True): + return + + for (data_format, use_gpu) in GetTestConfigs(): + if use_gpu: + self._RunAndVerifyBackpropFilterExplicitPadding( + input_sizes=[1, 2, 3, 1], + filter_sizes=[2, 2, 1, 2], + output_sizes=[1, 3, 4, 2], + strides=[1, 1], + padding=[[1, 1], [1, 1]], + data_format=data_format, + err=5e-5) + + self._RunAndVerifyBackpropFilterExplicitPadding( + input_sizes=[1, 2, 3, 2], + filter_sizes=[1, 1, 2, 1], + output_sizes=[1, 4, 3, 1], + strides=[1, 2], + padding=[[1, 1], [1, 1]], + data_format=data_format) + + self._RunAndVerifyBackpropFilterExplicitPadding( + input_sizes=[1, 4, 3, 1], + filter_sizes=[2, 2, 1, 1], + output_sizes=[1, 4, 2, 1], + strides=[1, 2], + padding=[[1, 1], [1, 1]], + data_format=data_format, + dilations=[2, 2]) + + @test_util.run_in_graph_and_eager_modes() + def testConv2D2x2Depth1Padding2x2BackpropFilter(self): + if not test.is_gpu_available(cuda_only=True): + return + + for (data_format, use_gpu) in GetTestConfigs(): + if use_gpu: + self._RunAndVerifyBackpropFilterExplicitPadding( + input_sizes=[2, 3, 1, 1], + filter_sizes=[2, 1, 1, 1], + output_sizes=[2, 2, 5, 1], + strides=[3, 1], + padding=[[2, 2], [2, 2]], + data_format=data_format) + + self._RunAndVerifyBackpropFilterExplicitPadding( + input_sizes=[1, 3, 6, 1], + filter_sizes=[3, 2, 1, 1], + output_sizes=[1, 3, 4, 1], + strides=[1, 2], + padding=[[2, 2], [2, 2]], + data_format=data_format, + dilations=[2, 3]) + + @test_util.run_in_graph_and_eager_modes() + def testConv2D2x2Depth1Padding_1_8_4_1_BackpropFilter(self): + if not test.is_gpu_available(cuda_only=True): + return + for (data_format, use_gpu) in GetTestConfigs(): + if use_gpu: + self._RunAndVerifyBackpropFilterExplicitPadding( + input_sizes=[1, 2, 3, 1], + filter_sizes=[2, 2, 1, 1], + output_sizes=[1, 10, 8, 1], + strides=[1, 1], + padding=[[1, 8], [4, 2]], + data_format=data_format, + err=1e-4) + + self._RunAndVerifyBackpropFilterExplicitPadding( + input_sizes=[1, 5, 3, 1], + filter_sizes=[3, 2, 1, 1], + output_sizes=[1, 4, 8, 1], + strides=[3, 1], + padding=[[1, 8], [4, 2]], + data_format=data_format) + + @test_util.run_in_graph_and_eager_modes() + def testConv2D2x2Depth1Padding_5_0_2_2_BackpropFilter(self): + if not test.is_gpu_available(cuda_only=True): + return + for (data_format, use_gpu) in GetTestConfigs(): + if use_gpu: + self._RunAndVerifyBackpropFilterExplicitPadding( + input_sizes=[1, 3, 3, 1], + filter_sizes=[2, 1, 1, 1], + output_sizes=[1, 7, 7, 1], + strides=[1, 1], + padding=[[5, 0], [2, 2]], + data_format=data_format, + err=1e-4) + + self._RunAndVerifyBackpropFilterExplicitPadding( + input_sizes=[1, 4, 2, 1], + filter_sizes=[3, 3, 1, 1], + output_sizes=[1, 5, 2, 1], + strides=[1, 2], + padding=[[5, 0], [2, 2]], + data_format=data_format, + dilations=[2, 1]) + # Gradient checkers def ConstructAndTestGradient(self, batch, input_rows, input_cols, filter_rows, filter_cols, in_depth, out_depth, stride_rows, stride_cols, padding, test_input, data_format, - use_gpu): + use_gpu, max_err=0.002): input_shape = [batch, input_rows, input_cols, in_depth] filter_shape = [filter_rows, filter_cols, in_depth, out_depth] # TODO(yangke): re-factor the computation of output shape. if padding == "VALID": output_rows = (input_rows - filter_rows + stride_rows) // stride_rows output_cols = (input_cols - filter_cols + stride_cols) // stride_cols - else: + elif padding == "SAME": output_rows = (input_rows + stride_rows - 1) // stride_rows output_cols = (input_cols + stride_cols - 1) // stride_cols + else: + self.assertIsInstance(padding, (list, tuple)) + output_rows = (input_rows + padding[1][0] + padding[1][1] - filter_rows + + stride_rows) // stride_rows + output_cols = (input_cols + padding[2][0] + padding[2][1] - filter_cols + + stride_cols) // stride_cols output_shape = [batch, output_rows, output_cols, out_depth] input_size = 1 for x in input_shape: @@ -1145,16 +1664,19 @@ class Conv2DTest(test.TestCase): filter_tensor = constant_op.constant( filter_data, shape=filter_shape, dtype=dtype, name="filter") strides = [1, stride_rows, stride_cols, 1] + new_padding = padding if data_format == "NCHW": new_input_tensor = test_util.NHWCToNCHW(input_tensor) strides = test_util.NHWCToNCHW(strides) + if isinstance(padding, (list, tuple)): + new_padding = test_util.NHWCToNCHW(padding) else: new_input_tensor = input_tensor conv = nn_ops.conv2d( new_input_tensor, filter_tensor, strides, - padding, + new_padding, data_format=data_format, name="conv") if data_format == "NCHW": @@ -1178,8 +1700,8 @@ class Conv2DTest(test.TestCase): # since fp16 numerical gradients are too imprecise. err = np.fabs(jacob_t - reference_jacob_t).max() - tf_logging.info("conv_2d gradient error = ", err) - self.assertLess(err, 0.002) + tf_logging.debug("conv_2d gradient error = %s", err) + self.assertLess(err, max_err) def testInputGradientValidPaddingStrideOne(self): for (data_format, use_gpu) in GetTestConfigs(): @@ -1436,6 +1958,248 @@ class Conv2DTest(test.TestCase): data_format=data_format, use_gpu=use_gpu) + def testInputGradient1x1PaddingStrideOne(self): + if not test.is_gpu_available(cuda_only=True): + return + for (data_format, use_gpu) in GetTestConfigs(): + if use_gpu: + self.ConstructAndTestGradient( + batch=2, + input_rows=5, + input_cols=4, + filter_rows=3, + filter_cols=3, + in_depth=2, + out_depth=3, + stride_rows=1, + stride_cols=1, + padding=[[0, 0], [1, 1], [1, 1], [0, 0]], + test_input=True, + data_format=data_format, + use_gpu=use_gpu, + max_err=0.0025) + + def testFilterGradient1x1PaddingStrideOne(self): + if not test.is_gpu_available(cuda_only=True): + return + for (data_format, use_gpu) in GetTestConfigs(): + if use_gpu: + self.ConstructAndTestGradient( + batch=2, + input_rows=5, + input_cols=4, + filter_rows=3, + filter_cols=3, + in_depth=2, + out_depth=3, + stride_rows=1, + stride_cols=1, + padding=[[0, 0], [1, 1], [1, 1], [0, 0]], + test_input=False, + data_format=data_format, + use_gpu=use_gpu) + + def testInputGradient1x1PaddingStrideTwo(self): + if not test.is_gpu_available(cuda_only=True): + return + for (data_format, use_gpu) in GetTestConfigs(): + if use_gpu: + self.ConstructAndTestGradient( + batch=2, + input_rows=4, + input_cols=5, + filter_rows=3, + filter_cols=3, + in_depth=2, + out_depth=3, + stride_rows=2, + stride_cols=2, + padding=[[0, 0], [1, 1], [1, 1], [0, 0]], + test_input=True, + data_format=data_format, + use_gpu=use_gpu) + + def testFilterGradient1x1PaddingStrideTwo(self): + if not test.is_gpu_available(cuda_only=True): + return + for (data_format, use_gpu) in GetTestConfigs(): + if use_gpu: + self.ConstructAndTestGradient( + batch=2, + input_rows=4, + input_cols=5, + filter_rows=3, + filter_cols=3, + in_depth=2, + out_depth=3, + stride_rows=2, + stride_cols=2, + padding=[[0, 0], [1, 1], [1, 1], [0, 0]], + test_input=False, + data_format=data_format, + use_gpu=use_gpu) + + def testInputGradient2x2PaddingStrideOne(self): + if not test.is_gpu_available(cuda_only=True): + return + for (data_format, use_gpu) in GetTestConfigs(): + if use_gpu: + self.ConstructAndTestGradient( + batch=2, + input_rows=5, + input_cols=4, + filter_rows=3, + filter_cols=3, + in_depth=2, + out_depth=3, + stride_rows=1, + stride_cols=1, + padding=[[0, 0], [2, 2], [2, 2], [0, 0]], + test_input=True, + data_format=data_format, + use_gpu=use_gpu) + + def testFilterGradient2x2PaddingStrideOne(self): + if not test.is_gpu_available(cuda_only=True): + return + for (data_format, use_gpu) in GetTestConfigs(): + if use_gpu: + self.ConstructAndTestGradient( + batch=2, + input_rows=5, + input_cols=4, + filter_rows=3, + filter_cols=3, + in_depth=2, + out_depth=3, + stride_rows=1, + stride_cols=1, + padding=[[0, 0], [2, 2], [2, 2], [0, 0]], + test_input=False, + data_format=data_format, + use_gpu=use_gpu, + max_err=0.003) + + def testInputGradient1_2_3_4PaddingStride3x2(self): + if not test.is_gpu_available(cuda_only=True): + return + for (data_format, use_gpu) in GetTestConfigs(): + if use_gpu: + self.ConstructAndTestGradient( + batch=2, + input_rows=8, + input_cols=5, + filter_rows=4, + filter_cols=2, + in_depth=3, + out_depth=2, + stride_rows=3, + stride_cols=2, + padding=[[0, 0], [1, 2], [3, 4], [0, 0]], + test_input=True, + data_format=data_format, + use_gpu=use_gpu) + + def testFilterGradient1_2_3_4PaddingStride3x2(self): + if not test.is_gpu_available(cuda_only=True): + return + for (data_format, use_gpu) in GetTestConfigs(): + if use_gpu: + self.ConstructAndTestGradient( + batch=2, + input_rows=8, + input_cols=5, + filter_rows=4, + filter_cols=2, + in_depth=3, + out_depth=2, + stride_rows=3, + stride_cols=2, + padding=[[0, 0], [1, 2], [3, 4], [0, 0]], + test_input=False, + data_format=data_format, + use_gpu=use_gpu) + + def testInputGradient4_3_2_1PaddingStride2x1(self): + if not test.is_gpu_available(cuda_only=True): + return + for (data_format, use_gpu) in GetTestConfigs(): + if use_gpu: + self.ConstructAndTestGradient( + batch=3, + input_rows=5, + input_cols=7, + filter_rows=3, + filter_cols=2, + in_depth=1, + out_depth=2, + stride_rows=2, + stride_cols=1, + padding=[[0, 0], [4, 3], [2, 1], [0, 0]], + test_input=True, + data_format=data_format, + use_gpu=use_gpu) + + def testFilterGradient4_3_2_1PaddingStride2x1(self): + if not test.is_gpu_available(cuda_only=True): + return + for (data_format, use_gpu) in GetTestConfigs(): + if use_gpu: + self.ConstructAndTestGradient( + batch=3, + input_rows=5, + input_cols=7, + filter_rows=3, + filter_cols=2, + in_depth=1, + out_depth=2, + stride_rows=2, + stride_cols=1, + padding=[[0, 0], [4, 3], [2, 1], [0, 0]], + test_input=False, + data_format=data_format, + use_gpu=use_gpu) + + def testInputGradient0_0_0_5PaddingStride1x2(self): + if not test.is_gpu_available(cuda_only=True): + return + for (data_format, use_gpu) in GetTestConfigs(): + if use_gpu: + self.ConstructAndTestGradient( + batch=2, + input_rows=6, + input_cols=7, + filter_rows=3, + filter_cols=4, + in_depth=3, + out_depth=2, + stride_rows=1, + stride_cols=2, + padding=[[0, 0], [0, 0], [0, 5], [0, 0]], + test_input=True, + data_format=data_format, + use_gpu=use_gpu) + + def testFilterGradient0_0_0_5PaddingStride1x2(self): + if not test.is_gpu_available(cuda_only=True): + return + for (data_format, use_gpu) in GetTestConfigs(): + if use_gpu: + self.ConstructAndTestGradient( + batch=2, + input_rows=6, + input_cols=7, + filter_rows=3, + filter_cols=4, + in_depth=3, + out_depth=2, + stride_rows=1, + stride_cols=2, + padding=[[0, 0], [0, 0], [0, 5], [0, 0]], + test_input=False, + data_format=data_format, + use_gpu=use_gpu) + def testShapeFunctionEdgeCases(self): # All shapes unknown. c1 = nn_ops.conv2d( @@ -1473,6 +2237,55 @@ class Conv2DTest(test.TestCase): strides=[1, 1, 1, 1], padding="SAME") + # Negative padding. + with self.assertRaises(ValueError): + nn_ops.conv2d( + array_ops.placeholder(dtypes.float32), + array_ops.placeholder(dtypes.float32), + strides=[1, 1, 1, 1], + padding=[[0, 0], [0, -1], [1, 2], [0, 0]]) + + # Nonzero padding in nonspatial dimension. + with self.assertRaises(ValueError): + nn_ops.conv2d( + array_ops.placeholder(dtypes.float32), + array_ops.placeholder(dtypes.float32), + strides=[1, 1, 1, 1], + padding=[[1, 0], [0, 0], [0, 0], [0, 0]]) + + # Nonzero NCHW padding in nonspatial dimension. + with self.assertRaises(ValueError): + nn_ops.conv2d( + array_ops.placeholder(dtypes.float32), + array_ops.placeholder(dtypes.float32), + strides=[1, 1, 1, 1], + padding=[[0, 0], [0, 1], [0, 0], [0, 0]], + data_format="NCHW") + + # Wrong amount of padding + with self.assertRaises(ValueError): + nn_ops.conv2d( + array_ops.placeholder(dtypes.float32), + array_ops.placeholder(dtypes.float32), + strides=[1, 1, 1, 1], + padding=[[0, 0], [0, 0], [0, 0]]) + + # Only specify one padding amount per dimension + with self.assertRaises(ValueError): + nn_ops.conv2d( + array_ops.placeholder(dtypes.float32), + array_ops.placeholder(dtypes.float32), + strides=[1, 1, 1, 1], + padding=[[0], [0], [0], [0]]) + + # Explicit padding elements are not lists + with self.assertRaises(ValueError): + nn_ops.conv2d( + array_ops.placeholder(dtypes.float32), + array_ops.placeholder(dtypes.float32), + strides=[1, 1, 1, 1], + padding=[0, 0, 0, 0]) + def testOpEdgeCases(self): with self.cached_session() as sess: # Illegal strides. @@ -1513,6 +2326,41 @@ class Conv2DTest(test.TestCase): strides=[1, 1, 1, 1], padding="VALID")) + # Filter larger than input + padding. + with self.assertRaisesRegexp(ValueError, "Negative dimension size"): + sess.run( + nn_ops.conv2d( + array_ops.placeholder(dtypes.float32, shape=[32, 20, 20, 3]), + array_ops.placeholder(dtypes.float32, shape=[24, 25, 3, 2]), + strides=[1, 1, 1, 1], + padding=[[0, 0], [2, 2], [2, 2], [0, 0]])) + + if test.is_gpu_available(cuda_only=True): + with self.test_session(use_gpu=True): + # Negative padding during backprop. + with self.assertRaisesRegexp(errors_impl.InvalidArgumentError, + "nonnegative"): + sess.run( + nn_ops.conv2d_backprop_input([32, 20, 20, 3], + array_ops.placeholder( + dtypes.float32, + shape=[18, 18, 3, 2]), + array_ops.placeholder( + dtypes.float32, + shape=[32, 3, 2, 2]), + strides=[1, 1, 1, 1], + padding=[[0, 0], [-1, 0], [0, 0], + [0, 0]])) + with self.assertRaisesRegexp(errors_impl.InvalidArgumentError, + "nonnegative"): + sess.run( + nn_ops.conv2d_backprop_filter( + array_ops.placeholder(dtypes.float32, shape=[32, 20, 20, 3]), + [18, 18, 3, 2], + array_ops.placeholder(dtypes.float32, shape=[32, 3, 2, 2]), + strides=[1, 1, 1, 1], + padding=[[0, 0], [-1, 0], [0, 0], [0, 0]])) + class DepthwiseConv2DTest(test.TestCase): @@ -1546,7 +2394,7 @@ class DepthwiseConv2DTest(test.TestCase): conv = nn_impl.depthwise_conv2d( t1, t2, strides=[1, stride, stride, 1], padding=padding) value = self.evaluate(conv) - tf_logging.info("value = ", value) + tf_logging.debug("value = %s", value) self.assertArrayNear(expected, np.ravel(value), 1e-5) self.assertShapeEqual(value, conv) @@ -1668,7 +2516,7 @@ class SeparableConv2DTest(test.TestCase): conv = array_ops.transpose(conv, [0, 2, 3, 1]) value = self.evaluate(conv) - tf_logging.info("value = ", value) + tf_logging.debug("value = %s", value) self.assertArrayNear(expected, np.ravel(value), 1e-3) self.assertShapeEqual(value, conv) @@ -1828,6 +2676,194 @@ class Conv2DBenchmark(test.Benchmark): name="conv_stack_iter_%d" % iter_index, wall_time=wall_time) tf_logging.info("conv_stack_iter_%d: %.4f" % (iter_index, wall_time)) + def _bench_op(self, name, op, burn_iters, num_iters): + config = config_pb2.ConfigProto() + # Prevent Grappler from optimizing away the entire graph. + config.graph_options.rewrite_options.dependency_optimization = ( + rewriter_config_pb2.RewriterConfig.OFF) + with session_lib.Session(config=config) as session: + variables.global_variables_initializer().run() + self.run_op_benchmark( + session, op, burn_iters=burn_iters, min_iters=num_iters, name=name) + + def benchmarkExplicitVsManualPadding(self): + """Compare performance of EXPLICIT padding and calling tf.pad. + + A Conv2D op with EXPLICIT padding is benchmarked, and a tf.pad with the same + padding followed by an equivalent Conv2D op is benchmarked. + """ + if not test.is_gpu_available(): + return + + with ops.Graph().as_default(): + burn_iters = 15 + num_iters = 300 + batch_size = 64 + # The input and filter correspond to the first layer of Resnet50. + input = variables.Variable( # pylint: disable=redefined-builtin + random_ops.random_uniform([ + batch_size, + 3, + 224, + 224 + ])) + filter = variables.Variable(random_ops.random_uniform([7, 7, 3, 64])) # pylint: disable=redefined-builtin + strides = [1, 1, 2, 2] + padding = [(0, 0), (0, 0), (3, 3), (3, 3)] + output_explicit_pad = nn_ops.conv2d( + input, filter, strides, padding=padding, data_format="NCHW") + input_padded = array_ops.pad(input, padding) + output_manual_pad = nn_ops.conv2d( + input_padded, filter, strides, padding="VALID", data_format="NCHW") + # Benchmark just the forward pass. + self._bench_op("explicit_pad_forward", output_explicit_pad.op, burn_iters, + num_iters) + self._bench_op("manual_pad_forward", output_manual_pad.op, burn_iters, + num_iters) + + # Benchmark both the forward and backwards passes. + input_grad_explicit_pad, filter_grad_explicit_pad = ( + gradients_impl.gradients(output_explicit_pad, [input, filter])) + self._bench_op( + "explicit_pad_backward", + control_flow_ops.group(input_grad_explicit_pad, + filter_grad_explicit_pad), burn_iters, + num_iters) + input_grad_manual_pad, filter_grad_manual_pad = gradients_impl.gradients( + output_manual_pad, [input, filter]) + self._bench_op( + "manual_pad_backward", + control_flow_ops.group(input_grad_manual_pad, filter_grad_manual_pad), + burn_iters, num_iters) + + def benchmarkExplicitVsSamePaddingGraph(self): + """Compare performance of EXPLICIT and SAME padding in graph mode. + + A Conv2D op with SAME padding is benchmarked, and an equivalent Conv2D op + with explicit padding is benchmarked, where the padding is the same as in + the SAME case. The purpose is to ensure EXPLICIT padding is just as + efficient as the SAME case + """ + if not test.is_gpu_available(): + return + + with ops.Graph().as_default(): + burn_iters = 15 + num_convs = 20 + num_iters = 50 + batch_size = 64 + # The input and filter correspond to a middle layer of Resnet50. + input = variables.Variable( # pylint: disable=redefined-builtin + random_ops.random_uniform([ + batch_size, + 256, + 14, + 14 + ])) + filter = variables.Variable(random_ops.random_uniform([3, 3, 256, 256])) # pylint: disable=redefined-builtin + strides = [1, 1, 1, 1] + padding = [(0, 0), (0, 0), (1, 1), (1, 1)] + output_explicit_pad = input + output_same_pad = input + + for _ in range(num_convs): + output_explicit_pad = nn_ops.conv2d( + output_explicit_pad, + filter, + strides, + padding=padding, + data_format="NCHW") + output_same_pad = nn_ops.conv2d( + output_same_pad, + filter, + strides, + padding="SAME", + data_format="NCHW") + grad_explicit_pad, = gradients_impl.gradients(output_explicit_pad, filter) + grad_same_pad, = gradients_impl.gradients(output_same_pad, filter) + self._bench_op("graph_explicit_pad", grad_explicit_pad.op, burn_iters, + num_iters) + self._bench_op("graph_same_pad", grad_same_pad.op, burn_iters, num_iters) + + def benchmarkExplicitVsSamePaddingEager(self): + """Compare performance of EXPLICIT and SAME padding in eager mode. + + A Conv2D op with SAME padding is benchmarked, and an equivalent Conv2D op + with explicit padding is benchmarked, where the padding is the same as in + the SAME case. Currently, EXPLICIT padding is slightly slower, due to the + fact the Python padding list must be checked and processed before the Conv2D + op can run. + """ + # TODO(reedwm): Make EXPLICIT padding as fast as SAME padding. + if not test.is_gpu_available(): + return + + with context.eager_mode(): + burn_iters = 15 + num_convs = 20 + num_iters = 50 + batch_size = 64 + # The input and filter correspond to a middle layer of Resnet50. + input = variables.Variable( # pylint: disable=redefined-builtin + random_ops.random_uniform([ + batch_size, + 256, + 14, + 14 + ])) + filter = variables.Variable(random_ops.random_uniform([3, 3, 256, 256])) # pylint: disable=redefined-builtin + strides = [1, 1, 1, 1] + padding = [(0, 0), (0, 0), (1, 1), (1, 1)] + output_explicit_pad = input + output_same_pad = input + for _ in range(burn_iters): + output_explicit_pad = nn_ops.conv2d( + output_explicit_pad, + filter, + strides, + padding=padding, + data_format="NCHW") + output_same_pad = nn_ops.conv2d( + output_same_pad, + filter, + strides, + padding="SAME", + data_format="NCHW") + + start = time.time() + for _ in range(num_iters): + with backprop.GradientTape() as tape: + for _ in range(num_convs): + output_explicit_pad = nn_ops.conv2d( + output_explicit_pad, + filter, + strides, + padding=padding, + data_format="NCHW") + tape.gradient(output_explicit_pad, filter) + end = time.time() + self.report_benchmark( + name="eager_explicit_pad", + wall_time=(end - start) / num_iters, + iters=num_iters) + + start = time.time() + for _ in range(num_iters): + with backprop.GradientTape() as tape: + for _ in range(num_convs): + output_same_pad = nn_ops.conv2d( + output_same_pad, + filter, + strides, + padding="SAME", + data_format="NCHW") + tape.gradient(output_same_pad, filter) + end = time.time() + self.report_benchmark( + name="eager_same_pad", + wall_time=(end - start) / num_iters, + iters=num_iters) + def GetInceptionFwdTest(input_size, filter_size, stride, padding, gpu_only=False): diff --git a/tensorflow/python/ops/nn_grad.py b/tensorflow/python/ops/nn_grad.py index 7131e4abc4..6ca2b2aafe 100644 --- a/tensorflow/python/ops/nn_grad.py +++ b/tensorflow/python/ops/nn_grad.py @@ -514,29 +514,40 @@ def _SparseSoftmaxCrossEntropyWithLogitsGrad(op, grad_0, _): @ops.RegisterGradient("Conv2D") def _Conv2DGrad(op, grad): + """Gradient function for Conv2D.""" dilations = op.get_attr("dilations") strides = op.get_attr("strides") padding = op.get_attr("padding") + explicit_paddings = op.get_attr("explicit_paddings") use_cudnn_on_gpu = op.get_attr("use_cudnn_on_gpu") data_format = op.get_attr("data_format") shape_0, shape_1 = array_ops.shape_n([op.inputs[0], op.inputs[1]]) + + # We call the gen_nn_ops backprop functions instead of nn_ops backprop + # functions for performance reasons in Eager mode. gen_nn_ops functions take a + # `explicit_paddings` parameter, but nn_ops functions do not. So if were were + # to use the nn_ops functions, we would have to convert `padding` and + # `explicit_paddings` into a single `padding` parameter, increasing overhead + # in Eager mode. return [ - nn_ops.conv2d_backprop_input( + gen_nn_ops.conv2d_backprop_input( shape_0, op.inputs[1], grad, dilations=dilations, strides=strides, padding=padding, + explicit_paddings=explicit_paddings, use_cudnn_on_gpu=use_cudnn_on_gpu, data_format=data_format), - nn_ops.conv2d_backprop_filter( + gen_nn_ops.conv2d_backprop_filter( op.inputs[0], shape_1, grad, dilations=dilations, strides=strides, padding=padding, + explicit_paddings=explicit_paddings, use_cudnn_on_gpu=use_cudnn_on_gpu, data_format=data_format) ] diff --git a/tensorflow/python/ops/nn_ops.py b/tensorflow/python/ops/nn_ops.py index 6f2d2c15bd..f71fcef130 100644 --- a/tensorflow/python/ops/nn_ops.py +++ b/tensorflow/python/ops/nn_ops.py @@ -171,7 +171,7 @@ class _NonAtrousConvolution(object): raise ValueError("data_format must be \"NHWC\" or \"NCHW\".") self.strides = strides self.data_format = data_format - self.conv_op = gen_nn_ops.conv2d + self.conv_op = conv2d elif conv_dims == 3: if data_format is None or data_format == "NDHWC": strides = [1] + list(strides) + [1] @@ -1373,6 +1373,44 @@ def atrous_conv2d(value, filters, rate, padding, name=None): name=name) +def _convert_padding(padding): + """Converts Python padding to C++ padding for ops which take EXPLICIT padding. + + Args: + padding: the `padding` argument for a Python op which supports EXPLICIT + padding. + + Returns: + (padding, explicit_paddings) pair, which should be passed as attributes to a + C++ op. + + Raises: + ValueError: If padding is invalid. + """ + explicit_paddings = [] + if padding == "EXPLICIT": + # Give a better error message if EXPLICIT is passed. + raise ValueError('"EXPLICIT" is not a valid value for the padding ' + "parameter. To use explicit padding, the padding " + "parameter must be a list.") + if isinstance(padding, (list, tuple)): + for i, dim_paddings in enumerate(padding): + if not isinstance(dim_paddings, (list, tuple)): + raise ValueError("When padding is a list, each element of padding must " + "be a list/tuple of size 2. Element with index %d of " + "padding is not a list/tuple" % i) + if len(dim_paddings) != 2: + raise ValueError("When padding is a list, each element of padding must " + "be a list/tuple of size 2. Element with index %d of " + "padding has size %d" % (i, len(dim_paddings))) + explicit_paddings.extend(dim_paddings) + if len(padding) != 4: + raise ValueError("When padding is a list, it must be of size 4. Got " + "padding of size: %d" % len(padding)) + padding = "EXPLICIT" + return padding, explicit_paddings + + @tf_export("nn.conv2d", v1=[]) def conv2d_v2(input, # pylint: disable=redefined-builtin filters, @@ -1418,8 +1456,13 @@ def conv2d_v2(input, # pylint: disable=redefined-builtin 1-D tensor of length 4. The stride of the sliding window for each dimension of `input`. The dimension order is determined by the value of `data_format`, see below for details. - padding: A `string` from: `"SAME", "VALID"`. - The type of padding algorithm to use. + padding: Either the `string `"SAME"` or `"VALID"` indicating the type of + padding algorithm to use, or a list indicating the explicit paddings at + the start and end of each dimension. When explicit padding is used and + data_format is `"NHWC"`, this should be in the form `[[0, 0], [pad_top, + pad_bottom], [pad_left, pad_right], [0, 0]]`. When explicit padding used + and data_format is `"NCHW"`, this should be in the form `[[0, 0], [0, 0], + [pad_top, pad_bottom], [pad_left, pad_right]]`. data_format: An optional `string` from: `"NHWC", "NCHW"`. Defaults to `"NHWC"`. Specify the data format of the input and output data. With the @@ -1441,15 +1484,98 @@ def conv2d_v2(input, # pylint: disable=redefined-builtin # pylint: enable=line-too-long if dilations is None: dilations = [1, 1, 1, 1] + return conv2d(input, # pylint: disable=redefined-builtin + filters, + strides, + padding, + use_cudnn_on_gpu=True, + data_format=data_format, + dilations=dilations, + name=name) + + +@tf_export(v1=["nn.conv2d"]) +def conv2d( # pylint: disable=redefined-builtin,dangerous-default-value + input, + filter, + strides, + padding, + use_cudnn_on_gpu=True, + data_format="NHWC", + dilations=[1, 1, 1, 1], + name=None): + r"""Computes a 2-D convolution given 4-D `input` and `filter` tensors. + + Given an input tensor of shape `[batch, in_height, in_width, in_channels]` + and a filter / kernel tensor of shape + `[filter_height, filter_width, in_channels, out_channels]`, this op + performs the following: + + 1. Flattens the filter to a 2-D matrix with shape + `[filter_height * filter_width * in_channels, output_channels]`. + 2. Extracts image patches from the input tensor to form a *virtual* + tensor of shape `[batch, out_height, out_width, + filter_height * filter_width * in_channels]`. + 3. For each patch, right-multiplies the filter matrix and the image patch + vector. + + In detail, with the default NHWC format, + + output[b, i, j, k] = + sum_{di, dj, q} input[b, strides[1] * i + di, strides[2] * j + dj, q] + * filter[di, dj, q, k] + + Must have `strides[0] = strides[3] = 1`. For the most common case of the same + horizontal and vertices strides, `strides = [1, stride, stride, 1]`. + + Args: + input: A `Tensor`. Must be one of the following types: + `half`, `bfloat16`, `float32`, `float64`. + A 4-D tensor. The dimension order is interpreted according to the value + of `data_format`, see below for details. + filter: A `Tensor`. Must have the same type as `input`. + A 4-D tensor of shape + `[filter_height, filter_width, in_channels, out_channels]` + strides: A list of `ints`. + 1-D tensor of length 4. The stride of the sliding window for each + dimension of `input`. The dimension order is determined by the value of + `data_format`, see below for details. + padding: Either the `string `"SAME"` or `"VALID"` indicating the type of + padding algorithm to use, or a list indicating the explicit paddings at + the start and end of each dimension. When explicit padding is used and + data_format is `"NHWC"`, this should be in the form `[[0, 0], [pad_top, + pad_bottom], [pad_left, pad_right], [0, 0]]`. When explicit padding used + and data_format is `"NCHW"`, this should be in the form `[[0, 0], [0, 0], + [pad_top, pad_bottom], [pad_left, pad_right]]`. + use_cudnn_on_gpu: An optional `bool`. Defaults to `True`. + data_format: An optional `string` from: `"NHWC", "NCHW"`. + Defaults to `"NHWC"`. + Specify the data format of the input and output data. With the + default format "NHWC", the data is stored in the order of: + [batch, height, width, channels]. + Alternatively, the format could be "NCHW", the data storage order of: + [batch, channels, height, width]. + dilations: An optional list of `ints`. Defaults to `[1, 1, 1, 1]`. + 1-D tensor of length 4. The dilation factor for each dimension of + `input`. If set to k > 1, there will be k-1 skipped cells between each + filter element on that dimension. The dimension order is determined by the + value of `data_format`, see above for details. Dilations in the batch and + depth dimensions must be 1. + name: A name for the operation (optional). + + Returns: + A `Tensor`. Has the same type as `input`. + """ + padding, explicit_paddings = _convert_padding(padding) return gen_nn_ops.conv2d(input, # pylint: disable=redefined-builtin - filters, + filter, strides, padding, - use_cudnn_on_gpu=True, + use_cudnn_on_gpu=use_cudnn_on_gpu, + explicit_paddings=explicit_paddings, data_format=data_format, dilations=dilations, name=name) -tf_export(v1=["nn.conv2d"])(gen_nn_ops.conv2d) @tf_export("nn.conv2d_backprop_filter", v1=[]) @@ -1478,8 +1604,13 @@ def conv2d_backprop_filter_v2(input, # pylint: disable=redefined-builtin The stride of the sliding window for each dimension of the input of the convolution. Must be in the same order as the dimension specified with format. - padding: A `string` from: `"SAME", "VALID"`. - The type of padding algorithm to use. + padding: Either the `string `"SAME"` or `"VALID"` indicating the type of + padding algorithm to use, or a list indicating the explicit paddings at + the start and end of each dimension. When explicit padding is used and + data_format is `"NHWC"`, this should be in the form `[[0, 0], [pad_top, + pad_bottom], [pad_left, pad_right], [0, 0]]`. When explicit padding used + and data_format is `"NCHW"`, this should be in the form `[[0, 0], [0, 0], + [pad_top, pad_bottom], [pad_left, pad_right]]`. data_format: An optional `string` from: `"NHWC", "NCHW"`. Defaults to `"NHWC"`. Specify the data format of the input and output data. With the @@ -1500,17 +1631,75 @@ def conv2d_backprop_filter_v2(input, # pylint: disable=redefined-builtin """ if dilations is None: dilations = [1, 1, 1, 1] - return gen_nn_ops.conv2d_backprop_filter(input, # pylint: disable=redefined-builtin - filter_sizes, - out_backprop, - strides, - padding, - use_cudnn_on_gpu=True, - data_format=data_format, - dilations=dilations, - name=name) -tf_export(v1=["nn.conv2d_backprop_filter"])( - gen_nn_ops.conv2d_backprop_filter) + return conv2d_backprop_filter(input, # pylint: disable=redefined-builtin + filter_sizes, + out_backprop, + strides, + padding, + use_cudnn_on_gpu=True, + data_format=data_format, + dilations=dilations, + name=name) + + +@tf_export(v1=["nn.conv2d_backprop_filter"]) +def conv2d_backprop_filter( # pylint: disable=redefined-builtin,dangerous-default-value + input, + filter_sizes, + out_backprop, + strides, + padding, + use_cudnn_on_gpu=True, + data_format="NHWC", + dilations=[1, 1, 1, 1], + name=None): + r"""Computes the gradients of convolution with respect to the filter. + + Args: + input: A `Tensor`. Must be one of the following types: + `half`, `bfloat16`, `float32`, `float64`. + 4-D with shape `[batch, in_height, in_width, in_channels]`. + filter_sizes: A `Tensor` of type `int32`. + An integer vector representing the tensor shape of `filter`, + where `filter` is a 4-D + `[filter_height, filter_width, in_channels, out_channels]` tensor. + out_backprop: A `Tensor`. Must have the same type as `input`. + 4-D with shape `[batch, out_height, out_width, out_channels]`. + Gradients w.r.t. the output of the convolution. + strides: A list of `ints`. + The stride of the sliding window for each dimension of the input + of the convolution. Must be in the same order as the dimension specified + with format. + padding: Either the `string `"SAME"` or `"VALID"` indicating the type of + padding algorithm to use, or a list indicating the explicit paddings at + the start and end of each dimension. When explicit padding is used and + data_format is `"NHWC"`, this should be in the form `[[0, 0], [pad_top, + pad_bottom], [pad_left, pad_right], [0, 0]]`. When explicit padding used + and data_format is `"NCHW"`, this should be in the form `[[0, 0], [0, 0], + [pad_top, pad_bottom], [pad_left, pad_right]]`. + use_cudnn_on_gpu: An optional `bool`. Defaults to `True`. + data_format: An optional `string` from: `"NHWC", "NCHW"`. + Defaults to `"NHWC"`. + Specify the data format of the input and output data. With the + default format "NHWC", the data is stored in the order of: + [batch, in_height, in_width, in_channels]. + Alternatively, the format could be "NCHW", the data storage order of: + [batch, in_channels, in_height, in_width]. + dilations: An optional list of `ints`. Defaults to `[1, 1, 1, 1]`. + 1-D tensor of length 4. The dilation factor for each dimension of + `input`. If set to k > 1, there will be k-1 skipped cells between each + filter element on that dimension. The dimension order is determined by + the value of `data_format`, see above for details. Dilations in the batch + and depth dimensions must be 1. + name: A name for the operation (optional). + + Returns: + A `Tensor`. Has the same type as `input`. + """ + padding, explicit_paddings = _convert_padding(padding) + return gen_nn_ops.conv2d_backprop_filter( + input, filter_sizes, out_backprop, strides, padding, use_cudnn_on_gpu, + explicit_paddings, data_format, dilations, name) @tf_export("nn.conv2d_backprop_input", v1=[]) @@ -1539,8 +1728,13 @@ def conv2d_backprop_input_v2(input_sizes, The stride of the sliding window for each dimension of the input of the convolution. Must be in the same order as the dimension specified with format. - padding: A `string` from: `"SAME", "VALID"`. - The type of padding algorithm to use. + padding: Either the `string `"SAME"` or `"VALID"` indicating the type of + padding algorithm to use, or a list indicating the explicit paddings at + the start and end of each dimension. When explicit padding is used and + data_format is `"NHWC"`, this should be in the form `[[0, 0], [pad_top, + pad_bottom], [pad_left, pad_right], [0, 0]]`. When explicit padding used + and data_format is `"NCHW"`, this should be in the form `[[0, 0], [0, 0], + [pad_top, pad_bottom], [pad_left, pad_right]]`. data_format: An optional `string` from: `"NHWC", "NCHW"`. Defaults to `"NHWC"`. Specify the data format of the input and output data. With the @@ -1561,17 +1755,75 @@ def conv2d_backprop_input_v2(input_sizes, """ if dilations is None: dilations = [1, 1, 1, 1] - return gen_nn_ops.conv2d_backprop_input(input_sizes, - filters, - out_backprop, - strides, - padding, - use_cudnn_on_gpu=True, - data_format=data_format, - dilations=dilations, - name=name) -tf_export(v1=["nn.conv2d_backprop_input"])( - gen_nn_ops.conv2d_backprop_input) + return conv2d_backprop_input(input_sizes, + filters, + out_backprop, + strides, + padding, + use_cudnn_on_gpu=True, + data_format=data_format, + dilations=dilations, + name=name) + + +@tf_export(v1=["nn.conv2d_backprop_input"]) +def conv2d_backprop_input( # pylint: disable=redefined-builtin,dangerous-default-value + input_sizes, + filter, + out_backprop, + strides, + padding, + use_cudnn_on_gpu=True, + data_format="NHWC", + dilations=[1, 1, 1, 1], + name=None): + r"""Computes the gradients of convolution with respect to the input. + + Args: + input_sizes: A `Tensor` of type `int32`. + An integer vector representing the shape of `input`, + where `input` is a 4-D `[batch, height, width, channels]` tensor. + filter: A `Tensor`. Must be one of the following types: + `half`, `bfloat16`, `float32`, `float64`. + 4-D with shape + `[filter_height, filter_width, in_channels, out_channels]`. + out_backprop: A `Tensor`. Must have the same type as `filter`. + 4-D with shape `[batch, out_height, out_width, out_channels]`. + Gradients w.r.t. the output of the convolution. + strides: A list of `ints`. + The stride of the sliding window for each dimension of the input + of the convolution. Must be in the same order as the dimension specified + with format. + padding: Either the `string `"SAME"` or `"VALID"` indicating the type of + padding algorithm to use, or a list indicating the explicit paddings at + the start and end of each dimension. When explicit padding is used and + data_format is `"NHWC"`, this should be in the form `[[0, 0], [pad_top, + pad_bottom], [pad_left, pad_right], [0, 0]]`. When explicit padding used + and data_format is `"NCHW"`, this should be in the form `[[0, 0], [0, 0], + [pad_top, pad_bottom], [pad_left, pad_right]]`. + use_cudnn_on_gpu: An optional `bool`. Defaults to `True`. + data_format: An optional `string` from: `"NHWC", "NCHW"`. + Defaults to `"NHWC"`. + Specify the data format of the input and output data. With the + default format "NHWC", the data is stored in the order of: + [batch, in_height, in_width, in_channels]. + Alternatively, the format could be "NCHW", the data storage order of: + [batch, in_channels, in_height, in_width]. + dilations: An optional list of `ints`. Defaults to `[1, 1, 1, 1]`. + 1-D tensor of length 4. The dilation factor for each dimension of + `input`. If set to k > 1, there will be k-1 skipped cells between each + filter element on that dimension. The dimension order is determined by + the value of `data_format`, see above for details. Dilations in the batch + and depth dimensions must be 1. + name: A name for the operation (optional). + + Returns: + A `Tensor`. Has the same type as `filter`. + """ + padding, explicit_paddings = _convert_padding(padding) + return gen_nn_ops.conv2d_backprop_input( + input_sizes, filter, out_backprop, strides, padding, use_cudnn_on_gpu, + explicit_paddings, data_format, dilations, name) @tf_export(v1=["nn.conv2d_transpose"]) -- GitLab From e3b8fedc923d03fd5f388daf444b42b22dd909b8 Mon Sep 17 00:00:00 2001 From: Cong Liu Date: Tue, 8 Jan 2019 18:56:15 -0800 Subject: [PATCH 532/622] [XLA] Propagate compiler specific ShapeSizeFunction into TargetVerifierMetadata. PiperOrigin-RevId: 228440043 --- .../compiler/xla/service/hlo_verifier.h | 33 ++++++++++++------- .../compiler/xla/tests/hlo_test_base.cc | 6 ++-- tensorflow/compiler/xla/tests/hlo_test_base.h | 8 +++-- 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/tensorflow/compiler/xla/service/hlo_verifier.h b/tensorflow/compiler/xla/service/hlo_verifier.h index a1a6aba972..479905b317 100644 --- a/tensorflow/compiler/xla/service/hlo_verifier.h +++ b/tensorflow/compiler/xla/service/hlo_verifier.h @@ -168,8 +168,13 @@ class ShapeVerifier : public DfsHloVisitor { // An interface used to encapsulate target-specific verification quirks. class TargetVerifierMetadata { public: + TargetVerifierMetadata(std::function shape_size_function) + : shape_size_function_(shape_size_function) {} + // Returns a target-specific shape size. - virtual int64 ShapeSize(const Shape& shape) const = 0; + int64 ShapeSize(const Shape& shape) const { + return shape_size_function_(shape); + } virtual std::unique_ptr GetVerifier() const = 0; @@ -178,20 +183,23 @@ class TargetVerifierMetadata { TargetVerifierMetadata(const TargetVerifierMetadata&) = delete; TargetVerifierMetadata& operator=(const TargetVerifierMetadata&) = delete; + + private: + // Returns a target-specific shape size. + std::function shape_size_function_; }; // The default implementation of TargetVerifierMetadata, used unless the target // needs to override it. class DefaultVerifierMetadata : public TargetVerifierMetadata { public: - DefaultVerifierMetadata(bool layout_sensitive, bool allow_mixed_precision) - : layout_sensitive_(layout_sensitive), + DefaultVerifierMetadata( + bool layout_sensitive, bool allow_mixed_precision, + std::function shape_size_function) + : TargetVerifierMetadata(shape_size_function), + layout_sensitive_(layout_sensitive), allow_mixed_precision_(allow_mixed_precision) {} - int64 ShapeSize(const Shape& shape) const override { - return ShapeUtil::ByteSizeOf(shape); - } - // Creates a ShapeVerifier that checks that shapes match inferred // expectations. This creates a new verifier every time because ShapeVerifier, // being a DfsHloVisitor, is stateful. We want a clean object for each run of @@ -210,11 +218,14 @@ class DefaultVerifierMetadata : public TargetVerifierMetadata { // the module. class HloVerifier : public HloModulePass { public: - explicit HloVerifier(bool layout_sensitive, bool allow_mixed_precision, - std::function - instruction_can_change_layout_func = {}) + explicit HloVerifier( + bool layout_sensitive, bool allow_mixed_precision, + std::function + instruction_can_change_layout_func = {}, + std::function shape_size_func = + [](const Shape& shape) { return ShapeUtil::ByteSizeOf(shape); }) : target_metadata_(absl::make_unique( - layout_sensitive, allow_mixed_precision)), + layout_sensitive, allow_mixed_precision, shape_size_func)), instruction_can_change_layout_func_( std::move(instruction_can_change_layout_func)) { CHECK(instruction_can_change_layout_func_ == nullptr || layout_sensitive); diff --git a/tensorflow/compiler/xla/tests/hlo_test_base.cc b/tensorflow/compiler/xla/tests/hlo_test_base.cc index d57846e19b..66f72ba8d2 100644 --- a/tensorflow/compiler/xla/tests/hlo_test_base.cc +++ b/tensorflow/compiler/xla/tests/hlo_test_base.cc @@ -139,7 +139,8 @@ std::unique_ptr HloTestBase::CreateNewVerifiedModule( const string& name) { return absl::make_unique( name, GetModuleConfigForTest(), verifier_layout_sensitive_, - allow_mixed_precision_in_hlo_verifier_); + allow_mixed_precision_in_hlo_verifier_, + backend().compiler()->ShapeSizeBytesFunction()); } StatusOr> @@ -147,7 +148,8 @@ HloTestBase::ParseAndReturnVerifiedModule(absl::string_view hlo_text, const HloModuleConfig& config) { auto module = absl::make_unique( TestName(), config, verifier_layout_sensitive_, - allow_mixed_precision_in_hlo_verifier_); + allow_mixed_precision_in_hlo_verifier_, + backend().compiler()->ShapeSizeBytesFunction()); TF_RETURN_IF_ERROR(ParseHloString(hlo_text, module.get())); TF_RETURN_IF_ERROR(module->Verify()); return std::move(module); diff --git a/tensorflow/compiler/xla/tests/hlo_test_base.h b/tensorflow/compiler/xla/tests/hlo_test_base.h index 1d1e7f4372..69a4f96288 100644 --- a/tensorflow/compiler/xla/tests/hlo_test_base.h +++ b/tensorflow/compiler/xla/tests/hlo_test_base.h @@ -46,10 +46,12 @@ class VerifiedHloModule : public HloModule { public: VerifiedHloModule(const string& name, const HloModuleConfig& config, bool verifier_layout_sensitive, - bool allow_mixed_precision_in_hlo_verifier) + bool allow_mixed_precision_in_hlo_verifier, + std::function shape_size_function) : HloModule(name, config), - verifier_(verifier_layout_sensitive, - allow_mixed_precision_in_hlo_verifier) {} + verifier_( + verifier_layout_sensitive, allow_mixed_precision_in_hlo_verifier, + /*instruction_can_change_layout_func=*/{}, shape_size_function) {} ~VerifiedHloModule() override { VerifyOrAddFailure("in destructor"); } -- GitLab From 6522d752cf3e5d26a507d9c4c517ee7633ae4c57 Mon Sep 17 00:00:00 2001 From: Tim Shen Date: Tue, 8 Jan 2019 19:41:27 -0800 Subject: [PATCH 533/622] Removes the hack that mutates an HloInstruction in order to auto-tune the convolution for a specific algorithm. Redemption. PiperOrigin-RevId: 228443963 --- .../service/gpu/cudnn_conv_algorithm_picker.cc | 12 +++++++----- .../service/gpu/cudnn_conv_algorithm_picker.h | 3 ++- .../xla/service/gpu/cudnn_conv_runner.cc | 16 ++++++++++------ .../compiler/xla/service/gpu/cudnn_conv_runner.h | 12 ++++++++++-- 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/tensorflow/compiler/xla/service/gpu/cudnn_conv_algorithm_picker.cc b/tensorflow/compiler/xla/service/gpu/cudnn_conv_algorithm_picker.cc index 6d6780fa1c..057d0d2364 100644 --- a/tensorflow/compiler/xla/service/gpu/cudnn_conv_algorithm_picker.cc +++ b/tensorflow/compiler/xla/service/gpu/cudnn_conv_algorithm_picker.cc @@ -146,7 +146,8 @@ tensorflow::mutex_lock LockGpu(const se::StreamExecutor* stream_exec) { // trouble, but we may want to revisit this if we ever find a model where // caching would speed up compilation a lot. StatusOr -CudnnConvAlgorithmPicker::PickBestAlgorithm(HloCustomCallInstruction* instr) { +CudnnConvAlgorithmPicker::PickBestAlgorithm( + const HloCustomCallInstruction* instr) { // TODO(timshen): for now only check fp16. It can be expanded to other types, // with some work on the HLO routines. const bool cross_check_enabled = @@ -249,12 +250,13 @@ CudnnConvAlgorithmPicker::PickBestAlgorithm(HloCustomCallInstruction* instr) { VLOG(3) << "Trying algorithm " << AlgorithmToString(alg) << " for " << instr->ToString(); - backend_config.set_algorithm(alg.algo_id()); - backend_config.set_tensor_ops_enabled(alg.tensor_ops_enabled()); - TF_RETURN_IF_ERROR(instr->set_backend_config(backend_config)); + // Use assignment insetad of brace-list to make GCC 4.9 happy. + RunConvOptions options; + options.profile_result = &profile_result; + options.algo_override = alg; bool launch_ok = RunCudnnConv(instr, absl::MakeSpan(operand_buffers), result_buffer, - &scratch_allocator, &stream, &profile_result) + &scratch_allocator, &stream, options) .ok(); if (launch_ok && profile_result.is_valid()) { diff --git a/tensorflow/compiler/xla/service/gpu/cudnn_conv_algorithm_picker.h b/tensorflow/compiler/xla/service/gpu/cudnn_conv_algorithm_picker.h index 642af787af..4991db0948 100644 --- a/tensorflow/compiler/xla/service/gpu/cudnn_conv_algorithm_picker.h +++ b/tensorflow/compiler/xla/service/gpu/cudnn_conv_algorithm_picker.h @@ -56,7 +56,8 @@ class CudnnConvAlgorithmPicker : public HloModulePass { StatusOr RunOnComputation(HloComputation* computation); StatusOr RunOnInstruction(HloInstruction* instr); - StatusOr PickBestAlgorithm(HloCustomCallInstruction* instr); + StatusOr PickBestAlgorithm( + const HloCustomCallInstruction* instr); se::StreamExecutor* stream_exec_; // never null DeviceMemoryAllocator* allocator_; // may be null diff --git a/tensorflow/compiler/xla/service/gpu/cudnn_conv_runner.cc b/tensorflow/compiler/xla/service/gpu/cudnn_conv_runner.cc index 3425e1b494..b628f27f4b 100644 --- a/tensorflow/compiler/xla/service/gpu/cudnn_conv_runner.cc +++ b/tensorflow/compiler/xla/service/gpu/cudnn_conv_runner.cc @@ -395,32 +395,36 @@ Status RunCudnnConv(const HloCustomCallInstruction* conv, absl::Span operand_buffers, se::DeviceMemoryBase result_buffer, se::DeviceMemoryBase scratch_buf, se::Stream* stream, - se::dnn::ProfileResult* profile_result) { + RunConvOptions options) { ScratchBufAllocator scratch_allocator(scratch_buf); return RunCudnnConv(conv, operand_buffers, result_buffer, &scratch_allocator, - stream, profile_result); + stream, options); } Status RunCudnnConv(const HloCustomCallInstruction* conv, absl::Span operand_buffers, se::DeviceMemoryBase result_buffer, se::ScratchAllocator* scratch_allocator, se::Stream* stream, - se::dnn::ProfileResult* profile_result) { + RunConvOptions options) { TF_ASSIGN_OR_RETURN(CudnnConvParams params, GetCudnnConvParams(conv, operand_buffers, result_buffer)); + if (options.algo_override) { + params.algorithm = AlgorithmConfig(*options.algo_override); + } + PrimitiveType output_primitive_type = conv->shape().tuple_shapes(0).element_type(); switch (output_primitive_type) { case F16: return RunCudnnConvImpl(params, scratch_allocator, stream, - profile_result); + options.profile_result); case F32: return RunCudnnConvImpl(params, scratch_allocator, stream, - profile_result); + options.profile_result); case F64: return RunCudnnConvImpl(params, scratch_allocator, stream, - profile_result); + options.profile_result); default: LOG(FATAL) << ShapeUtil::HumanString(*params.output_shape); } diff --git a/tensorflow/compiler/xla/service/gpu/cudnn_conv_runner.h b/tensorflow/compiler/xla/service/gpu/cudnn_conv_runner.h index edbc75a94a..7cc325bce3 100644 --- a/tensorflow/compiler/xla/service/gpu/cudnn_conv_runner.h +++ b/tensorflow/compiler/xla/service/gpu/cudnn_conv_runner.h @@ -28,6 +28,14 @@ limitations under the License. namespace xla { namespace gpu { +struct RunConvOptions { + // Nullable output-parameter pointer for profiling results. + se::dnn::ProfileResult* profile_result = nullptr; + + // Use this algorithm, instead the one from the instrcution. + absl::optional algo_override; +}; + // This file contains low-level routines for running cudnn convolutions. // Calls into cudnn to run the specified convolution. @@ -46,13 +54,13 @@ Status RunCudnnConv(const HloCustomCallInstruction* conv, absl::Span operand_buffers, se::DeviceMemoryBase result_buffer, se::DeviceMemoryBase scratch_buf, se::Stream* stream, - se::dnn::ProfileResult* profile_result = nullptr); + RunConvOptions = {}); Status RunCudnnConv(const HloCustomCallInstruction* conv, absl::Span operand_buffers, se::DeviceMemoryBase result_buffer, se::ScratchAllocator* scratch_allocator, se::Stream* stream, - se::dnn::ProfileResult* profile_result = nullptr); + RunConvOptions = {}); } // namespace gpu } // namespace xla -- GitLab From 9a54ef2b7cd156ce9fb11d143cea61cc84aed3b3 Mon Sep 17 00:00:00 2001 From: Sanjoy Das Date: Tue, 8 Jan 2019 19:54:03 -0800 Subject: [PATCH 534/622] Automated rollback of commit f71e77471c140bac2f4dcb9a003139fb5c657302 PiperOrigin-RevId: 228444974 --- tensorflow/compiler/xla/service/cpu/BUILD | 2 + .../cpu/cpu_eigen_tensor_alignment_test.cc | 38 ++++++ .../xla/service/cpu/dot_op_emitter.cc | 117 ++++++------------ .../xla/service/cpu/dot_op_emitter_internal.h | 88 +++++++++++++ 4 files changed, 168 insertions(+), 77 deletions(-) create mode 100644 tensorflow/compiler/xla/service/cpu/dot_op_emitter_internal.h diff --git a/tensorflow/compiler/xla/service/cpu/BUILD b/tensorflow/compiler/xla/service/cpu/BUILD index a197bdddc8..de62aa60aa 100644 --- a/tensorflow/compiler/xla/service/cpu/BUILD +++ b/tensorflow/compiler/xla/service/cpu/BUILD @@ -385,6 +385,7 @@ cc_library( srcs = ["dot_op_emitter.cc"], hdrs = [ "dot_op_emitter.h", + "dot_op_emitter_internal.h", ], deps = [ ":cpu_options", @@ -1028,6 +1029,7 @@ tf_cc_test( size = "small", srcs = ["cpu_eigen_tensor_alignment_test.cc"], deps = [ + ":dot_op_emitter", ":ir_emission_utils", ":target_machine_features_fake", "//tensorflow/compiler/xla:test", diff --git a/tensorflow/compiler/xla/service/cpu/cpu_eigen_tensor_alignment_test.cc b/tensorflow/compiler/xla/service/cpu/cpu_eigen_tensor_alignment_test.cc index 485769a373..823bdf259c 100644 --- a/tensorflow/compiler/xla/service/cpu/cpu_eigen_tensor_alignment_test.cc +++ b/tensorflow/compiler/xla/service/cpu/cpu_eigen_tensor_alignment_test.cc @@ -13,6 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ +#include "tensorflow/compiler/xla/service/cpu/dot_op_emitter_internal.h" #include "tensorflow/compiler/xla/service/cpu/ir_emission_utils.h" #include "tensorflow/compiler/xla/service/cpu/target_machine_features_fake.h" #include "tensorflow/compiler/xla/service/hlo_parser.h" @@ -22,11 +23,48 @@ namespace xla { namespace cpu { namespace { +using internal::DotImplementationStrategy; +using internal::DotInfo; +using internal::GetDotImplementationStrategy; + // Test that we don't call into Eigen with tensors too small to be aligned // reliably. class CpuEigenTensorAlignmentTest : public ::testing::Test {}; +TEST_F(CpuEigenTensorAlignmentTest, EigenDotAlignment) { + string hlo_string = R"( +HloModule DotOperation + +ENTRY DotOperation { + arg0 = f32[5,256] parameter(0) + arg1 = f32[256,1024] parameter(1) + ROOT dot = f32[5,1024] dot(arg0, arg1), lhs_contracting_dims={1}, rhs_contracting_dims={0} +} +)"; + + TF_ASSERT_OK_AND_ASSIGN(std::unique_ptr module, + ParseHloString(hlo_string)); + + HloInstruction* dot = module->entry_computation()->root_instruction(); + + TargetMachineFeaturesWithFakeAlignmentLogic target_machine_with_no_alignment( + [](int64 size) { return 1; }); + + EXPECT_EQ(GetDotImplementationStrategy(HloModuleConfig{}, DotInfo(*dot), + target_machine_with_no_alignment), + DotImplementationStrategy::kNaiveLlvmIr); + + TargetMachineFeaturesWithFakeAlignmentLogic + target_machine_with_full_alignment([](int64 size) { + return TargetMachineFeatures::kEigenExpectedTensorAlignment; + }); + + EXPECT_NE(GetDotImplementationStrategy(HloModuleConfig{}, DotInfo(*dot), + target_machine_with_full_alignment), + DotImplementationStrategy::kNaiveLlvmIr); +} + TEST_F(CpuEigenTensorAlignmentTest, EigenConvAlignment) { string hlo_string = R"( HloModule ConvOperation diff --git a/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc b/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc index b018e0cd46..e8a84ebe6b 100644 --- a/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc +++ b/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc @@ -14,6 +14,7 @@ limitations under the License. ==============================================================================*/ #include "tensorflow/compiler/xla/service/cpu/dot_op_emitter.h" +#include "tensorflow/compiler/xla/service/cpu/dot_op_emitter_internal.h" #include #include @@ -42,63 +43,17 @@ namespace xla { using llvm_ir::SetToFirstInsertPoint; namespace cpu { + +using internal::DotImplementationStrategy; +using internal::DotInfo; +using internal::GetDotImplementationStrategy; + namespace { // Returns true if we should call into multi-threaded Eigen routines. bool ShouldUseMultiThreadedEigen(const HloModuleConfig& config) { return config.debug_options().xla_cpu_multi_thread_eigen(); } -// Represents a dot operation. We use this in lieu of an `HloInstruction` -// because we want to be able to create this for the "inner" dot operation in a -// batch dot, for which there is no separate HLO instruction. -struct DotInfo { - Shape lhs_shape; - Shape rhs_shape; - Shape result_shape; - DotDimensionNumbers dim_nums; - - explicit DotInfo(const HloInstruction& instr) { - CHECK_EQ(instr.opcode(), HloOpcode::kDot); - lhs_shape = instr.operand(0)->shape(); - rhs_shape = instr.operand(1)->shape(); - result_shape = instr.shape(); - dim_nums = instr.dot_dimension_numbers(); - } -}; - -// Dictates how a dot operation is implemented. -enum class DotImplementationStrategy { - // The dot operation is lowered into LLVM IR that implements a naive nested - // loop that computes the result one element at a time. This is our - // "fallback"; we don't really want this to kick in for any non-trival dot - // operation. - kNaiveLlvmIr, - - // The dot operation is lowered into LLVM IR that implements a tiled - // Matrix*Vector operation. This strategy also allows fusing in a bias add - // into the dot. The matrix can be row major or column major, both are - // supported. - kTiledLlvmIrGemv, - - // The dot operation is lowered into LLVM IR that implemetns a tiled - // Matrix*Matrix operation. No fusions are supported. The two inputs - // and the output have to be row major. - kTiledLlvmIrGemm, - - // The dot operation is lowered into a call into an Eigen routine. No fusions - // are supported today. The two inputs and the output have to be row major. - // However, we do allow transposing either the LHS or the RHS as part of the - // GEMM -- we expose this flexibility as flexibility in the contraction - // dimensions, but we can also see this as flexibility in the input layouts. - kEigen, -}; - -// Returns the implementation strategy for a dot with the configuration -// `dot_info`. -DotImplementationStrategy GetDotImplementationStrategy( - const HloModuleConfig& config, const DotInfo& dot_info, - const TargetMachineFeatures& target_machine_features); - // Helper class for emitting LLVM IR to perform the dot operation. class DotOpEmitter { public: @@ -235,8 +190,9 @@ void DotOpEmitter::EmitTiledLlvmIrGemm() { } int64 size_bytes = m * n * ShapeUtil::ByteSizeOfPrimitiveType(primitive_type); - b_->CreateMemSet(target, b_->getInt8(0), /*Size=*/size_bytes, - /*Align=*/1); + b_->CreateMemSet( + target, b_->getInt8(0), size_bytes, + target_machine_features_.minimum_alignment_for_allocation(size_bytes)); int64 max_target_vector_width = target_machine_features_.vector_register_num_elements( @@ -740,34 +696,40 @@ absl::optional ProfitableToMakeDotOperandColumnMajor( return {}; } +namespace internal { namespace { // Return whether the given shape is rank 2. bool IsRank2(const Shape& shape) { return shape.rank() == 2; } -bool IsSimpleLayout(const Layout& layout) { - return layout.tiles().empty() && layout.format() == DENSE; -} - // In a gemm operation where output = lhs * rhs, check whether the given shapes // are valid for the operation. -bool AreGemmShapes(const Shape& lhs_shape, const Shape& rhs_shape, - const Shape& output_shape, - const TargetMachineFeatures& target_machine_features) { - CHECK(!lhs_shape.has_layout() || IsSimpleLayout(lhs_shape.layout())) - << lhs_shape.DebugString(); - CHECK(!rhs_shape.has_layout() || IsSimpleLayout(rhs_shape.layout())) - << rhs_shape.DebugString(); - CHECK(!output_shape.has_layout() || IsSimpleLayout(output_shape.layout())) - << output_shape.DebugString(); - - switch (output_shape.element_type()) { - case F64: - case F32: - case F16: - return IsRank2(lhs_shape) && IsRank2(rhs_shape) && IsRank2(output_shape); - default: - return false; +bool AreAlignedGemmShapes( + const Shape& lhs_shape, const Shape& rhs_shape, const Shape& output_shape, + const TargetMachineFeatures& target_machine_features) { + // The inputs and the output must + // 1) be matrices with no padding, and + // 2) have an allowed element type. + PrimitiveType output_primitive_type = output_shape.element_type(); + if (!(output_primitive_type == F64 || output_primitive_type == F32 || + output_primitive_type == F16)) { + return false; + } + + if (!(IsRank2(lhs_shape) && IsRank2(rhs_shape) && IsRank2(output_shape))) { + return false; + } + + auto is_aligned = [&](const Shape& shape) { + return GetMinimumAlignmentForArray(shape, target_machine_features) >= + TargetMachineFeatures::kEigenExpectedTensorAlignment; + }; + + if (!is_aligned(lhs_shape) || !is_aligned(rhs_shape) || + !is_aligned(output_shape)) { + return false; } + + return true; } bool IsAlignedGemm(const DotInfo& dot_info, @@ -777,8 +739,8 @@ bool IsAlignedGemm(const DotInfo& dot_info, return false; } - return AreGemmShapes(dot_info.lhs_shape, dot_info.rhs_shape, - dot_info.result_shape, target_machine_features); + return AreAlignedGemmShapes(dot_info.lhs_shape, dot_info.rhs_shape, + dot_info.result_shape, target_machine_features); } bool CanEmitTiledLlvmIrGemm( @@ -819,6 +781,7 @@ bool CanEmitTiledLlvmIrGemm( return true; } +} // namespace DotImplementationStrategy GetDotImplementationStrategy( const HloModuleConfig& config, const DotInfo& dot_info, @@ -843,7 +806,7 @@ DotImplementationStrategy GetDotImplementationStrategy( return DotImplementationStrategy::kNaiveLlvmIr; } -} // namespace +} // namespace internal bool DotImplementationCanHandleTranspose( const HloInstruction& dot_instr, diff --git a/tensorflow/compiler/xla/service/cpu/dot_op_emitter_internal.h b/tensorflow/compiler/xla/service/cpu/dot_op_emitter_internal.h new file mode 100644 index 0000000000..cc28918ed6 --- /dev/null +++ b/tensorflow/compiler/xla/service/cpu/dot_op_emitter_internal.h @@ -0,0 +1,88 @@ +/* Copyright 2018 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_COMPILER_XLA_SERVICE_CPU_DOT_OP_EMITTER_INTERNAL_H_ +#define TENSORFLOW_COMPILER_XLA_SERVICE_CPU_DOT_OP_EMITTER_INTERNAL_H_ + +#include "tensorflow/compiler/xla/service/cpu/target_machine_features.h" +#include "tensorflow/compiler/xla/service/hlo_instruction.h" +#include "tensorflow/compiler/xla/service/hlo_module.h" + +// ----------------------------------------------------------------------------- +// INTERNAL HEADER. +// +// This file exposes internal implementation details from dot_op_emitter.cc for +// unit tests. Please do not depend on this! +// +// ----------------------------------------------------------------------------- + +namespace xla { +namespace cpu { +namespace internal { + +// Represents a dot operation. We use this in lieu of an `HloInstruction` +// because we want to be able to create this for the "inner" dot operation in a +// batch dot, for which there is no separate HLO instruction. +struct DotInfo { + Shape lhs_shape; + Shape rhs_shape; + Shape result_shape; + DotDimensionNumbers dim_nums; + + explicit DotInfo(const HloInstruction& instr) { + CHECK_EQ(instr.opcode(), HloOpcode::kDot); + lhs_shape = instr.operand(0)->shape(); + rhs_shape = instr.operand(1)->shape(); + result_shape = instr.shape(); + dim_nums = instr.dot_dimension_numbers(); + } +}; + +// Dictates how a dot operation is implemented. +enum class DotImplementationStrategy { + // The dot operation is lowered into LLVM IR that implements a naive nested + // loop that computes the result one element at a time. This is our + // "fallback"; we don't really want this to kick in for any non-trival dot + // operation. + kNaiveLlvmIr, + + // The dot operation is lowered into LLVM IR that implements a tiled + // Matrix*Vector operation. This strategy also allows fusing in a bias add + // into the dot. The matrix can be row major or column major, both are + // supported. + kTiledLlvmIrGemv, + + // The dot operation is lowered into LLVM IR that implemetns a tiled + // Matrix*Matrix operation. No fusions are supported. The two inputs + // and the output have to be row major. + kTiledLlvmIrGemm, + + // The dot operation is lowered into a call into an Eigen routine. No fusions + // are supported today. The two inputs and the output have to be row major. + // However, we do allow transposing either the LHS or the RHS as part of the + // GEMM -- we expose this flexibility as flexibility in the contraction + // dimensions, but we can also see this as flexibility in the input layouts. + kEigen, +}; + +// Returns the implementation strategy for a dot with the configuration +// `dot_info`. +DotImplementationStrategy GetDotImplementationStrategy( + const HloModuleConfig& config, const DotInfo& dot_info, + const TargetMachineFeatures& target_machine_features); +} // namespace internal +} // namespace cpu +} // namespace xla + +#endif // TENSORFLOW_COMPILER_XLA_SERVICE_CPU_DOT_OP_EMITTER_INTERNAL_H_ -- GitLab From 8d4893d6efa971b35db9f4eb654adb1559b7186c Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Tue, 8 Jan 2019 20:19:22 -0800 Subject: [PATCH 535/622] Update ops-related pbtxt files. PiperOrigin-RevId: 228447178 --- .../core/ops/compat/ops_history.v1.pbtxt | 254 ++++++++++++++++++ tensorflow/core/ops/ops.pbtxt | 27 ++ 2 files changed, 281 insertions(+) diff --git a/tensorflow/core/ops/compat/ops_history.v1.pbtxt b/tensorflow/core/ops/compat/ops_history.v1.pbtxt index 28d085b2d2..9698673dfe 100644 --- a/tensorflow/core/ops/compat/ops_history.v1.pbtxt +++ b/tensorflow/core/ops/compat/ops_history.v1.pbtxt @@ -13631,6 +13631,151 @@ op { } } } +op { + name: "Conv2D" + input_arg { + name: "input" + type_attr: "T" + } + input_arg { + name: "filter" + type_attr: "T" + } + output_arg { + name: "output" + type_attr: "T" + } + attr { + name: "T" + type: "type" + allowed_values { + list { + type: DT_HALF + type: DT_BFLOAT16 + type: DT_FLOAT + type: DT_DOUBLE + } + } + } + attr { + name: "strides" + type: "list(int)" + } + attr { + name: "use_cudnn_on_gpu" + type: "bool" + default_value { + b: true + } + } + attr { + name: "padding" + type: "string" + allowed_values { + list { + s: "SAME" + s: "VALID" + s: "EXPLICIT" + } + } + } + attr { + name: "explicit_paddings" + type: "list(int)" + default_value { + list { + } + } + } + attr { + name: "data_format" + type: "string" + default_value { + s: "NHWC" + } + allowed_values { + list { + s: "NHWC" + s: "NCHW" + } + } + } + attr { + name: "dilations" + type: "list(int)" + default_value { + list { + i: 1 + i: 1 + i: 1 + i: 1 + } + } + } +} +op { + name: "Conv2DBackpropFilter" + input_arg { + name: "input" + type_attr: "T" + } + input_arg { + name: "filter_sizes" + type: DT_INT32 + } + input_arg { + name: "out_backprop" + type_attr: "T" + } + output_arg { + name: "output" + type_attr: "T" + } + attr { + name: "T" + type: "type" + allowed_values { + list { + type: DT_HALF + type: DT_FLOAT + } + } + } + attr { + name: "strides" + type: "list(int)" + } + attr { + name: "use_cudnn_on_gpu" + type: "bool" + default_value { + b: true + } + } + attr { + name: "padding" + type: "string" + allowed_values { + list { + s: "SAME" + s: "VALID" + } + } + } + attr { + name: "data_format" + type: "string" + default_value { + s: "NHWC" + } + allowed_values { + list { + s: "NHWC" + s: "NCHW" + } + } + } +} op { name: "Conv2DBackpropFilter" input_arg { @@ -13655,6 +13800,7 @@ op { allowed_values { list { type: DT_HALF + type: DT_BFLOAT16 type: DT_FLOAT } } @@ -13693,6 +13839,18 @@ op { } } } + attr { + name: "dilations" + type: "list(int)" + default_value { + list { + i: 1 + i: 1 + i: 1 + i: 1 + } + } + } } op { name: "Conv2DBackpropFilter" @@ -13720,6 +13878,7 @@ op { type: DT_HALF type: DT_BFLOAT16 type: DT_FLOAT + type: DT_DOUBLE } } } @@ -13818,6 +13977,15 @@ op { list { s: "SAME" s: "VALID" + s: "EXPLICIT" + } + } + } + attr { + name: "explicit_paddings" + type: "list(int)" + default_value { + list { } } } @@ -14063,6 +14231,92 @@ op { } } } +op { + name: "Conv2DBackpropInput" + input_arg { + name: "input_sizes" + type: DT_INT32 + } + input_arg { + name: "filter" + type_attr: "T" + } + input_arg { + name: "out_backprop" + type_attr: "T" + } + output_arg { + name: "output" + type_attr: "T" + } + attr { + name: "T" + type: "type" + allowed_values { + list { + type: DT_HALF + type: DT_BFLOAT16 + type: DT_FLOAT + type: DT_DOUBLE + } + } + } + attr { + name: "strides" + type: "list(int)" + } + attr { + name: "use_cudnn_on_gpu" + type: "bool" + default_value { + b: true + } + } + attr { + name: "padding" + type: "string" + allowed_values { + list { + s: "SAME" + s: "VALID" + s: "EXPLICIT" + } + } + } + attr { + name: "explicit_paddings" + type: "list(int)" + default_value { + list { + } + } + } + attr { + name: "data_format" + type: "string" + default_value { + s: "NHWC" + } + allowed_values { + list { + s: "NHWC" + s: "NCHW" + } + } + } + attr { + name: "dilations" + type: "list(int)" + default_value { + list { + i: 1 + i: 1 + i: 1 + i: 1 + } + } + } +} op { name: "Conv3D" input_arg { diff --git a/tensorflow/core/ops/ops.pbtxt b/tensorflow/core/ops/ops.pbtxt index b4767d35d2..4a1b3a6f2d 100644 --- a/tensorflow/core/ops/ops.pbtxt +++ b/tensorflow/core/ops/ops.pbtxt @@ -5876,6 +5876,15 @@ op { list { s: "SAME" s: "VALID" + s: "EXPLICIT" + } + } + } + attr { + name: "explicit_paddings" + type: "list(int)" + default_value { + list { } } } @@ -5953,6 +5962,15 @@ op { list { s: "SAME" s: "VALID" + s: "EXPLICIT" + } + } + } + attr { + name: "explicit_paddings" + type: "list(int)" + default_value { + list { } } } @@ -6030,6 +6048,15 @@ op { list { s: "SAME" s: "VALID" + s: "EXPLICIT" + } + } + } + attr { + name: "explicit_paddings" + type: "list(int)" + default_value { + list { } } } -- GitLab From ae2aceda4f6630dbdaa6c9a4da6ba690f34f40de Mon Sep 17 00:00:00 2001 From: Ilango R Date: Wed, 9 Jan 2019 13:10:52 +0530 Subject: [PATCH 536/622] Fix astor url to pypi.python.org --- tensorflow/workspace.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/workspace.bzl b/tensorflow/workspace.bzl index 5b0f6d4c84..a7f3665a31 100755 --- a/tensorflow/workspace.bzl +++ b/tensorflow/workspace.bzl @@ -285,7 +285,7 @@ def tf_workspace(path_prefix = "", tf_repo_name = ""): system_build_file = clean_dep("//third_party/systemlibs:astor.BUILD"), urls = [ "https://mirror.bazel.build/pypi.python.org/packages/99/80/f9482277c919d28bebd85813c0a70117214149a96b08981b72b63240b84c/astor-0.7.1.tar.gz", - "https://files.pythonhosted.org/packages/99/80/f9482277c919d28bebd85813c0a70117214149a96b08981b72b63240b84c/astor-0.7.1.tar.gz", + "https://pypi.python.org/packages/99/80/f9482277c919d28bebd85813c0a70117214149a96b08981b72b63240b84c/astor-0.7.1.tar.gz", ], ) -- GitLab From 746a9750f49d98b975a4cd0ee8c0f353426ffbc0 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Wed, 9 Jan 2019 01:02:25 -0800 Subject: [PATCH 537/622] compat: Update forward compatibility horizon to 2019-01-09 PiperOrigin-RevId: 228471346 --- tensorflow/python/compat/compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/python/compat/compat.py b/tensorflow/python/compat/compat.py index 5008741d55..dfee972cab 100644 --- a/tensorflow/python/compat/compat.py +++ b/tensorflow/python/compat/compat.py @@ -27,7 +27,7 @@ import datetime from tensorflow.python.util import tf_contextlib from tensorflow.python.util.tf_export import tf_export -_FORWARD_COMPATIBILITY_HORIZON = datetime.date(2019, 1, 8) +_FORWARD_COMPATIBILITY_HORIZON = datetime.date(2019, 1, 9) @tf_export("compat.forward_compatible") -- GitLab From 1bc98108a3e835221318e019fef1642914dd5720 Mon Sep 17 00:00:00 2001 From: Adrian Kuegel Date: Wed, 9 Jan 2019 02:37:03 -0800 Subject: [PATCH 538/622] Allow adding extra parameters to a cloned computation. There is currently no way to clone a computation and add additional parameters to it, except if it is a fusion computation. In a future change this will be necessary, so we add the possibility to add such parameters via CloneWithReplacements(). PiperOrigin-RevId: 228483852 --- .../compiler/xla/service/hlo_computation.cc | 18 +++++++-- .../compiler/xla/service/hlo_computation.h | 4 ++ .../xla/service/hlo_computation_test.cc | 38 +++++++++++++++++++ 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/tensorflow/compiler/xla/service/hlo_computation.cc b/tensorflow/compiler/xla/service/hlo_computation.cc index 9e15722633..f9b64d12ae 100644 --- a/tensorflow/compiler/xla/service/hlo_computation.cc +++ b/tensorflow/compiler/xla/service/hlo_computation.cc @@ -846,7 +846,7 @@ std::unique_ptr HloComputation::Clone( return CloneWithReplacements( /*replacements=*/std::unordered_map>(), - context, suffix); + /*extra_parameters=*/{}, context, suffix); } std::unique_ptr HloComputation::CloneWithReplacementPairs( @@ -855,7 +855,8 @@ std::unique_ptr HloComputation::CloneWithReplacementPairs( std::unordered_map> replacements; replacements.emplace(std::move(r1)); - return CloneWithReplacements(std::move(replacements), context, suffix); + return CloneWithReplacements(std::move(replacements), /*extra_parameters=*/{}, + context, suffix); } std::unique_ptr HloComputation::CloneWithReplacementPairs( @@ -866,7 +867,8 @@ std::unique_ptr HloComputation::CloneWithReplacementPairs( replacements; replacements.emplace(std::move(r1)); replacements.emplace(std::move(r2)); - return CloneWithReplacements(std::move(replacements), context, suffix); + return CloneWithReplacements(std::move(replacements), /*extra_parameters=*/{}, + context, suffix); } std::unique_ptr HloComputation::CloneWithReplacementPairs( @@ -879,12 +881,14 @@ std::unique_ptr HloComputation::CloneWithReplacementPairs( replacements.emplace(std::move(r1)); replacements.emplace(std::move(r2)); replacements.emplace(std::move(r3)); - return CloneWithReplacements(std::move(replacements), context, suffix); + return CloneWithReplacements(std::move(replacements), /*extra_parameters=*/{}, + context, suffix); } std::unique_ptr HloComputation::CloneWithReplacements( std::unordered_map> replacements, + absl::Span extra_parameters, HloCloneContext* context, const string& suffix) { std::unique_ptr context_ptr; if (context == nullptr) { @@ -950,6 +954,12 @@ std::unique_ptr HloComputation::CloneWithReplacements( } std::vector> instructions; + // First add the extra parameters to 'instructions'. + for (const auto& instr : extra_parameters) { + CHECK_EQ(instr->opcode(), HloOpcode::kParameter) + << "Only parameter instructions are allowed in 'extra_parameters'"; + instructions.emplace_back(instr->Clone()); + } for (auto instr : postorder) { std::vector new_operands; for (auto operand : instr->operands()) { diff --git a/tensorflow/compiler/xla/service/hlo_computation.h b/tensorflow/compiler/xla/service/hlo_computation.h index a0ccbc583f..e6a1eb89cf 100644 --- a/tensorflow/compiler/xla/service/hlo_computation.h +++ b/tensorflow/compiler/xla/service/hlo_computation.h @@ -323,11 +323,15 @@ class HloComputation { // that's not already in the computation, it's cloned and added to the new // computation. // + // 'extra_parameters' allows to specify additional parameters that should be + // added to the computation. + // // All relevant instructions are cloned, *including* unique_ptr in the // `replacements` map. std::unique_ptr CloneWithReplacements( std::unordered_map> replacements, + absl::Span extra_parameters = {}, HloCloneContext* context = nullptr, const string& suffix = "clone"); // Convenience overloads for CloneWithReplacements. You want to do diff --git a/tensorflow/compiler/xla/service/hlo_computation_test.cc b/tensorflow/compiler/xla/service/hlo_computation_test.cc index 216d56b868..251c7bbec4 100644 --- a/tensorflow/compiler/xla/service/hlo_computation_test.cc +++ b/tensorflow/compiler/xla/service/hlo_computation_test.cc @@ -15,7 +15,10 @@ limitations under the License. #include "tensorflow/compiler/xla/service/hlo_computation.h" +#include #include +#include +#include #include "absl/container/flat_hash_set.h" #include "tensorflow/compiler/xla/literal.h" @@ -492,6 +495,41 @@ TEST_F(HloComputationTest, CloneWithControlDependency) { EXPECT_THAT(successors, ::testing::ElementsAre(cloned_add)); } +TEST_F(HloComputationTest, CloneWithReplacements) { + auto builder = HloComputation::Builder(TestName()); + Shape r0s64 = ShapeUtil::MakeShape(S64, {}); + Shape r0s32 = ShapeUtil::MakeShape(S32, {}); + Shape r0u32 = ShapeUtil::MakeShape(U32, {}); + auto param0 = builder.AddInstruction( + HloInstruction::CreateParameter(0, r0f32_, "p.0.lhs")); + auto param1 = builder.AddInstruction( + HloInstruction::CreateParameter(1, r0f32_, "p.0.rhs")); + auto param2 = + builder.AddInstruction(HloInstruction::CreateParameter(2, r0s64, "p.1")); + auto lt = builder.AddInstruction(HloInstruction::CreateBinary( + ShapeUtil::MakeShape(PRED, {}), HloOpcode::kLt, param0, param1)); + auto module = CreateNewVerifiedModule(); + auto computation = + module->AddEntryComputation(builder.Build(/*root_instruction=*/lt)); + std::unordered_map> + replacements; + replacements.emplace(param2, + HloInstruction::CreateParameter(2, r0s32, "p.1")); + auto param3 = HloInstruction::CreateParameter(3, r0u32, "p.2"); + std::vector extra_parameters{param3.get()}; + auto clone = computation->CloneWithReplacements(std::move(replacements), + extra_parameters); + ASSERT_EQ(clone->num_parameters(), 4); + EXPECT_TRUE( + ShapeUtil::Equal(clone->parameter_instruction(0)->shape(), r0f32_)); + EXPECT_TRUE( + ShapeUtil::Equal(clone->parameter_instruction(1)->shape(), r0f32_)); + EXPECT_TRUE( + ShapeUtil::Equal(clone->parameter_instruction(2)->shape(), r0s32)); + EXPECT_TRUE( + ShapeUtil::Equal(clone->parameter_instruction(3)->shape(), r0u32)); +} + TEST_F(HloComputationTest, Stringification) { const Shape s1 = ShapeUtil::MakeShape(F32, {5, 10}); const Shape s2 = ShapeUtil::MakeShape(F32, {20, 10}); -- GitLab From 94548dbeca41c3a94287bf9e506f876272904227 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Wed, 9 Jan 2019 02:49:25 -0800 Subject: [PATCH 539/622] Object based saved model: Wire __call__ attribute as a method in each user object class. PiperOrigin-RevId: 228485119 --- tensorflow/python/saved_model/load.py | 20 +++++++++- tensorflow/python/saved_model/load_test.py | 43 ++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/tensorflow/python/saved_model/load.py b/tensorflow/python/saved_model/load.py index 1ee1b69b03..bbb1485612 100644 --- a/tensorflow/python/saved_model/load.py +++ b/tensorflow/python/saved_model/load.py @@ -85,11 +85,17 @@ class _Loader(object): raise ValueError("Can't convert node %s to tensor" % (type(obj))) def _load_all(self): + """Load all saved objects and wire their properties.""" self._nodes = [self._recreate(proto) for proto in self._proto.nodes] # After creating the objects, construct the edges between the objects. for obj, object_proto in zip(self._nodes, self._proto.nodes): for reference in object_proto.children: setattr(obj, reference.local_name, self._nodes[reference.node_id]) + # Note: if an object has an attribute `__call__` add a class method + # that allows `obj()` syntax to work. This is done per-instance to + # allow `callable` to be used to find out if an object is callable. + if reference.local_name == "__call__": + setattr(type(obj), "__call__", _call_attribute) def _restore_checkpoint(self): variables_path = saved_model_utils.get_variables_path(self._export_dir) @@ -113,8 +119,16 @@ class _Loader(object): return factory[kind]() def _recreate_user_object(self, proto): + """Instantiates a SavedUserObject.""" del proto - return tracking.Checkpointable() + + # Note: each user object has its own class. This allows to make each one + # individually callable by adding a `__call__` method to the classes of + # the objects instances that have a `__call__` property. + class _UserObject(tracking.Checkpointable): + pass + + return _UserObject() def _recreate_asset(self, proto): filename = os.path.join( @@ -132,6 +146,10 @@ class _Loader(object): return variables.Variable(dummy_value, trainable=proto.trainable) +def _call_attribute(instance, *args, **kwargs): + return instance.__call__(*args, **kwargs) + + def _load_saved_object_graph_proto(filename): with file_io.FileIO(filename, "rb") as f: contents = f.read() diff --git a/tensorflow/python/saved_model/load_test.py b/tensorflow/python/saved_model/load_test.py index 35e384dbfc..f7020e4a45 100644 --- a/tensorflow/python/saved_model/load_test.py +++ b/tensorflow/python/saved_model/load_test.py @@ -269,6 +269,49 @@ class LoadTest(test.TestCase): grad = t.gradient(loss, [imported.weight, imported.bias]) self.assertAllClose(grad, [3.5, 1.0]) + def test_callable(self): + class M1(tracking.Checkpointable): + + @def_function.function( + input_signature=[tensor_spec.TensorSpec(None, dtypes.float32)]) + def __call__(self, x): + return x + + root = tracking.Checkpointable() + root.m1 = M1() + root.m2 = tracking.Checkpointable() + root.m2.__call__ = def_function.function( + input_signature=[tensor_spec.TensorSpec(None, dtypes.float32)])( + lambda x: x*3.0) + imported = self.cycle(root) + x = constant_op.constant(1.0) + + self.assertTrue(callable(imported.m1)) + self.assertAllEqual(root.m1(x), imported.m1(x)) + + # Note: `root.m2` was not callable since `__call__` attribute was set + # into the instance and not on the class. But after a serialization cycle + # that starts to work. + self.assertTrue(callable(imported.m2)) + self.assertAllEqual(root.m2.__call__(x), imported.m2(x)) + + # Verify that user objects without `__call__` attribute are not callable. + self.assertFalse(callable(imported)) + + def test_chain_callable(self): + func = def_function.function( + input_signature=[tensor_spec.TensorSpec(None, dtypes.float32)])( + lambda x: x*3.0) + root = tracking.Checkpointable() + root.__call__ = tracking.Checkpointable() + root.__call__.__call__ = tracking.Checkpointable() + root.__call__.__call__.__call__ = func + + imported = self.cycle(root) + self.assertTrue(callable(imported)) + x = constant_op.constant(1.0) + self.assertAllEqual(imported(x).numpy(), 3.0) + if __name__ == "__main__": test.main() -- GitLab From 2db3c2ed12f7871322448c3265b82e21fa50e6aa Mon Sep 17 00:00:00 2001 From: Tamara Norman Date: Wed, 9 Jan 2019 02:50:05 -0800 Subject: [PATCH 540/622] Alter cast conversion so returns valid data types and allows a positional name arg PiperOrigin-RevId: 228485190 --- .../tools/compatibility/tf_upgrade_v2.py | 11 +++++++++ .../tools/compatibility/tf_upgrade_v2_test.py | 24 ++++++++++++++++--- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/tensorflow/tools/compatibility/tf_upgrade_v2.py b/tensorflow/tools/compatibility/tf_upgrade_v2.py index 2dbbe27598..940b57891a 100644 --- a/tensorflow/tools/compatibility/tf_upgrade_v2.py +++ b/tensorflow/tools/compatibility/tf_upgrade_v2.py @@ -1213,10 +1213,21 @@ class TFAPIChangeSpec(ast_edits.APIChangeSpec): # Find out the dtype to cast to from the function name dtype_str = name[3:] + # Special cases where the full dtype is not given + if dtype_str == "float": + dtype_str = "float32" + elif dtype_str == "double": + dtype_str = "float64" new_arg = ast.keyword(arg="dtype", value=ast.Attribute(value=ast.Name(id="tf", ctx=ast.Load()), attr=dtype_str, ctx=ast.Load())) + # Ensures a valid transformation when a positional name arg is given + if len(node.args) == 2: + name_arg = ast.keyword(arg="name", + value=node.args[-1]) + node.args = node.args[:-1] + node.keywords.append(name_arg) # Python3 ast requires the args for the Attribute, but codegen will mess up # the arg order if we just set them to 0. diff --git a/tensorflow/tools/compatibility/tf_upgrade_v2_test.py b/tensorflow/tools/compatibility/tf_upgrade_v2_test.py index 80d86d7a2b..b2bdddf15e 100644 --- a/tensorflow/tools/compatibility/tf_upgrade_v2_test.py +++ b/tensorflow/tools/compatibility/tf_upgrade_v2_test.py @@ -861,9 +861,27 @@ tf.print('abc') self.assertIn(new_text, [expected_text1, expected_text2]) def testCast(self): - for dtype in ["int32", "int64", "float", "double", - "complex64", "complex128", "bfloat16"]: - text = "tf.to_%s(x, name='test')" % dtype + for (name, dtype) in [("int32", "int32"), + ("int64", "int64"), + ("float", "float32"), + ("double", "float64"), + ("complex64", "complex64"), + ("complex128", "complex128"), + ("bfloat16", "bfloat16")]: + text = "tf.to_%s(x, name='test')" % name + expected_text = "tf.cast(x, name='test', dtype=tf.%s)" % dtype + _, unused_report, unused_errors, new_text = self._upgrade(text) + self.assertEqual(expected_text, new_text) + + def testCastPositionalSecondArgument(self): + for (name, dtype) in [("int32", "int32"), + ("int64", "int64"), + ("float", "float32"), + ("double", "float64"), + ("complex64", "complex64"), + ("complex128", "complex128"), + ("bfloat16", "bfloat16")]: + text = "tf.to_%s(x, 'test')" % name expected_text = "tf.cast(x, name='test', dtype=tf.%s)" % dtype _, unused_report, unused_errors, new_text = self._upgrade(text) self.assertEqual(expected_text, new_text) -- GitLab From 41b0fb94457c0de1688ed595e5ff2ada068fc93f Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Wed, 9 Jan 2019 03:00:27 -0800 Subject: [PATCH 541/622] Optimize transpose_conv. PiperOrigin-RevId: 228486143 --- .../internal/optimized/optimized_ops.h | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/tensorflow/lite/kernels/internal/optimized/optimized_ops.h b/tensorflow/lite/kernels/internal/optimized/optimized_ops.h index 8f09fab4ca..ac68757b06 100644 --- a/tensorflow/lite/kernels/internal/optimized/optimized_ops.h +++ b/tensorflow/lite/kernels/internal/optimized/optimized_ops.h @@ -6079,7 +6079,27 @@ inline void TransposeConv( const float* filter_data, const RuntimeShape& output_shape, float* output_data, const RuntimeShape& im2col_shape, float* im2col_data) { gemmlowp::ScopedProfilingLabel label("TransposeConv"); - + // The complexity of the reference implementation is input.flat_size() * + // filter.flat_size() / in_channel. + // + // While the complexity of im2col->gemm + // implmentation is batch * output_height * output_width * + // (filter.flat_size() / out_channel)^2 * out_channel. + // + // so if input.flat_size() * out_channel^2 is much smaller than + // output.flat_size() * filter.size() * in_channel we should fall back to the + // reference implementation. + // + // TODO(b/122331966): optimize the intuitive implementation. + const int out_channel = output_shape.Dims(3); + const int in_channel = input_shape.Dims(3); + if ((input_shape.FlatSize() * out_channel * out_channel * 4) < + (filter_shape.FlatSize() * output_shape.FlatSize() * in_channel)) { + reference_ops::TransposeConv(params, input_shape, input_data, filter_shape, + filter_data, output_shape, output_data, + im2col_shape, im2col_data); + return; + } // Note we could use transposed weights with forward conv for unstrided // cases. But we are already getting good performance with this code as-is. TFLITE_DCHECK(im2col_data); -- GitLab From 964e3d0bd8bcfbfc8bcc1de16f32df8190cd7b75 Mon Sep 17 00:00:00 2001 From: Tamara Norman Date: Wed, 9 Jan 2019 03:29:29 -0800 Subject: [PATCH 542/622] Add conversion for tf.image.resize_* methods PiperOrigin-RevId: 228489020 --- tensorflow/tools/compatibility/renames_v2.py | 5 +- .../tools/compatibility/tf_upgrade_v2.py | 51 +++++++++++++++++++ .../tools/compatibility/tf_upgrade_v2_test.py | 18 +++++++ 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/tensorflow/tools/compatibility/renames_v2.py b/tensorflow/tools/compatibility/renames_v2.py index 5ff8a86b91..e9085e94b3 100644 --- a/tensorflow/tools/compatibility/renames_v2.py +++ b/tensorflow/tools/compatibility/renames_v2.py @@ -141,8 +141,8 @@ renames = { 'tf.diag': 'tf.linalg.tensor_diag', 'tf.diag_part': 'tf.linalg.tensor_diag_part', 'tf.digamma': 'tf.math.digamma', - 'tf.dimension_at_index': 'tf.compat.v1.dimension_at_index', - 'tf.dimension_value': 'tf.compat.v1.dimension_value', + 'tf.dimension_at_index': 'tf.compat.dimension_at_index', + 'tf.dimension_value': 'tf.compat.dimension_value', 'tf.disable_eager_execution': 'tf.compat.v1.disable_eager_execution', 'tf.disable_resource_variables': 'tf.compat.v1.disable_resource_variables', 'tf.disable_v2_batch_normalization': 'tf.compat.v1.disable_v2_batch_normalization', @@ -221,7 +221,6 @@ renames = { 'tf.image.resize_area': 'tf.compat.v1.image.resize_area', 'tf.image.resize_bicubic': 'tf.compat.v1.image.resize_bicubic', 'tf.image.resize_bilinear': 'tf.compat.v1.image.resize_bilinear', - 'tf.image.resize_images': 'tf.compat.v1.image.resize_images', 'tf.image.resize_nearest_neighbor': 'tf.compat.v1.image.resize_nearest_neighbor', 'tf.image.transpose_image': 'tf.compat.v1.image.transpose_image', 'tf.initialize_all_tables': 'tf.compat.v1.initialize_all_tables', diff --git a/tensorflow/tools/compatibility/tf_upgrade_v2.py b/tensorflow/tools/compatibility/tf_upgrade_v2.py index 940b57891a..e93699fdbf 100644 --- a/tensorflow/tools/compatibility/tf_upgrade_v2.py +++ b/tensorflow/tools/compatibility/tf_upgrade_v2.py @@ -594,6 +594,8 @@ class TFAPIChangeSpec(ast_edits.APIChangeSpec): "tf.compat.v1.initializers.random_normal", "tf.truncated_normal_initializer": "tf.compat.v1.initializers.truncated_normal", + "tf.image.resize_images": + "tf.image.resize", } # pylint: enable=line-too-long @@ -725,6 +727,11 @@ class TFAPIChangeSpec(ast_edits.APIChangeSpec): "tf.to_int64": self._cast_transformer, "tf.nn.softmax_cross_entropy_with_logits": self._softmax_cross_entropy_with_logits_transformer, + "tf.image.resize_area": self._image_resize_transformer, + "tf.image.resize_bicubic": self._image_resize_transformer, + "tf.image.resize_bilinear": self._image_resize_transformer, + "tf.image.resize_nearest_neighbor": self._image_resize_transformer, + } decay_function_comment = ( @@ -1292,3 +1299,47 @@ class TFAPIChangeSpec(ast_edits.APIChangeSpec): logs.append((node.lineno, node.col_offset, "Added keyword argument batch_dims=-1 to tf.batch_gather.")) return node + + @staticmethod + def _image_resize_transformer(parent, node, full_name, name, logs, errors): + """Transforms image.resize_* to image.resize(..., method=*, ...).""" + + resize_method = name[7:].upper() + new_arg = ast.keyword(arg="method", + value=ast.Attribute( + value=ast.Attribute( + value=ast.Attribute( + value=ast.Name(id="tf", ctx=ast.Load()), + attr="image", ctx=ast.Load()), + attr="ResizeMethod", ctx=ast.Load()), + attr=resize_method, ctx=ast.Load())) + + # Ensures a valid transformation when a positional name arg is given + if len(node.args) == 4: + pos_arg = ast.keyword(arg="preserve_aspect_ratio", + value=node.args[-1]) + node.args = node.args[:-1] + node.keywords.append(pos_arg) + if len(node.args) == 3: + pos_arg = ast.keyword(arg="align_corners", + value=node.args[-1]) + node.args = node.args[:-1] + node.keywords.append(pos_arg) + + # Python3 ast requires the args for the Attribute, but codegen will mess up + # the arg order if we just set them to 0. + new_arg.value.lineno = node.lineno + new_arg.value.col_offset = node.col_offset+100 + + node.keywords.append(new_arg) + if isinstance(node.func, ast.Attribute): + node.func.attr = "resize" + else: + assert isinstance(node.func, ast.Name) + node.func.id = "resize" + + logs.append((node.lineno, node.col_offset, + "Changed %s call to tf.image.resize(..., " + "method=tf.image.ResizeMethod.%s)." % (full_name, + resize_method))) + return node diff --git a/tensorflow/tools/compatibility/tf_upgrade_v2_test.py b/tensorflow/tools/compatibility/tf_upgrade_v2_test.py index b2bdddf15e..c1882afd5c 100644 --- a/tensorflow/tools/compatibility/tf_upgrade_v2_test.py +++ b/tensorflow/tools/compatibility/tf_upgrade_v2_test.py @@ -886,6 +886,24 @@ tf.print('abc') _, unused_report, unused_errors, new_text = self._upgrade(text) self.assertEqual(expected_text, new_text) + def testImageResize(self): + for method in ["bilinear", "area", "bicubic", "nearest_neighbor"]: + text = "tf.image.resize_%s(i, s)" % method + expected_text = ("tf.image.resize(i, s, " + "method=tf.image.ResizeMethod.%s)" % method.upper()) + _, unused_report, unused_errors, new_text = self._upgrade(text) + self.assertEqual(expected_text, new_text) + + def testImageResizeExtraPositionalArgs(self): + for method in ["bilinear", "area", "bicubic", "nearest_neighbor"]: + text = "tf.image.resize_%s(i, s, a, p)" % method + expected_text = ["tf.image.resize(i, s, ", "align_corners=a, ", + "preserve_aspect_ratio=p, ", + "method=tf.image.ResizeMethod.%s)" % method.upper()] + _, unused_report, unused_errors, new_text = self._upgrade(text) + for s in expected_text: + self.assertIn(s, new_text) + class TestUpgradeFiles(test_util.TensorFlowTestCase): -- GitLab From 6e6d7aad7300294425933bd053afce55107ccd35 Mon Sep 17 00:00:00 2001 From: Tamara Norman Date: Wed, 9 Jan 2019 04:31:58 -0800 Subject: [PATCH 543/622] Make tf.cond remove the strict arg and emit a warning to say it has been set to true. Adds additional changes which ensure this warning is emitted if the original argument was positional. PiperOrigin-RevId: 228495150 --- tensorflow/tools/compatibility/ast_edits.py | 8 +++---- tensorflow/tools/compatibility/reorders_v2.py | 1 + .../tools/compatibility/tf_upgrade_v2.py | 21 ++++++++++++------- .../tools/compatibility/tf_upgrade_v2_test.py | 8 +++++++ 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/tensorflow/tools/compatibility/ast_edits.py b/tensorflow/tools/compatibility/ast_edits.py index 859fd95314..2254c223ce 100644 --- a/tensorflow/tools/compatibility/ast_edits.py +++ b/tensorflow/tools/compatibility/ast_edits.py @@ -213,7 +213,7 @@ class _PastaEditVisitor(ast.NodeVisitor): return False def _maybe_add_call_warning(self, node, full_name, name): - """Print a warning when specific functions are called. + """Print a warning when specific functions are called with selected args. The function _print_warning_for_function matches the full name of the called function, e.g., tf.foo.bar(). This function matches the function name that @@ -241,13 +241,13 @@ class _PastaEditVisitor(ast.NodeVisitor): full_name, name) used_args = [kw.arg for kw in node.keywords] - for arg, warning in arg_warnings.items(): - if arg in used_args: + for (kwarg, arg), warning in arg_warnings.items(): + if kwarg in used_args or len(node.args) > arg: warned = True warning_message = warning.replace("", full_name or name) self.add_error(node.lineno, node.col_offset, "%s called with %s argument requires manual check: %s." % - (full_name or name, arg, warning_message)) + (full_name or name, kwarg, warning_message)) return warned diff --git a/tensorflow/tools/compatibility/reorders_v2.py b/tensorflow/tools/compatibility/reorders_v2.py index 5c11388516..f9b0e3f9d8 100644 --- a/tensorflow/tools/compatibility/reorders_v2.py +++ b/tensorflow/tools/compatibility/reorders_v2.py @@ -31,6 +31,7 @@ reorders = { 'tf.batch_gather': ['params', 'indices', 'name'], 'tf.batch_to_space': ['input', 'crops', 'block_size', 'name'], 'tf.boolean_mask': ['tensor', 'mask', 'name', 'axis'], + 'tf.cond': ['pred', 'true_fn', 'false_fn', 'strict', 'name', 'fn1', 'fn2'], 'tf.confusion_matrix': ['labels', 'predictions', 'num_classes', 'dtype', 'name', 'weights'], 'tf.convert_to_tensor': ['value', 'dtype', 'name', 'preferred_dtype'], 'tf.decode_csv': ['records', 'record_defaults', 'field_delim', 'use_quote_delim', 'name', 'na_value', 'select_cols'], diff --git a/tensorflow/tools/compatibility/tf_upgrade_v2.py b/tensorflow/tools/compatibility/tf_upgrade_v2.py index e93699fdbf..2c56cbf36b 100644 --- a/tensorflow/tools/compatibility/tf_upgrade_v2.py +++ b/tensorflow/tools/compatibility/tf_upgrade_v2.py @@ -50,6 +50,11 @@ class TFAPIChangeSpec(ast_edits.APIChangeSpec): "*.compute_gradients": { "colocate_gradients_with_ops": None, }, + "tf.cond": { + "strict": None, + "fn1": "true_fn", + "fn2": "false_fn" + }, "tf.argmin": { "dimension": "axis", }, @@ -620,6 +625,7 @@ class TFAPIChangeSpec(ast_edits.APIChangeSpec): "tf.argmin", "tf.batch_gather", "tf.batch_to_space", + "tf.cond", "tf.nn.space_to_batch", "tf.boolean_mask", "tf.convert_to_tensor", @@ -847,10 +853,6 @@ class TFAPIChangeSpec(ast_edits.APIChangeSpec): assert_return_type_comment, "tf.assert_rank": assert_rank_comment, - "tf.cond": - "tf.cond no longer takes 'strict'. " - "Now 'strict' defaults to True." - "fn1/fn2 arguments are replaced by true_fn/false_fn.", "tf.debugging.assert_equal": assert_return_type_comment, "tf.debugging.assert_greater": @@ -1146,23 +1148,28 @@ class TFAPIChangeSpec(ast_edits.APIChangeSpec): # Warnings that are emitted only if a specific arg is found. self.function_arg_warnings = { "tf.gradients": { - "colocate_gradients_with_ops": + ("colocate_gradients_with_ops", 4): "tf.gradients no longer takes " "'colocate_gradients_with_ops' argument, it behaves as if it " "was set to True.", }, "*.minimize": { - "colocate_gradients_with_ops": + ("colocate_gradients_with_ops", 5): "Optimizer.minimize no longer takes " "'colocate_gradients_with_ops' argument, it behaves as if it " "was set to True.", }, "*.compute_gradients": { - "colocate_gradients_with_ops": + ("colocate_gradients_with_ops", 4): "Optimizer.compute_gradients no " "longer takes 'colocate_gradients_with_ops' argument, it " "behaves as if it was set to True.", }, + "tf.cond": { + ("strict", 3): + "tf.cond no longer takes 'strict' argument, it behaves as " + "if was set to True." + }, } self.symbol_renames = { diff --git a/tensorflow/tools/compatibility/tf_upgrade_v2_test.py b/tensorflow/tools/compatibility/tf_upgrade_v2_test.py index c1882afd5c..0a85cb39c8 100644 --- a/tensorflow/tools/compatibility/tf_upgrade_v2_test.py +++ b/tensorflow/tools/compatibility/tf_upgrade_v2_test.py @@ -904,6 +904,14 @@ tf.print('abc') for s in expected_text: self.assertIn(s, new_text) + def testCond(self): + text = "tf.cond(a, b, c, True)" + expected_text = "tf.cond(pred=a, true_fn=b, false_fn=c)" + _, unused_report, errors, new_text = self._upgrade(text) + self.assertEqual(expected_text, new_text) + self.assertIn("tf.cond", errors[0]) + self.assertIn("requires manual check", errors[0]) + class TestUpgradeFiles(test_util.TensorFlowTestCase): -- GitLab From b5fdd811998d495950c4b736879454be39acf163 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Wed, 9 Jan 2019 07:08:38 -0800 Subject: [PATCH 544/622] Stop using set for signatures comparison. Sets internal hashing mechanism makes it impossible to use any kind of structures (lists, etc.). Also do some refactoring. PiperOrigin-RevId: 228511638 --- tensorflow/python/eager/def_function.py | 16 +++++++++------ tensorflow/python/eager/function.py | 27 ++++++++++++++++++------- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/tensorflow/python/eager/def_function.py b/tensorflow/python/eager/def_function.py index aa4f20df49..4c22a12c1c 100644 --- a/tensorflow/python/eager/def_function.py +++ b/tensorflow/python/eager/def_function.py @@ -446,10 +446,12 @@ class PolymorphicFunction(object): @property def _cached_input_signatures(self): """All input signatures used to call this PolymorphicFunction.""" - seen = set() - # Preserves signature ordering rather than returning a set() so that we - # don't need to re-sort signatures later to work around Python 2's set - # nondeterminism. + seen = list() + # We are using a list so that: + # - the returned collection is deterministic, and + # - we can use a custom equality operator (is_same_structure). + # This is run only at serialization time on likely very small inputs so we + # are not concerned about O(n^2) runtime. # pylint: disable=protected-access concrete_functions = [] if self._stateful_fn: @@ -458,9 +460,11 @@ class PolymorphicFunction(object): concrete_functions.extend(self._stateless_fn._function_cache.values()) for concrete_function in concrete_functions: signature = concrete_function._python_call_signature - if signature not in seen: + equal_to_signature = functools.partial( + function_lib.is_same_structure, signature, check_values=True) + if not any(equal_to_signature(s) for s in seen): yield signature - seen.add(signature) + seen.append(signature) # pylint: enable=protected-access def get_concrete_function(self, *args, **kwargs): diff --git a/tensorflow/python/eager/function.py b/tensorflow/python/eager/function.py index 05fccfbcd9..67c633726f 100644 --- a/tensorflow/python/eager/function.py +++ b/tensorflow/python/eager/function.py @@ -74,6 +74,24 @@ CacheKey = collections.namedtuple("CacheKey", [ ]) +def is_same_structure(structure1, + structure2, + check_values=False): + """Check two structures for equality, optionally of types and of values.""" + try: + nest.assert_same_structure(structure1, structure2) + except (ValueError, TypeError): + return False + if check_values: + flattened1 = nest.flatten(structure1) + flattened2 = nest.flatten(structure2) + # First check the types to avoid AttributeErrors. + if any(type(f1) != type(f2) for f1, f2 in zip(flattened1, flattened2)): + return False + return flattened1 == flattened2 + return True + + def _parse_func_attrs(attributes): """Convert the keyword arguments into function_def attributes. @@ -919,10 +937,7 @@ class FunctionSpec(object): else: assert not kwargs signature_relevant_inputs = inputs[:len(self.input_signature)] - try: - nest.assert_same_structure(self.input_signature, - signature_relevant_inputs) - except (ValueError, TypeError): + if not is_same_structure(self.input_signature, signature_relevant_inputs): raise ValueError("Structure of Python function inputs does not match " "input_signature.") signature_inputs_flat = nest.flatten(signature_relevant_inputs) @@ -1049,9 +1064,7 @@ class PolymorphicFunction(object): "input_signature is provided.") if args: # If args are provided, they must match the input signature. - try: - nest.assert_same_structure(self._input_signature, args) - except (ValueError, TypeError): + if not is_same_structure(self._input_signature, args): raise ValueError("Structure of Python function inputs does not match " "input_signature.") flat_inputs = nest.flatten(args) -- GitLab From 433d47e39ce5e1c8c478393e7f5d2917c7df0fa7 Mon Sep 17 00:00:00 2001 From: Shashi Shekhar Date: Wed, 9 Jan 2019 08:00:10 -0800 Subject: [PATCH 545/622] Add symmetric quantization support to the accuracy tool. PiperOrigin-RevId: 228517966 --- .../ilsvrc/imagenet_model_evaluator.cc | 26 ++++------ .../accuracy/ilsvrc/imagenet_topk_eval.cc | 9 +++- .../ilsvrc/inception_preprocessing.cc | 42 +++++++++------ .../accuracy/ilsvrc/inception_preprocessing.h | 52 ++++++++++++++----- .../ilsvrc/inception_preprocessing_test.cc | 41 ++++++++++++--- 5 files changed, 119 insertions(+), 51 deletions(-) diff --git a/tensorflow/lite/tools/accuracy/ilsvrc/imagenet_model_evaluator.cc b/tensorflow/lite/tools/accuracy/ilsvrc/imagenet_model_evaluator.cc index 9a74e221c1..129747fe4d 100644 --- a/tensorflow/lite/tools/accuracy/ilsvrc/imagenet_model_evaluator.cc +++ b/tensorflow/lite/tools/accuracy/ilsvrc/imagenet_model_evaluator.cc @@ -22,6 +22,12 @@ limitations under the License. #include "absl/memory/memory.h" #include "tensorflow/cc/framework/scope.h" +#include "tensorflow/core/lib/core/blocking_counter.h" +#include "tensorflow/core/lib/core/threadpool.h" +#include "tensorflow/core/platform/init_main.h" +#include "tensorflow/core/platform/mutex.h" +#include "tensorflow/core/public/session.h" +#include "tensorflow/core/util/command_line_flags.h" #include "tensorflow/lite/tools/accuracy/eval_pipeline.h" #include "tensorflow/lite/tools/accuracy/eval_pipeline_builder.h" #include "tensorflow/lite/tools/accuracy/file_reader_stage.h" @@ -29,12 +35,6 @@ limitations under the License. #include "tensorflow/lite/tools/accuracy/ilsvrc/inception_preprocessing.h" #include "tensorflow/lite/tools/accuracy/run_tflite_model_stage.h" #include "tensorflow/lite/tools/accuracy/utils.h" -#include "tensorflow/core/lib/core/blocking_counter.h" -#include "tensorflow/core/lib/core/threadpool.h" -#include "tensorflow/core/platform/init_main.h" -#include "tensorflow/core/platform/mutex.h" -#include "tensorflow/core/public/session.h" -#include "tensorflow/core/util/command_line_flags.h" namespace { using tensorflow::string; @@ -185,21 +185,17 @@ Status EvaluateModelForShard(const uint64_t shard_id, const TensorShape& input_shape = model_info.input_shapes[0]; const int image_height = input_shape.dim_size(1); const int image_width = input_shape.dim_size(2); - const bool is_quantized = (model_info.input_types[0] == DT_UINT8); RunTFLiteModelStage::Params tfl_model_params; tfl_model_params.model_file_path = params.model_file_path; - if (is_quantized) { - tfl_model_params.input_type = {DT_UINT8}; - tfl_model_params.output_type = {DT_UINT8}; - } else { - tfl_model_params.input_type = {DT_FLOAT}; - tfl_model_params.output_type = {DT_FLOAT}; - } + + tfl_model_params.input_type = {model_info.input_types[0]}; + tfl_model_params.output_type = {model_info.input_types[0]}; Scope root = Scope::NewRootScope(); FileReaderStage reader; - InceptionPreprocessingStage inc(image_height, image_width, is_quantized); + InceptionPreprocessingStage inc(image_height, image_width, + model_info.input_types[0]); RunTFLiteModelStage tfl_model_stage(tfl_model_params); EvalPipelineBuilder builder; diff --git a/tensorflow/lite/tools/accuracy/ilsvrc/imagenet_topk_eval.cc b/tensorflow/lite/tools/accuracy/ilsvrc/imagenet_topk_eval.cc index 2b086cdf70..f5642d52a8 100644 --- a/tensorflow/lite/tools/accuracy/ilsvrc/imagenet_topk_eval.cc +++ b/tensorflow/lite/tools/accuracy/ilsvrc/imagenet_topk_eval.cc @@ -67,11 +67,18 @@ Status ImagenetTopKAccuracy::ComputeEval( for (size_t i = 0; i < probs.size(); i++) { probabilities.push_back(probs(i)); } - } else { + } else if (output.dtype() == DT_UINT8) { auto probs = output.flat(); for (size_t i = 0; i < probs.size(); i++) { probabilities.push_back(probs(i)); } + } else if (output.dtype() == DT_INT8) { + auto probs = output.flat(); + for (size_t i = 0; i < probs.size(); i++) { + probabilities.push_back(probs(i)); + } + } else { + return errors::InvalidArgument("Invalid datatype"); } CHECK_EQ(kNumCategories, probabilities.size()); diff --git a/tensorflow/lite/tools/accuracy/ilsvrc/inception_preprocessing.cc b/tensorflow/lite/tools/accuracy/ilsvrc/inception_preprocessing.cc index 9a889f0dd8..04b6cb7558 100644 --- a/tensorflow/lite/tools/accuracy/ilsvrc/inception_preprocessing.cc +++ b/tensorflow/lite/tools/accuracy/ilsvrc/inception_preprocessing.cc @@ -57,23 +57,33 @@ void InceptionPreprocessingStage::AddToGraph(const Scope& scope, tensorflow::Output cropped_image; CentralCropImage(s, decoded_jpeg, params_.cropping_fraction, &cropped_image); auto dims_expander = ops::ExpandDims(s, cropped_image, 0); - auto resized_image = ops::ResizeBilinear( - s, dims_expander, - ops::Const(s.WithOpName("size"), {image_height_, image_width_})); - if (is_quantized_) { - this->stage_output_ = - ops::Cast(s.WithOpName(output_name()), resized_image, DT_UINT8); - } else { - auto squeezed_image = ops::Squeeze(s, resized_image); - auto normalized_image = - ops::Div(s, - ops::Sub(s, squeezed_image, - {params_.input_means[0], params_.input_means[1], - params_.input_means[2]}), - {params_.scale}); - this->stage_output_ = - ops::ExpandDims(s.WithOpName(output_name()), normalized_image, {0}); + auto resized_image = + ops::ResizeBilinear(s.WithOpName("resize"), dims_expander, + ops::Const(s, {image_height_, image_width_})); + + ::tensorflow::Output preprocessed_image = resized_image; + + if (!params_.input_means.empty()) { + preprocessed_image = + ops::Sub(s.WithOpName("sub"), preprocessed_image, + {params_.input_means[0], params_.input_means[1], + params_.input_means[2]}); + } + + if (std::abs(params_.scale) > 1e-7f) { + auto squeezed_image = ops::Squeeze(s, preprocessed_image); + preprocessed_image = ops::Div(s, squeezed_image, {params_.scale}); + preprocessed_image = ops::ExpandDims(s, preprocessed_image, {0}); } + + // Cast the output from float to output datatype. + if (output_datatype_ != DT_FLOAT) { + preprocessed_image = + ops::Cast(s.WithOpName("cast"), preprocessed_image, output_datatype_); + } + + this->stage_output_ = + ops::Identity(s.WithOpName(output_name()), preprocessed_image); } } // namespace metrics diff --git a/tensorflow/lite/tools/accuracy/ilsvrc/inception_preprocessing.h b/tensorflow/lite/tools/accuracy/ilsvrc/inception_preprocessing.h index 4a1d3ce476..371feb3e76 100644 --- a/tensorflow/lite/tools/accuracy/ilsvrc/inception_preprocessing.h +++ b/tensorflow/lite/tools/accuracy/ilsvrc/inception_preprocessing.h @@ -13,14 +13,14 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#ifndef TENSORFLOW_LITE_TOOLS_ACCURACY_INCEPTION_PREPROCESSING_H_ -#define TENSORFLOW_LITE_TOOLS_ACCURACY_INCEPTION_PREPROCESSING_H_ +#ifndef TENSORFLOW_LITE_TOOLS_ACCURACY_ILSVRC_INCEPTION_PREPROCESSING_H_ +#define TENSORFLOW_LITE_TOOLS_ACCURACY_ILSVRC_INCEPTION_PREPROCESSING_H_ #include -#include "tensorflow/lite/tools/accuracy/stage.h" #include "tensorflow/core/framework/tensor.h" #include "tensorflow/core/lib/core/status.h" +#include "tensorflow/lite/tools/accuracy/stage.h" namespace tensorflow { namespace metrics { @@ -31,28 +31,53 @@ namespace metrics { // shape {1, image_height, image_width, 3}, where 3 is the number of channels. class InceptionPreprocessingStage : public Stage { public: + // Preprocessing params that govern scaling and normalization of channels of + // the image. struct Params { + // Input means are subtracted from each channel. + // In case of an empty vector this is skipped. std::vector input_means; + // Scale is used to divide the input. + // A scale of 0 means divison is skipped. float scale; double cropping_fraction; }; - static Params DefaultParams() { - return {.input_means = {127.5, 127.5, 127.5}, - .scale = 127.5, - .cropping_fraction = 0.875}; + // Default preprocessing for inception stage based on |output_type| + static Params DefaultParamsForType(DataType output_type) { + const float kCroppingFraction = 0.875; + Params params = {}; + params.cropping_fraction = kCroppingFraction; + if (output_type == DT_UINT8) { + } else if (output_type == DT_INT8) { + params.input_means = {128.0, 128.0, 128.0}; + } else { + // Assume floating point preprocessing. + params.input_means = {127.5, 127.5, 127.5}; + params.scale = 127.5; + } + return params; + } + + // Creates a new preprocessing stage object with provided |image_width| + // |image_height| as the size of output image. + // |output_datatype| is the datatype of output of the stage. + InceptionPreprocessingStage(int image_width, int image_height, + DataType output_datatype) + : output_datatype_(output_datatype), + image_width_(image_width), + image_height_(image_height) { + params_ = DefaultParamsForType(output_datatype); } // Creates a new preprocessing stage object with provided |image_width| // |image_height| as the size of output image. - // If |is_quantized| is set to true then |params| is ignored since quantized - // images don't go through any preprocessing. + // |output_datatype| is the datatype of output of the stage. InceptionPreprocessingStage(int image_width, int image_height, - bool is_quantized, - Params params = DefaultParams()) - : image_width_(image_width), + DataType output_datatype, Params params) + : output_datatype_(output_datatype), + image_width_(image_width), image_height_(image_height), - is_quantized_(is_quantized), params_(std::move(params)) {} string name() const override { return "stage_inception_preprocess"; } @@ -63,6 +88,7 @@ class InceptionPreprocessingStage : public Stage { void AddToGraph(const Scope& scope, const Input& input) override; private: + DataType output_datatype_; int image_width_; int image_height_; bool is_quantized_; diff --git a/tensorflow/lite/tools/accuracy/ilsvrc/inception_preprocessing_test.cc b/tensorflow/lite/tools/accuracy/ilsvrc/inception_preprocessing_test.cc index 5d0e01d7d1..f88847035f 100644 --- a/tensorflow/lite/tools/accuracy/ilsvrc/inception_preprocessing_test.cc +++ b/tensorflow/lite/tools/accuracy/ilsvrc/inception_preprocessing_test.cc @@ -17,10 +17,10 @@ limitations under the License. #include #include -#include "tensorflow/lite/tools/accuracy/ilsvrc/inception_preprocessing.h" #include "tensorflow/core/platform/init_main.h" #include "tensorflow/core/public/session.h" #include "tensorflow/core/util/command_line_flags.h" +#include "tensorflow/lite/tools/accuracy/ilsvrc/inception_preprocessing.h" namespace { tensorflow::string* g_test_image_file = nullptr; @@ -48,7 +48,7 @@ Status GetContents(const string& filename, string* output) { } } -TEST(InceptionPreprocessingTest, TestImagePreprocessQuantized) { +TEST(InceptionPreprocessingTest, TestImagePreprocessUInt8Quantized) { ASSERT_TRUE(g_test_image_file != nullptr); string image_contents; string image_path = *g_test_image_file; @@ -56,8 +56,8 @@ TEST(InceptionPreprocessingTest, TestImagePreprocessQuantized) { ASSERT_TRUE(status.ok()) << status.error_message(); const int width = 224; const int height = 224; - const bool is_quantized = true; - InceptionPreprocessingStage preprocess_stage(width, height, is_quantized); + auto params = InceptionPreprocessingStage::DefaultParamsForType(DT_UINT8); + InceptionPreprocessingStage preprocess_stage(width, height, DT_UINT8, params); Scope scope = Scope::NewRootScope(); preprocess_stage.AddToGraph(scope, image_contents); TF_CHECK_OK(scope.status()); @@ -77,6 +77,35 @@ TEST(InceptionPreprocessingTest, TestImagePreprocessQuantized) { EXPECT_TRUE(outputs[0].shape().IsSameSize({1, 224, 224, 3})); } +TEST(InceptionPreprocessingTest, TestImagePreprocessInt8Quantized) { + ASSERT_TRUE(g_test_image_file != nullptr); + string image_contents; + string image_path = *g_test_image_file; + auto status = GetContents(image_path, &image_contents); + ASSERT_TRUE(status.ok()) << status.error_message(); + const int width = 224; + const int height = 224; + auto params = InceptionPreprocessingStage::DefaultParamsForType(DT_INT8); + InceptionPreprocessingStage preprocess_stage(width, height, DT_INT8, params); + Scope scope = Scope::NewRootScope(); + preprocess_stage.AddToGraph(scope, image_contents); + TF_CHECK_OK(scope.status()); + + GraphDef graph_def; + TF_CHECK_OK(scope.ToGraphDef(&graph_def)); + std::unique_ptr session(NewSession(SessionOptions())); + TF_CHECK_OK(session->Create(graph_def)); + std::vector outputs; + auto run_status = + session->Run({}, /*inputs*/ + {preprocess_stage.output_name()}, {}, /*target node names */ + &outputs); + TF_CHECK_OK(run_status); + EXPECT_EQ(1, outputs.size()); + EXPECT_EQ(DT_INT8, outputs[0].dtype()); + EXPECT_TRUE(outputs[0].shape().IsSameSize({1, 224, 224, 3})); +} + TEST(InceptionPreprocessingTest, TestImagePreprocessFloat) { ASSERT_TRUE(g_test_image_file != nullptr); string image_contents; @@ -85,8 +114,8 @@ TEST(InceptionPreprocessingTest, TestImagePreprocessFloat) { ASSERT_TRUE(status.ok()) << status.error_message(); const int width = 224; const int height = 224; - const bool is_quantized = false; - InceptionPreprocessingStage preprocess_stage(width, height, is_quantized); + auto params = InceptionPreprocessingStage::DefaultParamsForType(DT_FLOAT); + InceptionPreprocessingStage preprocess_stage(width, height, DT_FLOAT, params); Scope scope = Scope::NewRootScope(); preprocess_stage.AddToGraph(scope, image_contents); TF_CHECK_OK(scope.status()); -- GitLab From eed1c1413ff2c45c82142d5d4dd8b6d51a2f5636 Mon Sep 17 00:00:00 2001 From: Jared Duke Date: Wed, 9 Jan 2019 08:11:28 -0800 Subject: [PATCH 546/622] Automated rollback of commit 9f3acf3d89a4ef5f9313a6e2cb2d81e03177e8ef PiperOrigin-RevId: 228519835 --- tensorflow/lite/python/BUILD | 1 + tensorflow/lite/python/convert.py | 13 +++++-------- tensorflow/lite/toco/python/BUILD | 21 ++++++++++++++++++--- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/tensorflow/lite/python/BUILD b/tensorflow/lite/python/BUILD index 7bfd1a8996..54b925cc05 100644 --- a/tensorflow/lite/python/BUILD +++ b/tensorflow/lite/python/BUILD @@ -74,6 +74,7 @@ py_test( data = ["@tflite_mobilenet_ssd_quant_protobuf//:tflite_graph.pb"], srcs_version = "PY2AND3", tags = [ + "no_oss", "no_windows", ], deps = [ diff --git a/tensorflow/lite/python/convert.py b/tensorflow/lite/python/convert.py index 9f315fc874..9c60399871 100644 --- a/tensorflow/lite/python/convert.py +++ b/tensorflow/lite/python/convert.py @@ -37,14 +37,11 @@ from tensorflow.python.util.tf_export import tf_export as _tf_export # Lazy load since some of the performance benchmark skylark rules # break dependencies. -if lite_constants.EXPERIMENTAL_USE_TOCO_API_DIRECTLY: - _toco_python = LazyLoader( - "tensorflow_wrap_toco", globals(), - "tensorflow.lite.toco.python." - "tensorflow_wrap_toco") - del LazyLoader -else: - _toco_python = None +_toco_python = LazyLoader( + "tensorflow_wrap_toco", globals(), + "tensorflow.lite.toco.python." + "tensorflow_wrap_toco") +del LazyLoader # Find the toco_from_protos binary using the resource loader if using from # bazel, otherwise we are in a pip where console_scripts already has diff --git a/tensorflow/lite/toco/python/BUILD b/tensorflow/lite/toco/python/BUILD index f3ea5b7bfe..8a6e82ec46 100644 --- a/tensorflow/lite/toco/python/BUILD +++ b/tensorflow/lite/toco/python/BUILD @@ -10,20 +10,35 @@ load("//tensorflow:tensorflow.bzl", "tf_py_wrap_cc") load("//tensorflow:tensorflow.bzl", "tf_py_test") load("//tensorflow:tensorflow.bzl", "py_binary") +config_setting( + name = "tflite_convert_with_select_tf_ops", + define_values = {"tflite_convert_with_select_tf_ops": "true"}, + visibility = [ + "//tensorflow/contrib/lite:__subpackages__", + "//tensorflow/lite:__subpackages__", + ], +) + cc_library( name = "toco_python_api", srcs = ["toco_python_api.cc"], hdrs = ["toco_python_api.h"], deps = [ + "//third_party/python_runtime:headers", "//tensorflow/core:lib", - "//tensorflow/core:ops", "//tensorflow/lite/toco:model_flags_proto_cc", "//tensorflow/lite/toco:toco_flags_proto_cc", "//tensorflow/lite/toco:toco_graphviz_dump_options", "//tensorflow/lite/toco:toco_port", "//tensorflow/lite/toco:toco_tooling", - "//third_party/python_runtime:headers", - ], + ] + select({ + # This is required when running `tflite_convert` from `bazel`. + # It requires to link with TensorFlow Ops to get the op definitions. + ":tflite_convert_with_select_tf_ops": [ + "//tensorflow/core:ops", + ], + "//conditions:default": [], + }), ) tf_py_wrap_cc( -- GitLab From beae80428e5a7d67417dc18561b8ed74724fe4bf Mon Sep 17 00:00:00 2001 From: Andrew Selle Date: Wed, 9 Jan 2019 08:32:27 -0800 Subject: [PATCH 547/622] Move explicit template instantiations to namespace scope. Fixes compile error on mac os on attached bug causing 1.13 build problems. PiperOrigin-RevId: 228522576 --- tensorflow/lite/testing/tflite_driver.cc | 121 ++++++++++++----------- 1 file changed, 62 insertions(+), 59 deletions(-) diff --git a/tensorflow/lite/testing/tflite_driver.cc b/tensorflow/lite/testing/tflite_driver.cc index ffe296432a..a637dc86c0 100644 --- a/tensorflow/lite/testing/tflite_driver.cc +++ b/tensorflow/lite/testing/tflite_driver.cc @@ -79,32 +79,7 @@ class TfLiteDriver::Expectation { SetTensorData(values, &data_); } - template <> - void SetData(const string& csv_values) { - string s = absl::HexStringToBytes(csv_values); - data_.raw = new char[s.size()]; - memcpy(data_.raw, s.data(), s.size()); - } - - bool Check(bool verbose, const TfLiteTensor& tensor) { - switch (tensor.type) { - case kTfLiteFloat32: - return TypedCheck(verbose, tensor); - case kTfLiteInt32: - return TypedCheck(verbose, tensor); - case kTfLiteInt64: - return TypedCheck(verbose, tensor); - case kTfLiteUInt8: - return TypedCheck(verbose, tensor); - case kTfLiteBool: - return TypedCheck(verbose, tensor); - case kTfLiteString: - return TypedCheck(verbose, tensor); - default: - fprintf(stderr, "Unsupported type %d in Check\n", tensor.type); - return false; - } - } + bool Check(bool verbose, const TfLiteTensor& tensor); private: template @@ -146,49 +121,77 @@ class TfLiteDriver::Expectation { return good_output; } - template <> - bool TypedCheck(bool verbose, const TfLiteTensor& tensor) { - if (tensor.data.raw == nullptr) { + TfLitePtrUnion data_; + size_t num_elements_; +}; + +template <> +void TfLiteDriver::Expectation::SetData(const string& csv_values) { + string s = absl::HexStringToBytes(csv_values); + data_.raw = new char[s.size()]; + memcpy(data_.raw, s.data(), s.size()); +} + +template <> +bool TfLiteDriver::Expectation::TypedCheck(bool verbose, + const TfLiteTensor& tensor) { + if (tensor.data.raw == nullptr) { + if (verbose) { + std::cerr << " got empty string" << std::endl; + } + return false; + } + int expected_num_strings = GetStringCount(data_.raw); + int returned_num_strings = GetStringCount(tensor.data.raw); + if (expected_num_strings != returned_num_strings) { + if (verbose) { + std::cerr << " string count differ: got " << returned_num_strings + << ", but expected " << expected_num_strings << std::endl; + } + return false; + } + for (int i = 0; i < returned_num_strings; ++i) { + auto expected_ref = GetString(data_.raw, i); + auto returned_ref = GetString(tensor.data.raw, i); + if (expected_ref.len != returned_ref.len) { if (verbose) { - std::cerr << " got empty string" << std::endl; + std::cerr << " index " << i << ": got string of size " + << returned_ref.len << ", but expected size " + << expected_ref.len << std::endl; } return false; } - int expected_num_strings = GetStringCount(data_.raw); - int returned_num_strings = GetStringCount(tensor.data.raw); - if (expected_num_strings != returned_num_strings) { + if (strncmp(expected_ref.str, returned_ref.str, returned_ref.len) != 0) { if (verbose) { - std::cerr << " string count differ: got " << returned_num_strings - << ", but expected " << expected_num_strings << std::endl; + std::cerr << " index " << i << ": strings are different" << std::endl; } return false; } - for (int i = 0; i < returned_num_strings; ++i) { - auto expected_ref = GetString(data_.raw, i); - auto returned_ref = GetString(tensor.data.raw, i); - if (expected_ref.len != returned_ref.len) { - if (verbose) { - std::cerr << " index " << i << ": got string of size " - << returned_ref.len << ", but expected size " - << expected_ref.len << std::endl; - } - return false; - } - if (strncmp(expected_ref.str, returned_ref.str, returned_ref.len) != 0) { - if (verbose) { - std::cerr << " index " << i << ": strings are different" - << std::endl; - } - return false; - } - } - - return true; } - TfLitePtrUnion data_; - size_t num_elements_; -}; + return true; +} + +bool TfLiteDriver::Expectation::Check(bool verbose, + const TfLiteTensor& tensor) { + switch (tensor.type) { + case kTfLiteFloat32: + return TypedCheck(verbose, tensor); + case kTfLiteInt32: + return TypedCheck(verbose, tensor); + case kTfLiteInt64: + return TypedCheck(verbose, tensor); + case kTfLiteUInt8: + return TypedCheck(verbose, tensor); + case kTfLiteBool: + return TypedCheck(verbose, tensor); + case kTfLiteString: + return TypedCheck(verbose, tensor); + default: + fprintf(stderr, "Unsupported type %d in Check\n", tensor.type); + return false; + } +} TfLiteDriver::TfLiteDriver(bool use_nnapi, const string& delegate_name, bool reference_kernel) -- GitLab From 5922eee396da936af1451c22e0433071f909eeaa Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Wed, 9 Jan 2019 09:15:17 -0800 Subject: [PATCH 548/622] Build file changes. PiperOrigin-RevId: 228528921 --- tensorflow/BUILD | 1 - 1 file changed, 1 deletion(-) diff --git a/tensorflow/BUILD b/tensorflow/BUILD index 0b4498ef12..29d71c323a 100644 --- a/tensorflow/BUILD +++ b/tensorflow/BUILD @@ -385,7 +385,6 @@ package_group( "-//third_party/tensorflow/python/estimator", "//learning/deepmind/...", "//learning/meta_rank/...", - "//learning/pathways/...", # While dataset C++ api requires internals "//tensorflow/...", "//tensorflow_estimator/contrib/...", "//tensorflow_fold/llgtm/...", -- GitLab From 1ee193a2563d51ee45b401f2cff91f6e480e21db Mon Sep 17 00:00:00 2001 From: Martin Wicke Date: Wed, 9 Jan 2019 09:33:54 -0800 Subject: [PATCH 549/622] Remove aux-bin from pip package, it's not needed any more. PiperOrigin-RevId: 228531942 --- tensorflow/tools/pip_package/MANIFEST.in | 1 - tensorflow/tools/pip_package/build_pip_package.sh | 3 --- 2 files changed, 4 deletions(-) diff --git a/tensorflow/tools/pip_package/MANIFEST.in b/tensorflow/tools/pip_package/MANIFEST.in index 272ff4735c..c304e8cf6e 100644 --- a/tensorflow/tools/pip_package/MANIFEST.in +++ b/tensorflow/tools/pip_package/MANIFEST.in @@ -6,7 +6,6 @@ recursive-include * *.so recursive-include * *.dll recursive-include * *.lib recursive-include * *.csv -recursive-include tensorflow/aux-bin * recursive-include tensorflow/include/tensorflow *.h recursive-include tensorflow/include/Eigen * recursive-include tensorflow/include/external * diff --git a/tensorflow/tools/pip_package/build_pip_package.sh b/tensorflow/tools/pip_package/build_pip_package.sh index 439b5428b3..27815491d2 100755 --- a/tensorflow/tools/pip_package/build_pip_package.sh +++ b/tensorflow/tools/pip_package/build_pip_package.sh @@ -118,9 +118,6 @@ function prepare_src() { fi fi fi - mkdir "${TMPDIR}/tensorflow/aux-bin" - # Install toco as a binary in aux-bin. - cp bazel-bin/tensorflow/lite/python/tflite_convert ${TMPDIR}/tensorflow/aux-bin/ fi # protobuf pip package doesn't ship with header files. Copy the headers -- GitLab From 2da8e8c60343d9e833f1c591c54ee2a27c240842 Mon Sep 17 00:00:00 2001 From: Tamara Norman Date: Wed, 9 Jan 2019 10:23:54 -0800 Subject: [PATCH 550/622] Change so that error message received if attempting to do + between a python literal and tensor with incompatible dtype to come from the op PiperOrigin-RevId: 228541386 --- tensorflow/python/ops/math_ops.py | 3 ++- tensorflow/python/ops/math_ops_test.py | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/tensorflow/python/ops/math_ops.py b/tensorflow/python/ops/math_ops.py index 5bccf5493f..248d092538 100644 --- a/tensorflow/python/ops/math_ops.py +++ b/tensorflow/python/ops/math_ops.py @@ -813,7 +813,8 @@ def _OverrideBinaryOperatorHelper(func, op_name, clazz_object=ops.Tensor): return func(x, y, name=name) elif not isinstance(y, sparse_tensor.SparseTensor): try: - y = ops.convert_to_tensor(y, dtype=x.dtype.base_dtype, name="y") + y = ops.convert_to_tensor_v2(y, dtype_hint=x.dtype.base_dtype, + name="y") except TypeError: # If the RHS is not a tensor, it might be a tensor aware object # that can implement the operator with knowledge of itself diff --git a/tensorflow/python/ops/math_ops_test.py b/tensorflow/python/ops/math_ops_test.py index b27cf7208c..b4832e09c0 100644 --- a/tensorflow/python/ops/math_ops_test.py +++ b/tensorflow/python/ops/math_ops_test.py @@ -22,6 +22,7 @@ import numpy as np from tensorflow.python.eager import context from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes +from tensorflow.python.framework import errors_impl from tensorflow.python.framework import ops from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops @@ -664,5 +665,22 @@ class NextAfterTest(test_util.TensorFlowTestCase): self.assertAllEqual(math_ops.nextafter(one, two) - one, eps_const) +class BinaryOpsTest(test_util.TensorFlowTestCase): + + @test_util.run_in_graph_and_eager_modes + def testErrorReceivedIfDtypeMismatchFromOp(self): + if context.executing_eagerly(): + error = errors_impl.InvalidArgumentError + error_message = ( + r"cannot compute Add as input #0\(zero-based\) was expected to be a " + r"float tensor but is a int32 tensor \[Op:Add\] name: add/") + else: + error = TypeError + error_message = ("Input 'y' of 'Add' Op has type float32 that does not " + "match type int32 of argument 'x'.") + with self.assertRaisesRegexp(error, error_message): + a = array_ops.ones([1], dtype=dtypes.int32) + 1.0 + self.evaluate(a) + if __name__ == "__main__": googletest.main() -- GitLab From 2c76c0a07fbf82d5bc8cfc81e688560d0b7e4537 Mon Sep 17 00:00:00 2001 From: Martin Wicke Date: Wed, 9 Jan 2019 10:24:39 -0800 Subject: [PATCH 551/622] Add dependency on google_pasta (freshly minted on pypi). PiperOrigin-RevId: 228541538 --- tensorflow/tools/pip_package/setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tensorflow/tools/pip_package/setup.py b/tensorflow/tools/pip_package/setup.py index 9567e0aff7..55b7046e30 100644 --- a/tensorflow/tools/pip_package/setup.py +++ b/tensorflow/tools/pip_package/setup.py @@ -51,6 +51,7 @@ REQUIRED_PACKAGES = [ 'absl-py >= 0.1.6', 'astor >= 0.6.0', 'gast >= 0.2.0', + 'google_pasta >= 0.1.0', 'keras_applications >= 1.0.6', 'keras_preprocessing >= 1.0.5', 'numpy >= 1.13.3', -- GitLab From 864fe25ff7363730ef81e402d6a10cc758df72cd Mon Sep 17 00:00:00 2001 From: Jared Duke Date: Wed, 9 Jan 2019 10:52:22 -0800 Subject: [PATCH 552/622] Align Android gradle build files Use Gradle 3.1.4 and modernize the TFLite example gradle files. PiperOrigin-RevId: 228547238 --- .../lite/examples/android/app/build.gradle | 6 ++--- tensorflow/lite/examples/android/build.gradle | 2 +- .../lite/g3doc/tfmobile/android_build.md | 4 +-- tensorflow/lite/g3doc/using_select_tf_ops.md | 2 +- tensorflow/lite/java/demo/app/build.gradle | 26 +++++++------------ tensorflow/lite/java/demo/build.gradle | 4 ++- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../lite/java/ovic/demo/app/build.gradle | 21 ++++++--------- tensorflow/lite/java/ovic/demo/build.gradle | 4 ++- .../gradle/wrapper/gradle-wrapper.properties | 2 +- 10 files changed, 33 insertions(+), 40 deletions(-) diff --git a/tensorflow/lite/examples/android/app/build.gradle b/tensorflow/lite/examples/android/app/build.gradle index e5f5c7efd1..b372afae19 100644 --- a/tensorflow/lite/examples/android/app/build.gradle +++ b/tensorflow/lite/examples/android/app/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion 26 - buildToolsVersion '26.0.2' + buildToolsVersion '27.0.3' defaultConfig { applicationId "org.tensorflow.lite.demo" minSdkVersion 15 @@ -45,6 +45,6 @@ project.ext.TMP_DIR = project.buildDir.toString() + '/downloads' apply from: "download-models.gradle" dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'org.tensorflow:tensorflow-lite:0.0.0-nightly' + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'org.tensorflow:tensorflow-lite:0.0.0-nightly' } diff --git a/tensorflow/lite/examples/android/build.gradle b/tensorflow/lite/examples/android/build.gradle index 7c79358e45..7c038ddd46 100644 --- a/tensorflow/lite/examples/android/build.gradle +++ b/tensorflow/lite/examples/android/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' + classpath 'com.android.tools.build:gradle:3.1.4' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/tensorflow/lite/g3doc/tfmobile/android_build.md b/tensorflow/lite/g3doc/tfmobile/android_build.md index 2eb776d10c..f8c0243298 100644 --- a/tensorflow/lite/g3doc/tfmobile/android_build.md +++ b/tensorflow/lite/g3doc/tfmobile/android_build.md @@ -91,10 +91,10 @@ following lines to your Gradle build file: repositories { jcenter() } - } + } dependencies { - compile 'org.tensorflow:tensorflow-android:+' + implementation 'org.tensorflow:tensorflow-android:+' } This automatically downloads the latest stable version of TensorFlow as an AAR diff --git a/tensorflow/lite/g3doc/using_select_tf_ops.md b/tensorflow/lite/g3doc/using_select_tf_ops.md index aa51f58baa..269774a4b1 100644 --- a/tensorflow/lite/g3doc/using_select_tf_ops.md +++ b/tensorflow/lite/g3doc/using_select_tf_ops.md @@ -130,7 +130,7 @@ allprojects { } dependencies { - compile 'org.tensorflow:tensorflow-lite-with-select-tf-ops:0.1.100' + implementation 'org.tensorflow:tensorflow-lite-with-select-tf-ops:0.1.100' } ``` diff --git a/tensorflow/lite/java/demo/app/build.gradle b/tensorflow/lite/java/demo/app/build.gradle index b8fc282cb1..8ea16a3417 100644 --- a/tensorflow/lite/java/demo/app/build.gradle +++ b/tensorflow/lite/java/demo/app/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion 26 - buildToolsVersion "26.0.1" + buildToolsVersion "27.0.3" defaultConfig { applicationId "android.example.com.tflitecamerademo" // Required by Camera2 API. @@ -10,11 +10,6 @@ android { targetSdkVersion 26 versionCode 1 versionName "1.0" - - // Remove this block. - jackOptions { - enabled true - } } lintOptions { abortOnError false @@ -40,6 +35,7 @@ repositories { url 'https://google.bintray.com/tensorflow' } } + allprojects { repositories { // Uncomment if you want to use a local repo. @@ -48,20 +44,18 @@ allprojects { } } - - dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:25.2.0' - compile 'com.android.support.constraint:constraint-layout:1.0.2' - compile 'com.android.support:design:25.2.0' - compile 'com.android.support:support-annotations:25.3.1' - compile 'com.android.support:support-v13:25.2.0' + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'com.android.support:appcompat-v7:25.2.0' + implementation 'com.android.support.constraint:constraint-layout:1.0.2' + implementation 'com.android.support:design:25.2.0' + implementation 'com.android.support:support-annotations:25.3.1' + implementation 'com.android.support:support-v13:25.2.0' // Build off of nightly TensorFlow Lite - compile 'org.tensorflow:tensorflow-lite:0.0.0-nightly' + implementation 'org.tensorflow:tensorflow-lite:0.0.0-nightly' // Use local TensorFlow library - // compile 'org.tensorflow:tensorflow-lite-local:0.0.0' + // implementation 'org.tensorflow:tensorflow-lite-local:0.0.0' } def targetFolder = "src/main/assets" diff --git a/tensorflow/lite/java/demo/build.gradle b/tensorflow/lite/java/demo/build.gradle index b78a0b86c9..a88b3fdc70 100644 --- a/tensorflow/lite/java/demo/build.gradle +++ b/tensorflow/lite/java/demo/build.gradle @@ -2,10 +2,11 @@ buildscript { repositories { + google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.1' + classpath 'com.android.tools.build:gradle:3.1.4' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -14,6 +15,7 @@ buildscript { allprojects { repositories { + google() jcenter() } } diff --git a/tensorflow/lite/java/demo/gradle/wrapper/gradle-wrapper.properties b/tensorflow/lite/java/demo/gradle/wrapper/gradle-wrapper.properties index fa7a38a0e4..9ff32fe2bb 100644 --- a/tensorflow/lite/java/demo/gradle/wrapper/gradle-wrapper.properties +++ b/tensorflow/lite/java/demo/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip diff --git a/tensorflow/lite/java/ovic/demo/app/build.gradle b/tensorflow/lite/java/ovic/demo/app/build.gradle index 4f3a6cdb2f..77f568448a 100644 --- a/tensorflow/lite/java/ovic/demo/app/build.gradle +++ b/tensorflow/lite/java/ovic/demo/app/build.gradle @@ -2,18 +2,13 @@ apply plugin: 'com.android.application' android { compileSdkVersion 26 - buildToolsVersion "26.0.1" + buildToolsVersion "27.0.3" defaultConfig { applicationId "android.example.com.ovicbenchmarker" minSdkVersion 15 targetSdkVersion 26 versionCode 1 versionName "1.0" - - // Remove this block. - jackOptions { - enabled true - } } lintOptions { abortOnError false @@ -41,12 +36,12 @@ repositories { } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:25.2.0' - compile 'com.android.support.constraint:constraint-layout:1.0.2' - compile 'com.android.support:design:25.2.0' - compile 'com.android.support:support-annotations:25.3.1' - compile 'com.android.support:support-v13:25.2.0' + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'com.android.support:appcompat-v7:25.2.0' + implementation 'com.android.support.constraint:constraint-layout:1.0.2' + implementation 'com.android.support:design:25.2.0' + implementation 'com.android.support:support-annotations:25.3.1' + implementation 'com.android.support:support-v13:25.2.0' - compile 'org.tensorflow:tensorflow-lite:+' + implementation 'org.tensorflow:tensorflow-lite:+' } diff --git a/tensorflow/lite/java/ovic/demo/build.gradle b/tensorflow/lite/java/ovic/demo/build.gradle index b78a0b86c9..a88b3fdc70 100644 --- a/tensorflow/lite/java/ovic/demo/build.gradle +++ b/tensorflow/lite/java/ovic/demo/build.gradle @@ -2,10 +2,11 @@ buildscript { repositories { + google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.1' + classpath 'com.android.tools.build:gradle:3.1.4' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -14,6 +15,7 @@ buildscript { allprojects { repositories { + google() jcenter() } } diff --git a/tensorflow/lite/java/ovic/demo/gradle/wrapper/gradle-wrapper.properties b/tensorflow/lite/java/ovic/demo/gradle/wrapper/gradle-wrapper.properties index fa7a38a0e4..9ff32fe2bb 100644 --- a/tensorflow/lite/java/ovic/demo/gradle/wrapper/gradle-wrapper.properties +++ b/tensorflow/lite/java/ovic/demo/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip -- GitLab From 65cfc6a54bc76912dd16c25b9e13610c4359a6d9 Mon Sep 17 00:00:00 2001 From: Blake Hechtman Date: Wed, 9 Jan 2019 11:13:04 -0800 Subject: [PATCH 553/622] [XLA] Fix Dot operation semantics. PiperOrigin-RevId: 228551710 --- tensorflow/compiler/xla/g3doc/operation_semantics.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/tensorflow/compiler/xla/g3doc/operation_semantics.md b/tensorflow/compiler/xla/g3doc/operation_semantics.md index 7377ed729b..59ba9bb658 100644 --- a/tensorflow/compiler/xla/g3doc/operation_semantics.md +++ b/tensorflow/compiler/xla/g3doc/operation_semantics.md @@ -871,9 +871,7 @@ DotGeneral performs the sum of products over contracting dimensions specified in 'dimension_numbers'. Associated contracting dimension numbers from the 'lhs' and 'rhs' do not need -to be the same, but must be listed in the same order in both -'lhs/rhs_contracting_dimensions' arrays and have the same dimension sizes. -There must be exactly one contracting dimension on both 'lhs' and 'rhs'. +to be the same and but must have the same dimension sizes. Example with contracting dimension numbers: @@ -892,10 +890,8 @@ DotGeneral(lhs, rhs, dnums) -> { {6.0, 12.0}, {15.0, 30.0} } ``` -Associated batch dimension numbers from the 'lhs' and 'rhs' must have the same -dimension number, must be listed in the same order in both arrays, must -have the same dimension sizes, and must be ordered before contracting and -non-contracting/non-batch dimension numbers. +Associated batch dimension numbers from the 'lhs' and 'rhs' must +have the same dimension sizes. Example with batch dimension numbers (batch size 2, 2x2 matrices): -- GitLab From d987224e93ba153201a7c3958594396af56a255d Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Wed, 9 Jan 2019 11:24:20 -0800 Subject: [PATCH 554/622] Typo fix PiperOrigin-RevId: 228554042 --- tensorflow/core/grappler/costs/graph_properties.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tensorflow/core/grappler/costs/graph_properties.cc b/tensorflow/core/grappler/costs/graph_properties.cc index cfbd340f08..1ed96ca12a 100644 --- a/tensorflow/core/grappler/costs/graph_properties.cc +++ b/tensorflow/core/grappler/costs/graph_properties.cc @@ -1463,9 +1463,9 @@ class SymbolicShapeRefiner { // Due to the cost of EvaluateNode(), we run it only for certain op types // (white listed) and small integer tensors. - const int max_elelment_size = 17; // Max up to 4x4 matrix or similar. + const int max_element_size = 17; // Max up to 4x4 matrix or similar. if (AllOutputValuesKnown(c) || !AllInputValuesKnown(c) || - !ShouldUpdateOutputValues(c, max_elelment_size)) { + !ShouldUpdateOutputValues(c, max_element_size)) { return Status::OK(); } UpdateOutputValues(node, c).IgnoreError(); // This is optional. -- GitLab From 9e4f493abffbcb1efc7ea2b1e9bce0afff8122ce Mon Sep 17 00:00:00 2001 From: Guangda Lai Date: Wed, 9 Jan 2019 11:31:52 -0800 Subject: [PATCH 555/622] Reduce max_workspace_size_bytes so the GPU memory allocation doesn't exceed the limit. PiperOrigin-RevId: 228555498 --- tensorflow/contrib/tensorrt/test/quantization_mnist_test.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tensorflow/contrib/tensorrt/test/quantization_mnist_test.py b/tensorflow/contrib/tensorrt/test/quantization_mnist_test.py index e7d6ec4ad3..79dde2a6b5 100644 --- a/tensorflow/contrib/tensorrt/test/quantization_mnist_test.py +++ b/tensorflow/contrib/tensorrt/test/quantization_mnist_test.py @@ -144,7 +144,10 @@ class QuantizationAwareTrainingMNISTTest(test_util.TensorFlowTestCase): outputs=[OUTPUT_NODE_NAME], max_batch_size=max_batch_size, precision_mode='INT8', - max_workspace_size_bytes=4096 << 19, + # There is a 2GB GPU memory limit for each test, so we set + # max_workspace_size_bytes to 256MB to leave enough room for TF + # runtime to allocate GPU memory. + max_workspace_size_bytes=1 << 28, minimum_segment_size=2, use_calibration=False, ) -- GitLab From 06974e29b1e9f4b4b5120acca2889bfa3bcf055d Mon Sep 17 00:00:00 2001 From: Zhenyu Tan Date: Wed, 9 Jan 2019 11:39:26 -0800 Subject: [PATCH 556/622] Internal Cleanup. PiperOrigin-RevId: 228557086 --- tensorflow/python/keras/optimizer_v2/BUILD | 4 - tensorflow/python/keras/optimizer_v2/nadam.py | 3 +- .../keras/optimizer_v2/optimizer_v2_test.py | 115 ++++++++++-------- 3 files changed, 62 insertions(+), 60 deletions(-) diff --git a/tensorflow/python/keras/optimizer_v2/BUILD b/tensorflow/python/keras/optimizer_v2/BUILD index 2b990c9334..da757edc74 100644 --- a/tensorflow/python/keras/optimizer_v2/BUILD +++ b/tensorflow/python/keras/optimizer_v2/BUILD @@ -177,11 +177,7 @@ py_test( srcs = ["optimizer_v2_test.py"], shard_count = 8, tags = [ - "manual", - "no_gpu", - "no_oss", "no_windows", - "notap", ], deps = [ ":optimizer_v2", diff --git a/tensorflow/python/keras/optimizer_v2/nadam.py b/tensorflow/python/keras/optimizer_v2/nadam.py index 627258f455..7141c6a079 100644 --- a/tensorflow/python/keras/optimizer_v2/nadam.py +++ b/tensorflow/python/keras/optimizer_v2/nadam.py @@ -84,8 +84,7 @@ class Nadam(optimizer_v2.OptimizerV2): """ # Backwards compatiblity with keras NAdam optimizer. - if 'schedule_decay' in kwargs: - kwargs['decay'] = kwargs.pop('schedule_decay', 0.004) + kwargs['decay'] = kwargs.pop('schedule_decay', 0.004) super(Nadam, self).__init__(name, **kwargs) self._set_hyper('learning_rate', kwargs.get('lr', learning_rate)) self._set_hyper('decay', self._initial_decay) diff --git a/tensorflow/python/keras/optimizer_v2/optimizer_v2_test.py b/tensorflow/python/keras/optimizer_v2/optimizer_v2_test.py index b41e4aa064..93a6a7e85e 100644 --- a/tensorflow/python/keras/optimizer_v2/optimizer_v2_test.py +++ b/tensorflow/python/keras/optimizer_v2/optimizer_v2_test.py @@ -18,10 +18,6 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -import os -import tempfile - -from absl.testing import parameterized import numpy as np from tensorflow.python import keras @@ -33,22 +29,25 @@ from tensorflow.python.framework import ops from tensorflow.python.framework import test_util from tensorflow.python.keras import backend from tensorflow.python.keras import callbacks +from tensorflow.python.keras import keras_parameterized from tensorflow.python.keras import optimizers from tensorflow.python.keras import testing_utils from tensorflow.python.keras.engine import input_layer -from tensorflow.python.keras.engine import saving from tensorflow.python.keras.engine import sequential from tensorflow.python.keras.engine import training from tensorflow.python.keras.layers import core +from tensorflow.python.keras.optimizer_v2 import adadelta +from tensorflow.python.keras.optimizer_v2 import adagrad from tensorflow.python.keras.optimizer_v2 import adam +from tensorflow.python.keras.optimizer_v2 import adamax from tensorflow.python.keras.optimizer_v2 import gradient_descent -from tensorflow.python.keras.optimizer_v2 import optimizer_v2 +from tensorflow.python.keras.optimizer_v2 import nadam +from tensorflow.python.keras.optimizer_v2 import rmsprop from tensorflow.python.ops import array_ops from tensorflow.python.ops import clip_ops from tensorflow.python.ops import resource_variable_ops from tensorflow.python.ops import state_ops from tensorflow.python.ops import variables -from tensorflow.python.platform import gfile from tensorflow.python.platform import test from tensorflow.python.training import momentum from tensorflow.python.training import training_util @@ -492,23 +491,20 @@ class OptimizerTest(test.TestCase): opt.iterations = global_step var = resource_variable_ops.ResourceVariable([1.0, 2.0], dtype=dtypes.float32) + self.evaluate(variables.global_variables_initializer()) + init_step_value = self.evaluate(global_step) loss = lambda: 3 * var opt_op = opt.minimize(loss, [var]) self.evaluate(variables.global_variables_initializer()) - init_step_value = self.evaluate(global_step) self.evaluate(opt_op) new_step_value = self.evaluate(global_step) self.assertEqual(new_step_value, init_step_value + 1) -class OptimizersCompatibilityTest(test.TestCase, parameterized.TestCase): +@keras_parameterized.run_with_all_model_types +class OptimizersCompatibilityTest(keras_parameterized.TestCase): - @parameterized.named_parameters( - ('adadelta', 'adadelta', True), ('adagrad', 'adagrad', True), - ('adam', 'adam', True), ('adamax', 'adamax', True), - ('nadam', 'nadam', True), ('momentum', 'momentum', True), - ('rmsprop', 'rmsprop', True), ('sgd', 'sgd', False)) - def testOptimizersCompatibility(self, opt_str, test_weights): + def _testOptimizersCompatibility(self, opt_v1, opt_v2, test_weights=True): np.random.seed(1331) with self.cached_session(): train_samples = 20 @@ -522,43 +518,63 @@ class OptimizersCompatibilityTest(test.TestCase, parameterized.TestCase): y = keras.utils.to_categorical(y) num_hidden = 5 - model = testing_utils.get_small_sequential_mlp( + model_v1 = testing_utils.get_small_sequential_mlp( num_hidden=num_hidden, num_classes=num_classes, input_dim=input_dim) + model_v1.compile(opt_v1, loss='categorical_crossentropy', metrics=[]) + model_v1.fit(x, y, batch_size=5, epochs=1) - old_mode = os.environ.get('TF2_BEHAVIOR', None) - # Disable tf2 to create V1 optimizer. - disable_tf2() - if opt_str == 'momentum': - opt_v1 = optimizers.SGD(momentum=0.9) - else: - opt_v1 = optimizers.get(opt_str) - - # Test compile and fit with v1 optimizer. - model.compile(opt_v1, loss='categorical_crossentropy', metrics=[]) - model.fit(x, y, batch_size=5, epochs=1) - model_dir = tempfile.mkdtemp() - gfile.MakeDirs(model_dir) - file_name = os.path.join(model_dir, 'model.h5') - model.save(file_name) - - enable_tf2() - # Test load and fit with v2 optimizer. - model_2 = saving.load_model(file_name) - opt_v2 = model_2.optimizer - self.assertIsInstance(opt_v2, optimizer_v2.OptimizerV2) - # set_weights is called inside load_model but exception is swallowed, - # this call checks the weights can be set correctly. + model_v2 = testing_utils.get_small_sequential_mlp( + num_hidden=num_hidden, num_classes=num_classes, input_dim=input_dim) + model_v2.set_weights(model_v1.get_weights()) + model_v2.compile(opt_v2, loss='categorical_crossentropy', metrics=[]) + model_v2._make_train_function() if test_weights: opt_v2.set_weights(opt_v1.get_weights()) - hist_1 = model.fit(x, y, batch_size=5, epochs=1, shuffle=False) - hist_2 = model_2.fit(x, y, batch_size=5, epochs=1, shuffle=False) - self.assertAllClose(model.get_weights(), model_2.get_weights()) - self.assertAllClose(model.get_weights(), model_2.get_weights()) + hist_1 = model_v1.fit(x, y, batch_size=5, epochs=1, shuffle=False) + hist_2 = model_v2.fit(x, y, batch_size=5, epochs=1, shuffle=False) + self.assertAllClose(model_v1.get_weights(), model_v2.get_weights()) self.assertAllClose(hist_1.history['loss'], hist_2.history['loss']) - if old_mode is not None: - os.environ['TF2_BEHAVIOR'] = old_mode + def testAdadeltaCompatibility(self): + opt_v1 = optimizers.Adadelta(lr=0.01) + opt_v2 = adadelta.Adadelta(learning_rate=0.01) + self._testOptimizersCompatibility(opt_v1, opt_v2) + + def testAdagradCompatibility(self): + opt_v1 = optimizers.Adagrad(lr=0.01) + opt_v2 = adagrad.Adagrad(learning_rate=0.01) + self._testOptimizersCompatibility(opt_v1, opt_v2) + + def testAdamCompatibility(self): + opt_v1 = optimizers.Adam() + opt_v2 = adam.Adam() + self._testOptimizersCompatibility(opt_v1, opt_v2) + + def testAdamaxCompatibility(self): + opt_v1 = optimizers.Adamax(lr=0.01) + opt_v2 = adamax.Adamax(learning_rate=0.01) + self._testOptimizersCompatibility(opt_v1, opt_v2) + + def testNadamCompatibility(self): + opt_v1 = optimizers.Nadam(lr=0.001) + opt_v2 = nadam.Nadam(learning_rate=0.001) + self._testOptimizersCompatibility(opt_v1, opt_v2) + + def testMomentumCompatibility(self): + opt_v1 = optimizers.SGD(lr=0.01, momentum=0.9) + opt_v2 = gradient_descent.SGD(learning_rate=0.01, momentum=0.9) + self._testOptimizersCompatibility(opt_v1, opt_v2) + + def testRMSpropCompatibility(self): + opt_v1 = optimizers.RMSprop() + opt_v2 = rmsprop.RMSprop() + self._testOptimizersCompatibility(opt_v1, opt_v2) + + def testSGDCompatibility(self): + opt_v1 = optimizers.SGD(lr=0.01) + opt_v2 = gradient_descent.SGD(learning_rate=0.01) + self._testOptimizersCompatibility(opt_v1, opt_v2, False) def testNumericEquivalenceForNesterovMomentum(self): np.random.seed(1331) @@ -636,15 +652,6 @@ class OptimizersCompatibilityTest(test.TestCase, parameterized.TestCase): self.assertAllClose(hist_k_v1.history['loss'], hist_k_v2.history['loss']) -def disable_tf2(): - if 'TF2_BEHAVIOR' in os.environ: - del os.environ['TF2_BEHAVIOR'] - - -def enable_tf2(): - os.environ['TF2_BEHAVIOR'] = 'enabled' - - # Note: These tests are kept in a separate class to avoid bugs in some # distributions of Python that break AutoGraph which is used by tf.function. class OptimizerWithFunctionTest(test.TestCase): -- GitLab From f5f079ca12c376eb2b86389973661f27054136af Mon Sep 17 00:00:00 2001 From: Karim Nosir Date: Wed, 9 Jan 2019 11:44:32 -0800 Subject: [PATCH 557/622] Add missing include needed for windows build. Fixes #24521 PiperOrigin-RevId: 228558062 --- tensorflow/core/util/stats_calculator.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tensorflow/core/util/stats_calculator.h b/tensorflow/core/util/stats_calculator.h index e191737bb2..5005ee08a4 100644 --- a/tensorflow/core/util/stats_calculator.h +++ b/tensorflow/core/util/stats_calculator.h @@ -18,6 +18,7 @@ limitations under the License. #include +#include #include #include #include -- GitLab From 245925078f574983593e42154091e07c311e30bd Mon Sep 17 00:00:00 2001 From: Sourabh Bajaj Date: Wed, 9 Jan 2019 11:45:24 -0800 Subject: [PATCH 558/622] Remove the implementation of TPU Strategy initialize and finalize PiperOrigin-RevId: 228558184 --- .../contrib/distribute/python/tpu_strategy.py | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/tensorflow/contrib/distribute/python/tpu_strategy.py b/tensorflow/contrib/distribute/python/tpu_strategy.py index f498f58b25..00360bf996 100644 --- a/tensorflow/contrib/distribute/python/tpu_strategy.py +++ b/tensorflow/contrib/distribute/python/tpu_strategy.py @@ -51,9 +51,6 @@ from tensorflow.python.platform import tf_logging as logging from tensorflow.python.util import nest -_TPU_INITIALIZE_SYSTEM_COLLECTION = "TPU_STRATEGY_INITIALIZE" - - def initialize_tpu_system(cluster_resolver=None): """Initialize the TPU devices in a separate session and graph. @@ -463,28 +460,6 @@ class TPUExtended(distribute_lib.DistributionStrategyExtended): """ initialize_tpu_system(self._tpu_cluster_resolver) - def _initialize(self): - if context.executing_eagerly(): - # TODO(priyag): Add appopriate call here when eager is supported for TPUs. - raise NotImplementedError("Eager mode not supported in TPUStrategy.") - else: - # TODO(jhseu): We need this hack because DistributionStrategies must be - # pickleable for copy.deepcopy(). Remove when initialize_system goes away. - graph = ops.get_default_graph() - tpu_init = graph.get_collection(_TPU_INITIALIZE_SYSTEM_COLLECTION) - if tpu_init: - return tpu_init - graph.add_to_collection(_TPU_INITIALIZE_SYSTEM_COLLECTION, - tpu.initialize_system()) - return graph.get_collection(_TPU_INITIALIZE_SYSTEM_COLLECTION) - - def _finalize(self): - if context.executing_eagerly(): - # TODO(priyag): Add appopriate call here when eager is supported for TPUs. - raise NotImplementedError("Eager mode not supported in TPUStrategy.") - else: - return [] - def _create_variable(self, next_creator, *args, **kwargs): """Create a TPUMirroredVariable. See `DistributionStrategy.scope`.""" colocate_with = kwargs.pop("colocate_with", None) -- GitLab From bebf6b3d5d11e0ae09edab0a3700e0e4000ac842 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Wed, 9 Jan 2019 11:57:48 -0800 Subject: [PATCH 559/622] Scope GBDT resource variables. PiperOrigin-RevId: 228560356 --- .../contrib/boosted_trees/python/ops/model_ops.py | 10 +++++----- .../python/training/functions/gbdt_batch.py | 5 +++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/tensorflow/contrib/boosted_trees/python/ops/model_ops.py b/tensorflow/contrib/boosted_trees/python/ops/model_ops.py index fca22c71a8..c3685b54e2 100644 --- a/tensorflow/contrib/boosted_trees/python/ops/model_ops.py +++ b/tensorflow/contrib/boosted_trees/python/ops/model_ops.py @@ -62,8 +62,8 @@ class TreeEnsembleVariableSavable(saver.BaseSaverBuilder.SaveableObject): saver.BaseSaverBuilder.SaveSpec(ensemble_config, slice_spec, name + "_config"), ] - super(TreeEnsembleVariableSavable, - self).__init__(tree_ensemble_handle, specs, name) + super(TreeEnsembleVariableSavable, self).__init__(tree_ensemble_handle, + specs, name) self._tree_ensemble_handle = tree_ensemble_handle self._create_op = create_op @@ -115,7 +115,7 @@ class TreeEnsembleVariable(tracking.TrackableResource): def _gather_saveables_for_checkpoint(self): return { - "tree_ensemble_variable": + self.resource_handle.op.name + "/tree_ensemble_variable": functools.partial( TreeEnsembleVariableSavable, tree_ensemble_handle=self.resource_handle, @@ -131,8 +131,8 @@ def tree_ensemble_variable(stamp_token, Args: stamp_token: The initial stamp token value for the ensemble resource. - tree_ensemble_config: A `Tensor` of type `string`. - Serialized proto of the tree ensemble. + tree_ensemble_config: A `Tensor` of type `string`. Serialized proto of the + tree ensemble. name: A name for the ensemble variable. container: An optional `string`. Defaults to `""`. diff --git a/tensorflow/contrib/boosted_trees/python/training/functions/gbdt_batch.py b/tensorflow/contrib/boosted_trees/python/training/functions/gbdt_batch.py index a5951fb737..e78ec476ab 100644 --- a/tensorflow/contrib/boosted_trees/python/training/functions/gbdt_batch.py +++ b/tensorflow/contrib/boosted_trees/python/training/functions/gbdt_batch.py @@ -566,9 +566,10 @@ class GradientBoostedDecisionTreeModel(object): # Determine if ensemble is colocated with the inputs. if self._ensemble_handle.device != input_deps[0].device: # Create a local ensemble and get its local stamp. - with ops.name_scope("local_ensemble", "TreeEnsembleVariable") as name: + with ops.name_scope("local_ensemble", "TreeEnsembleVariable"): local_ensemble_handle = ( - gen_model_ops.decision_tree_ensemble_resource_handle_op(name=name)) + gen_model_ops.decision_tree_ensemble_resource_handle_op( + self._ensemble_handle.op.name + "/local_ensemble")) create_op = gen_model_ops.create_tree_ensemble_variable( local_ensemble_handle, stamp_token=-1, tree_ensemble_config="") with ops.control_dependencies([create_op]): -- GitLab From 8d9872cebb57219acbfaad57e5203b3a407ac28e Mon Sep 17 00:00:00 2001 From: Adrian Kuegel Date: Wed, 9 Jan 2019 11:57:49 -0800 Subject: [PATCH 560/622] Fix typos in comments. PiperOrigin-RevId: 228560360 --- .../compiler/xla/service/gpu/cudnn_conv_algorithm_picker.cc | 2 +- tensorflow/compiler/xla/service/gpu/cudnn_conv_runner.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tensorflow/compiler/xla/service/gpu/cudnn_conv_algorithm_picker.cc b/tensorflow/compiler/xla/service/gpu/cudnn_conv_algorithm_picker.cc index 057d0d2364..309b0aca64 100644 --- a/tensorflow/compiler/xla/service/gpu/cudnn_conv_algorithm_picker.cc +++ b/tensorflow/compiler/xla/service/gpu/cudnn_conv_algorithm_picker.cc @@ -250,7 +250,7 @@ CudnnConvAlgorithmPicker::PickBestAlgorithm( VLOG(3) << "Trying algorithm " << AlgorithmToString(alg) << " for " << instr->ToString(); - // Use assignment insetad of brace-list to make GCC 4.9 happy. + // Use assignment instead of brace-list to make GCC 4.9 happy. RunConvOptions options; options.profile_result = &profile_result; options.algo_override = alg; diff --git a/tensorflow/compiler/xla/service/gpu/cudnn_conv_runner.h b/tensorflow/compiler/xla/service/gpu/cudnn_conv_runner.h index 7cc325bce3..25b2461ca6 100644 --- a/tensorflow/compiler/xla/service/gpu/cudnn_conv_runner.h +++ b/tensorflow/compiler/xla/service/gpu/cudnn_conv_runner.h @@ -32,7 +32,7 @@ struct RunConvOptions { // Nullable output-parameter pointer for profiling results. se::dnn::ProfileResult* profile_result = nullptr; - // Use this algorithm, instead the one from the instrcution. + // Use this algorithm, instead of the one from the instruction. absl::optional algo_override; }; -- GitLab From a14adaa2329fb46cb472b949ee52546c2516a21e Mon Sep 17 00:00:00 2001 From: Reed Wanderman-Milne Date: Wed, 9 Jan 2019 12:00:29 -0800 Subject: [PATCH 561/622] Automated rollback of changelist 205730535 PiperOrigin-RevId: 228560984 --- tensorflow/core/BUILD | 11 ---- .../core/common_runtime/gpu/gpu_device.cc | 51 +------------------ .../core/common_runtime/gpu/gpu_device.h | 11 ---- .../gpu/gpu_device_kernel_check.cu.cc | 37 -------------- .../gpu/gpu_device_kernel_check.h | 32 ------------ 5 files changed, 1 insertion(+), 141 deletions(-) delete mode 100644 tensorflow/core/common_runtime/gpu/gpu_device_kernel_check.cu.cc delete mode 100644 tensorflow/core/common_runtime/gpu/gpu_device_kernel_check.h diff --git a/tensorflow/core/BUILD b/tensorflow/core/BUILD index 6a842a432f..e6af9211b5 100644 --- a/tensorflow/core/BUILD +++ b/tensorflow/core/BUILD @@ -87,7 +87,6 @@ load( "tf_gen_op_libs", "tf_generate_proto_text_sources", "tf_genrule_cmd_append_to_srcs", - "tf_gpu_kernel_library", "tf_opts_nortti_if_android", "transitive_hdrs", ) @@ -3163,15 +3162,6 @@ cc_library( ], ) -tf_gpu_kernel_library( - name = "gpu_device_kernel_check", - srcs = ["common_runtime/gpu/gpu_device_kernel_check.cu.cc"], - hdrs = ["common_runtime/gpu/gpu_device_kernel_check.h"], - deps = [ - "//tensorflow/core:stream_executor", - ], -) - GPU_RUNTIME_HEADERS = [ "common_runtime/gpu/cuda_host_allocator.h", "common_runtime/gpu/gpu_bfc_allocator.h", @@ -3210,7 +3200,6 @@ tf_cuda_library( ":core_cpu_lib", ":framework", ":framework_internal", - ":gpu_device_kernel_check", ":gpu_id_impl", ":gpu_init_impl", ":gpu_lib", diff --git a/tensorflow/core/common_runtime/gpu/gpu_device.cc b/tensorflow/core/common_runtime/gpu/gpu_device.cc index 891b6e0e2a..010fdff4e9 100644 --- a/tensorflow/core/common_runtime/gpu/gpu_device.cc +++ b/tensorflow/core/common_runtime/gpu/gpu_device.cc @@ -31,7 +31,6 @@ limitations under the License. #include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor" #include "tensorflow/core/common_runtime/device_factory.h" -#include "tensorflow/core/common_runtime/gpu/gpu_device_kernel_check.h" #include "tensorflow/core/common_runtime/gpu/gpu_event_mgr.h" #include "tensorflow/core/common_runtime/gpu/gpu_id.h" #include "tensorflow/core/common_runtime/gpu/gpu_id_manager.h" @@ -389,7 +388,7 @@ Status BaseGPUDevice::Init(const SessionOptions& options) { } } - return CheckGPU(); + return Status::OK(); } bool BaseGPUDevice::RequiresRecordingAccessedTensors() const { @@ -908,54 +907,6 @@ Allocator* BaseGPUDevice::GetScopedAllocator(AllocatorAttributes attr, return gpu_allocator_; } -Status BaseGPUDevice::CheckGPU() { - se::Stream* stream = tensorflow_gpu_device_info()->stream; - TF_RETURN_IF_ERROR(stream->BlockHostUntilDone()); - Tensor device_tensor(gpu_allocator_, DT_FLOAT, {}); - if (!device_tensor.IsInitialized()) { - return errors::ResourceExhausted("Failed to allocate ", sizeof(float), - " bytes on the GPU for initialization " - "checks"); - } - float* val_dev = device_tensor.scalar().data(); - const cudaStream_t cu_stream = *reinterpret_cast( - stream->implementation()->GpuStreamMemberHack()); - { - se::cuda::ScopedActivateExecutorContext scoped_activation{stream->parent()}; - run_test_kernel(val_dev, cu_stream); - // We have to use the CUDA runtime function cudaPeekAtLastError here, - // because 'stream' does not provide a way to check if a kernel launch - // succeeds. Calling 'stream->BlockHostUntilDone()', which internally calls - // 'cuCtxSynchronize()', does not catch all kernel launch errors. - cudaError_t cuda_error = cudaPeekAtLastError(); - if (cuda_error == cudaSuccess) { - cuda_error = cudaDeviceSynchronize(); - } - TF_RETURN_IF_ERROR(CudaErrorToStatus(cuda_error, *stream)); - } - - float val_host = 0.; - stream->ThenMemcpy(&val_host, se::DeviceMemoryBase(val_dev, sizeof(float)), - sizeof(float)); - TF_RETURN_IF_ERROR(stream->BlockHostUntilDone()); - if (val_host != 12345.) { - return errors::Internal( - "GPU kernel for initialization returned wrong value: ", val_host); - } - return Status::OK(); -} - -Status BaseGPUDevice::CudaErrorToStatus(cudaError_t cuda_error, - const se::Stream& stream) { - if (cuda_error != cudaSuccess) { - return errors::Internal( - "Failed to run GPU kernel for the initialization check. Received " - "error ", - cudaGetErrorName(cuda_error), " after running GPU kernel."); - } - return Status::OK(); -} - const int BaseGPUDeviceFactory::InterconnectMap::kSameDeviceStrength = 1000; const int BaseGPUDeviceFactory::InterconnectMap::kStreamExecutorStrength = 1; diff --git a/tensorflow/core/common_runtime/gpu/gpu_device.h b/tensorflow/core/common_runtime/gpu/gpu_device.h index 86622dad49..d002d02c51 100644 --- a/tensorflow/core/common_runtime/gpu/gpu_device.h +++ b/tensorflow/core/common_runtime/gpu/gpu_device.h @@ -26,7 +26,6 @@ limitations under the License. #include #include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor" -#include "cuda/include/cuda_runtime_api.h" #include "tensorflow/core/common_runtime/device_factory.h" #include "tensorflow/core/common_runtime/gpu/gpu_event_mgr.h" #include "tensorflow/core/common_runtime/gpu/gpu_id.h" @@ -122,12 +121,6 @@ class BaseGPUDevice : public LocalDevice { se::StreamExecutor* executor_; // not owned std::unique_ptr scoped_allocator_mgr_; - // Returns a Status corresponding to a cudaError_t. The CUDA error must have - // been obtained from a CUDA kernel launch used to check if the GPU is - // initialized properly. - virtual Status CudaErrorToStatus(cudaError_t cuda_error, - const se::Stream& stream); - private: struct StreamGroup { se::Stream* compute = nullptr; @@ -168,10 +161,6 @@ class BaseGPUDevice : public LocalDevice { Status MaybeCopyTensorToGPU(const AllocatorAttributes& alloc_attrs, const Tensor& from, Tensor* to, StatusCallback done); - - // Checks that the GPU is capable of doing work, by running a test kernel on - // it. - Status CheckGPU(); }; class BaseGPUDeviceFactory : public DeviceFactory { diff --git a/tensorflow/core/common_runtime/gpu/gpu_device_kernel_check.cu.cc b/tensorflow/core/common_runtime/gpu/gpu_device_kernel_check.cu.cc deleted file mode 100644 index 017565195b..0000000000 --- a/tensorflow/core/common_runtime/gpu/gpu_device_kernel_check.cu.cc +++ /dev/null @@ -1,37 +0,0 @@ -/* Copyright 2018 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. -==============================================================================*/ - -#if GOOGLE_CUDA - -#include "tensorflow/core/common_runtime/gpu/gpu_device_kernel_check.h" -#include "tensorflow/stream_executor/cuda/cuda_activation.h" - -namespace { -__global__ void test_kernel(float* val) { - if (blockIdx.x == 0 && threadIdx.x == 0) { - (*val) = 12345.; - } -} -} // namespace - -namespace tensorflow { - -void run_test_kernel(float* val, cudaStream_t cu_stream) { - test_kernel<<<1, 1, 0, cu_stream>>>(val); -} - -} // namespace tensorflow - -#endif // GOOGLE_CUDA diff --git a/tensorflow/core/common_runtime/gpu/gpu_device_kernel_check.h b/tensorflow/core/common_runtime/gpu/gpu_device_kernel_check.h deleted file mode 100644 index 064fb7a49f..0000000000 --- a/tensorflow/core/common_runtime/gpu/gpu_device_kernel_check.h +++ /dev/null @@ -1,32 +0,0 @@ -/* Copyright 2018 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_COMMON_RUNTIME_GPU_GPU_DEVICE_KERNEL_CHECK_H_ -#define TENSORFLOW_CORE_COMMON_RUNTIME_GPU_GPU_DEVICE_KERNEL_CHECK_H_ - -#if GOOGLE_CUDA - -#include "tensorflow/core/platform/stream_executor.h" - -namespace tensorflow { - -// Runs a GPU kernel to test that it functions correctly. Sets 'val' to 12345. -void run_test_kernel(float* val, cudaStream_t cu_stream); - -} // namespace tensorflow - -#endif // GOOGLE_CUDA - -#endif // TENSORFLOW_CORE_COMMON_RUNTIME_GPU_GPU_DEVICE_KERNEL_CHECK_H_ -- GitLab From 4fe7980e6b33d075a4a9fcfa40310af85f071cff Mon Sep 17 00:00:00 2001 From: Smit Hinsu Date: Wed, 9 Jan 2019 12:09:12 -0800 Subject: [PATCH 562/622] Automated rollback of commit 7009febfed6721cbf664b1ccfd44358392c3d5ca PiperOrigin-RevId: 228562832 --- tensorflow/contrib/tensorrt/BUILD | 1 - 1 file changed, 1 deletion(-) diff --git a/tensorflow/contrib/tensorrt/BUILD b/tensorflow/contrib/tensorrt/BUILD index c17dea02f1..3d34b91a74 100644 --- a/tensorflow/contrib/tensorrt/BUILD +++ b/tensorflow/contrib/tensorrt/BUILD @@ -551,7 +551,6 @@ cuda_py_test( ], tags = [ "no_cuda_on_cpu_tap", - "no_oss", # TODO(b/121194394): re-enable in OSS after OOM is fixed. "no_pip", "no_tap", # It is not able to download the mnist data. "no_windows", -- GitLab From 57d4745d24195699f93d1674171a03345269d2c1 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Wed, 9 Jan 2019 12:10:02 -0800 Subject: [PATCH 563/622] Compute reshape permutation statically. Aside from presumably being more efficient generally, this enables using the routines in this file inside of XLA. (which currently throws an error `Input 0 to InvertPermutation operator must be a compile-time constant.`) PiperOrigin-RevId: 228563007 --- tensorflow/python/ops/linalg/linear_operator_util.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tensorflow/python/ops/linalg/linear_operator_util.py b/tensorflow/python/ops/linalg/linear_operator_util.py index 54d04e4a70..6c18943dab 100644 --- a/tensorflow/python/ops/linalg/linear_operator_util.py +++ b/tensorflow/python/ops/linalg/linear_operator_util.py @@ -481,9 +481,9 @@ def _reshape_for_efficiency(a, # Permutation to put the extra dims at the end. perm = ( - array_ops.concat( - (math_ops.range(b_extra_ndims, b.shape.ndims), - math_ops.range(0, b_extra_ndims)), 0)) + np.concatenate( + (np.arange(b_extra_ndims, b.shape.ndims), + np.arange(0, b_extra_ndims)), 0)) b_extra_on_end = array_ops.transpose(b, perm=perm) # Now squash this end into one long dim. @@ -497,7 +497,7 @@ def _reshape_for_efficiency(a, y_extra_shape = array_ops.concat( (array_ops.shape(y)[:-1], [b_main_sh[-1]], b_extra_sh), 0) y_extra_on_end = array_ops.reshape(y, y_extra_shape) - return array_ops.transpose( - y_extra_on_end, perm=array_ops.invert_permutation(perm)) + inverse_perm = np.argsort(perm) + return array_ops.transpose(y_extra_on_end, perm=inverse_perm) return a, b_squashed_end, reshape_inv, still_need_to_transpose -- GitLab From 01d9a34786ea0da087855f88ac32d0d7085c8266 Mon Sep 17 00:00:00 2001 From: Nick Felt Date: Wed, 9 Jan 2019 12:16:00 -0800 Subject: [PATCH 564/622] Mark tf.summary v1 API classes and tf.train.summary_iterator() as v1-only The tf.summary.FileWriter and FileWriterCache classes are only for v1 summary writing; TF 2.0 provides the v2 create_file_writer() API instead. The tf.summary python protos are being removed as part of the overall effort to remove protos from the TF 2.0 python API. The recourse for users will be accessing these bindings from within TensorBoard - the exact package path is still TBD, but when we have one we can add a migration hint. As for tf.train.summary_iterator(), this can be replaced by just using tf.io.record_iterator() and parsing each record with Event.FromString() using the Event proto symbol, wherever in TensorBoard it ends up. PiperOrigin-RevId: 228564320 --- tensorflow/python/__init__.py | 10 +- tensorflow/python/summary/summary.py | 5 +- tensorflow/python/summary/summary_iterator.py | 2 +- tensorflow/python/summary/writer/writer.py | 2 +- .../python/summary/writer/writer_cache.py | 2 +- .../python/tools/api/generator/doc_srcs.py | 1 + .../api/golden/v2/tensorflow.-event.pbtxt | 74 --------- ...rflow.-summary-metadata.-plugin-data.pbtxt | 18 --- .../v2/tensorflow.-summary-metadata.pbtxt | 40 ----- .../v2/tensorflow.-summary.-audio.pbtxt | 36 ----- .../v2/tensorflow.-summary.-image.pbtxt | 30 ---- .../v2/tensorflow.-summary.-value.pbtxt | 74 --------- .../api/golden/v2/tensorflow.-summary.pbtxt | 144 ------------------ .../tools/api/golden/v2/tensorflow.pbtxt | 12 -- .../golden/v2/tensorflow.summary.-event.pbtxt | 74 --------- ...ensorflow.summary.-file-writer-cache.pbtxt | 16 -- .../v2/tensorflow.summary.-file-writer.pbtxt | 50 ------ ...sorflow.summary.-summary-description.pbtxt | 12 -- .../tensorflow.summary.-summary.-audio.pbtxt | 36 ----- .../tensorflow.summary.-summary.-image.pbtxt | 30 ---- .../tensorflow.summary.-summary.-value.pbtxt | 74 --------- .../v2/tensorflow.summary.-summary.pbtxt | 144 ------------------ ...sorflow.summary.-tagged-run-metadata.pbtxt | 18 --- .../api/golden/v2/tensorflow.summary.pbtxt | 24 --- .../api/golden/v2/tensorflow.train.pbtxt | 4 - tensorflow/tools/compatibility/renames_v2.py | 10 ++ 26 files changed, 22 insertions(+), 920 deletions(-) delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.-event.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.-summary-metadata.-plugin-data.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.-summary-metadata.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.-summary.-audio.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.-summary.-image.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.-summary.-value.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.-summary.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.summary.-event.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.summary.-file-writer-cache.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.summary.-file-writer.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.summary.-summary-description.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.summary.-summary.-audio.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.summary.-summary.-image.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.summary.-summary.-value.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.summary.-summary.pbtxt delete mode 100644 tensorflow/tools/api/golden/v2/tensorflow.summary.-tagged-run-metadata.pbtxt diff --git a/tensorflow/python/__init__.py b/tensorflow/python/__init__.py index 207123cb14..31f9dce8e8 100644 --- a/tensorflow/python/__init__.py +++ b/tensorflow/python/__init__.py @@ -150,7 +150,7 @@ nn.rnn_cell = rnn_cell # pylint: disable=undefined-variable tf_export(v1=['AttrValue'])(AttrValue) tf_export(v1=['ConfigProto'])(ConfigProto) -tf_export('Event', 'summary.Event')(Event) +tf_export(v1=['Event', 'summary.Event'])(Event) tf_export(v1=['GPUOptions'])(GPUOptions) tf_export(v1=['GraphDef'])(GraphDef) tf_export(v1=['GraphOptions'])(GraphOptions) @@ -163,10 +163,10 @@ tf_export(v1=['OptimizerOptions'])(OptimizerOptions) tf_export(v1=['RunMetadata'])(RunMetadata) tf_export(v1=['RunOptions'])(RunOptions) tf_export(v1=['SessionLog', 'summary.SessionLog'])(SessionLog) -tf_export('Summary', 'summary.Summary')(Summary) -tf_export('summary.SummaryDescription')(SummaryDescription) -tf_export('SummaryMetadata')(SummaryMetadata) -tf_export('summary.TaggedRunMetadata')(TaggedRunMetadata) +tf_export(v1=['Summary', 'summary.Summary'])(Summary) +tf_export(v1=['summary.SummaryDescription'])(SummaryDescription) +tf_export(v1=['SummaryMetadata'])(SummaryMetadata) +tf_export(v1=['summary.TaggedRunMetadata'])(TaggedRunMetadata) tf_export(v1=['TensorInfo'])(TensorInfo) # pylint: enable=undefined-variable diff --git a/tensorflow/python/summary/summary.py b/tensorflow/python/summary/summary.py index 0c13016712..a01feb3dde 100644 --- a/tensorflow/python/summary/summary.py +++ b/tensorflow/python/summary/summary.py @@ -13,9 +13,10 @@ # limitations under the License. # ============================================================================== -"""Tensor summaries for exporting information about a model. +"""Operations for writing summary data, for use in analysis and visualization. -See the [Summary](https://tensorflow.org/api_guides/python/summary) guide. +See the [Summaries and +TensorBoard](https://www.tensorflow.org/guide/summaries_and_tensorboard) guide. """ from __future__ import absolute_import diff --git a/tensorflow/python/summary/summary_iterator.py b/tensorflow/python/summary/summary_iterator.py index 321b11ffb7..3675c235cf 100644 --- a/tensorflow/python/summary/summary_iterator.py +++ b/tensorflow/python/summary/summary_iterator.py @@ -24,7 +24,7 @@ from tensorflow.python.lib.io import tf_record from tensorflow.python.util.tf_export import tf_export -@tf_export('train.summary_iterator') +@tf_export(v1=['train.summary_iterator']) def summary_iterator(path): # pylint: disable=line-too-long """An iterator for reading `Event` protocol buffers from an event file. diff --git a/tensorflow/python/summary/writer/writer.py b/tensorflow/python/summary/writer/writer.py index 78217b503f..a66be4f833 100644 --- a/tensorflow/python/summary/writer/writer.py +++ b/tensorflow/python/summary/writer/writer.py @@ -279,7 +279,7 @@ class SummaryToEventTransformer(object): self.event_writer.add_event(event) -@tf_export("summary.FileWriter") +@tf_export(v1=["summary.FileWriter"]) class FileWriter(SummaryToEventTransformer): """Writes `Summary` protocol buffers to event files. diff --git a/tensorflow/python/summary/writer/writer_cache.py b/tensorflow/python/summary/writer/writer_cache.py index 645fa28a37..c62a7ce1a3 100644 --- a/tensorflow/python/summary/writer/writer_cache.py +++ b/tensorflow/python/summary/writer/writer_cache.py @@ -25,7 +25,7 @@ from tensorflow.python.summary.writer.writer import FileWriter from tensorflow.python.util.tf_export import tf_export -@tf_export('summary.FileWriterCache') +@tf_export(v1=['summary.FileWriterCache']) class FileWriterCache(object): """Cache for file writers. diff --git a/tensorflow/python/tools/api/generator/doc_srcs.py b/tensorflow/python/tools/api/generator/doc_srcs.py index b567eead3d..28bf0e9d01 100644 --- a/tensorflow/python/tools/api/generator/doc_srcs.py +++ b/tensorflow/python/tools/api/generator/doc_srcs.py @@ -61,6 +61,7 @@ _TENSORFLOW_DOC_SOURCES = { 'signal': DocSource(docstring_module_name='ops.signal.signal'), 'sparse': DocSource(docstring_module_name='ops.sparse_ops'), 'strings': DocSource(docstring_module_name='ops.string_ops'), + 'summary': DocSource(docstring_module_name='summary.summary'), 'sysconfig': DocSource(docstring_module_name='platform.sysconfig'), 'test': DocSource(docstring_module_name='platform.test'), 'train': DocSource(docstring_module_name='training.training'), diff --git a/tensorflow/tools/api/golden/v2/tensorflow.-event.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.-event.pbtxt deleted file mode 100644 index 3b75a1735b..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.-event.pbtxt +++ /dev/null @@ -1,74 +0,0 @@ -path: "tensorflow.Event" -tf_proto { - descriptor { - name: "Event" - field { - name: "wall_time" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_DOUBLE - } - field { - name: "step" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "file_version" - number: 3 - label: LABEL_OPTIONAL - type: TYPE_STRING - oneof_index: 0 - } - field { - name: "graph_def" - number: 4 - label: LABEL_OPTIONAL - type: TYPE_BYTES - oneof_index: 0 - } - field { - name: "summary" - number: 5 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.Summary" - oneof_index: 0 - } - field { - name: "log_message" - number: 6 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.LogMessage" - oneof_index: 0 - } - field { - name: "session_log" - number: 7 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.SessionLog" - oneof_index: 0 - } - field { - name: "tagged_run_metadata" - number: 8 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.TaggedRunMetadata" - oneof_index: 0 - } - field { - name: "meta_graph_def" - number: 9 - label: LABEL_OPTIONAL - type: TYPE_BYTES - oneof_index: 0 - } - oneof_decl { - name: "what" - } - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.-summary-metadata.-plugin-data.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.-summary-metadata.-plugin-data.pbtxt deleted file mode 100644 index a66b74b315..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.-summary-metadata.-plugin-data.pbtxt +++ /dev/null @@ -1,18 +0,0 @@ -path: "tensorflow.SummaryMetadata.PluginData" -tf_proto { - descriptor { - name: "PluginData" - field { - name: "plugin_name" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - field { - name: "content" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_BYTES - } - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.-summary-metadata.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.-summary-metadata.pbtxt deleted file mode 100644 index c02575b962..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.-summary-metadata.pbtxt +++ /dev/null @@ -1,40 +0,0 @@ -path: "tensorflow.SummaryMetadata" -tf_proto { - descriptor { - name: "SummaryMetadata" - field { - name: "plugin_data" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.SummaryMetadata.PluginData" - } - field { - name: "display_name" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - field { - name: "summary_description" - number: 3 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - nested_type { - name: "PluginData" - field { - name: "plugin_name" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - field { - name: "content" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_BYTES - } - } - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.-summary.-audio.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.-summary.-audio.pbtxt deleted file mode 100644 index 94f712073e..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.-summary.-audio.pbtxt +++ /dev/null @@ -1,36 +0,0 @@ -path: "tensorflow.Summary.Audio" -tf_proto { - descriptor { - name: "Audio" - field { - name: "sample_rate" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_FLOAT - } - field { - name: "num_channels" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "length_frames" - number: 3 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "encoded_audio_string" - number: 4 - label: LABEL_OPTIONAL - type: TYPE_BYTES - } - field { - name: "content_type" - number: 5 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.-summary.-image.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.-summary.-image.pbtxt deleted file mode 100644 index fc1acb483b..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.-summary.-image.pbtxt +++ /dev/null @@ -1,30 +0,0 @@ -path: "tensorflow.Summary.Image" -tf_proto { - descriptor { - name: "Image" - field { - name: "height" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_INT32 - } - field { - name: "width" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_INT32 - } - field { - name: "colorspace" - number: 3 - label: LABEL_OPTIONAL - type: TYPE_INT32 - } - field { - name: "encoded_image_string" - number: 4 - label: LABEL_OPTIONAL - type: TYPE_BYTES - } - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.-summary.-value.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.-summary.-value.pbtxt deleted file mode 100644 index feb84b6ee9..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.-summary.-value.pbtxt +++ /dev/null @@ -1,74 +0,0 @@ -path: "tensorflow.Summary.Value" -tf_proto { - descriptor { - name: "Value" - field { - name: "node_name" - number: 7 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - field { - name: "tag" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - field { - name: "metadata" - number: 9 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.SummaryMetadata" - } - field { - name: "simple_value" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_FLOAT - oneof_index: 0 - } - field { - name: "obsolete_old_style_histogram" - number: 3 - label: LABEL_OPTIONAL - type: TYPE_BYTES - oneof_index: 0 - } - field { - name: "image" - number: 4 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.Summary.Image" - oneof_index: 0 - } - field { - name: "histo" - number: 5 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.HistogramProto" - oneof_index: 0 - } - field { - name: "audio" - number: 6 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.Summary.Audio" - oneof_index: 0 - } - field { - name: "tensor" - number: 8 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.TensorProto" - oneof_index: 0 - } - oneof_decl { - name: "value" - } - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.-summary.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.-summary.pbtxt deleted file mode 100644 index b2bdff7171..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.-summary.pbtxt +++ /dev/null @@ -1,144 +0,0 @@ -path: "tensorflow.Summary" -tf_proto { - descriptor { - name: "Summary" - field { - name: "value" - number: 1 - label: LABEL_REPEATED - type: TYPE_MESSAGE - type_name: ".tensorflow.Summary.Value" - } - nested_type { - name: "Image" - field { - name: "height" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_INT32 - } - field { - name: "width" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_INT32 - } - field { - name: "colorspace" - number: 3 - label: LABEL_OPTIONAL - type: TYPE_INT32 - } - field { - name: "encoded_image_string" - number: 4 - label: LABEL_OPTIONAL - type: TYPE_BYTES - } - } - nested_type { - name: "Audio" - field { - name: "sample_rate" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_FLOAT - } - field { - name: "num_channels" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "length_frames" - number: 3 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "encoded_audio_string" - number: 4 - label: LABEL_OPTIONAL - type: TYPE_BYTES - } - field { - name: "content_type" - number: 5 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - } - nested_type { - name: "Value" - field { - name: "node_name" - number: 7 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - field { - name: "tag" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - field { - name: "metadata" - number: 9 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.SummaryMetadata" - } - field { - name: "simple_value" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_FLOAT - oneof_index: 0 - } - field { - name: "obsolete_old_style_histogram" - number: 3 - label: LABEL_OPTIONAL - type: TYPE_BYTES - oneof_index: 0 - } - field { - name: "image" - number: 4 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.Summary.Image" - oneof_index: 0 - } - field { - name: "histo" - number: 5 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.HistogramProto" - oneof_index: 0 - } - field { - name: "audio" - number: 6 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.Summary.Audio" - oneof_index: 0 - } - field { - name: "tensor" - number: 8 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.TensorProto" - oneof_index: 0 - } - oneof_decl { - name: "value" - } - } - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.pbtxt index 0cf1fe0e3c..7e5f86d7e6 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.pbtxt @@ -8,10 +8,6 @@ tf_module { name: "DType" mtype: "" } - member { - name: "Event" - mtype: "" - } member { name: "GradientTape" mtype: "" @@ -40,14 +36,6 @@ tf_module { name: "SparseTensor" mtype: "" } - member { - name: "Summary" - mtype: "" - } - member { - name: "SummaryMetadata" - mtype: "" - } member { name: "Tensor" mtype: "" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.summary.-event.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.summary.-event.pbtxt deleted file mode 100644 index eb99d0f533..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.summary.-event.pbtxt +++ /dev/null @@ -1,74 +0,0 @@ -path: "tensorflow.summary.Event" -tf_proto { - descriptor { - name: "Event" - field { - name: "wall_time" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_DOUBLE - } - field { - name: "step" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "file_version" - number: 3 - label: LABEL_OPTIONAL - type: TYPE_STRING - oneof_index: 0 - } - field { - name: "graph_def" - number: 4 - label: LABEL_OPTIONAL - type: TYPE_BYTES - oneof_index: 0 - } - field { - name: "summary" - number: 5 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.Summary" - oneof_index: 0 - } - field { - name: "log_message" - number: 6 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.LogMessage" - oneof_index: 0 - } - field { - name: "session_log" - number: 7 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.SessionLog" - oneof_index: 0 - } - field { - name: "tagged_run_metadata" - number: 8 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.TaggedRunMetadata" - oneof_index: 0 - } - field { - name: "meta_graph_def" - number: 9 - label: LABEL_OPTIONAL - type: TYPE_BYTES - oneof_index: 0 - } - oneof_decl { - name: "what" - } - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.summary.-file-writer-cache.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.summary.-file-writer-cache.pbtxt deleted file mode 100644 index 2a5b63dcea..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.summary.-file-writer-cache.pbtxt +++ /dev/null @@ -1,16 +0,0 @@ -path: "tensorflow.summary.FileWriterCache" -tf_class { - is_instance: "" - is_instance: "" - member_method { - name: "__init__" - } - member_method { - name: "clear" - argspec: "args=[], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "get" - argspec: "args=[\'logdir\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.summary.-file-writer.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.summary.-file-writer.pbtxt deleted file mode 100644 index 6b65b0ace3..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.summary.-file-writer.pbtxt +++ /dev/null @@ -1,50 +0,0 @@ -path: "tensorflow.summary.FileWriter" -tf_class { - is_instance: "" - is_instance: "" - is_instance: "" - member_method { - name: "__init__" - argspec: "args=[\'self\', \'logdir\', \'graph\', \'max_queue\', \'flush_secs\', \'graph_def\', \'filename_suffix\', \'session\'], varargs=None, keywords=None, defaults=[\'None\', \'10\', \'120\', \'None\', \'None\', \'None\'], " - } - member_method { - name: "add_event" - argspec: "args=[\'self\', \'event\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "add_graph" - argspec: "args=[\'self\', \'graph\', \'global_step\', \'graph_def\'], varargs=None, keywords=None, defaults=[\'None\', \'None\'], " - } - member_method { - name: "add_meta_graph" - argspec: "args=[\'self\', \'meta_graph_def\', \'global_step\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "add_run_metadata" - argspec: "args=[\'self\', \'run_metadata\', \'tag\', \'global_step\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "add_session_log" - argspec: "args=[\'self\', \'session_log\', \'global_step\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "add_summary" - argspec: "args=[\'self\', \'summary\', \'global_step\'], varargs=None, keywords=None, defaults=[\'None\'], " - } - member_method { - name: "close" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "flush" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "get_logdir" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } - member_method { - name: "reopen" - argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.summary.-summary-description.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.summary.-summary-description.pbtxt deleted file mode 100644 index 4a8b59cf02..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.summary.-summary-description.pbtxt +++ /dev/null @@ -1,12 +0,0 @@ -path: "tensorflow.summary.SummaryDescription" -tf_proto { - descriptor { - name: "SummaryDescription" - field { - name: "type_hint" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.summary.-summary.-audio.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.summary.-summary.-audio.pbtxt deleted file mode 100644 index 8b271cf58f..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.summary.-summary.-audio.pbtxt +++ /dev/null @@ -1,36 +0,0 @@ -path: "tensorflow.summary.Summary.Audio" -tf_proto { - descriptor { - name: "Audio" - field { - name: "sample_rate" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_FLOAT - } - field { - name: "num_channels" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "length_frames" - number: 3 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "encoded_audio_string" - number: 4 - label: LABEL_OPTIONAL - type: TYPE_BYTES - } - field { - name: "content_type" - number: 5 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.summary.-summary.-image.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.summary.-summary.-image.pbtxt deleted file mode 100644 index dbbc02dd05..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.summary.-summary.-image.pbtxt +++ /dev/null @@ -1,30 +0,0 @@ -path: "tensorflow.summary.Summary.Image" -tf_proto { - descriptor { - name: "Image" - field { - name: "height" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_INT32 - } - field { - name: "width" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_INT32 - } - field { - name: "colorspace" - number: 3 - label: LABEL_OPTIONAL - type: TYPE_INT32 - } - field { - name: "encoded_image_string" - number: 4 - label: LABEL_OPTIONAL - type: TYPE_BYTES - } - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.summary.-summary.-value.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.summary.-summary.-value.pbtxt deleted file mode 100644 index 4176171cd9..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.summary.-summary.-value.pbtxt +++ /dev/null @@ -1,74 +0,0 @@ -path: "tensorflow.summary.Summary.Value" -tf_proto { - descriptor { - name: "Value" - field { - name: "node_name" - number: 7 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - field { - name: "tag" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - field { - name: "metadata" - number: 9 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.SummaryMetadata" - } - field { - name: "simple_value" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_FLOAT - oneof_index: 0 - } - field { - name: "obsolete_old_style_histogram" - number: 3 - label: LABEL_OPTIONAL - type: TYPE_BYTES - oneof_index: 0 - } - field { - name: "image" - number: 4 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.Summary.Image" - oneof_index: 0 - } - field { - name: "histo" - number: 5 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.HistogramProto" - oneof_index: 0 - } - field { - name: "audio" - number: 6 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.Summary.Audio" - oneof_index: 0 - } - field { - name: "tensor" - number: 8 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.TensorProto" - oneof_index: 0 - } - oneof_decl { - name: "value" - } - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.summary.-summary.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.summary.-summary.pbtxt deleted file mode 100644 index d6c5e3a87a..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.summary.-summary.pbtxt +++ /dev/null @@ -1,144 +0,0 @@ -path: "tensorflow.summary.Summary" -tf_proto { - descriptor { - name: "Summary" - field { - name: "value" - number: 1 - label: LABEL_REPEATED - type: TYPE_MESSAGE - type_name: ".tensorflow.Summary.Value" - } - nested_type { - name: "Image" - field { - name: "height" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_INT32 - } - field { - name: "width" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_INT32 - } - field { - name: "colorspace" - number: 3 - label: LABEL_OPTIONAL - type: TYPE_INT32 - } - field { - name: "encoded_image_string" - number: 4 - label: LABEL_OPTIONAL - type: TYPE_BYTES - } - } - nested_type { - name: "Audio" - field { - name: "sample_rate" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_FLOAT - } - field { - name: "num_channels" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "length_frames" - number: 3 - label: LABEL_OPTIONAL - type: TYPE_INT64 - } - field { - name: "encoded_audio_string" - number: 4 - label: LABEL_OPTIONAL - type: TYPE_BYTES - } - field { - name: "content_type" - number: 5 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - } - nested_type { - name: "Value" - field { - name: "node_name" - number: 7 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - field { - name: "tag" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - field { - name: "metadata" - number: 9 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.SummaryMetadata" - } - field { - name: "simple_value" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_FLOAT - oneof_index: 0 - } - field { - name: "obsolete_old_style_histogram" - number: 3 - label: LABEL_OPTIONAL - type: TYPE_BYTES - oneof_index: 0 - } - field { - name: "image" - number: 4 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.Summary.Image" - oneof_index: 0 - } - field { - name: "histo" - number: 5 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.HistogramProto" - oneof_index: 0 - } - field { - name: "audio" - number: 6 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.Summary.Audio" - oneof_index: 0 - } - field { - name: "tensor" - number: 8 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".tensorflow.TensorProto" - oneof_index: 0 - } - oneof_decl { - name: "value" - } - } - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.summary.-tagged-run-metadata.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.summary.-tagged-run-metadata.pbtxt deleted file mode 100644 index 27c8873320..0000000000 --- a/tensorflow/tools/api/golden/v2/tensorflow.summary.-tagged-run-metadata.pbtxt +++ /dev/null @@ -1,18 +0,0 @@ -path: "tensorflow.summary.TaggedRunMetadata" -tf_proto { - descriptor { - name: "TaggedRunMetadata" - field { - name: "tag" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_STRING - } - field { - name: "run_metadata" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_BYTES - } - } -} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.summary.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.summary.pbtxt index 61670bd151..c59f1b8474 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.summary.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.summary.pbtxt @@ -1,33 +1,9 @@ path: "tensorflow.summary" tf_module { - member { - name: "Event" - mtype: "" - } - member { - name: "FileWriter" - mtype: "" - } - member { - name: "FileWriterCache" - mtype: "" - } - member { - name: "Summary" - mtype: "" - } - member { - name: "SummaryDescription" - mtype: "" - } member { name: "SummaryWriter" mtype: "" } - member { - name: "TaggedRunMetadata" - mtype: "" - } member_method { name: "create_file_writer" argspec: "args=[\'logdir\', \'max_queue\', \'flush_millis\', \'filename_suffix\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.train.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.train.pbtxt index c72564e598..a3ace15ca2 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.train.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.train.pbtxt @@ -140,8 +140,4 @@ tf_module { name: "sdca_shrink_l1" argspec: "args=[\'weights\', \'l1\', \'l2\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } - member_method { - name: "summary_iterator" - argspec: "args=[\'path\'], varargs=None, keywords=None, defaults=None" - } } diff --git a/tensorflow/tools/compatibility/renames_v2.py b/tensorflow/tools/compatibility/renames_v2.py index e9085e94b3..b757699f63 100644 --- a/tensorflow/tools/compatibility/renames_v2.py +++ b/tensorflow/tools/compatibility/renames_v2.py @@ -34,6 +34,7 @@ renames = { 'tf.ConfigProto': 'tf.compat.v1.ConfigProto', 'tf.DeviceSpec': 'tf.compat.v1.DeviceSpec', 'tf.Dimension': 'tf.compat.v1.Dimension', + 'tf.Event': 'tf.compat.v1.Event', 'tf.FIFOQueue': 'tf.queue.FIFOQueue', 'tf.FixedLenFeature': 'tf.io.FixedLenFeature', 'tf.FixedLenSequenceFeature': 'tf.io.FixedLenSequenceFeature', @@ -73,6 +74,8 @@ renames = { 'tf.SparseConditionalAccumulator': 'tf.sparse.SparseConditionalAccumulator', 'tf.SparseFeature': 'tf.io.SparseFeature', 'tf.SparseTensorValue': 'tf.compat.v1.SparseTensorValue', + 'tf.Summary': 'tf.compat.v1.Summary', + 'tf.SummaryMetadata': 'tf.compat.v1.SummaryMetadata', 'tf.TFRecordReader': 'tf.compat.v1.TFRecordReader', 'tf.TensorInfo': 'tf.compat.v1.TensorInfo', 'tf.TextLineReader': 'tf.compat.v1.TextLineReader', @@ -608,8 +611,14 @@ renames = { 'tf.string_strip': 'tf.strings.strip', 'tf.string_to_hash_bucket_fast': 'tf.strings.to_hash_bucket_fast', 'tf.string_to_hash_bucket_strong': 'tf.strings.to_hash_bucket_strong', + 'tf.summary.Event': 'tf.compat.v1.summary.Event', + 'tf.summary.FileWriter': 'tf.compat.v1.summary.FileWriter', + 'tf.summary.FileWriterCache': 'tf.compat.v1.summary.FileWriterCache', 'tf.summary.SessionLog': 'tf.compat.v1.summary.SessionLog', 'tf.summary.audio': 'tf.compat.v1.summary.audio', + 'tf.summary.Summary': 'tf.compat.v1.summary.Summary', + 'tf.summary.SummaryDescription': 'tf.compat.v1.summary.SummaryDescription', + 'tf.summary.TaggedRunMetadata': 'tf.compat.v1.summary.TaggedRunMetadata', 'tf.summary.get_summary_description': 'tf.compat.v1.summary.get_summary_description', 'tf.summary.histogram': 'tf.compat.v1.summary.histogram', 'tf.summary.image': 'tf.compat.v1.summary.image', @@ -713,6 +722,7 @@ renames = { 'tf.train.slice_input_producer': 'tf.compat.v1.train.slice_input_producer', 'tf.train.start_queue_runners': 'tf.compat.v1.train.start_queue_runners', 'tf.train.string_input_producer': 'tf.compat.v1.train.string_input_producer', + 'tf.train.summary_iterator': 'tf.compat.v1.train.summary_iterator', 'tf.train.update_checkpoint_state': 'tf.compat.v1.train.update_checkpoint_state', 'tf.train.warm_start': 'tf.compat.v1.train.warm_start', 'tf.train.write_graph': 'tf.io.write_graph', -- GitLab From 9a5dcb4c6b7b60557e996106402c46475307c4cd Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Wed, 9 Jan 2019 12:18:30 -0800 Subject: [PATCH 565/622] A new Eager C API: Profiler. The profiler starts profiling when it is created. It stops when it is destroyed. Its output format is tfprof proto. PiperOrigin-RevId: 228564754 --- tensorflow/c/eager/BUILD | 32 +++++++- tensorflow/c/eager/c_api.cc | 2 +- tensorflow/c/eager/c_api_experimental.cc | 23 ++++++ tensorflow/c/eager/c_api_experimental.h | 18 +++++ tensorflow/c/eager/c_api_experimental_test.cc | 79 +++++++++++++++++++ tensorflow/c/eager/c_api_internal.h | 8 ++ tensorflow/core/common_runtime/eager/BUILD | 28 +++++++ .../core/common_runtime/eager/context.cc | 32 +++++++- .../core/common_runtime/eager/context.h | 17 +++- .../core/common_runtime/eager/execute.cc | 2 +- .../core/common_runtime/eager/profiler.cc | 77 ++++++++++++++++++ .../core/common_runtime/eager/profiler.h | 63 +++++++++++++++ tensorflow/python/pywrap_tfe.i | 3 + 13 files changed, 375 insertions(+), 9 deletions(-) create mode 100644 tensorflow/c/eager/c_api_experimental_test.cc create mode 100644 tensorflow/core/common_runtime/eager/profiler.cc create mode 100644 tensorflow/core/common_runtime/eager/profiler.h diff --git a/tensorflow/c/eager/BUILD b/tensorflow/c/eager/BUILD index 9f09ad1fc3..3ea1724d1e 100644 --- a/tensorflow/c/eager/BUILD +++ b/tensorflow/c/eager/BUILD @@ -3,11 +3,10 @@ licenses(["notice"]) # Apache 2.0 load( "//tensorflow:tensorflow.bzl", - "tf_cuda_cc_test", - "tf_cc_test", "tf_copts", - "tfe_xla_copts", + "tf_cuda_cc_test", "tf_cuda_library", + "tfe_xla_copts", ) tf_cuda_library( @@ -29,6 +28,7 @@ tf_cuda_library( "//tensorflow/c:c_api_internal", "//tensorflow/core:core_cpu", "//tensorflow/core/common_runtime/eager:attr_builder", + "//tensorflow/core/common_runtime/eager:profiler", "//tensorflow/core/common_runtime/eager:context", "//tensorflow/core/common_runtime/eager:eager_executor", "//tensorflow/core/common_runtime/eager:execute", @@ -89,6 +89,7 @@ tf_cuda_library( "//tensorflow/core/common_runtime/eager:eager_executor", "//tensorflow/core/common_runtime/eager:eager_operation", "//tensorflow/core/common_runtime/eager:kernel_and_device", + "//tensorflow/core/common_runtime/eager:profiler", "//tensorflow/core/common_runtime/eager:tensor_handle", "//tensorflow/core/distributed_runtime:remote_device", "//tensorflow/core/distributed_runtime:server_lib", @@ -204,6 +205,31 @@ tf_cuda_library( ], ) +tf_cuda_cc_test( + name = "c_api_experimental_test", + size = "small", + srcs = [ + "c_api_experimental_test.cc", + ], + extra_copts = tfe_xla_copts(), + tags = [ + "guitar", + "multi_gpu", + ], + deps = [ + ":c_api_experimental", + ":c_api_test_util", + "//tensorflow/c:c_test_util", + "//tensorflow/cc/profiler", + "//tensorflow/core:lib", + "//tensorflow/core:protos_all_cc", + "//tensorflow/core:test", + "//tensorflow/core:test_main", + "//tensorflow/core/profiler:protos_all_cc", + "@com_google_absl//absl/strings", + ], +) + cc_library( name = "tape", hdrs = ["tape.h"], diff --git a/tensorflow/c/eager/c_api.cc b/tensorflow/c/eager/c_api.cc index 027d752f42..d5a391a98d 100755 --- a/tensorflow/c/eager/c_api.cc +++ b/tensorflow/c/eager/c_api.cc @@ -774,7 +774,7 @@ void TFE_ContextExportRunMetadata(TFE_Context* ctx, TF_Buffer* buf, if (!status->status.ok()) return; tensorflow::mutex_lock ml(*ctx->context.MetadataMu()); status->status = MessageToBuffer(*ctx->context.RunMetadataProto(), buf); - ctx->context.RunMetadataProto()->Clear(); + ctx->context.ClearRunMetadata(); } namespace { diff --git a/tensorflow/c/eager/c_api_experimental.cc b/tensorflow/c/eager/c_api_experimental.cc index 3461d81b93..1ce03fb226 100644 --- a/tensorflow/c/eager/c_api_experimental.cc +++ b/tensorflow/c/eager/c_api_experimental.cc @@ -18,6 +18,29 @@ limitations under the License. #include "tensorflow/c/c_api.h" #include "tensorflow/c/eager/c_api_internal.h" +using tensorflow::string; + void TFE_OpConsumeInput(TFE_Op* op, TFE_TensorHandle* h, TF_Status* status) { op->operation.ConsumeInput(h->handle); } + +TFE_Profiler* TFE_NewProfiler(TFE_Context* ctx) { + return new TFE_Profiler(ctx); +} + +void TFE_DeleteProfiler(TFE_Profiler* profiler) { delete profiler; } + +void TFE_ProfilerSerializeToString(TFE_Context* ctx, TFE_Profiler* profiler, + TF_Buffer* buf, TF_Status* status) { + TFE_ContextAsyncWait(ctx, status); + if (!status->status.ok()) return; + string content; + status->status = profiler->profiler->SerializeToString(&content); + void* data = tensorflow::port::Malloc(content.length()); + content.copy(static_cast(data), content.length(), 0); + buf->data = data; + buf->length = content.length(); + buf->data_deallocator = [](void* data, size_t length) { + tensorflow::port::Free(data); + }; +} diff --git a/tensorflow/c/eager/c_api_experimental.h b/tensorflow/c/eager/c_api_experimental.h index 4ee6c066ee..9eb80f5216 100644 --- a/tensorflow/c/eager/c_api_experimental.h +++ b/tensorflow/c/eager/c_api_experimental.h @@ -25,6 +25,24 @@ extern "C" { TF_CAPI_EXPORT extern void TFE_OpConsumeInput(TFE_Op* op, TFE_TensorHandle* h, TF_Status* status); +// A profiler which will start profiling when creating the object and will stop +// when the object is destroyed. It will profile all operations run under the +// given TFE_Context. Multiple instance of it can be created, but at most one +// of them will profile for each TFE_Context. +// Thread-safety: TFE_Profiler is thread-safe. +typedef struct TFE_Profiler TFE_Profiler; + +TF_CAPI_EXPORT extern TFE_Profiler* TFE_NewProfiler(TFE_Context* ctx); +TF_CAPI_EXPORT extern void TFE_DeleteProfiler(TFE_Profiler* profiler); + +// The output string is a binary string of tensorflow.tfprof.ProfileProto. +// User can write the string to file for offline analysis by tfprof command-line +// tools or graphical user interface. +TF_CAPI_EXPORT extern void TFE_ProfilerSerializeToString(TFE_Context* ctx, + TFE_Profiler* profiler, + TF_Buffer* buf, + TF_Status* status); + #ifdef __cplusplus } /* end extern "C" */ #endif diff --git a/tensorflow/c/eager/c_api_experimental_test.cc b/tensorflow/c/eager/c_api_experimental_test.cc new file mode 100644 index 0000000000..9b4fca9d45 --- /dev/null +++ b/tensorflow/c/eager/c_api_experimental_test.cc @@ -0,0 +1,79 @@ +/* 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/c/eager/c_api_experimental.h" + +#include +#include "absl/strings/match.h" +#include "tensorflow/c/eager/c_api_test_util.h" +#include "tensorflow/cc/profiler/profiler.h" +#include "tensorflow/core/platform/logging.h" +#include "tensorflow/core/platform/protobuf.h" +#include "tensorflow/core/platform/test.h" +#include "tensorflow/core/platform/test_benchmark.h" +#include "tensorflow/core/profiler/tfprof_log.pb.h" + +using tensorflow::string; + +namespace { + +void ExecuteWithProfiling(bool async) { + TF_Status* status = TF_NewStatus(); + TFE_ContextOptions* opts = TFE_NewContextOptions(); + TFE_ContextOptionsSetAsync(opts, static_cast(async)); + TFE_Context* ctx = TFE_NewContext(opts, status); + TFE_Profiler* profiler = TFE_NewProfiler(ctx); + CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status); + TFE_DeleteContextOptions(opts); + + TFE_TensorHandle* m = TestMatrixTensorHandle(); + TFE_Op* matmul = MatMulOp(ctx, m, m); + TFE_TensorHandle* retvals[1] = {nullptr}; + int num_retvals = 1; + TFE_Execute(matmul, &retvals[0], &num_retvals, status); + EXPECT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status); + TFE_DeleteOp(matmul); + TFE_DeleteTensorHandle(m); + + ASSERT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status); + ASSERT_EQ(1, num_retvals); + TF_Buffer* profiler_result = TF_NewBuffer(); + TFE_ProfilerSerializeToString(ctx, profiler, profiler_result, status); + TFE_DeleteProfiler(profiler); + ASSERT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status); + tensorflow::tfprof::ProfileProto profile_proto; + EXPECT_TRUE(profile_proto.ParseFromString( + {reinterpret_cast(profiler_result->data), + profiler_result->length})); + TF_DeleteBuffer(profiler_result); + + TF_Tensor* t = TFE_TensorHandleResolve(retvals[0], status); + TFE_DeleteTensorHandle(retvals[0]); + TFE_DeleteContext(ctx); + ASSERT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status); + float product[4] = {0}; + EXPECT_EQ(sizeof(product), TF_TensorByteSize(t)); + memcpy(&product[0], TF_TensorData(t), TF_TensorByteSize(t)); + TF_DeleteTensor(t); + EXPECT_EQ(7, product[0]); + EXPECT_EQ(10, product[1]); + EXPECT_EQ(15, product[2]); + EXPECT_EQ(22, product[3]); + TF_DeleteStatus(status); +} +TEST(CAPI, ExecuteWithTracing) { ExecuteWithProfiling(false); } +TEST(CAPI, ExecuteWithTracingAsync) { ExecuteWithProfiling(true); } + +} // namespace diff --git a/tensorflow/c/eager/c_api_internal.h b/tensorflow/c/eager/c_api_internal.h index 67bc1bcd24..7b1035f631 100644 --- a/tensorflow/c/eager/c_api_internal.h +++ b/tensorflow/c/eager/c_api_internal.h @@ -34,6 +34,7 @@ limitations under the License. #include "tensorflow/core/common_runtime/eager/eager_executor.h" #include "tensorflow/core/common_runtime/eager/eager_operation.h" #include "tensorflow/core/common_runtime/eager/kernel_and_device.h" +#include "tensorflow/core/common_runtime/eager/profiler.h" #include "tensorflow/core/common_runtime/eager/tensor_handle.h" #include "tensorflow/core/common_runtime/function.h" #include "tensorflow/core/common_runtime/rendezvous_mgr.h" @@ -100,6 +101,13 @@ struct TFE_Op { tensorflow::EagerOperation operation; }; +struct TFE_Profiler { + TFE_Profiler(TFE_Context* ctx) + : profiler(tensorflow::EagerProfiler::Create(&ctx->context)) {} + + std::unique_ptr profiler; +}; + namespace tensorflow { // Set an AttrValue on the op. Doesn't handle the list types. void SetOpAttrValueScalar(TFE_Context* ctx, TFE_Op* op, diff --git a/tensorflow/core/common_runtime/eager/BUILD b/tensorflow/core/common_runtime/eager/BUILD index 77e3246df0..cabbddb77d 100644 --- a/tensorflow/core/common_runtime/eager/BUILD +++ b/tensorflow/core/common_runtime/eager/BUILD @@ -88,6 +88,34 @@ tf_cuda_library( ], ) +tf_cuda_library( + name = "profiler", + srcs = [ + "profiler.cc", + ], + hdrs = [ + "profiler.h", + ], + visibility = ["//tensorflow:internal"], + deps = [ + ":context", + "//tensorflow/cc/profiler", + ] + select({ + "//tensorflow:android": [ + "//tensorflow/core:android_tensorflow_lib_lite", + ], + "//conditions:default": [ + "//tensorflow/core:core_cpu_lib", + "//tensorflow/core:framework", + "//tensorflow/core:framework_internal", + "//tensorflow/core:lib", + "//tensorflow/core:lib_internal", + "//tensorflow/core:protos_all_cc", + "//tensorflow/core:session_options", + ], + }), +) + tf_cuda_library( name = "tensor_handle", srcs = [ diff --git a/tensorflow/core/common_runtime/eager/context.cc b/tensorflow/core/common_runtime/eager/context.cc index 2212bda534..1d93d2bbe6 100644 --- a/tensorflow/core/common_runtime/eager/context.cc +++ b/tensorflow/core/common_runtime/eager/context.cc @@ -234,6 +234,29 @@ Status EagerContext::FindDeviceByName(const string& name, Device** result) { return Status::OK(); } +void EagerContext::ClearRunMetadata() { + if (metadata_listener_ != nullptr) { + metadata_listener_->BeforeClearRunMetadata(); + } + run_metadata_.Clear(); +} + +Status EagerContext::RegisterRunMetadataListener( + RunMetadataListener* listener) { + mutex_lock l(metadata_mu_); + if (metadata_listener_ != nullptr) { + return Status(error::Code::INVALID_ARGUMENT, + "Cannot run two eager profiler at the same time"); + } + metadata_listener_ = listener; + return Status::OK(); +} + +void EagerContext::ClearRunMetadataListener() { + mutex_lock l(metadata_mu_); + metadata_listener_ = nullptr; +} + void EagerContext::StartStep() { mutex_lock ml(metadata_mu_); num_active_steps_++; @@ -317,10 +340,15 @@ void EagerContext::AddKernelToCache(Fprint128 cache_key, gtl::InsertOrUpdate(&kernel_cache_, cache_key, kernel); } +bool EagerContext::ShouldStoreMetadata() { + mutex_lock ml(metadata_mu_); + return should_store_metadata_.load() || metadata_listener_ != nullptr; +} + void EagerContext::SetShouldStoreMetadata(bool value) { + mutex_lock ml(metadata_mu_); should_store_metadata_.store(value); - if (!value) { - mutex_lock ml(metadata_mu_); + if (!value || metadata_listener_ != nullptr) { run_metadata_.Clear(); } } diff --git a/tensorflow/core/common_runtime/eager/context.h b/tensorflow/core/common_runtime/eager/context.h index 5ff6b3ffbd..b83f5707da 100644 --- a/tensorflow/core/common_runtime/eager/context.h +++ b/tensorflow/core/common_runtime/eager/context.h @@ -29,6 +29,7 @@ limitations under the License. #include "tensorflow/core/common_runtime/eager/kernel_and_device.h" #include "tensorflow/core/common_runtime/function.h" #include "tensorflow/core/common_runtime/rendezvous_mgr.h" +#include "tensorflow/core/example/example.pb.h" #ifndef __ANDROID__ #include "tensorflow/core/distributed_runtime/eager/eager_client.h" #include "tensorflow/core/distributed_runtime/server_lib.h" @@ -66,6 +67,12 @@ enum ContextDevicePlacementPolicy { DEVICE_PLACEMENT_SILENT_FOR_INT32 = 3, }; +class RunMetadataListener { + public: + virtual ~RunMetadataListener() {} + virtual void BeforeClearRunMetadata() = 0; +}; + class EagerContext { public: // TODO: remove this constructor once we migrate all callers to the next one. @@ -172,10 +179,15 @@ class EagerContext { void ReleaseDeviceMgr() { local_device_manager_.release(); } // TODO(apassos) clean up RunMetadata storage. - mutex* MetadataMu() { return &metadata_mu_; } - bool ShouldStoreMetadata() { return should_store_metadata_.load(); } + mutex* MetadataMu() LOCK_RETURNED(metadata_mu_) { return &metadata_mu_; } + bool ShouldStoreMetadata() LOCKS_EXCLUDED(metadata_mu_); void SetShouldStoreMetadata(bool value); RunMetadata* RunMetadataProto() { return &run_metadata_; } + void ClearRunMetadata() EXCLUSIVE_LOCKS_REQUIRED(metadata_mu_); + + Status RegisterRunMetadataListener(RunMetadataListener* listener) + LOCKS_EXCLUDED(metadata_mu_); + void ClearRunMetadataListener() LOCKS_EXCLUDED(metadata_mu_); void StartStep(); void EndStep(); @@ -269,6 +281,7 @@ class EagerContext { std::atomic should_store_metadata_{false}; mutex metadata_mu_; RunMetadata run_metadata_ GUARDED_BY(metadata_mu_); + RunMetadataListener* metadata_listener_ GUARDED_BY(metadata_mu_) = nullptr; GraphCollector graph_collector_; const bool log_device_placement_; // EagerExecutor for async execution. diff --git a/tensorflow/core/common_runtime/eager/execute.cc b/tensorflow/core/common_runtime/eager/execute.cc index 79806c3c73..9d2401929d 100644 --- a/tensorflow/core/common_runtime/eager/execute.cc +++ b/tensorflow/core/common_runtime/eager/execute.cc @@ -752,8 +752,8 @@ Status EagerExecute(EagerContext* ctx, Device* device, maybe_stats->set_all_end_rel_micros(nanos / EnvTime::kMicrosToNanos - maybe_stats->all_start_micros()); maybe_stats->set_all_end_rel_nanos(nanos - maybe_stats->all_start_nanos()); - mutex_lock ml(*ctx->MetadataMu()); if (ctx->ShouldStoreMetadata()) { + mutex_lock ml(*ctx->MetadataMu()); { GraphCollector* collector = ctx->GetGraphCollector(); mutex_lock mll(collector->mu); diff --git a/tensorflow/core/common_runtime/eager/profiler.cc b/tensorflow/core/common_runtime/eager/profiler.cc new file mode 100644 index 0000000000..d670d6f344 --- /dev/null +++ b/tensorflow/core/common_runtime/eager/profiler.cc @@ -0,0 +1,77 @@ +/* Copyright 2018 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/common_runtime/eager/profiler.h" +#include "tensorflow/cc/profiler/profiler.h" +#include "tensorflow/core/framework/graph.pb.h" +#include "tensorflow/core/platform/mutex.h" +#include "tensorflow/core/protobuf/config.pb.h" + +namespace tensorflow { + +/*static*/ std::unique_ptr EagerProfiler::Create( + EagerContext* const context) { + return absl::WrapUnique(new EagerProfiler(context)); +} + +void EagerProfiler::BeforeClearRunMetadata() { + mutex_lock l(mutex_); + run_metadata_.MergeFrom(*context_->RunMetadataProto()); +} + +Status EagerProfiler::Status() { + mutex_lock l(mutex_); + return status_; +} + +Status EagerProfiler::SerializeToString(string* content) { + { + mutex_lock l(mutex_); + if (!status_.ok()) return status_; + } + RunMetadata metadata; + GetMergetRunMetadata(&metadata); + + // TODO(fishx): update tfprof to use a lighter representation instead of + // GraphDef. + GraphDef graph; + std::unique_ptr tfprof(new tfprof::Profiler(graph)); + tfprof->AddStep(0, metadata); + return tfprof->SerializeToString(content); +} + +EagerProfiler::EagerProfiler(EagerContext* const context) : context_(context) { + LOG(INFO) << "Eager Profiler started."; + + status_ = context_->RegisterRunMetadataListener(this); + if (!status_.ok()) { + LOG(INFO) << "Eager Profiler failed to start. Another profiler is running."; + return; + } +} + +EagerProfiler::~EagerProfiler() { + context_->ClearRunMetadataListener(); + LOG(INFO) << "Eager Profiler ended with status:" << status_; +} + +void EagerProfiler::GetMergetRunMetadata(RunMetadata* metadata) { + mutex_lock ml(*context_->MetadataMu()); + mutex_lock l(mutex_); + *metadata = run_metadata_; + metadata->MergeFrom(*context_->RunMetadataProto()); +} + +} // namespace tensorflow diff --git a/tensorflow/core/common_runtime/eager/profiler.h b/tensorflow/core/common_runtime/eager/profiler.h new file mode 100644 index 0000000000..cadbfdb498 --- /dev/null +++ b/tensorflow/core/common_runtime/eager/profiler.h @@ -0,0 +1,63 @@ +/* Copyright 2018 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_COMMON_RUNTIME_EAGER_PROFILER_H_ +#define TENSORFLOW_CORE_COMMON_RUNTIME_EAGER_PROFILER_H_ + +#include "tensorflow/core/common_runtime/eager/context.h" +#include "tensorflow/core/lib/core/status.h" +#include "tensorflow/core/platform/mutex.h" +#include "tensorflow/core/protobuf/config.pb.h" + +namespace tensorflow { + +// A profiler which will start profiling when creating the object and will stop +// when the object is destroyed. It will profile all operations run under the +// given EagerContext. +// Multiple instances of it can be created, but at most one of them will profile +// for each EagerContext. Status() will return OK only for the instance that is +// profiling. +// Thread-safety: TFE_Profiler is thread-safe. +class EagerProfiler : RunMetadataListener { + public: + // Creates and EagerProfiler and starts profiling. + static std::unique_ptr Create(EagerContext* const context); + + // Deletes an exsiting Profiler and enables starting a new one. + ~EagerProfiler() override; + + void BeforeClearRunMetadata() override LOCKS_EXCLUDED(mutex_) + EXCLUSIVE_LOCKS_REQUIRED(context_->MetadataMu()); + tensorflow::Status Status() LOCKS_EXCLUDED(mutex_); + + tensorflow::Status SerializeToString(string* content) LOCKS_EXCLUDED(mutex_); + + private: + // Constructs an instance of the class and starts profiling + explicit EagerProfiler(EagerContext* const context); + + // Profiler is neither copyable or movable. + EagerProfiler(const EagerProfiler&) = delete; + EagerProfiler& operator=(const EagerProfiler&) = delete; + + void GetMergetRunMetadata(RunMetadata* metadata) LOCKS_EXCLUDED(mutex_); + + RunMetadata run_metadata_ GUARDED_BY(mutex_); + tensorflow::Status status_ GUARDED_BY(mutex_); + EagerContext* const context_; + mutex mutex_; +}; + +} // namespace tensorflow +#endif // TENSORFLOW_CORE_COMMON_RUNTIME_EAGER_PROFILER_H_ diff --git a/tensorflow/python/pywrap_tfe.i b/tensorflow/python/pywrap_tfe.i index 733d471ca2..0620e0345c 100755 --- a/tensorflow/python/pywrap_tfe.i +++ b/tensorflow/python/pywrap_tfe.i @@ -32,6 +32,9 @@ limitations under the License. %rename("%s") TFE_ContextSetServerDef; %rename("%s") TFE_ContextAsyncWait; %rename("%s") TFE_ContextAsyncClearError; +%rename("%s") TFE_NewProfiler; +%rename("%s") TFE_DeleteProfiler; +%rename("%s") TFE_ProfilerSerializeToString; %rename("%s") TFE_OpNameGetAttrType; %rename("%s") TFE_Py_InitEagerTensor; %rename("%s") TFE_Py_SetEagerTensorProfiler; -- GitLab From 52718ef7adf0f0a292dcd43432587c818f895ce2 Mon Sep 17 00:00:00 2001 From: Pavithra Vijay Date: Wed, 9 Jan 2019 12:18:32 -0800 Subject: [PATCH 566/622] Add `axis` property to CosineProximity loss and metric. Refactor cosine proximity tests for better clarity. PiperOrigin-RevId: 228564762 --- tensorflow/python/keras/losses.py | 26 ++++-- tensorflow/python/keras/losses_test.py | 93 ++++++++++++------- tensorflow/python/keras/metrics.py | 12 ++- tensorflow/python/keras/metrics_test.py | 58 ++++++++---- ...rflow.keras.losses.-cosine-proximity.pbtxt | 2 +- .../golden/v1/tensorflow.keras.losses.pbtxt | 4 +- ...flow.keras.metrics.-cosine-proximity.pbtxt | 2 +- .../golden/v1/tensorflow.keras.metrics.pbtxt | 4 +- ...rflow.keras.losses.-cosine-proximity.pbtxt | 2 +- .../golden/v2/tensorflow.keras.losses.pbtxt | 4 +- ...flow.keras.metrics.-cosine-proximity.pbtxt | 2 +- .../golden/v2/tensorflow.keras.metrics.pbtxt | 4 +- 12 files changed, 143 insertions(+), 70 deletions(-) diff --git a/tensorflow/python/keras/losses.py b/tensorflow/python/keras/losses.py index 5f9278823c..66780de0f0 100644 --- a/tensorflow/python/keras/losses.py +++ b/tensorflow/python/keras/losses.py @@ -821,15 +821,15 @@ def poisson(y_true, y_pred): 'keras.metrics.cosine', 'keras.losses.cosine_proximity', 'keras.losses.cosine') -def cosine_proximity(y_true, y_pred): - y_true = nn.l2_normalize(y_true, axis=-1) - y_pred = nn.l2_normalize(y_pred, axis=-1) - return -math_ops.reduce_sum(y_true * y_pred, axis=-1) +def cosine_proximity(y_true, y_pred, axis=-1): + y_true = nn.l2_normalize(y_true, axis=axis) + y_pred = nn.l2_normalize(y_pred, axis=axis) + return -math_ops.reduce_sum(y_true * y_pred, axis=axis) @keras_export('keras.losses.CosineProximity') class CosineProximity(Loss): - """Computes the cosine distance between `y_true` and `y_pred`. + """Computes the cosine proximity between `y_true` and `y_pred`. Usage: @@ -845,8 +845,22 @@ class CosineProximity(Loss): model = keras.models.Model(inputs, outputs) model.compile('sgd', loss=tf.losses.CosineProximity()) ``` + + Args: + axis: (Optional) Defaults to -1. The dimension along which the cosine + proximity is computed. + reduction: (Optional) Type of `tf.losses.Reduction` to apply to loss. + Default value is `SUM_OVER_BATCH_SIZE`. + name: Optional name for the op. """ + def __init__(self, + axis=-1, + reduction=losses_impl.ReductionV2.SUM_OVER_BATCH_SIZE, + name=None): + super(CosineProximity, self).__init__(reduction=reduction, name=name) + self.axis = axis + def call(self, y_true, y_pred): """Calculates the cosine proximity loss. @@ -859,7 +873,7 @@ class CosineProximity(Loss): """ y_pred = ops.convert_to_tensor(y_pred) y_true = math_ops.cast(y_true, y_pred.dtype) - return cosine_proximity(y_true, y_pred) + return cosine_proximity(y_true, y_pred, axis=self.axis) # Aliases. diff --git a/tensorflow/python/keras/losses_test.py b/tensorflow/python/keras/losses_test.py index 5d6cd1be55..004c30f84d 100644 --- a/tensorflow/python/keras/losses_test.py +++ b/tensorflow/python/keras/losses_test.py @@ -485,59 +485,88 @@ class MeanSquaredLogarithmicErrorTest(test.TestCase): @test_util.run_all_in_graph_and_eager_modes class CosineProximityTest(test.TestCase): + def l2_norm(self, x, axis): + epsilon = 1e-12 + square_sum = np.sum(np.square(x), axis=axis, keepdims=True) + x_inv_norm = 1 / np.sqrt(np.maximum(square_sum, epsilon)) + return np.multiply(x, x_inv_norm) + + def setup(self, axis=1): + self.np_y_true = np.asarray([[1, 9, 2], [-5, -2, 6]], dtype=np.float32) + self.np_y_pred = np.asarray([[4, 8, 12], [8, 1, 3]], dtype=np.float32) + + y_true = self.l2_norm(self.np_y_true, axis) + y_pred = self.l2_norm(self.np_y_pred, axis) + self.expected_loss = -np.sum(np.multiply(y_true, y_pred), axis=(axis,)) + + self.y_true = constant_op.constant(self.np_y_true) + self.y_pred = constant_op.constant(self.np_y_pred) + def test_config(self): cosine_obj = keras.losses.CosineProximity( - reduction=losses_impl.ReductionV2.SUM, name='cosine_loss') + axis=2, reduction=losses_impl.ReductionV2.SUM, name='cosine_loss') self.assertEqual(cosine_obj.name, 'cosine_loss') self.assertEqual(cosine_obj.reduction, losses_impl.ReductionV2.SUM) + self.assertEqual(cosine_obj.axis, 2) def test_unweighted(self): + self.setup() cosine_obj = keras.losses.CosineProximity() - y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) - y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], - shape=(2, 3), - dtype=dtypes.float32) - loss = cosine_obj(y_true, y_pred) - self.assertAlmostEqual(self.evaluate(loss), -0.18722, 3) + loss = cosine_obj(self.y_true, self.y_pred) + expected_loss = np.mean(self.expected_loss) + self.assertAlmostEqual(self.evaluate(loss), expected_loss, 3) def test_scalar_weighted(self): + self.setup() cosine_obj = keras.losses.CosineProximity() - y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) - y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], - shape=(2, 3), - dtype=dtypes.float32) - loss = cosine_obj(y_true, y_pred, sample_weight=2.3) - self.assertAlmostEqual(self.evaluate(loss), -0.43060, 3) + sample_weight = 2.3 + loss = cosine_obj(self.y_true, self.y_pred, sample_weight=sample_weight) + expected_loss = np.mean(self.expected_loss * sample_weight) + self.assertAlmostEqual(self.evaluate(loss), expected_loss, 3) def test_sample_weighted(self): + self.setup() cosine_obj = keras.losses.CosineProximity() - y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) - y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], - shape=(2, 3), - dtype=dtypes.float32) - sample_weight = constant_op.constant([1.2, 3.4], shape=(2, 1)) - loss = cosine_obj(y_true, y_pred, sample_weight=sample_weight) - self.assertAlmostEqual(self.evaluate(loss), 0.15599, 3) + sample_weight = np.asarray([1.2, 3.4]) + loss = cosine_obj( + self.y_true, + self.y_pred, + sample_weight=constant_op.constant(sample_weight)) + expected_loss = np.mean(self.expected_loss * sample_weight) + self.assertAlmostEqual(self.evaluate(loss), expected_loss, 3) def test_timestep_weighted(self): + self.setup() cosine_obj = keras.losses.CosineProximity() - y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3, 1)) - y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], - shape=(2, 3, 1), - dtype=dtypes.float32) - sample_weight = constant_op.constant([3, 6, 5, 0, 4, 2], shape=(2, 3)) - loss = cosine_obj(y_true, y_pred, sample_weight=sample_weight) - self.assertAlmostEqual(self.evaluate(loss), -2.0000, 3) + np_y_true = self.np_y_true.reshape((2, 3, 1)) + np_y_pred = self.np_y_pred.reshape((2, 3, 1)) + sample_weight = np.asarray([3, 6, 5, 0, 4, 2]).reshape((2, 3)) + + y_true = self.l2_norm(np_y_true, 2) + y_pred = self.l2_norm(np_y_pred, 2) + expected_loss = -np.sum(np.multiply(y_true, y_pred), axis=(2,)) + + y_true = constant_op.constant(np_y_true) + y_pred = constant_op.constant(np_y_pred) + loss = cosine_obj( + y_true, y_pred, sample_weight=constant_op.constant(sample_weight)) + + expected_loss = np.mean(expected_loss * sample_weight) + self.assertAlmostEqual(self.evaluate(loss), expected_loss, 3) def test_zero_weighted(self): + self.setup() cosine_obj = keras.losses.CosineProximity() - y_true = constant_op.constant([1, 9, 2, -5, -2, 6], shape=(2, 3)) - y_pred = constant_op.constant([4, 8, 12, 8, 1, 3], - shape=(2, 3), - dtype=dtypes.float32) - loss = cosine_obj(y_true, y_pred, sample_weight=0) + loss = cosine_obj(self.y_true, self.y_pred, sample_weight=0) self.assertAlmostEqual(self.evaluate(loss), 0., 3) + def test_axis(self): + self.setup(axis=1) + cosine_obj = keras.losses.CosineProximity(axis=1) + loss = cosine_obj(self.y_true, self.y_pred) + expected_loss = np.mean(self.expected_loss) + self.assertAlmostEqual(self.evaluate(loss), expected_loss, 3) + @test_util.run_all_in_graph_and_eager_modes class BinaryCrossentropyTest(test.TestCase): diff --git a/tensorflow/python/keras/metrics.py b/tensorflow/python/keras/metrics.py index 2c2700ffa3..a13d8747d6 100644 --- a/tensorflow/python/keras/metrics.py +++ b/tensorflow/python/keras/metrics.py @@ -1443,8 +1443,16 @@ class CosineProximity(MeanMetricWrapper): ``` """ - def __init__(self, name='cosine_proximity', dtype=None): - super(CosineProximity, self).__init__(cosine, name, dtype=dtype) + def __init__(self, name='cosine_proximity', dtype=None, axis=-1): + """Creates a `CosineProximity` instance. + + Args: + name: (Optional) string name of the metric instance. + dtype: (Optional) data type of the metric result. + axis: (Optional) Defaults to -1. The dimension along which the cosine + proximity is computed. + """ + super(CosineProximity, self).__init__(cosine, name, dtype=dtype, axis=axis) @classmethod def from_config(cls, config): diff --git a/tensorflow/python/keras/metrics_test.py b/tensorflow/python/keras/metrics_test.py index 4f8406f0e3..42da1dfb99 100644 --- a/tensorflow/python/keras/metrics_test.py +++ b/tensorflow/python/keras/metrics_test.py @@ -1049,8 +1049,26 @@ class SpecificityAtSensitivityTest(test.TestCase, parameterized.TestCase): @test_util.run_all_in_graph_and_eager_modes class CosineProximityTest(test.TestCase): + def l2_norm(self, x, axis): + epsilon = 1e-12 + square_sum = np.sum(np.square(x), axis=axis, keepdims=True) + x_inv_norm = 1 / np.sqrt(np.maximum(square_sum, epsilon)) + return np.multiply(x, x_inv_norm) + + def setup(self, axis=1): + self.np_y_true = np.asarray([[1, 9, 2], [-5, -2, 6]], dtype=np.float32) + self.np_y_pred = np.asarray([[4, 8, 12], [8, 1, 3]], dtype=np.float32) + + y_true = self.l2_norm(self.np_y_true, axis) + y_pred = self.l2_norm(self.np_y_pred, axis) + self.expected_loss = -np.sum(np.multiply(y_true, y_pred), axis=(axis,)) + + self.y_true = constant_op.constant(self.np_y_true) + self.y_pred = constant_op.constant(self.np_y_pred) + def test_config(self): - cosine_obj = metrics.CosineProximity(name='my_cos', dtype=dtypes.int32) + cosine_obj = metrics.CosineProximity( + axis=2, name='my_cos', dtype=dtypes.int32) self.assertEqual(cosine_obj.name, 'my_cos') self.assertEqual(cosine_obj._dtype, dtypes.int32) @@ -1060,29 +1078,33 @@ class CosineProximityTest(test.TestCase): self.assertEqual(cosine_obj2._dtype, dtypes.int32) def test_unweighted(self): + self.setup() cosine_obj = metrics.CosineProximity() self.evaluate(variables.variables_initializer(cosine_obj.variables)) - - y_true = constant_op.constant(((0, 1, 0, 1, 0), (0, 0, 1, 1, 1), - (1, 1, 1, 1, 0), (0, 0, 0, 0, 1))) - y_pred = constant_op.constant(((0, 0, 1, 1, 0), (1, 1, 1, 1, 1), - (0, 1, 0, 1, 0), (1, 1, 1, 1, 1))) - - update_op = cosine_obj.update_state(y_true, y_pred) - self.evaluate(update_op) - result = cosine_obj.result() - self.assertAllClose(-0.60723, result, atol=1e-5) + loss = cosine_obj(self.y_true, self.y_pred) + expected_loss = np.mean(self.expected_loss) + self.assertAlmostEqual(self.evaluate(loss), expected_loss, 3) def test_weighted(self): + self.setup() cosine_obj = metrics.CosineProximity() self.evaluate(variables.variables_initializer(cosine_obj.variables)) - y_true = constant_op.constant(((0, 1, 0, 1, 0), (0, 0, 1, 1, 1), - (1, 1, 1, 1, 0), (0, 0, 0, 0, 1))) - y_pred = constant_op.constant(((0, 0, 1, 1, 0), (1, 1, 1, 1, 1), - (0, 1, 0, 1, 0), (1, 1, 1, 1, 1))) - sample_weight = constant_op.constant((1., 1.5, 2., 2.5)) - result = cosine_obj(y_true, y_pred, sample_weight=sample_weight) - self.assertAllClose(-0.59916, self.evaluate(result), atol=1e-5) + sample_weight = np.asarray([1.2, 3.4]) + loss = cosine_obj( + self.y_true, + self.y_pred, + sample_weight=constant_op.constant(sample_weight)) + expected_loss = np.sum( + self.expected_loss * sample_weight) / np.sum(sample_weight) + self.assertAlmostEqual(self.evaluate(loss), expected_loss, 3) + + def test_axis(self): + self.setup(axis=1) + cosine_obj = metrics.CosineProximity(axis=1) + self.evaluate(variables.variables_initializer(cosine_obj.variables)) + loss = cosine_obj(self.y_true, self.y_pred) + expected_loss = np.mean(self.expected_loss) + self.assertAlmostEqual(self.evaluate(loss), expected_loss, 3) @test_util.run_all_in_graph_and_eager_modes diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-cosine-proximity.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-cosine-proximity.pbtxt index de35966f48..4952a76291 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-cosine-proximity.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.-cosine-proximity.pbtxt @@ -5,7 +5,7 @@ tf_class { is_instance: "" member_method { name: "__init__" - argspec: "args=[\'self\', \'reduction\', \'name\'], varargs=None, keywords=None, defaults=[\'sum_over_batch_size\', \'None\'], " + argspec: "args=[\'self\', \'axis\', \'reduction\', \'name\'], varargs=None, keywords=None, defaults=[\'-1\', \'sum_over_batch_size\', \'None\'], " } member_method { name: "call" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.pbtxt index 532d2b2f88..b71e89883e 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.losses.pbtxt @@ -74,11 +74,11 @@ tf_module { } member_method { name: "cosine" - argspec: "args=[\'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + argspec: "args=[\'y_true\', \'y_pred\', \'axis\'], varargs=None, keywords=None, defaults=[\'-1\'], " } member_method { name: "cosine_proximity" - argspec: "args=[\'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + argspec: "args=[\'y_true\', \'y_pred\', \'axis\'], varargs=None, keywords=None, defaults=[\'-1\'], " } member_method { name: "deserialize" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-cosine-proximity.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-cosine-proximity.pbtxt index d095ed42ef..c50638740f 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-cosine-proximity.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.-cosine-proximity.pbtxt @@ -89,7 +89,7 @@ tf_class { } member_method { name: "__init__" - argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'cosine_proximity\', \'None\'], " + argspec: "args=[\'self\', \'name\', \'dtype\', \'axis\'], varargs=None, keywords=None, defaults=[\'cosine_proximity\', \'None\', \'-1\'], " } member_method { name: "add_loss" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.pbtxt index 743a8ae34a..54f5b21f55 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.keras.metrics.pbtxt @@ -122,11 +122,11 @@ tf_module { } member_method { name: "cosine" - argspec: "args=[\'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + argspec: "args=[\'y_true\', \'y_pred\', \'axis\'], varargs=None, keywords=None, defaults=[\'-1\'], " } member_method { name: "cosine_proximity" - argspec: "args=[\'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + argspec: "args=[\'y_true\', \'y_pred\', \'axis\'], varargs=None, keywords=None, defaults=[\'-1\'], " } member_method { name: "deserialize" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-cosine-proximity.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-cosine-proximity.pbtxt index de35966f48..4952a76291 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-cosine-proximity.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.-cosine-proximity.pbtxt @@ -5,7 +5,7 @@ tf_class { is_instance: "" member_method { name: "__init__" - argspec: "args=[\'self\', \'reduction\', \'name\'], varargs=None, keywords=None, defaults=[\'sum_over_batch_size\', \'None\'], " + argspec: "args=[\'self\', \'axis\', \'reduction\', \'name\'], varargs=None, keywords=None, defaults=[\'-1\', \'sum_over_batch_size\', \'None\'], " } member_method { name: "call" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.pbtxt index ef4fd66a7a..673593ed6c 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.losses.pbtxt @@ -78,11 +78,11 @@ tf_module { } member_method { name: "cosine" - argspec: "args=[\'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + argspec: "args=[\'y_true\', \'y_pred\', \'axis\'], varargs=None, keywords=None, defaults=[\'-1\'], " } member_method { name: "cosine_proximity" - argspec: "args=[\'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + argspec: "args=[\'y_true\', \'y_pred\', \'axis\'], varargs=None, keywords=None, defaults=[\'-1\'], " } member_method { name: "deserialize" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-cosine-proximity.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-cosine-proximity.pbtxt index d095ed42ef..c50638740f 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-cosine-proximity.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.-cosine-proximity.pbtxt @@ -89,7 +89,7 @@ tf_class { } member_method { name: "__init__" - argspec: "args=[\'self\', \'name\', \'dtype\'], varargs=None, keywords=None, defaults=[\'cosine_proximity\', \'None\'], " + argspec: "args=[\'self\', \'name\', \'dtype\', \'axis\'], varargs=None, keywords=None, defaults=[\'cosine_proximity\', \'None\', \'-1\'], " } member_method { name: "add_loss" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.pbtxt index 743a8ae34a..54f5b21f55 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.keras.metrics.pbtxt @@ -122,11 +122,11 @@ tf_module { } member_method { name: "cosine" - argspec: "args=[\'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + argspec: "args=[\'y_true\', \'y_pred\', \'axis\'], varargs=None, keywords=None, defaults=[\'-1\'], " } member_method { name: "cosine_proximity" - argspec: "args=[\'y_true\', \'y_pred\'], varargs=None, keywords=None, defaults=None" + argspec: "args=[\'y_true\', \'y_pred\', \'axis\'], varargs=None, keywords=None, defaults=[\'-1\'], " } member_method { name: "deserialize" -- GitLab From 0d15dbe9c5df49c6a9757fedbe47f893837363db Mon Sep 17 00:00:00 2001 From: Mark Heffernan Date: Wed, 9 Jan 2019 12:31:46 -0800 Subject: [PATCH 567/622] Remove deprecated methods in ShapeUtil. No functional change. Replace with calls to Shape methods. PiperOrigin-RevId: 228566953 --- tensorflow/c/eager/c_api_debug.cc | 4 +- .../compiler/xla/service/scatter_expander.cc | 2 +- .../compiler/xla/service/shape_inference.cc | 4 +- tensorflow/compiler/xla/shape.cc | 4 +- tensorflow/compiler/xla/shape_util.cc | 107 ++++++++---------- tensorflow/compiler/xla/shape_util.h | 44 +------ tensorflow/compiler/xla/shape_util_test.cc | 4 - tensorflow/compiler/xla/tests/tuple_test.cc | 5 +- 8 files changed, 61 insertions(+), 113 deletions(-) diff --git a/tensorflow/c/eager/c_api_debug.cc b/tensorflow/c/eager/c_api_debug.cc index 52b0824552..ffcd5ace0b 100644 --- a/tensorflow/c/eager/c_api_debug.cc +++ b/tensorflow/c/eager/c_api_debug.cc @@ -83,7 +83,7 @@ TF_CAPI_EXPORT extern TFE_TensorDebugInfo* TFE_TensorHandleTensorDebugInfo( } } - if (xla::ShapeUtil::IsTuple(padded_shape)) { + if (padded_shape.IsTuple()) { if (xla::ShapeUtil::TupleElementCount(padded_shape) != 2) { // Currently, the only case of XlaTensor containing a tuple shape is to // represent 64 bit ints, doubles, and complex numbers (we don't support @@ -99,7 +99,7 @@ TF_CAPI_EXPORT extern TFE_TensorDebugInfo* TFE_TensorHandleTensorDebugInfo( xla::Shape shape0 = xla::ShapeUtil::GetTupleElementShape(padded_shape, 0); const xla::Shape& shape1 = xla::ShapeUtil::GetTupleElementShape(padded_shape, 1); - if (xla::ShapeUtil::IsTuple(shape0) || xla::ShapeUtil::IsTuple(shape1)) { + if (shape0.IsTuple() || shape1.IsTuple()) { status->status = tensorflow::errors::InvalidArgument( "XlaTensors should not contain nested tuples. Shape: ", padded_shape.DebugString()); diff --git a/tensorflow/compiler/xla/service/scatter_expander.cc b/tensorflow/compiler/xla/service/scatter_expander.cc index 8b9955faf8..036c3c36f6 100644 --- a/tensorflow/compiler/xla/service/scatter_expander.cc +++ b/tensorflow/compiler/xla/service/scatter_expander.cc @@ -59,7 +59,7 @@ static StatusOr CanonicalizeScatterIndices( TF_ASSIGN_OR_RETURN( HloInstruction * transposed_scatter_indices, TransposeIndexVectorDimToLast(scatter_indices, index_vector_dim)); - if (ShapeUtil::Rank(scatter_indices->shape()) == index_vector_dim + 1 && + if (scatter_indices->shape().rank() == index_vector_dim + 1 && scatter_indices->shape().dimensions(index_vector_dim) == 1) { auto new_shape = ShapeUtil::DeleteDimension(index_vector_dim, scatter_indices->shape()); diff --git a/tensorflow/compiler/xla/service/shape_inference.cc b/tensorflow/compiler/xla/service/shape_inference.cc index b729dd7660..1d3f84af95 100644 --- a/tensorflow/compiler/xla/service/shape_inference.cc +++ b/tensorflow/compiler/xla/service/shape_inference.cc @@ -2358,7 +2358,7 @@ ShapeInference::InferDegenerateDimensionBroadcastShape(HloOpcode operation, }; // Check the shapes of computation parameters and return types. - if (!ShapeUtil::ShapeIs(condition.result(), PRED, {})) { + if (!ShapeUtil::Equal(condition.result(), ShapeUtil::MakeShape(PRED, {}))) { return InvalidArgument("Condition must return a boolean; got %s.", shape_string()); } @@ -2378,7 +2378,7 @@ ShapeInference::InferDegenerateDimensionBroadcastShape(HloOpcode operation, const Shape& predicate, const Shape& true_operand, const Shape& false_operand, const ProgramShape& true_computation, const ProgramShape& false_computation) { - if (!ShapeUtil::ShapeIs(predicate, PRED, {})) { + if (!ShapeUtil::Equal(predicate, ShapeUtil::MakeShape(PRED, {}))) { return InvalidArgument("Predicate must be a boolean; got %s.", ShapeUtil::HumanString(predicate)); } diff --git a/tensorflow/compiler/xla/shape.cc b/tensorflow/compiler/xla/shape.cc index ea4f722ef4..1a029efe85 100644 --- a/tensorflow/compiler/xla/shape.cc +++ b/tensorflow/compiler/xla/shape.cc @@ -80,7 +80,7 @@ string Shape::ToString(bool print_layout) const { } bool Shape::is_static() const { - if (ShapeUtil::IsTuple(*this)) { + if (IsTuple()) { for (const Shape& subshape : tuple_shapes_) { if (!subshape.is_static()) { return false; @@ -91,7 +91,7 @@ bool Shape::is_static() const { } void Shape::DeleteDimension(int64 dim_to_delete) { - CHECK(ShapeUtil::IsArray(*this)); + CHECK(IsArray()); CHECK_GE(dim_to_delete, 0); CHECK_LT(dim_to_delete, dimensions_.size()); dimensions_.erase(dimensions_.begin() + dim_to_delete); diff --git a/tensorflow/compiler/xla/shape_util.cc b/tensorflow/compiler/xla/shape_util.cc index 7f359311c3..235b065585 100644 --- a/tensorflow/compiler/xla/shape_util.cc +++ b/tensorflow/compiler/xla/shape_util.cc @@ -146,7 +146,7 @@ bool CompareShapes(const Shape& lhs, const Shape& rhs, bool compare_layouts, return false; } - for (int i = 0; i < ShapeUtil::Rank(lhs); ++i) { + for (int i = 0; i < lhs.rank(); ++i) { if (lhs.is_dynamic_dimension(i) != rhs.is_dynamic_dimension(i)) { VLOG(3) << "CompareShapes: lhs and rhs have different dynamic dimensions."; @@ -208,11 +208,6 @@ StatusOr MakeShapeWithLayoutInternal( return equal; } -/* static */ int64 ShapeUtil::Rank(const Shape& shape) { - CHECK(shape.IsArray()) << "Non-arrays do not have a rank, shape: " << shape; - return shape.dimensions_size(); -} - /* static */ int64 ShapeUtil::TrueRank(const Shape& shape) { int64 accum = 0; for (int64 dimension : shape.dimensions()) { @@ -344,7 +339,7 @@ ShapeUtil::MakeShapeWithDescendingLayoutAndSamePhysicalLayout( /* static */ void ShapeUtil::AppendMajorDimension(int bound, Shape* shape) { CHECK(LayoutUtil::IsDenseArray(*shape)); - shape->mutable_layout()->add_minor_to_major(Rank(*shape)); + shape->mutable_layout()->add_minor_to_major(shape->rank()); shape->add_dimensions(bound); TF_DCHECK_OK(ValidateShape(*shape)); } @@ -359,7 +354,7 @@ ShapeUtil::MakeShapeWithDescendingLayoutAndSamePhysicalLayout( } /* static */ bool ShapeUtil::ElementHasBitWidth(const Shape& shape, int bits) { - if (!IsArray(shape)) { + if (!shape.IsArray()) { return false; } return primitive_util::BitWidth(shape.element_type()) == bits; @@ -401,26 +396,24 @@ ShapeUtil::MakeShapeWithDescendingLayoutAndSamePhysicalLayout( return primitive_util::IsFloatingPointType(shape.element_type()); } -/* static */ bool ShapeUtil::IsArray(const Shape& shape) { - return IsArrayPrimitiveType(shape.element_type()); -} - /* static */ bool ShapeUtil::IsNestedTuple(const Shape& shape) { - return IsTuple(shape) && absl::c_any_of(shape.tuple_shapes(), IsTuple); + return shape.IsTuple() && + absl::c_any_of(shape.tuple_shapes(), + [](const Shape& s) { return s.IsTuple(); }); } /* static */ bool ShapeUtil::IsEmptyTuple(const Shape& shape) { - return IsTuple(shape) && TupleElementCount(shape) == 0; + return shape.IsTuple() && TupleElementCount(shape) == 0; } /* static */ int64 ShapeUtil::TupleElementCount(const Shape& shape) { - CHECK(IsTuple(shape)) << HumanString(shape); + CHECK(shape.IsTuple()) << HumanString(shape); return shape.tuple_shapes_size(); } /* static */ const Shape& ShapeUtil::GetTupleElementShape(const Shape& shape, int64 index) { - CHECK(IsTuple(shape)); + CHECK(shape.IsTuple()); CHECK_GT(TupleElementCount(shape), index); TF_DCHECK_OK(ValidateShapeWithOptionalLayout(shape.tuple_shapes(index))); return shape.tuple_shapes(index); @@ -436,7 +429,7 @@ ShapeUtil::MakeShapeWithDescendingLayoutAndSamePhysicalLayout( /* static */ Shape ShapeUtil::SliceTuple(const Shape& tuple, int64 start, int64 limit) { TF_DCHECK_OK(ValidateShapeWithOptionalLayout(tuple)); - CHECK(IsTuple(tuple)); + CHECK(tuple.IsTuple()); CHECK_LE(start, TupleElementCount(tuple)); CHECK_LE(limit, TupleElementCount(tuple)); @@ -453,15 +446,9 @@ ShapeUtil::MakeShapeWithDescendingLayoutAndSamePhysicalLayout( complex_shape.element_type())); } -/* static */ bool ShapeUtil::ShapeIs(const Shape& shape, - PrimitiveType element_type, - std::initializer_list dimensions) { - return Equal(shape, MakeShape(element_type, dimensions)); -} - /* static */ int64 ShapeUtil::ElementsIn(const Shape& shape) { - DCHECK(IsArray(shape)) << ShapeUtil::HumanString(shape); - DCHECK_EQ(shape.dimensions_size(), Rank(shape)); + DCHECK(shape.IsArray()) << ShapeUtil::HumanString(shape); + DCHECK_EQ(shape.dimensions_size(), shape.rank()); if (shape.dimensions().size() == 1) { return shape.dimensions()[0]; } @@ -471,8 +458,8 @@ ShapeUtil::MakeShapeWithDescendingLayoutAndSamePhysicalLayout( } /* static */ int64 ShapeUtil::ElementsInRecursive(const Shape& shape) { - CHECK(IsArray(shape) || IsTuple(shape)); - if (IsArray(shape)) { + CHECK(shape.IsArray() || shape.IsTuple()); + if (shape.IsArray()) { return ElementsIn(shape); } int64 count = 0; @@ -505,7 +492,7 @@ ShapeUtil::MakeShapeWithDescendingLayoutAndSamePhysicalLayout( } /* static */ string ShapeUtil::HumanString(const Shape& shape) { - if (IsTuple(shape)) { + if (shape.IsTuple()) { string text = "("; const char* prefix = ""; for (const Shape& elem_shape : shape.tuple_shapes()) { @@ -529,7 +516,7 @@ ShapeUtil::MakeShapeWithDescendingLayoutAndSamePhysicalLayout( } /* static */ string ShapeUtil::HumanStringWithLayout(const Shape& shape) { - if (IsTuple(shape)) { + if (shape.IsTuple()) { string text = "("; const char* prefix = ""; for (const Shape& elem_shape : shape.tuple_shapes()) { @@ -545,7 +532,7 @@ ShapeUtil::MakeShapeWithDescendingLayoutAndSamePhysicalLayout( StrAppend(&result, (i > 0) ? "," : "", shape.dimensions(i)); } result += "]"; - if (!IsScalar(shape) && IsArray(shape)) { + if (!IsScalar(shape) && shape.IsArray()) { if (LayoutUtil::HasLayout(shape)) { StrAppend(&result, LayoutUtil::HumanString(shape.layout())); } @@ -580,8 +567,8 @@ ShapeUtil::MakeShapeWithDescendingLayoutAndSamePhysicalLayout( /* static */ bool ShapeUtil::CompatibleIgnoringElementType(const Shape& lhs, const Shape& rhs) { - if (IsArray(lhs)) { - return IsArray(rhs) && SameDimensions(lhs, rhs); + if (lhs.IsArray()) { + return rhs.IsArray() && SameDimensions(lhs, rhs); } else if (lhs.element_type() == TUPLE) { return rhs.element_type() == TUPLE && absl::c_equal(lhs.tuple_shapes(), rhs.tuple_shapes(), @@ -594,8 +581,8 @@ ShapeUtil::MakeShapeWithDescendingLayoutAndSamePhysicalLayout( /* static */ bool ShapeUtil::CompatibleIgnoringFpPrecision(const Shape& lhs, const Shape& rhs) { - if (IsArray(lhs)) { - return IsArray(rhs) && SameElementTypeIgnoringFpPrecision(lhs, rhs) && + if (lhs.IsArray()) { + return rhs.IsArray() && SameElementTypeIgnoringFpPrecision(lhs, rhs) && CompatibleIgnoringElementType(lhs, rhs); } else if (lhs.element_type() == TUPLE) { return rhs.element_type() == TUPLE && @@ -615,7 +602,7 @@ ShapeUtil::MakeShapeWithDescendingLayoutAndSamePhysicalLayout( /* static */ int64 ShapeUtil::GetDimensionNumber(const Shape& shape, int64 dimension_number) { if (dimension_number < 0) { - dimension_number += Rank(shape); + dimension_number += shape.rank(); } CHECK_GE(dimension_number, 0); return dimension_number; @@ -669,7 +656,7 @@ ShapeUtil::MakeShapeWithDescendingLayoutAndSamePhysicalLayout( TF_DCHECK_OK(ValidateShape(shape)); if (shape.element_type() == TUPLE) { return ByteSizeOfTupleIndexTable(shape, pointer_size); - } else if (IsArray(shape)) { + } else if (shape.IsArray()) { int64 byte_size = ByteSizeOfElements(shape); if (LayoutUtil::IsSparseArray(shape)) { byte_size += ByteSizeOfSparseIndices(shape); @@ -755,10 +742,10 @@ ShapeUtil::MakeShapeWithDescendingLayoutAndSamePhysicalLayout( return Status::OK(); } - if (LayoutUtil::IsSparseArray(shape) && Rank(shape) == 0) { + if (LayoutUtil::IsSparseArray(shape) && shape.rank() == 0) { return InvalidArgument("sparse arrays must have rank > 0"); } - for (int64 i = 0; i < Rank(shape); ++i) { + for (int64 i = 0; i < shape.rank(); ++i) { int64 dimension = shape.dimensions(i); if (dimension < 0) { return InvalidArgument( @@ -774,7 +761,7 @@ ShapeUtil::MakeShapeWithDescendingLayoutAndSamePhysicalLayout( /* static */ Status ShapeUtil::ValidateShapeSize(const Shape& shape) { VLOG(3) << "Validating shape size: " << ShapeUtil::HumanString(shape); - if (!IsArray(shape)) { + if (!shape.IsArray()) { return Status::OK(); } @@ -867,7 +854,7 @@ ShapeUtil::MakeShapeWithDescendingLayoutAndSamePhysicalLayout( ShapeIndexView index) { const Shape* subshape = &shape; for (auto i : index) { - if (!IsTuple(*subshape) || i >= subshape->tuple_shapes_size() || i < 0) { + if (!subshape->IsTuple() || i >= subshape->tuple_shapes_size() || i < 0) { return false; } subshape = &subshape->tuple_shapes(i); @@ -879,7 +866,7 @@ ShapeUtil::MakeShapeWithDescendingLayoutAndSamePhysicalLayout( ShapeIndexView index) { const Shape* return_shape = &shape; for (auto i : index) { - CHECK(IsTuple(*return_shape)) + CHECK(return_shape->IsTuple()) << "Invalid index " << index << " for shape " << shape; return_shape = &return_shape->tuple_shapes(i); } @@ -890,7 +877,7 @@ ShapeUtil::MakeShapeWithDescendingLayoutAndSamePhysicalLayout( const Shape& shape, ShapeIndexView index) { const Shape* return_shape = &shape; for (auto i : index) { - if (!IsTuple(*return_shape) || i < 0 || + if (!return_shape->IsTuple() || i < 0 || i >= return_shape->tuple_shapes_size()) { return InvalidArgument( "Shape index %s not a valid subshape index for tuple with shape %s", @@ -905,7 +892,7 @@ ShapeUtil::MakeShapeWithDescendingLayoutAndSamePhysicalLayout( ShapeIndexView index) { Shape* return_shape = shape; for (auto i : index) { - CHECK(IsTuple(*return_shape)); + CHECK(return_shape->IsTuple()); return_shape = return_shape->mutable_tuple_shapes(i); } return return_shape; @@ -913,11 +900,11 @@ ShapeUtil::MakeShapeWithDescendingLayoutAndSamePhysicalLayout( /* static */ bool ShapeUtil::IsLeafIndex(const Shape& shape, const ShapeIndex& index) { - return !IsTuple(GetSubshape(shape, index)); + return !GetSubshape(shape, index).IsTuple(); } /* static */ int64 ShapeUtil::GetLeafCount(const Shape& shape) { - if (!IsTuple(shape)) { + if (!shape.IsTuple()) { return 1; } int64 count = 0; @@ -1081,8 +1068,8 @@ Status ForEachMutableSubshapeHelper( /* static */ std::tuple, std::vector> ShapeUtil::InsertedOrDeleted1SizedDimensions(const Shape& shape_pre, const Shape& shape_post) { - CHECK(IsArray(shape_pre)); - CHECK(IsArray(shape_post)); + CHECK(shape_pre.IsArray()); + CHECK(shape_post.IsArray()); auto nil = std::make_tuple(false, std::vector(), std::vector()); @@ -1129,7 +1116,7 @@ ShapeUtil::InsertedOrDeleted1SizedDimensions(const Shape& shape_pre, auto unmodified_dim_pair = i < unmodified_dims.size() ? unmodified_dims[i] - : std::make_pair(Rank(shape_pre), Rank(shape_post)); + : std::make_pair(shape_pre.rank(), shape_post.rank()); if (!check_modified_dims(prior_unmodified_dim_pair, unmodified_dim_pair)) { return nil; } @@ -1141,8 +1128,8 @@ ShapeUtil::InsertedOrDeleted1SizedDimensions(const Shape& shape_pre, /* static */ std::vector> ShapeUtil::DimensionsUnmodifiedByReshape(const Shape& input_shape, const Shape& output_shape) { - CHECK(IsArray(input_shape)); - CHECK(IsArray(output_shape)); + CHECK(input_shape.IsArray()); + CHECK(output_shape.IsArray()); // Unmodified dimensions are merely common factors of rank 1. auto common_factors = CommonFactors(AsInt64Slice(input_shape.dimensions()), @@ -1192,8 +1179,8 @@ ShapeUtil::DimensionsUnmodifiedByReshape(const Shape& input_shape, /* static */ bool ShapeUtil::ReshapeIsBitcast(const Shape& input_shape, const Shape& output_shape) { - CHECK(IsArray(input_shape)); - CHECK(IsArray(output_shape)); + CHECK(input_shape.IsArray()); + CHECK(output_shape.IsArray()); CHECK(LayoutUtil::HasLayout(input_shape)); CHECK(LayoutUtil::HasLayout(output_shape)); @@ -1321,12 +1308,12 @@ ShapeUtil::DimensionsUnmodifiedByReshape(const Shape& input_shape, Shape output_shape_dim0_major = MakeShapeWithDescendingLayout( output_shape.element_type(), AsInt64Slice(output_shape.dimensions())); - for (int64 input_dim = 0; input_dim < Rank(input_shape); ++input_dim) { + for (int64 input_dim = 0; input_dim < input_shape.rank(); ++input_dim) { if (input_shape.dimensions(input_dim) <= 1) { continue; } - std::vector input_unit_index(Rank(input_shape), 0); + std::vector input_unit_index(input_shape.rank(), 0); input_unit_index[input_dim] = 1; int64 logical_linear_index = IndexUtil::MultidimensionalIndexToLinearIndex(input_shape_dim0_major, @@ -1352,11 +1339,11 @@ ShapeUtil::DimensionsUnmodifiedByReshape(const Shape& input_shape, /* static */ absl::optional ShapeUtil::AlignLayouts( const Shape& input_shape, const Shape& output_shape) { - CHECK(IsArray(input_shape)); - CHECK(IsArray(output_shape)); + CHECK(input_shape.IsArray()); + CHECK(output_shape.IsArray()); - int64 input_rank = Rank(input_shape); - int64 output_rank = Rank(output_shape); + int64 input_rank = input_shape.rank(); + int64 output_rank = output_shape.rank(); // First, calculate an alignment of the dimensions. A consecutive sequence of // input dimensions and output dimensions belong to the same alignment part if @@ -1493,14 +1480,14 @@ ShapeUtil::DimensionsUnmodifiedByReshape(const Shape& input_shape, /* static */ Shape ShapeUtil::DeleteDimension(int64 dim_to_delete, Shape shape) { - CHECK(IsArray(shape)); + CHECK(shape.IsArray()); shape.DeleteDimension(dim_to_delete); return shape; } /* static */ Shape ShapeUtil::FilterDimensions( const std::function& p, Shape shape) { - CHECK(IsArray(shape)); + CHECK(shape.IsArray()); std::vector dims_to_delete; for (int64 i = shape.dimensions().size() - 1; i >= 0; --i) { if (!p(i)) { diff --git a/tensorflow/compiler/xla/shape_util.h b/tensorflow/compiler/xla/shape_util.h index c8295e85ce..e98c6e024b 100644 --- a/tensorflow/compiler/xla/shape_util.h +++ b/tensorflow/compiler/xla/shape_util.h @@ -185,7 +185,7 @@ class ShapeUtil { // may not actually be able to store this number of elements. See // LayoutUtil::MaxSparseElements(shape) to obtain the maximum number of // elements that can be stored in a sparse shape. - // Precondition: IsArray(shape) + // Precondition: shape.IsArray() static int64 ElementsIn(const Shape& shape); // As ElementsIn(), but recurses through tuples. @@ -296,11 +296,6 @@ class ShapeUtil { // As Equal, but allow one of lhs and rhs to be F16 while the other is F32. static bool EqualIgnoringFpPrecision(const Shape& lhs, const Shape& rhs); - // Returns the rank (number of dimensions) of the given shape. - // Precondition: !IsTuple(shape) - ABSL_DEPRECATED("Use `Shape::rank` instead.") - static int64 Rank(const Shape& shape); - // Returns the number of dimensions for which the dimension is not (trivially) // 1. e.g., f32[2x1x1] has a true rank of 1D, the other dimensions are just // fluff. Note that zero dimensions are included in the true rank, e.g., @@ -314,10 +309,10 @@ class ShapeUtil { // Scalar-specific static bool IsScalar(const Shape& shape) { - return IsArray(shape) && Rank(shape) == 0; + return shape.IsArray() && shape.rank() == 0; } static bool IsEffectiveScalar(const Shape& shape) { - return IsArray(shape) && TrueRank(shape) == 0; + return shape.IsArray() && TrueRank(shape) == 0; } // Returns whether "shape" is a scalar (array) with the given element_type. @@ -457,31 +452,6 @@ class ShapeUtil { // that floating point numbers are signed. static bool ElementIsSigned(const Shape& shape); - // Returns whether the shape is a tuple. - ABSL_DEPRECATED("Use Shape::IsTuple instead.") - static bool IsTuple(const Shape& shape) { - return shape.element_type() == TUPLE; - } - - // Returns whether the shape is an opaque value (i.e. an 'existential' typed - // value that is passed to CustomCall operations). - ABSL_DEPRECATED("Use Shape::IsOpaque instead.") - static bool IsOpaque(const Shape& shape) { - return shape.element_type() == OPAQUE; - } - - // Returns whether the shape is an token value used for ordering - // side-effecting operations. - ABSL_DEPRECATED("Use Shape::IsToken instead.") - static bool IsToken(const Shape& shape) { - return shape.element_type() == TOKEN; - } - - // Returns whether the shape is an array. Note that scalars are considered - // arrays. - ABSL_DEPRECATED("Use Shape::IsArray instead.") - static bool IsArray(const Shape& shape); - // Returns whether the given primitive type corresponds to an array shape. static bool IsArrayPrimitiveType(PrimitiveType primitive_type); @@ -511,12 +481,6 @@ class ShapeUtil { // shape. static Shape ComplexComponentShape(const Shape& complex_shape); - // Shorthand for testing whether a shape is of a given element type and - // sequence of dimensions. - ABSL_DEPRECATED("Use Equal() instead.") - static bool ShapeIs(const Shape& shape, PrimitiveType element_type, - std::initializer_list dimensions); - // Returns true if the given shape has a subshape at the given index. static bool IndexIsValid(const Shape& shape, ShapeIndexView index); @@ -764,7 +728,7 @@ class ShapeUtil { if (ShapeUtil::IsZeroElementArray(shape)) { return Status::OK(); } - CHECK_EQ(Rank(shape), base.size()); + CHECK_EQ(shape.rank(), base.size()); CHECK_EQ(incr.size(), base.size()); CHECK_EQ(count.size(), base.size()); const int64 rank = LayoutUtil::MinorToMajor(shape).size(); diff --git a/tensorflow/compiler/xla/shape_util_test.cc b/tensorflow/compiler/xla/shape_util_test.cc index 8e7c208193..61b4e73e06 100644 --- a/tensorflow/compiler/xla/shape_util_test.cc +++ b/tensorflow/compiler/xla/shape_util_test.cc @@ -538,10 +538,6 @@ TEST(ShapeUtilTest, InsertedOrDeleted1SizedDimensions) { ShapeUtil::InsertedOrDeleted1SizedDimensions(shape0, shape2))); } -TEST(ShapeUtilTest, ShapeIs) { - EXPECT_FALSE(ShapeUtil::ShapeIs(ShapeUtil::MakeShape(PRED, {2}), PRED, {})); -} - TEST(ShapeUtilTest, ForEachIndex) { struct ShapeDimensionAndNumberInvocations { std::vector dimensions; diff --git a/tensorflow/compiler/xla/tests/tuple_test.cc b/tensorflow/compiler/xla/tests/tuple_test.cc index 9c586bdeb0..426d6c84ee 100644 --- a/tensorflow/compiler/xla/tests/tuple_test.cc +++ b/tensorflow/compiler/xla/tests/tuple_test.cc @@ -176,8 +176,9 @@ XLA_TEST_F(TupleTest, AddTupleElements) { {2.f, 4.f, 6.f}, // row 0 {5.f, 7.f, 9.f}, // row 1 }); - ASSERT_TRUE(ShapeUtil::ShapeIs(vector_shape, F32, {3})); - ASSERT_TRUE(ShapeUtil::ShapeIs(matrix_shape, F32, {/*y=*/2, /*x=*/3})); + ASSERT_TRUE(ShapeUtil::Equal(vector_shape, ShapeUtil::MakeShape(F32, {3}))); + ASSERT_TRUE(ShapeUtil::Equal(matrix_shape, + ShapeUtil::MakeShape(F32, {/*y=*/2, /*x=*/3}))); ComputeAndCompareR2(&builder, expected, {}, error_spec_); } -- GitLab From 4053e17dbb7a1be85553e5173c2e08fb9653d33f Mon Sep 17 00:00:00 2001 From: Pete Warden Date: Wed, 9 Jan 2019 12:33:36 -0800 Subject: [PATCH 568/622] Generate IDE projects automatically for various platforms PiperOrigin-RevId: 228567285 --- tensorflow/lite/experimental/micro/BUILD | 4 + tensorflow/lite/experimental/micro/README.md | 259 ++++++++++++++---- .../experimental/micro/bluepill/debug_log.cc | 27 ++ .../lite/experimental/micro/debug_log.cc | 41 +++ .../lite/experimental/micro/debug_log.h | 23 ++ .../experimental/micro/debug_log_numbers.cc | 185 +++++++++++++ .../experimental/micro/debug_log_numbers.h | 28 ++ .../examples/micro_speech/CMSIS/Makefile.inc | 43 +++ .../micro/examples/micro_speech/Makefile.inc | 183 +++++-------- .../micro_speech/disco_f746ng/Makefile.inc | 7 + .../disco_f746ng/audio_provider.cc | 182 ++++++++++++ .../micro_speech/disco_f746ng/timer.cc | 24 ++ .../examples/micro_speech/osx/Makefile.inc | 6 +- .../examples/micro_speech/preprocessor.cc | 9 +- .../lite/experimental/micro/mbed/debug_log.cc | 24 ++ .../experimental/micro/micro_error_reporter.h | 22 +- .../micro/riscv32_mcu/debug_log.cc | 172 ------------ .../experimental/micro/tools/make/Makefile | 101 ++++--- .../micro/tools/make/helper_functions.inc | 117 ++++++++ .../tools/make/targets/bluepill_makefile.inc | 5 +- .../tools/make/targets/mbed_makefile.inc | 4 + .../micro/tools/make/targets/osx_makefile.inc | 2 +- .../make/templates/AUDIO_DISCO_F746NG.lib.tpl | 1 + .../make/templates/BSP_DISCO_F746NG.lib.tpl | 1 + .../micro/tools/make/templates/Makefile.tpl | 26 ++ .../tools/make/templates/README_MAKE.md.tpl | 29 ++ .../tools/make/templates/README_MBED.md.tpl | 48 ++++ .../make/templates/SDRAM_DISCO_F746NG.lib.tpl | 1 + .../tools/make/templates/mbed-os.lib.tpl | 1 + .../tools/make/templates/mbed_app.json.tpl | 7 + 30 files changed, 1170 insertions(+), 412 deletions(-) create mode 100644 tensorflow/lite/experimental/micro/bluepill/debug_log.cc create mode 100644 tensorflow/lite/experimental/micro/debug_log.cc create mode 100644 tensorflow/lite/experimental/micro/debug_log.h create mode 100644 tensorflow/lite/experimental/micro/debug_log_numbers.cc create mode 100644 tensorflow/lite/experimental/micro/debug_log_numbers.h create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/Makefile.inc create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/disco_f746ng/Makefile.inc create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/disco_f746ng/audio_provider.cc create mode 100644 tensorflow/lite/experimental/micro/examples/micro_speech/disco_f746ng/timer.cc create mode 100644 tensorflow/lite/experimental/micro/mbed/debug_log.cc create mode 100644 tensorflow/lite/experimental/micro/tools/make/helper_functions.inc create mode 100644 tensorflow/lite/experimental/micro/tools/make/targets/mbed_makefile.inc create mode 100644 tensorflow/lite/experimental/micro/tools/make/templates/AUDIO_DISCO_F746NG.lib.tpl create mode 100644 tensorflow/lite/experimental/micro/tools/make/templates/BSP_DISCO_F746NG.lib.tpl create mode 100644 tensorflow/lite/experimental/micro/tools/make/templates/Makefile.tpl create mode 100644 tensorflow/lite/experimental/micro/tools/make/templates/README_MAKE.md.tpl create mode 100644 tensorflow/lite/experimental/micro/tools/make/templates/README_MBED.md.tpl create mode 100644 tensorflow/lite/experimental/micro/tools/make/templates/SDRAM_DISCO_F746NG.lib.tpl create mode 100644 tensorflow/lite/experimental/micro/tools/make/templates/mbed-os.lib.tpl create mode 100644 tensorflow/lite/experimental/micro/tools/make/templates/mbed_app.json.tpl diff --git a/tensorflow/lite/experimental/micro/BUILD b/tensorflow/lite/experimental/micro/BUILD index e11159868e..2d00ef76f4 100644 --- a/tensorflow/lite/experimental/micro/BUILD +++ b/tensorflow/lite/experimental/micro/BUILD @@ -12,6 +12,8 @@ load( cc_library( name = "micro_framework", srcs = [ + "debug_log.cc", + "debug_log_numbers.cc", "micro_error_reporter.cc", "micro_interpreter.cc", "micro_mutable_op_resolver.cc", @@ -19,6 +21,8 @@ cc_library( ], hdrs = [ "compatibility.h", + "debug_log.h", + "debug_log_numbers.h", "micro_error_reporter.h", "micro_interpreter.h", "micro_mutable_op_resolver.h", diff --git a/tensorflow/lite/experimental/micro/README.md b/tensorflow/lite/experimental/micro/README.md index 464c7b6ad7..173f91fe90 100644 --- a/tensorflow/lite/experimental/micro/README.md +++ b/tensorflow/lite/experimental/micro/README.md @@ -1,46 +1,142 @@ # TensorFlow Lite for Microcontrollers -This an experimental port of TensorFlow Lite aimed at micro controllers and other devices with only kilobytes of memory. It doesn't require any operating system support, any standard C or C++ libraries, or dynamic memory allocation, so it's designed to be portable even to 'bare metal' systems. The core runtime fits in 16KB on a Cortex M3, and with enough operators to run a speech keyword detection model, takes up a total of 22KB. +This an experimental port of TensorFlow Lite aimed at micro controllers and +other devices with only kilobytes of memory. It doesn't require any operating +system support, any standard C or C++ libraries, or dynamic memory allocation, +so it's designed to be portable even to 'bare metal' systems. The core runtime +fits in 16KB on a Cortex M3, and with enough operators to run a speech keyword +detection model, takes up a total of 22KB. The design goals are for the framework to be: -- **Readable**: We want embedded software engineers to be able to understand what's required to run ML inference without having to study research papers. We've tried to keep the code base small, modular, and have reference implementations of all operations to help with this. - -- **Easy to modify**: We know that there are a lot of different platforms and requirements in the embedded world, and we don't expect to cover all of them in one framework. Instead, we're hoping that it can be a good starting point for developers to build on top of to meet their own needs. For example, we tried to make it easy to replace the implementations of key computational operators that are often crucial for performance, without having to touch the data flow and other runtime code. We want it to make more sense to use our workflow to handle things like model import and less-important operations, and customize the parts that matter, rather than having to reimplement everything in your own engine. - -- **Well-tested**: If you're modifying code, you need to know if your changes are correct. Having an easy way to test lets you develop much faster. To help there, we've written tests for all the components, and we've made sure that the tests can be run on almost any platform, with no dependencies apart from the ability to log text to a debug console somewhere. We also provide an easy way to run all the tests on-device as part of an automated test framework, and we use qemu/Renode emulation so that tests can be run even without physical devices present. - -- **Easy to integrate**: We want to be as open a system as possible, and use the best code available for each platform. To do that, we're going to rely on projects like [CMSIS-NN](https://www.keil.com/pack/doc/CMSIS/NN/html/index.html), [uTensor](https://github.com/uTensor/uTensor), and other vendor libraries to handle as much performance-critical code as possible. We know that there are an increasing number of options to accelerate neural networks on microcontrollers, so we're aiming to be a good host for deploying those hardware technologies too. - -- **Compatible**: We're using the same file schema, interpreter API, and kernel interface as regular TensorFlow Lite, so we leverage the large existing set of tools, documentation, and examples for the project. The biggest barrier to deploying ML models is getting them from a training environment into a form that's easy to run inference on, so we see reusing this rich ecosystem as being crucial to being easily usable. We also hope to integrate this experimental work back into the main codebase in the future. +- **Readable**: We want embedded software engineers to be able to understand + what's required to run ML inference without having to study research papers. + We've tried to keep the code base small, modular, and have reference + implementations of all operations to help with this. + +- **Easy to modify**: We know that there are a lot of different platforms and + requirements in the embedded world, and we don't expect to cover all of them + in one framework. Instead, we're hoping that it can be a good starting point + for developers to build on top of to meet their own needs. For example, we + tried to make it easy to replace the implementations of key computational + operators that are often crucial for performance, without having to touch + the data flow and other runtime code. We want it to make more sense to use + our workflow to handle things like model import and less-important + operations, and customize the parts that matter, rather than having to + reimplement everything in your own engine. + +- **Well-tested**: If you're modifying code, you need to know if your changes + are correct. Having an easy way to test lets you develop much faster. To + help there, we've written tests for all the components, and we've made sure + that the tests can be run on almost any platform, with no dependencies apart + from the ability to log text to a debug console somewhere. We also provide + an easy way to run all the tests on-device as part of an automated test + framework, and we use qemu/Renode emulation so that tests can be run even + without physical devices present. + +- **Easy to integrate**: We want to be as open a system as possible, and use + the best code available for each platform. To do that, we're going to rely + on projects like + [CMSIS-NN](https://www.keil.com/pack/doc/CMSIS/NN/html/index.html), + [uTensor](https://github.com/uTensor/uTensor), and other vendor libraries to + handle as much performance-critical code as possible. We know that there are + an increasing number of options to accelerate neural networks on + microcontrollers, so we're aiming to be a good host for deploying those + hardware technologies too. + +- **Compatible**: We're using the same file schema, interpreter API, and + kernel interface as regular TensorFlow Lite, so we leverage the large + existing set of tools, documentation, and examples for the project. The + biggest barrier to deploying ML models is getting them from a training + environment into a form that's easy to run inference on, so we see reusing + this rich ecosystem as being crucial to being easily usable. We also hope to + integrate this experimental work back into the main codebase in the future. To meet those goals, we've made some tradeoffs: -- **Simple C++**: To help with readability, our code is written in a modern version of C++, but we generally treat it as a "better C", rather relying on more complex features such as template meta-programming. As mentioned earlier, we avoid any use of dynamic memory allocation (new/delete) or the standard C/C++ libraries, so we believe this should still be fairly portable. It does mean that some older devices with C-only toolchains won't be supported, but we're hoping that the reference operator implementations (which are simple C-like functions) can still be useful in those cases. The interfaces are also designed to be C-only, so it should be possible to integrate the resulting library with pure C projects. - -- **Interpreted**: Code generation is a popular pattern for embedded code, because it gives standalone code that's easy to modify and step through, but we've chosen to go with an interpreted approach. In our internal microcontroller work we've found that using an extremely stripped-down interpreter with almost no dependencies gives us a lot of the same advantages, but is easier to maintain. For example, when new updates come out for the underlying library, you can just merge your local modifications in a single step, rather than having to regenerate new code and then patch in any changes you subsequently made. The coarse granularity of the interpreted primitives means that each operation call typically takes hundreds of thousands of instruction cycles at least, so we don't see noticeable performance gains from avoiding what's essentially a single switch statement at the interpreter level to call each operation. We're still working on improving the packaging though, for example we're considering having the ability to snapshot all the source files and headers used for a particular model, being able to compile the code and data together as a library, and then access it through a minimal set of C interface calls which hide the underlying complexity. - -- **Flatbuffers**: We represent our models using [the standard flatbuffer schema used by the rest of TensorFlow Lite](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/schema/schema.fbs), with the difference that we always keep it in read-only program memory (typically flash) rather than relying on having a file system to read it from. This is a good fit because flatbuffer's serialized format is designed to be mapped into memory without requiring any extra memory allocations or modifications to access it. All of the functions to read model values work directly on the serialized bytes, and large sections of data like weights are directly accessible as sequential C-style arrays of their data type, with no strides or unpacking needed. We do get a lot of value from using flatbuffers, but there is a cost in complexity. The flat buffer library code is all inline [inside the main headers](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/schema/schema_generated.h), but it isn't straightforward to inspect their implementations, and the model data structures aren't easy to comprehend from the debugger. The header for the schema itself also has to be periodically updated when new information is added to the file format, though we try to handle that transparently for most developers by checking in a pre-generated version. - -- **Code Duplication**: Some of the code in this prototype largely duplicates the logic in other parts of the TensorFlow Lite code base, for example the operator wrappers. We've tried to keep share as much as we can between the two interpreters, but there are some assumptions built into the original runtime that make this difficult. We'll be working on modularizing the main interpreter so that we can move to an entirely shared system. - -This initial preview release is designed to get early feedback, and is not intended to be a final product. It only includes enough operations to run a simple keyword recognition model, and the implementations are not optimized. We're hoping this will be a good way to get feedback and collaborate to improve the framework. - -## Getting Started +- **Simple C++**: To help with readability, our code is written in a modern + version of C++, but we generally treat it as a "better C", rather relying on + more complex features such as template meta-programming. As mentioned + earlier, we avoid any use of dynamic memory allocation (new/delete) or the + standard C/C++ libraries, so we believe this should still be fairly + portable. It does mean that some older devices with C-only toolchains won't + be supported, but we're hoping that the reference operator implementations + (which are simple C-like functions) can still be useful in those cases. The + interfaces are also designed to be C-only, so it should be possible to + integrate the resulting library with pure C projects. + +- **Interpreted**: Code generation is a popular pattern for embedded code, + because it gives standalone code that's easy to modify and step through, but + we've chosen to go with an interpreted approach. In our internal + microcontroller work we've found that using an extremely stripped-down + interpreter with almost no dependencies gives us a lot of the same + advantages, but is easier to maintain. For example, when new updates come + out for the underlying library, you can just merge your local modifications + in a single step, rather than having to regenerate new code and then patch + in any changes you subsequently made. The coarse granularity of the + interpreted primitives means that each operation call typically takes + hundreds of thousands of instruction cycles at least, so we don't see + noticeable performance gains from avoiding what's essentially a single + switch statement at the interpreter level to call each operation. We're + still working on improving the packaging though, for example we're + considering having the ability to snapshot all the source files and headers + used for a particular model, being able to compile the code and data + together as a library, and then access it through a minimal set of C + interface calls which hide the underlying complexity. + +- **Flatbuffers**: We represent our models using + [the standard flatbuffer schema used by the rest of TensorFlow Lite](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/schema/schema.fbs), + with the difference that we always keep it in read-only program memory + (typically flash) rather than relying on having a file system to read it + from. This is a good fit because flatbuffer's serialized format is designed + to be mapped into memory without requiring any extra memory allocations or + modifications to access it. All of the functions to read model values work + directly on the serialized bytes, and large sections of data like weights + are directly accessible as sequential C-style arrays of their data type, + with no strides or unpacking needed. We do get a lot of value from using + flatbuffers, but there is a cost in complexity. The flat buffer library code + is all inline + [inside the main headers](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/schema/schema_generated.h), + but it isn't straightforward to inspect their implementations, and the model + data structures aren't easy to comprehend from the debugger. The header for + the schema itself also has to be periodically updated when new information + is added to the file format, though we try to handle that transparently for + most developers by checking in a pre-generated version. + +- **Code Duplication**: Some of the code in this prototype largely duplicates + the logic in other parts of the TensorFlow Lite code base, for example the + operator wrappers. We've tried to keep share as much as we can between the + two interpreters, but there are some assumptions built into the original + runtime that make this difficult. We'll be working on modularizing the main + interpreter so that we can move to an entirely shared system. + +This initial preview release is designed to get early feedback, and is not +intended to be a final product. It only includes enough operations to run a +simple keyword recognition model, and the implementations are not optimized. +We're hoping this will be a good way to get feedback and collaborate to improve +the framework. + +## Getting Started with Make Building requires a Linux or OS X machine. - - Open a terminal - - Download the TensorFlow source with `git clone https://github.com/tensorflow/tensorflow.git` - - Enter the source root directory by running `cd tensorflow` - - Download the dependencies by running `tensorflow/lite/experimental/micro/tools/make/download_dependencies.sh`. This may take a few minutes - - Build and test the library with `make -f tensorflow/lite/experimental/micro/tools/make/Makefile test` +- Open a terminal +- Download the TensorFlow source with `git clone + https://github.com/tensorflow/tensorflow.git` +- Enter the source root directory by running `cd tensorflow` +- Download the dependencies by running + `tensorflow/lite/experimental/micro/tools/make/download_dependencies.sh`. + This may take a few minutes +- Build and test the library with `make -f + tensorflow/lite/experimental/micro/tools/make/Makefile test` You should see a series of compilation steps, followed by `~~~ALL TESTS PASSED~~~` for the various tests of the code that it will run. If there's an error, you should get an informative message from make about what went wrong. -These tests are all built as simple binaries with few dependencies, so you can run them manually. For example, here's how to run the depthwise convolution test, and its output: +These tests are all built as simple binaries with few dependencies, so you can +run them manually. For example, here's how to run the depthwise convolution +test, and its output: ``` tensorflow/lite/experimental/micro/tools/make/gen/linux_x86_64/bin/tensorflow/lite/experimental/micro/kernels/depthwise_conv_test @@ -53,7 +149,9 @@ Testing SimpleTestReluQuantized ~ALL TESTS PASSED~~~ ``` -Looking at the [depthwise_conv_test.cc](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/experimental/micro/kernels/depthwise_conv_test.cc) code, you'll see a sequence that looks like this: +Looking at the +[depthwise_conv_test.cc](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/experimental/micro/kernels/depthwise_conv_test.cc) +code, you'll see a sequence that looks like this: ``` ... @@ -74,19 +172,41 @@ output, and the test harness that runs the binary during the make process knows that everything ran correctly. If there's an error, the lack of the expected string lets the harness know that the test failed. -So, why are we running tests in this complicated way? So far, we've been building binaries that run locally on the Mac OS or Linux machine you're building on, but this approach becomes important when we're targeting simple micro controller devices. +So, why are we running tests in this complicated way? So far, we've been +building binaries that run locally on the Mac OS or Linux machine you're +building on, but this approach becomes important when we're targeting simple +micro controller devices. ## Building for the "Blue Pill" STM32F103 -The goal of this library is to enable machine learning on resource-constrained micro controllers and DSPs, and as part of that we've targeted the ["Blue Pill" STM32F103-compatible development board](https://github.com/google/stm32_bare_lib) as a cheap and popular platform. It only has 20KB of RAM and 64KB of flash, so it's a good device to ensure we can run efficiently on small chips. - -It's fairly easy to [buy and wire up a physical board](https://github.com/google/stm32_bare_lib#wiring-up-your-blue-pill), but even if you don't have an actual device, the [Renode project](https://renode.io/) makes it easy to run a faithful emulation on your desktop machine. You'll need [Docker](https://www.docker.com/) installed, but once you have that set up, try running the following command: - -`make -f tensorflow/lite/experimental/micro/tools/make/Makefile TARGET=bluepill test` - -You should see a similar set of outputs as you did in the previous section, with the addition of some extra Docker logging messages. These are because we're using Docker to run the Renode micro controller emulation tool, and the tests themselves are being run on a simulated STM32F103 device. The communication channels between an embedded device and the host are quite limited, so the test harness looks at the output of the debug log to see if tests have passed, just as it did in the previous section. This makes it a very flexible way to run cross-platform tests, even when a platform has no operating system facilities, as long as it can output debugging text logs. - -To understand what's happening here, try running the same depthwise convolution test, but through the emulated device test harness, with the following command: +The goal of this library is to enable machine learning on resource-constrained +micro controllers and DSPs, and as part of that we've targeted the +["Blue Pill" STM32F103-compatible development board](https://github.com/google/stm32_bare_lib) +as a cheap and popular platform. It only has 20KB of RAM and 64KB of flash, so +it's a good device to ensure we can run efficiently on small chips. + +It's fairly easy to +[buy and wire up a physical board](https://github.com/google/stm32_bare_lib#wiring-up-your-blue-pill), +but even if you don't have an actual device, the +[Renode project](https://renode.io/) makes it easy to run a faithful emulation +on your desktop machine. You'll need [Docker](https://www.docker.com/) +installed, but once you have that set up, try running the following command: + +`make -f tensorflow/lite/experimental/micro/tools/make/Makefile TARGET=bluepill +test` + +You should see a similar set of outputs as you did in the previous section, with +the addition of some extra Docker logging messages. These are because we're +using Docker to run the Renode micro controller emulation tool, and the tests +themselves are being run on a simulated STM32F103 device. The communication +channels between an embedded device and the host are quite limited, so the test +harness looks at the output of the debug log to see if tests have passed, just +as it did in the previous section. This makes it a very flexible way to run +cross-platform tests, even when a platform has no operating system facilities, +as long as it can output debugging text logs. + +To understand what's happening here, try running the same depthwise convolution +test, but through the emulated device test harness, with the following command: ``` tensorflow/lite/experimental/micro/testing/test_bluepill_binary.sh \ @@ -115,7 +235,7 @@ LOGS: 03:27:32.4834 [DEBUG] cpu.uartSemihosting: [+0.18ms host +0s virt 0s virt from start] Testing SimpleTestReluQuantized 03:27:32.4838 [DEBUG] cpu.uartSemihosting: [+0.4ms host +0s virt 0s virt from start] 4/4 tests passed 03:27:32.4839 [DEBUG] cpu.uartSemihosting: [+41µs host +0s virt 0s virt from start] ~~~ALL TESTS PASSED~~~ -03:27:32.4839 [DEBUG] cpu.uartSemihosting: [+5µs host +0s virt 0s virt from start] +03:27:32.4839 [DEBUG] cpu.uartSemihosting: [+5µs host +0s virt 0s virt from start] ... tensorflow/lite/experimental/micro/tools/make/gen/bluepill_cortex-m3/bin/tensorflow/lite/experimental/micro/kernels/depthwise_conv_test: PASS ``` @@ -128,16 +248,19 @@ than your desktop. We hope that the simplicity of this testing approach will help make adding support for new platforms as easy as possible. ## Building for "Hifive1" SiFive FE310 development board -We've targeted the ["HiFive1" Arduino-compatible development board](https://www.sifive.com/boards/hifive1) as a test platform for RISC-V MCU. -Similar to Blue Pill setup, you will need Docker installed. The binary can be executed on either HiFive1 board or emulated using [Renode project](https://renode.io/) on your desktop machine. +We've targeted the +["HiFive1" Arduino-compatible development board](https://www.sifive.com/boards/hifive1) +as a test platform for RISC-V MCU. + +Similar to Blue Pill setup, you will need Docker installed. The binary can be +executed on either HiFive1 board or emulated using +[Renode project](https://renode.io/) on your desktop machine. The following instructions builds and transfers the source files to the Docker -``` -docker build -t riscv_build \ --f {PATH_TO_TENSORFLOW_ROOT_DIR}/tensorflow/lite/experimental/micro/testing/Dockerfile.riscv \ -{PATH_TO_TENSORFLOW_ROOT_DIR}/tensorflow/lite/experimental/micro/testing/ -``` +`docker build -t riscv_build \ -f +{PATH_TO_TENSORFLOW_ROOT_DIR}/tensorflow/lite/experimental/micro/testing/Dockerfile.riscv +\ {PATH_TO_TENSORFLOW_ROOT_DIR}/tensorflow/lite/experimental/micro/testing/` You should see output that looks something like this: @@ -160,18 +283,25 @@ Successfully tagged riscv_build:latest Building micro_speech_test binary - - Lauch the Docker that we just created using: `docker run -it-v /tmp/copybara_out:/workspace riscv_build:latest bash` - - Enter the source root directory by running `cd /workspace` - - Download the dependencies by running `./tensorflow/lite/experimental/micro/tools/make/download_dependencies.sh`. This may take a few minutes. - - Set the path to RISC-V tools: `export PATH=${PATH}:/workspace/tensorflow/lite/experimental/micro/tools/make/downloads/riscv_toolchain/bin/` - - Build the binary: `make -f tensorflow/lite/experimental/micro/tools/make/Makefile TARGET=riscv32_mcu` +- Lauch the Docker that we just created using: `docker run -it-v + /tmp/copybara_out:/workspace riscv_build:latest bash` +- Enter the source root directory by running `cd /workspace` +- Download the dependencies by running + `./tensorflow/lite/experimental/micro/tools/make/download_dependencies.sh`. + This may take a few minutes. +- Set the path to RISC-V tools: `export + PATH=${PATH}:/workspace/tensorflow/lite/experimental/micro/tools/make/downloads/riscv_toolchain/bin/` +- Build the binary: `make -f + tensorflow/lite/experimental/micro/tools/make/Makefile TARGET=riscv32_mcu` Lauching Renode to test the binary, currently this set up is not automated. - - Until https://github.com/renode/renode/pull/30 is in the Docker image, patch the change manully: `sed -E -i 's/"rv32g"/"rv32imac"/g' /opt/renode/platforms/cpus/sifive-fe310.repl` - +- Until https://github.com/renode/renode/pull/30 is in the Docker image, patch + the change manully: `sed -E -i 's/"rv32g"/"rv32imac"/g' + /opt/renode/platforms/cpus/sifive-fe310.repl` - - Execute the binary on Renode: `renode -P 5000 --disable-xwt -e 's @/workspace/tensorflow/lite/experimental/micro/testing/sifive_fe310.resc'` +- Execute the binary on Renode: `renode -P 5000 --disable-xwt -e 's + @/workspace/tensorflow/lite/experimental/micro/testing/sifive_fe310.resc'` You should see the following log with the magic string `~~~ALL TEST PASSED~~~`: @@ -238,3 +368,24 @@ JFlashLiteExe 2. Device = AMA3B1KK-KBR 3. Interface = SWD at 1000 kHz 4. Data file = tensorflow/lite/experimental/micro/tools/make/gen/apollo3evb_cortex-m4/bin/pushbutton_cmsis_speech_test.bin 5. Prog Addr = 0x0000C000 + +## Generating Project Files + +It's not always easy or convenient to use a makefile-based build process, +especially if you're working on a product that uses a different IDE for the rest +of its code. To address that, it's possible to generate standalone project +folders for various popular build systems. These projects are self-contained, +with only the headers and source files needed by a particular binary, and +include project files to make loading them into an IDE easy. These can be +auto-generated for any target you can compile using the main Make system, using +a command like this (making sure you've run `download_dependencies.sh` first): + +``` +make -f tensorflow/lite/experimental/micro/tools/make/Makefile TARGET=mbed TAGS="CMSIS disco_f746ng" generate_micro_speech_main_test_mbed_project +``` + +This will create a folder in +`tensorflow/lite/experimental/micro/tools/make/gen/mbed_cortex-m4/prj/micro_speech_main_test/mbed` +that contains the source and header files, some Mbed configuration files, and a +README. You should then be able to copy this directory to another machine, and +use it just like any other Mbed project. diff --git a/tensorflow/lite/experimental/micro/bluepill/debug_log.cc b/tensorflow/lite/experimental/micro/bluepill/debug_log.cc new file mode 100644 index 0000000000..4812a91849 --- /dev/null +++ b/tensorflow/lite/experimental/micro/bluepill/debug_log.cc @@ -0,0 +1,27 @@ +/* Copyright 2018 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/lite/experimental/micro/debug_log.h" + +// For Arm Cortex-M devices, calling SYS_WRITE0 will output the zero-terminated +// string pointed to by R1 to any debug console that's attached to the system. +extern "C" void DebugLog(const char* s) { + asm("mov r0, #0x04\n" // SYS_WRITE0 + "mov r1, %[str]\n" + "bkpt #0xAB\n" + : + : [ str ] "r"(s) + : "r0", "r1"); +} diff --git a/tensorflow/lite/experimental/micro/debug_log.cc b/tensorflow/lite/experimental/micro/debug_log.cc new file mode 100644 index 0000000000..3d4ca44d76 --- /dev/null +++ b/tensorflow/lite/experimental/micro/debug_log.cc @@ -0,0 +1,41 @@ +/* Copyright 2018 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. +==============================================================================*/ + +// Reference implementation of the DebugLog() function that's required for a +// platform to support the TensorFlow Lite for Microcontrollers library. This is +// the only function that's absolutely required to be available on a target +// device, since it's used for communicating test results back to the host so +// that we can verify the implementation is working correctly. +// It's designed to be as easy as possible to supply an implementation though. +// On platforms that have a POSIX stack or C library, it can be written as a +// single call to `fprintf(stderr, "%s", s)` to output a string to the error +// stream of the console, but if there's no OS or C library available, there's +// almost always an equivalent way to write out a string to some serial +// interface that can be used instead. For example on Arm M-series MCUs, calling +// the `bkpt #0xAB` assembler instruction will output the string in r1 to +// whatever debug serial connection is available. If you're running mbed, you +// can do the same by creating `Serial pc(USBTX, USBRX)` and then calling +// `pc.printf("%s", s)`. +// To add an equivalent function for your own platform, create your own +// implementation file, and place it in a subfolder with named after the OS +// you're targeting. For example, see the Cortex M bare metal version in +// tensorflow/lite/experimental/micro/bluepill/debug_log.cc or the mbed one on +// tensorflow/lite/experimental/micro/mbed/debug_log.cc. + +#include "tensorflow/lite/experimental/micro/debug_log.h" + +#include + +extern "C" void DebugLog(const char* s) { fprintf(stderr, "%s", s); } diff --git a/tensorflow/lite/experimental/micro/debug_log.h b/tensorflow/lite/experimental/micro/debug_log.h new file mode 100644 index 0000000000..c0e395c376 --- /dev/null +++ b/tensorflow/lite/experimental/micro/debug_log.h @@ -0,0 +1,23 @@ +/* Copyright 2018 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_LITE_EXPERIMENTAL_MICRO_DEBUG_LOG_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_DEBUG_LOG_H_ + +// This function should be implemented by each target platform, and provide a +// way for strings to be output to some text stream. For more information, see +// tensorflow/lite/experimental/micro/debug_log.cc. +extern "C" void DebugLog(const char* s); + +#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_DEBUG_LOG_H_ diff --git a/tensorflow/lite/experimental/micro/debug_log_numbers.cc b/tensorflow/lite/experimental/micro/debug_log_numbers.cc new file mode 100644 index 0000000000..8e86730674 --- /dev/null +++ b/tensorflow/lite/experimental/micro/debug_log_numbers.cc @@ -0,0 +1,185 @@ +/* Copyright 2018 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. +==============================================================================*/ + +// Implements debug logging for numbers by converting them into strings and then +// calling the main DebugLog(char*) function. These are separated into a +// different file so that platforms can just implement the string output version +// of DebugLog() and then get the numerical variations without requiring any +// more code. + +#include "tensorflow/lite/experimental/micro/debug_log_numbers.h" + +#include "tensorflow/lite/experimental/micro/debug_log.h" + +namespace { + +// All input buffers to the number conversion functions must be this long. +static const int kFastToBufferSize = 48; + +// Reverses a zero-terminated string in-place. +char* ReverseStringInPlace(char* start, char* end) { + char* p1 = start; + char* p2 = end - 1; + while (p1 < p2) { + char tmp = *p1; + *p1++ = *p2; + *p2-- = tmp; + } + return start; +} + +// Appends a string to a string, in-place. You need to pass in the maximum +// string length as the second argument. +char* StrCatStr(char* main, int main_max_length, const char* to_append) { + char* current = main; + while (*current != 0) { + ++current; + } + char* current_end = main + (main_max_length - 1); + while ((*to_append != 0) && (current < current_end)) { + *current = *to_append; + ++current; + ++to_append; + } + *current = 0; + return current; +} + +// Populates the provided buffer with an ASCII representation of the number. +char* FastUInt32ToBufferLeft(uint32_t i, char* buffer, int base) { + char* start = buffer; + do { + int32_t digit = i % base; + char character; + if (digit < 10) { + character = '0' + digit; + } else { + character = 'a' + (digit - 10); + } + *buffer++ = character; + i /= base; + } while (i > 0); + *buffer = 0; + ReverseStringInPlace(start, buffer); + return buffer; +} + +// Populates the provided buffer with an ASCII representation of the number. +char* FastInt32ToBufferLeft(int32_t i, char* buffer) { + uint32_t u = i; + if (i < 0) { + *buffer++ = '-'; + u = -u; + } + return FastUInt32ToBufferLeft(u, buffer, 10); +} + +// Converts a number to a string and appends it to another. +char* StrCatInt32(char* main, int main_max_length, int32_t number) { + char number_string[kFastToBufferSize]; + FastInt32ToBufferLeft(number, number_string); + return StrCatStr(main, main_max_length, number_string); +} + +// Converts a number to a string and appends it to another. +char* StrCatUInt32(char* main, int main_max_length, uint32_t number, int base) { + char number_string[kFastToBufferSize]; + FastUInt32ToBufferLeft(number, number_string, base); + return StrCatStr(main, main_max_length, number_string); +} + +// Populates the provided buffer with ASCII representation of the float number. +// Avoids the use of any floating point instructions (since these aren't +// supported on many microcontrollers) and as a consequence prints values with +// power-of-two exponents. +char* FastFloatToBufferLeft(float f, char* buffer) { + char* current = buffer; + char* current_end = buffer + (kFastToBufferSize - 1); + // Access the bit fields of the floating point value to avoid requiring any + // float instructions. These constants are derived from IEEE 754. + const uint32_t sign_mask = 0x80000000; + const uint32_t exponent_mask = 0x7f800000; + const int32_t exponent_shift = 23; + const int32_t exponent_bias = 127; + const uint32_t fraction_mask = 0x007fffff; + const uint32_t u = *reinterpret_cast(&f); + const int32_t exponent = + ((u & exponent_mask) >> exponent_shift) - exponent_bias; + const uint32_t fraction = (u & fraction_mask); + // Expect ~0x2B1B9D3 for fraction. + if (u & sign_mask) { + *current = '-'; + current += 1; + } + *current = 0; + // These are special cases for infinities and not-a-numbers. + if (exponent == 128) { + if (fraction == 0) { + current = StrCatStr(current, (current_end - current), "Inf"); + return current; + } else { + current = StrCatStr(current, (current_end - current), "NaN"); + return current; + } + } + // 0x007fffff (8388607) represents 0.99... for the fraction, so to print the + // correct decimal digits we need to scale our value before passing it to the + // conversion function. This scale should be 10000000/8388608 = 1.1920928955. + // We can approximate this using multiply-adds and right-shifts using the + // values in this array. The 1. portion of the number string is printed out + // in a fixed way before the fraction, below. + const int32_t scale_shifts_size = 13; + const int8_t scale_shifts[13] = {3, 4, 8, 11, 13, 14, 17, + 18, 19, 20, 21, 22, 23}; + uint32_t scaled_fraction = fraction; + for (int i = 0; i < scale_shifts_size; ++i) { + scaled_fraction += (fraction >> scale_shifts[i]); + } + *current = '1'; + current += 1; + *current = '.'; + current += 1; + *current = 0; + current = StrCatUInt32(current, (current_end - current), scaled_fraction, 10); + current = StrCatStr(current, (current_end - current), "*2^"); + current = StrCatInt32(current, (current_end - current), exponent); + return current; +} + +} // namespace + +extern "C" void DebugLogInt32(int32_t i) { + char number_string[kFastToBufferSize]; + FastInt32ToBufferLeft(i, number_string); + DebugLog(number_string); +} + +extern "C" void DebugLogUInt32(uint32_t i) { + char number_string[kFastToBufferSize]; + FastUInt32ToBufferLeft(i, number_string, 10); + DebugLog(number_string); +} + +extern "C" void DebugLogHex(uint32_t i) { + char number_string[kFastToBufferSize]; + FastUInt32ToBufferLeft(i, number_string, 16); + DebugLog(number_string); +} + +extern "C" void DebugLogFloat(float i) { + char number_string[kFastToBufferSize]; + FastFloatToBufferLeft(i, number_string); + DebugLog(number_string); +} diff --git a/tensorflow/lite/experimental/micro/debug_log_numbers.h b/tensorflow/lite/experimental/micro/debug_log_numbers.h new file mode 100644 index 0000000000..d889e75173 --- /dev/null +++ b/tensorflow/lite/experimental/micro/debug_log_numbers.h @@ -0,0 +1,28 @@ +/* Copyright 2018 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_LITE_EXPERIMENTAL_MICRO_DEBUG_LOG_NUMBERS_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_DEBUG_LOG_NUMBERS_H_ + +#include + +// Output numbers to the debug logging stream. +extern "C" { +void DebugLogInt32(int32_t i); +void DebugLogUInt32(uint32_t i); +void DebugLogHex(uint32_t i); +void DebugLogFloat(float i); +} + +#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_DEBUG_LOG_NUMBERS_H_ diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/Makefile.inc b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/Makefile.inc new file mode 100644 index 0000000000..3d560510ad --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/Makefile.inc @@ -0,0 +1,43 @@ +# Settings for targets that use the CMSIS library. +ifneq ($(filter CMSIS,$(ALL_TAGS)),) + INCLUDES += \ + -isystem$(MAKEFILE_DIR)/downloads/cmsis/CMSIS/Core/Include/ \ + -isystem$(MAKEFILE_DIR)/downloads/cmsis/CMSIS/DSP/Include/ \ + -I$(MAKEFILE_DIR)/downloads/CMSIS_ext/ + + CMSIS_PREPROCESSOR_SRCS := \ + tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.cc \ + tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.cc \ + + CMSIS_PREPROCESSOR_HDRS := \ + tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/hanning.h \ + tensorflow/lite/experimental/micro/examples/micro_speech/CMSIS/sin_1k.h \ + third_party/CMSIS_ext/arm_cmplx_mag_squared_q10p6.h + + PREPROCESSOR_TEST_SRCS += $(CMSIS_PREPROCESSOR_SRCS) + PREPROCESSOR_TEST_HDRS += $(CMSIS_PREPROCESSOR_HDRS) + + FEATURE_PROVIDER_TEST_SRCS += $(CMSIS_PREPROCESSOR_SRCS) + FEATURE_PROVIDER_TEST_HDRS += $(CMSIS_PREPROCESSOR_HDRS) + + MICRO_SPEECH_SRCS += $(CMSIS_PREPROCESSOR_SRCS) + MICRO_SPEECH_HDRS += $(CMSIS_PREPROCESSOR_HDRS) + + THIRD_PARTY_CC_SRCS += \ + third_party/CMSIS_ext/arm_cmplx_mag_squared_q10p6.c \ + third_party/cmsis/CMSIS/DSP/Source/BasicMathFunctions/arm_mult_q15.c \ + third_party/cmsis/CMSIS/DSP/Source/TransformFunctions/arm_rfft_init_q15.c \ + third_party/cmsis/CMSIS/DSP/Source/TransformFunctions/arm_rfft_q15.c \ + third_party/cmsis/CMSIS/DSP/Source/TransformFunctions/arm_cfft_q15.c \ + third_party/cmsis/CMSIS/DSP/Source/TransformFunctions/arm_cfft_radix4_q15.c \ + third_party/cmsis/CMSIS/DSP/Source/TransformFunctions/arm_bitreversal2.S \ + third_party/cmsis/CMSIS/DSP/Source/CommonTables/arm_const_structs.c \ + third_party/cmsis/CMSIS/DSP/Source/CommonTables/arm_common_tables.c \ + third_party/cmsis/CMSIS/DSP/Source/StatisticsFunctions/arm_mean_q15.c \ + third_party/cmsis/CMSIS/DSP/Source/StatisticsFunctions/arm_max_q7.c + + THIRD_PARTY_CC_HDRS += \ + third_party/cmsis/CMSIS/DSP/Include/arm_common_tables.h \ + third_party/cmsis/CMSIS/DSP/Include/arm_const_structs.h + +endif diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/Makefile.inc b/tensorflow/lite/experimental/micro/examples/micro_speech/Makefile.inc index 6f7563c02f..49aace3d7d 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/Makefile.inc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/Makefile.inc @@ -1,140 +1,62 @@ -# Tests loading and running a speech model. MICRO_SPEECH_TEST_SRCS := \ tensorflow/lite/experimental/micro/examples/micro_speech/micro_speech_test.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/tiny_conv_model_data.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/no_features_data.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/yes_features_data.cc -MICRO_SPEECH_TEST_SRCS := $(call specialize,$(MICRO_SPEECH_TEST_SRCS)) -ALL_SRCS += $(MICRO_SPEECH_TEST_SRCS) -MICRO_SPEECH_TEST_OBJS := $(addprefix $(OBJDIR), \ -$(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(MICRO_SPEECH_TEST_SRCS)))) -MICRO_SPEECH_TEST_BINARY := $(BINDIR)micro_speech_test -ALL_BINARIES += $(MICRO_SPEECH_TEST_BINARY) -$(MICRO_SPEECH_TEST_BINARY): $(MICRO_SPEECH_TEST_OBJS) $(MICROLITE_LIB_PATH) - @mkdir -p $(dir $@) - $(CXX) $(CXXFLAGS) $(INCLUDES) \ - -o $(MICRO_SPEECH_TEST_BINARY) $(MICRO_SPEECH_TEST_OBJS) \ - $(LIBFLAGS) $(MICROLITE_LIB_PATH) $(LDFLAGS) $(MICROLITE_LIBS) -micro_speech_test: $(MICRO_SPEECH_TEST_BINARY) -micro_speech_test_bin: $(MICRO_SPEECH_TEST_BINARY).bin -test_micro_speech: $(MICRO_SPEECH_TEST_BINARY) - $(TEST_SCRIPT) $(MICRO_SPEECH_TEST_BINARY) '~~~ALL TESTS PASSED~~~' - -# Source files that are used by multiple preprocessor tests. -PREPROCESSOR_TEST_SHARED_SRCS := \ + +MICRO_SPEECH_TEST_HDRS := \ +tensorflow/lite/experimental/micro/examples/micro_speech/tiny_conv_model_data.h \ +tensorflow/lite/experimental/micro/examples/micro_speech/no_features_data.h \ +tensorflow/lite/experimental/micro/examples/micro_speech/yes_features_data.h \ + +PREPROCESSOR_TEST_SRCS := \ +tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor_test.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/no_30ms_sample_data.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/yes_30ms_sample_data.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/no_power_spectrum_data.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/yes_power_spectrum_data.cc -# Test the float reference code for feature generation. -PREPROCESSOR_REFERENCE_TEST_SRCS = \ -$(PREPROCESSOR_TEST_SHARED_SRCS) \ -tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.cc -PREPROCESSOR_REFERENCE_TEST_SRCS := $(call specialize,$(PREPROCESSOR_REFERENCE_TEST_SRCS)) -ALL_SRCS += $(PREPROCESSOR_REFERENCE_TEST_SRCS) -PREPROCESSOR_REFERENCE_TEST_OBJS := $(addprefix $(OBJDIR), \ -$(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(PREPROCESSOR_REFERENCE_TEST_SRCS)))) -PREPROCESSOR_REFERENCE_TEST_BINARY := $(BINDIR)preprocessor_reference_test -ALL_BINARIES += $(PREPROCESSOR_REFERENCE_TEST_BINARY) -$(PREPROCESSOR_REFERENCE_TEST_BINARY): $(PREPROCESSOR_REFERENCE_TEST_OBJS) $(MICROLITE_LIB_PATH) - @mkdir -p $(dir $@) - $(CXX) $(CXXFLAGS) $(INCLUDES) \ - -o $(PREPROCESSOR_REFERENCE_TEST_BINARY) $(PREPROCESSOR_REFERENCE_TEST_OBJS) \ - $(LIBFLAGS) $(MICROLITE_LIB_PATH) $(LDFLAGS) $(MICROLITE_LIBS) -preprocessor_reference_test: $(PREPROCESSOR_REFERENCE_TEST_BINARY) -preprocessor_reference_test_bin: $(PREPROCESSOR_REFERENCE_TEST_BINARY).bin -test_preprocessor_reference: $(PREPROCESSOR_REFERENCE_TEST_BINARY) - $(TEST_SCRIPT) $(PREPROCESSOR_REFERENCE_TEST_BINARY) '~~~ALL TESTS PASSED~~~' - -# Test the fixed point reference code for feature generation. -PREPROCESSOR_FIXED_TEST_SRCS = \ -$(PREPROCESSOR_TEST_SHARED_SRCS) \ -tensorflow/lite/experimental/micro/examples/micro_speech/fixed_point/preprocessor.cc -PREPROCESSOR_FIXED_TEST_SRCS := $(call specialize,$(PREPROCESSOR_FIXED_TEST_SRCS)) -ALL_SRCS += $(PREPROCESSOR_FIXED_TEST_SRCS) -PREPROCESSOR_FIXED_TEST_OBJS := $(addprefix $(OBJDIR), \ -$(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(PREPROCESSOR_FIXED_TEST_SRCS)))) -PREPROCESSOR_FIXED_TEST_BINARY := $(BINDIR)preprocessor_fixed_test -ALL_BINARIES += $(PREPROCESSOR_FIXED_TEST_BINARY) -$(PREPROCESSOR_FIXED_TEST_BINARY): $(PREPROCESSOR_FIXED_TEST_OBJS) $(MICROLITE_LIB_PATH) - @mkdir -p $(dir $@) - $(CXX) $(CXXFLAGS) $(INCLUDES) \ - -o $(PREPROCESSOR_FIXED_TEST_BINARY) $(PREPROCESSOR_FIXED_TEST_OBJS) \ - $(LIBFLAGS) $(MICROLITE_LIB_PATH) $(LDFLAGS) $(MICROLITE_LIBS) -preprocessor_fixed_test: $(PREPROCESSOR_FIXED_TEST_BINARY) -preprocessor_fixed_test_bin: $(PREPROCESSOR_FIXED_TEST_BINARY).bin -test_preprocessor_fixed: $(PREPROCESSOR_FIXED_TEST_BINARY) - $(TEST_SCRIPT) $(PREPROCESSOR_FIXED_TEST_BINARY) '~~~ALL TESTS PASSED~~~' +PREPROCESSOR_TEST_HDRS := \ +tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.h \ +tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h \ +tensorflow/lite/experimental/micro/examples/micro_speech/no_30ms_sample_data.h \ +tensorflow/lite/experimental/micro/examples/micro_speech/yes_30ms_sample_data.h \ +tensorflow/lite/experimental/micro/examples/micro_speech/no_power_spectrum_data.h \ +tensorflow/lite/experimental/micro/examples/micro_speech/yes_power_spectrum_data.h -# Tests the audio provider module. AUDIO_PROVIDER_TEST_SRCS := \ tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider_test.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.cc -AUDIO_PROVIDER_TEST_SRCS := $(call specialize,$(AUDIO_PROVIDER_TEST_SRCS)) -ALL_SRCS += $(AUDIO_PROVIDER_TEST_SRCS) -AUDIO_PROVIDER_TEST_OBJS := $(addprefix $(OBJDIR), \ -$(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(AUDIO_PROVIDER_TEST_SRCS)))) -AUDIO_PROVIDER_TEST_BINARY := $(BINDIR)audio_provider_test -ALL_BINARIES += $(AUDIO_PROVIDER_TEST_BINARY) -$(AUDIO_PROVIDER_TEST_BINARY): $(AUDIO_PROVIDER_TEST_OBJS) $(MICROLITE_LIB_PATH) - @mkdir -p $(dir $@) - $(CXX) $(CXXFLAGS) $(INCLUDES) \ - -o $(AUDIO_PROVIDER_TEST_BINARY) $(AUDIO_PROVIDER_TEST_OBJS) \ - $(LIBFLAGS) $(MICROLITE_LIB_PATH) $(LDFLAGS) $(MICROLITE_LIBS) -audio_provider_test: $(AUDIO_PROVIDER_TEST_BINARY) -audio_provider_test_bin: $(AUDIO_PROVIDER_TEST_BINARY).bin -test_audio_provider: $(AUDIO_PROVIDER_TEST_BINARY) - $(TEST_SCRIPT) $(AUDIO_PROVIDER_TEST_BINARY) '~~~ALL TESTS PASSED~~~' -# Tests the feature provider module. +AUDIO_PROVIDER_TEST_HDRS := \ +tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h \ +tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.h \ + FEATURE_PROVIDER_TEST_SRCS := \ tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider_test.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider.cc -FEATURE_PROVIDER_TEST_SRCS := $(call specialize,$(FEATURE_PROVIDER_TEST_SRCS)) -ALL_SRCS += $(FEATURE_PROVIDER_TEST_SRCS) -FEATURE_PROVIDER_TEST_OBJS := $(addprefix $(OBJDIR), \ -$(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(FEATURE_PROVIDER_TEST_SRCS)))) -FEATURE_PROVIDER_TEST_BINARY := $(BINDIR)feature_provider_test -ALL_BINARIES += $(FEATURE_PROVIDER_TEST_BINARY) -$(FEATURE_PROVIDER_TEST_BINARY): $(FEATURE_PROVIDER_TEST_OBJS) $(MICROLITE_LIB_PATH) - @mkdir -p $(dir $@) - $(CXX) $(CXXFLAGS) $(INCLUDES) \ - -o $(FEATURE_PROVIDER_TEST_BINARY) $(FEATURE_PROVIDER_TEST_OBJS) \ - $(LIBFLAGS) $(MICROLITE_LIB_PATH) $(LDFLAGS) $(MICROLITE_LIBS) -feature_provider_test: $(FEATURE_PROVIDER_TEST_BINARY) -feature_provider_test_bin: $(FEATURE_PROVIDER_TEST_BINARY).bin -test_feature_provider: $(FEATURE_PROVIDER_TEST_BINARY) - $(TEST_SCRIPT) $(FEATURE_PROVIDER_TEST_BINARY) '~~~ALL TESTS PASSED~~~' -# Tests the feature provider module. +FEATURE_PROVIDER_TEST_HDRS := \ +tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h \ +tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.h \ +tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.h \ +tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider.h + RECOGNIZE_COMMANDS_TEST_SRCS := \ tensorflow/lite/experimental/micro/examples/micro_speech/recognize_commands_test.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/recognize_commands.cc -RECOGNIZE_COMMANDS_TEST_SRCS := $(call specialize,$(RECOGNIZE_COMMANDS_TEST_SRCS)) -ALL_SRCS += $(RECOGNIZE_COMMANDS_TEST_SRCS) -RECOGNIZE_COMMANDS_TEST_OBJS := $(addprefix $(OBJDIR), \ -$(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(RECOGNIZE_COMMANDS_TEST_SRCS)))) -RECOGNIZE_COMMANDS_TEST_BINARY := $(BINDIR)recognize_commands_test -ALL_BINARIES += $(RECOGNIZE_COMMANDS_TEST_BINARY) -$(RECOGNIZE_COMMANDS_TEST_BINARY): $(RECOGNIZE_COMMANDS_TEST_OBJS) $(MICROLITE_LIB_PATH) - @mkdir -p $(dir $@) - $(CXX) $(CXXFLAGS) $(INCLUDES) \ - -o $(RECOGNIZE_COMMANDS_TEST_BINARY) $(RECOGNIZE_COMMANDS_TEST_OBJS) \ - $(LIBFLAGS) $(MICROLITE_LIB_PATH) $(LDFLAGS) $(MICROLITE_LIBS) -recognize_commands_test: $(RECOGNIZE_COMMANDS_TEST_BINARY) -recognize_commands_test_bin: $(RECOGNIZE_COMMANDS_TEST_BINARY).bin -test_recognize_commands: $(RECOGNIZE_COMMANDS_TEST_BINARY) - $(TEST_SCRIPT) $(RECOGNIZE_COMMANDS_TEST_BINARY) '~~~ALL TESTS PASSED~~~' -# Builds a standalone speech command recognizer binary. +RECOGNIZE_COMMANDS_TEST_HDRS := \ +tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h \ +tensorflow/lite/experimental/micro/examples/micro_speech/recognize_commands.h + MICRO_SPEECH_SRCS := \ tensorflow/lite/experimental/micro/examples/micro_speech/main.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.cc \ @@ -145,19 +67,40 @@ tensorflow/lite/experimental/micro/examples/micro_speech/no_features_data.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/yes_features_data.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/tiny_conv_model_data.cc \ tensorflow/lite/experimental/micro/examples/micro_speech/recognize_commands.cc -MICRO_SPEECH_SRCS := $(call specialize,$(MICRO_SPEECH_SRCS)) -ALL_SRCS += $(MICRO_SPEECH_SRCS) -MICRO_SPEECH_OBJS := $(addprefix $(OBJDIR), \ -$(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(MICRO_SPEECH_SRCS)))) -MICRO_SPEECH_BINARY := $(BINDIR)micro_speech -ALL_BINARIES += $(MICRO_SPEECH_BINARY) -$(MICRO_SPEECH_BINARY): $(MICRO_SPEECH_OBJS) $(MICROLITE_LIB_PATH) - @mkdir -p $(dir $@) - $(CXX) $(CXXFLAGS) $(INCLUDES) \ - -o $(MICRO_SPEECH_BINARY) $(MICRO_SPEECH_OBJS) \ - $(LIBFLAGS) $(MICROLITE_LIB_PATH) $(LDFLAGS) $(MICROLITE_LIBS) -micro_speech: $(MICRO_SPEECH_BINARY) -micro_speech_bin: $(MICRO_SPEECH_BINARY).bin + +MICRO_SPEECH_HDRS := \ +tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h \ +tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.h \ +tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider.h \ +tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.h \ +tensorflow/lite/experimental/micro/examples/micro_speech/no_features_data.h \ +tensorflow/lite/experimental/micro/examples/micro_speech/yes_features_data.h \ +tensorflow/lite/experimental/micro/examples/micro_speech/tiny_conv_model_data.h \ +tensorflow/lite/experimental/micro/examples/micro_speech/recognize_commands.h # Find any platform-specific rules for this example. include $(wildcard tensorflow/lite/experimental/micro/examples/micro_speech/*/Makefile.inc) + +# Tests loading and running a speech model. +$(eval $(call microlite_test,micro_speech_test,\ +$(MICRO_SPEECH_TEST_SRCS),$(MICRO_SPEECH_TEST_HDRS))) + +# Test the code for feature generation. +$(eval $(call microlite_test,preprocessor_test,\ +$(PREPROCESSOR_TEST_SRCS), $(PREPROCESSOR_TEST_HDRS))) + +# Tests the audio provider module. +$(eval $(call microlite_test,audio_provider_test,\ +$(AUDIO_PROVIDER_TEST_SRCS),$(AUDIO_PROVIDER_TEST_HDRS))) + +# Tests the feature provider module. +$(eval $(call microlite_test,feature_provider_test,\ +$(FEATURE_PROVIDER_TEST_SRCS),$(FEATURE_PROVIDER_TEST_HDRS))) + +# Tests the feature provider module. +$(eval $(call microlite_test,recognize_commands_test,\ +$(RECOGNIZE_COMMANDS_TEST_SRCS),$(RECOGNIZE_COMMANDS_TEST_HDRS))) + +# Builds a standalone speech command recognizer binary. +$(eval $(call microlite_test,micro_speech,\ +$(MICRO_SPEECH_SRCS),$(MICRO_SPEECH_HDRS))) diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/disco_f746ng/Makefile.inc b/tensorflow/lite/experimental/micro/examples/micro_speech/disco_f746ng/Makefile.inc new file mode 100644 index 0000000000..5585ed7269 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/disco_f746ng/Makefile.inc @@ -0,0 +1,7 @@ +# Settings for the Discovery STM32F746NG board. +ifneq ($(filter disco_f746ng,$(ALL_TAGS)),) + MBED_PROJECT_FILES += \ + AUDIO_DISCO_F746NG.lib \ + BSP_DISCO_F746NG.lib \ + SDRAM_DISCO_F746NG.lib +endif diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/disco_f746ng/audio_provider.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/disco_f746ng/audio_provider.cc new file mode 100644 index 0000000000..06647d0c53 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/disco_f746ng/audio_provider.cc @@ -0,0 +1,182 @@ +/* Copyright 2018 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/lite/experimental/micro/examples/micro_speech/audio_provider.h" + +#include "tensorflow/lite/experimental/micro/examples/micro_speech/model_settings.h" + +#include "AUDIO_DISCO_F746NG.h" +#include "SDRAM_DISCO_F746NG.h" +#include "mbed.h" // NOLINT + +namespace { + +bool g_is_audio_initialized = false; +constexpr int kAudioCaptureBufferSize = kAudioSampleFrequency * 0.5; +int16_t g_audio_capture_buffer[kAudioCaptureBufferSize]; +int16_t g_audio_output_buffer[kMaxAudioSampleSize]; +int32_t g_latest_audio_timestamp = 0; + +// For a full example of how to access audio on the STM32F746NG board, see +// https://os.mbed.com/teams/ST/code/DISCO-F746NG_AUDIO_demo/ +AUDIO_DISCO_F746NG g_audio_device; +SDRAM_DISCO_F746NG g_sdram_device; + +typedef enum { + BUFFER_OFFSET_NONE = 0, + BUFFER_OFFSET_HALF = 1, + BUFFER_OFFSET_FULL = 2, +} BUFFER_StateTypeDef; + +#define AUDIO_BLOCK_SIZE ((uint32_t)2048) +#define AUDIO_BUFFER_IN SDRAM_DEVICE_ADDR /* In SDRAM */ +#define AUDIO_BUFFER_OUT \ + (SDRAM_DEVICE_ADDR + (AUDIO_BLOCK_SIZE * 2)) /* In SDRAM */ +__IO uint32_t g_audio_rec_buffer_state = BUFFER_OFFSET_NONE; + +uint8_t SetSysClock_PLL_HSE_200MHz() { + RCC_ClkInitTypeDef RCC_ClkInitStruct; + RCC_OscInitTypeDef RCC_OscInitStruct; + + // Enable power clock + __PWR_CLK_ENABLE(); + + // Enable HSE oscillator and activate PLL with HSE as source + RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; + RCC_OscInitStruct.HSEState = RCC_HSE_ON; /* External xtal on OSC_IN/OSC_OUT */ + + // Warning: this configuration is for a 25 MHz xtal clock only + RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; + RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; + RCC_OscInitStruct.PLL.PLLM = 25; // VCO input clock = 1 MHz (25 MHz / 25) + RCC_OscInitStruct.PLL.PLLN = 400; // VCO output clock = 400 MHz (1 MHz * 400) + RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; // PLLCLK = 200 MHz (400 MHz / 2) + RCC_OscInitStruct.PLL.PLLQ = 8; // USB clock = 50 MHz (400 MHz / 8) + + if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { + return 0; // FAIL + } + + // Activate the OverDrive to reach the 216 MHz Frequency + if (HAL_PWREx_EnableOverDrive() != HAL_OK) { + return 0; // FAIL + } + + // Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 + // clocks dividers + RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | + RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2); + RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // 200 MHz + RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // 200 MHz + RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; // 50 MHz + RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; // 100 MHz + + if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7) != HAL_OK) { + return 0; // FAIL + } + HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_HSE, RCC_MCODIV_4); + return 1; // OK +} + +TfLiteStatus InitAudioRecording(tflite::ErrorReporter* error_reporter) { + SetSysClock_PLL_HSE_200MHz(); + + // Initialize SDRAM buffers. + memset((uint16_t*)AUDIO_BUFFER_IN, 0, AUDIO_BLOCK_SIZE * 2); + memset((uint16_t*)AUDIO_BUFFER_OUT, 0, AUDIO_BLOCK_SIZE * 2); + g_audio_rec_buffer_state = BUFFER_OFFSET_NONE; + + // Start Recording. + g_audio_device.IN_Record((uint16_t*)AUDIO_BUFFER_IN, AUDIO_BLOCK_SIZE); + + // Also play results out to headphone jack. + g_audio_device.OUT_SetAudioFrameSlot(CODEC_AUDIOFRAME_SLOT_02); + g_audio_device.OUT_Play((uint16_t*)AUDIO_BUFFER_OUT, AUDIO_BLOCK_SIZE * 2); + + return kTfLiteOk; +} + +void CaptureSamples(const int16_t* sample_data) { + const int sample_size = AUDIO_BLOCK_SIZE / (sizeof(int16_t) * 2); + const int32_t time_in_ms = + g_latest_audio_timestamp + (sample_size / (kAudioSampleFrequency / 1000)); + + const int32_t start_sample_offset = + g_latest_audio_timestamp * (kAudioSampleFrequency / 1000); + for (int i = 0; i < sample_size; ++i) { + const int capture_index = + (start_sample_offset + i) % kAudioCaptureBufferSize; + g_audio_capture_buffer[capture_index] = + (sample_data[(i * 2) + 0] / 2) + (sample_data[(i * 2) + 1] / 2); + } + // This is how we let the outside world know that new audio data has arrived. + g_latest_audio_timestamp = time_in_ms; +} + +} // namespace + +// These callbacks need to be linkable symbols, because they override weak +// default versions. +void BSP_AUDIO_IN_TransferComplete_CallBack(void) { + g_audio_rec_buffer_state = BUFFER_OFFSET_FULL; + /* Copy recorded 1st half block */ + memcpy((uint16_t*)(AUDIO_BUFFER_OUT), (uint16_t*)(AUDIO_BUFFER_IN), + AUDIO_BLOCK_SIZE); + CaptureSamples(reinterpret_cast(AUDIO_BUFFER_IN)); + return; +} + +// Another weak symbol override. +void BSP_AUDIO_IN_HalfTransfer_CallBack(void) { + g_audio_rec_buffer_state = BUFFER_OFFSET_HALF; + /* Copy recorded 2nd half block */ + memcpy((uint16_t*)(AUDIO_BUFFER_OUT + (AUDIO_BLOCK_SIZE)), + (uint16_t*)(AUDIO_BUFFER_IN + (AUDIO_BLOCK_SIZE)), AUDIO_BLOCK_SIZE); + CaptureSamples( + reinterpret_cast(AUDIO_BUFFER_IN + AUDIO_BLOCK_SIZE)); + return; +} + +// Main entry point for getting audio data. +TfLiteStatus GetAudioSamples(tflite::ErrorReporter* error_reporter, + int start_ms, int duration_ms, + int* audio_samples_size, int16_t** audio_samples) { + if (!g_is_audio_initialized) { + TfLiteStatus init_status = InitAudioRecording(error_reporter); + if (init_status != kTfLiteOk) { + return init_status; + } + g_is_audio_initialized = true; + } + // This should only be called when the main thread notices that the latest + // audio sample data timestamp has changed, so that there's new data in the + // capture ring buffer. The ring buffer will eventually wrap around and + // overwrite the data, but the assumption is that the main thread is checking + // often enough and the buffer is large enough that this call will be made + // before that happens. + const int start_offset = start_ms * (kAudioSampleFrequency / 1000); + const int duration_sample_count = + duration_ms * (kAudioSampleFrequency / 1000); + for (int i = 0; i < duration_sample_count; ++i) { + const int capture_index = (start_offset + i) % kAudioCaptureBufferSize; + g_audio_output_buffer[i] = g_audio_capture_buffer[capture_index]; + } + + *audio_samples_size = kMaxAudioSampleSize; + *audio_samples = g_audio_output_buffer; + return kTfLiteOk; +} + +int32_t LatestAudioTimestamp() { return g_latest_audio_timestamp; } diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/disco_f746ng/timer.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/disco_f746ng/timer.cc new file mode 100644 index 0000000000..a8f0fe4bd5 --- /dev/null +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/disco_f746ng/timer.cc @@ -0,0 +1,24 @@ +/* Copyright 2018 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/lite/experimental/micro/examples/micro_speech/timer.h" + +namespace { +int32_t g_current_time = 0; +} + +void SetTimeInMilliseconds(int32_t time) { g_current_time = time; } + +int32_t TimeInMilliseconds() { return g_current_time; } diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/osx/Makefile.inc b/tensorflow/lite/experimental/micro/examples/micro_speech/osx/Makefile.inc index 89d107cfe0..8f8b33a9fa 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/osx/Makefile.inc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/osx/Makefile.inc @@ -1,6 +1,8 @@ -#Settings for Mac OS platforms. +# Settings for Mac OS platforms. ifeq ($(TARGET), osx) - MICROLITE_LIBS += \ + LINKER_FLAGS := \ -framework Foundation \ -framework AudioToolbox + + MICROLITE_LIBS += $(LINKER_FLAGS) endif diff --git a/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.cc b/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.cc index 7b7db173ab..f8858aad72 100644 --- a/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.cc +++ b/tensorflow/lite/experimental/micro/examples/micro_speech/preprocessor.cc @@ -32,6 +32,9 @@ limitations under the License. namespace { +// Needed because some platforms don't have M_PI defined. +constexpr float kPi = 3.14159265358979323846f; + // Performs a discrete Fourier transform on the real inputs. This corresponds to // rdft() in the FFT package at http://www.kurims.kyoto-u.ac.jp/~ooura/fft.html, // and to kiss_fftr() in KISSFFT at https://github.com/mborgerding/kissfft. @@ -48,11 +51,11 @@ void CalculateDiscreteFourierTransform(float* time_series, int time_series_size, for (int i = 0; i < time_series_size / 2; ++i) { float real = 0; for (int j = 0; j < time_series_size; ++j) { - real += time_series[j] * cos(j * i * M_PI * 2 / time_series_size); + real += time_series[j] * cos(j * i * kPi * 2 / time_series_size); } float imaginary = 0; for (int j = 0; j < time_series_size; ++j) { - imaginary -= time_series[j] * sin(j * i * M_PI * 2 / time_series_size); + imaginary -= time_series[j] * sin(j * i * kPi * 2 / time_series_size); } fourier_output[(i * 2) + 0] = real; fourier_output[(i * 2) + 1] = imaginary; @@ -63,7 +66,7 @@ void CalculateDiscreteFourierTransform(float* time_series, int time_series_size, // of the current sample window are weighted more heavily than those at the end. void CalculatePeriodicHann(int window_length, float* window_function) { for (int i = 0; i < window_length; ++i) { - window_function[i] = 0.5 - 0.5 * cos((2 * M_PI * i) / window_length); + window_function[i] = 0.5 - 0.5 * cos((2 * kPi * i) / window_length); } } diff --git a/tensorflow/lite/experimental/micro/mbed/debug_log.cc b/tensorflow/lite/experimental/micro/mbed/debug_log.cc new file mode 100644 index 0000000000..d4a4a5a842 --- /dev/null +++ b/tensorflow/lite/experimental/micro/mbed/debug_log.cc @@ -0,0 +1,24 @@ +/* Copyright 2018 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/lite/experimental/micro/debug_log.h" + +#include + +// On mbed platforms, we set up a serial port and write to it for debug logging. +extern "C" void DebugLog(const char* s) { + static Serial pc(USBTX, USBRX); + pc.printf("%s", s); +} diff --git a/tensorflow/lite/experimental/micro/micro_error_reporter.h b/tensorflow/lite/experimental/micro/micro_error_reporter.h index 0ab853ec2a..6c18367c95 100644 --- a/tensorflow/lite/experimental/micro/micro_error_reporter.h +++ b/tensorflow/lite/experimental/micro/micro_error_reporter.h @@ -17,26 +17,8 @@ limitations under the License. #include "tensorflow/lite/core/api/error_reporter.h" #include "tensorflow/lite/experimental/micro/compatibility.h" - -#ifdef TF_LITE_MCU_DEBUG_LOG -// These functions should be supplied by the micro target library -extern "C" { -#include -void DebugLog(const char* s); -void DebugLogInt32(int32_t i); -void DebugLogUInt32(uint32_t i); -void DebugLogHex(uint32_t i); -void DebugLogFloat(float i); -} -#else // TF_LITE_MCU_DEBUG_LOG -#include -#include -static void inline DebugLog(const char* s) { fprintf(stderr, "%s", s); } -static void inline DebugLogInt32(int32_t i) { fprintf(stderr, "%d", i); } -static void inline DebugLogUInt32(uint32_t i) { fprintf(stderr, "%d", i); } -static void inline DebugLogHex(uint32_t i) { fprintf(stderr, "0x%8x", i); } -static void inline DebugLogFloat(float i) { fprintf(stderr, "%f", i); } -#endif // TF_LITE_MCU_DEBUG_LOG +#include "tensorflow/lite/experimental/micro/debug_log.h" +#include "tensorflow/lite/experimental/micro/debug_log_numbers.h" namespace tflite { diff --git a/tensorflow/lite/experimental/micro/riscv32_mcu/debug_log.cc b/tensorflow/lite/experimental/micro/riscv32_mcu/debug_log.cc index f0065267bf..d1c2df866e 100644 --- a/tensorflow/lite/experimental/micro/riscv32_mcu/debug_log.cc +++ b/tensorflow/lite/experimental/micro/riscv32_mcu/debug_log.cc @@ -15,176 +15,4 @@ limitations under the License. #include -namespace { - -// All input buffers to the number conversion functions must be this long. -static const int kFastToBufferSize = 48; - -// Reverses a zero-terminated string in-place. -char* ReverseStringInPlace(char* start, char* end) { - char* p1 = start; - char* p2 = end - 1; - while (p1 < p2) { - char tmp = *p1; - *p1++ = *p2; - *p2-- = tmp; - } - return start; -} - -// Appends a string to a string, in-place. You need to pass in the maximum -// string length as the second argument. -char* StrCatStr(char* main, int main_max_length, char* to_append) { - char* current = main; - while (*current != 0) { - ++current; - } - char* current_end = main + (main_max_length - 1); - while ((*to_append != 0) && (current < current_end)) { - *current = *to_append; - ++current; - ++to_append; - } - *current = 0; - return current; -} - -char* StrCpy(char* main, int main_max_length, const char* source) { - char* current = main; - char* current_end = main + (main_max_length - 1); - while ((*source != 0) && (current < current_end)) { - *current = *source; - ++current; - ++source; - } - *current = 0; - return current; -} - -// Populates the provided buffer with an ASCII representation of the number. -char* FastUInt32ToBufferLeft(uint32_t i, char* buffer, int base) { - char* start = buffer; - do { - int32_t digit = i % base; - char character; - if (digit < 10) { - character = '0' + digit; - } else { - character = 'a' + (digit - 10); - } - *buffer++ = character; - i /= base; - } while (i > 0); - *buffer = 0; - ReverseStringInPlace(start, buffer); - return buffer; -} - -// Populates the provided buffer with an ASCII representation of the number. -char* FastInt32ToBufferLeft(int32_t i, char* buffer) { - uint32_t u = i; - if (i < 0) { - *buffer++ = '-'; - u = -u; - } - return FastUInt32ToBufferLeft(u, buffer, 10); -} - -// Converts a number to a string and appends it to another. -char* StrCatInt32(char* main, int main_max_length, int32_t number) { - char number_string[kFastToBufferSize]; - FastInt32ToBufferLeft(number, number_string); - return StrCatStr(main, main_max_length, number_string); -} - -// Converts a number to a string and appends it to another. -char* StrCatUInt32(char* main, int main_max_length, uint32_t number, int base) { - char number_string[kFastToBufferSize]; - FastUInt32ToBufferLeft(number, number_string, base); - return StrCatStr(main, main_max_length, number_string); -} - -// Populates the provided buffer with ASCII representation of the float number. -// Avoids the use of any floating point instructions (since these aren't -// supported on many microcontrollers) and as a consequence prints values with -// power-of-two exponents. -char* FastFloatToBufferLeft(float i, char* buffer) { - char* current = buffer; - char* current_end = buffer + (kFastToBufferSize - 1); - // Access the bit fields of the floating point value to avoid requiring any - // float instructions. These constants are derived from IEEE 754. - const uint32_t sign_mask = 0x80000000; - const uint32_t exponent_mask = 0x7f800000; - const int32_t exponent_shift = 23; - const int32_t exponent_bias = 127; - const uint32_t fraction_mask = 0x007fffff; - const uint32_t u = *(uint32_t*)(&i); - const int32_t exponent = - ((u & exponent_mask) >> exponent_shift) - exponent_bias; - const uint32_t fraction = (u & fraction_mask); - // Expect ~0x2B1B9D3 for fraction. - if (u & sign_mask) { - *current = '-'; - current += 1; - } - *current = 0; - // These are special cases for infinities and not-a-numbers. - if (exponent == 128) { - if (fraction == 0) { - current = StrCatStr(current, (current_end - current), "Inf"); - return current; - } else { - current = StrCatStr(current, (current_end - current), "NaN"); - return current; - } - } - // 0x007fffff represents 0.99... for the fraction, so to print the correct - // decimal digits we need to scale our value before passing it to the - // conversion function. This scale should be 10000000/8388608 = 1.1920928955. - // We can approximate this using multipy-adds and right-shifts using the - // values in this array. - const int32_t scale_shifts_size = 13; - const int8_t scale_shifts[13] = {3, 4, 8, 11, 13, 14, 17, - 18, 19, 20, 21, 22, 23}; - uint32_t scaled_fraction = fraction; - for (int i = 0; i < scale_shifts_size; ++i) { - scaled_fraction += (fraction >> scale_shifts[i]); - } - *current = '1'; - current += 1; - *current = '.'; - current += 1; - *current = 0; - current = StrCatUInt32(current, (current_end - current), scaled_fraction, 10); - current = StrCatStr(current, (current_end - current), "*2^"); - current = StrCatInt32(current, (current_end - current), exponent); - return current; -} - -} // namespace - extern "C" void DebugLog(const char* s) { puts(s); } - -extern "C" void DebugLogInt32(int32_t i) { - char number_string[kFastToBufferSize]; - FastInt32ToBufferLeft(i, number_string); - DebugLog(number_string); -} - -extern "C" void DebugLogUInt32(uint32_t i) { - char number_string[kFastToBufferSize]; - FastUInt32ToBufferLeft(i, number_string, 10); - DebugLog(number_string); -} - -extern "C" void DebugLogHex(uint32_t i) { - char number_string[kFastToBufferSize]; - FastUInt32ToBufferLeft(i, number_string, 16); - DebugLog(number_string); -} - -extern "C" void DebugLogFloat(float i) { - char number_string[kFastToBufferSize]; - FastFloatToBufferLeft(i, number_string); - DebugLog(number_string); -} diff --git a/tensorflow/lite/experimental/micro/tools/make/Makefile b/tensorflow/lite/experimental/micro/tools/make/Makefile index f9a2dffae2..fde195118b 100644 --- a/tensorflow/lite/experimental/micro/tools/make/Makefile +++ b/tensorflow/lite/experimental/micro/tools/make/Makefile @@ -1,5 +1,9 @@ + MAKEFILE_DIR := tensorflow/lite/experimental/micro/tools/make +# Pull in some convenience functions. +include $(MAKEFILE_DIR)/helper_functions.inc + # Try to figure out the host system HOST_OS := ifeq ($(OS),Windows_NT) @@ -21,37 +25,10 @@ HOST_ARCH := $(shell if [[ $(shell uname -m) =~ i[345678]86 ]]; then echo x86_32 TARGET := $(HOST_OS) TARGET_ARCH := $(HOST_ARCH) -# Look for platform or target-specific implementation files to replace reference -# implementations with, given a tag. These are expected to occur in subfolders -# of a directory where a reference implementation exists, and have the same -# interface and header file. For example, -# tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.cc -# defines a module for supplying audio data, but since no platform or OS can be -# presumed, it just always returns zeroes for its samples. The MacOS-specific -# tensorflow/lite/experimental/micro/examples/micro_speech/osx/audio_provider.cc -# has an implementation that relies on CoreAudio, and there are equivalent -# versions for other operating systems. -# All lists of source files are put through this substitution process with the -# tags of their target OS and architecture, so that implementations can be added -# by simply placing them in the file tree, with no changes to the build files -# needed. -# One confusing thing about this implementation is that we're using wildcard to -# act as a 'does file exist?' function, rather than expanding an expression. -# Wildcard will return an empty string if given a plain file path with no actual -# wildcards, if the file doesn't exist, so taking the first word of the list -# between that and the reference path will pick the specialized one if it's -# available. -substitute_specialized_implementation = \ - $(firstword $(wildcard $(dir $(1))$(2)/$(notdir $(1))) $(wildcard $(1))) -substitute_specialized_implementations = \ - $(foreach source,$(1),$(call substitute_specialized_implementation,$(source),$(2))) -# Here we're first looking for specialized implementations in ref_dir/$(TARGET) -# and then ref_dir/$(TARGET_ARCH), before falling back to ref_dir's -# implementation. -# The argument to this function should be a list of space-separated file paths, -# with any wildcards already expanded. -specialize = \ - $(call substitute_specialized_implementations,$(call substitute_specialized_implementations,$(1),$(TARGET)),$(TARGET_ARCH)) +# Specify TAGS on the command line to add a particular set of specialized +# implementations, for example TAGS="CMSIS disco_f746ng" to target a Discovery +# STM32F746NG board, using the CMSIS library's implementations where possible. +ALL_TAGS := $(TAGS) $(TARGET) INCLUDES := \ -I. \ @@ -89,6 +66,9 @@ MICROLITE_TEST_SRCS := \ $(wildcard tensorflow/lite/experimental/micro/*test.cc) \ $(wildcard tensorflow/lite/experimental/micro/kernels/*test.cc) +MICROLITE_TEST_HDRS := \ +$(wildcard tensorflow/lite/experimental/micro/testing/*.h) + MICROLITE_CC_BASE_SRCS := \ $(wildcard tensorflow/lite/experimental/micro/*.cc) \ $(wildcard tensorflow/lite/experimental/micro/kernels/*.cc) \ @@ -101,12 +81,58 @@ tensorflow/lite/kernels/internal/quantization_util.cc MICROLITE_CC_SRCS := $(filter-out $(MICROLITE_TEST_SRCS), $(MICROLITE_CC_BASE_SRCS)) MICROLITE_CC_SRCS := $(call specialize,$(MICROLITE_CC_SRCS)) +MICROLITE_CC_HDRS := \ +$(wildcard tensorflow/lite/experimental/micro/*.h) \ +$(wildcard tensorflow/lite/experimental/micro/kernels/*.h) \ +LICENSE \ +tensorflow/lite/c/c_api_internal.h \ +tensorflow/lite/c/builtin_op_data.h \ +tensorflow/lite/core/api/error_reporter.h \ +tensorflow/lite/core/api/flatbuffer_conversions.h \ +tensorflow/lite/core/api/op_resolver.h \ +tensorflow/lite/kernels/kernel_util.h \ +tensorflow/lite/kernels/op_macros.h \ +tensorflow/lite/kernels/padding.h \ +tensorflow/lite/kernels/internal/common.h \ +tensorflow/lite/kernels/internal/compatibility.h \ +tensorflow/lite/kernels/internal/reference/depthwiseconv_float.h \ +tensorflow/lite/kernels/internal/reference/depthwiseconv_uint8.h \ +tensorflow/lite/kernels/internal/reference/fully_connected.h \ +tensorflow/lite/kernels/internal/reference/softmax.h \ +tensorflow/lite/kernels/internal/round.h \ +tensorflow/lite/kernels/internal/tensor_ctypes.h \ +tensorflow/lite/kernels/internal/types.h \ +tensorflow/lite/kernels/internal/quantization_util.h \ +tensorflow/lite/schema/schema_generated.h \ +tensorflow/lite/version.h + +THIRD_PARTY_CC_HDRS := \ +third_party/gemmlowp/fixedpoint/fixedpoint.h \ +third_party/gemmlowp/fixedpoint/fixedpoint_sse.h \ +third_party/gemmlowp/internal/detect_platform.h \ +third_party/gemmlowp/LICENSE \ +third_party/flatbuffers/include/flatbuffers/base.h \ +third_party/flatbuffers/include/flatbuffers/stl_emulation.h \ +third_party/flatbuffers/include/flatbuffers/flatbuffers.h \ +third_party/flatbuffers/LICENSE.txt + +MAKE_PROJECT_FILES := \ + README_MAKE.md \ + Makefile + +MBED_PROJECT_FILES := \ + README_MBED.md \ + mbed-os.lib \ + mbed_app.json + # These target-specific makefiles should modify or replace options like # CXXFLAGS or LIBS to work for a specific targetted architecture. All logic # based on platforms or architectures should happen within these files, to # keep this main makefile focused on the sources and dependencies. include $(wildcard $(MAKEFILE_DIR)/targets/*_makefile.inc) +ALL_TAGS += $(TARGET_ARCH) + ALL_SRCS := \ $(MICROLITE_CC_SRCS) \ $(MICROLITE_TEST_SRCS) @@ -116,6 +142,7 @@ GENDIR := $(MAKEFILE_DIR)/gen/$(TARGET)_$(TARGET_ARCH)/ OBJDIR := $(GENDIR)obj/ BINDIR := $(GENDIR)bin/ LIBDIR := $(GENDIR)lib/ +PRJDIR := $(GENDIR)prj/ MICROLITE_LIB_PATH := $(LIBDIR)$(MICROLITE_LIB_NAME) @@ -142,7 +169,7 @@ $(OBJDIR)%.o: %.c @mkdir -p $(dir $@) $(CC) $(CCFLAGS) $(INCLUDES) -c $< -o $@ - # For normal manually-created TensorFlow ASM source files. +# For normal manually-created TensorFlow ASM source files. $(OBJDIR)%.o: %.S @mkdir -p $(dir $@) $(CC) $(CCFLAGS) $(INCLUDES) -c $< -o $@ @@ -170,13 +197,9 @@ $(BINDIR)%_test : $(OBJDIR)%_test.o $(MICROLITE_LIB_PATH) $(BINDIR)%.test_target: $(BINDIR)%_test $(TEST_SCRIPT) $< '~~~ALL TESTS PASSED~~~' -# snease: Add %.bin rule here since BINDIR is now defined -# These are microcontroller-specific rules for converting the ELF output -# of the linker into a binary image that can be loaded directly. -OBJCOPY := $(TARGET_TOOLCHAIN_PREFIX)objcopy -$(BINDIR)%.bin: $(BINDIR)% - @mkdir -p $(dir $@) - $(OBJCOPY) $< $@ -O binary +# Generate standalone makefile projects for all of the test targets. +$(foreach TEST_TARGET,$(MICROLITE_TEST_SRCS),\ +$(eval $(call microlite_test,$(notdir $(basename $(TEST_TARGET))),$(TEST_TARGET)))) test: test_micro_speech $(MICROLITE_TEST_TARGETS) diff --git a/tensorflow/lite/experimental/micro/tools/make/helper_functions.inc b/tensorflow/lite/experimental/micro/tools/make/helper_functions.inc new file mode 100644 index 0000000000..87c002635f --- /dev/null +++ b/tensorflow/lite/experimental/micro/tools/make/helper_functions.inc @@ -0,0 +1,117 @@ + +# Reverses a space-separated list of words. +reverse = $(if $(1),$(call reverse,$(wordlist 2,$(words $(1)),$(1)))) $(firstword $(1)) + +# Look for platform or target-specific implementation files to replace reference +# implementations with, given a tag. These are expected to occur in subfolders +# of a directory where a reference implementation exists, and have the same +# interface and header file. For example, +# tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.cc +# defines a module for supplying audio data, but since no platform or OS can be +# presumed, it just always returns zeroes for its samples. The MacOS-specific +# tensorflow/lite/experimental/micro/examples/micro_speech/osx/audio_provider.cc +# has an implementation that relies on CoreAudio, and there are equivalent +# versions for other operating systems. +# The specific implementation yielded by the first tag in the list that produces +# a match is returned, else the reference version if none of the tags produce a +# match. +# All lists of source files are put through this substitution process with the +# tags of their target OS and architecture, so that implementations can be added +# by simply placing them in the file tree, with no changes to the build files +# needed. +# One confusing thing about this implementation is that we're using wildcard to +# act as a 'does file exist?' function, rather than expanding an expression. +# Wildcard will return an empty string if given a plain file path with no actual +# wildcards, if the file doesn't exist, so taking the first word of the list +# between that and the reference path will pick the specialized one if it's +# available. +substitute_specialized_implementation = \ + $(firstword $(wildcard $(dir $(1))$(2)/$(notdir $(1))) $(wildcard $(1))) +substitute_specialized_implementations = \ + $(foreach source,$(1),$(call substitute_specialized_implementation,$(source),$(2))) +# Here we're first looking for specialized implementations in ref_dir/$(TAG1) +# and then ref_dir/$(TAG2), etc, before falling back to ref_dir's +# implementation. +# The argument to this function should be a list of space-separated file paths, +# with any wildcards already expanded. +define specialize_on_tags +$(if $(2),$(call substitute_specialized_implementations,$(call specialize_on_tags,$(1),$(wordlist 2,$(words $(2)),$(2))),$(firstword $(2))),$(1)) +endef +# The entry point that most targets should use to find implementation-specific +# versions of their source files. The only argument is a list of file paths. +specialize = $(call specialize_on_tags,$(1),$(strip $(call reverse,$(ALL_TAGS)))) + +# Creates a set of rules to build a standalone makefile project for an +# executable, including all of the source and header files required in a +# separate folder and a simple makefile. +# Arguments are: +# 1 - Project type (make, mbed, etc). +# 2 - Project file template name. +# 3 - Name of executable. +# 4 - List of C/C++ source files needed to build the target. +# 5 - List of C/C++ header files needed to build the target. +# 6 - Linker flags required. +# 7 - C++ compilation flags needed. +# Calling eval on the output will create a _makefile target that you +# can invoke to create the standalone project. +define generate_project +$(PRJDIR)$(3)/$(1)/%: % + @mkdir -p $$(dir $$@) + cp $$< $$@ + +$(PRJDIR)$(3)/$(1)/third_party/%: tensorflow/lite/experimental/micro/tools/make/downloads/% + @mkdir -p $$(dir $$@) + cp $$< $$@ + +$(PRJDIR)$(3)/$(1)/%: tensorflow/lite/experimental/micro/tools/make/templates/%.tpl + @mkdir -p $$(dir $$@) + sed -E 's#\%\{SRCS\}\%#$(4)#g' $$< | \ + sed -E 's#\%\{EXECUTABLE\}\%#$(3)#g' | \ + sed -E 's#\%\{LINKER_FLAGS\}\%#$(6)#g' | \ + sed -E 's#\%\{CXX_FLAGS\}\%#$(7)#g' > $$@ + +generate_$(3)_$(1)_project: $(addprefix $(PRJDIR)$(3)/$(1)/, $(4) $(5) $(2)) +endef + +# Specialized version of generate_project for TF Lite Micro test targets that +# automatically includes standard library files, so you just need to pass the +# test name and any extra source files required. +# Arguments are: +# 1 - Name of test. +# 2 - C/C++ source files implementing the test. +# 3 - C/C++ header files needed for the test. +# Calling eval on the output will create targets that you can invoke to +# generate the standalone project. +define generate_microlite_projects +$(call generate_project,make,$(MAKE_PROJECT_FILES),$(1),$(MICROLITE_CC_SRCS) $(THIRD_PARTY_CC_SRCS) $(2),$(MICROLITE_CC_HDRS) $(THIRD_PARTY_CC_HDRS) $(MICROLITE_TEST_HDRS) $(3),$(MICROLITE_LIBS),$(CXXFLAGS)) +$(call generate_project,mbed,$(MBED_PROJECT_FILES),$(1),$(MICROLITE_CC_SRCS) $(THIRD_PARTY_CC_SRCS) $(2),$(MICROLITE_CC_HDRS) $(THIRD_PARTY_CC_HDRS) $(MICROLITE_TEST_HDRS) $(3),$(MICROLITE_LIBS),$(CXXFLAGS)) +endef + + +# Handles the details of generating a binary target, including specializing +# for the current platform, and generating project file targets. +# Arguments are: +# 1 - Name of test. +# 2 - C/C++ source files implementing the test. +# 3 - C/C++ header files needed for the test. +# Calling eval on the output will create the targets that you need. +define microlite_test +$(1)_LOCAL_SRCS := $(2) +$(1)_LOCAL_SRCS := $$(call specialize,$$($(1)_LOCAL_SRCS)) +ALL_SRCS += $$($(1)_LOCAL_SRCS) +$(1)_LOCAL_HDRS := $(3) +$(1)_LOCAL_OBJS := $$(addprefix $$(OBJDIR), \ +$$(patsubst %.cc,%.o,$$(patsubst %.c,%.o,$$($(1)_LOCAL_SRCS)))) +$(1)_BINARY := $$(BINDIR)$(1) +ALL_BINARIES += $$($(1)_BINARY) +$$($(1)_BINARY): $$($(1)_LOCAL_OBJS) $$(MICROLITE_LIB_PATH) + @mkdir -p $$(dir $$@) + $$(CXX) $$(CXXFLAGS) $$(INCLUDES) \ + -o $$($(1)_BINARY) $$($(1)_LOCAL_OBJS) \ + $$(LIBFLAGS) $$(MICROLITE_LIB_PATH) $$(LDFLAGS) $$(MICROLITE_LIBS) +$(1): $$($(1)_BINARY) +$(1)_bin: $$($(1)_BINARY).bin +test_$(1): $$($(1)_BINARY) + $$(TEST_SCRIPT) $$($(1)_BINARY) '~~~ALL TESTS PASSED~~~' +$(eval $(call generate_microlite_projects,$(1),$(call specialize,$(2)),$(3))) +endef diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/bluepill_makefile.inc b/tensorflow/lite/experimental/micro/tools/make/targets/bluepill_makefile.inc index 5e3105a109..b344f844bc 100644 --- a/tensorflow/lite/experimental/micro/tools/make/targets/bluepill_makefile.inc +++ b/tensorflow/lite/experimental/micro/tools/make/targets/bluepill_makefile.inc @@ -47,7 +47,10 @@ ifeq ($(TARGET), bluepill) MICROLITE_CC_SRCS += \ $(wildcard $(MAKEFILE_DIR)/downloads/stm32_bare_lib/source/*.c) \ $(wildcard $(MAKEFILE_DIR)/downloads/stm32_bare_lib/source/*.cc) - TEST_SCRIPT := tensorflow/lite/experimental/micro/testing/test_bluepill_binary.sh + EXCLUDED_SRCS := \ + $(MAKEFILE_DIR)/downloads/stm32_bare_lib/source/debug_log.c + MICROLITE_CC_SRCS := $(filter-out $(EXCLUDED_SRCS), $(MICROLITE_CC_SRCS)) + TEST_SCRIPT := tensorflow/lite/experimental/micro/testing/test_bluepill_binary.sh # These are tests that don't currently work on the blue pill. EXCLUDED_TESTS := \ tensorflow/lite/experimental/micro/micro_interpreter_test.cc \ diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/mbed_makefile.inc b/tensorflow/lite/experimental/micro/tools/make/targets/mbed_makefile.inc new file mode 100644 index 0000000000..161ff34cdb --- /dev/null +++ b/tensorflow/lite/experimental/micro/tools/make/targets/mbed_makefile.inc @@ -0,0 +1,4 @@ +# Settings for mbed platforms. +ifeq ($(TARGET), mbed) + TARGET_ARCH := cortex-m4 +endif \ No newline at end of file diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/osx_makefile.inc b/tensorflow/lite/experimental/micro/tools/make/targets/osx_makefile.inc index 0e8aad1993..3b91eeff9f 100644 --- a/tensorflow/lite/experimental/micro/tools/make/targets/osx_makefile.inc +++ b/tensorflow/lite/experimental/micro/tools/make/targets/osx_makefile.inc @@ -1,4 +1,4 @@ -#Settings for Mac OS platforms. +# Settings for Mac OS platforms. ifeq ($(TARGET), osx) PLATFORM_FLAGS = \ diff --git a/tensorflow/lite/experimental/micro/tools/make/templates/AUDIO_DISCO_F746NG.lib.tpl b/tensorflow/lite/experimental/micro/tools/make/templates/AUDIO_DISCO_F746NG.lib.tpl new file mode 100644 index 0000000000..11dae1ea16 --- /dev/null +++ b/tensorflow/lite/experimental/micro/tools/make/templates/AUDIO_DISCO_F746NG.lib.tpl @@ -0,0 +1 @@ +https://os.mbed.com/teams/ST/code/AUDIO_DISCO_F746NG/#7046ce26b7ed diff --git a/tensorflow/lite/experimental/micro/tools/make/templates/BSP_DISCO_F746NG.lib.tpl b/tensorflow/lite/experimental/micro/tools/make/templates/BSP_DISCO_F746NG.lib.tpl new file mode 100644 index 0000000000..48dc131707 --- /dev/null +++ b/tensorflow/lite/experimental/micro/tools/make/templates/BSP_DISCO_F746NG.lib.tpl @@ -0,0 +1 @@ +https://os.mbed.com/teams/ST/code/BSP_DISCO_F746NG/#df2ea349c37a diff --git a/tensorflow/lite/experimental/micro/tools/make/templates/Makefile.tpl b/tensorflow/lite/experimental/micro/tools/make/templates/Makefile.tpl new file mode 100644 index 0000000000..74d54f1ebe --- /dev/null +++ b/tensorflow/lite/experimental/micro/tools/make/templates/Makefile.tpl @@ -0,0 +1,26 @@ +SRCS := \ +%{SRCS}% + +OBJS := \ +$(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(SRCS))) + +INCLUDES := \ +-I. \ +-I./third_party/gemmlowp \ +-I./third_party/flatbuffers/include + +CXXFLAGS += %{CXX_FLAGS}% + +LDFLAGS += %{LINKER_FLAGS}% + +%.o: %.cc + $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@ + +%.o: %.c + $(CC) $(CCFLAGS) $(INCLUDES) -c $< -o $@ + +%{EXECUTABLE}% : $(OBJS) + $(CXX) $(LDFLAGS) $(OBJS) \ + -o $@ + +all: %{EXECUTABLE}% diff --git a/tensorflow/lite/experimental/micro/tools/make/templates/README_MAKE.md.tpl b/tensorflow/lite/experimental/micro/tools/make/templates/README_MAKE.md.tpl new file mode 100644 index 0000000000..7906a3226a --- /dev/null +++ b/tensorflow/lite/experimental/micro/tools/make/templates/README_MAKE.md.tpl @@ -0,0 +1,29 @@ +# TensorFlow Lite Micro Make Project + +This folder has been autogenerated by TensorFlow, and contains source, header, +and project files needed to build a single TensorFlow Lite Micro target using +the make tool. + +## Usage + +To build this, run: + +``` +make +``` + +This should attempt to build the target locally on your platform, using the +standard Makefile variables like CFLAGS, CC, CXX, and so on. + +## Project Generation + +See +[tensorflow/lite/experimental/micro](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/experimental/micro) +for details on how projects like this can be generated from the main source +tree. + +## License + +TensorFlow's code is covered by the Apache2 License included in the repository, +and third party dependencies are covered by their respective licenses, in the +third_party folder of this package. diff --git a/tensorflow/lite/experimental/micro/tools/make/templates/README_MBED.md.tpl b/tensorflow/lite/experimental/micro/tools/make/templates/README_MBED.md.tpl new file mode 100644 index 0000000000..2682236edf --- /dev/null +++ b/tensorflow/lite/experimental/micro/tools/make/templates/README_MBED.md.tpl @@ -0,0 +1,48 @@ +# TensorFlow Lite Micro Mbed Project + +This folder has been autogenerated by TensorFlow, and contains source, header, +and project files needed to build a single TensorFlow Lite Micro target using +the Mbed command line interface. + +## Usage + +To load the dependencies this code requires, run: + +``` +mbed config root . +mbed deploy +``` + +TensorFlow requires C++ 11, so you'll need to update your profiles to reflect +this. Here's a short Python command that does that: + +``` +python -c 'import fileinput, glob; +for filename in glob.glob("mbed-os/tools/profiles/*.json"): + for line in fileinput.input(filename, inplace=True): + print line.replace("\"-std=gnu++98\"","\"-std=c++11\", \"-fpermissive\"")' +``` + +With that setting updated, you should now be able to compile: + +``` +mbed compile -m auto -t GCC_ARM +``` + +If this works, it will give you a .bin file that you can flash onto the device +you're targeting. For example, using a Discovery STM3246G board, you can deploy +it by copying the bin to the volume mounted as a USB drive, just by dragging +over the file. + +## Project Generation + +See +[tensorflow/lite/experimental/micro](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/experimental/micro) +for details on how projects like this can be generated from the main source +tree. + +## License + +TensorFlow's code is covered by the Apache2 License included in the repository, +and third party dependencies are covered by their respective licenses, in the +third_party folder of this package. diff --git a/tensorflow/lite/experimental/micro/tools/make/templates/SDRAM_DISCO_F746NG.lib.tpl b/tensorflow/lite/experimental/micro/tools/make/templates/SDRAM_DISCO_F746NG.lib.tpl new file mode 100644 index 0000000000..e2ccd7b81b --- /dev/null +++ b/tensorflow/lite/experimental/micro/tools/make/templates/SDRAM_DISCO_F746NG.lib.tpl @@ -0,0 +1 @@ +https://os.mbed.com/teams/ST/code/SDRAM_DISCO_F746NG/#370f402a2219 diff --git a/tensorflow/lite/experimental/micro/tools/make/templates/mbed-os.lib.tpl b/tensorflow/lite/experimental/micro/tools/make/templates/mbed-os.lib.tpl new file mode 100644 index 0000000000..69fff22f33 --- /dev/null +++ b/tensorflow/lite/experimental/micro/tools/make/templates/mbed-os.lib.tpl @@ -0,0 +1 @@ +https://github.com/ARMmbed/mbed-os/#6a0a86538c0b9b2bfcc4583b1e2b7fea8f4e71e9 diff --git a/tensorflow/lite/experimental/micro/tools/make/templates/mbed_app.json.tpl b/tensorflow/lite/experimental/micro/tools/make/templates/mbed_app.json.tpl new file mode 100644 index 0000000000..1c547369fb --- /dev/null +++ b/tensorflow/lite/experimental/micro/tools/make/templates/mbed_app.json.tpl @@ -0,0 +1,7 @@ +{ + "config": { + "main-stack-size": { + "value": 65536 + } + } +} -- GitLab From 8fefcf7b8b236baf94674ca8fcb0777fd48c2ccd Mon Sep 17 00:00:00 2001 From: Tim Shen Date: Wed, 9 Jan 2019 12:42:37 -0800 Subject: [PATCH 569/622] Roll-forward: PR #24081: Split convolution invocation into preparation and actual invocation Please approve this CL. It will be submitted automatically, and its GitHub pull request will be marked as merged. Imported from GitHub PR #24081 - split DoConvolve into: PrepareForConvolution DoConvolve - split DoConvolveBackwardData into: PrepareForConvolutionBackwardData DoConvolveBackwardData - split DoConvolveBackwardFilter into: PrepareForConvolutionBackwardFilter DoConvolveBackwardFilter PrepareForConvolutionXXX would allocate scratch memory. DoConolveXXX would invoke actual convolution algorithms. Implement forward convoution, backward input convolution, backward filter convolution on CUDA path. Copybara import of the project: - fe9a9dbb8d0ef118b15beb4724f256190fd04d13 Split convolution invocation into preparation and actual ... by Wen-Heng (Jack) Chung - baeda0d8b5da58a97d28ddce264c80c58d937699 Merge fe9a9dbb8d0ef118b15beb4724f256190fd04d13 into bbe33... by Wen-Heng (Jack) Chung PiperOrigin-RevId: 228568812 --- tensorflow/stream_executor/cuda/cuda_dnn.cc | 471 +++++++++++++++----- tensorflow/stream_executor/cuda/cuda_dnn.h | 194 +++++++- tensorflow/stream_executor/dnn.h | 207 +++++++-- tensorflow/stream_executor/stream.cc | 172 +++++-- 4 files changed, 859 insertions(+), 185 deletions(-) diff --git a/tensorflow/stream_executor/cuda/cuda_dnn.cc b/tensorflow/stream_executor/cuda/cuda_dnn.cc index a34aa9354d..0bd953fe3b 100644 --- a/tensorflow/stream_executor/cuda/cuda_dnn.cc +++ b/tensorflow/stream_executor/cuda/cuda_dnn.cc @@ -2799,7 +2799,7 @@ void LogCudaProto(const dnn::ConvolutionProto& conv, float profile_time_ms, } // namespace template -port::Status CudnnSupport::DoConvolveImpl( +port::Status CudnnSupport::PrepareForConvolutionImpl( Stream* stream, const dnn::BatchDescriptor& input_descriptor, const DeviceMemory& input_data, const dnn::FilterDescriptor& filter_descriptor, @@ -2808,6 +2808,34 @@ port::Status CudnnSupport::DoConvolveImpl( const dnn::BatchDescriptor& output_descriptor, DeviceMemory* output_data, dnn::DataType accumulator_type, ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, + dnn::AlgorithmDesc* algorithm_desc, DeviceMemory* scratch_memory) { + cudnnDataType_t cudnn_type = GetCudnnDataType(); + CudnnTensorDescriptor input_nd(input_descriptor, cudnn_type); + CudnnTensorDescriptor output_nd(output_descriptor, cudnn_type); + CudnnFilterDescriptor filter(filter_descriptor, cudnn_type); + CudnnConvolutionDescriptor conv(convolution_descriptor, + ToCudnnDataType(accumulator_type)); + + auto cudnn = cudnn_->GetHandle(parent_, stream); + + SE_ASSIGN_OR_RETURN(*algorithm_desc, + GetCudnnConvolutionForwardAlgorithm( + stream, cudnn, algorithm_config, input_nd, filter, + conv, output_nd, scratch_allocator, scratch_memory)); + + return port::Status::OK(); +} + +template +port::Status CudnnSupport::DoConvolveImpl( + Stream* stream, const dnn::BatchDescriptor& input_descriptor, + const DeviceMemory& input_data, + const dnn::FilterDescriptor& filter_descriptor, + const DeviceMemory& filter_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::BatchDescriptor& output_descriptor, DeviceMemory* output_data, + dnn::DataType accumulator_type, const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, dnn::ProfileResult* output_profile_result) { cudnnDataType_t cudnn_type = GetCudnnDataType(); CudnnTensorDescriptor input_nd(input_descriptor, cudnn_type); @@ -2830,12 +2858,6 @@ port::Status CudnnSupport::DoConvolveImpl( const bool is_profiling = output_profile_result != nullptr; - DeviceMemory scratch; - SE_ASSIGN_OR_RETURN(dnn::AlgorithmDesc algo_desc, - GetCudnnConvolutionForwardAlgorithm( - stream, cudnn, algorithm_config, input_nd, filter, - conv, output_nd, scratch_allocator, &scratch)); - std::unique_ptr timer; if (is_profiling) { timer.reset(new CUDATimer(parent_)); // NOLINT @@ -2851,7 +2873,7 @@ port::Status CudnnSupport::DoConvolveImpl( // memory. See nvbugs/2138754, b/80018418. if (CUDNN_VERSION < 7300) { SE_RETURN_IF_ERROR([&] { - if (algo_desc.algo_id() != CUDNN_CONVOLUTION_FWD_ALGO_FFT_TILING) { + if (algorithm_desc.algo_id() != CUDNN_CONVOLUTION_FWD_ALGO_FFT_TILING) { return port::Status::OK(); } if (input_descriptor.ndims() < 3) { @@ -2876,7 +2898,8 @@ port::Status CudnnSupport::DoConvolveImpl( }()); } - if (algo_desc.algo_id() == CUDNN_CONVOLUTION_FWD_ALGO_WINOGRAD_NONFUSED && + if (algorithm_desc.algo_id() == + CUDNN_CONVOLUTION_FWD_ALGO_WINOGRAD_NONFUSED && !ShouldIncludeWinogradNonfusedAlgo(input_descriptor, output_descriptor)) { return port::Status(port::error::FAILED_PRECONDITION, "This configuration has potential integer overflow in " @@ -2888,24 +2911,25 @@ port::Status CudnnSupport::DoConvolveImpl( /*alpha=*/alpha, /*srcDesc=*/input_nd.handle(), /*srcData=*/input_data.opaque(), /*filterDesc=*/filter.handle(), /*filterData=*/filter_data.opaque(), /*convDesc=*/conv.handle(), - /*algo=*/ToConvForwardAlgo(algo_desc), /*workSpace=*/scratch.opaque(), - /*workSpaceSizeInBytes=*/scratch.size(), /*beta=*/beta, + /*algo=*/ToConvForwardAlgo(algorithm_desc), + /*workSpace=*/scratch_memory->opaque(), + /*workSpaceSizeInBytes=*/scratch_memory->size(), /*beta=*/beta, /*yDesc=*/output_nd.handle(), /*y=*/output_data->opaque())); if (is_profiling) { if (!timer->Stop(AsCUDAStream(stream))) { return port::Status(port::error::INTERNAL, "Failed to stop timer"); } - output_profile_result->set_algorithm(algo_desc); + output_profile_result->set_algorithm(algorithm_desc); output_profile_result->set_elapsed_time_in_ms( timer->GetElapsedMilliseconds()); - output_profile_result->set_scratch_size(scratch.size()); + output_profile_result->set_scratch_size(scratch_memory->size()); LogCudaProto( - GenerateConvProto(dnn::ConvolutionKind::FORWARD, input_descriptor, - filter_descriptor, output_descriptor, algo_desc, - convolution_descriptor, dalpha, dbeta, - accumulator_type, dnn::ActivationMode::kNone), + GenerateConvProto( + dnn::ConvolutionKind::FORWARD, input_descriptor, filter_descriptor, + output_descriptor, algorithm_desc, convolution_descriptor, dalpha, + dbeta, accumulator_type, dnn::ActivationMode::kNone), output_profile_result->elapsed_time_in_ms(), stream->parent()); } @@ -3310,7 +3334,7 @@ port::Status CudnnSupport::DoBatchNormalizationBackwardImpl( return port::Status::OK(); } -bool CudnnSupport::DoConvolve( +bool CudnnSupport::PrepareForConvolution( Stream* stream, const dnn::BatchDescriptor& batch_descriptor, const DeviceMemory& input_data, const dnn::FilterDescriptor& filter_descriptor, @@ -3319,12 +3343,70 @@ bool CudnnSupport::DoConvolve( const dnn::BatchDescriptor& output_descriptor, DeviceMemory* output_data, ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, + dnn::AlgorithmDesc* algorithm_desc, DeviceMemory* scratch_memory) { + return IsStatusOk(PrepareForConvolutionImpl( + stream, batch_descriptor, input_data, filter_descriptor, + filter_data, convolution_descriptor, output_descriptor, + output_data, dnn::DataType::kFloat, scratch_allocator, + algorithm_config, algorithm_desc, scratch_memory), + /*report_error=*/true); +} + +bool CudnnSupport::PrepareForConvolution( + Stream* stream, const dnn::BatchDescriptor& batch_descriptor, + const DeviceMemory& input_data, + const dnn::FilterDescriptor& filter_descriptor, + const DeviceMemory& filter_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::BatchDescriptor& output_descriptor, + DeviceMemory* output_data, ScratchAllocator* scratch_allocator, + const dnn::AlgorithmConfig& algorithm_config, + dnn::AlgorithmDesc* algorithm_desc, DeviceMemory* scratch_memory) { + return IsStatusOk(PrepareForConvolutionImpl( + stream, batch_descriptor, input_data, filter_descriptor, + filter_data, convolution_descriptor, output_descriptor, + output_data, dnn::DataType::kDouble, scratch_allocator, + algorithm_config, algorithm_desc, scratch_memory), + /*report_error=*/true); +} + +bool CudnnSupport::PrepareForConvolution( + Stream* stream, const dnn::BatchDescriptor& batch_descriptor, + const DeviceMemory& input_data, + const dnn::FilterDescriptor& filter_descriptor, + const DeviceMemory& filter_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::BatchDescriptor& output_descriptor, + DeviceMemory* output_data, ScratchAllocator* scratch_allocator, + const dnn::AlgorithmConfig& algorithm_config, + dnn::AlgorithmDesc* algorithm_desc, DeviceMemory* scratch_memory) { + dnn::DataType acc_type = + CudnnEnvVar::IsEnabled() + ? dnn::DataType::kFloat + : dnn::DataType::kHalf; + return IsStatusOk( + PrepareForConvolutionImpl( + stream, batch_descriptor, input_data, filter_descriptor, filter_data, + convolution_descriptor, output_descriptor, output_data, acc_type, + scratch_allocator, algorithm_config, algorithm_desc, scratch_memory), + /*report_error=*/true); +} + +bool CudnnSupport::DoConvolve( + Stream* stream, const dnn::BatchDescriptor& batch_descriptor, + const DeviceMemory& input_data, + const dnn::FilterDescriptor& filter_descriptor, + const DeviceMemory& filter_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::BatchDescriptor& output_descriptor, + DeviceMemory* output_data, const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, dnn::ProfileResult* output_profile_result) { return IsStatusOk( DoConvolveImpl(stream, batch_descriptor, input_data, filter_descriptor, filter_data, convolution_descriptor, output_descriptor, - output_data, dnn::DataType::kFloat, scratch_allocator, - algorithm_config, output_profile_result), + output_data, dnn::DataType::kFloat, algorithm_desc, + scratch_memory, output_profile_result), /*report_error=*/!output_profile_result); } @@ -3335,14 +3417,14 @@ bool CudnnSupport::DoConvolve( const DeviceMemory& filter_data, const dnn::ConvolutionDescriptor& convolution_descriptor, const dnn::BatchDescriptor& output_descriptor, - DeviceMemory* output_data, ScratchAllocator* scratch_allocator, - const dnn::AlgorithmConfig& algorithm_config, + DeviceMemory* output_data, const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, dnn::ProfileResult* output_profile_result) { return IsStatusOk( DoConvolveImpl(stream, batch_descriptor, input_data, filter_descriptor, filter_data, convolution_descriptor, output_descriptor, - output_data, dnn::DataType::kDouble, scratch_allocator, - algorithm_config, output_profile_result), + output_data, dnn::DataType::kDouble, algorithm_desc, + scratch_memory, output_profile_result), /*report_error=*/!output_profile_result); } @@ -3353,8 +3435,9 @@ bool CudnnSupport::DoConvolve( const DeviceMemory& filter_data, const dnn::ConvolutionDescriptor& convolution_descriptor, const dnn::BatchDescriptor& output_descriptor, - DeviceMemory* output_data, ScratchAllocator* scratch_allocator, - const dnn::AlgorithmConfig& algorithm_config, + DeviceMemory* output_data, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, dnn::ProfileResult* output_profile_result) { dnn::DataType acc_type = CudnnEnvVar::IsEnabled() @@ -3363,7 +3446,7 @@ bool CudnnSupport::DoConvolve( return IsStatusOk( DoConvolveImpl(stream, batch_descriptor, input_data, filter_descriptor, filter_data, convolution_descriptor, output_descriptor, - output_data, acc_type, scratch_allocator, algorithm_config, + output_data, acc_type, algorithm_desc, scratch_memory, output_profile_result), /*report_error=*/!output_profile_result); } @@ -3499,7 +3582,7 @@ bool CudnnSupport::DoTransformTensor(Stream* stream, } template -port::Status CudnnSupport::DoConvolveBackwardDataImpl( +port::Status CudnnSupport::PrepareForConvolutionBackwardDataImpl( Stream* stream, const dnn::FilterDescriptor& filter_descriptor, const DeviceMemory& filter_data, const dnn::BatchDescriptor& output_descriptor, @@ -3509,6 +3592,36 @@ port::Status CudnnSupport::DoConvolveBackwardDataImpl( DeviceMemory* backward_input_data, dnn::DataType accumulator_type, ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, + dnn::AlgorithmDesc* algorithm_desc, DeviceMemory* scratch_memory) { + cudnnDataType_t cudnn_type = GetCudnnDataType(); + auto cudnn = cudnn_->GetHandle(parent_, stream); + + CudnnTensorDescriptor out_back_nd(output_descriptor, cudnn_type); + CudnnTensorDescriptor in_back_nd(input_descriptor, cudnn_type); + CudnnFilterDescriptor filter(filter_descriptor, cudnn_type); + CudnnConvolutionDescriptor conv(convolution_descriptor, + ToCudnnDataType(accumulator_type)); + + SE_ASSIGN_OR_RETURN( + *algorithm_desc, + GetCudnnConvolutionBackwardDataAlgorithm( + stream, cudnn, algorithm_config, in_back_nd, filter, conv, + out_back_nd, scratch_allocator, scratch_memory)); + + return port::Status::OK(); +} + +template +port::Status CudnnSupport::DoConvolveBackwardDataImpl( + Stream* stream, const dnn::FilterDescriptor& filter_descriptor, + const DeviceMemory& filter_data, + const dnn::BatchDescriptor& output_descriptor, + DeviceMemory backward_output_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::BatchDescriptor& input_descriptor, + DeviceMemory* backward_input_data, dnn::DataType accumulator_type, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, dnn::ProfileResult* output_profile_result) { cudnnDataType_t cudnn_type = GetCudnnDataType(); // Alpha is the scaling factor for input. @@ -3532,12 +3645,6 @@ port::Status CudnnSupport::DoConvolveBackwardDataImpl( const bool is_profiling = output_profile_result != nullptr; - DeviceMemory scratch; - SE_ASSIGN_OR_RETURN(dnn::AlgorithmDesc algo_desc, - GetCudnnConvolutionBackwardDataAlgorithm( - stream, cudnn, algorithm_config, in_back_nd, filter, - conv, out_back_nd, scratch_allocator, &scratch)); - std::unique_ptr timer; if (is_profiling) { timer.reset(new CUDATimer(parent_)); // NOLINT @@ -3549,7 +3656,8 @@ port::Status CudnnSupport::DoConvolveBackwardDataImpl( } } - if (algo_desc.algo_id() == CUDNN_CONVOLUTION_FWD_ALGO_WINOGRAD_NONFUSED && + if (algorithm_desc.algo_id() == + CUDNN_CONVOLUTION_FWD_ALGO_WINOGRAD_NONFUSED && !ShouldIncludeWinogradNonfusedAlgo(input_descriptor, output_descriptor)) { return port::Status(port::error::FAILED_PRECONDITION, "This configuration has potential integer overflow in " @@ -3559,42 +3667,42 @@ port::Status CudnnSupport::DoConvolveBackwardDataImpl( // Cudnn 7.1.4 has a bug if the workspace of the following convolution is not // zero-initialized, nvbugs/2254619. if (CUDNN_VERSION >= 7000 && CUDNN_VERSION < 7300 && - algo_desc.algo_id() == CUDNN_CONVOLUTION_BWD_DATA_ALGO_1 && - cudnn_type == CUDNN_DATA_HALF && algo_desc.tensor_ops_enabled() && + algorithm_desc.algo_id() == CUDNN_CONVOLUTION_BWD_DATA_ALGO_1 && + cudnn_type == CUDNN_DATA_HALF && algorithm_desc.tensor_ops_enabled() && input_descriptor.layout() == dnn::DataLayout::kBatchYXDepth && filter_descriptor.layout() == dnn::FilterLayout::kOutputInputYX && output_descriptor.layout() == dnn::DataLayout::kBatchDepthYX && (convolution_descriptor.vertical_filter_stride() > 1 || convolution_descriptor.horizontal_filter_stride() > 1)) { - stream->ThenMemZero(&scratch, scratch.size()); + stream->ThenMemZero(scratch_memory, scratch_memory->size()); } - RETURN_IF_CUDNN_ERROR( - cudnnConvolutionBackwardData(cudnn.handle(), - /*alpha=*/alpha, - /*wDesc=*/filter.handle(), - /*w=*/filter_data.opaque(), - /*dyDesc=*/out_back_nd.handle(), - /*dy=*/backward_output_data.opaque(), - /*convDesc=*/conv.handle(), - /*algo=*/ToConvBackwardDataAlgo(algo_desc), - /*workSpace=*/scratch.opaque(), - /*workSpaceSizeInBytes=*/scratch.size(), - /*beta=*/beta, - /*dxDesc=*/in_back_nd.handle(), - /*dx=*/backward_input_data->opaque())); + RETURN_IF_CUDNN_ERROR(cudnnConvolutionBackwardData( + cudnn.handle(), + /*alpha=*/alpha, + /*wDesc=*/filter.handle(), + /*w=*/filter_data.opaque(), + /*dyDesc=*/out_back_nd.handle(), + /*dy=*/backward_output_data.opaque(), + /*convDesc=*/conv.handle(), + /*algo=*/ToConvBackwardDataAlgo(algorithm_desc), + /*workSpace=*/scratch_memory->opaque(), + /*workSpaceSizeInBytes=*/scratch_memory->size(), + /*beta=*/beta, + /*dxDesc=*/in_back_nd.handle(), + /*dx=*/backward_input_data->opaque())); if (is_profiling) { if (!timer->Stop(AsCUDAStream(stream))) { return port::Status(port::error::INTERNAL, "Failed to stop timer"); } - output_profile_result->set_algorithm(algo_desc); + output_profile_result->set_algorithm(algorithm_desc); output_profile_result->set_elapsed_time_in_ms( timer->GetElapsedMilliseconds()); - output_profile_result->set_scratch_size(scratch.size()); + output_profile_result->set_scratch_size(scratch_memory->size()); LogCudaProto(GenerateConvProto( dnn::ConvolutionKind::BACKWARD_DATA, input_descriptor, - filter_descriptor, output_descriptor, algo_desc, + filter_descriptor, output_descriptor, algorithm_desc, convolution_descriptor, dalpha, dbeta, accumulator_type, dnn::ActivationMode::kNone), output_profile_result->elapsed_time_in_ms(), stream->parent()); @@ -3603,7 +3711,7 @@ port::Status CudnnSupport::DoConvolveBackwardDataImpl( return port::Status::OK(); } -bool CudnnSupport::DoConvolveBackwardData( +bool CudnnSupport::PrepareForConvolutionBackwardData( Stream* stream, const dnn::FilterDescriptor& filter_descriptor, const DeviceMemory& filter_data, const dnn::BatchDescriptor& output_descriptor, @@ -3613,17 +3721,17 @@ bool CudnnSupport::DoConvolveBackwardData( DeviceMemory* backward_input_data, ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, - dnn::ProfileResult* output_profile_result) { + dnn::AlgorithmDesc* algorithm_desc, DeviceMemory* scratch_memory) { return IsStatusOk( - DoConvolveBackwardDataImpl( + PrepareForConvolutionBackwardDataImpl( stream, filter_descriptor, filter_data, output_descriptor, backward_output_data, convolution_descriptor, input_descriptor, backward_input_data, dnn::DataType::kDouble, scratch_allocator, - algorithm_config, output_profile_result), - /*report_error=*/!output_profile_result); + algorithm_config, algorithm_desc, scratch_memory), + /*report_error=*/true); } -bool CudnnSupport::DoConvolveBackwardData( +bool CudnnSupport::PrepareForConvolutionBackwardData( Stream* stream, const dnn::FilterDescriptor& filter_descriptor, const DeviceMemory& filter_data, const dnn::BatchDescriptor& output_descriptor, @@ -3633,17 +3741,17 @@ bool CudnnSupport::DoConvolveBackwardData( DeviceMemory* backward_input_data, ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, - dnn::ProfileResult* output_profile_result) { + dnn::AlgorithmDesc* algorithm_desc, DeviceMemory* scratch_memory) { return IsStatusOk( - DoConvolveBackwardDataImpl( + PrepareForConvolutionBackwardDataImpl( stream, filter_descriptor, filter_data, output_descriptor, backward_output_data, convolution_descriptor, input_descriptor, backward_input_data, dnn::DataType::kFloat, scratch_allocator, - algorithm_config, output_profile_result), - /*report_error=*/!output_profile_result); + algorithm_config, algorithm_desc, scratch_memory), + /*report_error=*/true); } -bool CudnnSupport::DoConvolveBackwardData( +bool CudnnSupport::PrepareForConvolutionBackwardData( Stream* stream, const dnn::FilterDescriptor& filter_descriptor, const DeviceMemory& filter_data, const dnn::BatchDescriptor& output_descriptor, @@ -3653,22 +3761,86 @@ bool CudnnSupport::DoConvolveBackwardData( DeviceMemory* backward_input_data, ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, - dnn::ProfileResult* output_profile_result) { + dnn::AlgorithmDesc* algorithm_desc, DeviceMemory* scratch_memory) { dnn::DataType acc_type = CudnnEnvVar::IsEnabled() ? dnn::DataType::kFloat : dnn::DataType::kHalf; return IsStatusOk( - DoConvolveBackwardDataImpl( + PrepareForConvolutionBackwardDataImpl( stream, filter_descriptor, filter_data, output_descriptor, backward_output_data, convolution_descriptor, input_descriptor, backward_input_data, acc_type, scratch_allocator, algorithm_config, - output_profile_result), + algorithm_desc, scratch_memory), + /*report_error=*/true); +} + +bool CudnnSupport::DoConvolveBackwardData( + Stream* stream, const dnn::FilterDescriptor& filter_descriptor, + const DeviceMemory& filter_data, + const dnn::BatchDescriptor& output_descriptor, + DeviceMemory backward_output_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::BatchDescriptor& input_descriptor, + DeviceMemory* backward_input_data, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, + dnn::ProfileResult* output_profile_result) { + return IsStatusOk( + DoConvolveBackwardDataImpl( + stream, filter_descriptor, filter_data, output_descriptor, + backward_output_data, convolution_descriptor, input_descriptor, + backward_input_data, dnn::DataType::kDouble, algorithm_desc, + scratch_memory, output_profile_result), + /*report_error=*/!output_profile_result); +} + +bool CudnnSupport::DoConvolveBackwardData( + Stream* stream, const dnn::FilterDescriptor& filter_descriptor, + const DeviceMemory& filter_data, + const dnn::BatchDescriptor& output_descriptor, + DeviceMemory backward_output_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::BatchDescriptor& input_descriptor, + DeviceMemory* backward_input_data, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, + dnn::ProfileResult* output_profile_result) { + return IsStatusOk( + DoConvolveBackwardDataImpl( + stream, filter_descriptor, filter_data, output_descriptor, + backward_output_data, convolution_descriptor, input_descriptor, + backward_input_data, dnn::DataType::kFloat, algorithm_desc, + scratch_memory, output_profile_result), + /*report_error=*/!output_profile_result); +} + +bool CudnnSupport::DoConvolveBackwardData( + Stream* stream, const dnn::FilterDescriptor& filter_descriptor, + const DeviceMemory& filter_data, + const dnn::BatchDescriptor& output_descriptor, + DeviceMemory backward_output_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::BatchDescriptor& input_descriptor, + DeviceMemory* backward_input_data, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, + dnn::ProfileResult* output_profile_result) { + dnn::DataType acc_type = + CudnnEnvVar::IsEnabled() + ? dnn::DataType::kFloat + : dnn::DataType::kHalf; + return IsStatusOk( + DoConvolveBackwardDataImpl(stream, filter_descriptor, filter_data, + output_descriptor, backward_output_data, + convolution_descriptor, input_descriptor, + backward_input_data, acc_type, algorithm_desc, + scratch_memory, output_profile_result), /*report_error=*/!output_profile_result); } template -port::Status CudnnSupport::DoConvolveBackwardFilterImpl( +port::Status CudnnSupport::PrepareForConvolutionBackwardFilterImpl( Stream* stream, const dnn::BatchDescriptor& input_descriptor, const DeviceMemory& input_data, const dnn::BatchDescriptor& output_descriptor, @@ -3678,6 +3850,36 @@ port::Status CudnnSupport::DoConvolveBackwardFilterImpl( DeviceMemory* backward_filter_data, dnn::DataType accumulator_type, ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, + dnn::AlgorithmDesc* algorithm_desc, DeviceMemory* scratch_memory) { + cudnnDataType_t cudnn_type = GetCudnnDataType(); + auto cudnn = cudnn_->GetHandle(parent_, stream); + + CudnnTensorDescriptor out_back_nd(output_descriptor, cudnn_type); + CudnnTensorDescriptor input_nd(input_descriptor, cudnn_type); + CudnnFilterDescriptor filter(filter_descriptor, cudnn_type); + CudnnConvolutionDescriptor conv(convolution_descriptor, + ToCudnnDataType(accumulator_type)); + + SE_ASSIGN_OR_RETURN( + *algorithm_desc, + GetCudnnConvolutionBackwardFilterAlgorithm( + stream, cudnn, algorithm_config, input_nd, filter, conv, out_back_nd, + scratch_allocator, scratch_memory)); + + return port::Status::OK(); +} + +template +port::Status CudnnSupport::DoConvolveBackwardFilterImpl( + Stream* stream, const dnn::BatchDescriptor& input_descriptor, + const DeviceMemory& input_data, + const dnn::BatchDescriptor& output_descriptor, + DeviceMemory backward_output_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::FilterDescriptor& filter_descriptor, + DeviceMemory* backward_filter_data, dnn::DataType accumulator_type, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, dnn::ProfileResult* output_profile_result) { cudnnDataType_t cudnn_type = GetCudnnDataType(); // Alpha is the scaling factor for input. @@ -3701,12 +3903,6 @@ port::Status CudnnSupport::DoConvolveBackwardFilterImpl( const bool is_profiling = output_profile_result != nullptr; - DeviceMemory scratch; - SE_ASSIGN_OR_RETURN(dnn::AlgorithmDesc algo_desc, - GetCudnnConvolutionBackwardFilterAlgorithm( - stream, cudnn, algorithm_config, input_nd, filter, - conv, out_back_nd, scratch_allocator, &scratch)); - std::unique_ptr timer; if (is_profiling) { timer.reset(new CUDATimer(parent_)); // NOLINT @@ -3722,7 +3918,8 @@ port::Status CudnnSupport::DoConvolveBackwardFilterImpl( // results. See nvbugs/2072856 if (CUDNN_VERSION < 7300) { SE_RETURN_IF_ERROR([&] { - if (algo_desc.algo_id() != CUDNN_CONVOLUTION_BWD_FILTER_ALGO_FFT_TILING) { + if (algorithm_desc.algo_id() != + CUDNN_CONVOLUTION_BWD_FILTER_ALGO_FFT_TILING) { return port::Status::OK(); } if (output_descriptor.height() > 1 && output_descriptor.width() > 1) { @@ -3748,7 +3945,8 @@ port::Status CudnnSupport::DoConvolveBackwardFilterImpl( }()); } - if (algo_desc.algo_id() == CUDNN_CONVOLUTION_FWD_ALGO_WINOGRAD_NONFUSED && + if (algorithm_desc.algo_id() == + CUDNN_CONVOLUTION_FWD_ALGO_WINOGRAD_NONFUSED && !ShouldIncludeWinogradNonfusedAlgo(input_descriptor, output_descriptor)) { return port::Status(port::error::FAILED_PRECONDITION, "This configuration has potential integer overflow in " @@ -3764,7 +3962,7 @@ port::Status CudnnSupport::DoConvolveBackwardFilterImpl( // // See nvbugs/2379553. if (CUDNN_VERSION >= 7100 && CUDNN_VERSION < 7300 && - algo_desc.algo_id() == CUDNN_CONVOLUTION_BWD_FILTER_ALGO_1 && + algorithm_desc.algo_id() == CUDNN_CONVOLUTION_BWD_FILTER_ALGO_1 && cudnn_type == CUDNN_DATA_HALF && input_descriptor.layout() == dnn::DataLayout::kBatchYXDepth && filter_descriptor.layout() == dnn::FilterLayout::kOutputYXInput && @@ -3782,9 +3980,9 @@ port::Status CudnnSupport::DoConvolveBackwardFilterImpl( /*diffDesc=*/out_back_nd.handle(), /*diffData=*/backward_output_data.opaque(), /*convDesc=*/conv.handle(), - /*algo=*/ToConvBackwardFilterAlgo(algo_desc), - /*workSpace=*/scratch.opaque(), - /*workSpaceSizeInBytes=*/scratch.size(), + /*algo=*/ToConvBackwardFilterAlgo(algorithm_desc), + /*workSpace=*/scratch_memory->opaque(), + /*workSpaceSizeInBytes=*/scratch_memory->size(), /*beta=*/beta, /*gradDesc=*/filter.handle(), /*dw=*/backward_filter_data->opaque())); @@ -3792,14 +3990,14 @@ port::Status CudnnSupport::DoConvolveBackwardFilterImpl( if (!timer->Stop(AsCUDAStream(stream))) { return port::Status(port::error::INTERNAL, "Failed to stop timer"); } - output_profile_result->set_algorithm(algo_desc); + output_profile_result->set_algorithm(algorithm_desc); output_profile_result->set_elapsed_time_in_ms( timer->GetElapsedMilliseconds()); - output_profile_result->set_scratch_size(scratch.size()); + output_profile_result->set_scratch_size(scratch_memory->size()); LogCudaProto(GenerateConvProto( dnn::ConvolutionKind::BACKWARD_FILTER, input_descriptor, - filter_descriptor, output_descriptor, algo_desc, + filter_descriptor, output_descriptor, algorithm_desc, convolution_descriptor, dalpha, dbeta, accumulator_type, dnn::ActivationMode::kNone), output_profile_result->elapsed_time_in_ms(), stream->parent()); @@ -3808,7 +4006,7 @@ port::Status CudnnSupport::DoConvolveBackwardFilterImpl( return port::Status::OK(); } -bool CudnnSupport::DoConvolveBackwardFilter( +bool CudnnSupport::PrepareForConvolutionBackwardFilter( Stream* stream, const dnn::BatchDescriptor& input_descriptor, const DeviceMemory& input_data, const dnn::BatchDescriptor& output_descriptor, @@ -3818,18 +4016,17 @@ bool CudnnSupport::DoConvolveBackwardFilter( DeviceMemory* backward_filter_data, ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, - dnn::ProfileResult* output_profile_result) { + dnn::AlgorithmDesc* algorithm_desc, DeviceMemory* scratch_memory) { return IsStatusOk( - DoConvolveBackwardFilterImpl( + PrepareForConvolutionBackwardFilterImpl( stream, input_descriptor, input_data, output_descriptor, backward_output_data, convolution_descriptor, filter_descriptor, - backward_filter_data, dnn::DataType::kDouble, - - scratch_allocator, algorithm_config, output_profile_result), - /*report_error=*/!output_profile_result); + backward_filter_data, dnn::DataType::kDouble, scratch_allocator, + algorithm_config, algorithm_desc, scratch_memory), + /*report_error=*/true); } -bool CudnnSupport::DoConvolveBackwardFilter( +bool CudnnSupport::PrepareForConvolutionBackwardFilter( Stream* stream, const dnn::BatchDescriptor& input_descriptor, const DeviceMemory& input_data, const dnn::BatchDescriptor& output_descriptor, @@ -3839,18 +4036,17 @@ bool CudnnSupport::DoConvolveBackwardFilter( DeviceMemory* backward_filter_data, ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, - dnn::ProfileResult* output_profile_result) { - return IsStatusOk(DoConvolveBackwardFilterImpl( - stream, input_descriptor, input_data, output_descriptor, - backward_output_data, convolution_descriptor, - filter_descriptor, backward_filter_data, - - dnn::DataType::kFloat, scratch_allocator, - algorithm_config, output_profile_result), - /*report_error=*/!output_profile_result); + dnn::AlgorithmDesc* algorithm_desc, DeviceMemory* scratch_memory) { + return IsStatusOk( + PrepareForConvolutionBackwardFilterImpl( + stream, input_descriptor, input_data, output_descriptor, + backward_output_data, convolution_descriptor, filter_descriptor, + backward_filter_data, dnn::DataType::kFloat, scratch_allocator, + algorithm_config, algorithm_desc, scratch_memory), + /*report_error=*/true); } -bool CudnnSupport::DoConvolveBackwardFilter( +bool CudnnSupport::PrepareForConvolutionBackwardFilter( Stream* stream, const dnn::BatchDescriptor& input_descriptor, const DeviceMemory& input_data, const dnn::BatchDescriptor& output_descriptor, @@ -3860,20 +4056,83 @@ bool CudnnSupport::DoConvolveBackwardFilter( DeviceMemory* backward_filter_data, ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, - dnn::ProfileResult* output_profile_result) { + dnn::AlgorithmDesc* algorithm_desc, DeviceMemory* scratch_memory) { dnn::DataType acc_type = CudnnEnvVar::IsEnabled() ? dnn::DataType::kFloat : dnn::DataType::kHalf; return IsStatusOk( - DoConvolveBackwardFilterImpl( + PrepareForConvolutionBackwardFilterImpl( stream, input_descriptor, input_data, output_descriptor, backward_output_data, convolution_descriptor, filter_descriptor, backward_filter_data, acc_type, scratch_allocator, algorithm_config, - output_profile_result), + algorithm_desc, scratch_memory), + /*report_error=*/true); +} + +bool CudnnSupport::DoConvolveBackwardFilter( + Stream* stream, const dnn::BatchDescriptor& input_descriptor, + const DeviceMemory& input_data, + const dnn::BatchDescriptor& output_descriptor, + DeviceMemory backward_output_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::FilterDescriptor& filter_descriptor, + DeviceMemory* backward_filter_data, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, + dnn::ProfileResult* output_profile_result) { + return IsStatusOk( + DoConvolveBackwardFilterImpl( + stream, input_descriptor, input_data, output_descriptor, + backward_output_data, convolution_descriptor, filter_descriptor, + backward_filter_data, dnn::DataType::kDouble, algorithm_desc, + scratch_memory, output_profile_result), + /*report_error=*/!output_profile_result); +} + +bool CudnnSupport::DoConvolveBackwardFilter( + Stream* stream, const dnn::BatchDescriptor& input_descriptor, + const DeviceMemory& input_data, + const dnn::BatchDescriptor& output_descriptor, + DeviceMemory backward_output_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::FilterDescriptor& filter_descriptor, + DeviceMemory* backward_filter_data, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, + dnn::ProfileResult* output_profile_result) { + return IsStatusOk( + DoConvolveBackwardFilterImpl( + stream, input_descriptor, input_data, output_descriptor, + backward_output_data, convolution_descriptor, filter_descriptor, + backward_filter_data, dnn::DataType::kFloat, algorithm_desc, + scratch_memory, output_profile_result), /*report_error=*/!output_profile_result); } +bool CudnnSupport::DoConvolveBackwardFilter( + Stream* stream, const dnn::BatchDescriptor& input_descriptor, + const DeviceMemory& input_data, + const dnn::BatchDescriptor& output_descriptor, + DeviceMemory backward_output_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::FilterDescriptor& filter_descriptor, + DeviceMemory* backward_filter_data, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, + dnn::ProfileResult* output_profile_result) { + dnn::DataType acc_type = + CudnnEnvVar::IsEnabled() + ? dnn::DataType::kFloat + : dnn::DataType::kHalf; + return IsStatusOk(DoConvolveBackwardFilterImpl( + stream, input_descriptor, input_data, output_descriptor, + backward_output_data, convolution_descriptor, + filter_descriptor, backward_filter_data, acc_type, + algorithm_desc, scratch_memory, output_profile_result), + /*report_error=*/!output_profile_result); +} + template port::Status CudnnSupport::DoConvolveBackwardBiasImpl( Stream* stream, const dnn::BatchDescriptor& input_descriptor, diff --git a/tensorflow/stream_executor/cuda/cuda_dnn.h b/tensorflow/stream_executor/cuda/cuda_dnn.h index 4cce3c5626..d7514981d5 100644 --- a/tensorflow/stream_executor/cuda/cuda_dnn.h +++ b/tensorflow/stream_executor/cuda/cuda_dnn.h @@ -258,6 +258,43 @@ class CudnnSupport : public dnn::DnnSupport { DeviceMemory* scale_backprop, DeviceMemory* offset_backprop) override; + bool PrepareForConvolution( + Stream* stream, const dnn::BatchDescriptor& batch_descriptor, + const DeviceMemory& input_data, + const dnn::FilterDescriptor& filter_descriptor, + const DeviceMemory& filter_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::BatchDescriptor& output_descriptor, + DeviceMemory* output_data, ScratchAllocator* scratch_allocator, + const dnn::AlgorithmConfig& algorithm_config, + dnn::AlgorithmDesc* algorithm_desc, + DeviceMemory* scratch_memory) override; + + bool PrepareForConvolution( + Stream* stream, const dnn::BatchDescriptor& batch_descriptor, + const DeviceMemory& input_data, + const dnn::FilterDescriptor& filter_descriptor, + const DeviceMemory& filter_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::BatchDescriptor& output_descriptor, + DeviceMemory* output_data, ScratchAllocator* scratch_allocator, + const dnn::AlgorithmConfig& algorithm_config, + dnn::AlgorithmDesc* algorithm_desc, + DeviceMemory* scratch_memory) override; + + bool PrepareForConvolution( + Stream* stream, const dnn::BatchDescriptor& batch_descriptor, + const DeviceMemory& input_data, + const dnn::FilterDescriptor& filter_descriptor, + const DeviceMemory& filter_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::BatchDescriptor& output_descriptor, + DeviceMemory* output_data, + ScratchAllocator* scratch_allocator, + const dnn::AlgorithmConfig& algorithm_config, + dnn::AlgorithmDesc* algorithm_desc, + DeviceMemory* scratch_memory) override; + bool DoConvolve(Stream* stream, const dnn::BatchDescriptor& batch_descriptor, const DeviceMemory& input_data, const dnn::FilterDescriptor& filter_descriptor, @@ -265,8 +302,8 @@ class CudnnSupport : public dnn::DnnSupport { const dnn::ConvolutionDescriptor& convolution_descriptor, const dnn::BatchDescriptor& output_descriptor, DeviceMemory* output_data, - ScratchAllocator* scratch_allocator, - const dnn::AlgorithmConfig& algorithm_config, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, dnn::ProfileResult* output_profile_result) override; bool DoConvolve(Stream* stream, const dnn::BatchDescriptor& batch_descriptor, @@ -276,8 +313,8 @@ class CudnnSupport : public dnn::DnnSupport { const dnn::ConvolutionDescriptor& convolution_descriptor, const dnn::BatchDescriptor& output_descriptor, DeviceMemory* output_data, - ScratchAllocator* scratch_allocator, - const dnn::AlgorithmConfig& algorithm_config, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, dnn::ProfileResult* output_profile_result) override; bool DoConvolve(Stream* stream, const dnn::BatchDescriptor& batch_descriptor, @@ -287,8 +324,8 @@ class CudnnSupport : public dnn::DnnSupport { const dnn::ConvolutionDescriptor& convolution_descriptor, const dnn::BatchDescriptor& output_descriptor, DeviceMemory* output_data, - ScratchAllocator* scratch_allocator, - const dnn::AlgorithmConfig& algorithm_config, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, dnn::ProfileResult* output_profile_result) override; bool DoFusedConvolve( @@ -390,7 +427,20 @@ class CudnnSupport : public dnn::DnnSupport { return false; } - bool DoConvolveBackwardData( + bool PrepareForConvolutionBackwardData( + Stream* stream, const dnn::FilterDescriptor& filter_descriptor, + const DeviceMemory& filter_data, + const dnn::BatchDescriptor& output_descriptor, + DeviceMemory backward_output_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::BatchDescriptor& input_descriptor, + DeviceMemory* backward_input_data, + ScratchAllocator* scratch_allocator, + const dnn::AlgorithmConfig& algorithm_config, + dnn::AlgorithmDesc* algorithm_desc, + DeviceMemory* scratch_memory) override; + + bool PrepareForConvolutionBackwardData( Stream* stream, const dnn::FilterDescriptor& filter_descriptor, const DeviceMemory& filter_data, const dnn::BatchDescriptor& output_descriptor, @@ -400,6 +450,32 @@ class CudnnSupport : public dnn::DnnSupport { DeviceMemory* backward_input_data, ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, + dnn::AlgorithmDesc* algorithm_desc, + DeviceMemory* scratch_memory) override; + + bool PrepareForConvolutionBackwardData( + Stream* stream, const dnn::FilterDescriptor& filter_descriptor, + const DeviceMemory& filter_data, + const dnn::BatchDescriptor& output_descriptor, + DeviceMemory backward_output_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::BatchDescriptor& input_descriptor, + DeviceMemory* backward_input_data, + ScratchAllocator* scratch_allocator, + const dnn::AlgorithmConfig& algorithm_config, + dnn::AlgorithmDesc* algorithm_desc, + DeviceMemory* scratch_memory) override; + + bool DoConvolveBackwardData( + Stream* stream, const dnn::FilterDescriptor& filter_descriptor, + const DeviceMemory& filter_data, + const dnn::BatchDescriptor& output_descriptor, + DeviceMemory backward_output_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::BatchDescriptor& input_descriptor, + DeviceMemory* backward_input_data, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, dnn::ProfileResult* output_profile_result) override; bool DoConvolveBackwardData( @@ -410,8 +486,8 @@ class CudnnSupport : public dnn::DnnSupport { const dnn::ConvolutionDescriptor& convolution_descriptor, const dnn::BatchDescriptor& input_descriptor, DeviceMemory* backward_input_data, - ScratchAllocator* scratch_allocator, - const dnn::AlgorithmConfig& algorithm_config, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, dnn::ProfileResult* output_profile_result) override; bool DoConvolveBackwardData( @@ -422,11 +498,11 @@ class CudnnSupport : public dnn::DnnSupport { const dnn::ConvolutionDescriptor& convolution_descriptor, const dnn::BatchDescriptor& input_descriptor, DeviceMemory* backward_input_data, - ScratchAllocator* scratch_allocator, - const dnn::AlgorithmConfig& algorithm_config, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, dnn::ProfileResult* output_profile_result) override; - bool DoConvolveBackwardFilter( + bool PrepareForConvolutionBackwardFilter( Stream* stream, const dnn::BatchDescriptor& input_descriptor, const DeviceMemory& input_data, const dnn::BatchDescriptor& output_descriptor, @@ -436,9 +512,10 @@ class CudnnSupport : public dnn::DnnSupport { DeviceMemory* backward_filter_data, ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, - dnn::ProfileResult* output_profile_result) override; + dnn::AlgorithmDesc* algorithm_desc, + DeviceMemory* scratch_memory) override; - bool DoConvolveBackwardFilter( + bool PrepareForConvolutionBackwardFilter( Stream* stream, const dnn::BatchDescriptor& input_descriptor, const DeviceMemory& input_data, const dnn::BatchDescriptor& output_descriptor, @@ -448,9 +525,10 @@ class CudnnSupport : public dnn::DnnSupport { DeviceMemory* backward_filter_data, ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, - dnn::ProfileResult* output_profile_result) override; + dnn::AlgorithmDesc* algorithm_desc, + DeviceMemory* scratch_memory) override; - bool DoConvolveBackwardFilter( + bool PrepareForConvolutionBackwardFilter( Stream* stream, const dnn::BatchDescriptor& input_descriptor, const DeviceMemory& input_data, const dnn::BatchDescriptor& output_descriptor, @@ -460,6 +538,43 @@ class CudnnSupport : public dnn::DnnSupport { DeviceMemory* backward_filter_data, ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, + dnn::AlgorithmDesc* algorithm_desc, + DeviceMemory* scratch_memory) override; + + bool DoConvolveBackwardFilter( + Stream* stream, const dnn::BatchDescriptor& input_descriptor, + const DeviceMemory& input_data, + const dnn::BatchDescriptor& output_descriptor, + DeviceMemory backward_output_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::FilterDescriptor& filter_descriptor, + DeviceMemory* backward_filter_data, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, + dnn::ProfileResult* output_profile_result) override; + + bool DoConvolveBackwardFilter( + Stream* stream, const dnn::BatchDescriptor& input_descriptor, + const DeviceMemory& input_data, + const dnn::BatchDescriptor& output_descriptor, + DeviceMemory backward_output_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::FilterDescriptor& filter_descriptor, + DeviceMemory* backward_filter_data, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, + dnn::ProfileResult* output_profile_result) override; + + bool DoConvolveBackwardFilter( + Stream* stream, const dnn::BatchDescriptor& input_descriptor, + const DeviceMemory& input_data, + const dnn::BatchDescriptor& output_descriptor, + DeviceMemory backward_output_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::FilterDescriptor& filter_descriptor, + DeviceMemory* backward_filter_data, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, dnn::ProfileResult* output_profile_result) override; bool DoConvolveBackwardBias( @@ -677,7 +792,7 @@ class CudnnSupport : public dnn::DnnSupport { DeviceMemory* offset_backprop); template - port::Status DoConvolveImpl( + port::Status PrepareForConvolutionImpl( Stream* stream, const dnn::BatchDescriptor& input_descriptor, const DeviceMemory& input_data, const dnn::FilterDescriptor& filter_descriptor, @@ -687,6 +802,19 @@ class CudnnSupport : public dnn::DnnSupport { DeviceMemory* output_data, dnn::DataType accumulator_type, ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, + dnn::AlgorithmDesc* algorithm_desc, DeviceMemory* scratch_memory); + + template + port::Status DoConvolveImpl( + Stream* stream, const dnn::BatchDescriptor& input_descriptor, + const DeviceMemory& input_data, + const dnn::FilterDescriptor& filter_descriptor, + const DeviceMemory& filter_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::BatchDescriptor& output_descriptor, + DeviceMemory* output_data, dnn::DataType accumulator_type, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, dnn::ProfileResult* output_profile_result); template @@ -707,7 +835,7 @@ class CudnnSupport : public dnn::DnnSupport { dnn::ProfileResult* output_profile_result); template - port::Status DoConvolveBackwardDataImpl( + port::Status PrepareForConvolutionBackwardDataImpl( Stream* stream, const dnn::FilterDescriptor& filter_descriptor, const DeviceMemory& filter_data, const dnn::BatchDescriptor& output_descriptor, @@ -717,19 +845,45 @@ class CudnnSupport : public dnn::DnnSupport { DeviceMemory* backward_input_data, dnn::DataType accumulator_type, ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, + dnn::AlgorithmDesc* algorithm_desc, DeviceMemory* scratch_memory); + + template + port::Status DoConvolveBackwardDataImpl( + Stream* stream, const dnn::FilterDescriptor& filter_descriptor, + const DeviceMemory& filter_data, + const dnn::BatchDescriptor& output_descriptor, + DeviceMemory backward_output_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::BatchDescriptor& input_descriptor, + DeviceMemory* backward_input_data, dnn::DataType accumulator_type, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, dnn::ProfileResult* output_profile_result); template - port::Status DoConvolveBackwardFilterImpl( + port::Status PrepareForConvolutionBackwardFilterImpl( Stream* stream, const dnn::BatchDescriptor& input_descriptor, const DeviceMemory& input_data, - const dnn::BatchDescriptor& output_descriptor, + const dnn::BatchDescriptor& output_descriptor_in, DeviceMemory backward_output_data, const dnn::ConvolutionDescriptor& convolution_descriptor, const dnn::FilterDescriptor& filter_descriptor, DeviceMemory* backward_filter_data, dnn::DataType accumulator_type, ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, + dnn::AlgorithmDesc* algorithm_desc, DeviceMemory* scratch_memory); + + template + port::Status DoConvolveBackwardFilterImpl( + Stream* stream, const dnn::BatchDescriptor& input_descriptor, + const DeviceMemory& input_data, + const dnn::BatchDescriptor& output_descriptor, + DeviceMemory backward_output_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::FilterDescriptor& filter_descriptor, + DeviceMemory* backward_filter_data, dnn::DataType accumulator_type, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, dnn::ProfileResult* output_profile_result); template diff --git a/tensorflow/stream_executor/dnn.h b/tensorflow/stream_executor/dnn.h index ff7b02cf6b..f5a77d6525 100644 --- a/tensorflow/stream_executor/dnn.h +++ b/tensorflow/stream_executor/dnn.h @@ -738,6 +738,7 @@ class PoolingDescriptor { class AlgorithmDesc { public: typedef int64 Index; + AlgorithmDesc() : AlgorithmDesc(0, false) {} AlgorithmDesc(Index a, bool use_tensor_ops) { proto_.set_algo_id(a); proto_.set_math_type(use_tensor_ops ? AlgorithmProto::TENSOR_OP_MATH @@ -1186,6 +1187,52 @@ class DnnSupport { return false; } + virtual bool PrepareForConvolution( + Stream* stream, const BatchDescriptor& batch_descriptor, + const DeviceMemory& input_data, + const FilterDescriptor& filter_descriptor, + const DeviceMemory& filter_data, + const ConvolutionDescriptor& convolution_descriptor, + const BatchDescriptor& output_descriptor, + DeviceMemory* output_data, ScratchAllocator* scratch_allocator, + const dnn::AlgorithmConfig& algorithm_config, + dnn::AlgorithmDesc* algorithm_desc, DeviceMemory* scratch_memory) { + *algorithm_desc = {}; + *scratch_memory = {}; + return true; + } + + virtual bool PrepareForConvolution( + Stream* stream, const BatchDescriptor& batch_descriptor, + const DeviceMemory& input_data, + const FilterDescriptor& filter_descriptor, + const DeviceMemory& filter_data, + const ConvolutionDescriptor& convolution_descriptor, + const BatchDescriptor& output_descriptor, + DeviceMemory* output_data, ScratchAllocator* scratch_allocator, + const dnn::AlgorithmConfig& algorithm_config, + dnn::AlgorithmDesc* algorithm_desc, DeviceMemory* scratch_memory) { + *algorithm_desc = {}; + *scratch_memory = {}; + return true; + } + + virtual bool PrepareForConvolution( + Stream* stream, const BatchDescriptor& batch_descriptor, + const DeviceMemory& input_data, + const FilterDescriptor& filter_descriptor, + const DeviceMemory& filter_data, + const ConvolutionDescriptor& convolution_descriptor, + const BatchDescriptor& output_descriptor, + DeviceMemory* output_data, + ScratchAllocator* scratch_allocator, + const dnn::AlgorithmConfig& algorithm_config, + dnn::AlgorithmDesc* algorithm_desc, DeviceMemory* scratch_memory) { + *algorithm_desc = {}; + *scratch_memory = {}; + return true; + } + // Enqueues a single-precision convolution operation onto the stream. // // Arguments (all borrowed): @@ -1199,10 +1246,10 @@ class DnnSupport { // output_descriptor: dimensions of the output layer. // output_data: un-owned device memory region in which to place the // convolution result. - // scratch_allocator: un-owned, may-be-null object that may allocate scratch - // space in order to speed up the convolution operation. - // algorithm_config: specifies which algorithm should be used for the + // algorithm_desc: specifies which algorithm should be used for the // operation. + // scratch: un-owned device memory for scratch space in order to speed up + // the convolution operation. // output_profile_result: the output profile result for this call. The // profiling is only enabled when this is not nullptr. // @@ -1227,8 +1274,9 @@ class DnnSupport { const DeviceMemory& filter_data, const dnn::ConvolutionDescriptor& convolution_descriptor, const dnn::BatchDescriptor& output_descriptor, - DeviceMemory* output_data, ScratchAllocator* scratch_allocator, - const dnn::AlgorithmConfig& algorithm_config, + DeviceMemory* output_data, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, ProfileResult* output_profile_result) = 0; // Enqueues a double-precision convolution operation onto the stream. @@ -1240,8 +1288,9 @@ class DnnSupport { const DeviceMemory& filter_data, const dnn::ConvolutionDescriptor& convolution_descriptor, const dnn::BatchDescriptor& output_descriptor, - DeviceMemory* output_data, ScratchAllocator* scratch_allocator, - const dnn::AlgorithmConfig& algorithm_config, + DeviceMemory* output_data, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, dnn::ProfileResult* output_profile_result) = 0; // Enqueues a half-precision convolution operation onto the stream. @@ -1254,8 +1303,8 @@ class DnnSupport { const dnn::ConvolutionDescriptor& convolution_descriptor, const dnn::BatchDescriptor& output_descriptor, DeviceMemory* output_data, - ScratchAllocator* scratch_allocator, - const dnn::AlgorithmConfig& algorithm_config, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, ProfileResult* output_profile_result) = 0; // Return a list of algorithms supported by the forward convolution pass. @@ -1311,6 +1360,54 @@ class DnnSupport { const BatchDescriptor& output_descriptor, DeviceMemory* output_data) = 0; + virtual bool PrepareForConvolutionBackwardData( + Stream* stream, const dnn::FilterDescriptor& filter_descriptor, + const DeviceMemory& filter_data, + const dnn::BatchDescriptor& output_descriptor, + DeviceMemory backward_output_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::BatchDescriptor& input_descriptor, + DeviceMemory* backward_input_data, + ScratchAllocator* scratch_allocator, + const dnn::AlgorithmConfig& algorithm_config, + dnn::AlgorithmDesc* algorithm_desc, DeviceMemory* scratch_memory) { + *algorithm_desc = {}; + *scratch_memory = {}; + return true; + } + + virtual bool PrepareForConvolutionBackwardData( + Stream* stream, const dnn::FilterDescriptor& filter_descriptor, + const DeviceMemory& filter_data, + const dnn::BatchDescriptor& output_descriptor, + DeviceMemory backward_output_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::BatchDescriptor& input_descriptor, + DeviceMemory* backward_input_data, + ScratchAllocator* scratch_allocator, + const dnn::AlgorithmConfig& algorithm_config, + dnn::AlgorithmDesc* algorithm_desc, DeviceMemory* scratch_memory) { + *algorithm_desc = {}; + *scratch_memory = {}; + return true; + } + + virtual bool PrepareForConvolutionBackwardData( + Stream* stream, const dnn::FilterDescriptor& filter_descriptor, + const DeviceMemory& filter_data, + const dnn::BatchDescriptor& output_descriptor, + DeviceMemory backward_output_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::BatchDescriptor& input_descriptor, + DeviceMemory* backward_input_data, + ScratchAllocator* scratch_allocator, + const dnn::AlgorithmConfig& algorithm_config, + dnn::AlgorithmDesc* algorithm_desc, DeviceMemory* scratch_memory) { + *algorithm_desc = {}; + *scratch_memory = {}; + return true; + } + // Enqueues a single-precision backward convolution (for data) operation onto // the stream. // @@ -1330,15 +1427,15 @@ class DnnSupport { // scratch_allocator: un-owned, may-be-null object that may allocate scratch // space in order to speed up the convolution operation. virtual bool DoConvolveBackwardData( - Stream* stream, const FilterDescriptor& filter_descriptor, + Stream* stream, const dnn::FilterDescriptor& filter_descriptor, const DeviceMemory& filter_data, - const BatchDescriptor& output_descriptor, + const dnn::BatchDescriptor& output_descriptor, DeviceMemory backward_output_data, - const ConvolutionDescriptor& convolution_descriptor, - const BatchDescriptor& input_descriptor, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::BatchDescriptor& input_descriptor, DeviceMemory* backward_input_data, - ScratchAllocator* scratch_allocator, - const dnn::AlgorithmConfig& algorithm_config, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, ProfileResult* output_profile_result) = 0; // Return a list of algorithms supported by the backward convolution pass for @@ -1348,28 +1445,76 @@ class DnnSupport { std::vector* out_algorithms); virtual bool DoConvolveBackwardData( - Stream* stream, const FilterDescriptor& filter_descriptor, + Stream* stream, const dnn::FilterDescriptor& filter_descriptor, const DeviceMemory& filter_data, - const BatchDescriptor& output_descriptor, + const dnn::BatchDescriptor& output_descriptor, DeviceMemory backward_output_data, - const ConvolutionDescriptor& convolution_descriptor, - const BatchDescriptor& input_descriptor, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::BatchDescriptor& input_descriptor, DeviceMemory* backward_input_data, - ScratchAllocator* scratch_allocator, - const dnn::AlgorithmConfig& algorithm_config, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, ProfileResult* output_profile_result) = 0; virtual bool DoConvolveBackwardData( - Stream* stream, const FilterDescriptor& filter_descriptor, + Stream* stream, const dnn::FilterDescriptor& filter_descriptor, const DeviceMemory& filter_data, + const dnn::BatchDescriptor& output_descriptor, + DeviceMemory backward_output_data, + const dnn::ConvolutionDescriptor& convolution_descriptor, + const dnn::BatchDescriptor& input_descriptor, + DeviceMemory* backward_input_data, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, + ProfileResult* output_profile_result) = 0; + + virtual bool PrepareForConvolutionBackwardFilter( + Stream* stream, const BatchDescriptor& input_descriptor, + const DeviceMemory& input_data, + const BatchDescriptor& output_descriptor, + DeviceMemory backward_output_data, + const ConvolutionDescriptor& convolution_descriptor, + const FilterDescriptor& filter_descriptor, + DeviceMemory* backward_filter_data, + ScratchAllocator* scratch_allocator, + const dnn::AlgorithmConfig& algorithm_config, + dnn::AlgorithmDesc* algorithm_desc, DeviceMemory* scratch_memory) { + *algorithm_desc = {}; + *scratch_memory = {}; + return true; + } + + virtual bool PrepareForConvolutionBackwardFilter( + Stream* stream, const BatchDescriptor& input_descriptor, + const DeviceMemory& input_data, + const BatchDescriptor& output_descriptor, + DeviceMemory backward_output_data, + const ConvolutionDescriptor& convolution_descriptor, + const FilterDescriptor& filter_descriptor, + DeviceMemory* backward_filter_data, + ScratchAllocator* scratch_allocator, + const dnn::AlgorithmConfig& algorithm_config, + dnn::AlgorithmDesc* algorithm_desc, DeviceMemory* scratch_memory) { + *algorithm_desc = {}; + *scratch_memory = {}; + return true; + } + + virtual bool PrepareForConvolutionBackwardFilter( + Stream* stream, const BatchDescriptor& input_descriptor, + const DeviceMemory& input_data, const BatchDescriptor& output_descriptor, DeviceMemory backward_output_data, const ConvolutionDescriptor& convolution_descriptor, - const BatchDescriptor& input_descriptor, - DeviceMemory* backward_input_data, + const FilterDescriptor& filter_descriptor, + DeviceMemory* backward_filter_data, ScratchAllocator* scratch_allocator, const dnn::AlgorithmConfig& algorithm_config, - ProfileResult* output_profile_result) = 0; + dnn::AlgorithmDesc* algorithm_desc, DeviceMemory* scratch_memory) { + *algorithm_desc = {}; + *scratch_memory = {}; + return true; + } // Enqueues a single-precision backward convolution (for filter) operation // onto the stream. @@ -1398,8 +1543,8 @@ class DnnSupport { const ConvolutionDescriptor& convolution_descriptor, const FilterDescriptor& filter_descriptor, DeviceMemory* backward_filter_data, - ScratchAllocator* scratch_allocator, - const dnn::AlgorithmConfig& algorithm_config, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, ProfileResult* output_profile_result) = 0; // Return a list of algorithms supported by the backward convolution pass for @@ -1416,8 +1561,8 @@ class DnnSupport { const ConvolutionDescriptor& convolution_descriptor, const FilterDescriptor& filter_descriptor, DeviceMemory* backward_filter_data, - ScratchAllocator* scratch_allocator, - const dnn::AlgorithmConfig& algorithm_config, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, ProfileResult* output_profile_result) = 0; virtual bool DoConvolveBackwardFilter( @@ -1428,8 +1573,8 @@ class DnnSupport { const ConvolutionDescriptor& convolution_descriptor, const FilterDescriptor& filter_descriptor, DeviceMemory* backward_filter_data, - ScratchAllocator* scratch_allocator, - const dnn::AlgorithmConfig& algorithm_config, + const dnn::AlgorithmDesc& algorithm_desc, + DeviceMemory* scratch_memory, ProfileResult* output_profile_result) = 0; // Enqueues a single-precision backward convolution (for bias) operation onto diff --git a/tensorflow/stream_executor/stream.cc b/tensorflow/stream_executor/stream.cc index 1b14e4c339..1befc18e19 100644 --- a/tensorflow/stream_executor/stream.cc +++ b/tensorflow/stream_executor/stream.cc @@ -549,11 +549,16 @@ Stream &Stream::ThenConvolveWithScratch( if (ok()) { if (dnn::DnnSupport *dnn = parent_->AsDnn()) { - CheckError(dnn->DoConvolve( + DeviceMemory scratch_memory; + dnn::AlgorithmDesc algorithm_desc; + CheckError(dnn->PrepareForConvolution( this, input_descriptor, input_data, filter_descriptor, filter_data, convolution_descriptor, output_descriptor, output, scratch_allocator, - dnn::AlgorithmConfig(), - /*output_profile_result=*/nullptr)); + dnn::AlgorithmConfig(), &algorithm_desc, &scratch_memory)); + CheckError(dnn->DoConvolve( + this, input_descriptor, input_data, filter_descriptor, filter_data, + convolution_descriptor, output_descriptor, output, algorithm_desc, + &scratch_memory, nullptr)); } else { SetErrorAndLogNoDnnSupport(); } @@ -576,11 +581,16 @@ Stream &Stream::ThenConvolveWithScratch( if (ok()) { if (dnn::DnnSupport *dnn = parent_->AsDnn()) { - CheckError(dnn->DoConvolve( + DeviceMemory scratch_memory; + dnn::AlgorithmDesc algorithm_desc; + CheckError(dnn->PrepareForConvolution( this, input_descriptor, input_data, filter_descriptor, filter_data, convolution_descriptor, output_descriptor, output, scratch_allocator, - dnn::AlgorithmConfig(), - /*output_profile_result=*/nullptr)); + dnn::AlgorithmConfig(), &algorithm_desc, &scratch_memory)); + CheckError(dnn->DoConvolve( + this, input_descriptor, input_data, filter_descriptor, filter_data, + convolution_descriptor, output_descriptor, output, algorithm_desc, + &scratch_memory, nullptr)); } else { SetErrorAndLogNoDnnSupport(); } @@ -758,10 +768,18 @@ Stream &Stream::ThenConvolveWithAlgorithm( if (ok()) { if (dnn::DnnSupport *dnn = parent_->AsDnn()) { - auto status = dnn->DoConvolve( + DeviceMemory scratch_memory; + dnn::AlgorithmDesc algorithm_desc; + auto status = dnn->PrepareForConvolution( this, input_descriptor, input_data, filter_descriptor, filter_data, convolution_descriptor, output_descriptor, output, scratch_allocator, - algorithm_config, output_profile_result); + algorithm_config, &algorithm_desc, &scratch_memory); + if (status) { + status = dnn->DoConvolve( + this, input_descriptor, input_data, filter_descriptor, filter_data, + convolution_descriptor, output_descriptor, output, algorithm_desc, + &scratch_memory, output_profile_result); + } if (!status && !output_profile_result) { SetError(); } @@ -789,10 +807,18 @@ Stream &Stream::ThenConvolveWithAlgorithm( if (ok()) { if (dnn::DnnSupport *dnn = parent_->AsDnn()) { - auto status = dnn->DoConvolve( + DeviceMemory scratch_memory; + dnn::AlgorithmDesc algorithm_desc; + auto status = dnn->PrepareForConvolution( this, input_descriptor, input_data, filter_descriptor, filter_data, convolution_descriptor, output_descriptor, output, scratch_allocator, - algorithm_config, output_profile_result); + algorithm_config, &algorithm_desc, &scratch_memory); + if (status) { + status = dnn->DoConvolve( + this, input_descriptor, input_data, filter_descriptor, filter_data, + convolution_descriptor, output_descriptor, output, algorithm_desc, + &scratch_memory, output_profile_result); + } if (!status && !output_profile_result) { SetError(); } @@ -820,10 +846,18 @@ Stream &Stream::ThenConvolveWithAlgorithm( if (ok()) { if (dnn::DnnSupport *dnn = parent_->AsDnn()) { - auto status = dnn->DoConvolve( + DeviceMemory scratch_memory; + dnn::AlgorithmDesc algorithm_desc; + auto status = dnn->PrepareForConvolution( this, input_descriptor, input_data, filter_descriptor, filter_data, convolution_descriptor, output_descriptor, output, scratch_allocator, - algorithm_config, output_profile_result); + algorithm_config, &algorithm_desc, &scratch_memory); + if (status) { + status = dnn->DoConvolve( + this, input_descriptor, input_data, filter_descriptor, filter_data, + convolution_descriptor, output_descriptor, output, algorithm_desc, + &scratch_memory, output_profile_result); + } if (!status && !output_profile_result) { SetError(); } @@ -969,10 +1003,17 @@ Stream &Stream::ThenConvolveBackwardDataWithScratch( if (ok()) { if (dnn::DnnSupport *dnn = parent_->AsDnn()) { - CheckError(dnn->DoConvolveBackwardData( + DeviceMemory scratch_memory; + dnn::AlgorithmDesc algorithm_desc; + CheckError(dnn->PrepareForConvolutionBackwardData( this, filter_descriptor, filter_data, output_descriptor, backward_output_data, convolution_descriptor, input_descriptor, backward_input_data, scratch_allocator, dnn::AlgorithmConfig(), + &algorithm_desc, &scratch_memory)); + CheckError(dnn->DoConvolveBackwardData( + this, filter_descriptor, filter_data, output_descriptor, + backward_output_data, convolution_descriptor, input_descriptor, + backward_input_data, algorithm_desc, &scratch_memory, /*output_profile_result=*/nullptr)); } else { SetErrorAndLogNoDnnSupport(); @@ -999,11 +1040,20 @@ Stream &Stream::ThenConvolveBackwardDataWithAlgorithm( if (ok()) { if (dnn::DnnSupport *dnn = parent_->AsDnn()) { - auto status = dnn->DoConvolveBackwardData( + DeviceMemory scratch_memory; + dnn::AlgorithmDesc algorithm_desc; + auto status = dnn->PrepareForConvolutionBackwardData( this, filter_descriptor, filter_data, output_descriptor, backward_output_data, convolution_descriptor, input_descriptor, backward_input_data, scratch_allocator, algorithm_config, - output_profile_result); + &algorithm_desc, &scratch_memory); + if (status) { + status = dnn->DoConvolveBackwardData( + this, filter_descriptor, filter_data, output_descriptor, + backward_output_data, convolution_descriptor, input_descriptor, + backward_input_data, algorithm_desc, &scratch_memory, + output_profile_result); + } if (!status && !output_profile_result) { SetError(); } @@ -1032,11 +1082,20 @@ Stream &Stream::ThenConvolveBackwardDataWithAlgorithm( if (ok()) { if (dnn::DnnSupport *dnn = parent_->AsDnn()) { - auto status = dnn->DoConvolveBackwardData( + DeviceMemory scratch_memory; + dnn::AlgorithmDesc algorithm_desc; + auto status = dnn->PrepareForConvolutionBackwardData( this, filter_descriptor, filter_data, output_descriptor, backward_output_data, convolution_descriptor, input_descriptor, backward_input_data, scratch_allocator, algorithm_config, - output_profile_result); + &algorithm_desc, &scratch_memory); + if (status) { + status = dnn->DoConvolveBackwardData( + this, filter_descriptor, filter_data, output_descriptor, + backward_output_data, convolution_descriptor, input_descriptor, + backward_input_data, algorithm_desc, &scratch_memory, + output_profile_result); + } if (!status && !output_profile_result) { SetError(); } @@ -1065,11 +1124,20 @@ Stream &Stream::ThenConvolveBackwardDataWithAlgorithm( if (ok()) { if (dnn::DnnSupport *dnn = parent_->AsDnn()) { - auto status = dnn->DoConvolveBackwardData( + DeviceMemory scratch_memory; + dnn::AlgorithmDesc algorithm_desc; + auto status = dnn->PrepareForConvolutionBackwardData( this, filter_descriptor, filter_data, output_descriptor, backward_output_data, convolution_descriptor, input_descriptor, backward_input_data, scratch_allocator, algorithm_config, - output_profile_result); + &algorithm_desc, &scratch_memory); + if (status) { + status = dnn->DoConvolveBackwardData( + this, filter_descriptor, filter_data, output_descriptor, + backward_output_data, convolution_descriptor, input_descriptor, + backward_input_data, algorithm_desc, &scratch_memory, + output_profile_result); + } if (!status && !output_profile_result) { SetError(); } @@ -1096,10 +1164,17 @@ Stream &Stream::ThenConvolveBackwardDataWithScratch( if (ok()) { if (dnn::DnnSupport *dnn = parent_->AsDnn()) { - CheckError(dnn->DoConvolveBackwardData( + DeviceMemory scratch_memory; + dnn::AlgorithmDesc algorithm_desc; + CheckError(dnn->PrepareForConvolutionBackwardData( this, filter_descriptor, filter_data, output_descriptor, backward_output_data, convolution_descriptor, input_descriptor, backward_input_data, scratch_allocator, dnn::AlgorithmConfig(), + &algorithm_desc, &scratch_memory)); + CheckError(dnn->DoConvolveBackwardData( + this, filter_descriptor, filter_data, output_descriptor, + backward_output_data, convolution_descriptor, input_descriptor, + backward_input_data, algorithm_desc, &scratch_memory, /*output_profile_result=*/nullptr)); } else { SetErrorAndLogNoDnnSupport(); @@ -1138,10 +1213,17 @@ Stream &Stream::ThenConvolveBackwardFilterWithScratch( if (ok()) { if (dnn::DnnSupport *dnn = parent_->AsDnn()) { - CheckError(dnn->DoConvolveBackwardFilter( + DeviceMemory scratch_memory; + dnn::AlgorithmDesc algorithm_desc; + CheckError(dnn->PrepareForConvolutionBackwardFilter( this, input_descriptor, input_data, output_descriptor, backward_output_data, convolution_descriptor, filter_descriptor, backward_filter_data, scratch_allocator, dnn::AlgorithmConfig(), + &algorithm_desc, &scratch_memory)); + CheckError(dnn->DoConvolveBackwardFilter( + this, input_descriptor, input_data, output_descriptor, + backward_output_data, convolution_descriptor, filter_descriptor, + backward_filter_data, algorithm_desc, &scratch_memory, /*output_profile_result=*/nullptr)); } else { SetErrorAndLogNoDnnSupport(); @@ -1168,11 +1250,20 @@ Stream &Stream::ThenConvolveBackwardFilterWithAlgorithm( if (ok()) { if (dnn::DnnSupport *dnn = parent_->AsDnn()) { - auto status = dnn->DoConvolveBackwardFilter( + DeviceMemory scratch_memory; + dnn::AlgorithmDesc algorithm_desc; + auto status = dnn->PrepareForConvolutionBackwardFilter( this, input_descriptor, input_data, output_descriptor, backward_output_data, convolution_descriptor, filter_descriptor, backward_filter_data, scratch_allocator, algorithm_config, - output_profile_result); + &algorithm_desc, &scratch_memory); + if (status) { + status = dnn->DoConvolveBackwardFilter( + this, input_descriptor, input_data, output_descriptor, + backward_output_data, convolution_descriptor, filter_descriptor, + backward_filter_data, algorithm_desc, &scratch_memory, + output_profile_result); + } if (!status && !output_profile_result) { SetError(); } @@ -1201,11 +1292,20 @@ Stream &Stream::ThenConvolveBackwardFilterWithAlgorithm( if (ok()) { if (dnn::DnnSupport *dnn = parent_->AsDnn()) { - auto status = dnn->DoConvolveBackwardFilter( + DeviceMemory scratch_memory; + dnn::AlgorithmDesc algorithm_desc; + auto status = dnn->PrepareForConvolutionBackwardFilter( this, input_descriptor, input_data, output_descriptor, backward_output_data, convolution_descriptor, filter_descriptor, backward_filter_data, scratch_allocator, algorithm_config, - output_profile_result); + &algorithm_desc, &scratch_memory); + if (status) { + status = dnn->DoConvolveBackwardFilter( + this, input_descriptor, input_data, output_descriptor, + backward_output_data, convolution_descriptor, filter_descriptor, + backward_filter_data, algorithm_desc, &scratch_memory, + output_profile_result); + } if (!status && !output_profile_result) { SetError(); } @@ -1232,10 +1332,17 @@ Stream &Stream::ThenConvolveBackwardFilterWithScratch( if (ok()) { if (dnn::DnnSupport *dnn = parent_->AsDnn()) { - CheckError(dnn->DoConvolveBackwardFilter( + DeviceMemory scratch_memory; + dnn::AlgorithmDesc algorithm_desc; + CheckError(dnn->PrepareForConvolutionBackwardFilter( this, input_descriptor, input_data, output_descriptor, backward_output_data, convolution_descriptor, filter_descriptor, backward_filter_data, scratch_allocator, dnn::AlgorithmConfig(), + &algorithm_desc, &scratch_memory)); + CheckError(dnn->DoConvolveBackwardFilter( + this, input_descriptor, input_data, output_descriptor, + backward_output_data, convolution_descriptor, filter_descriptor, + backward_filter_data, algorithm_desc, &scratch_memory, /*output_profile_result=*/nullptr)); } else { SetErrorAndLogNoDnnSupport(); @@ -1262,11 +1369,20 @@ Stream &Stream::ThenConvolveBackwardFilterWithAlgorithm( if (ok()) { if (dnn::DnnSupport *dnn = parent_->AsDnn()) { - auto status = dnn->DoConvolveBackwardFilter( + DeviceMemory scratch_memory; + dnn::AlgorithmDesc algorithm_desc; + auto status = dnn->PrepareForConvolutionBackwardFilter( this, input_descriptor, input_data, output_descriptor, backward_output_data, convolution_descriptor, filter_descriptor, backward_filter_data, scratch_allocator, algorithm_config, - output_profile_result); + &algorithm_desc, &scratch_memory); + if (status) { + status = dnn->DoConvolveBackwardFilter( + this, input_descriptor, input_data, output_descriptor, + backward_output_data, convolution_descriptor, filter_descriptor, + backward_filter_data, algorithm_desc, &scratch_memory, + output_profile_result); + } if (!status && !output_profile_result) { SetError(); } -- GitLab From 04266f6c791113ec9e0098e52579a4c53e9acc0b Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Wed, 9 Jan 2019 12:55:40 -0800 Subject: [PATCH 570/622] Fix off-by-one error in time calculation for eta ecm3531 platform. PiperOrigin-RevId: 228571040 --- .../lite/experimental/micro/tools/make/targets/ecm3531/_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/_main.c b/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/_main.c index 93941e119f..2764f3ba50 100644 --- a/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/_main.c +++ b/tensorflow/lite/experimental/micro/tools/make/targets/ecm3531/_main.c @@ -85,7 +85,7 @@ void EtaPrintExecutionTime(uint64_t time_ms) { time_ms = time_ms / 10; time_string[k1] = (char)(0x30 + c); } - for (k1 = 4; k1 > 0; k1--) { // print out 1 char at a time + for (k1 = 4; k1 >= 0; k1--) { // print out 1 char at a time EtaCspUartPutc(&g_sUart1, time_string[k1]); } } else { -- GitLab From 998a22ab58d8e5faa1f041afea1e322797d70329 Mon Sep 17 00:00:00 2001 From: Shivani Agrawal Date: Wed, 9 Jan 2019 13:05:00 -0800 Subject: [PATCH 571/622] [tf.data] document update to correct the non-working example. PiperOrigin-RevId: 228572720 --- tensorflow/python/data/experimental/ops/stats_aggregator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/python/data/experimental/ops/stats_aggregator.py b/tensorflow/python/data/experimental/ops/stats_aggregator.py index d5fcc033ab..3e4c66be27 100644 --- a/tensorflow/python/data/experimental/ops/stats_aggregator.py +++ b/tensorflow/python/data/experimental/ops/stats_aggregator.py @@ -45,7 +45,7 @@ class StatsAggregator(object): # Apply `StatsOptions` to associate `dataset` with `aggregator`. options = dataset_ops.Options() - options.experimental_stats = tf.data.experimental.StatsOptions(aggregator) + options.experimental_stats.aggregator = aggregator dataset = dataset.with_options(options) ``` -- GitLab From 02747c96978bc76427cf45d32ecb24a866520043 Mon Sep 17 00:00:00 2001 From: Jared Duke Date: Wed, 9 Jan 2019 13:06:08 -0800 Subject: [PATCH 572/622] Fix tensor_utils_test Update another test target relying on lite/kernels:test_util, which pulls in TF and requires the tf_cc_test build rule. A follow-up CL will remove this requirement for this and other kernel tests. PiperOrigin-RevId: 228572896 --- tensorflow/lite/kernels/internal/BUILD | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tensorflow/lite/kernels/internal/BUILD b/tensorflow/lite/kernels/internal/BUILD index 74a11a72ef..b734b2d6cc 100644 --- a/tensorflow/lite/kernels/internal/BUILD +++ b/tensorflow/lite/kernels/internal/BUILD @@ -1,3 +1,4 @@ +load("//tensorflow:tensorflow.bzl", "tf_cc_test") load("//tensorflow/lite:build_def.bzl", "tflite_copts") load("//tensorflow/lite:special_rules.bzl", "tflite_portable_test_suite") @@ -555,10 +556,11 @@ cc_library( ], ) -cc_test( +# TODO(b/122597976): Eliminate TF dependency from lite/kernels:test_util, +# in turn eliminating the need to use tf_cc_test for any dependent tests. +tf_cc_test( name = "tensor_utils_test", srcs = ["tensor_utils_test.cc"], - copts = NEON_FLAGS_IF_APPLICABLE, linkopts = select({ "//tensorflow:android": [ "-fPIE -pie", -- GitLab From 4495df6cb19802fabaae9999a3b3fdcfe88b268d Mon Sep 17 00:00:00 2001 From: Shashi Shekhar Date: Wed, 9 Jan 2019 13:32:55 -0800 Subject: [PATCH 573/622] Update types in accuracy tool. PiperOrigin-RevId: 228577610 --- tensorflow/lite/tools/accuracy/utils.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tensorflow/lite/tools/accuracy/utils.cc b/tensorflow/lite/tools/accuracy/utils.cc index c19dc1ff7c..953892b8dd 100644 --- a/tensorflow/lite/tools/accuracy/utils.cc +++ b/tensorflow/lite/tools/accuracy/utils.cc @@ -38,6 +38,12 @@ DataType GetTFDataType(TfLiteType tflite_type) { return DT_FLOAT; case kTfLiteUInt8: return DT_UINT8; + case kTfLiteInt8: + return DT_INT8; + case kTfLiteInt32: + return DT_INT32; + case kTfLiteInt64: + return DT_INT64; default: return DT_INVALID; } -- GitLab From 0dc03e940edcab6e6680990557634b335d3be4e9 Mon Sep 17 00:00:00 2001 From: Pete Warden Date: Wed, 9 Jan 2019 13:45:56 -0800 Subject: [PATCH 574/622] Fixed target name in documentation PiperOrigin-RevId: 228580269 --- tensorflow/lite/experimental/micro/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/lite/experimental/micro/README.md b/tensorflow/lite/experimental/micro/README.md index 173f91fe90..931c8d9d31 100644 --- a/tensorflow/lite/experimental/micro/README.md +++ b/tensorflow/lite/experimental/micro/README.md @@ -381,7 +381,7 @@ auto-generated for any target you can compile using the main Make system, using a command like this (making sure you've run `download_dependencies.sh` first): ``` -make -f tensorflow/lite/experimental/micro/tools/make/Makefile TARGET=mbed TAGS="CMSIS disco_f746ng" generate_micro_speech_main_test_mbed_project +make -f tensorflow/lite/experimental/micro/tools/make/Makefile TARGET=mbed TAGS="CMSIS disco_f746ng" generate_micro_speech_mbed_project ``` This will create a folder in -- GitLab From 9545dccbd749a626c5451421b090ed1c70125d6a Mon Sep 17 00:00:00 2001 From: Russell Power Date: Wed, 9 Jan 2019 13:48:27 -0800 Subject: [PATCH 575/622] graph_partition: Fix absl::Hash related issue in Windows build. PiperOrigin-RevId: 228580775 --- tensorflow/core/graph/graph_partition.cc | 35 ++++++++++++++++-------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/tensorflow/core/graph/graph_partition.cc b/tensorflow/core/graph/graph_partition.cc index be0cac50a1..00c7a5b091 100644 --- a/tensorflow/core/graph/graph_partition.cc +++ b/tensorflow/core/graph/graph_partition.cc @@ -67,16 +67,14 @@ struct DupRecvKey { c.recv_output_on_host); } - friend bool operator==(const DupRecvKey& x, const DupRecvKey& y); + friend bool operator==(const DupRecvKey& x, const DupRecvKey& y) { + return (x.src_node_id == y.src_node_id) && + (x.src_output_slot == y.src_output_slot) && + (x.dst_graph == y.dst_graph) && + (x.recv_output_on_host == y.recv_output_on_host); + } }; -bool operator==(const DupRecvKey& x, const DupRecvKey& y) { - return (x.src_node_id == y.src_node_id) && - (x.src_output_slot == y.src_output_slot) && - (x.dst_graph == y.dst_graph) && - (x.recv_output_on_host == y.recv_output_on_host); -} - // struct used to store the recvs, so that start times can be properly updated struct RecvInfo { NodeDef* recv; @@ -88,7 +86,22 @@ typedef absl::flat_hash_map DupRecvTable; // A map used to store memory types for the inputs/outputs of every node. // The key is a pair of ints consisting of a node id and input/output index. -typedef absl::flat_hash_map, MemoryType> MemoryTypeMap; +// TODO(power): migrate back to std::pair when absl::Hash is fixed for MSVC. +struct NodePort { + int node_id; + int index; + + friend bool operator==(const NodePort& x, const NodePort& y) { + return x.node_id == y.node_id && x.index == y.index; + } + + template + friend H AbslHashValue(H h, const NodePort& c) { + return H::combine(std::move(h), c.node_id, c.index); + } +}; + +typedef absl::flat_hash_map MemoryTypeMap; // We collect the following information about the graph before performing // graph partitioning. @@ -552,10 +565,10 @@ Status BuildMemoryDeviceInfo(const Graph& g, GraphInfo* info) { int node_id = node->id(); info->device_types[node_id] = DeviceType(parsed.type); - for (size_t i = 0; i < input_memory_types.size(); ++i) { + for (int i = 0; i < input_memory_types.size(); ++i) { info->input_types[{node_id, i}] = input_memory_types[i]; } - for (size_t i = 0; i < output_memory_types.size(); ++i) { + for (int i = 0; i < output_memory_types.size(); ++i) { info->output_types[{node_id, i}] = output_memory_types[i]; } } -- GitLab From 2b2c042367675fdac67a12bc9bae9689babae07d Mon Sep 17 00:00:00 2001 From: Eugene Zhulenev Date: Wed, 9 Jan 2019 13:51:30 -0800 Subject: [PATCH 576/622] [Grappler] Add identity nodes to read function outputs in GrapplerFunctionItem instantiations PiperOrigin-RevId: 228581341 --- .../core/grappler/costs/graph_properties.cc | 4 +- .../optimizers/constant_folding_test.cc | 2 +- .../grappler/optimizers/function_optimizer.cc | 129 +++++++++--- .../optimizers/function_optimizer_test.cc | 2 +- .../optimizers/meta_optimizer_test.cc | 111 ++++++++-- tensorflow/core/grappler/utils/BUILD | 1 + tensorflow/core/grappler/utils/functions.cc | 196 ++++++++++++------ tensorflow/core/grappler/utils/functions.h | 83 ++++---- .../core/grappler/utils/functions_test.cc | 105 +++++++--- .../core/grappler/utils/grappler_test.cc | 74 ++++--- .../core/grappler/utils/grappler_test.h | 7 + 11 files changed, 513 insertions(+), 201 deletions(-) diff --git a/tensorflow/core/grappler/costs/graph_properties.cc b/tensorflow/core/grappler/costs/graph_properties.cc index 1ed96ca12a..863a5087ae 100644 --- a/tensorflow/core/grappler/costs/graph_properties.cc +++ b/tensorflow/core/grappler/costs/graph_properties.cc @@ -669,7 +669,7 @@ class SymbolicShapeRefiner { ctx->output_tensor_protos.resize(grappler_function_item.output_size(), nullptr); for (auto const& out_arg : grappler_function_item.outputs()) { - if (out_arg.output_tensors.size() > 1) { + if (out_arg.output_nodes.size() > 1) { // TODO(jmdecker): Handle case of multiple output tensors return errors::Unimplemented( "Output arguments with multiple output tensors are not yet " @@ -678,7 +678,7 @@ class SymbolicShapeRefiner { // It is guaranteed that output_tensors does not contain any control // inputs, so port_id >= 0. - TensorId out_tensor = ParseTensorName(out_arg.output_tensors[0]); + TensorId out_tensor = ParseTensorName(out_arg.output_nodes[0]); const NodeDef* retnode = gv.GetNode(out_tensor.node()); if (retnode == nullptr) { diff --git a/tensorflow/core/grappler/optimizers/constant_folding_test.cc b/tensorflow/core/grappler/optimizers/constant_folding_test.cc index 192f48272f..d7cabf5a8b 100644 --- a/tensorflow/core/grappler/optimizers/constant_folding_test.cc +++ b/tensorflow/core/grappler/optimizers/constant_folding_test.cc @@ -1601,7 +1601,7 @@ TEST_F(ConstantFoldingTest, SplitRemoval) { AddNode("split_dim", "Const", {}, {}, &want); AddNode("s1", "Identity", {"in1", AsControlDependency("split_dim")}, {}, &want); - AddNode("s2", "Split", {"in2", "split_dim"}, {}, &want); + AddNode("s2", "Split", {"split_dim", "in2"}, {}, &want); AddNode("out", "Add", {"s1", "s2"}, {}, &want); CompareGraphs(want, got); diff --git a/tensorflow/core/grappler/optimizers/function_optimizer.cc b/tensorflow/core/grappler/optimizers/function_optimizer.cc index d074676c3d..e9c30fee25 100644 --- a/tensorflow/core/grappler/optimizers/function_optimizer.cc +++ b/tensorflow/core/grappler/optimizers/function_optimizer.cc @@ -806,8 +806,17 @@ Status SpecializeFunction(const NodeDef& func_node, const FunctionDef& func, // update outputs for the fetch nodes, so we just skip them. std::vector> output_mapping; if (!signature.is_in_fetch_set) { - TF_RETURN_IF_ERROR( - RemoveUnusedOutputs(signature.active_outputs, &item, &output_mapping)); + int num_func_outputs = 0; + for (const auto& out_arg : item.outputs()) { + num_func_outputs += out_arg.output_nodes.size(); + } + + absl::flat_hash_set remove; + for (int i = 0; i < num_func_outputs; ++i) { + if (!signature.active_outputs.count(i)) remove.insert(i); + } + + TF_RETURN_IF_ERROR(RemoveFunctionOutputs(remove, &item, &output_mapping)); } // TODO(ezhulenev): Push down known input shapes. @@ -962,8 +971,10 @@ NodeDef InlinedFunctionInputsNode(const NodeDef& func_node, // Create an IdentityN node to hook the function outputs to: this ensures that // the function body is fully evaluated before its fanout gets scheduled. -NodeDef InlinedFunctionOutputsNode(const NodeDef& func_node, - const GrapplerFunctionItem& item) { +NodeDef InlinedFunctionOutputsNode( + const NodeDef& func_node, const GrapplerFunctionItem& item, + const absl::flat_hash_map + output_tensors) { NodeDef outputs; outputs.set_name(func_node.name()); outputs.set_op("IdentityN"); @@ -972,7 +983,8 @@ NodeDef InlinedFunctionOutputsNode(const NodeDef& func_node, (*outputs.mutable_attr())["T"].mutable_list(); for (const OutputArgExpansion& output_arg : item.outputs()) { - for (const string& output_tensor : output_arg.output_tensors) { + for (const string& output_node : output_arg.output_nodes) { + const absl::string_view output_tensor = output_tensors.at(output_node); type_list->add_type(output_arg.data_type); outputs.add_input(strings::StrCat(func_node.name(), "/", output_tensor)); } @@ -1004,29 +1016,51 @@ Status InlineDirectFunctionCall(const NodeDef& func_node, } // Mapping from input placeholder name to function input position. - int idx = 0; - absl::flat_hash_map input_placeholders_idx; + absl::flat_hash_map input_placeholders_idx; for (const InputArgExpansion& input_arg : item.inputs()) { for (const string& placeholder : input_arg.placeholders) { - input_placeholders_idx[placeholder] = idx++; + const int idx = input_placeholders_idx.size(); + input_placeholders_idx[placeholder] = idx; + } + } + + // Bypass identity nodes added to the graph in place of function outputs. + absl::flat_hash_set output_nodes; + for (const OutputArgExpansion& output_arg : item.outputs()) { + for (const string& output_node : output_arg.output_nodes) { + output_nodes.insert(output_node); } } + // For each function output value we added an identity node that reads the + // tensor from one of the function body nodes. When we inline function into + // the main graph we want to bypass these nodes, so we keep a mapping from + // 'output node name' -> 'output tensor name'. + absl::flat_hash_map output_tensors; + // Hook inlined function inputs to IdentityN node. NodeDef* func_inputs = optimized_graph->add_node(); *func_inputs = InlinedFunctionInputsNode(func_node, item); for (NodeDef& func_body_node : *item.mutable_function_body().mutable_node()) { - if (item.IsInputPlaceholder(func_body_node.name())) { - // Turn input placeholders into identity nodes. + const string& node_name = func_body_node.name(); + + // Skip output identity node, and update a mapping to the output tensor. + if (IsIdentity(func_body_node) && output_nodes.count(node_name)) { + output_tensors.emplace(node_name, func_body_node.input(0)); + continue; + } + + // Turn placeholders added in place of input arguments into identity nodes. + const auto input_placeholder_idx = input_placeholders_idx.find(node_name); + if (input_placeholder_idx != input_placeholders_idx.end()) { CHECK_EQ(0, func_body_node.input_size()); func_body_node.set_op("Identity"); (*func_body_node.mutable_attr())["T"] = func_body_node.attr().at("dtype"); func_body_node.mutable_attr()->erase("dtype"); func_body_node.mutable_attr()->erase("shape"); - int input_idx = input_placeholders_idx[func_body_node.name()]; - func_body_node.add_input( - strings::StrCat(func_inputs->name(), ":", input_idx)); + func_body_node.add_input(strings::StrCat(func_inputs->name(), ":", + input_placeholder_idx->second)); } else { // Update the input names if any. for (string& input : *func_body_node.mutable_input()) { @@ -1082,9 +1116,12 @@ Status InlineDirectFunctionCall(const NodeDef& func_node, } } + DCHECK(output_tensors.size() == item.output_size()) + << "Each function output must be mapped to an output tensor"; + // Hook inlined function outputs to IdentityN node. NodeDef* func_outputs = optimized_graph->add_node(); - *func_outputs = InlinedFunctionOutputsNode(func_node, item); + *func_outputs = InlinedFunctionOutputsNode(func_node, item, output_tensors); return Status::OK(); } @@ -1363,11 +1400,11 @@ Status InlineIndirectFunctionCall(const NodeDef& func_node, } // Mapping from input placeholder name to function input position. - int idx = 0; absl::flat_hash_map input_placeholders_idx; for (const InputArgExpansion& input_arg : item.inputs()) { for (const string& placeholder : input_arg.placeholders) { - input_placeholders_idx[placeholder] = idx++; + const int idx = input_placeholders_idx.size(); + input_placeholders_idx[placeholder] = idx; } } @@ -1378,8 +1415,11 @@ Status InlineIndirectFunctionCall(const NodeDef& func_node, // same device as their corresponding input nodes. for (NodeDef& func_body_node : *item.graph.mutable_node()) { - if (item.IsInputPlaceholder(func_body_node.name())) { - const int input_idx = input_placeholders_idx[func_body_node.name()]; + const auto input_placeholder_idx = + input_placeholders_idx.find(func_body_node.name()); + + if (input_placeholder_idx != input_placeholders_idx.end()) { + const int input_idx = input_placeholder_idx->second; const GraphView::OutputPort output_port = ctx->graph_view().GetRegularFanin({&func_node, input_idx}); @@ -1441,16 +1481,23 @@ Status InlineIndirectFunctionCall(const NodeDef& func_node, // optimized graph: turn placeholders into identities, update nodes // connectivity, etc... + const auto inlined_node_name = [&func_node](const string& name) -> string { + return AddPrefixToNodeName(name, /*prefix=*/func_node.name()); + }; + for (NodeDef& func_body_node : *placed_graph_def.mutable_node()) { - if (item.IsInputPlaceholder(func_body_node.name())) { - // Turn input placeholders into identity node. + const string& node_name = func_body_node.name(); + + // Turn placeholders added in place of input arguments into identity nodes. + const auto input_placeholder_idx = input_placeholders_idx.find(node_name); + if (input_placeholder_idx != input_placeholders_idx.end()) { DCHECK_EQ(0, func_body_node.input_size()); func_body_node.set_op("Identity"); (*func_body_node.mutable_attr())["T"] = func_body_node.attr().at("dtype"); func_body_node.mutable_attr()->erase("dtype"); func_body_node.mutable_attr()->erase("shape"); - const int input_idx = input_placeholders_idx[func_body_node.name()]; - func_body_node.add_input(strings::StrCat(inputs[input_idx].ToString())); + const int input_idx = input_placeholder_idx->second; + func_body_node.add_input(inputs[input_idx].ToString()); // All side effects must happen before inputs can start executing. for (const string& hb_node : happens_before) { @@ -1460,7 +1507,7 @@ Status InlineIndirectFunctionCall(const NodeDef& func_node, } else { // Update inputs of the regular function body nodes. for (string& input : *func_body_node.mutable_input()) { - input = AddPrefixToNodeName(input, /*prefix=*/func_node.name()); + input = inlined_node_name(input); } if (func_body_node.input_size() == 0 && !empty_inputs_hook.empty()) { *func_body_node.add_input() = empty_inputs_hook[0]; @@ -1494,7 +1541,7 @@ Status InlineIndirectFunctionCall(const NodeDef& func_node, for (NodeDef& func_body_node : *placed_graph_def.mutable_node()) { if (!IsFreeOfSideEffect(func_body_node, &ctx->function_library())) { int num_fanouts = placed_graph_view.NumFanouts( - func_body_node, /*include_controlling_nodes=*/true); + func_body_node, /*include_controlled_nodes=*/true); // If the node doesn't have any outgoing edges and we do not have any // nodes in the `happens_after` set, we can't inline a function and @@ -1514,11 +1561,36 @@ Status InlineIndirectFunctionCall(const NodeDef& func_node, } } + // Identity nodes added to the function body in place of function outputs. + absl::flat_hash_set output_nodes; + for (const OutputArgExpansion& output_arg : item.outputs()) { + for (const string& output_node : output_arg.output_nodes) { + output_nodes.insert(inlined_node_name(output_node)); + } + } + + // For each function output value we added an identity node that reads the + // tensor from one of the function body nodes. When we inline function into + // the main graph we want to bypass these nodes, so we keep a mapping from + // 'output node name' -> 'output tensor name'. + absl::flat_hash_map output_tensors; + // Move all the nodes to the optimized graph after successful preprocessing. for (NodeDef& func_body_node : *placed_graph_def.mutable_node()) { + const string& node_name = func_body_node.name(); + + // Skip output identity node, and add a mapping to the output tensor. + if (IsIdentity(func_body_node) && output_nodes.count(node_name)) { + output_tensors.emplace(node_name, func_body_node.input(0)); + continue; + } + optimized_graph->add_node()->Swap(&func_body_node); } + DCHECK(output_tensors.size() == item.output_size()) + << "Each function output must be mapped to an output tensor"; + // TODO(ezhulenev): Inline nested indirect function calls. // Indirect function call is fully inlined into the optimized graph, and we do @@ -1526,10 +1598,13 @@ Status InlineIndirectFunctionCall(const NodeDef& func_node, // mapping from old output tensors, to the outputs of inlined nodes. int output_idx = 0; for (const OutputArgExpansion& output : item.outputs()) { - for (const string& output_tensor : output.output_tensors) { + for (const string& output_node : output.output_nodes) { + const string inlined_output = inlined_node_name(output_node); + const string& output_tensor = output_tensors.at(inlined_output); + const SafeTensorId from_tensor(func_node.name(), output_idx++); - const SafeTensorId to_tensor = ParseTensorName( - AddPrefixToNodeName(output_tensor, /*prefix=*/func_node.name())); + const SafeTensorId to_tensor = ParseTensorName(output_tensor); + ctx->AddTensorMapping(from_tensor, to_tensor); } } diff --git a/tensorflow/core/grappler/optimizers/function_optimizer_test.cc b/tensorflow/core/grappler/optimizers/function_optimizer_test.cc index 79da7dfa2d..cebd002bed 100644 --- a/tensorflow/core/grappler/optimizers/function_optimizer_test.cc +++ b/tensorflow/core/grappler/optimizers/function_optimizer_test.cc @@ -660,7 +660,7 @@ TEST_F(FunctionOptimizerTest, InlineSymbolicGradient_IdentityFunc) { test::ExpectTensorEqual(expected[0], optimized[0]); } -TEST_F(FunctionOptimizerTest, InlineSymbolicGradient_NoInlineFunc) { +TEST_F(FunctionOptimizerTest, InlineSymbolicGradientNoInlineFunc) { FunctionOptimizer optimizer(RewriterConfig::ON); FunctionDef func = FunctionDefHelper::Define( diff --git a/tensorflow/core/grappler/optimizers/meta_optimizer_test.cc b/tensorflow/core/grappler/optimizers/meta_optimizer_test.cc index 12db5d6ca9..a061f6194a 100644 --- a/tensorflow/core/grappler/optimizers/meta_optimizer_test.cc +++ b/tensorflow/core/grappler/optimizers/meta_optimizer_test.cc @@ -231,7 +231,7 @@ TEST_F(MetaOptimizerTest, RunToggleOptimizersAndCustomGraphOptimizerTwice) { TEST_F(MetaOptimizerTest, OptimizeFunctionLibrary) { using test::function::NDef; - // Enable ony function optimization. + // Enable only function optimization. ConfigProto config_proto; auto& rewriter_config = *config_proto.mutable_graph_options()->mutable_rewrite_options(); @@ -300,7 +300,7 @@ TEST_F(MetaOptimizerTest, OptimizeFunctionLibrary) { output.library()); // Specialized and optimized functions should be added to the graph. - EXPECT_EQ(6, optimized_flib.num_functions()); + EXPECT_EQ(5, optimized_flib.num_functions()); // Get a specialized function name. const auto specialized_name = [](const string& fn, const string& node, @@ -314,25 +314,22 @@ TEST_F(MetaOptimizerTest, OptimizeFunctionLibrary) { specialized_name("MyQuadratic", "quadratic", "tf_graph"); // MySquare should be specialized and optimized for 3 instantiations: - // 1. 'square' node in the main graph - // 2. 'square' node in the MyQuadratic specialization (not in a fetch set) - // 3. 'quadratic' node in the MyQuadratic specialization (is in a fetch set) + // 1. 'square' node in the main graph + // 2. 'square' node in the MyQuadratic specialization + // 3*. 'quadratic' node in the MyQuadratic specialization + // has identical instantiation context to #2 const string optimized_1 = specialized_name("MySquare", "square", "tf_graph"); const string optimized_2 = specialized_name("MySquare", "square", optimized_0); - const string optimized_3 = - specialized_name("MySquare", "quadratic", optimized_0); const FunctionDef* optimized_func_0 = optimized_flib.Find(optimized_0); const FunctionDef* optimized_func_1 = optimized_flib.Find(optimized_1); const FunctionDef* optimized_func_2 = optimized_flib.Find(optimized_2); - const FunctionDef* optimized_func_3 = optimized_flib.Find(optimized_3); ASSERT_NE(optimized_func_0, nullptr); ASSERT_NE(optimized_func_1, nullptr); ASSERT_NE(optimized_func_2, nullptr); - ASSERT_NE(optimized_func_3, nullptr); // Graph should call optimized function. int count = 0; @@ -351,13 +348,13 @@ TEST_F(MetaOptimizerTest, OptimizeFunctionLibrary) { if (node.name() == "square" && ++count) { EXPECT_EQ(optimized_2, node.op()); } else if (node.name() == "quadratic" && ++count) { - EXPECT_EQ(optimized_3, node.op()); + EXPECT_EQ(optimized_2, node.op()); } } EXPECT_EQ(2, count); - const std::vector optimized_funcs = { - optimized_func_1, optimized_func_2, optimized_func_3}; + const std::vector optimized_funcs = {optimized_func_1, + optimized_func_2}; // MyMul should be inlined into all optimized versions of MySquare. for (const FunctionDef* optimized_func : optimized_funcs) { @@ -403,6 +400,96 @@ TEST_F(MetaOptimizerTest, OptimizeFunctionLibrary) { test::ExpectTensorEqual(tensors_expected[1], tensors[1]); } +TEST_F(MetaOptimizerTest, OptimizeFunctionLibraryPruneUnusedOutputs) { + using test::function::NDef; + + ConfigProto config_proto; + MetaOptimizer optimizer(nullptr, config_proto); + + // MyMul computes x*y three times and has three output values. + FunctionDef my_mul = FunctionDefHelper::Create( + "MyMul", {"x:T", "y:T"}, {"z0:T", "z1:T", "z2:T"}, {"T: {float, int32}"}, + {{{"output0"}, "Mul", {"x", "y"}, {{"T", "$T"}}}, + {{"output1"}, "Mul", {"x", "y"}, {{"T", "$T"}}}, + {{"output2"}, "Mul", {"x", "y"}, {{"T", "$T"}}}}, + /* Mapping between function returns and function node outputs. */ + {{"z0", "output0:z:0"}, {"z1", "output1:z:0"}, {"z2", "output2:z:0"}}); + + // Call MyMyl and forward all three outputs. + FunctionDef my_fwd = FunctionDefHelper::Create( + "Fwd", {"x:T", "y:T"}, {"z0:T", "z1:T", "z2:T"}, {"T: {float, int32}"}, + {{{"output"}, "MyMul", {"x", "y"}, {{"T", "$T"}}}}, + /* Mapping between function returns and function node outputs. */ + {{"z0", "output:z0:0"}, {"z1", "output:z1:0"}, {"z2", "output:z2:0"}}); + + // Mark both functions as `_noinline` to trigger specialization. + (*my_mul.mutable_attr())["_noinline"].set_b(true); + (*my_fwd.mutable_attr())["_noinline"].set_b(true); + std::vector function_library = {my_mul, my_fwd}; + + // Tensorflow graph: + // a = Placeholder[T=float] + // b = Placeholder[T=float] + // fwd = Fwd(a, b) + // + // Fetch fwd:2 via Identity node. + GrapplerItem item; + item.id = "tf_graph"; + item.fetch = {"ret"}; + item.graph = test::function::GDef( + {NDef("a", "Placeholder", {}, {{"dtype", DT_FLOAT}}, kDevice), + NDef("b", "Placeholder", {}, {{"dtype", DT_FLOAT}}, kDevice), + NDef("fwd", "Fwd", {"a", "b"}, {{"T", DT_FLOAT}}, kDevice), + NDef("ret", "Identity", {"fwd:2"}, {{"T", DT_FLOAT}}, kDevice)}, + function_library); + + GraphDef output; + TF_EXPECT_OK(optimizer.Optimize(nullptr, item, &output)); + + FunctionLibraryDefinition optimized_flib(OpRegistry::Global(), + output.library()); + + // Specialized functions should be added to the graph. + EXPECT_EQ(3, optimized_flib.num_functions()); + + // Expected names of the specialized functions. + const string specialized_my_fwd = "Fwd_specialized_for_fwd_at_tf_graph"; + const string specialized_my_mul = + absl::StrCat("MyMul_specialized_for_output_at_", specialized_my_fwd); + + // Specialized MyMul should have just one output argument. + FunctionDef expected_my_mul = FunctionDefHelper::Create( + specialized_my_mul, {"x:float", "y:float"}, {"z2:float"}, {}, + {{{"output2"}, "Mul", {"x", "y"}, {{"T", DT_FLOAT}}}}, + /* Mapping between function returns and function node outputs. */ + {{"z2", "output2:z:0"}}); + + // Specialized Fwd should also have just one output argument. + FunctionDef expected_my_fwd = FunctionDefHelper::Create( + specialized_my_fwd, {"x:float", "y:float"}, {"z2:float"}, {}, + {{{"output"}, specialized_my_mul, {"x", "y"}, {{"T", DT_FLOAT}}}}, + /* Mapping between function returns and function node outputs. */ + {{"z2", "output:z2:0"}}); + + const FunctionDef* my_mul_spec = optimized_flib.Find(specialized_my_mul); + const FunctionDef* my_fwd_spec = optimized_flib.Find(specialized_my_fwd); + + ASSERT_NE(my_mul_spec, nullptr); + ASSERT_NE(my_fwd_spec, nullptr); + + CompareFunctions(expected_my_mul, *my_mul_spec); + CompareFunctions(expected_my_fwd, *my_fwd_spec); + + item.feed.emplace_back("a", test::AsScalar(2.0f)); + item.feed.emplace_back("b", test::AsScalar(4.0f)); + auto tensors_expected = EvaluateFetchNodes(item); + + GrapplerItem optimized = item.WithGraph(std::move(output)); + auto tensors = EvaluateFetchNodes(optimized); + + test::ExpectTensorEqual(tensors_expected[0], tensors[0]); +} + TEST_F(MetaOptimizerTest, OptimizeFunctionLibraryPruneFunctionBody) { using test::function::NDef; diff --git a/tensorflow/core/grappler/utils/BUILD b/tensorflow/core/grappler/utils/BUILD index ff15541cdc..1fd0a02b65 100644 --- a/tensorflow/core/grappler/utils/BUILD +++ b/tensorflow/core/grappler/utils/BUILD @@ -182,6 +182,7 @@ cc_library( "@com_google_absl//absl/algorithm:container", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/container:flat_hash_set", + "@com_google_absl//absl/container:inlined_vector", "@com_google_absl//absl/strings", ], ) diff --git a/tensorflow/core/grappler/utils/functions.cc b/tensorflow/core/grappler/utils/functions.cc index 7c2180ae40..150728d030 100644 --- a/tensorflow/core/grappler/utils/functions.cc +++ b/tensorflow/core/grappler/utils/functions.cc @@ -328,13 +328,12 @@ GrapplerFunctionItem::GrapplerFunctionItem( for (const InputArgExpansion& input_arg : input_arg_expansions_) { for (const string& placeholder : input_arg.placeholders) { feed.push_back({placeholder, Tensor()}); - input_arg_placeholders_.insert(placeholder); } } // Fill the fetch nodes with outputs. for (const OutputArgExpansion& output_arg : output_arg_expansions_) { - for (const string& output_tensor : output_arg.output_tensors) { - fetch.push_back(output_tensor); + for (const string& output_node : output_arg.output_nodes) { + fetch.push_back(output_node); } } @@ -357,11 +356,6 @@ const std::size_t GrapplerFunctionItem::input_size() const { return input_arg_expansions_.size(); } -bool GrapplerFunctionItem::IsInputPlaceholder(const string& node_name) const { - return input_arg_placeholders_.find(node_name) != - input_arg_placeholders_.end(); -} - const std::vector& GrapplerFunctionItem::outputs() const { return output_arg_expansions_; } @@ -514,12 +508,18 @@ Status MakeGrapplerFunctionItem(const FunctionDef& func, // TODO(ezhulenev): support functions with tensor sequence inputs/outputs - // Make sure that there is no tensor sequences in outputs + // Make sure that there are no tensor lists in inputs or outputs. + for (const OpDef::ArgDef& input : signature.input_arg()) { + if (!input.type_list_attr().empty() || !input.number_attr().empty()) { + return errors::InvalidArgument( + "Inputs with lists of tensors are not supported. Input: ", + input.name()); + } + } for (const OpDef::ArgDef& output : signature.output_arg()) { if (!output.type_list_attr().empty() || !output.number_attr().empty()) { return errors::InvalidArgument( - "Outputs with sequence of tensors are not supported. Unsupported " - "output: ", + "Outputs with lists of tensors are not supported. Output: ", output.name()); } } @@ -529,13 +529,6 @@ Status MakeGrapplerFunctionItem(const FunctionDef& func, // For each input argument create a placeholder in function body. for (const OpDef::ArgDef& input : signature.input_arg()) { - if (!input.type_list_attr().empty() || !input.number_attr().empty()) { - return errors::InvalidArgument( - "Inputs with sequence of tensors are not supported. Unsupported " - "input: ", - input.name()); - } - DataType input_data_type; TF_RETURN_IF_ERROR(instantiation.GetArgType(input, &input_data_type)); @@ -554,8 +547,25 @@ Status MakeGrapplerFunctionItem(const FunctionDef& func, inputs.push_back(std::move(input_expansion)); } - // Add all function nodes to the function body + // Keep names of all nodes in the function body to guarantee that we do not + // add an identity with a duplicate name. + absl::flat_hash_set func_body_nodes; + + // Generate unique output node name: "${out_arg_name}_output_node_${index}". + const auto output_node_name = [&func_body_nodes](const OpDef::ArgDef& out, + int index) -> string { + string name = absl::StrCat(out.name(), "_output_node_", index); + int i = 1; + while (func_body_nodes.find(name) != func_body_nodes.end()) { + name = absl::StrCat(out.name(), "_output_node_", index, "_", i++); + } + return name; + }; + + // Add all function nodes to the function body. for (const NodeDef& func_def_node : func.node_def()) { + func_body_nodes.insert(func_def_node.name()); + NodeDef* new_node = function_body.add_node(); *new_node = func_def_node; @@ -578,8 +588,13 @@ Status MakeGrapplerFunctionItem(const FunctionDef& func, std::vector outputs; outputs.reserve(signature.output_arg_size()); - // Add function outputs + + // For each function output argument we create an Identity node in the + // function body, that reads output tensor from the function body node. for (const OpDef::ArgDef& out : signature.output_arg()) { + DataType output_data_type; + TF_RETURN_IF_ERROR(instantiation.GetArgType(out, &output_data_type)); + std::vector output_tensors; auto ret = func.ret().find(out.name()); TF_RETURN_IF_ERROR( @@ -589,13 +604,23 @@ Status MakeGrapplerFunctionItem(const FunctionDef& func, // Otherwise output must be one of the function inputs : connectivity.ExpandFunctionDefInput(out.name(), &output_tensors)); - DataType output_data_type; - TF_RETURN_IF_ERROR(instantiation.GetArgType(out, &output_data_type)); + absl::InlinedVector output_nodes; + for (int i = 0; i < output_tensors.size(); ++i) { + const string& output_tensor = output_tensors[i]; + + NodeDef* identity = function_body.add_node(); + identity->set_name(output_node_name(out, i)); + identity->set_op("Identity"); + (*identity->mutable_attr())["T"].set_type(output_data_type); + identity->add_input(output_tensor); + + output_nodes.push_back(identity->name()); + } OutputArgExpansion output{/*output_name=*/out.name(), /*data_type=*/output_data_type, /*is_ref=*/out.is_ref(), - /*output_tensors=*/std::move(output_tensors)}; + /*output_nodes=*/std::move(output_nodes)}; outputs.push_back(std::move(output)); } @@ -663,7 +688,6 @@ Status ReplaceInputWithConst(const NodeDef& input_const, int input_index, // Delete placeholder from input expansion. string placeholder_name = input_arg_expansion->placeholders[placeholder_idx]; - item->input_arg_placeholders_.erase(placeholder_name); input_arg_expansion->placeholders.erase( input_arg_expansion->placeholders.begin() + placeholder_idx); @@ -687,43 +711,46 @@ Status ReplaceInputWithConst(const NodeDef& input_const, int input_index, return Status::OK(); } -Status RemoveUnusedOutputs(const absl::flat_hash_set& active_outputs, - GrapplerFunctionItem* item, - std::vector>* output_mapping) { +Status RemoveFunctionOutputs(const absl::flat_hash_set& remove_outputs, + GrapplerFunctionItem* item, + std::vector>* output_mapping) { DCHECK(output_mapping->empty()); - // Do some sanity checking of the active outputs positions. - for (int active_output : active_outputs) { - if (active_output < 0 || active_output >= item->output_size()) { + // Code below assumes that we do not support tensor list outputs and there is + // a 1-to-1 mapping between output tensor and output argument expansion. + for (const OutputArgExpansion& out_arg : item->outputs()) { + DCHECK(out_arg.output_nodes.size() == 1) + << "Output arg expansion must have single output"; + } + + // Do some sanity checking of the removed outputs positions. + for (int remove_output : remove_outputs) { + if (remove_output < 0 || remove_output >= item->output_size()) { return errors::InvalidArgument( - "Active output position is out of bound: active_output=", - active_output, " num_output_args=", item->output_size()); + "Function output index is out of bound: index=", remove_output, + " max_output_index=", item->output_size()); } } - absl::flat_hash_set unused_output_args; - - const auto is_unused_output_arg = [&](const OutputArgExpansion& output) { - return unused_output_args.find(&output) != unused_output_args.end(); + absl::flat_hash_set remove_output_args; + const auto is_remove_output_arg = [&](const OutputArgExpansion& output) { + return remove_output_args.find(&output) != remove_output_args.end(); }; for (int i = 0; i < item->output_size(); ++i) { const OutputArgExpansion& output = item->output(i); - DCHECK(output.output_tensors.size() == 1) - << "Output arg expansion must have single tensor"; - - if (active_outputs.find(i) == active_outputs.end()) { - VLOG(3) << "Remove unused output: output_name=" << output.output_name - << " output_position=" << i; - unused_output_args.insert(&output); - } else if (!unused_output_args.empty()) { + if (remove_outputs.find(i) != remove_outputs.end()) { + VLOG(3) << "Remove functions output: output_name=" << output.output_name + << "(index = " << i << ")"; + remove_output_args.insert(&output); + } else if (!remove_output_args.empty()) { // Add output mapping only if output position changed. - output_mapping->push_back({i, i - unused_output_args.size()}); + output_mapping->push_back({i, i - remove_output_args.size()}); } } auto& o = item->output_arg_expansions_; - o.erase(std::remove_if(o.begin(), o.end(), is_unused_output_arg), o.end()); + o.erase(std::remove_if(o.begin(), o.end(), is_remove_output_arg), o.end()); return Status::OK(); } @@ -735,6 +762,55 @@ Status MakeFunctionDef(const GrapplerFunctionItem& item, func->mutable_signature()->set_description(item.description()); func->mutable_signature()->set_is_stateful(item.is_stateful()); + // Keep track of placeholders that were added to the graph in place of + // expanded function input arguments. + absl::flat_hash_set input_placeholders; + for (const InputArgExpansion& input_arg : item.inputs()) { + for (const string& placeholder : input_arg.placeholders) { + input_placeholders.insert(placeholder); + } + } + + // Keep track of identity nodes that were added to the graph in place of + // expanded function output arguments. + absl::flat_hash_set output_nodes; + for (const OutputArgExpansion& output_arg : item.outputs()) { + for (const string& output_node : output_arg.output_nodes) { + output_nodes.insert(output_node); + } + } + + // If the output identity node was not modified by any optimizer, we can + // bypass it and returns the function value from its input. + absl::flat_hash_map output_tensors; + for (const NodeDef& func_body_node : item.function_body().node()) { + if (!IsIdentity(func_body_node)) continue; + + const string& node_name = func_body_node.name(); + if (output_nodes.find(node_name) != output_nodes.end()) { + // Grappler optimizers might optimize nodes in the fanin of the output + // node, and forward their control dependencies. We can't express control + // dependencies in a function signature, so we have to keep the node. + if (func_body_node.input_size() == 1) { + VLOG(3) << "Bypass function output node: " << node_name << " -> " + << func_body_node.input(0); + output_tensors.emplace(node_name, func_body_node.input(0)); + } else { + VLOG(3) << "Keep function output node: " << node_name; + } + } + } + + // Return output tensor name (input of the output node) if it's safe to bypass + // output node, otherwise returns the output node name. + const auto output_tensor = + [&output_tensors](const OutputArgExpansion& output_arg) -> const string& { + const string& output_node = output_arg.output_nodes[0]; + const auto is_output_tensor = output_tensors.find(output_node); + return is_output_tensor == output_tensors.end() ? output_node + : is_output_tensor->second; + }; + // Build a GrapplerFunctionConnectivity from inputs and new function body. GrapplerFunctionConnectivity connectivity; TF_RETURN_IF_ERROR( @@ -742,8 +818,8 @@ Status MakeFunctionDef(const GrapplerFunctionItem& item, // Add function input arguments. for (const InputArgExpansion& input_arg : item.inputs()) { - CHECK(input_arg.placeholders.size() == 1) // do some sanity checking - << "Inputs of tensor sequences are not supported"; + DCHECK(input_arg.placeholders.size() == 1) // do some sanity checking + << "Inputs of tensor lists are not supported"; OpDef::ArgDef arg_def; arg_def.set_name(input_arg.input_name); @@ -754,8 +830,8 @@ Status MakeFunctionDef(const GrapplerFunctionItem& item, // Add function output arguments. for (const OutputArgExpansion& output_arg : item.outputs()) { - CHECK(output_arg.output_tensors.size() == 1) // do some sanity checking - << "Outputs of tensor sequences are not supported"; + DCHECK(output_arg.output_nodes.size() == 1) // do some sanity checking + << "Outputs of tensor lists are not supported"; OpDef::ArgDef arg_def; arg_def.set_name(output_arg.output_name); @@ -763,11 +839,9 @@ Status MakeFunctionDef(const GrapplerFunctionItem& item, arg_def.set_is_ref(output_arg.is_ref); *func->mutable_signature()->add_output_arg() = arg_def; - string ret; - for (const string& output_tensor : output_arg.output_tensors) { - TF_RETURN_IF_ERROR(connectivity.AsFunctionDefInput(output_tensor, &ret)); - (*func->mutable_ret())[output_arg.output_name] = ret; - } + TF_RETURN_IF_ERROR(connectivity.AsFunctionDefInput( + output_tensor(output_arg), + &(*func->mutable_ret())[output_arg.output_name])); } // Copy function definition specific attributes. @@ -778,12 +852,16 @@ Status MakeFunctionDef(const GrapplerFunctionItem& item, } // Copy function body nodes to the FunctionDef and update input format - for (const NodeDef& func_body_node : item.function_body().node()) { - // Do not copy input placeholders - if (item.IsInputPlaceholder(func_body_node.name())) continue; + for (const NodeDef& func_node : item.function_body().node()) { + const string& name = func_node.name(); + + // Do not copy input placeholders. + if (IsPlaceholder(func_node) && input_placeholders.count(name)) continue; + // Do not copy output nodes that we bypassed. + if (IsIdentity(func_node) && output_tensors.count(name)) continue; NodeDef* func_def_node = func->add_node_def(); - *func_def_node = func_body_node; + *func_def_node = func_node; TF_RETURN_IF_ERROR(connectivity.AsFunctionDefNode(func_def_node)); } diff --git a/tensorflow/core/grappler/utils/functions.h b/tensorflow/core/grappler/utils/functions.h index ce8a3e5ac7..d5a41e7473 100644 --- a/tensorflow/core/grappler/utils/functions.h +++ b/tensorflow/core/grappler/utils/functions.h @@ -21,6 +21,7 @@ limitations under the License. #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" +#include "absl/container/inlined_vector.h" #include "tensorflow/core/framework/attr_value.pb.h" #include "tensorflow/core/framework/function.h" #include "tensorflow/core/framework/function.pb.h" @@ -32,6 +33,21 @@ limitations under the License. namespace tensorflow { namespace grappler { +// WARNING(ezhulenev): Currently we do not support functions with inputs or +// outputs instantiated into multiple tensors. This can happen if the +// input/output type is 'T*N' or 'list(type)'. This is enforced by multiple +// checks across this file and also function_optimizer.cc. InputArgExpansion and +// OutputArgExpansion already support lists of tensors, but that's pretty much +// it, all other code is written with assumption that expansions are always of +// size 1. MakeGrapplerFunctionItem will gracefully fail with Status error. +// +// This is a low priority feature, because in practice we don't see a lot (any +// at all?) functions with such arguments. Tensorflow-Eager always produces +// functions with plain input/output arguments. + +// TODO(ezhulenev): Support inputs and outputs of type 'T*N'. +// TODO(ezhulenev): Support inputs and outputs of type 'list(type)'. + // Depending on the function instantiation attributes, input argument to the // function might be a single tensor, list of tensors of the same type, or a // list of tensors of different types. @@ -39,30 +55,23 @@ namespace grappler { // InputArgExpansion keeps track of the placeholders that were added to the // function body in place of function inputs and a resolved input data type. struct InputArgExpansion { - // TODO(ezhulenev): Add support for functions with tensor sequence inputs of - // different data types. - // TODO(ezhulenev): Support type parametrized inputs? - string input_name; // name of the function input argument - DataType data_type; // input data type - bool is_ref; // if true, inputs are required to be refs - std::vector placeholders; // names of placeholder nodes in the - // function body + string input_name; + DataType data_type; + bool is_ref; + absl::InlinedVector placeholders; }; // Depending on the function instantiation attributes, output argument is mapped // to one or more outputs of one of the function body nodes. // -// OutputArgExpansion keeps mapping from a function output arg to the output -// tensors of a function body nodes and a resolved output data type +// OutputArgExpansion keeps track of the Identity nodes that were added to the +// function body to forward output tensors. Adding these output nodes allows +// nested function inlining and specialization (see function optimizer). struct OutputArgExpansion { - // TODO(ezhulenev): Add support for functions with tensor sequence outputs of - // different data types. - // TODO(ezhulenev): Support type parametrized outputs? - string output_name; // name of the function output argument - DataType data_type; // output data type - bool is_ref; // if true, outputs are refs - std::vector output_tensors; // names of output tensor from the - // function body nodes + string output_name; + DataType data_type; + bool is_ref; + absl::InlinedVector output_nodes; }; // FunctionDef uses different connectivity encoding for the function body nodes, @@ -87,7 +96,7 @@ class GrapplerFunctionConnectivity { // When expanding inputs in function def format, single input might be // expanded into multiple tensors. When converting back to the function def // format from graph def format, it's always a 1-to-1 relationship. - // FunctionDef built from GrapplerFunctionItem is always specialized to it's + // FunctionDef built from GrapplerFunctionItem is always specialized to its // instantiation attributes and length of input args (and node def outputs) is // known. @@ -144,8 +153,6 @@ class GrapplerFunctionItem : public GrapplerItem { const string& description() const; - bool IsInputPlaceholder(const string& node_name) const; - const std::vector& inputs() const; const InputArgExpansion& input(int i) const; const std::size_t input_size() const; @@ -168,10 +175,9 @@ class GrapplerFunctionItem : public GrapplerItem { GrapplerFunctionItem*); friend Status ReplaceInputWithConst(const NodeDef&, int, GrapplerFunctionItem*); - friend Status RemoveUnusedOutputs( - const absl::flat_hash_set& active_outputs, - GrapplerFunctionItem* item, - std::vector>* output_mapping); + friend Status RemoveFunctionOutputs(const absl::flat_hash_set&, + GrapplerFunctionItem*, + std::vector>*); GrapplerFunctionItem(string func_name, string description, AttrSlice func_attr, @@ -187,16 +193,14 @@ class GrapplerFunctionItem : public GrapplerItem { std::vector input_arg_expansions_; std::vector output_arg_expansions_; - std::set input_arg_placeholders_; - bool is_stateful_ = false; }; // Check if function input/output types are fully defined only at instantiation -// time (parametrized by it's instantiation node). +// time (parametrized by its instantiation node). bool HasParametrizedType(const FunctionDef& func); -// Check if a function body is parametrized by it's instantiation node. Function +// Check if a function body is parametrized by its instantiation node. Function // body is parametrized, if it has at least one node with a 'placeholder' // attribute. bool HasParametrizedBody(const FunctionDef& func); @@ -228,15 +232,16 @@ Status RegisterGrapplerFunctionConnectivity( Status ReplaceInputWithConst(const NodeDef& input_const, int input_index, GrapplerFunctionItem* item); -// Remove function output arguments that do not have any active outputs (output -// tensor connected to other node inputs or in a fetch set). Active outputs uses -// GraphDef output position encoding, and multiple active outputs could -// potentially be connected to the same output argument (in case of tensor list -// outputs). Add output mapping for all active outputs that changed it's output -// position (std::pair). -Status RemoveUnusedOutputs(const absl::flat_hash_set& active_outputs, - GrapplerFunctionItem* item, - std::vector>* output_mapping); +// Removes outputs from instantiated grappler function item. Function node +// outputs use GraphDef output index encoding, and multiple outputs might belong +// to the same output argument expansion (in case of tensor list outputs). For +// all active function outputs that changed its output index, this function adds +// an output mapping (std::pair). +Status RemoveFunctionOutputs(const absl::flat_hash_set& remove_outputs, + GrapplerFunctionItem* item, + std::vector>* output_mapping); + +// TODO(ezhulennev, b/120103818): Add RemoveFunctionInputs. // Make a GrapplerFunctionItem from the function definition and function // instantiation attributes (caller node attributes). Returns error if the given @@ -251,7 +256,7 @@ Status MakeGrapplerFunctionItem(const FunctionDef& func, // fully defined (no type or body parametrization). // TODO(ezhulenev): Support parametrized functions without fully defined // instantiation attributes? Do we ever want to optimize parametrized function -// without specializing it to it's instantiation attributes (at least types)? +// without specializing it to its instantiation attributes (at least types)? Status MakeGrapplerFunctionItem(const FunctionDef& func, const FunctionLibraryDefinition& flib, int graph_def_version, diff --git a/tensorflow/core/grappler/utils/functions_test.cc b/tensorflow/core/grappler/utils/functions_test.cc index 29d6100d23..c49920c79c 100644 --- a/tensorflow/core/grappler/utils/functions_test.cc +++ b/tensorflow/core/grappler/utils/functions_test.cc @@ -249,15 +249,16 @@ TEST_F(FunctionsTest, FromSimpleFunctionDef) { flib, TF_GRAPH_DEF_VERSION, &item)); EXPECT_EQ("XTimesTwo", item.id); - EXPECT_EQ(4, item.function_body().node_size()); + EXPECT_EQ(5, item.function_body().node_size()); EXPECT_EQ(1, item.input_size()); EXPECT_EQ("x", item.input(0).input_name); - EXPECT_EQ(std::vector{"x"}, item.input(0).placeholders); + ASSERT_EQ(1, item.input(0).placeholders.size()); + EXPECT_EQ("x", item.input(0).placeholders[0]); EXPECT_EQ(1, item.output_size()); EXPECT_EQ("y", item.output(0).output_name); - EXPECT_EQ("y", item.output(0).output_tensors[0]); + EXPECT_EQ("y_output_node_0", item.output(0).output_nodes[0]); int count = 0; for (const NodeDef &node : item.function_body().node()) { @@ -279,9 +280,13 @@ TEST_F(FunctionsTest, FromSimpleFunctionDef) { EXPECT_EQ(2, node.input_size()); EXPECT_EQ("x", node.input(0)); EXPECT_EQ("scale", node.input(1)); + } else if (node.name() == "y_output_node_0" && ++count) { + EXPECT_EQ("Identity", node.op()); + ASSERT_EQ(1, node.input_size()); + EXPECT_EQ("y", node.input(0)); } } - EXPECT_EQ(4, count); + EXPECT_EQ(5, count); } TEST_F(FunctionsTest, FromFunctionDefWithMultiOutputNodes) { @@ -326,7 +331,7 @@ TEST_F(FunctionsTest, FromFunctionDefWithMultiOutputNodes) { flib, TF_GRAPH_DEF_VERSION, &item)); EXPECT_EQ("SubGrad", item.id); - EXPECT_EQ(12, item.function_body().node_size()); + EXPECT_EQ(14, item.function_body().node_size()); ASSERT_EQ(3, item.input_size()); EXPECT_EQ("x", item.input(0).input_name); @@ -334,8 +339,8 @@ TEST_F(FunctionsTest, FromFunctionDefWithMultiOutputNodes) { EXPECT_EQ("dz", item.input(2).input_name); ASSERT_EQ(2, item.output_size()); - EXPECT_EQ("dx", item.output(0).output_tensors[0]); - EXPECT_EQ("dy", item.output(1).output_tensors[0]); + EXPECT_EQ("dx_output_node_0", item.output(0).output_nodes[0]); + EXPECT_EQ("dy_output_node_0", item.output(1).output_nodes[0]); int count = 0; for (const NodeDef &node : item.function_body().node()) { @@ -359,9 +364,17 @@ TEST_F(FunctionsTest, FromFunctionDefWithMultiOutputNodes) { EXPECT_EQ(2, node.input_size()); EXPECT_EQ("gy", node.input(0)); EXPECT_EQ("rx:1", node.input(1)); + } else if (node.name() == "dx_output_node_0" && ++count) { + EXPECT_EQ("Identity", node.op()); + ASSERT_EQ(1, node.input_size()); + EXPECT_EQ("dx", node.input(0)); + } else if (node.name() == "dy_output_node_0" && ++count) { + EXPECT_EQ("Identity", node.op()); + ASSERT_EQ(1, node.input_size()); + EXPECT_EQ("dy", node.input(0)); } } - EXPECT_EQ(6, count); + EXPECT_EQ(8, count); } TEST_F(FunctionsTest, FromFunctionDefWithNestedFuncs) { @@ -472,7 +485,7 @@ TEST_F(FunctionsTest, FromFunctionDefWithOutputMappings) { flib, TF_GRAPH_DEF_VERSION, &item)); EXPECT_EQ(1, item.output_size()); - EXPECT_EQ("Exp", item.output(0).output_tensors[0]); + EXPECT_EQ("out_output_node_0", item.output(0).output_nodes[0]); int count = 0; for (const NodeDef &node : item.function_body().node()) { @@ -488,9 +501,13 @@ TEST_F(FunctionsTest, FromFunctionDefWithOutputMappings) { EXPECT_EQ("Exp", node.op()); EXPECT_EQ(1, node.input_size()); EXPECT_EQ("Linear_func", node.input(0)); + } else if (node.name() == "out_output_node_0" && ++count) { + EXPECT_EQ("Identity", node.op()); + ASSERT_EQ(1, node.input_size()); + EXPECT_EQ("Exp", node.input(0)); } } - EXPECT_EQ(3, count); + EXPECT_EQ(4, count); } TEST_F(FunctionsTest, FromFunctionDefWithInputForwarding) { @@ -517,27 +534,44 @@ TEST_F(FunctionsTest, FromFunctionDefWithInputForwarding) { flib, TF_GRAPH_DEF_VERSION, &item)); EXPECT_EQ("ForwardInputs", item.id); - EXPECT_EQ(5, item.function_body().node_size()); + EXPECT_EQ(8, item.function_body().node_size()); EXPECT_EQ(3, item.output_size()); - EXPECT_EQ("in0", item.output(0).output_tensors[0]); - EXPECT_EQ("arg2", item.output(1).output_tensors[0]); - EXPECT_EQ("arg3", item.output(2).output_tensors[0]); + EXPECT_EQ("out0_output_node_0", item.output(0).output_nodes[0]); + EXPECT_EQ("arg2_output_node_0", item.output(1).output_nodes[0]); + EXPECT_EQ("arg3_output_node_0", item.output(2).output_nodes[0]); int count = 0; + + const auto is_arg_placeholder = [](const string &name) { + return name == "in0" || name == "in1" || name == "arg2" || name == "arg3" || + name == "arg4"; + }; + for (const NodeDef &node : item.function_body().node()) { - EXPECT_TRUE(node.name() == "in0" || node.name() == "in1" || - node.name() == "arg2" || node.name() == "arg3" || - node.name() == "arg4"); - count++; - EXPECT_EQ("Placeholder", node.op()); - if (node.name() == "arg3") { - EXPECT_EQ(DT_INT32, node.attr().at("dtype").type()); - } else { - EXPECT_EQ(DT_FLOAT, node.attr().at("dtype").type()); + if (is_arg_placeholder(node.name()) && node.op() == "Placeholder") { + count++; + if (node.name() == "arg3") { + EXPECT_EQ(DT_INT32, node.attr().at("dtype").type()); + } else { + EXPECT_EQ(DT_FLOAT, node.attr().at("dtype").type()); + } + continue; + } + + EXPECT_EQ("Identity", node.op()); + ASSERT_EQ(1, node.input_size()); + EXPECT_TRUE(is_arg_placeholder(node.input(0))); + + if (node.name() == "out0_output_node_0" && ++count) { + EXPECT_EQ("in0", node.input(0)); + } else if (node.name() == "arg2_output_node_0" && ++count) { + EXPECT_EQ("arg2", node.input(0)); + } else if (node.name() == "arg3_output_node_0" && ++count) { + EXPECT_EQ("arg3", node.input(0)); } } - EXPECT_EQ(5, count); + EXPECT_EQ(8, count); } TEST_F(FunctionsTest, FromFunctionDefWithoutInput) { @@ -566,16 +600,22 @@ TEST_F(FunctionsTest, FromFunctionDefWithoutInput) { EXPECT_EQ(0, item.input_size()); EXPECT_EQ(1, item.output_size()); - EXPECT_EQ("o", item.output(0).output_tensors[0]); + EXPECT_EQ("o_output_node_0", item.output(0).output_nodes[0]); + EXPECT_EQ(3, item.function_body().node_size()); - EXPECT_EQ(2, item.function_body().node_size()); const NodeDef &two = item.function_body().node(0); EXPECT_EQ("two", two.name()); EXPECT_EQ(0, two.input_size()); + const NodeDef &cast = item.function_body().node(1); EXPECT_EQ("o", cast.name()); EXPECT_EQ(1, cast.input_size()); EXPECT_EQ("two", cast.input(0)); + + const NodeDef &retval = item.function_body().node(2); + EXPECT_EQ("o_output_node_0", retval.name()); + EXPECT_EQ(1, retval.input_size()); + EXPECT_EQ("o", retval.input(0)); } TEST_F(FunctionsTest, FromFunctionDefWithSideEffectfulOps) { @@ -674,7 +714,7 @@ TEST_F(FunctionsTest, ReplaceInputWithConst) { EXPECT_EQ(2, item.input_size()); EXPECT_EQ(1, item.output_size()); - ASSERT_EQ(3, item.function_body().node_size()); + ASSERT_EQ(4, item.function_body().node_size()); const NodeDef &input_x = item.function_body().node(0); const NodeDef &input_y = item.function_body().node(1); @@ -748,8 +788,9 @@ TEST_F(FunctionsTest, SwapFunctionBodyAndMakeFunctionDef) { {{"z", "output:z:0"}}); GraphDef id_func_body = test::function::GDef( - {/* pass input to output through identity */ - NDef("output", "Identity", {"x"}, {{"T", "float"}})}); + {/* Read and return input argument through Identity node. */ + NDef("read_x", "Identity", {"x"}, {{"T", "float"}}), + NDef("z_output_node_0", "Identity", {"read_x"}, {{"T", "float"}})}); protobuf::Map func_instantiation_attr; func_instantiation_attr["T"].set_type(DT_FLOAT); @@ -772,15 +813,15 @@ TEST_F(FunctionsTest, SwapFunctionBodyAndMakeFunctionDef) { // Check that graph body was updated. int count = 0; for (const NodeDef &node : specialized.node_def()) { - if (node.name() == "output" && ++count) { + if (node.name() == "read_x" && ++count) { EXPECT_EQ("Identity", node.op()); EXPECT_EQ("x:0", node.input(0)); } } EXPECT_EQ(1, count); - // And return tensor mapping was updated with a new output name (z->output). - EXPECT_EQ("output:output:0", (*specialized.mutable_ret())["z"]); + // And return tensor mapping was updated with a new output name (z->read_x). + EXPECT_EQ("read_x:output:0", (*specialized.mutable_ret())["z"]); } TEST_F(FunctionsTest, FunctionDefGrapplerFunctionItemRoundTrip) { diff --git a/tensorflow/core/grappler/utils/grappler_test.cc b/tensorflow/core/grappler/utils/grappler_test.cc index 5ef1cf444b..1b4b9f9a51 100644 --- a/tensorflow/core/grappler/utils/grappler_test.cc +++ b/tensorflow/core/grappler/utils/grappler_test.cc @@ -14,7 +14,9 @@ limitations under the License. ==============================================================================*/ #include "tensorflow/core/grappler/utils/grappler_test.h" + #include + #include "absl/algorithm/container.h" #include "tensorflow/core/framework/attr_value_util.h" #include "tensorflow/core/grappler/utils.h" @@ -25,6 +27,46 @@ limitations under the License. namespace tensorflow { namespace grappler { +namespace { +void CompareGraphNodes(protobuf::RepeatedPtrField* want, + protobuf::RepeatedPtrField* got) { + auto comparator = [](const NodeDef& n1, const NodeDef& n2) -> bool { + return n1.name() < n2.name(); + }; + + std::sort(want->begin(), want->end(), comparator); + std::sort(got->begin(), got->end(), comparator); + + ASSERT_EQ(want->size(), got->size()); + + for (int i = 0; i < want->size(); ++i) { + NodeDef& want_node = (*want)[i]; + NodeDef& got_node = (*got)[i]; + + EXPECT_EQ(want_node.op(), got_node.op()); + EXPECT_EQ(want_node.name(), got_node.name()); + EXPECT_EQ(want_node.device(), got_node.device()); + ASSERT_EQ(want_node.input_size(), got_node.input_size()); + + // Order of control dependencies doesn't matter, so we sort them first. + const auto is_control = [](const string& input) -> bool { + return ParseTensorName(input).index() < 0; + }; + + auto want_inputs = want_node.mutable_input(); + auto got_inputs = got_node.mutable_input(); + std::sort(absl::c_find_if(*want_inputs, is_control), want_inputs->end()); + std::sort(absl::c_find_if(*got_inputs, is_control), got_inputs->end()); + + for (int j = 0; j < want_node.input_size(); ++j) { + const TensorId want_tensor = ParseTensorName(want_node.input(j)); + const TensorId got_tensor = ParseTensorName(got_node.input(j)); + EXPECT_EQ(want_tensor.ToString(), got_tensor.ToString()); + } + } +} +} // namespace + GrapplerTest::GrapplerTest() { // Turn off all the automatic optimizations to ensure that we run the graph // exactly as it is given to us. This ensures that we can compare the results @@ -96,35 +138,11 @@ NodeDef* GrapplerTest::AddNode( } void GrapplerTest::CompareGraphs(GraphDef want, GraphDef got) const { - auto comparator = [](const NodeDef& n1, const NodeDef& n2) -> bool { - return n1.name() < n2.name(); - }; - std::sort(want.mutable_node()->begin(), want.mutable_node()->end(), - comparator); - std::sort(got.mutable_node()->begin(), got.mutable_node()->end(), comparator); - - for (int i = 0; i < want.node_size(); ++i) { - std::sort(want.mutable_node(i)->mutable_input()->begin(), - want.mutable_node(i)->mutable_input()->end()); - } - for (int i = 0; i < got.node_size(); ++i) { - std::sort(got.mutable_node(i)->mutable_input()->begin(), - got.mutable_node(i)->mutable_input()->end()); - } - - ASSERT_EQ(want.node_size(), got.node_size()); - for (int i = 0; i < want.node_size(); ++i) { - EXPECT_EQ(want.node(i).op(), got.node(i).op()); - EXPECT_EQ(want.node(i).name(), got.node(i).name()); - EXPECT_EQ(want.node(i).device(), got.node(i).device()); + CompareGraphNodes(want.mutable_node(), got.mutable_node()); +} - ASSERT_EQ(want.node(i).input_size(), got.node(i).input_size()); - for (int j = 0; j < want.node(i).input_size(); ++j) { - const TensorId want_tensor = ParseTensorName(want.node(i).input(j)); - const TensorId got_tensor = ParseTensorName(got.node(i).input(j)); - EXPECT_EQ(want_tensor.ToString(), got_tensor.ToString()); - } - } +void GrapplerTest::CompareFunctions(FunctionDef want, FunctionDef got) const { + CompareGraphNodes(want.mutable_node_def(), got.mutable_node_def()); } void GrapplerTest::CompareNodes(const NodeDef& want, const NodeDef& got) const { diff --git a/tensorflow/core/grappler/utils/grappler_test.h b/tensorflow/core/grappler/utils/grappler_test.h index 20fd04c1c6..26c1db3740 100644 --- a/tensorflow/core/grappler/utils/grappler_test.h +++ b/tensorflow/core/grappler/utils/grappler_test.h @@ -62,6 +62,13 @@ class GrapplerTest : public ::testing::Test { // equality, and adds all failuires to the current test. void CompareNodes(const NodeDef& want, const NodeDef& got) const; + // Checks if two functions are equal. Both functions must have the same set of + // nodes with the same inputs and attributes. Nodes can be in different order. + // + // NOTE: This function uses EXPECT/ASSERT macros to check node properties + // equality, and adds all failures to the current test. + void CompareFunctions(FunctionDef want, FunctionDef got) const; + // Checks if node 'src' is directly connected to the input($position) of // 'dst'. bool IsNodesDirectlyConnected(const NodeMap& node_map, const string& src, -- GitLab From 0a88318ac73a3d30dfee4ab1541070cbf82fe05c Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Wed, 9 Jan 2019 14:04:07 -0800 Subject: [PATCH 577/622] Add `tf.distribute.Strategy.experimental_make_numpy_iterator()` function. PiperOrigin-RevId: 228584021 --- tensorflow/contrib/distribute/python/BUILD | 4 + .../python/collective_all_reduce_strategy.py | 8 +- .../collective_all_reduce_strategy_test.py | 7 ++ .../contrib/distribute/python/keras_test.py | 58 +++------ .../distribute/python/mirrored_strategy.py | 30 +++++ .../python/mirrored_strategy_multigpu_test.py | 3 + .../distribute/python/one_device_strategy.py | 9 +- .../python/one_device_strategy_test.py | 4 + .../python/parameter_server_strategy.py | 31 +++++ .../python/parameter_server_strategy_test.py | 12 ++ .../distribute/python/strategy_test_lib.py | 29 +++++ .../contrib/distribute/python/tpu_strategy.py | 9 ++ tensorflow/python/distribute/BUILD | 32 +++++ .../python/distribute/distribute_lib.py | 72 +++++++++++- .../python/distribute/mirrored_strategy.py | 10 ++ tensorflow/python/distribute/numpy_dataset.py | 97 +++++++++++++++ .../python/distribute/numpy_dataset_test.py | 44 +++++++ .../distribute/parameter_server_strategy.py | 13 +- .../engine/distributed_training_utils.py | 111 +----------------- tensorflow/python/keras/engine/training.py | 62 +++++----- .../keras/engine/training_distributed.py | 6 - tensorflow/python/training/optimizer.py | 13 +- ...orflow.distribute.-mirrored-strategy.pbtxt | 4 + ...orflow.distribute.-strategy-extended.pbtxt | 4 + .../v1/tensorflow.distribute.-strategy.pbtxt | 4 + ...orflow.distribute.-mirrored-strategy.pbtxt | 4 + ...orflow.distribute.-strategy-extended.pbtxt | 4 + .../v2/tensorflow.distribute.-strategy.pbtxt | 4 + 28 files changed, 490 insertions(+), 198 deletions(-) create mode 100644 tensorflow/python/distribute/numpy_dataset.py create mode 100644 tensorflow/python/distribute/numpy_dataset_test.py diff --git a/tensorflow/contrib/distribute/python/BUILD b/tensorflow/contrib/distribute/python/BUILD index 7ee12812af..d4758d7518 100644 --- a/tensorflow/contrib/distribute/python/BUILD +++ b/tensorflow/contrib/distribute/python/BUILD @@ -131,6 +131,7 @@ py_library( "//tensorflow/python:math_ops", "//tensorflow/python/distribute:distribute_lib", "//tensorflow/python/distribute:input_lib", + "//tensorflow/python/distribute:numpy_dataset", "//tensorflow/python/distribute:reduce_util", "//tensorflow/python/distribute:values", "//tensorflow/python/eager:context", @@ -153,6 +154,7 @@ py_library( "//tensorflow/python/distribute:cross_device_utils", "//tensorflow/python/distribute:input_lib", "//tensorflow/python/distribute:multi_worker_util", + "//tensorflow/python/distribute:numpy_dataset", "//tensorflow/python/distribute:values", "//tensorflow/python/eager:context", ], @@ -178,6 +180,7 @@ py_library( "//tensorflow/python/eager:backprop", "//tensorflow/python/eager:context", "//tensorflow/python/eager:test", + "//third_party/py/numpy", ], ) @@ -303,6 +306,7 @@ py_library( "//tensorflow/python:tensor_util", "//tensorflow/python:util", "//tensorflow/python/distribute:input_lib", + "//tensorflow/python/distribute:numpy_dataset", "//tensorflow/python/distribute:reduce_util", "//tensorflow/python/distribute:values", ], diff --git a/tensorflow/contrib/distribute/python/collective_all_reduce_strategy.py b/tensorflow/contrib/distribute/python/collective_all_reduce_strategy.py index f6361cb6e8..39756b32c5 100644 --- a/tensorflow/contrib/distribute/python/collective_all_reduce_strategy.py +++ b/tensorflow/contrib/distribute/python/collective_all_reduce_strategy.py @@ -28,6 +28,7 @@ from tensorflow.python.distribute import device_util from tensorflow.python.distribute import distribute_lib from tensorflow.python.distribute import input_lib from tensorflow.python.distribute import multi_worker_util +from tensorflow.python.distribute import numpy_dataset from tensorflow.python.distribute import values from tensorflow.python.eager import context from tensorflow.python.framework import ops @@ -86,6 +87,7 @@ class CollectiveAllReduceExtended(mirrored_strategy.MirroredExtended): else: local_devices = ("/device:CPU:0",) self._worker_device = device_util.canonicalize("/device:CPU:0") + self._host_input_device = numpy_dataset.SingleDevice(self._worker_device) self._collective_keys = cross_device_utils.CollectiveKeys() self._initialize_local(local_devices) @@ -121,6 +123,7 @@ class CollectiveAllReduceExtended(mirrored_strategy.MirroredExtended): task_id) self._worker_device = "/job:%s/task:%d" % (task_type, task_id) + self._host_input_device = numpy_dataset.SingleDevice(self._worker_device) if num_gpus_per_worker: local_devices = tuple( "%s/device:GPU:%d" % (self._worker_device, i) @@ -157,6 +160,9 @@ class CollectiveAllReduceExtended(mirrored_strategy.MirroredExtended): if colocate_with is None: device_map = self._device_map logical_device = 0 # TODO(josh11b): Get logical device from scope here. + elif isinstance(colocate_with, numpy_dataset.SingleDevice): + with ops.device(colocate_with.device): + return next_creator(*args, **kwargs) else: device_map = colocate_with.device_map logical_device = colocate_with.logical_device @@ -347,4 +353,4 @@ class CollectiveAllReduceExtended(mirrored_strategy.MirroredExtended): # TODO(priyag): Delete this once all strategies use global batch size. @property def _global_batch_size(self): - return False + return True diff --git a/tensorflow/contrib/distribute/python/collective_all_reduce_strategy_test.py b/tensorflow/contrib/distribute/python/collective_all_reduce_strategy_test.py index 4c8c01a216..c3e9c55e96 100644 --- a/tensorflow/contrib/distribute/python/collective_all_reduce_strategy_test.py +++ b/tensorflow/contrib/distribute/python/collective_all_reduce_strategy_test.py @@ -466,6 +466,13 @@ class LocalCollectiveAllReduceStrategy( with self.cached_session(config=config, target=target): self._test_all_reduce_mean_gradient_tape(distribution) + def testNumpyIterator(self): + num_gpus = 2 + if context.num_gpus() < num_gpus: + self.skipTest('Not enough GPUs') + strategy, _, _ = self._get_test_object(None, None, num_gpus) + self._test_numpy_iterator(strategy) + if __name__ == '__main__': test.main() diff --git a/tensorflow/contrib/distribute/python/keras_test.py b/tensorflow/contrib/distribute/python/keras_test.py index 1cb5fa30a3..40916afcfa 100644 --- a/tensorflow/contrib/distribute/python/keras_test.py +++ b/tensorflow/contrib/distribute/python/keras_test.py @@ -429,15 +429,6 @@ class TestEstimatorDistributionStrategy(test_util.TensorFlowTestCase, class TestDistributionStrategyWithNumpyArrays(test.TestCase, parameterized.TestCase): - @combinations.generate(strategy_for_numpy_input_combinations()) - def test_creating_var_with_numpy_arrays(self, distribution): - with self.cached_session(): - x = np.asarray(np.random.random((64, 3)), dtype=np.float32) - var_x = distributed_training_utils.get_var_for_numpy(distribution, x) - val = self.evaluate(var_x.value()) - # Verify that the numpy value is copied to the variable. - self.assertAllEqual(x, val) - @combinations.generate(strategy_for_numpy_input_combinations()) def test_calculating_input_params_no_steps_no_batch_size(self, distribution): # Calculate the per_replica_batch_size scaling factor for strategies @@ -576,26 +567,26 @@ class TestDistributionStrategyWithNumpyArrays(test.TestCase, metrics = ['mae'] model.compile(optimizer, loss, metrics=metrics) - inputs = np.zeros((64, 3), dtype=np.float32) - targets = np.zeros((64, 4), dtype=np.float32) + inputs = np.zeros((64, 3), dtype=np.float32) + targets = np.zeros((64, 4), dtype=np.float32) - # Call fit with validation data - model.fit(inputs, targets, epochs=1, batch_size=2, verbose=0, - validation_data=(inputs, targets)) + # Call fit with validation data + model.fit(inputs, targets, epochs=1, batch_size=2, verbose=0, + validation_data=(inputs, targets)) - # TODO(anjalisridhar): We need tests for when the batch size and steps are - # smaller and results in a 0 batch_size and steps value. - model.evaluate(inputs, targets) - # with steps - model.evaluate(inputs, targets, steps=2) - # with batch_size - model.evaluate(inputs, targets, batch_size=8) + # TODO(anjalisridhar): We need tests for when the batch size and steps + # are smaller and results in a 0 batch_size and steps value. + model.evaluate(inputs, targets) + # with steps + model.evaluate(inputs, targets, steps=2) + # with batch_size + model.evaluate(inputs, targets, batch_size=8) - model.predict(inputs) - # with steps - model.predict(inputs, steps=2) - # with batch_size - model.predict(inputs, batch_size=8) + model.predict(inputs) + # with steps + model.predict(inputs, steps=2) + # with batch_size + model.predict(inputs, batch_size=8) @combinations.generate(strategy_for_numpy_input_combinations()) def test_calling_model_with_nested_numpy_arrays(self, distribution): @@ -1192,21 +1183,6 @@ class TestDistributionStrategyValidation(test.TestCase, metrics = ['mae', keras.metrics.CategoricalAccuracy()] model.compile(optimizer, loss, metrics=metrics) - @combinations.generate(all_strategy_combinations_minus_default()) - def test_loop_in_scope(self, distribution): - with self.cached_session(): - with self.assertRaisesRegexp( - RuntimeError, 'should not be run inside the tf.distribute.Strategy'): - with distribution.scope(): - x = keras.layers.Input(shape=(3,), name='input') - y = keras.layers.Dense(4, name='dense')(x) - model = keras.Model(x, y) - optimizer = gradient_descent.GradientDescentOptimizer(0.001) - loss = 'mse' - model.compile(optimizer, loss) - input_array = np.zeros((3, 3), dtype=np.float32) - model.predict(input_array) - if __name__ == '__main__': test.main() diff --git a/tensorflow/contrib/distribute/python/mirrored_strategy.py b/tensorflow/contrib/distribute/python/mirrored_strategy.py index e3ab2bf19e..5fa36fb402 100644 --- a/tensorflow/contrib/distribute/python/mirrored_strategy.py +++ b/tensorflow/contrib/distribute/python/mirrored_strategy.py @@ -104,6 +104,36 @@ class MirroredStrategy(distribute_lib.DistributionStrategy): auto_shard_dataset) super(MirroredStrategy, self).__init__(extended) + # Override to change the documentation to reflect the different handling of + # global vs. local batch size between core and contrib. + def experimental_make_numpy_iterator( # pylint: disable=useless-super-delegation + self, numpy_input, batch_size, num_epochs=1, shuffle=1024, session=None): + """Makes an iterator for input provided via a nest of numpy arrays. + + NOTE: The `batch_size` argument here has different behavior for this + contrib version of `MirroredStrategy`. + + Args: + numpy_input: A nest of NumPy input arrays that will be distributed evenly + across all replicas. + batch_size: The number of entries from the array we should consume in one + step of the computation, across all replicas. This is the per-replica + batch size. The global batch size will be this times + `num_replicas_in_sync`. + num_epochs: The number of times to iterate through the examples. A value + of `None` means repeat forever. + shuffle: Size of buffer to use for shuffling the input examples. + Use `None` to disable shuffling. + session: (TensorFlow v1.x graph execution only) A session used for + initialization. + + Returns: + An `tf.distribute.InputIterator` which returns inputs for each step of the + computation. User should call `initialize` on the returned iterator. + """ + return super(MirroredStrategy, self).experimental_make_numpy_iterator( + numpy_input, batch_size, num_epochs, shuffle, session) + class MirroredExtended(CoreMirroredExtended): """Implementation of (contrib) MirroredStrategy.""" diff --git a/tensorflow/contrib/distribute/python/mirrored_strategy_multigpu_test.py b/tensorflow/contrib/distribute/python/mirrored_strategy_multigpu_test.py index 9821828b2d..59d711ae01 100644 --- a/tensorflow/contrib/distribute/python/mirrored_strategy_multigpu_test.py +++ b/tensorflow/contrib/distribute/python/mirrored_strategy_multigpu_test.py @@ -116,6 +116,9 @@ class MirroredTwoDeviceDistributionTest( self._test_input_fn_iterator(iterator, distribution.extended.worker_devices, expected_values) + def testNumpyIterator(self, distribution): + self._test_numpy_iterator(distribution) + def testGlobalStepUpdate(self, distribution): self._test_global_step_update(distribution) diff --git a/tensorflow/contrib/distribute/python/one_device_strategy.py b/tensorflow/contrib/distribute/python/one_device_strategy.py index fb470f8546..34b0c31087 100644 --- a/tensorflow/contrib/distribute/python/one_device_strategy.py +++ b/tensorflow/contrib/distribute/python/one_device_strategy.py @@ -21,6 +21,7 @@ from __future__ import print_function from tensorflow.python.distribute import device_util from tensorflow.python.distribute import distribute_lib from tensorflow.python.distribute import input_lib +from tensorflow.python.distribute import numpy_dataset from tensorflow.python.distribute import values from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes @@ -50,8 +51,8 @@ class OneDeviceExtended(distribute_lib.DistributionStrategyExtended): super(OneDeviceExtended, self).__init__(container_strategy) self._device = device self._default_device = device - worker = device_util.canonicalize("/device:CPU:0") - worker_device_pairs = [(worker, [self._device])] + self._input_device = device_util.canonicalize("/device:CPU:0") + worker_device_pairs = [(self._input_device, [self._device])] device_map = values.SingleDeviceMap(device) self._input_workers = input_lib.InputWorkers( device_map, worker_device_pairs) @@ -82,6 +83,10 @@ class OneDeviceExtended(distribute_lib.DistributionStrategyExtended): return input_lib.InputFunctionIterator( input_fn, self._input_workers, [distribute_lib.InputContext()]) + def _experimental_make_numpy_dataset(self, numpy_input, session): + return numpy_dataset.one_host_numpy_dataset( + numpy_input, self._input_device, session) + def _broadcast_to(self, tensor, destinations): del destinations return tensor diff --git a/tensorflow/contrib/distribute/python/one_device_strategy_test.py b/tensorflow/contrib/distribute/python/one_device_strategy_test.py index 2403dc8f12..f81466a6c7 100644 --- a/tensorflow/contrib/distribute/python/one_device_strategy_test.py +++ b/tensorflow/contrib/distribute/python/one_device_strategy_test.py @@ -59,6 +59,10 @@ class OneDeviceStrategyTest( self._test_input_fn_iterator( iterator, d.extended.worker_devices, expected_values) + @test_util.run_in_graph_and_eager_modes + def testNumpyIterator(self): + self._test_numpy_iterator(self._get_distribution_strategy()) + def testAllReduceSum(self): self._test_all_reduce_sum(self._get_distribution_strategy()) diff --git a/tensorflow/contrib/distribute/python/parameter_server_strategy.py b/tensorflow/contrib/distribute/python/parameter_server_strategy.py index bb0b8eb992..0785427c2c 100644 --- a/tensorflow/contrib/distribute/python/parameter_server_strategy.py +++ b/tensorflow/contrib/distribute/python/parameter_server_strategy.py @@ -89,6 +89,37 @@ class ParameterServerStrategy(distribute_lib.DistributionStrategy): super(ParameterServerStrategy, self).__init__( ParameterServerExtended(self, num_gpus_per_worker)) + # Override to change the documentation to reflect the different handling of + # global vs. local batch size between core and contrib. + def experimental_make_numpy_iterator( # pylint: disable=useless-super-delegation + self, numpy_input, batch_size, num_epochs=1, shuffle=1024, session=None): + """Makes an iterator for input provided via a nest of numpy arrays. + + NOTE: The `batch_size` argument here has different behavior for this + contrib version of `ParameterServerStrategy`. + + Args: + numpy_input: A nest of NumPy input arrays that will be distributed evenly + across all replicas. + batch_size: The number of entries from the array we should consume in one + step of the computation, across all replicas. This is the per-replica + batch size. The global batch size will be this times + `num_replicas_in_sync`. + num_epochs: The number of times to iterate through the examples. A value + of `None` means repeat forever. + shuffle: Size of buffer to use for shuffling the input examples. + Use `None` to disable shuffling. + session: (TensorFlow v1.x graph execution only) A session used for + initialization. + + Returns: + An `tf.distribute.InputIterator` which returns inputs for each step of the + computation. User should call `initialize` on the returned iterator. + """ + return super(ParameterServerStrategy, + self).experimental_make_numpy_iterator( + numpy_input, batch_size, num_epochs, shuffle, session) + class ParameterServerExtended(CoreParameterServerExtended): """Implementation of ParameterServerStrategy.""" diff --git a/tensorflow/contrib/distribute/python/parameter_server_strategy_test.py b/tensorflow/contrib/distribute/python/parameter_server_strategy_test.py index 7836687e7d..802809e7c7 100644 --- a/tensorflow/contrib/distribute/python/parameter_server_strategy_test.py +++ b/tensorflow/contrib/distribute/python/parameter_server_strategy_test.py @@ -893,5 +893,17 @@ class ParameterServerStrategyWithChiefTest(ParameterServerStrategyTestBase, strategy.extended.call_for_each_replica(f) +class LocalParameterServerStrategyTest(strategy_test_lib.DistributionTestBase, + parameterized.TestCase): + + @combinations.generate(combinations.combine(mode=['graph', 'eager'], + use_core_strategy=[True, False], + required_gpus=2)) + def testNumpyIterator(self, use_core_strategy): + strategy, _, _ = create_test_objects( + num_gpus=2, use_core_strategy=use_core_strategy) + self._test_numpy_iterator(strategy) + + if __name__ == '__main__': test.main() diff --git a/tensorflow/contrib/distribute/python/strategy_test_lib.py b/tensorflow/contrib/distribute/python/strategy_test_lib.py index 4fbd630cf7..7455cbd02a 100644 --- a/tensorflow/contrib/distribute/python/strategy_test_lib.py +++ b/tensorflow/contrib/distribute/python/strategy_test_lib.py @@ -18,6 +18,8 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import numpy as np + from tensorflow.core.protobuf import config_pb2 from tensorflow.python.data.ops import dataset_ops from tensorflow.python.distribute import distribution_strategy_context as ds_context @@ -295,6 +297,33 @@ class DistributionTestBase(test.TestCase): global_step_values = self.evaluate(global_step_tensors) self.assertEqual((1,) * len(global_step_tensors), global_step_values) + def _test_numpy_iterator(self, strategy): + with strategy.scope(), self.cached_session() as sess: + x = np.asarray([[1, 2], [6, 12], [2, 4], + [5, 10], [3, 6], [4, 8]]) + y = np.asarray([5, 4, 3, 2, 1, 0]) + batch_size = 6 + if not strategy.extended._global_batch_size: # pylint: disable=protected-access + batch_size = batch_size // strategy.num_replicas_in_sync + i = strategy.experimental_make_numpy_iterator( + (x, y), batch_size=batch_size, num_epochs=2, shuffle=None, + session=sess) + self.evaluate(i.initialize()) + + def run_and_concatenate(strategy, i): + x, y = strategy.experimental_run(lambda z: z, i) + x, y = self.evaluate((strategy.unwrap(x), strategy.unwrap(y))) + return np.concatenate(x), np.concatenate(y) + + x_1, y_1 = run_and_concatenate(strategy, i) + self.assertAllEqual(x, x_1) + self.assertAllEqual(y, y_1) + x_2, y_2 = run_and_concatenate(strategy, i) + self.assertAllEqual(x, x_2) + self.assertAllEqual(y, y_2) + with self.assertRaises(errors.OutOfRangeError): + run_and_concatenate(strategy, i) + class OneDeviceDistributionTestBase(test.TestCase): """Some tests that should work with any one-device DistributionStrategy.""" diff --git a/tensorflow/contrib/distribute/python/tpu_strategy.py b/tensorflow/contrib/distribute/python/tpu_strategy.py index 00360bf996..3f89c5869e 100644 --- a/tensorflow/contrib/distribute/python/tpu_strategy.py +++ b/tensorflow/contrib/distribute/python/tpu_strategy.py @@ -34,6 +34,7 @@ from tensorflow.python.distribute import cross_device_ops as cross_device_ops_li from tensorflow.python.distribute import device_util from tensorflow.python.distribute import distribute_lib from tensorflow.python.distribute import input_lib +from tensorflow.python.distribute import numpy_dataset from tensorflow.python.distribute import reduce_util from tensorflow.python.distribute import values from tensorflow.python.distribute.cluster_resolver import tpu_cluster_resolver as resolver_lib @@ -303,6 +304,11 @@ class TPUExtended(distribute_lib.DistributionStrategyExtended): functools.partial(self._call_dataset_fn, dataset_fn), self._input_workers) + def _experimental_make_numpy_dataset(self, numpy_input, session): + return numpy_dataset.one_host_numpy_dataset( + numpy_input, numpy_dataset.SingleDevice(self.get_host_cpu_device(0)), + session) + # TODO(priyag): Deal with OutOfRange errors once b/111349762 is fixed. # TODO(sourabhbajaj): Remove the initial_loop_values parameter when we have # a mechanism to infer the outputs of `fn`. Pending b/110550782. @@ -466,6 +472,9 @@ class TPUExtended(distribute_lib.DistributionStrategyExtended): if colocate_with is None: device_map = self._device_map logical_device = 0 # TODO(josh11b): Get logical device from scope here. + elif isinstance(colocate_with, numpy_dataset.SingleDevice): + with ops.device(colocate_with.device): + return next_creator(*args, **kwargs) else: device_map = colocate_with.device_map logical_device = colocate_with.logical_device diff --git a/tensorflow/python/distribute/BUILD b/tensorflow/python/distribute/BUILD index faaa61934a..a6a1c470b4 100644 --- a/tensorflow/python/distribute/BUILD +++ b/tensorflow/python/distribute/BUILD @@ -124,6 +124,7 @@ py_library( srcs_version = "PY2AND3", deps = [ ":device_util", + ":numpy_dataset", ":reduce_util", "//tensorflow/python:array_ops", "//tensorflow/python:constant_op", @@ -221,6 +222,7 @@ py_library( ":distribute_lib", ":input_lib", ":multi_worker_util", + ":numpy_dataset", ":reduce_util", ":shared_variable_creator", ":values", @@ -249,6 +251,7 @@ py_library( deps = [ ":input_lib", ":mirrored_strategy", + ":numpy_dataset", "//tensorflow/core:protos_all_py", "//tensorflow/python:array_ops", "//tensorflow/python:framework_ops", @@ -276,6 +279,35 @@ py_library( ], ) +py_library( + name = "numpy_dataset", + srcs = ["numpy_dataset.py"], + deps = [ + "//tensorflow/python:array_ops", + "//tensorflow/python:dtypes", + "//tensorflow/python:framework_ops", + "//tensorflow/python:util", + "//tensorflow/python:variable_scope", + "//tensorflow/python/data/ops:dataset_ops", + "//tensorflow/python/eager:context", + "//third_party/py/numpy", + ], +) + +py_test( + name = "numpy_dataset_test", + size = "small", + srcs = ["numpy_dataset_test.py"], + srcs_version = "PY2AND3", + deps = [ + ":numpy_dataset", + "//tensorflow/python:framework_test_lib", + "//tensorflow/python:variable_scope", + "//tensorflow/python/eager:test", + "//third_party/py/numpy", + ], +) + py_library( name = "input_lib", srcs = ["input_lib.py"], diff --git a/tensorflow/python/distribute/distribute_lib.py b/tensorflow/python/distribute/distribute_lib.py index 4ad8cc00b8..31213ab647 100644 --- a/tensorflow/python/distribute/distribute_lib.py +++ b/tensorflow/python/distribute/distribute_lib.py @@ -26,6 +26,7 @@ import enum from tensorflow.python.data.ops import dataset_ops from tensorflow.python.distribute import device_util from tensorflow.python.distribute import distribution_strategy_context +from tensorflow.python.distribute import numpy_dataset from tensorflow.python.distribute import reduce_util from tensorflow.python.eager import context as eager_context from tensorflow.python.framework import constant_op @@ -360,7 +361,7 @@ class DistributionStrategy(object): return self._extended._distribute_dataset(dataset_fn) # pylint: disable=protected-access def make_dataset_iterator(self, dataset): - """Makes an iterator for input provided via input_dataset. + """Makes an iterator for input provided via `dataset`. Data from the given dataset will be distributed evenly across all the compute replicas. We will assume that the input dataset is batched by the @@ -418,6 +419,40 @@ class DistributionStrategy(object): return self.extended._make_input_fn_iterator( # pylint: disable=protected-access input_fn, replication_mode=replication_mode) + def experimental_make_numpy_iterator( + self, numpy_input, batch_size, num_epochs=1, shuffle=1024, session=None): + """Makes an iterator for input provided via a nest of numpy arrays. + + Args: + numpy_input: A nest of NumPy input arrays that will be distributed evenly + across all replicas. Note that lists of Numpy arrays are stacked, + as that is normal `tf.data.Dataset` behavior. + batch_size: The number of entries from the array we should consume in one + step of the computation, across all replicas. This is the global batch + size. It should be divisible by `num_replicas_in_sync`. + num_epochs: The number of times to iterate through the examples. A value + of `None` means repeat forever. + shuffle: Size of buffer to use for shuffling the input examples. + Use `None` to disable shuffling. + session: (TensorFlow v1.x graph execution only) A session used for + initialization. + + Returns: + An `tf.distribute.InputIterator` which returns inputs for each step of the + computation. User should call `initialize` on the returned iterator. + """ + ds = self.extended.experimental_make_numpy_dataset( + numpy_input, session=session) + if shuffle: + ds = ds.shuffle(shuffle) + if num_epochs != 1: + ds = ds.repeat(num_epochs) + # We need to use the drop_remainder argument to get a known static + # input shape which is required for TPUs. + drop_remainder = self.extended.experimental_require_static_shapes + ds = ds.batch(batch_size, drop_remainder=drop_remainder) + return self.make_dataset_iterator(ds) + def experimental_run(self, fn, input_iterator=None): """Runs ops in `fn` on each replica, with inputs from `input_iterator`. @@ -1083,6 +1118,29 @@ class DistributionStrategyExtended(object): def _make_input_fn_iterator(self, input_fn, replication_mode): raise NotImplementedError("must be implemented in descendants") + def experimental_make_numpy_dataset(self, numpy_input, session=None): + """Makes a dataset for input provided via a numpy array. + + This avoids adding `numpy_input` as a large constant in the graph, + and copies the data to the machine or machines that will be processing + the input. + + Args: + numpy_input: A nest of NumPy input arrays that will be distributed evenly + across all replicas. Note that lists of Numpy arrays are stacked, + as that is normal `tf.data.Dataset` behavior. + session: (TensorFlow v1.x graph execution only) A session used for + initialization. + + Returns: + A `tf.data.Dataset` representing `numpy_input`. + """ + _require_cross_replica_context_extended(self) + return self._experimental_make_numpy_dataset(numpy_input, session=session) + + def _experimental_make_numpy_dataset(self, numpy_input, session): + raise NotImplementedError("must be implemented in descendants") + def broadcast_to(self, tensor, destinations): """Mirror a tensor on one device to all worker devices. @@ -1660,6 +1718,18 @@ class _DefaultDistributionExtended(DistributionStrategyExtended): replication_mode=InputReplicationMode.PER_WORKER): return input_fn(InputContext()).make_initializable_iterator() + def _experimental_make_numpy_dataset(self, numpy_input, session): + numpy_flat = nest.flatten(numpy_input) + vars_flat = tuple( + variable_scope.variable(array_ops.zeros(i.shape, i.dtype), + trainable=False, use_resource=True) + for i in numpy_flat + ) + for v, i in zip(vars_flat, numpy_flat): + numpy_dataset.init_var_from_numpy(v, i, session) + vars_nested = nest.pack_sequence_as(numpy_input, vars_flat) + return dataset_ops.Dataset.from_tensor_slices(vars_nested) + def _broadcast_to(self, tensor, destinations): if destinations is None: return tensor diff --git a/tensorflow/python/distribute/mirrored_strategy.py b/tensorflow/python/distribute/mirrored_strategy.py index 37b493d0f7..c0a39d4b55 100644 --- a/tensorflow/python/distribute/mirrored_strategy.py +++ b/tensorflow/python/distribute/mirrored_strategy.py @@ -29,6 +29,7 @@ from tensorflow.python.distribute import device_util from tensorflow.python.distribute import distribute_lib from tensorflow.python.distribute import input_lib from tensorflow.python.distribute import multi_worker_util +from tensorflow.python.distribute import numpy_dataset from tensorflow.python.distribute import reduce_util from tensorflow.python.distribute import shared_variable_creator from tensorflow.python.distribute import values @@ -460,6 +461,7 @@ class MirroredExtended(distribute_lib.DistributionStrategyExtended): self._input_workers = input_lib.InputWorkers(self._device_map) self._inferred_cross_device_ops = cross_device_ops_lib.choose_the_best( devices) + self._host_input_device = numpy_dataset.SingleDevice("/cpu:0") def _initialize_multi_worker(self, devices): """Initializes the object for multi-worker training.""" @@ -488,6 +490,7 @@ class MirroredExtended(distribute_lib.DistributionStrategyExtended): # their ops will end up on the cpu device of its first worker, e.g. # "/job:worker/task:0/device:CPU:0". Note this is not used in replica mode. self._default_device = workers[0] + self._host_input_device = numpy_dataset.SingleDevice(workers[0]) self._device_map = values.ReplicaDeviceMap(devices) self._input_workers = input_lib.InputWorkers( @@ -501,6 +504,9 @@ class MirroredExtended(distribute_lib.DistributionStrategyExtended): if colocate_with is None: device_map = self._device_map logical_device = 0 # TODO(josh11b): Get logical device from scope here. + elif isinstance(colocate_with, numpy_dataset.SingleDevice): + with ops.device(colocate_with.device): + return next_creator(*args, **kwargs) else: device_map = colocate_with.device_map logical_device = colocate_with.logical_device @@ -571,6 +577,10 @@ class MirroredExtended(distribute_lib.DistributionStrategyExtended): return input_lib.InputFunctionIterator( input_fn, self._input_workers, input_contexts) + def _experimental_make_numpy_dataset(self, numpy_input, session): + return numpy_dataset.one_host_numpy_dataset( + numpy_input, self._host_input_device, session) + # TODO(priyag): Deal with OutOfRange errors once b/111349762 is fixed. def _experimental_run_steps_on_iterator(self, fn, iterator, iterations, initial_loop_values=None): diff --git a/tensorflow/python/distribute/numpy_dataset.py b/tensorflow/python/distribute/numpy_dataset.py new file mode 100644 index 0000000000..5881e4cd59 --- /dev/null +++ b/tensorflow/python/distribute/numpy_dataset.py @@ -0,0 +1,97 @@ +# 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. +# ============================================================================== +"""Code for creating a dataset out of a NumPy array.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.eager import context +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import variable_scope +from tensorflow.python.util import nest + + +def init_var_from_numpy(input_var, numpy_input, session): + """Initialize `input_var` to `numpy_input` using `session` in graph mode.""" + with ops.init_scope(): + if context.executing_eagerly(): + input_var.assign(numpy_input) + return + + assert session is not None + session.run(input_var.initializer) + + start_placeholder = array_ops.placeholder(dtypes.int64, ()) + end_placeholder = array_ops.placeholder(dtypes.int64, ()) + slice_placeholder = array_ops.placeholder(input_var.dtype) + assign_slice_op = input_var[start_placeholder:end_placeholder].assign( + slice_placeholder) + + # If each batch element is > 64 MB, then we copy each batch element + # individually. Otherwise, the slices will be < 128 MB. There might be + # padding which might mean that the slices are 128 MB even if the size of + # the tensor allocated is less than 128 MB. This formula gives slices with + # size: ceil(64 MB / byte size per batch element) bytes. Using ceil() + # guarantees we get a number >= 1. + + # Calculate the size of each batch element. + byte_size_per_batch_element = ( + np.prod(numpy_input.shape[1:]) * input_var.dtype.size) + + # Calculate number of elements we want to copy per slice. + batch_size_per_slice = int( + np.ceil((64 << 20) / byte_size_per_batch_element)) + + # Copy slices of the above size starting at 0, except the last slice will be + # smaller. + start = 0 + limit = numpy_input.shape[0] + while start < limit: + end = min(start + batch_size_per_slice, limit) + session.run(assign_slice_op, feed_dict={ + start_placeholder: start, + end_placeholder: end, + slice_placeholder: numpy_input[start:end]}) + start = end + + +def one_host_numpy_dataset(numpy_input, colocate_with, session): + """Create a dataset on `colocate_with` from `numpy_input`.""" + def create_colocated_variable(next_creator, *args, **kwargs): + kwargs["colocate_with"] = colocate_with + return next_creator(*args, **kwargs) + + numpy_flat = nest.flatten(numpy_input) + with variable_scope.variable_creator_scope(create_colocated_variable): + vars_flat = tuple(variable_scope.variable(array_ops.zeros(i.shape, i.dtype), + trainable=False) + for i in numpy_flat) + for v, i in zip(vars_flat, numpy_flat): + init_var_from_numpy(v, i, session) + vars_nested = nest.pack_sequence_as(numpy_input, vars_flat) + return dataset_ops.Dataset.from_tensor_slices(vars_nested) + + +class SingleDevice(object): + """Used with `colocate_with` to create a non-mirrored variable.""" + + def __init__(self, device): + self.device = device diff --git a/tensorflow/python/distribute/numpy_dataset_test.py b/tensorflow/python/distribute/numpy_dataset_test.py new file mode 100644 index 0000000000..04eae1daa2 --- /dev/null +++ b/tensorflow/python/distribute/numpy_dataset_test.py @@ -0,0 +1,44 @@ +# 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. +# ============================================================================== +"""Tests for numpy_dataset.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +from tensorflow.python.distribute import numpy_dataset +from tensorflow.python.eager import test +from tensorflow.python.framework import test_util +from tensorflow.python.ops import variable_scope + + +class InitVarFromNumpyTest(test.TestCase): + + @test_util.run_in_graph_and_eager_modes + def test_creating_var_with_numpy_arrays(self): + with self.cached_session() as session: + x = np.asarray(np.random.random((64, 3)), dtype=np.float32) + initial = np.zeros_like(x) + var_x = variable_scope.variable(initial) + numpy_dataset.init_var_from_numpy(var_x, x, session) + val = self.evaluate(var_x.value()) + # Verify that the numpy value is copied to the variable. + self.assertAllEqual(x, val) + + +if __name__ == '__main__': + test.main() diff --git a/tensorflow/python/distribute/parameter_server_strategy.py b/tensorflow/python/distribute/parameter_server_strategy.py index 71fbffdc0d..ac5ee6f589 100644 --- a/tensorflow/python/distribute/parameter_server_strategy.py +++ b/tensorflow/python/distribute/parameter_server_strategy.py @@ -26,6 +26,7 @@ from tensorflow.python.distribute import device_util from tensorflow.python.distribute import distribute_lib from tensorflow.python.distribute import input_lib from tensorflow.python.distribute import multi_worker_util +from tensorflow.python.distribute import numpy_dataset from tensorflow.python.distribute import values from tensorflow.python.distribute.cluster_resolver import SimpleClusterResolver from tensorflow.python.distribute.cluster_resolver import TFConfigClusterResolver @@ -137,6 +138,7 @@ class ParameterServerStrategyExtended( assert cluster_spec.as_dict() worker_device = "/job:%s/task:%d" % (task_type, task_id) + self._input_host_device = numpy_dataset.SingleDevice(worker_device) # Define compute devices which is a list of device strings and one for each # replica. When there are GPUs, replicate operations on these GPUs. @@ -195,6 +197,7 @@ class ParameterServerStrategyExtended( def _initialize_local(self, cluster_resolver): """Initialize internal devices for local training.""" worker_device = device_util.canonicalize("/device:CPU:0") + self._input_host_device = numpy_dataset.SingleDevice(worker_device) num_gpus = cluster_resolver.num_accelerators() # Define compute devices which is a list of device strings and one for each # replica. When there are GPUs, replicate operations on these GPUs. @@ -262,6 +265,10 @@ class ParameterServerStrategyExtended( return input_lib.InputFunctionIterator(input_fn, self._input_workers, [input_context]) + def _experimental_make_numpy_dataset(self, numpy_input, session): + return numpy_dataset.one_host_numpy_dataset( + numpy_input, self._input_host_device, session) + def _broadcast_to(self, tensor, destinations): # This is both a fast path for Python constants, and a way to delay # converting Python values to a tensor until we know what type it @@ -329,8 +336,12 @@ class ParameterServerStrategyExtended( var_creator = next_creator if "colocate_with" in kwargs: + colocate_with = kwargs["colocate_with"] + if isinstance(colocate_with, numpy_dataset.SingleDevice): + with ops.device(colocate_with.device): + return var_creator(*args, **kwargs) with ops.device(None): - with ops.colocate_with(kwargs["colocate_with"]): + with ops.colocate_with(colocate_with): return var_creator(*args, **kwargs) with ops.colocate_with(None, ignore_existing=True): diff --git a/tensorflow/python/keras/engine/distributed_training_utils.py b/tensorflow/python/keras/engine/distributed_training_utils.py index 1678d84307..b6ef33f700 100644 --- a/tensorflow/python/keras/engine/distributed_training_utils.py +++ b/tensorflow/python/keras/engine/distributed_training_utils.py @@ -34,25 +34,13 @@ from tensorflow.python.keras import callbacks from tensorflow.python.keras import metrics as metrics_module from tensorflow.python.keras import optimizers from tensorflow.python.keras.optimizer_v2 import optimizer_v2 -from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import variables from tensorflow.python.platform import tf_logging as logging -from tensorflow.python.training import distribution_strategy_context from tensorflow.python.training.mode_keys import ModeKeys from tensorflow.python.util import nest -def validate_not_in_strategy_scope(): - """Validate fit/eval/predict are not running in DS scope.""" - if distribution_strategy_context.has_distribution_strategy(): - if distribution_strategy_context.in_cross_replica_context(): - raise RuntimeError( - 'Fit/Eval/Predict should not be run inside the tf.distribute.Strategy' - ' scope. Only model creation and compilation should be in ' - 'tf.distribute.Strategy scope.') - - def set_weights(distribution_strategy, dist_model, weights): """Sets the weights of the replicated models. @@ -530,100 +518,11 @@ def get_batch_dimension(iterator): return dims[0] if dims else None -def get_cpu_device(distribution_strategy): - """Returns the CPU device of the TPU host or the default CPU device string. - - Args: - distribution_strategy: The DistributionStrategy used to compile the model. - - Returns: - A device string which is the TPU host's CPU device in case of - TPUDistributionStrategy or the default CPU device string in all other - cases. - - Raises: - NotImplementedError: We currently don't support copying numpy data to - multiple hosts in the case of Cloud TPU pods. - """ - if is_tpu_strategy(distribution_strategy): - if distribution_strategy.extended.num_hosts > 1: - raise NotImplementedError('TPUDistributionStrategy does not ' - 'support numpy inputs when running on Cloud' - 'TPU pods.') - return distribution_strategy.extended.get_host_cpu_device(0) - else: - # For all strategies except TPUDistributionStrategy - # TODO(anjalisridhar): We may need to modify this when we add support for - # multi-worker strategy. - return '/CPU:0' - - -def get_var_for_numpy(distribution_strategy, x): - if isinstance(x, list): - var_x = tuple([_get_var_for_numpy(distribution_strategy, single_input) - for single_input in x]) - else: - var_x = _get_var_for_numpy(distribution_strategy, x) - return var_x - - -def _get_var_for_numpy(distribution_strategy, input_array): - """Creates a variable and assigns the value of the numpy array to it. - - Args: - distribution_strategy: The DistributionStrategy used to compile the model. - input_array: The input numpy array whose value will be assigned to the - variable we create. - - Returns: - The variable to which we will copy the value of the input numpy array. - - """ - with ops.device(get_cpu_device(distribution_strategy)): - # Create and initialize a variable on the CPU device. This is the CPU - # device of the host in the case of TPUDistributionStrategy. - input_var = variables.VariableV1(array_ops.zeros(input_array.shape, - input_array.dtype), - trainable=False, use_resource=True) - K.get_session().run(input_var.initializer) - - # Create a placeholder for the numpy array input slices. We copy the value - # of the input numpy array to the variable in slices of size 64 MB to avoid - # running into memory issues or RPC message limits. - start_placeholder = array_ops.placeholder(dtypes.int64, ()) - end_placeholder = array_ops.placeholder(dtypes.int64, ()) - slice_placeholder = array_ops.placeholder(input_var.dtype) - assign_slice_op = input_var[start_placeholder:end_placeholder].assign( - slice_placeholder) - - # If each batch element is > 64 MB, then we copy each batch element - # individually. Otherwise, the slices will be < 128 MB. There might be padding - # which might mean that the slices are 128 MB even if the size of the - # tensor allocated is less than 128 MB. - # This formula gives slices with size: - # ceil(64 MB / byte size per batch element) bytes. - # Using ceil() guarantees we get a number >= 1. - - # Calculate the size of each batch element. - byte_size_per_batch_element = np.prod(input_array.shape[1:]) * \ - input_var.dtype.size - - # Calculate number of elements we want to copy per slice. - batch_size_per_slice = int(np.ceil((64 << 20) / byte_size_per_batch_element)) - - # Copy slices of the above size starting at 0, except the last slice will be - # smaller. - start = 0 - limit = input_array.shape[0] - while start < limit: - end = min(start + batch_size_per_slice, limit) - K.get_session().run(assign_slice_op, feed_dict={ - start_placeholder: start, - end_placeholder: end, - slice_placeholder: input_array[start:end]}) - start = end - - return input_var +def list_to_tuple(maybe_list): + """Datasets treat lists specially, so switch them to tuples.""" + if isinstance(maybe_list, list): + return tuple(maybe_list) + return maybe_list def _get_input_from_iterator(iterator, model): diff --git a/tensorflow/python/keras/engine/training.py b/tensorflow/python/keras/engine/training.py index 1eda8cf797..a65c2b6413 100644 --- a/tensorflow/python/keras/engine/training.py +++ b/tensorflow/python/keras/engine/training.py @@ -2161,53 +2161,47 @@ class Model(Network): 'you should specify the `{steps_name}` argument.' .format(steps_name=steps_name)) - first_x_value = nest.flatten(x)[0] - if isinstance(first_x_value, np.ndarray): - # We need to use the drop_remainder argument to allow for a static - # input shape which is required for TPUs. - drop_remainder = self._distribution_strategy.require_static_shapes - if y is not None: - var_x = distributed_training_utils.get_var_for_numpy( - self._distribution_strategy, x) - var_y = distributed_training_utils.get_var_for_numpy( - self._distribution_strategy, y) - if sample_weight is not None: - var_sample_weights = distributed_training_utils.get_var_for_numpy( - self._distribution_strategy, sample_weight) - - x = dataset_ops.Dataset.from_tensor_slices((var_x, var_y, - var_sample_weights)) + if ops.executing_eagerly_outside_functions(): + session = None + else: + session = K.get_session() + + with self._distribution_strategy.scope(): + first_x_value = nest.flatten(x)[0] + if isinstance(first_x_value, np.ndarray): + x = distributed_training_utils.list_to_tuple(x) + if y is not None: + y = distributed_training_utils.list_to_tuple(y) + if sample_weight is not None: + sample_weight = distributed_training_utils.list_to_tuple( + sample_weight) + in_tuple = (x, y, sample_weight) + else: + in_tuple = (x, y) else: - x = dataset_ops.Dataset.from_tensor_slices((var_x, var_y)) + in_tuple = x if shuffle: # 1024 is a good buffer size since it is much larger than the average # batch size provided by the user and provides sufficient randomness. # One thing to keep in mind is the memory usage based on the size of # each sample. - x = x.shuffle(1024) - x = x.repeat() - x = x.batch(batch_size, drop_remainder=drop_remainder) - y = None - sample_weight = None + shuffle_buffer = 1024 + else: + shuffle_buffer = None + iterator = self._distribution_strategy.experimental_make_numpy_iterator( + in_tuple, batch_size, num_epochs=None, shuffle=shuffle_buffer, + session=session) else: - # This case is for the predict call where the dataset only contains - # inputs and no targets, i.e. it does not return a tuple - var_x = distributed_training_utils.get_var_for_numpy( - self._distribution_strategy, x) - x = dataset_ops.Dataset.from_tensor_slices(var_x) - x = x.batch(batch_size, drop_remainder=drop_remainder) - - assert isinstance(x, dataset_ops.DatasetV2) + assert isinstance(x, dataset_ops.DatasetV2) + training_utils.validate_dataset_input(x, y, sample_weight, + validation_split) + iterator = self._distribution_strategy.make_dataset_iterator(x) - with self._distribution_strategy.scope(): - iterator = self._distribution_strategy.make_dataset_iterator(x) init_op = iterator.initialize() if not context.executing_eagerly(): K.get_session().run(init_op) - training_utils.validate_dataset_input(x, y, sample_weight, - validation_split) return iterator def _standardize_user_data(self, diff --git a/tensorflow/python/keras/engine/training_distributed.py b/tensorflow/python/keras/engine/training_distributed.py index 92a46e399a..0bd79f2b47 100644 --- a/tensorflow/python/keras/engine/training_distributed.py +++ b/tensorflow/python/keras/engine/training_distributed.py @@ -59,8 +59,6 @@ def fit_distributed(model, first_x_value = nest.flatten(x)[0] if isinstance(first_x_value, np.ndarray): - # TODO(b/122314600): Remove the scope validate. - distributed_training_utils.validate_not_in_strategy_scope() steps_per_epoch, batch_size = ( distributed_training_utils.get_input_params( model._distribution_strategy, first_x_value, steps_per_epoch, @@ -141,8 +139,6 @@ def evaluate_distributed(model, distributed_training_utils.validate_inputs(x, y, model._distribution_strategy) first_x_value = nest.flatten(x)[0] if isinstance(first_x_value, np.ndarray): - # TODO(b/122314600): Remove the scope validate. - distributed_training_utils.validate_not_in_strategy_scope() steps, batch_size = distributed_training_utils.get_input_params( model._distribution_strategy, first_x_value, steps, batch_size) batch_size = model._validate_or_infer_batch_size(batch_size, steps, x) @@ -179,8 +175,6 @@ def predict_distributed(model, x, None, model._distribution_strategy) first_x_value = nest.flatten(x)[0] if isinstance(first_x_value, np.ndarray): - # TODO(b/122314600): Remove the scope validate. - distributed_training_utils.validate_not_in_strategy_scope() steps, batch_size = distributed_training_utils.get_input_params( model._distribution_strategy, first_x_value, steps, batch_size) batch_size = model._validate_or_infer_batch_size(batch_size, steps, x) diff --git a/tensorflow/python/training/optimizer.py b/tensorflow/python/training/optimizer.py index eaa563e84a..c6cc0b6044 100644 --- a/tensorflow/python/training/optimizer.py +++ b/tensorflow/python/training/optimizer.py @@ -554,14 +554,15 @@ class Optimizer( # by most optimizers. It relies on the subclass implementing the following # methods: _create_slots(), _prepare(), _apply_dense(), and _apply_sparse(). - # Handle DistributionStrategy case. - if distribute_ctx.get_cross_replica_context(): - raise RuntimeError("Use `_distributed_apply()` instead of " - "`apply_gradients()` in a cross-replica context.") - # TODO(isaprykin): Get rid of `has_distribution_strategy()` check by + # TODO(isaprykin): Get rid of `has_strategy()` check by # always calling _distributed_apply(), using the default distribution # as needed. - if distribute_ctx.has_distribution_strategy(): + if distribute_ctx.has_strategy(): + # Handle DistributionStrategy case. + if distribute_ctx.in_cross_replica_context(): + raise RuntimeError("Use `_distributed_apply()` instead of " + "`apply_gradients()` in a cross-replica context.") + grads_and_vars = get_filtered_grad_fn(lambda: grads_and_vars)() return distribute_ctx.get_replica_context().merge_call( self._distributed_apply, args=(grads_and_vars, global_step, name)) diff --git a/tensorflow/tools/api/golden/v1/tensorflow.distribute.-mirrored-strategy.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.distribute.-mirrored-strategy.pbtxt index b06c73d126..9c29067b6d 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.distribute.-mirrored-strategy.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.distribute.-mirrored-strategy.pbtxt @@ -75,6 +75,10 @@ tf_class { name: "experimental_initialize" argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" } + member_method { + name: "experimental_make_numpy_iterator" + argspec: "args=[\'self\', \'numpy_input\', \'batch_size\', \'num_epochs\', \'shuffle\', \'session\'], varargs=None, keywords=None, defaults=[\'1\', \'1024\', \'None\'], " + } member_method { name: "experimental_run" argspec: "args=[\'self\', \'fn\', \'input_iterator\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.distribute.-strategy-extended.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.distribute.-strategy-extended.pbtxt index 77706e5713..37b620891f 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.distribute.-strategy-extended.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.distribute.-strategy-extended.pbtxt @@ -50,6 +50,10 @@ tf_class { name: "colocate_vars_with" argspec: "args=[\'self\', \'colocate_with_variable\'], varargs=None, keywords=None, defaults=None" } + member_method { + name: "experimental_make_numpy_dataset" + argspec: "args=[\'self\', \'numpy_input\', \'session\'], varargs=None, keywords=None, defaults=[\'None\'], " + } member_method { name: "experimental_run_steps_on_iterator" argspec: "args=[\'self\', \'fn\', \'iterator\', \'iterations\', \'initial_loop_values\'], varargs=None, keywords=None, defaults=[\'1\', \'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.distribute.-strategy.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.distribute.-strategy.pbtxt index 9a1df55142..4aa6f1c4e1 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.distribute.-strategy.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.distribute.-strategy.pbtxt @@ -74,6 +74,10 @@ tf_class { name: "experimental_initialize" argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" } + member_method { + name: "experimental_make_numpy_iterator" + argspec: "args=[\'self\', \'numpy_input\', \'batch_size\', \'num_epochs\', \'shuffle\', \'session\'], varargs=None, keywords=None, defaults=[\'1\', \'1024\', \'None\'], " + } member_method { name: "experimental_run" argspec: "args=[\'self\', \'fn\', \'input_iterator\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.distribute.-mirrored-strategy.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.distribute.-mirrored-strategy.pbtxt index b06c73d126..9c29067b6d 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.distribute.-mirrored-strategy.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.distribute.-mirrored-strategy.pbtxt @@ -75,6 +75,10 @@ tf_class { name: "experimental_initialize" argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" } + member_method { + name: "experimental_make_numpy_iterator" + argspec: "args=[\'self\', \'numpy_input\', \'batch_size\', \'num_epochs\', \'shuffle\', \'session\'], varargs=None, keywords=None, defaults=[\'1\', \'1024\', \'None\'], " + } member_method { name: "experimental_run" argspec: "args=[\'self\', \'fn\', \'input_iterator\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.distribute.-strategy-extended.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.distribute.-strategy-extended.pbtxt index 77706e5713..37b620891f 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.distribute.-strategy-extended.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.distribute.-strategy-extended.pbtxt @@ -50,6 +50,10 @@ tf_class { name: "colocate_vars_with" argspec: "args=[\'self\', \'colocate_with_variable\'], varargs=None, keywords=None, defaults=None" } + member_method { + name: "experimental_make_numpy_dataset" + argspec: "args=[\'self\', \'numpy_input\', \'session\'], varargs=None, keywords=None, defaults=[\'None\'], " + } member_method { name: "experimental_run_steps_on_iterator" argspec: "args=[\'self\', \'fn\', \'iterator\', \'iterations\', \'initial_loop_values\'], varargs=None, keywords=None, defaults=[\'1\', \'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.distribute.-strategy.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.distribute.-strategy.pbtxt index 9a1df55142..4aa6f1c4e1 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.distribute.-strategy.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.distribute.-strategy.pbtxt @@ -74,6 +74,10 @@ tf_class { name: "experimental_initialize" argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None" } + member_method { + name: "experimental_make_numpy_iterator" + argspec: "args=[\'self\', \'numpy_input\', \'batch_size\', \'num_epochs\', \'shuffle\', \'session\'], varargs=None, keywords=None, defaults=[\'1\', \'1024\', \'None\'], " + } member_method { name: "experimental_run" argspec: "args=[\'self\', \'fn\', \'input_iterator\'], varargs=None, keywords=None, defaults=[\'None\'], " -- GitLab From 0fb8e5f9c0149c4cfaf8ec41b1293a947bc91895 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Wed, 9 Jan 2019 14:52:30 -0800 Subject: [PATCH 578/622] This change makes errors more informative in case of functions. We now report two additional things: 1. The line number/file name of the culprit node inside the function. 2. The function calling stack For eg, consider the following code: import tensorflow as tf tf.enable_eager_execution() @tf.contrib.eager.defun def fn3(x): return x + 2 @tf.contrib.eager.defun def fn2(x): tf.assert_equal(fn3(x), 3) return 2 @tf.contrib.eager.defun def fn(x): return fn2(x) @tf.contrib.eager.defun def main(argv): x = fn(2) print(x) if __name__ == '__main__': tf.app.run() In this, the error message would contain the following: InvalidArgumentError: assertion failed: [] [Condition x == y did not hold element-wise:] [x (PartitionedCall:0) = ] [4] [y (assert_equal/y:0) = ] [3] [[node assert_equal/Assert/Assert (defined at filename:line_num) ]] [[StatefulPartitionedCall]] [[StatefulPartitionedCall]] [Op:StatefulPartitionedCall] Function calling stack: main -> fn -> fn2 PiperOrigin-RevId: 228593653 --- .../core/kernels/partitioned_function_ops.cc | 9 ++- tensorflow/core/lib/core/errors.h | 4 ++ tensorflow/python/eager/function.py | 61 ++++++++++++++++--- tensorflow/python/eager/function_test.py | 24 ++++++++ .../python/framework/error_interpolation.py | 8 ++- 5 files changed, 92 insertions(+), 14 deletions(-) diff --git a/tensorflow/core/kernels/partitioned_function_ops.cc b/tensorflow/core/kernels/partitioned_function_ops.cc index e493dca188..cadb83d8cf 100644 --- a/tensorflow/core/kernels/partitioned_function_ops.cc +++ b/tensorflow/core/kernels/partitioned_function_ops.cc @@ -20,6 +20,7 @@ limitations under the License. #include "tensorflow/core/framework/types.h" #include "tensorflow/core/framework/types.pb.h" #include "tensorflow/core/grappler/optimizers/meta_optimizer.h" +#include "tensorflow/core/lib/strings/strcat.h" #include "tensorflow/core/protobuf/config.pb.h" #include "tensorflow/core/protobuf/rewriter_config.pb.h" #include "tensorflow/core/util/ptr_util.h" @@ -213,10 +214,14 @@ class PartitionedCallOp : public AsyncOpKernel { run_opts.rendezvous = rendez; std::vector* rets = new std::vector; + const string& func_name = func_.name(); lib->Run(run_opts, handle, inputs, rets, - [rets, rendez, done, ctx](const Status& status) { + [rets, rendez, done, ctx, func_name](const Status& status) { if (!status.ok()) { - ctx->SetStatus(status); + const string function_and_msg = + strings::StrCat(errors::FormatFunctionForError(func_name), + " ", status.error_message()); + ctx->SetStatus(Status(status.code(), function_and_msg)); } else { for (int i = 0; i < rets->size(); ++i) { ctx->set_output(i, (*rets)[i]); diff --git a/tensorflow/core/lib/core/errors.h b/tensorflow/core/lib/core/errors.h index d5cbe6c616..4815f7c2cc 100644 --- a/tensorflow/core/lib/core/errors.h +++ b/tensorflow/core/lib/core/errors.h @@ -150,6 +150,10 @@ string FormatColocationNodeForError(const T& names) { }); } +inline string FormatFunctionForError(const string& name) { + return strings::StrCat("{{function_node ", name, "}}"); +} + // The CanonicalCode() for non-errors. using ::tensorflow::error::OK; diff --git a/tensorflow/python/eager/function.py b/tensorflow/python/eager/function.py index 67c633726f..83cd140158 100644 --- a/tensorflow/python/eager/function.py +++ b/tensorflow/python/eager/function.py @@ -41,6 +41,8 @@ from tensorflow.python.framework import c_api_util from tensorflow.python.framework import constant_op from tensorflow.python.framework import device as pydev from tensorflow.python.framework import dtypes as dtypes_module +from tensorflow.python.framework import error_interpolation +from tensorflow.python.framework import errors from tensorflow.python.framework import func_graph as func_graph_module from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_spec @@ -131,6 +133,46 @@ def _parse_func_attrs(attributes): return attrs +class _InterpolateFunctionError(object): + """Context Manager that interpolates the exception from 'top_level_func'.""" + + def __init__(self, top_level_func): + self._func = top_level_func + + def __enter__(self): + pass + + def __exit__(self, typ, exc, tb): + if not exc or not isinstance(exc, errors.OpError): + return False + message = compat.as_text(exc.message) + _, tags = error_interpolation.parse_message(message) + g = None + func_stack = [] + # pylint: disable=protected-access + for t in tags: + if t.type == "function_node": + if t.name == compat.as_str(self._func.name): + g = self._func._graph + elif g: + next_func = g._get_function(t.name) + if next_func is not None and isinstance(next_func, + _EagerDefinedFunction): + g = next_func._graph + if g: + func_stack.append(g.name) + else: + func_stack.append("") + # pylint: enable=protected-access + if g: + message = error_interpolation.interpolate(message, g) + message += "\n\nFunction call stack:\n" + message += " -> ".join(func_stack) + message += "\n" + exc._message = message # pylint: disable=protected-access + return False + + def _forward_name(n): """The name of a generated forward defun named n.""" return "__forward_%s_%s" % (n, ops.uid()) @@ -279,13 +321,14 @@ class _EagerDefinedFunction(object): "Arguments and signature arguments do not match: %s %s " % (len(args), len(list(self.signature.input_arg)))) function_call_options = ctx.get_function_call_options() - outputs = functional_ops.partitioned_call( - args=args, - f=self, - tout=self._output_types, - executing_eagerly=executing_eagerly, - config=function_call_options.config_proto_serialized, - executor_type=function_call_options.executor_type) + with _InterpolateFunctionError(self): + outputs = functional_ops.partitioned_call( + args=args, + f=self, + tout=self._output_types, + executing_eagerly=executing_eagerly, + config=function_call_options.config_proto_serialized, + executor_type=function_call_options.executor_type) if executing_eagerly: return outputs @@ -401,8 +444,8 @@ class Function(object): """ return self._call_flat( (t for t in nest.flatten((args, kwargs)) - if isinstance( - t, (ops.Tensor, resource_variable_ops.ResourceVariable)))) + if isinstance(t, (ops.Tensor, + resource_variable_ops.ResourceVariable)))) def _call_flat(self, args): """Executes the wrapped function. diff --git a/tensorflow/python/eager/function_test.py b/tensorflow/python/eager/function_test.py index 55a9cc4e92..1966b259bf 100644 --- a/tensorflow/python/eager/function_test.py +++ b/tensorflow/python/eager/function_test.py @@ -45,6 +45,7 @@ from tensorflow.python.framework import test_util from tensorflow.python.keras.engine import training as keras_training from tensorflow.python.layers import convolutional from tensorflow.python.ops import array_ops +from tensorflow.python.ops import check_ops from tensorflow.python.ops import clip_ops from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import init_ops @@ -2063,6 +2064,29 @@ class FunctionTest(test.TestCase, parameterized.TestCase): # function itself is not involved in a reference cycle. self.assertIs(None, weak_fn()) + def testFunctionStackInErrorMessage(self): + + @def_function.function() + def fn3(x): + return x + 2 + + @def_function.function() + def fn2(x): + check_ops.assert_equal(fn3(x), 3) + return 2 + + @def_function.function() + def fn(x): + return fn2(x) + + try: + fn(2) + self.assertFail() + except errors.InvalidArgumentError as e: + self.assertIn('fn -> fn2', e.message) + self.assertIn('node assert_equal/Assert/Assert (defined at', e.message) + self.assertNotIn('fn3', e.message) + if __name__ == '__main__': ops.enable_eager_execution( diff --git a/tensorflow/python/framework/error_interpolation.py b/tensorflow/python/framework/error_interpolation.py index 5e1bed8e0e..af83b70a46 100644 --- a/tensorflow/python/framework/error_interpolation.py +++ b/tensorflow/python/framework/error_interpolation.py @@ -31,7 +31,7 @@ import six from tensorflow.python.util import tf_stack -_NAME_REGEX = r"[A-Za-z0-9.][A-Za-z0-9_.\-/]*?" +_NAME_REGEX = r"[A-Za-z0-9_.][A-Za-z0-9_.\-/]*?" _TAG_REGEX = r"{{{{({name}) ({name})}}}}".format(name=_NAME_REGEX) _INTERPOLATION_REGEX = r"^(.*?)({tag})".format(tag=_TAG_REGEX) _INTERPOLATION_PATTERN = re.compile(_INTERPOLATION_REGEX, re.DOTALL) @@ -45,7 +45,7 @@ _BAD_FILE_SUBSTRINGS = [ ] -def _parse_message(message): +def parse_message(message): """Parses the message. Splits the message into separators and tags. Tags are named tuples @@ -376,7 +376,7 @@ def interpolate(error_message, graph): Returns: The string with tags of the form {{type name}} interpolated. """ - seps, tags = _parse_message(error_message) + seps, tags = parse_message(error_message) subs = [] end_msg = collections.defaultdict(list) tagged_ops = [] @@ -404,6 +404,8 @@ def interpolate(error_message, graph): msg = "node %s%s placed on device %s " % ( ops[0].name, field_dict["defined_at"], field_dict["devices"]) end_msg["colocations"].append(field_dict["devs_and_colocs"]) + if tag.type == "function_node": + msg = "" subs.append(msg) if "source_nodes" in end_msg: -- GitLab From 00a3be761b620761a71effb793e2440096f1eebd Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Wed, 9 Jan 2019 14:55:31 -0800 Subject: [PATCH 579/622] Add LinearOperator.inverse method. PiperOrigin-RevId: 228594130 --- .../linalg/linear_operator_algebra_test.py | 48 ++++++ .../linalg/linear_operator_block_diag_test.py | 38 +++-- .../linalg/linear_operator_diag_test.py | 9 +- .../linalg/linear_operator_identity_test.py | 24 ++- .../linalg/linear_operator_kronecker_test.py | 37 +++-- .../linalg/linear_operator_zeros_test.py | 3 +- .../ops/linalg/inverse_registrations.py | 108 +++++++++++++ tensorflow/python/ops/linalg/linalg.py | 1 + .../python/ops/linalg/linear_operator.py | 25 +++ .../ops/linalg/linear_operator_algebra.py | 71 +++++++++ .../ops/linalg/linear_operator_test_util.py | 25 ++- ...w.linalg.-linear-operator-block-diag.pbtxt | 4 + ...ow.linalg.-linear-operator-circulant.pbtxt | 4 + ...linalg.-linear-operator-circulant2-d.pbtxt | 4 + ...linalg.-linear-operator-circulant3-d.pbtxt | 4 + ....linalg.-linear-operator-composition.pbtxt | 4 + ...sorflow.linalg.-linear-operator-diag.pbtxt | 4 + ....linalg.-linear-operator-full-matrix.pbtxt | 4 + ...low.linalg.-linear-operator-identity.pbtxt | 4 + ...ow.linalg.-linear-operator-inversion.pbtxt | 142 ++++++++++++++++++ ...ow.linalg.-linear-operator-kronecker.pbtxt | 4 + ...alg.-linear-operator-low-rank-update.pbtxt | 4 + ...lg.-linear-operator-lower-triangular.pbtxt | 4 + ...alg.-linear-operator-scaled-identity.pbtxt | 4 + ...orflow.linalg.-linear-operator-zeros.pbtxt | 4 + .../tensorflow.linalg.-linear-operator.pbtxt | 4 + .../api/golden/v1/tensorflow.linalg.pbtxt | 4 + ...w.linalg.-linear-operator-block-diag.pbtxt | 4 + ...ow.linalg.-linear-operator-circulant.pbtxt | 4 + ...linalg.-linear-operator-circulant2-d.pbtxt | 4 + ...linalg.-linear-operator-circulant3-d.pbtxt | 4 + ....linalg.-linear-operator-composition.pbtxt | 4 + ...sorflow.linalg.-linear-operator-diag.pbtxt | 4 + ....linalg.-linear-operator-full-matrix.pbtxt | 4 + ...low.linalg.-linear-operator-identity.pbtxt | 4 + ...ow.linalg.-linear-operator-inversion.pbtxt | 142 ++++++++++++++++++ ...ow.linalg.-linear-operator-kronecker.pbtxt | 4 + ...alg.-linear-operator-low-rank-update.pbtxt | 4 + ...lg.-linear-operator-lower-triangular.pbtxt | 4 + ...alg.-linear-operator-scaled-identity.pbtxt | 4 + ...orflow.linalg.-linear-operator-zeros.pbtxt | 4 + .../tensorflow.linalg.-linear-operator.pbtxt | 4 + .../api/golden/v2/tensorflow.linalg.pbtxt | 4 + 43 files changed, 761 insertions(+), 32 deletions(-) create mode 100644 tensorflow/python/ops/linalg/inverse_registrations.py create mode 100644 tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-inversion.pbtxt create mode 100644 tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-inversion.pbtxt diff --git a/tensorflow/python/kernel_tests/linalg/linear_operator_algebra_test.py b/tensorflow/python/kernel_tests/linalg/linear_operator_algebra_test.py index 8e296c026c..ec78a3ffe0 100644 --- a/tensorflow/python/kernel_tests/linalg/linear_operator_algebra_test.py +++ b/tensorflow/python/kernel_tests/linalg/linear_operator_algebra_test.py @@ -30,6 +30,8 @@ _CHOLESKY_DECOMPS = linear_operator_algebra._CHOLESKY_DECOMPS _MATMUL = linear_operator_algebra._MATMUL _registered_cholesky = linear_operator_algebra._registered_cholesky _registered_matmul = linear_operator_algebra._registered_matmul +_INVERSES = linear_operator_algebra._INVERSES +_registered_inverse = linear_operator_algebra._registered_inverse # pylint: enable=protected-access @@ -129,5 +131,51 @@ class MatmulTest(test.TestCase): self.assertEqual(v, _registered_matmul(k[0], k[1])) +class InverseTest(test.TestCase): + + def testRegistration(self): + + class CustomLinOp(linear_operator.LinearOperator): + + def _matmul(self, a): + pass + + def _shape(self): + return tensor_shape.TensorShape([1, 1]) + + def _shape_tensor(self): + pass + + # Register Inverse to a lambda that spits out the name parameter + @linear_operator_algebra.RegisterInverse(CustomLinOp) + def _inverse(a): # pylint: disable=unused-argument,unused-variable + return "OK" + + with self.assertRaisesRegexp(ValueError, "singular"): + CustomLinOp(dtype=None, is_non_singular=False).inverse() + + self.assertEqual("OK", CustomLinOp( + dtype=None, is_non_singular=True).inverse()) + + def testRegistrationFailures(self): + + class CustomLinOp(linear_operator.LinearOperator): + pass + + with self.assertRaisesRegexp(TypeError, "must be callable"): + linear_operator_algebra.RegisterInverse(CustomLinOp)("blah") + + # First registration is OK + linear_operator_algebra.RegisterInverse(CustomLinOp)(lambda a: None) + + # Second registration fails + with self.assertRaisesRegexp(ValueError, "has already been registered"): + linear_operator_algebra.RegisterInverse(CustomLinOp)(lambda a: None) + + def testExactRegistrationsAllMatch(self): + for (k, v) in _INVERSES.items(): + self.assertEqual(v, _registered_inverse(k[0])) + + if __name__ == "__main__": test.main() diff --git a/tensorflow/python/kernel_tests/linalg/linear_operator_block_diag_test.py b/tensorflow/python/kernel_tests/linalg/linear_operator_block_diag_test.py index f0cc5d709f..96e6e3c04c 100644 --- a/tensorflow/python/kernel_tests/linalg/linear_operator_block_diag_test.py +++ b/tensorflow/python/kernel_tests/linalg/linear_operator_block_diag_test.py @@ -155,20 +155,38 @@ class SquareLinearOperatorBlockDiagTest( is_self_adjoint=True, ) cholesky_factor = operator.cholesky() - self.assertTrue(isinstance( + self.assertIsInstance( cholesky_factor, - block_diag.LinearOperatorBlockDiag)) + block_diag.LinearOperatorBlockDiag) self.assertEqual(2, len(cholesky_factor.operators)) - self.assertTrue( - isinstance( - cholesky_factor.operators[0], - lower_triangular.LinearOperatorLowerTriangular) + self.assertIsInstance( + cholesky_factor.operators[0], + lower_triangular.LinearOperatorLowerTriangular) + self.assertIsInstance( + cholesky_factor.operators[1], + lower_triangular.LinearOperatorLowerTriangular ) - self.assertTrue( - isinstance( - cholesky_factor.operators[1], - lower_triangular.LinearOperatorLowerTriangular) + + def test_block_diag_inverse_type(self): + matrix = [[1., 0.], [0., 1.]] + operator = block_diag.LinearOperatorBlockDiag( + [ + linalg.LinearOperatorFullMatrix( + matrix, + is_non_singular=True, + ), + linalg.LinearOperatorFullMatrix( + matrix, + is_non_singular=True, + ), + ], + is_non_singular=True, ) + inverse = operator.inverse() + self.assertIsInstance( + inverse, + block_diag.LinearOperatorBlockDiag) + self.assertEqual(2, len(inverse.operators)) def test_is_non_singular_auto_set(self): # Matrix with two positive eigenvalues, 11 and 8. diff --git a/tensorflow/python/kernel_tests/linalg/linear_operator_diag_test.py b/tensorflow/python/kernel_tests/linalg/linear_operator_diag_test.py index dcbc0dd7c9..4d7a31be87 100644 --- a/tensorflow/python/kernel_tests/linalg/linear_operator_diag_test.py +++ b/tensorflow/python/kernel_tests/linalg/linear_operator_diag_test.py @@ -194,9 +194,12 @@ class LinearOperatorDiagTest( is_positive_definite=True, is_self_adjoint=True, ) - self.assertTrue(isinstance( - operator.cholesky(), - linalg.LinearOperatorDiag)) + self.assertIsInstance(operator.cholesky(), linalg.LinearOperatorDiag) + + def test_diag_inverse_type(self): + diag = [1., 3., 5., 8.] + operator = linalg.LinearOperatorDiag(diag, is_non_singular=True) + self.assertIsInstance(operator.inverse(), linalg.LinearOperatorDiag) if __name__ == "__main__": diff --git a/tensorflow/python/kernel_tests/linalg/linear_operator_identity_test.py b/tensorflow/python/kernel_tests/linalg/linear_operator_identity_test.py index 2da5e712d7..14b5228bca 100644 --- a/tensorflow/python/kernel_tests/linalg/linear_operator_identity_test.py +++ b/tensorflow/python/kernel_tests/linalg/linear_operator_identity_test.py @@ -265,9 +265,14 @@ class LinearOperatorIdentityTest( is_positive_definite=True, is_self_adjoint=True, ) - self.assertTrue(isinstance( - operator.cholesky(), - linalg_lib.LinearOperatorIdentity)) + self.assertIsInstance( + operator.cholesky(), linalg_lib.LinearOperatorIdentity) + + def test_identity_inverse_type(self): + operator = linalg_lib.LinearOperatorIdentity( + num_rows=2, is_non_singular=True) + self.assertIsInstance( + operator.inverse(), linalg_lib.LinearOperatorIdentity) class LinearOperatorScaledIdentityTest( @@ -491,10 +496,19 @@ class LinearOperatorScaledIdentityTest( is_positive_definite=True, is_self_adjoint=True, ) - self.assertTrue(isinstance( + self.assertIsInstance( operator.cholesky(), - linalg_lib.LinearOperatorScaledIdentity)) + linalg_lib.LinearOperatorScaledIdentity) + def test_scaled_identity_inverse_type(self): + operator = linalg_lib.LinearOperatorScaledIdentity( + num_rows=2, + multiplier=3., + is_non_singular=True, + ) + self.assertIsInstance( + operator.inverse(), + linalg_lib.LinearOperatorScaledIdentity) if __name__ == "__main__": test.main() diff --git a/tensorflow/python/kernel_tests/linalg/linear_operator_kronecker_test.py b/tensorflow/python/kernel_tests/linalg/linear_operator_kronecker_test.py index 513b246803..54ccc0c5f6 100644 --- a/tensorflow/python/kernel_tests/linalg/linear_operator_kronecker_test.py +++ b/tensorflow/python/kernel_tests/linalg/linear_operator_kronecker_test.py @@ -100,7 +100,7 @@ class SquareLinearOperatorKroneckerTest( @property def _tests_to_skip(self): - return ["det", "solve", "solve_with_broadcast"] + return ["det", "inverse", "solve", "solve_with_broadcast"] def _operator_and_matrix( self, build_info, dtype, use_placeholder, @@ -211,20 +211,33 @@ class SquareLinearOperatorKroneckerTest( is_self_adjoint=True, ) cholesky_factor = operator.cholesky() - self.assertTrue(isinstance( + self.assertIsInstance( cholesky_factor, - kronecker.LinearOperatorKronecker)) + kronecker.LinearOperatorKronecker) self.assertEqual(2, len(cholesky_factor.operators)) - self.assertTrue( - isinstance( - cholesky_factor.operators[0], - lower_triangular.LinearOperatorLowerTriangular) - ) - self.assertTrue( - isinstance( - cholesky_factor.operators[1], - lower_triangular.LinearOperatorLowerTriangular) + self.assertIsInstance( + cholesky_factor.operators[0], + lower_triangular.LinearOperatorLowerTriangular) + self.assertIsInstance( + cholesky_factor.operators[1], + lower_triangular.LinearOperatorLowerTriangular) + + def test_kronecker_inverse_type(self): + matrix = [[1., 0.], [0., 1.]] + operator = kronecker.LinearOperatorKronecker( + [ + linalg.LinearOperatorFullMatrix( + matrix, is_non_singular=True), + linalg.LinearOperatorFullMatrix( + matrix, is_non_singular=True), + ], + is_non_singular=True, ) + inverse = operator.inverse() + self.assertIsInstance( + inverse, + kronecker.LinearOperatorKronecker) + self.assertEqual(2, len(inverse.operators)) if __name__ == "__main__": diff --git a/tensorflow/python/kernel_tests/linalg/linear_operator_zeros_test.py b/tensorflow/python/kernel_tests/linalg/linear_operator_zeros_test.py index eb0b8ef127..10651d3c8a 100644 --- a/tensorflow/python/kernel_tests/linalg/linear_operator_zeros_test.py +++ b/tensorflow/python/kernel_tests/linalg/linear_operator_zeros_test.py @@ -36,7 +36,8 @@ class LinearOperatorZerosTest( @property def _tests_to_skip(self): - return ["cholesky", "log_abs_det", "solve", "solve_with_broadcast"] + return [ + "cholesky", "log_abs_det", "inverse", "solve", "solve_with_broadcast"] @property def _operator_build_infos(self): diff --git a/tensorflow/python/ops/linalg/inverse_registrations.py b/tensorflow/python/ops/linalg/inverse_registrations.py new file mode 100644 index 0000000000..b89bdd240e --- /dev/null +++ b/tensorflow/python/ops/linalg/inverse_registrations.py @@ -0,0 +1,108 @@ +# Copyright 2018 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. +# ============================================================================== +"""Registrations for LinearOperator.inverse.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.ops.linalg import linear_operator +from tensorflow.python.ops.linalg import linear_operator_algebra +from tensorflow.python.ops.linalg import linear_operator_block_diag +from tensorflow.python.ops.linalg import linear_operator_circulant +from tensorflow.python.ops.linalg import linear_operator_diag +from tensorflow.python.ops.linalg import linear_operator_identity +from tensorflow.python.ops.linalg import linear_operator_inversion +from tensorflow.python.ops.linalg import linear_operator_kronecker + + +# By default, return LinearOperatorInversion which switched the .matmul +# and .solve methods. +@linear_operator_algebra.RegisterInverse(linear_operator.LinearOperator) +def _inverse_linear_operator(linop): + return linear_operator_inversion.LinearOperatorInversion( + linop, + is_non_singular=linop.is_non_singular, + is_self_adjoint=linop.is_self_adjoint, + is_positive_definite=linop.is_positive_definite, + is_square=linop.is_square) + + +@linear_operator_algebra.RegisterInverse( + linear_operator_diag.LinearOperatorDiag) +def _inverse_diag(diag_operator): + return linear_operator_diag.LinearOperatorDiag( + 1. / diag_operator.diag, + is_non_singular=diag_operator.is_non_singular, + is_self_adjoint=diag_operator.is_self_adjoint, + is_positive_definite=diag_operator.is_positive_definite, + is_square=True) + + +@linear_operator_algebra.RegisterInverse( + linear_operator_identity.LinearOperatorIdentity) +def _inverse_identity(identity_operator): + return identity_operator + + +@linear_operator_algebra.RegisterInverse( + linear_operator_identity.LinearOperatorScaledIdentity) +def _inverse_scaled_identity(identity_operator): + return linear_operator_identity.LinearOperatorScaledIdentity( + num_rows=identity_operator._num_rows, # pylint: disable=protected-access + multiplier=1. / identity_operator.multiplier, + is_non_singular=identity_operator.is_non_singular, + is_self_adjoint=True, + is_positive_definite=identity_operator.is_positive_definite, + is_square=True) + + +@linear_operator_algebra.RegisterInverse( + linear_operator_block_diag.LinearOperatorBlockDiag) +def _inverse_block_diag(block_diag_operator): + # We take the inverse of each block on the diagonal. + return linear_operator_block_diag.LinearOperatorBlockDiag( + operators=[ + operator.inverse() for operator in block_diag_operator.operators], + is_non_singular=block_diag_operator.is_non_singular, + is_self_adjoint=block_diag_operator.is_self_adjoint, + is_positive_definite=block_diag_operator.is_positive_definite, + is_square=True) + + +@linear_operator_algebra.RegisterInverse( + linear_operator_kronecker.LinearOperatorKronecker) +def _inverse_kronecker(kronecker_operator): + # Inverse decomposition of a Kronecker product is the Kronecker product + # of inverse decompositions. + return linear_operator_kronecker.LinearOperatorKronecker( + operators=[ + operator.inverse() for operator in kronecker_operator.operators], + is_non_singular=kronecker_operator.is_non_singular, + is_self_adjoint=kronecker_operator.is_self_adjoint, + is_positive_definite=kronecker_operator.is_positive_definite, + is_square=True) + + +@linear_operator_algebra.RegisterInverse( + linear_operator_circulant.LinearOperatorCirculant) +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) diff --git a/tensorflow/python/ops/linalg/linalg.py b/tensorflow/python/ops/linalg/linalg.py index ac4fd4ebc6..eebe741337 100644 --- a/tensorflow/python/ops/linalg/linalg.py +++ b/tensorflow/python/ops/linalg/linalg.py @@ -21,6 +21,7 @@ from __future__ import print_function # go/tf-wildcard-import # pylint: disable=wildcard-import,unused-import from tensorflow.python.ops.linalg import cholesky_registrations as _cholesky_registrations +from tensorflow.python.ops.linalg import inverse_registrations as _inverse_registrations from tensorflow.python.ops.linalg import linear_operator_algebra as _linear_operator_algebra from tensorflow.python.ops.linalg import matmul_registrations as _matmul_registrations from tensorflow.python.ops.linalg.linalg_impl import * diff --git a/tensorflow/python/ops/linalg/linear_operator.py b/tensorflow/python/ops/linalg/linear_operator.py index 6be81f4b34..4c99e86dc5 100644 --- a/tensorflow/python/ops/linalg/linear_operator.py +++ b/tensorflow/python/ops/linalg/linear_operator.py @@ -847,6 +847,31 @@ class LinearOperator(object): return self._solvevec(rhs, adjoint=adjoint) + def inverse(self, name="inverse"): + """Returns the Inverse of this `LinearOperator`. + + Given `A` representing this `LinearOperator`, return a `LinearOperator` + representing `A^-1`. + + Args: + name: A name scope to use for ops added by this method. + + Returns: + `LinearOperator` representing inverse of this matrix. + + Raises: + ValueError: When the `LinearOperator` is not hinted to be `non_singular`. + """ + if self.is_square is False: # pylint: disable=g-bool-id-comparison + raise ValueError("Cannot take the Inverse: This operator represents " + "a non square matrix.") + if self.is_non_singular is False: # pylint: disable=g-bool-id-comparison + raise ValueError("Cannot take the Inverse: This operator represents " + "a singular matrix.") + + with self._name_scope(name): + return linear_operator_algebra.inverse(self) + def cholesky(self, name="cholesky"): """Returns a Cholesky factor as a `LinearOperator`. diff --git a/tensorflow/python/ops/linalg/linear_operator_algebra.py b/tensorflow/python/ops/linalg/linear_operator_algebra.py index 7b99066e4c..c1513fdb38 100644 --- a/tensorflow/python/ops/linalg/linear_operator_algebra.py +++ b/tensorflow/python/ops/linalg/linear_operator_algebra.py @@ -27,6 +27,7 @@ from tensorflow.python.util import tf_inspect _CHOLESKY_DECOMPS = {} _MATMUL = {} +_INVERSES = {} def _registered_function(type_list, registry): @@ -55,6 +56,11 @@ def _registered_matmul(type_a, type_b): return _registered_function([type_a, type_b], _MATMUL) +def _registered_inverse(type_a): + """Get the Cholesky function registered for class a.""" + return _registered_function([type_a], _INVERSES) + + def cholesky(lin_op_a, name=None): """Get the Cholesky factor associated to lin_op_a. @@ -103,6 +109,29 @@ def matmul(lin_op_a, lin_op_b, name=None): return matmul_fn(lin_op_a, lin_op_b) +def inverse(lin_op_a, name=None): + """Get the Inverse associated to lin_op_a. + + Args: + lin_op_a: The LinearOperator to decompose. + name: Name to use for this operation. + + Returns: + A LinearOperator that represents the inverse of `lin_op_a`. + + Raises: + NotImplementedError: If no Inverse method is defined for the LinearOperator + type of `lin_op_a`. + """ + inverse_fn = _registered_inverse(type(lin_op_a)) + if inverse_fn is None: + raise ValueError("No inverse registered for {}".format( + type(lin_op_a))) + + with ops.name_scope(name, "Inverse"): + return inverse_fn(lin_op_a) + + class RegisterCholesky(object): """Decorator to register a Cholesky implementation function. @@ -189,3 +218,45 @@ class RegisterMatmul(object): self._key[1].__name__)) _MATMUL[self._key] = matmul_fn return matmul_fn + + +class RegisterInverse(object): + """Decorator to register an Inverse implementation function. + + Usage: + + @linear_operator_algebra.RegisterInverse(lin_op.LinearOperatorIdentity) + def _inverse_identity(lin_op_a): + # Return the identity matrix. + """ + + def __init__(self, lin_op_cls_a): + """Initialize the LinearOperator registrar. + + Args: + lin_op_cls_a: the class of the LinearOperator to decompose. + """ + self._key = (lin_op_cls_a,) + + def __call__(self, inverse_fn): + """Perform the Inverse registration. + + Args: + inverse_fn: The function to use for the Inverse. + + Returns: + inverse_fn + + Raises: + TypeError: if inverse_fn is not a callable. + ValueError: if a Inverse function has already been registered for + the given argument classes. + """ + if not callable(inverse_fn): + raise TypeError( + "inverse_fn must be callable, received: {}".format(inverse_fn)) + if self._key in _INVERSES: + raise ValueError("Inverse({}) has already been registered to: {}".format( + self._key[0].__name__, _INVERSES[self._key])) + _INVERSES[self._key] = inverse_fn + return inverse_fn diff --git a/tensorflow/python/ops/linalg/linear_operator_test_util.py b/tensorflow/python/ops/linalg/linear_operator_test_util.py index e50f572b5f..a957c84dc1 100644 --- a/tensorflow/python/ops/linalg/linear_operator_test_util.py +++ b/tensorflow/python/ops/linalg/linear_operator_test_util.py @@ -336,6 +336,22 @@ class LinearOperatorDerivedClassTest(test.TestCase): self._skip_if_tests_to_skip_contains("solve_with_broadcast") self._test_solve(with_batch=False) + def _test_inverse(self): + for use_placeholder in self._use_placeholder_options: + for build_info in self._operator_build_infos: + for dtype in self._dtypes_to_test: + with self.session(graph=ops.Graph()) as sess: + sess.graph.seed = random_seed.DEFAULT_GRAPH_SEED + operator, mat = self._operator_and_matrix( + build_info, dtype, use_placeholder=use_placeholder) + op_inverse_v, mat_inverse_v = sess.run([ + operator.inverse().to_dense(), linalg.inv(mat)]) + self.assertAC(op_inverse_v, mat_inverse_v) + + def test_inverse(self): + self._skip_if_tests_to_skip_contains("inverse") + self._test_inverse() + def test_trace(self): self._skip_if_tests_to_skip_contains("trace") for use_placeholder in self._use_placeholder_options: @@ -463,7 +479,14 @@ class NonSquareLinearOperatorDerivedClassTest(LinearOperatorDerivedClassTest): @property def _tests_to_skip(self): """List of test names to skip.""" - return ["cholesky", "solve", "solve_with_broadcast", "det", "log_abs_det"] + return [ + "cholesky", + "inverse", + "solve", + "solve_with_broadcast", + "det", + "log_abs_det" + ] @property def _operator_build_infos(self): diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-block-diag.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-block-diag.pbtxt index 773c74e64d..c7a50969b5 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-block-diag.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-block-diag.pbtxt @@ -95,6 +95,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-circulant.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-circulant.pbtxt index 533544d21f..3900c752c8 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-circulant.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-circulant.pbtxt @@ -116,6 +116,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-circulant2-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-circulant2-d.pbtxt index e3926eb6d4..7b876099af 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-circulant2-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-circulant2-d.pbtxt @@ -116,6 +116,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-circulant3-d.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-circulant3-d.pbtxt index ba209df782..5bddba8e79 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-circulant3-d.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-circulant3-d.pbtxt @@ -116,6 +116,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-composition.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-composition.pbtxt index 081fb0e08b..62ba8bb59e 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-composition.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-composition.pbtxt @@ -95,6 +95,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-diag.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-diag.pbtxt index 2014a04301..0803feeabd 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-diag.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-diag.pbtxt @@ -95,6 +95,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-full-matrix.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-full-matrix.pbtxt index 9a87ae9687..6def32864b 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-full-matrix.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-full-matrix.pbtxt @@ -91,6 +91,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-identity.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-identity.pbtxt index 33afb835ce..dbf1ac82d3 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-identity.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-identity.pbtxt @@ -92,6 +92,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-inversion.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-inversion.pbtxt new file mode 100644 index 0000000000..6a3fe4dd66 --- /dev/null +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-inversion.pbtxt @@ -0,0 +1,142 @@ +path: "tensorflow.linalg.LinearOperatorInversion" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "batch_shape" + mtype: "" + } + member { + name: "domain_dimension" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "graph_parents" + mtype: "" + } + member { + name: "is_non_singular" + mtype: "" + } + member { + name: "is_positive_definite" + mtype: "" + } + member { + name: "is_self_adjoint" + mtype: "" + } + member { + name: "is_square" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "operator" + mtype: "" + } + member { + name: "range_dimension" + mtype: "" + } + member { + name: "shape" + mtype: "" + } + member { + name: "tensor_rank" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'operator\', \'is_non_singular\', \'is_self_adjoint\', \'is_positive_definite\', \'is_square\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " + } + member_method { + name: "add_to_tensor" + argspec: "args=[\'self\', \'x\', \'name\'], varargs=None, keywords=None, defaults=[\'add_to_tensor\'], " + } + member_method { + name: "assert_non_singular" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'assert_non_singular\'], " + } + member_method { + name: "assert_positive_definite" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'assert_positive_definite\'], " + } + member_method { + name: "assert_self_adjoint" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'assert_self_adjoint\'], " + } + member_method { + name: "batch_shape_tensor" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'batch_shape_tensor\'], " + } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } + member_method { + name: "determinant" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'det\'], " + } + member_method { + name: "diag_part" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'diag_part\'], " + } + member_method { + name: "domain_dimension_tensor" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " + } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } + member_method { + name: "log_abs_determinant" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " + } + member_method { + name: "matmul" + argspec: "args=[\'self\', \'x\', \'adjoint\', \'adjoint_arg\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'False\', \'matmul\'], " + } + member_method { + name: "matvec" + argspec: "args=[\'self\', \'x\', \'adjoint\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'matvec\'], " + } + member_method { + name: "range_dimension_tensor" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'range_dimension_tensor\'], " + } + member_method { + name: "shape_tensor" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'shape_tensor\'], " + } + member_method { + name: "solve" + argspec: "args=[\'self\', \'rhs\', \'adjoint\', \'adjoint_arg\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'False\', \'solve\'], " + } + member_method { + name: "solvevec" + argspec: "args=[\'self\', \'rhs\', \'adjoint\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'solve\'], " + } + member_method { + name: "tensor_rank_tensor" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'tensor_rank_tensor\'], " + } + member_method { + name: "to_dense" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'to_dense\'], " + } + member_method { + name: "trace" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'trace\'], " + } +} diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-kronecker.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-kronecker.pbtxt index a9078c8ab5..85d902b977 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-kronecker.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-kronecker.pbtxt @@ -95,6 +95,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-low-rank-update.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-low-rank-update.pbtxt index 4cfa3bb30d..638d82a599 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-low-rank-update.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-low-rank-update.pbtxt @@ -115,6 +115,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-lower-triangular.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-lower-triangular.pbtxt index a87649133f..ab1b04bd3c 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-lower-triangular.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-lower-triangular.pbtxt @@ -91,6 +91,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-scaled-identity.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-scaled-identity.pbtxt index 3265646784..961969aac5 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-scaled-identity.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-scaled-identity.pbtxt @@ -96,6 +96,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-zeros.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-zeros.pbtxt index 49d8890c89..e76738a964 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-zeros.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator-zeros.pbtxt @@ -91,6 +91,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator.pbtxt index c89dc067b3..b35cd69da4 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.-linear-operator.pbtxt @@ -90,6 +90,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.pbtxt index 9f7b422fab..5e49b75c31 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.pbtxt @@ -36,6 +36,10 @@ tf_module { name: "LinearOperatorIdentity" mtype: "" } + member { + name: "LinearOperatorInversion" + mtype: "" + } member { name: "LinearOperatorKronecker" mtype: "" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-block-diag.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-block-diag.pbtxt index 773c74e64d..c7a50969b5 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-block-diag.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-block-diag.pbtxt @@ -95,6 +95,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-circulant.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-circulant.pbtxt index 533544d21f..3900c752c8 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-circulant.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-circulant.pbtxt @@ -116,6 +116,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-circulant2-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-circulant2-d.pbtxt index e3926eb6d4..7b876099af 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-circulant2-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-circulant2-d.pbtxt @@ -116,6 +116,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-circulant3-d.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-circulant3-d.pbtxt index ba209df782..5bddba8e79 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-circulant3-d.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-circulant3-d.pbtxt @@ -116,6 +116,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-composition.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-composition.pbtxt index 081fb0e08b..62ba8bb59e 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-composition.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-composition.pbtxt @@ -95,6 +95,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-diag.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-diag.pbtxt index 2014a04301..0803feeabd 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-diag.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-diag.pbtxt @@ -95,6 +95,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-full-matrix.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-full-matrix.pbtxt index 9a87ae9687..6def32864b 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-full-matrix.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-full-matrix.pbtxt @@ -91,6 +91,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-identity.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-identity.pbtxt index 33afb835ce..dbf1ac82d3 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-identity.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-identity.pbtxt @@ -92,6 +92,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-inversion.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-inversion.pbtxt new file mode 100644 index 0000000000..6a3fe4dd66 --- /dev/null +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-inversion.pbtxt @@ -0,0 +1,142 @@ +path: "tensorflow.linalg.LinearOperatorInversion" +tf_class { + is_instance: "" + is_instance: "" + is_instance: "" + member { + name: "batch_shape" + mtype: "" + } + member { + name: "domain_dimension" + mtype: "" + } + member { + name: "dtype" + mtype: "" + } + member { + name: "graph_parents" + mtype: "" + } + member { + name: "is_non_singular" + mtype: "" + } + member { + name: "is_positive_definite" + mtype: "" + } + member { + name: "is_self_adjoint" + mtype: "" + } + member { + name: "is_square" + mtype: "" + } + member { + name: "name" + mtype: "" + } + member { + name: "operator" + mtype: "" + } + member { + name: "range_dimension" + mtype: "" + } + member { + name: "shape" + mtype: "" + } + member { + name: "tensor_rank" + mtype: "" + } + member_method { + name: "__init__" + argspec: "args=[\'self\', \'operator\', \'is_non_singular\', \'is_self_adjoint\', \'is_positive_definite\', \'is_square\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " + } + member_method { + name: "add_to_tensor" + argspec: "args=[\'self\', \'x\', \'name\'], varargs=None, keywords=None, defaults=[\'add_to_tensor\'], " + } + member_method { + name: "assert_non_singular" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'assert_non_singular\'], " + } + member_method { + name: "assert_positive_definite" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'assert_positive_definite\'], " + } + member_method { + name: "assert_self_adjoint" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'assert_self_adjoint\'], " + } + member_method { + name: "batch_shape_tensor" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'batch_shape_tensor\'], " + } + member_method { + name: "cholesky" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'cholesky\'], " + } + member_method { + name: "determinant" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'det\'], " + } + member_method { + name: "diag_part" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'diag_part\'], " + } + member_method { + name: "domain_dimension_tensor" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " + } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } + member_method { + name: "log_abs_determinant" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " + } + member_method { + name: "matmul" + argspec: "args=[\'self\', \'x\', \'adjoint\', \'adjoint_arg\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'False\', \'matmul\'], " + } + member_method { + name: "matvec" + argspec: "args=[\'self\', \'x\', \'adjoint\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'matvec\'], " + } + member_method { + name: "range_dimension_tensor" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'range_dimension_tensor\'], " + } + member_method { + name: "shape_tensor" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'shape_tensor\'], " + } + member_method { + name: "solve" + argspec: "args=[\'self\', \'rhs\', \'adjoint\', \'adjoint_arg\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'False\', \'solve\'], " + } + member_method { + name: "solvevec" + argspec: "args=[\'self\', \'rhs\', \'adjoint\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'solve\'], " + } + member_method { + name: "tensor_rank_tensor" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'tensor_rank_tensor\'], " + } + member_method { + name: "to_dense" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'to_dense\'], " + } + member_method { + name: "trace" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'trace\'], " + } +} diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-kronecker.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-kronecker.pbtxt index a9078c8ab5..85d902b977 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-kronecker.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-kronecker.pbtxt @@ -95,6 +95,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-low-rank-update.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-low-rank-update.pbtxt index 4cfa3bb30d..638d82a599 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-low-rank-update.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-low-rank-update.pbtxt @@ -115,6 +115,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-lower-triangular.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-lower-triangular.pbtxt index a87649133f..ab1b04bd3c 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-lower-triangular.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-lower-triangular.pbtxt @@ -91,6 +91,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-scaled-identity.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-scaled-identity.pbtxt index 3265646784..961969aac5 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-scaled-identity.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-scaled-identity.pbtxt @@ -96,6 +96,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-zeros.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-zeros.pbtxt index 49d8890c89..e76738a964 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-zeros.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator-zeros.pbtxt @@ -91,6 +91,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator.pbtxt index c89dc067b3..b35cd69da4 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.-linear-operator.pbtxt @@ -90,6 +90,10 @@ tf_class { name: "domain_dimension_tensor" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'domain_dimension_tensor\'], " } + member_method { + name: "inverse" + argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'inverse\'], " + } member_method { name: "log_abs_determinant" argspec: "args=[\'self\', \'name\'], varargs=None, keywords=None, defaults=[\'log_abs_det\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.pbtxt index 3e1e2e3d54..f9119cdd5f 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.pbtxt @@ -36,6 +36,10 @@ tf_module { name: "LinearOperatorIdentity" mtype: "" } + member { + name: "LinearOperatorInversion" + mtype: "" + } member { name: "LinearOperatorKronecker" mtype: "" -- GitLab From 4b2b456ea4353864478e608a587ea9b5f7eb696c Mon Sep 17 00:00:00 2001 From: Allen Lavoie Date: Wed, 9 Jan 2019 15:00:20 -0800 Subject: [PATCH 580/622] Remove a deprecation warning from tf.saved_model.save PiperOrigin-RevId: 228594973 --- tensorflow/python/saved_model/save.py | 2 +- tensorflow/python/saved_model/utils_impl.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tensorflow/python/saved_model/save.py b/tensorflow/python/saved_model/save.py index d79b5fe3a6..9db6d03ed0 100644 --- a/tensorflow/python/saved_model/save.py +++ b/tensorflow/python/saved_model/save.py @@ -222,7 +222,7 @@ def _normalize_outputs(outputs, function_name, signature_key): def _tensor_dict_to_tensorinfo(tensor_dict): - return {key: utils_impl.build_tensor_info(value) + return {key: utils_impl.build_tensor_info_internal(value) for key, value in tensor_dict.items()} diff --git a/tensorflow/python/saved_model/utils_impl.py b/tensorflow/python/saved_model/utils_impl.py index d43cfdf2b3..a82007fd54 100644 --- a/tensorflow/python/saved_model/utils_impl.py +++ b/tensorflow/python/saved_model/utils_impl.py @@ -60,6 +60,11 @@ def build_tensor_info(tensor): """ if context.executing_eagerly(): raise RuntimeError("build_tensor_info is not supported in Eager mode.") + return build_tensor_info_internal(tensor) + + +def build_tensor_info_internal(tensor): + """Utility function to build TensorInfo proto from a Tensor.""" tensor_info = meta_graph_pb2.TensorInfo( dtype=dtypes.as_dtype(tensor.dtype).as_datatype_enum, tensor_shape=tensor.get_shape().as_proto()) -- GitLab From 00a074f82ebaebb436830b13978b3c24e514869d Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Wed, 9 Jan 2019 15:04:45 -0800 Subject: [PATCH 581/622] Precalculate eager op's cache key, so this somewhat expensive operation doesn't have to be repeated for each inference. PiperOrigin-RevId: 228595957 --- .../core/common_runtime/eager/attr_builder.cc | 13 ++++++++++++- tensorflow/core/common_runtime/eager/attr_builder.h | 11 +++++++++-- .../core/common_runtime/eager/attr_builder_test.cc | 13 +++++++++++++ tensorflow/lite/delegates/flex/kernel.cc | 6 ++++++ 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/tensorflow/core/common_runtime/eager/attr_builder.cc b/tensorflow/core/common_runtime/eager/attr_builder.cc index a750f8cbba..689b04274f 100644 --- a/tensorflow/core/common_runtime/eager/attr_builder.cc +++ b/tensorflow/core/common_runtime/eager/attr_builder.cc @@ -125,6 +125,7 @@ Status AttrTypeMapForOp(const char* op_name, const AttrTypeMap** out, template <> \ AttrBuilder& AttrBuilder::Set(StringPiece attr_name, value_type&& value) { \ value_field.push_back(std::make_pair(string(attr_name), value)); \ + cached_cache_key_ = absl::nullopt; \ return *this; \ } @@ -231,7 +232,17 @@ inline tensorflow::Fprint128 CacheKeyHelper(StringPiece s, uint64 b) { } // namespace -tensorflow::Fprint128 AttrBuilder::CacheKey(const string& device) const { +tensorflow::Fprint128 AttrBuilder::CacheKey(const string& device) { + if (!cached_cache_key_ || device != device_for_cached_cache_key_) { + cached_cache_key_ = BuildCacheKeyForDevice(device); + device_for_cached_cache_key_ = device; + } + + return *cached_cache_key_; +} + +tensorflow::Fprint128 AttrBuilder::BuildCacheKeyForDevice( + const string& device) const { tensorflow::Fprint128 f = tensorflow::Fingerprint128(op_name_); f = tensorflow::FingerprintCat128(f, tensorflow::Fingerprint128(device)); if (node_def_ != nullptr) { diff --git a/tensorflow/core/common_runtime/eager/attr_builder.h b/tensorflow/core/common_runtime/eager/attr_builder.h index 5e0172dfd3..aa64b5f59b 100644 --- a/tensorflow/core/common_runtime/eager/attr_builder.h +++ b/tensorflow/core/common_runtime/eager/attr_builder.h @@ -28,6 +28,7 @@ limitations under the License. #include "tensorflow/core/framework/types.h" #include "tensorflow/core/lib/core/status.h" #include "tensorflow/core/lib/gtl/inlined_vector.h" +#include "tensorflow/core/lib/gtl/optional.h" #include "tensorflow/core/platform/fingerprint.h" #include "tensorflow/core/util/tensor_slice_reader_cache.h" @@ -74,7 +75,7 @@ Status AttrTypeByName(const AttrTypeMap& m, const string& attr_name, // AttrBuilder a; // a.NumInputs(2); // a.Set("T", TF_FLOAT); -// uint64 cache_key = a.CacheKey("cpu:0"); +// tensorflow::Fprint128 cache_key = a.CacheKey("cpu:0"); // const NodeDef& n = a.BuildNodeDef(); // // Note that all calls to Set and NumInputs should happen before calling @@ -100,10 +101,11 @@ class AttrBuilder { AttrBuilder& Set(StringPiece attr_name, T&& value) { MayBeInitializeNodeDef(); SetInAttrValueMap(node_def_->mutable_attr(), string(attr_name), value); + cached_cache_key_ = absl::nullopt; return *this; } - tensorflow::Fprint128 CacheKey(const string& device) const; + tensorflow::Fprint128 CacheKey(const string& device); void FillAttrValueMap(AttrValueMap* m) const { FillAttrValueMap(m, true); } const NodeDef& BuildNodeDef(); @@ -112,6 +114,8 @@ class AttrBuilder { template using AttrVec = tensorflow::gtl::InlinedVector, 2>; + tensorflow::Fprint128 BuildCacheKeyForDevice(const string& device) const; + void MayBeInitializeNodeDef(); // Fill `m` with the attr-value pairs set via AttrBuilder::Set() so far, as // well as any default attr-value pairs from the associated op_def, if there @@ -148,6 +152,9 @@ class AttrBuilder { int num_inputs_; std::unique_ptr node_def_; bool node_def_finalized_; + + absl::optional cached_cache_key_; + string device_for_cached_cache_key_; }; // namespace tensorflow template <> diff --git a/tensorflow/core/common_runtime/eager/attr_builder_test.cc b/tensorflow/core/common_runtime/eager/attr_builder_test.cc index 220cc6f5ce..8245660bfc 100644 --- a/tensorflow/core/common_runtime/eager/attr_builder_test.cc +++ b/tensorflow/core/common_runtime/eager/attr_builder_test.cc @@ -67,5 +67,18 @@ TEST(AttrTypeMap, Lookup) { EXPECT_NE(is_list, 0); } +TEST(AttrTypeMap, CacheKey) { + AttrBuilder a("op_name"); + a.NumInputs(2); + a.Set("T", TF_FLOAT); + tensorflow::Fprint128 cache_key = a.CacheKey("cpu:0"); + + ASSERT_FALSE(cache_key == a.CacheKey("cpu:1")); + ASSERT_TRUE(cache_key == a.CacheKey("cpu:0")); + + a.Set("x", 1.0); + ASSERT_FALSE(cache_key == a.CacheKey("cpu:0")); +} + } // namespace } // namespace tensorflow diff --git a/tensorflow/lite/delegates/flex/kernel.cc b/tensorflow/lite/delegates/flex/kernel.cc index d2853cf9f8..2e0fc22ad6 100644 --- a/tensorflow/lite/delegates/flex/kernel.cc +++ b/tensorflow/lite/delegates/flex/kernel.cc @@ -245,6 +245,12 @@ class OpNode { op_->MutableAttrs()->Set(attr.first, attr.second); } + // Precalculating a cache key saves about 10% of inference time for very + // small models. + tensorflow::Device* device = op_->Device(); + op_->MutableAttrs()->CacheKey(device == nullptr ? "unspecified" + : device->name()); + return tensorflow::Status::OK(); } -- GitLab From d7cde5bd2cf5d0d7a69773422716590b79c7bed2 Mon Sep 17 00:00:00 2001 From: Yuefeng Zhou Date: Wed, 9 Jan 2019 15:18:43 -0800 Subject: [PATCH 582/622] Local collective all reduce with eager mode: wraps invocations to collective allreduce into a defun; Only broadcast send/recv initial values on the first replica of each worker. PiperOrigin-RevId: 228598348 --- .../python/collective_all_reduce_strategy.py | 71 ++++++++++++------- .../collective_all_reduce_strategy_test.py | 26 ++++--- .../python/distribute/cross_device_ops.py | 15 +--- .../python/distribute/cross_device_utils.py | 28 +++++--- 4 files changed, 85 insertions(+), 55 deletions(-) diff --git a/tensorflow/contrib/distribute/python/collective_all_reduce_strategy.py b/tensorflow/contrib/distribute/python/collective_all_reduce_strategy.py index 39756b32c5..a3538dfc50 100644 --- a/tensorflow/contrib/distribute/python/collective_all_reduce_strategy.py +++ b/tensorflow/contrib/distribute/python/collective_all_reduce_strategy.py @@ -31,6 +31,7 @@ from tensorflow.python.distribute import multi_worker_util from tensorflow.python.distribute import numpy_dataset from tensorflow.python.distribute import values from tensorflow.python.eager import context +from tensorflow.python.eager import tape from tensorflow.python.framework import ops from tensorflow.python.ops import array_ops from tensorflow.python.ops import collective_ops @@ -91,6 +92,7 @@ class CollectiveAllReduceExtended(mirrored_strategy.MirroredExtended): self._collective_keys = cross_device_utils.CollectiveKeys() self._initialize_local(local_devices) + # TODO(yuefengz): remove num_gpus_per_worker from CollectiveAllReduce. self._cross_device_ops = cross_device_ops_lib.CollectiveAllReduce( num_workers=self._num_workers, num_gpus_per_worker=num_gpus_per_worker, @@ -166,16 +168,17 @@ class CollectiveAllReduceExtended(mirrored_strategy.MirroredExtended): else: device_map = colocate_with.device_map logical_device = colocate_with.logical_device - group_size = device_map.num_replicas_in_graph * self._num_workers - group_key = self._collective_keys.get_group_key(self.worker_devices) def _real_mirrored_creator(devices, *args, **kwargs): """Creates one MirroredVariable on the current worker.""" - value_list = [] unique_var_name = ops.get_default_graph().unique_name( kwargs["name"], mark_as_used=False).rstrip("/") + # pylint: disable=protected-access collective_instance_key = self._collective_keys.get_instance_key( key_id=unique_var_name) + # Only the first device participles in the broadcast of initial values. + group_key = self._collective_keys.get_group_key([devices[0]]) + group_size = self._num_workers if "initial_value" not in kwargs: raise ValueError("Initial value must be specified.") initial_value = kwargs["initial_value"] @@ -184,9 +187,33 @@ class CollectiveAllReduceExtended(mirrored_strategy.MirroredExtended): else: initial_value_fn = lambda: initial_value + value_list = [] for i, d in enumerate(devices): - with ops.device(d): - if i > 0: + with ops.init_scope(), ops.device(d): + if i == 0: + # The initial value fn makes sure variables all initialized to + # same values. The first device of the chief worker will send their + # variable values to other workers. + def _overridden_initial_value_fn(device=d, index=i): # pylint: disable=g-missing-docstring + with ops.device(device): + initial_value = initial_value_fn() + assert not callable(initial_value) + initial_value = ops.convert_to_tensor(initial_value) + + assert index == 0, index + if self._num_workers > 1: + if self._is_chief: + bcast_send = collective_ops.broadcast_send( + initial_value, initial_value.shape, initial_value.dtype, + group_size, group_key, collective_instance_key) + with ops.control_dependencies([bcast_send]): + return array_ops.identity(initial_value) + else: + return collective_ops.broadcast_recv( + initial_value.shape, initial_value.dtype, group_size, + group_key, collective_instance_key) + return initial_value + else: # Give replicas meaningful distinct names: var0name = value_list[0].name.split(":")[0] # We append a / to variable names created on replicas with id > 0 to @@ -194,30 +221,22 @@ class CollectiveAllReduceExtended(mirrored_strategy.MirroredExtended): # name as the absolute name of the variable. kwargs["name"] = "%s/replica_%d/" % (var0name, i) - # The initial value fn makes sure variables all initialized to - # same values. The first device of the chief worker will send their - # variable values to other devices and other workers. - def _overridden_initial_value_fn(device=d, index=i): # pylint: disable=g-missing-docstring - with ops.device(device): - initial_value = initial_value_fn() - assert not callable(initial_value) - initial_value = ops.convert_to_tensor(initial_value) - - if self._is_chief and index == 0: - bcast_send = collective_ops.broadcast_send( - initial_value, initial_value.shape, initial_value.dtype, - group_size, group_key, collective_instance_key) - with ops.control_dependencies([bcast_send]): - return array_ops.identity(initial_value) - else: - return collective_ops.broadcast_recv( - initial_value.shape, initial_value.dtype, group_size, - group_key, collective_instance_key) + # Variables on non-first replica get initial values from the + # variables created on the first device of each worker. + def _overridden_initial_value_fn(device=d, index=i): + assert index > 0 + with ops.device(device): + if context.executing_eagerly(): + return array_ops.identity(value_list[0].value()) + else: + return array_ops.identity(value_list[0].initial_value) kwargs["initial_value"] = _overridden_initial_value_fn - with context.context().device_policy(context.DEVICE_PLACEMENT_SILENT): - v = next_creator(*args, **kwargs) + # Don't record operations (e.g. other variable reads) during + # variable creation. + with tape.stop_recording(): + v = next_creator(*args, **kwargs) if i == 0: actual_var_name = v.name.split(":")[0] diff --git a/tensorflow/contrib/distribute/python/collective_all_reduce_strategy_test.py b/tensorflow/contrib/distribute/python/collective_all_reduce_strategy_test.py index c3e9c55e96..682da44f5d 100644 --- a/tensorflow/contrib/distribute/python/collective_all_reduce_strategy_test.py +++ b/tensorflow/contrib/distribute/python/collective_all_reduce_strategy_test.py @@ -192,6 +192,7 @@ class CollectiveAllReduceStrategyTestBase( image = random_ops.random_uniform([2, 28, 28]) label = random_ops.random_uniform([2, 1], maxval=10, dtype=dtypes.int32) logits = model(image, training=True) + # TODO(yuefengz): make loss a callable for eager mode. loss = losses.sparse_softmax_cross_entropy(labels=label, logits=logits) optimizer = adam.AdamOptimizer(learning_rate=1e-4) train_op = optimizer.minimize(loss, @@ -403,24 +404,31 @@ class LocalCollectiveAllReduceStrategy( strategy_test_lib.TwoDeviceDistributionTestBase, parameterized.TestCase): - def testMinimizeLossGraph(self, num_gpus=2): + @combinations.generate( + combinations.combine( + mode=['graph', 'eager'], num_gpus=[2, 4], required_gpus=2)) + def testMinimizeLoss(self, num_gpus): # Collective ops doesn't support strategy with one device. if context.num_gpus() < num_gpus: self.skipTest('Not enough GPUs') - self._test_minimize_loss_graph(None, None, num_gpus) + if context.executing_eagerly(): + strategy, _, _ = self._get_test_object(None, None, num_gpus) + self._test_minimize_loss_eager(strategy) + else: + self._test_minimize_loss_graph(None, None, num_gpus) - def testComplexModel(self, num_gpus=2): - # Collective ops doesn't support strategy with one device. + @combinations.generate( + combinations.combine(mode=['graph'], num_gpus=[2, 4], required_gpus=2)) + def testComplexModel(self, num_gpus): if context.num_gpus() < num_gpus: self.skipTest('Not enough GPUs') self._test_complex_model(None, None, num_gpus) + @combinations.generate( + combinations.combine(mode=['graph', 'eager'], required_gpus=2)) def testMakeInputFnIterator(self, num_gpus=2): - # Collective ops doesn't support strategy with one device. - if context.num_gpus() < num_gpus: - self.skipTest('Not enough GPUs') - dataset_fn = lambda: dataset_ops.Dataset.range(10) - expected_values = [[i, i+1] for i in range(0, 10, 2)] + dataset_fn = lambda: dataset_ops.Dataset.range(5 * num_gpus) + expected_values = [range(i, i + num_gpus) for i in range(0, 10, num_gpus)] input_fn = self._input_fn_to_test_input_context( dataset_fn, diff --git a/tensorflow/python/distribute/cross_device_ops.py b/tensorflow/python/distribute/cross_device_ops.py index 9575301d97..9729302c6d 100644 --- a/tensorflow/python/distribute/cross_device_ops.py +++ b/tensorflow/python/distribute/cross_device_ops.py @@ -323,6 +323,9 @@ class ReductionToOneDeviceCrossDeviceOps(CrossDeviceOps): assert check_destinations(destinations) devices = get_devices_from(destinations) reduce_to_device = self.reduce_to_device or devices[0] + logging.log_first_n( + logging.INFO, + "Reduce to %s then broadcast to %r." % (reduce_to_device, devices), 10) reduced = _simple_reduce(per_replica_value, reduce_to_device, self.accumulation_fn, reduce_op) return self.broadcast(reduced, destinations) @@ -839,9 +842,6 @@ class CollectiveAllReduce(CrossDeviceOps): if cross_device_utils.contains_indexed_slices(per_replica_value): raise ValueError( "`IndexSlices` is not supported for Collective All-Reduce.") - if context.executing_eagerly(): - raise ValueError( - "Eager execution is not supported for Collective All-Reduce") all_reduced = self._batch_all_reduce(reduce_op, [per_replica_value])[0] device_map, logical_device = get_device_map_from(destinations) @@ -865,9 +865,6 @@ class CollectiveAllReduce(CrossDeviceOps): if cross_device_utils.contains_indexed_slices(value_destination_pairs): raise ValueError( "`IndexSlices` is not supported for Collective All-Reduce.") - if context.executing_eagerly(): - raise ValueError( - "Eager execution is not supported for Collective All-Reduce") all_devices_match = _all_devices_match(value_destination_pairs) if all_devices_match: @@ -886,9 +883,6 @@ class CollectiveAllReduce(CrossDeviceOps): def _batch_all_reduce(self, reduce_op, per_replica_values): """All-reduce across all workers in a batch.""" - if context.executing_eagerly(): - raise ValueError( - "Eager execution with collective ops is not supported yet.") logging.log_first_n( logging.INFO, "Collective All-reduce invoked with batches size = %d, " @@ -949,12 +943,9 @@ def _has_dgx1_like_links(gpu_links): def _choose_all_reduce_algorithm(device_links): if _has_dgx1_like_links(device_links): - logging.info("Configured hierarchical_copy with num_packs=%d", - len(device_links)) return AllReduceCrossDeviceOps( "hierarchical_copy", num_packs=len(device_links)) else: - logging.info("Configured nccl all-reduce.") return AllReduceCrossDeviceOps("nccl", num_packs=1) diff --git a/tensorflow/python/distribute/cross_device_utils.py b/tensorflow/python/distribute/cross_device_utils.py index 5b4b3a6f97..e8066dd467 100644 --- a/tensorflow/python/distribute/cross_device_utils.py +++ b/tensorflow/python/distribute/cross_device_utils.py @@ -23,6 +23,8 @@ import threading from tensorflow.python.distribute import all_reduce from tensorflow.python.distribute import values as value_lib +from tensorflow.python.eager import context +from tensorflow.python.eager import def_function from tensorflow.python.framework import device as pydev from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops @@ -353,15 +355,25 @@ def build_collective_reduce(input_tensors, num_devices = len(devices) group_key = collective_keys.get_group_key(devices) instance_key = collective_keys.get_instance_key() - out_tensors = [] subdiv_offsets = [0] # TODO(tucker): maybe support non-default subdiv spec - for d in range(num_devices): - with ops.device(devices[d]): - reduce_op = collective_ops.all_reduce( - input_tensors[d], group_size, group_key, instance_key, reduction_op, - unary_op, subdiv_offsets) - out_tensors.append(reduce_op) - return out_tensors + + def collective_all_reduce(): + """Call collective allreduce.""" + assert not context.executing_eagerly() + out_tensors = [] + for d in range(num_devices): + with ops.device(devices[d]): + reduce_op = collective_ops.all_reduce( + input_tensors[d], group_size, group_key, instance_key, reduction_op, + unary_op, subdiv_offsets) + out_tensors.append(reduce_op) + return out_tensors + + if context.executing_eagerly(): + # Collective ops will block unless they are executed concurrently such as in + # a graph or a defun. + collective_all_reduce = def_function.function(collective_all_reduce) + return collective_all_reduce() def sum_grad_and_var_all_reduce(grad_and_vars, -- GitLab From 615182adb3d01fd8357e574bd8920c0995c6bbb8 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Wed, 9 Jan 2019 15:42:02 -0800 Subject: [PATCH 583/622] Fix issue with Subclassed models and generator input. PiperOrigin-RevId: 228602224 --- tensorflow/python/keras/callbacks.py | 9 +--- tensorflow/python/keras/callbacks_test.py | 42 ------------------- .../python/keras/engine/training_generator.py | 4 -- .../keras/engine/training_generator_test.py | 21 ++++------ 4 files changed, 8 insertions(+), 68 deletions(-) diff --git a/tensorflow/python/keras/callbacks.py b/tensorflow/python/keras/callbacks.py index 589cd992d6..b9074669eb 100644 --- a/tensorflow/python/keras/callbacks.py +++ b/tensorflow/python/keras/callbacks.py @@ -123,14 +123,6 @@ def configure_callbacks(callbacks, 'metrics': callback_metrics, } callback_list.set_params(callback_params) - - if (do_validation and not model._distribution_strategy and - not model.run_eagerly): - # Need to create the eval_function before start of the first epoch - # because TensorBoard callback on_epoch_begin adds summary to the - # list of fetches of the eval_function - callback_model._make_eval_function() - callback_list.model.stop_training = False return callback_list # pylint: enable=protected-access @@ -1373,6 +1365,7 @@ class TensorBoard(Callback): self._epoch = epoch # pylint: disable=protected-access # add the histogram summary op if it should run this epoch + self.model._make_eval_function() if self.merged not in self.model._eval_function.fetches: self.model._eval_function.fetches.append(self.merged) self.model._eval_function.fetch_callbacks[ diff --git a/tensorflow/python/keras/callbacks_test.py b/tensorflow/python/keras/callbacks_test.py index ef469c5e4f..d5ffdf789a 100644 --- a/tensorflow/python/keras/callbacks_test.py +++ b/tensorflow/python/keras/callbacks_test.py @@ -31,7 +31,6 @@ import numpy as np from tensorflow.core.framework import summary_pb2 from tensorflow.python import keras -from tensorflow.python.framework import ops from tensorflow.python.framework import random_seed from tensorflow.python.framework import test_util from tensorflow.python.keras import keras_parameterized @@ -1397,47 +1396,6 @@ class KerasCallbacksTest(test.TestCase): callbacks=cbks, epochs=1) - @test_util.run_deprecated_v1 - def test_fit_generator_with_callback(self): - - class TestCallback(keras.callbacks.Callback): - - def set_model(self, model): - # Check the model operations for the optimizer operations that - # the _make_train_function adds under a named scope for the - # optimizer. This ensurs the full model is populated before the - # set_model callback is called. - optimizer_name_scope = 'training/' + model.optimizer.__class__.__name__ - graph_def = ops.get_default_graph().as_graph_def() - for node in graph_def.node: - if node.name.startswith(optimizer_name_scope): - return - raise RuntimeError('The optimizer operations are not present in the ' - 'model graph when the Callback.set_model function ' - 'is called') - np.random.seed(1337) - - def generator(): - x = np.random.randn(10, 100).astype(np.float32) - y = np.random.randn(10, 10).astype(np.float32) - while True: - yield x, y - - with self.cached_session(): - model = testing_utils.get_small_sequential_mlp( - num_hidden=10, num_classes=10, input_dim=100) - model.compile( - loss='categorical_crossentropy', - optimizer='sgd', - metrics=['accuracy']) - model.fit_generator( - generator(), - steps_per_epoch=2, - epochs=1, - validation_data=generator(), - validation_steps=2, - callbacks=[TestCallback()], - verbose=0) if __name__ == '__main__': test.main() diff --git a/tensorflow/python/keras/engine/training_generator.py b/tensorflow/python/keras/engine/training_generator.py index d1699b2827..07de934259 100644 --- a/tensorflow/python/keras/engine/training_generator.py +++ b/tensorflow/python/keras/engine/training_generator.py @@ -430,12 +430,8 @@ def _make_enqueued_generator(generator, def _make_execution_function(model, mode, class_weight=None): """Makes function to run one step of model execution.""" if mode == ModeKeys.TRAIN: - if not context.executing_eagerly(): - model._make_fit_function() f = functools.partial(model.train_on_batch, class_weight=class_weight) elif mode == ModeKeys.TEST: - if not context.executing_eagerly(): - model._make_eval_function() f = model.test_on_batch else: # Match signature of other modes to allow diff --git a/tensorflow/python/keras/engine/training_generator_test.py b/tensorflow/python/keras/engine/training_generator_test.py index 90c45dfcb7..6b754c18b3 100644 --- a/tensorflow/python/keras/engine/training_generator_test.py +++ b/tensorflow/python/keras/engine/training_generator_test.py @@ -66,8 +66,7 @@ class TestGeneratorMethods(keras_parameterized.TestCase): @unittest.skipIf( os.name == 'nt', 'use_multiprocessing=True does not work on windows properly.') - # TODO(b/120940700): Bug with subclassed model inputs. - @keras_parameterized.run_with_all_model_types(exclude_models='subclass') + @keras_parameterized.run_with_all_model_types @keras_parameterized.run_all_keras_modes def test_fit_generator_method(self): model = testing_utils.get_small_mlp( @@ -107,8 +106,7 @@ class TestGeneratorMethods(keras_parameterized.TestCase): @unittest.skipIf( os.name == 'nt', 'use_multiprocessing=True does not work on windows properly.') - # TODO(b/120940700): Bug with subclassed model inputs. - @keras_parameterized.run_with_all_model_types(exclude_models='subclass') + @keras_parameterized.run_with_all_model_types @keras_parameterized.run_all_keras_modes def test_evaluate_generator_method(self): model = testing_utils.get_small_mlp( @@ -173,8 +171,7 @@ class TestGeneratorMethods(keras_parameterized.TestCase): max_queue_size=10, workers=0) - # TODO(b/120940700): Bug with subclassed model inputs. - @keras_parameterized.run_with_all_model_types(exclude_models='subclass') + @keras_parameterized.run_with_all_model_types @keras_parameterized.run_all_keras_modes def test_generator_methods_with_sample_weights(self): model = testing_utils.get_small_mlp( @@ -208,8 +205,7 @@ class TestGeneratorMethods(keras_parameterized.TestCase): max_queue_size=10, use_multiprocessing=False) - # TODO(b/120940700): Bug with subclassed model inputs. - @keras_parameterized.run_with_all_model_types(exclude_models='subclass') + @keras_parameterized.run_with_all_model_types @keras_parameterized.run_all_keras_modes def test_generator_methods_invalid_use_case(self): @@ -249,8 +245,7 @@ class TestGeneratorMethods(keras_parameterized.TestCase): max_queue_size=10, use_multiprocessing=False) - # TODO(b/120940700): Bug with subclassed model inputs. - @keras_parameterized.run_with_all_model_types(exclude_models='subclass') + @keras_parameterized.run_with_all_model_types @keras_parameterized.run_all_keras_modes def test_generator_input_to_fit_eval_predict(self): val_data = np.ones([10, 10], np.float32), np.ones([10, 1], np.float32) @@ -275,8 +270,7 @@ class TestGeneratorMethods(keras_parameterized.TestCase): class TestGeneratorMethodsWithSequences(keras_parameterized.TestCase): - # TODO(b/120940700): Bug with subclassed model inputs. - @keras_parameterized.run_with_all_model_types(exclude_models='subclass') + @keras_parameterized.run_with_all_model_types @keras_parameterized.run_all_keras_modes def test_training_with_sequences(self): @@ -307,8 +301,7 @@ class TestGeneratorMethodsWithSequences(keras_parameterized.TestCase): workers=0, use_multiprocessing=False) - # TODO(b/120940700): Bug with subclassed model inputs. - @keras_parameterized.run_with_all_model_types(exclude_models='subclass') + @keras_parameterized.run_with_all_model_types @keras_parameterized.run_all_keras_modes def test_sequence_input_to_fit_eval_predict(self): val_data = np.ones([10, 10], np.float32), np.ones([10, 1], np.float32) -- GitLab From fe13148bf6afcb85d71c05391d168302d8d4bf2d Mon Sep 17 00:00:00 2001 From: Frank Chen Date: Wed, 9 Jan 2019 16:24:30 -0800 Subject: [PATCH 584/622] Change TPU Cluster Resolver doc string to clarify current / future features PiperOrigin-RevId: 228610010 --- .../cluster_resolver/tpu_cluster_resolver.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tensorflow/python/distribute/cluster_resolver/tpu_cluster_resolver.py b/tensorflow/python/distribute/cluster_resolver/tpu_cluster_resolver.py index 529a443412..72b9990701 100644 --- a/tensorflow/python/distribute/cluster_resolver/tpu_cluster_resolver.py +++ b/tensorflow/python/distribute/cluster_resolver/tpu_cluster_resolver.py @@ -192,11 +192,12 @@ class TPUClusterResolver(ClusterResolver): for the IP addresses and ports of each Cloud TPU listed. Args: - tpu: Either a string, or a list of strings corresponding to the TPUs to - use. If the single string is the empty string, the string 'local', or a - string that begins with 'grpc://' or '/bns', then it is assumed to not - correspond with a Cloud TPU and will instead be passed as the session - master and no ClusterSpec propagation will be done. + tpu: A string corresponding to the TPU to use. If the string is the empty + string, the string 'local', or a string that begins with 'grpc://' or + '/bns', then it is assumed to not correspond with a Cloud TPU and will + instead be passed as the session master and no ClusterSpec propagation + will be done. In the future, this may also support a list of strings + when multiple Cloud TPUs are used. zone: Zone where the TPUs are located. If omitted or empty, we will assume that the zone of the TPU is the same as the zone of the GCE VM, which we will try to discover from the GCE metadata service. -- GitLab From 4bb6da8e92da8d75456ee00b264e4c02877c5d7d Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Wed, 9 Jan 2019 16:35:23 -0800 Subject: [PATCH 585/622] Enable TPUEstimator to ignore non predict mode exporters rather than just hard failing. PiperOrigin-RevId: 228611812 --- tensorflow/contrib/tpu/python/tpu/tpu_estimator.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tensorflow/contrib/tpu/python/tpu/tpu_estimator.py b/tensorflow/contrib/tpu/python/tpu/tpu_estimator.py index 2317300041..2df5b3e4eb 100644 --- a/tensorflow/contrib/tpu/python/tpu/tpu_estimator.py +++ b/tensorflow/contrib/tpu/python/tpu/tpu_estimator.py @@ -2186,7 +2186,9 @@ class TPUEstimator(estimator_lib.Estimator): eval_on_tpu: If False, evaluation runs on CPU or GPU. In this case, the model_fn must return `EstimatorSpec` when called with `mode` as `EVAL`. export_to_tpu: If True, `export_savedmodel()` exports a metagraph for - serving on TPU besides the one on CPU. + serving on TPU besides the one on CPU. Note that unsupported export + modes such as EVAL will be ignored. For those modes, only a CPU model + will be exported. Currently, export_to_tpu only supports PREDICT. warm_start_from: Optional string filepath to a checkpoint or SavedModel to warm-start from, or a `tf.estimator.WarmStartSettings` object to fully configure warm-starting. If the string filepath is provided instead of @@ -2277,10 +2279,9 @@ class TPUEstimator(estimator_lib.Estimator): export_tags=None, check_variables=True): if self._export_to_tpu and mode != model_fn_lib.ModeKeys.PREDICT: - raise NotImplementedError( - 'TPUEstimator only handles mode PREDICT for exporting ' - 'when `export_to_tpu` is `True`; ' - 'got {}.'.format(mode)) + logging.warning('TPUEstimator only handles mode PREDICT for exporting ' + 'when `export_to_tpu` is `True`; Mode {} will be ignored ' + 'for TPU.'.format(mode)) (super(TPUEstimator, self)._add_meta_graph_for_mode( builder, @@ -2291,7 +2292,7 @@ class TPUEstimator(estimator_lib.Estimator): export_tags=export_tags, check_variables=check_variables)) - if self._export_to_tpu: + if self._export_to_tpu and mode == model_fn_lib.ModeKeys.PREDICT: input_receiver_fn_map = { _REWRITE_FOR_INFERENCE_MODE: input_receiver_fn_map[mode] } -- GitLab From 1a768c9dc3895b3188b19d36a630f0e8ba5c85a0 Mon Sep 17 00:00:00 2001 From: Yunxing Dai Date: Wed, 9 Jan 2019 16:46:17 -0800 Subject: [PATCH 586/622] Populate entry parameter's dynamic dimension when using XlaBuilder::SetDynamicBinding Since now that we have is_dynamic_dimension field, we can set the field when calling SetDynamicBinding. ShapeInference will soon start populating this field based on the parameter shape. PiperOrigin-RevId: 228613510 --- tensorflow/compiler/xla/client/xla_builder.cc | 23 +++++++++++++++++++ .../compiler/xla/client/xla_builder_test.cc | 20 ++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/tensorflow/compiler/xla/client/xla_builder.cc b/tensorflow/compiler/xla/client/xla_builder.cc index 7a04c0ec69..59e156eb72 100644 --- a/tensorflow/compiler/xla/client/xla_builder.cc +++ b/tensorflow/compiler/xla/client/xla_builder.cc @@ -247,6 +247,29 @@ Status XlaBuilder::SetDynamicBinding(int64 dynamic_size_param_num, int64 target_param_num, ShapeIndex target_param_index, int64 target_dim_num) { + bool param_exists = false; + for (HloInstructionProto& instr : instructions_) { + if (instr.opcode() == HloOpcodeString(HloOpcode::kParameter) && + instr.parameter_number() == target_param_num) { + param_exists = true; + Shape param_shape(instr.shape()); + Shape* param_shape_ptr = ¶m_shape; + for (int64 index : target_param_index) { + param_shape_ptr = param_shape_ptr->mutable_tuple_shapes(index); + } + param_shape_ptr->set_dynamic_dimension(target_dim_num, + /*is_dynamic=*/true); + *instr.mutable_shape() = param_shape.ToProto(); + } + } + + if (!param_exists) { + return InvalidArgument( + "Asked to mark parameter %lld as dynamic sized parameter, but the " + "doesn't exists", + target_param_num); + } + TF_RETURN_IF_ERROR(dynamic_parameter_binding_.Bind( DynamicParameterBinding::DynamicParameter{dynamic_size_param_num, dynamic_size_param_index}, diff --git a/tensorflow/compiler/xla/client/xla_builder_test.cc b/tensorflow/compiler/xla/client/xla_builder_test.cc index ba929a1200..abc11b4732 100644 --- a/tensorflow/compiler/xla/client/xla_builder_test.cc +++ b/tensorflow/compiler/xla/client/xla_builder_test.cc @@ -446,6 +446,26 @@ TEST_F(XlaBuilderTest, ProtoMatches) { EXPECT_EQ(c0_string, c1_string); } +TEST_F(XlaBuilderTest, DynamicParameter) { + std::vector computations; + XlaBuilder b("builder"); + Shape tuple_param_shape = ShapeUtil::MakeTupleShape( + {ShapeUtil::MakeShape(F32, {5}), ShapeUtil::MakeShape(F32, {6})}); + auto p0 = Parameter(&b, 0, tuple_param_shape, "p0"); + Parameter(&b, 1, ShapeUtil::MakeShape(U32, {}), "p1"); + ASSERT_IS_OK(b.SetDynamicBinding(/*dynamic_size_param_num=*/1, + /*dynamic_size_param_index=*/{}, + /*target_param_num=*/0, + /*target_param_index=*/{1}, + /*target_dim_num=*/0)); + TF_ASSERT_OK_AND_ASSIGN(auto module, BuildHloModule(&b, /*root=*/p0)); + const Shape& param_shape = module->entry_computation() + ->parameter_instruction(0) + ->shape() + .tuple_shapes(1); + EXPECT_TRUE(param_shape.is_dynamic_dimension(0)); +} + TEST_F(XlaBuilderTest, AfterAllWithNonTokenOperands) { XlaBuilder b(TestName()); AfterAll(&b, {CreateToken(&b), ConstantR0(&b, 1.0)}); -- GitLab From 8e55a2f3fe768451b0e1ac8ae657f5ecfcdd0123 Mon Sep 17 00:00:00 2001 From: Dimitris Vardoulakis Date: Wed, 9 Jan 2019 16:55:33 -0800 Subject: [PATCH 587/622] [TF/XLA] Fix bug in AR/CRS combiner. Don't recur to the arguments when comparing cross-module AllReduces for equality. PiperOrigin-RevId: 228615009 --- tensorflow/compiler/xla/service/ar_crs_combiner.cc | 12 +++++++++--- .../compiler/xla/service/ar_crs_combiner_test.cc | 5 +++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/tensorflow/compiler/xla/service/ar_crs_combiner.cc b/tensorflow/compiler/xla/service/ar_crs_combiner.cc index 4a227d3b5c..35a32f537b 100644 --- a/tensorflow/compiler/xla/service/ar_crs_combiner.cc +++ b/tensorflow/compiler/xla/service/ar_crs_combiner.cc @@ -190,6 +190,15 @@ bool ArCrsCombiner::InstructionsComputeSameValue( if (opcode1 != i2->opcode() || operands1.size() != i2->operands().size()) { return false; } + auto eq_computations = [](const HloComputation* a, const HloComputation* b) { + return *a == *b; + }; + if (i1->IsCrossModuleAllReduce()) { + return i1->Identical(*i2, + /*eq_operands=*/std::equal_to(), + eq_computations, + /*layout_sensitive=*/false); + } visited_pairs->emplace(min_uid, max_uid); for (int i = 0; i < operands1.size(); ++i) { auto operand1 = operands1[i]; @@ -215,9 +224,6 @@ bool ArCrsCombiner::InstructionsComputeSameValue( // InstructionsComputeSameValue earlier. auto eq_instructions = [](const HloInstruction* i1, const HloInstruction* i2) -> bool { return true; }; - auto eq_computations = [](const HloComputation* a, const HloComputation* b) { - return *a == *b; - }; return i1->Identical(*i2, eq_instructions, eq_computations, /*layout_sensitive=*/false); } diff --git a/tensorflow/compiler/xla/service/ar_crs_combiner_test.cc b/tensorflow/compiler/xla/service/ar_crs_combiner_test.cc index caa57296f4..b12b63b2dd 100644 --- a/tensorflow/compiler/xla/service/ar_crs_combiner_test.cc +++ b/tensorflow/compiler/xla/service/ar_crs_combiner_test.cc @@ -360,6 +360,7 @@ HloModule foobar ENTRY %entrycomp (p: bf16[]) -> (f32[], f32[]) { %p = bf16[] parameter(0) + %constant.bf16 = bf16[] constant(1) %all-reduce.ar.1 = bf16[] all-reduce(%p), @@ -377,7 +378,7 @@ ENTRY %entrycomp (p: bf16[]) -> (f32[], f32[]) { sharding={maximal device=0} %all-reduce.ar.2 = bf16[] - all-reduce(%p), + all-reduce(%constant.bf16), replica_groups={{0},{1}}, all_reduce_id=1, to_apply=%sum.bf16, @@ -407,7 +408,7 @@ ENTRY %entrycomp (p: bf16[]) -> (f32[], f32[]) { EXPECT_TRUE(changed); EXPECT_THAT(module->entry_computation()->root_instruction(), op::Tuple(op::AllReduce(op::Convert(op::Parameter())), - op::AllReduce(op::Convert(op::Parameter())))); + op::AllReduce(op::Convert(op::Constant())))); auto crs_after = module->entry_computation()->root_instruction()->operands()[0]; auto replica_groups_after = crs_after->replica_groups(); -- GitLab From 7f9c7afd8829d20df25ee04f1999074290d7a45a Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Wed, 9 Jan 2019 16:58:27 -0800 Subject: [PATCH 588/622] Fix AllOutputValuesKnown logic. PiperOrigin-RevId: 228615404 --- .../core/grappler/costs/graph_properties.cc | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/tensorflow/core/grappler/costs/graph_properties.cc b/tensorflow/core/grappler/costs/graph_properties.cc index 863a5087ae..e869f7830c 100644 --- a/tensorflow/core/grappler/costs/graph_properties.cc +++ b/tensorflow/core/grappler/costs/graph_properties.cc @@ -1087,15 +1087,20 @@ class SymbolicShapeRefiner { c->output_tensor_protos.size() < ic->num_outputs()) { return false; } else { + // Checks if we can get output value via either output_tensor_proto or + // output_tensors_as_shapes. for (int i = 0; i < ic->num_outputs(); i++) { - if (c->output_tensor_protos.size() <= i || - c->output_tensor_protos[i] == nullptr) { - return false; + if (c->output_tensor_protos.size() > i && + c->output_tensor_protos[i] != nullptr) { + continue; } - if (c->output_tensors_as_shapes.size() <= i || - !ic->FullyDefined(c->output_tensors_as_shapes[i])) { - return false; + if (c->output_tensors_as_shapes.size() > i && + ic->FullyDefined(c->output_tensors_as_shapes[i])) { + continue; } + + // Unknown for output[i]. + return false; } } return true; -- GitLab From c2aa5390828b39863f9baaa2e5baad34788c7dec Mon Sep 17 00:00:00 2001 From: Jian Li Date: Wed, 9 Jan 2019 17:11:46 -0800 Subject: [PATCH 589/622] Update basic rnn test case. PiperOrigin-RevId: 228617690 --- tensorflow/lite/kernels/basic_rnn_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/lite/kernels/basic_rnn_test.cc b/tensorflow/lite/kernels/basic_rnn_test.cc index c71849fff3..9eb20444a6 100644 --- a/tensorflow/lite/kernels/basic_rnn_test.cc +++ b/tensorflow/lite/kernels/basic_rnn_test.cc @@ -240,7 +240,7 @@ class HybridRNNOpModel : public RNNOpModel { TensorType tensor_type_; - void SetWeights(int weights_idx, std::vector f) { + void SetWeights(int weights_idx, const std::vector& f) { if (tensor_type_ == TensorType_UINT8) { SymmetricQuantizeAndPopulate(weights_idx, f); } else { -- GitLab From 12f810aa76194935959cc0d414f7de6eeaa234b9 Mon Sep 17 00:00:00 2001 From: Bixia Zheng Date: Wed, 9 Jan 2019 17:14:32 -0800 Subject: [PATCH 590/622] [XLA:GPU] Enhance unrolling heuristics for column reduction. Previously, we enable unrolling only when the reduce operands are of small data types. This change adds a simple analysis to count the number of tensors that can be vectorized and can't be vectorized in order to decide whether unrolling is beneficial for the kernel. Add test cases. PiperOrigin-RevId: 228618121 --- .../xla/service/gpu/ir_emitter_unnested.cc | 106 ++++++++++++-- .../xla/service/gpu/ir_emitter_unnested.h | 8 +- .../gpu/tests/gpu_kernel_tiling_test.cc | 129 +++++++++++++++++- 3 files changed, 228 insertions(+), 15 deletions(-) diff --git a/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.cc b/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.cc index 2bc4912155..34ddeb1d41 100644 --- a/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.cc +++ b/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.cc @@ -3403,11 +3403,101 @@ std::tuple GetReductionToVectorDimensions( return std::make_tuple(num_reduced_major, num_kept, num_reduced_minor); } +// Returns true if all the transitive users of hlo before hitting users in +// use_chain_endings are elementwise operations. +bool AreUsersElementwise(const HloInstruction* hlo, + const ConstHloInstructionSet& use_chain_endings) { + return absl::c_all_of(hlo->users(), [&](const HloInstruction* user) { + return use_chain_endings.count(user) || + (user->IsElementwise() && + AreUsersElementwise(user, use_chain_endings)); + }); +} + +// Returns the number of fusion inputs that have the same dimension as the +// given shape, and involve in only elementwise operations. +int64 NumInputsInvolveInOnlyElementwiseOps( + const HloInstruction* unnested_hlo, const Shape& op_shape, + const ConstHloInstructionSet& use_chain_endings) { + return absl::c_count_if( + unnested_hlo->fused_parameters(), [&](const HloInstruction* parameter) { + const Shape& parameter_shape = parameter->shape(); + return ShapeUtil::SameDimensions(op_shape, parameter_shape) && + AreUsersElementwise(parameter, use_chain_endings); + }); +} + +// Returns the number of fusion inputs that have more elements than the given +// shape. +int64 NumInputsWithMoreElementsThan(const HloInstruction* unnested_hlo, + const Shape& shape) { + int64 num_elements = ShapeUtil::ElementsIn(shape); + return absl::c_count_if( + unnested_hlo->fused_parameters(), [&](const HloInstruction* parameter) { + return ShapeUtil::ElementsIn(parameter->shape()) > num_elements; + }); +} + +// The benefit of unrolling a kInput fusion that is a column reduction comes +// from the vectorization of non-reduction fusion outputs and fusion inputs. +// On the other hand, unrolling can also introduce factors that can cause +// the kernel to run slower. This routine uses a simple heuristic to estimate +// the benefit as well as the overhead of unrolling in order to decide whether +// unrolling is beneficial for the given kInput fusion. +bool IsUnrollingColumnReductionBeneficial(const HloInstruction* unnested_hlo, + const Shape& input_shape, + int64 num_kept) { + // TODO(b/122468062): Need further investigate to see whether we can + // remove the constraint on IsPowerOfTwo. + if (!IsPowerOfTwo(static_cast(num_kept))) { + return false; + } + + if (unnested_hlo->opcode() == HloOpcode::kReduce) { + return true; + } + + CHECK_EQ(unnested_hlo->opcode(), HloOpcode::kFusion); + int64 can_be_vectorized = 0; + int64 cannot_be_vectorized = 0; + const HloInstruction* fused_root = unnested_hlo->fused_expression_root(); + ConstHloInstructionSet use_chain_endings; + if (fused_root->opcode() == HloOpcode::kReduce) { + use_chain_endings.insert(fused_root); + // Atomic.add of the reduction result can't be vectorized. + cannot_be_vectorized++; + } else { + CHECK_EQ(fused_root->opcode(), HloOpcode::kTuple); + for (const HloInstruction* instr : fused_root->operands()) { + if (instr->opcode() == HloOpcode::kReduce) { + // Atomic.add of the reduction result can't be vectorized. + cannot_be_vectorized++; + } else { + // Write of the non-reduction result can be vectorized. + can_be_vectorized++; + } + use_chain_endings.insert(instr); + } + } + // Fusion inputs that have the same dimension as the reduce input and + // only involve in elementwise operations can be vectorized. + can_be_vectorized += NumInputsInvolveInOnlyElementwiseOps( + unnested_hlo, input_shape, use_chain_endings); + // Fusion inputs with more elements than the reduce op input must participate + // in non-elementwise operations and we assume that they are not vectorizable + // for the purpose of estimating the benefit of unrolling. If the kernel is + // unrolled even with such an assumption, and the accesses to those inputs + // turn out to be vectorizable, the compiler will still vectorize them. + cannot_be_vectorized += + NumInputsWithMoreElementsThan(unnested_hlo, input_shape); + return can_be_vectorized >= cannot_be_vectorized; +} + } // namespace std::tuple IrEmitterUnnested::ComputeMappingSchemeAndReductionKind( - const HloInstruction* first_reduce) { + const HloInstruction* unnested_hlo, const HloInstruction* first_reduce) { int64 depth = 1; int64 height = 1; int64 width = 1; @@ -3437,15 +3527,6 @@ IrEmitterUnnested::ComputeMappingSchemeAndReductionKind( height = num_reduced_major; width = num_kept; is_row_reduction = false; - // Assume unrolling is beneficial only when we can vectorize the loads - // of small data types. - auto is_unrolling_beneficial = [&] { - // TODO(b/122468062): Need further investigate to see whether we can - // remove the constraint on IsPowerOfTwo. - return IsPowerOfTwo(static_cast(num_kept)) && - primitive_util::BitWidth( - first_reduce->operand(0)->shape().element_type()) <= 16; - }; // Column reduction without transpose doesn't require communication among // threads processing elements in the same tile. The current implementation // only support the use of one hardware thread block to process one block of @@ -3454,7 +3535,8 @@ IrEmitterUnnested::ComputeMappingSchemeAndReductionKind( // num_threads_x and tile_size_x to allow a bigger hardware thread block. int64 hw_threads_per_block_limit = ThreadsPerBlockLimit(ir_emitter_context_->device_description()); - if (is_unrolling_beneficial()) { + if (IsUnrollingColumnReductionBeneficial(unnested_hlo, input_shape, + num_kept)) { tile_size_x = std::min(2 * hw_threads_per_block_limit, num_kept); num_threads_x = tile_size_x / 2; dilated_x = false; @@ -3539,7 +3621,7 @@ Status IrEmitterUnnested::EmitReductionToVector(HloInstruction* unnested_hlo) { bool is_row_reduction; llvm_ir::KernelMappingScheme mapping_scheme; std::tie(mapping_scheme, is_row_reduction) = - ComputeMappingSchemeAndReductionKind(first_reduce); + ComputeMappingSchemeAndReductionKind(unnested_hlo, first_reduce); ReductionCodegenInfo reduction_info(&mapping_scheme, is_row_reduction); KernelCodeGenerator kernel_generator( /*tile_element_generator=*/ diff --git a/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.h b/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.h index eb0ea90e14..21b842bb2c 100644 --- a/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.h +++ b/tensorflow/compiler/xla/service/gpu/ir_emitter_unnested.h @@ -217,9 +217,13 @@ class IrEmitterUnnested : public IrEmitter { Status EmitReductionToVector(HloInstruction* unnested_hlo); // Computes the KernelMappingScheme for the reduce HLO and indicates whether - // the reduction is a row reduction. + // the reduction is a row reduction. For an un-fused reduce op, unnested_hlo + // and first_reduce are the same instruction. For a kInput fusion, + // unnested_hlo is the fusion instruction while first_reduce is the first + // reduce op. std::tuple - ComputeMappingSchemeAndReductionKind(const HloInstruction* first_reduce); + ComputeMappingSchemeAndReductionKind(const HloInstruction* unnested_hlo, + const HloInstruction* first_reduce); // Emits code for an in-place scatter, modifying `thunk`s launch dimensions in // the process. `scatter` may be fused, scatter indices are taken from diff --git a/tensorflow/compiler/xla/service/gpu/tests/gpu_kernel_tiling_test.cc b/tensorflow/compiler/xla/service/gpu/tests/gpu_kernel_tiling_test.cc index 8004ebdc8e..869724db60 100644 --- a/tensorflow/compiler/xla/service/gpu/tests/gpu_kernel_tiling_test.cc +++ b/tensorflow/compiler/xla/service/gpu/tests/gpu_kernel_tiling_test.cc @@ -270,7 +270,7 @@ TEST_F(GpuKernelTilingTest, TransposedInputWithoutUnsafeUseTiled) { kind=kLoop, calls=fused_computation })"; - // Check that a call to llvm.nvvm.barrier0 is not generated. + // Check that a call to llvm.nvvm.barrier0 is generated. auto hlo_module = ParseHloString(kHloString, ConfigWithoutLayoutAssignment()).ValueOrDie(); CompileAndVerifyIr(std::move(hlo_module), @@ -284,6 +284,133 @@ TEST_F(GpuKernelTilingTest, TransposedInputWithoutUnsafeUseTiled) { EXPECT_TRUE(RunAndCompareNoHloPasses(kHloString, ErrorSpec{0.0})); } +TEST_F(GpuKernelTilingTest, ColumnReductionWithPowerOf2OutputElementsUnrolled) { + const char *const kHloString = R"( + HloModule column_reduce_powerof2 + + reduction { + x = f32[] parameter(0) + y = f32[] parameter(1) + ROOT add = f32[] add(x, y) + } + + ENTRY kernel_entry { + constant0 = f32[] constant(0) + arg1 = f16[1024,512]{1,0} parameter(0) + arg1_conv = f32[1024,512]{1,0} convert(arg1) + ROOT reduce = f32[512]{0} reduce(arg1_conv, constant0), dimensions={0}, to_apply=reduction + })"; + + // Check that two calls to llvm.nvvm.atomic are generated. + auto hlo_module = + ParseHloString(kHloString, ConfigWithoutLayoutAssignment()).ValueOrDie(); + CompileAndVerifyIr(std::move(hlo_module), + R"( +; CHECK-LABEL: define void @fusion +; CHECK: call float @llvm.nvvm.atomic.load.add.f32.p0f32 +; CHECK: call float @llvm.nvvm.atomic.load.add.f32.p0f32 +; CHECK-NOT: call float @llvm.nvvm.atomic.load.add.f32.p0f32 +; CHECK: } +)", + /*match_optimized_ir=*/true); + // Check that the kernel runs correctly. + EXPECT_TRUE(RunAndCompareNoHloPasses(kHloString, ErrorSpec{1.0e-5, 1.0e-5})); +} + +TEST_F(GpuKernelTilingTest, + ColumnReductionWithInputLargerThenReduceInputNotUnrolled) { + const char *const kHloString = R"( + HloModule larger_than_reduce_input_parameter + + reduction22 { + x = f32[] parameter(0) + y = f32[] parameter(1) + ROOT add = f32[] add(x, y) + } + + fused_computation { + constant0 = f32[] constant(0) + arg.1 = f16[1024,512]{1,0} parameter(0) + arg.2 = f16[1027,513]{1,0} parameter(1) + arg1.conv = f32[1024,512]{1,0} convert(arg.1) + arg2.conv = f32[1027,513]{1,0} convert(arg.2) + slice2 = f32[1024,512]{1,0} slice(arg2.conv), slice={[2:1026], [1:513]} + add2 = f32[1024,512]{1,0} add(arg1.conv, slice2) + ROOT reduce = f32[512]{0} reduce(add2, constant0), dimensions={0}, + to_apply=reduction22 + } + + ENTRY kernel_entry { + arg1 = f16[1024,512]{1,0} parameter(0) + arg2 = f16[1027,513]{1,0} parameter(1) + ROOT fusion = f32[512]{0} fusion(arg1, arg2), kind=kInput, + calls=fused_computation + })"; + + // Check that one call to llvm.nvvm.atomic is generated. + auto hlo_module = + ParseHloString(kHloString, ConfigWithoutLayoutAssignment()).ValueOrDie(); + CompileAndVerifyIr(std::move(hlo_module), + R"( +; CHECK-LABEL: define void @fusion +; CHECK: call float @llvm.nvvm.atomic.load.add.f32.p0f32 +; CHECK-NOT: call float @llvm.nvvm.atomic.load.add.f32.p0f32 +; CHECK: } +)", + /*match_optimized_ir=*/true); + // Check that the kernel runs correctly. + EXPECT_TRUE(RunAndCompareNoHloPasses(kHloString, ErrorSpec{1.0e-5, 1.0e-5})); +} + +TEST_F(GpuKernelTilingTest, ColumnReductionMOFUnrolled) { + const char *const kHloString = R"( + HloModule column_reduce_powerof2_mof + + reduction22 { + x = f32[] parameter(0) + y = f32[] parameter(1) + ROOT add = f32[] add(x, y) + } + + fused_computation { + constant0 = f32[] constant(0) + arg.1 = f16[1024,512]{1,0} parameter(0) + arg.2 = f16[1024,512]{1,0} parameter(1) + arg1.conv = f32[1024,512]{1,0} convert(arg.1) + arg2.conv = f32[1024,512]{1,0} convert(arg.2) + reduce1 = f32[512]{0} reduce(arg1.conv, constant0), dimensions={0}, + to_apply=reduction22 + reduce2 = f32[512]{0} reduce(arg2.conv, constant0), dimensions={0}, + to_apply=reduction22 + add = f32[1024,512]{1,0} add(arg1.conv, arg2.conv) + ROOT tuple = (f32[512]{0}, f32[512]{0}, f32[1024,512]{1,0}) + tuple(reduce1, reduce2, add) + } + + ENTRY kernel_entry { + arg1 = f16[1024,512]{1,0} parameter(0) + arg2 = f16[1024,512]{1,0} parameter(1) + ROOT fusion = (f32[512]{0}, f32[512]{0}, f32[1024,512]{1,0}) + fusion(arg1, arg2), kind=kInput, calls=fused_computation + })"; + + // Check that four calls to llvm.nvvm.atomic are generated. + auto hlo_module = + ParseHloString(kHloString, ConfigWithoutLayoutAssignment()).ValueOrDie(); + CompileAndVerifyIr(std::move(hlo_module), + R"( +; CHECK-LABEL: define void @fusion +; CHECK: call float @llvm.nvvm.atomic.load.add.f32.p0f32 +; CHECK: call float @llvm.nvvm.atomic.load.add.f32.p0f32 +; CHECK: call float @llvm.nvvm.atomic.load.add.f32.p0f32 +; CHECK: call float @llvm.nvvm.atomic.load.add.f32.p0f32 +; CHECK-NOT: call float @llvm.nvvm.atomic.load.add.f32.p0f32 +; CHECK: } +)", + /*match_optimized_ir=*/true); + // Check that the kernel runs correctly. + EXPECT_TRUE(RunAndCompareNoHloPasses(kHloString, ErrorSpec{1.0e-5, 1.0e-5})); +} } // namespace } // namespace gpu } // namespace xla -- GitLab From 84bf8020f3a99dc49116417a6430c7a51e349e14 Mon Sep 17 00:00:00 2001 From: Skye Wanderman-Milne Date: Wed, 9 Jan 2019 17:25:33 -0800 Subject: [PATCH 591/622] Set device on Identity ops representing inlined function inputs/outputs. This fixes a strange multi-GPU performance issue where the identities from inlined cond_v2 branch functions (part of the control flow lowering pass) were being placed alone on a GPU, creating unnecessary device traffic. Ideally the placer wouldn't be this stupid or grappler would remove the useless identities, but this fix is easier for now. This also adds a simple benchmark testing nested function calls, which currently are inlined. This benchmark doesn't expose the original problem (all of the ops, including the identities added by inlining, are placed on a single device regardless), but I've included it in case it catches any future regressions. I also increased the number of iterations, as I found this made the results more stable. PiperOrigin-RevId: 228619628 --- tensorflow/core/common_runtime/function.cc | 4 ++++ tensorflow/python/eager/benchmarks_test.py | 25 +++++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/tensorflow/core/common_runtime/function.cc b/tensorflow/core/common_runtime/function.cc index 24a0eee0df..48f32df275 100644 --- a/tensorflow/core/common_runtime/function.cc +++ b/tensorflow/core/common_runtime/function.cc @@ -104,6 +104,10 @@ static Node* AddIdentity(Graph* g, Endpoint input) { NodeDef ndef; ndef.set_name(g->NewName(kNodeLabel)); ndef.set_op("Identity"); + // NOTE(skyewm): we explicitly set the device here to address a multi-GPU + // performance issue where this Identity would be placed alone on a GPU, + // causing unnecessary device traffic. See b/122483225 for details. + ndef.set_device(input.node->def().device()); ndef.add_input(input.name()); AddNodeAttr("T", BaseType(input.dtype()), &ndef); Status s; diff --git a/tensorflow/python/eager/benchmarks_test.py b/tensorflow/python/eager/benchmarks_test.py index 9191c8e689..62c4a12cbf 100644 --- a/tensorflow/python/eager/benchmarks_test.py +++ b/tensorflow/python/eager/benchmarks_test.py @@ -140,7 +140,7 @@ class MicroBenchmarks(test.Benchmark): self._m_2_by_2 = random_ops.random_uniform((2, 2)) self._m_100_by_784 = random_ops.random_uniform((100, 784)) self._num_iters_2_by_2 = 30000 - self._num_iters_100_by_784 = 1000 + self._num_iters_100_by_784 = 30000 def _run(self, func, num_iters, execution_mode=None): # call func to maybe warm up the GPU @@ -370,6 +370,19 @@ class MicroBenchmarks(test.Benchmark): func = lambda: f(m, m, transpose_b=transpose_b) self._run(func, num_iters, execution_mode=execution_mode) + def _benchmark_nested_defun_matmul(self, m, transpose_b, num_iters): + inner = function.defun(math_ops.matmul) + + @function.defun + def outer(a, b, c, transpose_b): + return math_ops.matmul(inner(a, b, transpose_b=transpose_b), c) + + func = lambda: outer(m, m, m, transpose_b=transpose_b) + # Warmup before benchmark + for _ in range(1000): + func() + self._run(func, num_iters) + def _benchmark_defun_matmul_forward_backward(self, m, transpose_b, @@ -525,6 +538,11 @@ class MicroBenchmarks(test.Benchmark): num_iters=self._num_iters_2_by_2, execution_mode=context.ASYNC) + def benchmark_nested_defun_matmul_2_by_2(self): + m = self._m_2_by_2.cpu() + self._benchmark_nested_defun_matmul( + m, transpose_b=False, num_iters=self._num_iters_2_by_2) + # Benchmarks for AA.T, A of dimension 100 by 784. def benchmark_np_matmul_100_by_784(self): self._benchmark_np_matmul( @@ -614,6 +632,11 @@ class MicroBenchmarks(test.Benchmark): self._benchmark_defun_matmul( m, transpose_b=True, num_iters=self._num_iters_100_by_784) + def benchmark_nested_defun_matmul_100_by_784(self): + m = self._m_100_by_784.gpu() + self._benchmark_nested_defun_matmul( + m, transpose_b=True, num_iters=self._num_iters_100_by_784) + def benchmark_defun_without_signature(self): def func(t1, t2, t3, t4, t5, t6, t7, t8): -- GitLab From 0d53e22c582605d4f7f2a26b179a637a2d469206 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Wed, 9 Jan 2019 17:30:56 -0800 Subject: [PATCH 592/622] Refactor pfor tests into multiple files. PiperOrigin-RevId: 228620325 --- tensorflow/python/ops/parallel_for/BUILD | 47 +- .../python/ops/parallel_for/array_test.py | 274 +++++++ .../ops/parallel_for/control_flow_ops_test.py | 693 ++---------------- .../python/ops/parallel_for/gradients_test.py | 2 + .../python/ops/parallel_for/math_test.py | 405 ++++++++++ .../python/ops/parallel_for/test_util.py | 59 ++ 6 files changed, 830 insertions(+), 650 deletions(-) create mode 100644 tensorflow/python/ops/parallel_for/array_test.py create mode 100644 tensorflow/python/ops/parallel_for/math_test.py create mode 100644 tensorflow/python/ops/parallel_for/test_util.py diff --git a/tensorflow/python/ops/parallel_for/BUILD b/tensorflow/python/ops/parallel_for/BUILD index 07fc9433a2..1028963b1a 100644 --- a/tensorflow/python/ops/parallel_for/BUILD +++ b/tensorflow/python/ops/parallel_for/BUILD @@ -15,11 +15,13 @@ py_library( "control_flow_ops.py", "gradients.py", "pfor.py", + "test_util.py", ], srcs_version = "PY2AND3", deps = [ ":control_flow_ops", ":gradients", + ":test_util", "//tensorflow/python:array_ops", "//tensorflow/python:check_ops", "//tensorflow/python:constant_op", @@ -83,12 +85,25 @@ py_library( ], ) +py_library( + name = "test_util", + srcs = ["test_util.py"], + srcs_version = "PY2AND3", + deps = [ + ":pfor_lib", + "//tensorflow/python:client_testlib", + "//tensorflow/python:framework_ops", + "//tensorflow/python:util", + "//tensorflow/python:variables", + ], +) + cuda_py_test( name = "control_flow_ops_test", - size = "large", srcs = ["control_flow_ops_test.py"], additional_deps = [ ":control_flow_ops", + ":test_util", "//tensorflow/core:protos_all_py", "//tensorflow/python:client_testlib", "//tensorflow/python:gradients", @@ -101,6 +116,34 @@ cuda_py_test( ], ) +cuda_py_test( + name = "array_test", + srcs = ["array_test.py"], + additional_deps = [ + ":control_flow_ops", + ":test_util", + "//tensorflow/python:client_testlib", + "//tensorflow/python:array_ops", + "//tensorflow/python:random_ops", + "//tensorflow/python:util", + "//tensorflow/python/eager:backprop", + ], +) + +cuda_py_test( + name = "math_test", + srcs = ["math_test.py"], + additional_deps = [ + ":control_flow_ops", + ":test_util", + "//tensorflow/python:client_testlib", + "//tensorflow/python:math_ops", + "//tensorflow/python:random_ops", + "//tensorflow/python:util", + ], + tags = ["optonly"], # Too slow in non-opt mode +) + py_library( name = "gradients", srcs = ["gradients.py"], @@ -115,7 +158,6 @@ py_library( cuda_py_test( name = "gradients_test", - size = "large", srcs = ["gradients_test.py"], additional_deps = [ ":control_flow_ops", @@ -128,4 +170,5 @@ cuda_py_test( "//tensorflow/python:random_ops", "//tensorflow/python/ops/losses", ], + tags = ["optonly"], # Too slow in non-opt mode ) diff --git a/tensorflow/python/ops/parallel_for/array_test.py b/tensorflow/python/ops/parallel_for/array_test.py new file mode 100644 index 0000000000..7f0c0f5b99 --- /dev/null +++ b/tensorflow/python/ops/parallel_for/array_test.py @@ -0,0 +1,274 @@ +# Copyright 2018 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 vectorization of array kernels.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.eager import backprop +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import nn +from tensorflow.python.ops import random_ops +from tensorflow.python.ops import tensor_array_grad # pylint: disable=unused-import +from tensorflow.python.ops.parallel_for import control_flow_ops as pfor_control_flow_ops +from tensorflow.python.ops.parallel_for.test_util import PForTestCase +from tensorflow.python.platform import test + + +@test_util.run_all_in_graph_and_eager_modes +class ArrayTest(PForTestCase): + + def test_gather(self): + x = random_ops.random_uniform([3, 3, 3]) + + def loop_fn(i): + outputs = [] + x_i = array_ops.gather(x, i) + for y in [x, x_i]: + axes = [0, 2, -1] if y == x else [0] + for axis in axes: + outputs.append(array_ops.gather(y, 2, axis=axis)) + outputs.append(array_ops.gather(y, i, axis=axis)) + outputs.append(array_ops.gather(y, [i], axis=axis)) + outputs.append(array_ops.gather(y, [i, 2], axis=axis)) + outputs.append(array_ops.gather(y, [[2, i], [i, 1]], axis=axis)) + return outputs + + self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 20) + + def test_shape(self): + x = random_ops.random_uniform([3, 2, 3]) + + def loop_fn(i): + x_i = array_ops.gather(x, i) + return array_ops.shape(x_i), array_ops.shape(x_i, out_type=dtypes.int64) + + self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.int32, dtypes.int64]) + + def test_size(self): + x = random_ops.random_uniform([3, 2, 3]) + + def loop_fn(i): + x_i = array_ops.gather(x, i) + return array_ops.size(x_i), array_ops.size(x_i, out_type=dtypes.int64) + + self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.int32, dtypes.int64]) + + def test_rank(self): + x = random_ops.random_uniform([3, 2, 3]) + + def loop_fn(i): + x_i = array_ops.gather(x, i) + return array_ops.rank(x_i) + + self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.int32]) + + def test_shape_n(self): + x = random_ops.random_uniform([3, 2, 3]) + y = random_ops.random_uniform([3]) + + def loop_fn(i): + x_i = array_ops.gather(x, i) + y_i = array_ops.gather(y, i) + return array_ops.shape_n([x_i, x, y, y_i]), array_ops.shape_n( + [x_i, x, y, y_i], out_type=dtypes.int64) + + self._test_loop_fn( + loop_fn, 3, loop_fn_dtypes=[dtypes.int32] * 4 + [dtypes.int64] * 4) + + def test_reshape(self): + x = random_ops.random_uniform([3, 2, 3]) + + def loop_fn(i): + x1 = array_ops.gather(x, i) + return array_ops.reshape(x1, [-1]), array_ops.reshape(x1, [1, 3, 1, -1]) + + self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 2) + + def test_expand_dims(self): + x = random_ops.random_uniform([3, 2, 3]) + + def loop_fn(i): + x1 = array_ops.gather(x, i) + return array_ops.expand_dims( + x1, axis=-1), array_ops.expand_dims( + x1, axis=1) + + self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 2) + + def test_slice(self): + x = random_ops.random_uniform([3, 2, 3]) + + def loop_fn(i): + x1 = array_ops.gather(x, i) + return array_ops.slice(x1, begin=(0, 1), size=(2, 1)) + + self._test_loop_fn(loop_fn, 3) + + def test_tile(self): + x = random_ops.random_uniform([3, 2, 3]) + + def loop_fn(i): + x1 = array_ops.gather(x, i) + return array_ops.tile(x1, [2, 1]) + + self._test_loop_fn(loop_fn, 3) + + def test_tile_loop_dependent(self): + x = random_ops.random_uniform([3, 2, 3]) + + def loop_fn(i): + x1 = array_ops.gather(x, i) + return array_ops.tile(x1, [i, 1]) + + with self.assertRaisesRegexp(ValueError, "expected to be loop invariant"): + pfor_control_flow_ops.pfor(loop_fn, 2) + + def test_pack(self): + x = random_ops.random_uniform([3, 2, 3]) + y = random_ops.random_uniform([2, 3]) + + def loop_fn(i): + x1 = array_ops.gather(x, i) + return array_ops.stack([x1, y], axis=-1) + + self._test_loop_fn(loop_fn, 1) + + def test_unpack(self): + x = random_ops.random_uniform([3, 2, 3, 4]) + + def loop_fn(i): + x_i = array_ops.gather(x, i) + return array_ops.unstack( + x_i, 4, axis=-1), array_ops.unstack( + x_i, 3, axis=1) + + self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 7) + + def test_pad(self): + x = random_ops.random_uniform([3, 2, 3]) + padding = constant_op.constant([[1, 2], [3, 4]]) + + def loop_fn(i): + x1 = array_ops.gather(x, i) + return array_ops.pad(x1, padding, mode="CONSTANT") + + self._test_loop_fn(loop_fn, 3) + + def test_split(self): + x = random_ops.random_uniform([3, 2, 3]) + + def loop_fn(i): + x1 = array_ops.gather(x, i) + return array_ops.split(x1, 2, axis=0), array_ops.split(x1, 3, axis=-1) + + self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 5) + + def test_split_v(self): + x = random_ops.random_uniform([3, 6, 3]) + + def loop_fn(i): + x1 = array_ops.gather(x, i) + return (array_ops.split(x1, [2, 1, 3], axis=0), + array_ops.split(x1, [3], axis=-1)) + + self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 4) + + def test_transpose(self): + x = random_ops.random_uniform([3, 2, 3, 4]) + + def loop_fn(i): + x1 = array_ops.gather(x, i) + return array_ops.transpose(x1, [2, 1, 0]) + + self._test_loop_fn(loop_fn, 3) + + def test_zeros_like(self): + x = random_ops.random_uniform([3, 2, 3]) + + def loop_fn(i): + x1 = array_ops.gather(x, i) + z = array_ops.zeros_like(x1), + return z, z + x1 + + self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 2) + + def test_concat_v2(self): + x = random_ops.random_uniform([3, 2, 3]) + y = random_ops.random_uniform([2, 3]) + + def loop_fn(i): + x1 = array_ops.gather(x, i) + return array_ops.concat( + [x1, x1, y], axis=0), array_ops.concat( + [x1, x1, y], axis=-1) + + self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 2) + + def test_unary_cwise_ops(self): + for op in [array_ops.identity, array_ops.stop_gradient]: + with backprop.GradientTape(persistent=True) as g: + x = random_ops.random_uniform([3, 5]) + g.watch(x) + + # pylint: disable=cell-var-from-loop + def loop_fn(i): + with g: + x1 = array_ops.gather(x, i) + y = op(x1) + x1 + loss = nn.l2_loss(y) + return op(x), y, g.gradient(loss, x1) + + # pylint: enable=cell-var-from-loop + + self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 3) + + def test_identity_n(self): + x = random_ops.random_uniform([3, 4]) + + def loop_fn(i): + return array_ops.identity_n([x, array_ops.gather(x, i)]) + + self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 2) + + def test_matrix_diag_part(self): + x = random_ops.random_uniform([3, 4, 2]) + + def loop_fn(i): + return array_ops.matrix_diag_part(array_ops.gather(x, i)) + + self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32]) + + def test_strided_slice(self): + with backprop.GradientTape(persistent=True) as g: + x = random_ops.random_uniform([3, 3, 4, 4, 2, 2, 2]) + g.watch(x) + + def loop_fn(i): + with g: + x_i = array_ops.gather(x, i) + y = x_i[:2, ::2, 1::3, ..., array_ops.newaxis, 1] + loss = nn.l2_loss(y) + return y, g.gradient(loss, x_i) + + self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 2) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/ops/parallel_for/control_flow_ops_test.py b/tensorflow/python/ops/parallel_for/control_flow_ops_test.py index 933bddd8cc..1a8f639d43 100644 --- a/tensorflow/python/ops/parallel_for/control_flow_ops_test.py +++ b/tensorflow/python/ops/parallel_for/control_flow_ops_test.py @@ -34,7 +34,6 @@ from tensorflow.python.framework import sparse_tensor from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops from tensorflow.python.ops import bitwise_ops -from tensorflow.python.ops import clip_ops from tensorflow.python.ops import control_flow_ops from tensorflow.python.ops import data_flow_ops from tensorflow.python.ops import functional_ops @@ -50,40 +49,13 @@ from tensorflow.python.ops import tensor_array_grad # pylint: disable=unused-im from tensorflow.python.ops import tensor_array_ops from tensorflow.python.ops import variables from tensorflow.python.ops.parallel_for import control_flow_ops as pfor_control_flow_ops +from tensorflow.python.ops.parallel_for.test_util import PForTestCase from tensorflow.python.platform import test from tensorflow.python.util import nest @test_util.run_all_in_graph_and_eager_modes -class PForTest(test.TestCase): - - def _run_targets(self, targets1, targets2=None, run_init=True): - targets1 = nest.flatten(targets1) - targets2 = ([] if targets2 is None else nest.flatten(targets2)) - assert len(targets1) == len(targets2) or not targets2 - if run_init: - init = variables.global_variables_initializer() - self.evaluate(init) - return self.evaluate(targets1 + targets2) - - def run_and_assert_equal(self, targets1, targets2): - outputs = self._run_targets(targets1, targets2) - outputs = nest.flatten(outputs) # flatten SparseTensorValues - n = len(outputs) // 2 - for i in range(n): - if outputs[i + n].dtype != np.object: - self.assertAllClose(outputs[i + n], outputs[i], rtol=1e-4, atol=1e-5) - else: - self.assertAllEqual(outputs[i + n], outputs[i]) - - def _test_loop_fn(self, loop_fn, iters, - loop_fn_dtypes=dtypes.float32, - parallel_iterations=None): - t1 = pfor_control_flow_ops.pfor(loop_fn, iters=iters, - parallel_iterations=parallel_iterations) - t2 = pfor_control_flow_ops.for_loop(loop_fn, loop_fn_dtypes, iters=iters, - parallel_iterations=parallel_iterations) - self.run_and_assert_equal(t1, t2) +class PForTest(PForTestCase): def test_op_conversion_fallback_to_while_loop(self): # Note that we used top_k op for this test. If a converter gets defined for @@ -129,246 +101,7 @@ class PForTest(test.TestCase): @test_util.run_all_in_graph_and_eager_modes -class ArrayTest(PForTest): - - def test_gather(self): - x = random_ops.random_uniform([3, 3, 3]) - - def loop_fn(i): - outputs = [] - x_i = array_ops.gather(x, i) - for y in [x, x_i]: - axes = [0, 2, -1] if y == x else [0] - for axis in axes: - outputs.append(array_ops.gather(y, 2, axis=axis)) - outputs.append(array_ops.gather(y, i, axis=axis)) - outputs.append(array_ops.gather(y, [i], axis=axis)) - outputs.append(array_ops.gather(y, [i, 2], axis=axis)) - outputs.append(array_ops.gather(y, [[2, i], [i, 1]], axis=axis)) - return outputs - - self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 20) - - def test_shape(self): - x = random_ops.random_uniform([3, 2, 3]) - - def loop_fn(i): - x_i = array_ops.gather(x, i) - return array_ops.shape(x_i), array_ops.shape(x_i, out_type=dtypes.int64) - - self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.int32, dtypes.int64]) - - def test_size(self): - x = random_ops.random_uniform([3, 2, 3]) - - def loop_fn(i): - x_i = array_ops.gather(x, i) - return array_ops.size(x_i), array_ops.size(x_i, out_type=dtypes.int64) - - self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.int32, dtypes.int64]) - - def test_rank(self): - x = random_ops.random_uniform([3, 2, 3]) - - def loop_fn(i): - x_i = array_ops.gather(x, i) - return array_ops.rank(x_i) - - self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.int32]) - - def test_shape_n(self): - x = random_ops.random_uniform([3, 2, 3]) - y = random_ops.random_uniform([3]) - - def loop_fn(i): - x_i = array_ops.gather(x, i) - y_i = array_ops.gather(y, i) - return array_ops.shape_n([x_i, x, y, y_i]), array_ops.shape_n( - [x_i, x, y, y_i], out_type=dtypes.int64) - - self._test_loop_fn( - loop_fn, 3, loop_fn_dtypes=[dtypes.int32] * 4 + [dtypes.int64] * 4) - - def test_reshape(self): - x = random_ops.random_uniform([3, 2, 3]) - - def loop_fn(i): - x1 = array_ops.gather(x, i) - return array_ops.reshape(x1, [-1]), array_ops.reshape(x1, [1, 3, 1, -1]) - - self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 2) - - def test_expand_dims(self): - x = random_ops.random_uniform([3, 2, 3]) - - def loop_fn(i): - x1 = array_ops.gather(x, i) - return array_ops.expand_dims( - x1, axis=-1), array_ops.expand_dims( - x1, axis=1) - - self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 2) - - def test_slice(self): - x = random_ops.random_uniform([3, 2, 3]) - - def loop_fn(i): - x1 = array_ops.gather(x, i) - return array_ops.slice(x1, begin=(0, 1), size=(2, 1)) - - self._test_loop_fn(loop_fn, 3) - - def test_tile(self): - x = random_ops.random_uniform([3, 2, 3]) - - def loop_fn(i): - x1 = array_ops.gather(x, i) - return array_ops.tile(x1, [2, 1]) - - self._test_loop_fn(loop_fn, 3) - - def test_tile_loop_dependent(self): - x = random_ops.random_uniform([3, 2, 3]) - - def loop_fn(i): - x1 = array_ops.gather(x, i) - return array_ops.tile(x1, [i, 1]) - - with self.assertRaisesRegexp(ValueError, "expected to be loop invariant"): - pfor_control_flow_ops.pfor(loop_fn, 2) - - def test_pack(self): - x = random_ops.random_uniform([3, 2, 3]) - y = random_ops.random_uniform([2, 3]) - - def loop_fn(i): - x1 = array_ops.gather(x, i) - return array_ops.stack([x1, y], axis=-1) - - self._test_loop_fn(loop_fn, 1) - - def test_unpack(self): - x = random_ops.random_uniform([3, 2, 3, 4]) - - def loop_fn(i): - x_i = array_ops.gather(x, i) - return array_ops.unstack( - x_i, 4, axis=-1), array_ops.unstack( - x_i, 3, axis=1) - - self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 7) - - def test_pad(self): - x = random_ops.random_uniform([3, 2, 3]) - padding = constant_op.constant([[1, 2], [3, 4]]) - - def loop_fn(i): - x1 = array_ops.gather(x, i) - return array_ops.pad(x1, padding, mode="CONSTANT") - - self._test_loop_fn(loop_fn, 3) - - def test_split(self): - x = random_ops.random_uniform([3, 2, 3]) - - def loop_fn(i): - x1 = array_ops.gather(x, i) - return array_ops.split(x1, 2, axis=0), array_ops.split(x1, 3, axis=-1) - - self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 5) - - def test_split_v(self): - x = random_ops.random_uniform([3, 6, 3]) - - def loop_fn(i): - x1 = array_ops.gather(x, i) - return (array_ops.split(x1, [2, 1, 3], axis=0), - array_ops.split(x1, [3], axis=-1)) - - self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 4) - - def test_transpose(self): - x = random_ops.random_uniform([3, 2, 3, 4]) - - def loop_fn(i): - x1 = array_ops.gather(x, i) - return array_ops.transpose(x1, [2, 1, 0]) - - self._test_loop_fn(loop_fn, 3) - - def test_zeros_like(self): - x = random_ops.random_uniform([3, 2, 3]) - - def loop_fn(i): - x1 = array_ops.gather(x, i) - z = array_ops.zeros_like(x1), - return z, z + x1 - - self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 2) - - def test_concat_v2(self): - x = random_ops.random_uniform([3, 2, 3]) - y = random_ops.random_uniform([2, 3]) - - def loop_fn(i): - x1 = array_ops.gather(x, i) - return array_ops.concat( - [x1, x1, y], axis=0), array_ops.concat( - [x1, x1, y], axis=-1) - - self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 2) - - def test_unary_cwise_ops(self): - for op in [array_ops.identity, array_ops.stop_gradient]: - with backprop.GradientTape(persistent=True) as g: - x = random_ops.random_uniform([3, 5]) - g.watch(x) - - # pylint: disable=cell-var-from-loop - def loop_fn(i): - with g: - x1 = array_ops.gather(x, i) - y = op(x1) + x1 - loss = nn.l2_loss(y) - return op(x), y, g.gradient(loss, x1) - - # pylint: enable=cell-var-from-loop - - self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 3) - - def test_identity_n(self): - x = random_ops.random_uniform([3, 4]) - - def loop_fn(i): - return array_ops.identity_n([x, array_ops.gather(x, i)]) - - self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 2) - - def test_matrix_diag_part(self): - x = random_ops.random_uniform([3, 4, 2]) - - def loop_fn(i): - return array_ops.matrix_diag_part(array_ops.gather(x, i)) - - self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32]) - - def test_strided_slice(self): - with backprop.GradientTape(persistent=True) as g: - x = random_ops.random_uniform([3, 3, 4, 4, 2, 2, 2]) - g.watch(x) - - def loop_fn(i): - with g: - x_i = array_ops.gather(x, i) - y = x_i[:2, ::2, 1::3, ..., array_ops.newaxis, 1] - loss = nn.l2_loss(y) - return y, g.gradient(loss, x_i) - - self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 2) - - -@test_util.run_all_in_graph_and_eager_modes -class BitwiseTest(PForTest): +class BitwiseTest(PForTestCase): def test_unary_cwise(self): for op in [bitwise_ops.invert]: @@ -408,376 +141,7 @@ class BitwiseTest(PForTest): @test_util.run_all_in_graph_and_eager_modes -class MathTest(PForTest): - - def test_unary_cwise_ops(self): - complex_ops = [ - math_ops.angle, - math_ops.imag, - math_ops.complex_abs, - math_ops.real, - math_ops.conj, - ] - real_ops = [ - lambda x: math_ops.acosh(1 + math_ops.square(x)), - math_ops.abs, - math_ops.acos, - math_ops.asin, - math_ops.asinh, - math_ops.atan, - math_ops.atanh, - math_ops.bessel_i0e, - math_ops.bessel_i1e, - math_ops.cos, - math_ops.cosh, - math_ops.digamma, - math_ops.erf, - math_ops.erfc, - math_ops.exp, - math_ops.expm1, - math_ops.inv, - math_ops.is_finite, - math_ops.is_inf, - math_ops.lgamma, - math_ops.log, - math_ops.log1p, - math_ops.neg, - math_ops.negative, - math_ops.reciprocal, - math_ops.rint, - math_ops.round, - math_ops.rsqrt, - math_ops.sigmoid, - math_ops.sign, - math_ops.sin, - math_ops.sinh, - math_ops.sqrt, - math_ops.square, - math_ops.tan, - math_ops.tanh, - math_ops.tanh, - nn.elu, - nn.relu, - nn.relu6, - nn.selu, - nn.softplus, - nn.softsign, - ] - for op in complex_ops + real_ops: - with backprop.GradientTape(persistent=True) as g: - x = random_ops.random_uniform([3, 5]) - g.watch(x) - if op in complex_ops: - y = random_ops.random_uniform([3, 5]) - g.watch(y) - x = math_ops.complex(x, y) - - # pylint: disable=cell-var-from-loop - output_dtypes = [] - def loop_fn(i): - with g: - x1 = array_ops.gather(x, i) - y1 = op(x1) - outputs = [op(x), y1] - if y1.dtype == dtypes.float32: - loss = math_ops.reduce_sum(y1 * y1) - else: - loss = None - if loss is not None: - grad = g.gradient(loss, x1) - if grad is not None: - outputs.append(grad) - del output_dtypes[:] - output_dtypes.extend([t.dtype for t in outputs]) - return outputs - - # pylint: enable=cell-var-from-loop - - self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=output_dtypes) - - def test_unary_cwise_no_grad(self): - for op in [math_ops.ceil, - math_ops.floor, - math_ops.logical_not]: - x = random_ops.random_uniform([3, 5]) - if op == math_ops.logical_not: - x = x > 0 - - # pylint: disable=cell-var-from-loop - def loop_fn(i): - return op(array_ops.gather(x, i)) - - # pylint: enable=cell-var-from-loop - - self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=x.dtype) - - def test_binary_cwise_ops(self): - logical_ops = [ - math_ops.logical_and, - math_ops.logical_or, - math_ops.logical_xor - ] - - # Wrapper functions restricting the range of inputs of zeta and polygamma. - def safe_polygamma(x, y): - return math_ops.polygamma( - math_ops.round(clip_ops.clip_by_value(y, 1, 10)), - x * x + 1) - - def safe_zeta(x, y): - return math_ops.zeta(x * x + 1, y * y) - - float_ops = [ - math_ops.add, - math_ops.add_v2, - math_ops.atan2, - math_ops.complex, - math_ops.div, - math_ops.divide, - math_ops.div_no_nan, - math_ops.equal, - math_ops.floor_div, - math_ops.floor_mod, - math_ops.greater, - math_ops.greater_equal, - math_ops.igamma, - math_ops.igammac, - math_ops.igamma_grad_a, - math_ops.less, - math_ops.less_equal, - math_ops.maximum, - math_ops.minimum, - math_ops.mod, - math_ops.multiply, - math_ops.not_equal, - math_ops.pow, - math_ops.squared_difference, - math_ops.subtract, - math_ops.truncate_mod, - safe_polygamma, - safe_zeta, - ] - for op in logical_ops + float_ops: - x = random_ops.random_uniform([7, 3, 5]) - y = random_ops.random_uniform([3, 5]) - if op in logical_ops: - x = x > 0 - y = y > 0 - - output_dtypes = [] - # pylint: disable=cell-var-from-loop - def loop_fn(i): - x1 = array_ops.gather(x, i) - y1 = array_ops.gather(y, i) - outputs = [op(x, y), op(x1, y), op(x, y1), op(x1, y1), op(x1, x1)] - del output_dtypes[:] - output_dtypes.extend([t.dtype for t in outputs]) - return outputs - # pylint: enable=cell-var-from-loop - - self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=output_dtypes) - - def test_approximate_equal(self): - x = random_ops.random_uniform([3, 5]) - y = random_ops.random_uniform([3, 5]) - - def loop_fn(i): - x1 = array_ops.gather(x, i) - y1 = array_ops.gather(y, i) - return math_ops.approximate_equal(x1, y1) - - self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.bool]) - - def test_addn(self): - x = random_ops.random_uniform([2, 3, 5]) - y = random_ops.random_uniform([3, 5]) - z = random_ops.random_uniform([3, 5]) - - def loop_fn(i): - x1 = array_ops.gather(x, i) - return math_ops.add_n([x1, y, z]) - - self._test_loop_fn(loop_fn, 2) - - def test_matmul(self): - for tr_a in (True, False): - for tr_b in (True, False): - for stack_a in (True, False): - for stack_b in (True, False): - shape_a = (5, 3) if tr_a else (3, 5) - if stack_a: - shape_a = (2,) + shape_a - shape_b = (7, 5) if tr_b else (5, 7) - if stack_b: - shape_b = (2,) + shape_b - - x = random_ops.random_uniform(shape_a) - y = random_ops.random_uniform(shape_b) - - # pylint: disable=cell-var-from-loop - def loop_fn(i): - a = array_ops.gather(x, i) if stack_a else x - b = array_ops.gather(y, i) if stack_b else y - return math_ops.matmul(a, b, transpose_a=tr_a, transpose_b=tr_b) - - # pylint: enable=cell-var-from-loop - - self._test_loop_fn(loop_fn, 2) - - def test_batch_matmul(self): - for tr_a in (True, False): - for tr_b in (True, False): - for stack_a in (True, False): - for stack_b in (True, False): - shape_a = (4, 5, 3) if tr_a else (4, 3, 5) - if stack_a: - shape_a = (2,) + shape_a - shape_b = (4, 7, 5) if tr_b else (4, 5, 7) - if stack_b: - shape_b = (2,) + shape_b - - x = random_ops.random_uniform(shape_a) - y = random_ops.random_uniform(shape_b) - - # pylint: disable=cell-var-from-loop - def loop_fn(i): - a = array_ops.gather(x, i) if stack_a else x - b = array_ops.gather(y, i) if stack_b else y - return math_ops.matmul(a, b, transpose_a=tr_a, transpose_b=tr_b) - - # pylint: enable=cell-var-from-loop - - self._test_loop_fn(loop_fn, 2) - - def test_reduction(self): - x = random_ops.random_uniform([2, 3, 4, 5]) - for op in [ - math_ops.reduce_sum, math_ops.reduce_prod, math_ops.reduce_max, - math_ops.reduce_min - ]: - for axis in ([1], None, [0, 2]): - for keepdims in (True, False): - - # pylint: disable=cell-var-from-loop - def loop_fn(i): - a = array_ops.gather(x, i) - return op(a, axis=axis, keepdims=keepdims) - - # pylint: enable=cell-var-from-loop - - self._test_loop_fn(loop_fn, 2) - - def test_cum_sum(self): - x = random_ops.random_uniform([2, 3, 4, 5]) - for axis in (1, -2): - for exclusive in (True, False): - for reverse in (True, False): - - # pylint: disable=cell-var-from-loop - def loop_fn(i): - a = array_ops.gather(x, i) - return math_ops.cumsum( - a, axis=axis, exclusive=exclusive, reverse=reverse) - - # pylint: enable=cell-var-from-loop - - self._test_loop_fn(loop_fn, 2) - - def test_cum_prod(self): - x = random_ops.random_uniform([2, 3, 4, 5]) - for axis in (1, -2): - for exclusive in (True, False): - for reverse in (True, False): - - # pylint: disable=cell-var-from-loop - def loop_fn(i): - a = array_ops.gather(x, i) - return math_ops.cumprod( - a, axis=axis, exclusive=exclusive, reverse=reverse) - - # pylint: enable=cell-var-from-loop - - self._test_loop_fn(loop_fn, 2) - - def test_bias_add(self): - x_shape = [2, 3, 4, 5, 6] - x = random_ops.random_uniform(x_shape) - for data_format in ("NCHW", "NHWC"): - with backprop.GradientTape(persistent=True) as g: - bias_dim = 2 if data_format == "NCHW" else -1 - bias_shape = x_shape[bias_dim] - bias = random_ops.random_uniform([bias_shape]) - g.watch(bias) - - # pylint: disable=cell-var-from-loop - def loop_fn(i): - with g: - a = array_ops.gather(x, i) - y = nn.bias_add(a, bias, data_format=data_format) - loss = math_ops.reduce_sum(y * y) - return y, g.gradient(loss, bias) - # pylint: enable=cell-var-from-loop - - self._test_loop_fn( - loop_fn, 2, loop_fn_dtypes=[dtypes.float32, dtypes.float32]) - - def test_unsorted_segment_sum(self): - t = random_ops.random_uniform([3, 3, 2]) - segment_ids = constant_op.constant([[0, 0, 2], [0, 1, 2], [2, 2, 2]]) - num_segments = 3 - - def loop_fn(i): - data = array_ops.gather(t, i) - data_0 = array_ops.gather(t, 0) - seg_ids = array_ops.gather(segment_ids, i) - return (math_ops.unsorted_segment_sum(data, seg_ids, num_segments), - math_ops.unsorted_segment_sum(data_0, seg_ids, num_segments)) - - self._test_loop_fn(loop_fn, 3, [dtypes.float32] * 2) - - def test_cast(self): - x = constant_op.constant([[1], [2]]) - y = constant_op.constant([[1.0], [2.0]]) - - def loop_fn(i): - return (math_ops.cast(array_ops.gather(x, i), dtypes.float32), - math_ops.cast(array_ops.gather(y, i), dtypes.int32)) - - self._test_loop_fn( - loop_fn, 2, loop_fn_dtypes=[dtypes.float32, dtypes.int32]) - - def test_tanh_axpy(self): - a = constant_op.constant(3.) - x = random_ops.random_uniform([4, 5]) - y = random_ops.random_uniform([6, 5]) - n = x.shape[0] - - def loop_fn(i): - return math_ops.tanh(a * array_ops.gather(x, i) + array_ops.gather(y, i)) - - self._test_loop_fn(loop_fn, n) - - def test_select(self): - cond = constant_op.constant([True, False]) - a = random_ops.random_uniform([2, 3, 5]) - b = random_ops.random_uniform([2, 3, 5]) - for cond_shape in [2], [2, 3], [2, 3, 5]: - cond = random_ops.random_uniform(cond_shape) > 0.5 - - # pylint: disable=cell-var-from-loop - def loop_fn(i): - a_i = array_ops.gather(a, i) - b_i = array_ops.gather(b, i) - cond_i = array_ops.gather(cond, i) - return array_ops.where(cond_i, a_i, b_i) - - # pylint: enable=cell-var-from-loop - - self._test_loop_fn(loop_fn, 2) - - -@test_util.run_all_in_graph_and_eager_modes -class NNTest(PForTest): +class NNTest(PForTestCase): def test_conv2d(self): x = random_ops.random_uniform([3, 2, 12, 12, 3]) @@ -956,7 +320,7 @@ class NNTest(PForTest): self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32] * 2) -class RandomTest(PForTest): +class RandomTest(PForTestCase): # The random values generated in the two implementations are not guaranteed to # match. So we only check the returned shapes. @@ -1009,8 +373,9 @@ class RandomTest(PForTest): self._test_loop_fn(loop_fn, 5) -class LoggingTest(PForTest): +class LoggingTest(PForTestCase): + @test_util.run_v1_only("b/122612051") def test_print(self): x = random_ops.random_uniform([3, 5]) @@ -1031,8 +396,9 @@ class LoggingTest(PForTest): sess.run(pfor_control_flow_ops.pfor(loop_fn, 3)) -class TensorArrayTest(PForTest): +class TensorArrayTest(PForTestCase): + @test_util.run_v1_only("b/122612051") def test_create_outside_and_read(self): ta = tensor_array_ops.TensorArray( @@ -1043,6 +409,7 @@ class TensorArrayTest(PForTest): self._test_loop_fn(loop_fn, 2, [dtypes.int32] * 2) + @test_util.run_v1_only("b/122612051") def test_create_outside_and_gather(self): ta = tensor_array_ops.TensorArray( @@ -1053,6 +420,7 @@ class TensorArrayTest(PForTest): self._test_loop_fn(loop_fn, 2, [dtypes.int32] * 2) + @test_util.run_v1_only("b/122612051") def test_create_outside_and_write_and_scatter(self): t = tensor_array_ops.TensorArray(dtypes.int32, 10, clear_after_read=False) @@ -1074,6 +442,7 @@ class TensorArrayTest(PForTest): output2 = self._run_targets(out2) self.assertAllClose(output2, output1) + @test_util.run_v1_only("b/122612051") def test_create_inside_and_write(self): def loop_fn(i): @@ -1085,6 +454,7 @@ class TensorArrayTest(PForTest): self._test_loop_fn(loop_fn, 3, [dtypes.int32] * 2) + @test_util.run_v1_only("b/122612051") def test_create_inside_and_scatter(self): def loop_fn(i): @@ -1097,6 +467,7 @@ class TensorArrayTest(PForTest): self._test_loop_fn(loop_fn, 3, [dtypes.int32] * 2) + @test_util.run_v1_only("b/122612051") def test_create_inside_and_read(self): def loop_fn(i): @@ -1109,6 +480,7 @@ class TensorArrayTest(PForTest): self._test_loop_fn(loop_fn, 2, [dtypes.int32] * 3) + @test_util.run_v1_only("b/122612051") def test_create_inside_and_gather(self): def loop_fn(i): @@ -1121,6 +493,7 @@ class TensorArrayTest(PForTest): self._test_loop_fn(loop_fn, 2, [dtypes.int32] * 3) + @test_util.run_v1_only("b/122612051") def test_grad(self): x = random_ops.random_uniform([3, 2]) ta = tensor_array_ops.TensorArray( @@ -1140,8 +513,9 @@ class TensorArrayTest(PForTest): self.assertAllClose(actual_grad, computed_grad) -class StackTest(PForTest): +class StackTest(PForTestCase): + @test_util.run_v1_only("b/122612051") def test_stack_inside_loop_invariant(self): def loop_fn(_): @@ -1157,6 +531,7 @@ class StackTest(PForTest): self._test_loop_fn(loop_fn, 2, [dtypes.int32] * 2) + @test_util.run_v1_only("b/122612051") def test_stack_inside_push_loop_dependent(self): def loop_fn(i): @@ -1172,6 +547,7 @@ class StackTest(PForTest): self._test_loop_fn(loop_fn, 2, [dtypes.int32] * 2) + @test_util.run_v1_only("b/122612051") def test_stack_outside_pop(self): s = data_flow_ops.stack_v2(max_size=4, elem_type=dtypes.int32) op = data_flow_ops.stack_push_v2(s, 5) @@ -1195,6 +571,7 @@ class StackTest(PForTest): self.assertAllEqual([6, 6], v2) self.assertAllEqual(5, v3) + @test_util.run_v1_only("b/122612051") def test_stack_outside_push(self): s = data_flow_ops.stack_v2(max_size=4, elem_type=dtypes.int32) @@ -1207,7 +584,7 @@ class StackTest(PForTest): # TODO(agarwal): test nested while_loops. This currently requires converting a # tf.cond. -class ControlFlowTest(PForTest): +class ControlFlowTest(PForTestCase): def test_while_outside_loop(self): @@ -1218,6 +595,7 @@ class ControlFlowTest(PForTest): self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.int32]) + @test_util.run_v1_only("b/122612051") def test_invariant_while(self): def loop_fn(_): @@ -1225,6 +603,7 @@ class ControlFlowTest(PForTest): self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.int32]) + @test_util.run_v1_only("b/122612051") def test_invariant_while_with_control_dependency(self): def loop_fn(i): @@ -1234,6 +613,7 @@ class ControlFlowTest(PForTest): self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.int32]) + @test_util.run_v1_only("b/122612051") def test_while_with_stateful_ops(self): def loop_fn(_): @@ -1243,6 +623,7 @@ class ControlFlowTest(PForTest): self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.int32]) + @test_util.run_v1_only("b/122612051") def test_while_unstacked_condition(self): def loop_fn(i): @@ -1251,6 +632,7 @@ class ControlFlowTest(PForTest): self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.int32, dtypes.int32]) + @test_util.run_v1_only("b/122612051") def test_while(self): x = random_ops.random_uniform([3, 5]) lengths = constant_op.constant([4, 0, 2]) @@ -1266,6 +648,7 @@ class ControlFlowTest(PForTest): self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.float32]) + @test_util.run_v1_only("b/122612051") def test_while_jacobian(self): x = random_ops.random_uniform([1, 3]) y = random_ops.random_uniform([3, 3]) @@ -1293,6 +676,7 @@ class ControlFlowTest(PForTest): out, expected = sess.run([out, expected_output]) self.assertAllClose(expected, out) + @test_util.run_v1_only("b/122612051") def test_tensor_array_as_loop_variable(self): def loop_fn(i): @@ -1308,6 +692,7 @@ class ControlFlowTest(PForTest): self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.int32]) + @test_util.run_v1_only("b/122612051") def test_read_tensor_array_partitioned_indices(self): # Note that tensor array values are pfor loop dependent, and the while loop # termination condition is also dependent on pfor iteration. @@ -1325,6 +710,7 @@ class ControlFlowTest(PForTest): self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.int32]) + @test_util.run_v1_only("b/122612051") def test_external_while_loop_grad(self): # Here we test that external while_loops that are extended from inside pfor # (due to gradient calls) are not actually converted. If the below was @@ -1350,6 +736,7 @@ class ControlFlowTest(PForTest): self.assertAllEqual([1, 1, 1], sess.run(pfor_control_flow_ops.pfor(loop_fn, 3))) + @test_util.run_v1_only("b/122612051") def test_tensor_array_grad(self): inp = constant_op.constant(np.random.rand(3, 4, 2), dtype=dtypes.float32) ta = tensor_array_ops.TensorArray(dtypes.float32, size=3) @@ -1447,13 +834,15 @@ def create_dynamic_lstm(cell_fn, batch_size, state_size, max_steps): return pfor_output, tf_output -class RNNTest(PForTest): +class RNNTest(PForTestCase): + @test_util.run_v1_only("b/122612051") def test_dynamic_rnn(self): pfor_outputs, tf_outputs = create_dynamic_lstm(rnn_cell.BasicRNNCell, 3, 5, 7) self.run_and_assert_equal(pfor_outputs, tf_outputs) + @test_util.run_v1_only("b/122612051") def test_dynamic_lstm(self): pfor_outputs, tf_outputs = create_dynamic_lstm(rnn_cell.BasicLSTMCell, 3, 5, 7) @@ -1576,8 +965,9 @@ class Benchmarks(test.Benchmark): self._run(tf_outputs, 100, name="tf_rnn") -class SparseTest(PForTest): +class SparseTest(PForTestCase): + @test_util.run_v1_only("b/122612051") def test_var_loop_len(self): num_iters = array_ops.placeholder(dtypes.int32) @@ -1589,6 +979,7 @@ class SparseTest(PForTest): with self.cached_session() as sess: sess.run(pfor, feed_dict={num_iters: 3}) + @test_util.run_v1_only("b/122612051") def test_sparse_result_none_stacked(self): num_iters = 10 @@ -1605,6 +996,7 @@ class SparseTest(PForTest): manual = sparse_tensor.SparseTensor(indices, values, dense_shapes) self.run_and_assert_equal(pfor, manual) + @test_util.run_v1_only("b/122612051") def test_sparse_result_all_stacked(self): num_iters = 10 @@ -1620,6 +1012,7 @@ class SparseTest(PForTest): (num_iters, num_iters)) self.run_and_assert_equal(pfor, manual) + @test_util.run_v1_only("b/122612051") def test_sparse_result_indices_stacked(self): num_iters = 10 @@ -1634,6 +1027,7 @@ class SparseTest(PForTest): [1] * num_iters, (num_iters, num_iters)) self.run_and_assert_equal(pfor, manual) + @test_util.run_v1_only("b/122612051") def test_sparse_result_values_stacked(self): num_iters = 10 @@ -1648,6 +1042,7 @@ class SparseTest(PForTest): (num_iters, num_iters)) self.run_and_assert_equal(pfor, manual) + @test_util.run_v1_only("b/122612051") def test_sparse_result_shapes_stacked(self): num_iters = 10 @@ -1661,6 +1056,7 @@ class SparseTest(PForTest): [1] * num_iters, (num_iters, num_iters)) self.run_and_assert_equal(pfor, manual) + @test_util.run_v1_only("b/122612051") def test_sparse_result_shapes_stacked_2D(self): num_iters = 10 @@ -1677,7 +1073,7 @@ class SparseTest(PForTest): self.run_and_assert_equal(pfor, manual) -class ParsingTest(PForTest): +class ParsingTest(PForTestCase): def test_decode_csv(self): csv_tensor = constant_op.constant([["1:2:3"], ["::"], ["7:8:9"]]) @@ -1689,6 +1085,7 @@ class ParsingTest(PForTest): self._test_loop_fn(loop_fn, iters=3, loop_fn_dtypes=[dtypes.int32] * 3) + @test_util.run_v1_only("b/122612051") def test_parse_single_example(self): def _int64_feature(*values): diff --git a/tensorflow/python/ops/parallel_for/gradients_test.py b/tensorflow/python/ops/parallel_for/gradients_test.py index 4342833e3e..133e790992 100644 --- a/tensorflow/python/ops/parallel_for/gradients_test.py +++ b/tensorflow/python/ops/parallel_for/gradients_test.py @@ -29,6 +29,7 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util from tensorflow.python.keras.engine import training as keras_training from tensorflow.python.layers import layers as tf_layers from tensorflow.python.ops import array_ops @@ -338,6 +339,7 @@ def create_fc_per_eg_jacobians(batch_size, activation_size, num_layers): return jacobians, per_eg_jacobians_pfor, per_eg_jacobians_while +@test_util.run_v1_only("b/122612051") class GradientsTest(test.TestCase): def run_and_assert_equal(self, targets1, targets2, atol=1e-4, rtol=1e-4): diff --git a/tensorflow/python/ops/parallel_for/math_test.py b/tensorflow/python/ops/parallel_for/math_test.py new file mode 100644 index 0000000000..db88f4fe03 --- /dev/null +++ b/tensorflow/python/ops/parallel_for/math_test.py @@ -0,0 +1,405 @@ +# Copyright 2018 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 vectorization of math kernels.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.eager import backprop +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import test_util +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import clip_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import nn +from tensorflow.python.ops import random_ops +from tensorflow.python.ops import tensor_array_grad # pylint: disable=unused-import +from tensorflow.python.ops.parallel_for.test_util import PForTestCase +from tensorflow.python.platform import test + + +@test_util.run_all_in_graph_and_eager_modes +class MathTest(PForTestCase): + + def test_unary_cwise_ops(self): + complex_ops = [ + math_ops.angle, + math_ops.imag, + math_ops.complex_abs, + math_ops.real, + math_ops.conj, + ] + real_ops = [ + lambda x: math_ops.acosh(1 + math_ops.square(x)), + math_ops.abs, + math_ops.acos, + math_ops.asin, + math_ops.asinh, + math_ops.atan, + math_ops.atanh, + math_ops.bessel_i0e, + math_ops.bessel_i1e, + math_ops.cos, + math_ops.cosh, + math_ops.digamma, + math_ops.erf, + math_ops.erfc, + math_ops.exp, + math_ops.expm1, + math_ops.inv, + math_ops.is_finite, + math_ops.is_inf, + math_ops.lgamma, + math_ops.log, + math_ops.log1p, + math_ops.neg, + math_ops.negative, + math_ops.reciprocal, + math_ops.rint, + math_ops.round, + math_ops.rsqrt, + math_ops.sigmoid, + math_ops.sign, + math_ops.sin, + math_ops.sinh, + math_ops.sqrt, + math_ops.square, + math_ops.tan, + math_ops.tanh, + math_ops.tanh, + nn.elu, + nn.relu, + nn.relu6, + nn.selu, + nn.softplus, + nn.softsign, + ] + for op in complex_ops + real_ops: + with backprop.GradientTape(persistent=True) as g: + x = random_ops.random_uniform([3, 5]) + g.watch(x) + if op in complex_ops: + y = random_ops.random_uniform([3, 5]) + g.watch(y) + x = math_ops.complex(x, y) + + # pylint: disable=cell-var-from-loop + output_dtypes = [] + def loop_fn(i): + with g: + x1 = array_ops.gather(x, i) + y1 = op(x1) + outputs = [op(x), y1] + if y1.dtype == dtypes.float32: + loss = math_ops.reduce_sum(y1 * y1) + else: + loss = None + if loss is not None: + grad = g.gradient(loss, x1) + if grad is not None: + outputs.append(grad) + del output_dtypes[:] + output_dtypes.extend([t.dtype for t in outputs]) + return outputs + + # pylint: enable=cell-var-from-loop + + self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=output_dtypes) + + def test_unary_cwise_no_grad(self): + for op in [math_ops.ceil, + math_ops.floor, + math_ops.logical_not]: + x = random_ops.random_uniform([3, 5]) + if op == math_ops.logical_not: + x = x > 0 + + # pylint: disable=cell-var-from-loop + def loop_fn(i): + return op(array_ops.gather(x, i)) + + # pylint: enable=cell-var-from-loop + + self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=x.dtype) + + def test_binary_cwise_ops(self): + logical_ops = [ + math_ops.logical_and, + math_ops.logical_or, + math_ops.logical_xor + ] + + # Wrapper functions restricting the range of inputs of zeta and polygamma. + def safe_polygamma(x, y): + return math_ops.polygamma( + math_ops.round(clip_ops.clip_by_value(y, 1, 10)), + x * x + 1) + + def safe_zeta(x, y): + return math_ops.zeta(x * x + 1, y * y) + + float_ops = [ + math_ops.add, + math_ops.add_v2, + math_ops.atan2, + math_ops.complex, + math_ops.div, + math_ops.divide, + math_ops.div_no_nan, + math_ops.equal, + math_ops.floor_div, + math_ops.floor_mod, + math_ops.greater, + math_ops.greater_equal, + math_ops.igamma, + math_ops.igammac, + math_ops.igamma_grad_a, + math_ops.less, + math_ops.less_equal, + math_ops.maximum, + math_ops.minimum, + math_ops.mod, + math_ops.multiply, + math_ops.not_equal, + math_ops.pow, + math_ops.squared_difference, + math_ops.subtract, + math_ops.truncate_mod, + safe_polygamma, + safe_zeta, + ] + for op in logical_ops + float_ops: + x = random_ops.random_uniform([7, 3, 5]) + y = random_ops.random_uniform([3, 5]) + if op in logical_ops: + x = x > 0 + y = y > 0 + + output_dtypes = [] + # pylint: disable=cell-var-from-loop + def loop_fn(i): + x1 = array_ops.gather(x, i) + y1 = array_ops.gather(y, i) + outputs = [op(x, y), op(x1, y), op(x, y1), op(x1, y1), op(x1, x1)] + del output_dtypes[:] + output_dtypes.extend([t.dtype for t in outputs]) + return outputs + # pylint: enable=cell-var-from-loop + + self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=output_dtypes) + + def test_approximate_equal(self): + x = random_ops.random_uniform([3, 5]) + y = random_ops.random_uniform([3, 5]) + + def loop_fn(i): + x1 = array_ops.gather(x, i) + y1 = array_ops.gather(y, i) + return math_ops.approximate_equal(x1, y1) + + self._test_loop_fn(loop_fn, 3, loop_fn_dtypes=[dtypes.bool]) + + def test_addn(self): + x = random_ops.random_uniform([2, 3, 5]) + y = random_ops.random_uniform([3, 5]) + z = random_ops.random_uniform([3, 5]) + + def loop_fn(i): + x1 = array_ops.gather(x, i) + return math_ops.add_n([x1, y, z]) + + self._test_loop_fn(loop_fn, 2) + + def test_matmul(self): + for tr_a in (True, False): + for tr_b in (True, False): + for stack_a in (True, False): + for stack_b in (True, False): + shape_a = (5, 3) if tr_a else (3, 5) + if stack_a: + shape_a = (2,) + shape_a + shape_b = (7, 5) if tr_b else (5, 7) + if stack_b: + shape_b = (2,) + shape_b + + x = random_ops.random_uniform(shape_a) + y = random_ops.random_uniform(shape_b) + + # pylint: disable=cell-var-from-loop + def loop_fn(i): + a = array_ops.gather(x, i) if stack_a else x + b = array_ops.gather(y, i) if stack_b else y + return math_ops.matmul(a, b, transpose_a=tr_a, transpose_b=tr_b) + + # pylint: enable=cell-var-from-loop + + self._test_loop_fn(loop_fn, 2) + + def test_batch_matmul(self): + for tr_a in (True, False): + for tr_b in (True, False): + for stack_a in (True, False): + for stack_b in (True, False): + shape_a = (4, 5, 3) if tr_a else (4, 3, 5) + if stack_a: + shape_a = (2,) + shape_a + shape_b = (4, 7, 5) if tr_b else (4, 5, 7) + if stack_b: + shape_b = (2,) + shape_b + + x = random_ops.random_uniform(shape_a) + y = random_ops.random_uniform(shape_b) + + # pylint: disable=cell-var-from-loop + def loop_fn(i): + a = array_ops.gather(x, i) if stack_a else x + b = array_ops.gather(y, i) if stack_b else y + return math_ops.matmul(a, b, transpose_a=tr_a, transpose_b=tr_b) + + # pylint: enable=cell-var-from-loop + + self._test_loop_fn(loop_fn, 2) + + def test_reduction(self): + x = random_ops.random_uniform([2, 3, 4, 5]) + for op in [ + math_ops.reduce_sum, math_ops.reduce_prod, math_ops.reduce_max, + math_ops.reduce_min + ]: + for axis in ([1], None, [0, 2]): + for keepdims in (True, False): + + # pylint: disable=cell-var-from-loop + def loop_fn(i): + a = array_ops.gather(x, i) + return op(a, axis=axis, keepdims=keepdims) + + # pylint: enable=cell-var-from-loop + + self._test_loop_fn(loop_fn, 2) + + def test_cum_sum(self): + x = random_ops.random_uniform([2, 3, 4, 5]) + for axis in (1, -2): + for exclusive in (True, False): + for reverse in (True, False): + + # pylint: disable=cell-var-from-loop + def loop_fn(i): + a = array_ops.gather(x, i) + return math_ops.cumsum( + a, axis=axis, exclusive=exclusive, reverse=reverse) + + # pylint: enable=cell-var-from-loop + + self._test_loop_fn(loop_fn, 2) + + def test_cum_prod(self): + x = random_ops.random_uniform([2, 3, 4, 5]) + for axis in (1, -2): + for exclusive in (True, False): + for reverse in (True, False): + + # pylint: disable=cell-var-from-loop + def loop_fn(i): + a = array_ops.gather(x, i) + return math_ops.cumprod( + a, axis=axis, exclusive=exclusive, reverse=reverse) + + # pylint: enable=cell-var-from-loop + + self._test_loop_fn(loop_fn, 2) + + def test_bias_add(self): + x_shape = [2, 3, 4, 5, 6] + x = random_ops.random_uniform(x_shape) + for data_format in ("NCHW", "NHWC"): + with backprop.GradientTape(persistent=True) as g: + bias_dim = 2 if data_format == "NCHW" else -1 + bias_shape = x_shape[bias_dim] + bias = random_ops.random_uniform([bias_shape]) + g.watch(bias) + + # pylint: disable=cell-var-from-loop + def loop_fn(i): + with g: + a = array_ops.gather(x, i) + y = nn.bias_add(a, bias, data_format=data_format) + loss = math_ops.reduce_sum(y * y) + return y, g.gradient(loss, bias) + # pylint: enable=cell-var-from-loop + + self._test_loop_fn( + loop_fn, 2, loop_fn_dtypes=[dtypes.float32, dtypes.float32]) + + def test_unsorted_segment_sum(self): + t = random_ops.random_uniform([3, 3, 2]) + segment_ids = constant_op.constant([[0, 0, 2], [0, 1, 2], [2, 2, 2]]) + num_segments = 3 + + def loop_fn(i): + data = array_ops.gather(t, i) + data_0 = array_ops.gather(t, 0) + seg_ids = array_ops.gather(segment_ids, i) + return (math_ops.unsorted_segment_sum(data, seg_ids, num_segments), + math_ops.unsorted_segment_sum(data_0, seg_ids, num_segments)) + + self._test_loop_fn(loop_fn, 3, [dtypes.float32] * 2) + + def test_cast(self): + x = constant_op.constant([[1], [2]]) + y = constant_op.constant([[1.0], [2.0]]) + + def loop_fn(i): + return (math_ops.cast(array_ops.gather(x, i), dtypes.float32), + math_ops.cast(array_ops.gather(y, i), dtypes.int32)) + + self._test_loop_fn( + loop_fn, 2, loop_fn_dtypes=[dtypes.float32, dtypes.int32]) + + def test_tanh_axpy(self): + a = constant_op.constant(3.) + x = random_ops.random_uniform([4, 5]) + y = random_ops.random_uniform([6, 5]) + n = x.shape[0] + + def loop_fn(i): + return math_ops.tanh(a * array_ops.gather(x, i) + array_ops.gather(y, i)) + + self._test_loop_fn(loop_fn, n) + + def test_select(self): + cond = constant_op.constant([True, False]) + a = random_ops.random_uniform([2, 3, 5]) + b = random_ops.random_uniform([2, 3, 5]) + for cond_shape in [2], [2, 3], [2, 3, 5]: + cond = random_ops.random_uniform(cond_shape) > 0.5 + + # pylint: disable=cell-var-from-loop + def loop_fn(i): + a_i = array_ops.gather(a, i) + b_i = array_ops.gather(b, i) + cond_i = array_ops.gather(cond, i) + return array_ops.where(cond_i, a_i, b_i) + + # pylint: enable=cell-var-from-loop + + self._test_loop_fn(loop_fn, 2) + + +if __name__ == "__main__": + test.main() diff --git a/tensorflow/python/ops/parallel_for/test_util.py b/tensorflow/python/ops/parallel_for/test_util.py new file mode 100644 index 0000000000..7b4ef2239e --- /dev/null +++ b/tensorflow/python/ops/parallel_for/test_util.py @@ -0,0 +1,59 @@ +# Copyright 2018 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. +# ============================================================================== +"""Test utility.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +from tensorflow.python.framework import dtypes +from tensorflow.python.ops import variables +from tensorflow.python.ops.parallel_for import control_flow_ops as pfor_control_flow_ops +from tensorflow.python.platform import test +from tensorflow.python.util import nest + + +class PForTestCase(test.TestCase): + """Base class for test cases.""" + + def _run_targets(self, targets1, targets2=None, run_init=True): + targets1 = nest.flatten(targets1) + targets2 = ([] if targets2 is None else nest.flatten(targets2)) + assert len(targets1) == len(targets2) or not targets2 + if run_init: + init = variables.global_variables_initializer() + self.evaluate(init) + return self.evaluate(targets1 + targets2) + + def run_and_assert_equal(self, targets1, targets2): + outputs = self._run_targets(targets1, targets2) + outputs = nest.flatten(outputs) # flatten SparseTensorValues + n = len(outputs) // 2 + for i in range(n): + if outputs[i + n].dtype != np.object: + self.assertAllClose(outputs[i + n], outputs[i], rtol=1e-4, atol=1e-5) + else: + self.assertAllEqual(outputs[i + n], outputs[i]) + + def _test_loop_fn(self, loop_fn, iters, + loop_fn_dtypes=dtypes.float32, + parallel_iterations=None): + t1 = pfor_control_flow_ops.pfor(loop_fn, iters=iters, + parallel_iterations=parallel_iterations) + t2 = pfor_control_flow_ops.for_loop(loop_fn, loop_fn_dtypes, iters=iters, + parallel_iterations=parallel_iterations) + self.run_and_assert_equal(t1, t2) -- GitLab From d852dec831a4a4c30c3018187e52e25010bf9711 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Wed, 9 Jan 2019 17:42:18 -0800 Subject: [PATCH 593/622] Use %zu format specifier for size_t printf argument. This fixes -Wformat error in 32-bit Android build. PiperOrigin-RevId: 228621967 --- tensorflow/core/lib/core/status.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/core/lib/core/status.cc b/tensorflow/core/lib/core/status.cc index 3076c09337..0b63f66f6d 100644 --- a/tensorflow/core/lib/core/status.cc +++ b/tensorflow/core/lib/core/status.cc @@ -200,7 +200,7 @@ Status StatusGroup::as_status() const { const std::pair& b) { return a.second < b.second; }); fmt.push_back( - strings::Printf("Combined status information from %lu operations:\n", + strings::Printf("Combined status information from %zu operations:\n", num_ok_ + children_.size())); for (const auto& p : count_vec) { -- GitLab From 0b1b49fd5a39a5834d897dc61068e68f0649e313 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Wed, 9 Jan 2019 17:45:02 -0800 Subject: [PATCH 594/622] Go: Update generated wrapper functions for TensorFlow ops. PiperOrigin-RevId: 228622263 --- tensorflow/go/op/wrappers.go | 52 ++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/tensorflow/go/op/wrappers.go b/tensorflow/go/op/wrappers.go index 208c15d75a..6f6fb793a0 100644 --- a/tensorflow/go/op/wrappers.go +++ b/tensorflow/go/op/wrappers.go @@ -7477,6 +7477,19 @@ func Conv2DBackpropFilterUseCudnnOnGpu(value bool) Conv2DBackpropFilterAttr { } } +// Conv2DBackpropFilterExplicitPaddings sets the optional explicit_paddings attribute to value. +// +// value: If `padding` is `"EXPLICIT"`, the list of explicit padding amounts. For the ith +// dimension, the amount of padding inserted before and after the dimension is +// `explicit_paddings[2 * i]` and `explicit_paddings[2 * i + 1]`, respectively. If +// `padding` is not `"EXPLICIT"`, `explicit_paddings` must be empty. +// If not specified, defaults to <> +func Conv2DBackpropFilterExplicitPaddings(value []int64) Conv2DBackpropFilterAttr { + return func(m optionalAttr) { + m["explicit_paddings"] = value + } +} + // Conv2DBackpropFilterDataFormat sets the optional data_format attribute to value. // // value: Specify the data format of the input and output data. With the @@ -8176,7 +8189,7 @@ func RegexReplaceReplaceGlobal(value bool) RegexReplaceAttr { // Arguments: // input: The text to be processed. // pattern: The regular expression to match the input. -// rewrite: The rewrite to be applied to the matched expresion. +// rewrite: The rewrite to be applied to the matched expression. // // Returns The text after applying pattern and rewrite. func RegexReplace(scope *Scope, input tf.Output, pattern tf.Output, rewrite tf.Output, optional ...RegexReplaceAttr) (output tf.Output) { @@ -11605,15 +11618,12 @@ func NthElement(scope *Scope, input tf.Output, n tf.Output, optional ...NthEleme // // Arguments: // -// segment_ids: A tensor whose shape is a prefix of `data.shape`.END -// } -// out_arg { -// name: "output" -// description: < +func Conv2DBackpropInputExplicitPaddings(value []int64) Conv2DBackpropInputAttr { + return func(m optionalAttr) { + m["explicit_paddings"] = value + } +} + // Conv2DBackpropInputDataFormat sets the optional data_format attribute to value. // // value: Specify the data format of the input and output data. With the @@ -35308,6 +35331,19 @@ func Conv2DUseCudnnOnGpu(value bool) Conv2DAttr { } } +// Conv2DExplicitPaddings sets the optional explicit_paddings attribute to value. +// +// value: If `padding` is `"EXPLICIT"`, the list of explicit padding amounts. For the ith +// dimension, the amount of padding inserted before and after the dimension is +// `explicit_paddings[2 * i]` and `explicit_paddings[2 * i + 1]`, respectively. If +// `padding` is not `"EXPLICIT"`, `explicit_paddings` must be empty. +// If not specified, defaults to <> +func Conv2DExplicitPaddings(value []int64) Conv2DAttr { + return func(m optionalAttr) { + m["explicit_paddings"] = value + } +} + // Conv2DDataFormat sets the optional data_format attribute to value. // // value: Specify the data format of the input and output data. With the -- GitLab From 767a1fe74600fadee070fbaa88cbb5f5917170bd Mon Sep 17 00:00:00 2001 From: Sanjoy Das Date: Wed, 9 Jan 2019 18:31:55 -0800 Subject: [PATCH 595/622] Delete unused ServiceExecutableRunOptions::xla_intra_op_thread_pool; NFC PiperOrigin-RevId: 228627868 --- tensorflow/compiler/xla/client/local_client.cc | 5 ++--- tensorflow/compiler/xla/service/hlo_runner.cc | 4 +--- tensorflow/compiler/xla/service/service.cc | 4 +--- .../xla/service/service_executable_run_options.h | 14 +++----------- .../compiler/xla/tests/xla_hlo_profile_test.cc | 5 ++--- 5 files changed, 9 insertions(+), 23 deletions(-) diff --git a/tensorflow/compiler/xla/client/local_client.cc b/tensorflow/compiler/xla/client/local_client.cc index 049cd15738..48b5f94538 100644 --- a/tensorflow/compiler/xla/client/local_client.cc +++ b/tensorflow/compiler/xla/client/local_client.cc @@ -164,9 +164,8 @@ StatusOr LocalExecutable::Run( // ExecutableRunOptions.eigen_intra_op_thread_pool. // *) The thread pool used for XLA CPU ops is from // backend_->eigen_intra_op_thread_pool(). - ServiceExecutableRunOptions service_options( - run_options, backend_->StreamBorrower(), - backend_->eigen_intra_op_thread_pool()); + ServiceExecutableRunOptions service_options(run_options, + backend_->StreamBorrower()); if (executable_->dumping_snapshot()) { return ExecuteAndDump(&service_options, arguments); diff --git a/tensorflow/compiler/xla/service/hlo_runner.cc b/tensorflow/compiler/xla/service/hlo_runner.cc index 5a9b820a9d..d7d66ae1c4 100644 --- a/tensorflow/compiler/xla/service/hlo_runner.cc +++ b/tensorflow/compiler/xla/service/hlo_runner.cc @@ -383,9 +383,7 @@ ServiceExecutableRunOptions HloRunner::GetServiceRunOptionsForDevice( if (device_assignment != nullptr) { run_options.set_device_assignment(device_assignment); } - return ServiceExecutableRunOptions( - run_options, backend().StreamBorrower(), - /*xla_intra_op_thread_pool=*/backend().eigen_intra_op_thread_pool()); + return ServiceExecutableRunOptions(run_options, backend().StreamBorrower()); } Backend& HloRunner::backend() { diff --git a/tensorflow/compiler/xla/service/service.cc b/tensorflow/compiler/xla/service/service.cc index 4a8d272261..2732d498d8 100644 --- a/tensorflow/compiler/xla/service/service.cc +++ b/tensorflow/compiler/xla/service/service.cc @@ -553,9 +553,7 @@ StatusOr Service::ExecuteAndRegisterResult( options.set_intra_op_thread_pool( backend->eigen_intra_op_thread_pool_device()); options.set_device_assignment(&device_assignment); - run_options.emplace_back( - options, backend->StreamBorrower(), - /*xla_intra_op_thread_pool=*/backend->eigen_intra_op_thread_pool()); + run_options.emplace_back(options, backend->StreamBorrower()); } if (options_.number_of_replicas() == 1) { diff --git a/tensorflow/compiler/xla/service/service_executable_run_options.h b/tensorflow/compiler/xla/service/service_executable_run_options.h index dbfed628bf..6bee671056 100644 --- a/tensorflow/compiler/xla/service/service_executable_run_options.h +++ b/tensorflow/compiler/xla/service/service_executable_run_options.h @@ -32,12 +32,10 @@ class ServiceExecutableRunOptions { ServiceExecutableRunOptions() : ServiceExecutableRunOptions(ExecutableRunOptions()) {} - explicit ServiceExecutableRunOptions( - ExecutableRunOptions run_options, StreamBorrower borrow_stream = nullptr, - tensorflow::thread::ThreadPool* xla_intra_op_thread_pool = nullptr) + explicit ServiceExecutableRunOptions(ExecutableRunOptions run_options, + StreamBorrower borrow_stream = nullptr) : run_options_(std::move(run_options)), - borrow_stream_(std::move(borrow_stream)), - xla_intra_op_thread_pool_(xla_intra_op_thread_pool) {} + borrow_stream_(std::move(borrow_stream)) {} // Returns reference or pointer to `ExecutableRunOptions` member. const ExecutableRunOptions& run_options() const { return run_options_; } @@ -56,15 +54,9 @@ class ServiceExecutableRunOptions { : Status(tensorflow::error::UNIMPLEMENTED, "No stream cache"); } - // Returns reference to thread pool for execution of XLA ops on CPU backend. - tensorflow::thread::ThreadPool* xla_intra_op_thread_pool() const { - return xla_intra_op_thread_pool_; - } - private: ExecutableRunOptions run_options_; StreamBorrower borrow_stream_; - tensorflow::thread::ThreadPool* xla_intra_op_thread_pool_; }; } // namespace xla diff --git a/tensorflow/compiler/xla/tests/xla_hlo_profile_test.cc b/tensorflow/compiler/xla/tests/xla_hlo_profile_test.cc index 1538f2afba..c7337e8caa 100644 --- a/tensorflow/compiler/xla/tests/xla_hlo_profile_test.cc +++ b/tensorflow/compiler/xla/tests/xla_hlo_profile_test.cc @@ -174,9 +174,8 @@ void ExecuteAndFetchProfile(string* profile_output, LocalClient* client, exec_run_options.set_allocator(backend->memory_allocator()); exec_run_options.set_intra_op_thread_pool( backend->eigen_intra_op_thread_pool_device()); - ServiceExecutableRunOptions run_options( - exec_run_options, /*borrow_stream=*/nullptr, - backend->eigen_intra_op_thread_pool()); + ServiceExecutableRunOptions run_options(exec_run_options, + /*borrow_stream=*/nullptr); std::vector args = {&lhs_arg, &rhs_arg}; TF_ASSERT_OK_AND_ASSIGN( auto execution_result, -- GitLab From ebb3429856441149e41388dfbea59496f8dbf17b Mon Sep 17 00:00:00 2001 From: Penporn Koanantakool Date: Wed, 9 Jan 2019 22:30:09 -0800 Subject: [PATCH 596/622] Remove a TODO about moving choose() out of TensorContraction.h since it is now in unsupported/Eigen/CXX11/scr/Tensor/TensorMeta.h. PiperOrigin-RevId: 228648231 --- tensorflow/core/kernels/eigen_spatial_convolutions.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/tensorflow/core/kernels/eigen_spatial_convolutions.h b/tensorflow/core/kernels/eigen_spatial_convolutions.h index 86d8c98ee6..8b19813940 100644 --- a/tensorflow/core/kernels/eigen_spatial_convolutions.h +++ b/tensorflow/core/kernels/eigen_spatial_convolutions.h @@ -1683,8 +1683,6 @@ EIGEN_DEVICE_FUNC kernel_dims[0] = kernelChannels * kernelRows * kernelCols; kernel_dims[1] = kernelFilters; } - // TODO(yangke): choose() is defined in TensorContraction.h -- consider - // moving it to somewhere more "common". return choose( Cond::Layout == ColMajor>(), kernel.reshape(kernel_dims) -- GitLab From 6d3cbd2bcc165f8de064643719991db9753c8dca Mon Sep 17 00:00:00 2001 From: Smit Hinsu Date: Wed, 9 Jan 2019 23:57:12 -0800 Subject: [PATCH 597/622] Update TensorRT SegmentGraph function to return node pointers This is in preparation to share the graph segmentation code between XLA and TensorRT. TESTED with existing unit tests PiperOrigin-RevId: 228654815 --- .../contrib/tensorrt/convert/convert_graph.cc | 51 ++++++++++--------- .../contrib/tensorrt/convert/convert_nodes.cc | 15 +++--- .../contrib/tensorrt/convert/convert_nodes.h | 3 +- .../contrib/tensorrt/segment/segment.cc | 17 +++---- tensorflow/contrib/tensorrt/segment/segment.h | 8 +-- .../contrib/tensorrt/segment/segment_test.cc | 5 +- 6 files changed, 53 insertions(+), 46 deletions(-) diff --git a/tensorflow/contrib/tensorrt/convert/convert_graph.cc b/tensorflow/contrib/tensorrt/convert/convert_graph.cc index bf2de94e04..eef647473a 100644 --- a/tensorflow/contrib/tensorrt/convert/convert_graph.cc +++ b/tensorflow/contrib/tensorrt/convert/convert_graph.cc @@ -334,13 +334,12 @@ struct EdgePtrCompare { tensorflow::Status GetEngineInfo( const tensorflow::Graph* g, const tensorflow::grappler::GraphProperties& graph_properties, - const std::set& segment_nodes, + const std::set& segment_nodes, const std::unordered_map& node_map, const std::vector& reverse_topo_order, EngineInfo* info) { - std::vector subgraph_node_ids; // Topologically sorted node ids. - std::set subgraph_node_names = segment_nodes; - std::set added_const_node_ids; // Used to prevent double insertion. + std::vector subgraph_nodes; // Topologically sorted nodes. + std::set added_const_nodes; // Used to prevent double insertion. std::set segment_devices; // Map from src_node_name+port to the unique port numbers of the TRT op, where @@ -352,9 +351,8 @@ tensorflow::Status GetEngineInfo( std::unordered_map input_to_engine_port, output_to_engine_port; for (auto it = reverse_topo_order.rbegin(); it != reverse_topo_order.rend(); ++it) { - const auto& node_name = (*it)->name(); - if (segment_nodes.count(node_name) == 0) continue; - auto node = *it; + const Node* node = *it; + if (segment_nodes.count(node) == 0) continue; auto node_device = node->requested_device(); if (!node_device.empty()) { segment_devices.insert(node_device); @@ -366,8 +364,11 @@ tensorflow::Status GetEngineInfo( << " neither have requested device nor assigned device"; } } + subgraph_nodes.push_back(node); + const int node_id = node->id(); - subgraph_node_ids.push_back(node_id); + const string& node_name = node->name(); + // Create input connections. Sort edges first to make determnistic since // in_edges is a set of pointers. std::vector in_edges(node->in_edges().begin(), @@ -375,7 +376,7 @@ tensorflow::Status GetEngineInfo( std::sort(in_edges.begin(), in_edges.end(), EdgePtrCompare()); for (const auto edge : in_edges) { auto input_node = edge->src(); - if (input_node->IsSource() || segment_nodes.count(input_node->name())) { + if (input_node->IsSource() || segment_nodes.count(input_node)) { continue; } if (edge->IsControlEdge()) { @@ -392,12 +393,11 @@ tensorflow::Status GetEngineInfo( // // Note that the segmenter already ensure that the constant data input // is valid and suppported by the engine. - if (!added_const_node_ids.insert(input_node->id()).second) { + if (!added_const_nodes.insert(input_node).second) { // Already added before. continue; } VLOG(1) << "Adding const node " << input_node->name(); - QCHECK(subgraph_node_names.insert(input_node->name()).second); // Since we already add (duplicate) the const input node to the segment // graphdef, it's now not a data dependency any more, but to make the // dependency correct we still add a control dependency. @@ -428,7 +428,7 @@ tensorflow::Status GetEngineInfo( std::sort(out_edges.begin(), out_edges.end(), EdgePtrCompare()); for (const auto edge : out_edges) { auto output_node = edge->dst(); - if (output_node->IsSink() || segment_nodes.count(output_node->name())) { + if (output_node->IsSink() || segment_nodes.count(output_node)) { continue; } if (edge->IsControlEdge()) { @@ -456,12 +456,11 @@ tensorflow::Status GetEngineInfo( } // For each segment node in topological order. // Construct the const nodes first. - subgraph_node_ids.insert(subgraph_node_ids.begin(), - added_const_node_ids.begin(), - added_const_node_ids.end()); + subgraph_nodes.insert(subgraph_nodes.begin(), added_const_nodes.begin(), + added_const_nodes.end()); TF_RETURN_IF_ERROR(ConvertSegmentToGraphDef( - g, graph_properties, subgraph_node_names, subgraph_node_ids, - &info->connections, &info->segment_graph_def, &info->engine_name)); + g, graph_properties, subgraph_nodes, &info->connections, + &info->segment_graph_def, &info->engine_name)); // TODO(sami): This should not happen once segmenter is updated. if (segment_devices.size() == 1) { info->device = *segment_devices.begin(); @@ -1033,27 +1032,31 @@ tensorflow::Status ConvertAfterShapes(ConversionParams& params) { cudaSetDevice(cuda_device_id); auto status = CreateTRTNode(engine_segments, i, params.max_batch_size, &graph, alloc.get(), &engine_nodes); - // If status is ok, we successfully added the node to the graph and can - // remove segment ops. Otherwise graph is not modified. + string msg = StrCat("TensorRT node ", engine.engine_name, " added for segment ", i, " consisting of ", converted_segments.at(i).first.size(), " nodes"); if (status.ok()) { LOG(INFO) << msg << " succeeded."; - for (auto node_name : converted_segments.at(i).first) { - graph.RemoveNode(node_map.at(node_name)); - } } else { // Graph is not modified. LOG(WARNING) << msg << " failed: " << status << ". Fallback to TF..."; } if (VLOG_IS_ON(1)) { msg = "Segment consists of nodes: "; - for (const string& node_name : converted_segments.at(i).first) { - StrAppend(&msg, node_name, ", "); + for (const Node* node : converted_segments.at(i).first) { + StrAppend(&msg, node->name(), ", "); } VLOG(1) << msg; } + + // If status is ok, we successfully added the node to the graph and can + // remove segment ops. Otherwise graph is not modified. + if (status.ok()) { + for (const Node* node : converted_segments.at(i).first) { + graph.RemoveNode(const_cast(node)); + } + } } cudaSetDevice(old_cuda_device); graph.ToGraphDef(params.output_graph_def); diff --git a/tensorflow/contrib/tensorrt/convert/convert_nodes.cc b/tensorflow/contrib/tensorrt/convert/convert_nodes.cc index 179d631a3e..7b0c4b446d 100644 --- a/tensorflow/contrib/tensorrt/convert/convert_nodes.cc +++ b/tensorflow/contrib/tensorrt/convert/convert_nodes.cc @@ -3780,8 +3780,7 @@ tensorflow::Status ConvertGraphDefToEngine( tensorflow::Status ConvertSegmentToGraphDef( const tensorflow::Graph* graph, const tensorflow::grappler::GraphProperties& graph_properties, - const std::set& subgraph_node_names, - const std::vector& subgraph_node_ids, // In topological order + const std::vector& subgraph_nodes, // In topological order std::vector* connections, tensorflow::GraphDef* segment_def, string* common_scope) { std::set marker_nodes; @@ -3855,11 +3854,10 @@ tensorflow::Status ConvertSegmentToGraphDef( std::unordered_map old_to_new_id_map; // Copy internal nodes to new graphdef - string local_scope = graph->FindNodeId(*subgraph_node_ids.begin())->name(); - for (const auto node_id : subgraph_node_ids) { - const auto node = graph->FindNodeId(node_id); + string local_scope = subgraph_nodes.front()->name(); + for (const Node* node : subgraph_nodes) { local_scope = GetCommonNameScope(local_scope, node->name()); - old_to_new_id_map[node_id] = segment_def->node_size(); + old_to_new_id_map[node->id()] = segment_def->node_size(); auto snode = segment_def->add_node(); snode->CopyFrom(node->def()); VLOG(2) << "Copying " << snode->name() << " to subgraph"; @@ -3877,6 +3875,11 @@ tensorflow::Status ConvertSegmentToGraphDef( << placeholder_name; snode->set_input(connection.inside_port, placeholder_name); } + std::set subgraph_node_names; + for (const Node* node : subgraph_nodes) { + subgraph_node_names.insert(node->name()); + } + // Remove control inputs that are not inside the segment. for (int i = 0; i < segment_def->node_size(); ++i) { auto snode = segment_def->mutable_node(i); diff --git a/tensorflow/contrib/tensorrt/convert/convert_nodes.h b/tensorflow/contrib/tensorrt/convert/convert_nodes.h index 54e19b7395..8f2271ee3f 100644 --- a/tensorflow/contrib/tensorrt/convert/convert_nodes.h +++ b/tensorflow/contrib/tensorrt/convert/convert_nodes.h @@ -128,8 +128,7 @@ struct EngineInfo { tensorflow::Status ConvertSegmentToGraphDef( const tensorflow::Graph* graph, const tensorflow::grappler::GraphProperties& graph_properties, - const std::set& subgraph_node_names, - const std::vector& subgraph_node_ids, + const std::vector& subgraph_nodes, std::vector* connections, tensorflow::GraphDef* segment_def, string* common_scope); diff --git a/tensorflow/contrib/tensorrt/segment/segment.cc b/tensorflow/contrib/tensorrt/segment/segment.cc index 084a96e0fa..ecaffa3023 100644 --- a/tensorflow/contrib/tensorrt/segment/segment.cc +++ b/tensorflow/contrib/tensorrt/segment/segment.cc @@ -673,10 +673,11 @@ tensorflow::Status SegmentGraph( // --------------------------------- Step 3 --------------------------------- // Convert the segments into the expected return format for (const auto& itr : sg_map) { - const std::set& segment_nodes = - itr.second; + const string& segment_root = itr.first; + // Return format does not require set comparator. + std::set segment_nodes(itr.second.begin(), itr.second.end()); if (VLOG_IS_ON(1)) { - string s = "parent=" + itr.first + ":"; + string s = "parent=" + segment_root + ":"; for (auto node : segment_nodes) s += " " + node->name(); VLOG(1) << "Segment " << segments->size() << ": " << s; } @@ -689,12 +690,10 @@ tensorflow::Status SegmentGraph( } // TODO(sami): Make segmenter placement aware once trtscopes are in place - std::set segment_node_names; - for (auto node : itr.second) segment_node_names.insert(node->name()); - const auto& dev_itr = device_maps.find(itr.first); + const auto& dev_itr = device_maps.find(segment_root); if (dev_itr == device_maps.end() || dev_itr->second.empty()) { VLOG(1) << "No device assigned to segment " << segments->size(); - segments->emplace_back(std::make_pair(segment_node_names, string())); + segments->emplace_back(std::make_pair(segment_nodes, string())); } else if (dev_itr->second.size() > 1) { string s("Segment "); StrAppend(&s, segments->size(), " has multiple devices attached: "); @@ -703,10 +702,10 @@ tensorflow::Status SegmentGraph( } LOG(WARNING) << s << " choosing " << *(dev_itr->second.begin()); segments->emplace_back( - std::make_pair(segment_node_names, *(dev_itr->second.begin()))); + std::make_pair(segment_nodes, *(dev_itr->second.begin()))); } else { segments->emplace_back( - std::make_pair(segment_node_names, *(dev_itr->second.begin()))); + std::make_pair(segment_nodes, *(dev_itr->second.begin()))); } } if (VLOG_IS_ON(1)) { diff --git a/tensorflow/contrib/tensorrt/segment/segment.h b/tensorflow/contrib/tensorrt/segment/segment.h index b9693aad1b..6cc92cdb5d 100644 --- a/tensorflow/contrib/tensorrt/segment/segment.h +++ b/tensorflow/contrib/tensorrt/segment/segment.h @@ -29,10 +29,10 @@ namespace tensorflow { namespace tensorrt { namespace segment { -// Vector of segments, each entry contains a set of node names and a device name -// in the segment. -// TODO(aaroey): use node pointer instead of node name. -using SegmentNodesVector = std::vector, string>>; +// Vector of segments, each entry contains a set of node pointers and a device +// name in the segment. +using SegmentNodesVector = + std::vector, string>>; struct SegmentOptions { // Segment must contain at least this many nodes. diff --git a/tensorflow/contrib/tensorrt/segment/segment_test.cc b/tensorflow/contrib/tensorrt/segment/segment_test.cc index 4805ef9c61..4ac02327ae 100644 --- a/tensorflow/contrib/tensorrt/segment/segment_test.cc +++ b/tensorflow/contrib/tensorrt/segment/segment_test.cc @@ -75,7 +75,10 @@ class SegmentTest : public ::testing::Test { const std::vector>& expected_segments) { EXPECT_EQ(expected_segments.size(), segments.size()); for (int i = 0; i < segments.size(); ++i) { - const auto& segment_node_names = segments[i].first; + std::set segment_node_names; + for (const Node* node : segments[i].first) { + segment_node_names.insert(node->name()); + } const auto& expected = expected_segments[i]; for (const auto& name : expected) { EXPECT_TRUE(segment_node_names.count(name)) -- GitLab From 439d4ec9568a259f665efd878791530a6f5ca8f2 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Thu, 10 Jan 2019 01:02:17 -0800 Subject: [PATCH 598/622] compat: Update forward compatibility horizon to 2019-01-10 PiperOrigin-RevId: 228661930 --- tensorflow/python/compat/compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/python/compat/compat.py b/tensorflow/python/compat/compat.py index dfee972cab..638bd445a6 100644 --- a/tensorflow/python/compat/compat.py +++ b/tensorflow/python/compat/compat.py @@ -27,7 +27,7 @@ import datetime from tensorflow.python.util import tf_contextlib from tensorflow.python.util.tf_export import tf_export -_FORWARD_COMPATIBILITY_HORIZON = datetime.date(2019, 1, 9) +_FORWARD_COMPATIBILITY_HORIZON = datetime.date(2019, 1, 10) @tf_export("compat.forward_compatible") -- GitLab From 2f7e56a65104aeca0a7fcc98a7b10fb2cff9b1a3 Mon Sep 17 00:00:00 2001 From: Tamara Norman Date: Thu, 10 Jan 2019 02:46:36 -0800 Subject: [PATCH 599/622] Remove partitioned_info as argument to initializer in Keras make_variable to work with the v2 initializers PiperOrigin-RevId: 228675005 --- tensorflow/python/keras/engine/base_layer_utils.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tensorflow/python/keras/engine/base_layer_utils.py b/tensorflow/python/keras/engine/base_layer_utils.py index 60d0cf3391..f1b1f42c22 100644 --- a/tensorflow/python/keras/engine/base_layer_utils.py +++ b/tensorflow/python/keras/engine/base_layer_utils.py @@ -25,6 +25,7 @@ from tensorflow.python.framework import ops from tensorflow.python.keras import backend from tensorflow.python.ops import array_ops from tensorflow.python.ops import init_ops +from tensorflow.python.ops import init_ops_v2 from tensorflow.python.ops import variables as tf_variables from tensorflow.python.util import nest @@ -55,7 +56,6 @@ def make_variable(name, shape=None, dtype=dtypes.float32, initializer=None, - partition_info=None, trainable=None, caching_device=None, validate_shape=True, @@ -76,14 +76,12 @@ def make_variable(name, rid of this temporary solution. TODO(fchollet): remove this method when no longer needed. - TODO(fchollet): handle `partitioner` argument. Arguments: name: Variable name. shape: Variable shape. dtype: The type of the variable. Defaults to `self.dtype` or `float32`. initializer: Initializer instance (callable). - partition_info: Not handled at this time. trainable: Whether the variable should be part of the layer's "trainable_variables" (e.g. variables, biases) or "non_trainable_variables" (e.g. BatchNorm mean, stddev). @@ -123,8 +121,9 @@ def make_variable(name, # Instantiate initializer if provided initializer is a type object. if isinstance(initializer, type(init_ops.Initializer)): initializer = initializer(dtype=dtype) - init_val = lambda: initializer( # pylint: disable=g-long-lambda - shape, dtype=dtype, partition_info=partition_info) + elif isinstance(initializer, type(init_ops_v2.Initializer)): + initializer = initializer() + init_val = lambda: initializer(shape, dtype=dtype) variable_dtype = dtype.base_dtype if use_resource is None: use_resource = True -- GitLab From ef088e54734e0412bc47e4f33d1df7c0672fdd87 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Thu, 10 Jan 2019 04:21:44 -0800 Subject: [PATCH 600/622] Add a test for soft matching of inputs. PiperOrigin-RevId: 228686125 --- tensorflow/python/saved_model/load_test.py | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tensorflow/python/saved_model/load_test.py b/tensorflow/python/saved_model/load_test.py index f7020e4a45..8b34414d25 100644 --- a/tensorflow/python/saved_model/load_test.py +++ b/tensorflow/python/saved_model/load_test.py @@ -29,6 +29,7 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import tensor_spec from tensorflow.python.lib.io import file_io from tensorflow.python.ops import variables +from tensorflow.python.saved_model import function_serialization from tensorflow.python.saved_model import load from tensorflow.python.saved_model import save from tensorflow.python.training.checkpointable import tracking @@ -312,6 +313,36 @@ class LoadTest(test.TestCase): x = constant_op.constant(1.0) self.assertAllEqual(imported(x).numpy(), 3.0) + def test_soft_matching(self): + + @def_function.function( + input_signature=[tensor_spec.TensorSpec([None], dtypes.int32)]) + def func(x): + return 2 * x + + root = tracking.Checkpointable() + root.f = func + + self.assertAllEqual([2], root.f(constant_op.constant([1])).numpy()) + self.assertAllEqual([2, 4], root.f(constant_op.constant([1, 2])).numpy()) + + self.assertEqual( + 1, len(function_serialization.list_all_concrete_functions(root.f))) + + imported = self.cycle(root) + + with self.assertRaises(AssertionError): + # We cannot call the function with a constant of shape (). + self.assertEqual(7, imported.f(constant_op.constant(2)).numpy()) + + # TODO(vbardiovsky): When classes are revived with input_signatures, we + # should also check that the calls below are not generating any more + # concrete functions. + self.assertAllEqual([2, 4, 6, 8], + imported.f(constant_op.constant([1, 2, 3, 4])).numpy()) + self.assertAllEqual([2, 4, 6], + imported.f(constant_op.constant([1, 2, 3])).numpy()) + if __name__ == "__main__": test.main() -- GitLab From 9ad0810fd55096fc86a58300c5a2710b2f3b5175 Mon Sep 17 00:00:00 2001 From: Peter Hawkins Date: Thu, 10 Jan 2019 08:15:42 -0800 Subject: [PATCH 601/622] [XLA:Python] Fix build error in opensource due to xla::int64/protobuf_int64 differences. PiperOrigin-RevId: 228713538 --- tensorflow/compiler/xla/python/local_computation_builder.i | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/compiler/xla/python/local_computation_builder.i b/tensorflow/compiler/xla/python/local_computation_builder.i index cc96d84724..66ecee5c4d 100644 --- a/tensorflow/compiler/xla/python/local_computation_builder.i +++ b/tensorflow/compiler/xla/python/local_computation_builder.i @@ -171,7 +171,7 @@ bool HandleStringAttribute(PyObject* o, bool HandleRepeatedInt64Attribute( PyObject* o, const char* attr_name, - tensorflow::protobuf::RepeatedField* field) { + tensorflow::protobuf::RepeatedField* field) { PyObject* seq = PyObject_GetAttrString(o, attr_name); if (!seq) { return false; -- GitLab From 4e462bc347ddadeb939e4861f77fa349c083fe64 Mon Sep 17 00:00:00 2001 From: Karim Nosir Date: Thu, 10 Jan 2019 09:31:51 -0800 Subject: [PATCH 602/622] Update unit-test to enable for OSS PiperOrigin-RevId: 228724452 --- tensorflow/lite/models/smartreply/BUILD | 1 - .../lite/models/smartreply/predictor_test.cc | 15 +++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/tensorflow/lite/models/smartreply/BUILD b/tensorflow/lite/models/smartreply/BUILD index a3f4e61cb5..fccbf79eb9 100644 --- a/tensorflow/lite/models/smartreply/BUILD +++ b/tensorflow/lite/models/smartreply/BUILD @@ -58,7 +58,6 @@ tf_cc_test( "//tensorflow/lite/models:testdata/smartreply_samples.tsv", "@tflite_smartreply//:smartreply.tflite", ], - tags = ["no_oss"], deps = [ ":predictor_lib", "//tensorflow/core:test", diff --git a/tensorflow/lite/models/smartreply/predictor_test.cc b/tensorflow/lite/models/smartreply/predictor_test.cc index 9bdd7b537a..9ce8ce9a77 100644 --- a/tensorflow/lite/models/smartreply/predictor_test.cc +++ b/tensorflow/lite/models/smartreply/predictor_test.cc @@ -31,12 +31,16 @@ namespace custom { namespace smartreply { namespace { -const char kModelName[] = "smartreply_ondevice_model.bin"; const char kSamples[] = "smartreply_samples.tsv"; -string TestDataPath() { +string GetModelFilePath() { + return "third_party/tensorflow/lite/models/testdata/" + "smartreply_ondevice_model.bin"; +} + +string GetSamplesFilePath() { return string(absl::StrCat(tensorflow::testing::TensorFlowSrcRoot(), "/", - "lite/models/testdata/")); + "lite/models/testdata/", kSamples)); } MATCHER_P(IncludeAnyResponesIn, expected_response, "contains the response") { @@ -57,8 +61,7 @@ class PredictorTest : public ::testing::Test { ~PredictorTest() override {} void SetUp() override { - model_ = tflite::FlatBufferModel::BuildFromFile( - absl::StrCat(TestDataPath(), "/", kModelName).c_str()); + model_ = tflite::FlatBufferModel::BuildFromFile(GetModelFilePath().c_str()); ASSERT_NE(model_.get(), nullptr); } @@ -123,7 +126,7 @@ TEST_F(PredictorTest, BatchTest) { int total_triggers = 0; string line; - std::ifstream fin(absl::StrCat(TestDataPath(), "/", kSamples)); + std::ifstream fin(GetSamplesFilePath()); while (std::getline(fin, line)) { const std::vector fields = absl::StrSplit(line, '\t'); if (fields.empty()) { -- GitLab From 73b6d9262ea6d1578374ecb63560ad0fcad13e35 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Thu, 10 Jan 2019 09:55:14 -0800 Subject: [PATCH 603/622] Split Keras TPU correctness tests by models. PiperOrigin-RevId: 228727689 --- tensorflow/contrib/distribute/python/BUILD | 30 +- ...test.py => keras_correctness_test_base.py} | 261 ++++-------------- .../python/keras_dnn_correctness_test.py | 163 +++++++++++ .../keras_image_model_correctness_test.py | 92 ++++++ 4 files changed, 337 insertions(+), 209 deletions(-) rename tensorflow/contrib/distribute/python/{keras_correctness_test.py => keras_correctness_test_base.py} (58%) create mode 100644 tensorflow/contrib/distribute/python/keras_dnn_correctness_test.py create mode 100644 tensorflow/contrib/distribute/python/keras_image_model_correctness_test.py diff --git a/tensorflow/contrib/distribute/python/BUILD b/tensorflow/contrib/distribute/python/BUILD index d4758d7518..4c25f49f16 100644 --- a/tensorflow/contrib/distribute/python/BUILD +++ b/tensorflow/contrib/distribute/python/BUILD @@ -657,7 +657,11 @@ cuda_py_test( py_library( name = "keras_correctness_test_lib", testonly = 1, - srcs = ["keras_correctness_test.py"], + srcs = [ + "keras_correctness_test_base.py", + "keras_dnn_correctness_test.py", + "keras_image_model_correctness_test.py", + ], deps = [ ":combinations", "//tensorflow/contrib/distribute/python:mirrored_strategy", @@ -673,8 +677,9 @@ py_library( ) cuda_py_test( - name = "keras_correctness_test", - srcs = ["keras_correctness_test.py"], + name = "keras_dnn_correctness_test", + size = "medium", + srcs = ["keras_dnn_correctness_test.py"], additional_deps = [ ":keras_correctness_test_lib", ], @@ -690,6 +695,25 @@ cuda_py_test( ], ) +cuda_py_test( + name = "keras_image_model_correctness_test", + size = "medium", + srcs = ["keras_image_model_correctness_test.py"], + additional_deps = [ + ":keras_correctness_test_lib", + ], + # Shard count is set to an odd number to distribute tasks across + # shards more evenly. + shard_count = 31, + tags = [ + "multi_and_single_gpu", + "no_oss", # TODO(b/117919883): Fix python error. + "no_pip", + "no_windows_gpu", + "notsan", + ], +) + py_library( name = "metrics_v1_test_lib", testonly = 1, diff --git a/tensorflow/contrib/distribute/python/keras_correctness_test.py b/tensorflow/contrib/distribute/python/keras_correctness_test_base.py similarity index 58% rename from tensorflow/contrib/distribute/python/keras_correctness_test.py rename to tensorflow/contrib/distribute/python/keras_correctness_test_base.py index 3abdee2c0e..e13984b26d 100644 --- a/tensorflow/contrib/distribute/python/keras_correctness_test.py +++ b/tensorflow/contrib/distribute/python/keras_correctness_test_base.py @@ -30,8 +30,6 @@ from tensorflow.python.eager import context from tensorflow.python.eager import test from tensorflow.python.framework import random_seed from tensorflow.python.keras.engine import distributed_training_utils -from tensorflow.python.keras.optimizer_v2 import gradient_descent as gradient_descent_keras -from tensorflow.python.training import gradient_descent _RANDOM_SEED = 1337 _EVAL_STEPS = 20 @@ -62,24 +60,13 @@ def all_strategy_combinations_with_graph_mode(): return combinations.combine(distribution=all_strategies, mode=['graph']) -def strategy_and_input_combinations(): - def cnn_model_with_batch_norm(**kwargs): - return _create_cnn_model(with_batch_norm=True, **kwargs) - +def all_strategy_and_input_config_combinations(): return ( combinations.times( combinations.combine(distribution=all_strategies), combinations.combine(mode=['graph', 'eager'], use_numpy=[True, False], - use_validation_data=[True, False]), - combinations.combine(model_with_data=[ - ModelWithData('dnn', _create_dnn_model, _dnn_training_data), - ModelWithData('cnn', _create_cnn_model, _cnn_training_data), - ModelWithData('cnn_batch_norm', - cnn_model_with_batch_norm, - _cnn_training_data, - with_batch_norm=True), - ]))) + use_validation_data=[True, False]))) class MaybeDistributionScope(object): @@ -100,97 +87,6 @@ class MaybeDistributionScope(object): self._scope = None -class ModelWithData(object): - """An object giving a good name in combinations. - - The model_fn must take two arguments: initial_weights and distribution. - """ - - def __init__(self, name, model_fn, data_fn, with_batch_norm=False): - self.name = name - self.model_fn = model_fn - self.data_fn = data_fn - self.with_batch_norm = with_batch_norm - - def __repr__(self): - return self.name - - -def _dnn_training_data(): - # TODO(xiejw): Change this back to 10000, once we support final partial - # batch. - num_samples = 9984 - x_train = np.random.rand(num_samples, 1) - y_train = 3 * x_train - x_train = x_train.astype('float32') - y_train = y_train.astype('float32') - x_predict = [[1.], [2.], [3.], [4.]] - return x_train, y_train, x_predict - - -def _create_dnn_model(initial_weights=None, distribution=None): - with MaybeDistributionScope(distribution): - # We add few non-linear layers to make it non-trivial. - model = keras.Sequential() - model.add(keras.layers.Dense(10, activation='relu', input_shape=(1,))) - model.add(keras.layers.Dense(10, activation='relu')) - model.add(keras.layers.Dense(10, activation='relu')) - model.add(keras.layers.Dense(1)) - - if initial_weights: - model.set_weights(initial_weights) - - model.compile( - loss=keras.losses.mean_squared_error, - optimizer=gradient_descent_keras.SGD(0.5), - metrics=['mse']) - return model - - -def _cnn_training_data(count=_GLOBAL_BATCH_SIZE * _EVAL_STEPS, - shape=(28, 28, 3), num_classes=10): - centers = np.random.randn(num_classes, *shape) - - features = [] - labels = [] - for _ in range(count): - label = np.random.randint(0, num_classes, size=1)[0] - offset = np.random.normal(loc=0, scale=0.1, size=np.prod(shape)) - offset = offset.reshape(shape) - labels.append(label) - features.append(centers[label] + offset) - - x_train = np.asarray(features, dtype=np.float32) - y_train = np.asarray(labels, dtype=np.float32).reshape((count, 1)) - x_predict = x_train - return x_train, y_train, x_predict - - -def _create_cnn_model(initial_weights=None, distribution=None, - with_batch_norm=False): - with MaybeDistributionScope(distribution): - image = keras.layers.Input(shape=(28, 28, 3), name='image') - c1 = keras.layers.Conv2D( - name='conv1', filters=16, kernel_size=(3, 3), strides=(4, 4))( - image) - if with_batch_norm: - c1 = keras.layers.BatchNormalization(name='bn1')(c1) - c1 = keras.layers.MaxPooling2D(pool_size=(2, 2))(c1) - logits = keras.layers.Dense( - 10, activation='softmax', name='pred')( - keras.layers.Flatten()(c1)) - model = keras.Model(inputs=[image], outputs=[logits]) - - if initial_weights: - model.set_weights(initial_weights) - - model.compile( - optimizer=gradient_descent.GradientDescentOptimizer(learning_rate=0.1), - loss='sparse_categorical_crossentropy', - metrics=['sparse_categorical_accuracy']) - return model - - def batch_wrapper(dataset, batch_size, distribution, repeat=None): if repeat: dataset = dataset.repeat(repeat) @@ -350,6 +246,11 @@ def compare_results(results_with_ds, results_without_ds, distribution, msg='Fail to assert {}.'.format(key)) +def should_skip_tpu_with_eager(distribution): + return (context.executing_eagerly() and + isinstance(distribution, tpu_strategy.TPUStrategy)) + + class LearningRateBatchScheduler(keras.callbacks.Callback): def __init__(self, update_freq=None): @@ -364,107 +265,60 @@ class LearningRateBatchScheduler(keras.callbacks.Callback): keras.backend.set_value(self.model.optimizer.lr, lr) -class TestDistributionStrategyCorrectness(test.TestCase, - parameterized.TestCase): +class TestDistributionStrategyCorrectnessBase(test.TestCase, + parameterized.TestCase): + """Model agnostic testing infra to test correctness of Keras models.""" + + def set_up_test_config(self, use_numpy=False, + use_validation_data=False, + with_batch_norm=False): + self.use_numpy = use_numpy + self.use_validation_data = use_validation_data + self.with_batch_norm = with_batch_norm - def _should_skip_tpu_with_eager(self, distribution): - return (context.executing_eagerly() and - isinstance(distribution, tpu_strategy.TPUStrategy)) + keras.backend.set_image_data_format('channels_last') + np.random.seed(_RANDOM_SEED) + random_seed.set_random_seed(_RANDOM_SEED) - @combinations.generate(all_strategy_combinations_with_eager_and_graph_modes()) - def test_metric_correctness(self, distribution): - if self._should_skip_tpu_with_eager(distribution): - self.skipTest('TPUStrategy does not support eager mode now.') + def get_data(self): + num_samples = 10000 + x_train = np.random.randint(0, 2, num_samples) + x_train = np.reshape(x_train, (num_samples, 1)) + y_train = x_train + return (x_train.astype('float32'), y_train.astype('float32'), None) - with self.cached_session(): - keras.backend.set_image_data_format('channels_last') - num_samples = 10000 - - x_train = np.random.randint(0, 2, num_samples) - x_train = np.reshape(x_train, (num_samples, 1)) - y_train = x_train - x_train = x_train.astype('float32') - y_train = y_train.astype('float32') - - # Create identity model. - with distribution.scope(): - model = keras.Sequential() - model.add( - keras.layers.Dense(1, input_shape=(1,), kernel_initializer='ones')) - model.compile( - loss=keras.losses.mean_squared_error, - optimizer=gradient_descent.GradientDescentOptimizer(0.5), - metrics=[keras.metrics.BinaryAccuracy()]) - - batch_size = 64 - batch_size = get_batch_size(batch_size, distribution) - train_dataset = dataset_ops.Dataset.from_tensor_slices((x_train, y_train)) - train_dataset = batch_wrapper(train_dataset, batch_size, distribution) - - history = model.fit(x=train_dataset, epochs=2, steps_per_epoch=10) - self.assertEqual(history.history['binary_accuracy'], [1.0, 1.0]) - - @combinations.generate(all_strategy_combinations_with_eager_and_graph_modes()) - def test_eval_metrics_correctness(self, distribution): - if self._should_skip_tpu_with_eager(distribution): - self.skipTest('TPUStrategy does not support eager mode now.') + def get_model(self, distribution=None): + raise NotImplementedError - with self.cached_session(): - with distribution.scope(): - model = keras.Sequential() - model.add( - keras.layers.Dense( - 3, activation='relu', input_dim=4, kernel_initializer='ones')) - model.add( - keras.layers.Dense( - 1, activation='sigmoid', kernel_initializer='ones')) - model.compile( - loss='mae', - metrics=['accuracy', keras.metrics.BinaryAccuracy()], - optimizer=gradient_descent.GradientDescentOptimizer(0.001)) - - # verify correctness of stateful and stateless metrics. - x = np.ones((100, 4)).astype('float32') - y = np.ones((100, 1)).astype('float32') - dataset = dataset_ops.Dataset.from_tensor_slices((x, y)).repeat() - dataset = batch_wrapper(dataset, 4, distribution) - outs = model.evaluate(dataset, steps=10) - self.assertEqual(outs[1], 1.) - self.assertEqual(outs[2], 1.) - - y = np.zeros((100, 1)).astype('float32') - dataset = dataset_ops.Dataset.from_tensor_slices((x, y)).repeat() - dataset = batch_wrapper(dataset, 4, distribution) - outs = model.evaluate(dataset, steps=10) - self.assertEqual(outs[1], 0.) - self.assertEqual(outs[2], 0.) - - @combinations.generate(strategy_and_input_combinations()) - def test_correctness(self, distribution, use_numpy, use_validation_data, - model_with_data): - if self._should_skip_tpu_with_eager(distribution): + def skip_unsupported_test_configuration(self, distribution): + if should_skip_tpu_with_eager(distribution): self.skipTest('TPUStrategy does not support eager mode now.') - if context.executing_eagerly() and use_numpy: + if context.executing_eagerly() and self.use_numpy: self.skipTest('Numpy as inputs is not supported with strategy in eager.') - if context.executing_eagerly() and use_validation_data: - self.skipTest('TODO') + if context.executing_eagerly() and self.use_validation_data: + self.skipTest('TODO(hongjunchoi): Add test logic for using validation ' + 'data for eager execution.') + return + def run_correctness_test(self, + distribution, + use_numpy, + use_validation_data, + with_batch_norm=False): with self.cached_session(): - keras.backend.set_image_data_format('channels_last') - np.random.seed(_RANDOM_SEED) - random_seed.set_random_seed(_RANDOM_SEED) + self.set_up_test_config(use_numpy, use_validation_data, with_batch_norm) + self.skip_unsupported_test_configuration(distribution) - model_fn, data_fn = model_with_data.model_fn, model_with_data.data_fn # Train, eval, and predict datasets are created with the same input numpy # arrays. - x_train, y_train, x_predict = data_fn() + x_train, y_train, x_predict = self.get_data() # The model is built once and the initial weights are saved. # This is used to initialize the model for both the distribution and # non-distribution run. - model = model_fn() + model = self.get_model() initial_weights = model.get_weights() def input_fn(dist): @@ -472,15 +326,15 @@ class TestDistributionStrategyCorrectness(test.TestCase, use_numpy, use_validation_data, dist, x_train, y_train, x_predict) results_with_ds = fit_eval_and_predict( - initial_weights, input_fn=input_fn, model_fn=model_fn, + initial_weights, input_fn=input_fn, model_fn=self.get_model, distribution=distribution) results_without_ds = fit_eval_and_predict( - initial_weights, input_fn=input_fn, model_fn=model_fn, + initial_weights, input_fn=input_fn, model_fn=self.get_model, distribution=None) # First, special case, for multi-replica distributed training, batch norm # is not aggregated globally. So it is expected to have different weights. - if (model_with_data.with_batch_norm and + if (self.with_batch_norm and distribution.num_replicas_in_sync > 1): with self.assertRaises(AssertionError): compare_results(results_with_ds, results_without_ds, distribution, @@ -489,21 +343,16 @@ class TestDistributionStrategyCorrectness(test.TestCase, compare_results(results_with_ds, results_without_ds, distribution, testcase=self) - @combinations.generate(all_strategy_combinations_with_graph_mode()) - def test_dynamic_lr(self, distribution): - + def run_dynamic_lr_test(self, distribution): with self.cached_session(): + self.set_up_test_config() + self.skip_unsupported_test_configuration(distribution) - keras.backend.set_image_data_format('channels_last') - np.random.seed(_RANDOM_SEED) - random_seed.set_random_seed(_RANDOM_SEED) - - x_train, y_train, _ = _dnn_training_data() - - model = _create_dnn_model() + x_train, y_train, _ = self.get_data() + model = self.get_model() initial_weights = model.get_weights() - update_freq = None + if (isinstance(distribution, tpu_strategy.TPUStrategy) and distribution.extended.steps_per_run > 1): # For TPUStrategy with steps_per_run > 1, the callback is not invoked @@ -530,10 +379,10 @@ class TestDistributionStrategyCorrectness(test.TestCase, return training_inputs, eval_inputs, predict_inputs results_with_ds = fit_eval_and_predict( - initial_weights, input_fn=input_fn, model_fn=_create_dnn_model, + initial_weights, input_fn=input_fn, model_fn=self.get_model, distribution=distribution) results_without_ds = fit_eval_and_predict( - initial_weights, input_fn=input_fn, model_fn=_create_dnn_model, + initial_weights, input_fn=input_fn, model_fn=self.get_model, distribution=None) compare_results(results_with_ds, results_without_ds, distribution, testcase=self) diff --git a/tensorflow/contrib/distribute/python/keras_dnn_correctness_test.py b/tensorflow/contrib/distribute/python/keras_dnn_correctness_test.py new file mode 100644 index 0000000000..f21b1c6752 --- /dev/null +++ b/tensorflow/contrib/distribute/python/keras_dnn_correctness_test.py @@ -0,0 +1,163 @@ +# 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. +# ============================================================================== +"""Correctness tests for tf.keras DNN model using DistributionStrategy.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +from tensorflow.contrib.distribute.python import combinations +from tensorflow.contrib.distribute.python import keras_correctness_test_base +from tensorflow.python import keras +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.eager import test +from tensorflow.python.keras.optimizer_v2 import gradient_descent as gradient_descent_keras +from tensorflow.python.training import gradient_descent + + +class TestDistributionStrategyDnnCorrectness( + keras_correctness_test_base.TestDistributionStrategyCorrectnessBase): + + def get_model(self, initial_weights=None, distribution=None): + with keras_correctness_test_base.MaybeDistributionScope(distribution): + # We add few non-linear layers to make it non-trivial. + model = keras.Sequential() + model.add(keras.layers.Dense(10, activation='relu', input_shape=(1,))) + model.add(keras.layers.Dense(10, activation='relu')) + model.add(keras.layers.Dense(10, activation='relu')) + model.add(keras.layers.Dense(1)) + + if initial_weights: + model.set_weights(initial_weights) + + model.compile( + loss=keras.losses.mean_squared_error, + optimizer=gradient_descent_keras.SGD(0.5), + metrics=['mse']) + return model + + def get_data(self): + # TODO(xiejw): Change this back to 10000, once we support final partial + # batch. + num_samples = 9984 + x_train = np.random.rand(num_samples, 1) + y_train = 3 * x_train + x_train = x_train.astype('float32') + y_train = y_train.astype('float32') + x_predict = [[1.], [2.], [3.], [4.]] + return x_train, y_train, x_predict + + @combinations.generate(keras_correctness_test_base. + all_strategy_and_input_config_combinations()) + def test_dnn_correctness(self, distribution, use_numpy, use_validation_data): + self.run_correctness_test(distribution, use_numpy, use_validation_data) + + @combinations.generate(keras_correctness_test_base. + all_strategy_combinations_with_graph_mode()) + def test_dnn_with_dynamic_learning_rate(self, distribution): + self.run_dynamic_lr_test(distribution) + + +class TestDistributionStrategyDnnMetricCorrectness( + keras_correctness_test_base.TestDistributionStrategyCorrectnessBase): + + def get_model(self, distribution=None): + with distribution.scope(): + model = keras.Sequential() + model.add(keras.layers.Dense(1, + input_shape=(1,), + kernel_initializer='ones')) + model.compile( + loss=keras.losses.mean_squared_error, + optimizer=gradient_descent.GradientDescentOptimizer(0.5), + metrics=[keras.metrics.BinaryAccuracy()]) + return model + + def run_metric_correctness_test(self, distribution): + with self.cached_session(): + self.set_up_test_config() + self.skip_unsupported_test_configuration(distribution) + + x_train, y_train, _ = self.get_data() + model = self.get_model(distribution=distribution) + + batch_size = 64 + batch_size = (keras_correctness_test_base. + get_batch_size(batch_size, distribution)) + train_dataset = dataset_ops.Dataset.from_tensor_slices((x_train, y_train)) + train_dataset = (keras_correctness_test_base. + batch_wrapper(train_dataset, batch_size, distribution)) + + history = model.fit(x=train_dataset, epochs=2, steps_per_epoch=10) + self.assertEqual(history.history['binary_accuracy'], [1.0, 1.0]) + + @combinations.generate(keras_correctness_test_base. + all_strategy_combinations_with_eager_and_graph_modes()) + def test_simple_dnn_metric_correctness(self, distribution): + self.run_metric_correctness_test(distribution) + + +class TestDistributionStrategyDnnMetricEvalCorrectness( + keras_correctness_test_base.TestDistributionStrategyCorrectnessBase): + + def get_model(self, distribution=None): + with distribution.scope(): + model = keras.Sequential() + model.add( + keras.layers.Dense( + 3, activation='relu', input_dim=4, kernel_initializer='ones')) + model.add( + keras.layers.Dense( + 1, activation='sigmoid', kernel_initializer='ones')) + model.compile( + loss='mae', + metrics=['accuracy', keras.metrics.BinaryAccuracy()], + optimizer=gradient_descent.GradientDescentOptimizer(0.001)) + return model + + def run_eval_metrics_correctness_test(self, distribution): + with self.cached_session(): + self.set_up_test_config() + self.skip_unsupported_test_configuration(distribution) + + model = self.get_model(distribution=distribution) + + # verify correctness of stateful and stateless metrics. + x = np.ones((100, 4)).astype('float32') + y = np.ones((100, 1)).astype('float32') + dataset = dataset_ops.Dataset.from_tensor_slices((x, y)).repeat() + dataset = (keras_correctness_test_base. + batch_wrapper(dataset, 4, distribution)) + outs = model.evaluate(dataset, steps=10) + self.assertEqual(outs[1], 1.) + self.assertEqual(outs[2], 1.) + + y = np.zeros((100, 1)).astype('float32') + dataset = dataset_ops.Dataset.from_tensor_slices((x, y)).repeat() + dataset = (keras_correctness_test_base. + batch_wrapper(dataset, 4, distribution)) + outs = model.evaluate(dataset, steps=10) + self.assertEqual(outs[1], 0.) + self.assertEqual(outs[2], 0.) + + @combinations.generate(keras_correctness_test_base. + all_strategy_combinations_with_eager_and_graph_modes()) + def test_identity_model_metric_eval_correctness(self, distribution): + self.run_eval_metrics_correctness_test(distribution) + + +if __name__ == '__main__': + test.main() diff --git a/tensorflow/contrib/distribute/python/keras_image_model_correctness_test.py b/tensorflow/contrib/distribute/python/keras_image_model_correctness_test.py new file mode 100644 index 0000000000..f625664372 --- /dev/null +++ b/tensorflow/contrib/distribute/python/keras_image_model_correctness_test.py @@ -0,0 +1,92 @@ +# 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. +# ============================================================================== +"""Correctness tests for tf.keras CNN models using DistributionStrategy.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +from tensorflow.contrib.distribute.python import combinations +from tensorflow.contrib.distribute.python import keras_correctness_test_base +from tensorflow.python import keras +from tensorflow.python.eager import test +from tensorflow.python.training import gradient_descent + + +class DistributionStrategyCnnCorrectnessTest( + keras_correctness_test_base.TestDistributionStrategyCorrectnessBase): + + def get_model(self, initial_weights=None, distribution=None): + with keras_correctness_test_base.MaybeDistributionScope(distribution): + image = keras.layers.Input(shape=(28, 28, 3), name='image') + c1 = keras.layers.Conv2D( + name='conv1', filters=16, kernel_size=(3, 3), strides=(4, 4))( + image) + if self.with_batch_norm: + c1 = keras.layers.BatchNormalization(name='bn1')(c1) + c1 = keras.layers.MaxPooling2D(pool_size=(2, 2))(c1) + logits = keras.layers.Dense( + 10, activation='softmax', name='pred')( + keras.layers.Flatten()(c1)) + model = keras.Model(inputs=[image], outputs=[logits]) + + if initial_weights: + model.set_weights(initial_weights) + + model.compile( + optimizer=gradient_descent.GradientDescentOptimizer( + learning_rate=0.1), + loss='sparse_categorical_crossentropy', + metrics=['sparse_categorical_accuracy']) + + return model + + def get_data(self, + count=keras_correctness_test_base._GLOBAL_BATCH_SIZE + * keras_correctness_test_base._EVAL_STEPS, + shape=(28, 28, 3), + num_classes=10): + centers = np.random.randn(num_classes, *shape) + + features = [] + labels = [] + for _ in range(count): + label = np.random.randint(0, num_classes, size=1)[0] + offset = np.random.normal(loc=0, scale=0.1, size=np.prod(shape)) + offset = offset.reshape(shape) + labels.append(label) + features.append(centers[label] + offset) + + x_train = np.asarray(features, dtype=np.float32) + y_train = np.asarray(labels, dtype=np.float32).reshape((count, 1)) + x_predict = x_train + return x_train, y_train, x_predict + + @combinations.generate(keras_correctness_test_base. + all_strategy_and_input_config_combinations()) + def test_cnn_correctness(self, distribution, use_numpy, use_validation_data): + self.run_correctness_test(distribution, use_numpy, use_validation_data) + + @combinations.generate(keras_correctness_test_base. + all_strategy_and_input_config_combinations()) + def test_cnn_with_batch_norm_correctness(self, distribution, use_numpy, + use_validation_data): + self.run_correctness_test(distribution, use_numpy, use_validation_data, + with_batch_norm=True) + + +if __name__ == '__main__': + test.main() -- GitLab From 1210b6506dbde52a5bf5c33d56f3c52dfbd505e7 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Thu, 10 Jan 2019 10:28:19 -0800 Subject: [PATCH 604/622] fix links to renamed markdown documentation PiperOrigin-RevId: 228733282 --- tensorflow/lite/g3doc/apis.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tensorflow/lite/g3doc/apis.md b/tensorflow/lite/g3doc/apis.md index b15159ce41..1a05142bc4 100644 --- a/tensorflow/lite/g3doc/apis.md +++ b/tensorflow/lite/g3doc/apis.md @@ -1,4 +1,3 @@ - # TensorFlow Lite APIs TensorFlow Lite provides programming APIs in C++ and Java, and in both cases @@ -8,8 +7,7 @@ no surprise that the APIs try to avoid unnecessary copies at the expense of convenience. Similarly, consistency with TensorFlow APIs was not an explicit goal and some variance is to be expected. -There is also a Python API for TensorFlow Lite described -[here](../toco/g3doc/python_api.md#interpreter). +There is also a [Python API for TensorFlow Lite](g3doc/convert/python_api.md). ## C++ -- GitLab From 3c4b35297e4b44baaf4cc7ed5b099c586ffa049b Mon Sep 17 00:00:00 2001 From: Sanjoy Das Date: Thu, 10 Jan 2019 10:44:31 -0800 Subject: [PATCH 605/622] Re-apply "Don't check for alignment before calling into a tiled dot (Eigen or LLVM)" with fix PiperOrigin-RevId: 228736280 --- tensorflow/compiler/xla/service/cpu/BUILD | 2 - .../cpu/cpu_eigen_tensor_alignment_test.cc | 38 ------ .../xla/service/cpu/dot_op_emitter.cc | 117 ++++++++++++------ 3 files changed, 77 insertions(+), 80 deletions(-) diff --git a/tensorflow/compiler/xla/service/cpu/BUILD b/tensorflow/compiler/xla/service/cpu/BUILD index de62aa60aa..a197bdddc8 100644 --- a/tensorflow/compiler/xla/service/cpu/BUILD +++ b/tensorflow/compiler/xla/service/cpu/BUILD @@ -385,7 +385,6 @@ cc_library( srcs = ["dot_op_emitter.cc"], hdrs = [ "dot_op_emitter.h", - "dot_op_emitter_internal.h", ], deps = [ ":cpu_options", @@ -1029,7 +1028,6 @@ tf_cc_test( size = "small", srcs = ["cpu_eigen_tensor_alignment_test.cc"], deps = [ - ":dot_op_emitter", ":ir_emission_utils", ":target_machine_features_fake", "//tensorflow/compiler/xla:test", diff --git a/tensorflow/compiler/xla/service/cpu/cpu_eigen_tensor_alignment_test.cc b/tensorflow/compiler/xla/service/cpu/cpu_eigen_tensor_alignment_test.cc index 823bdf259c..485769a373 100644 --- a/tensorflow/compiler/xla/service/cpu/cpu_eigen_tensor_alignment_test.cc +++ b/tensorflow/compiler/xla/service/cpu/cpu_eigen_tensor_alignment_test.cc @@ -13,7 +13,6 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "tensorflow/compiler/xla/service/cpu/dot_op_emitter_internal.h" #include "tensorflow/compiler/xla/service/cpu/ir_emission_utils.h" #include "tensorflow/compiler/xla/service/cpu/target_machine_features_fake.h" #include "tensorflow/compiler/xla/service/hlo_parser.h" @@ -23,48 +22,11 @@ namespace xla { namespace cpu { namespace { -using internal::DotImplementationStrategy; -using internal::DotInfo; -using internal::GetDotImplementationStrategy; - // Test that we don't call into Eigen with tensors too small to be aligned // reliably. class CpuEigenTensorAlignmentTest : public ::testing::Test {}; -TEST_F(CpuEigenTensorAlignmentTest, EigenDotAlignment) { - string hlo_string = R"( -HloModule DotOperation - -ENTRY DotOperation { - arg0 = f32[5,256] parameter(0) - arg1 = f32[256,1024] parameter(1) - ROOT dot = f32[5,1024] dot(arg0, arg1), lhs_contracting_dims={1}, rhs_contracting_dims={0} -} -)"; - - TF_ASSERT_OK_AND_ASSIGN(std::unique_ptr module, - ParseHloString(hlo_string)); - - HloInstruction* dot = module->entry_computation()->root_instruction(); - - TargetMachineFeaturesWithFakeAlignmentLogic target_machine_with_no_alignment( - [](int64 size) { return 1; }); - - EXPECT_EQ(GetDotImplementationStrategy(HloModuleConfig{}, DotInfo(*dot), - target_machine_with_no_alignment), - DotImplementationStrategy::kNaiveLlvmIr); - - TargetMachineFeaturesWithFakeAlignmentLogic - target_machine_with_full_alignment([](int64 size) { - return TargetMachineFeatures::kEigenExpectedTensorAlignment; - }); - - EXPECT_NE(GetDotImplementationStrategy(HloModuleConfig{}, DotInfo(*dot), - target_machine_with_full_alignment), - DotImplementationStrategy::kNaiveLlvmIr); -} - TEST_F(CpuEigenTensorAlignmentTest, EigenConvAlignment) { string hlo_string = R"( HloModule ConvOperation diff --git a/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc b/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc index e8a84ebe6b..b018e0cd46 100644 --- a/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc +++ b/tensorflow/compiler/xla/service/cpu/dot_op_emitter.cc @@ -14,7 +14,6 @@ limitations under the License. ==============================================================================*/ #include "tensorflow/compiler/xla/service/cpu/dot_op_emitter.h" -#include "tensorflow/compiler/xla/service/cpu/dot_op_emitter_internal.h" #include #include @@ -43,17 +42,63 @@ namespace xla { using llvm_ir::SetToFirstInsertPoint; namespace cpu { - -using internal::DotImplementationStrategy; -using internal::DotInfo; -using internal::GetDotImplementationStrategy; - namespace { // Returns true if we should call into multi-threaded Eigen routines. bool ShouldUseMultiThreadedEigen(const HloModuleConfig& config) { return config.debug_options().xla_cpu_multi_thread_eigen(); } +// Represents a dot operation. We use this in lieu of an `HloInstruction` +// because we want to be able to create this for the "inner" dot operation in a +// batch dot, for which there is no separate HLO instruction. +struct DotInfo { + Shape lhs_shape; + Shape rhs_shape; + Shape result_shape; + DotDimensionNumbers dim_nums; + + explicit DotInfo(const HloInstruction& instr) { + CHECK_EQ(instr.opcode(), HloOpcode::kDot); + lhs_shape = instr.operand(0)->shape(); + rhs_shape = instr.operand(1)->shape(); + result_shape = instr.shape(); + dim_nums = instr.dot_dimension_numbers(); + } +}; + +// Dictates how a dot operation is implemented. +enum class DotImplementationStrategy { + // The dot operation is lowered into LLVM IR that implements a naive nested + // loop that computes the result one element at a time. This is our + // "fallback"; we don't really want this to kick in for any non-trival dot + // operation. + kNaiveLlvmIr, + + // The dot operation is lowered into LLVM IR that implements a tiled + // Matrix*Vector operation. This strategy also allows fusing in a bias add + // into the dot. The matrix can be row major or column major, both are + // supported. + kTiledLlvmIrGemv, + + // The dot operation is lowered into LLVM IR that implemetns a tiled + // Matrix*Matrix operation. No fusions are supported. The two inputs + // and the output have to be row major. + kTiledLlvmIrGemm, + + // The dot operation is lowered into a call into an Eigen routine. No fusions + // are supported today. The two inputs and the output have to be row major. + // However, we do allow transposing either the LHS or the RHS as part of the + // GEMM -- we expose this flexibility as flexibility in the contraction + // dimensions, but we can also see this as flexibility in the input layouts. + kEigen, +}; + +// Returns the implementation strategy for a dot with the configuration +// `dot_info`. +DotImplementationStrategy GetDotImplementationStrategy( + const HloModuleConfig& config, const DotInfo& dot_info, + const TargetMachineFeatures& target_machine_features); + // Helper class for emitting LLVM IR to perform the dot operation. class DotOpEmitter { public: @@ -190,9 +235,8 @@ void DotOpEmitter::EmitTiledLlvmIrGemm() { } int64 size_bytes = m * n * ShapeUtil::ByteSizeOfPrimitiveType(primitive_type); - b_->CreateMemSet( - target, b_->getInt8(0), size_bytes, - target_machine_features_.minimum_alignment_for_allocation(size_bytes)); + b_->CreateMemSet(target, b_->getInt8(0), /*Size=*/size_bytes, + /*Align=*/1); int64 max_target_vector_width = target_machine_features_.vector_register_num_elements( @@ -696,40 +740,34 @@ absl::optional ProfitableToMakeDotOperandColumnMajor( return {}; } -namespace internal { namespace { // Return whether the given shape is rank 2. bool IsRank2(const Shape& shape) { return shape.rank() == 2; } +bool IsSimpleLayout(const Layout& layout) { + return layout.tiles().empty() && layout.format() == DENSE; +} + // In a gemm operation where output = lhs * rhs, check whether the given shapes // are valid for the operation. -bool AreAlignedGemmShapes( - const Shape& lhs_shape, const Shape& rhs_shape, const Shape& output_shape, - const TargetMachineFeatures& target_machine_features) { - // The inputs and the output must - // 1) be matrices with no padding, and - // 2) have an allowed element type. - PrimitiveType output_primitive_type = output_shape.element_type(); - if (!(output_primitive_type == F64 || output_primitive_type == F32 || - output_primitive_type == F16)) { - return false; - } - - if (!(IsRank2(lhs_shape) && IsRank2(rhs_shape) && IsRank2(output_shape))) { - return false; - } - - auto is_aligned = [&](const Shape& shape) { - return GetMinimumAlignmentForArray(shape, target_machine_features) >= - TargetMachineFeatures::kEigenExpectedTensorAlignment; - }; - - if (!is_aligned(lhs_shape) || !is_aligned(rhs_shape) || - !is_aligned(output_shape)) { - return false; +bool AreGemmShapes(const Shape& lhs_shape, const Shape& rhs_shape, + const Shape& output_shape, + const TargetMachineFeatures& target_machine_features) { + CHECK(!lhs_shape.has_layout() || IsSimpleLayout(lhs_shape.layout())) + << lhs_shape.DebugString(); + CHECK(!rhs_shape.has_layout() || IsSimpleLayout(rhs_shape.layout())) + << rhs_shape.DebugString(); + CHECK(!output_shape.has_layout() || IsSimpleLayout(output_shape.layout())) + << output_shape.DebugString(); + + switch (output_shape.element_type()) { + case F64: + case F32: + case F16: + return IsRank2(lhs_shape) && IsRank2(rhs_shape) && IsRank2(output_shape); + default: + return false; } - - return true; } bool IsAlignedGemm(const DotInfo& dot_info, @@ -739,8 +777,8 @@ bool IsAlignedGemm(const DotInfo& dot_info, return false; } - return AreAlignedGemmShapes(dot_info.lhs_shape, dot_info.rhs_shape, - dot_info.result_shape, target_machine_features); + return AreGemmShapes(dot_info.lhs_shape, dot_info.rhs_shape, + dot_info.result_shape, target_machine_features); } bool CanEmitTiledLlvmIrGemm( @@ -781,7 +819,6 @@ bool CanEmitTiledLlvmIrGemm( return true; } -} // namespace DotImplementationStrategy GetDotImplementationStrategy( const HloModuleConfig& config, const DotInfo& dot_info, @@ -806,7 +843,7 @@ DotImplementationStrategy GetDotImplementationStrategy( return DotImplementationStrategy::kNaiveLlvmIr; } -} // namespace internal +} // namespace bool DotImplementationCanHandleTranspose( const HloInstruction& dot_instr, -- GitLab From eae721281d6c5bdd1e84a17de56c1f2cb911fd76 Mon Sep 17 00:00:00 2001 From: Michael Kuperstein Date: Thu, 10 Jan 2019 11:07:54 -0800 Subject: [PATCH 606/622] [XLA] Move over some uses of CreateDS / CreateDUS to new API Move over some uses of the R1-index API to use the span-of-scalars one. PiperOrigin-RevId: 228740894 --- .../xla/service/algebraic_simplifier_test.cc | 34 ++++++++++----- .../cpu/cpu_instruction_fusion_test.cc | 43 ++++++++++++------- 2 files changed, 51 insertions(+), 26 deletions(-) diff --git a/tensorflow/compiler/xla/service/algebraic_simplifier_test.cc b/tensorflow/compiler/xla/service/algebraic_simplifier_test.cc index e39321f1f3..916b953cfc 100644 --- a/tensorflow/compiler/xla/service/algebraic_simplifier_test.cc +++ b/tensorflow/compiler/xla/service/algebraic_simplifier_test.cc @@ -3774,12 +3774,16 @@ TEST_F(AlgebraicSimplifierTest, TrivialDynamicSlice) { HloComputation::Builder builder(TestName()); Shape shape = ShapeUtil::MakeShape(F32, {10, 100, 1000}); + std::vector params; + for (int i = 0; i < 3; ++i) { + params.push_back(builder.AddInstruction(HloInstruction::CreateParameter( + i + 1, ShapeUtil::MakeShape(U32, {}), "slice_indices"))); + } builder.AddInstruction(HloInstruction::CreateDynamicSlice( shape, builder.AddInstruction( HloInstruction::CreateParameter(0, shape, "slice_from")), - builder.AddInstruction(HloInstruction::CreateParameter( - 1, ShapeUtil::MakeShape(U32, {3}), "slice_indices")), + params, /*slice_sizes=*/{10, 100, 1000})); auto computation = m->AddEntryComputation(builder.Build()); @@ -3798,28 +3802,35 @@ TEST_F(AlgebraicSimplifierTest, TrivialDynamicUpdateSlice) { Shape full_shape = ShapeUtil::MakeShape(F32, {10, 100, 1000}); Shape slice_shape = ShapeUtil::MakeShape(F32, {10, 1, 1000}); + std::vector slice_indices, update_indices; + for (int i = 0; i < 3; ++i) { + slice_indices.push_back( + builder.AddInstruction(HloInstruction::CreateParameter( + i + 1, ShapeUtil::MakeShape(U32, {}), "slice_indices"))); + update_indices.push_back( + builder.AddInstruction(HloInstruction::CreateParameter( + i + 5, ShapeUtil::MakeShape(U32, {}), "update_indices"))); + } HloInstruction* slice = builder.AddInstruction(HloInstruction::CreateDynamicSlice( slice_shape, builder.AddInstruction( HloInstruction::CreateParameter(0, full_shape, "slice_from")), - builder.AddInstruction(HloInstruction::CreateParameter( - 1, ShapeUtil::MakeShape(U32, {3}), "slice_indices")), + slice_indices, /*slice_sizes=*/{10, 1, 1000})); builder.AddInstruction(HloInstruction::CreateDynamicUpdateSlice( slice_shape, builder.AddInstruction( - HloInstruction::CreateParameter(2, slice_shape, "to_update")), - slice, - builder.AddInstruction(HloInstruction::CreateParameter( - 3, ShapeUtil::MakeShape(U32, {3}), "update_indices")))); + HloInstruction::CreateParameter(4, slice_shape, "to_update")), + slice, update_indices)); auto computation = m->AddEntryComputation(builder.Build()); AlgebraicSimplifier simplifier(default_options_); ASSERT_TRUE(simplifier.Run(m.get()).ValueOrDie()); EXPECT_THAT(computation->root_instruction(), - GmockMatch(m::DynamicSlice(m::Parameter(), m::Parameter()))); + GmockMatch(m::DynamicSlice(m::Parameter(), m::Parameter(), + m::Parameter(), m::Parameter()))); } // Test that two consecutive broadcasts can be merged to one. @@ -4480,9 +4491,10 @@ TEST_F(AlgebraicSimplifierTest, DynamicUpdateSliceZeroUpdate) { HloInstruction* const update = builder.AddInstruction( HloInstruction::CreateParameter(1, update_shape, "update")); HloInstruction* const start_indices = builder.AddInstruction( - HloInstruction::CreateConstant(LiteralUtil::CreateR1({0}))); + HloInstruction::CreateConstant(LiteralUtil::CreateR0({}))); builder.AddInstruction(HloInstruction::CreateDynamicUpdateSlice( - dslice_shape, operand, update, start_indices)); + dslice_shape, operand, update, + std::initializer_list({start_indices}))); const HloComputation* const computation = m->AddEntryComputation(builder.Build()); diff --git a/tensorflow/compiler/xla/service/cpu/cpu_instruction_fusion_test.cc b/tensorflow/compiler/xla/service/cpu/cpu_instruction_fusion_test.cc index 527df0bd1c..46c1d4c38e 100644 --- a/tensorflow/compiler/xla/service/cpu/cpu_instruction_fusion_test.cc +++ b/tensorflow/compiler/xla/service/cpu/cpu_instruction_fusion_test.cc @@ -332,7 +332,7 @@ TEST_F(OpcodeFusionTest, Exponential_Reshape_Negate) { TEST_F(OpcodeFusionTest, Broadcast_Reshape_DynamicSlice_Tanh) { HloComputation::Builder builder(TestName()); Shape param_shape = ShapeUtil::MakeShape(F32, {8}); - Shape starts_shape = ShapeUtil::MakeShape(F32, {2}); + Shape starts_shape = ShapeUtil::MakeShape(F32, {}); Shape broadcast_shape = ShapeUtil::MakeShape(F32, {1, 8, 8}); Shape reshape_shape = ShapeUtil::MakeShape(F32, {8, 8}); Shape dynamic_slice_shape = ShapeUtil::MakeShape(F32, {4, 4}); @@ -340,13 +340,15 @@ TEST_F(OpcodeFusionTest, Broadcast_Reshape_DynamicSlice_Tanh) { HloInstruction::CreateParameter(0, param_shape, "param")); HloInstruction* param1 = builder.AddInstruction( HloInstruction::CreateParameter(1, starts_shape, "starts")); + HloInstruction* param2 = builder.AddInstruction( + HloInstruction::CreateParameter(2, starts_shape, "starts")); HloInstruction* broadcast2 = builder.AddInstruction( HloInstruction::CreateBroadcast(broadcast_shape, param0, {1})); HloInstruction* reshape3 = builder.AddInstruction( HloInstruction::CreateReshape(reshape_shape, broadcast2)); HloInstruction* dynamic_slice4 = builder.AddInstruction(HloInstruction::CreateDynamicSlice( - dynamic_slice_shape, reshape3, param1, {4, 4})); + dynamic_slice_shape, reshape3, {param1, param2}, {4, 4})); builder.AddInstruction(HloInstruction::CreateUnary( dynamic_slice_shape, HloOpcode::kTanh, dynamic_slice4)); @@ -356,7 +358,8 @@ TEST_F(OpcodeFusionTest, Broadcast_Reshape_DynamicSlice_Tanh) { RunFusionAndCheckOpcodesWereFused( module.get(), {HloOpcode::kTanh, HloOpcode::kDynamicSlice, HloOpcode::kReshape, - HloOpcode::kBroadcast, HloOpcode::kParameter, HloOpcode::kParameter}); + HloOpcode::kBroadcast, HloOpcode::kParameter, HloOpcode::kParameter, + HloOpcode::kParameter}); } TEST_F(OpcodeFusionTest, Broadcast_Negate) { @@ -381,14 +384,16 @@ TEST_F(OpcodeFusionTest, Broadcast_Negate) { TEST_F(OpcodeFusionTest, DynamicSlice_Negate) { HloComputation::Builder builder(TestName()); Shape param_shape = ShapeUtil::MakeShape(F32, {4}); - Shape slice_shape = ShapeUtil::MakeShape(F32, {1}); + Shape slice_shape = ShapeUtil::MakeShape(F32, {}); Shape result_shape = ShapeUtil::MakeShape(F32, {2}); HloInstruction* param0 = builder.AddInstruction( HloInstruction::CreateParameter(0, param_shape, "param")); HloInstruction* param1 = builder.AddInstruction( HloInstruction::CreateParameter(1, slice_shape, "starts")); - HloInstruction* dynamic_slice2 = builder.AddInstruction( - HloInstruction::CreateDynamicSlice(result_shape, param0, param1, {2})); + HloInstruction* dynamic_slice2 = + builder.AddInstruction(HloInstruction::CreateDynamicSlice( + result_shape, param0, + std::initializer_list({param1}), {2})); builder.AddInstruction(HloInstruction::CreateUnary( result_shape, HloOpcode::kNegate, dynamic_slice2)); @@ -548,28 +553,36 @@ TEST_F(OpcodeFusionTest, DynamicSliceWithDynamicUpdateSlice) { Shape full_shape = ShapeUtil::MakeShape(F32, {10, 100, 1000}); Shape slice_shape = ShapeUtil::MakeShape(F32, {10, 1, 1000}); + std::vector slice_indices, update_indices; + for (int i = 0; i < 3; ++i) { + slice_indices.push_back( + builder.AddInstruction(HloInstruction::CreateParameter( + 1 + i, ShapeUtil::MakeShape(U32, {}), "slice_indices"))); + update_indices.push_back( + builder.AddInstruction(HloInstruction::CreateParameter( + 5 + i, ShapeUtil::MakeShape(U32, {}), "update_indices"))); + } HloInstruction* slice = builder.AddInstruction(HloInstruction::CreateDynamicSlice( slice_shape, builder.AddInstruction( HloInstruction::CreateParameter(0, full_shape, "slice_from")), - builder.AddInstruction(HloInstruction::CreateParameter( - 1, ShapeUtil::MakeShape(U32, {3}), "slice_indices")), + slice_indices, /*slice_sizes=*/{10, 1, 1000})); builder.AddInstruction(HloInstruction::CreateDynamicUpdateSlice( full_shape, builder.AddInstruction( - HloInstruction::CreateParameter(2, full_shape, "to_update")), - slice, - builder.AddInstruction(HloInstruction::CreateParameter( - 3, ShapeUtil::MakeShape(U32, {3}), "update_indices")))); + HloInstruction::CreateParameter(4, full_shape, "to_update")), + slice, update_indices)); module->AddEntryComputation(builder.Build()); RunFusionAndCheckOpcodesWereFused( - module.get(), {HloOpcode::kDynamicSlice, HloOpcode::kDynamicUpdateSlice, - HloOpcode::kParameter, HloOpcode::kParameter, - HloOpcode::kParameter, HloOpcode::kParameter}); + module.get(), + {HloOpcode::kDynamicSlice, HloOpcode::kDynamicUpdateSlice, + HloOpcode::kParameter, HloOpcode::kParameter, HloOpcode::kParameter, + HloOpcode::kParameter, HloOpcode::kParameter, HloOpcode::kParameter, + HloOpcode::kParameter, HloOpcode::kParameter}); } TEST_F(OpcodeFusionTest, MessOfFusibleNodes) { -- GitLab From 8ccb11412614e7d9b702d9a56b6f876af8779f72 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Thu, 10 Jan 2019 11:24:15 -0800 Subject: [PATCH 607/622] Allows variable targets in gradient tapes. This allows using the native python `max` method on lists of variables inside of gradient tapes. PiperOrigin-RevId: 228743966 --- tensorflow/python/eager/backprop.py | 9 +++++---- tensorflow/python/eager/backprop_test.py | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/tensorflow/python/eager/backprop.py b/tensorflow/python/eager/backprop.py index 7d99f6e95a..42db726a7b 100644 --- a/tensorflow/python/eager/backprop.py +++ b/tensorflow/python/eager/backprop.py @@ -930,11 +930,12 @@ class GradientTape(object): "gradient in order to compute higher order " "derrivatives.", 1) - flat_targets = nest.flatten(target) - for t in flat_targets: + flat_targets = [] + for t in nest.flatten(target): if resource_variable_ops.is_resource_variable(t): - raise ValueError("GradientTape.gradient is not supported for variable " - "targets.") + with self: + t = ops.convert_to_tensor(t) + flat_targets.append(t) flat_sources = nest.flatten(sources) flat_sources = [_handle_or_self(x) for x in flat_sources] diff --git a/tensorflow/python/eager/backprop_test.py b/tensorflow/python/eager/backprop_test.py index 22ae6f74cb..5f4fda8897 100644 --- a/tensorflow/python/eager/backprop_test.py +++ b/tensorflow/python/eager/backprop_test.py @@ -354,6 +354,16 @@ class BackpropTest(test.TestCase): loss += v * v self.assertAllEqual(t.gradient(loss, v), 2.0) + def testPythonMax(self): + x = [resource_variable_ops.ResourceVariable(2.), + resource_variable_ops.ResourceVariable(3.), + resource_variable_ops.ResourceVariable(5.)] + with backprop.GradientTape() as t: + f = max(x) + grad = t.gradient(f, x) + self.assertAllEqual(self.evaluate(f), 5.) + self.assertAllEqual(self.evaluate(grad), [None, None, 1.0]) + def testAutomaticWatchedVariables(self): with backprop.GradientTape() as t: self.assertEqual(0, len(t.watched_variables())) @@ -674,10 +684,8 @@ class BackpropTest(test.TestCase): with backprop.GradientTape() as g: x = variables.Variable([3.0]) y = variables.Variable([2.0]) - with self.assertRaisesRegexp( - ValueError, - 'GradientTape.gradient is not supported for variable targets.'): - g.gradient(x, y) + grad = g.gradient(x, y) + self.assertAllEqual(grad, None) @test_util.run_in_graph_and_eager_modes @test_util.run_v1_only('b/120545219') -- GitLab From 32a8626373897a05195dfb3d895385a7584037cc Mon Sep 17 00:00:00 2001 From: Asim Shankar Date: Thu, 10 Jan 2019 11:26:10 -0800 Subject: [PATCH 608/622] [Java]: Correct some comments. PiperOrigin-RevId: 228744342 --- .../org/tensorflow/processor/OperatorProcessor.java | 2 +- .../java/org/tensorflow/op/annotation/Operator.java | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tensorflow/java/src/gen/java/org/tensorflow/processor/OperatorProcessor.java b/tensorflow/java/src/gen/java/org/tensorflow/processor/OperatorProcessor.java index 1b7bcdab35..df1426ad75 100644 --- a/tensorflow/java/src/gen/java/org/tensorflow/processor/OperatorProcessor.java +++ b/tensorflow/java/src/gen/java/org/tensorflow/processor/OperatorProcessor.java @@ -340,7 +340,7 @@ public final class OperatorProcessor extends AbstractProcessor { + "{@link $T @Operator} is exposed\n" + "by this API or one of its subgroup.\n

    Example usage:\n

    {@code\n"
                         + "try (Graph g = new Graph()) {\n"
    -                    + "  Ops ops = new Ops(g);\n"
    +                    + "  Ops ops = Ops.create(g);\n"
                         + "  // Operations are typed classes with convenience\n"
                         + "  // builders in Ops.\n"
                         + "  Constant three = ops.constant(3);\n"
    diff --git a/tensorflow/java/src/main/java/org/tensorflow/op/annotation/Operator.java b/tensorflow/java/src/main/java/org/tensorflow/op/annotation/Operator.java
    index 3782240edb..38f466c574 100644
    --- a/tensorflow/java/src/main/java/org/tensorflow/op/annotation/Operator.java
    +++ b/tensorflow/java/src/main/java/org/tensorflow/op/annotation/Operator.java
    @@ -25,11 +25,11 @@ import java.lang.annotation.Target;
      * Annotation used by classes to make TensorFlow operations conveniently accessible via {@code
      * org.tensorflow.op.Ops}.
      *
    - * 

    An annotation processor (TODO: not yet implemented) builds the {@code Ops} class by - * aggregating all classes annotated as {@code @Operator}s. Each annotated class must have at - * least one public static factory method named {@code create} that accepts a {@link - * org.tensorflow.op.Scope} as its first argument. The processor then adds a convenience method in - * the {@code Ops} class. For example: + *

    An annotation processor ({@code org.tensorflow.processor.OperatorProcessor}) builds the + * {@code Ops} class by aggregating all classes annotated as {@code @Operator}s. Each annotated + * class must have at least one public static factory method named {@code create} that + * accepts a {@link org.tensorflow.op.Scope} as its first argument. The processor then adds a + * convenience method in the {@code Ops} class. For example: * *

    {@code
      * @Operator
    @@ -45,7 +45,7 @@ import java.lang.annotation.Target;
      * 
    {@code
      * import org.tensorflow.op.Ops;
      * ...
    - * Ops ops = new Ops(graph);
    + * Ops ops = Ops.create(graph);
      * ...
      * ops.myOp(operand);
      * // and has exactly the same effect as calling
    -- 
    GitLab
    
    
    From 519efc7913ef2c6e5271c90727c6a1dc43817efb Mon Sep 17 00:00:00 2001
    From: Michael Kuperstein 
    Date: Thu, 10 Jan 2019 11:27:18 -0800
    Subject: [PATCH 609/622] [XLA] Move over more uses of CreateDS / CreateDUS to
     new API
    
    Move over more uses of the R1-index API to use the span-of-scalars one.
    
    PiperOrigin-RevId: 228744562
    ---
     .../xla/service/hlo_dataflow_analysis_test.cc | 47 +++++++++++--------
     .../xla/service/hlo_evaluator_test.cc         | 28 ++++++-----
     2 files changed, 45 insertions(+), 30 deletions(-)
    
    diff --git a/tensorflow/compiler/xla/service/hlo_dataflow_analysis_test.cc b/tensorflow/compiler/xla/service/hlo_dataflow_analysis_test.cc
    index 1d165ac35f..888886865b 100644
    --- a/tensorflow/compiler/xla/service/hlo_dataflow_analysis_test.cc
    +++ b/tensorflow/compiler/xla/service/hlo_dataflow_analysis_test.cc
    @@ -1970,12 +1970,13 @@ TEST_F(DoesNotUseOperandBufferTest, FusedDynamicUpdateSlice) {
     
       // Create a DynamicUpdateSlice instruction of tuple element 1.
       auto starts = builder.AddInstruction(
    -      HloInstruction::CreateConstant(LiteralUtil::CreateR1({2})));
    +      HloInstruction::CreateConstant(LiteralUtil::CreateR0(2)));
       auto update = builder.AddInstruction(HloInstruction::CreateConstant(
           LiteralUtil::CreateR1({2.f, 2.f, 2.f})));
       auto dynamic_update_slice =
           builder.AddInstruction(HloInstruction::CreateDynamicUpdateSlice(
    -          data_shape, gte1, update, starts));
    +          data_shape, gte1, update,
    +          std::initializer_list({starts})));
       builder.AddInstruction(
           HloInstruction::CreateTuple({gte0, dynamic_update_slice}));
     
    @@ -2012,12 +2013,13 @@ TEST_F(DoesNotUseOperandBufferTest, IndirectUses) {
     
       // Create a DynamicUpdateSlice instruction of tuple element 1.
       auto starts = builder.AddInstruction(
    -      HloInstruction::CreateConstant(LiteralUtil::CreateR1({2})));
    +      HloInstruction::CreateConstant(LiteralUtil::CreateR0(2)));
       auto update = builder.AddInstruction(HloInstruction::CreateConstant(
           LiteralUtil::CreateR1({2.f, 2.f, 2.f})));
       auto dynamic_update_slice =
           builder.AddInstruction(HloInstruction::CreateDynamicUpdateSlice(
    -          data_shape, gte1, update, starts));
    +          data_shape, gte1, update,
    +          std::initializer_list({starts})));
       builder.AddInstruction(
           HloInstruction::CreateTuple({gte0, dynamic_update_slice}));
     
    @@ -2150,17 +2152,17 @@ TEST_F(CanShareOperandBufferWithUserTest,
     
       auto param = builder.AddInstruction(
           HloInstruction::CreateParameter(0, data_shape, "param0"));
    -  auto index = builder.AddInstruction(
    -      HloInstruction::CreateConstant(LiteralUtil::CreateR1({0, 0})));
    -  auto ds = builder.AddInstruction(
    -      HloInstruction::CreateDynamicSlice(slice_shape, param, index, {1, 2, 2}));
    +  auto zero = builder.AddInstruction(
    +      HloInstruction::CreateConstant(LiteralUtil::CreateR0(0)));
    +  auto ds = builder.AddInstruction(HloInstruction::CreateDynamicSlice(
    +      slice_shape, param, {zero, zero}, {1, 2, 2}));
     
    -  auto dus = builder.AddInstruction(
    -      HloInstruction::CreateDynamicUpdateSlice(data_shape, param, ds, index));
    +  auto dus = builder.AddInstruction(HloInstruction::CreateDynamicUpdateSlice(
    +      data_shape, param, ds, {zero, zero}));
     
       BuildModule(builder.Build());
       auto fusion = computation_->CreateFusionInstruction(
    -      {dus, ds, index}, HloInstruction::FusionKind::kLoop);
    +      {dus, ds, zero}, HloInstruction::FusionKind::kLoop);
       RunAnalysis();
     
       EXPECT_TRUE(
    @@ -2219,12 +2221,13 @@ TEST_F(CanShareOperandBufferWithUserTest, FusedDynamicUpdateSlice) {
     
       // Create a DynamicUpdateSlice instruction of tuple element 1.
       auto starts = builder.AddInstruction(
    -      HloInstruction::CreateConstant(LiteralUtil::CreateR1({2})));
    +      HloInstruction::CreateConstant(LiteralUtil::CreateR0(2)));
       auto update = builder.AddInstruction(HloInstruction::CreateConstant(
           LiteralUtil::CreateR1({2.f, 2.f, 2.f})));
       auto dynamic_update_slice =
           builder.AddInstruction(HloInstruction::CreateDynamicUpdateSlice(
    -          data_shape, gte1, update, starts));
    +          data_shape, gte1, update,
    +          std::initializer_list({starts})));
       builder.AddInstruction(
           HloInstruction::CreateTuple({gte0, dynamic_update_slice}));
     
    @@ -2259,12 +2262,13 @@ TEST_F(CanShareOperandBufferWithUserTest,
     
       // Create a DynamicUpdateSlice instruction of tuple element 1.
       auto starts = builder.AddInstruction(
    -      HloInstruction::CreateConstant(LiteralUtil::CreateR1({2})));
    +      HloInstruction::CreateConstant(LiteralUtil::CreateR0(2)));
       auto update = builder.AddInstruction(HloInstruction::CreateConstant(
           LiteralUtil::CreateR1({2.f, 2.f, 2.f})));
       auto dynamic_update_slice =
           builder.AddInstruction(HloInstruction::CreateDynamicUpdateSlice(
    -          data_shape_bf16, convert1, update, starts));
    +          data_shape_bf16, convert1, update,
    +          std::initializer_list({starts})));
     
       auto convert2 = builder.AddInstruction(
           HloInstruction::CreateConvert(data_shape, dynamic_update_slice));
    @@ -2290,10 +2294,13 @@ TEST_F(CanShareOperandBufferWithUserTest, DynamicUpdateSliceCanShare) {
           HloInstruction::CreateParameter(0, data_shape, "data"));
       auto update = builder.AddInstruction(
           HloInstruction::CreateParameter(1, update_shape, "update"));
    -  auto starts = builder.AddInstruction(
    -      HloInstruction::CreateParameter(2, starts_shape, "starts"));
    +  auto start0 = builder.AddInstruction(
    +      HloInstruction::CreateParameter(2, starts_shape, "start0"));
    +  auto start1 = builder.AddInstruction(
    +      HloInstruction::CreateParameter(3, starts_shape, "start1"));
    +
       auto dus = builder.AddInstruction(HloInstruction::CreateDynamicUpdateSlice(
    -      data_shape, data, update, starts));
    +      data_shape, data, update, {start0, start1}));
     
       BuildModuleAndRunAnalysis(builder.Build());
     
    @@ -2304,7 +2311,9 @@ TEST_F(CanShareOperandBufferWithUserTest, DynamicUpdateSliceCanShare) {
       EXPECT_FALSE(
           dataflow_analysis_->CanShareOperandBufferWithUser(update, {}, dus, {}));
       EXPECT_FALSE(
    -      dataflow_analysis_->CanShareOperandBufferWithUser(starts, {}, dus, {}));
    +      dataflow_analysis_->CanShareOperandBufferWithUser(start0, {}, dus, {}));
    +  EXPECT_FALSE(
    +      dataflow_analysis_->CanShareOperandBufferWithUser(start1, {}, dus, {}));
     }
     
     TEST_F(CanShareOperandBufferWithUserTest, ScatterCanShare) {
    diff --git a/tensorflow/compiler/xla/service/hlo_evaluator_test.cc b/tensorflow/compiler/xla/service/hlo_evaluator_test.cc
    index 319f0db97a..674df0016a 100644
    --- a/tensorflow/compiler/xla/service/hlo_evaluator_test.cc
    +++ b/tensorflow/compiler/xla/service/hlo_evaluator_test.cc
    @@ -1739,12 +1739,14 @@ TEST_P(HloEvaluatorBf16Test, DynamicSlice) {
       HloInstruction* operand = b.AddInstruction(
           HloInstruction::CreateConstant(std::move(operand_literal)));
     
    -  auto start_indices = b.AddInstruction(
    -      HloInstruction::CreateConstant(LiteralUtil::CreateR1({0, 1})));
    +  auto zero = b.AddInstruction(
    +      HloInstruction::CreateConstant(LiteralUtil::CreateR0(0)));
    +  auto one = b.AddInstruction(
    +      HloInstruction::CreateConstant(LiteralUtil::CreateR0(1)));
     
       Shape shape = ShapeUtil::MakeShape(F32, {2, 3});
    -  b.AddInstruction(HloInstruction::CreateDynamicSlice(shape, operand,
    -                                                      start_indices, {2, 3}));
    +  b.AddInstruction(
    +      HloInstruction::CreateDynamicSlice(shape, operand, {zero, one}, {2, 3}));
       m_->AddEntryComputation(b.Build());
     
       Literal result = Evaluate();
    @@ -1775,12 +1777,14 @@ TEST_P(HloEvaluatorBf16Test, DynamicSliceModSlice) {
       HloInstruction* operand = b.AddInstruction(
           HloInstruction::CreateConstant(std::move(operand_literal)));
     
    -  auto start_indices = b.AddInstruction(
    -      HloInstruction::CreateConstant(LiteralUtil::CreateR1({2, 1})));
    +  auto two = b.AddInstruction(
    +      HloInstruction::CreateConstant(LiteralUtil::CreateR0(2)));
    +  auto one = b.AddInstruction(
    +      HloInstruction::CreateConstant(LiteralUtil::CreateR0(1)));
     
       Shape shape = ShapeUtil::MakeShape(F32, {2, 3});
    -  b.AddInstruction(HloInstruction::CreateDynamicSlice(shape, operand,
    -                                                      start_indices, {2, 3}));
    +  b.AddInstruction(
    +      HloInstruction::CreateDynamicSlice(shape, operand, {two, one}, {2, 3}));
       m_->AddEntryComputation(b.Build());
     
       Literal result = Evaluate();
    @@ -1809,15 +1813,17 @@ TEST_P(HloEvaluatorBf16Test, DynamicSliceUpdate) {
       HloInstruction* operand = b.AddInstruction(
           HloInstruction::CreateConstant(std::move(operand_literal)));
     
    -  auto start_indices = b.AddInstruction(
    -      HloInstruction::CreateConstant(LiteralUtil::CreateR1({0, 1})));
    +  auto zero = b.AddInstruction(
    +      HloInstruction::CreateConstant(LiteralUtil::CreateR0(0)));
    +  auto one = b.AddInstruction(
    +      HloInstruction::CreateConstant(LiteralUtil::CreateR0(1)));
     
       auto update = b.AddInstruction(HloInstruction::CreateConstant(
           LiteralUtil::CreateR2({{-2.0, -3.0}, {-6.0, -7.0}})));
     
       Shape shape = ShapeUtil::MakeShape(F64, {2, 3});
       b.AddInstruction(HloInstruction::CreateDynamicUpdateSlice(
    -      shape, operand, update, start_indices));
    +      shape, operand, update, {zero, one}));
       m_->AddEntryComputation(b.Build());
     
       Literal result = Evaluate();
    -- 
    GitLab
    
    
    From 4644d4e5f26cf72905d1ce40194a9733c4f8252f Mon Sep 17 00:00:00 2001
    From: "A. Unique TensorFlower" 
    Date: Thu, 10 Jan 2019 11:29:29 -0800
    Subject: [PATCH 610/622] [convergence_tools]: Tracing the subset of the ops
     that are in the execution path.
    
    PiperOrigin-RevId: 228744967
    ---
     .../contrib/tpu/python/tpu/tensor_tracer.py   | 53 +++++++++++++++++--
     .../contrib/tpu/python/tpu/tpu_estimator.py   |  3 +-
     2 files changed, 52 insertions(+), 4 deletions(-)
    
    diff --git a/tensorflow/contrib/tpu/python/tpu/tensor_tracer.py b/tensorflow/contrib/tpu/python/tpu/tensor_tracer.py
    index c7e29860e5..bf492e78a1 100644
    --- a/tensorflow/contrib/tpu/python/tpu/tensor_tracer.py
    +++ b/tensorflow/contrib/tpu/python/tpu/tensor_tracer.py
    @@ -59,6 +59,7 @@ _REASON_SCALAR_GET_TRACED = 'traced-scalar'
     _REASON_TENSOR_GET_TRACED = 'traced-tensor'
     _REASON_USER_INCLUDED = 'traced-user-included'
     _REASON_USER_EXCLUDED = 'not-traced-user-excluded'
    +_REASON_NOT_EXECUTED = 'not-traced-not-in-exec-path'
     _REASON_NON_NUMERIC_TENSOR = 'not-traced-non-numeric-tensor'
     _MARKER_SECTION_BEGIN = '!!!!!!! section-begin:'
     _MARKER_SECTION_END = '!!!!!!! section-end:'
    @@ -864,7 +865,8 @@ class TensorTracer(object):
         raise RuntimeError('Tensor trace fun for %s is not yet implemented'
                            %self._trace_mode)
     
    -  def _skip_op(self, op_id, op, user_included, user_excluded):
    +  def _skip_op(self, op_id, op, user_included, user_excluded,
    +               in_exec_path=True):
         """Returns True if we should not trace Op."""
     
         if user_included:
    @@ -875,6 +877,10 @@ class TensorTracer(object):
           self._instrument_records[op.name] = TensorTracer.reason(
               op_id, _REASON_USER_EXCLUDED)
           return True
    +    if not in_exec_path:
    +      self._instrument_records[op.name] = TensorTracer.reason(
    +          op_id, _REASON_NOT_EXECUTED)
    +      return True
         if not self._inside_op_range(op_id):
           self._instrument_records[op.name] = TensorTracer.reason(
               op_id, _REASON_OUTSIDE_OP_RANGE)
    @@ -946,6 +952,40 @@ class TensorTracer(object):
               op_id, _REASON_TENSOR_GET_TRACED)
           return False
     
    +  def _filter_execution_path_operations(self, operations, fetches):
    +    """Returns the set of ops in the execution path to compute given fetches."""
    +    # If no fetch provided, then return all operations.
    +    if fetches is None:
    +      return set(operations)
    +    # Convert to list, if a single element is provided.
    +    if not isinstance(fetches, (list, tuple)):
    +      fetches = [fetches]
    +    # If a tensor is given as fetch, convert it to op.
    +    op_fetches = []
    +    for fetch in fetches:
    +      if isinstance(fetch, ops.Operation):
    +        op_fetches.append(fetch)
    +      elif isinstance(fetch, ops.Tensor):
    +        op_fetches.append(fetch.op)
    +      else:
    +        raise RuntimeError('Given fetch:%s is neither a tensor nor an op.'
    +                           %fetch)
    +
    +    execution_path_operations = set(op_fetches)
    +    traverse_stack = list(op_fetches)
    +    while True:
    +      if not traverse_stack:
    +        break
    +      head_op = traverse_stack.pop()
    +      input_ops = [tensor_input.op for tensor_input in head_op.inputs]
    +      input_ops.extend(head_op.control_inputs)
    +
    +      for input_op in input_ops:
    +        if input_op not in execution_path_operations:
    +          execution_path_operations.add(input_op)
    +          traverse_stack.append(input_op)
    +    return execution_path_operations
    +
       def _pre_tracing(self, graph):
         """Work needs to be done prior to TPU or CPU tracing."""
     
    @@ -987,13 +1027,15 @@ class TensorTracer(object):
                                       _TENSOR_TRACER_CHECKPOINT))
         return checkpoint_operations
     
    -  def trace_tpu(self, graph, result_tensor, num_replicas=None):
    +  def trace_tpu(self, graph, result_tensor, num_replicas=None, fetches=None):
         """Traces the tensors generated by TPU Ops in a TF graph.
     
         Args:
           graph: the graph of Ops executed on the TPU.
           result_tensor: a result tensor of evaluating the graph.
           num_replicas: number of replicas used on the TPU.
    +      fetches: the list of fetches given to session.run, used to determine the
    +      ops in execution path. If None, the whole graph will be traced.
     
         Returns:
           A tuple (result_tensor_copy, tracing_ops), where:
    @@ -1022,6 +1064,10 @@ class TensorTracer(object):
         result_tensor_copy = self._add_replica_id_to_graph(num_replicas,
                                                            result_tensor)
         (operations, succeed, sorted_or_cycle) = self._pre_tracing(graph)
    +    # Filter out the operations that won't be executed.
    +    # if fetches=None, then ops_in_exec_path = set(operations)
    +    ops_in_exec_path = self._filter_execution_path_operations(operations,
    +                                                              fetches)
         tracing_ops = []
         checkpoint_operations = self._get_checkpoints(graph)
     
    @@ -1030,7 +1076,8 @@ class TensorTracer(object):
             continue
           user_included = self._is_user_included_op(op)
           user_excluded = self._is_user_excluded_op(op)
    -      if self._skip_op(op_id, op, user_included, user_excluded):
    +      in_exec_path = op in ops_in_exec_path
    +      if self._skip_op(op_id, op, user_included, user_excluded, in_exec_path):
             continue
           for i in range(len(op.outputs)):
             out_tensor = op.outputs[i]
    diff --git a/tensorflow/contrib/tpu/python/tpu/tpu_estimator.py b/tensorflow/contrib/tpu/python/tpu/tpu_estimator.py
    index 2df5b3e4eb..075ecbc52a 100644
    --- a/tensorflow/contrib/tpu/python/tpu/tpu_estimator.py
    +++ b/tensorflow/contrib/tpu/python/tpu/tpu_estimator.py
    @@ -1412,7 +1412,8 @@ class _ModelFnWrapper(object):
           if tensor_tracer.TensorTracer.is_enabled():
             tt = tensor_tracer.TensorTracer()
             loss, tracing_ops = tt.trace_tpu(ops.get_default_graph(), loss,
    -                                         self._ctx.num_replicas)
    +                                         self._ctx.num_replicas,
    +                                         fetches=[loss, train_op])
     
           # We must run train_op to update the variables prior to running the
           # outfeed.
    -- 
    GitLab
    
    
    From 53bd138262df6e141c0e196a262f4ae760c2cf5f Mon Sep 17 00:00:00 2001
    From: Asim Shankar 
    Date: Thu, 10 Jan 2019 11:34:00 -0800
    Subject: [PATCH 611/622] [Java]: Add place to put unittests for generated Op
     wrappers.
    
    PiperOrigin-RevId: 228745935
    ---
     tensorflow/java/BUILD                         | 13 ++++++
     .../op/core/GeneratedOperationsTest.java      | 43 +++++++++++++++++++
     2 files changed, 56 insertions(+)
     create mode 100644 tensorflow/java/src/test/java/org/tensorflow/op/core/GeneratedOperationsTest.java
    
    diff --git a/tensorflow/java/BUILD b/tensorflow/java/BUILD
    index 10808e162e..af5503f2ad 100644
    --- a/tensorflow/java/BUILD
    +++ b/tensorflow/java/BUILD
    @@ -295,6 +295,19 @@ tf_java_test(
         ],
     )
     
    +tf_java_test(
    +    name = "GeneratedOperationsTest",
    +    size = "small",
    +    srcs = ["src/test/java/org/tensorflow/op/core/GeneratedOperationsTest.java"],
    +    javacopts = JAVACOPTS,
    +    test_class = "org.tensorflow.op.core.GeneratedOperationsTest",
    +    deps = [
    +        ":tensorflow",
    +        ":testutil",
    +        "@junit",
    +    ],
    +)
    +
     tf_java_test(
         name = "GradientsTest",
         size = "small",
    diff --git a/tensorflow/java/src/test/java/org/tensorflow/op/core/GeneratedOperationsTest.java b/tensorflow/java/src/test/java/org/tensorflow/op/core/GeneratedOperationsTest.java
    new file mode 100644
    index 0000000000..42d126c3c4
    --- /dev/null
    +++ b/tensorflow/java/src/test/java/org/tensorflow/op/core/GeneratedOperationsTest.java
    @@ -0,0 +1,43 @@
    +/* 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.
    +==============================================================================*/
    +
    +package org.tensorflow.op.core;
    +
    +import static org.junit.Assert.assertEquals;
    +
    +import org.junit.Test;
    +import org.junit.runner.RunWith;
    +import org.junit.runners.JUnit4;
    +import org.tensorflow.Graph;
    +import org.tensorflow.Operand;
    +import org.tensorflow.Session;
    +import org.tensorflow.Tensor;
    +import org.tensorflow.op.Ops;
    +
    +@RunWith(JUnit4.class)
    +public final class GeneratedOperationsTest {
    +
    +  @Test
    +  public void tensorInputTensorOutput() {
    +    try (Graph g = new Graph();
    +        Session sess = new Session(g)) {
    +      Ops ops = Ops.create(g);
    +      Operand x = ops.math().add(ops.constant(1), ops.constant(2));
    +      try (Tensor result = sess.runner().fetch(x).run().get(0).expect(Integer.class)) {
    +        assertEquals(3, result.intValue());
    +      }
    +    }
    +  }
    +}
    -- 
    GitLab
    
    
    From 1095ed8fe6808a70edb66d7970e9462db791e690 Mon Sep 17 00:00:00 2001
    From: James Ring 
    Date: Thu, 10 Jan 2019 11:48:55 -0800
    Subject: [PATCH 612/622] Add TF_StepId and TF_ExpectedOutputDataType
    
    PiperOrigin-RevId: 228748943
    ---
     tensorflow/c/kernels.cc      | 9 +++++++++
     tensorflow/c/kernels.h       | 8 ++++++++
     tensorflow/c/kernels_test.cc | 6 ++++++
     3 files changed, 23 insertions(+)
    
    diff --git a/tensorflow/c/kernels.cc b/tensorflow/c/kernels.cc
    index 2a4eaecb6c..673aed558a 100644
    --- a/tensorflow/c/kernels.cc
    +++ b/tensorflow/c/kernels.cc
    @@ -158,3 +158,12 @@ void TF_SetOutput(TF_OpKernelContext* ctx, int i, const TF_Tensor* tensor,
         cc_ctx->set_output(i, cc_tensor);
       }
     }
    +
    +TF_DataType TF_ExpectedOutputDataType(TF_OpKernelContext* ctx, int i) {
    +  auto* cc_ctx = reinterpret_cast<::tensorflow::OpKernelContext*>(ctx);
    +  return static_cast(cc_ctx->expected_output_dtype(i));
    +}
    +
    +int64_t TF_StepId(TF_OpKernelContext* ctx) {
    +  return reinterpret_cast<::tensorflow::OpKernelContext*>(ctx)->step_id();
    +}
    diff --git a/tensorflow/c/kernels.h b/tensorflow/c/kernels.h
    index cefc30bcdf..721a4aca0b 100644
    --- a/tensorflow/c/kernels.h
    +++ b/tensorflow/c/kernels.h
    @@ -111,6 +111,14 @@ TF_CAPI_EXPORT extern void TF_SetOutput(TF_OpKernelContext* ctx, int i,
                                             const TF_Tensor* tensor,
                                             TF_Status* status);
     
    +// Returns the expected output data type of the ith output. If i < 0 or
    +// i >= TF_NumOutputs(ctx), the program aborts.
    +TF_CAPI_EXPORT extern TF_DataType TF_ExpectedOutputDataType(
    +    TF_OpKernelContext* ctx, int i);
    +
    +// Returns the step ID of the given context.
    +TF_CAPI_EXPORT extern int64_t TF_StepId(TF_OpKernelContext* ctx);
    +
     #ifdef __cplusplus
     } /* end extern "C" */
     #endif
    diff --git a/tensorflow/c/kernels_test.cc b/tensorflow/c/kernels_test.cc
    index e659ee3c3d..fdeb04c84c 100644
    --- a/tensorflow/c/kernels_test.cc
    +++ b/tensorflow/c/kernels_test.cc
    @@ -41,6 +41,9 @@ static void* MyCreateFunc(TF_OpKernelConstruction* ctx) {
     static void MyComputeFunc(void* kernel, TF_OpKernelContext* ctx) {
       struct MyCustomKernel* s = static_cast(kernel);
       s->compute_called = true;
    +  if (ctx != nullptr) {
    +    EXPECT_EQ(43, TF_StepId(ctx));
    +  }
     }
     
     static void MyDeleteFunc(void* kernel) {
    @@ -155,6 +158,8 @@ TEST(TestKernel, TestInputAndOutputCount) {
         TF_SetOutput(ctx, 24, input, s);
         EXPECT_EQ(TF_OUT_OF_RANGE, TF_GetCode(s));
     
    +    EXPECT_EQ(TF_UINT8, TF_ExpectedOutputDataType(ctx, 0));
    +
         TF_DeleteStatus(s);
         if (input != nullptr) {
           TF_DeleteTensor(input);
    @@ -175,6 +180,7 @@ TEST(TestKernel, TestInputAndOutputCount) {
         OpKernelContext::Params p;
         DummyDevice dummy_device(nullptr, false);
         p.device = &dummy_device;
    +    p.step_id = 43;
     
         Tensor t(tensorflow::uint8(123));
     
    -- 
    GitLab
    
    
    From d5977dfe93b95f6902cb9df28525a1dc50e4704d Mon Sep 17 00:00:00 2001
    From: Zhenyu Tan 
    Date: Thu, 10 Jan 2019 11:53:59 -0800
    Subject: [PATCH 613/622] Internal Cleanup.
    
    PiperOrigin-RevId: 228749766
    ---
     tensorflow/python/keras/BUILD | 3 ++-
     1 file changed, 2 insertions(+), 1 deletion(-)
    
    diff --git a/tensorflow/python/keras/BUILD b/tensorflow/python/keras/BUILD
    index 9f8da126bf..fb0161a677 100755
    --- a/tensorflow/python/keras/BUILD
    +++ b/tensorflow/python/keras/BUILD
    @@ -482,13 +482,14 @@ cuda_py_test(
     
     tf_py_test(
         name = "pooling_test",
    -    size = "large",
    +    size = "medium",
         srcs = ["layers/pooling_test.py"],
         additional_deps = [
             ":keras",
             "@absl_py//absl/testing:parameterized",
             "//tensorflow/python:client_testlib",
         ],
    +    shard_count = 8,
     )
     
     tf_py_test(
    -- 
    GitLab
    
    
    From 84572bff5cf02b1722ab763982a92be44e7719ef Mon Sep 17 00:00:00 2001
    From: Yuefeng Zhou 
    Date: Thu, 10 Jan 2019 12:18:36 -0800
    Subject: [PATCH 614/622] Fix collective_all_reduce_strategy test failure.
    
    PiperOrigin-RevId: 228754463
    ---
     .../distribute/python/collective_all_reduce_strategy_test.py   | 3 ++-
     1 file changed, 2 insertions(+), 1 deletion(-)
    
    diff --git a/tensorflow/contrib/distribute/python/collective_all_reduce_strategy_test.py b/tensorflow/contrib/distribute/python/collective_all_reduce_strategy_test.py
    index 682da44f5d..62ff4b178e 100644
    --- a/tensorflow/contrib/distribute/python/collective_all_reduce_strategy_test.py
    +++ b/tensorflow/contrib/distribute/python/collective_all_reduce_strategy_test.py
    @@ -426,7 +426,8 @@ class LocalCollectiveAllReduceStrategy(
     
       @combinations.generate(
           combinations.combine(mode=['graph', 'eager'], required_gpus=2))
    -  def testMakeInputFnIterator(self, num_gpus=2):
    +  def testMakeInputFnIterator(self):
    +    num_gpus = 2
         dataset_fn = lambda: dataset_ops.Dataset.range(5 * num_gpus)
         expected_values = [range(i, i + num_gpus) for i in range(0, 10, num_gpus)]
     
    -- 
    GitLab
    
    
    From 02630688f2fa239ce1d1551caa8a2f3559c248e3 Mon Sep 17 00:00:00 2001
    From: "A. Unique TensorFlower" 
    Date: Thu, 10 Jan 2019 12:25:01 -0800
    Subject: [PATCH 615/622] Automated rollback of commit
     73b6d9262ea6d1578374ecb63560ad0fcad13e35
    
    PiperOrigin-RevId: 228755594
    ---
     tensorflow/contrib/distribute/python/BUILD    |  30 +-
     ...test_base.py => keras_correctness_test.py} | 261 ++++++++++++++----
     .../python/keras_dnn_correctness_test.py      | 163 -----------
     .../keras_image_model_correctness_test.py     |  92 ------
     4 files changed, 209 insertions(+), 337 deletions(-)
     rename tensorflow/contrib/distribute/python/{keras_correctness_test_base.py => keras_correctness_test.py} (58%)
     delete mode 100644 tensorflow/contrib/distribute/python/keras_dnn_correctness_test.py
     delete mode 100644 tensorflow/contrib/distribute/python/keras_image_model_correctness_test.py
    
    diff --git a/tensorflow/contrib/distribute/python/BUILD b/tensorflow/contrib/distribute/python/BUILD
    index 4c25f49f16..d4758d7518 100644
    --- a/tensorflow/contrib/distribute/python/BUILD
    +++ b/tensorflow/contrib/distribute/python/BUILD
    @@ -657,11 +657,7 @@ cuda_py_test(
     py_library(
         name = "keras_correctness_test_lib",
         testonly = 1,
    -    srcs = [
    -        "keras_correctness_test_base.py",
    -        "keras_dnn_correctness_test.py",
    -        "keras_image_model_correctness_test.py",
    -    ],
    +    srcs = ["keras_correctness_test.py"],
         deps = [
             ":combinations",
             "//tensorflow/contrib/distribute/python:mirrored_strategy",
    @@ -677,9 +673,8 @@ py_library(
     )
     
     cuda_py_test(
    -    name = "keras_dnn_correctness_test",
    -    size = "medium",
    -    srcs = ["keras_dnn_correctness_test.py"],
    +    name = "keras_correctness_test",
    +    srcs = ["keras_correctness_test.py"],
         additional_deps = [
             ":keras_correctness_test_lib",
         ],
    @@ -695,25 +690,6 @@ cuda_py_test(
         ],
     )
     
    -cuda_py_test(
    -    name = "keras_image_model_correctness_test",
    -    size = "medium",
    -    srcs = ["keras_image_model_correctness_test.py"],
    -    additional_deps = [
    -        ":keras_correctness_test_lib",
    -    ],
    -    # Shard count is set to an odd number to distribute tasks across
    -    # shards more evenly.
    -    shard_count = 31,
    -    tags = [
    -        "multi_and_single_gpu",
    -        "no_oss",  # TODO(b/117919883): Fix python error.
    -        "no_pip",
    -        "no_windows_gpu",
    -        "notsan",
    -    ],
    -)
    -
     py_library(
         name = "metrics_v1_test_lib",
         testonly = 1,
    diff --git a/tensorflow/contrib/distribute/python/keras_correctness_test_base.py b/tensorflow/contrib/distribute/python/keras_correctness_test.py
    similarity index 58%
    rename from tensorflow/contrib/distribute/python/keras_correctness_test_base.py
    rename to tensorflow/contrib/distribute/python/keras_correctness_test.py
    index e13984b26d..3abdee2c0e 100644
    --- a/tensorflow/contrib/distribute/python/keras_correctness_test_base.py
    +++ b/tensorflow/contrib/distribute/python/keras_correctness_test.py
    @@ -30,6 +30,8 @@ from tensorflow.python.eager import context
     from tensorflow.python.eager import test
     from tensorflow.python.framework import random_seed
     from tensorflow.python.keras.engine import distributed_training_utils
    +from tensorflow.python.keras.optimizer_v2 import gradient_descent as gradient_descent_keras
    +from tensorflow.python.training import gradient_descent
     
     _RANDOM_SEED = 1337
     _EVAL_STEPS = 20
    @@ -60,13 +62,24 @@ def all_strategy_combinations_with_graph_mode():
       return combinations.combine(distribution=all_strategies, mode=['graph'])
     
     
    -def all_strategy_and_input_config_combinations():
    +def strategy_and_input_combinations():
    +  def cnn_model_with_batch_norm(**kwargs):
    +    return _create_cnn_model(with_batch_norm=True, **kwargs)
    +
       return (
           combinations.times(
               combinations.combine(distribution=all_strategies),
               combinations.combine(mode=['graph', 'eager'],
                                    use_numpy=[True, False],
    -                               use_validation_data=[True, False])))
    +                               use_validation_data=[True, False]),
    +          combinations.combine(model_with_data=[
    +              ModelWithData('dnn', _create_dnn_model, _dnn_training_data),
    +              ModelWithData('cnn', _create_cnn_model, _cnn_training_data),
    +              ModelWithData('cnn_batch_norm',
    +                            cnn_model_with_batch_norm,
    +                            _cnn_training_data,
    +                            with_batch_norm=True),
    +          ])))
     
     
     class MaybeDistributionScope(object):
    @@ -87,6 +100,97 @@ class MaybeDistributionScope(object):
           self._scope = None
     
     
    +class ModelWithData(object):
    +  """An object giving a good name in combinations.
    +
    +  The model_fn must take two arguments: initial_weights and distribution.
    +  """
    +
    +  def __init__(self, name, model_fn, data_fn, with_batch_norm=False):
    +    self.name = name
    +    self.model_fn = model_fn
    +    self.data_fn = data_fn
    +    self.with_batch_norm = with_batch_norm
    +
    +  def __repr__(self):
    +    return self.name
    +
    +
    +def _dnn_training_data():
    +  # TODO(xiejw): Change this back to 10000, once we support final partial
    +  # batch.
    +  num_samples = 9984
    +  x_train = np.random.rand(num_samples, 1)
    +  y_train = 3 * x_train
    +  x_train = x_train.astype('float32')
    +  y_train = y_train.astype('float32')
    +  x_predict = [[1.], [2.], [3.], [4.]]
    +  return x_train, y_train, x_predict
    +
    +
    +def _create_dnn_model(initial_weights=None, distribution=None):
    +  with MaybeDistributionScope(distribution):
    +    # We add few non-linear layers to make it non-trivial.
    +    model = keras.Sequential()
    +    model.add(keras.layers.Dense(10, activation='relu', input_shape=(1,)))
    +    model.add(keras.layers.Dense(10, activation='relu'))
    +    model.add(keras.layers.Dense(10, activation='relu'))
    +    model.add(keras.layers.Dense(1))
    +
    +    if initial_weights:
    +      model.set_weights(initial_weights)
    +
    +    model.compile(
    +        loss=keras.losses.mean_squared_error,
    +        optimizer=gradient_descent_keras.SGD(0.5),
    +        metrics=['mse'])
    +    return model
    +
    +
    +def _cnn_training_data(count=_GLOBAL_BATCH_SIZE * _EVAL_STEPS,
    +                       shape=(28, 28, 3), num_classes=10):
    +  centers = np.random.randn(num_classes, *shape)
    +
    +  features = []
    +  labels = []
    +  for _ in range(count):
    +    label = np.random.randint(0, num_classes, size=1)[0]
    +    offset = np.random.normal(loc=0, scale=0.1, size=np.prod(shape))
    +    offset = offset.reshape(shape)
    +    labels.append(label)
    +    features.append(centers[label] + offset)
    +
    +  x_train = np.asarray(features, dtype=np.float32)
    +  y_train = np.asarray(labels, dtype=np.float32).reshape((count, 1))
    +  x_predict = x_train
    +  return x_train, y_train, x_predict
    +
    +
    +def _create_cnn_model(initial_weights=None, distribution=None,
    +                      with_batch_norm=False):
    +  with MaybeDistributionScope(distribution):
    +    image = keras.layers.Input(shape=(28, 28, 3), name='image')
    +    c1 = keras.layers.Conv2D(
    +        name='conv1', filters=16, kernel_size=(3, 3), strides=(4, 4))(
    +            image)
    +    if with_batch_norm:
    +      c1 = keras.layers.BatchNormalization(name='bn1')(c1)
    +    c1 = keras.layers.MaxPooling2D(pool_size=(2, 2))(c1)
    +    logits = keras.layers.Dense(
    +        10, activation='softmax', name='pred')(
    +            keras.layers.Flatten()(c1))
    +    model = keras.Model(inputs=[image], outputs=[logits])
    +
    +    if initial_weights:
    +      model.set_weights(initial_weights)
    +
    +    model.compile(
    +        optimizer=gradient_descent.GradientDescentOptimizer(learning_rate=0.1),
    +        loss='sparse_categorical_crossentropy',
    +        metrics=['sparse_categorical_accuracy'])
    +  return model
    +
    +
     def batch_wrapper(dataset, batch_size, distribution, repeat=None):
       if repeat:
         dataset = dataset.repeat(repeat)
    @@ -246,11 +350,6 @@ def compare_results(results_with_ds, results_without_ds, distribution,
             msg='Fail to assert {}.'.format(key))
     
     
    -def should_skip_tpu_with_eager(distribution):
    -  return (context.executing_eagerly() and
    -          isinstance(distribution, tpu_strategy.TPUStrategy))
    -
    -
     class LearningRateBatchScheduler(keras.callbacks.Callback):
     
       def __init__(self, update_freq=None):
    @@ -265,60 +364,107 @@ class LearningRateBatchScheduler(keras.callbacks.Callback):
         keras.backend.set_value(self.model.optimizer.lr, lr)
     
     
    -class TestDistributionStrategyCorrectnessBase(test.TestCase,
    -                                              parameterized.TestCase):
    -  """Model agnostic testing infra to test correctness of Keras models."""
    -
    -  def set_up_test_config(self, use_numpy=False,
    -                         use_validation_data=False,
    -                         with_batch_norm=False):
    -    self.use_numpy = use_numpy
    -    self.use_validation_data = use_validation_data
    -    self.with_batch_norm = with_batch_norm
    +class TestDistributionStrategyCorrectness(test.TestCase,
    +                                          parameterized.TestCase):
     
    -    keras.backend.set_image_data_format('channels_last')
    -    np.random.seed(_RANDOM_SEED)
    -    random_seed.set_random_seed(_RANDOM_SEED)
    +  def _should_skip_tpu_with_eager(self, distribution):
    +    return (context.executing_eagerly() and
    +            isinstance(distribution, tpu_strategy.TPUStrategy))
     
    -  def get_data(self):
    -    num_samples = 10000
    -    x_train = np.random.randint(0, 2, num_samples)
    -    x_train = np.reshape(x_train, (num_samples, 1))
    -    y_train = x_train
    -    return (x_train.astype('float32'), y_train.astype('float32'), None)
    +  @combinations.generate(all_strategy_combinations_with_eager_and_graph_modes())
    +  def test_metric_correctness(self, distribution):
    +    if self._should_skip_tpu_with_eager(distribution):
    +      self.skipTest('TPUStrategy does not support eager mode now.')
     
    -  def get_model(self, distribution=None):
    -    raise NotImplementedError
    +    with self.cached_session():
    +      keras.backend.set_image_data_format('channels_last')
    +      num_samples = 10000
    +
    +      x_train = np.random.randint(0, 2, num_samples)
    +      x_train = np.reshape(x_train, (num_samples, 1))
    +      y_train = x_train
    +      x_train = x_train.astype('float32')
    +      y_train = y_train.astype('float32')
    +
    +      # Create identity model.
    +      with distribution.scope():
    +        model = keras.Sequential()
    +        model.add(
    +            keras.layers.Dense(1, input_shape=(1,), kernel_initializer='ones'))
    +        model.compile(
    +            loss=keras.losses.mean_squared_error,
    +            optimizer=gradient_descent.GradientDescentOptimizer(0.5),
    +            metrics=[keras.metrics.BinaryAccuracy()])
    +
    +      batch_size = 64
    +      batch_size = get_batch_size(batch_size, distribution)
    +      train_dataset = dataset_ops.Dataset.from_tensor_slices((x_train, y_train))
    +      train_dataset = batch_wrapper(train_dataset, batch_size, distribution)
    +
    +      history = model.fit(x=train_dataset, epochs=2, steps_per_epoch=10)
    +      self.assertEqual(history.history['binary_accuracy'], [1.0, 1.0])
    +
    +  @combinations.generate(all_strategy_combinations_with_eager_and_graph_modes())
    +  def test_eval_metrics_correctness(self, distribution):
    +    if self._should_skip_tpu_with_eager(distribution):
    +      self.skipTest('TPUStrategy does not support eager mode now.')
     
    -  def skip_unsupported_test_configuration(self, distribution):
    -    if should_skip_tpu_with_eager(distribution):
    +    with self.cached_session():
    +      with distribution.scope():
    +        model = keras.Sequential()
    +        model.add(
    +            keras.layers.Dense(
    +                3, activation='relu', input_dim=4, kernel_initializer='ones'))
    +        model.add(
    +            keras.layers.Dense(
    +                1, activation='sigmoid', kernel_initializer='ones'))
    +        model.compile(
    +            loss='mae',
    +            metrics=['accuracy', keras.metrics.BinaryAccuracy()],
    +            optimizer=gradient_descent.GradientDescentOptimizer(0.001))
    +
    +      # verify correctness of stateful and stateless metrics.
    +      x = np.ones((100, 4)).astype('float32')
    +      y = np.ones((100, 1)).astype('float32')
    +      dataset = dataset_ops.Dataset.from_tensor_slices((x, y)).repeat()
    +      dataset = batch_wrapper(dataset, 4, distribution)
    +      outs = model.evaluate(dataset, steps=10)
    +      self.assertEqual(outs[1], 1.)
    +      self.assertEqual(outs[2], 1.)
    +
    +      y = np.zeros((100, 1)).astype('float32')
    +      dataset = dataset_ops.Dataset.from_tensor_slices((x, y)).repeat()
    +      dataset = batch_wrapper(dataset, 4, distribution)
    +      outs = model.evaluate(dataset, steps=10)
    +      self.assertEqual(outs[1], 0.)
    +      self.assertEqual(outs[2], 0.)
    +
    +  @combinations.generate(strategy_and_input_combinations())
    +  def test_correctness(self, distribution, use_numpy, use_validation_data,
    +                       model_with_data):
    +    if self._should_skip_tpu_with_eager(distribution):
           self.skipTest('TPUStrategy does not support eager mode now.')
     
    -    if context.executing_eagerly() and self.use_numpy:
    +    if context.executing_eagerly() and use_numpy:
           self.skipTest('Numpy as inputs is not supported with strategy in eager.')
     
    -    if context.executing_eagerly() and self.use_validation_data:
    -      self.skipTest('TODO(hongjunchoi): Add test logic for using validation '
    -                    'data for eager execution.')
    -    return
    +    if context.executing_eagerly() and use_validation_data:
    +      self.skipTest('TODO')
     
    -  def run_correctness_test(self,
    -                           distribution,
    -                           use_numpy,
    -                           use_validation_data,
    -                           with_batch_norm=False):
         with self.cached_session():
    -      self.set_up_test_config(use_numpy, use_validation_data, with_batch_norm)
    -      self.skip_unsupported_test_configuration(distribution)
    +      keras.backend.set_image_data_format('channels_last')
    +      np.random.seed(_RANDOM_SEED)
    +      random_seed.set_random_seed(_RANDOM_SEED)
     
    +      model_fn, data_fn = model_with_data.model_fn, model_with_data.data_fn
           # Train, eval, and predict datasets are created with the same input numpy
           # arrays.
    -      x_train, y_train, x_predict = self.get_data()
    +      x_train, y_train, x_predict = data_fn()
     
           # The model is built once and the initial weights are saved.
           # This is used to initialize the model for both the distribution and
           # non-distribution run.
    -      model = self.get_model()
    +      model = model_fn()
           initial_weights = model.get_weights()
     
           def input_fn(dist):
    @@ -326,15 +472,15 @@ class TestDistributionStrategyCorrectnessBase(test.TestCase,
                 use_numpy, use_validation_data, dist, x_train, y_train, x_predict)
     
           results_with_ds = fit_eval_and_predict(
    -          initial_weights, input_fn=input_fn, model_fn=self.get_model,
    +          initial_weights, input_fn=input_fn, model_fn=model_fn,
               distribution=distribution)
           results_without_ds = fit_eval_and_predict(
    -          initial_weights, input_fn=input_fn, model_fn=self.get_model,
    +          initial_weights, input_fn=input_fn, model_fn=model_fn,
               distribution=None)
     
           # First, special case, for multi-replica distributed training, batch norm
           # is not aggregated globally. So it is expected to have different weights.
    -      if (self.with_batch_norm and
    +      if (model_with_data.with_batch_norm and
               distribution.num_replicas_in_sync > 1):
             with self.assertRaises(AssertionError):
               compare_results(results_with_ds, results_without_ds, distribution,
    @@ -343,16 +489,21 @@ class TestDistributionStrategyCorrectnessBase(test.TestCase,
             compare_results(results_with_ds, results_without_ds, distribution,
                             testcase=self)
     
    -  def run_dynamic_lr_test(self, distribution):
    +  @combinations.generate(all_strategy_combinations_with_graph_mode())
    +  def test_dynamic_lr(self, distribution):
    +
         with self.cached_session():
    -      self.set_up_test_config()
    -      self.skip_unsupported_test_configuration(distribution)
     
    -      x_train, y_train, _ = self.get_data()
    -      model = self.get_model()
    +      keras.backend.set_image_data_format('channels_last')
    +      np.random.seed(_RANDOM_SEED)
    +      random_seed.set_random_seed(_RANDOM_SEED)
    +
    +      x_train, y_train, _ = _dnn_training_data()
    +
    +      model = _create_dnn_model()
           initial_weights = model.get_weights()
    -      update_freq = None
     
    +      update_freq = None
           if (isinstance(distribution, tpu_strategy.TPUStrategy) and
               distribution.extended.steps_per_run > 1):
             # For TPUStrategy with steps_per_run > 1, the callback is not invoked
    @@ -379,10 +530,10 @@ class TestDistributionStrategyCorrectnessBase(test.TestCase,
             return training_inputs, eval_inputs, predict_inputs
     
           results_with_ds = fit_eval_and_predict(
    -          initial_weights, input_fn=input_fn, model_fn=self.get_model,
    +          initial_weights, input_fn=input_fn, model_fn=_create_dnn_model,
               distribution=distribution)
           results_without_ds = fit_eval_and_predict(
    -          initial_weights, input_fn=input_fn, model_fn=self.get_model,
    +          initial_weights, input_fn=input_fn, model_fn=_create_dnn_model,
               distribution=None)
           compare_results(results_with_ds, results_without_ds, distribution,
                           testcase=self)
    diff --git a/tensorflow/contrib/distribute/python/keras_dnn_correctness_test.py b/tensorflow/contrib/distribute/python/keras_dnn_correctness_test.py
    deleted file mode 100644
    index f21b1c6752..0000000000
    --- a/tensorflow/contrib/distribute/python/keras_dnn_correctness_test.py
    +++ /dev/null
    @@ -1,163 +0,0 @@
    -# 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.
    -# ==============================================================================
    -"""Correctness tests for tf.keras DNN model using DistributionStrategy."""
    -from __future__ import absolute_import
    -from __future__ import division
    -from __future__ import print_function
    -
    -import numpy as np
    -
    -from tensorflow.contrib.distribute.python import combinations
    -from tensorflow.contrib.distribute.python import keras_correctness_test_base
    -from tensorflow.python import keras
    -from tensorflow.python.data.ops import dataset_ops
    -from tensorflow.python.eager import test
    -from tensorflow.python.keras.optimizer_v2 import gradient_descent as gradient_descent_keras
    -from tensorflow.python.training import gradient_descent
    -
    -
    -class TestDistributionStrategyDnnCorrectness(
    -    keras_correctness_test_base.TestDistributionStrategyCorrectnessBase):
    -
    -  def get_model(self, initial_weights=None, distribution=None):
    -    with keras_correctness_test_base.MaybeDistributionScope(distribution):
    -      # We add few non-linear layers to make it non-trivial.
    -      model = keras.Sequential()
    -      model.add(keras.layers.Dense(10, activation='relu', input_shape=(1,)))
    -      model.add(keras.layers.Dense(10, activation='relu'))
    -      model.add(keras.layers.Dense(10, activation='relu'))
    -      model.add(keras.layers.Dense(1))
    -
    -      if initial_weights:
    -        model.set_weights(initial_weights)
    -
    -      model.compile(
    -          loss=keras.losses.mean_squared_error,
    -          optimizer=gradient_descent_keras.SGD(0.5),
    -          metrics=['mse'])
    -      return model
    -
    -  def get_data(self):
    -    # TODO(xiejw): Change this back to 10000, once we support final partial
    -    # batch.
    -    num_samples = 9984
    -    x_train = np.random.rand(num_samples, 1)
    -    y_train = 3 * x_train
    -    x_train = x_train.astype('float32')
    -    y_train = y_train.astype('float32')
    -    x_predict = [[1.], [2.], [3.], [4.]]
    -    return x_train, y_train, x_predict
    -
    -  @combinations.generate(keras_correctness_test_base.
    -                         all_strategy_and_input_config_combinations())
    -  def test_dnn_correctness(self, distribution, use_numpy, use_validation_data):
    -    self.run_correctness_test(distribution, use_numpy, use_validation_data)
    -
    -  @combinations.generate(keras_correctness_test_base.
    -                         all_strategy_combinations_with_graph_mode())
    -  def test_dnn_with_dynamic_learning_rate(self, distribution):
    -    self.run_dynamic_lr_test(distribution)
    -
    -
    -class TestDistributionStrategyDnnMetricCorrectness(
    -    keras_correctness_test_base.TestDistributionStrategyCorrectnessBase):
    -
    -  def get_model(self, distribution=None):
    -    with distribution.scope():
    -      model = keras.Sequential()
    -      model.add(keras.layers.Dense(1,
    -                                   input_shape=(1,),
    -                                   kernel_initializer='ones'))
    -      model.compile(
    -          loss=keras.losses.mean_squared_error,
    -          optimizer=gradient_descent.GradientDescentOptimizer(0.5),
    -          metrics=[keras.metrics.BinaryAccuracy()])
    -    return model
    -
    -  def run_metric_correctness_test(self, distribution):
    -    with self.cached_session():
    -      self.set_up_test_config()
    -      self.skip_unsupported_test_configuration(distribution)
    -
    -      x_train, y_train, _ = self.get_data()
    -      model = self.get_model(distribution=distribution)
    -
    -      batch_size = 64
    -      batch_size = (keras_correctness_test_base.
    -                    get_batch_size(batch_size, distribution))
    -      train_dataset = dataset_ops.Dataset.from_tensor_slices((x_train, y_train))
    -      train_dataset = (keras_correctness_test_base.
    -                       batch_wrapper(train_dataset, batch_size, distribution))
    -
    -      history = model.fit(x=train_dataset, epochs=2, steps_per_epoch=10)
    -      self.assertEqual(history.history['binary_accuracy'], [1.0, 1.0])
    -
    -  @combinations.generate(keras_correctness_test_base.
    -                         all_strategy_combinations_with_eager_and_graph_modes())
    -  def test_simple_dnn_metric_correctness(self, distribution):
    -    self.run_metric_correctness_test(distribution)
    -
    -
    -class TestDistributionStrategyDnnMetricEvalCorrectness(
    -    keras_correctness_test_base.TestDistributionStrategyCorrectnessBase):
    -
    -  def get_model(self, distribution=None):
    -    with distribution.scope():
    -      model = keras.Sequential()
    -      model.add(
    -          keras.layers.Dense(
    -              3, activation='relu', input_dim=4, kernel_initializer='ones'))
    -      model.add(
    -          keras.layers.Dense(
    -              1, activation='sigmoid', kernel_initializer='ones'))
    -      model.compile(
    -          loss='mae',
    -          metrics=['accuracy', keras.metrics.BinaryAccuracy()],
    -          optimizer=gradient_descent.GradientDescentOptimizer(0.001))
    -    return model
    -
    -  def run_eval_metrics_correctness_test(self, distribution):
    -    with self.cached_session():
    -      self.set_up_test_config()
    -      self.skip_unsupported_test_configuration(distribution)
    -
    -      model = self.get_model(distribution=distribution)
    -
    -      # verify correctness of stateful and stateless metrics.
    -      x = np.ones((100, 4)).astype('float32')
    -      y = np.ones((100, 1)).astype('float32')
    -      dataset = dataset_ops.Dataset.from_tensor_slices((x, y)).repeat()
    -      dataset = (keras_correctness_test_base.
    -                 batch_wrapper(dataset, 4, distribution))
    -      outs = model.evaluate(dataset, steps=10)
    -      self.assertEqual(outs[1], 1.)
    -      self.assertEqual(outs[2], 1.)
    -
    -      y = np.zeros((100, 1)).astype('float32')
    -      dataset = dataset_ops.Dataset.from_tensor_slices((x, y)).repeat()
    -      dataset = (keras_correctness_test_base.
    -                 batch_wrapper(dataset, 4, distribution))
    -      outs = model.evaluate(dataset, steps=10)
    -      self.assertEqual(outs[1], 0.)
    -      self.assertEqual(outs[2], 0.)
    -
    -  @combinations.generate(keras_correctness_test_base.
    -                         all_strategy_combinations_with_eager_and_graph_modes())
    -  def test_identity_model_metric_eval_correctness(self, distribution):
    -    self.run_eval_metrics_correctness_test(distribution)
    -
    -
    -if __name__ == '__main__':
    -  test.main()
    diff --git a/tensorflow/contrib/distribute/python/keras_image_model_correctness_test.py b/tensorflow/contrib/distribute/python/keras_image_model_correctness_test.py
    deleted file mode 100644
    index f625664372..0000000000
    --- a/tensorflow/contrib/distribute/python/keras_image_model_correctness_test.py
    +++ /dev/null
    @@ -1,92 +0,0 @@
    -# 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.
    -# ==============================================================================
    -"""Correctness tests for tf.keras CNN models using DistributionStrategy."""
    -from __future__ import absolute_import
    -from __future__ import division
    -from __future__ import print_function
    -
    -import numpy as np
    -
    -from tensorflow.contrib.distribute.python import combinations
    -from tensorflow.contrib.distribute.python import keras_correctness_test_base
    -from tensorflow.python import keras
    -from tensorflow.python.eager import test
    -from tensorflow.python.training import gradient_descent
    -
    -
    -class DistributionStrategyCnnCorrectnessTest(
    -    keras_correctness_test_base.TestDistributionStrategyCorrectnessBase):
    -
    -  def get_model(self, initial_weights=None, distribution=None):
    -    with keras_correctness_test_base.MaybeDistributionScope(distribution):
    -      image = keras.layers.Input(shape=(28, 28, 3), name='image')
    -      c1 = keras.layers.Conv2D(
    -          name='conv1', filters=16, kernel_size=(3, 3), strides=(4, 4))(
    -              image)
    -      if self.with_batch_norm:
    -        c1 = keras.layers.BatchNormalization(name='bn1')(c1)
    -      c1 = keras.layers.MaxPooling2D(pool_size=(2, 2))(c1)
    -      logits = keras.layers.Dense(
    -          10, activation='softmax', name='pred')(
    -              keras.layers.Flatten()(c1))
    -      model = keras.Model(inputs=[image], outputs=[logits])
    -
    -      if initial_weights:
    -        model.set_weights(initial_weights)
    -
    -      model.compile(
    -          optimizer=gradient_descent.GradientDescentOptimizer(
    -              learning_rate=0.1),
    -          loss='sparse_categorical_crossentropy',
    -          metrics=['sparse_categorical_accuracy'])
    -
    -    return model
    -
    -  def get_data(self,
    -               count=keras_correctness_test_base._GLOBAL_BATCH_SIZE
    -               * keras_correctness_test_base._EVAL_STEPS,
    -               shape=(28, 28, 3),
    -               num_classes=10):
    -    centers = np.random.randn(num_classes, *shape)
    -
    -    features = []
    -    labels = []
    -    for _ in range(count):
    -      label = np.random.randint(0, num_classes, size=1)[0]
    -      offset = np.random.normal(loc=0, scale=0.1, size=np.prod(shape))
    -      offset = offset.reshape(shape)
    -      labels.append(label)
    -      features.append(centers[label] + offset)
    -
    -    x_train = np.asarray(features, dtype=np.float32)
    -    y_train = np.asarray(labels, dtype=np.float32).reshape((count, 1))
    -    x_predict = x_train
    -    return x_train, y_train, x_predict
    -
    -  @combinations.generate(keras_correctness_test_base.
    -                         all_strategy_and_input_config_combinations())
    -  def test_cnn_correctness(self, distribution, use_numpy, use_validation_data):
    -    self.run_correctness_test(distribution, use_numpy, use_validation_data)
    -
    -  @combinations.generate(keras_correctness_test_base.
    -                         all_strategy_and_input_config_combinations())
    -  def test_cnn_with_batch_norm_correctness(self, distribution, use_numpy,
    -                                           use_validation_data):
    -    self.run_correctness_test(distribution, use_numpy, use_validation_data,
    -                              with_batch_norm=True)
    -
    -
    -if __name__ == '__main__':
    -  test.main()
    -- 
    GitLab
    
    
    From a76a7e8545c6f0255d7607ca817fa92ed7b08349 Mon Sep 17 00:00:00 2001
    From: "A. Unique TensorFlower" 
    Date: Thu, 10 Jan 2019 12:29:21 -0800
    Subject: [PATCH 616/622] Use CUB-based row reduction for sparse_xent_op. The
     Eigen implementation seems to trigger a bug on Volta GPUs.
    
    PiperOrigin-RevId: 228756250
    ---
     tensorflow/core/kernels/BUILD                 |  5 ++-
     tensorflow/core/kernels/sparse_xent_op.cc     |  9 +++--
     tensorflow/core/kernels/sparse_xent_op.h      | 29 ++++++++++++++--
     .../core/kernels/sparse_xent_op_gpu.cu.cc     | 34 +++++++++++++++++--
     4 files changed, 65 insertions(+), 12 deletions(-)
    
    diff --git a/tensorflow/core/kernels/BUILD b/tensorflow/core/kernels/BUILD
    index 5794ba520e..1a20e8628e 100644
    --- a/tensorflow/core/kernels/BUILD
    +++ b/tensorflow/core/kernels/BUILD
    @@ -4487,7 +4487,10 @@ tf_kernel_library(
         deps = SPARSE_DEPS + [
             ":bounds_check",
             "//third_party/eigen3",
    -    ],
    +    ] + if_cuda([
    +        ":reduction_ops",
    +        "@cub_archive//:cub",
    +    ]),
     )
     
     tf_kernel_library(
    diff --git a/tensorflow/core/kernels/sparse_xent_op.cc b/tensorflow/core/kernels/sparse_xent_op.cc
    index f84ffd5323..37d4d0661c 100644
    --- a/tensorflow/core/kernels/sparse_xent_op.cc
    +++ b/tensorflow/core/kernels/sparse_xent_op.cc
    @@ -90,9 +90,8 @@ class SparseSoftmaxXentWithLogitsOp : public OpKernel {
                 context, CheckInvalidLabelIndex(labels, logits.dim_size(1)));
           }
           functor::SparseXentFunctor functor;
    -      functor(context->eigen_device(), logits.matrix(),
    -              labels.vec(), scratch.vec(), loss_out->vec(),
    -              back_out->matrix());
    +      functor(context, logits.matrix(), labels.vec(),
    +              scratch.vec(), loss_out->vec(), back_out->matrix());
         }
       }
     };
    @@ -102,11 +101,11 @@ class SparseSoftmaxXentWithLogitsOp : public OpKernel {
     namespace functor {
     template 
     struct SparseXentFunctor {
    -  void operator()(const CPUDevice& d, typename TTypes::ConstMatrix logits,
    +  void operator()(OpKernelContext* ctx, typename TTypes::ConstMatrix logits,
                       typename TTypes::ConstVec labels,
                       typename TTypes::Vec scratch, typename TTypes::Vec loss,
                       typename TTypes::Matrix backprop) {
    -    SparseXentEigenImpl::Compute(d, logits, labels,
    +    SparseXentEigenImpl::Compute(ctx, logits, labels,
                                                           scratch, loss, backprop);
       }
     };
    diff --git a/tensorflow/core/kernels/sparse_xent_op.h b/tensorflow/core/kernels/sparse_xent_op.h
    index 6ba7931ab5..5e462424ed 100644
    --- a/tensorflow/core/kernels/sparse_xent_op.h
    +++ b/tensorflow/core/kernels/sparse_xent_op.h
    @@ -18,6 +18,7 @@ limitations under the License.
     // Functor definition for SparseXentOp, must be compilable by nvcc.
     
     #include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor"
    +#include "tensorflow/core/framework/op_kernel.h"
     #include "tensorflow/core/framework/tensor_types.h"
     #include "tensorflow/core/kernels/bounds_check.h"
     #include "tensorflow/core/platform/macros.h"
    @@ -128,6 +129,26 @@ class SparseXentGradGenerator {
     
     namespace functor {
     
    +template 
    +struct RowMaxReduction {
    +  // Computes the maximum across the rows of logits
    +  //
    +  // logits: batch_size, num_classes.
    +  // maximum: temporary tensor, dims: batch_size, 1
    +  static inline void Compute(OpKernelContext* ctx,
    +                             typename TTypes::ConstMatrix logits,
    +                             typename TTypes::Vec maximum) {
    +#if !defined(EIGEN_HAS_INDEX_LIST)
    +    Eigen::array along_row;
    +    along_row[0] = 1;
    +#else
    +    Eigen::IndexList > along_row;
    +#endif
    +    Device d = ctx->eigen_device();
    +    To32Bit(maximum).device(d) = To32Bit(logits).maximum(along_row);
    +  }
    +};
    +
     // Functor used by SparseXentOp to do the computations.
     template 
     struct SparseXentFunctor {
    @@ -138,7 +159,7 @@ struct SparseXentFunctor {
       // scratch: temporary tensor, dims: batch_size, 1
       // loss: output tensor for the loss, dims: batch_size.
       // backprop: output tensor for the backprop, dims: batch_size, num_classes.
    -  void operator()(const Device& d, typename TTypes::ConstMatrix logits,
    +  void operator()(OpKernelContext* ctx, typename TTypes::ConstMatrix logits,
                       typename TTypes::ConstVec labels,
                       typename TTypes::Vec scratch, typename TTypes::Vec loss,
                       typename TTypes::Matrix backprop);
    @@ -149,7 +170,8 @@ struct SparseXentFunctor {
     // specializations for both device types.
     template 
     struct SparseXentEigenImpl {
    -  static void Compute(const Device& d, typename TTypes::ConstMatrix logits,
    +  static void Compute(OpKernelContext* ctx,
    +                      typename TTypes::ConstMatrix logits,
                           typename TTypes::ConstVec labels,
                           typename TTypes::Vec scratch,
                           typename TTypes::Vec loss,
    @@ -188,8 +210,9 @@ struct SparseXentEigenImpl {
     #endif
     
         // scratch = max_logits along classes.
    -    To32Bit(scratch).device(d) = To32Bit(logits).maximum(along_class);
    +    RowMaxReduction::Compute(ctx, logits, scratch);
     
    +    Device d = ctx->eigen_device();
         // backprop = logits - max_logits.
         To32Bit(backprop).device(d) =
             To32Bit(logits) -
    diff --git a/tensorflow/core/kernels/sparse_xent_op_gpu.cu.cc b/tensorflow/core/kernels/sparse_xent_op_gpu.cu.cc
    index d053966028..5fe15352c3 100644
    --- a/tensorflow/core/kernels/sparse_xent_op_gpu.cu.cc
    +++ b/tensorflow/core/kernels/sparse_xent_op_gpu.cu.cc
    @@ -20,22 +20,50 @@ limitations under the License.
     #include "tensorflow/core/kernels/sparse_xent_op.h"
     
     #include "tensorflow/core/framework/tensor_types.h"
    +#include "tensorflow/core/kernels/reduction_gpu_kernels.cu.h"
    +#include "tensorflow/core/kernels/reduction_ops_common.h"
     #include "tensorflow/core/platform/types.h"
     
     namespace tensorflow {
     
     typedef Eigen::GpuDevice GPUDevice;
     
    +namespace functor {
    +
    +// Partial specialization for a GPUDevice, that uses the CUB implementation
    +// from reduction_gpu_kernels.cu.h.
    +template 
    +struct RowMaxReduction {
    +  // Computes the maximum across the rows of logits
    +  //
    +  // logits: batch_size, num_classes.
    +  // maximum: temporary tensor, dims: batch_size, 1
    +  static inline void Compute(OpKernelContext* ctx,
    +                             typename TTypes::ConstMatrix logits,
    +                             typename TTypes::Vec maximum) {
    +    const int kBatchDim = 0;
    +    const int kClassDim = 1;
    +    const int rows = logits.dimension(kBatchDim);
    +    const int cols = logits.dimension(kClassDim);
    +
    +    typedef const Eigen::array::Tensor::Index, 1>& ReductionAxes;
    +    Constants constants;
    +    cub::Max op;
    +    functor::ReduceImpl(
    +        ctx, maximum.data(), logits.data(), 2, rows, cols, 1, 1, constants.kOne,
    +        op);
    +  }
    +};
    +
     // Partial specialization for a GPUDevice, that uses the Eigen implementation
     // from XentEigenImpl.
    -namespace functor {
     template 
     struct SparseXentFunctor {
    -  void operator()(const GPUDevice& d, typename TTypes::ConstMatrix logits,
    +  void operator()(OpKernelContext* ctx, typename TTypes::ConstMatrix logits,
                       typename TTypes::ConstVec labels,
                       typename TTypes::Vec scratch, typename TTypes::Vec loss,
                       typename TTypes::Matrix backprop) {
    -    SparseXentEigenImpl::Compute(d, logits, labels,
    +    SparseXentEigenImpl::Compute(ctx, logits, labels,
                                                           scratch, loss, backprop);
       }
     };
    -- 
    GitLab
    
    
    From 0b3c3c55e177b35d38ba33170ebe2baa3f5badff Mon Sep 17 00:00:00 2001
    From: Sanjoy Das 
    Date: Thu, 10 Jan 2019 12:30:40 -0800
    Subject: [PATCH 617/622] Be resilient towards graphs without source/sink
     connectivity.
    
    PiperOrigin-RevId: 228756459
    ---
     tensorflow/compiler/jit/mark_for_compilation_pass.cc | 5 +++++
     1 file changed, 5 insertions(+)
    
    diff --git a/tensorflow/compiler/jit/mark_for_compilation_pass.cc b/tensorflow/compiler/jit/mark_for_compilation_pass.cc
    index 6618e3a58a..50afec020d 100644
    --- a/tensorflow/compiler/jit/mark_for_compilation_pass.cc
    +++ b/tensorflow/compiler/jit/mark_for_compilation_pass.cc
    @@ -677,6 +677,11 @@ Status MarkForCompilationPass::Run(
       VLOG(1) << "flags->tf_xla_auto_jit = " << flags->tf_xla_auto_jit;
       const FunctionLibraryDefinition* fld = options.flib_def;
     
    +  // Deadness analysis expects a graph with source and sink edges properly
    +  // connected but sometimes the incoming graph does not follow this invariant.
    +  // So fix up the source and sink edges before calling into deadness analysis.
    +  FixupSourceAndSinkEdges(options.graph->get());
    +
       std::unique_ptr deadness;
       {
         XLA_SCOPED_LOGGING_TIMER_LEVEL("DeadnessAnalysis", 1);
    -- 
    GitLab
    
    
    From 9c45814aac694a6454d312225121dd4c4cd33de9 Mon Sep 17 00:00:00 2001
    From: "A. Unique TensorFlower" 
    Date: Thu, 10 Jan 2019 12:45:38 -0800
    Subject: [PATCH 618/622] Update documentation of `tf.distribute.Strategy
     methods` to clarify and/or correct whether they expect global or per-replica
     batching.
    
    PiperOrigin-RevId: 228758901
    ---
     .../python/collective_all_reduce_strategy.py  |  8 ++++
     .../distribute/python/mirrored_strategy.py    | 26 +++++++++++
     .../distribute/python/one_device_strategy.py  |  1 +
     .../python/parameter_server_strategy.py       | 26 +++++++++++
     .../contrib/distribute/python/tpu_strategy.py |  9 +++-
     .../python/distribute/distribute_lib.py       | 44 ++++++++++++-------
     .../python/distribute/mirrored_strategy.py    |  8 ++++
     .../distribute/parameter_server_strategy.py   |  8 ++++
     8 files changed, 113 insertions(+), 17 deletions(-)
    
    diff --git a/tensorflow/contrib/distribute/python/collective_all_reduce_strategy.py b/tensorflow/contrib/distribute/python/collective_all_reduce_strategy.py
    index a3538dfc50..eee0754325 100644
    --- a/tensorflow/contrib/distribute/python/collective_all_reduce_strategy.py
    +++ b/tensorflow/contrib/distribute/python/collective_all_reduce_strategy.py
    @@ -372,4 +372,12 @@ class CollectiveAllReduceExtended(mirrored_strategy.MirroredExtended):
       # TODO(priyag): Delete this once all strategies use global batch size.
       @property
       def _global_batch_size(self):
    +    """`make_dataset_iterator` and `make_numpy_iterator` use global batch size.
    +
    +    `distribute_dataset` and `make_input_fn_iterator` assume per-replica
    +    batching.
    +
    +    Returns:
    +      Boolean.
    +    """
         return True
    diff --git a/tensorflow/contrib/distribute/python/mirrored_strategy.py b/tensorflow/contrib/distribute/python/mirrored_strategy.py
    index 5fa36fb402..2e23a51ee5 100644
    --- a/tensorflow/contrib/distribute/python/mirrored_strategy.py
    +++ b/tensorflow/contrib/distribute/python/mirrored_strategy.py
    @@ -104,6 +104,31 @@ class MirroredStrategy(distribute_lib.DistributionStrategy):
                                     auto_shard_dataset)
         super(MirroredStrategy, self).__init__(extended)
     
    +  # Override to change the documentation to reflect the different handling of
    +  # global vs. local batch size between core and contrib.
    +  def make_dataset_iterator(self, dataset):  # pylint: disable=useless-super-delegation
    +    """Makes an iterator for input provided via `dataset`.
    +
    +    NOTE: The batch size of the `dataset` argument is treated differently for
    +    this contrib version of `MirroredStrategy`.
    +
    +    Data from the given dataset will be distributed evenly across all the
    +    compute replicas. We will assume that the input dataset is batched by the
    +    per-replica batch size.
    +
    +    The user could also use `make_input_fn_iterator` if they want to
    +    customize which input is fed to which replica/worker etc.
    +
    +    Args:
    +      dataset: `tf.data.Dataset` that will be distributed evenly across all
    +        replicas.
    +
    +    Returns:
    +      An `tf.distribute.InputIterator` which returns inputs for each step of the
    +      computation.  User should call `initialize` on the returned iterator.
    +    """
    +    return super(MirroredStrategy, self).make_dataset_iterator(dataset)
    +
       # Override to change the documentation to reflect the different handling of
       # global vs. local batch size between core and contrib.
       def experimental_make_numpy_iterator(  # pylint: disable=useless-super-delegation
    @@ -180,4 +205,5 @@ class MirroredExtended(CoreMirroredExtended):
       # TODO(priyag): Delete this once all strategies use global batch size.
       @property
       def _global_batch_size(self):
    +    """The contrib version of Mirrored strategy uses per-replica batch size."""
         return False
    diff --git a/tensorflow/contrib/distribute/python/one_device_strategy.py b/tensorflow/contrib/distribute/python/one_device_strategy.py
    index 34b0c31087..836cb7cc41 100644
    --- a/tensorflow/contrib/distribute/python/one_device_strategy.py
    +++ b/tensorflow/contrib/distribute/python/one_device_strategy.py
    @@ -199,6 +199,7 @@ class OneDeviceExtended(distribute_lib.DistributionStrategyExtended):
       # TODO(priyag): Delete this once all strategies use global batch size.
       @property
       def _global_batch_size(self):
    +    """Global and per-replica batching are equivalent for OneDeviceStrategy."""
         return True
     
     
    diff --git a/tensorflow/contrib/distribute/python/parameter_server_strategy.py b/tensorflow/contrib/distribute/python/parameter_server_strategy.py
    index 0785427c2c..0cefef7545 100644
    --- a/tensorflow/contrib/distribute/python/parameter_server_strategy.py
    +++ b/tensorflow/contrib/distribute/python/parameter_server_strategy.py
    @@ -89,6 +89,31 @@ class ParameterServerStrategy(distribute_lib.DistributionStrategy):
         super(ParameterServerStrategy, self).__init__(
             ParameterServerExtended(self, num_gpus_per_worker))
     
    +  # Override to change the documentation to reflect the different handling of
    +  # global vs. local batch size between core and contrib.
    +  def make_dataset_iterator(self, dataset):  # pylint: disable=useless-super-delegation
    +    """Makes an iterator for input provided via `dataset`.
    +
    +    NOTE: The batch size of the `dataset` argument is treated differently for
    +    this contrib version of `ParameterServerStrategy`.
    +
    +    Data from the given dataset will be distributed evenly across all the
    +    compute replicas. We will assume that the input dataset is batched by the
    +    per-replica batch size.
    +
    +    The user could also use `make_input_fn_iterator` if they want to
    +    customize which input is fed to which replica/worker etc.
    +
    +    Args:
    +      dataset: `tf.data.Dataset` that will be distributed evenly across all
    +        replicas.
    +
    +    Returns:
    +      An `tf.distribute.InputIterator` which returns inputs for each step of the
    +      computation.  User should call `initialize` on the returned iterator.
    +    """
    +    return super(ParameterServerStrategy, self).make_dataset_iterator(dataset)
    +
       # Override to change the documentation to reflect the different handling of
       # global vs. local batch size between core and contrib.
       def experimental_make_numpy_iterator(  # pylint: disable=useless-super-delegation
    @@ -143,4 +168,5 @@ class ParameterServerExtended(CoreParameterServerExtended):
       # TODO(priyag): Delete this once all strategies use global batch size.
       @property
       def _global_batch_size(self):
    +    """The contrib version of PS strategy uses per-replica batch size."""
         return False
    diff --git a/tensorflow/contrib/distribute/python/tpu_strategy.py b/tensorflow/contrib/distribute/python/tpu_strategy.py
    index 3f89c5869e..518c704b89 100644
    --- a/tensorflow/contrib/distribute/python/tpu_strategy.py
    +++ b/tensorflow/contrib/distribute/python/tpu_strategy.py
    @@ -295,7 +295,6 @@ class TPUExtended(distribute_lib.DistributionStrategyExtended):
     
       def _make_dataset_iterator(self, dataset):
         """Make iterators for each of the TPU hosts."""
    -
         return input_lib.DatasetIterator(dataset, self._input_workers,
                                          self._num_replicas_in_sync)
     
    @@ -667,6 +666,14 @@ class TPUExtended(distribute_lib.DistributionStrategyExtended):
       # TODO(priyag): Delete this once all strategies use global batch size.
       @property
       def _global_batch_size(self):
    +    """`make_dataset_iterator` and `make_numpy_iterator` use global batch size.
    +
    +    `distribute_dataset` and `make_input_fn_iterator` assume per-replica
    +    batching.
    +
    +    Returns:
    +      Boolean.
    +    """
         return True
     
     
    diff --git a/tensorflow/python/distribute/distribute_lib.py b/tensorflow/python/distribute/distribute_lib.py
    index 31213ab647..5fe77e5478 100644
    --- a/tensorflow/python/distribute/distribute_lib.py
    +++ b/tensorflow/python/distribute/distribute_lib.py
    @@ -210,12 +210,14 @@ class _SameScopeAgainContext(object):
     # TODO(yuefengz): add more replication modes.
     @tf_export("distribute.InputReplicationMode")
     class InputReplicationMode(enum.Enum):
    -  """Replication mode for input function."""
    +  """Replication mode for input function.
     
    -  # The input function will be called on each worker independently, creating as
    -  # many input pipelines as number of workers. Replicas will dequeue from the
    -  # local Dataset on their worker. Distribution Strategy doesn't manage any
    -  # state sharing between such separate input pipelines.
    +  * `PER_WORKER`: The input function will be called on each worker
    +    independently, creating as many input pipelines as number of workers.
    +    Replicas will dequeue from the local Dataset on their worker.
    +    `tf.distribute.Strategy` doesn't manage any state sharing between such
    +    separate input pipelines.
    +  """
       PER_WORKER = "PER_WORKER"
     
     
    @@ -353,7 +355,8 @@ class DistributionStrategy(object):
         ```
     
         Args:
    -      dataset_fn: A function that returns a `tf.data.Dataset`.
    +      dataset_fn: A function that returns a `tf.data.Dataset` with per-replica
    +        batching.
     
         Returns:
           A `PerReplicaDataset` that will produce data for each replica.
    @@ -390,28 +393,36 @@ class DistributionStrategy(object):
         """Returns an iterator split across replicas created from an input function.
     
         The `input_fn` should take an `tf.distribute.InputContext` object where
    -    information about input sharding can be accessed:
    +    information about batching and input sharding can be accessed:
     
         ```
         def input_fn(input_context):
    -      d = tf.data.Dataset.from_tensors([[1.]]).repeat()
    +      batch_size = input_context.get_per_replica_batch_size(global_batch_size)
    +      d = tf.data.Dataset.from_tensors([[1.]]).repeat().batch(batch_size)
           return d.shard(input_context.num_input_pipelines,
                          input_context.input_pipeline_id)
         with strategy.scope():
    -      iterator = strategy.make_input_fn_iterator(
    -          input_fn)
    -      replica_results = strategy.extended.call_for_each_replica(
    -          replica_fn, iterator.get_next())
    +      iterator = strategy.make_input_fn_iterator(input_fn)
    +      replica_results = strategy.experimental_run(replica_fn, iterator)
         ```
     
    +    The `tf.data.Dataset` returned by `input_fn` should have a per-replica
    +    batch size, which may be computed using
    +    `input_context.get_per_replica_batch_size`.
    +
         Args:
    -      input_fn: A function that returns a `tf.data.Dataset`. This function is
    -        expected to take an `tf.distribute.InputContext` object.
    +      input_fn: A function taking a `tf.distribute.InputContext` object and
    +        returning a `tf.data.Dataset`.
           replication_mode: an enum value of `tf.distribute.InputReplicationMode`.
    -        Only `PER_WORKER` is supported currently.
    +        Only `PER_WORKER` is supported currently, which means there will be
    +        a single call to `input_fn` per worker. Replicas will dequeue from the
    +        local `tf.data.Dataset` on their worker.
     
         Returns:
    -      An iterator object that can be initialized and fetched next element.
    +      An iterator object that should first be `.initialize()`-ed. It may then
    +      either be passed to `strategy.experimental_run()` or you can
    +      `iterator.get_next()` to get the next value to pass to
    +      `strategy.extended.call_for_each_replica()`.
         """
         if replication_mode != InputReplicationMode.PER_WORKER:
           raise ValueError(
    @@ -1813,6 +1824,7 @@ class _DefaultDistributionExtended(DistributionStrategyExtended):
       # TODO(priyag): Delete this once all strategies use global batch size.
       @property
       def _global_batch_size(self):
    +    """Global and per-replica batching are equivalent for this strategy."""
         return True
     
     
    diff --git a/tensorflow/python/distribute/mirrored_strategy.py b/tensorflow/python/distribute/mirrored_strategy.py
    index c0a39d4b55..1ed7eef1ed 100644
    --- a/tensorflow/python/distribute/mirrored_strategy.py
    +++ b/tensorflow/python/distribute/mirrored_strategy.py
    @@ -782,6 +782,14 @@ class MirroredExtended(distribute_lib.DistributionStrategyExtended):
       # TODO(priyag): Delete this once all strategies use global batch size.
       @property
       def _global_batch_size(self):
    +    """`make_dataset_iterator` and `make_numpy_iterator` use global batch size.
    +
    +    `distribute_dataset` and `make_input_fn_iterator` assume per-replica
    +    batching.
    +
    +    Returns:
    +      Boolean.
    +    """
         return True
     
     
    diff --git a/tensorflow/python/distribute/parameter_server_strategy.py b/tensorflow/python/distribute/parameter_server_strategy.py
    index ac5ee6f589..9e865082f1 100644
    --- a/tensorflow/python/distribute/parameter_server_strategy.py
    +++ b/tensorflow/python/distribute/parameter_server_strategy.py
    @@ -536,4 +536,12 @@ class ParameterServerStrategyExtended(
       # TODO(priyag): Delete this once all strategies use global batch size.
       @property
       def _global_batch_size(self):
    +    """`make_dataset_iterator` and `make_numpy_iterator` use global batch size.
    +
    +    `distribute_dataset` and `make_input_fn_iterator` assume per-replica
    +    batching.
    +
    +    Returns:
    +      Boolean.
    +    """
         return True
    -- 
    GitLab
    
    
    From a23420e75ddf38b92c93fe80e0d83ea7b70b11b1 Mon Sep 17 00:00:00 2001
    From: Karim Nosir 
    Date: Thu, 10 Jan 2019 12:47:37 -0800
    Subject: [PATCH 619/622] Enable test in OSS
    
    PiperOrigin-RevId: 228759239
    ---
     tensorflow/lite/kernels/BUILD | 3 ---
     1 file changed, 3 deletions(-)
    
    diff --git a/tensorflow/lite/kernels/BUILD b/tensorflow/lite/kernels/BUILD
    index 50deac9bb6..7a4b6b8644 100644
    --- a/tensorflow/lite/kernels/BUILD
    +++ b/tensorflow/lite/kernels/BUILD
    @@ -585,9 +585,6 @@ tf_cc_test(
         name = "bidirectional_sequence_rnn_test",
         size = "small",
         srcs = ["bidirectional_sequence_rnn_test.cc"],
    -    tags = [
    -        "tflite_not_portable",
    -    ],
         deps = [
             ":builtin_ops",
             "//tensorflow/lite:framework",
    -- 
    GitLab
    
    
    From b1876c7c25cb1d40c79a77c21f1ae05deca36cc4 Mon Sep 17 00:00:00 2001
    From: "A. Unique TensorFlower" 
    Date: Thu, 10 Jan 2019 12:52:05 -0800
    Subject: [PATCH 620/622] Migrate to new names: * get_distribution_strategy ->
     get_strategy * has_distribution_strategy -> has_strategy
    
    PiperOrigin-RevId: 228759939
    ---
     tensorflow/contrib/distribute/__init__.py             | 2 ++
     tensorflow/contrib/optimizer_v2/optimizer_v2.py       | 3 +--
     tensorflow/python/distribute/input_lib.py             | 2 +-
     tensorflow/python/distribute/values.py                | 4 ++--
     tensorflow/python/eager/tape.py                       | 4 ++--
     tensorflow/python/framework/test_util.py              | 2 +-
     tensorflow/python/keras/engine/training.py            | 4 ++--
     tensorflow/python/keras/layers/normalization.py       | 9 +++++----
     tensorflow/python/keras/optimizer_v2/optimizer_v2.py  | 8 +++-----
     tensorflow/python/keras/optimizers.py                 | 2 +-
     tensorflow/python/training/optimizer.py               | 5 ++---
     tensorflow/python/training/session_manager.py         | 2 +-
     tensorflow/python/training/slot_creator.py            | 6 ++----
     tensorflow/python/training/sync_replicas_optimizer.py | 3 +--
     14 files changed, 26 insertions(+), 30 deletions(-)
    
    diff --git a/tensorflow/contrib/distribute/__init__.py b/tensorflow/contrib/distribute/__init__.py
    index 4c3f9b8f02..1bcc453a7e 100644
    --- a/tensorflow/contrib/distribute/__init__.py
    +++ b/tensorflow/contrib/distribute/__init__.py
    @@ -64,7 +64,9 @@ _allowed_symbols = [
         'get_distribution_strategy',
         'get_loss_reduction',
         'get_replica_context',
    +    'get_strategy',
         'has_distribution_strategy',
    +    'has_strategy',
         'in_cross_replica_context',
         'require_replica_context',
         'run_standard_tensorflow_server',
    diff --git a/tensorflow/contrib/optimizer_v2/optimizer_v2.py b/tensorflow/contrib/optimizer_v2/optimizer_v2.py
    index 7fb23abc38..1323ed014c 100644
    --- a/tensorflow/contrib/optimizer_v2/optimizer_v2.py
    +++ b/tensorflow/contrib/optimizer_v2/optimizer_v2.py
    @@ -843,8 +843,7 @@ class OptimizerV2(optimizer_v1.Optimizer):
           scale_loss_by_num_replicas = (
               distribute_lib.get_loss_reduction() == ds_reduce_util.ReduceOp.MEAN)
         if scale_loss_by_num_replicas:
    -      num_replicas = \
    -        distribute_ctx.get_distribution_strategy().num_replicas_in_sync
    +      num_replicas = distribute_ctx.get_strategy().num_replicas_in_sync
           if num_replicas > 1:
             loss_value *= 1. / num_replicas
         return loss_value
    diff --git a/tensorflow/python/distribute/input_lib.py b/tensorflow/python/distribute/input_lib.py
    index 022595146d..c64eea1604 100644
    --- a/tensorflow/python/distribute/input_lib.py
    +++ b/tensorflow/python/distribute/input_lib.py
    @@ -689,7 +689,7 @@ class MultiStepContext(object):
           if reduce_op is None:
             self._last_step_outputs[name] = output
           else:
    -        distribution = distribution_strategy_context.get_distribution_strategy()
    +        distribution = distribution_strategy_context.get_strategy()
             self._last_step_outputs[name] = distribution.reduce(reduce_op, output)
         else:
           assert reduce_op is not None
    diff --git a/tensorflow/python/distribute/values.py b/tensorflow/python/distribute/values.py
    index a9dcabdab6..2a57d5cfc7 100644
    --- a/tensorflow/python/distribute/values.py
    +++ b/tensorflow/python/distribute/values.py
    @@ -411,11 +411,11 @@ def _assign_on_device(device, variable, tensor):
     
     
     def _assert_strategy(strategy):
    -  if not distribution_strategy_context.has_distribution_strategy():
    +  if not distribution_strategy_context.has_strategy():
         raise RuntimeError(
             'Need to be inside "with strategy.scope()" for %s' %
             (strategy,))
    -  current_strategy = distribution_strategy_context.get_distribution_strategy()
    +  current_strategy = distribution_strategy_context.get_strategy()
       if current_strategy is not strategy:
         raise RuntimeError(
             "Mixing different tf.distribute.Strategy objects: %s is not %s" %
    diff --git a/tensorflow/python/eager/tape.py b/tensorflow/python/eager/tape.py
    index e501b403a3..56b68b9eea 100644
    --- a/tensorflow/python/eager/tape.py
    +++ b/tensorflow/python/eager/tape.py
    @@ -61,7 +61,7 @@ def watch(tape, tensor):
     
     def watch_variable(tape, variable):
       """Marks this variable to be watched by the given tape."""
    -  strategy = distribution_strategy_context.get_distribution_strategy()
    +  strategy = distribution_strategy_context.get_strategy()
       if distribution_strategy_context.get_replica_context():
         variables = [strategy.extended.value_container(variable)]
       else:
    @@ -76,7 +76,7 @@ def variable_accessed(variable):
       Args:
         variable: variable to be watched.
       """
    -  strategy = distribution_strategy_context.get_distribution_strategy()
    +  strategy = distribution_strategy_context.get_strategy()
       if distribution_strategy_context.get_replica_context():
         variables = [strategy.extended.value_container(variable)]
       else:
    diff --git a/tensorflow/python/framework/test_util.py b/tensorflow/python/framework/test_util.py
    index 21d21cc7f4..6d01d3bf54 100644
    --- a/tensorflow/python/framework/test_util.py
    +++ b/tensorflow/python/framework/test_util.py
    @@ -735,7 +735,7 @@ def assert_no_garbage_created(f):
         """Sets DEBUG_SAVEALL, runs the test, and checks for new garbage."""
         # Force-load `distribution_strategy_context` to prevent GC at
         # test time when using eager. Remove once b/117329403 is resolved.
    -    tape.distribution_strategy_context.get_distribution_strategy()
    +    tape.distribution_strategy_context.get_strategy()
     
         gc.disable()
         previous_debug_flags = gc.get_debug()
    diff --git a/tensorflow/python/keras/engine/training.py b/tensorflow/python/keras/engine/training.py
    index a65c2b6413..0b1743af38 100644
    --- a/tensorflow/python/keras/engine/training.py
    +++ b/tensorflow/python/keras/engine/training.py
    @@ -216,14 +216,14 @@ class Model(Network):
           self._distribution_strategy = distribute
           self._compile_distribution = True
         else:
    -      if distribution_strategy_context.has_distribution_strategy():
    +      if distribution_strategy_context.has_strategy():
             # When the user builds the model in the DS scope and cross replica
             # context we want distribution strategy to be set but when building the
             # replica copies of the models internally we should not be compiling
             # with distribution strategy and use the default compilation path.
             if distribution_strategy_context.in_cross_replica_context():
               self._distribution_strategy = (
    -              distribution_strategy_context.get_distribution_strategy())
    +              distribution_strategy_context.get_strategy())
     
         # Validate that arguments passed by the user to `compile` are supported by
         # DistributionStrategy.
    diff --git a/tensorflow/python/keras/layers/normalization.py b/tensorflow/python/keras/layers/normalization.py
    index 98bc10881b..c174c8ddd6 100644
    --- a/tensorflow/python/keras/layers/normalization.py
    +++ b/tensorflow/python/keras/layers/normalization.py
    @@ -417,8 +417,8 @@ class BatchNormalizationV2(Layer):
           # since TPUStrategy does not implement replica local variables.
           # Remove this hack once we support TPULocalVariables.
           is_tpu_strategy = False
    -      if distribution_strategy_context.has_distribution_strategy():
    -        distribute = distribution_strategy_context.get_distribution_strategy()
    +      if distribution_strategy_context.has_strategy():
    +        distribute = distribution_strategy_context.get_strategy()
             if distribute.__class__.__name__ == 'TPUStrategy':
               is_tpu_strategy = True
     
    @@ -474,7 +474,7 @@ class BatchNormalizationV2(Layer):
           momentum = ops.convert_to_tensor(self.momentum)
         if training_value or training_value is None:
           if distribution_strategy_context.in_cross_replica_context():
    -        strategy = distribution_strategy_context.get_distribution_strategy()
    +        strategy = distribution_strategy_context.get_strategy()
             mean_update = strategy.extended.update(
                 self.moving_mean, self._assign_moving_average,
                 (mean, self.momentum))
    @@ -666,7 +666,8 @@ class BatchNormalizationV2(Layer):
             scale, offset = _compose_transforms(r, d, scale, offset)
     
           if distribution_strategy_context.in_cross_replica_context():
    -        strategy = distribution_strategy_context.get_distribution_strategy()
    +        strategy = distribution_strategy_context.get_strategy()
    +
             def _do_update(var, value):
               """Compute the updates for mean and variance."""
               if in_eager_mode and not self.trainable:
    diff --git a/tensorflow/python/keras/optimizer_v2/optimizer_v2.py b/tensorflow/python/keras/optimizer_v2/optimizer_v2.py
    index 98f87a41ae..894af66f5d 100644
    --- a/tensorflow/python/keras/optimizer_v2/optimizer_v2.py
    +++ b/tensorflow/python/keras/optimizer_v2/optimizer_v2.py
    @@ -290,8 +290,7 @@ class OptimizerV2(checkpointable.CheckpointableBase):
       @staticmethod
       def _scale_loss(loss_value):
         if distribute_lib.get_loss_reduction() == ds_reduce_util.ReduceOp.MEAN:
    -      num_replicas = \
    -        distribute_ctx.get_distribution_strategy().num_replicas_in_sync
    +      num_replicas = distribute_ctx.get_strategy().num_replicas_in_sync
           if num_replicas > 1:
             loss_value *= (1. / num_replicas)
         return loss_value
    @@ -349,7 +348,7 @@ class OptimizerV2(checkpointable.CheckpointableBase):
         """
         grads_and_vars = _filter_grads(grads_and_vars)
         var_list = [v for (_, v) in grads_and_vars]
    -    if distribute_ctx.has_distribution_strategy():
    +    if distribute_ctx.has_strategy():
           reduced_grads = merge_grads(grads_and_vars)
           grads_and_vars = zip(reduced_grads, var_list)
     
    @@ -900,8 +899,7 @@ def _var_key(var):
       """
     
       # pylint: disable=protected-access
    -  if distribute_ctx.has_distribution_strategy() and hasattr(
    -      var, "_primary_var"):
    +  if distribute_ctx.has_strategy() and hasattr(var, "_primary_var"):
         var = var._primary_var
       if hasattr(var, "op"):
         return var._shared_name
    diff --git a/tensorflow/python/keras/optimizers.py b/tensorflow/python/keras/optimizers.py
    index 12168e5574..82fb555b57 100644
    --- a/tensorflow/python/keras/optimizers.py
    +++ b/tensorflow/python/keras/optimizers.py
    @@ -740,7 +740,7 @@ class TFOptimizer(Optimizer, checkpointable.CheckpointableBase):
         return self.optimizer.compute_gradients(loss, params)
     
       def get_updates(self, loss, params):
    -    if distribution_strategy_context.has_distribution_strategy():
    +    if distribution_strategy_context.has_strategy():
           self.updates = []
     
           if not params:
    diff --git a/tensorflow/python/training/optimizer.py b/tensorflow/python/training/optimizer.py
    index c6cc0b6044..8076ed31bf 100644
    --- a/tensorflow/python/training/optimizer.py
    +++ b/tensorflow/python/training/optimizer.py
    @@ -521,8 +521,7 @@ class Optimizer(
       @staticmethod
       def _scale_loss(loss_value):
         if distribute_lib.get_loss_reduction() == ds_reduce_util.ReduceOp.MEAN:
    -      num_replicas = \
    -        distribute_ctx.get_distribution_strategy().num_replicas_in_sync
    +      num_replicas = distribute_ctx.get_strategy().num_replicas_in_sync
           if num_replicas > 1:
             loss_value *= (1. / num_replicas)
         return loss_value
    @@ -816,7 +815,7 @@ class Optimizer(
         v = self._non_slot_dict.get(key, None)
         if v is None:
           self._maybe_initialize_checkpointable()
    -      distribution_strategy = distribute_ctx.get_distribution_strategy()
    +      distribution_strategy = distribute_ctx.get_strategy()
           with distribution_strategy.colocate_vars_with(colocate_with):
             if eager:
               restored_initial_value = self._preload_simple_restoration(
    diff --git a/tensorflow/python/training/session_manager.py b/tensorflow/python/training/session_manager.py
    index cd22e7e7de..7bd0891e35 100644
    --- a/tensorflow/python/training/session_manager.py
    +++ b/tensorflow/python/training/session_manager.py
    @@ -186,7 +186,7 @@ class SessionManager(object):
         # This is required to so that we initialize the TPU device before
         # restoring from checkpoint since we'll be placing variables on the device
         # and TPUInitialize wipes out the memory of the device.
    -    strategy = distribution_strategy_context.get_distribution_strategy()
    +    strategy = distribution_strategy_context.get_strategy()
         if strategy and hasattr(strategy.extended,
                                 "_experimental_initialize_system"):
           strategy.extended._experimental_initialize_system()  # pylint: disable=protected-access
    diff --git a/tensorflow/python/training/slot_creator.py b/tensorflow/python/training/slot_creator.py
    index bc1137e200..abe1253b00 100644
    --- a/tensorflow/python/training/slot_creator.py
    +++ b/tensorflow/python/training/slot_creator.py
    @@ -121,8 +121,7 @@ def create_slot(primary, val, name, colocate_with_primary=True):
         prefix = primary.op.name
       with variable_scope.variable_scope(None, prefix + "/" + name):
         if colocate_with_primary:
    -      distribution_strategy = (
    -          distribution_strategy_context.get_distribution_strategy())
    +      distribution_strategy = distribution_strategy_context.get_strategy()
           with distribution_strategy.colocate_vars_with(primary):
             return _create_slot_var(primary, val, "", validate_shape, None, None)
         else:
    @@ -159,8 +158,7 @@ def create_slot_with_initializer(primary, initializer, shape, dtype, name,
         prefix = primary.op.name
       with variable_scope.variable_scope(None, prefix + "/" + name):
         if colocate_with_primary:
    -      distribution_strategy = (
    -          distribution_strategy_context.get_distribution_strategy())
    +      distribution_strategy = distribution_strategy_context.get_strategy()
           with distribution_strategy.colocate_vars_with(primary):
             return _create_slot_var(primary, initializer, "", validate_shape, shape,
                                     dtype)
    diff --git a/tensorflow/python/training/sync_replicas_optimizer.py b/tensorflow/python/training/sync_replicas_optimizer.py
    index cd4590db7f..0079ecc98b 100644
    --- a/tensorflow/python/training/sync_replicas_optimizer.py
    +++ b/tensorflow/python/training/sync_replicas_optimizer.py
    @@ -260,8 +260,7 @@ class SyncReplicasOptimizer(optimizer.Optimizer):
         # local_anchor op will be placed on this worker task by default.
         local_anchor = control_flow_ops.no_op()
         # Colocating local_step variable prevents it being placed on the PS.
    -    distribution_strategy = (
    -        distribution_strategy_context.get_distribution_strategy())
    +    distribution_strategy = distribution_strategy_context.get_strategy()
         with distribution_strategy.colocate_vars_with(local_anchor):
           self._local_step = variable_scope.variable(
               initial_value=0,
    -- 
    GitLab
    
    
    From 0418e820833f88bc3545644d0aed81f51022a359 Mon Sep 17 00:00:00 2001
    From: Shashi Shekhar 
    Date: Thu, 10 Jan 2019 13:09:58 -0800
    Subject: [PATCH 621/622] TfLite calibration library.
    
    TfLite calibration library to capture activation ranges of tensors.
    
    PiperOrigin-RevId: 228762955
    ---
     .../lite/tools/optimize/calibration_common.h  |  61 ++++
     .../lite/tools/optimize/calibration_logger.h  |  85 +++++
     .../lite/tools/optimize/calibration_reader.cc |  55 +++
     .../lite/tools/optimize/calibration_reader.h  |  55 +++
     tensorflow/lite/tools/optimize/calibrator.cc  | 345 ++++++++++++++++++
     tensorflow/lite/tools/optimize/calibrator.h   |  64 ++++
     .../lite/tools/optimize/calibrator_test.cc    | 189 ++++++++++
     .../tools/optimize/logging_op_resolver.cc     |  61 ++++
     .../lite/tools/optimize/logging_op_resolver.h |  59 +++
     .../optimize/logging_op_resolver_test.cc      | 141 +++++++
     .../lite/tools/optimize/node_info_delegate.cc |  66 ++++
     .../lite/tools/optimize/node_info_delegate.h  |  67 ++++
     .../tools/optimize/node_info_delegate_test.cc | 152 ++++++++
     13 files changed, 1400 insertions(+)
     create mode 100644 tensorflow/lite/tools/optimize/calibration_common.h
     create mode 100644 tensorflow/lite/tools/optimize/calibration_logger.h
     create mode 100644 tensorflow/lite/tools/optimize/calibration_reader.cc
     create mode 100644 tensorflow/lite/tools/optimize/calibration_reader.h
     create mode 100644 tensorflow/lite/tools/optimize/calibrator.cc
     create mode 100644 tensorflow/lite/tools/optimize/calibrator.h
     create mode 100644 tensorflow/lite/tools/optimize/calibrator_test.cc
     create mode 100644 tensorflow/lite/tools/optimize/logging_op_resolver.cc
     create mode 100644 tensorflow/lite/tools/optimize/logging_op_resolver.h
     create mode 100644 tensorflow/lite/tools/optimize/logging_op_resolver_test.cc
     create mode 100644 tensorflow/lite/tools/optimize/node_info_delegate.cc
     create mode 100644 tensorflow/lite/tools/optimize/node_info_delegate.h
     create mode 100644 tensorflow/lite/tools/optimize/node_info_delegate_test.cc
    
    diff --git a/tensorflow/lite/tools/optimize/calibration_common.h b/tensorflow/lite/tools/optimize/calibration_common.h
    new file mode 100644
    index 0000000000..1ff2d3f18a
    --- /dev/null
    +++ b/tensorflow/lite/tools/optimize/calibration_common.h
    @@ -0,0 +1,61 @@
    +/* Copyright 2018 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_LITE_TOOLS_OPTIMIZE_CALIBRATION_COMMON_H_
    +#define TENSORFLOW_LITE_TOOLS_OPTIMIZE_CALIBRATION_COMMON_H_
    +
    +#include 
    +#include 
    +
    +#include "tensorflow/lite/mutable_op_resolver.h"
    +
    +namespace tflite {
    +namespace optimize {
    +namespace calibration {
    +using BuiltinOperatorKey = std::pair;
    +
    +using BuiltinOpsSet = std::unordered_set<
    +    BuiltinOperatorKey,
    +    op_resolver_hasher::OperatorKeyHasher>;
    +
    +template 
    +class BuiltinOpsMap
    +    : public std::unordered_map<
    +          BuiltinOperatorKey, T,
    +          op_resolver_hasher::OperatorKeyHasher> {};
    +
    +// An alias for |TfLiteRegistration.invoke|.
    +using KernelEvalFuncPtr = TfLiteStatus (*)(TfLiteContext*, TfLiteNode*);
    +
    +enum class OperatorTensorType { kNone, kInput, kOutput, kIntermediate };
    +
    +// Information about an operator in the TfLite graph.
    +struct OperatorInfo {
    +  int node_index;
    +  std::string name;
    +  BuiltinOperator builtin_op_code;
    +  bool is_custom_op;
    +  std::vector inputs;
    +  std::vector outputs;
    +  // Inputs that need to be logged.
    +  std::vector loggable_inputs;
    +  // Outputs that need to be logged.
    +  std::vector loggable_outputs;
    +  const TfLiteRegistration* registration;
    +};
    +
    +}  // namespace calibration
    +}  // namespace optimize
    +}  // namespace tflite
    +#endif  // TENSORFLOW_LITE_TOOLS_OPTIMIZE_CALIBRATION_COMMON_H_
    diff --git a/tensorflow/lite/tools/optimize/calibration_logger.h b/tensorflow/lite/tools/optimize/calibration_logger.h
    new file mode 100644
    index 0000000000..8fd380423a
    --- /dev/null
    +++ b/tensorflow/lite/tools/optimize/calibration_logger.h
    @@ -0,0 +1,85 @@
    +/* Copyright 2018 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_LITE_TOOLS_OPTIMIZE_CALIBRATION_LOGGER_H_
    +#define TENSORFLOW_LITE_TOOLS_OPTIMIZE_CALIBRATION_LOGGER_H_
    +
    +#include 
    +
    +#include "tensorflow/lite/c/c_api_internal.h"
    +
    +namespace tflite {
    +namespace optimize {
    +namespace calibration {
    +class MinMax {
    + public:
    +  void Update(const float* values, size_t tensor_size) {
    +    // TODO(shashishekhar): Really slow implementation, optimize
    +    if (tensor_size <= 0) return;
    +
    +    if (!has_values_) {
    +      min_ = max_ = values[0];
    +      has_values_ = true;
    +      return;
    +    }
    +
    +    // We are only logging absolute min/max here.
    +    // TODO(shashishekhar): Make it possible to use weighted/moving average.
    +    for (size_t i = 0; i < tensor_size; i++) {
    +      float val = values[i];
    +      if (min_ > val) {
    +        min_ = val;
    +      } else if (max_ < val) {
    +        max_ = val;
    +      }
    +    }
    +  }
    +
    +  bool HasValues() const { return has_values_; }
    +
    +  TfLiteStatus Get(float* min_val, float* max_val) const {
    +    if (!has_values_) return kTfLiteError;
    +    *min_val = min_;
    +    *max_val = max_;
    +    return kTfLiteOk;
    +  }
    +
    + private:
    +  bool has_values_;
    +  float min_, max_;
    +};
    +
    +// Captures min max values for tensors.
    +class Logger {
    + public:
    +  // Log the value for tensor at |tensor_index| which has |tensor_values|
    +  void LogTensorValue(int tensor_index, const float* tensor_values,
    +                      size_t tensor_size) {
    +    tensor_id_to_stats_map_[tensor_index].Update(tensor_values, tensor_size);
    +  }
    +
    +  // Returns a map from tensor_index -> observed min max values.
    +  const std::unordered_map& GetCalibrationValues() const {
    +    return tensor_id_to_stats_map_;
    +  }
    +
    + private:
    +  std::unordered_map tensor_id_to_stats_map_;
    +};
    +
    +}  // namespace calibration
    +}  // namespace optimize
    +}  // namespace tflite
    +
    +#endif  // TENSORFLOW_LITE_TOOLS_OPTIMIZE_CALIBRATION_LOGGER_H_
    diff --git a/tensorflow/lite/tools/optimize/calibration_reader.cc b/tensorflow/lite/tools/optimize/calibration_reader.cc
    new file mode 100644
    index 0000000000..b01a62bd6c
    --- /dev/null
    +++ b/tensorflow/lite/tools/optimize/calibration_reader.cc
    @@ -0,0 +1,55 @@
    +/* Copyright 2018 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/lite/tools/optimize/calibration_reader.h"
    +
    +#include "absl/memory/memory.h"
    +
    +namespace tflite {
    +namespace optimize {
    +namespace calibration {
    +TfLiteStatus CalibrationReader::GetTensorStatsAsMap(
    +    std::unordered_map* tensor_id_to_stats_map) const {
    +  tensor_id_to_stats_map->clear();
    +  for (const auto& tensorid_stat : logger_->GetCalibrationValues()) {
    +    auto minmax = tensorid_stat.second;
    +    CalibrationReader::CalibrationStats stats;
    +    TF_LITE_ENSURE_STATUS(minmax.Get(&stats.min, &stats.max));
    +    tensor_id_to_stats_map->insert({tensorid_stat.first, stats});
    +  }
    +
    +  return kTfLiteOk;
    +}
    +
    +TfLiteStatus CalibrationReader::AddCalibrationToModel(ModelT* model) const {
    +  if (!model || model->subgraphs.empty()) {
    +    return kTfLiteError;
    +  }
    +  const auto& subgraph = model->subgraphs[0];
    +  for (const auto& tensorid_stat : logger_->GetCalibrationValues()) {
    +    auto minmax = tensorid_stat.second;
    +    float min, max;
    +    TF_LITE_ENSURE_STATUS(minmax.Get(&min, &max));
    +    auto quant_params = absl::make_unique();
    +    quant_params->min.push_back(min);
    +    quant_params->max.push_back(max);
    +    subgraph->tensors[tensorid_stat.first]->quantization =
    +        std::move(quant_params);
    +  }
    +
    +  return kTfLiteOk;
    +}
    +}  // namespace calibration
    +}  // namespace optimize
    +}  // namespace tflite
    diff --git a/tensorflow/lite/tools/optimize/calibration_reader.h b/tensorflow/lite/tools/optimize/calibration_reader.h
    new file mode 100644
    index 0000000000..af0da1bb38
    --- /dev/null
    +++ b/tensorflow/lite/tools/optimize/calibration_reader.h
    @@ -0,0 +1,55 @@
    +/* Copyright 2018 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_LITE_TOOLS_OPTIMIZE_CALIBRATION_READER_H_
    +#define TENSORFLOW_LITE_TOOLS_OPTIMIZE_CALIBRATION_READER_H_
    +
    +#include 
    +
    +#include "tensorflow/lite/context.h"
    +#include "tensorflow/lite/model.h"
    +#include "tensorflow/lite/tools/optimize/calibration_logger.h"
    +
    +namespace tflite {
    +namespace optimize {
    +namespace calibration {
    +// Warning: This is not a public API and subject to change.
    +//
    +// Reads calibrator data collected by running the interpreter through
    +// a calibration set.
    +class CalibrationReader {
    + public:
    +  struct CalibrationStats {
    +    float min;
    +    float max;
    +  };
    +  explicit CalibrationReader(const Logger* logger) : logger_(logger) {}
    +
    +  // Gets a map from tensor index to recorded calibration values.
    +  virtual TfLiteStatus GetTensorStatsAsMap(
    +      std::unordered_map* tensor_id_to_stats_map) const;
    +
    +  // Annotates the tensors in the given model with statistics captured during
    +  // calibration.
    +  virtual TfLiteStatus AddCalibrationToModel(ModelT* model) const;
    +
    +  virtual ~CalibrationReader() {}
    +
    + private:
    +  const Logger* logger_;
    +};
    +}  // namespace calibration
    +}  // namespace optimize
    +}  // namespace tflite
    +#endif  // TENSORFLOW_LITE_TOOLS_OPTIMIZE_CALIBRATION_READER_H_
    diff --git a/tensorflow/lite/tools/optimize/calibrator.cc b/tensorflow/lite/tools/optimize/calibrator.cc
    new file mode 100644
    index 0000000000..0e817f9346
    --- /dev/null
    +++ b/tensorflow/lite/tools/optimize/calibrator.cc
    @@ -0,0 +1,345 @@
    +/* Copyright 2018 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/lite/tools/optimize/calibrator.h"
    +
    +#include 
    +#include 
    +#include 
    +#include 
    +#include 
    +#include 
    +
    +#include "absl/memory/memory.h"
    +#include "tensorflow/lite/core/api/error_reporter.h"
    +#include "tensorflow/lite/core/api/op_resolver.h"
    +#include "tensorflow/lite/interpreter.h"
    +#include "tensorflow/lite/kernels/register.h"
    +#include "tensorflow/lite/model.h"
    +#include "tensorflow/lite/op_resolver.h"
    +#include "tensorflow/lite/schema/schema_generated.h"
    +#include "tensorflow/lite/string_util.h"
    +#include "tensorflow/lite/tools/optimize/calibration_common.h"
    +#include "tensorflow/lite/tools/optimize/calibration_logger.h"
    +#include "tensorflow/lite/tools/optimize/calibration_reader.h"
    +#include "tensorflow/lite/tools/optimize/logging_op_resolver.h"
    +#include "tensorflow/lite/tools/optimize/node_info_delegate.h"
    +
    +namespace tflite {
    +namespace optimize {
    +namespace calibration {
    +
    +namespace {
    +
    +// Calibrator is used to hold information that can be accessed during kernel
    +// invocations.
    +// TfLite kernel invocations are C functions and cannot look at the global
    +// structure of the graph. Calibrator allows the kernel invoke functions to
    +// access the global structure of graph and know which node is currently being
    +// executed. This also allows us to write a simple kernel invoke wrapper
    +// (see LoggingEval) that can work for most builtin ops.
    +class Calibrator {
    + public:
    +  Calibrator(const std::unordered_map&
    +                 node_ptr_opinfo_map,
    +             std::unique_ptr logging_op_resolver)
    +      : node_ptr_opinfo_map_(node_ptr_opinfo_map),
    +        logging_op_resolver_(std::move(logging_op_resolver)) {
    +    logger_ = absl::make_unique();
    +  }
    +
    +  // Returns the wrapped kernel invoke function |TfLiteRegistration.invoke|.
    +  KernelEvalFuncPtr GetKernelInvoke(const TfLiteNode* node) const;
    +
    +  // Gets the instance of logger associated with the current context.
    +  Logger* GetLogger() const { return logger_.get(); }
    +
    +  // Gets the operator information about the given TfLiteNode.
    +  const OperatorInfo& GetOpInfo(const TfLiteNode* node) const {
    +    return node_ptr_opinfo_map_.at(node);
    +  }
    +
    + private:
    +  std::unordered_map node_ptr_opinfo_map_;
    +  std::unique_ptr logging_op_resolver_;
    +  const std::unordered_map index_opinfo_;
    +  std::unique_ptr logger_;
    +};
    +
    +KernelEvalFuncPtr Calibrator::GetKernelInvoke(const TfLiteNode* node) const {
    +  auto op_info = node_ptr_opinfo_map_.at(node);
    +  return logging_op_resolver_->GetWrappedKernelInvoke(op_info.builtin_op_code,
    +                                                      1);
    +}
    +
    +// A registry of |Calibrator| objects per |TfLiteContext|.
    +// This global registry is needed to access |Calibrator| objects in the kernel
    +// invoke functions i.e. |TfLiteRegistration.invoke|.
    +// Kernel invoke functions are C functions that have limited access to
    +// |TfLiteContext|. Kernel invoke functions don't have access to global state of
    +// graph. That means during a kernel invocation, the function cannot know which
    +// node it was invoked for. E.g. in case of a model with |Conv| op at two
    +// locations, there is no easy way for the Conv.invoke function to disambiguate
    +// the calls.
    +//
    +// For calibration we solve this problem by creating a map of calibrators
    +// per |TfLiteContext|. This map is |GlobalCalibrationRegistry|.
    +//
    +// This registry is then accessed using a global getter function:
    +// |GetCalibratorRegistry|.
    +// E.g.
    +// TfLiteStatus SomeKernelInvokeFn(TfLiteContext* context, TfLiteNode* node) {
    +//   .... code ....
    +//   auto registry = GetCalibratorRegistry();
    +//   auto calibrator = registry->GetCalibrator(context);
    +//   ..... code ....
    +//  }
    +//
    +// This way the kernel invoke functions can get the access to the Calibrator
    +// object associated with the |TfLiteContext|.
    +class GlobalCalibratorRegistry {
    + public:
    +  // Get the |Calibrator| associated with given context, returns null if no
    +  // calibrator is associated with the given context.
    +  Calibrator* GetCalibrator(const TfLiteContext* context) const {
    +    if (calibrator_registry_.find(context) == calibrator_registry_.cend()) {
    +      return nullptr;
    +    }
    +    return calibrator_registry_.at(context).get();
    +  }
    +
    +  // Removes the association between calibrator and context.
    +  // Note: This deletes the calibrator as well.
    +  void RemoveCalibrator(const TfLiteContext* context) {
    +    calibrator_registry_.erase(context);
    +  }
    +
    +  // Creates an instance of |Calibrator|.
    +  // Registry owns the |Calibrator| object which can be deleted by calling
    +  // |RemoveCalibrator|.
    +  TfLiteStatus CreateCalibrator(
    +      const TfLiteContext* context,
    +      const std::unordered_map& node_to_opinfo,
    +      std::unique_ptr logging_op_resolver,
    +      Calibrator** calibrator_ptr, ErrorReporter* reporter) {
    +    if (calibrator_registry_.find(context) != calibrator_registry_.cend()) {
    +      reporter->Report(
    +          "Failed to create calibrator, context already registered.");
    +      return kTfLiteError;
    +    }
    +    std::unique_ptr calibrator = absl::make_unique(
    +        node_to_opinfo, std::move(logging_op_resolver));
    +    calibrator_registry_[context] = std::move(calibrator);
    +    *calibrator_ptr = calibrator_registry_.at(context).get();
    +    return kTfLiteOk;
    +  }
    +
    + private:
    +  std::unordered_map>
    +      calibrator_registry_;
    +};
    +
    +GlobalCalibratorRegistry* GetCalibratorRegistry() {
    +  static GlobalCalibratorRegistry* registry = new GlobalCalibratorRegistry();
    +  return registry;
    +}
    +
    +// A wrapper implementation for |TfLiteRegistration.invoke| that logs inputs,
    +// invokes the wrapped implementation and then logs the outputs.
    +TfLiteStatus LoggingEval(TfLiteContext* context, TfLiteNode* node) {
    +  Calibrator* calibrator = GetCalibratorRegistry()->GetCalibrator(context);
    +
    +  if (!calibrator) {
    +    context->ReportError(context, "No calibrator found for context.");
    +    return kTfLiteError;
    +  }
    +
    +  auto kernel_invoke = calibrator->GetKernelInvoke(node);
    +  auto logger = calibrator->GetLogger();
    +  auto op_info = calibrator->GetOpInfo(node);
    +
    +  for (int i : op_info.loggable_inputs) {
    +    auto tensor = context->tensors[i];
    +    logger->LogTensorValue(i, tensor.data.f, tensor.bytes / sizeof(float));
    +  }
    +
    +  auto status = kernel_invoke(context, node);
    +  // TODO(shashishekhar): An intermediate tensor in graph will get logged twice
    +  // once as an input and second time as output. This doesn't change the min max
    +  // values but is inefficient.
    +  // Using moving average will also break this.
    +
    +  for (int i : op_info.loggable_outputs) {
    +    auto tensor = context->tensors[i];
    +    logger->LogTensorValue(i, tensor.data.f, tensor.bytes / sizeof(float));
    +  }
    +
    +  return status;
    +}
    +
    +// Returns the loggable tensors. Not all inputs and outputs need to be logged.
    +// For example, const weight tensors which have buffers associated with them
    +// don't need to be logged.
    +std::vector GetLoggableTensorIndices(
    +    const std::vector& tensor_indices,
    +    const flatbuffers::Vector>* tensors,
    +    const flatbuffers::Vector>* tensor_buffers) {
    +  std::vector loggable;
    +  for (auto tensor_index : tensor_indices) {
    +    auto tensor = tensors->Get(tensor_index);
    +    auto buffer_index = tensor->buffer();
    +    bool has_no_buffer =
    +        buffer_index == 0 || (tensor_buffers->Get(buffer_index) == nullptr);
    +    if (has_no_buffer && tensor->type() == tflite::TensorType_FLOAT32) {
    +      loggable.push_back(tensor_index);
    +    }
    +  }
    +  return loggable;
    +}
    +
    +// Creates a mapping between the static model graph and the runtime TfLiteNode*
    +// nodes in the graph for the given context.
    +// This is done by querying the TfLiteContext for node and registrations using
    +// the |NodeInfoDelegateObserver|.
    +TfLiteStatus GetNodeOpInfoMapAndContext(
    +    const std::unordered_map& node_to_opinfo,
    +    tflite::Interpreter* const interpreter,
    +    std::unordered_map* node_ptr_opinfo_map,
    +    const TfLiteContext** context
    +
    +) {
    +  NodeInfoDelegateObserver delegate_observer(node_to_opinfo,
    +                                             node_ptr_opinfo_map);
    +  NodeInfoDelegateParams delegate_params;
    +  delegate_params.delegate_observer = &delegate_observer;
    +  TfLiteDelegate logging_delegate = CreateNodeInfoDelegate(&delegate_params);
    +
    +  auto modify_status = interpreter->ModifyGraphWithDelegate(&logging_delegate);
    +  if (modify_status != kTfLiteOk) {
    +    return kTfLiteError;
    +  }
    +  *context = delegate_observer.GetContext();
    +  return kTfLiteOk;
    +}
    +
    +string GetOpName(const tflite::OperatorCode& opcode) {
    +  if (opcode.custom_code() != nullptr) {
    +    return opcode.custom_code()->str();
    +  }
    +  return tflite::EnumNamesBuiltinOperator()[opcode.builtin_code()];
    +}
    +
    +// A |CalibrationReader| that owns the Calibrator.
    +class Reader : public CalibrationReader {
    + public:
    +  Reader(const TfLiteContext* context, const Logger* logger)
    +      : CalibrationReader(logger), context_(context) {}
    +
    +  ~Reader() override { GetCalibratorRegistry()->RemoveCalibrator(context_); }
    +
    + private:
    +  const TfLiteContext* context_;
    +};
    +
    +}  // namespace
    +
    +TfLiteStatus BuildLoggingInterpreter(
    +    const FlatBufferModel& model, const OpResolver& op_resolver,
    +    std::unique_ptr* interpreter,
    +    std::unique_ptr* calibration_reader) {
    +  auto tflite_model = model.GetModel();
    +  auto subgraphs = tflite_model->subgraphs();
    +  auto tensor_buffers = tflite_model->buffers();
    +
    +  if (subgraphs->size() != 1) {
    +    model.error_reporter()->Report(
    +        "Only models with a single subgraph are supported, model had %d "
    +        "subgraphs",
    +        subgraphs->size());
    +    return kTfLiteError;
    +  }
    +
    +  // Populate the node index to operator info map.
    +  // We want to collect this information so we can use it during runtime to
    +  // log details of which inputs and outputs.
    +  // At runtime TFLite kernel invoke functions can only look into their
    +  // own node in the graph (TFLiteNode*) and some limited context information.
    +  auto primary_subgraph = subgraphs->Get(0);
    +  auto operator_codes = tflite_model->operator_codes();
    +  auto operators = primary_subgraph->operators();
    +  auto tensors = primary_subgraph->tensors();
    +  std::unordered_map node_to_opinfo;
    +  BuiltinOpsSet op_and_versions;
    +
    +  for (size_t i = 0; i < operators->size(); i++) {
    +    OperatorInfo op_info;
    +    op_info.node_index = i;
    +    auto op = operators->Get(i);
    +    auto operator_code = operator_codes->Get(op->opcode_index());
    +    op_info.builtin_op_code = operator_code->builtin_code();
    +    op_info.name = GetOpName(*operator_code);
    +    op_info.is_custom_op = operator_code->custom_code() != nullptr;
    +
    +    auto op_inputs = op->inputs();
    +    auto op_outputs = op->outputs();
    +    op_info.inputs = std::vector(op_inputs->begin(), op_inputs->end());
    +    op_info.outputs = std::vector(op_outputs->begin(), op_outputs->end());
    +    op_info.loggable_inputs =
    +        GetLoggableTensorIndices(op_info.inputs, tensors, tensor_buffers);
    +    op_info.loggable_outputs =
    +        GetLoggableTensorIndices(op_info.outputs, tensors, tensor_buffers);
    +    if (!op_info.is_custom_op) {
    +      op_info.registration = op_resolver.FindOp(operator_code->builtin_code(),
    +                                                operator_code->version());
    +    } else {
    +      op_info.registration =
    +          op_resolver.FindOp(op_info.name.c_str(), operator_code->version());
    +    }
    +    node_to_opinfo[i] = op_info;
    +    op_and_versions.insert({op_info.builtin_op_code, operator_code->version()});
    +  }
    +
    +  // Prepare the logging op resolver to use |LoggingEval| for kernel
    +  // invocations.
    +  auto logging_op_resolver = absl::make_unique(
    +      op_and_versions, op_resolver, LoggingEval);
    +  tflite::InterpreterBuilder(model, *logging_op_resolver)(interpreter);
    +
    +  if (!(*interpreter)) {
    +    model.error_reporter()->Report("Failed to construct interpreter");
    +    return kTfLiteError;
    +  }
    +
    +  // Compute the mapping between runtime and static graph structure, i.e.
    +  // (TfLiteContext, TfLiteNode) -> OperatorInfo
    +  std::unordered_map node_ptr_opinfo_map;
    +  const TfLiteContext* context = nullptr;
    +  GetNodeOpInfoMapAndContext(node_to_opinfo, interpreter->get(),
    +                             &node_ptr_opinfo_map, &context);
    +
    +  Calibrator* calibrator = nullptr;
    +  // Register a calibrator object for the context. This can be accessed
    +  // during invocations by the logging kernels.
    +  TF_LITE_ENSURE_STATUS(GetCalibratorRegistry()->CreateCalibrator(
    +      context, node_ptr_opinfo_map, std::move(logging_op_resolver), &calibrator,
    +      model.error_reporter()));
    +  *calibration_reader = std::unique_ptr(
    +      new Reader(context, calibrator->GetLogger()));
    +
    +  return kTfLiteOk;
    +}
    +
    +}  // namespace calibration
    +}  // namespace optimize
    +}  // namespace tflite
    diff --git a/tensorflow/lite/tools/optimize/calibrator.h b/tensorflow/lite/tools/optimize/calibrator.h
    new file mode 100644
    index 0000000000..ab3cb27eb7
    --- /dev/null
    +++ b/tensorflow/lite/tools/optimize/calibrator.h
    @@ -0,0 +1,64 @@
    +/* Copyright 2018 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_LITE_TOOLS_OPTIMIZE_CALIBRATOR_H_
    +#define TENSORFLOW_LITE_TOOLS_OPTIMIZE_CALIBRATOR_H_
    +
    +#include 
    +
    +#include "flatbuffers/flatbuffers.h"  // TF:flatbuffers
    +#include "tensorflow/lite/core/api/op_resolver.h"
    +#include "tensorflow/lite/model.h"
    +#include "tensorflow/lite/tools/optimize/calibration_reader.h"
    +
    +namespace tflite {
    +namespace optimize {
    +namespace calibration {
    +
    +// Warning: This is not a public API and subject to change.
    +
    +// Builds a interpreter that logs the calibration data in memory.
    +// The calibration data can be recovered using |calibration_reader|.
    +//
    +// Sample usage:
    +// std::unique_ptr interpreter;
    +// std::unique_ptr calibration_reader;
    +// BuiltinOpResolver resolver = ...
    +// FlatBufferModel model = ..
    +//
    +// BuildLoggingInterpreter(model, resolver, &interpreter,
    +//  &calibration_reader);
    +//
    +//
    +// * Allocate tensors...
    +// * Call interpreter->invoke on calibration dataset.
    +//
    +// Calibration data can be read either directly by calling
    +// std::unordered_map> tensor_index_to_stats;
    +// calibration_reader->GetTensorStatsAsMap(&tensor_index_to_stats);
    +//
    +// or adding calibration data to model itself.
    +// ModelT * original_floating_point_model = ...
    +// calibration_reader->AddCalibrationToModel(original_floating_point_model);
    +//
    +TfLiteStatus BuildLoggingInterpreter(
    +    const FlatBufferModel& model, const OpResolver& op_resolver,
    +    std::unique_ptr* interpreter,
    +    std::unique_ptr* calibration_reader);
    +
    +}  // namespace calibration
    +}  // namespace optimize
    +}  // namespace tflite
    +
    +#endif  // TENSORFLOW_LITE_TOOLS_OPTIMIZE_CALIBRATOR_H_
    diff --git a/tensorflow/lite/tools/optimize/calibrator_test.cc b/tensorflow/lite/tools/optimize/calibrator_test.cc
    new file mode 100644
    index 0000000000..bbbcc70fae
    --- /dev/null
    +++ b/tensorflow/lite/tools/optimize/calibrator_test.cc
    @@ -0,0 +1,189 @@
    +/* Copyright 2018 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 
    +
    +#include 
    +#include 
    +#include "tensorflow/lite/kernels/register.h"
    +#include "tensorflow/lite/model.h"
    +#include "tensorflow/lite/tools/optimize/calibrator.h"
    +
    +namespace tflite {
    +namespace optimize {
    +namespace calibration {
    +namespace {
    +
    +TEST(CalibratorTest, CalibrationStatsAreCollected) {
    +  auto model = FlatBufferModel::BuildFromFile(
    +      "third_party/tensorflow/lite/testdata/multi_add.bin");
    +  ASSERT_TRUE(model);
    +  std::unique_ptr interpreter;
    +  std::unique_ptr reader;
    +  auto status = BuildLoggingInterpreter(
    +      *model, ops::builtin::BuiltinOpResolver{}, &interpreter, &reader);
    +  EXPECT_EQ(kTfLiteOk, status);
    +
    +  ASSERT_TRUE(interpreter);
    +  ASSERT_TRUE(reader);
    +  std::unordered_map stats;
    +  status = reader->GetTensorStatsAsMap(&stats);
    +  EXPECT_EQ(kTfLiteOk, status);
    +  EXPECT_TRUE(stats.empty());
    +
    +  status = interpreter->AllocateTensors();
    +  ASSERT_EQ(kTfLiteOk, status);
    +  // Model does the following:
    +  // 0        1       2        3
    +  // |        |__ ____|        |
    +  // |           |             |
    +  // |          Add(tensor:4)  |
    +  // |____ ______|______ ______|
    +  //      |             |
    +  //      Add          Add
    +  //      |             |
    +  //    Output:5      Output:6
    +
    +  const size_t tensor_size = 1 * 8 * 8 * 3;
    +
    +  std::vector ones(tensor_size, 1.0f);
    +  // Fill input tensor i with i+1, i.e. input[0] = 1.0f, input[1] = 2.0f,
    +  // input[2] = 3.0f
    +
    +  for (size_t i = 0; i < interpreter->inputs().size(); i++) {
    +    int input_tensor_idx = interpreter->inputs()[i];
    +    TfLiteTensor* tensor = interpreter->tensor(input_tensor_idx);
    +    ASSERT_EQ(tensor->bytes, tensor_size * sizeof(float));
    +    for (size_t j = 0; j < tensor_size; j++) {
    +      tensor->data.f[j] = i + 1;
    +    }
    +  }
    +  status = interpreter->Invoke();
    +  ASSERT_EQ(kTfLiteOk, status);
    +  const float eps = 1e-6f;
    +  // Verify that tensor 5: is 6
    +  // Verify that tensor 6: is 9
    +  TfLiteTensor* tensor = interpreter->tensor(interpreter->outputs()[0]);
    +  for (size_t i = 0; i < tensor_size; i++) {
    +    EXPECT_NEAR(tensor->data.f[i], 6.0f, eps);
    +  }
    +  tensor = interpreter->tensor(interpreter->outputs()[1]);
    +  for (size_t i = 0; i < tensor_size; i++) {
    +    EXPECT_NEAR(tensor->data.f[i], 9.0f, eps);
    +  }
    +
    +  // Verify that min max of tensors.
    +  status = reader->GetTensorStatsAsMap(&stats);
    +  EXPECT_EQ(kTfLiteOk, status);
    +  EXPECT_EQ(7, stats.size());
    +  // Check inputs
    +  for (int tensor_idx = 0; tensor_idx < 4; tensor_idx++) {
    +    EXPECT_NEAR(stats.at(tensor_idx).min, tensor_idx + 1, eps);
    +    EXPECT_NEAR(stats.at(tensor_idx).max, tensor_idx + 1, eps);
    +  }
    +  // Check tensor 4 max.
    +  EXPECT_NEAR(stats.at(4).min, 5, eps);
    +  EXPECT_NEAR(stats.at(4).max, 5, eps);
    +
    +  // Check outputs
    +  EXPECT_NEAR(stats.at(5).min, 6, eps);
    +  EXPECT_NEAR(stats.at(5).max, 6, eps);
    +
    +  EXPECT_NEAR(stats.at(6).min, 9, eps);
    +  EXPECT_NEAR(stats.at(6).max, 9, eps);
    +}
    +
    +TEST(CalibratorTest, MultipleInvokes) {
    +  auto model = FlatBufferModel::BuildFromFile(
    +      "third_party/tensorflow/lite/testdata/multi_add.bin");
    +  ASSERT_TRUE(model);
    +  std::unique_ptr interpreter;
    +  std::unique_ptr reader;
    +  auto status = BuildLoggingInterpreter(
    +      *model, ops::builtin::BuiltinOpResolver{}, &interpreter, &reader);
    +  EXPECT_EQ(kTfLiteOk, status);
    +
    +  ASSERT_TRUE(interpreter);
    +  ASSERT_TRUE(reader);
    +  status = interpreter->AllocateTensors();
    +
    +  EXPECT_EQ(kTfLiteOk, status);
    +  const size_t tensor_size = 1 * 8 * 8 * 3;
    +  // Fill input tensor i with i+1, i.e. input[0] = 1.0f, input[1] = 2.0f,
    +  // input[2] = 3.0f
    +
    +  for (size_t i = 0; i < interpreter->inputs().size(); i++) {
    +    int input_tensor_idx = interpreter->inputs()[i];
    +    TfLiteTensor* tensor = interpreter->tensor(input_tensor_idx);
    +    ASSERT_EQ(tensor->bytes, tensor_size * sizeof(float));
    +    for (size_t j = 0; j < tensor_size; j++) {
    +      tensor->data.f[j] = i + 1;
    +    }
    +  }
    +  status = interpreter->Invoke();
    +  ASSERT_EQ(kTfLiteOk, status);
    +  const float eps = 1e-6f;
    +  // Verify that min max of tensors.
    +  std::unordered_map stats;
    +  status = reader->GetTensorStatsAsMap(&stats);
    +  EXPECT_EQ(kTfLiteOk, status);
    +  EXPECT_EQ(7, stats.size());
    +  const float expected_values[7] = {
    +      1.0f,  // input 0
    +      2.0f,  // input 1
    +      3.0f,  // input 2
    +      4.0f,  // input 3
    +      5.0f,  // Add(1, 2)
    +      6.0f,  // Output 5: Add(0, Add(1,2))
    +      9.0f,  // Output 6: Add(Add(1,2), 3)
    +  };
    +  for (int tensor_idx = 0; tensor_idx < 7; tensor_idx++) {
    +    EXPECT_NEAR(stats.at(tensor_idx).min, expected_values[tensor_idx], eps);
    +    EXPECT_NEAR(stats.at(tensor_idx).max, expected_values[tensor_idx], eps);
    +  }
    +  // Set input[0][0] = 1.5 and input[0][1] = 0.5 this should change the values
    +  // only for input[0] and tensor 4 and ouputs 5, 6.
    +  TfLiteTensor* input0 = interpreter->tensor(0);
    +  input0->data.f[0] = 1.5f;
    +  input0->data.f[1] = 0.5f;
    +  status = interpreter->Invoke();
    +  ASSERT_EQ(kTfLiteOk, status);
    +  status = reader->GetTensorStatsAsMap(&stats);
    +  EXPECT_EQ(kTfLiteOk, status);
    +  EXPECT_EQ(7, stats.size());
    +  EXPECT_NEAR(stats.at(0).min, 0.5f, eps);
    +  EXPECT_NEAR(stats.at(0).max, 1.5f, eps);
    +
    +  for (int tensor_idx = 1; tensor_idx < 5; tensor_idx++) {
    +    EXPECT_NEAR(stats.at(tensor_idx).min, expected_values[tensor_idx], eps);
    +    EXPECT_NEAR(stats.at(tensor_idx).max, expected_values[tensor_idx], eps);
    +  }
    +
    +  EXPECT_NEAR(stats.at(5).min, 5.5f, eps);
    +  EXPECT_NEAR(stats.at(5).max, 6.5f, eps);
    +
    +  EXPECT_NEAR(stats.at(6).min, 9.0f, eps);
    +  EXPECT_NEAR(stats.at(6).max, 9.0f, eps);
    +}
    +
    +}  // namespace
    +}  // namespace calibration
    +}  // namespace optimize
    +}  // namespace tflite
    +
    +int main(int argc, char** argv) {
    +  // On Linux, add: FLAGS_logtostderr = true;
    +  ::testing::InitGoogleTest(&argc, argv);
    +  return RUN_ALL_TESTS();
    +}
    diff --git a/tensorflow/lite/tools/optimize/logging_op_resolver.cc b/tensorflow/lite/tools/optimize/logging_op_resolver.cc
    new file mode 100644
    index 0000000000..7633ebb8dd
    --- /dev/null
    +++ b/tensorflow/lite/tools/optimize/logging_op_resolver.cc
    @@ -0,0 +1,61 @@
    +/* Copyright 2018 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/lite/tools/optimize/logging_op_resolver.h"
    +
    +#include "absl/memory/memory.h"
    +
    +namespace tflite {
    +namespace optimize {
    +namespace calibration {
    +
    +LoggingOpResolver::LoggingOpResolver(const BuiltinOpsSet& ops_to_replace,
    +                                     const OpResolver& base_resolver,
    +                                     KernelEvalFuncPtr logging_eval_fn) {
    +  for (const auto& op_and_version : ops_to_replace) {
    +    const TfLiteRegistration* base_registration =
    +        base_resolver.FindOp(op_and_version.first, op_and_version.second);
    +    BuiltinOperatorKey key = op_and_version;
    +    builtin_op_evalfn_map_[key] = base_registration->invoke;
    +    std::unique_ptr logging_registation =
    +        absl::make_unique(*base_registration);
    +    logging_registation->invoke = logging_eval_fn;
    +    builtin_op_registration_map_[key] = std::move(logging_registation);
    +  }
    +}
    +
    +const TfLiteRegistration* LoggingOpResolver::FindOp(BuiltinOperator op,
    +                                                    int version) const {
    +  BuiltinOperatorKey key = {op, version};
    +  if (builtin_op_registration_map_.find(key) !=
    +      builtin_op_registration_map_.end()) {
    +    return builtin_op_registration_map_.at(key).get();
    +  }
    +
    +  return nullptr;
    +}
    +
    +KernelEvalFuncPtr LoggingOpResolver::GetWrappedKernelInvoke(BuiltinOperator op,
    +                                                            int version) const {
    +  return builtin_op_evalfn_map_.at({op, version});
    +}
    +
    +const TfLiteRegistration* LoggingOpResolver::FindOp(const char* op,
    +                                                    int version) const {
    +  // TODO(b/121374947): Support custom ops as well.
    +  return nullptr;
    +}
    +}  // namespace calibration
    +}  // namespace optimize
    +}  // namespace tflite
    diff --git a/tensorflow/lite/tools/optimize/logging_op_resolver.h b/tensorflow/lite/tools/optimize/logging_op_resolver.h
    new file mode 100644
    index 0000000000..58a3a0fe3c
    --- /dev/null
    +++ b/tensorflow/lite/tools/optimize/logging_op_resolver.h
    @@ -0,0 +1,59 @@
    +/* Copyright 2018 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_LITE_TOOLS_OPTIMIZE_LOGGING_OP_RESOLVER_H_
    +#define TENSORFLOW_LITE_TOOLS_OPTIMIZE_LOGGING_OP_RESOLVER_H_
    +
    +#include 
    +#include 
    +
    +#include "tensorflow/lite/core/api/op_resolver.h"
    +#include "tensorflow/lite/mutable_op_resolver.h"
    +#include "tensorflow/lite/op_resolver.h"
    +#include "tensorflow/lite/tools/optimize/calibration_common.h"
    +
    +namespace tflite {
    +namespace optimize {
    +namespace calibration {
    +// A resolver that replaces the kernel invocations with a wrapper
    +// eval function.
    +class LoggingOpResolver : public OpResolver {
    + public:
    +  // Creates an instance of |LoggingOpResolver|.
    +  // All |TfLiteRegistration.invoke| functions are replaced by
    +  // |logging_eval_fn|.
    +  // TODO(shashishekhar): This interface needs to change for custom ops and
    +  // BuiltinOps that need special logging implementations.
    +  LoggingOpResolver(const BuiltinOpsSet& ops_to_replace,
    +                    const OpResolver& base_resolver,
    +                    KernelEvalFuncPtr logging_eval_fn);
    +
    +  const TfLiteRegistration* FindOp(BuiltinOperator op,
    +                                   int version) const override;
    +
    +  KernelEvalFuncPtr GetWrappedKernelInvoke(BuiltinOperator op,
    +                                           int version) const;
    +  const TfLiteRegistration* FindOp(const char* op, int version) const override;
    +
    + private:
    +  BuiltinOpsMap>
    +      builtin_op_registration_map_;
    +  BuiltinOpsMap builtin_op_evalfn_map_;
    +};
    +
    +}  // namespace calibration
    +}  // namespace optimize
    +}  // namespace tflite
    +
    +#endif  // TENSORFLOW_LITE_TOOLS_OPTIMIZE_LOGGING_OP_RESOLVER_H_
    diff --git a/tensorflow/lite/tools/optimize/logging_op_resolver_test.cc b/tensorflow/lite/tools/optimize/logging_op_resolver_test.cc
    new file mode 100644
    index 0000000000..18c29abec6
    --- /dev/null
    +++ b/tensorflow/lite/tools/optimize/logging_op_resolver_test.cc
    @@ -0,0 +1,141 @@
    +/* Copyright 2018 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/lite/tools/optimize/logging_op_resolver.h"
    +#include 
    +#include 
    +#include "tensorflow/lite/mutable_op_resolver.h"
    +
    +namespace tflite {
    +namespace optimize {
    +namespace calibration {
    +namespace {
    +
    +TfLiteStatus ConvPrepare(TfLiteContext* context, TfLiteNode* node) {
    +  return kTfLiteOk;
    +}
    +
    +TfLiteStatus ConvEval(TfLiteContext* context, TfLiteNode* node) {
    +  return kTfLiteOk;
    +}
    +
    +TfLiteStatus AddPrepare(TfLiteContext* context, TfLiteNode* node) {
    +  return kTfLiteOk;
    +}
    +
    +TfLiteStatus AddEval(TfLiteContext* context, TfLiteNode* node) {
    +  return kTfLiteOk;
    +}
    +
    +TfLiteStatus WrappingInvoke(TfLiteContext* context, TfLiteNode* node) {
    +  return kTfLiteOk;
    +}
    +
    +TEST(LoggingOpResolverTest, KernelInvokesAreReplaced) {
    +  MutableOpResolver base_resolver;
    +  TfLiteRegistration conv_registration = {
    +      .prepare = ConvPrepare,
    +      .invoke = ConvEval,
    +  };
    +  base_resolver.AddBuiltin(BuiltinOperator_CONV_2D, &conv_registration);
    +
    +  TfLiteRegistration add_registration = {
    +      .prepare = AddPrepare,
    +      .invoke = AddEval,
    +  };
    +  base_resolver.AddBuiltin(BuiltinOperator_ADD, &add_registration);
    +  BuiltinOpsSet ops_to_replace = {
    +      {BuiltinOperator_CONV_2D, /*version*/ 1},
    +      {BuiltinOperator_ADD, /*version*/ 1},
    +  };
    +
    +  LoggingOpResolver resolver(ops_to_replace, base_resolver, WrappingInvoke);
    +
    +  auto reg = resolver.FindOp(BuiltinOperator_CONV_2D, 1);
    +
    +  EXPECT_EQ(reg->builtin_code, BuiltinOperator_CONV_2D);
    +  EXPECT_TRUE(reg->prepare == ConvPrepare);
    +  EXPECT_TRUE(reg->invoke == WrappingInvoke);
    +
    +  reg = resolver.FindOp(BuiltinOperator_ADD, 1);
    +
    +  EXPECT_EQ(reg->builtin_code, BuiltinOperator_ADD);
    +  EXPECT_TRUE(reg->prepare == AddPrepare);
    +  EXPECT_TRUE(reg->invoke == WrappingInvoke);
    +}
    +
    +TEST(LoggingOpResolverTest, OriginalKernelInvokesAreRetained) {
    +  MutableOpResolver base_resolver;
    +  TfLiteRegistration conv_registration = {
    +      .prepare = ConvPrepare,
    +      .invoke = ConvEval,
    +  };
    +  base_resolver.AddBuiltin(BuiltinOperator_CONV_2D, &conv_registration);
    +
    +  TfLiteRegistration add_registration = {
    +      .prepare = AddPrepare,
    +      .invoke = AddEval,
    +  };
    +  base_resolver.AddBuiltin(BuiltinOperator_ADD, &add_registration);
    +  BuiltinOpsSet ops_to_replace = {
    +      {BuiltinOperator_CONV_2D, /*version*/ 1},
    +      {BuiltinOperator_ADD, /*version*/ 1},
    +  };
    +
    +  LoggingOpResolver resolver(ops_to_replace, base_resolver, WrappingInvoke);
    +  auto kernel_invoke =
    +      resolver.GetWrappedKernelInvoke(BuiltinOperator_CONV_2D, 1);
    +  EXPECT_TRUE(kernel_invoke == ConvEval);
    +  kernel_invoke = resolver.GetWrappedKernelInvoke(BuiltinOperator_ADD, 1);
    +  EXPECT_TRUE(kernel_invoke == AddEval);
    +}
    +
    +TEST(LoggingOpResolverTest, OnlyOpsInReplacementSetAreReplaces) {
    +  MutableOpResolver base_resolver;
    +  TfLiteRegistration conv_registration = {
    +      .prepare = ConvPrepare,
    +      .invoke = ConvEval,
    +  };
    +  base_resolver.AddBuiltin(BuiltinOperator_CONV_2D, &conv_registration);
    +
    +  TfLiteRegistration add_registration = {
    +      .prepare = AddPrepare,
    +      .invoke = AddEval,
    +  };
    +  base_resolver.AddBuiltin(BuiltinOperator_ADD, &add_registration);
    +  // Only replace conv2d
    +  BuiltinOpsSet ops_to_replace = {
    +      {BuiltinOperator_CONV_2D, /*version*/ 1},
    +  };
    +
    +  LoggingOpResolver resolver(ops_to_replace, base_resolver, WrappingInvoke);
    +  auto reg = resolver.FindOp(BuiltinOperator_CONV_2D, 1);
    +  EXPECT_EQ(reg->builtin_code, BuiltinOperator_CONV_2D);
    +  EXPECT_TRUE(reg->prepare == ConvPrepare);
    +  EXPECT_TRUE(reg->invoke == WrappingInvoke);
    +
    +  reg = resolver.FindOp(BuiltinOperator_ADD, 1);
    +  EXPECT_EQ(nullptr, reg);
    +}
    +
    +}  // namespace
    +}  // namespace calibration
    +}  // namespace optimize
    +}  // namespace tflite
    +
    +int main(int argc, char** argv) {
    +  // On Linux, add: FLAGS_logtostderr = true;
    +  ::testing::InitGoogleTest(&argc, argv);
    +  return RUN_ALL_TESTS();
    +}
    diff --git a/tensorflow/lite/tools/optimize/node_info_delegate.cc b/tensorflow/lite/tools/optimize/node_info_delegate.cc
    new file mode 100644
    index 0000000000..ccaa69373f
    --- /dev/null
    +++ b/tensorflow/lite/tools/optimize/node_info_delegate.cc
    @@ -0,0 +1,66 @@
    +/* Copyright 2018 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/lite/tools/optimize/node_info_delegate.h"
    +
    +namespace tflite {
    +namespace optimize {
    +namespace calibration {
    +
    +namespace {
    +// The prepare function for delegate that forwards the prepare call to the
    +// delegate observer in node info delegate params.
    +// The function simply calls a delegate observer OnDelegatePrepareMethod.
    +TfLiteStatus NodeInfoDelegatePrepare(TfLiteContext* context,
    +                                     TfLiteDelegate* delegate) {
    +  if (delegate == nullptr) return TfLiteStatus::kTfLiteError;
    +
    +  NodeInfoDelegateParams* params =
    +      reinterpret_cast(delegate->data_);
    +  return params->delegate_observer->OnDelegatePrepareCalled(context);
    +}
    +}  // namespace
    +
    +TfLiteDelegate CreateNodeInfoDelegate(NodeInfoDelegateParams* params) {
    +  return {.data_ = params,
    +          .Prepare = NodeInfoDelegatePrepare,
    +          .CopyFromBufferHandle = nullptr,
    +          .CopyToBufferHandle = nullptr,
    +          .FreeBufferHandle = nullptr};
    +}
    +
    +TfLiteStatus NodeInfoDelegateObserver::OnDelegatePrepareCalled(
    +    TfLiteContext* context) {
    +  context_ = context;
    +  const size_t num_nodes = node_index_opinfo_map_.size();
    +  for (size_t node_index = 0; node_index < num_nodes; node_index++) {
    +    TfLiteNode* node = nullptr;
    +    TfLiteRegistration* reg = nullptr;
    +    TF_LITE_ENSURE_STATUS(
    +        context->GetNodeAndRegistration(context, node_index, &node, ®));
    +    auto op_info = node_index_opinfo_map_.at(node_index);
    +    op_info.registration = reg;
    +    node_ptr_opinfo_map_->insert({node, op_info});
    +  }
    +
    +  if (node_ptr_opinfo_map_->size() != node_index_opinfo_map_.size()) {
    +    // Something wrong.
    +    return kTfLiteError;
    +  }
    +  return kTfLiteOk;
    +}
    +
    +}  // namespace calibration
    +}  // namespace optimize
    +}  // namespace tflite
    diff --git a/tensorflow/lite/tools/optimize/node_info_delegate.h b/tensorflow/lite/tools/optimize/node_info_delegate.h
    new file mode 100644
    index 0000000000..8ee2ce1978
    --- /dev/null
    +++ b/tensorflow/lite/tools/optimize/node_info_delegate.h
    @@ -0,0 +1,67 @@
    +/* Copyright 2018 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_LITE_TOOLS_OPTIMIZE_NODE_INFO_DELEGATE_H_
    +#define TENSORFLOW_LITE_TOOLS_OPTIMIZE_NODE_INFO_DELEGATE_H_
    +
    +#include 
    +
    +#include "tensorflow/lite/context.h"
    +#include "tensorflow/lite/tools/optimize/calibration_common.h"
    +
    +namespace tflite {
    +namespace optimize {
    +namespace calibration {
    +
    +// An interface for delegate observer that can listen to TfLiteDelegate::Prepare
    +// calls.
    +class DelegateObserver {
    + public:
    +  virtual TfLiteStatus OnDelegatePrepareCalled(TfLiteContext* context) = 0;
    +  virtual ~DelegateObserver() {}
    +};
    +
    +// The parameters for the node info delegate.
    +struct NodeInfoDelegateParams {
    +  DelegateObserver* delegate_observer;
    +};
    +
    +// Creates a delegate with the given |params|.
    +TfLiteDelegate CreateNodeInfoDelegate(NodeInfoDelegateParams* params);
    +
    +// A delegate observer that can construct the map from TfLiteNode* ->
    +// OperatorInfo.
    +class NodeInfoDelegateObserver : public DelegateObserver {
    + public:
    +  NodeInfoDelegateObserver(
    +      const std::unordered_map& node_index_to_op,
    +      std::unordered_map* node_ptr_opinfo_map)
    +      : node_index_opinfo_map_(node_index_to_op),
    +        node_ptr_opinfo_map_(node_ptr_opinfo_map) {}
    +
    +  TfLiteStatus OnDelegatePrepareCalled(TfLiteContext* context) override;
    +
    +  // Returns the context that was used to called the prepare method.
    +  const TfLiteContext* GetContext() const { return context_; }
    +
    + private:
    +  const TfLiteContext* context_ = nullptr;
    +  const std::unordered_map& node_index_opinfo_map_;
    +  std::unordered_map* node_ptr_opinfo_map_;
    +};
    +
    +}  // namespace calibration
    +}  // namespace optimize
    +}  // namespace tflite
    +#endif  // TENSORFLOW_LITE_TOOLS_OPTIMIZE_NODE_INFO_DELEGATE_H_
    diff --git a/tensorflow/lite/tools/optimize/node_info_delegate_test.cc b/tensorflow/lite/tools/optimize/node_info_delegate_test.cc
    new file mode 100644
    index 0000000000..e762d5c014
    --- /dev/null
    +++ b/tensorflow/lite/tools/optimize/node_info_delegate_test.cc
    @@ -0,0 +1,152 @@
    +/* Copyright 2018 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 
    +
    +#include 
    +#include 
    +#include "tensorflow/lite/kernels/register.h"
    +#include "tensorflow/lite/model.h"
    +#include "tensorflow/lite/tools/optimize/node_info_delegate.h"
    +
    +namespace tflite {
    +namespace optimize {
    +namespace calibration {
    +namespace {
    +
    +class TestDelegateObserver : public DelegateObserver {
    + public:
    +  explicit TestDelegateObserver(TfLiteStatus status_to_return)
    +      : status_to_return_(status_to_return) {}
    +
    +  TfLiteStatus OnDelegatePrepareCalled(TfLiteContext* context) override {
    +    num_times_called_++;
    +    return status_to_return_;
    +  }
    +  int num_times_called() { return num_times_called_; }
    +
    + private:
    +  int num_times_called_ = 0;
    +  TfLiteStatus status_to_return_;
    +};
    +
    +TEST(NodeInfoDelegateTest, DelegateObserverIsCalled) {
    +  TestDelegateObserver observer(kTfLiteOk);
    +  NodeInfoDelegateParams params;
    +  params.delegate_observer = &observer;
    +  auto model = FlatBufferModel::BuildFromFile(
    +      "third_party/tensorflow/lite/testdata/multi_add.bin");
    +  ASSERT_TRUE(model);
    +  std::unique_ptr interpreter;
    +  ASSERT_EQ(InterpreterBuilder(*model,
    +                               ops::builtin::BuiltinOpResolver{})(&interpreter),
    +            kTfLiteOk);
    +  ASSERT_TRUE(interpreter);
    +  EXPECT_EQ(0, observer.num_times_called());
    +  TfLiteDelegate delegate = CreateNodeInfoDelegate(¶ms);
    +
    +  auto status = interpreter->ModifyGraphWithDelegate(&delegate);
    +  EXPECT_EQ(kTfLiteOk, status);
    +  EXPECT_EQ(1, observer.num_times_called());
    +}
    +
    +TEST(NodeInfoDelegateTest, ObserverErrorCausesModifyGraphFailure) {
    +  // Observer returns error
    +  TestDelegateObserver observer(kTfLiteError);
    +  NodeInfoDelegateParams params;
    +  params.delegate_observer = &observer;
    +  auto model = FlatBufferModel::BuildFromFile(
    +      "third_party/tensorflow/lite/testdata/multi_add.bin");
    +  ASSERT_TRUE(model);
    +  std::unique_ptr interpreter;
    +  ASSERT_EQ(InterpreterBuilder(*model,
    +                               ops::builtin::BuiltinOpResolver{})(&interpreter),
    +            kTfLiteOk);
    +  ASSERT_TRUE(interpreter);
    +  TfLiteDelegate delegate = CreateNodeInfoDelegate(¶ms);
    +
    +  auto status = interpreter->ModifyGraphWithDelegate(&delegate);
    +  EXPECT_EQ(kTfLiteError, status);
    +}
    +
    +TEST(NodeInfoDelegateTest, NodeInfoDelegateObserver) {
    +  auto model = FlatBufferModel::BuildFromFile(
    +      "third_party/tensorflow/lite/testdata/multi_add.bin");
    +  ASSERT_TRUE(model);
    +
    +  std::unordered_map index_to_opinfo;
    +  auto primary_subgraph = model->GetModel()->subgraphs()->Get(0);
    +  auto operators = primary_subgraph->operators();
    +  auto subgraph_tensors = primary_subgraph->tensors();
    +  for (size_t i = 0; i < operators->size(); i++) {
    +    OperatorInfo info;
    +    auto op_inputs = operators->Get(i)->inputs();
    +    auto op_outputs = operators->Get(i)->outputs();
    +    info.inputs = std::vector(op_inputs->begin(), op_inputs->end());
    +    info.outputs = std::vector(op_outputs->begin(), op_outputs->end());
    +    index_to_opinfo[i] = info;
    +  }
    +
    +  std::unordered_map node_to_opinfo;
    +  NodeInfoDelegateObserver observer(index_to_opinfo, &node_to_opinfo);
    +  NodeInfoDelegateParams params;
    +  params.delegate_observer = &observer;
    +  std::unique_ptr interpreter;
    +  ASSERT_EQ(InterpreterBuilder(*model,
    +                               ops::builtin::BuiltinOpResolver{})(&interpreter),
    +            kTfLiteOk);
    +  ASSERT_TRUE(interpreter);
    +
    +  TfLiteDelegate delegate = CreateNodeInfoDelegate(¶ms);
    +
    +  auto status = interpreter->ModifyGraphWithDelegate(&delegate);
    +  EXPECT_EQ(kTfLiteOk, status);
    +  EXPECT_EQ(index_to_opinfo.size(), node_to_opinfo.size());
    +  EXPECT_EQ(interpreter->nodes_size(), node_to_opinfo.size());
    +
    +  for (const auto& node_and_opinfo : node_to_opinfo) {
    +    const TfLiteNode* tflite_node = node_and_opinfo.first;
    +    const OperatorInfo& info = node_and_opinfo.second;
    +    ASSERT_EQ(tflite_node->inputs->size, info.inputs.size());
    +    ASSERT_EQ(tflite_node->outputs->size, info.outputs.size());
    +
    +    for (size_t input_index = 0; input_index < info.inputs.size();
    +         input_index++) {
    +      const TfLiteTensor* tflite_tensor =
    +          interpreter->tensor(tflite_node->inputs->data[input_index]);
    +      EXPECT_EQ(tflite_tensor->name,
    +                subgraph_tensors->Get(info.inputs[input_index])->name()->str());
    +    }
    +
    +    for (size_t output_index = 0; output_index < info.outputs.size();
    +         output_index++) {
    +      const TfLiteTensor* tflite_tensor =
    +          interpreter->tensor(tflite_node->outputs->data[output_index]);
    +      EXPECT_EQ(
    +          tflite_tensor->name,
    +          subgraph_tensors->Get(info.outputs[output_index])->name()->str());
    +    }
    +  }
    +}
    +
    +}  // namespace
    +}  // namespace calibration
    +}  // namespace optimize
    +}  // namespace tflite
    +
    +int main(int argc, char** argv) {
    +  // On Linux, add: FLAGS_logtostderr = true;
    +  ::testing::InitGoogleTest(&argc, argv);
    +  return RUN_ALL_TESTS();
    +}
    -- 
    GitLab
    
    
    From 1271cb81425692e81a905cb0e7265c5b9c512093 Mon Sep 17 00:00:00 2001
    From: Andy Ly 
    Date: Thu, 10 Jan 2019 13:10:22 -0800
    Subject: [PATCH 622/622] [Grappler] Fix updating ports when removing regular
     fanins. Cleaned up some mutation logic and separated out adding and removing
     of regular and controlling fanins.
    
    PiperOrigin-RevId: 228763029
    ---
     .../core/grappler/mutable_graph_view.cc       | 310 ++++++++------
     tensorflow/core/grappler/mutable_graph_view.h |  78 ++--
     .../core/grappler/mutable_graph_view_test.cc  | 396 +++++++++++-------
     3 files changed, 470 insertions(+), 314 deletions(-)
    
    diff --git a/tensorflow/core/grappler/mutable_graph_view.cc b/tensorflow/core/grappler/mutable_graph_view.cc
    index 5df8bd8113..a8a90d1a57 100644
    --- a/tensorflow/core/grappler/mutable_graph_view.cc
    +++ b/tensorflow/core/grappler/mutable_graph_view.cc
    @@ -39,6 +39,22 @@ bool IsTensorIdPortValid(const TensorId& tensor_id) {
       return tensor_id.index() >= Graph::kControlSlot;
     }
     
    +bool IsTensorIdRegular(const TensorId& tensor_id) {
    +  return tensor_id.index() > Graph::kControlSlot;
    +}
    +
    +bool IsTensorIdControlling(const TensorId& tensor_id) {
    +  return tensor_id.index() == Graph::kControlSlot;
    +}
    +
    +bool IsOutputPortRegular(const MutableGraphView::OutputPort& port) {
    +  return port.port_id > Graph::kControlSlot;
    +}
    +
    +bool IsOutputPortControlling(const MutableGraphView::OutputPort& port) {
    +  return port.port_id == Graph::kControlSlot;
    +}
    +
     // Determines if node is an Identity where it's first regular input is a Switch
     // node.
     bool IsIdentityConsumingSwitch(const MutableGraphView& graph,
    @@ -46,7 +62,10 @@ bool IsIdentityConsumingSwitch(const MutableGraphView& graph,
       if ((IsIdentity(node) || IsIdentityNSingleInput(node)) &&
           node.input_size() > 0) {
         TensorId tensor_id = ParseTensorName(node.input(0));
    -    if (tensor_id.index() == Graph::kControlSlot) return false;
    +    if (IsTensorIdControlling(tensor_id)) {
    +      return false;
    +    }
    +
         NodeDef* input_node = graph.GetNode(tensor_id.node());
         return IsSwitch(*input_node);
       }
    @@ -83,7 +102,7 @@ void MutableGraphView::AddAndDedupFanouts(NodeDef* node) {
       while (pos <= last_pos) {
         TensorId tensor_id = ParseTensorName(node->input(pos));
         absl::string_view input_node_name = tensor_id.node();
    -    bool is_control_input = IsControlInput(tensor_id);
    +    bool is_control_input = IsTensorIdControlling(tensor_id);
         bool can_dedup_control_with_regular_input =
             CanDedupControlWithRegularInput(*this, input_node_name);
         bool can_dedup_control =
    @@ -246,15 +265,13 @@ bool MutableGraphView::UpdateFanoutsInternal(NodeDef* from_node,
       auto control_fanouts =
           GetFanout(GraphView::OutputPort(from_node, Graph::kControlSlot));
     
    -  const string from_control_input = absl::StrCat("^", from_node->name());
    -  const string to_control_input = absl::StrCat("^", to_node->name());
    -
       for (const InputPort& control_port : control_fanouts) {
         // Node can't be control dependency of itself.
         if (control_port.node == to_node) continue;
     
         NodeDef* node = control_port.node;
         modified |= RemoveControllingFaninInternal(node, from_node);
    +    // TODO(lyandy): Handle Switch control dependencies.
         modified |= AddFaninInternal(node, {to_node, Graph::kControlSlot});
       }
     
    @@ -276,7 +293,7 @@ bool MutableGraphView::AddFaninInternal(NodeDef* node,
                                             const OutputPort& fanin) {
       int num_non_controlling_fanins =
           NumFanins(*node, /*include_controlling_nodes=*/false);
    -  bool input_is_control = fanin.port_id == Graph::kControlSlot;
    +  bool input_is_control = IsOutputPortControlling(fanin);
       bool can_dedup_control_with_regular_input =
           CanDedupControlWithRegularInput(*this, *fanin.node);
       // Don't add duplicate control dependencies.
    @@ -296,12 +313,12 @@ bool MutableGraphView::AddFaninInternal(NodeDef* node,
           input_is_control ? Graph::kControlSlot : num_non_controlling_fanins;
     
       node->add_input(TensorIdToString({fanin.node->name(), fanin.port_id}));
    -  if (fanin.port_id > Graph::kControlSlot) {
    -    int node_input_size = node->input_size() - 1;
    +  if (IsOutputPortRegular(fanin)) {
    +    int last_node_input = node->input_size() - 1;
         // If there are control dependencies in node, move newly inserted fanin to
         // be before such control dependencies.
    -    if (num_non_controlling_fanins < node_input_size) {
    -      node->mutable_input()->SwapElements(node_input_size,
    +    if (num_non_controlling_fanins < last_node_input) {
    +      node->mutable_input()->SwapElements(last_node_input,
                                               num_non_controlling_fanins);
         }
       }
    @@ -327,9 +344,9 @@ bool MutableGraphView::AddFaninInternal(NodeDef* node, const TensorId& fanin) {
       return AddFaninInternal(node, {fanin_node, fanin.index()});
     }
     
    -bool MutableGraphView::AddFanin(absl::string_view node_name,
    -                                const TensorId& fanin) {
    -  if (!IsTensorIdPortValid(fanin)) {
    +bool MutableGraphView::AddRegularFanin(absl::string_view node_name,
    +                                       const TensorId& fanin) {
    +  if (!IsTensorIdRegular(fanin)) {
         return false;
       }
       NodeDef* node = GetNode(node_name);
    @@ -339,60 +356,153 @@ bool MutableGraphView::AddFanin(absl::string_view node_name,
       return AddFaninInternal(node, fanin);
     }
     
    -bool MutableGraphView::RemoveFanins(NodeDef* node,
    -                                    absl::Span fanins) {
    -  bool modified = false;
    -  auto mutable_inputs = node->mutable_input();
    -  int curr_pos = 0;
    -  int num_inputs = node->input_size();
    -  for (int i = 0; i < num_inputs; ++i) {
    -    TensorId tensor_id = ParseTensorName(node->input(i));
    -    bool remove_fanin =
    -        std::find(fanins.begin(), fanins.end(), tensor_id) != fanins.end();
    -    bool update_fanin = !remove_fanin && modified;
    -    if (remove_fanin || update_fanin) {
    -      OutputPort fanin(nodes()[tensor_id.node()], tensor_id.index());
    -
    -      InputPort input;
    -      input.node = node;
    -      input.port_id =
    -          tensor_id.index() == Graph::kControlSlot ? Graph::kControlSlot : i;
    -
    -      auto& fanouts_set = fanouts()[fanin];
    -      if (remove_fanin) {
    -        fanouts_set.erase(input);
    -      } else {
    -        // Shift inputs to be retained.
    -        if (tensor_id.index() > Graph::kControlSlot) {
    -          fanouts_set.erase(input);
    -          fanouts_set.insert(InputPort(node, i));
    +bool MutableGraphView::AddControllingFanin(absl::string_view node_name,
    +                                           const TensorId& fanin) {
    +  NodeDef* node = GetNode(node_name);
    +  if (node == nullptr) {
    +    return false;
    +  }
    +  NodeDef* fanin_node = GetNode(fanin.node());
    +  if (fanin_node == nullptr) {
    +    return false;
    +  }
    +
    +  if (!IsSwitch(*fanin_node)) {
    +    return AddFaninInternal(node, {fanin_node, Graph::kControlSlot});
    +  } else {
    +    if (IsTensorIdControlling(fanin)) {
    +      // Cannot add a Switch node control dependency.
    +      return false;
    +    }
    +    // We can't anchor control dependencies directly on the switch node: unlike
    +    // other nodes only one of the outputs of the switch node will be generated
    +    // when the switch node is executed, and we need to make sure the control
    +    // dependency is only triggered when the corresponding output is triggered.
    +    // We start by looking for an identity node connected to the output of the
    +    // switch node, and use it to anchor the control dependency.
    +    auto fanouts = GetFanouts(*fanin_node, /*include_controlled_nodes=*/false);
    +    for (auto fanout : fanouts) {
    +      if (IsIdentity(*fanout.node) || IsIdentityNSingleInput(*fanout.node)) {
    +        if (ParseTensorName(fanout.node->input(0)) == fanin) {
    +          return AddFaninInternal(node, {fanout.node, Graph::kControlSlot});
             }
    -        mutable_inputs->SwapElements(i, curr_pos++);
           }
    -      UpdateMaxRegularOutputPortForRemovedFanin(fanin, fanouts_set);
    +    }
    +    // We haven't found an existing node where we can anchor the control
    +    // dependency: add a new identity node.
    +    string ctrl_dep_name = AddPrefixToNodeName(
    +        absl::StrCat(fanin.node(), "_", fanin.index()), kMutableGraphViewCtrl);
     
    +    // Reuse a previously created node, if possible.
    +    NodeDef* ctrl_dep_node = GetNode(ctrl_dep_name);
    +    if (ctrl_dep_node == nullptr) {
    +      NodeDef new_node;
    +      new_node.set_name(ctrl_dep_name);
    +      new_node.set_op("Identity");
    +      new_node.set_device(fanin_node->device());
    +      (*new_node.mutable_attr())["T"].set_type(
    +          fanin_node->attr().at("T").type());
    +      new_node.add_input(TensorIdToString(fanin));
    +      ctrl_dep_node = AddNode(std::move(new_node));
    +    }
    +    return AddFaninInternal(node, {ctrl_dep_node, Graph::kControlSlot});
    +  }
    +}
    +
    +bool MutableGraphView::RemoveRegularFaninInternal(NodeDef* node,
    +                                                  const TensorId& fanin) {
    +  auto remove_input = [this, node](const TensorId& tensor_id, int port,
    +                                   bool update_max_port) {
    +    OutputPort fanin_port(nodes()[tensor_id.node()], tensor_id.index());
    +    InputPort input(node, port);
    +
    +    absl::flat_hash_set* fanouts_set = &fanouts()[fanin_port];
    +    fanouts_set->erase(input);
    +    if (update_max_port) {
    +      UpdateMaxRegularOutputPortForRemovedFanin(fanin_port, *fanouts_set);
    +    }
    +    return fanouts_set;
    +  };
    +
    +  auto mutable_inputs = node->mutable_input();
    +  bool modified = false;
    +  const int num_inputs = node->input_size();
    +  int i;
    +  int curr_pos = 0;
    +  for (i = 0; i < num_inputs; ++i) {
    +    TensorId tensor_id = ParseTensorName(node->input(i));
    +    if (IsTensorIdControlling(tensor_id)) {
    +      break;
    +    }
    +    if (tensor_id == fanin) {
    +      remove_input(tensor_id, i, /*update_max_port=*/true);
           modified = true;
    +    } else if (modified) {
    +      // Regular inputs will need to have their ports updated.
    +      auto fanouts_set = remove_input(tensor_id, i, /*update_max_port=*/false);
    +      fanouts_set->insert({node, curr_pos});
    +      // Shift inputs to be retained.
    +      mutable_inputs->SwapElements(i, curr_pos++);
         } else {
           // Skip inputs to be retained until first modification.
           curr_pos++;
         }
       }
    -  if (modified) {
    -    mutable_inputs->DeleteSubrange(curr_pos, num_inputs - curr_pos);
    +
    +  if (modified && curr_pos < i) {
    +    // Remove fanins from node inputs.
    +    mutable_inputs->DeleteSubrange(curr_pos, i - curr_pos);
       }
    +
       return modified;
     }
     
    -bool MutableGraphView::RemoveFanin(absl::string_view node_name,
    -                                   const TensorId& fanin) {
    -  if (!IsTensorIdPortValid(fanin)) {
    +bool MutableGraphView::RemoveRegularFanin(absl::string_view node_name,
    +                                          const TensorId& fanin) {
    +  if (!IsTensorIdRegular(fanin)) {
         return false;
       }
       NodeDef* node = GetNode(node_name);
       if (node == nullptr) {
         return false;
       }
    -  return RemoveFanins(node, {fanin});
    +  return RemoveRegularFaninInternal(node, fanin);
    +}
    +
    +bool MutableGraphView::RemoveControllingFaninInternal(NodeDef* node,
    +                                                      NodeDef* fanin_node) {
    +  for (int i = node->input_size() - 1; i >= 0; --i) {
    +    TensorId tensor_id = ParseTensorName(node->input(i));
    +    if (tensor_id.index() > Graph::kControlSlot) {
    +      break;
    +    }
    +    if (tensor_id.node() == fanin_node->name()) {
    +      fanouts()[{fanin_node, Graph::kControlSlot}].erase(
    +          {node, Graph::kControlSlot});
    +      node->mutable_input()->SwapElements(i, node->input_size() - 1);
    +      node->mutable_input()->RemoveLast();
    +      return true;
    +    }
    +  }
    +  return false;
    +}
    +
    +bool MutableGraphView::RemoveControllingFaninInternal(
    +    NodeDef* node, absl::string_view fanin_node_name) {
    +  NodeDef* fanin = GetNode(fanin_node_name);
    +  if (fanin == nullptr) {
    +    return false;
    +  }
    +  return RemoveControllingFaninInternal(node, fanin);
    +}
    +
    +bool MutableGraphView::RemoveControllingFanin(
    +    absl::string_view node_name, absl::string_view fanin_node_name) {
    +  NodeDef* node = GetNode(node_name);
    +  if (node == nullptr) {
    +    return false;
    +  }
    +  return RemoveControllingFaninInternal(node, fanin_node_name);
     }
     
     bool MutableGraphView::RemoveAllFanins(absl::string_view node_name,
    @@ -432,10 +542,18 @@ bool MutableGraphView::UpdateFanin(absl::string_view node_name,
     
       // When replacing a non control dependency fanin with a control dependency, or
       // vice versa, remove and add, so ports can be updated properly in fanout(s).
    -  if (from_fanin.index() == Graph::kControlSlot ||
    -      to_fanin.index() == Graph::kControlSlot) {
    -    bool modified = RemoveFanins(node, {from_fanin});
    -    modified |= AddFaninInternal(node, to_fanin);
    +  bool from_fanin_is_control = IsTensorIdControlling(from_fanin);
    +  if (from_fanin_is_control || IsTensorIdControlling(to_fanin)) {
    +    bool modified = false;
    +    if (from_fanin_is_control) {
    +      modified |= RemoveControllingFaninInternal(node, from_fanin.node());
    +    } else {
    +      modified |= RemoveRegularFaninInternal(node, from_fanin);
    +    }
    +    if (modified) {
    +      AddFaninInternal(node, to_fanin);
    +    }
    +
         return modified;
       }
     
    @@ -456,7 +574,7 @@ bool MutableGraphView::UpdateFanin(absl::string_view node_name,
           InputPort old_input;
           old_input.node = node;
           old_input.port_id =
    -          from_fanin.index() == Graph::kControlSlot ? Graph::kControlSlot : i;
    +          IsTensorIdControlling(from_fanin) ? Graph::kControlSlot : i;
           if (from_fanin_port_fanouts == nullptr) {
             OutputPort from_fanin_port(from_fanin_node, from_fanin.index());
             from_fanin_port_fanouts = &fanouts()[from_fanin_port];
    @@ -466,7 +584,7 @@ bool MutableGraphView::UpdateFanin(absl::string_view node_name,
           InputPort new_input;
           new_input.node = node;
           new_input.port_id =
    -          to_fanin.index() == Graph::kControlSlot ? Graph::kControlSlot : i;
    +          IsTensorIdControlling(to_fanin) ? Graph::kControlSlot : i;
           if (to_fanin_port_fanouts == nullptr) {
             OutputPort to_fanin_port(to_fanin_node, to_fanin.index());
             to_fanin_port_fanouts = &fanouts()[to_fanin_port];
    @@ -493,75 +611,6 @@ bool MutableGraphView::UpdateFanin(absl::string_view node_name,
       return modified;
     }
     
    -bool MutableGraphView::AddControllingFanin(absl::string_view node_name,
    -                                           const TensorId& fanin) {
    -  NodeDef* node = GetNode(node_name);
    -  if (node == nullptr) {
    -    return false;
    -  }
    -  NodeDef* fanin_node = GetNode(fanin.node());
    -  if (fanin_node == nullptr) {
    -    return false;
    -  }
    -  if (fanin.index() == Graph::kControlSlot) {
    -    return AddFaninInternal(node, {fanin_node, Graph::kControlSlot});
    -  }
    -
    -  if (!IsSwitch(*fanin_node)) {
    -    return AddFaninInternal(node, {fanin_node, Graph::kControlSlot});
    -  } else {
    -    // We can't anchor control dependencies directly on the switch node: unlike
    -    // other nodes only one of the outputs of the switch node will be generated
    -    // when the switch node is executed, and we need to make sure the control
    -    // dependency is only triggered when the corresponding output is triggered.
    -    // We start by looking for an identity node connected to the output of the
    -    // switch node, and use it to anchor the control dependency.
    -    auto fanouts = GetFanouts(*fanin_node, /*include_controlled_nodes=*/false);
    -    for (auto fanout : fanouts) {
    -      if (IsIdentity(*fanout.node) || IsIdentityNSingleInput(*fanout.node)) {
    -        if (ParseTensorName(fanout.node->input(0)) == fanin) {
    -          return AddFaninInternal(node, {fanout.node, Graph::kControlSlot});
    -        }
    -      }
    -    }
    -    // We haven't found an existing node where we can anchor the control
    -    // dependency: add a new identity node.
    -    string ctrl_dep_name = AddPrefixToNodeName(
    -        absl::StrCat(fanin.node(), "_", fanin.index()), kMutableGraphViewCtrl);
    -
    -    NodeDef* ctrl_dep_node = GetNode(ctrl_dep_name);
    -    if (ctrl_dep_node == nullptr) {
    -      NodeDef new_node;
    -      new_node.set_name(ctrl_dep_name);
    -      new_node.set_op("Identity");
    -      new_node.set_device(fanin_node->device());
    -      (*new_node.mutable_attr())["T"].set_type(
    -          fanin_node->attr().at("T").type());
    -      new_node.add_input(TensorIdToString(fanin));
    -      ctrl_dep_node = AddNode(std::move(new_node));
    -    }
    -    return AddFaninInternal(node, {ctrl_dep_node, Graph::kControlSlot});
    -  }
    -}
    -
    -bool MutableGraphView::RemoveControllingFaninInternal(NodeDef* node,
    -                                                      NodeDef* fanin_node) {
    -  for (int i = node->input_size() - 1; i >= 0; --i) {
    -    TensorId tensor_id = ParseTensorName(node->input(i));
    -    if (tensor_id.index() > Graph::kControlSlot) {
    -      break;
    -    }
    -    if (tensor_id.node() == fanin_node->name()) {
    -      fanouts()[{fanin_node, Graph::kControlSlot}].erase(
    -          {node, Graph::kControlSlot});
    -      node->mutable_input()->SwapElements(i, node->input_size() - 1);
    -      node->mutable_input()->RemoveLast();
    -      return true;
    -    }
    -  }
    -  return false;
    -}
    -
     void MutableGraphView::DeleteNodes(const std::set& nodes_to_delete) {
       for (const string& node_name_to_delete : nodes_to_delete)
         RemoveFaninsInternal(nodes().at(node_name_to_delete),
    @@ -575,21 +624,18 @@ void MutableGraphView::RemoveFaninsInternal(NodeDef* deleted_node,
                                                 bool keep_controlling_fanins) {
       for (int i = 0; i < deleted_node->input_size(); ++i) {
         TensorId tensor_id = ParseTensorName(deleted_node->input(i));
    -    if (keep_controlling_fanins && tensor_id.index() < 0) {
    +    if (keep_controlling_fanins && IsTensorIdControlling(tensor_id)) {
           break;
         }
         OutputPort fanin(nodes()[tensor_id.node()], tensor_id.index());
     
         InputPort input;
         input.node = deleted_node;
    -    if (tensor_id.index() < 0)
    -      input.port_id = Graph::kControlSlot;
    -    else
    -      input.port_id = i;
    -
    -    auto& fanouts_set = fanouts()[fanin];
    -    fanouts_set.erase(input);
    -    UpdateMaxRegularOutputPortForRemovedFanin(fanin, fanouts_set);
    +    input.port_id = IsTensorIdControlling(tensor_id) ? Graph::kControlSlot : i;
    +
    +    absl::flat_hash_set* fanouts_set = &fanouts()[fanin];
    +    fanouts_set->erase(input);
    +    UpdateMaxRegularOutputPortForRemovedFanin(fanin, *fanouts_set);
       }
     }
     
    diff --git a/tensorflow/core/grappler/mutable_graph_view.h b/tensorflow/core/grappler/mutable_graph_view.h
    index 93f0958d5e..f07254068d 100644
    --- a/tensorflow/core/grappler/mutable_graph_view.h
    +++ b/tensorflow/core/grappler/mutable_graph_view.h
    @@ -78,39 +78,17 @@ class MutableGraphView : public internal::GraphViewInternal {
       // This will return true iff the nodes are modified.
       bool UpdateFanouts(absl::string_view from_node, absl::string_view to_node);
     
    -  // Adds fanin to node `node_name`. If the node or fanin do not exist in the
    -  // graph, nothing will be modified in the graph. If fanin is a control
    -  // dependency, existing control dependencies will be checked first before
    -  // adding. Otherwise fanin will be added after existing non control dependency
    -  // inputs.
    -  //
    -  // This will return true iff the node is modified. If a control dependency
    -  // already exists, the node will not be modified.
    -  bool AddFanin(absl::string_view node_name, const TensorId& fanin);
    -
    -  // Removes fanin from node `node_name`. If the node or fanin do not exist in
    -  // the graph, nothing will be modified in the graph. If there are multiple
    -  // inputs that match the fanin, all of them will be removed.
    -  //
    -  // This will return true iff the node is modified. If no inputs match the
    -  // fanin, the node will not be modified.
    -  bool RemoveFanin(absl::string_view node_name, const TensorId& fanin);
    -
    -  // Removes all fanins from node `node_name`. Control dependencies will be
    -  // retained if keep_controlling_fanins is true.
    -  //
    -  // This will return true iff the node is modified.
    -  bool RemoveAllFanins(absl::string_view node_name,
    -                       bool keep_controlling_fanins);
    -
    -  // Replaces all fanins `from_fanin` with `to_fanin` in node `node_name`. If
    -  // the fanins or node do not exist, nothing will be modified in the graph.
    +  // Adds regular fanin `fanin` to node `node_name`. If the node or fanin do not
    +  // exist in the graph, nothing will be modified in the graph. Otherwise fanin
    +  // will be added after existing non control dependency fanins. Control
    +  // dependencies will be deduped. To add control dependencies, use
    +  // AddControllingFanin.
       //
       // This will return true iff the node is modified.
    -  bool UpdateFanin(absl::string_view node_name, const TensorId& from_fanin,
    -                   const TensorId& to_fanin);
    +  bool AddRegularFanin(absl::string_view node_name, const TensorId& fanin);
     
    -  // Adds a control dependency to the target node named `node_name`.
    +  // Adds control dependency `fanin` to the target node named `node_name`. To
    +  // add regular fanins, use AddRegularFanin.
       //
       // Case 1: If the fanin is not a Switch node, the control dependency is simply
       // added to the target node:
    @@ -130,6 +108,37 @@ class MutableGraphView : public internal::GraphViewInternal {
       // This will return true iff the node is modified.
       bool AddControllingFanin(absl::string_view node_name, const TensorId& fanin);
     
    +  // Removes regular fanin `fanin` from node `node_name`. If the node or fanin
    +  // do not exist in the graph, nothing will be modified in the graph. If there
    +  // are multiple inputs that match the fanin, all of them will be removed. To
    +  // remove controlling fanins, use RemoveControllingFanin.
    +  //
    +  // This will return true iff the node is modified.
    +  bool RemoveRegularFanin(absl::string_view node_name, const TensorId& fanin);
    +
    +  // Removes control dependency `fanin_node_name` from the target node named
    +  // `node_name`. If the node or fanin do not exist in the graph, nothing will
    +  // be modified in the graph. To remove regular fanins, use RemoveRegualrFanin.
    +  //
    +  // This will return true iff the node is modified.
    +  bool RemoveControllingFanin(absl::string_view node_name,
    +                              absl::string_view fanin_node_name);
    +
    +  // Removes all fanins from node `node_name`. Control dependencies will be
    +  // retained if keep_controlling_fanins is true.
    +  //
    +  // This will return true iff the node is modified.
    +  bool RemoveAllFanins(absl::string_view node_name,
    +                       bool keep_controlling_fanins);
    +
    +  // Replaces all fanins `from_fanin` with `to_fanin` in node `node_name`. If
    +  // the fanins or node do not exist, nothing will be modified in the graph.
    +  // Control dependencies will be deduped.
    +  //
    +  // This will return true iff the node is modified.
    +  bool UpdateFanin(absl::string_view node_name, const TensorId& from_fanin,
    +                   const TensorId& to_fanin);
    +
       // Deletes nodes from the graph.
       void DeleteNodes(const std::set& nodes_to_delete);
     
    @@ -189,7 +198,14 @@ class MutableGraphView : public internal::GraphViewInternal {
       // Removes any fanin in node that matches to a fanin in fanins.
       //
       // This will return true iff the node is modified.
    -  bool RemoveFanins(NodeDef* node, absl::Span fanins);
    +  bool RemoveRegularFaninInternal(NodeDef* node, const TensorId& fanin);
    +
    +  // Removes controlling fanin `fanin_node_name` from node if such controlling
    +  // fanin exists.
    +  //
    +  // This will return true iff the node is modified.
    +  bool RemoveControllingFaninInternal(NodeDef* node,
    +                                      absl::string_view fanin_node_name);
     
       // Removes controlling fanin `fanin_node` from node if such controlling fanin
       // exists.
    diff --git a/tensorflow/core/grappler/mutable_graph_view_test.cc b/tensorflow/core/grappler/mutable_graph_view_test.cc
    index 526ca85ec0..2048c67e3e 100644
    --- a/tensorflow/core/grappler/mutable_graph_view_test.cc
    +++ b/tensorflow/core/grappler/mutable_graph_view_test.cc
    @@ -186,10 +186,10 @@ GraphDef SimpleMutateFaninGraph() {
     void CompareNodeInputs(const MutableGraphView& graph, const NodeDef* expected,
                            NodeDef* actual) {
       ASSERT_EQ(actual->input_size(), expected->input_size());
    -  int port;
       for (int i = 0; i < actual->input_size(); ++i) {
         EXPECT_EQ(actual->input(i), expected->input(i));
         TensorId tensor_id = ParseTensorName(expected->input(i));
    +    int port;
         if (tensor_id.index() == Graph::kControlSlot) {
           port = Graph::kControlSlot;
         } else {
    @@ -203,8 +203,9 @@ void CompareNodeInputs(const MutableGraphView& graph, const NodeDef* expected,
       }
     }
     
    -void TestAddFanin(absl::string_view node_name, const TensorId& fanin_to_add,
    -                  bool modified, const NodeDef* expected_node) {
    +void TestAddRegularFanin(absl::string_view node_name,
    +                         const TensorId& fanin_to_add, bool modified,
    +                         const NodeDef* expected_node) {
       GraphDef graph_def = SimpleMutateFaninGraph();
     
       MutableGraphView graph(&graph_def);
    @@ -216,70 +217,71 @@ void TestAddFanin(absl::string_view node_name, const TensorId& fanin_to_add,
         EXPECT_NE(node, nullptr);
       }
     
    -  EXPECT_EQ(modified, graph.AddFanin(node_name, fanin_to_add));
    +  EXPECT_EQ(modified, graph.AddRegularFanin(node_name, fanin_to_add));
       if (expected_node != nullptr) {
         CompareNodeInputs(graph, expected_node, node);
       }
     }
     
    -TEST(MutableGraphViewTest, AddFanin) {
    +TEST(MutableGraphViewTest, AddRegularFanin) {
       NodeDef expected_node;
       // Add input to node with 1 input 0 controls.
       expected_node = NDef("", "", {"a", "b:1"});
    -  TestAddFanin("foo_1", {"b", 1}, /*modified=*/true, &expected_node);
    +  TestAddRegularFanin("foo_1", {"b", 1}, /*modified=*/true, &expected_node);
       // Add input to node with multiple inputs and 0 controls.
       expected_node = NDef("", "", {"b", "a:1", "a:1", "b:2"});
    -  TestAddFanin("foo_3", {"b", 2}, /*modified=*/true, &expected_node);
    +  TestAddRegularFanin("foo_3", {"b", 2}, /*modified=*/true, &expected_node);
       // Add input to node with 1 input multiple controls.
       expected_node = NDef("", "", {"b", "a", "^c"});
    -  TestAddFanin("foo_2", {"a", 0}, /*modified=*/true, &expected_node);
    +  TestAddRegularFanin("foo_2", {"a", 0}, /*modified=*/true, &expected_node);
       // Add input to node with multiple inputs and controls.
       expected_node = NDef("", "", {"a", "b:2", "b:2", "a:1", "^d", "^c"});
    -  TestAddFanin("foo_4", {"a", 1}, /*modified=*/true, &expected_node);
    +  TestAddRegularFanin("foo_4", {"a", 1}, /*modified=*/true, &expected_node);
       // Add input to node with 0 inputs 0 controls.
       expected_node = NDef("", "", {"a:1"});
    -  TestAddFanin("foo_5", {"a", 1}, /*modified=*/true, &expected_node);
    +  TestAddRegularFanin("foo_5", {"a", 1}, /*modified=*/true, &expected_node);
       // Add input to node with 0 inputs multiple controls.
       expected_node = NDef("", "", {"c:1", "^b", "^a"});
    -  TestAddFanin("foo_6", {"c", 1}, /*modified=*/true, &expected_node);
    +  TestAddRegularFanin("foo_6", {"c", 1}, /*modified=*/true, &expected_node);
     
       // Add control to node with 1 input 0 controls.
    -  expected_node = NDef("", "", {"a", "^b"});
    -  TestAddFanin("foo_1", {"b", Graph::kControlSlot}, /*modified=*/true,
    -               &expected_node);
    +  expected_node = NDef("", "", {"a"});
    +  TestAddRegularFanin("foo_1", {"b", Graph::kControlSlot}, /*modified=*/false,
    +                      &expected_node);
       // Add control to node with multiple inputs and 0 controls.
    -  expected_node = NDef("", "", {"b", "a:1", "a:1", "^c"});
    -  TestAddFanin("foo_3", {"c", Graph::kControlSlot}, /*modified=*/true,
    -               &expected_node);
    +  expected_node = NDef("", "", {"b", "a:1", "a:1"});
    +  TestAddRegularFanin("foo_3", {"c", Graph::kControlSlot}, /*modified=*/false,
    +                      &expected_node);
       // Add control to node with 1 input multiple controls.
    -  expected_node = NDef("", "", {"b", "^a", "^c", "^d"});
    -  TestAddFanin("foo_2", {"d", Graph::kControlSlot}, /*modified=*/true,
    -               &expected_node);
    +  expected_node = NDef("", "", {"b", "^a", "^c"});
    +  TestAddRegularFanin("foo_2", {"d", Graph::kControlSlot}, /*modified=*/false,
    +                      &expected_node);
       // Add control to node with multiple input multiple controls.
       expected_node = NDef("", "", {"a", "b:2", "b:2", "^c", "^d"});
    -  TestAddFanin("foo_4", {"a", Graph::kControlSlot}, /*modified=*/false,
    -               &expected_node);
    +  TestAddRegularFanin("foo_4", {"a", Graph::kControlSlot},
    +                      /*modified=*/false, &expected_node);
       // Add control to node with 0 inputs 0 controls.
    -  expected_node = NDef("", "", {"^a"});
    -  TestAddFanin("foo_5", {"a", Graph::kControlSlot}, /*modified=*/true,
    -               &expected_node);
    +  expected_node = NDef("", "", {});
    +  TestAddRegularFanin("foo_5", {"a", Graph::kControlSlot}, /*modified=*/false,
    +                      &expected_node);
       // Add control to node with 0 inputs multiple controls.
    -  expected_node = NDef("", "", {"^a", "^b", "^c"});
    -  TestAddFanin("foo_6", {"c", Graph::kControlSlot}, /*modified=*/true,
    -               &expected_node);
    +  expected_node = NDef("", "", {"^a", "^b"});
    +  TestAddRegularFanin("foo_6", {"c", Graph::kControlSlot}, /*modified=*/false,
    +                      &expected_node);
       // Add control to node with control that already exists.
       expected_node = NDef("", "", {"b", "^a", "^c"});
    -  TestAddFanin("foo_2", {"a", Graph::kControlSlot}, /*modified=*/false,
    -               &expected_node);
    +  TestAddRegularFanin("foo_2", {"a", Graph::kControlSlot},
    +                      /*modified=*/false, &expected_node);
     
       // Add fanin to node where node is missing.
    -  TestAddFanin("foo_missing", {"a", 0}, /*modified=*/false, nullptr);
    +  TestAddRegularFanin("foo_missing", {"a", 0}, /*modified=*/false, nullptr);
       // Add fanin to node where fanin is missing.
       expected_node = NDef("", "", {"a"});
    -  TestAddFanin("foo_1", {"bar_missing", 0}, /*modified=*/false, &expected_node);
    +  TestAddRegularFanin("foo_1", {"bar_missing", 0}, /*modified=*/false,
    +                      &expected_node);
       // Add fanin to node where node and fanin are missing.
    -  TestAddFanin("foo_missing", {"bar_missing", 0}, /*modified=*/false,
    -               /*expected_node=*/nullptr);
    +  TestAddRegularFanin("foo_missing", {"bar_missing", 0}, /*modified=*/false,
    +                      /*expected_node=*/nullptr);
     }
     
     void CheckFanout(const MutableGraphView& graph, const TensorId& fanin,
    @@ -292,9 +294,9 @@ void CheckFanout(const MutableGraphView& graph, const TensorId& fanin,
       }
     }
     
    -void TestRemoveFanin(absl::string_view node_name,
    -                     const TensorId& fanin_to_remove, bool modified,
    -                     const NodeDef* expected_node) {
    +void TestRemoveRegularFanin(absl::string_view node_name,
    +                            const TensorId& fanin_to_remove, bool modified,
    +                            const NodeDef* expected_node) {
       GraphDef graph_def = SimpleMutateFaninGraph();
     
       MutableGraphView graph(&graph_def);
    @@ -306,7 +308,7 @@ void TestRemoveFanin(absl::string_view node_name,
         EXPECT_NE(nullptr, node);
       }
     
    -  EXPECT_EQ(modified, graph.RemoveFanin(node_name, fanin_to_remove));
    +  EXPECT_EQ(modified, graph.RemoveRegularFanin(node_name, fanin_to_remove));
       if (expected_node != nullptr) {
         CompareNodeInputs(graph, expected_node, node);
         if (modified) {
    @@ -315,63 +317,68 @@ void TestRemoveFanin(absl::string_view node_name,
       }
     }
     
    -TEST(MutableGraphViewTest, RemoveFanin) {
    +TEST(MutableGraphViewTest, RemoveRegularFanin) {
       NodeDef expected_node;
       // Remove input from node with 1 input 0 controls.
       expected_node = NDef("", "", {});
    -  TestRemoveFanin("foo_1", {"a", 0}, /*modified=*/true, &expected_node);
    +  TestRemoveRegularFanin("foo_1", {"a", 0}, /*modified=*/true, &expected_node);
       // Remove input from node with multiple inputs and 0 controls.
       expected_node = NDef("", "", {"b"});
    -  TestRemoveFanin("foo_3", {"a", 1}, /*modified=*/true, &expected_node);
    +  TestRemoveRegularFanin("foo_3", {"a", 1}, /*modified=*/true, &expected_node);
       // Remove input from node with 1 input multiple controls.
       expected_node = NDef("", "", {"^a", "^c"});
    -  TestRemoveFanin("foo_2", {"b", 0}, /*modified=*/true, &expected_node);
    +  TestRemoveRegularFanin("foo_2", {"b", 0}, /*modified=*/true, &expected_node);
       // Remove input from node with multiple inputs and controls.
       expected_node = NDef("", "", {"a", "^c", "^d"});
    -  TestRemoveFanin("foo_4", {"b", 2}, /*modified=*/true, &expected_node);
    +  TestRemoveRegularFanin("foo_4", {"b", 2}, /*modified=*/true, &expected_node);
    +  // Remove input from node with multiple inputs and controls, and results in
    +  // shifting of ports.
    +  expected_node = NDef("", "", {"b:2", "b:2", "^c", "^d"});
    +  TestRemoveRegularFanin("foo_4", {"a", 0}, /*modified=*/true, &expected_node);
     
       // Remove control from node with 1 input multiple controls.
    -  expected_node = NDef("", "", {"b", "^c"});
    -  TestRemoveFanin("foo_2", {"a", Graph::kControlSlot}, /*modified=*/true,
    -                  &expected_node);
    +  expected_node = NDef("", "", {"b", "^a", "^c"});
    +  TestRemoveRegularFanin("foo_2", {"a", Graph::kControlSlot},
    +                         /*modified=*/false, &expected_node);
       // Remove control from node with multiple input multiple controls.
    -  expected_node = NDef("", "", {"a", "b:2", "b:2", "^c"});
    -  TestRemoveFanin("foo_4", {"d", Graph::kControlSlot}, /*modified=*/true,
    -                  &expected_node);
    +  expected_node = NDef("", "", {"a", "b:2", "b:2", "^c", "^d"});
    +  TestRemoveRegularFanin("foo_4", {"d", Graph::kControlSlot},
    +                         /*modified=*/false, &expected_node);
       // Remove control from node with 0 inputs multiple controls.
    -  expected_node = NDef("", "", {"^b"});
    -  TestRemoveFanin("foo_6", {"a", Graph::kControlSlot}, /*modified=*/true,
    -                  &expected_node);
    +  expected_node = NDef("", "", {"^a", "^b"});
    +  TestRemoveRegularFanin("foo_6", {"a", Graph::kControlSlot},
    +                         /*modified=*/false, &expected_node);
     
       // Remove input from node with 0 inputs 0 controls.
       expected_node = NDef("", "", {});
    -  TestRemoveFanin("foo_5", {"a", 1}, /*modified=*/false, &expected_node);
    +  TestRemoveRegularFanin("foo_5", {"a", 1}, /*modified=*/false, &expected_node);
       // Remove input from node with 0 inputs multiple controls.
       expected_node = NDef("", "", {"^a", "^b"});
    -  TestRemoveFanin("foo_6", {"a", 1}, /*modified=*/false, &expected_node);
    +  TestRemoveRegularFanin("foo_6", {"a", 1}, /*modified=*/false, &expected_node);
    +
       // Remove control from node with 1 input 0 controls.
       expected_node = NDef("", "", {"a"});
    -  TestRemoveFanin("foo_1", {"b", Graph::kControlSlot}, /*modified=*/false,
    -                  &expected_node);
    +  TestRemoveRegularFanin("foo_1", {"b", Graph::kControlSlot},
    +                         /*modified=*/false, &expected_node);
       // Remove control from node with multiple inputs and 0 controls.
       expected_node = NDef("", "", {"b", "a:1", "a:1"});
    -  TestRemoveFanin("foo_3", {"c", Graph::kControlSlot}, /*modified=*/false,
    -                  &expected_node);
    +  TestRemoveRegularFanin("foo_3", {"c", Graph::kControlSlot},
    +                         /*modified=*/false, &expected_node);
       // Remove control from node with 0 inputs 0 controls.
       expected_node = NDef("", "", {});
    -  TestRemoveFanin("foo_5", {"a", Graph::kControlSlot}, /*modified=*/false,
    -                  &expected_node);
    +  TestRemoveRegularFanin("foo_5", {"a", Graph::kControlSlot},
    +                         /*modified=*/false, &expected_node);
     
       // Remove fanin from node where node is missing.
    -  TestRemoveFanin("foo_missing", {"a", 0}, /*modified=*/false,
    -                  /*expected_node=*/nullptr);
    +  TestRemoveRegularFanin("foo_missing", {"a", 0}, /*modified=*/false,
    +                         /*expected_node=*/nullptr);
       // Remove fanin from node where fanin is missing.
       expected_node = NDef("", "", {"a"});
    -  TestRemoveFanin("foo_1", {"bar_missing", 0}, /*modified=*/false,
    -                  &expected_node);
    +  TestRemoveRegularFanin("foo_1", {"bar_missing", 0}, /*modified=*/false,
    +                         &expected_node);
       // Remove fanin from node where node and fanin are missing.
    -  TestRemoveFanin("foo_missing", {"bar_missing", 0}, /*modified=*/false,
    -                  /*expected_node=*/nullptr);
    +  TestRemoveRegularFanin("foo_missing", {"bar_missing", 0}, /*modified=*/false,
    +                         /*expected_node=*/nullptr);
     }
     
     void TestRemoveAllFanins(absl::string_view node_name,
    @@ -540,56 +547,59 @@ TEST(MutableGraphViewTest, DedupControllingFaninsOnGraphInit) {
       MutableGraphView graph(&graph_def);
     
       EXPECT_EQ(graph.graph()->node_size(), 11);
    +  NodeDef expected;
       NodeDef* a = graph.GetNode("a");
       ASSERT_NE(a, nullptr);
    -  ASSERT_EQ(a->input_size(), 0);
    +  expected = NDef("", "", {});
    +  CompareNodeInputs(graph, &expected, a);
    +
       NodeDef* b = graph.GetNode("b");
       ASSERT_NE(b, nullptr);
    -  ASSERT_EQ(b->input_size(), 0);
    +  CompareNodeInputs(graph, &expected, b);
    +
       NodeDef* c = graph.GetNode("c");
       ASSERT_NE(c, nullptr);
    -  ASSERT_EQ(c->input_size(), 0);
    +  CompareNodeInputs(graph, &expected, c);
    +
       NodeDef* d = graph.GetNode("d");
       ASSERT_NE(d, nullptr);
    -  ASSERT_EQ(d->input_size(), 1);
    -  EXPECT_EQ(d->input(0), "c:1");
    +  expected = NDef("", "", {"c:1"});
    +  CompareNodeInputs(graph, &expected, d);
    +
       NodeDef* foo_1 = graph.GetNode("foo_1");
       ASSERT_NE(foo_1, nullptr);
    -  ASSERT_EQ(foo_1->input_size(), 2);
    -  EXPECT_EQ(foo_1->input(0), "a");
    -  EXPECT_EQ(foo_1->input(1), "b:1");
    +  expected = NDef("", "", {"a", "b:1"});
    +  CompareNodeInputs(graph, &expected, foo_1);
    +
       NodeDef* foo_2 = graph.GetNode("foo_2");
       ASSERT_NE(foo_2, nullptr);
    -  ASSERT_EQ(foo_2->input_size(), 2);
    -  EXPECT_EQ(foo_2->input(0), "a");
    -  EXPECT_EQ(foo_2->input(1), "^b");
    +  expected = NDef("", "", {"a", "^b"});
    +  CompareNodeInputs(graph, &expected, foo_2);
    +
       NodeDef* foo_3 = graph.GetNode("foo_3");
       ASSERT_NE(foo_3, nullptr);
    -  ASSERT_EQ(foo_3->input_size(), 2);
    -  EXPECT_EQ(foo_3->input(0), "a");
    -  EXPECT_EQ(foo_3->input(1), "b:1");
    +  expected = NDef("", "", {"a", "b:1"});
    +  CompareNodeInputs(graph, &expected, foo_3);
    +
       NodeDef* foo_4 = graph.GetNode("foo_4");
       ASSERT_NE(foo_4, nullptr);
    -  ASSERT_EQ(foo_4->input_size(), 2);
    -  EXPECT_EQ(foo_4->input(0), "a:2");
    -  EXPECT_EQ(foo_4->input(1), "b:1");
    +  expected = NDef("", "", {"a:2", "b:1"});
    +  CompareNodeInputs(graph, &expected, foo_4);
    +
       NodeDef* foo_5 = graph.GetNode("foo_5");
       ASSERT_NE(foo_5, nullptr);
    -  ASSERT_EQ(foo_5->input_size(), 2);
    -  EXPECT_EQ(foo_5->input(0), "a:2");
    -  EXPECT_EQ(foo_5->input(1), "b:1");
    +  expected = NDef("", "", {"a:2", "b:1"});
    +  CompareNodeInputs(graph, &expected, foo_5);
    +
       NodeDef* foo_6 = graph.GetNode("foo_6");
       ASSERT_NE(foo_6, nullptr);
    -  ASSERT_EQ(foo_6->input_size(), 2);
    -  EXPECT_EQ(foo_6->input(0), "d");
    -  EXPECT_EQ(foo_6->input(1), "^d");
    +  expected = NDef("", "", {"d", "^d"});
    +  CompareNodeInputs(graph, &expected, foo_6);
    +
       NodeDef* foo_7 = graph.GetNode("foo_7");
       ASSERT_NE(foo_7, nullptr);
    -  ASSERT_EQ(foo_7->input_size(), 4);
    -  EXPECT_EQ(foo_7->input(0), "a:3");
    -  EXPECT_EQ(foo_7->input(1), "b:2");
    -  EXPECT_EQ(foo_7->input(2), "d");
    -  EXPECT_EQ(foo_7->input(3), "^d");
    +  expected = NDef("", "", {"a:3", "b:2", "d", "^d"});
    +  CompareNodeInputs(graph, &expected, foo_7);
     }
     
     TEST(MutableGraphViewTest, DedupControllingFaninsOnAddFanin) {
    @@ -601,17 +611,18 @@ TEST(MutableGraphViewTest, DedupControllingFaninsOnAddFanin) {
     
       MutableGraphView graph(&graph_def);
     
    -  EXPECT_TRUE(graph.AddFanin("b", {"a", 2}));
    +  EXPECT_TRUE(graph.AddRegularFanin("b", {"a", 2}));
    +  NodeDef expected;
       NodeDef* b = graph.GetNode("b");
       ASSERT_NE(b, nullptr);
    -  ASSERT_EQ(b->input_size(), 1);
    -  EXPECT_EQ(b->input(0), "a:2");
    +  expected = NDef("", "", {"a:2"});
    +  CompareNodeInputs(graph, &expected, b);
     
    -  EXPECT_FALSE(graph.AddFanin("c", {"a", Graph::kControlSlot}));
    +  EXPECT_FALSE(graph.AddControllingFanin("c", {"a", Graph::kControlSlot}));
       NodeDef* c = graph.GetNode("c");
       ASSERT_NE(c, nullptr);
    -  ASSERT_EQ(c->input_size(), 1);
    -  EXPECT_EQ(c->input(0), "a:1");
    +  expected = NDef("", "", {"a:1"});
    +  CompareNodeInputs(graph, &expected, c);
     }
     
     TEST(MutableGraphViewTest, NoDedupControlFlowControllingFaninsOnAddFanin) {
    @@ -622,32 +633,30 @@ TEST(MutableGraphViewTest, NoDedupControlFlowControllingFaninsOnAddFanin) {
     
       MutableGraphView graph(&graph_def);
     
    -  EXPECT_TRUE(graph.AddFanin("c", {"b", 2}));
    +  EXPECT_TRUE(graph.AddRegularFanin("c", {"b", 2}));
    +  NodeDef expected;
       NodeDef* c = graph.GetNode("c");
       ASSERT_NE(c, nullptr);
    -  ASSERT_EQ(c->input_size(), 1);
    -  EXPECT_EQ(c->input(0), "b:2");
    -  EXPECT_TRUE(graph.AddFanin("c", {"b", Graph::kControlSlot}));
    -  ASSERT_EQ(c->input_size(), 2);
    -  EXPECT_EQ(c->input(0), "b:2");
    -  EXPECT_EQ(c->input(1), "^b");
    -  EXPECT_FALSE(graph.AddFanin("c", {"b", Graph::kControlSlot}));
    -  ASSERT_EQ(c->input_size(), 2);
    -  EXPECT_EQ(c->input(0), "b:2");
    -  EXPECT_EQ(c->input(1), "^b");
    -
    -  EXPECT_TRUE(graph.AddFanin("d", {"b", Graph::kControlSlot}));
    +  expected = NDef("", "", {"b:2"});
    +  CompareNodeInputs(graph, &expected, c);
    +  EXPECT_TRUE(graph.AddControllingFanin("c", {"b", Graph::kControlSlot}));
    +  expected = NDef("", "", {"b:2", "^b"});
    +  CompareNodeInputs(graph, &expected, c);
    +  EXPECT_FALSE(graph.AddControllingFanin("c", {"b", Graph::kControlSlot}));
    +  expected = NDef("", "", {"b:2", "^b"});
    +  CompareNodeInputs(graph, &expected, c);
    +
    +  EXPECT_TRUE(graph.AddControllingFanin("d", {"b", Graph::kControlSlot}));
       NodeDef* d = graph.GetNode("d");
       ASSERT_NE(d, nullptr);
    -  ASSERT_EQ(d->input_size(), 1);
    -  EXPECT_EQ(d->input(0), "^b");
    -  EXPECT_FALSE(graph.AddFanin("d", {"b", Graph::kControlSlot}));
    -  ASSERT_EQ(d->input_size(), 1);
    -  EXPECT_EQ(d->input(0), "^b");
    -  EXPECT_TRUE(graph.AddFanin("d", {"b", 3}));
    -  ASSERT_EQ(d->input_size(), 2);
    -  EXPECT_EQ(d->input(0), "b:3");
    -  EXPECT_EQ(d->input(1), "^b");
    +  expected = NDef("", "", {"^b"});
    +  CompareNodeInputs(graph, &expected, d);
    +  EXPECT_FALSE(graph.AddControllingFanin("d", {"b", Graph::kControlSlot}));
    +  expected = NDef("", "", {"^b"});
    +  CompareNodeInputs(graph, &expected, d);
    +  EXPECT_TRUE(graph.AddRegularFanin("d", {"b", 3}));
    +  expected = NDef("", "", {"b:3", "^b"});
    +  CompareNodeInputs(graph, &expected, d);
     }
     
     TEST(MutableGraphViewTest, DedupControllingFaninsOnUpdateFanin) {
    @@ -662,8 +671,8 @@ TEST(MutableGraphViewTest, DedupControllingFaninsOnUpdateFanin) {
       EXPECT_TRUE(graph.UpdateFanin("c", {"a", 1}, {"b", 2}));
       NodeDef* c = graph.GetNode("c");
       ASSERT_NE(c, nullptr);
    -  ASSERT_EQ(c->input_size(), 1);
    -  EXPECT_EQ(c->input(0), "b:2");
    +  NodeDef expected = NDef("", "", {"b:2"});
    +  CompareNodeInputs(graph, &expected, c);
     }
     
     TEST(MutableGraphViewTest, NoDedupControlFlowControllingFaninsOnUpdateFanin) {
    @@ -677,23 +686,22 @@ TEST(MutableGraphViewTest, NoDedupControlFlowControllingFaninsOnUpdateFanin) {
     
       EXPECT_TRUE(graph.UpdateFanin("d", {"b", Graph::kControlSlot},
                                     {"c", Graph::kControlSlot}));
    +  NodeDef expected;
       NodeDef* d = graph.GetNode("d");
       ASSERT_NE(d, nullptr);
    -  ASSERT_EQ(d->input_size(), 2);
    -  EXPECT_EQ(d->input(0), "c");
    -  EXPECT_EQ(d->input(1), "^c");
    +  expected = NDef("", "", {"c", "^c"});
    +  CompareNodeInputs(graph, &expected, d);
     
       EXPECT_TRUE(graph.UpdateFanin("e", {"b", 0}, {"c", 3}));
       NodeDef* e = graph.GetNode("e");
       ASSERT_NE(e, nullptr);
    -  ASSERT_EQ(e->input_size(), 2);
    -  EXPECT_EQ(e->input(0), "c:3");
    -  EXPECT_EQ(e->input(1), "^c");
    +  expected = NDef("", "", {"c:3", "^c"});
    +  CompareNodeInputs(graph, &expected, e);
     
       EXPECT_TRUE(graph.UpdateFanin("e", {"c", 3}, {"c", Graph::kControlSlot}));
       ASSERT_NE(e, nullptr);
    -  ASSERT_EQ(e->input_size(), 1);
    -  EXPECT_EQ(e->input(0), "^c");
    +  expected = NDef("", "", {"^c"});
    +  CompareNodeInputs(graph, &expected, e);
     }
     
     TEST(MutableGraphViewTest, UpdateMaxRegularOutputPortOnAddFanin) {
    @@ -705,14 +713,17 @@ TEST(MutableGraphViewTest, UpdateMaxRegularOutputPortOnAddFanin) {
     
       MutableGraphView graph(&graph_def);
     
    -  EXPECT_TRUE(graph.AddFanin("c", {"a", 3}));
    +  EXPECT_TRUE(graph.AddRegularFanin("c", {"a", 3}));
       NodeDef* a = graph.GetNode("a");
       ASSERT_NE(a, nullptr);
    +
       auto fanouts = graph.GetFanouts(*a, /*include_controlled_nodes=*/true);
       EXPECT_EQ(fanouts.size(), 2);
    +
       NodeDef* b = graph.GetNode("b");
       ASSERT_NE(b, nullptr);
       EXPECT_EQ(fanouts.count(MutableGraphView::InputPort(b, 0)), 1);
    +
       NodeDef* c = graph.GetNode("c");
       ASSERT_NE(c, nullptr);
       EXPECT_EQ(fanouts.count(MutableGraphView::InputPort(c, 0)), 1);
    @@ -727,11 +738,13 @@ TEST(MutableGraphViewTest, UpdateMaxRegularOutputPortOnRemoveFanin) {
     
       MutableGraphView graph(&graph_def);
     
    -  EXPECT_TRUE(graph.RemoveFanin("c", {"a", 2}));
    +  EXPECT_TRUE(graph.RemoveRegularFanin("c", {"a", 2}));
       NodeDef* a = graph.GetNode("a");
       ASSERT_NE(a, nullptr);
    +
       auto fanouts = graph.GetFanouts(*a, /*include_controlled_nodes=*/true);
       EXPECT_EQ(fanouts.size(), 1);
    +
       NodeDef* b = graph.GetNode("b");
       ASSERT_NE(b, nullptr);
       EXPECT_EQ(fanouts.count(MutableGraphView::InputPort(b, 0)), 1);
    @@ -746,11 +759,13 @@ TEST(MutableGraphViewTest, KeepMaxRegularOutputPortOnRemoveFanin) {
     
       MutableGraphView graph(&graph_def);
     
    -  EXPECT_TRUE(graph.RemoveFanin("b", {"a", 1}));
    +  EXPECT_TRUE(graph.RemoveRegularFanin("b", {"a", 1}));
       NodeDef* a = graph.GetNode("a");
       ASSERT_NE(a, nullptr);
    +
       auto fanouts = graph.GetFanouts(*a, /*include_controlled_nodes=*/true);
       EXPECT_EQ(fanouts.size(), 1);
    +
       NodeDef* c = graph.GetNode("c");
       ASSERT_NE(c, nullptr);
       EXPECT_EQ(fanouts.count(MutableGraphView::InputPort(c, 0)), 1);
    @@ -768,13 +783,17 @@ TEST(MutableGraphViewTest, UpdateMaxRegularOutputPortOnUpdateFanin) {
       EXPECT_TRUE(graph.UpdateFanin("c", {"a", 2}, {"b", 3}));
       NodeDef* a = graph.GetNode("a");
       ASSERT_NE(a, nullptr);
    +
       auto a_fanouts = graph.GetFanouts(*a, /*include_controlled_nodes=*/true);
       EXPECT_EQ(a_fanouts.size(), 1);
    +
       NodeDef* b = graph.GetNode("b");
       ASSERT_NE(b, nullptr);
       EXPECT_EQ(a_fanouts.count(MutableGraphView::InputPort(b, 0)), 1);
    +
       auto b_fanouts = graph.GetFanouts(*b, /*include_controlled_nodes=*/true);
       EXPECT_EQ(b_fanouts.size(), 1);
    +
       NodeDef* c = graph.GetNode("c");
       ASSERT_NE(c, nullptr);
       EXPECT_EQ(b_fanouts.count(MutableGraphView::InputPort(c, 0)), 1);
    @@ -795,12 +814,15 @@ TEST(MutableGraphViewTest, AddControllingFaninMissing) {
       EXPECT_FALSE(graph.AddControllingFanin("c", {"d", Graph::kControlSlot}));
     
       ASSERT_EQ(graph.graph()->node_size(), 2);
    +  NodeDef expected;
       NodeDef* a = graph.GetNode("a");
       ASSERT_NE(a, nullptr);
    -  ASSERT_EQ(a->input_size(), 0);
    +  expected = NDef("", "", {});
    +  CompareNodeInputs(graph, &expected, a);
    +
       NodeDef* b = graph.GetNode("b");
       ASSERT_NE(b, nullptr);
    -  ASSERT_EQ(b->input_size(), 0);
    +  CompareNodeInputs(graph, &expected, b);
     }
     
     TEST(MutableGraphViewTest, AddControllingFaninExistingControl) {
    @@ -814,13 +836,16 @@ TEST(MutableGraphViewTest, AddControllingFaninExistingControl) {
       EXPECT_FALSE(graph.AddControllingFanin("a", {"b", Graph::kControlSlot}));
     
       ASSERT_EQ(graph.graph()->node_size(), 2);
    +  NodeDef expected;
       NodeDef* a = graph.GetNode("a");
       ASSERT_NE(a, nullptr);
    -  ASSERT_EQ(a->input_size(), 1);
    -  EXPECT_EQ(a->input(0), "^b");
    +  expected = NDef("", "", {"^b"});
    +  CompareNodeInputs(graph, &expected, a);
    +
       NodeDef* b = graph.GetNode("b");
       ASSERT_NE(b, nullptr);
    -  ASSERT_EQ(b->input_size(), 0);
    +  expected = NDef("", "", {});
    +  CompareNodeInputs(graph, &expected, b);
     }
     
     TEST(MutableGraphViewTest, AddControllingFaninNotSwitch) {
    @@ -834,13 +859,16 @@ TEST(MutableGraphViewTest, AddControllingFaninNotSwitch) {
       EXPECT_FALSE(graph.AddControllingFanin("a", {"b", 2}));
     
       ASSERT_EQ(graph.graph()->node_size(), 2);
    +  NodeDef expected;
       NodeDef* a = graph.GetNode("a");
       ASSERT_NE(a, nullptr);
    -  ASSERT_EQ(a->input_size(), 1);
    -  EXPECT_EQ(a->input(0), "^b");
    +  expected = NDef("", "", {"^b"});
    +  CompareNodeInputs(graph, &expected, a);
    +
       NodeDef* b = graph.GetNode("b");
       ASSERT_NE(b, nullptr);
    -  ASSERT_EQ(b->input_size(), 0);
    +  expected = NDef("", "", {});
    +  CompareNodeInputs(graph, &expected, b);
     }
     
     TEST(MutableGraphViewTest, AddControllingFaninSwitchWithIdentity) {
    @@ -857,8 +885,8 @@ TEST(MutableGraphViewTest, AddControllingFaninSwitchWithIdentity) {
       ASSERT_EQ(graph.graph()->node_size(), 3);
       NodeDef* a = graph.GetNode("a");
       ASSERT_NE(a, nullptr);
    -  ASSERT_EQ(a->input_size(), 1);
    -  EXPECT_EQ(a->input(0), "^identity");
    +  NodeDef expected = NDef("", "", {"^identity"});
    +  CompareNodeInputs(graph, &expected, a);
     }
     
     TEST(MutableGraphViewTest, AddControllingFaninSwitchWithNoExistingIdentity) {
    @@ -874,14 +902,16 @@ TEST(MutableGraphViewTest, AddControllingFaninSwitchWithNoExistingIdentity) {
       EXPECT_FALSE(graph.AddControllingFanin("a", {"switch", 0}));
     
       ASSERT_EQ(graph.graph()->node_size(), 3);
    +  NodeDef expected;
       NodeDef* a = graph.GetNode("a");
       ASSERT_NE(a, nullptr);
    -  ASSERT_EQ(a->input_size(), 1);
    -  EXPECT_EQ(a->input(0), "^ConstantFoldingCtrl/switch_0");
    +  expected = NDef("", "", {"^ConstantFoldingCtrl/switch_0"});
    +  CompareNodeInputs(graph, &expected, a);
    +
       NodeDef* identity = graph.GetNode("ConstantFoldingCtrl/switch_0");
       ASSERT_NE(identity, nullptr);
    -  ASSERT_EQ(identity->input_size(), 1);
    -  EXPECT_EQ(identity->input(0), "switch");
    +  expected = NDef("", "", {"switch"});
    +  CompareNodeInputs(graph, &expected, identity);
       EXPECT_EQ(identity->op(), "Identity");
       EXPECT_EQ(identity->device(), kDevice);
       ASSERT_TRUE(identity->attr().count("T"));
    @@ -902,8 +932,72 @@ TEST(MutableGraphViewTest, AddControllingFaninSwitchWithExistingAddedIdentity) {
       ASSERT_EQ(graph.graph()->node_size(), 3);
       NodeDef* a = graph.GetNode("a");
       ASSERT_NE(a, nullptr);
    -  ASSERT_EQ(a->input_size(), 1);
    -  EXPECT_EQ(a->input(0), "^ConstantFoldingCtrl/switch_0");
    +  NodeDef expected = NDef("", "", {"^ConstantFoldingCtrl/switch_0"});
    +  CompareNodeInputs(graph, &expected, a);
    +}
    +
    +TEST(MutableGraphViewTest, RemoveControllingFaninMissing) {
    +  // Actual node.op() is not important in this test.
    +  GraphDef graph_def = test::function::GDef(
    +      {
    +          NDef("a", "NotImportant", {}, {}),
    +          NDef("b", "NotImportant", {}, {}),
    +          NDef("c", "NotImportant", {}, {}),
    +          NDef("d", "NotImportant", {"^a", "^b"}),
    +      },
    +      /*funcs=*/{});
    +
    +  MutableGraphView graph(&graph_def);
    +
    +  EXPECT_FALSE(graph.RemoveControllingFanin("d", "c"));
    +
    +  ASSERT_EQ(graph.graph()->node_size(), 4);
    +  NodeDef* d = graph.GetNode("d");
    +  ASSERT_NE(d, nullptr);
    +  NodeDef expected = NDef("", "", {"^a", "^b"});
    +  CompareNodeInputs(graph, &expected, d);
    +}
    +
    +TEST(MutableGraphViewTest, RemoveControllingFaninExisting) {
    +  // Actual node.op() is not important in this test.
    +  GraphDef graph_def = test::function::GDef(
    +      {
    +          NDef("a", "NotImportant", {}, {}),
    +          NDef("b", "NotImportant", {}, {}),
    +          NDef("c", "NotImportant", {}, {}),
    +          NDef("d", "NotImportant", {"^a", "^b", "^c"}),
    +      },
    +      /*funcs=*/{});
    +
    +  MutableGraphView graph(&graph_def);
    +
    +  EXPECT_TRUE(graph.RemoveControllingFanin("d", "a"));
    +  EXPECT_FALSE(graph.RemoveControllingFanin("d", "a"));
    +
    +  ASSERT_EQ(graph.graph()->node_size(), 4);
    +  NodeDef* d = graph.GetNode("d");
    +  ASSERT_NE(d, nullptr);
    +  NodeDef expected = NDef("", "", {"^c", "^b"});
    +  CompareNodeInputs(graph, &expected, d);
    +}
    +
    +TEST(MutableGraphViewTest, RemoveControllingFaninOnRegularFanin) {
    +  // Actual node.op() is not important in this test.
    +  GraphDef graph_def = test::function::GDef(
    +      {NDef("a", "NotImportant", {}, {}), NDef("b", "NotImportant", {"a"}),
    +       NDef("c", "NotImportant", {"a", "b", "^c"})},
    +      /*funcs=*/{});
    +
    +  MutableGraphView graph(&graph_def);
    +
    +  EXPECT_FALSE(graph.RemoveControllingFanin("c", "a"));
    +  EXPECT_FALSE(graph.RemoveControllingFanin("c", "b"));
    +
    +  ASSERT_EQ(graph.graph()->node_size(), 3);
    +  NodeDef* c = graph.GetNode("c");
    +  ASSERT_NE(c, nullptr);
    +  NodeDef expected = NDef("", "", {"a", "b", "^c"});
    +  CompareNodeInputs(graph, &expected, c);
     }
     
     TEST(MutableGraphViewTest, DeleteNodes) {
    -- 
    GitLab
    
    

    08sZB24?7wg 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

  • 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